From 8d179683ff559143b54a63bb60c05edbbeddd013 Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Wed, 24 Jan 2018 12:27:17 -0800 Subject: [PATCH 0001/1530] Enable integer overflow sanitizer in audioflinger. Enables signed and unsigned integer overflow sanitization in audioflinger. Bug: 72380865 Test: CTS tests and usage testing in diagnostics mode Change-Id: I40413f06a2369a4d5569f08620e901670075ccab --- services/audioflinger/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 7419e648c8..16c1673f49 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -15,6 +15,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder LOCAL_CFLAGS := -Wall -Werror +LOCAL_SANITIZE := integer_overflow include $(BUILD_SHARED_LIBRARY) @@ -82,6 +83,7 @@ LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' LOCAL_CFLAGS += -fvisibility=hidden LOCAL_CFLAGS += -Werror -Wall +LOCAL_SANITIZE := integer_overflow include $(BUILD_SHARED_LIBRARY) -- GitLab From 9c8e893db50939c5f34175a571182c7728939fa5 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 23 Feb 2018 17:21:46 +0000 Subject: [PATCH 0002/1530] frameworks/av: Set LOCAL_SDK_VERSION where possible. This change sets LOCAL_SDK_VERSION for all packages where this is possible without breaking the build, and LOCAL_PRIVATE_PLATFORM_APIS := true otherwise. Setting one of these two will be made required soon, and this is a change in preparation for that. Not setting LOCAL_SDK_VERSION makes the app implicitly depend on the bootclasspath, which is often not required. This change effectively makes depending on private apis opt-in rather than opt-out. Test: make relevant packages Bug: 73535841 Change-Id: I616c59eede8627f73ed79c77b3a45c52eec1cd80 --- packages/MediaComponents/Android.mk | 1 + packages/MediaComponents/test/Android.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk index 22eea2533c..19e4b24d78 100644 --- a/packages/MediaComponents/Android.mk +++ b/packages/MediaComponents/Android.mk @@ -32,6 +32,7 @@ LOCAL_CERTIFICATE := platform # TODO: Use System SDK once public APIs are approved # LOCAL_SDK_VERSION := system_current +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ diff --git a/packages/MediaComponents/test/Android.mk b/packages/MediaComponents/test/Android.mk index 8703b9fc40..ea053e0ae9 100644 --- a/packages/MediaComponents/test/Android.mk +++ b/packages/MediaComponents/test/Android.mk @@ -25,4 +25,5 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ compatibility-device-util LOCAL_PACKAGE_NAME := MediaComponentsTest +LOCAL_PRIVATE_PLATFORM_APIS := true include $(BUILD_PACKAGE) -- GitLab From 1a6c11685d3868d0ebe8543842d213c4ff7e9009 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 27 Feb 2018 12:43:06 -0800 Subject: [PATCH 0003/1530] heif: use width and height for decoder The display dimensions from MPEG4Extractor is applied on top of width and height for scaling, it's not for cropping. For bitmap uses we have to use width and height, the frame from media server is not scaled. bug: 73172046 Test: test app in bug; open folders with heif files in Photos. Change-Id: I3d7f1492d08d48876836ccc05b6eb4de0d0c0f9a --- media/libheif/HeifDecoderImpl.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp index 2dfbdca024..4b17a9bcc1 100644 --- a/media/libheif/HeifDecoderImpl.cpp +++ b/media/libheif/HeifDecoderImpl.cpp @@ -332,8 +332,8 @@ bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) { if (frameInfo != nullptr) { frameInfo->set( - videoFrame->mDisplayWidth, - videoFrame->mDisplayHeight, + videoFrame->mWidth, + videoFrame->mHeight, videoFrame->mRotationAngle, videoFrame->mBytesPerPixel, videoFrame->mIccSize, @@ -410,8 +410,8 @@ bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { if (frameInfo != nullptr) { frameInfo->set( - videoFrame->mDisplayWidth, - videoFrame->mDisplayHeight, + videoFrame->mWidth, + videoFrame->mHeight, videoFrame->mRotationAngle, videoFrame->mBytesPerPixel, videoFrame->mIccSize, @@ -430,12 +430,12 @@ bool HeifDecoderImpl::getScanline(uint8_t* dst) { return false; } VideoFrame* videoFrame = static_cast(mFrameMemory->pointer()); - if (mCurScanline >= videoFrame->mDisplayHeight) { + if (mCurScanline >= videoFrame->mHeight) { ALOGE("no more scanline available"); return false; } uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++; - memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth); + memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth); return true; } @@ -447,8 +447,8 @@ size_t HeifDecoderImpl::skipScanlines(size_t count) { uint32_t oldScanline = mCurScanline; mCurScanline += count; - if (mCurScanline > videoFrame->mDisplayHeight) { - mCurScanline = videoFrame->mDisplayHeight; + if (mCurScanline > videoFrame->mHeight) { + mCurScanline = videoFrame->mHeight; } return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0; } -- GitLab From 3ce53590f7b4ce6eba59a8f1949584cbd6fe410c Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 23 Feb 2018 15:14:50 -0800 Subject: [PATCH 0004/1530] Audio V4: *CNT and *MAX are no longer part of the enum audio.h used to have *_CNT and *_MAX values in its enum mostly for enum iteration. This was not compatible with Treble as it denied any enum inheritance. *_CNT and *_MAX are now implemented with macro that can be updated and are not part of the API. Bug: 38184704 Test: Compile Change-Id: I3921fdf3435ba114ca77a4595ba8fc97a5e0438d Signed-off-by: Kevin Rocard --- media/libmedia/TypeConverter.cpp | 4 ---- media/libmediaplayerservice/StagefrightRecorder.cpp | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index 4cadeb120a..1096df46f3 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -308,8 +308,6 @@ const UsageTypeConverter::Table UsageTypeConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_USAGE_ASSISTANCE_SONIFICATION), MAKE_STRING_FROM_ENUM(AUDIO_USAGE_GAME), MAKE_STRING_FROM_ENUM(AUDIO_USAGE_VIRTUAL_SOURCE), - MAKE_STRING_FROM_ENUM(AUDIO_USAGE_CNT), - MAKE_STRING_FROM_ENUM(AUDIO_USAGE_MAX), TERMINATOR }; @@ -325,8 +323,6 @@ const SourceTypeConverter::Table SourceTypeConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_COMMUNICATION), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_REMOTE_SUBMIX), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_UNPROCESSED), - MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_CNT), - MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_MAX), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_FM_TUNER), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_HOTWORD), TERMINATOR diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 5cbf976235..9ab9aae76d 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -105,7 +105,7 @@ StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName) : MediaRecorderBase(opPackageName), mWriter(NULL), mOutputFd(-1), - mAudioSource(AUDIO_SOURCE_CNT), + mAudioSource((audio_source_t)AUDIO_SOURCE_CNT), // initialize with invalid value mVideoSource(VIDEO_SOURCE_LIST_END), mStarted(false), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), @@ -2047,7 +2047,7 @@ status_t StagefrightRecorder::reset() { stop(); // No audio or video source by default - mAudioSource = AUDIO_SOURCE_CNT; + mAudioSource = (audio_source_t)AUDIO_SOURCE_CNT; // reset to invalid value mVideoSource = VIDEO_SOURCE_LIST_END; // Default parameters -- GitLab From d4de2889dbe297942fccb8816c7dfa4b9e8bb7fe Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 28 Feb 2018 14:33:38 -0800 Subject: [PATCH 0005/1530] Audio V4: Copy 2.0 audiohal to 4.0 For easier review of the changes, first copy 2.0 to 4.0, the changes are made in the following patch. Bug: 38184704 Test: compile Change-Id: I5bf16e5f8e7b61cc13e86c06b899ec4e6624a961 Signed-off-by: Kevin Rocard --- .../libaudiohal/4.0/ConversionHelperHidl.cpp | 103 +++ media/libaudiohal/4.0/ConversionHelperHidl.h | 83 ++ media/libaudiohal/4.0/DeviceHalHidl.cpp | 358 +++++++++ media/libaudiohal/4.0/DeviceHalHidl.h | 126 +++ media/libaudiohal/4.0/DeviceHalLocal.cpp | 199 +++++ media/libaudiohal/4.0/DeviceHalLocal.h | 124 +++ .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 98 +++ media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 54 ++ .../4.0/DevicesFactoryHalHybrid.cpp | 41 + .../libaudiohal/4.0/DevicesFactoryHalHybrid.h | 47 ++ .../4.0/DevicesFactoryHalLocal.cpp | 69 ++ .../libaudiohal/4.0/DevicesFactoryHalLocal.h | 46 ++ media/libaudiohal/4.0/EffectBufferHalHidl.cpp | 144 ++++ media/libaudiohal/4.0/EffectBufferHalHidl.h | 76 ++ media/libaudiohal/4.0/EffectHalHidl.cpp | 338 ++++++++ media/libaudiohal/4.0/EffectHalHidl.h | 108 +++ .../libaudiohal/4.0/EffectsFactoryHalHidl.cpp | 150 ++++ media/libaudiohal/4.0/EffectsFactoryHalHidl.h | 73 ++ media/libaudiohal/4.0/StreamHalHidl.cpp | 752 ++++++++++++++++++ media/libaudiohal/4.0/StreamHalHidl.h | 239 ++++++ media/libaudiohal/4.0/StreamHalLocal.cpp | 316 ++++++++ media/libaudiohal/4.0/StreamHalLocal.h | 210 +++++ media/libaudiohal/4.0/StreamPowerLog.h | 102 +++ 23 files changed, 3856 insertions(+) create mode 100644 media/libaudiohal/4.0/ConversionHelperHidl.cpp create mode 100644 media/libaudiohal/4.0/ConversionHelperHidl.h create mode 100644 media/libaudiohal/4.0/DeviceHalHidl.cpp create mode 100644 media/libaudiohal/4.0/DeviceHalHidl.h create mode 100644 media/libaudiohal/4.0/DeviceHalLocal.cpp create mode 100644 media/libaudiohal/4.0/DeviceHalLocal.h create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHidl.h create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHybrid.h create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp create mode 100644 media/libaudiohal/4.0/DevicesFactoryHalLocal.h create mode 100644 media/libaudiohal/4.0/EffectBufferHalHidl.cpp create mode 100644 media/libaudiohal/4.0/EffectBufferHalHidl.h create mode 100644 media/libaudiohal/4.0/EffectHalHidl.cpp create mode 100644 media/libaudiohal/4.0/EffectHalHidl.h create mode 100644 media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp create mode 100644 media/libaudiohal/4.0/EffectsFactoryHalHidl.h create mode 100644 media/libaudiohal/4.0/StreamHalHidl.cpp create mode 100644 media/libaudiohal/4.0/StreamHalHidl.h create mode 100644 media/libaudiohal/4.0/StreamHalLocal.cpp create mode 100644 media/libaudiohal/4.0/StreamHalLocal.h create mode 100644 media/libaudiohal/4.0/StreamPowerLog.h diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.cpp b/media/libaudiohal/4.0/ConversionHelperHidl.cpp new file mode 100644 index 0000000000..f60bf8b69c --- /dev/null +++ b/media/libaudiohal/4.0/ConversionHelperHidl.cpp @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#include + +#define LOG_TAG "HalHidl" +#include +#include + +#include "ConversionHelperHidl.h" + +using ::android::hardware::audio::V2_0::Result; + +namespace android { + +// static +status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { + AudioParameter halKeys(keys); + if (halKeys.size() == 0) return BAD_VALUE; + hidlKeys->resize(halKeys.size()); + //FIXME: keyStreamSupportedChannels and keyStreamSupportedSamplingRates come with a + // "keyFormat=" pair. We need to transform it into a single key string so that it is + // carried over to the legacy HAL via HIDL. + String8 value; + bool keepFormatValue = halKeys.size() == 2 && + (halKeys.get(String8(AudioParameter::keyStreamSupportedChannels), value) == NO_ERROR || + halKeys.get(String8(AudioParameter::keyStreamSupportedSamplingRates), value) == NO_ERROR); + + for (size_t i = 0; i < halKeys.size(); ++i) { + String8 key; + status_t status = halKeys.getAt(i, key); + if (status != OK) return status; + if (keepFormatValue && key == AudioParameter::keyFormat) { + AudioParameter formatParam; + halKeys.getAt(i, key, value); + formatParam.add(key, value); + key = formatParam.toString(); + } + (*hidlKeys)[i] = key.string(); + } + return OK; +} + +// static +status_t ConversionHelperHidl::parametersFromHal( + const String8& kvPairs, hidl_vec *hidlParams) { + AudioParameter params(kvPairs); + if (params.size() == 0) return BAD_VALUE; + hidlParams->resize(params.size()); + for (size_t i = 0; i < params.size(); ++i) { + String8 key, value; + status_t status = params.getAt(i, key, value); + if (status != OK) return status; + (*hidlParams)[i].key = key.string(); + (*hidlParams)[i].value = value.string(); + } + return OK; +} + +// static +void ConversionHelperHidl::parametersToHal( + const hidl_vec& parameters, String8 *values) { + AudioParameter params; + for (size_t i = 0; i < parameters.size(); ++i) { + params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str())); + } + values->setTo(params.toString()); +} + +ConversionHelperHidl::ConversionHelperHidl(const char* className) + : mClassName(className) { +} + +// static +status_t ConversionHelperHidl::analyzeResult(const Result& result) { + switch (result) { + case Result::OK: return OK; + case Result::INVALID_ARGUMENTS: return BAD_VALUE; + case Result::INVALID_STATE: return NOT_ENOUGH_DATA; + case Result::NOT_INITIALIZED: return NO_INIT; + case Result::NOT_SUPPORTED: return INVALID_OPERATION; + default: return NO_INIT; + } +} + +void ConversionHelperHidl::emitError(const char* funcName, const char* description) { + ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.h b/media/libaudiohal/4.0/ConversionHelperHidl.h new file mode 100644 index 0000000000..c356f37e3d --- /dev/null +++ b/media/libaudiohal/4.0/ConversionHelperHidl.h @@ -0,0 +1,83 @@ +/* + * 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 ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H +#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H + +#include +#include +#include + +using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::Return; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +namespace android { + +class ConversionHelperHidl { + protected: + static status_t keysFromHal(const String8& keys, hidl_vec *hidlKeys); + static status_t parametersFromHal(const String8& kvPairs, hidl_vec *hidlParams); + static void parametersToHal(const hidl_vec& parameters, String8 *values); + + ConversionHelperHidl(const char* className); + + template + status_t processReturn(const char* funcName, const Return& ret, T *retval) { + if (ret.isOk()) { + // This way it also works for enum class to unscoped enum conversion. + *retval = static_cast(static_cast(ret)); + return OK; + } + return processReturn(funcName, ret); + } + + template + status_t processReturn(const char* funcName, const Return& ret) { + if (!ret.isOk()) { + emitError(funcName, ret.description().c_str()); + } + return ret.isOk() ? OK : FAILED_TRANSACTION; + } + + status_t processReturn(const char* funcName, const Return& ret) { + if (!ret.isOk()) { + emitError(funcName, ret.description().c_str()); + } + return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; + } + + template + status_t processReturn( + const char* funcName, const Return& ret, hardware::audio::V2_0::Result retval) { + if (!ret.isOk()) { + emitError(funcName, ret.description().c_str()); + } + return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; + } + + private: + const char* mClassName; + + static status_t analyzeResult(const hardware::audio::V2_0::Result& result); + + void emitError(const char* funcName, const char* description); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/4.0/DeviceHalHidl.cpp new file mode 100644 index 0000000000..0d9c6c4dec --- /dev/null +++ b/media/libaudiohal/4.0/DeviceHalHidl.cpp @@ -0,0 +1,358 @@ +/* + * 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. + */ + +#include + +#define LOG_TAG "DeviceHalHidl" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include + +#include "DeviceHalHidl.h" +#include "HidlUtils.h" +#include "StreamHalHidl.h" + +using ::android::hardware::audio::common::V2_0::AudioConfig; +using ::android::hardware::audio::common::V2_0::AudioDevice; +using ::android::hardware::audio::common::V2_0::AudioInputFlag; +using ::android::hardware::audio::common::V2_0::AudioOutputFlag; +using ::android::hardware::audio::common::V2_0::AudioPatchHandle; +using ::android::hardware::audio::common::V2_0::AudioPort; +using ::android::hardware::audio::common::V2_0::AudioPortConfig; +using ::android::hardware::audio::common::V2_0::AudioMode; +using ::android::hardware::audio::common::V2_0::AudioSource; +using ::android::hardware::audio::common::V2_0::HidlUtils; +using ::android::hardware::audio::V2_0::DeviceAddress; +using ::android::hardware::audio::V2_0::IPrimaryDevice; +using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +namespace android { + +namespace { + +status_t deviceAddressFromHal( + audio_devices_t device, const char* halAddress, DeviceAddress* address) { + address->device = AudioDevice(device); + + if (address == nullptr || strnlen(halAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { + return OK; + } + const bool isInput = (device & AUDIO_DEVICE_BIT_IN) != 0; + if (isInput) device &= ~AUDIO_DEVICE_BIT_IN; + if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_A2DP) != 0) + || (isInput && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) != 0)) { + int status = sscanf(halAddress, + "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", + &address->address.mac[0], &address->address.mac[1], &address->address.mac[2], + &address->address.mac[3], &address->address.mac[4], &address->address.mac[5]); + return status == 6 ? OK : BAD_VALUE; + } else if ((!isInput && (device & AUDIO_DEVICE_OUT_IP) != 0) + || (isInput && (device & AUDIO_DEVICE_IN_IP) != 0)) { + int status = sscanf(halAddress, + "%hhu.%hhu.%hhu.%hhu", + &address->address.ipv4[0], &address->address.ipv4[1], + &address->address.ipv4[2], &address->address.ipv4[3]); + return status == 4 ? OK : BAD_VALUE; + } else if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_USB)) != 0 + || (isInput && (device & AUDIO_DEVICE_IN_ALL_USB)) != 0) { + int status = sscanf(halAddress, + "card=%d;device=%d", + &address->address.alsa.card, &address->address.alsa.device); + return status == 2 ? OK : BAD_VALUE; + } else if ((!isInput && (device & AUDIO_DEVICE_OUT_BUS) != 0) + || (isInput && (device & AUDIO_DEVICE_IN_BUS) != 0)) { + if (halAddress != NULL) { + address->busAddress = halAddress; + return OK; + } + return BAD_VALUE; + } else if ((!isInput && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) != 0 + || (isInput && (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) != 0)) { + if (halAddress != NULL) { + address->rSubmixAddress = halAddress; + return OK; + } + return BAD_VALUE; + } + return OK; +} + +} // namespace + +DeviceHalHidl::DeviceHalHidl(const sp& device) + : ConversionHelperHidl("Device"), mDevice(device), + mPrimaryDevice(IPrimaryDevice::castFrom(device)) { +} + +DeviceHalHidl::~DeviceHalHidl() { + if (mDevice != 0) { + mDevice.clear(); + hardware::IPCThreadState::self()->flushCommands(); + } +} + +status_t DeviceHalHidl::getSupportedDevices(uint32_t*) { + // Obsolete. + return INVALID_OPERATION; +} + +status_t DeviceHalHidl::initCheck() { + if (mDevice == 0) return NO_INIT; + return processReturn("initCheck", mDevice->initCheck()); +} + +status_t DeviceHalHidl::setVoiceVolume(float volume) { + if (mDevice == 0) return NO_INIT; + if (mPrimaryDevice == 0) return INVALID_OPERATION; + return processReturn("setVoiceVolume", mPrimaryDevice->setVoiceVolume(volume)); +} + +status_t DeviceHalHidl::setMasterVolume(float volume) { + if (mDevice == 0) return NO_INIT; + if (mPrimaryDevice == 0) return INVALID_OPERATION; + return processReturn("setMasterVolume", mPrimaryDevice->setMasterVolume(volume)); +} + +status_t DeviceHalHidl::getMasterVolume(float *volume) { + if (mDevice == 0) return NO_INIT; + if (mPrimaryDevice == 0) return INVALID_OPERATION; + Result retval; + Return ret = mPrimaryDevice->getMasterVolume( + [&](Result r, float v) { + retval = r; + if (retval == Result::OK) { + *volume = v; + } + }); + return processReturn("getMasterVolume", ret, retval); +} + +status_t DeviceHalHidl::setMode(audio_mode_t mode) { + if (mDevice == 0) return NO_INIT; + if (mPrimaryDevice == 0) return INVALID_OPERATION; + return processReturn("setMode", mPrimaryDevice->setMode(AudioMode(mode))); +} + +status_t DeviceHalHidl::setMicMute(bool state) { + if (mDevice == 0) return NO_INIT; + return processReturn("setMicMute", mDevice->setMicMute(state)); +} + +status_t DeviceHalHidl::getMicMute(bool *state) { + if (mDevice == 0) return NO_INIT; + Result retval; + Return ret = mDevice->getMicMute( + [&](Result r, bool mute) { + retval = r; + if (retval == Result::OK) { + *state = mute; + } + }); + return processReturn("getMicMute", ret, retval); +} + +status_t DeviceHalHidl::setMasterMute(bool state) { + if (mDevice == 0) return NO_INIT; + return processReturn("setMasterMute", mDevice->setMasterMute(state)); +} + +status_t DeviceHalHidl::getMasterMute(bool *state) { + if (mDevice == 0) return NO_INIT; + Result retval; + Return ret = mDevice->getMasterMute( + [&](Result r, bool mute) { + retval = r; + if (retval == Result::OK) { + *state = mute; + } + }); + return processReturn("getMasterMute", ret, retval); +} + +status_t DeviceHalHidl::setParameters(const String8& kvPairs) { + if (mDevice == 0) return NO_INIT; + hidl_vec hidlParams; + status_t status = parametersFromHal(kvPairs, &hidlParams); + if (status != OK) return status; + return processReturn("setParameters", mDevice->setParameters(hidlParams)); +} + +status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { + values->clear(); + if (mDevice == 0) return NO_INIT; + hidl_vec hidlKeys; + status_t status = keysFromHal(keys, &hidlKeys); + if (status != OK) return status; + Result retval; + Return ret = mDevice->getParameters( + hidlKeys, + [&](Result r, const hidl_vec& parameters) { + retval = r; + if (retval == Result::OK) { + parametersToHal(parameters, values); + } + }); + return processReturn("getParameters", ret, retval); +} + +status_t DeviceHalHidl::getInputBufferSize( + const struct audio_config *config, size_t *size) { + if (mDevice == 0) return NO_INIT; + AudioConfig hidlConfig; + HidlUtils::audioConfigFromHal(*config, &hidlConfig); + Result retval; + Return ret = mDevice->getInputBufferSize( + hidlConfig, + [&](Result r, uint64_t bufferSize) { + retval = r; + if (retval == Result::OK) { + *size = static_cast(bufferSize); + } + }); + return processReturn("getInputBufferSize", ret, retval); +} + +status_t DeviceHalHidl::openOutputStream( + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address, + sp *outStream) { + if (mDevice == 0) return NO_INIT; + DeviceAddress hidlDevice; + status_t status = deviceAddressFromHal(devices, address, &hidlDevice); + if (status != OK) return status; + AudioConfig hidlConfig; + HidlUtils::audioConfigFromHal(*config, &hidlConfig); + Result retval = Result::NOT_INITIALIZED; + Return ret = mDevice->openOutputStream( + handle, + hidlDevice, + hidlConfig, + AudioOutputFlag(flags), + [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { + retval = r; + if (retval == Result::OK) { + *outStream = new StreamOutHalHidl(result); + } + HidlUtils::audioConfigToHal(suggestedConfig, config); + }); + return processReturn("openOutputStream", ret, retval); +} + +status_t DeviceHalHidl::openInputStream( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + audio_input_flags_t flags, + const char *address, + audio_source_t source, + sp *inStream) { + if (mDevice == 0) return NO_INIT; + DeviceAddress hidlDevice; + status_t status = deviceAddressFromHal(devices, address, &hidlDevice); + if (status != OK) return status; + AudioConfig hidlConfig; + HidlUtils::audioConfigFromHal(*config, &hidlConfig); + Result retval = Result::NOT_INITIALIZED; + Return ret = mDevice->openInputStream( + handle, + hidlDevice, + hidlConfig, + AudioInputFlag(flags), + AudioSource(source), + [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { + retval = r; + if (retval == Result::OK) { + *inStream = new StreamInHalHidl(result); + } + HidlUtils::audioConfigToHal(suggestedConfig, config); + }); + return processReturn("openInputStream", ret, retval); +} + +status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) { + if (mDevice == 0) return NO_INIT; + return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches); +} + +status_t DeviceHalHidl::createAudioPatch( + unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *patch) { + if (mDevice == 0) return NO_INIT; + hidl_vec hidlSources, hidlSinks; + HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources); + HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks); + Result retval; + Return ret = mDevice->createAudioPatch( + hidlSources, hidlSinks, + [&](Result r, AudioPatchHandle hidlPatch) { + retval = r; + if (retval == Result::OK) { + *patch = static_cast(hidlPatch); + } + }); + return processReturn("createAudioPatch", ret, retval); +} + +status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) { + if (mDevice == 0) return NO_INIT; + return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch)); +} + +status_t DeviceHalHidl::getAudioPort(struct audio_port *port) { + if (mDevice == 0) return NO_INIT; + AudioPort hidlPort; + HidlUtils::audioPortFromHal(*port, &hidlPort); + Result retval; + Return ret = mDevice->getAudioPort( + hidlPort, + [&](Result r, const AudioPort& p) { + retval = r; + if (retval == Result::OK) { + HidlUtils::audioPortToHal(p, port); + } + }); + return processReturn("getAudioPort", ret, retval); +} + +status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) { + if (mDevice == 0) return NO_INIT; + AudioPortConfig hidlConfig; + HidlUtils::audioPortConfigFromHal(*config, &hidlConfig); + return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig)); +} + +status_t DeviceHalHidl::dump(int fd) { + if (mDevice == 0) return NO_INIT; + native_handle_t* hidlHandle = native_handle_create(1, 0); + hidlHandle->data[0] = fd; + Return ret = mDevice->debugDump(hidlHandle); + native_handle_delete(hidlHandle); + return processReturn("dump", ret); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/DeviceHalHidl.h b/media/libaudiohal/4.0/DeviceHalHidl.h new file mode 100644 index 0000000000..8651b511a3 --- /dev/null +++ b/media/libaudiohal/4.0/DeviceHalHidl.h @@ -0,0 +1,126 @@ +/* + * 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 ANDROID_HARDWARE_DEVICE_HAL_HIDL_H +#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H + +#include +#include +#include + +#include "ConversionHelperHidl.h" + +using ::android::hardware::audio::V2_0::IDevice; +using ::android::hardware::audio::V2_0::IPrimaryDevice; +using ::android::hardware::Return; + +namespace android { + +class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl +{ + public: + // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. + virtual status_t getSupportedDevices(uint32_t *devices); + + // Check to see if the audio hardware interface has been initialized. + virtual status_t initCheck(); + + // Set the audio volume of a voice call. Range is between 0.0 and 1.0. + virtual status_t setVoiceVolume(float volume); + + // Set the audio volume for all audio activities other than voice call. + virtual status_t setMasterVolume(float volume); + + // Get the current master volume value for the HAL. + virtual status_t getMasterVolume(float *volume); + + // Called when the audio mode changes. + virtual status_t setMode(audio_mode_t mode); + + // Muting control. + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool *state); + virtual status_t setMasterMute(bool state); + virtual status_t getMasterMute(bool *state); + + // Set global audio parameters. + virtual status_t setParameters(const String8& kvPairs); + + // Get global audio parameters. + virtual status_t getParameters(const String8& keys, String8 *values); + + // Returns audio input buffer size according to parameters passed. + virtual status_t getInputBufferSize(const struct audio_config *config, + size_t *size); + + // Creates and opens the audio hardware output stream. The stream is closed + // by releasing all references to the returned object. + virtual status_t openOutputStream( + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address, + sp *outStream); + + // Creates and opens the audio hardware input stream. The stream is closed + // by releasing all references to the returned object. + virtual status_t openInputStream( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + audio_input_flags_t flags, + const char *address, + audio_source_t source, + sp *inStream); + + // Returns whether createAudioPatch and releaseAudioPatch operations are supported. + virtual status_t supportsAudioPatches(bool *supportsPatches); + + // Creates an audio patch between several source and sink ports. + virtual status_t createAudioPatch( + unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *patch); + + // Releases an audio patch. + virtual status_t releaseAudioPatch(audio_patch_handle_t patch); + + // Fills the list of supported attributes for a given audio port. + virtual status_t getAudioPort(struct audio_port *port); + + // Set audio port configuration. + virtual status_t setAudioPortConfig(const struct audio_port_config *config); + + virtual status_t dump(int fd); + + private: + friend class DevicesFactoryHalHidl; + sp mDevice; + sp mPrimaryDevice; // Null if it's not a primary device. + + // Can not be constructed directly by clients. + explicit DeviceHalHidl(const sp& device); + + // The destructor automatically closes the device. + virtual ~DeviceHalHidl(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalLocal.cpp b/media/libaudiohal/4.0/DeviceHalLocal.cpp new file mode 100644 index 0000000000..fc098f5136 --- /dev/null +++ b/media/libaudiohal/4.0/DeviceHalLocal.cpp @@ -0,0 +1,199 @@ +/* + * 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. + */ + +#define LOG_TAG "DeviceHalLocal" +//#define LOG_NDEBUG 0 + +#include + +#include "DeviceHalLocal.h" +#include "StreamHalLocal.h" + +namespace android { + +DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) + : mDev(dev) { +} + +DeviceHalLocal::~DeviceHalLocal() { + int status = audio_hw_device_close(mDev); + ALOGW_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status)); + mDev = 0; +} + +status_t DeviceHalLocal::getSupportedDevices(uint32_t *devices) { + if (mDev->get_supported_devices == NULL) return INVALID_OPERATION; + *devices = mDev->get_supported_devices(mDev); + return OK; +} + +status_t DeviceHalLocal::initCheck() { + return mDev->init_check(mDev); +} + +status_t DeviceHalLocal::setVoiceVolume(float volume) { + return mDev->set_voice_volume(mDev, volume); +} + +status_t DeviceHalLocal::setMasterVolume(float volume) { + if (mDev->set_master_volume == NULL) return INVALID_OPERATION; + return mDev->set_master_volume(mDev, volume); +} + +status_t DeviceHalLocal::getMasterVolume(float *volume) { + if (mDev->get_master_volume == NULL) return INVALID_OPERATION; + return mDev->get_master_volume(mDev, volume); +} + +status_t DeviceHalLocal::setMode(audio_mode_t mode) { + return mDev->set_mode(mDev, mode); +} + +status_t DeviceHalLocal::setMicMute(bool state) { + return mDev->set_mic_mute(mDev, state); +} + +status_t DeviceHalLocal::getMicMute(bool *state) { + return mDev->get_mic_mute(mDev, state); +} + +status_t DeviceHalLocal::setMasterMute(bool state) { + if (mDev->set_master_mute == NULL) return INVALID_OPERATION; + return mDev->set_master_mute(mDev, state); +} + +status_t DeviceHalLocal::getMasterMute(bool *state) { + if (mDev->get_master_mute == NULL) return INVALID_OPERATION; + return mDev->get_master_mute(mDev, state); +} + +status_t DeviceHalLocal::setParameters(const String8& kvPairs) { + return mDev->set_parameters(mDev, kvPairs.string()); +} + +status_t DeviceHalLocal::getParameters(const String8& keys, String8 *values) { + char *halValues = mDev->get_parameters(mDev, keys.string()); + if (halValues != NULL) { + values->setTo(halValues); + free(halValues); + } else { + values->clear(); + } + return OK; +} + +status_t DeviceHalLocal::getInputBufferSize( + const struct audio_config *config, size_t *size) { + *size = mDev->get_input_buffer_size(mDev, config); + return OK; +} + +status_t DeviceHalLocal::openOutputStream( + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address, + sp *outStream) { + audio_stream_out_t *halStream; + ALOGV("open_output_stream handle: %d devices: %x flags: %#x" + "srate: %d format %#x channels %x address %s", + handle, devices, flags, + config->sample_rate, config->format, config->channel_mask, + address); + int openResut = mDev->open_output_stream( + mDev, handle, devices, flags, config, &halStream, address); + if (openResut == OK) { + *outStream = new StreamOutHalLocal(halStream, this); + } + ALOGV("open_output_stream status %d stream %p", openResut, halStream); + return openResut; +} + +status_t DeviceHalLocal::openInputStream( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + audio_input_flags_t flags, + const char *address, + audio_source_t source, + sp *inStream) { + audio_stream_in_t *halStream; + ALOGV("open_input_stream handle: %d devices: %x flags: %#x " + "srate: %d format %#x channels %x address %s source %d", + handle, devices, flags, + config->sample_rate, config->format, config->channel_mask, + address, source); + int openResult = mDev->open_input_stream( + mDev, handle, devices, config, &halStream, flags, address, source); + if (openResult == OK) { + *inStream = new StreamInHalLocal(halStream, this); + } + ALOGV("open_input_stream status %d stream %p", openResult, inStream); + return openResult; +} + +status_t DeviceHalLocal::supportsAudioPatches(bool *supportsPatches) { + *supportsPatches = version() >= AUDIO_DEVICE_API_VERSION_3_0; + return OK; +} + +status_t DeviceHalLocal::createAudioPatch( + unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *patch) { + if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { + return mDev->create_audio_patch( + mDev, num_sources, sources, num_sinks, sinks, patch); + } else { + return INVALID_OPERATION; + } +} + +status_t DeviceHalLocal::releaseAudioPatch(audio_patch_handle_t patch) { + if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { + return mDev->release_audio_patch(mDev, patch); + } else { + return INVALID_OPERATION; + } +} + +status_t DeviceHalLocal::getAudioPort(struct audio_port *port) { + return mDev->get_audio_port(mDev, port); +} + +status_t DeviceHalLocal::setAudioPortConfig(const struct audio_port_config *config) { + if (version() >= AUDIO_DEVICE_API_VERSION_3_0) + return mDev->set_audio_port_config(mDev, config); + else + return INVALID_OPERATION; +} + +status_t DeviceHalLocal::dump(int fd) { + return mDev->dump(mDev, fd); +} + +void DeviceHalLocal::closeOutputStream(struct audio_stream_out *stream_out) { + mDev->close_output_stream(mDev, stream_out); +} + +void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { + mDev->close_input_stream(mDev, stream_in); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/DeviceHalLocal.h b/media/libaudiohal/4.0/DeviceHalLocal.h new file mode 100644 index 0000000000..865f2968e1 --- /dev/null +++ b/media/libaudiohal/4.0/DeviceHalLocal.h @@ -0,0 +1,124 @@ +/* + * 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 ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H +#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H + +#include +#include + +namespace android { + +class DeviceHalLocal : public DeviceHalInterface +{ + public: + // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. + virtual status_t getSupportedDevices(uint32_t *devices); + + // Check to see if the audio hardware interface has been initialized. + virtual status_t initCheck(); + + // Set the audio volume of a voice call. Range is between 0.0 and 1.0. + virtual status_t setVoiceVolume(float volume); + + // Set the audio volume for all audio activities other than voice call. + virtual status_t setMasterVolume(float volume); + + // Get the current master volume value for the HAL. + virtual status_t getMasterVolume(float *volume); + + // Called when the audio mode changes. + virtual status_t setMode(audio_mode_t mode); + + // Muting control. + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool *state); + virtual status_t setMasterMute(bool state); + virtual status_t getMasterMute(bool *state); + + // Set global audio parameters. + virtual status_t setParameters(const String8& kvPairs); + + // Get global audio parameters. + virtual status_t getParameters(const String8& keys, String8 *values); + + // Returns audio input buffer size according to parameters passed. + virtual status_t getInputBufferSize(const struct audio_config *config, + size_t *size); + + // Creates and opens the audio hardware output stream. The stream is closed + // by releasing all references to the returned object. + virtual status_t openOutputStream( + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address, + sp *outStream); + + // Creates and opens the audio hardware input stream. The stream is closed + // by releasing all references to the returned object. + virtual status_t openInputStream( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + audio_input_flags_t flags, + const char *address, + audio_source_t source, + sp *inStream); + + // Returns whether createAudioPatch and releaseAudioPatch operations are supported. + virtual status_t supportsAudioPatches(bool *supportsPatches); + + // Creates an audio patch between several source and sink ports. + virtual status_t createAudioPatch( + unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *patch); + + // Releases an audio patch. + virtual status_t releaseAudioPatch(audio_patch_handle_t patch); + + // Fills the list of supported attributes for a given audio port. + virtual status_t getAudioPort(struct audio_port *port); + + // Set audio port configuration. + virtual status_t setAudioPortConfig(const struct audio_port_config *config); + + virtual status_t dump(int fd); + + void closeOutputStream(struct audio_stream_out *stream_out); + void closeInputStream(struct audio_stream_in *stream_in); + + private: + audio_hw_device_t *mDev; + + friend class DevicesFactoryHalLocal; + + // Can not be constructed directly by clients. + explicit DeviceHalLocal(audio_hw_device_t *dev); + + // The destructor automatically closes the device. + virtual ~DeviceHalLocal(); + + uint32_t version() const { return mDev->common.version; } +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp new file mode 100644 index 0000000000..5b335920bf --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#include + +#define LOG_TAG "DevicesFactoryHalHidl" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include "ConversionHelperHidl.h" +#include "DeviceHalHidl.h" +#include "DevicesFactoryHalHidl.h" + +using ::android::hardware::audio::V2_0::IDevice; +using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::Return; + +namespace android { + +DevicesFactoryHalHidl::DevicesFactoryHalHidl() { + mDevicesFactory = IDevicesFactory::getService(); + if (mDevicesFactory != 0) { + // It is assumed that DevicesFactory is owned by AudioFlinger + // and thus have the same lifespan. + mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); + } else { + ALOGE("Failed to obtain IDevicesFactory service, terminating process."); + exit(1); + } + // The MSD factory is optional + mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD); + // TODO: Register death handler, and add 'restart' directive to audioserver.rc +} + +DevicesFactoryHalHidl::~DevicesFactoryHalHidl() { +} + +// static +status_t DevicesFactoryHalHidl::nameFromHal(const char *name, IDevicesFactory::Device *device) { + if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) { + *device = IDevicesFactory::Device::PRIMARY; + return OK; + } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) { + *device = IDevicesFactory::Device::A2DP; + return OK; + } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) { + *device = IDevicesFactory::Device::USB; + return OK; + } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) { + *device = IDevicesFactory::Device::R_SUBMIX; + return OK; + } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_STUB) == 0) { + *device = IDevicesFactory::Device::STUB; + return OK; + } + ALOGE("Invalid device name %s", name); + return BAD_VALUE; +} + +status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { + if (mDevicesFactory == 0) return NO_INIT; + IDevicesFactory::Device hidlDevice; + status_t status = nameFromHal(name, &hidlDevice); + if (status != OK) return status; + Result retval = Result::NOT_INITIALIZED; + Return ret = mDevicesFactory->openDevice( + hidlDevice, + [&](Result r, const sp& result) { + retval = r; + if (retval == Result::OK) { + *device = new DeviceHalHidl(result); + } + }); + if (ret.isOk()) { + if (retval == Result::OK) return OK; + else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE; + else return NO_INIT; + } + return FAILED_TRANSACTION; +} + +} // namespace android diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h new file mode 100644 index 0000000000..0748849a5b --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h @@ -0,0 +1,54 @@ +/* + * 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 ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H + +#include +#include +#include +#include + +#include "DeviceHalHidl.h" + +using ::android::hardware::audio::V2_0::IDevicesFactory; + +namespace android { + +class DevicesFactoryHalHidl : public DevicesFactoryHalInterface +{ + public: + // Opens a device with the specified name. To close the device, it is + // necessary to release references to the returned object. + virtual status_t openDevice(const char *name, sp *device); + + private: + friend class DevicesFactoryHalHybrid; + + sp mDevicesFactory; + sp mDevicesFactoryMsd; + + static status_t nameFromHal(const char *name, IDevicesFactory::Device *device); + + // Can not be constructed directly by clients. + DevicesFactoryHalHidl(); + + virtual ~DevicesFactoryHalHidl(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp new file mode 100644 index 0000000000..77df6b5eaf --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DevicesFactoryHalHybrid" +//#define LOG_NDEBUG 0 + +#include "DevicesFactoryHalHybrid.h" +#include "DevicesFactoryHalLocal.h" +#include "DevicesFactoryHalHidl.h" + +namespace android { + +DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() + : mLocalFactory(new DevicesFactoryHalLocal()), + mHidlFactory(new DevicesFactoryHalHidl()) { +} + +DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() { +} + +status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp *device) { + if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) { + return mHidlFactory->openDevice(name, device); + } + return mLocalFactory->openDevice(name, device); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h new file mode 100644 index 0000000000..abd57d6446 --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H + +#include +#include +#include + +namespace android { + +class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface +{ + public: + // Opens a device with the specified name. To close the device, it is + // necessary to release references to the returned object. + virtual status_t openDevice(const char *name, sp *device); + + private: + friend class DevicesFactoryHalInterface; + + // Can not be constructed directly by clients. + DevicesFactoryHalHybrid(); + + virtual ~DevicesFactoryHalHybrid(); + + sp mLocalFactory; + sp mHidlFactory; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp new file mode 100644 index 0000000000..13a9acd9e9 --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#define LOG_TAG "DevicesFactoryHalLocal" +//#define LOG_NDEBUG 0 + +#include + +#include +#include + +#include "DeviceHalLocal.h" +#include "DevicesFactoryHalLocal.h" + +namespace android { + +static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) +{ + const hw_module_t *mod; + int rc; + + rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); + if (rc) { + ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, + AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); + goto out; + } + rc = audio_hw_device_open(mod, dev); + if (rc) { + ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, + AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); + goto out; + } + if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { + ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); + rc = BAD_VALUE; + audio_hw_device_close(*dev); + goto out; + } + return OK; + +out: + *dev = NULL; + return rc; +} + +status_t DevicesFactoryHalLocal::openDevice(const char *name, sp *device) { + audio_hw_device_t *dev; + status_t rc = load_audio_interface(name, &dev); + if (rc == OK) { + *device = new DeviceHalLocal(dev); + } + return rc; +} + +} // namespace android diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.h b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h new file mode 100644 index 0000000000..b9d18ab0b1 --- /dev/null +++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h @@ -0,0 +1,46 @@ +/* + * 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 ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H + +#include +#include +#include + +#include "DeviceHalLocal.h" + +namespace android { + +class DevicesFactoryHalLocal : public DevicesFactoryHalInterface +{ + public: + // Opens a device with the specified name. To close the device, it is + // necessary to release references to the returned object. + virtual status_t openDevice(const char *name, sp *device); + + private: + friend class DevicesFactoryHalHybrid; + + // Can not be constructed directly by clients. + DevicesFactoryHalLocal() {} + + virtual ~DevicesFactoryHalLocal() {} +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp new file mode 100644 index 0000000000..226a500225 --- /dev/null +++ b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "EffectBufferHalHidl" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include "ConversionHelperHidl.h" +#include "EffectBufferHalHidl.h" + +using ::android::hardware::Return; +using ::android::hidl::allocator::V1_0::IAllocator; + +namespace android { + +// static +uint64_t EffectBufferHalHidl::makeUniqueId() { + static std::atomic counter{1}; + return counter++; +} + +status_t EffectBufferHalHidl::allocate( + size_t size, sp* buffer) { + return mirror(nullptr, size, buffer); +} + +status_t EffectBufferHalHidl::mirror( + void* external, size_t size, sp* buffer) { + sp tempBuffer = new EffectBufferHalHidl(size); + status_t result = static_cast(tempBuffer.get())->init(); + if (result == OK) { + tempBuffer->setExternalData(external); + *buffer = tempBuffer; + } + return result; +} + +EffectBufferHalHidl::EffectBufferHalHidl(size_t size) + : mBufferSize(size), mFrameCountChanged(false), + mExternalData(nullptr), mAudioBuffer{0, {nullptr}} { + mHidlBuffer.id = makeUniqueId(); + mHidlBuffer.frameCount = 0; +} + +EffectBufferHalHidl::~EffectBufferHalHidl() { +} + +status_t EffectBufferHalHidl::init() { + sp ashmem = IAllocator::getService("ashmem"); + if (ashmem == 0) { + ALOGE("Failed to retrieve ashmem allocator service"); + return NO_INIT; + } + status_t retval = NO_MEMORY; + Return result = ashmem->allocate( + mBufferSize, + [&](bool success, const hidl_memory& memory) { + if (success) { + mHidlBuffer.data = memory; + retval = OK; + } + }); + if (result.isOk() && retval == OK) { + mMemory = hardware::mapMemory(mHidlBuffer.data); + if (mMemory != 0) { + mMemory->update(); + mAudioBuffer.raw = static_cast(mMemory->getPointer()); + memset(mAudioBuffer.raw, 0, mMemory->getSize()); + mMemory->commit(); + } else { + ALOGE("Failed to map allocated ashmem"); + retval = NO_MEMORY; + } + } else { + ALOGE("Failed to allocate %d bytes from ashmem", (int)mBufferSize); + } + return result.isOk() ? retval : FAILED_TRANSACTION; +} + +audio_buffer_t* EffectBufferHalHidl::audioBuffer() { + return &mAudioBuffer; +} + +void* EffectBufferHalHidl::externalData() const { + return mExternalData; +} + +void EffectBufferHalHidl::setFrameCount(size_t frameCount) { + mHidlBuffer.frameCount = frameCount; + mAudioBuffer.frameCount = frameCount; + mFrameCountChanged = true; +} + +bool EffectBufferHalHidl::checkFrameCountChange() { + bool result = mFrameCountChanged; + mFrameCountChanged = false; + return result; +} + +void EffectBufferHalHidl::setExternalData(void* external) { + mExternalData = external; +} + +void EffectBufferHalHidl::update() { + update(mBufferSize); +} + +void EffectBufferHalHidl::commit() { + commit(mBufferSize); +} + +void EffectBufferHalHidl::update(size_t size) { + if (mExternalData == nullptr) return; + mMemory->update(); + if (size > mBufferSize) size = mBufferSize; + memcpy(mAudioBuffer.raw, mExternalData, size); + mMemory->commit(); +} + +void EffectBufferHalHidl::commit(size_t size) { + if (mExternalData == nullptr) return; + if (size > mBufferSize) size = mBufferSize; + memcpy(mExternalData, mAudioBuffer.raw, size); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.h b/media/libaudiohal/4.0/EffectBufferHalHidl.h new file mode 100644 index 0000000000..31e0087c23 --- /dev/null +++ b/media/libaudiohal/4.0/EffectBufferHalHidl.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H + +#include +#include +#include +#include +#include + +using android::hardware::audio::effect::V2_0::AudioBuffer; +using android::hardware::hidl_memory; +using android::hidl::memory::V1_0::IMemory; + +namespace android { + +class EffectBufferHalHidl : public EffectBufferHalInterface +{ + public: + static status_t allocate(size_t size, sp* buffer); + static status_t mirror(void* external, size_t size, sp* buffer); + + virtual audio_buffer_t* audioBuffer(); + virtual void* externalData() const; + + virtual size_t getSize() const override { return mBufferSize; } + + virtual void setExternalData(void* external); + virtual void setFrameCount(size_t frameCount); + virtual bool checkFrameCountChange(); + + virtual void update(); + virtual void commit(); + virtual void update(size_t size); + virtual void commit(size_t size); + + const AudioBuffer& hidlBuffer() const { return mHidlBuffer; } + + private: + friend class EffectBufferHalInterface; + + static uint64_t makeUniqueId(); + + const size_t mBufferSize; + bool mFrameCountChanged; + void* mExternalData; + AudioBuffer mHidlBuffer; + sp mMemory; + audio_buffer_t mAudioBuffer; + + // Can not be constructed directly by clients. + explicit EffectBufferHalHidl(size_t size); + + virtual ~EffectBufferHalHidl(); + + status_t init(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp new file mode 100644 index 0000000000..4fb032c7cb --- /dev/null +++ b/media/libaudiohal/4.0/EffectHalHidl.cpp @@ -0,0 +1,338 @@ +/* + * 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. + */ + +#define LOG_TAG "EffectHalHidl" +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include "ConversionHelperHidl.h" +#include "EffectBufferHalHidl.h" +#include "EffectHalHidl.h" +#include "HidlUtils.h" + +using ::android::hardware::audio::effect::V2_0::AudioBuffer; +using ::android::hardware::audio::effect::V2_0::EffectBufferAccess; +using ::android::hardware::audio::effect::V2_0::EffectConfigParameters; +using ::android::hardware::audio::effect::V2_0::MessageQueueFlagBits; +using ::android::hardware::audio::effect::V2_0::Result; +using ::android::hardware::audio::common::V2_0::HidlUtils; +using ::android::hardware::audio::common::V2_0::AudioChannelMask; +using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::hidl_vec; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::Return; + +namespace android { + +EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) + : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { +} + +EffectHalHidl::~EffectHalHidl() { + if (mEffect != 0) { + close(); + mEffect.clear(); + hardware::IPCThreadState::self()->flushCommands(); + } + if (mEfGroup) { + EventFlag::deleteEventFlag(&mEfGroup); + } +} + +// static +void EffectHalHidl::effectDescriptorToHal( + const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor) { + HidlUtils::uuidToHal(descriptor.type, &halDescriptor->type); + HidlUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid); + halDescriptor->flags = static_cast(descriptor.flags); + halDescriptor->cpuLoad = descriptor.cpuLoad; + halDescriptor->memoryUsage = descriptor.memoryUsage; + memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size()); + memcpy(halDescriptor->implementor, + descriptor.implementor.data(), descriptor.implementor.size()); +} + +// TODO(mnaganov): These buffer conversion functions should be shared with Effect wrapper +// via HidlUtils. Move them there when hardware/interfaces will get un-frozen again. + +// static +void EffectHalHidl::effectBufferConfigFromHal( + const buffer_config_t& halConfig, EffectBufferConfig* config) { + config->samplingRateHz = halConfig.samplingRate; + config->channels = AudioChannelMask(halConfig.channels); + config->format = AudioFormat(halConfig.format); + config->accessMode = EffectBufferAccess(halConfig.accessMode); + config->mask = EffectConfigParameters(halConfig.mask); +} + +// static +void EffectHalHidl::effectBufferConfigToHal( + const EffectBufferConfig& config, buffer_config_t* halConfig) { + halConfig->buffer.frameCount = 0; + halConfig->buffer.raw = NULL; + halConfig->samplingRate = config.samplingRateHz; + halConfig->channels = static_cast(config.channels); + halConfig->bufferProvider.cookie = NULL; + halConfig->bufferProvider.getBuffer = NULL; + halConfig->bufferProvider.releaseBuffer = NULL; + halConfig->format = static_cast(config.format); + halConfig->accessMode = static_cast(config.accessMode); + halConfig->mask = static_cast(config.mask); +} + +// static +void EffectHalHidl::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) { + effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg); + effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg); +} + +// static +void EffectHalHidl::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) { + effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg); + effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg); +} + +// static +status_t EffectHalHidl::analyzeResult(const Result& result) { + switch (result) { + case Result::OK: return OK; + case Result::INVALID_ARGUMENTS: return BAD_VALUE; + case Result::INVALID_STATE: return NOT_ENOUGH_DATA; + case Result::NOT_INITIALIZED: return NO_INIT; + case Result::NOT_SUPPORTED: return INVALID_OPERATION; + case Result::RESULT_TOO_BIG: return NO_MEMORY; + default: return NO_INIT; + } +} + +status_t EffectHalHidl::setInBuffer(const sp& buffer) { + if (!mBuffersChanged) { + if (buffer.get() == nullptr || mInBuffer.get() == nullptr) { + mBuffersChanged = buffer.get() != mInBuffer.get(); + } else { + mBuffersChanged = buffer->audioBuffer() != mInBuffer->audioBuffer(); + } + } + mInBuffer = buffer; + return OK; +} + +status_t EffectHalHidl::setOutBuffer(const sp& buffer) { + if (!mBuffersChanged) { + if (buffer.get() == nullptr || mOutBuffer.get() == nullptr) { + mBuffersChanged = buffer.get() != mOutBuffer.get(); + } else { + mBuffersChanged = buffer->audioBuffer() != mOutBuffer->audioBuffer(); + } + } + mOutBuffer = buffer; + return OK; +} + +status_t EffectHalHidl::process() { + return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS)); +} + +status_t EffectHalHidl::processReverse() { + return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE)); +} + +status_t EffectHalHidl::prepareForProcessing() { + std::unique_ptr tempStatusMQ; + Result retval; + Return ret = mEffect->prepareForProcessing( + [&](Result r, const MQDescriptorSync& statusMQ) { + retval = r; + if (retval == Result::OK) { + tempStatusMQ.reset(new StatusMQ(statusMQ)); + if (tempStatusMQ->isValid() && tempStatusMQ->getEventFlagWord()) { + EventFlag::createEventFlag(tempStatusMQ->getEventFlagWord(), &mEfGroup); + } + } + }); + if (!ret.isOk() || retval != Result::OK) { + return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; + } + if (!tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) { + ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for effects"); + ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), + "Status message queue for effects is invalid"); + ALOGE_IF(!mEfGroup, "Event flag creation for effects failed"); + return NO_INIT; + } + mStatusMQ = std::move(tempStatusMQ); + return OK; +} + +bool EffectHalHidl::needToResetBuffers() { + if (mBuffersChanged) return true; + bool inBufferFrameCountUpdated = mInBuffer->checkFrameCountChange(); + bool outBufferFrameCountUpdated = mOutBuffer->checkFrameCountChange(); + return inBufferFrameCountUpdated || outBufferFrameCountUpdated; +} + +status_t EffectHalHidl::processImpl(uint32_t mqFlag) { + if (mEffect == 0 || mInBuffer == 0 || mOutBuffer == 0) return NO_INIT; + status_t status; + if (!mStatusMQ && (status = prepareForProcessing()) != OK) { + return status; + } + if (needToResetBuffers() && (status = setProcessBuffers()) != OK) { + return status; + } + // The data is already in the buffers, just need to flush it and wake up the server side. + std::atomic_thread_fence(std::memory_order_release); + mEfGroup->wake(mqFlag); + uint32_t efState = 0; +retry: + status_t ret = mEfGroup->wait( + static_cast(MessageQueueFlagBits::DONE_PROCESSING), &efState); + if (efState & static_cast(MessageQueueFlagBits::DONE_PROCESSING)) { + Result retval = Result::NOT_INITIALIZED; + mStatusMQ->read(&retval); + if (retval == Result::OK || retval == Result::INVALID_STATE) { + // Sync back the changed contents of the buffer. + std::atomic_thread_fence(std::memory_order_acquire); + } + return analyzeResult(retval); + } + if (ret == -EAGAIN || ret == -EINTR) { + // Spurious wakeup. This normally retries no more than once. + goto retry; + } + return ret; +} + +status_t EffectHalHidl::setProcessBuffers() { + Return ret = mEffect->setProcessBuffers( + static_cast(mInBuffer.get())->hidlBuffer(), + static_cast(mOutBuffer.get())->hidlBuffer()); + if (ret.isOk() && ret == Result::OK) { + mBuffersChanged = false; + return OK; + } + return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; +} + +status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, + uint32_t *replySize, void *pReplyData) { + if (mEffect == 0) return NO_INIT; + + // Special cases. + if (cmdCode == EFFECT_CMD_SET_CONFIG || cmdCode == EFFECT_CMD_SET_CONFIG_REVERSE) { + return setConfigImpl(cmdCode, cmdSize, pCmdData, replySize, pReplyData); + } else if (cmdCode == EFFECT_CMD_GET_CONFIG || cmdCode == EFFECT_CMD_GET_CONFIG_REVERSE) { + return getConfigImpl(cmdCode, replySize, pReplyData); + } + + // Common case. + hidl_vec hidlData; + if (pCmdData != nullptr && cmdSize > 0) { + hidlData.setToExternal(reinterpret_cast(pCmdData), cmdSize); + } + status_t status; + uint32_t replySizeStub = 0; + if (replySize == nullptr || pReplyData == nullptr) replySize = &replySizeStub; + Return ret = mEffect->command(cmdCode, hidlData, *replySize, + [&](int32_t s, const hidl_vec& result) { + status = s; + if (status == 0) { + if (*replySize > result.size()) *replySize = result.size(); + if (pReplyData != nullptr && *replySize > 0) { + memcpy(pReplyData, &result[0], *replySize); + } + } + }); + return ret.isOk() ? status : FAILED_TRANSACTION; +} + +status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) { + if (mEffect == 0) return NO_INIT; + Result retval = Result::NOT_INITIALIZED; + Return ret = mEffect->getDescriptor( + [&](Result r, const EffectDescriptor& result) { + retval = r; + if (retval == Result::OK) { + effectDescriptorToHal(result, pDescriptor); + } + }); + return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; +} + +status_t EffectHalHidl::close() { + if (mEffect == 0) return NO_INIT; + Return ret = mEffect->close(); + return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; +} + +status_t EffectHalHidl::getConfigImpl( + uint32_t cmdCode, uint32_t *replySize, void *pReplyData) { + if (replySize == NULL || *replySize != sizeof(effect_config_t) || pReplyData == NULL) { + return BAD_VALUE; + } + status_t result = FAILED_TRANSACTION; + Return ret; + if (cmdCode == EFFECT_CMD_GET_CONFIG) { + ret = mEffect->getConfig([&] (Result r, const EffectConfig &hidlConfig) { + result = analyzeResult(r); + if (r == Result::OK) { + effectConfigToHal(hidlConfig, static_cast(pReplyData)); + } + }); + } else { + ret = mEffect->getConfigReverse([&] (Result r, const EffectConfig &hidlConfig) { + result = analyzeResult(r); + if (r == Result::OK) { + effectConfigToHal(hidlConfig, static_cast(pReplyData)); + } + }); + } + if (!ret.isOk()) { + result = FAILED_TRANSACTION; + } + return result; +} + +status_t EffectHalHidl::setConfigImpl( + uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData) { + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) || + replySize == NULL || *replySize != sizeof(int32_t) || pReplyData == NULL) { + return BAD_VALUE; + } + const effect_config_t *halConfig = static_cast(pCmdData); + if (halConfig->inputCfg.bufferProvider.getBuffer != NULL || + halConfig->inputCfg.bufferProvider.releaseBuffer != NULL || + halConfig->outputCfg.bufferProvider.getBuffer != NULL || + halConfig->outputCfg.bufferProvider.releaseBuffer != NULL) { + ALOGE("Buffer provider callbacks are not supported"); + } + EffectConfig hidlConfig; + effectConfigFromHal(*halConfig, &hidlConfig); + Return ret = cmdCode == EFFECT_CMD_SET_CONFIG ? + mEffect->setConfig(hidlConfig, nullptr, nullptr) : + mEffect->setConfigReverse(hidlConfig, nullptr, nullptr); + status_t result = FAILED_TRANSACTION; + if (ret.isOk()) { + result = analyzeResult(ret); + *static_cast(pReplyData) = result; + } + return result; +} + +} // namespace android diff --git a/media/libaudiohal/4.0/EffectHalHidl.h b/media/libaudiohal/4.0/EffectHalHidl.h new file mode 100644 index 0000000000..6ffdaf1f64 --- /dev/null +++ b/media/libaudiohal/4.0/EffectHalHidl.h @@ -0,0 +1,108 @@ +/* + * 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 ANDROID_HARDWARE_EFFECT_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H + +#include +#include +#include +#include +#include + +using ::android::hardware::audio::effect::V2_0::EffectBufferConfig; +using ::android::hardware::audio::effect::V2_0::EffectConfig; +using ::android::hardware::audio::effect::V2_0::EffectDescriptor; +using ::android::hardware::audio::effect::V2_0::IEffect; +using ::android::hardware::EventFlag; +using ::android::hardware::MessageQueue; + +namespace android { + +class EffectHalHidl : public EffectHalInterface +{ + public: + // Set the input buffer. + virtual status_t setInBuffer(const sp& buffer); + + // Set the output buffer. + virtual status_t setOutBuffer(const sp& buffer); + + // Effect process function. + virtual status_t process(); + + // Process reverse stream function. This function is used to pass + // a reference stream to the effect engine. + virtual status_t processReverse(); + + // Send a command and receive a response to/from effect engine. + virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, + uint32_t *replySize, void *pReplyData); + + // Returns the effect descriptor. + virtual status_t getDescriptor(effect_descriptor_t *pDescriptor); + + // Free resources on the remote side. + virtual status_t close(); + + // Whether it's a local implementation. + virtual bool isLocal() const { return false; } + + uint64_t effectId() const { return mEffectId; } + + static void effectDescriptorToHal( + const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor); + + private: + friend class EffectsFactoryHalHidl; + typedef MessageQueue< + hardware::audio::effect::V2_0::Result, hardware::kSynchronizedReadWrite> StatusMQ; + + sp mEffect; + const uint64_t mEffectId; + sp mInBuffer; + sp mOutBuffer; + bool mBuffersChanged; + std::unique_ptr mStatusMQ; + EventFlag* mEfGroup; + + static status_t analyzeResult(const hardware::audio::effect::V2_0::Result& result); + static void effectBufferConfigFromHal( + const buffer_config_t& halConfig, EffectBufferConfig* config); + static void effectBufferConfigToHal( + const EffectBufferConfig& config, buffer_config_t* halConfig); + static void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config); + static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig); + + // Can not be constructed directly by clients. + EffectHalHidl(const sp& effect, uint64_t effectId); + + // The destructor automatically releases the effect. + virtual ~EffectHalHidl(); + + status_t getConfigImpl(uint32_t cmdCode, uint32_t *replySize, void *pReplyData); + status_t prepareForProcessing(); + bool needToResetBuffers(); + status_t processImpl(uint32_t mqFlag); + status_t setConfigImpl( + uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, + uint32_t *replySize, void *pReplyData); + status_t setProcessBuffers(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp new file mode 100644 index 0000000000..0d40e6d9d0 --- /dev/null +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp @@ -0,0 +1,150 @@ +/* + * 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. + */ + +#define LOG_TAG "EffectsFactoryHalHidl" +//#define LOG_NDEBUG 0 + +#include + +#include "ConversionHelperHidl.h" +#include "EffectBufferHalHidl.h" +#include "EffectHalHidl.h" +#include "EffectsFactoryHalHidl.h" +#include "HidlUtils.h" + +using ::android::hardware::audio::common::V2_0::HidlUtils; +using ::android::hardware::audio::common::V2_0::Uuid; +using ::android::hardware::audio::effect::V2_0::IEffect; +using ::android::hardware::audio::effect::V2_0::Result; +using ::android::hardware::Return; + +namespace android { + +EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { + mEffectsFactory = IEffectsFactory::getService(); + if (mEffectsFactory == 0) { + ALOGE("Failed to obtain IEffectsFactory service, terminating process."); + exit(1); + } +} + +EffectsFactoryHalHidl::~EffectsFactoryHalHidl() { +} + +status_t EffectsFactoryHalHidl::queryAllDescriptors() { + if (mEffectsFactory == 0) return NO_INIT; + Result retval = Result::NOT_INITIALIZED; + Return ret = mEffectsFactory->getAllDescriptors( + [&](Result r, const hidl_vec& result) { + retval = r; + if (retval == Result::OK) { + mLastDescriptors = result; + } + }); + if (ret.isOk()) { + return retval == Result::OK ? OK : NO_INIT; + } + mLastDescriptors.resize(0); + return processReturn(__FUNCTION__, ret); +} + +status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) { + status_t queryResult = queryAllDescriptors(); + if (queryResult == OK) { + *pNumEffects = mLastDescriptors.size(); + } + return queryResult; +} + +status_t EffectsFactoryHalHidl::getDescriptor( + uint32_t index, effect_descriptor_t *pDescriptor) { + // TODO: We need somehow to track the changes on the server side + // or figure out how to convert everybody to query all the descriptors at once. + // TODO: check for nullptr + if (mLastDescriptors.size() == 0) { + status_t queryResult = queryAllDescriptors(); + if (queryResult != OK) return queryResult; + } + if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND; + EffectHalHidl::effectDescriptorToHal(mLastDescriptors[index], pDescriptor); + return OK; +} + +status_t EffectsFactoryHalHidl::getDescriptor( + const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) { + // TODO: check for nullptr + if (mEffectsFactory == 0) return NO_INIT; + Uuid hidlUuid; + HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid); + Result retval = Result::NOT_INITIALIZED; + Return ret = mEffectsFactory->getDescriptor(hidlUuid, + [&](Result r, const EffectDescriptor& result) { + retval = r; + if (retval == Result::OK) { + EffectHalHidl::effectDescriptorToHal(result, pDescriptor); + } + }); + if (ret.isOk()) { + if (retval == Result::OK) return OK; + else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND; + else return NO_INIT; + } + return processReturn(__FUNCTION__, ret); +} + +status_t EffectsFactoryHalHidl::createEffect( + const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, + sp *effect) { + if (mEffectsFactory == 0) return NO_INIT; + Uuid hidlUuid; + HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid); + Result retval = Result::NOT_INITIALIZED; + Return ret = mEffectsFactory->createEffect( + hidlUuid, sessionId, ioId, + [&](Result r, const sp& result, uint64_t effectId) { + retval = r; + if (retval == Result::OK) { + *effect = new EffectHalHidl(result, effectId); + } + }); + if (ret.isOk()) { + if (retval == Result::OK) return OK; + else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND; + else return NO_INIT; + } + return processReturn(__FUNCTION__, ret); +} + +status_t EffectsFactoryHalHidl::dumpEffects(int fd) { + if (mEffectsFactory == 0) return NO_INIT; + native_handle_t* hidlHandle = native_handle_create(1, 0); + hidlHandle->data[0] = fd; + Return ret = mEffectsFactory->debugDump(hidlHandle); + native_handle_delete(hidlHandle); + return processReturn(__FUNCTION__, ret); +} + +status_t EffectsFactoryHalHidl::allocateBuffer(size_t size, sp* buffer) { + return EffectBufferHalHidl::allocate(size, buffer); +} + +status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, + sp* buffer) { + return EffectBufferHalHidl::mirror(external, size, buffer); +} + + +} // namespace android diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h new file mode 100644 index 0000000000..82b54814b1 --- /dev/null +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h @@ -0,0 +1,73 @@ +/* + * 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 ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H + +#include +#include +#include + +#include "ConversionHelperHidl.h" + +namespace android { + +using ::android::hardware::audio::effect::V2_0::EffectDescriptor; +using ::android::hardware::audio::effect::V2_0::IEffectsFactory; +using ::android::hardware::hidl_vec; + +class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl +{ + public: + // Returns the number of different effects in all loaded libraries. + virtual status_t queryNumberEffects(uint32_t *pNumEffects); + + // Returns a descriptor of the next available effect. + virtual status_t getDescriptor(uint32_t index, + effect_descriptor_t *pDescriptor); + + virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid, + effect_descriptor_t *pDescriptor); + + // Creates an effect engine of the specified type. + // To release the effect engine, it is necessary to release references + // to the returned effect object. + virtual status_t createEffect(const effect_uuid_t *pEffectUuid, + int32_t sessionId, int32_t ioId, + sp *effect); + + virtual status_t dumpEffects(int fd); + + status_t allocateBuffer(size_t size, sp* buffer) override; + status_t mirrorBuffer(void* external, size_t size, + sp* buffer) override; + + private: + friend class EffectsFactoryHalInterface; + + sp mEffectsFactory; + hidl_vec mLastDescriptors; + + // Can not be constructed directly by clients. + EffectsFactoryHalHidl(); + virtual ~EffectsFactoryHalHidl(); + + status_t queryAllDescriptors(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/4.0/StreamHalHidl.cpp new file mode 100644 index 0000000000..0cafa365e2 --- /dev/null +++ b/media/libaudiohal/4.0/StreamHalHidl.cpp @@ -0,0 +1,752 @@ +/* + * 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. + */ + +#define LOG_TAG "StreamHalHidl" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include + +#include "DeviceHalHidl.h" +#include "EffectHalHidl.h" +#include "StreamHalHidl.h" + +using ::android::hardware::audio::common::V2_0::AudioChannelMask; +using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::audio::common::V2_0::ThreadInfo; +using ::android::hardware::audio::V2_0::AudioDrain; +using ::android::hardware::audio::V2_0::IStreamOutCallback; +using ::android::hardware::audio::V2_0::MessageQueueFlagBits; +using ::android::hardware::audio::V2_0::MmapBufferInfo; +using ::android::hardware::audio::V2_0::MmapPosition; +using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::V2_0::TimeSpec; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ReadCommand = ::android::hardware::audio::V2_0::IStreamIn::ReadCommand; + +namespace android { + +StreamHalHidl::StreamHalHidl(IStream *stream) + : ConversionHelperHidl("Stream"), + mStream(stream), + mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT), + mCachedBufferSize(0){ + + // Instrument audio signal power logging. + // Note: This assumes channel mask, format, and sample rate do not change after creation. + if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { + // Obtain audio properties (see StreamHalHidl::getAudioProperties() below). + Return ret = mStream->getAudioProperties( + [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + mStreamPowerLog.init(sr, + static_cast(m), + static_cast(f)); + }); + } +} + +StreamHalHidl::~StreamHalHidl() { + mStream = nullptr; +} + +status_t StreamHalHidl::getSampleRate(uint32_t *rate) { + if (!mStream) return NO_INIT; + return processReturn("getSampleRate", mStream->getSampleRate(), rate); +} + +status_t StreamHalHidl::getBufferSize(size_t *size) { + if (!mStream) return NO_INIT; + status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size); + if (status == OK) { + mCachedBufferSize = *size; + } + return status; +} + +status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) { + if (!mStream) return NO_INIT; + return processReturn("getChannelMask", mStream->getChannelMask(), mask); +} + +status_t StreamHalHidl::getFormat(audio_format_t *format) { + if (!mStream) return NO_INIT; + return processReturn("getFormat", mStream->getFormat(), format); +} + +status_t StreamHalHidl::getAudioProperties( + uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { + if (!mStream) return NO_INIT; + Return ret = mStream->getAudioProperties( + [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + *sampleRate = sr; + *mask = static_cast(m); + *format = static_cast(f); + }); + return processReturn("getAudioProperties", ret); +} + +status_t StreamHalHidl::setParameters(const String8& kvPairs) { + if (!mStream) return NO_INIT; + hidl_vec hidlParams; + status_t status = parametersFromHal(kvPairs, &hidlParams); + if (status != OK) return status; + return processReturn("setParameters", mStream->setParameters(hidlParams)); +} + +status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { + values->clear(); + if (!mStream) return NO_INIT; + hidl_vec hidlKeys; + status_t status = keysFromHal(keys, &hidlKeys); + if (status != OK) return status; + Result retval; + Return ret = mStream->getParameters( + hidlKeys, + [&](Result r, const hidl_vec& parameters) { + retval = r; + if (retval == Result::OK) { + parametersToHal(parameters, values); + } + }); + return processReturn("getParameters", ret, retval); +} + +status_t StreamHalHidl::addEffect(sp effect) { + if (!mStream) return NO_INIT; + return processReturn("addEffect", mStream->addEffect( + static_cast(effect.get())->effectId())); +} + +status_t StreamHalHidl::removeEffect(sp effect) { + if (!mStream) return NO_INIT; + return processReturn("removeEffect", mStream->removeEffect( + static_cast(effect.get())->effectId())); +} + +status_t StreamHalHidl::standby() { + if (!mStream) return NO_INIT; + return processReturn("standby", mStream->standby()); +} + +status_t StreamHalHidl::dump(int fd) { + if (!mStream) return NO_INIT; + native_handle_t* hidlHandle = native_handle_create(1, 0); + hidlHandle->data[0] = fd; + Return ret = mStream->debugDump(hidlHandle); + native_handle_delete(hidlHandle); + mStreamPowerLog.dump(fd); + return processReturn("dump", ret); +} + +status_t StreamHalHidl::start() { + if (!mStream) return NO_INIT; + return processReturn("start", mStream->start()); +} + +status_t StreamHalHidl::stop() { + if (!mStream) return NO_INIT; + return processReturn("stop", mStream->stop()); +} + +status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info) { + Result retval; + Return ret = mStream->createMmapBuffer( + minSizeFrames, + [&](Result r, const MmapBufferInfo& hidlInfo) { + retval = r; + if (retval == Result::OK) { + const native_handle *handle = hidlInfo.sharedMemory.handle(); + if (handle->numFds > 0) { + info->shared_memory_fd = handle->data[0]; + info->buffer_size_frames = hidlInfo.bufferSizeFrames; + info->burst_size_frames = hidlInfo.burstSizeFrames; + // info->shared_memory_address is not needed in HIDL context + info->shared_memory_address = NULL; + } else { + retval = Result::NOT_INITIALIZED; + } + } + }); + return processReturn("createMmapBuffer", ret, retval); +} + +status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) { + Result retval; + Return ret = mStream->getMmapPosition( + [&](Result r, const MmapPosition& hidlPosition) { + retval = r; + if (retval == Result::OK) { + position->time_nanoseconds = hidlPosition.timeNanoseconds; + position->position_frames = hidlPosition.positionFrames; + } + }); + return processReturn("getMmapPosition", ret, retval); +} + +status_t StreamHalHidl::setHalThreadPriority(int priority) { + mHalThreadPriority = priority; + return OK; +} + +status_t StreamHalHidl::getCachedBufferSize(size_t *size) { + if (mCachedBufferSize != 0) { + *size = mCachedBufferSize; + return OK; + } + return getBufferSize(size); +} + +bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) { + if (mHalThreadPriority == HAL_THREAD_PRIORITY_DEFAULT) { + return true; + } + int err = requestPriority( + threadPid, threadId, + mHalThreadPriority, false /*isForApp*/, true /*asynchronous*/); + ALOGE_IF(err, "failed to set priority %d for pid %d tid %d; error %d", + mHalThreadPriority, threadPid, threadId, err); + // Audio will still work, but latency will be higher and sometimes unacceptable. + return err == 0; +} + +namespace { + +/* Notes on callback ownership. + +This is how (Hw)Binder ownership model looks like. The server implementation +is owned by Binder framework (via sp<>). Proxies are owned by clients. +When the last proxy disappears, Binder framework releases the server impl. + +Thus, it is not needed to keep any references to StreamOutCallback (this is +the server impl) -- it will live as long as HAL server holds a strong ref to +IStreamOutCallback proxy. We clear that reference by calling 'clearCallback' +from the destructor of StreamOutHalHidl. + +The callback only keeps a weak reference to the stream. The stream is owned +by AudioFlinger. + +*/ + +struct StreamOutCallback : public IStreamOutCallback { + StreamOutCallback(const wp& stream) : mStream(stream) {} + + // IStreamOutCallback implementation + Return onWriteReady() override { + sp stream = mStream.promote(); + if (stream != 0) { + stream->onWriteReady(); + } + return Void(); + } + + Return onDrainReady() override { + sp stream = mStream.promote(); + if (stream != 0) { + stream->onDrainReady(); + } + return Void(); + } + + Return onError() override { + sp stream = mStream.promote(); + if (stream != 0) { + stream->onError(); + } + return Void(); + } + + private: + wp mStream; +}; + +} // namespace + +StreamOutHalHidl::StreamOutHalHidl(const sp& stream) + : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) { +} + +StreamOutHalHidl::~StreamOutHalHidl() { + if (mStream != 0) { + if (mCallback.unsafe_get()) { + processReturn("clearCallback", mStream->clearCallback()); + } + processReturn("close", mStream->close()); + mStream.clear(); + } + mCallback.clear(); + hardware::IPCThreadState::self()->flushCommands(); + if (mEfGroup) { + EventFlag::deleteEventFlag(&mEfGroup); + } +} + +status_t StreamOutHalHidl::getFrameSize(size_t *size) { + if (mStream == 0) return NO_INIT; + return processReturn("getFrameSize", mStream->getFrameSize(), size); +} + +status_t StreamOutHalHidl::getLatency(uint32_t *latency) { + if (mStream == 0) return NO_INIT; + if (mWriterClient == gettid() && mCommandMQ) { + return callWriterThread( + WriteCommand::GET_LATENCY, "getLatency", nullptr, 0, + [&](const WriteStatus& writeStatus) { + *latency = writeStatus.reply.latencyMs; + }); + } else { + return processReturn("getLatency", mStream->getLatency(), latency); + } +} + +status_t StreamOutHalHidl::setVolume(float left, float right) { + if (mStream == 0) return NO_INIT; + return processReturn("setVolume", mStream->setVolume(left, right)); +} + +status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) { + if (mStream == 0) return NO_INIT; + *written = 0; + + if (bytes == 0 && !mDataMQ) { + // Can't determine the size for the MQ buffer. Wait for a non-empty write request. + ALOGW_IF(mCallback.unsafe_get(), "First call to async write with 0 bytes"); + return OK; + } + + status_t status; + if (!mDataMQ) { + // In case if playback starts close to the end of a compressed track, the bytes + // that need to be written is less than the actual buffer size. Need to use + // full buffer size for the MQ since otherwise after seeking back to the middle + // data will be truncated. + size_t bufferSize; + if ((status = getCachedBufferSize(&bufferSize)) != OK) { + return status; + } + if (bytes > bufferSize) bufferSize = bytes; + if ((status = prepareForWriting(bufferSize)) != OK) { + return status; + } + } + + status = callWriterThread( + WriteCommand::WRITE, "write", static_cast(buffer), bytes, + [&] (const WriteStatus& writeStatus) { + *written = writeStatus.reply.written; + // Diagnostics of the cause of b/35813113. + ALOGE_IF(*written > bytes, + "hal reports more bytes written than asked for: %lld > %lld", + (long long)*written, (long long)bytes); + }); + mStreamPowerLog.log(buffer, *written); + return status; +} + +status_t StreamOutHalHidl::callWriterThread( + WriteCommand cmd, const char* cmdName, + const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) { + if (!mCommandMQ->write(&cmd)) { + ALOGE("command message queue write failed for \"%s\"", cmdName); + return -EAGAIN; + } + if (data != nullptr) { + size_t availableToWrite = mDataMQ->availableToWrite(); + if (dataSize > availableToWrite) { + ALOGW("truncating write data from %lld to %lld due to insufficient data queue space", + (long long)dataSize, (long long)availableToWrite); + dataSize = availableToWrite; + } + if (!mDataMQ->write(data, dataSize)) { + ALOGE("data message queue write failed for \"%s\"", cmdName); + } + } + mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_EMPTY)); + + // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 + uint32_t efState = 0; +retry: + status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_FULL), &efState); + if (efState & static_cast(MessageQueueFlagBits::NOT_FULL)) { + WriteStatus writeStatus; + writeStatus.retval = Result::NOT_INITIALIZED; + if (!mStatusMQ->read(&writeStatus)) { + ALOGE("status message read failed for \"%s\"", cmdName); + } + if (writeStatus.retval == Result::OK) { + ret = OK; + callback(writeStatus); + } else { + ret = processReturn(cmdName, writeStatus.retval); + } + return ret; + } + if (ret == -EAGAIN || ret == -EINTR) { + // Spurious wakeup. This normally retries no more than once. + goto retry; + } + return ret; +} + +status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) { + std::unique_ptr tempCommandMQ; + std::unique_ptr tempDataMQ; + std::unique_ptr tempStatusMQ; + Result retval; + pid_t halThreadPid, halThreadTid; + Return ret = mStream->prepareForWriting( + 1, bufferSize, + [&](Result r, + const CommandMQ::Descriptor& commandMQ, + const DataMQ::Descriptor& dataMQ, + const StatusMQ::Descriptor& statusMQ, + const ThreadInfo& halThreadInfo) { + retval = r; + if (retval == Result::OK) { + tempCommandMQ.reset(new CommandMQ(commandMQ)); + tempDataMQ.reset(new DataMQ(dataMQ)); + tempStatusMQ.reset(new StatusMQ(statusMQ)); + if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { + EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); + } + halThreadPid = halThreadInfo.pid; + halThreadTid = halThreadInfo.tid; + } + }); + if (!ret.isOk() || retval != Result::OK) { + return processReturn("prepareForWriting", ret, retval); + } + if (!tempCommandMQ || !tempCommandMQ->isValid() || + !tempDataMQ || !tempDataMQ->isValid() || + !tempStatusMQ || !tempStatusMQ->isValid() || + !mEfGroup) { + ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing"); + ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), + "Command message queue for writing is invalid"); + ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing"); + ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid"); + ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing"); + ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), + "Status message queue for writing is invalid"); + ALOGE_IF(!mEfGroup, "Event flag creation for writing failed"); + return NO_INIT; + } + requestHalThreadPriority(halThreadPid, halThreadTid); + + mCommandMQ = std::move(tempCommandMQ); + mDataMQ = std::move(tempDataMQ); + mStatusMQ = std::move(tempStatusMQ); + mWriterClient = gettid(); + return OK; +} + +status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) { + if (mStream == 0) return NO_INIT; + Result retval; + Return ret = mStream->getRenderPosition( + [&](Result r, uint32_t d) { + retval = r; + if (retval == Result::OK) { + *dspFrames = d; + } + }); + return processReturn("getRenderPosition", ret, retval); +} + +status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) { + if (mStream == 0) return NO_INIT; + Result retval; + Return ret = mStream->getNextWriteTimestamp( + [&](Result r, int64_t t) { + retval = r; + if (retval == Result::OK) { + *timestamp = t; + } + }); + return processReturn("getRenderPosition", ret, retval); +} + +status_t StreamOutHalHidl::setCallback(wp callback) { + if (mStream == 0) return NO_INIT; + status_t status = processReturn( + "setCallback", mStream->setCallback(new StreamOutCallback(this))); + if (status == OK) { + mCallback = callback; + } + return status; +} + +status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { + if (mStream == 0) return NO_INIT; + Return ret = mStream->supportsPauseAndResume( + [&](bool p, bool r) { + *supportsPause = p; + *supportsResume = r; + }); + return processReturn("supportsPauseAndResume", ret); +} + +status_t StreamOutHalHidl::pause() { + if (mStream == 0) return NO_INIT; + return processReturn("pause", mStream->pause()); +} + +status_t StreamOutHalHidl::resume() { + if (mStream == 0) return NO_INIT; + return processReturn("pause", mStream->resume()); +} + +status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) { + if (mStream == 0) return NO_INIT; + return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain); +} + +status_t StreamOutHalHidl::drain(bool earlyNotify) { + if (mStream == 0) return NO_INIT; + return processReturn( + "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL)); +} + +status_t StreamOutHalHidl::flush() { + if (mStream == 0) return NO_INIT; + return processReturn("pause", mStream->flush()); +} + +status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { + if (mStream == 0) return NO_INIT; + if (mWriterClient == gettid() && mCommandMQ) { + return callWriterThread( + WriteCommand::GET_PRESENTATION_POSITION, "getPresentationPosition", nullptr, 0, + [&](const WriteStatus& writeStatus) { + *frames = writeStatus.reply.presentationPosition.frames; + timestamp->tv_sec = writeStatus.reply.presentationPosition.timeStamp.tvSec; + timestamp->tv_nsec = writeStatus.reply.presentationPosition.timeStamp.tvNSec; + }); + } else { + Result retval; + Return ret = mStream->getPresentationPosition( + [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) { + retval = r; + if (retval == Result::OK) { + *frames = hidlFrames; + timestamp->tv_sec = hidlTimeStamp.tvSec; + timestamp->tv_nsec = hidlTimeStamp.tvNSec; + } + }); + return processReturn("getPresentationPosition", ret, retval); + } +} + +void StreamOutHalHidl::onWriteReady() { + sp callback = mCallback.promote(); + if (callback == 0) return; + ALOGV("asyncCallback onWriteReady"); + callback->onWriteReady(); +} + +void StreamOutHalHidl::onDrainReady() { + sp callback = mCallback.promote(); + if (callback == 0) return; + ALOGV("asyncCallback onDrainReady"); + callback->onDrainReady(); +} + +void StreamOutHalHidl::onError() { + sp callback = mCallback.promote(); + if (callback == 0) return; + ALOGV("asyncCallback onError"); + callback->onError(); +} + + +StreamInHalHidl::StreamInHalHidl(const sp& stream) + : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) { +} + +StreamInHalHidl::~StreamInHalHidl() { + if (mStream != 0) { + processReturn("close", mStream->close()); + mStream.clear(); + hardware::IPCThreadState::self()->flushCommands(); + } + if (mEfGroup) { + EventFlag::deleteEventFlag(&mEfGroup); + } +} + +status_t StreamInHalHidl::getFrameSize(size_t *size) { + if (mStream == 0) return NO_INIT; + return processReturn("getFrameSize", mStream->getFrameSize(), size); +} + +status_t StreamInHalHidl::setGain(float gain) { + if (mStream == 0) return NO_INIT; + return processReturn("setGain", mStream->setGain(gain)); +} + +status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) { + if (mStream == 0) return NO_INIT; + *read = 0; + + if (bytes == 0 && !mDataMQ) { + // Can't determine the size for the MQ buffer. Wait for a non-empty read request. + return OK; + } + + status_t status; + if (!mDataMQ && (status = prepareForReading(bytes)) != OK) { + return status; + } + + ReadParameters params; + params.command = ReadCommand::READ; + params.params.read = bytes; + status = callReaderThread(params, "read", + [&](const ReadStatus& readStatus) { + const size_t availToRead = mDataMQ->availableToRead(); + if (!mDataMQ->read(static_cast(buffer), std::min(bytes, availToRead))) { + ALOGE("data message queue read failed for \"read\""); + } + ALOGW_IF(availToRead != readStatus.reply.read, + "HAL read report inconsistent: mq = %d, status = %d", + (int32_t)availToRead, (int32_t)readStatus.reply.read); + *read = readStatus.reply.read; + }); + mStreamPowerLog.log(buffer, *read); + return status; +} + +status_t StreamInHalHidl::callReaderThread( + const ReadParameters& params, const char* cmdName, + StreamInHalHidl::ReaderCallback callback) { + if (!mCommandMQ->write(¶ms)) { + ALOGW("command message queue write failed"); + return -EAGAIN; + } + mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_FULL)); + + // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 + uint32_t efState = 0; +retry: + status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_EMPTY), &efState); + if (efState & static_cast(MessageQueueFlagBits::NOT_EMPTY)) { + ReadStatus readStatus; + readStatus.retval = Result::NOT_INITIALIZED; + if (!mStatusMQ->read(&readStatus)) { + ALOGE("status message read failed for \"%s\"", cmdName); + } + if (readStatus.retval == Result::OK) { + ret = OK; + callback(readStatus); + } else { + ret = processReturn(cmdName, readStatus.retval); + } + return ret; + } + if (ret == -EAGAIN || ret == -EINTR) { + // Spurious wakeup. This normally retries no more than once. + goto retry; + } + return ret; +} + +status_t StreamInHalHidl::prepareForReading(size_t bufferSize) { + std::unique_ptr tempCommandMQ; + std::unique_ptr tempDataMQ; + std::unique_ptr tempStatusMQ; + Result retval; + pid_t halThreadPid, halThreadTid; + Return ret = mStream->prepareForReading( + 1, bufferSize, + [&](Result r, + const CommandMQ::Descriptor& commandMQ, + const DataMQ::Descriptor& dataMQ, + const StatusMQ::Descriptor& statusMQ, + const ThreadInfo& halThreadInfo) { + retval = r; + if (retval == Result::OK) { + tempCommandMQ.reset(new CommandMQ(commandMQ)); + tempDataMQ.reset(new DataMQ(dataMQ)); + tempStatusMQ.reset(new StatusMQ(statusMQ)); + if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { + EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); + } + halThreadPid = halThreadInfo.pid; + halThreadTid = halThreadInfo.tid; + } + }); + if (!ret.isOk() || retval != Result::OK) { + return processReturn("prepareForReading", ret, retval); + } + if (!tempCommandMQ || !tempCommandMQ->isValid() || + !tempDataMQ || !tempDataMQ->isValid() || + !tempStatusMQ || !tempStatusMQ->isValid() || + !mEfGroup) { + ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing"); + ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), + "Command message queue for writing is invalid"); + ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading"); + ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid"); + ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading"); + ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), + "Status message queue for reading is invalid"); + ALOGE_IF(!mEfGroup, "Event flag creation for reading failed"); + return NO_INIT; + } + requestHalThreadPriority(halThreadPid, halThreadTid); + + mCommandMQ = std::move(tempCommandMQ); + mDataMQ = std::move(tempDataMQ); + mStatusMQ = std::move(tempStatusMQ); + mReaderClient = gettid(); + return OK; +} + +status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) { + if (mStream == 0) return NO_INIT; + return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost); +} + +status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { + if (mStream == 0) return NO_INIT; + if (mReaderClient == gettid() && mCommandMQ) { + ReadParameters params; + params.command = ReadCommand::GET_CAPTURE_POSITION; + return callReaderThread(params, "getCapturePosition", + [&](const ReadStatus& readStatus) { + *frames = readStatus.reply.capturePosition.frames; + *time = readStatus.reply.capturePosition.time; + }); + } else { + Result retval; + Return ret = mStream->getCapturePosition( + [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) { + retval = r; + if (retval == Result::OK) { + *frames = hidlFrames; + *time = hidlTime; + } + }); + return processReturn("getCapturePosition", ret, retval); + } +} + +} // namespace android diff --git a/media/libaudiohal/4.0/StreamHalHidl.h b/media/libaudiohal/4.0/StreamHalHidl.h new file mode 100644 index 0000000000..d4ab943419 --- /dev/null +++ b/media/libaudiohal/4.0/StreamHalHidl.h @@ -0,0 +1,239 @@ +/* + * 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 ANDROID_HARDWARE_STREAM_HAL_HIDL_H +#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H + +#include + +#include +#include +#include +#include +#include +#include + +#include "ConversionHelperHidl.h" +#include "StreamPowerLog.h" + +using ::android::hardware::audio::V2_0::IStream; +using ::android::hardware::audio::V2_0::IStreamIn; +using ::android::hardware::audio::V2_0::IStreamOut; +using ::android::hardware::EventFlag; +using ::android::hardware::MessageQueue; +using ::android::hardware::Return; +using ReadParameters = ::android::hardware::audio::V2_0::IStreamIn::ReadParameters; +using ReadStatus = ::android::hardware::audio::V2_0::IStreamIn::ReadStatus; +using WriteCommand = ::android::hardware::audio::V2_0::IStreamOut::WriteCommand; +using WriteStatus = ::android::hardware::audio::V2_0::IStreamOut::WriteStatus; + +namespace android { + +class DeviceHalHidl; + +class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl +{ + public: + // Return the sampling rate in Hz - eg. 44100. + virtual status_t getSampleRate(uint32_t *rate); + + // Return size of input/output buffer in bytes for this stream - eg. 4800. + virtual status_t getBufferSize(size_t *size); + + // Return the channel mask. + virtual status_t getChannelMask(audio_channel_mask_t *mask); + + // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT. + virtual status_t getFormat(audio_format_t *format); + + // Convenience method. + virtual status_t getAudioProperties( + uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format); + + // Set audio stream parameters. + virtual status_t setParameters(const String8& kvPairs); + + // Get audio stream parameters. + virtual status_t getParameters(const String8& keys, String8 *values); + + // Add or remove the effect on the stream. + virtual status_t addEffect(sp effect); + virtual status_t removeEffect(sp effect); + + // Put the audio hardware input/output into standby mode. + virtual status_t standby(); + + virtual status_t dump(int fd); + + // Start a stream operating in mmap mode. + virtual status_t start(); + + // Stop a stream operating in mmap mode. + virtual status_t stop(); + + // Retrieve information on the data buffer in mmap mode. + virtual status_t createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info); + + // Get current read/write position in the mmap buffer + virtual status_t getMmapPosition(struct audio_mmap_position *position); + + // Set the priority of the thread that interacts with the HAL + // (must match the priority of the audioflinger's thread that calls 'read' / 'write') + virtual status_t setHalThreadPriority(int priority); + + protected: + // Subclasses can not be constructed directly by clients. + explicit StreamHalHidl(IStream *stream); + + // The destructor automatically closes the stream. + virtual ~StreamHalHidl(); + + status_t getCachedBufferSize(size_t *size); + + bool requestHalThreadPriority(pid_t threadPid, pid_t threadId); + + // mStreamPowerLog is used for audio signal power logging. + StreamPowerLog mStreamPowerLog; + + private: + const int HAL_THREAD_PRIORITY_DEFAULT = -1; + IStream *mStream; + int mHalThreadPriority; + size_t mCachedBufferSize; +}; + +class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl { + public: + // Return the frame size (number of bytes per sample) of a stream. + virtual status_t getFrameSize(size_t *size); + + // Return the audio hardware driver estimated latency in milliseconds. + virtual status_t getLatency(uint32_t *latency); + + // Use this method in situations where audio mixing is done in the hardware. + virtual status_t setVolume(float left, float right); + + // Write audio buffer to driver. + virtual status_t write(const void *buffer, size_t bytes, size_t *written); + + // Return the number of audio frames written by the audio dsp to DAC since + // the output has exited standby. + virtual status_t getRenderPosition(uint32_t *dspFrames); + + // Get the local time at which the next write to the audio driver will be presented. + virtual status_t getNextWriteTimestamp(int64_t *timestamp); + + // Set the callback for notifying completion of non-blocking write and drain. + virtual status_t setCallback(wp callback); + + // Returns whether pause and resume operations are supported. + virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume); + + // Notifies to the audio driver to resume playback following a pause. + virtual status_t pause(); + + // Notifies to the audio driver to resume playback following a pause. + virtual status_t resume(); + + // Returns whether drain operation is supported. + virtual status_t supportsDrain(bool *supportsDrain); + + // Requests notification when data buffered by the driver/hardware has been played. + virtual status_t drain(bool earlyNotify); + + // Notifies to the audio driver to flush the queued data. + virtual status_t flush(); + + // Return a recent count of the number of audio frames presented to an external observer. + virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); + + // Methods used by StreamOutCallback (HIDL). + void onWriteReady(); + void onDrainReady(); + void onError(); + + private: + friend class DeviceHalHidl; + typedef MessageQueue CommandMQ; + typedef MessageQueue DataMQ; + typedef MessageQueue StatusMQ; + + wp mCallback; + sp mStream; + std::unique_ptr mCommandMQ; + std::unique_ptr mDataMQ; + std::unique_ptr mStatusMQ; + std::atomic mWriterClient; + EventFlag* mEfGroup; + + // Can not be constructed directly by clients. + StreamOutHalHidl(const sp& stream); + + virtual ~StreamOutHalHidl(); + + using WriterCallback = std::function; + status_t callWriterThread( + WriteCommand cmd, const char* cmdName, + const uint8_t* data, size_t dataSize, WriterCallback callback); + status_t prepareForWriting(size_t bufferSize); +}; + +class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { + public: + // Return the frame size (number of bytes per sample) of a stream. + virtual status_t getFrameSize(size_t *size); + + // Set the input gain for the audio driver. + virtual status_t setGain(float gain); + + // Read audio buffer in from driver. + virtual status_t read(void *buffer, size_t bytes, size_t *read); + + // Return the amount of input frames lost in the audio driver. + virtual status_t getInputFramesLost(uint32_t *framesLost); + + // Return a recent count of the number of audio frames received and + // the clock time associated with that frame count. + virtual status_t getCapturePosition(int64_t *frames, int64_t *time); + + private: + friend class DeviceHalHidl; + typedef MessageQueue CommandMQ; + typedef MessageQueue DataMQ; + typedef MessageQueue StatusMQ; + + sp mStream; + std::unique_ptr mCommandMQ; + std::unique_ptr mDataMQ; + std::unique_ptr mStatusMQ; + std::atomic mReaderClient; + EventFlag* mEfGroup; + + // Can not be constructed directly by clients. + StreamInHalHidl(const sp& stream); + + virtual ~StreamInHalHidl(); + + using ReaderCallback = std::function; + status_t callReaderThread( + const ReadParameters& params, const char* cmdName, ReaderCallback callback); + status_t prepareForReading(size_t bufferSize); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalLocal.cpp b/media/libaudiohal/4.0/StreamHalLocal.cpp new file mode 100644 index 0000000000..8d61e24cac --- /dev/null +++ b/media/libaudiohal/4.0/StreamHalLocal.cpp @@ -0,0 +1,316 @@ +/* + * 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. + */ + +#define LOG_TAG "StreamHalLocal" +//#define LOG_NDEBUG 0 + +#include +#include + +#include "DeviceHalLocal.h" +#include "StreamHalLocal.h" + +namespace android { + +StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) + : mDevice(device), + mStream(stream) { + // Instrument audio signal power logging. + // Note: This assumes channel mask, format, and sample rate do not change after creation. + if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { + mStreamPowerLog.init(mStream->get_sample_rate(mStream), + mStream->get_channels(mStream), + mStream->get_format(mStream)); + } +} + +StreamHalLocal::~StreamHalLocal() { + mStream = 0; + mDevice.clear(); +} + +status_t StreamHalLocal::getSampleRate(uint32_t *rate) { + *rate = mStream->get_sample_rate(mStream); + return OK; +} + +status_t StreamHalLocal::getBufferSize(size_t *size) { + *size = mStream->get_buffer_size(mStream); + return OK; +} + +status_t StreamHalLocal::getChannelMask(audio_channel_mask_t *mask) { + *mask = mStream->get_channels(mStream); + return OK; +} + +status_t StreamHalLocal::getFormat(audio_format_t *format) { + *format = mStream->get_format(mStream); + return OK; +} + +status_t StreamHalLocal::getAudioProperties( + uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { + *sampleRate = mStream->get_sample_rate(mStream); + *mask = mStream->get_channels(mStream); + *format = mStream->get_format(mStream); + return OK; +} + +status_t StreamHalLocal::setParameters(const String8& kvPairs) { + return mStream->set_parameters(mStream, kvPairs.string()); +} + +status_t StreamHalLocal::getParameters(const String8& keys, String8 *values) { + char *halValues = mStream->get_parameters(mStream, keys.string()); + if (halValues != NULL) { + values->setTo(halValues); + free(halValues); + } else { + values->clear(); + } + return OK; +} + +status_t StreamHalLocal::addEffect(sp) { + LOG_ALWAYS_FATAL("Local streams can not have effects"); + return INVALID_OPERATION; +} + +status_t StreamHalLocal::removeEffect(sp) { + LOG_ALWAYS_FATAL("Local streams can not have effects"); + return INVALID_OPERATION; +} + +status_t StreamHalLocal::standby() { + return mStream->standby(mStream); +} + +status_t StreamHalLocal::dump(int fd) { + status_t status = mStream->dump(mStream, fd); + mStreamPowerLog.dump(fd); + return status; +} + +status_t StreamHalLocal::setHalThreadPriority(int) { + // Don't need to do anything as local hal is executed by audioflinger directly + // on the same thread. + return OK; +} + +StreamOutHalLocal::StreamOutHalLocal(audio_stream_out_t *stream, sp device) + : StreamHalLocal(&stream->common, device), mStream(stream) { +} + +StreamOutHalLocal::~StreamOutHalLocal() { + mCallback.clear(); + mDevice->closeOutputStream(mStream); + mStream = 0; +} + +status_t StreamOutHalLocal::getFrameSize(size_t *size) { + *size = audio_stream_out_frame_size(mStream); + return OK; +} + +status_t StreamOutHalLocal::getLatency(uint32_t *latency) { + *latency = mStream->get_latency(mStream); + return OK; +} + +status_t StreamOutHalLocal::setVolume(float left, float right) { + if (mStream->set_volume == NULL) return INVALID_OPERATION; + return mStream->set_volume(mStream, left, right); +} + +status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) { + ssize_t writeResult = mStream->write(mStream, buffer, bytes); + if (writeResult > 0) { + *written = writeResult; + mStreamPowerLog.log(buffer, *written); + return OK; + } else { + *written = 0; + return writeResult; + } +} + +status_t StreamOutHalLocal::getRenderPosition(uint32_t *dspFrames) { + return mStream->get_render_position(mStream, dspFrames); +} + +status_t StreamOutHalLocal::getNextWriteTimestamp(int64_t *timestamp) { + if (mStream->get_next_write_timestamp == NULL) return INVALID_OPERATION; + return mStream->get_next_write_timestamp(mStream, timestamp); +} + +status_t StreamOutHalLocal::setCallback(wp callback) { + if (mStream->set_callback == NULL) return INVALID_OPERATION; + status_t result = mStream->set_callback(mStream, StreamOutHalLocal::asyncCallback, this); + if (result == OK) { + mCallback = callback; + } + return result; +} + +// static +int StreamOutHalLocal::asyncCallback(stream_callback_event_t event, void*, void *cookie) { + // We act as if we gave a wp to HAL. This way we should handle + // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is + // already running, because the destructor is invoked after the refcount has been atomically + // decremented. + wp weakSelf(static_cast(cookie)); + sp self = weakSelf.promote(); + if (self == 0) return 0; + sp callback = self->mCallback.promote(); + if (callback == 0) return 0; + ALOGV("asyncCallback() event %d", event); + switch (event) { + case STREAM_CBK_EVENT_WRITE_READY: + callback->onWriteReady(); + break; + case STREAM_CBK_EVENT_DRAIN_READY: + callback->onDrainReady(); + break; + case STREAM_CBK_EVENT_ERROR: + callback->onError(); + break; + default: + ALOGW("asyncCallback() unknown event %d", event); + break; + } + return 0; +} + +status_t StreamOutHalLocal::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { + *supportsPause = mStream->pause != NULL; + *supportsResume = mStream->resume != NULL; + return OK; +} + +status_t StreamOutHalLocal::pause() { + if (mStream->pause == NULL) return INVALID_OPERATION; + return mStream->pause(mStream); +} + +status_t StreamOutHalLocal::resume() { + if (mStream->resume == NULL) return INVALID_OPERATION; + return mStream->resume(mStream); +} + +status_t StreamOutHalLocal::supportsDrain(bool *supportsDrain) { + *supportsDrain = mStream->drain != NULL; + return OK; +} + +status_t StreamOutHalLocal::drain(bool earlyNotify) { + if (mStream->drain == NULL) return INVALID_OPERATION; + return mStream->drain(mStream, earlyNotify ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL); +} + +status_t StreamOutHalLocal::flush() { + if (mStream->flush == NULL) return INVALID_OPERATION; + return mStream->flush(mStream); +} + +status_t StreamOutHalLocal::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { + if (mStream->get_presentation_position == NULL) return INVALID_OPERATION; + return mStream->get_presentation_position(mStream, frames, timestamp); +} + +status_t StreamOutHalLocal::start() { + if (mStream->start == NULL) return INVALID_OPERATION; + return mStream->start(mStream); +} + +status_t StreamOutHalLocal::stop() { + if (mStream->stop == NULL) return INVALID_OPERATION; + return mStream->stop(mStream); +} + +status_t StreamOutHalLocal::createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info) { + if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; + return mStream->create_mmap_buffer(mStream, minSizeFrames, info); +} + +status_t StreamOutHalLocal::getMmapPosition(struct audio_mmap_position *position) { + if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; + return mStream->get_mmap_position(mStream, position); +} + +StreamInHalLocal::StreamInHalLocal(audio_stream_in_t *stream, sp device) + : StreamHalLocal(&stream->common, device), mStream(stream) { +} + +StreamInHalLocal::~StreamInHalLocal() { + mDevice->closeInputStream(mStream); + mStream = 0; +} + +status_t StreamInHalLocal::getFrameSize(size_t *size) { + *size = audio_stream_in_frame_size(mStream); + return OK; +} + +status_t StreamInHalLocal::setGain(float gain) { + return mStream->set_gain(mStream, gain); +} + +status_t StreamInHalLocal::read(void *buffer, size_t bytes, size_t *read) { + ssize_t readResult = mStream->read(mStream, buffer, bytes); + if (readResult > 0) { + *read = readResult; + mStreamPowerLog.log( buffer, *read); + return OK; + } else { + *read = 0; + return readResult; + } +} + +status_t StreamInHalLocal::getInputFramesLost(uint32_t *framesLost) { + *framesLost = mStream->get_input_frames_lost(mStream); + return OK; +} + +status_t StreamInHalLocal::getCapturePosition(int64_t *frames, int64_t *time) { + if (mStream->get_capture_position == NULL) return INVALID_OPERATION; + return mStream->get_capture_position(mStream, frames, time); +} + +status_t StreamInHalLocal::start() { + if (mStream->start == NULL) return INVALID_OPERATION; + return mStream->start(mStream); +} + +status_t StreamInHalLocal::stop() { + if (mStream->stop == NULL) return INVALID_OPERATION; + return mStream->stop(mStream); +} + +status_t StreamInHalLocal::createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info) { + if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; + return mStream->create_mmap_buffer(mStream, minSizeFrames, info); +} + +status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) { + if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; + return mStream->get_mmap_position(mStream, position); +} + +} // namespace android diff --git a/media/libaudiohal/4.0/StreamHalLocal.h b/media/libaudiohal/4.0/StreamHalLocal.h new file mode 100644 index 0000000000..c7136dff4e --- /dev/null +++ b/media/libaudiohal/4.0/StreamHalLocal.h @@ -0,0 +1,210 @@ +/* + * 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 ANDROID_HARDWARE_STREAM_HAL_LOCAL_H +#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_H + +#include +#include "StreamPowerLog.h" + +namespace android { + +class DeviceHalLocal; + +class StreamHalLocal : public virtual StreamHalInterface +{ + public: + // Return the sampling rate in Hz - eg. 44100. + virtual status_t getSampleRate(uint32_t *rate); + + // Return size of input/output buffer in bytes for this stream - eg. 4800. + virtual status_t getBufferSize(size_t *size); + + // Return the channel mask. + virtual status_t getChannelMask(audio_channel_mask_t *mask); + + // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT. + virtual status_t getFormat(audio_format_t *format); + + // Convenience method. + virtual status_t getAudioProperties( + uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format); + + // Set audio stream parameters. + virtual status_t setParameters(const String8& kvPairs); + + // Get audio stream parameters. + virtual status_t getParameters(const String8& keys, String8 *values); + + // Add or remove the effect on the stream. + virtual status_t addEffect(sp effect); + virtual status_t removeEffect(sp effect); + + // Put the audio hardware input/output into standby mode. + virtual status_t standby(); + + virtual status_t dump(int fd); + + // Start a stream operating in mmap mode. + virtual status_t start() = 0; + + // Stop a stream operating in mmap mode. + virtual status_t stop() = 0; + + // Retrieve information on the data buffer in mmap mode. + virtual status_t createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info) = 0; + + // Get current read/write position in the mmap buffer + virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0; + + // Set the priority of the thread that interacts with the HAL + // (must match the priority of the audioflinger's thread that calls 'read' / 'write') + virtual status_t setHalThreadPriority(int priority); + + protected: + // Subclasses can not be constructed directly by clients. + StreamHalLocal(audio_stream_t *stream, sp device); + + // The destructor automatically closes the stream. + virtual ~StreamHalLocal(); + + sp mDevice; + + // mStreamPowerLog is used for audio signal power logging. + StreamPowerLog mStreamPowerLog; + + private: + audio_stream_t *mStream; +}; + +class StreamOutHalLocal : public StreamOutHalInterface, public StreamHalLocal { + public: + // Return the frame size (number of bytes per sample) of a stream. + virtual status_t getFrameSize(size_t *size); + + // Return the audio hardware driver estimated latency in milliseconds. + virtual status_t getLatency(uint32_t *latency); + + // Use this method in situations where audio mixing is done in the hardware. + virtual status_t setVolume(float left, float right); + + // Write audio buffer to driver. + virtual status_t write(const void *buffer, size_t bytes, size_t *written); + + // Return the number of audio frames written by the audio dsp to DAC since + // the output has exited standby. + virtual status_t getRenderPosition(uint32_t *dspFrames); + + // Get the local time at which the next write to the audio driver will be presented. + virtual status_t getNextWriteTimestamp(int64_t *timestamp); + + // Set the callback for notifying completion of non-blocking write and drain. + virtual status_t setCallback(wp callback); + + // Returns whether pause and resume operations are supported. + virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume); + + // Notifies to the audio driver to resume playback following a pause. + virtual status_t pause(); + + // Notifies to the audio driver to resume playback following a pause. + virtual status_t resume(); + + // Returns whether drain operation is supported. + virtual status_t supportsDrain(bool *supportsDrain); + + // Requests notification when data buffered by the driver/hardware has been played. + virtual status_t drain(bool earlyNotify); + + // Notifies to the audio driver to flush the queued data. + virtual status_t flush(); + + // Return a recent count of the number of audio frames presented to an external observer. + virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); + + // Start a stream operating in mmap mode. + virtual status_t start(); + + // Stop a stream operating in mmap mode. + virtual status_t stop(); + + // Retrieve information on the data buffer in mmap mode. + virtual status_t createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info); + + // Get current read/write position in the mmap buffer + virtual status_t getMmapPosition(struct audio_mmap_position *position); + + private: + audio_stream_out_t *mStream; + wp mCallback; + + friend class DeviceHalLocal; + + // Can not be constructed directly by clients. + StreamOutHalLocal(audio_stream_out_t *stream, sp device); + + virtual ~StreamOutHalLocal(); + + static int asyncCallback(stream_callback_event_t event, void *param, void *cookie); +}; + +class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { + public: + // Return the frame size (number of bytes per sample) of a stream. + virtual status_t getFrameSize(size_t *size); + + // Set the input gain for the audio driver. + virtual status_t setGain(float gain); + + // Read audio buffer in from driver. + virtual status_t read(void *buffer, size_t bytes, size_t *read); + + // Return the amount of input frames lost in the audio driver. + virtual status_t getInputFramesLost(uint32_t *framesLost); + + // Return a recent count of the number of audio frames received and + // the clock time associated with that frame count. + virtual status_t getCapturePosition(int64_t *frames, int64_t *time); + + // Start a stream operating in mmap mode. + virtual status_t start(); + + // Stop a stream operating in mmap mode. + virtual status_t stop(); + + // Retrieve information on the data buffer in mmap mode. + virtual status_t createMmapBuffer(int32_t minSizeFrames, + struct audio_mmap_buffer_info *info); + + // Get current read/write position in the mmap buffer + virtual status_t getMmapPosition(struct audio_mmap_position *position); + + private: + audio_stream_in_t *mStream; + + friend class DeviceHalLocal; + + // Can not be constructed directly by clients. + StreamInHalLocal(audio_stream_in_t *stream, sp device); + + virtual ~StreamInHalLocal(); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/StreamPowerLog.h b/media/libaudiohal/4.0/StreamPowerLog.h new file mode 100644 index 0000000000..a78b1aa042 --- /dev/null +++ b/media/libaudiohal/4.0/StreamPowerLog.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_H +#define ANDROID_HARDWARE_STREAM_POWER_LOG_H + +#include +#include +#include +#include + +namespace android { + +class StreamPowerLog { +public: + StreamPowerLog() : + mIsUserDebugOrEngBuild(is_userdebug_or_eng_build()), + mPowerLog(nullptr), + mFrameSize(0) { + // use init() to set up the power log. + } + + ~StreamPowerLog() { + power_log_destroy(mPowerLog); // OK for null mPowerLog + mPowerLog = nullptr; + } + + // A one-time initialization (do not call twice) before using StreamPowerLog. + void init(uint32_t sampleRate, audio_channel_mask_t channelMask, audio_format_t format) { + if (mPowerLog == nullptr) { + // Note: A way to get channel count for both input and output channel masks + // but does not check validity of the channel mask. + const uint32_t channelCount = popcount(audio_channel_mask_get_bits(channelMask)); + mFrameSize = channelCount * audio_bytes_per_sample(format); + if (mFrameSize > 0) { + const size_t kPowerLogFramesPerEntry = + (long long)sampleRate * kPowerLogSamplingIntervalMs / 1000; + mPowerLog = power_log_create( + sampleRate, + channelCount, + format, + kPowerLogEntries, + kPowerLogFramesPerEntry); + } + } + // mPowerLog may be NULL (not the right build, format not accepted, etc.). + } + + // Dump the power log to fd. + void dump(int fd) const { + // OK for null mPowerLog + (void)power_log_dump( + mPowerLog, fd, " " /* prefix */, kPowerLogLines, 0 /* limit_ns */); + } + + // Log the audio data contained in buffer. + void log(const void *buffer, size_t sizeInBytes) const { + if (mPowerLog != nullptr) { // mFrameSize is always nonzero if mPowerLog exists. + power_log_log( + mPowerLog, buffer, sizeInBytes / mFrameSize, audio_utils_get_real_time_ns()); + } + } + + bool isUserDebugOrEngBuild() const { + return mIsUserDebugOrEngBuild; + } + +private: + + static inline bool is_userdebug_or_eng_build() { + char value[PROPERTY_VALUE_MAX]; + (void)property_get("ro.build.type", value, "unknown"); // ignore actual length + return strcmp(value, "userdebug") == 0 || strcmp(value, "eng") == 0; + } + + // Audio signal power log configuration. + static const size_t kPowerLogLines = 40; + static const size_t kPowerLogSamplingIntervalMs = 50; + static const size_t kPowerLogEntries = (1 /* minutes */ * 60 /* seconds */ * 1000 /* msec */ + / kPowerLogSamplingIntervalMs); + + const bool mIsUserDebugOrEngBuild; + power_log_t *mPowerLog; + size_t mFrameSize; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H -- GitLab From 2390b5d696471a655abd663fde3679d33412a093 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 28 Feb 2018 14:36:53 -0800 Subject: [PATCH 0006/1530] Audio V4: Update namespace and version of 4.0 shim Put the shim in a V4_0 namespace to comply with the ODR. Make the include guard unique. Split from the error prone interface adaptation change for safer review. Patch generated with: sed -i 's/V2_0/V4_0/g' * sed -i "/include.*2.0/s/2\.0/4.0/" * sed -i '/namespace android *{/anamespace V4_0 {' * sed -Ei 's/(ifndef|define|endif)(.*)_H/\1\2_4_0_H/' * sed -Ei 's#}(.*)namespace android$#}\1namespace V4_0\n\0#' * Bug: 38184704 Test: Compile Change-Id: I7d3aa46b3dfec664cbeea8a6b946456b8e8f06e3 Signed-off-by: Kevin Rocard --- .../libaudiohal/4.0/ConversionHelperHidl.cpp | 4 ++- media/libaudiohal/4.0/ConversionHelperHidl.h | 18 ++++++----- media/libaudiohal/4.0/DeviceHalHidl.cpp | 32 ++++++++++--------- media/libaudiohal/4.0/DeviceHalHidl.h | 16 ++++++---- media/libaudiohal/4.0/DeviceHalLocal.cpp | 2 ++ media/libaudiohal/4.0/DeviceHalLocal.h | 8 +++-- .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 8 +++-- media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 12 ++++--- .../4.0/DevicesFactoryHalHybrid.cpp | 2 ++ .../libaudiohal/4.0/DevicesFactoryHalHybrid.h | 8 +++-- .../4.0/DevicesFactoryHalLocal.cpp | 2 ++ .../libaudiohal/4.0/DevicesFactoryHalLocal.h | 8 +++-- media/libaudiohal/4.0/EffectBufferHalHidl.cpp | 2 ++ media/libaudiohal/4.0/EffectBufferHalHidl.h | 12 ++++--- media/libaudiohal/4.0/EffectHalHidl.cpp | 18 ++++++----- media/libaudiohal/4.0/EffectHalHidl.h | 22 +++++++------ .../libaudiohal/4.0/EffectsFactoryHalHidl.cpp | 10 +++--- media/libaudiohal/4.0/EffectsFactoryHalHidl.h | 16 ++++++---- media/libaudiohal/4.0/StreamHalHidl.cpp | 28 ++++++++-------- media/libaudiohal/4.0/StreamHalHidl.h | 28 ++++++++-------- media/libaudiohal/4.0/StreamHalLocal.cpp | 2 ++ media/libaudiohal/4.0/StreamHalLocal.h | 8 +++-- media/libaudiohal/4.0/StreamPowerLog.h | 8 +++-- 23 files changed, 160 insertions(+), 114 deletions(-) diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.cpp b/media/libaudiohal/4.0/ConversionHelperHidl.cpp index f60bf8b69c..a3cc28f797 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.cpp +++ b/media/libaudiohal/4.0/ConversionHelperHidl.cpp @@ -22,9 +22,10 @@ #include "ConversionHelperHidl.h" -using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::V4_0::Result; namespace android { +namespace V4_0 { // static status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { @@ -100,4 +101,5 @@ void ConversionHelperHidl::emitError(const char* funcName, const char* descripti ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.h b/media/libaudiohal/4.0/ConversionHelperHidl.h index c356f37e3d..ddc8569bfe 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.h +++ b/media/libaudiohal/4.0/ConversionHelperHidl.h @@ -14,19 +14,20 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H -#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H +#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H +#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H -#include +#include #include #include -using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::audio::V4_0::ParameterValue; using ::android::hardware::Return; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; namespace android { +namespace V4_0 { class ConversionHelperHidl { protected: @@ -54,7 +55,7 @@ class ConversionHelperHidl { return ret.isOk() ? OK : FAILED_TRANSACTION; } - status_t processReturn(const char* funcName, const Return& ret) { + status_t processReturn(const char* funcName, const Return& ret) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -63,7 +64,7 @@ class ConversionHelperHidl { template status_t processReturn( - const char* funcName, const Return& ret, hardware::audio::V2_0::Result retval) { + const char* funcName, const Return& ret, hardware::audio::V4_0::Result retval) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -73,11 +74,12 @@ class ConversionHelperHidl { private: const char* mClassName; - static status_t analyzeResult(const hardware::audio::V2_0::Result& result); + static status_t analyzeResult(const hardware::audio::V4_0::Result& result); void emitError(const char* funcName, const char* description); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H +#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/4.0/DeviceHalHidl.cpp index 0d9c6c4dec..24f2eeb6ed 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.cpp +++ b/media/libaudiohal/4.0/DeviceHalHidl.cpp @@ -19,7 +19,7 @@ #define LOG_TAG "DeviceHalHidl" //#define LOG_NDEBUG 0 -#include +#include #include #include #include @@ -28,24 +28,25 @@ #include "HidlUtils.h" #include "StreamHalHidl.h" -using ::android::hardware::audio::common::V2_0::AudioConfig; -using ::android::hardware::audio::common::V2_0::AudioDevice; -using ::android::hardware::audio::common::V2_0::AudioInputFlag; -using ::android::hardware::audio::common::V2_0::AudioOutputFlag; -using ::android::hardware::audio::common::V2_0::AudioPatchHandle; -using ::android::hardware::audio::common::V2_0::AudioPort; -using ::android::hardware::audio::common::V2_0::AudioPortConfig; -using ::android::hardware::audio::common::V2_0::AudioMode; -using ::android::hardware::audio::common::V2_0::AudioSource; -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::V2_0::DeviceAddress; -using ::android::hardware::audio::V2_0::IPrimaryDevice; -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::common::V4_0::AudioConfig; +using ::android::hardware::audio::common::V4_0::AudioDevice; +using ::android::hardware::audio::common::V4_0::AudioInputFlag; +using ::android::hardware::audio::common::V4_0::AudioOutputFlag; +using ::android::hardware::audio::common::V4_0::AudioPatchHandle; +using ::android::hardware::audio::common::V4_0::AudioPort; +using ::android::hardware::audio::common::V4_0::AudioPortConfig; +using ::android::hardware::audio::common::V4_0::AudioMode; +using ::android::hardware::audio::common::V4_0::AudioSource; +using ::android::hardware::audio::common::V4_0::HidlUtils; +using ::android::hardware::audio::V4_0::DeviceAddress; +using ::android::hardware::audio::V4_0::IPrimaryDevice; +using ::android::hardware::audio::V4_0::ParameterValue; +using ::android::hardware::audio::V4_0::Result; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; namespace android { +namespace V4_0 { namespace { @@ -355,4 +356,5 @@ status_t DeviceHalHidl::dump(int fd) { return processReturn("dump", ret); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/DeviceHalHidl.h b/media/libaudiohal/4.0/DeviceHalHidl.h index 8651b511a3..f460addc54 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.h +++ b/media/libaudiohal/4.0/DeviceHalHidl.h @@ -14,20 +14,21 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_H -#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H +#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H +#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H -#include -#include +#include +#include #include #include "ConversionHelperHidl.h" -using ::android::hardware::audio::V2_0::IDevice; -using ::android::hardware::audio::V2_0::IPrimaryDevice; +using ::android::hardware::audio::V4_0::IDevice; +using ::android::hardware::audio::V4_0::IPrimaryDevice; using ::android::hardware::Return; namespace android { +namespace V4_0 { class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl { @@ -121,6 +122,7 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl virtual ~DeviceHalHidl(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H +#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/DeviceHalLocal.cpp b/media/libaudiohal/4.0/DeviceHalLocal.cpp index fc098f5136..e64eee18bb 100644 --- a/media/libaudiohal/4.0/DeviceHalLocal.cpp +++ b/media/libaudiohal/4.0/DeviceHalLocal.cpp @@ -23,6 +23,7 @@ #include "StreamHalLocal.h" namespace android { +namespace V4_0 { DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) : mDev(dev) { @@ -196,4 +197,5 @@ void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { mDev->close_input_stream(mDev, stream_in); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/DeviceHalLocal.h b/media/libaudiohal/4.0/DeviceHalLocal.h index 865f2968e1..daafdc7fce 100644 --- a/media/libaudiohal/4.0/DeviceHalLocal.h +++ b/media/libaudiohal/4.0/DeviceHalLocal.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H -#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H +#ifndef ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H +#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H #include #include namespace android { +namespace V4_0 { class DeviceHalLocal : public DeviceHalInterface { @@ -119,6 +120,7 @@ class DeviceHalLocal : public DeviceHalInterface uint32_t version() const { return mDev->common.version; } }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H +#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp index 5b335920bf..5feed096cc 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp @@ -19,7 +19,7 @@ #define LOG_TAG "DevicesFactoryHalHidl" //#define LOG_NDEBUG 0 -#include +#include #include #include @@ -27,11 +27,12 @@ #include "DeviceHalHidl.h" #include "DevicesFactoryHalHidl.h" -using ::android::hardware::audio::V2_0::IDevice; -using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::V4_0::IDevice; +using ::android::hardware::audio::V4_0::Result; using ::android::hardware::Return; namespace android { +namespace V4_0 { DevicesFactoryHalHidl::DevicesFactoryHalHidl() { mDevicesFactory = IDevicesFactory::getService(); @@ -95,4 +96,5 @@ status_t DevicesFactoryHalHidl::openDevice(const char *name, sp +#include #include #include #include #include "DeviceHalHidl.h" -using ::android::hardware::audio::V2_0::IDevicesFactory; +using ::android::hardware::audio::V4_0::IDevicesFactory; namespace android { +namespace V4_0 { class DevicesFactoryHalHidl : public DevicesFactoryHalInterface { @@ -49,6 +50,7 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface virtual ~DevicesFactoryHalHidl(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp index 77df6b5eaf..722b95a8fe 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp @@ -22,6 +22,7 @@ #include "DevicesFactoryHalHidl.h" namespace android { +namespace V4_0 { DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() : mLocalFactory(new DevicesFactoryHalLocal()), @@ -38,4 +39,5 @@ status_t DevicesFactoryHalHybrid::openDevice(const char *name, spopenDevice(name, device); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h index abd57d6446..abf6de00c7 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h @@ -14,14 +14,15 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H +#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H #include #include #include namespace android { +namespace V4_0 { class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface { @@ -42,6 +43,7 @@ class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface sp mHidlFactory; }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp index 13a9acd9e9..e54edd4c29 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp @@ -26,6 +26,7 @@ #include "DevicesFactoryHalLocal.h" namespace android { +namespace V4_0 { static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) { @@ -66,4 +67,5 @@ status_t DevicesFactoryHalLocal::openDevice(const char *name, sp #include @@ -24,6 +24,7 @@ #include "DeviceHalLocal.h" namespace android { +namespace V4_0 { class DevicesFactoryHalLocal : public DevicesFactoryHalInterface { @@ -41,6 +42,7 @@ class DevicesFactoryHalLocal : public DevicesFactoryHalInterface virtual ~DevicesFactoryHalLocal() {} }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp index 226a500225..957c89f14f 100644 --- a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp @@ -30,6 +30,7 @@ using ::android::hardware::Return; using ::android::hidl::allocator::V1_0::IAllocator; namespace android { +namespace V4_0 { // static uint64_t EffectBufferHalHidl::makeUniqueId() { @@ -141,4 +142,5 @@ void EffectBufferHalHidl::commit(size_t size) { memcpy(mExternalData, mAudioBuffer.raw, size); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.h b/media/libaudiohal/4.0/EffectBufferHalHidl.h index 31e0087c23..6d578c6a24 100644 --- a/media/libaudiohal/4.0/EffectBufferHalHidl.h +++ b/media/libaudiohal/4.0/EffectBufferHalHidl.h @@ -14,20 +14,21 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H +#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H +#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H -#include +#include #include #include #include #include -using android::hardware::audio::effect::V2_0::AudioBuffer; +using android::hardware::audio::effect::V4_0::AudioBuffer; using android::hardware::hidl_memory; using android::hidl::memory::V1_0::IMemory; namespace android { +namespace V4_0 { class EffectBufferHalHidl : public EffectBufferHalInterface { @@ -71,6 +72,7 @@ class EffectBufferHalHidl : public EffectBufferHalInterface status_t init(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H +#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp index 4fb032c7cb..32d5cea19c 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectHalHidl.cpp @@ -26,19 +26,20 @@ #include "EffectHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::effect::V2_0::AudioBuffer; -using ::android::hardware::audio::effect::V2_0::EffectBufferAccess; -using ::android::hardware::audio::effect::V2_0::EffectConfigParameters; -using ::android::hardware::audio::effect::V2_0::MessageQueueFlagBits; -using ::android::hardware::audio::effect::V2_0::Result; -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::common::V2_0::AudioChannelMask; -using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::audio::effect::V4_0::AudioBuffer; +using ::android::hardware::audio::effect::V4_0::EffectBufferAccess; +using ::android::hardware::audio::effect::V4_0::EffectConfigParameters; +using ::android::hardware::audio::effect::V4_0::MessageQueueFlagBits; +using ::android::hardware::audio::effect::V4_0::Result; +using ::android::hardware::audio::common::V4_0::HidlUtils; +using ::android::hardware::audio::common::V4_0::AudioChannelMask; +using ::android::hardware::audio::common::V4_0::AudioFormat; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; namespace android { +namespace V4_0 { EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { @@ -335,4 +336,5 @@ status_t EffectHalHidl::setConfigImpl( return result; } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/EffectHalHidl.h b/media/libaudiohal/4.0/EffectHalHidl.h index 6ffdaf1f64..5a4dab16fb 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.h +++ b/media/libaudiohal/4.0/EffectHalHidl.h @@ -14,23 +14,24 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H +#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H +#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H -#include +#include #include #include #include #include -using ::android::hardware::audio::effect::V2_0::EffectBufferConfig; -using ::android::hardware::audio::effect::V2_0::EffectConfig; -using ::android::hardware::audio::effect::V2_0::EffectDescriptor; -using ::android::hardware::audio::effect::V2_0::IEffect; +using ::android::hardware::audio::effect::V4_0::EffectBufferConfig; +using ::android::hardware::audio::effect::V4_0::EffectConfig; +using ::android::hardware::audio::effect::V4_0::EffectDescriptor; +using ::android::hardware::audio::effect::V4_0::IEffect; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; namespace android { +namespace V4_0 { class EffectHalHidl : public EffectHalInterface { @@ -69,7 +70,7 @@ class EffectHalHidl : public EffectHalInterface private: friend class EffectsFactoryHalHidl; typedef MessageQueue< - hardware::audio::effect::V2_0::Result, hardware::kSynchronizedReadWrite> StatusMQ; + hardware::audio::effect::V4_0::Result, hardware::kSynchronizedReadWrite> StatusMQ; sp mEffect; const uint64_t mEffectId; @@ -79,7 +80,7 @@ class EffectHalHidl : public EffectHalInterface std::unique_ptr mStatusMQ; EventFlag* mEfGroup; - static status_t analyzeResult(const hardware::audio::effect::V2_0::Result& result); + static status_t analyzeResult(const hardware::audio::effect::V4_0::Result& result); static void effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config); static void effectBufferConfigToHal( @@ -103,6 +104,7 @@ class EffectHalHidl : public EffectHalInterface status_t setProcessBuffers(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H +#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp index 0d40e6d9d0..301175e78d 100644 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp @@ -25,13 +25,14 @@ #include "EffectsFactoryHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::common::V2_0::Uuid; -using ::android::hardware::audio::effect::V2_0::IEffect; -using ::android::hardware::audio::effect::V2_0::Result; +using ::android::hardware::audio::common::V4_0::HidlUtils; +using ::android::hardware::audio::common::V4_0::Uuid; +using ::android::hardware::audio::effect::V4_0::IEffect; +using ::android::hardware::audio::effect::V4_0::Result; using ::android::hardware::Return; namespace android { +namespace V4_0 { EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { mEffectsFactory = IEffectsFactory::getService(); @@ -147,4 +148,5 @@ status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h index 82b54814b1..680b7a1322 100644 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h @@ -14,19 +14,20 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H +#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H +#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H -#include -#include +#include +#include #include #include "ConversionHelperHidl.h" namespace android { +namespace V4_0 { -using ::android::hardware::audio::effect::V2_0::EffectDescriptor; -using ::android::hardware::audio::effect::V2_0::IEffectsFactory; +using ::android::hardware::audio::effect::V4_0::EffectDescriptor; +using ::android::hardware::audio::effect::V4_0::IEffectsFactory; using ::android::hardware::hidl_vec; class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl @@ -68,6 +69,7 @@ class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public Conversi status_t queryAllDescriptors(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H +#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/4.0/StreamHalHidl.cpp index 0cafa365e2..9ba3b694e2 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.cpp +++ b/media/libaudiohal/4.0/StreamHalHidl.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "StreamHalHidl" //#define LOG_NDEBUG 0 -#include +#include #include #include #include @@ -26,23 +26,24 @@ #include "EffectHalHidl.h" #include "StreamHalHidl.h" -using ::android::hardware::audio::common::V2_0::AudioChannelMask; -using ::android::hardware::audio::common::V2_0::AudioFormat; -using ::android::hardware::audio::common::V2_0::ThreadInfo; -using ::android::hardware::audio::V2_0::AudioDrain; -using ::android::hardware::audio::V2_0::IStreamOutCallback; -using ::android::hardware::audio::V2_0::MessageQueueFlagBits; -using ::android::hardware::audio::V2_0::MmapBufferInfo; -using ::android::hardware::audio::V2_0::MmapPosition; -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; -using ::android::hardware::audio::V2_0::TimeSpec; +using ::android::hardware::audio::common::V4_0::AudioChannelMask; +using ::android::hardware::audio::common::V4_0::AudioFormat; +using ::android::hardware::audio::common::V4_0::ThreadInfo; +using ::android::hardware::audio::V4_0::AudioDrain; +using ::android::hardware::audio::V4_0::IStreamOutCallback; +using ::android::hardware::audio::V4_0::MessageQueueFlagBits; +using ::android::hardware::audio::V4_0::MmapBufferInfo; +using ::android::hardware::audio::V4_0::MmapPosition; +using ::android::hardware::audio::V4_0::ParameterValue; +using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::audio::V4_0::TimeSpec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; using ::android::hardware::Void; -using ReadCommand = ::android::hardware::audio::V2_0::IStreamIn::ReadCommand; +using ReadCommand = ::android::hardware::audio::V4_0::IStreamIn::ReadCommand; namespace android { +namespace V4_0 { StreamHalHidl::StreamHalHidl(IStream *stream) : ConversionHelperHidl("Stream"), @@ -749,4 +750,5 @@ status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { } } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/StreamHalHidl.h b/media/libaudiohal/4.0/StreamHalHidl.h index d4ab943419..8d4dc8cda2 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.h +++ b/media/libaudiohal/4.0/StreamHalHidl.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_H -#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H +#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H +#define ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -29,18 +29,19 @@ #include "ConversionHelperHidl.h" #include "StreamPowerLog.h" -using ::android::hardware::audio::V2_0::IStream; -using ::android::hardware::audio::V2_0::IStreamIn; -using ::android::hardware::audio::V2_0::IStreamOut; +using ::android::hardware::audio::V4_0::IStream; +using ::android::hardware::audio::V4_0::IStreamIn; +using ::android::hardware::audio::V4_0::IStreamOut; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; using ::android::hardware::Return; -using ReadParameters = ::android::hardware::audio::V2_0::IStreamIn::ReadParameters; -using ReadStatus = ::android::hardware::audio::V2_0::IStreamIn::ReadStatus; -using WriteCommand = ::android::hardware::audio::V2_0::IStreamOut::WriteCommand; -using WriteStatus = ::android::hardware::audio::V2_0::IStreamOut::WriteStatus; +using ReadParameters = ::android::hardware::audio::V4_0::IStreamIn::ReadParameters; +using ReadStatus = ::android::hardware::audio::V4_0::IStreamIn::ReadStatus; +using WriteCommand = ::android::hardware::audio::V4_0::IStreamOut::WriteCommand; +using WriteStatus = ::android::hardware::audio::V4_0::IStreamOut::WriteStatus; namespace android { +namespace V4_0 { class DeviceHalHidl; @@ -234,6 +235,7 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { status_t prepareForReading(size_t bufferSize); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H +#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H diff --git a/media/libaudiohal/4.0/StreamHalLocal.cpp b/media/libaudiohal/4.0/StreamHalLocal.cpp index 8d61e24cac..fc6e047601 100644 --- a/media/libaudiohal/4.0/StreamHalLocal.cpp +++ b/media/libaudiohal/4.0/StreamHalLocal.cpp @@ -24,6 +24,7 @@ #include "StreamHalLocal.h" namespace android { +namespace V4_0 { StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) : mDevice(device), @@ -313,4 +314,5 @@ status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) return mStream->get_mmap_position(mStream, position); } +} // namespace V4_0 } // namespace android diff --git a/media/libaudiohal/4.0/StreamHalLocal.h b/media/libaudiohal/4.0/StreamHalLocal.h index c7136dff4e..076bc4cca4 100644 --- a/media/libaudiohal/4.0/StreamHalLocal.h +++ b/media/libaudiohal/4.0/StreamHalLocal.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_HAL_LOCAL_H -#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_H +#ifndef ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H +#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H #include #include "StreamPowerLog.h" namespace android { +namespace V4_0 { class DeviceHalLocal; @@ -205,6 +206,7 @@ class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { virtual ~StreamInHalLocal(); }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H +#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H diff --git a/media/libaudiohal/4.0/StreamPowerLog.h b/media/libaudiohal/4.0/StreamPowerLog.h index a78b1aa042..57b720147f 100644 --- a/media/libaudiohal/4.0/StreamPowerLog.h +++ b/media/libaudiohal/4.0/StreamPowerLog.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_H -#define ANDROID_HARDWARE_STREAM_POWER_LOG_H +#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H +#define ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H #include #include @@ -23,6 +23,7 @@ #include namespace android { +namespace V4_0 { class StreamPowerLog { public: @@ -97,6 +98,7 @@ private: size_t mFrameSize; }; +} // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H +#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H -- GitLab From 04c0edc8abc88c58ceb17beedaad116a1a7eb900 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Mon, 26 Feb 2018 16:27:02 -0800 Subject: [PATCH 0007/1530] Create Codec2.0 service Test: adb shell setenforce 0 adb shell /vendor/bin/hw/vendor.google.media.c2@1.0-service & adb shell /vendor/bin/hw/vendor.google.media.c2@1.0-service-system & adb shell lshal | grep media.c2 Test: Builds Bug: 31973802 Bug: 64121714 Change-Id: I00037f33836821b61bdb4d8773eb68379903ffd3 --- services/codec2/Android.bp | 71 +++++++++++++++ services/codec2/Android.mk | 43 +++++++++ .../codec2.system.base-arm.policy | 73 +++++++++++++++ .../codec2.system.base-x86.policy | 57 ++++++++++++ .../codec2.vendor.base-arm.policy | 73 +++++++++++++++ .../codec2.vendor.base-x86.policy | 57 ++++++++++++ services/codec2/system.cpp | 90 +++++++++++++++++++ services/codec2/vendor.cpp | 72 +++++++++++++++ ...ndor.google.media.c2@1.0-service-system.rc | 7 ++ .../vendor.google.media.c2@1.0-service.rc | 7 ++ 10 files changed, 550 insertions(+) create mode 100644 services/codec2/Android.bp create mode 100644 services/codec2/Android.mk create mode 100644 services/codec2/seccomp_policy/codec2.system.base-arm.policy create mode 100644 services/codec2/seccomp_policy/codec2.system.base-x86.policy create mode 100644 services/codec2/seccomp_policy/codec2.vendor.base-arm.policy create mode 100644 services/codec2/seccomp_policy/codec2.vendor.base-x86.policy create mode 100644 services/codec2/system.cpp create mode 100644 services/codec2/vendor.cpp create mode 100644 services/codec2/vendor.google.media.c2@1.0-service-system.rc create mode 100644 services/codec2/vendor.google.media.c2@1.0-service.rc diff --git a/services/codec2/Android.bp b/services/codec2/Android.bp new file mode 100644 index 0000000000..1d5d2054b9 --- /dev/null +++ b/services/codec2/Android.bp @@ -0,0 +1,71 @@ +cc_binary { + name: "vendor.google.media.c2@1.0-service", + defaults: ["hidl_defaults"], + soc_specific: true, + relative_install_path: "hw", + srcs: [ + "vendor.cpp", + ], + + init_rc: ["vendor.google.media.c2@1.0-service.rc"], + + shared_libs: [ + "vendor.google.media.c2@1.0", + "libavservices_minijail_vendor", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libstagefright_codec2_hidl@1.0", + "libstagefright_codec2_vndk", + "libutils", + ], + + arch: { + arm: { + required: ["codec2.vendor.base.policy"], + }, + x86: { + required: ["codec2.vendor.base.policy"], + }, + }, + + compile_multilib: "32", +} + +cc_binary { + name: "vendor.google.media.c2@1.0-service-system", + defaults: ["hidl_defaults"], + relative_install_path: "hw", + srcs: [ + "system.cpp", + ], + + init_rc: ["vendor.google.media.c2@1.0-service-system.rc"], + + shared_libs: [ + "vendor.google.media.c2@1.0", + "libavservices_minijail", + "libcutils", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libstagefright_codec2_hidl@1.0", + "libstagefright_codec2_vndk", + "libutils", + "libv4l2_c2componentstore", + ], + + arch: { + arm: { + required: ["codec2.system.base.policy"], + }, + x86: { + required: ["codec2.system.base.policy"], + }, + }, + + compile_multilib: "32", +} + diff --git a/services/codec2/Android.mk b/services/codec2/Android.mk new file mode 100644 index 0000000000..fa49875c98 --- /dev/null +++ b/services/codec2/Android.mk @@ -0,0 +1,43 @@ +LOCAL_PATH := $(call my-dir) + +# vendor service seccomp policy +ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) +include $(CLEAR_VARS) +LOCAL_MODULE := codec2.vendor.base.policy +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy +LOCAL_REQUIRED_MODULES := crash_dump.policy +ifdef TARGET_2ND_ARCH + ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) + LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy + else + LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy + endif +else + LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy +endif +include $(BUILD_PREBUILT) +endif + +# system service seccomp policy +ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) +include $(CLEAR_VARS) +LOCAL_MODULE := codec2.system.base.policy +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy +LOCAL_REQUIRED_MODULES := crash_dump.policy +ifdef TARGET_2ND_ARCH + ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) + LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_2ND_ARCH).policy + else + LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy + endif +else + LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy +endif +include $(BUILD_PREBUILT) +endif + +include $(call all-makefiles-under, $(LOCAL_PATH)) + diff --git a/services/codec2/seccomp_policy/codec2.system.base-arm.policy b/services/codec2/seccomp_policy/codec2.system.base-arm.policy new file mode 100644 index 0000000000..d5871d16a9 --- /dev/null +++ b/services/codec2/seccomp_policy/codec2.system.base-arm.policy @@ -0,0 +1,73 @@ +# 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. + +# Organized by frequency of systemcall - in descending order for +# best performance. +futex: 1 +ioctl: 1 +write: 1 +prctl: 1 +clock_gettime: 1 +getpriority: 1 +read: 1 +close: 1 +writev: 1 +dup: 1 +ppoll: 1 +mmap2: 1 +getrandom: 1 + +# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail +# parser support for '<' is in this needs to be modified to also prevent +# |old_address| and |new_address| from touching the exception vector page, which +# on ARM is statically loaded at 0xffff 0000. See +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html +# for more details. +mremap: arg3 == 3 +munmap: 1 +mprotect: 1 +madvise: 1 +openat: 1 +sigaltstack: 1 +clone: 1 +setpriority: 1 +getuid32: 1 +fstat64: 1 +fstatfs64: 1 +pread64: 1 +faccessat: 1 +readlinkat: 1 +exit: 1 +rt_sigprocmask: 1 +set_tid_address: 1 +restart_syscall: 1 +exit_group: 1 +rt_sigreturn: 1 +pipe2: 1 +gettimeofday: 1 +sched_yield: 1 +nanosleep: 1 +lseek: 1 +_llseek: 1 +sched_get_priority_max: 1 +sched_get_priority_min: 1 +statfs64: 1 +sched_setscheduler: 1 +fstatat64: 1 +ugetrlimit: 1 +getdents64: 1 +getrandom: 1 + +@include /system/etc/seccomp_policy/crash_dump.arm.policy + diff --git a/services/codec2/seccomp_policy/codec2.system.base-x86.policy b/services/codec2/seccomp_policy/codec2.system.base-x86.policy new file mode 100644 index 0000000000..20c7625b3b --- /dev/null +++ b/services/codec2/seccomp_policy/codec2.system.base-x86.policy @@ -0,0 +1,57 @@ +# 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. + +read: 1 +mprotect: 1 +prctl: 1 +openat: 1 +getuid32: 1 +writev: 1 +ioctl: 1 +close: 1 +mmap2: 1 +fstat64: 1 +madvise: 1 +fstatat64: 1 +futex: 1 +munmap: 1 +faccessat: 1 +_llseek: 1 +lseek: 1 +clone: 1 +sigaltstack: 1 +setpriority: 1 +restart_syscall: 1 +exit: 1 +exit_group: 1 +rt_sigreturn: 1 +ugetrlimit: 1 +readlinkat: 1 +_llseek: 1 +fstatfs64: 1 +pread64: 1 +mremap: 1 +dup: 1 +set_tid_address: 1 +write: 1 +nanosleep: 1 + +# Required by AddressSanitizer +gettid: 1 +sched_yield: 1 +getpid: 1 +gettid: 1 + +@include /system/etc/seccomp_policy/crash_dump.x86.policy + diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy new file mode 100644 index 0000000000..d5871d16a9 --- /dev/null +++ b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy @@ -0,0 +1,73 @@ +# 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. + +# Organized by frequency of systemcall - in descending order for +# best performance. +futex: 1 +ioctl: 1 +write: 1 +prctl: 1 +clock_gettime: 1 +getpriority: 1 +read: 1 +close: 1 +writev: 1 +dup: 1 +ppoll: 1 +mmap2: 1 +getrandom: 1 + +# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail +# parser support for '<' is in this needs to be modified to also prevent +# |old_address| and |new_address| from touching the exception vector page, which +# on ARM is statically loaded at 0xffff 0000. See +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html +# for more details. +mremap: arg3 == 3 +munmap: 1 +mprotect: 1 +madvise: 1 +openat: 1 +sigaltstack: 1 +clone: 1 +setpriority: 1 +getuid32: 1 +fstat64: 1 +fstatfs64: 1 +pread64: 1 +faccessat: 1 +readlinkat: 1 +exit: 1 +rt_sigprocmask: 1 +set_tid_address: 1 +restart_syscall: 1 +exit_group: 1 +rt_sigreturn: 1 +pipe2: 1 +gettimeofday: 1 +sched_yield: 1 +nanosleep: 1 +lseek: 1 +_llseek: 1 +sched_get_priority_max: 1 +sched_get_priority_min: 1 +statfs64: 1 +sched_setscheduler: 1 +fstatat64: 1 +ugetrlimit: 1 +getdents64: 1 +getrandom: 1 + +@include /system/etc/seccomp_policy/crash_dump.arm.policy + diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy new file mode 100644 index 0000000000..20c7625b3b --- /dev/null +++ b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy @@ -0,0 +1,57 @@ +# 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. + +read: 1 +mprotect: 1 +prctl: 1 +openat: 1 +getuid32: 1 +writev: 1 +ioctl: 1 +close: 1 +mmap2: 1 +fstat64: 1 +madvise: 1 +fstatat64: 1 +futex: 1 +munmap: 1 +faccessat: 1 +_llseek: 1 +lseek: 1 +clone: 1 +sigaltstack: 1 +setpriority: 1 +restart_syscall: 1 +exit: 1 +exit_group: 1 +rt_sigreturn: 1 +ugetrlimit: 1 +readlinkat: 1 +_llseek: 1 +fstatfs64: 1 +pread64: 1 +mremap: 1 +dup: 1 +set_tid_address: 1 +write: 1 +nanosleep: 1 + +# Required by AddressSanitizer +gettid: 1 +sched_yield: 1 +getpid: 1 +gettid: 1 + +@include /system/etc/seccomp_policy/crash_dump.x86.policy + diff --git a/services/codec2/system.cpp b/services/codec2/system.cpp new file mode 100644 index 0000000000..d6ec6449ed --- /dev/null +++ b/services/codec2/system.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "vendor.google.media.c2@1.0-service" + +#include +#include +#include + +#include +#include +#include + +// TODO: Remove this once "setenv()" call is removed. +#include + +// This is created by module "codec2.system.base.policy". This can be modified. +static constexpr char kBaseSeccompPolicyPath[] = + "/system/etc/seccomp_policy/codec2.system.base.policy"; + +// Additional device-specific seccomp permissions can be added in this file. +static constexpr char kExtSeccompPolicyPath[] = + "/system/etc/seccomp_policy/codec2.system.ext.policy"; + +int main(int /* argc */, char** /* argv */) { + ALOGD("vendor.google.media.c2@1.0-service-system starting..."); + + // TODO: Remove this when all the build settings and sepolicies are in place. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + + signal(SIGPIPE, SIG_IGN); + android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath); + + // Extra threads may be needed to handle a stacked IPC sequence that + // contains alternating binder and hwbinder calls. (See b/35283480.) + android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */); + + // Create IComponentStore service. + { + using namespace ::vendor::google::media::c2::V1_0; + android::sp store = + new implementation::ComponentStore( + android::GetCodec2PlatformComponentStore()); + if (store == nullptr) { + ALOGE("Cannot create Codec2's IComponentStore system service."); + } else { + if (store->registerAsService("system") != android::OK) { + ALOGE("Cannot register Codec2's " + "IComponentStore system service."); + } else { + ALOGI("Codec2's IComponentStore system service created."); + } + } + + // To enable the v4l2 service, set this sysprop and add "v4l2" instance + // to the system manifest file. + if (property_get_bool("debug.stagefright.ccodec_v4l2", false)) { + store = new implementation::ComponentStore( + android::GetCodec2VDAComponentStore()); + if (store == nullptr) { + ALOGE("Cannot create Codec2's IComponentStore V4L2 service."); + } else { + if (store->registerAsService("v4l2") != android::OK) { + ALOGE("Cannot register Codec2's " + "IComponentStore V4L2 service."); + } else { + ALOGI("Codec2's IComponentStore V4L2 service created."); + } + } + } + } + + android::hardware::joinRpcThreadpool(); + return 0; +} + diff --git a/services/codec2/vendor.cpp b/services/codec2/vendor.cpp new file mode 100644 index 0000000000..3993ebaae0 --- /dev/null +++ b/services/codec2/vendor.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "vendor.google.media.c2@1.0-service" + +// TODO: Replace this with vendor's implementation +#include + +#include +#include +#include + +// TODO: Remove this once "setenv()" call is removed. +#include + +// This is created by module "codec2.vendor.base.policy". This can be modified. +static constexpr char kBaseSeccompPolicyPath[] = + "/vendor/etc/seccomp_policy/codec2.vendor.base.policy"; + +// Additional device-specific seccomp permissions can be added in this file. +static constexpr char kExtSeccompPolicyPath[] = + "/vendor/etc/seccomp_policy/codec2.vendor.ext.policy"; + +int main(int /* argc */, char** /* argv */) { + ALOGD("vendor.google.media.c2@1.0-service starting..."); + + // TODO: Remove this when all the build settings and sepolicies are in place. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + + signal(SIGPIPE, SIG_IGN); + android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath); + + // Extra threads may be needed to handle a stacked IPC sequence that + // contains alternating binder and hwbinder calls. (See b/35283480.) + android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */); + + // Create IComponentStore service. + { + using namespace ::vendor::google::media::c2::V1_0; + android::sp store = + new implementation::ComponentStore( + android::GetCodec2PlatformComponentStore()); + if (store == nullptr) { + ALOGE("Cannot create Codec2's IComponentStore service."); + } else { + if (store->registerAsService("default") != android::OK) { + ALOGE("Cannot register Codec2's " + "IComponentStore service."); + } else { + ALOGI("Codec2's IComponentStore service created."); + } + } + } + + android::hardware::joinRpcThreadpool(); + return 0; +} + diff --git a/services/codec2/vendor.google.media.c2@1.0-service-system.rc b/services/codec2/vendor.google.media.c2@1.0-service-system.rc new file mode 100644 index 0000000000..0577a1d472 --- /dev/null +++ b/services/codec2/vendor.google.media.c2@1.0-service-system.rc @@ -0,0 +1,7 @@ +service vendor-google-media-c2-system-hal-1-0 /system/bin/hw/vendor.google.media.c2@1.0-service-system + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks + diff --git a/services/codec2/vendor.google.media.c2@1.0-service.rc b/services/codec2/vendor.google.media.c2@1.0-service.rc new file mode 100644 index 0000000000..3e7e0a696a --- /dev/null +++ b/services/codec2/vendor.google.media.c2@1.0-service.rc @@ -0,0 +1,7 @@ +service vendor-google-media-c2-hal-1-0 /vendor/bin/hw/vendor.google.media.c2@1.0-service + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks + -- GitLab From 0fafb401c66e83641d78c04275c95f8e98677b3d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 2 Mar 2018 14:53:29 -0800 Subject: [PATCH 0008/1530] save codec info during codec construction Save the codec info used to create the codec for use when get caps is called. bug: 74073607 Change-Id: Id12a5b96f5a00226f8cf1e71f32655a14aeac014 --- media/libstagefright/ACodec.cpp | 5 +-- media/libstagefright/MediaCodec.cpp | 45 ++++++++++++++++--- media/libstagefright/MediaCodecList.cpp | 8 +--- .../include/media/stagefright/CodecBase.h | 5 ++- .../include/media/stagefright/MediaCodec.h | 4 ++ .../media/stagefright/MediaCodecList.h | 3 +- 6 files changed, 51 insertions(+), 19 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index b296622204..220649939f 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -6346,9 +6346,6 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { sp notify = new AMessage(kWhatOMXDied, mCodec); - Vector matchingCodecs; - Vector owners; - AString componentName; CHECK(msg->findString("componentName", &componentName)); @@ -6424,7 +6421,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { mCodec->mOMX = omx; mCodec->mOMXNode = omxNode; - mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str()); + mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str(), info); mCodec->changeState(mCodec->mLoadedState); return true; diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 7d5c63aa85..e59259d7bf 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -278,7 +278,8 @@ public: virtual void onReleaseCompleted() override; virtual void onFlushCompleted() override; virtual void onError(status_t err, enum ActionCode actionCode) override; - virtual void onComponentAllocated(const char *componentName) override; + virtual void onComponentAllocated( + const char *componentName, const sp &codecInfo) override; virtual void onComponentConfigured( const sp &inputFormat, const sp &outputFormat) override; virtual void onInputSurfaceCreated( @@ -338,10 +339,14 @@ void CodecCallback::onError(status_t err, enum ActionCode actionCode) { notify->post(); } -void CodecCallback::onComponentAllocated(const char *componentName) { +void CodecCallback::onComponentAllocated( + const char *componentName, const sp &codecInfo) { sp notify(mNotify->dup()); notify->setInt32("what", kWhatComponentAllocated); notify->setString("componentName", componentName); + if (codecInfo != nullptr) { + notify->setObject("codecInfo", codecInfo); + } notify->post(); } @@ -422,14 +427,12 @@ sp MediaCodec::CreateByType( const sp &looper, const AString &mime, bool encoder, status_t *err, pid_t pid, uid_t uid) { Vector matchingCodecs; - Vector owners; MediaCodecList::findMatchingCodecs( mime.c_str(), encoder, 0, - &matchingCodecs, - &owners); + &matchingCodecs); if (err != NULL) { *err = NAME_NOT_FOUND; @@ -1205,6 +1208,22 @@ status_t MediaCodec::getName(AString *name) const { return OK; } +status_t MediaCodec::getCodecInfo(sp *codecInfo) const { + sp msg = new AMessage(kWhatGetCodecInfo, this); + + sp response; + status_t err; + if ((err = PostAndAwaitResponse(msg, &response)) != OK) { + return err; + } + + sp obj; + CHECK(response->findObject("codecInfo", &obj)); + *codecInfo = static_cast(obj.get()); + + return OK; +} + status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) { reply = NULL; @@ -1588,6 +1607,11 @@ void MediaCodec::onMessageReceived(const sp &msg) { CHECK(msg->findString("componentName", &mComponentName)); + sp obj; + if (msg->findObject("codecInfo", &obj)) { + mCodecInfo = static_cast(obj.get()); + } + if (mComponentName.c_str()) { mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str()); } @@ -2595,6 +2619,17 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } + case kWhatGetCodecInfo: + { + sp replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp response = new AMessage; + response->setObject("codecInfo", mCodecInfo); + response->postReply(replyID); + break; + } + case kWhatSetParameters: { sp replyID; diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index f595646ff8..924488680e 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -307,11 +307,8 @@ static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2 //static void MediaCodecList::findMatchingCodecs( const char *mime, bool encoder, uint32_t flags, - Vector *matches, Vector *owners) { + Vector *matches) { matches->clear(); - if (owners != nullptr) { - owners->clear(); - } const sp list = getInstance(); if (list == nullptr) { @@ -337,9 +334,6 @@ void MediaCodecList::findMatchingCodecs( ALOGV("skipping SW codec '%s'", componentName.c_str()); } else { matches->push(componentName); - if (owners != nullptr) { - owners->push(AString(info->getOwnerName())); - } ALOGV("matching '%s'", componentName.c_str()); } } diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h index 268662fad1..9606fd9a0b 100644 --- a/media/libstagefright/include/media/stagefright/CodecBase.h +++ b/media/libstagefright/include/media/stagefright/CodecBase.h @@ -95,8 +95,11 @@ struct CodecBase : public AHandler, /* static */ ColorUtils { * * @param componentName the unique name of the component specified in * MediaCodecList. + * @param codecInfo the associated codec info if available. */ - virtual void onComponentAllocated(const char *componentName) = 0; + virtual void onComponentAllocated( + const char *componentName, + const sp &codecInfo = nullptr) = 0; /** * Notify MediaCodec that the underlying component is configured. * diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index e7faea5818..ef8de1f691 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -184,6 +184,8 @@ struct MediaCodec : public AHandler { status_t getName(AString *componentName) const; + status_t getCodecInfo(sp *codecInfo) const; + status_t getMetrics(MediaAnalyticsItem * &reply); status_t setParameters(const sp ¶ms); @@ -248,6 +250,7 @@ private: kWhatRequestIDRFrame = 'ridr', kWhatRequestActivityNotification = 'racN', kWhatGetName = 'getN', + kWhatGetCodecInfo = 'gCoI', kWhatSetParameters = 'setP', kWhatSetCallback = 'setC', kWhatSetNotification = 'setN', @@ -308,6 +311,7 @@ private: sp mCodecLooper; sp mCodec; AString mComponentName; + sp mCodecInfo; sp mReplyID; uint32_t mFlags; status_t mStickyError; diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h index d46fe8544c..bb4da09333 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecList.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h @@ -74,8 +74,7 @@ struct MediaCodecList : public BnMediaCodecList { const char *mime, bool createEncoder, uint32_t flags, - Vector *matchingCodecs, - Vector *owners = nullptr); + Vector *matchingCodecs); static bool isSoftwareCodec(const AString &componentName); -- GitLab From 1afda1905dffbc9833779b80a01de0b113346ba8 Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Mon, 5 Mar 2018 13:40:29 +0900 Subject: [PATCH 0009/1530] VideoView2: Move MediaControlView2's hidden constants Bug: 74173212 Test: VideoViewTest app Change-Id: I7038130e91f48b7d9b4e70d08221f6397e4e3317 --- .../com/android/widget/MediaControlView2Impl.java | 13 ++++++++++--- .../src/com/android/widget/VideoView2Impl.java | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java index 1940953c4d..69febc2b3e 100644 --- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java +++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java @@ -75,6 +75,13 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement"; static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus"; + // String for receiving command to show subtitle from MediaSession. + static final String COMMAND_SHOW_SUBTITLE = "showSubtitle"; + // String for receiving command to hide subtitle from MediaSession. + static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle"; + // TODO: remove once the implementation is revised + public static final String COMMAND_SET_FULLSCREEN = "setFullscreen"; + private static final int MAX_PROGRESS = 1000; private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000; private static final int REWIND_TIME_MS = 10000; @@ -743,12 +750,12 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie if (!mSubtitleIsEnabled) { mSubtitleButton.setImageDrawable( mResources.getDrawable(R.drawable.ic_media_subtitle_enabled, null)); - mController.sendCommand(MediaControlView2.COMMAND_SHOW_SUBTITLE, null, null); + mController.sendCommand(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, null); mSubtitleIsEnabled = true; } else { mSubtitleButton.setImageDrawable( mResources.getDrawable(R.drawable.ic_media_subtitle_disabled, null)); - mController.sendCommand(MediaControlView2.COMMAND_HIDE_SUBTITLE, null, null); + mController.sendCommand(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, null); mSubtitleIsEnabled = false; } } @@ -768,7 +775,7 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie } Bundle args = new Bundle(); args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen); - mController.sendCommand(MediaControlView2.COMMAND_SET_FULLSCREEN, args, null); + mController.sendCommand(MediaControlView2Impl.COMMAND_SET_FULLSCREEN, args, null); mIsFullScreen = isEnteringFullScreen; } diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java index d23395c5bd..c3ca057bd2 100644 --- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java +++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java @@ -1001,13 +1001,13 @@ public class VideoView2Impl extends BaseLayout mRouteSessionCallback.onCommand(command, args, receiver); } else { switch (command) { - case MediaControlView2.COMMAND_SHOW_SUBTITLE: + case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE: mInstance.setSubtitleEnabled(true); break; - case MediaControlView2.COMMAND_HIDE_SUBTITLE: + case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE: mInstance.setSubtitleEnabled(false); break; - case MediaControlView2.COMMAND_SET_FULLSCREEN: + case MediaControlView2Impl.COMMAND_SET_FULLSCREEN: if (mFullScreenRequestListener != null) { mFullScreenRequestListener.onFullScreenRequest( mInstance, -- GitLab From 6145803cf8ad2b657d52348dbfbcebf031b49d3d Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 22 Feb 2018 11:16:23 -0800 Subject: [PATCH 0010/1530] Additional media.metrics for media recorder capture the output mime times (audio + video) capture duration recorded capture info about how the 'pause' capability is used [duration + count] Bug: 73774549 Test: manual recordings w&w/o pauses Change-Id: Iae3d95984a97babb52afdbbb09872e44dd869573 --- .../StagefrightRecorder.cpp | 105 ++++++++++++++---- .../StagefrightRecorder.h | 7 +- 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 5cbf976235..544a3790b6 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -89,6 +89,13 @@ static const char *kRecorderVideoProfile = "android.media.mediarecorder.video-en static const char *kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale"; static const char *kRecorderWidth = "android.media.mediarecorder.width"; +// new fields, not yet frozen in the public Java API definitions +static const char *kRecorderAudioMime = "android.media.mediarecorder.audio.mime"; +static const char *kRecorderVideoMime = "android.media.mediarecorder.video.mime"; +static const char *kRecorderDurationMs = "android.media.mediarecorder.durationMs"; +static const char *kRecorderPaused = "android.media.mediarecorder.pausedMs"; +static const char *kRecorderNumPauses = "android.media.mediarecorder.NPauses"; + // To collect the encoder usage for the battery app static void addBatteryData(uint32_t params) { @@ -126,21 +133,18 @@ StagefrightRecorder::~StagefrightRecorder() { } // log the current record, provided it has some information worth recording - if (mAnalyticsDirty && mAnalyticsItem != NULL) { - updateMetrics(); - if (mAnalyticsItem->count() > 0) { - mAnalyticsItem->selfrecord(); - } - delete mAnalyticsItem; - mAnalyticsItem = NULL; - } + // NB: this also reclaims & clears mAnalyticsItem. + flushAndResetMetrics(false); } void StagefrightRecorder::updateMetrics() { ALOGV("updateMetrics"); - // we'll populate the values from the raw fields. - // (NOT going to populate as we go through the various set* ops) + // we run as part of the media player service; what we really want to + // know is the app which requested the recording. + mAnalyticsItem->setUid(mClientUid); + + // populate the values from the raw fields. // TBD mOutputFormat = OUTPUT_FORMAT_THREE_GPP; // TBD mAudioEncoder = AUDIO_ENCODER_AMR_NB; @@ -168,7 +172,6 @@ void StagefrightRecorder::updateMetrics() { // TBD mTrackEveryTimeDurationUs = 0; mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable); mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps); - // TBD mCaptureFps = -1.0; // TBD mCameraSourceTimeLapse = NULL; // TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid; // TBD mEncoderProfiles = MediaProfiles::getInstance(); @@ -177,14 +180,17 @@ void StagefrightRecorder::updateMetrics() { // PII mLongitudex10000 = -3600000; // TBD mTotalBitRate = 0; - // TBD: some duration information (capture, paused) - // - + // duration information (recorded, paused, # of pauses) + mAnalyticsItem->setInt64(kRecorderDurationMs, (mDurationRecordedUs+500)/1000 ); + if (mNPauses != 0) { + mAnalyticsItem->setInt64(kRecorderPaused, (mDurationPausedUs+500)/1000 ); + mAnalyticsItem->setInt32(kRecorderNumPauses, mNPauses); + } } -void StagefrightRecorder::resetMetrics() { - ALOGV("resetMetrics"); - // flush anything we have, restart the record +void StagefrightRecorder::flushAndResetMetrics(bool reinitialize) { + ALOGV("flushAndResetMetrics"); + // flush anything we have, maybe setup a new record if (mAnalyticsDirty && mAnalyticsItem != NULL) { updateMetrics(); if (mAnalyticsItem->count() > 0) { @@ -193,8 +199,10 @@ void StagefrightRecorder::resetMetrics() { delete mAnalyticsItem; mAnalyticsItem = NULL; } - mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder); mAnalyticsDirty = false; + if (reinitialize) { + mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder); + } } status_t StagefrightRecorder::init() { @@ -1030,6 +1038,8 @@ status_t StagefrightRecorder::start() { mAnalyticsDirty = true; mStarted = true; + mStartedRecordingUs = systemTime() / 1000; + uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted; if (mAudioSource != AUDIO_SOURCE_CNT) { params |= IMediaPlayerService::kBatteryDataTrackAudio; @@ -1109,6 +1119,14 @@ sp StagefrightRecorder::createAudioSource() { return NULL; } + // log audio mime type for media metrics + if (mAnalyticsItem != NULL) { + AString audiomime; + if (format->findString("mime", &audiomime)) { + mAnalyticsItem->setCString(kRecorderAudioMime, audiomime.c_str()); + } + } + int32_t maxInputSize; CHECK(audioSource->getFormat()->findInt32( kKeyMaxInputSize, &maxInputSize)); @@ -1655,6 +1673,14 @@ status_t StagefrightRecorder::setupVideoEncoder( break; } + // log video mime type for media metrics + if (mAnalyticsItem != NULL) { + AString videomime; + if (format->findString("mime", &videomime)) { + mAnalyticsItem->setCString(kRecorderVideoMime, videomime.c_str()); + } + } + if (cameraSource != NULL) { sp meta = cameraSource->getFormat(); @@ -1917,6 +1943,13 @@ status_t StagefrightRecorder::pause() { sp meta = new MetaData; meta->setInt64(kKeyTime, mPauseStartTimeUs); + if (mStartedRecordingUs != 0) { + // should always be true + int64_t recordingUs = mPauseStartTimeUs - mStartedRecordingUs; + mDurationRecordedUs += recordingUs; + mStartedRecordingUs = 0; + } + if (mAudioEncoderSource != NULL) { mAudioEncoderSource->pause(); } @@ -1975,6 +2008,16 @@ status_t StagefrightRecorder::resume() { source->setInputBufferTimeOffset((int64_t)timeOffset); source->start(meta.get()); } + + + // sum info on pause duration + // (ignore the 30msec of overlap adjustment factored into mTotalPausedDurationUs) + int64_t pausedUs = resumeStartTimeUs - mPauseStartTimeUs; + mDurationPausedUs += pausedUs; + mNPauses++; + // and a timestamp marking that we're back to recording.... + mStartedRecordingUs = resumeStartTimeUs; + mPauseStartTimeUs = 0; return OK; @@ -2003,10 +2046,28 @@ status_t StagefrightRecorder::stop() { mWriter.clear(); } - resetMetrics(); + // account for the last 'segment' -- whether paused or recording + if (mPauseStartTimeUs != 0) { + // we were paused + int64_t additive = stopTimeUs - mPauseStartTimeUs; + mDurationPausedUs += additive; + mNPauses++; + } else if (mStartedRecordingUs != 0) { + // we were recording + int64_t additive = stopTimeUs - mStartedRecordingUs; + mDurationRecordedUs += additive; + } else { + ALOGW("stop while neither recording nor paused"); + } + + flushAndResetMetrics(true); + mDurationRecordedUs = 0; + mDurationPausedUs = 0; + mNPauses = 0; mTotalPausedDurationUs = 0; mPauseStartTimeUs = 0; + mStartedRecordingUs = 0; mGraphicBufferProducer.clear(); mPersistentSurface.clear(); @@ -2085,6 +2146,12 @@ status_t StagefrightRecorder::reset() { mLongitudex10000 = -3600000; mTotalBitRate = 0; + // tracking how long we recorded. + mDurationRecordedUs = 0; + mStartedRecordingUs = 0; + mDurationPausedUs = 0; + mNPauses = 0; + mOutputFd = -1; return OK; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 18c9256e25..faa2e59428 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -95,7 +95,7 @@ private: MediaAnalyticsItem *mAnalyticsItem; bool mAnalyticsDirty; - void resetMetrics(); + void flushAndResetMetrics(bool reinitialize); void updateMetrics(); audio_source_t mAudioSource; @@ -127,6 +127,11 @@ private: int32_t mStartTimeOffsetMs; int32_t mTotalBitRate; + int64_t mDurationRecordedUs; + int64_t mStartedRecordingUs; + int64_t mDurationPausedUs; + int32_t mNPauses; + bool mCaptureFpsEnable; double mCaptureFps; int64_t mTimeBetweenCaptureUs; -- GitLab From 5108d79aa21676a24de8dce3cf237f42fa301564 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 5 Mar 2018 12:25:58 -0800 Subject: [PATCH 0011/1530] Revert "audioflinger: Remove "exiting=1" parameter" This reverts commit e2bc208f05ed430a18a9078799b6b6d56e3d6dda. Bug: 74142786 Test: with Android Auto head unit simulator Change-Id: I9f96da7ba67b3ed774c9b9bdab7cee390d3f0ca2 --- services/audioflinger/Threads.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 1301998da8..3134323efa 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1837,6 +1837,10 @@ void AudioFlinger::PlaybackThread::onFirstRef() void AudioFlinger::PlaybackThread::preExit() { ALOGV(" preExit()"); + // FIXME this is using hard-coded strings but in the future, this functionality will be + // converted to use audio HAL extensions required to support tunneling + status_t result = mOutput->stream->setParameters(String8("exiting=1")); + ALOGE_IF(result != OK, "Error when setting parameters on exit: %d", result); } // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held -- GitLab From 5b81f550759c975fe6716e4e7870b11e523834d0 Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Fri, 2 Mar 2018 09:21:30 -0800 Subject: [PATCH 0012/1530] Revert "Revert "Use start/finish app op API for mic use"" This reverts commit 599ec462d94136949e0c90419ea99b0d9ba83eac. In addition it fixes an incorrect comparison against opPackageName instead of resolvedOpPackageName creating incorrect short circuit. Test: atest android.media.cts.AudioRecordAppOpTest "Okay Google" works fine bug: 64085448 Change-Id: I77a8486dfd6e4f020efdf05d28a856a32579ef31 --- services/audioflinger/AudioFlinger.cpp | 1 + services/audioflinger/ServiceUtilities.cpp | 116 ++++++++++++------ services/audioflinger/ServiceUtilities.h | 4 + .../service/AudioPolicyInterfaceImpl.cpp | 7 +- 4 files changed, 87 insertions(+), 41 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 229e08ee24..803338213e 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3051,6 +3051,7 @@ sp AudioFlinger::createEffect( // check recording permission for visualizer if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) && + // TODO: Do we need to start/stop op - i.e. is there recording being performed? !recordingAllowed(opPackageName, pid, IPCThreadState::self()->getCallingUid())) { lStatus = PERMISSION_DENIED; goto Exit; diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index f08698e41b..aa267ead6c 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -30,6 +30,8 @@ namespace android { +static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO"); + // Not valid until initialized by AudioFlinger constructor. It would have to be // re-initialized if the process containing AudioFlinger service forks (which it doesn't). // This is often used to validate binder interface calls within audioserver @@ -48,26 +50,11 @@ bool isTrustedCallingUid(uid_t uid) { } } -bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) { - // we're always OK. - if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; - - static const String16 sRecordAudio("android.permission.RECORD_AUDIO"); - - // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) - // may open a record track on behalf of a client. Note that pid may be a tid. - // IMPORTANT: Don't use PermissionCache - a runtime permission and may change. - const bool ok = checkPermission(sRecordAudio, pid, uid); - if (!ok) { - ALOGE("Request requires android.permission.RECORD_AUDIO"); - return false; +static String16 resolveCallingPackage(PermissionController& permissionController, + const String16& opPackageName, uid_t uid) { + if (opPackageName.size() > 0) { + return opPackageName; } - - // To permit command-line native tests - if (uid == AID_ROOT) return true; - - String16 checkedOpPackageName = opPackageName; - // In some cases the calling code has no access to the package it runs under. // For example, code using the wilhelm framework's OpenSL-ES APIs. In this // case we will get the packages for the calling UID and pick the first one @@ -75,40 +62,89 @@ bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) { // as for legacy apps we will toggle the app op for all packages in the UID. // The caveat is that the operation may be attributed to the wrong package and // stats based on app ops may be slightly off. - if (checkedOpPackageName.size() <= 0) { - sp sm = defaultServiceManager(); - sp binder = sm->getService(String16("permission")); - if (binder == 0) { - ALOGE("Cannot get permission service"); - return false; - } + Vector packages; + permissionController.getPackagesForUid(uid, packages); + if (packages.isEmpty()) { + ALOGE("No packages for uid %d", uid); + return opPackageName; // empty string + } + return packages[0]; +} - sp permCtrl = interface_cast(binder); - Vector packages; +static inline bool isAudioServerOrRoot(uid_t uid) { + // AID_ROOT is OK for command-line tests. Native unforked audioserver always OK. + return uid == AID_ROOT || uid == AID_AUDIOSERVER ; +} - permCtrl->getPackagesForUid(uid, packages); +static bool checkRecordingInternal(const String16& opPackageName, pid_t pid, + uid_t uid, bool start) { + // Okay to not track in app ops as audio server is us and if + // device is rooted security model is considered compromised. + if (isAudioServerOrRoot(uid)) return true; - if (packages.isEmpty()) { - ALOGE("No packages for calling UID"); - return false; - } - checkedOpPackageName = packages[0]; + // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) + // may open a record track on behalf of a client. Note that pid may be a tid. + // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE. + PermissionController permissionController; + const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid); + if (!ok) { + ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str()); + return false; } - AppOpsManager appOps; - if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, checkedOpPackageName) - != AppOpsManager::MODE_ALLOWED) { - ALOGE("Request denied by app op OP_RECORD_AUDIO"); + String16 resolvedOpPackageName = resolveCallingPackage( + permissionController, opPackageName, uid); + if (resolvedOpPackageName.size() == 0) { return false; } + AppOpsManager appOps; + const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio); + if (start) { + if (appOps.startOpNoThrow(op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false) + != AppOpsManager::MODE_ALLOWED) { + ALOGE("Request denied by app op: %d", op); + return false; + } + } else { + if (appOps.noteOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) { + ALOGE("Request denied by app op: %d", op); + return false; + } + } + return true; } +bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) { + return checkRecordingInternal(opPackageName, pid, uid, /*start*/ false); +} + +bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid) { + return checkRecordingInternal(opPackageName, pid, uid, /*start*/ true); +} + +void finishRecording(const String16& opPackageName, uid_t uid) { + // Okay to not track in app ops as audio server is us and if + // device is rooted security model is considered compromised. + if (isAudioServerOrRoot(uid)) return; + + PermissionController permissionController; + String16 resolvedOpPackageName = resolveCallingPackage( + permissionController, opPackageName, uid); + if (resolvedOpPackageName.size() == 0) { + return; + } + + AppOpsManager appOps; + const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio); + appOps.finishOp(op, uid, resolvedOpPackageName); +} + bool captureAudioOutputAllowed(pid_t pid, uid_t uid) { if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT"); - bool ok = checkPermission(sCaptureAudioOutput, pid, uid); + bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid); if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT"); return ok; } @@ -155,7 +191,7 @@ bool dumpAllowed() { bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) { static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE"); - bool ok = checkPermission(sModifyPhoneState, pid, uid); + bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid); if (!ok) ALOGE("Request requires android.permission.MODIFY_PHONE_STATE"); return ok; } diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h index 83533dd659..f45ada1a4a 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/services/audioflinger/ServiceUtilities.h @@ -16,11 +16,15 @@ #include +#include + namespace android { extern pid_t getpid_cached; bool isTrustedCallingUid(uid_t uid); bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid); +bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid); +void finishRecording(const String16& opPackageName, uid_t uid); bool captureAudioOutputAllowed(pid_t pid, uid_t uid); bool captureHotwordAllowed(pid_t pid, uid_t uid); bool settingsAllowed(); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 306de3f123..8f0c8468b9 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -412,7 +412,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc } // check calling permissions - if (!recordingAllowed(client->opPackageName, client->pid, client->uid)) { + if (!startRecording(client->opPackageName, client->pid, client->uid)) { ALOGE("%s permission denied: recording not allowed for uid %d pid %d", __func__, client->uid, client->pid); return PERMISSION_DENIED; @@ -439,6 +439,8 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE) { //TODO: check concurrent capture permission } + } else { + finishRecording(client->opPackageName, client->uid); } return status; @@ -457,6 +459,9 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) } sp client = mAudioRecordClients.valueAt(index); + // finish the recording app op + finishRecording(client->opPackageName, client->uid); + return mAudioPolicyManager->stopInput(client->input, client->session); } -- GitLab From 4a7484bdee0806caacdce9f5ddc598e883981894 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 23 Feb 2018 19:11:06 -0800 Subject: [PATCH 0013/1530] Audio V4: Introduce 4.0 shim to HIDL The difference between the 2.0 and the 4.0 is very small by design and most of the modification have been made in a retro compatible way The intent is to be able to merge the two implementation but it could not be done in this patch due to time constrains. Bug: 38184704 Test: compile Change-Id: I4738928313b7fd1f6332c0f22bb802d4fba41d82 Signed-off-by: Kevin Rocard --- media/libaudiohal/4.0/Android.bp | 57 +++++++++++++++++++ media/libaudiohal/4.0/DeviceHalHidl.cpp | 24 ++++++-- .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 27 +-------- media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 2 - .../4.0/DevicesFactoryHalHybrid.cpp | 2 +- media/libaudiohal/4.0/EffectHalHidl.cpp | 6 +- .../libaudiohal/4.0/EffectsFactoryHalHidl.cpp | 4 +- media/libaudiohal/4.0/StreamHalHidl.cpp | 14 +++-- media/libaudiohal/4.0/StreamHalLocal.cpp | 1 + media/libaudiohal/4.0/VersionUtils.h | 49 ++++++++++++++++ .../4.0}/DevicesFactoryHalHybrid.h | 0 .../libaudiohal/4.0}/EffectsFactoryHalHidl.h | 0 media/libaudiohal/Android.bp | 5 +- .../DevicesFactoryHalInterface.cpp | 10 +++- .../EffectsFactoryHalInterface.cpp | 16 +++--- 15 files changed, 161 insertions(+), 56 deletions(-) create mode 100644 media/libaudiohal/4.0/Android.bp create mode 100644 media/libaudiohal/4.0/VersionUtils.h rename media/libaudiohal/4.0/{ => include/libaudiohal/4.0}/DevicesFactoryHalHybrid.h (100%) rename media/libaudiohal/4.0/{ => include/libaudiohal/4.0}/EffectsFactoryHalHidl.h (100%) diff --git a/media/libaudiohal/4.0/Android.bp b/media/libaudiohal/4.0/Android.bp new file mode 100644 index 0000000000..3d104abecc --- /dev/null +++ b/media/libaudiohal/4.0/Android.bp @@ -0,0 +1,57 @@ +cc_library_shared { + name: "libaudiohal@4.0", + + srcs: [ + "DeviceHalLocal.cpp", + "DevicesFactoryHalHybrid.cpp", + "DevicesFactoryHalLocal.cpp", + "StreamHalLocal.cpp", + + "ConversionHelperHidl.cpp", + "DeviceHalHidl.cpp", + "DevicesFactoryHalHidl.cpp", + "EffectBufferHalHidl.cpp", + "EffectHalHidl.cpp", + "EffectsFactoryHalHidl.cpp", + "StreamHalHidl.cpp", + ], + + export_include_dirs: ["include"], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + shared_libs: [ + "libaudiohal_deathhandler", + "libaudioutils", + "libcutils", + "liblog", + "libutils", + "libhardware", + "libbase", + "libfmq", + "libhwbinder", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "android.hardware.audio@4.0", + "android.hardware.audio.common-util", + "android.hardware.audio.common@4.0", + "android.hardware.audio.common@4.0-util", + "android.hardware.audio.effect@4.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libmedia_helper", + "libmediautils", + ], + header_libs: [ + "android.hardware.audio.common.util@all-versions", + "libaudiohal_headers" + ], + + export_shared_lib_headers: [ + "libfmq", + ], +} diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/4.0/DeviceHalHidl.cpp index 24f2eeb6ed..8da1051aea 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.cpp +++ b/media/libaudiohal/4.0/DeviceHalHidl.cpp @@ -24,9 +24,12 @@ #include #include +#include + #include "DeviceHalHidl.h" #include "HidlUtils.h" #include "StreamHalHidl.h" +#include "VersionUtils.h" using ::android::hardware::audio::common::V4_0::AudioConfig; using ::android::hardware::audio::common::V4_0::AudioDevice; @@ -38,10 +41,12 @@ using ::android::hardware::audio::common::V4_0::AudioPortConfig; using ::android::hardware::audio::common::V4_0::AudioMode; using ::android::hardware::audio::common::V4_0::AudioSource; using ::android::hardware::audio::common::V4_0::HidlUtils; +using ::android::hardware::audio::common::utils::mkEnumConverter; using ::android::hardware::audio::V4_0::DeviceAddress; using ::android::hardware::audio::V4_0::IPrimaryDevice; using ::android::hardware::audio::V4_0::ParameterValue; using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::audio::V4_0::SinkMetadata; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; @@ -194,7 +199,9 @@ status_t DeviceHalHidl::setParameters(const String8& kvPairs) { hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); if (status != OK) return status; - return processReturn("setParameters", mDevice->setParameters(hidlParams)); + // TODO: change the API so that context and kvPairs are separated + return processReturn("setParameters", + utils::setParameters(mDevice, {} /* context */, hidlParams)); } status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { @@ -204,7 +211,8 @@ status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { status_t status = keysFromHal(keys, &hidlKeys); if (status != OK) return status; Result retval; - Return ret = mDevice->getParameters( + Return ret = utils::getParameters(mDevice, + {} /* context */, hidlKeys, [&](Result r, const hidl_vec& parameters) { retval = r; @@ -250,7 +258,8 @@ status_t DeviceHalHidl::openOutputStream( handle, hidlDevice, hidlConfig, - AudioOutputFlag(flags), + mkEnumConverter(flags), + {} /* metadata */, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { @@ -276,12 +285,15 @@ status_t DeviceHalHidl::openInputStream( AudioConfig hidlConfig; HidlUtils::audioConfigFromHal(*config, &hidlConfig); Result retval = Result::NOT_INITIALIZED; + // TODO: correctly propagate the tracks sources and volume + // for now, only send the main source at 1dbfs + SinkMetadata metadata = {{{AudioSource(source), 1}}}; Return ret = mDevice->openInputStream( handle, hidlDevice, hidlConfig, - AudioInputFlag(flags), - AudioSource(source), + flags, + metadata, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { @@ -351,7 +363,7 @@ status_t DeviceHalHidl::dump(int fd) { if (mDevice == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mDevice->debugDump(hidlHandle); + Return ret = mDevice->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); return processReturn("dump", ret); } diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp index 5feed096cc..c83194e91e 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp @@ -52,36 +52,11 @@ DevicesFactoryHalHidl::DevicesFactoryHalHidl() { DevicesFactoryHalHidl::~DevicesFactoryHalHidl() { } -// static -status_t DevicesFactoryHalHidl::nameFromHal(const char *name, IDevicesFactory::Device *device) { - if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) { - *device = IDevicesFactory::Device::PRIMARY; - return OK; - } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) { - *device = IDevicesFactory::Device::A2DP; - return OK; - } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) { - *device = IDevicesFactory::Device::USB; - return OK; - } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) { - *device = IDevicesFactory::Device::R_SUBMIX; - return OK; - } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_STUB) == 0) { - *device = IDevicesFactory::Device::STUB; - return OK; - } - ALOGE("Invalid device name %s", name); - return BAD_VALUE; -} - status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { if (mDevicesFactory == 0) return NO_INIT; - IDevicesFactory::Device hidlDevice; - status_t status = nameFromHal(name, &hidlDevice); - if (status != OK) return status; Result retval = Result::NOT_INITIALIZED; Return ret = mDevicesFactory->openDevice( - hidlDevice, + name, [&](Result r, const sp& result) { retval = r; if (retval == Result::OK) { diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h index 51a74a8d3c..114889bb7a 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h @@ -42,8 +42,6 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface sp mDevicesFactory; sp mDevicesFactoryMsd; - static status_t nameFromHal(const char *name, IDevicesFactory::Device *device); - // Can not be constructed directly by clients. DevicesFactoryHalHidl(); diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp index 722b95a8fe..c43509ca9f 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "DevicesFactoryHalHybrid" //#define LOG_NDEBUG 0 -#include "DevicesFactoryHalHybrid.h" +#include #include "DevicesFactoryHalLocal.h" #include "DevicesFactoryHalHidl.h" diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp index 32d5cea19c..c99c4c859f 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectHalHidl.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "EffectHalHidl" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -34,6 +35,7 @@ using ::android::hardware::audio::effect::V4_0::Result; using ::android::hardware::audio::common::V4_0::HidlUtils; using ::android::hardware::audio::common::V4_0::AudioChannelMask; using ::android::hardware::audio::common::V4_0::AudioFormat; +using ::android::hardware::audio::common::utils::mkEnumConverter; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; @@ -76,10 +78,10 @@ void EffectHalHidl::effectDescriptorToHal( void EffectHalHidl::effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config) { config->samplingRateHz = halConfig.samplingRate; - config->channels = AudioChannelMask(halConfig.channels); + config->channels = mkEnumConverter(halConfig.channels); config->format = AudioFormat(halConfig.format); config->accessMode = EffectBufferAccess(halConfig.accessMode); - config->mask = EffectConfigParameters(halConfig.mask); + config->mask = mkEnumConverter(halConfig.mask); } // static diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp index 301175e78d..dfed784e82 100644 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp @@ -18,11 +18,11 @@ //#define LOG_NDEBUG 0 #include +#include #include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" -#include "EffectsFactoryHalHidl.h" #include "HidlUtils.h" using ::android::hardware::audio::common::V4_0::HidlUtils; @@ -133,7 +133,7 @@ status_t EffectsFactoryHalHidl::dumpEffects(int fd) { if (mEffectsFactory == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mEffectsFactory->debugDump(hidlHandle); + Return ret = mEffectsFactory->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); return processReturn(__FUNCTION__, ret); } diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/4.0/StreamHalHidl.cpp index 9ba3b694e2..de16e98125 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.cpp +++ b/media/libaudiohal/4.0/StreamHalHidl.cpp @@ -25,6 +25,7 @@ #include "DeviceHalHidl.h" #include "EffectHalHidl.h" #include "StreamHalHidl.h" +#include "VersionUtils.h" using ::android::hardware::audio::common::V4_0::AudioChannelMask; using ::android::hardware::audio::common::V4_0::AudioFormat; @@ -56,7 +57,7 @@ StreamHalHidl::StreamHalHidl(IStream *stream) if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { // Obtain audio properties (see StreamHalHidl::getAudioProperties() below). Return ret = mStream->getAudioProperties( - [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + [&](auto sr, auto m, auto f) { mStreamPowerLog.init(sr, static_cast(m), static_cast(f)); @@ -96,7 +97,7 @@ status_t StreamHalHidl::getAudioProperties( uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { if (!mStream) return NO_INIT; Return ret = mStream->getAudioProperties( - [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + [&](uint32_t sr, auto m, auto f) { *sampleRate = sr; *mask = static_cast(m); *format = static_cast(f); @@ -109,7 +110,8 @@ status_t StreamHalHidl::setParameters(const String8& kvPairs) { hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); if (status != OK) return status; - return processReturn("setParameters", mStream->setParameters(hidlParams)); + return processReturn("setParameters", + utils::setParameters(mStream, hidlParams, {} /* options */)); } status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { @@ -119,7 +121,9 @@ status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { status_t status = keysFromHal(keys, &hidlKeys); if (status != OK) return status; Result retval; - Return ret = mStream->getParameters( + Return ret = utils::getParameters( + mStream, + {} /* context */, hidlKeys, [&](Result r, const hidl_vec& parameters) { retval = r; @@ -151,7 +155,7 @@ status_t StreamHalHidl::dump(int fd) { if (!mStream) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mStream->debugDump(hidlHandle); + Return ret = mStream->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); mStreamPowerLog.dump(fd); return processReturn("dump", ret); diff --git a/media/libaudiohal/4.0/StreamHalLocal.cpp b/media/libaudiohal/4.0/StreamHalLocal.cpp index fc6e047601..592a931bd3 100644 --- a/media/libaudiohal/4.0/StreamHalLocal.cpp +++ b/media/libaudiohal/4.0/StreamHalLocal.cpp @@ -22,6 +22,7 @@ #include "DeviceHalLocal.h" #include "StreamHalLocal.h" +#include "VersionUtils.h" namespace android { namespace V4_0 { diff --git a/media/libaudiohal/4.0/VersionUtils.h b/media/libaudiohal/4.0/VersionUtils.h new file mode 100644 index 0000000000..1246c2e949 --- /dev/null +++ b/media/libaudiohal/4.0/VersionUtils.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_VERSION_UTILS_4_0_H +#define ANDROID_HARDWARE_VERSION_UTILS_4_0_H + +#include +#include + +using ::android::hardware::audio::V4_0::ParameterValue; +using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::Return; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; + +namespace android { +namespace V4_0 { +namespace utils { + +template +Return getParameters(T& object, hidl_vec context, + hidl_vec keys, Callback callback) { + return object->getParameters(context, keys, callback); +} + +template +Return setParameters(T& object, hidl_vec context, + hidl_vec keys) { + return object->setParameters(context, keys); +} + +} // namespace utils +} // namespace V4_0 +} // namespace android + +#endif // ANDROID_HARDWARE_VERSION_UTILS_4_0_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h similarity index 100% rename from media/libaudiohal/4.0/DevicesFactoryHalHybrid.h rename to media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h similarity index 100% rename from media/libaudiohal/4.0/EffectsFactoryHalHidl.h rename to media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp index 7ecb9d8f9f..3a5df27971 100644 --- a/media/libaudiohal/Android.bp +++ b/media/libaudiohal/Android.bp @@ -12,9 +12,12 @@ cc_library_shared { ], shared_libs: [ - "android.hardware.audio@2.0", "android.hardware.audio.effect@2.0", + "android.hardware.audio.effect@4.0", + "android.hardware.audio@2.0", + "android.hardware.audio@4.0", "libaudiohal@2.0", + "libaudiohal@4.0", "libutils", ], diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp index cfec3d6259..4c8eaf6653 100644 --- a/media/libaudiohal/DevicesFactoryHalInterface.cpp +++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp @@ -14,16 +14,20 @@ * limitations under the License. */ -#include "DevicesFactoryHalHybrid.h" #include +#include -using namespace ::android::hardware::audio; +#include +#include namespace android { // static sp DevicesFactoryHalInterface::create() { - if (V2_0::IDevicesFactory::getService() != nullptr) { + if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) { + return new V4_0::DevicesFactoryHalHybrid(); + } + if (hardware::audio::V2_0::IDevicesFactory::getService() != nullptr) { return new DevicesFactoryHalHybrid(); } return nullptr; diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index 01a171e147..ead1fa2a02 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -14,21 +14,21 @@ * limitations under the License. */ -#define LOG_TAG "EffectsFactoryHalHidl" -//#define LOG_NDEBUG 0 +#include +#include -#include "EffectHalHidl.h" -#include "EffectsFactoryHalHidl.h" +#include +#include -using ::android::hardware::Return; - -using namespace ::android::hardware::audio::effect; namespace android { // static sp EffectsFactoryHalInterface::create() { - if (V2_0::IEffectsFactory::getService() != nullptr) { + if (hardware::audio::effect::V4_0::IEffectsFactory::getService() != nullptr) { + return new V4_0::EffectsFactoryHalHidl(); + } + if (hardware::audio::effect::V2_0::IEffectsFactory::getService() != nullptr) { return new EffectsFactoryHalHidl(); } return nullptr; -- GitLab From f3fcd4e458acd908fe00cb175722d5a7260fd792 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 28 Feb 2018 15:54:17 -0800 Subject: [PATCH 0014/1530] heif: omx image encoder changes bug: 74073607 Change-Id: I8809b70e60ae19dab0c3b6f5f7e951a4dc24e8d6 --- media/libstagefright/ACodec.cpp | 118 ++++++++++++++---- media/libstagefright/NuMediaExtractor.cpp | 2 +- .../include/media/stagefright/ACodec.h | 4 +- media/libstagefright/omx/OMXUtils.cpp | 2 + 4 files changed, 100 insertions(+), 26 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 220649939f..86d7c08d83 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -553,6 +553,7 @@ ACodec::ACodec() mNativeWindowUsageBits(0), mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN), mIsVideo(false), + mIsImage(false), mIsEncoder(false), mFatalError(false), mShutdownInProgress(false), @@ -1713,6 +1714,7 @@ status_t ACodec::configureCodec( mIsEncoder = encoder; mIsVideo = !strncasecmp(mime, "video/", 6); + mIsImage = !strncasecmp(mime, "image/", 6); mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer; mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer; @@ -1728,10 +1730,11 @@ status_t ACodec::configureCodec( // FLAC encoder or video encoder in constant quality mode doesn't need a // bitrate, other encoders do. if (encoder) { - if (mIsVideo && !findVideoBitrateControlInfo( - msg, &bitrateMode, &bitrate, &quality)) { - return INVALID_OPERATION; - } else if (!mIsVideo && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) + if (mIsVideo || mIsImage) { + if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) { + return INVALID_OPERATION; + } + } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC) && !msg->findInt32("bitrate", &bitrate)) { return INVALID_OPERATION; } @@ -2010,7 +2013,7 @@ status_t ACodec::configureCodec( (void)msg->findInt32("pcm-encoding", (int32_t*)&pcmEncoding); // invalid encodings will default to PCM-16bit in setupRawAudioFormat. - if (mIsVideo) { + if (mIsVideo || mIsImage) { // determine need for software renderer bool usingSwRenderer = false; if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) { @@ -2290,7 +2293,7 @@ status_t ACodec::configureCodec( } // create data converters if needed - if (!mIsVideo && err == OK) { + if (!mIsVideo && !mIsImage && err == OK) { AudioEncoding codecPcmEncoding = kAudioEncodingPcm16bit; if (encoder) { (void)mInputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding); @@ -3215,6 +3218,7 @@ static const struct VideoCodingMapEntry { { MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 }, { MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 }, { MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, OMX_VIDEO_CodingDolbyVision }, + { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, OMX_VIDEO_CodingImageHEIC }, }; static status_t GetVideoCodingTypeFromMime( @@ -3872,7 +3876,8 @@ status_t ACodec::setupVideoEncoder( break; case OMX_VIDEO_CodingHEVC: - err = setupHEVCEncoderParameters(msg); + case OMX_VIDEO_CodingImageHEIC: + err = setupHEVCEncoderParameters(msg, outputFormat); break; case OMX_VIDEO_CodingVP8: @@ -4378,27 +4383,63 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { return configureBitrate(bitrateMode, bitrate); } -status_t ACodec::setupHEVCEncoderParameters(const sp &msg) { - float iFrameInterval; - if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) { - return INVALID_OPERATION; +status_t ACodec::configureImageGrid( + const sp &msg, sp &outputFormat) { + int32_t gridWidth, gridHeight, gridRows, gridCols; + if (!msg->findInt32("grid-width", &gridWidth) || + !msg->findInt32("grid-height", &gridHeight) || + !msg->findInt32("grid-rows", &gridRows) || + !msg->findInt32("grid-cols", &gridCols)) { + return OK; + } + + OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE gridType; + InitOMXParams(&gridType); + gridType.nPortIndex = kPortIndexOutput; + gridType.bEnabled = OMX_TRUE; + gridType.nGridWidth = gridWidth; + gridType.nGridHeight = gridHeight; + gridType.nGridRows = gridRows; + gridType.nGridCols = gridCols; + + status_t err = mOMXNode->setParameter( + (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid, + &gridType, sizeof(gridType)); + + // for video encoders, grid config is only a hint. + if (!mIsImage) { + return OK; } + // image encoders must support grid config. + if (err != OK) { + return err; + } + + // query to get the image encoder's real grid config as it might be + // different from the requested, and transfer that to the output. + err = mOMXNode->getParameter( + (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid, + &gridType, sizeof(gridType)); + + if (err == OK && gridType.bEnabled) { + outputFormat->setInt32("grid-width", gridType.nGridWidth); + outputFormat->setInt32("grid-height", gridType.nGridHeight); + outputFormat->setInt32("grid-rows", gridType.nGridRows); + outputFormat->setInt32("grid-cols", gridType.nGridCols); + } + + return err; +} + +status_t ACodec::setupHEVCEncoderParameters( + const sp &msg, sp &outputFormat) { OMX_VIDEO_CONTROLRATETYPE bitrateMode; int32_t bitrate, quality; if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) { return INVALID_OPERATION; } - float frameRate; - if (!msg->findFloat("frame-rate", &frameRate)) { - int32_t tmp; - if (!msg->findInt32("frame-rate", &tmp)) { - return INVALID_OPERATION; - } - frameRate = (float)tmp; - } - OMX_VIDEO_PARAM_HEVCTYPE hevcType; InitOMXParams(&hevcType); hevcType.nPortIndex = kPortIndexOutput; @@ -4426,7 +4467,27 @@ status_t ACodec::setupHEVCEncoderParameters(const sp &msg) { hevcType.eLevel = static_cast(level); } // TODO: finer control? - hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1; + if (mIsImage) { + hevcType.nKeyFrameInterval = 1; + } else { + float iFrameInterval; + if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) { + return INVALID_OPERATION; + } + + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + hevcType.nKeyFrameInterval = + setPFramesSpacing(iFrameInterval, frameRate) + 1; + } + err = mOMXNode->setParameter( (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType)); @@ -4434,6 +4495,12 @@ status_t ACodec::setupHEVCEncoderParameters(const sp &msg) { return err; } + err = configureImageGrid(msg, outputFormat); + + if (err != OK) { + return err; + } + return configureBitrate(bitrateMode, bitrate, quality); } @@ -4875,7 +4942,8 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp ¬ify) { (void)getHDRStaticInfoForVideoCodec(kPortIndexInput, notify); } uint32_t latency = 0; - if (mIsEncoder && getLatency(&latency) == OK && latency > 0) { + if (mIsEncoder && !mIsImage && + getLatency(&latency) == OK && latency > 0) { notify->setInt32("latency", latency); } } @@ -4931,7 +4999,8 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp ¬ify) { notify->setString("mime", mime.c_str()); } uint32_t intraRefreshPeriod = 0; - if (mIsEncoder && getIntraRefreshPeriod(&intraRefreshPeriod) == OK + if (mIsEncoder && !mIsImage && + getIntraRefreshPeriod(&intraRefreshPeriod) == OK && intraRefreshPeriod > 0) { notify->setInt32("intra-refresh-period", intraRefreshPeriod); } @@ -8223,8 +8292,9 @@ status_t ACodec::queryCapabilities( } bool isVideo = strncasecmp(mime, "video/", 6) == 0; + bool isImage = strncasecmp(mime, "image/", 6) == 0; - if (isVideo) { + if (isVideo || isImage) { OMX_VIDEO_PARAM_PROFILELEVELTYPE param; InitOMXParams(¶m); param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput; diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index 540cf8cb8f..8916cc6758 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #define LOG_TAG "NuMediaExtractor" #include diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index fa22003bb0..1a5304be80 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -253,6 +253,7 @@ private: sp mLastOutputFormat; bool mIsVideo; + bool mIsImage; bool mIsEncoder; bool mFatalError; bool mShutdownInProgress; @@ -489,11 +490,12 @@ private: status_t setupMPEG4EncoderParameters(const sp &msg); status_t setupH263EncoderParameters(const sp &msg); status_t setupAVCEncoderParameters(const sp &msg); - status_t setupHEVCEncoderParameters(const sp &msg); + status_t setupHEVCEncoderParameters(const sp &msg, sp &outputFormat); status_t setupVPXEncoderParameters(const sp &msg, sp &outputFormat); status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level); + status_t configureImageGrid(const sp &msg, sp &outputFormat); status_t configureBitrate( OMX_VIDEO_CONTROLRATETYPE bitrateMode, int32_t bitrate, int32_t quality = 0); void configureEncoderLatency(const sp &msg); diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp index f597e027a9..f7b569dff6 100644 --- a/media/libstagefright/omx/OMXUtils.cpp +++ b/media/libstagefright/omx/OMXUtils.cpp @@ -163,6 +163,8 @@ const char *GetComponentRole(bool isEncoder, const char *mime) { "audio_decoder.ac3", "audio_encoder.ac3" }, { MEDIA_MIMETYPE_AUDIO_EAC3, "audio_decoder.eac3", "audio_encoder.eac3" }, + { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, + "image_decoder.heic", "image_encoder.heic" }, }; static const size_t kNumMimeToRole = -- GitLab From 450e9b164ff11abfe6a739bdc355463fd3588469 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:08:57 -0800 Subject: [PATCH 0015/1530] codec2: fix C2Buffer - _C2MappingBlock2DImpl::Mapped hold shared_ptr to _C2Block2DImpl instead of raw pointer to prevent use-after-free. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: Icf3bb6aa781da9916c891a1325480051f6ffdffe --- media/libstagefright/codec2/vndk/C2Buffer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp index dc765f503b..aeb47014c7 100644 --- a/media/libstagefright/codec2/vndk/C2Buffer.cpp +++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp @@ -388,7 +388,8 @@ private: std::shared_ptr<_C2BlockPoolData> mPoolData; }; -class C2_HIDE _C2MappingBlock2DImpl : public _C2Block2DImpl { +class C2_HIDE _C2MappingBlock2DImpl + : public _C2Block2DImpl, public std::enable_shared_from_this<_C2MappingBlock2DImpl> { public: using _C2Block2DImpl::_C2Block2DImpl; @@ -399,7 +400,7 @@ public: private: friend class _C2MappingBlock2DImpl; - Mapped(const _C2Block2DImpl *impl, bool writable, C2Fence *fence __unused) + Mapped(const std::shared_ptr<_C2Block2DImpl> &impl, bool writable, C2Fence *fence __unused) : mImpl(impl), mWritable(writable) { memset(mData, 0, sizeof(mData)); const C2Rect crop = mImpl->crop(); @@ -467,7 +468,7 @@ public: bool writable() const { return mWritable; } private: - const _C2Block2DImpl *mImpl; + const std::shared_ptr<_C2Block2DImpl> mImpl; bool mWritable; c2_status_t mError; uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES]; @@ -485,7 +486,7 @@ public: std::lock_guard lock(mMappedLock); std::shared_ptr existing = mMapped.lock(); if (!existing) { - existing = std::shared_ptr(new Mapped(this, writable, fence)); + existing = std::shared_ptr(new Mapped(shared_from_this(), writable, fence)); mMapped = existing; } else { // if we mapped the region read-only, we cannot remap it read-write -- GitLab From e3534403fa6666408fd419fc9809b3efbfa94479 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:18:12 -0800 Subject: [PATCH 0016/1530] codec2: fix SimpleC2Component race condition - Fix race condition around stop/reset/release. - Call onFlush at processing thread. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: I74c2d84c35842907c1f7f6e862d37b748409a73f --- .../codec2/SimpleC2Component.cpp | 90 +++++++++++++++---- .../codec2/include/SimpleC2Component.h | 20 ++++- 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp index 0753b08a27..f3d95f6912 100644 --- a/media/libstagefright/codec2/SimpleC2Component.cpp +++ b/media/libstagefright/codec2/SimpleC2Component.cpp @@ -51,6 +51,8 @@ void SimpleC2Component::WorkQueue::markDrain(uint32_t drainMode) { mQueue.push_back({ nullptr, drainMode }); } +//////////////////////////////////////////////////////////////////////////////// + SimpleC2Component::SimpleC2Component( const std::shared_ptr &intf) : mIntf(intf) { @@ -91,13 +93,13 @@ c2_status_t SimpleC2Component::queue_nb(std::list> * con } c2_status_t SimpleC2Component::announce_nb(const std::vector &items) { - (void) items; + (void)items; return C2_OMITTED; } c2_status_t SimpleC2Component::flush_sm( - flush_mode_t flushThrough, std::list>* const flushedWork) { - (void) flushThrough; + flush_mode_t flushMode, std::list>* const flushedWork) { + (void)flushMode; { Mutexed::Locked state(mExecState); if (state->mState != RUNNING) { @@ -107,6 +109,7 @@ c2_status_t SimpleC2Component::flush_sm( { Mutexed::Locked queue(mWorkQueue); queue->incGeneration(); + // TODO: queue->splicedBy(flushedWork, flushedWork->end()); while (!queue->empty()) { std::unique_ptr work = queue->pop_front(); if (work) { @@ -122,7 +125,7 @@ c2_status_t SimpleC2Component::flush_sm( } } - return onFlush_sm(); + return C2_OK; } c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) { @@ -160,6 +163,10 @@ c2_status_t SimpleC2Component::start() { } if (!state->mThread.joinable()) { mExitRequested = false; + { + Mutexed::Locked monitor(mExitMonitor); + monitor->mExited = false; + } state->mThread = std::thread( [](std::weak_ptr wp) { while (true) { @@ -168,6 +175,8 @@ c2_status_t SimpleC2Component::start() { return; } if (thiz->exitRequested()) { + ALOGV("stop processing"); + thiz->signalExit(); return; } thiz->processQueue(); @@ -179,7 +188,42 @@ c2_status_t SimpleC2Component::start() { return C2_OK; } +void SimpleC2Component::signalExit() { + Mutexed::Locked monitor(mExitMonitor); + monitor->mExited = true; + monitor->mCondition.broadcast(); +} + +void SimpleC2Component::requestExitAndWait(std::function job) { + { + Mutexed::Locked state(mExecState); + if (!state->mThread.joinable()) { + return; + } + } + mExitRequested = true; + { + Mutexed::Locked queue(mWorkQueue); + queue->mCondition.broadcast(); + } + // TODO: timeout? + { + Mutexed::Locked monitor(mExitMonitor); + while (!monitor->mExited) { + monitor.waitForCondition(monitor->mCondition); + } + job(); + } + Mutexed::Locked state(mExecState); + if (state->mThread.joinable()) { + ALOGV("joining the processing thread"); + state->mThread.join(); + ALOGV("joined the processing thread"); + } +} + c2_status_t SimpleC2Component::stop() { + ALOGV("stop"); { Mutexed::Locked state(mExecState); if (state->mState != RUNNING) { @@ -195,7 +239,8 @@ c2_status_t SimpleC2Component::stop() { Mutexed::Locked pending(mPendingWork); pending->clear(); } - c2_status_t err = onStop(); + c2_status_t err; + requestExitAndWait([this, &err]{ err = onStop(); }); if (err != C2_OK) { return err; } @@ -203,6 +248,7 @@ c2_status_t SimpleC2Component::stop() { } c2_status_t SimpleC2Component::reset() { + ALOGV("reset"); { Mutexed::Locked state(mExecState); state->mState = UNINITIALIZED; @@ -215,21 +261,13 @@ c2_status_t SimpleC2Component::reset() { Mutexed::Locked pending(mPendingWork); pending->clear(); } - onReset(); + requestExitAndWait([this]{ onReset(); }); return C2_OK; } c2_status_t SimpleC2Component::release() { - std::thread releasing; - { - Mutexed::Locked state(mExecState); - releasing = std::move(state->mThread); - } - mExitRequested = true; - if (releasing.joinable()) { - releasing.join(); - } - onRelease(); + ALOGV("release"); + requestExitAndWait([this]{ onRelease(); }); return C2_OK; } @@ -271,10 +309,14 @@ void SimpleC2Component::processQueue() { std::unique_ptr work; uint64_t generation; int32_t drainMode; + bool isFlushPending = false; { Mutexed::Locked queue(mWorkQueue); nsecs_t deadline = systemTime() + ms2ns(250); while (queue->empty()) { + if (exitRequested()) { + return; + } nsecs_t now = systemTime(); if (now >= deadline) { return; @@ -287,8 +329,17 @@ void SimpleC2Component::processQueue() { generation = queue->generation(); drainMode = queue->drainMode(); + isFlushPending = queue->popPendingFlush(); work = queue->pop_front(); } + if (isFlushPending) { + ALOGV("processing pending flush"); + c2_status_t err = onFlush_sm(); + if (err != C2_OK) { + ALOGD("flush err: %d", err); + // TODO: error + } + } if (!mOutputBlockPool) { c2_status_t err = [this] { @@ -333,10 +384,12 @@ void SimpleC2Component::processQueue() { } process(work, mOutputBlockPool); + ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku()); { Mutexed::Locked queue(mWorkQueue); if (queue->generation() != generation) { - ALOGW("work form old generation: was %" PRIu64 " now %" PRIu64, queue->generation(), generation); + ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64, + queue->generation(), generation); work->result = C2_NOT_FOUND; queue.unlock(); { @@ -361,9 +414,10 @@ void SimpleC2Component::processQueue() { unexpected = std::move(pending->at(frameIndex)); pending->erase(frameIndex); } - (void) pending->insert({ frameIndex, std::move(work) }); + (void)pending->insert({ frameIndex, std::move(work) }); } if (unexpected) { + ALOGD("unexpected pending work"); unexpected->result = C2_CORRUPTED; Mutexed::Locked state(mExecState); state->mListener->onWorkDone_nb(shared_from_this(), vec(unexpected)); diff --git a/media/libstagefright/codec2/include/SimpleC2Component.h b/media/libstagefright/codec2/include/SimpleC2Component.h index a4b6ee110b..168e79fd66 100644 --- a/media/libstagefright/codec2/include/SimpleC2Component.h +++ b/media/libstagefright/codec2/include/SimpleC2Component.h @@ -52,6 +52,7 @@ public: // for thread inline bool exitRequested() { return mExitRequested; } void processQueue(); + void signalExit(); protected: /** @@ -157,16 +158,21 @@ private: class WorkQueue { public: - inline WorkQueue() : mGeneration(0ul) {} + inline WorkQueue() : mFlush(false), mGeneration(0ul) {} inline uint64_t generation() const { return mGeneration; } - inline void incGeneration() { ++mGeneration; } + inline void incGeneration() { ++mGeneration; mFlush = true; } std::unique_ptr pop_front(); void push_back(std::unique_ptr work); bool empty() const; uint32_t drainMode() const; void markDrain(uint32_t drainMode); + inline bool popPendingFlush() { + bool flush = mFlush; + mFlush = false; + return flush; + } void clear(); Condition mCondition; @@ -177,6 +183,7 @@ private: uint32_t drainMode; }; + bool mFlush; uint64_t mGeneration; std::list mQueue; }; @@ -185,9 +192,18 @@ private: typedef std::unordered_map> PendingWork; Mutexed mPendingWork; + struct ExitMonitor { + inline ExitMonitor() : mExited(false) {} + Condition mCondition; + bool mExited; + }; + Mutexed mExitMonitor; + std::shared_ptr mOutputBlockPool; SimpleC2Component() = delete; + + void requestExitAndWait(std::function job); }; } // namespace android -- GitLab From b210d63597f3006d1966212d353184ceb2741edf Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:22:16 -0800 Subject: [PATCH 0017/1530] C2SoftAac: fix error handling - Handle zero-length data. - Do not return prematurely for corrupted data. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: I641c3200b9817d44e3e938c03a5d796857ed1be7 --- .../codecs/aacdec/C2SoftAac.cpp | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp index c82ea45cef..6bd15a57ab 100644 --- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp +++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp @@ -305,37 +305,57 @@ void C2SoftAac::drainRingBuffer( ALOGV("getting %d from ringbuffer", numSamples); std::shared_ptr block; - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - // TODO: error handling, proper usage, etc. - c2_status_t err = pool->fetchLinearBlock(numSamples * sizeof(int16_t), usage, &block); - if (err != C2_OK) { - ALOGE("err = %d", err); - } + std::function&)> fillWork = + [&block, numSamples, pool, this]() + -> std::function&)> { + auto fillEmptyWork = [](const std::unique_ptr &work, c2_status_t err) { + work->result = err; + work->worklets.front()->output.flags = work->input.flags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; + }; + + using namespace std::placeholders; + if (numSamples == 0) { + return std::bind(fillEmptyWork, _1, C2_OK); + } + + // TODO: error handling, proper usage, etc. + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + c2_status_t err = pool->fetchLinearBlock( + numSamples * sizeof(int16_t), usage, &block); + if (err != C2_OK) { + ALOGD("failed to fetch a linear block (%d)", err); + mSignalledError = true; + return std::bind(fillEmptyWork, _1, C2_NO_MEMORY); + } + C2WriteView wView = block->map().get(); + // TODO + INT_PCM *outBuffer = reinterpret_cast(wView.data()); + int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples); + if (ns != numSamples) { + ALOGE("not a complete frame of samples available"); + mSignalledError = true; + return std::bind(fillEmptyWork, _1, C2_CORRUPTED); + } + return [buffer = createLinearBuffer(block)](const std::unique_ptr &work) { + work->result = C2_OK; + work->worklets.front()->output.flags = work->input.flags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.buffers.push_back(buffer); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; + }; + }(); - C2WriteView wView = block->map().get(); - // TODO - INT_PCM *outBuffer = reinterpret_cast(wView.data()); - int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples); - if (ns != numSamples) { - ALOGE("not a complete frame of samples available"); - mSignalledError = true; - // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - return; - } - auto fillWork = [buffer = createLinearBuffer(block)](const std::unique_ptr &work) { - work->worklets.front()->output.flags = work->input.flags; - work->worklets.front()->output.buffers.clear(); - work->worklets.front()->output.buffers.push_back(buffer); - work->worklets.front()->output.ordinal = work->input.ordinal; - work->workletsProcessed = 1u; - }; if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) { fillWork(work); } else { finish(outInfo.frameIndex, fillWork); } - ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block->capacity()); + ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block ? block->capacity() : 0); mBuffersInfo.pop_front(); } } -- GitLab From 21914e524bcde2054cab13e7acacb25a09b825aa Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:25:50 -0800 Subject: [PATCH 0018/1530] C2SoftAvc: fix flush and map error - Fix infinite loop at flush. - Allocate flush buffer at setFlushMode(). - Handle input buffer mapping error. - Remove unused code. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: I6d0187fcd1d9ff2cf90bbf79d21ead1fba5df5a6 --- .../codecs/avcdec/C2SoftAvcDec.cpp | 560 +----------------- 1 file changed, 23 insertions(+), 537 deletions(-) diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp index b9ba25139b..ad36f0d260 100644 --- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp +++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp @@ -83,139 +83,6 @@ std::shared_ptr BuildIntf( .build(); } -#if 0 -using SupportedValuesWithFields = C2SoftAvcDecIntf::SupportedValuesWithFields; - -struct ValidateParam { - explicit ValidateParam( - const std::map &supportedValues) - : mSupportedValues(supportedValues) {} - - template ::value, size_t SIZE = sizeof(T)> - struct Getter { - static T get(const C2Value::Primitive &) { - static_assert(!std::is_arithmetic::value, "non-arithmetic type"); - static_assert(!std::is_floating_point::value || std::is_same::value, - "float is the only supported floating point type"); - static_assert(sizeof(T) <= 8, "type exceeds 64-bit"); - } - }; - - template - bool validateField( - const C2FieldSupportedValues &supportedValues, const T &value) { - switch (supportedValues.type) { - case C2FieldSupportedValues::EMPTY: - { - return false; - } - case C2FieldSupportedValues::RANGE: - { - // TODO: handle step, num, denom - return Getter::get(supportedValues.range.min) <= value - && value <= Getter::get(supportedValues.range.max); - } - case C2FieldSupportedValues::VALUES: - { - for (const auto &val : supportedValues.values) { - if (Getter::get(val) == value) { - return true; - } - } - return false; - } - case C2FieldSupportedValues::FLAGS: - // TODO - return false; - } - return false; - } - -protected: - const std::map &mSupportedValues; -}; - -template <> -struct ValidateParam::Getter { - static float get(const C2Value::Primitive &value) { return value.fp; } -}; -template -struct ValidateParam::Getter { - static int64_t get(const C2Value::Primitive &value) { return value.i64; } -}; -template -struct ValidateParam::Getter { - static int32_t get(const C2Value::Primitive &value) { return value.i32; } -}; -template -struct ValidateParam::Getter { - static uint64_t get(const C2Value::Primitive &value) { return value.u64; } -}; -template -struct ValidateParam::Getter { - static uint32_t get(const C2Value::Primitive &value) { return value.u32; } -}; - -template -struct ValidateSimpleParam : public ValidateParam { - explicit ValidateSimpleParam( - const std::map &supportedValues) - : ValidateParam(supportedValues) {} - - std::unique_ptr operator() (C2Param *c2param) { - T* param = (T*)c2param; - C2ParamField field(param, &T::value); - const C2FieldSupportedValues &supportedValues = mSupportedValues.at(field).supported; - if (!validateField(supportedValues, param->value)) { - return std::unique_ptr( - new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}}); - } - return nullptr; - } -}; - -template -struct ValidateVideoSize : public ValidateParam { - explicit ValidateVideoSize( - const std::map &supportedValues) - : ValidateParam(supportedValues) {} - - std::unique_ptr operator() (C2Param *c2param) { - T* param = (T*)c2param; - C2ParamField field(param, &T::width); - const C2FieldSupportedValues &supportedWidth = mSupportedValues.at(field).supported; - if (!validateField(supportedWidth, param->width)) { - return std::unique_ptr( - new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}}); - } - field = C2ParamField(param, &T::height); - const C2FieldSupportedValues &supportedHeight = mSupportedValues.at(field).supported; - if (!validateField(supportedHeight, param->height)) { - return std::unique_ptr( - new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}}); - } - return nullptr; - } -}; - -template -struct ValidateCString { - explicit ValidateCString(const char *expected) : mExpected(expected) {} - - std::unique_ptr operator() (C2Param *c2param) { - T* param = (T*)c2param; - if (strncmp(param->m.value, mExpected, param->flexCount()) != 0) { - return std::unique_ptr( - new C2SettingResult {C2SettingResult::BAD_VALUE, {C2ParamField(param, &T::m), nullptr}, {}}); - } - return nullptr; - } - -private: - const char *mExpected; -}; -#endif - void fillEmptyWork(const std::unique_ptr &work) { uint32_t flags = 0; if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) { @@ -229,398 +96,6 @@ void fillEmptyWork(const std::unique_ptr &work) { } // namespace -#if 0 -#define CASE(member) \ - case decltype(component->member)::CORE_INDEX: \ - return std::unique_ptr(new C2StructDescriptor( \ - static_castmember) *>(nullptr))) - -class C2SoftAvcDecIntf::ParamReflector : public C2ParamReflector { -public: - virtual std::unique_ptr describe(C2Param::CoreIndex coreIndex) override { - constexpr C2SoftAvcDecIntf *component = nullptr; - switch (coreIndex.coreIndex()) { - CASE(mDomainInfo); - CASE(mInputStreamCount); - CASE(mInputStreamFormat); - // Output counterparts for the above would be redundant. - CASE(mVideoSize); - CASE(mMaxVideoSizeHint); - - // port mime configs are stored as unique_ptr. - case C2PortMimeConfig::CORE_INDEX: - return std::unique_ptr(new C2StructDescriptor( - static_cast(nullptr))); - } - return nullptr; - } -}; -#undef CASE - -// static const CodecProfileLevel kProfileLevels[] = { -// { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 }, -// { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 }, -// { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 }, -// }; -C2SoftAvcDecIntf::C2SoftAvcDecIntf(const char *name, c2_node_id_t id) - : mName(name), - mId(id), - mDomainInfo(C2DomainVideo), - mInputStreamCount(1u), - mOutputStreamCount(1u), - mInputStreamFormat(0u, C2FormatCompressed), - mOutputStreamFormat(0u, C2FormatVideo), - mProfile(0u, kAvcProfileUnknown), - mLevel(0u, kAvcLevelUnknown), - mBlockSize(0u), - mAlignment(0u), - mFrameRate(0u, 0), - mBlocksPerSecond(0u, 0), - mParamReflector(new ParamReflector) { - ALOGV("in %s", __func__); - mInputPortMime = C2PortMimeConfig::input::AllocUnique(strlen(CODEC_MIME_TYPE) + 1); - strcpy(mInputPortMime->m.value, CODEC_MIME_TYPE); - mOutputPortMime = C2PortMimeConfig::output::AllocUnique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1); - strcpy(mOutputPortMime->m.value, MEDIA_MIMETYPE_VIDEO_RAW); - - mVideoSize.width = 320; - mVideoSize.height = 240; - mBlockSize.width = 16; - mBlockSize.height = 16; - mAlignment.width = 2; - mAlignment.height = 2; - - mMaxVideoSizeHint.width = H264_MAX_FRAME_WIDTH; - mMaxVideoSizeHint.height = H264_MAX_FRAME_HEIGHT; - - mOutputBlockPools = C2PortBlockPoolsTuning::output::AllocUnique({}); - - auto insertParam = [¶ms = mParams] (C2Param *param) { - params[param->index()] = param; - }; - - auto markReadOnly = [&supported = mSupportedValues] (auto *param) { - supported.emplace( - C2ParamField(param, &std::remove_pointer::type::value), - C2FieldSupportedValues(false /* flags */, {})); - }; - - auto markReadOnlyVideoSize = [&supported = mSupportedValues] (auto *param) { - supported.emplace( - C2ParamField(param, &std::remove_pointer::type::width), - C2FieldSupportedValues(false /* flags */, {})); - supported.emplace( - C2ParamField(param, &std::remove_pointer::type::height), - C2FieldSupportedValues(false /* flags */, {})); - }; - - insertParam(&mDomainInfo); - markReadOnly(&mDomainInfo); - mFieldVerifiers[mDomainInfo.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(mInputPortMime.get()); - mFieldVerifiers[mInputPortMime->index()] = - ValidateCString::type>(CODEC_MIME_TYPE); - - insertParam(&mInputStreamCount); - markReadOnly(&mInputStreamCount); - mFieldVerifiers[mInputStreamCount.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(mOutputPortMime.get()); - mFieldVerifiers[mOutputPortMime->index()] = - ValidateCString::type>(MEDIA_MIMETYPE_VIDEO_RAW); - - insertParam(&mOutputStreamCount); - markReadOnly(&mOutputStreamCount); - mFieldVerifiers[mOutputStreamCount.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mInputStreamFormat); - markReadOnly(&mInputStreamFormat); - mFieldVerifiers[mInputStreamFormat.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mOutputStreamFormat); - markReadOnly(&mOutputStreamFormat); - mFieldVerifiers[mOutputStreamFormat.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mVideoSize); - markReadOnlyVideoSize(&mVideoSize); - mFieldVerifiers[mVideoSize.index()] = - ValidateVideoSize(mSupportedValues); - - insertParam(&mMaxVideoSizeHint); - mSupportedValues.emplace( - C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width), - C2FieldSupportedValues(H264_MIN_FRAME_WIDTH, H264_MAX_FRAME_WIDTH, mAlignment.width)); - mSupportedValues.emplace( - C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height), - C2FieldSupportedValues(H264_MIN_FRAME_HEIGHT, H264_MAX_FRAME_HEIGHT, mAlignment.height)); - mFieldVerifiers[mMaxVideoSizeHint.index()] = - ValidateVideoSize(mSupportedValues); - - insertParam(&mProfile); - mSupportedValues.emplace( - C2ParamField(&mProfile, &C2AvcProfileInfo::value), - C2FieldSupportedValues(false /* flags */, { - kAvcProfileUnknown, - kAvcProfileBaseline, - kAvcProfileMain, - kAvcProfileHigh, - })); - mFieldVerifiers[mProfile.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mLevel); - mSupportedValues.emplace( - C2ParamField(&mLevel, &C2AvcLevelInfo::value), - C2FieldSupportedValues(false /* flags */, { - kAvcLevelUnknown, - kAvcLevel10, - kAvcLevel1b, - kAvcLevel11, - kAvcLevel12, - kAvcLevel13, - kAvcLevel20, - kAvcLevel21, - kAvcLevel22, - kAvcLevel30, - kAvcLevel31, - kAvcLevel32, - kAvcLevel40, - kAvcLevel41, - kAvcLevel42, - kAvcLevel50, - kAvcLevel51, - kAvcLevel52, - })); - mFieldVerifiers[mLevel.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mBlockSize); - markReadOnlyVideoSize(&mBlockSize); - mFieldVerifiers[mBlockSize.index()] = - ValidateVideoSize(mSupportedValues); - - insertParam(&mAlignment); - markReadOnlyVideoSize(&mAlignment); - mFieldVerifiers[mAlignment.index()] = - ValidateVideoSize(mSupportedValues); - - insertParam(&mFrameRate); - mSupportedValues.emplace( - C2ParamField(&mFrameRate, &C2FrameRateInfo::value), - C2FieldSupportedValues(0, 240)); - mFieldVerifiers[mFrameRate.index()] = - ValidateSimpleParam(mSupportedValues); - - insertParam(&mBlocksPerSecond); - mSupportedValues.emplace( - C2ParamField(&mFrameRate, &C2BlocksPerSecondInfo::value), - C2FieldSupportedValues(0, 244800)); - mFieldVerifiers[mBlocksPerSecond.index()] = - ValidateSimpleParam(mSupportedValues); - - mParamDescs.push_back(std::make_shared( - true, "_domain", &mDomainInfo)); - mParamDescs.push_back(std::make_shared( - true, "_input_port_mime", mInputPortMime.get())); - mParamDescs.push_back(std::make_shared( - true, "_input_stream_count", &mInputStreamCount)); - mParamDescs.push_back(std::make_shared( - true, "_output_port_mime", mOutputPortMime.get())); - mParamDescs.push_back(std::make_shared( - true, "_output_stream_count", &mOutputStreamCount)); - mParamDescs.push_back(std::make_shared( - true, "_input_stream_format", &mInputStreamFormat)); - mParamDescs.push_back(std::make_shared( - true, "_output_stream_format", &mOutputStreamFormat)); - mParamDescs.push_back(std::make_shared( - false, "_video_size", &mVideoSize)); - mParamDescs.push_back(std::make_shared( - false, "_max_video_size_hint", &mMaxVideoSizeHint)); - mParamDescs.push_back(std::make_shared( - false, "_output_block_pools", mOutputBlockPools.get())); -} - -C2SoftAvcDecIntf::~C2SoftAvcDecIntf() { - ALOGV("in %s", __func__); -} - -C2String C2SoftAvcDecIntf::getName() const { - return mName; -} - -c2_node_id_t C2SoftAvcDecIntf::getId() const { - return mId; -} - -c2_status_t C2SoftAvcDecIntf::query_vb( - const std::vector & stackParams, - const std::vector & heapParamIndices, - c2_blocking_t mayBlock, - std::vector>* const heapParams) const { - (void)mayBlock; - for (C2Param* const param : stackParams) { - if (!*param) { - continue; - } - - uint32_t index = param->index(); - if (!mParams.count(index)) { - // TODO: add support for output-block-pools (this will be done when we move all - // config to shared ptr) - continue; - } - - C2Param *myParam = mParams.find(index)->second; - if (myParam->size() != param->size()) { - param->invalidate(); - continue; - } - - param->updateFrom(*myParam); - } - - for (const C2Param::Index index : heapParamIndices) { - if (mParams.count(index)) { - C2Param *myParam = mParams.find(index)->second; - heapParams->emplace_back(C2Param::Copy(*myParam)); - } - } - - return C2_OK; -} - -c2_status_t C2SoftAvcDecIntf::config_vb( - const std::vector ¶ms, - c2_blocking_t mayBlock, - std::vector>* const failures) { - (void)mayBlock; - c2_status_t err = C2_OK; - for (C2Param *param : params) { - uint32_t index = param->index(); - if (param->index() == mOutputBlockPools.get()->index()) { - // setting output block pools - mOutputBlockPools.reset( - (C2PortBlockPoolsTuning::output *)C2Param::Copy(*param).release()); - continue; - } - - if (mParams.count(index) == 0) { - // We can't create C2SettingResult with no field, so just skipping in this case. - err = C2_BAD_INDEX; - continue; - } - C2Param *myParam = mParams.find(index)->second; - std::unique_ptr result; - if (!(result = mFieldVerifiers[index](param))) { - myParam->updateFrom(*param); - updateSupportedValues(); - } else { - failures->push_back(std::move(result)); - err = C2_BAD_VALUE; - } - } - return err; -} - -c2_status_t C2SoftAvcDecIntf::createTunnel_sm(c2_node_id_t targetComponent) { - // Tunneling is not supported - (void) targetComponent; - return C2_OMITTED; -} - -c2_status_t C2SoftAvcDecIntf::releaseTunnel_sm(c2_node_id_t targetComponent) { - // Tunneling is not supported - (void) targetComponent; - return C2_OMITTED; -} - -std::shared_ptr C2SoftAvcDecIntf::getParamReflector() const { - return mParamReflector; -} - -c2_status_t C2SoftAvcDecIntf::querySupportedParams_nb( - std::vector> * const params) const { - params->insert(params->begin(), mParamDescs.begin(), mParamDescs.end()); - return C2_OK; -} - -c2_status_t C2SoftAvcDecIntf::querySupportedValues_vb( - std::vector &fields, c2_blocking_t mayBlock) const { - (void)mayBlock; - c2_status_t res = C2_OK; - for (C2FieldSupportedValuesQuery &query : fields) { - if (mSupportedValues.count(query.field) == 0) { - query.status = C2_BAD_INDEX; - res = C2_BAD_INDEX; - } else { - query.status = C2_OK; - query.values = mSupportedValues.at(query.field).supported; - } - } - return res; -} - -void C2SoftAvcDecIntf::updateSupportedValues() { - int32_t maxWidth = H264_MAX_FRAME_WIDTH; - int32_t maxHeight = H264_MAX_FRAME_HEIGHT; - // cf: Rec. ITU-T H.264 A.3 - int maxFrameRate = 172; - std::vector fields; - if (mLevel.value != kAvcLevelUnknown) { - // cf: Rec. ITU-T H.264 Table A-1 - constexpr int MaxFS[] = { - // 0 1 2 3 4 5 6 7 8 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, - 99, 396, 396, 396, 0, 0, 0, 0, 0, 0, - 396, 792, 1620, 0, 0, 0, 0, 0, 0, 0, - 1620, 3600, 5120, 0, 0, 0, 0, 0, 0, 0, - 8192, 8192, 8704, 0, 0, 0, 0, 0, 0, 0, - 22080, 36864, 36864, - }; - constexpr int MaxMBPS[] = { - // 0 1 2 3 4 5 6 7 8 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1485, - 1485, 3000, 6000, 11880, 0, 0, 0, 0, 0, 0, - 11880, 19800, 20250, 0, 0, 0, 0, 0, 0, 0, - 40500, 108000, 216000, 0, 0, 0, 0, 0, 0, 0, - 245760, 245760, 522240, 0, 0, 0, 0, 0, 0, 0, - 589824, 983040, 2073600, - }; - - // cf: Rec. ITU-T H.264 A.3.1 - maxWidth = std::min(maxWidth, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE); - maxHeight = std::min(maxHeight, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE); - int32_t MBs = ((mVideoSize.width + 15) / 16) * ((mVideoSize.height + 15) / 16); - maxFrameRate = std::min(maxFrameRate, MaxMBPS[mLevel.value] / MBs); - fields.push_back(C2ParamField(&mLevel, &C2AvcLevelInfo::value)); - } - - SupportedValuesWithFields &maxWidthVals = mSupportedValues.at( - C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width)); - maxWidthVals.supported.range.max = maxWidth; - maxWidthVals.restrictingFields.clear(); - maxWidthVals.restrictingFields.insert(fields.begin(), fields.end()); - - SupportedValuesWithFields &maxHeightVals = mSupportedValues.at( - C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height)); - maxHeightVals.supported.range.max = maxHeight; - maxHeightVals.restrictingFields.clear(); - maxHeightVals.restrictingFields.insert(fields.begin(), fields.end()); - - SupportedValuesWithFields &frameRate = mSupportedValues.at( - C2ParamField(&mFrameRate, &C2FrameRateInfo::value)); - frameRate.supported.range.max = maxFrameRate; - frameRate.restrictingFields.clear(); - frameRate.restrictingFields.insert(fields.begin(), fields.end()); -} -#endif - /////////////////////////////////////////////////////////////////////////////// C2SoftAvcDec::C2SoftAvcDec( @@ -654,6 +129,7 @@ c2_status_t C2SoftAvcDec::onInit() { } c2_status_t C2SoftAvcDec::onStop() { + ALOGV("onStop"); mSignalledError = false; resetDecoder(); resetPlugin(); @@ -662,6 +138,7 @@ c2_status_t C2SoftAvcDec::onStop() { } void C2SoftAvcDec::onReset() { + ALOGV("onReset"); (void)onStop(); } @@ -672,17 +149,6 @@ void C2SoftAvcDec::onRelease() { c2_status_t C2SoftAvcDec::onFlush_sm() { setFlushMode(); - /* Allocate a picture buffer to flushed data */ - uint32_t displayStride = mWidth; - uint32_t displayHeight = mHeight; - - uint32_t bufferSize = displayStride * displayHeight * 3 / 2; - mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize); - if (NULL == mFlushOutBuffer) { - ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); - return C2_NO_MEMORY; - } - while (true) { ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; @@ -854,6 +320,18 @@ status_t C2SoftAvcDec::setFlushMode() { s_video_flush_op.u4_error_code); return UNKNOWN_ERROR; } + + /* Allocate a picture buffer to flushed data */ + uint32_t displayStride = mWidth; + uint32_t displayHeight = mHeight; + + uint32_t bufferSize = displayStride * displayHeight * 3 / 2; + mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); + return C2_NO_MEMORY; + } + return OK; } @@ -993,6 +471,7 @@ bool C2SoftAvcDec::setDecodeArgs( ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t); ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t); + ps_dec_op->u4_output_present = 0; ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE; @@ -1095,7 +574,7 @@ void C2SoftAvcDec::process( const C2ConstLinearBlock &buffer = work->input.buffers[0]->data().linearBlocks().front(); - if (buffer.capacity() == 0) { + if (buffer.size() == 0) { ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull()); // TODO: result? fillEmptyWork(work); @@ -1109,6 +588,13 @@ void C2SoftAvcDec::process( } C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get(); + if (input.error() != C2_OK) { + work->result = input.error(); + fillEmptyWork(work); + ALOGD("map error: %d", input.error()); + return; + } + ALOGV("buffer.size() = %u, input.capacity() = %u", buffer.size(), input.capacity()); uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF; size_t inOffset = 0u; -- GitLab From fab635c8f7f86db4aee369a2459579a88bcf291c Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:29:04 -0800 Subject: [PATCH 0019/1530] C2SoftAvcEnc: don't crash at unexpected input Report failure instead of crash at input buffer size different than what's configured. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: Id6c5eef205ae56f7b120cf5030630c7eabd8bc30 --- media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp index 8fb812288e..d75e4c1bec 100644 --- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp +++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp @@ -909,6 +909,9 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( } ALOGV("width = %d, height = %d", input->width(), input->height()); + if (mWidth != input->width() || mHeight != input->height()) { + return C2_BAD_VALUE; + } const C2PlanarLayout &layout = input->layout(); uint8_t *yPlane = const_cast(input->data()[C2PlanarLayout::PLANE_Y]); uint8_t *uPlane = const_cast(input->data()[C2PlanarLayout::PLANE_U]); -- GitLab From 073ed87139867a42f5998473df3eba62f821b878 Mon Sep 17 00:00:00 2001 From: Pin-chih Lin Date: Tue, 6 Mar 2018 21:24:31 +0800 Subject: [PATCH 0020/1530] codec2: add profile/level for c2.* components Set profiles and levels to codec capabilities for c2.* components which is required for some CTS tests (ex. VideoDecoderPerfTest) Bug: none Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell killall mediaserver Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.VideoDecoderPerfTest#testAvcOther0Perf0320x0240 Change-Id: Ied12923e28bdd7f8704da5f9c187ee1ba1e21ab4 --- media/libstagefright/Codec2InfoBuilder.cpp | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/media/libstagefright/Codec2InfoBuilder.cpp b/media/libstagefright/Codec2InfoBuilder.cpp index 7ce2ff165f..78c4e387e6 100644 --- a/media/libstagefright/Codec2InfoBuilder.cpp +++ b/media/libstagefright/Codec2InfoBuilder.cpp @@ -31,6 +31,63 @@ namespace android { using ConstTraitsPtr = std::shared_ptr; +struct ProfileLevel { + uint32_t profile; + uint32_t level; +}; +static const ProfileLevel kAvcProfileLevels[] = { + { 0x01, 0x0001 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 }, + { 0x01, 0x0002 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b }, + { 0x01, 0x0004 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 }, + { 0x01, 0x0008 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 }, + { 0x01, 0x0010 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 }, + { 0x01, 0x0020 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 }, + { 0x01, 0x0040 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 }, + { 0x01, 0x0080 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 }, + { 0x01, 0x0100 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 }, + { 0x01, 0x0200 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 }, + { 0x01, 0x0400 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 }, + { 0x01, 0x0800 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 }, + { 0x01, 0x1000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 }, + { 0x01, 0x2000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 }, + { 0x01, 0x4000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5 }, + { 0x01, 0x8000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 }, + + { 0x02, 0x0001 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1 }, + { 0x02, 0x0002 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b }, + { 0x02, 0x0004 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 }, + { 0x02, 0x0008 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 }, + { 0x02, 0x0010 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 }, + { 0x02, 0x0020 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2 }, + { 0x02, 0x0040 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 }, + { 0x02, 0x0080 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 }, + { 0x02, 0x0100 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3 }, + { 0x02, 0x0200 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 }, + { 0x02, 0x0400 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 }, + { 0x02, 0x0800 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4 }, + { 0x02, 0x1000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 }, + { 0x02, 0x2000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel42 }, + { 0x02, 0x4000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel5 }, + { 0x02, 0x8000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel51 }, + + { 0x04, 0x0001 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1 }, + { 0x04, 0x0002 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1b }, + { 0x04, 0x0004 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel11 }, + { 0x04, 0x0008 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel12 }, + { 0x04, 0x0010 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel13 }, + { 0x04, 0x0020 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel2 }, + { 0x04, 0x0040 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel21 }, + { 0x04, 0x0080 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel22 }, + { 0x04, 0x0100 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel3 }, + { 0x04, 0x0200 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel31 }, + { 0x04, 0x0400 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel32 }, + { 0x04, 0x0800 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel4 }, + { 0x04, 0x1000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel41 }, + { 0x04, 0x2000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel42 }, + { 0x04, 0x4000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel5 }, + { 0x04, 0x8000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel51 }, +}; + status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // Obtain C2ComponentStore std::shared_ptr store = GetCodec2PlatformComponentStore(); @@ -86,6 +143,12 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { caps->addDetail(key.c_str(), value.c_str()); } } + // TODO: get this from intf(), and apply to other codecs as well. + if (mediaType.find("video/avc") != std::string::npos && !encoder) { + for (const auto& pl : kAvcProfileLevels) { + caps->addProfileLevel(pl.profile, pl.level); + } + } // TODO: get this from intf(). if (mediaType.find("video") != std::string::npos && !encoder) { caps->addColorFormat(0x7F420888); // COLOR_FormatYUV420Flexible -- GitLab From d5d539f24927445e50e48b1f1af5ef6e570834d3 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 7 Mar 2018 22:12:08 +0000 Subject: [PATCH 0021/1530] Revert "Create Codec2.0 service" This reverts commit 04c0edc8abc88c58ceb17beedaad116a1a7eb900. Reason for revert: Moved to a different directory Change-Id: I1a9dcd9e6f2a9c5779711fecb4a0bc6f402d56f2 --- services/codec2/Android.bp | 71 --------------- services/codec2/Android.mk | 43 --------- .../codec2.system.base-arm.policy | 73 --------------- .../codec2.system.base-x86.policy | 57 ------------ .../codec2.vendor.base-arm.policy | 73 --------------- .../codec2.vendor.base-x86.policy | 57 ------------ services/codec2/system.cpp | 90 ------------------- services/codec2/vendor.cpp | 72 --------------- ...ndor.google.media.c2@1.0-service-system.rc | 7 -- .../vendor.google.media.c2@1.0-service.rc | 7 -- 10 files changed, 550 deletions(-) delete mode 100644 services/codec2/Android.bp delete mode 100644 services/codec2/Android.mk delete mode 100644 services/codec2/seccomp_policy/codec2.system.base-arm.policy delete mode 100644 services/codec2/seccomp_policy/codec2.system.base-x86.policy delete mode 100644 services/codec2/seccomp_policy/codec2.vendor.base-arm.policy delete mode 100644 services/codec2/seccomp_policy/codec2.vendor.base-x86.policy delete mode 100644 services/codec2/system.cpp delete mode 100644 services/codec2/vendor.cpp delete mode 100644 services/codec2/vendor.google.media.c2@1.0-service-system.rc delete mode 100644 services/codec2/vendor.google.media.c2@1.0-service.rc diff --git a/services/codec2/Android.bp b/services/codec2/Android.bp deleted file mode 100644 index 1d5d2054b9..0000000000 --- a/services/codec2/Android.bp +++ /dev/null @@ -1,71 +0,0 @@ -cc_binary { - name: "vendor.google.media.c2@1.0-service", - defaults: ["hidl_defaults"], - soc_specific: true, - relative_install_path: "hw", - srcs: [ - "vendor.cpp", - ], - - init_rc: ["vendor.google.media.c2@1.0-service.rc"], - - shared_libs: [ - "vendor.google.media.c2@1.0", - "libavservices_minijail_vendor", - "libhidlbase", - "libhidltransport", - "libhwbinder", - "liblog", - "libstagefright_codec2_hidl@1.0", - "libstagefright_codec2_vndk", - "libutils", - ], - - arch: { - arm: { - required: ["codec2.vendor.base.policy"], - }, - x86: { - required: ["codec2.vendor.base.policy"], - }, - }, - - compile_multilib: "32", -} - -cc_binary { - name: "vendor.google.media.c2@1.0-service-system", - defaults: ["hidl_defaults"], - relative_install_path: "hw", - srcs: [ - "system.cpp", - ], - - init_rc: ["vendor.google.media.c2@1.0-service-system.rc"], - - shared_libs: [ - "vendor.google.media.c2@1.0", - "libavservices_minijail", - "libcutils", - "libhidlbase", - "libhidltransport", - "libhwbinder", - "liblog", - "libstagefright_codec2_hidl@1.0", - "libstagefright_codec2_vndk", - "libutils", - "libv4l2_c2componentstore", - ], - - arch: { - arm: { - required: ["codec2.system.base.policy"], - }, - x86: { - required: ["codec2.system.base.policy"], - }, - }, - - compile_multilib: "32", -} - diff --git a/services/codec2/Android.mk b/services/codec2/Android.mk deleted file mode 100644 index fa49875c98..0000000000 --- a/services/codec2/Android.mk +++ /dev/null @@ -1,43 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -# vendor service seccomp policy -ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) -include $(CLEAR_VARS) -LOCAL_MODULE := codec2.vendor.base.policy -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy -LOCAL_REQUIRED_MODULES := crash_dump.policy -ifdef TARGET_2ND_ARCH - ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) - LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy - else - LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy - endif -else - LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy -endif -include $(BUILD_PREBUILT) -endif - -# system service seccomp policy -ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) -include $(CLEAR_VARS) -LOCAL_MODULE := codec2.system.base.policy -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy -LOCAL_REQUIRED_MODULES := crash_dump.policy -ifdef TARGET_2ND_ARCH - ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) - LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_2ND_ARCH).policy - else - LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy - endif -else - LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy -endif -include $(BUILD_PREBUILT) -endif - -include $(call all-makefiles-under, $(LOCAL_PATH)) - diff --git a/services/codec2/seccomp_policy/codec2.system.base-arm.policy b/services/codec2/seccomp_policy/codec2.system.base-arm.policy deleted file mode 100644 index d5871d16a9..0000000000 --- a/services/codec2/seccomp_policy/codec2.system.base-arm.policy +++ /dev/null @@ -1,73 +0,0 @@ -# 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. - -# Organized by frequency of systemcall - in descending order for -# best performance. -futex: 1 -ioctl: 1 -write: 1 -prctl: 1 -clock_gettime: 1 -getpriority: 1 -read: 1 -close: 1 -writev: 1 -dup: 1 -ppoll: 1 -mmap2: 1 -getrandom: 1 - -# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail -# parser support for '<' is in this needs to be modified to also prevent -# |old_address| and |new_address| from touching the exception vector page, which -# on ARM is statically loaded at 0xffff 0000. See -# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html -# for more details. -mremap: arg3 == 3 -munmap: 1 -mprotect: 1 -madvise: 1 -openat: 1 -sigaltstack: 1 -clone: 1 -setpriority: 1 -getuid32: 1 -fstat64: 1 -fstatfs64: 1 -pread64: 1 -faccessat: 1 -readlinkat: 1 -exit: 1 -rt_sigprocmask: 1 -set_tid_address: 1 -restart_syscall: 1 -exit_group: 1 -rt_sigreturn: 1 -pipe2: 1 -gettimeofday: 1 -sched_yield: 1 -nanosleep: 1 -lseek: 1 -_llseek: 1 -sched_get_priority_max: 1 -sched_get_priority_min: 1 -statfs64: 1 -sched_setscheduler: 1 -fstatat64: 1 -ugetrlimit: 1 -getdents64: 1 -getrandom: 1 - -@include /system/etc/seccomp_policy/crash_dump.arm.policy - diff --git a/services/codec2/seccomp_policy/codec2.system.base-x86.policy b/services/codec2/seccomp_policy/codec2.system.base-x86.policy deleted file mode 100644 index 20c7625b3b..0000000000 --- a/services/codec2/seccomp_policy/codec2.system.base-x86.policy +++ /dev/null @@ -1,57 +0,0 @@ -# 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. - -read: 1 -mprotect: 1 -prctl: 1 -openat: 1 -getuid32: 1 -writev: 1 -ioctl: 1 -close: 1 -mmap2: 1 -fstat64: 1 -madvise: 1 -fstatat64: 1 -futex: 1 -munmap: 1 -faccessat: 1 -_llseek: 1 -lseek: 1 -clone: 1 -sigaltstack: 1 -setpriority: 1 -restart_syscall: 1 -exit: 1 -exit_group: 1 -rt_sigreturn: 1 -ugetrlimit: 1 -readlinkat: 1 -_llseek: 1 -fstatfs64: 1 -pread64: 1 -mremap: 1 -dup: 1 -set_tid_address: 1 -write: 1 -nanosleep: 1 - -# Required by AddressSanitizer -gettid: 1 -sched_yield: 1 -getpid: 1 -gettid: 1 - -@include /system/etc/seccomp_policy/crash_dump.x86.policy - diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy deleted file mode 100644 index d5871d16a9..0000000000 --- a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy +++ /dev/null @@ -1,73 +0,0 @@ -# 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. - -# Organized by frequency of systemcall - in descending order for -# best performance. -futex: 1 -ioctl: 1 -write: 1 -prctl: 1 -clock_gettime: 1 -getpriority: 1 -read: 1 -close: 1 -writev: 1 -dup: 1 -ppoll: 1 -mmap2: 1 -getrandom: 1 - -# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail -# parser support for '<' is in this needs to be modified to also prevent -# |old_address| and |new_address| from touching the exception vector page, which -# on ARM is statically loaded at 0xffff 0000. See -# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html -# for more details. -mremap: arg3 == 3 -munmap: 1 -mprotect: 1 -madvise: 1 -openat: 1 -sigaltstack: 1 -clone: 1 -setpriority: 1 -getuid32: 1 -fstat64: 1 -fstatfs64: 1 -pread64: 1 -faccessat: 1 -readlinkat: 1 -exit: 1 -rt_sigprocmask: 1 -set_tid_address: 1 -restart_syscall: 1 -exit_group: 1 -rt_sigreturn: 1 -pipe2: 1 -gettimeofday: 1 -sched_yield: 1 -nanosleep: 1 -lseek: 1 -_llseek: 1 -sched_get_priority_max: 1 -sched_get_priority_min: 1 -statfs64: 1 -sched_setscheduler: 1 -fstatat64: 1 -ugetrlimit: 1 -getdents64: 1 -getrandom: 1 - -@include /system/etc/seccomp_policy/crash_dump.arm.policy - diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy deleted file mode 100644 index 20c7625b3b..0000000000 --- a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy +++ /dev/null @@ -1,57 +0,0 @@ -# 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. - -read: 1 -mprotect: 1 -prctl: 1 -openat: 1 -getuid32: 1 -writev: 1 -ioctl: 1 -close: 1 -mmap2: 1 -fstat64: 1 -madvise: 1 -fstatat64: 1 -futex: 1 -munmap: 1 -faccessat: 1 -_llseek: 1 -lseek: 1 -clone: 1 -sigaltstack: 1 -setpriority: 1 -restart_syscall: 1 -exit: 1 -exit_group: 1 -rt_sigreturn: 1 -ugetrlimit: 1 -readlinkat: 1 -_llseek: 1 -fstatfs64: 1 -pread64: 1 -mremap: 1 -dup: 1 -set_tid_address: 1 -write: 1 -nanosleep: 1 - -# Required by AddressSanitizer -gettid: 1 -sched_yield: 1 -getpid: 1 -gettid: 1 - -@include /system/etc/seccomp_policy/crash_dump.x86.policy - diff --git a/services/codec2/system.cpp b/services/codec2/system.cpp deleted file mode 100644 index d6ec6449ed..0000000000 --- a/services/codec2/system.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "vendor.google.media.c2@1.0-service" - -#include -#include -#include - -#include -#include -#include - -// TODO: Remove this once "setenv()" call is removed. -#include - -// This is created by module "codec2.system.base.policy". This can be modified. -static constexpr char kBaseSeccompPolicyPath[] = - "/system/etc/seccomp_policy/codec2.system.base.policy"; - -// Additional device-specific seccomp permissions can be added in this file. -static constexpr char kExtSeccompPolicyPath[] = - "/system/etc/seccomp_policy/codec2.system.ext.policy"; - -int main(int /* argc */, char** /* argv */) { - ALOGD("vendor.google.media.c2@1.0-service-system starting..."); - - // TODO: Remove this when all the build settings and sepolicies are in place. - setenv("TREBLE_TESTING_OVERRIDE", "true", true); - - signal(SIGPIPE, SIG_IGN); - android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath); - - // Extra threads may be needed to handle a stacked IPC sequence that - // contains alternating binder and hwbinder calls. (See b/35283480.) - android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */); - - // Create IComponentStore service. - { - using namespace ::vendor::google::media::c2::V1_0; - android::sp store = - new implementation::ComponentStore( - android::GetCodec2PlatformComponentStore()); - if (store == nullptr) { - ALOGE("Cannot create Codec2's IComponentStore system service."); - } else { - if (store->registerAsService("system") != android::OK) { - ALOGE("Cannot register Codec2's " - "IComponentStore system service."); - } else { - ALOGI("Codec2's IComponentStore system service created."); - } - } - - // To enable the v4l2 service, set this sysprop and add "v4l2" instance - // to the system manifest file. - if (property_get_bool("debug.stagefright.ccodec_v4l2", false)) { - store = new implementation::ComponentStore( - android::GetCodec2VDAComponentStore()); - if (store == nullptr) { - ALOGE("Cannot create Codec2's IComponentStore V4L2 service."); - } else { - if (store->registerAsService("v4l2") != android::OK) { - ALOGE("Cannot register Codec2's " - "IComponentStore V4L2 service."); - } else { - ALOGI("Codec2's IComponentStore V4L2 service created."); - } - } - } - } - - android::hardware::joinRpcThreadpool(); - return 0; -} - diff --git a/services/codec2/vendor.cpp b/services/codec2/vendor.cpp deleted file mode 100644 index 3993ebaae0..0000000000 --- a/services/codec2/vendor.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "vendor.google.media.c2@1.0-service" - -// TODO: Replace this with vendor's implementation -#include - -#include -#include -#include - -// TODO: Remove this once "setenv()" call is removed. -#include - -// This is created by module "codec2.vendor.base.policy". This can be modified. -static constexpr char kBaseSeccompPolicyPath[] = - "/vendor/etc/seccomp_policy/codec2.vendor.base.policy"; - -// Additional device-specific seccomp permissions can be added in this file. -static constexpr char kExtSeccompPolicyPath[] = - "/vendor/etc/seccomp_policy/codec2.vendor.ext.policy"; - -int main(int /* argc */, char** /* argv */) { - ALOGD("vendor.google.media.c2@1.0-service starting..."); - - // TODO: Remove this when all the build settings and sepolicies are in place. - setenv("TREBLE_TESTING_OVERRIDE", "true", true); - - signal(SIGPIPE, SIG_IGN); - android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath); - - // Extra threads may be needed to handle a stacked IPC sequence that - // contains alternating binder and hwbinder calls. (See b/35283480.) - android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */); - - // Create IComponentStore service. - { - using namespace ::vendor::google::media::c2::V1_0; - android::sp store = - new implementation::ComponentStore( - android::GetCodec2PlatformComponentStore()); - if (store == nullptr) { - ALOGE("Cannot create Codec2's IComponentStore service."); - } else { - if (store->registerAsService("default") != android::OK) { - ALOGE("Cannot register Codec2's " - "IComponentStore service."); - } else { - ALOGI("Codec2's IComponentStore service created."); - } - } - } - - android::hardware::joinRpcThreadpool(); - return 0; -} - diff --git a/services/codec2/vendor.google.media.c2@1.0-service-system.rc b/services/codec2/vendor.google.media.c2@1.0-service-system.rc deleted file mode 100644 index 0577a1d472..0000000000 --- a/services/codec2/vendor.google.media.c2@1.0-service-system.rc +++ /dev/null @@ -1,7 +0,0 @@ -service vendor-google-media-c2-system-hal-1-0 /system/bin/hw/vendor.google.media.c2@1.0-service-system - class hal - user media - group mediadrm drmrpc - ioprio rt 4 - writepid /dev/cpuset/foreground/tasks - diff --git a/services/codec2/vendor.google.media.c2@1.0-service.rc b/services/codec2/vendor.google.media.c2@1.0-service.rc deleted file mode 100644 index 3e7e0a696a..0000000000 --- a/services/codec2/vendor.google.media.c2@1.0-service.rc +++ /dev/null @@ -1,7 +0,0 @@ -service vendor-google-media-c2-hal-1-0 /vendor/bin/hw/vendor.google.media.c2@1.0-service - class hal - user media - group mediadrm drmrpc - ioprio rt 4 - writepid /dev/cpuset/foreground/tasks - -- GitLab From 971f74c60ec84c18e8aa1d5f6ba6dd53d93299cf Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 6 Mar 2018 17:08:13 -0800 Subject: [PATCH 0022/1530] codec2: fix {linear|graphic}Blocks() references Fix references to temporary objects. Bug: 74073134 Test: builds Change-Id: Ic742dee5e45e1a0dc0efb22b748e1d42d7978fc6 --- media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp | 2 +- media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp | 2 +- media/libstagefright/codecs/cmds/codec2.cpp | 2 +- media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp | 2 +- media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp | 2 +- media/libstagefright/codecs/g711/dec/C2SoftG711.cpp | 4 ++-- media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp | 2 +- media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp | 2 +- media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp | 2 +- media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp | 2 +- media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp index 4dd0309326..406d1ca799 100644 --- a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp +++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp @@ -115,7 +115,7 @@ void C2SoftAmrNbEnc::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp index ad36f0d260..9b39ae99a4 100644 --- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp +++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp @@ -572,7 +572,7 @@ void C2SoftAvcDec::process( work->result = C2_OK; work->workletsProcessed = 0u; - const C2ConstLinearBlock &buffer = + const C2ConstLinearBlock buffer = work->input.buffers[0]->data().linearBlocks().front(); if (buffer.size() == 0) { ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull()); diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp index 5558bcf42f..295a5b0e07 100644 --- a/media/libstagefright/codecs/cmds/codec2.cpp +++ b/media/libstagefright/codecs/cmds/codec2.cpp @@ -248,7 +248,7 @@ void SimplePlayer::play(const sp &source) { ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll()); const std::shared_ptr &output = work->worklets.front()->output.buffers[0]; if (output) { - const C2ConstGraphicBlock &block = output->data().graphicBlocks().front(); + const C2ConstGraphicBlock block = output->data().graphicBlocks().front(); native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(block.handle()); sp buffer(new GraphicBuffer( grallocHandle, diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp index ce40d6b8f6..0f1fecc2e8 100644 --- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp +++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp @@ -111,7 +111,7 @@ void C2SoftFlacDecoder::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get(); diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp index 6c147add96..fa93fe597e 100644 --- a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp +++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp @@ -140,7 +140,7 @@ void C2SoftFlacEnc::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); diff --git a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp index d296a3d07b..1049420f67 100644 --- a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp +++ b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp @@ -92,8 +92,8 @@ void C2SoftG711::process( return; } - const C2ConstLinearBlock &inBuffer = - work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = + work->input.buffers[0]->data().linearBlocks().front(); C2ReadView rView = inBuffer.map().get(); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp index 2a3239fc44..641c342cca 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp @@ -304,7 +304,7 @@ void C2SoftMpeg4Dec::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF; diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp index 9cac87e514..0a8891ebd2 100644 --- a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp @@ -288,7 +288,7 @@ void C2SoftMP3::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp index 0ebe7d6cfa..74ea340125 100644 --- a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp +++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp @@ -614,7 +614,7 @@ void C2SoftMpeg2Dec::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF; diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp index 96b303c06d..8528f2679a 100644 --- a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp +++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp @@ -209,7 +209,7 @@ void C2SoftVpx::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get(); diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp index 4eec362a33..47fb6dee07 100644 --- a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp @@ -251,7 +251,7 @@ void C2SoftOpus::process( return; } - const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front(); + const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front(); bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); size_t inOffset = inBuffer.offset(); size_t inSize = inBuffer.size(); -- GitLab From e1c3b395209f2c03a290bf0c91c4d96dff5edf00 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 1 Mar 2018 11:37:13 -0800 Subject: [PATCH 0023/1530] CCodec: fix corner cases - Catch invalid createInputSurface requests. - Move buffer allocation from setComponent() to start(). - Reset input surface at stop(). - Ignore flushed work. Bug: 74073134 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest android.media.cts.MediaCodecTest Change-Id: If95d3050e14fa9592d7eff4136a57326008455a0 --- media/libstagefright/CCodec.cpp | 171 ++++++++++++------ media/libstagefright/CCodecBufferChannel.cpp | 114 ++++++------ .../include/CCodecBufferChannel.h | 2 +- .../include/media/stagefright/CCodec.h | 2 + 4 files changed, 175 insertions(+), 114 deletions(-) diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp index f62ee411ab..a144382249 100644 --- a/media/libstagefright/CCodec.cpp +++ b/media/libstagefright/CCodec.cpp @@ -238,14 +238,25 @@ std::shared_ptr CCodec::getBufferChannel() { return mChannel; } +status_t CCodec::tryAndReportOnError(std::function job) { + status_t err = job(); + if (err != C2_OK) { + mCallback->onError(err, ACTION_CODE_FATAL); + } + return err; +} + void CCodec::initiateAllocateComponent(const sp &msg) { - { + auto setAllocating = [this] { Mutexed::Locked state(mState); if (state->get() != RELEASED) { - mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL); - return; + return INVALID_OPERATION; } state->set(ALLOCATING); + return OK; + }; + if (tryAndReportOnError(setAllocating) != OK) { + return; } AString componentName; @@ -259,7 +270,7 @@ void CCodec::initiateAllocateComponent(const sp &msg) { } void CCodec::allocate(const AString &componentName) { - // TODO: use C2ComponentStore to create component + ALOGV("allocate(%s)", componentName.c_str()); mListener.reset(new CCodecListener(this)); std::shared_ptr comp; @@ -282,29 +293,30 @@ void CCodec::allocate(const AString &componentName) { } ALOGV("Success Create component: %s", componentName.c_str()); comp->setListener_vb(mListener, C2_MAY_BLOCK); - { + mChannel->setComponent(comp); + auto setAllocated = [this, comp] { Mutexed::Locked state(mState); if (state->get() != ALLOCATING) { state->set(RELEASED); - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } state->set(ALLOCATED); state->comp = comp; + return OK; + }; + if (tryAndReportOnError(setAllocated) != OK) { + return; } - mChannel->setComponent(comp); mCallback->onComponentAllocated(comp->intf()->getName().c_str()); } void CCodec::initiateConfigureComponent(const sp &format) { - { + auto checkAllocated = [this] { Mutexed::Locked state(mState); - if (state->get() != ALLOCATED) { - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - return; - } + return (state->get() != ALLOCATED) ? UNKNOWN_ERROR : OK; + }; + if (tryAndReportOnError(checkAllocated) != OK) { + return; } sp msg(new AMessage(kWhatConfigure, this)); @@ -314,21 +326,22 @@ void CCodec::initiateConfigureComponent(const sp &format) { void CCodec::configure(const sp &msg) { std::shared_ptr intf; - { + auto checkAllocated = [this, &intf] { Mutexed::Locked state(mState); if (state->get() != ALLOCATED) { state->set(RELEASED); - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } intf = state->comp->intf(); + return OK; + }; + if (tryAndReportOnError(checkAllocated) != OK) { + return; } sp inputFormat(new AMessage); sp outputFormat(new AMessage); - if (status_t err = [=] { + auto doConfig = [=] { AString mime; if (!msg->findString("mime", &mime)) { return BAD_VALUE; @@ -339,6 +352,11 @@ void CCodec::configure(const sp &msg) { encoder = false; } + // TODO: read from intf() + if ((!encoder) != (intf->getName().find("encoder") == std::string::npos)) { + return UNKNOWN_ERROR; + } + sp obj; if (msg->findObject("native-window", &obj)) { sp surface = static_cast(obj.get()); @@ -392,8 +410,8 @@ void CCodec::configure(const sp &msg) { // TODO return OK; - }() != OK) { - mCallback->onError(err, ACTION_CODE_FATAL); + }; + if (tryAndReportOnError(doConfig) != OK) { return; } @@ -406,6 +424,22 @@ void CCodec::configure(const sp &msg) { } void CCodec::initiateCreateInputSurface() { + status_t err = [this] { + Mutexed::Locked state(mState); + if (state->get() != ALLOCATED) { + return UNKNOWN_ERROR; + } + // TODO: read it from intf() properly. + if (state->comp->intf()->getName().find("encoder") == std::string::npos) { + return INVALID_OPERATION; + } + return OK; + }(); + if (err != OK) { + mCallback->onInputSurfaceCreationFailed(err); + return; + } + (new AMessage(kWhatCreateInputSurface, this))->post(); } @@ -477,15 +511,16 @@ void CCodec::setInputSurface(const sp &surface) { } void CCodec::initiateStart() { - { + auto setStarting = [this] { Mutexed::Locked state(mState); if (state->get() != ALLOCATED) { - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } state->set(STARTING); + return OK; + }; + if (tryAndReportOnError(setStarting) != OK) { + return; } (new AMessage(kWhatStart, this))->post(); @@ -493,16 +528,18 @@ void CCodec::initiateStart() { void CCodec::start() { std::shared_ptr comp; - { + auto checkStarting = [this, &comp] { Mutexed::Locked state(mState); if (state->get() != STARTING) { - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } comp = state->comp; + return OK; + }; + if (tryAndReportOnError(checkStarting) != OK) { + return; } + c2_status_t err = comp->start(); if (err != C2_OK) { // TODO: convert err into status_t @@ -516,17 +553,22 @@ void CCodec::start() { inputFormat = formats->inputFormat; outputFormat = formats->outputFormat; } - mChannel->start(inputFormat, outputFormat); + status_t err2 = mChannel->start(inputFormat, outputFormat); + if (err2 != OK) { + mCallback->onError(err2, ACTION_CODE_FATAL); + return; + } - { + auto setRunning = [this] { Mutexed::Locked state(mState); if (state->get() != STARTING) { - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } state->set(RUNNING); + return OK; + }; + if (tryAndReportOnError(setRunning) != OK) { + return; } mCallback->onStartCompleted(); } @@ -652,13 +694,26 @@ status_t CCodec::setSurface(const sp &surface) { } void CCodec::signalFlush() { - { + status_t err = [this] { Mutexed::Locked state(mState); + if (state->get() == FLUSHED) { + return ALREADY_EXISTS; + } if (state->get() != RUNNING) { - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - return; + return UNKNOWN_ERROR; } state->set(FLUSHING); + return OK; + }(); + switch (err) { + case ALREADY_EXISTS: + mCallback->onFlushCompleted(); + return; + case OK: + break; + default: + mCallback->onError(err, ACTION_CODE_FATAL); + return; } (new AMessage(kWhatFlush, this))->post(); @@ -666,15 +721,16 @@ void CCodec::signalFlush() { void CCodec::flush() { std::shared_ptr comp; - { + auto checkFlushing = [this, &comp] { Mutexed::Locked state(mState); if (state->get() != FLUSHING) { - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } comp = state->comp; + return OK; + }; + if (tryAndReportOnError(checkFlushing) != OK) { + return; } mChannel->stop(); @@ -696,18 +752,19 @@ void CCodec::flush() { } void CCodec::signalResume() { - { + auto setResuming = [this] { Mutexed::Locked state(mState); if (state->get() != FLUSHED) { - state.unlock(); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - state.lock(); - return; + return UNKNOWN_ERROR; } state->set(RESUMING); + return OK; + }; + if (tryAndReportOnError(setResuming) != OK) { + return; } - mChannel->start(nullptr, nullptr); + (void)mChannel->start(nullptr, nullptr); { Mutexed::Locked state(mState); @@ -727,6 +784,8 @@ void CCodec::signalSetParameters(const sp &msg) { } void CCodec::signalEndOfInputStream() { + // TODO + mCallback->onSignaledInputEOS(INVALID_OPERATION); } void CCodec::signalRequestIDRFrame() { @@ -735,9 +794,7 @@ void CCodec::signalRequestIDRFrame() { void CCodec::onWorkDone(std::list> &workItems) { Mutexed>>::Locked queue(mWorkDoneQueue); - for (std::unique_ptr &item : workItems) { - queue->push_back(std::move(item)); - } + queue->splice(queue->end(), workItems); (new AMessage(kWhatWorkDone, this))->post(); } @@ -834,8 +891,8 @@ void CCodec::initiateReleaseIfStuck() { } ALOGW("previous call to %s exceeded timeout", name.c_str()); - mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); initiateRelease(false); + mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); } } // namespace android diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index 5ce49d9d33..de78c80b08 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -60,7 +60,10 @@ public: /** * Set format for MediaCodec-facing buffers. */ - void setFormat(const sp &format) { mFormat = format; } + void setFormat(const sp &format) { + CHECK(format != nullptr); + mFormat = format; + } /** * Returns true if the buffers are operating under array mode. @@ -585,6 +588,7 @@ public: size_t *index, sp *clientBuffer) override { sp newBuffer = wrap(buffer); + newBuffer->setFormat(mFormat); *index = mImpl.assignSlot(newBuffer); *clientBuffer = newBuffer; return true; @@ -735,55 +739,6 @@ CCodecBufferChannel::~CCodecBufferChannel() { void CCodecBufferChannel::setComponent(const std::shared_ptr &component) { mComponent = component; - - C2StreamFormatConfig::input inputFormat(0u); - C2StreamFormatConfig::output outputFormat(0u); - c2_status_t err = mComponent->intf()->query_vb( - { &inputFormat, &outputFormat }, - {}, - C2_DONT_BLOCK, - nullptr); - if (err != C2_OK) { - // TODO: error - return; - } - - { - Mutexed>::Locked buffers(mInputBuffers); - - bool graphic = (inputFormat.value == C2FormatVideo); - if (graphic) { - buffers->reset(new GraphicInputBuffers); - } else { - buffers->reset(new LinearInputBuffers); - } - - ALOGV("graphic = %s", graphic ? "true" : "false"); - std::shared_ptr pool; - if (graphic) { - err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &pool); - } else { - err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION, - component, &pool); - } - - if (err == C2_OK) { - (*buffers)->setPool(pool); - } else { - // TODO: error - } - } - - { - Mutexed>::Locked buffers(mOutputBuffers); - - bool graphic = (outputFormat.value == C2FormatVideo); - if (graphic) { - buffers->reset(new GraphicOutputBuffers); - } else { - buffers->reset(new LinearOutputBuffers); - } - } } status_t CCodecBufferChannel::setInputSurface( @@ -812,6 +767,7 @@ status_t CCodecBufferChannel::queueInputBuffer(const sp &buffe if (buffer->meta()->findInt32("csd", &tmp) && tmp) { flags |= C2FrameData::FLAG_CODEC_CONFIG; } + ALOGV("queueInputBuffer: buffer->size() = %zu", buffer->size()); std::unique_ptr work(new C2Work); work->input.flags = (C2FrameData::flags_t)flags; work->input.ordinal.timestamp = timeUs; @@ -968,13 +924,54 @@ void CCodecBufferChannel::getOutputBufferArray(Vector> *arr (*buffers)->getArray(array); } -void CCodecBufferChannel::start(const sp &inputFormat, const sp &outputFormat) { +status_t CCodecBufferChannel::start( + const sp &inputFormat, const sp &outputFormat) { + C2StreamFormatConfig::input iStreamFormat(0u); + C2StreamFormatConfig::output oStreamFormat(0u); + c2_status_t err = mComponent->intf()->query_vb( + { &iStreamFormat, &oStreamFormat }, + {}, + C2_DONT_BLOCK, + nullptr); + if (err != C2_OK) { + return UNKNOWN_ERROR; + } + if (inputFormat != nullptr) { Mutexed>::Locked buffers(mInputBuffers); + + bool graphic = (iStreamFormat.value == C2FormatVideo); + if (graphic) { + buffers->reset(new GraphicInputBuffers); + } else { + buffers->reset(new LinearInputBuffers); + } (*buffers)->setFormat(inputFormat); + + ALOGV("graphic = %s", graphic ? "true" : "false"); + std::shared_ptr pool; + if (graphic) { + err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, mComponent, &pool); + } else { + err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION, + mComponent, &pool); + } + if (err == C2_OK) { + (*buffers)->setPool(pool); + } else { + // TODO: error + } } + if (outputFormat != nullptr) { Mutexed>::Locked buffers(mOutputBuffers); + + bool graphic = (oStreamFormat.value == C2FormatVideo); + if (graphic) { + buffers->reset(new GraphicOutputBuffers); + } else { + buffers->reset(new LinearOutputBuffers); + } (*buffers)->setFormat(outputFormat); } @@ -989,9 +986,7 @@ void CCodecBufferChannel::start(const sp &inputFormat, const sprequestNewBuffer(&index, &buffer)) { if (i == 0) { ALOGE("start: cannot allocate memory at all"); - buffers.unlock(); - mOnError(NO_MEMORY, ACTION_CODE_FATAL); - buffers.lock(); + return NO_MEMORY; } else { ALOGV("start: cannot allocate memory, only %zu buffers allocated", i); } @@ -1003,6 +998,7 @@ void CCodecBufferChannel::start(const sp &inputFormat, const spconnect(mComponent); } + return OK; } void CCodecBufferChannel::stop() { @@ -1010,6 +1006,7 @@ void CCodecBufferChannel::stop() { mFirstValidFrameIndex = mFrameIndex.load(); if (mInputSurface != nullptr) { mInputSurface->disconnect(); + mInputSurface.reset(); } } @@ -1025,8 +1022,13 @@ void CCodecBufferChannel::flush(const std::list> &flushe } void CCodecBufferChannel::onWorkDone(const std::unique_ptr &work) { - if (work->result != OK) { - ALOGE("work failed to complete: %d", work->result); + if (work->result != C2_OK) { + if (work->result == C2_NOT_FOUND) { + // TODO: Define what flushed work's result is. + ALOGD("flushed work; ignored."); + return; + } + ALOGD("work failed to complete: %d", work->result); mOnError(work->result, ACTION_CODE_FATAL); return; } diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h index eb3255f4d3..51eee105cf 100644 --- a/media/libstagefright/include/CCodecBufferChannel.h +++ b/media/libstagefright/include/CCodecBufferChannel.h @@ -88,7 +88,7 @@ public: * Start queueing buffers to the component. This object should never queue * buffers before this call. */ - void start(const sp &inputFormat, const sp &outputFormat); + status_t start(const sp &inputFormat, const sp &outputFormat); /** * Stop queueing buffers to the component. This object should never queue diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h index 3a2670d43b..078b03e0fc 100644 --- a/media/libstagefright/include/media/stagefright/CCodec.h +++ b/media/libstagefright/include/media/stagefright/CCodec.h @@ -69,6 +69,8 @@ protected: private: typedef std::chrono::time_point TimePoint; + status_t tryAndReportOnError(std::function job); + void initiateStop(); void initiateRelease(bool sendCallback = true); -- GitLab From 156398391ed8d14b527328cb276922168c8d91a8 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 16 Feb 2018 10:05:03 -0800 Subject: [PATCH 0024/1530] CCodec: Episode IV --- Raw Video Buffers Define and handle new buffer types for raw video buffers. Bug: 69376489 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest CtsMediaTestCases:ImageReaderDecoderTest#testOtherH264ImageReader Change-Id: I9d1061287e4c46526170ce395f826eccf4def4b7 --- media/libstagefright/BufferImpl.cpp | 447 +++++++++++++++++- media/libstagefright/CCodec.cpp | 8 + media/libstagefright/CCodecBufferChannel.cpp | 264 ++++++++++- .../codecs/avcdec/C2SoftAvcDec.cpp | 142 ++++-- .../codecs/avcdec/C2SoftAvcDec.h | 2 +- media/libstagefright/include/Codec2Buffer.h | 115 +++++ 6 files changed, 915 insertions(+), 63 deletions(-) diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp index d2eee339bc..7b3fa026d3 100644 --- a/media/libstagefright/BufferImpl.cpp +++ b/media/libstagefright/BufferImpl.cpp @@ -70,6 +70,10 @@ bool Codec2Buffer::canCopyLinear(const std::shared_ptr &buffer) const if (const_cast(this)->base() == nullptr) { return false; } + if (!buffer) { + // Nothing to copy, so we can copy by doing nothing. + return true; + } if (buffer->data().type() != C2BufferData::LINEAR) { return false; } @@ -89,7 +93,7 @@ bool Codec2Buffer::canCopyLinear(const std::shared_ptr &buffer) const bool Codec2Buffer::copyLinear(const std::shared_ptr &buffer) { // We assume that all canCopyLinear() checks passed. - if (buffer->data().linearBlocks().size() == 0u) { + if (!buffer || buffer->data().linearBlocks().size() == 0u) { setRange(0, 0); return true; } @@ -207,4 +211,445 @@ std::shared_ptr ConstLinearBlockBuffer::asC2Buffer() { return std::move(mBufferRef); } +// GraphicView2MediaImageConverter + +namespace { + +class GraphicView2MediaImageConverter { +public: + explicit GraphicView2MediaImageConverter(const C2GraphicView &view) + : mInitCheck(NO_INIT), + mView(view), + mWidth(view.width()), + mHeight(view.height()), + mAllocatedDepth(0), + mBackBufferSize(0), + mMediaImage(new ABuffer(sizeof(MediaImage2))) { + if (view.error() != C2_OK) { + ALOGD("Converter: view.error() = %d", view.error()); + mInitCheck = BAD_VALUE; + return; + } + MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base(); + const C2PlanarLayout &layout = view.layout(); + if (layout.numPlanes == 0) { + ALOGD("Converter: 0 planes"); + mInitCheck = BAD_VALUE; + return; + } + mAllocatedDepth = layout.planes[0].allocatedDepth; + uint32_t bitDepth = layout.planes[0].bitDepth; + + switch (layout.type) { + case C2PlanarLayout::TYPE_YUV: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break; + case C2PlanarLayout::TYPE_YUVA: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break; + case C2PlanarLayout::TYPE_RGB: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break; + case C2PlanarLayout::TYPE_RGBA: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break; + default: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break; + } + mediaImage->mNumPlanes = layout.numPlanes; + mediaImage->mWidth = mWidth; + mediaImage->mHeight = mHeight; + mediaImage->mBitDepth = bitDepth; + mediaImage->mBitDepthAllocated = mAllocatedDepth; + + uint32_t bufferSize = 0; + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + if (plane.rightShift != 0) { + ALOGV("rightShift value of %u unsupported", plane.rightShift); + mInitCheck = BAD_VALUE; + return; + } + if (plane.endianness != C2PlaneInfo::NATIVE) { + ALOGV("endianness value of %u unsupported", plane.endianness); + mInitCheck = BAD_VALUE; + return; + } + if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) { + ALOGV("different allocatedDepth/bitDepth per plane unsupported"); + mInitCheck = BAD_VALUE; + return; + } + bufferSize += mWidth * mHeight + / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8); + } + + mBackBufferSize = bufferSize; + mInitCheck = OK; + } + + status_t initCheck() const { return mInitCheck; } + + uint32_t backBufferSize() const { return mBackBufferSize; } + + /** + * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content + * is not copied over in this function --- the caller should use + * CopyGraphicView2MediaImage() function to do that explicitly. + * + * \param view[in] source C2GraphicView object. + * \param alloc[in] allocator function for ABuffer. + * \param mediaImage[out] destination MediaImage2 object. + * \param buffer[out] new buffer object. + * \param wrapped[out] whether we wrapped around existing map or + * allocated a new buffer + * + * \return true if conversion succeeds, + * false otherwise; all output params should be ignored. + */ + sp wrap() { + MediaImage2 *mediaImage = getMediaImage(); + const C2PlanarLayout &layout = mView.layout(); + if (layout.numPlanes == 1) { + const C2PlaneInfo &plane = layout.planes[0]; + ssize_t offset = plane.minOffset(mWidth, mHeight); + mediaImage->mPlane[0].mOffset = -offset; + mediaImage->mPlane[0].mColInc = plane.colInc; + mediaImage->mPlane[0].mRowInc = plane.rowInc; + mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling; + return new ABuffer( + const_cast(mView.data()[0] + offset), + plane.maxOffset(mWidth, mHeight) - offset + 1); + } + const uint8_t *minPtr = mView.data()[0]; + const uint8_t *maxPtr = mView.data()[0]; + int32_t planeSize = 0; + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + ssize_t minOffset = plane.minOffset(mWidth, mHeight); + ssize_t maxOffset = plane.maxOffset(mWidth, mHeight); + if (minPtr > mView.data()[i] + minOffset) { + minPtr = mView.data()[i] + minOffset; + } + if (maxPtr < mView.data()[i] + maxOffset) { + maxPtr = mView.data()[i] + maxOffset; + } + planeSize += std::abs(plane.rowInc) * mHeight + / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8); + } + + if ((maxPtr - minPtr + 1) <= planeSize) { + // FIXME: this is risky as reading/writing data out of bound results in + // an undefined behavior. + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr; + mediaImage->mPlane[i].mColInc = plane.colInc; + mediaImage->mPlane[i].mRowInc = plane.rowInc; + mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + } + return new ABuffer(const_cast(minPtr), maxPtr - minPtr + 1); + } + + return nullptr; + } + + bool setBackBuffer(const sp &backBuffer) { + if (backBuffer->capacity() < mBackBufferSize) { + return false; + } + backBuffer->setRange(0, mBackBufferSize); + + const C2PlanarLayout &layout = mView.layout(); + MediaImage2 *mediaImage = getMediaImage(); + uint32_t offset = 0; + // TODO: keep interleaved planes together + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + mediaImage->mPlane[i].mOffset = offset; + mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8; + mediaImage->mPlane[i].mRowInc = + mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling; + mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling; + } + mBackBuffer = backBuffer; + return true; + } + + /** + * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is + * an output from GraphicView2MediaImage(), so it mostly skips sanity check. + * + * \param view[in] source C2GraphicView object. + * \param mediaImage[in] destination MediaImage2 object. + * \param buffer[out] new buffer object. + */ + void copy() { + // TODO: more efficient copying --- e.g. one row at a time, copying + // interleaved planes together, etc. + const C2PlanarLayout &layout = mView.layout(); + MediaImage2 *mediaImage = getMediaImage(); + uint8_t *dst = mBackBuffer->base(); + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + const uint8_t *src = mView.data()[i]; + int32_t planeW = mWidth / plane.colSampling; + int32_t planeH = mHeight / plane.rowSampling; + for (int32_t row = 0; row < planeH; ++row) { + for(int32_t col = 0; col < planeW; ++col) { + memcpy(dst, src, mAllocatedDepth / 8); + dst += mediaImage->mPlane[i].mColInc; + src += plane.colInc; + } + dst -= mediaImage->mPlane[i].mColInc * planeW; + dst += mediaImage->mPlane[i].mRowInc; + src -= plane.colInc * planeW; + src += plane.rowInc; + } + } + } + + const sp &imageData() const { return mMediaImage; } + +private: + status_t mInitCheck; + + const C2GraphicView mView; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mAllocatedDepth; + uint32_t mBackBufferSize; + sp mMediaImage; + std::function(size_t)> mAlloc; + + sp mBackBuffer; + + MediaImage2 *getMediaImage() { + return (MediaImage2 *)mMediaImage->base(); + } +}; + +} // namespace + +// GraphicBlockBuffer + +// static +sp GraphicBlockBuffer::Allocate( + const sp &format, + const std::shared_ptr &block, + std::function(size_t)> alloc) { + C2GraphicView view(block->map().get()); + if (view.error() != C2_OK) { + ALOGD("C2GraphicBlock::map failed: %d", view.error()); + return nullptr; + } + GraphicView2MediaImageConverter converter(view); + if (converter.initCheck() != OK) { + ALOGD("Converter init failed: %d", converter.initCheck()); + return nullptr; + } + bool wrapped = true; + sp buffer = converter.wrap(); + if (buffer == nullptr) { + buffer = alloc(converter.backBufferSize()); + if (!converter.setBackBuffer(buffer)) { + ALOGD("Converter failed to set back buffer"); + return nullptr; + } + wrapped = false; + } + return new GraphicBlockBuffer( + format, + buffer, + std::move(view), + block, + converter.imageData(), + wrapped); +} + +GraphicBlockBuffer::GraphicBlockBuffer( + const sp &format, + const sp &buffer, + C2GraphicView &&view, + const std::shared_ptr &block, + const sp &imageData, + bool wrapped) + : Codec2Buffer(format, buffer), + mView(view), + mBlock(block), + mImageData(imageData), + mWrapped(wrapped) { + meta()->setBuffer("image-data", imageData); +} + +std::shared_ptr GraphicBlockBuffer::asC2Buffer() { + uint32_t width = mView.width(); + uint32_t height = mView.height(); + if (!mWrapped) { + MediaImage2 *mediaImage = imageData(); + const C2PlanarLayout &layout = mView.layout(); + for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + int32_t planeW = width / plane.colSampling; + int32_t planeH = height / plane.rowSampling; + const uint8_t *src = base() + mediaImage->mPlane[i].mOffset; + uint8_t *dst = mView.data()[i]; + for (int32_t row = 0; row < planeH; ++row) { + for (int32_t col = 0; col < planeW; ++col) { + memcpy(dst, src, mediaImage->mBitDepthAllocated / 8); + src += mediaImage->mPlane[i].mColInc; + dst += plane.colInc; + } + src -= mediaImage->mPlane[i].mColInc * planeW; + dst -= plane.colInc * planeW; + src += mediaImage->mPlane[i].mRowInc; + dst += plane.rowInc; + } + } + } + return C2Buffer::CreateGraphicBuffer( + mBlock->share(C2Rect(width, height), C2Fence())); +} + +// ConstGraphicBlockBuffer + +// static +sp ConstGraphicBlockBuffer::Allocate( + const sp &format, + const std::shared_ptr &buffer, + std::function(size_t)> alloc) { + if (!buffer + || buffer->data().type() != C2BufferData::GRAPHIC + || buffer->data().graphicBlocks().size() != 1u) { + ALOGD("C2Buffer precond fail"); + return nullptr; + } + std::unique_ptr view(std::make_unique( + buffer->data().graphicBlocks()[0].map().get())); + std::unique_ptr holder; + + GraphicView2MediaImageConverter converter(*view); + if (converter.initCheck() != OK) { + ALOGD("Converter init failed: %d", converter.initCheck()); + return nullptr; + } + bool wrapped = true; + sp aBuffer = converter.wrap(); + if (aBuffer == nullptr) { + aBuffer = alloc(converter.backBufferSize()); + if (!converter.setBackBuffer(aBuffer)) { + ALOGD("Converter failed to set back buffer"); + return nullptr; + } + wrapped = false; + converter.copy(); + // We don't need the view. + holder = std::move(view); + } + return new ConstGraphicBlockBuffer( + format, + aBuffer, + std::move(view), + buffer, + converter.imageData(), + wrapped); +} + +// static +sp ConstGraphicBlockBuffer::AllocateEmpty( + const sp &format, + std::function(size_t)> alloc) { + int32_t width, height; + if (!format->findInt32("width", &width) + || !format->findInt32("height", &height)) { + ALOGD("format had no width / height"); + return nullptr; + } + sp aBuffer(alloc(width * height * 4)); + return new ConstGraphicBlockBuffer( + format, + aBuffer, + nullptr, + nullptr, + nullptr, + false); +} + +ConstGraphicBlockBuffer::ConstGraphicBlockBuffer( + const sp &format, + const sp &aBuffer, + std::unique_ptr &&view, + const std::shared_ptr &buffer, + const sp &imageData, + bool wrapped) + : Codec2Buffer(format, aBuffer), + mView(std::move(view)), + mBufferRef(buffer), + mWrapped(wrapped) { + if (imageData != nullptr) { + meta()->setBuffer("image-data", imageData); + } +} + +std::shared_ptr ConstGraphicBlockBuffer::asC2Buffer() { + mView.reset(); + return std::move(mBufferRef); +} + +bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr &buffer) const { + if (mWrapped || mBufferRef) { + ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s", + mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist"); + return false; + } + if (!buffer) { + // Nothing to copy, so we can copy by doing nothing. + return true; + } + if (buffer->data().type() != C2BufferData::GRAPHIC) { + ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied"); + return false; + } + if (buffer->data().graphicBlocks().size() == 0) { + return true; + } else if (buffer->data().graphicBlocks().size() != 1u) { + ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks"); + return false; + } + GraphicView2MediaImageConverter converter( + buffer->data().graphicBlocks()[0].map().get()); + if (converter.initCheck() != OK) { + ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck()); + return false; + } + if (converter.backBufferSize() > capacity()) { + ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu", + converter.backBufferSize(), capacity()); + return false; + } + return true; +} + +bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { + if (!buffer || buffer->data().graphicBlocks().size() == 0) { + setRange(0, 0); + return true; + } + GraphicView2MediaImageConverter converter( + buffer->data().graphicBlocks()[0].map().get()); + if (converter.initCheck() != OK) { + ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck()); + return false; + } + sp aBuffer = new ABuffer(base(), capacity()); + if (!converter.setBackBuffer(aBuffer)) { + ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed"); + return false; + } + converter.copy(); + meta()->setBuffer("image-data", converter.imageData()); + mBufferRef = buffer; + return true; +} + } // namespace android diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp index a144382249..0bdd8089d7 100644 --- a/media/libstagefright/CCodec.cpp +++ b/media/libstagefright/CCodec.cpp @@ -404,6 +404,14 @@ void CCodec::configure(const sp &msg) { if (audio) { outputFormat->setInt32("channel-count", 2); outputFormat->setInt32("sample-rate", 44100); + } else { + int32_t tmp; + if (msg->findInt32("width", &tmp)) { + outputFormat->setInt32("width", tmp); + } + if (msg->findInt32("height", &tmp)) { + outputFormat->setInt32("height", tmp); + } } } diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index de78c80b08..65d637bb89 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -182,21 +182,158 @@ namespace { const static size_t kMinBufferArraySize = 16; const static size_t kLinearBufferSize = 524288; -sp allocateLinearBuffer( +/** + * Simple local buffer pool backed by std::vector. + */ +class LocalBufferPool : public std::enable_shared_from_this { +public: + /** + * Create a new LocalBufferPool object. + * + * \param poolCapacity max total size of buffers managed by this pool. + * + * \return a newly created pool object. + */ + static std::shared_ptr Create(size_t poolCapacity) { + return std::shared_ptr(new LocalBufferPool(poolCapacity)); + } + + /** + * Return an ABuffer object whose size is at least |capacity|. + * + * \param capacity requested capacity + * \return nullptr if the pool capacity is reached + * an ABuffer object otherwise. + */ + sp newBuffer(size_t capacity) { + Mutex::Autolock lock(mMutex); + auto it = std::find_if( + mPool.begin(), mPool.end(), + [capacity](const std::vector &vec) { + return vec.capacity() >= capacity; + }); + if (it != mPool.end()) { + sp buffer = new VectorBuffer(std::move(*it), shared_from_this()); + mPool.erase(it); + return buffer; + } + if (mUsedSize + capacity > mPoolCapacity) { + while (!mPool.empty()) { + mUsedSize -= mPool.back().capacity(); + mPool.pop_back(); + } + if (mUsedSize + capacity > mPoolCapacity) { + ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu", + mUsedSize, capacity, mPoolCapacity); + return nullptr; + } + } + std::vector vec(capacity); + mUsedSize += vec.capacity(); + return new VectorBuffer(std::move(vec), shared_from_this()); + } + +private: + /** + * ABuffer backed by std::vector. + */ + class VectorBuffer : public ::android::ABuffer { + public: + /** + * Construct a VectorBuffer by taking the ownership of supplied vector. + * + * \param vec backing vector of the buffer. this object takes + * ownership at construction. + * \param pool a LocalBufferPool object to return the vector at + * destruction. + */ + VectorBuffer(std::vector &&vec, const std::shared_ptr &pool) + : ABuffer(vec.data(), vec.capacity()), + mVec(std::move(vec)), + mPool(pool) { + } + + ~VectorBuffer() override { + std::shared_ptr pool = mPool.lock(); + if (pool) { + // If pool is alive, return the vector back to the pool so that + // it can be recycled. + pool->returnVector(std::move(mVec)); + } + } + + private: + std::vector mVec; + std::weak_ptr mPool; + }; + + Mutex mMutex; + size_t mPoolCapacity; + size_t mUsedSize; + std::list> mPool; + + /** + * Private constructor to prevent constructing non-managed LocalBufferPool. + */ + explicit LocalBufferPool(size_t poolCapacity) + : mPoolCapacity(poolCapacity), mUsedSize(0) { + } + + /** + * Take back the ownership of vec from the destructed VectorBuffer and put + * it in front of the pool. + */ + void returnVector(std::vector &&vec) { + Mutex::Autolock lock(mMutex); + mPool.push_front(std::move(vec)); + } + + DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool); +}; + +sp AllocateLinearBuffer( const std::shared_ptr &pool, const sp &format, size_t size, const C2MemoryUsage &usage) { std::shared_ptr block; - status_t err = pool->fetchLinearBlock(size, usage, &block); - if (err != OK) { + c2_status_t err = pool->fetchLinearBlock(size, usage, &block); + if (err != C2_OK) { return nullptr; } return LinearBlockBuffer::Allocate(format, block); } +sp AllocateGraphicBuffer( + const std::shared_ptr &pool, + const sp &format, + uint32_t pixelFormat, + const C2MemoryUsage &usage, + const std::shared_ptr &localBufferPool) { + int32_t width, height; + if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) { + ALOGD("format lacks width or height"); + return nullptr; + } + + std::shared_ptr block; + c2_status_t err = pool->fetchGraphicBlock( + width, height, pixelFormat, usage, &block); + if (err != C2_OK) { + ALOGD("fetch graphic block failed: %d", err); + return nullptr; + } + + return GraphicBlockBuffer::Allocate( + format, + block, + [localBufferPool](size_t capacity) { + return localBufferPool->newBuffer(capacity); + }); +} + class BuffersArrayImpl; /** @@ -386,6 +523,7 @@ private: class InputBuffersArray : public CCodecBufferChannel::InputBuffers { public: InputBuffersArray() = default; + ~InputBuffersArray() override = default; void initialize( const FlexBuffersImpl &impl, @@ -435,7 +573,8 @@ public: // TODO: proper max input size // TODO: read usage from intf C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - sp newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage); + sp newBuffer = AllocateLinearBuffer( + mPool, mFormat, kLinearBufferSize, usage); if (newBuffer == nullptr) { return false; } @@ -461,7 +600,7 @@ public: kMinBufferArraySize, [pool = mPool, format = mFormat] () -> sp { C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - return allocateLinearBuffer(pool, format, kLinearBufferSize, usage); + return AllocateLinearBuffer(pool, format, kLinearBufferSize, usage); }); return std::move(array); } @@ -472,7 +611,54 @@ private: class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers { public: - GraphicInputBuffers() = default; + GraphicInputBuffers() : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {} + ~GraphicInputBuffers() override = default; + + bool requestNewBuffer(size_t *index, sp *buffer) override { + // TODO: proper max input size + // TODO: read usage from intf + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + sp newBuffer = AllocateGraphicBuffer( + mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool); + if (newBuffer == nullptr) { + return false; + } + *index = mImpl.assignSlot(newBuffer); + *buffer = newBuffer; + return true; + } + + std::shared_ptr releaseBuffer(const sp &buffer) override { + return mImpl.releaseSlot(buffer); + } + + void flush() override { + // This is no-op by default unless we're in array mode where we need to keep + // track of the flushed work. + } + + std::unique_ptr toArrayMode() final { + std::unique_ptr array(new InputBuffersArray); + array->setFormat(mFormat); + array->initialize( + mImpl, + kMinBufferArraySize, + [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp { + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + return AllocateGraphicBuffer( + pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp); + }); + return std::move(array); + } + +private: + FlexBuffersImpl mImpl; + std::shared_ptr mLocalBufferPool; +}; + +class DummyInputBuffers : public CCodecBufferChannel::InputBuffers { +public: + DummyInputBuffers() = default; bool requestNewBuffer(size_t *, sp *) override { return false; @@ -498,7 +684,8 @@ public: class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers { public: - using CCodecBufferChannel::OutputBuffers::OutputBuffers; + OutputBuffersArray() = default; + ~OutputBuffersArray() override = default; void initialize( const FlexBuffersImpl &impl, @@ -525,12 +712,14 @@ public: return clientBuffer->canCopy(buffer); }); if (err != OK) { + ALOGD("grabBuffer failed: %d", err); return false; } + c2Buffer->setFormat(mFormat); if (!c2Buffer->copy(buffer)) { + ALOGD("copy buffer failed"); return false; } - c2Buffer->setFormat(mFormat); *clientBuffer = c2Buffer; return true; } @@ -631,8 +820,21 @@ public: return std::move(array); } + /** + * Return an appropriate Codec2Buffer object for the type of buffers. + * + * \param buffer C2Buffer object to wrap. + * + * \return appropriate Codec2Buffer object to wrap |buffer|. + */ virtual sp wrap(const std::shared_ptr &buffer) = 0; + /** + * Return an appropriate Codec2Buffer object for the type of buffers, to be + * used as an empty array buffer. + * + * \return appropriate Codec2Buffer object which can copy() from C2Buffers. + */ virtual sp allocateArrayBuffer() = 0; private: @@ -677,6 +879,34 @@ public: } }; +class RawGraphicOutputBuffers : public FlexOutputBuffers { +public: + RawGraphicOutputBuffers() + : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) { + } + ~RawGraphicOutputBuffers() override = default; + + sp wrap(const std::shared_ptr &buffer) override { + return ConstGraphicBlockBuffer::Allocate( + mFormat, + buffer, + [lbp = mLocalBufferPool](size_t capacity) { + return lbp->newBuffer(capacity); + }); + } + + sp allocateArrayBuffer() override { + return ConstGraphicBlockBuffer::AllocateEmpty( + mFormat, + [lbp = mLocalBufferPool](size_t capacity) { + return lbp->newBuffer(capacity); + }); + } + +private: + std::shared_ptr mLocalBufferPool; +}; + } // namespace CCodecBufferChannel::QueueGuard::QueueGuard( @@ -942,7 +1172,11 @@ status_t CCodecBufferChannel::start( bool graphic = (iStreamFormat.value == C2FormatVideo); if (graphic) { - buffers->reset(new GraphicInputBuffers); + if (mInputSurface) { + buffers->reset(new DummyInputBuffers); + } else { + buffers->reset(new GraphicInputBuffers); + } } else { buffers->reset(new LinearInputBuffers); } @@ -964,11 +1198,21 @@ status_t CCodecBufferChannel::start( } if (outputFormat != nullptr) { + bool hasOutputSurface = false; + { + Mutexed>::Locked surface(mSurface); + hasOutputSurface = (*surface != nullptr); + } + Mutexed>::Locked buffers(mOutputBuffers); bool graphic = (oStreamFormat.value == C2FormatVideo); if (graphic) { - buffers->reset(new GraphicOutputBuffers); + if (hasOutputSurface) { + buffers->reset(new GraphicOutputBuffers); + } else { + buffers->reset(new RawGraphicOutputBuffers); + } } else { buffers->reset(new LinearOutputBuffers); } diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp index ad36f0d260..19e357ab83 100644 --- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp +++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp @@ -37,15 +37,6 @@ #include "ih264d_defs.h" -namespace { - -template -inline int32_t floor32(T arg) { - return (int32_t) std::llround(std::floor(arg)); -} - -} // namespace - namespace android { struct iv_obj_t : public ::iv_obj_t {}; @@ -83,6 +74,20 @@ std::shared_ptr BuildIntf( .build(); } +void CopyPlane( + uint8_t *dst, const C2PlaneInfo &plane, + const uint8_t *src, uint32_t width, uint32_t height) { + for (uint32_t row = 0; row < height; ++row) { + for (uint32_t col = 0; col < width; ++col) { + *dst = *src; + dst += plane.colInc; + ++src; + } + dst -= plane.colInc * width; + dst += plane.rowInc; + } +} + void fillEmptyWork(const std::unique_ptr &work) { uint32_t flags = 0; if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) { @@ -103,7 +108,7 @@ C2SoftAvcDec::C2SoftAvcDec( c2_node_id_t id) : SimpleC2Component(BuildIntf(name, id)), mCodecCtx(NULL), - mFlushOutBuffer(NULL), + mOutBuffer(NULL), mIvColorFormat(IV_YUV_420P), mChangingResolution(false), mSignalledError(false), @@ -164,9 +169,9 @@ c2_status_t C2SoftAvcDec::onFlush_sm() { } } - if (mFlushOutBuffer) { - free(mFlushOutBuffer); - mFlushOutBuffer = NULL; + if (mOutBuffer) { + free(mOutBuffer); + mOutBuffer = NULL; } return C2_OK; } @@ -244,6 +249,16 @@ status_t C2SoftAvcDec::setParams(size_t stride) { return UNKNOWN_ERROR; } + + if (mOutBuffer != NULL) { + free(mOutBuffer); + } + uint32_t bufferSize = mWidth * mHeight * 3 / 2; + mOutBuffer = (uint8_t *)memalign(128, bufferSize); + if (NULL == mOutBuffer) { + ALOGE("Could not allocate output buffer of size %u", bufferSize); + return C2_NO_MEMORY; + } return OK; } @@ -321,17 +336,6 @@ status_t C2SoftAvcDec::setFlushMode() { return UNKNOWN_ERROR; } - /* Allocate a picture buffer to flushed data */ - uint32_t displayStride = mWidth; - uint32_t displayHeight = mHeight; - - uint32_t bufferSize = displayStride * displayHeight * 3 / 2; - mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize); - if (NULL == mFlushOutBuffer) { - ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); - return C2_NO_MEMORY; - } - return OK; } @@ -499,14 +503,29 @@ bool C2SoftAvcDec::setDecodeArgs( outBuffer->width(), outBuffer->height(), width, height); return false; } + ALOGV("width = %u, stride[0] = %u, stride[1] = %u, stride[2] = %u", + outBuffer->width(), + outBuffer->layout().planes[0].rowInc, + outBuffer->layout().planes[1].rowInc, + outBuffer->layout().planes[2].rowInc); + const C2PlanarLayout &layout = outBuffer->layout(); ps_dec_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[0]; + if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) { + ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer; + } ps_dec_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[1]; + if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) { + ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY; + } ps_dec_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[2]; + if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) { + ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV; + } } else { - // mFlushOutBuffer always has the right size. - ps_dec_ip->s_out_buffer.pu1_bufs[0] = mFlushOutBuffer; - ps_dec_ip->s_out_buffer.pu1_bufs[1] = mFlushOutBuffer + sizeY; - ps_dec_ip->s_out_buffer.pu1_bufs[2] = mFlushOutBuffer + sizeY + sizeUV; + // mOutBuffer always has the right size. + ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer; + ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY; + ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV; } ps_dec_ip->s_out_buffer.u4_num_bufs = 3; @@ -544,7 +563,8 @@ c2_status_t C2SoftAvcDec::ensureDecoderState(const std::shared_ptr } void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr &work) { - std::shared_ptr buffer = createGraphicBuffer(std::move(mAllocatedBlock)); + std::shared_ptr buffer = createGraphicBuffer(mAllocatedBlock); + mAllocatedBlock.reset(); auto fillWork = [buffer](const std::unique_ptr &work) { uint32_t flags = 0; if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) { @@ -603,34 +623,54 @@ void C2SoftAvcDec::process( break; } (void)ensureDecoderState(pool); - C2GraphicView output = mAllocatedBlock->map().get(); - if (output.error() != OK) { - ALOGE("mapped err = %d", output.error()); - } - ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; WORD32 timeDelay, timeTaken; //size_t sizeY, sizeUV; - if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) { - ALOGE("Decoder arg setup failed"); - // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); - mSignalledError = true; - break; + { + C2GraphicView output = mAllocatedBlock->map().get(); + if (output.error() != C2_OK) { + ALOGE("mapped err = %d", output.error()); + work->result = output.error(); + fillEmptyWork(work); + return; + } + if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) { + ALOGE("Decoder arg setup failed"); + // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + break; + } + ALOGV("Decoder arg setup succeeded"); + // If input dump is enabled, then write to file + DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset); + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + IV_API_CALL_STATUS_T status; + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF)); + if (s_dec_op.u4_output_present) { + const C2PlanarLayout &layout = output.layout(); + if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) { + CopyPlane(output.data()[0], layout.planes[0], mOutBuffer, mWidth, mHeight); + } + if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) { + CopyPlane( + output.data()[1], layout.planes[1], + mOutBuffer + (mWidth * mHeight), mWidth / 2, mHeight / 2); + } + if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) { + CopyPlane( + output.data()[2], layout.planes[2], + mOutBuffer + (mWidth * mHeight * 5 / 4), mWidth / 2, mHeight / 2); + } + } } - ALOGV("Decoder arg setup succeeded"); - // If input dump is enabled, then write to file - DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset); - - GETTIME(&mTimeStart, NULL); - /* Compute time elapsed between end of previous decode() - * to start of current decode() */ - TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); - - IV_API_CALL_STATUS_T status; - status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); - ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF)); bool unsupportedResolution = (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h index 6632bf370d..d324a0fe67 100644 --- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h +++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h @@ -189,7 +189,7 @@ private: struct timeval mTimeEnd; // Time at the end of decode() // Internal buffer to be used to flush out the buffers from decoder - uint8_t *mFlushOutBuffer; + uint8_t *mOutBuffer; #ifdef FILE_DUMP_ENABLE char mInFile[200]; diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h index 9766b41c96..eeb889d9dc 100644 --- a/media/libstagefright/include/Codec2Buffer.h +++ b/media/libstagefright/include/Codec2Buffer.h @@ -20,6 +20,7 @@ #include +#include #include namespace android { @@ -109,6 +110,11 @@ class LinearBlockBuffer : public Codec2Buffer { public: /** * Allocate a new LinearBufferBlock wrapping around C2LinearBlock object. + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param block C2LinearBlock object to wrap around. + * \return LinearBlockBuffer object with writable mapping. + * nullptr if unsuccessful. */ static sp Allocate( const sp &format, const std::shared_ptr &block); @@ -137,6 +143,11 @@ class ConstLinearBlockBuffer : public Codec2Buffer { public: /** * Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object. + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param buffer linear C2Buffer object to wrap around. + * \return ConstLinearBlockBuffer object with readable mapping. + * nullptr if unsuccessful. */ static sp Allocate( const sp &format, const std::shared_ptr &buffer); @@ -156,6 +167,110 @@ private: std::shared_ptr mBufferRef; }; +/** + * MediaCodecBuffer implementation wraps around C2GraphicBlock. + * + * This object exposes the underlying bits via accessor APIs and "image-data" + * metadata, created automatically at allocation time. + */ +class GraphicBlockBuffer : public Codec2Buffer { +public: + /** + * Allocate a new GraphicBlockBuffer wrapping around C2GraphicBlock object. + * If |block| is not in good color formats, it allocates YV12 local buffer + * and copies the content over at asC2Buffer(). + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param block C2GraphicBlock object to wrap around. + * \param alloc a function to allocate backing ABuffer if needed. + * \return GraphicBlockBuffer object with writable mapping. + * nullptr if unsuccessful. + */ + static sp Allocate( + const sp &format, + const std::shared_ptr &block, + std::function(size_t)> alloc); + + std::shared_ptr asC2Buffer() override; + + virtual ~GraphicBlockBuffer() = default; + +private: + GraphicBlockBuffer( + const sp &format, + const sp &buffer, + C2GraphicView &&view, + const std::shared_ptr &block, + const sp &imageData, + bool wrapped); + GraphicBlockBuffer() = delete; + + inline MediaImage2 *imageData() { return (MediaImage2 *)mImageData->data(); } + + C2GraphicView mView; + std::shared_ptr mBlock; + sp mImageData; + const bool mWrapped; +}; + +/** + * MediaCodecBuffer implementation wraps around graphic C2Buffer object. + * + * This object exposes the underlying bits via accessor APIs and "image-data" + * metadata, created automatically at allocation time. + */ +class ConstGraphicBlockBuffer : public Codec2Buffer { +public: + /** + * Allocate a new ConstGraphicBlockBuffer wrapping around C2Buffer object. + * If |buffer| is not in good color formats, it allocates YV12 local buffer + * and copies the content of |buffer| over to expose. + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param buffer graphic C2Buffer object to wrap around. + * \param alloc a function to allocate backing ABuffer if needed. + * \return ConstGraphicBlockBuffer object with readable mapping. + * nullptr if unsuccessful. + */ + static sp Allocate( + const sp &format, + const std::shared_ptr &buffer, + std::function(size_t)> alloc); + + /** + * Allocate a new ConstGraphicBlockBuffer which allocates YV12 local buffer + * and copies the content of |buffer| over to expose. + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param alloc a function to allocate backing ABuffer if needed. + * \return ConstGraphicBlockBuffer object with no wrapping buffer. + */ + static sp AllocateEmpty( + const sp &format, + std::function(size_t)> alloc); + + std::shared_ptr asC2Buffer() override; + bool canCopy(const std::shared_ptr &buffer) const override; + bool copy(const std::shared_ptr &buffer) override; + + virtual ~ConstGraphicBlockBuffer() = default; + +private: + ConstGraphicBlockBuffer( + const sp &format, + const sp &aBuffer, + std::unique_ptr &&view, + const std::shared_ptr &buffer, + const sp &imageData, + bool wrapped); + ConstGraphicBlockBuffer() = delete; + + sp mImageData; + std::unique_ptr mView; + std::shared_ptr mBufferRef; + const bool mWrapped; +}; + } // namespace android #endif // CODEC2_BUFFER_H_ -- GitLab From 99756444e65853ca8a8ebe019abe9d39c3b714f6 Mon Sep 17 00:00:00 2001 From: Pin-chih Lin Date: Thu, 8 Mar 2018 23:10:49 +0800 Subject: [PATCH 0025/1530] codec2: use flexible YCbCr 420 888 format for GraphicBuffer Some platforms only support NV12 instead of YV12. Use flexible format to contain both. Bug: 73261432 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.ccodec_v4l2 yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.MediaCodecTest Change-Id: I6386afe88793fe40b9eba0b17baa650999e99fa0 --- media/libstagefright/CCodecBufferChannel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index 449c6aa90f..ac786cc87c 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -894,7 +894,7 @@ status_t CCodecBufferChannel::renderOutputBuffer( GraphicBuffer::CLONE_HANDLE, blocks.front().width(), blocks.front().height(), - HAL_PIXEL_FORMAT_YV12, + HAL_PIXEL_FORMAT_YCbCr_420_888, // TODO 1, (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, -- GitLab From ba1f0ab5ce8aa3a96578f6fb372d28d56a687aef Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 6 Mar 2018 12:44:24 -0800 Subject: [PATCH 0026/1530] Remove 6 out of 8 RefBase references from mkv extractor Bug: 67908544 Test: CTS DecoderTest Change-Id: I04b6cda83861e70f1de70abc671dd791327be159 --- include/media/ExtractorUtils.h | 1 + media/extractors/mkv/MatroskaExtractor.cpp | 11 ++++--- media/extractors/ogg/OggExtractor.cpp | 15 ++------- .../include/media/ExtractorUtils.h | 32 +++++++++++++++++++ media/libstagefright/MetaDataUtils.cpp | 3 +- .../codecs/flac/dec/C2SoftFlacDecoder.cpp | 4 +++ .../codecs/flac/dec/C2SoftFlacDecoder.h | 2 +- .../codecs/flac/dec/SoftFlacDecoder.cpp | 1 + .../codecs/flac/dec/SoftFlacDecoder.h | 2 +- media/libstagefright/flac/dec/FLACDecoder.cpp | 7 ++-- media/libstagefright/flac/dec/FLACDecoder.h | 6 ++-- .../include/media/stagefright/MetaDataUtils.h | 2 +- media/libstagefright/mpeg2ts/ESQueue.cpp | 8 +++-- 13 files changed, 65 insertions(+), 29 deletions(-) create mode 120000 include/media/ExtractorUtils.h create mode 100644 media/libmediaextractor/include/media/ExtractorUtils.h diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h new file mode 120000 index 0000000000..e2dd082066 --- /dev/null +++ b/include/media/ExtractorUtils.h @@ -0,0 +1 @@ +../../media/libmediaextractor/include/media/ExtractorUtils.h \ No newline at end of file diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 3f832bc47f..fc60fd4577 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -22,6 +22,7 @@ #include "MatroskaExtractor.h" #include +#include #include #include #include @@ -1108,7 +1109,7 @@ static status_t addFlacMetadata( meta.setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize); int32_t maxInputSize = 64 << 10; - sp flacDecoder = FLACDecoder::Create(); + FLACDecoder *flacDecoder = FLACDecoder::Create(); if (flacDecoder != NULL && flacDecoder->parseMetadata((const uint8_t*)codecPrivate, codecPrivateSize) == OK) { FLAC__StreamMetadata_StreamInfo streamInfo = flacDecoder->getStreamInfo(); @@ -1120,6 +1121,7 @@ static status_t addFlacMetadata( && streamInfo.channels != 0 && ((streamInfo.bits_per_sample + 7) / 8) > INT32_MAX / streamInfo.max_blocksize / streamInfo.channels) { + delete flacDecoder; return ERROR_MALFORMED; } maxInputSize = ((streamInfo.bits_per_sample + 7) / 8) @@ -1128,6 +1130,7 @@ static status_t addFlacMetadata( } meta.setInt32(kKeyMaxInputSize, maxInputSize); + delete flacDecoder; return OK; } @@ -1143,13 +1146,13 @@ status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) { } const mkvparser::Block::Frame &frame = block->GetFrame(0); - sp abuf = new ABuffer(frame.len); - long n = frame.Read(mReader, abuf->data()); + auto tmpData = heapbuffer(frame.len); + long n = frame.Read(mReader, tmpData.get()); if (n != 0) { return ERROR_MALFORMED; } - if (!MakeAVCCodecSpecificData(trackInfo->mMeta, abuf)) { + if (!MakeAVCCodecSpecificData(trackInfo->mMeta, tmpData.get(), frame.len)) { return ERROR_MALFORMED; } diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index 4d49013ad0..b2fe69cd07 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -990,21 +991,11 @@ status_t MyOpusExtractor::verifyOpusHeader(MediaBufferBase *buffer) { return OK; } -struct TmpData { - uint8_t *data; - TmpData(size_t size) { - data = (uint8_t*) malloc(size); - } - ~TmpData() { - free(data); - } -}; - status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) { // add artificial framing bit so we can reuse _vorbis_unpack_comment int32_t commentSize = buffer->range_length() + 1; - TmpData commentDataHolder(commentSize); - uint8_t *commentData = commentDataHolder.data; + auto tmp = heapbuffer(commentSize); + uint8_t *commentData = tmp.get(); if (commentData == nullptr) { return ERROR_MALFORMED; } diff --git a/media/libmediaextractor/include/media/ExtractorUtils.h b/media/libmediaextractor/include/media/ExtractorUtils.h new file mode 100644 index 0000000000..22f9349272 --- /dev/null +++ b/media/libmediaextractor/include/media/ExtractorUtils.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef EXTRACTOR_UTILS_H_ + +#define EXTRACTOR_UTILS_H_ + +#include + +namespace android { + +template +std::unique_ptr heapbuffer(size_t size) { + return std::unique_ptr(new (std::nothrow) T[size]); +} + +} // namespace android + +#endif // UTILS_H_ diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp index af8f539e35..04f6adea4d 100644 --- a/media/libstagefright/MetaDataUtils.cpp +++ b/media/libstagefright/MetaDataUtils.cpp @@ -24,11 +24,12 @@ namespace android { -bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp &accessUnit) { +bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) { int32_t width; int32_t height; int32_t sarWidth; int32_t sarHeight; + sp accessUnit = new ABuffer((void*)data, size); sp csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight); if (csd == nullptr) { return false; diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp index ce40d6b8f6..ba8ef6d102 100644 --- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp +++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp @@ -48,6 +48,7 @@ C2SoftFlacDecoder::C2SoftFlacDecoder(const char *name, c2_node_id_t id) } C2SoftFlacDecoder::~C2SoftFlacDecoder() { + delete mFLACDecoder; } c2_status_t C2SoftFlacDecoder::onInit() { @@ -77,6 +78,9 @@ c2_status_t C2SoftFlacDecoder::onFlush_sm() { } status_t C2SoftFlacDecoder::initDecoder() { + if (mFLACDecoder) { + delete mFLACDecoder; + } mFLACDecoder = FLACDecoder::Create(); if (!mFLACDecoder) { ALOGE("initDecoder: failed to create FLACDecoder"); diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h index a5c01a9ef5..43d913b83c 100644 --- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h +++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h @@ -46,7 +46,7 @@ private: kMaxBlockSize = 4096 }; - sp mFLACDecoder; + FLACDecoder *mFLACDecoder; FLAC__StreamMetadata_StreamInfo mStreamInfo; bool mSignalledError; bool mSignalledOutputEos; diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp index 4ab1ab2b5a..d0b72b77d2 100644 --- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp @@ -57,6 +57,7 @@ SoftFlacDecoder::SoftFlacDecoder( SoftFlacDecoder::~SoftFlacDecoder() { ALOGV("dtor:"); + delete mFLACDecoder; } void SoftFlacDecoder::initPorts() { diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h index 4a21c3429a..0f17ed819b 100644 --- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h @@ -50,7 +50,7 @@ private: kNumOutputBuffers = 4, }; - sp mFLACDecoder; + FLACDecoder *mFLACDecoder; FLAC__StreamMetadata_StreamInfo mStreamInfo; bool mHasStreamInfo; size_t mInputBufferCount; diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp index 8c7137c3be..e0e9211629 100644 --- a/media/libstagefright/flac/dec/FLACDecoder.cpp +++ b/media/libstagefright/flac/dec/FLACDecoder.cpp @@ -220,9 +220,10 @@ static void copyMultiCh24( } // static -sp FLACDecoder::Create() { - sp decoder = new FLACDecoder(); - if (decoder->init() != OK) { +FLACDecoder *FLACDecoder::Create() { + FLACDecoder *decoder = new (std::nothrow) FLACDecoder(); + if (decoder == NULL || decoder->init() != OK) { + delete decoder; return NULL; } return decoder; diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h index 36282a8c66..1a33caee44 100644 --- a/media/libstagefright/flac/dec/FLACDecoder.h +++ b/media/libstagefright/flac/dec/FLACDecoder.h @@ -26,14 +26,14 @@ namespace android { // packet based FLAC decoder, wrapps libFLAC stream decoder. -class FLACDecoder : public RefBase { +class FLACDecoder { public: enum { kMaxChannels = 8, }; - static sp Create(); + static FLACDecoder *Create(); FLAC__StreamMetadata_StreamInfo getStreamInfo() const { return mStreamInfo; @@ -43,10 +43,10 @@ public: status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen, short *outBuffer, size_t *outBufferLen); void flush(); + virtual ~FLACDecoder(); protected: FLACDecoder(); - virtual ~FLACDecoder() override; private: // stream properties diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h index 3af2218663..d5a8080e63 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h +++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h @@ -23,7 +23,7 @@ namespace android { struct ABuffer; -bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp &accessUnit); +bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size); bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index, unsigned channel_configuration); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 0b7bd2600b..50b1bea8ee 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -634,7 +634,7 @@ sp ElementaryStreamQueue::dequeueAccessUnit() { if (mFormat == NULL) { mFormat = new MetaData; - if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) { + if (!MakeAVCCodecSpecificData(*mFormat, accessUnit->data(), accessUnit->size())) { mFormat.clear(); } } @@ -1010,7 +1010,7 @@ sp ElementaryStreamQueue::dequeueAccessUnitH264() { } if (mFormat == NULL) { mFormat = new MetaData; - if (!MakeAVCCodecSpecificData(*mFormat, mBuffer)) { + if (!MakeAVCCodecSpecificData(*mFormat, mBuffer->data(), mBuffer->size())) { ALOGW("Creating dummy AVC format for scrambled content"); mFormat = new MetaData; mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); @@ -1172,7 +1172,9 @@ sp ElementaryStreamQueue::dequeueAccessUnitH264() { if (mFormat == NULL) { mFormat = new MetaData; - if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) { + if (!MakeAVCCodecSpecificData(*mFormat, + accessUnit->data(), + accessUnit->size())) { mFormat.clear(); } } -- GitLab From 237dc91c546cf5e39737812f49460b552a529f7d Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 8 Mar 2018 13:54:19 -0800 Subject: [PATCH 0027/1530] heic content_encoding field is optional comply with the ISO spec that allows for content type fields to be omitted in the mp4 header if they would be otherwise empty. Bug: 74375003 Test: before/after of PoC Change-Id: I522bacf192fee86122f603d6e87506e1a004f3f6 --- media/extractors/mp4/ItemTable.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index cef5f4a756..d13f0dd631 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -1060,9 +1060,12 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { return ERROR_MALFORMED; } - String8 content_encoding; - if (!parseNullTerminatedString(&offset, &size, &content_encoding)) { - return ERROR_MALFORMED; + // content_encoding is optional; can be omitted if would be empty + if (size > 0) { + String8 content_encoding; + if (!parseNullTerminatedString(&offset, &size, &content_encoding)) { + return ERROR_MALFORMED; + } } } else if (item_type == FOURCC('u', 'r', 'i', ' ')) { String8 item_uri_type; -- GitLab From 8424c61ab28a6069b75ed681ad713a52eb0329c8 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 8 Mar 2018 14:58:40 -0800 Subject: [PATCH 0028/1530] stagefright: separate buffer implementations Buffers used by CCodec is separated out. Bug: 74403547 Test: builds Change-Id: I40664234d2a30261498f36ffa4869db495074d0b --- media/libstagefright/Android.bp | 1 + media/libstagefright/BufferImpl.cpp | 589 ------------------------ media/libstagefright/Codec2Buffer.cpp | 616 ++++++++++++++++++++++++++ 3 files changed, 617 insertions(+), 589 deletions(-) create mode 100644 media/libstagefright/Codec2Buffer.cpp diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 13d80f5e0f..6db70b1394 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -63,6 +63,7 @@ cc_library_shared { "C2OMXNode.cpp", "CCodec.cpp", "CCodecBufferChannel.cpp", + "Codec2Buffer.cpp", "Codec2InfoBuilder.cpp", "CodecBase.cpp", "CallbackDataSource.cpp", diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp index 7b3fa026d3..b760273455 100644 --- a/media/libstagefright/BufferImpl.cpp +++ b/media/libstagefright/BufferImpl.cpp @@ -24,7 +24,6 @@ #include #include -#include "include/Codec2Buffer.h" #include "include/SecureBuffer.h" #include "include/SharedMemoryBuffer.h" @@ -64,592 +63,4 @@ ICrypto::DestinationType SecureBuffer::getDestinationType() { return ICrypto::kDestinationTypeNativeHandle; } -// Codec2Buffer - -bool Codec2Buffer::canCopyLinear(const std::shared_ptr &buffer) const { - if (const_cast(this)->base() == nullptr) { - return false; - } - if (!buffer) { - // Nothing to copy, so we can copy by doing nothing. - return true; - } - if (buffer->data().type() != C2BufferData::LINEAR) { - return false; - } - if (buffer->data().linearBlocks().size() == 0u) { - // Nothing to copy, so we can copy by doing nothing. - return true; - } else if (buffer->data().linearBlocks().size() > 1u) { - // We don't know how to copy more than one blocks. - return false; - } - if (buffer->data().linearBlocks()[0].size() > capacity()) { - // It won't fit. - return false; - } - return true; -} - -bool Codec2Buffer::copyLinear(const std::shared_ptr &buffer) { - // We assume that all canCopyLinear() checks passed. - if (!buffer || buffer->data().linearBlocks().size() == 0u) { - setRange(0, 0); - return true; - } - C2ReadView view = buffer->data().linearBlocks()[0].map().get(); - if (view.error() != C2_OK) { - ALOGD("Error while mapping: %d", view.error()); - return false; - } - if (view.capacity() > capacity()) { - ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)", - view.capacity(), capacity()); - return false; - } - memcpy(base(), view.data(), view.capacity()); - setRange(0, view.capacity()); - return true; -} - -// LocalLinearBuffer - -bool LocalLinearBuffer::canCopy(const std::shared_ptr &buffer) const { - return canCopyLinear(buffer); -} - -bool LocalLinearBuffer::copy(const std::shared_ptr &buffer) { - return copyLinear(buffer); -} - -// DummyContainerBuffer - -DummyContainerBuffer::DummyContainerBuffer( - const sp &format, const std::shared_ptr &buffer) - : Codec2Buffer(format, new ABuffer(nullptr, 1)), - mBufferRef(buffer) { - setRange(0, buffer ? 1 : 0); -} - -std::shared_ptr DummyContainerBuffer::asC2Buffer() { - return std::move(mBufferRef); -} - -bool DummyContainerBuffer::canCopy(const std::shared_ptr &) const { - return !mBufferRef; -} - -bool DummyContainerBuffer::copy(const std::shared_ptr &buffer) { - mBufferRef = buffer; - setRange(0, mBufferRef ? 1 : 0); - return true; -} - -// LinearBlockBuffer - -// static -sp LinearBlockBuffer::Allocate( - const sp &format, const std::shared_ptr &block) { - C2WriteView writeView(block->map().get()); - if (writeView.error() != C2_OK) { - return nullptr; - } - return new LinearBlockBuffer(format, std::move(writeView), block); -} - -std::shared_ptr LinearBlockBuffer::asC2Buffer() { - return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence())); -} - -bool LinearBlockBuffer::canCopy(const std::shared_ptr &buffer) const { - return canCopyLinear(buffer); -} - -bool LinearBlockBuffer::copy(const std::shared_ptr &buffer) { - return copyLinear(buffer); -} - -LinearBlockBuffer::LinearBlockBuffer( - const sp &format, - C2WriteView&& writeView, - const std::shared_ptr &block) - : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())), - mWriteView(writeView), - mBlock(block) { -} - -// ConstLinearBlockBuffer - -// static -sp ConstLinearBlockBuffer::Allocate( - const sp &format, const std::shared_ptr &buffer) { - if (!buffer - || buffer->data().type() != C2BufferData::LINEAR - || buffer->data().linearBlocks().size() != 1u) { - return nullptr; - } - C2ReadView readView(buffer->data().linearBlocks()[0].map().get()); - if (readView.error() != C2_OK) { - return nullptr; - } - return new ConstLinearBlockBuffer(format, std::move(readView), buffer); -} - -ConstLinearBlockBuffer::ConstLinearBlockBuffer( - const sp &format, - C2ReadView&& readView, - const std::shared_ptr &buffer) - : Codec2Buffer(format, new ABuffer( - // NOTE: ABuffer only takes non-const pointer but this data is - // supposed to be read-only. - const_cast(readView.data()), readView.capacity())), - mReadView(readView), - mBufferRef(buffer) { -} - -std::shared_ptr ConstLinearBlockBuffer::asC2Buffer() { - return std::move(mBufferRef); -} - -// GraphicView2MediaImageConverter - -namespace { - -class GraphicView2MediaImageConverter { -public: - explicit GraphicView2MediaImageConverter(const C2GraphicView &view) - : mInitCheck(NO_INIT), - mView(view), - mWidth(view.width()), - mHeight(view.height()), - mAllocatedDepth(0), - mBackBufferSize(0), - mMediaImage(new ABuffer(sizeof(MediaImage2))) { - if (view.error() != C2_OK) { - ALOGD("Converter: view.error() = %d", view.error()); - mInitCheck = BAD_VALUE; - return; - } - MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base(); - const C2PlanarLayout &layout = view.layout(); - if (layout.numPlanes == 0) { - ALOGD("Converter: 0 planes"); - mInitCheck = BAD_VALUE; - return; - } - mAllocatedDepth = layout.planes[0].allocatedDepth; - uint32_t bitDepth = layout.planes[0].bitDepth; - - switch (layout.type) { - case C2PlanarLayout::TYPE_YUV: - mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break; - case C2PlanarLayout::TYPE_YUVA: - mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break; - case C2PlanarLayout::TYPE_RGB: - mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break; - case C2PlanarLayout::TYPE_RGBA: - mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break; - default: - mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break; - } - mediaImage->mNumPlanes = layout.numPlanes; - mediaImage->mWidth = mWidth; - mediaImage->mHeight = mHeight; - mediaImage->mBitDepth = bitDepth; - mediaImage->mBitDepthAllocated = mAllocatedDepth; - - uint32_t bufferSize = 0; - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - if (plane.rightShift != 0) { - ALOGV("rightShift value of %u unsupported", plane.rightShift); - mInitCheck = BAD_VALUE; - return; - } - if (plane.endianness != C2PlaneInfo::NATIVE) { - ALOGV("endianness value of %u unsupported", plane.endianness); - mInitCheck = BAD_VALUE; - return; - } - if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) { - ALOGV("different allocatedDepth/bitDepth per plane unsupported"); - mInitCheck = BAD_VALUE; - return; - } - bufferSize += mWidth * mHeight - / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8); - } - - mBackBufferSize = bufferSize; - mInitCheck = OK; - } - - status_t initCheck() const { return mInitCheck; } - - uint32_t backBufferSize() const { return mBackBufferSize; } - - /** - * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content - * is not copied over in this function --- the caller should use - * CopyGraphicView2MediaImage() function to do that explicitly. - * - * \param view[in] source C2GraphicView object. - * \param alloc[in] allocator function for ABuffer. - * \param mediaImage[out] destination MediaImage2 object. - * \param buffer[out] new buffer object. - * \param wrapped[out] whether we wrapped around existing map or - * allocated a new buffer - * - * \return true if conversion succeeds, - * false otherwise; all output params should be ignored. - */ - sp wrap() { - MediaImage2 *mediaImage = getMediaImage(); - const C2PlanarLayout &layout = mView.layout(); - if (layout.numPlanes == 1) { - const C2PlaneInfo &plane = layout.planes[0]; - ssize_t offset = plane.minOffset(mWidth, mHeight); - mediaImage->mPlane[0].mOffset = -offset; - mediaImage->mPlane[0].mColInc = plane.colInc; - mediaImage->mPlane[0].mRowInc = plane.rowInc; - mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling; - mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling; - return new ABuffer( - const_cast(mView.data()[0] + offset), - plane.maxOffset(mWidth, mHeight) - offset + 1); - } - const uint8_t *minPtr = mView.data()[0]; - const uint8_t *maxPtr = mView.data()[0]; - int32_t planeSize = 0; - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - ssize_t minOffset = plane.minOffset(mWidth, mHeight); - ssize_t maxOffset = plane.maxOffset(mWidth, mHeight); - if (minPtr > mView.data()[i] + minOffset) { - minPtr = mView.data()[i] + minOffset; - } - if (maxPtr < mView.data()[i] + maxOffset) { - maxPtr = mView.data()[i] + maxOffset; - } - planeSize += std::abs(plane.rowInc) * mHeight - / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8); - } - - if ((maxPtr - minPtr + 1) <= planeSize) { - // FIXME: this is risky as reading/writing data out of bound results in - // an undefined behavior. - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr; - mediaImage->mPlane[i].mColInc = plane.colInc; - mediaImage->mPlane[i].mRowInc = plane.rowInc; - mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; - mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; - } - return new ABuffer(const_cast(minPtr), maxPtr - minPtr + 1); - } - - return nullptr; - } - - bool setBackBuffer(const sp &backBuffer) { - if (backBuffer->capacity() < mBackBufferSize) { - return false; - } - backBuffer->setRange(0, mBackBufferSize); - - const C2PlanarLayout &layout = mView.layout(); - MediaImage2 *mediaImage = getMediaImage(); - uint32_t offset = 0; - // TODO: keep interleaved planes together - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - mediaImage->mPlane[i].mOffset = offset; - mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8; - mediaImage->mPlane[i].mRowInc = - mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling; - mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; - mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; - offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling; - } - mBackBuffer = backBuffer; - return true; - } - - /** - * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is - * an output from GraphicView2MediaImage(), so it mostly skips sanity check. - * - * \param view[in] source C2GraphicView object. - * \param mediaImage[in] destination MediaImage2 object. - * \param buffer[out] new buffer object. - */ - void copy() { - // TODO: more efficient copying --- e.g. one row at a time, copying - // interleaved planes together, etc. - const C2PlanarLayout &layout = mView.layout(); - MediaImage2 *mediaImage = getMediaImage(); - uint8_t *dst = mBackBuffer->base(); - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - const uint8_t *src = mView.data()[i]; - int32_t planeW = mWidth / plane.colSampling; - int32_t planeH = mHeight / plane.rowSampling; - for (int32_t row = 0; row < planeH; ++row) { - for(int32_t col = 0; col < planeW; ++col) { - memcpy(dst, src, mAllocatedDepth / 8); - dst += mediaImage->mPlane[i].mColInc; - src += plane.colInc; - } - dst -= mediaImage->mPlane[i].mColInc * planeW; - dst += mediaImage->mPlane[i].mRowInc; - src -= plane.colInc * planeW; - src += plane.rowInc; - } - } - } - - const sp &imageData() const { return mMediaImage; } - -private: - status_t mInitCheck; - - const C2GraphicView mView; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mAllocatedDepth; - uint32_t mBackBufferSize; - sp mMediaImage; - std::function(size_t)> mAlloc; - - sp mBackBuffer; - - MediaImage2 *getMediaImage() { - return (MediaImage2 *)mMediaImage->base(); - } -}; - -} // namespace - -// GraphicBlockBuffer - -// static -sp GraphicBlockBuffer::Allocate( - const sp &format, - const std::shared_ptr &block, - std::function(size_t)> alloc) { - C2GraphicView view(block->map().get()); - if (view.error() != C2_OK) { - ALOGD("C2GraphicBlock::map failed: %d", view.error()); - return nullptr; - } - GraphicView2MediaImageConverter converter(view); - if (converter.initCheck() != OK) { - ALOGD("Converter init failed: %d", converter.initCheck()); - return nullptr; - } - bool wrapped = true; - sp buffer = converter.wrap(); - if (buffer == nullptr) { - buffer = alloc(converter.backBufferSize()); - if (!converter.setBackBuffer(buffer)) { - ALOGD("Converter failed to set back buffer"); - return nullptr; - } - wrapped = false; - } - return new GraphicBlockBuffer( - format, - buffer, - std::move(view), - block, - converter.imageData(), - wrapped); -} - -GraphicBlockBuffer::GraphicBlockBuffer( - const sp &format, - const sp &buffer, - C2GraphicView &&view, - const std::shared_ptr &block, - const sp &imageData, - bool wrapped) - : Codec2Buffer(format, buffer), - mView(view), - mBlock(block), - mImageData(imageData), - mWrapped(wrapped) { - meta()->setBuffer("image-data", imageData); -} - -std::shared_ptr GraphicBlockBuffer::asC2Buffer() { - uint32_t width = mView.width(); - uint32_t height = mView.height(); - if (!mWrapped) { - MediaImage2 *mediaImage = imageData(); - const C2PlanarLayout &layout = mView.layout(); - for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - int32_t planeW = width / plane.colSampling; - int32_t planeH = height / plane.rowSampling; - const uint8_t *src = base() + mediaImage->mPlane[i].mOffset; - uint8_t *dst = mView.data()[i]; - for (int32_t row = 0; row < planeH; ++row) { - for (int32_t col = 0; col < planeW; ++col) { - memcpy(dst, src, mediaImage->mBitDepthAllocated / 8); - src += mediaImage->mPlane[i].mColInc; - dst += plane.colInc; - } - src -= mediaImage->mPlane[i].mColInc * planeW; - dst -= plane.colInc * planeW; - src += mediaImage->mPlane[i].mRowInc; - dst += plane.rowInc; - } - } - } - return C2Buffer::CreateGraphicBuffer( - mBlock->share(C2Rect(width, height), C2Fence())); -} - -// ConstGraphicBlockBuffer - -// static -sp ConstGraphicBlockBuffer::Allocate( - const sp &format, - const std::shared_ptr &buffer, - std::function(size_t)> alloc) { - if (!buffer - || buffer->data().type() != C2BufferData::GRAPHIC - || buffer->data().graphicBlocks().size() != 1u) { - ALOGD("C2Buffer precond fail"); - return nullptr; - } - std::unique_ptr view(std::make_unique( - buffer->data().graphicBlocks()[0].map().get())); - std::unique_ptr holder; - - GraphicView2MediaImageConverter converter(*view); - if (converter.initCheck() != OK) { - ALOGD("Converter init failed: %d", converter.initCheck()); - return nullptr; - } - bool wrapped = true; - sp aBuffer = converter.wrap(); - if (aBuffer == nullptr) { - aBuffer = alloc(converter.backBufferSize()); - if (!converter.setBackBuffer(aBuffer)) { - ALOGD("Converter failed to set back buffer"); - return nullptr; - } - wrapped = false; - converter.copy(); - // We don't need the view. - holder = std::move(view); - } - return new ConstGraphicBlockBuffer( - format, - aBuffer, - std::move(view), - buffer, - converter.imageData(), - wrapped); -} - -// static -sp ConstGraphicBlockBuffer::AllocateEmpty( - const sp &format, - std::function(size_t)> alloc) { - int32_t width, height; - if (!format->findInt32("width", &width) - || !format->findInt32("height", &height)) { - ALOGD("format had no width / height"); - return nullptr; - } - sp aBuffer(alloc(width * height * 4)); - return new ConstGraphicBlockBuffer( - format, - aBuffer, - nullptr, - nullptr, - nullptr, - false); -} - -ConstGraphicBlockBuffer::ConstGraphicBlockBuffer( - const sp &format, - const sp &aBuffer, - std::unique_ptr &&view, - const std::shared_ptr &buffer, - const sp &imageData, - bool wrapped) - : Codec2Buffer(format, aBuffer), - mView(std::move(view)), - mBufferRef(buffer), - mWrapped(wrapped) { - if (imageData != nullptr) { - meta()->setBuffer("image-data", imageData); - } -} - -std::shared_ptr ConstGraphicBlockBuffer::asC2Buffer() { - mView.reset(); - return std::move(mBufferRef); -} - -bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr &buffer) const { - if (mWrapped || mBufferRef) { - ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s", - mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist"); - return false; - } - if (!buffer) { - // Nothing to copy, so we can copy by doing nothing. - return true; - } - if (buffer->data().type() != C2BufferData::GRAPHIC) { - ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied"); - return false; - } - if (buffer->data().graphicBlocks().size() == 0) { - return true; - } else if (buffer->data().graphicBlocks().size() != 1u) { - ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks"); - return false; - } - GraphicView2MediaImageConverter converter( - buffer->data().graphicBlocks()[0].map().get()); - if (converter.initCheck() != OK) { - ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck()); - return false; - } - if (converter.backBufferSize() > capacity()) { - ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu", - converter.backBufferSize(), capacity()); - return false; - } - return true; -} - -bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { - if (!buffer || buffer->data().graphicBlocks().size() == 0) { - setRange(0, 0); - return true; - } - GraphicView2MediaImageConverter converter( - buffer->data().graphicBlocks()[0].map().get()); - if (converter.initCheck() != OK) { - ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck()); - return false; - } - sp aBuffer = new ABuffer(base(), capacity()); - if (!converter.setBackBuffer(aBuffer)) { - ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed"); - return false; - } - converter.copy(); - meta()->setBuffer("image-data", converter.imageData()); - mBufferRef = buffer; - return true; -} - } // namespace android diff --git a/media/libstagefright/Codec2Buffer.cpp b/media/libstagefright/Codec2Buffer.cpp new file mode 100644 index 0000000000..c664f2ca3c --- /dev/null +++ b/media/libstagefright/Codec2Buffer.cpp @@ -0,0 +1,616 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2Buffer" +#include + +#include +#include + +#include "include/Codec2Buffer.h" + +namespace android { + +// Codec2Buffer + +bool Codec2Buffer::canCopyLinear(const std::shared_ptr &buffer) const { + if (const_cast(this)->base() == nullptr) { + return false; + } + if (!buffer) { + // Nothing to copy, so we can copy by doing nothing. + return true; + } + if (buffer->data().type() != C2BufferData::LINEAR) { + return false; + } + if (buffer->data().linearBlocks().size() == 0u) { + // Nothing to copy, so we can copy by doing nothing. + return true; + } else if (buffer->data().linearBlocks().size() > 1u) { + // We don't know how to copy more than one blocks. + return false; + } + if (buffer->data().linearBlocks()[0].size() > capacity()) { + // It won't fit. + return false; + } + return true; +} + +bool Codec2Buffer::copyLinear(const std::shared_ptr &buffer) { + // We assume that all canCopyLinear() checks passed. + if (!buffer || buffer->data().linearBlocks().size() == 0u) { + setRange(0, 0); + return true; + } + C2ReadView view = buffer->data().linearBlocks()[0].map().get(); + if (view.error() != C2_OK) { + ALOGD("Error while mapping: %d", view.error()); + return false; + } + if (view.capacity() > capacity()) { + ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)", + view.capacity(), capacity()); + return false; + } + memcpy(base(), view.data(), view.capacity()); + setRange(0, view.capacity()); + return true; +} + +// LocalLinearBuffer + +bool LocalLinearBuffer::canCopy(const std::shared_ptr &buffer) const { + return canCopyLinear(buffer); +} + +bool LocalLinearBuffer::copy(const std::shared_ptr &buffer) { + return copyLinear(buffer); +} + +// DummyContainerBuffer + +DummyContainerBuffer::DummyContainerBuffer( + const sp &format, const std::shared_ptr &buffer) + : Codec2Buffer(format, new ABuffer(nullptr, 1)), + mBufferRef(buffer) { + setRange(0, buffer ? 1 : 0); +} + +std::shared_ptr DummyContainerBuffer::asC2Buffer() { + return std::move(mBufferRef); +} + +bool DummyContainerBuffer::canCopy(const std::shared_ptr &) const { + return !mBufferRef; +} + +bool DummyContainerBuffer::copy(const std::shared_ptr &buffer) { + mBufferRef = buffer; + setRange(0, mBufferRef ? 1 : 0); + return true; +} + +// LinearBlockBuffer + +// static +sp LinearBlockBuffer::Allocate( + const sp &format, const std::shared_ptr &block) { + C2WriteView writeView(block->map().get()); + if (writeView.error() != C2_OK) { + return nullptr; + } + return new LinearBlockBuffer(format, std::move(writeView), block); +} + +std::shared_ptr LinearBlockBuffer::asC2Buffer() { + return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence())); +} + +bool LinearBlockBuffer::canCopy(const std::shared_ptr &buffer) const { + return canCopyLinear(buffer); +} + +bool LinearBlockBuffer::copy(const std::shared_ptr &buffer) { + return copyLinear(buffer); +} + +LinearBlockBuffer::LinearBlockBuffer( + const sp &format, + C2WriteView&& writeView, + const std::shared_ptr &block) + : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())), + mWriteView(writeView), + mBlock(block) { +} + +// ConstLinearBlockBuffer + +// static +sp ConstLinearBlockBuffer::Allocate( + const sp &format, const std::shared_ptr &buffer) { + if (!buffer + || buffer->data().type() != C2BufferData::LINEAR + || buffer->data().linearBlocks().size() != 1u) { + return nullptr; + } + C2ReadView readView(buffer->data().linearBlocks()[0].map().get()); + if (readView.error() != C2_OK) { + return nullptr; + } + return new ConstLinearBlockBuffer(format, std::move(readView), buffer); +} + +ConstLinearBlockBuffer::ConstLinearBlockBuffer( + const sp &format, + C2ReadView&& readView, + const std::shared_ptr &buffer) + : Codec2Buffer(format, new ABuffer( + // NOTE: ABuffer only takes non-const pointer but this data is + // supposed to be read-only. + const_cast(readView.data()), readView.capacity())), + mReadView(readView), + mBufferRef(buffer) { +} + +std::shared_ptr ConstLinearBlockBuffer::asC2Buffer() { + return std::move(mBufferRef); +} + +// GraphicView2MediaImageConverter + +namespace { + +class GraphicView2MediaImageConverter { +public: + explicit GraphicView2MediaImageConverter(const C2GraphicView &view) + : mInitCheck(NO_INIT), + mView(view), + mWidth(view.width()), + mHeight(view.height()), + mAllocatedDepth(0), + mBackBufferSize(0), + mMediaImage(new ABuffer(sizeof(MediaImage2))) { + if (view.error() != C2_OK) { + ALOGD("Converter: view.error() = %d", view.error()); + mInitCheck = BAD_VALUE; + return; + } + MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base(); + const C2PlanarLayout &layout = view.layout(); + if (layout.numPlanes == 0) { + ALOGD("Converter: 0 planes"); + mInitCheck = BAD_VALUE; + return; + } + mAllocatedDepth = layout.planes[0].allocatedDepth; + uint32_t bitDepth = layout.planes[0].bitDepth; + + switch (layout.type) { + case C2PlanarLayout::TYPE_YUV: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break; + case C2PlanarLayout::TYPE_YUVA: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break; + case C2PlanarLayout::TYPE_RGB: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break; + case C2PlanarLayout::TYPE_RGBA: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break; + default: + mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break; + } + mediaImage->mNumPlanes = layout.numPlanes; + mediaImage->mWidth = mWidth; + mediaImage->mHeight = mHeight; + mediaImage->mBitDepth = bitDepth; + mediaImage->mBitDepthAllocated = mAllocatedDepth; + + uint32_t bufferSize = 0; + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + if (plane.rightShift != 0) { + ALOGV("rightShift value of %u unsupported", plane.rightShift); + mInitCheck = BAD_VALUE; + return; + } + if (plane.endianness != C2PlaneInfo::NATIVE) { + ALOGV("endianness value of %u unsupported", plane.endianness); + mInitCheck = BAD_VALUE; + return; + } + if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) { + ALOGV("different allocatedDepth/bitDepth per plane unsupported"); + mInitCheck = BAD_VALUE; + return; + } + bufferSize += mWidth * mHeight + / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8); + } + + mBackBufferSize = bufferSize; + mInitCheck = OK; + } + + status_t initCheck() const { return mInitCheck; } + + uint32_t backBufferSize() const { return mBackBufferSize; } + + /** + * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content + * is not copied over in this function --- the caller should use + * CopyGraphicView2MediaImage() function to do that explicitly. + * + * \param view[in] source C2GraphicView object. + * \param alloc[in] allocator function for ABuffer. + * \param mediaImage[out] destination MediaImage2 object. + * \param buffer[out] new buffer object. + * \param wrapped[out] whether we wrapped around existing map or + * allocated a new buffer + * + * \return true if conversion succeeds, + * false otherwise; all output params should be ignored. + */ + sp wrap() { + MediaImage2 *mediaImage = getMediaImage(); + const C2PlanarLayout &layout = mView.layout(); + if (layout.numPlanes == 1) { + const C2PlaneInfo &plane = layout.planes[0]; + ssize_t offset = plane.minOffset(mWidth, mHeight); + mediaImage->mPlane[0].mOffset = -offset; + mediaImage->mPlane[0].mColInc = plane.colInc; + mediaImage->mPlane[0].mRowInc = plane.rowInc; + mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling; + return new ABuffer( + const_cast(mView.data()[0] + offset), + plane.maxOffset(mWidth, mHeight) - offset + 1); + } + const uint8_t *minPtr = mView.data()[0]; + const uint8_t *maxPtr = mView.data()[0]; + int32_t planeSize = 0; + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + ssize_t minOffset = plane.minOffset(mWidth, mHeight); + ssize_t maxOffset = plane.maxOffset(mWidth, mHeight); + if (minPtr > mView.data()[i] + minOffset) { + minPtr = mView.data()[i] + minOffset; + } + if (maxPtr < mView.data()[i] + maxOffset) { + maxPtr = mView.data()[i] + maxOffset; + } + planeSize += std::abs(plane.rowInc) * mHeight + / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8); + } + + if ((maxPtr - minPtr + 1) <= planeSize) { + // FIXME: this is risky as reading/writing data out of bound results in + // an undefined behavior. + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr; + mediaImage->mPlane[i].mColInc = plane.colInc; + mediaImage->mPlane[i].mRowInc = plane.rowInc; + mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + } + return new ABuffer(const_cast(minPtr), maxPtr - minPtr + 1); + } + + return nullptr; + } + + bool setBackBuffer(const sp &backBuffer) { + if (backBuffer->capacity() < mBackBufferSize) { + return false; + } + backBuffer->setRange(0, mBackBufferSize); + + const C2PlanarLayout &layout = mView.layout(); + MediaImage2 *mediaImage = getMediaImage(); + uint32_t offset = 0; + // TODO: keep interleaved planes together + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + mediaImage->mPlane[i].mOffset = offset; + mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8; + mediaImage->mPlane[i].mRowInc = + mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling; + mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling; + } + mBackBuffer = backBuffer; + return true; + } + + /** + * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is + * an output from GraphicView2MediaImage(), so it mostly skips sanity check. + * + * \param view[in] source C2GraphicView object. + * \param mediaImage[in] destination MediaImage2 object. + * \param buffer[out] new buffer object. + */ + void copy() { + // TODO: more efficient copying --- e.g. one row at a time, copying + // interleaved planes together, etc. + const C2PlanarLayout &layout = mView.layout(); + MediaImage2 *mediaImage = getMediaImage(); + uint8_t *dst = mBackBuffer->base(); + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + const uint8_t *src = mView.data()[i]; + int32_t planeW = mWidth / plane.colSampling; + int32_t planeH = mHeight / plane.rowSampling; + for (int32_t row = 0; row < planeH; ++row) { + for(int32_t col = 0; col < planeW; ++col) { + memcpy(dst, src, mAllocatedDepth / 8); + dst += mediaImage->mPlane[i].mColInc; + src += plane.colInc; + } + dst -= mediaImage->mPlane[i].mColInc * planeW; + dst += mediaImage->mPlane[i].mRowInc; + src -= plane.colInc * planeW; + src += plane.rowInc; + } + } + } + + const sp &imageData() const { return mMediaImage; } + +private: + status_t mInitCheck; + + const C2GraphicView mView; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mAllocatedDepth; + uint32_t mBackBufferSize; + sp mMediaImage; + std::function(size_t)> mAlloc; + + sp mBackBuffer; + + MediaImage2 *getMediaImage() { + return (MediaImage2 *)mMediaImage->base(); + } +}; + +} // namespace + +// GraphicBlockBuffer + +// static +sp GraphicBlockBuffer::Allocate( + const sp &format, + const std::shared_ptr &block, + std::function(size_t)> alloc) { + C2GraphicView view(block->map().get()); + if (view.error() != C2_OK) { + ALOGD("C2GraphicBlock::map failed: %d", view.error()); + return nullptr; + } + GraphicView2MediaImageConverter converter(view); + if (converter.initCheck() != OK) { + ALOGD("Converter init failed: %d", converter.initCheck()); + return nullptr; + } + bool wrapped = true; + sp buffer = converter.wrap(); + if (buffer == nullptr) { + buffer = alloc(converter.backBufferSize()); + if (!converter.setBackBuffer(buffer)) { + ALOGD("Converter failed to set back buffer"); + return nullptr; + } + wrapped = false; + } + return new GraphicBlockBuffer( + format, + buffer, + std::move(view), + block, + converter.imageData(), + wrapped); +} + +GraphicBlockBuffer::GraphicBlockBuffer( + const sp &format, + const sp &buffer, + C2GraphicView &&view, + const std::shared_ptr &block, + const sp &imageData, + bool wrapped) + : Codec2Buffer(format, buffer), + mView(view), + mBlock(block), + mImageData(imageData), + mWrapped(wrapped) { + meta()->setBuffer("image-data", imageData); +} + +std::shared_ptr GraphicBlockBuffer::asC2Buffer() { + uint32_t width = mView.width(); + uint32_t height = mView.height(); + if (!mWrapped) { + MediaImage2 *mediaImage = imageData(); + const C2PlanarLayout &layout = mView.layout(); + for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + int32_t planeW = width / plane.colSampling; + int32_t planeH = height / plane.rowSampling; + const uint8_t *src = base() + mediaImage->mPlane[i].mOffset; + uint8_t *dst = mView.data()[i]; + for (int32_t row = 0; row < planeH; ++row) { + for (int32_t col = 0; col < planeW; ++col) { + memcpy(dst, src, mediaImage->mBitDepthAllocated / 8); + src += mediaImage->mPlane[i].mColInc; + dst += plane.colInc; + } + src -= mediaImage->mPlane[i].mColInc * planeW; + dst -= plane.colInc * planeW; + src += mediaImage->mPlane[i].mRowInc; + dst += plane.rowInc; + } + } + } + return C2Buffer::CreateGraphicBuffer( + mBlock->share(C2Rect(width, height), C2Fence())); +} + +// ConstGraphicBlockBuffer + +// static +sp ConstGraphicBlockBuffer::Allocate( + const sp &format, + const std::shared_ptr &buffer, + std::function(size_t)> alloc) { + if (!buffer + || buffer->data().type() != C2BufferData::GRAPHIC + || buffer->data().graphicBlocks().size() != 1u) { + ALOGD("C2Buffer precond fail"); + return nullptr; + } + std::unique_ptr view(std::make_unique( + buffer->data().graphicBlocks()[0].map().get())); + std::unique_ptr holder; + + GraphicView2MediaImageConverter converter(*view); + if (converter.initCheck() != OK) { + ALOGD("Converter init failed: %d", converter.initCheck()); + return nullptr; + } + bool wrapped = true; + sp aBuffer = converter.wrap(); + if (aBuffer == nullptr) { + aBuffer = alloc(converter.backBufferSize()); + if (!converter.setBackBuffer(aBuffer)) { + ALOGD("Converter failed to set back buffer"); + return nullptr; + } + wrapped = false; + converter.copy(); + // We don't need the view. + holder = std::move(view); + } + return new ConstGraphicBlockBuffer( + format, + aBuffer, + std::move(view), + buffer, + converter.imageData(), + wrapped); +} + +// static +sp ConstGraphicBlockBuffer::AllocateEmpty( + const sp &format, + std::function(size_t)> alloc) { + int32_t width, height; + if (!format->findInt32("width", &width) + || !format->findInt32("height", &height)) { + ALOGD("format had no width / height"); + return nullptr; + } + sp aBuffer(alloc(width * height * 4)); + return new ConstGraphicBlockBuffer( + format, + aBuffer, + nullptr, + nullptr, + nullptr, + false); +} + +ConstGraphicBlockBuffer::ConstGraphicBlockBuffer( + const sp &format, + const sp &aBuffer, + std::unique_ptr &&view, + const std::shared_ptr &buffer, + const sp &imageData, + bool wrapped) + : Codec2Buffer(format, aBuffer), + mView(std::move(view)), + mBufferRef(buffer), + mWrapped(wrapped) { + if (imageData != nullptr) { + meta()->setBuffer("image-data", imageData); + } +} + +std::shared_ptr ConstGraphicBlockBuffer::asC2Buffer() { + mView.reset(); + return std::move(mBufferRef); +} + +bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr &buffer) const { + if (mWrapped || mBufferRef) { + ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s", + mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist"); + return false; + } + if (!buffer) { + // Nothing to copy, so we can copy by doing nothing. + return true; + } + if (buffer->data().type() != C2BufferData::GRAPHIC) { + ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied"); + return false; + } + if (buffer->data().graphicBlocks().size() == 0) { + return true; + } else if (buffer->data().graphicBlocks().size() != 1u) { + ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks"); + return false; + } + GraphicView2MediaImageConverter converter( + buffer->data().graphicBlocks()[0].map().get()); + if (converter.initCheck() != OK) { + ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck()); + return false; + } + if (converter.backBufferSize() > capacity()) { + ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu", + converter.backBufferSize(), capacity()); + return false; + } + return true; +} + +bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { + if (!buffer || buffer->data().graphicBlocks().size() == 0) { + setRange(0, 0); + return true; + } + GraphicView2MediaImageConverter converter( + buffer->data().graphicBlocks()[0].map().get()); + if (converter.initCheck() != OK) { + ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck()); + return false; + } + sp aBuffer = new ABuffer(base(), capacity()); + if (!converter.setBackBuffer(aBuffer)) { + ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed"); + return false; + } + converter.copy(); + meta()->setBuffer("image-data", converter.imageData()); + mBufferRef = buffer; + return true; +} + +} // namespace android -- GitLab From ae90834eec35fffb395d7244833f3fae107202fe Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 6 Mar 2018 15:29:33 -0800 Subject: [PATCH 0029/1530] CCodec: graphic C2Buffer lifecycle management hacks Bug: 74077399 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell stagefright -S -N c2.google.avc.decoder /sdcard/a.mp4 Change-Id: I57d86f78569bf743a5d5ffd08c48559f6aa12e76 --- media/libstagefright/CCodecBufferChannel.cpp | 38 +++++++++++++------ .../include/CCodecBufferChannel.h | 8 +++- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index 65d637bb89..438bc31dad 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -181,6 +181,7 @@ namespace { // TODO: get this info from component const static size_t kMinBufferArraySize = 16; const static size_t kLinearBufferSize = 524288; +const static size_t kMaxGraphicBufferRefCount = 4; /** * Simple local buffer pool backed by std::vector. @@ -1061,8 +1062,8 @@ status_t CCodecBufferChannel::renderOutputBuffer( c2Buffer = (*buffers)->releaseBuffer(buffer); } - Mutexed>::Locked surface(mSurface); - if (*surface == nullptr) { + Mutexed::Locked output(mOutputSurface); + if (output->surface == nullptr) { ALOGE("no surface"); return OK; } @@ -1087,7 +1088,7 @@ status_t CCodecBufferChannel::renderOutputBuffer( blocks.front().width())); native_handle_delete(grallocHandle); - status_t result = (*surface)->attachBuffer(graphicBuffer.get()); + status_t result = output->surface->attachBuffer(graphicBuffer.get()); if (result != OK) { ALOGE("attachBuffer failed: %d", result); return result; @@ -1095,23 +1096,32 @@ status_t CCodecBufferChannel::renderOutputBuffer( // TODO: read and set crop - result = native_window_set_buffers_timestamp((*surface).get(), timestampNs); + result = native_window_set_buffers_timestamp(output->surface.get(), timestampNs); ALOGW_IF(result != OK, "failed to set buffer timestamp: %d", result); // TODO: fix after C2Fence implementation #if 0 const C2Fence &fence = blocks.front().fence(); - result = ((ANativeWindow *)(*surface).get())->queueBuffer( - (*surface).get(), graphicBuffer.get(), fence.valid() ? fence.fd() : -1); + result = ((ANativeWindow *)output->surface.get())->queueBuffer( + output->surface.get(), graphicBuffer.get(), fence.valid() ? fence.fd() : -1); #else - result = ((ANativeWindow *)(*surface).get())->queueBuffer( - (*surface).get(), graphicBuffer.get(), -1); + result = ((ANativeWindow *)output->surface.get())->queueBuffer( + output->surface.get(), graphicBuffer.get(), -1); #endif if (result != OK) { ALOGE("queueBuffer failed: %d", result); return result; } + // XXX: Hack to keep C2Buffers unreleased until the consumer is done + // reading the content. Eventually IGBP-based C2BlockPool should handle + // the lifecycle. + output->bufferRefs.push_back(c2Buffer); + if (output->bufferRefs.size() > output->maxBufferCount + 1) { + output->bufferRefs.pop_front(); + ALOGV("%zu buffer refs remaining", output->bufferRefs.size()); + } + return OK; } @@ -1200,8 +1210,8 @@ status_t CCodecBufferChannel::start( if (outputFormat != nullptr) { bool hasOutputSurface = false; { - Mutexed>::Locked surface(mSurface); - hasOutputSurface = (*surface != nullptr); + Mutexed::Locked output(mOutputSurface); + hasOutputSurface = (output->surface != nullptr); } Mutexed>::Locked buffers(mOutputBuffers); @@ -1381,7 +1391,7 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); } - Mutexed>::Locked surface(mSurface); + Mutexed::Locked output(mOutputSurface); // if (newSurface == nullptr) { // if (*surface != nullptr) { // ALOGW("cannot unset a surface"); @@ -1395,7 +1405,11 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { // return INVALID_OPERATION; // } - *surface = newSurface; + output->surface = newSurface; + output->bufferRefs.clear(); + // XXX: hack + output->maxBufferCount = kMaxGraphicBufferRefCount; + return OK; } diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h index 51eee105cf..0017541758 100644 --- a/media/libstagefright/include/CCodecBufferChannel.h +++ b/media/libstagefright/include/CCodecBufferChannel.h @@ -180,7 +180,13 @@ private: std::atomic_uint64_t mFirstValidFrameIndex; sp makeMemoryDealer(size_t heapSize); - Mutexed> mSurface; + + struct OutputSurface { + sp surface; + std::list> bufferRefs; + size_t maxBufferCount; + }; + Mutexed mOutputSurface; std::shared_ptr mInputSurface; -- GitLab From 9051980a1d6298b88aee40ed11e169f3e6c832c5 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 9 Mar 2018 10:35:37 -0800 Subject: [PATCH 0030/1530] CCodec: adapt to modified onComponentAllocated callback Bug: 74435947 Test: atest CtsMediaTestCases:MediaCodecListTest Change-Id: I5a4b9063e59ca326e24821a1f2c92d5e49321a4d --- media/libstagefright/CCodec.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp index 0bdd8089d7..0bd4d860c7 100644 --- a/media/libstagefright/CCodec.cpp +++ b/media/libstagefright/CCodec.cpp @@ -30,7 +30,9 @@ #include #include #include +#include #include +#include #include "include/C2OMXNode.h" #include "include/CCodecBufferChannel.h" @@ -273,6 +275,31 @@ void CCodec::allocate(const AString &componentName) { ALOGV("allocate(%s)", componentName.c_str()); mListener.reset(new CCodecListener(this)); + sp list = MediaCodecList::getInstance(); + if (list == nullptr) { + ALOGE("Unable to obtain MediaCodecList while " + "attempting to create codec \"%s\"", + componentName.c_str()); + mCallback->onError(NO_INIT, ACTION_CODE_FATAL); + return; + } + ssize_t index = list->findCodecByName(componentName.c_str()); + if (index < 0) { + ALOGE("Unable to find codec \"%s\"", + componentName.c_str()); + mCallback->onError(NAME_NOT_FOUND, ACTION_CODE_FATAL); + return; + } + sp info = list->getCodecInfo(index); + if (info == nullptr) { + ALOGE("Unexpected error (index out-of-bound) while " + "retrieving information for codec \"%s\"", + componentName.c_str()); + mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + return; + } + // TODO: use info->getOwnerName() for connecting to remote process. + std::shared_ptr comp; c2_status_t err = GetCodec2PlatformComponentStore()->createComponent( componentName.c_str(), &comp); @@ -307,7 +334,7 @@ void CCodec::allocate(const AString &componentName) { if (tryAndReportOnError(setAllocated) != OK) { return; } - mCallback->onComponentAllocated(comp->intf()->getName().c_str()); + mCallback->onComponentAllocated(comp->intf()->getName().c_str(), info); } void CCodec::initiateConfigureComponent(const sp &format) { -- GitLab From 4c9e2dd5f4346a2f301382fd8a0e08b898f4b9e7 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Fri, 9 Mar 2018 11:13:08 -0800 Subject: [PATCH 0031/1530] media: Add taklee to owners group Change-Id: Iaa12fa462cc9a7ed38cbed45c29ec3cc7e340f0a --- media/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/media/OWNERS b/media/OWNERS index d49eb8d9a7..1f687a2d68 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -15,5 +15,6 @@ pmclean@google.com rachad@google.com rago@google.com robertshih@google.com +taklee@google.com wjia@google.com wonsik@google.com -- GitLab From 25aec414ed64c7d471fd66e2dd2e5693cc855a55 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 8 Mar 2018 22:54:23 -0800 Subject: [PATCH 0032/1530] CCodec: Episode V --- Encrypted Input Buffers Handle encrypted input buffers. Bug: 69376489 Test: adb shell setprop debug.stagefright.ccodec yes Test: adb shell setprop debug.stagefright.omx_default_rank 1000 Test: adb shell killall mediaserver Test: atest CtsMediaTestCases:MediaDrmClearkeyTest Change-Id: Ife2ea39419366d8e8998614a72a30f05cd7c4c4d --- media/libstagefright/CCodecBufferChannel.cpp | 240 ++++++++++++++---- media/libstagefright/Codec2Buffer.cpp | 56 ++++ .../include/CCodecBufferChannel.h | 1 + media/libstagefright/include/Codec2Buffer.h | 76 ++++++ 4 files changed, 330 insertions(+), 43 deletions(-) diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp index 65d637bb89..a2526bc355 100644 --- a/media/libstagefright/CCodecBufferChannel.cpp +++ b/media/libstagefright/CCodecBufferChannel.cpp @@ -49,6 +49,8 @@ using hardware::hidl_vec; using namespace hardware::cas::V1_0; using namespace hardware::cas::native::V1_0; +using CasStatus = hardware::cas::V1_0::Status; + /** * Base class for representation of buffers at one port. */ @@ -291,21 +293,6 @@ private: DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool); }; -sp AllocateLinearBuffer( - const std::shared_ptr &pool, - const sp &format, - size_t size, - const C2MemoryUsage &usage) { - std::shared_ptr block; - - c2_status_t err = pool->fetchLinearBlock(size, usage, &block); - if (err != C2_OK) { - return nullptr; - } - - return LinearBlockBuffer::Allocate(format, block); -} - sp AllocateGraphicBuffer( const std::shared_ptr &pool, const sp &format, @@ -572,9 +559,7 @@ public: bool requestNewBuffer(size_t *index, sp *buffer) override { // TODO: proper max input size // TODO: read usage from intf - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - sp newBuffer = AllocateLinearBuffer( - mPool, mFormat, kLinearBufferSize, usage); + sp newBuffer = alloc(kLinearBufferSize); if (newBuffer == nullptr) { return false; } @@ -598,17 +583,88 @@ public: array->initialize( mImpl, kMinBufferArraySize, - [pool = mPool, format = mFormat] () -> sp { - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - return AllocateLinearBuffer(pool, format, kLinearBufferSize, usage); - }); + [this] () -> sp { return alloc(kLinearBufferSize); }); return std::move(array); } + virtual sp alloc(size_t size) const { + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + std::shared_ptr block; + + c2_status_t err = mPool->fetchLinearBlock(size, usage, &block); + if (err != C2_OK) { + return nullptr; + } + + return LinearBlockBuffer::Allocate(mFormat, block); + } + private: FlexBuffersImpl mImpl; }; +class EncryptedLinearInputBuffers : public LinearInputBuffers { +public: + EncryptedLinearInputBuffers( + bool secure, + const sp &dealer, + const sp &crypto, + int32_t heapSeqNum) + : mUsage({0, 0}), + mDealer(dealer), + mCrypto(crypto), + mHeapSeqNum(heapSeqNum) { + if (secure) { + mUsage = { C2MemoryUsage::READ_PROTECTED, 0 }; + } else { + mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + } + for (size_t i = 0; i < kMinBufferArraySize; ++i) { + sp memory = mDealer->allocate(kLinearBufferSize); + if (memory == nullptr) { + ALOGD("Failed to allocate memory from dealer: only %zu slots allocated", i); + break; + } + mMemoryVector.push_back({std::weak_ptr(), memory}); + } + } + + ~EncryptedLinearInputBuffers() override { + } + + sp alloc(size_t size) const override { + sp memory; + for (const Entry &entry : mMemoryVector) { + if (entry.block.expired()) { + memory = entry.memory; + break; + } + } + if (memory == nullptr) { + return nullptr; + } + + std::shared_ptr block; + c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block); + if (err != C2_OK) { + return nullptr; + } + + return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum); + } + +private: + C2MemoryUsage mUsage; + sp mDealer; + sp mCrypto; + int32_t mHeapSeqNum; + struct Entry { + std::weak_ptr block; + sp memory; + }; + std::vector mMemoryVector; +}; + class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers { public: GraphicInputBuffers() : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {} @@ -956,7 +1012,8 @@ void CCodecBufferChannel::QueueSync::stop() { CCodecBufferChannel::CCodecBufferChannel( const std::function &onError) - : mOnError(onError), + : mHeapSeqNum(-1), + mOnError(onError), mFrameIndex(0u), mFirstValidFrameIndex(0u) { } @@ -978,13 +1035,7 @@ status_t CCodecBufferChannel::setInputSurface( return OK; } -status_t CCodecBufferChannel::queueInputBuffer(const sp &buffer) { - QueueGuard guard(mSync); - if (!guard.isRunning()) { - ALOGW("No more buffers should be queued at current state."); - return -ENOSYS; - } - +status_t CCodecBufferChannel::queueInputBufferInternal(const sp &buffer) { int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); @@ -1005,7 +1056,11 @@ status_t CCodecBufferChannel::queueInputBuffer(const sp &buffe work->input.buffers.clear(); { Mutexed>::Locked buffers(mInputBuffers); - work->input.buffers.push_back((*buffers)->releaseBuffer(buffer)); + std::shared_ptr c2buffer = (*buffers)->releaseBuffer(buffer); + if (!c2buffer) { + return -ENOENT; + } + work->input.buffers.push_back(c2buffer); } // TODO: fill info's @@ -1017,22 +1072,103 @@ status_t CCodecBufferChannel::queueInputBuffer(const sp &buffe return mComponent->queue_nb(&items); } +status_t CCodecBufferChannel::queueInputBuffer(const sp &buffer) { + QueueGuard guard(mSync); + if (!guard.isRunning()) { + ALOGW("No more buffers should be queued at current state."); + return -ENOSYS; + } + return queueInputBufferInternal(buffer); +} + status_t CCodecBufferChannel::queueSecureInputBuffer( const sp &buffer, bool secure, const uint8_t *key, const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern, const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, AString *errorDetailMsg) { - // TODO - (void) buffer; - (void) secure; - (void) key; - (void) iv; - (void) mode; - (void) pattern; - (void) subSamples; - (void) numSubSamples; - (void) errorDetailMsg; - return -ENOSYS; + QueueGuard guard(mSync); + if (!guard.isRunning()) { + ALOGW("No more buffers should be queued at current state."); + return -ENOSYS; + } + + if (!hasCryptoOrDescrambler()) { + return -ENOSYS; + } + sp encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get()); + + ssize_t result = -1; + if (mCrypto != nullptr) { + ICrypto::DestinationBuffer destination; + if (secure) { + destination.mType = ICrypto::kDestinationTypeNativeHandle; + destination.mHandle = encryptedBuffer->handle(); + } else { + destination.mType = ICrypto::kDestinationTypeSharedMemory; + destination.mSharedMemory = mDecryptDestination; + } + ICrypto::SourceBuffer source; + encryptedBuffer->fillSourceBuffer(&source); + result = mCrypto->decrypt( + key, iv, mode, pattern, source, buffer->offset(), + subSamples, numSubSamples, destination, errorDetailMsg); + if (result < 0) { + return result; + } + if (destination.mType == ICrypto::kDestinationTypeSharedMemory) { + encryptedBuffer->copyDecryptedContent(mDecryptDestination, result); + } + } else { + // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample + // directly, the structure definitions should match as checked in DescramblerImpl.cpp. + hidl_vec hidlSubSamples; + hidlSubSamples.setToExternal((SubSample *)subSamples, numSubSamples, false /*own*/); + + hardware::cas::native::V1_0::SharedBuffer srcBuffer; + encryptedBuffer->fillSourceBuffer(&srcBuffer); + + DestinationBuffer dstBuffer; + if (secure) { + dstBuffer.type = BufferType::NATIVE_HANDLE; + dstBuffer.secureMemory = hidl_handle(encryptedBuffer->handle()); + } else { + dstBuffer.type = BufferType::SHARED_MEMORY; + dstBuffer.nonsecureMemory = srcBuffer; + } + + CasStatus status = CasStatus::OK; + hidl_string detailedError; + + auto returnVoid = mDescrambler->descramble( + key != NULL ? (ScramblingControl)key[0] : ScramblingControl::UNSCRAMBLED, + hidlSubSamples, + srcBuffer, + 0, + dstBuffer, + 0, + [&status, &result, &detailedError] ( + CasStatus _status, uint32_t _bytesWritten, + const hidl_string& _detailedError) { + status = _status; + result = (ssize_t)_bytesWritten; + detailedError = _detailedError; + }); + + if (!returnVoid.isOk() || status != CasStatus::OK || result < 0) { + ALOGE("descramble failed, trans=%s, status=%d, result=%zd", + returnVoid.description().c_str(), status, result); + return UNKNOWN_ERROR; + } + + ALOGV("descramble succeeded, %zd bytes", result); + + if (dstBuffer.type == BufferType::SHARED_MEMORY) { + encryptedBuffer->copyDecryptedContentFromMemory(result); + } + } + + buffer->setRange(0, result); + return queueInputBufferInternal(buffer); } void CCodecBufferChannel::feedInputBufferIfAvailable() { @@ -1166,6 +1302,7 @@ status_t CCodecBufferChannel::start( if (err != C2_OK) { return UNKNOWN_ERROR; } + bool secure = mComponent->intf()->getName().find(".secure") != std::string::npos; if (inputFormat != nullptr) { Mutexed>::Locked buffers(mInputBuffers); @@ -1178,7 +1315,24 @@ status_t CCodecBufferChannel::start( buffers->reset(new GraphicInputBuffers); } } else { - buffers->reset(new LinearInputBuffers); + if (hasCryptoOrDescrambler()) { + if (mDealer == nullptr) { + mDealer = new MemoryDealer( + align(kLinearBufferSize, MemoryDealer::getAllocationAlignment()) + * (kMinBufferArraySize + 1), + "EncryptedLinearInputBuffers"); + mDecryptDestination = mDealer->allocate(kLinearBufferSize); + } + if (mCrypto != nullptr && mHeapSeqNum < 0) { + mHeapSeqNum = mCrypto->setHeap(mDealer->getMemoryHeap()); + } else { + mHeapSeqNum = -1; + } + buffers->reset(new EncryptedLinearInputBuffers( + secure, mDealer, mCrypto, mHeapSeqNum)); + } else { + buffers->reset(new LinearInputBuffers); + } } (*buffers)->setFormat(inputFormat); diff --git a/media/libstagefright/Codec2Buffer.cpp b/media/libstagefright/Codec2Buffer.cpp index c664f2ca3c..d2ef2294f9 100644 --- a/media/libstagefright/Codec2Buffer.cpp +++ b/media/libstagefright/Codec2Buffer.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "Codec2Buffer" #include +#include #include #include @@ -613,4 +614,59 @@ bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { return true; } +// EncryptedLinearBlockBuffer + +EncryptedLinearBlockBuffer::EncryptedLinearBlockBuffer( + const sp &format, + const std::shared_ptr &block, + const sp &memory, + int32_t heapSeqNum) + : Codec2Buffer(format, new ABuffer(memory->pointer(), memory->size())), + mBlock(block), + mMemory(memory), + mHeapSeqNum(heapSeqNum) { +} + +std::shared_ptr EncryptedLinearBlockBuffer::asC2Buffer() { + return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence())); +} + +void EncryptedLinearBlockBuffer::fillSourceBuffer( + ICrypto::SourceBuffer *source) { + source->mSharedMemory = mMemory; + source->mHeapSeqNum = mHeapSeqNum; +} + +void EncryptedLinearBlockBuffer::fillSourceBuffer( + hardware::cas::native::V1_0::SharedBuffer *source) { + ssize_t offset; + size_t size; + + mHidlMemory = hardware::fromHeap(mMemory->getMemory(&offset, &size)); + source->heapBase = *mHidlMemory; + source->offset = offset; + source->size = size; +} + +bool EncryptedLinearBlockBuffer::copyDecryptedContent( + const sp &decrypted, size_t length) { + C2WriteView view = mBlock->map().get(); + if (view.error() != C2_OK) { + return false; + } + if (view.size() < length) { + return false; + } + memcpy(view.data(), decrypted->pointer(), length); + return true; +} + +bool EncryptedLinearBlockBuffer::copyDecryptedContentFromMemory(size_t length) { + return copyDecryptedContent(mMemory, length); +} + +native_handle_t *EncryptedLinearBlockBuffer::handle() const { + return const_cast(mBlock->handle()); +} + } // namespace android diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h index 51eee105cf..673b74fb43 100644 --- a/media/libstagefright/include/CCodecBufferChannel.h +++ b/media/libstagefright/include/CCodecBufferChannel.h @@ -162,6 +162,7 @@ private: }; void feedInputBufferIfAvailable(); + status_t queueInputBufferInternal(const sp &buffer); QueueSync mSync; sp mDealer; diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h index eeb889d9dc..b2e3b1be6a 100644 --- a/media/libstagefright/include/Codec2Buffer.h +++ b/media/libstagefright/include/Codec2Buffer.h @@ -20,8 +20,11 @@ #include +#include +#include #include #include +#include namespace android { @@ -271,6 +274,79 @@ private: const bool mWrapped; }; +/** + * MediaCodecBuffer implementation wraps around C2LinearBlock for component + * and IMemory for client. Underlying C2LinearBlock won't be mapped for secure + * usecases.. + */ +class EncryptedLinearBlockBuffer : public Codec2Buffer { +public: + /** + * Construct a new EncryptedLinearBufferBlock wrapping around C2LinearBlock + * object and writable IMemory region. + * + * \param format mandatory buffer format for MediaCodecBuffer + * \param block C2LinearBlock object to wrap around. + * \param memory IMemory object to store encrypted content. + * \param heapSeqNum Heap sequence number from ICrypto; -1 if N/A + */ + EncryptedLinearBlockBuffer( + const sp &format, + const std::shared_ptr &block, + const sp &memory, + int32_t heapSeqNum = -1); + EncryptedLinearBlockBuffer() = delete; + + virtual ~EncryptedLinearBlockBuffer() = default; + + std::shared_ptr asC2Buffer() override; + + /** + * Fill the source buffer structure with appropriate value based on + * internal IMemory object. + * + * \param source source buffer structure to fill. + */ + void fillSourceBuffer(ICrypto::SourceBuffer *source); + void fillSourceBuffer( + hardware::cas::native::V1_0::SharedBuffer *source); + + /** + * Copy the content of |decrypted| into C2LinearBlock inside. This shall + * only be called in non-secure usecases. + * + * \param decrypted decrypted content to copy from. + * \param length length of the content + * \return true if successful + * false otherwise. + */ + bool copyDecryptedContent(const sp &decrypted, size_t length); + + /** + * Copy the content of internal IMemory object into C2LinearBlock inside. + * This shall only be called in non-secure usecases. + * + * \param length length of the content + * \return true if successful + * false otherwise. + */ + bool copyDecryptedContentFromMemory(size_t length); + + /** + * Return native handle of secure buffer understood by ICrypto. + * + * \return secure buffer handle + */ + native_handle_t *handle() const; + +private: + + std::shared_ptr mBlock; + sp mMemory; + sp mHidlMemory; + int32_t mHeapSeqNum; +}; + } // namespace android #endif // CODEC2_BUFFER_H_ -- GitLab From 68364ffbd312cdc28dd44e66c4d80ce5a6ec49ba Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Wed, 31 Jan 2018 20:24:23 -0800 Subject: [PATCH 0033/1530] audiopolicy: Check if A2DP playback happens via primary output Audio policy implementation assumes A2DP playback to happen via HW module other than the primary HAL. This need not be true always, since primary audio HAL can support A2DP playback too. Add an API to check if A2DP playback is supported via primary HAL, and if it is supported avoid suspendOutput. Bug: 68824150 Test: A2DP offload verified on supported codecs Change-Id: Ic5546bc2df632462dcc28442af561d05ba2d7c58 --- .../include/AudioOutputDescriptor.h | 10 ++++++++ .../src/AudioOutputDescriptor.cpp | 23 +++++++++++++++++++ .../audiopolicy/enginedefault/src/Engine.cpp | 6 ++--- .../managerdefault/AudioPolicyManager.cpp | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 78a184b70b..5e5d38b069 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -203,6 +203,16 @@ public: */ audio_io_handle_t getA2dpOutput() const; + /** + * returns true if primary HAL supports A2DP Offload + */ + bool isA2dpOffloadedOnPrimary() const; + + /** + * returns true if A2DP is supported (either via hardware offload or software encoding) + */ + bool isA2dpSupported() const; + sp getOutputFromId(audio_port_handle_t id) const; sp getPrimaryOutput() const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index caaa0f7ad9..294a2a695e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -645,6 +645,29 @@ audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const return 0; } +bool SwAudioOutputCollection::isA2dpOffloadedOnPrimary() const +{ + sp primaryOutput = getPrimaryOutput(); + + if ((primaryOutput != NULL) && (primaryOutput->mProfile != NULL) + && (primaryOutput->mProfile->mModule != NULL)) { + sp primaryHwModule = primaryOutput->mProfile->mModule; + Vector > primaryHwModuleOutputProfiles = + primaryHwModule->getOutputProfiles(); + for (size_t i = 0; i < primaryHwModuleOutputProfiles.size(); i++) { + if (primaryHwModuleOutputProfiles[i]->supportDevice(AUDIO_DEVICE_OUT_ALL_A2DP)) { + return true; + } + } + } + return false; +} + +bool SwAudioOutputCollection::isA2dpSupported() const +{ + return (isA2dpOffloadedOnPrimary() || (getA2dpOutput() != 0)); +} + sp SwAudioOutputCollection::getPrimaryOutput() const { for (size_t i = 0; i < size(); i++) { diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 5ec04757ef..18b70121de 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -335,7 +335,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (!isInCall() && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (outputs.getA2dpOutput() != 0)) { + outputs.isA2dpSupported()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; @@ -369,7 +369,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // A2DP speaker when forcing to speaker output if (!isInCall() && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (outputs.getA2dpOutput() != 0)) { + outputs.isA2dpSupported()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; } @@ -483,7 +483,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, } if ((device2 == AUDIO_DEVICE_NONE) && (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (outputs.getA2dpOutput() != 0)) { + outputs.isA2dpSupported()) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device2 == AUDIO_DEVICE_NONE) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 42b199adaa..1055007887 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4381,7 +4381,7 @@ void AudioPolicyManager::checkOutputForAllStrategies() void AudioPolicyManager::checkA2dpSuspend() { audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput(); - if (a2dpOutput == 0) { + if (a2dpOutput == 0 || mOutputs.isA2dpOffloadedOnPrimary()) { mA2dpSuspended = false; return; } -- GitLab From 713273460076a6559e31c770fe6c51861127fd5c Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Wed, 31 Jan 2018 20:26:59 -0800 Subject: [PATCH 0034/1530] audiopolicy: Add support for A2DP offload audio policy config Add support for A2DP offload audio policy config in AudioPolicyManager. Bug: 68824150 Test: A2DP offload verified on supported codecs Change-Id: Ie62eaa10218d24e6ec51d20413fb35426ed5498a --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 1055007887..74ca902e69 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -26,6 +26,7 @@ #define AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH 128 #define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml" +#define AUDIO_POLICY_A2DP_OFFLOAD_XML_CONFIG_FILE_NAME "audio_policy_a2dp_offload_configuration.xml" #include #include @@ -3517,11 +3518,14 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { for (int i = 0; i < kConfigLocationListSize; i++) { PolicySerializer serializer; + bool use_a2dp_offload_config = + property_get_bool("persist.bluetooth.a2dp_offload.enable", false); snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), "%s/%s", kConfigLocationList[i], - AUDIO_POLICY_XML_CONFIG_FILE_NAME); + use_a2dp_offload_config ? AUDIO_POLICY_A2DP_OFFLOAD_XML_CONFIG_FILE_NAME : + AUDIO_POLICY_XML_CONFIG_FILE_NAME); ret = serializer.deserialize(audioPolicyXmlConfigFile, config); if (ret == NO_ERROR) { break; -- GitLab From 058661c60be5543c8fa0ae1eadfd90693d359bca Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 12 Mar 2018 21:06:47 -0700 Subject: [PATCH 0035/1530] Separate libstagefright_{ccodec|codecbase} from libstagefright Bug: 74403547 Test: adb shell am instrument -e size small -w 'android.media.cts/android.support.test.runner.AndroidJUnitRunner' Change-Id: I2f57f08a17f1a14d431dd6aa1904bf50d1c6c378 --- media/libstagefright/ACodec.cpp | 32 ++--- media/libstagefright/Android.bp | 110 +++++++++++++++--- media/libstagefright/CCodec.cpp | 51 +++----- media/libstagefright/MediaCodec.cpp | 39 ++++--- .../include/media/stagefright/CCodec.h | 3 +- .../include/media/stagefright/CodecBase.h | 5 +- 6 files changed, 144 insertions(+), 96 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 86d7c08d83..3570aafd5b 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -6415,34 +6414,19 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { sp notify = new AMessage(kWhatOMXDied, mCodec); - AString componentName; - CHECK(msg->findString("componentName", &componentName)); - - sp list = MediaCodecList::getInstance(); - if (list == nullptr) { - ALOGE("Unable to obtain MediaCodecList while " - "attempting to create codec \"%s\"", - componentName.c_str()); - mCodec->signalError(OMX_ErrorUndefined, NO_INIT); - return false; - } - ssize_t index = list->findCodecByName(componentName.c_str()); - if (index < 0) { - ALOGE("Unable to find codec \"%s\"", - componentName.c_str()); - mCodec->signalError(OMX_ErrorInvalidComponent, NAME_NOT_FOUND); - return false; - } - sp info = list->getCodecInfo(index); + sp obj; + CHECK(msg->findObject("codecInfo", &obj)); + sp info = (MediaCodecInfo *)obj.get(); if (info == nullptr) { - ALOGE("Unexpected error (index out-of-bound) while " - "retrieving information for codec \"%s\"", - componentName.c_str()); + ALOGE("Unexpected nullptr for codec information"); mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR); return false; } AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName(); + AString componentName; + CHECK(msg->findString("componentName", &componentName)); + sp observer = new CodecObserver; sp omx; sp omxNode; @@ -6490,7 +6474,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { mCodec->mOMX = omx; mCodec->mOMXNode = omxNode; - mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str(), info); + mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str()); mCodec->changeState(mCodec->mLoadedState); return true; diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 6db70b1394..71bff84d5f 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -48,6 +48,100 @@ cc_library_static { shared_libs: ["libmedia"], } +cc_library_shared { + name: "libstagefright_codecbase", + + export_include_dirs: ["include"], + + srcs: [ + "CodecBase.cpp", + "FrameRenderTracker.cpp", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + shared_libs: [ + "libgui", + "liblog", + "libmedia", + "libstagefright_foundation", + "libui", + "libutils", + "android.hardware.cas.native@1.0", + ], + + sanitize: { + cfi: true, + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + diag: { + cfi: true, + }, + }, +} + +cc_library_shared { + name: "libstagefright_ccodec", + + local_include_dirs: ["include"], + + srcs: [ + "C2OMXNode.cpp", + "CCodec.cpp", + "CCodecBufferChannel.cpp", + "Codec2Buffer.cpp", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + header_libs: [ + "libstagefright_codec2_internal", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libgui", + "libhidlallocatorutils", + "libhidlbase", + "liblog", + "libmedia", + "libmedia_omx", + "libstagefright_codec2", + "libstagefright_codec2_vndk", + "libstagefright_codecbase", + "libstagefright_foundation", + "libstagefright_omx_utils", + "libui", + "libutils", + "libv4l2_c2componentstore", + "android.hardware.cas.native@1.0", + + // TODO: do not link directly with impl + "libstagefright_bufferqueue_helper", + "android.hardware.media.c2@1.0-service-impl", + ], + + sanitize: { + cfi: true, + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + diag: { + cfi: true, + }, + }, +} + cc_library_shared { name: "libstagefright", @@ -60,12 +154,7 @@ cc_library_shared { "AudioPresentationInfo.cpp", "AudioSource.cpp", "BufferImpl.cpp", - "C2OMXNode.cpp", - "CCodec.cpp", - "CCodecBufferChannel.cpp", - "Codec2Buffer.cpp", "Codec2InfoBuilder.cpp", - "CodecBase.cpp", "CallbackDataSource.cpp", "CallbackMediaSource.cpp", "CameraSource.cpp", @@ -75,7 +164,6 @@ cc_library_shared { "DataURISource.cpp", "FileSource.cpp", "FrameDecoder.cpp", - "FrameRenderTracker.cpp", "HTTPBase.cpp", "HevcUtils.cpp", "InterfaceUtils.cpp", @@ -108,10 +196,6 @@ cc_library_shared { "VideoFrameScheduler.cpp", ], - header_libs: [ - "libstagefright_codec2_internal", - ], - shared_libs: [ "libaudioutils", "libbinder", @@ -132,8 +216,10 @@ cc_library_shared { "libui", "libutils", "libmedia_helper", + "libstagefright_ccodec", "libstagefright_codec2", "libstagefright_codec2_vndk", + "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx", "libstagefright_omx_utils", @@ -150,10 +236,6 @@ cc_library_shared { "android.hardware.media.omx@1.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.mapper@2.0", - - // TODO: do not link directly with impl - "android.hardware.media.c2@1.0-service-impl", - "libstagefright_bufferqueue_helper", ], static_libs: [ diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp index 0bd4d860c7..0a20d34d3c 100644 --- a/media/libstagefright/CCodec.cpp +++ b/media/libstagefright/CCodec.cpp @@ -30,9 +30,7 @@ #include #include #include -#include #include -#include #include "include/C2OMXNode.h" #include "include/CCodecBufferChannel.h" @@ -261,44 +259,25 @@ void CCodec::initiateAllocateComponent(const sp &msg) { return; } - AString componentName; - if (!msg->findString("componentName", &componentName)) { - // TODO: find componentName appropriate with the media type - } + sp codecInfo; + CHECK(msg->findObject("codecInfo", &codecInfo)); + // For Codec 2.0 components, componentName == codecInfo->getCodecName(). sp allocMsg(new AMessage(kWhatAllocate, this)); - allocMsg->setString("componentName", componentName); + allocMsg->setObject("codecInfo", codecInfo); allocMsg->post(); } -void CCodec::allocate(const AString &componentName) { - ALOGV("allocate(%s)", componentName.c_str()); - mListener.reset(new CCodecListener(this)); - - sp list = MediaCodecList::getInstance(); - if (list == nullptr) { - ALOGE("Unable to obtain MediaCodecList while " - "attempting to create codec \"%s\"", - componentName.c_str()); - mCallback->onError(NO_INIT, ACTION_CODE_FATAL); - return; - } - ssize_t index = list->findCodecByName(componentName.c_str()); - if (index < 0) { - ALOGE("Unable to find codec \"%s\"", - componentName.c_str()); - mCallback->onError(NAME_NOT_FOUND, ACTION_CODE_FATAL); - return; - } - sp info = list->getCodecInfo(index); - if (info == nullptr) { - ALOGE("Unexpected error (index out-of-bound) while " - "retrieving information for codec \"%s\"", - componentName.c_str()); +void CCodec::allocate(const sp &codecInfo) { + if (codecInfo == nullptr) { mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); return; } - // TODO: use info->getOwnerName() for connecting to remote process. + ALOGV("allocate(%s)", codecInfo->getCodecName()); + mListener.reset(new CCodecListener(this)); + + AString componentName = codecInfo->getCodecName(); + // TODO: use codecInfo->getOwnerName() for connecting to remote process. std::shared_ptr comp; c2_status_t err = GetCodec2PlatformComponentStore()->createComponent( @@ -334,7 +313,7 @@ void CCodec::allocate(const AString &componentName) { if (tryAndReportOnError(setAllocated) != OK) { return; } - mCallback->onComponentAllocated(comp->intf()->getName().c_str(), info); + mCallback->onComponentAllocated(comp->intf()->getName().c_str()); } void CCodec::initiateConfigureComponent(const sp &format) { @@ -839,9 +818,9 @@ void CCodec::onMessageReceived(const sp &msg) { case kWhatAllocate: { // C2ComponentStore::createComponent() should return within 100ms. setDeadline(now + 150ms, "allocate"); - AString componentName; - CHECK(msg->findString("componentName", &componentName)); - allocate(componentName); + sp obj; + CHECK(msg->findObject("codecInfo", &obj)); + allocate((MediaCodecInfo *)obj.get()); break; } case kWhatConfigure: { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index e59259d7bf..edcb8c7421 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -278,8 +278,7 @@ public: virtual void onReleaseCompleted() override; virtual void onFlushCompleted() override; virtual void onError(status_t err, enum ActionCode actionCode) override; - virtual void onComponentAllocated( - const char *componentName, const sp &codecInfo) override; + virtual void onComponentAllocated(const char *componentName) override; virtual void onComponentConfigured( const sp &inputFormat, const sp &outputFormat) override; virtual void onInputSurfaceCreated( @@ -339,14 +338,10 @@ void CodecCallback::onError(status_t err, enum ActionCode actionCode) { notify->post(); } -void CodecCallback::onComponentAllocated( - const char *componentName, const sp &codecInfo) { +void CodecCallback::onComponentAllocated(const char *componentName) { sp notify(mNotify->dup()); notify->setInt32("what", kWhatComponentAllocated); notify->setString("componentName", componentName); - if (codecInfo != nullptr) { - notify->setObject("codecInfo", codecInfo); - } notify->post(); } @@ -606,6 +601,8 @@ status_t MediaCodec::init(const AString &name) { return NAME_NOT_FOUND; } + mCodecInfo.clear(); + bool secureCodec = false; AString tmp = name; if (tmp.endsWith(".secure")) { @@ -617,17 +614,24 @@ status_t MediaCodec::init(const AString &name) { mCodec = NULL; // remove the codec. return NO_INIT; // if called from Java should raise IOException } - ssize_t codecIdx = mcl->findCodecByName(tmp.c_str()); - if (codecIdx >= 0) { - const sp info = mcl->getCodecInfo(codecIdx); + for (const AString &codecName : { name, tmp }) { + ssize_t codecIdx = mcl->findCodecByName(codecName.c_str()); + if (codecIdx < 0) { + continue; + } + mCodecInfo = mcl->getCodecInfo(codecIdx); Vector mimes; - info->getSupportedMimes(&mimes); + mCodecInfo->getSupportedMimes(&mimes); for (size_t i = 0; i < mimes.size(); i++) { if (mimes[i].startsWith("video/")) { mIsVideo = true; break; } } + break; + } + if (mCodecInfo == nullptr) { + return NAME_NOT_FOUND; } if (mIsVideo) { @@ -654,6 +658,9 @@ status_t MediaCodec::init(const AString &name) { new BufferCallback(new AMessage(kWhatCodecNotify, this)))); sp msg = new AMessage(kWhatInit, this); + msg->setObject("codecInfo", mCodecInfo); + // name may be different from mCodecInfo->getCodecName() if we stripped + // ".secure" msg->setString("name", name); if (mAnalyticsItem != NULL) { @@ -1607,11 +1614,6 @@ void MediaCodec::onMessageReceived(const sp &msg) { CHECK(msg->findString("componentName", &mComponentName)); - sp obj; - if (msg->findObject("codecInfo", &obj)) { - mCodecInfo = static_cast(obj.get()); - } - if (mComponentName.c_str()) { mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str()); } @@ -1996,11 +1998,14 @@ void MediaCodec::onMessageReceived(const sp &msg) { mReplyID = replyID; setState(INITIALIZING); + sp codecInfo; + CHECK(msg->findObject("codecInfo", &codecInfo)); AString name; CHECK(msg->findString("name", &name)); sp format = new AMessage; - format->setString("componentName", name.c_str()); + format->setObject("codecInfo", codecInfo); + format->setString("componentName", name); mCodec->initiateAllocateComponent(format); break; diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h index 078b03e0fc..86fbd3a7f5 100644 --- a/media/libstagefright/include/media/stagefright/CCodec.h +++ b/media/libstagefright/include/media/stagefright/CCodec.h @@ -36,6 +36,7 @@ namespace android { class CCodecBufferChannel; class InputSurfaceWrapper; +struct MediaCodecInfo; class CCodec : public CodecBase { public: @@ -74,7 +75,7 @@ private: void initiateStop(); void initiateRelease(bool sendCallback = true); - void allocate(const AString &componentName); + void allocate(const sp &codecInfo); void configure(const sp &msg); void start(); void stop(); diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h index 9606fd9a0b..268662fad1 100644 --- a/media/libstagefright/include/media/stagefright/CodecBase.h +++ b/media/libstagefright/include/media/stagefright/CodecBase.h @@ -95,11 +95,8 @@ struct CodecBase : public AHandler, /* static */ ColorUtils { * * @param componentName the unique name of the component specified in * MediaCodecList. - * @param codecInfo the associated codec info if available. */ - virtual void onComponentAllocated( - const char *componentName, - const sp &codecInfo = nullptr) = 0; + virtual void onComponentAllocated(const char *componentName) = 0; /** * Notify MediaCodec that the underlying component is configured. * -- GitLab From 9abe5ee514114a58728eed08253f8d08bd90b959 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 11 Oct 2016 09:24:54 -0700 Subject: [PATCH 0036/1530] Add missing extensions, fix scan of bad media files. Update extensions list in StagefrightMediaScanner. Mark files that fail to scan as non-media files. Bug: 31900187 Bug: 32045229 Change-Id: I78f21b044f3a038d6e78dbe9f461f650650afe8b --- media/libstagefright/StagefrightMediaScanner.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 4ff2bfe189..e010b3e751 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -40,7 +40,8 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", - ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov" + ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov", + ".m4v", ".oga" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); @@ -62,6 +63,11 @@ MediaScanResult StagefrightMediaScanner::processFile( client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); + ALOGV("result: %d", result); + if (mimeType == NULL && result != MEDIA_SCAN_RESULT_OK) { + ALOGW("media scan failed for %s", path); + client.setMimeType("application/octet-stream"); + } client.endFile(); return result; } -- GitLab From 44ecf0521fd4ed27089d88bad050f0430139d3ff Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 22 Mar 2018 14:55:52 -0700 Subject: [PATCH 0037/1530] metrics on latency through codecs track the latency for frames to go through the codec and return to the caller. Provide min/max/avg/N information for the total and for the last couple hundred frames. Histogram data is collected, but not yet emitted. Bug: 64022743 Test: dumpsys media.metrics Change-Id: I954f0048997c8f65931c4faa678666eec59aaf86 --- media/libstagefright/MediaCodec.cpp | 304 +++++++++++++++++- .../include/media/stagefright/MediaCodec.h | 59 ++++ 2 files changed, 354 insertions(+), 9 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 5ad4c01e1f..b874df498a 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "include/SecureBuffer.h" #include "include/SharedMemoryBuffer.h" @@ -88,6 +89,22 @@ static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth"; /* 0.. static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight"; /* 0..n */ static const char *kCodecError = "android.media.mediacodec.errcode"; static const char *kCodecErrorState = "android.media.mediacodec.errstate"; +static const char *kCodecLatencyMax = "android.media.mediacodec.latency.max"; /* in us */ +static const char *kCodecLatencyMin = "android.media.mediacodec.latency.min"; /* in us */ +static const char *kCodecLatencyAvg = "android.media.mediacodec.latency.avg"; /* in us */ +static const char *kCodecLatencyCount = "android.media.mediacodec.latency.n"; +static const char *kCodecLatencyHist = "android.media.mediacodec.latency.hist"; /* in us */ +static const char *kCodecLatencyUnknown = "android.media.mediacodec.latency.unknown"; + +// the kCodecRecent* fields appear only in getMetrics() results +static const char *kCodecRecentLatencyMax = "android.media.mediacodec.recent.max"; /* in us */ +static const char *kCodecRecentLatencyMin = "android.media.mediacodec.recent.min"; /* in us */ +static const char *kCodecRecentLatencyAvg = "android.media.mediacodec.recent.avg"; /* in us */ +static const char *kCodecRecentLatencyCount = "android.media.mediacodec.recent.n"; +static const char *kCodecRecentLatencyHist = "android.media.mediacodec.recent.hist"; /* in us */ + +// XXX suppress until we get our representation right +static bool kEmitHistogram = false; static int64_t getId(const sp &client) { @@ -506,12 +523,14 @@ MediaCodec::MediaCodec(const sp &looper, pid_t pid, uid_t uid) mDequeueOutputTimeoutGeneration(0), mDequeueOutputReplyID(0), mHaveInputSurface(false), - mHavePendingInputBuffers(false) { + mHavePendingInputBuffers(false), + mLatencyUnknown(0) { if (uid == kNoUid) { mUid = IPCThreadState::self()->getCallingUid(); } else { mUid = uid; } + initAnalyticsItem(); } @@ -523,16 +542,90 @@ MediaCodec::~MediaCodec() { } void MediaCodec::initAnalyticsItem() { - CHECK(mAnalyticsItem == NULL); - // set up our new record, get a sessionID, put it into the in-progress list - mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName); - if (mAnalyticsItem != NULL) { - // don't record it yet; only at the end, when we have decided that we have - // data worth writing (e.g. .count() > 0) + if (mAnalyticsItem == NULL) { + mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName); + } + + mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor); + + { + Mutex::Autolock al(mRecentLock); + for (int i = 0; isetInt64(kCodecLatencyMax, mLatencyHist.getMax()); + mAnalyticsItem->setInt64(kCodecLatencyMin, mLatencyHist.getMin()); + mAnalyticsItem->setInt64(kCodecLatencyAvg, mLatencyHist.getAvg()); + mAnalyticsItem->setInt64(kCodecLatencyCount, mLatencyHist.getCount()); + + if (kEmitHistogram) { + // and the histogram itself + std::string hist = mLatencyHist.emit(); + mAnalyticsItem->setCString(kCodecLatencyHist, hist.c_str()); + } + } + if (mLatencyUnknown > 0) { + mAnalyticsItem->setInt64(kCodecLatencyUnknown, mLatencyUnknown); + } + +#if 0 + // enable for short term, only while debugging + updateEphemeralAnalytics(mAnalyticsItem); +#endif +} + +void MediaCodec::updateEphemeralAnalytics(MediaAnalyticsItem *item) { + ALOGD("MediaCodec::updateEphemeralAnalytics()"); + + if (item == NULL) { + return; + } + + Histogram recentHist; + + // build an empty histogram + recentHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor); + + // stuff it with the samples in the ring buffer + { + Mutex::Autolock al(mRecentLock); + + for (int i=0; isetInt64(kCodecRecentLatencyMax, recentHist.getMax()); + item->setInt64(kCodecRecentLatencyMin, recentHist.getMin()); + item->setInt64(kCodecRecentLatencyAvg, recentHist.getAvg()); + item->setInt64(kCodecRecentLatencyCount, recentHist.getCount()); + + if (kEmitHistogram) { + // and the histogram itself + std::string hist = recentHist.emit(); + item->setCString(kCodecRecentLatencyHist, hist.c_str()); + } } } void MediaCodec::flushAnalyticsItem() { + updateAnalyticsItem(); if (mAnalyticsItem != NULL) { // don't log empty records if (mAnalyticsItem->count() > 0) { @@ -543,6 +636,190 @@ void MediaCodec::flushAnalyticsItem() { } } +bool MediaCodec::Histogram::setup(int nbuckets, int64_t width, int64_t floor) +{ + if (nbuckets <= 0 || width <= 0) { + return false; + } + + // get histogram buckets + if (nbuckets == mBucketCount && mBuckets != NULL) { + // reuse our existing buffer + memset(mBuckets, 0, sizeof(*mBuckets) * mBucketCount); + } else { + // get a new pre-zeroed buffer + int64_t *newbuckets = (int64_t *)calloc(nbuckets, sizeof (*mBuckets)); + if (newbuckets == NULL) { + goto bad; + } + if (mBuckets != NULL) + free(mBuckets); + mBuckets = newbuckets; + } + + mWidth = width; + mFloor = floor; + mCeiling = floor + nbuckets * width; + mBucketCount = nbuckets; + + mMin = INT64_MAX; + mMax = INT64_MIN; + mSum = 0; + mCount = 0; + mBelow = mAbove = 0; + + return true; + + bad: + if (mBuckets != NULL) { + free(mBuckets); + mBuckets = NULL; + } + + return false; +} + +void MediaCodec::Histogram::insert(int64_t sample) +{ + // histogram is not set up + if (mBuckets == NULL) { + return; + } + + mCount++; + mSum += sample; + if (mMin > sample) mMin = sample; + if (mMax < sample) mMax = sample; + + if (sample < mFloor) { + mBelow++; + } else if (sample >= mCeiling) { + mAbove++; + } else { + int64_t slot = (sample - mFloor) / mWidth; + CHECK(slot < mBucketCount); + mBuckets[slot]++; + } + return; +} + +std::string MediaCodec::Histogram::emit() +{ + std::string value; + char buffer[64]; + + // emits: width,Below{bucket0,bucket1,...., bucketN}above + // unconfigured will emit: 0,0{}0 + // XXX: is this best representation? + snprintf(buffer, sizeof(buffer), "%" PRId64 ",%" PRId64 ",%" PRId64 "{", + mFloor, mWidth, mBelow); + value = buffer; + for (int i = 0; i < mBucketCount; i++) { + if (i != 0) { + value = value + ","; + } + snprintf(buffer, sizeof(buffer), "%" PRId64, mBuckets[i]); + value = value + buffer; + } + snprintf(buffer, sizeof(buffer), "}%" PRId64 , mAbove); + value = value + buffer; + return value; +} + +// when we send a buffer to the codec; +void MediaCodec::statsBufferSent(int64_t presentationUs) { + + // only enqueue if we have a legitimate time + if (presentationUs <= 0) { + ALOGV("presentation time: %" PRId64, presentationUs); + return; + } + + const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); + BufferFlightTiming_t startdata = { presentationUs, nowNs }; + + { + // mutex access to mBuffersInFlight and other stats + Mutex::Autolock al(mLatencyLock); + + + // XXX: we *could* make sure that the time is later than the end of queue + // as part of a consistency check... + mBuffersInFlight.push_back(startdata); + } +} + +// when we get a buffer back from the codec +void MediaCodec::statsBufferReceived(int64_t presentationUs) { + + CHECK_NE(mState, UNINITIALIZED); + + // mutex access to mBuffersInFlight and other stats + Mutex::Autolock al(mLatencyLock); + + // how long this buffer took for the round trip through the codec + // NB: pipelining can/will make these times larger. e.g., if each packet + // is always 2 msec and we have 3 in flight at any given time, we're going to + // see "6 msec" as an answer. + + // ignore stuff with no presentation time + if (presentationUs <= 0) { + ALOGD("-- returned buffer has bad timestamp %" PRId64 ", ignore it", presentationUs); + mLatencyUnknown++; + return; + } + + BufferFlightTiming_t startdata; + bool valid = false; + while (mBuffersInFlight.size() > 0) { + startdata = *mBuffersInFlight.begin(); + ALOGV("-- Looking at startdata. presentation %" PRId64 ", start %" PRId64, + startdata.presentationUs, startdata.startedNs); + if (startdata.presentationUs == presentationUs) { + // a match + ALOGV("-- match entry for %" PRId64 ", hits our frame of %" PRId64, + startdata.presentationUs, presentationUs); + mBuffersInFlight.pop_front(); + valid = true; + break; + } else if (startdata.presentationUs < presentationUs) { + // we must have missed the match for this, drop it and keep looking + ALOGV("-- drop entry for %" PRId64 ", before our frame of %" PRId64, + startdata.presentationUs, presentationUs); + mBuffersInFlight.pop_front(); + continue; + } else { + // head is after, so we don't have a frame for ourselves + ALOGV("-- found entry for %" PRId64 ", AFTER our frame of %" PRId64 + " we have nothing to pair with", + startdata.presentationUs, presentationUs); + mLatencyUnknown++; + return; + } + } + if (!valid) { + ALOGV("-- empty queue, so ignore that."); + mLatencyUnknown++; + return; + } + + // nowNs start our calculations + const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); + int64_t latencyUs = (nowNs - startdata.startedNs + 500) / 1000; + + mLatencyHist.insert(latencyUs); + + // push into the recent samples + { + Mutex::Autolock al(mRecentLock); + + if (mRecentHead >= kRecentLatencyFrames) { + mRecentHead = 0; + } + mRecentSamples[mRecentHead++] = latencyUs; + } +} + // static status_t MediaCodec::PostAndAwaitResponse( const sp &msg, sp *response) { @@ -778,7 +1055,6 @@ status_t MediaCodec::configure( msg->setPointer("descrambler", descrambler.get()); } if (mAnalyticsItem != NULL) { - // XXX: save indication that it's crypto in some way... mAnalyticsItem->setInt32(kCodecCrypto, 1); } } else if (mFlags & kFlagIsSecure) { @@ -1245,11 +1521,14 @@ status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) { return UNKNOWN_ERROR; } - // XXX: go get current values for whatever in-flight data we want + // update any in-flight data that's not carried within the record + updateAnalyticsItem(); // send it back to the caller. reply = mAnalyticsItem->dup(); + updateEphemeralAnalytics(reply); + return OK; } @@ -1435,6 +1714,8 @@ bool MediaCodec::handleDequeueOutputBuffer(const sp &replyID, bool int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + statsBufferReceived(timeUs); + response->setInt64("timeUs", timeUs); int32_t flags; @@ -2919,6 +3200,9 @@ status_t MediaCodec::onQueueInputBuffer(const sp &msg) { Mutex::Autolock al(mBufferLock); info->mOwnedByClient = false; info->mData.clear(); + + statsBufferSent(timeUs); + if (mAnalyticsItem != NULL) { mAnalyticsItem->addInt64(kCodecBytesIn, size); } @@ -3138,6 +3422,8 @@ void MediaCodec::onOutputBufferAvailable() { msg->setInt64("timeUs", timeUs); + statsBufferReceived(timeUs); + int32_t flags; CHECK(buffer->meta()->findInt32("flags", &flags)); diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index ef8de1f691..48a1224bfa 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -320,7 +320,9 @@ private: MediaAnalyticsItem *mAnalyticsItem; void initAnalyticsItem(); + void updateAnalyticsItem(); void flushAnalyticsItem(); + void updateEphemeralAnalytics(MediaAnalyticsItem *item); sp mOutputFormat; sp mInputFormat; @@ -441,6 +443,63 @@ private: void onReleaseCrypto(const sp& msg); + // managing time-of-flight aka latency + typedef struct { + int64_t presentationUs; + int64_t startedNs; + } BufferFlightTiming_t; + std::deque mBuffersInFlight; + Mutex mLatencyLock; + int64_t mLatencyUnknown; // buffers for which we couldn't calculate latency + + void statsBufferSent(int64_t presentationUs); + void statsBufferReceived(int64_t presentationUs); + + enum { + // the default shape of our latency histogram buckets + // XXX: should these be configurable in some way? + kLatencyHistBuckets = 20, + kLatencyHistWidth = 2000, + kLatencyHistFloor = 2000, + + // how many samples are in the 'recent latency' histogram + // 300 frames = 5 sec @ 60fps or ~12 sec @ 24fps + kRecentLatencyFrames = 300, + + // how we initialize mRecentSamples + kRecentSampleInvalid = -1, + }; + + int64_t mRecentSamples[kRecentLatencyFrames]; + int mRecentHead; + Mutex mRecentLock; + + class Histogram { + public: + Histogram() : mFloor(0), mWidth(0), mBelow(0), mAbove(0), + mMin(INT64_MAX), mMax(INT64_MIN), mSum(0), mCount(0), + mBucketCount(0), mBuckets(NULL) {}; + ~Histogram() { clear(); }; + void clear() { if (mBuckets != NULL) free(mBuckets); mBuckets = NULL; }; + bool setup(int nbuckets, int64_t width, int64_t floor = 0); + void insert(int64_t sample); + int64_t getMin() const { return mMin; } + int64_t getMax() const { return mMax; } + int64_t getCount() const { return mCount; } + int64_t getSum() const { return mSum; } + int64_t getAvg() const { return mSum / (mCount == 0 ? 1 : mCount); } + std::string emit(); + private: + int64_t mFloor, mCeiling, mWidth; + int64_t mBelow, mAbove; + int64_t mMin, mMax, mSum, mCount; + + int mBucketCount; + int64_t *mBuckets; + }; + + Histogram mLatencyHist; + DISALLOW_EVIL_CONSTRUCTORS(MediaCodec); }; -- GitLab From d3ac20b4257530c74878e514e948eec74c96db24 Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Fri, 6 Apr 2018 13:31:01 -0700 Subject: [PATCH 0038/1530] stagefright: Move NULL check We switch our NULL check to before we dereference 'buffer' for the range_length(). This allows a debug build with a bad state to have things caught via CHECK instead of a crash. Test: Treehugger Change-Id: Iae3a6a9e90ec91962dc0e9bebcac075cf1d7d4c1 --- cmds/stagefright/stagefright.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 936733d3f3..bd25ceeb64 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -579,12 +579,12 @@ static void performSeekTest(const sp &source) { break; } + CHECK(buffer != NULL); + if (buffer->range_length() > 0) { break; } - CHECK(buffer != NULL); - buffer->release(); buffer = NULL; } -- GitLab From 5c67e746885b37f6080d63410876db13a68bae75 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Sat, 10 Mar 2018 21:10:38 -0800 Subject: [PATCH 0039/1530] Use regex pattern in version scripts for lld. * lld complains about unfound symbols in version scripts. Bug: 77820876 Test: make checkbuild Change-Id: I38af432c0c78b2ab3cdcfc5eefbf941fdba8beee --- media/libmedia/exports.lds | 2 +- media/libstagefright/exports.lds | 412 +++++++++++++++---------------- 2 files changed, 207 insertions(+), 207 deletions(-) diff --git a/media/libmedia/exports.lds b/media/libmedia/exports.lds index b09fbce81e..904a7f72e5 100644 --- a/media/libmedia/exports.lds +++ b/media/libmedia/exports.lds @@ -3,5 +3,5 @@ *; local: _ZN7android13MidiIoWrapper*; - _ZTVN7android13MidiIoWrapperE; + _ZTVN7android13MidiIoWrapperE*; }; diff --git a/media/libstagefright/exports.lds b/media/libstagefright/exports.lds index 59dfc492f7..aabc233f90 100644 --- a/media/libstagefright/exports.lds +++ b/media/libstagefright/exports.lds @@ -9,51 +9,51 @@ _ZN7android16SoftwareRenderer*; ABGRToARGB; ABGRToI420; - ABGRToUVRow_Any_NEON; + ABGRToUVRow_Any_NEON*; ABGRToUVRow_C; - ABGRToUVRow_NEON; - ABGRToYRow_Any_NEON; + ABGRToUVRow_NEON*; + ABGRToYRow_Any_NEON*; ABGRToYRow_C; - ABGRToYRow_NEON; + ABGRToYRow_NEON*; Android420ToI420; ARGB1555ToARGB; - ARGB1555ToARGBRow_Any_NEON; + ARGB1555ToARGBRow_Any_NEON*; ARGB1555ToARGBRow_C; - ARGB1555ToARGBRow_NEON; + ARGB1555ToARGBRow_NEON*; ARGB1555ToI420; - ARGB1555ToUVRow_Any_NEON; + ARGB1555ToUVRow_Any_NEON*; ARGB1555ToUVRow_C; - ARGB1555ToUVRow_NEON; - ARGB1555ToYRow_Any_NEON; + ARGB1555ToUVRow_NEON*; + ARGB1555ToYRow_Any_NEON*; ARGB1555ToYRow_C; - ARGB1555ToYRow_NEON; + ARGB1555ToYRow_NEON*; ARGB4444ToARGB; - ARGB4444ToARGBRow_Any_NEON; + ARGB4444ToARGBRow_Any_NEON*; ARGB4444ToARGBRow_C; - ARGB4444ToARGBRow_NEON; + ARGB4444ToARGBRow_NEON*; ARGB4444ToI420; - ARGB4444ToUVRow_Any_NEON; + ARGB4444ToUVRow_Any_NEON*; ARGB4444ToUVRow_C; - ARGB4444ToUVRow_NEON; - ARGB4444ToYRow_Any_NEON; + ARGB4444ToUVRow_NEON*; + ARGB4444ToYRow_Any_NEON*; ARGB4444ToYRow_C; - ARGB4444ToYRow_NEON; + ARGB4444ToYRow_NEON*; ARGBAdd; - ARGBAddRow_Any_NEON; + ARGBAddRow_Any_NEON*; ARGBAddRow_C; - ARGBAddRow_NEON; + ARGBAddRow_NEON*; ARGBAffineRow_C; ARGBAttenuate; - ARGBAttenuateRow_Any_NEON; + ARGBAttenuateRow_Any_NEON*; ARGBAttenuateRow_C; - ARGBAttenuateRow_NEON; + ARGBAttenuateRow_NEON*; ARGBBlend; ARGBBlendRow_C; - ARGBBlendRow_NEON; + ARGBBlendRow_NEON*; ARGBBlur; ARGBColorMatrix; ARGBColorMatrixRow_C; - ARGBColorMatrixRow_NEON; + ARGBColorMatrixRow_NEON*; ARGBColorTable; ARGBColorTableRow_C; ARGBComputeCumulativeSum; @@ -63,97 +63,97 @@ ARGBCopyYToAlpha; ARGBCopyYToAlphaRow_C; ARGBExtractAlpha; - ARGBExtractAlphaRow_Any_NEON; + ARGBExtractAlphaRow_Any_NEON*; ARGBExtractAlphaRow_C; - ARGBExtractAlphaRow_NEON; + ARGBExtractAlphaRow_NEON*; ARGBGray; ARGBGrayRow_C; - ARGBGrayRow_NEON; + ARGBGrayRow_NEON*; ARGBGrayTo; ARGBInterpolate; ARGBLumaColorTable; ARGBLumaColorTableRow_C; ARGBMirror; - ARGBMirrorRow_Any_NEON; + ARGBMirrorRow_Any_NEON*; ARGBMirrorRow_C; - ARGBMirrorRow_NEON; + ARGBMirrorRow_NEON*; ARGBMultiply; - ARGBMultiplyRow_Any_NEON; + ARGBMultiplyRow_Any_NEON*; ARGBMultiplyRow_C; - ARGBMultiplyRow_NEON; + ARGBMultiplyRow_NEON*; ARGBPolynomial; ARGBPolynomialRow_C; ARGBQuantize; ARGBQuantizeRow_C; - ARGBQuantizeRow_NEON; + ARGBQuantizeRow_NEON*; ARGBRect; ARGBSepia; ARGBSepiaRow_C; - ARGBSepiaRow_NEON; - ARGBSetRow_Any_NEON; + ARGBSepiaRow_NEON*; + ARGBSetRow_Any_NEON*; ARGBSetRow_C; - ARGBSetRow_NEON; + ARGBSetRow_NEON*; ARGBShade; ARGBShadeRow_C; - ARGBShadeRow_NEON; + ARGBShadeRow_NEON*; ARGBShuffle; - ARGBShuffleRow_Any_NEON; + ARGBShuffleRow_Any_NEON*; ARGBShuffleRow_C; - ARGBShuffleRow_NEON; + ARGBShuffleRow_NEON*; ARGBSobel; ARGBSobelToPlane; ARGBSobelXY; ARGBSubtract; - ARGBSubtractRow_Any_NEON; + ARGBSubtractRow_Any_NEON*; ARGBSubtractRow_C; - ARGBSubtractRow_NEON; + ARGBSubtractRow_NEON*; ARGBToABGR; - ARGBToARGB1555Row_Any_NEON; + ARGBToARGB1555Row_Any_NEON*; ARGBToARGB1555Row_C; - ARGBToARGB1555Row_NEON; - ARGBToARGB4444Row_Any_NEON; + ARGBToARGB1555Row_NEON*; + ARGBToARGB4444Row_Any_NEON*; ARGBToARGB4444Row_C; - ARGBToARGB4444Row_NEON; + ARGBToARGB4444Row_NEON*; ARGBToBGRA; ARGBToI420; - ARGBToRAWRow_Any_NEON; + ARGBToRAWRow_Any_NEON*; ARGBToRAWRow_C; - ARGBToRAWRow_NEON; - ARGBToRGB24Row_Any_NEON; + ARGBToRAWRow_NEON*; + ARGBToRGB24Row_Any_NEON*; ARGBToRGB24Row_C; - ARGBToRGB24Row_NEON; - ARGBToRGB565DitherRow_Any_NEON; + ARGBToRGB24Row_NEON*; + ARGBToRGB565DitherRow_Any_NEON*; ARGBToRGB565DitherRow_C; - ARGBToRGB565DitherRow_NEON; - ARGBToRGB565Row_Any_NEON; + ARGBToRGB565DitherRow_NEON*; + ARGBToRGB565Row_Any_NEON*; ARGBToRGB565Row_C; - ARGBToRGB565Row_NEON; - ARGBToUV444Row_Any_NEON; + ARGBToRGB565Row_NEON*; + ARGBToUV444Row_Any_NEON*; ARGBToUV444Row_C; - ARGBToUV444Row_NEON; - ARGBToUVJRow_Any_NEON; + ARGBToUV444Row_NEON*; + ARGBToUVJRow_Any_NEON*; ARGBToUVJRow_C; - ARGBToUVJRow_NEON; - ARGBToUVRow_Any_NEON; + ARGBToUVJRow_NEON*; + ARGBToUVRow_Any_NEON*; ARGBToUVRow_C; - ARGBToUVRow_NEON; - ARGBToYJRow_Any_NEON; + ARGBToUVRow_NEON*; + ARGBToYJRow_Any_NEON*; ARGBToYJRow_C; - ARGBToYJRow_NEON; - ARGBToYRow_Any_NEON; + ARGBToYJRow_NEON*; + ARGBToYRow_Any_NEON*; ARGBToYRow_C; - ARGBToYRow_NEON; + ARGBToYRow_NEON*; ARGBUnattenuate; ARGBUnattenuateRow_C; - ArmCpuCaps; + ArmCpuCaps*; BGRAToARGB; BGRAToI420; - BGRAToUVRow_Any_NEON; + BGRAToUVRow_Any_NEON*; BGRAToUVRow_C; - BGRAToUVRow_NEON; - BGRAToYRow_Any_NEON; + BGRAToUVRow_NEON*; + BGRAToYRow_Any_NEON*; BGRAToYRow_C; - BGRAToYRow_NEON; + BGRAToYRow_NEON*; BlendPlane; BlendPlaneRow_C; CanonicalFourCC; @@ -162,11 +162,11 @@ CopyPlane; CopyPlane_16; CopyRow_16_C; - CopyRow_Any_NEON; + CopyRow_Any_NEON*; CopyRow_C; - CopyRow_NEON; - CpuId; - cpu_info_; + CopyRow_NEON*; + CpuId*; + cpu_info_*; CumulativeSumToAverageRow_C; FixedDiv1_C; FixedDiv_C; @@ -176,18 +176,18 @@ H420ToARGB; H422ToABGR; H422ToARGB; - HalfFloat1Row_Any_NEON; - HalfFloat1Row_NEON; + HalfFloat1Row_Any_NEON*; + HalfFloat1Row_NEON*; HalfFloatPlane; - HalfFloatRow_Any_NEON; + HalfFloatRow_Any_NEON*; HalfFloatRow_C; - HalfFloatRow_NEON; + HalfFloatRow_NEON*; I400Copy; I400Mirror; I400ToARGB; - I400ToARGBRow_Any_NEON; + I400ToARGBRow_Any_NEON*; I400ToARGBRow_C; - I400ToARGBRow_NEON; + I400ToARGBRow_NEON*; I400ToI400; I400ToI420; I420AlphaToABGR; @@ -216,59 +216,59 @@ I420ToRGBA; I420ToUYVY; I420ToYUY2; - I422AlphaToARGBRow_Any_NEON; + I422AlphaToARGBRow_Any_NEON*; I422AlphaToARGBRow_C; - I422AlphaToARGBRow_NEON; + I422AlphaToARGBRow_NEON*; I422Copy; I422ToABGR; I422ToARGB; - I422ToARGB1555Row_Any_NEON; + I422ToARGB1555Row_Any_NEON*; I422ToARGB1555Row_C; - I422ToARGB1555Row_NEON; - I422ToARGB4444Row_Any_NEON; + I422ToARGB1555Row_NEON*; + I422ToARGB4444Row_Any_NEON*; I422ToARGB4444Row_C; - I422ToARGB4444Row_NEON; - I422ToARGBRow_Any_NEON; + I422ToARGB4444Row_NEON*; + I422ToARGBRow_Any_NEON*; I422ToARGBRow_C; - I422ToARGBRow_NEON; + I422ToARGBRow_NEON*; I422ToBGRA; I422ToI420; - I422ToRGB24Row_Any_NEON; + I422ToRGB24Row_Any_NEON*; I422ToRGB24Row_C; - I422ToRGB24Row_NEON; + I422ToRGB24Row_NEON*; I422ToRGB565; - I422ToRGB565Row_Any_NEON; + I422ToRGB565Row_Any_NEON*; I422ToRGB565Row_C; - I422ToRGB565Row_NEON; + I422ToRGB565Row_NEON*; I422ToRGBA; - I422ToRGBARow_Any_NEON; + I422ToRGBARow_Any_NEON*; I422ToRGBARow_C; - I422ToRGBARow_NEON; + I422ToRGBARow_NEON*; I422ToUYVY; - I422ToUYVYRow_Any_NEON; + I422ToUYVYRow_Any_NEON*; I422ToUYVYRow_C; - I422ToUYVYRow_NEON; + I422ToUYVYRow_NEON*; I422ToYUY2; - I422ToYUY2Row_Any_NEON; + I422ToYUY2Row_Any_NEON*; I422ToYUY2Row_C; - I422ToYUY2Row_NEON; + I422ToYUY2Row_NEON*; I444Copy; I444ToABGR; I444ToARGB; - I444ToARGBRow_Any_NEON; + I444ToARGBRow_Any_NEON*; I444ToARGBRow_C; - I444ToARGBRow_NEON; + I444ToARGBRow_NEON*; I444ToI420; - InitCpuFlags; + InitCpuFlags*; InterpolatePlane; InterpolateRow_16_C; - InterpolateRow_Any_NEON; + InterpolateRow_Any_NEON*; InterpolateRow_C; - InterpolateRow_NEON; + InterpolateRow_NEON*; J400ToARGB; - J400ToARGBRow_Any_NEON; + J400ToARGBRow_Any_NEON*; J400ToARGBRow_C; - J400ToARGBRow_NEON; + J400ToARGBRow_NEON*; J420ToABGR; J420ToARGB; J422ToABGR; @@ -282,107 +282,107 @@ kYvuJPEGConstants; M420ToARGB; M420ToI420; - MaskCpuFlags; + MaskCpuFlags*; MergeUVPlane; - MergeUVRow_Any_NEON; + MergeUVRow_Any_NEON*; MergeUVRow_C; - MergeUVRow_NEON; - MipsCpuCaps; + MergeUVRow_NEON*; + MipsCpuCaps*; MirrorPlane; - MirrorRow_Any_NEON; + MirrorRow_Any_NEON*; MirrorRow_C; - MirrorRow_NEON; + MirrorRow_NEON*; MirrorUVRow_C; - MirrorUVRow_NEON; - NV12ToARGBRow_Any_NEON; + MirrorUVRow_NEON*; + NV12ToARGBRow_Any_NEON*; NV12ToARGBRow_C; - NV12ToARGBRow_NEON; + NV12ToARGBRow_NEON*; NV12ToI420; NV12ToRGB565; - NV12ToRGB565Row_Any_NEON; + NV12ToRGB565Row_Any_NEON*; NV12ToRGB565Row_C; - NV12ToRGB565Row_NEON; + NV12ToRGB565Row_NEON*; NV21ToARGB; - NV21ToARGBRow_Any_NEON; + NV21ToARGBRow_Any_NEON*; NV21ToARGBRow_C; - NV21ToARGBRow_NEON; + NV21ToARGBRow_NEON*; NV21ToI420; RAWToARGB; - RAWToARGBRow_Any_NEON; + RAWToARGBRow_Any_NEON*; RAWToARGBRow_C; - RAWToARGBRow_NEON; + RAWToARGBRow_NEON*; RAWToI420; RAWToRGB24; - RAWToRGB24Row_Any_NEON; + RAWToRGB24Row_Any_NEON*; RAWToRGB24Row_C; - RAWToRGB24Row_NEON; - RAWToUVRow_Any_NEON; + RAWToRGB24Row_NEON*; + RAWToUVRow_Any_NEON*; RAWToUVRow_C; - RAWToUVRow_NEON; - RAWToYRow_Any_NEON; + RAWToUVRow_NEON*; + RAWToYRow_Any_NEON*; RAWToYRow_C; - RAWToYRow_NEON; + RAWToYRow_NEON*; RGB24ToARGB; - RGB24ToARGBRow_Any_NEON; + RGB24ToARGBRow_Any_NEON*; RGB24ToARGBRow_C; - RGB24ToARGBRow_NEON; + RGB24ToARGBRow_NEON*; RGB24ToI420; - RGB24ToUVRow_Any_NEON; + RGB24ToUVRow_Any_NEON*; RGB24ToUVRow_C; - RGB24ToUVRow_NEON; - RGB24ToYRow_Any_NEON; + RGB24ToUVRow_NEON*; + RGB24ToYRow_Any_NEON*; RGB24ToYRow_C; - RGB24ToYRow_NEON; + RGB24ToYRow_NEON*; RGB565ToARGB; - RGB565ToARGBRow_Any_NEON; + RGB565ToARGBRow_Any_NEON*; RGB565ToARGBRow_C; - RGB565ToARGBRow_NEON; + RGB565ToARGBRow_NEON*; RGB565ToI420; - RGB565ToUVRow_Any_NEON; + RGB565ToUVRow_Any_NEON*; RGB565ToUVRow_C; - RGB565ToUVRow_NEON; - RGB565ToYRow_Any_NEON; + RGB565ToUVRow_NEON*; + RGB565ToYRow_Any_NEON*; RGB565ToYRow_C; - RGB565ToYRow_NEON; + RGB565ToYRow_NEON*; RGBAToARGB; RGBAToI420; - RGBAToUVRow_Any_NEON; + RGBAToUVRow_Any_NEON*; RGBAToUVRow_C; - RGBAToUVRow_NEON; - RGBAToYRow_Any_NEON; + RGBAToUVRow_NEON*; + RGBAToYRow_Any_NEON*; RGBAToYRow_C; - RGBAToYRow_NEON; + RGBAToYRow_NEON*; RGBColorMatrix; RGBColorTable; RGBColorTableRow_C; Scale; ScaleAddRow_16_C; ScaleAddRow_C; - ScaleAddRows_NEON; + ScaleAddRows_NEON*; ScaleARGBCols64_C; - ScaleARGBCols_Any_NEON; + ScaleARGBCols_Any_NEON*; ScaleARGBCols_C; - ScaleARGBCols_NEON; + ScaleARGBCols_NEON*; ScaleARGBColsUp2_C; ScaleARGBFilterCols64_C; - ScaleARGBFilterCols_Any_NEON; + ScaleARGBFilterCols_Any_NEON*; ScaleARGBFilterCols_C; - ScaleARGBFilterCols_NEON; - ScaleARGBRowDown2_Any_NEON; - ScaleARGBRowDown2Box_Any_NEON; + ScaleARGBFilterCols_NEON*; + ScaleARGBRowDown2_Any_NEON*; + ScaleARGBRowDown2Box_Any_NEON*; ScaleARGBRowDown2Box_C; - ScaleARGBRowDown2Box_NEON; + ScaleARGBRowDown2Box_NEON*; ScaleARGBRowDown2_C; - ScaleARGBRowDown2Linear_Any_NEON; + ScaleARGBRowDown2Linear_Any_NEON*; ScaleARGBRowDown2Linear_C; - ScaleARGBRowDown2Linear_NEON; - ScaleARGBRowDown2_NEON; - ScaleARGBRowDownEven_Any_NEON; - ScaleARGBRowDownEvenBox_Any_NEON; + ScaleARGBRowDown2Linear_NEON*; + ScaleARGBRowDown2_NEON*; + ScaleARGBRowDownEven_Any_NEON*; + ScaleARGBRowDownEvenBox_Any_NEON*; ScaleARGBRowDownEvenBox_C; - ScaleARGBRowDownEvenBox_NEON; + ScaleARGBRowDownEvenBox_NEON*; ScaleARGBRowDownEven_C; - ScaleARGBRowDownEven_NEON; + ScaleARGBRowDownEven_NEON*; ScaleCols_16_C; ScaleCols_C; ScaleColsUp2_16_C; @@ -390,11 +390,11 @@ ScaleFilterCols_16_C; ScaleFilterCols64_16_C; ScaleFilterCols64_C; - ScaleFilterCols_Any_NEON; + ScaleFilterCols_Any_NEON*; ScaleFilterCols_C; - ScaleFilterCols_NEON; + ScaleFilterCols_NEON*; ScaleFilterReduce; - ScaleFilterRows_NEON; + ScaleFilterRows_NEON*; ScaleOffset; ScalePlane; ScalePlane_16; @@ -405,104 +405,104 @@ ScalePlaneVertical; ScalePlaneVertical_16; ScaleRowDown2_16_C; - ScaleRowDown2_Any_NEON; + ScaleRowDown2_Any_NEON*; ScaleRowDown2Box_16_C; - ScaleRowDown2Box_Any_NEON; + ScaleRowDown2Box_Any_NEON*; ScaleRowDown2Box_C; - ScaleRowDown2Box_NEON; + ScaleRowDown2Box_NEON*; ScaleRowDown2Box_Odd_C; - ScaleRowDown2Box_Odd_NEON; + ScaleRowDown2Box_Odd_NEON*; ScaleRowDown2_C; ScaleRowDown2Linear_16_C; - ScaleRowDown2Linear_Any_NEON; + ScaleRowDown2Linear_Any_NEON*; ScaleRowDown2Linear_C; - ScaleRowDown2Linear_NEON; - ScaleRowDown2_NEON; + ScaleRowDown2Linear_NEON*; + ScaleRowDown2_NEON*; ScaleRowDown34_0_Box_16_C; - ScaleRowDown34_0_Box_Any_NEON; + ScaleRowDown34_0_Box_Any_NEON*; ScaleRowDown34_0_Box_C; - ScaleRowDown34_0_Box_NEON; + ScaleRowDown34_0_Box_NEON*; ScaleRowDown34_16_C; ScaleRowDown34_1_Box_16_C; - ScaleRowDown34_1_Box_Any_NEON; + ScaleRowDown34_1_Box_Any_NEON*; ScaleRowDown34_1_Box_C; - ScaleRowDown34_1_Box_NEON; - ScaleRowDown34_Any_NEON; + ScaleRowDown34_1_Box_NEON*; + ScaleRowDown34_Any_NEON*; ScaleRowDown34_C; - ScaleRowDown34_NEON; + ScaleRowDown34_NEON*; ScaleRowDown38_16_C; ScaleRowDown38_2_Box_16_C; - ScaleRowDown38_2_Box_Any_NEON; + ScaleRowDown38_2_Box_Any_NEON*; ScaleRowDown38_2_Box_C; - ScaleRowDown38_2_Box_NEON; + ScaleRowDown38_2_Box_NEON*; ScaleRowDown38_3_Box_16_C; - ScaleRowDown38_3_Box_Any_NEON; + ScaleRowDown38_3_Box_Any_NEON*; ScaleRowDown38_3_Box_C; - ScaleRowDown38_3_Box_NEON; - ScaleRowDown38_Any_NEON; + ScaleRowDown38_3_Box_NEON*; + ScaleRowDown38_Any_NEON*; ScaleRowDown38_C; - ScaleRowDown38_NEON; + ScaleRowDown38_NEON*; ScaleRowDown4_16_C; - ScaleRowDown4_Any_NEON; + ScaleRowDown4_Any_NEON*; ScaleRowDown4Box_16_C; - ScaleRowDown4Box_Any_NEON; + ScaleRowDown4Box_Any_NEON*; ScaleRowDown4Box_C; - ScaleRowDown4Box_NEON; + ScaleRowDown4Box_NEON*; ScaleRowDown4_C; - ScaleRowDown4_NEON; + ScaleRowDown4_NEON*; ScaleSlope; SetPlane; - SetRow_Any_NEON; + SetRow_Any_NEON*; SetRow_C; - SetRow_NEON; - SobelRow_Any_NEON; + SetRow_NEON*; + SobelRow_Any_NEON*; SobelRow_C; - SobelRow_NEON; - SobelToPlaneRow_Any_NEON; + SobelRow_NEON*; + SobelToPlaneRow_Any_NEON*; SobelToPlaneRow_C; - SobelToPlaneRow_NEON; + SobelToPlaneRow_NEON*; SobelXRow_C; - SobelXRow_NEON; - SobelXYRow_Any_NEON; + SobelXRow_NEON*; + SobelXYRow_Any_NEON*; SobelXYRow_C; - SobelXYRow_NEON; + SobelXYRow_NEON*; SobelYRow_C; - SobelYRow_NEON; + SobelYRow_NEON*; SplitUVPlane; - SplitUVRow_Any_NEON; + SplitUVRow_Any_NEON*; SplitUVRow_C; - SplitUVRow_NEON; + SplitUVRow_NEON*; UYVYToARGB; - UYVYToARGBRow_Any_NEON; + UYVYToARGBRow_Any_NEON*; UYVYToARGBRow_C; - UYVYToARGBRow_NEON; + UYVYToARGBRow_NEON*; UYVYToI420; UYVYToI422; UYVYToNV12; - UYVYToUV422Row_Any_NEON; + UYVYToUV422Row_Any_NEON*; UYVYToUV422Row_C; - UYVYToUV422Row_NEON; - UYVYToUVRow_Any_NEON; + UYVYToUV422Row_NEON*; + UYVYToUVRow_Any_NEON*; UYVYToUVRow_C; - UYVYToUVRow_NEON; - UYVYToYRow_Any_NEON; + UYVYToUVRow_NEON*; + UYVYToYRow_Any_NEON*; UYVYToYRow_C; - UYVYToYRow_NEON; + UYVYToYRow_NEON*; YUY2ToARGB; - YUY2ToARGBRow_Any_NEON; + YUY2ToARGBRow_Any_NEON*; YUY2ToARGBRow_C; - YUY2ToARGBRow_NEON; + YUY2ToARGBRow_NEON*; YUY2ToI420; YUY2ToI422; YUY2ToNV12; - YUY2ToUV422Row_Any_NEON; + YUY2ToUV422Row_Any_NEON*; YUY2ToUV422Row_C; - YUY2ToUV422Row_NEON; - YUY2ToUVRow_Any_NEON; + YUY2ToUV422Row_NEON*; + YUY2ToUVRow_Any_NEON*; YUY2ToUVRow_C; - YUY2ToUVRow_NEON; + YUY2ToUVRow_NEON*; YUY2ToY; - YUY2ToYRow_Any_NEON; + YUY2ToYRow_Any_NEON*; YUY2ToYRow_C; - YUY2ToYRow_NEON; + YUY2ToYRow_NEON*; }; -- GitLab From fcd1537dcd1e974ec9b204cc4ce3ab4a8aec09b1 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 9 Apr 2018 12:13:06 +0900 Subject: [PATCH 0040/1530] Mark libmedia_omx and libstagefright_foundation as double_loadable These two are explicitly marked as double_loadable since they are one of the dependencies of the LLNDK library libnativewindow (libmediandk) and at the same time the lib themselves are marked as VNDK. Such libs can be double loaded inside a vendor process. Note: even without this change, the library is already capable of being double loaded due to the dependency graph around it. This change is to make it explicit so that double loading of a library is carefully tracked and signed-off by the owner of the lib. Bug: 77155589 Test: m -j Change-Id: I38e3ff4a76930a99a04f3dbc53a2dcfea18de8f3 --- media/libmedia/Android.bp | 1 + media/libstagefright/foundation/Android.bp | 1 + 2 files changed, 2 insertions(+) diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 3990e697f9..4e1e4c7558 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -39,6 +39,7 @@ cc_library_shared { vndk: { enabled: true, }, + double_loadable: true, srcs: [ "aidl/android/IGraphicBufferSource.aidl", diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index f663542bfc..6b384c0182 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -10,6 +10,7 @@ cc_library { vndk: { enabled: true, }, + double_loadable: true, include_dirs: [ "frameworks/av/include", "frameworks/native/include", -- GitLab From 7e6ac73f9cc1d78760f73fee2c55d0a3a3a19881 Mon Sep 17 00:00:00 2001 From: Previr Rangroo Date: Mon, 13 Nov 2017 20:20:20 -0800 Subject: [PATCH 0041/1530] stagefright: Add AC4 support in MediaExtractor for MP4/TS * Add AC4 mime type * Add AC-4 support to TS extractor ** Extract PES private data and AC4 descriptor. ** Dequeue and parse AC-4 access unit and sync frame ** Validate AC4 elementary stream header and parse sampling rate. * Add AC-4 support to MPEG4Extractor ** Parse AC-4 sample entry in MP4 and set AC-4 MIME ** Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame * Add AC4 DSI Parser ** In order to populate the AudioPresentation objects we need to extract the AC4 presentation information from the dsi that is written to the MPEG4 file during muxing. Change-Id: If84c24ca475cabf3e0d2bdf3e4850aeeb185a0de Signed-off-by: Previr Rangroo --- cmds/stagefright/stagefright.cpp | 2 +- media/extractors/mp4/AC4Parser.cpp | 624 ++++++++++++++++++ media/extractors/mp4/AC4Parser.h | 109 +++ media/extractors/mp4/Android.bp | 1 + media/extractors/mp4/MPEG4Extractor.cpp | 156 ++++- media/extractors/mp4/MPEG4Extractor.h | 2 + media/libstagefright/foundation/MediaDefs.cpp | 1 + .../media/stagefright/foundation/MediaDefs.h | 1 + media/libstagefright/mpeg2ts/ATSParser.cpp | 74 ++- media/libstagefright/mpeg2ts/ATSParser.h | 15 + media/libstagefright/mpeg2ts/ESQueue.cpp | 188 ++++++ media/libstagefright/mpeg2ts/ESQueue.h | 2 + 12 files changed, 1156 insertions(+), 19 deletions(-) create mode 100644 media/extractors/mp4/AC4Parser.cpp create mode 100644 media/extractors/mp4/AC4Parser.h diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index bd25ceeb64..a2574ea55f 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -640,7 +640,7 @@ static void dumpCodecProfiles(bool queryDecoders) { MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9, - MEDIA_MIMETYPE_VIDEO_DOLBY_VISION + MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4 }; const char *codecType = queryDecoders? "decoder" : "encoder"; diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp new file mode 100644 index 0000000000..167d474b23 --- /dev/null +++ b/media/extractors/mp4/AC4Parser.cpp @@ -0,0 +1,624 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "AC4Parser" + +#include +#include +#include + +#include "AC4Parser.h" + +#define BOOLSTR(a) ((a)?"true":"false") +#define BYTE_ALIGN mBitReader.skipBits(mBitReader.numBitsLeft() % 8) +#define CHECK_BITS_LEFT(n) if (mBitReader.numBitsLeft() < n) {return false;} + +namespace android { + +AC4Parser::AC4Parser() { +} + +AC4DSIParser::AC4DSIParser(ABitReader &br) + : mBitReader(br){ + + mDSISize = mBitReader.numBitsLeft(); +} + +// ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode +static const char *ChannelModes[] = { + "mono", + "stereo", + "3.0", + "5.0", + "5.1", + "7.0 (3/4/0)", + "7.1 (3/4/0.1)", + "7.0 (5/2/0)", + "7.1 (5/2/0.1)", + "7.0 (3/2/2)", + "7.1 (3/2/2.1)", + "7.0.4", + "7.1.4", + "9.0.4", + "9.1.4", + "22.2" +}; + +static const char* ContentClassifier[] = { + "Complete Main", + "Music and Effects", + "Visually Impaired", + "Hearing Impaired", + "Dialog", + "Commentary", + "Emergency", + "Voice Over" +}; + +bool AC4DSIParser::parseLanguageTag(uint32_t presentationID, uint32_t substreamID){ + CHECK_BITS_LEFT(6); + uint32_t n_language_tag_bytes = mBitReader.getBits(6); + if (n_language_tag_bytes < 2 || n_language_tag_bytes >= 42) { + return false; + } + CHECK_BITS_LEFT(n_language_tag_bytes * 8); + char language_tag_bytes[42]; // TS 103 190 part 1 4.3.3.8.7 + for (uint32_t i = 0; i < n_language_tag_bytes; i++) { + language_tag_bytes[i] = (char)mBitReader.getBits(8); + } + language_tag_bytes[n_language_tag_bytes] = 0; + ALOGV("%u.%u: language_tag = %s\n", presentationID, substreamID, language_tag_bytes); + + std::string language(language_tag_bytes, n_language_tag_bytes); + mPresentations[presentationID].mLanguage = language; + + return true; +} + +// TS 103 190-1 v1.2.1 E.5 and TS 103 190-2 v1.1.1 E.9 +bool AC4DSIParser::parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID){ + CHECK_BITS_LEFT(5); + uint32_t channel_mode = mBitReader.getBits(5); + CHECK_BITS_LEFT(2); + uint32_t dsi_sf_multiplier = mBitReader.getBits(2); + CHECK_BITS_LEFT(1); + bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u: channel_mode = %u (%s)\n", presentationID, substreamID, channel_mode, + channel_mode < NELEM(ChannelModes) ? ChannelModes[channel_mode] : "reserved"); + ALOGV("%u.%u: dsi_sf_multiplier = %u\n", presentationID, + substreamID, dsi_sf_multiplier); + ALOGV("%u.%u: b_substream_bitrate_indicator = %s\n", presentationID, + substreamID, BOOLSTR(b_substream_bitrate_indicator)); + + if (b_substream_bitrate_indicator) { + CHECK_BITS_LEFT(5); + uint32_t substream_bitrate_indicator = mBitReader.getBits(5); + ALOGV("%u.%u: substream_bitrate_indicator = %u\n", presentationID, substreamID, + substream_bitrate_indicator); + } + if (channel_mode >= 7 && channel_mode <= 10) { + CHECK_BITS_LEFT(1); + uint32_t add_ch_base = mBitReader.getBits(1); + ALOGV("%u.%u: add_ch_base = %u\n", presentationID, substreamID, add_ch_base); + } + CHECK_BITS_LEFT(1); + bool b_content_type = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u: b_content_type = %s\n", presentationID, substreamID, BOOLSTR(b_content_type)); + if (b_content_type) { + CHECK_BITS_LEFT(3); + uint32_t content_classifier = mBitReader.getBits(3); + ALOGV("%u.%u: content_classifier = %u (%s)\n", presentationID, substreamID, + content_classifier, ContentClassifier[content_classifier]); + + // For streams based on TS 103 190 part 1 the presentation level channel_mode doesn't + // exist and so we use the channel_mode from either the CM or M&E substream + // (they are mutually exclusive) + if (mPresentations[presentationID].mChannelMode == -1 && + (content_classifier == 0 || content_classifier == 1)) { + mPresentations[presentationID].mChannelMode = channel_mode; + } + mPresentations[presentationID].mContentClassifier = content_classifier; + CHECK_BITS_LEFT(1); + bool b_language_indicator = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, substreamID, + BOOLSTR(b_language_indicator)); + if (b_language_indicator) { + if (!parseLanguageTag(presentationID, substreamID)) { + return false; + } + } + } + + return true; +} + +// ETSI TS 103 190-2 v1.1.1 section E.11 +bool AC4DSIParser::parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID) +{ + CHECK_BITS_LEFT(1); + bool b_substreams_present = (mBitReader.getBits(1) == 1); + CHECK_BITS_LEFT(1); + bool b_hsf_ext = (mBitReader.getBits(1) == 1); + CHECK_BITS_LEFT(1); + bool b_channel_coded = (mBitReader.getBits(1) == 1); + CHECK_BITS_LEFT(8); + uint32_t n_substreams = mBitReader.getBits(8); + ALOGV("%u.%u: b_substreams_present = %s\n", presentationID, groupID, + BOOLSTR(b_substreams_present)); + ALOGV("%u.%u: b_hsf_ext = %s\n", presentationID, groupID, BOOLSTR(b_hsf_ext)); + ALOGV("%u.%u: b_channel_coded = %s\n", presentationID, groupID, BOOLSTR(b_channel_coded)); + ALOGV("%u.%u: n_substreams = %u\n", presentationID, groupID, n_substreams); + + for (uint32_t i = 0; i < n_substreams; i++) { + CHECK_BITS_LEFT(2); + uint32_t dsi_sf_multiplier = mBitReader.getBits(2); + CHECK_BITS_LEFT(1); + bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u.%u: dsi_sf_multiplier = %u\n", presentationID, groupID, i, dsi_sf_multiplier); + ALOGV("%u.%u.%u: b_substream_bitrate_indicator = %s\n", presentationID, groupID, i, + BOOLSTR(b_substream_bitrate_indicator)); + + if (b_substream_bitrate_indicator) { + CHECK_BITS_LEFT(5); + uint32_t substream_bitrate_indicator = mBitReader.getBits(5); + ALOGV("%u.%u.%u: substream_bitrate_indicator = %u\n", presentationID, groupID, i, + substream_bitrate_indicator); + } + if (b_channel_coded) { + CHECK_BITS_LEFT(24); + uint32_t dsi_substream_channel_mask = mBitReader.getBits(24); + ALOGV("%u.%u.%u: dsi_substream_channel_mask = 0x%06x\n", presentationID, groupID, i, + dsi_substream_channel_mask); + } else { + CHECK_BITS_LEFT(1); + bool b_ajoc = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u.%u: b_ajoc = %s\n", presentationID, groupID, i, BOOLSTR(b_ajoc)); + if (b_ajoc) { + CHECK_BITS_LEFT(1); + bool b_static_dmx = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u.%u: b_static_dmx = %s\n", presentationID, groupID, i, + BOOLSTR(b_static_dmx)); + if (!b_static_dmx) { + CHECK_BITS_LEFT(4); + uint32_t n_dmx_objects_minus1 = mBitReader.getBits(4); + ALOGV("%u.%u.%u: n_dmx_objects_minus1 = %u\n", presentationID, groupID, i, + n_dmx_objects_minus1); + } + CHECK_BITS_LEFT(6); + uint32_t n_umx_objects_minus1 = mBitReader.getBits(6); + ALOGV("%u.%u.%u: n_umx_objects_minus1 = %u\n", presentationID, groupID, i, + n_umx_objects_minus1); + } + CHECK_BITS_LEFT(4); + mBitReader.skipBits(4); // objects_assignment_mask + } + } + + CHECK_BITS_LEFT(1); + bool b_content_type = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u: b_content_type = %s\n", presentationID, groupID, BOOLSTR(b_content_type)); + if (b_content_type) { + CHECK_BITS_LEFT(3); + uint32_t content_classifier = mBitReader.getBits(3); + ALOGV("%u.%u: content_classifier = %s (%u)\n", presentationID, groupID, + ContentClassifier[content_classifier], content_classifier); + + mPresentations[presentationID].mContentClassifier = content_classifier; + + CHECK_BITS_LEFT(1); + bool b_language_indicator = (mBitReader.getBits(1) == 1); + ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, groupID, + BOOLSTR(b_language_indicator)); + + if (b_language_indicator) { + if (!parseLanguageTag(presentationID, groupID)) { + return false; + } + } + } + + return true; +} + +bool AC4DSIParser::parseBitrateDsi() { + CHECK_BITS_LEFT(2 + 32 + 32); + mBitReader.skipBits(2); // bit_rate_mode + mBitReader.skipBits(32); // bit_rate + mBitReader.skipBits(32); // bit_rate_precision + + return true; +} + +// TS 103 190-1 section E.4 (ac4_dsi) and TS 103 190-2 section E.6 (ac4_dsi_v1) +bool AC4DSIParser::parse() { + CHECK_BITS_LEFT(3); + uint32_t ac4_dsi_version = mBitReader.getBits(3); + if (ac4_dsi_version > 1) { + ALOGE("error while parsing ac-4 dsi: only versions 0 and 1 are supported"); + return false; + } + + CHECK_BITS_LEFT(7 + 1 + 4 + 9); + uint32_t bitstream_version = mBitReader.getBits(7); + mBitReader.skipBits(1); // fs_index + mBitReader.skipBits(4); // frame_rate_index + uint32_t n_presentations = mBitReader.getBits(9); + + int32_t short_program_id = -1; + if (bitstream_version > 1) { + if (ac4_dsi_version == 0){ + ALOGE("invalid ac4 dsi"); + return false; + } + CHECK_BITS_LEFT(1); + bool b_program_id = (mBitReader.getBits(1) == 1); + if (b_program_id) { + CHECK_BITS_LEFT(16 + 1); + short_program_id = mBitReader.getBits(16); + bool b_uuid = (mBitReader.getBits(1) == 1); + if (b_uuid) { + const uint32_t kAC4UUIDSizeInBytes = 16; + char program_uuid[kAC4UUIDSizeInBytes]; + CHECK_BITS_LEFT(kAC4UUIDSizeInBytes * 8); + for (uint32_t i = 0; i < kAC4UUIDSizeInBytes; i++) { + program_uuid[i] = (char)(mBitReader.getBits(8)); + } + ALOGV("UUID = %s", program_uuid); + } + } + } + + if (ac4_dsi_version == 1) { + if (!parseBitrateDsi()) { + return false; + } + BYTE_ALIGN; + } + + for (uint32_t presentation = 0; presentation < n_presentations; presentation++) { + mPresentations[presentation].mProgramID = short_program_id; + // known as b_single_substream in ac4_dsi_version 0 + bool b_single_substream_group = false; + uint32_t presentation_config = 0, presentation_version = 0; + uint32_t pres_bytes = 0; + + if (ac4_dsi_version == 0) { + CHECK_BITS_LEFT(1 + 5 + 5); + b_single_substream_group = (mBitReader.getBits(1) == 1); + presentation_config = mBitReader.getBits(5); + presentation_version = mBitReader.getBits(5); + } else if (ac4_dsi_version == 1) { + CHECK_BITS_LEFT(8 + 8); + presentation_version = mBitReader.getBits(8); + pres_bytes = mBitReader.getBits(8); + if (pres_bytes == 0xff) { + CHECK_BITS_LEFT(16); + pres_bytes += mBitReader.getBits(16); + } + ALOGV("%u: pres_bytes = %u\n", presentation, pres_bytes); + if (presentation_version > 1) { + CHECK_BITS_LEFT(pres_bytes * 8); + mBitReader.skipBits(pres_bytes * 8); + continue; + } + // ac4_presentation_v0_dsi() and ac4_presentation_v1_dsi() both + // start with a presentation_config of 5 bits + CHECK_BITS_LEFT(5); + presentation_config = mBitReader.getBits(5); + b_single_substream_group = (presentation_config == 0x1f); + } + + static const char *PresentationConfig[] = { + "Music&Effects + Dialog", + "Main + DE", + "Main + Associate", + "Music&Effects + Dialog + Associate", + "Main + DE + Associate", + "Arbitrary substream groups", + "EMDF only" + }; + ALOGV("%u: b_single_substream/group = %s\n", presentation, + BOOLSTR(b_single_substream_group)); + ALOGV("%u: presentation_version = %u\n", presentation, presentation_version); + ALOGV("%u: presentation_config = %u (%s)\n", presentation, presentation_config, + (presentation_config >= NELEM(PresentationConfig) ? + "reserved" : PresentationConfig[presentation_config])); + + /* record a marker, less the size of the presentation_config */ + uint64_t start = (mDSISize - mBitReader.numBitsLeft()) / 8; + + bool b_add_emdf_substreams = false; + if (!b_single_substream_group && presentation_config == 6) { + b_add_emdf_substreams = true; + ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, BOOLSTR(b_add_emdf_substreams)); + } else { + CHECK_BITS_LEFT(3 + 1); + uint32_t mdcompat = mBitReader.getBits(3); + ALOGV("%u: mdcompat = %d\n", presentation, mdcompat); + + bool b_presentation_group_index = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_presentation_group_index = %s\n", presentation, + BOOLSTR(b_presentation_group_index)); + if (b_presentation_group_index) { + CHECK_BITS_LEFT(5); + mPresentations[presentation].mGroupIndex = mBitReader.getBits(5); + ALOGV("%u: presentation_group_index = %d\n", presentation, + mPresentations[presentation].mGroupIndex); + } + CHECK_BITS_LEFT(2); + uint32_t dsi_frame_rate_multiply_info = mBitReader.getBits(2); + ALOGV("%u: dsi_frame_rate_multiply_info = %d\n", presentation, + dsi_frame_rate_multiply_info); + if (ac4_dsi_version == 1 && presentation_version == 1) { + CHECK_BITS_LEFT(2); + uint32_t dsi_frame_rate_fraction_info = mBitReader.getBits(2); + ALOGV("%u: dsi_frame_rate_fraction_info = %d\n", presentation, + dsi_frame_rate_fraction_info); + } + CHECK_BITS_LEFT(5 + 10); + uint32_t presentation_emdf_version = mBitReader.getBits(5); + uint32_t presentation_key_id = mBitReader.getBits(10); + ALOGV("%u: presentation_emdf_version = %d\n", presentation, presentation_emdf_version); + ALOGV("%u: presentation_key_id = %d\n", presentation, presentation_key_id); + + if (ac4_dsi_version == 1) { + bool b_presentation_channel_coded = false; + if (presentation_version == 0) { + b_presentation_channel_coded = true; + } else { + CHECK_BITS_LEFT(1); + b_presentation_channel_coded = (mBitReader.getBits(1) == 1); + } + ALOGV("%u: b_presentation_channel_coded = %s\n", presentation, + BOOLSTR(b_presentation_channel_coded)); + if (b_presentation_channel_coded) { + if (presentation_version == 1) { + CHECK_BITS_LEFT(5); + uint32_t dsi_presentation_ch_mode = mBitReader.getBits(5); + mPresentations[presentation].mChannelMode = dsi_presentation_ch_mode; + ALOGV("%u: dsi_presentation_ch_mode = %d (%s)\n", presentation, + dsi_presentation_ch_mode, + dsi_presentation_ch_mode < NELEM(ChannelModes) ? + ChannelModes[dsi_presentation_ch_mode] : "reserved"); + + if (dsi_presentation_ch_mode >= 11 && dsi_presentation_ch_mode <= 14) { + CHECK_BITS_LEFT(1 + 2); + uint32_t pres_b_4_back_channels_present = mBitReader.getBits(1); + uint32_t pres_top_channel_pairs = mBitReader.getBits(2); + ALOGV("%u: pres_b_4_back_channels_present = %s\n", presentation, + BOOLSTR(pres_b_4_back_channels_present)); + ALOGV("%u: pres_top_channel_pairs = %d\n", presentation, + pres_top_channel_pairs); + } + } + // presentation_channel_mask in ac4_presentation_v0_dsi() + CHECK_BITS_LEFT(24); + uint32_t presentation_channel_mask_v1 = mBitReader.getBits(24); + ALOGV("%u: presentation_channel_mask_v1 = 0x%06x\n", presentation, + presentation_channel_mask_v1); + } + if (presentation_version == 1) { + CHECK_BITS_LEFT(1); + bool b_presentation_core_differs = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_presentation_core_differs = %s\n", presentation, + BOOLSTR(b_presentation_core_differs)); + if (b_presentation_core_differs) { + CHECK_BITS_LEFT(1); + bool b_presentation_core_channel_coded = (mBitReader.getBits(1) == 1); + if (b_presentation_core_channel_coded) { + CHECK_BITS_LEFT(2); + mBitReader.skipBits(2); // dsi_presentation_channel_mode_core + } + } + CHECK_BITS_LEFT(1); + bool b_presentation_filter = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_presentation_filter = %s\n", presentation, + BOOLSTR(b_presentation_filter)); + if (b_presentation_filter) { + CHECK_BITS_LEFT(1 + 8); + bool b_enable_presentation = (mBitReader.getBits(1) == 1); + if (!b_enable_presentation) { + mPresentations[presentation].mEnabled = false; + } + ALOGV("%u: b_enable_presentation = %s\n", presentation, + BOOLSTR(b_enable_presentation)); + uint32_t n_filter_bytes = mBitReader.getBits(8); + CHECK_BITS_LEFT(n_filter_bytes * 8); + for (uint32_t i = 0; i < n_filter_bytes; i++) { + mBitReader.skipBits(8); // filter_data + } + } + } + } /* ac4_dsi_version == 1 */ + + if (b_single_substream_group) { + if (presentation_version == 0) { + if (!parseSubstreamDSI(presentation, 0)) { + return false; + } + } else { + if (!parseSubstreamGroupDSI(presentation, 0)) { + return false; + } + } + } else { + if (ac4_dsi_version == 1) { + CHECK_BITS_LEFT(1); + bool b_multi_pid = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_multi_pid = %s\n", presentation, BOOLSTR(b_multi_pid)); + } else { + CHECK_BITS_LEFT(1); + bool b_hsf_ext = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_hsf_ext = %s\n", presentation, BOOLSTR(b_hsf_ext)); + } + switch (presentation_config) { + case 0: + case 1: + case 2: + if (presentation_version == 0) { + if (!parseSubstreamDSI(presentation, 0)) { + return false; + } + if (!parseSubstreamDSI(presentation, 1)) { + return false; + } + } else { + if (!parseSubstreamGroupDSI(presentation, 0)) { + return false; + } + if (!parseSubstreamGroupDSI(presentation, 1)) { + return false; + } + } + break; + case 3: + case 4: + if (presentation_version == 0) { + if (!parseSubstreamDSI(presentation, 0)) { + return false; + } + if (!parseSubstreamDSI(presentation, 1)) { + return false; + } + if (!parseSubstreamDSI(presentation, 2)) { + return false; + } + } else { + if (!parseSubstreamGroupDSI(presentation, 0)) { + return false; + } + if (!parseSubstreamGroupDSI(presentation, 1)) { + return false; + } + if (!parseSubstreamGroupDSI(presentation, 2)) { + return false; + } + } + break; + case 5: + if (presentation_version == 0) { + if (!parseSubstreamDSI(presentation, 0)) { + return false; + } + } else { + CHECK_BITS_LEFT(3); + uint32_t n_substream_groups_minus2 = mBitReader.getBits(3); + ALOGV("%u: n_substream_groups_minus2 = %d\n", presentation, + n_substream_groups_minus2); + for (uint32_t sg = 0; sg < n_substream_groups_minus2 + 2; sg++) { + if (!parseSubstreamGroupDSI(presentation, sg)) { + return false; + } + } + } + break; + default: + CHECK_BITS_LEFT(7); + uint32_t n_skip_bytes = mBitReader.getBits(7); + CHECK_BITS_LEFT(n_skip_bytes * 8) + for (uint32_t j = 0; j < n_skip_bytes; j++) { + mBitReader.getBits(8); + } + break; + } + CHECK_BITS_LEFT(1 + 1); + bool b_pre_virtualized = (mBitReader.getBits(1) == 1); + mPresentations[presentation].mPreVirtualized = b_pre_virtualized; + b_add_emdf_substreams = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized)); + ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, + BOOLSTR(b_add_emdf_substreams)); + } + } + if (b_add_emdf_substreams) { + CHECK_BITS_LEFT(7); + uint32_t n_add_emdf_substreams = mBitReader.getBits(7); + for (uint32_t j = 0; j < n_add_emdf_substreams; j++) { + CHECK_BITS_LEFT(5 + 10); + uint32_t substream_emdf_version = mBitReader.getBits(5); + uint32_t substream_key_id = mBitReader.getBits(10); + ALOGV("%u: emdf_substream[%d]: version=%d, key_id=%d\n", presentation, j, + substream_emdf_version, substream_key_id); + } + } + + bool b_presentation_bitrate_info = false; + if (presentation_version > 0) { + CHECK_BITS_LEFT(1); + b_presentation_bitrate_info = (mBitReader.getBits(1) == 1); + } + + ALOGV("b_presentation_bitrate_info = %s\n", BOOLSTR(b_presentation_bitrate_info)); + if (b_presentation_bitrate_info) { + if (!parseBitrateDsi()) { + return false; + } + } + + if (presentation_version > 0) { + CHECK_BITS_LEFT(1); + bool b_alternative = (mBitReader.getBits(1) == 1); + ALOGV("b_alternative = %s\n", BOOLSTR(b_alternative)); + if (b_alternative) { + BYTE_ALIGN; + CHECK_BITS_LEFT(16); + uint32_t name_len = mBitReader.getBits(16); + char* presentation_name = new char[name_len+1]; + CHECK_BITS_LEFT(name_len * 8); + for (uint32_t i = 0; i < name_len; i++) { + presentation_name[i] = (char)(mBitReader.getBits(8)); + } + presentation_name[name_len] = '\0'; + std::string description(presentation_name, name_len); + mPresentations[presentation].mDescription = description; + CHECK_BITS_LEFT(5); + uint32_t n_targets = mBitReader.getBits(5); + CHECK_BITS_LEFT(n_targets * (3 + 8)); + for (uint32_t i = 0; i < n_targets; i++){ + mBitReader.skipBits(3); // target_md_compat + mBitReader.skipBits(8); // target_device_category + } + } + } + + BYTE_ALIGN; + + if (ac4_dsi_version == 1) { + uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8; + if (mBitReader.numBitsLeft() % 8 != 0) { + end += 1; + } + + uint64_t presentation_bytes = end - start; + uint64_t skip_bytes = pres_bytes - presentation_bytes; + ALOGV("skipping = %" PRIu64 " bytes", skip_bytes); + CHECK_BITS_LEFT(skip_bytes * 8); + mBitReader.skipBits(skip_bytes * 8); + } + + // we should know this or something is probably wrong + // with the bitstream (or we don't support it) + if (mPresentations[presentation].mChannelMode == -1){ + ALOGE("could not determing channel mode of presentation %d", presentation); + return false; + } + } /* each presentation */ + + return true; +} + +}; diff --git a/media/extractors/mp4/AC4Parser.h b/media/extractors/mp4/AC4Parser.h new file mode 100644 index 0000000000..73b6e31d63 --- /dev/null +++ b/media/extractors/mp4/AC4Parser.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef AC4_PARSER_H_ +#define AC4_PARSER_H_ + +#include +#include +#include + +#include + +namespace android { + +class AC4Parser { +public: + AC4Parser(); + virtual ~AC4Parser() { } + + virtual bool parse() = 0; + + struct AC4Presentation { + int32_t mChannelMode = -1; + int32_t mProgramID = -1; + int32_t mGroupIndex = -1; + + // TS 103 190-1 v1.2.1 4.3.3.8.1 + enum ContentClassifiers { + kCompleteMain, + kMusicAndEffects, + kVisuallyImpaired, + kHearingImpaired, + kDialog, + kCommentary, + kEmergency, + kVoiceOver + }; + + uint32_t mContentClassifier = kCompleteMain; + + // ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode + enum InputChannelMode { + kChannelMode_Mono, + kChannelMode_Stereo, + kChannelMode_3_0, + kChannelMode_5_0, + kChannelMode_5_1, + kChannelMode_7_0_34, + kChannelMode_7_1_34, + kChannelMode_7_0_52, + kChannelMode_7_1_52, + kChannelMode_7_0_322, + kChannelMode_7_1_322, + kChannelMode_7_0_4, + kChannelMode_7_1_4, + kChannelMode_9_0_4, + kChannelMode_9_1_4, + kChannelMode_22_2, + kChannelMode_Reserved, + }; + + bool mHasDialogEnhancements = false; + bool mPreVirtualized = false; + bool mEnabled = true; + + std::string mLanguage; + std::string mDescription; + }; + typedef std::map AC4Presentations; + + const AC4Presentations& getPresentations() const { return mPresentations; } + +protected: + AC4Presentations mPresentations; +}; + +class AC4DSIParser: public AC4Parser { +public: + explicit AC4DSIParser(ABitReader &br); + virtual ~AC4DSIParser() { } + + bool parse(); + +private: + bool parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID); + bool parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID); + bool parseLanguageTag(uint32_t presentationID, uint32_t substreamID); + bool parseBitrateDsi(); + + uint64_t mDSISize; + ABitReader& mBitReader; +}; + +}; + +#endif // AC4_PARSER_H_ diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp index 3fe23365e3..0b6e75a40b 100644 --- a/media/extractors/mp4/Android.bp +++ b/media/extractors/mp4/Android.bp @@ -2,6 +2,7 @@ cc_defaults { name: "libmp4extractor_defaults", srcs: [ + "AC4Parser.cpp", "ItemTable.cpp", "MPEG4Extractor.cpp", "SampleIterator.cpp", diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 99f32d5063..bbc735eb54 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -26,6 +26,7 @@ #include +#include "AC4Parser.h" #include "MPEG4Extractor.h" #include "SampleTable.h" #include "ItemTable.h" @@ -123,6 +124,8 @@ private: bool mIsAVC; bool mIsHEVC; + bool mIsAC4; + size_t mNALLengthSize; bool mStarted; @@ -320,6 +323,8 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC('h', 'v', 'c', '1'): case FOURCC('h', 'e', 'v', '1'): return MEDIA_MIMETYPE_VIDEO_HEVC; + case FOURCC('a', 'c', '-', '4'): + return MEDIA_MIMETYPE_AUDIO_AC4; default: CHECK(!"should not be here."); return NULL; @@ -2326,6 +2331,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return parseAC3SampleEntry(data_offset); } + case FOURCC('a', 'c', '-', '4'): + { + *offset += chunk_size; + return parseAC4SampleEntry(data_offset); + } + case FOURCC('f', 't', 'y', 'p'): { if (chunk_data_size < 8 || depth != 0) { @@ -2397,6 +2408,84 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return OK; } +status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) { + // skip 16 bytes: + // + 6-byte reserved, + // + 2-byte data reference index, + // + 8-byte reserved + offset += 16; + uint16_t channelCount; + if (!mDataSource->getUInt16(offset, &channelCount)) { + ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count"); + return ERROR_MALFORMED; + } + // skip 8 bytes: + // + 2-byte channelCount, + // + 2-byte sample size, + // + 4-byte reserved + offset += 8; + uint16_t sampleRate; + if (!mDataSource->getUInt16(offset, &sampleRate)) { + ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate"); + return ERROR_MALFORMED; + } + + // skip 4 bytes: + // + 2-byte sampleRate, + // + 2-byte reserved + offset += 4; + + if (mLastTrack == NULL) { + return ERROR_MALFORMED; + } + mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4); + mLastTrack->meta.setInt32(kKeyChannelCount, channelCount); + mLastTrack->meta.setInt32(kKeySampleRate, sampleRate); + return parseAC4SpecificBox(offset); +} + +status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { + uint32_t size; + // + 4-byte size + // + 4-byte type + // + 3-byte payload + const uint32_t kAC4MinimumBoxSize = 4 + 4 + 3; + if (!mDataSource->getUInt32(offset, &size) || size < kAC4MinimumBoxSize) { + ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read specific box size"); + return ERROR_MALFORMED; + } + + // + 4-byte size + offset += 4; + uint32_t type; + if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '4')) { + ALOGE("MPEG4Extractor: error while reading ac-4 specific block: header not dac4"); + return ERROR_MALFORMED; + } + + // + 4-byte type + offset += 4; + // at least for AC4 DSI v1 this is big enough + const uint32_t kAC4SpecificBoxPayloadSize = 256; + uint8_t chunk[kAC4SpecificBoxPayloadSize]; + ssize_t dsiSize = size - 8; // size of box - size and type fields + if (dsiSize >= (ssize_t)kAC4SpecificBoxPayloadSize || + mDataSource->readAt(offset, chunk, dsiSize) != dsiSize) { + ALOGE("MPEG4Extractor: error while reading ac-4 specific block: bitstream fields"); + return ERROR_MALFORMED; + } + // + size-byte payload + offset += dsiSize; + ABitReader br(chunk, dsiSize); + AC4DSIParser parser(br); + if (!parser.parse()){ + ALOGE("MPEG4Extractor: error while parsing ac-4 specific block"); + return ERROR_MALFORMED; + } + + return OK; +} + status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) { // skip 16 bytes: // + 6-byte reserved, @@ -3745,6 +3834,7 @@ MPEG4Source::MPEG4Source( mCurrentSampleInfoOffsets(NULL), mIsAVC(false), mIsHEVC(false), + mIsAC4(false), mNALLengthSize(0), mStarted(false), mGroup(NULL), @@ -3775,6 +3865,7 @@ MPEG4Source::MPEG4Source( mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); + mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4); if (mIsAVC) { uint32_t type; @@ -4672,7 +4763,7 @@ status_t MPEG4Source::read( } } - if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) { + if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) { if (newBuffer) { ssize_t num_bytes_read = mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); @@ -4704,13 +4795,20 @@ status_t MPEG4Source::read( ++mCurrentSampleIndex; } - if (!mIsAVC && !mIsHEVC) { + if (!mIsAVC && !mIsHEVC && !mIsAC4) { *out = mBuffer; mBuffer = NULL; return OK; } + if (mIsAC4) { + mBuffer->release(); + mBuffer = NULL; + + return ERROR_IO; + } + // Each NAL unit is split up into its constituent fragments and // each one of them returned in its own buffer. @@ -4748,6 +4846,58 @@ status_t MPEG4Source::read( *out = clone; + return OK; + } else if (mIsAC4) { + CHECK(mBuffer != NULL); + // Make sure there is enough space to write the sync header and the raw frame + if (mBuffer->range_length() < (7 + size)) { + mBuffer->release(); + mBuffer = NULL; + + return ERROR_IO; + } + + uint8_t *dstData = (uint8_t *)mBuffer->data(); + size_t dstOffset = 0; + // Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame + // AC40 sync word, meaning no CRC at the end of the frame + dstData[dstOffset++] = 0xAC; + dstData[dstOffset++] = 0x40; + dstData[dstOffset++] = 0xFF; + dstData[dstOffset++] = 0xFF; + dstData[dstOffset++] = (uint8_t)((size >> 16) & 0xFF); + dstData[dstOffset++] = (uint8_t)((size >> 8) & 0xFF); + dstData[dstOffset++] = (uint8_t)((size >> 0) & 0xFF); + + ssize_t numBytesRead = mDataSource->readAt(offset, dstData + dstOffset, size); + if (numBytesRead != (ssize_t)size) { + mBuffer->release(); + mBuffer = NULL; + + return ERROR_IO; + } + + mBuffer->set_range(0, dstOffset + size); + mBuffer->meta_data().clear(); + mBuffer->meta_data().setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + mBuffer->meta_data().setInt64( + kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data().setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + } + + ++mCurrentSampleIndex; + + *out = mBuffer; + mBuffer = NULL; + return OK; } else { // Whole NAL units are returned but each fragment is prefixed by @@ -5191,6 +5341,8 @@ status_t MPEG4Source::fragmentedRead( return OK; } + + return OK; } MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 831f120504..6148334081 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -140,6 +140,8 @@ private: status_t parseAC3SampleEntry(off64_t offset); status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate); + status_t parseAC4SampleEntry(off64_t offset); + status_t parseAC4SpecificBox(off64_t offset); MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index 1695c75bd8..a32cf08287 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -50,6 +50,7 @@ const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3"; const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3"; +const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4"; const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index 25be89f4c1..b165bcb946 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -52,6 +52,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM; extern const char *MEDIA_MIMETYPE_AUDIO_AC3; extern const char *MEDIA_MIMETYPE_AUDIO_EAC3; +extern const char *MEDIA_MIMETYPE_AUDIO_AC4; extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 5cc509326a..271d601ccd 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -119,6 +119,7 @@ struct ATSParser::Program : public RefBase { private: struct StreamInfo { unsigned mType; + unsigned mTypeExt; unsigned mPID; int32_t mCASystemId; }; @@ -145,10 +146,12 @@ struct ATSParser::Stream : public RefBase { Stream(Program *program, unsigned elementaryPID, unsigned streamType, + unsigned streamTypeExt, unsigned PCR_PID, int32_t CA_system_ID); unsigned type() const { return mStreamType; } + unsigned typeExt() const { return mStreamTypeExt; } unsigned pid() const { return mElementaryPID; } void setPID(unsigned pid) { mElementaryPID = pid; } @@ -194,6 +197,7 @@ private: Program *mProgram; unsigned mElementaryPID; unsigned mStreamType; + unsigned mStreamTypeExt; unsigned mPCR_PID; int32_t mExpectedContinuityCounter; @@ -447,7 +451,7 @@ bool ATSParser::Program::findCADescriptor( if (descriptor_length > infoLength) { break; } - if (descriptor_tag == 9 && descriptor_length >= 4) { + if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) { found = true; caDescriptor->mSystemID = br->getBits(16); caDescriptor->mPID = br->getBits(16) & 0x1fff; @@ -513,37 +517,65 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { // infoBytesRemaining is the number of bytes that make up the // variable length section of ES_infos. It does not include the // final CRC. - size_t infoBytesRemaining = section_length - 9 - program_info_length - 4; + int32_t infoBytesRemaining = section_length - 9 - program_info_length - 4; while (infoBytesRemaining >= 5) { - - unsigned streamType = br->getBits(8); - ALOGV(" stream_type = 0x%02x", streamType); - + StreamInfo info; + info.mType = br->getBits(8); + ALOGV(" stream_type = 0x%02x", info.mType); MY_LOGV(" reserved = %u", br->getBits(3)); - unsigned elementaryPID = br->getBits(13); - ALOGV(" elementary_PID = 0x%04x", elementaryPID); + info.mPID = br->getBits(13); + ALOGV(" elementary_PID = 0x%04x", info.mPID); MY_LOGV(" reserved = %u", br->getBits(4)); unsigned ES_info_length = br->getBits(12); ALOGV(" ES_info_length = %u", ES_info_length); + infoBytesRemaining -= 5 + ES_info_length; CADescriptor streamCA; - bool hasStreamCA = findCADescriptor(br, ES_info_length, &streamCA); + info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX; + bool hasStreamCA = false; + while (ES_info_length > 2 && infoBytesRemaining >= 0) { + unsigned descriptor_tag = br->getBits(8); + ALOGV(" tag = 0x%02x", descriptor_tag); + + unsigned descriptor_length = br->getBits(8); + ALOGV(" len = %u", descriptor_length); + + ES_info_length -= 2; + if (descriptor_length > ES_info_length) { + return ERROR_MALFORMED; + } + if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) { + hasStreamCA = true; + streamCA.mSystemID = br->getBits(16); + streamCA.mPID = br->getBits(16) & 0x1fff; + ES_info_length -= 4; + streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length - 4); + } else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA && + descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) { + unsigned descTagExt = br->getBits(8); + ALOGV(" tag_ext = 0x%02x", descTagExt); + if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) { + info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4; + } + ES_info_length -= descriptor_length; + descriptor_length--; + br->skipBits(descriptor_length * 8); + } else { + ES_info_length -= descriptor_length; + br->skipBits(descriptor_length * 8); + } + } if (hasStreamCA && !mParser->mCasManager->addStream( - mProgramNumber, elementaryPID, streamCA)) { + mProgramNumber, info.mPID, streamCA)) { return ERROR_MALFORMED; } - StreamInfo info; - info.mType = streamType; - info.mPID = elementaryPID; info.mCASystemId = hasProgramCA ? programCA.mSystemID : hasStreamCA ? streamCA.mSystemID : -1; infos.push(info); - - infoBytesRemaining -= 5 + ES_info_length; } if (infoBytesRemaining != 0) { @@ -602,7 +634,7 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { if (index < 0) { sp stream = new Stream( - this, info.mPID, info.mType, PCR_PID, info.mCASystemId); + this, info.mPID, info.mType, info.mTypeExt, PCR_PID, info.mCASystemId); if (mSampleAesKeyItem != NULL) { stream->signalNewSampleAesKey(mSampleAesKeyItem); @@ -720,11 +752,13 @@ ATSParser::Stream::Stream( Program *program, unsigned elementaryPID, unsigned streamType, + unsigned streamTypeExt, unsigned PCR_PID, int32_t CA_system_ID) : mProgram(program), mElementaryPID(elementaryPID), mStreamType(streamType), + mStreamTypeExt(streamTypeExt), mPCR_PID(PCR_PID), mExpectedContinuityCounter(-1), mPayloadStarted(false), @@ -781,6 +815,12 @@ ATSParser::Stream::Stream( mode = ElementaryStreamQueue::AC3; break; + case STREAMTYPE_PES_PRIVATE_DATA: + if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) { + mode = ElementaryStreamQueue::AC4; + } + break; + case STREAMTYPE_METADATA: mode = ElementaryStreamQueue::METADATA; break; @@ -989,6 +1029,8 @@ bool ATSParser::Stream::isAudio() const { case STREAMTYPE_AAC_ENCRYPTED: case STREAMTYPE_AC3_ENCRYPTED: return true; + case STREAMTYPE_PES_PRIVATE_DATA: + return mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4; default: return false; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 45ca06ba4a..adb4fb2f04 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -142,6 +142,7 @@ struct ATSParser : public RefBase { STREAMTYPE_MPEG2_VIDEO = 0x02, STREAMTYPE_MPEG1_AUDIO = 0x03, STREAMTYPE_MPEG2_AUDIO = 0x04, + STREAMTYPE_PES_PRIVATE_DATA = 0x06, STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f, STREAMTYPE_MPEG4_VIDEO = 0x10, STREAMTYPE_METADATA = 0x15, @@ -160,6 +161,20 @@ struct ATSParser : public RefBase { STREAMTYPE_AC3_ENCRYPTED = 0xC1, }; + enum { + // From ISO/IEC 13818-1: 2007 (E), Table 2-29 + DESCRIPTOR_CA = 0x09, + + // DVB BlueBook A038 Table 12 + DESCRIPTOR_DVB_EXTENSION = 0x7F, + }; + + // DVB BlueBook A038 Table 109 + enum { + EXT_DESCRIPTOR_DVB_AC4 = 0x15, + EXT_DESCRIPTOR_DVB_RESERVED_MAX = 0x7F, + }; + protected: virtual ~ATSParser(); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 0fa9fcb2f5..3deee7e9ab 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -86,6 +86,21 @@ void ElementaryStreamQueue::setCasInfo( mCasSessionId = sessionId; } +static int32_t readVariableBits(ABitReader &bits, int32_t nbits) { + int32_t value = 0; + int32_t more_bits = 1; + + while (more_bits) { + value += bits.getBits(nbits); + more_bits = bits.getBits(1); + if (!more_bits) + break; + value++; + value <<= nbits; + } + return value; +} + // Parse AC3 header assuming the current ptr is start position of syncframe, // update metadata only applicable, and return the payload size static unsigned parseAC3SyncFrame( @@ -199,6 +214,78 @@ static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) { return parseAC3SyncFrame(ptr, size, NULL) > 0; } +// Parse AC4 header assuming the current ptr is start position of syncframe +// and update frameSize and metadata. +static status_t parseAC4SyncFrame( + const uint8_t *ptr, size_t size, unsigned &frameSize, sp *metaData) { + // ETSI TS 103 190-2 V1.1.1 (2015-09), Annex C + // The sync_word can be either 0xAC40 or 0xAC41. + static const int kSyncWordAC40 = 0xAC40; + static const int kSyncWordAC41 = 0xAC41; + + size_t headerSize = 0; + ABitReader bits(ptr, size); + int32_t syncWord = bits.getBits(16); + if ((syncWord != kSyncWordAC40) && (syncWord != kSyncWordAC41)) { + ALOGE("Invalid syncword in AC4 header"); + return ERROR_MALFORMED; + } + headerSize += 2; + + frameSize = bits.getBits(16); + headerSize += 2; + if (frameSize == 0xFFFF) { + frameSize = bits.getBits(24); + headerSize += 3; + } + + if (frameSize == 0) { + ALOGE("Invalid frame size in AC4 header"); + return ERROR_MALFORMED; + } + frameSize += headerSize; + // If the sync_word is 0xAC41, a crc_word is also transmitted. + if (syncWord == kSyncWordAC41) { + frameSize += 2; // crc_word + } + ALOGV("AC4 frameSize = %u", frameSize); + + // ETSI TS 103 190-2 V1.1.1 6.2.1.1 + uint32_t bitstreamVersion = bits.getBits(2); + if (bitstreamVersion == 3) { + bitstreamVersion += readVariableBits(bits, 2); + } + + bits.skipBits(10); // Sequence Counter + + uint32_t bWaitFrames = bits.getBits(1); + if (bWaitFrames) { + uint32_t waitFrames = bits.getBits(3); + if (waitFrames > 0) { + bits.skipBits(2); // br_code; + } + } + + // ETSI TS 103 190 V1.1.1 Table 82 + bool fsIndex = bits.getBits(1); + uint32_t samplingRate = fsIndex ? 48000 : 44100; + + if (metaData != NULL) { + ALOGV("dequeueAccessUnitAC4 Setting mFormat"); + (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4); + (*metaData)->setInt32(kKeyIsSyncFrame, 1); + // [FIXME] AC4 channel count is defined per presentation. Provide a default channel count + // as stereo for the entire stream. + (*metaData)->setInt32(kKeyChannelCount, 2); + (*metaData)->setInt32(kKeySampleRate, samplingRate); + } + return OK; +} + +static status_t IsSeeminglyValidAC4Header(const uint8_t *ptr, size_t size, unsigned &frameSize) { + return parseAC4SyncFrame(ptr, size, frameSize, NULL); +} + static bool IsSeeminglyValidADTSHeader( const uint8_t *ptr, size_t size, size_t *frameLength) { if (size < 7) { @@ -416,6 +503,42 @@ status_t ElementaryStreamQueue::appendData( break; } + case AC4: + { + uint8_t *ptr = (uint8_t *)data; + unsigned frameSize = 0; + ssize_t startOffset = -1; + + // A valid AC4 stream should have minimum of 7 bytes in its buffer. + // (Sync header 4 bytes + AC4 toc 3 bytes) + if (size < 7) { + return ERROR_MALFORMED; + } + for (size_t i = 0; i < size; ++i) { + if (IsSeeminglyValidAC4Header(&ptr[i], size - i, frameSize) == OK) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + ALOGI("found something resembling an AC4 syncword at offset %zd", + startOffset); + } + if (frameSize != size - startOffset) { + ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.", + frameSize, size - startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; + break; + } + case MPEG_AUDIO: { uint8_t *ptr = (uint8_t *)data; @@ -649,6 +772,8 @@ sp ElementaryStreamQueue::dequeueAccessUnit() { return dequeueAccessUnitAAC(); case AC3: return dequeueAccessUnitAC3(); + case AC4: + return dequeueAccessUnitAC4(); case MPEG_VIDEO: return dequeueAccessUnitMPEGVideo(); case MPEG4_VIDEO: @@ -730,6 +855,69 @@ sp ElementaryStreamQueue::dequeueAccessUnitAC3() { return accessUnit; } +sp ElementaryStreamQueue::dequeueAccessUnitAC4() { + unsigned syncStartPos = 0; + unsigned payloadSize = 0; + sp format = new MetaData; + ALOGV("dequeueAccessUnit_AC4[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size()); + + // A valid AC4 stream should have minimum of 7 bytes in its buffer. + // (Sync header 4 bytes + AC4 toc 3 bytes) + if (mBuffer->size() < 7) { + return NULL; + } + + while (true) { + if (syncStartPos + 2 >= mBuffer->size()) { + return NULL; + } + + status_t status = parseAC4SyncFrame( + mBuffer->data() + syncStartPos, + mBuffer->size() - syncStartPos, + payloadSize, + &format); + if (status == OK) { + break; + } + + ALOGV("dequeueAccessUnit_AC4[%d]: syncStartPos %u payloadSize %u", + mAUIndex, syncStartPos, payloadSize); + + ++syncStartPos; + } + + if (mBuffer->size() < syncStartPos + payloadSize) { + ALOGV("Not enough buffer size for AC4"); + return NULL; + } + + if (mFormat == NULL) { + mFormat = format; + } + + int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); + if (timeUs < 0ll) { + ALOGE("negative timeUs"); + return NULL; + } + mAUIndex++; + + sp accessUnit = new ABuffer(syncStartPos + payloadSize); + memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); + + accessUnit->meta()->setInt64("timeUs", timeUs); + accessUnit->meta()->setInt32("isSync", 1); + + memmove( + mBuffer->data(), + mBuffer->data() + syncStartPos + payloadSize, + mBuffer->size() - syncStartPos - payloadSize); + + mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize); + return accessUnit; +} + sp ElementaryStreamQueue::dequeueAccessUnitPCMAudio() { if (mBuffer->size() < 4) { return NULL; diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index ffcb502c0f..399214ad1e 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -38,6 +38,7 @@ struct ElementaryStreamQueue { H264, AAC, AC3, + AC4, MPEG_AUDIO, MPEG_VIDEO, MPEG4_VIDEO, @@ -116,6 +117,7 @@ private: sp dequeueAccessUnitH264(); sp dequeueAccessUnitAAC(); sp dequeueAccessUnitAC3(); + sp dequeueAccessUnitAC4(); sp dequeueAccessUnitMPEGAudio(); sp dequeueAccessUnitMPEGVideo(); sp dequeueAccessUnitMPEG4Video(); -- GitLab From 723bafe9442caea71bf2a15c7a7480f671d45e38 Mon Sep 17 00:00:00 2001 From: Dean Wheatley Date: Tue, 13 Mar 2018 09:31:16 +1100 Subject: [PATCH 0042/1530] Map AC4 mime to audio format Change-Id: I0b7d59782a5555b09ed4759f6cc0bc75cadff8c9 --- media/libstagefright/Utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index c61f4b50f0..6457300dbf 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1576,6 +1576,7 @@ static const struct mime_conv_t mimeLookup[] = { { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS}, { MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3}, + { MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4}, { MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC}, { 0, AUDIO_FORMAT_INVALID } }; -- GitLab From 97bf89c0e48ca30f56c91ccad96e646d853b7472 Mon Sep 17 00:00:00 2001 From: Previr Rangroo Date: Tue, 12 Dec 2017 10:11:33 +1100 Subject: [PATCH 0043/1530] Add AudioParameter keyAudioLanguagePreferred This parameter will be used to set user's preferred audio language to the MSD HAL. The MSD HAL will use this as a preference indication when selecting an audio presentation to decode and render for next generation audio codecs such as AC-4. Bug: 63901775 Test: make Change-Id: Ic1da11ced50857f62affd636ba197fb8e6759110 Signed-off-by: Previr Rangroo --- media/libaudioclient/include/media/AudioParameter.h | 3 +++ media/libmedia/AudioParameter.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h index 59ac1db998..8d2a2cf419 100644 --- a/media/libaudioclient/include/media/AudioParameter.h +++ b/media/libaudioclient/include/media/AudioParameter.h @@ -64,6 +64,9 @@ public: static const char * const keyPresentationId; static const char * const keyProgramId; + // keyAudioLanguagePreferred: Preferred audio language + static const char * const keyAudioLanguagePreferred; + // keyStreamConnect / Disconnect: value is an int in audio_devices_t static const char * const keyStreamConnect; static const char * const keyStreamDisconnect; diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp index cb0e927209..190e46fa0d 100644 --- a/media/libmedia/AudioParameter.cpp +++ b/media/libmedia/AudioParameter.cpp @@ -36,6 +36,8 @@ const char * const AudioParameter::keyBtNrec = AUDIO_PARAMETER_KEY_BT_NREC; const char * const AudioParameter::keyHwAvSync = AUDIO_PARAMETER_HW_AV_SYNC; const char * const AudioParameter::keyPresentationId = AUDIO_PARAMETER_STREAM_PRESENTATION_ID; const char * const AudioParameter::keyProgramId = AUDIO_PARAMETER_STREAM_PROGRAM_ID; +const char * const AudioParameter::keyAudioLanguagePreferred = + AUDIO_PARAMETER_KEY_AUDIO_LANGUAGE_PREFERRED; const char * const AudioParameter::keyMonoOutput = AUDIO_PARAMETER_MONO_OUTPUT; const char * const AudioParameter::keyStreamHwAvSync = AUDIO_PARAMETER_STREAM_HW_AV_SYNC; const char * const AudioParameter::keyStreamConnect = AUDIO_PARAMETER_DEVICE_CONNECT; -- GitLab From 115418b3c0ad77a6910d3490a071182d68aca2ea Mon Sep 17 00:00:00 2001 From: Aravind Sreekumar Date: Thu, 5 Apr 2018 15:13:34 -0700 Subject: [PATCH 0044/1530] Migrate MediaComponents to androidx. Test: make MediaComponents Bug: 76692459 Change-Id: I013659d10c5c7dd97704f3d31bd2ee72c2779473 --- packages/MediaComponents/Android.mk | 8 +++---- .../mr_controller_material_dialog_b.xml | 6 ++--- .../res/layout/mr_controller_volume_item.xml | 2 +- .../com/android/media/MediaBrowser2Impl.java | 1 - .../android/media/MediaController2Impl.java | 9 ++++---- .../android/media/MediaController2Stub.java | 2 +- .../com/android/media/MediaSession2Impl.java | 7 +++--- .../com/android/media/MediaSession2Stub.java | 9 ++++---- .../media/MediaSessionService2Impl.java | 4 ++-- .../src/com/android/media/Rating2Impl.java | 1 - .../src/com/android/media/RoutePlayer.java | 5 ++-- .../com/android/media/SessionToken2Impl.java | 2 +- .../media/subtitle/SubtitleController.java | 7 +++--- .../com/android/media/update/ApiFactory.java | 7 +++--- .../com/android/media/update/ApiHelper.java | 8 +++---- .../app/MediaRouteActionProvider.java | 7 +++--- .../mediarouter/app/MediaRouteButton.java | 11 +++++---- .../app/MediaRouteChooserDialog.java | 9 ++++---- .../app/MediaRouteControllerDialog.java | 9 ++++---- .../app/MediaRouteDialogFactory.java | 2 +- .../app/MediaRouteDiscoveryFragment.java | 11 +++++---- .../app/MediaRouteExpandCollapseButton.java | 1 - .../app/MediaRouterThemeHelper.java | 7 +++--- .../MediaRouterJellybeanMr1.java | 1 - .../mediarouter/media/MediaItemStatus.java | 3 ++- .../mediarouter/media/MediaRouteProvider.java | 3 ++- .../media/MediaRouteProviderService.java | 20 ++++++++++------ .../mediarouter/media/MediaRouteSelector.java | 5 ++-- .../mediarouter/media/MediaRouter.java | 23 ++++++++++--------- .../mediarouter/media/MediaSessionStatus.java | 3 ++- .../media/RegisteredMediaRouteProvider.java | 15 ++++++++---- .../media/RemoteControlClientCompat.java | 1 - .../media/RemotePlaybackClient.java | 9 ++++---- .../media/SystemMediaRouteProvider.java | 1 - .../android/widget/MediaControlView2Impl.java | 13 +++++------ .../src/com/android/widget/SubtitleView.java | 3 ++- .../com/android/widget/VideoSurfaceView.java | 5 ++-- .../com/android/widget/VideoTextureView.java | 7 +++--- .../com/android/widget/VideoView2Impl.java | 17 +++++++------- 39 files changed, 142 insertions(+), 122 deletions(-) diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk index def9dc5b53..55a5424bbd 100644 --- a/packages/MediaComponents/Android.mk +++ b/packages/MediaComponents/Android.mk @@ -42,7 +42,7 @@ # #LOCAL_MULTILIB := first # -#LOCAL_JAVA_LIBRARIES += android-support-annotations +#LOCAL_JAVA_LIBRARIES += androidx.annotation_annotation # ## To embed native libraries in package, uncomment the lines below. ##LOCAL_MODULE_TAGS := samples @@ -60,9 +60,9 @@ # ## TODO: Remove dependency with other support libraries. #LOCAL_STATIC_ANDROID_LIBRARIES += \ -# android-support-v4 \ -# android-support-v7-appcompat \ -# android-support-v7-palette +# androidx.legacy_legacy-support-v4 \ +# androidx.appcompat_appcompat \ +# androidx.palette_palette #LOCAL_USE_AAPT2 := true # #include $(BUILD_PACKAGE) diff --git a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml index b304471e65..f6f7be5316 100644 --- a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml +++ b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml @@ -169,7 +169,7 @@ android:layout_height="wrap_content" android:fillViewport="true" android:scrollIndicators="top|bottom"> - - - + diff --git a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml b/packages/MediaComponents/res/layout/mr_controller_volume_item.xml index a89058b207..12d85ae151 100644 --- a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml +++ b/packages/MediaComponents/res/layout/mr_controller_volume_item.xml @@ -40,7 +40,7 @@ android:layout_marginBottom="8dp" android:scaleType="fitCenter" android:src="?attr/mediaRouteAudioTrackDrawable" /> - Prerequisites *

* To use the media route action provider, the activity must be a subclass of - * {@link AppCompatActivity} from the android.support.v7.appcompat + * {@link AppCompatActivity} from the androidx.appcompat.appcompat * support library. Refer to support library documentation for details. *

* @@ -65,7 +66,7 @@ import java.lang.ref.WeakReference; * <item android:id="@+id/media_route_menu_item" * android:title="@string/media_route_menu_title" * app:showAsAction="always" - * app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/> + * app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/> * </menu> *

* Then configure the menu and set the route selector for the chooser. diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java index fde8a6337a..e82fcb9a5b 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java @@ -28,14 +28,15 @@ import android.graphics.Canvas; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.support.v4.graphics.drawable.DrawableCompat; -import android.support.v7.widget.TooltipCompat; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.SoundEffectConstants; import android.view.View; +import androidx.appcompat.widget.TooltipCompat; +import androidx.core.graphics.drawable.DrawableCompat; + import com.android.media.update.ApiHelper; import com.android.media.update.R; import com.android.support.mediarouter.media.MediaRouteSelector; @@ -70,7 +71,7 @@ import com.android.support.mediarouter.media.MediaRouter; *

Prerequisites

*

* To use the media route button, the activity must be a subclass of - * {@link FragmentActivity} from the android.support.v4 + * {@link FragmentActivity} from the androidx.core./code> * support library. Refer to support library documentation for details. *

* @@ -81,9 +82,9 @@ public class MediaRouteButton extends View { private static final String TAG = "MediaRouteButton"; private static final String CHOOSER_FRAGMENT_TAG = - "android.support.v7.mediarouter:MediaRouteChooserDialogFragment"; + "androidx.mediarouter.media.outer:MediaRouteChooserDialogFragment"; private static final String CONTROLLER_FRAGMENT_TAG = - "android.support.v7.mediarouter:MediaRouteControllerDialogFragment"; + "androidx.mediarouter.media.outer:MediaRouteControllerDialogFragment"; private final MediaRouter mRouter; private final MediaRouterCallback mCallback; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java index cac64d95db..f24028afcd 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java @@ -16,13 +16,14 @@ package com.android.support.mediarouter.app; -import static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED; -import static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING; +import static com.android.support.mediarouter.media.MediaRouter.RouteInfo + .CONNECTION_STATE_CONNECTED; +import static com.android.support.mediarouter.media.MediaRouter.RouteInfo + .CONNECTION_STATE_CONNECTING; import android.annotation.NonNull; import android.app.Dialog; import android.content.Context; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -30,12 +31,10 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; -import android.support.v7.app.AppCompatDialog; import android.text.TextUtils; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java index 060cfcaa48..f6c1d2f25c 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java @@ -40,9 +40,6 @@ import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; -import android.support.v4.util.ObjectsCompat; -import android.support.v4.view.accessibility.AccessibilityEventCompat; -import android.support.v7.graphics.Palette; import android.text.TextUtils; import android.util.Log; import android.view.ContextThemeWrapper; @@ -72,11 +69,15 @@ import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; +import androidx.core.util.ObjectsCompat; +import androidx.core.view.accessibility.AccessibilityEventCompat; +import androidx.palette.graphics.Palette; + import com.android.media.update.ApiHelper; import com.android.media.update.R; +import com.android.support.mediarouter.app.OverlayListView.OverlayObject; import com.android.support.mediarouter.media.MediaRouteSelector; import com.android.support.mediarouter.media.MediaRouter; -import com.android.support.mediarouter.app.OverlayListView.OverlayObject; import java.io.BufferedInputStream; import java.io.IOException; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java index a9eaf394bb..b5ee63ec21 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java @@ -16,7 +16,7 @@ package com.android.support.mediarouter.app; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; /** * The media route dialog factory is responsible for creating the media route diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java index 02ee1183b9..52aecd880c 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java @@ -17,10 +17,11 @@ package com.android.support.mediarouter.app; import android.os.Bundle; -import android.support.v4.app.Fragment; -import com.android.support.mediarouter.media.MediaRouter; +import androidx.fragment.app.Fragment; + import com.android.support.mediarouter.media.MediaRouteSelector; +import com.android.support.mediarouter.media.MediaRouter; /** * Media route discovery fragment. @@ -34,7 +35,7 @@ import com.android.support.mediarouter.media.MediaRouteSelector; * provide the {@link MediaRouter} callback to register. *

* Note that the discovery callback makes the application be connected with all the - * {@link android.support.v7.media.MediaRouteProviderService media route provider services} + * {@link androidx.mediarouter.media.MediaRouteProviderService media route provider services} * while it is registered. *

*/ @@ -114,7 +115,7 @@ public class MediaRouteDiscoveryFragment extends Fragment { } /** - * Called to create the {@link android.support.v7.media.MediaRouter.Callback callback} + * Called to create the {@link androidx.mediarouter.media.MediaRouter.Callback callback} * that will be registered. *

* The default callback does nothing. The application may override this method to @@ -129,7 +130,7 @@ public class MediaRouteDiscoveryFragment extends Fragment { /** * Called to prepare the callback flags that will be used when the - * {@link android.support.v7.media.MediaRouter.Callback callback} is registered. + * {@link androidx.mediarouter.media.MediaRouter.Callback callback} is registered. *

* The default implementation returns {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}. *

diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java index 6a0a95adcf..dcca6a000c 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java @@ -21,7 +21,6 @@ import android.graphics.ColorFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.AnimationDrawable; -import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java index 63f042f5bf..b4bf8d1090 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java @@ -19,12 +19,13 @@ package com.android.support.mediarouter.app; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; -import android.support.annotation.IntDef; -import android.support.v4.graphics.ColorUtils; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.View; +import androidx.annotation.IntDef; +import androidx.core.graphics.ColorUtils; + import com.android.media.update.R; import java.lang.annotation.Retention; @@ -170,7 +171,7 @@ final class MediaRouterThemeHelper { private static boolean isLightTheme(Context context) { TypedValue value = new TypedValue(); // TODO(sungsoo): Switch to com.android.internal.R.attr.isLightTheme - return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme, + return context.getTheme().resolveAttribute(androidx.appcompat.R.attr.isLightTheme, value, true) && value.data != 0; } diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java index f8539bda97..5a0bc95442 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java @@ -20,7 +20,6 @@ import android.content.Context; import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Handler; -import android.support.annotation.RequiresApi; import android.util.Log; import android.view.Display; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java index 90ea2d5e64..92f608b6c8 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java @@ -19,7 +19,8 @@ package com.android.support.mediarouter.media; import android.app.PendingIntent; import android.os.Bundle; import android.os.SystemClock; -import android.support.v4.util.TimeUtils; + +import androidx.core.util.TimeUtils; /** * Describes the playback status of a media item. diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java index 91a2e1ac8c..7ea328ccfe 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java @@ -23,7 +23,8 @@ import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; -import android.support.v4.util.ObjectsCompat; + +import androidx.core.util.ObjectsCompat; import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java index 43cde10650..a186fee47c 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java @@ -29,12 +29,14 @@ import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_ROUTE_CONTROL_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .CLIENT_MSG_SELECT_ROUTE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_SET_DISCOVERY_REQUEST; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_SET_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .CLIENT_MSG_UNREGISTER; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_UNSELECT_ROUTE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol @@ -51,9 +53,12 @@ import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .SERVICE_MSG_GENERIC_FAILURE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .SERVICE_MSG_GENERIC_SUCCESS; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_CURRENT; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .SERVICE_MSG_REGISTERED; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .SERVICE_VERSION_CURRENT; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .isValidRemoteMessenger; import android.app.Service; import android.content.Intent; @@ -65,11 +70,12 @@ import android.os.IBinder.DeathRecipient; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.support.annotation.VisibleForTesting; -import android.support.v4.util.ObjectsCompat; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.VisibleForTesting; +import androidx.core.util.ObjectsCompat; + import java.lang.ref.WeakReference; import java.util.ArrayList; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java index 5669b19a90..f20dcc0974 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java @@ -17,8 +17,9 @@ package com.android.support.mediarouter.media; import android.content.IntentFilter; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java index db0052e362..4b56b19ff1 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java @@ -33,15 +33,16 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.support.v4.app.ActivityManagerCompat; -import android.support.v4.hardware.display.DisplayManagerCompat; -import android.support.v4.media.VolumeProviderCompat; import android.support.v4.media.session.MediaSessionCompat; -import android.support.v4.util.Pair; import android.text.TextUtils; import android.util.Log; import android.view.Display; +import androidx.core.app.ActivityManagerCompat; +import androidx.core.hardware.display.DisplayManagerCompat; +import androidx.core.util.Pair; +import androidx.media.VolumeProviderCompat; + import com.android.support.mediarouter.media.MediaRouteProvider.ProviderMetadata; import com.android.support.mediarouter.media.MediaRouteProvider.RouteController; @@ -81,13 +82,13 @@ public final class MediaRouter { static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** - * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} + * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route * was unselected is unknown. */ public static final int UNSELECT_REASON_UNKNOWN = 0; /** - * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} + * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed * the disconnect button to disconnect and keep playing. *

@@ -96,13 +97,13 @@ public final class MediaRouter { */ public static final int UNSELECT_REASON_DISCONNECTED = 1; /** - * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} + * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed * the stop casting button. */ public static final int UNSELECT_REASON_STOPPED = 2; /** - * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} + * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected * a different route. */ @@ -174,7 +175,7 @@ public final class MediaRouter { * Applications should typically add a callback using this flag in the * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} * method and remove it in the {@link android.app.Activity#onStop onStop} method. - * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may + * The {@link androidx.mediarouter.app.MediaRouteDiscoveryFragment} fragment may * also be used for this purpose. *

* On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag @@ -182,7 +183,7 @@ public final class MediaRouter { * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. *

* - * @see android.support.v7.app.MediaRouteDiscoveryFragment + * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment */ public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; @@ -197,7 +198,7 @@ public final class MediaRouter { * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. *

* - * @see android.support.v7.app.MediaRouteDiscoveryFragment + * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment */ public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java index 320659660e..0e7514c951 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java @@ -19,7 +19,8 @@ package com.android.support.mediarouter.media; import android.app.PendingIntent; import android.os.Bundle; import android.os.SystemClock; -import android.support.v4.util.TimeUtils; + +import androidx.core.util.TimeUtils; /** * Describes the playback status of a media session. diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java index 98e4e283c4..eacf1c81c2 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java @@ -29,17 +29,20 @@ import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_ROUTE_CONTROL_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .CLIENT_MSG_SELECT_ROUTE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_SET_DISCOVERY_REQUEST; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_SET_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .CLIENT_MSG_UNREGISTER; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_UNSELECT_ROUTE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .CLIENT_MSG_UPDATE_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_CURRENT; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .CLIENT_VERSION_CURRENT; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .SERVICE_MSG_CONTROL_REQUEST_FAILED; @@ -51,9 +54,11 @@ import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .SERVICE_MSG_GENERIC_FAILURE; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol .SERVICE_MSG_GENERIC_SUCCESS; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .SERVICE_MSG_REGISTERED; import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger; +import static com.android.support.mediarouter.media.MediaRouteProviderProtocol + .isValidRemoteMessenger; import android.annotation.NonNull; import android.content.ComponentName; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java index 826449b2a9..65c5518824 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java @@ -18,7 +18,6 @@ package com.android.support.mediarouter.media; import android.content.Context; import android.media.AudioManager; import android.os.Build; -import android.support.annotation.RequiresApi; import java.lang.ref.WeakReference; diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java index f6e1497c6b..e76564eb2c 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java @@ -22,9 +22,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; -import android.support.v4.util.ObjectsCompat; import android.util.Log; +import androidx.core.util.ObjectsCompat; + /** * A helper class for playing media on remote routes using the remote playback protocol * defined by {@link MediaControlIntent}. @@ -867,11 +868,11 @@ public class RemotePlaybackClient { private final class ActionReceiver extends BroadcastReceiver { public static final String ACTION_ITEM_STATUS_CHANGED = - "android.support.v7.media.actions.ACTION_ITEM_STATUS_CHANGED"; + "androidx.mediarouter.media.actions.ACTION_ITEM_STATUS_CHANGED"; public static final String ACTION_SESSION_STATUS_CHANGED = - "android.support.v7.media.actions.ACTION_SESSION_STATUS_CHANGED"; + "androidx.mediarouter.media.actions.ACTION_SESSION_STATUS_CHANGED"; public static final String ACTION_MESSAGE_RECEIVED = - "android.support.v7.media.actions.ACTION_MESSAGE_RECEIVED"; + "androidx.mediarouter.media.actions.ACTION_MESSAGE_RECEIVED"; ActionReceiver() { } diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java index a38491ff55..53901a4284 100644 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java +++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java @@ -24,7 +24,6 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.media.AudioManager; import android.os.Build; -import android.support.annotation.RequiresApi; import android.view.Display; import com.android.media.update.ApiHelper; diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java index 3aff1504a1..ad85af434e 100644 --- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java +++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java @@ -20,15 +20,13 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.media.MediaMetadata; +import android.media.SessionToken2; import android.media.session.MediaController; import android.media.session.PlaybackState; -import android.media.SessionToken2; import android.media.update.MediaControlView2Provider; import android.media.update.ViewGroupProvider; import android.os.Bundle; -import android.support.annotation.Nullable; import android.util.AttributeSet; -import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -36,27 +34,28 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; -import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.MediaControlView2; -import android.widget.ProgressBar; import android.widget.PopupWindow; +import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.media.update.ApiHelper; import com.android.media.update.R; import com.android.support.mediarouter.app.MediaRouteButton; -import com.android.support.mediarouter.media.MediaRouter; import com.android.support.mediarouter.media.MediaRouteSelector; +import com.android.support.mediarouter.media.MediaRouter; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.Formatter; import java.util.List; import java.util.Locale; diff --git a/packages/MediaComponents/src/com/android/widget/SubtitleView.java b/packages/MediaComponents/src/com/android/widget/SubtitleView.java index 67b2cd1d74..db0ae331c9 100644 --- a/packages/MediaComponents/src/com/android/widget/SubtitleView.java +++ b/packages/MediaComponents/src/com/android/widget/SubtitleView.java @@ -19,10 +19,11 @@ package com.android.widget; import android.content.Context; import android.graphics.Canvas; import android.os.Looper; -import android.support.annotation.Nullable; import android.util.AttributeSet; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import com.android.media.subtitle.SubtitleController.Anchor; import com.android.media.subtitle.SubtitleTrack.RenderingWidget; diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java index fc92e85138..c9869c0439 100644 --- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java +++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java @@ -16,17 +16,18 @@ package com.android.widget; +import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW; + import android.content.Context; import android.graphics.Rect; import android.media.MediaPlayer2; -import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; -import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW; +import androidx.annotation.NonNull; class VideoSurfaceView extends SurfaceView implements VideoViewInterface, SurfaceHolder.Callback { private static final String TAG = "VideoSurfaceView"; diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java index 024a3aa3bd..40fb046384 100644 --- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java +++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java @@ -16,18 +16,19 @@ package com.android.widget; +import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW; + import android.content.Context; import android.graphics.SurfaceTexture; import android.media.MediaPlayer2; -import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.util.Log; import android.view.Surface; import android.view.TextureView; import android.view.View; -import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; @RequiresApi(26) class VideoTextureView extends TextureView diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java index 97279d6d27..ffb145a534 100644 --- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java +++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java @@ -28,30 +28,29 @@ import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.media.DataSourceDesc; +import android.media.MediaItem2; import android.media.MediaMetadata; +import android.media.MediaMetadata2; +import android.media.MediaMetadataRetriever; import android.media.MediaPlayer2; import android.media.MediaPlayer2.MediaPlayer2EventCallback; import android.media.MediaPlayer2.OnSubtitleDataListener; import android.media.MediaPlayer2Impl; -import android.media.SubtitleData; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaMetadataRetriever; import android.media.Metadata; import android.media.PlaybackParams; +import android.media.SessionToken2; +import android.media.SubtitleData; import android.media.TimedText; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.PlaybackState; -import android.media.SessionToken2; import android.media.update.VideoView2Provider; import android.media.update.ViewGroupProvider; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.ResultReceiver; -import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -66,6 +65,8 @@ import android.widget.MediaControlView2; import android.widget.TextView; import android.widget.VideoView2; +import androidx.annotation.Nullable; + import com.android.internal.graphics.palette.Palette; import com.android.media.RoutePlayer; import com.android.media.subtitle.ClosedCaptionRenderer; @@ -73,10 +74,10 @@ import com.android.media.subtitle.SubtitleController; import com.android.media.subtitle.SubtitleTrack; import com.android.media.update.ApiHelper; import com.android.media.update.R; -import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaControlIntent; -import com.android.support.mediarouter.media.MediaRouter; +import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaRouteSelector; +import com.android.support.mediarouter.media.MediaRouter; import java.util.ArrayList; import java.util.List; -- GitLab From 782049413376ee1833a08ba02cc5ba6b5905792f Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Mon, 16 Apr 2018 12:47:19 -0700 Subject: [PATCH 0045/1530] Add retry mechanism to AudioRecord::restoreRecord_l(). This is analogous to similar code in AudioTrack::restoreTrack_l(). Bug: 77682459 Test: Connect USB headset. Start Skype call. Note input audio on DUT is heard on remote phone. Manually kill audio server, verify that input from USB headset is restored on DUT and heard on remote phone. Change-Id: Icbf624aba8a3fe6917b132d8c38c2812f9519ec6 --- media/libaudioclient/AudioRecord.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 21d3fa666c..a8369c22b1 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -1231,6 +1231,14 @@ status_t AudioRecord::restoreRecord_l(const char *from) ALOGW("dead IAudioRecord, creating a new one from %s()", from); ++mSequence; + const int INITIAL_RETRIES = 3; + int retries = INITIAL_RETRIES; +retry: + if (retries < INITIAL_RETRIES) { + // refresh the audio configuration cache in this process to make sure we get new + // input parameters and new IAudioRecord in createRecord_l() + AudioSystem::clearAudioConfigCache(); + } mFlags = mOrigFlags; // if the new IAudioRecord is created, createRecord_l() will modify the @@ -1239,7 +1247,11 @@ status_t AudioRecord::restoreRecord_l(const char *from) Modulo position(mProxy->getPosition()); mNewPosition = position + mUpdatePeriod; status_t result = createRecord_l(position, mOpPackageName); - if (result == NO_ERROR) { + + if (result != NO_ERROR) { + ALOGW("%s(): createRecord_l failed, do not retry", __func__); + retries = 0; + } else { if (mActive) { // callback thread or sync event hasn't changed // FIXME this fails if we have a new AudioFlinger instance @@ -1248,6 +1260,14 @@ status_t AudioRecord::restoreRecord_l(const char *from) } mFramesReadServerOffset = mFramesRead; // server resets to zero so we need an offset. } + + if (result != NO_ERROR) { + ALOGW("%s() failed status %d, retries %d", __func__, result, retries); + if (--retries > 0) { + goto retry; + } + } + if (result != NO_ERROR) { ALOGW("restoreRecord_l() failed status %d", result); mActive = false; -- GitLab From ede0467aa41bdcb1eed2add1fcde9733b0d0ac58 Mon Sep 17 00:00:00 2001 From: Daniel Van Veen Date: Mon, 26 Mar 2018 10:57:09 +1100 Subject: [PATCH 0046/1530] libaudiohal: Use IDeviceFactory in MSD HAL when opening a device This allows instantiating the MSD device if the HAL is present. The directive in the .rc about restarting "vendor.audio-hal-4-0-msd" service is ignored when the service is not present. The current policy for handling HAL service restarts is to terminate the audio service. It is possible to do better, e.g. only re-open the devices of the crashed HAL when possible, without affecting devices from other HALs. Will try to implement that later. Remove the MSD-related code from the V2.0 shim, as MSD isn't supported on HAL V2.0. Bug: 63901775 Test: manual on a MSD HAL prototype, also on taimen with no MSD HAL Change-Id: I7f9de692d6e7b8ff52cdbcaba1074692c5f1c90e --- media/audioserver/audioserver.rc | 1 + .../libaudiohal/2.0/DevicesFactoryHalHidl.cpp | 3 - media/libaudiohal/2.0/DevicesFactoryHalHidl.h | 1 - .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 61 +++++++++++-------- media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 5 +- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc index 75675a9705..ec859f26af 100644 --- a/media/audioserver/audioserver.rc +++ b/media/audioserver/audioserver.rc @@ -6,6 +6,7 @@ service audioserver /system/bin/audioserver ioprio rt 4 writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks onrestart restart vendor.audio-hal-2-0 + onrestart restart vendor.audio-hal-4-0-msd # Keep the original service name for backward compatibility when upgrading # O-MR1 devices with framework-only. onrestart restart audio-hal-2-0 diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp index 5b335920bf..31da2637ab 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp @@ -43,9 +43,6 @@ DevicesFactoryHalHidl::DevicesFactoryHalHidl() { ALOGE("Failed to obtain IDevicesFactory service, terminating process."); exit(1); } - // The MSD factory is optional - mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD); - // TODO: Register death handler, and add 'restart' directive to audioserver.rc } DevicesFactoryHalHidl::~DevicesFactoryHalHidl() { diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h index 0748849a5b..e2f1ad15b5 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h @@ -39,7 +39,6 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface friend class DevicesFactoryHalHybrid; sp mDevicesFactory; - sp mDevicesFactoryMsd; static status_t nameFromHal(const char *name, IDevicesFactory::Device *device); diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp index c83194e91e..c566728b26 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp @@ -15,6 +15,7 @@ */ #include +#include #define LOG_TAG "DevicesFactoryHalHidl" //#define LOG_NDEBUG 0 @@ -35,40 +36,48 @@ namespace android { namespace V4_0 { DevicesFactoryHalHidl::DevicesFactoryHalHidl() { - mDevicesFactory = IDevicesFactory::getService(); - if (mDevicesFactory != 0) { - // It is assumed that DevicesFactory is owned by AudioFlinger - // and thus have the same lifespan. - mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); - } else { - ALOGE("Failed to obtain IDevicesFactory service, terminating process."); + sp defaultFactory{IDevicesFactory::getService()}; + if (!defaultFactory) { + ALOGE("Failed to obtain IDevicesFactory/default service, terminating process."); exit(1); } + mDeviceFactories.push_back(defaultFactory); // The MSD factory is optional - mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD); - // TODO: Register death handler, and add 'restart' directive to audioserver.rc -} - -DevicesFactoryHalHidl::~DevicesFactoryHalHidl() { + sp msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)}; + if (msdFactory) { + mDeviceFactories.push_back(msdFactory); + } + for (const auto& factory : mDeviceFactories) { + // It is assumed that the DevicesFactoryHalInterface instance is owned + // by AudioFlinger and thus have the same lifespan. + factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); + } } status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { - if (mDevicesFactory == 0) return NO_INIT; + if (mDeviceFactories.empty()) return NO_INIT; Result retval = Result::NOT_INITIALIZED; - Return ret = mDevicesFactory->openDevice( - name, - [&](Result r, const sp& result) { - retval = r; - if (retval == Result::OK) { - *device = new DeviceHalHidl(result); - } - }); - if (ret.isOk()) { - if (retval == Result::OK) return OK; - else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE; - else return NO_INIT; + for (const auto& factory : mDeviceFactories) { + Return ret = factory->openDevice( + name, + [&](Result r, const sp& result) { + retval = r; + if (retval == Result::OK) { + *device = new DeviceHalHidl(result); + } + }); + if (!ret.isOk()) return FAILED_TRANSACTION; + switch (retval) { + // Device was found and was initialized successfully. + case Result::OK: return OK; + // Device was found but failed to initalize. + case Result::NOT_INITIALIZED: return NO_INIT; + // Otherwise continue iterating. + default: ; + } } - return FAILED_TRANSACTION; + ALOGW("The specified device name is not recognized: \"%s\"", name); + return BAD_VALUE; } } // namespace V4_0 diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h index 114889bb7a..c97178f765 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h @@ -39,13 +39,12 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface private: friend class DevicesFactoryHalHybrid; - sp mDevicesFactory; - sp mDevicesFactoryMsd; + std::vector> mDeviceFactories; // Can not be constructed directly by clients. DevicesFactoryHalHidl(); - virtual ~DevicesFactoryHalHidl(); + virtual ~DevicesFactoryHalHidl() = default; }; } // namespace V4_0 -- GitLab From 2e5167e17a4c860a297c451e9892660db0ad8dab Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 19 Apr 2018 13:41:22 -0700 Subject: [PATCH 0047/1530] AudioPolicy: Print the actual config file name in the dump Since the audio policy config file is looked up in many locations, specify which one we have ended up using. Simplify rendering of the audio policy state header. Test: adb shell dumpsys media.audio_policy Change-Id: I0d410eb697898acf4e4beb2333949b1daf08c92b --- .../include/AudioPolicyConfig.h | 10 ++++ .../src/ConfigParsingUtils.cpp | 1 + .../managerdefault/AudioPolicyManager.cpp | 47 ++++++------------- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index 43f6ed6e7a..f747c369df 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -48,6 +48,14 @@ public: mIsSpeakerDrcEnabled(false) {} + const std::string& getSource() const { + return mSource; + } + + void setSource(const std::string& file) { + mSource = file; + } + void setVolumes(const VolumeCurvesCollection &volumes) { if (mVolumeCurves != nullptr) { @@ -107,6 +115,7 @@ public: void setDefault(void) { + mSource = "AudioPolicyConfig::setDefault"; mDefaultOutputDevices = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER); sp module; sp defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC); @@ -136,6 +145,7 @@ public: } private: + std::string mSource; HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */ DeviceVector &mAvailableOutputDevices; DeviceVector &mAvailableInputDevices; diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp index 1e105f5c06..19eac263d5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp @@ -412,6 +412,7 @@ status_t ConfigParsingUtils::loadConfig(const char *path, AudioPolicyConfig &con free(data); ALOGI("loadAudioPolicyConfig() loaded %s\n", path); + config.setSource(path); return NO_ERROR; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 51439f40ea..e4c9cb74d5 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2570,42 +2570,24 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) status_t AudioPolicyManager::dump(int fd) { - const size_t SIZE = 256; - char buffer[SIZE]; String8 result; - - snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); - result.append(buffer); - - snprintf(buffer, SIZE, " Primary Output: %d\n", + result.appendFormat("\nAudioPolicyManager Dump: %p\n", this); + result.appendFormat(" Primary Output: %d\n", hasPrimaryOutput() ? mPrimaryOutput->mIoHandle : AUDIO_IO_HANDLE_NONE); - result.append(buffer); std::string stateLiteral; AudioModeConverter::toString(mEngine->getPhoneState(), stateLiteral); - snprintf(buffer, SIZE, " Phone state: %s\n", stateLiteral.c_str()); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for communications %d\n", - mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for media %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for record %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for dock %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for system %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for hdmi system audio %d\n", - mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO)); - result.append(buffer); - snprintf(buffer, SIZE, " Force use for encoded surround output %d\n", - mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND)); - result.append(buffer); - snprintf(buffer, SIZE, " TTS output %s\n", mTtsOutputAvailable ? "available" : "not available"); - result.append(buffer); - snprintf(buffer, SIZE, " Master mono: %s\n", mMasterMono ? "on" : "off"); - result.append(buffer); - + result.appendFormat(" Phone state: %s\n", stateLiteral.c_str()); + const char* forceUses[AUDIO_POLICY_FORCE_USE_CNT] = { + "communications", "media", "record", "dock", "system", + "HDMI system audio", "encoded surround output", "vibrate ringing" }; + for (audio_policy_force_use_t i = AUDIO_POLICY_FORCE_FOR_COMMUNICATION; + i < AUDIO_POLICY_FORCE_USE_CNT; i = (audio_policy_force_use_t)((int)i + 1)) { + result.appendFormat(" Force use for %s: %d\n", + forceUses[i], mEngine->getForceUse(i)); + } + result.appendFormat(" TTS output %savailable\n", mTtsOutputAvailable ? "" : "not "); + result.appendFormat(" Master mono: %s\n", mMasterMono ? "on" : "off"); + result.appendFormat(" Config source: %s\n", getConfig().getSource().c_str()); write(fd, result.string(), result.size()); mAvailableOutputDevices.dump(fd, String8("Available output")); @@ -3555,6 +3537,7 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { "%s/%s", kConfigLocationList[i], fileName); ret = serializer.deserialize(audioPolicyXmlConfigFile, config); if (ret == NO_ERROR) { + config.setSource(audioPolicyXmlConfigFile); return ret; } } -- GitLab From dea53040273928d1cf4dc028c93afac706445c49 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 26 Apr 2018 13:10:21 -0700 Subject: [PATCH 0048/1530] Refactor PatchPanel Major changes: * since PatchPanel is internal to AudioFlinger, and has the same lifetime period, there is no need to use reference counting; * setAudioPortConfig moved to AudioFlinger; * store Patches in std::map instead of SortedVector, and directly--not as pointers; * move {create|clear}PatchConnections into Patch class; * use __func__ in logging. Test: test basic audio functionality on taimen Change-Id: I813fd8430a0e97cd01cc74e6b3b828b10ff5acd4 --- services/audioflinger/AudioFlinger.cpp | 25 +- services/audioflinger/AudioFlinger.h | 3 +- services/audioflinger/PatchPanel.cpp | 477 ++++++++++--------------- services/audioflinger/PatchPanel.h | 48 +-- 4 files changed, 231 insertions(+), 322 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 3c4f500a4f..f28132de3d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -159,6 +159,7 @@ AudioFlinger::AudioFlinger() mTotalMemory(0), mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes), mGlobalEffectEnableTime(0), + mPatchPanel(this), mSystemReady(false) { // unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum @@ -225,8 +226,6 @@ void AudioFlinger::onFirstRef() } } - mPatchPanel = new PatchPanel(this); - mMode = AUDIO_MODE_NORMAL; gAudioFlinger = this; @@ -1928,6 +1927,28 @@ size_t AudioFlinger::getClientSharedHeapSize() const return mClientSharedHeapSize; } +status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config) +{ + ALOGV(__func__); + + audio_module_handle_t module; + if (config->type == AUDIO_PORT_TYPE_DEVICE) { + module = config->ext.device.hw_module; + } else { + module = config->ext.mix.hw_module; + } + + Mutex::Autolock _l(mLock); + ssize_t index = mAudioHwDevs.indexOfKey(module); + if (index < 0) { + ALOGW("%s() bad hw module %d", __func__, module); + return BAD_VALUE; + } + + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(index); + return audioHwDevice->hwDevice()->setAudioPortConfig(config); +} + audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId) { Mutex::Autolock _l(mLock); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 963a87da58..692a90472a 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -843,7 +843,8 @@ private: nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled - sp mPatchPanel; + // protected by mLock + PatchPanel mPatchPanel; sp mEffectsFactoryHal; bool mSystemReady; diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index e5cb8a2138..3ae198bc84 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -49,107 +49,65 @@ status_t AudioFlinger::listAudioPorts(unsigned int *num_ports, struct audio_port *ports) { Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->listAudioPorts(num_ports, ports); - } - return NO_INIT; + return mPatchPanel.listAudioPorts(num_ports, ports); } /* Get supported attributes for a given audio port */ status_t AudioFlinger::getAudioPort(struct audio_port *port) { Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->getAudioPort(port); - } - return NO_INIT; + return mPatchPanel.getAudioPort(port); } - /* Connect a patch between several source and sink ports */ status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle) { Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->createAudioPatch(patch, handle); - } - return NO_INIT; + return mPatchPanel.createAudioPatch(patch, handle); } /* Disconnect a patch */ status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle) { Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->releaseAudioPatch(handle); - } - return NO_INIT; + return mPatchPanel.releaseAudioPatch(handle); } - /* List connected audio ports and they attributes */ status_t AudioFlinger::listAudioPatches(unsigned int *num_patches, struct audio_patch *patches) { Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->listAudioPatches(num_patches, patches); - } - return NO_INIT; -} - -/* Set audio port configuration */ -status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config) -{ - Mutex::Autolock _l(mLock); - if (mPatchPanel != 0) { - return mPatchPanel->setAudioPortConfig(config); - } - return NO_INIT; -} - - -AudioFlinger::PatchPanel::PatchPanel(const sp& audioFlinger) - : mAudioFlinger(audioFlinger) -{ -} - -AudioFlinger::PatchPanel::~PatchPanel() -{ + return mPatchPanel.listAudioPatches(num_patches, patches); } /* List connected audio ports and their attributes */ status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused, struct audio_port *ports __unused) { - ALOGV("listAudioPorts"); + ALOGV(__func__); return NO_ERROR; } /* Get supported attributes for a given audio port */ status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused) { - ALOGV("getAudioPort"); + ALOGV(__func__); return NO_ERROR; } - /* Connect a patch between several source and sink ports */ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle) { - status_t status = NO_ERROR; - audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; - sp audioflinger = mAudioFlinger.promote(); if (handle == NULL || patch == NULL) { return BAD_VALUE; } - ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", - patch->num_sources, patch->num_sinks, *handle); - if (audioflinger == 0) { - return NO_INIT; - } + ALOGV("%s() num_sources %d num_sinks %d handle %d", + __func__, patch->num_sources, patch->num_sinks, *handle); + status_t status = NO_ERROR; + audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || (patch->num_sinks == 0 && patch->num_sources != 2) || @@ -163,70 +121,66 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } if (*handle != AUDIO_PATCH_HANDLE_NONE) { - for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { - if (*handle == mPatches[index]->mHandle) { - ALOGV("createAudioPatch() removing patch handle %d", *handle); - halHandle = mPatches[index]->mHalHandle; - Patch *removedPatch = mPatches[index]; - // free resources owned by the removed patch if applicable - // 1) if a software patch is present, release the playback and capture threads and - // tracks created. This will also release the corresponding audio HAL patches - if ((removedPatch->mRecordPatchHandle - != AUDIO_PATCH_HANDLE_NONE) || - (removedPatch->mPlaybackPatchHandle != - AUDIO_PATCH_HANDLE_NONE)) { - clearPatchConnections(removedPatch); + auto iter = mPatches.find(*handle); + if (iter != mPatches.end()) { + ALOGV("%s() removing patch handle %d", __func__, *handle); + Patch &removedPatch = iter->second; + halHandle = removedPatch.mHalHandle; + // free resources owned by the removed patch if applicable + // 1) if a software patch is present, release the playback and capture threads and + // tracks created. This will also release the corresponding audio HAL patches + if ((removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) || + (removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE)) { + removedPatch.clearConnections(this); + } + // 2) if the new patch and old patch source or sink are devices from different + // hw modules, clear the audio HAL patches now because they will not be updated + // by call to create_audio_patch() below which will happen on a different HW module + if (halHandle != AUDIO_PATCH_HANDLE_NONE) { + audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE; + const struct audio_patch &oldPatch = removedPatch.mAudioPatch; + if (oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE && + (patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE || + oldPatch.sources[0].ext.device.hw_module != + patch->sources[0].ext.device.hw_module)) { + hwModule = oldPatch.sources[0].ext.device.hw_module; + } else if (patch->num_sinks == 0 || + (oldPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE && + (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE || + oldPatch.sinks[0].ext.device.hw_module != + patch->sinks[0].ext.device.hw_module))) { + // Note on (patch->num_sinks == 0): this situation should not happen as + // these special patches are only created by the policy manager but just + // in case, systematically clear the HAL patch. + // Note that removedPatch.mAudioPatch.num_sinks cannot be 0 here because + // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case. + hwModule = oldPatch.sinks[0].ext.device.hw_module; } - // 2) if the new patch and old patch source or sink are devices from different - // hw modules, clear the audio HAL patches now because they will not be updated - // by call to create_audio_patch() below which will happen on a different HW module - if (halHandle != AUDIO_PATCH_HANDLE_NONE) { - audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE; - if ((removedPatch->mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE) && - ((patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE) || - (removedPatch->mAudioPatch.sources[0].ext.device.hw_module != - patch->sources[0].ext.device.hw_module))) { - hwModule = removedPatch->mAudioPatch.sources[0].ext.device.hw_module; - } else if ((patch->num_sinks == 0) || - ((removedPatch->mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE) && - ((patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) || - (removedPatch->mAudioPatch.sinks[0].ext.device.hw_module != - patch->sinks[0].ext.device.hw_module)))) { - // Note on (patch->num_sinks == 0): this situation should not happen as - // these special patches are only created by the policy manager but just - // in case, systematically clear the HAL patch. - // Note that removedPatch->mAudioPatch.num_sinks cannot be 0 here because - // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case. - hwModule = removedPatch->mAudioPatch.sinks[0].ext.device.hw_module; - } - if (hwModule != AUDIO_MODULE_HANDLE_NONE) { - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(hwModule); - if (index >= 0) { - sp hwDevice = - audioflinger->mAudioHwDevs.valueAt(index)->hwDevice(); - hwDevice->releaseAudioPatch(halHandle); - } + if (hwModule != AUDIO_MODULE_HANDLE_NONE) { + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(hwModule); + if (index >= 0) { + sp hwDevice = + mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice(); + hwDevice->releaseAudioPatch(halHandle); } } - mPatches.removeAt(index); - delete removedPatch; - break; } + mPatches.erase(iter); } } - Patch *newPatch = new Patch(patch); + Patch newPatch{*patch}; switch (patch->sources[0].type) { case AUDIO_PORT_TYPE_DEVICE: { audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); if (index < 0) { - ALOGW("createAudioPatch() bad src hw module %d", srcModule); + ALOGW("%s() bad src hw module %d", __func__, srcModule); status = BAD_VALUE; goto exit; } - AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index); for (unsigned int i = 0; i < patch->num_sinks; i++) { // support only one sink if connection to a mix or across HW modules if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || @@ -237,7 +191,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } // reject connection to different sink types if (patch->sinks[i].type != patch->sinks[0].type) { - ALOGW("createAudioPatch() different sink types in same patch not supported"); + ALOGW("%s() different sink types in same patch not supported", __func__); status = BAD_VALUE; goto exit; } @@ -256,16 +210,16 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module != patch->sources[1].ext.mix.hw_module)) { - ALOGW("createAudioPatch() invalid source combination"); + ALOGW("%s() invalid source combination", __func__); status = INVALID_OPERATION; goto exit; } sp thread = - audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); - newPatch->mPlaybackThread = (MixerThread *)thread.get(); + mAudioFlinger.checkPlaybackThread_l(patch->sources[1].ext.mix.handle); + newPatch.mPlaybackThread = (MixerThread *)thread.get(); if (thread == 0) { - ALOGW("createAudioPatch() cannot get playback thread"); + ALOGW("%s() cannot get playback thread", __func__); status = INVALID_OPERATION; goto exit; } @@ -274,17 +228,17 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa audio_devices_t device = patch->sinks[0].ext.device.type; String8 address = String8(patch->sinks[0].ext.device.address); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; - sp thread = audioflinger->openOutput_l( + sp thread = mAudioFlinger.openOutput_l( patch->sinks[0].ext.device.hw_module, &output, &config, device, address, AUDIO_OUTPUT_FLAG_NONE); - newPatch->mPlaybackThread = (PlaybackThread *)thread.get(); - ALOGV("audioflinger->openOutput_l() returned %p", - newPatch->mPlaybackThread.get()); - if (newPatch->mPlaybackThread == 0) { + newPatch.mPlaybackThread = (PlaybackThread *)thread.get(); + ALOGV("mAudioFlinger.openOutput_l() returned %p", + newPatch.mPlaybackThread.get()); + if (newPatch.mPlaybackThread == 0) { status = NO_MEMORY; goto exit; } @@ -297,47 +251,47 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { config.sample_rate = patch->sources[0].sample_rate; } else { - config.sample_rate = newPatch->mPlaybackThread->sampleRate(); + config.sample_rate = newPatch.mPlaybackThread->sampleRate(); } if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { config.channel_mask = patch->sources[0].channel_mask; } else { config.channel_mask = - audio_channel_in_mask_from_count(newPatch->mPlaybackThread->channelCount()); + audio_channel_in_mask_from_count(newPatch.mPlaybackThread->channelCount()); } if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) { config.format = patch->sources[0].format; } else { - config.format = newPatch->mPlaybackThread->format(); + config.format = newPatch.mPlaybackThread->format(); } audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; - sp thread = audioflinger->openInput_l(srcModule, + sp thread = mAudioFlinger.openInput_l(srcModule, &input, &config, device, address, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE); - newPatch->mRecordThread = (RecordThread *)thread.get(); - ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", - newPatch->mRecordThread.get(), config.channel_mask); - if (newPatch->mRecordThread == 0) { + newPatch.mRecordThread = (RecordThread *)thread.get(); + ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x", + newPatch.mRecordThread.get(), config.channel_mask); + if (newPatch.mRecordThread == 0) { status = NO_MEMORY; goto exit; } - status = createPatchConnections(newPatch, patch); + status = newPatch.createConnections(this); if (status != NO_ERROR) { goto exit; } } else { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { - sp thread = audioflinger->checkRecordThread_l( + sp thread = mAudioFlinger.checkRecordThread_l( patch->sinks[0].ext.mix.handle); if (thread == 0) { - thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle); + thread = mAudioFlinger.checkMmapThread_l(patch->sinks[0].ext.mix.handle); if (thread == 0) { - ALOGW("createAudioPatch() bad capture I/O handle %d", - patch->sinks[0].ext.mix.handle); + ALOGW("%s() bad capture I/O handle %d", + __func__, patch->sinks[0].ext.mix.handle); status = BAD_VALUE; goto exit; } @@ -356,9 +310,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } break; case AUDIO_PORT_TYPE_MIX: { audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); if (index < 0) { - ALOGW("createAudioPatch() bad src hw module %d", srcModule); + ALOGW("%s() bad src hw module %d", __func__, srcModule); status = BAD_VALUE; goto exit; } @@ -366,8 +320,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa audio_devices_t type = AUDIO_DEVICE_NONE; for (unsigned int i = 0; i < patch->num_sinks; i++) { if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { - ALOGW("createAudioPatch() invalid sink type %d for mix source", - patch->sinks[i].type); + ALOGW("%s() invalid sink type %d for mix source", + __func__, patch->sinks[i].type); status = BAD_VALUE; goto exit; } @@ -379,21 +333,21 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa type |= patch->sinks[i].ext.device.type; } sp thread = - audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + mAudioFlinger.checkPlaybackThread_l(patch->sources[0].ext.mix.handle); if (thread == 0) { - thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle); + thread = mAudioFlinger.checkMmapThread_l(patch->sources[0].ext.mix.handle); if (thread == 0) { - ALOGW("createAudioPatch() bad playback I/O handle %d", - patch->sources[0].ext.mix.handle); + ALOGW("%s() bad playback I/O handle %d", + __func__, patch->sources[0].ext.mix.handle); status = BAD_VALUE; goto exit; } } - if (thread == audioflinger->primaryPlaybackThread_l()) { + if (thread == mAudioFlinger.primaryPlaybackThread_l()) { AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyRouting), (int)type); - audioflinger->broacastParametersToRecordThreads_l(param.toString()); + mAudioFlinger.broacastParametersToRecordThreads_l(param.toString()); } status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); @@ -403,72 +357,69 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa goto exit; } exit: - ALOGV("createAudioPatch() status %d", status); + ALOGV("%s() status %d", __func__, status); if (status == NO_ERROR) { - *handle = (audio_patch_handle_t) audioflinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); - newPatch->mHandle = *handle; - newPatch->mHalHandle = halHandle; - mPatches.add(newPatch); - ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); + *handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); + newPatch.mHalHandle = halHandle; + mPatches.insert(std::make_pair(*handle, std::move(newPatch))); + ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle); } else { - clearPatchConnections(newPatch); - delete newPatch; + newPatch.clearConnections(this); } return status; } -status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, - const struct audio_patch *audioPatch) +status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) { // create patch from source device to record thread input struct audio_patch subPatch; subPatch.num_sources = 1; - subPatch.sources[0] = audioPatch->sources[0]; + subPatch.sources[0] = mAudioPatch.sources[0]; subPatch.num_sinks = 1; - patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); + mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; - status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); + status_t status = panel->createAudioPatch(&subPatch, &mRecordPatchHandle); if (status != NO_ERROR) { - patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; return status; } // create patch from playback thread output to sink device - if (audioPatch->num_sinks != 0) { - patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); - subPatch.sinks[0] = audioPatch->sinks[0]; - status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); + if (mAudioPatch.num_sinks != 0) { + mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); + subPatch.sinks[0] = mAudioPatch.sinks[0]; + status = panel->createAudioPatch(&subPatch, &mPlaybackPatchHandle); if (status != NO_ERROR) { - patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; return status; } } else { - patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; } // use a pseudo LCM between input and output framecount - size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); + size_t playbackFrameCount = mPlaybackThread->frameCount(); int playbackShift = __builtin_ctz(playbackFrameCount); - size_t recordFramecount = patch->mRecordThread->frameCount(); + size_t recordFramecount = mRecordThread->frameCount(); int shift = __builtin_ctz(recordFramecount); if (playbackShift < shift) { shift = playbackShift; } size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; - ALOGV("createPatchConnections() playframeCount %zu recordFramecount %zu frameCount %zu", - playbackFrameCount, recordFramecount, frameCount); + ALOGV("%s() playframeCount %zu recordFramecount %zu frameCount %zu", + __func__, playbackFrameCount, recordFramecount, frameCount); // create a special record track to capture from record thread - uint32_t channelCount = patch->mPlaybackThread->channelCount(); + uint32_t channelCount = mPlaybackThread->channelCount(); audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); - audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); - uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); - audio_format_t format = patch->mPlaybackThread->format(); + audio_channel_mask_t outChannelMask = mPlaybackThread->channelMask(); + uint32_t sampleRate = mPlaybackThread->sampleRate(); + audio_format_t format = mPlaybackThread->format(); - patch->mPatchRecord = new RecordThread::PatchRecord( - patch->mRecordThread.get(), + mPatchRecord = new RecordThread::PatchRecord( + mRecordThread.get(), sampleRate, inChannelMask, format, @@ -476,94 +427,89 @@ status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, NULL, (size_t)0 /* bufferSize */, AUDIO_INPUT_FLAG_NONE); - if (patch->mPatchRecord == 0) { + if (mPatchRecord == 0) { return NO_MEMORY; } - status = patch->mPatchRecord->initCheck(); + status = mPatchRecord->initCheck(); if (status != NO_ERROR) { return status; } - patch->mRecordThread->addPatchRecord(patch->mPatchRecord); + mRecordThread->addPatchRecord(mPatchRecord); // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer - patch->mPatchTrack = new PlaybackThread::PatchTrack( - patch->mPlaybackThread.get(), - audioPatch->sources[1].ext.mix.usecase.stream, + mPatchTrack = new PlaybackThread::PatchTrack( + mPlaybackThread.get(), + mAudioPatch.sources[1].ext.mix.usecase.stream, sampleRate, outChannelMask, format, frameCount, - patch->mPatchRecord->buffer(), - patch->mPatchRecord->bufferSize(), + mPatchRecord->buffer(), + mPatchRecord->bufferSize(), AUDIO_OUTPUT_FLAG_NONE); - status = patch->mPatchTrack->initCheck(); + status = mPatchTrack->initCheck(); if (status != NO_ERROR) { return status; } - patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); + mPlaybackThread->addPatchTrack(mPatchTrack); // tie playback and record tracks together - patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); - patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); + mPatchRecord->setPeerProxy(mPatchTrack.get()); + mPatchTrack->setPeerProxy(mPatchRecord.get()); // start capture and playback - patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); - patch->mPatchTrack->start(); + mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); + mPatchTrack->start(); return status; } -void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) +void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) { - sp audioflinger = mAudioFlinger.promote(); - if (audioflinger == 0) { - return; - } - - ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", - patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); + ALOGV("%s() mRecordPatchHandle %d mPlaybackPatchHandle %d", + __func__, mRecordPatchHandle, mPlaybackPatchHandle); - if (patch->mPatchRecord != 0) { - patch->mPatchRecord->stop(); + if (mPatchRecord != 0) { + mPatchRecord->stop(); } - if (patch->mPatchTrack != 0) { - patch->mPatchTrack->stop(); + if (mPatchTrack != 0) { + mPatchTrack->stop(); } - if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { - releaseAudioPatch(patch->mRecordPatchHandle); - patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + if (mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + panel->releaseAudioPatch(mRecordPatchHandle); + mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; } - if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { - releaseAudioPatch(patch->mPlaybackPatchHandle); - patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + if (mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + panel->releaseAudioPatch(mPlaybackPatchHandle); + mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; } - if (patch->mRecordThread != 0) { - if (patch->mPatchRecord != 0) { - patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); + if (mRecordThread != 0) { + if (mPatchRecord != 0) { + mRecordThread->deletePatchRecord(mPatchRecord); } - audioflinger->closeInputInternal_l(patch->mRecordThread); + panel->mAudioFlinger.closeInputInternal_l(mRecordThread); } - if (patch->mPlaybackThread != 0) { - if (patch->mPatchTrack != 0) { - patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); + if (mPlaybackThread != 0) { + if (mPatchTrack != 0) { + mPlaybackThread->deletePatchTrack(mPatchTrack); } // if num sources == 2 we are reusing an existing playback thread so we do not close it - if (patch->mAudioPatch.num_sources != 2) { - audioflinger->closeOutputInternal_l(patch->mPlaybackThread); + if (mAudioPatch.num_sources != 2) { + panel->mAudioFlinger.closeOutputInternal_l(mPlaybackThread); } } - if (patch->mRecordThread != 0) { - if (patch->mPatchRecord != 0) { - patch->mPatchRecord.clear(); + if (mRecordThread != 0) { + if (mPatchRecord != 0) { + mPatchRecord.clear(); } - patch->mRecordThread.clear(); + mRecordThread.clear(); } - if (patch->mPlaybackThread != 0) { - if (patch->mPatchTrack != 0) { - patch->mPatchTrack.clear(); + if (mPlaybackThread != 0) { + if (mPatchTrack != 0) { + mPatchTrack.clear(); } - patch->mPlaybackThread.clear(); + mPlaybackThread.clear(); } } @@ -571,127 +517,84 @@ void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) /* Disconnect a patch */ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) { - ALOGV("releaseAudioPatch handle %d", handle); + ALOGV("%s handle %d", __func__, handle); status_t status = NO_ERROR; - size_t index; - sp audioflinger = mAudioFlinger.promote(); - if (audioflinger == 0) { - return NO_INIT; - } - - for (index = 0; index < mPatches.size(); index++) { - if (handle == mPatches[index]->mHandle) { - break; - } - } - if (index == mPatches.size()) { + auto iter = mPatches.find(handle); + if (iter == mPatches.end()) { return BAD_VALUE; } - Patch *removedPatch = mPatches[index]; - mPatches.removeAt(index); - - struct audio_patch *patch = &removedPatch->mAudioPatch; + Patch &removedPatch = iter->second; + const struct audio_patch &patch = removedPatch.mAudioPatch; - switch (patch->sources[0].type) { + switch (patch.sources[0].type) { case AUDIO_PORT_TYPE_DEVICE: { - audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + audio_module_handle_t srcModule = patch.sources[0].ext.device.hw_module; + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); if (index < 0) { - ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + ALOGW("%s() bad src hw module %d", __func__, srcModule); status = BAD_VALUE; break; } - if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || - removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { - clearPatchConnections(removedPatch); + if (removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || + removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + removedPatch.clearConnections(this); break; } - if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { - sp thread = audioflinger->checkRecordThread_l( - patch->sinks[0].ext.mix.handle); + if (patch.sinks[0].type == AUDIO_PORT_TYPE_MIX) { + audio_io_handle_t ioHandle = patch.sinks[0].ext.mix.handle; + sp thread = mAudioFlinger.checkRecordThread_l(ioHandle); if (thread == 0) { - thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle); + thread = mAudioFlinger.checkMmapThread_l(ioHandle); if (thread == 0) { - ALOGW("releaseAudioPatch() bad capture I/O handle %d", - patch->sinks[0].ext.mix.handle); + ALOGW("%s() bad capture I/O handle %d", __func__, ioHandle); status = BAD_VALUE; break; } } - status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle); } else { - AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index); sp hwDevice = audioHwDevice->hwDevice(); - status = hwDevice->releaseAudioPatch(removedPatch->mHalHandle); + status = hwDevice->releaseAudioPatch(removedPatch.mHalHandle); } } break; case AUDIO_PORT_TYPE_MIX: { - audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + audio_module_handle_t srcModule = patch.sources[0].ext.mix.hw_module; + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); if (index < 0) { - ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + ALOGW("%s() bad src hw module %d", __func__, srcModule); status = BAD_VALUE; break; } - sp thread = - audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + audio_io_handle_t ioHandle = patch.sources[0].ext.mix.handle; + sp thread = mAudioFlinger.checkPlaybackThread_l(ioHandle); if (thread == 0) { - thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle); + thread = mAudioFlinger.checkMmapThread_l(ioHandle); if (thread == 0) { - ALOGW("releaseAudioPatch() bad playback I/O handle %d", - patch->sources[0].ext.mix.handle); + ALOGW("%s() bad playback I/O handle %d", __func__, ioHandle); status = BAD_VALUE; break; } } - status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle); } break; default: status = BAD_VALUE; - break; } - delete removedPatch; + mPatches.erase(iter); return status; } - /* List connected audio ports and they attributes */ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, struct audio_patch *patches __unused) { - ALOGV("listAudioPatches"); + ALOGV(__func__); return NO_ERROR; } -/* Set audio port configuration */ -status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config) -{ - ALOGV("setAudioPortConfig"); - - sp audioflinger = mAudioFlinger.promote(); - if (audioflinger == 0) { - return NO_INIT; - } - - audio_module_handle_t module; - if (config->type == AUDIO_PORT_TYPE_DEVICE) { - module = config->ext.device.hw_module; - } else { - module = config->ext.mix.hw_module; - } - - ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module); - if (index < 0) { - ALOGW("setAudioPortConfig() bad hw module %d", module); - return BAD_VALUE; - } - - AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); - return audioHwDevice->hwDevice()->setAudioPortConfig(config); -} - } // namespace android diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index d37c0d3297..c2cb7ac5a4 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -19,13 +19,10 @@ #error This header file should only be included from AudioFlinger.h #endif -class PatchPanel : public RefBase { +// PatchPanel is concealed within AudioFlinger, their lifetimes are the same. +class PatchPanel { public: - - class Patch; - - explicit PatchPanel(const sp& audioFlinger); - virtual ~PatchPanel(); + explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {} /* List connected audio ports and their attributes */ status_t listAudioPorts(unsigned int *num_ports, @@ -45,46 +42,33 @@ public: status_t listAudioPatches(unsigned int *num_patches, struct audio_patch *patches); - /* Set audio port configuration */ - status_t setAudioPortConfig(const struct audio_port_config *config); - - status_t createPatchConnections(Patch *patch, - const struct audio_patch *audioPatch); - void clearPatchConnections(Patch *patch); - +private: class Patch { public: - explicit Patch(const struct audio_patch *patch) : - mAudioPatch(*patch), mHandle(AUDIO_PATCH_HANDLE_NONE), - mHalHandle(AUDIO_PATCH_HANDLE_NONE), mRecordPatchHandle(AUDIO_PATCH_HANDLE_NONE), - mPlaybackPatchHandle(AUDIO_PATCH_HANDLE_NONE) {} - ~Patch() {} + explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {} + + status_t createConnections(PatchPanel *panel); + void clearConnections(PatchPanel *panel); + // Note that audio_patch::id is only unique within a HAL module struct audio_patch mAudioPatch; - audio_patch_handle_t mHandle; // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0 - audio_patch_handle_t mHalHandle; + audio_patch_handle_t mHalHandle = AUDIO_PATCH_HANDLE_NONE; // below members are used by a software audio patch connecting a source device from a // given audio HW module to a sink device on an other audio HW module. - // playback thread created by createAudioPatch() and released by clearPatchConnections() if - // no existing playback thread can be used by the software patch + // the objects are created by createConnections() and released by clearConnections() + // playback thread is created if no existing playback thread can be used sp mPlaybackThread; - // audio track created by createPatchConnections() and released by clearPatchConnections() sp mPatchTrack; - // record thread created by createAudioPatch() and released by clearPatchConnections() sp mRecordThread; - // audio record created by createPatchConnections() and released by clearPatchConnections() sp mPatchRecord; // handle for audio patch connecting source device to record thread input. - // created by createPatchConnections() and released by clearPatchConnections() - audio_patch_handle_t mRecordPatchHandle; + audio_patch_handle_t mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; // handle for audio patch connecting playback thread output to sink device - // created by createPatchConnections() and released by clearPatchConnections() - audio_patch_handle_t mPlaybackPatchHandle; + audio_patch_handle_t mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; }; -private: - const wp mAudioFlinger; - SortedVector mPatches; + AudioFlinger &mAudioFlinger; + std::map mPatches; }; -- GitLab From b4542562d8df69f864e97b5d1f100021fdec6d44 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Apr 2018 12:40:13 -0700 Subject: [PATCH 0049/1530] Add reference MSD audio policy configuration Add the XML configuration file: msd_audio_policy_configuration.xml Include the configuration file into reference audio_policy_configuration.xml Bug: 63901775 Test: booting a device with MSD HAL present, and without Change-Id: If2547a6d9c865bb511d5223bcb84dae057acca67 --- .../config/audio_policy_configuration.xml | 3 + .../config/msd_audio_policy_configuration.xml | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 services/audiopolicy/config/msd_audio_policy_configuration.xml diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index a75f1cbc4a..9381f1f1c9 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -185,6 +185,9 @@ + + + diff --git a/services/audiopolicy/config/msd_audio_policy_configuration.xml b/services/audiopolicy/config/msd_audio_policy_configuration.xml new file mode 100644 index 0000000000..a84117eca1 --- /dev/null +++ b/services/audiopolicy/config/msd_audio_policy_configuration.xml @@ -0,0 +1,62 @@ + + + + + + MS12 Input + MS12 Output + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From 444ecc3476107a1e80f1573b20ceca5e9949e4ae Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 1 May 2018 17:40:05 -0700 Subject: [PATCH 0050/1530] PatchPanel: Cleanup and refactoring * Unified software patch endpoints management code. * Noticed that Patch::clearConnections is only called before getting rid of Patch instance, so there is no need to explicitly clear references to endpoints' Track and Thread. * Fixed out-of-memory handling when creating tracks for a software patch. * Factored out finding HAL Device by module handle. Test: verify transitions to/from BT while playing media and making calls Change-Id: If6459c477054d6dff60dfa13f2d99ee2d6e887ad --- services/audioflinger/AudioFlinger.cpp | 4 +- services/audioflinger/AudioFlinger.h | 4 +- services/audioflinger/PatchPanel.cpp | 188 ++++++++++--------------- services/audioflinger/PatchPanel.h | 64 +++++++-- services/audioflinger/Threads.cpp | 4 +- services/audioflinger/Threads.h | 4 +- 6 files changed, 138 insertions(+), 130 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index f28132de3d..e3e53b798b 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2293,7 +2293,7 @@ void AudioFlinger::closeOutputFinish(const sp& thread) delete out; } -void AudioFlinger::closeOutputInternal_l(const sp& thread) +void AudioFlinger::closeThreadInternal_l(const sp& thread) { mPlaybackThreads.removeItem(thread->mId); thread->exit(); @@ -2586,7 +2586,7 @@ void AudioFlinger::closeInputFinish(const sp& thread) delete in; } -void AudioFlinger::closeInputInternal_l(const sp& thread) +void AudioFlinger::closeThreadInternal_l(const sp& thread) { mRecordThreads.removeItem(thread->mId); closeInputFinish(thread); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 692a90472a..7cfe542412 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -791,9 +791,9 @@ private: // for use from destructor status_t closeOutput_nonvirtual(audio_io_handle_t output); - void closeOutputInternal_l(const sp& thread); + void closeThreadInternal_l(const sp& thread); status_t closeInput_nonvirtual(audio_io_handle_t input); - void closeInputInternal_l(const sp& thread); + void closeThreadInternal_l(const sp& thread); void setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId); status_t checkStreamType(audio_stream_type_t stream) const; diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 3ae198bc84..eaf1120c68 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -125,18 +125,16 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (iter != mPatches.end()) { ALOGV("%s() removing patch handle %d", __func__, *handle); Patch &removedPatch = iter->second; - halHandle = removedPatch.mHalHandle; // free resources owned by the removed patch if applicable // 1) if a software patch is present, release the playback and capture threads and // tracks created. This will also release the corresponding audio HAL patches - if ((removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) || - (removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE)) { + if (removedPatch.isSoftware()) { removedPatch.clearConnections(this); } // 2) if the new patch and old patch source or sink are devices from different // hw modules, clear the audio HAL patches now because they will not be updated // by call to create_audio_patch() below which will happen on a different HW module - if (halHandle != AUDIO_PATCH_HANDLE_NONE) { + if (removedPatch.mHalHandle != AUDIO_PATCH_HANDLE_NONE) { audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE; const struct audio_patch &oldPatch = removedPatch.mAudioPatch; if (oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE && @@ -153,16 +151,12 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa // these special patches are only created by the policy manager but just // in case, systematically clear the HAL patch. // Note that removedPatch.mAudioPatch.num_sinks cannot be 0 here because - // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case. + // removedPatch.mHalHandle would be AUDIO_PATCH_HANDLE_NONE in this case. hwModule = oldPatch.sinks[0].ext.device.hw_module; } - if (hwModule != AUDIO_MODULE_HANDLE_NONE) { - ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(hwModule); - if (index >= 0) { - sp hwDevice = - mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice(); - hwDevice->releaseAudioPatch(halHandle); - } + sp hwDevice = findHwDeviceByModule(hwModule); + if (hwDevice != 0) { + hwDevice->releaseAudioPatch(removedPatch.mHalHandle); } } mPatches.erase(iter); @@ -217,12 +211,14 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa sp thread = mAudioFlinger.checkPlaybackThread_l(patch->sources[1].ext.mix.handle); - newPatch.mPlaybackThread = (MixerThread *)thread.get(); if (thread == 0) { ALOGW("%s() cannot get playback thread", __func__); status = INVALID_OPERATION; goto exit; } + // existing playback thread is reused, so it is not closed when patch is cleared + newPatch.mPlayback.setThread( + reinterpret_cast(thread.get()), false /*closeThread*/); } else { audio_config_t config = AUDIO_CONFIG_INITIALIZER; audio_devices_t device = patch->sinks[0].ext.device.type; @@ -235,13 +231,12 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa device, address, AUDIO_OUTPUT_FLAG_NONE); - newPatch.mPlaybackThread = (PlaybackThread *)thread.get(); - ALOGV("mAudioFlinger.openOutput_l() returned %p", - newPatch.mPlaybackThread.get()); - if (newPatch.mPlaybackThread == 0) { + ALOGV("mAudioFlinger.openOutput_l() returned %p", thread.get()); + if (thread == 0) { status = NO_MEMORY; goto exit; } + newPatch.mPlayback.setThread(reinterpret_cast(thread.get())); } audio_devices_t device = patch->sources[0].ext.device.type; String8 address = String8(patch->sources[0].ext.device.address); @@ -251,18 +246,18 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { config.sample_rate = patch->sources[0].sample_rate; } else { - config.sample_rate = newPatch.mPlaybackThread->sampleRate(); + config.sample_rate = newPatch.mPlayback.thread()->sampleRate(); } if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { config.channel_mask = patch->sources[0].channel_mask; } else { - config.channel_mask = - audio_channel_in_mask_from_count(newPatch.mPlaybackThread->channelCount()); + config.channel_mask = audio_channel_in_mask_from_count( + newPatch.mPlayback.thread()->channelCount()); } if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) { config.format = patch->sources[0].format; } else { - config.format = newPatch.mPlaybackThread->format(); + config.format = newPatch.mPlayback.thread()->format(); } audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; sp thread = mAudioFlinger.openInput_l(srcModule, @@ -272,13 +267,13 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa address, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE); - newPatch.mRecordThread = (RecordThread *)thread.get(); ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x", - newPatch.mRecordThread.get(), config.channel_mask); - if (newPatch.mRecordThread == 0) { + thread.get(), config.channel_mask); + if (thread == 0) { status = NO_MEMORY; goto exit; } + newPatch.mRecord.setThread(reinterpret_cast(thread.get())); status = newPatch.createConnections(this); if (status != NO_ERROR) { goto exit; @@ -369,6 +364,12 @@ exit: return status; } +AudioFlinger::PatchPanel::Patch::~Patch() +{ + ALOGE_IF(isSoftware(), "Software patch connections leaked %d %d", + mRecord.handle(), mPlayback.handle()); +} + status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) { // create patch from source device to record thread input @@ -377,32 +378,32 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) subPatch.sources[0] = mAudioPatch.sources[0]; subPatch.num_sinks = 1; - mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); + mRecord.thread()->getAudioPortConfig(&subPatch.sinks[0]); subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; - status_t status = panel->createAudioPatch(&subPatch, &mRecordPatchHandle); + status_t status = panel->createAudioPatch(&subPatch, mRecord.handlePtr()); if (status != NO_ERROR) { - mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + *mRecord.handlePtr() = AUDIO_PATCH_HANDLE_NONE; return status; } // create patch from playback thread output to sink device if (mAudioPatch.num_sinks != 0) { - mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); + mPlayback.thread()->getAudioPortConfig(&subPatch.sources[0]); subPatch.sinks[0] = mAudioPatch.sinks[0]; - status = panel->createAudioPatch(&subPatch, &mPlaybackPatchHandle); + status = panel->createAudioPatch(&subPatch, mPlayback.handlePtr()); if (status != NO_ERROR) { - mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE; return status; } } else { - mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE; } // use a pseudo LCM between input and output framecount - size_t playbackFrameCount = mPlaybackThread->frameCount(); + size_t playbackFrameCount = mPlayback.thread()->frameCount(); int playbackShift = __builtin_ctz(playbackFrameCount); - size_t recordFramecount = mRecordThread->frameCount(); + size_t recordFramecount = mRecord.thread()->frameCount(); int shift = __builtin_ctz(recordFramecount); if (playbackShift < shift) { shift = playbackShift; @@ -412,14 +413,14 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) __func__, playbackFrameCount, recordFramecount, frameCount); // create a special record track to capture from record thread - uint32_t channelCount = mPlaybackThread->channelCount(); + uint32_t channelCount = mPlayback.thread()->channelCount(); audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); - audio_channel_mask_t outChannelMask = mPlaybackThread->channelMask(); - uint32_t sampleRate = mPlaybackThread->sampleRate(); - audio_format_t format = mPlaybackThread->format(); + audio_channel_mask_t outChannelMask = mPlayback.thread()->channelMask(); + uint32_t sampleRate = mPlayback.thread()->sampleRate(); + audio_format_t format = mPlayback.thread()->format(); - mPatchRecord = new RecordThread::PatchRecord( - mRecordThread.get(), + sp tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord( + mRecord.thread().get(), sampleRate, inChannelMask, format, @@ -427,91 +428,47 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) NULL, (size_t)0 /* bufferSize */, AUDIO_INPUT_FLAG_NONE); - if (mPatchRecord == 0) { - return NO_MEMORY; - } - status = mPatchRecord->initCheck(); + status = mRecord.checkTrack(tempRecordTrack.get()); if (status != NO_ERROR) { return status; } - mRecordThread->addPatchRecord(mPatchRecord); // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer - mPatchTrack = new PlaybackThread::PatchTrack( - mPlaybackThread.get(), + sp tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( + mPlayback.thread().get(), mAudioPatch.sources[1].ext.mix.usecase.stream, sampleRate, outChannelMask, format, frameCount, - mPatchRecord->buffer(), - mPatchRecord->bufferSize(), + tempRecordTrack->buffer(), + tempRecordTrack->bufferSize(), AUDIO_OUTPUT_FLAG_NONE); - status = mPatchTrack->initCheck(); + status = mPlayback.checkTrack(tempPatchTrack.get()); if (status != NO_ERROR) { return status; } - mPlaybackThread->addPatchTrack(mPatchTrack); // tie playback and record tracks together - mPatchRecord->setPeerProxy(mPatchTrack.get()); - mPatchTrack->setPeerProxy(mPatchRecord.get()); + mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack.get()); + mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack.get()); // start capture and playback - mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); - mPatchTrack->start(); + mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); + mPlayback.track()->start(); return status; } void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) { - ALOGV("%s() mRecordPatchHandle %d mPlaybackPatchHandle %d", - __func__, mRecordPatchHandle, mPlaybackPatchHandle); - - if (mPatchRecord != 0) { - mPatchRecord->stop(); - } - if (mPatchTrack != 0) { - mPatchTrack->stop(); - } - if (mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { - panel->releaseAudioPatch(mRecordPatchHandle); - mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; - } - if (mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { - panel->releaseAudioPatch(mPlaybackPatchHandle); - mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; - } - if (mRecordThread != 0) { - if (mPatchRecord != 0) { - mRecordThread->deletePatchRecord(mPatchRecord); - } - panel->mAudioFlinger.closeInputInternal_l(mRecordThread); - } - if (mPlaybackThread != 0) { - if (mPatchTrack != 0) { - mPlaybackThread->deletePatchTrack(mPatchTrack); - } - // if num sources == 2 we are reusing an existing playback thread so we do not close it - if (mAudioPatch.num_sources != 2) { - panel->mAudioFlinger.closeOutputInternal_l(mPlaybackThread); - } - } - if (mRecordThread != 0) { - if (mPatchRecord != 0) { - mPatchRecord.clear(); - } - mRecordThread.clear(); - } - if (mPlaybackThread != 0) { - if (mPatchTrack != 0) { - mPatchTrack.clear(); - } - mPlaybackThread.clear(); - } - + ALOGV("%s() mRecord.handle %d mPlayback.handle %d", + __func__, mRecord.handle(), mPlayback.handle()); + mRecord.stopTrack(); + mPlayback.stopTrack(); + mRecord.closeConnections(panel); + mPlayback.closeConnections(panel); } /* Disconnect a patch */ @@ -527,18 +484,17 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle Patch &removedPatch = iter->second; const struct audio_patch &patch = removedPatch.mAudioPatch; - switch (patch.sources[0].type) { + const struct audio_port_config &src = patch.sources[0]; + switch (src.type) { case AUDIO_PORT_TYPE_DEVICE: { - audio_module_handle_t srcModule = patch.sources[0].ext.device.hw_module; - ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); - if (index < 0) { - ALOGW("%s() bad src hw module %d", __func__, srcModule); + sp hwDevice = findHwDeviceByModule(src.ext.device.hw_module); + if (hwDevice == 0) { + ALOGW("%s() bad src hw module %d", __func__, src.ext.device.hw_module); status = BAD_VALUE; break; } - if (removedPatch.mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE || - removedPatch.mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + if (removedPatch.isSoftware()) { removedPatch.clearConnections(this); break; } @@ -556,20 +512,16 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle } status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle); } else { - AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index); - sp hwDevice = audioHwDevice->hwDevice(); status = hwDevice->releaseAudioPatch(removedPatch.mHalHandle); } } break; case AUDIO_PORT_TYPE_MIX: { - audio_module_handle_t srcModule = patch.sources[0].ext.mix.hw_module; - ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); - if (index < 0) { - ALOGW("%s() bad src hw module %d", __func__, srcModule); + if (findHwDeviceByModule(src.ext.mix.hw_module) == 0) { + ALOGW("%s() bad src hw module %d", __func__, src.ext.mix.hw_module); status = BAD_VALUE; break; } - audio_io_handle_t ioHandle = patch.sources[0].ext.mix.handle; + audio_io_handle_t ioHandle = src.ext.mix.handle; sp thread = mAudioFlinger.checkPlaybackThread_l(ioHandle); if (thread == 0) { thread = mAudioFlinger.checkMmapThread_l(ioHandle); @@ -597,4 +549,14 @@ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __ return NO_ERROR; } +sp AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module) +{ + if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr; + ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module); + if (index < 0) { + return nullptr; + } + return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice(); +} + } // namespace android diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index c2cb7ac5a4..5a68960dfb 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -43,12 +43,61 @@ public: struct audio_patch *patches); private: + template + class Endpoint { + public: + status_t checkTrack(TrackType *trackOrNull) const { + if (trackOrNull == nullptr) return NO_MEMORY; + return trackOrNull->initCheck(); + } + audio_patch_handle_t handle() const { return mHandle; } + sp thread() { return mThread; } + sp track() { return mTrack; } + + void closeConnections(PatchPanel *panel) { + if (mHandle != AUDIO_PATCH_HANDLE_NONE) { + panel->releaseAudioPatch(mHandle); + mHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (mThread != 0) { + if (mTrack != 0) { + mThread->deletePatchTrack(mTrack); + } + if (mCloseThread) { + panel->mAudioFlinger.closeThreadInternal_l(mThread); + } + } + } + audio_patch_handle_t* handlePtr() { return &mHandle; } + void setThread(const sp& thread, bool closeThread = true) { + mThread = thread; + mCloseThread = closeThread; + } + void setTrackAndPeer(const sp& track, + ThreadBase::PatchProxyBufferProvider *peer) { + mTrack = track; + mThread->addPatchTrack(mTrack); + mTrack->setPeerProxy(peer); + } + void stopTrack() { if (mTrack) mTrack->stop(); } + + private: + sp mThread; + bool mCloseThread = true; + audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE; + sp mTrack; + }; + class Patch { public: explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {} + ~Patch(); status_t createConnections(PatchPanel *panel); void clearConnections(PatchPanel *panel); + bool isSoftware() const { + return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE || + mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; } // Note that audio_patch::id is only unique within a HAL module struct audio_patch mAudioPatch; @@ -58,17 +107,14 @@ private: // given audio HW module to a sink device on an other audio HW module. // the objects are created by createConnections() and released by clearConnections() // playback thread is created if no existing playback thread can be used - sp mPlaybackThread; - sp mPatchTrack; - sp mRecordThread; - sp mPatchRecord; - // handle for audio patch connecting source device to record thread input. - audio_patch_handle_t mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; - // handle for audio patch connecting playback thread output to sink device - audio_patch_handle_t mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; - + // connects playback thread output to sink device + Endpoint mPlayback; + // connects source device to record thread input + Endpoint mRecord; }; + sp findHwDeviceByModule(audio_module_handle_t module); + AudioFlinger &mAudioFlinger; std::map mPatches; }; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index c47aa016c1..998cf4ec33 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7835,13 +7835,13 @@ status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handl return status; } -void AudioFlinger::RecordThread::addPatchRecord(const sp& record) +void AudioFlinger::RecordThread::addPatchTrack(const sp& record) { Mutex::Autolock _l(mLock); mTracks.add(record); } -void AudioFlinger::RecordThread::deletePatchRecord(const sp& record) +void AudioFlinger::RecordThread::deletePatchTrack(const sp& record) { Mutex::Autolock _l(mLock); destroyTrack_l(record); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 28d4482129..c490fb58c7 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1437,8 +1437,8 @@ public: audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); - void addPatchRecord(const sp& record); - void deletePatchRecord(const sp& record); + void addPatchTrack(const sp& record); + void deletePatchTrack(const sp& record); void readInputParameters_l(); virtual uint32_t getInputFramesLost(); -- GitLab From 1ed007c1e1d1a400cbfa55c4ea649dd48eab09ef Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 4 May 2018 15:41:10 -0700 Subject: [PATCH 0051/1530] Remove obsolete include path from makefiles frameworks/av/services/audiopolicy/utilities does not exist, remove it from audio service components' makefiles. Test: build Change-Id: I6c29052b449099d4b0942b7fc0a7ae68d0dc1ba7 --- services/audiopolicy/Android.mk | 2 -- services/audiopolicy/common/managerdefinitions/Android.mk | 3 +-- services/audiopolicy/engineconfigurable/wrapper/Android.mk | 1 - services/audiopolicy/tests/Android.mk | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 65571f9b86..39a9dc771b 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -13,7 +13,6 @@ LOCAL_C_INCLUDES := \ $(call include-path-for, audio-utils) \ frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ - frameworks/av/services/audiopolicy/utilities LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -72,7 +71,6 @@ endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) LOCAL_C_INCLUDES += \ frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ - frameworks/av/services/audiopolicy/utilities LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index e69e687d0a..b3611c4c0e 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -35,8 +35,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy \ - frameworks/av/services/audiopolicy/utilities \ - system/media/audio_utils/include \ + $(call include-path-for, audio-utils) \ ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk index 36e0f429b7..b128a38098 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk @@ -10,7 +10,6 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ frameworks/av/services/audiopolicy/engineconfigurable/include \ frameworks/av/services/audiopolicy/engineconfigurable/interface \ - frameworks/av/services/audiopolicy/utilities/convert \ LOCAL_SRC_FILES:= ParameterManagerWrapper.cpp diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index a43daea3a7..cfa9ab1141 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -6,7 +6,6 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ - frameworks/av/services/audiopolicy/utilities LOCAL_SHARED_LIBRARIES := \ libaudiopolicymanagerdefault \ -- GitLab From b421f6ea33ed85813740c9c60618ed3a82f2c167 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Mon, 7 May 2018 11:50:21 -0700 Subject: [PATCH 0052/1530] Initialize mMetaData correctly, fix ref count mMetaData was inconsistently initialized to either a MetaData or MetaDataBase object. The former resulted in the destruction of a RefBase object without ever incrementing its reference count. This is normally very brittle, since the accidental creation of an sp<> results in duplicate deallocation. Hence we are starting to diagnose it. In this case it appears to just add extra overhead. Since mRefCount can be concurrently accessed, declare it as atomic. This is a minimal fix, which can be improved further. Local and remote reference counts are currently still read with inconsistent memory orders. Since the results appear to only be trusted when they guarantee that there is no concurrent access (as they should be), memory_order_relaxed on loads seems sufficient. Generally reference count increments can also be relaxed, while decrements should be acq_rel to ensure that all accesses to the object become effective before deallocation. I believe that's correct here as well. Since I'm unfamiliar with the code, I made only essential changes. Bug: 79265389 Test: Built and booted master. Change-Id: I3cb65e9f880588abdb0712890e7a654311577b07 --- media/libmediaextractor/MediaBuffer.cpp | 14 +++++++------- .../include/media/stagefright/MediaBuffer.h | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp index 39f8d6ee4f..d197b3f0a7 100644 --- a/media/libmediaextractor/MediaBuffer.cpp +++ b/media/libmediaextractor/MediaBuffer.cpp @@ -39,7 +39,7 @@ MediaBuffer::MediaBuffer(void *data, size_t size) mRangeOffset(0), mRangeLength(size), mOwnsData(false), - mMetaData(new MetaData), + mMetaData(new MetaDataBase), mOriginal(NULL) { } @@ -51,7 +51,7 @@ MediaBuffer::MediaBuffer(size_t size) mRangeOffset(0), mRangeLength(size), mOwnsData(true), - mMetaData(new MetaData), + mMetaData(new MetaDataBase), mOriginal(NULL) { if (size < kSharedMemThreshold || std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) { @@ -84,7 +84,7 @@ MediaBuffer::MediaBuffer(const sp &buffer) mRangeLength(mSize), mBuffer(buffer), mOwnsData(false), - mMetaData(new MetaData), + mMetaData(new MetaDataBase), mOriginal(NULL) { } @@ -96,7 +96,7 @@ void MediaBuffer::release() { return; } - int prevCount = __sync_fetch_and_sub(&mRefCount, 1); + int prevCount = mRefCount.fetch_sub(1); if (prevCount == 1) { if (mObserver == NULL) { delete this; @@ -110,13 +110,13 @@ void MediaBuffer::release() { void MediaBuffer::claim() { CHECK(mObserver != NULL); - CHECK_EQ(mRefCount, 1); + CHECK_EQ(mRefCount.load(std::memory_order_relaxed), 1); - mRefCount = 0; + mRefCount.store(0, std::memory_order_relaxed); } void MediaBuffer::add_ref() { - (void) __sync_fetch_and_add(&mRefCount, 1); + (void) mRefCount.fetch_add(1); } void *MediaBuffer::data() const { diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h index f944d51de2..5a25965b86 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h @@ -86,12 +86,14 @@ public: virtual MediaBufferBase *clone(); // sum of localRefcount() and remoteRefcount() + // Result should be treated as approximate unless the result precludes concurrent accesses. virtual int refcount() const { return localRefcount() + remoteRefcount(); } + // Result should be treated as approximate unless the result precludes concurrent accesses. virtual int localRefcount() const { - return mRefCount; + return mRefCount.load(std::memory_order_relaxed); } virtual int remoteRefcount() const { @@ -146,7 +148,7 @@ private: void claim(); MediaBufferObserver *mObserver; - int mRefCount; + std::atomic mRefCount; void *mData; size_t mSize, mRangeOffset, mRangeLength; -- GitLab From 9dfa2641d0ce00113747c580f9731cf36c6b68cd Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 10 May 2018 10:09:19 -0700 Subject: [PATCH 0053/1530] PatchPanel: follow up to remove false warning from ~Patch When a new patch is being added to mPatches in this line: mPatches.insert(std::make_pair(*handle, std::move(newPatch))); the source of the move--`newPatch` remains intact, and if it is a software patch, it has playback and record patch handles remaining to be non-NONE values. Thus, on destruction it falsely warns that it hasn't been cleared. The solution is to define a move constructor for Endpoint which "transfers" patch handle ownership. Also delete copy constructors to make sure that each Patch is a unique resource. Test: create a software patch, watch logcat Change-Id: I41ea2dff087696252132e79d827e705378baaf8f --- services/audioflinger/PatchPanel.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 5a68960dfb..f4e43e2a51 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -46,6 +46,16 @@ private: template class Endpoint { public: + Endpoint() = default; + Endpoint(Endpoint&& other) { *this = std::move(other); } + Endpoint& operator=(Endpoint&& other) { + ALOGE_IF(mHandle != AUDIO_PATCH_HANDLE_NONE, + "A non empty Patch Endpoint leaked, handle %d", mHandle); + *this = other; + other.mHandle = AUDIO_PATCH_HANDLE_NONE; + return *this; + } + status_t checkTrack(TrackType *trackOrNull) const { if (trackOrNull == nullptr) return NO_MEMORY; return trackOrNull->initCheck(); @@ -82,6 +92,9 @@ private: void stopTrack() { if (mTrack) mTrack->stop(); } private: + Endpoint(const Endpoint&) = default; + Endpoint& operator=(const Endpoint&) = default; + sp mThread; bool mCloseThread = true; audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE; @@ -92,6 +105,10 @@ private: public: explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {} ~Patch(); + Patch(const Patch&) = delete; + Patch(Patch&&) = default; + Patch& operator=(const Patch&) = delete; + Patch& operator=(Patch&&) = default; status_t createConnections(PatchPanel *panel); void clearConnections(PatchPanel *panel); -- GitLab From 7ced919cddad4f8db132da56737a2527f9e0e242 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 10 May 2018 17:07:20 -0700 Subject: [PATCH 0054/1530] AudioPolicy: Improve audio patches dump Was: Audio Patches: Audio patch 1: - handle: 5 - audio flinger handle: 12 - owner uid: 1041 - 1 sources: - Mix ID 1 I/O handle 13 - 1 sinks: - Device ID 3 AUDIO_DEVICE_OUT_SPEAKER Now: Audio Patches: Patch 1: owner uid 1041, handle 5, af handle 12 [src 1] Mix ID 1 I/O handle 13 [sink 1] Device ID 3 AUDIO_DEVICE_OUT_SPEAKER Test: adb shell dumpsys media.audio_policy Change-Id: I164ab9b384e77929ef44dae019b7ec15239dceaa --- .../managerdefinitions/src/AudioPatch.cpp | 57 +++++++------------ 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp index a9fe48da30..e78e121728 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp @@ -34,51 +34,32 @@ AudioPatch::AudioPatch(const struct audio_patch *patch, uid_t uid) : { } -status_t AudioPatch::dump(int fd, int spaces, int index) const +static String8 dumpPatchEndpoints( + int spaces, const char *prefix, int count, const audio_port_config *cfgs) { - const size_t SIZE = 256; - char buffer[SIZE]; String8 result; - - snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sources; i++) { - if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { - std::string device; - deviceToString(mPatch.sources[i].ext.device.type, device); - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sources[i].id, - device.c_str()); - } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); - } - result.append(buffer); - } - snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sinks; i++) { - if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { + for (int i = 0; i < count; ++i) { + const audio_port_config &cfg = cfgs[i]; + result.appendFormat("%*s [%s %d] ", spaces, "", prefix, i + 1); + if (cfg.type == AUDIO_PORT_TYPE_DEVICE) { std::string device; - deviceToString(mPatch.sinks[i].ext.device.type, device); - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sinks[i].id, - device.c_str()); + deviceToString(cfg.ext.device.type, device); + result.appendFormat("Device ID %d %s", cfg.id, device.c_str()); } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); + result.appendFormat("Mix ID %d I/O handle %d", cfg.id, cfg.ext.mix.handle); } - result.append(buffer); + result.append("\n"); } + return result; +} +status_t AudioPatch::dump(int fd, int spaces, int index) const +{ + String8 result; + result.appendFormat("%*sPatch %d: owner uid %4d, handle %2d, af handle %2d\n", + spaces, "", index + 1, mUid, mHandle, mAfPatchHandle); + result.append(dumpPatchEndpoints(spaces, "src ", mPatch.num_sources, mPatch.sources)); + result.append(dumpPatchEndpoints(spaces, "sink", mPatch.num_sinks, mPatch.sinks)); write(fd, result.string(), result.size()); return NO_ERROR; } -- GitLab From c9948499fe578a4f7a10af98eedbb807880f3d44 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 10 May 2018 17:25:28 -0700 Subject: [PATCH 0055/1530] AudioFlinger: Add missing channel names for thread dump channelMaskToString in Threads.cpp was missing some of newly added discrete channels. Test: adb shell dumpsys media.audio_flinger Change-Id: I09c60b0cbbf3e10bcac6bc30e47f5009e50ccc93 --- services/audioflinger/Threads.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d037207bb1..d8363eef75 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -769,6 +769,8 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, "); if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " ); if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " ); + if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT) s.append("top-side-left, " ); + if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT) s.append("top-side-right, " ); if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, "); } else { if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, "); @@ -783,6 +785,12 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, "); if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, "); if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, "); + if (mask & AUDIO_CHANNEL_IN_BACK_LEFT) s.append("back-left, "); + if (mask & AUDIO_CHANNEL_IN_BACK_RIGHT) s.append("back-right, "); + if (mask & AUDIO_CHANNEL_IN_CENTER) s.append("center, "); + if (mask & AUDIO_CHANNEL_IN_LOW_FREQUENCY) s.append("low freq, "); + if (mask & AUDIO_CHANNEL_IN_TOP_LEFT) s.append("top-left, " ); + if (mask & AUDIO_CHANNEL_IN_TOP_RIGHT) s.append("top-right, " ); if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, "); if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, "); if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown, "); -- GitLab From d43239e1d8d05041816fcbb4a0f5117e5945960b Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 15 May 2018 14:37:22 -0700 Subject: [PATCH 0056/1530] Provide OMX/xaac Interface Provides the interfacing between the codec process through OMX into the new xHE-AAC implementation. Bug: 77287124 Bug: 72386934 Test: CTS DecoderTest Change-Id: Ic2ef0cb2ca54969673753938bab1f1456fe045c6 --- .../libstagefright/codecs/xaacdec/Android.bp | 38 + .../codecs/xaacdec/SoftXAAC.cpp | 1329 +++++++++++++++++ .../libstagefright/codecs/xaacdec/SoftXAAC.h | 127 ++ media/libstagefright/omx/SoftOMXPlugin.cpp | 5 + services/mediacodec/Android.mk | 1 + 5 files changed, 1500 insertions(+) create mode 100644 media/libstagefright/codecs/xaacdec/Android.bp create mode 100644 media/libstagefright/codecs/xaacdec/SoftXAAC.cpp create mode 100644 media/libstagefright/codecs/xaacdec/SoftXAAC.h diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp new file mode 100644 index 0000000000..465951bfec --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -0,0 +1,38 @@ +cc_library_shared { + name: "libstagefright_soft_xaacdec", + vendor_available: true, + vndk: { + enabled: true, + }, + + srcs: [ + "SoftXAAC.cpp", + ], + + include_dirs: [ + "frameworks/av/media/libstagefright/include", + "frameworks/native/include/media/openmax", + ], + + cflags: [ + "-Werror", + ], + + sanitize: { + // integer_overflow: true, + misc_undefined: [ "signed-integer-overflow", "unsigned-integer-overflow", ], + cfi: true, + }, + + static_libs: ["libxaacdec"], + + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "libcutils", + "liblog", + ], + + compile_multilib: "32", +} diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp new file mode 100644 index 0000000000..376755bae2 --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -0,0 +1,1329 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "SoftXAAC" +#include + +#include "SoftXAAC.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */ +#define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ + +#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" +#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" +#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" +#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" +#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" +#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */ + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static const OMX_U32 kSupportedProfiles[] = { + OMX_AUDIO_AACObjectLC, + OMX_AUDIO_AACObjectHE, + OMX_AUDIO_AACObjectHE_PS, + OMX_AUDIO_AACObjectLD, + OMX_AUDIO_AACObjectELD, +}; + +SoftXAAC::SoftXAAC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsADTS(false), + mInputBufferCount(0), + mOutputBufferCount(0), + mSignalledError(false), + mLastInHeader(NULL), + mPrevTimestamp(0), + mCurrentTimestamp(0), + mOutputPortSettingsChange(NONE), + mXheaacCodecHandle(NULL), + mInputBufferSize(0), + mOutputFrameLength(1024), + mInputBuffer(NULL), + mOutputBuffer(NULL), + mSampFreq(0), + mNumChannels(0), + mPcmWdSz(0), + mChannelMask(0), + mIsCodecInitialized(false), + mIsCodecConfigFlushRequired(false) +{ + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftXAAC::~SoftXAAC() { + int errCode = deInitXAACDecoder(); + if (0 != errCode) { + ALOGE("deInitXAACDecoder() failed %d",errCode); + } + + mIsCodecInitialized = false; + mIsCodecConfigFlushRequired = false; +} + +void SoftXAAC::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 4096 * MAX_CHANNEL_COUNT; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftXAAC::initDecoder() { + status_t status = UNKNOWN_ERROR; + + unsigned int ui_drc_val; + IA_ERRORCODE err_code = IA_NO_ERROR; + initXAACDecoder(); + if (NULL == mXheaacCodecHandle) { + ALOGE("AAC decoder is null. initXAACDecoder Failed"); + } else { + status = OK; + } + + mEndOfInput = false; + mEndOfOutput = false; + + char value[PROPERTY_VALUE_MAX]; + if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d",ui_drc_val, + DRC_DEFAULT_MOBILE_REF_LEVEL); + } + else + { + ui_drc_val= DRC_DEFAULT_MOBILE_REF_LEVEL; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, + &ui_drc_val); + + ALOGV("Error code returned after DRC Target level set_config is %d", err_code); + ALOGV("Setting IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL with value %d", ui_drc_val); + + if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_CUT); + } + else + { + ui_drc_val=DRC_DEFAULT_MOBILE_DRC_CUT; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, + &ui_drc_val); + ALOGV("Error code returned after DRC cut factor set_config is %d", err_code); + ALOGV("Setting IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT with value %d", ui_drc_val); + + if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_BOOST); + } + else + { + ui_drc_val = DRC_DEFAULT_MOBILE_DRC_BOOST; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, + &ui_drc_val); + ALOGV("Error code returned after DRC boost factor set_config is %d", err_code); + ALOGV("Setting DRC_DEFAULT_MOBILE_DRC_BOOST with value %d", ui_drc_val); + + if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_HEAVY); + } + else + { + ui_drc_val = DRC_DEFAULT_MOBILE_DRC_HEAVY; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, + &ui_drc_val); + ALOGV("Error code returned after DRC heavy set_config is %d", err_code); + ALOGV("Setting DRC_DEFAULT_MOBILE_DRC_HEAVY with value %d", ui_drc_val); + + return status; +} + +OMX_ERRORTYPE SoftXAAC::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + + switch ((OMX_U32) index) { + + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingAAC : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = 0; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; + + aacParams->eAACStreamFormat = + mIsADTS + ? OMX_AUDIO_AACStreamFormatMP4ADTS + : OMX_AUDIO_AACStreamFormatMP4FF; + + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + if (!isConfigured()) { + aacParams->nChannels = 1; + aacParams->nSampleRate = 44100; + aacParams->nFrameLength = 0; + } else { + aacParams->nChannels = mNumChannels; + aacParams->nSampleRate = mSampFreq; + aacParams->nFrameLength = mOutputFrameLength; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS; + pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSampFreq; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioProfileQuerySupported: + { + OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *profileParams = + (OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *)params; + + if (!isValidOMXParam(profileParams)) { + return OMX_ErrorBadParameter; + } + + if (profileParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (profileParams->nProfileIndex >= NELEM(kSupportedProfiles)) { + return OMX_ErrorNoMore; + } + + profileParams->eProfile = + kSupportedProfiles[profileParams->nProfileIndex]; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftXAAC::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + + switch ((int)index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingAAC) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) { + mIsADTS = false; + } else if (aacParams->eAACStreamFormat + == OMX_AUDIO_AACStreamFormatMP4ADTS) { + mIsADTS = true; + } else { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidAacPresentation: + { + const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams = + (const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params; + + if (!isValidOMXParam(aacPresParams)) { + ALOGE("set OMX_ErrorBadParameter"); + return OMX_ErrorBadParameter; + } + + // for the following parameters of the OMX_AUDIO_PARAM_AACPROFILETYPE structure, + // a value of -1 implies the parameter is not set by the application: + // nMaxOutputChannels -1 by default + // nDrcCut uses default platform properties, see initDecoder() + // nDrcBoost idem + // nHeavyCompression idem + // nTargetReferenceLevel idem + // nEncodedTargetLevel idem + if (aacPresParams->nMaxOutputChannels >= 0) { + int max; + if (aacPresParams->nMaxOutputChannels >= 8) { max = 8; } + else if (aacPresParams->nMaxOutputChannels >= 6) { max = 6; } + else if (aacPresParams->nMaxOutputChannels >= 2) { max = 2; } + else { + // -1 or 0: disable downmix, 1: mono + max = aacPresParams->nMaxOutputChannels; + } + } + /* Apply DRC Changes */ + setXAACDRCInfo(aacPresParams->nDrcCut, + aacPresParams->nDrcBoost, + aacPresParams->nTargetReferenceLevel, + aacPresParams->nHeavyCompression); + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftXAAC::isConfigured() const { + return mInputBufferCount > 0; +} + +void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + ALOGE("onQueueFilled do not process %d %d",mSignalledError,mOutputPortSettingsChange); + return; + } + + uint8_t* inBuffer = NULL; + uint32_t inBufferLength = 0; + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + signed int numOutBytes = 0; + + /* If decoder call fails in between, then mOutputFrameLength is used */ + /* Decoded output for AAC is 1024/2048 samples / channel */ + /* TODO: For USAC mOutputFrameLength can go up to 4096 */ + /* Note: entire buffer logic to save and retrieve assumes 2 bytes per*/ + /* sample currently */ + if (mIsCodecInitialized) { + numOutBytes = mOutputFrameLength * (mPcmWdSz/8) * mNumChannels; + if ((mPcmWdSz/8) != 2) { + ALOGE("XAAC assumes 2 bytes per sample! mPcmWdSz %d",mPcmWdSz); + } + } + + while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) { + if (!inQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0; + + if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { + ALOGE("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set"); + inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + } + if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + inBuffer = inHeader->pBuffer + inHeader->nOffset; + inBufferLength = inHeader->nFilledLen; + + /* GA header configuration sent to Decoder! */ + int err_code = configXAACDecoder(inBuffer,inBufferLength); + if (0 != err_code) { + ALOGW("configXAACDecoder err_code = %d", err_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); + return; + } + mInputBufferCount++; + mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + // Only send out port settings changed event if both sample rate + // and mNumChannels are valid. + if (mSampFreq && mNumChannels && !mIsCodecConfigFlushRequired) { + ALOGV("Configuring decoder: %d Hz, %d channels", mSampFreq, mNumChannels); + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + + return; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + continue; + } + + // Restore Offset and Length for Port reconfig case + size_t tempOffset = inHeader->nOffset; + size_t tempFilledLen = inHeader->nFilledLen; + if (mIsADTS) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %d bytes.", inHeader->nFilledLen); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %d bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + if (aac_frame_length < adtsHeaderSize) { + signalError = true; + } else { + inBuffer = (uint8_t *)adtsHeader + adtsHeaderSize; + inBufferLength = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } + } + } + + if (signalError) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL); + return; + } + + // insert buffer size and time stamp + if (mLastInHeader != inHeader) { + mCurrentTimestamp = inHeader->nTimeStamp; + mLastInHeader = inHeader; + } else { + mCurrentTimestamp = mPrevTimestamp + + mOutputFrameLength * 1000000ll / mSampFreq; + } + } else { + inBuffer = inHeader->pBuffer + inHeader->nOffset; + inBufferLength = inHeader->nFilledLen; + mLastInHeader = inHeader; + mCurrentTimestamp = inHeader->nTimeStamp; + } + + int numLoops = 0; + signed int prevSampleRate = mSampFreq; + signed int prevNumChannels = mNumChannels; + + /* XAAC decoder expects first frame to be fed via configXAACDecoder API */ + /* which should initialize the codec. Once this state is reached, call the */ + /* decodeXAACStream API with same frame to decode! */ + if (!mIsCodecInitialized) { + int err_code = configXAACDecoder(inBuffer,inBufferLength); + if (0 != err_code) { + ALOGW("configXAACDecoder Failed 2 err_code = %d", err_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); + return; + } + mIsCodecConfigFlushRequired = true; + } + + if (!mSampFreq || !mNumChannels) { + if ((mInputBufferCount > 2) && (mOutputBufferCount <= 1)) { + ALOGW("Invalid AAC stream"); + ALOGW("mSampFreq %d mNumChannels %d ",mSampFreq,mNumChannels); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + } else if ((mSampFreq != prevSampleRate) || + (mNumChannels != prevNumChannels)) { + ALOGV("Reconfiguring decoder: %d->%d Hz, %d->%d channels", + prevSampleRate, mSampFreq, prevNumChannels, mNumChannels); + inHeader->nOffset = tempOffset; + inHeader->nFilledLen = tempFilledLen; + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + signed int bytesConsumed = 0; + int errorCode = 0; + if (mIsCodecInitialized) { + errorCode = decodeXAACStream(inBuffer,inBufferLength, &bytesConsumed, &numOutBytes); + } else { + ALOGW("Assumption that first frame after header initializes decoder failed!"); + } + inHeader->nFilledLen -= bytesConsumed; + inHeader->nOffset += bytesConsumed; + + if (inHeader->nFilledLen != 0) { + ALOGE("All data not consumed"); + } + + /* In case of error, decoder would have given out empty buffer */ + if ((0 != errorCode) && (0 == numOutBytes) && mIsCodecInitialized) { + numOutBytes = mOutputFrameLength * (mPcmWdSz/8) * mNumChannels; + } + numLoops++; + + if (0 == bytesConsumed) { + ALOGE("bytesConsumed = 0 should never happen"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + if (errorCode) { + /* Clear buffer for output buffer is done inside XAAC codec */ + /* TODO - Check if below memset is on top of reset inside codec */ + memset(mOutputBuffer, 0, numOutBytes); // TODO: check for overflow, ASAN + + // Discard input buffer. + if (inHeader) { + inHeader->nFilledLen = 0; + } + + // fall through + } + + if (inHeader && inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + ALOGV("inHeader->nFilledLen = %d", inHeader ? inHeader->nFilledLen : 0); + } + + if (!outQueue.empty() && numOutBytes) { + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (outHeader->nOffset != 0) { + ALOGE("outHeader->nOffset != 0 is not handled"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + signed short *outBuffer = + reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + int samplesize = mNumChannels * sizeof(int16_t); + if (outHeader->nOffset + + mOutputFrameLength * samplesize + > outHeader->nAllocLen) { + ALOGE("buffer overflow"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + memcpy(outBuffer, mOutputBuffer, numOutBytes); + outHeader->nFilledLen = numOutBytes; + + if (mEndOfInput && !outQueue.empty()) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mEndOfOutput = true; + } else { + outHeader->nFlags = 0; + } + outHeader->nTimeStamp = mCurrentTimestamp; + mPrevTimestamp = mCurrentTimestamp; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen); + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + } + + if (mEndOfInput) { + if (!outQueue.empty()) { + if (!mEndOfOutput) { + ALOGV(" empty block signaling EOS"); + // send partial or empty block signaling EOS + mEndOfOutput = true; + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outHeader->nTimeStamp = mPrevTimestamp ; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + break; // if outQueue not empty but no more output + } + } + } +} + +void SoftXAAC::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + // drain all existing data + if (mIsCodecInitialized) { + configflushDecode(); + } + drainDecoder(); + mLastInHeader = NULL; + mEndOfInput = false; + } else { + mEndOfOutput = false; + } +} + +void SoftXAAC::configflushDecode() { + IA_ERRORCODE err_code; + UWORD32 ui_init_done; + uint32_t inBufferLength=8203; + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_FLUSH_MEM, + NULL); + ALOGV("Codec initialized:%d",mIsCodecInitialized); + ALOGV("Error code from first flush %d",err_code); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_INPUT_BYTES, + 0, + &inBufferLength); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_FLUSH_MEM, + NULL); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_DONE_QUERY, + &ui_init_done); + + ALOGV("Flush called"); + + if (ui_init_done) { + err_code = getXAACStreamInfo(); + ALOGV("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", + mSampFreq,mNumChannels,mPcmWdSz,mChannelMask,mOutputFrameLength); + mIsCodecInitialized = true; + } + +} +int SoftXAAC::drainDecoder() { + return 0; +} + +void SoftXAAC::onReset() { + drainDecoder(); + + // reset the "configured" state + mInputBufferCount = 0; + mOutputBufferCount = 0; + mEndOfInput = false; + mEndOfOutput = false; + mLastInHeader = NULL; + + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + +void SoftXAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +int SoftXAAC::initXAACDecoder() { + LOOPIDX i; + + /* Error code */ + IA_ERRORCODE err_code = IA_NO_ERROR; + + /* First part */ + /* Error Handler Init */ + /* Get Library Name, Library Version and API Version */ + /* Initialize API structure + Default config set */ + /* Set config params from user */ + /* Initialize memory tables */ + /* Get memory information and allocate memory */ + + /* Memory variables */ + UWORD32 n_mems, ui_rem; + UWORD32 ui_proc_mem_tabs_size; + /* API size */ + UWORD32 pui_ap_isize; + + mInputBufferSize = 0; + mInputBuffer = 0; + mOutputBuffer = 0; + mMallocCount = 0; + + /* Process struct initing end */ + /* ******************************************************************/ + /* Initialize API structure and set config params to default */ + /* ******************************************************************/ + + /* Get the API size */ + err_code = ixheaacd_dec_api(NULL, + IA_API_CMD_GET_API_SIZE, + 0, + &pui_ap_isize); + ALOGV("return code of IA_API_CMD_GET_API_SIZE: %d",err_code); + /* Allocate memory for API */ + mMemoryArray[mMallocCount] = memalign(4, pui_ap_isize); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE("malloc for pui_ap_isize + 4 >> %d Failed",pui_ap_isize + 4); + } + /* Set API object with the memory allocated */ + mXheaacCodecHandle = + (pVOID)((WORD8*)mMemoryArray[mMallocCount]); + mMallocCount++; + + /* Set the config params to default values */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, + NULL); + ALOGV("return code of IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS: %d",err_code); + + /* ******************************************************************/ + /* Set config parameters */ + /* ******************************************************************/ + UWORD32 ui_mp4_flag = 1; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4, + &ui_mp4_flag); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4: %d",err_code); + + /* ******************************************************************/ + /* Initialize Memory info tables */ + /* ******************************************************************/ + + /* Get memory info tables size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_MEMTABS_SIZE, + 0, + &ui_proc_mem_tabs_size); + ALOGV("return code of IA_API_CMD_GET_MEMTABS_SIZE: %d",err_code); + mMemoryArray[mMallocCount] = memalign(4, ui_proc_mem_tabs_size); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!",ui_proc_mem_tabs_size + 4); + } + + /* Set pointer for process memory tables */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_MEMTABS_PTR, + 0, + (pVOID)((WORD8*)mMemoryArray[mMallocCount])); + ALOGV("return code of IA_API_CMD_SET_MEMTABS_PTR: %d",err_code); + mMallocCount++; + + /* initialize the API, post config, fill memory tables */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, + NULL); + ALOGV("return code of IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS: %d",err_code); + + /* ******************************************************************/ + /* Allocate Memory with info from library */ + /* ******************************************************************/ + + /* Get number of memory tables required */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_N_MEMTABS, + 0, + &n_mems); + ALOGV("return code of IA_API_CMD_GET_N_MEMTABS: %d",err_code); + + for(i = 0; i < (WORD32)n_mems; i++) { + int ui_size = 0, ui_alignment = 0, ui_type = 0; + pVOID pv_alloc_ptr; + + /* Get memory size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_MEM_INFO_SIZE, + i, + &ui_size); + ALOGV("return code of IA_API_CMD_GET_MEM_INFO_SIZE: %d",err_code); + + /* Get memory alignment */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_MEM_INFO_ALIGNMENT, + i, + &ui_alignment); + ALOGV("return code of IA_API_CMD_GET_MEM_INFO_ALIGNMENT: %d",err_code); + + /* Get memory type */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_MEM_INFO_TYPE, + i, + &ui_type); + ALOGV("return code of IA_API_CMD_GET_MEM_INFO_TYPE: %d",err_code); + + mMemoryArray[mMallocCount] = + memalign(ui_alignment , ui_size); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!",ui_size + ui_alignment); + } + pv_alloc_ptr = + (pVOID )((WORD8*)mMemoryArray[mMallocCount]); + mMallocCount++; + + /* Set the buffer pointer */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_MEM_PTR, + i, + pv_alloc_ptr); + ALOGV("return code of IA_API_CMD_SET_MEM_PTR: %d",err_code); + if (ui_type == IA_MEMTYPE_INPUT) { + mInputBuffer = (pWORD8)pv_alloc_ptr; + mInputBufferSize = ui_size; + + } + + if (ui_type == IA_MEMTYPE_OUTPUT) { + mOutputBuffer = (pWORD8)pv_alloc_ptr; + } + + } + /* End first part */ + + return IA_NO_ERROR; +} + +int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { + + UWORD32 ui_init_done; + int32_t i_bytes_consumed; + + if (mInputBufferSize < inBufferLength) { + ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d",mInputBufferSize,inBufferLength); + return false; + } + + /* Copy the buffer passed by Android plugin to codec input buffer */ + memcpy(mInputBuffer, inBuffer, inBufferLength); + + /* Set number of bytes to be processed */ + IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_INPUT_BYTES, + 0, + &inBufferLength); + ALOGV("return code of IA_API_CMD_SET_INPUT_BYTES: %d",err_code); + + if (mIsCodecConfigFlushRequired) { + /* If codec is already initialized, then GA header is passed again */ + /* Need to call the Flush API instead of INIT_PROCESS */ + mIsCodecInitialized = false; /* Codec needs to be Reinitialized after flush */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_GA_HDR, + NULL); + ALOGV("return code of IA_CMD_TYPE_GA_HDR: %d",err_code); + } + else { + /* Initialize the process */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_PROCESS, + NULL); + ALOGV("return code of IA_CMD_TYPE_INIT_PROCESS: %d",err_code); + } + + /* Checking for end of initialization */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_DONE_QUERY, + &ui_init_done); + ALOGV("return code of IA_CMD_TYPE_INIT_DONE_QUERY: %d",err_code); + + /* How much buffer is used in input buffers */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CURIDX_INPUT_BUF, + 0, + &i_bytes_consumed); + ALOGV("return code of IA_API_CMD_GET_CURIDX_INPUT_BUF: %d",err_code); + + if(ui_init_done){ + err_code = getXAACStreamInfo(); + ALOGI("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", + mSampFreq,mNumChannels,mPcmWdSz,mChannelMask,mOutputFrameLength); + mIsCodecInitialized = true; + } + + return err_code; +} + +int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, + uint32_t inBufferLength, + int32_t *bytesConsumed, + int32_t *outBytes) { + if (mInputBufferSize < inBufferLength) { + ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d",mInputBufferSize,inBufferLength); + return -1; + } + + /* Copy the buffer passed by Android plugin to codec input buffer */ + memcpy(mInputBuffer,inBuffer,inBufferLength); + + /* Set number of bytes to be processed */ + IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_INPUT_BYTES, + 0, + &inBufferLength); + ALOGV("return code of IA_API_CMD_SET_INPUT_BYTES: %d",err_code); + + /* Execute process */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_EXECUTE, + IA_CMD_TYPE_DO_EXECUTE, + NULL); + ALOGV("return code of IA_CMD_TYPE_DO_EXECUTE: %d",err_code); + + UWORD32 ui_exec_done; + /* Checking for end of processing */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_EXECUTE, + IA_CMD_TYPE_DONE_QUERY, + &ui_exec_done); + ALOGV("return code of IA_CMD_TYPE_DONE_QUERY: %d",err_code); + + /* How much buffer is used in input buffers */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CURIDX_INPUT_BUF, + 0, + bytesConsumed); + ALOGV("return code of IA_API_CMD_GET_CURIDX_INPUT_BUF: %d",err_code); + + /* Get the output bytes */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_OUTPUT_BYTES, + 0, + outBytes); + ALOGV("return code of IA_API_CMD_GET_OUTPUT_BYTES: %d",err_code); + + return err_code; +} + +int SoftXAAC::deInitXAACDecoder() { + ALOGI("deInitXAACDecoder"); + + /* Tell that the input is over in this buffer */ + IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_INPUT_OVER, + 0, + NULL); + ALOGV("return code of IA_API_CMD_INPUT_OVER: %d",err_code); + + for(int i = 0; i < mMallocCount; i++) + { + if(mMemoryArray[i]) + free(mMemoryArray[i]); + } + mMallocCount = 0; + + return err_code; +} + +IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { + IA_ERRORCODE err_code = IA_NO_ERROR; + + /* Sampling frequency */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ, + &mSampFreq); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ: %d",err_code); + + /* Total Number of Channels */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS, + &mNumChannels); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS: %d",err_code); + + /* PCM word size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ, + &mPcmWdSz); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ: %d",err_code); + + /* channel mask to tell the arrangement of channels in bit stream */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK, + &mChannelMask); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK: %d",err_code); + + /* Channel mode to tell MONO/STEREO/DUAL-MONO/NONE_OF_THESE */ + UWORD32 ui_channel_mode; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE, + &ui_channel_mode); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE: %d",err_code); + if(ui_channel_mode == 0) + ALOGV("Channel Mode: MONO_OR_PS\n"); + else if(ui_channel_mode == 1) + ALOGV("Channel Mode: STEREO\n"); + else if(ui_channel_mode == 2) + ALOGV("Channel Mode: DUAL-MONO\n"); + else + ALOGV("Channel Mode: NONE_OF_THESE or MULTICHANNEL\n"); + + /* Channel mode to tell SBR PRESENT/NOT_PRESENT */ + UWORD32 ui_sbr_mode; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, + &ui_sbr_mode); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE: %d",err_code); + if(ui_sbr_mode == 0) + ALOGV("SBR Mode: NOT_PRESENT\n"); + else if(ui_sbr_mode == 1) + ALOGV("SBR Mode: PRESENT\n"); + else + ALOGV("SBR Mode: ILLEGAL\n"); + + /* mOutputFrameLength = 1024 * (1 + SBR_MODE) for AAC */ + /* For USAC it could be 1024 * 3 , support to query */ + /* not yet added in codec */ + mOutputFrameLength = 1024 * (1 + ui_sbr_mode); + + ALOGI("mOutputFrameLength %d ui_sbr_mode %d",mOutputFrameLength,ui_sbr_mode); + + return IA_NO_ERROR; +} + +IA_ERRORCODE SoftXAAC::setXAACDRCInfo(int32_t drcCut, + int32_t drcBoost, + int32_t drcRefLevel, + int32_t drcHeavyCompression) { + IA_ERRORCODE err_code = IA_NO_ERROR; + + int32_t ui_drc_enable = 1; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE, + &ui_drc_enable); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE: %d",err_code); + if (drcCut !=-1) { + ALOGI("set drcCut=%d", drcCut); + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, + &drcCut); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT: %d",err_code); + } + + if (drcBoost !=-1) { + ALOGI("set drcBoost=%d", drcBoost); + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, + &drcBoost); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST: %d",err_code); + } + + if (drcRefLevel != -1) { + ALOGI("set drcRefLevel=%d", drcRefLevel); + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, + &drcRefLevel); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL: %d",err_code); + } + + if (drcHeavyCompression != -1) { + ALOGI("set drcHeavyCompression=%d", drcHeavyCompression); + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, + &drcHeavyCompression); + ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP: %d",err_code); + } + + return IA_NO_ERROR; +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + ALOGI("createSoftOMXComponent for SoftXAACDEC"); + return new android::SoftXAAC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.h b/media/libstagefright/codecs/xaacdec/SoftXAAC.h new file mode 100644 index 0000000000..f4b4c542df --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.h @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#ifndef SOFTXAAC_H_ +#define SOFTXAAC_H_ + +#include + +#include +#include +#include + +#include "ixheaacd_type_def.h" +#include "ixheaacd_error_standards.h" +#include "ixheaacd_error_handler.h" +#include "ixheaacd_apicmd_standards.h" +#include "ixheaacd_memory_standards.h" +#include "ixheaacd_aac_config.h" +//#include "ixheaacd_aac_dec_error.h" + +#define MAX_MEM_ALLOCS 100 + +extern "C" IA_ERRORCODE ixheaacd_dec_api(pVOID p_ia_module_obj, + WORD32 i_cmd, WORD32 i_idx, pVOID pv_value); + +namespace android { + +struct SoftXAAC : public SimpleSoftOMXComponent { + SoftXAAC(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftXAAC(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 4, + kNumDelayBlocksMax = 8, + }; + + bool mIsADTS; + size_t mInputBufferCount; + size_t mOutputBufferCount; + bool mSignalledError; + OMX_BUFFERHEADERTYPE *mLastInHeader; + int64_t mPrevTimestamp; + int64_t mCurrentTimestamp; + uint32_t mBufSize; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + int drainDecoder(); + int initXAACDecoder(); + int deInitXAACDecoder(); + + int configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength); + int decodeXAACStream(uint8_t* inBuffer, + uint32_t inBufferLength, + int32_t *bytesConsumed, + int32_t *outBytes); + + void configflushDecode(); + IA_ERRORCODE getXAACStreamInfo(); + IA_ERRORCODE setXAACDRCInfo(int32_t drcCut, + int32_t drcBoost, + int32_t drcRefLevel, + int32_t drcHeavyCompression); + + bool mEndOfInput; + bool mEndOfOutput; + + void* mXheaacCodecHandle; + uint32_t mInputBufferSize; + uint32_t mOutputFrameLength; + int8_t* mInputBuffer; + int8_t* mOutputBuffer; + int32_t mSampFreq; + int32_t mNumChannels; + int32_t mPcmWdSz; + int32_t mChannelMask; + bool mIsCodecInitialized; + bool mIsCodecConfigFlushRequired; + + void* mMemoryArray[MAX_MEM_ALLOCS]; + int32_t mMallocCount; + + DISALLOW_EVIL_CONSTRUCTORS(SoftXAAC); + +}; + +} // namespace android + +#endif // SOFTXAAC_H_ diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 4946ada543..1f3e8c17b4 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -34,7 +34,12 @@ static const struct { const char *mRole; } kComponents[] = { + // two choices for aac decoding. + // configurable in media/libstagefright/data/media_codecs_google_audio.xml + // default implementation { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" }, + // alternate implementation + { "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" }, { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" }, { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" }, { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" }, diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index db5f0fff88..089d62b495 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -22,6 +22,7 @@ _software_codecs := \ libstagefright_soft_vorbisdec \ libstagefright_soft_vpxdec \ libstagefright_soft_vpxenc \ + libstagefright_soft_xaacdec \ # service executable include $(CLEAR_VARS) -- GitLab From dc769682023ba8a8a5da07c3e3a3adfabe4bd612 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 4 May 2018 15:34:08 -0700 Subject: [PATCH 0057/1530] audio_server: Unify audio_patch creation Provide PatchBuilder helper class instead of fiddling with struct audio_patch directly. Rename 'getAudioPortConfig' methods of AudioFlinger helper classes into 'toAudioPortConfig' to match classes of AudioPolicyManager. Factor out common code in AudioPolicyManager that was adding audio patches. For that, AudioOutputDescriptor now inherits from AudioSessionInfoProvider, and the latter has been extended with 'setPatchHandle' method. Test: switch to/from Bluetooth on phone calls and media playback, use camcorder Change-Id: Idd99645dc6943ed078c4d94d0197fead7831ab4d --- media/libmedia/include/media/PatchBuilder.h | 103 +++++++++ services/audioflinger/PatchPanel.cpp | 20 +- services/audioflinger/Threads.cpp | 14 +- services/audioflinger/Threads.h | 8 +- ...rovider.h => AudioIODescriptorInterface.h} | 15 +- .../include/AudioInputDescriptor.h | 13 +- .../include/AudioOutputDescriptor.h | 9 +- .../managerdefinitions/include/AudioSession.h | 20 +- .../src/AudioInputDescriptor.cpp | 4 +- .../src/AudioOutputDescriptor.cpp | 17 ++ .../managerdefinitions/src/AudioSession.cpp | 14 +- .../managerdefault/AudioPolicyManager.cpp | 218 +++++++----------- .../managerdefault/AudioPolicyManager.h | 16 +- .../tests/audiopolicymanager_tests.cpp | 27 +-- 14 files changed, 286 insertions(+), 212 deletions(-) create mode 100644 media/libmedia/include/media/PatchBuilder.h rename services/audiopolicy/common/managerdefinitions/include/{AudioSessionInfoProvider.h => AudioIODescriptorInterface.h} (67%) diff --git a/media/libmedia/include/media/PatchBuilder.h b/media/libmedia/include/media/PatchBuilder.h new file mode 100644 index 0000000000..f2722a6d41 --- /dev/null +++ b/media/libmedia/include/media/PatchBuilder.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#ifndef ANDROID_PATCH_BUILDER_H +#define ANDROID_PATCH_BUILDER_H + +#include +#include + +#include +#include + +// This is a header-only utility. + +namespace android { + +class PatchBuilder { + public: + using mix_usecase_t = decltype(audio_port_config_mix_ext::usecase); + + PatchBuilder() = default; + + // All existing methods operating on audio patches take a pointer to const. + // It's OK to construct a temporary PatchBuilder while preparing a parameter + // to such a function because the Builder will be kept alive until the code + // execution reaches the function call statement semicolon. + const struct audio_patch* patch() const { return &mPatch; } + + template + PatchBuilder& addSink(T&& t, S&&... s) { + sinks().add(std::forward(t), std::forward(s)...); + return *this; + } + // Explicit type of the second parameter allows clients to provide the struct inline. + template + PatchBuilder& addSink(T&& t, const mix_usecase_t& update) { + sinks().add(std::forward(t), update); + return *this; + } + template + PatchBuilder& addSource(T&& t, S&&... s) { + sources().add(std::forward(t), std::forward(s)...); + return *this; + } + // Explicit type of the second parameter allows clients to provide the struct inline. + template + PatchBuilder& addSource(T&& t, const mix_usecase_t& update) { + sources().add(std::forward(t), update); + return *this; + } + + private: + struct PortCfgs { + PortCfgs(unsigned int *countPtr, struct audio_port_config *portCfgs) + : mCountPtr(countPtr), mPortCfgs(portCfgs) {} + audio_port_config& add(const audio_port_config& portCfg) { + return *advance() = portCfg; + } + template + audio_port_config& add(const sp& entity) { + audio_port_config* added = advance(); + entity->toAudioPortConfig(added); + return *added; + } + template + void add(const sp& entity, const mix_usecase_t& usecaseUpdate) { + add(entity).ext.mix.usecase = usecaseUpdate; + } + template + void add(const sp& entity, + std::function usecaseUpdater) { + mix_usecase_t* usecase = &add(entity).ext.mix.usecase; + *usecase = usecaseUpdater(*usecase); + } + struct audio_port_config* advance() { + return &mPortCfgs[(*mCountPtr)++]; + } + unsigned int *mCountPtr; + struct audio_port_config *mPortCfgs; + }; + + PortCfgs sinks() { return PortCfgs(&mPatch.num_sinks, mPatch.sinks); } + PortCfgs sources() { return PortCfgs(&mPatch.num_sources, mPatch.sources); } + + struct audio_patch mPatch = {}; +}; + +} // namespace android + +#endif // ANDROID_PATCH_BUILDER_H diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index eaf1120c68..be3286f334 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -26,6 +26,7 @@ #include "AudioFlinger.h" #include "ServiceUtilities.h" #include +#include // ---------------------------------------------------------------------------- @@ -373,15 +374,10 @@ AudioFlinger::PatchPanel::Patch::~Patch() status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) { // create patch from source device to record thread input - struct audio_patch subPatch; - subPatch.num_sources = 1; - subPatch.sources[0] = mAudioPatch.sources[0]; - subPatch.num_sinks = 1; - - mRecord.thread()->getAudioPortConfig(&subPatch.sinks[0]); - subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; - - status_t status = panel->createAudioPatch(&subPatch, mRecord.handlePtr()); + status_t status = panel->createAudioPatch( + PatchBuilder().addSource(mAudioPatch.sources[0]). + addSink(mRecord.thread(), { .source = AUDIO_SOURCE_MIC }).patch(), + mRecord.handlePtr()); if (status != NO_ERROR) { *mRecord.handlePtr() = AUDIO_PATCH_HANDLE_NONE; return status; @@ -389,9 +385,9 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) // create patch from playback thread output to sink device if (mAudioPatch.num_sinks != 0) { - mPlayback.thread()->getAudioPortConfig(&subPatch.sources[0]); - subPatch.sinks[0] = mAudioPatch.sinks[0]; - status = panel->createAudioPatch(&subPatch, mPlayback.handlePtr()); + status = panel->createAudioPatch( + PatchBuilder().addSource(mPlayback.thread()).addSink(mAudioPatch.sinks[0]).patch(), + mPlayback.handlePtr()); if (status != NO_ERROR) { *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE; return status; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 5db3e44174..44230336b5 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1527,7 +1527,7 @@ void AudioFlinger::ThreadBase::setMode(audio_mode_t mode) } } -void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config) +void AudioFlinger::ThreadBase::toAudioPortConfig(struct audio_port_config *config) { config->type = AUDIO_PORT_TYPE_MIX; config->ext.mix.handle = mId; @@ -3780,9 +3780,9 @@ void AudioFlinger::PlaybackThread::deletePatchTrack(const sp& track) destroyTrack_l(track); } -void AudioFlinger::PlaybackThread::getAudioPortConfig(struct audio_port_config *config) +void AudioFlinger::PlaybackThread::toAudioPortConfig(struct audio_port_config *config) { - ThreadBase::getAudioPortConfig(config); + ThreadBase::toAudioPortConfig(config); config->role = AUDIO_PORT_ROLE_SOURCE; config->ext.mix.hw_module = mOutput->audioHwDev->handle(); config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; @@ -7875,9 +7875,9 @@ void AudioFlinger::RecordThread::deletePatchTrack(const sp& record) destroyTrack_l(record); } -void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *config) +void AudioFlinger::RecordThread::toAudioPortConfig(struct audio_port_config *config) { - ThreadBase::getAudioPortConfig(config); + ThreadBase::toAudioPortConfig(config); config->role = AUDIO_PORT_ROLE_SINK; config->ext.mix.hw_module = mInput->audioHwDev->handle(); config->ext.mix.usecase.source = mAudioSource; @@ -8462,9 +8462,9 @@ status_t AudioFlinger::MmapThread::releaseAudioPatch_l(const audio_patch_handle_ return status; } -void AudioFlinger::MmapThread::getAudioPortConfig(struct audio_port_config *config) +void AudioFlinger::MmapThread::toAudioPortConfig(struct audio_port_config *config) { - ThreadBase::getAudioPortConfig(config); + ThreadBase::toAudioPortConfig(config); if (isOutput()) { config->role = AUDIO_PORT_ROLE_SOURCE; config->ext.mix.hw_module = mAudioHwDev->handle(); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index c490fb58c7..5e5e9487fc 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -281,7 +281,7 @@ public: virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle) = 0; virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) = 0; - virtual void getAudioPortConfig(struct audio_port_config *config) = 0; + virtual void toAudioPortConfig(struct audio_port_config *config) = 0; // see note at declaration of mStandby, mOutDevice and mInDevice @@ -782,7 +782,7 @@ public: void addPatchTrack(const sp& track); void deletePatchTrack(const sp& track); - virtual void getAudioPortConfig(struct audio_port_config *config); + virtual void toAudioPortConfig(struct audio_port_config *config); // Return the asynchronous signal wait time. virtual int64_t computeWaitTimeNs_l() const { return INT64_MAX; } @@ -1459,7 +1459,7 @@ public: virtual size_t frameCount() const { return mFrameCount; } bool hasFastCapture() const { return mFastCapture != 0; } - virtual void getAudioPortConfig(struct audio_port_config *config); + virtual void toAudioPortConfig(struct audio_port_config *config); virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc, audio_session_t sessionId); @@ -1602,7 +1602,7 @@ class MmapThread : public ThreadBase virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); - virtual void getAudioPortConfig(struct audio_port_config *config); + virtual void toAudioPortConfig(struct audio_port_config *config); virtual sp stream() const { return mHalStream; } virtual status_t addEffectChain_l(const sp& chain); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h similarity index 67% rename from services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h rename to services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h index e0037fc18b..9f3fc0c801 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h @@ -19,26 +19,27 @@ namespace android { /** - * Interface for input descriptors to implement so dependent audio sessions can query information - * about their context + * Interface for I/O descriptors to implement so information about their context + * can be queried and updated. */ -class AudioSessionInfoProvider +class AudioIODescriptorInterface { public: - virtual ~AudioSessionInfoProvider() {}; + virtual ~AudioIODescriptorInterface() {}; virtual audio_config_base_t getConfig() const = 0; virtual audio_patch_handle_t getPatchHandle() const = 0; + virtual void setPatchHandle(audio_patch_handle_t handle) = 0; }; -class AudioSessionInfoUpdateListener +class AudioIODescriptorUpdateListener { public: - virtual ~AudioSessionInfoUpdateListener() {}; + virtual ~AudioIODescriptorUpdateListener() {}; - virtual void onSessionInfoUpdate() const = 0;; + virtual void onIODescriptorUpdate() const = 0; }; } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index b25d6d424c..85f3b869c3 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -16,9 +16,9 @@ #pragma once +#include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "AudioSession.h" -#include "AudioSessionInfoProvider.h" #include #include #include @@ -31,7 +31,7 @@ class AudioMix; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. -class AudioInputDescriptor: public AudioPortConfig, public AudioSessionInfoProvider +class AudioInputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface { public: explicit AudioInputDescriptor(const sp& profile, @@ -67,11 +67,10 @@ public: size_t getAudioSessionCount(bool activeOnly) const; audio_source_t getHighestPrioritySource(bool activeOnly) const; - // implementation of AudioSessionInfoProvider - virtual audio_config_base_t getConfig() const; - virtual audio_patch_handle_t getPatchHandle() const; - - void setPatchHandle(audio_patch_handle_t handle); + // implementation of AudioIODescriptorInterface + audio_config_base_t getConfig() const override; + audio_patch_handle_t getPatchHandle() const override; + void setPatchHandle(audio_patch_handle_t handle) override; status_t open(const audio_config_t *config, audio_devices_t device, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 5e5d38b069..57d1cfa474 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -24,6 +24,7 @@ #include #include #include +#include "AudioIODescriptorInterface.h" #include "AudioSourceDescriptor.h" namespace android { @@ -35,7 +36,7 @@ class DeviceDescriptor; // descriptor for audio outputs. Used to maintain current configuration of each opened audio output // and keep track of the usage of this output by each audio stream type. -class AudioOutputDescriptor: public AudioPortConfig +class AudioOutputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface { public: AudioOutputDescriptor(const sp& port, @@ -73,8 +74,10 @@ public: audio_module_handle_t getModuleHandle() const; - audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }; - void setPatchHandle(audio_patch_handle_t handle) { mPatchHandle = handle; }; + // implementation of AudioIODescriptorInterface + audio_config_base_t getConfig() const override; + audio_patch_handle_t getPatchHandle() const override; + void setPatchHandle(audio_patch_handle_t handle) override; sp mPort; audio_devices_t mDevice; // current device this output is routed to diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index dd5247d9d2..53e6ec990e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -23,13 +23,13 @@ #include #include #include -#include "AudioSessionInfoProvider.h" +#include "AudioIODescriptorInterface.h" namespace android { class AudioPolicyClientInterface; -class AudioSession : public RefBase, public AudioSessionInfoUpdateListener +class AudioSession : public RefBase, public AudioIODescriptorUpdateListener { public: AudioSession(audio_session_t session, @@ -63,9 +63,9 @@ public: uint32_t changeOpenCount(int delta); uint32_t changeActiveCount(int delta); - void setInfoProvider(AudioSessionInfoProvider *provider); - // implementation of AudioSessionInfoUpdateListener - virtual void onSessionInfoUpdate() const; + void setInfoProvider(AudioIODescriptorInterface *provider); + // implementation of AudioIODescriptorUpdateListener + virtual void onIODescriptorUpdate() const; private: record_client_info_t mRecordClientInfo; @@ -77,17 +77,17 @@ private: uint32_t mActiveCount; AudioMix* mPolicyMix; // non NULL when used by a dynamic policy AudioPolicyClientInterface* mClientInterface; - const AudioSessionInfoProvider* mInfoProvider; + const AudioIODescriptorInterface* mInfoProvider; }; class AudioSessionCollection : public DefaultKeyedVector >, - public AudioSessionInfoUpdateListener + public AudioIODescriptorUpdateListener { public: status_t addSession(audio_session_t session, const sp& audioSession, - AudioSessionInfoProvider *provider); + AudioIODescriptorInterface *provider); status_t removeSession(audio_session_t session); @@ -99,8 +99,8 @@ public: bool isSourceActive(audio_source_t source) const; audio_source_t getHighestPrioritySource(bool activeOnly) const; - // implementation of AudioSessionInfoUpdateListener - virtual void onSessionInfoUpdate() const; + // implementation of AudioIODescriptorUpdateListener + virtual void onIODescriptorUpdate() const; status_t dump(int fd, int spaces) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 92332fb112..f0144dbebe 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -164,7 +164,7 @@ size_t AudioInputDescriptor::getAudioSessionCount(bool activeOnly) const status_t AudioInputDescriptor::addAudioSession(audio_session_t session, const sp& audioSession) { - return mSessions.addSession(session, audioSession, /*AudioSessionInfoProvider*/this); + return mSessions.addSession(session, audioSession, /*AudioIODescriptorInterface*/this); } status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) { @@ -179,7 +179,7 @@ audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle) { mPatchHandle = handle; - mSessions.onSessionInfoUpdate(); + mSessions.onIODescriptorUpdate(); } audio_config_base_t AudioInputDescriptor::getConfig() const diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 294a2a695e..3c69de5ba9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -55,11 +55,28 @@ AudioOutputDescriptor::AudioOutputDescriptor(const sp& port, } } +audio_config_base_t AudioOutputDescriptor::getConfig() const +{ + const audio_config_base_t config = { .sample_rate = mSamplingRate, .channel_mask = mChannelMask, + .format = mFormat }; + return config; +} + audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const { return mPort.get() != nullptr ? mPort->getModuleHandle() : AUDIO_MODULE_HANDLE_NONE; } +audio_patch_handle_t AudioOutputDescriptor::getPatchHandle() const +{ + return mPatchHandle; +} + +void AudioOutputDescriptor::setPatchHandle(audio_patch_handle_t handle) +{ + mPatchHandle = handle; +} + audio_port_handle_t AudioOutputDescriptor::getId() const { return mId; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index 7cda46b8ab..91dee35afa 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -86,7 +86,7 @@ uint32_t AudioSession::changeActiveCount(int delta) } // Recording configuration callback: - const AudioSessionInfoProvider* provider = mInfoProvider; + const AudioIODescriptorInterface* provider = mInfoProvider; const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : AUDIO_CONFIG_BASE_INITIALIZER; const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : @@ -114,16 +114,16 @@ bool AudioSession::matches(const sp &other) const return false; } -void AudioSession::setInfoProvider(AudioSessionInfoProvider *provider) +void AudioSession::setInfoProvider(AudioIODescriptorInterface *provider) { mInfoProvider = provider; } -void AudioSession::onSessionInfoUpdate() const +void AudioSession::onIODescriptorUpdate() const { if (mActiveCount > 0) { // resend the callback after requerying the informations from the info provider - const AudioSessionInfoProvider* provider = mInfoProvider; + const AudioIODescriptorInterface* provider = mInfoProvider; const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : AUDIO_CONFIG_BASE_INITIALIZER; const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : @@ -170,7 +170,7 @@ status_t AudioSession::dump(int fd, int spaces, int index) const status_t AudioSessionCollection::addSession(audio_session_t session, const sp& audioSession, - AudioSessionInfoProvider *provider) + AudioIODescriptorInterface *provider) { ssize_t index = indexOfKey(session); @@ -271,10 +271,10 @@ audio_source_t AudioSessionCollection::getHighestPrioritySource(bool activeOnly) return source; } -void AudioSessionCollection::onSessionInfoUpdate() const +void AudioSessionCollection::onIODescriptorUpdate() const { for (size_t i = 0; i < size(); i++) { - valueAt(i)->onSessionInfoUpdate(); + valueAt(i)->onIODescriptorUpdate(); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 992cf7422b..0770134d09 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -433,20 +434,15 @@ uint32_t AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, uint32_ sp AudioPolicyManager::createTelephonyPatch( bool isRx, audio_devices_t device, uint32_t delayMs) { - struct audio_patch patch; - patch.num_sources = 1; - patch.num_sinks = 1; + PatchBuilder patchBuilder; sp txSourceDeviceDesc; if (isRx) { - fillAudioPortConfigForDevice(mAvailableOutputDevices, device, &patch.sinks[0]); - fillAudioPortConfigForDevice( - mAvailableInputDevices, AUDIO_DEVICE_IN_TELEPHONY_RX, &patch.sources[0]); + patchBuilder.addSink(findDevice(mAvailableOutputDevices, device)). + addSource(findDevice(mAvailableInputDevices, AUDIO_DEVICE_IN_TELEPHONY_RX)); } else { - txSourceDeviceDesc = fillAudioPortConfigForDevice( - mAvailableInputDevices, device, &patch.sources[0]); - fillAudioPortConfigForDevice( - mAvailableOutputDevices, AUDIO_DEVICE_OUT_TELEPHONY_TX, &patch.sinks[0]); + patchBuilder.addSource(txSourceDeviceDesc = findDevice(mAvailableInputDevices, device)). + addSink(findDevice(mAvailableOutputDevices, AUDIO_DEVICE_OUT_TELEPHONY_TX)); } audio_devices_t outputDevice = isRx ? device : AUDIO_DEVICE_OUT_TELEPHONY_TX; @@ -457,9 +453,7 @@ sp AudioPolicyManager::createTelephonyPatch( sp outputDesc = mOutputs.valueFor(output); ALOG_ASSERT(!outputDesc->isDuplicated(), "%s() %#x device output %d is duplicated", __func__, outputDevice, output); - outputDesc->toAudioPortConfig(&patch.sources[1]); - patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH; - patch.num_sources = 2; + patchBuilder.addSource(outputDesc, { .stream = AUDIO_STREAM_PATCH }); } if (!isRx) { @@ -481,26 +475,25 @@ sp AudioPolicyManager::createTelephonyPatch( } audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - status_t status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, delayMs); + status_t status = mpClientInterface->createAudioPatch( + patchBuilder.patch(), &afPatchHandle, delayMs); ALOGW_IF(status != NO_ERROR, "%s() error %d creating %s audio patch", __func__, status, isRx ? "RX" : "TX"); sp audioPatch; if (status == NO_ERROR) { - audioPatch = new AudioPatch(&patch, mUidCached); + audioPatch = new AudioPatch(patchBuilder.patch(), mUidCached); audioPatch->mAfPatchHandle = afPatchHandle; audioPatch->mUid = mUidCached; } return audioPatch; } -sp AudioPolicyManager::fillAudioPortConfigForDevice( - const DeviceVector& devices, audio_devices_t device, audio_port_config *config) { +sp AudioPolicyManager::findDevice( + const DeviceVector& devices, audio_devices_t device) { DeviceVector deviceList = devices.getDevicesFromType(device); ALOG_ASSERT(!deviceList.isEmpty(), "%s() selected device type %#x is not in devices list", __func__, device); - sp deviceDesc = deviceList.itemAt(0); - deviceDesc->toAudioPortConfig(config); - return deviceDesc; + return deviceList.itemAt(0); } void AudioPolicyManager::setPhoneState(audio_mode_t state) @@ -2991,28 +2984,8 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } // TODO: check from routing capabilities in config file and other conflicting patches - audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - if (index >= 0) { - afPatchHandle = patchDesc->mAfPatchHandle; - } - - status_t status = mpClientInterface->createAudioPatch(&newPatch, - &afPatchHandle, - 0); - ALOGV("createAudioPatch() patch panel returned %d patchHandle %d", - status, afPatchHandle); - if (status == NO_ERROR) { - if (index < 0) { - patchDesc = new AudioPatch(&newPatch, uid); - addAudioPatch(patchDesc->mHandle, patchDesc); - } else { - patchDesc->mPatch = newPatch; - } - patchDesc->mAfPatchHandle = afPatchHandle; - *handle = patchDesc->mHandle; - nextAudioPortGeneration(); - mpClientInterface->onAudioPatchListUpdate(); - } else { + status_t status = installPatch(__func__, index, handle, &newPatch, 0, uid, &patchDesc); + if (status != NO_ERROR) { ALOGW("createAudioPatch() patch panel could not connect device patch, error %d", status); return INVALID_OPERATION; @@ -3324,7 +3297,6 @@ status_t AudioPolicyManager::connectAudioSource(const sp& mAvailableOutputDevices.getDevice(sinkDevice, String8("")); audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - struct audio_patch *patch = &sourceDesc->mPatchDesc->mPatch; if (srcDeviceDesc->getAudioPort()->mModule->getHandle() == sinkDeviceDesc->getAudioPort()->mModule->getHandle() && @@ -3356,16 +3328,14 @@ status_t AudioPolicyManager::connectAudioSource(const sp& // be connected as well as the stream type for volume control // - the sink is defined by whatever output device is currently selected for the output // though which this patch is routed. - patch->num_sinks = 0; - patch->num_sources = 2; - srcDeviceDesc->toAudioPortConfig(&patch->sources[0], NULL); - outputDesc->toAudioPortConfig(&patch->sources[1], NULL); - patch->sources[1].ext.mix.usecase.stream = stream; - status = mpClientInterface->createAudioPatch(patch, + PatchBuilder patchBuilder; + patchBuilder.addSource(srcDeviceDesc).addSource(outputDesc, { .stream = stream }); + status = mpClientInterface->createAudioPatch(patchBuilder.patch(), &afPatchHandle, 0); ALOGV("%s patch panel returned %d patchHandle %d", __FUNCTION__, status, afPatchHandle); + sourceDesc->mPatchDesc->mPatch = *patchBuilder.patch(); if (status != NO_ERROR) { ALOGW("%s patch panel could not connect device patch, error %d", __FUNCTION__, status); @@ -4875,48 +4845,12 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou } if (!deviceList.isEmpty()) { - struct audio_patch patch; - outputDesc->toAudioPortConfig(&patch.sources[0]); - patch.num_sources = 1; - patch.num_sinks = 0; + PatchBuilder patchBuilder; + patchBuilder.addSource(outputDesc); for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) { - deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]); - patch.num_sinks++; - } - ssize_t index; - if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) { - index = mAudioPatches.indexOfKey(*patchHandle); - } else { - index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle()); - } - sp< AudioPatch> patchDesc; - audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - if (index >= 0) { - patchDesc = mAudioPatches.valueAt(index); - afPatchHandle = patchDesc->mAfPatchHandle; - } - - status_t status = mpClientInterface->createAudioPatch(&patch, - &afPatchHandle, - delayMs); - ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d" - "num_sources %d num_sinks %d", - status, afPatchHandle, patch.num_sources, patch.num_sinks); - if (status == NO_ERROR) { - if (index < 0) { - patchDesc = new AudioPatch(&patch, mUidCached); - addAudioPatch(patchDesc->mHandle, patchDesc); - } else { - patchDesc->mPatch = patch; - } - patchDesc->mAfPatchHandle = afPatchHandle; - if (patchHandle) { - *patchHandle = patchDesc->mHandle; - } - outputDesc->setPatchHandle(patchDesc->mHandle); - nextAudioPortGeneration(); - mpClientInterface->onAudioPatchListUpdate(); + patchBuilder.addSink(deviceList.itemAt(i)); } + installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } // inform all input as well @@ -4976,51 +4910,19 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device); if (!deviceList.isEmpty()) { - struct audio_patch patch; - inputDesc->toAudioPortConfig(&patch.sinks[0]); + PatchBuilder patchBuilder; + patchBuilder.addSink(inputDesc, // AUDIO_SOURCE_HOTWORD is for internal use only: // handled as AUDIO_SOURCE_VOICE_RECOGNITION by the audio HAL - if (patch.sinks[0].ext.mix.usecase.source == AUDIO_SOURCE_HOTWORD && - !inputDesc->isSoundTrigger()) { - patch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_VOICE_RECOGNITION; - } - patch.num_sinks = 1; + [inputDesc](const PatchBuilder::mix_usecase_t& usecase) { + auto result = usecase; + if (result.source == AUDIO_SOURCE_HOTWORD && !inputDesc->isSoundTrigger()) { + result.source = AUDIO_SOURCE_VOICE_RECOGNITION; + } + return result; }). //only one input device for now - deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]); - patch.num_sources = 1; - ssize_t index; - if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) { - index = mAudioPatches.indexOfKey(*patchHandle); - } else { - index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle()); - } - sp< AudioPatch> patchDesc; - audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - if (index >= 0) { - patchDesc = mAudioPatches.valueAt(index); - afPatchHandle = patchDesc->mAfPatchHandle; - } - - status_t status = mpClientInterface->createAudioPatch(&patch, - &afPatchHandle, - 0); - ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d", - status, afPatchHandle); - if (status == NO_ERROR) { - if (index < 0) { - patchDesc = new AudioPatch(&patch, mUidCached); - addAudioPatch(patchDesc->mHandle, patchDesc); - } else { - patchDesc->mPatch = patch; - } - patchDesc->mAfPatchHandle = afPatchHandle; - if (patchHandle) { - *patchHandle = patchDesc->mHandle; - } - inputDesc->setPatchHandle(patchDesc->mHandle); - nextAudioPortGeneration(); - mpClientInterface->onAudioPatchListUpdate(); - } + addSource(deviceList.itemAt(0)); + status = installPatch(__func__, patchHandle, inputDesc.get(), patchBuilder.patch(), 0); } } return status; @@ -5743,4 +5645,58 @@ void AudioPolicyManager::updateAudioProfiles(audio_devices_t device, } } +status_t AudioPolicyManager::installPatch(const char *caller, + audio_patch_handle_t *patchHandle, + AudioIODescriptorInterface *ioDescriptor, + const struct audio_patch *patch, + int delayMs) +{ + ssize_t index = mAudioPatches.indexOfKey( + patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE ? + *patchHandle : ioDescriptor->getPatchHandle()); + sp patchDesc; + status_t status = installPatch( + caller, index, patchHandle, patch, delayMs, mUidCached, &patchDesc); + if (status == NO_ERROR) { + ioDescriptor->setPatchHandle(patchDesc->mHandle); + } + return status; +} + +status_t AudioPolicyManager::installPatch(const char *caller, + ssize_t index, + audio_patch_handle_t *patchHandle, + const struct audio_patch *patch, + int delayMs, + uid_t uid, + sp *patchDescPtr) +{ + sp patchDesc; + audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; + if (index >= 0) { + patchDesc = mAudioPatches.valueAt(index); + afPatchHandle = patchDesc->mAfPatchHandle; + } + + status_t status = mpClientInterface->createAudioPatch(patch, &afPatchHandle, delayMs); + ALOGV("%s() AF::createAudioPatch returned %d patchHandle %d num_sources %d num_sinks %d", + caller, status, afPatchHandle, patch->num_sources, patch->num_sinks); + if (status == NO_ERROR) { + if (index < 0) { + patchDesc = new AudioPatch(patch, uid); + addAudioPatch(patchDesc->mHandle, patchDesc); + } else { + patchDesc->mPatch = *patch; + } + patchDesc->mAfPatchHandle = afPatchHandle; + if (patchHandle) { + *patchHandle = patchDesc->mHandle; + } + nextAudioPortGeneration(); + mpClientInterface->onAudioPatchListUpdate(); + } + if (patchDescPtr) *patchDescPtr = patchDesc; + return status; +} + } // namespace android diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 2b68882215..21e258e69e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -491,8 +491,8 @@ protected: uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0); sp createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs); - sp fillAudioPortConfigForDevice( - const DeviceVector& devices, audio_devices_t device, audio_port_config *config); + sp findDevice( + const DeviceVector& devices, audio_devices_t device); // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force // the re-evaluation of the output device. @@ -664,6 +664,18 @@ private: param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono); mpClientInterface->setParameters(output, param.toString()); } + status_t installPatch(const char *caller, + audio_patch_handle_t *patchHandle, + AudioIODescriptorInterface *ioDescriptor, + const struct audio_patch *patch, + int delayMs); + status_t installPatch(const char *caller, + ssize_t index, + audio_patch_handle_t *patchHandle, + const struct audio_patch *patch, + int delayMs, + uid_t uid, + sp *patchDescPtr); bool soundTriggerSupportsConcurrentCapture(); bool mSoundTriggerSupportsConcurrentCapture; diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp index a9593b8a54..2d9260edcd 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -19,6 +19,8 @@ #include +#include + #include "AudioPolicyTestClient.h" #include "AudioPolicyTestManager.h" @@ -166,29 +168,14 @@ TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { } TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) { - audio_patch patch{}; audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; uid_t uid = 42; const size_t patchCountBefore = mClient->getActivePatchesCount(); - patch.num_sources = 1; - { - auto& src = patch.sources[0]; - src.role = AUDIO_PORT_ROLE_SOURCE; - src.type = AUDIO_PORT_TYPE_MIX; - src.id = mManager->getConfig().getAvailableInputDevices()[0]->getId(); - // Note: these are the parameters of the output device. - src.sample_rate = 44100; - src.format = AUDIO_FORMAT_PCM_16_BIT; - src.channel_mask = AUDIO_CHANNEL_OUT_STEREO; - } - patch.num_sinks = 1; - { - auto& sink = patch.sinks[0]; - sink.role = AUDIO_PORT_ROLE_SINK; - sink.type = AUDIO_PORT_TYPE_DEVICE; - sink.id = mManager->getConfig().getDefaultOutputDevice()->getId(); - } - ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(&patch, &handle, uid)); + ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty()); + PatchBuilder patchBuilder; + patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]). + addSink(mManager->getConfig().getDefaultOutputDevice()); + ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid)); ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle); ASSERT_EQ(patchCountBefore + 1, mClient->getActivePatchesCount()); } -- GitLab From c589a49b2efbb6cb6cbe9221269bdd7953ae6d06 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 16 May 2018 11:14:57 -0700 Subject: [PATCH 0058/1530] PatchPanel: Fix typo in patch validation This piece of logic does not seem to be correct: (patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || patch->sinks[i].ext.mix.hw_module != srcModule) I assume it should be: (patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || (patch->sinks[i].type == AUDIO_PORT_TYPE_DEVICE && patch->sinks[i].ext.device.hw_module != srcModule) Was working fine because 'hw_module' is the first field both in audio_port_config_device_ext and audio_port_config_mix_ext. Test: make Change-Id: Icf80cec701048bddc4d63eaedf00fc75b5b891dd --- services/audioflinger/PatchPanel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index eaf1120c68..adefd4bc52 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -178,8 +178,10 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa for (unsigned int i = 0; i < patch->num_sinks; i++) { // support only one sink if connection to a mix or across HW modules if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || - patch->sinks[i].ext.mix.hw_module != srcModule) && + (patch->sinks[i].type == AUDIO_PORT_TYPE_DEVICE && + patch->sinks[i].ext.device.hw_module != srcModule)) && patch->num_sinks > 1) { + ALOGW("%s() multiple sinks for mix or across modules not supported", __func__); status = INVALID_OPERATION; goto exit; } -- GitLab From ab7ef300b74904bce6c5c810d9e5679abfa582e6 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 15 May 2018 19:35:29 -0700 Subject: [PATCH 0059/1530] Move ServiceUtilities from audioflinger to libmediautils Test: Play Music, Play Movies, Camera, Photos Bug: 79485140 Change-Id: Ib8382547828e386024145c416321d24e4af182db --- media/utils/Android.bp | 1 + .../utils}/ServiceUtilities.cpp | 2 +- .../include/mediautils}/ServiceUtilities.h | 2 ++ services/audioflinger/Android.mk | 20 ------------------- services/audioflinger/AudioFlinger.cpp | 2 +- services/audioflinger/Effects.cpp | 2 +- services/audioflinger/PatchPanel.cpp | 2 +- services/audioflinger/Threads.cpp | 4 ++-- services/audioflinger/Tracks.cpp | 2 +- services/audiopolicy/Android.mk | 2 +- .../service/AudioPolicyEffects.cpp | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 2 +- .../service/AudioPolicyService.cpp | 2 +- services/oboeservice/AAudioService.cpp | 2 +- services/oboeservice/Android.mk | 1 - services/soundtrigger/Android.mk | 3 +-- .../soundtrigger/SoundTriggerHwService.cpp | 2 +- 17 files changed, 17 insertions(+), 36 deletions(-) rename {services/audioflinger => media/utils}/ServiceUtilities.cpp (99%) rename {services/audioflinger => media/utils/include/mediautils}/ServiceUtilities.h (97%) diff --git a/media/utils/Android.bp b/media/utils/Android.bp index d6dae5b04b..c8da34d559 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -21,6 +21,7 @@ cc_library { "MemoryLeakTrackUtil.cpp", "ProcessInfo.cpp", "SchedulingPolicyService.cpp", + "ServiceUtilities.cpp", ], shared_libs: [ "libbinder", diff --git a/services/audioflinger/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp similarity index 99% rename from services/audioflinger/ServiceUtilities.cpp rename to media/utils/ServiceUtilities.cpp index aa267ead6c..c4a4374258 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "ServiceUtilities.h" +#include "mediautils/ServiceUtilities.h" /* When performing permission checks we do not use permission cache for * runtime permissions (protection level dangerous) as they may change at diff --git a/services/audioflinger/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h similarity index 97% rename from services/audioflinger/ServiceUtilities.h rename to media/utils/include/mediautils/ServiceUtilities.h index f45ada1a4a..8ead410111 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -20,6 +20,8 @@ namespace android { +// Audio permission utilities + extern pid_t getpid_cached; bool isTrustedCallingUid(uid_t uid); bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid); diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 16c1673f49..78a62ca8dc 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -2,25 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - ServiceUtilities.cpp - -# FIXME Move this library to frameworks/native -LOCAL_MODULE := libserviceutility - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - liblog \ - libbinder - -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SANITIZE := integer_overflow - -include $(BUILD_SHARED_LIBRARY) - -include $(CLEAR_VARS) - LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ Threads.cpp \ @@ -54,7 +35,6 @@ LOCAL_SHARED_LIBRARIES := \ libnbaio \ libnblog \ libpowermanager \ - libserviceutility \ libmediautils \ libmemunreachable \ libmedia_helper diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 306d1a10e9..08d901d505 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -47,7 +47,6 @@ #include #include "AudioFlinger.h" -#include "ServiceUtilities.h" #include @@ -66,6 +65,7 @@ #include #include #include +#include #include //#define BUFLOG_NDEBUG 0 diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index dcf223ce3d..5e82b75e42 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -31,9 +31,9 @@ #include #include #include +#include #include "AudioFlinger.h" -#include "ServiceUtilities.h" // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index be3286f334..ca86c42cc9 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -24,9 +24,9 @@ #include #include "AudioFlinger.h" -#include "ServiceUtilities.h" #include #include +#include // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 44230336b5..7b5d9e640a 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -62,8 +62,8 @@ #include "AudioFlinger.h" #include "FastMixer.h" #include "FastCapture.h" -#include "ServiceUtilities.h" -#include "mediautils/SchedulingPolicyService.h" +#include +#include #ifdef ADD_BATTERY_DATA #include diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index a7c4253569..fc8f34b5f0 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -28,11 +28,11 @@ #include #include "AudioFlinger.h" -#include "ServiceUtilities.h" #include #include #include +#include #include // ---------------------------------------------------------------------------- diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index b0253401c6..b75e9573ac 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -21,10 +21,10 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libaudioclient \ libhardware_legacy \ - libserviceutility \ libaudiopolicymanager \ libmedia_helper \ libmediametrics \ + libmediautils \ libeffectsconfig LOCAL_STATIC_LIBRARIES := \ diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index c7dfe0f3b7..fdae23b4c9 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include "AudioPolicyEffects.h" -#include "ServiceUtilities.h" namespace android { diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 9592b6acdc..b74bd966f1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -21,7 +21,7 @@ #include #include "AudioPolicyService.h" -#include "ServiceUtilities.h" +#include #include "TypeConverter.h" namespace android { diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index f3cddc33da..5d25ea837b 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -35,10 +35,10 @@ #include #include #include "AudioPolicyService.h" -#include "ServiceUtilities.h" #include #include #include +#include #include #include diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp index 6a72e5bd29..94440b17b9 100644 --- a/services/oboeservice/AAudioService.cpp +++ b/services/oboeservice/AAudioService.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include "binding/AAudioServiceMessage.h" @@ -33,7 +34,6 @@ #include "AAudioServiceStreamMMAP.h" #include "AAudioServiceStreamShared.h" #include "binding/IAAudioService.h" -#include "ServiceUtilities.h" using namespace android; using namespace aaudio; diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk index 584b2ef07b..3d5f140797 100644 --- a/services/oboeservice/Android.mk +++ b/services/oboeservice/Android.mk @@ -53,7 +53,6 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ libmediautils \ - libserviceutility \ libutils \ liblog diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk index ad3666e427..3c7d29d495 100644 --- a/services/soundtrigger/Android.mk +++ b/services/soundtrigger/Android.mk @@ -34,8 +34,7 @@ LOCAL_SHARED_LIBRARIES:= \ libhardware \ libsoundtrigger \ libaudioclient \ - libserviceutility - + libmediautils \ ifeq ($(USE_LEGACY_LOCAL_AUDIO_HAL),true) # libhardware configuration diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index a7d6e839e1..6bf6e94f3b 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -27,13 +27,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "SoundTriggerHwService.h" #ifdef SOUND_TRIGGER_USE_STUB_MODULE -- GitLab From 201369ba583a63b87f40dacbbc2d9c2a13cbb958 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 16 May 2018 16:52:32 -0700 Subject: [PATCH 0060/1530] audioflinger: Dump software patches Example output: Software patches: Patch 44: thread 0xa6d83900 => thread 0xa7dfe000 This helps to identify threads used for the patch. Test: adb shell dumpsys media.audio_flinger Change-Id: I6c70945abd8e4abd46cd0311559d35efb6127555 --- services/audioflinger/AudioFlinger.cpp | 2 ++ services/audioflinger/PatchPanel.cpp | 30 ++++++++++++++++++++++++++ services/audioflinger/PatchPanel.h | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 306d1a10e9..7068377355 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -533,6 +533,8 @@ status_t AudioFlinger::dump(int fd, const Vector& args) dev->dump(fd); } + mPatchPanel.dump(fd); + #ifdef TEE_SINK // dump the serially shared record tee sink if (mRecordTeeSource != 0) { diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index be3286f334..76661e78ed 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -467,6 +467,14 @@ void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) mPlayback.closeConnections(panel); } +String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) +{ + String8 result; + result.appendFormat("Patch %d: thread %p => thread %p\n", + myHandle, mRecord.thread().get(), mPlayback.thread().get()); + return result; +} + /* Disconnect a patch */ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) { @@ -555,4 +563,26 @@ sp AudioFlinger::PatchPanel::findHwDeviceByModule(audio_modu return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice(); } +void AudioFlinger::PatchPanel::dump(int fd) +{ + // Only dump software patches. + bool headerPrinted = false; + for (auto& iter : mPatches) { + if (iter.second.isSoftware()) { + if (!headerPrinted) { + String8 header("\nSoftware patches:\n"); + write(fd, header.string(), header.size()); + headerPrinted = true; + } + String8 patchDump(" "); + patchDump.append(iter.second.dump(iter.first)); + write(fd, patchDump.string(), patchDump.size()); + } + } + if (headerPrinted) { + String8 trailing("\n"); + write(fd, trailing.string(), trailing.size()); + } +} + } // namespace android diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index f4e43e2a51..dff8ad2d61 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -42,6 +42,8 @@ public: status_t listAudioPatches(unsigned int *num_patches, struct audio_patch *patches); + void dump(int fd); + private: template class Endpoint { @@ -116,6 +118,8 @@ private: return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE || mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; } + String8 dump(audio_patch_handle_t myHandle); + // Note that audio_patch::id is only unique within a HAL module struct audio_patch mAudioPatch; // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0 -- GitLab From 05a594f745a842a475cfbf4695a25ebc68899246 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 17 May 2018 11:43:44 -0700 Subject: [PATCH 0061/1530] Update audio team members in frameworks/av/media/OWNERS Test: not a compile issue Change-Id: I1dac17d911e5bcb72df13a39cd1e53899984fac4 --- media/OWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/OWNERS b/media/OWNERS index 1f687a2d68..1e2d123d17 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -2,8 +2,10 @@ chz@google.com dwkang@google.com elaurent@google.com essick@google.com +gkasten@google.com hkuang@google.com hunga@google.com +jiabin@google.com jmtrivi@google.com krocard@google.com lajos@google.com -- GitLab From 4ef19fa450e8b523741a22fabee8f62691abec37 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 15 May 2018 19:35:29 -0700 Subject: [PATCH 0062/1530] Update audio permission checking Change captureAudioOutputAllowed to check client pid. Check calling uid with AID_AUDIOSERVER instead of calling pid with own pid. This is consistent, and works if AudioFlinger and AudioPolicyManager exist as different processes. Remove getpid_cached since getpid() is very fast. This removes any initialization issues. Replace getuid() with AID_AUDIOSERVER to remove ambiguity of multiple native audio services for multiple users. Only one exists regardless of users. Do not use multiuser UID checks for certain native services that do not spawn for multiple users to prevent accidently exposure. Move permission checks to use ServiceUtilities for control and consistency. Rename isTrustedCallingUid to isAudioServerOrMediaServerUid so that permission check is explicitly known to caller. Update MediaLogService to use ServiceUtilities. Test: Basic sanity Test: AudioTrackTest, AudioRecordTest, SoundPool, SoundTrigger Bug: 79485140 Change-Id: Ib8ccb36929a9b4806c01626f32fa023a046d6020 --- media/libaudioclient/Android.bp | 1 + media/libaudioclient/IAudioFlinger.cpp | 6 +-- media/libaudioclient/IAudioPolicyService.cpp | 5 +-- media/utils/Android.bp | 1 + media/utils/ServiceUtilities.cpp | 34 +++------------- .../include/mediautils/ServiceUtilities.h | 40 ++++++++++++++++++- services/audioflinger/AudioFlinger.cpp | 36 ++++++++--------- services/audioflinger/Effects.cpp | 2 +- services/audioflinger/Threads.cpp | 6 +-- services/audioflinger/Tracks.cpp | 11 ++--- .../managerdefault/AudioPolicyManager.cpp | 3 +- .../managerdefault/AudioPolicyManager.h | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 4 +- .../service/AudioPolicyService.cpp | 11 +---- .../audiopolicy/service/AudioPolicyService.h | 1 - services/medialog/Android.bp | 2 + services/medialog/MediaLogService.cpp | 12 +++--- 17 files changed, 91 insertions(+), 86 deletions(-) diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index 2df37a85c1..6146c0e2f5 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -49,6 +49,7 @@ cc_library_shared { "libaudiomanager", "libmedia_helper", "libmediametrics", + "libmediautils", ], export_shared_lib_headers: ["libbinder"], diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 00af7e8ea1..37c62a81dd 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -24,10 +24,8 @@ #include #include -#include #include -#include - +#include #include "IAudioFlinger.h" namespace android { @@ -912,7 +910,7 @@ status_t BnAudioFlinger::onTransact( case SET_MIC_MUTE: case SET_LOW_RAM_DEVICE: case SYSTEM_READY: { - if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) { + if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index a1236e7389..316105c56f 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -24,11 +24,10 @@ #include #include -#include #include #include #include -#include +#include #include namespace android { @@ -936,7 +935,7 @@ status_t BnAudioPolicyService::onTransact( case STOP_AUDIO_SOURCE: case GET_SURROUND_FORMATS: case SET_SURROUND_FORMAT_ENABLED: { - if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) { + if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); diff --git a/media/utils/Android.bp b/media/utils/Android.bp index c8da34d559..de8e46a016 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -25,6 +25,7 @@ cc_library { ], shared_libs: [ "libbinder", + "libcutils", "liblog", "libutils", "libmemunreachable", diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index c4a4374258..6a90beaeba 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "mediautils/ServiceUtilities.h" /* When performing permission checks we do not use permission cache for @@ -32,24 +31,6 @@ namespace android { static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO"); -// Not valid until initialized by AudioFlinger constructor. It would have to be -// re-initialized if the process containing AudioFlinger service forks (which it doesn't). -// This is often used to validate binder interface calls within audioserver -// (e.g. AudioPolicyManager to AudioFlinger). -pid_t getpid_cached; - -// A trusted calling UID may specify the client UID as part of a binder interface call. -// otherwise the calling UID must be equal to the client UID. -bool isTrustedCallingUid(uid_t uid) { - switch (uid) { - case AID_MEDIA: - case AID_AUDIOSERVER: - return true; - default: - return false; - } -} - static String16 resolveCallingPackage(PermissionController& permissionController, const String16& opPackageName, uid_t uid) { if (opPackageName.size() > 0) { @@ -71,16 +52,11 @@ static String16 resolveCallingPackage(PermissionController& permissionController return packages[0]; } -static inline bool isAudioServerOrRoot(uid_t uid) { - // AID_ROOT is OK for command-line tests. Native unforked audioserver always OK. - return uid == AID_ROOT || uid == AID_AUDIOSERVER ; -} - static bool checkRecordingInternal(const String16& opPackageName, pid_t pid, uid_t uid, bool start) { // Okay to not track in app ops as audio server is us and if // device is rooted security model is considered compromised. - if (isAudioServerOrRoot(uid)) return true; + if (isAudioServerOrRootUid(uid)) return true; // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder) // may open a record track on behalf of a client. Note that pid may be a tid. @@ -127,7 +103,7 @@ bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid) { void finishRecording(const String16& opPackageName, uid_t uid) { // Okay to not track in app ops as audio server is us and if // device is rooted security model is considered compromised. - if (isAudioServerOrRoot(uid)) return; + if (isAudioServerOrRootUid(uid)) return; PermissionController permissionController; String16 resolvedOpPackageName = resolveCallingPackage( @@ -142,7 +118,7 @@ void finishRecording(const String16& opPackageName, uid_t uid) { } bool captureAudioOutputAllowed(pid_t pid, uid_t uid) { - if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; + if (isAudioServerOrRootUid(uid)) return true; static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT"); bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid); if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT"); @@ -163,7 +139,8 @@ bool captureHotwordAllowed(pid_t pid, uid_t uid) { } bool settingsAllowed() { - if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; + // given this is a permission check, could this be isAudioServerOrRootUid()? + if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true; static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS"); // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. bool ok = PermissionCache::checkCallingPermission(sAudioSettings); @@ -180,7 +157,6 @@ bool modifyAudioRoutingAllowed() { } bool dumpAllowed() { - // don't optimize for same pid, since mediaserver never dumps itself static const String16 sDump("android.permission.DUMP"); // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. bool ok = PermissionCache::checkCallingPermission(sDump); diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 8ead410111..2bdba5e3d0 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -17,13 +17,49 @@ #include #include +#include +#include namespace android { // Audio permission utilities -extern pid_t getpid_cached; -bool isTrustedCallingUid(uid_t uid); +// Used for calls that should originate from system services. +// We allow that some services might have separate processes to +// handle multiple users, e.g. u10_system, u10_bluetooth, u10_radio. +static inline bool isServiceUid(uid_t uid) { + return multiuser_get_app_id(uid) < AID_APP_START; +} + +// Used for calls that should originate from audioserver. +static inline bool isAudioServerUid(uid_t uid) { + return uid == AID_AUDIOSERVER; +} + +// Used for some permission checks. +// AID_ROOT is OK for command-line tests. Native audioserver always OK. +static inline bool isAudioServerOrRootUid(uid_t uid) { + return uid == AID_AUDIOSERVER || uid == AID_ROOT; +} + +// Used for calls that should come from system server or internal. +// Note: system server is multiprocess for multiple users. audioserver is not. +static inline bool isAudioServerOrSystemServerUid(uid_t uid) { + return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER; +} + +// Mediaserver may forward the client PID and UID as part of a binder interface call; +// otherwise the calling UID must be equal to the client UID. +static inline bool isAudioServerOrMediaServerUid(uid_t uid) { + switch (uid) { + case AID_MEDIA: + case AID_AUDIOSERVER: + return true; + default: + return false; + } +} + bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid); bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid); void finishRecording(const String16& opPackageName, uid_t uid); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 08d901d505..090705337a 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -169,7 +168,6 @@ AudioFlinger::AudioFlinger() mNextUniqueIds[use] = AUDIO_UNIQUE_ID_USE_MAX; } - getpid_cached = getpid(); const bool doLog = property_get_bool("ro.test_harness", false); if (doLog) { mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", @@ -665,7 +663,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, bool updatePid = (input.clientInfo.clientPid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); uid_t clientUid = input.clientInfo.clientUid; - if (!isTrustedCallingUid(callingUid)) { + if (!isAudioServerOrMediaServerUid(callingUid)) { ALOGW_IF(clientUid != callingUid, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid); @@ -1077,9 +1075,9 @@ status_t AudioFlinger::checkStreamType(audio_stream_type_t stream) const ALOGW("checkStreamType() invalid stream %d", stream); return BAD_VALUE; } - pid_t caller = IPCThreadState::self()->getCallingPid(); - if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && caller != getpid_cached) { - ALOGW("checkStreamType() pid %d cannot use internal stream type %d", caller, stream); + const uid_t callerUid = IPCThreadState::self()->getCallingUid(); + if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && !isAudioServerUid(callerUid)) { + ALOGW("checkStreamType() uid %d cannot use internal stream type %d", callerUid, stream); return PERMISSION_DENIED; } @@ -1199,9 +1197,8 @@ void AudioFlinger::filterReservedParameters(String8& keyValuePairs, uid_t callin String8(AudioParameter::keyStreamSupportedSamplingRates), }; - // multiuser friendly app ID check for requests coming from audioserver - if (multiuser_get_app_id(callingUid) == AID_AUDIOSERVER) { - return; + if (isAudioServerUid(callingUid)) { + return; // no need to filter if audioserver. } AudioParameter param = AudioParameter(keyValuePairs); @@ -1635,7 +1632,7 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu bool updatePid = (input.clientInfo.clientPid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); uid_t clientUid = input.clientInfo.clientUid; - if (!isTrustedCallingUid(callingUid)) { + if (!isAudioServerOrMediaServerUid(callingUid)) { ALOGW_IF(clientUid != callingUid, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid); @@ -1883,7 +1880,7 @@ size_t AudioFlinger::getPrimaryOutputFrameCount() status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) { uid_t uid = IPCThreadState::self()->getCallingUid(); - if (uid != AID_SYSTEM) { + if (!isAudioServerOrSystemServerUid(uid)) { return PERMISSION_DENIED; } Mutex::Autolock _l(mLock); @@ -2625,7 +2622,8 @@ void AudioFlinger::acquireAudioSessionId(audio_session_t audioSession, pid_t pid Mutex::Autolock _l(mLock); pid_t caller = IPCThreadState::self()->getCallingPid(); ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid); - if (pid != -1 && (caller == getpid_cached)) { + const uid_t callerUid = IPCThreadState::self()->getCallingUid(); + if (pid != -1 && isAudioServerUid(callerUid)) { // check must match releaseAudioSessionId() caller = pid; } @@ -2659,7 +2657,8 @@ void AudioFlinger::releaseAudioSessionId(audio_session_t audioSession, pid_t pid Mutex::Autolock _l(mLock); pid_t caller = IPCThreadState::self()->getCallingPid(); ALOGV("releasing %d from %d for %d", audioSession, caller, pid); - if (pid != -1 && (caller == getpid_cached)) { + const uid_t callerUid = IPCThreadState::self()->getCallingUid(); + if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId() caller = pid; } size_t num = mAudioSessionRefs.size(); @@ -2676,9 +2675,10 @@ void AudioFlinger::releaseAudioSessionId(audio_session_t audioSession, pid_t pid return; } } - // If the caller is mediaserver it is likely that the session being released was acquired + // If the caller is audioserver it is likely that the session being released was acquired // on behalf of a process not in notification clients and we ignore the warning. - ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller); + ALOGW_IF(!isAudioServerUid(callerUid), + "session id %d not found for pid %d", audioSession, caller); } bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession) @@ -2986,7 +2986,7 @@ sp AudioFlinger::createEffect( effect_descriptor_t desc; const uid_t callingUid = IPCThreadState::self()->getCallingUid(); - if (pid == -1 || !isTrustedCallingUid(callingUid)) { + if (pid == -1 || !isAudioServerOrMediaServerUid(callingUid)) { const pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW_IF(pid != -1 && pid != callingPid, "%s uid %d pid %d tried to pass itself off as pid %d", @@ -3009,8 +3009,8 @@ sp AudioFlinger::createEffect( } // Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects - // that can only be created by audio policy manager (running in same process) - if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid_cached != pid) { + // that can only be created by audio policy manager + if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && !isAudioServerUid(callingUid)) { lStatus = PERMISSION_DENIED; goto Exit; } diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 5e82b75e42..25425b2363 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1814,7 +1814,7 @@ void AudioFlinger::EffectHandle::dumpToBuffer(char* buffer, size_t size) bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock); snprintf(buffer, size, "\t\t\t%5d %5d %3s %3s %5u %5u\n", - (mClient == 0) ? getpid_cached : mClient->pid(), + (mClient == 0) ? getpid() : mClient->pid(), mPriority, mHasControl ? "yes" : "no", locked ? "yes" : "no", diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 7b5d9e640a..8a41785eec 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3946,7 +3946,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // start the fast mixer mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO); pid_t tid = mFastMixer->getTid(); - sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/); + sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/); stream()->setHalThreadPriority(kPriorityFastMixer); #ifdef AUDIO_WATCHDOG @@ -3955,7 +3955,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud mAudioWatchdog->setDump(&mAudioWatchdogDump); mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO); tid = mAudioWatchdog->getTid(); - sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/); + sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/); #endif } @@ -6362,7 +6362,7 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, // start the fast capture mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO); pid_t tid = mFastCapture->getTid(); - sendPrioConfigEvent(getpid_cached, tid, kPriorityFastCapture, false /*forApp*/); + sendPrioConfigEvent(getpid(), tid, kPriorityFastCapture, false /*forApp*/); stream()->setHalThreadPriority(kPriorityFastCapture); #ifdef AUDIO_WATCHDOG // FIXME diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index fc8f34b5f0..34950999cf 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -102,7 +102,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mIsInvalid(false) { const uid_t callingUid = IPCThreadState::self()->getCallingUid(); - if (!isTrustedCallingUid(callingUid) || clientUid == AUDIO_UID_INVALID) { + if (!isAudioServerOrMediaServerUid(callingUid) || clientUid == AUDIO_UID_INVALID) { ALOGW_IF(clientUid != AUDIO_UID_INVALID && clientUid != callingUid, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid); clientUid = callingUid; @@ -599,7 +599,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ "%08X %6zu%c %6zu %c %9u%c %7u " "%08zX %08zX\n", active ? "yes" : "no", - (mClient == 0) ? getpid_cached : mClient->pid(), + (mClient == 0) ? getpid() : mClient->pid(), mSessionId, getTrackStateString(), mCblk->mFlags, @@ -1509,7 +1509,7 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, buffer, bufferSize, nullptr /* sharedBuffer */, - AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH), + AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH), mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true)) { uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) / @@ -1795,7 +1795,7 @@ void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool a "%08X %6zu %3c\n", isFastTrack() ? 'F' : ' ', active ? "yes" : "no", - (mClient == 0) ? getpid_cached : mClient->pid(), + (mClient == 0) ? getpid() : mClient->pid(), mSessionId, getTrackStateString(), mCblk->mFlags, @@ -1875,7 +1875,8 @@ AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, : RecordTrack(recordThread, NULL, audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, - buffer, bufferSize, AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH), + buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER, + flags, TYPE_PATCH), mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true)) { uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) / diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 899a790e49..d0cea6e933 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -3819,7 +3820,7 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface, bool /*forTesting*/) : - mUidCached(getuid()), + mUidCached(AID_AUDIOSERVER), // no need to call getuid(), there's only one of us running. mpClientInterface(clientInterface), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mA2dpSuspended(false), diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index c814ff91a6..008e1caf98 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -540,7 +540,7 @@ protected: static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); - uid_t mUidCached; + const uid_t mUidCached; // AID_AUDIOSERVER AudioPolicyClientInterface *mpClientInterface; // audio policy client interface sp mPrimaryOutput; // primary output descriptor // list of descriptors for outputs currently opened diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index b74bd966f1..48c4a206a1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -183,7 +183,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, Mutex::Autolock _l(mLock); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); - if (!isTrustedCallingUid(callingUid) || uid == (uid_t)-1) { + if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) { ALOGW_IF(uid != (uid_t)-1 && uid != callingUid, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid); uid = callingUid; @@ -320,7 +320,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, bool updatePid = (pid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); - if (!isTrustedCallingUid(callingUid)) { + if (!isAudioServerOrMediaServerUid(callingUid)) { ALOGW_IF(uid != (uid_t)-1 && uid != callingUid, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid); uid = callingUid; diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 5d25ea837b..65b84957d4 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -43,8 +42,6 @@ #include #include -#include - namespace android { static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; @@ -275,7 +272,7 @@ void AudioPolicyService::NotificationClient::onAudioPatchListUpdate() void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate( const String8& regId, int32_t state) { - if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) { + if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) { mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state); } } @@ -285,7 +282,7 @@ void AudioPolicyService::NotificationClient::onRecordingConfigurationUpdate( const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle) { - if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) { + if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) { mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo, clientConfig, deviceConfig, patchHandle); } @@ -577,10 +574,6 @@ void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) updateUidCache(uid, false, true); } -bool AudioPolicyService::UidPolicy::isServiceUid(uid_t uid) const { - return multiuser_get_app_id(uid) < AID_APP_START; -} - void AudioPolicyService::UidPolicy::notifyService(uid_t uid, bool active) { sp service = mService.promote(); if (service != nullptr) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 3e179c088d..7755c3b027 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -291,7 +291,6 @@ private: void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); } private: - bool isServiceUid(uid_t uid) const; void notifyService(uid_t uid, bool active); void updateOverrideUid(uid_t uid, bool active, bool insert); void updateUidCache(uid_t uid, bool active, bool insert); diff --git a/services/medialog/Android.bp b/services/medialog/Android.bp index 29e6dfcd68..ca96f622fc 100644 --- a/services/medialog/Android.bp +++ b/services/medialog/Android.bp @@ -9,7 +9,9 @@ cc_library_shared { shared_libs: [ "libaudioutils", "libbinder", + "libcutils", "liblog", + "libmediautils", "libnbaio", "libnblog", "libutils", diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index 1be5544457..e58dff7159 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "MediaLogService.h" namespace android { @@ -53,7 +53,7 @@ MediaLogService::~MediaLogService() void MediaLogService::registerWriter(const sp& shared, size_t size, const char *name) { - if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0 || + if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0 || size < kMinSize || size > kMaxSize || name == NULL || shared->size() < NBLog::Timeline::sharedSize(size)) { return; @@ -67,7 +67,7 @@ void MediaLogService::registerWriter(const sp& shared, size_t size, con void MediaLogService::unregisterWriter(const sp& shared) { - if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0) { + if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0) { return; } Mutex::Autolock _l(mLock); @@ -95,10 +95,8 @@ bool MediaLogService::dumpTryLock(Mutex& mutex) status_t MediaLogService::dump(int fd, const Vector& args __unused) { - // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp - static const String16 sDump("android.permission.DUMP"); - if (!(IPCThreadState::self()->getCallingUid() == AID_AUDIOSERVER || - PermissionCache::checkCallingPermission(sDump))) { + if (!(isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) + || dumpAllowed())) { dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n", IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); -- GitLab From b1243bf60d3a4ee226bc33bf449c4624133958a2 Mon Sep 17 00:00:00 2001 From: Mickey Keeley Date: Mon, 21 May 2018 09:08:22 -0700 Subject: [PATCH 0063/1530] libmedia_helper export headers Have libmedia_helper export libmedia_headers so that future clients won't need to explicitly include them. Bug: 80076889 Test: sdk_google_iot_x86 builds. Change-Id: Iac6e1237f10b461dbcd72370be843634aaff431d --- media/libmedia/Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index ba7003a7d1..8ff267fe4e 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -32,6 +32,9 @@ cc_library { "libaudioclient_headers", "libaudio_system_headers", ], + export_header_lib_headers: [ + "libmedia_headers", + ], clang: true, } -- GitLab From 8946a28aaaadf426ee22b098b4aec7be4d9edc00 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 19 Apr 2018 20:04:56 -0700 Subject: [PATCH 0064/1530] AudioFlinger: Fix Tee logging 1) Fix bad WAV files for tee sources that aren't 16 bit PCM. 2) Allow multiple RecordThread tees instead of one. 3) Allow dump from non-fast MixerThreads (e.g. deep buffer). 4) Parallelize tee dumping for improved concurrency; dump outside of AF lock. 5) Async dumping to allow dump to be issued in time critical code. 6) Improve file naming to distinguish record vs playback tracks, threads, and dump reason. 7) Allow Tee insertion anywhere in code with global running Tee management. 8) Increase resolution of filename time to msec avoid file overwrite. 9) Dump track data upon removal from active tracks to improve timeliness of dumped data. 10) Dump tee data on tee destruction. 11) Refactor Tee code out of AudioFlinger.cpp; minimize footprint. AudioFlinger enabling code requires Configuration.h define TEE_SINK; this is disabled, hence avoiding regression risk. Test: Enable tee log, repeatedly call dumpsys, examine files. Bug: 78369241 Change-Id: I7b22cfa7dbbc1601828de931522065a509ab4047 --- services/audioflinger/Android.mk | 4 +- services/audioflinger/AudioFlinger.cpp | 227 +--------- services/audioflinger/AudioFlinger.h | 29 +- services/audioflinger/FastMixer.cpp | 19 +- services/audioflinger/FastMixer.h | 10 +- services/audioflinger/FastMixerState.cpp | 2 +- services/audioflinger/FastMixerState.h | 3 - services/audioflinger/NBAIO_Tee.cpp | 517 +++++++++++++++++++++++ services/audioflinger/NBAIO_Tee.h | 326 ++++++++++++++ services/audioflinger/PlaybackTracks.h | 6 + services/audioflinger/Threads.cpp | 63 +-- services/audioflinger/Threads.h | 13 +- services/audioflinger/TrackBase.h | 11 +- services/audioflinger/Tracks.cpp | 35 +- 14 files changed, 928 insertions(+), 337 deletions(-) create mode 100644 services/audioflinger/NBAIO_Tee.cpp create mode 100644 services/audioflinger/NBAIO_Tee.h diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 78a62ca8dc..c0aa4777b7 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -13,7 +13,8 @@ LOCAL_SRC_FILES:= \ PatchPanel.cpp \ StateQueue.cpp \ BufLog.cpp \ - TypedLogger.cpp + TypedLogger.cpp \ + NBAIO_Tee.cpp \ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ @@ -41,6 +42,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libcpustats \ + libsndfile \ LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index f165c319e9..9d1142fa97 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -47,6 +47,7 @@ #include #include "AudioFlinger.h" +#include "NBAIO_Tee.h" #include @@ -55,7 +56,6 @@ #include #include -#include #include @@ -100,17 +100,6 @@ nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs; uint32_t AudioFlinger::mScreenState; - -#ifdef TEE_SINK -bool AudioFlinger::mTeeSinkInputEnabled = false; -bool AudioFlinger::mTeeSinkOutputEnabled = false; -bool AudioFlinger::mTeeSinkTrackEnabled = false; - -size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault; -size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault; -size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault; -#endif - // In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off // we define a minimum time during which a global effect is considered enabled. static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200); @@ -186,27 +175,6 @@ AudioFlinger::AudioFlinger() mEffectsFactoryHal = EffectsFactoryHalInterface::create(); mMediaLogNotifier->run("MediaLogNotifier"); - -#ifdef TEE_SINK - char value[PROPERTY_VALUE_MAX]; - (void) property_get("ro.debuggable", value, "0"); - int debuggable = atoi(value); - int teeEnabled = 0; - if (debuggable) { - (void) property_get("af.tee", value, "0"); - teeEnabled = atoi(value); - } - // FIXME symbolic constants here - if (teeEnabled & 1) { - mTeeSinkInputEnabled = true; - } - if (teeEnabled & 2) { - mTeeSinkOutputEnabled = true; - } - if (teeEnabled & 4) { - mTeeSinkTrackEnabled = true; - } -#endif } void AudioFlinger::onFirstRef() @@ -535,19 +503,16 @@ status_t AudioFlinger::dump(int fd, const Vector& args) mPatchPanel.dump(fd); -#ifdef TEE_SINK - // dump the serially shared record tee sink - if (mRecordTeeSource != 0) { - dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C'); - } -#endif - BUFLOG_RESET; if (locked) { mLock.unlock(); } +#ifdef TEE_SINK + // NBAIO_Tee dump is safe to call outside of AF lock. + NBAIO_Tee::dumpAll(fd, "_DUMP"); +#endif // append a copy of media.log here by forwarding fd to it, but don't attempt // to lookup the service if it's not running, as it will block for a second if (sMediaLogServiceAsBinder != 0) { @@ -2430,55 +2395,6 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod thread.get()); return thread; } else { -#ifdef TEE_SINK - // Try to re-use most recently used Pipe to archive a copy of input for dumpsys, - // or (re-)create if current Pipe is idle and does not match the new format - sp teeSink; - enum { - TEE_SINK_NO, // don't copy input - TEE_SINK_NEW, // copy input using a new pipe - TEE_SINK_OLD, // copy input using an existing pipe - } kind; - NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate, - audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format); - if (!mTeeSinkInputEnabled) { - kind = TEE_SINK_NO; - } else if (!Format_isValid(format)) { - kind = TEE_SINK_NO; - } else if (mRecordTeeSink == 0) { - kind = TEE_SINK_NEW; - } else if (mRecordTeeSink->getStrongCount() != 1) { - kind = TEE_SINK_NO; - } else if (Format_isEqual(format, mRecordTeeSink->format())) { - kind = TEE_SINK_OLD; - } else { - kind = TEE_SINK_NEW; - } - switch (kind) { - case TEE_SINK_NEW: { - Pipe *pipe = new Pipe(mTeeSinkInputFrames, format); - size_t numCounterOffers = 0; - const NBAIO_Format offers[1] = {format}; - ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - PipeReader *pipeReader = new PipeReader(*pipe); - numCounterOffers = 0; - index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - mRecordTeeSink = pipe; - mRecordTeeSource = pipeReader; - teeSink = pipe; - } - break; - case TEE_SINK_OLD: - teeSink = mRecordTeeSink; - break; - case TEE_SINK_NO: - default: - break; - } -#endif - // Start record thread // RecordThread requires both input and output device indication to forward to audio // pre processing modules @@ -2488,9 +2404,6 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod primaryOutputDevice_l(), devices, mSystemReady -#ifdef TEE_SINK - , teeSink -#endif ); mRecordThreads.add(*input, thread); ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get()); @@ -3393,136 +3306,6 @@ bool AudioFlinger::updateOrphanEffectChains(const sp } -struct Entry { -#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21 - char mFileName[TEE_MAX_FILENAME]; -}; - -int comparEntry(const void *p1, const void *p2) -{ - return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName); -} - -#ifdef TEE_SINK -void AudioFlinger::dumpTee(int fd, const sp& source, audio_io_handle_t id, char suffix) -{ - NBAIO_Source *teeSource = source.get(); - if (teeSource != NULL) { - // .wav rotation - // There is a benign race condition if 2 threads call this simultaneously. - // They would both traverse the directory, but the result would simply be - // failures at unlink() which are ignored. It's also unlikely since - // normally dumpsys is only done by bugreport or from the command line. - char teePath[PATH_MAX] = "/data/misc/audioserver"; - size_t teePathLen = strlen(teePath); - DIR *dir = opendir(teePath); - teePath[teePathLen++] = '/'; - if (dir != NULL) { -#define TEE_MAX_SORT 20 // number of entries to sort -#define TEE_MAX_KEEP 10 // number of entries to keep - struct Entry entries[TEE_MAX_SORT]; - size_t entryCount = 0; - while (entryCount < TEE_MAX_SORT) { - errno = 0; // clear errno before readdir() to track potential errors. - const struct dirent *result = readdir(dir); - if (result == nullptr) { - ALOGW_IF(errno != 0, "tee readdir() failure %s", strerror(errno)); - break; - } - // ignore non .wav file entries - const size_t nameLen = strlen(result->d_name); - if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME || - strcmp(&result->d_name[nameLen - 4], ".wav")) { - continue; - } - (void)audio_utils_strlcpy(entries[entryCount++].mFileName, result->d_name); - } - (void) closedir(dir); - if (entryCount > TEE_MAX_KEEP) { - qsort(entries, entryCount, sizeof(Entry), comparEntry); - for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) { - strcpy(&teePath[teePathLen], entries[i].mFileName); - (void) unlink(teePath); - } - } - } else { - if (fd >= 0) { - dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath, - strerror(errno)); - } - } - char teeTime[16]; - struct timeval tv; - gettimeofday(&tv, NULL); - struct tm tm; - localtime_r(&tv.tv_sec, &tm); - strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm); - snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id, - suffix); - // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd - int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR); - if (teeFd >= 0) { - // FIXME use libsndfile - char wavHeader[44]; - memcpy(wavHeader, - "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0", - sizeof(wavHeader)); - NBAIO_Format format = teeSource->format(); - unsigned channelCount = Format_channelCount(format); - uint32_t sampleRate = Format_sampleRate(format); - size_t frameSize = Format_frameSize(format); - wavHeader[22] = channelCount; // number of channels - wavHeader[24] = sampleRate; // sample rate - wavHeader[25] = sampleRate >> 8; - wavHeader[32] = frameSize; // block alignment - wavHeader[33] = frameSize >> 8; - write(teeFd, wavHeader, sizeof(wavHeader)); - size_t total = 0; - bool firstRead = true; -#define TEE_SINK_READ 1024 // frames per I/O operation - void *buffer = malloc(TEE_SINK_READ * frameSize); - for (;;) { - size_t count = TEE_SINK_READ; - ssize_t actual = teeSource->read(buffer, count); - bool wasFirstRead = firstRead; - firstRead = false; - if (actual <= 0) { - if (actual == (ssize_t) OVERRUN && wasFirstRead) { - continue; - } - break; - } - ALOG_ASSERT(actual <= (ssize_t)count); - write(teeFd, buffer, actual * frameSize); - total += actual; - } - free(buffer); - lseek(teeFd, (off_t) 4, SEEK_SET); - uint32_t temp = 44 + total * frameSize - 8; - // FIXME not big-endian safe - write(teeFd, &temp, sizeof(temp)); - lseek(teeFd, (off_t) 40, SEEK_SET); - temp = total * frameSize; - // FIXME not big-endian safe - write(teeFd, &temp, sizeof(temp)); - close(teeFd); - // TODO Should create file with temporary name and then rename to final if non-empty. - if (total > 0) { - if (fd >= 0) { - dprintf(fd, "tee copied to %s\n", teePath); - } - } else { - unlink(teePath); - } - } else { - if (fd >= 0) { - dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); - } - } - } -} -#endif - // ---------------------------------------------------------------------------- status_t AudioFlinger::onTransact( diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 7cfe542412..a59c13ee1b 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -71,6 +71,7 @@ #include "AudioStreamOut.h" #include "SpdifStreamOut.h" #include "AudioHwDevice.h" +#include "NBAIO_Tee.h" #include @@ -800,35 +801,7 @@ private: void filterReservedParameters(String8& keyValuePairs, uid_t callingUid); -#ifdef TEE_SINK - // all record threads serially share a common tee sink, which is re-created on format change - sp mRecordTeeSink; - sp mRecordTeeSource; -#endif - public: - -#ifdef TEE_SINK - // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file - static void dumpTee(int fd, const sp& source, audio_io_handle_t id, char suffix); - - // whether tee sink is enabled by property - static bool mTeeSinkInputEnabled; - static bool mTeeSinkOutputEnabled; - static bool mTeeSinkTrackEnabled; - - // runtime configured size of each tee sink pipe, in frames - static size_t mTeeSinkInputFrames; - static size_t mTeeSinkOutputFrames; - static size_t mTeeSinkTrackFrames; - - // compile-time default size of tee sink pipes, in frames - // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes - static const size_t kTeeSinkInputFramesDefault = 0x200000; - static const size_t kTeeSinkOutputFramesDefault = 0x200000; - static const size_t kTeeSinkTrackFramesDefault = 0x200000; -#endif - // These methods read variables atomically without mLock, // though the variables are updated with mLock. bool isLowRamDevice() const { return mIsLowRamDevice; } diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 79bb9fe039..7a029637de 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -47,7 +47,8 @@ namespace android { /*static*/ const FastMixerState FastMixer::sInitial; -FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"), +FastMixer::FastMixer(audio_io_handle_t parentIoHandle) + : FastThread("cycle_ms", "load_us"), // mFastTrackNames // mGenerations mOutputSink(NULL), @@ -66,8 +67,11 @@ FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"), mTotalNativeFramesWritten(0), // timestamp mNativeFramesWrittenButNotPresented(0), // the = 0 is to silence the compiler - mMasterMono(false) + mMasterMono(false), + mThreadIoHandle(parentIoHandle) { + (void)mThreadIoHandle; // prevent unused warning, see C++17 [[maybe_unused]] + // FIXME pass sInitial as parameter to base class constructor, and make it static local mPrevious = &sInitial; mCurrent = &sInitial; @@ -220,6 +224,10 @@ void FastMixer::onStateChange() previousTrackMask = 0; mFastTracksGen = current->mFastTracksGen - 1; dumpState->mFrameCount = frameCount; +#ifdef TEE_SINK + mTee.set(mFormat, NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD); + mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_F"); +#endif } else { previousTrackMask = previous->mTrackMask; } @@ -446,10 +454,9 @@ void FastMixer::onWork() frameCount * Format_channelCount(mFormat)); } // if non-NULL, then duplicate write() to this non-blocking sink - NBAIO_Sink* teeSink; - if ((teeSink = current->mTeeSink) != NULL) { - (void) teeSink->write(buffer, frameCount); - } +#ifdef TEE_SINK + mTee.write(buffer, frameCount); +#endif // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 235d23faf9..1c86d9ae7d 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -22,6 +22,7 @@ #include "StateQueue.h" #include "FastMixerState.h" #include "FastMixerDumpState.h" +#include "NBAIO_Tee.h" namespace android { @@ -32,7 +33,9 @@ typedef StateQueue FastMixerStateQueue; class FastMixer : public FastThread { public: - FastMixer(); + /** FastMixer constructor takes as param the parent MixerThread's io handle (id) + for purposes of identification. */ + explicit FastMixer(audio_io_handle_t threadIoHandle); virtual ~FastMixer(); FastMixerStateQueue* sq(); @@ -87,6 +90,11 @@ private: // accessed without lock between multiple threads. std::atomic_bool mMasterMono; std::atomic_int_fast64_t mBoottimeOffset; + + const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes +#ifdef TEE_SINK + NBAIO_Tee mTee; +#endif }; // class FastMixer } // namespace android diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 36d8eef543..b98842d4a7 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -35,7 +35,7 @@ FastTrack::~FastTrack() FastMixerState::FastMixerState() : FastThreadState(), // mFastTracks mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0), - mFrameCount(0), mTeeSink(NULL) + mFrameCount(0) { int ok = pthread_once(&sMaxFastTracksOnce, sMaxFastTracksInit); if (ok != 0) { diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 2be1e91930..c7fcbd83ce 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -77,9 +77,6 @@ struct FastMixerState : FastThreadState { WRITE = 0x10, // write to output sink MIX_WRITE = 0x18; // mix tracks and write to output sink - // This might be a one-time configuration rather than per-state - NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink - // never returns NULL; asserts if command is invalid static const char *commandToString(Command command); diff --git a/services/audioflinger/NBAIO_Tee.cpp b/services/audioflinger/NBAIO_Tee.cpp new file mode 100644 index 0000000000..53083d5036 --- /dev/null +++ b/services/audioflinger/NBAIO_Tee.cpp @@ -0,0 +1,517 @@ +/* + * 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 "NBAIO_Tee" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Configuration.h" +#include "NBAIO_Tee.h" + +// Enabled with TEE_SINK in Configuration.h +#ifdef TEE_SINK + +namespace android { + +/* + Tee filenames generated as follows: + + "aftee_Date_ThreadId_C_reason.wav" RecordThread + "aftee_Date_ThreadId_M_reason.wav" MixerThread (Normal) + "aftee_Date_ThreadId_F_reason.wav" MixerThread (Fast) + "aftee_Date_ThreadId_TrackId_R_reason.wav" RecordTrack + "aftee_Date_ThreadId_TrackId_TrackName_T_reason.wav" PlaybackTrack + + where Date = YYYYmmdd_HHMMSS_MSEC + + where Reason = [ DTOR | DUMP | REMOVE ] + + Examples: + aftee_20180424_153811_038_13_57_2_T_REMOVE.wav + aftee_20180424_153811_218_13_57_2_T_REMOVE.wav + aftee_20180424_153811_378_13_57_2_T_REMOVE.wav + aftee_20180424_153825_147_62_C_DUMP.wav + aftee_20180424_153825_148_62_59_R_DUMP.wav + aftee_20180424_153825_149_13_F_DUMP.wav + aftee_20180424_153842_125_62_59_R_REMOVE.wav + aftee_20180424_153842_168_62_C_DTOR.wav +*/ + +static constexpr char DEFAULT_PREFIX[] = "aftee_"; +static constexpr char DEFAULT_DIRECTORY[] = "/data/misc/audioserver"; +static constexpr size_t DEFAULT_THREADPOOL_SIZE = 8; + +/** AudioFileHandler manages temporary audio wav files with a least recently created + retention policy. + + The temporary filenames are systematically generated. A common filename prefix, + storage directory, and concurrency pool are passed in on creating the object. + + Temporary files are created by "create", which returns a filename generated by + + prefix + 14 char date + suffix + + TODO Move to audio_utils. + TODO Avoid pointing two AudioFileHandlers to the same directory and prefix + as we don't have a prefix specific lock file. */ + +class AudioFileHandler { +public: + + AudioFileHandler(const std::string &prefix, const std::string &directory, size_t pool) + : mThreadPool(pool) + , mPrefix(prefix) + { + (void)setDirectory(directory); + } + + /** returns filename of created audio file, else empty string on failure. */ + std::string create( + std::function reader, + uint32_t sampleRate, + uint32_t channelCount, + audio_format_t format, + const std::string &suffix); + +private: + /** sets the current directory. this is currently private to avoid confusion + when changing while pending operations are occurring (it's okay, but + weakly synchronized). */ + status_t setDirectory(const std::string &directory); + + /** cleans current directory and returns the directory name done. */ + status_t clean(std::string *dir = nullptr); + + /** creates an audio file from a reader functor passed in. */ + status_t createInternal( + std::function reader, + uint32_t sampleRate, + uint32_t channelCount, + audio_format_t format, + const std::string &filename); + + static bool isDirectoryValid(const std::string &directory) { + return directory.size() > 0 && directory[0] == '/'; + } + + std::string generateFilename(const std::string &suffix) const { + char fileTime[sizeof("YYYYmmdd_HHMMSS_\0")]; + struct timeval tv; + gettimeofday(&tv, NULL); + struct tm tm; + localtime_r(&tv.tv_sec, &tm); + LOG_ALWAYS_FATAL_IF(strftime(fileTime, sizeof(fileTime), "%Y%m%d_%H%M%S_", &tm) == 0, + "incorrect fileTime buffer"); + char msec[4]; + (void)snprintf(msec, sizeof(msec), "%03d", (int)(tv.tv_usec / 1000)); + return mPrefix + fileTime + msec + suffix + ".wav"; + } + + bool isManagedFilename(const char *name) { + constexpr size_t FILENAME_LEN_DATE = 4 + 2 + 2 // %Y%m%d% + + 1 + 2 + 2 + 2 // _H%M%S + + 1 + 3; //_MSEC + const size_t prefixLen = mPrefix.size(); + const size_t nameLen = strlen(name); + + // reject on size, prefix, and .wav + if (nameLen < prefixLen + FILENAME_LEN_DATE + 4 /* .wav */ + || strncmp(name, mPrefix.c_str(), prefixLen) != 0 + || strcmp(name + nameLen - 4, ".wav") != 0) { + return false; + } + + // validate date portion + const char *date = name + prefixLen; + return std::all_of(date, date + 8, isdigit) + && date[8] == '_' + && std::all_of(date + 9, date + 15, isdigit) + && date[15] == '_' + && std::all_of(date + 16, date + 19, isdigit); + } + + // yet another ThreadPool implementation. + class ThreadPool { + public: + ThreadPool(size_t size) + : mThreadPoolSize(size) + { } + + /** launches task "name" with associated function "func". + if the threadpool is exhausted, it will launch on calling function */ + status_t launch(const std::string &name, std::function func); + + private: + std::mutex mLock; + std::list>> mFutures; // GUARDED_BY(mLock) + + const size_t mThreadPoolSize; + } mThreadPool; + + const std::string mPrefix; + std::mutex mLock; + std::string mDirectory; // GUARDED_BY(mLock) + std::deque mFiles; // GUARDED_BY(mLock) sorted list of files by creation time + + static constexpr size_t FRAMES_PER_READ = 1024; + static constexpr size_t MAX_FILES_READ = 1024; + static constexpr size_t MAX_FILES_KEEP = 32; +}; + +/* static */ +void NBAIO_Tee::NBAIO_TeeImpl::dumpTee( + int fd, const NBAIO_SinkSource &sinkSource, const std::string &suffix) +{ + // Singleton. Constructed thread-safe on first call, never destroyed. + static AudioFileHandler audioFileHandler( + DEFAULT_PREFIX, DEFAULT_DIRECTORY, DEFAULT_THREADPOOL_SIZE); + + auto &source = sinkSource.second; + if (source.get() == nullptr) { + return; + } + + const NBAIO_Format format = source->format(); + bool firstRead = true; + std::string filename = audioFileHandler.create( + // this functor must not hold references to stack + [firstRead, sinkSource] (void *buffer, size_t frames) mutable { + auto &source = sinkSource.second; + ssize_t actualRead = source->read(buffer, frames); + if (actualRead == (ssize_t)OVERRUN && firstRead) { + // recheck once + actualRead = source->read(buffer, frames); + } + firstRead = false; + return actualRead; + }, + Format_sampleRate(format), + Format_channelCount(format), + format.mFormat, + suffix); + + if (fd >= 0 && filename.size() > 0) { + dprintf(fd, "tee wrote to %s\n", filename.c_str()); + } +} + +/* static */ +NBAIO_Tee::NBAIO_TeeImpl::NBAIO_SinkSource NBAIO_Tee::NBAIO_TeeImpl::makeSinkSource( + const NBAIO_Format &format, size_t frames, bool *enabled) +{ + if (Format_isValid(format) && audio_is_linear_pcm(format.mFormat)) { + Pipe *pipe = new Pipe(frames, format); + size_t numCounterOffers = 0; + const NBAIO_Format offers[1] = {format}; + ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); + if (index != 0) { + ALOGW("pipe failure to negotiate: %zd", index); + goto exit; + } + PipeReader *pipeReader = new PipeReader(*pipe); + numCounterOffers = 0; + index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); + if (index != 0) { + ALOGW("pipeReader failure to negotiate: %zd", index); + goto exit; + } + if (enabled != nullptr) *enabled = true; + return {pipe, pipeReader}; + } +exit: + if (enabled != nullptr) *enabled = false; + return {nullptr, nullptr}; +} + +std::string AudioFileHandler::create( + std::function reader, + uint32_t sampleRate, + uint32_t channelCount, + audio_format_t format, + const std::string &suffix) +{ + const std::string filename = generateFilename(suffix); + + if (mThreadPool.launch(std::string("create ") + filename, + [=]() { return createInternal(reader, sampleRate, channelCount, format, filename); }) + == NO_ERROR) { + return filename; + } + return ""; +} + +status_t AudioFileHandler::setDirectory(const std::string &directory) +{ + if (!isDirectoryValid(directory)) return BAD_VALUE; + + // TODO: consider using std::filesystem in C++17 + DIR *dir = opendir(directory.c_str()); + + if (dir == nullptr) { + ALOGW("%s: cannot open directory %s", __func__, directory.c_str()); + return BAD_VALUE; + } + + size_t toRemove = 0; + decltype(mFiles) files; + + while (files.size() < MAX_FILES_READ) { + errno = 0; + const struct dirent *result = readdir(dir); + if (result == nullptr) { + ALOGW_IF(errno != 0, "%s: readdir failure %s", __func__, strerror(errno)); + break; + } + // is it a managed filename? + if (!isManagedFilename(result->d_name)) { + continue; + } + files.emplace_back(result->d_name); + } + (void)closedir(dir); + + // OPTIMIZATION: we don't need to stat each file, the filenames names are + // already (roughly) ordered by creation date. we use std::deque instead + // of std::set for faster insertion and sorting times. + + if (files.size() > MAX_FILES_KEEP) { + // removed files can use a partition (no need to do a full sort). + toRemove = files.size() - MAX_FILES_KEEP; + std::nth_element(files.begin(), files.begin() + toRemove - 1, files.end()); + } + + // kept files must be sorted. + std::sort(files.begin() + toRemove, files.end()); + + { + std::lock_guard _l(mLock); + + mDirectory = directory; + mFiles = std::move(files); + } + + if (toRemove > 0) { // launch a clean in background. + (void)mThreadPool.launch( + std::string("cleaning ") + directory, [this]() { return clean(); }); + } + return NO_ERROR; +} + +status_t AudioFileHandler::clean(std::string *directory) +{ + std::vector filesToRemove; + std::string dir; + { + std::lock_guard _l(mLock); + + if (!isDirectoryValid(mDirectory)) return NO_INIT; + + dir = mDirectory; + if (mFiles.size() > MAX_FILES_KEEP) { + size_t toRemove = mFiles.size() - MAX_FILES_KEEP; + + // use move and erase to efficiently transfer std::string + std::move(mFiles.begin(), + mFiles.begin() + toRemove, + std::back_inserter(filesToRemove)); + mFiles.erase(mFiles.begin(), mFiles.begin() + toRemove); + } + } + + std::string dirp = dir + "/"; + // remove files outside of lock for better concurrency. + for (const auto &file : filesToRemove) { + (void)unlink((dirp + file).c_str()); + } + + // return the directory if requested. + if (directory != nullptr) { + *directory = dir; + } + return NO_ERROR; +} + +status_t AudioFileHandler::ThreadPool::launch( + const std::string &name, std::function func) +{ + if (mThreadPoolSize > 1) { + std::lock_guard _l(mLock); + if (mFutures.size() >= mThreadPoolSize) { + for (auto it = mFutures.begin(); it != mFutures.end();) { + const std::string &filename = it->first; + std::future &future = it->second; + if (!future.valid() || + future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + ALOGV("%s: future %s ready", __func__, filename.c_str()); + it = mFutures.erase(it); + } else { + ALOGV("%s: future %s not ready", __func__, filename.c_str()); + ++it; + } + } + } + if (mFutures.size() < mThreadPoolSize) { + ALOGV("%s: deferred calling %s", __func__, name.c_str()); + mFutures.emplace_back(name, std::async(std::launch::async, func)); + return NO_ERROR; + } + } + ALOGV("%s: immediate calling %s", __func__, name.c_str()); + return func(); +} + +status_t AudioFileHandler::createInternal( + std::function reader, + uint32_t sampleRate, + uint32_t channelCount, + audio_format_t format, + const std::string &filename) +{ + // Attempt to choose the best matching file format. + // We can choose any sf_format + // but writeFormat must be one of 16, 32, float + // due to sf_writef compatibility. + int sf_format; + audio_format_t writeFormat; + switch (format) { + case AUDIO_FORMAT_PCM_8_BIT: + case AUDIO_FORMAT_PCM_16_BIT: + sf_format = SF_FORMAT_PCM_16; + writeFormat = AUDIO_FORMAT_PCM_16_BIT; + ALOGV("%s: %s using PCM_16 for format %#x", __func__, filename.c_str(), format); + break; + case AUDIO_FORMAT_PCM_8_24_BIT: + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + case AUDIO_FORMAT_PCM_32_BIT: + sf_format = SF_FORMAT_PCM_32; + writeFormat = AUDIO_FORMAT_PCM_32_BIT; + ALOGV("%s: %s using PCM_32 for format %#x", __func__, filename.c_str(), format); + break; + case AUDIO_FORMAT_PCM_FLOAT: + sf_format = SF_FORMAT_FLOAT; + writeFormat = AUDIO_FORMAT_PCM_FLOAT; + ALOGV("%s: %s using PCM_FLOAT for format %#x", __func__, filename.c_str(), format); + break; + default: + // TODO: + // handle audio_has_proportional_frames() formats. + // handle compressed formats as single byte files. + return BAD_VALUE; + } + + std::string directory; + status_t status = clean(&directory); + if (status != NO_ERROR) return status; + std::string dirPrefix = directory + "/"; + + const std::string path = dirPrefix + filename; + + /* const */ SF_INFO info = { + .frames = 0, + .samplerate = (int)sampleRate, + .channels = (int)channelCount, + .format = SF_FORMAT_WAV | sf_format, + }; + SNDFILE *sf = sf_open(path.c_str(), SFM_WRITE, &info); + if (sf == nullptr) { + return INVALID_OPERATION; + } + + size_t total = 0; + void *buffer = malloc(FRAMES_PER_READ * std::max( + channelCount * audio_bytes_per_sample(writeFormat), //output framesize + channelCount * audio_bytes_per_sample(format))); // input framesize + if (buffer == nullptr) { + sf_close(sf); + return NO_MEMORY; + } + + for (;;) { + const ssize_t actualRead = reader(buffer, FRAMES_PER_READ); + if (actualRead <= 0) { + break; + } + + // Convert input format to writeFormat as needed. + if (format != writeFormat) { + memcpy_by_audio_format( + buffer, writeFormat, buffer, format, actualRead * info.channels); + } + + ssize_t reallyWritten; + switch (writeFormat) { + case AUDIO_FORMAT_PCM_16_BIT: + reallyWritten = sf_writef_short(sf, (const int16_t *)buffer, actualRead); + break; + case AUDIO_FORMAT_PCM_32_BIT: + reallyWritten = sf_writef_int(sf, (const int32_t *)buffer, actualRead); + break; + case AUDIO_FORMAT_PCM_FLOAT: + reallyWritten = sf_writef_float(sf, (const float *)buffer, actualRead); + break; + default: + LOG_ALWAYS_FATAL("%s: %s writeFormat: %#x", __func__, filename.c_str(), writeFormat); + break; + } + + if (reallyWritten < 0) { + ALOGW("%s: %s write error: %zd", __func__, filename.c_str(), reallyWritten); + break; + } + total += reallyWritten; + if (reallyWritten < actualRead) { + ALOGW("%s: %s write short count: %zd < %zd", + __func__, filename.c_str(), reallyWritten, actualRead); + break; + } + } + sf_close(sf); + free(buffer); + if (total == 0) { + (void)unlink(path.c_str()); + return NOT_ENOUGH_DATA; + } + + // Success: add our name to managed files. + { + std::lock_guard _l(mLock); + // weak synchronization - only update mFiles if the directory hasn't changed. + if (mDirectory == directory) { + mFiles.emplace_back(filename); // add to the end to preserve sort. + } + } + return NO_ERROR; // return full path +} + +} // namespace android + +#endif // TEE_SINK diff --git a/services/audioflinger/NBAIO_Tee.h b/services/audioflinger/NBAIO_Tee.h new file mode 100644 index 0000000000..fed8cc81c8 --- /dev/null +++ b/services/audioflinger/NBAIO_Tee.h @@ -0,0 +1,326 @@ +/* + * 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. + */ + +// Enabled with TEE_SINK in Configuration.h +#ifndef ANDROID_NBAIO_TEE_H +#define ANDROID_NBAIO_TEE_H + +#ifdef TEE_SINK + +#include +#include +#include + +#include +#include + +namespace android { + +/** + * The NBAIO_Tee uses the NBAIO Pipe and PipeReader for nonblocking + * data collection, for eventual dump to log files. + * See https://source.android.com/devices/audio/debugging for how to + * enable by ro.debuggable and af.tee properties. + * + * The write() into the NBAIO_Tee is therefore nonblocking, + * but changing NBAIO_Tee formats with set() cannot be done during a write(); + * usually the caller already implements this mutual exclusion. + * + * All other calls except set() vs write() may occur at any time. + * + * dump() disruption is minimized to the caller since system calls are executed + * in an asynchronous thread (when possible). + * + * Currently the NBAIO_Tee is "hardwired" for AudioFlinger support. + * + * Some AudioFlinger specific notes: + * + * 1) Tees capture only linear PCM data. + * 2) Tees without any data written are considered empty and do not generate + * any output files. + * 2) Once a Tee dumps data, it is considered "emptied" and new data + * needs to be written before another Tee file is generated. + * 3) Tee file format is + * WAV integer PCM 16 bit for AUDIO_FORMAT_PCM_8_BIT, AUDIO_FORMAT_PCM_16_BIT. + * WAV integer PCM 32 bit for AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED + * AUDIO_FORMAT_PCM_32_BIT. + * WAV float PCM 32 bit for AUDIO_FORMAT_PCM_FLOAT. + * + * Input_Thread: + * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord + * client. + * + * Output_Thread: + * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the + * NormalMixer output (if no FastMixer). + * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack + * or on the upstream playback Tracks. + * 3) DirectThreads and OffloadThreads do not tee any data. The upstream track + * (if linear PCM format) may be teed to discover data. + * 4) MmapThreads are not supported. + * + * Tracks: + * 1) RecordTracks and playback Tracks tee as data is being written to or + * read from the shared client-server track buffer by the associated Threads. + * 2) The mechanism is on the AudioBufferProvider release() so large static Track + * playback may not show any Tee data depending on when it is released. + * 3) When a track becomes inactive, the Thread will trigger a dump. + */ + +class NBAIO_Tee { +public: + /* TEE_FLAG is used in set() and must match the flags for the af.tee property + given in https://source.android.com/devices/audio/debugging + */ + enum TEE_FLAG { + TEE_FLAG_NONE = 0, + TEE_FLAG_INPUT_THREAD = (1 << 0), // treat as a Tee for input (Capture) Threads + TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads + TEE_FLAG_TRACK = (1 << 2), // treat as a Tee for tracks (Record and Playback) + }; + + NBAIO_Tee() + : mTee(std::make_shared()) + { + getRunningTees().add(mTee); + } + + ~NBAIO_Tee() { + getRunningTees().remove(mTee); + dump(-1, "_DTOR"); // log any data remaining in Tee. + } + + /** + * \brief set is used for deferred configuration of Tee. + * + * May be called anytime except concurrently with write(). + * + * \param format NBAIO_Format used to open NBAIO pipes + * \param flags (https://source.android.com/devices/audio/debugging) + * - TEE_FLAG_NONE to bypass af.tee property checks (default); + * - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set; + * - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set; + * - TEE_FLAG_TRACK to check af.tee if track logging set. + * \param frames number of frames to open the NBAIO pipe (set to 0 to use default). + * + * \return + * - NO_ERROR on success (or format unchanged) + * - BAD_VALUE if format or flags invalid. + * - PERMISSION_DENIED if flags not allowed by af.tee + */ + + status_t set(const NBAIO_Format &format, + TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const { + return mTee->set(format, flags, frames); + } + + status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format, + TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const { + return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames); + } + + /** + * \brief write data to the tee. + * + * This call is lock free (as shared pointer and NBAIO is lock free); + * may be called simultaneous to all methods except set(). + * + * \param buffer to write to pipe. + * \param frameCount in frames as specified by the format passed to set() + */ + + void write(const void *buffer, size_t frameCount) const { + mTee->write(buffer, frameCount); + } + + /** sets Tee id string which identifies the generated file (should be unique). */ + void setId(const std::string &id) const { + mTee->setId(id); + } + + /** + * \brief dump the audio content written to the Tee. + * + * \param fd file descriptor to write dumped filename for logging, use -1 to ignore. + * \param reason string suffix to append to the generated file. + */ + void dump(int fd, const std::string &reason = "") const { + mTee->dump(fd, reason); + } + + /** + * \brief dump all Tees currently alive. + * + * \param fd file descriptor to write dumped filename for logging, use -1 to ignore. + * \param reason string suffix to append to the generated file. + */ + static void dumpAll(int fd, const std::string &reason = "") { + getRunningTees().dump(fd, reason); + } + +private: + + /** The underlying implementation of the Tee - the lifetime is through + a shared pointer so destruction of the NBAIO_Tee container may proceed + even though dumping is occurring. */ + class NBAIO_TeeImpl { + public: + status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) { + static const int teeConfig = property_get_bool("ro.debuggable", false) + ? property_get_int32("af.tee", 0) : 0; + + // check the type of Tee + const TEE_FLAG type = TEE_FLAG( + flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK)); + + // parameter flags can't select multiple types. + if (__builtin_popcount(type) > 1) { + return BAD_VALUE; + } + + // if type is set, we check to see if it is permitted by configuration. + if (type != 0 && (type & teeConfig) == 0) { + return PERMISSION_DENIED; + } + + // determine number of frames for Tee + if (frames == 0) { + // TODO: consider varying frame count based on type. + frames = DEFAULT_TEE_FRAMES; + } + + // TODO: should we check minimum number of frames? + + // don't do anything if format and frames are the same. + if (Format_isEqual(format, mFormat) && frames == mFrames) { + return NO_ERROR; + } + + bool enabled = false; + auto sinksource = makeSinkSource(format, frames, &enabled); + + // enabled is set if makeSinkSource is successful. + // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is + // ongoing. + if (enabled) { + std::lock_guard _l(mLock); + mFlags = flags; + mFormat = format; // could get this from the Sink. + mFrames = frames; + mSinkSource = std::move(sinksource); + mEnabled.store(true); + return NO_ERROR; + } + return BAD_VALUE; + } + + void setId(const std::string &id) { + std::lock_guard _l(mLock); + mId = id; + } + + void dump(int fd, const std::string &reason) { + if (!mDataReady.exchange(false)) return; + std::string suffix; + NBAIO_SinkSource sinkSource; + { + std::lock_guard _l(mLock); + suffix = mId + reason; + sinkSource = mSinkSource; + } + dumpTee(fd, sinkSource, suffix); + } + + void write(const void *buffer, size_t frameCount) { + if (!mEnabled.load() || frameCount == 0) return; + (void)mSinkSource.first->write(buffer, frameCount); + mDataReady.store(true); + } + + private: + // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time + // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe. + using NBAIO_SinkSource = std::pair, sp>; + + static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix); + + static NBAIO_SinkSource makeSinkSource( + const NBAIO_Format &format, size_t frames, bool *enabled); + + // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes + static constexpr size_t DEFAULT_TEE_FRAMES = 0x200000; + + // atomic status checking + std::atomic mEnabled{false}; + std::atomic mDataReady{false}; + + // locked dump information + mutable std::mutex mLock; + std::string mId; // GUARDED_BY(mLock) + TEE_FLAG mFlags = TEE_FLAG_NONE; // GUARDED_BY(mLock) + NBAIO_Format mFormat = Format_Invalid; // GUARDED_BY(mLock) + size_t mFrames = 0; // GUARDED_BY(mLock) + NBAIO_SinkSource mSinkSource; // GUARDED_BY(mLock) + }; + + /** RunningTees tracks current running tees for dump purposes. + It is implemented to have minimal locked regions, to be transparent to the caller. */ + class RunningTees { + public: + void add(const std::shared_ptr &tee) { + std::lock_guard _l(mLock); + ALOGW_IF(!mTees.emplace(tee).second, + "%s: %p already exists in mTees", __func__, tee.get()); + } + + void remove(const std::shared_ptr &tee) { + std::lock_guard _l(mLock); + ALOGW_IF(mTees.erase(tee) != 1, + "%s: %p doesn't exist in mTees", __func__, tee.get()); + } + + void dump(int fd, const std::string &reason) { + std::vector> tees; // safe snapshot of tees + { + std::lock_guard _l(mLock); + tees.insert(tees.end(), mTees.begin(), mTees.end()); + } + for (const auto &tee : tees) { + tee->dump(fd, reason); + } + } + + private: + std::mutex mLock; + std::set> mTees; // GUARDED_BY(mLock) + }; + + // singleton + static RunningTees &getRunningTees() { + static RunningTees runningTees; + return runningTees; + } + + // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if + // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee(). + // This is allowed for maximum concurrency. + const std::shared_ptr mTee; +}; // NBAIO_Tee + +} // namespace android + +#endif // TEE_SINK +#endif // !ANDROID_NBAIO_TEE_H diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index a78be99294..ff9444da56 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -56,6 +56,12 @@ public: LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0, "%s both old name %d and new name %d are valid", __func__, mName, name); mName = name; +#ifdef TEE_SINK + mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + + "_" + std::to_string(mId) + + "_" + std::to_string(mName) + + "_T"); +#endif } virtual uint32_t sampleRate() const; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 7b5d9e640a..c1b091227c 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1579,6 +1579,9 @@ ssize_t AudioFlinger::ThreadBase::ActiveTracks::remove(const sp &track) { --mBatteryCounter[track->uid()].second; // mLatestActiveTrack is not cleared even if is the same as track. mHasChanged = true; +#ifdef TEE_SINK + track->dumpTee(-1 /* fd */, "_REMOVE"); +#endif return index; } @@ -2853,6 +2856,9 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() ATRACE_END(); if (framesWritten > 0) { bytesWritten = framesWritten * mFrameSize; +#ifdef TEE_SINK + mTee.write((char *)mSinkBuffer + offset, framesWritten); +#endif } else { bytesWritten = framesWritten; } @@ -3866,9 +3872,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // create a MonoPipe to connect our submix to FastMixer NBAIO_Format format = mOutputSink->format(); -#ifdef TEE_SINK - NBAIO_Format origformat = format; -#endif + // adjust format to match that of the Fast Mixer ALOGV("format changed from %#x to %#x", format.mFormat, fastMixerFormat); format.mFormat = fastMixerFormat; @@ -3880,7 +3884,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud MonoPipe *monoPipe = new MonoPipe(mNormalFrameCount * 4, format, true /*writeCanBlock*/); const NBAIO_Format offers[1] = {format}; size_t numCounterOffers = 0; -#if !LOG_NDEBUG || defined(TEE_SINK) +#if !LOG_NDEBUG ssize_t index = #else (void) @@ -3891,25 +3895,8 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); mPipeSink = monoPipe; -#ifdef TEE_SINK - if (mTeeSinkOutputEnabled) { - // create a Pipe to archive a copy of FastMixer's output for dumpsys - Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, origformat); - const NBAIO_Format offers2[1] = {origformat}; - numCounterOffers = 0; - index = teeSink->negotiate(offers2, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - mTeeSink = teeSink; - PipeReader *teeSource = new PipeReader(*teeSink); - numCounterOffers = 0; - index = teeSource->negotiate(offers2, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - mTeeSource = teeSource; - } -#endif - // create fast mixer and configure it initially with just one fast track for our submix - mFastMixer = new FastMixer(); + mFastMixer = new FastMixer(mId); FastMixerStateQueue *sq = mFastMixer->sq(); #ifdef STATE_QUEUE_DUMP sq->setObserverDump(&mStateQueueObserverDump); @@ -3935,9 +3922,6 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud state->mColdFutexAddr = &mFastMixerFutex; state->mColdGen++; state->mDumpState = &mFastMixerDumpState; -#ifdef TEE_SINK - state->mTeeSink = mTeeSink.get(); -#endif mFastMixerNBLogWriter = audioFlinger->newWriter_l(kFastMixerLogSize, "FastMixer"); state->mNBLogWriter = mFastMixerNBLogWriter.get(); sq->end(); @@ -3957,7 +3941,12 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud tid = mAudioWatchdog->getTid(); sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/); #endif - + } else { +#ifdef TEE_SINK + // Only use the MixerThread tee if there is no FastMixer. + mTee.set(mOutputSink->format(), NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD); + mTee.setId(std::string("_") + std::to_string(mId) + "_M"); +#endif } switch (kUseFastMixer) { @@ -5064,12 +5053,6 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar } else { dprintf(fd, " No FastMixer\n"); } - -#ifdef TEE_SINK - // Write the tee output to a .wav file - dumpTee(fd, mTeeSource, mId, 'M'); -#endif - } uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const @@ -6235,9 +6218,6 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady -#ifdef TEE_SINK - , const sp& teeSink -#endif ) : ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady), mInput(input), @@ -6245,9 +6225,6 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, mRsmpInBuffer(NULL), // mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l() mRsmpInRear(0) -#ifdef TEE_SINK - , mTeeSink(teeSink) -#endif , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize, "RecordThreadRO", MemoryHeapBase::READ_ONLY)) // mFastCapture below @@ -6370,6 +6347,10 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, mFastTrackAvail = true; } +#ifdef TEE_SINK + mTee.set(mInputSource->format(), NBAIO_Tee::TEE_FLAG_INPUT_THREAD); + mTee.setId(std::string("_") + std::to_string(mId) + "_C"); +#endif failed: ; // FIXME mNormalSource @@ -6712,9 +6693,9 @@ reacquire_wakelock: } ALOG_ASSERT(framesRead > 0); - if (mTeeSink != 0) { - (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead); - } +#ifdef TEE_SINK + (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead); +#endif // If destination is non-contiguous, we now correct for reading past end of buffer. { size_t part1 = mRsmpInFramesP2 - rear; diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 5e5e9487fc..f18294b728 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -497,6 +497,9 @@ protected: // we must not wait for async write callback in the thread loop before evaluating it bool mSignalPending; +#ifdef TEE_SINK + NBAIO_Tee mTee; +#endif // ActiveTracks is a sorted vector of track type T representing the // active tracks of threadLoop() to be considered by the locked prepare portion. // ActiveTracks should be accessed with the ThreadBase lock held. @@ -1054,11 +1057,6 @@ private: sp mPipeSink; // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink sp mNormalSink; -#ifdef TEE_SINK - // For dumpsys - sp mTeeSink; - sp mTeeSource; -#endif uint32_t mScreenState; // cached copy of gScreenState // TODO: add comment and adjust size as needed static const size_t kFastMixerLogSize = 8 * 1024; @@ -1374,9 +1372,6 @@ public: audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady -#ifdef TEE_SINK - , const sp& teeSink -#endif ); virtual ~RecordThread(); @@ -1506,8 +1501,6 @@ private: int32_t mRsmpInRear; // last filled frame + 1 // For dumpsys - const sp mTeeSink; - const sp mReadOnlyHeap; // one-time initialization, no locks required diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index ccfb69fbb2..9f17c3ce72 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -100,6 +100,12 @@ public: audio_attributes_t attributes() const { return mAttr; } +#ifdef TEE_SINK + void dumpTee(int fd, const std::string &reason) const { + mTee.dump(fd, reason); + } +#endif + protected: DISALLOW_COPY_AND_ASSIGN(TrackBase); @@ -208,8 +214,9 @@ protected: const bool mIsOut; sp mServerProxy; const int mId; - sp mTeeSink; - sp mTeeSource; +#ifdef TEE_SINK + NBAIO_Tee mTee; +#endif bool mTerminated; track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ... audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index fc8f34b5f0..9fd17c9930 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -210,22 +210,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mBufferSize = bufferSize; #ifdef TEE_SINK - if (mTeeSinkTrackEnabled) { - NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount, mFormat); - if (Format_isValid(pipeFormat)) { - Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat); - size_t numCounterOffers = 0; - const NBAIO_Format offers[1] = {pipeFormat}; - ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - PipeReader *pipeReader = new PipeReader(*pipe); - numCounterOffers = 0; - index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - mTeeSink = pipe; - mTeeSource = pipeReader; - } - } + mTee.set(sampleRate, mChannelCount, format, NBAIO_Tee::TEE_FLAG_TRACK); #endif } @@ -244,9 +229,6 @@ status_t AudioFlinger::ThreadBase::TrackBase::initCheck() const AudioFlinger::ThreadBase::TrackBase::~TrackBase() { -#ifdef TEE_SINK - dumpTee(-1, mTeeSource, mId, 'T'); -#endif // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference mServerProxy.clear(); if (mCblk != NULL) { @@ -274,9 +256,7 @@ AudioFlinger::ThreadBase::TrackBase::~TrackBase() void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { #ifdef TEE_SINK - if (mTeeSink != 0) { - (void) mTeeSink->write(buffer->raw, buffer->frameCount); - } + mTee.write(buffer->raw, buffer->frameCount); #endif ServerProxy::Buffer buf; @@ -454,6 +434,12 @@ AudioFlinger::PlaybackThread::Track::Track( thread->mFastTrackAvailMask &= ~(1 << i); } mName = TRACK_NAME_PENDING; + +#ifdef TEE_SINK + mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + + "_" + std::to_string(mId) + + + "_PEND_T"); +#endif } AudioFlinger::PlaybackThread::Track::~Track() @@ -1695,6 +1681,11 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( ALOG_ASSERT(thread->mFastTrackAvail); thread->mFastTrackAvail = false; } +#ifdef TEE_SINK + mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + + "_" + std::to_string(mId) + + "_R"); +#endif } AudioFlinger::RecordThread::RecordTrack::~RecordTrack() -- GitLab From bd3b2b01a297c913dea182f89a0009f9a2f83ab9 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 21 May 2018 10:53:11 -0700 Subject: [PATCH 0065/1530] AudioFlinger: remove false underruns if we retry for data Test: see bug Bug: 79999301 Change-Id: Ib35bd43ca59ea9f8857f08c5d6e7e3dbf68236a7 --- services/audioflinger/Threads.cpp | 44 ++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 7b5d9e640a..8bf5337a30 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4273,6 +4273,37 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found. mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found. + // DeferredOperations handles statistics after setting mixerStatus. + class DeferredOperations { + public: + DeferredOperations(mixer_state *mixerStatus) + : mMixerStatus(mixerStatus) { } + + // when leaving scope, tally frames properly. + ~DeferredOperations() { + // Tally underrun frames only if we are actually mixing (MIXER_TRACKS_READY) + // because that is when the underrun occurs. + // We do not distinguish between FastTracks and NormalTracks here. + if (*mMixerStatus == MIXER_TRACKS_READY) { + for (const auto &underrun : mUnderrunFrames) { + underrun.first->mAudioTrackServerProxy->tallyUnderrunFrames( + underrun.second); + } + } + } + + // tallyUnderrunFrames() is called to update the track counters + // with the number of underrun frames for a particular mixer period. + // We defer tallying until we know the final mixer status. + void tallyUnderrunFrames(sp track, size_t underrunFrames) { + mUnderrunFrames.emplace_back(track, underrunFrames); + } + + private: + const mixer_state * const mMixerStatus; + std::vector, size_t>> mUnderrunFrames; + } deferredOperations(&mixerStatus); // implicit nested scope for variable capture + for (size_t i=0 ; i t = mActiveTracks[i]; @@ -4307,13 +4338,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mObservedUnderruns = underruns; // don't count underruns that occur while stopping or pausing // or stopped which can occur when flush() is called while active + size_t underrunFrames = 0; if (!(track->isStopping() || track->isPausing() || track->isStopped()) && recentUnderruns > 0) { // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun - track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount); - } else { - track->mAudioTrackServerProxy->tallyUnderrunFrames(0); + underrunFrames = recentUnderruns * mFrameCount; } + // Immediately account for FastTrack underruns. + track->mAudioTrackServerProxy->tallyUnderrunFrames(underrunFrames); // This is similar to the state machine for normal tracks, // with a few modifications for fast tracks. @@ -4728,13 +4760,13 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac mixerStatus = MIXER_TRACKS_READY; } } else { + size_t underrunFrames = 0; if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) { ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)", track, framesReady, desiredFrames); - track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames); - } else { - track->mAudioTrackServerProxy->tallyUnderrunFrames(0); + underrunFrames = desiredFrames; } + deferredOperations.tallyUnderrunFrames(track, underrunFrames); // clear effect chain input buffer if an active track underruns to avoid sending // previous audio buffer again to effects -- GitLab From 7be71d225fd6d44cc38eaef532c2eb502dcadb91 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 23 May 2018 16:51:46 -0700 Subject: [PATCH 0066/1530] Zero initialize audio_port_config and containing structs Add zero initialization for struct audio_port_config and the structures that contain it: audio_port and audio_patch. Left intact places where the struct is overwritten completely or memset'd right after declaration. Test: make Change-Id: I889e55fd7c7b8810ef840090a9f0a85d55eaedb3 --- .../audiopolicy/common/managerdefinitions/src/AudioPort.cpp | 3 +-- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 6 +++--- services/audiopolicy/service/AudioPolicyService.cpp | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp index d85562e319..fc868d315f 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -398,10 +398,9 @@ AudioPortConfig::AudioPortConfig() status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *config, struct audio_port_config *backupConfig) { - struct audio_port_config localBackupConfig; + struct audio_port_config localBackupConfig = { .config_mask = config->config_mask }; status_t status = NO_ERROR; - localBackupConfig.config_mask = config->config_mask; toAudioPortConfig(&localBackupConfig); sp audioport = getAudioPort(); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5d67799163..a35e6db317 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3185,10 +3185,10 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * return BAD_VALUE; } - struct audio_port_config backupConfig; + struct audio_port_config backupConfig = {}; status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig); if (status == NO_ERROR) { - struct audio_port_config newConfig; + struct audio_port_config newConfig = {}; audioPortConfig->toAudioPortConfig(&newConfig, config); status = mpClientInterface->setAudioPortConfig(&newConfig, 0); } @@ -3336,7 +3336,7 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so sp sourceDesc = new AudioSourceDescriptor(srcDeviceDesc, attributes, uid); - struct audio_patch dummyPatch; + struct audio_patch dummyPatch = {}; sp patchDesc = new AudioPatch(&dummyPatch, uid); sourceDesc->mPatchDesc = patchDesc; diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 65b84957d4..13792232e0 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -1205,6 +1205,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(sp& c patch = ((CreateAudioPatchData *)command->mParam.get())->mPatch; } else { handle = ((ReleaseAudioPatchData *)command->mParam.get())->mHandle; + memset(&patch, 0, sizeof(patch)); } audio_patch_handle_t handle2; struct audio_patch patch2; -- GitLab From 59fef32845e5fec2f5c97c9637965b8c3ca24a6f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 25 May 2018 12:43:43 -0700 Subject: [PATCH 0067/1530] SingleStateQueue: Add last method to Mutator Obtains last value written. Test: see bug for CL topic and test Bug: 80272001 Change-Id: Ia71752970ac2e66bb1c89cd9e06691fc88f64647 --- media/libmedia/include/media/SingleStateQueue.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/libmedia/include/media/SingleStateQueue.h b/media/libmedia/include/media/SingleStateQueue.h index d423962f66..c2761cb0b2 100644 --- a/media/libmedia/include/media/SingleStateQueue.h +++ b/media/libmedia/include/media/SingleStateQueue.h @@ -99,6 +99,13 @@ private: return mShared->mAck - sequence >= 0; } + // returns the last value written (or the contents of the shared buffer after initialization + // if no value was written). + T last() const + { // assume no sequence check required - we are the writer. + return mShared->mValue; + } + private: int32_t mSequence; Shared * const mShared; -- GitLab From f6ab58dfbc394e88115eb1483aaf47db91279981 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 25 May 2018 12:50:39 -0700 Subject: [PATCH 0068/1530] AudioFlinger: Add latency measurements from timestamp Replaces main and aux buf from track dump. Bug: 80272001 Test: adb shell dumpsys media.audio_flinger Change-Id: I5d6565410e652ec7fc6701b171d299dea9f7bc3e --- include/private/media/AudioTrackShared.h | 4 +++ .../include/media/AudioTimestamp.h | 15 +++++++++ services/audioflinger/FastMixer.cpp | 4 +++ services/audioflinger/FastMixerDumpState.cpp | 4 +-- services/audioflinger/FastMixerDumpState.h | 1 + services/audioflinger/PlaybackTracks.h | 9 +++++- services/audioflinger/Threads.cpp | 8 +++-- services/audioflinger/Tracks.cpp | 31 ++++++++++++------- 8 files changed, 60 insertions(+), 16 deletions(-) diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index ca119d5da1..518cc632cb 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -538,6 +538,10 @@ public: mTimestampMutator.push(timestamp); } + virtual ExtendedTimestamp getTimestamp() const { + return mTimestampMutator.last(); + } + // Flushes the shared ring buffer if the client had requested it using mStreaming.mFlush. // If flush occurs then: // cblk->u.mStreaming.mFront, ServerProxy::mFlush and ServerProxy::mFlushed will be modified diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h index 498de8e071..bdffdac9ca 100644 --- a/media/libaudioclient/include/media/AudioTimestamp.h +++ b/media/libaudioclient/include/media/AudioTimestamp.h @@ -135,6 +135,21 @@ struct alignas(8) /* bug 29096183, bug 29108507 */ ExtendedTimestamp { return INVALID_OPERATION; } + double getOutputServerLatencyMs(uint32_t sampleRate) const { + return getLatencyMs(sampleRate, LOCATION_SERVER, LOCATION_KERNEL); + } + + double getLatencyMs(uint32_t sampleRate, Location location1, Location location2) const { + if (mTimeNs[location1] > 0 && mTimeNs[location2] > 0) { + const int64_t frameDifference = + mPosition[location1] - mPosition[location2]; + const int64_t timeDifferenceNs = + mTimeNs[location1] - mTimeNs[location2]; + return ((double)frameDifference * 1e9 / sampleRate - timeDifferenceNs) * 1e-6; + } + return 0.; + } + // convert fields to a printable string std::string toString() { std::stringstream ss; diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 7a029637de..caeede9263 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -489,6 +489,10 @@ void FastMixer::onWork() timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]; mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; + // We don't compensate for server - kernel time difference and + // only update latency if we have valid info. + dumpState->mLatencyMs = + (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate; } else { // HAL reported that more frames were presented than were written mNativeFramesWrittenButNotPresented = 0; diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index 2e4fb8ceb5..b3a25203e6 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -68,11 +68,11 @@ void FastMixerDumpState::dump(int fd) const dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", + " mixPeriod=%.2f ms latency=%.2f ms\n", FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten, mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, - mixPeriodSec * 1e3); + mixPeriodSec * 1e3, mLatencyMs); #ifdef FAST_THREAD_STATISTICS // find the interval of valid samples uint32_t bounds = mBounds; diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index 8ef31d1f7a..aed6bc598c 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -66,6 +66,7 @@ struct FastMixerDumpState : FastThreadDumpState { void dump(int fd) const; // should only be called on a stable copy, not the original + double mLatencyMs = 0.; // measured latency, default of 0 if no valid timestamp read. uint32_t mWriteSequence; // incremented before and after each write() uint32_t mFramesWritten; // total number of frames written successfully uint32_t mNumTracks; // total number of active fast tracks diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index ff9444da56..9a8a1544e9 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -41,7 +41,7 @@ public: virtual ~Track(); virtual status_t initCheck() const; - static void appendDumpHeader(String8& result); + void appendDumpHeader(String8& result); void appendDump(String8& result, bool active); virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, @@ -75,6 +75,7 @@ public: bool isOffloadedOrDirect() const { return (mFlags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) != 0; } + bool isStatic() const { return mSharedBuffer.get() != nullptr; } status_t setParameters(const String8& keyValuePairs); status_t attachAuxEffect(int EffectId); @@ -93,6 +94,11 @@ public: virtual bool isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; } + virtual double bufferLatencyMs() { + return isStatic() ? 0. + : (double)mAudioTrackServerProxy->framesReadySafe() * 1000 / sampleRate(); + } + // implement volume handling. media::VolumeShaper::Status applyVolumeShaper( const sp& configuration, @@ -194,6 +200,7 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations + bool mDumpLatency = false; // true if track supports latency dumps. private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 90e2e8e746..e9721fa1d5 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1784,7 +1784,7 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& ar if (numtracks) { dprintf(fd, " of which %zu are active\n", numactive); result.append(prefix); - Track::appendDumpHeader(result); + mTracks[0]->appendDumpHeader(result); for (size_t i = 0; i < numtracks; ++i) { sp track = mTracks[i]; if (track != 0) { @@ -1804,7 +1804,7 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& ar result.append(" The following tracks are in the active list but" " not in the track list\n"); result.append(prefix); - Track::appendDumpHeader(result); + mActiveTracks[0]->appendDumpHeader(result); for (size_t i = 0; i < numactive; ++i) { sp track = mActiveTracks[i]; if (mTracks.indexOf(track) < 0) { @@ -5054,6 +5054,10 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs); dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); + const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); + if (latencyMs > 0.) { + dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); + } if (hasFastMixer()) { dprintf(fd, " FastMixer thread %p tid=%d", mFastMixer.get(), mFastMixer->getTid()); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 824dac767d..6953ebfb53 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -435,6 +435,7 @@ AudioFlinger::PlaybackThread::Track::Track( } mName = TRACK_NAME_PENDING; + mDumpLatency = thread->type() == ThreadBase::MIXER; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId) + @@ -489,13 +490,14 @@ void AudioFlinger::PlaybackThread::Track::destroy() } } -/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) +void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) { - result.append("T Name Active Client Session S Flags " + result.appendFormat("T Name Active Client Session S Flags " " Format Chn mask SRate " "ST L dB R dB VS dB " - " Server FrmCnt FrmRdy F Underruns Flushed " - "Main Buf Aux Buf\n"); + " Server FrmCnt FrmRdy F Underruns Flushed" + "%s\n", + mDumpLatency ? " Latency" : ""); } void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active) @@ -504,7 +506,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ switch (mType) { case TYPE_DEFAULT: case TYPE_OUTPUT: - if (mSharedBuffer.get() != nullptr) { + if (isStatic()) { trackType = 'S'; // static } else { trackType = ' '; // normal @@ -582,8 +584,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ result.appendFormat("%7s %6u %7u %2s 0x%03X " "%08X %08X %6u " "%2u %5.2g %5.2g %5.2g%c " - "%08X %6zu%c %6zu %c %9u%c %7u " - "%08zX %08zX\n", + "%08X %6zu%c %6zu %c %9u%c %7u", active ? "yes" : "no", (mClient == 0) ? getpid() : mClient->pid(), mSessionId, @@ -607,11 +608,19 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ fillingStatus, mAudioTrackServerProxy->getUnderrunFrames(), nowInUnderrun, - (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000, - - (size_t)mMainBuffer, // use %zX as %p appends 0x - (size_t)mAuxBuffer // use %zX as %p appends 0x + (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000 ); + if (mDumpLatency) { + double latencyMs = + mAudioTrackServerProxy->getTimestamp().getOutputServerLatencyMs(mSampleRate); + if (latencyMs > 0.) { + latencyMs += bufferLatencyMs(); + result.appendFormat(" %7.3f", latencyMs); + } else { + result.appendFormat(" Unknown"); + } + } + result.append("\n"); } uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const { -- GitLab From 572aea0952f847377de2f85049cb442cf6a73513 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 23 May 2018 09:54:12 -0700 Subject: [PATCH 0069/1530] xaacdec plugin is not part of vndk xaacdec codec is currently an alternate, so not part of all devices and not part of the VNDK. It's included on a per-device basis. Also reconciling differences introduced in last pi-dev flurry. Bug: 77287124 Test: boot, play appropriate videos/sounds --- .../libstagefright/codecs/xaacdec/Android.bp | 4 +- .../codecs/xaacdec/SoftXAAC.cpp | 710 ++++++++++++++++-- .../libstagefright/codecs/xaacdec/SoftXAAC.h | 24 +- services/mediacodec/Android.mk | 1 - 4 files changed, 654 insertions(+), 85 deletions(-) diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp index 465951bfec..7392f1e1d1 100644 --- a/media/libstagefright/codecs/xaacdec/Android.bp +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -1,9 +1,6 @@ cc_library_shared { name: "libstagefright_soft_xaacdec", vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "SoftXAAC.cpp", @@ -16,6 +13,7 @@ cc_library_shared { cflags: [ "-Werror", + "-DENABLE_MPEG_D_DRC" ], sanitize: { diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp index 376755bae2..b3aefa803a 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -34,14 +34,28 @@ #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */ #define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ +#define DRC_KEY_AAC_DRC_EFFECT_TYPE (3) /* Default Effect type is "Limited playback" */ +/* REF_LEVEL of 64 pairs well with EFFECT_TYPE of 3. */ +#define DRC_DEFAULT_MOBILE_LOUDNESS_LEVEL (64) /* Default loudness value for MPEG-D DRC */ #define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" #define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" #define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" #define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" #define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" +#define PROP_DRC_OVERRIDE_EFFECT_TYPE "ro.aac_drc_effect_type" +#define PROP_DRC_OVERRIDE_LOUDNESS_LEVEL "aac_drc_loudness_level" + #define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */ + +#define RETURN_IF_NE(returned, expected, retval, str) \ + if ( returned != expected ) { \ + ALOGE("Error in %s: Returned: %d Expected: %d", str, returned, expected); \ + return retval; \ + } + + namespace android { template @@ -76,6 +90,7 @@ SoftXAAC::SoftXAAC( mCurrentTimestamp(0), mOutputPortSettingsChange(NONE), mXheaacCodecHandle(NULL), + mMpegDDrcHandle(NULL), mInputBufferSize(0), mOutputFrameLength(1024), mInputBuffer(NULL), @@ -85,7 +100,10 @@ SoftXAAC::SoftXAAC( mPcmWdSz(0), mChannelMask(0), mIsCodecInitialized(false), - mIsCodecConfigFlushRequired(false) + mIsCodecConfigFlushRequired(false), + mpegd_drc_present(0), + drc_flag(0) + { initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); @@ -145,11 +163,32 @@ void SoftXAAC::initPorts() { status_t SoftXAAC::initDecoder() { status_t status = UNKNOWN_ERROR; - unsigned int ui_drc_val; + int ui_drc_val; IA_ERRORCODE err_code = IA_NO_ERROR; - initXAACDecoder(); - if (NULL == mXheaacCodecHandle) { - ALOGE("AAC decoder is null. initXAACDecoder Failed"); + int loop = 0; + + err_code = initXAACDecoder(); + if(err_code != IA_NO_ERROR) { + if (NULL == mXheaacCodecHandle) { + ALOGE("AAC decoder handle is null"); + } + if (NULL == mMpegDDrcHandle) { + ALOGE("MPEG-D DRC decoder handle is null"); + } + for(loop= 1; loop < mMallocCount; loop++) { + if (mMemoryArray[loop] == NULL) { + ALOGE(" memory allocation error %d\n",loop); + break; + } + } + ALOGE("initXAACDecoder Failed"); + + for(loop = 0; loop < mMallocCount; loop++) { + if(mMemoryArray[loop]) + free(mMemoryArray[loop]); + } + mMallocCount = 0; + return status; } else { status = OK; } @@ -174,8 +213,30 @@ status_t SoftXAAC::initDecoder() { IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, &ui_drc_val); - ALOGV("Error code returned after DRC Target level set_config is %d", err_code); - ALOGV("Setting IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL with value %d", ui_drc_val); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL"); +#ifdef ENABLE_MPEG_D_DRC + + if (property_get(PROP_DRC_OVERRIDE_LOUDNESS_LEVEL, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d",ui_drc_val, + DRC_DEFAULT_MOBILE_LOUDNESS_LEVEL); + } + else + { + ui_drc_val= DRC_DEFAULT_MOBILE_LOUDNESS_LEVEL; + } + + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, + &ui_drc_val); + + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS"); +#endif + if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { @@ -192,8 +253,8 @@ status_t SoftXAAC::initDecoder() { IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, &ui_drc_val); - ALOGV("Error code returned after DRC cut factor set_config is %d", err_code); - ALOGV("Setting IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT with value %d", ui_drc_val); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT"); if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { @@ -210,13 +271,12 @@ status_t SoftXAAC::initDecoder() { IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, &ui_drc_val); - ALOGV("Error code returned after DRC boost factor set_config is %d", err_code); - ALOGV("Setting DRC_DEFAULT_MOBILE_DRC_BOOST with value %d", ui_drc_val); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST"); - if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) + if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) { ui_drc_val = atoi(value); - ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", ui_drc_val, + ALOGV("AAC decoder using desired Heavy compression factor of %d instead of %d", ui_drc_val, DRC_DEFAULT_MOBILE_DRC_HEAVY); } else @@ -228,9 +288,28 @@ status_t SoftXAAC::initDecoder() { IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, &ui_drc_val); - ALOGV("Error code returned after DRC heavy set_config is %d", err_code); - ALOGV("Setting DRC_DEFAULT_MOBILE_DRC_HEAVY with value %d", ui_drc_val); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP"); +#ifdef ENABLE_MPEG_D_DRC + if (property_get(PROP_DRC_OVERRIDE_EFFECT_TYPE, value, NULL)) + { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC effect type of %d instead of %d", ui_drc_val, + DRC_KEY_AAC_DRC_EFFECT_TYPE); + } + else + { + ui_drc_val = DRC_KEY_AAC_DRC_EFFECT_TYPE; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, + &ui_drc_val); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE"); + +#endif return status; } @@ -436,10 +515,10 @@ OMX_ERRORTYPE SoftXAAC::internalSetParameter( return OMX_ErrorNone; } - case OMX_IndexParamAudioAndroidAacPresentation: + case OMX_IndexParamAudioAndroidAacDrcPresentation: { - const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams = - (const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params; + const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *aacPresParams = + (const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *)params; if (!isValidOMXParam(aacPresParams)) { ALOGE("set OMX_ErrorBadParameter"); @@ -468,7 +547,11 @@ OMX_ERRORTYPE SoftXAAC::internalSetParameter( setXAACDRCInfo(aacPresParams->nDrcCut, aacPresParams->nDrcBoost, aacPresParams->nTargetReferenceLevel, - aacPresParams->nHeavyCompression); + aacPresParams->nHeavyCompression + #ifdef ENABLE_MPEG_D_DRC + ,aacPresParams->nDrcEffectType + #endif + ); // TOD0 : Revert this change return OMX_ErrorNone; } @@ -774,7 +857,6 @@ void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; - ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen); notifyFillBufferDone(outHeader); outHeader = NULL; } @@ -831,31 +913,33 @@ void SoftXAAC::configflushDecode() { IA_API_CMD_INIT, IA_CMD_TYPE_FLUSH_MEM, NULL); - ALOGV("Codec initialized:%d",mIsCodecInitialized); - ALOGV("Error code from first flush %d",err_code); err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); - + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_FLUSH_MEM, NULL); - + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_DONE_QUERY, &ui_init_done); - - ALOGV("Flush called"); + if (ui_init_done) { err_code = getXAACStreamInfo(); ALOGV("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", mSampFreq,mNumChannels,mPcmWdSz,mChannelMask,mOutputFrameLength); - mIsCodecInitialized = true; + if(mNumChannels > MAX_CHANNEL_COUNT) { + ALOGE(" No of channels are more than max channels\n"); + mIsCodecInitialized = false; + } + else + mIsCodecInitialized = true; } } @@ -918,10 +1002,9 @@ int SoftXAAC::initXAACDecoder() { /* Get memory information and allocate memory */ /* Memory variables */ - UWORD32 n_mems, ui_rem; UWORD32 ui_proc_mem_tabs_size; /* API size */ - UWORD32 pui_ap_isize; + UWORD32 pui_api_size; mInputBufferSize = 0; mInputBuffer = 0; @@ -937,12 +1020,14 @@ int SoftXAAC::initXAACDecoder() { err_code = ixheaacd_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, - &pui_ap_isize); - ALOGV("return code of IA_API_CMD_GET_API_SIZE: %d",err_code); + &pui_api_size); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_API_SIZE"); + /* Allocate memory for API */ - mMemoryArray[mMallocCount] = memalign(4, pui_ap_isize); + mMemoryArray[mMallocCount] = memalign(4, pui_api_size); if (mMemoryArray[mMallocCount] == NULL) { - ALOGE("malloc for pui_ap_isize + 4 >> %d Failed",pui_ap_isize + 4); + ALOGE("malloc for pui_api_size + 4 >> %d Failed",pui_api_size + 4); + return IA_FATAL_ERROR; } /* Set API object with the memory allocated */ mXheaacCodecHandle = @@ -954,7 +1039,38 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_INIT, IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL); - ALOGV("return code of IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS"); +#ifdef ENABLE_MPEG_D_DRC + /* Get the API size */ + err_code = ia_drc_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_API_SIZE"); + + /* Allocate memory for API */ + mMemoryArray[mMallocCount] = memalign(4, pui_api_size); + + if(mMemoryArray[mMallocCount] == NULL) + { + ALOGE("malloc for drc api structure Failed"); + return IA_FATAL_ERROR; + } + memset(mMemoryArray[mMallocCount],0,pui_api_size); + + /* Set API object with the memory allocated */ + mMpegDDrcHandle = + (pVOID)((WORD8*)mMemoryArray[mMallocCount]); + mMallocCount++; + + + /* Set the config params to default values */ + err_code = ia_drc_dec_api( + mMpegDDrcHandle, + IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, + NULL); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS"); +#endif /* ******************************************************************/ /* Set config parameters */ @@ -964,7 +1080,7 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4, &ui_mp4_flag); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4"); /* ******************************************************************/ /* Initialize Memory info tables */ @@ -975,10 +1091,11 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_GET_MEMTABS_SIZE, 0, &ui_proc_mem_tabs_size); - ALOGV("return code of IA_API_CMD_GET_MEMTABS_SIZE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); mMemoryArray[mMallocCount] = memalign(4, ui_proc_mem_tabs_size); if (mMemoryArray[mMallocCount] == NULL) { ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!",ui_proc_mem_tabs_size + 4); + return IA_FATAL_ERROR; } /* Set pointer for process memory tables */ @@ -986,7 +1103,7 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_SET_MEMTABS_PTR, 0, (pVOID)((WORD8*)mMemoryArray[mMallocCount])); - ALOGV("return code of IA_API_CMD_SET_MEMTABS_PTR: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEMTABS_PTR"); mMallocCount++; /* initialize the API, post config, fill memory tables */ @@ -994,20 +1111,14 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_INIT, IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); - ALOGV("return code of IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); /* ******************************************************************/ /* Allocate Memory with info from library */ /* ******************************************************************/ - - /* Get number of memory tables required */ - err_code = ixheaacd_dec_api(mXheaacCodecHandle, - IA_API_CMD_GET_N_MEMTABS, - 0, - &n_mems); - ALOGV("return code of IA_API_CMD_GET_N_MEMTABS: %d",err_code); - - for(i = 0; i < (WORD32)n_mems; i++) { + /* There are four different types of memories, that needs to be allocated */ + /* persistent,scratch,input and output */ + for(i = 0; i < 4; i++) { int ui_size = 0, ui_alignment = 0, ui_type = 0; pVOID pv_alloc_ptr; @@ -1016,26 +1127,27 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); - ALOGV("return code of IA_API_CMD_GET_MEM_INFO_SIZE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); /* Get memory alignment */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &ui_alignment); - ALOGV("return code of IA_API_CMD_GET_MEM_INFO_ALIGNMENT: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); /* Get memory type */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); - ALOGV("return code of IA_API_CMD_GET_MEM_INFO_TYPE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); mMemoryArray[mMallocCount] = memalign(ui_alignment , ui_size); if (mMemoryArray[mMallocCount] == NULL) { ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!",ui_size + ui_alignment); + return IA_FATAL_ERROR; } pv_alloc_ptr = (pVOID )((WORD8*)mMemoryArray[mMallocCount]); @@ -1046,7 +1158,7 @@ int SoftXAAC::initXAACDecoder() { IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); - ALOGV("return code of IA_API_CMD_SET_MEM_PTR: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEM_PTR"); if (ui_type == IA_MEMTYPE_INPUT) { mInputBuffer = (pWORD8)pv_alloc_ptr; mInputBufferSize = ui_size; @@ -1081,7 +1193,7 @@ int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); - ALOGV("return code of IA_API_CMD_SET_INPUT_BYTES: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_INPUT_BYTES"); if (mIsCodecConfigFlushRequired) { /* If codec is already initialized, then GA header is passed again */ @@ -1091,7 +1203,7 @@ int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { IA_API_CMD_INIT, IA_CMD_TYPE_GA_HDR, NULL); - ALOGV("return code of IA_CMD_TYPE_GA_HDR: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_GA_HDR"); } else { /* Initialize the process */ @@ -1099,7 +1211,7 @@ int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { IA_API_CMD_INIT, IA_CMD_TYPE_INIT_PROCESS, NULL); - ALOGV("return code of IA_CMD_TYPE_INIT_PROCESS: %d",err_code); + ALOGV("IA_CMD_TYPE_INIT_PROCESS returned error_code = %d",err_code); } /* Checking for end of initialization */ @@ -1107,25 +1219,344 @@ int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { IA_API_CMD_INIT, IA_CMD_TYPE_INIT_DONE_QUERY, &ui_init_done); - ALOGV("return code of IA_CMD_TYPE_INIT_DONE_QUERY: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_DONE_QUERY"); /* How much buffer is used in input buffers */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &i_bytes_consumed); - ALOGV("return code of IA_API_CMD_GET_CURIDX_INPUT_BUF: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); if(ui_init_done){ err_code = getXAACStreamInfo(); ALOGI("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d", mSampFreq,mNumChannels,mPcmWdSz,mChannelMask,mOutputFrameLength); mIsCodecInitialized = true; + +#ifdef ENABLE_MPEG_D_DRC + configMPEGDDrc(); +#endif } return err_code; } +int SoftXAAC::configMPEGDDrc() +{ + IA_ERRORCODE err_code = IA_NO_ERROR; + int i_effect_type; + int i_loud_norm; + int i_target_loudness; + unsigned int i_sbr_mode; + int n_mems; + int i; + +#ifdef ENABLE_MPEG_D_DRC + { + + /* Sampling Frequency */ + { + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ, &mSampFreq); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ"); + } + /* Total Number of Channels */ + { + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS"); + } + + /* PCM word size */ + { + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ"); + } + + /*Set Effect Type*/ + + { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE"); + + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE"); + + } + +/*Set target loudness */ + + { + err_code = ixheaacd_dec_api( + mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS"); + + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS"); + + } + + /*Set loud_norm_flag*/ + { + err_code = ixheaacd_dec_api( + mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_NE(err_code, IA_NO_ERROR , err_code,"IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM"); + + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_NE(err_code, IA_NO_ERROR , err_code,"IA_DRC_DEC_CONFIG_DRC_LOUD_NORM"); + + } + + + + err_code = ixheaacd_dec_api( + mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &i_sbr_mode); + RETURN_IF_NE(err_code, IA_NO_ERROR , err_code,"IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); + + + if(i_sbr_mode!=0) + { + WORD32 frame_length; + if (i_sbr_mode==1) + { + frame_length=2048; + } + else if(i_sbr_mode==3) + { + frame_length=4096; + } + else + { + frame_length=1024; + } + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE, &frame_length); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE"); + + } + + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); + + + + for (i = 0; i < (WORD32)2; i++) { + WORD32 ui_size, ui_alignment, ui_type; + pVOID pv_alloc_ptr; + + /* Get memory size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); + /* Get memory alignment */ + err_code = + ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &ui_alignment); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); + + /* Get memory type */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); + + + mMemoryArray[mMallocCount] = memalign(4, ui_size); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE(" Cannot create requested memory %d",ui_size); + return IA_FATAL_ERROR; + } + pv_alloc_ptr = + (pVOID )((WORD8*)mMemoryArray[mMallocCount]); + mMallocCount++; + + /* Set the buffer pointer */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEM_PTR"); + } + { + WORD32 ui_size; + ui_size=8192*2; + mMemoryArray[mMallocCount]=memalign(4, ui_size); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE(" Cannot create requested memory %d",ui_size); + return IA_FATAL_ERROR; + } + + drc_ip_buf=(int8_t *)mMemoryArray[mMallocCount]; + mMallocCount++; + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, + 2, /*mOutputBuffer*/ drc_ip_buf); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEM_PTR"); + + mMemoryArray[mMallocCount]=memalign(4, ui_size); + if (mMemoryArray[mMallocCount] == NULL) { + ALOGE(" Cannot create requested memory %d",ui_size); + return IA_FATAL_ERROR; + } + + drc_op_buf=(int8_t *)mMemoryArray[mMallocCount]; + mMallocCount++; + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, + 3, /*mOutputBuffer*/ drc_op_buf); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_MEM_PTR"); + } + /*ITTIAM: DRC buffers + buf[0] - contains extension element pay load loudness related + buf[1] - contains extension element pay load*/ + { + VOID *p_array[2][16]; + WORD32 ii; + WORD32 buf_sizes[2][16]; + WORD32 num_elements; + WORD32 num_config_ext; + WORD32 bit_str_fmt = 1; + + + + WORD32 uo_num_chan; + + memset(buf_sizes, 0, 32 * sizeof(WORD32)); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_BUF_SIZES, &buf_sizes[0][0]); + + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR, &p_array); + + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_SET_BUFF_PTR, 0); + + + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_NUM_ELE, &num_elements); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_NUM_CONFIG_EXT, &num_config_ext); + + for (ii = 0; ii < num_config_ext; ii++) { + /*copy loudness bitstream*/ + if (buf_sizes[0][ii] > 0) { + memcpy(drc_ip_buf, p_array[0][ii], buf_sizes[0][ii]); + + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + + /* Set number of bytes to be processed */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_SET_INPUT_BYTES_IL_BS, 0, + &buf_sizes[0][ii]); + + + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF, NULL); + + + + drc_flag = 1; + } + } + + for (ii = 0; ii < num_elements; ii++) { + /*copy config bitstream*/ + if (buf_sizes[1][ii] > 0) { + memcpy(drc_ip_buf, p_array[1][ii], buf_sizes[1][ii]); + /* Set number of bytes to be processed */ + + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_SET_INPUT_BYTES_IC_BS, 0, + &buf_sizes[1][ii]); + + + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF, NULL); + + + + drc_flag = 1; + } + } + + if (drc_flag == 1) { + mpegd_drc_present = 1; + } else { + mpegd_drc_present = 0; + } + + + /*Read interface buffer config file bitstream*/ + if(mpegd_drc_present==1){ + + WORD32 interface_is_present = 1; + + + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_INT_PRESENT, &interface_is_present); + + + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF, NULL); + + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_PROCESS, NULL); + + + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &uo_num_chan); + + } + } + } +#endif + +return err_code; + +} int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, int32_t *bytesConsumed, @@ -1143,14 +1574,14 @@ int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); - ALOGV("return code of IA_API_CMD_SET_INPUT_BYTES: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_SET_INPUT_BYTES"); /* Execute process */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL); - ALOGV("return code of IA_CMD_TYPE_DO_EXECUTE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_DO_EXECUTE"); UWORD32 ui_exec_done; /* Checking for end of processing */ @@ -1158,22 +1589,78 @@ int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DONE_QUERY, &ui_exec_done); - ALOGV("return code of IA_CMD_TYPE_DONE_QUERY: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_CMD_TYPE_DONE_QUERY"); + +#ifdef ENABLE_MPEG_D_DRC + { + if (ui_exec_done != 1) { + VOID *p_array; // ITTIAM:buffer to handle gain payload + WORD32 buf_size = 0; // ITTIAM:gain payload length + WORD32 bit_str_fmt = 1; + WORD32 gain_stream_flag = 1; + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size); + + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array); + + if (buf_size > 0) { + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + + memcpy(drc_ip_buf, p_array, buf_size); + /* Set number of bytes to be processed */ + err_code = + ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size); + + err_code = ia_drc_dec_api( + mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag); + + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL); + + + mpegd_drc_present = 1; + } + } + } +#endif /* How much buffer is used in input buffers */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, bytesConsumed); - ALOGV("return code of IA_API_CMD_GET_CURIDX_INPUT_BUF: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); /* Get the output bytes */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes); - ALOGV("return code of IA_API_CMD_GET_OUTPUT_BYTES: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_GET_OUTPUT_BYTES"); +#ifdef ENABLE_MPEG_D_DRC + + if (mpegd_drc_present == 1) { + memcpy(drc_ip_buf, mOutputBuffer, *outBytes); + err_code = ia_drc_dec_api(mMpegDDrcHandle, + IA_API_CMD_SET_INPUT_BYTES, 0, outBytes); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, + IA_CMD_TYPE_DO_EXECUTE, NULL); + + memcpy(mOutputBuffer, drc_op_buf, *outBytes); + } +#endif return err_code; } @@ -1185,7 +1672,7 @@ int SoftXAAC::deInitXAACDecoder() { IA_API_CMD_INPUT_OVER, 0, NULL); - ALOGV("return code of IA_API_CMD_INPUT_OVER: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_API_CMD_INPUT_OVER"); for(int i = 0; i < mMallocCount; i++) { @@ -1205,28 +1692,28 @@ IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ, &mSampFreq); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ"); /* Total Number of Channels */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS"); /* PCM word size */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ"); /* channel mask to tell the arrangement of channels in bit stream */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK, &mChannelMask); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK"); /* Channel mode to tell MONO/STEREO/DUAL-MONO/NONE_OF_THESE */ UWORD32 ui_channel_mode; @@ -1234,7 +1721,7 @@ IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE, &ui_channel_mode); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE"); if(ui_channel_mode == 0) ALOGV("Channel Mode: MONO_OR_PS\n"); else if(ui_channel_mode == 1) @@ -1250,7 +1737,7 @@ IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &ui_sbr_mode); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); if(ui_sbr_mode == 0) ALOGV("SBR Mode: NOT_PRESENT\n"); else if(ui_sbr_mode == 1) @@ -1271,51 +1758,116 @@ IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { IA_ERRORCODE SoftXAAC::setXAACDRCInfo(int32_t drcCut, int32_t drcBoost, int32_t drcRefLevel, - int32_t drcHeavyCompression) { + int32_t drcHeavyCompression + #ifdef ENABLE_MPEG_D_DRC + ,int32_t drEffectType + #endif + ) { IA_ERRORCODE err_code = IA_NO_ERROR; int32_t ui_drc_enable = 1; + int32_t i_effect_type, i_target_loudness, i_loud_norm; err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE, &ui_drc_enable); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE"); if (drcCut !=-1) { - ALOGI("set drcCut=%d", drcCut); err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, &drcCut); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT"); } if (drcBoost !=-1) { - ALOGI("set drcBoost=%d", drcBoost); err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, &drcBoost); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST"); } if (drcRefLevel != -1) { - ALOGI("set drcRefLevel=%d", drcRefLevel); err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, &drcRefLevel); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL"); } - +#ifdef ENABLE_MPEG_D_DRC + if (drcRefLevel != -1) { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, + &drcRefLevel); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS"); + } +#endif if (drcHeavyCompression != -1) { - ALOGI("set drcHeavyCompression=%d", drcHeavyCompression); err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, &drcHeavyCompression); - ALOGV("return code of IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP: %d",err_code); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP"); } +#ifdef ENABLE_MPEG_D_DRC + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, + &drEffectType); + +#endif + +#ifdef ENABLE_MPEG_D_DRC + /*Set Effect Type*/ + + { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE"); + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type); + + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE"); + + } + +/*Set target loudness */ + + { + err_code = ixheaacd_dec_api( + mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS"); + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_NE(err_code, IA_NO_ERROR, err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS"); + + } + /*Set loud_norm_flag*/ + { + err_code = ixheaacd_dec_api( + mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_NE(err_code, IA_NO_ERROR , err_code,"IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM"); + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm); + + RETURN_IF_NE(err_code, IA_NO_ERROR , err_code,"IA_DRC_DEC_CONFIG_DRC_LOUD_NORM"); + + } + +#endif + + return IA_NO_ERROR; } diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.h b/media/libstagefright/codecs/xaacdec/SoftXAAC.h index f4b4c542df..11a9c77928 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.h +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.h @@ -29,12 +29,21 @@ #include "ixheaacd_apicmd_standards.h" #include "ixheaacd_memory_standards.h" #include "ixheaacd_aac_config.h" -//#include "ixheaacd_aac_dec_error.h" + +#include "impd_apicmd_standards.h" +#include "impd_drc_config_params.h" #define MAX_MEM_ALLOCS 100 extern "C" IA_ERRORCODE ixheaacd_dec_api(pVOID p_ia_module_obj, WORD32 i_cmd, WORD32 i_idx, pVOID pv_value); +extern "C" IA_ERRORCODE ia_drc_dec_api(pVOID p_ia_module_obj, + WORD32 i_cmd, WORD32 i_idx, pVOID pv_value); +extern "C" IA_ERRORCODE ixheaacd_get_config_param(pVOID p_ia_process_api_obj, + pWORD32 pi_samp_freq, + pWORD32 pi_num_chan, + pWORD32 pi_pcm_wd_sz, + pWORD32 pi_channel_mask); namespace android { @@ -88,6 +97,7 @@ private: int deInitXAACDecoder(); int configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength); + int configMPEGDDrc(); int decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, int32_t *bytesConsumed, @@ -98,12 +108,17 @@ private: IA_ERRORCODE setXAACDRCInfo(int32_t drcCut, int32_t drcBoost, int32_t drcRefLevel, - int32_t drcHeavyCompression); + int32_t drcHeavyCompression +#ifdef ENABLE_MPEG_D_DRC + ,int32_t drEffectType +#endif + ); bool mEndOfInput; bool mEndOfOutput; void* mXheaacCodecHandle; + void* mMpegDDrcHandle; uint32_t mInputBufferSize; uint32_t mOutputFrameLength; int8_t* mInputBuffer; @@ -114,6 +129,11 @@ private: int32_t mChannelMask; bool mIsCodecInitialized; bool mIsCodecConfigFlushRequired; + int8_t *drc_ip_buf; + int8_t *drc_op_buf; + int32_t mpegd_drc_present; + int32_t drc_flag; +// int32_t is_drc_enabled; void* mMemoryArray[MAX_MEM_ALLOCS]; int32_t mMallocCount; diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 089d62b495..db5f0fff88 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -22,7 +22,6 @@ _software_codecs := \ libstagefright_soft_vorbisdec \ libstagefright_soft_vpxdec \ libstagefright_soft_vpxenc \ - libstagefright_soft_xaacdec \ # service executable include $(CLEAR_VARS) -- GitLab From f4c9c2591eca3d672e20ef428c787bb0054efc7d Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 29 May 2018 20:01:40 -0700 Subject: [PATCH 0070/1530] AudioTimestamp: toString should be const Test: compile, no material difference Change-Id: Ie97d15a4010150202e833603ebdd4289131de87d --- media/libaudioclient/include/media/AudioTimestamp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h index bdffdac9ca..2cd8c87987 100644 --- a/media/libaudioclient/include/media/AudioTimestamp.h +++ b/media/libaudioclient/include/media/AudioTimestamp.h @@ -151,7 +151,7 @@ struct alignas(8) /* bug 29096183, bug 29108507 */ ExtendedTimestamp { } // convert fields to a printable string - std::string toString() { + std::string toString() const { std::stringstream ss; ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << "\n"; -- GitLab From 5186695a2fd99806dc2b05a51bc3a6ca79adcb39 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 30 May 2018 11:22:27 -0700 Subject: [PATCH 0071/1530] mediea metrics audiopolicy pkg fields if the requesting/active package field is empty, substitute in the uid of the corresponding app -- so we get some identification. Bug: 80441117 Test: dumpsys media.metrics Change-Id: I5dfe014b82c30bd25cb9a4b129a351949c12945f --- .../service/AudioPolicyInterfaceImpl.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 1d8f10d652..d3a1188d8e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -528,10 +528,13 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc item->setCString(kAudioPolicyRqstSrc, audioSourceString(client->attributes.source).c_str()); - item->setCString(kAudioPolicyRqstPkg, - std::string(String8(client->opPackageName).string()).c_str()); item->setInt32(kAudioPolicyRqstSession, client->session); - + if (client->opPackageName.size() != 0) { + item->setCString(kAudioPolicyRqstPkg, + std::string(String8(client->opPackageName).string()).c_str()); + } else { + item->setCString(kAudioPolicyRqstPkg, to_string(client->uid).c_str()); + } item->setCString( kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str()); @@ -550,9 +553,13 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc // keeps the last of the clients marked active item->setCString(kAudioPolicyActiveSrc, audioSourceString(other->attributes.source).c_str()); - item->setCString(kAudioPolicyActivePkg, - std::string(String8(other->opPackageName).string()).c_str()); item->setInt32(kAudioPolicyActiveSession, other->session); + if (other->opPackageName.size() != 0) { + item->setCString(kAudioPolicyActivePkg, + std::string(String8(other->opPackageName).string()).c_str()); + } else { + item->setCString(kAudioPolicyRqstPkg, to_string(other->uid).c_str()); + } item->setCString(kAudioPolicyActiveDevice, getDeviceTypeStrForPortId(other->deviceId).c_str()); } -- GitLab From 32abc2bfbf3bc8db0bdcbdc0f700350d7a5bc547 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 24 May 2018 12:57:11 -0700 Subject: [PATCH 0072/1530] Use the new audio_port_config.flags field - When filling out audio_port_config from AudioPortConfig and AudioFlinger's threads, populate audio_port_config.flags when needed. - When creating software patches, apply the flags provided in audio_port_config_flags to the created threads. Bug: 63901775 Test: use USB headset for telephony on sailfish Change-Id: I7704797a84427f7a9431e5132b8f5c51538f9217 --- services/audioflinger/PatchPanel.cpp | 10 ++- services/audioflinger/Threads.cpp | 26 ++++++++ services/audioflinger/Threads.h | 4 ++ .../managerdefinitions/include/AudioPort.h | 12 ++-- .../managerdefinitions/src/AudioPort.cpp | 66 ++++++++++--------- 5 files changed, 77 insertions(+), 41 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index a08da96dd5..3d688fb7ab 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -227,13 +227,16 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa audio_devices_t device = patch->sinks[0].ext.device.type; String8 address = String8(patch->sinks[0].ext.device.address); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + audio_output_flags_t flags = + patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? + patch->sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE; sp thread = mAudioFlinger.openOutput_l( patch->sinks[0].ext.device.hw_module, &output, &config, device, address, - AUDIO_OUTPUT_FLAG_NONE); + flags); ALOGV("mAudioFlinger.openOutput_l() returned %p", thread.get()); if (thread == 0) { status = NO_MEMORY; @@ -262,6 +265,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } else { config.format = newPatch.mPlayback.thread()->format(); } + audio_input_flags_t flags = + patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? + patch->sources[0].flags.input : AUDIO_INPUT_FLAG_NONE; audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; sp thread = mAudioFlinger.openInput_l(srcModule, &input, @@ -269,7 +275,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa device, address, AUDIO_SOURCE_MIC, - AUDIO_INPUT_FLAG_NONE); + flags); ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x", thread.get(), config.channel_mask); if (thread == 0) { diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 90e2e8e746..fe80d923ef 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3792,6 +3792,10 @@ void AudioFlinger::PlaybackThread::toAudioPortConfig(struct audio_port_config *c config->role = AUDIO_PORT_ROLE_SOURCE; config->ext.mix.hw_module = mOutput->audioHwDev->handle(); config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; + if (mOutput && mOutput->flags != AUDIO_OUTPUT_FLAG_NONE) { + config->config_mask |= AUDIO_PORT_CONFIG_FLAGS; + config->flags.output = mOutput->flags; + } } // ---------------------------------------------------------------------------- @@ -7894,6 +7898,10 @@ void AudioFlinger::RecordThread::toAudioPortConfig(struct audio_port_config *con config->role = AUDIO_PORT_ROLE_SINK; config->ext.mix.hw_module = mInput->audioHwDev->handle(); config->ext.mix.usecase.source = mAudioSource; + if (mInput && mInput->flags != AUDIO_INPUT_FLAG_NONE) { + config->config_mask |= AUDIO_PORT_CONFIG_FLAGS; + config->flags.input = mInput->flags; + } } // ---------------------------------------------------------------------------- @@ -8861,6 +8869,15 @@ void AudioFlinger::MmapPlaybackThread::checkSilentMode_l() } } +void AudioFlinger::MmapPlaybackThread::toAudioPortConfig(struct audio_port_config *config) +{ + MmapThread::toAudioPortConfig(config); + if (mOutput && mOutput->flags != AUDIO_OUTPUT_FLAG_NONE) { + config->config_mask |= AUDIO_PORT_CONFIG_FLAGS; + config->flags.output = mOutput->flags; + } +} + void AudioFlinger::MmapPlaybackThread::dumpInternals(int fd, const Vector& args) { MmapThread::dumpInternals(fd, args); @@ -8951,4 +8968,13 @@ void AudioFlinger::MmapCaptureThread::setRecordSilenced(uid_t uid, bool silenced } } +void AudioFlinger::MmapCaptureThread::toAudioPortConfig(struct audio_port_config *config) +{ + MmapThread::toAudioPortConfig(config); + if (mInput && mInput->flags != AUDIO_INPUT_FLAG_NONE) { + config->config_mask |= AUDIO_PORT_CONFIG_FLAGS; + config->flags.input = mInput->flags; + } +} + } // namespace android diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index f18294b728..680e021ade 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1679,6 +1679,8 @@ public: void updateMetadata_l() override; + virtual void toAudioPortConfig(struct audio_port_config *config); + protected: audio_stream_type_t mStreamType; @@ -1707,6 +1709,8 @@ public: void processVolume_l() override; void setRecordSilenced(uid_t uid, bool silenced) override; + virtual void toAudioPortConfig(struct audio_port_config *config); + protected: AudioStreamIn* mInput; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h index 09a86dde03..bd7517f27a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -153,9 +153,6 @@ private: class AudioPortConfig : public virtual RefBase { public: - AudioPortConfig(); - virtual ~AudioPortConfig() {} - status_t applyAudioPortConfig(const struct audio_port_config *config, struct audio_port_config *backupConfig = NULL); virtual void toAudioPortConfig(struct audio_port_config *dstConfig, @@ -165,10 +162,11 @@ public: return (other != 0) && (other->getAudioPort()->getModuleHandle() == getAudioPort()->getModuleHandle()); } - uint32_t mSamplingRate; - audio_format_t mFormat; - audio_channel_mask_t mChannelMask; - struct audio_gain_config mGain; + unsigned int mSamplingRate = 0u; + audio_format_t mFormat = AUDIO_FORMAT_INVALID; + audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE; + struct audio_gain_config mGain = { .index = -1 }; + union audio_io_flags mFlags = { AUDIO_INPUT_FLAG_NONE }; }; } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp index fc868d315f..3fe37abf5c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -386,15 +386,6 @@ void AudioPort::log(const char* indent) const // --- AudioPortConfig class implementation -AudioPortConfig::AudioPortConfig() -{ - mSamplingRate = 0; - mChannelMask = AUDIO_CHANNEL_NONE; - mFormat = AUDIO_FORMAT_INVALID; - memset(&mGain, 0, sizeof(struct audio_gain_config)); - mGain.index = -1; -} - status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *config, struct audio_port_config *backupConfig) { @@ -424,6 +415,9 @@ status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *c if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { mGain = config->gain; } + if (config->config_mask & AUDIO_PORT_CONFIG_FLAGS) { + mFlags = config->flags; + } exit: if (status != NO_ERROR) { @@ -435,33 +429,38 @@ exit: return status; } -void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const +namespace { + +template +void updateField( + const T& portConfigField, T audio_port_config::*port_config_field, + struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig, + unsigned int configMask, T defaultValue) { - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - dstConfig->sample_rate = mSamplingRate; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { - dstConfig->sample_rate = srcConfig->sample_rate; + if (dstConfig->config_mask & configMask) { + if ((srcConfig != nullptr) && (srcConfig->config_mask & configMask)) { + dstConfig->*port_config_field = srcConfig->*port_config_field; + } else { + dstConfig->*port_config_field = portConfigField; } } else { - dstConfig->sample_rate = 0; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = mChannelMask; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - } else { - dstConfig->channel_mask = AUDIO_CHANNEL_NONE; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - dstConfig->format = mFormat; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { - dstConfig->format = srcConfig->format; - } - } else { - dstConfig->format = AUDIO_FORMAT_INVALID; + dstConfig->*port_config_field = defaultValue; } +} + +} // namespace + +void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + updateField(mSamplingRate, &audio_port_config::sample_rate, + dstConfig, srcConfig, AUDIO_PORT_CONFIG_SAMPLE_RATE, 0u); + updateField(mChannelMask, &audio_port_config::channel_mask, + dstConfig, srcConfig, AUDIO_PORT_CONFIG_CHANNEL_MASK, + (audio_channel_mask_t)AUDIO_CHANNEL_NONE); + updateField(mFormat, &audio_port_config::format, + dstConfig, srcConfig, AUDIO_PORT_CONFIG_FORMAT, AUDIO_FORMAT_INVALID); + sp audioport = getAudioPort(); if ((dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) && audioport != NULL) { dstConfig->gain = mGain; @@ -477,6 +476,9 @@ void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, } else { dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; } + + updateField(mFlags, &audio_port_config::flags, + dstConfig, srcConfig, AUDIO_PORT_CONFIG_FLAGS, { AUDIO_INPUT_FLAG_NONE }); } } // namespace android -- GitLab From 6a867d536f4b5c00ffca2d2025d8d36b7f6a58d8 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 30 May 2018 11:51:32 -0700 Subject: [PATCH 0073/1530] Separate buffer width and stride bug:80105792 Test: Disable hw vp9 decoder, and modify SoftVPX to use different stride and width, test VP9/VP9 profile2 streams playback in Photos. Test: HEIF decoding. Change-Id: I97364accc5f610bb28c44f7da2e8ae57a30ab581 --- media/libstagefright/FrameDecoder.cpp | 16 +++--- .../colorconversion/ColorConverter.cpp | 44 +++++++++------- .../colorconversion/SoftwareRenderer.cpp | 50 ++++++++----------- .../libstagefright/include/SoftwareRenderer.h | 2 +- .../media/stagefright/ColorConverter.h | 6 +-- .../omx/SoftVideoDecoderOMXComponent.cpp | 4 +- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp index 29a219f5ff..1a185ab772 100644 --- a/media/libstagefright/FrameDecoder.cpp +++ b/media/libstagefright/FrameDecoder.cpp @@ -498,9 +498,10 @@ status_t VideoFrameDecoder::onOutputReceived( return ERROR_MALFORMED; } - int32_t width, height; + int32_t width, height, stride; CHECK(outputFormat->findInt32("width", &width)); CHECK(outputFormat->findInt32("height", &height)); + CHECK(outputFormat->findInt32("stride", &stride)); int32_t crop_left, crop_top, crop_right, crop_bottom; if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) { @@ -527,11 +528,10 @@ status_t VideoFrameDecoder::onOutputReceived( if (converter.isValid()) { converter.convert( (const uint8_t *)videoFrameBuffer->data(), - width, height, + width, height, stride, crop_left, crop_top, crop_right, crop_bottom, frame->getFlattenedData(), - frame->mWidth, - frame->mHeight, + frame->mWidth, frame->mHeight, frame->mRowBytes, crop_left, crop_top, crop_right, crop_bottom); return OK; } @@ -678,9 +678,10 @@ status_t ImageDecoder::onOutputReceived( return ERROR_MALFORMED; } - int32_t width, height; + int32_t width, height, stride; CHECK(outputFormat->findInt32("width", &width)); CHECK(outputFormat->findInt32("height", &height)); + CHECK(outputFormat->findInt32("stride", &stride)); if (mFrame == NULL) { sp frameMem = allocVideoFrame( @@ -724,11 +725,10 @@ status_t ImageDecoder::onOutputReceived( if (converter.isValid()) { converter.convert( (const uint8_t *)videoFrameBuffer->data(), - width, height, + width, height, stride, crop_left, crop_top, crop_right, crop_bottom, mFrame->getFlattenedData(), - mFrame->mWidth, - mFrame->mHeight, + mFrame->mWidth, mFrame->mHeight, mFrame->mRowBytes, dstLeft, dstTop, dstRight, dstBottom); return OK; } diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index 05f4104b69..c46a40f0b7 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -85,9 +85,15 @@ bool ColorConverter::isDstRGB() const { || mDstFormat == OMX_COLOR_Format32bitBGRA8888; } +/* + * If stride is non-zero, client's stride will be used. For planar + * or semi-planar YUV formats, stride must be even numbers. + * If stride is zero, it will be calculated based on width and bpp + * of the format, assuming no padding on the right edge. + */ ColorConverter::BitmapParams::BitmapParams( void *bits, - size_t width, size_t height, + size_t width, size_t height, size_t stride, size_t cropLeft, size_t cropTop, size_t cropRight, size_t cropBottom, OMX_COLOR_FORMATTYPE colorFromat) @@ -101,6 +107,8 @@ ColorConverter::BitmapParams::BitmapParams( mCropBottom(cropBottom) { switch(mColorFormat) { case OMX_COLOR_Format16bitRGB565: + case OMX_COLOR_FormatYUV420Planar16: + case OMX_COLOR_FormatCbYCrY: mBpp = 2; mStride = 2 * mWidth; break; @@ -112,13 +120,7 @@ ColorConverter::BitmapParams::BitmapParams( mStride = 4 * mWidth; break; - case OMX_COLOR_FormatYUV420Planar16: - mBpp = 2; - mStride = 2 * mWidth; - break; - case OMX_COLOR_FormatYUV420Planar: - case OMX_COLOR_FormatCbYCrY: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: @@ -132,6 +134,10 @@ ColorConverter::BitmapParams::BitmapParams( mStride = mWidth; break; } + // use client's stride if it's specified. + if (stride != 0) { + mStride = stride; + } } size_t ColorConverter::BitmapParams::cropWidth() const { @@ -144,21 +150,21 @@ size_t ColorConverter::BitmapParams::cropHeight() const { status_t ColorConverter::convert( const void *srcBits, - size_t srcWidth, size_t srcHeight, + size_t srcWidth, size_t srcHeight, size_t srcStride, size_t srcCropLeft, size_t srcCropTop, size_t srcCropRight, size_t srcCropBottom, void *dstBits, - size_t dstWidth, size_t dstHeight, + size_t dstWidth, size_t dstHeight, size_t dstStride, size_t dstCropLeft, size_t dstCropTop, size_t dstCropRight, size_t dstCropBottom) { BitmapParams src( const_cast(srcBits), - srcWidth, srcHeight, + srcWidth, srcHeight, srcStride, srcCropLeft, srcCropTop, srcCropRight, srcCropBottom, mSrcFormat); BitmapParams dst( dstBits, - dstWidth, dstHeight, + dstWidth, dstHeight, dstStride, dstCropLeft, dstCropTop, dstCropRight, dstCropBottom, mDstFormat); if (!((src.mCropLeft & 1) == 0 @@ -792,15 +798,15 @@ status_t ColorConverter::convertYUV420SemiPlanar( uint8_t *kAdjustedClip = initClip(); - uint16_t *dst_ptr = (uint16_t *)dst.mBits - + dst.mCropTop * dst.mWidth + dst.mCropLeft; + uint16_t *dst_ptr = (uint16_t *)((uint8_t *) + dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp); const uint8_t *src_y = - (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft; + (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft; const uint8_t *src_u = - (const uint8_t *)src_y + src.mWidth * src.mHeight - + src.mCropTop * src.mWidth + src.mCropLeft; + (const uint8_t *)src.mBits + src.mHeight * src.mStride + + src.mCropTop * src.mStride / 2 + src.mCropLeft; for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { @@ -842,13 +848,13 @@ status_t ColorConverter::convertYUV420SemiPlanar( } } - src_y += src.mWidth; + src_y += src.mStride; if (y & 1) { - src_u += src.mWidth; + src_u += src.mStride; } - dst_ptr += dst.mWidth; + dst_ptr = (uint16_t*)((uint8_t*)dst_ptr + dst.mStride); } return OK; diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 657a05b420..359df3d540 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -44,6 +44,7 @@ SoftwareRenderer::SoftwareRenderer( mNativeWindow(nativeWindow), mWidth(0), mHeight(0), + mStride(0), mCropLeft(0), mCropTop(0), mCropRight(0), @@ -67,9 +68,10 @@ void SoftwareRenderer::resetFormatIfChanged( int32_t colorFormatNew; CHECK(format->findInt32("color-format", &colorFormatNew)); - int32_t widthNew, heightNew; - CHECK(format->findInt32("stride", &widthNew)); + int32_t widthNew, heightNew, strideNew; + CHECK(format->findInt32("width", &widthNew)); CHECK(format->findInt32("slice-height", &heightNew)); + CHECK(format->findInt32("stride", &strideNew)); int32_t cropLeftNew, cropTopNew, cropRightNew, cropBottomNew; if (!format->findRect( @@ -106,6 +108,7 @@ void SoftwareRenderer::resetFormatIfChanged( mColorFormat = static_cast(colorFormatNew); mWidth = widthNew; mHeight = heightNew; + mStride = strideNew; mCropLeft = cropLeftNew; mCropTop = cropTopNew; mCropRight = cropRightNew; @@ -276,20 +279,15 @@ std::list SoftwareRenderer::render( if (mConverter) { mConverter->convert( data, - mWidth, mHeight, + mWidth, mHeight, mStride, mCropLeft, mCropTop, mCropRight, mCropBottom, dst, - buf->stride, buf->height, + buf->stride, buf->height, 0, 0, 0, mCropWidth - 1, mCropHeight - 1); } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) { - const uint8_t *src_y = (const uint8_t *)data; - const uint8_t *src_u = - (const uint8_t *)data + mWidth * mHeight; - const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2); - - src_y +=mCropLeft + mCropTop * mWidth; - src_u +=(mCropLeft + mCropTop * mWidth / 2)/2; - src_v +=(mCropLeft + mCropTop * mWidth / 2)/2; + const uint8_t *src_y = (const uint8_t *)data + mCropTop * mStride + mCropLeft; + const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4; + const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4; uint8_t *dst_y = (uint8_t *)dst; size_t dst_y_size = buf->stride * buf->height; @@ -305,7 +303,7 @@ std::list SoftwareRenderer::render( for (int y = 0; y < mCropHeight; ++y) { memcpy(dst_y, src_y, mCropWidth); - src_y += mWidth; + src_y += mStride; dst_y += buf->stride; } @@ -313,19 +311,15 @@ std::list SoftwareRenderer::render( memcpy(dst_u, src_u, (mCropWidth + 1) / 2); memcpy(dst_v, src_v, (mCropWidth + 1) / 2); - src_u += mWidth / 2; - src_v += mWidth / 2; + src_u += mStride / 2; + src_v += mStride / 2; dst_u += dst_c_stride; dst_v += dst_c_stride; } } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) { - const uint16_t *src_y = (const uint16_t *)data; - const uint16_t *src_u = (const uint16_t *)data + mWidth * mHeight; - const uint16_t *src_v = src_u + (mWidth / 2 * mHeight / 2); - - src_y += mCropLeft + mCropTop * mWidth; - src_u += (mCropLeft + mCropTop * mWidth / 2) / 2; - src_v += (mCropLeft + mCropTop * mWidth / 2) / 2; + const uint8_t *src_y = (const uint8_t *)data + mCropTop * mStride + mCropLeft * 2; + const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4; + const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4; uint8_t *dst_y = (uint8_t *)dst; size_t dst_y_size = buf->stride * buf->height; @@ -340,21 +334,21 @@ std::list SoftwareRenderer::render( for (int y = 0; y < mCropHeight; ++y) { for (int x = 0; x < mCropWidth; ++x) { - dst_y[x] = (uint8_t)(src_y[x] >> 2); + dst_y[x] = (uint8_t)(((uint16_t *)src_y)[x] >> 2); } - src_y += mWidth; + src_y += mStride; dst_y += buf->stride; } for (int y = 0; y < (mCropHeight + 1) / 2; ++y) { for (int x = 0; x < (mCropWidth + 1) / 2; ++x) { - dst_u[x] = (uint8_t)(src_u[x] >> 2); - dst_v[x] = (uint8_t)(src_v[x] >> 2); + dst_u[x] = (uint8_t)(((uint16_t *)src_u)[x] >> 2); + dst_v[x] = (uint8_t)(((uint16_t *)src_v)[x] >> 2); } - src_u += mWidth / 2; - src_v += mWidth / 2; + src_u += mStride / 2; + src_v += mStride / 2; dst_u += dst_c_stride; dst_v += dst_c_stride; } diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h index c2865169d2..64dca4e722 100644 --- a/media/libstagefright/include/SoftwareRenderer.h +++ b/media/libstagefright/include/SoftwareRenderer.h @@ -51,7 +51,7 @@ private: ColorConverter *mConverter; YUVMode mYUVMode; sp mNativeWindow; - int32_t mWidth, mHeight; + int32_t mWidth, mHeight, mStride; int32_t mCropLeft, mCropTop, mCropRight, mCropBottom; int32_t mCropWidth, mCropHeight; int32_t mRotationDegrees; diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h index 5b3543de66..2b8c7c84d8 100644 --- a/media/libstagefright/include/media/stagefright/ColorConverter.h +++ b/media/libstagefright/include/media/stagefright/ColorConverter.h @@ -37,11 +37,11 @@ struct ColorConverter { status_t convert( const void *srcBits, - size_t srcWidth, size_t srcHeight, + size_t srcWidth, size_t srcHeight, size_t srcStride, size_t srcCropLeft, size_t srcCropTop, size_t srcCropRight, size_t srcCropBottom, void *dstBits, - size_t dstWidth, size_t dstHeight, + size_t dstWidth, size_t dstHeight, size_t dstStride, size_t dstCropLeft, size_t dstCropTop, size_t dstCropRight, size_t dstCropBottom); @@ -49,7 +49,7 @@ private: struct BitmapParams { BitmapParams( void *bits, - size_t width, size_t height, + size_t width, size_t height, size_t stride, size_t cropLeft, size_t cropTop, size_t cropRight, size_t cropBottom, OMX_COLOR_FORMATTYPE colorFromat); diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index cd0f75c96b..361b873791 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -154,12 +154,12 @@ void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop, bool u outDef->format.video.nFrameWidth = outputBufferWidth(); outDef->format.video.nFrameHeight = outputBufferHeight(); outDef->format.video.eColorFormat = mOutputFormat; - outDef->format.video.nStride = outDef->format.video.nFrameWidth; outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight; int32_t bpp = (mOutputFormat == OMX_COLOR_FormatYUV420Planar16) ? 2 : 1; + outDef->format.video.nStride = outDef->format.video.nFrameWidth * bpp; outDef->nBufferSize = - (outDef->format.video.nStride * outDef->format.video.nSliceHeight * bpp * 3) / 2; + (outDef->format.video.nStride * outDef->format.video.nSliceHeight * 3) / 2; OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef; inDef->format.video.nFrameWidth = mWidth; -- GitLab From 708e038f7e6c452de8186e2d6981de7540495094 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 30 May 2018 09:53:04 -0700 Subject: [PATCH 0074/1530] audiopolicy: Brush up DeviceVector::getDevice... methods The semantics of these methods wasn't consistent and wasn't clear from their names (and aggravated by the fact that audio_devices_t can be used either as a single device, or as a mask): * sp getDevice(type, address) -- 'type' is a single device; if 'address' is an empty string, and no matching device with an empty address is found, returns any device of this type; * DeviceVector getDevicesFromType(type) -- 'type' is a mask, device addresses get ignored; * DeviceVector getDevicesFromTypeAddr(type, address) -- 'type' is a single device, address is matched exactly. So in practice, only 1 device could be returned. Changes: * 'getDevicesFromType' renamed to 'getDevicesFromTypeMask' -- emhpasizes the fact that 'type' can be a mask; * 'getDevicesFromTypeAddr' removed, usages replaced with a call to 'getDevice'. There were just 2 usages, and their intent seem to be covered by the semantics of 'getDevice'. Test: verified basic audio functionality and Auto with simulator Change-Id: If3e42e4c06f42573fd33ba1303febe0625e079e6 --- .../include/DeviceDescriptor.h | 7 +++-- .../managerdefinitions/include/HwModule.h | 2 +- .../src/DeviceDescriptor.cpp | 26 +++++----------- .../managerdefinitions/src/HwModule.cpp | 17 ++++------ .../managerdefinitions/src/SessionRoute.cpp | 2 +- .../audiopolicy/enginedefault/src/Engine.cpp | 2 +- .../managerdefault/AudioPolicyManager.cpp | 31 ++++++++++--------- 7 files changed, 37 insertions(+), 50 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index 92a4c3e9e5..2325e4f574 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -76,11 +76,12 @@ public: audio_devices_t types() const { return mDeviceTypes; } - sp getDevice(audio_devices_t type, const String8& address) const; - DeviceVector getDevicesFromType(audio_devices_t types) const; + // If 'address' is empty, a device with a non-empty address may be returned + // if there is no device with the specified 'type' and empty address. + sp getDevice(audio_devices_t type, const String8 &address) const; + DeviceVector getDevicesFromTypeMask(audio_devices_t types) const; sp getDeviceFromId(audio_port_handle_t id) const; sp getDeviceFromTagName(const String8 &tagName) const; - DeviceVector getDevicesFromTypeAddr(audio_devices_t type, const String8& address) const; audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h index cb9f49ede6..05cfc312a1 100644 --- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -107,7 +107,7 @@ public: sp getDeviceDescriptor(const audio_devices_t device, const char *device_address, const char *device_name, - bool matchAdress = true) const; + bool matchAddress = true) const; status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 19c2062cd5..d3cc8b90bb 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -145,8 +145,8 @@ sp DeviceVector::getDevice(audio_devices_t type, const String8 } } } - ALOGV("DeviceVector::getDevice() for type %08x address %s found %p", - type, address.string(), device.get()); + ALOGV("DeviceVector::%s() for type %08x address \"%s\" found %p", + __func__, type, address.string(), device.get()); return device; } @@ -160,7 +160,7 @@ sp DeviceVector::getDeviceFromId(audio_port_handle_t id) const return nullptr; } -DeviceVector DeviceVector::getDevicesFromType(audio_devices_t type) const +DeviceVector DeviceVector::getDevicesFromTypeMask(audio_devices_t type) const { DeviceVector devices; bool isOutput = audio_is_output_devices(type); @@ -171,20 +171,8 @@ DeviceVector DeviceVector::getDevicesFromType(audio_devices_t type) const if ((isOutput == curIsOutput) && ((type & curType) != 0)) { devices.add(itemAt(i)); type &= ~curType; - ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", - itemAt(i)->type(), itemAt(i).get()); - } - } - return devices; -} - -DeviceVector DeviceVector::getDevicesFromTypeAddr( - audio_devices_t type, const String8& address) const -{ - DeviceVector devices; - for (const auto& device : *this) { - if (device->type() == type && device->mAddress == address) { - devices.add(device); + ALOGV("DeviceVector::%s() for type %08x found %p", + __func__, itemAt(i)->type(), itemAt(i).get()); } } return devices; @@ -253,7 +241,7 @@ void DeviceDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, void DeviceDescriptor::toAudioPort(struct audio_port *port) const { - ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); + ALOGV("DeviceDescriptor::toAudioPort() handle %d type %08x", mId, mDeviceType); AudioPort::toAudioPort(port); port->id = mId; toAudioPortConfig(&port->active_config); @@ -305,7 +293,7 @@ void DeviceDescriptor::log() const { std::string device; deviceToString(mDeviceType, device); - ALOGI("Device id:%d type:0x%X:%s, addr:%s", mId, mDeviceType, device.c_str(), + ALOGI("Device id:%d type:0x%08X:%s, addr:%s", mId, mDeviceType, device.c_str(), mAddress.string()); AudioPort::log(" "); diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index aef7dbe6eb..dcc0ec89b8 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -278,9 +278,10 @@ sp HwModuleCollection::getModuleForDevice(audio_devices_t device) con sp HwModuleCollection::getDeviceDescriptor(const audio_devices_t device, const char *device_address, const char *device_name, - bool matchAdress) const + bool matchAddress) const { - String8 address = (device_address == nullptr) ? String8("") : String8(device_address); + String8 address = (device_address == nullptr || !matchAddress) ? + String8("") : String8(device_address); // handle legacy remote submix case where the address was not always specified if (device_distinguishes_on_address(device) && (address.length() == 0)) { address = String8("0"); @@ -288,15 +289,9 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices for (const auto& hwModule : *this) { DeviceVector declaredDevices = hwModule->getDeclaredDevices(); - DeviceVector deviceList = declaredDevices.getDevicesFromTypeAddr(device, address); - if (!deviceList.isEmpty()) { - return deviceList.itemAt(0); - } - if (!matchAdress) { - deviceList = declaredDevices.getDevicesFromType(device); - if (!deviceList.isEmpty()) { - return deviceList.itemAt(0); - } + sp deviceDesc = declaredDevices.getDevice(device, address); + if (deviceDesc) { + return deviceDesc; } } diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp index d34214b72e..38ab560403 100644 --- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp @@ -129,7 +129,7 @@ audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t st if (streamType == route->mStreamType && route->isActiveOrChanged() && route->mDeviceDescriptor != 0) { device = route->mDeviceDescriptor->type(); - if (!availableDevices.getDevicesFromType(device).isEmpty()) { + if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) { break; } } diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 3e13e50a44..007eea0e40 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -482,7 +482,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, } } availableOutputDevices = - availableOutputDevices.getDevicesFromType(availableOutputDevicesType); + availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType); if (outputs.isStreamActive(AUDIO_STREAM_RING) || outputs.isStreamActive(AUDIO_STREAM_ALARM)) { return getDeviceForStrategyInt( diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 418604af9d..0a8bed1f16 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -534,7 +534,7 @@ sp AudioPolicyManager::createTelephonyPatch( sp AudioPolicyManager::findDevice( const DeviceVector& devices, audio_devices_t device) { - DeviceVector deviceList = devices.getDevicesFromType(device); + DeviceVector deviceList = devices.getDevicesFromTypeMask(device); ALOG_ASSERT(!deviceList.isEmpty(), "%s() selected device type %#x is not in devices list", __func__, device); return deviceList.itemAt(0); @@ -865,7 +865,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, return INVALID_OPERATION; } - DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device); + DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; @@ -970,7 +970,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( sp outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface); - DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device); + DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress : String8(""); @@ -1539,7 +1539,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (*portId == AUDIO_PORT_HANDLE_NONE) { *portId = AudioPort::getNextUniqueId(); } - inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice); + inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(inputDesc->mDevice); *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session); @@ -1606,7 +1606,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, goto error; } - inputDevices = mAvailableInputDevices.getDevicesFromType(device); + inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; @@ -1774,7 +1774,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, lConfig.format = profileFormat; if (address == "") { - DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device); + DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); // the inputs vector must be of size >= 1, but we don't want to crash here address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8(""); } @@ -3568,7 +3568,7 @@ status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats } } // Open an output to query dynamic parameters. - DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromType( + DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromTypeMask( AUDIO_DEVICE_OUT_HDMI); for (size_t i = 0; i < hdmiOutputDevices.size(); i++) { String8 address = hdmiOutputDevices[i]->mAddress; @@ -3694,7 +3694,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat sp outputDesc; bool profileUpdated = false; - DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromType( + DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromTypeMask( AUDIO_DEVICE_OUT_HDMI); for (size_t i = 0; i < hdmiOutputDevices.size(); i++) { // Simulate reconnection to update enabled surround sound formats. @@ -3713,7 +3713,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat name.c_str()); profileUpdated |= (status == NO_ERROR); } - DeviceVector hdmiInputDevices = mAvailableInputDevices.getDevicesFromType( + DeviceVector hdmiInputDevices = mAvailableInputDevices.getDevicesFromTypeMask( AUDIO_DEVICE_IN_HDMI); for (size_t i = 0; i < hdmiInputDevices.size(); i++) { // Simulate reconnection to update enabled surround sound formats. @@ -3980,7 +3980,8 @@ status_t AudioPolicyManager::initialize() { sp outputDesc = new SwAudioOutputDescriptor(outProfile, mpClientInterface); const DeviceVector &supportedDevices = outProfile->getSupportedDevices(); - const DeviceVector &devicesForType = supportedDevices.getDevicesFromType(profileType); + const DeviceVector &devicesForType = supportedDevices.getDevicesFromTypeMask( + profileType); String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress : String8(""); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; @@ -4034,7 +4035,7 @@ status_t AudioPolicyManager::initialize() { sp inputDesc = new AudioInputDescriptor(inProfile, mpClientInterface); - DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(profileType); + DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(profileType); // the inputs vector must be of size >= 1, but we don't want to crash here String8 address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8(""); @@ -5199,9 +5200,11 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou } else { DeviceVector deviceList; if ((address == NULL) || (strlen(address) == 0)) { - deviceList = mAvailableOutputDevices.getDevicesFromType(device); + deviceList = mAvailableOutputDevices.getDevicesFromTypeMask(device); } else { - deviceList = mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address)); + sp deviceDesc = mAvailableOutputDevices.getDevice( + device, String8(address)); + if (deviceDesc) deviceList.add(deviceDesc); } if (!deviceList.isEmpty()) { @@ -5268,7 +5271,7 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) { inputDesc->mDevice = device; - DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device); + DeviceVector deviceList = mAvailableInputDevices.getDevicesFromTypeMask(device); if (!deviceList.isEmpty()) { PatchBuilder patchBuilder; patchBuilder.addSink(inputDesc, -- GitLab From 2a4e16168061c0516c48b93450917eede7723c44 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 15:06:09 -0700 Subject: [PATCH 0075/1530] AudioTrackShared: Move framesReadySafe to ServerProxy Allows common use for latency computation. Implementation for AudioRecordServerProxy added. Test: audio sanity, subsequent latency tests Bug: 80447764 Change-Id: Ib53181cdced16606bf268769a04bd52c689d89d3 --- include/private/media/AudioTrackShared.h | 8 ++++++-- media/libaudioclient/AudioTrackShared.cpp | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 518cc632cb..5f19f74bd1 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -555,6 +555,9 @@ public: // Total count of the number of flushed frames since creation (never reset). virtual int64_t framesFlushed() const { return mFlushed; } + // Safe frames ready query with no side effects. + virtual size_t framesReadySafe() const = 0; + // Get dynamic buffer size from the shared control block. uint32_t getBufferSizeInFrames() const { return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames); @@ -592,8 +595,7 @@ public: // which may include non-contiguous frames virtual size_t framesReady(); - // Safe frames ready query used by dump() - this has no side effects. - virtual size_t framesReadySafe() const; + size_t framesReadySafe() const override; // frames available to read by server. // Currently AudioFlinger will call framesReady() for a fast track from two threads: // FastMixer thread, and normal mixer thread. This is dangerous, as the proxy is intended @@ -697,6 +699,8 @@ public: return mCblk->u.mStreaming.mRear; // For completeness only; mRear written by server. } + size_t framesReadySafe() const override; // frames available to read by client. + protected: virtual ~AudioRecordServerProxy() { } }; diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp index dced3c4389..a018b22af6 100644 --- a/media/libaudioclient/AudioTrackShared.cpp +++ b/media/libaudioclient/AudioTrackShared.cpp @@ -1231,6 +1231,21 @@ int32_t StaticAudioTrackServerProxy::getRear() const return 0; } +__attribute__((no_sanitize("integer"))) +size_t AudioRecordServerProxy::framesReadySafe() const +{ + if (mIsShutdown) { + return 0; + } + const int32_t front = android_atomic_acquire_load(&mCblk->u.mStreaming.mFront); + const int32_t rear = mCblk->u.mStreaming.mRear; + const ssize_t filled = rear - front; + if (!(0 <= filled && (size_t) filled <= mFrameCount)) { + return 0; // error condition, silently return 0. + } + return filled; +} + // --------------------------------------------------------------------------- } // namespace android -- GitLab From 885957513e8e86453bac323de6f6e562cc174050 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 15:21:47 -0700 Subject: [PATCH 0076/1530] AudioTimestamp: check zero sample rate in latency computation Test: compile, no material difference Change-Id: I27a49ee56a16cec6937ca493ba4b1fbe0bb4121e --- media/libaudioclient/include/media/AudioTimestamp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h index 2cd8c87987..e5925dd942 100644 --- a/media/libaudioclient/include/media/AudioTimestamp.h +++ b/media/libaudioclient/include/media/AudioTimestamp.h @@ -140,7 +140,7 @@ struct alignas(8) /* bug 29096183, bug 29108507 */ ExtendedTimestamp { } double getLatencyMs(uint32_t sampleRate, Location location1, Location location2) const { - if (mTimeNs[location1] > 0 && mTimeNs[location2] > 0) { + if (sampleRate > 0 && mTimeNs[location1] > 0 && mTimeNs[location2] > 0) { const int64_t frameDifference = mPosition[location1] - mPosition[location2]; const int64_t timeDifferenceNs = -- GitLab From 20bd30baf9fcd59e7000e7c626cc8bc1e85637bc Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 15:39:35 -0700 Subject: [PATCH 0077/1530] AudioFlinger: Add latency information for RecordThread Test: audioflinger dumpsys Bug: 80493498 Change-Id: I52e481432b73f019c4eea06f1e11e41ad8a79b5c --- services/audioflinger/Threads.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index ee4283ee64..27d39191a7 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7333,6 +7333,13 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a (void)input->stream->dump(fd); } + const double latencyMs = - mTimestamp.getOutputServerLatencyMs(mSampleRate); + if (latencyMs != 0.) { + dprintf(fd, " NormalRecord latency ms: %.2lf\n", latencyMs); + } else { + dprintf(fd, " NormalRecord latency ms: unavail\n"); + } + dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no"); dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); -- GitLab From cef2daaf49e8802e3377ebc604012aa244b7231a Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 15:31:49 -0700 Subject: [PATCH 0078/1530] AudioFlinger: refine latency computation if track is drained Use server timestamp if track has no actively presented frames (i.e. drained). Sometimes track frames may not have reached the HAL. Test: audioflinger dumpsys with BT audio Bug: 80447764 Change-Id: Iffc52f4cfcbadd419c6b6ccfa278e0712f3af4af --- services/audioflinger/PlaybackTracks.h | 10 ++- services/audioflinger/Threads.cpp | 5 +- services/audioflinger/TrackBase.h | 84 ++++++++++++++++++++++++++ services/audioflinger/Tracks.cpp | 49 ++++++++++----- 4 files changed, 125 insertions(+), 23 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 9a8a1544e9..3381e773db 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -94,10 +94,9 @@ public: virtual bool isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; } - virtual double bufferLatencyMs() { - return isStatic() ? 0. - : (double)mAudioTrackServerProxy->framesReadySafe() * 1000 / sampleRate(); - } + double bufferLatencyMs() const override { + return isStatic() ? 0. : TrackBase::bufferLatencyMs(); + } // implement volume handling. media::VolumeShaper::Status applyVolumeShaper( @@ -152,7 +151,7 @@ protected: bool isResumePending(); void resumeAck(); void updateTrackFrameInfo(int64_t trackFramesReleased, int64_t sinkFramesWritten, - const ExtendedTimestamp &timeStamp); + uint32_t halSampleRate, const ExtendedTimestamp &timeStamp); sp sharedBuffer() const { return mSharedBuffer; } @@ -200,7 +199,6 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations - bool mDumpLatency = false; // true if track supports latency dumps. private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 27d39191a7..70fb2b91dd 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3265,6 +3265,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() t->updateTrackFrameInfo( t->mAudioTrackServerProxy->framesReleased(), mFramesWritten, + mSampleRate, mTimestamp); } } @@ -5059,8 +5060,10 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); - if (latencyMs > 0.) { + if (latencyMs != 0.) { dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); + } else { + dprintf(fd, " NormalMixer latency ms: unavail\n"); } if (hasFastMixer()) { diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 9f17c3ce72..dafba1ee9d 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -106,6 +106,86 @@ public: } #endif + /** returns the buffer contents size converted to time in milliseconds + * for PCM Playback or Record streaming tracks. The return value is zero for + * PCM static tracks and not defined for non-PCM tracks. + * + * This may be called without the thread lock. + */ + virtual double bufferLatencyMs() const { + return mServerProxy->framesReadySafe() * 1000 / sampleRate(); + } + + /** returns whether the track supports server latency computation. + * This is set in the constructor and constant throughout the track lifetime. + */ + + bool isServerLatencySupported() const { return mServerLatencySupported; } + + /** computes the server latency for PCM Playback or Record track + * to the device sink/source. This is the time for the next frame in the track buffer + * written or read from the server thread to the device source or sink. + * + * This may be called without the thread lock, but latencyMs and fromTrack + * may be not be synchronized. For example PatchPanel may not obtain the + * thread lock before calling. + * + * \param latencyMs on success is set to the latency in milliseconds of the + * next frame written/read by the server thread to/from the track buffer + * from the device source/sink. + * \param fromTrack on success is set to true if latency was computed directly + * from the track timestamp; otherwise set to false if latency was + * estimated from the server timestamp. + * fromTrack may be nullptr or omitted if not required. + * + * \returns OK or INVALID_OPERATION on failure. + */ + status_t getServerLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { + if (!isServerLatencySupported()) { + return INVALID_OPERATION; + } + + // if no thread lock is acquired, these atomics are not + // synchronized with each other, considered a benign race. + + const double serverLatencyMs = mServerLatencyMs.load(); + if (serverLatencyMs == 0.) { + return INVALID_OPERATION; + } + if (fromTrack != nullptr) { + *fromTrack = mServerLatencyFromTrack.load(); + } + *latencyMs = serverLatencyMs; + return OK; + } + + /** computes the total client latency for PCM Playback or Record tracks + * for the next client app access to the device sink/source; i.e. the + * server latency plus the buffer latency. + * + * This may be called without the thread lock, but latencyMs and fromTrack + * may be not be synchronized. For example PatchPanel may not obtain the + * thread lock before calling. + * + * \param latencyMs on success is set to the latency in milliseconds of the + * next frame written/read by the client app to/from the track buffer + * from the device sink/source. + * \param fromTrack on success is set to true if latency was computed directly + * from the track timestamp; otherwise set to false if latency was + * estimated from the server timestamp. + * fromTrack may be nullptr or omitted if not required. + * + * \returns OK or INVALID_OPERATION on failure. + */ + status_t getTrackLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const { + double serverLatencyMs; + status_t status = getServerLatencyMs(&serverLatencyMs, fromTrack); + if (status == OK) { + *latencyMs = serverLatencyMs + bufferLatencyMs(); + } + return status; + } + protected: DISALLOW_COPY_AND_ASSIGN(TrackBase); @@ -222,6 +302,10 @@ protected: audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to audio_port_handle_t mPortId; // unique ID for this track used by audio policy bool mIsInvalid; // non-resettable latch, set by invalidate() + + bool mServerLatencySupported = false; + std::atomic mServerLatencyFromTrack{}; // latency from track or server timestamp. + std::atomic mServerLatencyMs{}; // last latency pushed from server thread. }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 6953ebfb53..37e67af51a 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -435,7 +435,7 @@ AudioFlinger::PlaybackThread::Track::Track( } mName = TRACK_NAME_PENDING; - mDumpLatency = thread->type() == ThreadBase::MIXER; + mServerLatencySupported = thread->type() == ThreadBase::MIXER; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId) + @@ -497,7 +497,7 @@ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) "ST L dB R dB VS dB " " Server FrmCnt FrmRdy F Underruns Flushed" "%s\n", - mDumpLatency ? " Latency" : ""); + isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active) @@ -593,7 +593,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ mFormat, mChannelMask, - mAudioTrackServerProxy->getSampleRate(), + sampleRate(), mStreamType, 20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))), @@ -610,14 +610,16 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ nowInUnderrun, (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000 ); - if (mDumpLatency) { - double latencyMs = - mAudioTrackServerProxy->getTimestamp().getOutputServerLatencyMs(mSampleRate); - if (latencyMs > 0.) { - latencyMs += bufferLatencyMs(); - result.appendFormat(" %7.3f", latencyMs); + + if (isServerLatencySupported()) { + double latencyMs; + bool fromTrack; + if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) { + // Show latency in msec, followed by 't' if from track timestamp (the most accurate) + // or 'k' if estimated from kernel because track frames haven't been presented yet. + result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k'); } else { - result.appendFormat(" Unknown"); + result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new"); } } result.append("\n"); @@ -677,6 +679,13 @@ void AudioFlinger::PlaybackThread::Track::onTimestamp(const ExtendedTimestamp &t mAudioTrackServerProxy->setTimestamp(timestamp); // We do not set drained here, as FastTrack timestamp may not go to very last frame. + + // Compute latency. + // TODO: Consider whether the server latency may be passed in by FastMixer + // as a constant for all active FastTracks. + const double latencyMs = timestamp.getOutputServerLatencyMs(sampleRate()); + mServerLatencyFromTrack.store(true); + mServerLatencyMs.store(latencyMs); } // Don't call for fast tracks; the framesReady() could result in priority inversion @@ -1241,7 +1250,7 @@ void AudioFlinger::PlaybackThread::Track::resumeAck() { //To be called with thread lock held void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( int64_t trackFramesReleased, int64_t sinkFramesWritten, - const ExtendedTimestamp &timeStamp) { + uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) { //update frame map mFrameMap.push(trackFramesReleased, sinkFramesWritten); @@ -1250,6 +1259,7 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( // Our timestamps are only updated when the track is on the Thread active list. // We need to ensure that tracks are not removed before full drain. ExtendedTimestamp local = timeStamp; + bool drained = true; // default assume drained, if no server info found bool checked = false; for (int i = ExtendedTimestamp::LOCATION_MAX - 1; i >= ExtendedTimestamp::LOCATION_SERVER; --i) { @@ -1258,18 +1268,25 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( local.mPosition[i] = mFrameMap.findX(local.mPosition[i]); // check drain state from the latest stage in the pipeline. if (!checked && i <= ExtendedTimestamp::LOCATION_KERNEL) { - mAudioTrackServerProxy->setDrained( - local.mPosition[i] >= mAudioTrackServerProxy->framesReleased()); + drained = local.mPosition[i] >= mAudioTrackServerProxy->framesReleased(); checked = true; } } } - if (!checked) { // no server info, assume drained. - mAudioTrackServerProxy->setDrained(true); - } + + mAudioTrackServerProxy->setDrained(drained); // Set correction for flushed frames that are not accounted for in released. local.mFlushed = mAudioTrackServerProxy->framesFlushed(); mServerProxy->setTimestamp(local); + + // Compute latency info. + const bool useTrackTimestamp = !drained; + const double latencyMs = useTrackTimestamp + ? local.getOutputServerLatencyMs(sampleRate()) + : timeStamp.getOutputServerLatencyMs(halSampleRate); + + mServerLatencyFromTrack.store(useTrackTimestamp); + mServerLatencyMs.store(latencyMs); } // ---------------------------------------------------------------------------- -- GitLab From 000adb5e9135a739b5e3a0607604cdf6be6fe11b Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 15:43:26 -0700 Subject: [PATCH 0079/1530] AudioFlinger: Add latency information for RecordTrack Test: audioflinger dumpsys Bug: 80493498 Change-Id: I902783b7705f69f00a5f5bd713d93d8657ba3ccb --- services/audioflinger/RecordTracks.h | 2 +- services/audioflinger/Threads.cpp | 4 ++-- services/audioflinger/Tracks.cpp | 32 +++++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index fc2dbbba8c..43b6391fbb 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -51,7 +51,7 @@ public: bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } - static void appendDumpHeader(String8& result); + void appendDumpHeader(String8& result); void appendDump(String8& result, bool active); void handleSyncStartEvent(const sp& event); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 70fb2b91dd..d61b1f0d15 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7366,7 +7366,7 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector& args if (numtracks) { dprintf(fd, " of which %zu are active\n", numactive); result.append(prefix); - RecordTrack::appendDumpHeader(result); + mTracks[0]->appendDumpHeader(result); for (size_t i = 0; i < numtracks ; ++i) { sp track = mTracks[i]; if (track != 0) { @@ -7386,7 +7386,7 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector& args result.append(" The following tracks are in the active list but" " not in the track list\n"); result.append(prefix); - RecordTrack::appendDumpHeader(result); + mActiveTracks[0]->appendDumpHeader(result); for (size_t i = 0; i < numactive; ++i) { sp track = mActiveTracks[i]; if (mTracks.indexOf(track) < 0) { diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 37e67af51a..5966f85814 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1706,6 +1706,9 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( if (flags & AUDIO_INPUT_FLAG_FAST) { ALOG_ASSERT(thread->mFastTrackAvail); thread->mFastTrackAvail = false; + } else { + // TODO: only Normal Record has timestamps (Fast Record does not). + mServerLatencySupported = true; } #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) @@ -1800,16 +1803,17 @@ void AudioFlinger::RecordThread::RecordTrack::invalidate() } -/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) +void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) { - result.append("Active Client Session S Flags Format Chn mask SRate Server FrmCnt Sil\n"); + result.appendFormat("Active Client Session S Flags Format Chn mask SRate Server" + " FrmCnt FrmRdy Sil%s\n", isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool active) { result.appendFormat("%c%5s %6u %7u %2s 0x%03X " "%08X %08X %6u " - "%08X %6zu %3c\n", + "%08X %6zu %6zu %3c", isFastTrack() ? 'F' : ' ', active ? "yes" : "no", (mClient == 0) ? getpid() : mClient->pid(), @@ -1823,8 +1827,21 @@ void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool a mCblk->mServer, mFrameCount, + mServerProxy->framesReadySafe(), isSilenced() ? 's' : 'n' ); + if (isServerLatencySupported()) { + double latencyMs; + bool fromTrack; + if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) { + // Show latency in msec, followed by 't' if from track timestamp (the most accurate) + // or 'k' if estimated from kernel (usually for debugging). + result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k'); + } else { + result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new"); + } + } + result.append("\n"); } void AudioFlinger::RecordThread::RecordTrack::handleSyncStartEvent(const sp& event) @@ -1867,6 +1884,15 @@ void AudioFlinger::RecordThread::RecordTrack::updateTrackFrameInfo( } } mServerProxy->setTimestamp(local); + + // Compute latency info. + const bool useTrackTimestamp = true; // use track unless debugging. + const double latencyMs = - (useTrackTimestamp + ? local.getOutputServerLatencyMs(sampleRate()) + : timestamp.getOutputServerLatencyMs(halSampleRate)); + + mServerLatencyFromTrack.store(useTrackTimestamp); + mServerLatencyMs.store(latencyMs); } status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones( -- GitLab From 2fb209685730d73f5fc692d3bca8da7f9ceacad1 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 4 Jun 2018 15:36:56 +0100 Subject: [PATCH 0080/1530] Remove common_time code This code is unused. Bug: 80462439 Test: Build / boot Change-Id: I1cfac4bdd59f1b8a9231abc49d3311637f71ce88 --- include/common_time/ICommonClock.h | 108 ----- include/common_time/ICommonTimeConfig.h | 73 ---- include/common_time/cc_helper.h | 72 ---- include/common_time/local_clock.h | 47 --- media/common_time/Android.mk | 24 -- media/common_time/ICommonClock.cpp | 433 ------------------- media/common_time/ICommonTimeConfig.cpp | 509 ----------------------- media/common_time/MODULE_LICENSE_APACHE2 | 0 media/common_time/NOTICE | 190 --------- media/common_time/cc_helper.cpp | 129 ------ media/common_time/local_clock.cpp | 92 ---- media/common_time/utils.cpp | 89 ---- media/common_time/utils.h | 34 -- 13 files changed, 1800 deletions(-) delete mode 100644 include/common_time/ICommonClock.h delete mode 100644 include/common_time/ICommonTimeConfig.h delete mode 100644 include/common_time/cc_helper.h delete mode 100644 include/common_time/local_clock.h delete mode 100644 media/common_time/Android.mk delete mode 100644 media/common_time/ICommonClock.cpp delete mode 100644 media/common_time/ICommonTimeConfig.cpp delete mode 100644 media/common_time/MODULE_LICENSE_APACHE2 delete mode 100644 media/common_time/NOTICE delete mode 100644 media/common_time/cc_helper.cpp delete mode 100644 media/common_time/local_clock.cpp delete mode 100644 media/common_time/utils.cpp delete mode 100644 media/common_time/utils.h diff --git a/include/common_time/ICommonClock.h b/include/common_time/ICommonClock.h deleted file mode 100644 index d7073f1337..0000000000 --- a/include/common_time/ICommonClock.h +++ /dev/null @@ -1,108 +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_ICOMMONCLOCK_H -#define ANDROID_ICOMMONCLOCK_H - -#include -#include - -#include -#include - -namespace android { - -class ICommonClockListener : public IInterface { - public: - DECLARE_META_INTERFACE(CommonClockListener); - - virtual void onTimelineChanged(uint64_t timelineID) = 0; -}; - -class BnCommonClockListener : public BnInterface { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -class ICommonClock : public IInterface { - public: - DECLARE_META_INTERFACE(CommonClock); - - // Name of the ICommonClock service registered with the service manager. - static const String16 kServiceName; - - // a reserved invalid timeline ID - static const uint64_t kInvalidTimelineID; - - // a reserved invalid error estimate - static const int32_t kErrorEstimateUnknown; - - enum State { - // the device just came up and is trying to discover the master - STATE_INITIAL, - - // the device is a client of a master - STATE_CLIENT, - - // the device is acting as master - STATE_MASTER, - - // the device has lost contact with its master and needs to participate - // in the election of a new master - STATE_RONIN, - - // the device is waiting for announcement of the newly elected master - STATE_WAIT_FOR_ELECTION, - }; - - virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) = 0; - virtual status_t commonTimeToLocalTime(int64_t commonTime, - int64_t* localTime) = 0; - virtual status_t localTimeToCommonTime(int64_t localTime, - int64_t* commonTime) = 0; - virtual status_t getCommonTime(int64_t* commonTime) = 0; - virtual status_t getCommonFreq(uint64_t* freq) = 0; - virtual status_t getLocalTime(int64_t* localTime) = 0; - virtual status_t getLocalFreq(uint64_t* freq) = 0; - virtual status_t getEstimatedError(int32_t* estimate) = 0; - virtual status_t getTimelineID(uint64_t* id) = 0; - virtual status_t getState(State* state) = 0; - virtual status_t getMasterAddr(struct sockaddr_storage* addr) = 0; - - virtual status_t registerListener( - const sp& listener) = 0; - virtual status_t unregisterListener( - const sp& listener) = 0; - - // Simple helper to make it easier to connect to the CommonClock service. - static inline sp getInstance() { - sp binder = defaultServiceManager()->checkService( - ICommonClock::kServiceName); - sp clk = interface_cast(binder); - return clk; - } -}; - -class BnCommonClock : public BnInterface { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -}; // namespace android - -#endif // ANDROID_ICOMMONCLOCK_H diff --git a/include/common_time/ICommonTimeConfig.h b/include/common_time/ICommonTimeConfig.h deleted file mode 100644 index 497b666de6..0000000000 --- a/include/common_time/ICommonTimeConfig.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2012 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_ICOMMONTIMECONFIG_H -#define ANDROID_ICOMMONTIMECONFIG_H - -#include -#include - -#include -#include - -namespace android { - -class String16; - -class ICommonTimeConfig : public IInterface { - public: - DECLARE_META_INTERFACE(CommonTimeConfig); - - // Name of the ICommonTimeConfig service registered with the service - // manager. - static const String16 kServiceName; - - virtual status_t getMasterElectionPriority(uint8_t *priority) = 0; - virtual status_t setMasterElectionPriority(uint8_t priority) = 0; - virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) = 0; - virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr) = 0; - virtual status_t getMasterElectionGroupId(uint64_t *id) = 0; - virtual status_t setMasterElectionGroupId(uint64_t id) = 0; - virtual status_t getInterfaceBinding(String16& ifaceName) = 0; - virtual status_t setInterfaceBinding(const String16& ifaceName) = 0; - virtual status_t getMasterAnnounceInterval(int *interval) = 0; - virtual status_t setMasterAnnounceInterval(int interval) = 0; - virtual status_t getClientSyncInterval(int *interval) = 0; - virtual status_t setClientSyncInterval(int interval) = 0; - virtual status_t getPanicThreshold(int *threshold) = 0; - virtual status_t setPanicThreshold(int threshold) = 0; - virtual status_t getAutoDisable(bool *autoDisable) = 0; - virtual status_t setAutoDisable(bool autoDisable) = 0; - virtual status_t forceNetworklessMasterMode() = 0; - - // Simple helper to make it easier to connect to the CommonTimeConfig service. - static inline sp getInstance() { - sp binder = defaultServiceManager()->checkService( - ICommonTimeConfig::kServiceName); - sp clk = interface_cast(binder); - return clk; - } -}; - -class BnCommonTimeConfig : public BnInterface { - public: - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -}; // namespace android - -#endif // ANDROID_ICOMMONTIMECONFIG_H diff --git a/include/common_time/cc_helper.h b/include/common_time/cc_helper.h deleted file mode 100644 index 8c4d5c063d..0000000000 --- a/include/common_time/cc_helper.h +++ /dev/null @@ -1,72 +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 __CC_HELPER_H__ -#define __CC_HELPER_H__ - -#include -#include -#include - -namespace android { - -// CCHelper is a simple wrapper class to help with centralizing access to the -// Common Clock service and implementing lifetime managment, as well as to -// implement a simple policy of making a basic attempt to reconnect to the -// common clock service when things go wrong. -// -// On platforms which run the native common_time service in auto-disable mode, -// the service will go into networkless mode whenever it has no active clients. -// It tracks active clients using registered CommonClockListeners (the callback -// interface for onTimelineChanged) since this provides a convienent death -// handler notification for when the service's clients die unexpectedly. This -// means that users of the common time service should really always have a -// CommonClockListener, unless they know that the time service is not running in -// auto disabled mode, or that there is at least one other registered listener -// active in the system. The CCHelper makes this a little easier by sharing a -// ref counted ICommonClock interface across all clients and automatically -// registering and unregistering a listener whenever there are CCHelper -// instances active in the process. -class CCHelper { - public: - CCHelper(); - ~CCHelper(); - - status_t isCommonTimeValid(bool* valid, uint32_t* timelineID); - status_t commonTimeToLocalTime(int64_t commonTime, int64_t* localTime); - status_t localTimeToCommonTime(int64_t localTime, int64_t* commonTime); - status_t getCommonTime(int64_t* commonTime); - status_t getCommonFreq(uint64_t* freq); - status_t getLocalTime(int64_t* localTime); - status_t getLocalFreq(uint64_t* freq); - - private: - class CommonClockListener : public BnCommonClockListener { - public: - void onTimelineChanged(uint64_t timelineID); - }; - - static bool verifyClock_l(); - - static Mutex lock_; - static sp common_clock_; - static sp common_clock_listener_; - static uint32_t ref_count_; -}; - - -} // namespace android -#endif // __CC_HELPER_H__ diff --git a/include/common_time/local_clock.h b/include/common_time/local_clock.h deleted file mode 100644 index 384c3dec01..0000000000 --- a/include/common_time/local_clock.h +++ /dev/null @@ -1,47 +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 __LOCAL_CLOCK_H__ -#define __LOCAL_CLOCK_H__ - -#include - -#include -#include -#include - -namespace android { - -class LocalClock { - public: - LocalClock(); - - bool initCheck(); - - int64_t getLocalTime(); - uint64_t getLocalFreq(); - status_t setLocalSlew(int16_t rate); - int32_t getDebugLog(struct local_time_debug_event* records, - int max_records); - - private: - static Mutex dev_lock_; - static local_time_hw_device_t* dev_; -}; - -} // namespace android -#endif // __LOCAL_CLOCK_H__ diff --git a/media/common_time/Android.mk b/media/common_time/Android.mk deleted file mode 100644 index aaa0db2899..0000000000 --- a/media/common_time/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -# -# libcommon_time_client -# (binder marshalers for ICommonClock as well as common clock and local clock -# helper code) -# - -include $(CLEAR_VARS) - -LOCAL_MODULE := libcommon_time_client -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := cc_helper.cpp \ - local_clock.cpp \ - ICommonClock.cpp \ - ICommonTimeConfig.cpp \ - utils.cpp -LOCAL_SHARED_LIBRARIES := libbinder \ - libhardware \ - libutils \ - liblog - -LOCAL_CFLAGS := -Wall -Werror - -include $(BUILD_SHARED_LIBRARY) diff --git a/media/common_time/ICommonClock.cpp b/media/common_time/ICommonClock.cpp deleted file mode 100644 index f1f1fcac7e..0000000000 --- a/media/common_time/ICommonClock.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2012 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 - -#include -#include - -#include "utils.h" - -namespace android { - -/***** ICommonClock *****/ - -enum { - IS_COMMON_TIME_VALID = IBinder::FIRST_CALL_TRANSACTION, - COMMON_TIME_TO_LOCAL_TIME, - LOCAL_TIME_TO_COMMON_TIME, - GET_COMMON_TIME, - GET_COMMON_FREQ, - GET_LOCAL_TIME, - GET_LOCAL_FREQ, - GET_ESTIMATED_ERROR, - GET_TIMELINE_ID, - GET_STATE, - GET_MASTER_ADDRESS, - REGISTER_LISTENER, - UNREGISTER_LISTENER, -}; - -const String16 ICommonClock::kServiceName("common_time.clock"); -const uint64_t ICommonClock::kInvalidTimelineID = 0; -const int32_t ICommonClock::kErrorEstimateUnknown = 0x7FFFFFFF; - -class BpCommonClock : public BpInterface -{ - public: - explicit BpCommonClock(const sp& impl) - : BpInterface(impl) {} - - virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(IS_COMMON_TIME_VALID, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *valid = reply.readInt32(); - *timelineID = reply.readInt32(); - } - } - return status; - } - - virtual status_t commonTimeToLocalTime(int64_t commonTime, - int64_t* localTime) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeInt64(commonTime); - status_t status = remote()->transact(COMMON_TIME_TO_LOCAL_TIME, - data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *localTime = reply.readInt64(); - } - } - return status; - } - - virtual status_t localTimeToCommonTime(int64_t localTime, - int64_t* commonTime) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeInt64(localTime); - status_t status = remote()->transact(LOCAL_TIME_TO_COMMON_TIME, - data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *commonTime = reply.readInt64(); - } - } - return status; - } - - virtual status_t getCommonTime(int64_t* commonTime) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_COMMON_TIME, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *commonTime = reply.readInt64(); - } - } - return status; - } - - virtual status_t getCommonFreq(uint64_t* freq) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_COMMON_FREQ, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *freq = reply.readInt64(); - } - } - return status; - } - - virtual status_t getLocalTime(int64_t* localTime) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_LOCAL_TIME, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *localTime = reply.readInt64(); - } - } - return status; - } - - virtual status_t getLocalFreq(uint64_t* freq) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_LOCAL_FREQ, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *freq = reply.readInt64(); - } - } - return status; - } - - virtual status_t getEstimatedError(int32_t* estimate) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_ESTIMATED_ERROR, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *estimate = reply.readInt32(); - } - } - return status; - } - - virtual status_t getTimelineID(uint64_t* id) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_TIMELINE_ID, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *id = static_cast(reply.readInt64()); - } - } - return status; - } - - virtual status_t getState(State* state) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_STATE, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *state = static_cast(reply.readInt32()); - } - } - return status; - } - - virtual status_t getMasterAddr(struct sockaddr_storage* addr) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_MASTER_ADDRESS, data, &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) - deserializeSockaddr(&reply, addr); - } - return status; - } - - virtual status_t registerListener( - const sp& listener) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(listener)); - - status_t status = remote()->transact(REGISTER_LISTENER, data, &reply); - - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t unregisterListener( - const sp& listener) { - Parcel data, reply; - data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(listener)); - status_t status = remote()->transact(UNREGISTER_LISTENER, data, &reply); - - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } -}; - -IMPLEMENT_META_INTERFACE(CommonClock, "android.os.ICommonClock"); - -status_t BnCommonClock::onTransact(uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags) { - switch(code) { - case IS_COMMON_TIME_VALID: { - CHECK_INTERFACE(ICommonClock, data, reply); - bool valid; - uint32_t timelineID; - status_t status = isCommonTimeValid(&valid, &timelineID); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(valid); - reply->writeInt32(timelineID); - } - return OK; - } break; - - case COMMON_TIME_TO_LOCAL_TIME: { - CHECK_INTERFACE(ICommonClock, data, reply); - int64_t commonTime = data.readInt64(); - int64_t localTime; - status_t status = commonTimeToLocalTime(commonTime, &localTime); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(localTime); - } - return OK; - } break; - - case LOCAL_TIME_TO_COMMON_TIME: { - CHECK_INTERFACE(ICommonClock, data, reply); - int64_t localTime = data.readInt64(); - int64_t commonTime; - status_t status = localTimeToCommonTime(localTime, &commonTime); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(commonTime); - } - return OK; - } break; - - case GET_COMMON_TIME: { - CHECK_INTERFACE(ICommonClock, data, reply); - int64_t commonTime; - status_t status = getCommonTime(&commonTime); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(commonTime); - } - return OK; - } break; - - case GET_COMMON_FREQ: { - CHECK_INTERFACE(ICommonClock, data, reply); - uint64_t freq; - status_t status = getCommonFreq(&freq); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(freq); - } - return OK; - } break; - - case GET_LOCAL_TIME: { - CHECK_INTERFACE(ICommonClock, data, reply); - int64_t localTime; - status_t status = getLocalTime(&localTime); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(localTime); - } - return OK; - } break; - - case GET_LOCAL_FREQ: { - CHECK_INTERFACE(ICommonClock, data, reply); - uint64_t freq; - status_t status = getLocalFreq(&freq); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(freq); - } - return OK; - } break; - - case GET_ESTIMATED_ERROR: { - CHECK_INTERFACE(ICommonClock, data, reply); - int32_t error; - status_t status = getEstimatedError(&error); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(error); - } - return OK; - } break; - - case GET_TIMELINE_ID: { - CHECK_INTERFACE(ICommonClock, data, reply); - uint64_t id; - status_t status = getTimelineID(&id); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(static_cast(id)); - } - return OK; - } break; - - case GET_STATE: { - CHECK_INTERFACE(ICommonClock, data, reply); - State state; - status_t status = getState(&state); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(static_cast(state)); - } - return OK; - } break; - - case GET_MASTER_ADDRESS: { - CHECK_INTERFACE(ICommonClock, data, reply); - struct sockaddr_storage addr; - status_t status = getMasterAddr(&addr); - - if ((status == OK) && !canSerializeSockaddr(&addr)) { - status = UNKNOWN_ERROR; - } - - reply->writeInt32(status); - - if (status == OK) { - serializeSockaddr(reply, &addr); - } - - return OK; - } break; - - case REGISTER_LISTENER: { - CHECK_INTERFACE(ICommonClock, data, reply); - sp listener = - interface_cast(data.readStrongBinder()); - status_t status = registerListener(listener); - reply->writeInt32(status); - return OK; - } break; - - case UNREGISTER_LISTENER: { - CHECK_INTERFACE(ICommonClock, data, reply); - sp listener = - interface_cast(data.readStrongBinder()); - status_t status = unregisterListener(listener); - reply->writeInt32(status); - return OK; - } break; - } - return BBinder::onTransact(code, data, reply, flags); -} - -/***** ICommonClockListener *****/ - -enum { - ON_TIMELINE_CHANGED = IBinder::FIRST_CALL_TRANSACTION, -}; - -class BpCommonClockListener : public BpInterface -{ - public: - explicit BpCommonClockListener(const sp& impl) - : BpInterface(impl) {} - - virtual void onTimelineChanged(uint64_t timelineID) { - Parcel data, reply; - data.writeInterfaceToken( - ICommonClockListener::getInterfaceDescriptor()); - data.writeInt64(timelineID); - remote()->transact(ON_TIMELINE_CHANGED, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(CommonClockListener, - "android.os.ICommonClockListener"); - -status_t BnCommonClockListener::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch(code) { - case ON_TIMELINE_CHANGED: { - CHECK_INTERFACE(ICommonClockListener, data, reply); - uint32_t timelineID = data.readInt64(); - onTimelineChanged(timelineID); - return NO_ERROR; - } break; - } - - return BBinder::onTransact(code, data, reply, flags); -} - -}; // namespace android diff --git a/media/common_time/ICommonTimeConfig.cpp b/media/common_time/ICommonTimeConfig.cpp deleted file mode 100644 index e587b394c8..0000000000 --- a/media/common_time/ICommonTimeConfig.cpp +++ /dev/null @@ -1,509 +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. - */ - -#include - -#include -#include - -#include "utils.h" - -namespace android { - -/***** ICommonTimeConfig *****/ - -enum { - GET_MASTER_ELECTION_PRIORITY = IBinder::FIRST_CALL_TRANSACTION, - SET_MASTER_ELECTION_PRIORITY, - GET_MASTER_ELECTION_ENDPOINT, - SET_MASTER_ELECTION_ENDPOINT, - GET_MASTER_ELECTION_GROUP_ID, - SET_MASTER_ELECTION_GROUP_ID, - GET_INTERFACE_BINDING, - SET_INTERFACE_BINDING, - GET_MASTER_ANNOUNCE_INTERVAL, - SET_MASTER_ANNOUNCE_INTERVAL, - GET_CLIENT_SYNC_INTERVAL, - SET_CLIENT_SYNC_INTERVAL, - GET_PANIC_THRESHOLD, - SET_PANIC_THRESHOLD, - GET_AUTO_DISABLE, - SET_AUTO_DISABLE, - FORCE_NETWORKLESS_MASTER_MODE, -}; - -const String16 ICommonTimeConfig::kServiceName("common_time.config"); - -class BpCommonTimeConfig : public BpInterface -{ - public: - explicit BpCommonTimeConfig(const sp& impl) - : BpInterface(impl) {} - - virtual status_t getMasterElectionPriority(uint8_t *priority) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_MASTER_ELECTION_PRIORITY, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *priority = static_cast(reply.readInt32()); - } - } - - return status; - } - - virtual status_t setMasterElectionPriority(uint8_t priority) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt32(static_cast(priority)); - status_t status = remote()->transact(SET_MASTER_ELECTION_PRIORITY, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_MASTER_ELECTION_ENDPOINT, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - deserializeSockaddr(&reply, addr); - } - } - - return status; - } - - virtual status_t setMasterElectionEndpoint( - const struct sockaddr_storage *addr) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - if (!canSerializeSockaddr(addr)) - return BAD_VALUE; - if (NULL == addr) { - data.writeInt32(0); - } else { - data.writeInt32(1); - serializeSockaddr(&data, addr); - } - status_t status = remote()->transact(SET_MASTER_ELECTION_ENDPOINT, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getMasterElectionGroupId(uint64_t *id) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_MASTER_ELECTION_GROUP_ID, - data, - &reply); - - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *id = static_cast(reply.readInt64()); - } - } - - return status; - } - - virtual status_t setMasterElectionGroupId(uint64_t id) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt64(id); - status_t status = remote()->transact(SET_MASTER_ELECTION_GROUP_ID, - data, - &reply); - - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getInterfaceBinding(String16& ifaceName) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_INTERFACE_BINDING, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - ifaceName = reply.readString16(); - } - } - - return status; - } - - virtual status_t setInterfaceBinding(const String16& ifaceName) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeString16(ifaceName); - status_t status = remote()->transact(SET_INTERFACE_BINDING, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getMasterAnnounceInterval(int *interval) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_MASTER_ANNOUNCE_INTERVAL, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *interval = reply.readInt32(); - } - } - - return status; - } - - virtual status_t setMasterAnnounceInterval(int interval) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt32(interval); - status_t status = remote()->transact(SET_MASTER_ANNOUNCE_INTERVAL, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getClientSyncInterval(int *interval) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_CLIENT_SYNC_INTERVAL, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *interval = reply.readInt32(); - } - } - - return status; - } - - virtual status_t setClientSyncInterval(int interval) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt32(interval); - status_t status = remote()->transact(SET_CLIENT_SYNC_INTERVAL, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getPanicThreshold(int *threshold) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_PANIC_THRESHOLD, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *threshold = reply.readInt32(); - } - } - - return status; - } - - virtual status_t setPanicThreshold(int threshold) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt32(threshold); - status_t status = remote()->transact(SET_PANIC_THRESHOLD, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t getAutoDisable(bool *autoDisable) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(GET_AUTO_DISABLE, - data, - &reply); - if (status == OK) { - status = reply.readInt32(); - if (status == OK) { - *autoDisable = (0 != reply.readInt32()); - } - } - - return status; - } - - virtual status_t setAutoDisable(bool autoDisable) { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - data.writeInt32(autoDisable ? 1 : 0); - status_t status = remote()->transact(SET_AUTO_DISABLE, - data, - &reply); - - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } - - virtual status_t forceNetworklessMasterMode() { - Parcel data, reply; - data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); - status_t status = remote()->transact(FORCE_NETWORKLESS_MASTER_MODE, - data, - &reply); - - if (status == OK) { - status = reply.readInt32(); - } - - return status; - } -}; - -IMPLEMENT_META_INTERFACE(CommonTimeConfig, "android.os.ICommonTimeConfig"); - -status_t BnCommonTimeConfig::onTransact(uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags) { - switch(code) { - case GET_MASTER_ELECTION_PRIORITY: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - uint8_t priority; - status_t status = getMasterElectionPriority(&priority); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(static_cast(priority)); - } - return OK; - } break; - - case SET_MASTER_ELECTION_PRIORITY: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - uint8_t priority = static_cast(data.readInt32()); - status_t status = setMasterElectionPriority(priority); - reply->writeInt32(status); - return OK; - } break; - - case GET_MASTER_ELECTION_ENDPOINT: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - struct sockaddr_storage addr; - status_t status = getMasterElectionEndpoint(&addr); - - if ((status == OK) && !canSerializeSockaddr(&addr)) { - status = UNKNOWN_ERROR; - } - - reply->writeInt32(status); - - if (status == OK) { - serializeSockaddr(reply, &addr); - } - - return OK; - } break; - - case SET_MASTER_ELECTION_ENDPOINT: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - struct sockaddr_storage addr; - int hasAddr = data.readInt32(); - - status_t status; - if (hasAddr) { - deserializeSockaddr(&data, &addr); - status = setMasterElectionEndpoint(&addr); - } else { - status = setMasterElectionEndpoint(&addr); - } - - reply->writeInt32(status); - return OK; - } break; - - case GET_MASTER_ELECTION_GROUP_ID: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - uint64_t id; - status_t status = getMasterElectionGroupId(&id); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt64(id); - } - return OK; - } break; - - case SET_MASTER_ELECTION_GROUP_ID: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - uint64_t id = static_cast(data.readInt64()); - status_t status = setMasterElectionGroupId(id); - reply->writeInt32(status); - return OK; - } break; - - case GET_INTERFACE_BINDING: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - String16 ret; - status_t status = getInterfaceBinding(ret); - reply->writeInt32(status); - if (status == OK) { - reply->writeString16(ret); - } - return OK; - } break; - - case SET_INTERFACE_BINDING: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - String16 ifaceName; - ifaceName = data.readString16(); - status_t status = setInterfaceBinding(ifaceName); - reply->writeInt32(status); - return OK; - } break; - - case GET_MASTER_ANNOUNCE_INTERVAL: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int interval; - status_t status = getMasterAnnounceInterval(&interval); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(interval); - } - return OK; - } break; - - case SET_MASTER_ANNOUNCE_INTERVAL: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int interval = data.readInt32(); - status_t status = setMasterAnnounceInterval(interval); - reply->writeInt32(status); - return OK; - } break; - - case GET_CLIENT_SYNC_INTERVAL: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int interval; - status_t status = getClientSyncInterval(&interval); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(interval); - } - return OK; - } break; - - case SET_CLIENT_SYNC_INTERVAL: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int interval = data.readInt32(); - status_t status = setClientSyncInterval(interval); - reply->writeInt32(status); - return OK; - } break; - - case GET_PANIC_THRESHOLD: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int threshold; - status_t status = getPanicThreshold(&threshold); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(threshold); - } - return OK; - } break; - - case SET_PANIC_THRESHOLD: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - int threshold = data.readInt32(); - status_t status = setPanicThreshold(threshold); - reply->writeInt32(status); - return OK; - } break; - - case GET_AUTO_DISABLE: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - bool autoDisable; - status_t status = getAutoDisable(&autoDisable); - reply->writeInt32(status); - if (status == OK) { - reply->writeInt32(autoDisable ? 1 : 0); - } - return OK; - } break; - - case SET_AUTO_DISABLE: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - bool autoDisable = (0 != data.readInt32()); - status_t status = setAutoDisable(autoDisable); - reply->writeInt32(status); - return OK; - } break; - - case FORCE_NETWORKLESS_MASTER_MODE: { - CHECK_INTERFACE(ICommonTimeConfig, data, reply); - status_t status = forceNetworklessMasterMode(); - reply->writeInt32(status); - return OK; - } break; - } - return BBinder::onTransact(code, data, reply, flags); -} - -}; // namespace android - diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/common_time/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/media/common_time/NOTICE b/media/common_time/NOTICE deleted file mode 100644 index c5b1efa7aa..0000000000 --- a/media/common_time/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/media/common_time/cc_helper.cpp b/media/common_time/cc_helper.cpp deleted file mode 100644 index 6a7de74ac0..0000000000 --- a/media/common_time/cc_helper.cpp +++ /dev/null @@ -1,129 +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. - */ - -#include - -#include -#include -#include - -namespace android { - -Mutex CCHelper::lock_; -sp CCHelper::common_clock_; -sp CCHelper::common_clock_listener_; -uint32_t CCHelper::ref_count_ = 0; - -bool CCHelper::verifyClock_l() { - bool ret = false; - - if (common_clock_ == NULL) { - common_clock_ = ICommonClock::getInstance(); - if (common_clock_ == NULL) - goto bailout; - } - - if (ref_count_ > 0) { - if (common_clock_listener_ == NULL) { - common_clock_listener_ = new CommonClockListener(); - if (common_clock_listener_ == NULL) - goto bailout; - - if (OK != common_clock_->registerListener(common_clock_listener_)) - goto bailout; - } - } - - ret = true; - -bailout: - if (!ret) { - common_clock_listener_ = NULL; - common_clock_ = NULL; - } - return ret; -} - -CCHelper::CCHelper() { - Mutex::Autolock lock(&lock_); - ref_count_++; - verifyClock_l(); -} - -CCHelper::~CCHelper() { - Mutex::Autolock lock(&lock_); - - assert(ref_count_ > 0); - ref_count_--; - - // If we were the last CCHelper instance in the system, and we had - // previously register a listener, unregister it now so that the common time - // service has the chance to go into auto-disabled mode. - if (!ref_count_ && - (common_clock_ != NULL) && - (common_clock_listener_ != NULL)) { - common_clock_->unregisterListener(common_clock_listener_); - common_clock_listener_ = NULL; - } -} - -void CCHelper::CommonClockListener::onTimelineChanged(uint64_t /*timelineID*/) { - // do nothing; listener is only really used as a token so the server can - // find out when clients die. -} - -// Helper methods which attempts to make calls to the common time binder -// service. If the first attempt fails with DEAD_OBJECT, the helpers will -// attempt to make a connection to the service again (assuming that the process -// hosting the service had crashed and the client proxy we are holding is dead) -// If the second attempt fails, or no connection can be made, the we let the -// error propagate up the stack and let the caller deal with the situation as -// best they can. -#define CCHELPER_METHOD(decl, call) \ - status_t CCHelper::decl { \ - Mutex::Autolock lock(&lock_); \ - \ - if (!verifyClock_l()) \ - return DEAD_OBJECT; \ - \ - status_t status = common_clock_->call; \ - if (DEAD_OBJECT == status) { \ - if (!verifyClock_l()) \ - return DEAD_OBJECT; \ - status = common_clock_->call; \ - } \ - \ - return status; \ - } - -#define VERIFY_CLOCK() - -CCHELPER_METHOD(isCommonTimeValid(bool* valid, uint32_t* timelineID), - isCommonTimeValid(valid, timelineID)) -CCHELPER_METHOD(commonTimeToLocalTime(int64_t commonTime, int64_t* localTime), - commonTimeToLocalTime(commonTime, localTime)) -CCHELPER_METHOD(localTimeToCommonTime(int64_t localTime, int64_t* commonTime), - localTimeToCommonTime(localTime, commonTime)) -CCHELPER_METHOD(getCommonTime(int64_t* commonTime), - getCommonTime(commonTime)) -CCHELPER_METHOD(getCommonFreq(uint64_t* freq), - getCommonFreq(freq)) -CCHELPER_METHOD(getLocalTime(int64_t* localTime), - getLocalTime(localTime)) -CCHELPER_METHOD(getLocalFreq(uint64_t* freq), - getLocalFreq(freq)) - -} // namespace android diff --git a/media/common_time/local_clock.cpp b/media/common_time/local_clock.cpp deleted file mode 100644 index a7c61fc76a..0000000000 --- a/media/common_time/local_clock.cpp +++ /dev/null @@ -1,92 +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_TAG "common_time" -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace android { - -Mutex LocalClock::dev_lock_; -local_time_hw_device_t* LocalClock::dev_ = NULL; - -LocalClock::LocalClock() { - int res; - const hw_module_t* mod; - - AutoMutex lock(&dev_lock_); - - if (dev_ != NULL) - return; - - res = hw_get_module_by_class(LOCAL_TIME_HARDWARE_MODULE_ID, NULL, &mod); - if (res) { - ALOGE("Failed to open local time HAL module (res = %d)", res); - } else { - res = local_time_hw_device_open(mod, &dev_); - if (res) { - ALOGE("Failed to open local time HAL device (res = %d)", res); - dev_ = NULL; - } - } -} - -bool LocalClock::initCheck() { - return (NULL != dev_); -} - -int64_t LocalClock::getLocalTime() { - assert(NULL != dev_); - assert(NULL != dev_->get_local_time); - - return dev_->get_local_time(dev_); -} - -uint64_t LocalClock::getLocalFreq() { - assert(NULL != dev_); - assert(NULL != dev_->get_local_freq); - - return dev_->get_local_freq(dev_); -} - -status_t LocalClock::setLocalSlew(int16_t rate) { - assert(NULL != dev_); - - if (!dev_->set_local_slew) - return INVALID_OPERATION; - - return static_cast(dev_->set_local_slew(dev_, rate)); -} - -int32_t LocalClock::getDebugLog(struct local_time_debug_event* records, - int max_records) { - assert(NULL != dev_); - - if (!dev_->get_debug_log) - return INVALID_OPERATION; - - return dev_->get_debug_log(dev_, records, max_records); -} - -} // namespace android diff --git a/media/common_time/utils.cpp b/media/common_time/utils.cpp deleted file mode 100644 index 91cf2fd4b1..0000000000 --- a/media/common_time/utils.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2012 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 -#include - -#include - -namespace android { - -bool canSerializeSockaddr(const struct sockaddr_storage* addr) { - switch (addr->ss_family) { - case AF_INET: - case AF_INET6: - return true; - default: - return false; - } -} - -void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr) { - switch (addr->ss_family) { - case AF_INET: { - const struct sockaddr_in* s = - reinterpret_cast(addr); - p->writeInt32(AF_INET); - p->writeInt32(ntohl(s->sin_addr.s_addr)); - p->writeInt32(static_cast(ntohs(s->sin_port))); - } break; - - case AF_INET6: { - const struct sockaddr_in6* s = - reinterpret_cast(addr); - const int32_t* a = - reinterpret_cast(s->sin6_addr.s6_addr); - p->writeInt32(AF_INET6); - p->writeInt32(ntohl(a[0])); - p->writeInt32(ntohl(a[1])); - p->writeInt32(ntohl(a[2])); - p->writeInt32(ntohl(a[3])); - p->writeInt32(static_cast(ntohs(s->sin6_port))); - p->writeInt32(ntohl(s->sin6_flowinfo)); - p->writeInt32(ntohl(s->sin6_scope_id)); - } break; - } -} - -void deserializeSockaddr(const Parcel* p, struct sockaddr_storage* addr) { - memset(addr, 0, sizeof(*addr)); - - addr->ss_family = p->readInt32(); - switch(addr->ss_family) { - case AF_INET: { - struct sockaddr_in* s = - reinterpret_cast(addr); - s->sin_addr.s_addr = htonl(p->readInt32()); - s->sin_port = htons(static_cast(p->readInt32())); - } break; - - case AF_INET6: { - struct sockaddr_in6* s = - reinterpret_cast(addr); - int32_t* a = reinterpret_cast(s->sin6_addr.s6_addr); - - a[0] = htonl(p->readInt32()); - a[1] = htonl(p->readInt32()); - a[2] = htonl(p->readInt32()); - a[3] = htonl(p->readInt32()); - s->sin6_port = htons(static_cast(p->readInt32())); - s->sin6_flowinfo = htonl(p->readInt32()); - s->sin6_scope_id = htonl(p->readInt32()); - } break; - } -} - -} // namespace android diff --git a/media/common_time/utils.h b/media/common_time/utils.h deleted file mode 100644 index ce79d0d74c..0000000000 --- a/media/common_time/utils.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 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_LIBCOMMONCLOCK_UTILS_H -#define ANDROID_LIBCOMMONCLOCK_UTILS_H - -#include - -#include -#include - -namespace android { - -extern bool canSerializeSockaddr(const struct sockaddr_storage* addr); -extern void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr); -extern status_t deserializeSockaddr(const Parcel* p, - struct sockaddr_storage* addr); - -}; // namespace android - -#endif // ANDROID_LIBCOMMONCLOCK_UTILS_H -- GitLab From c3ab7730885d26b95014be1768dfac3db53dd7c1 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Jun 2018 13:46:29 -0700 Subject: [PATCH 0081/1530] PatchPanel: Add latency information for software patches Test: audioflinger dumpsys when patches present Bug: 80546849 Change-Id: Ib5b26e065ab20b5f8d530db57398e7b4a59ed1f1 --- services/audioflinger/PatchPanel.cpp | 39 +++++++++++++++++++++++++++- services/audioflinger/PatchPanel.h | 4 +++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 3d688fb7ab..c2927ba31c 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -475,11 +475,48 @@ void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) mPlayback.closeConnections(panel); } +status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const +{ + if (!isSoftware()) return INVALID_OPERATION; + + auto recordTrack = mRecord.const_track(); + if (recordTrack.get() == nullptr) return INVALID_OPERATION; + + auto playbackTrack = mPlayback.const_track(); + if (playbackTrack.get() == nullptr) return INVALID_OPERATION; + + // Latency information for tracks may be called without obtaining + // the underlying thread lock. + // + // We use record server latency + playback track latency (generally smaller than the + // reverse due to internal biases). + // + // TODO: is this stable enough? Consider a PatchTrack synchronized version of this. + double recordServerLatencyMs; + if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) != OK) return INVALID_OPERATION; + + double playbackTrackLatencyMs; + if (playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) != OK) return INVALID_OPERATION; + + *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs; + return OK; +} + String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) { String8 result; - result.appendFormat("Patch %d: thread %p => thread %p\n", + + // TODO: Consider table dump form for patches, just like tracks. + result.appendFormat("Patch %d: thread %p => thread %p", myHandle, mRecord.thread().get(), mPlayback.thread().get()); + + // add latency if it exists + double latencyMs; + if (getLatencyMs(&latencyMs) == OK) { + result.appendFormat(" latency: %.2lf", latencyMs); + } + + result.append("\n"); return result; } diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index dff8ad2d61..5d6bf00d85 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -65,6 +65,7 @@ private: audio_patch_handle_t handle() const { return mHandle; } sp thread() { return mThread; } sp track() { return mTrack; } + sp const_track() const { return mTrack; } void closeConnections(PatchPanel *panel) { if (mHandle != AUDIO_PATCH_HANDLE_NONE) { @@ -118,6 +119,9 @@ private: return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE || mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; } + // returns the latency of the patch (from record to playback). + status_t getLatencyMs(double *latencyMs) const; + String8 dump(audio_patch_handle_t myHandle); // Note that audio_patch::id is only unique within a HAL module -- GitLab From 21b43364f0eb3916264d046718d74c7fb98efe59 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 4 Jun 2018 10:37:09 -0700 Subject: [PATCH 0082/1530] APM test: Fix default config, add test for 'dump' - Fix AudioPolicyConfig::setDefault so the config is valid again; - Enhance the default config with dynamic profiles; - Factor out the code for creating a full dynamic profile; - Add a method to unit test that verifies calling APM::dump. This is also useful to examine how the default config looks like. Test: audiopolicy_tests Change-Id: I102071e160b835da50ed5806bd2d62102a10d775 --- .../include/AudioPolicyConfig.h | 32 +++++++++-------- .../managerdefinitions/include/AudioProfile.h | 2 ++ .../managerdefinitions/src/AudioProfile.cpp | 17 ++++++++++ .../managerdefinitions/src/Serializer.cpp | 14 ++------ .../tests/audiopolicymanager_tests.cpp | 34 +++++++++++++++++++ 5 files changed, 73 insertions(+), 26 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index f747c369df..f861b9592b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -38,12 +38,12 @@ public: AudioPolicyConfig(HwModuleCollection &hwModules, DeviceVector &availableOutputDevices, DeviceVector &availableInputDevices, - sp &defaultOutputDevices, + sp &defaultOutputDevice, VolumeCurvesCollection *volumes = nullptr) : mHwModules(hwModules), mAvailableOutputDevices(availableOutputDevices), mAvailableInputDevices(availableInputDevices), - mDefaultOutputDevices(defaultOutputDevices), + mDefaultOutputDevice(defaultOutputDevice), mVolumeCurves(volumes), mIsSpeakerDrcEnabled(false) {} @@ -108,40 +108,44 @@ public: void setDefaultOutputDevice(const sp &defaultDevice) { - mDefaultOutputDevices = defaultDevice; + mDefaultOutputDevice = defaultDevice; } - const sp &getDefaultOutputDevice() const { return mDefaultOutputDevices; } + const sp &getDefaultOutputDevice() const { return mDefaultOutputDevice; } void setDefault(void) { mSource = "AudioPolicyConfig::setDefault"; - mDefaultOutputDevices = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER); - sp module; + mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER); + mDefaultOutputDevice->addAudioProfile(AudioProfile::createFullDynamic()); sp defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC); - mAvailableOutputDevices.add(mDefaultOutputDevices); + defaultInputDevice->addAudioProfile(AudioProfile::createFullDynamic()); + sp micProfile = new AudioProfile( + AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000); + defaultInputDevice->addAudioProfile(micProfile); + mAvailableOutputDevices.add(mDefaultOutputDevice); mAvailableInputDevices.add(defaultInputDevice); - module = new HwModule(AUDIO_HARDWARE_MODULE_ID_PRIMARY); + sp module = new HwModule(AUDIO_HARDWARE_MODULE_ID_PRIMARY, 2 /*halVersionMajor*/); + mHwModules.add(module); + mDefaultOutputDevice->attach(module); + defaultInputDevice->attach(module); sp outProfile; outProfile = new OutputProfile(String8("primary")); outProfile->attach(module); outProfile->addAudioProfile( new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 44100)); - outProfile->addSupportedDevice(mDefaultOutputDevices); + outProfile->addSupportedDevice(mDefaultOutputDevice); outProfile->setFlags(AUDIO_OUTPUT_FLAG_PRIMARY); module->addOutputProfile(outProfile); sp inProfile; inProfile = new InputProfile(String8("primary")); inProfile->attach(module); - inProfile->addAudioProfile( - new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000)); + inProfile->addAudioProfile(micProfile); inProfile->addSupportedDevice(defaultInputDevice); module->addInputProfile(inProfile); - - mHwModules.add(module); } private: @@ -149,7 +153,7 @@ private: HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */ DeviceVector &mAvailableOutputDevices; DeviceVector &mAvailableInputDevices; - sp &mDefaultOutputDevices; + sp &mDefaultOutputDevice; VolumeCurvesCollection *mVolumeCurves; // TODO: remove when legacy conf file is removed. true on devices that use DRC on the // DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly. diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h index 8741c66693..4226ff2f7b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h @@ -36,6 +36,8 @@ bool operator == (const SortedVector &left, const SortedVector &right); class AudioProfile : public virtual RefBase { public: + static sp createFullDynamic(); + AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate) : diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index fd6fc1c015..26af9b42f4 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -28,6 +28,23 @@ namespace android { +static AudioProfile* createFullDynamicImpl() +{ + AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat, + ChannelsVector(), SampleRateVector()); + dynamicProfile->setDynamicFormat(true); + dynamicProfile->setDynamicChannels(true); + dynamicProfile->setDynamicRate(true); + return dynamicProfile; +} + +// static +sp AudioProfile::createFullDynamic() +{ + static sp dynamicProfile = createFullDynamicImpl(); + return dynamicProfile; +} + status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask, audio_format_t format) const { diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index a2531131d5..8008a7c7a9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -242,12 +242,7 @@ status_t MixPortTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, PtrElem AudioProfileTraits::Collection profiles; deserializeCollection(doc, child, profiles, NULL); if (profiles.isEmpty()) { - sp dynamicProfile = new AudioProfile(gDynamicFormat, - ChannelsVector(), SampleRateVector()); - dynamicProfile->setDynamicFormat(true); - dynamicProfile->setDynamicChannels(true); - dynamicProfile->setDynamicRate(true); - profiles.add(dynamicProfile); + profiles.add(AudioProfile::createFullDynamic()); } mixPort->setAudioProfiles(profiles); @@ -328,12 +323,7 @@ status_t DevicePortTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, PtrEl AudioProfileTraits::Collection profiles; deserializeCollection(doc, root, profiles, NULL); if (profiles.isEmpty()) { - sp dynamicProfile = new AudioProfile(gDynamicFormat, - ChannelsVector(), SampleRateVector()); - dynamicProfile->setDynamicFormat(true); - dynamicProfile->setDynamicChannels(true); - dynamicProfile->setDynamicRate(true); - profiles.add(dynamicProfile); + profiles.add(AudioProfile::createFullDynamic()); } deviceDesc->setAudioProfiles(profiles); diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp index 2d9260edcd..56af1528c0 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -16,9 +16,13 @@ #include #include +#include +#include #include +#define LOG_TAG "APM_Test" +#include #include #include "AudioPolicyTestClient.h" @@ -134,6 +138,36 @@ TEST_F(AudioPolicyManagerTest, InitSuccess) { // SetUp must finish with no assertions. } +TEST_F(AudioPolicyManagerTest, Dump) { + int pipefd[2]; + ASSERT_NE(-1, pipe(pipefd)); + pid_t cpid = fork(); + ASSERT_NE(-1, cpid); + if (cpid == 0) { + // Child process reads from the pipe and logs. + close(pipefd[1]); + std::string line; + char buf; + while (read(pipefd[0], &buf, sizeof(buf)) > 0) { + if (buf != '\n') { + line += buf; + } else { + ALOGI("%s", line.c_str()); + line = ""; + } + } + if (!line.empty()) ALOGI("%s", line.c_str()); + close(pipefd[0]); + _exit(EXIT_SUCCESS); + } else { + // Parent does the dump and checks the status code. + close(pipefd[0]); + ASSERT_EQ(NO_ERROR, mManager->dump(pipefd[1])); + close(pipefd[1]); + wait(NULL); // Wait for the child to exit. + } +} + TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { audio_patch patch{}; audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; -- GitLab From 9200bf9bbed390954cf824aa42ad2603dfcb6bcb Mon Sep 17 00:00:00 2001 From: Kazuhiro Inaba Date: Thu, 7 Jun 2018 13:59:11 +0900 Subject: [PATCH 0083/1530] Makes sure to set up the mediaextractor x86_64 seccomp policy. Three fixes (1) Sets LOCAL_REQUIRED_MODULES_x86_64. Otherwise the policy file won't be in the system image at all. (2) Drops the executable bit (chmod a-x) of the policy file. (3) Incorporates the changes done on x86.policy between O and P. The previous patch seems to be a cherry-pick from AOSP, which does not take the P changes into account. (Hence mediaextractor just crashes if the previous policy file is applied to P.) Bug: 79158930 Bug: 69073312 Test: ProcessMustUseSeccompTest#testMediaextractorHasSeccompFilter Test: CtsMediaTestCases runs without mediaextractor/minijail error Change-Id: Iada4c75883de03b5a44a06df33de428c2ceef3d5 --- services/mediaextractor/Android.mk | 1 + .../mediaextractor-x86_64.policy | 26 +++++++------------ 2 files changed, 11 insertions(+), 16 deletions(-) mode change 100755 => 100644 services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 37d6cc9c94..73c953597b 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaextractor.policy LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaextractor.policy LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy +LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy # extractor libraries LOCAL_REQUIRED_MODULES += \ diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy old mode 100755 new mode 100644 index 63c77805f6..6d9ed6f675 --- a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy +++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy @@ -21,6 +21,7 @@ clone: 1 getuid: 1 setpriority: 1 sigaltstack: 1 +fstatfs: 1 newfstatat: 1 restart_syscall: 1 exit: 1 @@ -30,28 +31,21 @@ faccessat: 1 sched_setscheduler: 1 getrlimit: 1 nanosleep: 1 +getrandom: 1 -# for FileSource +# for dynamically loading extractors +getdents64: 1 readlinkat: 1 +pread64: 1 +mremap: 1 -# for attaching to debuggerd on process crash -tgkill: 1 -socket: arg0 == 1 -connect: 1 -fcntl: 1 -rt_sigprocmask: 1 -rt_sigaction: 1 -rt_tgsigqueueinfo: 1 -geteuid: 1 -getgid: 1 -getegid: 1 -getgroups: 1 -getdents64: 1 -pipe2: 1 -ppoll: 1 +# for FileSource +readlinkat: 1 # Required by AddressSanitizer gettid: 1 sched_yield: 1 getpid: 1 gettid: 1 + +@include /system/etc/seccomp_policy/crash_dump.x86_64.policy -- GitLab From 1c86ebe110381daa6b05d07020fb75f166280f34 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 29 May 2018 20:29:08 -0700 Subject: [PATCH 0084/1530] AudioFlinger: Add latency information for duplicating threads Test: Bluetooth and ringtones Bug: 80447941 Change-Id: I0af9090f08636609e4c5a8c47d1d4c10f446746d --- services/audioflinger/PlaybackTracks.h | 15 ++++++++++++- services/audioflinger/Threads.cpp | 23 +++++++++++++++----- services/audioflinger/Threads.h | 30 ++++++++++++++++++++++++++ services/audioflinger/Tracks.cpp | 10 ++++++--- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 3381e773db..a93899b7b4 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -244,7 +244,7 @@ public: AudioSystem::SYNC_EVENT_NONE, audio_session_t triggerSession = AUDIO_SESSION_NONE); virtual void stop(); - bool write(void* data, uint32_t frames); + ssize_t write(void* data, uint32_t frames); bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; } bool isActive() const { return mActive; } const wp& thread() const { return mThread; } @@ -252,6 +252,18 @@ public: void copyMetadataTo(MetadataInserter& backInserter) const override; /** Set the metadatas of the upstream tracks. Thread safe. */ void setMetadatas(const SourceMetadatas& metadatas); + /** returns client timestamp to the upstream duplicating thread. */ + ExtendedTimestamp getClientProxyTimestamp() const { + // server - kernel difference is not true latency when drained + // i.e. mServerProxy->isDrained(). + ExtendedTimestamp timestamp; + (void) mClientProxy->getTimestamp(×tamp); + // On success, the timestamp LOCATION_SERVER and LOCATION_KERNEL + // entries will be properly filled. If getTimestamp() + // is unsuccessful, then a default initialized timestamp + // (with mTimeNs[] filled with -1's) is returned. + return timestamp; + } private: status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, @@ -268,6 +280,7 @@ private: bool mActive; DuplicatingThread* const mSourceThread; // for waitTimeMs() in write() sp mClientProxy; + /** Attributes of the source tracks. * * This member must be accessed with mTrackMetadatasMutex taken. diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d61b1f0d15..b737edec1f 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3203,12 +3203,10 @@ bool AudioFlinger::PlaybackThread::threadLoop() // and associate with the sink frames written out. We need // this to convert the sink timestamp to the track timestamp. bool kernelLocationUpdate = false; - if (mNormalSink != 0) { - // Note: The DuplicatingThread may not have a mNormalSink. + ExtendedTimestamp timestamp; // use private copy to fetch + if (threadloop_getHalTimestamp_l(×tamp) == OK) { // We always fetch the timestamp here because often the downstream // sink will block while writing. - ExtendedTimestamp timestamp; // use private copy to fetch - (void) mNormalSink->getTimestamp(timestamp); // We keep track of the last valid kernel position in case we are in underrun // and the normal mixer period is the same as the fast mixer period, or there @@ -6096,7 +6094,22 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() ssize_t AudioFlinger::DuplicatingThread::threadLoop_write() { for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(mSinkBuffer, writeFrames); + const ssize_t actualWritten = outputTracks[i]->write(mSinkBuffer, writeFrames); + + // Consider the first OutputTrack for timestamp and frame counting. + + // The threadLoop() generally assumes writing a full sink buffer size at a time. + // Here, we correct for writeFrames of 0 (a stop) or underruns because + // we always claim success. + if (i == 0) { + const ssize_t correction = mSinkBufferSize / mFrameSize - actualWritten; + ALOGD_IF(correction != 0 && writeFrames != 0, + "%s: writeFrames:%u actualWritten:%zd correction:%zd mFramesWritten:%lld", + __func__, writeFrames, actualWritten, correction, (long long)mFramesWritten); + mFramesWritten -= correction; + } + + // TODO: Report correction for the other output tracks and show in the dump. } mStandby = false; return (ssize_t)mSinkBufferSize; diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 680e021ade..b691ca9612 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -434,6 +434,12 @@ protected: virtual void setMasterMono_l(bool mono __unused) { } virtual bool requireMonoBlend() { return false; } + // called within the threadLoop to obtain timestamp from the HAL. + virtual status_t threadloop_getHalTimestamp_l( + ExtendedTimestamp *timestamp __unused) const { + return INVALID_OPERATION; + } + friend class AudioFlinger; // for mEffectChains const type_t mType; @@ -1150,6 +1156,14 @@ public: return mFastMixerDumpState.mTracks[fastIndex].mUnderruns; } + status_t threadloop_getHalTimestamp_l( + ExtendedTimestamp *timestamp) const override { + if (mNormalSink.get() != nullptr) { + return mNormalSink->getTimestamp(*timestamp); + } + return INVALID_OPERATION; + } + protected: virtual void setMasterMono_l(bool mono) { mMasterMono.store(mono); @@ -1314,6 +1328,22 @@ private: SortedVector < sp > mOutputTracks; public: virtual bool hasFastMixer() const { return false; } + status_t threadloop_getHalTimestamp_l( + ExtendedTimestamp *timestamp) const override { + if (mOutputTracks.size() > 0) { + // forward the first OutputTrack's kernel information for timestamp. + const ExtendedTimestamp trackTimestamp = + mOutputTracks[0]->getClientProxyTimestamp(); + if (trackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] > 0) { + timestamp->mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = + trackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; + timestamp->mPosition[ExtendedTimestamp::LOCATION_KERNEL] = + trackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]; + return OK; // discard server timestamp - that's ignored. + } + } + return INVALID_OPERATION; + } }; // record thread diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 5966f85814..4fa024ea32 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -435,7 +435,8 @@ AudioFlinger::PlaybackThread::Track::Track( } mName = TRACK_NAME_PENDING; - mServerLatencySupported = thread->type() == ThreadBase::MIXER; + mServerLatencySupported = thread->type() == ThreadBase::MIXER + || thread->type() == ThreadBase::DUPLICATING; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId) + @@ -1354,7 +1355,7 @@ void AudioFlinger::PlaybackThread::OutputTrack::stop() mActive = false; } -bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames) +ssize_t AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames) { Buffer *pInBuffer; Buffer inBuffer; @@ -1443,9 +1444,12 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frame mBufferQueue.add(pInBuffer); ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %zu", this, mThread.unsafe_get(), mBufferQueue.size()); + // audio data is consumed (stored locally); set frameCount to 0. + inBuffer.frameCount = 0; } else { ALOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this); + // TODO: return error for this. } } } @@ -1456,7 +1460,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frame stop(); } - return outputBufferFull; + return frames - inBuffer.frameCount; // number of frames consumed. } void AudioFlinger::PlaybackThread::OutputTrack::copyMetadataTo(MetadataInserter& backInserter) const -- GitLab From 9b11c026344077dab9591bb8b9afdf3e7bd93eb1 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 6 Jun 2018 19:19:22 -0700 Subject: [PATCH 0085/1530] sound trigger: more checks on IMemory received from client Add a verification on actual size of the fd backing up the IMemory recevied for sound model or recognition config. Fix similar problem for AudioTrack shared buffer. Bug: 78596657 Test: run POC. OK Google regression. Change-Id: I7cb02785f8ba46c437c7fcaa5b821f4b7e3240a0 --- media/utils/ServiceUtilities.cpp | 27 +++++++++++++++++++ .../include/mediautils/ServiceUtilities.h | 2 ++ services/audioflinger/Threads.cpp | 8 +++++- .../soundtrigger/SoundTriggerHwService.cpp | 16 +++++------ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 6a90beaeba..0d50be0cb4 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "ServiceUtilities" + #include #include #include @@ -172,4 +174,29 @@ bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) { return ok; } +status_t checkIMemory(const sp& iMemory) +{ + if (iMemory == 0) { + ALOGE("%s check failed: NULL IMemory pointer", __FUNCTION__); + return BAD_VALUE; + } + + sp heap = iMemory->getMemory(); + if (heap == 0) { + ALOGE("%s check failed: NULL heap pointer", __FUNCTION__); + return BAD_VALUE; + } + + off_t size = lseek(heap->getHeapID(), 0, SEEK_END); + lseek(heap->getHeapID(), 0, SEEK_SET); + + if (iMemory->pointer() == NULL || size < (off_t)iMemory->size()) { + ALOGE("%s check failed: pointer %p size %zu fd size %u", + __FUNCTION__, iMemory->pointer(), iMemory->size(), (uint32_t)size); + return BAD_VALUE; + } + + return NO_ERROR; +} + } // namespace android diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 2bdba5e3d0..0911744fb6 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -69,4 +70,5 @@ bool settingsAllowed(); bool modifyAudioRoutingAllowed(); bool dumpAllowed(); bool modifyPhoneStateAllowed(pid_t pid, uid_t uid); +status_t checkIMemory(const sp& iMemory); } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d61b1f0d15..509386ee1c 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1891,11 +1891,17 @@ sp AudioFlinger::PlaybackThread::createTrac status_t lStatus; audio_output_flags_t outputFlags = mOutput->flags; audio_output_flags_t requestedFlags = *flags; + uint32_t sampleRate; + + if (sharedBuffer != 0 && checkIMemory(sharedBuffer) != NO_ERROR) { + lStatus = BAD_VALUE; + goto Exit; + } if (*pSampleRate == 0) { *pSampleRate = mSampleRate; } - uint32_t sampleRate = *pSampleRate; + sampleRate = *pSampleRate; // special case for FAST flag considered OK if fast mixer is present if (hasFastMixer()) { diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index 6bf6e94f3b..eb9cd1d712 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -562,10 +562,7 @@ status_t SoundTriggerHwService::Module::loadSoundModel(const sp& modelM if (mHalInterface == 0) { return NO_INIT; } - if (modelMemory == 0 || modelMemory->pointer() == NULL) { - ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()"); - return BAD_VALUE; - } + struct sound_trigger_sound_model *sound_model = (struct sound_trigger_sound_model *)modelMemory->pointer(); @@ -659,11 +656,6 @@ status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t ha if (mHalInterface == 0) { return NO_INIT; } - if (dataMemory == 0 || dataMemory->pointer() == NULL) { - ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()"); - return BAD_VALUE; - - } struct sound_trigger_recognition_config *config = (struct sound_trigger_recognition_config *)dataMemory->pointer(); @@ -966,6 +958,9 @@ status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp& IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } + if (checkIMemory(modelMemory) != NO_ERROR) { + return BAD_VALUE; + } sp module = mModule.promote(); if (module == 0) { @@ -997,6 +992,9 @@ status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handl IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } + if (checkIMemory(dataMemory) != NO_ERROR) { + return BAD_VALUE; + } sp module = mModule.promote(); if (module == 0) { -- GitLab From 7142df6eb369d2c3d1a8c12e6df2861ed1bd8c36 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 8 Jun 2018 15:01:57 +0100 Subject: [PATCH 0086/1530] Camera: Skip gathering complete 3A state FrameProcessor will always gather the complete 3A state before notifying clients. This could introduce unnecessary delays for camera devices that support partial results. To avoid this check the pending 3A state for each algorithm individually and notify about state changes as soon as possible. Bug: 110058858 Test: Manual using application, Camera CTS Change-Id: Ie197b2adcd884f1296f621f62cef8d53c9dc99c1 --- .../api1/client2/FrameProcessor.cpp | 94 ++++++++++++------- .../api1/client2/FrameProcessor.h | 3 +- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp index 0c738e7324..683e84daee 100644 --- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp @@ -33,7 +33,10 @@ FrameProcessor::FrameProcessor(wp device, FrameProcessorBase(device), mClient(client), mLastFrameNumberOfFaces(0), - mLast3AFrameNumber(-1) { + mLast3AFrameNumber(-1), + mLastAEFrameNumber(-1), + mLastAFrameNumber(-1), + mLastAWBFrameNumber(-1) { sp d = device.promote(); mSynthesize3ANotify = !(d->willNotify3A()); @@ -262,24 +265,73 @@ status_t FrameProcessor::process3aState(const CaptureResult &frame, bool gotAllStates = true; // TODO: Also use AE mode, AE trigger ID - gotAllStates &= updatePendingState(metadata, ANDROID_CONTROL_AF_MODE, + bool gotAFState = updatePendingState(metadata, ANDROID_CONTROL_AF_MODE, &pendingState.afMode, frameNumber, cameraId); - gotAllStates &= updatePendingState(metadata, ANDROID_CONTROL_AWB_MODE, + bool gotAWBState = updatePendingState(metadata, ANDROID_CONTROL_AWB_MODE, &pendingState.awbMode, frameNumber, cameraId); - gotAllStates &= updatePendingState(metadata, ANDROID_CONTROL_AE_STATE, + bool gotAEState = updatePendingState(metadata, ANDROID_CONTROL_AE_STATE, &pendingState.aeState, frameNumber, cameraId); - gotAllStates &= updatePendingState(metadata, ANDROID_CONTROL_AF_STATE, + gotAFState &= updatePendingState(metadata, ANDROID_CONTROL_AF_STATE, &pendingState.afState, frameNumber, cameraId); - gotAllStates &= updatePendingState(metadata, ANDROID_CONTROL_AWB_STATE, + gotAWBState &= updatePendingState(metadata, ANDROID_CONTROL_AWB_STATE, &pendingState.awbState, frameNumber, cameraId); pendingState.afTriggerId = frame.mResultExtras.afTriggerId; pendingState.aeTriggerId = frame.mResultExtras.precaptureTriggerId; + if (gotAEState && (frameNumber > mLastAEFrameNumber)) { + if (pendingState.aeState != m3aState.aeState || + pendingState.aeTriggerId > m3aState.aeTriggerId) { + ALOGV("%s: Camera %d: AE state %d->%d", + __FUNCTION__, cameraId, + m3aState.aeState, pendingState.aeState); + client->notifyAutoExposure(pendingState.aeState, pendingState.aeTriggerId); + + m3aState.aeState = pendingState.aeState; + m3aState.aeTriggerId = pendingState.aeTriggerId; + mLastAEFrameNumber = frameNumber; + } + } + + if (gotAFState && (frameNumber > mLastAFrameNumber)) { + if (pendingState.afState != m3aState.afState || + pendingState.afMode != m3aState.afMode || + pendingState.afTriggerId != m3aState.afTriggerId) { + ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d", + __FUNCTION__, cameraId, + m3aState.afState, pendingState.afState, + m3aState.afMode, pendingState.afMode, + m3aState.afTriggerId, pendingState.afTriggerId); + client->notifyAutoFocus(pendingState.afState, pendingState.afTriggerId); + + m3aState.afState = pendingState.afState; + m3aState.afMode = pendingState.afMode; + m3aState.afTriggerId = pendingState.afTriggerId; + mLastAFrameNumber = frameNumber; + } + } + + if (gotAWBState && (frameNumber > mLastAWBFrameNumber)) { + if (pendingState.awbState != m3aState.awbState || + pendingState.awbMode != m3aState.awbMode) { + ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d", + __FUNCTION__, cameraId, + m3aState.awbState, pendingState.awbState, + m3aState.awbMode, pendingState.awbMode); + client->notifyAutoWhitebalance(pendingState.awbState, + pendingState.aeTriggerId); + + m3aState.awbMode = pendingState.awbMode; + m3aState.awbState = pendingState.awbState; + mLastAWBFrameNumber = frameNumber; + } + } + + gotAllStates &= gotAEState & gotAFState & gotAWBState; if (!gotAllStates) { // If not all states are received, put the pending state to mPending3AStates. if (index == NAME_NOT_FOUND) { @@ -290,40 +342,10 @@ status_t FrameProcessor::process3aState(const CaptureResult &frame, return NOT_ENOUGH_DATA; } - // Once all 3A states are received, notify the client about 3A changes. - if (pendingState.aeState != m3aState.aeState || - pendingState.aeTriggerId > m3aState.aeTriggerId) { - ALOGV("%s: Camera %d: AE state %d->%d", - __FUNCTION__, cameraId, - m3aState.aeState, pendingState.aeState); - client->notifyAutoExposure(pendingState.aeState, pendingState.aeTriggerId); - } - - if (pendingState.afState != m3aState.afState || - pendingState.afMode != m3aState.afMode || - pendingState.afTriggerId != m3aState.afTriggerId) { - ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d", - __FUNCTION__, cameraId, - m3aState.afState, pendingState.afState, - m3aState.afMode, pendingState.afMode, - m3aState.afTriggerId, pendingState.afTriggerId); - client->notifyAutoFocus(pendingState.afState, pendingState.afTriggerId); - } - if (pendingState.awbState != m3aState.awbState || - pendingState.awbMode != m3aState.awbMode) { - ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d", - __FUNCTION__, cameraId, - m3aState.awbState, pendingState.awbState, - m3aState.awbMode, pendingState.awbMode); - client->notifyAutoWhitebalance(pendingState.awbState, - pendingState.aeTriggerId); - } - if (index != NAME_NOT_FOUND) { mPending3AStates.removeItemsAt(index); } - m3aState = pendingState; mLast3AFrameNumber = frameNumber; return OK; diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h index 62a4e91dff..8183c1284d 100644 --- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h +++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h @@ -104,8 +104,7 @@ class FrameProcessor : public FrameProcessorBase { // Track most recent frame number for which 3A notifications were sent for. // Used to filter against sending 3A notifications for the same frame // several times. - int32_t mLast3AFrameNumber; - + int32_t mLast3AFrameNumber, mLastAEFrameNumber, mLastAFrameNumber, mLastAWBFrameNumber; // Emit FaceDetection event to java if faces changed void callbackFaceDetection(const sp& client, const camera_frame_metadata &metadata); -- GitLab From 5f2136e6323c1e241c918a59cd06e715480241cb Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 11 May 2018 22:03:00 -0700 Subject: [PATCH 0087/1530] AudioFlinger: Log audio attributes in dumpsys Test: adb shell dumpsys media.audio_flinger|grep FrmRdy -A1 Change-Id: I47d402133ab30d0f2276577d2b51c30956f873c6 Signed-off-by: Kevin Rocard --- services/audioflinger/MmapTracks.h | 3 +- services/audioflinger/Threads.cpp | 4 +-- services/audioflinger/Tracks.cpp | 56 ++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h index 6f546c3add..241af099e7 100644 --- a/services/audioflinger/MmapTracks.h +++ b/services/audioflinger/MmapTracks.h @@ -28,6 +28,7 @@ public: audio_format_t format, audio_channel_mask_t channelMask, audio_session_t sessionId, + bool isOut, uid_t uid, pid_t pid, audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); @@ -40,7 +41,7 @@ public: virtual void stop(); virtual bool isFastTrack() const { return false; } - static void appendDumpHeader(String8& result); + void appendDumpHeader(String8& result); void appendDump(String8& result, bool active); // protected by MMapThread::mLock diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d61b1f0d15..1a2436521b 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -8151,7 +8151,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, - client.clientUid, client.clientPid, portId); + isOutput(), client.clientUid, client.clientPid, portId); track->setSilenced_l(silenced); mActiveTracks.add(track); @@ -8677,7 +8677,7 @@ void AudioFlinger::MmapThread::dumpTracks(int fd, const Vector& args _ const char *prefix = " "; if (numtracks) { result.append(prefix); - MmapTrack::appendDumpHeader(result); + mActiveTracks[0]->appendDumpHeader(result); for (size_t i = 0; i < numtracks ; ++i) { sp track = mActiveTracks[i]; result.append(prefix); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 5966f85814..8b5cc32fba 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -493,11 +493,12 @@ void AudioFlinger::PlaybackThread::Track::destroy() void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) { result.appendFormat("T Name Active Client Session S Flags " - " Format Chn mask SRate " - "ST L dB R dB VS dB " - " Server FrmCnt FrmRdy F Underruns Flushed" - "%s\n", - isServerLatencySupported() ? " Latency" : ""); + " Format Chn mask SRate " + "ST Usg CT " + " G db L dB R dB VS dB " + " Server FrmCnt FrmRdy F Underruns Flushed" + "%s\n", + isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active) @@ -582,9 +583,10 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ ? 'e' /* error */ : ' ' /* identical */; result.appendFormat("%7s %6u %7u %2s 0x%03X " - "%08X %08X %6u " - "%2u %5.2g %5.2g %5.2g%c " - "%08X %6zu%c %6zu %c %9u%c %7u", + "%08X %08X %6u " + "%2u %3x %2x " + "%5.2g %5.2g %5.2g %5.2g%c " + "%08X %6zu%c %6zu %c %9u%c %7u", active ? "yes" : "no", (mClient == 0) ? getpid() : mClient->pid(), mSessionId, @@ -596,6 +598,10 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ sampleRate(), mStreamType, + mAttr.usage, + mAttr.content_type, + + 20.0 * log10(mFinalVolume), 20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))), 20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))), 20.0 * log10(vsVolume.first), // VolumeShaper(s) total volume @@ -989,7 +995,7 @@ VolumeShaper::Status AudioFlinger::PlaybackThread::Track::applyVolumeShaper( // Signal thread to fetch new volume. sp thread = mThread.promote(); if (thread != 0) { - Mutex::Autolock _l(thread->mLock); + Mutex::Autolock _l(thread->mLock); thread->broadcast_l(); } } @@ -1540,7 +1546,7 @@ AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() } status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event, - audio_session_t triggerSession) + audio_session_t triggerSession) { status_t status = Track::start(event, triggerSession); if (status != NO_ERROR) { @@ -1805,14 +1811,16 @@ void AudioFlinger::RecordThread::RecordTrack::invalidate() void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) { - result.appendFormat("Active Client Session S Flags Format Chn mask SRate Server" - " FrmCnt FrmRdy Sil%s\n", isServerLatencySupported() ? " Latency" : ""); + result.appendFormat("Active Client Session S Flags " + " Format Chn mask SRate Source " + " Server FrmCnt FrmRdy Sil%s\n", + isServerLatencySupported() ? " Latency" : ""); } void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool active) { result.appendFormat("%c%5s %6u %7u %2s 0x%03X " - "%08X %08X %6u " + "%08X %08X %6u %6X " "%08X %6zu %6zu %3c", isFastTrack() ? 'F' : ' ', active ? "yes" : "no", @@ -1824,6 +1832,7 @@ void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool a mFormat, mChannelMask, mSampleRate, + mAttr.source, mCblk->mServer, mFrameCount, @@ -1984,13 +1993,14 @@ AudioFlinger::MmapThread::MmapTrack::MmapTrack(ThreadBase *thread, audio_format_t format, audio_channel_mask_t channelMask, audio_session_t sessionId, + bool isOut, uid_t uid, pid_t pid, audio_port_handle_t portId) : TrackBase(thread, NULL, attr, sampleRate, format, channelMask, (size_t)0 /* frameCount */, nullptr /* buffer */, (size_t)0 /* bufferSize */, - sessionId, uid, false /* isOut */, + sessionId, uid, isOut, ALLOC_NONE, TYPE_DEFAULT, portId), mPid(pid), mSilenced(false), mSilencedNotified(false) @@ -2007,7 +2017,7 @@ status_t AudioFlinger::MmapThread::MmapTrack::initCheck() const } status_t AudioFlinger::MmapThread::MmapTrack::start(AudioSystem::sync_event_t event __unused, - audio_session_t triggerSession __unused) + audio_session_t triggerSession __unused) { return NO_ERROR; } @@ -2038,19 +2048,27 @@ void AudioFlinger::MmapThread::MmapTrack::onTimestamp(const ExtendedTimestamp &t { } -/*static*/ void AudioFlinger::MmapThread::MmapTrack::appendDumpHeader(String8& result) +void AudioFlinger::MmapThread::MmapTrack::appendDumpHeader(String8& result) { - result.append("Client Session Format Chn mask SRate\n"); + result.appendFormat("Client Session Format Chn mask SRate Flags %s\n", + isOut() ? "Usg CT": "Source"); } void AudioFlinger::MmapThread::MmapTrack::appendDump(String8& result, bool active __unused) { - result.appendFormat("%6u %7u %08X %08X %6u\n", + result.appendFormat("%6u %7u %08X %08X %6u 0x%03X ", mPid, mSessionId, mFormat, mChannelMask, - mSampleRate); + mSampleRate, + mAttr.flags); + if (isOut()) { + result.appendFormat("%3x %2x", mAttr.usage, mAttr.content_type); + } else { + result.appendFormat("%6x", mAttr.source); + } + result.append("\n"); } } // namespace android -- GitLab From 9dcd34f438ed63b2806134368c68cd3004e4dffb Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 13 Jun 2018 16:19:36 -0700 Subject: [PATCH 0088/1530] AAudio should not use legacy path if not allowed Test: mmm frameworks/av/media/libaaudio/examples && adb sync data && adb shell ! /data/nativetest64/write_sine_callback/write_sine_callback -m3 Change-Id: Ie17c4037c4b61caed07748a0ae79ec950da3c802 Signed-off-by: Kevin Rocard --- media/libaaudio/examples/utils/AAudioArgsParser.h | 6 ++++-- media/libaaudio/src/core/AudioStreamBuilder.cpp | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h index 88d7401110..0e61589ae5 100644 --- a/media/libaaudio/examples/utils/AAudioArgsParser.h +++ b/media/libaaudio/examples/utils/AAudioArgsParser.h @@ -272,7 +272,9 @@ public: if (strlen(arg) > 2) { policy = atoi(&arg[2]); } - AAudio_setMMapPolicy(policy); + if (!AAudio_setMMapPolicy(policy)) { + printf("ERROR: invalid MMAP policy mode %i\n", policy); + } } break; case 'n': setNumberOfBursts(atoi(&arg[2])); @@ -363,7 +365,7 @@ public: mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING; break; default: - printf("ERROR invalid performance mode %c\n", c); + printf("ERROR: invalid performance mode %c\n", c); break; } return mode; diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp index 3a7a578f78..4ef765d7ba 100644 --- a/media/libaaudio/src/core/AudioStreamBuilder.cpp +++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp @@ -150,6 +150,11 @@ aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) { allowMMap = false; } + if (!allowMMap && !allowLegacy) { + ALOGE("%s() no backend available: neither MMAP nor legacy path are allowed", __func__); + return AAUDIO_ERROR_ILLEGAL_ARGUMENT; + } + result = builder_createStream(getDirection(), sharingMode, allowMMap, &audioStream); if (result == AAUDIO_OK) { // Open the stream using the parameters from the builder. -- GitLab From 79a7782b708920dbe5cf7167601bcabb39f6c074 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 10 May 2018 15:57:25 -0700 Subject: [PATCH 0089/1530] Increase RecordThread heap size When creating a software patch for transporting encoded audio (AC3), the old heap size was not sufficient. Bug: 63901775 Test: check for "not enough memory for pipe buffer" log message while creating a patch for transporting AC3 Change-Id: Idb0b2604f35f00e65e28bf8c066f6d861b2dd3c9 --- services/audioflinger/Threads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6220bbf045..2fe63783dd 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -200,7 +200,7 @@ static int sFastTrackMultiplier = kFastTrackMultiplier; // Initially this heap is used to allocate client buffers for "fast" AudioRecord. // Eventually it will be the single buffer that FastCapture writes into via HAL read(), // and that all "fast" AudioRecord clients read from. In either case, the size can be small. -static const size_t kRecordThreadReadOnlyHeapSize = 0x4000; +static const size_t kRecordThreadReadOnlyHeapSize = 0xD000; // ---------------------------------------------------------------------------- -- GitLab From 7c6ae98049e422454f6e9ea21d9d3fd6a7bfd217 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 14 Jun 2018 12:33:38 -0700 Subject: [PATCH 0090/1530] audioflinger: Add support for RecordTrack with no conversion When piping encoded audio data via software patch, it needs to be created without the buffer converter. In this case the audio data is copied directly from ResamplerBufferProvider. Added input flag AUDIO_INPUT_FLAG_DIRECT to indicate to PatchPanel that the PatchRecord track needs to be opened in this mode. This flag can be later added to minor Audio HAL update. Bug: 63901775 Test: MSD scenario; verified that phone over USB headset still works on marlin / sailfish Change-Id: If2745778d356769b143fb3c29910275ddef119ac --- media/libmedia/TypeConverter.cpp | 1 + services/audioflinger/AudioFlinger.cpp | 3 +++ services/audioflinger/MmapTracks.h | 1 + services/audioflinger/PatchPanel.cpp | 15 +++++++++-- services/audioflinger/PlaybackTracks.h | 3 ++- services/audioflinger/RecordTracks.h | 2 ++ services/audioflinger/Threads.cpp | 36 ++++++++++++++++++++++---- services/audioflinger/TrackBase.h | 1 + services/audioflinger/Tracks.cpp | 26 ++++++++++--------- 9 files changed, 68 insertions(+), 20 deletions(-) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index a3db754637..514c7957ab 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -132,6 +132,7 @@ const InputFlagConverter::Table InputFlagConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_MMAP_NOIRQ), MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_VOIP_TX), MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_HW_AV_SYNC), + MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_DIRECT), TERMINATOR }; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 13477f31be..53a4ce9e0c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2335,6 +2335,9 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod return 0; } + // Some flags are specific to framework and must not leak to the HAL. + flags = static_cast(flags & ~AUDIO_INPUT_FRAMEWORK_FLAGS); + // Audio Policy can request a specific handle for hardware hotword. // The goal here is not to re-open an already opened input. // It is to use a pre-assigned I/O handle. diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h index 241af099e7..968d5aa3f1 100644 --- a/services/audioflinger/MmapTracks.h +++ b/services/audioflinger/MmapTracks.h @@ -40,6 +40,7 @@ public: audio_session_t triggerSession); virtual void stop(); virtual bool isFastTrack() const { return false; } + bool isDirect() const override { return true; } void appendDumpHeader(String8& result); void appendDump(String8& result, bool active); diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index c2927ba31c..42a5a9077d 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -423,6 +423,14 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) uint32_t sampleRate = mPlayback.thread()->sampleRate(); audio_format_t format = mPlayback.thread()->format(); + audio_format_t inputFormat = mRecord.thread()->format(); + if (!audio_is_linear_pcm(inputFormat)) { + // The playbackThread format will say PCM for IEC61937 packetized stream. + // Use recordThread format. + format = inputFormat; + } + audio_input_flags_t inputFlags = mAudioPatch.sources[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? + mAudioPatch.sources[0].flags.input : AUDIO_INPUT_FLAG_NONE; sp tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord( mRecord.thread().get(), sampleRate, @@ -431,12 +439,15 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) frameCount, NULL, (size_t)0 /* bufferSize */, - AUDIO_INPUT_FLAG_NONE); + inputFlags); status = mRecord.checkTrack(tempRecordTrack.get()); if (status != NO_ERROR) { return status; } + audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? + mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE; + // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer sp tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( @@ -448,7 +459,7 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) frameCount, tempRecordTrack->buffer(), tempRecordTrack->bufferSize(), - AUDIO_OUTPUT_FLAG_NONE); + outputFlags); status = mPlayback.checkTrack(tempPatchTrack.get()); if (status != NO_ERROR) { return status; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index a93899b7b4..4d5f6b0e23 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -71,7 +71,8 @@ public: } bool isOffloaded() const { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; } - bool isDirect() const { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; } + bool isDirect() const override + { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; } bool isOffloadedOrDirect() const { return (mFlags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) != 0; } diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 43b6391fbb..b0c9fda1fe 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -63,6 +63,8 @@ public: const ExtendedTimestamp ×tamp); virtual bool isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; } + bool isDirect() const override + { return (mFlags & AUDIO_INPUT_FLAG_DIRECT) != 0; } void setSilenced(bool silenced) { if (!isPatchTrack()) mSilenced = silenced; } bool isSilenced() const { return mSilenced; } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6220bbf045..85e84f44e9 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6815,9 +6815,33 @@ reacquire_wakelock: framesOut = min(framesOut, destinationFramesPossible( framesIn, mSampleRate, activeTrack->mSampleRate)); - // process frames from the RecordThread buffer provider to the RecordTrack buffer - framesOut = activeTrack->mRecordBufferConverter->convert( - activeTrack->mSink.raw, activeTrack->mResamplerBufferProvider, framesOut); + + if (activeTrack->isDirect()) { + // No RecordBufferConverter used for compressed formats. Pass + // straight from RecordThread buffer to RecordTrack buffer. + AudioBufferProvider::Buffer buffer; + buffer.frameCount = framesOut; + status_t status = activeTrack->mResamplerBufferProvider->getNextBuffer(&buffer); + if (status == OK && buffer.frameCount != 0) { + ALOGV_IF(buffer.frameCount != framesOut, + "%s() read less than expected (%zu vs %zu)", + __func__, buffer.frameCount, framesOut); + framesOut = buffer.frameCount; + memcpy(activeTrack->mSink.raw, buffer.raw, buffer.frameCount); + activeTrack->mResamplerBufferProvider->releaseBuffer(&buffer); + } else { + framesOut = 0; + ALOGE("%s() cannot fill request, status: %d, frameCount: %zu", + __func__, status, buffer.frameCount); + } + } else { + // process frames from the RecordThread buffer provider to the RecordTrack + // buffer + framesOut = activeTrack->mRecordBufferConverter->convert( + activeTrack->mSink.raw, + activeTrack->mResamplerBufferProvider, + framesOut); + } if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) { overrun = OVERRUN_FALSE; @@ -7189,8 +7213,10 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac // see previously buffered data before it called start(), but with greater risk of overrun. recordTrack->mResamplerBufferProvider->reset(); - // clear any converter state as new data will be discontinuous - recordTrack->mRecordBufferConverter->reset(); + if (!recordTrack->isDirect()) { + // clear any converter state as new data will be discontinuous + recordTrack->mRecordBufferConverter->reset(); + } recordTrack->mState = TrackBase::STARTING_2; // signal thread to start mWaitWorkCV.broadcast(); diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index dafba1ee9d..95da9d7c5d 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -91,6 +91,7 @@ public: void* buffer() const { return mBuffer; } size_t bufferSize() const { return mBufferSize; } virtual bool isFastTrack() const = 0; + virtual bool isDirect() const = 0; bool isOutputTrack() const { return (mType == TYPE_OUTPUT); } bool isPatchTrack() const { return (mType == TYPE_PATCH); } bool isExternalTrack() const { return !isOutputTrack() && !isPatchTrack(); } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index f6c33e27e4..22e610e963 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1694,18 +1694,20 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( return; } - mRecordBufferConverter = new RecordBufferConverter( - thread->mChannelMask, thread->mFormat, thread->mSampleRate, - channelMask, format, sampleRate); - // Check if the RecordBufferConverter construction was successful. - // If not, don't continue with construction. - // - // NOTE: It would be extremely rare that the record track cannot be created - // for the current device, but a pending or future device change would make - // the record track configuration valid. - if (mRecordBufferConverter->initCheck() != NO_ERROR) { - ALOGE("RecordTrack unable to create record buffer converter"); - return; + if (!isDirect()) { + mRecordBufferConverter = new RecordBufferConverter( + thread->mChannelMask, thread->mFormat, thread->mSampleRate, + channelMask, format, sampleRate); + // Check if the RecordBufferConverter construction was successful. + // If not, don't continue with construction. + // + // NOTE: It would be extremely rare that the record track cannot be created + // for the current device, but a pending or future device change would make + // the record track configuration valid. + if (mRecordBufferConverter->initCheck() != NO_ERROR) { + ALOGE("RecordTrack unable to create record buffer converter"); + return; + } } mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, -- GitLab From fbdfebe55b23934e8f97c72065d653b7ced6330a Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 18 Jun 2018 12:30:40 -0700 Subject: [PATCH 0091/1530] Audio policy: remove using namespace std in header Introduced by I1442a9dda1553e9ea7a4a654c50555ac1ec06aa0 Bug: 79417899 Test: m -j Change-Id: I4d5a598ea1a376f12c133c0fc6a425a738da7a7a Signed-off-by: Kevin Rocard --- services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 5 +++-- services/audiopolicy/service/AudioPolicyService.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index d3a1188d8e..d2bc40d9e9 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -533,7 +533,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc item->setCString(kAudioPolicyRqstPkg, std::string(String8(client->opPackageName).string()).c_str()); } else { - item->setCString(kAudioPolicyRqstPkg, to_string(client->uid).c_str()); + item->setCString(kAudioPolicyRqstPkg, std::to_string(client->uid).c_str()); } item->setCString( kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str()); @@ -558,7 +558,8 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc item->setCString(kAudioPolicyActivePkg, std::string(String8(other->opPackageName).string()).c_str()); } else { - item->setCString(kAudioPolicyRqstPkg, to_string(other->uid).c_str()); + item->setCString(kAudioPolicyRqstPkg, + std::to_string(other->uid).c_str()); } item->setCString(kAudioPolicyActiveDevice, getDeviceTypeStrForPortId(other->deviceId).c_str()); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 02019a2f6b..09375f12ea 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -38,8 +38,6 @@ namespace android { -using namespace std; - // ---------------------------------------------------------------------------- class AudioPolicyService : -- GitLab From df9b420d1a43d4fa7d75e10f00a15e482c994cae Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 10 May 2018 19:56:08 -0700 Subject: [PATCH 0092/1530] Align LibAudioHal V2 and V4 for factorisation In order to merge the two version, they must first align as much as possible. Test: playback music, assistant Change-Id: If517cc3f4646a224af3e9b3d42b68a75f1801b39 Signed-off-by: Kevin Rocard --- media/libaudiohal/2.0/Android.bp | 25 ++++-- .../libaudiohal/2.0/ConversionHelperHidl.cpp | 2 + media/libaudiohal/2.0/ConversionHelperHidl.h | 10 ++- media/libaudiohal/2.0/DeviceHalHidl.cpp | 22 +++-- media/libaudiohal/2.0/DeviceHalHidl.h | 2 + media/libaudiohal/2.0/DeviceHalLocal.cpp | 2 + media/libaudiohal/2.0/DeviceHalLocal.h | 2 + .../libaudiohal/2.0/DevicesFactoryHalHidl.cpp | 82 ++++++++++--------- media/libaudiohal/2.0/DevicesFactoryHalHidl.h | 8 +- .../2.0/DevicesFactoryHalHybrid.cpp | 5 +- .../libaudiohal/2.0/DevicesFactoryHalHybrid.h | 15 ++-- .../2.0/DevicesFactoryHalLocal.cpp | 2 + .../libaudiohal/2.0/DevicesFactoryHalLocal.h | 2 + media/libaudiohal/2.0/EffectBufferHalHidl.cpp | 2 + media/libaudiohal/2.0/EffectBufferHalHidl.h | 2 + media/libaudiohal/2.0/EffectHalHidl.cpp | 9 +- media/libaudiohal/2.0/EffectHalHidl.h | 8 +- .../libaudiohal/2.0/EffectsFactoryHalHidl.cpp | 9 +- media/libaudiohal/2.0/EffectsFactoryHalHidl.h | 14 ++-- media/libaudiohal/2.0/StreamHalHidl.cpp | 16 ++-- media/libaudiohal/2.0/StreamHalHidl.h | 2 + media/libaudiohal/2.0/StreamHalLocal.cpp | 3 + media/libaudiohal/2.0/StreamHalLocal.h | 2 + media/libaudiohal/2.0/StreamPowerLog.h | 2 + media/libaudiohal/2.0/VersionUtils.h | 49 +++++++++++ .../include/libaudiohal/2.0/FactoryHalHidl.h | 36 ++++++++ media/libaudiohal/4.0/Android.bp | 19 +++-- .../libaudiohal/4.0/ConversionHelperHidl.cpp | 8 +- media/libaudiohal/4.0/ConversionHelperHidl.h | 13 +-- media/libaudiohal/4.0/DeviceHalHidl.cpp | 6 +- media/libaudiohal/4.0/DeviceHalHidl.h | 6 +- media/libaudiohal/4.0/DeviceHalLocal.h | 6 +- .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 11 ++- media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 6 +- .../4.0/DevicesFactoryHalHybrid.cpp | 5 +- .../4.0 => }/DevicesFactoryHalHybrid.h | 19 ++--- .../libaudiohal/4.0/DevicesFactoryHalLocal.h | 6 +- media/libaudiohal/4.0/EffectBufferHalHidl.h | 6 +- media/libaudiohal/4.0/EffectHalHidl.cpp | 1 - media/libaudiohal/4.0/EffectHalHidl.h | 12 +-- .../libaudiohal/4.0/EffectsFactoryHalHidl.cpp | 5 +- .../4.0 => }/EffectsFactoryHalHidl.h | 18 ++-- media/libaudiohal/4.0/StreamHalHidl.cpp | 14 ++-- media/libaudiohal/4.0/StreamHalHidl.h | 6 +- media/libaudiohal/4.0/StreamHalLocal.h | 6 +- media/libaudiohal/4.0/StreamPowerLog.h | 6 +- media/libaudiohal/4.0/VersionUtils.h | 6 +- .../include/libaudiohal/4.0/FactoryHalHidl.h | 36 ++++++++ .../DevicesFactoryHalInterface.cpp | 8 +- .../EffectsFactoryHalInterface.cpp | 9 +- 50 files changed, 384 insertions(+), 187 deletions(-) create mode 100644 media/libaudiohal/2.0/VersionUtils.h create mode 100644 media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h rename media/libaudiohal/4.0/{include/libaudiohal/4.0 => }/DevicesFactoryHalHybrid.h (80%) rename media/libaudiohal/4.0/{include/libaudiohal/4.0 => }/EffectsFactoryHalHidl.h (88%) create mode 100644 media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h diff --git a/media/libaudiohal/2.0/Android.bp b/media/libaudiohal/2.0/Android.bp index 574b435c36..29a32af094 100644 --- a/media/libaudiohal/2.0/Android.bp +++ b/media/libaudiohal/2.0/Android.bp @@ -1,5 +1,5 @@ -cc_library_shared { - name: "libaudiohal@2.0", +cc_defaults { + name: "libaudiohal@2.0_default", srcs: [ "DeviceHalLocal.cpp", @@ -16,15 +16,17 @@ cc_library_shared { "StreamHalHidl.cpp", ], - export_include_dirs: ["."], + export_include_dirs: ["include"], cflags: [ "-Wall", + "-Wextra", "-Werror", ], shared_libs: [ "libaudiohal_deathhandler", "libaudioutils", + "libbinder", "libcutils", "liblog", "libutils", @@ -35,16 +37,14 @@ cc_library_shared { "libhidlbase", "libhidlmemory", "libhidltransport", - "android.hardware.audio@2.0", - "android.hardware.audio.common@2.0", - "android.hardware.audio.common@2.0-util", - "android.hardware.audio.effect@2.0", + "android.hardware.audio.common-util", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libmedia_helper", "libmediautils", ], header_libs: [ + "android.hardware.audio.common.util@all-versions", "libaudiohal_headers" ], @@ -52,3 +52,14 @@ cc_library_shared { "libfmq", ], } + +cc_library_shared { + name: "libaudiohal@2.0", + defaults: ["libaudiohal@2.0_default"], + shared_libs: [ + "android.hardware.audio@2.0", + "android.hardware.audio.common@2.0", + "android.hardware.audio.common@2.0-util", + "android.hardware.audio.effect@2.0", + ], +} diff --git a/media/libaudiohal/2.0/ConversionHelperHidl.cpp b/media/libaudiohal/2.0/ConversionHelperHidl.cpp index f60bf8b69c..b7209e012a 100644 --- a/media/libaudiohal/2.0/ConversionHelperHidl.cpp +++ b/media/libaudiohal/2.0/ConversionHelperHidl.cpp @@ -25,6 +25,7 @@ using ::android::hardware::audio::V2_0::Result; namespace android { +namespace V2_0 { // static status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { @@ -100,4 +101,5 @@ void ConversionHelperHidl::emitError(const char* funcName, const char* descripti ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/ConversionHelperHidl.h b/media/libaudiohal/2.0/ConversionHelperHidl.h index c356f37e3d..593afb1eae 100644 --- a/media/libaudiohal/2.0/ConversionHelperHidl.h +++ b/media/libaudiohal/2.0/ConversionHelperHidl.h @@ -19,14 +19,17 @@ #include #include +#include #include using ::android::hardware::audio::V2_0::ParameterValue; +using CoreResult = ::android::hardware::audio::V2_0::Result; using ::android::hardware::Return; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; namespace android { +namespace V2_0 { class ConversionHelperHidl { protected: @@ -54,7 +57,7 @@ class ConversionHelperHidl { return ret.isOk() ? OK : FAILED_TRANSACTION; } - status_t processReturn(const char* funcName, const Return& ret) { + status_t processReturn(const char* funcName, const Return& ret) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -63,7 +66,7 @@ class ConversionHelperHidl { template status_t processReturn( - const char* funcName, const Return& ret, hardware::audio::V2_0::Result retval) { + const char* funcName, const Return& ret, CoreResult retval) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -73,11 +76,12 @@ class ConversionHelperHidl { private: const char* mClassName; - static status_t analyzeResult(const hardware::audio::V2_0::Result& result); + static status_t analyzeResult(const CoreResult& result); void emitError(const char* funcName, const char* description); }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/2.0/DeviceHalHidl.cpp b/media/libaudiohal/2.0/DeviceHalHidl.cpp index 5b99d70fa7..a79cedd986 100644 --- a/media/libaudiohal/2.0/DeviceHalHidl.cpp +++ b/media/libaudiohal/2.0/DeviceHalHidl.cpp @@ -24,9 +24,12 @@ #include #include +#include + #include "DeviceHalHidl.h" #include "HidlUtils.h" #include "StreamHalHidl.h" +#include "VersionUtils.h" using ::android::hardware::audio::common::V2_0::AudioConfig; using ::android::hardware::audio::common::V2_0::AudioDevice; @@ -38,6 +41,7 @@ using ::android::hardware::audio::common::V2_0::AudioPortConfig; using ::android::hardware::audio::common::V2_0::AudioMode; using ::android::hardware::audio::common::V2_0::AudioSource; using ::android::hardware::audio::common::V2_0::HidlUtils; +using ::android::hardware::audio::common::utils::mkEnumConverter; using ::android::hardware::audio::V2_0::DeviceAddress; using ::android::hardware::audio::V2_0::IPrimaryDevice; using ::android::hardware::audio::V2_0::ParameterValue; @@ -46,6 +50,7 @@ using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; namespace android { +namespace V2_0 { namespace { @@ -193,7 +198,9 @@ status_t DeviceHalHidl::setParameters(const String8& kvPairs) { hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); if (status != OK) return status; - return processReturn("setParameters", mDevice->setParameters(hidlParams)); + // TODO: change the API so that context and kvPairs are separated + return processReturn("setParameters", + utils::setParameters(mDevice, {} /* context */, hidlParams)); } status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { @@ -203,7 +210,8 @@ status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { status_t status = keysFromHal(keys, &hidlKeys); if (status != OK) return status; Result retval; - Return ret = mDevice->getParameters( + Return ret = utils::getParameters(mDevice, + {} /* context */, hidlKeys, [&](Result r, const hidl_vec& parameters) { retval = r; @@ -249,7 +257,7 @@ status_t DeviceHalHidl::openOutputStream( handle, hidlDevice, hidlConfig, - AudioOutputFlag(flags), + mkEnumConverter(flags), [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { @@ -275,12 +283,13 @@ status_t DeviceHalHidl::openInputStream( AudioConfig hidlConfig; HidlUtils::audioConfigFromHal(*config, &hidlConfig); Result retval = Result::NOT_INITIALIZED; + auto sourceMetadata = AudioSource(source); Return ret = mDevice->openInputStream( handle, hidlDevice, hidlConfig, - AudioInputFlag(flags), - AudioSource(source), + mkEnumConverter(flags), + sourceMetadata, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { @@ -356,9 +365,10 @@ status_t DeviceHalHidl::dump(int fd) { if (mDevice == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mDevice->debugDump(hidlHandle); + Return ret = mDevice->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); return processReturn("dump", ret); } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/DeviceHalHidl.h b/media/libaudiohal/2.0/DeviceHalHidl.h index 3c1cb594c2..d6b0ab0818 100644 --- a/media/libaudiohal/2.0/DeviceHalHidl.h +++ b/media/libaudiohal/2.0/DeviceHalHidl.h @@ -28,6 +28,7 @@ using ::android::hardware::audio::V2_0::IPrimaryDevice; using ::android::hardware::Return; namespace android { +namespace V2_0 { class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl { @@ -124,6 +125,7 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl virtual ~DeviceHalHidl(); }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/DeviceHalLocal.cpp b/media/libaudiohal/2.0/DeviceHalLocal.cpp index ec3bf787d9..41646593f3 100644 --- a/media/libaudiohal/2.0/DeviceHalLocal.cpp +++ b/media/libaudiohal/2.0/DeviceHalLocal.cpp @@ -23,6 +23,7 @@ #include "StreamHalLocal.h" namespace android { +namespace V2_0 { DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) : mDev(dev) { @@ -201,4 +202,5 @@ void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { mDev->close_input_stream(mDev, stream_in); } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/DeviceHalLocal.h b/media/libaudiohal/2.0/DeviceHalLocal.h index aec201a1d1..11e063acde 100644 --- a/media/libaudiohal/2.0/DeviceHalLocal.h +++ b/media/libaudiohal/2.0/DeviceHalLocal.h @@ -21,6 +21,7 @@ #include namespace android { +namespace V2_0 { class DeviceHalLocal : public DeviceHalInterface { @@ -122,6 +123,7 @@ class DeviceHalLocal : public DeviceHalInterface uint32_t version() const { return mDev->common.version; } }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp index 31da2637ab..ac65fcc55c 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp @@ -15,6 +15,7 @@ */ #include +#include #define LOG_TAG "DevicesFactoryHalHidl" //#define LOG_NDEBUG 0 @@ -32,64 +33,69 @@ using ::android::hardware::audio::V2_0::Result; using ::android::hardware::Return; namespace android { +namespace V2_0 { DevicesFactoryHalHidl::DevicesFactoryHalHidl() { - mDevicesFactory = IDevicesFactory::getService(); - if (mDevicesFactory != 0) { - // It is assumed that DevicesFactory is owned by AudioFlinger - // and thus have the same lifespan. - mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); - } else { - ALOGE("Failed to obtain IDevicesFactory service, terminating process."); + sp defaultFactory{IDevicesFactory::getService()}; + if (!defaultFactory) { + ALOGE("Failed to obtain IDevicesFactory/default service, terminating process."); exit(1); } + mDeviceFactories.push_back(defaultFactory); + for (const auto& factory : mDeviceFactories) { + // It is assumed that the DevicesFactoryHalInterface instance is owned + // by AudioFlinger and thus have the same lifespan. + factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); + } } -DevicesFactoryHalHidl::~DevicesFactoryHalHidl() { -} -// static -status_t DevicesFactoryHalHidl::nameFromHal(const char *name, IDevicesFactory::Device *device) { +static IDevicesFactory::Device idFromHal(const char *name, status_t* status) { + *status = OK; if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) { - *device = IDevicesFactory::Device::PRIMARY; - return OK; + return IDevicesFactory::Device::PRIMARY; } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) { - *device = IDevicesFactory::Device::A2DP; - return OK; + return IDevicesFactory::Device::A2DP; } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) { - *device = IDevicesFactory::Device::USB; - return OK; + return IDevicesFactory::Device::USB; } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) { - *device = IDevicesFactory::Device::R_SUBMIX; - return OK; + return IDevicesFactory::Device::R_SUBMIX; } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_STUB) == 0) { - *device = IDevicesFactory::Device::STUB; - return OK; + return IDevicesFactory::Device::STUB; } ALOGE("Invalid device name %s", name); - return BAD_VALUE; + *status = BAD_VALUE; + return {}; } status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { - if (mDevicesFactory == 0) return NO_INIT; - IDevicesFactory::Device hidlDevice; - status_t status = nameFromHal(name, &hidlDevice); + if (mDeviceFactories.empty()) return NO_INIT; + status_t status; + auto hidlId = idFromHal(name, &status); if (status != OK) return status; Result retval = Result::NOT_INITIALIZED; - Return ret = mDevicesFactory->openDevice( - hidlDevice, - [&](Result r, const sp& result) { - retval = r; - if (retval == Result::OK) { - *device = new DeviceHalHidl(result); - } - }); - if (ret.isOk()) { - if (retval == Result::OK) return OK; - else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE; - else return NO_INIT; + for (const auto& factory : mDeviceFactories) { + Return ret = factory->openDevice( + hidlId, + [&](Result r, const sp& result) { + retval = r; + if (retval == Result::OK) { + *device = new DeviceHalHidl(result); + } + }); + if (!ret.isOk()) return FAILED_TRANSACTION; + switch (retval) { + // Device was found and was initialized successfully. + case Result::OK: return OK; + // Device was found but failed to initalize. + case Result::NOT_INITIALIZED: return NO_INIT; + // Otherwise continue iterating. + default: ; + } } - return FAILED_TRANSACTION; + ALOGW("The specified device name is not recognized: \"%s\"", name); + return BAD_VALUE; } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h index e2f1ad15b5..45419f4210 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h @@ -27,6 +27,7 @@ using ::android::hardware::audio::V2_0::IDevicesFactory; namespace android { +namespace V2_0 { class DevicesFactoryHalHidl : public DevicesFactoryHalInterface { @@ -38,16 +39,15 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface private: friend class DevicesFactoryHalHybrid; - sp mDevicesFactory; - - static status_t nameFromHal(const char *name, IDevicesFactory::Device *device); + std::vector> mDeviceFactories; // Can not be constructed directly by clients. DevicesFactoryHalHidl(); - virtual ~DevicesFactoryHalHidl(); + virtual ~DevicesFactoryHalHidl() = default; }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp index 1c4be74280..7d9c15c0b2 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp +++ b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp @@ -22,15 +22,13 @@ #include "DevicesFactoryHalHidl.h" namespace android { +namespace V2_0 { DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() : mLocalFactory(new DevicesFactoryHalLocal()), mHidlFactory(new DevicesFactoryHalHidl()) { } -DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() { -} - status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp *device) { if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) { @@ -39,4 +37,5 @@ status_t DevicesFactoryHalHybrid::openDevice(const char *name, spopenDevice(name, device); } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h index abd57d6446..ba8c6f21c5 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h +++ b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h @@ -22,26 +22,27 @@ #include namespace android { +namespace V2_0 { class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface { public: + DevicesFactoryHalHybrid(); + // Opens a device with the specified name. To close the device, it is // necessary to release references to the returned object. virtual status_t openDevice(const char *name, sp *device); private: - friend class DevicesFactoryHalInterface; - - // Can not be constructed directly by clients. - DevicesFactoryHalHybrid(); - - virtual ~DevicesFactoryHalHybrid(); - sp mLocalFactory; sp mHidlFactory; }; +sp createDevicesFactoryHal() { + return new DevicesFactoryHalHybrid(); +} + +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp index 13a9acd9e9..6f6077560e 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp +++ b/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp @@ -26,6 +26,7 @@ #include "DevicesFactoryHalLocal.h" namespace android { +namespace V2_0 { static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) { @@ -66,4 +67,5 @@ status_t DevicesFactoryHalLocal::openDevice(const char *name, sp #include #include #include -#include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" #include "HidlUtils.h" @@ -34,11 +34,13 @@ using ::android::hardware::audio::effect::V2_0::Result; using ::android::hardware::audio::common::V2_0::HidlUtils; using ::android::hardware::audio::common::V2_0::AudioChannelMask; using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::audio::common::utils::mkEnumConverter; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; namespace android { +namespace V2_0 { EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { @@ -75,10 +77,10 @@ void EffectHalHidl::effectDescriptorToHal( void EffectHalHidl::effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config) { config->samplingRateHz = halConfig.samplingRate; - config->channels = AudioChannelMask(halConfig.channels); + config->channels = mkEnumConverter(halConfig.channels); config->format = AudioFormat(halConfig.format); config->accessMode = EffectBufferAccess(halConfig.accessMode); - config->mask = EffectConfigParameters(halConfig.mask); + config->mask = mkEnumConverter(halConfig.mask); } // static @@ -335,4 +337,5 @@ status_t EffectHalHidl::setConfigImpl( return result; } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/EffectHalHidl.h b/media/libaudiohal/2.0/EffectHalHidl.h index 6ffdaf1f64..6fd405e779 100644 --- a/media/libaudiohal/2.0/EffectHalHidl.h +++ b/media/libaudiohal/2.0/EffectHalHidl.h @@ -27,10 +27,12 @@ using ::android::hardware::audio::effect::V2_0::EffectBufferConfig; using ::android::hardware::audio::effect::V2_0::EffectConfig; using ::android::hardware::audio::effect::V2_0::EffectDescriptor; using ::android::hardware::audio::effect::V2_0::IEffect; +using EffectResult = ::android::hardware::audio::effect::V2_0::Result; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; namespace android { +namespace V2_0 { class EffectHalHidl : public EffectHalInterface { @@ -68,8 +70,7 @@ class EffectHalHidl : public EffectHalInterface private: friend class EffectsFactoryHalHidl; - typedef MessageQueue< - hardware::audio::effect::V2_0::Result, hardware::kSynchronizedReadWrite> StatusMQ; + typedef MessageQueue StatusMQ; sp mEffect; const uint64_t mEffectId; @@ -79,7 +80,7 @@ class EffectHalHidl : public EffectHalInterface std::unique_ptr mStatusMQ; EventFlag* mEfGroup; - static status_t analyzeResult(const hardware::audio::effect::V2_0::Result& result); + static status_t analyzeResult(const EffectResult& result); static void effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config); static void effectBufferConfigToHal( @@ -103,6 +104,7 @@ class EffectHalHidl : public EffectHalInterface status_t setProcessBuffers(); }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp index 0d40e6d9d0..ade2574325 100644 --- a/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp @@ -19,10 +19,10 @@ #include +#include "EffectsFactoryHalHidl.h" #include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" -#include "EffectsFactoryHalHidl.h" #include "HidlUtils.h" using ::android::hardware::audio::common::V2_0::HidlUtils; @@ -32,6 +32,7 @@ using ::android::hardware::audio::effect::V2_0::Result; using ::android::hardware::Return; namespace android { +namespace V2_0 { EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { mEffectsFactory = IEffectsFactory::getService(); @@ -41,9 +42,6 @@ EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFa } } -EffectsFactoryHalHidl::~EffectsFactoryHalHidl() { -} - status_t EffectsFactoryHalHidl::queryAllDescriptors() { if (mEffectsFactory == 0) return NO_INIT; Result retval = Result::NOT_INITIALIZED; @@ -132,7 +130,7 @@ status_t EffectsFactoryHalHidl::dumpEffects(int fd) { if (mEffectsFactory == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mEffectsFactory->debugDump(hidlHandle); + Return ret = mEffectsFactory->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); return processReturn(__FUNCTION__, ret); } @@ -147,4 +145,5 @@ status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/EffectsFactoryHalHidl.h b/media/libaudiohal/2.0/EffectsFactoryHalHidl.h index 82b54814b1..c7c37c2cf3 100644 --- a/media/libaudiohal/2.0/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/2.0/EffectsFactoryHalHidl.h @@ -24,6 +24,7 @@ #include "ConversionHelperHidl.h" namespace android { +namespace V2_0 { using ::android::hardware::audio::effect::V2_0::EffectDescriptor; using ::android::hardware::audio::effect::V2_0::IEffectsFactory; @@ -32,6 +33,8 @@ using ::android::hardware::hidl_vec; class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl { public: + EffectsFactoryHalHidl(); + // Returns the number of different effects in all loaded libraries. virtual status_t queryNumberEffects(uint32_t *pNumEffects); @@ -56,18 +59,17 @@ class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public Conversi sp* buffer) override; private: - friend class EffectsFactoryHalInterface; - sp mEffectsFactory; hidl_vec mLastDescriptors; - // Can not be constructed directly by clients. - EffectsFactoryHalHidl(); - virtual ~EffectsFactoryHalHidl(); - status_t queryAllDescriptors(); }; +sp createEffectsFactoryHal() { + return new EffectsFactoryHalHidl(); +} + +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/StreamHalHidl.cpp b/media/libaudiohal/2.0/StreamHalHidl.cpp index 9869cd25d9..b6e83658fd 100644 --- a/media/libaudiohal/2.0/StreamHalHidl.cpp +++ b/media/libaudiohal/2.0/StreamHalHidl.cpp @@ -25,6 +25,7 @@ #include "DeviceHalHidl.h" #include "EffectHalHidl.h" #include "StreamHalHidl.h" +#include "VersionUtils.h" using ::android::hardware::audio::common::V2_0::AudioChannelMask; using ::android::hardware::audio::common::V2_0::AudioFormat; @@ -43,6 +44,7 @@ using ::android::hardware::Void; using ReadCommand = ::android::hardware::audio::V2_0::IStreamIn::ReadCommand; namespace android { +namespace V2_0 { StreamHalHidl::StreamHalHidl(IStream *stream) : ConversionHelperHidl("Stream"), @@ -55,7 +57,7 @@ StreamHalHidl::StreamHalHidl(IStream *stream) if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { // Obtain audio properties (see StreamHalHidl::getAudioProperties() below). Return ret = mStream->getAudioProperties( - [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + [&](auto sr, auto m, auto f) { mStreamPowerLog.init(sr, static_cast(m), static_cast(f)); @@ -95,7 +97,7 @@ status_t StreamHalHidl::getAudioProperties( uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { if (!mStream) return NO_INIT; Return ret = mStream->getAudioProperties( - [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { + [&](uint32_t sr, auto m, auto f) { *sampleRate = sr; *mask = static_cast(m); *format = static_cast(f); @@ -108,7 +110,8 @@ status_t StreamHalHidl::setParameters(const String8& kvPairs) { hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); if (status != OK) return status; - return processReturn("setParameters", mStream->setParameters(hidlParams)); + return processReturn("setParameters", + utils::setParameters(mStream, hidlParams, {} /* options */)); } status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { @@ -118,7 +121,9 @@ status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { status_t status = keysFromHal(keys, &hidlKeys); if (status != OK) return status; Result retval; - Return ret = mStream->getParameters( + Return ret = utils::getParameters( + mStream, + {} /* context */, hidlKeys, [&](Result r, const hidl_vec& parameters) { retval = r; @@ -150,7 +155,7 @@ status_t StreamHalHidl::dump(int fd) { if (!mStream) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; - Return ret = mStream->debugDump(hidlHandle); + Return ret = mStream->debug(hidlHandle, {} /* options */); native_handle_delete(hidlHandle); mStreamPowerLog.dump(fd); return processReturn("dump", ret); @@ -765,4 +770,5 @@ status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& /* sinkMetadata return INVALID_OPERATION; } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/StreamHalHidl.h b/media/libaudiohal/2.0/StreamHalHidl.h index ebad8aecc8..872762088d 100644 --- a/media/libaudiohal/2.0/StreamHalHidl.h +++ b/media/libaudiohal/2.0/StreamHalHidl.h @@ -41,6 +41,7 @@ using WriteCommand = ::android::hardware::audio::V2_0::IStreamOut::WriteCommand; using WriteStatus = ::android::hardware::audio::V2_0::IStreamOut::WriteStatus; namespace android { +namespace V2_0 { class DeviceHalHidl; @@ -243,6 +244,7 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { status_t prepareForReading(size_t bufferSize); }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/StreamHalLocal.cpp b/media/libaudiohal/2.0/StreamHalLocal.cpp index 98107e5ec1..7951f79b13 100644 --- a/media/libaudiohal/2.0/StreamHalLocal.cpp +++ b/media/libaudiohal/2.0/StreamHalLocal.cpp @@ -22,8 +22,10 @@ #include "DeviceHalLocal.h" #include "StreamHalLocal.h" +#include "VersionUtils.h" namespace android { +namespace V2_0 { StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) : mDevice(device), @@ -344,4 +346,5 @@ status_t StreamInHalLocal::getActiveMicrophones( return INVALID_OPERATION; } +} // namespace V2_0 } // namespace android diff --git a/media/libaudiohal/2.0/StreamHalLocal.h b/media/libaudiohal/2.0/StreamHalLocal.h index cda8d0c60c..a4a71e1745 100644 --- a/media/libaudiohal/2.0/StreamHalLocal.h +++ b/media/libaudiohal/2.0/StreamHalLocal.h @@ -21,6 +21,7 @@ #include "StreamPowerLog.h" namespace android { +namespace V2_0 { class DeviceHalLocal; @@ -214,6 +215,7 @@ class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { virtual ~StreamInHalLocal(); }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/StreamPowerLog.h b/media/libaudiohal/2.0/StreamPowerLog.h index a78b1aa042..db2f0689d5 100644 --- a/media/libaudiohal/2.0/StreamPowerLog.h +++ b/media/libaudiohal/2.0/StreamPowerLog.h @@ -23,6 +23,7 @@ #include namespace android { +namespace V2_0 { class StreamPowerLog { public: @@ -97,6 +98,7 @@ private: size_t mFrameSize; }; +} // namespace V2_0 } // namespace android #endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H diff --git a/media/libaudiohal/2.0/VersionUtils.h b/media/libaudiohal/2.0/VersionUtils.h new file mode 100644 index 0000000000..54e5ae02be --- /dev/null +++ b/media/libaudiohal/2.0/VersionUtils.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_VERSION_UTILS_H +#define ANDROID_HARDWARE_VERSION_UTILS_H + +#include +#include + +using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::Return; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; + +namespace android { +namespace V2_0 { +namespace utils { + +template +Return getParameters(T& object, hidl_vec /*context*/, + hidl_vec keys, Callback callback) { + return object->getParameters(keys, callback); +} + +template +Return setParameters(T& object, hidl_vec /*context*/, + hidl_vec keys) { + return object->setParameters(keys); +} + +} // namespace utils +} // namespace V2_0 +} // namespace android + +#endif // ANDROID_HARDWARE_VERSION_UTILS_H diff --git a/media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h b/media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h new file mode 100644 index 0000000000..95335c0411 --- /dev/null +++ b/media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H +#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H + +/** @file Library entry points to create the HAL factories. */ + +#include +#include +#include + +namespace android { +namespace V2_0 { + +sp createEffectsFactoryHal(); + +sp createDevicesFactoryHal(); + +} // namespace V2_0 +} // namespace android + +#endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H diff --git a/media/libaudiohal/4.0/Android.bp b/media/libaudiohal/4.0/Android.bp index 833defad81..2fe0a17ff8 100644 --- a/media/libaudiohal/4.0/Android.bp +++ b/media/libaudiohal/4.0/Android.bp @@ -1,5 +1,5 @@ -cc_library_shared { - name: "libaudiohal@4.0", +cc_defaults { + name: "libaudiohal@4.0_default", srcs: [ "DeviceHalLocal.cpp", @@ -37,11 +37,7 @@ cc_library_shared { "libhidlbase", "libhidlmemory", "libhidltransport", - "android.hardware.audio@4.0", "android.hardware.audio.common-util", - "android.hardware.audio.common@4.0", - "android.hardware.audio.common@4.0-util", - "android.hardware.audio.effect@4.0", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libmedia_helper", @@ -56,3 +52,14 @@ cc_library_shared { "libfmq", ], } + +cc_library_shared { + name: "libaudiohal@4.0", + defaults: ["libaudiohal@4.0_default"], + shared_libs: [ + "android.hardware.audio@4.0", + "android.hardware.audio.common@4.0", + "android.hardware.audio.common@4.0-util", + "android.hardware.audio.effect@4.0", + ], +} diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.cpp b/media/libaudiohal/4.0/ConversionHelperHidl.cpp index fe27504b9c..8f64c969ed 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.cpp +++ b/media/libaudiohal/4.0/ConversionHelperHidl.cpp @@ -107,7 +107,7 @@ void ConversionHelperHidl::emitError(const char* funcName, const char* descripti } // TODO: Use the same implementation in the hal when it moves to a util library. -std::string deviceAddressToHal(const DeviceAddress& address) { +static std::string deviceAddressToHal(const DeviceAddress& address) { // HAL assumes that the address is NUL-terminated. char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN]; memset(halAddress, 0, sizeof(halAddress)); @@ -141,7 +141,7 @@ std::string deviceAddressToHal(const DeviceAddress& address) { //local conversion helpers -audio_microphone_channel_mapping_t channelMappingToHal(AudioMicrophoneChannelMapping mapping) { +static audio_microphone_channel_mapping_t channelMappingToHal(AudioMicrophoneChannelMapping mapping) { switch (mapping) { case AudioMicrophoneChannelMapping::UNUSED: return AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED; @@ -154,7 +154,7 @@ audio_microphone_channel_mapping_t channelMappingToHal(AudioMicrophoneChannelMa } } -audio_microphone_location_t locationToHal(AudioMicrophoneLocation location) { +static audio_microphone_location_t locationToHal(AudioMicrophoneLocation location) { switch (location) { case AudioMicrophoneLocation::UNKNOWN: return AUDIO_MICROPHONE_LOCATION_UNKNOWN; @@ -168,7 +168,7 @@ audio_microphone_location_t locationToHal(AudioMicrophoneLocation location) { LOG_ALWAYS_FATAL("Unknown locationToHal conversion %d", location); } } -audio_microphone_directionality_t directionalityToHal(AudioMicrophoneDirectionality dir) { +static audio_microphone_directionality_t directionalityToHal(AudioMicrophoneDirectionality dir) { switch (dir) { case AudioMicrophoneDirectionality::UNKNOWN: return AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN; diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.h b/media/libaudiohal/4.0/ConversionHelperHidl.h index 8823a8dc82..1dc113c51c 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.h +++ b/media/libaudiohal/4.0/ConversionHelperHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H -#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H +#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H #include #include @@ -23,6 +23,7 @@ #include using ::android::hardware::audio::V4_0::ParameterValue; +using CoreResult = ::android::hardware::audio::V4_0::Result; using ::android::hardware::audio::V4_0::MicrophoneInfo; using ::android::hardware::Return; using ::android::hardware::hidl_string; @@ -59,7 +60,7 @@ class ConversionHelperHidl { return ret.isOk() ? OK : FAILED_TRANSACTION; } - status_t processReturn(const char* funcName, const Return& ret) { + status_t processReturn(const char* funcName, const Return& ret) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -68,7 +69,7 @@ class ConversionHelperHidl { template status_t processReturn( - const char* funcName, const Return& ret, hardware::audio::V4_0::Result retval) { + const char* funcName, const Return& ret, CoreResult retval) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } @@ -78,7 +79,7 @@ class ConversionHelperHidl { private: const char* mClassName; - static status_t analyzeResult(const hardware::audio::V4_0::Result& result); + static status_t analyzeResult(const CoreResult& result); void emitError(const char* funcName, const char* description); }; @@ -86,4 +87,4 @@ class ConversionHelperHidl { } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_4_0_H +#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/4.0/DeviceHalHidl.cpp index 6facca96ec..c547994ae5 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.cpp +++ b/media/libaudiohal/4.0/DeviceHalHidl.cpp @@ -287,13 +287,13 @@ status_t DeviceHalHidl::openInputStream( Result retval = Result::NOT_INITIALIZED; // TODO: correctly propagate the tracks sources and volume // for now, only send the main source at 1dbfs - SinkMetadata metadata = {{{AudioSource(source), 1}}}; + SinkMetadata sourceMetadata = {{{AudioSource(source), 1}}}; Return ret = mDevice->openInputStream( handle, hidlDevice, hidlConfig, - flags, - metadata, + mkEnumConverter(flags), + sourceMetadata, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { diff --git a/media/libaudiohal/4.0/DeviceHalHidl.h b/media/libaudiohal/4.0/DeviceHalHidl.h index 0bd2175109..5c48f1253b 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.h +++ b/media/libaudiohal/4.0/DeviceHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_H +#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H #include #include @@ -128,4 +128,4 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalLocal.h b/media/libaudiohal/4.0/DeviceHalLocal.h index 08341a4ebe..e652f2599a 100644 --- a/media/libaudiohal/4.0/DeviceHalLocal.h +++ b/media/libaudiohal/4.0/DeviceHalLocal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H -#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H +#ifndef ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H +#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H #include #include @@ -126,4 +126,4 @@ class DeviceHalLocal : public DeviceHalInterface } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_4_0_H +#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp index c566728b26..9c78d8f60d 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp @@ -54,12 +54,21 @@ DevicesFactoryHalHidl::DevicesFactoryHalHidl() { } } + +static const char* idFromHal(const char *name, status_t* status) { + *status = OK; + return name; +} + status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { if (mDeviceFactories.empty()) return NO_INIT; + status_t status; + auto hidlId = idFromHal(name, &status); + if (status != OK) return status; Result retval = Result::NOT_INITIALIZED; for (const auto& factory : mDeviceFactories) { Return ret = factory->openDevice( - name, + hidlId, [&](Result r, const sp& result) { retval = r; if (retval == Result::OK) { diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h index c97178f765..5412d50800 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H #include #include @@ -50,4 +50,4 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp index 7ff1ec7df2..d40ffadf31 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "DevicesFactoryHalHybrid" //#define LOG_NDEBUG 0 -#include +#include "DevicesFactoryHalHybrid.h" #include "DevicesFactoryHalLocal.h" #include "DevicesFactoryHalHidl.h" @@ -29,9 +29,6 @@ DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() mHidlFactory(new DevicesFactoryHalHidl()) { } -DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() { -} - status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp *device) { if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) { diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h similarity index 80% rename from media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h rename to media/libaudiohal/4.0/DevicesFactoryHalHybrid.h index abf6de00c7..1dab434913 100644 --- a/media/libaudiohal/4.0/include/libaudiohal/4.0/DevicesFactoryHalHybrid.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H +#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H #include #include @@ -27,23 +27,22 @@ namespace V4_0 { class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface { public: + DevicesFactoryHalHybrid(); + // Opens a device with the specified name. To close the device, it is // necessary to release references to the returned object. virtual status_t openDevice(const char *name, sp *device); private: - friend class DevicesFactoryHalInterface; - - // Can not be constructed directly by clients. - DevicesFactoryHalHybrid(); - - virtual ~DevicesFactoryHalHybrid(); - sp mLocalFactory; sp mHidlFactory; }; +sp createDevicesFactoryHal() { + return new DevicesFactoryHalHybrid(); +} + } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_4_0_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.h b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h index bc1c5216b7..313bdc739b 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalLocal.h +++ b/media/libaudiohal/4.0/DevicesFactoryHalLocal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H +#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H +#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H #include #include @@ -45,4 +45,4 @@ class DevicesFactoryHalLocal : public DevicesFactoryHalInterface } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_4_0_H +#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.h b/media/libaudiohal/4.0/EffectBufferHalHidl.h index 6d578c6a24..8ad6ff56be 100644 --- a/media/libaudiohal/4.0/EffectBufferHalHidl.h +++ b/media/libaudiohal/4.0/EffectBufferHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H #include #include @@ -75,4 +75,4 @@ class EffectBufferHalHidl : public EffectBufferHalInterface } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp index c99c4c859f..2e1e3524a7 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectHalHidl.cpp @@ -22,7 +22,6 @@ #include #include -#include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" #include "HidlUtils.h" diff --git a/media/libaudiohal/4.0/EffectHalHidl.h b/media/libaudiohal/4.0/EffectHalHidl.h index 5a4dab16fb..63310b19cc 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.h +++ b/media/libaudiohal/4.0/EffectHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H #include #include @@ -27,6 +27,7 @@ using ::android::hardware::audio::effect::V4_0::EffectBufferConfig; using ::android::hardware::audio::effect::V4_0::EffectConfig; using ::android::hardware::audio::effect::V4_0::EffectDescriptor; using ::android::hardware::audio::effect::V4_0::IEffect; +using EffectResult = ::android::hardware::audio::effect::V4_0::Result; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; @@ -69,8 +70,7 @@ class EffectHalHidl : public EffectHalInterface private: friend class EffectsFactoryHalHidl; - typedef MessageQueue< - hardware::audio::effect::V4_0::Result, hardware::kSynchronizedReadWrite> StatusMQ; + typedef MessageQueue StatusMQ; sp mEffect; const uint64_t mEffectId; @@ -80,7 +80,7 @@ class EffectHalHidl : public EffectHalInterface std::unique_ptr mStatusMQ; EventFlag* mEfGroup; - static status_t analyzeResult(const hardware::audio::effect::V4_0::Result& result); + static status_t analyzeResult(const EffectResult& result); static void effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config); static void effectBufferConfigToHal( @@ -107,4 +107,4 @@ class EffectHalHidl : public EffectHalInterface } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp index dfed784e82..e23aec9283 100644 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp @@ -18,8 +18,8 @@ //#define LOG_NDEBUG 0 #include -#include +#include "EffectsFactoryHalHidl.h" #include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" @@ -42,9 +42,6 @@ EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFa } } -EffectsFactoryHalHidl::~EffectsFactoryHalHidl() { -} - status_t EffectsFactoryHalHidl::queryAllDescriptors() { if (mEffectsFactory == 0) return NO_INIT; Result retval = Result::NOT_INITIALIZED; diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h similarity index 88% rename from media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h rename to media/libaudiohal/4.0/EffectsFactoryHalHidl.h index 680b7a1322..2152c6fa21 100644 --- a/media/libaudiohal/4.0/include/libaudiohal/4.0/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H +#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H #include #include @@ -33,6 +33,8 @@ using ::android::hardware::hidl_vec; class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl { public: + EffectsFactoryHalHidl(); + // Returns the number of different effects in all loaded libraries. virtual status_t queryNumberEffects(uint32_t *pNumEffects); @@ -57,19 +59,17 @@ class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public Conversi sp* buffer) override; private: - friend class EffectsFactoryHalInterface; - sp mEffectsFactory; hidl_vec mLastDescriptors; - // Can not be constructed directly by clients. - EffectsFactoryHalHidl(); - virtual ~EffectsFactoryHalHidl(); - status_t queryAllDescriptors(); }; +sp createEffectsFactoryHal() { + return new EffectsFactoryHalHidl(); +} + } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/4.0/StreamHalHidl.cpp index 1c2fdb092d..250dcc2049 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.cpp +++ b/media/libaudiohal/4.0/StreamHalHidl.cpp @@ -28,20 +28,14 @@ #include "VersionUtils.h" using ::android::hardware::audio::common::V4_0::AudioChannelMask; -using ::android::hardware::audio::common::V4_0::AudioContentType; using ::android::hardware::audio::common::V4_0::AudioFormat; -using ::android::hardware::audio::common::V4_0::AudioSource; -using ::android::hardware::audio::common::V4_0::AudioUsage; using ::android::hardware::audio::common::V4_0::ThreadInfo; using ::android::hardware::audio::V4_0::AudioDrain; using ::android::hardware::audio::V4_0::IStreamOutCallback; using ::android::hardware::audio::V4_0::MessageQueueFlagBits; -using ::android::hardware::audio::V4_0::MicrophoneInfo; using ::android::hardware::audio::V4_0::MmapBufferInfo; using ::android::hardware::audio::V4_0::MmapPosition; using ::android::hardware::audio::V4_0::ParameterValue; -using ::android::hardware::audio::V4_0::PlaybackTrackMetadata; -using ::android::hardware::audio::V4_0::RecordTrackMetadata; using ::android::hardware::audio::V4_0::Result; using ::android::hardware::audio::V4_0::TimeSpec; using ::android::hardware::MQDescriptorSync; @@ -49,6 +43,13 @@ using ::android::hardware::Return; using ::android::hardware::Void; using ReadCommand = ::android::hardware::audio::V4_0::IStreamIn::ReadCommand; +using ::android::hardware::audio::common::V4_0::AudioContentType; +using ::android::hardware::audio::common::V4_0::AudioSource; +using ::android::hardware::audio::common::V4_0::AudioUsage; +using ::android::hardware::audio::V4_0::MicrophoneInfo; +using ::android::hardware::audio::V4_0::PlaybackTrackMetadata; +using ::android::hardware::audio::V4_0::RecordTrackMetadata; + namespace android { namespace V4_0 { @@ -782,7 +783,6 @@ status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { } } - status_t StreamInHalHidl::getActiveMicrophones( std::vector *microphonesInfo) { if (!mStream) return NO_INIT; diff --git a/media/libaudiohal/4.0/StreamHalHidl.h b/media/libaudiohal/4.0/StreamHalHidl.h index 2dda0f8dcd..d0948188ed 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.h +++ b/media/libaudiohal/4.0/StreamHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H -#define ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H +#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_H +#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H #include @@ -247,4 +247,4 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_4_0_H +#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalLocal.h b/media/libaudiohal/4.0/StreamHalLocal.h index 72375098d6..208b70e3f6 100644 --- a/media/libaudiohal/4.0/StreamHalLocal.h +++ b/media/libaudiohal/4.0/StreamHalLocal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H -#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H +#ifndef ANDROID_HARDWARE_STREAM_HAL_LOCAL_H +#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_H #include #include "StreamPowerLog.h" @@ -218,4 +218,4 @@ class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_4_0_H +#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H diff --git a/media/libaudiohal/4.0/StreamPowerLog.h b/media/libaudiohal/4.0/StreamPowerLog.h index 57b720147f..64998b21af 100644 --- a/media/libaudiohal/4.0/StreamPowerLog.h +++ b/media/libaudiohal/4.0/StreamPowerLog.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H -#define ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H +#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_H +#define ANDROID_HARDWARE_STREAM_POWER_LOG_H #include #include @@ -101,4 +101,4 @@ private: } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_4_0_H +#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H diff --git a/media/libaudiohal/4.0/VersionUtils.h b/media/libaudiohal/4.0/VersionUtils.h index 1246c2e949..4c5b7d9a7a 100644 --- a/media/libaudiohal/4.0/VersionUtils.h +++ b/media/libaudiohal/4.0/VersionUtils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_VERSION_UTILS_4_0_H -#define ANDROID_HARDWARE_VERSION_UTILS_4_0_H +#ifndef ANDROID_HARDWARE_VERSION_UTILS_H +#define ANDROID_HARDWARE_VERSION_UTILS_H #include #include @@ -46,4 +46,4 @@ Return setParameters(T& object, hidl_vec context, } // namespace V4_0 } // namespace android -#endif // ANDROID_HARDWARE_VERSION_UTILS_4_0_H +#endif // ANDROID_HARDWARE_VERSION_UTILS_H diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h new file mode 100644 index 0000000000..3d37687258 --- /dev/null +++ b/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H +#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H + +/** @file Library entry points to create the HAL factories. */ + +#include +#include +#include + +namespace android { +namespace V4_0 { + +sp createEffectsFactoryHal(); + +sp createDevicesFactoryHal(); + +} // namespace V4_0 +} // namespace android + +#endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp index 4c8eaf6653..9f70e4f997 100644 --- a/media/libaudiohal/DevicesFactoryHalInterface.cpp +++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp @@ -17,18 +17,18 @@ #include #include -#include -#include +#include +#include namespace android { // static sp DevicesFactoryHalInterface::create() { if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) { - return new V4_0::DevicesFactoryHalHybrid(); + return V4_0::createDevicesFactoryHal(); } if (hardware::audio::V2_0::IDevicesFactory::getService() != nullptr) { - return new DevicesFactoryHalHybrid(); + return V2_0::createDevicesFactoryHal(); } return nullptr; } diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index ead1fa2a02..50f976fd6e 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -17,19 +17,18 @@ #include #include -#include -#include - +#include +#include namespace android { // static sp EffectsFactoryHalInterface::create() { if (hardware::audio::effect::V4_0::IEffectsFactory::getService() != nullptr) { - return new V4_0::EffectsFactoryHalHidl(); + return V4_0::createEffectsFactoryHal(); } if (hardware::audio::effect::V2_0::IEffectsFactory::getService() != nullptr) { - return new EffectsFactoryHalHidl(); + return V2_0::createEffectsFactoryHal(); } return nullptr; } -- GitLab From 85a41536a4885b12120ddafb4d0b3fee8a8613b6 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 18 Jun 2018 13:17:24 -0700 Subject: [PATCH 0093/1530] Add __INTRODUCED_IN to audio/camera/media headers. Bug: https://github.com/android-ndk/ndk/issues/706 Test: builds Change-Id: I519d4fd825d40c3f72515f29a7e5b12c23a1749a --- .../include/camera/NdkCameraCaptureSession.h | 21 ++- camera/ndk/include/camera/NdkCameraDevice.h | 40 +++--- camera/ndk/include/camera/NdkCameraError.h | 4 - camera/ndk/include/camera/NdkCameraManager.h | 25 ++-- camera/ndk/include/camera/NdkCameraMetadata.h | 14 +- .../include/camera/NdkCameraMetadataTags.h | 5 - camera/ndk/include/camera/NdkCaptureRequest.h | 41 +++--- camera/ndk/libcamera2ndk.map.txt | 16 +-- media/libaaudio/include/aaudio/AAudio.h | 120 +++++++++--------- media/ndk/include/media/NdkImage.h | 32 ++--- media/ndk/include/media/NdkImageReader.h | 36 ++---- media/ndk/include/media/NdkMediaCodec.h | 92 ++++++-------- media/ndk/include/media/NdkMediaCrypto.h | 12 +- media/ndk/include/media/NdkMediaDataSource.h | 16 +-- media/ndk/include/media/NdkMediaDrm.h | 56 ++++---- media/ndk/include/media/NdkMediaExtractor.h | 59 ++++----- media/ndk/include/media/NdkMediaFormat.h | 44 +++---- media/ndk/include/media/NdkMediaMuxer.h | 22 ++-- 18 files changed, 288 insertions(+), 367 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 78e062a889..9bf8247b95 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -45,8 +45,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - /** * ACameraCaptureSession is an opaque type that manages frame captures of a camera device. * @@ -434,7 +432,7 @@ typedef struct ACameraDevice ACameraDevice; * */ camera_status_t ACameraCaptureSession_getDevice( - ACameraCaptureSession* session, /*out*/ACameraDevice** device); + ACameraCaptureSession* session, /*out*/ACameraDevice** device) __INTRODUCED_IN(24); /** * Submit an array of requests to be captured in sequence as a burst in the minimum of time possible. @@ -472,7 +470,7 @@ camera_status_t ACameraCaptureSession_capture( ACameraCaptureSession* session, /*optional*/ACameraCaptureSession_captureCallbacks* callbacks, int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId); + /*optional*/int* captureSequenceId) __INTRODUCED_IN(24); /** * Request endlessly repeating capture of a sequence of images by this capture session. @@ -526,7 +524,7 @@ camera_status_t ACameraCaptureSession_setRepeatingRequest( ACameraCaptureSession* session, /*optional*/ACameraCaptureSession_captureCallbacks* callbacks, int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId); + /*optional*/int* captureSequenceId) __INTRODUCED_IN(24); /** * Cancel any ongoing repeating capture set by {@link ACameraCaptureSession_setRepeatingRequest}. @@ -549,7 +547,8 @@ camera_status_t ACameraCaptureSession_setRepeatingRequest( *
  • {@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error
  • *
  • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons
  • */ -camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session); +camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session) + __INTRODUCED_IN(24); /** * Discard all captures currently pending and in-progress as fast as possible. @@ -589,11 +588,8 @@ camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* sessi *
  • {@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error
  • *
  • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons
  • */ -camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 28 +camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session) + __INTRODUCED_IN(24); typedef struct ACaptureSessionOutput ACaptureSessionOutput; @@ -638,8 +634,7 @@ typedef struct ACaptureSessionOutput ACaptureSessionOutput; *
  • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons
  • */ camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session, - ACaptureSessionOutput* output); -#endif /* __ANDROID_API__ >= 28 */ + ACaptureSessionOutput* output) __INTRODUCED_IN(28); __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index b715b12e15..bdd27f9942 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -44,8 +44,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - /** * ACameraDevice is opaque type that provides access to a camera device. * @@ -176,7 +174,7 @@ typedef ACameraDevice_StateCallbacks ACameraDevice_stateCallbacks; *
  • {@link ACAMERA_OK} if the method call succeeds.
  • *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if device is NULL.
  • */ -camera_status_t ACameraDevice_close(ACameraDevice* device); +camera_status_t ACameraDevice_close(ACameraDevice* device) __INTRODUCED_IN(24); /** * Return the camera id associated with this camera device. @@ -187,7 +185,7 @@ camera_status_t ACameraDevice_close(ACameraDevice* device); * delete/free by the application. Also the returned string must not be used after the device * has been closed. */ -const char* ACameraDevice_getId(const ACameraDevice* device); +const char* ACameraDevice_getId(const ACameraDevice* device) __INTRODUCED_IN(24); typedef enum { /** @@ -290,7 +288,7 @@ typedef enum { */ camera_status_t ACameraDevice_createCaptureRequest( const ACameraDevice* device, ACameraDevice_request_template templateId, - /*out*/ACaptureRequest** request); + /*out*/ACaptureRequest** request) __INTRODUCED_IN(24); typedef struct ACaptureSessionOutputContainer ACaptureSessionOutputContainer; @@ -313,7 +311,7 @@ typedef struct ACaptureSessionOutput ACaptureSessionOutput; *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if container is NULL.
  • */ camera_status_t ACaptureSessionOutputContainer_create( - /*out*/ACaptureSessionOutputContainer** container); + /*out*/ACaptureSessionOutputContainer** container) __INTRODUCED_IN(24); /** * Free a capture session output container. @@ -322,7 +320,8 @@ camera_status_t ACaptureSessionOutputContainer_create( * * @see ACaptureSessionOutputContainer_create */ -void ACaptureSessionOutputContainer_free(ACaptureSessionOutputContainer* container); +void ACaptureSessionOutputContainer_free(ACaptureSessionOutputContainer* container) + __INTRODUCED_IN(24); /** * Create a ACaptureSessionOutput object. @@ -344,7 +343,7 @@ void ACaptureSessionOutputContainer_free(ACaptureSessionOutputContain * @see ACaptureSessionOutputContainer_add */ camera_status_t ACaptureSessionOutput_create( - ANativeWindow* anw, /*out*/ACaptureSessionOutput** output); + ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24); /** * Free a ACaptureSessionOutput object. @@ -353,7 +352,7 @@ camera_status_t ACaptureSessionOutput_create( * * @see ACaptureSessionOutput_create */ -void ACaptureSessionOutput_free(ACaptureSessionOutput* output); +void ACaptureSessionOutput_free(ACaptureSessionOutput* output) __INTRODUCED_IN(24); /** * Add an {@link ACaptureSessionOutput} object to {@link ACaptureSessionOutputContainer}. @@ -366,7 +365,8 @@ void ACaptureSessionOutput_free(ACaptureSessionOutput* output); *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if container or output is NULL.
  • */ camera_status_t ACaptureSessionOutputContainer_add( - ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output); + ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output) + __INTRODUCED_IN(24); /** * Remove an {@link ACaptureSessionOutput} object from {@link ACaptureSessionOutputContainer}. @@ -382,7 +382,8 @@ camera_status_t ACaptureSessionOutputContainer_add( *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if container or output is NULL.
  • */ camera_status_t ACaptureSessionOutputContainer_remove( - ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output); + ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output) + __INTRODUCED_IN(24); /** * Create a new camera capture session by providing the target output set of {@link ANativeWindow} @@ -663,11 +664,7 @@ camera_status_t ACameraDevice_createCaptureSession( ACameraDevice* device, const ACaptureSessionOutputContainer* outputs, const ACameraCaptureSession_stateCallbacks* callbacks, - /*out*/ACameraCaptureSession** session); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 28 + /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(24); /** * Create a shared ACaptureSessionOutput object. @@ -691,7 +688,7 @@ camera_status_t ACameraDevice_createCaptureSession( * @see ACaptureSessionOutputContainer_add */ camera_status_t ACaptureSessionSharedOutput_create( - ANativeWindow* anw, /*out*/ACaptureSessionOutput** output); + ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28); /** * Add a native window to shared ACaptureSessionOutput. @@ -708,7 +705,8 @@ camera_status_t ACaptureSessionSharedOutput_create( * window associated with ACaptureSessionOutput; or anw is already present inside * ACaptureSessionOutput. */ -camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, ANativeWindow *anw); +camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, + ANativeWindow *anw) __INTRODUCED_IN(28); /** * Remove a native window from shared ACaptureSessionOutput. @@ -724,7 +722,7 @@ camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, A * ACaptureSessionOutput. */ camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output, - ANativeWindow* anw); + ANativeWindow* anw) __INTRODUCED_IN(28); /** * Create a new camera capture session similar to {@link ACameraDevice_createCaptureSession}. This @@ -757,9 +755,7 @@ camera_status_t ACameraDevice_createCaptureSessionWithSessionParameters( const ACaptureSessionOutputContainer* outputs, const ACaptureRequest* sessionParameters, const ACameraCaptureSession_stateCallbacks* callbacks, - /*out*/ACameraCaptureSession** session); - -#endif /* __ANDROID_API__ >= 28 */ + /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(28); __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h index 6b5815502b..e19ce362f4 100644 --- a/camera/ndk/include/camera/NdkCameraError.h +++ b/camera/ndk/include/camera/NdkCameraError.h @@ -40,8 +40,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - typedef enum { ACAMERA_OK = 0, @@ -132,8 +130,6 @@ typedef enum { ACAMERA_ERROR_PERMISSION_DENIED = ACAMERA_ERROR_BASE - 13, } camera_status_t; -#endif /* __ANDROID_API__ >= 24 */ - __END_DECLS #endif /* _NDK_CAMERA_ERROR_H */ diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index e5b3ad8aac..a1cca4d926 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -44,8 +44,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - /** * ACameraManager is opaque type that provides access to camera service. * @@ -65,14 +63,14 @@ typedef struct ACameraManager ACameraManager; * @return a {@link ACameraManager} instance. * */ -ACameraManager* ACameraManager_create(); +ACameraManager* ACameraManager_create() __INTRODUCED_IN(24); /** *

    Delete the {@link ACameraManager} instance and free its resources.

    * * @param manager the {@link ACameraManager} instance to be deleted. */ -void ACameraManager_delete(ACameraManager* manager); +void ACameraManager_delete(ACameraManager* manager) __INTRODUCED_IN(24); /// Struct to hold list of camera devices typedef struct ACameraIdList { @@ -102,14 +100,14 @@ typedef struct ACameraIdList { *
  • {@link ACAMERA_ERROR_NOT_ENOUGH_MEMORY} if allocating memory fails.
  • */ camera_status_t ACameraManager_getCameraIdList(ACameraManager* manager, - /*out*/ACameraIdList** cameraIdList); + /*out*/ACameraIdList** cameraIdList) __INTRODUCED_IN(24); /** * Delete a list of camera devices allocated via {@link ACameraManager_getCameraIdList}. * * @param cameraIdList the {@link ACameraIdList} to be deleted. */ -void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList); +void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList) __INTRODUCED_IN(24); /** * Definition of camera availability callbacks. @@ -120,7 +118,8 @@ void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList); * argument is owned by camera framework and will become invalid immediately after * this callback returns. */ -typedef void (*ACameraManager_AvailabilityCallback)(void* context, const char* cameraId); +typedef void (*ACameraManager_AvailabilityCallback)(void* context, + const char* cameraId) __INTRODUCED_IN(24); /** * A listener for camera devices becoming available or unavailable to open. @@ -168,7 +167,8 @@ typedef struct ACameraManager_AvailabilityListener { * {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL. */ camera_status_t ACameraManager_registerAvailabilityCallback( - ACameraManager* manager, const ACameraManager_AvailabilityCallbacks* callback); + ACameraManager* manager, + const ACameraManager_AvailabilityCallbacks* callback) __INTRODUCED_IN(24); /** * Unregister camera availability callbacks. @@ -185,7 +185,8 @@ camera_status_t ACameraManager_registerAvailabilityCallback( * {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL. */ camera_status_t ACameraManager_unregisterAvailabilityCallback( - ACameraManager* manager, const ACameraManager_AvailabilityCallbacks* callback); + ACameraManager* manager, + const ACameraManager_AvailabilityCallbacks* callback) __INTRODUCED_IN(24); /** * Query the capabilities of a camera device. These capabilities are @@ -211,7 +212,7 @@ camera_status_t ACameraManager_unregisterAvailabilityCallback( */ camera_status_t ACameraManager_getCameraCharacteristics( ACameraManager* manager, const char* cameraId, - /*out*/ACameraMetadata** characteristics); + /*out*/ACameraMetadata** characteristics) __INTRODUCED_IN(24); /** * Open a connection to a camera with the given ID. The opened camera device will be @@ -271,9 +272,7 @@ camera_status_t ACameraManager_getCameraCharacteristics( camera_status_t ACameraManager_openCamera( ACameraManager* manager, const char* cameraId, ACameraDevice_StateCallbacks* callback, - /*out*/ACameraDevice** device); - -#endif /* __ANDROID_API__ >= 24 */ + /*out*/ACameraDevice** device) __INTRODUCED_IN(24); __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h index bdb1587198..2078da7d31 100644 --- a/camera/ndk/include/camera/NdkCameraMetadata.h +++ b/camera/ndk/include/camera/NdkCameraMetadata.h @@ -44,8 +44,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - /** * ACameraMetadata is opaque type that provides access to read-only camera metadata like camera * characteristics (via {@link ACameraManager_getCameraCharacteristics}) or capture results (via @@ -191,7 +189,8 @@ typedef struct ACameraMetadata_const_entry { * of input tag value. */ camera_status_t ACameraMetadata_getConstEntry( - const ACameraMetadata* metadata, uint32_t tag, /*out*/ACameraMetadata_const_entry* entry); + const ACameraMetadata* metadata, + uint32_t tag, /*out*/ACameraMetadata_const_entry* entry) __INTRODUCED_IN(24); /** * List all the entry tags in input {@link ACameraMetadata}. @@ -208,7 +207,8 @@ camera_status_t ACameraMetadata_getConstEntry( *
  • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.
  • */ camera_status_t ACameraMetadata_getAllTags( - const ACameraMetadata* metadata, /*out*/int32_t* numEntries, /*out*/const uint32_t** tags); + const ACameraMetadata* metadata, + /*out*/int32_t* numEntries, /*out*/const uint32_t** tags) __INTRODUCED_IN(24); /** * Create a copy of input {@link ACameraMetadata}. @@ -220,16 +220,14 @@ camera_status_t ACameraMetadata_getAllTags( * * @return a valid ACameraMetadata pointer or NULL if the input metadata cannot be copied. */ -ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src); +ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src) __INTRODUCED_IN(24); /** * Free a {@link ACameraMetadata} structure. * * @param metadata the {@link ACameraMetadata} to be freed. */ -void ACameraMetadata_free(ACameraMetadata* metadata); - -#endif /* __ANDROID_API__ >= 24 */ +void ACameraMetadata_free(ACameraMetadata* metadata) __INTRODUCED_IN(24); __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 3010646afc..de18a97ec6 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -40,8 +40,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - typedef enum acamera_metadata_section { ACAMERA_COLOR_CORRECTION, ACAMERA_CONTROL, @@ -7779,9 +7777,6 @@ typedef enum acamera_metadata_enum_acamera_distortion_correction_mode { } acamera_metadata_enum_android_distortion_correction_mode_t; - -#endif /* __ANDROID_API__ >= 24 */ - __END_DECLS #endif /* _NDK_CAMERA_METADATA_TAGS_H */ diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h index 4961ce39f6..2fb5d12c7b 100644 --- a/camera/ndk/include/camera/NdkCaptureRequest.h +++ b/camera/ndk/include/camera/NdkCaptureRequest.h @@ -44,8 +44,6 @@ __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - // Container for output targets typedef struct ACameraOutputTargets ACameraOutputTargets; @@ -101,7 +99,8 @@ typedef struct ACaptureRequest ACaptureRequest; * * @see ACaptureRequest_addTarget */ -camera_status_t ACameraOutputTarget_create(ANativeWindow* window, ACameraOutputTarget** output); +camera_status_t ACameraOutputTarget_create(ANativeWindow* window, + ACameraOutputTarget** output) __INTRODUCED_IN(24); /** * Free a ACameraOutputTarget object. @@ -110,7 +109,7 @@ camera_status_t ACameraOutputTarget_create(ANativeWindow* window, ACameraOutputT * * @see ACameraOutputTarget_create */ -void ACameraOutputTarget_free(ACameraOutputTarget* output); +void ACameraOutputTarget_free(ACameraOutputTarget* output) __INTRODUCED_IN(24); /** * Add an {@link ACameraOutputTarget} object to {@link ACaptureRequest}. @@ -123,7 +122,7 @@ void ACameraOutputTarget_free(ACameraOutputTarget* output); *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or output is NULL.
  • */ camera_status_t ACaptureRequest_addTarget(ACaptureRequest* request, - const ACameraOutputTarget* output); + const ACameraOutputTarget* output) __INTRODUCED_IN(24); /** * Remove an {@link ACameraOutputTarget} object from {@link ACaptureRequest}. @@ -138,7 +137,7 @@ camera_status_t ACaptureRequest_addTarget(ACaptureRequest* request, *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or output is NULL.
  • */ camera_status_t ACaptureRequest_removeTarget(ACaptureRequest* request, - const ACameraOutputTarget* output); + const ACameraOutputTarget* output) __INTRODUCED_IN(24); /** * Get a metadata entry from input {@link ACaptureRequest}. @@ -158,7 +157,7 @@ camera_status_t ACaptureRequest_removeTarget(ACaptureRequest* request, * entry of input tag value. */ camera_status_t ACaptureRequest_getConstEntry( - const ACaptureRequest* request, uint32_t tag, ACameraMetadata_const_entry* entry); + const ACaptureRequest* request, uint32_t tag, ACameraMetadata_const_entry* entry) __INTRODUCED_IN(24); /* * List all the entry tags in input {@link ACaptureRequest}. @@ -179,7 +178,7 @@ camera_status_t ACaptureRequest_getConstEntry( *
  • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.
  • */ camera_status_t ACaptureRequest_getAllTags( - const ACaptureRequest* request, /*out*/int32_t* numTags, /*out*/const uint32_t** tags); + const ACaptureRequest* request, /*out*/int32_t* numTags, /*out*/const uint32_t** tags) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with unsigned 8 bits data type. @@ -198,7 +197,7 @@ camera_status_t ACaptureRequest_getAllTags( * the tag is not controllable by application. */ camera_status_t ACaptureRequest_setEntry_u8( - ACaptureRequest* request, uint32_t tag, uint32_t count, const uint8_t* data); + ACaptureRequest* request, uint32_t tag, uint32_t count, const uint8_t* data) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with signed 32 bits data type. @@ -217,7 +216,7 @@ camera_status_t ACaptureRequest_setEntry_u8( * the tag is not controllable by application. */ camera_status_t ACaptureRequest_setEntry_i32( - ACaptureRequest* request, uint32_t tag, uint32_t count, const int32_t* data); + ACaptureRequest* request, uint32_t tag, uint32_t count, const int32_t* data) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with float data type. @@ -236,7 +235,7 @@ camera_status_t ACaptureRequest_setEntry_i32( * the tag is not controllable by application. */ camera_status_t ACaptureRequest_setEntry_float( - ACaptureRequest* request, uint32_t tag, uint32_t count, const float* data); + ACaptureRequest* request, uint32_t tag, uint32_t count, const float* data) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with signed 64 bits data type. @@ -255,7 +254,7 @@ camera_status_t ACaptureRequest_setEntry_float( * the tag is not controllable by application. */ camera_status_t ACaptureRequest_setEntry_i64( - ACaptureRequest* request, uint32_t tag, uint32_t count, const int64_t* data); + ACaptureRequest* request, uint32_t tag, uint32_t count, const int64_t* data) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with double data type. @@ -274,7 +273,7 @@ camera_status_t ACaptureRequest_setEntry_i64( * the tag is not controllable by application. */ camera_status_t ACaptureRequest_setEntry_double( - ACaptureRequest* request, uint32_t tag, uint32_t count, const double* data); + ACaptureRequest* request, uint32_t tag, uint32_t count, const double* data) __INTRODUCED_IN(24); /** * Set/change a camera capture control entry with rational data type. @@ -294,18 +293,14 @@ camera_status_t ACaptureRequest_setEntry_double( */ camera_status_t ACaptureRequest_setEntry_rational( ACaptureRequest* request, uint32_t tag, uint32_t count, - const ACameraMetadata_rational* data); + const ACameraMetadata_rational* data) __INTRODUCED_IN(24); /** * Free a {@link ACaptureRequest} structure. * * @param request the {@link ACaptureRequest} to be freed. */ -void ACaptureRequest_free(ACaptureRequest* request); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 28 +void ACaptureRequest_free(ACaptureRequest* request) __INTRODUCED_IN(24); /** * Associate an arbitrary user context pointer to the {@link ACaptureRequest} @@ -325,7 +320,7 @@ void ACaptureRequest_free(ACaptureRequest* request); *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request is NULL.
  • */ camera_status_t ACaptureRequest_setUserContext( - ACaptureRequest* request, void* context); + ACaptureRequest* request, void* context) __INTRODUCED_IN(28); /** * Get the user context pointer of the {@link ACaptureRequest} @@ -341,7 +336,7 @@ camera_status_t ACaptureRequest_setUserContext( *
  • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request is NULL.
  • */ camera_status_t ACaptureRequest_getUserContext( - const ACaptureRequest* request, /*out*/void** context); + const ACaptureRequest* request, /*out*/void** context) __INTRODUCED_IN(28); /** * Create a copy of input {@link ACaptureRequest}. @@ -353,9 +348,7 @@ camera_status_t ACaptureRequest_getUserContext( * * @return a valid ACaptureRequest pointer or NULL if the input request cannot be copied. */ -ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src); - -#endif /* __ANDROID_API__ >= 28 */ +ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) __INTRODUCED_IN(28); __END_DECLS diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index d179aa01f0..a29e96df34 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -6,11 +6,11 @@ LIBCAMERA2NDK { ACameraCaptureSession_getDevice; ACameraCaptureSession_setRepeatingRequest; ACameraCaptureSession_stopRepeating; - ACameraCaptureSession_updateSharedOutput; + ACameraCaptureSession_updateSharedOutput; # introduced=28 ACameraDevice_close; ACameraDevice_createCaptureRequest; ACameraDevice_createCaptureSession; - ACameraDevice_createCaptureSessionWithSessionParameters; + ACameraDevice_createCaptureSessionWithSessionParameters; # introduced=28 ACameraDevice_getId; ACameraManager_create; ACameraManager_delete; @@ -27,11 +27,11 @@ LIBCAMERA2NDK { ACameraOutputTarget_create; ACameraOutputTarget_free; ACaptureRequest_addTarget; - ACaptureRequest_copy; + ACaptureRequest_copy; # introduced=28 ACaptureRequest_free; ACaptureRequest_getAllTags; ACaptureRequest_getConstEntry; - ACaptureRequest_getUserContext; + ACaptureRequest_getUserContext; # introduced=28 ACaptureRequest_removeTarget; ACaptureRequest_setEntry_double; ACaptureRequest_setEntry_float; @@ -39,15 +39,15 @@ LIBCAMERA2NDK { ACaptureRequest_setEntry_i64; ACaptureRequest_setEntry_rational; ACaptureRequest_setEntry_u8; - ACaptureRequest_setUserContext; + ACaptureRequest_setUserContext; # introduced=28 ACaptureSessionOutputContainer_add; ACaptureSessionOutputContainer_create; ACaptureSessionOutputContainer_free; ACaptureSessionOutputContainer_remove; ACaptureSessionOutput_create; - ACaptureSessionSharedOutput_create; - ACaptureSessionSharedOutput_add; - ACaptureSessionSharedOutput_remove; + ACaptureSessionSharedOutput_create; # introduced=28 + ACaptureSessionSharedOutput_add; # introduced=28 + ACaptureSessionSharedOutput_remove; # introduced=28 ACaptureSessionOutput_free; local: *; diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 5b29419535..1493b261e2 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -423,7 +423,7 @@ typedef struct AAudioStreamBuilderStruct AAudioStreamBuilder; * * @return pointer to a text representation of an AAudio result code. */ -AAUDIO_API const char * AAudio_convertResultToText(aaudio_result_t returnCode); +AAUDIO_API const char * AAudio_convertResultToText(aaudio_result_t returnCode) __INTRODUCED_IN(26); /** * The text is the ASCII symbol corresponding to the stream state, @@ -433,7 +433,8 @@ AAUDIO_API const char * AAudio_convertResultToText(aaudio_result_t returnCode); * * @return pointer to a text representation of an AAudio state. */ -AAUDIO_API const char * AAudio_convertStreamStateToText(aaudio_stream_state_t state); +AAUDIO_API const char * AAudio_convertStreamStateToText(aaudio_stream_state_t state) + __INTRODUCED_IN(26); // ============================================================ // StreamBuilder @@ -451,7 +452,8 @@ AAUDIO_API const char * AAudio_convertStreamStateToText(aaudio_stream_state_t st * * AAudioStreamBuilder_delete() must be called when you are done using the builder. */ -AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** builder); +AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** builder) + __INTRODUCED_IN(26); /** * Request an audio device identified device using an ID. @@ -464,7 +466,7 @@ AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** buil * @param deviceId device identifier or AAUDIO_UNSPECIFIED */ AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, - int32_t deviceId); + int32_t deviceId) __INTRODUCED_IN(26); /** * Request a sample rate in Hertz. @@ -481,7 +483,7 @@ AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, * @param sampleRate frames per second. Common rates include 44100 and 48000 Hz. */ AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder, - int32_t sampleRate); + int32_t sampleRate) __INTRODUCED_IN(26); /** * Request a number of channels for the stream. @@ -498,7 +500,7 @@ AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder, * @param channelCount Number of channels desired. */ AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder, - int32_t channelCount); + int32_t channelCount) __INTRODUCED_IN(26); /** * Identical to AAudioStreamBuilder_setChannelCount(). @@ -507,7 +509,7 @@ AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder * @param samplesPerFrame Number of samples in a frame. */ AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder, - int32_t samplesPerFrame); + int32_t samplesPerFrame) __INTRODUCED_IN(26); /** * Request a sample data format, for example AAUDIO_FORMAT_PCM_I16. @@ -524,7 +526,7 @@ AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* buil * @param format common formats are AAUDIO_FORMAT_PCM_FLOAT and AAUDIO_FORMAT_PCM_I16. */ AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, - aaudio_format_t format); + aaudio_format_t format) __INTRODUCED_IN(26); /** * Request a mode for sharing the device. @@ -538,7 +540,7 @@ AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, * @param sharingMode AAUDIO_SHARING_MODE_SHARED or AAUDIO_SHARING_MODE_EXCLUSIVE */ AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder, - aaudio_sharing_mode_t sharingMode); + aaudio_sharing_mode_t sharingMode) __INTRODUCED_IN(26); /** * Request the direction for a stream. @@ -549,7 +551,7 @@ AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder, * @param direction AAUDIO_DIRECTION_OUTPUT or AAUDIO_DIRECTION_INPUT */ AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, - aaudio_direction_t direction); + aaudio_direction_t direction) __INTRODUCED_IN(26); /** * Set the requested buffer capacity in frames. @@ -561,7 +563,7 @@ AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, * @param numFrames the desired buffer capacity in frames or AAUDIO_UNSPECIFIED */ AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder, - int32_t numFrames); + int32_t numFrames) __INTRODUCED_IN(26); /** * Set the requested performance mode. @@ -578,7 +580,7 @@ AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilde * @param mode the desired performance mode, eg. AAUDIO_PERFORMANCE_MODE_LOW_LATENCY */ AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder, - aaudio_performance_mode_t mode); + aaudio_performance_mode_t mode) __INTRODUCED_IN(26); /** * Set the intended use case for the stream. @@ -595,7 +597,7 @@ AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* buil * @param usage the desired usage, eg. AAUDIO_USAGE_GAME */ AAUDIO_API void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, - aaudio_usage_t usage); + aaudio_usage_t usage) __INTRODUCED_IN(28); /** * Set the type of audio data that the stream will carry. @@ -612,7 +614,7 @@ AAUDIO_API void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, * @param contentType the type of audio data, eg. AAUDIO_CONTENT_TYPE_SPEECH */ AAUDIO_API void AAudioStreamBuilder_setContentType(AAudioStreamBuilder* builder, - aaudio_content_type_t contentType); + aaudio_content_type_t contentType) __INTRODUCED_IN(28); /** * Set the input (capture) preset for the stream. @@ -632,7 +634,7 @@ AAUDIO_API void AAudioStreamBuilder_setContentType(AAudioStreamBuilder* builder, * @param inputPreset the desired configuration for recording */ AAUDIO_API void AAudioStreamBuilder_setInputPreset(AAudioStreamBuilder* builder, - aaudio_input_preset_t inputPreset); + aaudio_input_preset_t inputPreset) __INTRODUCED_IN(28); /** Set the requested session ID. * @@ -662,7 +664,7 @@ AAUDIO_API void AAudioStreamBuilder_setInputPreset(AAudioStreamBuilder* builder, * @param sessionId an allocated sessionID or AAUDIO_SESSION_ID_ALLOCATE */ AAUDIO_API void AAudioStreamBuilder_setSessionId(AAudioStreamBuilder* builder, - aaudio_session_id_t sessionId); + aaudio_session_id_t sessionId) __INTRODUCED_IN(28); /** * Return one of these values from the data callback function. @@ -760,8 +762,7 @@ typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)( * to the callback functions. */ AAUDIO_API void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder, - AAudioStream_dataCallback callback, - void *userData); + AAudioStream_dataCallback callback, void *userData) __INTRODUCED_IN(26); /** * Set the requested data callback buffer size in frames. @@ -787,7 +788,7 @@ AAUDIO_API void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder * @param numFrames the desired buffer size in frames or AAUDIO_UNSPECIFIED */ AAUDIO_API void AAudioStreamBuilder_setFramesPerDataCallback(AAudioStreamBuilder* builder, - int32_t numFrames); + int32_t numFrames) __INTRODUCED_IN(26); /** * Prototype for the callback function that is passed to @@ -840,8 +841,7 @@ typedef void (*AAudioStream_errorCallback)( * to the callback functions. */ AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder, - AAudioStream_errorCallback callback, - void *userData); + AAudioStream_errorCallback callback, void *userData) __INTRODUCED_IN(26); /** * Open a stream based on the options in the StreamBuilder. @@ -854,7 +854,7 @@ AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builde * @return AAUDIO_OK or a negative error. */ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder, - AAudioStream** stream); + AAudioStream** stream) __INTRODUCED_IN(26); /** * Delete the resources associated with the StreamBuilder. @@ -862,7 +862,8 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* * @param builder reference provided by AAudio_createStreamBuilder() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder); +AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder) + __INTRODUCED_IN(26); // ============================================================ // Stream Control @@ -874,7 +875,7 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* buil * @param stream reference provided by AAudioStreamBuilder_openStream() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream); +AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronously request to start playing the stream. For output streams, one should @@ -885,7 +886,7 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream); +AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronous request for the stream to pause. @@ -899,7 +900,7 @@ AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream); +AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronous request for the stream to flush. @@ -913,7 +914,7 @@ AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream); +AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronous request for the stream to stop. @@ -923,7 +924,7 @@ AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return AAUDIO_OK or a negative error. */ -AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream); +AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream) __INTRODUCED_IN(26); /** * Query the current state of the client, eg. AAUDIO_STREAM_STATE_PAUSING @@ -935,7 +936,7 @@ AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream); * * @param stream reference provided by AAudioStreamBuilder_openStream() */ -AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream); +AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream) __INTRODUCED_IN(26); /** * Wait until the current state no longer matches the input state. @@ -960,9 +961,8 @@ AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream); * @return AAUDIO_OK or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream, - aaudio_stream_state_t inputState, - aaudio_stream_state_t *nextState, - int64_t timeoutNanoseconds); + aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, + int64_t timeoutNanoseconds) __INTRODUCED_IN(26); // ============================================================ // Stream I/O @@ -989,9 +989,7 @@ AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream, * @return The number of frames actually read or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_read(AAudioStream* stream, - void *buffer, - int32_t numFrames, - int64_t timeoutNanoseconds); + void *buffer, int32_t numFrames, int64_t timeoutNanoseconds) __INTRODUCED_IN(26); /** * Write data to the stream. @@ -1014,9 +1012,7 @@ AAUDIO_API aaudio_result_t AAudioStream_read(AAudioStream* stream, * @return The number of frames actually written or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_write(AAudioStream* stream, - const void *buffer, - int32_t numFrames, - int64_t timeoutNanoseconds); + const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds) __INTRODUCED_IN(26); // ============================================================ // Stream - queries @@ -1039,7 +1035,7 @@ AAUDIO_API aaudio_result_t AAudioStream_write(AAudioStream* stream, * @return actual buffer size in frames or a negative error */ AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stream, - int32_t numFrames); + int32_t numFrames) __INTRODUCED_IN(26); /** * Query the maximum number of frames that can be filled without blocking. @@ -1047,7 +1043,7 @@ AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stre * @param stream reference provided by AAudioStreamBuilder_openStream() * @return buffer size in frames. */ -AAUDIO_API int32_t AAudioStream_getBufferSizeInFrames(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getBufferSizeInFrames(AAudioStream* stream) __INTRODUCED_IN(26); /** * Query the number of frames that the application should read or write at @@ -1062,7 +1058,7 @@ AAUDIO_API int32_t AAudioStream_getBufferSizeInFrames(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return burst size */ -AAUDIO_API int32_t AAudioStream_getFramesPerBurst(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getFramesPerBurst(AAudioStream* stream) __INTRODUCED_IN(26); /** * Query maximum buffer capacity in frames. @@ -1070,7 +1066,7 @@ AAUDIO_API int32_t AAudioStream_getFramesPerBurst(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return buffer capacity in frames */ -AAUDIO_API int32_t AAudioStream_getBufferCapacityInFrames(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getBufferCapacityInFrames(AAudioStream* stream) __INTRODUCED_IN(26); /** * Query the size of the buffer that will be passed to the dataProc callback @@ -1091,7 +1087,7 @@ AAUDIO_API int32_t AAudioStream_getBufferCapacityInFrames(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return callback buffer size in frames or AAUDIO_UNSPECIFIED */ -AAUDIO_API int32_t AAudioStream_getFramesPerDataCallback(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getFramesPerDataCallback(AAudioStream* stream) __INTRODUCED_IN(26); /** * An XRun is an Underrun or an Overrun. @@ -1108,13 +1104,13 @@ AAUDIO_API int32_t AAudioStream_getFramesPerDataCallback(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return the underrun or overrun count */ -AAUDIO_API int32_t AAudioStream_getXRunCount(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getXRunCount(AAudioStream* stream) __INTRODUCED_IN(26); /** * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual sample rate */ -AAUDIO_API int32_t AAudioStream_getSampleRate(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getSampleRate(AAudioStream* stream) __INTRODUCED_IN(26); /** * A stream has one or more channels of data. @@ -1123,7 +1119,7 @@ AAUDIO_API int32_t AAudioStream_getSampleRate(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual number of channels */ -AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream) __INTRODUCED_IN(26); /** * Identical to AAudioStream_getChannelCount(). @@ -1131,39 +1127,41 @@ AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual number of samples frame */ -AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream) __INTRODUCED_IN(26); /** * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual device ID */ -AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* stream); +AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* stream) __INTRODUCED_IN(26); /** * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual data format */ -AAUDIO_API aaudio_format_t AAudioStream_getFormat(AAudioStream* stream); +AAUDIO_API aaudio_format_t AAudioStream_getFormat(AAudioStream* stream) __INTRODUCED_IN(26); /** * Provide actual sharing mode. * @param stream reference provided by AAudioStreamBuilder_openStream() * @return actual sharing mode */ -AAUDIO_API aaudio_sharing_mode_t AAudioStream_getSharingMode(AAudioStream* stream); +AAUDIO_API aaudio_sharing_mode_t AAudioStream_getSharingMode(AAudioStream* stream) + __INTRODUCED_IN(26); /** * Get the performance mode used by the stream. * * @param stream reference provided by AAudioStreamBuilder_openStream() */ -AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream); +AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream) + __INTRODUCED_IN(26); /** * @param stream reference provided by AAudioStreamBuilder_openStream() * @return direction */ -AAUDIO_API aaudio_direction_t AAudioStream_getDirection(AAudioStream* stream); +AAUDIO_API aaudio_direction_t AAudioStream_getDirection(AAudioStream* stream) __INTRODUCED_IN(26); /** * Passes back the number of frames that have been written since the stream was created. @@ -1176,7 +1174,7 @@ AAUDIO_API aaudio_direction_t AAudioStream_getDirection(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return frames written */ -AAUDIO_API int64_t AAudioStream_getFramesWritten(AAudioStream* stream); +AAUDIO_API int64_t AAudioStream_getFramesWritten(AAudioStream* stream) __INTRODUCED_IN(26); /** * Passes back the number of frames that have been read since the stream was created. @@ -1189,7 +1187,7 @@ AAUDIO_API int64_t AAudioStream_getFramesWritten(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return frames read */ -AAUDIO_API int64_t AAudioStream_getFramesRead(AAudioStream* stream); +AAUDIO_API int64_t AAudioStream_getFramesRead(AAudioStream* stream) __INTRODUCED_IN(26); /** * Passes back the session ID associated with this stream. @@ -1213,7 +1211,7 @@ AAUDIO_API int64_t AAudioStream_getFramesRead(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return session ID or AAUDIO_SESSION_ID_NONE */ -AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream); +AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream) __INTRODUCED_IN(28); /** * Passes back the time at which a particular frame was presented. @@ -1238,9 +1236,7 @@ AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream); * @return AAUDIO_OK or a negative error */ AAUDIO_API aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream, - clockid_t clockid, - int64_t *framePosition, - int64_t *timeNanoseconds); + clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds) __INTRODUCED_IN(26); /** * Return the use case for the stream. @@ -1250,7 +1246,7 @@ AAUDIO_API aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream, * @param stream reference provided by AAudioStreamBuilder_openStream() * @return frames read */ -AAUDIO_API aaudio_usage_t AAudioStream_getUsage(AAudioStream* stream); +AAUDIO_API aaudio_usage_t AAudioStream_getUsage(AAudioStream* stream) __INTRODUCED_IN(28); /** * Return the content type for the stream. @@ -1260,7 +1256,8 @@ AAUDIO_API aaudio_usage_t AAudioStream_getUsage(AAudioStream* stream); * @param stream reference provided by AAudioStreamBuilder_openStream() * @return content type, for example AAUDIO_CONTENT_TYPE_MUSIC */ -AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* stream); +AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* stream) + __INTRODUCED_IN(28); /** * Return the input preset for the stream. @@ -1270,7 +1267,8 @@ AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* strea * @param stream reference provided by AAudioStreamBuilder_openStream() * @return input preset, for example AAUDIO_INPUT_PRESET_CAMCORDER */ -AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* stream); +AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* stream) + __INTRODUCED_IN(28); #ifdef __cplusplus } diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h index 19df760ab3..38e12e32bf 100644 --- a/media/ndk/include/media/NdkImage.h +++ b/media/ndk/include/media/NdkImage.h @@ -516,8 +516,6 @@ typedef struct AImageCropRect { int32_t bottom; } AImageCropRect; -#if __ANDROID_API__ >= 24 - /** * Return the image back the the system and delete the AImage object from memory. * @@ -529,7 +527,7 @@ typedef struct AImageCropRect { * * @param image The {@link AImage} to be deleted. */ -void AImage_delete(AImage* image); +void AImage_delete(AImage* image) __INTRODUCED_IN(24); /** * Query the width of the input {@link AImage}. @@ -543,7 +541,7 @@ void AImage_delete(AImage* image); *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width); +media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width) __INTRODUCED_IN(24); /** * Query the height of the input {@link AImage}. @@ -557,7 +555,7 @@ media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width); *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height); +media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height) __INTRODUCED_IN(24); /** * Query the format of the input {@link AImage}. @@ -573,7 +571,7 @@ media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height); *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format); +media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format) __INTRODUCED_IN(24); /** * Query the cropped rectangle of the input {@link AImage}. @@ -590,7 +588,7 @@ media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format); *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* rect); +media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* rect) __INTRODUCED_IN(24); /** * Query the timestamp of the input {@link AImage}. @@ -614,7 +612,7 @@ media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* re *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestampNs); +media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestampNs) __INTRODUCED_IN(24); /** * Query the number of planes of the input {@link AImage}. @@ -632,7 +630,7 @@ media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestam *
  • {@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this * image has been deleted.
  • */ -media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* numPlanes); +media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* numPlanes) __INTRODUCED_IN(24); /** * Query the pixel stride of the input {@link AImage}. @@ -660,7 +658,7 @@ media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* num * for CPU access. */ media_status_t AImage_getPlanePixelStride( - const AImage* image, int planeIdx, /*out*/int32_t* pixelStride); + const AImage* image, int planeIdx, /*out*/int32_t* pixelStride) __INTRODUCED_IN(24); /** * Query the row stride of the input {@link AImage}. @@ -687,7 +685,7 @@ media_status_t AImage_getPlanePixelStride( * for CPU access. */ media_status_t AImage_getPlaneRowStride( - const AImage* image, int planeIdx, /*out*/int32_t* rowStride); + const AImage* image, int planeIdx, /*out*/int32_t* rowStride) __INTRODUCED_IN(24); /** * Get the data pointer of the input image for direct application access. @@ -712,11 +710,7 @@ media_status_t AImage_getPlaneRowStride( */ media_status_t AImage_getPlaneData( const AImage* image, int planeIdx, - /*out*/uint8_t** data, /*out*/int* dataLength); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 26 + /*out*/uint8_t** data, /*out*/int* dataLength) __INTRODUCED_IN(24); /** * Return the image back the the system and delete the AImage object from memory asynchronously. @@ -732,7 +726,7 @@ media_status_t AImage_getPlaneData( * * @see sync.h */ -void AImage_deleteAsync(AImage* image, int releaseFenceFd); +void AImage_deleteAsync(AImage* image, int releaseFenceFd) __INTRODUCED_IN(26); /** * Get the hardware buffer handle of the input image intended for GPU and/or hardware access. @@ -760,9 +754,7 @@ void AImage_deleteAsync(AImage* image, int releaseFenceFd); * * @see AImageReader_ImageCallback */ -media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer); - -#endif /* __ANDROID_API__ >= 26 */ +media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer) __INTRODUCED_IN(26); __END_DECLS diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h index 571410bf4d..eb1a44aaa2 100644 --- a/media/ndk/include/media/NdkImageReader.h +++ b/media/ndk/include/media/NdkImageReader.h @@ -50,8 +50,6 @@ __BEGIN_DECLS */ typedef struct AImageReader AImageReader; -#if __ANDROID_API__ >= 24 - /** * Create a new reader for images of the desired size and format. * @@ -88,7 +86,7 @@ typedef struct AImageReader AImageReader; */ media_status_t AImageReader_new( int32_t width, int32_t height, int32_t format, int32_t maxImages, - /*out*/AImageReader** reader); + /*out*/AImageReader** reader) __INTRODUCED_IN(24); /** * Delete an {@link AImageReader} and return all images generated by this reader to system. @@ -100,7 +98,7 @@ media_status_t AImageReader_new( * * @param reader The image reader to be deleted. */ -void AImageReader_delete(AImageReader* reader); +void AImageReader_delete(AImageReader* reader) __INTRODUCED_IN(24); /** * Get a {@link ANativeWindow} that can be used to produce {@link AImage} for this image reader. @@ -114,7 +112,7 @@ void AImageReader_delete(AImageReader* reader); *
  • {@link AMEDIA_OK} if the method call succeeds.
  • *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader or window is NULL.
  • */ -media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window); +media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window) __INTRODUCED_IN(24); /** * Query the default width of the {@link AImage} generated by this reader, in pixels. @@ -130,7 +128,7 @@ media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow *
  • {@link AMEDIA_OK} if the method call succeeds.
  • *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader or width is NULL.
  • */ -media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width); +media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width) __INTRODUCED_IN(24); /** * Query the default height of the {@link AImage} generated by this reader, in pixels. @@ -146,7 +144,7 @@ media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* *
  • {@link AMEDIA_OK} if the method call succeeds.
  • *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader or height is NULL.
  • */ -media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height); +media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height) __INTRODUCED_IN(24); /** * Query the format of the {@link AImage} generated by this reader. @@ -159,7 +157,7 @@ media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t *
  • {@link AMEDIA_OK} if the method call succeeds.
  • *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader or format is NULL.
  • */ -media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format); +media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format) __INTRODUCED_IN(24); /** * Query the maximum number of concurrently acquired {@link AImage}s of this reader. @@ -172,7 +170,7 @@ media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t *
  • {@link AMEDIA_OK} if the method call succeeds.
  • *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader or maxImages is NULL.
  • */ -media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages); +media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages) __INTRODUCED_IN(24); /** * Acquire the next {@link AImage} from the image reader's queue. @@ -208,7 +206,7 @@ media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int3 * * @see AImageReader_acquireLatestImage */ -media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image); +media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) __INTRODUCED_IN(24); /** @@ -252,7 +250,7 @@ media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage * * @see AImageReader_acquireNextImage */ -media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image); +media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) __INTRODUCED_IN(24); /** @@ -296,11 +294,7 @@ typedef struct AImageReader_ImageListener { *
  • {@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL.
  • */ media_status_t AImageReader_setImageListener( - AImageReader* reader, AImageReader_ImageListener* listener); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 26 + AImageReader* reader, AImageReader_ImageListener* listener) __INTRODUCED_IN(24); /** * AImageReader constructor similar to {@link AImageReader_new} that takes an additional parameter @@ -365,7 +359,7 @@ media_status_t AImageReader_setImageListener( */ media_status_t AImageReader_newWithUsage( int32_t width, int32_t height, int32_t format, uint64_t usage, int32_t maxImages, - /*out*/ AImageReader** reader); + /*out*/ AImageReader** reader) __INTRODUCED_IN(26); /** * Acquire the next {@link AImage} from the image reader's queue asynchronously. @@ -384,7 +378,7 @@ media_status_t AImageReader_newWithUsage( * @see sync_get_fence_info */ media_status_t AImageReader_acquireNextImageAsync( - AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd); + AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) __INTRODUCED_IN(26); /** * Acquire the latest {@link AImage} from the image reader's queue asynchronously, dropping older @@ -404,7 +398,7 @@ media_status_t AImageReader_acquireNextImageAsync( * @see sync_get_fence_info */ media_status_t AImageReader_acquireLatestImageAsync( - AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd); + AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) __INTRODUCED_IN(26); /** * Signature of the callback which is called when {@link AImageReader} is about to remove a buffer. * @@ -459,9 +453,7 @@ typedef struct AImageReader_BufferRemovedListener { * @see AImage_getHardwareBuffer */ media_status_t AImageReader_setBufferRemovedListener( - AImageReader* reader, AImageReader_BufferRemovedListener* listener); - -#endif /* __ANDROID_API__ >= 26 */ + AImageReader* reader, AImageReader_BufferRemovedListener* listener) __INTRODUCED_IN(26); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h index c49582dfc1..b329b39d75 100644 --- a/media/ndk/include/media/NdkMediaCodec.h +++ b/media/ndk/include/media/NdkMediaCodec.h @@ -121,30 +121,28 @@ struct AMediaCodecOnAsyncNotifyCallback { AMediaCodecOnAsyncError onAsyncError; }; -#if __ANDROID_API__ >= 21 - /** * Create codec by name. Use this if you know the exact codec you want to use. * When configuring, you will need to specify whether to use the codec as an * encoder or decoder. */ -AMediaCodec* AMediaCodec_createCodecByName(const char *name); +AMediaCodec* AMediaCodec_createCodecByName(const char *name) __INTRODUCED_IN(21); /** * Create codec by mime type. Most applications will use this, specifying a * mime type obtained from media extractor. */ -AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type); +AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) __INTRODUCED_IN(21); /** * Create encoder by name. */ -AMediaCodec* AMediaCodec_createEncoderByType(const char *mime_type); +AMediaCodec* AMediaCodec_createEncoderByType(const char *mime_type) __INTRODUCED_IN(21); /** * delete the codec and free its resources */ -media_status_t AMediaCodec_delete(AMediaCodec*); +media_status_t AMediaCodec_delete(AMediaCodec*) __INTRODUCED_IN(21); /** * Configure the codec. For decoding you would typically get the format from an extractor. @@ -154,43 +152,43 @@ media_status_t AMediaCodec_configure( const AMediaFormat* format, ANativeWindow* surface, AMediaCrypto *crypto, - uint32_t flags); + uint32_t flags) __INTRODUCED_IN(21); /** * Start the codec. A codec must be configured before it can be started, and must be started * before buffers can be sent to it. */ -media_status_t AMediaCodec_start(AMediaCodec*); +media_status_t AMediaCodec_start(AMediaCodec*) __INTRODUCED_IN(21); /** * Stop the codec. */ -media_status_t AMediaCodec_stop(AMediaCodec*); +media_status_t AMediaCodec_stop(AMediaCodec*) __INTRODUCED_IN(21); /* * Flush the codec's input and output. All indices previously returned from calls to * AMediaCodec_dequeueInputBuffer and AMediaCodec_dequeueOutputBuffer become invalid. */ -media_status_t AMediaCodec_flush(AMediaCodec*); +media_status_t AMediaCodec_flush(AMediaCodec*) __INTRODUCED_IN(21); /** * Get an input buffer. The specified buffer index must have been previously obtained from * dequeueInputBuffer, and not yet queued. */ -uint8_t* AMediaCodec_getInputBuffer(AMediaCodec*, size_t idx, size_t *out_size); +uint8_t* AMediaCodec_getInputBuffer(AMediaCodec*, size_t idx, size_t *out_size) __INTRODUCED_IN(21); /** * Get an output buffer. The specified buffer index must have been previously obtained from * dequeueOutputBuffer, and not yet queued. */ -uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec*, size_t idx, size_t *out_size); +uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec*, size_t idx, size_t *out_size) __INTRODUCED_IN(21); /** * Get the index of the next available input buffer. An app will typically use this with * getInputBuffer() to get a pointer to the buffer, then copy the data to be encoded or decoded * into the buffer before passing it to the codec. */ -ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs); +ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs) __INTRODUCED_IN(21); /* * __USE_FILE_OFFSET64 changes the type of off_t in LP32, which changes the ABI @@ -221,7 +219,7 @@ static_assert(sizeof(_off_t_compat) == sizeof(long), */ media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*, size_t idx, _off_t_compat offset, size_t size, - uint64_t time, uint32_t flags); + uint64_t time, uint32_t flags) __INTRODUCED_IN(21); /** * Send the specified buffer to the codec for processing. @@ -229,7 +227,7 @@ media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*, size_t idx, media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*, size_t idx, _off_t_compat offset, AMediaCodecCryptoInfo*, - uint64_t time, uint32_t flags); + uint64_t time, uint32_t flags) __INTRODUCED_IN(21); #undef _off_t_compat @@ -237,21 +235,21 @@ media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*, size_t idx, * Get the index of the next available buffer of processed data. */ ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info, - int64_t timeoutUs); -AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*); + int64_t timeoutUs) __INTRODUCED_IN(21); +AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*) __INTRODUCED_IN(21); /** * Get format of the buffer. The specified buffer index must have been previously obtained from * dequeueOutputBuffer. */ -AMediaFormat* AMediaCodec_getBufferFormat(AMediaCodec*, size_t index); +AMediaFormat* AMediaCodec_getBufferFormat(AMediaCodec*, size_t index) __INTRODUCED_IN(21); /** * If you are done with a buffer, use this call to return the buffer to * the codec. If you previously specified a surface when configuring this * video decoder you can optionally render the buffer. */ -media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render); +media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render) __INTRODUCED_IN(21); /** * Dynamically sets the output surface of a codec. @@ -263,7 +261,7 @@ media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool re * * For more details, see the Java documentation for MediaCodec.setOutputSurface. */ -media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface); +media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface) __INTRODUCED_IN(21); /** * If you are done with a buffer, use this call to update its surface timestamp @@ -274,9 +272,7 @@ media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface * For more details, see the Java documentation for MediaCodec.releaseOutputBuffer. */ media_status_t AMediaCodec_releaseOutputBufferAtTime( - AMediaCodec *mData, size_t idx, int64_t timestampNs); - -#if __ANDROID_API__ >= 26 + AMediaCodec *mData, size_t idx, int64_t timestampNs) __INTRODUCED_IN(21); /** * Creates a Surface that can be used as the input to encoder, in place of input buffers @@ -290,7 +286,7 @@ media_status_t AMediaCodec_releaseOutputBufferAtTime( * For more details, see the Java documentation for MediaCodec.createInputSurface. */ media_status_t AMediaCodec_createInputSurface( - AMediaCodec *mData, ANativeWindow **surface); + AMediaCodec *mData, ANativeWindow **surface) __INTRODUCED_IN(26); /** * Creates a persistent Surface that can be used as the input to encoder @@ -306,7 +302,7 @@ media_status_t AMediaCodec_createInputSurface( * For more details, see the Java documentation for MediaCodec.createPersistentInputSurface. */ media_status_t AMediaCodec_createPersistentInputSurface( - ANativeWindow **surface); + ANativeWindow **surface) __INTRODUCED_IN(26); /** * Set a persistent-surface that can be used as the input to encoder, in place of input buffers @@ -319,7 +315,7 @@ media_status_t AMediaCodec_createPersistentInputSurface( * For more details, see the Java documentation for MediaCodec.setInputSurface. */ media_status_t AMediaCodec_setInputSurface( - AMediaCodec *mData, ANativeWindow *surface); + AMediaCodec *mData, ANativeWindow *surface) __INTRODUCED_IN(26); /** * Signal additional parameters to the codec instance. @@ -330,7 +326,7 @@ media_status_t AMediaCodec_setInputSurface( * NOTE: Some of these parameter changes may silently fail to apply. */ media_status_t AMediaCodec_setParameters( - AMediaCodec *mData, const AMediaFormat* params); + AMediaCodec *mData, const AMediaFormat* params) __INTRODUCED_IN(26); /** * Signals end-of-stream on input. Equivalent to submitting an empty buffer with @@ -346,23 +342,19 @@ media_status_t AMediaCodec_setParameters( * * For more details, see the Java documentation for MediaCodec.signalEndOfInputStream. */ -media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData); - -#endif /* __ANDROID_API__ >= 26 */ - -#if __ANDROID_API__ >= 28 +media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) __INTRODUCED_IN(26); /** * Get the component name. If the codec was created by createDecoderByType * or createEncoderByType, what component is chosen is not known beforehand. * Caller shall call AMediaCodec_releaseName to free the returned pointer. */ -media_status_t AMediaCodec_getName(AMediaCodec*, char** out_name); +media_status_t AMediaCodec_getName(AMediaCodec*, char** out_name) __INTRODUCED_IN(28); /** * Free the memory pointed by name which is returned by AMediaCodec_getName. */ -void AMediaCodec_releaseName(AMediaCodec*, char* name); +void AMediaCodec_releaseName(AMediaCodec*, char* name) __INTRODUCED_IN(28); /** * Set an asynchronous callback for actionable AMediaCodec events. @@ -386,34 +378,32 @@ void AMediaCodec_releaseName(AMediaCodec*, char* name); media_status_t AMediaCodec_setAsyncNotifyCallback( AMediaCodec*, AMediaCodecOnAsyncNotifyCallback callback, - void *userdata); + void *userdata) __INTRODUCED_IN(28); /** * Release the crypto if applicable. */ -media_status_t AMediaCodec_releaseCrypto(AMediaCodec*); +media_status_t AMediaCodec_releaseCrypto(AMediaCodec*) __INTRODUCED_IN(28); /** * Call this after AMediaCodec_configure() returns successfully to get the input * format accepted by the codec. Do this to determine what optional configuration * parameters were supported by the codec. */ -AMediaFormat* AMediaCodec_getInputFormat(AMediaCodec*); +AMediaFormat* AMediaCodec_getInputFormat(AMediaCodec*) __INTRODUCED_IN(28); /** * Returns true if the codec cannot proceed further, but can be recovered by stopping, * configuring, and starting again. */ -bool AMediaCodecActionCode_isRecoverable(int32_t actionCode); +bool AMediaCodecActionCode_isRecoverable(int32_t actionCode) __INTRODUCED_IN(28); /** * Returns true if the codec error is a transient issue, perhaps due to * resource constraints, and that the method (or encoding/decoding) may be * retried at a later time. */ -bool AMediaCodecActionCode_isTransient(int32_t actionCode); - -#endif /* __ANDROID_API__ >= 28 */ +bool AMediaCodecActionCode_isTransient(int32_t actionCode) __INTRODUCED_IN(28); typedef enum { AMEDIACODECRYPTOINFO_MODE_CLEAR = 0, @@ -447,53 +437,51 @@ AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new( uint8_t iv[16], cryptoinfo_mode_t mode, size_t *clearbytes, - size_t *encryptedbytes); + size_t *encryptedbytes) __INTRODUCED_IN(21); /** * delete an AMediaCodecCryptoInfo created previously with AMediaCodecCryptoInfo_new, or * obtained from AMediaExtractor */ -media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*); +media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*) __INTRODUCED_IN(21); /** * Set the crypto pattern on an AMediaCryptoInfo object */ void AMediaCodecCryptoInfo_setPattern( AMediaCodecCryptoInfo *info, - cryptoinfo_pattern_t *pattern); + cryptoinfo_pattern_t *pattern) __INTRODUCED_IN(21); /** * The number of subsamples that make up the buffer's contents. */ -size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*); +size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*) __INTRODUCED_IN(21); /** * A 16-byte opaque key */ -media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst); +media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst) __INTRODUCED_IN(21); /** * A 16-byte initialization vector */ -media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst); +media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst) __INTRODUCED_IN(21); /** * The type of encryption that has been applied, * one of AMEDIACODECRYPTOINFO_MODE_CLEAR or AMEDIACODECRYPTOINFO_MODE_AES_CTR. */ -cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*); +cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*) __INTRODUCED_IN(21); /** * The number of leading unencrypted bytes in each subsample. */ -media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst); +media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst) __INTRODUCED_IN(21); /** * The number of trailing encrypted bytes in each subsample. */ -media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst); - -#endif /* __ANDROID_API__ >= 21 */ +media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst) __INTRODUCED_IN(21); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaCrypto.h b/media/ndk/include/media/NdkMediaCrypto.h index 6f2926eb36..b673adc6a4 100644 --- a/media/ndk/include/media/NdkMediaCrypto.h +++ b/media/ndk/include/media/NdkMediaCrypto.h @@ -47,17 +47,13 @@ typedef struct AMediaCrypto AMediaCrypto; typedef uint8_t AMediaUUID[16]; -#if __ANDROID_API__ >= 21 +bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid) __INTRODUCED_IN(21); -bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid); +bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime) __INTRODUCED_IN(21); -bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime); +AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *initData, size_t initDataSize) __INTRODUCED_IN(21); -AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *initData, size_t initDataSize); - -void AMediaCrypto_delete(AMediaCrypto* crypto); - -#endif /* __ANDROID_API__ >= 21 */ +void AMediaCrypto_delete(AMediaCrypto* crypto) __INTRODUCED_IN(21); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h index 9e2e351aec..3a4373c8a1 100644 --- a/media/ndk/include/media/NdkMediaDataSource.h +++ b/media/ndk/include/media/NdkMediaDataSource.h @@ -38,8 +38,6 @@ __BEGIN_DECLS struct AMediaDataSource; typedef struct AMediaDataSource AMediaDataSource; -#if __ANDROID_API__ >= 28 - /* * AMediaDataSource's callbacks will be invoked on an implementation-defined thread * or thread pool. No guarantees are provided about which thread(s) will be used for @@ -84,19 +82,19 @@ typedef void (*AMediaDataSourceClose)(void *userdata); * Create new media data source. Returns NULL if memory allocation * for the new data source object fails. */ -AMediaDataSource* AMediaDataSource_new(); +AMediaDataSource* AMediaDataSource_new() __INTRODUCED_IN(28); /** * Delete a previously created media data source. */ -void AMediaDataSource_delete(AMediaDataSource*); +void AMediaDataSource_delete(AMediaDataSource*) __INTRODUCED_IN(28); /** * Set an user provided opaque handle. This opaque handle is passed as * the first argument to the data source callbacks. */ void AMediaDataSource_setUserdata( - AMediaDataSource*, void *userdata); + AMediaDataSource*, void *userdata) __INTRODUCED_IN(28); /** * Set a custom callback for supplying random access media data to the @@ -111,7 +109,7 @@ void AMediaDataSource_setUserdata( */ void AMediaDataSource_setReadAt( AMediaDataSource*, - AMediaDataSourceReadAt); + AMediaDataSourceReadAt) __INTRODUCED_IN(28); /** * Set a custom callback for supplying the size of the data source to the @@ -122,7 +120,7 @@ void AMediaDataSource_setReadAt( */ void AMediaDataSource_setGetSize( AMediaDataSource*, - AMediaDataSourceGetSize); + AMediaDataSourceGetSize) __INTRODUCED_IN(28); /** * Set a custom callback to receive signal from the NDK media framework @@ -133,9 +131,7 @@ void AMediaDataSource_setGetSize( */ void AMediaDataSource_setClose( AMediaDataSource*, - AMediaDataSourceClose); - -#endif /*__ANDROID_API__ >= 28 */ + AMediaDataSourceClose) __INTRODUCED_IN(28); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h index d45dc209fc..24c0d6d86f 100644 --- a/media/ndk/include/media/NdkMediaDrm.h +++ b/media/ndk/include/media/NdkMediaDrm.h @@ -87,8 +87,6 @@ typedef enum AMediaDrmEventType { typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId, AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize); -#if __ANDROID_API__ >= 21 - /** * Query if the given scheme identified by its UUID is supported on this device, and * whether the drm plugin is able to handle the media container format specified by mimeType. @@ -97,25 +95,27 @@ typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *se * mimeType is the MIME type of the media container, e.g. "video/mp4". If mimeType * is not known or required, it can be provided as NULL. */ -bool AMediaDrm_isCryptoSchemeSupported(const uint8_t *uuid, const char *mimeType); +bool AMediaDrm_isCryptoSchemeSupported(const uint8_t *uuid, + const char *mimeType) __INTRODUCED_IN(21); /** * Create a MediaDrm instance from a UUID * uuid identifies the universal unique ID of the crypto scheme. uuid must be 16 bytes. */ -AMediaDrm* AMediaDrm_createByUUID(const uint8_t *uuid); +AMediaDrm* AMediaDrm_createByUUID(const uint8_t *uuid) __INTRODUCED_IN(21); /** * Release a MediaDrm object */ -void AMediaDrm_release(AMediaDrm *); +void AMediaDrm_release(AMediaDrm *) __INTRODUCED_IN(21); /** * Register a callback to be invoked when an event occurs * * listener is the callback that will be invoked on event */ -media_status_t AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener); +media_status_t AMediaDrm_setOnEventListener(AMediaDrm *, + AMediaDrmEventListener listener) __INTRODUCED_IN(21); /** * Open a new session with the MediaDrm object. A session ID is returned. @@ -123,13 +123,15 @@ media_status_t AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed * returns MEDIADRM_RESOURCE_BUSY_ERROR if required resources are in use */ -media_status_t AMediaDrm_openSession(AMediaDrm *, AMediaDrmSessionId *sessionId); +media_status_t AMediaDrm_openSession(AMediaDrm *, + AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21); /** * Close a session on the MediaDrm object that was previously opened * with AMediaDrm_openSession. */ -media_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId *sessionId); +media_status_t AMediaDrm_closeSession(AMediaDrm *, + const AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21); typedef enum AMediaDrmKeyType { /** @@ -208,7 +210,7 @@ typedef struct AMediaDrmKeyValuePair { media_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope *scope, const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType, const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters, - const uint8_t **keyRequest, size_t *keyRequestSize); + const uint8_t **keyRequest, size_t *keyRequestSize) __INTRODUCED_IN(21); /** * A key response is received from the license server by the app, then it is @@ -228,7 +230,8 @@ media_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope *scope, */ media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope *scope, - const uint8_t *response, size_t responseSize, AMediaDrmKeySetId *keySetId); + const uint8_t *response, size_t responseSize, + AMediaDrmKeySetId *keySetId) __INTRODUCED_IN(21); /** * Restore persisted offline keys into a new session. keySetId identifies the @@ -238,14 +241,15 @@ media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope *s * keySetId identifies the saved key set to restore */ media_status_t AMediaDrm_restoreKeys(AMediaDrm *, const AMediaDrmSessionId *sessionId, - const AMediaDrmKeySetId *keySetId); + const AMediaDrmKeySetId *keySetId) __INTRODUCED_IN(21); /** * Remove the current keys from a session. * * keySetId identifies keys to remove */ -media_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId *keySetId); +media_status_t AMediaDrm_removeKeys(AMediaDrm *, + const AMediaDrmSessionId *keySetId) __INTRODUCED_IN(21); /** * Request an informative description of the key status for the session. The status is @@ -261,7 +265,7 @@ media_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId *keySe * and numPairs will be set to the number of pairs available. */ media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId *sessionId, - AMediaDrmKeyValue *keyValuePairs, size_t *numPairs); + AMediaDrmKeyValue *keyValuePairs, size_t *numPairs) __INTRODUCED_IN(21); /** @@ -280,7 +284,7 @@ media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId *s * the next call to getProvisionRequest. */ media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t **provisionRequest, - size_t *provisionRequestSize, const char **serverUrl); + size_t *provisionRequestSize, const char **serverUrl) __INTRODUCED_IN(21); /** @@ -295,7 +299,7 @@ media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t **provis * server rejected the request */ media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *, - const uint8_t *response, size_t responseSize); + const uint8_t *response, size_t responseSize) __INTRODUCED_IN(21); /** @@ -320,7 +324,7 @@ media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *, * number required. */ media_status_t AMediaDrm_getSecureStops(AMediaDrm *, - AMediaDrmSecureStop *secureStops, size_t *numSecureStops); + AMediaDrmSecureStop *secureStops, size_t *numSecureStops) __INTRODUCED_IN(21); /** * Process the SecureStop server response message ssRelease. After authenticating @@ -329,7 +333,7 @@ media_status_t AMediaDrm_getSecureStops(AMediaDrm *, * ssRelease is the server response indicating which secure stops to release */ media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *, - const AMediaDrmSecureStop *ssRelease); + const AMediaDrmSecureStop *ssRelease) __INTRODUCED_IN(21); /** * String property name: identifies the maker of the DRM engine plugin @@ -362,7 +366,7 @@ media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *, * will remain valid until the next call to AMediaDrm_getPropertyString. */ media_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName, - const char **propertyValue); + const char **propertyValue) __INTRODUCED_IN(21); /** * Byte array property name: the device unique identifier is established during @@ -377,19 +381,19 @@ media_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName * will remain valid until the next call to AMediaDrm_getPropertyByteArray. */ media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName, - AMediaDrmByteArray *propertyValue); + AMediaDrmByteArray *propertyValue) __INTRODUCED_IN(21); /** * Set a DRM engine plugin String property value. */ media_status_t AMediaDrm_setPropertyString(AMediaDrm *, const char *propertyName, - const char *value); + const char *value) __INTRODUCED_IN(21); /** * Set a DRM engine plugin byte array property value. */ media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyName, - const uint8_t *value, size_t valueSize); + const uint8_t *value, size_t valueSize) __INTRODUCED_IN(21); /** * In addition to supporting decryption of DASH Common Encrypted Media, the @@ -418,7 +422,7 @@ media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyN */ media_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId *sessionId, const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv, - const uint8_t *input, uint8_t *output, size_t dataSize); + const uint8_t *input, uint8_t *output, size_t dataSize) __INTRODUCED_IN(21); /* * Decrypt the data referenced by input of length dataSize using algorithm specified @@ -429,7 +433,7 @@ media_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId *sessionI */ media_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId *sessionId, const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv, - const uint8_t *input, uint8_t *output, size_t dataSize); + const uint8_t *input, uint8_t *output, size_t dataSize) __INTRODUCED_IN(21); /* * Generate a signature using the specified macAlgorithm over the message data @@ -442,7 +446,7 @@ media_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId *sessionI */ media_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId *sessionId, const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize, - uint8_t *signature, size_t *signatureSize); + uint8_t *signature, size_t *signatureSize) __INTRODUCED_IN(21); /* * Perform a signature verification using the specified macAlgorithm over the message @@ -453,9 +457,7 @@ media_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId *sessionId, */ media_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId *sessionId, const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize, - const uint8_t *signature, size_t signatureSize); - -#endif /* __ANDROID_API__ >= 21 */ + const uint8_t *signature, size_t signatureSize) __INTRODUCED_IN(21); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h index 3452cc9dd9..9f6089182c 100644 --- a/media/ndk/include/media/NdkMediaExtractor.h +++ b/media/ndk/include/media/NdkMediaExtractor.h @@ -49,48 +49,44 @@ __BEGIN_DECLS struct AMediaExtractor; typedef struct AMediaExtractor AMediaExtractor; -#if __ANDROID_API__ >= 21 - /** * Create new media extractor */ -AMediaExtractor* AMediaExtractor_new(); +AMediaExtractor* AMediaExtractor_new() __INTRODUCED_IN(21); /** * Delete a previously created media extractor */ -media_status_t AMediaExtractor_delete(AMediaExtractor*); +media_status_t AMediaExtractor_delete(AMediaExtractor*) __INTRODUCED_IN(21); /** * Set the file descriptor from which the extractor will read. */ media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, - off64_t length); + off64_t length) __INTRODUCED_IN(21); /** * Set the URI from which the extractor will read. */ -media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); +media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, + const char *location) __INTRODUCED_IN(21); // TODO support headers -#if __ANDROID_API__ >= 28 - /** * Set the custom data source implementation from which the extractor will read. */ -media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*, AMediaDataSource *src); - -#endif /* __ANDROID_API__ >= 28 */ +media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*, + AMediaDataSource *src) __INTRODUCED_IN(28); /** * Return the number of tracks in the previously specified media file */ -size_t AMediaExtractor_getTrackCount(AMediaExtractor*); +size_t AMediaExtractor_getTrackCount(AMediaExtractor*) __INTRODUCED_IN(21); /** * Return the format of the specified track. The caller must free the returned format */ -AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor*, size_t idx); +AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor*, size_t idx) __INTRODUCED_IN(21); /** * Select the specified track. Subsequent calls to readSampleData, getSampleTrackIndex and @@ -98,41 +94,42 @@ AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor*, size_t idx); * Selecting the same track multiple times has no effect, the track is * only selected once. */ -media_status_t AMediaExtractor_selectTrack(AMediaExtractor*, size_t idx); +media_status_t AMediaExtractor_selectTrack(AMediaExtractor*, size_t idx) __INTRODUCED_IN(21); /** * Unselect the specified track. Subsequent calls to readSampleData, getSampleTrackIndex and * getSampleTime only retrieve information for the subset of tracks selected.. */ -media_status_t AMediaExtractor_unselectTrack(AMediaExtractor*, size_t idx); +media_status_t AMediaExtractor_unselectTrack(AMediaExtractor*, size_t idx) __INTRODUCED_IN(21); /** * Read the current sample. */ -ssize_t AMediaExtractor_readSampleData(AMediaExtractor*, uint8_t *buffer, size_t capacity); +ssize_t AMediaExtractor_readSampleData(AMediaExtractor*, + uint8_t *buffer, size_t capacity) __INTRODUCED_IN(21); /** * Read the current sample's flags. */ -uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor*); // see definitions below +uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor*) __INTRODUCED_IN(21); /** * Returns the track index the current sample originates from (or -1 * if no more samples are available) */ -int AMediaExtractor_getSampleTrackIndex(AMediaExtractor*); +int AMediaExtractor_getSampleTrackIndex(AMediaExtractor*) __INTRODUCED_IN(21); /** * Returns the current sample's presentation time in microseconds. * or -1 if no more samples are available. */ -int64_t AMediaExtractor_getSampleTime(AMediaExtractor*); +int64_t AMediaExtractor_getSampleTime(AMediaExtractor*) __INTRODUCED_IN(21); /** * Advance to the next sample. Returns false if no more sample data * is available (end of stream). */ -bool AMediaExtractor_advance(AMediaExtractor*); +bool AMediaExtractor_advance(AMediaExtractor*) __INTRODUCED_IN(21); typedef enum { AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC, @@ -143,7 +140,8 @@ typedef enum { /** * */ -media_status_t AMediaExtractor_seekTo(AMediaExtractor*, int64_t seekPosUs, SeekMode mode); +media_status_t AMediaExtractor_seekTo(AMediaExtractor*, + int64_t seekPosUs, SeekMode mode) __INTRODUCED_IN(21); /** * mapping of crypto scheme uuid to the scheme specific data for that scheme @@ -165,18 +163,16 @@ typedef struct PsshInfo { /** * Get the PSSH info if present. */ -PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor*); +PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor*) __INTRODUCED_IN(21); -AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *); +AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *) __INTRODUCED_IN(21); enum { AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC = 1, AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2, }; -#if __ANDROID_API__ >= 28 - /** * Returns the format of the extractor. The caller must free the returned format * using AMediaFormat_delete(format). @@ -184,7 +180,7 @@ enum { * This function will always return a format; however, the format could be empty * (no key-value pairs) if the media container does not provide format information. */ -AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor*); +AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor*) __INTRODUCED_IN(28); /** * Returns the size of the current sample in bytes, or -1 when no samples are @@ -196,7 +192,7 @@ AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor*); * AMediaExtractor_readSampleData(ex, buf, sampleSize); * */ -ssize_t AMediaExtractor_getSampleSize(AMediaExtractor*); +ssize_t AMediaExtractor_getSampleSize(AMediaExtractor*) __INTRODUCED_IN(28); /** * Returns the duration of cached media samples downloaded from a network data source @@ -209,7 +205,7 @@ ssize_t AMediaExtractor_getSampleSize(AMediaExtractor*); * cached duration cannot be calculated (bitrate, duration, and file size information * not available). */ -int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *); +int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *) __INTRODUCED_IN(28); /** * Read the current sample's metadata format into |fmt|. Examples of sample metadata are @@ -220,11 +216,8 @@ int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *); * Existing key-value pairs in |fmt| would be removed if this API returns AMEDIA_OK. * The contents of |fmt| is undefined if this API returns AMEDIA_ERROR_*. */ -media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt); - -#endif /* __ANDROID_API__ >= 28 */ - -#endif /* __ANDROID_API__ >= 21 */ +media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, + AMediaFormat *fmt) __INTRODUCED_IN(28); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index f510dff082..dd39acff0d 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -46,44 +46,42 @@ __BEGIN_DECLS struct AMediaFormat; typedef struct AMediaFormat AMediaFormat; -#if __ANDROID_API__ >= 21 - -AMediaFormat *AMediaFormat_new(); -media_status_t AMediaFormat_delete(AMediaFormat*); +AMediaFormat *AMediaFormat_new() __INTRODUCED_IN(21); +media_status_t AMediaFormat_delete(AMediaFormat*) __INTRODUCED_IN(21); /** * Human readable representation of the format. The returned string is owned by the format, * and remains valid until the next call to toString, or until the format is deleted. */ -const char* AMediaFormat_toString(AMediaFormat*); +const char* AMediaFormat_toString(AMediaFormat*) __INTRODUCED_IN(21); -bool AMediaFormat_getInt32(AMediaFormat*, const char *name, int32_t *out); -bool AMediaFormat_getInt64(AMediaFormat*, const char *name, int64_t *out); -bool AMediaFormat_getFloat(AMediaFormat*, const char *name, float *out); -bool AMediaFormat_getSize(AMediaFormat*, const char *name, size_t *out); +bool AMediaFormat_getInt32(AMediaFormat*, const char *name, int32_t *out) __INTRODUCED_IN(21); +bool AMediaFormat_getInt64(AMediaFormat*, const char *name, int64_t *out) __INTRODUCED_IN(21); +bool AMediaFormat_getFloat(AMediaFormat*, const char *name, float *out) __INTRODUCED_IN(21); +bool AMediaFormat_getSize(AMediaFormat*, const char *name, size_t *out) __INTRODUCED_IN(21); /** * The returned data is owned by the format and remains valid as long as the named entry * is part of the format. */ -bool AMediaFormat_getBuffer(AMediaFormat*, const char *name, void** data, size_t *size); +bool AMediaFormat_getBuffer(AMediaFormat*, const char *name, void** data, size_t *size) __INTRODUCED_IN(21); /** * The returned string is owned by the format, and remains valid until the next call to getString, * or until the format is deleted. */ -bool AMediaFormat_getString(AMediaFormat*, const char *name, const char **out); +bool AMediaFormat_getString(AMediaFormat*, const char *name, const char **out) __INTRODUCED_IN(21); -void AMediaFormat_setInt32(AMediaFormat*, const char* name, int32_t value); -void AMediaFormat_setInt64(AMediaFormat*, const char* name, int64_t value); -void AMediaFormat_setFloat(AMediaFormat*, const char* name, float value); +void AMediaFormat_setInt32(AMediaFormat*, const char* name, int32_t value) __INTRODUCED_IN(21); +void AMediaFormat_setInt64(AMediaFormat*, const char* name, int64_t value) __INTRODUCED_IN(21); +void AMediaFormat_setFloat(AMediaFormat*, const char* name, float value) __INTRODUCED_IN(21); /** * The provided string is copied into the format. */ -void AMediaFormat_setString(AMediaFormat*, const char* name, const char* value); +void AMediaFormat_setString(AMediaFormat*, const char* name, const char* value) __INTRODUCED_IN(21); /** * The provided data is copied into the format. */ -void AMediaFormat_setBuffer(AMediaFormat*, const char* name, void* data, size_t size); +void AMediaFormat_setBuffer(AMediaFormat*, const char* name, void* data, size_t size) __INTRODUCED_IN(21); @@ -157,18 +155,14 @@ extern const char* AMEDIAFORMAT_KEY_TRACK_ID; extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX; extern const char* AMEDIAFORMAT_KEY_WIDTH; -#endif /* __ANDROID_API__ >= 21 */ - -#if __ANDROID_API__ >= 28 -bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out); +bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out) __INTRODUCED_IN(28); bool AMediaFormat_getRect(AMediaFormat*, const char *name, - int32_t *left, int32_t *top, int32_t *right, int32_t *bottom); + int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) __INTRODUCED_IN(28); -void AMediaFormat_setDouble(AMediaFormat*, const char* name, double value); -void AMediaFormat_setSize(AMediaFormat*, const char* name, size_t value); +void AMediaFormat_setDouble(AMediaFormat*, const char* name, double value) __INTRODUCED_IN(28); +void AMediaFormat_setSize(AMediaFormat*, const char* name, size_t value) __INTRODUCED_IN(28); void AMediaFormat_setRect(AMediaFormat*, const char* name, - int32_t left, int32_t top, int32_t right, int32_t bottom); -#endif /* __ANDROID_API__ >= 28 */ + int32_t left, int32_t top, int32_t right, int32_t bottom) __INTRODUCED_IN(28); __END_DECLS diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h index dc9e0acfd7..75c70edba8 100644 --- a/media/ndk/include/media/NdkMediaMuxer.h +++ b/media/ndk/include/media/NdkMediaMuxer.h @@ -53,17 +53,15 @@ typedef enum { AMEDIAMUXER_OUTPUT_FORMAT_WEBM = 1, } OutputFormat; -#if __ANDROID_API__ >= 21 - /** * Create new media muxer */ -AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format); +AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) __INTRODUCED_IN(21); /** * Delete a previously created media muxer */ -media_status_t AMediaMuxer_delete(AMediaMuxer*); +media_status_t AMediaMuxer_delete(AMediaMuxer*) __INTRODUCED_IN(21); /** * Set and store the geodata (latitude and longitude) in the output file. @@ -76,7 +74,8 @@ media_status_t AMediaMuxer_delete(AMediaMuxer*); * Latitude must be in the range [-90, 90]. * Longitude must be in the range [-180, 180]. */ -media_status_t AMediaMuxer_setLocation(AMediaMuxer*, float latitude, float longitude); +media_status_t AMediaMuxer_setLocation(AMediaMuxer*, + float latitude, float longitude) __INTRODUCED_IN(21); /** * Sets the orientation hint for output video playback. @@ -90,26 +89,26 @@ media_status_t AMediaMuxer_setLocation(AMediaMuxer*, float latitude, float longi * The angle is specified in degrees, clockwise. * The supported angles are 0, 90, 180, and 270 degrees. */ -media_status_t AMediaMuxer_setOrientationHint(AMediaMuxer*, int degrees); +media_status_t AMediaMuxer_setOrientationHint(AMediaMuxer*, int degrees) __INTRODUCED_IN(21); /** * Adds a track with the specified format. * Returns the index of the new track or a negative value in case of failure, * which can be interpreted as a media_status_t. */ -ssize_t AMediaMuxer_addTrack(AMediaMuxer*, const AMediaFormat* format); +ssize_t AMediaMuxer_addTrack(AMediaMuxer*, const AMediaFormat* format) __INTRODUCED_IN(21); /** * Start the muxer. Should be called after AMediaMuxer_addTrack and * before AMediaMuxer_writeSampleData. */ -media_status_t AMediaMuxer_start(AMediaMuxer*); +media_status_t AMediaMuxer_start(AMediaMuxer*) __INTRODUCED_IN(21); /** * Stops the muxer. * Once the muxer stops, it can not be restarted. */ -media_status_t AMediaMuxer_stop(AMediaMuxer*); +media_status_t AMediaMuxer_stop(AMediaMuxer*) __INTRODUCED_IN(21); /** * Writes an encoded sample into the muxer. @@ -119,9 +118,8 @@ media_status_t AMediaMuxer_stop(AMediaMuxer*); * by the encoder.) */ media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer, - size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info); - -#endif /* __ANDROID_API__ >= 21 */ + size_t trackIdx, const uint8_t *data, + const AMediaCodecBufferInfo *info) __INTRODUCED_IN(21); __END_DECLS -- GitLab From 070e751eee408cc7d23cf6985ef796e424fcc4ba Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 22 May 2018 09:29:13 -0700 Subject: [PATCH 0094/1530] libAudioHal: factorize V2 and V4 Merge the two versions and ifdef the few parts still different. Test: playback music, assistant Change-Id: I750f5d18ba34236637c18a87e2daa54a7c446d91 Signed-off-by: Kevin Rocard --- .../libaudiohal/2.0/ConversionHelperHidl.cpp | 105 --- media/libaudiohal/2.0/ConversionHelperHidl.h | 87 -- media/libaudiohal/2.0/DeviceHalHidl.cpp | 374 --------- media/libaudiohal/2.0/DeviceHalLocal.cpp | 206 ----- media/libaudiohal/2.0/DeviceHalLocal.h | 129 --- .../2.0/DevicesFactoryHalHybrid.cpp | 41 - .../2.0/DevicesFactoryHalLocal.cpp | 71 -- .../libaudiohal/2.0/DevicesFactoryHalLocal.h | 48 -- media/libaudiohal/2.0/EffectHalHidl.h | 110 --- media/libaudiohal/2.0/StreamHalHidl.cpp | 774 ------------------ media/libaudiohal/2.0/StreamHalHidl.h | 250 ------ media/libaudiohal/2.0/StreamHalLocal.cpp | 350 -------- media/libaudiohal/2.0/StreamHalLocal.h | 221 ----- media/libaudiohal/2.0/StreamPowerLog.h | 104 --- media/libaudiohal/2.0/VersionUtils.h | 49 -- media/libaudiohal/4.0/Android.bp | 65 -- media/libaudiohal/4.0/DeviceHalHidl.h | 131 --- .../libaudiohal/4.0/DevicesFactoryHalHidl.cpp | 93 --- media/libaudiohal/4.0/DevicesFactoryHalHidl.h | 53 -- .../libaudiohal/4.0/DevicesFactoryHalHybrid.h | 48 -- media/libaudiohal/4.0/EffectBufferHalHidl.cpp | 146 ---- media/libaudiohal/4.0/EffectBufferHalHidl.h | 78 -- media/libaudiohal/4.0/EffectHalHidl.cpp | 341 -------- .../libaudiohal/4.0/EffectsFactoryHalHidl.cpp | 149 ---- media/libaudiohal/4.0/EffectsFactoryHalHidl.h | 75 -- .../include/libaudiohal/4.0/FactoryHalHidl.h | 36 - media/libaudiohal/Android.bp | 1 + .../DevicesFactoryHalInterface.cpp | 3 +- .../EffectsFactoryHalInterface.cpp | 3 +- media/libaudiohal/{2.0 => impl}/Android.bp | 47 +- .../{4.0 => impl}/ConversionHelperHidl.cpp | 26 +- .../{4.0 => impl}/ConversionHelperHidl.h | 19 +- .../{4.0 => impl}/DeviceHalHidl.cpp | 52 +- .../libaudiohal/{2.0 => impl}/DeviceHalHidl.h | 10 +- .../{4.0 => impl}/DeviceHalLocal.cpp | 11 +- .../{4.0 => impl}/DeviceHalLocal.h | 4 +- .../{2.0 => impl}/DevicesFactoryHalHidl.cpp | 23 +- .../{2.0 => impl}/DevicesFactoryHalHidl.h | 7 +- .../{4.0 => impl}/DevicesFactoryHalHybrid.cpp | 4 +- .../{2.0 => impl}/DevicesFactoryHalHybrid.h | 4 +- .../{4.0 => impl}/DevicesFactoryHalLocal.cpp | 4 +- .../{4.0 => impl}/DevicesFactoryHalLocal.h | 4 +- .../{2.0 => impl}/EffectBufferHalHidl.cpp | 4 +- .../{2.0 => impl}/EffectBufferHalHidl.h | 7 +- .../{2.0 => impl}/EffectHalHidl.cpp | 20 +- .../libaudiohal/{4.0 => impl}/EffectHalHidl.h | 15 +- .../{2.0 => impl}/EffectsFactoryHalHidl.cpp | 12 +- .../{2.0 => impl}/EffectsFactoryHalHidl.h | 10 +- .../{4.0 => impl}/StreamHalHidl.cpp | 68 +- .../libaudiohal/{4.0 => impl}/StreamHalHidl.h | 21 +- .../{4.0 => impl}/StreamHalLocal.cpp | 11 +- .../{4.0 => impl}/StreamHalLocal.h | 4 +- .../{4.0 => impl}/StreamPowerLog.h | 4 +- media/libaudiohal/impl/VersionMacro.h | 34 + .../libaudiohal/{4.0 => impl}/VersionUtils.h | 23 +- .../include/libaudiohal}/FactoryHalHidl.h | 14 +- 56 files changed, 316 insertions(+), 4287 deletions(-) delete mode 100644 media/libaudiohal/2.0/ConversionHelperHidl.cpp delete mode 100644 media/libaudiohal/2.0/ConversionHelperHidl.h delete mode 100644 media/libaudiohal/2.0/DeviceHalHidl.cpp delete mode 100644 media/libaudiohal/2.0/DeviceHalLocal.cpp delete mode 100644 media/libaudiohal/2.0/DeviceHalLocal.h delete mode 100644 media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp delete mode 100644 media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp delete mode 100644 media/libaudiohal/2.0/DevicesFactoryHalLocal.h delete mode 100644 media/libaudiohal/2.0/EffectHalHidl.h delete mode 100644 media/libaudiohal/2.0/StreamHalHidl.cpp delete mode 100644 media/libaudiohal/2.0/StreamHalHidl.h delete mode 100644 media/libaudiohal/2.0/StreamHalLocal.cpp delete mode 100644 media/libaudiohal/2.0/StreamHalLocal.h delete mode 100644 media/libaudiohal/2.0/StreamPowerLog.h delete mode 100644 media/libaudiohal/2.0/VersionUtils.h delete mode 100644 media/libaudiohal/4.0/Android.bp delete mode 100644 media/libaudiohal/4.0/DeviceHalHidl.h delete mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp delete mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHidl.h delete mode 100644 media/libaudiohal/4.0/DevicesFactoryHalHybrid.h delete mode 100644 media/libaudiohal/4.0/EffectBufferHalHidl.cpp delete mode 100644 media/libaudiohal/4.0/EffectBufferHalHidl.h delete mode 100644 media/libaudiohal/4.0/EffectHalHidl.cpp delete mode 100644 media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp delete mode 100644 media/libaudiohal/4.0/EffectsFactoryHalHidl.h delete mode 100644 media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h rename media/libaudiohal/{2.0 => impl}/Android.bp (70%) rename media/libaudiohal/{4.0 => impl}/ConversionHelperHidl.cpp (94%) rename media/libaudiohal/{4.0 => impl}/ConversionHelperHidl.h (86%) rename media/libaudiohal/{4.0 => impl}/DeviceHalHidl.cpp (90%) rename media/libaudiohal/{2.0 => impl}/DeviceHalHidl.h (94%) rename media/libaudiohal/{4.0 => impl}/DeviceHalLocal.cpp (96%) rename media/libaudiohal/{4.0 => impl}/DeviceHalLocal.h (98%) rename media/libaudiohal/{2.0 => impl}/DevicesFactoryHalHidl.cpp (83%) rename media/libaudiohal/{2.0 => impl}/DevicesFactoryHalHidl.h (90%) rename media/libaudiohal/{4.0 => impl}/DevicesFactoryHalHybrid.cpp (96%) rename media/libaudiohal/{2.0 => impl}/DevicesFactoryHalHybrid.h (96%) rename media/libaudiohal/{4.0 => impl}/DevicesFactoryHalLocal.cpp (97%) rename media/libaudiohal/{4.0 => impl}/DevicesFactoryHalLocal.h (96%) rename media/libaudiohal/{2.0 => impl}/EffectBufferHalHidl.cpp (98%) rename media/libaudiohal/{2.0 => impl}/EffectBufferHalHidl.h (93%) rename media/libaudiohal/{2.0 => impl}/EffectHalHidl.cpp (95%) rename media/libaudiohal/{4.0 => impl}/EffectHalHidl.h (88%) rename media/libaudiohal/{2.0 => impl}/EffectsFactoryHalHidl.cpp (94%) rename media/libaudiohal/{2.0 => impl}/EffectsFactoryHalHidl.h (88%) rename media/libaudiohal/{4.0 => impl}/StreamHalHidl.cpp (93%) rename media/libaudiohal/{4.0 => impl}/StreamHalHidl.h (92%) rename media/libaudiohal/{4.0 => impl}/StreamHalLocal.cpp (97%) rename media/libaudiohal/{4.0 => impl}/StreamHalLocal.h (99%) rename media/libaudiohal/{4.0 => impl}/StreamPowerLog.h (98%) create mode 100644 media/libaudiohal/impl/VersionMacro.h rename media/libaudiohal/{4.0 => impl}/VersionUtils.h (67%) rename media/libaudiohal/{2.0/include/libaudiohal/2.0 => impl/include/libaudiohal}/FactoryHalHidl.h (78%) diff --git a/media/libaudiohal/2.0/ConversionHelperHidl.cpp b/media/libaudiohal/2.0/ConversionHelperHidl.cpp deleted file mode 100644 index b7209e012a..0000000000 --- a/media/libaudiohal/2.0/ConversionHelperHidl.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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. - */ - -#include - -#define LOG_TAG "HalHidl" -#include -#include - -#include "ConversionHelperHidl.h" - -using ::android::hardware::audio::V2_0::Result; - -namespace android { -namespace V2_0 { - -// static -status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { - AudioParameter halKeys(keys); - if (halKeys.size() == 0) return BAD_VALUE; - hidlKeys->resize(halKeys.size()); - //FIXME: keyStreamSupportedChannels and keyStreamSupportedSamplingRates come with a - // "keyFormat=" pair. We need to transform it into a single key string so that it is - // carried over to the legacy HAL via HIDL. - String8 value; - bool keepFormatValue = halKeys.size() == 2 && - (halKeys.get(String8(AudioParameter::keyStreamSupportedChannels), value) == NO_ERROR || - halKeys.get(String8(AudioParameter::keyStreamSupportedSamplingRates), value) == NO_ERROR); - - for (size_t i = 0; i < halKeys.size(); ++i) { - String8 key; - status_t status = halKeys.getAt(i, key); - if (status != OK) return status; - if (keepFormatValue && key == AudioParameter::keyFormat) { - AudioParameter formatParam; - halKeys.getAt(i, key, value); - formatParam.add(key, value); - key = formatParam.toString(); - } - (*hidlKeys)[i] = key.string(); - } - return OK; -} - -// static -status_t ConversionHelperHidl::parametersFromHal( - const String8& kvPairs, hidl_vec *hidlParams) { - AudioParameter params(kvPairs); - if (params.size() == 0) return BAD_VALUE; - hidlParams->resize(params.size()); - for (size_t i = 0; i < params.size(); ++i) { - String8 key, value; - status_t status = params.getAt(i, key, value); - if (status != OK) return status; - (*hidlParams)[i].key = key.string(); - (*hidlParams)[i].value = value.string(); - } - return OK; -} - -// static -void ConversionHelperHidl::parametersToHal( - const hidl_vec& parameters, String8 *values) { - AudioParameter params; - for (size_t i = 0; i < parameters.size(); ++i) { - params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str())); - } - values->setTo(params.toString()); -} - -ConversionHelperHidl::ConversionHelperHidl(const char* className) - : mClassName(className) { -} - -// static -status_t ConversionHelperHidl::analyzeResult(const Result& result) { - switch (result) { - case Result::OK: return OK; - case Result::INVALID_ARGUMENTS: return BAD_VALUE; - case Result::INVALID_STATE: return NOT_ENOUGH_DATA; - case Result::NOT_INITIALIZED: return NO_INIT; - case Result::NOT_SUPPORTED: return INVALID_OPERATION; - default: return NO_INIT; - } -} - -void ConversionHelperHidl::emitError(const char* funcName, const char* description) { - ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/ConversionHelperHidl.h b/media/libaudiohal/2.0/ConversionHelperHidl.h deleted file mode 100644 index 593afb1eae..0000000000 --- a/media/libaudiohal/2.0/ConversionHelperHidl.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H -#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H - -#include -#include -#include -#include - -using ::android::hardware::audio::V2_0::ParameterValue; -using CoreResult = ::android::hardware::audio::V2_0::Result; -using ::android::hardware::Return; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; - -namespace android { -namespace V2_0 { - -class ConversionHelperHidl { - protected: - static status_t keysFromHal(const String8& keys, hidl_vec *hidlKeys); - static status_t parametersFromHal(const String8& kvPairs, hidl_vec *hidlParams); - static void parametersToHal(const hidl_vec& parameters, String8 *values); - - ConversionHelperHidl(const char* className); - - template - status_t processReturn(const char* funcName, const Return& ret, T *retval) { - if (ret.isOk()) { - // This way it also works for enum class to unscoped enum conversion. - *retval = static_cast(static_cast(ret)); - return OK; - } - return processReturn(funcName, ret); - } - - template - status_t processReturn(const char* funcName, const Return& ret) { - if (!ret.isOk()) { - emitError(funcName, ret.description().c_str()); - } - return ret.isOk() ? OK : FAILED_TRANSACTION; - } - - status_t processReturn(const char* funcName, const Return& ret) { - if (!ret.isOk()) { - emitError(funcName, ret.description().c_str()); - } - return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; - } - - template - status_t processReturn( - const char* funcName, const Return& ret, CoreResult retval) { - if (!ret.isOk()) { - emitError(funcName, ret.description().c_str()); - } - return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; - } - - private: - const char* mClassName; - - static status_t analyzeResult(const CoreResult& result); - - void emitError(const char* funcName, const char* description); -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/2.0/DeviceHalHidl.cpp b/media/libaudiohal/2.0/DeviceHalHidl.cpp deleted file mode 100644 index a79cedd986..0000000000 --- a/media/libaudiohal/2.0/DeviceHalHidl.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * 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. - */ - -#include - -#define LOG_TAG "DeviceHalHidl" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include - -#include - -#include "DeviceHalHidl.h" -#include "HidlUtils.h" -#include "StreamHalHidl.h" -#include "VersionUtils.h" - -using ::android::hardware::audio::common::V2_0::AudioConfig; -using ::android::hardware::audio::common::V2_0::AudioDevice; -using ::android::hardware::audio::common::V2_0::AudioInputFlag; -using ::android::hardware::audio::common::V2_0::AudioOutputFlag; -using ::android::hardware::audio::common::V2_0::AudioPatchHandle; -using ::android::hardware::audio::common::V2_0::AudioPort; -using ::android::hardware::audio::common::V2_0::AudioPortConfig; -using ::android::hardware::audio::common::V2_0::AudioMode; -using ::android::hardware::audio::common::V2_0::AudioSource; -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::common::utils::mkEnumConverter; -using ::android::hardware::audio::V2_0::DeviceAddress; -using ::android::hardware::audio::V2_0::IPrimaryDevice; -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; - -namespace android { -namespace V2_0 { - -namespace { - -status_t deviceAddressFromHal( - audio_devices_t device, const char* halAddress, DeviceAddress* address) { - address->device = AudioDevice(device); - - if (halAddress == nullptr || strnlen(halAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { - return OK; - } - const bool isInput = (device & AUDIO_DEVICE_BIT_IN) != 0; - if (isInput) device &= ~AUDIO_DEVICE_BIT_IN; - if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_A2DP) != 0) - || (isInput && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) != 0)) { - int status = sscanf(halAddress, - "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", - &address->address.mac[0], &address->address.mac[1], &address->address.mac[2], - &address->address.mac[3], &address->address.mac[4], &address->address.mac[5]); - return status == 6 ? OK : BAD_VALUE; - } else if ((!isInput && (device & AUDIO_DEVICE_OUT_IP) != 0) - || (isInput && (device & AUDIO_DEVICE_IN_IP) != 0)) { - int status = sscanf(halAddress, - "%hhu.%hhu.%hhu.%hhu", - &address->address.ipv4[0], &address->address.ipv4[1], - &address->address.ipv4[2], &address->address.ipv4[3]); - return status == 4 ? OK : BAD_VALUE; - } else if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_USB)) != 0 - || (isInput && (device & AUDIO_DEVICE_IN_ALL_USB)) != 0) { - int status = sscanf(halAddress, - "card=%d;device=%d", - &address->address.alsa.card, &address->address.alsa.device); - return status == 2 ? OK : BAD_VALUE; - } else if ((!isInput && (device & AUDIO_DEVICE_OUT_BUS) != 0) - || (isInput && (device & AUDIO_DEVICE_IN_BUS) != 0)) { - if (halAddress != NULL) { - address->busAddress = halAddress; - return OK; - } - return BAD_VALUE; - } else if ((!isInput && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) != 0 - || (isInput && (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) != 0)) { - if (halAddress != NULL) { - address->rSubmixAddress = halAddress; - return OK; - } - return BAD_VALUE; - } - return OK; -} - -} // namespace - -DeviceHalHidl::DeviceHalHidl(const sp& device) - : ConversionHelperHidl("Device"), mDevice(device), - mPrimaryDevice(IPrimaryDevice::castFrom(device)) { -} - -DeviceHalHidl::~DeviceHalHidl() { - if (mDevice != 0) { - mDevice.clear(); - hardware::IPCThreadState::self()->flushCommands(); - } -} - -status_t DeviceHalHidl::getSupportedDevices(uint32_t*) { - // Obsolete. - return INVALID_OPERATION; -} - -status_t DeviceHalHidl::initCheck() { - if (mDevice == 0) return NO_INIT; - return processReturn("initCheck", mDevice->initCheck()); -} - -status_t DeviceHalHidl::setVoiceVolume(float volume) { - if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; - return processReturn("setVoiceVolume", mPrimaryDevice->setVoiceVolume(volume)); -} - -status_t DeviceHalHidl::setMasterVolume(float volume) { - if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; - return processReturn("setMasterVolume", mPrimaryDevice->setMasterVolume(volume)); -} - -status_t DeviceHalHidl::getMasterVolume(float *volume) { - if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; - Result retval; - Return ret = mPrimaryDevice->getMasterVolume( - [&](Result r, float v) { - retval = r; - if (retval == Result::OK) { - *volume = v; - } - }); - return processReturn("getMasterVolume", ret, retval); -} - -status_t DeviceHalHidl::setMode(audio_mode_t mode) { - if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; - return processReturn("setMode", mPrimaryDevice->setMode(AudioMode(mode))); -} - -status_t DeviceHalHidl::setMicMute(bool state) { - if (mDevice == 0) return NO_INIT; - return processReturn("setMicMute", mDevice->setMicMute(state)); -} - -status_t DeviceHalHidl::getMicMute(bool *state) { - if (mDevice == 0) return NO_INIT; - Result retval; - Return ret = mDevice->getMicMute( - [&](Result r, bool mute) { - retval = r; - if (retval == Result::OK) { - *state = mute; - } - }); - return processReturn("getMicMute", ret, retval); -} - -status_t DeviceHalHidl::setMasterMute(bool state) { - if (mDevice == 0) return NO_INIT; - return processReturn("setMasterMute", mDevice->setMasterMute(state)); -} - -status_t DeviceHalHidl::getMasterMute(bool *state) { - if (mDevice == 0) return NO_INIT; - Result retval; - Return ret = mDevice->getMasterMute( - [&](Result r, bool mute) { - retval = r; - if (retval == Result::OK) { - *state = mute; - } - }); - return processReturn("getMasterMute", ret, retval); -} - -status_t DeviceHalHidl::setParameters(const String8& kvPairs) { - if (mDevice == 0) return NO_INIT; - hidl_vec hidlParams; - status_t status = parametersFromHal(kvPairs, &hidlParams); - if (status != OK) return status; - // TODO: change the API so that context and kvPairs are separated - return processReturn("setParameters", - utils::setParameters(mDevice, {} /* context */, hidlParams)); -} - -status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { - values->clear(); - if (mDevice == 0) return NO_INIT; - hidl_vec hidlKeys; - status_t status = keysFromHal(keys, &hidlKeys); - if (status != OK) return status; - Result retval; - Return ret = utils::getParameters(mDevice, - {} /* context */, - hidlKeys, - [&](Result r, const hidl_vec& parameters) { - retval = r; - if (retval == Result::OK) { - parametersToHal(parameters, values); - } - }); - return processReturn("getParameters", ret, retval); -} - -status_t DeviceHalHidl::getInputBufferSize( - const struct audio_config *config, size_t *size) { - if (mDevice == 0) return NO_INIT; - AudioConfig hidlConfig; - HidlUtils::audioConfigFromHal(*config, &hidlConfig); - Result retval; - Return ret = mDevice->getInputBufferSize( - hidlConfig, - [&](Result r, uint64_t bufferSize) { - retval = r; - if (retval == Result::OK) { - *size = static_cast(bufferSize); - } - }); - return processReturn("getInputBufferSize", ret, retval); -} - -status_t DeviceHalHidl::openOutputStream( - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream) { - if (mDevice == 0) return NO_INIT; - DeviceAddress hidlDevice; - status_t status = deviceAddressFromHal(devices, address, &hidlDevice); - if (status != OK) return status; - AudioConfig hidlConfig; - HidlUtils::audioConfigFromHal(*config, &hidlConfig); - Result retval = Result::NOT_INITIALIZED; - Return ret = mDevice->openOutputStream( - handle, - hidlDevice, - hidlConfig, - mkEnumConverter(flags), - [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { - retval = r; - if (retval == Result::OK) { - *outStream = new StreamOutHalHidl(result); - } - HidlUtils::audioConfigToHal(suggestedConfig, config); - }); - return processReturn("openOutputStream", ret, retval); -} - -status_t DeviceHalHidl::openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - sp *inStream) { - if (mDevice == 0) return NO_INIT; - DeviceAddress hidlDevice; - status_t status = deviceAddressFromHal(devices, address, &hidlDevice); - if (status != OK) return status; - AudioConfig hidlConfig; - HidlUtils::audioConfigFromHal(*config, &hidlConfig); - Result retval = Result::NOT_INITIALIZED; - auto sourceMetadata = AudioSource(source); - Return ret = mDevice->openInputStream( - handle, - hidlDevice, - hidlConfig, - mkEnumConverter(flags), - sourceMetadata, - [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { - retval = r; - if (retval == Result::OK) { - *inStream = new StreamInHalHidl(result); - } - HidlUtils::audioConfigToHal(suggestedConfig, config); - }); - return processReturn("openInputStream", ret, retval); -} - -status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) { - if (mDevice == 0) return NO_INIT; - return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches); -} - -status_t DeviceHalHidl::createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch) { - if (mDevice == 0) return NO_INIT; - hidl_vec hidlSources, hidlSinks; - HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources); - HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks); - Result retval; - Return ret = mDevice->createAudioPatch( - hidlSources, hidlSinks, - [&](Result r, AudioPatchHandle hidlPatch) { - retval = r; - if (retval == Result::OK) { - *patch = static_cast(hidlPatch); - } - }); - return processReturn("createAudioPatch", ret, retval); -} - -status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) { - if (mDevice == 0) return NO_INIT; - return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch)); -} - -status_t DeviceHalHidl::getAudioPort(struct audio_port *port) { - if (mDevice == 0) return NO_INIT; - AudioPort hidlPort; - HidlUtils::audioPortFromHal(*port, &hidlPort); - Result retval; - Return ret = mDevice->getAudioPort( - hidlPort, - [&](Result r, const AudioPort& p) { - retval = r; - if (retval == Result::OK) { - HidlUtils::audioPortToHal(p, port); - } - }); - return processReturn("getAudioPort", ret, retval); -} - -status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) { - if (mDevice == 0) return NO_INIT; - AudioPortConfig hidlConfig; - HidlUtils::audioPortConfigFromHal(*config, &hidlConfig); - return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig)); -} - -status_t DeviceHalHidl::getMicrophones( - std::vector *microphonesInfo __unused) { - if (mDevice == 0) return NO_INIT; - return INVALID_OPERATION; -} - -status_t DeviceHalHidl::dump(int fd) { - if (mDevice == 0) return NO_INIT; - native_handle_t* hidlHandle = native_handle_create(1, 0); - hidlHandle->data[0] = fd; - Return ret = mDevice->debug(hidlHandle, {} /* options */); - native_handle_delete(hidlHandle); - return processReturn("dump", ret); -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/DeviceHalLocal.cpp b/media/libaudiohal/2.0/DeviceHalLocal.cpp deleted file mode 100644 index 41646593f3..0000000000 --- a/media/libaudiohal/2.0/DeviceHalLocal.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "DeviceHalLocal" -//#define LOG_NDEBUG 0 - -#include - -#include "DeviceHalLocal.h" -#include "StreamHalLocal.h" - -namespace android { -namespace V2_0 { - -DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) - : mDev(dev) { -} - -DeviceHalLocal::~DeviceHalLocal() { - int status = audio_hw_device_close(mDev); - ALOGW_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status)); - mDev = 0; -} - -status_t DeviceHalLocal::getSupportedDevices(uint32_t *devices) { - if (mDev->get_supported_devices == NULL) return INVALID_OPERATION; - *devices = mDev->get_supported_devices(mDev); - return OK; -} - -status_t DeviceHalLocal::initCheck() { - return mDev->init_check(mDev); -} - -status_t DeviceHalLocal::setVoiceVolume(float volume) { - return mDev->set_voice_volume(mDev, volume); -} - -status_t DeviceHalLocal::setMasterVolume(float volume) { - if (mDev->set_master_volume == NULL) return INVALID_OPERATION; - return mDev->set_master_volume(mDev, volume); -} - -status_t DeviceHalLocal::getMasterVolume(float *volume) { - if (mDev->get_master_volume == NULL) return INVALID_OPERATION; - return mDev->get_master_volume(mDev, volume); -} - -status_t DeviceHalLocal::setMode(audio_mode_t mode) { - return mDev->set_mode(mDev, mode); -} - -status_t DeviceHalLocal::setMicMute(bool state) { - return mDev->set_mic_mute(mDev, state); -} - -status_t DeviceHalLocal::getMicMute(bool *state) { - return mDev->get_mic_mute(mDev, state); -} - -status_t DeviceHalLocal::setMasterMute(bool state) { - if (mDev->set_master_mute == NULL) return INVALID_OPERATION; - return mDev->set_master_mute(mDev, state); -} - -status_t DeviceHalLocal::getMasterMute(bool *state) { - if (mDev->get_master_mute == NULL) return INVALID_OPERATION; - return mDev->get_master_mute(mDev, state); -} - -status_t DeviceHalLocal::setParameters(const String8& kvPairs) { - return mDev->set_parameters(mDev, kvPairs.string()); -} - -status_t DeviceHalLocal::getParameters(const String8& keys, String8 *values) { - char *halValues = mDev->get_parameters(mDev, keys.string()); - if (halValues != NULL) { - values->setTo(halValues); - free(halValues); - } else { - values->clear(); - } - return OK; -} - -status_t DeviceHalLocal::getInputBufferSize( - const struct audio_config *config, size_t *size) { - *size = mDev->get_input_buffer_size(mDev, config); - return OK; -} - -status_t DeviceHalLocal::openOutputStream( - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream) { - audio_stream_out_t *halStream; - ALOGV("open_output_stream handle: %d devices: %x flags: %#x" - "srate: %d format %#x channels %x address %s", - handle, devices, flags, - config->sample_rate, config->format, config->channel_mask, - address); - int openResut = mDev->open_output_stream( - mDev, handle, devices, flags, config, &halStream, address); - if (openResut == OK) { - *outStream = new StreamOutHalLocal(halStream, this); - } - ALOGV("open_output_stream status %d stream %p", openResut, halStream); - return openResut; -} - -status_t DeviceHalLocal::openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - sp *inStream) { - audio_stream_in_t *halStream; - ALOGV("open_input_stream handle: %d devices: %x flags: %#x " - "srate: %d format %#x channels %x address %s source %d", - handle, devices, flags, - config->sample_rate, config->format, config->channel_mask, - address, source); - int openResult = mDev->open_input_stream( - mDev, handle, devices, config, &halStream, flags, address, source); - if (openResult == OK) { - *inStream = new StreamInHalLocal(halStream, this); - } - ALOGV("open_input_stream status %d stream %p", openResult, inStream); - return openResult; -} - -status_t DeviceHalLocal::supportsAudioPatches(bool *supportsPatches) { - *supportsPatches = version() >= AUDIO_DEVICE_API_VERSION_3_0; - return OK; -} - -status_t DeviceHalLocal::createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { - return mDev->create_audio_patch( - mDev, num_sources, sources, num_sinks, sinks, patch); - } else { - return INVALID_OPERATION; - } -} - -status_t DeviceHalLocal::releaseAudioPatch(audio_patch_handle_t patch) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { - return mDev->release_audio_patch(mDev, patch); - } else { - return INVALID_OPERATION; - } -} - -status_t DeviceHalLocal::getAudioPort(struct audio_port *port) { - return mDev->get_audio_port(mDev, port); -} - -status_t DeviceHalLocal::setAudioPortConfig(const struct audio_port_config *config) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) - return mDev->set_audio_port_config(mDev, config); - else - return INVALID_OPERATION; -} - -status_t DeviceHalLocal::getMicrophones( - std::vector *microphones __unused) { - return INVALID_OPERATION; -} - -status_t DeviceHalLocal::dump(int fd) { - return mDev->dump(mDev, fd); -} - -void DeviceHalLocal::closeOutputStream(struct audio_stream_out *stream_out) { - mDev->close_output_stream(mDev, stream_out); -} - -void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { - mDev->close_input_stream(mDev, stream_in); -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/DeviceHalLocal.h b/media/libaudiohal/2.0/DeviceHalLocal.h deleted file mode 100644 index 11e063acde..0000000000 --- a/media/libaudiohal/2.0/DeviceHalLocal.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H -#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H - -#include -#include - -namespace android { -namespace V2_0 { - -class DeviceHalLocal : public DeviceHalInterface -{ - public: - // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. - virtual status_t getSupportedDevices(uint32_t *devices); - - // Check to see if the audio hardware interface has been initialized. - virtual status_t initCheck(); - - // Set the audio volume of a voice call. Range is between 0.0 and 1.0. - virtual status_t setVoiceVolume(float volume); - - // Set the audio volume for all audio activities other than voice call. - virtual status_t setMasterVolume(float volume); - - // Get the current master volume value for the HAL. - virtual status_t getMasterVolume(float *volume); - - // Called when the audio mode changes. - virtual status_t setMode(audio_mode_t mode); - - // Muting control. - virtual status_t setMicMute(bool state); - virtual status_t getMicMute(bool *state); - virtual status_t setMasterMute(bool state); - virtual status_t getMasterMute(bool *state); - - // Set global audio parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get global audio parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Returns audio input buffer size according to parameters passed. - virtual status_t getInputBufferSize(const struct audio_config *config, - size_t *size); - - // Creates and opens the audio hardware output stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openOutputStream( - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream); - - // Creates and opens the audio hardware input stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - sp *inStream); - - // Returns whether createAudioPatch and releaseAudioPatch operations are supported. - virtual status_t supportsAudioPatches(bool *supportsPatches); - - // Creates an audio patch between several source and sink ports. - virtual status_t createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch); - - // Releases an audio patch. - virtual status_t releaseAudioPatch(audio_patch_handle_t patch); - - // Fills the list of supported attributes for a given audio port. - virtual status_t getAudioPort(struct audio_port *port); - - // Set audio port configuration. - virtual status_t setAudioPortConfig(const struct audio_port_config *config); - - // List microphones - virtual status_t getMicrophones(std::vector *microphones); - - virtual status_t dump(int fd); - - void closeOutputStream(struct audio_stream_out *stream_out); - void closeInputStream(struct audio_stream_in *stream_in); - - private: - audio_hw_device_t *mDev; - - friend class DevicesFactoryHalLocal; - - // Can not be constructed directly by clients. - explicit DeviceHalLocal(audio_hw_device_t *dev); - - // The destructor automatically closes the device. - virtual ~DeviceHalLocal(); - - uint32_t version() const { return mDev->common.version; } -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp deleted file mode 100644 index 7d9c15c0b2..0000000000 --- a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DevicesFactoryHalHybrid" -//#define LOG_NDEBUG 0 - -#include "DevicesFactoryHalHybrid.h" -#include "DevicesFactoryHalLocal.h" -#include "DevicesFactoryHalHidl.h" - -namespace android { -namespace V2_0 { - -DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() - : mLocalFactory(new DevicesFactoryHalLocal()), - mHidlFactory(new DevicesFactoryHalHidl()) { -} - -status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp *device) { - if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 && - strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) { - return mHidlFactory->openDevice(name, device); - } - return mLocalFactory->openDevice(name, device); -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp deleted file mode 100644 index 6f6077560e..0000000000 --- a/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "DevicesFactoryHalLocal" -//#define LOG_NDEBUG 0 - -#include - -#include -#include - -#include "DeviceHalLocal.h" -#include "DevicesFactoryHalLocal.h" - -namespace android { -namespace V2_0 { - -static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) -{ - const hw_module_t *mod; - int rc; - - rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); - if (rc) { - ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, - AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); - goto out; - } - rc = audio_hw_device_open(mod, dev); - if (rc) { - ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, - AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); - goto out; - } - if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { - ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); - rc = BAD_VALUE; - audio_hw_device_close(*dev); - goto out; - } - return OK; - -out: - *dev = NULL; - return rc; -} - -status_t DevicesFactoryHalLocal::openDevice(const char *name, sp *device) { - audio_hw_device_t *dev; - status_t rc = load_audio_interface(name, &dev); - if (rc == OK) { - *device = new DeviceHalLocal(dev); - } - return rc; -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/DevicesFactoryHalLocal.h b/media/libaudiohal/2.0/DevicesFactoryHalLocal.h deleted file mode 100644 index 41330b4db5..0000000000 --- a/media/libaudiohal/2.0/DevicesFactoryHalLocal.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H - -#include -#include -#include - -#include "DeviceHalLocal.h" - -namespace android { -namespace V2_0 { - -class DevicesFactoryHalLocal : public DevicesFactoryHalInterface -{ - public: - // Opens a device with the specified name. To close the device, it is - // necessary to release references to the returned object. - virtual status_t openDevice(const char *name, sp *device); - - private: - friend class DevicesFactoryHalHybrid; - - // Can not be constructed directly by clients. - DevicesFactoryHalLocal() {} - - virtual ~DevicesFactoryHalLocal() {} -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/EffectHalHidl.h b/media/libaudiohal/2.0/EffectHalHidl.h deleted file mode 100644 index 6fd405e779..0000000000 --- a/media/libaudiohal/2.0/EffectHalHidl.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_EFFECT_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H - -#include -#include -#include -#include -#include - -using ::android::hardware::audio::effect::V2_0::EffectBufferConfig; -using ::android::hardware::audio::effect::V2_0::EffectConfig; -using ::android::hardware::audio::effect::V2_0::EffectDescriptor; -using ::android::hardware::audio::effect::V2_0::IEffect; -using EffectResult = ::android::hardware::audio::effect::V2_0::Result; -using ::android::hardware::EventFlag; -using ::android::hardware::MessageQueue; - -namespace android { -namespace V2_0 { - -class EffectHalHidl : public EffectHalInterface -{ - public: - // Set the input buffer. - virtual status_t setInBuffer(const sp& buffer); - - // Set the output buffer. - virtual status_t setOutBuffer(const sp& buffer); - - // Effect process function. - virtual status_t process(); - - // Process reverse stream function. This function is used to pass - // a reference stream to the effect engine. - virtual status_t processReverse(); - - // Send a command and receive a response to/from effect engine. - virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, - uint32_t *replySize, void *pReplyData); - - // Returns the effect descriptor. - virtual status_t getDescriptor(effect_descriptor_t *pDescriptor); - - // Free resources on the remote side. - virtual status_t close(); - - // Whether it's a local implementation. - virtual bool isLocal() const { return false; } - - uint64_t effectId() const { return mEffectId; } - - static void effectDescriptorToHal( - const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor); - - private: - friend class EffectsFactoryHalHidl; - typedef MessageQueue StatusMQ; - - sp mEffect; - const uint64_t mEffectId; - sp mInBuffer; - sp mOutBuffer; - bool mBuffersChanged; - std::unique_ptr mStatusMQ; - EventFlag* mEfGroup; - - static status_t analyzeResult(const EffectResult& result); - static void effectBufferConfigFromHal( - const buffer_config_t& halConfig, EffectBufferConfig* config); - static void effectBufferConfigToHal( - const EffectBufferConfig& config, buffer_config_t* halConfig); - static void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config); - static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig); - - // Can not be constructed directly by clients. - EffectHalHidl(const sp& effect, uint64_t effectId); - - // The destructor automatically releases the effect. - virtual ~EffectHalHidl(); - - status_t getConfigImpl(uint32_t cmdCode, uint32_t *replySize, void *pReplyData); - status_t prepareForProcessing(); - bool needToResetBuffers(); - status_t processImpl(uint32_t mqFlag); - status_t setConfigImpl( - uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, - uint32_t *replySize, void *pReplyData); - status_t setProcessBuffers(); -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/StreamHalHidl.cpp b/media/libaudiohal/2.0/StreamHalHidl.cpp deleted file mode 100644 index b6e83658fd..0000000000 --- a/media/libaudiohal/2.0/StreamHalHidl.cpp +++ /dev/null @@ -1,774 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "StreamHalHidl" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include - -#include "DeviceHalHidl.h" -#include "EffectHalHidl.h" -#include "StreamHalHidl.h" -#include "VersionUtils.h" - -using ::android::hardware::audio::common::V2_0::AudioChannelMask; -using ::android::hardware::audio::common::V2_0::AudioFormat; -using ::android::hardware::audio::common::V2_0::ThreadInfo; -using ::android::hardware::audio::V2_0::AudioDrain; -using ::android::hardware::audio::V2_0::IStreamOutCallback; -using ::android::hardware::audio::V2_0::MessageQueueFlagBits; -using ::android::hardware::audio::V2_0::MmapBufferInfo; -using ::android::hardware::audio::V2_0::MmapPosition; -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; -using ::android::hardware::audio::V2_0::TimeSpec; -using ::android::hardware::MQDescriptorSync; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ReadCommand = ::android::hardware::audio::V2_0::IStreamIn::ReadCommand; - -namespace android { -namespace V2_0 { - -StreamHalHidl::StreamHalHidl(IStream *stream) - : ConversionHelperHidl("Stream"), - mStream(stream), - mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT), - mCachedBufferSize(0){ - - // Instrument audio signal power logging. - // Note: This assumes channel mask, format, and sample rate do not change after creation. - if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { - // Obtain audio properties (see StreamHalHidl::getAudioProperties() below). - Return ret = mStream->getAudioProperties( - [&](auto sr, auto m, auto f) { - mStreamPowerLog.init(sr, - static_cast(m), - static_cast(f)); - }); - } -} - -StreamHalHidl::~StreamHalHidl() { - mStream = nullptr; -} - -status_t StreamHalHidl::getSampleRate(uint32_t *rate) { - if (!mStream) return NO_INIT; - return processReturn("getSampleRate", mStream->getSampleRate(), rate); -} - -status_t StreamHalHidl::getBufferSize(size_t *size) { - if (!mStream) return NO_INIT; - status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size); - if (status == OK) { - mCachedBufferSize = *size; - } - return status; -} - -status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) { - if (!mStream) return NO_INIT; - return processReturn("getChannelMask", mStream->getChannelMask(), mask); -} - -status_t StreamHalHidl::getFormat(audio_format_t *format) { - if (!mStream) return NO_INIT; - return processReturn("getFormat", mStream->getFormat(), format); -} - -status_t StreamHalHidl::getAudioProperties( - uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { - if (!mStream) return NO_INIT; - Return ret = mStream->getAudioProperties( - [&](uint32_t sr, auto m, auto f) { - *sampleRate = sr; - *mask = static_cast(m); - *format = static_cast(f); - }); - return processReturn("getAudioProperties", ret); -} - -status_t StreamHalHidl::setParameters(const String8& kvPairs) { - if (!mStream) return NO_INIT; - hidl_vec hidlParams; - status_t status = parametersFromHal(kvPairs, &hidlParams); - if (status != OK) return status; - return processReturn("setParameters", - utils::setParameters(mStream, hidlParams, {} /* options */)); -} - -status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { - values->clear(); - if (!mStream) return NO_INIT; - hidl_vec hidlKeys; - status_t status = keysFromHal(keys, &hidlKeys); - if (status != OK) return status; - Result retval; - Return ret = utils::getParameters( - mStream, - {} /* context */, - hidlKeys, - [&](Result r, const hidl_vec& parameters) { - retval = r; - if (retval == Result::OK) { - parametersToHal(parameters, values); - } - }); - return processReturn("getParameters", ret, retval); -} - -status_t StreamHalHidl::addEffect(sp effect) { - if (!mStream) return NO_INIT; - return processReturn("addEffect", mStream->addEffect( - static_cast(effect.get())->effectId())); -} - -status_t StreamHalHidl::removeEffect(sp effect) { - if (!mStream) return NO_INIT; - return processReturn("removeEffect", mStream->removeEffect( - static_cast(effect.get())->effectId())); -} - -status_t StreamHalHidl::standby() { - if (!mStream) return NO_INIT; - return processReturn("standby", mStream->standby()); -} - -status_t StreamHalHidl::dump(int fd) { - if (!mStream) return NO_INIT; - native_handle_t* hidlHandle = native_handle_create(1, 0); - hidlHandle->data[0] = fd; - Return ret = mStream->debug(hidlHandle, {} /* options */); - native_handle_delete(hidlHandle); - mStreamPowerLog.dump(fd); - return processReturn("dump", ret); -} - -status_t StreamHalHidl::start() { - if (!mStream) return NO_INIT; - return processReturn("start", mStream->start()); -} - -status_t StreamHalHidl::stop() { - if (!mStream) return NO_INIT; - return processReturn("stop", mStream->stop()); -} - -status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) { - Result retval; - Return ret = mStream->createMmapBuffer( - minSizeFrames, - [&](Result r, const MmapBufferInfo& hidlInfo) { - retval = r; - if (retval == Result::OK) { - const native_handle *handle = hidlInfo.sharedMemory.handle(); - if (handle->numFds > 0) { - info->shared_memory_fd = handle->data[0]; - info->buffer_size_frames = hidlInfo.bufferSizeFrames; - info->burst_size_frames = hidlInfo.burstSizeFrames; - // info->shared_memory_address is not needed in HIDL context - info->shared_memory_address = NULL; - } else { - retval = Result::NOT_INITIALIZED; - } - } - }); - return processReturn("createMmapBuffer", ret, retval); -} - -status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) { - Result retval; - Return ret = mStream->getMmapPosition( - [&](Result r, const MmapPosition& hidlPosition) { - retval = r; - if (retval == Result::OK) { - position->time_nanoseconds = hidlPosition.timeNanoseconds; - position->position_frames = hidlPosition.positionFrames; - } - }); - return processReturn("getMmapPosition", ret, retval); -} - -status_t StreamHalHidl::setHalThreadPriority(int priority) { - mHalThreadPriority = priority; - return OK; -} - -status_t StreamHalHidl::getCachedBufferSize(size_t *size) { - if (mCachedBufferSize != 0) { - *size = mCachedBufferSize; - return OK; - } - return getBufferSize(size); -} - -bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) { - if (mHalThreadPriority == HAL_THREAD_PRIORITY_DEFAULT) { - return true; - } - int err = requestPriority( - threadPid, threadId, - mHalThreadPriority, false /*isForApp*/, true /*asynchronous*/); - ALOGE_IF(err, "failed to set priority %d for pid %d tid %d; error %d", - mHalThreadPriority, threadPid, threadId, err); - // Audio will still work, but latency will be higher and sometimes unacceptable. - return err == 0; -} - -namespace { - -/* Notes on callback ownership. - -This is how (Hw)Binder ownership model looks like. The server implementation -is owned by Binder framework (via sp<>). Proxies are owned by clients. -When the last proxy disappears, Binder framework releases the server impl. - -Thus, it is not needed to keep any references to StreamOutCallback (this is -the server impl) -- it will live as long as HAL server holds a strong ref to -IStreamOutCallback proxy. We clear that reference by calling 'clearCallback' -from the destructor of StreamOutHalHidl. - -The callback only keeps a weak reference to the stream. The stream is owned -by AudioFlinger. - -*/ - -struct StreamOutCallback : public IStreamOutCallback { - StreamOutCallback(const wp& stream) : mStream(stream) {} - - // IStreamOutCallback implementation - Return onWriteReady() override { - sp stream = mStream.promote(); - if (stream != 0) { - stream->onWriteReady(); - } - return Void(); - } - - Return onDrainReady() override { - sp stream = mStream.promote(); - if (stream != 0) { - stream->onDrainReady(); - } - return Void(); - } - - Return onError() override { - sp stream = mStream.promote(); - if (stream != 0) { - stream->onError(); - } - return Void(); - } - - private: - wp mStream; -}; - -} // namespace - -StreamOutHalHidl::StreamOutHalHidl(const sp& stream) - : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) { -} - -StreamOutHalHidl::~StreamOutHalHidl() { - if (mStream != 0) { - if (mCallback.unsafe_get()) { - processReturn("clearCallback", mStream->clearCallback()); - } - processReturn("close", mStream->close()); - mStream.clear(); - } - mCallback.clear(); - hardware::IPCThreadState::self()->flushCommands(); - if (mEfGroup) { - EventFlag::deleteEventFlag(&mEfGroup); - } -} - -status_t StreamOutHalHidl::getFrameSize(size_t *size) { - if (mStream == 0) return NO_INIT; - return processReturn("getFrameSize", mStream->getFrameSize(), size); -} - -status_t StreamOutHalHidl::getLatency(uint32_t *latency) { - if (mStream == 0) return NO_INIT; - if (mWriterClient == gettid() && mCommandMQ) { - return callWriterThread( - WriteCommand::GET_LATENCY, "getLatency", nullptr, 0, - [&](const WriteStatus& writeStatus) { - *latency = writeStatus.reply.latencyMs; - }); - } else { - return processReturn("getLatency", mStream->getLatency(), latency); - } -} - -status_t StreamOutHalHidl::setVolume(float left, float right) { - if (mStream == 0) return NO_INIT; - return processReturn("setVolume", mStream->setVolume(left, right)); -} - -status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) { - if (mStream == 0) return NO_INIT; - *written = 0; - - if (bytes == 0 && !mDataMQ) { - // Can't determine the size for the MQ buffer. Wait for a non-empty write request. - ALOGW_IF(mCallback.unsafe_get(), "First call to async write with 0 bytes"); - return OK; - } - - status_t status; - if (!mDataMQ) { - // In case if playback starts close to the end of a compressed track, the bytes - // that need to be written is less than the actual buffer size. Need to use - // full buffer size for the MQ since otherwise after seeking back to the middle - // data will be truncated. - size_t bufferSize; - if ((status = getCachedBufferSize(&bufferSize)) != OK) { - return status; - } - if (bytes > bufferSize) bufferSize = bytes; - if ((status = prepareForWriting(bufferSize)) != OK) { - return status; - } - } - - status = callWriterThread( - WriteCommand::WRITE, "write", static_cast(buffer), bytes, - [&] (const WriteStatus& writeStatus) { - *written = writeStatus.reply.written; - // Diagnostics of the cause of b/35813113. - ALOGE_IF(*written > bytes, - "hal reports more bytes written than asked for: %lld > %lld", - (long long)*written, (long long)bytes); - }); - mStreamPowerLog.log(buffer, *written); - return status; -} - -status_t StreamOutHalHidl::callWriterThread( - WriteCommand cmd, const char* cmdName, - const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) { - if (!mCommandMQ->write(&cmd)) { - ALOGE("command message queue write failed for \"%s\"", cmdName); - return -EAGAIN; - } - if (data != nullptr) { - size_t availableToWrite = mDataMQ->availableToWrite(); - if (dataSize > availableToWrite) { - ALOGW("truncating write data from %lld to %lld due to insufficient data queue space", - (long long)dataSize, (long long)availableToWrite); - dataSize = availableToWrite; - } - if (!mDataMQ->write(data, dataSize)) { - ALOGE("data message queue write failed for \"%s\"", cmdName); - } - } - mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_EMPTY)); - - // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 - uint32_t efState = 0; -retry: - status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_FULL), &efState); - if (efState & static_cast(MessageQueueFlagBits::NOT_FULL)) { - WriteStatus writeStatus; - writeStatus.retval = Result::NOT_INITIALIZED; - if (!mStatusMQ->read(&writeStatus)) { - ALOGE("status message read failed for \"%s\"", cmdName); - } - if (writeStatus.retval == Result::OK) { - ret = OK; - callback(writeStatus); - } else { - ret = processReturn(cmdName, writeStatus.retval); - } - return ret; - } - if (ret == -EAGAIN || ret == -EINTR) { - // Spurious wakeup. This normally retries no more than once. - goto retry; - } - return ret; -} - -status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) { - std::unique_ptr tempCommandMQ; - std::unique_ptr tempDataMQ; - std::unique_ptr tempStatusMQ; - Result retval; - pid_t halThreadPid, halThreadTid; - Return ret = mStream->prepareForWriting( - 1, bufferSize, - [&](Result r, - const CommandMQ::Descriptor& commandMQ, - const DataMQ::Descriptor& dataMQ, - const StatusMQ::Descriptor& statusMQ, - const ThreadInfo& halThreadInfo) { - retval = r; - if (retval == Result::OK) { - tempCommandMQ.reset(new CommandMQ(commandMQ)); - tempDataMQ.reset(new DataMQ(dataMQ)); - tempStatusMQ.reset(new StatusMQ(statusMQ)); - if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { - EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); - } - halThreadPid = halThreadInfo.pid; - halThreadTid = halThreadInfo.tid; - } - }); - if (!ret.isOk() || retval != Result::OK) { - return processReturn("prepareForWriting", ret, retval); - } - if (!tempCommandMQ || !tempCommandMQ->isValid() || - !tempDataMQ || !tempDataMQ->isValid() || - !tempStatusMQ || !tempStatusMQ->isValid() || - !mEfGroup) { - ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing"); - ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), - "Command message queue for writing is invalid"); - ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing"); - ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid"); - ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing"); - ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), - "Status message queue for writing is invalid"); - ALOGE_IF(!mEfGroup, "Event flag creation for writing failed"); - return NO_INIT; - } - requestHalThreadPriority(halThreadPid, halThreadTid); - - mCommandMQ = std::move(tempCommandMQ); - mDataMQ = std::move(tempDataMQ); - mStatusMQ = std::move(tempStatusMQ); - mWriterClient = gettid(); - return OK; -} - -status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) { - if (mStream == 0) return NO_INIT; - Result retval; - Return ret = mStream->getRenderPosition( - [&](Result r, uint32_t d) { - retval = r; - if (retval == Result::OK) { - *dspFrames = d; - } - }); - return processReturn("getRenderPosition", ret, retval); -} - -status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) { - if (mStream == 0) return NO_INIT; - Result retval; - Return ret = mStream->getNextWriteTimestamp( - [&](Result r, int64_t t) { - retval = r; - if (retval == Result::OK) { - *timestamp = t; - } - }); - return processReturn("getRenderPosition", ret, retval); -} - -status_t StreamOutHalHidl::setCallback(wp callback) { - if (mStream == 0) return NO_INIT; - status_t status = processReturn( - "setCallback", mStream->setCallback(new StreamOutCallback(this))); - if (status == OK) { - mCallback = callback; - } - return status; -} - -status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { - if (mStream == 0) return NO_INIT; - Return ret = mStream->supportsPauseAndResume( - [&](bool p, bool r) { - *supportsPause = p; - *supportsResume = r; - }); - return processReturn("supportsPauseAndResume", ret); -} - -status_t StreamOutHalHidl::pause() { - if (mStream == 0) return NO_INIT; - return processReturn("pause", mStream->pause()); -} - -status_t StreamOutHalHidl::resume() { - if (mStream == 0) return NO_INIT; - return processReturn("pause", mStream->resume()); -} - -status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) { - if (mStream == 0) return NO_INIT; - return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain); -} - -status_t StreamOutHalHidl::drain(bool earlyNotify) { - if (mStream == 0) return NO_INIT; - return processReturn( - "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL)); -} - -status_t StreamOutHalHidl::flush() { - if (mStream == 0) return NO_INIT; - return processReturn("pause", mStream->flush()); -} - -status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { - if (mStream == 0) return NO_INIT; - if (mWriterClient == gettid() && mCommandMQ) { - return callWriterThread( - WriteCommand::GET_PRESENTATION_POSITION, "getPresentationPosition", nullptr, 0, - [&](const WriteStatus& writeStatus) { - *frames = writeStatus.reply.presentationPosition.frames; - timestamp->tv_sec = writeStatus.reply.presentationPosition.timeStamp.tvSec; - timestamp->tv_nsec = writeStatus.reply.presentationPosition.timeStamp.tvNSec; - }); - } else { - Result retval; - Return ret = mStream->getPresentationPosition( - [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) { - retval = r; - if (retval == Result::OK) { - *frames = hidlFrames; - timestamp->tv_sec = hidlTimeStamp.tvSec; - timestamp->tv_nsec = hidlTimeStamp.tvNSec; - } - }); - return processReturn("getPresentationPosition", ret, retval); - } -} - -status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& /* sourceMetadata */) { - // Audio HAL V2.0 does not support propagating source metadata - return INVALID_OPERATION; -} - -void StreamOutHalHidl::onWriteReady() { - sp callback = mCallback.promote(); - if (callback == 0) return; - ALOGV("asyncCallback onWriteReady"); - callback->onWriteReady(); -} - -void StreamOutHalHidl::onDrainReady() { - sp callback = mCallback.promote(); - if (callback == 0) return; - ALOGV("asyncCallback onDrainReady"); - callback->onDrainReady(); -} - -void StreamOutHalHidl::onError() { - sp callback = mCallback.promote(); - if (callback == 0) return; - ALOGV("asyncCallback onError"); - callback->onError(); -} - - -StreamInHalHidl::StreamInHalHidl(const sp& stream) - : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) { -} - -StreamInHalHidl::~StreamInHalHidl() { - if (mStream != 0) { - processReturn("close", mStream->close()); - mStream.clear(); - hardware::IPCThreadState::self()->flushCommands(); - } - if (mEfGroup) { - EventFlag::deleteEventFlag(&mEfGroup); - } -} - -status_t StreamInHalHidl::getFrameSize(size_t *size) { - if (mStream == 0) return NO_INIT; - return processReturn("getFrameSize", mStream->getFrameSize(), size); -} - -status_t StreamInHalHidl::setGain(float gain) { - if (mStream == 0) return NO_INIT; - return processReturn("setGain", mStream->setGain(gain)); -} - -status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) { - if (mStream == 0) return NO_INIT; - *read = 0; - - if (bytes == 0 && !mDataMQ) { - // Can't determine the size for the MQ buffer. Wait for a non-empty read request. - return OK; - } - - status_t status; - if (!mDataMQ && (status = prepareForReading(bytes)) != OK) { - return status; - } - - ReadParameters params; - params.command = ReadCommand::READ; - params.params.read = bytes; - status = callReaderThread(params, "read", - [&](const ReadStatus& readStatus) { - const size_t availToRead = mDataMQ->availableToRead(); - if (!mDataMQ->read(static_cast(buffer), std::min(bytes, availToRead))) { - ALOGE("data message queue read failed for \"read\""); - } - ALOGW_IF(availToRead != readStatus.reply.read, - "HAL read report inconsistent: mq = %d, status = %d", - (int32_t)availToRead, (int32_t)readStatus.reply.read); - *read = readStatus.reply.read; - }); - mStreamPowerLog.log(buffer, *read); - return status; -} - -status_t StreamInHalHidl::callReaderThread( - const ReadParameters& params, const char* cmdName, - StreamInHalHidl::ReaderCallback callback) { - if (!mCommandMQ->write(¶ms)) { - ALOGW("command message queue write failed"); - return -EAGAIN; - } - mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_FULL)); - - // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 - uint32_t efState = 0; -retry: - status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_EMPTY), &efState); - if (efState & static_cast(MessageQueueFlagBits::NOT_EMPTY)) { - ReadStatus readStatus; - readStatus.retval = Result::NOT_INITIALIZED; - if (!mStatusMQ->read(&readStatus)) { - ALOGE("status message read failed for \"%s\"", cmdName); - } - if (readStatus.retval == Result::OK) { - ret = OK; - callback(readStatus); - } else { - ret = processReturn(cmdName, readStatus.retval); - } - return ret; - } - if (ret == -EAGAIN || ret == -EINTR) { - // Spurious wakeup. This normally retries no more than once. - goto retry; - } - return ret; -} - -status_t StreamInHalHidl::prepareForReading(size_t bufferSize) { - std::unique_ptr tempCommandMQ; - std::unique_ptr tempDataMQ; - std::unique_ptr tempStatusMQ; - Result retval; - pid_t halThreadPid, halThreadTid; - Return ret = mStream->prepareForReading( - 1, bufferSize, - [&](Result r, - const CommandMQ::Descriptor& commandMQ, - const DataMQ::Descriptor& dataMQ, - const StatusMQ::Descriptor& statusMQ, - const ThreadInfo& halThreadInfo) { - retval = r; - if (retval == Result::OK) { - tempCommandMQ.reset(new CommandMQ(commandMQ)); - tempDataMQ.reset(new DataMQ(dataMQ)); - tempStatusMQ.reset(new StatusMQ(statusMQ)); - if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { - EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); - } - halThreadPid = halThreadInfo.pid; - halThreadTid = halThreadInfo.tid; - } - }); - if (!ret.isOk() || retval != Result::OK) { - return processReturn("prepareForReading", ret, retval); - } - if (!tempCommandMQ || !tempCommandMQ->isValid() || - !tempDataMQ || !tempDataMQ->isValid() || - !tempStatusMQ || !tempStatusMQ->isValid() || - !mEfGroup) { - ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing"); - ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), - "Command message queue for writing is invalid"); - ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading"); - ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid"); - ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading"); - ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), - "Status message queue for reading is invalid"); - ALOGE_IF(!mEfGroup, "Event flag creation for reading failed"); - return NO_INIT; - } - requestHalThreadPriority(halThreadPid, halThreadTid); - - mCommandMQ = std::move(tempCommandMQ); - mDataMQ = std::move(tempDataMQ); - mStatusMQ = std::move(tempStatusMQ); - mReaderClient = gettid(); - return OK; -} - -status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) { - if (mStream == 0) return NO_INIT; - return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost); -} - -status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { - if (mStream == 0) return NO_INIT; - if (mReaderClient == gettid() && mCommandMQ) { - ReadParameters params; - params.command = ReadCommand::GET_CAPTURE_POSITION; - return callReaderThread(params, "getCapturePosition", - [&](const ReadStatus& readStatus) { - *frames = readStatus.reply.capturePosition.frames; - *time = readStatus.reply.capturePosition.time; - }); - } else { - Result retval; - Return ret = mStream->getCapturePosition( - [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) { - retval = r; - if (retval == Result::OK) { - *frames = hidlFrames; - *time = hidlTime; - } - }); - return processReturn("getCapturePosition", ret, retval); - } -} - -status_t StreamInHalHidl::getActiveMicrophones( - std::vector *microphones __unused) { - if (mStream == 0) return NO_INIT; - return INVALID_OPERATION; -} - -status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& /* sinkMetadata */) { - // Audio HAL V2.0 does not support propagating sink metadata - return INVALID_OPERATION; -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/StreamHalHidl.h b/media/libaudiohal/2.0/StreamHalHidl.h deleted file mode 100644 index 872762088d..0000000000 --- a/media/libaudiohal/2.0/StreamHalHidl.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_STREAM_HAL_HIDL_H -#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H - -#include - -#include -#include -#include -#include -#include -#include - -#include "ConversionHelperHidl.h" -#include "StreamPowerLog.h" - -using ::android::hardware::audio::V2_0::IStream; -using ::android::hardware::audio::V2_0::IStreamIn; -using ::android::hardware::audio::V2_0::IStreamOut; -using ::android::hardware::EventFlag; -using ::android::hardware::MessageQueue; -using ::android::hardware::Return; -using ReadParameters = ::android::hardware::audio::V2_0::IStreamIn::ReadParameters; -using ReadStatus = ::android::hardware::audio::V2_0::IStreamIn::ReadStatus; -using WriteCommand = ::android::hardware::audio::V2_0::IStreamOut::WriteCommand; -using WriteStatus = ::android::hardware::audio::V2_0::IStreamOut::WriteStatus; - -namespace android { -namespace V2_0 { - -class DeviceHalHidl; - -class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl -{ - public: - // Return the sampling rate in Hz - eg. 44100. - virtual status_t getSampleRate(uint32_t *rate); - - // Return size of input/output buffer in bytes for this stream - eg. 4800. - virtual status_t getBufferSize(size_t *size); - - // Return the channel mask. - virtual status_t getChannelMask(audio_channel_mask_t *mask); - - // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT. - virtual status_t getFormat(audio_format_t *format); - - // Convenience method. - virtual status_t getAudioProperties( - uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format); - - // Set audio stream parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get audio stream parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Add or remove the effect on the stream. - virtual status_t addEffect(sp effect); - virtual status_t removeEffect(sp effect); - - // Put the audio hardware input/output into standby mode. - virtual status_t standby(); - - virtual status_t dump(int fd); - - // Start a stream operating in mmap mode. - virtual status_t start(); - - // Stop a stream operating in mmap mode. - virtual status_t stop(); - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info); - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position); - - // Set the priority of the thread that interacts with the HAL - // (must match the priority of the audioflinger's thread that calls 'read' / 'write') - virtual status_t setHalThreadPriority(int priority); - - protected: - // Subclasses can not be constructed directly by clients. - explicit StreamHalHidl(IStream *stream); - - // The destructor automatically closes the stream. - virtual ~StreamHalHidl(); - - status_t getCachedBufferSize(size_t *size); - - bool requestHalThreadPriority(pid_t threadPid, pid_t threadId); - - // mStreamPowerLog is used for audio signal power logging. - StreamPowerLog mStreamPowerLog; - - private: - const int HAL_THREAD_PRIORITY_DEFAULT = -1; - IStream *mStream; - int mHalThreadPriority; - size_t mCachedBufferSize; -}; - -class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Return the audio hardware driver estimated latency in milliseconds. - virtual status_t getLatency(uint32_t *latency); - - // Use this method in situations where audio mixing is done in the hardware. - virtual status_t setVolume(float left, float right); - - // Write audio buffer to driver. - virtual status_t write(const void *buffer, size_t bytes, size_t *written); - - // Return the number of audio frames written by the audio dsp to DAC since - // the output has exited standby. - virtual status_t getRenderPosition(uint32_t *dspFrames); - - // Get the local time at which the next write to the audio driver will be presented. - virtual status_t getNextWriteTimestamp(int64_t *timestamp); - - // Set the callback for notifying completion of non-blocking write and drain. - virtual status_t setCallback(wp callback); - - // Returns whether pause and resume operations are supported. - virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t pause(); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t resume(); - - // Returns whether drain operation is supported. - virtual status_t supportsDrain(bool *supportsDrain); - - // Requests notification when data buffered by the driver/hardware has been played. - virtual status_t drain(bool earlyNotify); - - // Notifies to the audio driver to flush the queued data. - virtual status_t flush(); - - // Return a recent count of the number of audio frames presented to an external observer. - virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); - - // Called when the metadata of the stream's source has been changed. - status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) override; - - // Methods used by StreamOutCallback (HIDL). - void onWriteReady(); - void onDrainReady(); - void onError(); - - private: - friend class DeviceHalHidl; - typedef MessageQueue CommandMQ; - typedef MessageQueue DataMQ; - typedef MessageQueue StatusMQ; - - wp mCallback; - sp mStream; - std::unique_ptr mCommandMQ; - std::unique_ptr mDataMQ; - std::unique_ptr mStatusMQ; - std::atomic mWriterClient; - EventFlag* mEfGroup; - - // Can not be constructed directly by clients. - StreamOutHalHidl(const sp& stream); - - virtual ~StreamOutHalHidl(); - - using WriterCallback = std::function; - status_t callWriterThread( - WriteCommand cmd, const char* cmdName, - const uint8_t* data, size_t dataSize, WriterCallback callback); - status_t prepareForWriting(size_t bufferSize); -}; - -class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Set the input gain for the audio driver. - virtual status_t setGain(float gain); - - // Read audio buffer in from driver. - virtual status_t read(void *buffer, size_t bytes, size_t *read); - - // Return the amount of input frames lost in the audio driver. - virtual status_t getInputFramesLost(uint32_t *framesLost); - - // Return a recent count of the number of audio frames received and - // the clock time associated with that frame count. - virtual status_t getCapturePosition(int64_t *frames, int64_t *time); - - // Get active microphones - virtual status_t getActiveMicrophones(std::vector *microphones); - - // Called when the metadata of the stream's sink has been changed. - status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; - - private: - friend class DeviceHalHidl; - typedef MessageQueue CommandMQ; - typedef MessageQueue DataMQ; - typedef MessageQueue StatusMQ; - - sp mStream; - std::unique_ptr mCommandMQ; - std::unique_ptr mDataMQ; - std::unique_ptr mStatusMQ; - std::atomic mReaderClient; - EventFlag* mEfGroup; - - // Can not be constructed directly by clients. - StreamInHalHidl(const sp& stream); - - virtual ~StreamInHalHidl(); - - using ReaderCallback = std::function; - status_t callReaderThread( - const ReadParameters& params, const char* cmdName, ReaderCallback callback); - status_t prepareForReading(size_t bufferSize); -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/StreamHalLocal.cpp b/media/libaudiohal/2.0/StreamHalLocal.cpp deleted file mode 100644 index 7951f79b13..0000000000 --- a/media/libaudiohal/2.0/StreamHalLocal.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "StreamHalLocal" -//#define LOG_NDEBUG 0 - -#include -#include - -#include "DeviceHalLocal.h" -#include "StreamHalLocal.h" -#include "VersionUtils.h" - -namespace android { -namespace V2_0 { - -StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) - : mDevice(device), - mStream(stream) { - // Instrument audio signal power logging. - // Note: This assumes channel mask, format, and sample rate do not change after creation. - if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { - mStreamPowerLog.init(mStream->get_sample_rate(mStream), - mStream->get_channels(mStream), - mStream->get_format(mStream)); - } -} - -StreamHalLocal::~StreamHalLocal() { - mStream = 0; - mDevice.clear(); -} - -status_t StreamHalLocal::getSampleRate(uint32_t *rate) { - *rate = mStream->get_sample_rate(mStream); - return OK; -} - -status_t StreamHalLocal::getBufferSize(size_t *size) { - *size = mStream->get_buffer_size(mStream); - return OK; -} - -status_t StreamHalLocal::getChannelMask(audio_channel_mask_t *mask) { - *mask = mStream->get_channels(mStream); - return OK; -} - -status_t StreamHalLocal::getFormat(audio_format_t *format) { - *format = mStream->get_format(mStream); - return OK; -} - -status_t StreamHalLocal::getAudioProperties( - uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { - *sampleRate = mStream->get_sample_rate(mStream); - *mask = mStream->get_channels(mStream); - *format = mStream->get_format(mStream); - return OK; -} - -status_t StreamHalLocal::setParameters(const String8& kvPairs) { - return mStream->set_parameters(mStream, kvPairs.string()); -} - -status_t StreamHalLocal::getParameters(const String8& keys, String8 *values) { - char *halValues = mStream->get_parameters(mStream, keys.string()); - if (halValues != NULL) { - values->setTo(halValues); - free(halValues); - } else { - values->clear(); - } - return OK; -} - -status_t StreamHalLocal::addEffect(sp) { - LOG_ALWAYS_FATAL("Local streams can not have effects"); - return INVALID_OPERATION; -} - -status_t StreamHalLocal::removeEffect(sp) { - LOG_ALWAYS_FATAL("Local streams can not have effects"); - return INVALID_OPERATION; -} - -status_t StreamHalLocal::standby() { - return mStream->standby(mStream); -} - -status_t StreamHalLocal::dump(int fd) { - status_t status = mStream->dump(mStream, fd); - mStreamPowerLog.dump(fd); - return status; -} - -status_t StreamHalLocal::setHalThreadPriority(int) { - // Don't need to do anything as local hal is executed by audioflinger directly - // on the same thread. - return OK; -} - -StreamOutHalLocal::StreamOutHalLocal(audio_stream_out_t *stream, sp device) - : StreamHalLocal(&stream->common, device), mStream(stream) { -} - -StreamOutHalLocal::~StreamOutHalLocal() { - mCallback.clear(); - mDevice->closeOutputStream(mStream); - mStream = 0; -} - -status_t StreamOutHalLocal::getFrameSize(size_t *size) { - *size = audio_stream_out_frame_size(mStream); - return OK; -} - -status_t StreamOutHalLocal::getLatency(uint32_t *latency) { - *latency = mStream->get_latency(mStream); - return OK; -} - -status_t StreamOutHalLocal::setVolume(float left, float right) { - if (mStream->set_volume == NULL) return INVALID_OPERATION; - return mStream->set_volume(mStream, left, right); -} - -status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) { - ssize_t writeResult = mStream->write(mStream, buffer, bytes); - if (writeResult > 0) { - *written = writeResult; - mStreamPowerLog.log(buffer, *written); - return OK; - } else { - *written = 0; - return writeResult; - } -} - -status_t StreamOutHalLocal::getRenderPosition(uint32_t *dspFrames) { - return mStream->get_render_position(mStream, dspFrames); -} - -status_t StreamOutHalLocal::getNextWriteTimestamp(int64_t *timestamp) { - if (mStream->get_next_write_timestamp == NULL) return INVALID_OPERATION; - return mStream->get_next_write_timestamp(mStream, timestamp); -} - -status_t StreamOutHalLocal::setCallback(wp callback) { - if (mStream->set_callback == NULL) return INVALID_OPERATION; - status_t result = mStream->set_callback(mStream, StreamOutHalLocal::asyncCallback, this); - if (result == OK) { - mCallback = callback; - } - return result; -} - -// static -int StreamOutHalLocal::asyncCallback(stream_callback_event_t event, void*, void *cookie) { - // We act as if we gave a wp to HAL. This way we should handle - // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is - // already running, because the destructor is invoked after the refcount has been atomically - // decremented. - wp weakSelf(static_cast(cookie)); - sp self = weakSelf.promote(); - if (self == 0) return 0; - sp callback = self->mCallback.promote(); - if (callback == 0) return 0; - ALOGV("asyncCallback() event %d", event); - switch (event) { - case STREAM_CBK_EVENT_WRITE_READY: - callback->onWriteReady(); - break; - case STREAM_CBK_EVENT_DRAIN_READY: - callback->onDrainReady(); - break; - case STREAM_CBK_EVENT_ERROR: - callback->onError(); - break; - default: - ALOGW("asyncCallback() unknown event %d", event); - break; - } - return 0; -} - -status_t StreamOutHalLocal::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { - *supportsPause = mStream->pause != NULL; - *supportsResume = mStream->resume != NULL; - return OK; -} - -status_t StreamOutHalLocal::pause() { - if (mStream->pause == NULL) return INVALID_OPERATION; - return mStream->pause(mStream); -} - -status_t StreamOutHalLocal::resume() { - if (mStream->resume == NULL) return INVALID_OPERATION; - return mStream->resume(mStream); -} - -status_t StreamOutHalLocal::supportsDrain(bool *supportsDrain) { - *supportsDrain = mStream->drain != NULL; - return OK; -} - -status_t StreamOutHalLocal::drain(bool earlyNotify) { - if (mStream->drain == NULL) return INVALID_OPERATION; - return mStream->drain(mStream, earlyNotify ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL); -} - -status_t StreamOutHalLocal::flush() { - if (mStream->flush == NULL) return INVALID_OPERATION; - return mStream->flush(mStream); -} - -status_t StreamOutHalLocal::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { - if (mStream->get_presentation_position == NULL) return INVALID_OPERATION; - return mStream->get_presentation_position(mStream, frames, timestamp); -} - -status_t StreamOutHalLocal::updateSourceMetadata(const SourceMetadata& sourceMetadata) { - if (mStream->update_source_metadata == nullptr) { - return INVALID_OPERATION; - } - const source_metadata_t metadata { - .track_count = sourceMetadata.tracks.size(), - // const cast is fine as it is in a const structure - .tracks = const_cast(sourceMetadata.tracks.data()), - }; - mStream->update_source_metadata(mStream, &metadata); - return OK; -} - -status_t StreamOutHalLocal::start() { - if (mStream->start == NULL) return INVALID_OPERATION; - return mStream->start(mStream); -} - -status_t StreamOutHalLocal::stop() { - if (mStream->stop == NULL) return INVALID_OPERATION; - return mStream->stop(mStream); -} - -status_t StreamOutHalLocal::createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) { - if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; - return mStream->create_mmap_buffer(mStream, minSizeFrames, info); -} - -status_t StreamOutHalLocal::getMmapPosition(struct audio_mmap_position *position) { - if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; - return mStream->get_mmap_position(mStream, position); -} - -StreamInHalLocal::StreamInHalLocal(audio_stream_in_t *stream, sp device) - : StreamHalLocal(&stream->common, device), mStream(stream) { -} - -StreamInHalLocal::~StreamInHalLocal() { - mDevice->closeInputStream(mStream); - mStream = 0; -} - -status_t StreamInHalLocal::getFrameSize(size_t *size) { - *size = audio_stream_in_frame_size(mStream); - return OK; -} - -status_t StreamInHalLocal::setGain(float gain) { - return mStream->set_gain(mStream, gain); -} - -status_t StreamInHalLocal::read(void *buffer, size_t bytes, size_t *read) { - ssize_t readResult = mStream->read(mStream, buffer, bytes); - if (readResult > 0) { - *read = readResult; - mStreamPowerLog.log( buffer, *read); - return OK; - } else { - *read = 0; - return readResult; - } -} - -status_t StreamInHalLocal::getInputFramesLost(uint32_t *framesLost) { - *framesLost = mStream->get_input_frames_lost(mStream); - return OK; -} - -status_t StreamInHalLocal::getCapturePosition(int64_t *frames, int64_t *time) { - if (mStream->get_capture_position == NULL) return INVALID_OPERATION; - return mStream->get_capture_position(mStream, frames, time); -} - -status_t StreamInHalLocal::updateSinkMetadata(const SinkMetadata& sinkMetadata) { - if (mStream->update_sink_metadata == nullptr) { - return INVALID_OPERATION; - } - const sink_metadata_t metadata { - .track_count = sinkMetadata.tracks.size(), - // const cast is fine as it is in a const structure - .tracks = const_cast(sinkMetadata.tracks.data()), - }; - mStream->update_sink_metadata(mStream, &metadata); - return OK; -} - -status_t StreamInHalLocal::start() { - if (mStream->start == NULL) return INVALID_OPERATION; - return mStream->start(mStream); -} - -status_t StreamInHalLocal::stop() { - if (mStream->stop == NULL) return INVALID_OPERATION; - return mStream->stop(mStream); -} - -status_t StreamInHalLocal::createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) { - if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; - return mStream->create_mmap_buffer(mStream, minSizeFrames, info); -} - -status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) { - if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; - return mStream->get_mmap_position(mStream, position); -} - -status_t StreamInHalLocal::getActiveMicrophones( - std::vector *microphones __unused) { - return INVALID_OPERATION; -} - -} // namespace V2_0 -} // namespace android diff --git a/media/libaudiohal/2.0/StreamHalLocal.h b/media/libaudiohal/2.0/StreamHalLocal.h deleted file mode 100644 index a4a71e1745..0000000000 --- a/media/libaudiohal/2.0/StreamHalLocal.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_STREAM_HAL_LOCAL_H -#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_H - -#include -#include "StreamPowerLog.h" - -namespace android { -namespace V2_0 { - -class DeviceHalLocal; - -class StreamHalLocal : public virtual StreamHalInterface -{ - public: - // Return the sampling rate in Hz - eg. 44100. - virtual status_t getSampleRate(uint32_t *rate); - - // Return size of input/output buffer in bytes for this stream - eg. 4800. - virtual status_t getBufferSize(size_t *size); - - // Return the channel mask. - virtual status_t getChannelMask(audio_channel_mask_t *mask); - - // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT. - virtual status_t getFormat(audio_format_t *format); - - // Convenience method. - virtual status_t getAudioProperties( - uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format); - - // Set audio stream parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get audio stream parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Add or remove the effect on the stream. - virtual status_t addEffect(sp effect); - virtual status_t removeEffect(sp effect); - - // Put the audio hardware input/output into standby mode. - virtual status_t standby(); - - virtual status_t dump(int fd); - - // Start a stream operating in mmap mode. - virtual status_t start() = 0; - - // Stop a stream operating in mmap mode. - virtual status_t stop() = 0; - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) = 0; - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0; - - // Set the priority of the thread that interacts with the HAL - // (must match the priority of the audioflinger's thread that calls 'read' / 'write') - virtual status_t setHalThreadPriority(int priority); - - protected: - // Subclasses can not be constructed directly by clients. - StreamHalLocal(audio_stream_t *stream, sp device); - - // The destructor automatically closes the stream. - virtual ~StreamHalLocal(); - - sp mDevice; - - // mStreamPowerLog is used for audio signal power logging. - StreamPowerLog mStreamPowerLog; - - private: - audio_stream_t *mStream; -}; - -class StreamOutHalLocal : public StreamOutHalInterface, public StreamHalLocal { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Return the audio hardware driver estimated latency in milliseconds. - virtual status_t getLatency(uint32_t *latency); - - // Use this method in situations where audio mixing is done in the hardware. - virtual status_t setVolume(float left, float right); - - // Write audio buffer to driver. - virtual status_t write(const void *buffer, size_t bytes, size_t *written); - - // Return the number of audio frames written by the audio dsp to DAC since - // the output has exited standby. - virtual status_t getRenderPosition(uint32_t *dspFrames); - - // Get the local time at which the next write to the audio driver will be presented. - virtual status_t getNextWriteTimestamp(int64_t *timestamp); - - // Set the callback for notifying completion of non-blocking write and drain. - virtual status_t setCallback(wp callback); - - // Returns whether pause and resume operations are supported. - virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t pause(); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t resume(); - - // Returns whether drain operation is supported. - virtual status_t supportsDrain(bool *supportsDrain); - - // Requests notification when data buffered by the driver/hardware has been played. - virtual status_t drain(bool earlyNotify); - - // Notifies to the audio driver to flush the queued data. - virtual status_t flush(); - - // Return a recent count of the number of audio frames presented to an external observer. - virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); - - // Start a stream operating in mmap mode. - virtual status_t start(); - - // Stop a stream operating in mmap mode. - virtual status_t stop(); - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info); - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position); - - // Called when the metadata of the stream's source has been changed. - status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) override; - - private: - audio_stream_out_t *mStream; - wp mCallback; - - friend class DeviceHalLocal; - - // Can not be constructed directly by clients. - StreamOutHalLocal(audio_stream_out_t *stream, sp device); - - virtual ~StreamOutHalLocal(); - - static int asyncCallback(stream_callback_event_t event, void *param, void *cookie); -}; - -class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Set the input gain for the audio driver. - virtual status_t setGain(float gain); - - // Read audio buffer in from driver. - virtual status_t read(void *buffer, size_t bytes, size_t *read); - - // Return the amount of input frames lost in the audio driver. - virtual status_t getInputFramesLost(uint32_t *framesLost); - - // Return a recent count of the number of audio frames received and - // the clock time associated with that frame count. - virtual status_t getCapturePosition(int64_t *frames, int64_t *time); - - // Start a stream operating in mmap mode. - virtual status_t start(); - - // Stop a stream operating in mmap mode. - virtual status_t stop(); - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info); - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position); - - // Get active microphones - virtual status_t getActiveMicrophones(std::vector *microphones); - - // Called when the metadata of the stream's sink has been changed. - status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; - - private: - audio_stream_in_t *mStream; - - friend class DeviceHalLocal; - - // Can not be constructed directly by clients. - StreamInHalLocal(audio_stream_in_t *stream, sp device); - - virtual ~StreamInHalLocal(); -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/StreamPowerLog.h b/media/libaudiohal/2.0/StreamPowerLog.h deleted file mode 100644 index db2f0689d5..0000000000 --- a/media/libaudiohal/2.0/StreamPowerLog.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HARDWARE_STREAM_POWER_LOG_H -#define ANDROID_HARDWARE_STREAM_POWER_LOG_H - -#include -#include -#include -#include - -namespace android { -namespace V2_0 { - -class StreamPowerLog { -public: - StreamPowerLog() : - mIsUserDebugOrEngBuild(is_userdebug_or_eng_build()), - mPowerLog(nullptr), - mFrameSize(0) { - // use init() to set up the power log. - } - - ~StreamPowerLog() { - power_log_destroy(mPowerLog); // OK for null mPowerLog - mPowerLog = nullptr; - } - - // A one-time initialization (do not call twice) before using StreamPowerLog. - void init(uint32_t sampleRate, audio_channel_mask_t channelMask, audio_format_t format) { - if (mPowerLog == nullptr) { - // Note: A way to get channel count for both input and output channel masks - // but does not check validity of the channel mask. - const uint32_t channelCount = popcount(audio_channel_mask_get_bits(channelMask)); - mFrameSize = channelCount * audio_bytes_per_sample(format); - if (mFrameSize > 0) { - const size_t kPowerLogFramesPerEntry = - (long long)sampleRate * kPowerLogSamplingIntervalMs / 1000; - mPowerLog = power_log_create( - sampleRate, - channelCount, - format, - kPowerLogEntries, - kPowerLogFramesPerEntry); - } - } - // mPowerLog may be NULL (not the right build, format not accepted, etc.). - } - - // Dump the power log to fd. - void dump(int fd) const { - // OK for null mPowerLog - (void)power_log_dump( - mPowerLog, fd, " " /* prefix */, kPowerLogLines, 0 /* limit_ns */); - } - - // Log the audio data contained in buffer. - void log(const void *buffer, size_t sizeInBytes) const { - if (mPowerLog != nullptr) { // mFrameSize is always nonzero if mPowerLog exists. - power_log_log( - mPowerLog, buffer, sizeInBytes / mFrameSize, audio_utils_get_real_time_ns()); - } - } - - bool isUserDebugOrEngBuild() const { - return mIsUserDebugOrEngBuild; - } - -private: - - static inline bool is_userdebug_or_eng_build() { - char value[PROPERTY_VALUE_MAX]; - (void)property_get("ro.build.type", value, "unknown"); // ignore actual length - return strcmp(value, "userdebug") == 0 || strcmp(value, "eng") == 0; - } - - // Audio signal power log configuration. - static const size_t kPowerLogLines = 40; - static const size_t kPowerLogSamplingIntervalMs = 50; - static const size_t kPowerLogEntries = (1 /* minutes */ * 60 /* seconds */ * 1000 /* msec */ - / kPowerLogSamplingIntervalMs); - - const bool mIsUserDebugOrEngBuild; - power_log_t *mPowerLog; - size_t mFrameSize; -}; - -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H diff --git a/media/libaudiohal/2.0/VersionUtils.h b/media/libaudiohal/2.0/VersionUtils.h deleted file mode 100644 index 54e5ae02be..0000000000 --- a/media/libaudiohal/2.0/VersionUtils.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_HARDWARE_VERSION_UTILS_H -#define ANDROID_HARDWARE_VERSION_UTILS_H - -#include -#include - -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; -using ::android::hardware::Return; -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_string; - -namespace android { -namespace V2_0 { -namespace utils { - -template -Return getParameters(T& object, hidl_vec /*context*/, - hidl_vec keys, Callback callback) { - return object->getParameters(keys, callback); -} - -template -Return setParameters(T& object, hidl_vec /*context*/, - hidl_vec keys) { - return object->setParameters(keys); -} - -} // namespace utils -} // namespace V2_0 -} // namespace android - -#endif // ANDROID_HARDWARE_VERSION_UTILS_H diff --git a/media/libaudiohal/4.0/Android.bp b/media/libaudiohal/4.0/Android.bp deleted file mode 100644 index 2fe0a17ff8..0000000000 --- a/media/libaudiohal/4.0/Android.bp +++ /dev/null @@ -1,65 +0,0 @@ -cc_defaults { - name: "libaudiohal@4.0_default", - - srcs: [ - "DeviceHalLocal.cpp", - "DevicesFactoryHalHybrid.cpp", - "DevicesFactoryHalLocal.cpp", - "StreamHalLocal.cpp", - - "ConversionHelperHidl.cpp", - "DeviceHalHidl.cpp", - "DevicesFactoryHalHidl.cpp", - "EffectBufferHalHidl.cpp", - "EffectHalHidl.cpp", - "EffectsFactoryHalHidl.cpp", - "StreamHalHidl.cpp", - ], - - export_include_dirs: ["include"], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - ], - shared_libs: [ - "libaudiohal_deathhandler", - "libaudioutils", - "libbinder", - "libcutils", - "liblog", - "libutils", - "libhardware", - "libbase", - "libfmq", - "libhwbinder", - "libhidlbase", - "libhidlmemory", - "libhidltransport", - "android.hardware.audio.common-util", - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", - "libmedia_helper", - "libmediautils", - ], - header_libs: [ - "android.hardware.audio.common.util@all-versions", - "libaudiohal_headers" - ], - - export_shared_lib_headers: [ - "libfmq", - ], -} - -cc_library_shared { - name: "libaudiohal@4.0", - defaults: ["libaudiohal@4.0_default"], - shared_libs: [ - "android.hardware.audio@4.0", - "android.hardware.audio.common@4.0", - "android.hardware.audio.common@4.0-util", - "android.hardware.audio.effect@4.0", - ], -} diff --git a/media/libaudiohal/4.0/DeviceHalHidl.h b/media/libaudiohal/4.0/DeviceHalHidl.h deleted file mode 100644 index 5c48f1253b..0000000000 --- a/media/libaudiohal/4.0/DeviceHalHidl.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_DEVICE_HAL_HIDL_H -#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H - -#include -#include -#include - -#include "ConversionHelperHidl.h" - -using ::android::hardware::audio::V4_0::IDevice; -using ::android::hardware::audio::V4_0::IPrimaryDevice; -using ::android::hardware::Return; - -namespace android { -namespace V4_0 { - -class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl -{ - public: - // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. - virtual status_t getSupportedDevices(uint32_t *devices); - - // Check to see if the audio hardware interface has been initialized. - virtual status_t initCheck(); - - // Set the audio volume of a voice call. Range is between 0.0 and 1.0. - virtual status_t setVoiceVolume(float volume); - - // Set the audio volume for all audio activities other than voice call. - virtual status_t setMasterVolume(float volume); - - // Get the current master volume value for the HAL. - virtual status_t getMasterVolume(float *volume); - - // Called when the audio mode changes. - virtual status_t setMode(audio_mode_t mode); - - // Muting control. - virtual status_t setMicMute(bool state); - virtual status_t getMicMute(bool *state); - virtual status_t setMasterMute(bool state); - virtual status_t getMasterMute(bool *state); - - // Set global audio parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get global audio parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Returns audio input buffer size according to parameters passed. - virtual status_t getInputBufferSize(const struct audio_config *config, - size_t *size); - - // Creates and opens the audio hardware output stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openOutputStream( - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream); - - // Creates and opens the audio hardware input stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - sp *inStream); - - // Returns whether createAudioPatch and releaseAudioPatch operations are supported. - virtual status_t supportsAudioPatches(bool *supportsPatches); - - // Creates an audio patch between several source and sink ports. - virtual status_t createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch); - - // Releases an audio patch. - virtual status_t releaseAudioPatch(audio_patch_handle_t patch); - - // Fills the list of supported attributes for a given audio port. - virtual status_t getAudioPort(struct audio_port *port); - - // Set audio port configuration. - virtual status_t setAudioPortConfig(const struct audio_port_config *config); - - // List microphones - virtual status_t getMicrophones(std::vector *microphones); - - virtual status_t dump(int fd); - - private: - friend class DevicesFactoryHalHidl; - sp mDevice; - sp mPrimaryDevice; // Null if it's not a primary device. - - // Can not be constructed directly by clients. - explicit DeviceHalHidl(const sp& device); - - // The destructor automatically closes the device. - virtual ~DeviceHalHidl(); -}; - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp deleted file mode 100644 index 9c78d8f60d..0000000000 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ - -#include -#include - -#define LOG_TAG "DevicesFactoryHalHidl" -//#define LOG_NDEBUG 0 - -#include -#include -#include - -#include "ConversionHelperHidl.h" -#include "DeviceHalHidl.h" -#include "DevicesFactoryHalHidl.h" - -using ::android::hardware::audio::V4_0::IDevice; -using ::android::hardware::audio::V4_0::Result; -using ::android::hardware::Return; - -namespace android { -namespace V4_0 { - -DevicesFactoryHalHidl::DevicesFactoryHalHidl() { - sp defaultFactory{IDevicesFactory::getService()}; - if (!defaultFactory) { - ALOGE("Failed to obtain IDevicesFactory/default service, terminating process."); - exit(1); - } - mDeviceFactories.push_back(defaultFactory); - // The MSD factory is optional - sp msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)}; - if (msdFactory) { - mDeviceFactories.push_back(msdFactory); - } - for (const auto& factory : mDeviceFactories) { - // It is assumed that the DevicesFactoryHalInterface instance is owned - // by AudioFlinger and thus have the same lifespan. - factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/); - } -} - - -static const char* idFromHal(const char *name, status_t* status) { - *status = OK; - return name; -} - -status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { - if (mDeviceFactories.empty()) return NO_INIT; - status_t status; - auto hidlId = idFromHal(name, &status); - if (status != OK) return status; - Result retval = Result::NOT_INITIALIZED; - for (const auto& factory : mDeviceFactories) { - Return ret = factory->openDevice( - hidlId, - [&](Result r, const sp& result) { - retval = r; - if (retval == Result::OK) { - *device = new DeviceHalHidl(result); - } - }); - if (!ret.isOk()) return FAILED_TRANSACTION; - switch (retval) { - // Device was found and was initialized successfully. - case Result::OK: return OK; - // Device was found but failed to initalize. - case Result::NOT_INITIALIZED: return NO_INIT; - // Otherwise continue iterating. - default: ; - } - } - ALOGW("The specified device name is not recognized: \"%s\"", name); - return BAD_VALUE; -} - -} // namespace V4_0 -} // namespace android diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h deleted file mode 100644 index 5412d50800..0000000000 --- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H - -#include -#include -#include -#include - -#include "DeviceHalHidl.h" - -using ::android::hardware::audio::V4_0::IDevicesFactory; - -namespace android { -namespace V4_0 { - -class DevicesFactoryHalHidl : public DevicesFactoryHalInterface -{ - public: - // Opens a device with the specified name. To close the device, it is - // necessary to release references to the returned object. - virtual status_t openDevice(const char *name, sp *device); - - private: - friend class DevicesFactoryHalHybrid; - - std::vector> mDeviceFactories; - - // Can not be constructed directly by clients. - DevicesFactoryHalHidl(); - - virtual ~DevicesFactoryHalHidl() = default; -}; - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h deleted file mode 100644 index 1dab434913..0000000000 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H - -#include -#include -#include - -namespace android { -namespace V4_0 { - -class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface -{ - public: - DevicesFactoryHalHybrid(); - - // Opens a device with the specified name. To close the device, it is - // necessary to release references to the returned object. - virtual status_t openDevice(const char *name, sp *device); - - private: - sp mLocalFactory; - sp mHidlFactory; -}; - -sp createDevicesFactoryHal() { - return new DevicesFactoryHalHybrid(); -} - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp b/media/libaudiohal/4.0/EffectBufferHalHidl.cpp deleted file mode 100644 index 957c89f14f..0000000000 --- a/media/libaudiohal/4.0/EffectBufferHalHidl.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#define LOG_TAG "EffectBufferHalHidl" -//#define LOG_NDEBUG 0 - -#include -#include -#include - -#include "ConversionHelperHidl.h" -#include "EffectBufferHalHidl.h" - -using ::android::hardware::Return; -using ::android::hidl::allocator::V1_0::IAllocator; - -namespace android { -namespace V4_0 { - -// static -uint64_t EffectBufferHalHidl::makeUniqueId() { - static std::atomic counter{1}; - return counter++; -} - -status_t EffectBufferHalHidl::allocate( - size_t size, sp* buffer) { - return mirror(nullptr, size, buffer); -} - -status_t EffectBufferHalHidl::mirror( - void* external, size_t size, sp* buffer) { - sp tempBuffer = new EffectBufferHalHidl(size); - status_t result = static_cast(tempBuffer.get())->init(); - if (result == OK) { - tempBuffer->setExternalData(external); - *buffer = tempBuffer; - } - return result; -} - -EffectBufferHalHidl::EffectBufferHalHidl(size_t size) - : mBufferSize(size), mFrameCountChanged(false), - mExternalData(nullptr), mAudioBuffer{0, {nullptr}} { - mHidlBuffer.id = makeUniqueId(); - mHidlBuffer.frameCount = 0; -} - -EffectBufferHalHidl::~EffectBufferHalHidl() { -} - -status_t EffectBufferHalHidl::init() { - sp ashmem = IAllocator::getService("ashmem"); - if (ashmem == 0) { - ALOGE("Failed to retrieve ashmem allocator service"); - return NO_INIT; - } - status_t retval = NO_MEMORY; - Return result = ashmem->allocate( - mBufferSize, - [&](bool success, const hidl_memory& memory) { - if (success) { - mHidlBuffer.data = memory; - retval = OK; - } - }); - if (result.isOk() && retval == OK) { - mMemory = hardware::mapMemory(mHidlBuffer.data); - if (mMemory != 0) { - mMemory->update(); - mAudioBuffer.raw = static_cast(mMemory->getPointer()); - memset(mAudioBuffer.raw, 0, mMemory->getSize()); - mMemory->commit(); - } else { - ALOGE("Failed to map allocated ashmem"); - retval = NO_MEMORY; - } - } else { - ALOGE("Failed to allocate %d bytes from ashmem", (int)mBufferSize); - } - return result.isOk() ? retval : FAILED_TRANSACTION; -} - -audio_buffer_t* EffectBufferHalHidl::audioBuffer() { - return &mAudioBuffer; -} - -void* EffectBufferHalHidl::externalData() const { - return mExternalData; -} - -void EffectBufferHalHidl::setFrameCount(size_t frameCount) { - mHidlBuffer.frameCount = frameCount; - mAudioBuffer.frameCount = frameCount; - mFrameCountChanged = true; -} - -bool EffectBufferHalHidl::checkFrameCountChange() { - bool result = mFrameCountChanged; - mFrameCountChanged = false; - return result; -} - -void EffectBufferHalHidl::setExternalData(void* external) { - mExternalData = external; -} - -void EffectBufferHalHidl::update() { - update(mBufferSize); -} - -void EffectBufferHalHidl::commit() { - commit(mBufferSize); -} - -void EffectBufferHalHidl::update(size_t size) { - if (mExternalData == nullptr) return; - mMemory->update(); - if (size > mBufferSize) size = mBufferSize; - memcpy(mAudioBuffer.raw, mExternalData, size); - mMemory->commit(); -} - -void EffectBufferHalHidl::commit(size_t size) { - if (mExternalData == nullptr) return; - if (size > mBufferSize) size = mBufferSize; - memcpy(mExternalData, mAudioBuffer.raw, size); -} - -} // namespace V4_0 -} // namespace android diff --git a/media/libaudiohal/4.0/EffectBufferHalHidl.h b/media/libaudiohal/4.0/EffectBufferHalHidl.h deleted file mode 100644 index 8ad6ff56be..0000000000 --- a/media/libaudiohal/4.0/EffectBufferHalHidl.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H - -#include -#include -#include -#include -#include - -using android::hardware::audio::effect::V4_0::AudioBuffer; -using android::hardware::hidl_memory; -using android::hidl::memory::V1_0::IMemory; - -namespace android { -namespace V4_0 { - -class EffectBufferHalHidl : public EffectBufferHalInterface -{ - public: - static status_t allocate(size_t size, sp* buffer); - static status_t mirror(void* external, size_t size, sp* buffer); - - virtual audio_buffer_t* audioBuffer(); - virtual void* externalData() const; - - virtual size_t getSize() const override { return mBufferSize; } - - virtual void setExternalData(void* external); - virtual void setFrameCount(size_t frameCount); - virtual bool checkFrameCountChange(); - - virtual void update(); - virtual void commit(); - virtual void update(size_t size); - virtual void commit(size_t size); - - const AudioBuffer& hidlBuffer() const { return mHidlBuffer; } - - private: - friend class EffectBufferHalInterface; - - static uint64_t makeUniqueId(); - - const size_t mBufferSize; - bool mFrameCountChanged; - void* mExternalData; - AudioBuffer mHidlBuffer; - sp mMemory; - audio_buffer_t mAudioBuffer; - - // Can not be constructed directly by clients. - explicit EffectBufferHalHidl(size_t size); - - virtual ~EffectBufferHalHidl(); - - status_t init(); -}; - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/EffectHalHidl.cpp b/media/libaudiohal/4.0/EffectHalHidl.cpp deleted file mode 100644 index 2e1e3524a7..0000000000 --- a/media/libaudiohal/4.0/EffectHalHidl.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "EffectHalHidl" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include - -#include "EffectBufferHalHidl.h" -#include "EffectHalHidl.h" -#include "HidlUtils.h" - -using ::android::hardware::audio::effect::V4_0::AudioBuffer; -using ::android::hardware::audio::effect::V4_0::EffectBufferAccess; -using ::android::hardware::audio::effect::V4_0::EffectConfigParameters; -using ::android::hardware::audio::effect::V4_0::MessageQueueFlagBits; -using ::android::hardware::audio::effect::V4_0::Result; -using ::android::hardware::audio::common::V4_0::HidlUtils; -using ::android::hardware::audio::common::V4_0::AudioChannelMask; -using ::android::hardware::audio::common::V4_0::AudioFormat; -using ::android::hardware::audio::common::utils::mkEnumConverter; -using ::android::hardware::hidl_vec; -using ::android::hardware::MQDescriptorSync; -using ::android::hardware::Return; - -namespace android { -namespace V4_0 { - -EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) - : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { -} - -EffectHalHidl::~EffectHalHidl() { - if (mEffect != 0) { - close(); - mEffect.clear(); - hardware::IPCThreadState::self()->flushCommands(); - } - if (mEfGroup) { - EventFlag::deleteEventFlag(&mEfGroup); - } -} - -// static -void EffectHalHidl::effectDescriptorToHal( - const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor) { - HidlUtils::uuidToHal(descriptor.type, &halDescriptor->type); - HidlUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid); - halDescriptor->flags = static_cast(descriptor.flags); - halDescriptor->cpuLoad = descriptor.cpuLoad; - halDescriptor->memoryUsage = descriptor.memoryUsage; - memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size()); - memcpy(halDescriptor->implementor, - descriptor.implementor.data(), descriptor.implementor.size()); -} - -// TODO(mnaganov): These buffer conversion functions should be shared with Effect wrapper -// via HidlUtils. Move them there when hardware/interfaces will get un-frozen again. - -// static -void EffectHalHidl::effectBufferConfigFromHal( - const buffer_config_t& halConfig, EffectBufferConfig* config) { - config->samplingRateHz = halConfig.samplingRate; - config->channels = mkEnumConverter(halConfig.channels); - config->format = AudioFormat(halConfig.format); - config->accessMode = EffectBufferAccess(halConfig.accessMode); - config->mask = mkEnumConverter(halConfig.mask); -} - -// static -void EffectHalHidl::effectBufferConfigToHal( - const EffectBufferConfig& config, buffer_config_t* halConfig) { - halConfig->buffer.frameCount = 0; - halConfig->buffer.raw = NULL; - halConfig->samplingRate = config.samplingRateHz; - halConfig->channels = static_cast(config.channels); - halConfig->bufferProvider.cookie = NULL; - halConfig->bufferProvider.getBuffer = NULL; - halConfig->bufferProvider.releaseBuffer = NULL; - halConfig->format = static_cast(config.format); - halConfig->accessMode = static_cast(config.accessMode); - halConfig->mask = static_cast(config.mask); -} - -// static -void EffectHalHidl::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) { - effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg); - effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg); -} - -// static -void EffectHalHidl::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) { - effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg); - effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg); -} - -// static -status_t EffectHalHidl::analyzeResult(const Result& result) { - switch (result) { - case Result::OK: return OK; - case Result::INVALID_ARGUMENTS: return BAD_VALUE; - case Result::INVALID_STATE: return NOT_ENOUGH_DATA; - case Result::NOT_INITIALIZED: return NO_INIT; - case Result::NOT_SUPPORTED: return INVALID_OPERATION; - case Result::RESULT_TOO_BIG: return NO_MEMORY; - default: return NO_INIT; - } -} - -status_t EffectHalHidl::setInBuffer(const sp& buffer) { - if (!mBuffersChanged) { - if (buffer.get() == nullptr || mInBuffer.get() == nullptr) { - mBuffersChanged = buffer.get() != mInBuffer.get(); - } else { - mBuffersChanged = buffer->audioBuffer() != mInBuffer->audioBuffer(); - } - } - mInBuffer = buffer; - return OK; -} - -status_t EffectHalHidl::setOutBuffer(const sp& buffer) { - if (!mBuffersChanged) { - if (buffer.get() == nullptr || mOutBuffer.get() == nullptr) { - mBuffersChanged = buffer.get() != mOutBuffer.get(); - } else { - mBuffersChanged = buffer->audioBuffer() != mOutBuffer->audioBuffer(); - } - } - mOutBuffer = buffer; - return OK; -} - -status_t EffectHalHidl::process() { - return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS)); -} - -status_t EffectHalHidl::processReverse() { - return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE)); -} - -status_t EffectHalHidl::prepareForProcessing() { - std::unique_ptr tempStatusMQ; - Result retval; - Return ret = mEffect->prepareForProcessing( - [&](Result r, const MQDescriptorSync& statusMQ) { - retval = r; - if (retval == Result::OK) { - tempStatusMQ.reset(new StatusMQ(statusMQ)); - if (tempStatusMQ->isValid() && tempStatusMQ->getEventFlagWord()) { - EventFlag::createEventFlag(tempStatusMQ->getEventFlagWord(), &mEfGroup); - } - } - }); - if (!ret.isOk() || retval != Result::OK) { - return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; - } - if (!tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) { - ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for effects"); - ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), - "Status message queue for effects is invalid"); - ALOGE_IF(!mEfGroup, "Event flag creation for effects failed"); - return NO_INIT; - } - mStatusMQ = std::move(tempStatusMQ); - return OK; -} - -bool EffectHalHidl::needToResetBuffers() { - if (mBuffersChanged) return true; - bool inBufferFrameCountUpdated = mInBuffer->checkFrameCountChange(); - bool outBufferFrameCountUpdated = mOutBuffer->checkFrameCountChange(); - return inBufferFrameCountUpdated || outBufferFrameCountUpdated; -} - -status_t EffectHalHidl::processImpl(uint32_t mqFlag) { - if (mEffect == 0 || mInBuffer == 0 || mOutBuffer == 0) return NO_INIT; - status_t status; - if (!mStatusMQ && (status = prepareForProcessing()) != OK) { - return status; - } - if (needToResetBuffers() && (status = setProcessBuffers()) != OK) { - return status; - } - // The data is already in the buffers, just need to flush it and wake up the server side. - std::atomic_thread_fence(std::memory_order_release); - mEfGroup->wake(mqFlag); - uint32_t efState = 0; -retry: - status_t ret = mEfGroup->wait( - static_cast(MessageQueueFlagBits::DONE_PROCESSING), &efState); - if (efState & static_cast(MessageQueueFlagBits::DONE_PROCESSING)) { - Result retval = Result::NOT_INITIALIZED; - mStatusMQ->read(&retval); - if (retval == Result::OK || retval == Result::INVALID_STATE) { - // Sync back the changed contents of the buffer. - std::atomic_thread_fence(std::memory_order_acquire); - } - return analyzeResult(retval); - } - if (ret == -EAGAIN || ret == -EINTR) { - // Spurious wakeup. This normally retries no more than once. - goto retry; - } - return ret; -} - -status_t EffectHalHidl::setProcessBuffers() { - Return ret = mEffect->setProcessBuffers( - static_cast(mInBuffer.get())->hidlBuffer(), - static_cast(mOutBuffer.get())->hidlBuffer()); - if (ret.isOk() && ret == Result::OK) { - mBuffersChanged = false; - return OK; - } - return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; -} - -status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, - uint32_t *replySize, void *pReplyData) { - if (mEffect == 0) return NO_INIT; - - // Special cases. - if (cmdCode == EFFECT_CMD_SET_CONFIG || cmdCode == EFFECT_CMD_SET_CONFIG_REVERSE) { - return setConfigImpl(cmdCode, cmdSize, pCmdData, replySize, pReplyData); - } else if (cmdCode == EFFECT_CMD_GET_CONFIG || cmdCode == EFFECT_CMD_GET_CONFIG_REVERSE) { - return getConfigImpl(cmdCode, replySize, pReplyData); - } - - // Common case. - hidl_vec hidlData; - if (pCmdData != nullptr && cmdSize > 0) { - hidlData.setToExternal(reinterpret_cast(pCmdData), cmdSize); - } - status_t status; - uint32_t replySizeStub = 0; - if (replySize == nullptr || pReplyData == nullptr) replySize = &replySizeStub; - Return ret = mEffect->command(cmdCode, hidlData, *replySize, - [&](int32_t s, const hidl_vec& result) { - status = s; - if (status == 0) { - if (*replySize > result.size()) *replySize = result.size(); - if (pReplyData != nullptr && *replySize > 0) { - memcpy(pReplyData, &result[0], *replySize); - } - } - }); - return ret.isOk() ? status : FAILED_TRANSACTION; -} - -status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) { - if (mEffect == 0) return NO_INIT; - Result retval = Result::NOT_INITIALIZED; - Return ret = mEffect->getDescriptor( - [&](Result r, const EffectDescriptor& result) { - retval = r; - if (retval == Result::OK) { - effectDescriptorToHal(result, pDescriptor); - } - }); - return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; -} - -status_t EffectHalHidl::close() { - if (mEffect == 0) return NO_INIT; - Return ret = mEffect->close(); - return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; -} - -status_t EffectHalHidl::getConfigImpl( - uint32_t cmdCode, uint32_t *replySize, void *pReplyData) { - if (replySize == NULL || *replySize != sizeof(effect_config_t) || pReplyData == NULL) { - return BAD_VALUE; - } - status_t result = FAILED_TRANSACTION; - Return ret; - if (cmdCode == EFFECT_CMD_GET_CONFIG) { - ret = mEffect->getConfig([&] (Result r, const EffectConfig &hidlConfig) { - result = analyzeResult(r); - if (r == Result::OK) { - effectConfigToHal(hidlConfig, static_cast(pReplyData)); - } - }); - } else { - ret = mEffect->getConfigReverse([&] (Result r, const EffectConfig &hidlConfig) { - result = analyzeResult(r); - if (r == Result::OK) { - effectConfigToHal(hidlConfig, static_cast(pReplyData)); - } - }); - } - if (!ret.isOk()) { - result = FAILED_TRANSACTION; - } - return result; -} - -status_t EffectHalHidl::setConfigImpl( - uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData) { - if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) || - replySize == NULL || *replySize != sizeof(int32_t) || pReplyData == NULL) { - return BAD_VALUE; - } - const effect_config_t *halConfig = static_cast(pCmdData); - if (halConfig->inputCfg.bufferProvider.getBuffer != NULL || - halConfig->inputCfg.bufferProvider.releaseBuffer != NULL || - halConfig->outputCfg.bufferProvider.getBuffer != NULL || - halConfig->outputCfg.bufferProvider.releaseBuffer != NULL) { - ALOGE("Buffer provider callbacks are not supported"); - } - EffectConfig hidlConfig; - effectConfigFromHal(*halConfig, &hidlConfig); - Return ret = cmdCode == EFFECT_CMD_SET_CONFIG ? - mEffect->setConfig(hidlConfig, nullptr, nullptr) : - mEffect->setConfigReverse(hidlConfig, nullptr, nullptr); - status_t result = FAILED_TRANSACTION; - if (ret.isOk()) { - result = analyzeResult(ret); - *static_cast(pReplyData) = result; - } - return result; -} - -} // namespace V4_0 -} // namespace android diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp deleted file mode 100644 index e23aec9283..0000000000 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "EffectsFactoryHalHidl" -//#define LOG_NDEBUG 0 - -#include - -#include "EffectsFactoryHalHidl.h" -#include "ConversionHelperHidl.h" -#include "EffectBufferHalHidl.h" -#include "EffectHalHidl.h" -#include "HidlUtils.h" - -using ::android::hardware::audio::common::V4_0::HidlUtils; -using ::android::hardware::audio::common::V4_0::Uuid; -using ::android::hardware::audio::effect::V4_0::IEffect; -using ::android::hardware::audio::effect::V4_0::Result; -using ::android::hardware::Return; - -namespace android { -namespace V4_0 { - -EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { - mEffectsFactory = IEffectsFactory::getService(); - if (mEffectsFactory == 0) { - ALOGE("Failed to obtain IEffectsFactory service, terminating process."); - exit(1); - } -} - -status_t EffectsFactoryHalHidl::queryAllDescriptors() { - if (mEffectsFactory == 0) return NO_INIT; - Result retval = Result::NOT_INITIALIZED; - Return ret = mEffectsFactory->getAllDescriptors( - [&](Result r, const hidl_vec& result) { - retval = r; - if (retval == Result::OK) { - mLastDescriptors = result; - } - }); - if (ret.isOk()) { - return retval == Result::OK ? OK : NO_INIT; - } - mLastDescriptors.resize(0); - return processReturn(__FUNCTION__, ret); -} - -status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) { - status_t queryResult = queryAllDescriptors(); - if (queryResult == OK) { - *pNumEffects = mLastDescriptors.size(); - } - return queryResult; -} - -status_t EffectsFactoryHalHidl::getDescriptor( - uint32_t index, effect_descriptor_t *pDescriptor) { - // TODO: We need somehow to track the changes on the server side - // or figure out how to convert everybody to query all the descriptors at once. - // TODO: check for nullptr - if (mLastDescriptors.size() == 0) { - status_t queryResult = queryAllDescriptors(); - if (queryResult != OK) return queryResult; - } - if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND; - EffectHalHidl::effectDescriptorToHal(mLastDescriptors[index], pDescriptor); - return OK; -} - -status_t EffectsFactoryHalHidl::getDescriptor( - const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) { - // TODO: check for nullptr - if (mEffectsFactory == 0) return NO_INIT; - Uuid hidlUuid; - HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid); - Result retval = Result::NOT_INITIALIZED; - Return ret = mEffectsFactory->getDescriptor(hidlUuid, - [&](Result r, const EffectDescriptor& result) { - retval = r; - if (retval == Result::OK) { - EffectHalHidl::effectDescriptorToHal(result, pDescriptor); - } - }); - if (ret.isOk()) { - if (retval == Result::OK) return OK; - else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND; - else return NO_INIT; - } - return processReturn(__FUNCTION__, ret); -} - -status_t EffectsFactoryHalHidl::createEffect( - const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, - sp *effect) { - if (mEffectsFactory == 0) return NO_INIT; - Uuid hidlUuid; - HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid); - Result retval = Result::NOT_INITIALIZED; - Return ret = mEffectsFactory->createEffect( - hidlUuid, sessionId, ioId, - [&](Result r, const sp& result, uint64_t effectId) { - retval = r; - if (retval == Result::OK) { - *effect = new EffectHalHidl(result, effectId); - } - }); - if (ret.isOk()) { - if (retval == Result::OK) return OK; - else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND; - else return NO_INIT; - } - return processReturn(__FUNCTION__, ret); -} - -status_t EffectsFactoryHalHidl::dumpEffects(int fd) { - if (mEffectsFactory == 0) return NO_INIT; - native_handle_t* hidlHandle = native_handle_create(1, 0); - hidlHandle->data[0] = fd; - Return ret = mEffectsFactory->debug(hidlHandle, {} /* options */); - native_handle_delete(hidlHandle); - return processReturn(__FUNCTION__, ret); -} - -status_t EffectsFactoryHalHidl::allocateBuffer(size_t size, sp* buffer) { - return EffectBufferHalHidl::allocate(size, buffer); -} - -status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, - sp* buffer) { - return EffectBufferHalHidl::mirror(external, size, buffer); -} - - -} // namespace V4_0 -} // namespace android diff --git a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h b/media/libaudiohal/4.0/EffectsFactoryHalHidl.h deleted file mode 100644 index 2152c6fa21..0000000000 --- a/media/libaudiohal/4.0/EffectsFactoryHalHidl.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H -#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H - -#include -#include -#include - -#include "ConversionHelperHidl.h" - -namespace android { -namespace V4_0 { - -using ::android::hardware::audio::effect::V4_0::EffectDescriptor; -using ::android::hardware::audio::effect::V4_0::IEffectsFactory; -using ::android::hardware::hidl_vec; - -class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl -{ - public: - EffectsFactoryHalHidl(); - - // Returns the number of different effects in all loaded libraries. - virtual status_t queryNumberEffects(uint32_t *pNumEffects); - - // Returns a descriptor of the next available effect. - virtual status_t getDescriptor(uint32_t index, - effect_descriptor_t *pDescriptor); - - virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid, - effect_descriptor_t *pDescriptor); - - // Creates an effect engine of the specified type. - // To release the effect engine, it is necessary to release references - // to the returned effect object. - virtual status_t createEffect(const effect_uuid_t *pEffectUuid, - int32_t sessionId, int32_t ioId, - sp *effect); - - virtual status_t dumpEffects(int fd); - - status_t allocateBuffer(size_t size, sp* buffer) override; - status_t mirrorBuffer(void* external, size_t size, - sp* buffer) override; - - private: - sp mEffectsFactory; - hidl_vec mLastDescriptors; - - status_t queryAllDescriptors(); -}; - -sp createEffectsFactoryHal() { - return new EffectsFactoryHalHidl(); -} - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h b/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h deleted file mode 100644 index 3d37687258..0000000000 --- a/media/libaudiohal/4.0/include/libaudiohal/4.0/FactoryHalHidl.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H -#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H - -/** @file Library entry points to create the HAL factories. */ - -#include -#include -#include - -namespace android { -namespace V4_0 { - -sp createEffectsFactoryHal(); - -sp createDevicesFactoryHal(); - -} // namespace V4_0 -} // namespace android - -#endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_V4_0_H diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp index 3a5df27971..0ff0d4a0fd 100644 --- a/media/libaudiohal/Android.bp +++ b/media/libaudiohal/Android.bp @@ -8,6 +8,7 @@ cc_library_shared { cflags: [ "-Wall", + "-Wextra", "-Werror", ], diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp index 9f70e4f997..e631acef9a 100644 --- a/media/libaudiohal/DevicesFactoryHalInterface.cpp +++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp @@ -17,8 +17,7 @@ #include #include -#include -#include +#include namespace android { diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index 50f976fd6e..f7734a8d16 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -17,8 +17,7 @@ #include #include -#include -#include +#include namespace android { diff --git a/media/libaudiohal/2.0/Android.bp b/media/libaudiohal/impl/Android.bp similarity index 70% rename from media/libaudiohal/2.0/Android.bp rename to media/libaudiohal/impl/Android.bp index 29a32af094..3827336302 100644 --- a/media/libaudiohal/2.0/Android.bp +++ b/media/libaudiohal/impl/Android.bp @@ -1,5 +1,5 @@ cc_defaults { - name: "libaudiohal@2.0_default", + name: "libaudiohal_default", srcs: [ "DeviceHalLocal.cpp", @@ -24,24 +24,30 @@ cc_defaults { "-Werror", ], shared_libs: [ + "android.hardware.audio.common-util", + "android.hardware.audio.common@2.0", + "android.hardware.audio.common@4.0", + "android.hardware.audio.effect@2.0", + "android.hardware.audio.effect@4.0", + "android.hardware.audio@2.0", + "android.hardware.audio@4.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", "libaudiohal_deathhandler", "libaudioutils", + "libbase", "libbinder", "libcutils", - "liblog", - "libutils", - "libhardware", - "libbase", "libfmq", - "libhwbinder", + "libhardware", "libhidlbase", "libhidlmemory", "libhidltransport", - "android.hardware.audio.common-util", - "android.hidl.allocator@1.0", - "android.hidl.memory@1.0", + "libhwbinder", + "liblog", "libmedia_helper", "libmediautils", + "libutils", ], header_libs: [ "android.hardware.audio.common.util@all-versions", @@ -55,11 +61,26 @@ cc_defaults { cc_library_shared { name: "libaudiohal@2.0", - defaults: ["libaudiohal@2.0_default"], + defaults: ["libaudiohal_default"], shared_libs: [ - "android.hardware.audio@2.0", - "android.hardware.audio.common@2.0", "android.hardware.audio.common@2.0-util", - "android.hardware.audio.effect@2.0", ], + cflags: [ + "-DMAJOR_VERSION=2", + "-DMINOR_VERSION=0", + "-include VersionMacro.h", + ] +} + +cc_library_shared { + name: "libaudiohal@4.0", + defaults: ["libaudiohal_default"], + shared_libs: [ + "android.hardware.audio.common@4.0-util", + ], + cflags: [ + "-DMAJOR_VERSION=4", + "-DMINOR_VERSION=0", + "-include VersionMacro.h", + ] } diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp similarity index 94% rename from media/libaudiohal/4.0/ConversionHelperHidl.cpp rename to media/libaudiohal/impl/ConversionHelperHidl.cpp index 8f64c969ed..5d12fadbd8 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.cpp +++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp @@ -22,15 +22,18 @@ #include "ConversionHelperHidl.h" -using ::android::hardware::audio::V4_0::AudioMicrophoneChannelMapping; -using ::android::hardware::audio::V4_0::AudioMicrophoneDirectionality; -using ::android::hardware::audio::V4_0::AudioMicrophoneLocation; -using ::android::hardware::audio::V4_0::DeviceAddress; -using ::android::hardware::audio::V4_0::MicrophoneInfo; -using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::audio::CPP_VERSION::Result; + +#if MAJOR_VERSION == 4 +using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneChannelMapping; +using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneDirectionality; +using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneLocation; +using ::android::hardware::audio::CPP_VERSION::DeviceAddress; +using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; +#endif namespace android { -namespace V4_0 { +namespace CPP_VERSION { // static status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { @@ -106,6 +109,7 @@ void ConversionHelperHidl::emitError(const char* funcName, const char* descripti ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); } +#if MAJOR_VERSION == 4 // TODO: Use the same implementation in the hal when it moves to a util library. static std::string deviceAddressToHal(const DeviceAddress& address) { // HAL assumes that the address is NUL-terminated. @@ -187,9 +191,8 @@ static audio_microphone_directionality_t directionalityToHal(AudioMicrophoneDire } } -// static -void ConversionHelperHidl::microphoneInfoToHal(const MicrophoneInfo& src, - audio_microphone_characteristic_t *pDst) { +void microphoneInfoToHal(const MicrophoneInfo& src, + audio_microphone_characteristic_t *pDst) { if (pDst != NULL) { snprintf(pDst->device_id, sizeof(pDst->device_id), "%s", src.deviceId.c_str()); @@ -232,6 +235,7 @@ void ConversionHelperHidl::microphoneInfoToHal(const MicrophoneInfo& src, pDst->orientation.z = src.orientation.z; } } +#endif -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/4.0/ConversionHelperHidl.h b/media/libaudiohal/impl/ConversionHelperHidl.h similarity index 86% rename from media/libaudiohal/4.0/ConversionHelperHidl.h rename to media/libaudiohal/impl/ConversionHelperHidl.h index 1dc113c51c..1a9319f632 100644 --- a/media/libaudiohal/4.0/ConversionHelperHidl.h +++ b/media/libaudiohal/impl/ConversionHelperHidl.h @@ -17,28 +17,27 @@ #ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H #define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H +#include #include #include #include #include -using ::android::hardware::audio::V4_0::ParameterValue; -using CoreResult = ::android::hardware::audio::V4_0::Result; -using ::android::hardware::audio::V4_0::MicrophoneInfo; +using ::android::hardware::audio::CPP_VERSION::ParameterValue; +using CoreResult = ::android::hardware::audio::CPP_VERSION::Result; + using ::android::hardware::Return; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; namespace android { -namespace V4_0 { +namespace CPP_VERSION { class ConversionHelperHidl { protected: static status_t keysFromHal(const String8& keys, hidl_vec *hidlKeys); static status_t parametersFromHal(const String8& kvPairs, hidl_vec *hidlParams); static void parametersToHal(const hidl_vec& parameters, String8 *values); - static void microphoneInfoToHal(const MicrophoneInfo& src, - audio_microphone_characteristic_t *pDst); ConversionHelperHidl(const char* className); @@ -84,7 +83,13 @@ class ConversionHelperHidl { void emitError(const char* funcName, const char* description); }; -} // namespace V4_0 +#if MAJOR_VERSION == 4 +using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; +void microphoneInfoToHal(const MicrophoneInfo& src, + audio_microphone_characteristic_t *pDst); +#endif + +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp similarity index 90% rename from media/libaudiohal/4.0/DeviceHalHidl.cpp rename to media/libaudiohal/impl/DeviceHalHidl.cpp index c547994ae5..723e2eb127 100644 --- a/media/libaudiohal/4.0/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "DeviceHalHidl" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -31,27 +32,30 @@ #include "StreamHalHidl.h" #include "VersionUtils.h" -using ::android::hardware::audio::common::V4_0::AudioConfig; -using ::android::hardware::audio::common::V4_0::AudioDevice; -using ::android::hardware::audio::common::V4_0::AudioInputFlag; -using ::android::hardware::audio::common::V4_0::AudioOutputFlag; -using ::android::hardware::audio::common::V4_0::AudioPatchHandle; -using ::android::hardware::audio::common::V4_0::AudioPort; -using ::android::hardware::audio::common::V4_0::AudioPortConfig; -using ::android::hardware::audio::common::V4_0::AudioMode; -using ::android::hardware::audio::common::V4_0::AudioSource; -using ::android::hardware::audio::common::V4_0::HidlUtils; +using ::android::hardware::audio::common::CPP_VERSION::AudioConfig; +using ::android::hardware::audio::common::CPP_VERSION::AudioDevice; +using ::android::hardware::audio::common::CPP_VERSION::AudioInputFlag; +using ::android::hardware::audio::common::CPP_VERSION::AudioOutputFlag; +using ::android::hardware::audio::common::CPP_VERSION::AudioPatchHandle; +using ::android::hardware::audio::common::CPP_VERSION::AudioPort; +using ::android::hardware::audio::common::CPP_VERSION::AudioPortConfig; +using ::android::hardware::audio::common::CPP_VERSION::AudioMode; +using ::android::hardware::audio::common::CPP_VERSION::AudioSource; +using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; using ::android::hardware::audio::common::utils::mkEnumConverter; -using ::android::hardware::audio::V4_0::DeviceAddress; -using ::android::hardware::audio::V4_0::IPrimaryDevice; -using ::android::hardware::audio::V4_0::ParameterValue; -using ::android::hardware::audio::V4_0::Result; -using ::android::hardware::audio::V4_0::SinkMetadata; +using ::android::hardware::audio::CPP_VERSION::DeviceAddress; +using ::android::hardware::audio::CPP_VERSION::IPrimaryDevice; +using ::android::hardware::audio::CPP_VERSION::ParameterValue; +using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; +#if MAJOR_VERSION == 4 +using ::android::hardware::audio::CPP_VERSION::SinkMetadata; +#endif + namespace android { -namespace V4_0 { +namespace CPP_VERSION { namespace { @@ -259,7 +263,9 @@ status_t DeviceHalHidl::openOutputStream( hidlDevice, hidlConfig, mkEnumConverter(flags), +#if MAJOR_VERSION == 4 {} /* metadata */, +#endif [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { @@ -285,9 +291,13 @@ status_t DeviceHalHidl::openInputStream( AudioConfig hidlConfig; HidlUtils::audioConfigFromHal(*config, &hidlConfig); Result retval = Result::NOT_INITIALIZED; +#if MAJOR_VERSION == 2 + auto sourceMetadata = AudioSource(source); +#elif MAJOR_VERSION == 4 // TODO: correctly propagate the tracks sources and volume // for now, only send the main source at 1dbfs SinkMetadata sourceMetadata = {{{AudioSource(source), 1}}}; +#endif Return ret = mDevice->openInputStream( handle, hidlDevice, @@ -359,6 +369,13 @@ status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *confi return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig)); } +#if MAJOR_VERSION == 2 +status_t DeviceHalHidl::getMicrophones( + std::vector *microphonesInfo __unused) { + if (mDevice == 0) return NO_INIT; + return INVALID_OPERATION; +} +#elif MAJOR_VERSION == 4 status_t DeviceHalHidl::getMicrophones(std::vector *microphonesInfo) { if (mDevice == 0) return NO_INIT; Result retval; @@ -375,6 +392,7 @@ status_t DeviceHalHidl::getMicrophones(std::vector *micro }); return processReturn("getMicrophones", ret, retval); } +#endif status_t DeviceHalHidl::dump(int fd) { if (mDevice == 0) return NO_INIT; @@ -385,5 +403,5 @@ status_t DeviceHalHidl::dump(int fd) { return processReturn("dump", ret); } -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/2.0/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h similarity index 94% rename from media/libaudiohal/2.0/DeviceHalHidl.h rename to media/libaudiohal/impl/DeviceHalHidl.h index d6b0ab0818..fb5e7e7302 100644 --- a/media/libaudiohal/2.0/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -18,17 +18,19 @@ #define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H #include +#include #include +#include #include #include "ConversionHelperHidl.h" -using ::android::hardware::audio::V2_0::IDevice; -using ::android::hardware::audio::V2_0::IPrimaryDevice; +using ::android::hardware::audio::CPP_VERSION::IDevice; +using ::android::hardware::audio::CPP_VERSION::IPrimaryDevice; using ::android::hardware::Return; namespace android { -namespace V2_0 { +namespace CPP_VERSION { class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl { @@ -125,7 +127,7 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl virtual ~DeviceHalHidl(); }; -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp similarity index 96% rename from media/libaudiohal/4.0/DeviceHalLocal.cpp rename to media/libaudiohal/impl/DeviceHalLocal.cpp index a245dd9c2a..14e26f55fe 100644 --- a/media/libaudiohal/4.0/DeviceHalLocal.cpp +++ b/media/libaudiohal/impl/DeviceHalLocal.cpp @@ -23,7 +23,7 @@ #include "StreamHalLocal.h" namespace android { -namespace V4_0 { +namespace CPP_VERSION { DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) : mDev(dev) { @@ -185,6 +185,12 @@ status_t DeviceHalLocal::setAudioPortConfig(const struct audio_port_config *conf return INVALID_OPERATION; } +#if MAJOR_VERSION == 2 +status_t DeviceHalLocal::getMicrophones( + std::vector *microphones __unused) { + return INVALID_OPERATION; +} +#elif MAJOR_VERSION == 4 status_t DeviceHalLocal::getMicrophones(std::vector *microphones) { if (mDev->get_microphones == NULL) return INVALID_OPERATION; size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; @@ -196,6 +202,7 @@ status_t DeviceHalLocal::getMicrophones(std::vector *micr } return status; } +#endif status_t DeviceHalLocal::dump(int fd) { return mDev->dump(mDev, fd); @@ -209,5 +216,5 @@ void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { mDev->close_input_stream(mDev, stream_in); } -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/4.0/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h similarity index 98% rename from media/libaudiohal/4.0/DeviceHalLocal.h rename to media/libaudiohal/impl/DeviceHalLocal.h index e652f2599a..18bd879384 100644 --- a/media/libaudiohal/4.0/DeviceHalLocal.h +++ b/media/libaudiohal/impl/DeviceHalLocal.h @@ -21,7 +21,7 @@ #include namespace android { -namespace V4_0 { +namespace CPP_VERSION { class DeviceHalLocal : public DeviceHalInterface { @@ -123,7 +123,7 @@ class DeviceHalLocal : public DeviceHalInterface uint32_t version() const { return mDev->common.version; } }; -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp similarity index 83% rename from media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp rename to media/libaudiohal/impl/DevicesFactoryHalHidl.cpp index ac65fcc55c..28001dafdf 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp @@ -21,6 +21,7 @@ //#define LOG_NDEBUG 0 #include +#include #include #include @@ -28,12 +29,12 @@ #include "DeviceHalHidl.h" #include "DevicesFactoryHalHidl.h" -using ::android::hardware::audio::V2_0::IDevice; -using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::CPP_VERSION::IDevice; +using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::Return; namespace android { -namespace V2_0 { +namespace CPP_VERSION { DevicesFactoryHalHidl::DevicesFactoryHalHidl() { sp defaultFactory{IDevicesFactory::getService()}; @@ -42,6 +43,13 @@ DevicesFactoryHalHidl::DevicesFactoryHalHidl() { exit(1); } mDeviceFactories.push_back(defaultFactory); + if (MAJOR_VERSION >= 4) { + // The MSD factory is optional and only available starting at HAL 4.0 + sp msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)}; + if (msdFactory) { + mDeviceFactories.push_back(msdFactory); + } + } for (const auto& factory : mDeviceFactories) { // It is assumed that the DevicesFactoryHalInterface instance is owned // by AudioFlinger and thus have the same lifespan. @@ -50,6 +58,7 @@ DevicesFactoryHalHidl::DevicesFactoryHalHidl() { } +#if MAJOR_VERSION == 2 static IDevicesFactory::Device idFromHal(const char *name, status_t* status) { *status = OK; if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) { @@ -67,6 +76,12 @@ static IDevicesFactory::Device idFromHal(const char *name, status_t* status) { *status = BAD_VALUE; return {}; } +#elif MAJOR_VERSION == 4 +static const char* idFromHal(const char *name, status_t* status) { + *status = OK; + return name; +} +#endif status_t DevicesFactoryHalHidl::openDevice(const char *name, sp *device) { if (mDeviceFactories.empty()) return NO_INIT; @@ -97,5 +112,5 @@ status_t DevicesFactoryHalHidl::openDevice(const char *name, sp +#include #include #include #include #include "DeviceHalHidl.h" -using ::android::hardware::audio::V2_0::IDevicesFactory; +using ::android::hardware::audio::CPP_VERSION::IDevicesFactory; namespace android { -namespace V2_0 { +namespace CPP_VERSION { class DevicesFactoryHalHidl : public DevicesFactoryHalInterface { @@ -47,7 +48,7 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface virtual ~DevicesFactoryHalHidl() = default; }; -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp similarity index 96% rename from media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp rename to media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp index d40ffadf31..f337a8bed7 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalHybrid.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp @@ -22,7 +22,7 @@ #include "DevicesFactoryHalHidl.h" namespace android { -namespace V4_0 { +namespace CPP_VERSION { DevicesFactoryHalHybrid::DevicesFactoryHalHybrid() : mLocalFactory(new DevicesFactoryHalLocal()), @@ -37,5 +37,5 @@ status_t DevicesFactoryHalHybrid::openDevice(const char *name, spopenDevice(name, device); } -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h similarity index 96% rename from media/libaudiohal/2.0/DevicesFactoryHalHybrid.h rename to media/libaudiohal/impl/DevicesFactoryHalHybrid.h index ba8c6f21c5..5ac0d0d45b 100644 --- a/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h +++ b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h @@ -22,7 +22,7 @@ #include namespace android { -namespace V2_0 { +namespace CPP_VERSION { class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface { @@ -42,7 +42,7 @@ sp createDevicesFactoryHal() { return new DevicesFactoryHalHybrid(); } -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp b/media/libaudiohal/impl/DevicesFactoryHalLocal.cpp similarity index 97% rename from media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp rename to media/libaudiohal/impl/DevicesFactoryHalLocal.cpp index e54edd4c29..af67ff5d40 100644 --- a/media/libaudiohal/4.0/DevicesFactoryHalLocal.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalLocal.cpp @@ -26,7 +26,7 @@ #include "DevicesFactoryHalLocal.h" namespace android { -namespace V4_0 { +namespace CPP_VERSION { static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) { @@ -67,5 +67,5 @@ status_t DevicesFactoryHalLocal::openDevice(const char *name, sp +#include #include #include #include #include -using android::hardware::audio::effect::V2_0::AudioBuffer; +using android::hardware::audio::effect::CPP_VERSION::AudioBuffer; using android::hardware::hidl_memory; using android::hidl::memory::V1_0::IMemory; namespace android { -namespace V2_0 { +namespace CPP_VERSION { class EffectBufferHalHidl : public EffectBufferHalInterface { @@ -72,7 +73,7 @@ class EffectBufferHalHidl : public EffectBufferHalInterface status_t init(); }; -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp similarity index 95% rename from media/libaudiohal/2.0/EffectHalHidl.cpp rename to media/libaudiohal/impl/EffectHalHidl.cpp index dcd2dc2338..12649a1f13 100644 --- a/media/libaudiohal/2.0/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -26,21 +26,21 @@ #include "EffectHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::effect::V2_0::AudioBuffer; -using ::android::hardware::audio::effect::V2_0::EffectBufferAccess; -using ::android::hardware::audio::effect::V2_0::EffectConfigParameters; -using ::android::hardware::audio::effect::V2_0::MessageQueueFlagBits; -using ::android::hardware::audio::effect::V2_0::Result; -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::common::V2_0::AudioChannelMask; -using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::audio::effect::CPP_VERSION::AudioBuffer; +using ::android::hardware::audio::effect::CPP_VERSION::EffectBufferAccess; +using ::android::hardware::audio::effect::CPP_VERSION::EffectConfigParameters; +using ::android::hardware::audio::effect::CPP_VERSION::MessageQueueFlagBits; +using ::android::hardware::audio::effect::CPP_VERSION::Result; +using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; +using ::android::hardware::audio::common::CPP_VERSION::AudioChannelMask; +using ::android::hardware::audio::common::CPP_VERSION::AudioFormat; using ::android::hardware::audio::common::utils::mkEnumConverter; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; namespace android { -namespace V2_0 { +namespace CPP_VERSION { EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { @@ -337,5 +337,5 @@ status_t EffectHalHidl::setConfigImpl( return result; } -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/4.0/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h similarity index 88% rename from media/libaudiohal/4.0/EffectHalHidl.h rename to media/libaudiohal/impl/EffectHalHidl.h index 63310b19cc..04f40d36f8 100644 --- a/media/libaudiohal/4.0/EffectHalHidl.h +++ b/media/libaudiohal/impl/EffectHalHidl.h @@ -17,22 +17,23 @@ #ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_H #define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H +#include #include #include #include #include #include -using ::android::hardware::audio::effect::V4_0::EffectBufferConfig; -using ::android::hardware::audio::effect::V4_0::EffectConfig; -using ::android::hardware::audio::effect::V4_0::EffectDescriptor; -using ::android::hardware::audio::effect::V4_0::IEffect; -using EffectResult = ::android::hardware::audio::effect::V4_0::Result; +using ::android::hardware::audio::effect::CPP_VERSION::EffectBufferConfig; +using ::android::hardware::audio::effect::CPP_VERSION::EffectConfig; +using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor; +using ::android::hardware::audio::effect::CPP_VERSION::IEffect; +using EffectResult = ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; namespace android { -namespace V4_0 { +namespace CPP_VERSION { class EffectHalHidl : public EffectHalInterface { @@ -104,7 +105,7 @@ class EffectHalHidl : public EffectHalInterface status_t setProcessBuffers(); }; -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp similarity index 94% rename from media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp rename to media/libaudiohal/impl/EffectsFactoryHalHidl.cpp index ade2574325..b8804332cd 100644 --- a/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp @@ -25,14 +25,14 @@ #include "EffectHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::common::V2_0::HidlUtils; -using ::android::hardware::audio::common::V2_0::Uuid; -using ::android::hardware::audio::effect::V2_0::IEffect; -using ::android::hardware::audio::effect::V2_0::Result; +using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; +using ::android::hardware::audio::common::CPP_VERSION::Uuid; +using ::android::hardware::audio::effect::CPP_VERSION::IEffect; +using ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::Return; namespace android { -namespace V2_0 { +namespace CPP_VERSION { EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { mEffectsFactory = IEffectsFactory::getService(); @@ -145,5 +145,5 @@ status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, } -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/2.0/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h similarity index 88% rename from media/libaudiohal/2.0/EffectsFactoryHalHidl.h rename to media/libaudiohal/impl/EffectsFactoryHalHidl.h index c7c37c2cf3..c6fced7104 100644 --- a/media/libaudiohal/2.0/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h @@ -18,16 +18,18 @@ #define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H #include +#include #include +#include #include #include "ConversionHelperHidl.h" namespace android { -namespace V2_0 { +namespace CPP_VERSION { -using ::android::hardware::audio::effect::V2_0::EffectDescriptor; -using ::android::hardware::audio::effect::V2_0::IEffectsFactory; +using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor; +using ::android::hardware::audio::effect::CPP_VERSION::IEffectsFactory; using ::android::hardware::hidl_vec; class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl @@ -69,7 +71,7 @@ sp createEffectsFactoryHal() { return new EffectsFactoryHalHidl(); } -} // namespace V2_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp similarity index 93% rename from media/libaudiohal/4.0/StreamHalHidl.cpp rename to media/libaudiohal/impl/StreamHalHidl.cpp index 250dcc2049..b23e01869b 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "StreamHalHidl" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -27,31 +28,33 @@ #include "StreamHalHidl.h" #include "VersionUtils.h" -using ::android::hardware::audio::common::V4_0::AudioChannelMask; -using ::android::hardware::audio::common::V4_0::AudioFormat; -using ::android::hardware::audio::common::V4_0::ThreadInfo; -using ::android::hardware::audio::V4_0::AudioDrain; -using ::android::hardware::audio::V4_0::IStreamOutCallback; -using ::android::hardware::audio::V4_0::MessageQueueFlagBits; -using ::android::hardware::audio::V4_0::MmapBufferInfo; -using ::android::hardware::audio::V4_0::MmapPosition; -using ::android::hardware::audio::V4_0::ParameterValue; -using ::android::hardware::audio::V4_0::Result; -using ::android::hardware::audio::V4_0::TimeSpec; +using ::android::hardware::audio::common::CPP_VERSION::AudioChannelMask; +using ::android::hardware::audio::common::CPP_VERSION::AudioFormat; +using ::android::hardware::audio::common::CPP_VERSION::ThreadInfo; +using ::android::hardware::audio::CPP_VERSION::AudioDrain; +using ::android::hardware::audio::CPP_VERSION::IStreamOutCallback; +using ::android::hardware::audio::CPP_VERSION::MessageQueueFlagBits; +using ::android::hardware::audio::CPP_VERSION::MmapBufferInfo; +using ::android::hardware::audio::CPP_VERSION::MmapPosition; +using ::android::hardware::audio::CPP_VERSION::ParameterValue; +using ::android::hardware::audio::CPP_VERSION::Result; +using ::android::hardware::audio::CPP_VERSION::TimeSpec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; using ::android::hardware::Void; -using ReadCommand = ::android::hardware::audio::V4_0::IStreamIn::ReadCommand; +using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand; -using ::android::hardware::audio::common::V4_0::AudioContentType; -using ::android::hardware::audio::common::V4_0::AudioSource; -using ::android::hardware::audio::common::V4_0::AudioUsage; -using ::android::hardware::audio::V4_0::MicrophoneInfo; -using ::android::hardware::audio::V4_0::PlaybackTrackMetadata; -using ::android::hardware::audio::V4_0::RecordTrackMetadata; +#if MAJOR_VERSION == 4 +using ::android::hardware::audio::common::CPP_VERSION::AudioContentType; +using ::android::hardware::audio::common::CPP_VERSION::AudioSource; +using ::android::hardware::audio::common::CPP_VERSION::AudioUsage; +using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; +using ::android::hardware::audio::CPP_VERSION::PlaybackTrackMetadata; +using ::android::hardware::audio::CPP_VERSION::RecordTrackMetadata; +#endif namespace android { -namespace V4_0 { +namespace CPP_VERSION { StreamHalHidl::StreamHalHidl(IStream *stream) : ConversionHelperHidl("Stream"), @@ -567,6 +570,12 @@ status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct time } } +#if MAJOR_VERSION == 2 +status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& /* sourceMetadata */) { + // Audio HAL V2.0 does not support propagating source metadata + return INVALID_OPERATION; +} +#elif MAJOR_VERSION == 4 /** Transform a standard collection to an HIDL vector. */ template static auto transformToHidlVec(const Values& values, ElementConverter converter) { @@ -577,7 +586,7 @@ static auto transformToHidlVec(const Values& values, ElementConverter converter) } status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& sourceMetadata) { - hardware::audio::V4_0::SourceMetadata halMetadata = { + hardware::audio::CPP_VERSION::SourceMetadata halMetadata = { .tracks = transformToHidlVec(sourceMetadata.tracks, [](const playback_track_metadata& metadata) -> PlaybackTrackMetadata { return { @@ -588,6 +597,7 @@ status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& sourceMeta })}; return processReturn("updateSourceMetadata", mStream->updateSourceMetadata(halMetadata)); } +#endif void StreamOutHalHidl::onWriteReady() { sp callback = mCallback.promote(); @@ -783,6 +793,19 @@ status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { } } +#if MAJOR_VERSION == 2 +status_t StreamInHalHidl::getActiveMicrophones( + std::vector *microphones __unused) { + if (mStream == 0) return NO_INIT; + return INVALID_OPERATION; +} + +status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& /* sinkMetadata */) { + // Audio HAL V2.0 does not support propagating sink metadata + return INVALID_OPERATION; +} + +#elif MAJOR_VERSION == 4 status_t StreamInHalHidl::getActiveMicrophones( std::vector *microphonesInfo) { if (!mStream) return NO_INIT; @@ -802,7 +825,7 @@ status_t StreamInHalHidl::getActiveMicrophones( } status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& sinkMetadata) { - hardware::audio::V4_0::SinkMetadata halMetadata = { + hardware::audio::CPP_VERSION::SinkMetadata halMetadata = { .tracks = transformToHidlVec(sinkMetadata.tracks, [](const record_track_metadata& metadata) -> RecordTrackMetadata { return { @@ -812,6 +835,7 @@ status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& sinkMetadata) { })}; return processReturn("updateSinkMetadata", mStream->updateSinkMetadata(halMetadata)); } +#endif -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/4.0/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h similarity index 92% rename from media/libaudiohal/4.0/StreamHalHidl.h rename to media/libaudiohal/impl/StreamHalHidl.h index d0948188ed..95ec7f1d17 100644 --- a/media/libaudiohal/4.0/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -19,8 +19,11 @@ #include +#include #include +#include #include +#include #include #include #include @@ -29,19 +32,19 @@ #include "ConversionHelperHidl.h" #include "StreamPowerLog.h" -using ::android::hardware::audio::V4_0::IStream; -using ::android::hardware::audio::V4_0::IStreamIn; -using ::android::hardware::audio::V4_0::IStreamOut; +using ::android::hardware::audio::CPP_VERSION::IStream; +using ::android::hardware::audio::CPP_VERSION::IStreamIn; +using ::android::hardware::audio::CPP_VERSION::IStreamOut; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; using ::android::hardware::Return; -using ReadParameters = ::android::hardware::audio::V4_0::IStreamIn::ReadParameters; -using ReadStatus = ::android::hardware::audio::V4_0::IStreamIn::ReadStatus; -using WriteCommand = ::android::hardware::audio::V4_0::IStreamOut::WriteCommand; -using WriteStatus = ::android::hardware::audio::V4_0::IStreamOut::WriteStatus; +using ReadParameters = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadParameters; +using ReadStatus = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadStatus; +using WriteCommand = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteCommand; +using WriteStatus = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteStatus; namespace android { -namespace V4_0 { +namespace CPP_VERSION { class DeviceHalHidl; @@ -244,7 +247,7 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { status_t prepareForReading(size_t bufferSize); }; -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H diff --git a/media/libaudiohal/4.0/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp similarity index 97% rename from media/libaudiohal/4.0/StreamHalLocal.cpp rename to media/libaudiohal/impl/StreamHalLocal.cpp index e9d96bf9fe..b134f5797c 100644 --- a/media/libaudiohal/4.0/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -25,7 +25,7 @@ #include "VersionUtils.h" namespace android { -namespace V4_0 { +namespace CPP_VERSION { StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) : mDevice(device), @@ -341,6 +341,12 @@ status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) return mStream->get_mmap_position(mStream, position); } +#if MAJOR_VERSION == 2 +status_t StreamInHalLocal::getActiveMicrophones( + std::vector *microphones __unused) { + return INVALID_OPERATION; +} +#elif MAJOR_VERSION == 4 status_t StreamInHalLocal::getActiveMicrophones(std::vector *microphones) { if (mStream->get_active_microphones == NULL) return INVALID_OPERATION; size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; @@ -352,6 +358,7 @@ status_t StreamInHalLocal::getActiveMicrophones(std::vector namespace android { -namespace V4_0 { +namespace CPP_VERSION { class StreamPowerLog { public: @@ -98,7 +98,7 @@ private: size_t mFrameSize; }; -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_STREAM_POWER_LOG_H diff --git a/media/libaudiohal/impl/VersionMacro.h b/media/libaudiohal/impl/VersionMacro.h new file mode 100644 index 0000000000..98e9c07eed --- /dev/null +++ b/media/libaudiohal/impl/VersionMacro.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_VERSION_MACRO_H +#define ANDROID_HARDWARE_VERSION_MACRO_H + +#if !defined(MAJOR_VERSION) || !defined(MINOR_VERSION) +#error "MAJOR_VERSION and MINOR_VERSION must be defined" +#endif + +#define CONCAT_3(a,b,c) a##b##c +#define EXPAND_CONCAT_3(a,b,c) CONCAT_3(a,b,c) +/** The directory name of the version: . */ +#define FILE_VERSION EXPAND_CONCAT_3(MAJOR_VERSION,.,MINOR_VERSION) + +#define CONCAT_4(a,b,c,d) a##b##c##d +#define EXPAND_CONCAT_4(a,b,c,d) CONCAT_4(a,b,c,d) +/** The c++ namespace of the version: V_ */ +#define CPP_VERSION EXPAND_CONCAT_4(V,MAJOR_VERSION,_,MINOR_VERSION) + +#endif // ANDROID_HARDWARE_VERSION_MACRO_H diff --git a/media/libaudiohal/4.0/VersionUtils.h b/media/libaudiohal/impl/VersionUtils.h similarity index 67% rename from media/libaudiohal/4.0/VersionUtils.h rename to media/libaudiohal/impl/VersionUtils.h index 4c5b7d9a7a..500489594f 100644 --- a/media/libaudiohal/4.0/VersionUtils.h +++ b/media/libaudiohal/impl/VersionUtils.h @@ -17,19 +17,33 @@ #ifndef ANDROID_HARDWARE_VERSION_UTILS_H #define ANDROID_HARDWARE_VERSION_UTILS_H +#include #include #include -using ::android::hardware::audio::V4_0::ParameterValue; -using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::audio::CPP_VERSION::ParameterValue; +using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::Return; using ::android::hardware::hidl_vec; using ::android::hardware::hidl_string; namespace android { -namespace V4_0 { +namespace CPP_VERSION { namespace utils { +#if MAJOR_VERSION == 2 +template +Return getParameters(T& object, hidl_vec /*context*/, + hidl_vec keys, Callback callback) { + return object->getParameters(keys, callback); +} + +template +Return setParameters(T& object, hidl_vec /*context*/, + hidl_vec keys) { + return object->setParameters(keys); +} +#elif MAJOR_VERSION == 4 template Return getParameters(T& object, hidl_vec context, hidl_vec keys, Callback callback) { @@ -41,9 +55,10 @@ Return setParameters(T& object, hidl_vec context, hidl_vec keys) { return object->setParameters(context, keys); } +#endif } // namespace utils -} // namespace V4_0 +} // namespace CPP_VERSION } // namespace android #endif // ANDROID_HARDWARE_VERSION_UTILS_H diff --git a/media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h similarity index 78% rename from media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h rename to media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h index 95335c0411..fa0effc719 100644 --- a/media/libaudiohal/2.0/include/libaudiohal/2.0/FactoryHalHidl.h +++ b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H -#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H +#ifndef ANDROID_HARDWARE_FACTORY_HAL_HIDL_H +#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_H /** @file Library entry points to create the HAL factories. */ @@ -24,13 +24,17 @@ #include namespace android { -namespace V2_0 { +namespace V2_0 { sp createEffectsFactoryHal(); +sp createDevicesFactoryHal(); +} // namespace V2_0 +namespace V4_0 { +sp createEffectsFactoryHal(); sp createDevicesFactoryHal(); +} // namespace V4_0 -} // namespace V2_0 } // namespace android -#endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_V2_0_H +#endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_H -- GitLab From 2e2c0bbe19c4cc50ee9ae8bef7955229fe2fb058 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 11 Jun 2018 19:13:11 -0700 Subject: [PATCH 0095/1530] Timestamp: Collect and dump statistics Test: Audioflinger dumpsys Bug: 80502521 Change-Id: I8c7c8b0c2f0f51dc898ed96f6a973e20490022f1 --- services/audioflinger/AudioFlinger.h | 1 + services/audioflinger/FastMixer.cpp | 74 +++++++++++--------- services/audioflinger/FastMixerDumpState.cpp | 1 + services/audioflinger/FastMixerDumpState.h | 4 ++ services/audioflinger/Threads.cpp | 34 ++++++++- services/audioflinger/Threads.h | 3 + 6 files changed, 82 insertions(+), 35 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index a59c13ee1b..0276cadec2 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -63,6 +63,7 @@ #include #include +#include #include "FastCapture.h" #include "FastMixer.h" diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index caeede9263..ad3526498d 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -336,13 +336,15 @@ void FastMixer::onWork() { // TODO: pass an ID parameter to indicate which time series we want to write to in NBLog.cpp // Or: pass both of these into a single call with a boolean + const FastMixerState * const current = (const FastMixerState *) mCurrent; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; + if (mIsWarm) { LOG_HIST_TS(); } else { + dumpState->mTimestampVerifier.discontinuity(); LOG_AUDIO_STATE(); } - const FastMixerState * const current = (const FastMixerState *) mCurrent; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; const FastMixerState::Command command = mCommand; const size_t frameCount = current->mFrameCount; @@ -477,39 +479,47 @@ void FastMixer::onWork() mAttemptedWrite = true; // FIXME count # of writes blocked excessively, CPU usage, etc. for dump - ExtendedTimestamp timestamp; // local - status_t status = mOutputSink->getTimestamp(timestamp); - if (status == NO_ERROR) { - const int64_t totalNativeFramesPresented = - timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]; - if (totalNativeFramesPresented <= mTotalNativeFramesWritten) { - mNativeFramesWrittenButNotPresented = - mTotalNativeFramesWritten - totalNativeFramesPresented; - mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = + if (mIsWarm) { + ExtendedTimestamp timestamp; // local + status_t status = mOutputSink->getTimestamp(timestamp); + if (status == NO_ERROR) { + dumpState->mTimestampVerifier.add( + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], + timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], + mSampleRate); + const int64_t totalNativeFramesPresented = timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]; - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = - timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; - // We don't compensate for server - kernel time difference and - // only update latency if we have valid info. - dumpState->mLatencyMs = - (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate; + if (totalNativeFramesPresented <= mTotalNativeFramesWritten) { + mNativeFramesWrittenButNotPresented = + mTotalNativeFramesWritten - totalNativeFramesPresented; + mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]; + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = + timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; + // We don't compensate for server - kernel time difference and + // only update latency if we have valid info. + dumpState->mLatencyMs = + (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate; + } else { + // HAL reported that more frames were presented than were written + mNativeFramesWrittenButNotPresented = 0; + status = INVALID_OPERATION; + } } else { - // HAL reported that more frames were presented than were written - mNativeFramesWrittenButNotPresented = 0; - status = INVALID_OPERATION; + dumpState->mTimestampVerifier.error(); + } + if (status == NO_ERROR) { + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; + } else { + // fetch server time if we can't get timestamp + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = + systemTime(SYSTEM_TIME_MONOTONIC); + // clear out kernel cached position as this may get rapidly stale + // if we never get a new valid timestamp + mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0; + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1; } - } - if (status == NO_ERROR) { - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; - } else { - // fetch server time if we can't get timestamp - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = - systemTime(SYSTEM_TIME_MONOTONIC); - // clear out kernel cached position as this may get rapidly stale - // if we never get a new valid timestamp - mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0; - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1; } } } diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index b3a25203e6..d60643c99f 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -73,6 +73,7 @@ void FastMixerDumpState::dump(int fd) const mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, mixPeriodSec * 1e3, mLatencyMs); + dprintf(fd, " FastMixer Timestamp stats: %s\n", mTimestampVerifier.toString().c_str()); #ifdef FAST_THREAD_STATISTICS // find the interval of valid samples uint32_t bounds = mBounds; diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index aed6bc598c..9b91cbcade 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H #include +#include #include "Configuration.h" #include "FastThreadDumpState.h" #include "FastMixerState.h" @@ -75,6 +76,9 @@ struct FastMixerDumpState : FastThreadDumpState { size_t mFrameCount; uint32_t mTrackMask; // mask of active tracks FastTrackDump mTracks[FastMixerState::kMaxFastTracks]; + + // For timestamp statistics. + TimestampVerifier mTimestampVerifier; }; } // android diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 65bbd8d302..c2a2fe0959 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -853,6 +853,14 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u dprintf(fd, " Input device: %#x (%s)\n", mInDevice, devicesToString(mInDevice).c_str()); dprintf(fd, " Audio source: %d (%s)\n", mAudioSource, sourceToString(mAudioSource)); + // Dump timestamp statistics for the Thread types that support it. + if (mType == RECORD + || mType == MIXER + || mType == DUPLICATING + || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) { + dprintf(fd, " Timestamp stats: %s\n", mTimestampVerifier.toString().c_str()); + } + if (locked) { mLock.unlock(); } @@ -3205,12 +3213,21 @@ bool AudioFlinger::PlaybackThread::threadLoop() logString = NULL; } + // Collect timestamp statistics for the Playback Thread types that support it. + if (mType == MIXER + || mType == DUPLICATING + || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) { // no indentation // Gather the framesReleased counters for all active tracks, // and associate with the sink frames written out. We need // this to convert the sink timestamp to the track timestamp. bool kernelLocationUpdate = false; ExtendedTimestamp timestamp; // use private copy to fetch - if (threadloop_getHalTimestamp_l(×tamp) == OK) { + if (mStandby) { + mTimestampVerifier.discontinuity(); + } else if (threadloop_getHalTimestamp_l(×tamp) == OK) { + mTimestampVerifier.add(timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], + timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], + mSampleRate); // We always fetch the timestamp here because often the downstream // sink will block while writing. @@ -3241,7 +3258,10 @@ bool AudioFlinger::PlaybackThread::threadLoop() + mSuspendedFrames; // add frames discarded when suspended mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; + } else { + mTimestampVerifier.error(); } + // mFramesWritten for non-offloaded tracks are contiguous // even after standby() is called. This is useful for the track frame // to sink frame mapping. @@ -3274,6 +3294,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } } + } // if (mType ... ) { // no indentation #if 0 // logFormat example if (z % 100 == 0) { @@ -6730,8 +6751,9 @@ reacquire_wakelock: // Update server timestamp with kernel stats if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) { int64_t position, time; - int ret = mInput->stream->getCapturePosition(&position, &time); - if (ret == NO_ERROR) { + if (mStandby) { + mTimestampVerifier.discontinuity(); + } else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR) { mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = position; mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = time; // Note: In general record buffers should tend to be empty in @@ -6739,6 +6761,12 @@ reacquire_wakelock: // // Also, it is not advantageous to call get_presentation_position during the read // as the read obtains a lock, preventing the timestamp call from executing. + + mTimestampVerifier.add(mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], + mSampleRate); + } else { + mTimestampVerifier.error(); } } // Use this to track timestamp information diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index b691ca9612..064e29178c 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -499,6 +499,9 @@ protected: sp mNBLogWriter; bool mSystemReady; ExtendedTimestamp mTimestamp; + TimestampVerifier< // For timestamp statistics. + int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier; + // A condition that must be evaluated by the thread loop has changed and // we must not wait for async write callback in the thread loop before evaluating it bool mSignalPending; -- GitLab From ecd3616e7bcf083825b8fabc9e32224c1768776d Mon Sep 17 00:00:00 2001 From: Umang Saini Date: Fri, 30 Mar 2018 16:43:26 +0530 Subject: [PATCH 0096/1530] codec2: add support for webm and 3gpp output format Test: setprop debug.stagefright.ccodec yes Test: screenrecord --mime-type video/x-vnd.on2.vp8 --codec-name c2.google.vp8.encoder --output-format webm /sdcard/record.webm Test: screenrecord --mime-type video/x-vnd.on2.vp9 --codec-name c2.google.vp9.encoder --output-format webm /sdcard/record.webm Test: screenrecord --size 176x144 --mime-type video/3gpp --codec-name c2.google.h263.encoder --output-format 3gpp /sdcard/record.3gpp Change-Id: I2c017a231a0f76a03aff0758c57441275c7bd6d5 --- cmds/screenrecord/screenrecord.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index 46035159ee..a7ac2d7c55 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -73,7 +73,7 @@ static bool gRotate = false; // rotate 90 degrees static bool gMonotonicTime = false; // use system monotonic time for timestamps static bool gPersistentSurface = false; // use persistent surface static enum { - FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES + FORMAT_MP4, FORMAT_H264, FORMAT_WEBM, FORMAT_3GPP, FORMAT_FRAMES, FORMAT_RAW_FRAMES } gOutputFormat = FORMAT_MP4; // data format for output static AString gCodecName = ""; // codec name override static bool gSizeSpecified = false; // was size explicitly requested? @@ -669,7 +669,9 @@ static status_t recordScreen(const char* fileName) { sp muxer = NULL; FILE* rawFp = NULL; switch (gOutputFormat) { - case FORMAT_MP4: { + case FORMAT_MP4: + case FORMAT_WEBM: + case FORMAT_3GPP: { // Configure muxer. We have to wait for the CSD blob from the encoder // before we can start it. err = unlink(fileName); @@ -682,7 +684,13 @@ static status_t recordScreen(const char* fileName) { fprintf(stderr, "ERROR: couldn't open file\n"); abort(); } - muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4); + if (gOutputFormat == FORMAT_MP4) { + muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4); + } else if (gOutputFormat == FORMAT_WEBM) { + muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_WEBM); + } else { + muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_THREE_GPP); + } close(fd); if (gRotate) { muxer->setOrientationHint(90); // TODO: does this do anything? @@ -1002,6 +1010,10 @@ int main(int argc, char* const argv[]) { gOutputFormat = FORMAT_MP4; } else if (strcmp(optarg, "h264") == 0) { gOutputFormat = FORMAT_H264; + } else if (strcmp(optarg, "webm") == 0) { + gOutputFormat = FORMAT_WEBM; + } else if (strcmp(optarg, "3gpp") == 0) { + gOutputFormat = FORMAT_3GPP; } else if (strcmp(optarg, "frames") == 0) { gOutputFormat = FORMAT_FRAMES; } else if (strcmp(optarg, "raw-frames") == 0) { -- GitLab From 49a9c182b357c8d37488e80db8c31850ec7359cf Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 20 Jun 2018 12:49:57 -0700 Subject: [PATCH 0097/1530] Sort extractor list Use std::list instead of List and sort the extractor list so that the order in which they are called is deterministic across builds. Bug: 110230427 Test: manual Change-Id: I614e33e388e74cdab0d77a7721ca75c78d968db1 --- media/libstagefright/MediaExtractorFactory.cpp | 18 ++++++++++++------ .../media/stagefright/MediaExtractorFactory.h | 9 ++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index f6c61a0081..2d4bd39da1 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -123,7 +123,7 @@ struct ExtractorPlugin : public RefBase { }; Mutex MediaExtractorFactory::gPluginMutex; -std::shared_ptr>> MediaExtractorFactory::gPlugins; +std::shared_ptr>> MediaExtractorFactory::gPlugins; bool MediaExtractorFactory::gPluginsRegistered = false; // static @@ -133,7 +133,7 @@ MediaExtractor::CreatorFunc MediaExtractorFactory::sniff( *confidence = 0.0f; *meta = nullptr; - std::shared_ptr>> plugins; + std::shared_ptr>> plugins; { Mutex::Autolock autoLock(gPluginMutex); if (!gPluginsRegistered) { @@ -145,6 +145,7 @@ MediaExtractor::CreatorFunc MediaExtractorFactory::sniff( MediaExtractor::CreatorFunc curCreator = NULL; MediaExtractor::CreatorFunc bestCreator = NULL; for (auto it = plugins->begin(); it != plugins->end(); ++it) { + ALOGV("sniffing %s", (*it)->def.extractor_name); float newConfidence; void *newMeta = nullptr; MediaExtractor::FreeMetaFunc newFreeMeta = nullptr; @@ -171,7 +172,7 @@ MediaExtractor::CreatorFunc MediaExtractorFactory::sniff( // static void MediaExtractorFactory::RegisterExtractor(const sp &plugin, - List> &pluginList) { + std::list> &pluginList) { // sanity check check struct version, uuid, name if (plugin->def.def_version == 0 || plugin->def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) { @@ -213,7 +214,7 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, //static void MediaExtractorFactory::RegisterExtractorsInApk( - const char *apkPath, List> &pluginList) { + const char *apkPath, std::list> &pluginList) { ALOGV("search for plugins at %s", apkPath); ZipArchiveHandle zipHandle; int32_t ret = OpenArchive(apkPath, &zipHandle); @@ -261,7 +262,7 @@ void MediaExtractorFactory::RegisterExtractorsInApk( //static void MediaExtractorFactory::RegisterExtractorsInSystem( - const char *libDirPath, List> &pluginList) { + const char *libDirPath, std::list> &pluginList) { ALOGV("search for plugins at %s", libDirPath); DIR *libDir = opendir(libDirPath); if (libDir) { @@ -291,6 +292,10 @@ void MediaExtractorFactory::RegisterExtractorsInSystem( } } +static bool compareFunc(const sp& first, const sp& second) { + return strcmp(first->def.extractor_name, second->def.extractor_name) < 0; +} + // static void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { Mutex::Autolock autoLock(gPluginMutex); @@ -301,7 +306,7 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { return; } - std::shared_ptr>> newList(new List>()); + std::shared_ptr>> newList(new std::list>()); RegisterExtractorsInSystem("/system/lib" #ifdef __LP64__ @@ -319,6 +324,7 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { RegisterExtractorsInApk(newUpdateApkPath, *newList); } + newList->sort(compareFunc); gPlugins = newList; gPluginsRegistered = true; } diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index fb9f5bd38e..d5f4b35446 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -22,7 +22,6 @@ #include #include -#include namespace android { @@ -40,15 +39,15 @@ public: private: static Mutex gPluginMutex; - static std::shared_ptr>> gPlugins; + static std::shared_ptr>> gPlugins; static bool gPluginsRegistered; static void RegisterExtractorsInApk( - const char *apkPath, List> &pluginList); + const char *apkPath, std::list> &pluginList); static void RegisterExtractorsInSystem( - const char *libDirPath, List> &pluginList); + const char *libDirPath, std::list> &pluginList); static void RegisterExtractor( - const sp &plugin, List> &pluginList); + const sp &plugin, std::list> &pluginList); static MediaExtractor::CreatorFunc sniff(DataSourceBase *source, float *confidence, void **meta, MediaExtractor::FreeMetaFunc *freeMeta, -- GitLab From c0fa2711b476a6550cc657f0a0b7b9df532df6bc Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 20 Jun 2018 16:39:58 -0700 Subject: [PATCH 0098/1530] Add a null checking for heic thumbnail data. Test: run poc with and without the patch Bug: 79944875 Change-Id: Ie3a9610c19baef65f4a5c7e7f6b8e051b558d663 --- media/extractors/mp4/ItemTable.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index ca9deab13f..be442e6fec 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -1529,12 +1529,16 @@ sp ItemTable::getImageMeta(const uint32_t imageIndex) { if (thumbItemIndex >= 0) { const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex]; - meta->setInt32(kKeyThumbnailWidth, thumbnail.width); - meta->setInt32(kKeyThumbnailHeight, thumbnail.height); - meta->setData(kKeyThumbnailHVCC, kTypeHVCC, - thumbnail.hvcc->data(), thumbnail.hvcc->size()); - ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd", - imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex); + if (thumbnail.hvcc != NULL) { + meta->setInt32(kKeyThumbnailWidth, thumbnail.width); + meta->setInt32(kKeyThumbnailHeight, thumbnail.height); + meta->setData(kKeyThumbnailHVCC, kTypeHVCC, + thumbnail.hvcc->data(), thumbnail.hvcc->size()); + ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd", + imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex); + } else { + ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex); + } } else { ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__); } -- GitLab From 1f043e44864433e198be556ae1282d11720c741c Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 20 Jun 2018 16:52:50 -0700 Subject: [PATCH 0099/1530] mediaplayer2: rename getMediaPlayer2State to getState Test: MediaPlayer2Test Bug: 109928575 Change-Id: Ib357cdd82bde0b298c5d9e749267737bd0cd3af3 --- .../include/mediaplayer2/MediaPlayer2Types.h | 10 +++++----- .../include/mediaplayer2/mediaplayer2.h | 2 +- media/libmediaplayer2/mediaplayer2.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h index 3905b55b62..2fb5a2c7a2 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h @@ -150,11 +150,11 @@ enum media2_info_type { // Do not change these values without updating their counterparts in MediaPlayer2.java enum mediaplayer2_states { - MEDIAPLAYER2_STATE_IDLE = 1, - MEDIAPLAYER2_STATE_PREPARED = 2, - MEDIAPLAYER2_STATE_PLAYING = 3, - MEDIAPLAYER2_STATE_PAUSED = 4, - MEDIAPLAYER2_STATE_ERROR = 5, + MEDIAPLAYER2_STATE_IDLE = 1001, + MEDIAPLAYER2_STATE_PREPARED = 1002, + MEDIAPLAYER2_STATE_PLAYING = 1003, + MEDIAPLAYER2_STATE_PAUSED = 1004, + MEDIAPLAYER2_STATE_ERROR = 1005, }; enum media_player2_internal_states { diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index d5861928b4..3af212e054 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -68,7 +68,7 @@ public: status_t stop(); status_t pause(); bool isPlaying(); - mediaplayer2_states getMediaPlayer2State(); + mediaplayer2_states getState(); status_t setPlaybackSettings(const AudioPlaybackRate& rate); status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */); status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index e5567dcf39..4fb47b8f3c 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -860,7 +860,7 @@ bool MediaPlayer2::isPlaying() { return false; } -mediaplayer2_states MediaPlayer2::getMediaPlayer2State() { +mediaplayer2_states MediaPlayer2::getState() { Mutex::Autolock _l(mLock); if (mCurrentState & MEDIA_PLAYER2_STATE_ERROR) { return MEDIAPLAYER2_STATE_ERROR; -- GitLab From 3e7eb5eb24e6bf9a31495dee5e5f8252728a2b7d Mon Sep 17 00:00:00 2001 From: Yung Ti Su Date: Wed, 13 Jun 2018 11:48:42 +0800 Subject: [PATCH 0100/1530] audiopolicy: fix for ringtone and offload music playback concurrency Root cause: music output is still active when setphonestate and will be reevaluate routing and change back to normal speaker. follow the change of https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/av/+/4292147 Bug: 110072502 Test: self-tested over 30 times. Change-Id: Iec1c85f9cd24e3eae149fa27ad8d1ec2b23019e0 Signed-off-by: Yung Ti Su --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7154cb2e50..958ac3014c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4842,7 +4842,7 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp Date: Mon, 18 Jun 2018 15:33:03 -0700 Subject: [PATCH 0101/1530] setPreferedDevice considered ahead of Dynamic Rules Audio policy is updated to allow an applications expressed preference for a specific audio device to override dynamic policy rules, just as it already overrides the rest of the policy engine's logic. This is important in allowing 1st party automotive applications to explicitly targets specific audio buses. Test: build and run on Mojave Change-Id: I482b74cba40f6d6150f69856769a998158a54a94 --- .../managerdefault/AudioPolicyManager.cpp | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7154cb2e50..c22e795187 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -813,39 +813,53 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, stream_type_to_audio_attributes(*stream, &attributes); } + ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" + " session %d selectedDeviceId %d", + attributes.usage, attributes.content_type, attributes.tags, attributes.flags, + session, *selectedDeviceId); + // TODO: check for existing client for this port ID if (*portId == AUDIO_PORT_HANDLE_NONE) { *portId = AudioPort::getNextUniqueId(); } - sp desc; - if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) { - ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); - if (!audio_has_proportional_frames(config->format)) { + // First check for explicit routing (eg. setPreferredDevice) + sp deviceDesc; + if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { + deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId); + } else { + // If no explict route, is there a matching dynamic policy that applies? + sp desc; + if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) { + ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); + if (!audio_has_proportional_frames(config->format)) { + return BAD_VALUE; + } + *stream = streamTypefromAttributesInt(&attributes); + *output = desc->mIoHandle; + ALOGV("getOutputForAttr() returns output %d", *output); + return NO_ERROR; + } + + // Virtual sources must always be dynamicaly or explicitly routed + if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { + ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); return BAD_VALUE; } - *stream = streamTypefromAttributesInt(&attributes); - *output = desc->mIoHandle; - ALOGV("getOutputForAttr() returns output %d", *output); - return NO_ERROR; } + + // Virtual sources must always be dynamicaly or explicitly routed if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); return BAD_VALUE; } - ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" - " session %d selectedDeviceId %d", - attributes.usage, attributes.content_type, attributes.tags, attributes.flags, - session, *selectedDeviceId); - *stream = streamTypefromAttributesInt(&attributes); - // Explicit routing? - sp deviceDesc; - if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { - deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId); - } + // TODO: Should this happen only if an explicit route is active? + // the previous code structure meant that this would always happen which + // would appear to result in adding a null deviceDesc when not using an + // explicit route. Is that the intended and necessary behavior? mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid); routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); -- GitLab From fffc153839e9a7fc6ac222849ec89dc64d121397 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 25 Jun 2018 15:23:31 -0700 Subject: [PATCH 0102/1530] Add verbose option to stagefright command Currently this prints out the format of each buffer being read from the input during 'dump' operation. Change-Id: I44e59b96bfb07181d03903dab30c1b65f5eb6fcd --- cmds/stagefright/stagefright.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 2c088e631d..85aab57a95 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -78,6 +78,7 @@ static bool gForceToUseHardwareCodec; static bool gPlaybackAudio; static bool gWriteMP4; static bool gDisplayHistogram; +static bool gVerbose = false; static bool showProgress = true; static String8 gWriteMP4Filename; static String8 gComponentNameOverride; @@ -159,6 +160,11 @@ static void dumpSource(const sp &source, const String8 &filename) { break; } + if (gVerbose) { + MetaDataBase &meta = mbuf->meta_data(); + fprintf(stdout, "sample format: %s\n", meta.toString().c_str()); + } + CHECK_EQ( fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(), 1, @@ -630,6 +636,7 @@ static void usage(const char *me) { fprintf(stderr, " -T allocate buffers from a surface texture\n"); fprintf(stderr, " -d(ump) output_filename (raw stream data to a file)\n"); fprintf(stderr, " -D(ump) output_filename (decoded PCM data to a file)\n"); + fprintf(stderr, " -v be more verbose\n"); } static void dumpCodecProfiles(bool queryDecoders) { @@ -708,7 +715,7 @@ int main(int argc, char **argv) { sp looper; int res; - while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) { + while ((res = getopt(argc, argv, "vhaqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) { switch (res) { case 'a': { @@ -832,6 +839,12 @@ int main(int argc, char **argv) { break; } + case 'v': + { + gVerbose = true; + break; + } + case '?': case 'h': default: -- GitLab From 975a39e9067e8d8f71e74813d0199e0e51556bb8 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 27 Jun 2018 14:35:46 -0700 Subject: [PATCH 0103/1530] Camera: Handle duplicate camera Id due to openLegacy support When HAL supports openLegacy, same camera id can be added more than once. Because we now use vector to store API1 compatible camera ids, make sure no duplicate strings are added. Test: Vendor camera app using openLegacy, Camera CTS Bug: 110815837 Change-Id: I327744e102cc75929ebcdf51661f9f4ec7f3acf9 --- .../libcameraservice/common/CameraProviderManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 43f1a91fd2..0ce4318e7f 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -631,7 +631,12 @@ status_t CameraProviderManager::ProviderInfo::addDevice(const std::string& name, mUniqueCameraIds.insert(id); if (isAPI1Compatible) { - mUniqueAPI1CompatibleCameraIds.push_back(id); + // addDevice can be called more than once for the same camera id if HAL + // supports openLegacy. + if (std::find(mUniqueAPI1CompatibleCameraIds.begin(), mUniqueAPI1CompatibleCameraIds.end(), + id) == mUniqueAPI1CompatibleCameraIds.end()) { + mUniqueAPI1CompatibleCameraIds.push_back(id); + } } if (parsedId != nullptr) { -- GitLab From 8144aeefd1d7d8d06612df31c3f7728c03d36000 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 28 Jun 2018 17:40:03 -0700 Subject: [PATCH 0104/1530] Remove AndroidRuntime::getJNIEnv() usage from libmediaplayer2 Test: pass MediaPlayer2Test#testPlayVideo Bug: 63934228 Change-Id: I29da97cdc458bca18d95f4da349c93382e722727 --- media/libmediaplayer2/Android.bp | 5 ++ media/libmediaplayer2/JAudioTrack.cpp | 60 +++++++++---------- media/libmediaplayer2/JavaVMHelper.cpp | 48 +++++++++++++++ .../include/mediaplayer2/JavaVMHelper.h | 40 +++++++++++++ 4 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 media/libmediaplayer2/JavaVMHelper.cpp create mode 100644 media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 1fa8789459..0fb5abc126 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -9,6 +9,7 @@ cc_library { srcs: [ "JAudioTrack.cpp", + "JavaVMHelper.cpp", "MediaPlayer2AudioOutput.cpp", "mediaplayer2.cpp", ], @@ -49,6 +50,10 @@ cc_library { "media_plugin_headers", ], + include_dirs: [ + "frameworks/base/core/jni", + ], + static_libs: [ "libmedia_helper", "libstagefright_nuplayer2", diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index ac0cc5775e..778ae1be8b 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -21,7 +21,7 @@ #include "mediaplayer2/JAudioTrack.h" #include -#include +#include namespace android { @@ -39,7 +39,7 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen const audio_attributes_t* pAttributes, // AudioAttributes float maxRequiredSpeed) { // bufferSizeInBytes - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack"); mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls); @@ -116,19 +116,19 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen } JAudioTrack::~JAudioTrack() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); env->DeleteGlobalRef(mAudioTrackCls); } size_t JAudioTrack::frameCount() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetBufferSizeInFrames = env->GetMethodID( mAudioTrackCls, "getBufferSizeInFrames", "()I"); return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames); } size_t JAudioTrack::channelCount() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I"); return env->CallIntMethod(mAudioTrackObj, jGetChannelCount); } @@ -143,7 +143,7 @@ status_t JAudioTrack::getPosition(uint32_t *position) { return BAD_VALUE; } - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetPlaybackHeadPosition = env->GetMethodID( mAudioTrackCls, "getPlaybackHeadPosition", "()I"); *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition); @@ -152,7 +152,7 @@ status_t JAudioTrack::getPosition(uint32_t *position) { } bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp"); jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls); @@ -189,7 +189,7 @@ status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) { status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) { // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks. // Should we do the same thing? - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams"); jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "", "()V"); @@ -224,7 +224,7 @@ status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) { } const AudioPlaybackRate JAudioTrack::getPlaybackRate() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetPlaybackParams = env->GetMethodID( mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;"); @@ -266,7 +266,7 @@ media::VolumeShaper::Status JAudioTrack::applyVolumeShaper( return media::VolumeShaper::Status(BAD_VALUE); } - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper", "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;"); @@ -282,7 +282,7 @@ media::VolumeShaper::Status JAudioTrack::applyVolumeShaper( } status_t JAudioTrack::setAuxEffectSendLevel(float level) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jSetAuxEffectSendLevel = env->GetMethodID( mAudioTrackCls, "setAuxEffectSendLevel", "(F)I"); int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level); @@ -290,14 +290,14 @@ status_t JAudioTrack::setAuxEffectSendLevel(float level) { } status_t JAudioTrack::attachAuxEffect(int effectId) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I"); int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId); return javaToNativeStatus(result); } status_t JAudioTrack::setVolume(float left, float right) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); // TODO: Java setStereoVolume is deprecated. Do we really need this method? jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I"); int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right); @@ -305,14 +305,14 @@ status_t JAudioTrack::setVolume(float left, float right) { } status_t JAudioTrack::setVolume(float volume) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I"); int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume); return javaToNativeStatus(result); } status_t JAudioTrack::start() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V"); // TODO: Should we catch the Java IllegalStateException from play()? env->CallVoidMethod(mAudioTrackObj, jPlay); @@ -324,7 +324,7 @@ ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) { return BAD_VALUE; } - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jbyteArray jAudioData = env->NewByteArray(size); env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer); @@ -353,7 +353,7 @@ ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) { } void JAudioTrack::stop() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V"); env->CallVoidMethod(mAudioTrackObj, jStop); // TODO: Should we catch IllegalStateException? @@ -365,20 +365,20 @@ bool JAudioTrack::stopped() const { } void JAudioTrack::flush() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V"); env->CallVoidMethod(mAudioTrackObj, jFlush); } void JAudioTrack::pause() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V"); env->CallVoidMethod(mAudioTrackObj, jPause); // TODO: Should we catch IllegalStateException? } bool JAudioTrack::isPlaying() const { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I"); int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState); @@ -393,7 +393,7 @@ bool JAudioTrack::isPlaying() const { } uint32_t JAudioTrack::getSampleRate() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I"); return env->CallIntMethod(mAudioTrackObj, jGetSampleRate); } @@ -403,7 +403,7 @@ status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) { return BAD_VALUE; } - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetBufferSizeInFrames = env->GetMethodID( mAudioTrackCls, "getBufferSizeInFrames", "()I"); int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames); @@ -417,7 +417,7 @@ status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) { } audio_format_t JAudioTrack::format() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I"); int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat); return audioFormatToNative(javaFormat); @@ -454,7 +454,7 @@ status_t JAudioTrack::dump(int fd, const Vector& args __unused) const } audio_port_handle_t JAudioTrack::getRoutedDeviceId() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice", "()Landroid/media/AudioDeviceInfo;"); jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice); @@ -469,14 +469,14 @@ audio_port_handle_t JAudioTrack::getRoutedDeviceId() { } audio_session_t JAudioTrack::getAudioSessionId() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I"); jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId); return (audio_session_t) sessionId; } status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl"); jmethodID jSetAudioOutputDeviceById = env->GetMethodID( jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z"); @@ -550,7 +550,7 @@ jobject JAudioTrack::createVolumeShaperConfigurationObj( return NULL; } - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); // Referenced "android_media_VolumeShaper.h". jfloatArray xarray = nullptr; @@ -595,7 +595,7 @@ jobject JAudioTrack::createVolumeShaperConfigurationObj( jobject JAudioTrack::createVolumeShaperOperationObj( const sp& operation) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder"); jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "", "()V"); @@ -647,7 +647,7 @@ jobject JAudioTrack::createVolumeShaperOperationObj( } jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback"); jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "", "(JJJ)V"); jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user); @@ -655,7 +655,7 @@ jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) { } jobject JAudioTrack::createCallbackExecutor() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); + JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors"); jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls, "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;"); diff --git a/media/libmediaplayer2/JavaVMHelper.cpp b/media/libmediaplayer2/JavaVMHelper.cpp new file mode 100644 index 0000000000..90aaa7fb46 --- /dev/null +++ b/media/libmediaplayer2/JavaVMHelper.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "JavaVMHelper" + +#include "mediaplayer2/JavaVMHelper.h" + +#include + +#include + +namespace android { + +// static +std::atomic JavaVMHelper::sJavaVM(NULL); + +// static +JNIEnv *JavaVMHelper::getJNIEnv() { + JNIEnv *env; + JavaVM *vm = sJavaVM.load(); + CHECK(vm != NULL); + + if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { + return NULL; + } + + return env; +} + +// static +void JavaVMHelper::setJavaVM(JavaVM *vm) { + sJavaVM.store(vm); +} + +} // namespace android diff --git a/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h new file mode 100644 index 0000000000..35091b7917 --- /dev/null +++ b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JAVA_VM_HELPER_H_ + +#define JAVA_VM_HELPER_H_ + +#include "jni.h" + +#include + +namespace android { + +struct JavaVMHelper { + static JNIEnv *getJNIEnv(); + static void setJavaVM(JavaVM *vm); + +private: + // Once a valid JavaVM has been set, it should never be reset or changed. + // However, as it may be accessed from multiple threads, access needs to be + // synchronized. + static std::atomic sJavaVM; +}; + +} // namespace android + +#endif // JAVA_VM_HELPER_H_ -- GitLab From 545403e92b693a35015575896acf467f95f50373 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 26 Jun 2018 11:57:05 -0700 Subject: [PATCH 0105/1530] Clean Opus headers after parse failure more robust behavior when opus headers are bad. avoid unneeded CHECK() failures; recover allocations appropriately. Bug: 110811823 Test: poc from bug Change-Id: Ia8ccfa184ec7a1ab1d00e5ff197f7236b6bd491d --- media/libstagefright/codecs/opus/dec/SoftOpus.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp index 813004bed0..942f8506a0 100644 --- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -431,7 +431,7 @@ void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) { } if (mInputBufferCount == 0) { - CHECK(mHeader == NULL); + delete mHeader; mHeader = new OpusHeader(); memset(mHeader, 0, sizeof(*mHeader)); if (!ParseOpusHeader(data, size, mHeader)) { @@ -452,6 +452,9 @@ void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) { } int status = OPUS_INVALID_STATE; + if (mDecoder != NULL) { + opus_multistream_decoder_destroy(mDecoder); + } mDecoder = opus_multistream_decoder_create(kRate, mHeader->channels, mHeader->num_streams, -- GitLab From e2d5f7bc3b45a2dc3e072f02d6efdcc767e49c4e Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 29 Jun 2018 09:56:21 -0700 Subject: [PATCH 0106/1530] Add __INTRODUCED_IN to more NdkMediaFormat.h variables Annotate the ones that were added when the NDK media APIs were first added in API 21 Test: build Change-Id: Id60039944981f3af56571f2de31e7989e4d377a2 --- media/ndk/include/media/NdkMediaFormat.h | 48 ++++++++++++------------ media/ndk/libmediandk.map.txt | 48 ++++++++++++------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 778f9c2920..112073e9d4 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -94,15 +94,15 @@ extern const char* AMEDIAFORMAT_KEY_AAC_DRC_HEAVY_COMPRESSION __INTRODUCED_IN(28 extern const char* AMEDIAFORMAT_KEY_AAC_DRC_TARGET_REFERENCE_LEVEL __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_AAC_ENCODED_TARGET_LEVEL __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_AAC_PROFILE; +extern const char* AMEDIAFORMAT_KEY_AAC_PROFILE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_AAC_SBR_MODE __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_AUDIO_SESSION_ID __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_BITRATE_MODE __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_BIT_RATE; +extern const char* AMEDIAFORMAT_KEY_BIT_RATE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_CAPTURE_RATE __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT; -extern const char* AMEDIAFORMAT_KEY_CHANNEL_MASK; -extern const char* AMEDIAFORMAT_KEY_COLOR_FORMAT; +extern const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_CHANNEL_MASK __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_COLOR_FORMAT __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_COLOR_RANGE __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER __INTRODUCED_IN(28); @@ -114,38 +114,38 @@ extern const char* AMEDIAFORMAT_KEY_CSD_2; extern const char* AMEDIAFORMAT_KEY_DISPLAY_CROP __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; extern const char* AMEDIAFORMAT_KEY_DISPLAY_WIDTH; -extern const char* AMEDIAFORMAT_KEY_DURATION; -extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; -extern const char* AMEDIAFORMAT_KEY_FRAME_RATE; +extern const char* AMEDIAFORMAT_KEY_DURATION __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_FRAME_RATE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_GRID_COLUMNS __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_GRID_ROWS __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO; -extern const char* AMEDIAFORMAT_KEY_HEIGHT; +extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_IS_ADTS; -extern const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT; -extern const char* AMEDIAFORMAT_KEY_IS_DEFAULT; -extern const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE; -extern const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; -extern const char* AMEDIAFORMAT_KEY_LANGUAGE; +extern const char* AMEDIAFORMAT_KEY_IS_ADTS __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_IS_DEFAULT __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_LANGUAGE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_LATENCY __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_LEVEL __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_MAX_HEIGHT; -extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; -extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH; -extern const char* AMEDIAFORMAT_KEY_MIME; +extern const char* AMEDIAFORMAT_KEY_MAX_HEIGHT __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_MIME __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA; extern const char* AMEDIAFORMAT_KEY_OPERATING_RATE __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_PCM_ENCODING __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_PRIORITY __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_PROFILE __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; -extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; +extern const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP __INTRODUCED_IN(21); +extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_ROTATION __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE; +extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_SEI; extern const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_STRIDE; +extern const char* AMEDIAFORMAT_KEY_STRIDE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TILE_HEIGHT __INTRODUCED_IN(28); @@ -153,7 +153,7 @@ extern const char* AMEDIAFORMAT_KEY_TILE_WIDTH __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TIME_US; extern const char* AMEDIAFORMAT_KEY_TRACK_ID __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX; -extern const char* AMEDIAFORMAT_KEY_WIDTH; +extern const char* AMEDIAFORMAT_KEY_WIDTH __INTRODUCED_IN(21); bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out) __INTRODUCED_IN(28); bool AMediaFormat_getRect(AMediaFormat*, const char *name, diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index fb566947fb..e9e32e540c 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -32,55 +32,55 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_AAC_DRC_TARGET_REFERENCE_LEVEL; # var introduced=28 AMEDIAFORMAT_KEY_AAC_ENCODED_TARGET_LEVEL; # var introduced=28 AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT; # var introduced=28 - AMEDIAFORMAT_KEY_AAC_PROFILE; # var + AMEDIAFORMAT_KEY_AAC_PROFILE; # var introduced=21 AMEDIAFORMAT_KEY_AAC_SBR_MODE; # var introduced=28 AMEDIAFORMAT_KEY_AUDIO_SESSION_ID; # var introduced=28 AMEDIAFORMAT_KEY_BITRATE_MODE; # var introduced=28 - AMEDIAFORMAT_KEY_BIT_RATE; # var + AMEDIAFORMAT_KEY_BIT_RATE; # var introduced=21 AMEDIAFORMAT_KEY_CAPTURE_RATE; # var introduced=28 - AMEDIAFORMAT_KEY_CHANNEL_COUNT; # var - AMEDIAFORMAT_KEY_CHANNEL_MASK; # var - AMEDIAFORMAT_KEY_COLOR_FORMAT; # var + AMEDIAFORMAT_KEY_CHANNEL_COUNT; # var introduced=21 + AMEDIAFORMAT_KEY_CHANNEL_MASK; # var introduced=21 + AMEDIAFORMAT_KEY_COLOR_FORMAT; # var introduced=21 AMEDIAFORMAT_KEY_COLOR_RANGE; # var introduced=28 AMEDIAFORMAT_KEY_COLOR_STANDARD; # var introduced=28 AMEDIAFORMAT_KEY_COLOR_TRANSFER; # var introduced=28 AMEDIAFORMAT_KEY_COMPLEXITY; # var introduced=28 AMEDIAFORMAT_KEY_DISPLAY_CROP; # var introduced=28 - AMEDIAFORMAT_KEY_DURATION; # var - AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var - AMEDIAFORMAT_KEY_FRAME_RATE; # var + AMEDIAFORMAT_KEY_DURATION; # var introduced=21 + AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var introduced=21 + AMEDIAFORMAT_KEY_FRAME_RATE; # var introduced=21 AMEDIAFORMAT_KEY_GRID_COLUMNS; # var introduced=28 AMEDIAFORMAT_KEY_GRID_ROWS; # var introduced=28 AMEDIAFORMAT_KEY_HDR_STATIC_INFO; # var introduced=28 - AMEDIAFORMAT_KEY_HEIGHT; # var + AMEDIAFORMAT_KEY_HEIGHT; # var introduced=21 AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD; # var introduced=28 - AMEDIAFORMAT_KEY_IS_ADTS; # var - AMEDIAFORMAT_KEY_IS_AUTOSELECT; # var - AMEDIAFORMAT_KEY_IS_DEFAULT; # var - AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE; # var - AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; # var - AMEDIAFORMAT_KEY_LANGUAGE; # var + AMEDIAFORMAT_KEY_IS_ADTS; # var introduced=21 + AMEDIAFORMAT_KEY_IS_AUTOSELECT; # var introduced=21 + AMEDIAFORMAT_KEY_IS_DEFAULT; # var introduced=21 + AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE; # var introduced=21 + AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; # var introduced=21 + AMEDIAFORMAT_KEY_LANGUAGE; # var introduced=21 AMEDIAFORMAT_KEY_LATENCY; # var introduced=28 AMEDIAFORMAT_KEY_LEVEL; # var introduced=28 - AMEDIAFORMAT_KEY_MAX_HEIGHT; # var - AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var - AMEDIAFORMAT_KEY_MAX_WIDTH; # var - AMEDIAFORMAT_KEY_MIME; # var + AMEDIAFORMAT_KEY_MAX_HEIGHT; # var introduced=21 + AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var introduced=21 + AMEDIAFORMAT_KEY_MAX_WIDTH; # var introduced=21 + AMEDIAFORMAT_KEY_MIME; # var introduced=21 AMEDIAFORMAT_KEY_OPERATING_RATE; # var introduced=28 AMEDIAFORMAT_KEY_PCM_ENCODING; # var introduced=28 AMEDIAFORMAT_KEY_PRIORITY; # var introduced=28 AMEDIAFORMAT_KEY_PROFILE; # var introduced=28 - AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; # var - AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var + AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; # var introduced=21 + AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var introduced=21 AMEDIAFORMAT_KEY_ROTATION; # var introduced=28 - AMEDIAFORMAT_KEY_SAMPLE_RATE; # var + AMEDIAFORMAT_KEY_SAMPLE_RATE; # var introduced=21 AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28 - AMEDIAFORMAT_KEY_STRIDE; # var + AMEDIAFORMAT_KEY_STRIDE; # var introduced=21 AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28 AMEDIAFORMAT_KEY_TILE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_TILE_WIDTH; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28 - AMEDIAFORMAT_KEY_WIDTH; # var + AMEDIAFORMAT_KEY_WIDTH; # var introduced=21 AMediaCodecActionCode_isRecoverable; # introduced=28 AMediaCodecActionCode_isTransient; # introduced=28 AMediaCodecCryptoInfo_delete; -- GitLab From 185a4a5ff90cc0a3e13e82877341a5493af8f86f Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 29 Jun 2018 13:00:06 -0700 Subject: [PATCH 0107/1530] Annotate remaining AMEDIAFORMAT variables Test: build Change-Id: I9a37defebd6824ec994887f88958a949228d1da1 --- media/ndk/include/media/NdkMediaFormat.h | 24 ++++++++++++------------ media/ndk/libmediandk.map.txt | 11 +++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 112073e9d4..581c7bf14c 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -107,19 +107,19 @@ extern const char* AMEDIAFORMAT_KEY_COLOR_RANGE __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_COMPLEXITY __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_CSD; -extern const char* AMEDIAFORMAT_KEY_CSD_0; -extern const char* AMEDIAFORMAT_KEY_CSD_1; -extern const char* AMEDIAFORMAT_KEY_CSD_2; +extern const char* AMEDIAFORMAT_KEY_CSD __INTRODUCED_IN(28); +extern const char* AMEDIAFORMAT_KEY_CSD_0 __INTRODUCED_IN(28); +extern const char* AMEDIAFORMAT_KEY_CSD_1 __INTRODUCED_IN(28); +extern const char* AMEDIAFORMAT_KEY_CSD_2 __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_DISPLAY_CROP __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; -extern const char* AMEDIAFORMAT_KEY_DISPLAY_WIDTH; +extern const char* AMEDIAFORMAT_KEY_DISPLAY_HEIGHT __INTRODUCED_IN(28); +extern const char* AMEDIAFORMAT_KEY_DISPLAY_WIDTH __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_DURATION __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_FRAME_RATE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_GRID_COLUMNS __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_GRID_ROWS __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO; +extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28);; extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_IS_ADTS __INTRODUCED_IN(21); @@ -134,7 +134,7 @@ extern const char* AMEDIAFORMAT_KEY_MAX_HEIGHT __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_MAX_WIDTH __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_MIME __INTRODUCED_IN(21); -extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA; +extern const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_OPERATING_RATE __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_PCM_ENCODING __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_PRIORITY __INTRODUCED_IN(28); @@ -143,16 +143,16 @@ extern const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP __INTRODUCED_IN(2 extern const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_ROTATION __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_SAMPLE_RATE __INTRODUCED_IN(21); -extern const char* AMEDIAFORMAT_KEY_SEI; +extern const char* AMEDIAFORMAT_KEY_SEI __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_STRIDE __INTRODUCED_IN(21); -extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; +extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TILE_HEIGHT __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TILE_WIDTH __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_TIME_US; +extern const char* AMEDIAFORMAT_KEY_TIME_US __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TRACK_ID __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX; +extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_WIDTH __INTRODUCED_IN(21); bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out) __INTRODUCED_IN(28); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index e9e32e540c..25511bf7a3 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -45,7 +45,13 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_COLOR_STANDARD; # var introduced=28 AMEDIAFORMAT_KEY_COLOR_TRANSFER; # var introduced=28 AMEDIAFORMAT_KEY_COMPLEXITY; # var introduced=28 + AMEDIAFORMAT_KEY_CSD; # var introduced=28 + AMEDIAFORMAT_KEY_CSD_0; # var introduced=28 + AMEDIAFORMAT_KEY_CSD_1; # var introduced=28 + AMEDIAFORMAT_KEY_CSD_2; # var introduced=28 AMEDIAFORMAT_KEY_DISPLAY_CROP; # var introduced=28 + AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; introduced=28 + AMEDIAFORMAT_KEY_DISPLAY_WIDTH; introduced=28 AMEDIAFORMAT_KEY_DURATION; # var introduced=21 AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var introduced=21 AMEDIAFORMAT_KEY_FRAME_RATE; # var introduced=21 @@ -66,6 +72,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var introduced=21 AMEDIAFORMAT_KEY_MAX_WIDTH; # var introduced=21 AMEDIAFORMAT_KEY_MIME; # var introduced=21 + AMEDIAFORMAT_KEY_MPEG_USER_DATA; # var introduced=28 AMEDIAFORMAT_KEY_OPERATING_RATE; # var introduced=28 AMEDIAFORMAT_KEY_PCM_ENCODING; # var introduced=28 AMEDIAFORMAT_KEY_PRIORITY; # var introduced=28 @@ -74,11 +81,15 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var introduced=21 AMEDIAFORMAT_KEY_ROTATION; # var introduced=28 AMEDIAFORMAT_KEY_SAMPLE_RATE; # var introduced=21 + AMEDIAFORMAT_KEY_SEI; # var introduced=28 AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_STRIDE; # var introduced=21 + AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; # var introduced=28 AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28 AMEDIAFORMAT_KEY_TILE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_TILE_WIDTH; # var introduced=28 + AMEDIAFORMAT_KEY_TIME_US; # var introduced=28 + AMEDIAFORMAT_KEY_TRACK_INDEX; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28 AMEDIAFORMAT_KEY_WIDTH; # var introduced=21 AMediaCodecActionCode_isRecoverable; # introduced=28 -- GitLab From 908c7d77355ae2c63a179a4ebfce69b7a6d31e3f Mon Sep 17 00:00:00 2001 From: luochaojiang Date: Thu, 21 Jun 2018 14:58:04 +0800 Subject: [PATCH 0108/1530] audiopolicy: Restrict the audio port callbacks by both uid and pid Set audio port callbacks enabled is called by each client, the client maybe has the same uid by different pid. And the notification clients is indexed with uid in services, so the audio port cb may be disabled by other pid. Here is the steps to reproduce this issue: 1.make a phone call 2.plugin a USB headset 3.check the P-sensor The screen will get black when the hand is near to the phone, actually it should not get black Test: make, take a photo, record a video and play video; make a call, plugin a USB headset, and check the P-sensor status. Change-Id: I43970ca0ab78ef47bd34d1c7191d4c8c3ad2b793 Signed-off-by: luochaojiang --- .../service/AudioPolicyService.cpp | 40 +++++++++++++------ .../audiopolicy/service/AudioPolicyService.h | 11 +++-- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 13792232e0..4292651f1b 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -115,13 +115,17 @@ void AudioPolicyService::registerClient(const sp& cli Mutex::Autolock _l(mNotificationClientsLock); uid_t uid = IPCThreadState::self()->getCallingUid(); - if (mNotificationClients.indexOfKey(uid) < 0) { + pid_t pid = IPCThreadState::self()->getCallingPid(); + int64_t token = ((int64_t)uid<<32) | pid; + + if (mNotificationClients.indexOfKey(token) < 0) { sp notificationClient = new NotificationClient(this, client, - uid); - ALOGV("registerClient() client %p, uid %d", client.get(), uid); + uid, + pid); + ALOGV("registerClient() client %p, uid %d pid %d", client.get(), uid, pid); - mNotificationClients.add(uid, notificationClient); + mNotificationClients.add(token, notificationClient); sp binder = IInterface::asBinder(client); binder->linkToDeath(notificationClient); @@ -133,22 +137,33 @@ void AudioPolicyService::setAudioPortCallbacksEnabled(bool enabled) Mutex::Autolock _l(mNotificationClientsLock); uid_t uid = IPCThreadState::self()->getCallingUid(); - if (mNotificationClients.indexOfKey(uid) < 0) { + pid_t pid = IPCThreadState::self()->getCallingPid(); + int64_t token = ((int64_t)uid<<32) | pid; + + if (mNotificationClients.indexOfKey(token) < 0) { return; } - mNotificationClients.valueFor(uid)->setAudioPortCallbacksEnabled(enabled); + mNotificationClients.valueFor(token)->setAudioPortCallbacksEnabled(enabled); } // removeNotificationClient() is called when the client process dies. -void AudioPolicyService::removeNotificationClient(uid_t uid) +void AudioPolicyService::removeNotificationClient(uid_t uid, pid_t pid) { { Mutex::Autolock _l(mNotificationClientsLock); - mNotificationClients.removeItem(uid); + int64_t token = ((int64_t)uid<<32) | pid; + mNotificationClients.removeItem(token); } { Mutex::Autolock _l(mLock); - if (mAudioPolicyManager) { + bool hasSameUid = false; + for (size_t i = 0; i < mNotificationClients.size(); i++) { + if (mNotificationClients.valueAt(i)->uid() == uid) { + hasSameUid = true; + break; + } + } + if (mAudioPolicyManager && !hasSameUid) { // called from binder death notification: no need to clear caller identity mAudioPolicyManager->releaseResourcesForUid(uid); } @@ -236,8 +251,9 @@ status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_co AudioPolicyService::NotificationClient::NotificationClient(const sp& service, const sp& client, - uid_t uid) - : mService(service), mUid(uid), mAudioPolicyServiceClient(client), + uid_t uid, + pid_t pid) + : mService(service), mUid(uid), mPid(pid), mAudioPolicyServiceClient(client), mAudioPortCallbacksEnabled(false) { } @@ -251,7 +267,7 @@ void AudioPolicyService::NotificationClient::binderDied(const wp& who _ sp keep(this); sp service = mService.promote(); if (service != 0) { - service->removeNotificationClient(mUid); + service->removeNotificationClient(mUid, mPid); } } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 09375f12ea..6b42ba35e3 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -222,7 +222,7 @@ public: virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config, int delayMs); - void removeNotificationClient(uid_t uid); + void removeNotificationClient(uid_t uid, pid_t pid); void onAudioPortListUpdate(); void doOnAudioPortListUpdate(); void onAudioPatchListUpdate(); @@ -594,7 +594,7 @@ private: public: NotificationClient(const sp& service, const sp& client, - uid_t uid); + uid_t uid, pid_t pid); virtual ~NotificationClient(); void onAudioPortListUpdate(); @@ -607,6 +607,10 @@ private: audio_patch_handle_t patchHandle); void setAudioPortCallbacksEnabled(bool enabled); + uid_t uid() { + return mUid; + } + // IBinder::DeathRecipient virtual void binderDied(const wp& who); @@ -616,6 +620,7 @@ private: const wp mService; const uid_t mUid; + const pid_t mPid; const sp mAudioPolicyServiceClient; bool mAudioPortCallbacksEnabled; }; @@ -680,7 +685,7 @@ private: AudioPolicyInterface *mAudioPolicyManager; AudioPolicyClient *mAudioPolicyClient; - DefaultKeyedVector< uid_t, sp > mNotificationClients; + DefaultKeyedVector< int64_t, sp > mNotificationClients; Mutex mNotificationClientsLock; // protects mNotificationClients // Manage all effects configured in audio_effects.conf sp mAudioPolicyEffects; -- GitLab From dcd4ab18a3cdb8f5d382a635c8602a18288137c1 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 29 Jun 2018 17:45:13 -0700 Subject: [PATCH 0109/1530] audio policy: remove special notifications handling in call Not needed anymore as this is handled by NotificationManager. Extend caping of alarm and ringtone volume to call volume to all output devices (was only done for earpiece). Test: make Change-Id: I9a1cd8610e42fefd0518f6ccc68e50e5541884ed --- services/audiopolicy/AudioPolicyInterface.h | 5 -- .../audiopolicy/enginedefault/src/Engine.cpp | 3 +- .../managerdefault/AudioPolicyManager.cpp | 84 ++----------------- .../managerdefault/AudioPolicyManager.h | 4 - .../service/AudioPolicyClientImpl.cpp | 11 --- .../service/AudioPolicyService.cpp | 77 ----------------- .../audiopolicy/service/AudioPolicyService.h | 27 +----- .../audiopolicy/tests/AudioPolicyTestClient.h | 3 - 8 files changed, 9 insertions(+), 205 deletions(-) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 4812b1fe7d..fe4948368d 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -324,11 +324,6 @@ public: // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) = 0; - // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing - // over a telephony device during a phone call. - virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream) = 0; - virtual status_t stopTone() = 0; - // set down link audio volume. virtual status_t setVoiceVolume(float volume, int delayMs = 0) = 0; diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 007eea0e40..941119b389 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -408,8 +408,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, case STRATEGY_SONIFICATION: - // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by - // handleIncallSonification(). + // If incall, just select the STRATEGY_PHONE device if (isInCall() || outputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL)) { device = getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7154cb2e50..6e66d3ef32 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -551,14 +551,8 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) return; } /// Opens: can these line be executed after the switch of volume curves??? - // if leaving call state, handle special case of active streams - // pertaining to sonification strategy see handleIncallSonification() if (isStateInCall(oldState)) { ALOGV("setPhoneState() in call state management: new state is %d", state); - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - handleIncallSonification((audio_stream_type_t)stream, false, true); - } - // force reevaluating accessibility routing when call stops mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); } @@ -637,14 +631,8 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) } } - // if entering in call state, handle special case of active streams - // pertaining to sonification strategy see handleIncallSonification() if (isStateInCall(state)) { ALOGV("setPhoneState() in call state management: new state is %d", state); - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - handleIncallSonification((audio_stream_type_t)stream, true, true); - } - // force reevaluating accessibility routing when call starts mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); } @@ -1279,11 +1267,6 @@ status_t AudioPolicyManager::startSource(const sp& output const uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address, requiresMuteCheck); - // handle special case for sonification while in call - if (isInCall()) { - handleIncallSonification(stream, true, false); - } - // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, mVolumeCurves->getVolumeIndex(stream, outputDesc->device()), @@ -1378,11 +1361,6 @@ status_t AudioPolicyManager::stopSource(const sp& outputD // always handle stream stop, check which stream type is stopping handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); - // handle special case for sonification while in call - if (isInCall()) { - handleIncallSonification(stream, false, false); - } - if (outputDesc->mRefCount[stream] > 0) { // decrement usage count of this stream on the output outputDesc->changeRefCount(stream, -1); @@ -2273,11 +2251,10 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, sp desc = mOutputs.valueAt(i); audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device()); for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { - if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { + if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream) || isInCall())) { continue; } - if (!(desc->isStreamActive((audio_stream_type_t)curStream) || - (isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) { + if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) { continue; } routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); @@ -5423,8 +5400,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB; } - // in-call: always cap earpiece volume by voice volume + some low headroom - if ((stream != AUDIO_STREAM_VOICE_CALL) && (device & AUDIO_DEVICE_OUT_EARPIECE) && + // in-call: always cap volume by voice volume + some low headroom + if ((stream != AUDIO_STREAM_VOICE_CALL) && (isInCall() || mOutputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL))) { switch (stream) { case AUDIO_STREAM_SYSTEM: @@ -5436,9 +5413,9 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, case AUDIO_STREAM_DTMF: case AUDIO_STREAM_ACCESSIBILITY: { int voiceVolumeIndex = - mVolumeCurves->getVolumeIndex(AUDIO_STREAM_VOICE_CALL, AUDIO_DEVICE_OUT_EARPIECE); + mVolumeCurves->getVolumeIndex(AUDIO_STREAM_VOICE_CALL, device); const float maxVoiceVolDb = - computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, AUDIO_DEVICE_OUT_EARPIECE) + computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; if (volumeDB > maxVoiceVolDb) { ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f", @@ -5635,55 +5612,6 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } } -void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, - bool starting, bool stateChange) -{ - if(!hasPrimaryOutput()) { - return; - } - - // if the stream pertains to sonification strategy and we are in call we must - // mute the stream if it is low visibility. If it is high visibility, we must play a tone - // in the device used for phone strategy and play the tone if the selected device does not - // interfere with the device used for phone strategy - // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as - // many times as there are active tracks on the output - const routing_strategy stream_strategy = getStrategy(stream); - if ((stream_strategy == STRATEGY_SONIFICATION) || - ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) { - sp outputDesc = mPrimaryOutput; - ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", - stream, starting, outputDesc->mDevice, stateChange); - if (outputDesc->mRefCount[stream]) { - int muteCount = 1; - if (stateChange) { - muteCount = outputDesc->mRefCount[stream]; - } - if (audio_is_low_visibility(stream)) { - ALOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); - for (int i = 0; i < muteCount; i++) { - setStreamMute(stream, starting, mPrimaryOutput); - } - } else { - ALOGV("handleIncallSonification() high visibility"); - if (outputDesc->device() & - getDeviceForStrategy(STRATEGY_PHONE, true /*fromCache*/)) { - ALOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); - for (int i = 0; i < muteCount; i++) { - setStreamMute(stream, starting, mPrimaryOutput); - } - } - if (starting) { - mpClientInterface->startTone(AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION, - AUDIO_STREAM_VOICE_CALL); - } else { - mpClientInterface->stopTone(); - } - } - } - } -} - audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_attributes_t *attr) { // flags to stream type mapping diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 893b96365b..136e52256f 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -377,10 +377,6 @@ protected: int delayMs = 0, audio_devices_t device = (audio_devices_t)0); - // handle special cases for sonification strategy while in call: mute streams or replace by - // a special tone in the device used for communication - void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); - audio_mode_t getPhoneState(); // true if device is in a telephony or VoIP call diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index b064f8cb03..21fffec518 100644 --- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -155,17 +155,6 @@ String8 AudioPolicyService::AudioPolicyClient::getParameters(audio_io_handle_t i return result; } -status_t AudioPolicyService::AudioPolicyClient::startTone(audio_policy_tone_t tone, - audio_stream_type_t stream) -{ - return mAudioPolicyService->startTone(tone, stream); -} - -status_t AudioPolicyService::AudioPolicyClient::stopTone() -{ - return mAudioPolicyService->stopTone(); -} - status_t AudioPolicyService::AudioPolicyClient::setVoiceVolume(float volume, int delay_ms) { return mAudioPolicyService->setVoiceVolume(volume, delay_ms); diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 13792232e0..5cfa0eed38 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -67,8 +67,6 @@ void AudioPolicyService::onFirstRef() { Mutex::Autolock _l(mLock); - // start tone playback thread - mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this); // start audio commands thread mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); // start output activity command thread @@ -90,7 +88,6 @@ void AudioPolicyService::onFirstRef() AudioPolicyService::~AudioPolicyService() { - mTonePlaybackThread->exit(); mAudioCommandThread->exit(); mOutputCommandThread->exit(); @@ -322,8 +319,6 @@ status_t AudioPolicyService::dumpInternals(int fd) result.append(buffer); snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); result.append(buffer); - snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get()); - result.append(buffer); write(fd, result.string(), result.size()); return NO_ERROR; @@ -359,9 +354,6 @@ status_t AudioPolicyService::dump(int fd, const Vector& args __unused) if (mAudioCommandThread != 0) { mAudioCommandThread->dump(fd); } - if (mTonePlaybackThread != 0) { - mTonePlaybackThread->dump(fd); - } if (mAudioPolicyManager) { mAudioPolicyManager->dump(fd); @@ -632,7 +624,6 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, const wp& service) : Thread(false), mName(name), mService(service) { - mpToneGenerator = NULL; } @@ -642,7 +633,6 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread() release_wake_lock(mName.string()); } mAudioCommands.clear(); - delete mpToneGenerator; } void AudioPolicyService::AudioCommandThread::onFirstRef() @@ -667,26 +657,6 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() mLastCommand = command; switch (command->mCommand) { - case START_TONE: { - mLock.unlock(); - ToneData *data = (ToneData *)command->mParam.get(); - ALOGV("AudioCommandThread() processing start tone %d on stream %d", - data->mType, data->mStream); - delete mpToneGenerator; - mpToneGenerator = new ToneGenerator(data->mStream, 1.0); - mpToneGenerator->startTone(data->mType); - mLock.lock(); - }break; - case STOP_TONE: { - mLock.unlock(); - ALOGV("AudioCommandThread() processing stop tone"); - if (mpToneGenerator != NULL) { - mpToneGenerator->stopTone(); - delete mpToneGenerator; - mpToneGenerator = NULL; - } - mLock.lock(); - }break; case SET_VOLUME: { VolumeData *data = (VolumeData *)command->mParam.get(); ALOGV("AudioCommandThread() processing set volume stream %d, \ @@ -893,27 +863,6 @@ status_t AudioPolicyService::AudioCommandThread::dump(int fd) return NO_ERROR; } -void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type, - audio_stream_type_t stream) -{ - sp command = new AudioCommand(); - command->mCommand = START_TONE; - sp data = new ToneData(); - data->mType = type; - data->mStream = stream; - command->mParam = data; - ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); - sendCommand(command); -} - -void AudioPolicyService::AudioCommandThread::stopToneCommand() -{ - sp command = new AudioCommand(); - command->mCommand = STOP_TONE; - ALOGV("AudioCommandThread() adding tone stop"); - sendCommand(command); -} - status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream, float volume, audio_io_handle_t output, @@ -1250,8 +1199,6 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(sp& c } break; - case START_TONE: - case STOP_TONE: default: break; } @@ -1324,27 +1271,6 @@ int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, output, delayMs); } -int AudioPolicyService::startTone(audio_policy_tone_t tone, - audio_stream_type_t stream) -{ - if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) { - ALOGE("startTone: illegal tone requested (%d)", tone); - } - if (stream != AUDIO_STREAM_VOICE_CALL) { - ALOGE("startTone: illegal stream (%d) requested for tone %d", stream, - tone); - } - mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING, - AUDIO_STREAM_VOICE_CALL); - return 0; -} - -int AudioPolicyService::stopTone() -{ - mTonePlaybackThread->stopToneCommand(); - return 0; -} - int AudioPolicyService::setVoiceVolume(float volume, int delayMs) { return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); @@ -1400,9 +1326,6 @@ void aps_set_parameters(void *service, audio_io_handle_t io_handle, int aps_set_stream_volume(void *service, audio_stream_type_t stream, float volume, audio_io_handle_t output, int delay_ms); -int aps_start_tone(void *service, audio_policy_tone_t tone, - audio_stream_type_t stream); -int aps_stop_tone(void *service); int aps_set_voice_volume(void *service, float volume, int delay_ms); }; diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 09375f12ea..ed3324b753 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -157,8 +157,6 @@ public: float volume, audio_io_handle_t output, int delayMs = 0); - virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); - virtual status_t stopTone(); virtual status_t setVoiceVolume(float volume, int delayMs = 0); virtual bool isOffloadSupported(const audio_offload_info_t &config); @@ -304,10 +302,7 @@ private: std::unordered_map mCachedUids; }; - // Thread used for tone playback and to send audio config commands to audio flinger - // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because - // startTone() and stopTone() are normally called with mLock locked and requesting a tone start - // or stop will cause calls to AudioPolicyService and an attempt to lock mLock. + // Thread used to send audio config commands to audio flinger // For audio config commands, it is necessary because audio flinger requires that the calling // process (user) has permission to modify audio settings. class AudioCommandThread : public Thread { @@ -316,8 +311,6 @@ private: // commands for tone AudioCommand enum { - START_TONE, - STOP_TONE, SET_VOLUME, SET_PARAMETERS, SET_VOICE_VOLUME, @@ -342,9 +335,6 @@ private: virtual bool threadLoop(); void exit(); - void startToneCommand(ToneGenerator::tone_type type, - audio_stream_type_t stream); - void stopToneCommand(); status_t volumeCommand(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0); status_t parametersCommand(audio_io_handle_t ioHandle, @@ -387,7 +377,7 @@ private: void dump(char* buffer, size_t size); - int mCommand; // START_TONE, STOP_TONE ... + int mCommand; // SET_VOLUME, SET_PARAMETERS... nsecs_t mTime; // time stamp Mutex mLock; // mutex associated to mCond Condition mCond; // condition for status return @@ -403,12 +393,6 @@ private: AudioCommandData() {} }; - class ToneData : public AudioCommandData { - public: - ToneGenerator::tone_type mType; // tone type (START_TONE only) - audio_stream_type_t mStream; // stream type (START_TONE only) - }; - class VolumeData : public AudioCommandData { public: audio_stream_type_t mStream; @@ -475,7 +459,6 @@ private: Mutex mLock; Condition mWaitWorkCV; Vector < sp > mAudioCommands; // list of pending commands - ToneGenerator *mpToneGenerator; // the tone generator sp mLastCommand; // last processed command (used by dump) String8 mName; // string used by wake lock fo delayed commands wp mService; @@ -550,11 +533,6 @@ private: // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); - // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing - // over a telephony device during a phone call. - virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); - virtual status_t stopTone(); - // set down link audio volume. virtual status_t setVoiceVolume(float volume, int delayMs = 0); @@ -673,7 +651,6 @@ private: // mLock protects AudioPolicyManager methods that can call into audio flinger // and possibly back in to audio policy service and acquire mEffectsLock. sp mAudioCommandThread; // audio commands thread - sp mTonePlaybackThread; // tone playback thread sp mOutputCommandThread; // process stop and release output struct audio_policy_device *mpAudioPolicyDev; struct audio_policy *mpAudioPolicy; diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h index eb8222c785..2ff767570d 100644 --- a/services/audiopolicy/tests/AudioPolicyTestClient.h +++ b/services/audiopolicy/tests/AudioPolicyTestClient.h @@ -60,9 +60,6 @@ public: int /*delayMs*/) override { } String8 getParameters(audio_io_handle_t /*ioHandle*/, const String8& /*keys*/) override { return String8(); } - status_t startTone(audio_policy_tone_t /*tone*/, - audio_stream_type_t /*stream*/) override { return NO_INIT; } - status_t stopTone() override { return NO_INIT; } status_t setVoiceVolume(float /*volume*/, int /*delayMs*/) override { return NO_INIT; } status_t moveEffects(audio_session_t /*session*/, audio_io_handle_t /*srcOutput*/, -- GitLab From b0d1c1b901819a038ed2cc75310e319065955a5e Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 31 May 2018 16:30:09 -0700 Subject: [PATCH 0110/1530] SoftXaac: types and allocations Test: CTS android.media.cgts.DecoderTest{AacDrc,XheAac} Bug: 77287124 Combined fixes for: SoftXaac: Fix return types of member functions Functions are updated to return IA_ERRORCODE Bug: 80546260 Change-Id: If639b15da4d89065e0436e1afe741f510efe690d SoftXaac: Moved MPEG D DRC allocations to init function Tracking MPEG D Drc allocations in a different array now and ensure every time a config data is passed, previously allocated DRC memory is freed Bug: 80548842 Change-Id: Icd07c94ef4b13abae937bfbfe65328d98b7df5e9 SoftXaac: Replaced memory arrays with Vectors Bug: 109746745 Change-Id: Icbde1f9abd1dd534752b071f4f15d6f11673d4a3 --- .../codecs/xaacdec/SoftXAAC.cpp | 309 ++++++++---------- .../libstagefright/codecs/xaacdec/SoftXAAC.h | 30 +- 2 files changed, 154 insertions(+), 185 deletions(-) diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp index f173e0fba7..03f0902173 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -110,15 +110,22 @@ SoftXAAC::SoftXAAC(const char* name, const OMX_CALLBACKTYPE* callbacks, OMX_PTR { initPorts(); - CHECK_EQ(initDecoder(), (status_t)OK); + mMemoryVec.clear(); + mDrcMemoryVec.clear(); + + CHECK_EQ(initDecoder(), IA_NO_ERROR); } SoftXAAC::~SoftXAAC() { - int errCode = deInitXAACDecoder(); - if (0 != errCode) { - ALOGE("deInitXAACDecoder() failed %d", errCode); + IA_ERRORCODE err_code = deInitXAACDecoder(); + if (IA_NO_ERROR != err_code) { + ALOGE("deInitXAACDecoder() failed %d", err_code); } + err_code = deInitMPEGDDDrc(); + if (IA_NO_ERROR != err_code) { + ALOGE("deInitMPEGDDDrc() failed %d", err_code); + } mIsCodecInitialized = false; mIsCodecConfigFlushRequired = false; } @@ -164,36 +171,16 @@ void SoftXAAC::initPorts() { addPort(def); } -status_t SoftXAAC::initDecoder() { - status_t status = UNKNOWN_ERROR; - +IA_ERRORCODE SoftXAAC::initDecoder() { int ui_drc_val; IA_ERRORCODE err_code = IA_NO_ERROR; int loop = 0; err_code = initXAACDecoder(); if (err_code != IA_NO_ERROR) { - if (NULL == mXheaacCodecHandle) { - ALOGE("AAC decoder handle is null"); - } - if (NULL == mMpegDDrcHandle) { - ALOGE("MPEG-D DRC decoder handle is null"); - } - for (loop = 1; loop < mMallocCount; loop++) { - if (mMemoryArray[loop] == NULL) { - ALOGE(" memory allocation error %d\n", loop); - break; - } - } - ALOGE("initXAACDecoder Failed"); - - for (loop = 0; loop < mMallocCount; loop++) { - if (mMemoryArray[loop]) free(mMemoryArray[loop]); - } - mMallocCount = 0; - return status; - } else { - status = OK; + ALOGE("initXAACDecoder failed with error %d", err_code); + deInitXAACDecoder(); + return err_code; } mEndOfInput = false; @@ -274,7 +261,7 @@ status_t SoftXAAC::initDecoder() { RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE"); #endif - return status; + return IA_NO_ERROR; } OMX_ERRORTYPE SoftXAAC::internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params) { @@ -547,9 +534,6 @@ void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { /* sample currently */ if (mIsCodecInitialized) { numOutBytes = mOutputFrameLength * (mPcmWdSz / 8) * mNumChannels; - if ((mPcmWdSz / 8) != 2) { - ALOGE("XAAC assumes 2 bytes per sample! mPcmWdSz %d", mPcmWdSz); - } } while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) { @@ -569,8 +553,8 @@ void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { inBufferLength = inHeader->nFilledLen; /* GA header configuration sent to Decoder! */ - int err_code = configXAACDecoder(inBuffer, inBufferLength); - if (0 != err_code) { + IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength); + if (IA_NO_ERROR != err_code) { ALOGW("configXAACDecoder err_code = %d", err_code); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); @@ -682,8 +666,8 @@ void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { /* which should initialize the codec. Once this state is reached, call the */ /* decodeXAACStream API with same frame to decode! */ if (!mIsCodecInitialized) { - int err_code = configXAACDecoder(inBuffer, inBufferLength); - if (0 != err_code) { + IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength); + if (IA_NO_ERROR != err_code) { ALOGW("configXAACDecoder Failed 2 err_code = %d", err_code); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); @@ -842,7 +826,7 @@ void SoftXAAC::onPortFlushCompleted(OMX_U32 portIndex) { } } -int SoftXAAC::configflushDecode() { +IA_ERRORCODE SoftXAAC::configflushDecode() { IA_ERRORCODE err_code; UWORD32 ui_init_done; uint32_t inBufferLength = 8203; @@ -868,16 +852,13 @@ int SoftXAAC::configflushDecode() { "Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz " "%d\nchannelMask %d\noutputFrameLength %d", mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength); - if (mNumChannels > MAX_CHANNEL_COUNT) { - ALOGE(" No of channels are more than max channels\n"); - mIsCodecInitialized = false; - } else - mIsCodecInitialized = true; + + mIsCodecInitialized = true; } - return err_code; + return IA_NO_ERROR; } -int SoftXAAC::drainDecoder() { - return 0; +IA_ERRORCODE SoftXAAC::drainDecoder() { + return IA_NO_ERROR; } void SoftXAAC::onReset() { @@ -918,7 +899,7 @@ void SoftXAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { } } -int SoftXAAC::initXAACDecoder() { +IA_ERRORCODE SoftXAAC::initXAACDecoder() { LOOPIDX i; /* Error code */ @@ -936,11 +917,11 @@ int SoftXAAC::initXAACDecoder() { UWORD32 ui_proc_mem_tabs_size; /* API size */ UWORD32 pui_api_size; + pVOID pv_alloc_ptr; mInputBufferSize = 0; mInputBuffer = 0; mOutputBuffer = 0; - mMallocCount = 0; /* Process struct initing end */ /* ******************************************************************/ @@ -951,20 +932,13 @@ int SoftXAAC::initXAACDecoder() { err_code = ixheaacd_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size); RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - /* Allocate memory for API */ - mMemoryArray[mMallocCount] = memalign(4, pui_api_size); - if (mMemoryArray[mMallocCount] == NULL) { + mXheaacCodecHandle = memalign(4, pui_api_size); + if (mXheaacCodecHandle == NULL) { ALOGE("malloc for pui_api_size + 4 >> %d Failed", pui_api_size + 4); return IA_FATAL_ERROR; } - /* Set API object with the memory allocated */ - mXheaacCodecHandle = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); - mMallocCount++; + mMemoryVec.push(mXheaacCodecHandle); /* Set the config params to default values */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, @@ -976,23 +950,16 @@ int SoftXAAC::initXAACDecoder() { RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - /* Allocate memory for API */ - mMemoryArray[mMallocCount] = memalign(4, pui_api_size); + mMpegDDrcHandle = memalign(4, pui_api_size); - if (mMemoryArray[mMallocCount] == NULL) { + if (mMpegDDrcHandle == NULL) { ALOGE("malloc for drc api structure Failed"); return IA_FATAL_ERROR; } - memset(mMemoryArray[mMallocCount], 0, pui_api_size); + mMemoryVec.push(mMpegDDrcHandle); - /* Set API object with the memory allocated */ - mMpegDDrcHandle = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); - mMallocCount++; + memset(mMpegDDrcHandle, 0, pui_api_size); /* Set the config params to default values */ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, @@ -1018,24 +985,18 @@ int SoftXAAC::initXAACDecoder() { &ui_proc_mem_tabs_size); RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - - mMemoryArray[mMallocCount] = memalign(4, ui_proc_mem_tabs_size); - if (mMemoryArray[mMallocCount] == NULL) { + pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size); + if (pv_alloc_ptr == NULL) { ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!", ui_proc_mem_tabs_size + 4); return IA_FATAL_ERROR; } - mMallocCount++; + mMemoryVec.push(pv_alloc_ptr); + /* Set pointer for process memory tables */ - err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, - (pVOID)((WORD8*)mMemoryArray[mMallocCount - 1])); + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, pv_alloc_ptr); RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR"); - /* initialize the API, post config, fill memory tables */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); @@ -1063,17 +1024,12 @@ int SoftXAAC::initXAACDecoder() { err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - mMemoryArray[mMallocCount] = memalign(ui_alignment, ui_size); - if (mMemoryArray[mMallocCount] == NULL) { + pv_alloc_ptr = memalign(ui_alignment, ui_size); + if (pv_alloc_ptr == NULL) { ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!", ui_size + ui_alignment); return IA_FATAL_ERROR; } - pv_alloc_ptr = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); - mMallocCount++; + mMemoryVec.push(pv_alloc_ptr); /* Set the buffer pointer */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); @@ -1092,7 +1048,7 @@ int SoftXAAC::initXAACDecoder() { return IA_NO_ERROR; } -int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { +IA_ERRORCODE SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { UWORD32 ui_init_done; int32_t i_bytes_consumed; @@ -1151,13 +1107,73 @@ int SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { return IA_NO_ERROR; } -int SoftXAAC::configMPEGDDrc() { +IA_ERRORCODE SoftXAAC::initMPEGDDDrc() { + IA_ERRORCODE err_code = IA_NO_ERROR; + int i; + + for (i = 0; i < (WORD32)2; i++) { + WORD32 ui_size, ui_alignment, ui_type; + pVOID pv_alloc_ptr; + + /* Get memory size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); + + /* Get memory alignment */ + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &ui_alignment); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); + + /* Get memory type */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); + + pv_alloc_ptr = memalign(4, ui_size); + if (pv_alloc_ptr == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(pv_alloc_ptr); + + /* Set the buffer pointer */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + } + + WORD32 ui_size; + ui_size = 8192 * 2; + + mDrcInBuf = (int8_t*)memalign(4, ui_size); + if (mDrcInBuf == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(mDrcInBuf); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 2, mDrcInBuf); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + + mDrcOutBuf = (int8_t*)memalign(4, ui_size); + if (mDrcOutBuf == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(mDrcOutBuf); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 3, mDrcOutBuf); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + + return IA_NO_ERROR; +} +IA_ERRORCODE SoftXAAC::configMPEGDDrc() { IA_ERRORCODE err_code = IA_NO_ERROR; int i_effect_type; int i_loud_norm; int i_target_loudness; unsigned int i_sbr_mode; - int n_mems; int i; #ifdef ENABLE_MPEG_D_DRC @@ -1214,78 +1230,16 @@ int SoftXAAC::configMPEGDDrc() { RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); - for (i = 0; i < (WORD32)2; i++) { - WORD32 ui_size, ui_alignment, ui_type; - pVOID pv_alloc_ptr; - - /* Get memory size */ - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); - - RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); - - /* Get memory alignment */ - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, - &ui_alignment); - - RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); - - /* Get memory type */ - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); - RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - - mMemoryArray[mMallocCount] = memalign(4, ui_size); - if (mMemoryArray[mMallocCount] == NULL) { - ALOGE(" Cannot create requested memory %d", ui_size); - return IA_FATAL_ERROR; - } - pv_alloc_ptr = (pVOID)((WORD8*)mMemoryArray[mMallocCount]); - mMallocCount++; + /* Free any memory that is allocated for MPEG D Drc so far */ + deInitMPEGDDDrc(); - /* Set the buffer pointer */ - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); - - RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + err_code = initMPEGDDDrc(); + if (err_code != IA_NO_ERROR) { + ALOGE("initMPEGDDDrc failed with error %d", err_code); + deInitMPEGDDDrc(); + return err_code; } - { - WORD32 ui_size; - ui_size = 8192 * 2; - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - - mMemoryArray[mMallocCount] = memalign(4, ui_size); - if (mMemoryArray[mMallocCount] == NULL) { - ALOGE(" Cannot create requested memory %d", ui_size); - return IA_FATAL_ERROR; - } - - mDrcInBuf = (int8_t*)mMemoryArray[mMallocCount]; - mMallocCount++; - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 2, - /*mOutputBuffer*/ mDrcInBuf); - RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); - if (mMallocCount == MAX_MEM_ALLOCS) { - ALOGE("mMemoryArray is full"); - return IA_FATAL_ERROR; - } - mMemoryArray[mMallocCount] = memalign(4, ui_size); - if (mMemoryArray[mMallocCount] == NULL) { - ALOGE(" Cannot create requested memory %d", ui_size); - return IA_FATAL_ERROR; - } - - mDrcOutBuf = (int8_t*)mMemoryArray[mMallocCount]; - mMallocCount++; - err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 3, - /*mOutputBuffer*/ mDrcOutBuf); - RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); - } /* DRC buffers buf[0] - contains extension element pay load loudness related buf[1] - contains extension element pay load*/ @@ -1420,10 +1374,10 @@ int SoftXAAC::configMPEGDDrc() { } #endif - return err_code; + return IA_NO_ERROR; } -int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, int32_t* bytesConsumed, - int32_t* outBytes) { +IA_ERRORCODE SoftXAAC::decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, + int32_t* bytesConsumed, int32_t* outBytes) { if (mInputBufferSize < inBufferLength) { ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, inBufferLength); @@ -1513,24 +1467,33 @@ int SoftXAAC::decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, int32 memcpy(mOutputBuffer, mDrcOutBuf, *outBytes); } #endif - return err_code; + return IA_NO_ERROR; } -int SoftXAAC::deInitXAACDecoder() { +IA_ERRORCODE SoftXAAC::deInitXAACDecoder() { ALOGI("deInitXAACDecoder"); /* Tell that the input is over in this buffer */ IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INPUT_OVER, 0, NULL); - RETURN_IF_FATAL(err_code, "IA_API_CMD_INPUT_OVER"); - for (int i = 0; i < mMallocCount; i++) { - if (mMemoryArray[i]) free(mMemoryArray[i]); + /* Irrespective of error returned in IA_API_CMD_INPUT_OVER, free allocated memory */ + for (void* buf : mMemoryVec) { + free(buf); } - mMallocCount = 0; - + mMemoryVec.clear(); return err_code; } +IA_ERRORCODE SoftXAAC::deInitMPEGDDDrc() { + ALOGI("deInitMPEGDDDrc"); + + for (void* buf : mDrcMemoryVec) { + free(buf); + } + mDrcMemoryVec.clear(); + return IA_NO_ERROR; +} + IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { IA_ERRORCODE err_code = IA_NO_ERROR; @@ -1543,11 +1506,19 @@ IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels); RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS"); + if (mNumChannels > MAX_CHANNEL_COUNT) { + ALOGE(" No of channels are more than max channels\n"); + return IA_FATAL_ERROR; + } /* PCM word size */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz); RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ"); + if ((mPcmWdSz / 8) != 2) { + ALOGE("Invalid Number of bytes per sample: %d, Expected is 2", mPcmWdSz); + return IA_FATAL_ERROR; + } /* channel mask to tell the arrangement of channels in bit stream */ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.h b/media/libstagefright/codecs/xaacdec/SoftXAAC.h index 6176082a78..a62a797e7c 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.h +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.h @@ -33,8 +33,6 @@ #include "impd_apicmd_standards.h" #include "impd_drc_config_params.h" -#define MAX_MEM_ALLOCS 100 - extern "C" IA_ERRORCODE ixheaacd_dec_api(pVOID p_ia_module_obj, WORD32 i_cmd, WORD32 i_idx, pVOID pv_value); extern "C" IA_ERRORCODE ia_drc_dec_api(pVOID p_ia_module_obj, WORD32 i_cmd, WORD32 i_idx, @@ -80,18 +78,19 @@ struct SoftXAAC : public SimpleSoftOMXComponent { enum { NONE, AWAITING_DISABLED, AWAITING_ENABLED } mOutputPortSettingsChange; void initPorts(); - status_t initDecoder(); + IA_ERRORCODE initDecoder(); bool isConfigured() const; - int drainDecoder(); - int initXAACDecoder(); - int deInitXAACDecoder(); - - int configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength); - int configMPEGDDrc(); - int decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, int32_t* bytesConsumed, - int32_t* outBytes); - - int configflushDecode(); + IA_ERRORCODE drainDecoder(); + IA_ERRORCODE initXAACDecoder(); + IA_ERRORCODE deInitXAACDecoder(); + IA_ERRORCODE initMPEGDDDrc(); + IA_ERRORCODE deInitMPEGDDDrc(); + IA_ERRORCODE configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength); + IA_ERRORCODE configMPEGDDrc(); + IA_ERRORCODE decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, + int32_t* bytesConsumed, int32_t* outBytes); + + IA_ERRORCODE configflushDecode(); IA_ERRORCODE getXAACStreamInfo(); IA_ERRORCODE setXAACDRCInfo(int32_t drcCut, int32_t drcBoost, int32_t drcRefLevel, int32_t drcHeavyCompression @@ -120,9 +119,8 @@ struct SoftXAAC : public SimpleSoftOMXComponent { int8_t* mDrcOutBuf; int32_t mMpegDDRCPresent; int32_t mDRCFlag; - - void* mMemoryArray[MAX_MEM_ALLOCS]; - int32_t mMallocCount; + Vector mMemoryVec; + Vector mDrcMemoryVec; DISALLOW_EVIL_CONSTRUCTORS(SoftXAAC); }; -- GitLab From 26d975de2fa0119745d6af120428617b2ffa1b09 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Thu, 5 Jul 2018 14:52:57 +0100 Subject: [PATCH 0111/1530] Camera: Use separate lock for status tracker access sync A race condition is possible during access to status tracker in 'disonnect' and inflight updates like 'registerInFlight' and 'removeInFlightMapEntryLocked'. To avoid this, access is serialized using 'mLock'. However 'mLock' is also used in other contexts which under the right conditions could result in a deadlock. One such instance could occur during consecutive reprocess requests. The request thread while holding the request lock will try to obtain a free input buffer. In case the input stream doesn't have any free buffers left, it will block on an internal condition and wait. In the meantime if another capture request gets submitted, then the request itself will block on the request lock within request thread while holding 'mLock' inside submit helper. This will not allow incoming input buffer results to get processed as they could call 'removeInFlightMapEntryLocked' and try to acquire the already locked 'mLock'. The deadlock will continue until the input stream timeout expires, which will fail the request. One way to resolve this is by adding a separate lock which will only be used for the status tracker access synchronization. Bug: 79972865 Test: Camera CTS Change-Id: Ic63f891202ba102f6408ed714c5eef29b41404e3 --- .../camera/libcameraservice/device3/Camera3Device.cpp | 11 +++++++---- .../camera/libcameraservice/device3/Camera3Device.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 543914e5a4..4794bfc66a 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -263,6 +263,7 @@ status_t Camera3Device::initializeCommonLocked() { status_t Camera3Device::disconnect() { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); + Mutex::Autolock stLock(mTrackerLock); ALOGI("%s: E", __FUNCTION__); @@ -2699,8 +2700,9 @@ status_t Camera3Device::registerInFlight(uint32_t frameNumber, if (res < 0) return res; if (mInFlightMap.size() == 1) { - // hold mLock to prevent race with disconnect - Mutex::Autolock l(mLock); + // Hold a separate dedicated tracker lock to prevent race with disconnect and also + // avoid a deadlock during reprocess requests. + Mutex::Autolock l(mTrackerLock); if (mStatusTracker != nullptr) { mStatusTracker->markComponentActive(mInFlightStatusId); } @@ -2733,8 +2735,9 @@ void Camera3Device::removeInFlightMapEntryLocked(int idx) { // Indicate idle inFlightMap to the status tracker if (mInFlightMap.size() == 0) { - // hold mLock to prevent race with disconnect - Mutex::Autolock l(mLock); + // Hold a separate dedicated tracker lock to prevent race with disconnect and also + // avoid a deadlock during reprocess requests. + Mutex::Autolock l(mTrackerLock); if (mStatusTracker != nullptr) { mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE); } diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index d8fe19fa66..e78bb6166d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -1208,6 +1208,9 @@ class Camera3Device : static callbacks_notify_t sNotify; + // Synchronizes access to status tracker between inflight updates and disconnect. + // b/79972865 + Mutex mTrackerLock; }; // class Camera3Device }; // namespace android -- GitLab From b5c8f21eeb64b5d802c7953083104abefa02716b Mon Sep 17 00:00:00 2001 From: Edwin Wong Date: Fri, 6 Jul 2018 17:29:24 -0700 Subject: [PATCH 0112/1530] Fixed AMediaDrm_getKeyRequest returning both new and old requests. If multiple key requests are made through AMediaDrm_getKeyRequest, (e.g. Service Certification + key request in private mode), the new request will be appended to the old request/s. AMediaDrm_getKeyRequest should just return the most recent request. Test: CTS test Modify testQueryKeyStatus to make two back to back queries. The test should pass. Test: CTS test ANDROID_BUILD_TOP= ./android-cts/toolsefed run cts-dev --module CtsMediaTestCases -t android.media.cts.NativeMediaDrmClearkeyTest#testQueryKeyStatus bug: 77713912 Change-Id: I21a30572fedc67cdf1d813ea36b92d2a67b4ebb4 --- media/ndk/NdkMediaDrm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp index 6d10f1c41f..5597488637 100644 --- a/media/ndk/NdkMediaDrm.cpp +++ b/media/ndk/NdkMediaDrm.cpp @@ -309,6 +309,7 @@ media_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope *sc } String8 defaultUrl; DrmPlugin::KeyRequestType keyRequestType; + mObj->mKeyRequest.clear(); status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType), mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl, &keyRequestType); -- GitLab From 6ba8eb275c0d69ea91c5a7a2829be259a63a3827 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Sun, 8 Jul 2018 13:10:44 -0700 Subject: [PATCH 0113/1530] Camera: Synchronize between provider removal and registration Because onDeviceStatusChanged is reenterable, in the case of camera provider process crash, the following race condition could happen: 1. Camera provider crashes, onDeviceStatusChanged is called with NOT_PRESENT. camera service calls disconnect to disconnect from CameraDevice. 2. Camera provider comes back online, and onDeviceStatusChanged is called to change the state back to PRESENT. 3. disconnect returns, and the camera state is deleted, resulting in cameraservice to be in bad state. This change synchronizes onRegistration and removeProvider to avoid aforementioned race condition. Test: Camera CTS Test: Add delay in removeProvider and kill provider process while camera is running. Bug: 110837617 Change-Id: I472f1bfebf770d8eb0b2d7eb5f4a0fa26566bdea --- .../camera/libcameraservice/common/CameraProviderManager.cpp | 2 ++ .../camera/libcameraservice/common/CameraProviderManager.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 0ce4318e7f..3be6399959 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -336,6 +336,7 @@ hardware::Return CameraProviderManager::onRegistration( const hardware::hidl_string& /*fqName*/, const hardware::hidl_string& name, bool /*preexisting*/) { + std::lock_guard providerLock(mProviderLifecycleLock); { std::lock_guard lock(mInterfaceMutex); @@ -458,6 +459,7 @@ status_t CameraProviderManager::addProviderLocked(const std::string& newProvider } status_t CameraProviderManager::removeProvider(const std::string& provider) { + std::lock_guard providerLock(mProviderLifecycleLock); std::unique_lock lock(mInterfaceMutex); std::vector removedDeviceIds; status_t res = NAME_NOT_FOUND; diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index b8b8b8c0dd..c523c2d72f 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -246,6 +246,9 @@ private: wp mListener; ServiceInteractionProxy* mServiceProxy; + // mProviderLifecycleLock is locked during onRegistration and removeProvider + mutable std::mutex mProviderLifecycleLock; + static HardwareServiceInteractionProxy sHardwareServiceInteractionProxy; struct ProviderInfo : -- GitLab From ac9858be02edea7770ab3bb27d1cc15e4640fb26 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 15 Jun 2018 13:12:37 -0700 Subject: [PATCH 0114/1530] Use and test patch utilities from system/audio.h Use `audio_patch_is_valid` function. Add basic tests for the following functions: audio_patch_is_valid audio_port_config_has_hw_av_sync audio_patches_are_equal Bug: 63901775 Test: systemaudio_tests Change-Id: I378ab544e4148fda0dd7b9520d6fd57ff01cb34b --- services/audioflinger/PatchPanel.cpp | 4 +- .../managerdefault/AudioPolicyManager.cpp | 3 +- services/audiopolicy/tests/Android.mk | 23 ++++ .../audiopolicy/tests/systemaudio_tests.cpp | 117 ++++++++++++++++++ 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 services/audiopolicy/tests/systemaudio_tests.cpp diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 42a5a9077d..0caa0af778 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -110,9 +110,7 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa status_t status = NO_ERROR; audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; - if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || - (patch->num_sinks == 0 && patch->num_sources != 2) || - patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { + if (!audio_patch_is_valid(patch) || (patch->num_sinks == 0 && patch->num_sources != 2)) { return BAD_VALUE; } // limit number of sources to 1 for now or 2 sources for special cross hw module case. diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7154cb2e50..87cacf8df9 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2834,8 +2834,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks); - if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || - patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { + if (!audio_patch_is_valid(patch)) { return BAD_VALUE; } // only one source per audio patch supported for now diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index cfa9ab1141..b739b8837d 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -29,3 +29,26 @@ LOCAL_CFLAGS := -Werror -Wall LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) include $(BUILD_NATIVE_TEST) + +# system/audio.h utilities test + +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := \ + libbase \ + liblog \ + libmedia_helper \ + libutils + +LOCAL_SRC_FILES := \ + systemaudio_tests.cpp \ + +LOCAL_MODULE := systemaudio_tests + +LOCAL_MODULE_TAGS := tests + +LOCAL_CFLAGS := -Werror -Wall + +LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) + +include $(BUILD_NATIVE_TEST) diff --git a/services/audiopolicy/tests/systemaudio_tests.cpp b/services/audiopolicy/tests/systemaudio_tests.cpp new file mode 100644 index 0000000000..abaae52229 --- /dev/null +++ b/services/audiopolicy/tests/systemaudio_tests.cpp @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#include + +#define LOG_TAG "SysAudio_Test" +#include +#include +#include + +using namespace android; + +TEST(SystemAudioTest, PatchInvalid) { + audio_patch patch{}; + ASSERT_FALSE(audio_patch_is_valid(&patch)); + patch.num_sources = AUDIO_PATCH_PORTS_MAX + 1; + patch.num_sinks = 1; + ASSERT_FALSE(audio_patch_is_valid(&patch)); + patch.num_sources = 1; + patch.num_sinks = AUDIO_PATCH_PORTS_MAX + 1; + ASSERT_FALSE(audio_patch_is_valid(&patch)); + patch.num_sources = 0; + patch.num_sinks = 1; + ASSERT_FALSE(audio_patch_is_valid(&patch)); +} + +TEST(SystemAudioTest, PatchValid) { + const audio_port_config src = { + .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE }; + // It's OK not to have sinks. + ASSERT_TRUE(audio_patch_is_valid((PatchBuilder{}).addSource(src).patch())); + const audio_port_config sink = { + .id = 2, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE }; + ASSERT_TRUE(audio_patch_is_valid((PatchBuilder{}).addSource(src).addSink(sink).patch())); + ASSERT_TRUE(audio_patch_is_valid( + (PatchBuilder{}).addSource(src).addSource(src).addSink(sink).patch())); + ASSERT_TRUE(audio_patch_is_valid( + (PatchBuilder{}).addSource(src).addSink(sink).addSink(sink).patch())); + ASSERT_TRUE(audio_patch_is_valid( + (PatchBuilder{}).addSource(src).addSource(src). + addSink(sink).addSink(sink).patch())); +} + +TEST(SystemAudioTest, PatchHwAvSync) { + audio_port_config device_src_cfg = { + .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE }; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_src_cfg)); + device_src_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_src_cfg)); + device_src_cfg.flags.input = AUDIO_INPUT_FLAG_HW_AV_SYNC; + ASSERT_TRUE(audio_port_config_has_hw_av_sync(&device_src_cfg)); + + audio_port_config device_sink_cfg = { + .id = 1, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE }; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_sink_cfg)); + device_sink_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_sink_cfg)); + device_sink_cfg.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC; + ASSERT_TRUE(audio_port_config_has_hw_av_sync(&device_sink_cfg)); + + audio_port_config mix_sink_cfg = { + .id = 1, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_MIX }; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_sink_cfg)); + mix_sink_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_sink_cfg)); + mix_sink_cfg.flags.input = AUDIO_INPUT_FLAG_HW_AV_SYNC; + ASSERT_TRUE(audio_port_config_has_hw_av_sync(&mix_sink_cfg)); + + audio_port_config mix_src_cfg = { + .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_MIX }; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_src_cfg)); + mix_src_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS; + ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_src_cfg)); + mix_src_cfg.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC; + ASSERT_TRUE(audio_port_config_has_hw_av_sync(&mix_src_cfg)); +} + +TEST(SystemAudioTest, PatchEqual) { + const audio_patch patch1{}, patch2{}; + // Invalid patches are not equal. + ASSERT_FALSE(audio_patches_are_equal(&patch1, &patch2)); + const audio_port_config src = { + .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE }; + const audio_port_config sink = { + .id = 2, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE }; + ASSERT_FALSE(audio_patches_are_equal( + (PatchBuilder{}).addSource(src).patch(), + (PatchBuilder{}).addSource(src).addSink(sink).patch())); + ASSERT_TRUE(audio_patches_are_equal( + (PatchBuilder{}).addSource(src).addSink(sink).patch(), + (PatchBuilder{}).addSource(src).addSink(sink).patch())); + ASSERT_FALSE(audio_patches_are_equal( + (PatchBuilder{}).addSource(src).addSink(sink).patch(), + (PatchBuilder{}).addSource(src).addSource(src).addSink(sink).patch())); + audio_port_config sink_hw_av_sync = sink; + sink_hw_av_sync.config_mask |= AUDIO_PORT_CONFIG_FLAGS; + sink_hw_av_sync.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC; + ASSERT_FALSE(audio_patches_are_equal( + (PatchBuilder{}).addSource(src).addSink(sink).patch(), + (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch())); + ASSERT_TRUE(audio_patches_are_equal( + (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch(), + (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch())); +} -- GitLab From 40863cba7685f3f42041f0a66986cef4c1949d48 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 9 Jul 2018 15:21:14 -0700 Subject: [PATCH 0115/1530] Fix copy/paste error Test: build Change-Id: I176209b42312e19eb831946dc2f7592266e363e4 --- media/ndk/include/media/NdkMediaFormat.h | 2 +- media/ndk/libmediandk.map.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 581c7bf14c..8f37f7b0b3 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -119,7 +119,7 @@ extern const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_FRAME_RATE __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_GRID_COLUMNS __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_GRID_ROWS __INTRODUCED_IN(28); -extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28);; +extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21); extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_IS_ADTS __INTRODUCED_IN(21); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 25511bf7a3..d828d6a2cb 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -50,8 +50,8 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_CSD_1; # var introduced=28 AMEDIAFORMAT_KEY_CSD_2; # var introduced=28 AMEDIAFORMAT_KEY_DISPLAY_CROP; # var introduced=28 - AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; introduced=28 - AMEDIAFORMAT_KEY_DISPLAY_WIDTH; introduced=28 + AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; # var introduced=28 + AMEDIAFORMAT_KEY_DISPLAY_WIDTH; # var introduced=28 AMEDIAFORMAT_KEY_DURATION; # var introduced=21 AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var introduced=21 AMEDIAFORMAT_KEY_FRAME_RATE; # var introduced=21 -- GitLab From c1a30c4d8a5ed581ccc9d42791044c43565e6d71 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 10 Jul 2018 15:46:24 +0100 Subject: [PATCH 0116/1530] Remove unnecessary libcore includes This commit removes includes that don't appear to be needed. Test: build Bug: 111055375 Change-Id: I72465d0af56e408c5e99c1fd304bf2e7ff6d9f83 --- services/mediaanalytics/Android.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk index 2eeb7fa386..5b20e61479 100644 --- a/services/mediaanalytics/Android.mk +++ b/services/mediaanalytics/Android.mk @@ -34,8 +34,7 @@ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/av/include/camera \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/frameworks/native/include/media/hardware \ - $(TOP)/external/tremolo/Tremolo \ - libcore/include + $(TOP)/external/tremolo/Tremolo LOCAL_MODULE:= mediametrics -- GitLab From 39b09b52e313e89c3493927b379d2e256bf04bd9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 29 Jun 2018 12:24:40 -0700 Subject: [PATCH 0117/1530] audioserver: log system event for binder timeouts Add event logging in case of audioserver watchdog to help collect statistics. Test: make. Simulate watchdog and check events in logcat. Change-Id: I4716852c77d56703ad5f3dfc2500f598a2b80a12 --- include/media/EventLog.h | 1 + include/media/TimeCheck.h | 2 +- media/libaudioclient/IAudioFlinger.cpp | 3 +- media/libaudioclient/IAudioPolicyService.cpp | 3 +- media/libmedia/Android.bp | 2 +- media/utils/Android.bp | 3 ++ media/utils/EventLogTags.logtags | 41 +++++++++++++++++++ media/{libmedia => utils}/TimeCheck.cpp | 7 +++- media/utils/include/mediautils/EventLog.h | 28 +++++++++++++ .../include/mediautils}/TimeCheck.h | 0 10 files changed, 85 insertions(+), 5 deletions(-) create mode 120000 include/media/EventLog.h create mode 100644 media/utils/EventLogTags.logtags rename media/{libmedia => utils}/TimeCheck.cpp (92%) create mode 100644 media/utils/include/mediautils/EventLog.h rename media/{libmedia/include/media => utils/include/mediautils}/TimeCheck.h (100%) diff --git a/include/media/EventLog.h b/include/media/EventLog.h new file mode 120000 index 0000000000..9b2c4bfb80 --- /dev/null +++ b/include/media/EventLog.h @@ -0,0 +1 @@ +../../media/utils/include/mediautils/EventLog.h \ No newline at end of file diff --git a/include/media/TimeCheck.h b/include/media/TimeCheck.h index e3ef134404..85e17f91c2 120000 --- a/include/media/TimeCheck.h +++ b/include/media/TimeCheck.h @@ -1 +1 @@ -../../media/libmedia/include/media/TimeCheck.h \ No newline at end of file +../../media/utils/include/mediautils/TimeCheck.h \ No newline at end of file diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 37c62a81dd..84e8beef17 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -949,7 +949,8 @@ status_t BnAudioFlinger::onTransact( break; } - TimeCheck check("IAudioFlinger"); + std::string tag("IAudioFlinger command " + std::to_string(code)); + TimeCheck check(tag.c_str()); switch (code) { case CREATE_TRACK: { diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 316105c56f..cf4b600b57 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -947,7 +947,8 @@ status_t BnAudioPolicyService::onTransact( break; } - TimeCheck check("IAudioPolicyService"); + std::string tag("IAudioPolicyService command " + std::to_string(code)); + TimeCheck check(tag.c_str()); switch (code) { case SET_DEVICE_CONNECTION_STATE: { diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index a22819a688..e6d6b3e45f 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -20,7 +20,7 @@ cc_library { vndk: { enabled: true, }, - srcs: ["AudioParameter.cpp", "TypeConverter.cpp", "TimeCheck.cpp"], + srcs: ["AudioParameter.cpp", "TypeConverter.cpp"], cflags: [ "-Werror", "-Wno-error=deprecated-declarations", diff --git a/media/utils/Android.bp b/media/utils/Android.bp index de8e46a016..13b66ed950 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -22,6 +22,7 @@ cc_library { "ProcessInfo.cpp", "SchedulingPolicyService.cpp", "ServiceUtilities.cpp", + "TimeCheck.cpp", ], shared_libs: [ "libbinder", @@ -31,6 +32,8 @@ cc_library { "libmemunreachable", ], + logtags: ["EventLogTags.logtags"], + cflags: [ "-Wall", "-Wextra", diff --git a/media/utils/EventLogTags.logtags b/media/utils/EventLogTags.logtags new file mode 100644 index 0000000000..67f0ea8cdb --- /dev/null +++ b/media/utils/EventLogTags.logtags @@ -0,0 +1,41 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# See system/core/logcat/event.logtags for the master copy of the tags. + +# 61000 - 61199 reserved for audioserver + +61000 audioserver_binder_timeout (command|3) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform. diff --git a/media/libmedia/TimeCheck.cpp b/media/utils/TimeCheck.cpp similarity index 92% rename from media/libmedia/TimeCheck.cpp rename to media/utils/TimeCheck.cpp index dab5d4f336..59cf4ef393 100644 --- a/media/libmedia/TimeCheck.cpp +++ b/media/utils/TimeCheck.cpp @@ -15,7 +15,9 @@ */ +#include #include +#include namespace android { @@ -81,7 +83,10 @@ bool TimeCheck::TimeCheckThread::threadLoop() status = mCond.waitRelative(mMutex, waitTimeNs); } } - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "TimeCheck timeout for %s", tag); + if (status != NO_ERROR) { + LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag); + LOG_ALWAYS_FATAL("TimeCheck timeout for %s", tag); + } return true; } diff --git a/media/utils/include/mediautils/EventLog.h b/media/utils/include/mediautils/EventLog.h new file mode 100644 index 0000000000..553d3bda20 --- /dev/null +++ b/media/utils/include/mediautils/EventLog.h @@ -0,0 +1,28 @@ +/* + * 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. + */ +#ifndef ANDROID_AUDIO_EVENT_LOG_H_ +#define ANDROID_AUDIO_EVENT_LOG_H_ + +namespace android { + +// keep values in sync with frameworks/av/media/utils/EventLogTags.logtags +enum { + LOGTAG_AUDIO_BINDER_TIMEOUT = 61000, +}; + +} // namespace android + +#endif // ANDROID_AUDIO_EVENT_LOG_H_ diff --git a/media/libmedia/include/media/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h similarity index 100% rename from media/libmedia/include/media/TimeCheck.h rename to media/utils/include/mediautils/TimeCheck.h -- GitLab From f323451b24b06c4f3375488409af7c93ce08ba73 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 3 Jul 2018 14:51:47 -0700 Subject: [PATCH 0118/1530] AudioFlinger: Dump Direct and Offload timestamp statistics Test: AudioFlinger dumpsys during offload playback Bug: 80502521 Change-Id: I8b4cb5eb47000a075a8ecaefdf4eb4c4ee23a304 --- services/audioflinger/AudioFlinger.h | 1 + services/audioflinger/Threads.cpp | 29 ++++++++++++++++++++++++++-- services/audioflinger/Threads.h | 17 ++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 0276cadec2..0bb492a184 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -62,6 +62,7 @@ #include #include +#include #include #include diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 70af5c6916..f68bfeea4b 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -857,7 +857,8 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u if (mType == RECORD || mType == MIXER || mType == DUPLICATING - || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) { + || mType == DIRECT + || mType == OFFLOAD) { dprintf(fd, " Timestamp stats: %s\n", mTimestampVerifier.toString().c_str()); } @@ -2482,6 +2483,11 @@ void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence) Mutex::Autolock _l(mLock); // reject out of sequence requests if ((mDrainSequence & 1) && (sequence == mDrainSequence)) { + // Register discontinuity when HW drain is completed because that can cause + // the timestamp frame position to reset to 0 for direct and offload threads. + // (Out of sequence requests are ignored, since the discontinuity would be handled + // elsewhere, e.g. in flush). + mTimestampVerifier.discontinuity(); mDrainSequence &= ~1; mWaitWorkCV.signal(); } @@ -3190,6 +3196,15 @@ bool AudioFlinger::PlaybackThread::threadLoop() checkSilentMode_l(); + // DIRECT and OFFLOAD threads should reset frame count to zero on stop/flush + // TODO: add confirmation checks: + // 1) DIRECT threads and linear PCM format really resets to 0? + // 2) Is frame count really valid if not linear pcm? + // 3) Are all 64 bits of position returned, not just lowest 32 bits? + if (mType == OFFLOAD || mType == DIRECT) { + mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO); + } + while (!exitPending()) { // Log merge requests are performed during AudioFlinger binder transactions, but @@ -3216,7 +3231,8 @@ bool AudioFlinger::PlaybackThread::threadLoop() // Collect timestamp statistics for the Playback Thread types that support it. if (mType == MIXER || mType == DUPLICATING - || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) { // no indentation + || mType == DIRECT + || mType == OFFLOAD) { // no indentation // Gather the framesReleased counters for all active tracks, // and associate with the sink frames written out. We need // this to convert the sink timestamp to the track timestamp. @@ -5622,6 +5638,7 @@ void AudioFlinger::DirectOutputThread::flushHw_l() mOutput->flush(); mHwPaused = false; mFlushPending = false; + mTimestampVerifier.discontinuity(); // DIRECT and OFFLOADED flush resets frame count. } int64_t AudioFlinger::DirectOutputThread::computeWaitTimeNs_l() const { @@ -5956,6 +5973,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr track->presentationComplete(framesWritten, audioHALFrames); track->reset(); tracksToRemove->add(track); + // DIRECT and OFFLOADED stop resets frame counts. + if (!mUseAsyncWrite) { + // If we don't get explicit drain notification we must + // register discontinuity regardless of whether this is + // the previous (!last) or the upcoming (last) track + // to avoid skipping the discontinuity. + mTimestampVerifier.discontinuity(); + } } } else { // No buffers for this track. Give it a few chances to diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 064e29178c..0c833f19df 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1224,6 +1224,23 @@ public: virtual bool hasFastMixer() const { return false; } virtual int64_t computeWaitTimeNs_l() const override; + + status_t threadloop_getHalTimestamp_l(ExtendedTimestamp *timestamp) const override { + // For DIRECT and OFFLOAD threads, query the output sink directly. + if (mOutput != nullptr) { + uint64_t uposition64; + struct timespec time; + if (mOutput->getPresentationPosition( + &uposition64, &time) == OK) { + timestamp->mPosition[ExtendedTimestamp::LOCATION_KERNEL] + = (int64_t)uposition64; + timestamp->mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] + = audio_utils_ns_from_timespec(&time); + return NO_ERROR; + } + } + return INVALID_OPERATION; + } }; class OffloadThread : public DirectOutputThread { -- GitLab From 84c05cdca73f0378c92ed06147463c2962b5c1b6 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 10 Jul 2018 16:56:27 -0700 Subject: [PATCH 0119/1530] audio policy: limit voice call index to max index Workaround for issue 111194621: limit voice call volume index to max value before computing voice volume. Bug: 111194621 Test: repro steps in bug. Change-Id: Id965a3cc9d0d20900c910f43f41d998917e6040d --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 28d2ea68c4..10b9ebeaf3 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5541,7 +5541,14 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); + // FIXME: issue 111194621: this should not happen + int maxIndex = mVolumeCurves->getVolumeIndexMax(stream); + if (index > maxIndex) { + ALOGW("%s limiting voice call index %d to max index %d", + __FUNCTION__, index, maxIndex); + index = maxIndex; + } + voiceVolume = (float)index/(float)maxIndex; } else { voiceVolume = 1.0; } -- GitLab From 8180b74a8e2c02eee7938b32fd0ecfeda53c91be Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Tue, 10 Jul 2018 15:23:29 -0700 Subject: [PATCH 0120/1530] Rearrange variables in PerformanceAnalysis::reportPerformance in preparation for moving to separate file later. Test: build Change-Id: I0a13f4a2637d1e783515dbeb98fbd59efec24d44 --- media/libnblog/PerformanceAnalysis.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp index f09e93d3f7..22a30b982f 100644 --- a/media/libnblog/PerformanceAnalysis.cpp +++ b/media/libnblog/PerformanceAnalysis.cpp @@ -254,7 +254,7 @@ inline double logRound(double x, double mean) { // of PerformanceAnalysis void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_t hash, int maxHeight) { - if (mHists.empty()) { + if (mHists.empty() || body == nullptr) { return; } @@ -273,10 +273,13 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ } } - // underscores and spaces length corresponds to maximum width of histogram - static const int kLen = 200; - std::string underscores(kLen, '_'); - std::string spaces(kLen, ' '); + static const int SIZE = 128; + char title[SIZE]; + snprintf(title, sizeof(title), "\n%s %3.2f %s\n%s%d, %lld, %lld\n", + "Occurrences in", (elapsedMs / kMsPerSec), "seconds of audio:", + "Thread, hash, starting timestamp: ", author, + static_cast(hash), static_cast(startingTs)); + static const char * const kLabel = "ms"; auto it = buckets.begin(); double maxDelta = it->first; @@ -299,11 +302,7 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ scalingFactor = (height + maxHeight) / maxHeight; height /= scalingFactor; } - body->appendFormat("\n%*s %3.2f %s", leftPadding + 11, - "Occurrences in", (elapsedMs / kMsPerSec), "seconds of audio:"); - body->appendFormat("\n%*s%d, %lld, %lld\n", leftPadding + 11, - "Thread, hash, starting timestamp: ", author, - static_cast(hash), static_cast(startingTs)); + body->appendFormat("%s", title); // write histogram label line with bucket values body->appendFormat("\n%s", " "); body->appendFormat("%*s", leftPadding, " "); @@ -312,6 +311,11 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ body->appendFormat("%*d", colWidth, x.second); } // write histogram ascii art + // underscores and spaces length corresponds to maximum width of histogram + static const int kLen = 200; + static const std::string underscores(kLen, '_'); + static const std::string spaces(kLen, ' '); + body->appendFormat("\n%s", " "); for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) { const int value = 1 << row; @@ -335,7 +339,7 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ const int colWidth = numberWidth(x.first, leftPadding); body->appendFormat("%*.*f", colWidth, 1, x.first); } - body->appendFormat("%.*s%s", bucketWidth, spaces.c_str(), "ms\n"); + body->appendFormat("%.*s%s\n", bucketWidth, spaces.c_str(), kLabel); // Now report glitches body->appendFormat("\ntime elapsed between glitches and glitch timestamps:\n"); -- GitLab From 3839bc0122c23a33d52ff0247bfa70016d9769be Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 10 Jul 2018 18:33:34 -0700 Subject: [PATCH 0121/1530] audio policy: rescale volume index in setStreamVolumeIndex Fix a problem in setStreamVolumeIndex() introduced by commit dcd4ab18 where stream volumes where applied systematically when in call for all streams regardless of streamsMatchForvolume(). Also add index rescaling as in theory two streams matching for volume do not have necessarily the same index range. Bug: 111194621 Test: repro steps in bug. Change-Id: I713b35921d4df19b521fc98281636a9c17483998 --- .../managerdefault/AudioPolicyManager.cpp | 32 ++++++++++++------- .../managerdefault/AudioPolicyManager.h | 4 +++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 10b9ebeaf3..9b10d68742 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2265,7 +2265,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, sp desc = mOutputs.valueAt(i); audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device()); for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { - if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream) || isInCall())) { + if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) { continue; } if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) { @@ -2286,13 +2286,15 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, applyVolume = !mVolumeCurves->hasVolumeIndexForDevice( stream, curStreamDevice); } - + // rescale index before applying to curStream as ranges may be different for + // stream and curStream + int idx = rescaleVolumeIndex(index, stream, (audio_stream_type_t)curStream); if (applyVolume) { //FIXME: workaround for truncated touch sounds // delayed volume change for system stream to be removed when the problem is // handled by system UI status_t volStatus = - checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice, + checkAndSetVolume((audio_stream_type_t)curStream, idx, desc, curDevice, (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0); if (volStatus != NO_ERROR) { status = volStatus; @@ -5499,6 +5501,21 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, return volumeDB; } +int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, + audio_stream_type_t srcStream, + audio_stream_type_t dstStream) +{ + if (srcStream == dstStream) { + return srcIndex; + } + float minSrc = (float)mVolumeCurves->getVolumeIndexMin(srcStream); + float maxSrc = (float)mVolumeCurves->getVolumeIndexMax(srcStream); + float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream); + float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream); + + return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc)); +} + status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, int index, const sp& outputDesc, @@ -5541,14 +5558,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - // FIXME: issue 111194621: this should not happen - int maxIndex = mVolumeCurves->getVolumeIndexMax(stream); - if (index > maxIndex) { - ALOGW("%s limiting voice call index %d to max index %d", - __FUNCTION__, index, maxIndex); - index = maxIndex; - } - voiceVolume = (float)index/(float)maxIndex; + voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); } else { voiceVolume = 1.0; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 136e52256f..8a0e2c3a6b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -353,6 +353,10 @@ protected: int index, audio_devices_t device); + // rescale volume index from srcStream within range of dstStream + int rescaleVolumeIndex(int srcIndex, + audio_stream_type_t srcStream, + audio_stream_type_t dstStream); // check that volume change is permitted, compute and send new volume to audio hardware virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index, const sp& outputDesc, -- GitLab From 661e90a66274a4a10d0b18166688507692569b42 Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Wed, 11 Jul 2018 06:20:25 -0700 Subject: [PATCH 0122/1530] AC4Parser: Avoid memory leak Our 'presentation_name' was allocated, but never deleted. Instead of creating a temporary char array, we directly use std::string to build up this name, and avoid the leak. Test: Treehugger Change-Id: Ie3baf602d24edffe2408a77e0fae714e6a8e8baf --- media/extractors/mp4/AC4Parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp index 167d474b23..a95c2db8c1 100644 --- a/media/extractors/mp4/AC4Parser.cpp +++ b/media/extractors/mp4/AC4Parser.cpp @@ -577,14 +577,14 @@ bool AC4DSIParser::parse() { BYTE_ALIGN; CHECK_BITS_LEFT(16); uint32_t name_len = mBitReader.getBits(16); - char* presentation_name = new char[name_len+1]; CHECK_BITS_LEFT(name_len * 8); + std::string &presentation_name = + mPresentations[presentation].mDescription; + presentation_name.clear(); + presentation_name.resize(name_len); for (uint32_t i = 0; i < name_len; i++) { presentation_name[i] = (char)(mBitReader.getBits(8)); } - presentation_name[name_len] = '\0'; - std::string description(presentation_name, name_len); - mPresentations[presentation].mDescription = description; CHECK_BITS_LEFT(5); uint32_t n_targets = mBitReader.getBits(5); CHECK_BITS_LEFT(n_targets * (3 + 8)); -- GitLab From 37977159598ff6a85d37ee8dad812cb0b24dc737 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 11 Jul 2018 15:54:44 -0700 Subject: [PATCH 0123/1530] APM: Coalesce calls to check and update outputs Introduce a method 'checkForDeviceAndOutputChanges' that calls 'check...' and 'update...' methods in the correct sequence. Bug: 63901775 Test: make Change-Id: I32fc39d5b62a29e9c1801cb89046fad6fa156073 --- .../managerdefault/AudioPolicyManager.cpp | 57 +++++++++++-------- .../managerdefault/AudioPolicyManager.h | 8 +++ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 687dd60731..94090230e8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -202,27 +202,25 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, return BAD_VALUE; } - // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP - // output is suspended before any tracks are moved to it - checkA2dpSuspend(); - checkOutputForAllStrategies(); - // outputs must be closed after checkOutputForAllStrategies() is executed - if (!outputs.isEmpty()) { - for (audio_io_handle_t output : outputs) { - sp desc = mOutputs.valueFor(output); - // close unused outputs after device disconnection or direct outputs that have been - // opened by checkOutputsForDevice() to query dynamic parameters - if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || - (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && - (desc->mDirectOpenCount == 0))) { - closeOutput(output); + checkForDeviceAndOutputChanges([&]() { + // outputs must be closed after checkOutputForAllStrategies() is executed + if (!outputs.isEmpty()) { + for (audio_io_handle_t output : outputs) { + sp desc = mOutputs.valueFor(output); + // close unused outputs after device disconnection or direct outputs that have been + // opened by checkOutputsForDevice() to query dynamic parameters + if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || + (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && + (desc->mDirectOpenCount == 0))) { + closeOutput(output); + } } + // check A2DP again after closing A2DP output to reset mA2dpSuspended if needed + return true; } - // check again after closing A2DP output to reset mA2dpSuspended if needed - checkA2dpSuspend(); - } + return false; + }); - updateDevicesAndOutputs(); if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) { audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); updateCallRouting(newDevice); @@ -565,9 +563,7 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) || (is_state_in_call(state) && (state != oldState))); // check for device and output changes triggered by new phone state - checkA2dpSuspend(); - checkOutputForAllStrategies(); - updateDevicesAndOutputs(); + checkForDeviceAndOutputChanges(); int delayMs = 0; if (isStateInCall(state)) { @@ -667,9 +663,7 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, (usage == AUDIO_POLICY_FORCE_FOR_SYSTEM); // check for device and output changes triggered by new force usage - checkA2dpSuspend(); - checkOutputForAllStrategies(); - updateDevicesAndOutputs(); + checkForDeviceAndOutputChanges(); //FIXME: workaround for truncated touch sounds // to be removed when the problem is handled by system UI @@ -4631,6 +4625,21 @@ bool AudioPolicyManager::vectorsEqual(SortedVector& outputs1, return true; } +void AudioPolicyManager::checkForDeviceAndOutputChanges() +{ + checkForDeviceAndOutputChanges([](){ return false; }); +} + +void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function onOutputsChecked) +{ + // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP + // output is suspended before any tracks are moved to it + checkA2dpSuspend(); + checkOutputForAllStrategies(); + if (onOutputsChecked()) checkA2dpSuspend(); + updateDevicesAndOutputs(); +} + void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) { audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 136e52256f..30109898e8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -406,6 +407,13 @@ protected: // close an input. void closeInput(audio_io_handle_t input); + // runs all the checks required for accomodating changes in devices and outputs + // if 'onOutputsChecked' callback is provided, it is executed after the outputs + // check via 'checkOutputForAllStrategies'. If the callback returns 'true', + // A2DP suspend status is rechecked. + void checkForDeviceAndOutputChanges(); + void checkForDeviceAndOutputChanges(std::function onOutputsChecked); + // checks and if necessary changes outputs used for all strategies. // must be called every time a condition that affects the output choice for a given strategy // changes: connected device, phone state, force use... -- GitLab From 2e7f58fb7f7536ff1a5f8469466071c796c519ef Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 11 Jul 2018 14:00:29 -0700 Subject: [PATCH 0124/1530] Camera: Use physical camera's capability for physical stream check If stream being created is a physical stream, the size check should be against the physical stream's capability. Test: Camera CTS Bug: 111288509 Change-Id: Iad8814562694f16f16d9656ec482b8b21a859c44 --- .../api2/CameraDeviceClient.cpp | 15 +++++--- .../api2/CameraDeviceClient.h | 3 +- .../common/CameraDeviceBase.h | 4 +++ .../device3/Camera3Device.cpp | 35 +++++++++++++++++-- .../libcameraservice/device3/Camera3Device.h | 2 ++ 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 98d053448e..baf051a1c2 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -661,7 +661,8 @@ binder::Status CameraDeviceClient::createStream( } sp surface; - res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer); + res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer, + physicalCameraId); if (!res.isOk()) return res; @@ -889,6 +890,8 @@ binder::Status CameraDeviceClient::updateOutputConfiguration(int streamId, const std::vector >& bufferProducers = outputConfiguration.getGraphicBufferProducers(); + String8 physicalCameraId(outputConfiguration.getPhysicalCameraId()); + auto producerCount = bufferProducers.size(); if (producerCount == 0) { ALOGE("%s: bufferProducers must not be empty", __FUNCTION__); @@ -942,7 +945,7 @@ binder::Status CameraDeviceClient::updateOutputConfiguration(int streamId, OutputStreamInfo outInfo; sp surface; res = createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false, surface, - newOutputsMap.valueAt(i)); + newOutputsMap.valueAt(i), physicalCameraId); if (!res.isOk()) return res; @@ -1021,7 +1024,8 @@ bool CameraDeviceClient::isPublicFormat(int32_t format) binder::Status CameraDeviceClient::createSurfaceFromGbp( OutputStreamInfo& streamInfo, bool isStreamInfoValid, - sp& surface, const sp& gbp) { + sp& surface, const sp& gbp, + const String8& physicalId) { // bufferProducer must be non-null if (gbp == nullptr) { @@ -1098,7 +1102,7 @@ binder::Status CameraDeviceClient::createSurfaceFromGbp( // Round dimensions to the nearest dimensions available for this format if (flexibleConsumer && isPublicFormat(format) && !CameraDeviceClient::roundBufferDimensionNearest(width, height, - format, dataSpace, mDevice->info(), /*out*/&width, /*out*/&height)) { + format, dataSpace, mDevice->info(physicalId), /*out*/&width, /*out*/&height)) { String8 msg = String8::format("Camera %s: No supported stream configurations with " "format %#x defined, failed to create output stream", mCameraIdStr.string(), format); @@ -1468,6 +1472,7 @@ binder::Status CameraDeviceClient::finalizeOutputConfigurations(int32_t streamId const std::vector >& bufferProducers = outputConfiguration.getGraphicBufferProducers(); + String8 physicalId(outputConfiguration.getPhysicalCameraId()); if (bufferProducers.size() == 0) { ALOGE("%s: bufferProducers must not be empty", __FUNCTION__); @@ -1521,7 +1526,7 @@ binder::Status CameraDeviceClient::finalizeOutputConfigurations(int32_t streamId sp surface; res = createSurfaceFromGbp(mStreamInfoMap[streamId], true /*isStreamInfoValid*/, - surface, bufferProducer); + surface, bufferProducer, physicalId); if (!res.isOk()) return res; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 5aaf5aaee3..c30561d488 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -258,7 +258,8 @@ private: // Create a Surface from an IGraphicBufferProducer. Returns error if // IGraphicBufferProducer's property doesn't match with streamInfo binder::Status createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid, - sp& surface, const sp& gbp); + sp& surface, const sp& gbp, + const String8& physicalCameraId); // Utility method to insert the surface into SurfaceMap diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index 0ba74038c5..98c1b5e971 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -69,6 +69,10 @@ class CameraDeviceBase : public virtual RefBase { * The device's static characteristics metadata buffer */ virtual const CameraMetadata& info() const = 0; + /** + * The physical camera device's static characteristics metadata buffer + */ + virtual const CameraMetadata& info(const String8& physicalId) const = 0; struct PhysicalCameraSettings { std::string cameraId; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 543914e5a4..0db5642030 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -121,11 +121,25 @@ status_t Camera3Device::initialize(sp manager, const Stri res = manager->getCameraCharacteristics(mId.string(), &mDeviceInfo); if (res != OK) { - SET_ERR_L("Could not retrive camera characteristics: %s (%d)", strerror(-res), res); + SET_ERR_L("Could not retrieve camera characteristics: %s (%d)", strerror(-res), res); session->close(); return res; } + std::vector physicalCameraIds; + bool isLogical = CameraProviderManager::isLogicalCamera(mDeviceInfo, &physicalCameraIds); + if (isLogical) { + for (auto& physicalId : physicalCameraIds) { + res = manager->getCameraCharacteristics(physicalId, &mPhysicalDeviceInfoMap[physicalId]); + if (res != OK) { + SET_ERR_L("Could not retrieve camera %s characteristics: %s (%d)", + physicalId.c_str(), strerror(-res), res); + session->close(); + return res; + } + } + } + std::shared_ptr queue; auto requestQueueRet = session->getCaptureRequestMetadataQueue( [&queue](const auto& descriptor) { @@ -719,7 +733,7 @@ status_t Camera3Device::dump(int fd, const Vector &args) { return OK; } -const CameraMetadata& Camera3Device::info() const { +const CameraMetadata& Camera3Device::info(const String8& physicalId) const { ALOGVV("%s: E", __FUNCTION__); if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED || mStatus == STATUS_ERROR)) { @@ -727,7 +741,22 @@ const CameraMetadata& Camera3Device::info() const { mStatus == STATUS_ERROR ? "when in error state" : "before init"); } - return mDeviceInfo; + if (physicalId.isEmpty()) { + return mDeviceInfo; + } else { + std::string id(physicalId.c_str()); + if (mPhysicalDeviceInfoMap.find(id) != mPhysicalDeviceInfoMap.end()) { + return mPhysicalDeviceInfoMap.at(id); + } else { + ALOGE("%s: Invalid physical camera id %s", __FUNCTION__, physicalId.c_str()); + return mDeviceInfo; + } + } +} + +const CameraMetadata& Camera3Device::info() const { + String8 emptyId; + return info(emptyId); } status_t Camera3Device::checkStatusOkToCaptureLocked() { diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index d8fe19fa66..ef3cbc4e9c 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -101,6 +101,7 @@ class Camera3Device : status_t disconnect() override; status_t dump(int fd, const Vector &args) override; const CameraMetadata& info() const override; + const CameraMetadata& info(const String8& physicalId) const override; // Capture and setStreamingRequest will configure streams if currently in // idle state @@ -379,6 +380,7 @@ class Camera3Device : sp mInterface; CameraMetadata mDeviceInfo; + std::unordered_map mPhysicalDeviceInfoMap; CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT]; -- GitLab From adca70f3a1526943d69b506922553b15fa58564c Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 9 Jul 2018 12:49:25 -0700 Subject: [PATCH 0125/1530] PatchPanel: store "downstream" patches for a device When the system inserts an intermediate processing module between audio flinger and hardware HAL, and they are connected via a software patch, the threads of this patch need to receive metadata passed to the thread serving the "upstream" device of the processing module. PatchPanel stores an association between "upstream" streams and the software patch. This allows audio flinger to access the patch threads. For more info, see the comments in PatchPanel.h Bug: 63901775 Test: manual Change-Id: I60e578c37a1bc7385daf32d379ea143120584a31 --- services/audioflinger/AudioFlinger.cpp | 6 ++ services/audioflinger/AudioHwDevice.h | 7 ++ services/audioflinger/PatchPanel.cpp | 138 ++++++++++++++++++++----- services/audioflinger/PatchPanel.h | 66 +++++++++++- 4 files changed, 192 insertions(+), 25 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 53a4ce9e0c..d186887fc8 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1817,6 +1817,10 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) mHardwareStatus = AUDIO_HW_IDLE; } + if (strcmp(name, AUDIO_HAL_SERVICE_NAME_MSD) == 0) { + // An MSD module is inserted before hardware modules in order to mix encoded streams. + flags = static_cast(flags | AudioHwDevice::AHWD_IS_INSERT); + } audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE); mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags)); @@ -2096,6 +2100,7 @@ sp AudioFlinger::openOutput_l(audio_module_handle_t mo *output, thread.get()); } mPlaybackThreads.add(*output, thread); + mPatchPanel.notifyStreamOpened(outHwDev, *output); return thread; } } @@ -2231,6 +2236,7 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) const sp ioDesc = new AudioIoDescriptor(); ioDesc->mIoHandle = output; ioConfigChanged(AUDIO_OUTPUT_CLOSED, ioDesc); + mPatchPanel.notifyStreamClosed(output); } // The thread entity (active unit of execution) is no longer running here, // but the ThreadBase container still exists. diff --git a/services/audioflinger/AudioHwDevice.h b/services/audioflinger/AudioHwDevice.h index eb826c650e..d4299b09e0 100644 --- a/services/audioflinger/AudioHwDevice.h +++ b/services/audioflinger/AudioHwDevice.h @@ -35,6 +35,9 @@ public: enum Flags { AHWD_CAN_SET_MASTER_VOLUME = 0x1, AHWD_CAN_SET_MASTER_MUTE = 0x2, + // Means that this isn't a terminal module, and software patches + // are used to transport audio data further. + AHWD_IS_INSERT = 0x4, }; AudioHwDevice(audio_module_handle_t handle, @@ -55,6 +58,10 @@ public: return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); } + bool isInsert() const { + return (0 != (mFlags & AHWD_IS_INSERT)); + } + audio_module_handle_t handle() const { return mHandle; } const char *moduleName() const { return mModuleName; } sp hwDevice() const { return mHwDevice; } diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 0caa0af778..7f46747d9c 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -83,6 +83,16 @@ status_t AudioFlinger::listAudioPatches(unsigned int *num_patches, return mPatchPanel.listAudioPatches(num_patches, patches); } +status_t AudioFlinger::PatchPanel::SoftwarePatch::getLatencyMs_l(double *latencyMs) const +{ + const auto& iter = mPatchPanel.mPatches.find(mPatchHandle); + if (iter != mPatchPanel.mPatches.end()) { + return iter->second.getLatencyMs(latencyMs); + } else { + return BAD_VALUE; + } +} + /* List connected audio ports and their attributes */ status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused, struct audio_port *ports __unused) @@ -159,21 +169,21 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } } mPatches.erase(iter); + removeSoftwarePatchFromInsertedModules(*handle); } } Patch newPatch{*patch}; + audio_module_handle_t insertedModule = AUDIO_MODULE_HANDLE_NONE; switch (patch->sources[0].type) { case AUDIO_PORT_TYPE_DEVICE: { audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; - ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule); - if (index < 0) { - ALOGW("%s() bad src hw module %d", __func__, srcModule); + AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(srcModule); + if (!audioHwDevice) { status = BAD_VALUE; goto exit; } - AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index); for (unsigned int i = 0; i < patch->num_sinks; i++) { // support only one sink if connection to a mix or across HW modules if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || @@ -285,6 +295,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa if (status != NO_ERROR) { goto exit; } + if (audioHwDevice->isInsert()) { + insertedModule = audioHwDevice->handle(); + } } else { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { sp thread = mAudioFlinger.checkRecordThread_l( @@ -364,6 +377,9 @@ exit: *handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH); newPatch.mHalHandle = halHandle; mPatches.insert(std::make_pair(*handle, std::move(newPatch))); + if (insertedModule != AUDIO_MODULE_HANDLE_NONE) { + addSoftwarePatchToInsertedModules(insertedModule, *handle); + } ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle); } else { newPatch.clearConnections(this); @@ -511,21 +527,17 @@ status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const return OK; } -String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) +String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const { - String8 result; - // TODO: Consider table dump form for patches, just like tracks. - result.appendFormat("Patch %d: thread %p => thread %p", - myHandle, mRecord.thread().get(), mPlayback.thread().get()); + String8 result = String8::format("Patch %d: thread %p => thread %p", + myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get()); // add latency if it exists double latencyMs; if (getLatencyMs(&latencyMs) == OK) { result.appendFormat(" latency: %.2lf", latencyMs); } - - result.append("\n"); return result; } @@ -596,6 +608,7 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle } mPatches.erase(iter); + removeSoftwarePatchFromInsertedModules(handle); return status; } @@ -607,35 +620,114 @@ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __ return NO_ERROR; } -sp AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module) +status_t AudioFlinger::PatchPanel::getDownstreamSoftwarePatches( + audio_io_handle_t stream, + std::vector *patches) const +{ + for (const auto& module : mInsertedModules) { + if (module.second.streams.count(stream)) { + for (const auto& patchHandle : module.second.sw_patches) { + const auto& patch_iter = mPatches.find(patchHandle); + if (patch_iter != mPatches.end()) { + const Patch &patch = patch_iter->second; + patches->emplace_back(*this, patchHandle, + patch.mPlayback.const_thread()->id(), + patch.mRecord.const_thread()->id()); + } else { + ALOGE("Stale patch handle in the cache: %d", patchHandle); + } + } + return OK; + } + } + // The stream is not associated with any of inserted modules. + return BAD_VALUE; +} + +void AudioFlinger::PatchPanel::notifyStreamOpened( + AudioHwDevice *audioHwDevice, audio_io_handle_t stream) +{ + if (audioHwDevice->isInsert()) { + mInsertedModules[audioHwDevice->handle()].streams.insert(stream); + } +} + +void AudioFlinger::PatchPanel::notifyStreamClosed(audio_io_handle_t stream) +{ + for (auto& module : mInsertedModules) { + module.second.streams.erase(stream); + } +} + +AudioHwDevice* AudioFlinger::PatchPanel::findAudioHwDeviceByModule(audio_module_handle_t module) { if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr; ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module); if (index < 0) { + ALOGW("%s() bad hw module %d", __func__, module); return nullptr; } - return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice(); + return mAudioFlinger.mAudioHwDevs.valueAt(index); } -void AudioFlinger::PatchPanel::dump(int fd) +sp AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module) { + AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(module); + return audioHwDevice ? audioHwDevice->hwDevice() : nullptr; +} + +void AudioFlinger::PatchPanel::addSoftwarePatchToInsertedModules( + audio_module_handle_t module, audio_patch_handle_t handle) +{ + mInsertedModules[module].sw_patches.insert(handle); +} + +void AudioFlinger::PatchPanel::removeSoftwarePatchFromInsertedModules( + audio_patch_handle_t handle) +{ + for (auto& module : mInsertedModules) { + module.second.sw_patches.erase(handle); + } +} + +void AudioFlinger::PatchPanel::dump(int fd) const +{ + String8 patchPanelDump; + const char *indent = " "; + // Only dump software patches. bool headerPrinted = false; - for (auto& iter : mPatches) { + for (const auto& iter : mPatches) { if (iter.second.isSoftware()) { if (!headerPrinted) { - String8 header("\nSoftware patches:\n"); - write(fd, header.string(), header.size()); + patchPanelDump += "\nSoftware patches:\n"; + headerPrinted = true; + } + patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string()); + } + } + + headerPrinted = false; + for (const auto& module : mInsertedModules) { + if (!module.second.streams.empty() || !module.second.sw_patches.empty()) { + if (!headerPrinted) { + patchPanelDump += "\nTracked inserted modules:\n"; headerPrinted = true; } - String8 patchDump(" "); - patchDump.append(iter.second.dump(iter.first)); - write(fd, patchDump.string(), patchDump.size()); + String8 moduleDump = String8::format("Module %d: I/O handles: ", module.first); + for (const auto& stream : module.second.streams) { + moduleDump.appendFormat("%d ", stream); + } + moduleDump.append("; SW Patches: "); + for (const auto& patch : module.second.sw_patches) { + moduleDump.appendFormat("%d ", patch); + } + patchPanelDump.appendFormat("%s%s\n", indent, moduleDump.string()); } } - if (headerPrinted) { - String8 trailing("\n"); - write(fd, trailing.string(), trailing.size()); + + if (!patchPanelDump.isEmpty()) { + write(fd, patchPanelDump.string(), patchPanelDump.size()); } } diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 5d6bf00d85..269a398315 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -19,9 +19,31 @@ #error This header file should only be included from AudioFlinger.h #endif + // PatchPanel is concealed within AudioFlinger, their lifetimes are the same. class PatchPanel { public: + class SoftwarePatch { + public: + SoftwarePatch(const PatchPanel &patchPanel, audio_patch_handle_t patchHandle, + audio_io_handle_t playbackThreadHandle, audio_io_handle_t recordThreadHandle) + : mPatchPanel(patchPanel), mPatchHandle(patchHandle), + mPlaybackThreadHandle(playbackThreadHandle), + mRecordThreadHandle(recordThreadHandle) {} + SoftwarePatch(const SoftwarePatch&) = default; + SoftwarePatch& operator=(const SoftwarePatch&) = default; + + // Must be called under AudioFlinger::mLock + status_t getLatencyMs_l(double *latencyMs) const; + audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; }; + audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; }; + private: + const PatchPanel &mPatchPanel; + const audio_patch_handle_t mPatchHandle; + const audio_io_handle_t mPlaybackThreadHandle; + const audio_io_handle_t mRecordThreadHandle; + }; + explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {} /* List connected audio ports and their attributes */ @@ -42,7 +64,16 @@ public: status_t listAudioPatches(unsigned int *num_patches, struct audio_patch *patches); - void dump(int fd); + // Retrieves all currently estrablished software patches for a stream + // opened on an intermediate module. + status_t getDownstreamSoftwarePatches(audio_io_handle_t stream, + std::vector *patches) const; + + // Notifies patch panel about all opened and closed streams. + void notifyStreamOpened(AudioHwDevice *audioHwDevice, audio_io_handle_t stream); + void notifyStreamClosed(audio_io_handle_t stream); + + void dump(int fd) const; private: template @@ -65,6 +96,7 @@ private: audio_patch_handle_t handle() const { return mHandle; } sp thread() { return mThread; } sp track() { return mTrack; } + sp const_thread() const { return mThread; } sp const_track() const { return mTrack; } void closeConnections(PatchPanel *panel) { @@ -122,7 +154,7 @@ private: // returns the latency of the patch (from record to playback). status_t getLatencyMs(double *latencyMs) const; - String8 dump(audio_patch_handle_t myHandle); + String8 dump(audio_patch_handle_t myHandle) const; // Note that audio_patch::id is only unique within a HAL module struct audio_patch mAudioPatch; @@ -138,8 +170,38 @@ private: Endpoint mRecord; }; + AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module); sp findHwDeviceByModule(audio_module_handle_t module); + void addSoftwarePatchToInsertedModules( + audio_module_handle_t module, audio_patch_handle_t handle); + void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle); AudioFlinger &mAudioFlinger; std::map mPatches; + + // This map allows going from a thread to "downstream" software patches + // when a processing module inserted in between. Example: + // + // from map value.streams map key + // [Mixer thread] --> [Virtual output device] --> [Processing module] ---\ + // [Harware module] <-- [Physical output device] <-- [S/W Patch] <--/ + // from map value.sw_patches + // + // This allows the mixer thread to look up the threads of the software patch + // for propagating timing info, parameters, etc. + // + // The current assumptions are: + // 1) The processing module acts as a mixer with several outputs which + // represent differently downmixed and / or encoded versions of the same + // mixed stream. There is no 1:1 correspondence between the input streams + // and the software patches, but rather a N:N correspondence between + // a group of streams and a group of patches. + // 2) There are only a couple of inserted processing modules in the system, + // so when looking for a stream or patch handle we can iterate over + // all modules. + struct ModuleConnections { + std::set streams; + std::set sw_patches; + }; + std::map mInsertedModules; }; -- GitLab From ddd87af881ef31976af3d051503fcb00040f4f9a Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Wed, 11 Jul 2018 09:16:23 -0700 Subject: [PATCH 0126/1530] Add syscalls required by UBSan. UBSan diagnostic mode uses the open and readlink syscalls in the execution path that writes diagnostics messages to logcat. Bug: 110791537 Test: media.codec no longer crashing when logging UBSan messages Change-Id: Ia33bb59c21c65b1019d0d7fc9eaff7b35484fc63 --- services/mediacodec/seccomp_policy/mediacodec-arm.policy | 4 ++++ services/mediacodec/seccomp_policy/mediacodec-x86.policy | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy index 6ec8895787..edf4dabc4d 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy @@ -55,4 +55,8 @@ ugetrlimit: 1 getdents64: 1 getrandom: 1 +# Used by UBSan diagnostic messages +readlink: 1 +open: 1 + @include /system/etc/seccomp_policy/crash_dump.arm.policy diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index bbbe552342..6e6b276196 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -55,4 +55,8 @@ sched_yield: 1 getpid: 1 gettid: 1 +# Used by UBSan diagnostic messages +readlink: 1 +open: 1 + @include /system/etc/seccomp_policy/crash_dump.x86.policy -- GitLab From 4a6dea91426d9a5d8c3be8acd8d6d450a8478d6f Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 13 Jul 2018 10:32:44 -0700 Subject: [PATCH 0127/1530] Support verbose mode for regular decoding Change-Id: I90ed292e7879a78491e2f62b7ee92906496e8fc7 --- cmds/stagefright/stagefright.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 97e160e1be..d41d3fd369 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -354,7 +354,10 @@ static void playSource(sp &source) { decodeTimesUs.push(delayDecodeUs); } - if (showProgress && (n++ % 16) == 0) { + if (gVerbose) { + MetaDataBase &meta = buffer->meta_data(); + fprintf(stdout, "%ld sample format: %s\n", numFrames, meta.toString().c_str()); + } else if (showProgress && (n++ % 16) == 0) { printf("."); fflush(stdout); } -- GitLab From b2451bb3f868e7224b8c477fa077fbd057c3389b Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 12 Jul 2018 12:19:12 -0700 Subject: [PATCH 0128/1530] MediaCas: re-send the first scrambled TS packet to decoder Currently, for scrambled streams that require secure decoder, we descramble the first TS packet (which includes the PES header) into the clear, and this TS packet is not descrambled again at decoding time. However, descramblers may not be able to descramble the bitstream into clear buffers past the PES header portion. So we need to assume that in the first pass, we can only get up to the PES header portion, and at decoding time, this TS packet should be descrambled again fully. bug: 111271614 Test: CTS MediaCasTest; MediaDrmClearKeyTest; tested with clearkey cas plugin modified to return only the PES header portion of the first TS packet, instead of the full TS. Change-Id: I8e77df3c5fa1268282442b7556851d89918c244d --- media/libstagefright/ACodecBufferChannel.cpp | 17 +++++++-- media/libstagefright/mpeg2ts/ATSParser.cpp | 36 +++++++++++++------ .../mpeg2ts/AnotherPacketSource.cpp | 8 +++++ media/libstagefright/mpeg2ts/ESQueue.cpp | 21 ++--------- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp index 710ae68e09..266a240c71 100644 --- a/media/libstagefright/ACodecBufferChannel.cpp +++ b/media/libstagefright/ACodecBufferChannel.cpp @@ -129,6 +129,7 @@ status_t ACodecBufferChannel::queueSecureInputBuffer( secureHandle = static_cast(secureData->getDestinationPointer()); } ssize_t result = -1; + ssize_t codecDataOffset = 0; if (mCrypto != NULL) { ICrypto::DestinationBuffer destination; if (secure) { @@ -180,9 +181,16 @@ status_t ACodecBufferChannel::queueSecureInputBuffer( Status status = Status::OK; hidl_string detailedError; + ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED; + + if (key != NULL) { + sctrl = (ScramblingControl)key[0]; + // Adjust for the PES offset + codecDataOffset = key[2] | (key[3] << 8); + } auto returnVoid = mDescrambler->descramble( - key != NULL ? (ScramblingControl)key[0] : ScramblingControl::UNSCRAMBLED, + sctrl, hidlSubSamples, srcBuffer, 0, @@ -202,6 +210,11 @@ status_t ACodecBufferChannel::queueSecureInputBuffer( return UNKNOWN_ERROR; } + if (result < codecDataOffset) { + ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result); + return BAD_VALUE; + } + ALOGV("descramble succeeded, %zd bytes", result); if (dstBuffer.type == BufferType::SHARED_MEMORY) { @@ -210,7 +223,7 @@ status_t ACodecBufferChannel::queueSecureInputBuffer( } } - it->mCodecBuffer->setRange(0, result); + it->mCodecBuffer->setRange(codecDataOffset, result - codecDataOffset); // Copy metadata from client to codec buffer. it->mCodecBuffer->meta()->clear(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 271d601ccd..cc318153de 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -1437,7 +1437,7 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { // Perform the 1st pass descrambling if needed if (descrambleBytes > 0) { memcpy(mDescrambledBuffer->data(), mBuffer->data(), descrambleBytes); - mDescrambledBuffer->setRange(0, descrambleBytes); + mDescrambledBuffer->setRange(0, mBuffer->size()); hidl_vec subSamples; subSamples.resize(descrambleSubSamples); @@ -1454,10 +1454,9 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { } } - uint64_t srcOffset = 0, dstOffset = 0; - // If scrambled at PES-level, PES header should be skipped + // If scrambled at PES-level, PES header is in the clear if (pesScramblingControl != 0) { - srcOffset = dstOffset = pesOffset; + subSamples[0].numBytesOfClearData = pesOffset; subSamples[0].numBytesOfEncryptedData -= pesOffset; } @@ -1473,9 +1472,9 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { (ScramblingControl) sctrl, subSamples, mDescramblerSrcBuffer, - srcOffset, + 0 /*srcOffset*/, dstBuffer, - dstOffset, + 0 /*dstOffset*/, [&status, &bytesWritten, &detailedError] ( Status _status, uint32_t _bytesWritten, const hidl_string& _detailedError) { @@ -1492,9 +1491,15 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { ALOGV("[stream %d] descramble succeeded, %d bytes", mElementaryPID, bytesWritten); - memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes); + + // Set descrambleBytes to the returned result. + // Note that this might be smaller than the total length of input data. + // (eg. when we're descrambling the PES header portion of a secure stream, + // the plugin might cut it off right after the PES header.) + descrambleBytes = bytesWritten; } + sp buffer; if (mQueue->isScrambled()) { // Queue subSample info for scrambled queue sp clearSizesBuffer = new ABuffer(mSubSamples.size() * 4); @@ -1506,8 +1511,7 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { for (auto it = mSubSamples.begin(); it != mSubSamples.end(); it++, i++) { if ((it->transport_scrambling_mode == 0 - && pesScramblingControl == 0) - || i < descrambleSubSamples) { + && pesScramblingControl == 0)) { clearSizePtr[i] = it->subSampleSize; encSizePtr[i] = 0; } else { @@ -1516,14 +1520,26 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { } isSync |= it->random_access_indicator; } + + // If scrambled at PES-level, PES header is in the clear + if (pesScramblingControl != 0) { + clearSizePtr[0] = pesOffset; + encSizePtr[0] -= pesOffset; + } // Pass the original TS subsample size now. The PES header adjust // will be applied when the scrambled AU is dequeued. mQueue->appendScrambledData( mBuffer->data(), mBuffer->size(), sctrl, isSync, clearSizesBuffer, encSizesBuffer); + + buffer = mDescrambledBuffer; + } else { + memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes); + + buffer = mBuffer; } - ABitReader br(mBuffer->data(), mBuffer->size()); + ABitReader br(buffer->data(), buffer->size()); status_t err = parsePES(&br, event); if (err != OK) { diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index ece06924c3..9e154a3d2c 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -226,6 +226,7 @@ status_t AnotherPacketSource::read( int32_t cryptoMode; if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) { int32_t cryptoKey; + int32_t pesOffset; sp clearBytesBuffer, encBytesBuffer; CHECK(buffer->meta()->findInt32("cryptoKey", &cryptoKey)); @@ -233,6 +234,8 @@ status_t AnotherPacketSource::read( && clearBytesBuffer != NULL); CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer) && encBytesBuffer != NULL); + CHECK(buffer->meta()->findInt32("pesOffset", &pesOffset) + && (pesOffset >= 0) && (pesOffset < 65536)); bufmeta.setInt32(kKeyCryptoMode, cryptoMode); @@ -240,6 +243,11 @@ status_t AnotherPacketSource::read( bufmeta.setData(kKeyCryptoIV, 0, array, 16); array[0] = (uint8_t) (cryptoKey & 0xff); + // array[1] contains PES header flag, which we don't use. + // array[2~3] contain the PES offset. + array[2] = (uint8_t) (pesOffset & 0xff); + array[3] = (uint8_t) ((pesOffset >> 8) & 0xff); + bufmeta.setData(kKeyCryptoKey, 0, array, 16); bufmeta.setData(kKeyPlainSizes, 0, diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 3deee7e9ab..34d0bcc249 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -691,25 +691,9 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { return NULL; } - // skip the PES header, and copy the rest into scrambled access unit + // copy into scrambled access unit sp scrambledAccessUnit = ABuffer::CreateAsCopy( - mScrambledBuffer->data() + pesOffset, - scrambledLength - pesOffset); - - // fix up first sample size after skipping the PES header - if (pesOffset > 0) { - int32_t &firstClearSize = *(int32_t*)clearSizes->data(); - int32_t &firstEncSize = *(int32_t*)encSizes->data(); - // Cut away the PES header - if (firstClearSize >= pesOffset) { - // This is for TS-level scrambling, we descrambled the first - // (or it was clear to begin with) - firstClearSize -= pesOffset; - } else if (firstEncSize >= pesOffset) { - // This can only be PES-level scrambling - firstEncSize -= pesOffset; - } - } + mScrambledBuffer->data(), scrambledLength); scrambledAccessUnit->meta()->setInt64("timeUs", timeUs); if (isSync) { @@ -723,6 +707,7 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { scrambledAccessUnit->meta()->setInt32("cryptoKey", keyId); scrambledAccessUnit->meta()->setBuffer("clearBytes", clearSizes); scrambledAccessUnit->meta()->setBuffer("encBytes", encSizes); + scrambledAccessUnit->meta()->setInt32("pesOffset", pesOffset); memmove(mScrambledBuffer->data(), mScrambledBuffer->data() + scrambledLength, -- GitLab From d07b11eb1e7ef1c3c6b4bff0e9dd5380f3aefd79 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Fri, 1 Jun 2018 12:50:02 -0700 Subject: [PATCH 0129/1530] Camera: apply nullptr settings in HFR batch Test: high speed recording on Pixel2 Bug: 74342751 Change-Id: Ic4315d4584824ad65584ab4104350dd675128337 --- .../camera/libcameraservice/device3/Camera3Device.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 0db5642030..eb66493404 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4739,6 +4739,7 @@ bool Camera3Device::RequestThread::threadLoop() { status_t Camera3Device::RequestThread::prepareHalRequests() { ATRACE_CALL(); + bool batchedRequest = mNextRequests[0].captureRequest->mBatchSize > 1; for (size_t i = 0; i < mNextRequests.size(); i++) { auto& nextRequest = mNextRequests.editItemAt(i); sp captureRequest = nextRequest.captureRequest; @@ -4762,7 +4763,10 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { mPrevTriggers = triggerCount; // If the request is the same as last, or we had triggers last time - bool newRequest = mPrevRequest != captureRequest || triggersMixedIn; + bool newRequest = (mPrevRequest != captureRequest || triggersMixedIn) && + // Request settings are all the same within one batch, so only treat the first + // request in a batch as new + !(batchedRequest && i >= 0); if (newRequest) { /** * HAL workaround: @@ -4911,7 +4915,7 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { // preview), and the current request is not the last one in the batch, // do not send callback to the app. bool hasCallback = true; - if (mNextRequests[0].captureRequest->mBatchSize > 1 && i != mNextRequests.size()-1) { + if (batchedRequest && i != mNextRequests.size()-1) { hasCallback = false; } res = parent->registerInFlight(halRequest->frame_number, -- GitLab From 3034eec363dd7be94b3ad05a947d1c7a7011dfaf Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 16 Jul 2018 13:50:50 -0700 Subject: [PATCH 0130/1530] ACodec: handle state machine being stuck Bug: 110975323 Test: atest CtsMediaTestCases:EncodeDecodeTest \ CtsMediaTestCases:EncodeVirtualDisplayTest \ CtsMediaTestCases:EncodeVirtualDisplayWithCompositionTest \ CtsMediaTestCases:MediaCasTest \ CtsMediaTestCases:MediaCodecListTest \ CtsMediaTestCases:MediaCodecTest \ CtsMediaTestCases:MediaMetadataRetrieverTest \ CtsMediaTestCases:MediaPlayerDrmTest \ CtsMediaTestCases:MediaPlayerSurfaceTest \ CtsMediaTestCases:MediaScannerTest \ CtsMediaTestCases:ResourceManagerTest \ CtsMediaTestCases:MediaRecorderTest Change-Id: Iaf0a13b06fe13845a99ea19e09065b77a93bd751 --- media/libstagefright/ACodec.cpp | 40 +++++++++++++++++++ .../include/media/stagefright/ACodec.h | 1 + 2 files changed, 41 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 7f39d109f2..3526047800 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -5556,6 +5556,11 @@ bool ACodec::BaseState::onMessageReceived(const sp &msg) { break; } + case kWhatCheckIfStuck: { + ALOGV("No-op by default"); + break; + } + default: return false; } @@ -7873,6 +7878,18 @@ bool ACodec::OutputPortSettingsChangedState::onMessageReceived( break; } + case kWhatCheckIfStuck: + { + int32_t generation = 0; + CHECK(msg->findInt32("generation", &generation)); + if (generation == mCodec->mStateGeneration) { + mCodec->signalError(OMX_ErrorUndefined, TIMED_OUT); + } + + handled = true; + break; + } + default: handled = BaseState::onMessageReceived(msg); break; @@ -7884,6 +7901,11 @@ bool ACodec::OutputPortSettingsChangedState::onMessageReceived( void ACodec::OutputPortSettingsChangedState::stateEntered() { ALOGV("[%s] Now handling output port settings change", mCodec->mComponentName.c_str()); + + // If we haven't transitioned after 3 seconds, we're probably stuck. + sp msg = new AMessage(ACodec::kWhatCheckIfStuck, mCodec); + msg->setInt32("generation", mCodec->mStateGeneration); + msg->post(3000000); } bool ACodec::OutputPortSettingsChangedState::onOMXFrameRendered( @@ -8146,6 +8168,11 @@ void ACodec::FlushingState::stateEntered() { ALOGV("[%s] Now Flushing", mCodec->mComponentName.c_str()); mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false; + + // If we haven't transitioned after 3 seconds, we're probably stuck. + sp msg = new AMessage(ACodec::kWhatCheckIfStuck, mCodec); + msg->setInt32("generation", mCodec->mStateGeneration); + msg->post(3000000); } bool ACodec::FlushingState::onMessageReceived(const sp &msg) { @@ -8160,6 +8187,7 @@ bool ACodec::FlushingState::onMessageReceived(const sp &msg) { msg->setInt32("generation", mCodec->mStateGeneration); msg->post(3000000); } + handled = true; break; } @@ -8180,6 +8208,18 @@ bool ACodec::FlushingState::onMessageReceived(const sp &msg) { break; } + case kWhatCheckIfStuck: + { + int32_t generation = 0; + CHECK(msg->findInt32("generation", &generation)); + if (generation == mCodec->mStateGeneration) { + mCodec->signalError(OMX_ErrorUndefined, TIMED_OUT); + } + + handled = true; + break; + } + default: handled = BaseState::onMessageReceived(msg); break; diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 97d15a71fe..1137cf180f 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -137,6 +137,7 @@ private: kWhatOMXDied = 'OMXd', kWhatReleaseCodecInstance = 'relC', kWhatForceStateTransition = 'fstt', + kWhatCheckIfStuck = 'Cstk', }; enum { -- GitLab From 67bae2cd210377e140fac0dc68ebe3e207c34143 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 16 Jul 2018 15:44:35 -0700 Subject: [PATCH 0131/1530] audioflinger: Honor config flags when opening s/w patch output If the patch sink configuration specifies sampling rate, format, and channel mask, use them when opening the output side of a software patch. Bug: 63901775 Test: MSD prototype; Telecom calls via USB headset on Pixel. Change-Id: I705d46524cfcc59c88c57b18a45bee0c6b922b62 --- services/audioflinger/PatchPanel.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 7f46747d9c..15a60c2df0 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -235,9 +235,19 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa audio_devices_t device = patch->sinks[0].ext.device.type; String8 address = String8(patch->sinks[0].ext.device.address); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; - audio_output_flags_t flags = - patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? - patch->sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE; + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; + if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + config.sample_rate = patch->sinks[0].sample_rate; + } + if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + config.channel_mask = patch->sinks[0].channel_mask; + } + if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) { + config.format = patch->sinks[0].format; + } + if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS) { + flags = patch->sinks[0].flags.output; + } sp thread = mAudioFlinger.openOutput_l( patch->sinks[0].ext.device.hw_module, &output, -- GitLab From ce9f265fc4fe60ee24ae334b2e8123ffc2be0bea Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 16 Jul 2018 11:09:24 -0700 Subject: [PATCH 0132/1530] audioflinger: Allow encoded PCM for RecordThread Change the fatal condition in AudioFlinger::RecordThread to info. Require AUDIO_INPUT_FLAG_DIRECT when creating tracks on such streams from the client side. Note that PatchPanel uses PatchRecord constructor directly. Bug: 63901775 Test: on MSD prototype Change-Id: Ic94e51e6b8a089f3100131851db8c3ce0994b4ac --- services/audioflinger/Threads.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f68bfeea4b..5fc422e5ac 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7065,6 +7065,12 @@ sp AudioFlinger::RecordThread::createRe goto Exit; } + if (!audio_is_linear_pcm(mFormat) && (*flags & AUDIO_INPUT_FLAG_DIRECT) == 0) { + ALOGE("createRecordTrack_l() on an encoded stream requires AUDIO_INPUT_FLAG_DIRECT"); + lStatus = BAD_VALUE; + goto Exit; + } + if (*pSampleRate == 0) { *pSampleRate = mSampleRate; } @@ -7779,10 +7785,15 @@ void AudioFlinger::RecordThread::readInputParameters_l() { status_t result = mInput->stream->getAudioProperties(&mSampleRate, &mChannelMask, &mHALFormat); LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving audio properties from HAL: %d", result); - mChannelCount = audio_channel_count_from_in_mask(mChannelMask); - LOG_ALWAYS_FATAL_IF(mChannelCount > FCC_8, "HAL channel count %d > %d", mChannelCount, FCC_8); mFormat = mHALFormat; - LOG_ALWAYS_FATAL_IF(!audio_is_linear_pcm(mFormat), "HAL format %#x is not linear pcm", mFormat); + mChannelCount = audio_channel_count_from_in_mask(mChannelMask); + if (audio_is_linear_pcm(mFormat)) { + LOG_ALWAYS_FATAL_IF(mChannelCount > FCC_8, "HAL channel count %d > %d", + mChannelCount, FCC_8); + } else { + // Can have more that FCC_8 channels in encoded streams. + ALOGI("HAL format %#x is not linear pcm", mFormat); + } result = mInput->stream->getFrameSize(&mFrameSize); LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result); result = mInput->stream->getBufferSize(&mBufferSize); -- GitLab From d7fe086d3b45f088083170006367dfc4246f70b0 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Sat, 14 Jul 2018 16:48:01 -0700 Subject: [PATCH 0133/1530] audio policy: refactor audio playback APIs Refactor audio policy service APIs controlling audio playback (startOutput, stopOutput, releaseOutput) To allow finer grain control per AudioTrack client. Test: AudioTrack CTS test. manual test of playback use cases. Change-Id: I49a681f3c2a8211e524824993049b24d8086376d --- media/libaudioclient/AudioSystem.cpp | 18 +-- media/libaudioclient/IAudioPolicyService.cpp | 48 ++----- .../include/media/AudioSystem.h | 12 +- .../include/media/IAudioPolicyService.h | 12 +- services/audioflinger/AudioFlinger.cpp | 4 +- services/audioflinger/Threads.cpp | 22 ++-- services/audioflinger/Tracks.cpp | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 121 +++++++++++------- .../service/AudioPolicyService.cpp | 32 ++--- .../audiopolicy/service/AudioPolicyService.h | 93 ++++++++------ 10 files changed, 175 insertions(+), 189 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index c072901a40..cb4bcfc750 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -878,31 +878,25 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, flags, selectedDeviceId, portId); } -status_t AudioSystem::startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioSystem::startOutput(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->startOutput(output, stream, session); + return aps->startOutput(portId); } -status_t AudioSystem::stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioSystem::stopOutput(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->stopOutput(output, stream, session); + return aps->stopOutput(portId); } -void AudioSystem::releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +void AudioSystem::releaseOutput(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return; - aps->releaseOutput(output, stream, session); + aps->releaseOutput(portId); } status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 316105c56f..9d77376d89 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -244,41 +244,29 @@ public: return status; } - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) + virtual status_t startOutput(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeInt32(output); - data.writeInt32((int32_t) stream); - data.writeInt32((int32_t) session); + data.writeInt32((int32_t)portId); remote()->transact(START_OUTPUT, data, &reply); return static_cast (reply.readInt32()); } - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) + virtual status_t stopOutput(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeInt32(output); - data.writeInt32((int32_t) stream); - data.writeInt32((int32_t) session); + data.writeInt32((int32_t)portId); remote()->transact(STOP_OUTPUT, data, &reply); return static_cast (reply.readInt32()); } - virtual void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) + virtual void releaseOutput(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeInt32(output); - data.writeInt32((int32_t)stream); - data.writeInt32((int32_t)session); + data.writeInt32((int32_t)portId); remote()->transact(RELEASE_OUTPUT, data, &reply); } @@ -1074,34 +1062,22 @@ status_t BnAudioPolicyService::onTransact( case START_OUTPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_io_handle_t output = static_cast (data.readInt32()); - audio_stream_type_t stream = - static_cast (data.readInt32()); - audio_session_t session = (audio_session_t)data.readInt32(); - reply->writeInt32(static_cast (startOutput(output, - stream, - session))); + const audio_port_handle_t portId = static_cast (data.readInt32()); + reply->writeInt32(static_cast (startOutput(portId))); return NO_ERROR; } break; case STOP_OUTPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_io_handle_t output = static_cast (data.readInt32()); - audio_stream_type_t stream = - static_cast (data.readInt32()); - audio_session_t session = (audio_session_t)data.readInt32(); - reply->writeInt32(static_cast (stopOutput(output, - stream, - session))); + const audio_port_handle_t portId = static_cast (data.readInt32()); + reply->writeInt32(static_cast (stopOutput(portId))); return NO_ERROR; } break; case RELEASE_OUTPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_io_handle_t output = static_cast (data.readInt32()); - audio_stream_type_t stream = (audio_stream_type_t)data.readInt32(); - audio_session_t session = (audio_session_t)data.readInt32(); - releaseOutput(output, stream, session); + const audio_port_handle_t portId = static_cast (data.readInt32()); + releaseOutput(portId); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 4c0f796f5e..10d6e92f51 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -224,15 +224,9 @@ public: audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - static status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - static status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - static void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); + static status_t startOutput(audio_port_handle_t portId); + static status_t stopOutput(audio_port_handle_t portId); + static void releaseOutput(audio_port_handle_t portId); // Client must successfully hand off the handle reference to AudioFlinger via createRecord(), // or release it with releaseInput(). diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index c3876afc40..6c017a36d2 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -66,15 +66,9 @@ public: audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId) = 0; - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; - virtual void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; + virtual status_t startOutput(audio_port_handle_t portId) = 0; + virtual status_t stopOutput(audio_port_handle_t portId) = 0; + virtual void releaseOutput(audio_port_handle_t portId) = 0; virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 53a4ce9e0c..0687a7cba0 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -306,7 +306,7 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di *sessionId = actualSessionId; } else { if (direction == MmapStreamInterface::DIRECTION_OUTPUT) { - AudioSystem::releaseOutput(io, streamType, actualSessionId); + AudioSystem::releaseOutput(portId); } else { AudioSystem::releaseInput(portId); } @@ -777,7 +777,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, Exit: if (lStatus != NO_ERROR && output.outputId != AUDIO_IO_HANDLE_NONE) { - AudioSystem::releaseOutput(output.outputId, streamType, sessionId); + AudioSystem::releaseOutput(portId); } *status = lStatus; return trackHandle; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f68bfeea4b..ca5ae14c2b 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2324,15 +2324,13 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) if (track->isExternalTrack()) { TrackBase::track_state state = track->mState; mLock.unlock(); - status = AudioSystem::startOutput(mId, track->streamType(), - track->sessionId()); + status = AudioSystem::startOutput(track->portId()); mLock.lock(); // abort track was stopped/paused while we released the lock if (state != track->mState) { if (status == NO_ERROR) { mLock.unlock(); - AudioSystem::stopOutput(mId, track->streamType(), - track->sessionId()); + AudioSystem::stopOutput(track->portId()); mLock.lock(); } return INVALID_OPERATION; @@ -2812,15 +2810,13 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks( for (size_t i = 0 ; i < count ; i++) { const sp& track = tracksToRemove.itemAt(i); if (track->isExternalTrack()) { - AudioSystem::stopOutput(mId, track->streamType(), - track->sessionId()); + AudioSystem::stopOutput(track->portId()); #ifdef ADD_BATTERY_DATA // to track the speaker usage addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop); #endif if (track->isTerminated()) { - AudioSystem::releaseOutput(mId, track->streamType(), - track->sessionId()); + AudioSystem::releaseOutput(track->portId()); } } } @@ -8100,7 +8096,7 @@ void AudioFlinger::MmapThread::disconnect() } // This will decrement references and may cause the destruction of this thread. if (isOutput()) { - AudioSystem::releaseOutput(mId, streamType(), mSessionId); + AudioSystem::releaseOutput(mPortId); } else { AudioSystem::releaseInput(mPortId); } @@ -8214,7 +8210,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, bool silenced = false; if (isOutput()) { - ret = AudioSystem::startOutput(mId, streamType(), mSessionId); + ret = AudioSystem::startOutput(portId); } else { ret = AudioSystem::startInput(portId, &silenced); } @@ -8226,7 +8222,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, if (mActiveTracks.size() != 0) { mLock.unlock(); if (isOutput()) { - AudioSystem::releaseOutput(mId, streamType(), mSessionId); + AudioSystem::releaseOutput(portId); } else { AudioSystem::releaseInput(portId); } @@ -8298,8 +8294,8 @@ status_t AudioFlinger::MmapThread::stop(audio_port_handle_t handle) mLock.unlock(); if (isOutput()) { - AudioSystem::stopOutput(mId, streamType(), track->sessionId()); - AudioSystem::releaseOutput(mId, streamType(), track->sessionId()); + AudioSystem::stopOutput(track->portId()); + AudioSystem::releaseOutput(track->portId()); } else { AudioSystem::stopInput(track->portId()); AudioSystem::releaseInput(track->portId()); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 22e610e963..8b9485f677 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -486,7 +486,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() wasActive = playbackThread->destroyTrack_l(this); } if (isExternalTrack() && !wasActive) { - AudioSystem::releaseOutput(mThreadIoHandle, mStreamType, mSessionId); + AudioSystem::releaseOutput(mPortId); } } } diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index d2bc40d9e9..859072b712 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -17,12 +17,12 @@ #define LOG_TAG "AudioPolicyIntefaceImpl" //#define LOG_NDEBUG 0 -#include -#include - #include "AudioPolicyService.h" -#include #include "TypeConverter.h" +#include +#include +#include +#include namespace android { @@ -208,93 +208,128 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, config, &flags, selectedDeviceId, portId); } + + if (result == NO_ERROR) { + sp client = + new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream); + mAudioPlaybackClients.add(*portId, client); + } return result; } -status_t AudioPolicyService::startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioPolicyService::startOutput(audio_port_handle_t portId) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } if (mAudioPolicyManager == NULL) { return NO_INIT; } ALOGV("startOutput()"); + sp client; spaudioPolicyEffects; { Mutex::Autolock _l(mLock); + const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); + if (index < 0) { + ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); + return INVALID_OPERATION; + } + client = mAudioPlaybackClients.valueAt(index); audioPolicyEffects = mAudioPolicyEffects; } if (audioPolicyEffects != 0) { // create audio processors according to stream - status_t status = audioPolicyEffects->addOutputSessionEffects(output, stream, session); + status_t status = audioPolicyEffects->addOutputSessionEffects( + client->io, client->stream, client->session); if (status != NO_ERROR && status != ALREADY_EXISTS) { - ALOGW("Failed to add effects on session %d", session); + ALOGW("Failed to add effects on session %d", client->session); } } Mutex::Autolock _l(mLock); AutoCallerClear acc; - return mAudioPolicyManager->startOutput(output, stream, session); + status_t status = mAudioPolicyManager->startOutput( + client->io, client->stream, client->session); + if (status == NO_ERROR) { + client->active = true; + } + return status; } -status_t AudioPolicyService::stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioPolicyService::stopOutput(audio_port_handle_t portId) { - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; + { + Mutex::Autolock _l(mLock); + + const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); + if (index < 0) { + ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); + return INVALID_OPERATION; + } } if (mAudioPolicyManager == NULL) { return NO_INIT; } ALOGV("stopOutput()"); - mOutputCommandThread->stopOutputCommand(output, stream, session); + mOutputCommandThread->stopOutputCommand(portId); return NO_ERROR; } -status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioPolicyService::doStopOutput(audio_port_handle_t portId) { - ALOGV("doStopOutput from tid %d", gettid()); + ALOGV("doStopOutput"); + sp client; spaudioPolicyEffects; { Mutex::Autolock _l(mLock); + + const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); + if (index < 0) { + ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); + return INVALID_OPERATION; + } + client = mAudioPlaybackClients.valueAt(index); audioPolicyEffects = mAudioPolicyEffects; } if (audioPolicyEffects != 0) { // release audio processors from the stream - status_t status = audioPolicyEffects->releaseOutputSessionEffects(output, stream, session); + status_t status = audioPolicyEffects->releaseOutputSessionEffects( + client->io, client->stream, client->session); if (status != NO_ERROR && status != ALREADY_EXISTS) { - ALOGW("Failed to release effects on session %d", session); + ALOGW("Failed to release effects on session %d", client->session); } } Mutex::Autolock _l(mLock); AutoCallerClear acc; - return mAudioPolicyManager->stopOutput(output, stream, session); + status_t status = mAudioPolicyManager->stopOutput( + client->io, client->stream, client->session); + if (status == NO_ERROR) { + client->active = false; + } + return status; } -void AudioPolicyService::releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +void AudioPolicyService::releaseOutput(audio_port_handle_t portId) { if (mAudioPolicyManager == NULL) { return; } ALOGV("releaseOutput()"); - mOutputCommandThread->releaseOutputCommand(output, stream, session); + mOutputCommandThread->releaseOutputCommand(portId); } -void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId) { ALOGV("doReleaseOutput from tid %d", gettid()); Mutex::Autolock _l(mLock); + const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); + if (index < 0) { + ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); + return; + } + sp client = mAudioPlaybackClients.valueAt(index); + mAudioRecordClients.removeItem(portId); + // called from internal thread: no need to clear caller identity - mAudioPolicyManager->releaseOutput(output, stream, session); + mAudioPolicyManager->releaseOutput( + client->io, client->stream, client->session); } status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, @@ -403,12 +438,8 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, return status; } - sp client = - new AudioRecordClient(*attr, *input, uid, pid, opPackageName, session); - client->active = false; - client->isConcurrent = false; - client->isVirtualDevice = false; //TODO : update from APM->getInputForAttr() - client->deviceId = *selectedDeviceId; + sp client = new AudioRecordClient(*attr, *input, uid, pid, session, + *selectedDeviceId, opPackageName); mAudioRecordClients.add(*portId, client); } @@ -497,7 +528,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc { AutoCallerClear acc; status = mAudioPolicyManager->startInput( - client->input, client->session, *silenced, &concurrency); + client->io, client->session, *silenced, &concurrency); } @@ -610,7 +641,7 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) // finish the recording app op finishRecording(client->opPackageName, client->uid); AutoCallerClear acc; - return mAudioPolicyManager->stopInput(client->input, client->session); + return mAudioPolicyManager->stopInput(client->io, client->session); } void AudioPolicyService::releaseInput(audio_port_handle_t portId) @@ -635,15 +666,15 @@ void AudioPolicyService::releaseInput(audio_port_handle_t portId) } if (audioPolicyEffects != 0) { // release audio processors from the input - status_t status = audioPolicyEffects->releaseInputEffects(client->input, client->session); + status_t status = audioPolicyEffects->releaseInputEffects(client->io, client->session); if(status != NO_ERROR) { - ALOGW("Failed to release effects on input %d", client->input); + ALOGW("Failed to release effects on input %d", client->io); } } { Mutex::Autolock _l(mLock); AutoCallerClear acc; - mAudioPolicyManager->releaseInput(client->input, client->session); + mAudioPolicyManager->releaseInput(client->io, client->session); } } diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index ca3b6b66f3..8bca22182a 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -695,26 +695,26 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() }break; case STOP_OUTPUT: { StopOutputData *data = (StopOutputData *)command->mParam.get(); - ALOGV("AudioCommandThread() processing stop output %d", - data->mIO); + ALOGV("AudioCommandThread() processing stop output portId %d", + data->mPortId); svc = mService.promote(); if (svc == 0) { break; } mLock.unlock(); - svc->doStopOutput(data->mIO, data->mStream, data->mSession); + svc->doStopOutput(data->mPortId); mLock.lock(); }break; case RELEASE_OUTPUT: { ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get(); - ALOGV("AudioCommandThread() processing release output %d", - data->mIO); + ALOGV("AudioCommandThread() processing release output portId %d", + data->mPortId); svc = mService.promote(); if (svc == 0) { break; } mLock.unlock(); - svc->doReleaseOutput(data->mIO, data->mStream, data->mSession); + svc->doReleaseOutput(data->mPortId); mLock.lock(); }break; case CREATE_AUDIO_PATCH: { @@ -925,33 +925,25 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume return sendCommand(command, delayMs); } -void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_port_handle_t portId) { sp command = new AudioCommand(); command->mCommand = STOP_OUTPUT; sp data = new StopOutputData(); - data->mIO = output; - data->mStream = stream; - data->mSession = session; + data->mPortId = portId; command->mParam = data; - ALOGV("AudioCommandThread() adding stop output %d", output); + ALOGV("AudioCommandThread() adding stop output portId %d", portId); sendCommand(command); } -void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_port_handle_t portId) { sp command = new AudioCommand(); command->mCommand = RELEASE_OUTPUT; sp data = new ReleaseOutputData(); - data->mIO = output; - data->mStream = stream; - data->mSession = session; + data->mPortId = portId; command->mParam = data; - ALOGV("AudioCommandThread() adding release output %d", output); + ALOGV("AudioCommandThread() adding release output portId %d", portId); sendCommand(command); } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index a1366bb2ec..d41069ef61 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -81,15 +81,9 @@ public: audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - virtual void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); + virtual status_t startOutput(audio_port_handle_t portId); + virtual status_t stopOutput(audio_port_handle_t portId); + virtual void releaseOutput(audio_port_handle_t portId); virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, @@ -205,12 +199,8 @@ public: bool reported); virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); - status_t doStopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - void doReleaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); + status_t doStopOutput(audio_port_handle_t portId); + void doReleaseOutput(audio_port_handle_t portId); status_t clientCreateAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, @@ -340,12 +330,8 @@ private: status_t parametersCommand(audio_io_handle_t ioHandle, const char *keyValuePairs, int delayMs = 0); status_t voiceVolumeCommand(float volume, int delayMs = 0); - void stopOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - void releaseOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); + void stopOutputCommand(audio_port_handle_t portId); + void releaseOutputCommand(audio_port_handle_t portId); status_t sendCommand(sp& command, int delayMs = 0); void insertCommand_l(sp& command, int delayMs = 0); status_t createAudioPatchCommand(const struct audio_patch *patch, @@ -413,16 +399,12 @@ private: class StopOutputData : public AudioCommandData { public: - audio_io_handle_t mIO; - audio_stream_type_t mStream; - audio_session_t mSession; + audio_port_handle_t mPortId; }; class ReleaseOutputData : public AudioCommandData { public: - audio_io_handle_t mIO; - audio_stream_type_t mStream; - audio_session_t mSession; + audio_port_handle_t mPortId; }; class CreateAudioPatchData : public AudioCommandData { @@ -603,30 +585,56 @@ private: bool mAudioPortCallbacksEnabled; }; + class AudioClient : public virtual RefBase { + public: + AudioClient(const audio_attributes_t attributes, + const audio_io_handle_t io, uid_t uid, pid_t pid, + const audio_session_t session, const audio_port_handle_t deviceId) : + attributes(attributes), io(io), uid(uid), pid(pid), + session(session), deviceId(deviceId), active(false) {} + ~AudioClient() override = default; + + + const audio_attributes_t attributes; // source, flags ... + const audio_io_handle_t io; // audio HAL stream IO handle + const uid_t uid; // client UID + const pid_t pid; // client PID + const audio_session_t session; // audio session ID + const audio_port_handle_t deviceId; // selected input device port ID + bool active; // Playback/Capture is active or inactive + }; + // --- AudioRecordClient --- // Information about each registered AudioRecord client // (between calls to getInputForAttr() and releaseInput()) - class AudioRecordClient : public RefBase { + class AudioRecordClient : public AudioClient { public: AudioRecordClient(const audio_attributes_t attributes, - const audio_io_handle_t input, uid_t uid, pid_t pid, - const String16& opPackageName, const audio_session_t session) : - attributes(attributes), - input(input), uid(uid), pid(pid), - opPackageName(opPackageName), session(session), - active(false), isConcurrent(false), isVirtualDevice(false) {} - virtual ~AudioRecordClient() {} + const audio_io_handle_t io, uid_t uid, pid_t pid, + const audio_session_t session, const audio_port_handle_t deviceId, + const String16& opPackageName) : + AudioClient(attributes, io, uid, pid, session, deviceId), + opPackageName(opPackageName), isConcurrent(false), isVirtualDevice(false) {} + ~AudioRecordClient() override = default; - const audio_attributes_t attributes; // source, flags ... - const audio_io_handle_t input; // audio HAL input IO handle - const uid_t uid; // client UID - const pid_t pid; // client PID const String16 opPackageName; // client package name - const audio_session_t session; // audio session ID - bool active; // Capture is active or inactive bool isConcurrent; // is allowed to concurrent capture bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr() - audio_port_handle_t deviceId; // selected input device port ID + }; + + // --- AudioPlaybackClient --- + // Information about each registered AudioTrack client + // (between calls to getOutputForAttr() and releaseOutput()) + class AudioPlaybackClient : public AudioClient { + public: + AudioPlaybackClient(const audio_attributes_t attributes, + const audio_io_handle_t io, uid_t uid, pid_t pid, + const audio_session_t session, audio_port_handle_t deviceId, + audio_stream_type_t stream) : + AudioClient(attributes, io, uid, pid, session, deviceId), stream(stream) {} + ~AudioPlaybackClient() override = default; + + const audio_stream_type_t stream; }; // A class automatically clearing and restoring binder caller identity inside @@ -670,6 +678,7 @@ private: sp mUidPolicy; DefaultKeyedVector< audio_port_handle_t, sp > mAudioRecordClients; + DefaultKeyedVector< audio_port_handle_t, sp > mAudioPlaybackClients; }; } // namespace android -- GitLab From b261ef51db4891290647ee2fe2365b0ece6a530e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 16 Jul 2018 13:34:38 -0700 Subject: [PATCH 0134/1530] audioflinger: Forward stream parameters to downstream patches When passing parameters to immediate output streams also check if there are any downstream patch threads that need to receive these parameters. Added AudioFlinger::forwardParametersToDownstreamPatches_l method, similar to existing broacastParametersToRecordThreads_l. Bug: 63901775 Test: make Change-Id: I83229fd6f4890e5e69a40d568d1c3e1eebed6504 --- services/audioflinger/AudioFlinger.cpp | 31 +++++++++++++++++++++++--- services/audioflinger/AudioFlinger.h | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index d186887fc8..0beb86cf94 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1144,6 +1144,23 @@ void AudioFlinger::broacastParametersToRecordThreads_l(const String8& keyValuePa } } +// forwardAudioHwSyncToDownstreamPatches_l() must be called with AudioFlinger::mLock held +void AudioFlinger::forwardParametersToDownstreamPatches_l( + audio_io_handle_t upStream, const String8& keyValuePairs, + std::function&)> useThread) +{ + std::vector swPatches; + if (mPatchPanel.getDownstreamSoftwarePatches(upStream, &swPatches) != OK) return; + ALOGV_IF(!swPatches.empty(), "%s found %zu downstream patches for stream ID %d", + __func__, swPatches.size(), upStream); + for (const auto& swPatch : swPatches) { + sp downStream = checkPlaybackThread_l(swPatch.getPlaybackThreadHandle()); + if (downStream != NULL && (useThread == nullptr || useThread(downStream))) { + downStream->setParameters(keyValuePairs); + } + } +} + // Filter reserved keys from setParameters() before forwarding to audio HAL or acting upon. // Some keys are used for audio routing and audio path configuration and should be reserved for use // by audio policy and audio flinger for functional, privacy and security reasons. @@ -1259,7 +1276,9 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& } } if (thread != 0) { - return thread->setParameters(filteredKeyValuePairs); + status_t result = thread->setParameters(filteredKeyValuePairs); + forwardParametersToDownstreamPatches_l(thread->id(), filteredKeyValuePairs); + return result; } return BAD_VALUE; } @@ -1964,7 +1983,10 @@ audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId if (sessions & ThreadBase::TRACK_SESSION) { AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyStreamHwAvSync), value); - thread->setParameters(param.toString()); + String8 keyValuePairs = param.toString(); + thread->setParameters(keyValuePairs); + forwardParametersToDownstreamPatches_l(thread->id(), keyValuePairs, + [](const sp& thread) { return thread->usesHwAvSync(); }); break; } } @@ -2010,7 +2032,10 @@ void AudioFlinger::setAudioHwSyncForSession_l(PlaybackThread *thread, audio_sess ALOGV("setAudioHwSyncForSession_l found ID %d for session %d", syncId, sessionId); AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyStreamHwAvSync), syncId); - thread->setParameters(param.toString()); + String8 keyValuePairs = param.toString(); + thread->setParameters(keyValuePairs); + forwardParametersToDownstreamPatches_l(thread->id(), keyValuePairs, + [](const sp& thread) { return thread->usesHwAvSync(); }); } } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 0bb492a184..4a780ce4aa 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -678,6 +678,9 @@ using effect_buffer_t = int16_t; bool updateOrphanEffectChains(const sp& effect); void broacastParametersToRecordThreads_l(const String8& keyValuePairs); + void forwardParametersToDownstreamPatches_l( + audio_io_handle_t upStream, const String8& keyValuePairs, + std::function&)> useThread = nullptr); // AudioStreamIn is immutable, so their fields are const. // For emphasis, we could also make all pointers to them be "const *", -- GitLab From 61811a640bd60321324b436069531a4409ca13ef Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Tue, 17 Jul 2018 10:12:44 +0900 Subject: [PATCH 0135/1530] mediacodec-x86.policy: Allow media.omx to call statfs64 on x86 platform After using std::thread in MojoThread, 'statfs64' needs to be called in media.omx initialization. This adds statfs64 to system calls list that media.omx process is allowed to execute. Bug: 110722333 Test: m Change-Id: I79d9223cb4189941001abdce5456c4c5e9f9ffe1 --- services/mediacodec/seccomp_policy/mediacodec-x86.policy | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index 6e6b276196..4031b11355 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -24,6 +24,7 @@ close: 1 mmap2: 1 fstat64: 1 stat64: 1 +statfs64: 1 madvise: 1 fstatat64: 1 futex: 1 -- GitLab From 54c36820a330fa3cce559d15e5fcc8dd6ff6a28c Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Wed, 18 Jul 2018 09:33:39 -0700 Subject: [PATCH 0136/1530] Camera: fix condition check typo HFR should send valid setting for the first request inside a batch. Bug: 111528642 Test: Slowmo on Pixel 3 Change-Id: Id92a9e23d12b7f9dde3acfc70b656ae39bdaee5b --- services/camera/libcameraservice/device3/Camera3Device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index eb66493404..8ee70efba5 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4766,7 +4766,7 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { bool newRequest = (mPrevRequest != captureRequest || triggersMixedIn) && // Request settings are all the same within one batch, so only treat the first // request in a batch as new - !(batchedRequest && i >= 0); + !(batchedRequest && i > 0); if (newRequest) { /** * HAL workaround: -- GitLab From 293184487fccf0f1926482ad3d9145949cdc8a3c Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 18 Jul 2018 14:17:04 -0700 Subject: [PATCH 0137/1530] libaaudio: remove unused .mk files They have been replaced with .bp files. Bug: http://b/32019064 Test: mmm frameworks/av/media/libaaudio Change-Id: I34d20c09f36f79929c0878a62673bd2a3c8ef610 --- .../examples/input_monitor/jni/Android.mk | 35 ------------------- .../examples/input_monitor/jni/Application.mk | 3 -- .../examples/loopback/jni/Android.mk | 16 --------- .../examples/loopback/jni/Application.mk | 1 - .../examples/write_sine/jni/Android.mk | 35 ------------------- .../examples/write_sine/jni/Application.mk | 1 - 6 files changed, 91 deletions(-) delete mode 100644 media/libaaudio/examples/input_monitor/jni/Android.mk delete mode 100644 media/libaaudio/examples/input_monitor/jni/Application.mk delete mode 100644 media/libaaudio/examples/loopback/jni/Android.mk delete mode 100644 media/libaaudio/examples/loopback/jni/Application.mk delete mode 100644 media/libaaudio/examples/write_sine/jni/Android.mk delete mode 100644 media/libaaudio/examples/write_sine/jni/Application.mk diff --git a/media/libaaudio/examples/input_monitor/jni/Android.mk b/media/libaaudio/examples/input_monitor/jni/Android.mk deleted file mode 100644 index a0b981c2b4..0000000000 --- a/media/libaaudio/examples/input_monitor/jni/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-utils) \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/src \ - frameworks/av/media/libaaudio/examples/utils - -# NDK recommends using this kind of relative path instead of an absolute path. -LOCAL_SRC_FILES:= ../src/input_monitor.cpp -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libaaudio -LOCAL_MODULE := input_monitor -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-utils) \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/examples/utils - -LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libaaudio -LOCAL_MODULE := input_monitor_callback -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_MODULE := libaaudio_prebuilt -LOCAL_SRC_FILES := libaaudio.so -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -include $(PREBUILT_SHARED_LIBRARY) diff --git a/media/libaaudio/examples/input_monitor/jni/Application.mk b/media/libaaudio/examples/input_monitor/jni/Application.mk deleted file mode 100644 index e74475c4d2..0000000000 --- a/media/libaaudio/examples/input_monitor/jni/Application.mk +++ /dev/null @@ -1,3 +0,0 @@ -# TODO remove then when we support other architectures -APP_ABI := arm64-v8a -APP_CPPFLAGS += -std=c++11 diff --git a/media/libaaudio/examples/loopback/jni/Android.mk b/media/libaaudio/examples/loopback/jni/Android.mk deleted file mode 100644 index aebe877006..0000000000 --- a/media/libaaudio/examples/loopback/jni/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-utils) \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/examples/utils - -# NDK recommends using this kind of relative path instead of an absolute path. -LOCAL_SRC_FILES:= ../src/loopback.cpp -LOCAL_CFLAGS := -Wall -Werror -LOCAL_STATIC_LIBRARIES := libsndfile -LOCAL_SHARED_LIBRARIES := libaaudio libaudioutils -LOCAL_MODULE := aaudio_loopback -include $(BUILD_EXECUTABLE) diff --git a/media/libaaudio/examples/loopback/jni/Application.mk b/media/libaaudio/examples/loopback/jni/Application.mk deleted file mode 100644 index ba44f3705b..0000000000 --- a/media/libaaudio/examples/loopback/jni/Application.mk +++ /dev/null @@ -1 +0,0 @@ -APP_CPPFLAGS += -std=c++11 diff --git a/media/libaaudio/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk deleted file mode 100644 index 1a1bd43c4e..0000000000 --- a/media/libaaudio/examples/write_sine/jni/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-utils) \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/src \ - frameworks/av/media/libaaudio/examples/utils - -# NDK recommends using this kind of relative path instead of an absolute path. -LOCAL_SRC_FILES:= ../src/write_sine.cpp -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libaaudio -LOCAL_MODULE := write_sine -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-utils) \ - frameworks/av/media/libaaudio/include \ - frameworks/av/media/libaaudio/examples/utils - -LOCAL_SRC_FILES:= ../src/write_sine_callback.cpp -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SHARED_LIBRARIES := libaaudio -LOCAL_MODULE := write_sine_callback -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_MODULE := libaaudio_prebuilt -LOCAL_SRC_FILES := libaaudio.so -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -include $(PREBUILT_SHARED_LIBRARY) diff --git a/media/libaaudio/examples/write_sine/jni/Application.mk b/media/libaaudio/examples/write_sine/jni/Application.mk deleted file mode 100644 index ba44f3705b..0000000000 --- a/media/libaaudio/examples/write_sine/jni/Application.mk +++ /dev/null @@ -1 +0,0 @@ -APP_CPPFLAGS += -std=c++11 -- GitLab From a82fd60004272acc0ea25914cac429035082be21 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 12 Jul 2018 17:14:27 -0700 Subject: [PATCH 0138/1530] Effect config parser: fix use after free on file path ParsingResult::configPath is the path of the configuration file used for the factory config parsing. This path is used for an error log if the configuration file has errors. The paths used to be a static string literals stored as char* without lifecycle management. When it was changed to dynamic strings, the code was not updated. This patch changes it to a std::string. Bug: 111261328 Test: flash and check effect works Change-Id: Ia2022c794936f3f75793371cdde86c3047bb6c0a Signed-off-by: Kevin Rocard --- .../config/include/media/EffectsConfig.h | 2 +- media/libeffects/config/src/EffectsConfig.cpp | 16 ++++++++-------- .../factory/EffectsXmlConfigLoader.cpp | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h index 55b946f620..fa0415bbdc 100644 --- a/media/libeffects/config/include/media/EffectsConfig.h +++ b/media/libeffects/config/include/media/EffectsConfig.h @@ -96,7 +96,7 @@ struct ParsingResult { /** Parsed config, nullptr if the xml lib could not load the file */ std::unique_ptr parsedConfig; size_t nbSkippedElement; //< Number of skipped invalid library, effect or processing chain - const char* configPath; //< Path to the loaded configuration + const std::string configPath; //< Path to the loaded configuration }; /** Parses the provided effect configuration. diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp index d79501ff48..351b1ee8c8 100644 --- a/media/libeffects/config/src/EffectsConfig.cpp +++ b/media/libeffects/config/src/EffectsConfig.cpp @@ -250,14 +250,14 @@ bool parseStream(const XMLElement& xmlStream, Effects& effects, std::vector(); @@ -295,7 +295,7 @@ ParsingResult parseWithPath(const char* path) { } } } - return {std::move(config), nbSkippedElements, path}; + return {std::move(config), nbSkippedElements, std::move(path)}; } }; // namespace @@ -310,14 +310,14 @@ ParsingResult parse(const char* path) { if (access(defaultPath.c_str(), R_OK) != 0) { continue; } - auto result = parseWithPath(defaultPath.c_str()); + auto result = parseWithPath(std::move(defaultPath)); if (result.parsedConfig != nullptr) { return result; } } ALOGE("Could not parse effect configuration in any of the default locations."); - return {nullptr, 0, nullptr}; + return {nullptr, 0, ""}; } } // namespace effectsConfig diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp index 7a7d431d99..052a88b585 100644 --- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp +++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp @@ -327,7 +327,8 @@ extern "C" ssize_t EffectLoadXmlEffectConfig(const char* path) &gSkippedEffects, &gSubEffectList); ALOGE_IF(result.nbSkippedElement != 0, "%zu errors during loading of configuration: %s", - result.nbSkippedElement, result.configPath ?: "No config file found"); + result.nbSkippedElement, + result.configPath.empty() ? "No config file found" : result.configPath.c_str()); return result.nbSkippedElement; } -- GitLab From 776eb210f79bb3f944e4b7405a1f8a9ea00438f6 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 19 Jul 2018 15:14:11 -0700 Subject: [PATCH 0139/1530] audioflinger: Fix stream type for device->device patch This logic got broken in ag/668511. The stream type was always extracted from the 2nd patch source, which does not present for device->device patches. Test: MSD prototype, USB headset telecom calls in Pixel Change-Id: Idc8314819010c3a315c5220785a4190e26e55b47 --- services/audioflinger/PatchPanel.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 7f46747d9c..331db488f1 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -461,12 +461,16 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE; - + audio_stream_type_t streamType = AUDIO_STREAM_PATCH; + if (mAudioPatch.num_sources == 2 && mAudioPatch.sources[1].type == AUDIO_PORT_TYPE_MIX) { + // "reuse one existing output mix" case + streamType = mAudioPatch.sources[1].ext.mix.usecase.stream; + } // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer sp tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( mPlayback.thread().get(), - mAudioPatch.sources[1].ext.mix.usecase.stream, + streamType, sampleRate, outChannelMask, format, -- GitLab From c8d581e1ba30b6ec049c77655282660d56781944 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 16 Jul 2018 14:46:23 -0700 Subject: [PATCH 0140/1530] Clean up duplicate lambda in cameraserver device3 code. Test: mm -j64 Test: runtest -x src/ in cts/tests/camera; no new tests fail Change-Id: Id7a8516eb92ce7112f0200cfeaba92278541bfa7 Signed-off-by: Jayant Chowdhary --- .../libcameraservice/device3/Camera3Device.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 8ee70efba5..3958d5bb21 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -3918,18 +3918,17 @@ status_t Camera3Device::HalInterface::processBatchCaptureRequests( } hardware::details::return_status err; - if (hidlSession_3_4 != nullptr) { - err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove, - [&status, &numRequestProcessed] (auto s, uint32_t n) { + auto resultCallback = + [&status, &numRequestProcessed] (auto s, uint32_t n) { status = s; *numRequestProcessed = n; - }); + }; + if (hidlSession_3_4 != nullptr) { + err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove, + resultCallback); } else { err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove, - [&status, &numRequestProcessed] (auto s, uint32_t n) { - status = s; - *numRequestProcessed = n; - }); + resultCallback); } if (!err.isOk()) { ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str()); -- GitLab From e0ec2e3fb2da0d7617f58e0cdbbbb83a75a8d1f0 Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Thu, 19 Jul 2018 21:31:57 -0700 Subject: [PATCH 0141/1530] Remove __INTRODUCED_IN from a typedef __INTRODUCED_IN only applies to function or variable declarations, not a typedef to a function pointer. Bug: https://github.com/android-ndk/ndk/issues/706 Test: builds Change-Id: I472565d3e34e5c6a3cd88500ad03ef9ce4489c25 --- camera/ndk/include/camera/NdkCameraManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index a1cca4d926..df102f25aa 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -119,7 +119,7 @@ void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList) __INTRODUCED * this callback returns. */ typedef void (*ACameraManager_AvailabilityCallback)(void* context, - const char* cameraId) __INTRODUCED_IN(24); + const char* cameraId); /** * A listener for camera devices becoming available or unavailable to open. -- GitLab From 7b651154476414dcb15b822a9ebea2b4bf25cd3d Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Fri, 13 Jul 2018 10:17:19 -0700 Subject: [PATCH 0142/1530] dumpsys: add --json option to media.audio_flinger. Currently only extracting audio performance data. Test: dumpsys media.audio_flinger --json Bug: 68148948 Change-Id: If207e7e20683dcf8d8d5874417cd7466c78a5df0 --- services/audioflinger/AudioFlinger.cpp | 43 +++++++++++++- services/audioflinger/AudioFlinger.h | 3 + services/audioflinger/Configuration.h | 2 +- services/audioflinger/FastMixerDumpState.cpp | 59 +++++++++++++++++++- services/audioflinger/FastMixerDumpState.h | 4 +- services/audioflinger/Threads.cpp | 23 ++++++-- services/audioflinger/Threads.h | 3 + 7 files changed, 127 insertions(+), 10 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 53a4ce9e0c..0237aee0f3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -20,9 +20,11 @@ //#define LOG_NDEBUG 0 #include "Configuration.h" +#include // std::any_of #include #include #include +#include #include #include @@ -434,6 +436,15 @@ status_t AudioFlinger::dump(int fd, const Vector& args) if (!dumpAllowed()) { dumpPermissionDenial(fd, args); } else { + // XXX This is sort of hacky for now. + const bool formatJson = std::any_of(args.begin(), args.end(), + [](const String16 &arg) { return arg == String16("--json"); }); + if (formatJson) { + // XXX consider buffering if the string happens to be too long. + dprintf(fd, "%s", getJsonString().c_str()); + return NO_ERROR; + } + // get state of hardware lock bool hardwareLocked = dumpTryLock(mHardwareLock); if (!hardwareLocked) { @@ -443,7 +454,7 @@ status_t AudioFlinger::dump(int fd, const Vector& args) mHardwareLock.unlock(); } - bool locked = dumpTryLock(mLock); + const bool locked = dumpTryLock(mLock); // failed to lock - AudioFlinger is probably deadlocked if (!locked) { @@ -545,6 +556,36 @@ status_t AudioFlinger::dump(int fd, const Vector& args) return NO_ERROR; } +std::string AudioFlinger::getJsonString() +{ + std::string jsonStr = "{\n"; + const bool locked = dumpTryLock(mLock); + + // failed to lock - AudioFlinger is probably deadlocked + if (!locked) { + jsonStr += " \"deadlock_message\": "; + jsonStr += kDeadlockedString; + jsonStr += ",\n"; + } + // FIXME risky to access data structures without a lock held? + + jsonStr += " \"Playback_Threads\": [\n"; + // dump playback threads + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (i != 0) { + jsonStr += ",\n"; + } + jsonStr += mPlaybackThreads.valueAt(i)->getJsonString(); + } + jsonStr += "\n ]\n}\n"; + + if (locked) { + mLock.unlock(); + } + + return jsonStr; +} + sp AudioFlinger::registerPid(pid_t pid) { Mutex::Autolock _cl(mClientLock); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 0276cadec2..fb83d75e7b 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -111,6 +113,7 @@ public: static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; } virtual status_t dump(int fd, const Vector& args); + std::string getJsonString(); // IAudioFlinger interface, in binder opcode order virtual sp createTrack(const CreateTrackInput& input, diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h index ede8e3f690..64bb426180 100644 --- a/services/audioflinger/Configuration.h +++ b/services/audioflinger/Configuration.h @@ -27,7 +27,7 @@ //#define AUDIO_WATCHDOG // uncomment to display CPU load adjusted for CPU frequency -//#define CPU_FREQUENCY_STATISTICS +#define CPU_FREQUENCY_STATISTICS // uncomment to enable fast threads to take performance samples for later statistical analysis #define FAST_THREAD_STATISTICS diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index d60643c99f..c0f9f0f2f9 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -24,6 +24,7 @@ #include #endif #endif +#include #include #include #include "FastMixerDumpState.h" @@ -76,14 +77,14 @@ void FastMixerDumpState::dump(int fd) const dprintf(fd, " FastMixer Timestamp stats: %s\n", mTimestampVerifier.toString().c_str()); #ifdef FAST_THREAD_STATISTICS // find the interval of valid samples - uint32_t bounds = mBounds; - uint32_t newestOpen = bounds & 0xFFFF; + const uint32_t bounds = mBounds; + const uint32_t newestOpen = bounds & 0xFFFF; uint32_t oldestClosed = bounds >> 16; //uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; uint32_t n; __builtin_sub_overflow(newestOpen, oldestClosed, &n); - n = n & 0xFFFF; + n &= 0xFFFF; if (n > mSamplingN) { ALOGE("too many samples %u", n); @@ -204,4 +205,56 @@ void FastMixerDumpState::dump(int fd) const } } +// TODO get rid of extraneous lines and use better key names. +// TODO may go back to using a library to do the json formatting. +std::string FastMixerDumpState::getJsonString() const +{ + if (mCommand == FastMixerState::INITIAL) { + return " {\n \"status\": \"uninitialized\"\n }"; + } + std::string jsonStr = " {\n"; +#ifdef FAST_THREAD_STATISTICS + // find the interval of valid samples + const uint32_t bounds = mBounds; + const uint32_t newestOpen = bounds & 0xFFFF; + uint32_t oldestClosed = bounds >> 16; + + //uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; + uint32_t n; + __builtin_sub_overflow(newestOpen, oldestClosed, &n); + n &= 0xFFFF; + + if (n > mSamplingN) { + ALOGE("too many samples %u", n); + n = mSamplingN; + } + // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, + // and adjusted CPU load in MHz normalized for CPU clock frequency + std::string jsonWallStr = " \"wall_clock_time\":["; + std::string jsonLoadNsStr = " \"raw_cpu_load\":["; + // loop over all the samples + for (uint32_t j = 0; j < n; ++j) { + size_t i = oldestClosed++ & (mSamplingN - 1); + uint32_t wallNs = mMonotonicNs[i]; + if (j != 0) { + jsonWallStr += ','; + jsonLoadNsStr += ','; + } + /* jsonObject["wall"].append(wallNs); */ + jsonWallStr += std::to_string(wallNs); + uint32_t sampleLoadNs = mLoadNs[i]; + jsonLoadNsStr += std::to_string(sampleLoadNs); + } + jsonWallStr += ']'; + jsonLoadNsStr += ']'; + if (n) { + jsonStr += jsonWallStr + ",\n" + jsonLoadNsStr + "\n"; + } else { + //dprintf(fd, " No FastMixer statistics available currently\n"); + } +#endif + jsonStr += " }"; + return jsonStr; +} + } // android diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index 9b91cbcade..81c4175e37 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H #include +#include #include #include "Configuration.h" #include "FastThreadDumpState.h" @@ -65,7 +66,8 @@ struct FastMixerDumpState : FastThreadDumpState { FastMixerDumpState(); /*virtual*/ ~FastMixerDumpState(); - void dump(int fd) const; // should only be called on a stable copy, not the original + void dump(int fd) const; // should only be called on a stable copy, not the original + std::string getJsonString() const; // should only be called on a stable copy, not the original double mLatencyMs = 0.; // measured latency, default of 0 if no valid timestamp read. uint32_t mWriteSequence; // incremented before and after each write() diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 70af5c6916..5cc8c312be 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -23,6 +23,8 @@ #include "Configuration.h" #include #include +#include +#include #include #include #include @@ -1760,6 +1762,11 @@ void AudioFlinger::PlaybackThread::dump(int fd, const Vector& args) mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); } +std::string AudioFlinger::PlaybackThread::getJsonString() const +{ + return "{}"; +} + void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& args __unused) { String8 result; @@ -5098,9 +5105,8 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar // while we are dumping it. It may be inconsistent, but it won't mutate! // This is a large object so we place it on the heap. // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages. - const FastMixerDumpState *copy = new FastMixerDumpState(mFastMixerDumpState); + const std::unique_ptr copy(new FastMixerDumpState(mFastMixerDumpState)); copy->dump(fd); - delete copy; #ifdef STATE_QUEUE_DUMP // Similar for state queue @@ -5123,6 +5129,16 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar } } +std::string AudioFlinger::MixerThread::getJsonString() const +{ + // Make a non-atomic copy of fast mixer dump state so it won't change underneath us + // while we are dumping it. It may be inconsistent, but it won't mutate! + // This is a large object so we place it on the heap. + // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages. + return std::unique_ptr(new FastMixerDumpState(mFastMixerDumpState)) + ->getJsonString(); +} + uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const { return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000) / 2; @@ -7423,9 +7439,8 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a // while we are dumping it. It may be inconsistent, but it won't mutate! // This is a large object so we place it on the heap. // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages. - const FastCaptureDumpState *copy = new FastCaptureDumpState(mFastCaptureDumpState); + std::unique_ptr copy(new FastCaptureDumpState(mFastCaptureDumpState)); copy->dump(fd); - delete copy; } void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector& args __unused) diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 064e29178c..734a7bd371 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -658,6 +658,8 @@ public: virtual ~PlaybackThread(); void dump(int fd, const Vector& args); + // returns a string of audio performance related data in JSON format. + virtual std::string getJsonString() const; // Thread virtuals virtual bool threadLoop(); @@ -1103,6 +1105,7 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); virtual void dumpInternals(int fd, const Vector& args); + std::string getJsonString() const override; virtual bool isTrackAllowed_l( audio_channel_mask_t channelMask, audio_format_t format, -- GitLab From a18dced5af77307be66d8c05ac05e6160ab8d8ed Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 20 Jul 2018 15:14:54 -0700 Subject: [PATCH 0143/1530] audio policy: refactor record activity tracking (AudioSession) Refactor input activity ref counting and callbacks to align with implementation for output. This will help migration to per client activity tracking. Test: audio smoke test, CTS for AudioRecord Change-Id: I8806446f33d3090f1042e0956df81318646d5285 --- .../include/AudioIODescriptorInterface.h | 8 --- .../include/AudioInputDescriptor.h | 10 ++- .../managerdefinitions/include/AudioSession.h | 24 ++----- .../src/AudioInputDescriptor.cpp | 72 +++++++++++++++++-- .../managerdefinitions/src/AudioSession.cpp | 69 +----------------- .../managerdefault/AudioPolicyManager.cpp | 9 ++- 6 files changed, 89 insertions(+), 103 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h index 9f3fc0c801..555412e0cd 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h @@ -34,12 +34,4 @@ public: virtual void setPatchHandle(audio_patch_handle_t handle) = 0; }; -class AudioIODescriptorUpdateListener -{ -public: - virtual ~AudioIODescriptorUpdateListener() {}; - - virtual void onIODescriptorUpdate() const = 0; -}; - } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 85f3b869c3..ca837c421a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -66,6 +66,8 @@ public: AudioSessionCollection getAudioSessions(bool activeOnly) const; size_t getAudioSessionCount(bool activeOnly) const; audio_source_t getHighestPrioritySource(bool activeOnly) const; + void changeRefCount(audio_session_t session, int delta); + // implementation of AudioIODescriptorInterface audio_config_base_t getConfig() const override; @@ -79,14 +81,17 @@ public: audio_input_flags_t flags, audio_io_handle_t *input); // Called when a stream is about to be started. - // Note: called after AudioSession::changeActiveCount(1) + // Note: called after changeRefCount(session, 1) status_t start(); // Called after a stream is stopped - // Note: called after AudioSession::changeActiveCount(-1) + // Note: called after changeRefCount(session, -1) void stop(); void close(); private: + + void updateSessionRecordingConfiguration(int event, const sp& audioSession); + audio_patch_handle_t mPatchHandle; audio_port_handle_t mId; // audio sessions attached to this input @@ -99,6 +104,7 @@ private: // We also inherit sessions from the preempted input to avoid a 3 way preemption loop etc... SortedVector mPreemptedSessions; AudioPolicyClientInterface *mClientInterface; + uint32_t mGlobalRefCount; // non-session-specific ref count }; class AudioInputCollection : diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index 53e6ec990e..1636d3a480 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -29,7 +29,7 @@ namespace android { class AudioPolicyClientInterface; -class AudioSession : public RefBase, public AudioIODescriptorUpdateListener +class AudioSession : public RefBase { public: AudioSession(audio_session_t session, @@ -39,9 +39,7 @@ public: audio_channel_mask_t channelMask, audio_input_flags_t flags, uid_t uid, - bool isSoundTrigger, - AudioMix* policyMix, - AudioPolicyClientInterface *clientInterface); + bool isSoundTrigger); status_t dump(int fd, int spaces, int index) const; @@ -50,6 +48,8 @@ public: audio_format_t format() const { return mConfig.format; } uint32_t sampleRate() const { return mConfig.sample_rate; } audio_channel_mask_t channelMask() const { return mConfig.channel_mask; } + audio_config_base config() const { return mConfig; } + record_client_info_t recordClientInfo() const { return mRecordClientInfo; } audio_input_flags_t flags() const { return mFlags; } uid_t uid() const { return mRecordClientInfo.uid; } void setUid(uid_t uid) { mRecordClientInfo.uid = uid; } @@ -63,10 +63,6 @@ public: uint32_t changeOpenCount(int delta); uint32_t changeActiveCount(int delta); - void setInfoProvider(AudioIODescriptorInterface *provider); - // implementation of AudioIODescriptorUpdateListener - virtual void onIODescriptorUpdate() const; - private: record_client_info_t mRecordClientInfo; const struct audio_config_base mConfig; @@ -75,19 +71,14 @@ private: bool mSilenced; uint32_t mOpenCount; uint32_t mActiveCount; - AudioMix* mPolicyMix; // non NULL when used by a dynamic policy - AudioPolicyClientInterface* mClientInterface; - const AudioIODescriptorInterface* mInfoProvider; }; class AudioSessionCollection : - public DefaultKeyedVector >, - public AudioIODescriptorUpdateListener + public DefaultKeyedVector > { public: status_t addSession(audio_session_t session, - const sp& audioSession, - AudioIODescriptorInterface *provider); + const sp& audioSession); status_t removeSession(audio_session_t session); @@ -99,9 +90,6 @@ public: bool isSourceActive(audio_source_t source) const; audio_source_t getHighestPrioritySource(bool activeOnly) const; - // implementation of AudioIODescriptorUpdateListener - virtual void onIODescriptorUpdate() const; - status_t dump(int fd, int spaces) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index f0144dbebe..b9895a94b5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -32,7 +32,7 @@ AudioInputDescriptor::AudioInputDescriptor(const sp& profile, : mIoHandle(0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mProfile(profile), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0), - mClientInterface(clientInterface) + mClientInterface(clientInterface), mGlobalRefCount(0) { if (profile != NULL) { profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat); @@ -164,7 +164,7 @@ size_t AudioInputDescriptor::getAudioSessionCount(bool activeOnly) const status_t AudioInputDescriptor::addAudioSession(audio_session_t session, const sp& audioSession) { - return mSessions.addSession(session, audioSession, /*AudioIODescriptorInterface*/this); + return mSessions.addSession(session, audioSession); } status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) { @@ -179,7 +179,11 @@ audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle) { mPatchHandle = handle; - mSessions.onIODescriptorUpdate(); + for (size_t i = 0; i < mSessions.size(); i++) { + if (mSessions[i]->activeCount() > 0) { + updateSessionRecordingConfiguration(RECORD_CONFIG_EVENT_START, mSessions[i]); + } + } } audio_config_base_t AudioInputDescriptor::getConfig() const @@ -266,7 +270,7 @@ void AudioInputDescriptor::close() LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u", __FUNCTION__, mProfile->curOpenCount); // do not call stop() here as stop() is supposed to be called after - // AudioSession::changeActiveCount(-1) and we don't know how many sessions + // changeRefCount(session, -1) and we don't know how many sessions // are still active at this time if (isActive()) { mProfile->curActiveCount--; @@ -276,6 +280,66 @@ void AudioInputDescriptor::close() } } +void AudioInputDescriptor::changeRefCount(audio_session_t session, int delta) +{ + sp audioSession = mSessions.valueFor(session); + if (audioSession == 0) { + return; + } + // handle session-independent ref count + uint32_t oldGlobalRefCount = mGlobalRefCount; + if ((delta + (int)mGlobalRefCount) < 0) { + ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount); + delta = -((int)mGlobalRefCount); + } + mGlobalRefCount += delta; + if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { + mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + MIX_STATE_MIXING); + } + + } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { + mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + MIX_STATE_IDLE); + } + } + + uint32_t oldActiveCount = audioSession->activeCount(); + if ((delta + (int)oldActiveCount) < 0) { + ALOGW("changeRefCount() invalid delta %d for sesion %d active count %d", + delta, session, oldActiveCount); + delta = -((int)oldActiveCount); + } + + audioSession->changeActiveCount(delta); + + int event = RECORD_CONFIG_EVENT_NONE; + if ((oldActiveCount == 0) && (audioSession->activeCount() > 0)) { + event = RECORD_CONFIG_EVENT_START; + } else if ((oldActiveCount > 0) && (audioSession->activeCount() == 0)) { + event = RECORD_CONFIG_EVENT_STOP; + } + if (event != RECORD_CONFIG_EVENT_NONE) { + updateSessionRecordingConfiguration(event, audioSession); + } + +} + +void AudioInputDescriptor::updateSessionRecordingConfiguration( + int event, const sp& audioSession) { + + const audio_config_base_t sessionConfig = audioSession->config(); + const record_client_info_t recordClientInfo = audioSession->recordClientInfo(); + const audio_config_base_t config = getConfig(); + mClientInterface->onRecordingConfigurationUpdate(event, + &recordClientInfo, &sessionConfig, + &config, mPatchHandle); +} + status_t AudioInputDescriptor::dump(int fd) { const size_t SIZE = 256; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index 91dee35afa..5ea4c926d6 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -35,14 +35,11 @@ AudioSession::AudioSession(audio_session_t session, audio_channel_mask_t channelMask, audio_input_flags_t flags, uid_t uid, - bool isSoundTrigger, - AudioMix* policyMix, - AudioPolicyClientInterface *clientInterface) : + bool isSoundTrigger) : mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), mFlags(flags), mIsSoundTrigger(isSoundTrigger), - mOpenCount(1), mActiveCount(0), mPolicyMix(policyMix), mClientInterface(clientInterface), - mInfoProvider(NULL) + mOpenCount(1), mActiveCount(0) { } @@ -60,7 +57,6 @@ uint32_t AudioSession::changeOpenCount(int delta) uint32_t AudioSession::changeActiveCount(int delta) { - const uint32_t oldActiveCount = mActiveCount; if ((delta + (int)mActiveCount) < 0) { ALOGW("%s invalid delta %d, active count %d", __FUNCTION__, delta, mActiveCount); @@ -68,34 +64,6 @@ uint32_t AudioSession::changeActiveCount(int delta) } mActiveCount += delta; ALOGV("%s active count %d", __FUNCTION__, mActiveCount); - int event = RECORD_CONFIG_EVENT_NONE; - - if ((oldActiveCount == 0) && (mActiveCount > 0)) { - event = RECORD_CONFIG_EVENT_START; - } else if ((oldActiveCount > 0) && (mActiveCount == 0)) { - event = RECORD_CONFIG_EVENT_STOP; - } - - if (event != RECORD_CONFIG_EVENT_NONE) { - // Dynamic policy callback: - // if input maps to a dynamic policy with an activity listener, notify of state change - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) - { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, - (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); - } - - // Recording configuration callback: - const AudioIODescriptorInterface* provider = mInfoProvider; - const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : - AUDIO_CONFIG_BASE_INITIALIZER; - const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : - AUDIO_PATCH_HANDLE_NONE; - if (patchHandle != AUDIO_PATCH_HANDLE_NONE) { - mClientInterface->onRecordingConfigurationUpdate(event, &mRecordClientInfo, - &mConfig, &deviceConfig, patchHandle); - } - } return mActiveCount; } @@ -114,27 +82,6 @@ bool AudioSession::matches(const sp &other) const return false; } -void AudioSession::setInfoProvider(AudioIODescriptorInterface *provider) -{ - mInfoProvider = provider; -} - -void AudioSession::onIODescriptorUpdate() const -{ - if (mActiveCount > 0) { - // resend the callback after requerying the informations from the info provider - const AudioIODescriptorInterface* provider = mInfoProvider; - const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : - AUDIO_CONFIG_BASE_INITIALIZER; - const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : - AUDIO_PATCH_HANDLE_NONE; - if (patchHandle != AUDIO_PATCH_HANDLE_NONE) { - mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_START, - &mRecordClientInfo, &mConfig, &deviceConfig, patchHandle); - } - } -} - status_t AudioSession::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; @@ -169,8 +116,7 @@ status_t AudioSession::dump(int fd, int spaces, int index) const } status_t AudioSessionCollection::addSession(audio_session_t session, - const sp& audioSession, - AudioIODescriptorInterface *provider) + const sp& audioSession) { ssize_t index = indexOfKey(session); @@ -178,7 +124,6 @@ status_t AudioSessionCollection::addSession(audio_session_t session, ALOGW("addSession() session %d already in", session); return ALREADY_EXISTS; } - audioSession->setInfoProvider(provider); add(session, audioSession); ALOGV("addSession() session %d client %d source %d", session, audioSession->uid(), audioSession->inputSource()); @@ -194,7 +139,6 @@ status_t AudioSessionCollection::removeSession(audio_session_t session) return ALREADY_EXISTS; } ALOGV("removeSession() session %d", session); - valueAt(index)->setInfoProvider(NULL); removeItemsAt(index); return NO_ERROR; } @@ -271,13 +215,6 @@ audio_source_t AudioSessionCollection::getHighestPrioritySource(bool activeOnly) return source; } -void AudioSessionCollection::onIODescriptorUpdate() const -{ - for (size_t i = 0; i < size(); i++) { - valueAt(i)->onIODescriptorUpdate(); - } -} - status_t AudioSessionCollection::dump(int fd, int spaces) const { const size_t SIZE = 256; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5cdd63aa04..efc9753387 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1679,8 +1679,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, config->channel_mask, flags, uid, - isSoundTrigger, - policyMix, mpClientInterface); + isSoundTrigger); // FIXME: disable concurrent capture until UI is ready #if 0 @@ -2007,7 +2006,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection - audioSession->changeActiveCount(1); + inputDesc->changeRefCount(session, 1); // Routing? mInputRoutes.incRouteActivity(session); @@ -2021,7 +2020,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, status_t status = inputDesc->start(); if (status != NO_ERROR) { mInputRoutes.decRouteActivity(session); - audioSession->changeActiveCount(-1); + inputDesc->changeRefCount(session, -1); return status; } @@ -2085,7 +2084,7 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, return INVALID_OPERATION; } - audioSession->changeActiveCount(-1); + inputDesc->changeRefCount(session, -1); // Routing? mInputRoutes.decRouteActivity(session); -- GitLab From 0c9470cec23a62876c398bc6ab9ac7e1588e2bba Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Thu, 19 Jul 2018 18:03:48 -0700 Subject: [PATCH 0144/1530] Restore __ANDROID_API__ guards for camera/media headers Partial revert of "Add __INTRODUCED_IN to audio/camera/media headers.", commit 85a41536a4885b12120ddafb4d0b3fee8a8613b6. Restore the API guards where they used to exist. I *could* add __ANDROID_API__ around only the function declarations, which is what the versioner would do, but the function declarations are mixed up with the types and constants, so I'd need to add a bunch of __ANDROID_API__ checks or reorganize the headers. NdkMediaFormat.h and AAudio.h still have __INTRODUCED_IN symbols that aren't guarded by __ANDROID_API__. Maybe those files should be reorganized so they don't alternate between APIs so much. Test: builds Bug: http://b/111668906 Change-Id: I757a6097f7840ea50e2fc92db7defef493097672 --- camera/ndk/include/camera/NdkCameraCaptureSession.h | 7 +++++++ camera/ndk/include/camera/NdkCameraDevice.h | 8 ++++++++ camera/ndk/include/camera/NdkCameraError.h | 4 ++++ camera/ndk/include/camera/NdkCameraManager.h | 4 ++++ camera/ndk/include/camera/NdkCameraMetadata.h | 4 ++++ camera/ndk/include/camera/NdkCameraMetadataTags.h | 5 +++++ camera/ndk/include/camera/NdkCaptureRequest.h | 8 ++++++++ media/ndk/include/media/NdkImage.h | 8 ++++++++ media/ndk/include/media/NdkImageReader.h | 8 ++++++++ media/ndk/include/media/NdkMediaCodec.h | 12 ++++++++++++ media/ndk/include/media/NdkMediaCrypto.h | 4 ++++ media/ndk/include/media/NdkMediaDataSource.h | 4 ++++ media/ndk/include/media/NdkMediaDrm.h | 4 ++++ media/ndk/include/media/NdkMediaExtractor.h | 12 ++++++++++++ media/ndk/include/media/NdkMediaFormat.h | 6 ++++++ media/ndk/include/media/NdkMediaMuxer.h | 4 ++++ 16 files changed, 102 insertions(+) diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 9bf8247b95..5e0db60c17 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -45,6 +45,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + /** * ACameraCaptureSession is an opaque type that manages frame captures of a camera device. * @@ -591,6 +593,10 @@ camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* sessi camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 28 + typedef struct ACaptureSessionOutput ACaptureSessionOutput; /** @@ -635,6 +641,7 @@ typedef struct ACaptureSessionOutput ACaptureSessionOutput; */ camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session, ACaptureSessionOutput* output) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index bdd27f9942..7c13b34a82 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -44,6 +44,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + /** * ACameraDevice is opaque type that provides access to a camera device. * @@ -666,6 +668,10 @@ camera_status_t ACameraDevice_createCaptureSession( const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 28 + /** * Create a shared ACaptureSessionOutput object. * @@ -757,6 +763,8 @@ camera_status_t ACameraDevice_createCaptureSessionWithSessionParameters( const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ + __END_DECLS #endif /* _NDK_CAMERA_DEVICE_H */ diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h index e19ce362f4..6b5815502b 100644 --- a/camera/ndk/include/camera/NdkCameraError.h +++ b/camera/ndk/include/camera/NdkCameraError.h @@ -40,6 +40,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + typedef enum { ACAMERA_OK = 0, @@ -130,6 +132,8 @@ typedef enum { ACAMERA_ERROR_PERMISSION_DENIED = ACAMERA_ERROR_BASE - 13, } camera_status_t; +#endif /* __ANDROID_API__ >= 24 */ + __END_DECLS #endif /* _NDK_CAMERA_ERROR_H */ diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index df102f25aa..ea76738806 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -44,6 +44,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + /** * ACameraManager is opaque type that provides access to camera service. * @@ -274,6 +276,8 @@ camera_status_t ACameraManager_openCamera( ACameraDevice_StateCallbacks* callback, /*out*/ACameraDevice** device) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + __END_DECLS #endif /* _NDK_CAMERA_MANAGER_H */ diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h index 2078da7d31..611e270e56 100644 --- a/camera/ndk/include/camera/NdkCameraMetadata.h +++ b/camera/ndk/include/camera/NdkCameraMetadata.h @@ -44,6 +44,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + /** * ACameraMetadata is opaque type that provides access to read-only camera metadata like camera * characteristics (via {@link ACameraManager_getCameraCharacteristics}) or capture results (via @@ -229,6 +231,8 @@ ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src) __INTRODUCED_I */ void ACameraMetadata_free(ACameraMetadata* metadata) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + __END_DECLS #endif /* _NDK_CAMERA_METADATA_H */ diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index bd854694fc..05010060e4 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -40,6 +40,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + typedef enum acamera_metadata_section { ACAMERA_COLOR_CORRECTION, ACAMERA_CONTROL, @@ -7894,6 +7896,9 @@ typedef enum acamera_metadata_enum_acamera_distortion_correction_mode { } acamera_metadata_enum_android_distortion_correction_mode_t; + +#endif /* __ANDROID_API__ >= 24 */ + __END_DECLS #endif /* _NDK_CAMERA_METADATA_TAGS_H */ diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h index 2fb5d12c7b..5340e76044 100644 --- a/camera/ndk/include/camera/NdkCaptureRequest.h +++ b/camera/ndk/include/camera/NdkCaptureRequest.h @@ -44,6 +44,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 24 + // Container for output targets typedef struct ACameraOutputTargets ACameraOutputTargets; @@ -302,6 +304,10 @@ camera_status_t ACaptureRequest_setEntry_rational( */ void ACaptureRequest_free(ACaptureRequest* request) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 28 + /** * Associate an arbitrary user context pointer to the {@link ACaptureRequest} * @@ -350,6 +356,8 @@ camera_status_t ACaptureRequest_getUserContext( */ ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ + __END_DECLS #endif /* _NDK_CAPTURE_REQUEST_H */ diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h index 38e12e32bf..5562e82cfd 100644 --- a/media/ndk/include/media/NdkImage.h +++ b/media/ndk/include/media/NdkImage.h @@ -516,6 +516,8 @@ typedef struct AImageCropRect { int32_t bottom; } AImageCropRect; +#if __ANDROID_API__ >= 24 + /** * Return the image back the the system and delete the AImage object from memory. * @@ -712,6 +714,10 @@ media_status_t AImage_getPlaneData( const AImage* image, int planeIdx, /*out*/uint8_t** data, /*out*/int* dataLength) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 26 + /** * Return the image back the the system and delete the AImage object from memory asynchronously. * @@ -756,6 +762,8 @@ void AImage_deleteAsync(AImage* image, int releaseFenceFd) __INTRODUCED_IN(26); */ media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer) __INTRODUCED_IN(26); +#endif /* __ANDROID_API__ >= 26 */ + __END_DECLS #endif //_NDK_IMAGE_H diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h index eb1a44aaa2..68de17687e 100644 --- a/media/ndk/include/media/NdkImageReader.h +++ b/media/ndk/include/media/NdkImageReader.h @@ -50,6 +50,8 @@ __BEGIN_DECLS */ typedef struct AImageReader AImageReader; +#if __ANDROID_API__ >= 24 + /** * Create a new reader for images of the desired size and format. * @@ -296,6 +298,10 @@ typedef struct AImageReader_ImageListener { media_status_t AImageReader_setImageListener( AImageReader* reader, AImageReader_ImageListener* listener) __INTRODUCED_IN(24); +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 26 + /** * AImageReader constructor similar to {@link AImageReader_new} that takes an additional parameter * for the consumer usage. All other parameters and the return values are identical to those passed @@ -455,6 +461,8 @@ typedef struct AImageReader_BufferRemovedListener { media_status_t AImageReader_setBufferRemovedListener( AImageReader* reader, AImageReader_BufferRemovedListener* listener) __INTRODUCED_IN(26); +#endif /* __ANDROID_API__ >= 26 */ + __END_DECLS #endif //_NDK_IMAGE_READER_H diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h index b329b39d75..9dc120df29 100644 --- a/media/ndk/include/media/NdkMediaCodec.h +++ b/media/ndk/include/media/NdkMediaCodec.h @@ -121,6 +121,8 @@ struct AMediaCodecOnAsyncNotifyCallback { AMediaCodecOnAsyncError onAsyncError; }; +#if __ANDROID_API__ >= 21 + /** * Create codec by name. Use this if you know the exact codec you want to use. * When configuring, you will need to specify whether to use the codec as an @@ -274,6 +276,8 @@ media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface media_status_t AMediaCodec_releaseOutputBufferAtTime( AMediaCodec *mData, size_t idx, int64_t timestampNs) __INTRODUCED_IN(21); +#if __ANDROID_API__ >= 26 + /** * Creates a Surface that can be used as the input to encoder, in place of input buffers * @@ -344,6 +348,10 @@ media_status_t AMediaCodec_setParameters( */ media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) __INTRODUCED_IN(26); +#endif /* __ANDROID_API__ >= 26 */ + +#if __ANDROID_API__ >= 28 + /** * Get the component name. If the codec was created by createDecoderByType * or createEncoderByType, what component is chosen is not known beforehand. @@ -405,6 +413,8 @@ bool AMediaCodecActionCode_isRecoverable(int32_t actionCode) __INTRODUCED_IN(28) */ bool AMediaCodecActionCode_isTransient(int32_t actionCode) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ + typedef enum { AMEDIACODECRYPTOINFO_MODE_CLEAR = 0, AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1, @@ -483,6 +493,8 @@ media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_ */ media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst) __INTRODUCED_IN(21); +#endif /* __ANDROID_API__ >= 21 */ + __END_DECLS #endif //_NDK_MEDIA_CODEC_H diff --git a/media/ndk/include/media/NdkMediaCrypto.h b/media/ndk/include/media/NdkMediaCrypto.h index b673adc6a4..bcdf9a004d 100644 --- a/media/ndk/include/media/NdkMediaCrypto.h +++ b/media/ndk/include/media/NdkMediaCrypto.h @@ -47,6 +47,8 @@ typedef struct AMediaCrypto AMediaCrypto; typedef uint8_t AMediaUUID[16]; +#if __ANDROID_API__ >= 21 + bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid) __INTRODUCED_IN(21); bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime) __INTRODUCED_IN(21); @@ -55,6 +57,8 @@ AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *initData, size void AMediaCrypto_delete(AMediaCrypto* crypto) __INTRODUCED_IN(21); +#endif /* __ANDROID_API__ >= 21 */ + __END_DECLS #endif // _NDK_MEDIA_CRYPTO_H diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h index 3a4373c8a1..ea5ba0c43b 100644 --- a/media/ndk/include/media/NdkMediaDataSource.h +++ b/media/ndk/include/media/NdkMediaDataSource.h @@ -38,6 +38,8 @@ __BEGIN_DECLS struct AMediaDataSource; typedef struct AMediaDataSource AMediaDataSource; +#if __ANDROID_API__ >= 28 + /* * AMediaDataSource's callbacks will be invoked on an implementation-defined thread * or thread pool. No guarantees are provided about which thread(s) will be used for @@ -133,6 +135,8 @@ void AMediaDataSource_setClose( AMediaDataSource*, AMediaDataSourceClose) __INTRODUCED_IN(28); +#endif /*__ANDROID_API__ >= 28 */ + __END_DECLS #endif // _NDK_MEDIA_DATASOURCE_H diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h index 24c0d6d86f..020968173b 100644 --- a/media/ndk/include/media/NdkMediaDrm.h +++ b/media/ndk/include/media/NdkMediaDrm.h @@ -87,6 +87,8 @@ typedef enum AMediaDrmEventType { typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId, AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize); +#if __ANDROID_API__ >= 21 + /** * Query if the given scheme identified by its UUID is supported on this device, and * whether the drm plugin is able to handle the media container format specified by mimeType. @@ -459,6 +461,8 @@ media_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId *sessionId const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize, const uint8_t *signature, size_t signatureSize) __INTRODUCED_IN(21); +#endif /* __ANDROID_API__ >= 21 */ + __END_DECLS #endif //_NDK_MEDIA_DRM_H diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h index 9f6089182c..6a1796f977 100644 --- a/media/ndk/include/media/NdkMediaExtractor.h +++ b/media/ndk/include/media/NdkMediaExtractor.h @@ -49,6 +49,8 @@ __BEGIN_DECLS struct AMediaExtractor; typedef struct AMediaExtractor AMediaExtractor; +#if __ANDROID_API__ >= 21 + /** * Create new media extractor */ @@ -72,12 +74,16 @@ media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location) __INTRODUCED_IN(21); // TODO support headers +#if __ANDROID_API__ >= 28 + /** * Set the custom data source implementation from which the extractor will read. */ media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*, AMediaDataSource *src) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ + /** * Return the number of tracks in the previously specified media file */ @@ -173,6 +179,8 @@ enum { AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2, }; +#if __ANDROID_API__ >= 28 + /** * Returns the format of the extractor. The caller must free the returned format * using AMediaFormat_delete(format). @@ -219,6 +227,10 @@ int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *) __INTRODUCED_IN(28) media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat *fmt) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ + +#endif /* __ANDROID_API__ >= 21 */ + __END_DECLS #endif // _NDK_MEDIA_EXTRACTOR_H diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 8f37f7b0b3..5f7804d8a8 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -46,6 +46,8 @@ __BEGIN_DECLS struct AMediaFormat; typedef struct AMediaFormat AMediaFormat; +#if __ANDROID_API__ >= 21 + AMediaFormat *AMediaFormat_new() __INTRODUCED_IN(21); media_status_t AMediaFormat_delete(AMediaFormat*) __INTRODUCED_IN(21); @@ -155,6 +157,9 @@ extern const char* AMEDIAFORMAT_KEY_TRACK_ID __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_TRACK_INDEX __INTRODUCED_IN(28); extern const char* AMEDIAFORMAT_KEY_WIDTH __INTRODUCED_IN(21); +#endif /* __ANDROID_API__ >= 21 */ + +#if __ANDROID_API__ >= 28 bool AMediaFormat_getDouble(AMediaFormat*, const char *name, double *out) __INTRODUCED_IN(28); bool AMediaFormat_getRect(AMediaFormat*, const char *name, int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) __INTRODUCED_IN(28); @@ -163,6 +168,7 @@ void AMediaFormat_setDouble(AMediaFormat*, const char* name, double value) __INT void AMediaFormat_setSize(AMediaFormat*, const char* name, size_t value) __INTRODUCED_IN(28); void AMediaFormat_setRect(AMediaFormat*, const char* name, int32_t left, int32_t top, int32_t right, int32_t bottom) __INTRODUCED_IN(28); +#endif /* __ANDROID_API__ >= 28 */ __END_DECLS diff --git a/media/ndk/include/media/NdkMediaMuxer.h b/media/ndk/include/media/NdkMediaMuxer.h index 75c70edba8..7393867c41 100644 --- a/media/ndk/include/media/NdkMediaMuxer.h +++ b/media/ndk/include/media/NdkMediaMuxer.h @@ -53,6 +53,8 @@ typedef enum { AMEDIAMUXER_OUTPUT_FORMAT_WEBM = 1, } OutputFormat; +#if __ANDROID_API__ >= 21 + /** * Create new media muxer */ @@ -121,6 +123,8 @@ media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer, size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info) __INTRODUCED_IN(21); +#endif /* __ANDROID_API__ >= 21 */ + __END_DECLS #endif // _NDK_MEDIA_MUXER_H -- GitLab From 6b51d7d119b9ac92e7351bbaf0466398ff4db87a Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Mon, 23 Jul 2018 11:41:44 +0100 Subject: [PATCH 0145/1530] Camera: Flush in-flight requests during device detach Improve performance during device detach by flushing all camera requests. Bug: 80402005 Test: Camera CTS Change-Id: I3a6864575b1533c77b5478c2390a908892700f6e --- .../camera/libcameraservice/api2/CameraDeviceClient.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 98d053448e..18788f2a78 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -1677,9 +1677,12 @@ void CameraDeviceClient::detachDevice() { // WORKAROUND: HAL refuses to disconnect while there's streams in flight { - mDevice->clearStreamingRequest(); - + int64_t lastFrameNumber; status_t code; + if ((code = mDevice->flush(&lastFrameNumber)) != OK) { + ALOGE("%s: flush failed with code 0x%x", __FUNCTION__, code); + } + if ((code = mDevice->waitUntilDrained()) != OK) { ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code); -- GitLab From a7a63186c5eb8bce903ba21ddf088e11195e247c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 4 Jun 2018 13:24:49 -0700 Subject: [PATCH 0146/1530] Fix integer overflow abort Bug: 78031949 Test: manual, CTS Change-Id: Ib528ef9fff9494b554bd02817e4176ab9f7a13a4 --- media/extractors/mp4/SampleIterator.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/SampleIterator.cpp b/media/extractors/mp4/SampleIterator.cpp index 93ee7c6cb8..1a6d306dbd 100644 --- a/media/extractors/mp4/SampleIterator.cpp +++ b/media/extractors/mp4/SampleIterator.cpp @@ -328,7 +328,15 @@ status_t SampleIterator::findSampleTimeAndDuration( ++mTimeToSampleIndex; } - *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); + // below is equivalent to: + // *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); + uint32_t tmp; + if (__builtin_sub_overflow(sampleIndex, mTTSSampleIndex, &tmp) || + __builtin_mul_overflow(mTTSDuration, tmp, &tmp) || + __builtin_add_overflow(mTTSSampleTime, tmp, &tmp)) { + return ERROR_OUT_OF_RANGE; + } + *time = tmp; int32_t offset = mTable->getCompositionTimeOffset(sampleIndex); if ((offset < 0 && *time < (offset == INT32_MIN ? -- GitLab From 97373b0fbabf670bb33bcc433ab04848d9594385 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 23 Jul 2018 13:27:24 -0700 Subject: [PATCH 0147/1530] audioflinger: Use AUDIO_HARDWARE_MODULE_ID_MSD where appropriate After separating the constants for MSD service and module names, use the module name when working with modules. Bug: 63901775 Test: make Change-Id: Icf80d28903e5c36051e33c4be818fd3d7256ce91 --- services/audioflinger/AudioFlinger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index c389683c92..9234364248 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1877,7 +1877,7 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) mHardwareStatus = AUDIO_HW_IDLE; } - if (strcmp(name, AUDIO_HAL_SERVICE_NAME_MSD) == 0) { + if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_MSD) == 0) { // An MSD module is inserted before hardware modules in order to mix encoded streams. flags = static_cast(flags | AudioHwDevice::AHWD_IS_INSERT); } -- GitLab From c2039dc505ec5dcc2b5d2f80a3957be1684913a8 Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Thu, 19 Jul 2018 18:30:29 -0700 Subject: [PATCH 0148/1530] Fix NdkImage.h includes It needs stdint.h. It's OK to include android/hardware_buffer.h on pre-26. We'd like to remove the __ANDROID_API__ guard around AImage_getHardwareBuffer, and if/when we do, we'll need android/hardware_buffer.h. The last time we removed the __ANDROID_API__ guards around the APIs, we forgot to remove the one around this #include. Bug: http://b/111130072#comment12 Test: builds Change-Id: Ice8073476d4ad4802e3540f15eee3af29e0b891f --- media/ndk/include/media/NdkImage.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h index 5562e82cfd..f936118776 100644 --- a/media/ndk/include/media/NdkImage.h +++ b/media/ndk/include/media/NdkImage.h @@ -36,13 +36,12 @@ #ifndef _NDK_IMAGE_H #define _NDK_IMAGE_H +#include #include #include "NdkMediaError.h" -#if __ANDROID_API__ >= 26 #include -#endif /* __ANDROID_API__ >= 26 */ __BEGIN_DECLS -- GitLab From 04ff947b07f0b4becacfa0668862b2d48437a6be Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 23 Jul 2018 14:15:40 -0700 Subject: [PATCH 0149/1530] audio policy: fix regression in Audio MMAP device selection Commit f3a5a601 caused a regression for AAudio MMAP playback use case where disconnecting currently selected device does not generate a disconnect callback to client causing silent audio until the stream is closed and reopened. This CL fixes a logical flaw in method SessionRouteMap::getActiveDeviceForStream() introduced by commit f3a5a601 causing a stale forced device to be returned after this device was disconnected. Bug: 111711159 Bug: 79878501 Test: CTS tests for audi routing and AAudio. Audio smoke tests Change-Id: Ibb16e26bc59b9e3f99bc74eb944601c6be5026dd --- .../common/managerdefinitions/src/SessionRoute.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp index 38ab560403..440a4e7b2b 100644 --- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "APM::SessionRoute" +#define LOG_TAG "APM_SessionRoute" //#define LOG_NDEBUG 0 #include "SessionRoute.h" @@ -122,19 +122,17 @@ void SessionRouteMap::addRoute(audio_session_t session, audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType, const DeviceVector& availableDevices) { - audio_devices_t device = AUDIO_DEVICE_NONE; - for (size_t index = 0; index < size(); index++) { sp route = valueAt(index); if (streamType == route->mStreamType && route->isActiveOrChanged() && route->mDeviceDescriptor != 0) { - device = route->mDeviceDescriptor->type(); + audio_devices_t device = route->mDeviceDescriptor->type(); if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) { - break; + return device; } } } - return device; + return AUDIO_DEVICE_NONE; } } // namespace android -- GitLab From 9de8bd18b21642f6f1fb4e3ef75e44a2cc79f3ce Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 23 Jul 2018 16:41:31 -0700 Subject: [PATCH 0150/1530] AudioPolicy: Reuse SortedVector comparison from AudioProfile Operation for comparing sorted vectors was defined twice in APM code: once in AudioProfile.h as operator==, then in APM as 'vectorsEqual' method. Remove the latter and use the operator instead. Test: make Change-Id: I0697843aabb13539b7b552773d96dbb7fb56daa4 --- .../managerdefinitions/include/AudioProfile.h | 19 ++++++++++++++++++- .../managerdefinitions/src/AudioProfile.cpp | 14 -------------- .../managerdefault/AudioPolicyManager.cpp | 18 ++---------------- .../managerdefault/AudioPolicyManager.h | 3 +-- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h index 4226ff2f7b..194d8ad03e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h @@ -31,7 +31,24 @@ typedef SortedVector ChannelsVector; typedef Vector FormatVector; template -bool operator == (const SortedVector &left, const SortedVector &right); +bool operator== (const SortedVector &left, const SortedVector &right) +{ + if (left.size() != right.size()) { + return false; + } + for (size_t index = 0; index < right.size(); index++) { + if (left[index] != right[index]) { + return false; + } + } + return true; +} + +template +bool operator!= (const SortedVector &left, const SortedVector &right) +{ + return !(left == right); +} class AudioProfile : public virtual RefBase { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index 26af9b42f4..083edf6996 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -56,20 +56,6 @@ status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t ch return BAD_VALUE; } -template -bool operator == (const SortedVector &left, const SortedVector &right) -{ - if (left.size() != right.size()) { - return false; - } - for(size_t index = 0; index < right.size(); index++) { - if (left[index] != right[index]) { - return false; - } - } - return true; -} - bool operator == (const AudioProfile &left, const AudioProfile &compareTo) { return (left.getFormat() == compareTo.getFormat()) && diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index bc9514f4b5..cd33fe1150 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3426,7 +3426,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp& return NO_ERROR; } -status_t AudioPolicyManager::stopAudioSource(audio_patch_handle_t handle __unused) +status_t AudioPolicyManager::stopAudioSource(audio_patch_handle_t handle) { sp sourceDesc = mAudioSources.valueFor(handle); ALOGV("%s handle %d", __FUNCTION__, handle); @@ -4637,20 +4637,6 @@ SortedVector AudioPolicyManager::getOutputsForDevice( return outputs; } -bool AudioPolicyManager::vectorsEqual(SortedVector& outputs1, - SortedVector& outputs2) -{ - if (outputs1.size() != outputs2.size()) { - return false; - } - for (size_t i = 0; i < outputs1.size(); i++) { - if (outputs1[i] != outputs2[i]) { - return false; - } - } - return true; -} - void AudioPolicyManager::checkForDeviceAndOutputChanges() { checkForDeviceAndOutputChanges([](){ return false; }); @@ -4691,7 +4677,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) } } - if (!vectorsEqual(srcOutputs,dstOutputs)) { + if (srcOutputs != dstOutputs) { // get maximum latency of all source outputs to determine the minimum mute time guaranteeing // audio from invalidated tracks will be rendered when unmuting uint32_t maxLatency = 0; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 70ca39f2f1..e412645818 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -459,8 +460,6 @@ protected: SortedVector getOutputsForDevice(audio_devices_t device, const SwAudioOutputCollection& openOutputs); - bool vectorsEqual(SortedVector& outputs1, - SortedVector& outputs2); // mute/unmute strategies using an incompatible device combination // if muting, wait for the audio in pcm buffer to be drained before proceeding -- GitLab From 39ec8d62f75ffcf216714856aeab073c878b0abe Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Tue, 24 Jul 2018 09:49:29 -0700 Subject: [PATCH 0151/1530] audioflinger Threads.cpp: size() == 0 -> isEmpty(). Clean up PerformanceAnalysis.cpp reportPerformance code. Test: build, dumpsys media.audio_flinger Change-Id: I1e89b16872384e589b2060272daa3116184ec4d8 --- media/libnblog/PerformanceAnalysis.cpp | 83 +------------------ .../include/media/nblog/PerformanceAnalysis.h | 2 + .../include/media/nblog/ReportPerformance.h | 3 - services/audioflinger/Threads.cpp | 16 ++-- services/audioflinger/Threads.h | 3 + 5 files changed, 16 insertions(+), 91 deletions(-) diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp index 22a30b982f..3418dc0ef3 100644 --- a/media/libnblog/PerformanceAnalysis.cpp +++ b/media/libnblog/PerformanceAnalysis.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -208,27 +209,6 @@ bool PerformanceAnalysis::detectAndStoreOutlier(const msInterval diffMs) { return isOutlier; } -static int widthOf(int x) { - int width = 0; - if (x < 0) { - width++; - x = x == INT_MIN ? INT_MAX : -x; - } - // assert (x >= 0) - do { - ++width; - x /= 10; - } while (x > 0); - return width; -} - -// computes the column width required for a specific histogram value -inline int numberWidth(double number, int leftPadding) { - // Added values account for whitespaces needed around numbers, and for the - // dot and decimal digit not accounted for by widthOf - return std::max(std::max(widthOf(static_cast(number)) + 3, 2), leftPadding + 1); -} - // rounds value to precision based on log-distance from mean __attribute__((no_sanitize("signed-integer-overflow"))) inline double logRound(double x, double mean) { @@ -281,65 +261,8 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ static_cast(hash), static_cast(startingTs)); static const char * const kLabel = "ms"; - auto it = buckets.begin(); - double maxDelta = it->first; - int maxCount = it->second; - // Compute maximum values - while (++it != buckets.end()) { - if (it->first > maxDelta) { - maxDelta = it->first; - } - if (it->second > maxCount) { - maxCount = it->second; - } - } - int height = log2(maxCount) + 1; // maxCount > 0, safe to call log2 - const int leftPadding = widthOf(1 << height); - const int bucketWidth = numberWidth(maxDelta, leftPadding); - int scalingFactor = 1; - // scale data if it exceeds maximum height - if (height > maxHeight) { - scalingFactor = (height + maxHeight) / maxHeight; - height /= scalingFactor; - } - body->appendFormat("%s", title); - // write histogram label line with bucket values - body->appendFormat("\n%s", " "); - body->appendFormat("%*s", leftPadding, " "); - for (auto const &x : buckets) { - const int colWidth = numberWidth(x.first, leftPadding); - body->appendFormat("%*d", colWidth, x.second); - } - // write histogram ascii art - // underscores and spaces length corresponds to maximum width of histogram - static const int kLen = 200; - static const std::string underscores(kLen, '_'); - static const std::string spaces(kLen, ' '); - - body->appendFormat("\n%s", " "); - for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) { - const int value = 1 << row; - body->appendFormat("%.*s", leftPadding, spaces.c_str()); - for (auto const &x : buckets) { - const int colWidth = numberWidth(x.first, leftPadding); - body->appendFormat("%.*s%s", colWidth - 1, - spaces.c_str(), x.second < value ? " " : "|"); - } - body->appendFormat("\n%s", " "); - } - // print x-axis - const int columns = static_cast(buckets.size()); - body->appendFormat("%*c", leftPadding, ' '); - body->appendFormat("%.*s", (columns + 1) * bucketWidth, underscores.c_str()); - body->appendFormat("\n%s", " "); - - // write footer with bucket labels - body->appendFormat("%*s", leftPadding, " "); - for (auto const &x : buckets) { - const int colWidth = numberWidth(x.first, leftPadding); - body->appendFormat("%*.*f", colWidth, 1, x.first); - } - body->appendFormat("%.*s%s\n", bucketWidth, spaces.c_str(), kLabel); + body->appendFormat("%s", + audio_utils_plot_histogram(buckets, title, kLabel, maxHeight).c_str()); // Now report glitches body->appendFormat("\ntime elapsed between glitches and glitch timestamps:\n"); diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h index ddfe9d67da..56e0ea66ba 100644 --- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h +++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h @@ -25,6 +25,8 @@ namespace android { +class String8; + namespace ReportPerformance { class PerformanceAnalysis; diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h index ec0842fcaf..1b1119758a 100644 --- a/media/libnblog/include/media/nblog/ReportPerformance.h +++ b/media/libnblog/include/media/nblog/ReportPerformance.h @@ -23,9 +23,6 @@ namespace android { -// The String8 class is used by reportPerformance function -class String8; - namespace ReportPerformance { constexpr int kMsPerSec = 1000; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 1e411c7256..d55da1bac1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3354,7 +3354,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() continue; } - if ((!mActiveTracks.size() && systemTime() > mStandbyTimeNs) || + if ((mActiveTracks.isEmpty() && systemTime() > mStandbyTimeNs) || isSuspended()) { // put audio hardware into standby after short delay if (shouldStandby_l()) { @@ -3368,7 +3368,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() mStandby = true; } - if (!mActiveTracks.size() && mConfigEvents.isEmpty()) { + if (mActiveTracks.isEmpty() && mConfigEvents.isEmpty()) { // we're about to wait, flush the binder command buffer IPCThreadState::self()->flushCommands(); @@ -6649,7 +6649,7 @@ reacquire_wakelock: } // sleep if there are no active tracks to process - if (activeTracks.size() == 0) { + if (activeTracks.isEmpty()) { if (sleepUs == 0) { sleepUs = kRecordThreadSleepUs; } @@ -7443,7 +7443,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a audio_input_flags_t flags = input != NULL ? input->flags : AUDIO_INPUT_FLAG_NONE; dprintf(fd, " AudioStreamIn: %p flags %#x (%s)\n", input, flags, inputFlagsToString(flags).c_str()); - if (mActiveTracks.size() == 0) { + if (mActiveTracks.isEmpty()) { dprintf(fd, " No active record clients\n"); } @@ -7909,7 +7909,7 @@ sp AudioFlinger::RecordThread::stream() const status_t AudioFlinger::RecordThread::addEffectChain_l(const sp& chain) { // only one chain per input thread - if (mEffectChains.size() != 0) { + if (!mEffectChains.isEmpty()) { ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this); return INVALID_OPERATION; } @@ -8245,7 +8245,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, // abort if start is rejected by audio policy manager if (ret != NO_ERROR) { ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret); - if (mActiveTracks.size() != 0) { + if (!mActiveTracks.isEmpty()) { mLock.unlock(); if (isOutput()) { AudioSystem::releaseOutput(portId); @@ -8346,7 +8346,7 @@ status_t AudioFlinger::MmapThread::standby() if (mHalStream == 0) { return NO_INIT; } - if (mActiveTracks.size() != 0) { + if (!mActiveTracks.isEmpty()) { return INVALID_OPERATION; } mHalStream->standby(); @@ -8784,7 +8784,7 @@ void AudioFlinger::MmapThread::dumpInternals(int fd, const Vector& arg dprintf(fd, " Attributes: content type %d usage %d source %d\n", mAttr.content_type, mAttr.usage, mAttr.source); dprintf(fd, " Session: %d port Id: %d\n", mSessionId, mPortId); - if (mActiveTracks.size() == 0) { + if (mActiveTracks.isEmpty()) { dprintf(fd, " No active clients\n"); } } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 847a9c6ec6..dc237174bc 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -555,6 +555,9 @@ protected: size_t size() const { return mActiveTracks.size(); } + bool isEmpty() const { + return mActiveTracks.isEmpty(); + } ssize_t indexOf(const sp& item) { return mActiveTracks.indexOf(item); } -- GitLab From 3fcfb3c014335da69e381cef47d0065ef01350e6 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 23 Jul 2018 11:24:05 -0700 Subject: [PATCH 0152/1530] audio policy: remove unused concurrent capture code Remove obsolete concurrent capture logic in audio policy manager given it is not reusable by the new policy being defined for concurrent capture situations. Test: make Change-Id: I2f5d9f13b0e1d00889968ff4e97930eb464b4799 --- .../managerdefault/AudioPolicyManager.cpp | 81 ------------------- 1 file changed, 81 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index fdc8fb45c3..856bcb35bc 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1691,72 +1691,6 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, uid, isSoundTrigger); -// FIXME: disable concurrent capture until UI is ready -#if 0 - // reuse an open input if possible - sp reusedInputDesc; - for (size_t i = 0; i < mInputs.size(); i++) { - sp desc = mInputs.valueAt(i); - // reuse input if: - // - it shares the same profile - // AND - // - it is not a reroute submix input - // AND - // - it is: not used for sound trigger - // OR - // used for sound trigger and all clients use the same session ID - // - if ((profile == desc->mProfile) && - (isSoundTrigger == desc->isSoundTrigger()) && - !is_virtual_input_device(device)) { - - sp as = desc->getAudioSession(session); - if (as != 0) { - // do not allow unmatching properties on same session - if (as->matches(audioSession)) { - as->changeOpenCount(1); - } else { - ALOGW("getInputForDevice() record with different attributes" - " exists for session %d", session); - continue; - } - } else if (isSoundTrigger) { - continue; - } - - // Reuse the already opened input stream on this profile if: - // - the new capture source is background OR - // - the path requested configurations match OR - // - the new source priority is less than the highest source priority on this input - // If the input stream cannot be reused, close it before opening a new stream - // on the same profile for the new client so that the requested path configuration - // can be selected. - if (!isConcurrentSource(inputSource) && - ((desc->mSamplingRate != samplingRate || - desc->mChannelMask != config->channel_mask || - !audio_formats_match(desc->mFormat, config->format)) && - (source_priority(desc->getHighestPrioritySource(false /*activeOnly*/)) < - source_priority(inputSource)))) { - reusedInputDesc = desc; - continue; - } else { - desc->addAudioSession(session, audioSession); - ALOGV("%s: reusing input %d", __FUNCTION__, mInputs.keyAt(i)); - return mInputs.keyAt(i); - } - } - } - - if (reusedInputDesc != 0) { - AudioSessionCollection sessions = reusedInputDesc->getAudioSessions(false /*activeOnly*/); - for (size_t j = 0; j < sessions.size(); j++) { - audio_session_t currentSession = sessions.keyAt(j); - stopInput(reusedInputDesc->mIoHandle, currentSession); - releaseInput(reusedInputDesc->mIoHandle, currentSession); - } - } -#endif - if (!profile->canOpenNewIo()) { return AUDIO_IO_HANDLE_NONE; } @@ -1900,20 +1834,6 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, return BAD_VALUE; } -// FIXME: disable concurrent capture until UI is ready -#if 0 - if (!isConcurentCaptureAllowed(inputDesc, audioSession)) { - ALOGW("startInput(%d) failed: other input already started", input); - return INVALID_OPERATION; - } - - if (isInCall()) { - *concurrency |= API_INPUT_CONCURRENCY_CALL; - } - if (mInputs.activeInputsCountOnDevices() != 0) { - *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; - } -#else if (!is_virtual_input_device(inputDesc->mDevice)) { if (mCallTxPatch != 0 && inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) { @@ -2009,7 +1929,6 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } } } -#endif // Make sure we start with the correct silence state audioSession->setSilenced(silenced); -- GitLab From fa4fd528583279049b334a75e3782c12e832403b Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 20 Jul 2018 18:23:34 -0700 Subject: [PATCH 0153/1530] audio policy: add client descriptors Add client descriptors to keep track of opened AudioTrack and AudioRecord individualy. Client descriptor contains audio attributes, sampling rate, format, channels, uid, session, preferred route etc... Test: make. Change-Id: I785bdf7548670769f463549a3245114ac4c86e41 --- .../common/managerdefinitions/Android.mk | 3 +- .../include/AudioOutputDescriptor.h | 1 - .../include/ClientDescriptor.h | 101 ++++++++++++++++++ .../src/ClientDescriptor.cpp | 78 ++++++++++++++ 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h create mode 100644 services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index b3611c4c0e..09dbb3293d 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -21,7 +21,8 @@ LOCAL_SRC_FILES:= \ src/AudioSourceDescriptor.cpp \ src/VolumeCurve.cpp \ src/TypeConverter.cpp \ - src/AudioSession.cpp + src/AudioSession.cpp \ + src/ClientDescriptor.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 57d1cfa474..292e59fc6f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -17,7 +17,6 @@ #pragma once #include - #include "AudioPort.h" #include #include diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h new file mode 100644 index 0000000000..fb0993214d --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace android { + +class ClientDescriptor: public RefBase +{ +public: + ClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, + audio_attributes_t attributes, audio_config_base_t config, + audio_port_handle_t preferredDeviceId) : + mPortId(portId), mUid(uid), mSessionId(sessionId), mAttributes(attributes), + mConfig(config), mPreferredDeviceId(preferredDeviceId), mActive(false) {} + ~ClientDescriptor() override = default; + + virtual status_t dump(int fd); + + audio_port_handle_t portId() const { return mPortId; } + uid_t uid() const { return mUid; } + audio_session_t session() const { return mSessionId; }; + audio_attributes_t attributes() const { return mAttributes; } + audio_config_base_t config() const { return mConfig; } + audio_port_handle_t preferredDeviceId() const { return mPreferredDeviceId; }; + void setActive(bool active) { mActive = active; } + bool active() const { return mActive; } + +private: + const audio_port_handle_t mPortId; // unique Id for this client + const uid_t mUid; // client UID + const audio_session_t mSessionId; // audio session ID + const audio_attributes_t mAttributes; // usage... + const audio_config_base_t mConfig; + const audio_port_handle_t mPreferredDeviceId; // selected input device port ID + bool mActive; +}; + +class TrackClientDescriptor: public ClientDescriptor +{ +public: + TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, + audio_attributes_t attributes, audio_config_base_t config, + audio_port_handle_t preferredDeviceId, + audio_stream_type_t stream, audio_output_flags_t flags) : + ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), + mStream(stream), mFlags(flags) {} + ~TrackClientDescriptor() override = default; + + status_t dump(int fd) override; + + audio_output_flags_t flags() const { return mFlags; } + audio_stream_type_t stream() const { return mStream; } + +private: + const audio_stream_type_t mStream; + const audio_output_flags_t mFlags; +}; + +class RecordClientDescriptor: public ClientDescriptor +{ +public: + RecordClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, + audio_attributes_t attributes, audio_config_base_t config, + audio_port_handle_t preferredDeviceId, + audio_source_t source, audio_input_flags_t flags) : + ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), + mSource(source), mFlags(flags) {} + ~RecordClientDescriptor() override = default; + + status_t dump(int fd) override; + + audio_source_t source() const { return mSource; } + audio_input_flags_t flags() const { return mFlags; } + +private: + const audio_source_t mSource; + const audio_input_flags_t mFlags; +}; + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp new file mode 100644 index 0000000000..1e64d2e361 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -0,0 +1,78 @@ +/* + * 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 "APM_ClientDescriptor" +//#define LOG_NDEBUG 0 + +#include +#include "ClientDescriptor.h" + +namespace android { + +status_t ClientDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Port ID: %d Session Id: %d UID: %d\n", mPortId, mSessionId, mUid); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %08x Sampling rate: %d Channels: %08x\n", + mConfig.format, mConfig.sample_rate, mConfig.channel_mask); + result.append(buffer); + snprintf(buffer, SIZE, " Preferred Device Id: %08x\n", mPreferredDeviceId); + result.append(buffer); + snprintf(buffer, SIZE, " State: %s\n", mActive ? "Active" : "Inactive"); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +status_t TrackClientDescriptor::dump(int fd) +{ + ClientDescriptor::dump(fd); + + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Stream: %d flags: %08x\n", mStream, mFlags); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +status_t RecordClientDescriptor::dump(int fd) +{ + ClientDescriptor::dump(fd); + + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " Source: %d flags: %08x\n", mSource, mFlags); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +}; //namespace android -- GitLab From 5a7913b405ac893998591fbad1242d7d9b7b2a62 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 24 Jul 2018 12:35:06 -0700 Subject: [PATCH 0154/1530] Add utility functions to AudioProfile Added AudioProfileVector::getFirstValidProfileFor for retrieving first valid profile supporting the given format. Added ChannelsVector::as{In|Out}Mask for reversing direction on all contained channel masks. Bug: 63901775 Test: make Change-Id: Ib4a816abd9a3904a552f670ee75266e7dd890011 --- .../managerdefinitions/include/AudioProfile.h | 26 ++++++++++++++++++- .../managerdefinitions/src/AudioProfile.cpp | 22 ++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h index 194d8ad03e..d2fc6a332b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h @@ -27,7 +27,6 @@ namespace android { typedef SortedVector SampleRateVector; -typedef SortedVector ChannelsVector; typedef Vector FormatVector; template @@ -50,6 +49,21 @@ bool operator!= (const SortedVector &left, const SortedVector &right) return !(left == right); } +class ChannelsVector : public SortedVector +{ +public: + ChannelsVector() = default; + ChannelsVector(const ChannelsVector&) = default; + ChannelsVector(const SortedVector& sv) : + SortedVector(sv) {} + ChannelsVector& operator=(const ChannelsVector&) = default; + + // Applies audio_channel_mask_out_to_in to all elements and returns the result. + ChannelsVector asInMask() const; + // Applies audio_channel_mask_in_to_out to all elements and returns the result. + ChannelsVector asOutMask() const; +}; + class AudioProfile : public virtual RefBase { public: @@ -211,6 +225,16 @@ public: return 0; } + sp getFirstValidProfileFor(audio_format_t format) const + { + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->isValid() && itemAt(i)->getFormat() == format) { + return itemAt(i); + } + } + return 0; + } + bool hasValidProfile() const { return getFirstValidProfile() != 0; } status_t checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask, diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index 083edf6996..e8fe5ff0e5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -28,6 +28,28 @@ namespace android { +ChannelsVector ChannelsVector::asInMask() const +{ + ChannelsVector inMaskVector; + for (const auto& channel : *this) { + if (audio_channel_mask_out_to_in(channel) != AUDIO_CHANNEL_INVALID) { + inMaskVector.add(audio_channel_mask_out_to_in(channel)); + } + } + return inMaskVector; +} + +ChannelsVector ChannelsVector::asOutMask() const +{ + ChannelsVector outMaskVector; + for (const auto& channel : *this) { + if (audio_channel_mask_in_to_out(channel) != AUDIO_CHANNEL_INVALID) { + outMaskVector.add(audio_channel_mask_in_to_out(channel)); + } + } + return outMaskVector; +} + static AudioProfile* createFullDynamicImpl() { AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat, -- GitLab From 86325527d409779b1c5392f4dcc2adeb7c906adc Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 26 Jul 2018 10:01:19 -0700 Subject: [PATCH 0155/1530] Camera: skip unnecessary reconfig Test: manual testing 3rd party camera app + hacked walleye Bug: 111850901 Change-Id: Ia2977a290bb0bd706f500a3d39e0d996d50cbe1e --- .../libcameraservice/api1/Camera2Client.cpp | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index d59b313289..c8b3c2ff29 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -780,33 +780,35 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { int lastJpegStreamId = mJpegProcessor->getStreamId(); // If jpeg stream will slow down preview, make sure we remove it before starting preview if (params.slowJpegMode) { - // Pause preview if we are streaming - int32_t activeRequestId = mStreamingProcessor->getActiveRequestId(); - if (activeRequestId != 0) { - res = mStreamingProcessor->togglePauseStream(/*pause*/true); - if (res != OK) { - ALOGE("%s: Camera %d: Can't pause streaming: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); - } - res = mDevice->waitUntilDrained(); - if (res != OK) { - ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); + if (lastJpegStreamId != NO_STREAM) { + // Pause preview if we are streaming + int32_t activeRequestId = mStreamingProcessor->getActiveRequestId(); + if (activeRequestId != 0) { + res = mStreamingProcessor->togglePauseStream(/*pause*/true); + if (res != OK) { + ALOGE("%s: Camera %d: Can't pause streaming: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } } - } - - res = mJpegProcessor->deleteStream(); - if (res != OK) { - ALOGE("%s: Camera %d: delete Jpeg stream failed: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); - } + res = mJpegProcessor->deleteStream(); - if (activeRequestId != 0) { - res = mStreamingProcessor->togglePauseStream(/*pause*/false); if (res != OK) { - ALOGE("%s: Camera %d: Can't unpause streaming: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); + ALOGE("%s: Camera %d: delete Jpeg stream failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + + if (activeRequestId != 0) { + res = mStreamingProcessor->togglePauseStream(/*pause*/false); + if (res != OK) { + ALOGE("%s: Camera %d: Can't unpause streaming: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } } } } else { -- GitLab From 9366193e6ba8e2c554de747a3ab58a336297c067 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 26 Jul 2018 14:37:41 -0700 Subject: [PATCH 0156/1530] audiopolicy: Refactor DeviceVector::getDevicesFromHwModule Rename method audio_devices_t DeviceVector::getDevicesFromHwModule into getDeviceTypesFromHwModule. Introduce method DeviceVector DeviceVector::getDevicesFromHwModule. Bug: 63901775 Test: make Change-Id: Ieec0c116fa0a6ff20241d61f65202a8eaf9f0674 --- .../include/DeviceDescriptor.h | 4 ++-- .../src/DeviceDescriptor.cpp | 21 ++++++++++++++----- .../audiopolicy/enginedefault/src/Engine.cpp | 7 +++---- .../managerdefault/AudioPolicyManager.h | 3 ++- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index 2325e4f574..c08e752717 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -82,8 +82,8 @@ public: DeviceVector getDevicesFromTypeMask(audio_devices_t types) const; sp getDeviceFromId(audio_port_handle_t id) const; sp getDeviceFromTagName(const String8 &tagName) const; - - audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; + DeviceVector getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; + audio_devices_t getDeviceTypesFromHwModule(audio_module_handle_t moduleHandle) const; status_t dump(int fd, const String8 &tag, int spaces = 0, bool verbose = true) const; diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index d3cc8b90bb..1638645319 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -121,17 +121,28 @@ ssize_t DeviceVector::remove(const sp& item) return ret; } -audio_devices_t DeviceVector::getDevicesFromHwModule(audio_module_handle_t moduleHandle) const +DeviceVector DeviceVector::getDevicesFromHwModule(audio_module_handle_t moduleHandle) const { - audio_devices_t devices = AUDIO_DEVICE_NONE; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->getModuleHandle() == moduleHandle) { - devices |= itemAt(i)->type(); + DeviceVector devices; + for (const auto& device : *this) { + if (device->getModuleHandle() == moduleHandle) { + devices.add(device); } } return devices; } +audio_devices_t DeviceVector::getDeviceTypesFromHwModule(audio_module_handle_t moduleHandle) const +{ + audio_devices_t deviceTypes = AUDIO_DEVICE_NONE; + for (const auto& device : *this) { + if (device->getModuleHandle() == moduleHandle) { + deviceTypes |= device->type(); + } + } + return deviceTypes; +} + sp DeviceVector::getDevice(audio_devices_t type, const String8& address) const { sp device; diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index ca64d865d0..30f275fb6c 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -313,7 +313,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); sp primaryOutput = outputs.getPrimaryOutput(); audio_devices_t availPrimaryInputDevices = - availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle()); + availableInputDevices.getDeviceTypesFromHwModule(primaryOutput->getModuleHandle()); // TODO: getPrimaryOutput return only devices from first module in // audio_policy_configuration.xml, hearing aid is not there, but it's @@ -668,9 +668,8 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons if ((getPhoneState() == AUDIO_MODE_IN_CALL) && (availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { sp primaryOutput = outputs.getPrimaryOutput(); - availableDeviceTypes = - availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle()) - & ~AUDIO_DEVICE_BIT_IN; + availableDeviceTypes = availableInputDevices.getDeviceTypesFromHwModule( + primaryOutput->getModuleHandle()) & ~AUDIO_DEVICE_BIT_IN; } switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index e412645818..48aadc96ba 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -508,7 +508,8 @@ protected: if (!hasPrimaryOutput()) { return AUDIO_DEVICE_NONE; } - return mAvailableInputDevices.getDevicesFromHwModule(mPrimaryOutput->getModuleHandle()); + return mAvailableInputDevices.getDeviceTypesFromHwModule( + mPrimaryOutput->getModuleHandle()); } uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0); -- GitLab From e50f628034f4b0b5a87f8e9f9eb3c170eef62955 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 26 Jul 2018 16:20:43 -0700 Subject: [PATCH 0157/1530] audio policy: Refactor AudioProfile * Move non-trivial method definitions into .cpp file; * Re-organize method definitions by name and by functionality; * Update and sort includes. The code of the methods left unchanged. Bug: 63901775 Test: make Change-Id: Ic1b1c456983a23f078947dd84a837dc194bd48cc --- .../managerdefinitions/include/AudioProfile.h | 283 ++---------------- .../managerdefinitions/src/AudioProfile.cpp | 269 ++++++++++++++++- 2 files changed, 285 insertions(+), 267 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h index d2fc6a332b..31c75a4b41 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h @@ -16,13 +16,12 @@ #pragma once -#include "policy.h" -#include -#include -#include -#include #include -#include +#include +#include +#include + +#include "policy.h" namespace android { @@ -69,71 +68,30 @@ class AudioProfile : public virtual RefBase public: static sp createFullDynamic(); - AudioProfile(audio_format_t format, - audio_channel_mask_t channelMasks, - uint32_t samplingRate) : - mName(String8("")), - mFormat(format) - { - mChannelMasks.add(channelMasks); - mSamplingRates.add(samplingRate); - } - + AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate); AudioProfile(audio_format_t format, const ChannelsVector &channelMasks, - const SampleRateVector &samplingRateCollection) : - mName(String8("")), - mFormat(format), - mChannelMasks(channelMasks), - mSamplingRates(samplingRateCollection) - {} + const SampleRateVector &samplingRateCollection); audio_format_t getFormat() const { return mFormat; } - - void setChannels(const ChannelsVector &channelMasks) - { - if (mIsDynamicChannels) { - mChannelMasks = channelMasks; - } - } const ChannelsVector &getChannels() const { return mChannelMasks; } - - void setSampleRates(const SampleRateVector &sampleRates) - { - if (mIsDynamicRate) { - mSamplingRates = sampleRates; - } - } const SampleRateVector &getSampleRates() const { return mSamplingRates; } + void setChannels(const ChannelsVector &channelMasks); + void setSampleRates(const SampleRateVector &sampleRates); + void clear(); bool isValid() const { return hasValidFormat() && hasValidRates() && hasValidChannels(); } - - void clear() - { - if (mIsDynamicChannels) { - mChannelMasks.clear(); - } - if (mIsDynamicRate) { - mSamplingRates.clear(); - } - } - - inline bool supportsChannels(audio_channel_mask_t channels) const + bool supportsChannels(audio_channel_mask_t channels) const { return mChannelMasks.indexOf(channels) >= 0; } - inline bool supportsRate(uint32_t rate) const - { - return mSamplingRates.indexOf(rate) >= 0; - } + bool supportsRate(uint32_t rate) const { return mSamplingRates.indexOf(rate) >= 0; } status_t checkExact(uint32_t rate, audio_channel_mask_t channels, audio_format_t format) const; - status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask, audio_channel_mask_t &updatedChannelMask, audio_port_type_t portType, audio_port_role_t portRole) const; - status_t checkCompatibleSamplingRate(uint32_t samplingRate, uint32_t &updatedSamplingRate) const; @@ -169,223 +127,38 @@ private: class AudioProfileVector : public Vector > { public: - ssize_t add(const sp &profile) - { - ssize_t index = Vector::add(profile); - // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry. - // TODO: compareFormats could be a lambda to convert between pointer-to-format to format: - // [](const audio_format_t *format1, const audio_format_t *format2) { - // return compareFormats(*format1, *format2); - // } - sort(compareFormats); - return index; - } - + ssize_t add(const sp &profile); // This API is intended to be used by the policy manager once retrieving capabilities // for a profile with dynamic format, rate and channels attributes - ssize_t addProfileFromHal(const sp &profileToAdd) - { - // Check valid profile to add: - if (!profileToAdd->hasValidFormat()) { - return -1; - } - if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) { - FormatVector formats; - formats.add(profileToAdd->getFormat()); - setFormats(FormatVector(formats)); - return 0; - } - if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) { - setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat()); - return 0; - } - if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) { - setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat()); - return 0; - } - // Go through the list of profile to avoid duplicates - for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) { - const sp &profile = itemAt(profileIndex); - if (profile->isValid() && profile == profileToAdd) { - // Nothing to do - return profileIndex; - } - } - profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal - return add(profileToAdd); - } - - sp getFirstValidProfile() const - { - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->isValid()) { - return itemAt(i); - } - } - return 0; - } - - sp getFirstValidProfileFor(audio_format_t format) const - { - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->isValid() && itemAt(i)->getFormat() == format) { - return itemAt(i); - } - } - return 0; - } - - bool hasValidProfile() const { return getFirstValidProfile() != 0; } + ssize_t addProfileFromHal(const sp &profileToAdd); status_t checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask, audio_format_t format) const; - status_t checkCompatibleProfile(uint32_t &samplingRate, audio_channel_mask_t &channelMask, audio_format_t &format, audio_port_type_t portType, audio_port_role_t portRole) const; + void clearProfiles(); - FormatVector getSupportedFormats() const - { - FormatVector supportedFormats; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->hasValidFormat()) { - supportedFormats.add(itemAt(i)->getFormat()); - } - } - return supportedFormats; - } - - bool hasDynamicProfile() const - { - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->isDynamic()) { - return true; - } - } - return false; - } - - bool hasDynamicFormat() const - { - return getProfileFor(gDynamicFormat) != 0; - } - - bool hasDynamicChannelsFor(audio_format_t format) const - { - for (size_t i = 0; i < size(); i++) { - sp profile = itemAt(i); - if (profile->getFormat() == format && profile->isDynamicChannels()) { - return true; - } - } - return false; - } + sp getFirstValidProfile() const; + sp getFirstValidProfileFor(audio_format_t format) const; + bool hasValidProfile() const { return getFirstValidProfile() != 0; } - bool hasDynamicRateFor(audio_format_t format) const - { - for (size_t i = 0; i < size(); i++) { - sp profile = itemAt(i); - if (profile->getFormat() == format && profile->isDynamicRate()) { - return true; - } - } - return false; - } + FormatVector getSupportedFormats() const; + bool hasDynamicChannelsFor(audio_format_t format) const; + bool hasDynamicFormat() const { return getProfileFor(gDynamicFormat) != 0; } + bool hasDynamicProfile() const; + bool hasDynamicRateFor(audio_format_t format) const; // One audio profile will be added for each format supported by Audio HAL - void setFormats(const FormatVector &formats) - { - // Only allow to change the format of dynamic profile - sp dynamicFormatProfile = getProfileFor(gDynamicFormat); - if (dynamicFormatProfile == 0) { - return; - } - for (size_t i = 0; i < formats.size(); i++) { - sp profile = new AudioProfile(formats[i], - dynamicFormatProfile->getChannels(), - dynamicFormatProfile->getSampleRates()); - profile->setDynamicFormat(true); - profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels()); - profile->setDynamicRate(dynamicFormatProfile->isDynamicRate()); - add(profile); - } - } - - void clearProfiles() - { - for (size_t i = size(); i != 0; ) { - sp profile = itemAt(--i); - if (profile->isDynamicFormat() && profile->hasValidFormat()) { - removeAt(i); - continue; - } - profile->clear(); - } - } + void setFormats(const FormatVector &formats); - void dump(int fd, int spaces) const - { - const size_t SIZE = 256; - char buffer[SIZE]; - - snprintf(buffer, SIZE, "%*s- Profiles:\n", spaces, ""); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < size(); i++) { - snprintf(buffer, SIZE, "%*sProfile %zu:", spaces + 4, "", i); - write(fd, buffer, strlen(buffer)); - itemAt(i)->dump(fd, spaces + 8); - } - } + void dump(int fd, int spaces) const; private: - void setSampleRatesFor(const SampleRateVector &sampleRates, audio_format_t format) - { - for (size_t i = 0; i < size(); i++) { - sp profile = itemAt(i); - if (profile->getFormat() == format && profile->isDynamicRate()) { - if (profile->hasValidRates()) { - // Need to create a new profile with same format - sp profileToAdd = new AudioProfile(format, profile->getChannels(), - sampleRates); - profileToAdd->setDynamicFormat(true); // need to set to allow cleaning - add(profileToAdd); - } else { - profile->setSampleRates(sampleRates); - } - return; - } - } - } - - void setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format) - { - for (size_t i = 0; i < size(); i++) { - sp profile = itemAt(i); - if (profile->getFormat() == format && profile->isDynamicChannels()) { - if (profile->hasValidChannels()) { - // Need to create a new profile with same format - sp profileToAdd = new AudioProfile(format, channelMasks, - profile->getSampleRates()); - profileToAdd->setDynamicFormat(true); // need to set to allow cleaning - add(profileToAdd); - } else { - profile->setChannels(channelMasks); - } - return; - } - } - } - - sp getProfileFor(audio_format_t format) const - { - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->getFormat() == format) { - return itemAt(i); - } - } - return 0; - } + sp getProfileFor(audio_format_t format) const; + void setSampleRatesFor(const SampleRateVector &sampleRates, audio_format_t format); + void setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format); static int compareFormats(const sp *profile1, const sp *profile2); }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index e8fe5ff0e5..b76b609628 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -14,17 +14,19 @@ * limitations under the License. */ +#include + #define LOG_TAG "APM::AudioProfile" //#define LOG_NDEBUG 0 -#include "AudioProfile.h" +#include +#include + +#include "AudioGain.h" #include "AudioPort.h" +#include "AudioProfile.h" #include "HwModule.h" -#include "AudioGain.h" -#include #include "TypeConverter.h" -#include -#include namespace android { @@ -50,6 +52,13 @@ ChannelsVector ChannelsVector::asOutMask() const return outMaskVector; } +bool operator == (const AudioProfile &left, const AudioProfile &compareTo) +{ + return (left.getFormat() == compareTo.getFormat()) && + (left.getChannels() == compareTo.getChannels()) && + (left.getSampleRates() == compareTo.getSampleRates()); +} + static AudioProfile* createFullDynamicImpl() { AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat, @@ -67,6 +76,48 @@ sp AudioProfile::createFullDynamic() return dynamicProfile; } +AudioProfile::AudioProfile(audio_format_t format, + audio_channel_mask_t channelMasks, + uint32_t samplingRate) : + mName(String8("")), + mFormat(format) +{ + mChannelMasks.add(channelMasks); + mSamplingRates.add(samplingRate); +} + +AudioProfile::AudioProfile(audio_format_t format, + const ChannelsVector &channelMasks, + const SampleRateVector &samplingRateCollection) : + mName(String8("")), + mFormat(format), + mChannelMasks(channelMasks), + mSamplingRates(samplingRateCollection) {} + +void AudioProfile::setChannels(const ChannelsVector &channelMasks) +{ + if (mIsDynamicChannels) { + mChannelMasks = channelMasks; + } +} + +void AudioProfile::setSampleRates(const SampleRateVector &sampleRates) +{ + if (mIsDynamicRate) { + mSamplingRates = sampleRates; + } +} + +void AudioProfile::clear() +{ + if (mIsDynamicChannels) { + mChannelMasks.clear(); + } + if (mIsDynamicRate) { + mSamplingRates.clear(); + } +} + status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask, audio_format_t format) const { @@ -78,13 +129,6 @@ status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t ch return BAD_VALUE; } -bool operator == (const AudioProfile &left, const AudioProfile &compareTo) -{ - return (left.getFormat() == compareTo.getFormat()) && - (left.getChannels() == compareTo.getChannels()) && - (left.getSampleRates() == compareTo.getSampleRates()); -} - status_t AudioProfile::checkCompatibleSamplingRate(uint32_t samplingRate, uint32_t &updatedSamplingRate) const { @@ -250,6 +294,50 @@ void AudioProfile::dump(int fd, int spaces) const write(fd, result.string(), result.size()); } +ssize_t AudioProfileVector::add(const sp &profile) +{ + ssize_t index = Vector::add(profile); + // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry. + // TODO: compareFormats could be a lambda to convert between pointer-to-format to format: + // [](const audio_format_t *format1, const audio_format_t *format2) { + // return compareFormats(*format1, *format2); + // } + sort(compareFormats); + return index; +} + +ssize_t AudioProfileVector::addProfileFromHal(const sp &profileToAdd) +{ + // Check valid profile to add: + if (!profileToAdd->hasValidFormat()) { + return -1; + } + if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) { + FormatVector formats; + formats.add(profileToAdd->getFormat()); + setFormats(FormatVector(formats)); + return 0; + } + if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) { + setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat()); + return 0; + } + if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) { + setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat()); + return 0; + } + // Go through the list of profile to avoid duplicates + for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) { + const sp &profile = itemAt(profileIndex); + if (profile->isValid() && profile == profileToAdd) { + // Nothing to do + return profileIndex; + } + } + profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal + return add(profileToAdd); +} + status_t AudioProfileVector::checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask, audio_format_t format) const @@ -306,6 +394,163 @@ status_t AudioProfileVector::checkCompatibleProfile(uint32_t &samplingRate, return BAD_VALUE; } +void AudioProfileVector::clearProfiles() +{ + for (size_t i = size(); i != 0; ) { + sp profile = itemAt(--i); + if (profile->isDynamicFormat() && profile->hasValidFormat()) { + removeAt(i); + continue; + } + profile->clear(); + } +} + +sp AudioProfileVector::getFirstValidProfile() const +{ + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->isValid()) { + return itemAt(i); + } + } + return 0; +} + +sp AudioProfileVector::getFirstValidProfileFor(audio_format_t format) const +{ + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->isValid() && itemAt(i)->getFormat() == format) { + return itemAt(i); + } + } + return 0; +} + +FormatVector AudioProfileVector::getSupportedFormats() const +{ + FormatVector supportedFormats; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->hasValidFormat()) { + supportedFormats.add(itemAt(i)->getFormat()); + } + } + return supportedFormats; +} + +bool AudioProfileVector::hasDynamicChannelsFor(audio_format_t format) const +{ + for (size_t i = 0; i < size(); i++) { + sp profile = itemAt(i); + if (profile->getFormat() == format && profile->isDynamicChannels()) { + return true; + } + } + return false; +} + +bool AudioProfileVector::hasDynamicProfile() const +{ + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->isDynamic()) { + return true; + } + } + return false; +} + +bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const +{ + for (size_t i = 0; i < size(); i++) { + sp profile = itemAt(i); + if (profile->getFormat() == format && profile->isDynamicRate()) { + return true; + } + } + return false; +} + +void AudioProfileVector::setFormats(const FormatVector &formats) +{ + // Only allow to change the format of dynamic profile + sp dynamicFormatProfile = getProfileFor(gDynamicFormat); + if (dynamicFormatProfile == 0) { + return; + } + for (size_t i = 0; i < formats.size(); i++) { + sp profile = new AudioProfile(formats[i], + dynamicFormatProfile->getChannels(), + dynamicFormatProfile->getSampleRates()); + profile->setDynamicFormat(true); + profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels()); + profile->setDynamicRate(dynamicFormatProfile->isDynamicRate()); + add(profile); + } +} + +void AudioProfileVector::dump(int fd, int spaces) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "%*s- Profiles:\n", spaces, ""); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, "%*sProfile %zu:", spaces + 4, "", i); + write(fd, buffer, strlen(buffer)); + itemAt(i)->dump(fd, spaces + 8); + } +} + +sp AudioProfileVector::getProfileFor(audio_format_t format) const +{ + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->getFormat() == format) { + return itemAt(i); + } + } + return 0; +} + +void AudioProfileVector::setSampleRatesFor( + const SampleRateVector &sampleRates, audio_format_t format) +{ + for (size_t i = 0; i < size(); i++) { + sp profile = itemAt(i); + if (profile->getFormat() == format && profile->isDynamicRate()) { + if (profile->hasValidRates()) { + // Need to create a new profile with same format + sp profileToAdd = new AudioProfile(format, profile->getChannels(), + sampleRates); + profileToAdd->setDynamicFormat(true); // need to set to allow cleaning + add(profileToAdd); + } else { + profile->setSampleRates(sampleRates); + } + return; + } + } +} + +void AudioProfileVector::setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format) +{ + for (size_t i = 0; i < size(); i++) { + sp profile = itemAt(i); + if (profile->getFormat() == format && profile->isDynamicChannels()) { + if (profile->hasValidChannels()) { + // Need to create a new profile with same format + sp profileToAdd = new AudioProfile(format, channelMasks, + profile->getSampleRates()); + profileToAdd->setDynamicFormat(true); // need to set to allow cleaning + add(profileToAdd); + } else { + profile->setChannels(channelMasks); + } + return; + } + } +} + +// static int AudioProfileVector::compareFormats(const sp *profile1, const sp *profile2) { -- GitLab From 8fc147b21a7627eeda6383ae501add4dc903634e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Sun, 22 Jul 2018 19:13:55 -0700 Subject: [PATCH 0158/1530] audio policy: use client descriptors First step in implementing use of ClientDescriptor class to keep track of playback and capture activity: - update AudioPolicyInterface methods to just use port ID as client identifier - add list of clients in input and output descriptors and update list when clients are added and removed. - list clients in audio policy manager dump. Test: Audio smoke tests. CTS tests for AudioTrack, AudioRecord, routing, recording and playback configurations Change-Id: Ia0f62f295d421fb845d57afcaa0ce77cd2c58775 --- services/audiopolicy/AudioPolicyInterface.h | 21 +- .../include/AudioInputDescriptor.h | 10 +- .../include/AudioOutputDescriptor.h | 12 +- .../include/ClientDescriptor.h | 17 +- .../src/AudioInputDescriptor.cpp | 32 +++ .../src/AudioOutputDescriptor.cpp | 19 ++ .../src/ClientDescriptor.cpp | 58 ++--- .../managerdefault/AudioPolicyManager.cpp | 219 +++++++++++------- .../managerdefault/AudioPolicyManager.h | 25 +- .../service/AudioPolicyInterfaceImpl.cpp | 20 +- 10 files changed, 263 insertions(+), 170 deletions(-) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index fe4948368d..c9e99d5cba 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -124,17 +124,11 @@ public: audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId) = 0; // indicates to the audio policy manager that the output starts being used by corresponding stream. - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; + virtual status_t startOutput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the output stops being used by corresponding stream. - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; + virtual status_t stopOutput(audio_port_handle_t portId) = 0; // releases the output. - virtual void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) = 0; + virtual void releaseOutput(audio_port_handle_t portId) = 0; // request an input appropriate for record from the supplied device with supplied parameters. virtual status_t getInputForAttr(const audio_attributes_t *attr, @@ -147,16 +141,13 @@ public: input_type_t *inputType, audio_port_handle_t *portId) = 0; // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_io_handle_t input, - audio_session_t session, + virtual status_t startInput(audio_port_handle_t portId, bool silenced, concurrency_type__mask_t *concurrency) = 0; // indicates to the audio policy manager that the input stops being used. - virtual status_t stopInput(audio_io_handle_t input, - audio_session_t session) = 0; + virtual status_t stopInput(audio_port_handle_t portId) = 0; // releases the input. - virtual void releaseInput(audio_io_handle_t input, - audio_session_t session) = 0; + virtual void releaseInput(audio_port_handle_t portId) = 0; // // volume control functions diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index ca837c421a..44662e5f67 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -19,6 +19,7 @@ #include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "AudioSession.h" +#include "ClientDescriptor.h" #include #include #include @@ -88,7 +89,10 @@ public: void stop(); void close(); -private: + RecordClientMap& clients() { return mClients; } + RecordClientVector getClientsForSession(audio_session_t session); + + private: void updateSessionRecordingConfiguration(int event, const sp& audioSession); @@ -105,6 +109,8 @@ private: SortedVector mPreemptedSessions; AudioPolicyClientInterface *mClientInterface; uint32_t mGlobalRefCount; // non-session-specific ref count + + RecordClientMap mClients; }; class AudioInputCollection : @@ -128,6 +134,8 @@ public: audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; + sp getInputForClient(audio_port_handle_t portId); + status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 292e59fc6f..e6112bfb24 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -17,14 +17,15 @@ #pragma once #include -#include "AudioPort.h" -#include #include #include #include #include +#include #include "AudioIODescriptorInterface.h" +#include "AudioPort.h" #include "AudioSourceDescriptor.h" +#include "ClientDescriptor.h" namespace android { @@ -78,7 +79,9 @@ public: audio_patch_handle_t getPatchHandle() const override; void setPatchHandle(audio_patch_handle_t handle) override; - sp mPort; + TrackClientMap& clients() { return mClients; } + + sp mPort; audio_devices_t mDevice; // current device this output is routed to uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output nsecs_t mStopTime[AUDIO_STREAM_CNT]; @@ -91,6 +94,7 @@ public: protected: audio_patch_handle_t mPatchHandle; audio_port_handle_t mId; + TrackClientMap mClients; }; // Audio output driven by a software mixer in audio flinger. @@ -226,6 +230,8 @@ public: audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; + sp getOutputForClient(audio_port_handle_t portId); + status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index fb0993214d..221c2e96ee 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -16,12 +16,15 @@ #pragma once +#include +#include #include #include #include #include #include +#include namespace android { @@ -35,7 +38,8 @@ public: mConfig(config), mPreferredDeviceId(preferredDeviceId), mActive(false) {} ~ClientDescriptor() override = default; - virtual status_t dump(int fd); + status_t dump(int fd, int spaces, int index); + virtual status_t dump(String8& dst, int spaces, int index); audio_port_handle_t portId() const { return mPortId; } uid_t uid() const { return mUid; } @@ -67,7 +71,8 @@ public: mStream(stream), mFlags(flags) {} ~TrackClientDescriptor() override = default; - status_t dump(int fd) override; + using ClientDescriptor::dump; + status_t dump(String8& dst, int spaces, int index) override; audio_output_flags_t flags() const { return mFlags; } audio_stream_type_t stream() const { return mStream; } @@ -88,7 +93,8 @@ public: mSource(source), mFlags(flags) {} ~RecordClientDescriptor() override = default; - status_t dump(int fd) override; + using ClientDescriptor::dump; + status_t dump(String8& dst, int spaces, int index) override; audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } @@ -98,4 +104,9 @@ private: const audio_input_flags_t mFlags; }; +typedef std::vector< sp > TrackClientVector; +typedef std::map< audio_port_handle_t, sp > TrackClientMap; +typedef std::vector< sp > RecordClientVector; +typedef std::map< audio_port_handle_t, sp > RecordClientMap; + } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index b9895a94b5..2770e74341 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -340,6 +340,18 @@ void AudioInputDescriptor::updateSessionRecordingConfiguration( &config, mPatchHandle); } +RecordClientVector AudioInputDescriptor::getClientsForSession( + audio_session_t session) +{ + RecordClientVector clients; + for (const auto &client : mClients) { + if (client.second->session() == session) { + clients.push_back(client.second); + } + } + return clients; +} + status_t AudioInputDescriptor::dump(int fd) { const size_t SIZE = 256; @@ -361,6 +373,13 @@ status_t AudioInputDescriptor::dump(int fd) mSessions.dump(fd, 1); + size_t index = 0; + result = " AudioRecord clients:\n"; + for (const auto& client: mClients) { + client.second->dump(result, 2, index++); + } + result.append(" \n"); + write(fd, result.string(), result.size()); return NO_ERROR; } @@ -423,6 +442,19 @@ audio_devices_t AudioInputCollection::getSupportedDevices(audio_io_handle_t hand return devices; } +sp AudioInputCollection::getInputForClient(audio_port_handle_t portId) +{ + for (size_t i = 0; i < size(); i++) { + sp inputDesc = valueAt(i); + for (const auto& client : inputDesc->clients()) { + if (client.second->portId() == portId) { + return inputDesc; + } + } + } + return 0; +} + status_t AudioInputCollection::dump(int fd) const { const size_t SIZE = 256; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 3c69de5ba9..3dfbe1b111 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -224,6 +224,13 @@ status_t AudioOutputDescriptor::dump(int fd) i, mCurVolume[i], mRefCount[i], mMuteCount[i]); result.append(buffer); } + + result.append(" AudioTrack clients:\n"); + size_t index = 0; + for (const auto& client : mClients) { + client.second->dump(result, 2, index++); + } + result.append(" \n"); write(fd, result.string(), result.size()); return NO_ERROR; @@ -731,6 +738,18 @@ audio_devices_t SwAudioOutputCollection::getSupportedDevices(audio_io_handle_t h return devices; } +sp SwAudioOutputCollection::getOutputForClient(audio_port_handle_t portId) +{ + for (size_t i = 0; i < size(); i++) { + sp outputDesc = valueAt(i); + for (const auto& client : outputDesc->clients()) { + if (client.second->portId() == portId) { + return outputDesc; + } + } + } + return 0; +} status_t SwAudioOutputCollection::dump(int fd) const { diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 1e64d2e361..bdc748ebd7 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -17,60 +17,50 @@ #define LOG_TAG "APM_ClientDescriptor" //#define LOG_NDEBUG 0 +#include #include #include "ClientDescriptor.h" namespace android { -status_t ClientDescriptor::dump(int fd) +status_t ClientDescriptor::dump(int fd, int spaces, int index) { - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; + String8 out; - snprintf(buffer, SIZE, " Port ID: %d Session Id: %d UID: %d\n", mPortId, mSessionId, mUid); - result.append(buffer); - snprintf(buffer, SIZE, " Format: %08x Sampling rate: %d Channels: %08x\n", - mConfig.format, mConfig.sample_rate, mConfig.channel_mask); - result.append(buffer); - snprintf(buffer, SIZE, " Preferred Device Id: %08x\n", mPreferredDeviceId); - result.append(buffer); - snprintf(buffer, SIZE, " State: %s\n", mActive ? "Active" : "Inactive"); - result.append(buffer); + status_t status = dump(out, spaces, index); + if (status == NO_ERROR) { + write(fd, out.string(), out.size()); + } - write(fd, result.string(), result.size()); + return status; +} +status_t ClientDescriptor::dump(String8& out, int spaces, int index) +{ + out.appendFormat("%*sClient %d:\n", spaces, "", index+1); + out.appendFormat("%*s- Port ID: %d Session Id: %d UID: %d\n", spaces, "", + mPortId, mSessionId, mUid); + out.appendFormat("%*s- Format: %08x Sampling rate: %d Channels: %08x\n", spaces, "", + mConfig.format, mConfig.sample_rate, mConfig.channel_mask); + out.appendFormat("%*s- Preferred Device Id: %08x\n", spaces, "", mPreferredDeviceId); + out.appendFormat("%*s- State: %s\n", spaces, "", mActive ? "Active" : "Inactive"); return NO_ERROR; } -status_t TrackClientDescriptor::dump(int fd) +status_t TrackClientDescriptor::dump(String8& out, int spaces, int index) { - ClientDescriptor::dump(fd); + ClientDescriptor::dump(out, spaces, index); - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " Stream: %d flags: %08x\n", mStream, mFlags); - result.append(buffer); - - write(fd, result.string(), result.size()); + out.appendFormat("%*s- Stream: %d flags: %08x\n", spaces, "", mStream, mFlags); return NO_ERROR; } -status_t RecordClientDescriptor::dump(int fd) +status_t RecordClientDescriptor::dump(String8& out, int spaces, int index) { - ClientDescriptor::dump(fd); - - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " Source: %d flags: %08x\n", mSource, mFlags); - result.append(buffer); + ClientDescriptor::dump(out, spaces, index); - write(fd, result.string(), result.size()); + out.appendFormat("%*s- Source: %d flags: %08x\n", spaces, "", mSource, mFlags); return NO_ERROR; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 856bcb35bc..f343bec833 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -505,13 +505,7 @@ sp AudioPolicyManager::createTelephonyPatch( // symmetric to the one in startInput() for (const auto& activeDesc : mInputs.getActiveInputs()) { if (activeDesc->hasSameHwModuleAs(txSourceDeviceDesc)) { - AudioSessionCollection activeSessions = - activeDesc->getAudioSessions(true /*activeOnly*/); - for (size_t j = 0; j < activeSessions.size(); j++) { - audio_session_t activeSession = activeSessions.keyAt(j); - stopInput(activeDesc->mIoHandle, activeSession); - releaseInput(activeDesc->mIoHandle, activeSession); - } + closeSessions(activeDesc, true /*activeOnly*/); } } } @@ -779,6 +773,11 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, audio_port_handle_t *portId) { audio_attributes_t attributes; + DeviceVector outputDevices; + routing_strategy strategy; + audio_devices_t device; + audio_port_handle_t requestedDeviceId = *selectedDeviceId; + if (attr != NULL) { if (!isValidAttributes(attr)) { ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", @@ -820,7 +819,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, *stream = streamTypefromAttributesInt(&attributes); *output = desc->mIoHandle; ALOGV("getOutputForAttr() returns output %d", *output); - return NO_ERROR; + goto exit; } // Virtual sources must always be dynamicaly or explicitly routed @@ -844,8 +843,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, // explicit route. Is that the intended and necessary behavior? mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid); - routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); - audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + strategy = (routing_strategy) getStrategyForAttr(&attributes); + device = getDeviceForStrategy(strategy, false /*fromCache*/); if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); @@ -876,11 +875,22 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, return INVALID_OPERATION; } - DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); + outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; - ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d", *output, *selectedDeviceId); +exit: + audio_config_base_t clientConfig = {.sample_rate = config->sample_rate, + .format = config->format, + .channel_mask = config->channel_mask }; + sp clientDesc = + new TrackClientDescriptor(*portId, uid, session, + attributes, clientConfig, requestedDeviceId, *stream, *flags); + sp outputDesc = mOutputs.valueFor(*output); + outputDesc->clients().emplace(*portId, clientDesc); + + ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d", + *output, *selectedDeviceId, *portId); return NO_ERROR; } @@ -1122,19 +1132,21 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector outputDesc = mOutputs.getOutputForClient(portId); + if (outputDesc == 0) { + ALOGW("startOutput() no output for client %d", portId); return BAD_VALUE; } + sp client = outputDesc->clients()[portId]; + audio_stream_type_t stream = client->stream(); + audio_session_t session = client->session(); - sp outputDesc = mOutputs.valueAt(index); + ALOGV("startOutput() output %d, stream %d, session %d", + outputDesc->mIoHandle, stream, session); status_t status = outputDesc->start(); if (status != NO_ERROR) { @@ -1158,7 +1170,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, } else if (mOutputRoutes.getAndClearRouteChanged(session)) { newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); if (newDevice != outputDesc->device()) { - checkStrategyRoute(getStrategy(stream), output); + checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle); } } else { newDevice = AUDIO_DEVICE_NONE; @@ -1326,19 +1338,20 @@ status_t AudioPolicyManager::startSource(const sp& output return NO_ERROR; } - -status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session) +status_t AudioPolicyManager::stopOutput(audio_port_handle_t portId) { - ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); - ssize_t index = mOutputs.indexOfKey(output); - if (index < 0) { - ALOGW("stopOutput() unknown output %d", output); + ALOGV("%s portId %d", __FUNCTION__, portId); + + sp outputDesc = mOutputs.getOutputForClient(portId); + if (outputDesc == 0) { + ALOGW("stopOutput() no output for client %d", portId); return BAD_VALUE; } + sp client = outputDesc->clients()[portId]; + audio_stream_type_t stream = client->stream(); + audio_session_t session = client->session(); - sp outputDesc = mOutputs.valueAt(index); + ALOGV("stopOutput() output %d, stream %d, session %d", outputDesc->mIoHandle, stream, session); if (outputDesc->mRefCount[stream] == 1) { // Automatically disable the remote submix input when output is stopped on a @@ -1435,32 +1448,35 @@ status_t AudioPolicyManager::stopSource(const sp& outputD } } -void AudioPolicyManager::releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream __unused, - audio_session_t session __unused) +void AudioPolicyManager::releaseOutput(audio_port_handle_t portId) { - ALOGV("releaseOutput() %d", output); - ssize_t index = mOutputs.indexOfKey(output); - if (index < 0) { - ALOGW("releaseOutput() releasing unknown output %d", output); + ALOGV("%s portId %d", __FUNCTION__, portId); + + sp outputDesc = mOutputs.getOutputForClient(portId); + if (outputDesc == 0) { + ALOGW("releaseOutput() no output for client %d", portId); return; } + sp client = outputDesc->clients()[portId]; + audio_session_t session = client->session(); + + ALOGV("releaseOutput() %d", outputDesc->mIoHandle); // Routing mOutputRoutes.removeRoute(session); - sp desc = mOutputs.valueAt(index); - if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { - if (desc->mDirectOpenCount <= 0) { + if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (outputDesc->mDirectOpenCount <= 0) { ALOGW("releaseOutput() invalid open count %d for output %d", - desc->mDirectOpenCount, output); + outputDesc->mDirectOpenCount, outputDesc->mIoHandle); return; } - if (--desc->mDirectOpenCount == 0) { - closeOutput(output); + if (--outputDesc->mDirectOpenCount == 0) { + closeOutput(outputDesc->mIoHandle); mpClientInterface->onAudioPortListUpdate(); } } + outputDesc->clients().erase(portId); } @@ -1485,6 +1501,9 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, audio_source_t inputSource = attr->source; AudioMix *policyMix = NULL; DeviceVector inputDevices; + sp inputDesc; + sp clientDesc; + audio_port_handle_t requestedDeviceId = *selectedDeviceId; if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; @@ -1540,7 +1559,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, : AUDIO_PORT_HANDLE_NONE; ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session); - return NO_ERROR; + goto exit; } *input = AUDIO_IO_HANDLE_NONE; @@ -1606,8 +1625,14 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; - ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d", - *input, *inputType, *selectedDeviceId); +exit: + clientDesc = new RecordClientDescriptor(*portId, uid, session, + *attr, *config, requestedDeviceId, inputSource, flags); + inputDesc = mInputs.valueFor(*input); + inputDesc->clients().emplace(*portId, clientDesc); + + ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d", + *input, *inputType, *selectedDeviceId, *portId); return NO_ERROR; @@ -1810,23 +1835,25 @@ bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { } -status_t AudioPolicyManager::startInput(audio_io_handle_t input, - audio_session_t session, +status_t AudioPolicyManager::startInput(audio_port_handle_t portId, bool silenced, concurrency_type__mask_t *concurrency) { - - ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)", - input, session, silenced, *concurrency); - *concurrency = API_INPUT_CONCURRENCY_NONE; - ssize_t index = mInputs.indexOfKey(input); - if (index < 0) { - ALOGW("startInput() unknown input %d", input); + ALOGV("%s portId %d", __FUNCTION__, portId); + + sp inputDesc = mInputs.getInputForClient(portId); + if (inputDesc == 0) { + ALOGW("startInput() no input for client %d", portId); return BAD_VALUE; } - sp inputDesc = mInputs.valueAt(index); + sp client = inputDesc->clients()[portId]; + audio_session_t session = client->session(); + audio_io_handle_t input = inputDesc->mIoHandle; + + ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)", + input, session, silenced, *concurrency); sp audioSession = inputDesc->getAudioSession(session); if (audioSession == 0) { @@ -1858,11 +1885,8 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, true /*activeOnly*/); sp activeSession = activeSessions.valueAt(0); if (activeSession->isSilenced()) { - audio_io_handle_t activeInput = activeDesc->mIoHandle; - audio_session_t activeSessionId = activeSession->session(); - stopInput(activeInput, activeSessionId); - releaseInput(activeInput, activeSessionId); - ALOGV("startInput(%d) stopping silenced input %d", input, activeInput); + closeSession(activeDesc, activeSession); + ALOGV("startInput() session %d stopping silenced session %d", session, activeSession->session()); activeInputs = mInputs.getActiveInputs(); } } @@ -1916,14 +1940,12 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, if (activeSource == AUDIO_SOURCE_HOTWORD) { AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true /*activeOnly*/); - audio_session_t activeSession = activeSessions.keyAt(0); - audio_io_handle_t activeHandle = activeDesc->mIoHandle; + sp activeSession = activeSessions[0]; SortedVector sessions = activeDesc->getPreemptedSessions(); *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; - sessions.add(activeSession); + sessions.add(activeSession->session()); inputDesc->setPreemptedSessions(sessions); - stopInput(activeHandle, activeSession); - releaseInput(activeHandle, activeSession); + closeSession(inputDesc, activeSession); ALOGV("startInput(%d) for HOTWORD preempting HOTWORD input %d", input, activeDesc->mIoHandle); } @@ -1991,22 +2013,22 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, return NO_ERROR; } -status_t AudioPolicyManager::stopInput(audio_io_handle_t input, - audio_session_t session) +status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) { - ALOGV("stopInput() input %d", input); - ssize_t index = mInputs.indexOfKey(input); - if (index < 0) { - ALOGW("stopInput() unknown input %d", input); + ALOGV("%s portId %d", __FUNCTION__, portId); + + sp inputDesc = mInputs.getInputForClient(portId); + if (inputDesc == 0) { + ALOGW("stopInput() no input for client %d", portId); return BAD_VALUE; } - sp inputDesc = mInputs.valueAt(index); + sp client = inputDesc->clients()[portId]; + audio_session_t session = client->session(); + audio_io_handle_t input = inputDesc->mIoHandle; + + ALOGV("stopInput() input %d", input); sp audioSession = inputDesc->getAudioSession(session); - if (index < 0) { - ALOGW("stopInput() unknown session %d on input %d", session, input); - return BAD_VALUE; - } if (audioSession->activeCount() == 0) { ALOGW("stopInput() input %d already stopped", input); @@ -2062,22 +2084,24 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, return NO_ERROR; } -void AudioPolicyManager::releaseInput(audio_io_handle_t input, - audio_session_t session) +void AudioPolicyManager::releaseInput(audio_port_handle_t portId) { - ALOGV("releaseInput() %d", input); - ssize_t index = mInputs.indexOfKey(input); - if (index < 0) { - ALOGW("releaseInput() releasing unknown input %d", input); + ALOGV("%s portId %d", __FUNCTION__, portId); + + sp inputDesc = mInputs.getInputForClient(portId); + if (inputDesc == 0) { + ALOGW("releaseInput() no input for client %d", portId); return; } + sp client = inputDesc->clients()[portId]; + audio_session_t session = client->session(); + audio_io_handle_t input = inputDesc->mIoHandle; + + ALOGV("releaseInput() %d", input); // Routing mInputRoutes.removeRoute(session); - sp inputDesc = mInputs.valueAt(index); - ALOG_ASSERT(inputDesc != 0); - sp audioSession = inputDesc->getAudioSession(session); if (audioSession == 0) { ALOGW("releaseInput() unknown session %d on input %d", session, input); @@ -2100,10 +2124,31 @@ void AudioPolicyManager::releaseInput(audio_io_handle_t input, } closeInput(input); + inputDesc->clients().erase(portId); mpClientInterface->onAudioPortListUpdate(); ALOGV("releaseInput() exit"); } +void AudioPolicyManager::closeSessions(const sp& input, bool activeOnly) +{ + AudioSessionCollection sessions = input->getAudioSessions(activeOnly /*activeOnly*/); + for (size_t i = 0; i < sessions.size(); i++) { + closeSession(input, sessions[i]); + } +} + +void AudioPolicyManager::closeSession(const sp& input, + const sp& session) +{ + RecordClientVector clients = input->getClientsForSession(session->session()); + + for (const auto& client : clients) { + stopInput(client->portId()); + releaseInput(client->portId()); + } +} + + void AudioPolicyManager::closeAllInputs() { bool patchRemoved = false; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index e412645818..a92223f524 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -120,15 +120,9 @@ public: audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); - virtual void releaseOutput(audio_io_handle_t output, - audio_stream_type_t stream, - audio_session_t session); + virtual status_t startOutput(audio_port_handle_t portId); + virtual status_t stopOutput(audio_port_handle_t portId); + virtual void releaseOutput(audio_port_handle_t portId); virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, @@ -140,16 +134,13 @@ public: audio_port_handle_t *portId); // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_io_handle_t input, - audio_session_t session, + virtual status_t startInput(audio_port_handle_t portId, bool silenced, concurrency_type__mask_t *concurrency); // indicates to the audio policy manager that the input stops being used. - virtual status_t stopInput(audio_io_handle_t input, - audio_session_t session); - virtual void releaseInput(audio_io_handle_t input, - audio_session_t session); + virtual status_t stopInput(audio_port_handle_t portId); + virtual void releaseInput(audio_port_handle_t portId); virtual void closeAllInputs(); virtual void initStreamVolume(audio_stream_type_t stream, int indexMin, @@ -551,6 +542,10 @@ protected: static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); + void closeSessions(const sp& input, bool activeOnly); + void closeSession(const sp& input, + const sp& session); + const uid_t mUidCached; // AID_AUDIOSERVER AudioPolicyClientInterface *mpClientInterface; // audio policy client interface sp mPrimaryOutput; // primary output descriptor diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 859072b712..fdfd573143 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -200,7 +200,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, !modifyPhoneStateAllowed(pid, uid)) { // If the app tries to play music through the telephony device and doesn't have permission // the fallback to the default output device. - mAudioPolicyManager->releaseOutput(*output, *stream, session); + mAudioPolicyManager->releaseOutput(*portId); flags = originalFlags; *selectedDeviceId = AUDIO_PORT_HANDLE_NONE; *portId = AUDIO_PORT_HANDLE_NONE; @@ -245,8 +245,7 @@ status_t AudioPolicyService::startOutput(audio_port_handle_t portId) } Mutex::Autolock _l(mLock); AutoCallerClear acc; - status_t status = mAudioPolicyManager->startOutput( - client->io, client->stream, client->session); + status_t status = mAudioPolicyManager->startOutput(portId); if (status == NO_ERROR) { client->active = true; } @@ -298,8 +297,7 @@ status_t AudioPolicyService::doStopOutput(audio_port_handle_t portId) } Mutex::Autolock _l(mLock); AutoCallerClear acc; - status_t status = mAudioPolicyManager->stopOutput( - client->io, client->stream, client->session); + status_t status = mAudioPolicyManager->stopOutput(portId); if (status == NO_ERROR) { client->active = false; } @@ -328,8 +326,7 @@ void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId) mAudioRecordClients.removeItem(portId); // called from internal thread: no need to clear caller identity - mAudioPolicyManager->releaseOutput( - client->io, client->stream, client->session); + mAudioPolicyManager->releaseOutput(portId); } status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, @@ -433,7 +430,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, if (status != NO_ERROR) { if (status == PERMISSION_DENIED) { AutoCallerClear acc; - mAudioPolicyManager->releaseInput(*input, session); + mAudioPolicyManager->releaseInput(*portId); } return status; } @@ -527,8 +524,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc status_t status; { AutoCallerClear acc; - status = mAudioPolicyManager->startInput( - client->io, client->session, *silenced, &concurrency); + status = mAudioPolicyManager->startInput(portId, *silenced, &concurrency); } @@ -641,7 +637,7 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) // finish the recording app op finishRecording(client->opPackageName, client->uid); AutoCallerClear acc; - return mAudioPolicyManager->stopInput(client->io, client->session); + return mAudioPolicyManager->stopInput(portId); } void AudioPolicyService::releaseInput(audio_port_handle_t portId) @@ -674,7 +670,7 @@ void AudioPolicyService::releaseInput(audio_port_handle_t portId) { Mutex::Autolock _l(mLock); AutoCallerClear acc; - mAudioPolicyManager->releaseInput(client->io, client->session); + mAudioPolicyManager->releaseInput(portId); } } -- GitLab From 5513843dfeb5ffcff1fa5f8a5254187eca245256 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 2 Jul 2018 18:07:30 +0800 Subject: [PATCH 0159/1530] MTP: Add support of ObjectInfoChanged Event Under MTP mode, new file created at device side shown size zero at PC side due to file information is not updated to PC in time with ObjectAdded event Bug: 77883345 Test: 1. Take screenshot of device under MTP mode with Win10 / Linux 2. file copy, delete, rename between PC and device 3. file copy, delete, rename on device with adb shell command Test: adb shell am instrument -w android.mtp/ android.support.test.runner.AndroidJUnitRunner Change-Id: If428064fb0104b53b8afd5b050a4fab4a09312f0 --- media/mtp/MtpServer.cpp | 6 ++++++ media/mtp/MtpServer.h | 1 + 2 files changed, 7 insertions(+) diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index ccddd6e8fc..cff28039a9 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -99,6 +99,7 @@ static const MtpEventCode kSupportedEventCodes[] = { MTP_EVENT_STORE_ADDED, MTP_EVENT_STORE_REMOVED, MTP_EVENT_DEVICE_PROP_CHANGED, + MTP_EVENT_OBJECT_INFO_CHANGED, }; MtpServer::MtpServer(IMtpDatabase* database, int controlFd, bool ptp, @@ -259,6 +260,11 @@ void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { sendEvent(MTP_EVENT_OBJECT_REMOVED, handle); } +void MtpServer::sendObjectInfoChanged(MtpObjectHandle handle) { + ALOGV("sendObjectInfoChanged %d\n", handle); + sendEvent(MTP_EVENT_OBJECT_INFO_CHANGED, handle); +} + void MtpServer::sendStoreAdded(MtpStorageID id) { ALOGV("sendStoreAdded %08X\n", id); sendEvent(MTP_EVENT_STORE_ADDED, id); diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index f6939d7403..1f8799ff23 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -115,6 +115,7 @@ public: void sendObjectAdded(MtpObjectHandle handle); void sendObjectRemoved(MtpObjectHandle handle); + void sendObjectInfoChanged(MtpObjectHandle handle); void sendDevicePropertyChanged(MtpDeviceProperty property); private: -- GitLab From 09f9c0237236f21cf331bcd811da89c26fc9c627 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Fri, 27 Jul 2018 10:22:35 -0700 Subject: [PATCH 0160/1530] Fix undefined error in ALOGV_IF. Error found with clang static analyzer after making ALOGV_IF to use its parameters. Bug: 111850071 Test: build with WITH_TIDY Change-Id: I35bdfd6583a106d82f30063cbd629bcff1d58289 --- media/libaudioprocessing/AudioMixer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index f6f817a33f..f3ea8260de 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -875,7 +875,7 @@ void AudioMixer::process__validate() t->hook = Track::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat); ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, - "Track %d needs downmix + resample", i); + "Track %d needs downmix + resample", name); } else { if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ t->hook = Track::getTrackHook( @@ -890,7 +890,7 @@ void AudioMixer::process__validate() t->hook = Track::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat); ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, - "Track %d needs downmix", i); + "Track %d needs downmix", name); } } } -- GitLab From 5b13ff8f66e4b376127af9cfb6fa3f075f17eaa2 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Fri, 27 Jul 2018 11:20:17 -0700 Subject: [PATCH 0161/1530] audioflinger: Replace cpustats/CentralTendencyStatistics with audio_utils/Statistics Revert CPU_FREQUENCY_STATISTICS macro in Configuration.h. Test: build with different #define, dumpsys media.audio_flinger Change-Id: I91680c462b069779d8db3b7a10603fb11a81a135 --- services/audioflinger/Configuration.h | 2 +- services/audioflinger/FastMixer.cpp | 2 +- services/audioflinger/FastMixerDumpState.cpp | 40 ++++++++++---------- services/audioflinger/Threads.cpp | 38 +++++++++---------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h index 64bb426180..34cd8217f7 100644 --- a/services/audioflinger/Configuration.h +++ b/services/audioflinger/Configuration.h @@ -27,7 +27,7 @@ //#define AUDIO_WATCHDOG // uncomment to display CPU load adjusted for CPU frequency -#define CPU_FREQUENCY_STATISTICS +//define CPU_FREQUENCY_STATISTICS // uncomment to enable fast threads to take performance samples for later statistical analysis #define FAST_THREAD_STATISTICS diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index ad3526498d..a42d6b3f57 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -32,7 +32,7 @@ #include #include #ifdef FAST_THREAD_STATISTICS -#include +#include #ifdef CPU_FREQUENCY_STATISTICS #include #endif diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index c0f9f0f2f9..ffdc117726 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -19,7 +19,7 @@ #include "Configuration.h" #ifdef FAST_THREAD_STATISTICS -#include +#include #ifdef CPU_FREQUENCY_STATISTICS #include #endif @@ -92,9 +92,9 @@ void FastMixerDumpState::dump(int fd) const } // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, // and adjusted CPU load in MHz normalized for CPU clock frequency - CentralTendencyStatistics wall, loadNs; + Statistics wall, loadNs; #ifdef CPU_FREQUENCY_STATISTICS - CentralTendencyStatistics kHz, loadMHz; + Statistics kHz, loadMHz; uint32_t previousCpukHz = 0; #endif // Assuming a normal distribution for cycle times, three standard deviations on either side of @@ -109,18 +109,18 @@ void FastMixerDumpState::dump(int fd) const if (tail != NULL) { tail[j] = wallNs; } - wall.sample(wallNs); + wall.add(wallNs); uint32_t sampleLoadNs = mLoadNs[i]; - loadNs.sample(sampleLoadNs); + loadNs.add(sampleLoadNs); #ifdef CPU_FREQUENCY_STATISTICS uint32_t sampleCpukHz = mCpukHz[i]; // skip bad kHz samples if ((sampleCpukHz & ~0xF) != 0) { - kHz.sample(sampleCpukHz >> 4); + kHz.add(sampleCpukHz >> 4); if (sampleCpukHz == previousCpukHz) { double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 - loadMHz.sample(adjMHz); + loadMHz.add(adjMHz); } } previousCpukHz = sampleCpukHz; @@ -128,42 +128,42 @@ void FastMixerDumpState::dump(int fd) const } if (n) { dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); + wall.getN() * mixPeriodSec); dprintf(fd, " wall clock time in ms per mix cycle:\n" " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, - wall.stddev()*1e-6); + wall.getMean()*1e-6, wall.getMin()*1e-6, wall.getMax()*1e-6, + wall.getStdDev()*1e-6); dprintf(fd, " raw CPU load in us per mix cycle:\n" " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); + loadNs.getMean()*1e-3, loadNs.getMin()*1e-3, loadNs.getMax()*1e-3, + loadNs.getStdDev()*1e-3); } else { dprintf(fd, " No FastMixer statistics available currently\n"); } #ifdef CPU_FREQUENCY_STATISTICS dprintf(fd, " CPU clock frequency in MHz:\n" " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + kHz.getMean()*1e-3, kHz.getMin()*1e-3, kHz.getMax()*1e-3, kHz.getStdDev()*1e-3); dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); + loadMHz.getMean(), loadMHz.getMin(), loadMHz.getMax(), loadMHz.getStdDev()); #endif if (tail != NULL) { qsort(tail, n, sizeof(uint32_t), compare_uint32_t); // assume same number of tail samples on each side, left and right uint32_t count = n / kTailDenominator; - CentralTendencyStatistics left, right; + Statistics left, right; for (uint32_t i = 0; i < count; ++i) { - left.sample(tail[i]); - right.sample(tail[n - (i + 1)]); + left.add(tail[i]); + right.add(tail[n - (i + 1)]); } dprintf(fd, " Distribution of mix cycle times in ms for the tails " "(> ~3 stddev outliers):\n" " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, - right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, - right.stddev()*1e-6); + left.getMean()*1e-6, left.getMin()*1e-6, left.getMax()*1e-6, left.getStdDev()*1e-6, + right.getMean()*1e-6, right.getMin()*1e-6, right.getMax()*1e-6, + right.getStdDev()*1e-6); delete[] tail; } #endif diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d55da1bac1..6c7179e985 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -73,7 +73,7 @@ #endif #ifdef DEBUG_CPU_USAGE -#include +#include #include #endif @@ -335,9 +335,9 @@ public: #ifdef DEBUG_CPU_USAGE private: ThreadCpuUsage mCpuUsage; // instantaneous thread CPU usage in wall clock ns - CentralTendencyStatistics mWcStats; // statistics on thread CPU usage in wall clock ns + Statistics mWcStats; // statistics on thread CPU usage in wall clock ns - CentralTendencyStatistics mHzStats; // statistics on thread CPU usage in cycles + Statistics mHzStats; // statistics on thread CPU usage in cycles int mCpuNum; // thread's current CPU number int mCpukHz; // frequency of thread's current CPU in kHz @@ -363,7 +363,7 @@ void CpuStats::sample(const String8 &title // record sample for wall clock statistics if (valid) { - mWcStats.sample(wcNs); + mWcStats.add(wcNs); } // get the current CPU number @@ -382,26 +382,26 @@ void CpuStats::sample(const String8 &title // if no change in CPU number or frequency, then record sample for cycle statistics if (valid && mCpukHz > 0) { - double cycles = wcNs * cpukHz * 0.000001; - mHzStats.sample(cycles); + const double cycles = wcNs * cpukHz * 0.000001; + mHzStats.add(cycles); } - unsigned n = mWcStats.n(); + const unsigned n = mWcStats.getN(); // mCpuUsage.elapsed() is expensive, so don't call it every loop if ((n & 127) == 1) { - long long elapsed = mCpuUsage.elapsed(); + const long long elapsed = mCpuUsage.elapsed(); if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) { - double perLoop = elapsed / (double) n; - double perLoop100 = perLoop * 0.01; - double perLoop1k = perLoop * 0.001; - double mean = mWcStats.mean(); - double stddev = mWcStats.stddev(); - double minimum = mWcStats.minimum(); - double maximum = mWcStats.maximum(); - double meanCycles = mHzStats.mean(); - double stddevCycles = mHzStats.stddev(); - double minCycles = mHzStats.minimum(); - double maxCycles = mHzStats.maximum(); + const double perLoop = elapsed / (double) n; + const double perLoop100 = perLoop * 0.01; + const double perLoop1k = perLoop * 0.001; + const double mean = mWcStats.getMean(); + const double stddev = mWcStats.getStdDev(); + const double minimum = mWcStats.getMin(); + const double maximum = mWcStats.getMax(); + const double meanCycles = mHzStats.getMean(); + const double stddevCycles = mHzStats.getStdDev(); + const double minCycles = mHzStats.getMin(); + const double maxCycles = mHzStats.getMax(); mCpuUsage.resetElapsed(); mWcStats.reset(); mHzStats.reset(); -- GitLab From 01c8f5681ce875bb60b6451929bb3fbd0876ca61 Mon Sep 17 00:00:00 2001 From: jiabin Date: Thu, 19 Jul 2018 17:47:28 -0700 Subject: [PATCH 0162/1530] Use fast mode with patch track and patch record if possible. Use fast mode with patch track/patch record if fast mixer/fast capture is enabled. With the fast track and fast mixer/fast capture, we can get a better performance on latency. Bug: 111800664 Test: make phone call on marlin/walleye with enabling legacy usb hal Change-Id: I0edd5978049e7c55da217a7a19e45fcbe5c3ac62 --- services/audioflinger/FastCapture.cpp | 33 ++++++++++++++++++++---- services/audioflinger/FastCaptureState.h | 5 ++++ services/audioflinger/PatchPanel.cpp | 26 +++++++++++++++---- services/audioflinger/Threads.cpp | 8 ++++++ services/audioflinger/Threads.h | 2 ++ 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp index d063772bab..dd84bf2b0c 100644 --- a/services/audioflinger/FastCapture.cpp +++ b/services/audioflinger/FastCapture.cpp @@ -20,6 +20,7 @@ #define ATRACE_TAG ATRACE_TAG_AUDIO #include "Configuration.h" +#include #include #include #include @@ -161,7 +162,21 @@ void FastCapture::onWork() const FastCaptureState * const current = (const FastCaptureState *) mCurrent; FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const FastCaptureState::Command command = mCommand; - const size_t frameCount = current->mFrameCount; + size_t frameCount = current->mFrameCount; + AudioBufferProvider* fastPatchRecordBufferProvider = current->mFastPatchRecordBufferProvider; + AudioBufferProvider::Buffer patchBuffer; + + if (fastPatchRecordBufferProvider != 0) { + patchBuffer.frameCount = ~0; + status_t status = fastPatchRecordBufferProvider->getNextBuffer(&patchBuffer); + if (status != NO_ERROR) { + frameCount = 0; + } else if (patchBuffer.frameCount < frameCount) { + // TODO: Make sure that it doesn't cause any issues if we just get a small available + // buffer from the buffer provider. + frameCount = patchBuffer.frameCount; + } + } if ((command & FastCaptureState::READ) /*&& isWarm*/) { ALOG_ASSERT(mInputSource != NULL); @@ -176,6 +191,7 @@ void FastCapture::onWork() mTotalNativeFramesRead += framesRead; dumpState->mFramesRead = mTotalNativeFramesRead; mReadBufferState = framesRead; + patchBuffer.frameCount = framesRead; } else { dumpState->mReadErrors++; mReadBufferState = 0; @@ -193,11 +209,18 @@ void FastCapture::onWork() } if (mReadBufferState > 0) { ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); - // FIXME This supports at most one fast capture client. - // To handle multiple clients this could be converted to an array, - // or with a lot more work the control block could be shared by all clients. audio_track_cblk_t* cblk = current->mCblk; - if (cblk != NULL && framesWritten > 0) { + if (fastPatchRecordBufferProvider != 0) { + // This indicates the fast track is a patch record, update the cblk by + // calling releaseBuffer(). + memcpy_by_audio_format(patchBuffer.raw, current->mFastPatchRecordFormat, + mReadBuffer, mFormat.mFormat, framesWritten * mFormat.mChannelCount); + patchBuffer.frameCount = framesWritten; + fastPatchRecordBufferProvider->releaseBuffer(&patchBuffer); + } else if (cblk != NULL && framesWritten > 0) { + // FIXME This supports at most one fast capture client. + // To handle multiple clients this could be converted to an array, + // or with a lot more work the control block could be shared by all clients. int32_t rear = cblk->u.mStreaming.mRear; android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear); cblk->mServer += framesWritten; diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h index 9bca2d4b50..d287232776 100644 --- a/services/audioflinger/FastCaptureState.h +++ b/services/audioflinger/FastCaptureState.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_FAST_CAPTURE_STATE_H #include +#include #include "FastThreadState.h" #include @@ -37,6 +38,10 @@ struct FastCaptureState : FastThreadState { size_t mFrameCount; // number of frames per fast capture buffer audio_track_cblk_t* mCblk; // control block for the single fast client, or NULL + audio_format_t mFastPatchRecordFormat = AUDIO_FORMAT_INVALID; + AudioBufferProvider* mFastPatchRecordBufferProvider = nullptr; // a reference to a patch + // record in fast mode + // Extends FastThreadState::Command static const Command // The following commands also process configuration changes, and can be "or"ed: diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index f044fb7d42..ada8572541 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -431,14 +431,14 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) // use a pseudo LCM between input and output framecount size_t playbackFrameCount = mPlayback.thread()->frameCount(); int playbackShift = __builtin_ctz(playbackFrameCount); - size_t recordFramecount = mRecord.thread()->frameCount(); - int shift = __builtin_ctz(recordFramecount); + size_t recordFrameCount = mRecord.thread()->frameCount(); + int shift = __builtin_ctz(recordFrameCount); if (playbackShift < shift) { shift = playbackShift; } - size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; - ALOGV("%s() playframeCount %zu recordFramecount %zu frameCount %zu", - __func__, playbackFrameCount, recordFramecount, frameCount); + size_t frameCount = (playbackFrameCount * recordFrameCount) >> shift; + ALOGV("%s() playframeCount %zu recordFrameCount %zu frameCount %zu", + __func__, playbackFrameCount, recordFrameCount, frameCount); // create a special record track to capture from record thread uint32_t channelCount = mPlayback.thread()->channelCount(); @@ -455,6 +455,17 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) } audio_input_flags_t inputFlags = mAudioPatch.sources[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? mAudioPatch.sources[0].flags.input : AUDIO_INPUT_FLAG_NONE; + if (sampleRate == mRecord.thread()->sampleRate() && + inChannelMask == mRecord.thread()->channelMask() && + mRecord.thread()->fastTrackAvailable() && + mRecord.thread()->hasFastCapture()) { + // Create a fast track if the record thread has fast capture to get better performance. + // Only enable fast mode when there is no resample needed. + inputFlags = (audio_input_flags_t) (inputFlags | AUDIO_INPUT_FLAG_FAST); + } else { + // Fast mode is not available in this case. + inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST); + } sp tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord( mRecord.thread().get(), sampleRate, @@ -476,6 +487,11 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) // "reuse one existing output mix" case streamType = mAudioPatch.sources[1].ext.mix.usecase.stream; } + if (mPlayback.thread()->hasFastMixer()) { + // Create a fast track if the playback thread has fast mixer to get better performance. + outputFlags = (audio_output_flags_t) (outputFlags | AUDIO_OUTPUT_FLAG_FAST); + } + // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer sp tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 1e411c7256..68c8cac65f 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6700,6 +6700,14 @@ reacquire_wakelock: } didModify = true; } + AudioBufferProvider* abp = (fastTrack != 0 && fastTrack->isPatchTrack()) ? + reinterpret_cast(fastTrack.get()) : nullptr; + if (state->mFastPatchRecordBufferProvider != abp) { + state->mFastPatchRecordBufferProvider = abp; + state->mFastPatchRecordFormat = fastTrack == 0 ? + AUDIO_FORMAT_INVALID : fastTrack->format(); + didModify = true; + } sq->end(didModify); if (didModify) { sq->push(block); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 847a9c6ec6..23ab37b718 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1527,6 +1527,8 @@ public: void updateMetadata_l() override; + bool fastTrackAvailable() const { return mFastTrackAvail; } + private: // Enter standby if not already in standby, and set mStandby flag void standbyIfNotAlreadyInStandby(); -- GitLab From fa69dc61def2931f01a1e14a3b385ffc56a4f2bc Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 27 Jul 2018 09:58:58 -0700 Subject: [PATCH 0163/1530] audiopolicy: Add AudioProfileVector::findBestMatchingOutputConfig Assuming that the call target AudioProfileVector contains input profiles, the method finds the best matching config from provided AudioProfileVector of output profiles. Matching is performed according to the given preferences for audio formats and channel masks. Bug: 63901775 Test: MSD prototype Change-Id: Id005aad9de8bfe8cea5cb6291efc278d6260210c --- .../managerdefinitions/include/AudioProfile.h | 12 ++++ .../managerdefinitions/src/AudioProfile.cpp | 72 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h index 31c75a4b41..a1ee7087ae 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioProfile.h @@ -16,6 +16,8 @@ #pragma once +#include + #include #include #include @@ -139,6 +141,16 @@ public: audio_port_type_t portType, audio_port_role_t portRole) const; void clearProfiles(); + // Assuming that this profile vector contains input profiles, + // find the best matching config from 'outputProfiles', according to + // the given preferences for audio formats and channel masks. + // Note: std::vectors are used because specialized containers for formats + // and channels can be sorted and use their own ordering. + status_t findBestMatchingOutputConfig(const AudioProfileVector& outputProfiles, + const std::vector& preferredFormats, // order: most pref -> least pref + const std::vector& preferredOutputChannels, + bool preferHigherSamplingRates, + audio_config_base *bestOutputConfig) const; sp getFirstValidProfile() const; sp getFirstValidProfileFor(audio_format_t format) const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index b76b609628..d04beec16e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #include #define LOG_TAG "APM::AudioProfile" @@ -406,6 +408,76 @@ void AudioProfileVector::clearProfiles() } } +// Returns an intersection between two possibly unsorted vectors and the contents of 'order'. +// The result is ordered according to 'order'. +template +std::vector intersectFilterAndOrder( + const T& input1, const T& input2, const Order& order) +{ + std::set set1{input1.begin(), input1.end()}; + std::set set2{input2.begin(), input2.end()}; + std::set common; + std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), + std::inserter(common, common.begin())); + std::vector result; + for (const auto& e : order) { + if (common.find(e) != common.end()) result.push_back(e); + } + return result; +} + +// Intersect two possibly unsorted vectors, return common elements according to 'comp' ordering. +// 'comp' is a comparator function. +template +std::vector intersectAndOrder( + const T& input1, const T& input2, Compare comp) +{ + std::set set1{input1.begin(), input1.end(), comp}; + std::set set2{input2.begin(), input2.end(), comp}; + std::vector result; + std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), + std::back_inserter(result), comp); + return result; +} + +status_t AudioProfileVector::findBestMatchingOutputConfig(const AudioProfileVector& outputProfiles, + const std::vector& preferredFormats, + const std::vector& preferredOutputChannels, + bool preferHigherSamplingRates, + audio_config_base *bestOutputConfig) const +{ + auto formats = intersectFilterAndOrder(getSupportedFormats(), + outputProfiles.getSupportedFormats(), preferredFormats); + // Pick the best compatible profile. + for (const auto& f : formats) { + sp inputProfile = getFirstValidProfileFor(f); + sp outputProfile = outputProfiles.getFirstValidProfileFor(f); + if (inputProfile == nullptr || outputProfile == nullptr) { + continue; + } + auto channels = intersectFilterAndOrder(inputProfile->getChannels().asOutMask(), + outputProfile->getChannels(), preferredOutputChannels); + if (channels.empty()) { + continue; + } + auto sampleRates = preferHigherSamplingRates ? + intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(), + std::greater()) : + intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(), + std::less()); + if (sampleRates.empty()) { + continue; + } + ALOGD("%s() found channel mask %#x and sample rate %d for format %#x.", + __func__, *channels.begin(), *sampleRates.begin(), f); + bestOutputConfig->format = f; + bestOutputConfig->sample_rate = *sampleRates.begin(); + bestOutputConfig->channel_mask = *channels.begin(); + return NO_ERROR; + } + return BAD_VALUE; +} + sp AudioProfileVector::getFirstValidProfile() const { for (size_t i = 0; i < size(); i++) { -- GitLab From 15be9d2bb881e79c06d90c855f9efa426fd42b78 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 8 Nov 2017 14:18:13 +1100 Subject: [PATCH 0164/1530] audiopolicy: Add support for the MSD prototype MSD is a Multi-Stream Decoder. It's an optional module that allows mixing of encoded surround streams with PCM streams. This patch adds elementary support for setting up a patch between the MSD module and actual output device, and prioritizing the MSD module's device for output. The patch is always set up, and is automatically re-establishes itself if the actual output device changes. Bug: 63901775 Test: MSD prototype Change-Id: I9efa714455dae10b58bd5cc5cdf5557c0227b40c --- .../managerdefault/AudioPolicyManager.cpp | 202 +++++++++++++++++- .../managerdefault/AudioPolicyManager.h | 17 +- 2 files changed, 206 insertions(+), 13 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index f343bec833..b31e396dad 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -38,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -82,6 +82,16 @@ static const audio_format_t AAC_FORMATS[] = { AUDIO_FORMAT_AAC_XHE, }; +// Compressed formats for MSD module, ordered from most preferred to least preferred. +static const std::vector compressedFormatsOrder = {{ + AUDIO_FORMAT_MAT_2_1, AUDIO_FORMAT_MAT_2_0, AUDIO_FORMAT_E_AC3, + AUDIO_FORMAT_AC3, AUDIO_FORMAT_PCM_16_BIT }}; +// Channel masks for MSD module, 3D > 2D > 1D ordering (most preferred to least preferred). +static const std::vector surroundChannelMasksOrder = {{ + AUDIO_CHANNEL_OUT_3POINT1POINT2, AUDIO_CHANNEL_OUT_3POINT0POINT2, + AUDIO_CHANNEL_OUT_2POINT1POINT2, AUDIO_CHANNEL_OUT_2POINT0POINT2, + AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_STEREO }}; + // ---------------------------------------------------------------------------- // AudioPolicyInterface implementation // ---------------------------------------------------------------------------- @@ -225,6 +235,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); updateCallRouting(newDevice); } + const audio_devices_t msdOutDevice = getMsdAudioOutDeviceTypes(); for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) { @@ -232,7 +243,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // do not force device change on duplicated output because if device is 0, it will // also force a device 0 for the two outputs it is duplicated to which may override // a valid device selection on those outputs. - bool force = !desc->isDuplicated() + bool force = (msdOutDevice == AUDIO_DEVICE_NONE || msdOutDevice != desc->device()) + && !desc->isDuplicated() && (!device_distinguishes_on_address(device) // always force when disconnecting (a non-duplicated device) || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)); @@ -525,7 +537,7 @@ sp AudioPolicyManager::createTelephonyPatch( } sp AudioPolicyManager::findDevice( - const DeviceVector& devices, audio_devices_t device) { + const DeviceVector& devices, audio_devices_t device) const { DeviceVector deviceList = devices.getDevicesFromTypeMask(device); ALOG_ASSERT(!deviceList.isEmpty(), "%s() selected device type %#x is not in devices list", __func__, device); @@ -777,6 +789,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, routing_strategy strategy; audio_devices_t device; audio_port_handle_t requestedDeviceId = *selectedDeviceId; + audio_devices_t msdDevice = getMsdAudioOutDeviceTypes(); if (attr != NULL) { if (!isValidAttributes(attr)) { @@ -869,7 +882,20 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, "flags %#x", device, config->sample_rate, config->format, config->channel_mask, *flags); - *output = getOutputForDevice(device, session, *stream, config, flags); + *output = AUDIO_IO_HANDLE_NONE; + if (msdDevice != AUDIO_DEVICE_NONE) { + *output = getOutputForDevice(msdDevice, session, *stream, config, flags); + if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(device) == NO_ERROR) { + ALOGV("%s() Using MSD device 0x%x instead of device 0x%x", + __func__, msdDevice, device); + device = msdDevice; + } else { + *output = AUDIO_IO_HANDLE_NONE; + } + } + if (*output == AUDIO_IO_HANDLE_NONE) { + *output = getOutputForDevice(device, session, *stream, config, flags); + } if (*output == AUDIO_IO_HANDLE_NONE) { mOutputRoutes.removeRoute(session); return INVALID_OPERATION; @@ -1052,6 +1078,164 @@ non_direct_output: return output; } +sp AudioPolicyManager::getMsdAudioInDevice() const { + sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); + if (msdModule != 0) { + DeviceVector msdInputDevices = mAvailableInputDevices.getDevicesFromHwModule( + msdModule->getHandle()); + if (!msdInputDevices.isEmpty()) return msdInputDevices.itemAt(0); + } + return 0; +} + +audio_devices_t AudioPolicyManager::getMsdAudioOutDeviceTypes() const { + sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); + if (msdModule != 0) { + return mAvailableOutputDevices.getDeviceTypesFromHwModule(msdModule->getHandle()); + } + return AUDIO_DEVICE_NONE; +} + +const AudioPatchCollection AudioPolicyManager::getMsdPatches() const { + AudioPatchCollection msdPatches; + audio_module_handle_t msdModuleHandle = mHwModules.getModuleFromName( + AUDIO_HARDWARE_MODULE_ID_MSD)->getHandle(); + if (msdModuleHandle == AUDIO_MODULE_HANDLE_NONE) return msdPatches; + for (size_t i = 0; i < mAudioPatches.size(); ++i) { + sp patch = mAudioPatches.valueAt(i); + for (size_t j = 0; j < patch->mPatch.num_sources; ++j) { + const struct audio_port_config *source = &patch->mPatch.sources[j]; + if (source->type == AUDIO_PORT_TYPE_DEVICE && + source->ext.device.hw_module == msdModuleHandle) { + msdPatches.addAudioPatch(patch->mHandle, patch); + } + } + } + return msdPatches; +} + +status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDevice, + bool hwAvSync, audio_port_config *sourceConfig, audio_port_config *sinkConfig) const +{ + sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); + if (msdModule == nullptr) { + ALOGE("%s() unable to get MSD module", __func__); + return NO_INIT; + } + sp deviceModule = mHwModules.getModuleForDevice(outputDevice); + if (deviceModule == nullptr) { + ALOGE("%s() unable to get module for %#x", __func__, outputDevice); + return NO_INIT; + } + const InputProfileCollection &inputProfiles = msdModule->getInputProfiles(); + if (inputProfiles.isEmpty()) { + ALOGE("%s() no input profiles for MSD module", __func__); + return NO_INIT; + } + const OutputProfileCollection &outputProfiles = deviceModule->getOutputProfiles(); + if (outputProfiles.isEmpty()) { + ALOGE("%s() no output profiles for device %#x", __func__, outputDevice); + return NO_INIT; + } + AudioProfileVector msdProfiles; + // Each IOProfile represents a MixPort from audio_policy_configuration.xml + for (const auto &inProfile : inputProfiles) { + if (hwAvSync == ((inProfile->getFlags() & AUDIO_INPUT_FLAG_HW_AV_SYNC) != 0)) { + msdProfiles.appendVector(inProfile->getAudioProfiles()); + } + } + AudioProfileVector deviceProfiles; + for (const auto &outProfile : outputProfiles) { + if (hwAvSync == ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0)) { + deviceProfiles.appendVector(outProfile->getAudioProfiles()); + } + } + struct audio_config_base bestSinkConfig; + status_t result = msdProfiles.findBestMatchingOutputConfig(deviceProfiles, + compressedFormatsOrder, surroundChannelMasksOrder, true /*preferHigherSamplingRates*/, + &bestSinkConfig); + if (result != NO_ERROR) { + return result; + ALOGD("%s() no matching profiles found for device: %#x, hwAvSync: %d", + __func__, outputDevice, hwAvSync); + } + sinkConfig->sample_rate = bestSinkConfig.sample_rate; + sinkConfig->channel_mask = bestSinkConfig.channel_mask; + sinkConfig->format = bestSinkConfig.format; + // For encoded streams force direct flag to prevent downstream mixing. + sinkConfig->flags.output = static_cast( + sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_DIRECT); + sourceConfig->sample_rate = bestSinkConfig.sample_rate; + // Specify exact channel mask to prevent guessing by bit count in PatchPanel. + sourceConfig->channel_mask = audio_channel_mask_out_to_in(bestSinkConfig.channel_mask); + sourceConfig->format = bestSinkConfig.format; + // Copy input stream directly without any processing (e.g. resampling). + sourceConfig->flags.input = static_cast( + sourceConfig->flags.input | AUDIO_INPUT_FLAG_DIRECT); + if (hwAvSync) { + sinkConfig->flags.output = static_cast( + sinkConfig->flags.output | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); + sourceConfig->flags.input = static_cast( + sourceConfig->flags.input | AUDIO_INPUT_FLAG_HW_AV_SYNC); + } + const unsigned int config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE | + AUDIO_PORT_CONFIG_CHANNEL_MASK | AUDIO_PORT_CONFIG_FORMAT | AUDIO_PORT_CONFIG_FLAGS; + sinkConfig->config_mask |= config_mask; + sourceConfig->config_mask |= config_mask; + return NO_ERROR; +} + +PatchBuilder AudioPolicyManager::buildMsdPatch(audio_devices_t outputDevice) const +{ + PatchBuilder patchBuilder; + patchBuilder.addSource(getMsdAudioInDevice()). + addSink(findDevice(mAvailableOutputDevices, outputDevice)); + audio_port_config sourceConfig = patchBuilder.patch()->sources[0]; + audio_port_config sinkConfig = patchBuilder.patch()->sinks[0]; + // TODO: Figure out whether MSD module has HW_AV_SYNC flag set in the AP config file. + // For now, we just forcefully try with HwAvSync first. + status_t res = getBestMsdAudioProfileFor(outputDevice, true /*hwAvSync*/, + &sourceConfig, &sinkConfig) == NO_ERROR ? NO_ERROR : + getBestMsdAudioProfileFor( + outputDevice, false /*hwAvSync*/, &sourceConfig, &sinkConfig); + if (res == NO_ERROR) { + // Found a matching profile for encoded audio. Re-create PatchBuilder with this config. + return (PatchBuilder()).addSource(sourceConfig).addSink(sinkConfig); + } + ALOGV("%s() no matching profile found. Fall through to default PCM patch" + " supporting PCM format conversion.", __func__); + return patchBuilder; +} + +status_t AudioPolicyManager::setMsdPatch(audio_devices_t outputDevice) { + ALOGV("%s() for outputDevice %#x", __func__, outputDevice); + if (outputDevice == AUDIO_DEVICE_NONE) { + // Use media strategy for unspecified output device. This should only + // occur on checkForDeviceAndOutputChanges(). Device connection events may + // therefore invalidate explicit routing requests. + outputDevice = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + } + PatchBuilder patchBuilder = buildMsdPatch(outputDevice); + const struct audio_patch* patch = patchBuilder.patch(); + const AudioPatchCollection msdPatches = getMsdPatches(); + if (!msdPatches.isEmpty()) { + LOG_ALWAYS_FATAL_IF(msdPatches.size() > 1, + "The current MSD prototype only supports one output patch"); + sp currentPatch = msdPatches.valueAt(0); + if (audio_patches_are_equal(¤tPatch->mPatch, patch)) { + return NO_ERROR; + } + releaseAudioPatch(currentPatch->mHandle, mUidCached); + } + status_t status = installPatch(__func__, -1 /*index*/, nullptr /*patchHandle*/, + patch, 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/); + ALOGE_IF(status != NO_ERROR, "%s() error %d creating MSD audio patch", __func__, status); + ALOGI_IF(status == NO_ERROR, "%s() Patch created from MSD_IN to " + "device:%#x (format:%#x channels:%#x samplerate:%d)", __func__, outputDevice, + patch->sources[0].format, patch->sources[0].channel_mask, patch->sources[0].sample_rate); + return status; +} + audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector& outputs, audio_output_flags_t flags, audio_format_t format) @@ -4600,19 +4784,17 @@ SortedVector AudioPolicyManager::getOutputsForDevice( return outputs; } -void AudioPolicyManager::checkForDeviceAndOutputChanges() -{ - checkForDeviceAndOutputChanges([](){ return false; }); -} - void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function onOutputsChecked) { // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP // output is suspended before any tracks are moved to it checkA2dpSuspend(); checkOutputForAllStrategies(); - if (onOutputsChecked()) checkA2dpSuspend(); + if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend(); updateDevicesAndOutputs(); + if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) { + setMsdPatch(); + } } void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index e025803d38..869cd9d08b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "AudioPolicyInterface.h" #include @@ -407,8 +408,7 @@ protected: // if 'onOutputsChecked' callback is provided, it is executed after the outputs // check via 'checkOutputForAllStrategies'. If the callback returns 'true', // A2DP suspend status is rechecked. - void checkForDeviceAndOutputChanges(); - void checkForDeviceAndOutputChanges(std::function onOutputsChecked); + void checkForDeviceAndOutputChanges(std::function onOutputsChecked = nullptr); // checks and if necessary changes outputs used for all strategies. // must be called every time a condition that affects the output choice for a given strategy @@ -506,7 +506,7 @@ protected: uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0); sp createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs); sp findDevice( - const DeviceVector& devices, audio_devices_t device); + const DeviceVector& devices, audio_devices_t device) const; // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force // the re-evaluation of the output device. @@ -620,6 +620,17 @@ private: status_t getSupportedFormats(audio_io_handle_t ioHandle, FormatVector& formats); + // Support for Multi-Stream Decoder (MSD) module + sp getMsdAudioInDevice() const; + audio_devices_t getMsdAudioOutDeviceTypes() const; + const AudioPatchCollection getMsdPatches() const; + status_t getBestMsdAudioProfileFor(audio_devices_t outputDevice, + bool hwAvSync, + audio_port_config *sourceConfig, + audio_port_config *sinkConfig) const; + PatchBuilder buildMsdPatch(audio_devices_t outputDevice) const; + status_t setMsdPatch(audio_devices_t outputDevice = AUDIO_DEVICE_NONE); + // If any, resolve any "dynamic" fields of an Audio Profiles collection void updateAudioProfiles(audio_devices_t device, audio_io_handle_t ioHandle, AudioProfileVector &profiles); -- GitLab From 9b5a7ad0e9901bb1b4ed46f0f1aac5a75ea8e627 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Fri, 27 Jul 2018 17:33:24 -0700 Subject: [PATCH 0165/1530] NuPlayerDecoder: check buffer size before memcpy Test: none Bug: 111649621 Change-Id: I7f19a66a11eaeb54bf9513a4f56aa1ee7d3d81f5 --- media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp | 6 ++++++ media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp index 645138a722..931b86ef90 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp @@ -1088,6 +1088,12 @@ bool NuPlayer2::Decoder::onInputBufferFetched(const sp &msg) { static_cast(holder.get())->mediaBuffer() : nullptr; } if (mediaBuf != NULL) { + if (mediaBuf->size() > codecBuffer->capacity()) { + handleError(ERROR_BUFFER_TOO_SMALL); + mDequeuedInputBuffers.push_back(bufferIx); + return false; + } + codecBuffer->setRange(0, mediaBuf->size()); memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size()); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 69cd82e2df..050e4fb292 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -1069,6 +1069,12 @@ bool NuPlayer::Decoder::onInputBufferFetched(const sp &msg) { static_cast(holder.get())->mediaBuffer() : nullptr; } if (mediaBuf != NULL) { + if (mediaBuf->size() > codecBuffer->capacity()) { + handleError(ERROR_BUFFER_TOO_SMALL); + mDequeuedInputBuffers.push_back(bufferIx); + return false; + } + codecBuffer->setRange(0, mediaBuf->size()); memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size()); -- GitLab From 83289653ef22399dae84f1e7bb75c32e1c432c95 Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Mon, 30 Jul 2018 06:13:57 -0700 Subject: [PATCH 0166/1530] AudioPolicyManager: Fix logging statement We move our 'return' to after our ALOGD statement, so we'll get the information logged. Test: Treehugger Change-Id: Ieb6953633d197baf4311ca51a381f31bec884afc --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b31e396dad..47e17f113f 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1155,9 +1155,9 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDev compressedFormatsOrder, surroundChannelMasksOrder, true /*preferHigherSamplingRates*/, &bestSinkConfig); if (result != NO_ERROR) { - return result; ALOGD("%s() no matching profiles found for device: %#x, hwAvSync: %d", __func__, outputDevice, hwAvSync); + return result; } sinkConfig->sample_rate = bestSinkConfig.sample_rate; sinkConfig->channel_mask = bestSinkConfig.channel_mask; -- GitLab From 3e6c7e15913a812b8658504105100063d57d1d6e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Jul 2018 17:09:23 -0700 Subject: [PATCH 0167/1530] audio policy: refactor HW audio sources Refactor management of hardware audio sources to make use of ClientDescriptor class: - HW sources are now tracked by port ID and not audio patch handle. - startAudioSource() and stopAudioSource() APIs are updated to use audio_port_handle_t as source identifier. - AudioSourceDescriptor class is deleted and replaced by SourceClientDescriptor class deriving from TrackClientDescriptor Test: make. Change-Id: Ie418b566519a591f036b538a77319f8e30aa99a8 --- media/libaudioclient/AudioSystem.cpp | 8 +- media/libaudioclient/IAudioPolicyService.cpp | 20 ++-- .../include/media/AudioSystem.h | 6 +- .../include/media/IAudioPolicyService.h | 4 +- services/audiopolicy/AudioPolicyInterface.h | 4 +- .../common/managerdefinitions/Android.mk | 1 - .../include/AudioOutputDescriptor.h | 5 +- .../include/AudioSourceDescriptor.h | 59 ---------- .../include/ClientDescriptor.h | 42 +++++++ .../src/AudioOutputDescriptor.cpp | 10 +- .../src/AudioSourceDescriptor.cpp | 64 ----------- .../src/ClientDescriptor.cpp | 55 +++++++++ .../managerdefault/AudioPolicyManager.cpp | 106 ++++++++++-------- .../managerdefault/AudioPolicyManager.h | 12 +- .../service/AudioPolicyInterfaceImpl.cpp | 10 +- .../audiopolicy/service/AudioPolicyService.h | 4 +- 16 files changed, 195 insertions(+), 215 deletions(-) delete mode 100644 services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h delete mode 100644 services/audiopolicy/common/managerdefinitions/src/AudioSourceDescriptor.cpp diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index cb4bcfc750..e260fd838a 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1238,18 +1238,18 @@ status_t AudioSystem::registerPolicyMixes(const Vector& mixes, bool re status_t AudioSystem::startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle) + audio_port_handle_t *portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->startAudioSource(source, attributes, handle); + return aps->startAudioSource(source, attributes, portId); } -status_t AudioSystem::stopAudioSource(audio_patch_handle_t handle) +status_t AudioSystem::stopAudioSource(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->stopAudioSource(handle); + return aps->stopAudioSource(portId); } status_t AudioSystem::setMasterMono(bool mono) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index e229f4c7b0..73a8b747b4 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -740,11 +740,11 @@ public: virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle) + audio_port_handle_t *portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - if (source == NULL || attributes == NULL || handle == NULL) { + if (source == NULL || attributes == NULL || portId == NULL) { return BAD_VALUE; } data.write(source, sizeof(struct audio_port_config)); @@ -757,15 +757,15 @@ public: if (status != NO_ERROR) { return status; } - *handle = (audio_patch_handle_t)reply.readInt32(); + *portId = (audio_port_handle_t)reply.readInt32(); return status; } - virtual status_t stopAudioSource(audio_patch_handle_t handle) + virtual status_t stopAudioSource(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - data.writeInt32(handle); + data.writeInt32(portId); status_t status = remote()->transact(STOP_AUDIO_SOURCE, data, &reply); if (status != NO_ERROR) { return status; @@ -1472,17 +1472,17 @@ status_t BnAudioPolicyService::onTransact( audio_attributes_t attributes = {}; data.read(&attributes, sizeof(audio_attributes_t)); sanetizeAudioAttributes(&attributes); - audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; - status_t status = startAudioSource(&source, &attributes, &handle); + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE; + status_t status = startAudioSource(&source, &attributes, &portId); reply->writeInt32(status); - reply->writeInt32(handle); + reply->writeInt32(portId); return NO_ERROR; } break; case STOP_AUDIO_SOURCE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_patch_handle_t handle = (audio_patch_handle_t) data.readInt32(); - status_t status = stopAudioSource(handle); + audio_port_handle_t portId = (audio_port_handle_t) data.readInt32(); + status_t status = stopAudioSource(portId); reply->writeInt32(status); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 10d6e92f51..adfee8b7c4 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -322,9 +322,9 @@ public: static status_t registerPolicyMixes(const Vector& mixes, bool registration); static status_t startAudioSource(const struct audio_port_config *source, - const audio_attributes_t *attributes, - audio_patch_handle_t *handle); - static status_t stopAudioSource(audio_patch_handle_t handle); + const audio_attributes_t *attributes, + audio_port_handle_t *portId); + static status_t stopAudioSource(audio_port_handle_t portId); static status_t setMasterMono(bool mono); static status_t getMasterMono(bool *mono); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 6c017a36d2..cdbb8760c1 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -153,8 +153,8 @@ public: virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle) = 0; - virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0; + audio_port_handle_t *portId) = 0; + virtual status_t stopAudioSource(audio_port_handle_t portId) = 0; virtual status_t setMasterMono(bool mono) = 0; virtual status_t getMasterMono(bool *mono) = 0; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index c9e99d5cba..d4c49d9f0e 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -226,9 +226,9 @@ public: virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle, + audio_port_handle_t *portId, uid_t uid) = 0; - virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0; + virtual status_t stopAudioSource(audio_port_handle_t portId) = 0; virtual status_t setMasterMono(bool mono) = 0; virtual status_t getMasterMono(bool *mono) = 0; diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index 09dbb3293d..9b8f0956c6 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES:= \ src/EffectDescriptor.cpp \ src/SoundTriggerSession.cpp \ src/SessionRoute.cpp \ - src/AudioSourceDescriptor.cpp \ src/VolumeCurve.cpp \ src/TypeConverter.cpp \ src/AudioSession.cpp \ diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index e6112bfb24..ff0201a268 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -24,7 +24,6 @@ #include #include "AudioIODescriptorInterface.h" #include "AudioPort.h" -#include "AudioSourceDescriptor.h" #include "ClientDescriptor.h" namespace android { @@ -159,7 +158,7 @@ public: class HwAudioOutputDescriptor: public AudioOutputDescriptor { public: - HwAudioOutputDescriptor(const sp& source, + HwAudioOutputDescriptor(const sp& source, AudioPolicyClientInterface *clientInterface); virtual ~HwAudioOutputDescriptor() {} @@ -176,7 +175,7 @@ public: const struct audio_port_config *srcConfig = NULL) const; virtual void toAudioPort(struct audio_port *port) const; - const sp mSource; + const sp mSource; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h deleted file mode 100644 index 0d90f42eca..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace android { - -class SwAudioOutputDescriptor; -class HwAudioOutputDescriptor; -class DeviceDescriptor; - -class AudioSourceDescriptor: public RefBase -{ -public: - AudioSourceDescriptor(const sp device, const audio_attributes_t *attributes, - uid_t uid) : - mDevice(device), mAttributes(*attributes), mUid(uid) {} - virtual ~AudioSourceDescriptor() {} - - audio_patch_handle_t getHandle() const { return mPatchDesc->mHandle; } - - status_t dump(int fd); - - const sp mDevice; - const audio_attributes_t mAttributes; - uid_t mUid; - sp mPatchDesc; - wp mSwOutput; - wp mHwOutput; -}; - -class AudioSourceCollection : - public DefaultKeyedVector< audio_patch_handle_t, sp > -{ -public: - status_t dump(int fd) const; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 221c2e96ee..9efe57fe38 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -23,11 +23,17 @@ #include #include +#include #include #include +#include "AudioPatch.h" namespace android { +class DeviceDescriptor; +class HwAudioOutputDescriptor; +class SwAudioOutputDescriptor; + class ClientDescriptor: public RefBase { public: @@ -58,6 +64,10 @@ private: const audio_config_base_t mConfig; const audio_port_handle_t mPreferredDeviceId; // selected input device port ID bool mActive; + +protected: + // FIXME: use until other descriptor classes have a dump to String8 method + int mDumpFd; }; class TrackClientDescriptor: public ClientDescriptor @@ -104,6 +114,38 @@ private: const audio_input_flags_t mFlags; }; +class SourceClientDescriptor: public TrackClientDescriptor +{ +public: + SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, + const sp& patchDesc, const sp& srcDevice, + audio_stream_type_t stream); + ~SourceClientDescriptor() override = default; + + sp patchDesc() const { return mPatchDesc; } + sp srcDevice() const { return mSrcDevice; }; + wp swOutput() const { return mSwOutput; } + void setSwOutput(const sp& swOutput); + wp hwOutput() const { return mHwOutput; } + void setHwOutput(const sp& hwOutput); + + using ClientDescriptor::dump; + status_t dump(String8& dst, int spaces, int index) override; + + private: + const sp mPatchDesc; + const sp mSrcDevice; + wp mSwOutput; + wp mHwOutput; +}; + +class SourceClientCollection : + public DefaultKeyedVector< audio_port_handle_t, sp > +{ +public: + status_t dump(int fd) const; +}; + typedef std::vector< sp > TrackClientVector; typedef std::map< audio_port_handle_t, sp > TrackClientMap; typedef std::vector< sp > RecordClientVector; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 3dfbe1b111..39fce4db59 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -558,9 +558,9 @@ status_t SwAudioOutputDescriptor::openDuplicating(const sp& source, +HwAudioOutputDescriptor::HwAudioOutputDescriptor(const sp& source, AudioPolicyClientInterface *clientInterface) - : AudioOutputDescriptor(source->mDevice, clientInterface), + : AudioOutputDescriptor(source->srcDevice(), clientInterface), mSource(source) { } @@ -576,7 +576,7 @@ status_t HwAudioOutputDescriptor::dump(int fd) snprintf(buffer, SIZE, "Source:\n"); result.append(buffer); write(fd, result.string(), result.size()); - mSource->dump(fd); + mSource->dump(fd, 0, 0); return NO_ERROR; } @@ -590,13 +590,13 @@ void HwAudioOutputDescriptor::toAudioPortConfig( struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const { - mSource->mDevice->toAudioPortConfig(dstConfig, srcConfig); + mSource->srcDevice()->toAudioPortConfig(dstConfig, srcConfig); } void HwAudioOutputDescriptor::toAudioPort( struct audio_port *port) const { - mSource->mDevice->toAudioPort(port); + mSource->srcDevice()->toAudioPort(port); } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSourceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSourceDescriptor.cpp deleted file mode 100644 index ba33e57d2e..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSourceDescriptor.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::AudioSourceDescriptor" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -status_t AudioSourceDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "mStream: %d\n", audio_attributes_to_stream_type(&mAttributes)); - result.append(buffer); - snprintf(buffer, SIZE, "mDevice:\n"); - result.append(buffer); - write(fd, result.string(), result.size()); - mDevice->dump(fd, 2 , 0); - return NO_ERROR; -} - - -status_t AudioSourceCollection::dump(int fd) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - - snprintf(buffer, SIZE, "\nAudio sources dump:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < size(); i++) { - snprintf(buffer, SIZE, "- Source %d dump:\n", keyAt(i)); - write(fd, buffer, strlen(buffer)); - valueAt(i)->dump(fd); - } - - return NO_ERROR; -} - -}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index bdc748ebd7..5aca3cc4a9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -19,7 +19,13 @@ #include #include +#include "AudioGain.h" +#include "AudioOutputDescriptor.h" +#include "AudioPatch.h" #include "ClientDescriptor.h" +#include "DeviceDescriptor.h" +#include "HwModule.h" +#include "IOProfile.h" namespace android { @@ -27,6 +33,9 @@ status_t ClientDescriptor::dump(int fd, int spaces, int index) { String8 out; + // FIXME: use until other descriptor classes have a dump to String8 method + mDumpFd = fd; + status_t status = dump(out, spaces, index); if (status == NO_ERROR) { write(fd, out.string(), out.size()); @@ -65,4 +74,50 @@ status_t RecordClientDescriptor::dump(String8& out, int spaces, int index) return NO_ERROR; } +SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, + audio_attributes_t attributes, const sp& patchDesc, + const sp& srcDevice, audio_stream_type_t stream) : + TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, + AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, stream, AUDIO_OUTPUT_FLAG_NONE), + mPatchDesc(patchDesc), mSrcDevice(srcDevice) +{ +} + +void SourceClientDescriptor::setSwOutput(const sp& swOutput) +{ + mSwOutput = swOutput; +} + +void SourceClientDescriptor::setHwOutput(const sp& hwOutput) +{ + mHwOutput = hwOutput; +} + +status_t SourceClientDescriptor::dump(String8& out, int spaces, int index) +{ + TrackClientDescriptor::dump(out, spaces, index); + + if (mDumpFd >= 0) { + out.appendFormat("%*s- Device:\n", spaces, ""); + write(mDumpFd, out.string(), out.size()); + + mSrcDevice->dump(mDumpFd, 2, 0); + mDumpFd = -1; + } + + return NO_ERROR; +} + +status_t SourceClientCollection::dump(int fd) const +{ + String8 out; + out.append("\nAudio sources:\n"); + write(fd, out.string(), out.size()); + for (size_t i = 0; i < size(); i++) { + valueAt(i)->dump(fd, 2, i); + } + + return NO_ERROR; +} + }; //namespace android diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 47e17f113f..92f6c79701 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2794,6 +2794,7 @@ status_t AudioPolicyManager::dump(int fd) mEffects.dump(fd); mAudioPatches.dump(fd); mPolicyMixes.dump(fd); + mAudioSources.dump(fd); return NO_ERROR; } @@ -3436,8 +3437,8 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) void AudioPolicyManager::clearAudioSources(uid_t uid) { for (ssize_t i = (ssize_t)mAudioSources.size() - 1; i >= 0; i--) { - sp sourceDesc = mAudioSources.valueAt(i); - if (sourceDesc->mUid == uid) { + sp sourceDesc = mAudioSources.valueAt(i); + if (sourceDesc->uid() == uid) { stopAudioSource(mAudioSources.keyAt(i)); } } @@ -3455,20 +3456,23 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session } status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source, - const audio_attributes_t *attributes, - audio_patch_handle_t *handle, - uid_t uid) + const audio_attributes_t *attributes, + audio_port_handle_t *portId, + uid_t uid) { - ALOGV("%s source %p attributes %p handle %p", __FUNCTION__, source, attributes, handle); - if (source == NULL || attributes == NULL || handle == NULL) { + ALOGV("%s", __FUNCTION__); + *portId = AUDIO_PORT_HANDLE_NONE; + + if (source == NULL || attributes == NULL || portId == NULL) { + ALOGW("%s invalid argument: source %p attributes %p handle %p", + __FUNCTION__, source, attributes, portId); return BAD_VALUE; } - *handle = AUDIO_PATCH_HANDLE_NONE; - if (source->role != AUDIO_PORT_ROLE_SOURCE || source->type != AUDIO_PORT_TYPE_DEVICE) { - ALOGV("%s INVALID_OPERATION source->role %d source->type %d", __FUNCTION__, source->role, source->type); + ALOGW("%s INVALID_OPERATION source->role %d source->type %d", + __FUNCTION__, source->role, source->type); return INVALID_OPERATION; } @@ -3476,34 +3480,37 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so mAvailableInputDevices.getDevice(source->ext.device.type, String8(source->ext.device.address)); if (srcDeviceDesc == 0) { - ALOGV("%s source->ext.device.type %08x not found", __FUNCTION__, source->ext.device.type); + ALOGW("%s source->ext.device.type %08x not found", __FUNCTION__, source->ext.device.type); return BAD_VALUE; } - sp sourceDesc = - new AudioSourceDescriptor(srcDeviceDesc, attributes, uid); + + *portId = AudioPort::getNextUniqueId(); struct audio_patch dummyPatch = {}; sp patchDesc = new AudioPatch(&dummyPatch, uid); - sourceDesc->mPatchDesc = patchDesc; + + sp sourceDesc = + new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc, + streamTypefromAttributesInt(attributes)); status_t status = connectAudioSource(sourceDesc); if (status == NO_ERROR) { - mAudioSources.add(sourceDesc->getHandle(), sourceDesc); - *handle = sourceDesc->getHandle(); + mAudioSources.add(*portId, sourceDesc); } return status; } -status_t AudioPolicyManager::connectAudioSource(const sp& sourceDesc) +status_t AudioPolicyManager::connectAudioSource(const sp& sourceDesc) { - ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle()); + ALOGV("%s handle %d", __FUNCTION__, sourceDesc->portId()); // make sure we only have one patch per source. disconnectAudioSource(sourceDesc); - routing_strategy strategy = (routing_strategy) getStrategyForAttr(&sourceDesc->mAttributes); - audio_stream_type_t stream = streamTypefromAttributesInt(&sourceDesc->mAttributes); - sp srcDeviceDesc = sourceDesc->mDevice; + audio_attributes_t attributes = sourceDesc->attributes(); + routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); + audio_stream_type_t stream = sourceDesc->stream(); + sp srcDeviceDesc = sourceDesc->srcDevice(); audio_devices_t sinkDevice = getDeviceForStrategy(strategy, true); sp sinkDeviceDesc = @@ -3548,7 +3555,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp& 0); ALOGV("%s patch panel returned %d patchHandle %d", __FUNCTION__, status, afPatchHandle); - sourceDesc->mPatchDesc->mPatch = *patchBuilder.patch(); + sourceDesc->patchDesc()->mPatch = *patchBuilder.patch(); if (status != NO_ERROR) { ALOGW("%s patch panel could not connect device patch, error %d", __FUNCTION__, status); @@ -3558,32 +3565,32 @@ status_t AudioPolicyManager::connectAudioSource(const sp& status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs); if (status != NO_ERROR) { - mpClientInterface->releaseAudioPatch(sourceDesc->mPatchDesc->mAfPatchHandle, 0); + mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0); return status; } - sourceDesc->mSwOutput = outputDesc; + sourceDesc->setSwOutput(outputDesc); if (delayMs != 0) { usleep(delayMs * 1000); } } - sourceDesc->mPatchDesc->mAfPatchHandle = afPatchHandle; - addAudioPatch(sourceDesc->mPatchDesc->mHandle, sourceDesc->mPatchDesc); + sourceDesc->patchDesc()->mAfPatchHandle = afPatchHandle; + addAudioPatch(sourceDesc->patchDesc()->mHandle, sourceDesc->patchDesc()); return NO_ERROR; } -status_t AudioPolicyManager::stopAudioSource(audio_patch_handle_t handle) +status_t AudioPolicyManager::stopAudioSource(audio_port_handle_t portId) { - sp sourceDesc = mAudioSources.valueFor(handle); - ALOGV("%s handle %d", __FUNCTION__, handle); + sp sourceDesc = mAudioSources.valueFor(portId); + ALOGV("%s port ID %d", __FUNCTION__, portId); if (sourceDesc == 0) { - ALOGW("%s unknown source for handle %d", __FUNCTION__, handle); + ALOGW("%s unknown source for port ID %d", __FUNCTION__, portId); return BAD_VALUE; } status_t status = disconnectAudioSource(sourceDesc); - mAudioSources.removeItem(handle); + mAudioSources.removeItem(portId); return status; } @@ -3917,20 +3924,20 @@ void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced) } } -status_t AudioPolicyManager::disconnectAudioSource(const sp& sourceDesc) +status_t AudioPolicyManager::disconnectAudioSource(const sp& sourceDesc) { - ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle()); + ALOGV("%s port Id %d", __FUNCTION__, sourceDesc->portId()); - sp patchDesc = mAudioPatches.valueFor(sourceDesc->mPatchDesc->mHandle); + sp patchDesc = mAudioPatches.valueFor(sourceDesc->patchDesc()->mHandle); if (patchDesc == 0) { ALOGW("%s source has no patch with handle %d", __FUNCTION__, - sourceDesc->mPatchDesc->mHandle); + sourceDesc->patchDesc()->mHandle); return BAD_VALUE; } - removeAudioPatch(sourceDesc->mPatchDesc->mHandle); + removeAudioPatch(sourceDesc->patchDesc()->mHandle); - audio_stream_type_t stream = streamTypefromAttributesInt(&sourceDesc->mAttributes); - sp swOutputDesc = sourceDesc->mSwOutput.promote(); + audio_stream_type_t stream = sourceDesc->stream(); + sp swOutputDesc = sourceDesc->swOutput().promote(); if (swOutputDesc != 0) { status_t status = stopSource(swOutputDesc, stream, false); if (status == NO_ERROR) { @@ -3938,7 +3945,7 @@ status_t AudioPolicyManager::disconnectAudioSource(const spreleaseAudioPatch(patchDesc->mAfPatchHandle, 0); } else { - sp hwOutputDesc = sourceDesc->mHwOutput.promote(); + sp hwOutputDesc = sourceDesc->hwOutput().promote(); if (hwOutputDesc != 0) { // release patch between src device and output device // close Hwoutput and remove from mHwOutputs @@ -3949,15 +3956,16 @@ status_t AudioPolicyManager::disconnectAudioSource(const sp AudioPolicyManager::getSourceForStrategyOnOutput( +sp AudioPolicyManager::getSourceForStrategyOnOutput( audio_io_handle_t output, routing_strategy strategy) { - sp source; + sp source; for (size_t i = 0; i < mAudioSources.size(); i++) { - sp sourceDesc = mAudioSources.valueAt(i); + sp sourceDesc = mAudioSources.valueAt(i); + audio_attributes_t attributes = sourceDesc->attributes(); routing_strategy sourceStrategy = - (routing_strategy) getStrategyForAttr(&sourceDesc->mAttributes); - sp outputDesc = sourceDesc->mSwOutput.promote(); + (routing_strategy) getStrategyForAttr(&attributes); + sp outputDesc = sourceDesc->swOutput().promote(); if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) { source = sourceDesc; break; @@ -4841,7 +4849,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) setStrategyMute(strategy, true, desc); setStrategyMute(strategy, false, desc, maxLatency * LATENCY_MUTE_FACTOR, newDevice); } - sp source = + sp source = getSourceForStrategyOnOutput(srcOut, strategy); if (source != 0){ connectAudioSource(source); @@ -5922,10 +5930,10 @@ bool AudioPolicyManager::isStateInCall(int state) void AudioPolicyManager::cleanUpForDevice(const sp& deviceDesc) { for (ssize_t i = (ssize_t)mAudioSources.size() - 1; i >= 0; i--) { - sp sourceDesc = mAudioSources.valueAt(i); - if (sourceDesc->mDevice->equals(deviceDesc)) { - ALOGV("%s releasing audio source %d", __FUNCTION__, sourceDesc->getHandle()); - stopAudioSource(sourceDesc->getHandle()); + sp sourceDesc = mAudioSources.valueAt(i); + if (sourceDesc->srcDevice()->equals(deviceDesc)) { + ALOGV("%s releasing audio source %d", __FUNCTION__, sourceDesc->portId()); + stopAudioSource(sourceDesc->portId()); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 869cd9d08b..943676772d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -223,9 +223,9 @@ public: virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle, + audio_port_handle_t *portId, uid_t uid); - virtual status_t stopAudioSource(audio_patch_handle_t handle); + virtual status_t stopAudioSource(audio_port_handle_t portId); virtual status_t setMasterMono(bool mono); virtual status_t getMasterMono(bool *mono); @@ -525,10 +525,10 @@ protected: status_t hasPrimaryOutput() const { return mPrimaryOutput != 0; } - status_t connectAudioSource(const sp& sourceDesc); - status_t disconnectAudioSource(const sp& sourceDesc); + status_t connectAudioSource(const sp& sourceDesc); + status_t disconnectAudioSource(const sp& sourceDesc); - sp getSourceForStrategyOnOutput(audio_io_handle_t output, + sp getSourceForStrategyOnOutput(audio_io_handle_t output, routing_strategy strategy); void cleanUpForDevice(const sp& deviceDesc); @@ -587,7 +587,7 @@ protected: sp mCallRxPatch; HwAudioOutputCollection mHwOutputs; - AudioSourceCollection mAudioSources; + SourceClientCollection mAudioSources; // for supporting "beacon" streams, i.e. streams that only play on speaker, and never // when something other than STREAM_TTS (a.k.a. "Transmitted Through Speaker") is playing diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index fdfd573143..dbfda44e5e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -990,26 +990,26 @@ status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, } status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source, - const audio_attributes_t *attributes, - audio_patch_handle_t *handle) + const audio_attributes_t *attributes, + audio_port_handle_t *portId) { Mutex::Autolock _l(mLock); if (mAudioPolicyManager == NULL) { return NO_INIT; } AutoCallerClear acc; - return mAudioPolicyManager->startAudioSource(source, attributes, handle, + return mAudioPolicyManager->startAudioSource(source, attributes, portId, IPCThreadState::self()->getCallingUid()); } -status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle) +status_t AudioPolicyService::stopAudioSource(audio_port_handle_t portId) { Mutex::Autolock _l(mLock); if (mAudioPolicyManager == NULL) { return NO_INIT; } AutoCallerClear acc; - return mAudioPolicyManager->stopAudioSource(handle); + return mAudioPolicyManager->stopAudioSource(portId); } status_t AudioPolicyService::setMasterMono(bool mono) diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index d41069ef61..6a256683ce 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -184,8 +184,8 @@ public: virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, - audio_patch_handle_t *handle); - virtual status_t stopAudioSource(audio_patch_handle_t handle); + audio_port_handle_t *portId); + virtual status_t stopAudioSource(audio_port_handle_t portId); virtual status_t setMasterMono(bool mono); virtual status_t getMasterMono(bool *mono); -- GitLab From 62e83c9512486a24f0cfd3fe25ba5f2b4f2c2fe5 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 31 Jul 2018 15:25:58 -0700 Subject: [PATCH 0168/1530] Check for crypto size overflow Bug: 111922341 Test: build Change-Id: Icfe2d15c13f1b4bb77e0a9684ffc1284f557e6d5 --- media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp index bde08622b6..8d876dabe8 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp @@ -239,8 +239,14 @@ NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo( size_t *encryptedbytes) { // size needed to store all the crypto data - size_t cryptosize = sizeof(CryptoInfo) + - sizeof(CryptoPlugin::SubSample) * numSubSamples; + size_t cryptosize; + // sizeof(CryptoInfo) + sizeof(CryptoPlugin::SubSample) * numSubSamples; + if (__builtin_mul_overflow(sizeof(CryptoPlugin::SubSample), numSubSamples, &cryptosize) || + __builtin_add_overflow(cryptosize, sizeof(CryptoInfo), &cryptosize)) { + ALOGE("crypto size overflow"); + return NULL; + } + CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize); if (ret == NULL) { ALOGE("couldn't allocate %zu bytes", cryptosize); -- GitLab From cad5a3a204aca14bf4e6f9aba169f82026549b5e Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Tue, 31 Jul 2018 17:03:56 -0700 Subject: [PATCH 0169/1530] mediaplayer2: rename some MEDIA2_INFO_* constants Test: cts tests Bug: 109928575 Change-Id: I186e5c5c43f67cc594fc34fbb0d996587fb98ffb --- .../include/mediaplayer2/MediaPlayer2Types.h | 12 ++++++------ media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h index 2fb5a2c7a2..bc84729145 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h @@ -96,17 +96,17 @@ enum media2_error_type { enum media2_info_type { // 0xx MEDIA2_INFO_UNKNOWN = 1, - // The player was started because it was used as the next player for another - // player, which just completed playback - MEDIA2_INFO_STARTED_AS_NEXT = 2, + // The player just started the playback of this data source. + MEDIA2_INFO_DATA_SOURCE_START = 2, // The player just pushed the very first video frame for rendering MEDIA2_INFO_VIDEO_RENDERING_START = 3, // The player just pushed the very first audio frame for rendering MEDIA2_INFO_AUDIO_RENDERING_START = 4, // The player just completed the playback of this data source - MEDIA2_INFO_PLAYBACK_COMPLETE = 5, - // The player just completed the playback of the full play list - MEDIA2_INFO_PLAYLIST_END = 6, + MEDIA2_INFO_DATA_SOURCE_END = 5, + // The player just completed the playback of all data sources. + // But this is not visible in native code. Just keep this entry for completeness. + MEDIA2_INFO_DATA_SOURCE_LIST_END = 6, //1xx // The player just prepared a data source. diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 060b698881..c649573b9b 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -2474,8 +2474,8 @@ void NuPlayer2::performPlayNextDataSource() { if (mDriver != NULL) { sp driver = mDriver.promote(); if (driver != NULL) { - notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAYBACK_COMPLETE, 0); - notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0); + notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_END, 0); + notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_START, 0); } } -- GitLab From 2046ec756ee59acc4cfa1d52eb97aaa84efc9754 Mon Sep 17 00:00:00 2001 From: Ari Hausman-Cohen Date: Tue, 24 Apr 2018 14:00:55 -0700 Subject: [PATCH 0170/1530] Update getEffectDescriptors() to include type Allows getting effect descriptors by type or uuid, not just by uuid. Bug: 78528249 Test: Builds. Manually tested that an app playing audio with an effect works. Change-Id: I9d339a7d6d81161065e1adaf427dd2d3430436c2 --- media/libaudioclient/AudioEffect.cpp | 7 +- media/libaudioclient/IAudioFlinger.cpp | 13 +- .../include/media/AudioEffect.h | 17 ++- .../include/media/IAudioFlinger.h | 4 +- services/audioflinger/AudioFlinger.cpp | 131 ++++++++++-------- services/audioflinger/AudioFlinger.h | 2 + 6 files changed, 103 insertions(+), 71 deletions(-) diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp index b1cb0e7bdd..cb46b93c05 100644 --- a/media/libaudioclient/AudioEffect.cpp +++ b/media/libaudioclient/AudioEffect.cpp @@ -430,14 +430,15 @@ status_t AudioEffect::queryEffect(uint32_t index, effect_descriptor_t *descripto } status_t AudioEffect::getEffectDescriptor(const effect_uuid_t *uuid, - effect_descriptor_t *descriptor) /*const*/ + const effect_uuid_t *type, + uint32_t preferredTypeFlag, + effect_descriptor_t *descriptor) { const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return PERMISSION_DENIED; - return af->getEffectDescriptor(uuid, descriptor); + return af->getEffectDescriptor(uuid, type, preferredTypeFlag, descriptor); } - status_t AudioEffect::queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count) diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 84e8beef17..aca95db0c9 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -598,14 +598,18 @@ public: } virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid, - effect_descriptor_t *pDescriptor) const + const effect_uuid_t *pType, + uint32_t preferredTypeFlag, + effect_descriptor_t *pDescriptor) const { - if (pUuid == NULL || pDescriptor == NULL) { + if (pUuid == NULL || pType == NULL || pDescriptor == NULL) { return BAD_VALUE; } Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.write(pUuid, sizeof(effect_uuid_t)); + data.write(pType, sizeof(effect_uuid_t)); + data.writeUint32(preferredTypeFlag); status_t status = remote()->transact(GET_EFFECT_DESCRIPTOR, data, &reply); if (status != NO_ERROR) { return status; @@ -1277,8 +1281,11 @@ status_t BnAudioFlinger::onTransact( CHECK_INTERFACE(IAudioFlinger, data, reply); effect_uuid_t uuid; data.read(&uuid, sizeof(effect_uuid_t)); + effect_uuid_t type; + data.read(&type, sizeof(effect_uuid_t)); + uint32_t preferredTypeFlag = data.readUint32(); effect_descriptor_t desc = {}; - status_t status = getEffectDescriptor(&uuid, &desc); + status_t status = getEffectDescriptor(&uuid, &type, preferredTypeFlag, &desc); reply->writeInt32(status); if (status == NO_ERROR) { reply->write(&desc, sizeof(effect_descriptor_t)); diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h index bfc068b389..ae66e8f2d4 100644 --- a/media/libaudioclient/include/media/AudioEffect.h +++ b/media/libaudioclient/include/media/AudioEffect.h @@ -90,27 +90,34 @@ public: */ static status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor); - /* - * Returns the descriptor for the specified effect uuid. + * Returns a descriptor for the specified effect uuid or type. + * + * Lookup an effect by uuid, or if that's unspecified (EFFECT_UUID_NULL), + * do so by type and preferred flags instead. * * Parameters: * uuid: pointer to effect uuid. + * type: pointer to effect type uuid. + * preferredTypeFlags: if multiple effects of the given type exist, + * one with a matching type flag will be chosen over one without. + * Use EFFECT_FLAG_TYPE_MASK to indicate no preference. * descriptor: address where the effect descriptor should be returned. * * Returned status (from utils/Errors.h) can be: * NO_ERROR successful operation. * PERMISSION_DENIED could not get AudioFlinger interface * NO_INIT effect library failed to initialize - * BAD_VALUE invalid uuid or descriptor pointers + * BAD_VALUE invalid type or descriptor pointers * NAME_NOT_FOUND no effect with this uuid found * * Returned value * *descriptor updated with effect descriptor */ static status_t getEffectDescriptor(const effect_uuid_t *uuid, - effect_descriptor_t *descriptor) /*const*/; - + const effect_uuid_t *type, + uint32_t preferredTypeFlag, + effect_descriptor_t *descriptor); /* * Returns a list of descriptors corresponding to the pre processings enabled by default diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index e6bf72f547..31326abf46 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -428,7 +428,9 @@ public: virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) const = 0; virtual status_t getEffectDescriptor(const effect_uuid_t *pEffectUUID, - effect_descriptor_t *pDescriptor) const = 0; + const effect_uuid_t *pTypeUUID, + uint32_t preferredTypeFlag, + effect_descriptor_t *pDescriptor) const = 0; virtual sp createEffect( effect_descriptor_t *pDesc, diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 9234364248..38483c35a5 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2949,16 +2949,74 @@ status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descript } status_t AudioFlinger::getEffectDescriptor(const effect_uuid_t *pUuid, - effect_descriptor_t *descriptor) const + const effect_uuid_t *pTypeUuid, + uint32_t preferredTypeFlag, + effect_descriptor_t *descriptor) const { + if (pUuid == NULL || pTypeUuid == NULL || descriptor == NULL) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); - if (mEffectsFactoryHal.get()) { - return mEffectsFactoryHal->getDescriptor(pUuid, descriptor); - } else { + + if (!mEffectsFactoryHal.get()) { return -ENODEV; } -} + status_t status = NO_ERROR; + if (!EffectsFactoryHalInterface::isNullUuid(pUuid)) { + // If uuid is specified, request effect descriptor from that. + status = mEffectsFactoryHal->getDescriptor(pUuid, descriptor); + } else if (!EffectsFactoryHalInterface::isNullUuid(pTypeUuid)) { + // If uuid is not specified, look for an available implementation + // of the required type instead. + + // Use a temporary descriptor to avoid modifying |descriptor| in the failure case. + effect_descriptor_t desc; + desc.flags = 0; // prevent compiler warning + + uint32_t numEffects = 0; + status = mEffectsFactoryHal->queryNumberEffects(&numEffects); + if (status < 0) { + ALOGW("getEffectDescriptor() error %d from FactoryHal queryNumberEffects", status); + return status; + } + + bool found = false; + for (uint32_t i = 0; i < numEffects; i++) { + status = mEffectsFactoryHal->getDescriptor(i, &desc); + if (status < 0) { + ALOGW("getEffectDescriptor() error %d from FactoryHal getDescriptor", status); + continue; + } + if (memcmp(&desc.type, pTypeUuid, sizeof(effect_uuid_t)) == 0) { + // If matching type found save effect descriptor. + found = true; + *descriptor = desc; + + // If there's no preferred flag or this descriptor matches the preferred + // flag, success! If this descriptor doesn't match the preferred + // flag, continue enumeration in case a better matching version of this + // effect type is available. Note that this means if no effect with a + // correct flag is found, the descriptor returned will correspond to the + // last effect that at least had a matching type uuid (if any). + if (preferredTypeFlag == EFFECT_FLAG_TYPE_MASK || + (desc.flags & EFFECT_FLAG_TYPE_MASK) == preferredTypeFlag) { + break; + } + } + } + + if (!found) { + status = NAME_NOT_FOUND; + ALOGW("getEffectDescriptor(): Effect not found by type."); + } + } else { + status = BAD_VALUE; + ALOGE("getEffectDescriptor(): Either uuid or type uuid must be non-null UUIDs."); + } + return status; +} sp AudioFlinger::createEffect( effect_descriptor_t *pDesc, @@ -3012,60 +3070,15 @@ sp AudioFlinger::createEffect( } { - if (!EffectsFactoryHalInterface::isNullUuid(&pDesc->uuid)) { - // if uuid is specified, request effect descriptor - lStatus = mEffectsFactoryHal->getDescriptor(&pDesc->uuid, &desc); - if (lStatus < 0) { - ALOGW("createEffect() error %d from EffectGetDescriptor", lStatus); - goto Exit; - } - } else { - // if uuid is not specified, look for an available implementation - // of the required type in effect factory - if (EffectsFactoryHalInterface::isNullUuid(&pDesc->type)) { - ALOGW("createEffect() no effect type"); - lStatus = BAD_VALUE; - goto Exit; - } - uint32_t numEffects = 0; - effect_descriptor_t d; - d.flags = 0; // prevent compiler warning - bool found = false; - - lStatus = mEffectsFactoryHal->queryNumberEffects(&numEffects); - if (lStatus < 0) { - ALOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus); - goto Exit; - } - for (uint32_t i = 0; i < numEffects; i++) { - lStatus = mEffectsFactoryHal->getDescriptor(i, &desc); - if (lStatus < 0) { - ALOGW("createEffect() error %d from EffectQueryEffect", lStatus); - continue; - } - if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) { - // If matching type found save effect descriptor. If the session is - // 0 and the effect is not auxiliary, continue enumeration in case - // an auxiliary version of this effect type is available - found = true; - d = desc; - if (sessionId != AUDIO_SESSION_OUTPUT_MIX || - (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { - break; - } - } - } - if (!found) { - lStatus = BAD_VALUE; - ALOGW("createEffect() effect not found"); - goto Exit; - } - // For same effect type, chose auxiliary version over insert version if - // connect to output mix (Compliance to OpenSL ES) - if (sessionId == AUDIO_SESSION_OUTPUT_MIX && - (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) { - desc = d; - } + // Get the full effect descriptor from the uuid/type. + // If the session is the output mix, prefer an auxiliary effect, + // otherwise no preference. + uint32_t preferredType = (sessionId == AUDIO_SESSION_OUTPUT_MIX ? + EFFECT_FLAG_TYPE_AUXILIARY : EFFECT_FLAG_TYPE_MASK); + lStatus = getEffectDescriptor(&pDesc->uuid, &pDesc->type, preferredType, &desc); + if (lStatus < 0) { + ALOGW("createEffect() error %d from getEffectDescriptor", lStatus); + goto Exit; } // Do not allow auxiliary effects on a session different from 0 (output mix) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 95b947cbdf..9b9a15dfe2 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -208,6 +208,8 @@ public: virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor) const; virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid, + const effect_uuid_t *pTypeUuid, + uint32_t preferredTypeFlag, effect_descriptor_t *descriptor) const; virtual sp createEffect( -- GitLab From 5c00d01c85430178d19f6266f1bc5c94a29853c1 Mon Sep 17 00:00:00 2001 From: Ari Hausman-Cohen Date: Tue, 26 Jun 2018 17:09:11 -0700 Subject: [PATCH 0171/1530] Add helper method to convert usage to stream type. Adds audio_usage_to_stream_type method to aid in converting audio_usage_t to the legacy audio_stream_type_t. Bug: 78527120 Test: Builds Change-Id: I411bb687d0f46b82afef3a2e9ac6a974f922da91 --- .../include/media/AudioPolicyHelper.h | 70 ++++++++++--------- .../managerdefault/AudioPolicyManager.cpp | 34 +-------- 2 files changed, 39 insertions(+), 65 deletions(-) diff --git a/media/libaudioclient/include/media/AudioPolicyHelper.h b/media/libaudioclient/include/media/AudioPolicyHelper.h index 73ee0a7378..35d2e85b61 100644 --- a/media/libaudioclient/include/media/AudioPolicyHelper.h +++ b/media/libaudioclient/include/media/AudioPolicyHelper.h @@ -18,6 +18,43 @@ #include +static inline +audio_stream_type_t audio_usage_to_stream_type(const audio_usage_t usage) +{ + switch(usage) { + case AUDIO_USAGE_MEDIA: + case AUDIO_USAGE_GAME: + case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case AUDIO_USAGE_ASSISTANT: + return AUDIO_STREAM_MUSIC; + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + return AUDIO_STREAM_ACCESSIBILITY; + case AUDIO_USAGE_ASSISTANCE_SONIFICATION: + return AUDIO_STREAM_SYSTEM; + case AUDIO_USAGE_VOICE_COMMUNICATION: + return AUDIO_STREAM_VOICE_CALL; + + case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: + return AUDIO_STREAM_DTMF; + + case AUDIO_USAGE_ALARM: + return AUDIO_STREAM_ALARM; + case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + return AUDIO_STREAM_RING; + + case AUDIO_USAGE_NOTIFICATION: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case AUDIO_USAGE_NOTIFICATION_EVENT: + return AUDIO_STREAM_NOTIFICATION; + + case AUDIO_USAGE_UNKNOWN: + default: + return AUDIO_STREAM_MUSIC; + } +} + static inline audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *attr) { @@ -30,38 +67,7 @@ audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *at } // usage to stream type mapping - switch (attr->usage) { - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - case AUDIO_USAGE_ASSISTANT: - return AUDIO_STREAM_MUSIC; - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return AUDIO_STREAM_ACCESSIBILITY; - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return AUDIO_STREAM_SYSTEM; - case AUDIO_USAGE_VOICE_COMMUNICATION: - return AUDIO_STREAM_VOICE_CALL; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return AUDIO_STREAM_DTMF; - - case AUDIO_USAGE_ALARM: - return AUDIO_STREAM_ALARM; - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return AUDIO_STREAM_RING; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - return AUDIO_STREAM_NOTIFICATION; - - case AUDIO_USAGE_UNKNOWN: - default: - return AUDIO_STREAM_MUSIC; - } + return audio_usage_to_stream_type(attr->usage); } static inline diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 92f6c79701..d1515e26f2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5813,39 +5813,7 @@ audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_ return AUDIO_STREAM_TTS; } - // usage to stream type mapping - switch (attr->usage) { - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANT: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - return AUDIO_STREAM_MUSIC; - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return AUDIO_STREAM_ACCESSIBILITY; - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return AUDIO_STREAM_SYSTEM; - case AUDIO_USAGE_VOICE_COMMUNICATION: - return AUDIO_STREAM_VOICE_CALL; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return AUDIO_STREAM_DTMF; - - case AUDIO_USAGE_ALARM: - return AUDIO_STREAM_ALARM; - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return AUDIO_STREAM_RING; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - return AUDIO_STREAM_NOTIFICATION; - - case AUDIO_USAGE_UNKNOWN: - default: - return AUDIO_STREAM_MUSIC; - } + return audio_usage_to_stream_type(attr->usage); } bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) -- GitLab From 16698b8c36aa6cba8a3dab3432cbed361264a509 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 1 Aug 2018 10:48:38 -0700 Subject: [PATCH 0172/1530] AudioFlinger: Update namespace for Statistics Test: Build and basic sanity Bug: 80502521 Change-Id: I4f9180d1eb1eb53b75bf6fdb9f98cdcb03b6e9f2 --- services/audioflinger/FastMixerDumpState.cpp | 6 +++--- services/audioflinger/Threads.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index ffdc117726..e26dca166d 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -92,9 +92,9 @@ void FastMixerDumpState::dump(int fd) const } // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, // and adjusted CPU load in MHz normalized for CPU clock frequency - Statistics wall, loadNs; + audio_utils::Statistics wall, loadNs; #ifdef CPU_FREQUENCY_STATISTICS - Statistics kHz, loadMHz; + audio_utils::Statistics kHz, loadMHz; uint32_t previousCpukHz = 0; #endif // Assuming a normal distribution for cycle times, three standard deviations on either side of @@ -152,7 +152,7 @@ void FastMixerDumpState::dump(int fd) const qsort(tail, n, sizeof(uint32_t), compare_uint32_t); // assume same number of tail samples on each side, left and right uint32_t count = n / kTailDenominator; - Statistics left, right; + audio_utils::Statistics left, right; for (uint32_t i = 0; i < count; ++i) { left.add(tail[i]); right.add(tail[n - (i + 1)]); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6c7179e985..b1841b002d 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -335,9 +335,9 @@ public: #ifdef DEBUG_CPU_USAGE private: ThreadCpuUsage mCpuUsage; // instantaneous thread CPU usage in wall clock ns - Statistics mWcStats; // statistics on thread CPU usage in wall clock ns + audio_utils::Statistics mWcStats; // statistics on thread CPU usage in wall clock ns - Statistics mHzStats; // statistics on thread CPU usage in cycles + audio_utils::Statistics mHzStats; // statistics on thread CPU usage in cycles int mCpuNum; // thread's current CPU number int mCpukHz; // frequency of thread's current CPU in kHz -- GitLab From 91913565fb5afffca98e9db0c61dad2e4fe19725 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 27 Jul 2018 10:34:57 +0100 Subject: [PATCH 0173/1530] Camera: Fix off-by-one error during clamping in distortion mapper The (right, bottom) coordinates of any input rectangles are exclusive. Bug: 111885753 Test: Camera CTS, adb shell /data/nativetest64/cameraservice_test/cameraservice_test --gtest_filter=DistortionMapperTest.* Change-Id: Ied956bd154256a10fc4c81d27c93b0ba2e33044d --- .../device3/DistortionMapper.cpp | 16 ++++++------- .../tests/DistortionMapperTest.cpp | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp index 4dafefde55..ae7af8e001 100644 --- a/services/camera/libcameraservice/device3/DistortionMapper.cpp +++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp @@ -312,8 +312,8 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, int32_t coords[4] = { rects[i], rects[i + 1], - rects[i] + rects[i + 2], - rects[i + 1] + rects[i + 3] + rects[i] + rects[i + 2] - 1, + rects[i + 1] + rects[i + 3] - 1 }; mapRawToCorrected(coords, 2, clamp, simple); @@ -321,8 +321,8 @@ status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i + 1] = coords[1]; - rects[i + 2] = coords[2] - coords[0]; - rects[i + 3] = coords[3] - coords[1]; + rects[i + 2] = coords[2] - coords[0] + 1; + rects[i + 3] = coords[3] - coords[1] + 1; } return OK; @@ -400,8 +400,8 @@ status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, int32_t coords[4] = { rects[i], rects[i + 1], - rects[i] + rects[i + 2], - rects[i + 1] + rects[i + 3] + rects[i] + rects[i + 2] - 1, + rects[i + 1] + rects[i + 3] - 1 }; mapCorrectedToRaw(coords, 2, clamp, simple); @@ -409,8 +409,8 @@ status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, // Map back to (l, t, width, height) rects[i] = coords[0]; rects[i + 1] = coords[1]; - rects[i + 2] = coords[2] - coords[0]; - rects[i + 3] = coords[3] - coords[1]; + rects[i + 2] = coords[2] - coords[0] + 1; + rects[i + 3] = coords[3] - coords[1] + 1; } return OK; diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp index 2a689c6bb3..54935c9485 100644 --- a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp +++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp @@ -167,6 +167,30 @@ TEST(DistortionMapperTest, IdentityTransform) { } } +TEST(DistortionMapperTest, ClampConsistency) { + status_t res; + + std::array activeArray = {0, 0, 4032, 3024}; + DistortionMapper m; + setupTestMapper(&m, identityDistortion, testICal, /*activeArray*/ activeArray.data(), + /*preCorrectionActiveArray*/ activeArray.data()); + + auto rectsOrig = activeArray; + res = m.mapCorrectedRectToRaw(activeArray.data(), 1, /*clamp*/true, /*simple*/ true); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < activeArray.size(); i++) { + EXPECT_EQ(activeArray[i], rectsOrig[i]); + } + + res = m.mapRawRectToCorrected(activeArray.data(), 1, /*clamp*/true, /*simple*/ true); + ASSERT_EQ(res, OK); + + for (size_t i = 0; i < activeArray.size(); i++) { + EXPECT_EQ(activeArray[i], rectsOrig[i]); + } +} + TEST(DistortionMapperTest, SimpleTransform) { status_t res; -- GitLab From 70ec5e892f83b2c2d0c0832571974d9d0a9b2b39 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 1 Aug 2018 18:45:37 -0700 Subject: [PATCH 0174/1530] audio policy: fix mutex in stopOutput Commit d7fe086d introduced a problem by acquiring the AudioPolicyService mutex in stopOutput(). stopOutput() and releaseOtuput() can be called from audioflinger thread loop and should never acquire the main AudioPolicyService mutex. Removed unnecessary check on valid port Id as this check is also performed when doStopOutput() is executed by the command thread. Bug: 111966786 Test: verify no timeout when starting or ending a call. Change-Id: Id09eaae7a4409dfc54c6fbb1eced1624cd7b3536 --- .../audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index fdfd573143..7c12e20a9e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -254,15 +254,6 @@ status_t AudioPolicyService::startOutput(audio_port_handle_t portId) status_t AudioPolicyService::stopOutput(audio_port_handle_t portId) { - { - Mutex::Autolock _l(mLock); - - const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); - if (index < 0) { - ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); - return INVALID_OPERATION; - } - } if (mAudioPolicyManager == NULL) { return NO_INIT; } -- GitLab From 0147f95f7b11817835c94f44753cfad4fea0d65e Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 31 Jul 2018 08:13:58 -0700 Subject: [PATCH 0175/1530] Fix media extractor abort Bug: 111764444 Test: manual Change-Id: Iae6238c26ef8c0971c65e4403b933970a562221a --- media/extractors/mp4/MPEG4Extractor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 8412812c62..44b6a3d8a0 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5231,9 +5231,13 @@ status_t MPEG4Source::fragmentedRead( uint32_t cts = 0; bool isSyncSample = false; bool newBuffer = false; - if (mBuffer == NULL) { + if (mBuffer == NULL || mCurrentSampleIndex >= mCurrentSamples.size()) { newBuffer = true; + if (mBuffer != NULL) { + mBuffer->release(); + mBuffer = NULL; + } if (mCurrentSampleIndex >= mCurrentSamples.size()) { // move to next fragment if there is one if (mNextMoofOffset <= mCurrentMoofOffset) { -- GitLab From c2196d84c7ba9f48c9b65e6136055fd6bdf566db Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 2 Aug 2018 08:54:38 -0700 Subject: [PATCH 0176/1530] Camera: fix operator precedence bug Test: none Bug: 112135008 Change-Id: I518682026fbfdfbfda35de1921a00ef67e1272ee --- services/camera/libcameraservice/utils/TagMonitor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp index c0a353f2ed..f4c49ec995 100644 --- a/services/camera/libcameraservice/utils/TagMonitor.cpp +++ b/services/camera/libcameraservice/utils/TagMonitor.cpp @@ -49,7 +49,8 @@ void TagMonitor::parseTagsToMonitor(String8 tagNames) { std::lock_guard lock(mMonitorMutex); // Expand shorthands - if (ssize_t idx = tagNames.find("3a") != -1) { + ssize_t idx = tagNames.find("3a"); + if (idx != -1) { ssize_t end = tagNames.find(",", idx); char* start = tagNames.lockBuffer(tagNames.size()); start[idx] = '\0'; -- GitLab From 433722eeb72ab11bb945664fa939cf7ef737af6d Mon Sep 17 00:00:00 2001 From: Ari Hausman-Cohen Date: Tue, 24 Apr 2018 14:25:22 -0700 Subject: [PATCH 0177/1530] Add dynamic stream default effects Allows runtime modification of what effects should be default attached to streams of different types/usages. The core functionality was already in the AudioPolicyEffects system, this allows the dynamic modification of the lists. Bug: 78527120 Test: Builds, manually tested an app adding a stream effect, tested both media on the specified stream and on a different stream. Also tested by Android Things integration tests. Change-Id: Ie0426b17ff7daece58b8c85d72fbef620844325b --- media/libaudioclient/AudioEffect.cpp | 49 ++++++++++ media/libaudioclient/IAudioPolicyService.cpp | 77 ++++++++++++++- .../include/media/AudioEffect.h | 73 ++++++++++++++ .../include/media/IAudioPolicyService.h | 7 ++ media/utils/Android.bp | 6 ++ media/utils/ServiceUtilities.cpp | 21 ++++ .../include/mediautils/ServiceUtilities.h | 1 + .../service/AudioPolicyEffects.cpp | 97 +++++++++++++++++++ .../audiopolicy/service/AudioPolicyEffects.h | 42 +++++++- .../service/AudioPolicyInterfaceImpl.cpp | 44 +++++++++ .../audiopolicy/service/AudioPolicyService.h | 8 ++ 11 files changed, 420 insertions(+), 5 deletions(-) diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp index cb46b93c05..0641b6ead6 100644 --- a/media/libaudioclient/AudioEffect.cpp +++ b/media/libaudioclient/AudioEffect.cpp @@ -447,6 +447,55 @@ status_t AudioEffect::queryDefaultPreProcessing(audio_session_t audioSession, if (aps == 0) return PERMISSION_DENIED; return aps->queryDefaultPreProcessing(audioSession, descriptors, count); } + +status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + *id = af->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); + return NO_ERROR; +} + +status_t AudioEffect::addStreamDefaultEffect(const char *typeStr, + const String16& opPackageName, + const char *uuidStr, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t *id) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE; + + // Convert type & uuid from string to effect_uuid_t. + effect_uuid_t type; + if (typeStr != NULL) { + status_t res = stringToGuid(typeStr, &type); + if (res != OK) return res; + } else { + type = *EFFECT_UUID_NULL; + } + + effect_uuid_t uuid; + if (uuidStr != NULL) { + status_t res = stringToGuid(uuidStr, &uuid); + if (res != OK) return res; + } else { + uuid = *EFFECT_UUID_NULL; + } + + return aps->addStreamDefaultEffect(&type, opPackageName, &uuid, priority, usage, id); +} + +status_t AudioEffect::removeStreamDefaultEffect(audio_unique_id_t id) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + return aps->removeStreamDefaultEffect(id); +} + // ------------------------------------------------------------------------- status_t AudioEffect::stringToGuid(const char *str, effect_uuid_t *guid) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 73a8b747b4..abf74f80e4 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -81,7 +81,9 @@ enum { GET_MASTER_MONO, GET_STREAM_VOLUME_DB, GET_SURROUND_FORMATS, - SET_SURROUND_FORMAT_ENABLED + SET_SURROUND_FORMAT_ENABLED, + ADD_STREAM_DEFAULT_EFFECT, + REMOVE_STREAM_DEFAULT_EFFECT }; #define MAX_ITEMS_PER_LIST 1024 @@ -866,6 +868,42 @@ public: } return reply.readInt32(); } + + virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(type, sizeof(effect_uuid_t)); + data.writeString16(opPackageName); + data.write(uuid, sizeof(effect_uuid_t)); + data.writeInt32(priority); + data.writeInt32((int32_t) usage); + status_t status = remote()->transact(ADD_STREAM_DEFAULT_EFFECT, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast (reply.readInt32()); + *id = reply.readInt32(); + return status; + } + + virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(id); + status_t status = remote()->transact(REMOVE_STREAM_DEFAULT_EFFECT, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1561,6 +1599,43 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case ADD_STREAM_DEFAULT_EFFECT: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + effect_uuid_t type; + status_t status = data.read(&type, sizeof(effect_uuid_t)); + if (status != NO_ERROR) { + return status; + } + String16 opPackageName; + status = data.readString16(&opPackageName); + if (status != NO_ERROR) { + return status; + } + effect_uuid_t uuid; + status = data.read(&uuid, sizeof(effect_uuid_t)); + if (status != NO_ERROR) { + return status; + } + int32_t priority = data.readInt32(); + audio_usage_t usage = (audio_usage_t) data.readInt32(); + audio_unique_id_t id = 0; + reply->writeInt32(static_cast (addStreamDefaultEffect(&type, + opPackageName, + &uuid, + priority, + usage, + &id))); + reply->writeInt32(id); + return NO_ERROR; + } + + case REMOVE_STREAM_DEFAULT_EFFECT: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_unique_id_t id = static_cast(data.readInt32()); + reply->writeInt32(static_cast (removeStreamDefaultEffect(id))); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h index ae66e8f2d4..c97f7838a3 100644 --- a/media/libaudioclient/include/media/AudioEffect.h +++ b/media/libaudioclient/include/media/AudioEffect.h @@ -150,6 +150,79 @@ public: effect_descriptor_t *descriptors, uint32_t *count); + /* + * Gets a new system-wide unique effect id. + * + * Parameters: + * id: The address to return the generated id. + * + * Returned status (from utils/Errors.h) can be: + * NO_ERROR successful operation. + * PERMISSION_DENIED could not get AudioFlinger interface + * or caller lacks required permissions. + * Returned value + * *id: The new unique system-wide effect id. + */ + static status_t newEffectUniqueId(audio_unique_id_t* id); + + /* + * Static methods for adding/removing system-wide effects. + */ + + /* + * Adds an effect to the list of default output effects for a given stream type. + * + * If the effect is no longer available when a stream of the given type + * is created, the system will continue without adding it. + * + * Parameters: + * typeStr: Type uuid of effect to be a default: can be null if uuidStr is specified. + * This may correspond to the OpenSL ES interface implemented by this effect, + * or could be some vendor-defined type. + * opPackageName: The package name used for app op checks. + * uuidStr: Uuid of effect to be a default: can be null if type is specified. + * This uuid corresponds to a particular implementation of an effect type. + * Note if both uuidStr and typeStr are specified, typeStr is ignored. + * priority: Requested priority for effect control: the priority level corresponds to the + * value of priority parameter: negative values indicate lower priorities, positive + * values higher priorities, 0 being the normal priority. + * usage: The usage this effect should be a default for. Unrecognized values will be + * treated as AUDIO_USAGE_UNKNOWN. + * id: Address where the system-wide unique id of the default effect should be returned. + * + * Returned status (from utils/Errors.h) can be: + * NO_ERROR successful operation. + * PERMISSION_DENIED could not get AudioFlinger interface + * or caller lacks required permissions. + * NO_INIT effect library failed to initialize. + * BAD_VALUE invalid type uuid or implementation uuid. + * NAME_NOT_FOUND no effect with this uuid or type found. + * + * Returned value + * *id: The system-wide unique id of the added default effect. + */ + static status_t addStreamDefaultEffect(const char* typeStr, + const String16& opPackageName, + const char* uuidStr, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id); + + /* + * Removes an effect from the list of default output effects for a given stream type. + * + * Parameters: + * id: The system-wide unique id of the effect that should no longer be a default. + * + * Returned status (from utils/Errors.h) can be: + * NO_ERROR successful operation. + * PERMISSION_DENIED could not get AudioFlinger interface + * or caller lacks required permissions. + * NO_INIT effect library failed to initialize. + * BAD_VALUE invalid id. + */ + static status_t removeStreamDefaultEffect(audio_unique_id_t id); + /* * Events used by callback function (effect_callback_t). */ diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index cdbb8760c1..c2899f83e0 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -109,6 +109,13 @@ public: virtual status_t queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count) = 0; + virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id) = 0; + virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0; // Check if offload is possible for given format, stream type, sample rate, // bit rate, duration, video and streaming or offload property is enabled virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0; diff --git a/media/utils/Android.bp b/media/utils/Android.bp index 13b66ed950..f5b3f929c2 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -40,6 +40,12 @@ cc_library { "-Werror", ], + product_variables: { + product_is_iot: { + cflags: ["-DTARGET_ANDROID_THINGS"], + }, + }, + local_include_dirs: ["include"], export_include_dirs: ["include"], } diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 0d50be0cb4..1c54aec468 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -158,6 +158,27 @@ bool modifyAudioRoutingAllowed() { return ok; } +bool modifyDefaultAudioEffectsAllowed() { + static const String16 sModifyDefaultAudioEffectsAllowed( + "android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"); + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. + bool ok = PermissionCache::checkCallingPermission(sModifyDefaultAudioEffectsAllowed); + +#ifdef TARGET_ANDROID_THINGS + if (!ok) { + // Use a secondary permission on Android Things to allow a more lenient level of protection. + static const String16 sModifyDefaultAudioEffectsAndroidThingsAllowed( + "com.google.android.things.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"); + ok = PermissionCache::checkCallingPermission( + sModifyDefaultAudioEffectsAndroidThingsAllowed); + } + if (!ok) ALOGE("com.google.android.things.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"); +#else + if (!ok) ALOGE("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"); +#endif + return ok; +} + bool dumpAllowed() { static const String16 sDump("android.permission.DUMP"); // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 0911744fb6..98f54c2b8b 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -68,6 +68,7 @@ bool captureAudioOutputAllowed(pid_t pid, uid_t uid); bool captureHotwordAllowed(pid_t pid, uid_t uid); bool settingsAllowed(); bool modifyAudioRoutingAllowed(); +bool modifyDefaultAudioEffectsAllowed(); bool dumpAllowed(); bool modifyPhoneStateAllowed(pid_t pid, uid_t uid); status_t checkIMemory(const sp& iMemory); diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index fdae23b4c9..2858aadb2d 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -317,6 +318,102 @@ status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t outpu return status; } +status_t AudioPolicyEffects::addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id) +{ + if (uuid == NULL || type == NULL) { + ALOGE("addStreamDefaultEffect(): Null uuid or type uuid pointer"); + return BAD_VALUE; + } + + audio_stream_type_t stream = audio_usage_to_stream_type(usage); + + if (stream < AUDIO_STREAM_MIN || stream >= AUDIO_STREAM_PUBLIC_CNT) { + ALOGE("addStreamDefaultEffect(): Unsupported stream type %d", stream); + return BAD_VALUE; + } + + // Check that |uuid| or |type| corresponds to an effect on the system. + effect_descriptor_t descriptor = {}; + status_t res = AudioEffect::getEffectDescriptor( + uuid, type, EFFECT_FLAG_TYPE_INSERT, &descriptor); + if (res != OK) { + ALOGE("addStreamDefaultEffect(): Failed to find effect descriptor matching uuid/type."); + return res; + } + + // Only insert effects can be added dynamically as stream defaults. + if ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_INSERT) { + ALOGE("addStreamDefaultEffect(): Desired effect cannot be attached " + "as a stream default effect."); + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // Find the EffectDescVector for the given stream type, or create a new one if necessary. + ssize_t index = mOutputStreams.indexOfKey(stream); + EffectDescVector *desc = NULL; + if (index < 0) { + // No effects for this stream type yet. + desc = new EffectDescVector(); + mOutputStreams.add(stream, desc); + } else { + desc = mOutputStreams.valueAt(index); + } + + // Create a new effect and add it to the vector. + res = AudioEffect::newEffectUniqueId(id); + if (res != OK) { + ALOGE("addStreamDefaultEffect(): failed to get new unique id."); + return res; + } + EffectDesc *effect = new EffectDesc( + descriptor.name, *type, opPackageName, *uuid, priority, *id); + desc->mEffects.add(effect); + // TODO(b/71813697): Support setting params as well. + + // TODO(b/71814300): Retroactively attach to any existing streams of the given type. + // This requires tracking the stream type of each session id in addition to what is + // already being tracked. + + return NO_ERROR; +} + +status_t AudioPolicyEffects::removeStreamDefaultEffect(audio_unique_id_t id) +{ + if (id == AUDIO_UNIQUE_ID_ALLOCATE) { + // ALLOCATE is not a unique identifier, but rather a reserved value indicating + // a real id has not been assigned. For default effects, this value is only used + // by system-owned defaults from the loaded config, which cannot be removed. + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // Check each stream type. + size_t numStreams = mOutputStreams.size(); + for (size_t i = 0; i < numStreams; ++i) { + // Check each effect for each stream. + EffectDescVector* descVector = mOutputStreams[i]; + for (auto desc = descVector->mEffects.begin(); desc != descVector->mEffects.end(); ++desc) { + if ((*desc)->mId == id) { + // Found it! + // TODO(b/71814300): Remove from any streams the effect was attached to. + descVector->mEffects.erase(desc); + // Handles are unique; there can only be one match, so return early. + return NO_ERROR; + } + } + } + + // Effect wasn't found, so it's been trivially removed successfully. + return NO_ERROR; +} void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) { diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 623180ea26..69367b1dc0 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -64,7 +64,6 @@ public: status_t releaseInputEffects(audio_io_handle_t input, audio_session_t audioSession); - // Return a list of effect descriptors for default output effects // associated with audioSession status_t queryDefaultOutputSessionEffects(audio_session_t audioSession, @@ -82,18 +81,49 @@ public: audio_stream_type_t stream, audio_session_t audioSession); + // Add the effect to the list of default effects for streams of type |stream|. + status_t addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id); + + // Remove the default stream effect from wherever it's attached. + status_t removeStreamDefaultEffect(audio_unique_id_t id); + private: // class to store the description of an effects and its parameters // as defined in audio_effects.conf class EffectDesc { public: - EffectDesc(const char *name, const effect_uuid_t& uuid) : + EffectDesc(const char *name, + const effect_uuid_t& typeUuid, + const String16& opPackageName, + const effect_uuid_t& uuid, + uint32_t priority, + audio_unique_id_t id) : mName(strdup(name)), - mUuid(uuid) { } + mTypeUuid(typeUuid), + mOpPackageName(opPackageName), + mUuid(uuid), + mPriority(priority), + mId(id) { } + EffectDesc(const char *name, const effect_uuid_t& uuid) : + EffectDesc(name, + *EFFECT_UUID_NULL, + String16(""), + uuid, + 0, + AUDIO_UNIQUE_ID_ALLOCATE) { } EffectDesc(const EffectDesc& orig) : mName(strdup(orig.mName)), - mUuid(orig.mUuid) { + mTypeUuid(orig.mTypeUuid), + mOpPackageName(orig.mOpPackageName), + mUuid(orig.mUuid), + mPriority(orig.mPriority), + mId(orig.mId) { // deep copy mParams for (size_t k = 0; k < orig.mParams.size(); k++) { effect_param_t *origParam = orig.mParams[k]; @@ -116,7 +146,11 @@ private: } } char *mName; + effect_uuid_t mTypeUuid; + String16 mOpPackageName; effect_uuid_t mUuid; + int32_t mPriority; + audio_unique_id_t mId; Vector mParams; }; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index dbfda44e5e..d29f637807 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -859,6 +859,50 @@ status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSess (audio_session_t)audioSession, descriptors, count); } +status_t AudioPolicyService::addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!modifyDefaultAudioEffectsAllowed()) { + return PERMISSION_DENIED; + } + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects == 0) { + return NO_INIT; + } + return audioPolicyEffects->addStreamDefaultEffect( + type, opPackageName, uuid, priority, usage, id); +} + +status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!modifyDefaultAudioEffectsAllowed()) { + return PERMISSION_DENIED; + } + spaudioPolicyEffects; + { + Mutex::Autolock _l(mLock); + audioPolicyEffects = mAudioPolicyEffects; + } + if (audioPolicyEffects == 0) { + return NO_INIT; + } + return audioPolicyEffects->removeStreamDefaultEffect(id); +} + bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) { if (mAudioPolicyManager == NULL) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 6a256683ce..44c0347f7f 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -126,6 +126,14 @@ public: virtual status_t queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count); + virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id); + virtual status_t removeStreamDefaultEffect(audio_unique_id_t id); + virtual status_t onTransact( uint32_t code, const Parcel& data, -- GitLab From 156317a32323b8a3826a6e9e9eec49b9049c14bd Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 2 Aug 2018 11:49:29 -0700 Subject: [PATCH 0178/1530] AudioFlinger: Remove unnecessary computation Test: Build and basic record sanity Bug: 112142872 Change-Id: Ibfc0c715930593c23e58acd4afda6986d69d53b2 --- services/audioflinger/Threads.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 79147afefb..3ef8ce29d1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6733,8 +6733,7 @@ reacquire_wakelock: // If an NBAIO source is present, use it to read the normal capture's data if (mPipeSource != 0) { - size_t framesToRead = mBufferSize / mFrameSize; - framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2); + size_t framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2); // The audio fifo read() returns OVERRUN on overflow, and advances the read pointer // to the full buffer point (clearing the overflow condition). Upon OVERRUN error, -- GitLab From 68e6fe1fef7c638805afdbe7f95b2f505ebe50ba Mon Sep 17 00:00:00 2001 From: Previr Rangroo Date: Thu, 30 Nov 2017 20:02:13 -0800 Subject: [PATCH 0179/1530] Add EAC3 support to MediaExtractor Report EAC3 mime, sample-rate and channel count for MPEG4 and TS. This commit supersedes the commit on p-fs-release branch. https://partner-android-review.googlesource.com/c/platform/frameworks/av/+/1057810 Change-Id: I9e14d821b5e78a03028bc52d8ee838c23859e9e9 Signed-off-by: Previr Rangroo --- cmds/stagefright/stagefright.cpp | 3 +- media/extractors/mp4/MPEG4Extractor.cpp | 240 +++++++++++++++++---- media/extractors/mp4/MPEG4Extractor.h | 7 +- media/libstagefright/Utils.cpp | 2 +- media/libstagefright/mpeg2ts/ATSParser.cpp | 5 + media/libstagefright/mpeg2ts/ATSParser.h | 1 + media/libstagefright/mpeg2ts/ESQueue.cpp | 124 +++++++++-- media/libstagefright/mpeg2ts/ESQueue.h | 3 +- 8 files changed, 318 insertions(+), 67 deletions(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index d41d3fd369..bddf9452ac 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -650,7 +650,8 @@ static void dumpCodecProfiles(bool queryDecoders) { MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9, - MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4 + MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, + MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4 }; const char *codecType = queryDecoders? "decoder" : "encoder"; diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 44b6a3d8a0..fe9f99c98e 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -313,6 +313,9 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC('s', 'a', 'w', 'b'): return MEDIA_MIMETYPE_AUDIO_AMR_WB; + case FOURCC('e', 'c', '-', '3'): + return MEDIA_MIMETYPE_AUDIO_EAC3; + case FOURCC('m', 'p', '4', 'v'): return MEDIA_MIMETYPE_VIDEO_MPEG4; @@ -2438,13 +2441,19 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('a', 'c', '-', '3'): { *offset += chunk_size; - return parseAC3SampleEntry(data_offset); + return parseAC3SpecificBox(data_offset); + } + + case FOURCC('e', 'c', '-', '3'): + { + *offset += chunk_size; + return parseEAC3SpecificBox(data_offset); } case FOURCC('a', 'c', '-', '4'): { *offset += chunk_size; - return parseAC4SampleEntry(data_offset); + return parseAC4SpecificBox(data_offset); } case FOURCC('f', 't', 'y', 'p'): @@ -2518,43 +2527,43 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return OK; } -status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) { +status_t MPEG4Extractor::parseChannelCountSampleRate( + off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate) { // skip 16 bytes: // + 6-byte reserved, // + 2-byte data reference index, // + 8-byte reserved - offset += 16; - uint16_t channelCount; - if (!mDataSource->getUInt16(offset, &channelCount)) { - ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count"); + *offset += 16; + if (!mDataSource->getUInt16(*offset, channelCount)) { + ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read channel count"); return ERROR_MALFORMED; } // skip 8 bytes: // + 2-byte channelCount, // + 2-byte sample size, // + 4-byte reserved - offset += 8; - uint16_t sampleRate; - if (!mDataSource->getUInt16(offset, &sampleRate)) { - ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate"); + *offset += 8; + if (!mDataSource->getUInt16(*offset, sampleRate)) { + ALOGE("MPEG4Extractor: error while reading sample entry box: cannot read sample rate"); return ERROR_MALFORMED; } - // skip 4 bytes: // + 2-byte sampleRate, // + 2-byte reserved - offset += 4; + *offset += 4; + return OK; +} +status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { if (mLastTrack == NULL) { return ERROR_MALFORMED; } - mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4); - mLastTrack->meta.setInt32(kKeyChannelCount, channelCount); - mLastTrack->meta.setInt32(kKeySampleRate, sampleRate); - return parseAC4SpecificBox(offset); -} -status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { + uint16_t sampleRate, channelCount; + status_t status; + if ((status = parseChannelCountSampleRate(&offset, &channelCount, &sampleRate)) != OK) { + return status; + } uint32_t size; // + 4-byte size // + 4-byte type @@ -2593,39 +2602,185 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { return ERROR_MALFORMED; } + mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4); + mLastTrack->meta.setInt32(kKeyChannelCount, channelCount); + mLastTrack->meta.setInt32(kKeySampleRate, sampleRate); return OK; } -status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) { - // skip 16 bytes: - // + 6-byte reserved, - // + 2-byte data reference index, - // + 8-byte reserved - offset += 16; - uint16_t channelCount; - if (!mDataSource->getUInt16(offset, &channelCount)) { +status_t MPEG4Extractor::parseEAC3SpecificBox(off64_t offset) { + if (mLastTrack == NULL) { return ERROR_MALFORMED; } - // skip 8 bytes: - // + 2-byte channelCount, - // + 2-byte sample size, - // + 4-byte reserved - offset += 8; - uint16_t sampleRate; - if (!mDataSource->getUInt16(offset, &sampleRate)) { - ALOGE("MPEG4Extractor: error while reading ac-3 block: cannot read sample rate"); + + uint16_t sampleRate, channels; + status_t status; + if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) { + return status; + } + uint32_t size; + // + 4-byte size + // + 4-byte type + // + 3-byte payload + const uint32_t kEAC3SpecificBoxMinSize = 11; + // 13 + 3 + (8 * (2 + 5 + 5 + 3 + 1 + 3 + 4 + (14 * 9 + 1))) bits == 152 bytes theoretical max + // calculated from the required bits read below as well as the maximum number of independent + // and dependant sub streams you can have + const uint32_t kEAC3SpecificBoxMaxSize = 152; + if (!mDataSource->getUInt32(offset, &size) || + size < kEAC3SpecificBoxMinSize || + size > kEAC3SpecificBoxMaxSize) { + ALOGE("MPEG4Extractor: error while reading eac-3 block: cannot read specific box size"); + return ERROR_MALFORMED; + } + + offset += 4; + uint32_t type; + if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'e', 'c', '3')) { + ALOGE("MPEG4Extractor: error while reading eac-3 specific block: header not dec3"); return ERROR_MALFORMED; } - // skip 4 bytes: - // + 2-byte sampleRate, - // + 2-byte reserved offset += 4; - return parseAC3SpecificBox(offset, sampleRate); + uint8_t* chunk = new (std::nothrow) uint8_t[size]; + if (chunk == NULL) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt(offset, chunk, size) != (ssize_t)size) { + ALOGE("MPEG4Extractor: error while reading eac-3 specific block: bitstream fields"); + delete[] chunk; + return ERROR_MALFORMED; + } + + ABitReader br(chunk, size); + static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5}; + static const unsigned sampleRateTable[] = {48000, 44100, 32000}; + + if (br.numBitsLeft() < 16) { + delete[] chunk; + return ERROR_MALFORMED; + } + unsigned data_rate = br.getBits(13); + ALOGV("EAC3 data rate = %d", data_rate); + + unsigned num_ind_sub = br.getBits(3) + 1; + ALOGV("EAC3 independant substreams = %d", num_ind_sub); + if (br.numBitsLeft() < (num_ind_sub * 23)) { + delete[] chunk; + return ERROR_MALFORMED; + } + + unsigned channelCount = 0; + for (unsigned i = 0; i < num_ind_sub; i++) { + unsigned fscod = br.getBits(2); + if (fscod == 3) { + ALOGE("Incorrect fscod (3) in EAC3 header"); + delete[] chunk; + return ERROR_MALFORMED; + } + unsigned boxSampleRate = sampleRateTable[fscod]; + if (boxSampleRate != sampleRate) { + ALOGE("sample rate mismatch: boxSampleRate = %d, sampleRate = %d", + boxSampleRate, sampleRate); + delete[] chunk; + return ERROR_MALFORMED; + } + + unsigned bsid = br.getBits(5); + if (bsid < 8) { + ALOGW("Incorrect bsid in EAC3 header. Possibly AC-3?"); + delete[] chunk; + return ERROR_MALFORMED; + } + + // skip + br.skipBits(2); + unsigned bsmod = br.getBits(3); + unsigned acmod = br.getBits(3); + unsigned lfeon = br.getBits(1); + // we currently only support the first stream + if (i == 0) + channelCount = channelCountTable[acmod] + lfeon; + ALOGV("bsmod = %d, acmod = %d, lfeon = %d", bsmod, acmod, lfeon); + + br.skipBits(3); + unsigned num_dep_sub = br.getBits(4); + ALOGV("EAC3 dependant substreams = %d", num_dep_sub); + if (num_dep_sub != 0) { + if (br.numBitsLeft() < 9) { + delete[] chunk; + return ERROR_MALFORMED; + } + static const char* chan_loc_tbl[] = { "Lc/Rc","Lrs/Rrs","Cs","Ts","Lsd/Rsd", + "Lw/Rw","Lvh/Rvh","Cvh","Lfe2" }; + unsigned chan_loc = br.getBits(9); + unsigned mask = 1; + for (unsigned j = 0; j < 9; j++, mask <<= 1) { + if ((chan_loc & mask) != 0) { + // we currently only support the first stream + if (i == 0) { + channelCount++; + // these are 2 channels in the mask + if (j == 0 || j == 1 || j == 4 || j == 5 || j == 6) { + channelCount++; + } + } + ALOGV(" %s", chan_loc_tbl[j]); + } + } + } else { + if (br.numBitsLeft() == 0) { + delete[] chunk; + return ERROR_MALFORMED; + } + br.skipBits(1); + } + } + + if (br.numBitsLeft() != 0) { + if (br.numBitsLeft() < 8) { + delete[] chunk; + return ERROR_MALFORMED; + } + unsigned mask = br.getBits(8); + for (unsigned i = 0; i < 8; i++) { + if (((0x1 << i) && mask) == 0) + continue; + + if (br.numBitsLeft() < 8) { + delete[] chunk; + return ERROR_MALFORMED; + } + switch (i) { + case 0: { + unsigned complexity = br.getBits(8); + ALOGV("Found a JOC stream with complexity = %d", complexity); + }break; + default: { + br.skipBits(8); + }break; + } + } + } + mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3); + mLastTrack->meta.setInt32(kKeyChannelCount, channelCount); + mLastTrack->meta.setInt32(kKeySampleRate, sampleRate); + + delete[] chunk; + return OK; } -status_t MPEG4Extractor::parseAC3SpecificBox( - off64_t offset, uint16_t sampleRate) { +status_t MPEG4Extractor::parseAC3SpecificBox(off64_t offset) { + if (mLastTrack == NULL) { + return ERROR_MALFORMED; + } + + uint16_t sampleRate, channels; + status_t status; + if ((status = parseChannelCountSampleRate(&offset, &channels, &sampleRate)) != OK) { + return status; + } uint32_t size; // + 4-byte size // + 4-byte type @@ -2680,9 +2835,6 @@ status_t MPEG4Extractor::parseAC3SpecificBox( unsigned lfeon = br.getBits(1); unsigned channelCount = channelCountTable[acmod] + lfeon; - if (mLastTrack == NULL) { - return ERROR_MALFORMED; - } mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3); mLastTrack->meta.setInt32(kKeyChannelCount, channelCount); mLastTrack->meta.setInt32(kKeySampleRate, sampleRate); diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index ed70aa74af..a4a5684f9c 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -139,9 +139,10 @@ private: Track *findTrackByMimePrefix(const char *mimePrefix); - status_t parseAC3SampleEntry(off64_t offset); - status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate); - status_t parseAC4SampleEntry(off64_t offset); + status_t parseChannelCountSampleRate( + off64_t *offset, uint16_t *channelCount, uint16_t *sampleRate); + status_t parseAC3SpecificBox(off64_t offset); + status_t parseEAC3SpecificBox(off64_t offset); status_t parseAC4SpecificBox(off64_t offset); MPEG4Extractor(const MPEG4Extractor &); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index ea778a4bc0..ada37a624b 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1577,6 +1577,7 @@ static const struct mime_conv_t mimeLookup[] = { { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS}, { MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3}, + { MEDIA_MIMETYPE_AUDIO_EAC3, AUDIO_FORMAT_E_AC3}, { MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4}, { MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC}, { 0, AUDIO_FORMAT_INVALID } @@ -1868,4 +1869,3 @@ AString nameForFd(int fd) { } } // namespace android - diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index cc318153de..32635d12e4 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -815,6 +815,10 @@ ATSParser::Stream::Stream( mode = ElementaryStreamQueue::AC3; break; + case STREAMTYPE_EAC3: + mode = ElementaryStreamQueue::EAC3; + break; + case STREAMTYPE_PES_PRIVATE_DATA: if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) { mode = ElementaryStreamQueue::AC4; @@ -1026,6 +1030,7 @@ bool ATSParser::Stream::isAudio() const { case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: + case STREAMTYPE_EAC3: case STREAMTYPE_AAC_ENCRYPTED: case STREAMTYPE_AC3_ENCRYPTED: return true; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index adb4fb2f04..a31dc46c19 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -154,6 +154,7 @@ struct ATSParser : public RefBase { // Stream type 0x83 is non-standard, // it could be LPCM or TrueHD AC3 STREAMTYPE_LPCM_AC3 = 0x83, + STREAMTYPE_EAC3 = 0x87, //Sample Encrypted types STREAMTYPE_H264_ENCRYPTED = 0xDB, diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 34d0bcc249..90005c38a9 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -210,8 +210,81 @@ static unsigned parseAC3SyncFrame( return payloadSize; } -static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) { - return parseAC3SyncFrame(ptr, size, NULL) > 0; +// Parse EAC3 header assuming the current ptr is start position of syncframe, +// update metadata only applicable, and return the payload size +// ATSC A/52:2012 E2.3.1 +static unsigned parseEAC3SyncFrame( + const uint8_t *ptr, size_t size, sp *metaData) { + static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5}; + static const unsigned samplingRateTable[] = {48000, 44100, 32000}; + static const unsigned samplingRateTable2[] = {24000, 22050, 16000}; + + ABitReader bits(ptr, size); + if (bits.numBitsLeft() < 16) { + ALOGE("Not enough bits left for further parsing"); + return 0; + } + if (bits.getBits(16) != 0x0B77) { + ALOGE("No valid sync word in EAC3 header"); + return 0; + } + + // we parse up to bsid so there needs to be at least that many bits + if (bits.numBitsLeft() < 2 + 3 + 11 + 2 + 2 + 3 + 1 + 5) { + ALOGE("Not enough bits left for further parsing"); + return 0; + } + + unsigned strmtyp = bits.getBits(2); + if (strmtyp == 3) { + ALOGE("Incorrect strmtyp in EAC3 header"); + return 0; + } + + unsigned substreamid = bits.getBits(3); + // only the first independent stream is supported + if ((strmtyp == 0 || strmtyp == 2) && substreamid != 0) + return 0; + + unsigned frmsiz = bits.getBits(11); + unsigned fscod = bits.getBits(2); + + unsigned samplingRate = 0; + if (fscod == 0x3) { + unsigned fscod2 = bits.getBits(2); + if (fscod2 == 3) { + ALOGW("Incorrect fscod2 in EAC3 header"); + return 0; + } + samplingRate = samplingRateTable2[fscod2]; + } else { + samplingRate = samplingRateTable[fscod]; + unsigned numblkscod __unused = bits.getBits(2); + } + + unsigned acmod = bits.getBits(3); + unsigned lfeon = bits.getBits(1); + unsigned bsid = bits.getBits(5); + if (bsid < 11 || bsid > 16) { + ALOGW("Incorrect bsid in EAC3 header. Could be AC-3 or some unknown EAC3 format"); + return 0; + } + + // we currently only support the first independant stream + if (metaData != NULL && (strmtyp == 0 || strmtyp == 2)) { + unsigned channelCount = channelCountTable[acmod] + lfeon; + ALOGV("EAC3 channelCount = %d", channelCount); + ALOGV("EAC3 samplingRate = %d", samplingRate); + (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3); + (*metaData)->setInt32(kKeyChannelCount, channelCount); + (*metaData)->setInt32(kKeySampleRate, samplingRate); + (*metaData)->setInt32(kKeyIsSyncFrame, 1); + } + + unsigned payloadSize = frmsiz + 1; + payloadSize <<= 1; // convert from 16-bit words to bytes + + return payloadSize; } // Parse AC4 header assuming the current ptr is start position of syncframe @@ -477,12 +550,19 @@ status_t ElementaryStreamQueue::appendData( } case AC3: + case EAC3: { uint8_t *ptr = (uint8_t *)data; ssize_t startOffset = -1; for (size_t i = 0; i < size; ++i) { - if (IsSeeminglyValidAC3Header(&ptr[i], size - i)) { + unsigned payloadSize = 0; + if (mMode == AC3) { + payloadSize = parseAC3SyncFrame(&ptr[i], size - i, NULL); + } else if (mMode == EAC3) { + payloadSize = parseEAC3SyncFrame(&ptr[i], size - i, NULL); + } + if (payloadSize > 0) { startOffset = i; break; } @@ -493,7 +573,7 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - ALOGI("found something resembling an AC3 syncword at " + ALOGI("found something resembling an (E)AC3 syncword at " "offset %zd", startOffset); } @@ -526,8 +606,9 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - ALOGI("found something resembling an AC4 syncword at offset %zd", - startOffset); + ALOGI("found something resembling an AC4 syncword at " + "offset %zd", + startOffset); } if (frameSize != size - startOffset) { ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.", @@ -756,7 +837,8 @@ sp ElementaryStreamQueue::dequeueAccessUnit() { case AAC: return dequeueAccessUnitAAC(); case AC3: - return dequeueAccessUnitAC3(); + case EAC3: + return dequeueAccessUnitEAC3(); case AC4: return dequeueAccessUnitAC4(); case MPEG_VIDEO: @@ -776,34 +858,38 @@ sp ElementaryStreamQueue::dequeueAccessUnit() { } } -sp ElementaryStreamQueue::dequeueAccessUnitAC3() { +sp ElementaryStreamQueue::dequeueAccessUnitEAC3() { unsigned syncStartPos = 0; // in bytes unsigned payloadSize = 0; sp format = new MetaData; - ALOGV("dequeueAccessUnit_AC3[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size()); + ALOGV("dequeueAccessUnitEAC3[%d]: mBuffer %p(%zu)", mAUIndex, + mBuffer->data(), mBuffer->size()); while (true) { if (syncStartPos + 2 >= mBuffer->size()) { return NULL; } - payloadSize = parseAC3SyncFrame( - mBuffer->data() + syncStartPos, - mBuffer->size() - syncStartPos, - &format); + uint8_t *ptr = mBuffer->data() + syncStartPos; + size_t size = mBuffer->size() - syncStartPos; + if (mMode == AC3) { + payloadSize = parseAC3SyncFrame(ptr, size, &format); + } else if (mMode == EAC3) { + payloadSize = parseEAC3SyncFrame(ptr, size, &format); + } if (payloadSize > 0) { break; } - ALOGV("dequeueAccessUnit_AC3[%d]: syncStartPos %u payloadSize %u", + ALOGV("dequeueAccessUnitEAC3[%d]: syncStartPos %u payloadSize %u", mAUIndex, syncStartPos, payloadSize); ++syncStartPos; } if (mBuffer->size() < syncStartPos + payloadSize) { - ALOGV("Not enough buffer size for AC3"); + ALOGV("Not enough buffer size for E/AC3"); return NULL; } @@ -811,7 +897,6 @@ sp ElementaryStreamQueue::dequeueAccessUnitAC3() { mFormat = format; } - int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); if (timeUs < 0ll) { ALOGE("negative timeUs"); @@ -820,7 +905,12 @@ sp ElementaryStreamQueue::dequeueAccessUnitAC3() { // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files) if (mSampleDecryptor != NULL) { - mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize); + if (mMode == AC3) { + mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize); + } else if (mMode == EAC3) { + ALOGE("EAC3 AU is encrypted and decryption is not supported"); + return NULL; + } } mAUIndex++; diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index 399214ad1e..8c1d112cbd 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -38,6 +38,7 @@ struct ElementaryStreamQueue { H264, AAC, AC3, + EAC3, AC4, MPEG_AUDIO, MPEG_VIDEO, @@ -116,7 +117,7 @@ private: sp dequeueAccessUnitH264(); sp dequeueAccessUnitAAC(); - sp dequeueAccessUnitAC3(); + sp dequeueAccessUnitEAC3(); sp dequeueAccessUnitAC4(); sp dequeueAccessUnitMPEGAudio(); sp dequeueAccessUnitMPEGVideo(); -- GitLab From 734334fd0045bf7897a6d749c00204a784766a7e Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 12 Jul 2018 19:37:41 -0700 Subject: [PATCH 0180/1530] Aaudio: Implement app shareable flag instead of -size hack Test: adb shell data/nativetest64/write_sine/write_sine -pl -m3 Bug: 38118159 Change-Id: I349e10a00a5466f54e084f4745b1ed4776378c81 Signed-off-by: Kevin Rocard --- media/libaudiohal/impl/StreamHalHidl.cpp | 10 ++++++++++ services/oboeservice/AAudioServiceEndpointMMAP.cpp | 12 ++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index b23e01869b..bfa80e8c71 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -192,7 +192,17 @@ status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, const native_handle *handle = hidlInfo.sharedMemory.handle(); if (handle->numFds > 0) { info->shared_memory_fd = handle->data[0]; +#if MAJOR_VERSION == 4 + info->flags = audio_mmap_buffer_flag(hidlInfo.flags); +#endif info->buffer_size_frames = hidlInfo.bufferSizeFrames; + // Negative buffer size frame was a hack in O and P to + // indicate that the buffer is shareable to applications + if (info->buffer_size_frames < 0) { + info->buffer_size_frames *= -1; + info->flags = audio_mmap_buffer_flag( + info->flags | AUDIO_MMAP_APPLICATION_SHAREABLE); + } info->burst_size_frames = hidlInfo.burstSizeFrames; // info->shared_memory_address is not needed in HIDL context info->shared_memory_address = NULL; diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index f9e21fba77..f30f9bbb8a 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -189,6 +189,7 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN; } status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo); + bool isBufferShareable = mMmapBufferinfo.flags & AUDIO_MMAP_APPLICATION_SHAREABLE; if (status != OK) { ALOGE("%s() - createMmapBuffer() failed with status %d %s", __func__, status, strerror(-status)); @@ -198,18 +199,13 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques ALOGD("%s() createMmapBuffer() returned = %d, buffer_size = %d, burst_size %d" ", Sharable FD: %s", __func__, status, - abs(mMmapBufferinfo.buffer_size_frames), + mMmapBufferinfo.buffer_size_frames, mMmapBufferinfo.burst_size_frames, - mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No"); + isBufferShareable ? "Yes" : "No"); } setBufferCapacity(mMmapBufferinfo.buffer_size_frames); - // The audio HAL indicates if the shared memory fd can be shared outside of audioserver - // by returning a negative buffer size - if (getBufferCapacity() < 0) { - // Exclusive mode can be used by client or service. - setBufferCapacity(-getBufferCapacity()); - } else { + if (!isBufferShareable) { // Exclusive mode can only be used by the service because the FD cannot be shared. uid_t audioServiceUid = getuid(); if ((mMmapClient.clientUid != audioServiceUid) && -- GitLab From b2fe78f72435f475fad855d20c1ef0309e392b8f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 2 Aug 2018 17:56:11 -0700 Subject: [PATCH 0181/1530] IAudioFlinger: fix early return in createEffect Bug: 112161565 Test: make Change-Id: I7056ed1a3454c7e0fbe692029f75fe5b50c4c42f --- media/libaudioclient/IAudioFlinger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 84e8beef17..23d5c49ed4 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -634,10 +634,10 @@ public: sp effect; if (pDesc == NULL) { - return effect; if (status != NULL) { *status = BAD_VALUE; } + return effect; } data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); -- GitLab From 1d539d95ea476c525cc26ff059f6f6a1f6e3ea42 Mon Sep 17 00:00:00 2001 From: Haynes Mathew George Date: Fri, 16 Mar 2018 11:40:49 -0700 Subject: [PATCH 0182/1530] audiopolicy: reset capture state when input is closed An active input can be closed before stopInput is called, thus leaving the input handle to be an unknown input, then the capture state can keep in true which blocks sound trigger session. Reset the capture state if needed when input is closed. Bug: 75253700 Test: manual authored-by: Zhou Song Change-Id: I969aa346e503cab593c33b6ca408845d058f00bc --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 92f6c79701..d66d5ec199 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4761,6 +4761,7 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) nextAudioPortGeneration(); + audio_devices_t device = inputDesc->mDevice; ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); @@ -4771,6 +4772,12 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) inputDesc->close(); mInputs.removeItem(input); + + audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); + if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { + SoundTrigger::setCaptureState(false); + } } SortedVector AudioPolicyManager::getOutputsForDevice( -- GitLab From 0c36a0680da7434825cddec3cb49adcc3f69efe7 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Fri, 3 Aug 2018 04:15:28 -0700 Subject: [PATCH 0183/1530] Fix typo in dequeueInputBuffer's warning Test: Builds Bug: 112177862 Change-Id: I9dc2fbe11f9892f725ccf404e7068f371d454a24 --- media/libstagefright/MediaCodec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 72eff946ce..1610bd0263 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -2664,7 +2664,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { CHECK(msg->senderAwaitsResponse(&replyID)); if (mFlags & kFlagIsAsync) { - ALOGE("dequeueOutputBuffer can't be used in async mode"); + ALOGE("dequeueInputBuffer can't be used in async mode"); PostReplyWithError(replyID, INVALID_OPERATION); break; } -- GitLab From 6a59bd149c4ebee2efd3497cb226db0f6ffe9ffe Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Fri, 3 Aug 2018 04:20:47 -0700 Subject: [PATCH 0184/1530] Fix typo in StagefrightPluginLoader Test: Builds Bug: 112179158 Change-Id: Ifec3e8bdde84c674a1e2de56588b8feebb45ace1 --- media/libstagefright/StagefrightPluginLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index 519e870a1b..dd5903add3 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -46,7 +46,7 @@ StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) } mCreateInputSurface = (CodecBase::CreateInputSurfaceFunc)dlsym( mLibHandle, "CreateInputSurface"); - if (mCreateBuilder == nullptr) { + if (mCreateInputSurface == nullptr) { ALOGD("Failed to find symbol: CreateInputSurface (%s)", dlerror()); } } -- GitLab From 9dd21f48f9a700c5ef1ee5a7e1ee0f0183965d34 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 3 Aug 2018 13:39:29 +0100 Subject: [PATCH 0185/1530] Camera: Trace still capture requests Trace any requests that include a still capture intent. Bug: 112008412 Test: Camera CTS Change-Id: I6e11feff24a69f91eaa9f82410dd551b3fabc872 --- .../device3/Camera3Device.cpp | 19 ++++++++++++++++--- .../libcameraservice/device3/Camera3Device.h | 15 ++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 491ed72907..4c6718c5db 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -2738,13 +2738,13 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) { status_t Camera3Device::registerInFlight(uint32_t frameNumber, int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput, bool hasAppCallback, nsecs_t maxExpectedDuration, - std::set& physicalCameraIds) { + std::set& physicalCameraIds, bool isStillCapture) { ATRACE_CALL(); Mutex::Autolock l(mInFlightLock); ssize_t res; res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput, - hasAppCallback, maxExpectedDuration, physicalCameraIds)); + hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture)); if (res < 0) return res; if (mInFlightMap.size() == 1) { @@ -2810,6 +2810,10 @@ void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) { if (request.numBuffersLeft == 0 && (request.skipResultMetadata || (request.haveResultMetadata && shutterTimestamp != 0))) { + if (request.stillCapture) { + ATRACE_ASYNC_END("still capture", frameNumber); + } + ATRACE_ASYNC_END("frame capture", frameNumber); // Sanity check - if sensor timestamp matches shutter timestamp in the @@ -4939,12 +4943,21 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { if (batchedRequest && i != mNextRequests.size()-1) { hasCallback = false; } + bool isStillCapture = false; + if (!mNextRequests[0].captureRequest->mSettingsList.begin()->metadata.isEmpty()) { + camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t(); + find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_CAPTURE_INTENT, &e); + if ((e.count > 0) && (e.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE)) { + isStillCapture = true; + ATRACE_ASYNC_BEGIN("still capture", mNextRequests[i].halRequest.frame_number); + } + } res = parent->registerInFlight(halRequest->frame_number, totalNumBuffers, captureRequest->mResultExtras, /*hasInput*/halRequest->input_buffer != NULL, hasCallback, calculateMaxExpectedDuration(halRequest->settings), - requestedPhysicalCameras); + requestedPhysicalCameras, isStillCapture); ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64 ", burstId = %" PRId32 ".", __FUNCTION__, diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 96212ab8b6..51e1fb0a42 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -994,6 +994,9 @@ class Camera3Device : // Map of physicalCameraId <-> Metadata std::vector physicalMetadatas; + // Indicates a still capture request. + bool stillCapture; + // Default constructor needed by KeyedVector InFlightRequest() : shutterTimestamp(0), @@ -1004,12 +1007,13 @@ class Camera3Device : hasInputBuffer(false), hasCallback(true), maxExpectedDuration(kDefaultExpectedDuration), - skipResultMetadata(false) { + skipResultMetadata(false), + stillCapture(false) { } InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput, bool hasAppCallback, nsecs_t maxDuration, - const std::set& physicalCameraIdSet) : + const std::set& physicalCameraIdSet, bool isStillCapture) : shutterTimestamp(0), sensorTimestamp(0), requestStatus(OK), @@ -1020,7 +1024,8 @@ class Camera3Device : hasCallback(hasAppCallback), maxExpectedDuration(maxDuration), skipResultMetadata(false), - physicalCameraIds(physicalCameraIdSet) { + physicalCameraIds(physicalCameraIdSet), + stillCapture(isStillCapture) { } }; @@ -1034,10 +1039,10 @@ class Camera3Device : nsecs_t mExpectedInflightDuration = 0; int mInFlightStatusId; - status_t registerInFlight(uint32_t frameNumber, int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput, - bool callback, nsecs_t maxExpectedDuration, std::set& physicalCameraIds); + bool callback, nsecs_t maxExpectedDuration, std::set& physicalCameraIds, + bool isStillCapture); /** * Returns the maximum expected time it'll take for all currently in-flight -- GitLab From 06fcfb071cee44bb0364a379c791bac183c38e45 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 30 Jul 2018 18:23:31 -0700 Subject: [PATCH 0186/1530] Camera: Play shutter sound in absence of CAMERA_MSG_SHUTTER When shutter sound is ON, even if application calls takePicture without CAMERA_MSG_SHUTTER, the camera service should still play shutter sound. Also remove the code using ro.camera.sound.forced since it's not used any more. Test: 3rd party app on device with enforced shutter sound Bug: 111995040 Change-Id: I0d2e888b7f17eff5e5bff8d0f3ec3da7f7ad97b7 --- .../camera/libcameraservice/CameraService.cpp | 25 +++++++--------- .../camera/libcameraservice/CameraService.h | 5 ++-- .../libcameraservice/api1/Camera2Client.cpp | 30 ++----------------- .../libcameraservice/api1/Camera2Client.h | 3 +- .../libcameraservice/api1/CameraClient.cpp | 23 +------------- .../libcameraservice/api1/CameraClient.h | 3 +- .../api1/client2/CaptureSequencer.cpp | 15 ++++------ .../api1/client2/CaptureSequencer.h | 5 ++-- 8 files changed, 26 insertions(+), 83 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index c41de8286c..b85dd51357 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -582,7 +582,7 @@ Status CameraService::filterGetInfoErrorCode(status_t err) { Status CameraService::makeClient(const sp& cameraService, const sp& cameraCb, const String16& packageName, const String8& cameraId, int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid, - bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, /*out*/sp* client) { if (halVersion < 0 || halVersion == deviceVersion) { @@ -594,7 +594,7 @@ Status CameraService::makeClient(const sp& cameraService, sp tmp = static_cast(cameraCb.get()); *client = new CameraClient(cameraService, tmp, packageName, api1CameraId, facing, clientPid, clientUid, - getpid(), legacyMode); + getpid()); } else { // Camera2 API route ALOGW("Camera using old HAL version: %d", deviceVersion); return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL, @@ -612,7 +612,7 @@ Status CameraService::makeClient(const sp& cameraService, *client = new Camera2Client(cameraService, tmp, packageName, cameraId, api1CameraId, facing, clientPid, clientUid, - servicePid, legacyMode); + servicePid); } else { // Camera2 API route sp tmp = static_cast(cameraCb.get()); @@ -636,7 +636,7 @@ Status CameraService::makeClient(const sp& cameraService, sp tmp = static_cast(cameraCb.get()); *client = new CameraClient(cameraService, tmp, packageName, api1CameraId, facing, clientPid, clientUid, - servicePid, legacyMode); + servicePid); } else { // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. ALOGE("Invalid camera HAL version %x: HAL %x device can only be" @@ -735,8 +735,7 @@ Status CameraService::initializeShimMetadata(int cameraId) { sp{nullptr}, id, cameraId, static_cast(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, USE_CALLING_PID, - API_1, /*legacyMode*/ false, /*shimUpdateOnly*/ true, - /*out*/ tmp) + API_1, /*shimUpdateOnly*/ true, /*out*/ tmp) ).isOk()) { ALOGE("%s: Error initializing shim metadata: %s", __FUNCTION__, ret.toString8().string()); } @@ -1200,8 +1199,7 @@ Status CameraService::connect( sp client = nullptr; ret = connectHelper(cameraClient, id, api1CameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1, - /*legacyMode*/ false, /*shimUpdateOnly*/ false, - /*out*/client); + /*shimUpdateOnly*/ false, /*out*/client); if(!ret.isOk()) { logRejected(id, getCallingPid(), String8(clientPackageName), @@ -1227,8 +1225,7 @@ Status CameraService::connectLegacy( Status ret = Status::ok(); sp client = nullptr; ret = connectHelper(cameraClient, id, api1CameraId, halVersion, - clientPackageName, clientUid, USE_CALLING_PID, API_1, - /*legacyMode*/ true, /*shimUpdateOnly*/ false, + clientPackageName, clientUid, USE_CALLING_PID, API_1, /*shimUpdateOnly*/ false, /*out*/client); if(!ret.isOk()) { @@ -1256,9 +1253,7 @@ Status CameraService::connectDevice( ret = connectHelper(cameraCb, id, /*api1CameraId*/-1, CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, - clientUid, USE_CALLING_PID, API_2, - /*legacyMode*/ false, /*shimUpdateOnly*/ false, - /*out*/client); + clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, /*out*/client); if(!ret.isOk()) { logRejected(id, getCallingPid(), String8(clientPackageName), @@ -1273,7 +1268,7 @@ Status CameraService::connectDevice( template Status CameraService::connectHelper(const sp& cameraCb, const String8& cameraId, int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid, - int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, + int clientPid, apiLevel effectiveApiLevel, bool shimUpdateOnly, /*out*/sp& device) { binder::Status ret = binder::Status::ok(); @@ -1358,7 +1353,7 @@ Status CameraService::connectHelper(const sp& cameraCb, const String8& sp tmp = nullptr; if(!(ret = makeClient(this, cameraCb, clientPackageName, cameraId, api1CameraId, facing, - clientPid, clientUid, getpid(), legacyMode, + clientPid, clientUid, getpid(), halVersion, deviceVersion, effectiveApiLevel, /*out*/&tmp)).isOk()) { return ret; diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 8d4bcdbb0e..e4a18d3491 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -585,8 +585,7 @@ private: template binder::Status connectHelper(const sp& cameraCb, const String8& cameraId, int api1CameraId, int halVersion, const String16& clientPackageName, - int clientUid, int clientPid, - apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, + int clientUid, int clientPid, apiLevel effectiveApiLevel, bool shimUpdateOnly, /*out*/sp& device); // Lock guarding camera service state @@ -844,7 +843,7 @@ private: static binder::Status makeClient(const sp& cameraService, const sp& cameraCb, const String16& packageName, const String8& cameraId, int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid, - bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, /*out*/sp* client); status_t checkCameraAccess(const String16& opPackageName); diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index c8b3c2ff29..261cdbfb14 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -54,8 +54,7 @@ Camera2Client::Camera2Client(const sp& cameraService, int cameraFacing, int clientPid, uid_t clientUid, - int servicePid, - bool legacyMode): + int servicePid): Camera2ClientBase(cameraService, cameraClient, clientPackageName, cameraDeviceId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid), @@ -65,8 +64,6 @@ Camera2Client::Camera2Client(const sp& cameraService, SharedParameters::Lock l(mParameters); l.mParameters.state = Parameters::DISCONNECTED; - - mLegacyMode = legacyMode; } status_t Camera2Client::initialize(sp manager, const String8& monitorTags) { @@ -1443,7 +1440,7 @@ status_t Camera2Client::cancelAutoFocus() { return OK; } -status_t Camera2Client::takePicture(int msgType) { +status_t Camera2Client::takePicture(int /*msgType*/) { ATRACE_CALL(); Mutex::Autolock icl(mBinderSerializationLock); status_t res; @@ -1542,7 +1539,7 @@ status_t Camera2Client::takePicture(int msgType) { // Need HAL to have correct settings before (possibly) triggering precapture syncWithDevice(); - res = mCaptureSequencer->startCapture(msgType); + res = mCaptureSequencer->startCapture(); if (res != OK) { ALOGE("%s: Camera %d: Unable to start capture: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); @@ -1662,27 +1659,6 @@ status_t Camera2Client::commandEnableShutterSoundL(bool enable) { return OK; } - // the camera2 api legacy mode can unconditionally disable the shutter sound - if (mLegacyMode) { - ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__); - l.mParameters.playShutterSound = false; - return OK; - } - - // Disabling shutter sound may not be allowed. In that case only - // allow the mediaserver process to disable the sound. - char value[PROPERTY_VALUE_MAX]; - property_get("ro.camera.sound.forced", value, "0"); - if (strncmp(value, "0", 2) != 0) { - // Disabling shutter sound is not allowed. Deny if the current - // process is not mediaserver. - if (getCallingPid() != getpid()) { - ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", - getCallingPid()); - return PERMISSION_DENIED; - } - } - l.mParameters.playShutterSound = false; return OK; } diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h index 44929c324e..a9ea271dd1 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.h +++ b/services/camera/libcameraservice/api1/Camera2Client.h @@ -96,8 +96,7 @@ public: int cameraFacing, int clientPid, uid_t clientUid, - int servicePid, - bool legacyMode); + int servicePid); virtual ~Camera2Client(); diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index f1203f998f..ce44efe2f8 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -40,7 +40,7 @@ CameraClient::CameraClient(const sp& cameraService, const String16& clientPackageName, int cameraId, int cameraFacing, int clientPid, int clientUid, - int servicePid, bool legacyMode): + int servicePid): Client(cameraService, cameraClient, clientPackageName, String8::format("%d", cameraId), cameraId, cameraFacing, clientPid, clientUid, servicePid) @@ -57,7 +57,6 @@ CameraClient::CameraClient(const sp& cameraService, // Callback is disabled by default mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT); - mLegacyMode = legacyMode; mPlayShutterSound = true; LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); } @@ -715,26 +714,6 @@ status_t CameraClient::enableShutterSound(bool enable) { return OK; } - // the camera2 api legacy mode can unconditionally disable the shutter sound - if (mLegacyMode) { - ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__); - mPlayShutterSound = false; - return OK; - } - - // Disabling shutter sound may not be allowed. In that case only - // allow the mediaserver process to disable the sound. - char value[PROPERTY_VALUE_MAX]; - property_get("ro.camera.sound.forced", value, "0"); - if (strcmp(value, "0") != 0) { - // Disabling shutter sound is not allowed. Deny if the current - // process is not mediaserver. - if (getCallingPid() != getpid()) { - ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid()); - return PERMISSION_DENIED; - } - } - mPlayShutterSound = false; return OK; } diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h index 1910536a00..9530b6c4a9 100644 --- a/services/camera/libcameraservice/api1/CameraClient.h +++ b/services/camera/libcameraservice/api1/CameraClient.h @@ -68,8 +68,7 @@ public: int cameraFacing, int clientPid, int clientUid, - int servicePid, - bool legacyMode = false); + int servicePid); ~CameraClient(); virtual status_t initialize(sp manager, diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp index 1ee216fd9a..f42cdd3deb 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp @@ -50,8 +50,7 @@ CaptureSequencer::CaptureSequencer(wp client): mStateTransitionCount(0), mTriggerId(0), mTimeoutCount(0), - mCaptureId(Camera2Client::kCaptureRequestIdStart), - mMsgType(0) { + mCaptureId(Camera2Client::kCaptureRequestIdStart) { ALOGV("%s", __FUNCTION__); } @@ -64,7 +63,7 @@ void CaptureSequencer::setZslProcessor(const wp& processor) { mZslProcessor = processor; } -status_t CaptureSequencer::startCapture(int msgType) { +status_t CaptureSequencer::startCapture() { ALOGV("%s", __FUNCTION__); ATRACE_CALL(); Mutex::Autolock l(mInputMutex); @@ -73,7 +72,6 @@ status_t CaptureSequencer::startCapture(int msgType) { return INVALID_OPERATION; } if (!mStartCapture) { - mMsgType = msgType; mStartCapture = true; mStartCaptureSignal.signal(); } @@ -386,7 +384,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageZslStart( SharedParameters::Lock l(client->getParameters()); /* warning: this also locks a SharedCameraCallbacks */ - shutterNotifyLocked(l.mParameters, client, mMsgType); + shutterNotifyLocked(l.mParameters, client); mShutterNotified = true; mTimeoutCount = kMaxTimeoutsForCaptureEnd; return STANDARD_CAPTURE_WAIT; @@ -610,7 +608,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait( if (!mShutterNotified) { SharedParameters::Lock l(client->getParameters()); /* warning: this also locks a SharedCameraCallbacks */ - shutterNotifyLocked(l.mParameters, client, mMsgType); + shutterNotifyLocked(l.mParameters, client); mShutterNotified = true; } } else if (mTimeoutCount <= 0) { @@ -715,12 +713,11 @@ status_t CaptureSequencer::updateCaptureRequest(const Parameters ¶ms, } /*static*/ void CaptureSequencer::shutterNotifyLocked(const Parameters ¶ms, - const sp& client, int msgType) { + const sp& client) { ATRACE_CALL(); if (params.state == Parameters::STILL_CAPTURE - && params.playShutterSound - && (msgType & CAMERA_MSG_SHUTTER)) { + && params.playShutterSound) { client->getCameraService()->playSound(CameraService::SOUND_SHUTTER); } diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h index f2e37508f9..c23b12da8a 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h @@ -51,7 +51,7 @@ class CaptureSequencer: void setZslProcessor(const wp& processor); // Begin still image capture - status_t startCapture(int msgType); + status_t startCapture(); // Wait until current image capture completes; returns immediately if no // capture is active. Returns TIMED_OUT if capture does not complete during @@ -145,7 +145,6 @@ class CaptureSequencer: bool mAeInPrecapture; int32_t mCaptureId; - int mMsgType; // Main internal methods @@ -172,7 +171,7 @@ class CaptureSequencer: // Emit Shutter/Raw callback to java, and maybe play a shutter sound static void shutterNotifyLocked(const Parameters ¶ms, - const sp& client, int msgType); + const sp& client); }; }; // namespace camera2 -- GitLab From 1e1bcaae46b7901f095bc244d554692146deccf2 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 1 Aug 2018 12:44:46 -0700 Subject: [PATCH 0187/1530] MediaPlayer2: Remove libdrmframework.so dependency. libdrmframework.so is being used in FileSource and MediaHTTP for Forward Lock DRM use case. This change removes this dependency from MediaPlayer2 by adding ClearFileSource and ClearMediaHTTP. Forward Lock DRM use case will be covered by MediaPlayer1. Test: build & MediaPlayerTest & DrmTest Bug: 111406958 Change-Id: I5cf51b2d6f477e8fa8950ac931c0142491baa747 --- .../nuplayer2/GenericSource2.cpp | 6 +- media/libstagefright/Android.bp | 9 +- .../libstagefright/ClearDataSourceFactory.cpp | 117 ++++++++++++ media/libstagefright/ClearFileSource.cpp | 143 ++++++++++++++ media/libstagefright/FileSource.cpp | 94 +-------- media/libstagefright/http/ClearMediaHTTP.cpp | 180 ++++++++++++++++++ media/libstagefright/http/MediaHTTP.cpp | 143 +------------- .../httplive/HTTPDownloader.cpp | 8 +- .../stagefright/ClearDataSourceFactory.h | 45 +++++ .../media/stagefright/ClearFileSource.h | 68 +++++++ .../media/stagefright/ClearMediaHTTP.h | 71 +++++++ .../include/media/stagefright/FileSource.h | 24 +-- .../include/media/stagefright/MediaHTTP.h | 33 +--- media/libstagefright/rtsp/SDPLoader.cpp | 4 +- 14 files changed, 649 insertions(+), 296 deletions(-) create mode 100644 media/libstagefright/ClearDataSourceFactory.cpp create mode 100644 media/libstagefright/ClearFileSource.cpp create mode 100644 media/libstagefright/http/ClearMediaHTTP.cpp create mode 100644 media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h create mode 100644 media/libstagefright/include/media/stagefright/ClearFileSource.h create mode 100644 media/libstagefright/include/media/stagefright/ClearMediaHTTP.h diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp index 196b10364a..a6d88a281e 100644 --- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -368,7 +368,7 @@ void NuPlayer2::GenericSource2::onPrepareAsync() { String8 contentType; if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) { - mHttpSource = DataSourceFactory::CreateMediaHTTP(mHTTPService); + mHttpSource = ClearDataSourceFactory::CreateMediaHTTP(mHTTPService); if (mHttpSource == NULL) { ALOGE("Failed to create http source!"); notifyPreparedAndCleanup(UNKNOWN_ERROR); @@ -378,7 +378,7 @@ void NuPlayer2::GenericSource2::onPrepareAsync() { mLock.unlock(); // This might take long time if connection has some issue. - sp dataSource = DataSourceFactory::CreateFromURI( + sp dataSource = ClearDataSourceFactory::CreateFromURI( mHTTPService, uri, &mUriHeaders, &contentType, static_cast(mHttpSource.get())); mLock.lock(); diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 48e351b594..27383cbdfc 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -105,6 +105,7 @@ cc_library_shared { "DataConverter.cpp", "DataSourceFactory.cpp", "DataURISource.cpp", + "ClearFileSource.cpp", "FileSource.cpp", "FrameDecoder.cpp", "HTTPBase.cpp", @@ -121,6 +122,7 @@ cc_library_shared { "MediaCodecSource.cpp", "MediaExtractorFactory.cpp", "MediaSync.cpp", + "http/ClearMediaHTTP.cpp", "http/MediaHTTP.cpp", "MediaMuxer.cpp", "NuCachedSource2.cpp", @@ -232,9 +234,9 @@ cc_library { srcs: [ "CallbackDataSource.cpp", "CallbackMediaSource.cpp", - "DataSourceFactory.cpp", + "ClearDataSourceFactory.cpp", + "ClearFileSource.cpp", "DataURISource.cpp", - "FileSource.cpp", "HTTPBase.cpp", "HevcUtils.cpp", "InterfaceUtils.cpp", @@ -246,13 +248,12 @@ cc_library { "RemoteMediaSource.cpp", "Utils.cpp", "VideoFrameScheduler.cpp", - "http/MediaHTTP.cpp", + "http/ClearMediaHTTP.cpp", ], shared_libs: [ "libbinder", "libcutils", - "libdrmframework", "libgui", "liblog", "libmedia_player2_util", diff --git a/media/libstagefright/ClearDataSourceFactory.cpp b/media/libstagefright/ClearDataSourceFactory.cpp new file mode 100644 index 0000000000..5d23fda539 --- /dev/null +++ b/media/libstagefright/ClearDataSourceFactory.cpp @@ -0,0 +1,117 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "ClearDataSourceFactory" + +#include "include/HTTPBase.h" +#include "include/NuCachedSource2.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +// static +sp ClearDataSourceFactory::CreateFromURI( + const sp &httpService, + const char *uri, + const KeyedVector *headers, + String8 *contentType, + HTTPBase *httpSource) { + if (contentType != NULL) { + *contentType = ""; + } + + sp source; + if (!strncasecmp("file://", uri, 7)) { + source = new ClearFileSource(uri + 7); + } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) { + if (httpService == NULL) { + ALOGE("Invalid http service!"); + return NULL; + } + + if (httpSource == NULL) { + sp conn = httpService->makeHTTPConnection(); + if (conn == NULL) { + ALOGE("Failed to make http connection from http service!"); + return NULL; + } + httpSource = new ClearMediaHTTP(conn); + } + + String8 cacheConfig; + bool disconnectAtHighwatermark = false; + KeyedVector nonCacheSpecificHeaders; + if (headers != NULL) { + nonCacheSpecificHeaders = *headers; + NuCachedSource2::RemoveCacheSpecificHeaders( + &nonCacheSpecificHeaders, + &cacheConfig, + &disconnectAtHighwatermark); + } + + if (httpSource->connect(uri, &nonCacheSpecificHeaders) != OK) { + ALOGE("Failed to connect http source!"); + return NULL; + } + + if (contentType != NULL) { + *contentType = httpSource->getMIMEType(); + } + + source = NuCachedSource2::Create( + httpSource, + cacheConfig.isEmpty() ? NULL : cacheConfig.string(), + disconnectAtHighwatermark); + } else if (!strncasecmp("data:", uri, 5)) { + source = DataURISource::Create(uri); + } else { + // Assume it's a filename. + source = new ClearFileSource(uri); + } + + if (source == NULL || source->initCheck() != OK) { + return NULL; + } + + return source; +} + +sp ClearDataSourceFactory::CreateFromFd(int fd, int64_t offset, int64_t length) { + sp source = new ClearFileSource(fd, offset, length); + return source->initCheck() != OK ? nullptr : source; +} + +sp ClearDataSourceFactory::CreateMediaHTTP(const sp &httpService) { + if (httpService == NULL) { + return NULL; + } + + sp conn = httpService->makeHTTPConnection(); + if (conn == NULL) { + return NULL; + } else { + return new ClearMediaHTTP(conn); + } +} + +} // namespace android diff --git a/media/libstagefright/ClearFileSource.cpp b/media/libstagefright/ClearFileSource.cpp new file mode 100644 index 0000000000..e3a2cb721e --- /dev/null +++ b/media/libstagefright/ClearFileSource.cpp @@ -0,0 +1,143 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "ClearFileSource" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +ClearFileSource::ClearFileSource(const char *filename) + : mFd(-1), + mOffset(0), + mLength(-1), + mName("") { + + if (filename) { + mName = String8::format("FileSource(%s)", filename); + } + ALOGV("%s", filename); + mFd = open(filename, O_LARGEFILE | O_RDONLY); + + if (mFd >= 0) { + mLength = lseek64(mFd, 0, SEEK_END); + } else { + ALOGE("Failed to open file '%s'. (%s)", filename, strerror(errno)); + } +} + +ClearFileSource::ClearFileSource(int fd, int64_t offset, int64_t length) + : mFd(fd), + mOffset(offset), + mLength(length), + mName("") { + ALOGV("fd=%d (%s), offset=%lld, length=%lld", + fd, nameForFd(fd).c_str(), (long long) offset, (long long) length); + + if (mOffset < 0) { + mOffset = 0; + } + if (mLength < 0) { + mLength = 0; + } + if (mLength > INT64_MAX - mOffset) { + mLength = INT64_MAX - mOffset; + } + struct stat s; + if (fstat(fd, &s) == 0) { + if (mOffset > s.st_size) { + mOffset = s.st_size; + mLength = 0; + } + if (mOffset + mLength > s.st_size) { + mLength = s.st_size - mOffset; + } + } + if (mOffset != offset || mLength != length) { + ALOGW("offset/length adjusted from %lld/%lld to %lld/%lld", + (long long) offset, (long long) length, + (long long) mOffset, (long long) mLength); + } + + mName = String8::format( + "FileSource(fd(%s), %lld, %lld)", + nameForFd(fd).c_str(), + (long long) mOffset, + (long long) mLength); + +} + +ClearFileSource::~ClearFileSource() { + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } +} + +status_t ClearFileSource::initCheck() const { + return mFd >= 0 ? OK : NO_INIT; +} + +ssize_t ClearFileSource::readAt(off64_t offset, void *data, size_t size) { + if (mFd < 0) { + return NO_INIT; + } + + Mutex::Autolock autoLock(mLock); + if (mLength >= 0) { + if (offset >= mLength) { + return 0; // read beyond EOF. + } + uint64_t numAvailable = mLength - offset; + if ((uint64_t)size > numAvailable) { + size = numAvailable; + } + } + return readAt_l(offset, data, size); +} + +ssize_t ClearFileSource::readAt_l(off64_t offset, void *data, size_t size) { + off64_t result = lseek64(mFd, offset + mOffset, SEEK_SET); + if (result == -1) { + ALOGE("seek to %lld failed", (long long)(offset + mOffset)); + return UNKNOWN_ERROR; + } + + return ::read(mFd, data, size); +} + +status_t ClearFileSource::getSize(off64_t *size) { + Mutex::Autolock autoLock(mLock); + + if (mFd < 0) { + return NO_INIT; + } + + *size = mLength; + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index eef5314e02..aee7fd8b49 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -22,90 +22,28 @@ #include #include #include -#include -#include -#include -#include -#include namespace android { FileSource::FileSource(const char *filename) - : mFd(-1), - mOffset(0), - mLength(-1), - mName(""), + : ClearFileSource(filename), mDecryptHandle(NULL), mDrmManagerClient(NULL), mDrmBufOffset(0), mDrmBufSize(0), mDrmBuf(NULL){ - - if (filename) { - mName = String8::format("FileSource(%s)", filename); - } - ALOGV("%s", filename); - mFd = open(filename, O_LARGEFILE | O_RDONLY); - - if (mFd >= 0) { - mLength = lseek64(mFd, 0, SEEK_END); - } else { - ALOGE("Failed to open file '%s'. (%s)", filename, strerror(errno)); - } } FileSource::FileSource(int fd, int64_t offset, int64_t length) - : mFd(fd), - mOffset(offset), - mLength(length), - mName(""), + : ClearFileSource(fd, offset, length), mDecryptHandle(NULL), mDrmManagerClient(NULL), mDrmBufOffset(0), mDrmBufSize(0), mDrmBuf(NULL) { - ALOGV("fd=%d (%s), offset=%lld, length=%lld", - fd, nameForFd(fd).c_str(), (long long) offset, (long long) length); - - if (mOffset < 0) { - mOffset = 0; - } - if (mLength < 0) { - mLength = 0; - } - if (mLength > INT64_MAX - mOffset) { - mLength = INT64_MAX - mOffset; - } - struct stat s; - if (fstat(fd, &s) == 0) { - if (mOffset > s.st_size) { - mOffset = s.st_size; - mLength = 0; - } - if (mOffset + mLength > s.st_size) { - mLength = s.st_size - mOffset; - } - } - if (mOffset != offset || mLength != length) { - ALOGW("offset/length adjusted from %lld/%lld to %lld/%lld", - (long long) offset, (long long) length, - (long long) mOffset, (long long) mLength); - } - - mName = String8::format( - "FileSource(fd(%s), %lld, %lld)", - nameForFd(fd).c_str(), - (long long) mOffset, - (long long) mLength); - } FileSource::~FileSource() { - if (mFd >= 0) { - ::close(mFd); - mFd = -1; - } - if (mDrmBuf != NULL) { delete[] mDrmBuf; mDrmBuf = NULL; @@ -124,10 +62,6 @@ FileSource::~FileSource() { } } -status_t FileSource::initCheck() const { - return mFd >= 0 ? OK : NO_INIT; -} - ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) { if (mFd < 0) { return NO_INIT; @@ -147,30 +81,12 @@ ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) { if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED == mDecryptHandle->decryptApiType) { - return readAtDRM(offset, data, size); + return readAtDRM_l(offset, data, size); } else { - off64_t result = lseek64(mFd, offset + mOffset, SEEK_SET); - if (result == -1) { - ALOGE("seek to %lld failed", (long long)(offset + mOffset)); - return UNKNOWN_ERROR; - } - - return ::read(mFd, data, size); + return readAt_l(offset, data, size); } } -status_t FileSource::getSize(off64_t *size) { - Mutex::Autolock autoLock(mLock); - - if (mFd < 0) { - return NO_INIT; - } - - *size = mLength; - - return OK; -} - sp FileSource::DrmInitialization(const char *mime) { if (getuid() == AID_MEDIA_EX) return nullptr; // no DRM in media extractor if (mDrmManagerClient == NULL) { @@ -194,7 +110,7 @@ sp FileSource::DrmInitialization(const char *mime) { return mDecryptHandle; } -ssize_t FileSource::readAtDRM(off64_t offset, void *data, size_t size) { +ssize_t FileSource::readAtDRM_l(off64_t offset, void *data, size_t size) { size_t DRM_CACHE_SIZE = 1024; if (mDrmBuf == NULL) { mDrmBuf = new unsigned char[DRM_CACHE_SIZE]; diff --git a/media/libstagefright/http/ClearMediaHTTP.cpp b/media/libstagefright/http/ClearMediaHTTP.cpp new file mode 100644 index 0000000000..bfbad1e7b0 --- /dev/null +++ b/media/libstagefright/http/ClearMediaHTTP.cpp @@ -0,0 +1,180 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "ClearMediaHTTP" +#include + +#include + +#include +#include +#include + +#include + +namespace android { + +ClearMediaHTTP::ClearMediaHTTP(const sp &conn) + : mInitCheck((conn != NULL) ? OK : NO_INIT), + mHTTPConnection(conn), + mCachedSizeValid(false), + mCachedSize(0ll) { +} + +ClearMediaHTTP::~ClearMediaHTTP() { +} + +status_t ClearMediaHTTP::connect( + const char *uri, + const KeyedVector *headers, + off64_t /* offset */) { + if (mInitCheck != OK) { + return mInitCheck; + } + + KeyedVector extHeaders; + if (headers != NULL) { + extHeaders = *headers; + } + + if (extHeaders.indexOfKey(String8("User-Agent")) < 0) { + extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str())); + } + + mLastURI = uri; + // reconnect() calls with uri == old mLastURI.c_str(), which gets zapped + // as part of the above assignment. Ensure no accidental later use. + uri = NULL; + + bool success = mHTTPConnection->connect(mLastURI.c_str(), &extHeaders); + + mLastHeaders = extHeaders; + + mCachedSizeValid = false; + + if (success) { + AString sanitized = uriDebugString(mLastURI); + mName = String8::format("ClearMediaHTTP(%s)", sanitized.c_str()); + } + + return success ? OK : UNKNOWN_ERROR; +} + +void ClearMediaHTTP::disconnect() { + mName = String8("ClearMediaHTTP()"); + if (mInitCheck != OK) { + return; + } + + mHTTPConnection->disconnect(); +} + +status_t ClearMediaHTTP::initCheck() const { + return mInitCheck; +} + +ssize_t ClearMediaHTTP::readAt(off64_t offset, void *data, size_t size) { + if (mInitCheck != OK) { + return mInitCheck; + } + + int64_t startTimeUs = ALooper::GetNowUs(); + + size_t numBytesRead = 0; + while (numBytesRead < size) { + size_t copy = size - numBytesRead; + + if (copy > 64 * 1024) { + // limit the buffer sizes transferred across binder boundaries + // to avoid spurious transaction failures. + copy = 64 * 1024; + } + + ssize_t n = mHTTPConnection->readAt( + offset + numBytesRead, (uint8_t *)data + numBytesRead, copy); + + if (n < 0) { + return n; + } else if (n == 0) { + break; + } + + numBytesRead += n; + } + + int64_t delayUs = ALooper::GetNowUs() - startTimeUs; + + addBandwidthMeasurement(numBytesRead, delayUs); + + return numBytesRead; +} + +status_t ClearMediaHTTP::getSize(off64_t *size) { + if (mInitCheck != OK) { + return mInitCheck; + } + + // Caching the returned size so that it stays valid even after a + // disconnect. NuCachedSource2 relies on this. + + if (!mCachedSizeValid) { + mCachedSize = mHTTPConnection->getSize(); + mCachedSizeValid = true; + } + + *size = mCachedSize; + + return *size < 0 ? *size : static_cast(OK); +} + +uint32_t ClearMediaHTTP::flags() { + return kWantsPrefetching | kIsHTTPBasedSource; +} + +status_t ClearMediaHTTP::reconnectAtOffset(off64_t offset) { + return connect(mLastURI.c_str(), &mLastHeaders, offset); +} + + +String8 ClearMediaHTTP::getUri() { + if (mInitCheck != OK) { + return String8::empty(); + } + + String8 uri; + if (OK == mHTTPConnection->getUri(&uri)) { + return uri; + } + return String8(mLastURI.c_str()); +} + +String8 ClearMediaHTTP::getMIMEType() const { + if (mInitCheck != OK) { + return String8("application/octet-stream"); + } + + String8 mimeType; + status_t err = mHTTPConnection->getMIMEType(&mimeType); + + if (err != OK) { + return String8("application/octet-stream"); + } + + return mimeType; +} + +} // namespace android diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp index 7c9247e478..0fba3dc377 100644 --- a/media/libstagefright/http/MediaHTTP.cpp +++ b/media/libstagefright/http/MediaHTTP.cpp @@ -30,10 +30,7 @@ namespace android { MediaHTTP::MediaHTTP(const sp &conn) - : mInitCheck((conn != NULL) ? OK : NO_INIT), - mHTTPConnection(conn), - mCachedSizeValid(false), - mCachedSize(0ll), + : ClearMediaHTTP(conn), mDrmManagerClient(NULL) { } @@ -41,117 +38,6 @@ MediaHTTP::~MediaHTTP() { clearDRMState_l(); } -status_t MediaHTTP::connect( - const char *uri, - const KeyedVector *headers, - off64_t /* offset */) { - if (mInitCheck != OK) { - return mInitCheck; - } - - KeyedVector extHeaders; - if (headers != NULL) { - extHeaders = *headers; - } - - if (extHeaders.indexOfKey(String8("User-Agent")) < 0) { - extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str())); - } - - mLastURI = uri; - // reconnect() calls with uri == old mLastURI.c_str(), which gets zapped - // as part of the above assignment. Ensure no accidental later use. - uri = NULL; - - bool success = mHTTPConnection->connect(mLastURI.c_str(), &extHeaders); - - mLastHeaders = extHeaders; - - mCachedSizeValid = false; - - if (success) { - AString sanitized = uriDebugString(mLastURI); - mName = String8::format("MediaHTTP(%s)", sanitized.c_str()); - } - - return success ? OK : UNKNOWN_ERROR; -} - -void MediaHTTP::disconnect() { - mName = String8("MediaHTTP()"); - if (mInitCheck != OK) { - return; - } - - mHTTPConnection->disconnect(); -} - -status_t MediaHTTP::initCheck() const { - return mInitCheck; -} - -ssize_t MediaHTTP::readAt(off64_t offset, void *data, size_t size) { - if (mInitCheck != OK) { - return mInitCheck; - } - - int64_t startTimeUs = ALooper::GetNowUs(); - - size_t numBytesRead = 0; - while (numBytesRead < size) { - size_t copy = size - numBytesRead; - - if (copy > 64 * 1024) { - // limit the buffer sizes transferred across binder boundaries - // to avoid spurious transaction failures. - copy = 64 * 1024; - } - - ssize_t n = mHTTPConnection->readAt( - offset + numBytesRead, (uint8_t *)data + numBytesRead, copy); - - if (n < 0) { - return n; - } else if (n == 0) { - break; - } - - numBytesRead += n; - } - - int64_t delayUs = ALooper::GetNowUs() - startTimeUs; - - addBandwidthMeasurement(numBytesRead, delayUs); - - return numBytesRead; -} - -status_t MediaHTTP::getSize(off64_t *size) { - if (mInitCheck != OK) { - return mInitCheck; - } - - // Caching the returned size so that it stays valid even after a - // disconnect. NuCachedSource2 relies on this. - - if (!mCachedSizeValid) { - mCachedSize = mHTTPConnection->getSize(); - mCachedSizeValid = true; - } - - *size = mCachedSize; - - return *size < 0 ? *size : static_cast(OK); -} - -uint32_t MediaHTTP::flags() { - return kWantsPrefetching | kIsHTTPBasedSource; -} - -status_t MediaHTTP::reconnectAtOffset(off64_t offset) { - return connect(mLastURI.c_str(), &mLastHeaders, offset); -} - // DRM... sp MediaHTTP::DrmInitialization(const char* mime) { @@ -176,33 +62,6 @@ sp MediaHTTP::DrmInitialization(const char* mime) { return mDecryptHandle; } -String8 MediaHTTP::getUri() { - if (mInitCheck != OK) { - return String8::empty(); - } - - String8 uri; - if (OK == mHTTPConnection->getUri(&uri)) { - return uri; - } - return String8(mLastURI.c_str()); -} - -String8 MediaHTTP::getMIMEType() const { - if (mInitCheck != OK) { - return String8("application/octet-stream"); - } - - String8 mimeType; - status_t err = mHTTPConnection->getMIMEType(&mimeType); - - if (err != OK) { - return String8("application/octet-stream"); - } - - return mimeType; -} - void MediaHTTP::clearDRMState_l() { if (mDecryptHandle != NULL) { // To release mDecryptHandle diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp index 72604e3702..59265fe650 100644 --- a/media/libstagefright/httplive/HTTPDownloader.cpp +++ b/media/libstagefright/httplive/HTTPDownloader.cpp @@ -26,8 +26,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -38,7 +38,7 @@ namespace android { HTTPDownloader::HTTPDownloader( const sp &httpService, const KeyedVector &headers) : - mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())), + mHTTPDataSource(new ClearMediaHTTP(httpService->makeHTTPConnection())), mExtraHeaders(headers), mDisconnecting(false) { } @@ -91,7 +91,7 @@ ssize_t HTTPDownloader::fetchBlock( if (reconnect) { if (!strncasecmp(url, "file://", 7)) { - mDataSource = new FileSource(url + 7); + mDataSource = new ClearFileSource(url + 7); } else if (strncasecmp(url, "http://", 7) && strncasecmp(url, "https://", 8)) { return ERROR_UNSUPPORTED; diff --git a/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h b/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h new file mode 100644 index 0000000000..12bcdd3d39 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/ClearDataSourceFactory.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef DATA_SOURCE_FACTORY2_H_ + +#define DATA_SOURCE_FACTORY2_H_ + +#include +#include + +namespace android { + +struct MediaHTTPService; +class String8; +struct HTTPBase; + +class ClearDataSourceFactory { +public: + static sp CreateFromURI( + const sp &httpService, + const char *uri, + const KeyedVector *headers = NULL, + String8 *contentType = NULL, + HTTPBase *httpSource = NULL); + + static sp CreateMediaHTTP(const sp &httpService); + static sp CreateFromFd(int fd, int64_t offset, int64_t length); +}; + +} // namespace android + +#endif // DATA_SOURCE_FACTORY2_H_ diff --git a/media/libstagefright/include/media/stagefright/ClearFileSource.h b/media/libstagefright/include/media/stagefright/ClearFileSource.h new file mode 100644 index 0000000000..be83748d74 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/ClearFileSource.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef CLEAR_FILE_SOURCE_H_ + +#define CLEAR_FILE_SOURCE_H_ + +#include + +#include +#include +#include + +namespace android { + +class ClearFileSource : public DataSource { +public: + ClearFileSource(const char *filename); + // ClearFileSource takes ownership and will close the fd + ClearFileSource(int fd, int64_t offset, int64_t length); + + virtual status_t initCheck() const; + + virtual ssize_t readAt(off64_t offset, void *data, size_t size); + + virtual status_t getSize(off64_t *size); + + virtual uint32_t flags() { + return kIsLocalFileSource; + } + + virtual String8 toString() { + return mName; + } + +protected: + virtual ~ClearFileSource(); + virtual ssize_t readAt_l(off64_t offset, void *data, size_t size); + + int mFd; + int64_t mOffset; + int64_t mLength; + Mutex mLock; + +private: + String8 mName; + + ClearFileSource(const ClearFileSource &); + ClearFileSource &operator=(const ClearFileSource &); +}; + +} // namespace android + +#endif // CLEAR_FILE_SOURCE_H_ + diff --git a/media/libstagefright/include/media/stagefright/ClearMediaHTTP.h b/media/libstagefright/include/media/stagefright/ClearMediaHTTP.h new file mode 100644 index 0000000000..7fe9c74dcd --- /dev/null +++ b/media/libstagefright/include/media/stagefright/ClearMediaHTTP.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef CLEAR_MEDIA_HTTP_H_ + +#define CLEAR_MEDIA_HTTP_H_ + +#include + +#include "include/HTTPBase.h" + +namespace android { + +struct MediaHTTPConnection; + +struct ClearMediaHTTP : public HTTPBase { + ClearMediaHTTP(const sp &conn); + + virtual status_t connect( + const char *uri, + const KeyedVector *headers, + off64_t offset); + + virtual void disconnect(); + + virtual status_t initCheck() const; + + virtual ssize_t readAt(off64_t offset, void *data, size_t size); + + virtual status_t getSize(off64_t *size); + + virtual uint32_t flags(); + + virtual status_t reconnectAtOffset(off64_t offset); + +protected: + virtual ~ClearMediaHTTP(); + + virtual String8 getUri(); + virtual String8 getMIMEType() const; + + AString mLastURI; + +private: + status_t mInitCheck; + sp mHTTPConnection; + + KeyedVector mLastHeaders; + + bool mCachedSizeValid; + off64_t mCachedSize; + + DISALLOW_EVIL_CONSTRUCTORS(ClearMediaHTTP); +}; + +} // namespace android + +#endif // CLEAR_MEDIA_HTTP_H_ diff --git a/media/libstagefright/include/media/stagefright/FileSource.h b/media/libstagefright/include/media/stagefright/FileSource.h index 8604890b4c..b610eefb55 100644 --- a/media/libstagefright/include/media/stagefright/FileSource.h +++ b/media/libstagefright/include/media/stagefright/FileSource.h @@ -20,47 +20,29 @@ #include -#include +#include #include #include #include namespace android { -class FileSource : public DataSource { +class FileSource : public ClearFileSource { public: FileSource(const char *filename); // FileSource takes ownership and will close the fd FileSource(int fd, int64_t offset, int64_t length); - virtual status_t initCheck() const; - virtual ssize_t readAt(off64_t offset, void *data, size_t size); - virtual status_t getSize(off64_t *size); - - virtual uint32_t flags() { - return kIsLocalFileSource; - } - virtual sp DrmInitialization(const char *mime); - virtual String8 toString() { - return mName; - } - static bool requiresDrm(int fd, int64_t offset, int64_t length, const char *mime); protected: virtual ~FileSource(); private: - int mFd; - int64_t mOffset; - int64_t mLength; - Mutex mLock; - String8 mName; - /*for DRM*/ sp mDecryptHandle; DrmManagerClient *mDrmManagerClient; @@ -68,7 +50,7 @@ private: ssize_t mDrmBufSize; unsigned char *mDrmBuf; - ssize_t readAtDRM(off64_t offset, void *data, size_t size); + ssize_t readAtDRM_l(off64_t offset, void *data, size_t size); FileSource(const FileSource &); FileSource &operator=(const FileSource &); diff --git a/media/libstagefright/include/media/stagefright/MediaHTTP.h b/media/libstagefright/include/media/stagefright/MediaHTTP.h index fe0e613c55..acaa6c4bb6 100644 --- a/media/libstagefright/include/media/stagefright/MediaHTTP.h +++ b/media/libstagefright/include/media/stagefright/MediaHTTP.h @@ -19,50 +19,21 @@ #define MEDIA_HTTP_H_ #include - -#include "include/HTTPBase.h" +#include namespace android { struct MediaHTTPConnection; -struct MediaHTTP : public HTTPBase { +struct MediaHTTP : public ClearMediaHTTP { MediaHTTP(const sp &conn); - virtual status_t connect( - const char *uri, - const KeyedVector *headers, - off64_t offset); - - virtual void disconnect(); - - virtual status_t initCheck() const; - - virtual ssize_t readAt(off64_t offset, void *data, size_t size); - - virtual status_t getSize(off64_t *size); - - virtual uint32_t flags(); - - virtual status_t reconnectAtOffset(off64_t offset); - protected: virtual ~MediaHTTP(); virtual sp DrmInitialization(const char* mime); - virtual String8 getUri(); - virtual String8 getMIMEType() const; private: - status_t mInitCheck; - sp mHTTPConnection; - - KeyedVector mLastHeaders; - AString mLastURI; - - bool mCachedSizeValid; - off64_t mCachedSize; - sp mDecryptHandle; DrmManagerClient *mDrmManagerClient; diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp index d459cbd7be..665d51aba3 100644 --- a/media/libstagefright/rtsp/SDPLoader.cpp +++ b/media/libstagefright/rtsp/SDPLoader.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include @@ -41,7 +41,7 @@ SDPLoader::SDPLoader( mFlags(flags), mNetLooper(new ALooper), mCancelled(false), - mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())) { + mHTTPDataSource(new ClearMediaHTTP(httpService->makeHTTPConnection())) { mNetLooper->setName("sdp net"); mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, -- GitLab From 9876eacdbab140e50a69bae1bd5e0020f789fab6 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 3 Aug 2018 12:00:50 -0700 Subject: [PATCH 0188/1530] aaudio_loopback: do not set buffer capacity by default That was causing us to not get a FAST track. Bug: 112186931 Test: adb shell aaudio_loopback -te -pl -Pl -m1 -n2 Test: should get PerformanceMode 12 on INPUT Change-Id: Ib8b00585a80bdd86f752a5ee37aacd92f06db347 --- media/libaaudio/examples/loopback/src/loopback.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index 91ebf73a1b..84f9c22d7f 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -338,7 +338,7 @@ int main(int argc, const char **argv) aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED; int requestedInputChannelCount = NUM_INPUT_CHANNELS; aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_UNSPECIFIED; - int32_t requestedInputCapacity = -1; + int32_t requestedInputCapacity = AAUDIO_UNSPECIFIED; aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; int32_t outputFramesPerBurst = 0; @@ -459,15 +459,8 @@ int main(int argc, const char **argv) argParser.setPerformanceMode(inputPerformanceLevel); argParser.setChannelCount(requestedInputChannelCount); argParser.setSharingMode(requestedInputSharingMode); - - // Make sure the input buffer has plenty of capacity. - // Extra capacity on input should not increase latency if we keep it drained. - int32_t inputBufferCapacity = requestedInputCapacity; - if (inputBufferCapacity < 0) { - int32_t outputBufferCapacity = AAudioStream_getBufferCapacityInFrames(outputStream); - inputBufferCapacity = 2 * outputBufferCapacity; - } - argParser.setBufferCapacity(inputBufferCapacity); + // Warning! If you change input capacity then you may not get a FAST track on Legacy path. + argParser.setBufferCapacity(requestedInputCapacity); result = recorder.open(argParser); if (result != AAUDIO_OK) { -- GitLab From 1882f16c80d6e1e047a43c1e685a005034b138e4 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Thu, 2 Aug 2018 18:05:39 -0700 Subject: [PATCH 0189/1530] Audioflinger dumpsys: Use libjsoncpp for json dump Test: dumpsys media.audio_flinger --json Bug: 68148948 Change-Id: Ieebe2c52e3613e48a5ec5cbb2b1f167a32d5a47d --- media/audioserver/Android.mk | 3 ++ services/audioflinger/Android.mk | 1 + services/audioflinger/AudioFlinger.cpp | 27 ++++++++-------- services/audioflinger/AudioFlinger.h | 3 +- services/audioflinger/FastMixerDumpState.cpp | 34 ++++++++------------ services/audioflinger/FastMixerDumpState.h | 3 +- services/audioflinger/Threads.cpp | 25 ++++++++------ services/audioflinger/Threads.h | 4 +-- 8 files changed, 54 insertions(+), 46 deletions(-) diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk index 70c281aa33..8a97299eb0 100644 --- a/media/audioserver/Android.mk +++ b/media/audioserver/Android.mk @@ -21,6 +21,9 @@ LOCAL_SHARED_LIBRARIES := \ libsoundtriggerservice \ libutils +LOCAL_STATIC_LIBRARIES := \ + libjsoncpp + # TODO oboeservice is the old folder name for aaudioservice. It will be changed. LOCAL_C_INCLUDES := \ frameworks/av/services/audioflinger \ diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index c0aa4777b7..2c26ba407d 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -42,6 +42,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libcpustats \ + libjsoncpp \ libsndfile \ LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 9234364248..9dd92a30c0 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -58,6 +58,8 @@ #include +#include + #include #include @@ -440,8 +442,11 @@ status_t AudioFlinger::dump(int fd, const Vector& args) const bool formatJson = std::any_of(args.begin(), args.end(), [](const String16 &arg) { return arg == String16("--json"); }); if (formatJson) { + Json::Value root = getJsonDump(); + Json::FastWriter writer; + std::string rootStr = writer.write(root); // XXX consider buffering if the string happens to be too long. - dprintf(fd, "%s", getJsonString().c_str()); + dprintf(fd, "%s", rootStr.c_str()); return NO_ERROR; } @@ -556,34 +561,30 @@ status_t AudioFlinger::dump(int fd, const Vector& args) return NO_ERROR; } -std::string AudioFlinger::getJsonString() +Json::Value AudioFlinger::getJsonDump() { - std::string jsonStr = "{\n"; + Json::Value root(Json::objectValue); const bool locked = dumpTryLock(mLock); // failed to lock - AudioFlinger is probably deadlocked if (!locked) { - jsonStr += " \"deadlock_message\": "; - jsonStr += kDeadlockedString; - jsonStr += ",\n"; + root["deadlock_message"] = kDeadlockedString; } // FIXME risky to access data structures without a lock held? - jsonStr += " \"Playback_Threads\": [\n"; + Json::Value playbackThreads = Json::arrayValue; // dump playback threads for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (i != 0) { - jsonStr += ",\n"; - } - jsonStr += mPlaybackThreads.valueAt(i)->getJsonString(); + playbackThreads.append(mPlaybackThreads.valueAt(i)->getJsonDump()); } - jsonStr += "\n ]\n}\n"; if (locked) { mLock.unlock(); } - return jsonStr; + root["playback_threads"] = playbackThreads; + + return root; } sp AudioFlinger::registerPid(pid_t pid) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 95b947cbdf..a7fdf41fe5 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -79,6 +79,7 @@ #include +#include #include #include #include @@ -114,7 +115,7 @@ public: static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; } virtual status_t dump(int fd, const Vector& args); - std::string getJsonString(); + Json::Value getJsonDump(); // IAudioFlinger interface, in binder opcode order virtual sp createTrack(const CreateTrackInput& input, diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp index ffdc117726..c67ddad50f 100644 --- a/services/audioflinger/FastMixerDumpState.cpp +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -24,6 +24,7 @@ #include #endif #endif +#include #include #include #include @@ -205,14 +206,13 @@ void FastMixerDumpState::dump(int fd) const } } -// TODO get rid of extraneous lines and use better key names. -// TODO may go back to using a library to do the json formatting. -std::string FastMixerDumpState::getJsonString() const +Json::Value FastMixerDumpState::getJsonDump() const { + Json::Value root(Json::objectValue); if (mCommand == FastMixerState::INITIAL) { - return " {\n \"status\": \"uninitialized\"\n }"; + root["status"] = "uninitialized"; + return root; } - std::string jsonStr = " {\n"; #ifdef FAST_THREAD_STATISTICS // find the interval of valid samples const uint32_t bounds = mBounds; @@ -230,31 +230,25 @@ std::string FastMixerDumpState::getJsonString() const } // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, // and adjusted CPU load in MHz normalized for CPU clock frequency - std::string jsonWallStr = " \"wall_clock_time\":["; - std::string jsonLoadNsStr = " \"raw_cpu_load\":["; + Json::Value jsonWall(Json::arrayValue); + Json::Value jsonLoadNs(Json::arrayValue); // loop over all the samples for (uint32_t j = 0; j < n; ++j) { size_t i = oldestClosed++ & (mSamplingN - 1); uint32_t wallNs = mMonotonicNs[i]; - if (j != 0) { - jsonWallStr += ','; - jsonLoadNsStr += ','; - } - /* jsonObject["wall"].append(wallNs); */ - jsonWallStr += std::to_string(wallNs); + jsonWall.append(wallNs); uint32_t sampleLoadNs = mLoadNs[i]; - jsonLoadNsStr += std::to_string(sampleLoadNs); + jsonLoadNs.append(sampleLoadNs); } - jsonWallStr += ']'; - jsonLoadNsStr += ']'; if (n) { - jsonStr += jsonWallStr + ",\n" + jsonLoadNsStr + "\n"; + root["wall_clock_time_ns"] = jsonWall; + root["raw_cpu_load_ns"] = jsonLoadNs; + root["status"] = "ok"; } else { - //dprintf(fd, " No FastMixer statistics available currently\n"); + root["status"] = "unavailable"; } #endif - jsonStr += " }"; - return jsonStr; + return root; } } // android diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index 81c4175e37..69c2e4e87b 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Configuration.h" #include "FastThreadDumpState.h" #include "FastMixerState.h" @@ -67,7 +68,7 @@ struct FastMixerDumpState : FastThreadDumpState { /*virtual*/ ~FastMixerDumpState(); void dump(int fd) const; // should only be called on a stable copy, not the original - std::string getJsonString() const; // should only be called on a stable copy, not the original + Json::Value getJsonDump() const; // should only be called on a stable copy, not the original double mLatencyMs = 0.; // measured latency, default of 0 if no valid timestamp read. uint32_t mWriteSequence; // incremented before and after each write() diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6c7179e985..31115854b6 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1763,9 +1764,9 @@ void AudioFlinger::PlaybackThread::dump(int fd, const Vector& args) mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); } -std::string AudioFlinger::PlaybackThread::getJsonString() const +Json::Value AudioFlinger::PlaybackThread::getJsonDump() const { - return "{}"; + return Json::Value(Json::objectValue); } void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& args __unused) @@ -5141,14 +5142,20 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar } } -std::string AudioFlinger::MixerThread::getJsonString() const +Json::Value AudioFlinger::MixerThread::getJsonDump() const { - // Make a non-atomic copy of fast mixer dump state so it won't change underneath us - // while we are dumping it. It may be inconsistent, but it won't mutate! - // This is a large object so we place it on the heap. - // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages. - return std::unique_ptr(new FastMixerDumpState(mFastMixerDumpState)) - ->getJsonString(); + Json::Value root; + if (hasFastMixer()) { + // Make a non-atomic copy of fast mixer dump state so it won't change underneath us + // while we are dumping it. It may be inconsistent, but it won't mutate! + // This is a large object so we place it on the heap. + // FIXME 25972958: Need an intelligent copy constructor that does not touch unused pages. + const std::unique_ptr copy(new FastMixerDumpState(mFastMixerDumpState)); + root["fastmixer_stats"] = copy->getJsonDump(); + } else { + root["fastmixer_stats"] = "no_fastmixer"; + } + return root; } uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index dc237174bc..09a5530334 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -662,7 +662,7 @@ public: void dump(int fd, const Vector& args); // returns a string of audio performance related data in JSON format. - virtual std::string getJsonString() const; + virtual Json::Value getJsonDump() const; // Thread virtuals virtual bool threadLoop(); @@ -1108,7 +1108,7 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); virtual void dumpInternals(int fd, const Vector& args); - std::string getJsonString() const override; + Json::Value getJsonDump() const override; virtual bool isTrackAllowed_l( audio_channel_mask_t channelMask, audio_format_t format, -- GitLab From e32a41301f8853ff628004743c102eb138c10942 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 23 Jul 2018 14:17:38 -0700 Subject: [PATCH 0190/1530] MediaCas: return audio samples in scrambled form We can no longer assume audio stream can always be descrambled into clear buffers. If audio is scrambled, handle it the same way and extract samples in scrambled form. Set a dummy format on the track to provide info about stream type only, and leave it to the decoder to handle the format change. bug: 111271614 Test: CTS MediaDrmClearkeyTest#testClearKeyPlaybackMpeg2ts MediaDrmClearKeyTest#testPlaybackMpeg2ts Change-Id: I2a803a92f395f06af54f3ad65a5c436edd7ce251 --- media/libstagefright/MetaDataUtils.cpp | 44 ++++++ .../include/media/stagefright/MetaDataUtils.h | 1 + media/libstagefright/mpeg2ts/ATSParser.cpp | 31 ++-- media/libstagefright/mpeg2ts/ESQueue.cpp | 146 ++++++++++-------- media/libstagefright/mpeg2ts/ESQueue.h | 3 +- 5 files changed, 150 insertions(+), 75 deletions(-) diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp index 04f6adea4d..2475e7b2c5 100644 --- a/media/libstagefright/MetaDataUtils.cpp +++ b/media/libstagefright/MetaDataUtils.cpp @@ -16,8 +16,10 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MetaDataUtils" +#include #include +#include #include #include #include @@ -25,6 +27,10 @@ namespace android { bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) { + if (data == nullptr || size == 0) { + return false; + } + int32_t width; int32_t height; int32_t sarWidth; @@ -46,6 +52,44 @@ bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t si return true; } +bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) { + if (data == nullptr || size < 7) { + return false; + } + + ABitReader bits(data, size); + + // adts_fixed_header + + if (bits.getBits(12) != 0xfffu) { + ALOGE("Wrong atds_fixed_header"); + return false; + } + + bits.skipBits(4); // ID, layer, protection_absent + + unsigned profile = bits.getBits(2); + if (profile == 3u) { + ALOGE("profile should not be 3"); + return false; + } + unsigned sampling_freq_index = bits.getBits(4); + bits.getBits(1); // private_bit + unsigned channel_configuration = bits.getBits(3); + if (channel_configuration == 0u) { + ALOGE("channel_config should not be 0"); + return false; + } + + if (!MakeAACCodecSpecificData( + meta, profile, sampling_freq_index, channel_configuration)) { + return false; + } + + meta.setInt32(kKeyIsADTS, true); + return true; +} + bool MakeAACCodecSpecificData( MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index, diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h index d5a8080e63..4a7107d9c4 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h +++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h @@ -24,6 +24,7 @@ namespace android { struct ABuffer; bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size); +bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size); bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index, unsigned channel_configuration); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index cc318153de..5cf6e5a268 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -775,10 +775,12 @@ ATSParser::Stream::Stream( ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d", elementaryPID, streamType, mScrambled, mSampleEncrypted); - uint32_t flags = - (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData : - (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData : - 0; + uint32_t flags = 0; + if (((isVideo() || isAudio()) && mScrambled)) { + flags = ElementaryStreamQueue::kFlag_ScrambledData; + } else if (mSampleEncrypted) { + flags = ElementaryStreamQueue::kFlag_SampleEncryptedData; + } ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID; @@ -1499,7 +1501,13 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { descrambleBytes = bytesWritten; } - sp buffer; + // |buffer| points to the buffer from which we'd parse the PES header. + // When the output stream is scrambled, it points to mDescrambledBuffer + // (unless all packets in this PES are actually clear, in which case, + // it points to mBuffer since we never copied into mDescrambledBuffer). + // When the output stream is clear, it points to mBuffer, and we'll + // copy all descrambled data back to mBuffer. + sp buffer = mBuffer; if (mQueue->isScrambled()) { // Queue subSample info for scrambled queue sp clearSizesBuffer = new ABuffer(mSubSamples.size() * 4); @@ -1528,15 +1536,18 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { } // Pass the original TS subsample size now. The PES header adjust // will be applied when the scrambled AU is dequeued. + // Note that if descrambleBytes is 0, it means this PES contains only + // all ts packets, leadingClearBytes is entire buffer size. mQueue->appendScrambledData( - mBuffer->data(), mBuffer->size(), sctrl, - isSync, clearSizesBuffer, encSizesBuffer); + mBuffer->data(), mBuffer->size(), + (descrambleBytes > 0) ? descrambleBytes : mBuffer->size(), + sctrl, isSync, clearSizesBuffer, encSizesBuffer); - buffer = mDescrambledBuffer; + if (descrambleBytes > 0) { + buffer = mDescrambledBuffer; + } } else { memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes); - - buffer = mBuffer; } ABitReader br(buffer->data(), buffer->size()); diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index 34d0bcc249..da6bc4e0ae 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -366,7 +366,8 @@ status_t ElementaryStreamQueue::appendData( ALOGE("appending data after EOS"); return ERROR_MALFORMED; } - if (mBuffer == NULL || mBuffer->size() == 0) { + + if (!isScrambled() && (mBuffer == NULL || mBuffer->size() == 0)) { switch (mMode) { case H264: case MPEG_VIDEO: @@ -617,6 +618,7 @@ status_t ElementaryStreamQueue::appendData( void ElementaryStreamQueue::appendScrambledData( const void *data, size_t size, + size_t leadingClearBytes, int32_t keyId, bool isSync, sp clearSizes, sp encSizes) { if (!isScrambled()) { @@ -644,6 +646,7 @@ void ElementaryStreamQueue::appendScrambledData( ScrambledRangeInfo scrambledInfo; scrambledInfo.mLength = size; + scrambledInfo.mLeadingClearBytes = leadingClearBytes; scrambledInfo.mKeyId = keyId; scrambledInfo.mIsSync = isSync; scrambledInfo.mClearSizes = clearSizes; @@ -656,7 +659,6 @@ void ElementaryStreamQueue::appendScrambledData( sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { size_t nextScan = mBuffer->size(); - mBuffer->setRange(0, 0); int32_t pesOffset = 0, pesScramblingControl = 0; int64_t timeUs = fetchTimestamp(nextScan, &pesOffset, &pesScramblingControl); if (timeUs < 0ll) { @@ -667,6 +669,7 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { // return scrambled unit int32_t keyId = pesScramblingControl, isSync = 0, scrambledLength = 0; sp clearSizes, encSizes; + size_t leadingClearBytes; while (mScrambledRangeInfos.size() > mRangeInfos.size()) { auto it = mScrambledRangeInfos.begin(); ALOGV("[stream %d] fetching scrambled range: size=%zu", mMode, it->mLength); @@ -684,6 +687,7 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { clearSizes = it->mClearSizes; encSizes = it->mEncSizes; isSync = it->mIsSync; + leadingClearBytes = it->mLeadingClearBytes; mScrambledRangeInfos.erase(it); } if (scrambledLength == 0) { @@ -691,6 +695,70 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { return NULL; } + // Retrieve the leading clear bytes info, and use it to set the clear + // range on mBuffer. Note that the leading clear bytes includes the + // PES header portion, while mBuffer doesn't. + if ((int32_t)leadingClearBytes > pesOffset) { + mBuffer->setRange(0, leadingClearBytes - pesOffset); + } else { + mBuffer->setRange(0, 0); + } + + // Try to parse formats, and if unavailable set up a dummy format. + // Only support the following modes for scrambled content for now. + // (will be expanded later). + if (mFormat == NULL) { + mFormat = new MetaData; + switch (mMode) { + case H264: + { + if (!MakeAVCCodecSpecificData( + *mFormat, mBuffer->data(), mBuffer->size())) { + ALOGI("Creating dummy AVC format for scrambled content"); + + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + mFormat->setInt32(kKeyWidth, 1280); + mFormat->setInt32(kKeyHeight, 720); + } + break; + } + case AAC: + { + if (!MakeAACCodecSpecificData( + *mFormat, mBuffer->data(), mBuffer->size())) { + ALOGI("Creating dummy AAC format for scrambled content"); + + MakeAACCodecSpecificData(*mFormat, + 1 /*profile*/, 7 /*sampling_freq_index*/, 1 /*channel_config*/); + mFormat->setInt32(kKeyIsADTS, true); + } + + break; + } + case MPEG_VIDEO: + { + ALOGI("Creating dummy MPEG format for scrambled content"); + + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); + mFormat->setInt32(kKeyWidth, 1280); + mFormat->setInt32(kKeyHeight, 720); + break; + } + default: + { + ALOGE("Unknown mode for scrambled content"); + return NULL; + } + } + + // for MediaExtractor.CasInfo + mFormat->setInt32(kKeyCASystemID, mCASystemId); + mFormat->setData(kKeyCASessionID, + 0, mCasSessionId.data(), mCasSessionId.size()); + } + + mBuffer->setRange(0, 0); + // copy into scrambled access unit sp scrambledAccessUnit = ABuffer::CreateAsCopy( mScrambledBuffer->data(), scrambledLength); @@ -722,7 +790,11 @@ sp ElementaryStreamQueue::dequeueScrambledAccessUnit() { } sp ElementaryStreamQueue::dequeueAccessUnit() { - if ((mFlags & kFlag_AlignedData) && mMode == H264 && !isScrambled()) { + if (isScrambled()) { + return dequeueScrambledAccessUnit(); + } + + if ((mFlags & kFlag_AlignedData) && mMode == H264) { if (mRangeInfos.empty()) { return NULL; } @@ -1024,25 +1096,11 @@ sp ElementaryStreamQueue::dequeueAccessUnitAAC() { bool protection_absent = bits.getBits(1) != 0; if (mFormat == NULL) { - unsigned profile = bits.getBits(2); - if (profile == 3u) { - ALOGE("profile should not be 3"); - return NULL; - } - unsigned sampling_freq_index = bits.getBits(4); - bits.getBits(1); // private_bit - unsigned channel_configuration = bits.getBits(3); - if (channel_configuration == 0u) { - ALOGE("channel_config should not be 0"); + mFormat = new MetaData; + if (!MakeAACCodecSpecificData( + *mFormat, mBuffer->data() + offset, mBuffer->size() - offset)) { return NULL; } - bits.skipBits(2); // original_copy, home - - mFormat = new MetaData; - MakeAACCodecSpecificData(*mFormat, - profile, sampling_freq_index, channel_configuration); - - mFormat->setInt32(kKeyIsADTS, true); int32_t sampleRate; int32_t numChannels; @@ -1057,12 +1115,12 @@ sp ElementaryStreamQueue::dequeueAccessUnitAAC() { ALOGI("found AAC codec config (%d Hz, %d channels)", sampleRate, numChannels); - } else { - // profile_ObjectType, sampling_frequency_index, private_bits, - // channel_configuration, original_copy, home - bits.skipBits(12); } + // profile_ObjectType, sampling_frequency_index, private_bits, + // channel_configuration, original_copy, home + bits.skipBits(12); + // adts_variable_header // copyright_identification_bit, copyright_identification_start @@ -1177,27 +1235,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp( } sp ElementaryStreamQueue::dequeueAccessUnitH264() { - if (isScrambled()) { - if (mBuffer == NULL || mBuffer->size() == 0) { - return NULL; - } - if (mFormat == NULL) { - mFormat = new MetaData; - if (!MakeAVCCodecSpecificData(*mFormat, mBuffer->data(), mBuffer->size())) { - ALOGW("Creating dummy AVC format for scrambled content"); - mFormat = new MetaData; - mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); - mFormat->setInt32(kKeyWidth, 1280); - mFormat->setInt32(kKeyHeight, 720); - } - // for MediaExtractor.CasInfo - mFormat->setInt32(kKeyCASystemID, mCASystemId); - mFormat->setData(kKeyCASessionID, 0, - mCasSessionId.data(), mCasSessionId.size()); - } - return dequeueScrambledAccessUnit(); - } - const uint8_t *data = mBuffer->data(); size_t size = mBuffer->size(); @@ -1497,25 +1534,6 @@ static sp MakeMPEGVideoESDS(const sp &csd) { } sp ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() { - if (isScrambled()) { - if (mBuffer == NULL || mBuffer->size() == 0) { - return NULL; - } - if (mFormat == NULL) { - ALOGI("Creating dummy MPEG format for scrambled content"); - mFormat = new MetaData; - mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); - mFormat->setInt32(kKeyWidth, 1280); - mFormat->setInt32(kKeyHeight, 720); - - // for MediaExtractor.CasInfo - mFormat->setInt32(kKeyCASystemID, mCASystemId); - mFormat->setData(kKeyCASessionID, 0, - mCasSessionId.data(), mCasSessionId.size()); - } - return dequeueScrambledAccessUnit(); - } - const uint8_t *data = mBuffer->data(); size_t size = mBuffer->size(); diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index 399214ad1e..32dead49f3 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -60,6 +60,7 @@ struct ElementaryStreamQueue { void appendScrambledData( const void *data, size_t size, + size_t leadingClearBytes, int32_t keyId, bool isSync, sp clearSizes, sp encSizes); @@ -85,8 +86,8 @@ private: }; struct ScrambledRangeInfo { - //int64_t mTimestampUs; size_t mLength; + size_t mLeadingClearBytes; int32_t mKeyId; int32_t mIsSync; sp mClearSizes; -- GitLab From e36499115c0bbd619358017afa2099edb1f568d8 Mon Sep 17 00:00:00 2001 From: rago Date: Tue, 7 Aug 2018 14:28:29 -0700 Subject: [PATCH 0191/1530] Fix for redundant comparisons bug Fixed typo on comparisons for isAudioPlaybackRateEqual. Bug: 112144275 Test: manual testing Change-Id: Ib7fbcda2bbf8f10499cbff312abee2cc548e9dae --- media/libaudioprocessing/include/media/AudioResamplerPublic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libaudioprocessing/include/media/AudioResamplerPublic.h b/media/libaudioprocessing/include/media/AudioResamplerPublic.h index 055f724a1d..50ca33d843 100644 --- a/media/libaudioprocessing/include/media/AudioResamplerPublic.h +++ b/media/libaudioprocessing/include/media/AudioResamplerPublic.h @@ -104,8 +104,8 @@ static inline bool isAudioPlaybackRateEqual(const AudioPlaybackRate &pr1, const AudioPlaybackRate &pr2) { return fabs(pr1.mSpeed - pr2.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA && fabs(pr1.mPitch - pr2.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA && - pr2.mStretchMode == pr2.mStretchMode && - pr2.mFallbackMode == pr2.mFallbackMode; + pr1.mStretchMode == pr2.mStretchMode && + pr1.mFallbackMode == pr2.mFallbackMode; } static inline bool isAudioPlaybackRateValid(const AudioPlaybackRate &playbackRate) { -- GitLab From a9d3b5b323a676af06e6c8dd10113438bc89f56b Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 7 Aug 2018 19:08:24 -0700 Subject: [PATCH 0192/1530] LoudnessEnhancer: Convert effect to float Test: CTS LoudnessEnhancerTest, logcat Bug: 112335274 Change-Id: I58e83f10da729779db153d2376ceacda6679b6bc --- .../loudness/EffectLoudnessEnhancer.cpp | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp index 9d29cf14aa..d61efd36a6 100644 --- a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp +++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp @@ -30,6 +30,26 @@ #include #include "dsp/core/dynamic_range_compression.h" +// BUILD_FLOAT targets building a float effect instead of the legacy int16_t effect. +#define BUILD_FLOAT + +#ifdef BUILD_FLOAT + +static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_FLOAT; + +#else + +static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_16_BIT; + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +#endif // BUILD_FLOAT + extern "C" { // effect_handle_t interface implementation for LE effect @@ -80,13 +100,6 @@ void LE_reset(LoudnessEnhancerContext *pContext) } } -static inline int16_t clamp16(int32_t sample) -{ - if ((sample>>15) ^ (sample>>31)) - sample = 0x7FFF ^ (sample>>31); - return sample; -} - //---------------------------------------------------------------------------- // LE_setConfig() //---------------------------------------------------------------------------- @@ -111,7 +124,7 @@ int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig) if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; - if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; + if (pConfig->inputCfg.format != kProcessFormat) return -EINVAL; pContext->mConfig = *pConfig; @@ -159,7 +172,7 @@ int LE_init(LoudnessEnhancerContext *pContext) pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.inputCfg.format = kProcessFormat; pContext->mConfig.inputCfg.samplingRate = 44100; pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; @@ -167,7 +180,7 @@ int LE_init(LoudnessEnhancerContext *pContext) pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.outputCfg.format = kProcessFormat; pContext->mConfig.outputCfg.samplingRate = 44100; pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; @@ -284,18 +297,41 @@ int LE_process( //ALOGV("LE about to process %d samples", inBuffer->frameCount); uint16_t inIdx; +#ifdef BUILD_FLOAT + constexpr float scale = 1 << 15; // power of 2 is lossless conversion to int16_t range + constexpr float inverseScale = 1.f / scale; + const float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f) * scale; +#else float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f); +#endif float leftSample, rightSample; for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) { // makeup gain is applied on the input of the compressor +#ifdef BUILD_FLOAT + leftSample = inputAmp * inBuffer->f32[2*inIdx]; + rightSample = inputAmp * inBuffer->f32[2*inIdx +1]; + pContext->mCompressor->Compress(&leftSample, &rightSample); + inBuffer->f32[2*inIdx] = leftSample * inverseScale; + inBuffer->f32[2*inIdx +1] = rightSample * inverseScale; +#else leftSample = inputAmp * (float)inBuffer->s16[2*inIdx]; rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1]; pContext->mCompressor->Compress(&leftSample, &rightSample); inBuffer->s16[2*inIdx] = (int16_t) leftSample; inBuffer->s16[2*inIdx +1] = (int16_t) rightSample; +#endif // BUILD_FLOAT } if (inBuffer->raw != outBuffer->raw) { +#ifdef BUILD_FLOAT + if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + for (size_t i = 0; i < outBuffer->frameCount*2; i++) { + outBuffer->f32[i] += inBuffer->f32[i]; + } + } else { + memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(float)); + } +#else if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { for (size_t i = 0; i < outBuffer->frameCount*2; i++) { outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); @@ -303,6 +339,7 @@ int LE_process( } else { memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); } +#endif // BUILD_FLOAT } if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) { return -ENODATA; -- GitLab From fda70d3149451ad32f6e19774b0dafa180940b9e Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 8 Aug 2018 11:51:52 -0700 Subject: [PATCH 0193/1530] Visualizer: Convert Effect to float Test: Cts effects tests Bug: 112363078 Change-Id: Id4a57c2398273a369bd1d14a9eb42e96ee15367f --- media/libeffects/visualizer/Android.mk | 3 +- .../visualizer/EffectVisualizer.cpp | 114 +++++++++++++----- 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk index 70409defd0..3534149e9b 100644 --- a/media/libeffects/visualizer/Android.mk +++ b/media/libeffects/visualizer/Android.mk @@ -19,7 +19,8 @@ LOCAL_MODULE_RELATIVE_PATH := soundfx LOCAL_MODULE:= libvisualizer LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-effects) + $(call include-path-for, audio-effects) \ + $(call include-path-for, audio-utils) LOCAL_HEADER_LIBRARIES += libhardware_headers diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 807f24d27b..e2ccfb798e 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -24,11 +24,25 @@ #include #include +#include // max #include #include #include +#include + +#define BUILD_FLOAT + +#ifdef BUILD_FLOAT + +static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_FLOAT; + +#else + +static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_16_BIT; + +#endif // BUILD_FLOAT extern "C" { @@ -146,7 +160,7 @@ int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig) if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; - if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; + if (pConfig->inputCfg.format != kProcessFormat) return -EINVAL; pContext->mConfig = *pConfig; @@ -192,7 +206,7 @@ int Visualizer_init(VisualizerContext *pContext) { pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.inputCfg.format = kProcessFormat; pContext->mConfig.inputCfg.samplingRate = 44100; pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; @@ -200,7 +214,7 @@ int Visualizer_init(VisualizerContext *pContext) pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pContext->mConfig.outputCfg.format = kProcessFormat; pContext->mConfig.outputCfg.samplingRate = 44100; pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; @@ -301,15 +315,8 @@ int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid, //--- Effect Control Interface Implementation // -static inline int16_t clamp16(int32_t sample) -{ - if ((sample>>15) ^ (sample>>31)) - sample = 0x7FFF ^ (sample>>31); - return sample; -} - int Visualizer_process( - effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) + effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) { VisualizerContext * pContext = (VisualizerContext *)self; @@ -324,20 +331,28 @@ int Visualizer_process( return -EINVAL; } + const size_t sampleLen = inBuffer->frameCount * pContext->mChannelCount; + // perform measurements if needed if (pContext->mMeasurementMode & MEASUREMENT_MODE_PEAK_RMS) { // find the peak and RMS squared for the new buffer - uint32_t inIdx; - int16_t maxSample = 0; float rmsSqAcc = 0; - for (inIdx = 0 ; inIdx < inBuffer->frameCount * pContext->mChannelCount ; inIdx++) { - if (inBuffer->s16[inIdx] > maxSample) { - maxSample = inBuffer->s16[inIdx]; - } else if (-inBuffer->s16[inIdx] > maxSample) { - maxSample = -inBuffer->s16[inIdx]; - } - rmsSqAcc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]); + +#ifdef BUILD_FLOAT + float maxSample = 0.f; + for (size_t inIdx = 0; inIdx < sampleLen; ++inIdx) { + maxSample = fmax(maxSample, fabs(inBuffer->f32[inIdx])); + rmsSqAcc += inBuffer->f32[inIdx] * inBuffer->f32[inIdx]; } + maxSample *= 1 << 15; // scale to int16_t, with exactly 1 << 15 representing positive num. + rmsSqAcc *= 1 << 30; // scale to int16_t * 2 +#else + int maxSample = 0; + for (size_t inIdx = 0; inIdx < sampleLen; ++inIdx) { + maxSample = std::max(maxSample, std::abs(int32_t(inBuffer->s16[inIdx]))); + rmsSqAcc += inBuffer->s16[inIdx] * inBuffer->s16[inIdx]; + } +#endif // store the measurement pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample; pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared = @@ -348,32 +363,59 @@ int Visualizer_process( } } - // all code below assumes stereo 16 bit PCM output and input +#ifdef BUILD_FLOAT + float fscale; // multiplicative scale +#else int32_t shift; +#endif // BUILD_FLOAT if (pContext->mScalingMode == VISUALIZER_SCALING_MODE_NORMALIZED) { // derive capture scaling factor from peak value in current buffer // this gives more interesting captures for display. - shift = 32; - int len = inBuffer->frameCount * 2; - for (int i = 0; i < len; i++) { + +#ifdef BUILD_FLOAT + float maxSample = 0.f; + for (size_t inIdx = 0; inIdx < sampleLen; ++inIdx) { + maxSample = fmax(maxSample, fabs(inBuffer->f32[inIdx])); + } + if (maxSample > 0.f) { + constexpr float halfish = 127.f / 256.f; + fscale = halfish / maxSample; + int exp; // unused + const float significand = frexp(fscale, &exp); + if (significand == 0.5f) { + fscale *= 255.f / 256.f; // avoid returning unaltered PCM signal + } + } else { + // scale doesn't matter, the values are all 0. + fscale = 1.f; + } +#else + int32_t orAccum = 0; + for (size_t i = 0; i < sampleLen; ++i) { int32_t smp = inBuffer->s16[i]; if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range - int32_t clz = __builtin_clz(smp); - if (shift > clz) shift = clz; + orAccum |= smp; } + // A maximum amplitude signal will have 17 leading zeros, which we want to // translate to a shift of 8 (for converting 16 bit to 8 bit) - shift = 25 - shift; + shift = 25 - __builtin_clz(orAccum); + // Never scale by less than 8 to avoid returning unaltered PCM signal. if (shift < 3) { shift = 3; } // add one to combine the division by 2 needed after summing left and right channels below shift++; +#endif // BUILD_FLOAT } else { assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED); +#ifdef BUILD_FLOAT + fscale = 0.5f; // default divide by 2 to account for sum of L + R. +#else shift = 9; +#endif // BUILD_FLOAT } uint32_t captIdx; @@ -386,9 +428,13 @@ int Visualizer_process( // wrap around captIdx = 0; } - int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; - smp = smp >> shift; +#ifdef BUILD_FLOAT + const float smp = (inBuffer->f32[2 * inIdx] + inBuffer->f32[2 * inIdx + 1]) * fscale; + buf[captIdx] = clamp8_from_float(smp); +#else + const int32_t smp = (inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]) >> shift; buf[captIdx] = ((uint8_t)smp)^0x80; +#endif // BUILD_FLOAT } // XXX the following two should really be atomic, though it probably doesn't @@ -400,6 +446,15 @@ int Visualizer_process( } if (inBuffer->raw != outBuffer->raw) { +#ifdef BUILD_FLOAT + if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + for (size_t i = 0; i < sampleLen; ++i) { + outBuffer->f32[i] += inBuffer->f32[i]; + } + } else { + memcpy(outBuffer->raw, inBuffer->raw, sampleLen * sizeof(float)); + } +#else if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { for (size_t i = 0; i < outBuffer->frameCount*2; i++) { outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); @@ -407,6 +462,7 @@ int Visualizer_process( } else { memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); } +#endif // BUILD_FLOAT } if (pContext->mState != VISUALIZER_STATE_ACTIVE) { return -ENODATA; -- GitLab From 71920217d6017a5112ecd73abc4e68c16d680458 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Tue, 7 Aug 2018 18:01:50 -0700 Subject: [PATCH 0194/1530] NuPlayer2CCDecoder: Add bound check before memcpy Test: none Bug: 111874331 Change-Id: I6764802e8e8afd7e970ee433741f73a9b3d366dd --- .../nuplayer2/NuPlayer2CCDecoder.cpp | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp index e48e388234..e2159659f5 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp @@ -372,10 +372,16 @@ bool NuPlayer2::CCDecoder::parseMPEGCCData(int64_t timeUs, const uint8_t *data, timeUs, mDTVCCPacket->data(), mDTVCCPacket->size()); mDTVCCPacket->setRange(0, 0); } + if (mDTVCCPacket->size() + 2 > mDTVCCPacket->capacity()) { + return false; + } memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); br.skipBits(16); } else if (mDTVCCPacket->size() > 0 && cc_type == 2) { + if (mDTVCCPacket->size() + 2 > mDTVCCPacket->capacity()) { + return false; + } memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); br.skipBits(16); @@ -403,6 +409,9 @@ bool NuPlayer2::CCDecoder::parseMPEGCCData(int64_t timeUs, const uint8_t *data, line21CCBuf = new ABuffer((cc_count - i) * sizeof(CCData)); line21CCBuf->setRange(0, 0); } + if (line21CCBuf->size() + sizeof(cc) > line21CCBuf->capacity()) { + return false; + } memcpy(line21CCBuf->data() + line21CCBuf->size(), &cc, sizeof(cc)); line21CCBuf->setRange(0, line21CCBuf->size() + sizeof(CCData)); } @@ -464,6 +473,9 @@ bool NuPlayer2::CCDecoder::parseDTVCCPacket(int64_t timeUs, const uint8_t *data, size_t trackIndex = getTrackIndex(kTrackTypeCEA708, service_number, &trackAdded); if (mSelectedTrack == (ssize_t)trackIndex) { sp ccPacket = new ABuffer(block_size); + if (ccPacket->capacity() == 0) { + return false; + } memcpy(ccPacket->data(), br.data(), block_size); mCCMap.add(timeUs, ccPacket); } @@ -527,10 +539,12 @@ void NuPlayer2::CCDecoder::display(int64_t timeUs) { ccBuf = new ABuffer(size); ccBuf->setRange(0, 0); - for (ssize_t i = 0; i <= index; ++i) { - sp buf = mCCMap.valueAt(i); - memcpy(ccBuf->data() + ccBuf->size(), buf->data(), buf->size()); - ccBuf->setRange(0, ccBuf->size() + buf->size()); + if (ccBuf->capacity() > 0) { + for (ssize_t i = 0; i <= index; ++i) { + sp buf = mCCMap.valueAt(i); + memcpy(ccBuf->data() + ccBuf->size(), buf->data(), buf->size()); + ccBuf->setRange(0, ccBuf->size() + buf->size()); + } } } -- GitLab From 9a7689267854d2ffe24a1be2b491734757df4cee Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 7 Aug 2018 17:16:18 -0700 Subject: [PATCH 0195/1530] clear space returned by malloc() code sometimes assumed malloc() returned cleared memory; inserted appropriate memset() calls. Bug: 112052258 Bug: 112052432 Test: manual Change-Id: I739ac5ea22330f45401ed7df05fd4b948d470700 --- .../codecs/m4v_h263/dec/src/pvdec_api.cpp | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp index 75ca8467f2..9c0fcfaf86 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp @@ -186,8 +186,8 @@ OSCL_EXPORT_REF Bool PVInitVideoDecoder(VideoDecControls *decCtrl, uint8 *volbuf #ifdef DEC_INTERNAL_MEMORY_OPT video->vol[idx] = IMEM_vol[idx]; video->memoryUsage += sizeof(Vol); - oscl_memset(video->vol[idx], 0, sizeof(Vol)); if (video->vol[idx] == NULL) status = PV_FALSE; + else oscl_memset(video->vol[idx], 0, sizeof(Vol)); stream = IMEM_BitstreamDecVideo; #else video->vol[idx] = (Vol *) oscl_malloc(sizeof(Vol)); @@ -213,6 +213,7 @@ OSCL_EXPORT_REF Bool PVInitVideoDecoder(VideoDecControls *decCtrl, uint8 *volbuf else { int32 buffer_size; + oscl_memset(stream, 0, sizeof(BitstreamDecVideo)); if ((buffer_size = BitstreamOpen(stream, idx)) < 0) { mp4dec_log("InitVideoDecoder(): Can't allocate bitstream buffer.\n"); @@ -339,27 +340,33 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay #ifdef DEC_INTERNAL_MEMORY_OPT video->currVop->yChan = IMEM_currVop_yChan; /* Allocate memory for all VOP OKA 3/2/1*/ if (video->currVop->yChan == NULL) status = PV_FALSE; - video->currVop->uChan = video->currVop->yChan + size; - video->currVop->vChan = video->currVop->uChan + (size >> 2); + else { + video->currVop->uChan = video->currVop->yChan + size; + video->currVop->vChan = video->currVop->uChan + (size >> 2); + } video->prevVop->yChan = IMEM_prevVop_yChan; /* Allocate memory for all VOP OKA 3/2/1*/ if (video->prevVop->yChan == NULL) status = PV_FALSE; - video->prevVop->uChan = video->prevVop->yChan + size; - video->prevVop->vChan = video->prevVop->uChan + (size >> 2); + else { + video->prevVop->uChan = video->prevVop->yChan + size; + video->prevVop->vChan = video->prevVop->uChan + (size >> 2); + } #else if (size > INT32_MAX / 3) { return PV_FALSE; } video->currVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/ if (video->currVop->yChan == NULL) status = PV_FALSE; - - video->currVop->uChan = video->currVop->yChan + size; - video->currVop->vChan = video->currVop->uChan + (size >> 2); + else { + video->currVop->uChan = video->currVop->yChan + size; + video->currVop->vChan = video->currVop->uChan + (size >> 2); + } video->prevVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/ if (video->prevVop->yChan == NULL) status = PV_FALSE; - - video->prevVop->uChan = video->prevVop->yChan + size; - video->prevVop->vChan = video->prevVop->uChan + (size >> 2); + else { + video->prevVop->uChan = video->prevVop->yChan + size; + video->prevVop->vChan = video->prevVop->uChan + (size >> 2); + } #endif video->memoryUsage += (size * 3); #endif // MEMORY_POOL @@ -383,8 +390,10 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay video->prevEnhcVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/ if (video->prevEnhcVop->yChan == NULL) status = PV_FALSE; - video->prevEnhcVop->uChan = video->prevEnhcVop->yChan + size; - video->prevEnhcVop->vChan = video->prevEnhcVop->uChan + (size >> 2); + else { + video->prevEnhcVop->uChan = video->prevEnhcVop->yChan + size; + video->prevEnhcVop->vChan = video->prevEnhcVop->uChan + (size >> 2); + } video->memoryUsage += (3 * size / 2); #endif } @@ -431,10 +440,12 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay #else video->sliceNo = (uint8 *) oscl_malloc(nTotalMB); if (video->sliceNo == NULL) status = PV_FALSE; + else oscl_memset(video->sliceNo, 0, nTotalMB); video->memoryUsage += nTotalMB; video->acPredFlag = (uint8 *) oscl_malloc(nTotalMB * sizeof(uint8)); if (video->acPredFlag == NULL) status = PV_FALSE; + else oscl_memset(video->acPredFlag, 0, nTotalMB * sizeof(uint8)); video->memoryUsage += (nTotalMB); if ((size_t)nTotalMB > SIZE_MAX / sizeof(typeDCStore)) { @@ -442,6 +453,7 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay } video->predDC = (typeDCStore *) oscl_malloc(nTotalMB * sizeof(typeDCStore)); if (video->predDC == NULL) status = PV_FALSE; + else oscl_memset(video->predDC, 0, nTotalMB * sizeof(typeDCStore)); video->memoryUsage += (nTotalMB * sizeof(typeDCStore)); if (nMBPerRow > INT32_MAX - 1 @@ -450,6 +462,7 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay } video->predDCAC_col = (typeDCACStore *) oscl_malloc((nMBPerRow + 1) * sizeof(typeDCACStore)); if (video->predDCAC_col == NULL) status = PV_FALSE; + else oscl_memset(video->predDCAC_col, 0, (nMBPerRow + 1) * sizeof(typeDCACStore)); video->memoryUsage += ((nMBPerRow + 1) * sizeof(typeDCACStore)); /* element zero will be used for storing vertical (col) AC coefficients */ @@ -459,9 +472,11 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay /* Allocating HeaderInfo structure & Quantizer array */ video->headerInfo.Mode = (uint8 *) oscl_malloc(nTotalMB); if (video->headerInfo.Mode == NULL) status = PV_FALSE; + else oscl_memset(video->headerInfo.Mode, 0, nTotalMB); video->memoryUsage += nTotalMB; video->headerInfo.CBP = (uint8 *) oscl_malloc(nTotalMB); if (video->headerInfo.CBP == NULL) status = PV_FALSE; + else oscl_memset (video->headerInfo.CBP, 0, nTotalMB); video->memoryUsage += nTotalMB; if ((size_t)nTotalMB > SIZE_MAX / sizeof(int16)) { @@ -469,6 +484,7 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay } video->QPMB = (int16 *) oscl_malloc(nTotalMB * sizeof(int16)); if (video->QPMB == NULL) status = PV_FALSE; + else memset(video->QPMB, 0x0, nTotalMB * sizeof(int16)); video->memoryUsage += (nTotalMB * sizeof(int)); /* Allocating macroblock space */ @@ -489,8 +505,10 @@ Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLay } video->motX = (MOT *) oscl_malloc(sizeof(MOT) * 4 * nTotalMB); if (video->motX == NULL) status = PV_FALSE; + else memset(video->motX, 0, sizeof(MOT) * 4 * nTotalMB); video->motY = (MOT *) oscl_malloc(sizeof(MOT) * 4 * nTotalMB); if (video->motY == NULL) status = PV_FALSE; + else memset(video->motY, 0, sizeof(MOT) * 4 * nTotalMB); video->memoryUsage += (sizeof(MOT) * 8 * nTotalMB); #endif -- GitLab From a0a5d2a9061cfb58b6f7632117502f2061ab74cb Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 6 Aug 2018 15:03:18 -0700 Subject: [PATCH 0196/1530] Add external setParameter to audioflinger dumpsys Apps are not expected to send any setParameter, system should only send very few keys. This log can help verify what is bypassing audioflinger control. Only the first 1024 characters are logged per SimpleLog limitation to avoid high memory retention in the privilege audio service in case very big strings are sent. Thus in the worst case around 60*1*3=180kB can be consumed in Audioflinger by those logs (aka: no app memory deny of service). Test: adb shell dumpsys media.audio_flinger Change-Id: Ida9c21be9d20528f67a63eddf6ca7a6c962c7cc2 Signed-off-by: Kevin Rocard --- services/audioflinger/AudioFlinger.cpp | 31 +++++++++++++++++++++++--- services/audioflinger/AudioFlinger.h | 9 +++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 43566b770a..ed2b3c0e00 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -66,7 +66,6 @@ #include #include #include -#include #include #include #include @@ -517,6 +516,15 @@ status_t AudioFlinger::dump(int fd, const Vector& args) mPatchPanel.dump(fd); + // dump external setParameters + auto dumpLogger = [fd](SimpleLog& logger, const char* name) { + dprintf(fd, "\n%s setParameters:\n", name); + logger.dump(fd, " " /* prefix */); + }; + dumpLogger(mRejectedSetParameterLog, "Rejected"); + dumpLogger(mAppSetParameterLog, "App"); + dumpLogger(mSystemSetParameterLog, "System"); + BUFLOG_RESET; if (locked) { @@ -1229,16 +1237,33 @@ void AudioFlinger::filterReservedParameters(String8& keyValuePairs, uid_t callin AudioParameter param = AudioParameter(keyValuePairs); String8 value; + AudioParameter rejectedParam; for (auto& key : kReservedParameters) { if (param.get(key, value) == NO_ERROR) { - ALOGW("%s: filtering key %s value %s from uid %d", - __func__, key.string(), value.string(), callingUid); + rejectedParam.add(key, value); param.remove(key); } } + logFilteredParameters(param.size() + rejectedParam.size(), keyValuePairs, + rejectedParam.size(), rejectedParam.toString(), callingUid); keyValuePairs = param.toString(); } +void AudioFlinger::logFilteredParameters(size_t originalKVPSize, const String8& originalKVPs, + size_t rejectedKVPSize, const String8& rejectedKVPs, + uid_t callingUid) { + auto prefix = String8::format("UID %5d", callingUid); + auto suffix = String8::format("%zu KVP received: %s", originalKVPSize, originalKVPs.c_str()); + if (rejectedKVPSize != 0) { + auto error = String8::format("%zu KVP rejected: %s", rejectedKVPSize, rejectedKVPs.c_str()); + ALOGW("%s: %s, %s, %s", __func__, prefix.c_str(), error.c_str(), suffix.c_str()); + mRejectedSetParameterLog.log("%s, %s, %s", prefix.c_str(), error.c_str(), suffix.c_str()); + } else { + auto& logger = (isServiceUid(callingUid) ? mSystemSetParameterLog : mAppSetParameterLog); + logger.log("%s, %s", prefix.c_str(), suffix.c_str()); + } +} + status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) { ALOGV("setParameters(): io %d, keyvalue %s, calling pid %d calling uid %d", diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index e9e6e944c7..cc277c351a 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -811,6 +811,9 @@ private: status_t checkStreamType(audio_stream_type_t stream) const; void filterReservedParameters(String8& keyValuePairs, uid_t callingUid); + void logFilteredParameters(size_t originalKVPSize, const String8& originalKVPs, + size_t rejectedKVPSize, const String8& rejectedKVPs, + uid_t callingUid); public: // These methods read variables atomically without mLock, @@ -831,7 +834,11 @@ private: PatchPanel mPatchPanel; sp mEffectsFactoryHal; - bool mSystemReady; + bool mSystemReady; + + SimpleLog mRejectedSetParameterLog; + SimpleLog mAppSetParameterLog; + SimpleLog mSystemSetParameterLog; }; #undef INCLUDING_FROM_AUDIOFLINGER_H -- GitLab From 7f39f56e33bc0feb5188cc0372e0be69239ba78f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 8 Aug 2018 17:30:20 -0700 Subject: [PATCH 0197/1530] AudioFlinger: Do not show latency for non-PCM record in dumpsys Test: Check AudioFlinger dumpsys during MSD direct record Bug: 112388863 Change-Id: Id404c70af0c0e23a1e349522568b20f50573b1e9 (cherry picked from commit 2ceaf3dee96d1696b315afdee9ffc0df662f88d8) --- services/audioflinger/Threads.cpp | 3 ++- services/audioflinger/Tracks.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index cd585f5574..babf91d400 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7466,7 +7466,8 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a (void)input->stream->dump(fd); } - const double latencyMs = - mTimestamp.getOutputServerLatencyMs(mSampleRate); + const double latencyMs = audio_is_linear_pcm(mFormat) + ? - mTimestamp.getOutputServerLatencyMs(mSampleRate) : 0.; if (latencyMs != 0.) { dprintf(fd, " NormalRecord latency ms: %.2lf\n", latencyMs); } else { diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 8b9485f677..8ad06f0cbc 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1720,7 +1720,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( thread->mFastTrackAvail = false; } else { // TODO: only Normal Record has timestamps (Fast Record does not). - mServerLatencySupported = true; + mServerLatencySupported = audio_is_linear_pcm(mFormat); } #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) -- GitLab From 6427e44be65e3b2fed6da30b4e442d25fe7769ec Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 9 Aug 2018 12:51:02 -0700 Subject: [PATCH 0198/1530] AudioFlinger: Add record frames read to dumpsys Allows comparison to playback frames written for patches. Test: audioflinger dumpsys Change-Id: I2ef0ccbc36819d48b05756115de70f5a24a2c2cd --- services/audioflinger/Threads.cpp | 2 ++ services/audioflinger/Threads.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index cd585f5574..f4f48bb3e6 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6833,6 +6833,7 @@ reacquire_wakelock: goto unlock; } ALOG_ASSERT(framesRead > 0); + mFramesRead += framesRead; #ifdef TEE_SINK (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead); @@ -7457,6 +7458,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a audio_input_flags_t flags = input != NULL ? input->flags : AUDIO_INPUT_FLAG_NONE; dprintf(fd, " AudioStreamIn: %p flags %#x (%s)\n", input, flags, inputFlagsToString(flags).c_str()); + dprintf(fd, " Frames read: %lld\n", (long long)mFramesRead); if (mActiveTracks.isEmpty()) { dprintf(fd, " No active record clients\n"); } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 2ab0beeaa6..705efbca7e 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1601,6 +1601,8 @@ private: bool mFastTrackAvail; // true if fast track available // common state to all record threads std::atomic_bool mBtNrecSuspended; + + int64_t mFramesRead = 0; // continuous running counter. }; class MmapThread : public ThreadBase -- GitLab From 5654fa737782fb27f3f79f77e4a4b41b1280e1fa Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 10 Aug 2018 15:05:24 -0700 Subject: [PATCH 0199/1530] audio policy: fix bad auto merge conflict resolution When commit a81f098f1 was merged from aosp to pi-dev-plus-aosp there was a problem where a code block already present in pi-dev for the same fix (commit b62d78b72d9) was duplicated. This caused double muting of notifications and alarms when a camera sound is played in countries where this sound is enforced while unmuting only once when camera sound stops. Bug: 110913234 Test: force camera sound and verify mute/unmute counts after using the camera. Change-Id: Ie97e3642803310acbe5eb85a53a850e010a9f42f --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 96079cc366..b0e6b0fea1 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1514,11 +1514,6 @@ status_t AudioPolicyManager::startSource(const sp& output setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc); } - if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE && - mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc); - } - return NO_ERROR; } -- GitLab From 86be8728fb6efae5a35ed4840c45a2eafce06823 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Fri, 10 Aug 2018 10:25:47 -0700 Subject: [PATCH 0200/1530] NBLog: Add checks for possible nullptr dereference Made minor style and formatting fixes. Test: build, dumpsys media.log Bug: 111882126 Change-Id: I73fca6ebdd994d9b68cf1e1ccb8ea08b3fe14d4d --- media/libnblog/NBLog.cpp | 343 +++++++++++---------- media/libnblog/include/media/nblog/NBLog.h | 25 +- 2 files changed, 192 insertions(+), 176 deletions(-) diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index d6fa3e3d02..bfc797c7b9 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -64,7 +64,9 @@ int NBLog::Entry::copyEntryDataAt(size_t offset) const // --------------------------------------------------------------------------- /*static*/ -std::unique_ptr NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) { +std::unique_ptr NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) +{ + if (ptr == nullptr) return nullptr; const uint8_t type = EntryIterator(ptr)->type; switch (type) { case EVENT_START_FMT: @@ -78,31 +80,33 @@ std::unique_ptr NBLog::AbstractEntry::buildEntry(const uin } } -NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) { +NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) +{ } // --------------------------------------------------------------------------- -NBLog::EntryIterator NBLog::FormatEntry::begin() const { +NBLog::EntryIterator NBLog::FormatEntry::begin() const +{ return EntryIterator(mEntry); } -const char *NBLog::FormatEntry::formatString() const { +const char *NBLog::FormatEntry::formatString() const +{ return (const char*) mEntry + offsetof(entry, data); } -size_t NBLog::FormatEntry::formatStringLength() const { +size_t NBLog::FormatEntry::formatStringLength() const +{ return mEntry[offsetof(entry, length)]; } -NBLog::EntryIterator NBLog::FormatEntry::args() const { +NBLog::EntryIterator NBLog::FormatEntry::args() const +{ auto it = begin(); - // skip start fmt - ++it; - // skip timestamp - ++it; - // skip hash - ++it; + ++it; // skip start fmt + ++it; // skip timestamp + ++it; // skip hash // Skip author if present if (it->type == EVENT_AUTHOR) { ++it; @@ -110,33 +114,30 @@ NBLog::EntryIterator NBLog::FormatEntry::args() const { return it; } -int64_t NBLog::FormatEntry::timestamp() const { +int64_t NBLog::FormatEntry::timestamp() const +{ auto it = begin(); - // skip start fmt - ++it; + ++it; // skip start fmt return it.payload(); } -NBLog::log_hash_t NBLog::FormatEntry::hash() const { +NBLog::log_hash_t NBLog::FormatEntry::hash() const +{ auto it = begin(); - // skip start fmt - ++it; - // skip timestamp - ++it; + ++it; // skip start fmt + ++it; // skip timestamp // unaligned 64-bit read not supported log_hash_t hash; memcpy(&hash, it->data, sizeof(hash)); return hash; } -int NBLog::FormatEntry::author() const { +int NBLog::FormatEntry::author() const +{ auto it = begin(); - // skip start fmt - ++it; - // skip timestamp - ++it; - // skip hash - ++it; + ++it; // skip start fmt + ++it; // skip timestamp + ++it; // skip hash // if there is an author entry, return it, return -1 otherwise if (it->type == EVENT_AUTHOR) { return it.payload(); @@ -145,19 +146,18 @@ int NBLog::FormatEntry::author() const { } NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( - std::unique_ptr &dst, int author) const { + std::unique_ptr &dst, int author) const +{ auto it = begin(); - // copy fmt start entry - it.copyTo(dst); - // copy timestamp - (++it).copyTo(dst); // copy hash - (++it).copyTo(dst); + it.copyTo(dst); // copy fmt start entry + (++it).copyTo(dst); // copy timestamp + (++it).copyTo(dst); // copy hash // insert author entry - size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author); + size_t authorEntrySize = Entry::kOverhead + sizeof(author); uint8_t authorEntry[authorEntrySize]; authorEntry[offsetof(entry, type)] = EVENT_AUTHOR; authorEntry[offsetof(entry, length)] = - authorEntry[authorEntrySize + NBLog::Entry::kPreviousLengthOffset] = + authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] = sizeof(author); *(int*) (&authorEntry[offsetof(entry, data)]) = author; dst->write(authorEntry, authorEntrySize); @@ -170,76 +170,96 @@ NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( return it; } -void NBLog::EntryIterator::copyTo(std::unique_ptr &dst) const { - size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead; - dst->write(ptr, length); +void NBLog::EntryIterator::copyTo(std::unique_ptr &dst) const +{ + size_t length = mPtr[offsetof(entry, length)] + Entry::kOverhead; + dst->write(mPtr, length); } -void NBLog::EntryIterator::copyData(uint8_t *dst) const { - memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]); +void NBLog::EntryIterator::copyData(uint8_t *dst) const +{ + memcpy((void*) dst, mPtr + offsetof(entry, data), mPtr[offsetof(entry, length)]); } -NBLog::EntryIterator::EntryIterator() - : ptr(nullptr) {} +NBLog::EntryIterator::EntryIterator() // Dummy initialization. + : mPtr(nullptr) +{ +} NBLog::EntryIterator::EntryIterator(const uint8_t *entry) - : ptr(entry) {} + : mPtr(entry) +{ +} NBLog::EntryIterator::EntryIterator(const NBLog::EntryIterator &other) - : ptr(other.ptr) {} + : mPtr(other.mPtr) +{ +} -const NBLog::entry& NBLog::EntryIterator::operator*() const { - return *(entry*) ptr; +const NBLog::entry& NBLog::EntryIterator::operator*() const +{ + return *(entry*) mPtr; } -const NBLog::entry* NBLog::EntryIterator::operator->() const { - return (entry*) ptr; +const NBLog::entry* NBLog::EntryIterator::operator->() const +{ + return (entry*) mPtr; } -NBLog::EntryIterator& NBLog::EntryIterator::operator++() { - ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead; +NBLog::EntryIterator& NBLog::EntryIterator::operator++() +{ + mPtr += mPtr[offsetof(entry, length)] + Entry::kOverhead; return *this; } -NBLog::EntryIterator& NBLog::EntryIterator::operator--() { - ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead; +NBLog::EntryIterator& NBLog::EntryIterator::operator--() +{ + mPtr -= mPtr[Entry::kPreviousLengthOffset] + Entry::kOverhead; return *this; } -NBLog::EntryIterator NBLog::EntryIterator::next() const { +NBLog::EntryIterator NBLog::EntryIterator::next() const +{ EntryIterator aux(*this); return ++aux; } -NBLog::EntryIterator NBLog::EntryIterator::prev() const { +NBLog::EntryIterator NBLog::EntryIterator::prev() const +{ EntryIterator aux(*this); return --aux; } -int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const { - return ptr - other.ptr; +int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const +{ + return mPtr - other.mPtr; } -bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const { - return ptr != other.ptr; +bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const +{ + return mPtr != other.mPtr; } -bool NBLog::EntryIterator::hasConsistentLength() const { - return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] + - NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset]; +bool NBLog::EntryIterator::hasConsistentLength() const +{ + return mPtr[offsetof(entry, length)] == mPtr[mPtr[offsetof(entry, length)] + + Entry::kOverhead + Entry::kPreviousLengthOffset]; } // --------------------------------------------------------------------------- -int64_t NBLog::HistogramEntry::timestamp() const { +int64_t NBLog::HistogramEntry::timestamp() const +{ return EntryIterator(mEntry).payload().ts; } -NBLog::log_hash_t NBLog::HistogramEntry::hash() const { +NBLog::log_hash_t NBLog::HistogramEntry::hash() const +{ return EntryIterator(mEntry).payload().hash; } -int NBLog::HistogramEntry::author() const { +int NBLog::HistogramEntry::author() const +{ EntryIterator it(mEntry); if (it->length == sizeof(HistTsEntryWithAuthor)) { return it.payload().author; @@ -249,7 +269,8 @@ int NBLog::HistogramEntry::author() const { } NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor( - std::unique_ptr &dst, int author) const { + std::unique_ptr &dst, int author) const +{ // Current histogram entry has {type, length, struct HistTsEntry, length}. // We now want {type, length, struct HistTsEntryWithAuthor, length} uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)]; @@ -336,9 +357,7 @@ NBLog::Writer::~Writer() void NBLog::Writer::log(const char *string) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string"); size_t length = strlen(string); if (length > Entry::kMaxLength) { @@ -349,9 +368,7 @@ void NBLog::Writer::log(const char *string) void NBLog::Writer::logf(const char *fmt, ...) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; va_list ap; va_start(ap, fmt); Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter @@ -360,9 +377,7 @@ void NBLog::Writer::logf(const char *fmt, ...) void NBLog::Writer::logvf(const char *fmt, va_list ap) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; char buffer[Entry::kMaxLength + 1 /*NUL*/]; int length = vsnprintf(buffer, sizeof(buffer), fmt, ap); if (length >= (int) sizeof(buffer)) { @@ -377,9 +392,7 @@ void NBLog::Writer::logvf(const char *fmt, va_list ap) void NBLog::Writer::logTimestamp() { - if (!mEnabled) { - return; - } + if (!mEnabled) return; int64_t ts = get_monotonic_ns(); if (ts > 0) { log(EVENT_TIMESTAMP, &ts, sizeof(ts)); @@ -390,41 +403,31 @@ void NBLog::Writer::logTimestamp() void NBLog::Writer::logTimestamp(const int64_t ts) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; log(EVENT_TIMESTAMP, &ts, sizeof(ts)); } void NBLog::Writer::logInteger(const int x) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; log(EVENT_INTEGER, &x, sizeof(x)); } void NBLog::Writer::logFloat(const float x) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; log(EVENT_FLOAT, &x, sizeof(x)); } void NBLog::Writer::logPID() { - if (!mEnabled) { - return; - } + if (!mEnabled) return; log(EVENT_PID, mPidTag, mPidTagSize); } void NBLog::Writer::logStart(const char *fmt) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; size_t length = strlen(fmt); if (length > Entry::kMaxLength) { length = Entry::kMaxLength; @@ -434,26 +437,20 @@ void NBLog::Writer::logStart(const char *fmt) void NBLog::Writer::logEnd() { - if (!mEnabled) { - return; - } + if (!mEnabled) return; Entry entry = Entry(EVENT_END_FMT, NULL, 0); - log(&entry, true); + log(entry, true); } void NBLog::Writer::logHash(log_hash_t hash) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; log(EVENT_HASH, &hash, sizeof(hash)); } void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; HistTsEntry data; data.hash = hash; data.ts = get_monotonic_ns(); @@ -466,10 +463,7 @@ void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) { - if (!mEnabled) { - return; - } - + if (!mEnabled) return; va_list ap; va_start(ap, hash); Writer::logVFormat(fmt, hash, ap); @@ -478,9 +472,7 @@ void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; Writer::logStart(fmt); int i; double f; @@ -536,9 +528,7 @@ void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) void NBLog::Writer::log(Event event, const void *data, size_t length) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; if (data == NULL || length > Entry::kMaxLength) { // TODO Perhaps it makes sense to display truncated data or at least a // message that the data is too long? The current behavior can create @@ -550,19 +540,17 @@ void NBLog::Writer::log(Event event, const void *data, size_t length) return; } Entry etr(event, data, length); - log(&etr, true /*trusted*/); + log(etr, true /*trusted*/); } -void NBLog::Writer::log(const NBLog::Entry *etr, bool trusted) +void NBLog::Writer::log(const NBLog::Entry &etr, bool trusted) { - if (!mEnabled) { - return; - } + if (!mEnabled) return; if (!trusted) { - log(etr->mEvent, etr->mData, etr->mLength); + log(etr.mEvent, etr.mData, etr.mLength); return; } - size_t need = etr->mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength + size_t need = etr.mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength // need = number of bytes written to FIFO // FIXME optimize this using memcpy for the data part of the Entry. @@ -571,7 +559,7 @@ void NBLog::Writer::log(const NBLog::Entry *etr, bool trusted) uint8_t temp[Entry::kMaxLength + Entry::kOverhead]; // write this data to temp array for (size_t i = 0; i < need; i++) { - temp[i] = etr->copyEntryDataAt(i); + temp[i] = etr.copyEntryDataAt(i); } // write to circular buffer mFifoWriter->write(temp, need); @@ -743,15 +731,14 @@ std::unique_ptr NBLog::Reader::getSnapshot() if (mFifoReader == NULL) { return std::unique_ptr(new Snapshot()); } - // make a copy to avoid race condition with writer - size_t capacity = mFifo->capacity(); // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the // reader index. The index is incremented after handling corruption, to after the last complete // entry of the buffer size_t lost; audio_utils_iovec iovec[2]; - ssize_t availToRead = mFifoReader->obtain(iovec, capacity, NULL /*timeout*/, &lost); + const ssize_t availToRead = mFifoReader->obtain(iovec, mFifo->capacity(), + NULL /*timeout*/, &lost); if (availToRead <= 0) { return std::unique_ptr(new Snapshot()); } @@ -800,7 +787,6 @@ std::unique_ptr NBLog::Reader::getSnapshot() snapshot->mLost = lost; return snapshot; - } // Takes raw content of the local merger FIFO, processes log entries, and @@ -854,7 +840,7 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot } // FIXME: decide whether to print the warnings here or elsewhere if (!body.isEmpty()) { - dumpLine(timestamp, body); + dumpLine(×tamp, &body); } } @@ -865,20 +851,22 @@ void NBLog::MergeReader::getAndProcessSnapshot() getAndProcessSnapshot(*snap); } -void NBLog::MergeReader::dump(int fd, int indent) { +void NBLog::MergeReader::dump(int fd, int indent) +{ // TODO: add a mutex around media.log dump ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); } // Writes a string to the console -void NBLog::Reader::dumpLine(const String8 ×tamp, String8 &body) +void NBLog::Reader::dumpLine(const String8 *timestamp, String8 *body) { + if (timestamp == nullptr || body == nullptr) return; if (mFd >= 0) { - dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string()); + dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp->string(), body->string()); } else { - ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string()); + ALOGI("%.*s%s %s", mIndent, "", timestamp->string(), body->string()); } - body.clear(); + body->clear(); } bool NBLog::Reader::isIMemory(const sp& iMemory) const @@ -888,25 +876,33 @@ bool NBLog::Reader::isIMemory(const sp& iMemory) const // --------------------------------------------------------------------------- -void NBLog::appendTimestamp(String8 *body, const void *data) { +void NBLog::appendTimestamp(String8 *body, const void *data) +{ + if (body == nullptr || data == nullptr) return; int64_t ts; memcpy(&ts, data, sizeof(ts)); body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)), (int) ((ts / (1000 * 1000)) % 1000)); } -void NBLog::appendInt(String8 *body, const void *data) { +void NBLog::appendInt(String8 *body, const void *data) +{ + if (body == nullptr || data == nullptr) return; int x = *((int*) data); body->appendFormat("<%d>", x); } -void NBLog::appendFloat(String8 *body, const void *data) { +void NBLog::appendFloat(String8 *body, const void *data) +{ + if (body == nullptr || data == nullptr) return; float f; - memcpy(&f, data, sizeof(float)); + memcpy(&f, data, sizeof(f)); body->appendFormat("<%f>", f); } -void NBLog::appendPID(String8 *body, const void* data, size_t length) { +void NBLog::appendPID(String8 *body, const void* data, size_t length) +{ + if (body == nullptr || data == nullptr) return; pid_t id = *((pid_t*) data); char * name = &((char*) data)[sizeof(pid_t)]; body->appendFormat("", id, (int) (length - sizeof(pid_t)), name); @@ -915,9 +911,9 @@ void NBLog::appendPID(String8 *body, const void* data, size_t length) { String8 NBLog::bufferDump(const uint8_t *buffer, size_t size) { String8 str; + if (buffer == nullptr) return str; str.append("[ "); - for(size_t i = 0; i < size; i++) - { + for(size_t i = 0; i < size; i++) { str.appendFormat("%d ", buffer[i]); } str.append("]"); @@ -931,7 +927,8 @@ String8 NBLog::bufferDump(const EntryIterator &it) NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, - String8 *body) { + String8 *body) +{ // log timestamp int64_t ts = fmtEntry.timestamp(); timestamp->clear(); @@ -947,7 +944,7 @@ NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, handleAuthor(fmtEntry, body); // log string - NBLog::EntryIterator arg = fmtEntry.args(); + EntryIterator arg = fmtEntry.args(); const char* fmt = fmtEntry.formatString(); size_t fmt_length = fmtEntry.formatStringLength(); @@ -1026,10 +1023,11 @@ NBLog::Merger::Merger(const void *shared, size_t size): new audio_utils_fifo(size, sizeof(uint8_t), mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL), mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL) - {} - -void NBLog::Merger::addReader(const NBLog::NamedReader &reader) { +{ +} +void NBLog::Merger::addReader(const NBLog::NamedReader &reader) +{ // FIXME This is called by binder thread in MediaLogService::registerWriter // but the access to shared variable mNamedReaders is not yet protected by a lock. mNamedReaders.push_back(reader); @@ -1044,25 +1042,23 @@ struct MergeItem MergeItem(int64_t ts, int index): ts(ts), index(index) {} }; -// operators needed for priority queue in merge -// bool operator>(const int64_t &t1, const int64_t &t2) { -// return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec); -// } - -bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) { +bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) +{ return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index); } // Merge registered readers, sorted by timestamp, and write data to a single FIFO in local memory -void NBLog::Merger::merge() { +void NBLog::Merger::merge() +{ // FIXME This is called by merge thread // but the access to shared variable mNamedReaders is not yet protected by a lock. - int nLogs = mNamedReaders.size(); + const int nLogs = mNamedReaders.size(); std::vector> snapshots(nLogs); - std::vector offsets(nLogs); + std::vector offsets; + offsets.reserve(nLogs); for (int i = 0; i < nLogs; ++i) { snapshots[i] = mNamedReaders[i].reader()->getSnapshot(); - offsets[i] = snapshots[i]->begin(); + offsets.push_back(snapshots[i]->begin()); } // initialize offsets // TODO custom heap implementation could allow to update top, improving performance @@ -1071,17 +1067,19 @@ void NBLog::Merger::merge() { for (int i = 0; i < nLogs; ++i) { if (offsets[i] != snapshots[i]->end()) { - int64_t ts = AbstractEntry::buildEntry(offsets[i])->timestamp(); - timestamps.emplace(ts, i); + std::unique_ptr abstractEntry = AbstractEntry::buildEntry(offsets[i]); + if (abstractEntry == nullptr) { + continue; + } + timestamps.emplace(abstractEntry->timestamp(), i); } } while (!timestamps.empty()) { - // find minimum timestamp - int index = timestamps.top().index; + int index = timestamps.top().index; // find minimum timestamp // copy it to the log, increasing offset - offsets[index] = AbstractEntry::buildEntry(offsets[index])->copyWithAuthor(mFifoWriter, - index); + offsets[index] = AbstractEntry::buildEntry(offsets[index])-> + copyWithAuthor(mFifoWriter, index); // update data structures timestamps.pop(); if (offsets[index] != snapshots[index]->end()) { @@ -1091,7 +1089,8 @@ void NBLog::Merger::merge() { } } -const std::vector& NBLog::Merger::getNamedReaders() const { +const std::vector& NBLog::Merger::getNamedReaders() const +{ // FIXME This is returning a reference to a shared variable that needs a lock return mNamedReaders; } @@ -1099,10 +1098,16 @@ const std::vector& NBLog::Merger::getNamedReaders() const { // --------------------------------------------------------------------------- NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger) - : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {} + : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) +{ +} -void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) { +void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) +{ int author = entry.author(); + if (author == -1) { + return; + } // FIXME Needs a lock const char* name = mNamedReaders[author].name(); body->appendFormat("%s: ", name); @@ -1113,16 +1118,20 @@ void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 NBLog::MergeThread::MergeThread(NBLog::Merger &merger, NBLog::MergeReader &mergeReader) : mMerger(merger), mMergeReader(mergeReader), - mTimeoutUs(0) {} + mTimeoutUs(0) +{ +} -NBLog::MergeThread::~MergeThread() { +NBLog::MergeThread::~MergeThread() +{ // set exit flag, set timeout to 0 to force threadLoop to exit and wait for the thread to join requestExit(); setTimeoutUs(0); join(); } -bool NBLog::MergeThread::threadLoop() { +bool NBLog::MergeThread::threadLoop() +{ bool doMerge; { AutoMutex _l(mMutex); @@ -1144,11 +1153,13 @@ bool NBLog::MergeThread::threadLoop() { return true; } -void NBLog::MergeThread::wakeup() { +void NBLog::MergeThread::wakeup() +{ setTimeoutUs(kThreadWakeupPeriodUs); } -void NBLog::MergeThread::setTimeoutUs(int time) { +void NBLog::MergeThread::setTimeoutUs(int time) +{ AutoMutex _l(mMutex); mTimeoutUs = time; mCond.signal(); diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index fb6f179150..bee3ad3898 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -98,7 +98,12 @@ private: // entry iterator class EntryIterator { public: + // Used for dummy initialization. Performing operations on a default-constructed + // EntryIterator other than assigning it to another valid EntryIterator + // is undefined behavior. EntryIterator(); + // Caller's responsibility to make sure entry is not nullptr. + // Passing in nullptr can result in undefined behavior. explicit EntryIterator(const uint8_t *entry); EntryIterator(const EntryIterator &other); @@ -109,7 +114,9 @@ private: EntryIterator& operator++(); // ++i // back to previous entry EntryIterator& operator--(); // --i + // returns an EntryIterator corresponding to the next entry EntryIterator next() const; + // returns an EntryIterator corresponding to the previous entry EntryIterator prev() const; bool operator!=(const EntryIterator &other) const; int operator-(const EntryIterator &other) const; @@ -120,25 +127,22 @@ private: template inline const T& payload() { - return *reinterpret_cast(ptr + offsetof(entry, data)); + return *reinterpret_cast(mPtr + offsetof(entry, data)); } inline operator const uint8_t*() const { - return ptr; + return mPtr; } private: - const uint8_t *ptr; + const uint8_t *mPtr; // Should not be nullptr except for dummy initialization }; class AbstractEntry { public: - - // Entry starting in the given pointer - explicit AbstractEntry(const uint8_t *entry); virtual ~AbstractEntry() {} - // build concrete entry of appropriate class from pointer + // build concrete entry of appropriate class from ptr. static std::unique_ptr buildEntry(const uint8_t *ptr); // get format entry timestamp @@ -158,6 +162,8 @@ private: int author) const = 0; protected: + // Entry starting in the given pointer, which shall not be nullptr. + explicit AbstractEntry(const uint8_t *entry); // copies ordinary entry from src to dst, and returns length of entry // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it); const uint8_t *mEntry; @@ -360,7 +366,7 @@ public: // writes a single Entry to the FIFO void log(Event event, const void *data, size_t length); // checks validity of an event before calling log above this one - void log(const Entry *entry, bool trusted = false); + void log(const Entry &entry, bool trusted = false); Shared* const mShared; // raw pointer to shared memory sp mIMemory; // ref-counted version, initialized in constructor @@ -432,7 +438,6 @@ public: EntryIterator end() { return mEnd; } private: - friend class MergeReader; friend class Reader; uint8_t *mData; size_t mLost; @@ -454,7 +459,7 @@ public: protected: // print a summary of the performance to the console - void dumpLine(const String8& timestamp, String8& body); + void dumpLine(const String8 *timestamp, String8 *body); EntryIterator handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, String8 *body); -- GitLab From 9af566a4c30a79984bdbb51fb3bc11208ec246b5 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Fri, 10 Aug 2018 16:36:35 -0700 Subject: [PATCH 0201/1530] NuPlayer2Driver: send MEDIA2_INFO_DATA_SOURCE_REPEAT when looping Test: cts test Bug: 109928575 Change-Id: Iaacaa2fa9f957244d305d821d76e5139164e2b35 --- .../include/mediaplayer2/MediaPlayer2Types.h | 3 +++ media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h index bc84729145..211a5c0312 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h @@ -107,6 +107,9 @@ enum media2_info_type { // The player just completed the playback of all data sources. // But this is not visible in native code. Just keep this entry for completeness. MEDIA2_INFO_DATA_SOURCE_LIST_END = 6, + // The player just completed an iteration of playback loop. This event is sent only when + // looping is enabled. + MEDIA2_INFO_DATA_SOURCE_REPEAT = 7, //1xx // The player just prepared a data source. diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 03d17a5262..3069f54116 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -930,7 +930,12 @@ void NuPlayer2Driver::notifyListener_l( // the last little bit of audio. In looping mode, we need to restart it. mAudioSink->start(); } - // don't send completion event when looping + + sp notify = new AMessage(kWhatNotifyListener, this); + notify->setInt64("srcId", srcId); + notify->setInt32("messageId", MEDIA2_INFO); + notify->setInt32("ext1", MEDIA2_INFO_DATA_SOURCE_REPEAT); + notify->post(); return; } if (property_get_bool("persist.debug.sf.stats", false)) { -- GitLab From 355b15bb1695118ef85c02e3ddc28335ad9c696d Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 13 Aug 2018 11:30:12 -0700 Subject: [PATCH 0202/1530] clear allocated memory for mp3 decoding buffers mp3 decoder assumed malloc() was returning zero'd memory; replace malloc() with calloc() so that this assumption is true. Bug: 112005441 Test: manual --- media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index 2364684b1b..cd984f0db2 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -114,7 +114,7 @@ void SoftMP3::initDecoder() { mConfig->crcEnabled = false; uint32_t memRequirements = pvmp3_decoderMemRequirements(); - mDecoderBuf = malloc(memRequirements); + mDecoderBuf = calloc(1, memRequirements); pvmp3_InitDecoder(mConfig, mDecoderBuf); mIsFirst = true; -- GitLab From 30282562329b3031b6bfa0869bd76ff5f543ebba Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 8 Aug 2018 18:27:03 -0700 Subject: [PATCH 0203/1530] AudioFlinger: Add patch latency for direct record/playback This relies on being able to obtain presentation position and capture position accurately from the HAL, in the presence of potential underruns or overruns. Test: MSD hal dumpsys Bug: 112428710 Change-Id: I9aad574baaff60b5e0c5d8c39a2147d19ee613f5 --- services/audioflinger/PatchPanel.cpp | 44 ++++++++++++++++++++++++---- services/audioflinger/TrackBase.h | 16 ++++++++-- services/audioflinger/Tracks.cpp | 20 +++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index ada8572541..7b165a15e5 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -547,14 +547,46 @@ status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const // reverse due to internal biases). // // TODO: is this stable enough? Consider a PatchTrack synchronized version of this. - double recordServerLatencyMs; - if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) != OK) return INVALID_OPERATION; - double playbackTrackLatencyMs; - if (playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) != OK) return INVALID_OPERATION; + // For PCM tracks get server latency. + if (audio_is_linear_pcm(recordTrack->format())) { + double recordServerLatencyMs, playbackTrackLatencyMs; + if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) == OK + && playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) == OK) { + *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs; + return OK; + } + } + + // See if kernel latencies are available. + // If so, do a frame diff and time difference computation to estimate + // the total patch latency. This requires that frame counts are reported by the + // HAL are matched properly in the case of record overruns and playback underruns. + ThreadBase::TrackBase::FrameTime recordFT{}, playFT{}; + recordTrack->getKernelFrameTime(&recordFT); + playbackTrack->getKernelFrameTime(&playFT); + if (recordFT.timeNs > 0 && playFT.timeNs > 0) { + const int64_t frameDiff = recordFT.frames - playFT.frames; + const int64_t timeDiffNs = recordFT.timeNs - playFT.timeNs; + + // It is possible that the patch track and patch record have a large time disparity because + // one thread runs but another is stopped. We arbitrarily choose the maximum timestamp + // time difference based on how often we expect the timestamps to update in normal operation + // (typical should be no more than 50 ms). + // + // If the timestamps aren't sampled close enough, the patch latency is not + // considered valid. + // + // TODO: change this based on more experiments. + constexpr int64_t maxValidTimeDiffNs = 200 * NANOS_PER_MILLISECOND; + if (std::abs(timeDiffNs) < maxValidTimeDiffNs) { + *latencyMs = frameDiff * 1e3 / recordTrack->sampleRate() + - timeDiffNs * 1e-6; + return OK; + } + } - *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs; - return OK; + return INVALID_OPERATION; } String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 95da9d7c5d..a43cb7570f 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -187,6 +187,19 @@ public: return status; } + // TODO: Consider making this external. + struct FrameTime { + int64_t frames; + int64_t timeNs; + }; + + // KernelFrameTime is updated per "mix" period even for non-pcm tracks. + void getKernelFrameTime(FrameTime *ft) const { + *ft = mKernelFrameTime.load(); + } + + audio_format_t format() const { return mFormat; } + protected: DISALLOW_COPY_AND_ASSIGN(TrackBase); @@ -198,8 +211,6 @@ protected: // but putting it in TrackBase avoids the complexity of virtual inheritance virtual size_t framesReady() const { return SIZE_MAX; } - audio_format_t format() const { return mFormat; } - uint32_t channelCount() const { return mChannelCount; } audio_channel_mask_t channelMask() const { return mChannelMask; } @@ -307,6 +318,7 @@ protected: bool mServerLatencySupported = false; std::atomic mServerLatencyFromTrack{}; // latency from track or server timestamp. std::atomic mServerLatencyMs{}; // last latency pushed from server thread. + std::atomic mKernelFrameTime{}; // last frame time on kernel side. }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 8ad06f0cbc..78e6c6cfa6 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1258,6 +1258,16 @@ void AudioFlinger::PlaybackThread::Track::resumeAck() { void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( int64_t trackFramesReleased, int64_t sinkFramesWritten, uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) { + // Make the kernel frametime available. + const FrameTime ft{ + timeStamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], + timeStamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]}; + // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs); + mKernelFrameTime.store(ft); + if (!audio_is_linear_pcm(mFormat)) { + return; + } + //update frame map mFrameMap.push(trackFramesReleased, sinkFramesWritten); @@ -1886,6 +1896,16 @@ void AudioFlinger::RecordThread::RecordTrack::updateTrackFrameInfo( int64_t trackFramesReleased, int64_t sourceFramesRead, uint32_t halSampleRate, const ExtendedTimestamp ×tamp) { + // Make the kernel frametime available. + const FrameTime ft{ + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], + timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]}; + // ALOGD("FrameTime: %lld %lld", (long long)ft.frames, (long long)ft.timeNs); + mKernelFrameTime.store(ft); + if (!audio_is_linear_pcm(mFormat)) { + return; + } + ExtendedTimestamp local = timestamp; // Convert HAL frames to server-side track frames at track sample rate. -- GitLab From c8fddf3dc6a1d51058d9d3b207e818e84ac80fed Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 8 Aug 2018 18:32:37 -0700 Subject: [PATCH 0204/1530] AudioFlinger: Add timestamp correction to MSD BUS Threads Test: MSD YouTube playback Bug: 112431914 Change-Id: Ic15ace24d6ab3613be08978b925fd7b9dc7aef38 --- services/audioflinger/Threads.cpp | 60 ++++++++++++++++++++++++++++--- services/audioflinger/Threads.h | 17 ++++++++- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d07f4ba150..2a37dd8de1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -863,6 +863,7 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u || mType == DIRECT || mType == OFFLOAD) { dprintf(fd, " Timestamp stats: %s\n", mTimestampVerifier.toString().c_str()); + dprintf(fd, " Timestamp corrected: %s\n", isTimestampCorrectionEnabled() ? "yes" : "no"); } if (locked) { @@ -1732,10 +1733,21 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp& audioFlinge if (mOutput->audioHwDev->canSetMasterMute()) { mMasterMute = false; } + mIsMsdDevice = strcmp( + mOutput->audioHwDev->moduleName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0; } readOutputParameters_l(); + // TODO: We may also match on address as well as device type for + // AUDIO_DEVICE_OUT_BUS, AUDIO_DEVICE_OUT_ALL_A2DP, AUDIO_DEVICE_OUT_REMOTE_SUBMIX + if (type == MIXER || type == DIRECT) { + mTimestampCorrectedDevices = (audio_devices_t)property_get_int64( + "audio.timestamp.corrected_output_devices", + (int64_t)(mIsMsdDevice ? AUDIO_DEVICE_OUT_BUS // turn on by default for MSD + : AUDIO_DEVICE_NONE)); + } + // ++ operator does not compile for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream = (audio_stream_type_t) (stream + 1)) { @@ -3248,6 +3260,21 @@ bool AudioFlinger::PlaybackThread::threadLoop() mTimestampVerifier.add(timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], mSampleRate); + + if (isTimestampCorrectionEnabled()) { + ALOGV("TS_BEFORE: %d %lld %lld", id(), + (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], + (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]); + auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp(); + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] + = correctedTimestamp.mFrames; + timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] + = correctedTimestamp.mTimeNs; + ALOGV("TS_AFTER: %d %lld %lld", id(), + (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], + (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]); + } + // We always fetch the timestamp here because often the downstream // sink will block while writing. @@ -6368,8 +6395,20 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id); mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); + if (mInput != nullptr && mInput->audioHwDev != nullptr) { + mIsMsdDevice = strcmp( + mInput->audioHwDev->moduleName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0; + } + readInputParameters_l(); + // TODO: We may also match on address as well as device type for + // AUDIO_DEVICE_IN_BUS, AUDIO_DEVICE_IN_BLUETOOTH_A2DP, AUDIO_DEVICE_IN_REMOTE_SUBMIX + mTimestampCorrectedDevices = (audio_devices_t)property_get_int64( + "audio.timestamp.corrected_input_devices", + (int64_t)(mIsMsdDevice ? AUDIO_DEVICE_IN_BUS // turn on by default for MSD + : AUDIO_DEVICE_NONE)); + // create an NBAIO source for the HAL input stream, and negotiate mInputSource = new AudioStreamInSource(input->stream); size_t numCounterOffers = 0; @@ -6804,7 +6843,22 @@ reacquire_wakelock: int64_t position, time; if (mStandby) { mTimestampVerifier.discontinuity(); - } else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR) { + } else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR + && time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) { + + mTimestampVerifier.add(position, time, mSampleRate); + + // Correct timestamps + if (isTimestampCorrectionEnabled()) { + ALOGV("TS_BEFORE: %d %lld %lld", + id(), (long long)time, (long long)position); + auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp(); + position = correctedTimestamp.mFrames; + time = correctedTimestamp.mTimeNs; + ALOGV("TS_AFTER: %d %lld %lld", + id(), (long long)time, (long long)position); + } + mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = position; mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = time; // Note: In general record buffers should tend to be empty in @@ -6812,10 +6866,6 @@ reacquire_wakelock: // // Also, it is not advantageous to call get_presentation_position during the read // as the read obtains a lock, preventing the timestamp call from executing. - - mTimestampVerifier.add(mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL], - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], - mSampleRate); } else { mTimestampVerifier.error(); } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 705efbca7e..37b3870c93 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -393,6 +393,10 @@ public: void broadcast_l(); + virtual bool isTimestampCorrectionEnabled() const { return false; } + + bool isMsdDevice() const { return mIsMsdDevice; } + mutable Mutex mLock; protected: @@ -501,7 +505,8 @@ protected: ExtendedTimestamp mTimestamp; TimestampVerifier< // For timestamp statistics. int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier; - + audio_devices_t mTimestampCorrectedDevices = AUDIO_DEVICE_NONE; + bool mIsMsdDevice = false; // A condition that must be evaluated by the thread loop has changed and // we must not wait for async write callback in the thread loop before evaluating it bool mSignalPending; @@ -816,6 +821,11 @@ public: && mTracks.size() < PlaybackThread::kMaxTracks; } + bool isTimestampCorrectionEnabled() const override { + const audio_devices_t device = + mOutDevice & mTimestampCorrectedDevices; + return audio_is_output_devices(device) && popcount(device) > 0; + } protected: // updated by readOutputParameters_l() size_t mNormalFrameCount; // normal mixer and effects @@ -1532,6 +1542,11 @@ public: bool fastTrackAvailable() const { return mFastTrackAvail; } + bool isTimestampCorrectionEnabled() const override { + // checks popcount for exactly one device. + return audio_is_input_device( + mInDevice & mTimestampCorrectedDevices); + } private: // Enter standby if not already in standby, and set mStandby flag void standbyIfNotAlreadyInStandby(); -- GitLab From 2dbffc2af5e200dbddfa1aeb79569e7811dfbc9a Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 8 Aug 2018 18:50:41 -0700 Subject: [PATCH 0205/1530] AudioFlinger: Incorporate downstream patch latency into timestamps Available for MSD BUS devices. Test: MSD YouTube playback and audioflinger dumpsys Bug: 112435419 Change-Id: Ifd35b68a77464e0db79ad1ed50b72b00a616c253 --- services/audioflinger/PatchPanel.h | 1 + services/audioflinger/Threads.cpp | 54 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 269a398315..2d9bd8e2e9 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -35,6 +35,7 @@ public: // Must be called under AudioFlinger::mLock status_t getLatencyMs_l(double *latencyMs) const; + audio_patch_handle_t getPatchHandle() const { return mPatchHandle; }; audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; }; audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; }; private: diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 2a37dd8de1..c73a4464bd 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3220,6 +3220,8 @@ bool AudioFlinger::PlaybackThread::threadLoop() if (mType == OFFLOAD || mType == DIRECT) { mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO); } + audio_utils::Statistics downstreamLatencyStatMs(0.999 /* alpha */); + audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE; while (!exitPending()) { @@ -3231,6 +3233,46 @@ bool AudioFlinger::PlaybackThread::threadLoop() Vector< sp > effectChains; + // If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency. + // + // Note: we access outDevice() outside of mLock. + if (isMsdDevice() && (outDevice() & AUDIO_DEVICE_OUT_BUS) != 0) { + // Here, we try for the AF lock, but do not block on it as the latency + // is more informational. + if (mAudioFlinger->mLock.tryLock() == NO_ERROR) { + std::vector swPatches; + double latencyMs; + status_t status = INVALID_OPERATION; + audio_patch_handle_t downstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE; + if (mAudioFlinger->mPatchPanel.getDownstreamSoftwarePatches(id(), &swPatches) == OK + && swPatches.size() > 0) { + status = swPatches[0].getLatencyMs_l(&latencyMs); + downstreamPatchHandle = swPatches[0].getPatchHandle(); + } + if (downstreamPatchHandle != lastDownstreamPatchHandle) { + downstreamLatencyStatMs.reset(); + lastDownstreamPatchHandle = downstreamPatchHandle; + } + if (status == OK) { + // verify downstream latency (we assume a max reasonable + // latency of 1 second). + if (latencyMs >= 0. && latencyMs <= 1000.) { + ALOGV("new downstream latency %lf ms", latencyMs); + downstreamLatencyStatMs.add(latencyMs); + } else { + ALOGD("out of range downstream latency %lf ms", latencyMs); + } + } + mAudioFlinger->mLock.unlock(); + } + } else { + if (lastDownstreamPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + // our device is no longer AUDIO_DEVICE_OUT_BUS, reset patch handle and stats. + downstreamLatencyStatMs.reset(); + lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + } + { // scope for mLock Mutex::Autolock _l(mLock); @@ -3273,6 +3315,18 @@ bool AudioFlinger::PlaybackThread::threadLoop() ALOGV("TS_AFTER: %d %lld %lld", id(), (long long)timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL], (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]); + + // Note: Downstream latency only added if timestamp correction enabled. + if (downstreamLatencyStatMs.getN() > 0) { // we have latency info. + const int64_t newPosition = + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] + - int64_t(downstreamLatencyStatMs.getMean() * mSampleRate * 1e-3); + // prevent retrograde + timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max( + newPosition, + (mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] + - mSuspendedFrames)); + } } // We always fetch the timestamp here because often the downstream -- GitLab From 5033aa19cd443559777b84acc63cad1177121cc9 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 24 Jul 2018 11:56:19 -0700 Subject: [PATCH 0206/1530] Convert libcameraservice Android.mk -> Android.bp module This would allow other client modules defined in Android.bp to include libcameraservice as a dependency. Bug: 110364143 Test: mm -j64 Change-Id: Ia3be563e4fbb27155d6a46278931ca689b8cf8fd Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/Android.bp | 106 ++++++++++++++++++++ services/camera/libcameraservice/Android.mk | 84 +--------------- 2 files changed, 107 insertions(+), 83 deletions(-) create mode 100644 services/camera/libcameraservice/Android.bp diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp new file mode 100644 index 0000000000..eccbe54941 --- /dev/null +++ b/services/camera/libcameraservice/Android.bp @@ -0,0 +1,106 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// libcameraservice +// + +cc_library_shared { + name: "libcameraservice", + + // Camera service source + + srcs: [ + "CameraService.cpp", + "CameraFlashlight.cpp", + "common/Camera2ClientBase.cpp", + "common/CameraDeviceBase.cpp", + "common/CameraProviderManager.cpp", + "common/FrameProcessorBase.cpp", + "api1/CameraClient.cpp", + "api1/Camera2Client.cpp", + "api1/client2/Parameters.cpp", + "api1/client2/FrameProcessor.cpp", + "api1/client2/StreamingProcessor.cpp", + "api1/client2/JpegProcessor.cpp", + "api1/client2/CallbackProcessor.cpp", + "api1/client2/JpegCompressor.cpp", + "api1/client2/CaptureSequencer.cpp", + "api1/client2/ZslProcessor.cpp", + "api2/CameraDeviceClient.cpp", + "device1/CameraHardwareInterface.cpp", + "device3/Camera3Device.cpp", + "device3/Camera3Stream.cpp", + "device3/Camera3IOStreamBase.cpp", + "device3/Camera3InputStream.cpp", + "device3/Camera3OutputStream.cpp", + "device3/Camera3DummyStream.cpp", + "device3/Camera3SharedOutputStream.cpp", + "device3/StatusTracker.cpp", + "device3/Camera3BufferManager.cpp", + "device3/Camera3StreamSplitter.cpp", + "device3/DistortionMapper.cpp", + "gui/RingBufferConsumer.cpp", + "utils/CameraTraces.cpp", + "utils/AutoConditionLock.cpp", + "utils/TagMonitor.cpp", + "utils/LatencyHistogram.cpp", + ], + + shared_libs: [ + "libui", + "liblog", + "libutilscallstack", + "libutils", + "libbinder", + "libcutils", + "libmedia", + "libmediautils", + "libcamera_client", + "libcamera_metadata", + "libfmq", + "libgui", + "libhardware", + "libhidlbase", + "libhidltransport", + "libjpeg", + "libmemunreachable", + "android.hardware.camera.common@1.0", + "android.hardware.camera.provider@2.4", + "android.hardware.camera.device@1.0", + "android.hardware.camera.device@3.2", + "android.hardware.camera.device@3.3", + "android.hardware.camera.device@3.4", + ], + + export_shared_lib_headers: [ + "libbinder", + "libcamera_client", + "libfmq", + ], + + include_dirs: [ + "system/media/private/camera/include", + "frameworks/native/include/media/openmax", + ], + + export_include_dirs: ["."], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + +} diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 96261ab0ef..4cfecfdfa1 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -14,91 +14,9 @@ LOCAL_PATH:= $(call my-dir) -# -# libcameraservice -# - include $(CLEAR_VARS) -# Camera service source - -LOCAL_SRC_FILES := \ - CameraService.cpp \ - CameraFlashlight.cpp \ - common/Camera2ClientBase.cpp \ - common/CameraDeviceBase.cpp \ - common/CameraProviderManager.cpp \ - common/FrameProcessorBase.cpp \ - api1/CameraClient.cpp \ - api1/Camera2Client.cpp \ - api1/client2/Parameters.cpp \ - api1/client2/FrameProcessor.cpp \ - api1/client2/StreamingProcessor.cpp \ - api1/client2/JpegProcessor.cpp \ - api1/client2/CallbackProcessor.cpp \ - api1/client2/JpegCompressor.cpp \ - api1/client2/CaptureSequencer.cpp \ - api1/client2/ZslProcessor.cpp \ - api2/CameraDeviceClient.cpp \ - device1/CameraHardwareInterface.cpp \ - device3/Camera3Device.cpp \ - device3/Camera3Stream.cpp \ - device3/Camera3IOStreamBase.cpp \ - device3/Camera3InputStream.cpp \ - device3/Camera3OutputStream.cpp \ - device3/Camera3DummyStream.cpp \ - device3/Camera3SharedOutputStream.cpp \ - device3/StatusTracker.cpp \ - device3/Camera3BufferManager.cpp \ - device3/Camera3StreamSplitter.cpp \ - device3/DistortionMapper.cpp \ - gui/RingBufferConsumer.cpp \ - utils/CameraTraces.cpp \ - utils/AutoConditionLock.cpp \ - utils/TagMonitor.cpp \ - utils/LatencyHistogram.cpp - -LOCAL_SHARED_LIBRARIES:= \ - libui \ - liblog \ - libutilscallstack \ - libutils \ - libbinder \ - libcutils \ - libmedia \ - libmediautils \ - libcamera_client \ - libcamera_metadata \ - libfmq \ - libgui \ - libhardware \ - libhidlbase \ - libhidltransport \ - libjpeg \ - libmemunreachable \ - android.hardware.camera.common@1.0 \ - android.hardware.camera.provider@2.4 \ - android.hardware.camera.device@1.0 \ - android.hardware.camera.device@3.2 \ - android.hardware.camera.device@3.3 \ - android.hardware.camera.device@3.4 - -LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libcamera_client libfmq - -LOCAL_C_INCLUDES += \ - system/media/private/camera/include \ - frameworks/native/include/media/openmax - -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - frameworks/av/services/camera/libcameraservice - -LOCAL_CFLAGS += -Wall -Wextra -Werror - -LOCAL_MODULE:= libcameraservice - -include $(BUILD_SHARED_LIBRARY) - -# Build tests too +# Build tests include $(LOCAL_PATH)/tests/Android.mk -- GitLab From c524813b806a64123d376d1afb4d21b09a6de453 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 15 Aug 2018 12:19:20 -0700 Subject: [PATCH 0207/1530] Camera: fix race issue in Camera2 clients Updating of the mDevice sp<> is racy. This change will cause the Camera3Device object lives until the client is destructed (which can be a few seconds after camera app closed and GC kicking in to delete the client) The benefit is the dumpDevice can remain lock free. Also fix a problem where we call virtual function disconnect in Camera3Device destructor. Test: the double free problem cannot reproduce Bug: 112639939 Change-Id: I77762db8f59cf33891631d13fc3bdb3830ae08fb --- services/camera/libcameraservice/api1/Camera2Client.cpp | 2 -- .../camera/libcameraservice/common/Camera2ClientBase.cpp | 4 +--- services/camera/libcameraservice/common/Camera2ClientBase.h | 5 ++++- services/camera/libcameraservice/device3/Camera3Device.cpp | 6 +++++- services/camera/libcameraservice/device3/Camera3Device.h | 2 ++ 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index 261cdbfb14..bf18c481f7 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -453,8 +453,6 @@ binder::Status Camera2Client::disconnect() { mDevice->disconnect(); - mDevice.clear(); - CameraService::Client::disconnect(); return res; diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index ce006a77ff..aeea473090 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -57,13 +57,13 @@ Camera2ClientBase::Camera2ClientBase( cameraId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid), mSharedCameraCallbacks(remoteCallback), mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)), + mDevice(new Camera3Device(cameraId)), mDeviceActive(false), mApi1CameraId(api1CameraId) { ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(), String8(clientPackageName).string(), clientPid, clientUid); mInitialClientPid = clientPid; - mDevice = new Camera3Device(cameraId); LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here."); } @@ -206,8 +206,6 @@ void Camera2ClientBase::detachDevice() { if (mDevice == 0) return; mDevice->disconnect(); - mDevice.clear(); - ALOGV("Camera %s: Detach complete", TClientBase::mCameraIdStr.string()); } diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index e74fbdf8a2..6693847aec 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -130,7 +130,10 @@ protected: /** CameraDeviceBase instance wrapping HAL3+ entry */ const int mDeviceVersion; - sp mDevice; + + // Set to const to avoid mDevice being updated (update of sp<> is racy) during + // dumpDevice (which is important to be lock free for debugging purpose) + const sp mDevice; /** Utility members */ diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 4c6718c5db..765640742e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -90,7 +90,7 @@ Camera3Device::~Camera3Device() { ATRACE_CALL(); ALOGV("%s: Tearing down for camera id %s", __FUNCTION__, mId.string()); - disconnect(); + disconnectImpl(); } const String8& Camera3Device::getId() const { @@ -275,6 +275,10 @@ status_t Camera3Device::initializeCommonLocked() { } status_t Camera3Device::disconnect() { + return disconnectImpl(); +} + +status_t Camera3Device::disconnectImpl() { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); Mutex::Autolock stLock(mTrackerLock); diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 51e1fb0a42..85f9614481 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -210,6 +210,8 @@ class Camera3Device : private: + status_t disconnectImpl(); + // internal typedefs using RequestMetadataQueue = hardware::MessageQueue; using ResultMetadataQueue = hardware::MessageQueue; -- GitLab From 24628310591c3b4d9701ebad9df7f5750dc599b2 Mon Sep 17 00:00:00 2001 From: Ari Hausman-Cohen Date: Mon, 13 Aug 2018 15:01:09 -0700 Subject: [PATCH 0208/1530] Add dynamic source default effects Allows runtime modification of what effects should be default attached to sources of different types. The core functionality was already in the AudioPolicyEffects system, this allows the dynamic modification of the lists. Bug: 78527120 Test: Builds, manually tested an app adding a source effect, tested both media on the specified source and on a different source. Also tested by Android Things integration tests. Change-Id: I4da15787278c79d80043e172f4f81d3b2a139f44 --- media/libaudioclient/AudioEffect.cpp | 40 ++++++++ media/libaudioclient/IAudioPolicyService.cpp | 76 +++++++++++++- .../include/media/AudioEffect.h | 53 ++++++++++ .../include/media/IAudioPolicyService.h | 7 ++ .../service/AudioPolicyEffects.cpp | 99 +++++++++++++++++++ .../audiopolicy/service/AudioPolicyEffects.h | 13 ++- .../service/AudioPolicyInterfaceImpl.cpp | 75 +++++++++----- .../audiopolicy/service/AudioPolicyService.h | 9 ++ 8 files changed, 347 insertions(+), 25 deletions(-) diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp index 0641b6ead6..0ae2738138 100644 --- a/media/libaudioclient/AudioEffect.cpp +++ b/media/libaudioclient/AudioEffect.cpp @@ -456,6 +456,38 @@ status_t AudioEffect::newEffectUniqueId(audio_unique_id_t* id) return NO_ERROR; } +status_t AudioEffect::addSourceDefaultEffect(const char *typeStr, + const String16& opPackageName, + const char *uuidStr, + int32_t priority, + audio_source_t source, + audio_unique_id_t *id) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + if (typeStr == NULL && uuidStr == NULL) return BAD_VALUE; + + // Convert type & uuid from string to effect_uuid_t. + effect_uuid_t type; + if (typeStr != NULL) { + status_t res = stringToGuid(typeStr, &type); + if (res != OK) return res; + } else { + type = *EFFECT_UUID_NULL; + } + + effect_uuid_t uuid; + if (uuidStr != NULL) { + status_t res = stringToGuid(uuidStr, &uuid); + if (res != OK) return res; + } else { + uuid = *EFFECT_UUID_NULL; + } + + return aps->addSourceDefaultEffect(&type, opPackageName, &uuid, priority, source, id); +} + status_t AudioEffect::addStreamDefaultEffect(const char *typeStr, const String16& opPackageName, const char *uuidStr, @@ -488,6 +520,14 @@ status_t AudioEffect::addStreamDefaultEffect(const char *typeStr, return aps->addStreamDefaultEffect(&type, opPackageName, &uuid, priority, usage, id); } +status_t AudioEffect::removeSourceDefaultEffect(audio_unique_id_t id) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + return aps->removeSourceDefaultEffect(id); +} + status_t AudioEffect::removeStreamDefaultEffect(audio_unique_id_t id) { const sp& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index abf74f80e4..32a71f3b6a 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -83,7 +83,9 @@ enum { GET_SURROUND_FORMATS, SET_SURROUND_FORMAT_ENABLED, ADD_STREAM_DEFAULT_EFFECT, - REMOVE_STREAM_DEFAULT_EFFECT + REMOVE_STREAM_DEFAULT_EFFECT, + ADD_SOURCE_DEFAULT_EFFECT, + REMOVE_SOURCE_DEFAULT_EFFECT }; #define MAX_ITEMS_PER_LIST 1024 @@ -904,6 +906,41 @@ public: return static_cast (reply.readInt32()); } + virtual status_t addSourceDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(type, sizeof(effect_uuid_t)); + data.writeString16(opPackageName); + data.write(uuid, sizeof(effect_uuid_t)); + data.writeInt32(priority); + data.writeInt32((int32_t) source); + status_t status = remote()->transact(ADD_SOURCE_DEFAULT_EFFECT, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast (reply.readInt32()); + *id = reply.readInt32(); + return status; + } + + virtual status_t removeSourceDefaultEffect(audio_unique_id_t id) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(id); + status_t status = remote()->transact(REMOVE_SOURCE_DEFAULT_EFFECT, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1636,6 +1673,43 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case ADD_SOURCE_DEFAULT_EFFECT: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + effect_uuid_t type; + status_t status = data.read(&type, sizeof(effect_uuid_t)); + if (status != NO_ERROR) { + return status; + } + String16 opPackageName; + status = data.readString16(&opPackageName); + if (status != NO_ERROR) { + return status; + } + effect_uuid_t uuid; + status = data.read(&uuid, sizeof(effect_uuid_t)); + if (status != NO_ERROR) { + return status; + } + int32_t priority = data.readInt32(); + audio_source_t source = (audio_source_t) data.readInt32(); + audio_unique_id_t id = 0; + reply->writeInt32(static_cast (addSourceDefaultEffect(&type, + opPackageName, + &uuid, + priority, + source, + &id))); + reply->writeInt32(id); + return NO_ERROR; + } + + case REMOVE_SOURCE_DEFAULT_EFFECT: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_unique_id_t id = static_cast(data.readInt32()); + reply->writeInt32(static_cast (removeSourceDefaultEffect(id))); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h index c97f7838a3..0de58b85a5 100644 --- a/media/libaudioclient/include/media/AudioEffect.h +++ b/media/libaudioclient/include/media/AudioEffect.h @@ -169,6 +169,44 @@ public: * Static methods for adding/removing system-wide effects. */ + /* + * Adds an effect to the list of default output effects for a given source type. + * + * If the effect is no longer available when a source of the given type + * is created, the system will continue without adding it. + * + * Parameters: + * typeStr: Type uuid of effect to be a default: can be null if uuidStr is specified. + * This may correspond to the OpenSL ES interface implemented by this effect, + * or could be some vendor-defined type. + * opPackageName: The package name used for app op checks. + * uuidStr: Uuid of effect to be a default: can be null if type is specified. + * This uuid corresponds to a particular implementation of an effect type. + * Note if both uuidStr and typeStr are specified, typeStr is ignored. + * priority: Requested priority for effect control: the priority level corresponds to the + * value of priority parameter: negative values indicate lower priorities, positive + * values higher priorities, 0 being the normal priority. + * source: The source this effect should be a default for. + * id: Address where the system-wide unique id of the default effect should be returned. + * + * Returned status (from utils/Errors.h) can be: + * NO_ERROR successful operation. + * PERMISSION_DENIED could not get AudioFlinger interface + * or caller lacks required permissions. + * NO_INIT effect library failed to initialize. + * BAD_VALUE invalid source, type uuid or implementation uuid. + * NAME_NOT_FOUND no effect with this uuid or type found. + * + * Returned value + * *id: The system-wide unique id of the added default effect. + */ + static status_t addSourceDefaultEffect(const char* typeStr, + const String16& opPackageName, + const char* uuidStr, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id); + /* * Adds an effect to the list of default output effects for a given stream type. * @@ -208,6 +246,21 @@ public: audio_usage_t usage, audio_unique_id_t* id); + /* + * Removes an effect from the list of default output effects for a given source type. + * + * Parameters: + * id: The system-wide unique id of the effect that should no longer be a default. + * + * Returned status (from utils/Errors.h) can be: + * NO_ERROR successful operation. + * PERMISSION_DENIED could not get AudioFlinger interface + * or caller lacks required permissions. + * NO_INIT effect library failed to initialize. + * BAD_VALUE invalid id. + */ + static status_t removeSourceDefaultEffect(audio_unique_id_t id); + /* * Removes an effect from the list of default output effects for a given stream type. * diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index c2899f83e0..fdd8d57969 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -109,12 +109,19 @@ public: virtual status_t queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count) = 0; + virtual status_t addSourceDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id) = 0; virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, int32_t priority, audio_usage_t usage, audio_unique_id_t* id) = 0; + virtual status_t removeSourceDefaultEffect(audio_unique_id_t id) = 0; virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0; // Check if offload is possible for given format, stream type, sample rate, // bit rate, duration, video and streaming or offload property is enabled diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index 2858aadb2d..b3d564aaf3 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -318,6 +318,74 @@ status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t outpu return status; } +status_t AudioPolicyEffects::addSourceDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id) +{ + if (uuid == NULL || type == NULL) { + ALOGE("addSourceDefaultEffect(): Null uuid or type uuid pointer"); + return BAD_VALUE; + } + + // HOTWORD and FM_TUNER are two special case sources > MAX. + if (source < AUDIO_SOURCE_DEFAULT || + (source > AUDIO_SOURCE_MAX && + source != AUDIO_SOURCE_HOTWORD && + source != AUDIO_SOURCE_FM_TUNER)) { + ALOGE("addSourceDefaultEffect(): Unsupported source type %d", source); + return BAD_VALUE; + } + + // Check that |uuid| or |type| corresponds to an effect on the system. + effect_descriptor_t descriptor = {}; + status_t res = AudioEffect::getEffectDescriptor( + uuid, type, EFFECT_FLAG_TYPE_PRE_PROC, &descriptor); + if (res != OK) { + ALOGE("addSourceDefaultEffect(): Failed to find effect descriptor matching uuid/type."); + return res; + } + + // Only pre-processing effects can be added dynamically as source defaults. + if ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC) { + ALOGE("addSourceDefaultEffect(): Desired effect cannot be attached " + "as a source default effect."); + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // Find the EffectDescVector for the given source type, or create a new one if necessary. + ssize_t index = mInputSources.indexOfKey(source); + EffectDescVector *desc = NULL; + if (index < 0) { + // No effects for this source type yet. + desc = new EffectDescVector(); + mInputSources.add(source, desc); + } else { + desc = mInputSources.valueAt(index); + } + + // Create a new effect and add it to the vector. + res = AudioEffect::newEffectUniqueId(id); + if (res != OK) { + ALOGE("addSourceDefaultEffect(): failed to get new unique id."); + return res; + } + EffectDesc *effect = new EffectDesc( + descriptor.name, *type, opPackageName, *uuid, priority, *id); + desc->mEffects.add(effect); + // TODO(b/71813697): Support setting params as well. + + // TODO(b/71814300): Retroactively attach to any existing sources of the given type. + // This requires tracking the source type of each session id in addition to what is + // already being tracked. + + return NO_ERROR; +} + status_t AudioPolicyEffects::addStreamDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, @@ -384,6 +452,37 @@ status_t AudioPolicyEffects::addStreamDefaultEffect(const effect_uuid_t *type, return NO_ERROR; } +status_t AudioPolicyEffects::removeSourceDefaultEffect(audio_unique_id_t id) +{ + if (id == AUDIO_UNIQUE_ID_ALLOCATE) { + // ALLOCATE is not a unique identifier, but rather a reserved value indicating + // a real id has not been assigned. For default effects, this value is only used + // by system-owned defaults from the loaded config, which cannot be removed. + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // Check each source type. + size_t numSources = mInputSources.size(); + for (size_t i = 0; i < numSources; ++i) { + // Check each effect for each source. + EffectDescVector* descVector = mInputSources[i]; + for (auto desc = descVector->mEffects.begin(); desc != descVector->mEffects.end(); ++desc) { + if ((*desc)->mId == id) { + // Found it! + // TODO(b/71814300): Remove from any sources the effect was attached to. + descVector->mEffects.erase(desc); + // Handles are unique; there can only be one match, so return early. + return NO_ERROR; + } + } + } + + // Effect wasn't found, so it's been trivially removed successfully. + return NO_ERROR; +} + status_t AudioPolicyEffects::removeStreamDefaultEffect(audio_unique_id_t id) { if (id == AUDIO_UNIQUE_ID_ALLOCATE) { diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 69367b1dc0..6ad01f7611 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -81,7 +81,15 @@ public: audio_stream_type_t stream, audio_session_t audioSession); - // Add the effect to the list of default effects for streams of type |stream|. + // Add the effect to the list of default effects for sources of type |source|. + status_t addSourceDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id); + + // Add the effect to the list of default effects for streams of a given usage. status_t addStreamDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, @@ -89,6 +97,9 @@ public: audio_usage_t usage, audio_unique_id_t* id); + // Remove the default source effect from wherever it's attached. + status_t removeSourceDefaultEffect(audio_unique_id_t id); + // Remove the default stream effect from wherever it's attached. status_t removeStreamDefaultEffect(audio_unique_id_t id); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 3439c9bcda..02ab07fbed 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -829,67 +829,96 @@ bool AudioPolicyService::isSourceActive(audio_source_t source) const return mAudioPolicyManager->isSourceActive(source); } -status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSession, - effect_descriptor_t *descriptors, - uint32_t *count) +status_t AudioPolicyService::getAudioPolicyEffects(sp& audioPolicyEffects) { if (mAudioPolicyManager == NULL) { - *count = 0; return NO_INIT; } - spaudioPolicyEffects; { Mutex::Autolock _l(mLock); audioPolicyEffects = mAudioPolicyEffects; } if (audioPolicyEffects == 0) { - *count = 0; return NO_INIT; } + + return OK; +} + +status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + spaudioPolicyEffects; + status_t status = getAudioPolicyEffects(audioPolicyEffects); + if (status != OK) { + *count = 0; + return status; + } return audioPolicyEffects->queryDefaultInputEffects( (audio_session_t)audioSession, descriptors, count); } -status_t AudioPolicyService::addStreamDefaultEffect(const effect_uuid_t *type, +status_t AudioPolicyService::addSourceDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, int32_t priority, - audio_usage_t usage, + audio_source_t source, audio_unique_id_t* id) { - if (mAudioPolicyManager == NULL) { - return NO_INIT; + spaudioPolicyEffects; + status_t status = getAudioPolicyEffects(audioPolicyEffects); + if (status != OK) { + return status; } if (!modifyDefaultAudioEffectsAllowed()) { return PERMISSION_DENIED; } + return audioPolicyEffects->addSourceDefaultEffect( + type, opPackageName, uuid, priority, source, id); +} + +status_t AudioPolicyService::addStreamDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_usage_t usage, + audio_unique_id_t* id) +{ spaudioPolicyEffects; - { - Mutex::Autolock _l(mLock); - audioPolicyEffects = mAudioPolicyEffects; + status_t status = getAudioPolicyEffects(audioPolicyEffects); + if (status != OK) { + return status; } - if (audioPolicyEffects == 0) { - return NO_INIT; + if (!modifyDefaultAudioEffectsAllowed()) { + return PERMISSION_DENIED; } return audioPolicyEffects->addStreamDefaultEffect( type, opPackageName, uuid, priority, usage, id); } -status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id) +status_t AudioPolicyService::removeSourceDefaultEffect(audio_unique_id_t id) { - if (mAudioPolicyManager == NULL) { - return NO_INIT; + spaudioPolicyEffects; + status_t status = getAudioPolicyEffects(audioPolicyEffects); + if (status != OK) { + return status; } if (!modifyDefaultAudioEffectsAllowed()) { return PERMISSION_DENIED; } + return audioPolicyEffects->removeSourceDefaultEffect(id); +} + +status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id) +{ spaudioPolicyEffects; - { - Mutex::Autolock _l(mLock); - audioPolicyEffects = mAudioPolicyEffects; + status_t status = getAudioPolicyEffects(audioPolicyEffects); + if (status != OK) { + return status; } - if (audioPolicyEffects == 0) { - return NO_INIT; + if (!modifyDefaultAudioEffectsAllowed()) { + return PERMISSION_DENIED; } return audioPolicyEffects->removeStreamDefaultEffect(id); } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 44c0347f7f..6c5564728a 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -126,12 +126,19 @@ public: virtual status_t queryDefaultPreProcessing(audio_session_t audioSession, effect_descriptor_t *descriptors, uint32_t *count); + virtual status_t addSourceDefaultEffect(const effect_uuid_t *type, + const String16& opPackageName, + const effect_uuid_t *uuid, + int32_t priority, + audio_source_t source, + audio_unique_id_t* id); virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, int32_t priority, audio_usage_t usage, audio_unique_id_t* id); + virtual status_t removeSourceDefaultEffect(audio_unique_id_t id); virtual status_t removeStreamDefaultEffect(audio_unique_id_t id); virtual status_t onTransact( @@ -259,6 +266,8 @@ private: std::string getDeviceTypeStrForPortId(audio_port_handle_t portId); + status_t getAudioPolicyEffects(sp& audioPolicyEffects); + // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID // transitions to an active state we will start reporting buffers with data. This approach -- GitLab From ae7f740a793dc5a36a5ee25ec356b869d9b4bd5b Mon Sep 17 00:00:00 2001 From: "zhongli.wang" Date: Thu, 12 Jun 2014 14:30:31 +0200 Subject: [PATCH 0209/1530] Fix no sound with FDR-AX100 recorded 4k video 1.Pcm(twos and sowt) parsing support in MPEG4Extractor 2.Pcm data reading from SampleTable support Bug: 80501047 Test: Play XAVC-S Video and check if the audio is heard Change-Id: I5edc7f1920c80e23dfdb069259d19e28240799b5 --- media/extractors/mp4/MPEG4Extractor.cpp | 125 +++++++++++++++--- media/extractors/mp4/SampleIterator.h | 5 + media/extractors/mp4/SampleTable.cpp | 5 + media/extractors/mp4/SampleTable.h | 3 + .../include/media/stagefright/MetaDataBase.h | 2 + 5 files changed, 118 insertions(+), 22 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 8412812c62..d10d0d60fa 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -68,6 +68,7 @@ enum { }; class MPEG4Source : public MediaTrack { +static const size_t kMaxPcmFrameSize = 8192; public: // Caller retains ownership of both "dataSource" and "sampleTable". MPEG4Source(MetaDataBase &format, @@ -127,7 +128,7 @@ private: bool mIsAVC; bool mIsHEVC; bool mIsAC4; - + bool mIsPcm; size_t mNALLengthSize; bool mStarted; @@ -329,6 +330,11 @@ static const char *FourCC2MIME(uint32_t fourcc) { return MEDIA_MIMETYPE_VIDEO_HEVC; case FOURCC('a', 'c', '-', '4'): return MEDIA_MIMETYPE_AUDIO_AC4; + + case FOURCC('t', 'w', 'o', 's'): + case FOURCC('s', 'o', 'w', 't'): + return MEDIA_MIMETYPE_AUDIO_RAW; + default: ALOGW("Unknown fourcc: %c%c%c%c", (fourcc >> 24) & 0xff, @@ -1478,6 +1484,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('e', 'n', 'c', 'a'): case FOURCC('s', 'a', 'm', 'r'): case FOURCC('s', 'a', 'w', 'b'): + case FOURCC('t', 'w', 'o', 's'): + case FOURCC('s', 'o', 'w', 't'): { if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a') && depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) { @@ -1547,6 +1555,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // if the chunk type is enca, we'll get the type from the frma box later mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate); + + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, FourCC2MIME(chunk_type))) { + mLastTrack->meta.setInt32(kKeyBitsPerSample, sample_size); + if (chunk_type == FOURCC('t', 'w', 'o', 's')) { + mLastTrack->meta.setInt32(kKeyPcmBigEndian, 1); + } + } } ALOGV("*** coding='%s' %d channels, size %d, rate %d\n", chunk, num_channels, sample_size, sample_rate); @@ -3947,6 +3962,7 @@ MPEG4Source::MPEG4Source( mIsAVC(false), mIsHEVC(false), mIsAC4(false), + mIsPcm(false), mNALLengthSize(0), mStarted(false), mGroup(NULL), @@ -4009,6 +4025,27 @@ MPEG4Source::MPEG4Source( mNALLengthSize = 1 + (ptr[14 + 7] & 3); } + mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW); + + if (mIsPcm) { + int32_t numChannels = 0; + int32_t bitsPerSample = 0; + CHECK(mFormat.findInt32(kKeyBitsPerSample, &bitsPerSample)); + CHECK(mFormat.findInt32(kKeyChannelCount, &numChannels)); + + int32_t bytesPerSample = bitsPerSample >> 3; + int32_t pcmSampleSize = bytesPerSample * numChannels; + + size_t maxSampleSize; + status_t err = mSampleTable->getMaxSampleSize(&maxSampleSize); + if (err != OK || maxSampleSize != static_cast(pcmSampleSize) || bitsPerSample != 16) { + // Not supported + mIsPcm = false; + } else { + mFormat.setInt32(kKeyMaxInputSize, pcmSampleSize * kMaxPcmFrameSize); + } + } + CHECK(format.findInt32(kKeyTrackID, &mTrackId)); } @@ -4923,34 +4960,78 @@ status_t MPEG4Source::read( if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) { if (newBuffer) { - ssize_t num_bytes_read = - mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); + if (mIsPcm) { + // The twos' PCM block reader assumes that all samples has the same size. - if (num_bytes_read < (ssize_t)size) { - mBuffer->release(); - mBuffer = NULL; + uint32_t samplesToRead = mSampleTable->getLastSampleIndexInChunk() + - mCurrentSampleIndex + 1; + if (samplesToRead > kMaxPcmFrameSize) { + samplesToRead = kMaxPcmFrameSize; + } - return ERROR_IO; - } + ALOGV("Reading %d PCM frames of size %zu at index %d to stop of chunk at %d", + samplesToRead, size, mCurrentSampleIndex, + mSampleTable->getLastSampleIndexInChunk()); - CHECK(mBuffer != NULL); - mBuffer->set_range(0, size); - mBuffer->meta_data().clear(); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + size_t totalSize = samplesToRead * size; + uint8_t* buf = (uint8_t *)mBuffer->data(); + ssize_t bytesRead = mDataSource->readAt(offset, buf, totalSize); + if (bytesRead < (ssize_t)totalSize) { + mBuffer->release(); + mBuffer = NULL; - if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); - } + return ERROR_IO; + } - if (isSyncSample) { + mBuffer->meta_data().clear(); + mBuffer->meta_data().setInt64(kKeyTime, ((int64_t)cts * 1000000) / mTimescale); mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); - } - ++mCurrentSampleIndex; + int32_t byteOrder; + mFormat.findInt32(kKeyPcmBigEndian, &byteOrder); + + if (byteOrder == 1) { + // Big-endian -> little-endian + uint16_t *dstData = (uint16_t *)buf; + uint16_t *srcData = (uint16_t *)buf; + + for (size_t j = 0; j < bytesRead / sizeof(uint16_t); j++) { + dstData[j] = ntohs(srcData[j]); + } + } + + mCurrentSampleIndex += samplesToRead; + mBuffer->set_range(0, totalSize); + } else { + ssize_t num_bytes_read = + mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size); + + if (num_bytes_read < (ssize_t)size) { + mBuffer->release(); + mBuffer = NULL; + + return ERROR_IO; + } + + CHECK(mBuffer != NULL); + mBuffer->set_range(0, size); + mBuffer->meta_data().clear(); + mBuffer->meta_data().setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + mBuffer->meta_data().setInt64( + kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data().setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + } + + ++mCurrentSampleIndex; + } } if (!mIsAVC && !mIsHEVC && !mIsAC4) { diff --git a/media/extractors/mp4/SampleIterator.h b/media/extractors/mp4/SampleIterator.h index 6a3fd3b210..6e4f60eccc 100644 --- a/media/extractors/mp4/SampleIterator.h +++ b/media/extractors/mp4/SampleIterator.h @@ -36,6 +36,11 @@ struct SampleIterator { uint32_t getSampleTime() const { return mCurrentSampleTime; } uint32_t getSampleDuration() const { return mCurrentSampleDuration; } + uint32_t getLastSampleIndexInChunk() const { + return mCurrentSampleIndex + mSamplesPerChunk - + ((mCurrentSampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk) - 1; + } + status_t getSampleSizeDirect( uint32_t sampleIndex, size_t *size); diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp index 81c353e21e..28fe7177c3 100644 --- a/media/extractors/mp4/SampleTable.cpp +++ b/media/extractors/mp4/SampleTable.cpp @@ -946,6 +946,11 @@ status_t SampleTable::getSampleSize_l( sampleIndex, sampleSize); } +uint32_t SampleTable::getLastSampleIndexInChunk() { + Mutex::Autolock autoLock(mLock); + return mSampleIterator->getLastSampleIndexInChunk(); +} + status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off64_t *offset, diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/SampleTable.h index e4e974be94..dd688601b4 100644 --- a/media/extractors/mp4/SampleTable.h +++ b/media/extractors/mp4/SampleTable.h @@ -69,6 +69,9 @@ public: bool *isSyncSample = NULL, uint32_t *sampleDuration = NULL); + // call only after getMetaDataForSample has been called successfully. + uint32_t getLastSampleIndexInChunk(); + enum { kFlagBefore, kFlagAfter, diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h index dfe34e870d..2e9aedecc4 100644 --- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h +++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h @@ -53,6 +53,7 @@ enum { kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) kKeyBitRate = 'brte', // int32_t (bps) kKeyMaxBitRate = 'mxBr', // int32_t (bps) + kKeyBitsPerSample = 'bits', // int32_t (bits per sample) kKeyStreamHeader = 'stHd', // raw data kKeyESDS = 'esds', // raw data kKeyAACProfile = 'aacp', // int32_t @@ -225,6 +226,7 @@ enum { kKeyExifOffset = 'exof', // int64_t, Exif data offset kKeyExifSize = 'exsz', // int64_t, Exif data size kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block + kKeyPcmBigEndian = 'pcmb', // bool (int32_t) }; enum { -- GitLab From 5786e01d64860448d93b681a59c7d5a60bc12298 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Wed, 15 Aug 2018 09:03:47 -0700 Subject: [PATCH 0210/1530] NBLog: Restore log dump and add new logging types Merging is temporarily disabled for now because the readers managed by the merger are the same as the ones used for the dumpsys log dump, and reading from the log effectively consumes the buffer. Eventually, the readers for the two functionalities will need to be separated to avoid conflict. The dump of the merged buffer is also disabled, which removes the dumping of histograms. The new types added are monotonic thread cycle time, CPU thread cycle time, CPU frequency, and latency (which will need to be specified in further detail later). Logging support is added only for monotonic thread cycle time at the moment. Test: build, log monotonic thread cycle time in FastThread, check output in dumpsys media.log -r. Change-Id: I1b781d6db102fb917fd0bac964eeebd0309234c0 Bug: 68148948 --- media/libnblog/NBLog.cpp | 239 ++++++++++++++++----- media/libnblog/include/media/nblog/NBLog.h | 66 +++--- services/audioflinger/AudioFlinger.h | 1 + services/audioflinger/FastThread.cpp | 1 + services/audioflinger/TypedLogger.h | 5 + services/medialog/MediaLogService.cpp | 24 +-- services/medialog/MediaLogService.h | 2 +- 7 files changed, 234 insertions(+), 104 deletions(-) diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index bfc797c7b9..f5d2d6d427 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,9 @@ int NBLog::Entry::copyEntryDataAt(size_t offset) const /*static*/ std::unique_ptr NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) { - if (ptr == nullptr) return nullptr; + if (ptr == nullptr) { + return nullptr; + } const uint8_t type = EntryIterator(ptr)->type; switch (type) { case EVENT_START_FMT: @@ -139,10 +142,7 @@ int NBLog::FormatEntry::author() const ++it; // skip timestamp ++it; // skip hash // if there is an author entry, return it, return -1 otherwise - if (it->type == EVENT_AUTHOR) { - return it.payload(); - } - return -1; + return it->type == EVENT_AUTHOR ? it.payload() : -1; } NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( @@ -261,11 +261,8 @@ NBLog::log_hash_t NBLog::HistogramEntry::hash() const int NBLog::HistogramEntry::author() const { EntryIterator it(mEntry); - if (it->length == sizeof(HistTsEntryWithAuthor)) { - return it.payload().author; - } else { - return -1; - } + return it->length == sizeof(HistTsEntryWithAuthor) + ? it.payload().author : -1; } NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor( @@ -357,7 +354,9 @@ NBLog::Writer::~Writer() void NBLog::Writer::log(const char *string) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string"); size_t length = strlen(string); if (length > Entry::kMaxLength) { @@ -368,7 +367,9 @@ void NBLog::Writer::log(const char *string) void NBLog::Writer::logf(const char *fmt, ...) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } va_list ap; va_start(ap, fmt); Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter @@ -377,7 +378,9 @@ void NBLog::Writer::logf(const char *fmt, ...) void NBLog::Writer::logvf(const char *fmt, va_list ap) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } char buffer[Entry::kMaxLength + 1 /*NUL*/]; int length = vsnprintf(buffer, sizeof(buffer), fmt, ap); if (length >= (int) sizeof(buffer)) { @@ -392,7 +395,9 @@ void NBLog::Writer::logvf(const char *fmt, va_list ap) void NBLog::Writer::logTimestamp() { - if (!mEnabled) return; + if (!mEnabled) { + return; + } int64_t ts = get_monotonic_ns(); if (ts > 0) { log(EVENT_TIMESTAMP, &ts, sizeof(ts)); @@ -403,31 +408,41 @@ void NBLog::Writer::logTimestamp() void NBLog::Writer::logTimestamp(const int64_t ts) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } log(EVENT_TIMESTAMP, &ts, sizeof(ts)); } void NBLog::Writer::logInteger(const int x) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } log(EVENT_INTEGER, &x, sizeof(x)); } void NBLog::Writer::logFloat(const float x) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } log(EVENT_FLOAT, &x, sizeof(x)); } void NBLog::Writer::logPID() { - if (!mEnabled) return; + if (!mEnabled) { + return; + } log(EVENT_PID, mPidTag, mPidTagSize); } void NBLog::Writer::logStart(const char *fmt) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } size_t length = strlen(fmt); if (length > Entry::kMaxLength) { length = Entry::kMaxLength; @@ -437,20 +452,26 @@ void NBLog::Writer::logStart(const char *fmt) void NBLog::Writer::logEnd() { - if (!mEnabled) return; + if (!mEnabled) { + return; + } Entry entry = Entry(EVENT_END_FMT, NULL, 0); log(entry, true); } void NBLog::Writer::logHash(log_hash_t hash) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } log(EVENT_HASH, &hash, sizeof(hash)); } void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } HistTsEntry data; data.hash = hash; data.ts = get_monotonic_ns(); @@ -461,9 +482,19 @@ void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) } } +void NBLog::Writer::logMonotonicCycleTime(uint32_t monotonicNs) +{ + if (!mEnabled) { + return; + } + log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(&monotonicNs)); +} + void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } va_list ap; va_start(ap, hash); Writer::logVFormat(fmt, hash, ap); @@ -472,7 +503,9 @@ void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } Writer::logStart(fmt); int i; double f; @@ -528,7 +561,9 @@ void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) void NBLog::Writer::log(Event event, const void *data, size_t length) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } if (data == NULL || length > Entry::kMaxLength) { // TODO Perhaps it makes sense to display truncated data or at least a // message that the data is too long? The current behavior can create @@ -545,13 +580,15 @@ void NBLog::Writer::log(Event event, const void *data, size_t length) void NBLog::Writer::log(const NBLog::Entry &etr, bool trusted) { - if (!mEnabled) return; + if (!mEnabled) { + return; + } if (!trusted) { log(etr.mEvent, etr.mData, etr.mLength); return; } - size_t need = etr.mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength - // need = number of bytes written to FIFO + const size_t need = etr.mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength + // need = number of bytes written to FIFO // FIXME optimize this using memcpy for the data part of the Entry. // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy. @@ -676,15 +713,21 @@ bool NBLog::LockedWriter::setEnabled(bool enabled) // --------------------------------------------------------------------------- -const std::set NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT, +const std::unordered_set NBLog::Reader::startingTypes { + NBLog::Event::EVENT_START_FMT, NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, - NBLog::Event::EVENT_AUDIO_STATE}; -const std::set NBLog::Reader::endingTypes {NBLog::Event::EVENT_END_FMT, + NBLog::Event::EVENT_AUDIO_STATE, + NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME +}; +const std::unordered_set NBLog::Reader::endingTypes { + NBLog::Event::EVENT_END_FMT, NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, - NBLog::Event::EVENT_AUDIO_STATE}; + NBLog::Event::EVENT_AUDIO_STATE, + NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME +}; -NBLog::Reader::Reader(const void *shared, size_t size) - : mFd(-1), mIndent(0), mLost(0), +NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name) + : mFd(-1), mIndent(0), mLost(0), mName(name), mShared((/*const*/ Shared *) shared), /*mIMemory*/ mFifo(mShared != NULL ? new audio_utils_fifo(size, sizeof(uint8_t), @@ -693,8 +736,8 @@ NBLog::Reader::Reader(const void *shared, size_t size) { } -NBLog::Reader::Reader(const sp& iMemory, size_t size) - : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size) +NBLog::Reader::Reader(const sp& iMemory, size_t size, const std::string &name) + : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size, name) { mIMemory = iMemory; } @@ -706,7 +749,7 @@ NBLog::Reader::~Reader() } const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, - const std::set &types) { + const std::unordered_set &types) { while (back + Entry::kPreviousLengthOffset >= front) { const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead; if (prev < front || prev + prev[offsetof(entry, length)] + @@ -735,19 +778,38 @@ std::unique_ptr NBLog::Reader::getSnapshot() // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the // reader index. The index is incremented after handling corruption, to after the last complete // entry of the buffer - size_t lost; + size_t lost = 0; audio_utils_iovec iovec[2]; - const ssize_t availToRead = mFifoReader->obtain(iovec, mFifo->capacity(), - NULL /*timeout*/, &lost); + const size_t capacity = mFifo->capacity(); + ssize_t availToRead; + // A call to audio_utils_fifo_reader::obtain() places the read pointer one buffer length + // before the writer's pointer (since mFifoReader was constructed with flush=false). The + // do while loop is an attempt to read all of the FIFO's contents regardless of how behind + // the reader is with respect to the writer. However, the following scheduling sequence is + // possible and can lead to a starvation situation: + // - Writer T1 writes, overrun with respect to Reader T2 + // - T2 calls obtain() and gets EOVERFLOW, T2 ptr placed one buffer size behind T1 ptr + // - T1 write, overrun + // - T2 obtain(), EOVERFLOW (and so on...) + // To address this issue, we limit the number of tries for the reader to catch up with + // the writer. + int tries = 0; + size_t lostTemp; + do { + availToRead = mFifoReader->obtain(iovec, capacity, NULL /*timeout*/, &lostTemp); + lost += lostTemp; + } while (availToRead < 0 || ++tries <= kMaxObtainTries); + if (availToRead <= 0) { - return std::unique_ptr(new Snapshot()); + ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str()); + return std::make_unique(); } std::unique_ptr snapshot(new Snapshot(availToRead)); memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength); if (iovec[1].mLength > 0) { memcpy(snapshot->mData + (iovec[0].mLength), - (const char *) mFifo->buffer() + iovec[1].mOffset, iovec[1].mLength); + (const char *) mFifo->buffer() + iovec[1].mOffset, iovec[1].mLength); } // Handle corrupted buffer @@ -857,10 +919,60 @@ void NBLog::MergeReader::dump(int fd, int indent) ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); } +void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot) +{ + mFd = fd; + mIndent = indent; + String8 timestamp, body; + + // Range-based for loop isn't used here because handleFormat() returns an EntryIterator + // that points to the next entry (it handles all of the necessary operator++() calls). + for (auto entry = snapshot.begin(); entry != snapshot.end();) { + switch (entry->type) { + case EVENT_START_FMT: + entry = handleFormat(FormatEntry(entry), ×tamp, &body); + break; + case EVENT_HISTOGRAM_ENTRY_TS: + ++entry; + break; + case EVENT_AUDIO_STATE: + ++entry; + break; + case EVENT_END_FMT: + body.appendFormat("warning: got to end format event"); + ++entry; + break; + case EVENT_MONOTONIC_CYCLE_TIME: { + uint32_t monotonicNs = *(uint32_t *) (entry->data); + body.appendFormat("Thread cycle took %u ns", monotonicNs); + ++entry; + } break; + case EVENT_RESERVED: + default: + body.appendFormat("warning: unexpected event %d", entry->type); + ++entry; + break; + } + // FIXME: decide whether to print the warnings here or elsewhere + if (!body.isEmpty()) { + dumpLine(×tamp, &body); + } + } +} + +void NBLog::Reader::dump(int fd, size_t indent) +{ + // get a snapshot, dump it + std::unique_ptr snap = getSnapshot(); + dump(fd, indent, *snap); +} + // Writes a string to the console void NBLog::Reader::dumpLine(const String8 *timestamp, String8 *body) { - if (timestamp == nullptr || body == nullptr) return; + if (timestamp == nullptr || body == nullptr) { + return; + } if (mFd >= 0) { dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp->string(), body->string()); } else { @@ -878,7 +990,9 @@ bool NBLog::Reader::isIMemory(const sp& iMemory) const void NBLog::appendTimestamp(String8 *body, const void *data) { - if (body == nullptr || data == nullptr) return; + if (body == nullptr || data == nullptr) { + return; + } int64_t ts; memcpy(&ts, data, sizeof(ts)); body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)), @@ -887,14 +1001,18 @@ void NBLog::appendTimestamp(String8 *body, const void *data) void NBLog::appendInt(String8 *body, const void *data) { - if (body == nullptr || data == nullptr) return; + if (body == nullptr || data == nullptr) { + return; + } int x = *((int*) data); body->appendFormat("<%d>", x); } void NBLog::appendFloat(String8 *body, const void *data) { - if (body == nullptr || data == nullptr) return; + if (body == nullptr || data == nullptr) { + return; + } float f; memcpy(&f, data, sizeof(f)); body->appendFormat("<%f>", f); @@ -902,7 +1020,9 @@ void NBLog::appendFloat(String8 *body, const void *data) void NBLog::appendPID(String8 *body, const void* data, size_t length) { - if (body == nullptr || data == nullptr) return; + if (body == nullptr || data == nullptr) { + return; + } pid_t id = *((pid_t*) data); char * name = &((char*) data)[sizeof(pid_t)]; body->appendFormat("", id, (int) (length - sizeof(pid_t)), name); @@ -911,7 +1031,9 @@ void NBLog::appendPID(String8 *body, const void* data, size_t length) String8 NBLog::bufferDump(const uint8_t *buffer, size_t size) { String8 str; - if (buffer == nullptr) return str; + if (buffer == nullptr) { + return str; + } str.append("[ "); for(size_t i = 0; i < size; i++) { str.appendFormat("%d ", buffer[i]); @@ -1026,11 +1148,11 @@ NBLog::Merger::Merger(const void *shared, size_t size): { } -void NBLog::Merger::addReader(const NBLog::NamedReader &reader) +void NBLog::Merger::addReader(const sp &reader) { // FIXME This is called by binder thread in MediaLogService::registerWriter - // but the access to shared variable mNamedReaders is not yet protected by a lock. - mNamedReaders.push_back(reader); + // but the access to shared variable mReaders is not yet protected by a lock. + mReaders.push_back(reader); } // items placed in priority queue during merge @@ -1052,12 +1174,12 @@ void NBLog::Merger::merge() { // FIXME This is called by merge thread // but the access to shared variable mNamedReaders is not yet protected by a lock. - const int nLogs = mNamedReaders.size(); + const int nLogs = mReaders.size(); std::vector> snapshots(nLogs); std::vector offsets; offsets.reserve(nLogs); for (int i = 0; i < nLogs; ++i) { - snapshots[i] = mNamedReaders[i].reader()->getSnapshot(); + snapshots[i] = mReaders[i]->getSnapshot(); offsets.push_back(snapshots[i]->begin()); } // initialize offsets @@ -1089,16 +1211,16 @@ void NBLog::Merger::merge() } } -const std::vector& NBLog::Merger::getNamedReaders() const +const std::vector>& NBLog::Merger::getReaders() const { - // FIXME This is returning a reference to a shared variable that needs a lock - return mNamedReaders; + //AutoMutex _l(mLock); + return mReaders; } // --------------------------------------------------------------------------- NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger) - : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) + : Reader(shared, size, "MergeReader"), mReaders(merger.getReaders()) { } @@ -1109,7 +1231,7 @@ void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 return; } // FIXME Needs a lock - const char* name = mNamedReaders[author].name(); + const char* name = mReaders[author]->name().c_str(); body->appendFormat("%s: ", name); } @@ -1142,6 +1264,7 @@ bool NBLog::MergeThread::threadLoop() doMerge = mTimeoutUs > 0; mTimeoutUs -= kThreadSleepPeriodUs; } + doMerge = false; // Disable merging for now. if (doMerge) { // Merge data from all the readers mMerger.merge(); diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index bee3ad3898..71941a4968 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -64,6 +64,12 @@ public: EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call EVENT_END_FMT, // end of logFormat argument list + // Types representing audio performance metrics + EVENT_LATENCY, // TODO classify specifically what this is + EVENT_CPU_FREQUENCY, // instantaneous CPU frequency in kHz + EVENT_MONOTONIC_CYCLE_TIME, // thread per-cycle monotonic time + EVENT_CPU_CYCLE_TIME, // thread per-cycle cpu time + EVENT_UPPER_BOUND, // to check for invalid events }; @@ -200,7 +206,6 @@ private: // copy entry, adding author before timestamp, returns size of original entry virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, int author) const override; - }; class HistogramEntry : public AbstractEntry { @@ -217,13 +222,13 @@ private: virtual EntryIterator copyWithAuthor(std::unique_ptr &dst, int author) const override; - }; // --------------------------------------------------------------------------- // representation of a single log entry in private memory - struct Entry { + class Entry { + public: Entry(Event event, const void *data, size_t length) : mEvent(event), mLength(length), mData(data) { } /*virtual*/ ~Entry() { } @@ -345,12 +350,15 @@ public: virtual void logInteger(const int x); virtual void logFloat(const float x); virtual void logPID(); - virtual void logFormat(const char *fmt, log_hash_t hash, ...); - virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); virtual void logStart(const char *fmt); virtual void logEnd(); virtual void logHash(log_hash_t hash); + // The functions below are not in LockedWriter yet. + virtual void logFormat(const char *fmt, log_hash_t hash, ...); + virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); virtual void logEventHistTs(Event event, log_hash_t hash); + virtual void logMonotonicCycleTime(uint32_t monotonicNs); + // End of functions that are not in LockedWriter yet. virtual bool isEnabled() const; @@ -447,16 +455,22 @@ public: // Input parameter 'size' is the desired size of the timeline in byte units. // The size of the shared memory must be at least Timeline::sharedSize(size). - Reader(const void *shared, size_t size); - Reader(const sp& iMemory, size_t size); + Reader(const void *shared, size_t size, const std::string &name); + Reader(const sp& iMemory, size_t size, const std::string &name); virtual ~Reader(); // get snapshot of readers fifo buffer, effectively consuming the buffer std::unique_ptr getSnapshot(); + // dump a particular snapshot of the reader + void dump(int fd, size_t indent, Snapshot &snap); + // dump the current content of the reader's buffer (call getSnapshot() and previous dump()) + void dump(int fd, size_t indent = 0); bool isIMemory(const sp& iMemory) const; + const std::string &name() const { return mName; } + protected: // print a summary of the performance to the console void dumpLine(const String8 *timestamp, String8 *body); @@ -466,10 +480,12 @@ public: int mFd; // file descriptor int mIndent; // indentation level int mLost; // bytes of data lost before buffer was read + const std::string mName; // name of reader (actually name of writer) + static constexpr int kMaxObtainTries = 3; private: - static const std::set startingTypes; - static const std::set endingTypes; + static const std::unordered_set startingTypes; + static const std::unordered_set endingTypes; // declared as const because audio_utils_fifo() constructor sp mIMemory; // ref-counted version, assigned only in constructor @@ -483,29 +499,12 @@ public: // Searches for the last entry of type in the range [front, back) // back has to be entry-aligned. Returns nullptr if none enconuntered. static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, - const std::set &types); + const std::unordered_set &types); // dummy method for handling absent author entry virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} }; - // Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name - class NamedReader { - public: - NamedReader() { mName[0] = '\0'; } // for Vector - NamedReader(const sp& reader, const char *name) : - mReader(reader) - { strlcpy(mName, name, sizeof(mName)); } - ~NamedReader() { } - const sp& reader() const { return mReader; } - const char* name() const { return mName; } - - private: - sp mReader; - static const size_t kMaxName = 32; - char mName[kMaxName]; - }; - // --------------------------------------------------------------------------- // This class is used to read data from each thread's individual FIFO in shared memory @@ -516,18 +515,18 @@ public: virtual ~Merger() {} - void addReader(const NamedReader &reader); + void addReader(const sp &reader); // TODO add removeReader void merge(); // FIXME This is returning a reference to a shared variable that needs a lock - const std::vector& getNamedReaders() const; + const std::vector>& getReaders() const; private: // vector of the readers the merger is supposed to merge from. // every reader reads from a writer's buffer // FIXME Needs to be protected by a lock - std::vector mNamedReaders; + std::vector> mReaders; Shared * const mShared; // raw pointer to shared memory std::unique_ptr mFifo; // FIFO itself @@ -535,7 +534,7 @@ public: }; // This class has a pointer to the FIFO in local memory which stores the merged - // data collected by NBLog::Merger from all NamedReaders. It is used to process + // data collected by NBLog::Merger from all Readers. It is used to process // this data and write the result to PerformanceAnalysis. class MergeReader : public Reader { public: @@ -550,7 +549,8 @@ public: private: // FIXME Needs to be protected by a lock, // because even though our use of it is read-only there may be asynchronous updates - const std::vector& mNamedReaders; + // The object is owned by the Merger class. + const std::vector>& mReaders; // analyzes, compresses and stores the merged data // contains a separate instance for every author (thread), and for every source file diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index e9e6e944c7..2d2da3e222 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index dc15487a31..e5870269d0 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -339,6 +339,7 @@ bool FastThread::threadLoop() // these stores #1, #2, #3 are not atomic with respect to each other, // or with respect to store #4 below mDumpState->mMonotonicNs[i] = monotonicNs; + LOG_MONOTONIC_CYCLE_TIME(monotonicNs); mDumpState->mLoadNs[i] = loadNs; #ifdef CPU_FREQUENCY_STATISTICS mDumpState->mCpukHz[i] = kHz; diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h index 38c3c02f76..0fea42aa40 100644 --- a/services/audioflinger/TypedLogger.h +++ b/services/audioflinger/TypedLogger.h @@ -97,6 +97,11 @@ constexpr uint64_t hash(const char (&file)[n], uint32_t line) { #define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0) +// Record a typed entry that represents a thread's cycle time in nanoseconds. +// Parameter ns should be of type uint32_t. +#define LOG_MONOTONIC_CYCLE_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->logMonotonicCycleTime(ns); } while (0) + namespace android { extern "C" { extern thread_local NBLog::Writer *tlNBLogWriter; diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index e58dff7159..095dd5c77f 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -58,11 +58,10 @@ void MediaLogService::registerWriter(const sp& shared, size_t size, con shared->size() < NBLog::Timeline::sharedSize(size)) { return; } - sp reader(new NBLog::Reader(shared, size)); - NBLog::NamedReader namedReader(reader, name); + sp reader(new NBLog::Reader(shared, size, name)); Mutex::Autolock _l(mLock); - mNamedReaders.add(namedReader); - mMerger.addReader(namedReader); + mReaders.add(reader); + mMerger.addReader(reader); } void MediaLogService::unregisterWriter(const sp& shared) @@ -71,9 +70,9 @@ void MediaLogService::unregisterWriter(const sp& shared) return; } Mutex::Autolock _l(mLock); - for (size_t i = 0; i < mNamedReaders.size(); ) { - if (mNamedReaders[i].reader()->isIMemory(shared)) { - mNamedReaders.removeAt(i); + for (size_t i = 0; i < mReaders.size(); ) { + if (mReaders[i]->isIMemory(shared)) { + mReaders.removeAt(i); } else { i++; } @@ -106,7 +105,7 @@ status_t MediaLogService::dump(int fd, const Vector& args __unused) if (args.size() > 0) { const String8 arg0(args[0]); if (!strcmp(arg0.string(), "-r")) { - // needed because mNamedReaders is protected by mLock + // needed because mReaders is protected by mLock bool locked = dumpTryLock(mLock); // failed to lock - MediaLogService is probably deadlocked @@ -121,17 +120,18 @@ status_t MediaLogService::dump(int fd, const Vector& args __unused) return NO_ERROR; } - for (const auto& namedReader : mNamedReaders) { + for (auto reader : mReaders) { if (fd >= 0) { - dprintf(fd, "\n%s:\n", namedReader.name()); + dprintf(fd, "\n%s:\n", reader->name().c_str()); + reader->dump(fd, 0 /*indent*/); } else { - ALOGI("%s:", namedReader.name()); + ALOGI("%s:", reader->name().c_str()); } } mLock.unlock(); } } - mMergeReader.dump(fd); + //mMergeReader.dump(fd); return NO_ERROR; } diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h index c945d1f2b0..3763484257 100644 --- a/services/medialog/MediaLogService.h +++ b/services/medialog/MediaLogService.h @@ -55,7 +55,7 @@ private: Mutex mLock; - Vector mNamedReaders; // protected by mLock + Vector> mReaders; // protected by mLock // FIXME Need comments on all of these, especially about locking NBLog::Shared *mMergerShared; -- GitLab From df79be32cb412910717b70e4d333711421702a7d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 8 Aug 2018 17:42:44 -0700 Subject: [PATCH 0211/1530] Choose color conversion matrix for color space When doing the YUV->RGB conversion in image/video frame decoder, choose the libyuv function that uses the color matrix that matches the source color space. bug: 109762970 Test: - bitmap decoding of HEIF images; - video thumbnail extraction in Photos app, observe color of thumbnail vs video playback Change-Id: Id11cb192a45bb2a5c3944ba7f99f599ca851b0d2 --- media/libstagefright/FrameDecoder.cpp | 24 ++++++++ .../colorconversion/ColorConverter.cpp | 61 ++++++++++++++++--- .../media/stagefright/ColorConverter.h | 12 ++++ 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp index 3370df1d3e..c0e65e811c 100644 --- a/media/libstagefright/FrameDecoder.cpp +++ b/media/libstagefright/FrameDecoder.cpp @@ -528,6 +528,18 @@ status_t VideoFrameDecoder::onOutputReceived( ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat()); + uint32_t standard, range, transfer; + if (!outputFormat->findInt32("color-standard", (int32_t*)&standard)) { + standard = 0; + } + if (!outputFormat->findInt32("color-range", (int32_t*)&range)) { + range = 0; + } + if (!outputFormat->findInt32("color-transfer", (int32_t*)&transfer)) { + transfer = 0; + } + converter.setSrcColorSpace(standard, range, transfer); + if (converter.isValid()) { converter.convert( (const uint8_t *)videoFrameBuffer->data(), @@ -699,6 +711,18 @@ status_t ImageDecoder::onOutputReceived( ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat()); + uint32_t standard, range, transfer; + if (!outputFormat->findInt32("color-standard", (int32_t*)&standard)) { + standard = 0; + } + if (!outputFormat->findInt32("color-range", (int32_t*)&range)) { + range = 0; + } + if (!outputFormat->findInt32("color-transfer", (int32_t*)&transfer)) { + transfer = 0; + } + converter.setSrcColorSpace(standard, range, transfer); + int32_t dstLeft, dstTop, dstRight, dstBottom; dstLeft = mTilesDecoded % mGridCols * width; dstTop = mTilesDecoded / mGridCols * height; diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index c46a40f0b7..862cc63aa3 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -20,10 +20,12 @@ #include #include +#include #include #include #include "libyuv/convert_from.h" +#include "libyuv/convert_argb.h" #include "libyuv/video_common.h" #include #include @@ -44,10 +46,28 @@ namespace android { +static bool isRGB(OMX_COLOR_FORMATTYPE colorFormat) { + return colorFormat == OMX_COLOR_Format16bitRGB565 + || colorFormat == OMX_COLOR_Format32BitRGBA8888 + || colorFormat == OMX_COLOR_Format32bitBGRA8888; +} + +bool ColorConverter::ColorSpace::isBt709() { + return (mStandard == ColorUtils::kColorStandardBT709); +} + + +bool ColorConverter::ColorSpace::isJpeg() { + return ((mStandard == ColorUtils::kColorStandardBT601_625) + || (mStandard == ColorUtils::kColorStandardBT601_525)) + && (mRange == ColorUtils::kColorRangeFull); +} + ColorConverter::ColorConverter( OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to) : mSrcFormat(from), mDstFormat(to), + mSrcColorSpace({0, 0, 0}), mClip(NULL) { } @@ -80,9 +100,18 @@ bool ColorConverter::isValid() const { } bool ColorConverter::isDstRGB() const { - return mDstFormat == OMX_COLOR_Format16bitRGB565 - || mDstFormat == OMX_COLOR_Format32BitRGBA8888 - || mDstFormat == OMX_COLOR_Format32bitBGRA8888; + return isRGB(mDstFormat); +} + +void ColorConverter::setSrcColorSpace( + uint32_t standard, uint32_t range, uint32_t transfer) { + if (isRGB(mSrcFormat)) { + ALOGW("Can't set color space on RGB source"); + return; + } + mSrcColorSpace.mStandard = standard; + mSrcColorSpace.mRange = range; + mSrcColorSpace.mTransfer = transfer; } /* @@ -281,6 +310,13 @@ status_t ColorConverter::convertCbYCrY( return OK; } +#define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)( \ + const uint8*, int, const uint8*, int, \ + const uint8*, int, uint8*, int, int, int) \ + = mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \ + : mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb \ + : libyuv::I420To##rgb + status_t ColorConverter::convertYUV420PlanarUseLibYUV( const BitmapParams &src, const BitmapParams &dst) { uint8_t *dst_ptr = (uint8_t *)dst.mBits @@ -298,19 +334,28 @@ status_t ColorConverter::convertYUV420PlanarUseLibYUV( switch (mDstFormat) { case OMX_COLOR_Format16bitRGB565: - libyuv::I420ToRGB565(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, + { + DECLARE_YUV2RGBFUNC(func, RGB565); + (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight()); break; + } case OMX_COLOR_Format32BitRGBA8888: - libyuv::ConvertFromI420(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, - (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight(), libyuv::FOURCC_ABGR); + { + DECLARE_YUV2RGBFUNC(func, ABGR); + (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, + (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight()); break; + } case OMX_COLOR_Format32bitBGRA8888: - libyuv::ConvertFromI420(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, - (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight(), libyuv::FOURCC_ARGB); + { + DECLARE_YUV2RGBFUNC(func, ARGB); + (*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2, + (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight()); break; + } default: return ERROR_UNSUPPORTED; diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h index 2b8c7c84d8..6d4c1bfa1b 100644 --- a/media/libstagefright/include/media/stagefright/ColorConverter.h +++ b/media/libstagefright/include/media/stagefright/ColorConverter.h @@ -35,6 +35,8 @@ struct ColorConverter { bool isDstRGB() const; + void setSrcColorSpace(uint32_t standard, uint32_t range, uint32_t transfer); + status_t convert( const void *srcBits, size_t srcWidth, size_t srcHeight, size_t srcStride, @@ -46,6 +48,15 @@ struct ColorConverter { size_t dstCropRight, size_t dstCropBottom); private: + struct ColorSpace { + uint32_t mStandard; + uint32_t mRange; + uint32_t mTransfer; + + bool isBt709(); + bool isJpeg(); + }; + struct BitmapParams { BitmapParams( void *bits, @@ -65,6 +76,7 @@ private: }; OMX_COLOR_FORMATTYPE mSrcFormat, mDstFormat; + ColorSpace mSrcColorSpace; uint8_t *mClip; uint8_t *initClip(); -- GitLab From dd045bf3607fe6ab5881872c484241627731cc82 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Mon, 20 Aug 2018 12:39:19 -0700 Subject: [PATCH 0212/1530] Camera NDK: don't explicit delete ACameraMetadata To fix the warning in RefBase destructor. Test: NDK CTS tests, and ARCore measure app. Bug: 112775599 Change-Id: Ia448ac6d7e6c17699c16d287f65fc2826c9d7578 --- camera/ndk/NdkCameraManager.cpp | 10 +++++++++- camera/ndk/NdkCameraMetadata.cpp | 6 ++++-- camera/ndk/NdkCaptureRequest.cpp | 2 +- camera/ndk/impl/ACameraDevice.cpp | 6 +++--- camera/ndk/impl/ACameraDevice.h | 8 ++++---- camera/ndk/impl/ACameraManager.cpp | 10 ++++------ camera/ndk/impl/ACameraManager.h | 2 +- camera/ndk/impl/ACaptureRequest.h | 2 +- 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp index 60b4763d85..f14a4c9757 100644 --- a/camera/ndk/NdkCameraManager.cpp +++ b/camera/ndk/NdkCameraManager.cpp @@ -23,6 +23,7 @@ #include #include "impl/ACameraManager.h" +#include "impl/ACameraMetadata.h" using namespace android; @@ -107,7 +108,14 @@ camera_status_t ACameraManager_getCameraCharacteristics( __FUNCTION__, mgr, cameraId, chars); return ACAMERA_ERROR_INVALID_PARAMETER; } - return mgr->getCameraCharacteristics(cameraId, chars); + sp spChars; + camera_status_t status = mgr->getCameraCharacteristics(cameraId, &spChars); + if (status != ACAMERA_OK) { + return status; + } + spChars->incStrong((void*) ACameraManager_getCameraCharacteristics); + *chars = spChars.get(); + return ACAMERA_OK; } EXPORT diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp index 65de81ff81..34ec2da100 100644 --- a/camera/ndk/NdkCameraMetadata.cpp +++ b/camera/ndk/NdkCameraMetadata.cpp @@ -57,13 +57,15 @@ ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src) { ALOGE("%s: src is null!", __FUNCTION__); return nullptr; } - return new ACameraMetadata(*src); + ACameraMetadata* copy = new ACameraMetadata(*src); + copy->incStrong((void*) ACameraMetadata_copy); + return copy; } EXPORT void ACameraMetadata_free(ACameraMetadata* metadata) { ATRACE_CALL(); if (metadata != nullptr) { - delete metadata; + metadata->decStrong((void*) ACameraMetadata_free); } } diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp index ac1856b6f1..ddb69d77ac 100644 --- a/camera/ndk/NdkCaptureRequest.cpp +++ b/camera/ndk/NdkCaptureRequest.cpp @@ -137,7 +137,7 @@ void ACaptureRequest_free(ACaptureRequest* request) { if (request == nullptr) { return; } - delete request->settings; + request->settings.clear(); delete request->targets; delete request; return; diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 907debc96e..c908323e08 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -50,11 +50,11 @@ const char* CameraDevice::kAnwKey = "Anw"; CameraDevice::CameraDevice( const char* id, ACameraDevice_StateCallbacks* cb, - std::unique_ptr chars, + sp chars, ACameraDevice* wrapper) : mCameraId(id), mAppCallbacks(*cb), - mChars(std::move(chars)), + mChars(chars), mServiceCallback(new ServiceCallback(this)), mWrapper(wrapper), mInError(false), @@ -436,7 +436,7 @@ CameraDevice::freeACaptureRequest(ACaptureRequest* req) { if (req == nullptr) { return; } - delete req->settings; + req->settings.clear(); delete req->targets; delete req; } diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 136914823a..7d64081ae1 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -48,7 +48,7 @@ struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {}; class CameraDevice final : public RefBase { public: CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, - std::unique_ptr chars, + sp chars, ACameraDevice* wrapper); ~CameraDevice(); @@ -156,7 +156,7 @@ class CameraDevice final : public RefBase { mutable Mutex mDeviceLock; const String8 mCameraId; // Camera ID const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app - const std::unique_ptr mChars; // Camera characteristics + const sp mChars; // Camera characteristics const sp mServiceCallback; ACameraDevice* mWrapper; @@ -294,8 +294,8 @@ class CameraDevice final : public RefBase { */ struct ACameraDevice { ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, - std::unique_ptr chars) : - mDevice(new CameraDevice(id, cb, std::move(chars), this)) {} + sp chars) : + mDevice(new CameraDevice(id, cb, chars, this)) {} ~ACameraDevice() {}; diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index c59d0e7cf6..ee67677e9b 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -402,7 +402,7 @@ ACameraManager::deleteCameraIdList(ACameraIdList* cameraIdList) { } camera_status_t ACameraManager::getCameraCharacteristics( - const char *cameraIdStr, ACameraMetadata **characteristics) { + const char* cameraIdStr, sp* characteristics) { Mutex::Autolock _l(mLock); sp cs = CameraManagerGlobal::getInstance().getCameraService(); @@ -437,18 +437,16 @@ ACameraManager::openCamera( const char* cameraId, ACameraDevice_StateCallbacks* callback, /*out*/ACameraDevice** outDevice) { - ACameraMetadata* rawChars; - camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars); + sp chars; + camera_status_t ret = getCameraCharacteristics(cameraId, &chars); Mutex::Autolock _l(mLock); if (ret != ACAMERA_OK) { ALOGE("%s: cannot get camera characteristics for camera %s. err %d", __FUNCTION__, cameraId, ret); return ACAMERA_ERROR_INVALID_PARAMETER; } - std::unique_ptr chars(rawChars); - rawChars = nullptr; - ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(chars)); + ACameraDevice* device = new ACameraDevice(cameraId, callback, chars); sp cs = CameraManagerGlobal::getInstance().getCameraService(); if (cs == nullptr) { diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index cc42f77c2c..ce65769cc8 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -186,7 +186,7 @@ struct ACameraManager { static void deleteCameraIdList(ACameraIdList* cameraIdList); camera_status_t getCameraCharacteristics( - const char *cameraId, ACameraMetadata **characteristics); + const char* cameraId, android::sp* characteristics); camera_status_t openCamera(const char* cameraId, ACameraDevice_StateCallbacks* callback, /*out*/ACameraDevice** device); diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h index 06b2cc3c27..b11dafb093 100644 --- a/camera/ndk/impl/ACaptureRequest.h +++ b/camera/ndk/impl/ACaptureRequest.h @@ -55,7 +55,7 @@ struct ACaptureRequest { return ACAMERA_OK; } - ACameraMetadata* settings; + sp settings; ACameraOutputTargets* targets; void* context; }; -- GitLab From 6af1847b8ea55d9f21e75b0f0ed91dc0e8f68c32 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Mon, 20 Aug 2018 09:27:50 -0700 Subject: [PATCH 0213/1530] NBLog: Make separate readers for dumping logs and histograms Prior to this commit, the dumping of log contents and histograms from PerformanceAnalysis were in conflict because histograms obtained data from the merger's buffer, and the merger used the same readers as the log dumps. One read would consume the whole buffer, which would cause either a lossy dump or an lossy merge. Now, both operations can exist without loss of information from sharing the same reader. The step of merging contents of each writer's local buffer into a bigger buffer has effectively been removed. PerformanceAnalysis now directly reads from each writer's buffer instead of reading from the merger's bigger buffer. Test: dumpsys media.log -r Bug: 68148948 Change-Id: I6d8ea6a8f6a43555183a6d8f17af567506a102f1 --- media/libnblog/NBLog.cpp | 168 ++++++++------------- media/libnblog/include/media/nblog/NBLog.h | 152 +++++++++---------- services/medialog/MediaLogService.cpp | 22 +-- services/medialog/MediaLogService.h | 2 +- 4 files changed, 147 insertions(+), 197 deletions(-) diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index f5d2d6d427..d659445d0f 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -20,11 +20,7 @@ #include #include -#include -#include -#include #include -#include #include #include #include @@ -727,7 +723,7 @@ const std::unordered_set NBLog::Reader::endingTypes { }; NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name) - : mFd(-1), mIndent(0), mLost(0), mName(name), + : mName(name), mShared((/*const*/ Shared *) shared), /*mIMemory*/ mFifo(mShared != NULL ? new audio_utils_fifo(size, sizeof(uint8_t), @@ -769,10 +765,10 @@ const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const u // Copies content of a Reader FIFO into its Snapshot // The Snapshot has the same raw data, but represented as a sequence of entries // and an EntryIterator making it possible to process the data. -std::unique_ptr NBLog::Reader::getSnapshot() +std::unique_ptr NBLog::Reader::getSnapshot() { if (mFifoReader == NULL) { - return std::unique_ptr(new Snapshot()); + return std::make_unique(); } // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the @@ -802,7 +798,7 @@ std::unique_ptr NBLog::Reader::getSnapshot() if (availToRead <= 0) { ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str()); - return std::make_unique(); + return std::make_unique(); } std::unique_ptr snapshot(new Snapshot(availToRead)); @@ -853,17 +849,12 @@ std::unique_ptr NBLog::Reader::getSnapshot() // Takes raw content of the local merger FIFO, processes log entries, and // writes the data to a map of class PerformanceAnalysis, based on their thread ID. -void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot) +void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author) { - String8 timestamp, body; - - for (auto entry = snapshot.begin(); entry != snapshot.end();) { - switch (entry->type) { - case EVENT_START_FMT: - entry = handleFormat(FormatEntry(entry), ×tamp, &body); - break; + for (const entry &etr : snapshot) { + switch (etr.type) { case EVENT_HISTOGRAM_ENTRY_TS: { - HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data); + HistTsEntry *data = (HistTsEntry *) (etr.data); // TODO This memcpies are here to avoid unaligned memory access crash. // There's probably a more efficient way to do it log_hash_t hash; @@ -872,45 +863,41 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Reader::Snapshot &snapshot memcpy(&ts, &data->ts, sizeof(ts)); // TODO: hash for histogram ts and audio state need to match // and correspond to audio production source file location - mThreadPerformanceAnalysis[data->author][0 /*hash*/].logTsEntry(ts); - ++entry; - break; - } + mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(ts); + } break; case EVENT_AUDIO_STATE: { - HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data); + HistTsEntry *data = (HistTsEntry *) (etr.data); // TODO This memcpies are here to avoid unaligned memory access crash. // There's probably a more efficient way to do it log_hash_t hash; memcpy(&hash, &(data->hash), sizeof(hash)); - // TODO: remove ts if unused - int64_t ts; - memcpy(&ts, &data->ts, sizeof(ts)); - mThreadPerformanceAnalysis[data->author][0 /*hash*/].handleStateChange(); - ++entry; - break; - } + mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange(); + } break; case EVENT_END_FMT: - body.appendFormat("warning: got to end format event"); - ++entry; - break; case EVENT_RESERVED: + case EVENT_UPPER_BOUND: + ALOGW("warning: unexpected event %d", etr.type); default: - body.appendFormat("warning: unexpected event %d", entry->type); - ++entry; break; } } - // FIXME: decide whether to print the warnings here or elsewhere - if (!body.isEmpty()) { - dumpLine(×tamp, &body); - } } void NBLog::MergeReader::getAndProcessSnapshot() { - // get a snapshot, process it - std::unique_ptr snap = getSnapshot(); - getAndProcessSnapshot(*snap); + // get a snapshot of each reader and process them + // TODO insert lock here + const size_t nLogs = mReaders.size(); + std::vector> snapshots(nLogs); + for (size_t i = 0; i < nLogs; i++) { + snapshots[i] = mReaders[i]->getSnapshot(); + } + // TODO unlock lock here + for (size_t i = 0; i < nLogs; i++) { + if (snapshots[i] != nullptr) { + getAndProcessSnapshot(*(snapshots[i]), i); + } + } } void NBLog::MergeReader::dump(int fd, int indent) @@ -919,76 +906,49 @@ void NBLog::MergeReader::dump(int fd, int indent) ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); } -void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot) +// TODO for future compatibility, would prefer to have a dump() go to string, and then go +// to fd only when invoked through binder. +void NBLog::DumpReader::dump(int fd, size_t indent) { - mFd = fd; - mIndent = indent; + if (fd < 0) return; + std::unique_ptr snapshot = getSnapshot(); + if (snapshot == nullptr) { + return; + } String8 timestamp, body; - // Range-based for loop isn't used here because handleFormat() returns an EntryIterator - // that points to the next entry (it handles all of the necessary operator++() calls). - for (auto entry = snapshot.begin(); entry != snapshot.end();) { - switch (entry->type) { + // TODO all logged types should have a printable format. + for (auto it = snapshot->begin(); it != snapshot->end(); ++it) { + switch (it->type) { case EVENT_START_FMT: - entry = handleFormat(FormatEntry(entry), ×tamp, &body); - break; - case EVENT_HISTOGRAM_ENTRY_TS: - ++entry; - break; - case EVENT_AUDIO_STATE: - ++entry; - break; - case EVENT_END_FMT: - body.appendFormat("warning: got to end format event"); - ++entry; + it = handleFormat(FormatEntry(it), ×tamp, &body); break; case EVENT_MONOTONIC_CYCLE_TIME: { - uint32_t monotonicNs = *(uint32_t *) (entry->data); + uint32_t monotonicNs; + memcpy(&monotonicNs, it->data, sizeof(monotonicNs)); body.appendFormat("Thread cycle took %u ns", monotonicNs); - ++entry; } break; + case EVENT_END_FMT: case EVENT_RESERVED: + case EVENT_UPPER_BOUND: + body.appendFormat("warning: unexpected event %d", it->type); default: - body.appendFormat("warning: unexpected event %d", entry->type); - ++entry; break; } - // FIXME: decide whether to print the warnings here or elsewhere if (!body.isEmpty()) { - dumpLine(×tamp, &body); + dprintf(fd, "%.*s%s %s\n", (int)indent, "", timestamp.string(), body.string()); + body.clear(); } + timestamp.clear(); } } -void NBLog::Reader::dump(int fd, size_t indent) -{ - // get a snapshot, dump it - std::unique_ptr snap = getSnapshot(); - dump(fd, indent, *snap); -} - -// Writes a string to the console -void NBLog::Reader::dumpLine(const String8 *timestamp, String8 *body) -{ - if (timestamp == nullptr || body == nullptr) { - return; - } - if (mFd >= 0) { - dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp->string(), body->string()); - } else { - ALOGI("%.*s%s %s", mIndent, "", timestamp->string(), body->string()); - } - body->clear(); -} - bool NBLog::Reader::isIMemory(const sp& iMemory) const { return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer(); } -// --------------------------------------------------------------------------- - -void NBLog::appendTimestamp(String8 *body, const void *data) +void NBLog::DumpReader::appendTimestamp(String8 *body, const void *data) { if (body == nullptr || data == nullptr) { return; @@ -999,7 +959,7 @@ void NBLog::appendTimestamp(String8 *body, const void *data) (int) ((ts / (1000 * 1000)) % 1000)); } -void NBLog::appendInt(String8 *body, const void *data) +void NBLog::DumpReader::appendInt(String8 *body, const void *data) { if (body == nullptr || data == nullptr) { return; @@ -1008,7 +968,7 @@ void NBLog::appendInt(String8 *body, const void *data) body->appendFormat("<%d>", x); } -void NBLog::appendFloat(String8 *body, const void *data) +void NBLog::DumpReader::appendFloat(String8 *body, const void *data) { if (body == nullptr || data == nullptr) { return; @@ -1018,7 +978,7 @@ void NBLog::appendFloat(String8 *body, const void *data) body->appendFormat("<%f>", f); } -void NBLog::appendPID(String8 *body, const void* data, size_t length) +void NBLog::DumpReader::appendPID(String8 *body, const void* data, size_t length) { if (body == nullptr || data == nullptr) { return; @@ -1028,7 +988,7 @@ void NBLog::appendPID(String8 *body, const void* data, size_t length) body->appendFormat("", id, (int) (length - sizeof(pid_t)), name); } -String8 NBLog::bufferDump(const uint8_t *buffer, size_t size) +String8 NBLog::DumpReader::bufferDump(const uint8_t *buffer, size_t size) { String8 str; if (buffer == nullptr) { @@ -1042,14 +1002,14 @@ String8 NBLog::bufferDump(const uint8_t *buffer, size_t size) return str; } -String8 NBLog::bufferDump(const EntryIterator &it) +String8 NBLog::DumpReader::bufferDump(const EntryIterator &it) { return bufferDump(it, it->length + Entry::kOverhead); } -NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, - String8 *timestamp, - String8 *body) +NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry, + String8 *timestamp, + String8 *body) { // log timestamp int64_t ts = fmtEntry.timestamp(); @@ -1135,7 +1095,6 @@ NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, ++arg; } ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type); - ++arg; return arg; } @@ -1172,10 +1131,10 @@ bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) // Merge registered readers, sorted by timestamp, and write data to a single FIFO in local memory void NBLog::Merger::merge() { - // FIXME This is called by merge thread - // but the access to shared variable mNamedReaders is not yet protected by a lock. + if (true) return; // Merging is not necessary at the moment, so this is to disable it + // and bypass compiler warnings about member variables not being used. const int nLogs = mReaders.size(); - std::vector> snapshots(nLogs); + std::vector> snapshots(nLogs); std::vector offsets; offsets.reserve(nLogs); for (int i = 0; i < nLogs; ++i) { @@ -1258,13 +1217,12 @@ bool NBLog::MergeThread::threadLoop() { AutoMutex _l(mMutex); // If mTimeoutUs is negative, wait on the condition variable until it's positive. - // If it's positive, wait kThreadSleepPeriodUs and then merge - nsecs_t waitTime = mTimeoutUs > 0 ? kThreadSleepPeriodUs * 1000 : LLONG_MAX; - mCond.waitRelative(mMutex, waitTime); + // If it's positive, merge. The minimum period between waking the condition variable + // is handled in AudioFlinger::MediaLogNotifier::threadLoop(). + mCond.wait(mMutex); doMerge = mTimeoutUs > 0; mTimeoutUs -= kThreadSleepPeriodUs; } - doMerge = false; // Disable merging for now. if (doMerge) { // Merge data from all the readers mMerger.merge(); diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index 71941a4968..c9bfaae746 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -19,7 +19,6 @@ #ifndef ANDROID_MEDIA_NBLOG_H #define ANDROID_MEDIA_NBLOG_H -#include #include #include #include @@ -76,17 +75,6 @@ public: private: // --------------------------------------------------------------------------- - // API for handling format entry operations - - // a formatted entry has the following structure: - // * START_FMT entry, containing the format string - // * TIMESTAMP entry - // * HASH entry - // * author entry of the thread that generated it (optional, present in merged log) - // * format arg1 - // * format arg2 - // * ... - // * END_FMT entry // entry representation in memory struct entry { @@ -144,6 +132,9 @@ private: const uint8_t *mPtr; // Should not be nullptr except for dummy initialization }; + // --------------------------------------------------------------------------- + // The following classes are used for merging into the Merger's buffer. + class AbstractEntry { public: virtual ~AbstractEntry() {} @@ -175,6 +166,17 @@ private: const uint8_t *mEntry; }; + // API for handling format entry operations + + // a formatted entry has the following structure: + // * START_FMT entry, containing the format string + // * TIMESTAMP entry + // * HASH entry + // * author entry of the thread that generated it (optional, present in merged log) + // * format arg1 + // * format arg2 + // * ... + // * END_FMT entry class FormatEntry : public AbstractEntry { public: // explicit FormatEntry(const EntryIterator &it); @@ -226,7 +228,16 @@ private: // --------------------------------------------------------------------------- - // representation of a single log entry in private memory + // representation of a single log entry in shared memory + // byte[0] mEvent + // byte[1] mLength + // byte[2] mData[0] + // ... + // byte[2+i] mData[i] + // ... + // byte[2+mLength-1] mData[mLength-1] + // byte[2+mLength] duplicate copy of mLength to permit reverse scan + // byte[3+mLength] start of next log entry class Entry { public: Entry(Event event, const void *data, size_t length) @@ -267,24 +278,6 @@ private: int value; }; //TODO __attribute__((packed)); - // representation of a single log entry in shared memory - // byte[0] mEvent - // byte[1] mLength - // byte[2] mData[0] - // ... - // byte[2+i] mData[i] - // ... - // byte[2+mLength-1] mData[mLength-1] - // byte[2+mLength] duplicate copy of mLength to permit reverse scan - // byte[3+mLength] start of next log entry - - static void appendInt(String8 *body, const void *data); - static void appendFloat(String8 *body, const void *data); - static void appendPID(String8 *body, const void *data, size_t length); - static void appendTimestamp(String8 *body, const void *data); - static size_t fmtEntryLength(const uint8_t *data); - static String8 bufferDump(const uint8_t *buffer, size_t size); - static String8 bufferDump(const EntryIterator &it); public: // Located in shared memory, must be POD. @@ -420,89 +413,86 @@ public: // --------------------------------------------------------------------------- - class Reader : public RefBase { + // A snapshot of a readers buffer + // This is raw data. No analysis has been done on it + class Snapshot { public: - // A snapshot of a readers buffer - // This is raw data. No analysis has been done on it - class Snapshot { - public: - Snapshot() : mData(NULL), mLost(0) {} + Snapshot() = default; - Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} + explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} - ~Snapshot() { delete[] mData; } + ~Snapshot() { delete[] mData; } - // copy of the buffer - uint8_t *data() const { return mData; } + // amount of data lost (given by audio_utils_fifo_reader) + size_t lost() const { return mLost; } - // amount of data lost (given by audio_utils_fifo_reader) - size_t lost() const { return mLost; } + // iterator to beginning of readable segment of snapshot + // data between begin and end has valid entries + EntryIterator begin() const { return mBegin; } - // iterator to beginning of readable segment of snapshot - // data between begin and end has valid entries - EntryIterator begin() { return mBegin; } + // iterator to end of readable segment of snapshot + EntryIterator end() const { return mEnd; } - // iterator to end of readable segment of snapshot - EntryIterator end() { return mEnd; } - - private: - friend class Reader; - uint8_t *mData; - size_t mLost; - EntryIterator mBegin; - EntryIterator mEnd; - }; + private: + friend class Reader; + uint8_t * const mData{}; + size_t mLost{0}; + EntryIterator mBegin; + EntryIterator mEnd; + }; + class Reader : public RefBase { + public: // Input parameter 'size' is the desired size of the timeline in byte units. // The size of the shared memory must be at least Timeline::sharedSize(size). Reader(const void *shared, size_t size, const std::string &name); Reader(const sp& iMemory, size_t size, const std::string &name); - virtual ~Reader(); // get snapshot of readers fifo buffer, effectively consuming the buffer std::unique_ptr getSnapshot(); - // dump a particular snapshot of the reader - void dump(int fd, size_t indent, Snapshot &snap); - // dump the current content of the reader's buffer (call getSnapshot() and previous dump()) - void dump(int fd, size_t indent = 0); - bool isIMemory(const sp& iMemory) const; - const std::string &name() const { return mName; } - protected: - // print a summary of the performance to the console - void dumpLine(const String8 *timestamp, String8 *body); - EntryIterator handleFormat(const FormatEntry &fmtEntry, - String8 *timestamp, - String8 *body); - int mFd; // file descriptor - int mIndent; // indentation level - int mLost; // bytes of data lost before buffer was read - const std::string mName; // name of reader (actually name of writer) - static constexpr int kMaxObtainTries = 3; - private: + static constexpr int kMaxObtainTries = 3; + // startingTypes and endingTypes are used to check for log corruption. static const std::unordered_set startingTypes; static const std::unordered_set endingTypes; - // declared as const because audio_utils_fifo() constructor sp mIMemory; // ref-counted version, assigned only in constructor + const std::string mName; // name of reader (actually name of writer) /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not audio_utils_fifo * const mFifo; // FIFO itself, - // non-NULL unless constructor fails + // non-NULL unless constructor fails audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO, - // non-NULL unless constructor fails + // non-NULL unless constructor fails // Searches for the last entry of type in the range [front, back) // back has to be entry-aligned. Returns nullptr if none enconuntered. static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, const std::unordered_set &types); + }; - // dummy method for handling absent author entry - virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {} + class DumpReader : public Reader { + public: + DumpReader(const void *shared, size_t size, const std::string &name) + : Reader(shared, size, name) {} + DumpReader(const sp& iMemory, size_t size, const std::string &name) + : Reader(iMemory, size, name) {} + void dump(int fd, size_t indent = 0); + private: + void handleAuthor(const AbstractEntry& fmtEntry __unused, String8* body __unused) {} + EntryIterator handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, String8 *body); + + static void appendInt(String8 *body, const void *data); + static void appendFloat(String8 *body, const void *data); + static void appendPID(String8 *body, const void *data, size_t length); + static void appendTimestamp(String8 *body, const void *data); + //static size_t fmtEntryLength(const uint8_t *data); // TODO Eric remove if not used + static String8 bufferDump(const uint8_t *buffer, size_t size); + static String8 bufferDump(const EntryIterator &it); }; // --------------------------------------------------------------------------- @@ -542,7 +532,7 @@ public: void dump(int fd, int indent = 0); // process a particular snapshot of the reader - void getAndProcessSnapshot(Snapshot & snap); + void getAndProcessSnapshot(Snapshot &snap, int author); // call getSnapshot of the content of the reader's buffer and process the data void getAndProcessSnapshot(); diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index 095dd5c77f..b23832e114 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -58,9 +58,10 @@ void MediaLogService::registerWriter(const sp& shared, size_t size, con shared->size() < NBLog::Timeline::sharedSize(size)) { return; } - sp reader(new NBLog::Reader(shared, size, name)); + sp reader(new NBLog::Reader(shared, size, name)); // Reader handled by merger + sp dumpReader(new NBLog::DumpReader(shared, size, name)); // for dumpsys Mutex::Autolock _l(mLock); - mReaders.add(reader); + mDumpReaders.add(dumpReader); mMerger.addReader(reader); } @@ -70,9 +71,10 @@ void MediaLogService::unregisterWriter(const sp& shared) return; } Mutex::Autolock _l(mLock); - for (size_t i = 0; i < mReaders.size(); ) { - if (mReaders[i]->isIMemory(shared)) { - mReaders.removeAt(i); + for (size_t i = 0; i < mDumpReaders.size(); ) { + if (mDumpReaders[i]->isIMemory(shared)) { + mDumpReaders.removeAt(i); + // TODO mMerger.removeReaders(shared) } else { i++; } @@ -120,18 +122,18 @@ status_t MediaLogService::dump(int fd, const Vector& args __unused) return NO_ERROR; } - for (auto reader : mReaders) { + for (const auto &dumpReader : mDumpReaders) { if (fd >= 0) { - dprintf(fd, "\n%s:\n", reader->name().c_str()); - reader->dump(fd, 0 /*indent*/); + dprintf(fd, "\n%s:\n", dumpReader->name().c_str()); + dumpReader->dump(fd, 0 /*indent*/); } else { - ALOGI("%s:", reader->name().c_str()); + ALOGI("%s:", dumpReader->name().c_str()); } } mLock.unlock(); } } - //mMergeReader.dump(fd); + mMergeReader.dump(fd); return NO_ERROR; } diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h index 3763484257..a1572f98aa 100644 --- a/services/medialog/MediaLogService.h +++ b/services/medialog/MediaLogService.h @@ -55,7 +55,7 @@ private: Mutex mLock; - Vector> mReaders; // protected by mLock + Vector> mDumpReaders; // protected by mLock // FIXME Need comments on all of these, especially about locking NBLog::Shared *mMergerShared; -- GitLab From 0b16447f1cbaab5838477a68b26eeefa18b4e7aa Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 30 May 2018 12:16:56 -0700 Subject: [PATCH 0214/1530] C API for extractor plugins Define a C extractor plugin API, along with some helpers so extractors can still be implemented in C++ if desired. Bug: 111407253 Test: build, boot, play some files Change-Id: Iba947381441769d77929b4235cdb4a4cd5d4f606 --- include/media/MediaExtractorPluginApi.h | 91 +++++++++ include/media/MediaExtractorPluginHelper.h | 184 ++++++++++++++++++ media/extractors/aac/AACExtractor.cpp | 12 +- media/extractors/aac/AACExtractor.h | 5 +- media/extractors/amr/AMRExtractor.cpp | 10 +- media/extractors/amr/AMRExtractor.h | 5 +- media/extractors/flac/FLACExtractor.cpp | 10 +- media/extractors/flac/FLACExtractor.h | 5 +- media/extractors/midi/MidiExtractor.cpp | 10 +- media/extractors/midi/MidiExtractor.h | 5 +- media/extractors/mkv/MatroskaExtractor.cpp | 10 +- media/extractors/mkv/MatroskaExtractor.h | 5 +- media/extractors/mp3/MP3Extractor.cpp | 12 +- media/extractors/mp3/MP3Extractor.h | 5 +- media/extractors/mp4/MPEG4Extractor.cpp | 12 +- media/extractors/mp4/MPEG4Extractor.h | 5 +- media/extractors/mpeg2/ExtractorBundle.cpp | 14 +- media/extractors/mpeg2/MPEG2PSExtractor.h | 5 +- media/extractors/mpeg2/MPEG2TSExtractor.h | 5 +- media/extractors/ogg/OggExtractor.cpp | 12 +- media/extractors/ogg/OggExtractor.h | 5 +- media/extractors/wav/WAVExtractor.cpp | 14 +- media/extractors/wav/WAVExtractor.h | 5 +- media/libmediaextractor/MediaExtractor.cpp | 39 ++++ .../include/media/MediaExtractor.h | 118 ++--------- .../libstagefright/MediaExtractorFactory.cpp | 37 ++-- .../media/stagefright/MediaExtractorFactory.h | 4 +- 27 files changed, 443 insertions(+), 201 deletions(-) create mode 100644 include/media/MediaExtractorPluginApi.h create mode 100644 include/media/MediaExtractorPluginHelper.h diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h new file mode 100644 index 0000000000..30c2b05407 --- /dev/null +++ b/include/media/MediaExtractorPluginApi.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef MEDIA_EXTRACTOR_PLUGIN_API_H_ +#define MEDIA_EXTRACTOR_PLUGIN_API_H_ + +#include // for status_t + +namespace android { + +struct MediaTrack; +class MetaDataBase; +class DataSourceBase; + +extern "C" { + +struct CMediaExtractor { + void *data; + + void (*free)(void *data); + size_t (*countTracks)(void *data); + MediaTrack* (*getTrack)(void *data, size_t index); + status_t (*getTrackMetaData)( + void *data, + MetaDataBase& meta, + size_t index, uint32_t flags); + + status_t (*getMetaData)(void *data, MetaDataBase& meta); + uint32_t (*flags)(void *data); + status_t (*setMediaCas)(void *data, const uint8_t* casToken, size_t size); + const char * (*name)(void *data); +}; + +typedef CMediaExtractor* (*CreatorFunc)(DataSourceBase *source, void *meta); +typedef void (*FreeMetaFunc)(void *meta); + +// The sniffer can optionally fill in an opaque object, "meta", that helps +// the corresponding extractor initialize its state without duplicating +// effort already exerted by the sniffer. If "freeMeta" is given, it will be +// called against the opaque object when it is no longer used. +typedef CreatorFunc (*SnifferFunc)( + DataSourceBase *source, float *confidence, + void **meta, FreeMetaFunc *freeMeta); + +typedef struct { + const uint8_t b[16]; +} media_uuid_t; + +typedef struct { + // version number of this structure + const uint32_t def_version; + + // A unique identifier for this extractor. + // See below for a convenience macro to create this from a string. + media_uuid_t extractor_uuid; + + // Version number of this extractor. When two extractors with the same + // uuid are encountered, the one with the largest version number will + // be used. + const uint32_t extractor_version; + + // a human readable name + const char *extractor_name; + + // the sniffer function + const SnifferFunc sniff; +} ExtractorDef; + +const uint32_t EXTRACTORDEF_VERSION = 1; + +// each plugin library exports one function of this type +typedef ExtractorDef (*GetExtractorDef)(); + +} // extern "C" + +} // namespace android + +#endif // MEDIA_EXTRACTOR_PLUGIN_API_H_ diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h new file mode 100644 index 0000000000..465f76ae29 --- /dev/null +++ b/include/media/MediaExtractorPluginHelper.h @@ -0,0 +1,184 @@ +/* + * 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. + */ + +#ifndef MEDIA_EXTRACTOR_PLUGIN_HELPER_H_ + +#define MEDIA_EXTRACTOR_PLUGIN_HELPER_H_ + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class DataSourceBase; +class MetaDataBase; +struct MediaTrack; + +// extractor plugins can derive from this class which looks remarkably +// like MediaExtractor and can be easily wrapped in the required C API +class MediaExtractorPluginHelper +{ +public: + virtual ~MediaExtractorPluginHelper() {} + virtual size_t countTracks() = 0; + virtual MediaTrack *getTrack(size_t index) = 0; + + enum GetTrackMetaDataFlags { + kIncludeExtensiveMetaData = 1 + }; + virtual status_t getTrackMetaData( + MetaDataBase& meta, + size_t index, uint32_t flags = 0) = 0; + + // Return container specific meta-data. The default implementation + // returns an empty metadata object. + virtual status_t getMetaData(MetaDataBase& meta) = 0; + + enum Flags { + CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" + CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button" + CAN_PAUSE = 4, + CAN_SEEK = 8, // the "seek bar" + }; + + // If subclasses do _not_ override this, the default is + // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE + virtual uint32_t flags() const { + return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE; + }; + + virtual status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { + return INVALID_OPERATION; + } + + virtual const char * name() { return ""; } + +protected: + MediaExtractorPluginHelper() {} + +private: + MediaExtractorPluginHelper(const MediaExtractorPluginHelper &); + MediaExtractorPluginHelper &operator=(const MediaExtractorPluginHelper &); +}; + +inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) { + CMediaExtractor *wrapper = (CMediaExtractor*) malloc(sizeof(CMediaExtractor)); + wrapper->data = extractor; + wrapper->free = [](void *data) -> void { + delete (MediaExtractorPluginHelper*)(data); + }; + wrapper->countTracks = [](void *data) -> size_t { + return ((MediaExtractorPluginHelper*)data)->countTracks(); + }; + wrapper->getTrack = [](void *data, size_t index) -> MediaTrack* { + return ((MediaExtractorPluginHelper*)data)->getTrack(index); + }; + wrapper->getTrackMetaData = []( + void *data, + MetaDataBase& meta, + size_t index, uint32_t flags) -> status_t { + return ((MediaExtractorPluginHelper*)data)->getTrackMetaData(meta, index, flags); + }; + wrapper->getMetaData = []( + void *data, + MetaDataBase& meta) -> status_t { + return ((MediaExtractorPluginHelper*)data)->getMetaData(meta); + }; + wrapper->flags = []( + void *data) -> uint32_t { + return ((MediaExtractorPluginHelper*)data)->flags(); + }; + wrapper->setMediaCas = []( + void *data, const uint8_t *casToken, size_t size) -> status_t { + return ((MediaExtractorPluginHelper*)data)->setMediaCas(casToken, size); + }; + wrapper->name = []( + void *data) -> const char * { + return ((MediaExtractorPluginHelper*)data)->name(); + }; + return wrapper; +} + +// helpers to create a media_uuid_t from a string literal + +// purposely not defined anywhere so that this will fail to link if +// expressions below are not evaluated at compile time +int invalid_uuid_string(const char *); + +template +constexpr uint8_t _digitAt_(const T (&s)[N], const size_t n) { + return s[n] >= '0' && s[n] <= '9' ? s[n] - '0' + : s[n] >= 'a' && s[n] <= 'f' ? s[n] - 'a' + 10 + : s[n] >= 'A' && s[n] <= 'F' ? s[n] - 'A' + 10 + : invalid_uuid_string("uuid: bad digits"); +} + +template +constexpr uint8_t _hexByteAt_(const T (&s)[N], size_t n) { + return (_digitAt_(s, n) << 4) + _digitAt_(s, n + 1); +} + +constexpr bool _assertIsDash_(char c) { + return c == '-' ? true : invalid_uuid_string("Wrong format"); +} + +template +constexpr media_uuid_t constUUID(const char (&s) [N]) { + static_assert(N == 37, "uuid: wrong length"); + return + _assertIsDash_(s[8]), + _assertIsDash_(s[13]), + _assertIsDash_(s[18]), + _assertIsDash_(s[23]), + media_uuid_t {{ + _hexByteAt_(s, 0), + _hexByteAt_(s, 2), + _hexByteAt_(s, 4), + _hexByteAt_(s, 6), + _hexByteAt_(s, 9), + _hexByteAt_(s, 11), + _hexByteAt_(s, 14), + _hexByteAt_(s, 16), + _hexByteAt_(s, 19), + _hexByteAt_(s, 21), + _hexByteAt_(s, 24), + _hexByteAt_(s, 26), + _hexByteAt_(s, 28), + _hexByteAt_(s, 30), + _hexByteAt_(s, 32), + _hexByteAt_(s, 34), + }}; +} +// Convenience macro to create a media_uuid_t from a string literal, which should +// be formatted as "12345678-1234-1234-1234-123456789abc", as generated by +// e.g. https://www.uuidgenerator.net/ or the 'uuidgen' linux command. +// Hex digits may be upper or lower case. +// +// The macro call is otherwise equivalent to specifying the structure directly +// (e.g. UUID("7d613858-5837-4a38-84c5-332d1cddee27") is the same as +// {{0x7d, 0x61, 0x38, 0x58, 0x58, 0x37, 0x4a, 0x38, +// 0x84, 0xc5, 0x33, 0x2d, 0x1c, 0xdd, 0xee, 0x27}}) + +#define UUID(str) []{ constexpr media_uuid_t uuid = constUUID(str); return uuid; }() + +} // namespace android + +#endif // MEDIA_EXTRACTOR_PLUGIN_HELPER_H_ diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index 9fc5a76462..7b9da47f9e 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -323,16 +323,16 @@ status_t AACSource::read( //////////////////////////////////////////////////////////////////////////////// -static MediaExtractor* CreateExtractor( +static CMediaExtractor* CreateExtractor( DataSourceBase *source, void *meta) { off64_t offset = *static_cast(meta); - return new AACExtractor(source, offset); + return wrap(new AACExtractor(source, offset)); } -static MediaExtractor::CreatorFunc Sniff( +static CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **meta, - MediaExtractor::FreeMetaFunc *freeMeta) { + FreeMetaFunc *freeMeta) { off64_t pos = 0; for (;;) { @@ -387,9 +387,9 @@ static MediaExtractor::CreatorFunc Sniff( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("4fd80eae-03d2-4d72-9eb9-48fa6bb54613"), 1, // version "AAC Extractor", diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h index 9dadbed2ec..a48de5c77d 100644 --- a/media/extractors/aac/AACExtractor.h +++ b/media/extractors/aac/AACExtractor.h @@ -18,7 +18,8 @@ #define AAC_EXTRACTOR_H_ -#include +#include +#include #include #include @@ -28,7 +29,7 @@ namespace android { struct AMessage; class String8; -class AACExtractor : public MediaExtractor { +class AACExtractor : public MediaExtractorPluginHelper { public: AACExtractor(DataSourceBase *source, off64_t offset); diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index f56d5ef656..f1143c4fa5 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -365,9 +365,9 @@ status_t AMRSource::read( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("c86639c9-2f31-40ac-a715-fa01b4493aaf"), 1, "AMR Extractor", @@ -375,12 +375,12 @@ MediaExtractor::ExtractorDef GETEXTRACTORDEF() { DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc { + FreeMetaFunc *) -> CreatorFunc { if (SniffAMR(source, nullptr, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new AMRExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new AMRExtractor(source));}; } return NULL; } diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h index c90b32510e..1099dbffa0 100644 --- a/media/extractors/amr/AMRExtractor.h +++ b/media/extractors/amr/AMRExtractor.h @@ -19,7 +19,8 @@ #define AMR_EXTRACTOR_H_ #include -#include +#include +#include #include namespace android { @@ -28,7 +29,7 @@ struct AMessage; class String8; #define OFFSET_TABLE_LEN 300 -class AMRExtractor : public MediaExtractor { +class AMRExtractor : public MediaExtractorPluginHelper { public: explicit AMRExtractor(DataSourceBase *source); diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index a721b6530d..ba4e5ac402 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -859,9 +859,9 @@ bool SniffFLAC(DataSourceBase *source, float *confidence) extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("1364b048-cc45-4fda-9934-327d0ebf9829"), 1, "FLAC Extractor", @@ -869,12 +869,12 @@ MediaExtractor::ExtractorDef GETEXTRACTORDEF() { DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc { + FreeMetaFunc *) -> CreatorFunc { if (SniffFLAC(source, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new FLACExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new FLACExtractor(source));}; } return NULL; } diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h index 7fb6ec612d..70ba9ac7c4 100644 --- a/media/extractors/flac/FLACExtractor.h +++ b/media/extractors/flac/FLACExtractor.h @@ -18,7 +18,8 @@ #define FLAC_EXTRACTOR_H_ #include -#include +#include +#include #include #include @@ -26,7 +27,7 @@ namespace android { class FLACParser; -class FLACExtractor : public MediaExtractor { +class FLACExtractor : public MediaExtractorPluginHelper { public: explicit FLACExtractor(DataSourceBase *source); diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index a30b6f8109..1b9cd5e9ef 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -326,9 +326,9 @@ bool SniffMidi(DataSourceBase *source, float *confidence) extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("ef6cca0a-f8a2-43e6-ba5f-dfcd7c9a7ef2"), 1, "MIDI Extractor", @@ -336,12 +336,12 @@ MediaExtractor::ExtractorDef GETEXTRACTORDEF() { DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc { + FreeMetaFunc *) -> CreatorFunc { if (SniffMidi(source, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new MidiExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new MidiExtractor(source));}; } return NULL; } diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h index 244dd0f8ad..6ea8781df1 100644 --- a/media/extractors/midi/MidiExtractor.h +++ b/media/extractors/midi/MidiExtractor.h @@ -18,7 +18,8 @@ #define MIDI_EXTRACTOR_H_ #include -#include +#include +#include #include #include #include @@ -50,7 +51,7 @@ private: bool mIsInitialized; }; -class MidiExtractor : public MediaExtractor { +class MidiExtractor : public MediaExtractorPluginHelper { public: explicit MidiExtractor(DataSourceBase *source); diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index d6575822c7..3f703e5474 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1638,9 +1638,9 @@ bool SniffMatroska( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("abbedd92-38c4-4904-a4c1-b3f45f899980"), 1, "Matroska Extractor", @@ -1648,12 +1648,12 @@ MediaExtractor::ExtractorDef GETEXTRACTORDEF() { DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc { + FreeMetaFunc *) -> CreatorFunc { if (SniffMatroska(source, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new MatroskaExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new MatroskaExtractor(source));}; } return NULL; } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 3568ea19e8..6d6204410d 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -20,7 +20,8 @@ #include "mkvparser/mkvparser.h" -#include +#include +#include #include #include #include @@ -34,7 +35,7 @@ class MetaData; struct DataSourceBaseReader; struct MatroskaSource; -struct MatroskaExtractor : public MediaExtractor { +struct MatroskaExtractor : public MediaExtractorPluginHelper { explicit MatroskaExtractor(DataSourceBase *source); virtual size_t countTracks(); diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 33cff96cd3..c86212c1a4 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -669,16 +669,16 @@ status_t MP3Extractor::getMetaData(MetaDataBase &meta) { return OK; } -static MediaExtractor* CreateExtractor( +static CMediaExtractor* CreateExtractor( DataSourceBase *source, void *meta) { Mp3Meta *metaData = static_cast(meta); - return new MP3Extractor(source, metaData); + return wrap(new MP3Extractor(source, metaData)); } -static MediaExtractor::CreatorFunc Sniff( +static CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **meta, - MediaExtractor::FreeMetaFunc *freeMeta) { + FreeMetaFunc *freeMeta) { off64_t pos = 0; off64_t post_id3_pos; uint32_t header; @@ -710,9 +710,9 @@ static MediaExtractor::CreatorFunc Sniff( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("812a3f6c-c8cf-46de-b529-3774b14103d4"), 1, // version "MP3 Extractor", diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h index 485b0ca90b..89573027b2 100644 --- a/media/extractors/mp3/MP3Extractor.h +++ b/media/extractors/mp3/MP3Extractor.h @@ -19,7 +19,8 @@ #define MP3_EXTRACTOR_H_ #include -#include +#include +#include #include namespace android { @@ -30,7 +31,7 @@ struct MP3Seeker; class String8; struct Mp3Meta; -class MP3Extractor : public MediaExtractor { +class MP3Extractor : public MediaExtractorPluginHelper { public: MP3Extractor(DataSourceBase *source, Mp3Meta *meta); ~MP3Extractor(); diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 251a4cde2b..0cb4e6b17d 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5944,13 +5944,13 @@ static bool BetterSniffMPEG4(DataSourceBase *source, float *confidence) { return true; } -static MediaExtractor* CreateExtractor(DataSourceBase *source, void *) { - return new MPEG4Extractor(source); +static CMediaExtractor* CreateExtractor(DataSourceBase *source, void *) { + return wrap(new MPEG4Extractor(source)); } -static MediaExtractor::CreatorFunc Sniff( +static CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) { + FreeMetaFunc *) { if (BetterSniffMPEG4(source, confidence)) { return CreateExtractor; } @@ -5966,9 +5966,9 @@ static MediaExtractor::CreatorFunc Sniff( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("27575c67-4417-4c54-8d3d-8e626985a164"), 1, // version "MP4 Extractor", diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index a4a5684f9c..561b567421 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -21,7 +21,8 @@ #include #include -#include +#include +#include #include #include #include @@ -53,7 +54,7 @@ struct Trex { uint32_t default_sample_flags; }; -class MPEG4Extractor : public MediaExtractor { +class MPEG4Extractor : public MediaExtractorPluginHelper { public: explicit MPEG4Extractor(DataSourceBase *source, const char *mime = NULL); diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index 8a0fa0386c..3045aa9293 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -27,9 +27,9 @@ namespace android { extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("3d1dcfeb-e40a-436d-a574-c2438a555e5f"), 1, "MPEG2-PS/TS Extractor", @@ -37,17 +37,17 @@ MediaExtractor::ExtractorDef GETEXTRACTORDEF() { DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc { + FreeMetaFunc *) -> CreatorFunc { if (SniffMPEG2TS(source, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new MPEG2TSExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new MPEG2TSExtractor(source));}; } else if (SniffMPEG2PS(source, confidence)) { return []( DataSourceBase *source, - void *) -> MediaExtractor* { - return new MPEG2PSExtractor(source);}; + void *) -> CMediaExtractor* { + return wrap(new MPEG2PSExtractor(source));}; } return NULL; } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h index 8b9dad9c06..93827faa4b 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.h +++ b/media/extractors/mpeg2/MPEG2PSExtractor.h @@ -19,7 +19,8 @@ #define MPEG2_PS_EXTRACTOR_H_ #include -#include +#include +#include #include #include #include @@ -31,7 +32,7 @@ struct AMessage; struct Track; class String8; -struct MPEG2PSExtractor : public MediaExtractor { +struct MPEG2PSExtractor : public MediaExtractorPluginHelper { explicit MPEG2PSExtractor(DataSourceBase *source); virtual size_t countTracks(); diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index cbdd3cb847..05e57c50de 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -20,7 +20,8 @@ #define MPEG2_TS_EXTRACTOR_H_ #include -#include +#include +#include #include #include #include @@ -38,7 +39,7 @@ class DataSourceBase; struct MPEG2TSSource; class String8; -struct MPEG2TSExtractor : public MediaExtractor { +struct MPEG2TSExtractor : public MediaExtractorPluginHelper { explicit MPEG2TSExtractor(DataSourceBase *source); virtual size_t countTracks(); diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index b2fe69cd07..efacfb3d5d 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -1248,17 +1248,17 @@ status_t OggExtractor::getMetaData(MetaDataBase &meta) { return mImpl->getFileMetaData(meta); } -static MediaExtractor* CreateExtractor( +static CMediaExtractor* CreateExtractor( DataSourceBase *source, void *) { - return new OggExtractor(source); + return wrap(new OggExtractor(source)); } -static MediaExtractor::CreatorFunc Sniff( +static CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) { + FreeMetaFunc *) { char tmp[4]; if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) { return NULL; @@ -1272,9 +1272,9 @@ static MediaExtractor::CreatorFunc Sniff( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("8cc5cd06-f772-495e-8a62-cba9649374e9"), 1, // version "Ogg Extractor", diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h index 9fe2944de7..94f857a08d 100644 --- a/media/extractors/ogg/OggExtractor.h +++ b/media/extractors/ogg/OggExtractor.h @@ -19,7 +19,8 @@ #define OGG_EXTRACTOR_H_ #include -#include +#include +#include namespace android { @@ -30,7 +31,7 @@ class String8; struct MyOggExtractor; struct OggSource; -struct OggExtractor : public MediaExtractor { +struct OggExtractor : public MediaExtractorPluginHelper { explicit OggExtractor(DataSourceBase *source); virtual size_t countTracks(); diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index f5a1b01669..903de524ae 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -544,17 +544,17 @@ status_t WAVSource::read( //////////////////////////////////////////////////////////////////////////////// -static MediaExtractor* CreateExtractor( +static CMediaExtractor* CreateExtractor( DataSourceBase *source, void *) { - return new WAVExtractor(source); + return wrap(new WAVExtractor(source)); } -static MediaExtractor::CreatorFunc Sniff( +static CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **, - MediaExtractor::FreeMetaFunc *) { + FreeMetaFunc *) { char header[12]; if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { return NULL; @@ -564,7 +564,7 @@ static MediaExtractor::CreatorFunc Sniff( return NULL; } - MediaExtractor *extractor = new WAVExtractor(source); + WAVExtractor *extractor = new WAVExtractor(source); int numTracks = extractor->countTracks(); delete extractor; if (numTracks == 0) { @@ -579,9 +579,9 @@ static MediaExtractor::CreatorFunc Sniff( extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) -MediaExtractor::ExtractorDef GETEXTRACTORDEF() { +ExtractorDef GETEXTRACTORDEF() { return { - MediaExtractor::EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION, UUID("7d613858-5837-4a38-84c5-332d1cddee27"), 1, // version "WAV Extractor", diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h index 467d0b7d96..519a730e10 100644 --- a/media/extractors/wav/WAVExtractor.h +++ b/media/extractors/wav/WAVExtractor.h @@ -19,7 +19,8 @@ #define WAV_EXTRACTOR_H_ #include -#include +#include +#include #include namespace android { @@ -28,7 +29,7 @@ struct AMessage; class DataSourceBase; class String8; -class WAVExtractor : public MediaExtractor { +class WAVExtractor : public MediaExtractorPluginHelper { public: explicit WAVExtractor(DataSourceBase *source); diff --git a/media/libmediaextractor/MediaExtractor.cpp b/media/libmediaextractor/MediaExtractor.cpp index a6b3dc96d5..8bf44c84b9 100644 --- a/media/libmediaextractor/MediaExtractor.cpp +++ b/media/libmediaextractor/MediaExtractor.cpp @@ -39,4 +39,43 @@ uint32_t MediaExtractor::flags() const { return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK; } +// -------------------------------------------------------------------------------- +MediaExtractorCUnwrapper::MediaExtractorCUnwrapper(CMediaExtractor *wrapper) { + this->wrapper = wrapper; +} + +MediaExtractorCUnwrapper::~MediaExtractorCUnwrapper() { + wrapper->free(wrapper->data); + free(wrapper); +} + +size_t MediaExtractorCUnwrapper::countTracks() { + return wrapper->countTracks(wrapper->data); +} + +MediaTrack *MediaExtractorCUnwrapper::getTrack(size_t index) { + return wrapper->getTrack(wrapper->data, index); +} + +status_t MediaExtractorCUnwrapper::getTrackMetaData( + MetaDataBase& meta, size_t index, uint32_t flags) { + return wrapper->getTrackMetaData(wrapper->data, meta, index, flags); +} + +status_t MediaExtractorCUnwrapper::getMetaData(MetaDataBase& meta) { + return wrapper->getMetaData(wrapper->data, meta); +} + +const char * MediaExtractorCUnwrapper::name() { + return wrapper->name(wrapper->data); +} + +uint32_t MediaExtractorCUnwrapper::flags() const { + return wrapper->flags(wrapper->data); +} + +status_t MediaExtractorCUnwrapper::setMediaCas(const uint8_t* casToken, size_t size) { + return wrapper->setMediaCas(wrapper->data, casToken, size); +} + } // namespace android diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h index 4ba98da533..d9456ab0a4 100644 --- a/media/libmediaextractor/include/media/MediaExtractor.h +++ b/media/libmediaextractor/include/media/MediaExtractor.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include namespace android { @@ -42,7 +44,6 @@ public: } }; - class MediaExtractor // : public ExtractorAllocTracker { @@ -79,46 +80,6 @@ public: virtual const char * name() { return ""; } - typedef MediaExtractor* (*CreatorFunc)( - DataSourceBase *source, void *meta); - typedef void (*FreeMetaFunc)(void *meta); - - // The sniffer can optionally fill in an opaque object, "meta", that helps - // the corresponding extractor initialize its state without duplicating - // effort already exerted by the sniffer. If "freeMeta" is given, it will be - // called against the opaque object when it is no longer used. - typedef CreatorFunc (*SnifferFunc)( - DataSourceBase *source, float *confidence, - void **meta, FreeMetaFunc *freeMeta); - - typedef struct { - const uint8_t b[16]; - } uuid_t; - - typedef struct { - // version number of this structure - const uint32_t def_version; - - // A unique identifier for this extractor. - // See below for a convenience macro to create this from a string. - uuid_t extractor_uuid; - - // Version number of this extractor. When two extractors with the same - // uuid are encountered, the one with the largest version number will - // be used. - const uint32_t extractor_version; - - // a human readable name - const char *extractor_name; - - // the sniffer function - const SnifferFunc sniff; - } ExtractorDef; - - static const uint32_t EXTRACTORDEF_VERSION = 1; - - typedef ExtractorDef (*GetExtractorDef)(); - protected: MediaExtractor(); @@ -127,66 +88,21 @@ private: MediaExtractor &operator=(const MediaExtractor &); }; -// purposely not defined anywhere so that this will fail to link if -// expressions below are not evaluated at compile time -int invalid_uuid_string(const char *); - -template -constexpr uint8_t _digitAt_(const T (&s)[N], const size_t n) { - return s[n] >= '0' && s[n] <= '9' ? s[n] - '0' - : s[n] >= 'a' && s[n] <= 'f' ? s[n] - 'a' + 10 - : s[n] >= 'A' && s[n] <= 'F' ? s[n] - 'A' + 10 - : invalid_uuid_string("uuid: bad digits"); -} - -template -constexpr uint8_t _hexByteAt_(const T (&s)[N], size_t n) { - return (_digitAt_(s, n) << 4) + _digitAt_(s, n + 1); -} - -constexpr bool _assertIsDash_(char c) { - return c == '-' ? true : invalid_uuid_string("Wrong format"); -} - -template -constexpr MediaExtractor::uuid_t constUUID(const char (&s) [N]) { - static_assert(N == 37, "uuid: wrong length"); - return - _assertIsDash_(s[8]), - _assertIsDash_(s[13]), - _assertIsDash_(s[18]), - _assertIsDash_(s[23]), - MediaExtractor::uuid_t {{ - _hexByteAt_(s, 0), - _hexByteAt_(s, 2), - _hexByteAt_(s, 4), - _hexByteAt_(s, 6), - _hexByteAt_(s, 9), - _hexByteAt_(s, 11), - _hexByteAt_(s, 14), - _hexByteAt_(s, 16), - _hexByteAt_(s, 19), - _hexByteAt_(s, 21), - _hexByteAt_(s, 24), - _hexByteAt_(s, 26), - _hexByteAt_(s, 28), - _hexByteAt_(s, 30), - _hexByteAt_(s, 32), - _hexByteAt_(s, 34), - }}; -} -// Convenience macro to create a uuid_t from a string literal, which should -// be formatted as "12345678-1234-1234-1234-123456789abc", as generated by -// e.g. https://www.uuidgenerator.net/ or the 'uuidgen' linux command. -// Hex digits may be upper or lower case. -// -// The macro call is otherwise equivalent to specifying the structure directly -// (e.g. UUID("7d613858-5837-4a38-84c5-332d1cddee27") is the same as -// {{0x7d, 0x61, 0x38, 0x58, 0x58, 0x37, 0x4a, 0x38, -// 0x84, 0xc5, 0x33, 0x2d, 0x1c, 0xdd, 0xee, 0x27}}) - -#define UUID(str) []{ constexpr MediaExtractor::uuid_t uuid = constUUID(str); return uuid; }() - +class MediaExtractorCUnwrapper : public MediaExtractor { +public: + explicit MediaExtractorCUnwrapper(CMediaExtractor *wrapper); + virtual size_t countTracks(); + virtual MediaTrack *getTrack(size_t index); + virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0); + virtual status_t getMetaData(MetaDataBase& meta); + virtual const char * name(); + virtual uint32_t flags() const; + virtual status_t setMediaCas(const uint8_t* casToken, size_t size); +protected: + virtual ~MediaExtractorCUnwrapper(); +private: + CMediaExtractor *wrapper; +}; } // namespace android diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 2d4bd39da1..dd98fc0a41 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -74,8 +74,8 @@ sp MediaExtractorFactory::CreateFromService( source->DrmInitialization(nullptr /* mime */); void *meta = nullptr; - MediaExtractor::CreatorFunc creator = NULL; - MediaExtractor::FreeMetaFunc freeMeta = nullptr; + CreatorFunc creator = NULL; + FreeMetaFunc freeMeta = nullptr; float confidence; sp plugin; creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin); @@ -84,15 +84,16 @@ sp MediaExtractorFactory::CreateFromService( return NULL; } - MediaExtractor *ret = creator(source.get(), meta); + CMediaExtractor *ret = creator(source.get(), meta); if (meta != nullptr && freeMeta != nullptr) { freeMeta(meta); } + MediaExtractor *ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr; ALOGV("Created an extractor '%s' with confidence %.2f", - ret != nullptr ? ret->name() : "", confidence); + ex != nullptr ? ex->name() : "", confidence); - return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin); + return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin); } //static @@ -103,14 +104,14 @@ void MediaExtractorFactory::LoadPlugins(const ::std::string& apkPath) { } struct ExtractorPlugin : public RefBase { - MediaExtractor::ExtractorDef def; + ExtractorDef def; void *libHandle; String8 libPath; String8 uuidString; - ExtractorPlugin(MediaExtractor::ExtractorDef definition, void *handle, String8 &path) + ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path) : def(definition), libHandle(handle), libPath(path) { - for (size_t i = 0; i < sizeof MediaExtractor::ExtractorDef::extractor_uuid; i++) { + for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) { uuidString.appendFormat("%02x", def.extractor_uuid.b[i]); } } @@ -127,9 +128,9 @@ std::shared_ptr>> MediaExtractorFactory::gPlugins; bool MediaExtractorFactory::gPluginsRegistered = false; // static -MediaExtractor::CreatorFunc MediaExtractorFactory::sniff( +CreatorFunc MediaExtractorFactory::sniff( DataSourceBase *source, float *confidence, void **meta, - MediaExtractor::FreeMetaFunc *freeMeta, sp &plugin) { + FreeMetaFunc *freeMeta, sp &plugin) { *confidence = 0.0f; *meta = nullptr; @@ -142,13 +143,13 @@ MediaExtractor::CreatorFunc MediaExtractorFactory::sniff( plugins = gPlugins; } - MediaExtractor::CreatorFunc curCreator = NULL; - MediaExtractor::CreatorFunc bestCreator = NULL; + CreatorFunc curCreator = NULL; + CreatorFunc bestCreator = NULL; for (auto it = plugins->begin(); it != plugins->end(); ++it) { ALOGV("sniffing %s", (*it)->def.extractor_name); float newConfidence; void *newMeta = nullptr; - MediaExtractor::FreeMetaFunc newFreeMeta = nullptr; + FreeMetaFunc newFreeMeta = nullptr; if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) { if (newConfidence > *confidence) { *confidence = newConfidence; @@ -175,7 +176,7 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, std::list> &pluginList) { // sanity check check struct version, uuid, name if (plugin->def.def_version == 0 - || plugin->def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) { + || plugin->def.def_version > EXTRACTORDEF_VERSION) { ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version); return; } @@ -236,8 +237,8 @@ void MediaExtractorFactory::RegisterExtractorsInApk( // within the apk instead of system libraries already loaded. void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL); if (libHandle) { - MediaExtractor::GetExtractorDef getDef = - (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); if (getDef) { ALOGV("registering sniffer for %s", libPath.string()); RegisterExtractor( @@ -271,8 +272,8 @@ void MediaExtractorFactory::RegisterExtractorsInSystem( String8 libPath = String8(libDirPath) + "/" + libEntry->d_name; void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL); if (libHandle) { - MediaExtractor::GetExtractorDef getDef = - (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); if (getDef) { ALOGV("registering sniffer for %s", libPath.string()); RegisterExtractor( diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index d5f4b35446..3920cccef3 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -49,8 +49,8 @@ private: static void RegisterExtractor( const sp &plugin, std::list> &pluginList); - static MediaExtractor::CreatorFunc sniff(DataSourceBase *source, - float *confidence, void **meta, MediaExtractor::FreeMetaFunc *freeMeta, + static CreatorFunc sniff(DataSourceBase *source, + float *confidence, void **meta, FreeMetaFunc *freeMeta, sp &plugin); static void UpdateExtractors(const char *newUpdateApkPath); -- GitLab From cec44d0c8dc9703ca87435d95bdcd6ef228784d7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Sun, 17 Jun 2018 22:21:09 -0700 Subject: [PATCH 0215/1530] C-ify DataSource Add a C API for DataSource/Base, and a helper so extractors can continue using a C++ API. Bug: 111407253 Test: build, boot, play some files Change-Id: I1c8b2990e17d18eee53c9abf7ebef2ced0e8b7fd --- include/media/MediaExtractorPluginApi.h | 13 +- include/media/MediaExtractorPluginHelper.h | 131 ++++++++++++++++++ media/extractors/aac/AACExtractor.cpp | 23 +-- media/extractors/aac/AACExtractor.h | 6 +- media/extractors/amr/AMRExtractor.cpp | 23 +-- media/extractors/amr/AMRExtractor.h | 4 +- media/extractors/flac/FLACExtractor.cpp | 28 ++-- media/extractors/flac/FLACExtractor.h | 6 +- media/extractors/midi/MidiExtractor.cpp | 10 +- media/extractors/midi/MidiExtractor.h | 8 +- media/extractors/mkv/MatroskaExtractor.cpp | 19 +-- media/extractors/mkv/MatroskaExtractor.h | 4 +- media/extractors/mp3/MP3Extractor.cpp | 29 ++-- media/extractors/mp3/MP3Extractor.h | 7 +- media/extractors/mp3/VBRISeeker.cpp | 6 +- media/extractors/mp3/VBRISeeker.h | 4 +- media/extractors/mp3/XINGSeeker.cpp | 6 +- media/extractors/mp3/XINGSeeker.h | 4 +- media/extractors/mp4/ItemTable.cpp | 39 +++--- media/extractors/mp4/ItemTable.h | 6 +- media/extractors/mp4/MPEG4Extractor.cpp | 43 +++--- media/extractors/mp4/MPEG4Extractor.h | 12 +- media/extractors/mp4/SampleTable.cpp | 4 +- media/extractors/mp4/SampleTable.h | 7 +- media/extractors/mpeg2/ExtractorBundle.cpp | 18 ++- media/extractors/mpeg2/MPEG2PSExtractor.cpp | 5 +- media/extractors/mpeg2/MPEG2PSExtractor.h | 6 +- media/extractors/mpeg2/MPEG2TSExtractor.cpp | 8 +- media/extractors/mpeg2/MPEG2TSExtractor.h | 15 +- media/extractors/ogg/OggExtractor.cpp | 21 +-- media/extractors/ogg/OggExtractor.h | 5 +- media/extractors/wav/WAVExtractor.cpp | 24 ++-- media/extractors/wav/WAVExtractor.h | 6 +- media/libmedia/MidiIoWrapper.cpp | 46 ++++++ media/libmedia/include/media/MidiIoWrapper.h | 3 + .../include/media/DataSource.h | 30 +++- .../libstagefright/MediaExtractorFactory.cpp | 8 +- media/libstagefright/id3/ID3.cpp | 51 +++++++ media/libstagefright/include/ID3.h | 2 + .../media/stagefright/MediaExtractorFactory.h | 2 +- 40 files changed, 493 insertions(+), 199 deletions(-) diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h index 30c2b05407..930b6e27e1 100644 --- a/include/media/MediaExtractorPluginApi.h +++ b/include/media/MediaExtractorPluginApi.h @@ -23,10 +23,17 @@ namespace android { struct MediaTrack; class MetaDataBase; -class DataSourceBase; extern "C" { +struct CDataSource { + ssize_t (*readAt)(void *handle, off64_t offset, void *data, size_t size); + status_t (*getSize)(void *handle, off64_t *size); + uint32_t (*flags)(void *handle ); + bool (*getUri)(void *handle, char *uriString, size_t bufferSize); + void *handle; +}; + struct CMediaExtractor { void *data; @@ -44,7 +51,7 @@ struct CMediaExtractor { const char * (*name)(void *data); }; -typedef CMediaExtractor* (*CreatorFunc)(DataSourceBase *source, void *meta); +typedef CMediaExtractor* (*CreatorFunc)(CDataSource *source, void *meta); typedef void (*FreeMetaFunc)(void *meta); // The sniffer can optionally fill in an opaque object, "meta", that helps @@ -52,7 +59,7 @@ typedef void (*FreeMetaFunc)(void *meta); // effort already exerted by the sniffer. If "freeMeta" is given, it will be // called against the opaque object when it is no longer used. typedef CreatorFunc (*SnifferFunc)( - DataSourceBase *source, float *confidence, + CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta); typedef struct { diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h index 465f76ae29..c817b30764 100644 --- a/include/media/MediaExtractorPluginHelper.h +++ b/include/media/MediaExtractorPluginHelper.h @@ -18,6 +18,7 @@ #define MEDIA_EXTRACTOR_PLUGIN_HELPER_H_ +#include #include #include @@ -117,6 +118,136 @@ inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) { return wrapper; } +/* adds some convience methods */ +class DataSourceHelper { +public: + explicit DataSourceHelper(CDataSource *csource) { + mSource = csource; + } + + explicit DataSourceHelper(DataSourceHelper *source) { + mSource = source->mSource; + } + + ssize_t readAt(off64_t offset, void *data, size_t size) { + return mSource->readAt(mSource->handle, offset, data, size); + } + + status_t getSize(off64_t *size) { + return mSource->getSize(mSource->handle, size); + } + + bool getUri(char *uriString, size_t bufferSize) { + return mSource->getUri(mSource->handle, uriString, bufferSize); + } + + uint32_t flags() { + return mSource->flags(mSource->handle); + } + + // Convenience methods: + bool getUInt16(off64_t offset, uint16_t *x) { + *x = 0; + + uint8_t byte[2]; + if (readAt(offset, byte, 2) != 2) { + return false; + } + + *x = (byte[0] << 8) | byte[1]; + + return true; + } + + // 3 byte int, returned as a 32-bit int + bool getUInt24(off64_t offset, uint32_t *x) { + *x = 0; + + uint8_t byte[3]; + if (readAt(offset, byte, 3) != 3) { + return false; + } + + *x = (byte[0] << 16) | (byte[1] << 8) | byte[2]; + + return true; + } + + bool getUInt32(off64_t offset, uint32_t *x) { + *x = 0; + + uint32_t tmp; + if (readAt(offset, &tmp, 4) != 4) { + return false; + } + + *x = ntohl(tmp); + + return true; + } + + bool getUInt64(off64_t offset, uint64_t *x) { + *x = 0; + + uint64_t tmp; + if (readAt(offset, &tmp, 8) != 8) { + return false; + } + + *x = ((uint64_t)ntohl(tmp & 0xffffffff) << 32) | ntohl(tmp >> 32); + + return true; + } + + // read either int or int<2N> into a uint<2N>_t, size is the int size in bytes. + bool getUInt16Var(off64_t offset, uint16_t *x, size_t size) { + if (size == 2) { + return getUInt16(offset, x); + } + if (size == 1) { + uint8_t tmp; + if (readAt(offset, &tmp, 1) == 1) { + *x = tmp; + return true; + } + } + return false; + } + + bool getUInt32Var(off64_t offset, uint32_t *x, size_t size) { + if (size == 4) { + return getUInt32(offset, x); + } + if (size == 2) { + uint16_t tmp; + if (getUInt16(offset, &tmp)) { + *x = tmp; + return true; + } + } + return false; + } + + bool getUInt64Var(off64_t offset, uint64_t *x, size_t size) { + if (size == 8) { + return getUInt64(offset, x); + } + if (size == 4) { + uint32_t tmp; + if (getUInt32(offset, &tmp)) { + *x = tmp; + return true; + } + } + return false; + } + +protected: + CDataSource *mSource; +}; + + + // helpers to create a media_uuid_t from a string literal // purposely not defined anywhere so that this will fail to link if diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index 7b9da47f9e..955a588c42 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -19,7 +19,7 @@ #include #include "AACExtractor.h" -#include +#include #include #include #include @@ -36,7 +36,7 @@ namespace android { class AACSource : public MediaTrack { public: AACSource( - DataSourceBase *source, + DataSourceHelper *source, MetaDataBase &meta, const Vector &offset_vector, int64_t frame_duration_us); @@ -54,7 +54,7 @@ protected: private: static const size_t kMaxFrameSize; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase mMeta; off64_t mOffset; @@ -92,7 +92,7 @@ uint32_t get_sample_rate(const uint8_t sf_index) // The returned value is the AAC frame size with the ADTS header length (regardless of // the presence of the CRC). // If headerSize is non-NULL, it will be used to return the size of the header of this ADTS frame. -static size_t getAdtsFrameLength(DataSourceBase *source, off64_t offset, size_t* headerSize) { +static size_t getAdtsFrameLength(DataSourceHelper *source, off64_t offset, size_t* headerSize) { const size_t kAdtsHeaderLengthNoCrc = 7; const size_t kAdtsHeaderLengthWithCrc = 9; @@ -133,7 +133,7 @@ static size_t getAdtsFrameLength(DataSourceBase *source, off64_t offset, size_t* } AACExtractor::AACExtractor( - DataSourceBase *source, off64_t offset) + DataSourceHelper *source, off64_t offset) : mDataSource(source), mInitCheck(NO_INIT), mFrameDurationUs(0) { @@ -219,7 +219,7 @@ status_t AACExtractor::getTrackMetaData(MetaDataBase &meta, size_t index, uint32 const size_t AACSource::kMaxFrameSize = 8192; AACSource::AACSource( - DataSourceBase *source, + DataSourceHelper *source, MetaDataBase &meta, const Vector &offset_vector, int64_t frame_duration_us) @@ -324,20 +324,21 @@ status_t AACSource::read( //////////////////////////////////////////////////////////////////////////////// static CMediaExtractor* CreateExtractor( - DataSourceBase *source, + CDataSource *source, void *meta) { off64_t offset = *static_cast(meta); - return wrap(new AACExtractor(source, offset)); + return wrap(new AACExtractor(new DataSourceHelper(source), offset)); } static CreatorFunc Sniff( - DataSourceBase *source, float *confidence, void **meta, + CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; + DataSourceHelper helper(source); for (;;) { uint8_t id3header[10]; - if (source->readAt(pos, id3header, sizeof(id3header)) + if (helper.readAt(pos, id3header, sizeof(id3header)) < (ssize_t)sizeof(id3header)) { return NULL; } @@ -364,7 +365,7 @@ static CreatorFunc Sniff( uint8_t header[2]; - if (source->readAt(pos, &header, 2) != 2) { + if (helper.readAt(pos, &header, 2) != 2) { return NULL; } diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h index a48de5c77d..3f2046131b 100644 --- a/media/extractors/aac/AACExtractor.h +++ b/media/extractors/aac/AACExtractor.h @@ -31,7 +31,7 @@ class String8; class AACExtractor : public MediaExtractorPluginHelper { public: - AACExtractor(DataSourceBase *source, off64_t offset); + AACExtractor(DataSourceHelper *source, off64_t offset); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -44,7 +44,7 @@ protected: virtual ~AACExtractor(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase mMeta; status_t mInitCheck; @@ -56,7 +56,7 @@ private: }; bool SniffAAC( - DataSourceBase *source, String8 *mimeType, float *confidence, off64_t *offset); + DataSourceHelper *source, String8 *mimeType, float *confidence, off64_t *offset); } // namespace android diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index f1143c4fa5..e109fb3d5a 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -20,7 +20,6 @@ #include "AMRExtractor.h" -#include #include #include #include @@ -34,7 +33,7 @@ namespace android { class AMRSource : public MediaTrack { public: AMRSource( - DataSourceBase *source, + DataSourceHelper *source, MetaDataBase &meta, bool isWide, const off64_t *offset_table, @@ -52,7 +51,7 @@ protected: virtual ~AMRSource(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase mMeta; bool mIsWide; @@ -98,7 +97,7 @@ static size_t getFrameSize(bool isWide, unsigned FT) { return frameSize; } -static status_t getFrameSizeByOffset(DataSourceBase *source, +static status_t getFrameSizeByOffset(DataSourceHelper *source, off64_t offset, bool isWide, size_t *frameSize) { uint8_t header; ssize_t count = source->readAt(offset, &header, 1); @@ -118,7 +117,7 @@ static status_t getFrameSizeByOffset(DataSourceBase *source, } static bool SniffAMR( - DataSourceBase *source, bool *isWide, float *confidence) { + DataSourceHelper *source, bool *isWide, float *confidence) { char header[9]; if (source->readAt(0, header, sizeof(header)) != sizeof(header)) { @@ -144,7 +143,7 @@ static bool SniffAMR( return false; } -AMRExtractor::AMRExtractor(DataSourceBase *source) +AMRExtractor::AMRExtractor(DataSourceHelper *source) : mDataSource(source), mInitCheck(NO_INIT), mOffsetTableLength(0) { @@ -192,6 +191,7 @@ AMRExtractor::AMRExtractor(DataSourceBase *source) } AMRExtractor::~AMRExtractor() { + delete mDataSource; } status_t AMRExtractor::getMetaData(MetaDataBase &meta) { @@ -229,7 +229,7 @@ status_t AMRExtractor::getTrackMetaData(MetaDataBase &meta, size_t index, uint32 //////////////////////////////////////////////////////////////////////////////// AMRSource::AMRSource( - DataSourceBase *source, MetaDataBase &meta, + DataSourceHelper *source, MetaDataBase &meta, bool isWide, const off64_t *offset_table, size_t offset_table_length) : mDataSource(source), mMeta(meta), @@ -372,15 +372,16 @@ ExtractorDef GETEXTRACTORDEF() { 1, "AMR Extractor", []( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - if (SniffAMR(source, nullptr, confidence)) { + DataSourceHelper helper(source); + if (SniffAMR(&helper, nullptr, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { - return wrap(new AMRExtractor(source));}; + return wrap(new AMRExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h index 1099dbffa0..499ca67da3 100644 --- a/media/extractors/amr/AMRExtractor.h +++ b/media/extractors/amr/AMRExtractor.h @@ -31,7 +31,7 @@ class String8; class AMRExtractor : public MediaExtractorPluginHelper { public: - explicit AMRExtractor(DataSourceBase *source); + explicit AMRExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -44,7 +44,7 @@ protected: virtual ~AMRExtractor(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase mMeta; status_t mInitCheck; bool mIsWide; diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index ba4e5ac402..1efaa0c617 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -24,7 +24,7 @@ // libFLAC parser #include "FLAC/stream_decoder.h" -#include +#include #include #include #include @@ -43,7 +43,7 @@ class FLACSource : public MediaTrack { public: FLACSource( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, MetaDataBase &meta); virtual status_t start(MetaDataBase *params); @@ -57,7 +57,7 @@ protected: virtual ~FLACSource(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase mTrackMetadata; FLACParser *mParser; bool mInitCheck; @@ -79,7 +79,7 @@ public: }; explicit FLACParser( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, // If metadata pointers aren't provided, we don't fill them MetaDataBase *fileMetadata = 0, MetaDataBase *trackMetadata = 0); @@ -118,7 +118,7 @@ public: } private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase *mFileMetadata; MetaDataBase *mTrackMetadata; bool mInitCheck; @@ -487,7 +487,7 @@ static void copyTrespass( // FLACParser FLACParser::FLACParser( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, MetaDataBase *fileMetadata, MetaDataBase *trackMetadata) : mDataSource(dataSource), @@ -708,7 +708,7 @@ MediaBufferBase *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) // FLACsource FLACSource::FLACSource( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, MetaDataBase &trackMetadata) : mDataSource(dataSource), mTrackMetadata(trackMetadata), @@ -789,7 +789,7 @@ status_t FLACSource::read( // FLACExtractor FLACExtractor::FLACExtractor( - DataSourceBase *dataSource) + DataSourceHelper *dataSource) : mDataSource(dataSource), mParser(nullptr), mInitCheck(false) @@ -804,6 +804,7 @@ FLACExtractor::~FLACExtractor() { ALOGV("~FLACExtractor::FLACExtractor"); delete mParser; + delete mDataSource; } size_t FLACExtractor::countTracks() @@ -837,7 +838,7 @@ status_t FLACExtractor::getMetaData(MetaDataBase &meta) // Sniffer -bool SniffFLAC(DataSourceBase *source, float *confidence) +bool SniffFLAC(DataSourceHelper *source, float *confidence) { // first 4 is the signature word // second 4 is the sizeof STREAMINFO @@ -866,15 +867,16 @@ ExtractorDef GETEXTRACTORDEF() { 1, "FLAC Extractor", []( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - if (SniffFLAC(source, confidence)) { + DataSourceHelper helper(source); + if (SniffFLAC(&helper, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { - return wrap(new FLACExtractor(source));}; + return wrap(new FLACExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h index 70ba9ac7c4..1ddff436da 100644 --- a/media/extractors/flac/FLACExtractor.h +++ b/media/extractors/flac/FLACExtractor.h @@ -30,7 +30,7 @@ class FLACParser; class FLACExtractor : public MediaExtractorPluginHelper { public: - explicit FLACExtractor(DataSourceBase *source); + explicit FLACExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -43,7 +43,7 @@ protected: virtual ~FLACExtractor(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; FLACParser *mParser; status_t mInitCheck; MetaDataBase mFileMetadata; @@ -56,8 +56,6 @@ private: }; -bool SniffFLAC(DataSourceBase *source, float *confidence); - } // namespace android #endif // FLAC_EXTRACTOR_H_ diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index 1b9cd5e9ef..233033eadc 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -142,7 +142,7 @@ status_t MidiSource::init() // MidiEngine -MidiEngine::MidiEngine(DataSourceBase *dataSource, +MidiEngine::MidiEngine(CDataSource *dataSource, MetaDataBase *fileMetadata, MetaDataBase *trackMetadata) : mGroup(NULL), @@ -263,7 +263,7 @@ MediaBufferBase* MidiEngine::readBuffer() { // MidiExtractor MidiExtractor::MidiExtractor( - DataSourceBase *dataSource) + CDataSource *dataSource) : mDataSource(dataSource), mInitCheck(false) { @@ -310,7 +310,7 @@ status_t MidiExtractor::getMetaData(MetaDataBase &meta) // Sniffer -bool SniffMidi(DataSourceBase *source, float *confidence) +bool SniffMidi(CDataSource *source, float *confidence) { MidiEngine p(source, NULL, NULL); if (p.initCheck() == OK) { @@ -333,13 +333,13 @@ ExtractorDef GETEXTRACTORDEF() { 1, "MIDI Extractor", []( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { if (SniffMidi(source, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { return wrap(new MidiExtractor(source));}; } diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h index 6ea8781df1..fbbe93ed5c 100644 --- a/media/extractors/midi/MidiExtractor.h +++ b/media/extractors/midi/MidiExtractor.h @@ -31,7 +31,7 @@ namespace android { class MidiEngine { public: - explicit MidiEngine(DataSourceBase *dataSource, + explicit MidiEngine(CDataSource *dataSource, MetaDataBase *fileMetadata, MetaDataBase *trackMetadata); ~MidiEngine(); @@ -54,7 +54,7 @@ private: class MidiExtractor : public MediaExtractorPluginHelper { public: - explicit MidiExtractor(DataSourceBase *source); + explicit MidiExtractor(CDataSource *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -67,7 +67,7 @@ protected: virtual ~MidiExtractor(); private: - DataSourceBase *mDataSource; + CDataSource *mDataSource; status_t mInitCheck; MetaDataBase mFileMetadata; @@ -89,7 +89,7 @@ private: }; -bool SniffMidi(DataSourceBase *source, float *confidence); +bool SniffMidi(CDataSource *source, float *confidence); } // namespace android diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 3f703e5474..a387970dc8 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -44,7 +44,7 @@ namespace android { struct DataSourceBaseReader : public mkvparser::IMkvReader { - explicit DataSourceBaseReader(DataSourceBase *source) + explicit DataSourceBaseReader(DataSourceHelper *source) : mSource(source) { } @@ -86,7 +86,7 @@ struct DataSourceBaseReader : public mkvparser::IMkvReader { } private: - DataSourceBase *mSource; + DataSourceHelper *mSource; DataSourceBaseReader(const DataSourceBaseReader &); DataSourceBaseReader &operator=(const DataSourceBaseReader &); @@ -921,7 +921,7 @@ status_t MatroskaSource::read( //////////////////////////////////////////////////////////////////////////////// -MatroskaExtractor::MatroskaExtractor(DataSourceBase *source) +MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) : mDataSource(source), mReader(new DataSourceBaseReader(mDataSource)), mSegment(NULL), @@ -994,6 +994,8 @@ MatroskaExtractor::~MatroskaExtractor() { delete mReader; mReader = NULL; + + delete mDataSource; } size_t MatroskaExtractor::countTracks() { @@ -1621,7 +1623,7 @@ uint32_t MatroskaExtractor::flags() const { } bool SniffMatroska( - DataSourceBase *source, float *confidence) { + DataSourceHelper *source, float *confidence) { DataSourceBaseReader reader(source); mkvparser::EBMLHeader ebmlHeader; long long pos; @@ -1645,15 +1647,16 @@ ExtractorDef GETEXTRACTORDEF() { 1, "Matroska Extractor", []( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - if (SniffMatroska(source, confidence)) { + DataSourceHelper helper(source); + if (SniffMatroska(&helper, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { - return wrap(new MatroskaExtractor(source));}; + return wrap(new MatroskaExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 6d6204410d..2c6ca850b2 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -36,7 +36,7 @@ struct DataSourceBaseReader; struct MatroskaSource; struct MatroskaExtractor : public MediaExtractorPluginHelper { - explicit MatroskaExtractor(DataSourceBase *source); + explicit MatroskaExtractor(DataSourceHelper *source); virtual size_t countTracks(); @@ -77,7 +77,7 @@ private: Mutex mLock; Vector mTracks; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; DataSourceBaseReader *mReader; mkvparser::Segment *mSegment; bool mExtractedThumbnails; diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index c86212c1a4..a1e5593cff 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -24,7 +24,6 @@ #include "VBRISeeker.h" #include "XINGSeeker.h" -#include #include #include #include @@ -46,7 +45,7 @@ namespace android { static const uint32_t kMask = 0xfffe0c00; static bool Resync( - DataSourceBase *source, uint32_t match_header, + DataSourceHelper *source, uint32_t match_header, off64_t *inout_pos, off64_t *post_id3_pos, uint32_t *out_header) { if (post_id3_pos != NULL) { *post_id3_pos = 0; @@ -212,7 +211,7 @@ static bool Resync( class MP3Source : public MediaTrack { public: MP3Source( - MetaDataBase &meta, DataSourceBase *source, + MetaDataBase &meta, DataSourceHelper *source, off64_t first_frame_pos, uint32_t fixed_header, MP3Seeker *seeker); @@ -230,7 +229,7 @@ protected: private: static const size_t kMaxFrameSize; MetaDataBase &mMeta; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; off64_t mFirstFramePos; uint32_t mFixedHeader; off64_t mCurrentPos; @@ -253,7 +252,7 @@ struct Mp3Meta { }; MP3Extractor::MP3Extractor( - DataSourceBase *source, Mp3Meta *meta) + DataSourceHelper *source, Mp3Meta *meta) : mInitCheck(NO_INIT), mDataSource(source), mFirstFramePos(-1), @@ -371,7 +370,8 @@ MP3Extractor::MP3Extractor( // Get iTunes-style gapless info if present. // When getting the id3 tag, skip the V1 tags to prevent the source cache // from being iterated to the end of the file. - ID3 id3(mDataSource, true); + DataSourceHelper helper(mDataSource); + ID3 id3(&helper, true); if (id3.isValid()) { ID3::Iterator *com = new ID3::Iterator(id3, "COM"); if (com->done()) { @@ -404,6 +404,7 @@ MP3Extractor::MP3Extractor( MP3Extractor::~MP3Extractor() { delete mSeeker; + delete mDataSource; } size_t MP3Extractor::countTracks() { @@ -440,7 +441,7 @@ status_t MP3Extractor::getTrackMetaData( // Set our max frame size to the nearest power of 2 above this size (aka, 4kB) const size_t MP3Source::kMaxFrameSize = (1 << 12); /* 4096 bytes */ MP3Source::MP3Source( - MetaDataBase &meta, DataSourceBase *source, + MetaDataBase &meta, DataSourceHelper *source, off64_t first_frame_pos, uint32_t fixed_header, MP3Seeker *seeker) : mMeta(meta), @@ -612,7 +613,8 @@ status_t MP3Extractor::getMetaData(MetaDataBase &meta) { } meta.setCString(kKeyMIMEType, "audio/mpeg"); - ID3 id3(mDataSource); + DataSourceHelper helper(mDataSource); + ID3 id3(&helper); if (!id3.isValid()) { return OK; @@ -670,20 +672,21 @@ status_t MP3Extractor::getMetaData(MetaDataBase &meta) { } static CMediaExtractor* CreateExtractor( - DataSourceBase *source, + CDataSource *source, void *meta) { Mp3Meta *metaData = static_cast(meta); - return wrap(new MP3Extractor(source, metaData)); + return wrap(new MP3Extractor(new DataSourceHelper(source), metaData)); } static CreatorFunc Sniff( - DataSourceBase *source, float *confidence, void **meta, + CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; off64_t post_id3_pos; uint32_t header; uint8_t mpeg_header[5]; - if (source->readAt(0, mpeg_header, sizeof(mpeg_header)) < (ssize_t)sizeof(mpeg_header)) { + DataSourceHelper helper(source); + if (helper.readAt(0, mpeg_header, sizeof(mpeg_header)) < (ssize_t)sizeof(mpeg_header)) { return NULL; } @@ -691,7 +694,7 @@ static CreatorFunc Sniff( ALOGV("MPEG1PS container is not supported!"); return NULL; } - if (!Resync(source, 0, &pos, &post_id3_pos, &header)) { + if (!Resync(&helper, 0, &pos, &post_id3_pos, &header)) { return NULL; } diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h index 89573027b2..585d9f6629 100644 --- a/media/extractors/mp3/MP3Extractor.h +++ b/media/extractors/mp3/MP3Extractor.h @@ -25,15 +25,16 @@ namespace android { +class DataSourceHelper; + struct AMessage; -class DataSourceBase; struct MP3Seeker; class String8; struct Mp3Meta; class MP3Extractor : public MediaExtractorPluginHelper { public: - MP3Extractor(DataSourceBase *source, Mp3Meta *meta); + MP3Extractor(DataSourceHelper *source, Mp3Meta *meta); ~MP3Extractor(); virtual size_t countTracks(); @@ -46,7 +47,7 @@ public: private: status_t mInitCheck; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; off64_t mFirstFramePos; MetaDataBase mMeta; uint32_t mFixedHeader; diff --git a/media/extractors/mp3/VBRISeeker.cpp b/media/extractors/mp3/VBRISeeker.cpp index 523f14cff2..9eb72a79b5 100644 --- a/media/extractors/mp3/VBRISeeker.cpp +++ b/media/extractors/mp3/VBRISeeker.cpp @@ -27,7 +27,9 @@ #include #include -#include + +#include +#include namespace android { @@ -37,7 +39,7 @@ static uint32_t U24_AT(const uint8_t *ptr) { // static VBRISeeker *VBRISeeker::CreateFromSource( - DataSourceBase *source, off64_t post_id3_pos) { + DataSourceHelper *source, off64_t post_id3_pos) { off64_t pos = post_id3_pos; uint8_t header[4]; diff --git a/media/extractors/mp3/VBRISeeker.h b/media/extractors/mp3/VBRISeeker.h index 9213f6e8c8..507899c322 100644 --- a/media/extractors/mp3/VBRISeeker.h +++ b/media/extractors/mp3/VBRISeeker.h @@ -24,11 +24,11 @@ namespace android { -class DataSourceBase; +class DataSourceHelper; struct VBRISeeker : public MP3Seeker { static VBRISeeker *CreateFromSource( - DataSourceBase *source, off64_t post_id3_pos); + DataSourceHelper *source, off64_t post_id3_pos); virtual bool getDuration(int64_t *durationUs); virtual bool getOffsetForTime(int64_t *timeUs, off64_t *pos); diff --git a/media/extractors/mp3/XINGSeeker.cpp b/media/extractors/mp3/XINGSeeker.cpp index 01e06ca51d..9f1fd7a91b 100644 --- a/media/extractors/mp3/XINGSeeker.cpp +++ b/media/extractors/mp3/XINGSeeker.cpp @@ -21,7 +21,9 @@ #include #include -#include + +#include +#include namespace android { @@ -79,7 +81,7 @@ bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) { // static XINGSeeker *XINGSeeker::CreateFromSource( - DataSourceBase *source, off64_t first_frame_pos) { + DataSourceHelper *source, off64_t first_frame_pos) { uint8_t buffer[4]; int offset = first_frame_pos; diff --git a/media/extractors/mp3/XINGSeeker.h b/media/extractors/mp3/XINGSeeker.h index 5867eaeb8f..9acee38a36 100644 --- a/media/extractors/mp3/XINGSeeker.h +++ b/media/extractors/mp3/XINGSeeker.h @@ -22,11 +22,11 @@ namespace android { -class DataSourceBase; +class DataSourceHelper; struct XINGSeeker : public MP3Seeker { static XINGSeeker *CreateFromSource( - DataSourceBase *source, off64_t first_frame_pos); + DataSourceHelper *source, off64_t first_frame_pos); virtual bool getDuration(int64_t *durationUs); virtual bool getOffsetForTime(int64_t *timeUs, off64_t *pos); diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index be442e6fec..a61e60a8c8 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -18,7 +18,8 @@ #define LOG_TAG "ItemTable" #include -#include +#include +#include #include #include #include @@ -92,7 +93,7 @@ struct ExifItem { struct Box { protected: - Box(DataSourceBase *source, uint32_t type) : + Box(DataSourceHelper *source, uint32_t type) : mDataSource(source), mType(type) {} virtual ~Box() {} @@ -104,14 +105,14 @@ protected: inline uint32_t type() const { return mType; } - inline DataSourceBase *source() const { return mDataSource; } + inline DataSourceHelper *source() const { return mDataSource; } status_t parseChunk(off64_t *offset); status_t parseChunks(off64_t offset, size_t size); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; uint32_t mType; }; @@ -186,7 +187,7 @@ status_t Box::parseChunks(off64_t offset, size_t size) { struct FullBox : public Box { protected: - FullBox(DataSourceBase *source, uint32_t type) : + FullBox(DataSourceHelper *source, uint32_t type) : Box(source, type), mVersion(0), mFlags(0) {} inline uint8_t version() const { return mVersion; } @@ -221,7 +222,7 @@ status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) { // struct PitmBox : public FullBox { - PitmBox(DataSourceBase *source) : + PitmBox(DataSourceHelper *source) : FullBox(source, FOURCC('p', 'i', 't', 'm')) {} status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId); @@ -301,7 +302,7 @@ struct ItemLoc { }; struct IlocBox : public FullBox { - IlocBox(DataSourceBase *source, KeyedVector *itemLocs) : + IlocBox(DataSourceHelper *source, KeyedVector *itemLocs) : FullBox(source, FOURCC('i', 'l', 'o', 'c')), mItemLocs(itemLocs), mHasConstructMethod1(false) {} @@ -471,7 +472,7 @@ status_t IlocBox::parse(off64_t offset, size_t size) { // struct ItemReference : public Box, public RefBase { - ItemReference(DataSourceBase *source, uint32_t type, uint32_t itemIdSize) : + ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) : Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {} status_t parse(off64_t offset, size_t size); @@ -626,7 +627,7 @@ status_t ItemReference::parse(off64_t offset, size_t size) { } struct IrefBox : public FullBox { - IrefBox(DataSourceBase *source, Vector > *itemRefs) : + IrefBox(DataSourceHelper *source, Vector > *itemRefs) : FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {} status_t parse(off64_t offset, size_t size); @@ -688,7 +689,7 @@ private: }; struct IspeBox : public FullBox, public ItemProperty { - IspeBox(DataSourceBase *source) : + IspeBox(DataSourceHelper *source) : FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {} status_t parse(off64_t offset, size_t size) override; @@ -724,7 +725,7 @@ status_t IspeBox::parse(off64_t offset, size_t size) { } struct HvccBox : public Box, public ItemProperty { - HvccBox(DataSourceBase *source) : + HvccBox(DataSourceHelper *source) : Box(source, FOURCC('h', 'v', 'c', 'C')) {} status_t parse(off64_t offset, size_t size) override; @@ -757,7 +758,7 @@ status_t HvccBox::parse(off64_t offset, size_t size) { } struct IrotBox : public Box, public ItemProperty { - IrotBox(DataSourceBase *source) : + IrotBox(DataSourceHelper *source) : Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {} status_t parse(off64_t offset, size_t size) override; @@ -786,7 +787,7 @@ status_t IrotBox::parse(off64_t offset, size_t size) { } struct ColrBox : public Box, public ItemProperty { - ColrBox(DataSourceBase *source) : + ColrBox(DataSourceHelper *source) : Box(source, FOURCC('c', 'o', 'l', 'r')) {} status_t parse(off64_t offset, size_t size) override; @@ -834,7 +835,7 @@ status_t ColrBox::parse(off64_t offset, size_t size) { } struct IpmaBox : public FullBox { - IpmaBox(DataSourceBase *source, Vector *associations) : + IpmaBox(DataSourceHelper *source, Vector *associations) : FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {} status_t parse(off64_t offset, size_t size); @@ -908,7 +909,7 @@ status_t IpmaBox::parse(off64_t offset, size_t size) { } struct IpcoBox : public Box { - IpcoBox(DataSourceBase *source, Vector > *properties) : + IpcoBox(DataSourceHelper *source, Vector > *properties) : Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {} status_t parse(off64_t offset, size_t size); @@ -965,7 +966,7 @@ status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) { } struct IprpBox : public Box { - IprpBox(DataSourceBase *source, + IprpBox(DataSourceHelper *source, Vector > *properties, Vector *associations) : Box(source, FOURCC('i', 'p', 'r', 'p')), @@ -1022,7 +1023,7 @@ struct ItemInfo { }; struct InfeBox : public FullBox { - InfeBox(DataSourceBase *source) : + InfeBox(DataSourceHelper *source) : FullBox(source, FOURCC('i', 'n', 'f', 'e')) {} status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo); @@ -1127,7 +1128,7 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { } struct IinfBox : public FullBox { - IinfBox(DataSourceBase *source, Vector *itemInfos) : + IinfBox(DataSourceHelper *source, Vector *itemInfos) : FullBox(source, FOURCC('i', 'i', 'n', 'f')), mItemInfos(itemInfos), mHasGrids(false) {} @@ -1196,7 +1197,7 @@ status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) { ////////////////////////////////////////////////////////////////// -ItemTable::ItemTable(DataSourceBase *source) +ItemTable::ItemTable(DataSourceHelper *source) : mDataSource(source), mPrimaryItemId(0), mIdatOffset(0), diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h index 536dcb0faf..650b3f3be1 100644 --- a/media/extractors/mp4/ItemTable.h +++ b/media/extractors/mp4/ItemTable.h @@ -25,7 +25,7 @@ namespace android { -class DataSourceBase; +class DataSourceHelper; class MetaData; namespace heif { @@ -45,7 +45,7 @@ struct ItemReference; class ItemTable : public RefBase { public: - explicit ItemTable(DataSourceBase *source); + explicit ItemTable(DataSourceHelper *source); status_t parse(uint32_t type, off64_t offset, size_t size); @@ -62,7 +62,7 @@ protected: ~ItemTable(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; KeyedVector mItemLocs; Vector mItemInfos; diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 0cb4e6b17d..b6b0ae2efc 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -32,6 +32,7 @@ #include "ItemTable.h" #include "include/ESDS.h" +#include #include #include #include @@ -72,7 +73,7 @@ static const size_t kMaxPcmFrameSize = 8192; public: // Caller retains ownership of both "dataSource" and "sampleTable". MPEG4Source(MetaDataBase &format, - DataSourceBase *dataSource, + DataSourceHelper *dataSource, int32_t timeScale, const sp &sampleTable, Vector &sidx, @@ -96,7 +97,7 @@ private: Mutex mLock; MetaDataBase &mFormat; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; int32_t mTimescale; sp mSampleTable; uint32_t mCurrentSampleIndex; @@ -197,11 +198,10 @@ private: // possibly wrapping multiple times to cover all tracks, i.e. // Each CachedRangedDataSource caches the sampletable metadata for a single track. -struct CachedRangedDataSource : public DataSourceBase { - explicit CachedRangedDataSource(DataSourceBase *source); +struct CachedRangedDataSource : public DataSourceHelper { + explicit CachedRangedDataSource(DataSourceHelper *source); virtual ~CachedRangedDataSource(); - virtual status_t initCheck() const; virtual ssize_t readAt(off64_t offset, void *data, size_t size); virtual status_t getSize(off64_t *size); virtual uint32_t flags(); @@ -212,7 +212,7 @@ struct CachedRangedDataSource : public DataSourceBase { private: Mutex mLock; - DataSourceBase *mSource; + DataSourceHelper *mSource; bool mOwnsDataSource; off64_t mCachedOffset; size_t mCachedSize; @@ -224,8 +224,9 @@ private: CachedRangedDataSource &operator=(const CachedRangedDataSource &); }; -CachedRangedDataSource::CachedRangedDataSource(DataSourceBase *source) - : mSource(source), +CachedRangedDataSource::CachedRangedDataSource(DataSourceHelper *source) + : DataSourceHelper(source), + mSource(source), mOwnsDataSource(false), mCachedOffset(0), mCachedSize(0), @@ -249,10 +250,6 @@ void CachedRangedDataSource::clearCache() { mCachedSize = 0; } -status_t CachedRangedDataSource::initCheck() const { - return mSource->initCheck(); -} - ssize_t CachedRangedDataSource::readAt(off64_t offset, void *data, size_t size) { Mutex::Autolock autoLock(mLock); @@ -364,11 +361,11 @@ static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t return false; } -MPEG4Extractor::MPEG4Extractor(DataSourceBase *source, const char *mime) +MPEG4Extractor::MPEG4Extractor(DataSourceHelper *source, const char *mime) : mMoofOffset(0), mMoofFound(false), mMdatFound(false), - mDataSource(source), + mDataSource(new DataSourceHelper(source)), mCachedSource(NULL), mInitCheck(NO_INIT), mHeaderTimescale(0), @@ -397,6 +394,7 @@ MPEG4Extractor::~MPEG4Extractor() { mPssh.clear(); delete mCachedSource; + delete mDataSource; } uint32_t MPEG4Extractor::flags() const { @@ -4093,7 +4091,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( MPEG4Source::MPEG4Source( MetaDataBase &format, - DataSourceBase *dataSource, + DataSourceHelper *dataSource, int32_t timeScale, const sp &sampleTable, Vector &sidx, @@ -5772,7 +5770,7 @@ MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix( return NULL; } -static bool LegacySniffMPEG4(DataSourceBase *source, float *confidence) { +static bool LegacySniffMPEG4(DataSourceHelper *source, float *confidence) { uint8_t header[8]; ssize_t n = source->readAt(4, header, sizeof(header)); @@ -5837,7 +5835,7 @@ static bool isCompatibleBrand(uint32_t fourcc) { // Also try to identify where this file's metadata ends // (end of the 'moov' atom) and report it to the caller as part of // the metadata. -static bool BetterSniffMPEG4(DataSourceBase *source, float *confidence) { +static bool BetterSniffMPEG4(DataSourceHelper *source, float *confidence) { // We scan up to 128 bytes to identify this file as an MP4. static const off64_t kMaxScanOffset = 128ll; @@ -5944,18 +5942,19 @@ static bool BetterSniffMPEG4(DataSourceBase *source, float *confidence) { return true; } -static CMediaExtractor* CreateExtractor(DataSourceBase *source, void *) { - return wrap(new MPEG4Extractor(source)); +static CMediaExtractor* CreateExtractor(CDataSource *source, void *) { + return wrap(new MPEG4Extractor(new DataSourceHelper(source))); } static CreatorFunc Sniff( - DataSourceBase *source, float *confidence, void **, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) { - if (BetterSniffMPEG4(source, confidence)) { + DataSourceHelper helper(source); + if (BetterSniffMPEG4(&helper, confidence)) { return CreateExtractor; } - if (LegacySniffMPEG4(source, confidence)) { + if (LegacySniffMPEG4(&helper, confidence)) { ALOGW("Identified supported mpeg4 through LegacySniffMPEG4."); return CreateExtractor; } diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 561b567421..ca273e082c 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -20,7 +20,6 @@ #include -#include #include #include #include @@ -32,7 +31,8 @@ namespace android { struct AMessage; -class DataSourceBase; +struct CDataSource; +class DataSourceHelper; struct CachedRangedDataSource; class SampleTable; class String8; @@ -56,7 +56,7 @@ struct Trex { class MPEG4Extractor : public MediaExtractorPluginHelper { public: - explicit MPEG4Extractor(DataSourceBase *source, const char *mime = NULL); + explicit MPEG4Extractor(DataSourceHelper *source, const char *mime = NULL); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -98,7 +98,7 @@ private: Vector mTrex; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; CachedRangedDataSource *mCachedSource; status_t mInitCheck; uint32_t mHeaderTimescale; @@ -150,10 +150,6 @@ private: MPEG4Extractor &operator=(const MPEG4Extractor &); }; -bool SniffMPEG4( - DataSourceBase *source, String8 *mimeType, float *confidence, - sp *); - } // namespace android #endif // MPEG4_EXTRACTOR_H_ diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp index 28fe7177c3..d242798c73 100644 --- a/media/extractors/mp4/SampleTable.cpp +++ b/media/extractors/mp4/SampleTable.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include @@ -114,7 +114,7 @@ int32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset( //////////////////////////////////////////////////////////////////////////////// -SampleTable::SampleTable(DataSourceBase *source) +SampleTable::SampleTable(DataSourceHelper *source) : mDataSource(source), mChunkOffsetOffset(-1), mChunkOffsetType(0), diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/SampleTable.h index dd688601b4..d4b5dc84c1 100644 --- a/media/extractors/mp4/SampleTable.h +++ b/media/extractors/mp4/SampleTable.h @@ -21,18 +21,19 @@ #include #include +#include #include #include #include namespace android { -class DataSourceBase; +class DataSourceHelper; struct SampleIterator; class SampleTable : public RefBase { public: - explicit SampleTable(DataSourceBase *source); + explicit SampleTable(DataSourceHelper *source); bool isValid() const; @@ -102,7 +103,7 @@ private: // Limit the total size of all internal tables to 200MiB. static const size_t kMaxTotalSize = 200 * (1 << 20); - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; Mutex mLock; off64_t mChunkOffsetOffset; diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index 3045aa9293..ff40ed4c18 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -19,11 +19,14 @@ #include #include +#include #include "MPEG2PSExtractor.h" #include "MPEG2TSExtractor.h" namespace android { +struct CDataSource; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -34,20 +37,21 @@ ExtractorDef GETEXTRACTORDEF() { 1, "MPEG2-PS/TS Extractor", []( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - if (SniffMPEG2TS(source, confidence)) { + DataSourceHelper helper(source); + if (SniffMPEG2TS(&helper, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { - return wrap(new MPEG2TSExtractor(source));}; - } else if (SniffMPEG2PS(source, confidence)) { + return wrap(new MPEG2TSExtractor(new DataSourceHelper(source)));}; + } else if (SniffMPEG2PS(&helper, confidence)) { return []( - DataSourceBase *source, + CDataSource *source, void *) -> CMediaExtractor* { - return wrap(new MPEG2PSExtractor(source));}; + return wrap(new MPEG2PSExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index 6980b82185..ae1e6ba73c 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -94,7 +94,7 @@ private: //////////////////////////////////////////////////////////////////////////////// -MPEG2PSExtractor::MPEG2PSExtractor(DataSourceBase *source) +MPEG2PSExtractor::MPEG2PSExtractor(DataSourceHelper *source) : mDataSource(source), mOffset(0), mFinalResult(OK), @@ -120,6 +120,7 @@ MPEG2PSExtractor::MPEG2PSExtractor(DataSourceBase *source) } MPEG2PSExtractor::~MPEG2PSExtractor() { + delete mDataSource; } size_t MPEG2PSExtractor::countTracks() { @@ -754,7 +755,7 @@ status_t MPEG2PSExtractor::WrappedTrack::read( //////////////////////////////////////////////////////////////////////////////// bool SniffMPEG2PS( - DataSourceBase *source, float *confidence) { + DataSourceHelper *source, float *confidence) { uint8_t header[5]; if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { return false; diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h index 93827faa4b..7689910022 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.h +++ b/media/extractors/mpeg2/MPEG2PSExtractor.h @@ -33,7 +33,7 @@ struct Track; class String8; struct MPEG2PSExtractor : public MediaExtractorPluginHelper { - explicit MPEG2PSExtractor(DataSourceBase *source); + explicit MPEG2PSExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -52,7 +52,7 @@ private: struct WrappedTrack; mutable Mutex mLock; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; off64_t mOffset; status_t mFinalResult; @@ -73,7 +73,7 @@ private: DISALLOW_EVIL_CONSTRUCTORS(MPEG2PSExtractor); }; -bool SniffMPEG2PS(DataSourceBase *source, float *confidence); +bool SniffMPEG2PS(DataSourceHelper *source, float *confidence); } // namespace android diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index c83f7ce65c..cbe85564c4 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -123,7 +123,7 @@ status_t MPEG2TSSource::read( //////////////////////////////////////////////////////////////////////////////// -MPEG2TSExtractor::MPEG2TSExtractor(DataSourceBase *source) +MPEG2TSExtractor::MPEG2TSExtractor(DataSourceHelper *source) : mDataSource(source), mParser(new ATSParser), mLastSyncEvent(0), @@ -131,6 +131,10 @@ MPEG2TSExtractor::MPEG2TSExtractor(DataSourceBase *source) init(); } +MPEG2TSExtractor::~MPEG2TSExtractor() { + delete mDataSource; +} + size_t MPEG2TSExtractor::countTracks() { return mSourceImpls.size(); } @@ -652,7 +656,7 @@ status_t MPEG2TSExtractor::feedUntilBufferAvailable( //////////////////////////////////////////////////////////////////////////////// -bool SniffMPEG2TS(DataSourceBase *source, float *confidence) { +bool SniffMPEG2TS(DataSourceHelper *source, float *confidence) { for (int i = 0; i < 5; ++i) { char header; if (source->readAt(kTSPacketSize * i, &header, 1) != 1 diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index 05e57c50de..cdaede326c 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -35,12 +35,12 @@ namespace android { struct AMessage; struct AnotherPacketSource; struct ATSParser; -class DataSourceBase; +struct CDataSource; struct MPEG2TSSource; class String8; struct MPEG2TSExtractor : public MediaExtractorPluginHelper { - explicit MPEG2TSExtractor(DataSourceBase *source); + explicit MPEG2TSExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -53,12 +53,15 @@ struct MPEG2TSExtractor : public MediaExtractorPluginHelper { virtual uint32_t flags() const; virtual const char * name() { return "MPEG2TSExtractor"; } +protected: + virtual ~MPEG2TSExtractor(); + private: friend struct MPEG2TSSource; mutable Mutex mLock; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; sp mParser; @@ -82,9 +85,9 @@ private: // Try to feed more data from source to parser. // |isInit| means this function is called inside init(). This is a signal to // save SyncEvent so that init() can add SyncPoint after it updates |mSourceImpls|. - // This function returns OK if expected amount of data is fed from DataSourceBase to + // This function returns OK if expected amount of data is fed from DataSourceHelper to // parser and is successfully parsed. Otherwise, various error codes could be - // returned, e.g., ERROR_END_OF_STREAM, or no data availalbe from DataSourceBase, or + // returned, e.g., ERROR_END_OF_STREAM, or no data availalbe from DataSourceHelper, or // the data has syntax error during parsing, etc. status_t feedMore(bool isInit = false); status_t seek(int64_t seekTimeUs, @@ -102,7 +105,7 @@ private: DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor); }; -bool SniffMPEG2TS(DataSourceBase *source, float *confidence); +bool SniffMPEG2TS(DataSourceHelper *source, float *confidence); } // namespace android diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index efacfb3d5d..d46ace4932 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -71,7 +71,7 @@ private: struct MyOggExtractor { MyOggExtractor( - DataSourceBase *source, + DataSourceHelper *source, const char *mimeType, size_t numHeaders, int64_t seekPreRollUs); @@ -110,7 +110,7 @@ protected: int64_t mTimeUs; }; - DataSourceBase *mSource; + DataSourceHelper *mSource; off64_t mOffset; Page mCurrentPage; uint64_t mCurGranulePosition; @@ -169,7 +169,7 @@ protected: }; struct MyVorbisExtractor : public MyOggExtractor { - explicit MyVorbisExtractor(DataSourceBase *source) + explicit MyVorbisExtractor(DataSourceHelper *source) : MyOggExtractor(source, MEDIA_MIMETYPE_AUDIO_VORBIS, /* numHeaders */ 3, @@ -197,7 +197,7 @@ struct MyOpusExtractor : public MyOggExtractor { static const int32_t kOpusSampleRate = 48000; static const int64_t kOpusSeekPreRollUs = 80000; // 80 ms - explicit MyOpusExtractor(DataSourceBase *source) + explicit MyOpusExtractor(DataSourceHelper *source) : MyOggExtractor(source, MEDIA_MIMETYPE_AUDIO_OPUS, /*numHeaders*/ 2, kOpusSeekPreRollUs), mChannelCount(0), mCodecDelay(0), @@ -296,7 +296,7 @@ status_t OggSource::read( //////////////////////////////////////////////////////////////////////////////// MyOggExtractor::MyOggExtractor( - DataSourceBase *source, + DataSourceHelper *source, const char *mimeType, size_t numHeaders, int64_t seekPreRollUs) @@ -1193,7 +1193,7 @@ void MyOggExtractor::parseFileMetaData() { //////////////////////////////////////////////////////////////////////////////// -OggExtractor::OggExtractor(DataSourceBase *source) +OggExtractor::OggExtractor(DataSourceHelper *source) : mDataSource(source), mInitCheck(NO_INIT), mImpl(NULL) { @@ -1249,18 +1249,19 @@ status_t OggExtractor::getMetaData(MetaDataBase &meta) { } static CMediaExtractor* CreateExtractor( - DataSourceBase *source, + CDataSource *source, void *) { - return wrap(new OggExtractor(source)); + return wrap(new OggExtractor(new DataSourceHelper(source))); } static CreatorFunc Sniff( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) { + DataSourceHelper helper(source); char tmp[4]; - if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) { + if (helper.readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) { return NULL; } diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h index 94f857a08d..fbd466362d 100644 --- a/media/extractors/ogg/OggExtractor.h +++ b/media/extractors/ogg/OggExtractor.h @@ -25,14 +25,13 @@ namespace android { struct AMessage; -class DataSourceBase; class String8; struct MyOggExtractor; struct OggSource; struct OggExtractor : public MediaExtractorPluginHelper { - explicit OggExtractor(DataSourceBase *source); + explicit OggExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -47,7 +46,7 @@ protected: private: friend struct OggSource; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; status_t mInitCheck; MyOggExtractor *mImpl; diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 903de524ae..80dc9c8ce5 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -57,7 +57,7 @@ static uint16_t U16_LE_AT(const uint8_t *ptr) { struct WAVSource : public MediaTrack { WAVSource( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, MetaDataBase &meta, uint16_t waveFormat, int32_t bitsPerSample, @@ -78,7 +78,7 @@ protected: private: static const size_t kMaxFrameSize; - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; MetaDataBase &mMeta; uint16_t mWaveFormat; int32_t mSampleRate; @@ -94,7 +94,7 @@ private: WAVSource &operator=(const WAVSource &); }; -WAVExtractor::WAVExtractor(DataSourceBase *source) +WAVExtractor::WAVExtractor(DataSourceHelper *source) : mDataSource(source), mValidFormat(false), mChannelMask(CHANNEL_MASK_USE_CHANNEL_ORDER) { @@ -102,6 +102,7 @@ WAVExtractor::WAVExtractor(DataSourceBase *source) } WAVExtractor::~WAVExtractor() { + delete mDataSource; } status_t WAVExtractor::getMetaData(MetaDataBase &meta) { @@ -347,7 +348,7 @@ status_t WAVExtractor::init() { const size_t WAVSource::kMaxFrameSize = 32768; WAVSource::WAVSource( - DataSourceBase *dataSource, + DataSourceHelper *dataSource, MetaDataBase &meta, uint16_t waveFormat, int32_t bitsPerSample, @@ -545,33 +546,38 @@ status_t WAVSource::read( //////////////////////////////////////////////////////////////////////////////// static CMediaExtractor* CreateExtractor( - DataSourceBase *source, + CDataSource *source, void *) { - return wrap(new WAVExtractor(source)); + return wrap(new WAVExtractor(new DataSourceHelper(source))); } static CreatorFunc Sniff( - DataSourceBase *source, + CDataSource *source, float *confidence, void **, FreeMetaFunc *) { + DataSourceHelper *helper = new DataSourceHelper(source); char header[12]; - if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { + if (helper->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { + delete helper; return NULL; } if (memcmp(header, "RIFF", 4) || memcmp(&header[8], "WAVE", 4)) { + delete helper; return NULL; } - WAVExtractor *extractor = new WAVExtractor(source); + WAVExtractor *extractor = new WAVExtractor(helper); int numTracks = extractor->countTracks(); delete extractor; if (numTracks == 0) { + delete helper; return NULL; } *confidence = 0.3f; + delete helper; return CreateExtractor; } diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h index 519a730e10..5136aa8684 100644 --- a/media/extractors/wav/WAVExtractor.h +++ b/media/extractors/wav/WAVExtractor.h @@ -26,12 +26,12 @@ namespace android { struct AMessage; -class DataSourceBase; +struct CDataSource; class String8; class WAVExtractor : public MediaExtractorPluginHelper { public: - explicit WAVExtractor(DataSourceBase *source); + explicit WAVExtractor(DataSourceHelper *source); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); @@ -43,7 +43,7 @@ public: virtual ~WAVExtractor(); private: - DataSourceBase *mDataSource; + DataSourceHelper *mDataSource; status_t mInitCheck; bool mValidFormat; uint16_t mWaveFormat; diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp index 5ca3b48373..1150d61c77 100644 --- a/media/libmedia/MidiIoWrapper.cpp +++ b/media/libmedia/MidiIoWrapper.cpp @@ -23,6 +23,7 @@ #include #include +#include static int readAt(void *handle, void *buffer, int pos, int size) { return ((android::MidiIoWrapper*)handle)->readAt(buffer, pos, size); @@ -61,6 +62,51 @@ MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) { } } +class DataSourceUnwrapper : public DataSourceBase { + +public: + explicit DataSourceUnwrapper(CDataSource *csource) { + mSource = csource; + } + virtual status_t initCheck() const { return OK; } + + // Returns the number of bytes read, or -1 on failure. It's not an error if + // this returns zero; it just means the given offset is equal to, or + // beyond, the end of the source. + virtual ssize_t readAt(off64_t offset, void *data, size_t size) { + return mSource->readAt(mSource->handle, offset, data, size); + } + + // May return ERROR_UNSUPPORTED. + virtual status_t getSize(off64_t *size) { + return mSource->getSize(mSource->handle, size); + } + + virtual bool getUri(char * /*uriString*/, size_t /*bufferSize*/) { + return false; + } + + virtual uint32_t flags() { + return 0; + } + + virtual void close() {}; +private: + CDataSource *mSource; +}; + +MidiIoWrapper::MidiIoWrapper(CDataSource *csource) { + ALOGV("MidiIoWrapper(CDataSource)"); + mFd = -1; + mDataSource = new DataSourceUnwrapper(csource); + off64_t l; + if (mDataSource->getSize(&l) == OK) { + mLength = l; + } else { + mLength = 0; + } +} + MidiIoWrapper::~MidiIoWrapper() { ALOGV("~MidiIoWrapper"); if (mFd >= 0) { diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h index b5e565ef60..6309dda53b 100644 --- a/media/libmedia/include/media/MidiIoWrapper.h +++ b/media/libmedia/include/media/MidiIoWrapper.h @@ -23,11 +23,14 @@ namespace android { +struct CDataSource; + class MidiIoWrapper { public: explicit MidiIoWrapper(const char *path); explicit MidiIoWrapper(int fd, off64_t offset, int64_t size); explicit MidiIoWrapper(DataSourceBase *source); + explicit MidiIoWrapper(CDataSource *csource); ~MidiIoWrapper(); diff --git a/media/libmediaextractor/include/media/DataSource.h b/media/libmediaextractor/include/media/DataSource.h index 0e59f39e42..cb96ff5d4c 100644 --- a/media/libmediaextractor/include/media/DataSource.h +++ b/media/libmediaextractor/include/media/DataSource.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,7 @@ class String8; class DataSource : public DataSourceBase, public virtual RefBase { public: - DataSource() {} + DataSource() : mWrapper(NULL) {} // returns a pointer to IDataSource if it is wrapped. virtual sp getIDataSource() const { @@ -69,10 +70,35 @@ public: return String8("application/octet-stream"); } + CDataSource *wrap() { + if (mWrapper) { + return mWrapper; + } + mWrapper = new CDataSource(); + mWrapper->handle = this; + + mWrapper->readAt = [](void *handle, off64_t offset, void *data, size_t size) -> ssize_t { + return ((DataSource*)handle)->readAt(offset, data, size); + }; + mWrapper->getSize = [](void *handle, off64_t *size) -> status_t { + return ((DataSource*)handle)->getSize(size); + }; + mWrapper->flags = [](void *handle) -> uint32_t { + return ((DataSource*)handle)->flags(); + }; + mWrapper->getUri = [](void *handle, char *uriString, size_t bufferSize) -> bool { + return ((DataSource*)handle)->getUri(uriString, bufferSize); + }; + return mWrapper; + } + protected: - virtual ~DataSource() {} + virtual ~DataSource() { + delete mWrapper; + } private: + CDataSource *mWrapper; DataSource(const DataSource &); DataSource &operator=(const DataSource &); }; diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index dd98fc0a41..6e361497e8 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -78,13 +78,13 @@ sp MediaExtractorFactory::CreateFromService( FreeMetaFunc freeMeta = nullptr; float confidence; sp plugin; - creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin); + creator = sniff(source, &confidence, &meta, &freeMeta, plugin); if (!creator) { ALOGV("FAILED to autodetect media content."); return NULL; } - CMediaExtractor *ret = creator(source.get(), meta); + CMediaExtractor *ret = creator(source->wrap(), meta); if (meta != nullptr && freeMeta != nullptr) { freeMeta(meta); } @@ -129,7 +129,7 @@ bool MediaExtractorFactory::gPluginsRegistered = false; // static CreatorFunc MediaExtractorFactory::sniff( - DataSourceBase *source, float *confidence, void **meta, + const sp &source, float *confidence, void **meta, FreeMetaFunc *freeMeta, sp &plugin) { *confidence = 0.0f; *meta = nullptr; @@ -150,7 +150,7 @@ CreatorFunc MediaExtractorFactory::sniff( float newConfidence; void *newMeta = nullptr; FreeMetaFunc newFreeMeta = nullptr; - if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) { + if ((curCreator = (*it)->def.sniff(source->wrap(), &newConfidence, &newMeta, &newFreeMeta))) { if (newConfidence > *confidence) { *confidence = newConfidence; if (*meta != nullptr && *freeMeta != nullptr) { diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index a0a62f4106..b489183a37 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -21,6 +21,8 @@ #include "../include/ID3.h" #include +#include +#include #include #include #include @@ -56,6 +58,55 @@ private: DISALLOW_EVIL_CONSTRUCTORS(MemorySource); }; +class DataSourceUnwrapper : public DataSourceBase { + +public: + explicit DataSourceUnwrapper(DataSourceHelper *sourcehelper) { + mSource = sourcehelper; + } + virtual status_t initCheck() const { return OK; } + + // Returns the number of bytes read, or -1 on failure. It's not an error if + // this returns zero; it just means the given offset is equal to, or + // beyond, the end of the source. + virtual ssize_t readAt(off64_t offset, void *data, size_t size) { + return mSource->readAt(offset, data, size); + } + + // May return ERROR_UNSUPPORTED. + virtual status_t getSize(off64_t *size) { + return mSource->getSize(size); + } + + virtual bool getUri(char * /*uriString*/, size_t /*bufferSize*/) { + return false; + } + + virtual uint32_t flags() { + return 0; + } + + virtual void close() {}; +private: + DataSourceHelper *mSource; +}; + + +ID3::ID3(DataSourceHelper *sourcehelper, bool ignoreV1, off64_t offset) + : mIsValid(false), + mData(NULL), + mSize(0), + mFirstFrameOffset(0), + mVersion(ID3_UNKNOWN), + mRawSize(0) { + DataSourceUnwrapper source(sourcehelper); + mIsValid = parseV2(&source, offset); + + if (!mIsValid && !ignoreV1) { + mIsValid = parseV1(&source); + } +} + ID3::ID3(DataSourceBase *source, bool ignoreV1, off64_t offset) : mIsValid(false), mData(NULL), diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 7c2391e1f1..5e433ea699 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -24,6 +24,7 @@ namespace android { class DataSourceBase; class String8; +class DataSourceHelper; struct ID3 { enum Version { @@ -35,6 +36,7 @@ struct ID3 { ID3_V2_4, }; + explicit ID3(DataSourceHelper *source, bool ignoreV1 = false, off64_t offset = 0); explicit ID3(DataSourceBase *source, bool ignoreV1 = false, off64_t offset = 0); ID3(const uint8_t *data, size_t size, bool ignoreV1 = false); ~ID3(); diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index 3920cccef3..4ee55565e3 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -49,7 +49,7 @@ private: static void RegisterExtractor( const sp &plugin, std::list> &pluginList); - static CreatorFunc sniff(DataSourceBase *source, + static CreatorFunc sniff(const sp &source, float *confidence, void **meta, FreeMetaFunc *freeMeta, sp &plugin); -- GitLab From bb6e414ae835f08083de839bae0ca284e1613b18 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Tue, 21 Aug 2018 14:17:40 -0700 Subject: [PATCH 0216/1530] CameraService: Enable presubmit unit tests, and fix them - Add cameraservice_test to presubmit TEST_MAPPING - Fix broken CameraProviderManager test Test: Fixed test passes; 'atest' in frameworks/av/services/camera/libcameraservice runs the expected test Change-Id: Ia1ceaf526884325d56f0e273f89220fdb33493dd --- services/camera/libcameraservice/TEST_MAPPING | 7 ++++++ .../tests/CameraProviderManagerTest.cpp | 23 +++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 services/camera/libcameraservice/TEST_MAPPING diff --git a/services/camera/libcameraservice/TEST_MAPPING b/services/camera/libcameraservice/TEST_MAPPING new file mode 100644 index 0000000000..6fdac68fa0 --- /dev/null +++ b/services/camera/libcameraservice/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "cameraservice_test" + } + ] +} diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp index ef93d9a9ca..0086c6c5de 100644 --- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp +++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp @@ -163,7 +163,7 @@ struct TestInteractionProxy : public CameraProviderManager::ServiceInteractionPr mTestCameraProvider = provider; } - std::string mLastRequestedServiceName; + std::vector mLastRequestedServiceNames; virtual ~TestInteractionProxy() {} @@ -177,7 +177,7 @@ struct TestInteractionProxy : public CameraProviderManager::ServiceInteractionPr virtual sp getService( const std::string &serviceName) override { - mLastRequestedServiceName = serviceName; + mLastRequestedServiceNames.push_back(serviceName); return mTestCameraProvider; } @@ -210,9 +210,18 @@ TEST(CameraProviderManagerTest, InitializeTest) { res = providerManager->initialize(statusListener, &serviceProxy); ASSERT_EQ(res, OK) << "Unable to initialize provider manager"; - hardware::hidl_string legacyInstanceName = "legacy/0"; - ASSERT_EQ(serviceProxy.mLastRequestedServiceName, legacyInstanceName) << + std::string legacyInstanceName = "legacy/0"; + std::string externalInstanceName = "external/0"; + bool gotLegacy = false; + bool gotExternal = false; + for (auto& serviceName : serviceProxy.mLastRequestedServiceNames) { + if (serviceName == legacyInstanceName) gotLegacy = true; + if (serviceName == externalInstanceName) gotExternal = true; + } + ASSERT_TRUE(gotLegacy) << "Legacy instance not requested from service manager"; + ASSERT_TRUE(gotExternal) << + "External instance not requested from service manager"; hardware::hidl_string testProviderFqInterfaceName = "android.hardware.camera.provider@2.4::ICameraProvider"; @@ -221,7 +230,7 @@ TEST(CameraProviderManagerTest, InitializeTest) { testProviderFqInterfaceName, testProviderInstanceName, false); - ASSERT_EQ(serviceProxy.mLastRequestedServiceName, testProviderInstanceName) << + ASSERT_EQ(serviceProxy.mLastRequestedServiceNames.back(), testProviderInstanceName) << "Incorrect instance requested from service manager"; } @@ -255,7 +264,7 @@ TEST(CameraProviderManagerTest, MultipleVendorTagTest) { "android.hardware.camera.provider@2.4::ICameraProvider"; serviceProxy.mManagerNotificationInterface->onRegistration( testProviderFqInterfaceName, testProviderInstanceName, false); - ASSERT_EQ(serviceProxy.mLastRequestedServiceName, testProviderInstanceName) << + ASSERT_EQ(serviceProxy.mLastRequestedServiceNames.back(), testProviderInstanceName) << "Incorrect instance requested from service manager"; hardware::hidl_string sectionNameSecond = "SecondVendorTestSection"; @@ -273,7 +282,7 @@ TEST(CameraProviderManagerTest, MultipleVendorTagTest) { hardware::hidl_string testProviderSecondInstanceName = "test2/0"; serviceProxy.mManagerNotificationInterface->onRegistration( testProviderFqInterfaceName, testProviderSecondInstanceName, false); - ASSERT_EQ(serviceProxy.mLastRequestedServiceName, + ASSERT_EQ(serviceProxy.mLastRequestedServiceNames.back(), testProviderSecondInstanceName) << "Incorrect instance requested from service manager"; -- GitLab From 4ead95ed6ecbeed8fe7b9b5433008ca300d20083 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 21 Aug 2018 15:10:37 -0700 Subject: [PATCH 0217/1530] Address comments on previous CL Bug: 111407253 Test: build, boot, play some files Change-Id: Iba77e07057ae62af117ff78d8d67c0f830572cf7 --- media/extractors/mp4/MPEG4Extractor.cpp | 2 +- media/extractors/ogg/OggExtractor.cpp | 1 + media/extractors/wav/WAVExtractor.cpp | 4 +--- media/libstagefright/MediaExtractorFactory.cpp | 3 ++- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index b6b0ae2efc..c02d2fb10c 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -365,7 +365,7 @@ MPEG4Extractor::MPEG4Extractor(DataSourceHelper *source, const char *mime) : mMoofOffset(0), mMoofFound(false), mMdatFound(false), - mDataSource(new DataSourceHelper(source)), + mDataSource(source), mCachedSource(NULL), mInitCheck(NO_INIT), mHeaderTimescale(0), diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index d46ace4932..4e979219a2 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -1220,6 +1220,7 @@ OggExtractor::OggExtractor(DataSourceHelper *source) OggExtractor::~OggExtractor() { delete mImpl; mImpl = NULL; + delete mDataSource; } size_t OggExtractor::countTracks() { diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 80dc9c8ce5..c739c2a7bf 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -568,16 +568,14 @@ static CreatorFunc Sniff( return NULL; } - WAVExtractor *extractor = new WAVExtractor(helper); + WAVExtractor *extractor = new WAVExtractor(helper); // extractor owns the helper int numTracks = extractor->countTracks(); delete extractor; if (numTracks == 0) { - delete helper; return NULL; } *confidence = 0.3f; - delete helper; return CreateExtractor; } diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 6e361497e8..7578b1e3e1 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -150,7 +150,8 @@ CreatorFunc MediaExtractorFactory::sniff( float newConfidence; void *newMeta = nullptr; FreeMetaFunc newFreeMeta = nullptr; - if ((curCreator = (*it)->def.sniff(source->wrap(), &newConfidence, &newMeta, &newFreeMeta))) { + if ((curCreator = (*it)->def.sniff( + source->wrap(), &newConfidence, &newMeta, &newFreeMeta))) { if (newConfidence > *confidence) { *confidence = newConfidence; if (*meta != nullptr && *freeMeta != nullptr) { -- GitLab From 685dd472a9880631e97bd5f88d278da8cd9710e8 Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Tue, 21 Aug 2018 15:13:36 -0700 Subject: [PATCH 0218/1530] Get screenrecord to exclude black cutout Bug: b/112869712 Test: adb shell screenrecord Change-Id: I69b497b81cfca93a8ad9ed46f25cdbb5dda81a89 --- cmds/screenrecord/screenrecord.cpp | 80 ++++++++++++++++++------------ 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index a7ac2d7c55..c559ff90d0 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -139,14 +139,6 @@ static status_t configureSignals() { return NO_ERROR; } -/* - * Returns "true" if the device is rotated 90 degrees. - */ -static bool isDeviceRotated(int orientation) { - return orientation != DISPLAY_ORIENTATION_0 && - orientation != DISPLAY_ORIENTATION_180; -} - /* * Configures and starts the MediaCodec encoder. Obtains an input surface * from the codec. @@ -242,22 +234,11 @@ static status_t setDisplayProjection( const DisplayInfo& mainDpyInfo) { // Set the region of the layer stack we're interested in, which in our - // case is "all of it". If the app is rotated (so that the width of the - // app is based on the height of the display), reverse width/height. - bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation); - uint32_t sourceWidth, sourceHeight; - if (!deviceRotated) { - sourceWidth = mainDpyInfo.w; - sourceHeight = mainDpyInfo.h; - } else { - ALOGV("using rotated width/height"); - sourceHeight = mainDpyInfo.w; - sourceWidth = mainDpyInfo.h; - } - Rect layerStackRect(sourceWidth, sourceHeight); + // case is "all of it". + Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h); // We need to preserve the aspect ratio of the display. - float displayAspect = (float) sourceHeight / (float) sourceWidth; + float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w; // Set the way we map the output onto the display surface (which will @@ -333,6 +314,22 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, return NO_ERROR; } +/* + * Set the main display width and height to the actual width and height + */ +static status_t getActualDisplaySize(const sp& mainDpy, DisplayInfo* mainDpyInfo) { + Rect viewport; + status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: unable to get display viewport\n"); + return err; + } + mainDpyInfo->w = viewport.width(); + mainDpyInfo->h = viewport.height(); + + return NO_ERROR; +} + /* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger @@ -403,14 +400,22 @@ static status_t runEncoder(const sp& encoder, // useful stuff is hard to get at without a Dalvik VM. err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); - if (err != NO_ERROR) { + if (err == NO_ERROR) { + err = getActualDisplaySize(mainDpy, &mainDpyInfo); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: unable to set actual display size\n"); + return err; + } + + if (orientation != mainDpyInfo.orientation) { + ALOGD("orientation changed, now %d", mainDpyInfo.orientation); + SurfaceComposerClient::Transaction t; + setDisplayProjection(t, virtualDpy, mainDpyInfo); + t.apply(); + orientation = mainDpyInfo.orientation; + } + } else { ALOGW("getDisplayInfo(main) failed: %d", err); - } else if (orientation != mainDpyInfo.orientation) { - ALOGD("orientation changed, now %d", mainDpyInfo.orientation); - SurfaceComposerClient::Transaction t; - setDisplayProjection(t, virtualDpy, mainDpyInfo); - t.apply(); - orientation = mainDpyInfo.orientation; } } @@ -552,6 +557,10 @@ static FILE* prepareRawOutput(const char* fileName) { return rawFp; } +static inline uint32_t floorToEven(uint32_t num) { + return num & ~1; +} + /* * Main "do work" start point. * @@ -579,6 +588,13 @@ static status_t recordScreen(const char* fileName) { fprintf(stderr, "ERROR: unable to get display characteristics\n"); return err; } + + err = getActualDisplaySize(mainDpy, &mainDpyInfo); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: unable to set actual display size\n"); + return err; + } + if (gVerbose) { printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps, @@ -586,12 +602,12 @@ static status_t recordScreen(const char* fileName) { fflush(stdout); } - bool rotated = isDeviceRotated(mainDpyInfo.orientation); + // Encoder can't take odd number as config if (gVideoWidth == 0) { - gVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w; + gVideoWidth = floorToEven(mainDpyInfo.w); } if (gVideoHeight == 0) { - gVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h; + gVideoHeight = floorToEven(mainDpyInfo.h); } // Configure and start the encoder. -- GitLab From 88785a3bd9c2ca6235ee42c48fb1c878efecfb75 Mon Sep 17 00:00:00 2001 From: Simran Basi Date: Wed, 22 Aug 2018 14:30:24 -0700 Subject: [PATCH 0219/1530] Add cameraservice_test to device-tests This test was added to TEST_MAPPING but it needs to be packaged properly in order to be utilized by TEST_MAPPING. Bug: None Test: TH Run Change-Id: I86d885ef12c5725211ca1fc3123ddb10311f7372 --- services/camera/libcameraservice/tests/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk index f77069c32f..8d80ff1fb6 100644 --- a/services/camera/libcameraservice/tests/Android.mk +++ b/services/camera/libcameraservice/tests/Android.mk @@ -38,6 +38,7 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -Wall -Wextra -Werror LOCAL_MODULE:= cameraservice_test +LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_MODULE_TAGS := tests include $(BUILD_NATIVE_TEST) -- GitLab From a70beb16195266900997f2f376d2f28017cd45a4 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 17 Jul 2018 14:10:03 -0700 Subject: [PATCH 0220/1530] Move MediaExtractor back to libstagefright It is no longer needed in libmediaextractor, and moving it back to libstagefright will avoid a circular dependency later. Bug: 111407253 Test: build, boot, play some files Change-Id: I1373a9eedee27998e7733ea23ae1a375e45cc9c3 --- cmds/stagefright/record.cpp | 2 +- cmds/stagefright/stagefright.cpp | 2 +- cmds/stagefright/stream.cpp | 2 +- include/media/MediaExtractor.h | 1 - media/extractors/mpeg2/ExtractorBundle.cpp | 1 - media/libmedia/IMediaExtractorService.cpp | 1 - media/libmediaextractor/Android.bp | 1 - media/libmediaplayer2/nuplayer2/GenericSource2.cpp | 1 - media/libmediaplayerservice/nuplayer/GenericSource.cpp | 2 +- media/libstagefright/Android.bp | 2 ++ media/{libmediaextractor => libstagefright}/MediaExtractor.cpp | 2 +- media/libstagefright/MediaExtractorFactory.cpp | 2 +- media/libstagefright/NuMediaExtractor.cpp | 2 +- media/libstagefright/StagefrightMetadataRetriever.cpp | 1 + media/libstagefright/include/StagefrightMetadataRetriever.h | 1 - media/libstagefright/include/media/stagefright/InterfaceUtils.h | 1 - .../include/media/stagefright}/MediaExtractor.h | 0 .../include/media/stagefright/MediaExtractorFactory.h | 1 - .../include/media/stagefright/RemoteMediaExtractor.h | 2 +- services/mediaextractor/MediaExtractorService.cpp | 1 - 20 files changed, 11 insertions(+), 17 deletions(-) delete mode 120000 include/media/MediaExtractor.h rename media/{libmediaextractor => libstagefright}/MediaExtractor.cpp (98%) rename media/{libmediaextractor/include/media => libstagefright/include/media/stagefright}/MediaExtractor.h (100%) diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp index 44b0015f0f..95a16f3a04 100644 --- a/cmds/stagefright/record.cpp +++ b/cmds/stagefright/record.cpp @@ -17,7 +17,6 @@ #include "SineSource.h" #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index bddf9452ac..0d331df18d 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -50,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index b0199d8cf5..b2f39dc7a8 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/include/media/MediaExtractor.h b/include/media/MediaExtractor.h deleted file mode 120000 index 4b35fe1f3f..0000000000 --- a/include/media/MediaExtractor.h +++ /dev/null @@ -1 +0,0 @@ -../../media/libmediaextractor/include/media/MediaExtractor.h \ No newline at end of file diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index ff40ed4c18..88c2d873d3 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -18,7 +18,6 @@ #define LOG_TAG "MPEG2ExtractorBundle" #include -#include #include #include "MPEG2PSExtractor.h" #include "MPEG2TSExtractor.h" diff --git a/media/libmedia/IMediaExtractorService.cpp b/media/libmedia/IMediaExtractorService.cpp index d7533ca635..0295abcb35 100644 --- a/media/libmedia/IMediaExtractorService.cpp +++ b/media/libmedia/IMediaExtractorService.cpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace android { diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp index b9b47cd605..0208ad491b 100644 --- a/media/libmediaextractor/Android.bp +++ b/media/libmediaextractor/Android.bp @@ -29,7 +29,6 @@ cc_library { "MediaBufferGroup.cpp", "MediaSource.cpp", "MediaTrack.cpp", - "MediaExtractor.cpp", "MetaData.cpp", "MetaDataBase.cpp", "VorbisComment.cpp", diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp index a6d88a281e..e317e23d39 100644 --- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 23d66bb667..8cd6eda24c 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -38,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 27383cbdfc..fec40b6010 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -120,6 +120,7 @@ cc_library_shared { "MediaCodecList.cpp", "MediaCodecListOverrides.cpp", "MediaCodecSource.cpp", + "MediaExtractor.cpp", "MediaExtractorFactory.cpp", "MediaSync.cpp", "http/ClearMediaHTTP.cpp", @@ -241,6 +242,7 @@ cc_library { "HevcUtils.cpp", "InterfaceUtils.cpp", "MediaClock.cpp", + "MediaExtractor.cpp", "MediaExtractorFactory.cpp", "NdkUtils.cpp", "NuCachedSource2.cpp", diff --git a/media/libmediaextractor/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp similarity index 98% rename from media/libmediaextractor/MediaExtractor.cpp rename to media/libstagefright/MediaExtractor.cpp index 8bf44c84b9..5e1dc777b2 100644 --- a/media/libmediaextractor/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -19,8 +19,8 @@ #include #include -#include #include +#include #include namespace android { diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 7578b1e3e1..1a12c9524f 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -21,9 +21,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index 4a7d6ca7ad..8e8c77c878 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -24,7 +24,6 @@ #include "include/NuCachedSource2.h" #include -#include #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index e80ec3bd76..f8dde7986d 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h index f78e125d6a..a7090adce1 100644 --- a/media/libstagefright/include/StagefrightMetadataRetriever.h +++ b/media/libstagefright/include/StagefrightMetadataRetriever.h @@ -26,7 +26,6 @@ namespace android { class DataSource; -class MediaExtractor; struct ImageDecoder; struct FrameRect; diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h index f0ebd48347..b83a9585f1 100644 --- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h +++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h @@ -18,7 +18,6 @@ #define INTERFACE_UTILS_H_ #include -#include #include #include #include diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libstagefright/include/media/stagefright/MediaExtractor.h similarity index 100% rename from media/libmediaextractor/include/media/MediaExtractor.h rename to media/libstagefright/include/media/stagefright/MediaExtractor.h diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index 4ee55565e3..f93d6102ac 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -21,7 +21,6 @@ #include #include -#include namespace android { diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h index 509e669a79..9925114a68 100644 --- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h @@ -18,7 +18,7 @@ #define REMOTE_MEDIA_EXTRACTOR_H_ #include -#include +#include #include namespace android { diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp index f0f44f5c18..f4d8b43dbe 100644 --- a/services/mediaextractor/MediaExtractorService.cpp +++ b/services/mediaextractor/MediaExtractorService.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include #include -- GitLab From e8198ce2bb3d27604dee02299c9de7117124cad2 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 23 Aug 2018 09:44:15 -0700 Subject: [PATCH 0221/1530] Fix build Change-Id: I7298e964304877191b64e1049fb6b502313d6861 --- media/libstagefright/omx/tests/OMXHarness.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index 895a4cef4c..5388ba7649 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include -- GitLab From 72a224dfbc25f196f107ff4f4e406e2f338b74f5 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 23 Aug 2018 09:48:04 -0700 Subject: [PATCH 0222/1530] MediaExtractorFactory: add a property for debugging/development Test: build & install & dumpsys media.extractor Bug: 112766913 Change-Id: I0f24b64105af36fce48e93269ac7056a6cb7adce --- media/libstagefright/MediaExtractorFactory.cpp | 5 ++++- .../include/media/stagefright/MediaExtractorFactory.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 6e361497e8..2f77803fd8 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -126,6 +126,7 @@ struct ExtractorPlugin : public RefBase { Mutex MediaExtractorFactory::gPluginMutex; std::shared_ptr>> MediaExtractorFactory::gPlugins; bool MediaExtractorFactory::gPluginsRegistered = false; +bool MediaExtractorFactory::gIgnoreVersion = false; // static CreatorFunc MediaExtractorFactory::sniff( @@ -192,7 +193,7 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, for (auto it = pluginList.begin(); it != pluginList.end(); ++it) { if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) { // there's already an extractor with the same uuid - if ((*it)->def.extractor_version < plugin->def.extractor_version) { + if (gIgnoreVersion || (*it)->def.extractor_version < plugin->def.extractor_version) { // this one is newer, replace the old one ALOGW("replacing extractor '%s' version %u with version %u", plugin->def.extractor_name, @@ -307,6 +308,8 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { return; } + gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false); + std::shared_ptr>> newList(new std::list>()); RegisterExtractorsInSystem("/system/lib" diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index 4ee55565e3..a78d441e4e 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -41,6 +41,7 @@ private: static Mutex gPluginMutex; static std::shared_ptr>> gPlugins; static bool gPluginsRegistered; + static bool gIgnoreVersion; static void RegisterExtractorsInApk( const char *apkPath, std::list> &pluginList); -- GitLab From 71f9066587a7912ab0f1bfcdd380a8079b8275a2 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 22 Aug 2018 19:10:29 -0700 Subject: [PATCH 0223/1530] Change skip frame to ALOGD To help debug frame drops in antibanding case. b/112893730 Change-Id: I7a69c89d1fcf442bbacf2e4b766109ae76b84999 --- media/libstagefright/bqhelper/GraphicBufferSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index dd03d38373..6d938079f6 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -767,7 +767,7 @@ bool GraphicBufferSource::calculateCodecTimestamp_l( double nFrames = (timeUs - mPrevCaptureUs) * mCaptureFps / 1000000; if (nFrames < 0.5 - kTimestampFluctuation) { // skip this frame as it's too close to previous capture - ALOGV("skipping frame, timeUs %lld", static_cast(timeUs)); + ALOGD("skipping frame, timeUs %lld", static_cast(timeUs)); return false; } if (nFrames <= 1.0) { -- GitLab From 882c520fd82ef5ce1420bb3eb6fc4bcbb205cb87 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 23 Apr 2018 10:32:45 -0700 Subject: [PATCH 0224/1530] aaudio: cleanup FIFO Remove unused methods and fields. Better code format and alignment. Use unique_ptr for mFifo. Remove getFifoController() method that exposed pointer. Updates tests and calling code as needed. Test: test_atomic_fifo.cpp Change-Id: I1ea66beaadeb74083307835858b757e4753affda --- media/libaaudio/src/client/AudioEndpoint.cpp | 8 +-- media/libaaudio/src/fifo/FifoBuffer.cpp | 56 ++++++------------- media/libaaudio/src/fifo/FifoBuffer.h | 48 ++++++++-------- media/libaaudio/src/fifo/FifoController.h | 2 +- media/libaaudio/tests/test_atomic_fifo.cpp | 12 ++-- services/oboeservice/AAudioMixer.cpp | 2 +- .../AAudioServiceEndpointCapture.cpp | 2 +- 7 files changed, 54 insertions(+), 76 deletions(-) diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp index f8e34d17f7..214f8881c0 100644 --- a/media/libaaudio/src/client/AudioEndpoint.cpp +++ b/media/libaaudio/src/client/AudioEndpoint.cpp @@ -196,7 +196,7 @@ int32_t AudioEndpoint::getEmptyFramesAvailable(WrappingBuffer *wrappingBuffer) { int32_t AudioEndpoint::getEmptyFramesAvailable() { - return mDataQueue->getFifoControllerBase()->getEmptyFramesAvailable(); + return mDataQueue->getEmptyFramesAvailable(); } int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer) @@ -206,15 +206,15 @@ int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer) int32_t AudioEndpoint::getFullFramesAvailable() { - return mDataQueue->getFifoControllerBase()->getFullFramesAvailable(); + return mDataQueue->getFullFramesAvailable(); } void AudioEndpoint::advanceWriteIndex(int32_t deltaFrames) { - mDataQueue->getFifoControllerBase()->advanceWriteIndex(deltaFrames); + mDataQueue->advanceWriteIndex(deltaFrames); } void AudioEndpoint::advanceReadIndex(int32_t deltaFrames) { - mDataQueue->getFifoControllerBase()->advanceReadIndex(deltaFrames); + mDataQueue->advanceReadIndex(deltaFrames); } void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead) diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp index b09258ea1b..f5113f2d3e 100644 --- a/media/libaaudio/src/fifo/FifoBuffer.cpp +++ b/media/libaaudio/src/fifo/FifoBuffer.cpp @@ -23,30 +23,26 @@ #include #include +#include #include "FifoControllerBase.h" #include "FifoController.h" #include "FifoControllerIndirect.h" #include "FifoBuffer.h" -using namespace android; // TODO just import names needed +using android::FifoBuffer; +using android::fifo_frames_t; FifoBuffer::FifoBuffer(int32_t bytesPerFrame, fifo_frames_t capacityInFrames) - : mFrameCapacity(capacityInFrames) - , mBytesPerFrame(bytesPerFrame) - , mStorage(nullptr) - , mFramesReadCount(0) - , mFramesUnderrunCount(0) - , mUnderrunCount(0) + : mBytesPerFrame(bytesPerFrame) { - // TODO Handle possible failures to allocate. Move out of constructor? - mFifo = new FifoController(capacityInFrames, capacityInFrames); + mFifo = std::make_unique(capacityInFrames, capacityInFrames); // allocate buffer int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames; mStorage = new uint8_t[bytesPerBuffer]; mStorageOwned = true; - ALOGV("capacityInFrames = %d, bytesPerFrame = %d", - capacityInFrames, bytesPerFrame); + ALOGV("%s() capacityInFrames = %d, bytesPerFrame = %d", + __func__, capacityInFrames, bytesPerFrame); } FifoBuffer::FifoBuffer( int32_t bytesPerFrame, @@ -55,14 +51,10 @@ FifoBuffer::FifoBuffer( int32_t bytesPerFrame, fifo_counter_t * writeIndexAddress, void * dataStorageAddress ) - : mFrameCapacity(capacityInFrames) - , mBytesPerFrame(bytesPerFrame) + : mBytesPerFrame(bytesPerFrame) , mStorage(static_cast(dataStorageAddress)) - , mFramesReadCount(0) - , mFramesUnderrunCount(0) - , mUnderrunCount(0) { - mFifo = new FifoControllerIndirect(capacityInFrames, + mFifo = std::make_unique(capacityInFrames, capacityInFrames, readIndexAddress, writeIndexAddress); @@ -73,10 +65,8 @@ FifoBuffer::~FifoBuffer() { if (mStorageOwned) { delete[] mStorage; } - delete mFifo; } - int32_t FifoBuffer::convertFramesToBytes(fifo_frames_t frames) { return frames * mBytesPerFrame; } @@ -87,11 +77,12 @@ void FifoBuffer::fillWrappingBuffer(WrappingBuffer *wrappingBuffer, wrappingBuffer->data[1] = nullptr; wrappingBuffer->numFrames[1] = 0; if (framesAvailable > 0) { + fifo_frames_t capacity = mFifo->getCapacity(); uint8_t *source = &mStorage[convertFramesToBytes(startIndex)]; // Does the available data cross the end of the FIFO? - if ((startIndex + framesAvailable) > mFrameCapacity) { + if ((startIndex + framesAvailable) > capacity) { wrappingBuffer->data[0] = source; - fifo_frames_t firstFrames = mFrameCapacity - startIndex; + fifo_frames_t firstFrames = capacity - startIndex; wrappingBuffer->numFrames[0] = firstFrames; wrappingBuffer->data[1] = &mStorage[0]; wrappingBuffer->numFrames[1] = framesAvailable - firstFrames; @@ -107,7 +98,8 @@ void FifoBuffer::fillWrappingBuffer(WrappingBuffer *wrappingBuffer, fifo_frames_t FifoBuffer::getFullDataAvailable(WrappingBuffer *wrappingBuffer) { // The FIFO might be overfull so clip to capacity. - fifo_frames_t framesAvailable = std::min(mFifo->getFullFramesAvailable(), mFrameCapacity); + fifo_frames_t framesAvailable = std::min(mFifo->getFullFramesAvailable(), + mFifo->getCapacity()); fifo_frames_t startIndex = mFifo->getReadIndex(); fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex); return framesAvailable; @@ -115,7 +107,8 @@ fifo_frames_t FifoBuffer::getFullDataAvailable(WrappingBuffer *wrappingBuffer) { fifo_frames_t FifoBuffer::getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer) { // The FIFO might have underrun so clip to capacity. - fifo_frames_t framesAvailable = std::min(mFifo->getEmptyFramesAvailable(), mFrameCapacity); + fifo_frames_t framesAvailable = std::min(mFifo->getEmptyFramesAvailable(), + mFifo->getCapacity()); fifo_frames_t startIndex = mFifo->getWriteIndex(); fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex); return framesAvailable; @@ -183,23 +176,6 @@ fifo_frames_t FifoBuffer::write(const void *buffer, fifo_frames_t numFrames) { return framesWritten; } -fifo_frames_t FifoBuffer::readNow(void *buffer, fifo_frames_t numFrames) { - mLastReadSize = numFrames; - fifo_frames_t framesLeft = numFrames; - fifo_frames_t framesRead = read(buffer, numFrames); - framesLeft -= framesRead; - mFramesReadCount += framesRead; - mFramesUnderrunCount += framesLeft; - // Zero out any samples we could not set. - if (framesLeft > 0) { - mUnderrunCount++; - int32_t bytesToZero = convertFramesToBytes(framesLeft); - memset(buffer, 0, bytesToZero); - } - - return framesRead; -} - fifo_frames_t FifoBuffer::getThreshold() { return mFifo->getThreshold(); } diff --git a/media/libaaudio/src/fifo/FifoBuffer.h b/media/libaaudio/src/fifo/FifoBuffer.h index f5a9e27c59..0d188c413e 100644 --- a/media/libaaudio/src/fifo/FifoBuffer.h +++ b/media/libaaudio/src/fifo/FifoBuffer.h @@ -17,6 +17,7 @@ #ifndef FIFO_FIFO_BUFFER_H #define FIFO_FIFO_BUFFER_H +#include #include #include "FifoControllerBase.h" @@ -77,24 +78,12 @@ public: */ fifo_frames_t getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer); - /** - * Copy data from the FIFO into the buffer. - * @param buffer - * @param numFrames - * @return - */ - fifo_frames_t readNow(void *buffer, fifo_frames_t numFrames); - - int64_t getNextReadTime(int32_t frameRate); - - int32_t getUnderrunCount() const { return mUnderrunCount; } - - FifoControllerBase *getFifoControllerBase() { return mFifo; } - int32_t getBytesPerFrame() { return mBytesPerFrame; } + // Proxy methods for the internal FifoController + fifo_counter_t getReadCounter() { return mFifo->getReadCounter(); } @@ -111,6 +100,22 @@ public: mFifo->setWriteCounter(n); } + void advanceReadIndex(fifo_frames_t numFrames) { + mFifo->advanceReadIndex(numFrames); + } + + void advanceWriteIndex(fifo_frames_t numFrames) { + mFifo->advanceWriteIndex(numFrames); + } + + fifo_frames_t getEmptyFramesAvailable() { + return mFifo->getEmptyFramesAvailable(); + } + + fifo_frames_t getFullFramesAvailable() { + return mFifo->getFullFramesAvailable(); + } + /* * This is generally only called before or after the buffer is used. */ @@ -121,15 +126,12 @@ private: void fillWrappingBuffer(WrappingBuffer *wrappingBuffer, int32_t framesAvailable, int32_t startIndex); - const fifo_frames_t mFrameCapacity; - const int32_t mBytesPerFrame; - uint8_t *mStorage; - bool mStorageOwned; // did this object allocate the storage? - FifoControllerBase *mFifo; - fifo_counter_t mFramesReadCount; - fifo_counter_t mFramesUnderrunCount; - int32_t mUnderrunCount; // need? just use frames - int32_t mLastReadSize; + const int32_t mBytesPerFrame; + // We do not use a std::unique_ptr for mStorage because it is often a pointer to + // memory shared between processes and cannot be deleted trivially. + uint8_t *mStorage = nullptr; + bool mStorageOwned = false; // did this object allocate the storage? + std::unique_ptr mFifo{}; }; } // android diff --git a/media/libaaudio/src/fifo/FifoController.h b/media/libaaudio/src/fifo/FifoController.h index 79d98a1fc8..057a94e699 100644 --- a/media/libaaudio/src/fifo/FifoController.h +++ b/media/libaaudio/src/fifo/FifoController.h @@ -30,7 +30,7 @@ namespace android { class FifoController : public FifoControllerBase { public: - FifoController(fifo_counter_t bufferSize, fifo_counter_t threshold) + FifoController(fifo_frames_t bufferSize, fifo_frames_t threshold) : FifoControllerBase(bufferSize, threshold) , mReadCounter(0) , mWriteCounter(0) diff --git a/media/libaaudio/tests/test_atomic_fifo.cpp b/media/libaaudio/tests/test_atomic_fifo.cpp index 0085217546..a09b74c3ec 100644 --- a/media/libaaudio/tests/test_atomic_fifo.cpp +++ b/media/libaaudio/tests/test_atomic_fifo.cpp @@ -96,14 +96,14 @@ public: void checkWrappingBuffer() { WrappingBuffer wrappingBuffer; fifo_frames_t framesAvailable = - mFifoBuffer.getFifoControllerBase()->getEmptyFramesAvailable(); + mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t wrapAvailable = mFifoBuffer.getEmptyRoomAvailable(&wrappingBuffer); EXPECT_EQ(framesAvailable, wrapAvailable); fifo_frames_t bothAvailable = wrappingBuffer.numFrames[0] + wrappingBuffer.numFrames[1]; EXPECT_EQ(framesAvailable, bothAvailable); framesAvailable = - mFifoBuffer.getFifoControllerBase()->getFullFramesAvailable(); + mFifoBuffer.getFullFramesAvailable(); wrapAvailable = mFifoBuffer.getFullDataAvailable(&wrappingBuffer); EXPECT_EQ(framesAvailable, wrapAvailable); bothAvailable = wrappingBuffer.numFrames[0] + wrappingBuffer.numFrames[1]; @@ -113,7 +113,7 @@ public: // Write data but do not overflow. void writeData(fifo_frames_t numFrames) { fifo_frames_t framesAvailable = - mFifoBuffer.getFifoControllerBase()->getEmptyFramesAvailable(); + mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t framesToWrite = std::min(framesAvailable, numFrames); for (int i = 0; i < framesToWrite; i++) { mData[i] = mNextWriteIndex++; @@ -125,7 +125,7 @@ public: // Read data but do not underflow. void verifyData(fifo_frames_t numFrames) { fifo_frames_t framesAvailable = - mFifoBuffer.getFifoControllerBase()->getFullFramesAvailable(); + mFifoBuffer.getFullFramesAvailable(); fifo_frames_t framesToRead = std::min(framesAvailable, numFrames); fifo_frames_t actual = mFifoBuffer.read(mData, framesToRead); ASSERT_EQ(framesToRead, actual); @@ -178,12 +178,12 @@ public: void checkRandomWriteRead() { for (int i = 0; i < 20; i++) { fifo_frames_t framesEmpty = - mFifoBuffer.getFifoControllerBase()->getEmptyFramesAvailable(); + mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t numFrames = (fifo_frames_t)(drand48() * framesEmpty); writeData(numFrames); fifo_frames_t framesFull = - mFifoBuffer.getFifoControllerBase()->getFullFramesAvailable(); + mFifoBuffer.getFullFramesAvailable(); numFrames = (fifo_frames_t)(drand48() * framesFull); verifyData(numFrames); } diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp index b031888b52..1c03b7facd 100644 --- a/services/oboeservice/AAudioMixer.cpp +++ b/services/oboeservice/AAudioMixer.cpp @@ -99,7 +99,7 @@ int32_t AAudioMixer::mix(int streamIndex, FifoBuffer *fifo, bool allowUnderflow) } partIndex++; } - fifo->getFifoControllerBase()->advanceReadIndex(framesDesired); + fifo->advanceReadIndex(framesDesired); #if AAUDIO_MIXER_ATRACE_ENABLED ATRACE_END(); diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp index efac7888ad..7ae7f1b3d5 100644 --- a/services/oboeservice/AAudioServiceEndpointCapture.cpp +++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp @@ -102,7 +102,7 @@ void *AAudioServiceEndpointCapture::callbackLoop() { streamShared->setTimestampPositionOffset(positionOffset); // Is the buffer too full to write a burst? - if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() < + if (fifo->getEmptyFramesAvailable() < getFramesPerBurst()) { streamShared->incrementXRunCount(); } else { -- GitLab From b1040c5f1a18b1f77775ffbb2810b2450c2950bc Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Thu, 23 Aug 2018 17:02:04 -0700 Subject: [PATCH 0225/1530] Let screenrecord to use viewport in DisplayInfo Bug: b/113041831 Test: adb shell screenrecord in all screen rotation and DPIs Change-Id: Ic6bbc233bfbc502b6272c5b4ee991282b055bc77 --- cmds/screenrecord/screenrecord.cpp | 54 +++++++----------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index c559ff90d0..1b8e8d968f 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -235,10 +235,10 @@ static status_t setDisplayProjection( // Set the region of the layer stack we're interested in, which in our // case is "all of it". - Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h); + Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH); // We need to preserve the aspect ratio of the display. - float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w; + float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW; // Set the way we map the output onto the display surface (which will @@ -314,22 +314,6 @@ static status_t prepareVirtualDisplay(const DisplayInfo& mainDpyInfo, return NO_ERROR; } -/* - * Set the main display width and height to the actual width and height - */ -static status_t getActualDisplaySize(const sp& mainDpy, DisplayInfo* mainDpyInfo) { - Rect viewport; - status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport); - if (err != NO_ERROR) { - fprintf(stderr, "ERROR: unable to get display viewport\n"); - return err; - } - mainDpyInfo->w = viewport.width(); - mainDpyInfo->h = viewport.height(); - - return NO_ERROR; -} - /* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger @@ -400,22 +384,14 @@ static status_t runEncoder(const sp& encoder, // useful stuff is hard to get at without a Dalvik VM. err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); - if (err == NO_ERROR) { - err = getActualDisplaySize(mainDpy, &mainDpyInfo); - if (err != NO_ERROR) { - fprintf(stderr, "ERROR: unable to set actual display size\n"); - return err; - } - - if (orientation != mainDpyInfo.orientation) { - ALOGD("orientation changed, now %d", mainDpyInfo.orientation); - SurfaceComposerClient::Transaction t; - setDisplayProjection(t, virtualDpy, mainDpyInfo); - t.apply(); - orientation = mainDpyInfo.orientation; - } - } else { + if (err != NO_ERROR) { ALOGW("getDisplayInfo(main) failed: %d", err); + } else if (orientation != mainDpyInfo.orientation) { + ALOGD("orientation changed, now %d", mainDpyInfo.orientation); + SurfaceComposerClient::Transaction t; + setDisplayProjection(t, virtualDpy, mainDpyInfo); + t.apply(); + orientation = mainDpyInfo.orientation; } } @@ -589,25 +565,19 @@ static status_t recordScreen(const char* fileName) { return err; } - err = getActualDisplaySize(mainDpy, &mainDpyInfo); - if (err != NO_ERROR) { - fprintf(stderr, "ERROR: unable to set actual display size\n"); - return err; - } - if (gVerbose) { printf("Main display is %dx%d @%.2ffps (orientation=%u)\n", - mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps, + mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps, mainDpyInfo.orientation); fflush(stdout); } // Encoder can't take odd number as config if (gVideoWidth == 0) { - gVideoWidth = floorToEven(mainDpyInfo.w); + gVideoWidth = floorToEven(mainDpyInfo.viewportW); } if (gVideoHeight == 0) { - gVideoHeight = floorToEven(mainDpyInfo.h); + gVideoHeight = floorToEven(mainDpyInfo.viewportH); } // Configure and start the encoder. -- GitLab From 975be2f38fdef33e614aa0f6982f551fabb0d163 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Thu, 23 Aug 2018 18:23:04 -0700 Subject: [PATCH 0226/1530] NuPlayer2: send MEDIA2_INFO_DATA_SOURCE_START when starting Test: cts testPlaylist Bug: 109928575 Change-Id: Icc52599504c2c157f7d8ae0777c509f268eaab20 --- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index c649573b9b..b6b9b78aef 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -1684,6 +1684,7 @@ void NuPlayer2::onStart() { } startPlaybackTimer("onstart"); + notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_DATA_SOURCE_START, 0); postScanSources(); } -- GitLab From 19c8e3ae879f446e991d8ff83c7b220d6d583b8c Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 23 Aug 2018 19:07:37 -0700 Subject: [PATCH 0227/1530] AudioResampler: Clear input buffer on reset Test: use debug tee - play audio, pause, seek to start, play. Bug: 73132891 Change-Id: I9f4237802d203cabd5d1372828eba7c7320f02d7 --- media/libaudioprocessing/AudioResamplerDyn.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/media/libaudioprocessing/AudioResamplerDyn.h b/media/libaudioprocessing/AudioResamplerDyn.h index 92144d0b53..479142e467 100644 --- a/media/libaudioprocessing/AudioResamplerDyn.h +++ b/media/libaudioprocessing/AudioResamplerDyn.h @@ -55,6 +55,11 @@ public: virtual size_t resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); + void reset() override { + AudioResampler::reset(); + mInBuffer.reset(); + } + // Make available key design criteria for testing int getHalfLength() const { return mConstants.mHalfNumCoefs; -- GitLab From e57e9a2ca66a801ace5d6b93200d24807d2566cc Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 23 Aug 2018 15:36:41 -0700 Subject: [PATCH 0228/1530] Camera NDK: internal->public format conversion Test: new CTS test Bug: 112897492 Change-Id: I785612966e7c6201db333e3a736e6efda9e63feb --- camera/ndk/impl/ACameraMetadata.cpp | 70 +++++++++++++++++++++++++++++ camera/ndk/impl/ACameraMetadata.h | 8 ++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index fc00a2d2ba..d73f7440e7 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -32,6 +32,10 @@ ACameraMetadata::ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYP if (mType == ACM_CHARACTERISTICS) { filterUnsupportedFeatures(); filterStreamConfigurations(); + filterDurations(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS); + filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS); + filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS); + filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS); } // TODO: filter request/result keys } @@ -81,6 +85,72 @@ ACameraMetadata::filterUnsupportedFeatures() { } +void +ACameraMetadata::filterDurations(uint32_t tag) { + const int STREAM_CONFIGURATION_SIZE = 4; + const int STREAM_FORMAT_OFFSET = 0; + const int STREAM_WIDTH_OFFSET = 1; + const int STREAM_HEIGHT_OFFSET = 2; + const int STREAM_DURATION_OFFSET = 3; + camera_metadata_entry entry = mData.find(tag); + if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT64) { + ALOGE("%s: malformed duration key %d! count %zu, type %d", + __FUNCTION__, tag, entry.count, entry.type); + return; + } + Vector filteredDurations; + filteredDurations.setCapacity(entry.count * 2); + + for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) { + int64_t format = entry.data.i64[i + STREAM_FORMAT_OFFSET]; + int64_t width = entry.data.i64[i + STREAM_WIDTH_OFFSET]; + int64_t height = entry.data.i64[i + STREAM_HEIGHT_OFFSET]; + int64_t duration = entry.data.i32[i + STREAM_DURATION_OFFSET]; + + // Leave the unfiltered format in so apps depending on previous wrong + // filter behavior continue to work + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + + // Translate HAL formats to NDK format + switch (tag) { + case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS: + case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS: + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_JPEG; + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + } + break; + case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS: + case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS: + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD; + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + } else if (format == HAL_PIXEL_FORMAT_Y16) { + format = AIMAGE_FORMAT_DEPTH16; + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + } + break; + default: + // Should not reach here + ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag); + } + } + + mData.update(tag, filteredDurations); +} + void ACameraMetadata::filterStreamConfigurations() { const int STREAM_CONFIGURATION_SIZE = 4; diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h index 0fd7efa163..e76b80cef7 100644 --- a/camera/ndk/impl/ACameraMetadata.h +++ b/camera/ndk/impl/ACameraMetadata.h @@ -58,13 +58,16 @@ struct ACameraMetadata : public RefBase { camera_status_t getTags(/*out*/int32_t* numTags, /*out*/const uint32_t** tags) const; + const CameraMetadata& getInternalData() const; + + private: + bool isNdkSupportedCapability(const int32_t capability); static inline bool isVendorTag(const uint32_t tag); static bool isCaptureRequestTag(const uint32_t tag); void filterUnsupportedFeatures(); // Hide features not yet supported by NDK void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats - - const CameraMetadata& getInternalData() const; + void filterDurations(uint32_t tag); // translate hal format to NDK formats template camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) { @@ -96,7 +99,6 @@ struct ACameraMetadata : public RefBase { } } - private: // guard access of public APIs: get/update/getTags mutable Mutex mLock; CameraMetadata mData; -- GitLab From 98603d8711b10969ba770fbb19774201212207a1 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 17 Jul 2018 11:06:55 -0700 Subject: [PATCH 0229/1530] Add AMediaFormat functions to clear and copy Also move NdkMediaFormat internals to libmedia to avoid a future circular dependency between libmedia and libmediandk. Bug: 111407253 Test: build, boot, play some files Change-Id: I54d91439f216c3834726e5505afef40dc0d93e67 --- .../media}/NdkMediaFormatPriv.h | 14 ++++- media/libmedia/Android.bp | 1 + media/libmedia/NdkMediaFormatPriv.cpp | 56 +++++++++++++++++++ media/ndk/NdkMediaCodec.cpp | 2 +- media/ndk/NdkMediaCrypto.cpp | 2 +- media/ndk/NdkMediaExtractor.cpp | 2 +- media/ndk/NdkMediaFormat.cpp | 21 ++++--- media/ndk/NdkMediaMuxer.cpp | 2 +- media/ndk/include/media/NdkMediaFormat.h | 14 ++++- media/ndk/libmediandk.map.txt | 2 + 10 files changed, 102 insertions(+), 14 deletions(-) rename {media/ndk => include/media}/NdkMediaFormatPriv.h (79%) create mode 100644 media/libmedia/NdkMediaFormatPriv.cpp diff --git a/media/ndk/NdkMediaFormatPriv.h b/include/media/NdkMediaFormatPriv.h similarity index 79% rename from media/ndk/NdkMediaFormatPriv.h rename to include/media/NdkMediaFormatPriv.h index c6a6563aa5..6c452c3649 100644 --- a/media/ndk/NdkMediaFormatPriv.h +++ b/include/media/NdkMediaFormatPriv.h @@ -27,13 +27,23 @@ #ifndef _NDK_MEDIA_FORMAT_PRIV_H #define _NDK_MEDIA_FORMAT_PRIV_H -#include +#include +#include +#include #ifdef __cplusplus extern "C" { #endif -AMediaFormat* AMediaFormat_fromMsg(void*); +using namespace android; + +struct AMediaFormat { + sp mFormat; + String8 mDebug; + KeyedVector mStringCache; +}; + +AMediaFormat* AMediaFormat_fromMsg(const void*); void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest); #ifdef __cplusplus diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index e6d6b3e45f..9fe9ee5789 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -191,6 +191,7 @@ cc_library { "MediaResourcePolicy.cpp", "Visualizer.cpp", "StringArray.cpp", + "NdkMediaFormatPriv.cpp", ], aidl: { diff --git a/media/libmedia/NdkMediaFormatPriv.cpp b/media/libmedia/NdkMediaFormatPriv.cpp new file mode 100644 index 0000000000..3c84d6a455 --- /dev/null +++ b/media/libmedia/NdkMediaFormatPriv.cpp @@ -0,0 +1,56 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "NdkMediaFormat" + +#include + +//#include + +#include +#include +#include +#include +#include +//#include +//#include + +#include + +using namespace android; + +extern "C" { + +// private functions for conversion to/from AMessage +AMediaFormat* AMediaFormat_fromMsg(const void* data) { + ALOGV("private ctor"); + AMediaFormat* mData = new AMediaFormat(); + mData->mFormat = *((sp*)data); + if (mData->mFormat == NULL) { + ALOGW("got NULL format"); + mData->mFormat = new AMessage; + } + return mData; +} + +void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest) { + *((sp*)dest) = mData->mFormat; +} + +} // extern "C" + + diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp index 42285f899d..c23f19b9d2 100644 --- a/media/ndk/NdkMediaCodec.cpp +++ b/media/ndk/NdkMediaCodec.cpp @@ -21,8 +21,8 @@ #include #include +#include #include "NdkMediaCryptoPriv.h" -#include "NdkMediaFormatPriv.h" #include #include diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp index d7193caf0a..b8af5ff09d 100644 --- a/media/ndk/NdkMediaCrypto.cpp +++ b/media/ndk/NdkMediaCrypto.cpp @@ -20,7 +20,7 @@ #include #include -#include "NdkMediaFormatPriv.h" +#include #include diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index b5e60a4be6..8c1ac59c7f 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -20,8 +20,8 @@ #include #include +#include #include "NdkMediaDataSourcePriv.h" -#include "NdkMediaFormatPriv.h" #include diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index f32b83e7ae..a66f3b32eb 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -32,12 +33,6 @@ using namespace android; -struct AMediaFormat { - sp mFormat; - String8 mDebug; - KeyedVector mStringCache; -}; - extern "C" { // private functions for conversion to/from AMessage @@ -74,6 +69,18 @@ media_status_t AMediaFormat_delete(AMediaFormat *mData) { return AMEDIA_OK; } +EXPORT +void AMediaFormat_clear(AMediaFormat *format) { + format->mFormat->clear(); +} + +EXPORT +media_status_t AMediaFormat_copy(AMediaFormat *to, AMediaFormat *from) { + to->mFormat->clear(); + to->mFormat->extend(from->mFormat); + return AMEDIA_OK; +} + EXPORT const char* AMediaFormat_toString(AMediaFormat *mData) { @@ -256,7 +263,7 @@ void AMediaFormat_setString(AMediaFormat* format, const char* name, const char* } EXPORT -void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, void* data, size_t size) { +void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, const void* data, size_t size) { // the ABuffer(void*, size_t) constructor doesn't take ownership of the data, so create // a new buffer and copy the data into it sp buf = new ABuffer(size); diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp index dffc4d7869..b213fa93a7 100644 --- a/media/ndk/NdkMediaMuxer.cpp +++ b/media/ndk/NdkMediaMuxer.cpp @@ -20,7 +20,7 @@ #include #include -#include "NdkMediaFormatPriv.h" +#include #include diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 5f7804d8a8..3f853d071b 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -83,7 +83,7 @@ void AMediaFormat_setString(AMediaFormat*, const char* name, const char* value) /** * The provided data is copied into the format. */ -void AMediaFormat_setBuffer(AMediaFormat*, const char* name, void* data, size_t size) __INTRODUCED_IN(21); +void AMediaFormat_setBuffer(AMediaFormat*, const char* name, const void* data, size_t size) __INTRODUCED_IN(21); @@ -170,6 +170,18 @@ void AMediaFormat_setRect(AMediaFormat*, const char* name, int32_t left, int32_t top, int32_t right, int32_t bottom) __INTRODUCED_IN(28); #endif /* __ANDROID_API__ >= 28 */ +#if __ANDROID_API__ >= 29 +/** + * remove all key/value pairs from the given AMediaFormat + */ +void AMediaFormat_clear(AMediaFormat*) __INTRODUCED_IN(29); + +/** + * copy one AMediaFormat to another + */ +media_status_t AMediaFormat_copy(AMediaFormat *to, AMediaFormat *from) __INTRODUCED_IN(29); +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif // _NDK_MEDIA_FORMAT_H diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index d828d6a2cb..0751a55220 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -185,6 +185,8 @@ LIBMEDIANDK { AMediaExtractor_setDataSourceCustom; # introduced=28 AMediaExtractor_setDataSourceFd; AMediaExtractor_unselectTrack; + AMediaFormat_clear; # introduced=29 + AMediaFormat_copy; # introduced=29 AMediaFormat_delete; AMediaFormat_getBuffer; AMediaFormat_getDouble; # introduced=28 -- GitLab From cd03192ba4471f48db8253518cba7a1b3ae36a29 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 24 Aug 2018 13:03:19 -0700 Subject: [PATCH 0230/1530] Fix deleter in HeifDataSource bug: 112647413 Test: POC; CTS MediaMetadataRetrieverTest; .heic file decoding in Photos Change-Id: I55ccb4ccb99884295ae02a6f1250a8177875f478 --- media/libheif/HeifDecoderImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp index 01f014fb3d..a9773002da 100644 --- a/media/libheif/HeifDecoderImpl.cpp +++ b/media/libheif/HeifDecoderImpl.cpp @@ -86,7 +86,7 @@ private: sp mMemory; std::unique_ptr mStream; bool mEOS; - std::unique_ptr mCache; + std::unique_ptr mCache; off64_t mCachedOffset; size_t mCachedSize; size_t mCacheBufferSize; @@ -165,7 +165,7 @@ ssize_t HeifDataSource::readAt(off64_t offset, size_t size) { // it's reaching max cache buffer size, need to roll window, and possibly // expand the cache buffer. size_t newCacheBufferSize = mCacheBufferSize; - std::unique_ptr newCache; + std::unique_ptr newCache; uint8_t* dst = mCache.get(); if (newCacheBufferSize < kMaxCacheBufferSize) { newCacheBufferSize = kMaxCacheBufferSize; -- GitLab From 0151ef43e9e435c4fd55652dae40432dc8681c51 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Fri, 24 Aug 2018 16:40:21 -0700 Subject: [PATCH 0231/1530] MediaPlayer2: remove stop and related enums Test: cts Bug: 109928575 Change-Id: Ice031d6cecd4cc3642b57e9cbeeabbf48cd9c1b0 --- .../mediaplayer2/MediaPlayer2Interface.h | 1 - .../include/mediaplayer2/MediaPlayer2Types.h | 3 +- .../include/mediaplayer2/mediaplayer2.h | 1 - media/libmediaplayer2/mediaplayer2.cpp | 27 ++------- .../nuplayer2/NuPlayer2Driver.cpp | 60 +------------------ .../nuplayer2/NuPlayer2Driver.h | 44 +++++++------- 6 files changed, 26 insertions(+), 110 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index 02bf8911d9..a6bf543f2d 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -171,7 +171,6 @@ public: virtual status_t prepareAsync() = 0; virtual status_t start() = 0; - virtual status_t stop() = 0; virtual status_t pause() = 0; virtual bool isPlaying() = 0; virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) { diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h index 211a5c0312..4b0a960356 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h @@ -168,8 +168,7 @@ enum media_player2_internal_states { MEDIA_PLAYER2_PREPARED = 1 << 3, MEDIA_PLAYER2_STARTED = 1 << 4, MEDIA_PLAYER2_PAUSED = 1 << 5, - MEDIA_PLAYER2_STOPPED = 1 << 6, - MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7 + MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 6 }; // Keep KEY_PARAMETER_* in sync with MediaPlayer2.java. diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 3af212e054..43fba2396a 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -65,7 +65,6 @@ public: status_t setBufferingSettings(const BufferingSettings& buffering); status_t prepareAsync(); status_t start(); - status_t stop(); status_t pause(); bool isPlaying(); mediaplayer2_states getState(); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 4fb47b8f3c..f0ea59e9c5 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -750,7 +750,7 @@ status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) { status_t MediaPlayer2::prepareAsync() { ALOGV("prepareAsync"); Mutex::Autolock _l(mLock); - if ((mPlayer != 0) && (mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED))) { + if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER2_INITIALIZED)) { if (mAudioAttributesParcel != NULL) { status_t err = setAudioAttributes_l(*mAudioAttributesParcel); if (err != OK) { @@ -806,24 +806,6 @@ status_t MediaPlayer2::start() { return ret; } -status_t MediaPlayer2::stop() { - ALOGV("stop"); - Mutex::Autolock _l(mLock); - if (mCurrentState & MEDIA_PLAYER2_STOPPED) return NO_ERROR; - if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED | - MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE ) ) ) { - status_t ret = mPlayer->stop(); - if (ret != NO_ERROR) { - mCurrentState = MEDIA_PLAYER2_STATE_ERROR; - } else { - mCurrentState = MEDIA_PLAYER2_STOPPED; - } - return ret; - } - ALOGE("stop called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get()); - return INVALID_OPERATION; -} - status_t MediaPlayer2::pause() { ALOGV("pause"); Mutex::Autolock _l(mLock); @@ -873,8 +855,7 @@ mediaplayer2_states MediaPlayer2::getState() { if (mCurrentState & MEDIA_PLAYER2_STARTED) { return MEDIAPLAYER2_STATE_PLAYING; } - if (mCurrentState - & (MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_STOPPED | MEDIA_PLAYER2_PLAYBACK_COMPLETE)) { + if (mCurrentState & (MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE)) { return MEDIAPLAYER2_STATE_PAUSED; } // now only mCurrentState & MEDIA_PLAYER2_PREPARED is true @@ -890,7 +871,7 @@ status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate) { return BAD_VALUE; } Mutex::Autolock _l(mLock); - if (mPlayer == 0 || (mCurrentState & MEDIA_PLAYER2_STOPPED)) { + if (mPlayer == 0) { return INVALID_OPERATION; } @@ -982,7 +963,7 @@ status_t MediaPlayer2::getDuration(int64_t *msec) { Mutex::Autolock _l(mLock); ALOGV("getDuration_l"); bool isValidState = (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED | - MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_STOPPED | MEDIA_PLAYER2_PLAYBACK_COMPLETE)); + MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE)); if (mPlayer == 0 || !isValidState) { ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 3069f54116..f85e3a2f87 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -272,13 +272,6 @@ status_t NuPlayer2Driver::prepareAsync() { mState = STATE_PREPARING; mPlayer->prepareAsync(); return OK; - case STATE_STOPPED: - // this is really just paused. handle as seek to start - mAtEOS = false; - mState = STATE_STOPPED_AND_PREPARING; - mPlayer->seekToAsync(0, MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC /* mode */, - true /* needNotify */); - return OK; default: return INVALID_OPERATION; }; @@ -293,7 +286,6 @@ status_t NuPlayer2Driver::start() { status_t NuPlayer2Driver::start_l() { switch (mState) { case STATE_PAUSED: - case STATE_STOPPED_AND_PREPARED: case STATE_PREPARED: { mPlayer->start(); @@ -320,34 +312,6 @@ status_t NuPlayer2Driver::start_l() { return OK; } -status_t NuPlayer2Driver::stop() { - ALOGD("stop(%p)", this); - Mutex::Autolock autoLock(mLock); - - switch (mState) { - case STATE_RUNNING: - mPlayer->pause(); - // fall through - - case STATE_PAUSED: - mState = STATE_STOPPED; - //notifyListener_l(MEDIA2_STOPPED); - break; - - case STATE_PREPARED: - case STATE_STOPPED: - case STATE_STOPPED_AND_PREPARING: - case STATE_STOPPED_AND_PREPARED: - mState = STATE_STOPPED; - break; - - default: - return INVALID_OPERATION; - } - - return OK; -} - status_t NuPlayer2Driver::pause() { ALOGD("pause(%p)", this); // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear @@ -391,7 +355,6 @@ status_t NuPlayer2Driver::setPlaybackSettings(const AudioPlaybackRate &rate) { mState = STATE_PAUSED; } else if (rate.mSpeed != 0.f && (mState == STATE_PAUSED - || mState == STATE_STOPPED_AND_PREPARED || mState == STATE_PREPARED)) { err = start_l(); } @@ -419,7 +382,6 @@ status_t NuPlayer2Driver::seekTo(int64_t msec, MediaPlayer2SeekMode mode) { switch (mState) { case STATE_PREPARED: - case STATE_STOPPED_AND_PREPARED: case STATE_PAUSED: case STATE_RUNNING: { @@ -601,10 +563,6 @@ status_t NuPlayer2Driver::reset() { break; } - if (mState != STATE_STOPPED) { - // notifyListener_l(MEDIA2_STOPPED); - } - mState = STATE_RESET_IN_PROGRESS; mPlayer->resetAsync(); @@ -780,20 +738,7 @@ void NuPlayer2Driver::notifySeekComplete(int64_t srcId) { ALOGV("notifySeekComplete(%p)", this); Mutex::Autolock autoLock(mLock); mSeekInProgress = false; - notifySeekComplete_l(srcId); -} - -void NuPlayer2Driver::notifySeekComplete_l(int64_t srcId) { - bool wasSeeking = true; - if (mState == STATE_STOPPED_AND_PREPARING) { - wasSeeking = false; - mState = STATE_STOPPED_AND_PREPARED; - mCondition.broadcast(); - } else if (mState == STATE_STOPPED) { - // no need to notify listener - return; - } - notifyListener_l(srcId, wasSeeking ? MEDIA2_SEEK_COMPLETE : MEDIA2_PREPARED); + notifyListener_l(srcId, MEDIA2_SEEK_COMPLETE); } status_t NuPlayer2Driver::dump( @@ -1078,9 +1023,6 @@ std::string NuPlayer2Driver::stateString(State state) { case STATE_RUNNING: rval = "RUNNING"; break; case STATE_PAUSED: rval = "PAUSED"; break; case STATE_RESET_IN_PROGRESS: rval = "RESET_IN_PROGRESS"; break; - case STATE_STOPPED: rval = "STOPPED"; break; - case STATE_STOPPED_AND_PREPARING: rval = "STOPPED_AND_PREPARING"; break; - case STATE_STOPPED_AND_PREPARED: rval = "STOPPED_AND_PREPARED"; break; default: // yes, this buffer is shared and vulnerable to races snprintf(rawbuffer, sizeof(rawbuffer), "%d", state); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 4da256664d..6d5a007d38 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -40,31 +40,31 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { BufferingSettings* buffering /* nonnull */) override; virtual status_t setBufferingSettings(const BufferingSettings& buffering) override; - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate); - virtual status_t getPlaybackSettings(AudioPlaybackRate *rate); - virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint); - virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps); + virtual status_t prepareAsync() override; + virtual status_t start() override; + virtual status_t pause() override; + virtual bool isPlaying() override; + virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate) override; + virtual status_t getPlaybackSettings(AudioPlaybackRate *rate) override; + virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) override; + virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps) override; virtual status_t seekTo( - int64_t msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC); - virtual status_t getCurrentPosition(int64_t *msec); - virtual status_t getDuration(int64_t *msec); - virtual status_t reset(); + int64_t msec, + MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) override; + virtual status_t getCurrentPosition(int64_t *msec) override; + virtual status_t getDuration(int64_t *msec) override; + virtual status_t reset() override; virtual status_t notifyAt(int64_t mediaTimeUs) override; - virtual status_t setLooping(int loop); - virtual status_t invoke(const Parcel &request, Parcel *reply); - virtual void setAudioSink(const sp &audioSink); - virtual status_t setParameter(int key, const Parcel &request); - virtual status_t getParameter(int key, Parcel *reply); + virtual status_t setLooping(int loop) override; + virtual status_t invoke(const Parcel &request, Parcel *reply) override; + virtual void setAudioSink(const sp &audioSink) override; + virtual status_t setParameter(int key, const Parcel &request) override; + virtual status_t getParameter(int key, Parcel *reply) override; virtual status_t getMetadata( - const media::Metadata::Filter& ids, Parcel *records); + const media::Metadata::Filter& ids, Parcel *records) override; - virtual status_t dump(int fd, const Vector &args) const; + virtual status_t dump(int fd, const Vector &args) const override; virtual void onMessageReceived(const sp &msg) override; @@ -77,7 +77,6 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { void notifyMoreRebufferingTimeUs(int64_t srcId, int64_t timeUs); void notifyRebufferingWhenExit(int64_t srcId, bool status); void notifySeekComplete(int64_t srcId); - void notifySeekComplete_l(int64_t srcId); void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL); void notifyFlagsChanged(int64_t srcId, uint32_t flags); @@ -99,9 +98,6 @@ private: STATE_RUNNING, STATE_PAUSED, STATE_RESET_IN_PROGRESS, - STATE_STOPPED, // equivalent to PAUSED - STATE_STOPPED_AND_PREPARING, // equivalent to PAUSED, but seeking - STATE_STOPPED_AND_PREPARED, // equivalent to PAUSED, but seek complete }; std::string stateString(State state); -- GitLab From 2f074ce7ca8cce3189cebe0a0ce1b1ff402c814b Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 27 Aug 2018 11:39:53 -0700 Subject: [PATCH 0232/1530] Camera3: Handle stream format override for shared output stream If HAL overrides stream format, use the overridden format to configure the buffer queues. Bug: 113326269 Test: Camera CTS Change-Id: I6b198e8ebfeaeafbda530722d995a12f88f0b35a --- .../device3/Camera3SharedOutputStream.cpp | 3 +-- .../libcameraservice/device3/Camera3StreamSplitter.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp index 2bb9ff7a3f..fb3ce4c619 100644 --- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp @@ -60,9 +60,8 @@ status_t Camera3SharedOutputStream::connectStreamSplitterLocked() { } } - android::PixelFormat format = isFormatOverridden() ? getOriginalFormat() : getFormat(); res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera3_stream::max_buffers, - getWidth(), getHeight(), format, &mConsumer); + getWidth(), getHeight(), getFormat(), &mConsumer); if (res != OK) { ALOGE("%s: Failed to connect to stream splitter: %s(%d)", __FUNCTION__, strerror(-res), res); diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp index 8a9402e9a4..8bc208dfa7 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp +++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp @@ -182,12 +182,19 @@ status_t Camera3StreamSplitter::addOutputLocked(size_t surfaceId, const sp gbp = outputQueue->getIGraphicBufferProducer(); // Connect to the buffer producer -- GitLab From 33e7f547cace25f1223e3c9a5eceafc42eed6e85 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 7 Aug 2018 10:46:38 -0700 Subject: [PATCH 0233/1530] SoftXaac: Add OMX_AUDIO_AACObjectXHE to supported profiles list OMX_AUDIO_AACObjectXHE enum was missing from supported profiles list in SoftXaac plugin though it is supported Bug: 112310991 Test: cts-tradefed run commandAndExit cts-dev --abi armeabi-v7a\ -m CtsMediaTestCases -t android.media.cts.DecoderTestXheAac Change-Id: I37d14662f89fb65f8196363a5c26bb63583d81b9 --- media/libstagefright/codecs/xaacdec/SoftXAAC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp index 06b15b3e5c..f352fbab72 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -79,7 +79,7 @@ static void InitOMXParams(T* params) { static const OMX_U32 kSupportedProfiles[] = { OMX_AUDIO_AACObjectLC, OMX_AUDIO_AACObjectHE, OMX_AUDIO_AACObjectHE_PS, - OMX_AUDIO_AACObjectLD, OMX_AUDIO_AACObjectELD, + OMX_AUDIO_AACObjectLD, OMX_AUDIO_AACObjectELD, OMX_AUDIO_AACObjectXHE }; SoftXAAC::SoftXAAC(const char* name, const OMX_CALLBACKTYPE* callbacks, OMX_PTR appData, -- GitLab From a8115dc6d814c18d1ec68067425485add3ea1af2 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 24 Aug 2018 15:51:59 -0700 Subject: [PATCH 0234/1530] AudioFlinger: Add Thread history to dumpsys Test: Bluetooth + audioflinger dumpsys Bug: 80155745 Change-Id: Icee723a9fe653395119dd639883be5b7d8f3f158 --- services/audioflinger/AudioFlinger.cpp | 17 +++++++++++++++++ services/audioflinger/AudioFlinger.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index ed2b3c0e00..9e4d739a67 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -56,6 +56,7 @@ #include #include +#include #include #include @@ -525,6 +526,12 @@ status_t AudioFlinger::dump(int fd, const Vector& args) dumpLogger(mAppSetParameterLog, "App"); dumpLogger(mSystemSetParameterLog, "System"); + // dump historical threads in the last 10 seconds + const std::string threadLog = mThreadLog.dumpToString( + "Historical Thread Log ", 0 /* lines */, + audio_utils_get_real_time_ns() - 10 * 60 * NANOS_PER_SECOND); + write(fd, threadLog.c_str(), threadLog.size()); + BUFLOG_RESET; if (locked) { @@ -2291,6 +2298,16 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) if (playbackThread != NULL) { ALOGV("closeOutput() %d", output); + { + // Dump thread before deleting for history + audio_utils::FdToString fdToString; + const int fd = fdToString.fd(); + if (fd >= 0) { + playbackThread->dump(fd, {} /* args */); + mThreadLog.logs(-1 /* time */, fdToString.getStringAndClose()); + } + } + if (playbackThread->type() == ThreadBase::MIXER) { for (size_t i = 0; i < mPlaybackThreads.size(); i++) { if (mPlaybackThreads.valueAt(i)->isDuplicating()) { diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 9c36479fe3..53a7a8f851 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -426,6 +426,8 @@ private: void dumpClients(int fd, const Vector& args); void dumpInternals(int fd, const Vector& args); + SimpleLog mThreadLog{10}; // 10 Thread history limit + // --- Client --- class Client : public RefBase { public: -- GitLab From 5c4f82623287b09db3a8131dbe4508893648be90 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 17 Nov 2017 12:16:22 -0800 Subject: [PATCH 0235/1530] aaudio: clip setBufferSizeInFrames() Bug: 69469801 Test: test_various.cpp Change-Id: I8ce9a6ef19ed2bf12aa1d618b6a4095c94797636 --- media/libaaudio/src/fifo/FifoControllerBase.cpp | 5 +++++ media/libaaudio/src/fifo/FifoControllerBase.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp index 14a2be1caf..9885cb0205 100644 --- a/media/libaaudio/src/fifo/FifoControllerBase.cpp +++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp @@ -59,5 +59,10 @@ void FifoControllerBase::advanceWriteIndex(fifo_frames_t numFrames) { } void FifoControllerBase::setThreshold(fifo_frames_t threshold) { + if (threshold > mCapacity) { + threshold = mCapacity; + } else if (threshold < 0) { + threshold = 0; + } mThreshold = threshold; } diff --git a/media/libaaudio/src/fifo/FifoControllerBase.h b/media/libaaudio/src/fifo/FifoControllerBase.h index 64af77765e..1edb8a3cb5 100644 --- a/media/libaaudio/src/fifo/FifoControllerBase.h +++ b/media/libaaudio/src/fifo/FifoControllerBase.h @@ -102,6 +102,9 @@ public: /** * You can request that the buffer not be filled above a maximum * number of frames. + * + * The threshold will be clipped between zero and the buffer capacity. + * * @param threshold effective size of the buffer */ void setThreshold(fifo_frames_t threshold); -- GitLab From 9f6319815f0dea99268530f1fd0d0f4f299f7eee Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Tue, 10 Jul 2018 12:34:41 -0700 Subject: [PATCH 0236/1530] MediaPlayer2: use protobuf instead of parcel in invoke() Test: pass MediaPlayer2Test Bug: 63934228 Change-Id: Ie3d7c9352f369b6481e35c98e40b736cdff23204 --- media/libmediaplayer2/Android.bp | 2 ++ .../mediaplayer2/MediaPlayer2Interface.h | 6 +++- .../include/mediaplayer2/mediaplayer2.h | 2 +- media/libmediaplayer2/mediaplayer2.cpp | 5 ++-- media/libmediaplayer2/nuplayer2/Android.bp | 1 + media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 28 +++++++++---------- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 10 +++++-- .../nuplayer2/NuPlayer2Driver.cpp | 27 +++++++++--------- .../nuplayer2/NuPlayer2Driver.h | 2 +- 9 files changed, 45 insertions(+), 38 deletions(-) diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 0fb5abc126..300adecd74 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -56,6 +56,8 @@ cc_library { static_libs: [ "libmedia_helper", + "libmediaplayer2-protos", + "libprotobuf-cpp-lite", "libstagefright_nuplayer2", "libstagefright_rtsp", "libstagefright_timedtext", diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index a6bf543f2d..715bbe0dde 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -33,6 +33,10 @@ #include #include +#include "mediaplayer2.pb.h" + +using android::media::MediaPlayer2Proto::PlayerMessage; + // Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is // global, and not in android:: struct sockaddr_in; @@ -217,7 +221,7 @@ public: // data sent by the java layer. // @param[out] reply Parcel to hold the reply data. Cannot be null. // @return OK if the call was successful. - virtual status_t invoke(const Parcel& request, Parcel *reply) = 0; + virtual status_t invoke(const PlayerMessage &request, PlayerMessage *reply) = 0; // The Client in the MetadataPlayerService calls this method on // the native player to retrieve all or a subset of metadata. diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 43fba2396a..628e7328cf 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -90,7 +90,7 @@ public: status_t setVolume(float leftVolume, float rightVolume); void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj = NULL); - status_t invoke(const Parcel& request, Parcel *reply); + status_t invoke(const PlayerMessage &request, PlayerMessage *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); status_t setAudioSessionId(audio_session_t sessionId); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index f0ea59e9c5..18cf040044 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -565,16 +565,15 @@ status_t MediaPlayer2::playNextDataSource(int64_t srcId) { return mPlayer->playNextDataSource(srcId); } -status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) { +status_t MediaPlayer2::invoke(const PlayerMessage &request, PlayerMessage *reply) { Mutex::Autolock _l(mLock); const bool hasBeenInitialized = (mCurrentState != MEDIA_PLAYER2_STATE_ERROR) && ((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE); if ((mPlayer == NULL) || !hasBeenInitialized) { - ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get()); + ALOGE("invoke() failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get()); return INVALID_OPERATION; } - ALOGV("invoke %zu", request.dataSize()); return mPlayer->invoke(request, reply); } diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp index 1634f35409..dd91628621 100644 --- a/media/libmediaplayer2/nuplayer2/Android.bp +++ b/media/libmediaplayer2/nuplayer2/Android.bp @@ -54,6 +54,7 @@ cc_library_static { static_libs: [ "libmedia_helper", + "libmediaplayer2-protos", ], name: "libstagefright_nuplayer2", diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index b6b9b78aef..2a0fbddaa6 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -584,9 +584,8 @@ void NuPlayer2::seekToAsync(int64_t seekTimeUs, MediaPlayer2SeekMode mode, bool msg->post(); } - void NuPlayer2::writeTrackInfo( - Parcel* reply, const sp& format) const { + PlayerMessage* reply, const sp& format) const { if (format == NULL) { ALOGE("NULL format"); return; @@ -619,10 +618,9 @@ void NuPlayer2::writeTrackInfo( return; } - reply->writeInt32(2); // write something non-zero - reply->writeInt32(trackType); - reply->writeString16(String16(mime.c_str())); - reply->writeString16(String16(lang.c_str())); + reply->add_values()->set_int32_value(trackType); + reply->add_values()->set_string_value(mime.c_str()); + reply->add_values()->set_string_value(lang.c_str()); if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) { int32_t isAuto, isDefault, isForced; @@ -630,9 +628,9 @@ void NuPlayer2::writeTrackInfo( CHECK(format->findInt32("default", &isDefault)); CHECK(format->findInt32("forced", &isForced)); - reply->writeInt32(isAuto); - reply->writeInt32(isDefault); - reply->writeInt32(isForced); + reply->add_values()->set_int32_value(isAuto); + reply->add_values()->set_int32_value(isDefault); + reply->add_values()->set_int32_value(isForced); } } @@ -764,7 +762,7 @@ void NuPlayer2::onMessageReceived(const sp &msg) { sp replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - Parcel* reply; + PlayerMessage* reply; CHECK(msg->findPointer("reply", (void**)&reply)); size_t inbandTracks = 0; @@ -778,7 +776,7 @@ void NuPlayer2::onMessageReceived(const sp &msg) { } // total track count - reply->writeInt32(inbandTracks + ccTracks); + reply->add_values()->set_int32_value(inbandTracks + ccTracks); // write inband tracks for (size_t i = 0; i < inbandTracks; ++i) { @@ -806,9 +804,9 @@ void NuPlayer2::onMessageReceived(const sp &msg) { media_track_type type = (media_track_type)type32; ssize_t selectedTrack = mSource->getSelectedTrack(type); - Parcel* reply; + PlayerMessage* reply; CHECK(msg->findPointer("reply", (void**)&reply)); - reply->writeInt32(selectedTrack); + reply->add_values()->set_int32_value(selectedTrack); } sp response = new AMessage; @@ -2231,7 +2229,7 @@ status_t NuPlayer2::setVideoScalingMode(int32_t mode) { return OK; } -status_t NuPlayer2::getTrackInfo(Parcel* reply) const { +status_t NuPlayer2::getTrackInfo(PlayerMessage* reply) const { sp msg = new AMessage(kWhatGetTrackInfo, this); msg->setPointer("reply", reply); @@ -2240,7 +2238,7 @@ status_t NuPlayer2::getTrackInfo(Parcel* reply) const { return err; } -status_t NuPlayer2::getSelectedTrack(int32_t type, Parcel* reply) const { +status_t NuPlayer2::getSelectedTrack(int32_t type, PlayerMessage* reply) const { sp msg = new AMessage(kWhatGetSelectedTrack, this); msg->setPointer("reply", reply); msg->setInt32("type", type); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index 96f85f9d90..c92d408941 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -23,6 +23,10 @@ #include +#include "mediaplayer2.pb.h" + +using android::media::MediaPlayer2Proto::PlayerMessage; + namespace android { struct ABuffer; @@ -77,8 +81,8 @@ struct NuPlayer2 : public AHandler { bool needNotify = false); status_t setVideoScalingMode(int32_t mode); - status_t getTrackInfo(Parcel* reply) const; - status_t getSelectedTrack(int32_t type, Parcel* reply) const; + status_t getTrackInfo(PlayerMessage* reply) const; + status_t getSelectedTrack(int32_t type, PlayerMessage* reply) const; status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); status_t getCurrentPosition(int64_t *mediaUs); void getStats(Vector > *mTrackStats); @@ -333,7 +337,7 @@ private: void sendTimedMetaData(const sp &buffer); void sendTimedTextData(const sp &buffer); - void writeTrackInfo(Parcel* reply, const sp& format) const; + void writeTrackInfo(PlayerMessage* reply, const sp& format) const; status_t onPrepareDrm(const sp &msg); status_t onReleaseDrm(); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index f85e3a2f87..fd231a3ec7 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -36,6 +36,9 @@ #include +using google::protobuf::RepeatedPtrField; +using android::media::MediaPlayer2Proto::Value; + static const int kDumpLockRetries = 50; static const int kDumpLockSleepUs = 20000; @@ -591,34 +594,30 @@ status_t NuPlayer2Driver::setLooping(int loop) { return OK; } -status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) { - if (reply == NULL) { +status_t NuPlayer2Driver::invoke(const PlayerMessage &request, PlayerMessage *response) { + if (response == NULL) { ALOGE("reply is a NULL pointer"); return BAD_VALUE; } - int32_t methodId; - status_t ret = request.readInt32(&methodId); - if (ret != OK) { - ALOGE("Failed to retrieve the requested method to invoke, err(%d)", ret); - return ret; - } + RepeatedPtrField::const_iterator it = request.values().cbegin(); + int32_t methodId = (it++)->int32_value(); switch (methodId) { case MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE: { - int mode = request.readInt32(); + int mode = (it++)->int32_value(); return mPlayer->setVideoScalingMode(mode); } case MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO: { - return mPlayer->getTrackInfo(reply); + return mPlayer->getTrackInfo(response); } case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK: { - int trackIndex = request.readInt32(); + int trackIndex = (it++)->int32_value(); int64_t msec = 0; // getCurrentPosition should always return OK getCurrentPosition(&msec); @@ -627,14 +626,14 @@ status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) { case MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK: { - int trackIndex = request.readInt32(); + int trackIndex = (it++)->int32_value(); return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); } case MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK: { - int32_t type = request.readInt32(); - return mPlayer->getSelectedTrack(type, reply); + int32_t type = (it++)->int32_value(); + return mPlayer->getSelectedTrack(type, response); } default: diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 6d5a007d38..8dfcaa165a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -56,7 +56,7 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { virtual status_t reset() override; virtual status_t notifyAt(int64_t mediaTimeUs) override; virtual status_t setLooping(int loop) override; - virtual status_t invoke(const Parcel &request, Parcel *reply) override; + virtual status_t invoke(const PlayerMessage &request, PlayerMessage *response) override; virtual void setAudioSink(const sp &audioSink) override; virtual status_t setParameter(int key, const Parcel &request) override; virtual status_t getParameter(int key, Parcel *reply) override; -- GitLab From 60b65449e7494c27aea059908f547eff5817fdda Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 29 Aug 2018 13:02:41 -0700 Subject: [PATCH 0237/1530] Fix double free in MPEG4Extractor Bug: 113341381 Test: yes Change-Id: I6ae1cb03b04fa1d9b664a0ca8b29a21f60bd6bc3 --- media/extractors/mp4/MPEG4Extractor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index c02d2fb10c..f52d451356 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -393,7 +393,9 @@ MPEG4Extractor::~MPEG4Extractor() { } mPssh.clear(); - delete mCachedSource; + if (mCachedSource != mDataSource) { + delete mCachedSource; + } delete mDataSource; } -- GitLab From 64dce361eb3dbfc8c78f474b4c24dcaceb904ae1 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 28 Mar 2018 15:30:39 -0700 Subject: [PATCH 0238/1530] aaudio: add simple flowgraph system Implement a simple data flow model that can be used to chain various processing modules including: data format conversion volume ramping channel conversion Bug: 65067568 Test: test_flowgraph.cpp Change-Id: I81a5655406cfa8c1c7d7cef4d933879f823939a5 --- media/libaaudio/src/Android.bp | 11 + .../src/flowgraph/AudioProcessorBase.cpp | 82 +++++ .../src/flowgraph/AudioProcessorBase.h | 293 ++++++++++++++++++ media/libaaudio/src/flowgraph/ClipToRange.cpp | 40 +++ media/libaaudio/src/flowgraph/ClipToRange.h | 67 ++++ .../src/flowgraph/MonoToMultiConverter.cpp | 47 +++ .../src/flowgraph/MonoToMultiConverter.h | 42 +++ media/libaaudio/src/flowgraph/RampLinear.cpp | 85 +++++ media/libaaudio/src/flowgraph/RampLinear.h | 87 ++++++ media/libaaudio/src/flowgraph/SinkFloat.cpp | 46 +++ media/libaaudio/src/flowgraph/SinkFloat.h | 38 +++ media/libaaudio/src/flowgraph/SinkI16.cpp | 58 ++++ media/libaaudio/src/flowgraph/SinkI16.h | 36 +++ media/libaaudio/src/flowgraph/SinkI24.cpp | 66 ++++ media/libaaudio/src/flowgraph/SinkI24.h | 36 +++ media/libaaudio/src/flowgraph/SourceFloat.cpp | 43 +++ media/libaaudio/src/flowgraph/SourceFloat.h | 36 +++ media/libaaudio/src/flowgraph/SourceI16.cpp | 54 ++++ media/libaaudio/src/flowgraph/SourceI16.h | 36 +++ media/libaaudio/src/flowgraph/SourceI24.cpp | 65 ++++ media/libaaudio/src/flowgraph/SourceI24.h | 36 +++ 21 files changed, 1304 insertions(+) create mode 100644 media/libaaudio/src/flowgraph/AudioProcessorBase.cpp create mode 100644 media/libaaudio/src/flowgraph/AudioProcessorBase.h create mode 100644 media/libaaudio/src/flowgraph/ClipToRange.cpp create mode 100644 media/libaaudio/src/flowgraph/ClipToRange.h create mode 100644 media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp create mode 100644 media/libaaudio/src/flowgraph/MonoToMultiConverter.h create mode 100644 media/libaaudio/src/flowgraph/RampLinear.cpp create mode 100644 media/libaaudio/src/flowgraph/RampLinear.h create mode 100644 media/libaaudio/src/flowgraph/SinkFloat.cpp create mode 100644 media/libaaudio/src/flowgraph/SinkFloat.h create mode 100644 media/libaaudio/src/flowgraph/SinkI16.cpp create mode 100644 media/libaaudio/src/flowgraph/SinkI16.h create mode 100644 media/libaaudio/src/flowgraph/SinkI24.cpp create mode 100644 media/libaaudio/src/flowgraph/SinkI24.h create mode 100644 media/libaaudio/src/flowgraph/SourceFloat.cpp create mode 100644 media/libaaudio/src/flowgraph/SourceFloat.h create mode 100644 media/libaaudio/src/flowgraph/SourceI16.cpp create mode 100644 media/libaaudio/src/flowgraph/SourceI16.h create mode 100644 media/libaaudio/src/flowgraph/SourceI24.cpp create mode 100644 media/libaaudio/src/flowgraph/SourceI24.h diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp index b9e28a063d..4b5f30dcaa 100644 --- a/media/libaaudio/src/Android.bp +++ b/media/libaaudio/src/Android.bp @@ -6,6 +6,7 @@ cc_library { "client", "core", "fifo", + "flowgraph", "legacy", "utility", ], @@ -42,6 +43,16 @@ cc_library { "binding/RingBufferParcelable.cpp", "binding/SharedMemoryParcelable.cpp", "binding/SharedRegionParcelable.cpp", + "flowgraph/AudioProcessorBase.cpp", + "flowgraph/ClipToRange.cpp", + "flowgraph/MonoToMultiConverter.cpp", + "flowgraph/RampLinear.cpp", + "flowgraph/SinkFloat.cpp", + "flowgraph/SinkI16.cpp", + "flowgraph/SinkI24.cpp", + "flowgraph/SourceFloat.cpp", + "flowgraph/SourceI16.cpp", + "flowgraph/SourceI24.cpp", ], cflags: [ diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp new file mode 100644 index 0000000000..5667fdbc7b --- /dev/null +++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "AudioProcessorBase.h" + +using namespace flowgraph; + +/***************************************************************************/ +int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) { + if (framePosition > mLastFramePosition) { + mLastFramePosition = framePosition; + mFramesValid = onProcess(framePosition, numFrames); + } + return mFramesValid; +} + +/***************************************************************************/ +AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent, + int32_t samplesPerFrame, + int32_t framesPerBlock) + : AudioPort(parent, samplesPerFrame) + , mFramesPerBlock(framesPerBlock) + , mSampleBlock(NULL) { + int32_t numFloats = framesPerBlock * getSamplesPerFrame(); + mSampleBlock = new float[numFloats]{0.0f}; +} + +AudioFloatBlockPort::~AudioFloatBlockPort() { + delete[] mSampleBlock; +} + +/***************************************************************************/ +int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) { + numFrames = std::min(getFramesPerBlock(), numFrames); + return mParent.pullData(framePosition, numFrames); +} + +// These need to be in the .cpp file because of forward cross references. +void AudioFloatOutputPort::connect(AudioFloatInputPort *port) { + port->connect(this); +} + +void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) { + port->disconnect(this); +} + +/***************************************************************************/ +int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) { + return (mConnected == NULL) + ? std::min(getFramesPerBlock(), numFrames) + : mConnected->pullData(framePosition, numFrames); +} + +float *AudioFloatInputPort::getBlock() { + if (mConnected == NULL) { + return AudioFloatBlockPort::getBlock(); // loaded using setValue() + } else { + return mConnected->getBlock(); + } +} + +/***************************************************************************/ +int32_t AudioSink::pull(int32_t numFrames) { + int32_t actualFrames = input.pullData(mFramePosition, numFrames); + mFramePosition += actualFrames; + return actualFrames; +} \ No newline at end of file diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h new file mode 100644 index 0000000000..eda46aef43 --- /dev/null +++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h @@ -0,0 +1,293 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * AudioProcessorBase.h + * + * Audio processing node and ports that can be used in a simple data flow graph. + */ + +#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H +#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H + +#include +#include +#include +#include +#include +#include + +// TODO consider publishing all header files under "include/libaaudio/FlowGraph.h" + +namespace flowgraph { + +// Default block size that can be overridden when the AudioFloatBlockPort is created. +// If it is too small then we will have too much overhead from switching between nodes. +// If it is too high then we will thrash the caches. +constexpr int kDefaultBlockSize = 8; // arbitrary + +class AudioFloatInputPort; + +/***************************************************************************/ +class AudioProcessorBase { +public: + virtual ~AudioProcessorBase() = default; + + /** + * Perform custom function. + * + * @param framePosition index of first frame to be processed + * @param numFrames maximum number of frames requested for processing + * @return number of frames actually processed + */ + virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0; + + /** + * If the framePosition is at or after the last frame position then call onProcess(). + * This prevents infinite recursion in case of cyclic graphs. + * It also prevents nodes upstream from a branch from being executed twice. + * + * @param framePosition + * @param numFrames + * @return + */ + int32_t pullData(int64_t framePosition, int32_t numFrames); + +protected: + int64_t mLastFramePosition = -1; // Start at -1 so that the first pull works. + +private: + int32_t mFramesValid = 0; // num valid frames in the block +}; + +/***************************************************************************/ +/** + * This is a connector that allows data to flow between modules. + */ +class AudioPort { +public: + AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame) + : mParent(parent) + , mSamplesPerFrame(samplesPerFrame) { + } + + // Ports are often declared public. So let's make them non-copyable. + AudioPort(const AudioPort&) = delete; + AudioPort& operator=(const AudioPort&) = delete; + + int32_t getSamplesPerFrame() const { + return mSamplesPerFrame; + } + +protected: + AudioProcessorBase &mParent; + +private: + const int32_t mSamplesPerFrame = 1; +}; + +/***************************************************************************/ +/** + * This port contains a float type buffer. + * The size is framesPerBlock * samplesPerFrame). + */ +class AudioFloatBlockPort : public AudioPort { +public: + AudioFloatBlockPort(AudioProcessorBase &mParent, + int32_t samplesPerFrame, + int32_t framesPerBlock = kDefaultBlockSize + ); + + virtual ~AudioFloatBlockPort(); + + int32_t getFramesPerBlock() const { + return mFramesPerBlock; + } + +protected: + + /** + * @return buffer internal to the port or from a connected port + */ + virtual float *getBlock() { + return mSampleBlock; + } + + +private: + const int32_t mFramesPerBlock = 1; + float *mSampleBlock = nullptr; // allocated in constructor +}; + +/***************************************************************************/ +/** + * The results of a module are stored in the buffer of the output ports. + */ +class AudioFloatOutputPort : public AudioFloatBlockPort { +public: + AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame) + : AudioFloatBlockPort(parent, samplesPerFrame) { + } + + virtual ~AudioFloatOutputPort() = default; + + using AudioFloatBlockPort::getBlock; + + /** + * Call the parent module's onProcess() method. + * That may pull data from its inputs and recursively + * process the entire graph. + * @return number of frames actually pulled + */ + int32_t pullData(int64_t framePosition, int32_t numFrames); + + /** + * Connect to the input of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * If you connect a second output port to an input port + * then it overwrites the previous connection. + * + * This not thread safe. Do not modify the graph topology form another thread while running. + */ + void connect(AudioFloatInputPort *port); + + /** + * Disconnect from the input of another module. + * This not thread safe. + */ + void disconnect(AudioFloatInputPort *port); +}; + +/***************************************************************************/ +class AudioFloatInputPort : public AudioFloatBlockPort { +public: + AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame) + : AudioFloatBlockPort(parent, samplesPerFrame) { + } + + virtual ~AudioFloatInputPort() = default; + + /** + * If connected to an output port then this will return + * that output ports buffers. + * If not connected then it returns the input ports own buffer + * which can be loaded using setValue(). + */ + float *getBlock() override; + + /** + * Pull data from any output port that is connected. + */ + int32_t pullData(int64_t framePosition, int32_t numFrames); + + /** + * Write every value of the float buffer. + * This value will be ignored if an output port is connected + * to this port. + */ + void setValue(float value) { + int numFloats = kDefaultBlockSize * getSamplesPerFrame(); + float *buffer = getBlock(); + for (int i = 0; i < numFloats; i++) { + *buffer++ = value; + } + } + + /** + * Connect to the output of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * This not thread safe. + */ + void connect(AudioFloatOutputPort *port) { + assert(getSamplesPerFrame() == port->getSamplesPerFrame()); + mConnected = port; + } + + void disconnect(AudioFloatOutputPort *port) { + assert(mConnected == port); + (void) port; + mConnected = nullptr; + } + + void disconnect() { + mConnected = nullptr; + } + +private: + AudioFloatOutputPort *mConnected = nullptr; +}; + +/***************************************************************************/ +class AudioSource : public AudioProcessorBase { +public: + explicit AudioSource(int32_t channelCount) + : output(*this, channelCount) { + } + + virtual ~AudioSource() = default; + + AudioFloatOutputPort output; + + void setData(const void *data, int32_t numFrames) { + mData = data; + mSizeInFrames = numFrames; + mFrameIndex = 0; + } + +protected: + const void *mData = nullptr; + int32_t mSizeInFrames = 0; // number of frames in mData + int32_t mFrameIndex = 0; // index of next frame to be processed +}; + +/***************************************************************************/ +class AudioSink : public AudioProcessorBase { +public: + explicit AudioSink(int32_t channelCount) + : input(*this, channelCount) { + } + + virtual ~AudioSink() = default; + + AudioFloatInputPort input; + + /** + * Dummy processor. The work happens in the read() method. + * + * @param framePosition index of first frame to be processed + * @param numFrames + * @return number of frames actually processed + */ + int32_t onProcess(int64_t framePosition, int32_t numFrames) override { + (void) framePosition; + (void) numFrames; + return 0; + }; + + virtual int32_t read(void *data, int32_t numFrames) = 0; + +protected: + int32_t pull(int32_t numFrames); + +private: + int64_t mFramePosition = 0; +}; + +} /* namespace flowgraph */ + +#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */ diff --git a/media/libaaudio/src/flowgraph/ClipToRange.cpp b/media/libaaudio/src/flowgraph/ClipToRange.cpp new file mode 100644 index 0000000000..bd9c22adfb --- /dev/null +++ b/media/libaaudio/src/flowgraph/ClipToRange.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "AudioProcessorBase.h" +#include "ClipToRange.h" + +using namespace flowgraph; + +ClipToRange::ClipToRange(int32_t channelCount) + : input(*this, channelCount) + , output(*this, channelCount) { +} + +int32_t ClipToRange::onProcess(int64_t framePosition, int32_t numFrames) { + int32_t framesToProcess = input.pullData(framePosition, numFrames); + const float *inputBuffer = input.getBlock(); + float *outputBuffer = output.getBlock(); + + int32_t numSamples = framesToProcess * output.getSamplesPerFrame(); + for (int32_t i = 0; i < numSamples; i++) { + *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++)); + } + + return framesToProcess; +} diff --git a/media/libaaudio/src/flowgraph/ClipToRange.h b/media/libaaudio/src/flowgraph/ClipToRange.h new file mode 100644 index 0000000000..9eef254942 --- /dev/null +++ b/media/libaaudio/src/flowgraph/ClipToRange.h @@ -0,0 +1,67 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_CLIP_TO_RANGE_H +#define FLOWGRAPH_CLIP_TO_RANGE_H + +#include +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data. +// It is designed to allow occasional transient peaks. +constexpr float kDefaultMaxHeadroom = 1.41253754f; +constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom; + +class ClipToRange : public AudioProcessorBase { +public: + explicit ClipToRange(int32_t channelCount); + + virtual ~ClipToRange() = default; + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + + void setMinimum(float min) { + mMinimum = min; + } + + float getMinimum() const { + return mMinimum; + } + + void setMaximum(float min) { + mMaximum = min; + } + + float getMaximum() const { + return mMaximum; + } + + AudioFloatInputPort input; + AudioFloatOutputPort output; + +private: + float mMinimum = kDefaultMinHeadroom; + float mMaximum = kDefaultMaxHeadroom; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_CLIP_TO_RANGE_H diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp new file mode 100644 index 0000000000..78aad52fa2 --- /dev/null +++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include "AudioProcessorBase.h" +#include "MonoToMultiConverter.h" + +using namespace flowgraph; + +MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount) + : input(*this, 1) + , output(*this, channelCount) { +} + +MonoToMultiConverter::~MonoToMultiConverter() { } + +int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) { + int32_t framesToProcess = input.pullData(framePosition, numFrames); + + const float *inputBuffer = input.getBlock(); + float *outputBuffer = output.getBlock(); + int32_t channelCount = output.getSamplesPerFrame(); + // TODO maybe move to audio_util as audio_mono_to_multi() + for (int i = 0; i < framesToProcess; i++) { + // read one, write many + float sample = *inputBuffer++; + for (int channel = 0; channel < channelCount; channel++) { + *outputBuffer++ = sample; + } + } + return framesToProcess; +} + diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h new file mode 100644 index 0000000000..34d53c713a --- /dev/null +++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H +#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class MonoToMultiConverter : public AudioProcessorBase { +public: + explicit MonoToMultiConverter(int32_t channelCount); + + virtual ~MonoToMultiConverter(); + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + + AudioFloatInputPort input; + AudioFloatOutputPort output; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/RampLinear.cpp b/media/libaaudio/src/flowgraph/RampLinear.cpp new file mode 100644 index 0000000000..a260828696 --- /dev/null +++ b/media/libaaudio/src/flowgraph/RampLinear.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RampLinear" +//#define LOG_NDEBUG 0 +#include + +#include +#include +#include "AudioProcessorBase.h" +#include "RampLinear.h" + +using namespace flowgraph; + +RampLinear::RampLinear(int32_t channelCount) + : input(*this, channelCount) + , output(*this, channelCount) { + mTarget.store(1.0f); +} + +void RampLinear::setLengthInFrames(int32_t frames) { + mLengthInFrames = frames; +} + +void RampLinear::setTarget(float target) { + mTarget.store(target); +} + +float RampLinear::interpolateCurrent() { + return mLevelTo - (mRemaining * mScaler); +} + +int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) { + int32_t framesToProcess = input.pullData(framePosition, numFrames); + const float *inputBuffer = input.getBlock(); + float *outputBuffer = output.getBlock(); + int32_t channelCount = output.getSamplesPerFrame(); + + float target = getTarget(); + if (target != mLevelTo) { + // Start new ramp. Continue from previous level. + mLevelFrom = interpolateCurrent(); + mLevelTo = target; + mRemaining = mLengthInFrames; + ALOGV("%s() mLevelFrom = %f, mLevelTo = %f, mRemaining = %d, mScaler = %f", + __func__, mLevelFrom, mLevelTo, mRemaining, mScaler); + mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation + } + + int32_t framesLeft = framesToProcess; + + if (mRemaining > 0) { // Ramping? This doesn't happen very often. + int32_t framesToRamp = std::min(framesLeft, mRemaining); + framesLeft -= framesToRamp; + while (framesToRamp > 0) { + float currentLevel = interpolateCurrent(); + for (int ch = 0; ch < channelCount; ch++) { + *outputBuffer++ = *inputBuffer++ * currentLevel; + } + mRemaining--; + framesToRamp--; + } + } + + // Process any frames after the ramp. + int32_t samplesLeft = framesLeft * channelCount; + for (int i = 0; i < samplesLeft; i++) { + *outputBuffer++ = *inputBuffer++ * mLevelTo; + } + + return framesToProcess; +} diff --git a/media/libaaudio/src/flowgraph/RampLinear.h b/media/libaaudio/src/flowgraph/RampLinear.h new file mode 100644 index 0000000000..bdc8f41c66 --- /dev/null +++ b/media/libaaudio/src/flowgraph/RampLinear.h @@ -0,0 +1,87 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_RAMP_LINEAR_H +#define FLOWGRAPH_RAMP_LINEAR_H + +#include +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class RampLinear : public AudioProcessorBase { +public: + explicit RampLinear(int32_t channelCount); + + virtual ~RampLinear() = default; + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + + /** + * This is used for the next ramp. + * Calling this does not affect a ramp that is in progress. + */ + void setLengthInFrames(int32_t frames); + + int32_t getLengthInFrames() const { + return mLengthInFrames; + } + + /** + * This may be safely called by another thread. + * @param target + */ + void setTarget(float target); + + float getTarget() const { + return mTarget.load(); + } + + /** + * Force the nextSegment to start from this level. + * + * WARNING: this can cause a discontinuity if called while the ramp is being used. + * Only call this when setting the initial ramp. + * + * @param level + */ + void forceCurrent(float level) { + mLevelFrom = level; + mLevelTo = level; + } + + AudioFloatInputPort input; + AudioFloatOutputPort output; + +private: + + float interpolateCurrent(); + + std::atomic mTarget; + + int32_t mLengthInFrames = 48000.0f / 100.0f ; // 10 msec at 48000 Hz; + int32_t mRemaining = 0; + float mScaler = 0.0f; + float mLevelFrom = 0.0f; + float mLevelTo = 0.0f; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_RAMP_LINEAR_H diff --git a/media/libaaudio/src/flowgraph/SinkFloat.cpp b/media/libaaudio/src/flowgraph/SinkFloat.cpp new file mode 100644 index 0000000000..fb3dcbc937 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkFloat.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "AudioProcessorBase.h" +#include "SinkFloat.h" + +using namespace flowgraph; + +SinkFloat::SinkFloat(int32_t channelCount) + : AudioSink(channelCount) { +} + +int32_t SinkFloat::read(void *data, int32_t numFrames) { + float *floatData = (float *) data; + int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pull(framesLeft); + if (framesRead <= 0) { + break; + } + const float *signal = input.getBlock(); + int32_t numSamples = framesRead * channelCount; + memcpy(floatData, signal, numSamples * sizeof(float)); + floatData += numSamples; + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/media/libaaudio/src/flowgraph/SinkFloat.h b/media/libaaudio/src/flowgraph/SinkFloat.h new file mode 100644 index 0000000000..7775c08135 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkFloat.h @@ -0,0 +1,38 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef FLOWGRAPH_SINK_FLOAT_H +#define FLOWGRAPH_SINK_FLOAT_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SinkFloat : public AudioSink { +public: + explicit SinkFloat(int32_t channelCount); + + int32_t read(void *data, int32_t numFrames) override; + +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SINK_FLOAT_H diff --git a/media/libaaudio/src/flowgraph/SinkI16.cpp b/media/libaaudio/src/flowgraph/SinkI16.cpp new file mode 100644 index 0000000000..ffec8f5371 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkI16.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#include "AudioProcessorBase.h" +#include "SinkI16.h" + +using namespace flowgraph; + +SinkI16::SinkI16(int32_t channelCount) + : AudioSink(channelCount) {} + +int32_t SinkI16::read(void *data, int32_t numFrames) { + int16_t *shortData = (int16_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pull(framesLeft); + if (framesRead <= 0) { + break; + } + const float *signal = input.getBlock(); + int32_t numSamples = framesRead * channelCount; +#ifdef __ANDROID__ + memcpy_to_i16_from_float(shortData, signal, numSamples); + shortData += numSamples; + signal += numSamples; +#else + for (int i = 0; i < numSamples; i++) { + int32_t n = (int32_t) (*signal++ * 32768.0f); + *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/media/libaaudio/src/flowgraph/SinkI16.h b/media/libaaudio/src/flowgraph/SinkI16.h new file mode 100644 index 0000000000..6d86266e95 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkI16.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SINK_I16_H +#define FLOWGRAPH_SINK_I16_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SinkI16 : public AudioSink { +public: + explicit SinkI16(int32_t channelCount); + + int32_t read(void *data, int32_t numFrames) override; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SINK_I16_H diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp new file mode 100644 index 0000000000..6592828e07 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkI24.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#include "AudioProcessorBase.h" +#include "SinkI24.h" + +using namespace flowgraph; + + +SinkI24::SinkI24(int32_t channelCount) + : AudioSink(channelCount) {} + +int32_t SinkI24::read(void *data, int32_t numFrames) { + uint8_t *byteData = (uint8_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pull(framesLeft); + if (framesRead <= 0) { + break; + } + const float *floatData = input.getBlock(); + int32_t numSamples = framesRead * channelCount; +#ifdef __ANDROID__ + memcpy_to_p24_from_float(byteData, floatData, numSamples); + static const int kBytesPerI24Packed = 3; + byteData += numSamples * kBytesPerI24Packed; + floatData += numSamples; +#else + const int32_t kI24PackedMax = 0x007FFFFF; + const int32_t kI24PackedMin = 0xFF800000; + for (int i = 0; i < numSamples; i++) { + int32_t n = (int32_t) (*floatData++ * 0x00800000); + n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip + // Write as a packed 24-bit integer in Little Endian format. + *byteData++ = (uint8_t) n; + *byteData++ = (uint8_t) (n >> 8); + *byteData++ = (uint8_t) (n >> 16); + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/media/libaaudio/src/flowgraph/SinkI24.h b/media/libaaudio/src/flowgraph/SinkI24.h new file mode 100644 index 0000000000..5b9b505421 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SinkI24.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SINK_I24_H +#define FLOWGRAPH_SINK_I24_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SinkI24 : public AudioSink { +public: + explicit SinkI24(int32_t channelCount); + + int32_t read(void *data, int32_t numFrames) override; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SINK_I24_H diff --git a/media/libaaudio/src/flowgraph/SourceFloat.cpp b/media/libaaudio/src/flowgraph/SourceFloat.cpp new file mode 100644 index 0000000000..4bb674fdfc --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceFloat.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "AudioProcessorBase.h" +#include "SourceFloat.h" + +using namespace flowgraph; + +SourceFloat::SourceFloat(int32_t channelCount) + : AudioSource(channelCount) { +} + +int32_t SourceFloat::onProcess(int64_t framePosition, int32_t numFrames) { + + float *outputBuffer = output.getBlock(); + int32_t channelCount = output.getSamplesPerFrame(); + + int32_t framesLeft = mSizeInFrames - mFrameIndex; + int32_t framesToProcess = std::min(numFrames, framesLeft); + int32_t numSamples = framesToProcess * channelCount; + + const float *floatBase = (float *) mData; + const float *floatData = &floatBase[mFrameIndex * channelCount]; + memcpy(outputBuffer, floatData, numSamples * sizeof(float)); + mFrameIndex += framesToProcess; + return framesToProcess; +} + diff --git a/media/libaaudio/src/flowgraph/SourceFloat.h b/media/libaaudio/src/flowgraph/SourceFloat.h new file mode 100644 index 0000000000..e6eed9f90a --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceFloat.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_FLOAT_H +#define FLOWGRAPH_SOURCE_FLOAT_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SourceFloat : public AudioSource { +public: + explicit SourceFloat(int32_t channelCount); + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SOURCE_FLOAT_H diff --git a/media/libaaudio/src/flowgraph/SourceI16.cpp b/media/libaaudio/src/flowgraph/SourceI16.cpp new file mode 100644 index 0000000000..c3fcec28e2 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceI16.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#include "AudioProcessorBase.h" +#include "SourceI16.h" + +using namespace flowgraph; + +SourceI16::SourceI16(int32_t channelCount) + : AudioSource(channelCount) { +} + +int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) { + float *floatData = output.getBlock(); + int32_t channelCount = output.getSamplesPerFrame(); + + int32_t framesLeft = mSizeInFrames - mFrameIndex; + int32_t framesToProcess = std::min(numFrames, framesLeft); + int32_t numSamples = framesToProcess * channelCount; + + const int16_t *shortBase = static_cast(mData); + const int16_t *shortData = &shortBase[mFrameIndex * channelCount]; + +#ifdef __ANDROID__ + memcpy_to_float_from_i16(floatData, shortData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *shortData++ * (1.0f / 32768); + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} \ No newline at end of file diff --git a/media/libaaudio/src/flowgraph/SourceI16.h b/media/libaaudio/src/flowgraph/SourceI16.h new file mode 100644 index 0000000000..2b116cfb2d --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceI16.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_I16_H +#define FLOWGRAPH_SOURCE_I16_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SourceI16 : public AudioSource { +public: + explicit SourceI16(int32_t channelCount); + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SOURCE_I16_H diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp new file mode 100644 index 0000000000..f319880098 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceI24.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#include "AudioProcessorBase.h" +#include "SourceI24.h" + +using namespace flowgraph; + +constexpr int kBytesPerI24Packed = 3; + +SourceI24::SourceI24(int32_t channelCount) + : AudioSource(channelCount) { +} + +int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) { + float *floatData = output.getBlock(); + int32_t channelCount = output.getSamplesPerFrame(); + + int32_t framesLeft = mSizeInFrames - mFrameIndex; + int32_t framesToProcess = std::min(numFrames, framesLeft); + int32_t numSamples = framesToProcess * channelCount; + + const uint8_t *byteBase = (uint8_t *) mData; + const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed]; + +#ifdef __ANDROID__ + memcpy_to_float_from_p24(floatData, byteData, numSamples); +#else + static const float scale = 1. / (float)(1UL << 31); + for (int i = 0; i < numSamples; i++) { + // Assemble the data assuming Little Endian format. + int32_t pad = byteData[2]; + pad <<= 8; + pad |= byteData[1]; + pad <<= 8; + pad |= byteData[0]; + pad <<= 8; // Shift to 32 bit data so the sign is correct. + byteData += kBytesPerI24Packed; + *floatData++ = pad * scale; // scale to range -1.0 to 1.0 + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} \ No newline at end of file diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h new file mode 100644 index 0000000000..39f14daf78 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SourceI24.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_I24_H +#define FLOWGRAPH_SOURCE_I24_H + +#include +#include + +#include "AudioProcessorBase.h" + +namespace flowgraph { + +class SourceI24 : public AudioSource { +public: + explicit SourceI24(int32_t channelCount); + + int32_t onProcess(int64_t framePosition, int32_t numFrames) override; +}; + +} /* namespace flowgraph */ + +#endif //FLOWGRAPH_SOURCE_I24_H -- GitLab From bb78a73db83955151feb6b150f7cb925bcabe79a Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 28 Mar 2018 15:37:19 -0700 Subject: [PATCH 0239/1530] aaudio test: test flowgraph Construct simple data flow graphs and measure the output. Bug: 65067568 Test: this is a test Change-Id: I7d7794fd56c2e2ced130ea501a456f2690a64b59 --- media/libaaudio/tests/Android.bp | 12 ++ media/libaaudio/tests/test_flowgraph.cpp | 157 +++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 media/libaaudio/tests/test_flowgraph.cpp diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index 68194dba8c..99aa4d6945 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -167,3 +167,15 @@ cc_test { srcs: ["test_atomic_fifo.cpp"], shared_libs: ["libaaudio"], } + +cc_test { + name: "test_flowgraph", + defaults: ["libaaudio_tests_defaults"], + srcs: ["test_flowgraph.cpp"], + shared_libs: [ + "libaaudio", + "libbinder", + "libcutils", + "libutils", + ], +} diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp new file mode 100644 index 0000000000..d563a7e8da --- /dev/null +++ b/media/libaaudio/tests/test_flowgraph.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Test FlowGraph + */ + +#include + +#include + +#include "flowgraph/ClipToRange.h" +#include "flowgraph/MonoToMultiConverter.h" +#include "flowgraph/SourceFloat.h" +#include "flowgraph/RampLinear.h" +#include "flowgraph/SinkFloat.h" +#include "flowgraph/SinkI16.h" +#include "flowgraph/SinkI24.h" +#include "flowgraph/SourceI16.h" +#include "flowgraph/SourceI24.h" + +using namespace flowgraph; + +constexpr int kBytesPerI24Packed = 3; + +TEST(test_flowgraph, module_sinki16) { + static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f}; + static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768}; + int16_t output[20]; + SourceFloat sourceFloat{1}; + SinkI16 sinkI16{1}; + + int numInputFrames = sizeof(input) / sizeof(input[0]); + sourceFloat.setData(input, numInputFrames); + sourceFloat.output.connect(&sinkI16.input); + + int numOutputFrames = sizeof(output) / sizeof(int16_t); + int32_t numRead = sinkI16.read(output, numOutputFrames); + ASSERT_EQ(numInputFrames, numRead); + for (int i = 0; i < numRead; i++) { + EXPECT_EQ(expected[i], output[i]); + } +} + +TEST(test_flowgraph, module_mono_to_stereo) { + static const float input[] = {1.0f, 2.0f, 3.0f}; + float output[100] = {}; + SourceFloat sourceFloat{1}; + MonoToMultiConverter monoToStereo{2}; + SinkFloat sinkFloat{2}; + + sourceFloat.setData(input, 3); + + sourceFloat.output.connect(&monoToStereo.input); + monoToStereo.output.connect(&sinkFloat.input); + + int32_t numRead = sinkFloat.read(output, 8); + ASSERT_EQ(3, numRead); + EXPECT_EQ(input[0], output[0]); + EXPECT_EQ(input[0], output[1]); + EXPECT_EQ(input[1], output[2]); + EXPECT_EQ(input[1], output[3]); +} + +TEST(test_flowgraph, module_ramp_linear) { + constexpr int rampSize = 5; + constexpr int numOutput = 100; + constexpr float value = 1.0f; + constexpr float target = 100.0f; + float output[numOutput] = {}; + RampLinear rampLinear{1}; + SinkFloat sinkFloat{1}; + + rampLinear.input.setValue(value); + rampLinear.setLengthInFrames(rampSize); + rampLinear.setTarget(target); + rampLinear.forceCurrent(0.0f); + + rampLinear.output.connect(&sinkFloat.input); + + int32_t numRead = sinkFloat.read(output, numOutput); + ASSERT_EQ(numOutput, numRead); + constexpr float tolerance = 0.0001f; // arbitrary + int i = 0; + for (; i < rampSize; i++) { + float expected = i * value * target / rampSize; + EXPECT_NEAR(expected, output[i], tolerance); + } + for (; i < numOutput; i++) { + float expected = value * target; + EXPECT_NEAR(expected, output[i], tolerance); + } +} + +// It is easiest to represent packed 24-bit data as a byte array. +// This test will read from input, convert to float, then write +// back to output as bytes. +TEST(test_flowgraph, module_packed_24) { + static const uint8_t input[] = {0x01, 0x23, 0x45, + 0x67, 0x89, 0xAB, + 0xCD, 0xEF, 0x5A}; + uint8_t output[99] = {}; + SourceI24 sourceI24{1}; + SinkI24 sinkI24{1}; + + int numInputFrames = sizeof(input) / kBytesPerI24Packed; + sourceI24.setData(input, numInputFrames); + sourceI24.output.connect(&sinkI24.input); + + int32_t numRead = sinkI24.read(output, sizeof(output) / kBytesPerI24Packed); + ASSERT_EQ(numInputFrames, numRead); + for (size_t i = 0; i < sizeof(input); i++) { + EXPECT_EQ(input[i], output[i]); + } +} + +TEST(test_flowgraph, module_clip_to_range) { + constexpr float myMin = -2.0f; + constexpr float myMax = 1.5f; + + static const float input[] = {-9.7, 0.5f, -0.25, 1.0f, 12.3}; + static const float expected[] = {myMin, 0.5f, -0.25, 1.0f, myMax}; + float output[100]; + SourceFloat sourceFloat{1}; + ClipToRange clipper{1}; + SinkFloat sinkFloat{1}; + + int numInputFrames = sizeof(input) / sizeof(input[0]); + sourceFloat.setData(input, numInputFrames); + + clipper.setMinimum(myMin); + clipper.setMaximum(myMax); + + sourceFloat.output.connect(&clipper.input); + clipper.output.connect(&sinkFloat.input); + + int numOutputFrames = sizeof(output) / sizeof(output[0]); + int32_t numRead = sinkFloat.read(output, numOutputFrames); + ASSERT_EQ(numInputFrames, numRead); + constexpr float tolerance = 0.000001f; // arbitrary + for (int i = 0; i < numRead; i++) { + EXPECT_NEAR(expected[i], output[i], tolerance); + } +} -- GitLab From 5451855f487c47142c63350c838a3e35b89711eb Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 29 Aug 2018 14:59:31 -0700 Subject: [PATCH 0240/1530] Use audio/ogg instead of application/ogg Bug: 113368328 Test: build, boot, check media db. Change-Id: I4c4bd683f0dc726ee8186de3907a3c347e919fec --- media/libstagefright/foundation/MediaDefs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index a32cf08287..28bb10aec2 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -55,7 +55,7 @@ const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; -const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg"; +const char *MEDIA_MIMETYPE_CONTAINER_OGG = "audio/ogg"; const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts"; const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi"; -- GitLab From e98dd6f5496aaba009ed27be8e83af1cd10869c5 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Wed, 22 Aug 2018 18:23:50 -0700 Subject: [PATCH 0241/1530] NBLog: add latency logging and histogram serialization ReportPerformance::writeToFile and the recording of PerformanceAnalysis FastMixer timestamps are disabled for now. Test: dumpsys media.log -r Bug: 68148948 Change-Id: I4ba55fc20521ad2278348e6a3f713f9db04d951b --- media/libnblog/NBLog.cpp | 31 ++++- media/libnblog/PerformanceAnalysis.cpp | 101 ++++++++++++++- media/libnblog/include/media/nblog/NBLog.h | 3 + .../include/media/nblog/PerformanceAnalysis.h | 122 ++++++++++++++++++ services/audioflinger/FastMixer.cpp | 13 +- services/audioflinger/TypedLogger.h | 3 + 6 files changed, 268 insertions(+), 5 deletions(-) diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index d659445d0f..f1d7523974 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -478,12 +478,20 @@ void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) } } +void NBLog::Writer::logLatency(double latencyMs) +{ + if (!mEnabled) { + return; + } + log(EVENT_LATENCY, &latencyMs, sizeof(latencyMs)); +} + void NBLog::Writer::logMonotonicCycleTime(uint32_t monotonicNs) { if (!mEnabled) { return; } - log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(&monotonicNs)); + log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(monotonicNs)); } void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) @@ -713,12 +721,14 @@ const std::unordered_set NBLog::Reader::startingTypes { NBLog::Event::EVENT_START_FMT, NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, NBLog::Event::EVENT_AUDIO_STATE, + NBLog::Event::EVENT_LATENCY, NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME }; const std::unordered_set NBLog::Reader::endingTypes { NBLog::Event::EVENT_END_FMT, NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, NBLog::Event::EVENT_AUDIO_STATE, + NBLog::Event::EVENT_LATENCY, NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME }; @@ -873,6 +883,17 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int au memcpy(&hash, &(data->hash), sizeof(hash)); mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange(); } break; + case EVENT_LATENCY: { + double latencyMs; + memcpy(&latencyMs, etr.data, sizeof(latencyMs)); + mPerformanceData.addLatencyEntry(author, latencyMs); + } break; + case EVENT_MONOTONIC_CYCLE_TIME: { + uint32_t monotonicNs; + memcpy(&monotonicNs, etr.data, sizeof(monotonicNs)); + const double monotonicMs = monotonicNs * 1e-6; + mPerformanceData.addCycleTimeEntry(author, monotonicMs); + } break; case EVENT_END_FMT: case EVENT_RESERVED: case EVENT_UPPER_BOUND: @@ -904,6 +925,7 @@ void NBLog::MergeReader::dump(int fd, int indent) { // TODO: add a mutex around media.log dump ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); + mPerformanceData.dump(fd); } // TODO for future compatibility, would prefer to have a dump() go to string, and then go @@ -926,7 +948,12 @@ void NBLog::DumpReader::dump(int fd, size_t indent) case EVENT_MONOTONIC_CYCLE_TIME: { uint32_t monotonicNs; memcpy(&monotonicNs, it->data, sizeof(monotonicNs)); - body.appendFormat("Thread cycle took %u ns", monotonicNs); + body.appendFormat("Thread cycle: %u ns", monotonicNs); + } break; + case EVENT_LATENCY: { + double latencyMs; + memcpy(&latencyMs, it->data, sizeof(latencyMs)); + body.appendFormat("latency: %.3f ms", latencyMs); } break; case EVENT_END_FMT: case EVENT_RESERVED: diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp index 3418dc0ef3..ce9e22a87b 100644 --- a/media/libnblog/PerformanceAnalysis.cpp +++ b/media/libnblog/PerformanceAnalysis.cpp @@ -17,13 +17,15 @@ #define LOG_TAG "PerformanceAnalysis" // #define LOG_NDEBUG 0 +// #define WRITE_TO_FILE #include #include #include -#include #include #include +#include +#include #include #include #include @@ -45,6 +47,97 @@ namespace android { +void Histogram::add(double value) +{ + // TODO Handle domain and range error exceptions? + const int binIndex = lround((value - mLow) / mBinSize); + if (binIndex < 0) { + mLowCount++; + } else if (binIndex >= mNumBins) { + mHighCount++; + } else { + mBins[binIndex]++; + } + mTotalCount++; +} + +void Histogram::clear() +{ + std::fill(mBins.begin(), mBins.end(), 0); + mLowCount = 0; + mHighCount = 0; + mTotalCount = 0; +} + +uint64_t Histogram::totalCount() const +{ + return mTotalCount; +} + +std::string Histogram::serializeToString() const { + std::stringstream ss; + static constexpr char kDivider = '|'; + ss << mBinSize << "," << mNumBins << "," << mLow << ",{"; + bool first = true; + if (mLowCount != 0) { + ss << "-1" << kDivider << mLowCount; + first = false; + } + for (size_t i = 0; i < mNumBins; i++) { + if (mBins[i] != 0) { + if (!first) { + ss << ","; + } + ss << i << kDivider << mBins[i]; + first = false; + } + } + if (mHighCount != 0) { + if (!first) { + ss << ","; + } + ss << mNumBins << kDivider << mHighCount; + first = false; + } + ss << "}"; + + return ss.str(); +} + +// TODO make a hash map from Event type to std::pair> +// so that we don't have to create a "add histogram entry" method for every different metric. +void PerformanceData::addCycleTimeEntry(int author, double cycleTimeMs) +{ + if (mCycleTimeMsHists.count(author) == 0) { + mCycleTimeMsHists.emplace(author, Histogram(kCycleTimeConfig)); + } + mCycleTimeMsHists.at(author).add(cycleTimeMs); +} + +void PerformanceData::addLatencyEntry(int author, double latencyMs) +{ + if (mLatencyMsHists.count(author) == 0) { + mLatencyMsHists.emplace(author, Histogram(kLatencyConfig)); + } + mLatencyMsHists.at(author).add(latencyMs); +} + +void PerformanceData::dump(int fd, int indent __unused) +{ + // TODO add thread metadata for better context. + // Also output in a more machine-readable friendly format. + dprintf(fd, "Thread cycle time histograms:\n"); + for (const auto &item : mCycleTimeMsHists) { + dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str()); + } + dprintf(fd, "Latency histograms:\n"); + for (const auto &item : mLatencyMsHists) { + dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str()); + } +} + +//------------------------------------------------------------------------------ + namespace ReportPerformance { // Given an audio processing wakeup timestamp, buckets the time interval @@ -277,7 +370,9 @@ void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_ // writes summary of performance into specified file descriptor void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) { String8 body; +#ifdef WRITE_TO_FILE const char* const kDirectory = "/data/misc/audioserver/"; +#endif for (auto & thread : threadPerformanceAnalysis) { for (auto & hash: thread.second) { PerformanceAnalysis& curr = hash.second; @@ -287,9 +382,11 @@ void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) dumpLine(fd, indent, body); body.clear(); } - // write to file +#ifdef WRITE_TO_FILE + // write to file. Enable by uncommenting macro at top of file. writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps, kDirectory, false, thread.first, hash.first); +#endif } } } diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index c9bfaae746..763d7433d9 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -350,6 +350,7 @@ public: virtual void logFormat(const char *fmt, log_hash_t hash, ...); virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); virtual void logEventHistTs(Event event, log_hash_t hash); + virtual void logLatency(double latencyMs); virtual void logMonotonicCycleTime(uint32_t monotonicNs); // End of functions that are not in LockedWriter yet. @@ -547,6 +548,8 @@ public: // location within each author ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis; + PerformanceData mPerformanceData; + // handle author entry by looking up the author's name and appending it to the body // returns number of bytes read from fmtEntry void handleAuthor(const AbstractEntry &fmtEntry, String8 *body); diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h index 56e0ea66ba..f2c3a48765 100644 --- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h +++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -27,6 +28,127 @@ namespace android { class String8; +// TODO make this a templated class and put it in a separate file. +// The templated parameters would be bin size and low limit. +/* + * Histogram provides a way to store numeric data in histogram format and read it as a serialized + * string. The terms "bin" and "bucket" are used interchangeably. + * + * This class is not thread-safe. + */ +class Histogram { +public: + struct Config { + const double binSize; // TODO template type + const size_t numBins; + const double low; // TODO template type + }; + + // Histograms are constructed with fixed configuration numbers. Dynamic configuration based + // the data is possible but complex because + // - data points are added one by one, not processed as a batch. + // - Histograms with different configuration parameters are tricky to aggregate, and they + // will need to be aggregated at the Media Metrics cloud side. + // - not providing limits theoretically allows for infinite number of buckets. + + /** + * \brief Creates a Histogram object. + * + * \param binSize the width of each bin of the histogram. + * Units are whatever data the caller decides to store. + * \param numBins the number of bins desired in the histogram range. + * \param low the lower bound of the histogram bucket values. + * Units are whatever data the caller decides to store. + * Note that the upper bound can be calculated by the following: + * upper = lower + binSize * numBins. + */ + Histogram(double binSize, size_t numBins, double low = 0.) + : mBinSize(binSize), mNumBins(numBins), mLow(low), mBins(mNumBins) {} + + Histogram(const Config &c) + : Histogram(c.binSize, c.numBins, c.low) {} + + /** + * \brief Add a data point to the histogram. The value of the data point + * is rounded to the nearest multiple of the bin size (before accounting + * for the lower bound offset, which may not be a multiple of the bin size). + * + * \param value the value of the data point to add. + */ + void add(double value); + + /** + * \brief Removes all data points from the histogram. + */ + void clear(); + + /** + * \brief Returns the total number of data points added to the histogram. + * + * \return the total number of data points in the histogram. + */ + uint64_t totalCount() const; + + /** + * \brief Serializes the histogram into a string. The format is chosen to be compatible with + * the histogram representation to send to the Media Metrics service. + * + * The string is as follows: + * binSize,numBins,low,{-1|lowCount,...,binIndex|count,...,numBins|highCount} + * + * - binIndex is an integer with 0 <= binIndex < numBins. + * - count is the number of occurrences of the (rounded) value + * low + binSize * bucketIndex. + * - lowCount is the number of (rounded) values less than low. + * - highCount is the number of (rounded) values greater than or equal to + * low + binSize * numBins. + * - a binIndex may be skipped if its count is 0. + * + * \return the histogram serialized as a string. + */ + std::string serializeToString() const; + +private: + const double mBinSize; // Size of each bucket + const size_t mNumBins; // Number of buckets in histogram range + const double mLow; // Lower bound of values + std::vector mBins; // Data structure to store the actual histogram + + int mLowCount = 0; // Number of values less than mLow + int mHighCount = 0; // Number of values >= mLow + mBinSize * mNumBins + uint64_t mTotalCount = 0; // Total number of values recorded +}; + +// TODO For now this is a holder of audio performance metrics. The idea is essentially the same +// as PerformanceAnalysis, but the design structure is different. There is a PerformanceAnalysis +// instance for each thread writer (see PerformanceAnalysisMap later in this file), while a +// PerformanceData instance already takes into account each thread writer in its calculations. +// Eventually, this class should be merged with PerformanceAnalysis into some single entity. +/* + * PerformanceData stores audio performance data from audioflinger threads as histograms, + * time series, or counts, and outputs them in a machine-readable format. + */ +class PerformanceData { +public: + void addCycleTimeEntry(int author, double cycleTimeMs); + void addLatencyEntry(int author, double latencyMs); + void addWarmupTimeEntry(int author, double warmupTimeMs); + void addWarmupCyclesEntry(int author, double warmupCycles); + void dump(int fd, int indent = 0); +private: + // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192 and + // mSampleRate = 48000, which correspond to 2 and 7 seconds. + static constexpr Histogram::Config kCycleTimeConfig = { 0.25, 20, 2.}; + std::unordered_map mCycleTimeMsHists; + + // Values based on trial and error logging. Need a better way to determine + // bin size and lower/upper limits. + static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.}; + std::unordered_map mLatencyMsHists; +}; + +//------------------------------------------------------------------------------ + namespace ReportPerformance { class PerformanceAnalysis; diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index a42d6b3f57..fd784a427b 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -340,10 +340,19 @@ void FastMixer::onWork() FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; if (mIsWarm) { + // Logging timestamps for FastMixer is currently disabled to make memory room for logging + // other statistics in FastMixer. + // To re-enable, delete the #ifdef FASTMIXER_LOG_HIST_TS lines (and the #endif lines). +#ifdef FASTMIXER_LOG_HIST_TS LOG_HIST_TS(); +#endif + //ALOGD("Eric FastMixer::onWork() mIsWarm"); } else { dumpState->mTimestampVerifier.discontinuity(); + // See comment in if block. +#ifdef FASTMIXER_LOG_HIST_TS LOG_AUDIO_STATE(); +#endif } const FastMixerState::Command command = mCommand; const size_t frameCount = current->mFrameCount; @@ -498,8 +507,10 @@ void FastMixer::onWork() timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]; // We don't compensate for server - kernel time difference and // only update latency if we have valid info. - dumpState->mLatencyMs = + const double latencyMs = (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate; + dumpState->mLatencyMs = latencyMs; + LOG_LATENCY(latencyMs); } else { // HAL reported that more frames were presented than were written mNativeFramesWrittenButNotPresented = 0; diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h index 0fea42aa40..736ac603df 100644 --- a/services/audioflinger/TypedLogger.h +++ b/services/audioflinger/TypedLogger.h @@ -102,6 +102,9 @@ constexpr uint64_t hash(const char (&file)[n], uint32_t line) { #define LOG_MONOTONIC_CYCLE_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ x->logMonotonicCycleTime(ns); } while (0) +#define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->logLatency(ms); } while (0) + namespace android { extern "C" { extern thread_local NBLog::Writer *tlNBLogWriter; -- GitLab From bbe37b645da9a07fca8820b6d15b1adb595eee96 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 29 Aug 2018 15:15:48 -0700 Subject: [PATCH 0242/1530] android.hardware.media.bufferpool@2.0 impl Implementation is currently identical to 1.0 impl. The update will be applied afterwards. Bug: 112203066 Change-Id: If19af34121f5c9736ab4e8ccf7b1716d0be05c81 --- media/bufferpool/2.0/Accessor.cpp | 201 +++++ media/bufferpool/2.0/Accessor.h | 188 +++++ media/bufferpool/2.0/AccessorImpl.cpp | 543 ++++++++++++++ media/bufferpool/2.0/AccessorImpl.h | 300 ++++++++ media/bufferpool/2.0/Android.bp | 29 + media/bufferpool/2.0/BufferPoolClient.cpp | 708 ++++++++++++++++++ media/bufferpool/2.0/BufferPoolClient.h | 102 +++ media/bufferpool/2.0/BufferStatus.cpp | 191 +++++ media/bufferpool/2.0/BufferStatus.h | 143 ++++ media/bufferpool/2.0/ClientManager.cpp | 504 +++++++++++++ media/bufferpool/2.0/Connection.cpp | 89 +++ media/bufferpool/2.0/Connection.h | 103 +++ .../2.0/include/bufferpool/BufferPoolTypes.h | 118 +++ .../2.0/include/bufferpool/ClientManager.h | 179 +++++ 14 files changed, 3398 insertions(+) create mode 100644 media/bufferpool/2.0/Accessor.cpp create mode 100644 media/bufferpool/2.0/Accessor.h create mode 100644 media/bufferpool/2.0/AccessorImpl.cpp create mode 100644 media/bufferpool/2.0/AccessorImpl.h create mode 100644 media/bufferpool/2.0/Android.bp create mode 100644 media/bufferpool/2.0/BufferPoolClient.cpp create mode 100644 media/bufferpool/2.0/BufferPoolClient.h create mode 100644 media/bufferpool/2.0/BufferStatus.cpp create mode 100644 media/bufferpool/2.0/BufferStatus.h create mode 100644 media/bufferpool/2.0/ClientManager.cpp create mode 100644 media/bufferpool/2.0/Connection.cpp create mode 100644 media/bufferpool/2.0/Connection.h create mode 100644 media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h create mode 100644 media/bufferpool/2.0/include/bufferpool/ClientManager.h diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp new file mode 100644 index 0000000000..3fd41f0b91 --- /dev/null +++ b/media/bufferpool/2.0/Accessor.cpp @@ -0,0 +1,201 @@ +/* + * 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 "BufferPoolConnection" + +#include "Accessor.h" +#include "AccessorImpl.h" +#include "Connection.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +void ConnectionDeathRecipient::add( + int64_t connectionId, + const sp &accessor) { + std::lock_guard lock(mLock); + if (mAccessors.find(connectionId) == mAccessors.end()) { + mAccessors.insert(std::make_pair(connectionId, accessor)); + } +} + +void ConnectionDeathRecipient::remove(int64_t connectionId) { + std::lock_guard lock(mLock); + mAccessors.erase(connectionId); + auto it = mConnectionToCookie.find(connectionId); + if (it != mConnectionToCookie.end()) { + uint64_t cookie = it->second; + mConnectionToCookie.erase(it); + auto cit = mCookieToConnections.find(cookie); + if (cit != mCookieToConnections.end()) { + cit->second.erase(connectionId); + if (cit->second.size() == 0) { + mCookieToConnections.erase(cit); + } + } + } +} + +void ConnectionDeathRecipient::addCookieToConnection( + uint64_t cookie, + int64_t connectionId) { + std::lock_guard lock(mLock); + if (mAccessors.find(connectionId) == mAccessors.end()) { + return; + } + mConnectionToCookie.insert(std::make_pair(connectionId, cookie)); + auto it = mCookieToConnections.find(cookie); + if (it != mCookieToConnections.end()) { + it->second.insert(connectionId); + } else { + mCookieToConnections.insert(std::make_pair( + cookie, std::set{connectionId})); + } +} + +void ConnectionDeathRecipient::serviceDied( + uint64_t cookie, + const wp<::android::hidl::base::V1_0::IBase>& /* who */ + ) { + std::map> connectionsToClose; + { + std::lock_guard lock(mLock); + + auto it = mCookieToConnections.find(cookie); + if (it != mCookieToConnections.end()) { + for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) { + auto accessorIt = mAccessors.find(*conIt); + if (accessorIt != mAccessors.end()) { + connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second)); + mAccessors.erase(accessorIt); + } + mConnectionToCookie.erase(*conIt); + } + mCookieToConnections.erase(it); + } + } + + if (connectionsToClose.size() > 0) { + sp accessor; + for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) { + accessor = it->second.promote(); + + if (accessor) { + accessor->close(it->first); + ALOGD("connection %lld closed on death", (long long)it->first); + } + } + } +} + +namespace { +static sp sConnectionDeathRecipient = + new ConnectionDeathRecipient(); +} + +sp Accessor::getConnectionDeathRecipient() { + return sConnectionDeathRecipient; +} + +// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow. +Return Accessor::connect(connect_cb _hidl_cb) { + sp connection; + ConnectionId connectionId; + const QueueDescriptor* fmqDesc; + + ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false); + if (status == ResultStatus::OK) { + _hidl_cb(status, connection, connectionId, *fmqDesc); + } else { + _hidl_cb(status, nullptr, -1LL, + android::hardware::MQDescriptorSync( + std::vector(), + nullptr /* nhandle */, 0 /* size */)); + } + return Void(); +} + +Accessor::Accessor(const std::shared_ptr &allocator) + : mImpl(new Impl(allocator)) {} + +Accessor::~Accessor() { +} + +bool Accessor::isValid() { + return (bool)mImpl; +} + +ResultStatus Accessor::allocate( + ConnectionId connectionId, + const std::vector ¶ms, + BufferId *bufferId, const native_handle_t** handle) { + if (mImpl) { + return mImpl->allocate(connectionId, params, bufferId, handle); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus Accessor::fetch( + ConnectionId connectionId, TransactionId transactionId, + BufferId bufferId, const native_handle_t** handle) { + if (mImpl) { + return mImpl->fetch(connectionId, transactionId, bufferId, handle); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus Accessor::connect( + sp *connection, ConnectionId *pConnectionId, + const QueueDescriptor** fmqDescPtr, bool local) { + if (mImpl) { + ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr); + if (!local && status == ResultStatus::OK) { + sp accessor(this); + sConnectionDeathRecipient->add(*pConnectionId, accessor); + } + return status; + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus Accessor::close(ConnectionId connectionId) { + if (mImpl) { + ResultStatus status = mImpl->close(connectionId); + sConnectionDeathRecipient->remove(connectionId); + return status; + } + return ResultStatus::CRITICAL_ERROR; +} + +void Accessor::cleanUp(bool clearCache) { + if (mImpl) { + mImpl->cleanUp(clearCache); + } +} + +//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) { +// return new Accessor(); +//} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h new file mode 100644 index 0000000000..4fd8f5b2e2 --- /dev/null +++ b/media/bufferpool/2.0/Accessor.h @@ -0,0 +1,188 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H + +#include +#include +#include +#include +#include "BufferStatus.h" + +#include + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +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::sp; + +struct Accessor; +struct Connection; + +/** + * Receives death notifications from remote connections. + * On death notifications, the connections are closed and used resources + * are released. + */ +struct ConnectionDeathRecipient : public hardware::hidl_death_recipient { + /** + * Registers a newly connected connection from remote processes. + */ + void add(int64_t connectionId, const sp &accessor); + + /** + * Removes a connection. + */ + void remove(int64_t connectionId); + + void addCookieToConnection(uint64_t cookie, int64_t connectionId); + + virtual void serviceDied( + uint64_t /* cookie */, + const wp<::android::hidl::base::V1_0::IBase>& /* who */ + ) override; + +private: + std::mutex mLock; + std::map> mCookieToConnections; + std::map mConnectionToCookie; + std::map> mAccessors; +}; + +/** + * A buffer pool accessor which enables a buffer pool to communicate with buffer + * pool clients. 1:1 correspondense holds between a buffer pool and an accessor. + */ +struct Accessor : public IAccessor { + // Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow. + Return connect(connect_cb _hidl_cb) override; + + /** + * Creates a buffer pool accessor which uses the specified allocator. + * + * @param allocator buffer allocator. + */ + explicit Accessor(const std::shared_ptr &allocator); + + /** Destructs a buffer pool accessor. */ + ~Accessor(); + + /** Returns whether the accessor is valid. */ + bool isValid(); + + /** Allocates a buffer from a buffer pool. + * + * @param connectionId the connection id of the client. + * @param params the allocation parameters. + * @param bufferId the id of the allocated buffer. + * @param handle the native handle of the allocated buffer. + * + * @return OK when a buffer is successfully allocated. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus allocate( + ConnectionId connectionId, + const std::vector& params, + BufferId *bufferId, + const native_handle_t** handle); + + /** + * Fetches a buffer for the specified transaction. + * + * @param connectionId the id of receiving connection(client). + * @param transactionId the id of the transfer transaction. + * @param bufferId the id of the buffer to be fetched. + * @param handle the native handle of the fetched buffer. + * + * @return OK when a buffer is successfully fetched. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus fetch( + ConnectionId connectionId, + TransactionId transactionId, + BufferId bufferId, + const native_handle_t** handle); + + /** + * Makes a connection to the buffer pool. The buffer pool client uses the + * created connection in order to communicate with the buffer pool. An + * FMQ for buffer status message is also created for the client. + * + * @param connection created connection + * @param pConnectionId the id of the created connection + * @param fmqDescPtr FMQ descriptor for shared buffer status message + * queue between a buffer pool and the client. + * @param local true when a connection request comes from local process, + * false otherwise. + * + * @return OK when a connection is successfully made. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus connect( + sp *connection, ConnectionId *pConnectionId, + const QueueDescriptor** fmqDescPtr, bool local); + + /** + * Closes the specified connection to the client. + * + * @param connectionId the id of the connection. + * + * @return OK when the connection is closed. + * CRITICAL_ERROR otherwise. + */ + ResultStatus close(ConnectionId connectionId); + + /** + * Processes pending buffer status messages and perfoms periodic cache + * cleaning. + * + * @param clearCache if clearCache is true, it frees all buffers waiting + * to be recycled. + */ + void cleanUp(bool clearCache); + + /** + * Gets a hidl_death_recipient for remote connection death. + */ + static sp getConnectionDeathRecipient(); + +private: + class Impl; + std::unique_ptr mImpl; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp new file mode 100644 index 0000000000..e5cf275ee3 --- /dev/null +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -0,0 +1,543 @@ +/* + * 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 "BufferPoolAccessor" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include "AccessorImpl.h" +#include "Connection.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +namespace { + static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec + static constexpr int64_t kLogDurationUs = 5000000; // 5 secs + + static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15; + static constexpr size_t kMinBufferCountForEviction = 40; +} + +// Buffer structure in bufferpool process +struct InternalBuffer { + BufferId mId; + size_t mOwnerCount; + size_t mTransactionCount; + const std::shared_ptr mAllocation; + const size_t mAllocSize; + const std::vector mConfig; + + InternalBuffer( + BufferId id, + const std::shared_ptr &alloc, + const size_t allocSize, + const std::vector &allocConfig) + : mId(id), mOwnerCount(0), mTransactionCount(0), + mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {} + + const native_handle_t *handle() { + return mAllocation->handle(); + } +}; + +struct TransactionStatus { + TransactionId mId; + BufferId mBufferId; + ConnectionId mSender; + ConnectionId mReceiver; + BufferStatus mStatus; + int64_t mTimestampUs; + bool mSenderValidated; + + TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) { + mId = message.transactionId; + mBufferId = message.bufferId; + mStatus = message.newStatus; + mTimestampUs = timestampUs; + if (mStatus == BufferStatus::TRANSFER_TO) { + mSender = message.connectionId; + mReceiver = message.targetConnectionId; + mSenderValidated = true; + } else { + mSender = -1LL; + mReceiver = message.connectionId; + mSenderValidated = false; + } + } +}; + +// Helper template methods for handling map of set. +template +bool insert(std::map> *mapOfSet, T key, U value) { + auto iter = mapOfSet->find(key); + if (iter == mapOfSet->end()) { + std::set valueSet{value}; + mapOfSet->insert(std::make_pair(key, valueSet)); + return true; + } else if (iter->second.find(value) == iter->second.end()) { + iter->second.insert(value); + return true; + } + return false; +} + +template +bool erase(std::map> *mapOfSet, T key, U value) { + bool ret = false; + auto iter = mapOfSet->find(key); + if (iter != mapOfSet->end()) { + if (iter->second.erase(value) > 0) { + ret = true; + } + if (iter->second.size() == 0) { + mapOfSet->erase(iter); + } + } + return ret; +} + +template +bool contains(std::map> *mapOfSet, T key, U value) { + auto iter = mapOfSet->find(key); + if (iter != mapOfSet->end()) { + auto setIter = iter->second.find(value); + return setIter != iter->second.end(); + } + return false; +} + +int32_t Accessor::Impl::sPid = getpid(); +uint32_t Accessor::Impl::sSeqId = time(nullptr); + +Accessor::Impl::Impl( + const std::shared_ptr &allocator) + : mAllocator(allocator) {} + +Accessor::Impl::~Impl() { +} + +ResultStatus Accessor::Impl::connect( + const sp &accessor, sp *connection, + ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) { + sp newConnection = new Connection(); + ResultStatus status = ResultStatus::CRITICAL_ERROR; + { + std::lock_guard lock(mBufferPool.mMutex); + if (newConnection) { + ConnectionId id = (int64_t)sPid << 32 | sSeqId; + status = mBufferPool.mObserver.open(id, fmqDescPtr); + if (status == ResultStatus::OK) { + newConnection->initialize(accessor, id); + *connection = newConnection; + *pConnectionId = id; + ++sSeqId; + } + } + mBufferPool.processStatusMessages(); + mBufferPool.cleanUp(); + } + return status; +} + +ResultStatus Accessor::Impl::close(ConnectionId connectionId) { + std::lock_guard lock(mBufferPool.mMutex); + mBufferPool.processStatusMessages(); + mBufferPool.handleClose(connectionId); + mBufferPool.mObserver.close(connectionId); + // Since close# will be called after all works are finished, it is OK to + // evict unused buffers. + mBufferPool.cleanUp(true); + return ResultStatus::OK; +} + +ResultStatus Accessor::Impl::allocate( + ConnectionId connectionId, const std::vector& params, + BufferId *bufferId, const native_handle_t** handle) { + std::unique_lock lock(mBufferPool.mMutex); + mBufferPool.processStatusMessages(); + ResultStatus status = ResultStatus::OK; + if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) { + lock.unlock(); + std::shared_ptr alloc; + size_t allocSize; + status = mAllocator->allocate(params, &alloc, &allocSize); + lock.lock(); + if (status == ResultStatus::OK) { + status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle); + } + ALOGV("create a buffer %d : %u %p", + status == ResultStatus::OK, *bufferId, *handle); + } + if (status == ResultStatus::OK) { + // TODO: handle ownBuffer failure + mBufferPool.handleOwnBuffer(connectionId, *bufferId); + } + mBufferPool.cleanUp(); + return status; +} + +ResultStatus Accessor::Impl::fetch( + ConnectionId connectionId, TransactionId transactionId, + BufferId bufferId, const native_handle_t** handle) { + std::lock_guard lock(mBufferPool.mMutex); + mBufferPool.processStatusMessages(); + auto found = mBufferPool.mTransactions.find(transactionId); + if (found != mBufferPool.mTransactions.end() && + contains(&mBufferPool.mPendingTransactions, + connectionId, transactionId)) { + if (found->second->mSenderValidated && + found->second->mStatus == BufferStatus::TRANSFER_FROM && + found->second->mBufferId == bufferId) { + found->second->mStatus = BufferStatus::TRANSFER_FETCH; + auto bufferIt = mBufferPool.mBuffers.find(bufferId); + if (bufferIt != mBufferPool.mBuffers.end()) { + mBufferPool.mStats.onBufferFetched(); + *handle = bufferIt->second->handle(); + return ResultStatus::OK; + } + } + } + mBufferPool.cleanUp(); + return ResultStatus::CRITICAL_ERROR; +} + +void Accessor::Impl::cleanUp(bool clearCache) { + // transaction timeout, buffer cacheing TTL handling + std::lock_guard lock(mBufferPool.mMutex); + mBufferPool.processStatusMessages(); + mBufferPool.cleanUp(clearCache); +} + +Accessor::Impl::Impl::BufferPool::BufferPool() + : mTimestampUs(getTimestampNow()), + mLastCleanUpUs(mTimestampUs), + mLastLogUs(mTimestampUs), + mSeq(0) {} + + +// Statistics helper +template +int percentage(T base, S total) { + return int(total ? 0.5 + 100. * static_cast(base) / total : 0); +} + +Accessor::Impl::Impl::BufferPool::~BufferPool() { + std::lock_guard lock(mMutex); + ALOGD("Destruction - bufferpool %p " + "cached: %zu/%zuM, %zu/%d%% in use; " + "allocs: %zu, %d%% recycled; " + "transfers: %zu, %d%% unfetced", + this, mStats.mBuffersCached, mStats.mSizeCached >> 20, + mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached), + mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations), + mStats.mTotalTransfers, + percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers)); +} + +bool Accessor::Impl::BufferPool::handleOwnBuffer( + ConnectionId connectionId, BufferId bufferId) { + + bool added = insert(&mUsingBuffers, connectionId, bufferId); + if (added) { + auto iter = mBuffers.find(bufferId); + iter->second->mOwnerCount++; + } + insert(&mUsingConnections, bufferId, connectionId); + return added; +} + +bool Accessor::Impl::BufferPool::handleReleaseBuffer( + ConnectionId connectionId, BufferId bufferId) { + bool deleted = erase(&mUsingBuffers, connectionId, bufferId); + if (deleted) { + auto iter = mBuffers.find(bufferId); + iter->second->mOwnerCount--; + if (iter->second->mOwnerCount == 0 && + iter->second->mTransactionCount == 0) { + mStats.onBufferUnused(iter->second->mAllocSize); + mFreeBuffers.insert(bufferId); + } + } + erase(&mUsingConnections, bufferId, connectionId); + ALOGV("release buffer %u : %d", bufferId, deleted); + return deleted; +} + +bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) { + auto completed = mCompletedTransactions.find( + message.transactionId); + if (completed != mCompletedTransactions.end()) { + // already completed + mCompletedTransactions.erase(completed); + return true; + } + // the buffer should exist and be owned. + auto bufferIter = mBuffers.find(message.bufferId); + if (bufferIter == mBuffers.end() || + !contains(&mUsingBuffers, message.connectionId, message.bufferId)) { + return false; + } + auto found = mTransactions.find(message.transactionId); + if (found != mTransactions.end()) { + // transfer_from was received earlier. + found->second->mSender = message.connectionId; + found->second->mSenderValidated = true; + return true; + } + // TODO: verify there is target connection Id + mStats.onBufferSent(); + mTransactions.insert(std::make_pair( + message.transactionId, + std::make_unique(message, mTimestampUs))); + insert(&mPendingTransactions, message.targetConnectionId, + message.transactionId); + bufferIter->second->mTransactionCount++; + return true; +} + +bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) { + auto found = mTransactions.find(message.transactionId); + if (found == mTransactions.end()) { + // TODO: is it feasible to check ownership here? + mStats.onBufferSent(); + mTransactions.insert(std::make_pair( + message.transactionId, + std::make_unique(message, mTimestampUs))); + insert(&mPendingTransactions, message.connectionId, + message.transactionId); + auto bufferIter = mBuffers.find(message.bufferId); + bufferIter->second->mTransactionCount++; + } else { + if (message.connectionId == found->second->mReceiver) { + found->second->mStatus = BufferStatus::TRANSFER_FROM; + } + } + return true; +} + +bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) { + auto found = mTransactions.find(message.transactionId); + if (found != mTransactions.end()) { + bool deleted = erase(&mPendingTransactions, message.connectionId, + message.transactionId); + if (deleted) { + if (!found->second->mSenderValidated) { + mCompletedTransactions.insert(message.transactionId); + } + auto bufferIter = mBuffers.find(message.bufferId); + if (message.newStatus == BufferStatus::TRANSFER_OK) { + handleOwnBuffer(message.connectionId, message.bufferId); + } + bufferIter->second->mTransactionCount--; + if (bufferIter->second->mOwnerCount == 0 + && bufferIter->second->mTransactionCount == 0) { + mStats.onBufferUnused(bufferIter->second->mAllocSize); + mFreeBuffers.insert(message.bufferId); + } + mTransactions.erase(found); + } + ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId, + message.bufferId, deleted); + return deleted; + } + ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId, + message.bufferId); + return false; +} + +void Accessor::Impl::BufferPool::processStatusMessages() { + std::vector messages; + mObserver.getBufferStatusChanges(messages); + mTimestampUs = getTimestampNow(); + for (BufferStatusMessage& message: messages) { + bool ret = false; + switch (message.newStatus) { + case BufferStatus::NOT_USED: + ret = handleReleaseBuffer( + message.connectionId, message.bufferId); + break; + case BufferStatus::USED: + // not happening + break; + case BufferStatus::TRANSFER_TO: + ret = handleTransferTo(message); + break; + case BufferStatus::TRANSFER_FROM: + ret = handleTransferFrom(message); + break; + case BufferStatus::TRANSFER_TIMEOUT: + // TODO + break; + case BufferStatus::TRANSFER_LOST: + // TODO + break; + case BufferStatus::TRANSFER_FETCH: + // not happening + break; + case BufferStatus::TRANSFER_OK: + case BufferStatus::TRANSFER_ERROR: + ret = handleTransferResult(message); + break; + } + if (ret == false) { + ALOGW("buffer status message processing failure - message : %d connection : %lld", + message.newStatus, (long long)message.connectionId); + } + } + messages.clear(); +} + +bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) { + // Cleaning buffers + auto buffers = mUsingBuffers.find(connectionId); + if (buffers != mUsingBuffers.end()) { + for (const BufferId& bufferId : buffers->second) { + bool deleted = erase(&mUsingConnections, bufferId, connectionId); + if (deleted) { + auto bufferIter = mBuffers.find(bufferId); + bufferIter->second->mOwnerCount--; + if (bufferIter->second->mOwnerCount == 0 && + bufferIter->second->mTransactionCount == 0) { + // TODO: handle freebuffer insert fail + mStats.onBufferUnused(bufferIter->second->mAllocSize); + mFreeBuffers.insert(bufferId); + } + } + } + mUsingBuffers.erase(buffers); + } + + // Cleaning transactions + auto pending = mPendingTransactions.find(connectionId); + if (pending != mPendingTransactions.end()) { + for (const TransactionId& transactionId : pending->second) { + auto iter = mTransactions.find(transactionId); + if (iter != mTransactions.end()) { + if (!iter->second->mSenderValidated) { + mCompletedTransactions.insert(transactionId); + } + BufferId bufferId = iter->second->mBufferId; + auto bufferIter = mBuffers.find(bufferId); + bufferIter->second->mTransactionCount--; + if (bufferIter->second->mOwnerCount == 0 && + bufferIter->second->mTransactionCount == 0) { + // TODO: handle freebuffer insert fail + mStats.onBufferUnused(bufferIter->second->mAllocSize); + mFreeBuffers.insert(bufferId); + } + mTransactions.erase(iter); + } + } + } + return true; +} + +bool Accessor::Impl::BufferPool::getFreeBuffer( + const std::shared_ptr &allocator, + const std::vector ¶ms, BufferId *pId, + const native_handle_t** handle) { + auto bufferIt = mFreeBuffers.begin(); + for (;bufferIt != mFreeBuffers.end(); ++bufferIt) { + BufferId bufferId = *bufferIt; + if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) { + break; + } + } + if (bufferIt != mFreeBuffers.end()) { + BufferId id = *bufferIt; + mFreeBuffers.erase(bufferIt); + mStats.onBufferRecycled(mBuffers[id]->mAllocSize); + *handle = mBuffers[id]->handle(); + *pId = id; + ALOGV("recycle a buffer %u %p", id, *handle); + return true; + } + return false; +} + +ResultStatus Accessor::Impl::BufferPool::addNewBuffer( + const std::shared_ptr &alloc, + const size_t allocSize, + const std::vector ¶ms, + BufferId *pId, + const native_handle_t** handle) { + + BufferId bufferId = mSeq++; + if (mSeq == Connection::SYNC_BUFFERID) { + mSeq = 0; + } + std::unique_ptr buffer = + std::make_unique( + bufferId, alloc, allocSize, params); + if (buffer) { + auto res = mBuffers.insert(std::make_pair( + bufferId, std::move(buffer))); + if (res.second) { + mStats.onBufferAllocated(allocSize); + *handle = alloc->handle(); + *pId = bufferId; + return ResultStatus::OK; + } + } + return ResultStatus::NO_MEMORY; +} + +void Accessor::Impl::BufferPool::cleanUp(bool clearCache) { + if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) { + mLastCleanUpUs = mTimestampUs; + if (mTimestampUs > mLastLogUs + kLogDurationUs) { + mLastLogUs = mTimestampUs; + ALOGD("bufferpool %p : %zu(%zu size) total buffers - " + "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - " + "%zu/%zu (fetch/transfer)", + this, mStats.mBuffersCached, mStats.mSizeCached, + mStats.mBuffersInUse, mStats.mSizeInUse, + mStats.mTotalRecycles, mStats.mTotalAllocations, + mStats.mTotalFetches, mStats.mTotalTransfers); + } + for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) { + if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction + && mBuffers.size() < kMinBufferCountForEviction) { + break; + } + auto it = mBuffers.find(*freeIt); + if (it != mBuffers.end() && + it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) { + mStats.onBufferEvicted(it->second->mAllocSize); + mBuffers.erase(it); + freeIt = mFreeBuffers.erase(freeIt); + } else { + ++freeIt; + ALOGW("bufferpool inconsistent!"); + } + } + } +} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h new file mode 100644 index 0000000000..404394071c --- /dev/null +++ b/media/bufferpool/2.0/AccessorImpl.h @@ -0,0 +1,300 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H + +#include +#include +#include "Accessor.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +struct InternalBuffer; +struct TransactionStatus; + +/** + * An implementation of a buffer pool accessor(or a buffer pool implementation.) */ +class Accessor::Impl { +public: + Impl(const std::shared_ptr &allocator); + + ~Impl(); + + ResultStatus connect( + const sp &accessor, sp *connection, + ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr); + + ResultStatus close(ConnectionId connectionId); + + ResultStatus allocate(ConnectionId connectionId, + const std::vector& params, + BufferId *bufferId, + const native_handle_t** handle); + + ResultStatus fetch(ConnectionId connectionId, + TransactionId transactionId, + BufferId bufferId, + const native_handle_t** handle); + + void cleanUp(bool clearCache); + +private: + // ConnectionId = pid : (timestamp_created + seqId) + // in order to guarantee uniqueness for each connection + static uint32_t sSeqId; + static int32_t sPid; + + const std::shared_ptr mAllocator; + + /** + * Buffer pool implementation. + * + * Handles buffer status messages. Handles buffer allocation/recycling. + * Handles buffer transfer between buffer pool clients. + */ + struct BufferPool { + private: + std::mutex mMutex; + int64_t mTimestampUs; + int64_t mLastCleanUpUs; + int64_t mLastLogUs; + BufferId mSeq; + BufferStatusObserver mObserver; + + std::map> mUsingBuffers; + std::map> mUsingConnections; + + std::map> mPendingTransactions; + // Transactions completed before TRANSFER_TO message arrival. + // Fetch does not occur for the transactions. + // Only transaction id is kept for the transactions in short duration. + std::set mCompletedTransactions; + // Currently active(pending) transations' status & information. + std::map> + mTransactions; + + std::map> mBuffers; + std::set mFreeBuffers; + + /// Buffer pool statistics which tracks allocation and transfer statistics. + struct Stats { + /// Total size of allocations which are used or available to use. + /// (bytes or pixels) + size_t mSizeCached; + /// # of cached buffers which are used or available to use. + size_t mBuffersCached; + /// Total size of allocations which are currently used. (bytes or pixels) + size_t mSizeInUse; + /// # of currently used buffers + size_t mBuffersInUse; + + /// # of allocations called on bufferpool. (# of fetched from BlockPool) + size_t mTotalAllocations; + /// # of allocations that were served from the cache. + /// (# of allocator alloc prevented) + size_t mTotalRecycles; + /// # of buffer transfers initiated. + size_t mTotalTransfers; + /// # of transfers that had to be fetched. + size_t mTotalFetches; + + Stats() + : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0), + mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {} + + /// A new buffer is allocated on an allocation request. + void onBufferAllocated(size_t allocSize) { + mSizeCached += allocSize; + mBuffersCached++; + + mSizeInUse += allocSize; + mBuffersInUse++; + + mTotalAllocations++; + } + + /// A buffer is evicted and destroyed. + void onBufferEvicted(size_t allocSize) { + mSizeCached -= allocSize; + mBuffersCached--; + } + + /// A buffer is recycled on an allocation request. + void onBufferRecycled(size_t allocSize) { + mSizeInUse += allocSize; + mBuffersInUse++; + + mTotalAllocations++; + mTotalRecycles++; + } + + /// A buffer is available to be recycled. + void onBufferUnused(size_t allocSize) { + mSizeInUse -= allocSize; + mBuffersInUse--; + } + + /// A buffer transfer is initiated. + void onBufferSent() { + mTotalTransfers++; + } + + /// A buffer fetch is invoked by a buffer transfer. + void onBufferFetched() { + mTotalFetches++; + } + } mStats; + + public: + /** Creates a buffer pool. */ + BufferPool(); + + /** Destroys a buffer pool. */ + ~BufferPool(); + + /** + * Processes all pending buffer status messages, and returns the result. + * Each status message is handled by methods with 'handle' prefix. + */ + void processStatusMessages(); + + /** + * Handles a buffer being owned by a connection. + * + * @param connectionId the id of the buffer owning connection. + * @param bufferId the id of the buffer. + * + * @return {@code true} when the buffer is owned, + * {@code false} otherwise. + */ + bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId); + + /** + * Handles a buffer being released by a connection. + * + * @param connectionId the id of the buffer owning connection. + * @param bufferId the id of the buffer. + * + * @return {@code true} when the buffer ownership is released, + * {@code false} otherwise. + */ + bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId); + + /** + * Handles a transfer transaction start message from the sender. + * + * @param message a buffer status message for the transaction. + * + * @result {@code true} when transfer_to message is acknowledged, + * {@code false} otherwise. + */ + bool handleTransferTo(const BufferStatusMessage &message); + + /** + * Handles a transfer transaction being acked by the receiver. + * + * @param message a buffer status message for the transaction. + * + * @result {@code true} when transfer_from message is acknowledged, + * {@code false} otherwise. + */ + bool handleTransferFrom(const BufferStatusMessage &message); + + /** + * Handles a transfer transaction result message from the receiver. + * + * @param message a buffer status message for the transaction. + * + * @result {@code true} when the exisitng transaction is finished, + * {@code false} otherwise. + */ + bool handleTransferResult(const BufferStatusMessage &message); + + /** + * Handles a connection being closed, and returns the result. All the + * buffers and transactions owned by the connection will be cleaned up. + * The related FMQ will be cleaned up too. + * + * @param connectionId the id of the connection. + * + * @result {@code true} when the connection existed, + * {@code false} otherwise. + */ + bool handleClose(ConnectionId connectionId); + + /** + * Recycles a existing free buffer if it is possible. + * + * @param allocator the buffer allocator + * @param params the allocation parameters. + * @param pId the id of the recycled buffer. + * @param handle the native handle of the recycled buffer. + * + * @return {@code true} when a buffer is recycled, {@code false} + * otherwise. + */ + bool getFreeBuffer( + const std::shared_ptr &allocator, + const std::vector ¶ms, + BufferId *pId, const native_handle_t **handle); + + /** + * Adds a newly allocated buffer to bufferpool. + * + * @param alloc the newly allocated buffer. + * @param allocSize the size of the newly allocated buffer. + * @param params the allocation parameters. + * @param pId the buffer id for the newly allocated buffer. + * @param handle the native handle for the newly allocated buffer. + * + * @return OK when an allocation is successfully allocated. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus addNewBuffer( + const std::shared_ptr &alloc, + const size_t allocSize, + const std::vector ¶ms, + BufferId *pId, + const native_handle_t **handle); + + /** + * Processes pending buffer status messages and performs periodic cache + * cleaning. + * + * @param clearCache if clearCache is true, it frees all buffers + * waiting to be recycled. + */ + void cleanUp(bool clearCache = false); + + friend class Accessor::Impl; + } mBufferPool; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace ufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp new file mode 100644 index 0000000000..413125a571 --- /dev/null +++ b/media/bufferpool/2.0/Android.bp @@ -0,0 +1,29 @@ +cc_library { + name: "libstagefright_bufferpool@2.0", + vendor_available: true, + srcs: [ + "Accessor.cpp", + "AccessorImpl.cpp", + "BufferPoolClient.cpp", + "BufferStatus.cpp", + "ClientManager.cpp", + "Connection.cpp", + ], + export_include_dirs: [ + "include", + ], + shared_libs: [ + "libcutils", + "libfmq", + "libhidlbase", + "libhwbinder", + "libhidltransport", + "liblog", + "libutils", + "android.hardware.media.bufferpool@2.0", + ], + export_shared_lib_headers: [ + "libfmq", + "android.hardware.media.bufferpool@2.0", + ], +} diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp new file mode 100644 index 0000000000..10158c375c --- /dev/null +++ b/media/bufferpool/2.0/BufferPoolClient.cpp @@ -0,0 +1,708 @@ +/* + * 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 "BufferPoolClient" +//#define LOG_NDEBUG 0 + +#include +#include +#include "BufferPoolClient.h" +#include "Connection.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms +static constexpr int kPostMaxRetry = 3; +static constexpr int kCacheTtlUs = 1000000; // TODO: tune + +class BufferPoolClient::Impl + : public std::enable_shared_from_this { +public: + explicit Impl(const sp &accessor); + + explicit Impl(const sp &accessor); + + bool isValid() { + return mValid; + } + + bool isLocal() { + return mValid && mLocal; + } + + ConnectionId getConnectionId() { + return mConnectionId; + } + + sp &getAccessor() { + return mAccessor; + } + + bool isActive(int64_t *lastTransactionUs, bool clearCache); + + ResultStatus allocate(const std::vector ¶ms, + native_handle_t **handle, + std::shared_ptr *buffer); + + ResultStatus receive( + TransactionId transactionId, BufferId bufferId, + int64_t timestampUs, + native_handle_t **handle, std::shared_ptr *buffer); + + void postBufferRelease(BufferId bufferId); + + bool postSend( + BufferId bufferId, ConnectionId receiver, + TransactionId *transactionId, int64_t *timestampUs); +private: + + bool postReceive( + BufferId bufferId, TransactionId transactionId, + int64_t timestampUs); + + bool postReceiveResult( + BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync); + + void trySyncFromRemote(); + + bool syncReleased(); + + void evictCaches(bool clearCache = false); + + ResultStatus allocateBufferHandle( + const std::vector& params, BufferId *bufferId, + native_handle_t **handle); + + ResultStatus fetchBufferHandle( + TransactionId transactionId, BufferId bufferId, + native_handle_t **handle); + + struct BlockPoolDataDtor; + struct ClientBuffer; + + bool mLocal; + bool mValid; + sp mAccessor; + sp mLocalConnection; + sp mRemoteConnection; + uint32_t mSeqId; + ConnectionId mConnectionId; + int64_t mLastEvictCacheUs; + + // CachedBuffers + struct BufferCache { + std::mutex mLock; + bool mCreating; + std::condition_variable mCreateCv; + std::map> mBuffers; + int mActive; + int64_t mLastChangeUs; + + BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {} + + void incActive_l() { + ++mActive; + mLastChangeUs = getTimestampNow(); + } + + void decActive_l() { + --mActive; + mLastChangeUs = getTimestampNow(); + } + } mCache; + + // FMQ - release notifier + struct { + std::mutex mLock; + // TODO: use only one list?(using one list may dealy sending messages?) + std::list mReleasingIds; + std::list mReleasedIds; + std::unique_ptr mStatusChannel; + } mReleasing; + + // This lock is held during synchronization from remote side. + // In order to minimize remote calls and locking durtaion, this lock is held + // by best effort approach using try_lock(). + std::mutex mRemoteSyncLock; +}; + +struct BufferPoolClient::Impl::BlockPoolDataDtor { + BlockPoolDataDtor(const std::shared_ptr &impl) + : mImpl(impl) {} + + void operator()(BufferPoolData *buffer) { + BufferId id = buffer->mId; + delete buffer; + + auto impl = mImpl.lock(); + if (impl && impl->isValid()) { + impl->postBufferRelease(id); + } + } + const std::weak_ptr mImpl; +}; + +struct BufferPoolClient::Impl::ClientBuffer { +private: + bool mInvalidated; // TODO: implement + int64_t mExpireUs; + bool mHasCache; + ConnectionId mConnectionId; + BufferId mId; + native_handle_t *mHandle; + std::weak_ptr mCache; + + void updateExpire() { + mExpireUs = getTimestampNow() + kCacheTtlUs; + } + +public: + ClientBuffer( + ConnectionId connectionId, BufferId id, native_handle_t *handle) + : mInvalidated(false), mHasCache(false), + mConnectionId(connectionId), mId(id), mHandle(handle) { + (void)mInvalidated; + mExpireUs = getTimestampNow() + kCacheTtlUs; + } + + ~ClientBuffer() { + if (mHandle) { + native_handle_close(mHandle); + native_handle_delete(mHandle); + } + } + + bool expire() const { + int64_t now = getTimestampNow(); + return now >= mExpireUs; + } + + bool hasCache() const { + return mHasCache; + } + + std::shared_ptr fetchCache(native_handle_t **pHandle) { + if (mHasCache) { + std::shared_ptr cache = mCache.lock(); + if (cache) { + *pHandle = mHandle; + } + return cache; + } + return nullptr; + } + + std::shared_ptr createCache( + const std::shared_ptr &impl, + native_handle_t **pHandle) { + if (!mHasCache) { + // Allocates a raw ptr in order to avoid sending #postBufferRelease + // from deleter, in case of native_handle_clone failure. + BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId); + if (ptr) { + std::shared_ptr cache(ptr, BlockPoolDataDtor(impl)); + if (cache) { + mCache = cache; + mHasCache = true; + *pHandle = mHandle; + return cache; + } + } + if (ptr) { + delete ptr; + } + } + return nullptr; + } + + bool onCacheRelease() { + if (mHasCache) { + // TODO: verify mCache is not valid; + updateExpire(); + mHasCache = false; + return true; + } + return false; + } +}; + +BufferPoolClient::Impl::Impl(const sp &accessor) + : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0), + mLastEvictCacheUs(getTimestampNow()) { + const QueueDescriptor *fmqDesc; + ResultStatus status = accessor->connect( + &mLocalConnection, &mConnectionId, &fmqDesc, true); + if (status == ResultStatus::OK) { + mReleasing.mStatusChannel = + std::make_unique(*fmqDesc); + mValid = mReleasing.mStatusChannel && + mReleasing.mStatusChannel->isValid(); + } +} + +BufferPoolClient::Impl::Impl(const sp &accessor) + : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0), + mLastEvictCacheUs(getTimestampNow()) { + bool valid = false; + sp& outConnection = mRemoteConnection; + ConnectionId& id = mConnectionId; + std::unique_ptr& outChannel = + mReleasing.mStatusChannel; + Return transResult = accessor->connect( + [&valid, &outConnection, &id, &outChannel] + (ResultStatus status, sp connection, + ConnectionId connectionId, const QueueDescriptor& desc) { + if (status == ResultStatus::OK) { + outConnection = connection; + id = connectionId; + outChannel = std::make_unique(desc); + if (outChannel && outChannel->isValid()) { + valid = true; + } + } + }); + mValid = transResult.isOk() && valid; +} + +bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) { + bool active = false; + { + std::lock_guard lock(mCache.mLock); + syncReleased(); + evictCaches(clearCache); + *lastTransactionUs = mCache.mLastChangeUs; + active = mCache.mActive > 0; + } + if (mValid && mLocal && mLocalConnection) { + mLocalConnection->cleanUp(clearCache); + return true; + } + return active; +} + +ResultStatus BufferPoolClient::Impl::allocate( + const std::vector ¶ms, + native_handle_t **pHandle, + std::shared_ptr *buffer) { + if (!mLocal || !mLocalConnection || !mValid) { + return ResultStatus::CRITICAL_ERROR; + } + BufferId bufferId; + native_handle_t *handle = nullptr; + buffer->reset(); + ResultStatus status = allocateBufferHandle(params, &bufferId, &handle); + if (status == ResultStatus::OK) { + if (handle) { + std::unique_lock lock(mCache.mLock); + syncReleased(); + evictCaches(); + auto cacheIt = mCache.mBuffers.find(bufferId); + if (cacheIt != mCache.mBuffers.end()) { + // TODO: verify it is recycled. (not having active ref) + mCache.mBuffers.erase(cacheIt); + } + auto clientBuffer = std::make_unique( + mConnectionId, bufferId, handle); + if (clientBuffer) { + auto result = mCache.mBuffers.insert(std::make_pair( + bufferId, std::move(clientBuffer))); + if (result.second) { + *buffer = result.first->second->createCache( + shared_from_this(), pHandle); + if (*buffer) { + mCache.incActive_l(); + } + } + } + } + if (!*buffer) { + ALOGV("client cache creation failure %d: %lld", + handle != nullptr, (long long)mConnectionId); + status = ResultStatus::NO_MEMORY; + postBufferRelease(bufferId); + } + } + return status; +} + +ResultStatus BufferPoolClient::Impl::receive( + TransactionId transactionId, BufferId bufferId, int64_t timestampUs, + native_handle_t **pHandle, + std::shared_ptr *buffer) { + if (!mValid) { + return ResultStatus::CRITICAL_ERROR; + } + if (timestampUs != 0) { + timestampUs += kReceiveTimeoutUs; + } + if (!postReceive(bufferId, transactionId, timestampUs)) { + return ResultStatus::CRITICAL_ERROR; + } + ResultStatus status = ResultStatus::CRITICAL_ERROR; + buffer->reset(); + while(1) { + std::unique_lock lock(mCache.mLock); + syncReleased(); + evictCaches(); + auto cacheIt = mCache.mBuffers.find(bufferId); + if (cacheIt != mCache.mBuffers.end()) { + if (cacheIt->second->hasCache()) { + *buffer = cacheIt->second->fetchCache(pHandle); + if (!*buffer) { + // check transfer time_out + lock.unlock(); + std::this_thread::yield(); + continue; + } + ALOGV("client receive from reference %lld", (long long)mConnectionId); + break; + } else { + *buffer = cacheIt->second->createCache(shared_from_this(), pHandle); + if (*buffer) { + mCache.incActive_l(); + } + ALOGV("client receive from cache %lld", (long long)mConnectionId); + break; + } + } else { + if (!mCache.mCreating) { + mCache.mCreating = true; + lock.unlock(); + native_handle_t* handle = nullptr; + status = fetchBufferHandle(transactionId, bufferId, &handle); + lock.lock(); + if (status == ResultStatus::OK) { + if (handle) { + auto clientBuffer = std::make_unique( + mConnectionId, bufferId, handle); + if (clientBuffer) { + auto result = mCache.mBuffers.insert( + std::make_pair(bufferId, std::move( + clientBuffer))); + if (result.second) { + *buffer = result.first->second->createCache( + shared_from_this(), pHandle); + if (*buffer) { + mCache.incActive_l(); + } + } + } + } + if (!*buffer) { + status = ResultStatus::NO_MEMORY; + } + } + mCache.mCreating = false; + lock.unlock(); + mCache.mCreateCv.notify_all(); + break; + } + mCache.mCreateCv.wait(lock); + } + } + bool needsSync = false; + bool posted = postReceiveResult(bufferId, transactionId, + *buffer ? true : false, &needsSync); + ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId, + *buffer ? "ok" : "fail", posted); + if (mValid && mLocal && mLocalConnection) { + mLocalConnection->cleanUp(false); + } + if (needsSync && mRemoteConnection) { + trySyncFromRemote(); + } + if (*buffer) { + if (!posted) { + buffer->reset(); + return ResultStatus::CRITICAL_ERROR; + } + return ResultStatus::OK; + } + return status; +} + + +void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) { + std::lock_guard lock(mReleasing.mLock); + mReleasing.mReleasingIds.push_back(bufferId); + mReleasing.mStatusChannel->postBufferRelease( + mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds); +} + +// TODO: revise ad-hoc posting data structure +bool BufferPoolClient::Impl::postSend( + BufferId bufferId, ConnectionId receiver, + TransactionId *transactionId, int64_t *timestampUs) { + bool ret = false; + bool needsSync = false; + { + std::lock_guard lock(mReleasing.mLock); + *timestampUs = getTimestampNow(); + *transactionId = (mConnectionId << 32) | mSeqId++; + // TODO: retry, add timeout, target? + ret = mReleasing.mStatusChannel->postBufferStatusMessage( + *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId, + receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds); + needsSync = !mLocal && mReleasing.mStatusChannel->needsSync(); + } + if (mValid && mLocal && mLocalConnection) { + mLocalConnection->cleanUp(false); + } + if (needsSync && mRemoteConnection) { + trySyncFromRemote(); + } + return ret; +} + +bool BufferPoolClient::Impl::postReceive( + BufferId bufferId, TransactionId transactionId, int64_t timestampUs) { + for (int i = 0; i < kPostMaxRetry; ++i) { + std::unique_lock lock(mReleasing.mLock); + int64_t now = getTimestampNow(); + if (timestampUs == 0 || now < timestampUs) { + bool result = mReleasing.mStatusChannel->postBufferStatusMessage( + transactionId, bufferId, BufferStatus::TRANSFER_FROM, + mConnectionId, -1, mReleasing.mReleasingIds, + mReleasing.mReleasedIds); + if (result) { + return true; + } + lock.unlock(); + std::this_thread::yield(); + } else { + mReleasing.mStatusChannel->postBufferStatusMessage( + transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT, + mConnectionId, -1, mReleasing.mReleasingIds, + mReleasing.mReleasedIds); + return false; + } + } + return false; +} + +bool BufferPoolClient::Impl::postReceiveResult( + BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) { + std::lock_guard lock(mReleasing.mLock); + // TODO: retry, add timeout + bool ret = mReleasing.mStatusChannel->postBufferStatusMessage( + transactionId, bufferId, + result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR, + mConnectionId, -1, mReleasing.mReleasingIds, + mReleasing.mReleasedIds); + *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync(); + return ret; +} + +void BufferPoolClient::Impl::trySyncFromRemote() { + if (mRemoteSyncLock.try_lock()) { + bool needsSync = false; + { + std::lock_guard lock(mReleasing.mLock); + needsSync = mReleasing.mStatusChannel->needsSync(); + } + if (needsSync) { + TransactionId transactionId = (mConnectionId << 32); + BufferId bufferId = Connection::SYNC_BUFFERID; + Return transResult = mRemoteConnection->fetch( + transactionId, bufferId, + [] + (ResultStatus outStatus, Buffer outBuffer) { + (void) outStatus; + (void) outBuffer; + }); + } + mRemoteSyncLock.unlock(); + } +} + +// should have mCache.mLock +bool BufferPoolClient::Impl::syncReleased() { + std::lock_guard lock(mReleasing.mLock); + if (mReleasing.mReleasingIds.size() > 0) { + mReleasing.mStatusChannel->postBufferRelease( + mConnectionId, mReleasing.mReleasingIds, + mReleasing.mReleasedIds); + } + if (mReleasing.mReleasedIds.size() > 0) { + for (BufferId& id: mReleasing.mReleasedIds) { + ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id); + auto found = mCache.mBuffers.find(id); + if (found != mCache.mBuffers.end()) { + if (found->second->onCacheRelease()) { + mCache.decActive_l(); + } else { + // should not happen! + ALOGW("client %lld cache release status inconsitent!", + (long long)mConnectionId); + } + } else { + // should not happen! + ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId); + } + } + mReleasing.mReleasedIds.clear(); + return true; + } + return false; +} + +// should have mCache.mLock +void BufferPoolClient::Impl::evictCaches(bool clearCache) { + int64_t now = getTimestampNow(); + if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) { + size_t evicted = 0; + for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) { + if (!it->second->hasCache() && (it->second->expire() || clearCache)) { + it = mCache.mBuffers.erase(it); + ++evicted; + } else { + ++it; + } + } + ALOGV("cache count %lld : total %zu, active %d, evicted %zu", + (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted); + mLastEvictCacheUs = now; + } +} + +ResultStatus BufferPoolClient::Impl::allocateBufferHandle( + const std::vector& params, BufferId *bufferId, + native_handle_t** handle) { + if (mLocalConnection) { + const native_handle_t* allocHandle = nullptr; + ResultStatus status = mLocalConnection->allocate( + params, bufferId, &allocHandle); + if (status == ResultStatus::OK) { + *handle = native_handle_clone(allocHandle); + } + ALOGV("client allocate result %lld %d : %u clone %p", + (long long)mConnectionId, status == ResultStatus::OK, + *handle ? *bufferId : 0 , *handle); + return status; + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus BufferPoolClient::Impl::fetchBufferHandle( + TransactionId transactionId, BufferId bufferId, + native_handle_t **handle) { + sp connection; + if (mLocal) { + connection = mLocalConnection; + } else { + connection = mRemoteConnection; + } + ResultStatus status; + Return transResult = connection->fetch( + transactionId, bufferId, + [&status, &handle] + (ResultStatus outStatus, Buffer outBuffer) { + status = outStatus; + if (status == ResultStatus::OK) { + *handle = native_handle_clone( + outBuffer.buffer.getNativeHandle()); + } + }); + return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR; +} + + +BufferPoolClient::BufferPoolClient(const sp &accessor) { + mImpl = std::make_shared(accessor); +} + +BufferPoolClient::BufferPoolClient(const sp &accessor) { + mImpl = std::make_shared(accessor); +} + +BufferPoolClient::~BufferPoolClient() { + // TODO: how to handle orphaned buffers? +} + +bool BufferPoolClient::isValid() { + return mImpl && mImpl->isValid(); +} + +bool BufferPoolClient::isLocal() { + return mImpl && mImpl->isLocal(); +} + +bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) { + if (!isValid()) { + *lastTransactionUs = 0; + return false; + } + return mImpl->isActive(lastTransactionUs, clearCache); +} + +ConnectionId BufferPoolClient::getConnectionId() { + if (isValid()) { + return mImpl->getConnectionId(); + } + return -1; +} + +ResultStatus BufferPoolClient::getAccessor(sp *accessor) { + if (isValid()) { + *accessor = mImpl->getAccessor(); + return ResultStatus::OK; + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus BufferPoolClient::allocate( + const std::vector ¶ms, + native_handle_t **handle, + std::shared_ptr *buffer) { + if (isValid()) { + return mImpl->allocate(params, handle, buffer); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus BufferPoolClient::receive( + TransactionId transactionId, BufferId bufferId, int64_t timestampUs, + native_handle_t **handle, std::shared_ptr *buffer) { + if (isValid()) { + return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus BufferPoolClient::postSend( + ConnectionId receiverId, + const std::shared_ptr &buffer, + TransactionId *transactionId, + int64_t *timestampUs) { + if (isValid()) { + bool result = mImpl->postSend( + buffer->mId, receiverId, transactionId, timestampUs); + return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR; + } + return ResultStatus::CRITICAL_ERROR; +} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h new file mode 100644 index 0000000000..00d6839d31 --- /dev/null +++ b/media/bufferpool/2.0/BufferPoolClient.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H + +#include +#include +#include +#include +#include +#include "Accessor.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::media::bufferpool::V2_0::IAccessor; +using ::android::hardware::media::bufferpool::V2_0::IConnection; +using ::android::hardware::media::bufferpool::V2_0::ResultStatus; +using ::android::sp; + +/** + * A buffer pool client for a buffer pool. For a specific buffer pool, at most + * one buffer pool client exists per process. This class will not be exposed + * outside. A buffer pool client will be used via ClientManager. + */ +class BufferPoolClient { +public: + /** + * Creates a buffer pool client from a local buffer pool + * (via ClientManager#create). + */ + explicit BufferPoolClient(const sp &accessor); + + /** + * Creates a buffer pool client from a remote buffer pool + * (via ClientManager#registerSender). + * Note: A buffer pool client created with remote buffer pool cannot + * allocate a buffer. + */ + explicit BufferPoolClient(const sp &accessor); + + /** Destructs a buffer pool client. */ + ~BufferPoolClient(); + +private: + bool isValid(); + + bool isLocal(); + + bool isActive(int64_t *lastTransactionUs, bool clearCache); + + ConnectionId getConnectionId(); + + ResultStatus getAccessor(sp *accessor); + + ResultStatus allocate(const std::vector ¶ms, + native_handle_t **handle, + std::shared_ptr *buffer); + + ResultStatus receive(TransactionId transactionId, + BufferId bufferId, + int64_t timestampUs, + native_handle_t **handle, + std::shared_ptr *buffer); + + ResultStatus postSend(ConnectionId receiver, + const std::shared_ptr &buffer, + TransactionId *transactionId, + int64_t *timestampUs); + + class Impl; + std::shared_ptr mImpl; + + friend struct ClientManager; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp new file mode 100644 index 0000000000..3379e21502 --- /dev/null +++ b/media/bufferpool/2.0/BufferStatus.cpp @@ -0,0 +1,191 @@ +/* + * 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 "BufferPoolStatus" +//#define LOG_NDEBUG 0 + +#include +#include "BufferStatus.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +int64_t getTimestampNow() { + int64_t stamp; + struct timespec ts; + // TODO: CLOCK_MONOTONIC_COARSE? + clock_gettime(CLOCK_MONOTONIC, &ts); + stamp = ts.tv_nsec / 1000; + stamp += (ts.tv_sec * 1000000LL); + return stamp; +} + +static constexpr int kNumElementsInQueue = 1024*16; +static constexpr int kMinElementsToSyncInQueue = 128; + +ResultStatus BufferStatusObserver::open( + ConnectionId id, const QueueDescriptor** fmqDescPtr) { + if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) { + // TODO: id collision log? + return ResultStatus::CRITICAL_ERROR; + } + std::unique_ptr queue = + std::make_unique(kNumElementsInQueue); + if (!queue || queue->isValid() == false) { + *fmqDescPtr = nullptr; + return ResultStatus::NO_MEMORY; + } else { + *fmqDescPtr = queue->getDesc(); + } + auto result = mBufferStatusQueues.insert( + std::make_pair(id, std::move(queue))); + if (!result.second) { + *fmqDescPtr = nullptr; + return ResultStatus::NO_MEMORY; + } + return ResultStatus::OK; +} + +ResultStatus BufferStatusObserver::close(ConnectionId id) { + if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) { + return ResultStatus::CRITICAL_ERROR; + } + mBufferStatusQueues.erase(id); + return ResultStatus::OK; +} + +void BufferStatusObserver::getBufferStatusChanges(std::vector &messages) { + for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) { + BufferStatusMessage message; + size_t avail = it->second->availableToRead(); + while (avail > 0) { + if (!it->second->read(&message, 1)) { + // Since avaliable # of reads are already confirmed, + // this should not happen. + // TODO: error handling (spurious client?) + ALOGW("FMQ message cannot be read from %lld", (long long)it->first); + return; + } + message.connectionId = it->first; + messages.push_back(message); + --avail; + } + } +} + +BufferStatusChannel::BufferStatusChannel( + const QueueDescriptor &fmqDesc) { + std::unique_ptr queue = + std::make_unique(fmqDesc); + if (!queue || queue->isValid() == false) { + mValid = false; + return; + } + mValid = true; + mBufferStatusQueue = std::move(queue); +} + +bool BufferStatusChannel::isValid() { + return mValid; +} + +bool BufferStatusChannel::needsSync() { + if (mValid) { + size_t avail = mBufferStatusQueue->availableToWrite(); + return avail + kMinElementsToSyncInQueue < kNumElementsInQueue; + } + return false; +} + +void BufferStatusChannel::postBufferRelease( + ConnectionId connectionId, + std::list &pending, std::list &posted) { + if (mValid && pending.size() > 0) { + size_t avail = mBufferStatusQueue->availableToWrite(); + avail = std::min(avail, pending.size()); + BufferStatusMessage message; + for (size_t i = 0 ; i < avail; ++i) { + BufferId id = pending.front(); + message.newStatus = BufferStatus::NOT_USED; + message.bufferId = id; + message.connectionId = connectionId; + if (!mBufferStatusQueue->write(&message, 1)) { + // Since avaliable # of writes are already confirmed, + // this should not happen. + // TODO: error handing? + ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId); + return; + } + pending.pop_front(); + posted.push_back(id); + } + } +} + +bool BufferStatusChannel::postBufferStatusMessage( + TransactionId transactionId, BufferId bufferId, + BufferStatus status, ConnectionId connectionId, ConnectionId targetId, + std::list &pending, std::list &posted) { + if (mValid) { + size_t avail = mBufferStatusQueue->availableToWrite(); + size_t numPending = pending.size(); + if (avail >= numPending + 1) { + BufferStatusMessage release, message; + for (size_t i = 0; i < numPending; ++i) { + BufferId id = pending.front(); + release.newStatus = BufferStatus::NOT_USED; + release.bufferId = id; + release.connectionId = connectionId; + if (!mBufferStatusQueue->write(&release, 1)) { + // Since avaliable # of writes are already confirmed, + // this should not happen. + // TODO: error handling? + ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId); + return false; + } + pending.pop_front(); + posted.push_back(id); + } + message.transactionId = transactionId; + message.bufferId = bufferId; + message.newStatus = status; + message.connectionId = connectionId; + message.targetConnectionId = targetId; + // TODO : timesatamp + message.timestampUs = 0; + if (!mBufferStatusQueue->write(&message, 1)) { + // Since avaliable # of writes are already confirmed, + // this should not happen. + ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId); + return false; + } + return true; + } + } + return false; +} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h new file mode 100644 index 0000000000..a74f0a5d32 --- /dev/null +++ b/media/bufferpool/2.0/BufferStatus.h @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +/** Returns monotonic timestamp in Us since fixed point in time. */ +int64_t getTimestampNow(); + +/** + * A collection of FMQ for a buffer pool. buffer ownership/status change + * messages are sent via the FMQs from the clients. + */ +class BufferStatusObserver { +private: + std::map> + mBufferStatusQueues; + +public: + /** Creates an FMQ for the specified connection(client). + * + * @param connectionId connection Id of the specified client. + * @param fmqDescPtr double ptr of created FMQ's descriptor. + * + * @return OK if FMQ is created successfully. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr); + + /** Closes an FMQ for the specified connection(client). + * + * @param connectionId connection Id of the specified client. + * + * @return OK if the specified connection is closed successfully. + * CRITICAL_ERROR otherwise. + */ + ResultStatus close(ConnectionId id); + + /** Retrieves all pending FMQ buffer status messages from clients. + * + * @param messages retrieved pending messages. + */ + void getBufferStatusChanges(std::vector &messages); +}; + +/** + * An FMQ for a buffer pool client. Buffer ownership/status change messages + * are sent via the fmq to the buffer pool. + */ +class BufferStatusChannel { +private: + bool mValid; + std::unique_ptr mBufferStatusQueue; + +public: + /** + * Connects to an FMQ from a descriptor of the created FMQ. + * + * @param fmqDesc Descriptor of the created FMQ. + */ + BufferStatusChannel(const QueueDescriptor &fmqDesc); + + /** Returns whether the FMQ is connected successfully. */ + bool isValid(); + + /** Returns whether the FMQ needs to be synced from the buffer pool */ + bool needsSync(); + + /** + * Posts a buffer release message to the buffer pool. + * + * @param connectionId connection Id of the client. + * @param pending currently pending buffer release messages. + * @param posted posted buffer release messages. + */ + void postBufferRelease( + ConnectionId connectionId, + std::list &pending, std::list &posted); + + /** + * Posts a buffer status message regarding the specified buffer + * transfer transaction. + * + * @param transactionId Id of the specified transaction. + * @param bufferId buffer Id of the specified transaction. + * @param status new status of the buffer. + * @param connectionId connection Id of the client. + * @param targetId connection Id of the receiver(only when the sender + * posts a status message). + * @param pending currently pending buffer release messages. + * @param posted posted buffer release messages. + * + * @return {@code true} when the specified message is posted, + * {@code false} otherwise. + */ + bool postBufferStatusMessage( + TransactionId transactionId, + BufferId bufferId, + BufferStatus status, + ConnectionId connectionId, + ConnectionId targetId, + std::list &pending, std::list &posted); +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp new file mode 100644 index 0000000000..eeaf093a40 --- /dev/null +++ b/media/bufferpool/2.0/ClientManager.cpp @@ -0,0 +1,504 @@ +/* + * 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 "BufferPoolManager" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include "BufferPoolClient.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec +static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune +static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune + +/** + * The holder of the cookie of remote IClientManager. + * The cookie is process locally unique for each IClientManager. + * (The cookie is used to notify death of clients to bufferpool process.) + */ +class ClientManagerCookieHolder { +public: + /** + * Creates a cookie holder for remote IClientManager(s). + */ + ClientManagerCookieHolder(); + + /** + * Gets a cookie for a remote IClientManager. + * + * @param manager the specified remote IClientManager. + * @param added true when the specified remote IClientManager is added + * newly, false otherwise. + * + * @return the process locally unique cookie for the specified IClientManager. + */ + uint64_t getCookie(const sp &manager, bool *added); + +private: + uint64_t mSeqId; + std::mutex mLock; + std::list, uint64_t>> mManagers; +}; + +ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){} + +uint64_t ClientManagerCookieHolder::getCookie( + const sp &manager, + bool *added) { + std::lock_guard lock(mLock); + for (auto it = mManagers.begin(); it != mManagers.end();) { + const sp key = it->first.promote(); + if (key) { + if (interfacesEqual(key, manager)) { + *added = false; + return it->second; + } + ++it; + } else { + it = mManagers.erase(it); + } + } + uint64_t id = mSeqId++; + *added = true; + mManagers.push_back(std::make_pair(manager, id)); + return id; +} + +class ClientManager::Impl { +public: + Impl(); + + // BnRegisterSender + ResultStatus registerSender(const sp &accessor, + ConnectionId *pConnectionId); + + // BpRegisterSender + ResultStatus registerSender(const sp &receiver, + ConnectionId senderId, + ConnectionId *receiverId); + + ResultStatus create(const std::shared_ptr &allocator, + ConnectionId *pConnectionId); + + ResultStatus close(ConnectionId connectionId); + + ResultStatus allocate(ConnectionId connectionId, + const std::vector ¶ms, + native_handle_t **handle, + std::shared_ptr *buffer); + + ResultStatus receive(ConnectionId connectionId, + TransactionId transactionId, + BufferId bufferId, + int64_t timestampUs, + native_handle_t **handle, + std::shared_ptr *buffer); + + ResultStatus postSend(ConnectionId receiverId, + const std::shared_ptr &buffer, + TransactionId *transactionId, + int64_t *timestampUs); + + ResultStatus getAccessor(ConnectionId connectionId, + sp *accessor); + + void cleanUp(bool clearCache = false); + +private: + // In order to prevent deadlock between multiple locks, + // always lock ClientCache.lock before locking ActiveClients.lock. + struct ClientCache { + // This lock is held for brief duration. + // Blocking operation is not performed while holding the lock. + std::mutex mMutex; + std::list, const std::weak_ptr>> + mClients; + std::condition_variable mConnectCv; + bool mConnecting; + int64_t mLastCleanUpUs; + + ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {} + } mCache; + + // Active clients which can be retrieved via ConnectionId + struct ActiveClients { + // This lock is held for brief duration. + // Blocking operation is not performed holding the lock. + std::mutex mMutex; + std::map> + mClients; + } mActive; + + ClientManagerCookieHolder mRemoteClientCookies; +}; + +ClientManager::Impl::Impl() {} + +ResultStatus ClientManager::Impl::registerSender( + const sp &accessor, ConnectionId *pConnectionId) { + cleanUp(); + int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs; + do { + std::unique_lock lock(mCache.mMutex); + for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) { + sp sAccessor = it->first.promote(); + if (sAccessor && interfacesEqual(sAccessor, accessor)) { + const std::shared_ptr client = it->second.lock(); + if (client) { + std::lock_guard lock(mActive.mMutex); + *pConnectionId = client->getConnectionId(); + if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) { + ALOGV("register existing connection %lld", (long long)*pConnectionId); + return ResultStatus::ALREADY_EXISTS; + } + } + mCache.mClients.erase(it); + break; + } + } + if (!mCache.mConnecting) { + mCache.mConnecting = true; + lock.unlock(); + ResultStatus result = ResultStatus::OK; + const std::shared_ptr client = + std::make_shared(accessor); + lock.lock(); + if (!client) { + result = ResultStatus::NO_MEMORY; + } else if (!client->isValid()) { + result = ResultStatus::CRITICAL_ERROR; + } + if (result == ResultStatus::OK) { + // TODO: handle insert fail. (malloc fail) + const std::weak_ptr wclient = client; + mCache.mClients.push_back(std::make_pair(accessor, wclient)); + ConnectionId conId = client->getConnectionId(); + { + std::lock_guard lock(mActive.mMutex); + mActive.mClients.insert(std::make_pair(conId, client)); + } + *pConnectionId = conId; + ALOGV("register new connection %lld", (long long)*pConnectionId); + } + mCache.mConnecting = false; + lock.unlock(); + mCache.mConnectCv.notify_all(); + return result; + } + mCache.mConnectCv.wait_for( + lock, std::chrono::microseconds(kRegisterTimeoutUs)); + } while (getTimestampNow() < timeoutUs); + // TODO: return timeout error + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::Impl::registerSender( + const sp &receiver, + ConnectionId senderId, + ConnectionId *receiverId) { + sp accessor; + bool local = false; + { + std::lock_guard lock(mActive.mMutex); + auto it = mActive.mClients.find(senderId); + if (it == mActive.mClients.end()) { + return ResultStatus::NOT_FOUND; + } + it->second->getAccessor(&accessor); + local = it->second->isLocal(); + } + ResultStatus rs = ResultStatus::CRITICAL_ERROR; + if (accessor) { + Return transResult = receiver->registerSender( + accessor, + [&rs, receiverId]( + ResultStatus status, + int64_t connectionId) { + rs = status; + *receiverId = connectionId; + }); + if (!transResult.isOk()) { + return ResultStatus::CRITICAL_ERROR; + } else if (local && rs == ResultStatus::OK) { + sp recipient = Accessor::getConnectionDeathRecipient(); + if (recipient) { + ALOGV("client death recipient registered %lld", (long long)*receiverId); + bool added; + uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added); + recipient->addCookieToConnection(cookie, *receiverId); + if (added) { + Return transResult = receiver->linkToDeath(recipient, cookie); + } + } + } + } + return rs; +} + +ResultStatus ClientManager::Impl::create( + const std::shared_ptr &allocator, + ConnectionId *pConnectionId) { + const sp accessor = new Accessor(allocator); + if (!accessor || !accessor->isValid()) { + return ResultStatus::CRITICAL_ERROR; + } + std::shared_ptr client = + std::make_shared(accessor); + if (!client || !client->isValid()) { + return ResultStatus::CRITICAL_ERROR; + } + // Since a new bufferpool is created, evict memories which are used by + // existing bufferpools and clients. + cleanUp(true); + { + // TODO: handle insert fail. (malloc fail) + std::lock_guard lock(mCache.mMutex); + const std::weak_ptr wclient = client; + mCache.mClients.push_back(std::make_pair(accessor, wclient)); + ConnectionId conId = client->getConnectionId(); + { + std::lock_guard lock(mActive.mMutex); + mActive.mClients.insert(std::make_pair(conId, client)); + } + *pConnectionId = conId; + ALOGV("create new connection %lld", (long long)*pConnectionId); + } + return ResultStatus::OK; +} + +ResultStatus ClientManager::Impl::close(ConnectionId connectionId) { + std::lock_guard lock1(mCache.mMutex); + std::lock_guard lock2(mActive.mMutex); + auto it = mActive.mClients.find(connectionId); + if (it != mActive.mClients.end()) { + sp accessor; + it->second->getAccessor(&accessor); + mActive.mClients.erase(connectionId); + for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) { + // clean up dead client caches + sp cAccessor = cit->first.promote(); + if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) { + cit = mCache.mClients.erase(cit); + } else { + cit++; + } + } + return ResultStatus::OK; + } + return ResultStatus::NOT_FOUND; +} + +ResultStatus ClientManager::Impl::allocate( + ConnectionId connectionId, const std::vector ¶ms, + native_handle_t **handle, std::shared_ptr *buffer) { + std::shared_ptr client; + { + std::lock_guard lock(mActive.mMutex); + auto it = mActive.mClients.find(connectionId); + if (it == mActive.mClients.end()) { + return ResultStatus::NOT_FOUND; + } + client = it->second; + } + return client->allocate(params, handle, buffer); +} + +ResultStatus ClientManager::Impl::receive( + ConnectionId connectionId, TransactionId transactionId, + BufferId bufferId, int64_t timestampUs, + native_handle_t **handle, std::shared_ptr *buffer) { + std::shared_ptr client; + { + std::lock_guard lock(mActive.mMutex); + auto it = mActive.mClients.find(connectionId); + if (it == mActive.mClients.end()) { + return ResultStatus::NOT_FOUND; + } + client = it->second; + } + return client->receive(transactionId, bufferId, timestampUs, handle, buffer); +} + +ResultStatus ClientManager::Impl::postSend( + ConnectionId receiverId, const std::shared_ptr &buffer, + TransactionId *transactionId, int64_t *timestampUs) { + ConnectionId connectionId = buffer->mConnectionId; + std::shared_ptr client; + { + std::lock_guard lock(mActive.mMutex); + auto it = mActive.mClients.find(connectionId); + if (it == mActive.mClients.end()) { + return ResultStatus::NOT_FOUND; + } + client = it->second; + } + return client->postSend(receiverId, buffer, transactionId, timestampUs); +} + +ResultStatus ClientManager::Impl::getAccessor( + ConnectionId connectionId, sp *accessor) { + std::shared_ptr client; + { + std::lock_guard lock(mActive.mMutex); + auto it = mActive.mClients.find(connectionId); + if (it == mActive.mClients.end()) { + return ResultStatus::NOT_FOUND; + } + client = it->second; + } + return client->getAccessor(accessor); +} + +void ClientManager::Impl::cleanUp(bool clearCache) { + int64_t now = getTimestampNow(); + int64_t lastTransactionUs; + std::lock_guard lock1(mCache.mMutex); + if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) { + std::lock_guard lock2(mActive.mMutex); + int cleaned = 0; + for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) { + if (!it->second->isActive(&lastTransactionUs, clearCache)) { + if (lastTransactionUs + kClientTimeoutUs < now) { + sp accessor; + it->second->getAccessor(&accessor); + it = mActive.mClients.erase(it); + ++cleaned; + continue; + } + } + ++it; + } + for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) { + // clean up dead client caches + sp cAccessor = cit->first.promote(); + if (!cAccessor) { + cit = mCache.mClients.erase(cit); + } else { + ++cit; + } + } + ALOGV("# of cleaned connections: %d", cleaned); + mCache.mLastCleanUpUs = now; + } +} + +// Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow. +Return ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) { + if (mImpl) { + ConnectionId connectionId = -1; + ResultStatus status = mImpl->registerSender(bufferPool, &connectionId); + _hidl_cb(status, connectionId); + } else { + _hidl_cb(ResultStatus::CRITICAL_ERROR, -1); + } + return Void(); +} + +// Methods for local use. +sp ClientManager::sInstance; +std::mutex ClientManager::sInstanceLock; + +sp ClientManager::getInstance() { + std::lock_guard lock(sInstanceLock); + if (!sInstance) { + sInstance = new ClientManager(); + } + return sInstance; +} + +ClientManager::ClientManager() : mImpl(new Impl()) {} + +ClientManager::~ClientManager() { +} + +ResultStatus ClientManager::create( + const std::shared_ptr &allocator, + ConnectionId *pConnectionId) { + if (mImpl) { + return mImpl->create(allocator, pConnectionId); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::registerSender( + const sp &receiver, + ConnectionId senderId, + ConnectionId *receiverId) { + if (mImpl) { + return mImpl->registerSender(receiver, senderId, receiverId); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::close(ConnectionId connectionId) { + if (mImpl) { + return mImpl->close(connectionId); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::allocate( + ConnectionId connectionId, const std::vector ¶ms, + native_handle_t **handle, std::shared_ptr *buffer) { + if (mImpl) { + return mImpl->allocate(connectionId, params, handle, buffer); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::receive( + ConnectionId connectionId, TransactionId transactionId, + BufferId bufferId, int64_t timestampUs, + native_handle_t **handle, std::shared_ptr *buffer) { + if (mImpl) { + return mImpl->receive(connectionId, transactionId, bufferId, + timestampUs, handle, buffer); + } + return ResultStatus::CRITICAL_ERROR; +} + +ResultStatus ClientManager::postSend( + ConnectionId receiverId, const std::shared_ptr &buffer, + TransactionId *transactionId, int64_t* timestampUs) { + if (mImpl && buffer) { + return mImpl->postSend(receiverId, buffer, transactionId, timestampUs); + } + return ResultStatus::CRITICAL_ERROR; +} + +void ClientManager::cleanUp() { + if (mImpl) { + mImpl->cleanUp(true); + } +} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp new file mode 100644 index 0000000000..cd837a19dc --- /dev/null +++ b/media/bufferpool/2.0/Connection.cpp @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#include "Connection.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +// Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow. +Return Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) { + ResultStatus status = ResultStatus::CRITICAL_ERROR; + if (mInitialized && mAccessor) { + if (bufferId != SYNC_BUFFERID) { + const native_handle_t *handle = nullptr; + status = mAccessor->fetch( + mConnectionId, transactionId, bufferId, &handle); + if (status == ResultStatus::OK) { + _hidl_cb(status, Buffer{bufferId, handle}); + return Void(); + } + } else { + mAccessor->cleanUp(false); + } + } + _hidl_cb(status, Buffer{0, nullptr}); + return Void(); +} + +Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {} + +Connection::~Connection() { + if (mInitialized && mAccessor) { + mAccessor->close(mConnectionId); + } +} + +void Connection::initialize( + const sp& accessor, ConnectionId connectionId) { + if (!mInitialized) { + mAccessor = accessor; + mConnectionId = connectionId; + mInitialized = true; + } +} + +ResultStatus Connection::allocate( + const std::vector ¶ms, BufferId *bufferId, + const native_handle_t **handle) { + if (mInitialized && mAccessor) { + return mAccessor->allocate(mConnectionId, params, bufferId, handle); + } + return ResultStatus::CRITICAL_ERROR; +} + +void Connection::cleanUp(bool clearCache) { + if (mInitialized && mAccessor) { + mAccessor->cleanUp(clearCache); + } +} + +// Methods from ::android::hidl::base::V1_0::IBase follow. + +//IConnection* HIDL_FETCH_IConnection(const char* /* name */) { +// return new Connection(); +//} + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h new file mode 100644 index 0000000000..e2b47f1dcb --- /dev/null +++ b/media/bufferpool/2.0/Connection.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H + +#include +#include +#include +#include +#include "Accessor.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::media::bufferpool::V2_0::implementation::Accessor; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct Connection : public IConnection { + // Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow. + Return fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override; + + /** + * Allocates a buffer using the specified parameters. Recycles a buffer if + * it is possible. The returned buffer can be transferred to other remote + * clients(Connection). + * + * @param params allocation parameters. + * @param bufferId Id of the allocated buffer. + * @param handle native handle of the allocated buffer. + * + * @return OK if a buffer is successfully allocated. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus allocate(const std::vector ¶ms, + BufferId *bufferId, const native_handle_t **handle); + + /** + * Processes pending buffer status messages and performs periodic cache cleaning + * from bufferpool. + * + * @param clearCache if clearCache is true, bufferpool frees all buffers + * waiting to be recycled. + */ + void cleanUp(bool clearCache); + + /** Destructs a connection. */ + ~Connection(); + + /** Creates a connection. */ + Connection(); + + /** + * Initializes with the specified buffer pool and the connection id. + * The connection id should be unique in the whole system. + * + * @param accessor the specified buffer pool. + * @param connectionId Id. + */ + void initialize(const sp &accessor, ConnectionId connectionId); + + enum : uint32_t { + SYNC_BUFFERID = UINT32_MAX, + }; + +private: + bool mInitialized; + sp mAccessor; + ConnectionId mConnectionId; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H diff --git a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h new file mode 100644 index 0000000000..d2de62842c --- /dev/null +++ b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H + +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { + +struct BufferPoolData { + // For local use, to specify a bufferpool (client connection) for buffers. + // Return value from connect#IAccessor(android.hardware.media.bufferpool@2.0). + int64_t mConnectionId; + // BufferId + uint32_t mId; + + BufferPoolData() : mConnectionId(0), mId(0) {} + + BufferPoolData( + int64_t connectionId, uint32_t id) + : mConnectionId(connectionId), mId(id) {} + + ~BufferPoolData() {} +}; + +namespace V2_0 { +namespace implementation { + +using ::android::hardware::kSynchronizedReadWrite; + +typedef uint32_t BufferId; +typedef uint64_t TransactionId; +typedef int64_t ConnectionId; + +enum : ConnectionId { + INVALID_CONNECTIONID = 0, +}; + +typedef android::hardware::MessageQueue BufferStatusQueue; +typedef BufferStatusQueue::Descriptor QueueDescriptor; + +/** + * Allocation wrapper class for buffer pool. + */ +struct BufferPoolAllocation { + const native_handle_t *mHandle; + + const native_handle_t *handle() { + return mHandle; + } + + BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {} + + ~BufferPoolAllocation() {}; +}; + +/** + * Allocator wrapper class for buffer pool. + */ +class BufferPoolAllocator { +public: + + /** + * Allocate an allocation(buffer) for buffer pool. + * + * @param params allocation parameters + * @param alloc created allocation + * @param allocSize size of created allocation + * + * @return OK when an allocation is created successfully. + */ + virtual ResultStatus allocate( + const std::vector ¶ms, + std::shared_ptr *alloc, + size_t *allocSize) = 0; + + /** + * Returns whether allocation parameters of an old allocation are + * compatible with new allocation parameters. + */ + virtual bool compatible(const std::vector &newParams, + const std::vector &oldParams) = 0; + +protected: + BufferPoolAllocator() = default; + + virtual ~BufferPoolAllocator() = default; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h new file mode 100644 index 0000000000..cfc3bc3bcb --- /dev/null +++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H +#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H + +#include +#include +#include +#include +#include "BufferPoolTypes.h" + +namespace android { +namespace hardware { +namespace media { +namespace bufferpool { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::media::bufferpool::V2_0::IAccessor; +using ::android::hardware::media::bufferpool::V2_0::ResultStatus; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct ClientManager : public IClientManager { + // Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow. + Return registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override; + + /** Gets an instance. */ + static sp getInstance(); + + /** + * Creates a local connection with a newly created buffer pool. + * + * @param allocator for new buffer allocation. + * @param pConnectionId Id of the created connection. This is + * system-wide unique. + * + * @return OK when a buffer pool and a local connection is successfully + * created. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus create(const std::shared_ptr &allocator, + ConnectionId *pConnectionId); + + /** + * Register a created connection as sender for remote process. + * + * @param receiver The remote receiving process. + * @param senderId A local connection which will send buffers to. + * @param receiverId Id of the created receiving connection on the receiver + * process. + * + * @return OK when the receiving connection is successfully created on the + * receiver process. + * NOT_FOUND when the sender connection was not found. + * ALREADY_EXISTS the receiving connection is already made. + * CRITICAL_ERROR otherwise. + */ + ResultStatus registerSender(const sp &receiver, + ConnectionId senderId, + ConnectionId *receiverId); + + /** + * Closes the specified connection. + * + * @param connectionId The id of the connection. + * + * @return OK when the connection is closed. + * NOT_FOUND when the specified connection was not found. + * CRITICAL_ERROR otherwise. + */ + ResultStatus close(ConnectionId connectionId); + + /** + * Allocates a buffer from the specified connection. + * + * @param connectionId The id of the connection. + * @param params The allocation parameters. + * @param handle The native handle to the allocated buffer. handle + * should be cloned before use. + * @param buffer The allocated buffer. + * + * @return OK when a buffer was allocated successfully. + * NOT_FOUND when the specified connection was not found. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus allocate(ConnectionId connectionId, + const std::vector ¶ms, + native_handle_t **handle, + std::shared_ptr *buffer); + + /** + * Receives a buffer for the transaction. + * + * @param connectionId The id of the receiving connection. + * @param transactionId The id for the transaction. + * @param bufferId The id for the buffer. + * @param timestampUs The timestamp of the buffer is being sent. + * @param handle The native handle to the allocated buffer. handle + * should be cloned before use. + * @param buffer The received buffer. + * + * @return OK when a buffer was received successfully. + * NOT_FOUND when the specified connection was not found. + * NO_MEMORY when there is no memory. + * CRITICAL_ERROR otherwise. + */ + ResultStatus receive(ConnectionId connectionId, + TransactionId transactionId, + BufferId bufferId, + int64_t timestampUs, + native_handle_t **handle, + std::shared_ptr *buffer); + + /** + * Posts a buffer transfer transaction to the buffer pool. Sends a buffer + * to other remote clients(connection) after this call has been succeeded. + * + * @param receiverId The id of the receiving connection. + * @param buffer to transfer + * @param transactionId Id of the transfer transaction. + * @param timestampUs The timestamp of the buffer transaction is being + * posted. + * + * @return OK when a buffer transaction was posted successfully. + * NOT_FOUND when the sending connection was not found. + * CRITICAL_ERROR otherwise. + */ + ResultStatus postSend(ConnectionId receiverId, + const std::shared_ptr &buffer, + TransactionId *transactionId, + int64_t *timestampUs); + + /** + * Time out inactive lingering connections and close. + */ + void cleanUp(); + + /** Destructs the manager of buffer pool clients. */ + ~ClientManager(); +private: + static sp sInstance; + static std::mutex sInstanceLock; + + class Impl; + const std::unique_ptr mImpl; + + ClientManager(); +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace bufferpool +} // namespace media +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H -- GitLab From 26abaf466f053b033ca678c8f4972c51a4b38d39 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 28 Aug 2018 15:41:20 -0700 Subject: [PATCH 0243/1530] Camera: Make sure timestamp is increasing Guard against case where timestamp of stream buffers doesn't increase. Timestamp source is either monotonic or boottime, both of which are guaranteed to increase. There are 2 exceptions where timestamp may go back in time: - For requests where HAL ZSL is enabled, and - For reprocessing requests Test: Camera CTS Bug: 113340974 Change-Id: I0ae9290696d651168150fa5ed7aa13c140a0294f --- .../device3/Camera3Device.cpp | 24 +++++++++++++------ .../libcameraservice/device3/Camera3Device.h | 16 +++++++++---- .../device3/Camera3OutputStream.cpp | 5 ++++ .../device3/Camera3Stream.cpp | 12 ++++++++-- .../libcameraservice/device3/Camera3Stream.h | 3 ++- .../device3/Camera3StreamInterface.h | 2 +- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 765640742e..cbcc85b798 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -2742,13 +2742,14 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) { status_t Camera3Device::registerInFlight(uint32_t frameNumber, int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput, bool hasAppCallback, nsecs_t maxExpectedDuration, - std::set& physicalCameraIds, bool isStillCapture) { + std::set& physicalCameraIds, bool isStillCapture, + bool isZslCapture) { ATRACE_CALL(); Mutex::Autolock l(mInFlightLock); ssize_t res; res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput, - hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture)); + hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture)); if (res < 0) return res; if (mInFlightMap.size() == 1) { @@ -2766,11 +2767,12 @@ status_t Camera3Device::registerInFlight(uint32_t frameNumber, void Camera3Device::returnOutputBuffers( const camera3_stream_buffer_t *outputBuffers, size_t numBuffers, - nsecs_t timestamp) { + nsecs_t timestamp, bool timestampIncreasing) { + for (size_t i = 0; i < numBuffers; i++) { Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream); - status_t res = stream->returnBuffer(outputBuffers[i], timestamp); + status_t res = stream->returnBuffer(outputBuffers[i], timestamp, timestampIncreasing); // Note: stream may be deallocated at this point, if this buffer was // the last reference to it. if (res != OK) { @@ -3214,8 +3216,9 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { request.pendingOutputBuffers.appendArray(result->output_buffers, result->num_output_buffers); } else { + bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer); returnOutputBuffers(result->output_buffers, - result->num_output_buffers, shutterTimestamp); + result->num_output_buffers, shutterTimestamp, timestampIncreasing); } if (result->result != NULL && !isPartialResult) { @@ -3421,8 +3424,9 @@ void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, r.collectedPartialResult, msg.frame_number, r.hasInputBuffer, r.physicalMetadatas); } + bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer); returnOutputBuffers(r.pendingOutputBuffers.array(), - r.pendingOutputBuffers.size(), r.shutterTimestamp); + r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing); r.pendingOutputBuffers.clear(); removeInFlightRequestIfReadyLocked(idx); @@ -4948,6 +4952,7 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { hasCallback = false; } bool isStillCapture = false; + bool isZslCapture = false; if (!mNextRequests[0].captureRequest->mSettingsList.begin()->metadata.isEmpty()) { camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t(); find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_CAPTURE_INTENT, &e); @@ -4955,13 +4960,18 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { isStillCapture = true; ATRACE_ASYNC_BEGIN("still capture", mNextRequests[i].halRequest.frame_number); } + + find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_ENABLE_ZSL, &e); + if ((e.count > 0) && (e.data.u8[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE)) { + isZslCapture = true; + } } res = parent->registerInFlight(halRequest->frame_number, totalNumBuffers, captureRequest->mResultExtras, /*hasInput*/halRequest->input_buffer != NULL, hasCallback, calculateMaxExpectedDuration(halRequest->settings), - requestedPhysicalCameras, isStillCapture); + requestedPhysicalCameras, isStillCapture, isZslCapture); ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64 ", burstId = %" PRId32 ".", __FUNCTION__, diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 85f9614481..159f2caf97 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -999,6 +999,9 @@ class Camera3Device : // Indicates a still capture request. bool stillCapture; + // Indicates a ZSL capture request + bool zslCapture; + // Default constructor needed by KeyedVector InFlightRequest() : shutterTimestamp(0), @@ -1010,12 +1013,14 @@ class Camera3Device : hasCallback(true), maxExpectedDuration(kDefaultExpectedDuration), skipResultMetadata(false), - stillCapture(false) { + stillCapture(false), + zslCapture(false) { } InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput, bool hasAppCallback, nsecs_t maxDuration, - const std::set& physicalCameraIdSet, bool isStillCapture) : + const std::set& physicalCameraIdSet, bool isStillCapture, + bool isZslCapture) : shutterTimestamp(0), sensorTimestamp(0), requestStatus(OK), @@ -1027,7 +1032,8 @@ class Camera3Device : maxExpectedDuration(maxDuration), skipResultMetadata(false), physicalCameraIds(physicalCameraIdSet), - stillCapture(isStillCapture) { + stillCapture(isStillCapture), + zslCapture(isZslCapture) { } }; @@ -1044,7 +1050,7 @@ class Camera3Device : status_t registerInFlight(uint32_t frameNumber, int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput, bool callback, nsecs_t maxExpectedDuration, std::set& physicalCameraIds, - bool isStillCapture); + bool isStillCapture, bool isZslCapture); /** * Returns the maximum expected time it'll take for all currently in-flight @@ -1154,7 +1160,7 @@ class Camera3Device : // helper function to return the output buffers to the streams. void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers, - size_t numBuffers, nsecs_t timestamp); + size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true); // Send a partial capture result. void sendPartialCaptureResult(const camera_metadata_t * partialResult, diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 2c020a2797..ec3f35f060 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -268,6 +268,11 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( mTraceFirstBuffer = false; } + if (timestamp == 0) { + ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); + return BAD_VALUE; + } + /* Certain consumers (such as AudioSource or HardwareComposer) use * MONOTONIC time, causing time misalignment if camera timestamp is * in BOOTTIME. Do the conversion if necessary. */ diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 6030d15f50..53ff9fe080 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -66,7 +66,8 @@ Camera3Stream::Camera3Stream(int id, mBufferLimitLatency(kBufferLimitLatencyBinSize), mFormatOverridden(false), mOriginalFormat(-1), - mPhysicalCameraId(physicalCameraId) { + mPhysicalCameraId(physicalCameraId), + mLastTimestamp(0) { camera3_stream::stream_type = type; camera3_stream::width = width; @@ -649,7 +650,7 @@ void Camera3Stream::removeOutstandingBuffer(const camera3_stream_buffer &buffer) } status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, - nsecs_t timestamp) { + nsecs_t timestamp, bool timestampIncreasing) { ATRACE_CALL(); Mutex::Autolock l(mLock); @@ -661,6 +662,13 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, removeOutstandingBuffer(buffer); + if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) { + ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64, + __FUNCTION__, mId, timestamp, mLastTimestamp); + return BAD_VALUE; + } + mLastTimestamp = timestamp; + /** * TODO: Check that the state is valid first. * diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 4ddcf1ab85..e30fc598c4 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -320,7 +320,7 @@ class Camera3Stream : * For bidirectional streams, this method applies to the output-side buffers */ status_t returnBuffer(const camera3_stream_buffer &buffer, - nsecs_t timestamp); + nsecs_t timestamp, bool timestampIncreasing); /** * Fill in the camera3_stream_buffer with the next valid buffer for this @@ -559,6 +559,7 @@ class Camera3Stream : android_dataspace mOriginalDataSpace; String8 mPhysicalCameraId; + nsecs_t mLastTimestamp; }; // class Camera3Stream }; // namespace camera3 diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index 9ed7184203..bddd2e7c7d 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -246,7 +246,7 @@ class Camera3StreamInterface : public virtual RefBase { * For bidirectional streams, this method applies to the output-side buffers */ virtual status_t returnBuffer(const camera3_stream_buffer &buffer, - nsecs_t timestamp) = 0; + nsecs_t timestamp, bool timestampIncreasing = true) = 0; /** * Fill in the camera3_stream_buffer with the next valid buffer for this -- GitLab From 3a34df9c6e81d4638dcd763f990de89b1de3a8b5 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 21 Aug 2018 12:32:30 -0700 Subject: [PATCH 0244/1530] AudioMixer: Change setBufferProvider order of reset Test: stress test Bug: 111313529 Change-Id: I1269c1e7f1609ee6edca31625cf508ff3101a0a0 --- media/libaudioprocessing/AudioMixer.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index f3ea8260de..af54b21986 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -337,6 +337,7 @@ status_t AudioMixer::Track::prepareForReformat() void AudioMixer::Track::reconfigureBufferProviders() { + // configure from upstream to downstream buffer providers. bufferProvider = mInputBufferProvider; if (mReformatBufferProvider.get() != nullptr) { mReformatBufferProvider->setBufferProvider(bufferProvider); @@ -813,14 +814,15 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider if (track->mInputBufferProvider == bufferProvider) { return; // don't reset any buffer providers if identical. } - if (track->mReformatBufferProvider.get() != nullptr) { - track->mReformatBufferProvider->reset(); - } else if (track->mDownmixerBufferProvider != nullptr) { - track->mDownmixerBufferProvider->reset(); + // reset order from downstream to upstream buffer providers. + if (track->mTimestretchBufferProvider.get() != nullptr) { + track->mTimestretchBufferProvider->reset(); } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) { track->mPostDownmixReformatBufferProvider->reset(); - } else if (track->mTimestretchBufferProvider.get() != nullptr) { - track->mTimestretchBufferProvider->reset(); + } else if (track->mDownmixerBufferProvider != nullptr) { + track->mDownmixerBufferProvider->reset(); + } else if (track->mReformatBufferProvider.get() != nullptr) { + track->mReformatBufferProvider->reset(); } track->mInputBufferProvider = bufferProvider; -- GitLab From ad02cc6c4091a44fb60c475731c8f4cac9a55ab0 Mon Sep 17 00:00:00 2001 From: Edwin Wong Date: Thu, 23 Aug 2018 13:48:27 -0700 Subject: [PATCH 0245/1530] Add onExpirationUpdate and onKeyStatusChange listeners. We must only create one DrmListener instance. We then process different listeners in DrmListener::notify. To facilitate testing, we call the listeners from clearkey plugin's provideKeyResponse function. We have previously tested EventType::VENDOR_DEFINED in the same manner. bug: 77712870 Test: native CTS test testClearKeyPlaybackCenc Test: CTS MediaDrmMockTest Change-Id: Ie15e3012a4068824f72371a66e9fca2ee27180f8 --- .../plugins/clearkey/hidl/DrmPlugin.cpp | 24 ++- media/ndk/NdkMediaDrm.cpp | 165 +++++++++++++++--- media/ndk/include/media/NdkMediaDrm.h | 124 ++++++++++--- 3 files changed, 259 insertions(+), 54 deletions(-) diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp index d51e29dc57..3b6108526b 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -245,11 +245,29 @@ Return DrmPlugin::provideKeyResponse( setPlayPolicy(); std::vector keySetId; + keySetId.clear(); + Status status = session->provideKeyResponse(response); if (status == Status::OK) { - // This is for testing AMediaDrm_setOnEventListener only. - sendEvent(EventType::VENDOR_DEFINED, 0, scope); - keySetId.clear(); + // Test calling AMediaDrm listeners. + sendEvent(EventType::VENDOR_DEFINED, toVector(scope), toVector(scope)); + + sendExpirationUpdate(toVector(scope), 100); + + std::vector keysStatus; + KeyStatus keyStatus; + + std::vector keyId1 = { 0xA, 0xB, 0xC }; + keyStatus.keyId = keyId1; + keyStatus.type = V1_0::KeyStatusType::USABLE; + keysStatus.push_back(keyStatus); + + std::vector keyId2 = { 0xD, 0xE, 0xF }; + keyStatus.keyId = keyId2; + keyStatus.type = V1_0::KeyStatusType::EXPIRED; + keysStatus.push_back(keyStatus); + + sendKeysChange(toVector(scope), keysStatus, true); } installSecureStop(scope); diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp index 5597488637..2552073324 100644 --- a/media/ndk/NdkMediaDrm.cpp +++ b/media/ndk/NdkMediaDrm.cpp @@ -17,6 +17,8 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "NdkMediaDrm" +#include + #include #include @@ -40,10 +42,32 @@ struct DrmListener: virtual public BnDrmClient { private: AMediaDrm *mObj; - AMediaDrmEventListener mListener; + AMediaDrmEventListener mEventListener; + AMediaDrmExpirationUpdateListener mExpirationUpdateListener; + AMediaDrmKeysChangeListener mKeysChangeListener; public: - DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {} + DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), + mEventListener(listener), mExpirationUpdateListener(NULL), mKeysChangeListener(NULL) {} + + DrmListener(AMediaDrm *obj, AMediaDrmExpirationUpdateListener listener) : mObj(obj), + mEventListener(NULL), mExpirationUpdateListener(listener), mKeysChangeListener(NULL) {} + + DrmListener(AMediaDrm *obj, AMediaDrmKeysChangeListener listener) : mObj(obj), + mEventListener(NULL), mExpirationUpdateListener(NULL), mKeysChangeListener(listener) {} + + void setEventListener(AMediaDrmEventListener listener) { + mEventListener = listener; + } + + void setExpirationUpdateListener(AMediaDrmExpirationUpdateListener listener) { + mExpirationUpdateListener = listener; + } + + void setKeysChangeListener(AMediaDrmKeysChangeListener listener) { + mKeysChangeListener = listener; + } + void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj); }; @@ -62,27 +86,75 @@ struct AMediaDrm { }; void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) { - if (!mListener) { + if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) { + ALOGE("No listeners are specified"); return; } + obj->setDataPosition(0); + AMediaDrmSessionId sessionId = {NULL, 0}; int32_t sessionIdSize = obj->readInt32(); - if (sessionIdSize) { - uint8_t *sessionIdData = new uint8_t[sessionIdSize]; - sessionId.ptr = sessionIdData; - sessionId.length = sessionIdSize; - obj->read(sessionIdData, sessionId.length); + if (sessionIdSize <= 0) { + ALOGE("Invalid session id size"); + return; } - int32_t dataSize = obj->readInt32(); - uint8_t *data = NULL; - if (dataSize) { - data = new uint8_t[dataSize]; - obj->read(data, dataSize); + std::unique_ptr sessionIdData(new uint8_t[sessionIdSize]); + sessionId.ptr = sessionIdData.get(); + sessionId.length = sessionIdSize; + status_t err = obj->read(sessionIdData.get(), sessionId.length); + if (err != OK) { + ALOGE("Failed to read session id, error=%d", err); + return; } - // translate DrmPlugin event types into their NDK equivalents + if (DrmPlugin::kDrmPluginEventExpirationUpdate == eventType) { + int64_t expiryTimeInMS = obj->readInt64(); + if (expiryTimeInMS >= 0) { + (*mExpirationUpdateListener)(mObj, &sessionId, expiryTimeInMS); + } else { + ALOGE("Failed to read expiry time, status=%" PRId64 "", expiryTimeInMS); + } + return; + } else if (DrmPlugin::kDrmPluginEventKeysChange == eventType) { + int32_t numKeys = 0; + err = obj->readInt32(&numKeys); + if (err != OK) { + ALOGE("Failed to read number of keys status, error=%d", err); + return; + } + + Vector keysStatus; + std::vector > dataPointers; + AMediaDrmKeyStatus keyStatus; + + for (size_t i = 0; i < numKeys; ++i) { + keyStatus.keyId.ptr = nullptr; + keyStatus.keyId.length = 0; + int32_t idSize = obj->readInt32(); + if (idSize > 0) { + std::unique_ptr data(new uint8_t[idSize]); + err = obj->read(data.get(), idSize); + if (err != OK) { + ALOGE("Failed to read key data, error=%d", err); + return; + } + keyStatus.keyId.ptr = data.get(); + keyStatus.keyId.length = idSize; + dataPointers.push_back(std::move(data)); + } + keyStatus.keyType = static_cast(obj->readInt32()); + keysStatus.push(keyStatus); + } + + bool hasNewUsableKey = obj->readInt32(); + (*mKeysChangeListener)(mObj, &sessionId, keysStatus.array(), numKeys, hasNewUsableKey); + return; + } + + // Handles AMediaDrmEventListener below: + // translates DrmPlugin event types into their NDK equivalents AMediaDrmEventType ndkEventType; switch(eventType) { case DrmPlugin::kDrmPluginEventProvisionRequired: @@ -97,19 +169,30 @@ void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel case DrmPlugin::kDrmPluginEventVendorDefined: ndkEventType = EVENT_VENDOR_DEFINED; break; + case DrmPlugin::kDrmPluginEventSessionReclaimed: + ndkEventType = EVENT_SESSION_RECLAIMED; + break; default: ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); - goto cleanup; + return; } - (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize); - - cleanup: - delete [] sessionId.ptr; - delete [] data; + int32_t dataSize = obj->readInt32(); + uint8_t *data = NULL; + if (dataSize > 0) { + data = new uint8_t[dataSize]; + err = obj->read(data, dataSize); + if (err == OK) { + (*mEventListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize); + } else { + ALOGE("Failed to read event data, error=%d", err); + } + delete [] data; + } else { + ALOGE("Error reading parcel: invalid event data size=%d", dataSize); + } } - extern "C" { static media_status_t translateStatus(status_t status) { @@ -198,6 +281,8 @@ EXPORT AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) { AMediaDrm *mObj = new AMediaDrm(); mObj->mDrm = CreateDrmFromUUID(uuid); + + mObj->mListener.clear(); return mObj; } @@ -216,11 +301,47 @@ media_status_t AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListe if (!mObj || mObj->mDrm == NULL) { return AMEDIA_ERROR_INVALID_OBJECT; } - mObj->mListener = new DrmListener(mObj, listener); + + if (mObj->mListener.get()) { + mObj->mListener->setEventListener(listener); + } else { + mObj->mListener = new DrmListener(mObj, listener); + } + mObj->mDrm->setListener(mObj->mListener); + return AMEDIA_OK; +} + +EXPORT +media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *mObj, + AMediaDrmExpirationUpdateListener listener) { + if (!mObj || mObj->mDrm == NULL) { + return AMEDIA_ERROR_INVALID_OBJECT; + } + + if (mObj->mListener.get()) { + mObj->mListener->setExpirationUpdateListener(listener); + } else { + mObj->mListener = new DrmListener(mObj, listener); + } mObj->mDrm->setListener(mObj->mListener); return AMEDIA_OK; } +EXPORT +media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *mObj, + AMediaDrmKeysChangeListener listener) { + if (!mObj || mObj->mDrm == NULL) { + return AMEDIA_ERROR_INVALID_OBJECT; + } + + if (mObj->mListener.get()) { + mObj->mListener->setKeysChangeListener(listener); + } else { + mObj->mListener = new DrmListener(mObj, listener); + } + mObj->mDrm->setListener(mObj->mListener); + return AMEDIA_OK; +} static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List::iterator &iter) { for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) { diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h index 020968173b..2e438d9cbf 100644 --- a/media/ndk/include/media/NdkMediaDrm.h +++ b/media/ndk/include/media/NdkMediaDrm.h @@ -56,6 +56,7 @@ typedef AMediaDrmByteArray AMediaDrmSessionId; typedef AMediaDrmByteArray AMediaDrmScope; typedef AMediaDrmByteArray AMediaDrmKeySetId; typedef AMediaDrmByteArray AMediaDrmSecureStop; +typedef AMediaDrmByteArray AMediaDrmKeyId; typedef enum AMediaDrmEventType { /** @@ -81,12 +82,89 @@ typedef enum AMediaDrmEventType { * This event may indicate some specific vendor-defined condition, see your * DRM provider documentation for details */ - EVENT_VENDOR_DEFINED = 4 + EVENT_VENDOR_DEFINED = 4, + + /** + * This event indicates that a session opened by the app has been reclaimed + * by the resource manager. + */ + EVENT_SESSION_RECLAIMED = 5, } AMediaDrmEventType; +typedef enum AMediaDrmKeyType { + /** + * This key request type specifies that the keys will be for online use, they will + * not be saved to the device for subsequent use when the device is not connected + * to a network. + */ + KEY_TYPE_STREAMING = 1, + + /** + * This key request type specifies that the keys will be for offline use, they + * will be saved to the device for use when the device is not connected to a network. + */ + KEY_TYPE_OFFLINE = 2, + + /** + * This key request type specifies that previously saved offline keys should be released. + */ + KEY_TYPE_RELEASE = 3 +} AMediaDrmKeyType; + +/** + * Data type containing {key, value} pair + */ +typedef struct AMediaDrmKeyValuePair { + const char *mKey; + const char *mValue; +} AMediaDrmKeyValue; + +typedef enum AMediaKeyStatusType { + /** + * The key is currently usable to decrypt media data. + */ + KEY_STATUS_TYPE_USABLE, + + /** + * The key is no longer usable to decrypt media data because its expiration + * time has passed. + */ + KEY_STATUS_TYPE_EXPIRED, + + /** + * The key is not currently usable to decrypt media data because its output + * requirements cannot currently be met. + */ + KEY_STATUS_TYPE_OUTPUTNOTALLOWED, + + /** + * The status of the key is not yet known and is being determined. + */ + KEY_STATUS_TYPE_STATUSPENDING, + + /** + * The key is not currently usable to decrypt media data because of an + * internal error in processing unrelated to input parameters. + */ + KEY_STATUS_TYPE_INTERNALERROR, + +} AMediaDrmKeyStatusType; + +typedef struct AMediaDrmKeyStatus { + AMediaDrmKeyId keyId; + AMediaDrmKeyStatusType keyType; +} AMediaDrmKeyStatus; + typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId, AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize); +typedef void (*AMediaDrmExpirationUpdateListener)(AMediaDrm *, + const AMediaDrmSessionId *sessionId, int64_t expiryTimeInMS); + +typedef void (*AMediaDrmKeysChangeListener)(AMediaDrm *, + const AMediaDrmSessionId *sessionId, const AMediaDrmKeyStatus *keyStatus, + size_t numKeys, bool hasNewUsableKey); + #if __ANDROID_API__ >= 21 /** @@ -119,6 +197,22 @@ void AMediaDrm_release(AMediaDrm *) __INTRODUCED_IN(21); media_status_t AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener) __INTRODUCED_IN(21); +/** + * Register a callback to be invoked when an expiration update event occurs + * + * listener is the callback that will be invoked on event + */ +media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *, + AMediaDrmExpirationUpdateListener listener) __INTRODUCED_IN(29); + +/** + * Register a callback to be invoked when a key status change event occurs + * + * listener is the callback that will be invoked on event + */ +media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *, + AMediaDrmKeysChangeListener listener) __INTRODUCED_IN(29); + /** * Open a new session with the MediaDrm object. A session ID is returned. * @@ -135,34 +229,6 @@ media_status_t AMediaDrm_openSession(AMediaDrm *, media_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21); -typedef enum AMediaDrmKeyType { - /** - * This key request type species that the keys will be for online use, they will - * not be saved to the device for subsequent use when the device is not connected - * to a network. - */ - KEY_TYPE_STREAMING = 1, - - /** - * This key request type specifies that the keys will be for offline use, they - * will be saved to the device for use when the device is not connected to a network. - */ - KEY_TYPE_OFFLINE = 2, - - /** - * This key request type specifies that previously saved offline keys should be released. - */ - KEY_TYPE_RELEASE = 3 -} AMediaDrmKeyType; - -/** - * Data type containing {key, value} pair - */ -typedef struct AMediaDrmKeyValuePair { - const char *mKey; - const char *mValue; -} AMediaDrmKeyValue; - /** * A key request/response exchange occurs between the app and a license server * to obtain or release keys used to decrypt encrypted content. -- GitLab From 5ec3d6ac0c607e89d03ba5a9499e471d8559dc7e Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 24 Aug 2018 16:57:58 -0700 Subject: [PATCH 0246/1530] Move unused classes out of stagefright foundataion ANetworkSession is no longer used by frameworks, AHierachicalStateMachine is needed by ACodec only. bug: 111407413 test: build test Change-Id: I203e8c36552a60be4c8fa37df3c981d32998286e --- .../AHierarchicalStateMachine.cpp | 2 +- media/libstagefright/Android.bp | 3 +- .../foundation/ANetworkSession.cpp | 1401 ----------------- media/libstagefright/foundation/Android.bp | 3 - .../foundation/ParsedMessage.cpp | 302 ---- .../stagefright/foundation/ANetworkSession.h | 135 -- .../stagefright/foundation/ParsedMessage.h | 60 - .../include/media/stagefright/ACodec.h | 2 +- .../stagefright}/AHierarchicalStateMachine.h | 0 9 files changed, 4 insertions(+), 1904 deletions(-) rename media/libstagefright/{foundation => }/AHierarchicalStateMachine.cpp (97%) delete mode 100644 media/libstagefright/foundation/ANetworkSession.cpp delete mode 100644 media/libstagefright/foundation/ParsedMessage.cpp delete mode 100644 media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h delete mode 100644 media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h rename media/libstagefright/{foundation/include/media/stagefright/foundation => include/media/stagefright}/AHierarchicalStateMachine.h (100%) diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/AHierarchicalStateMachine.cpp similarity index 97% rename from media/libstagefright/foundation/AHierarchicalStateMachine.cpp rename to media/libstagefright/AHierarchicalStateMachine.cpp index b837f66f08..f89b8b0bfa 100644 --- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp +++ b/media/libstagefright/AHierarchicalStateMachine.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "AHierarchicalStateMachine" #include -#include +#include #include #include diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index fec40b6010..ae2f61b0e1 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -90,9 +90,10 @@ cc_library_shared { name: "libstagefright", srcs: [ + "AACWriter.cpp", "ACodec.cpp", "ACodecBufferChannel.cpp", - "AACWriter.cpp", + "AHierarchicalStateMachine.cpp", "AMRWriter.cpp", "AudioPlayer.cpp", "AudioPresentationInfo.cpp", diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp deleted file mode 100644 index eafdc3773c..0000000000 --- a/media/libstagefright/foundation/ANetworkSession.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * Copyright 2012, 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 "NetworkSession" -#include - -#include "ANetworkSession.h" -#include "ParsedMessage.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace android { - -static const size_t kMaxUDPSize = 1500; -static const int32_t kMaxUDPRetries = 200; - -struct ANetworkSession::NetworkThread : public Thread { - explicit NetworkThread(ANetworkSession *session); - -protected: - virtual ~NetworkThread(); - -private: - ANetworkSession *mSession; - - virtual bool threadLoop(); - - DISALLOW_EVIL_CONSTRUCTORS(NetworkThread); -}; - -struct ANetworkSession::Session : public RefBase { - enum Mode { - MODE_RTSP, - MODE_DATAGRAM, - MODE_WEBSOCKET, - }; - - enum State { - CONNECTING, - CONNECTED, - LISTENING_RTSP, - LISTENING_TCP_DGRAMS, - DATAGRAM, - }; - - Session(int32_t sessionID, - State state, - int s, - const sp ¬ify); - - int32_t sessionID() const; - int socket() const; - sp getNotificationMessage() const; - - bool isRTSPServer() const; - bool isTCPDatagramServer() const; - - bool wantsToRead(); - bool wantsToWrite(); - - status_t readMore(); - status_t writeMore(); - - status_t sendRequest( - const void *data, ssize_t size, bool timeValid, int64_t timeUs); - - void setMode(Mode mode); - - status_t switchToWebSocketMode(); - -protected: - virtual ~Session(); - -private: - enum { - FRAGMENT_FLAG_TIME_VALID = 1, - }; - struct Fragment { - uint32_t mFlags; - int64_t mTimeUs; - sp mBuffer; - }; - - int32_t mSessionID; - State mState; - Mode mMode; - int mSocket; - sp mNotify; - bool mSawReceiveFailure, mSawSendFailure; - int32_t mUDPRetries; - - List mOutFragments; - - AString mInBuffer; - - int64_t mLastStallReportUs; - - void notifyError(bool send, status_t err, const char *detail); - void notify(NotificationReason reason); - - void dumpFragmentStats(const Fragment &frag); - - DISALLOW_EVIL_CONSTRUCTORS(Session); -}; -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session) - : mSession(session) { -} - -ANetworkSession::NetworkThread::~NetworkThread() { -} - -bool ANetworkSession::NetworkThread::threadLoop() { - mSession->threadLoop(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::Session::Session( - int32_t sessionID, - State state, - int s, - const sp ¬ify) - : mSessionID(sessionID), - mState(state), - mMode(MODE_DATAGRAM), - mSocket(s), - mNotify(notify), - mSawReceiveFailure(false), - mSawSendFailure(false), - mUDPRetries(kMaxUDPRetries), - mLastStallReportUs(-1ll) { - if (mState == CONNECTED) { - struct sockaddr_in localAddr; - socklen_t localAddrLen = sizeof(localAddr); - - int res = getsockname( - mSocket, (struct sockaddr *)&localAddr, &localAddrLen); - CHECK_GE(res, 0); - - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - res = getpeername( - mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen); - CHECK_GE(res, 0); - - in_addr_t addr = ntohl(localAddr.sin_addr.s_addr); - AString localAddrString = AStringPrintf( - "%d.%d.%d.%d", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff); - - addr = ntohl(remoteAddr.sin_addr.s_addr); - AString remoteAddrString = AStringPrintf( - "%d.%d.%d.%d", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff); - - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatClientConnected); - msg->setString("server-ip", localAddrString.c_str()); - msg->setInt32("server-port", ntohs(localAddr.sin_port)); - msg->setString("client-ip", remoteAddrString.c_str()); - msg->setInt32("client-port", ntohs(remoteAddr.sin_port)); - msg->post(); - } -} - -ANetworkSession::Session::~Session() { - ALOGV("Session %d gone", mSessionID); - - close(mSocket); - mSocket = -1; -} - -int32_t ANetworkSession::Session::sessionID() const { - return mSessionID; -} - -int ANetworkSession::Session::socket() const { - return mSocket; -} - -void ANetworkSession::Session::setMode(Mode mode) { - mMode = mode; -} - -status_t ANetworkSession::Session::switchToWebSocketMode() { - if (mState != CONNECTED || mMode != MODE_RTSP) { - return INVALID_OPERATION; - } - - mMode = MODE_WEBSOCKET; - - return OK; -} - -sp ANetworkSession::Session::getNotificationMessage() const { - return mNotify; -} - -bool ANetworkSession::Session::isRTSPServer() const { - return mState == LISTENING_RTSP; -} - -bool ANetworkSession::Session::isTCPDatagramServer() const { - return mState == LISTENING_TCP_DGRAMS; -} - -bool ANetworkSession::Session::wantsToRead() { - return !mSawReceiveFailure && mState != CONNECTING; -} - -bool ANetworkSession::Session::wantsToWrite() { - return !mSawSendFailure - && (mState == CONNECTING - || (mState == CONNECTED && !mOutFragments.empty()) - || (mState == DATAGRAM && !mOutFragments.empty())); -} - -status_t ANetworkSession::Session::readMore() { - if (mState == DATAGRAM) { - CHECK_EQ(mMode, MODE_DATAGRAM); - - status_t err; - do { - sp buf = new ABuffer(kMaxUDPSize); - - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - ssize_t n; - do { - n = recvfrom( - mSocket, buf->data(), buf->capacity(), 0, - (struct sockaddr *)&remoteAddr, &remoteAddrLen); - } while (n < 0 && errno == EINTR); - - err = OK; - if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } else { - buf->setRange(0, n); - - int64_t nowUs = ALooper::GetNowUs(); - buf->meta()->setInt64("arrivalTimeUs", nowUs); - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatDatagram); - - uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr); - notify->setString( - "fromAddr", - AStringPrintf( - "%u.%u.%u.%u", - ip >> 24, - (ip >> 16) & 0xff, - (ip >> 8) & 0xff, - ip & 0xff).c_str()); - - notify->setInt32("fromPort", ntohs(remoteAddr.sin_port)); - - notify->setBuffer("data", buf); - notify->post(); - } - } while (err == OK); - - if (err == -EAGAIN) { - err = OK; - } - - if (err != OK) { - if (!mUDPRetries) { - notifyError(false /* send */, err, "Recvfrom failed."); - mSawReceiveFailure = true; - } else { - mUDPRetries--; - ALOGE("Recvfrom failed, %d/%d retries left", - mUDPRetries, kMaxUDPRetries); - err = OK; - } - } else { - mUDPRetries = kMaxUDPRetries; - } - - return err; - } - - char tmp[512]; - ssize_t n; - do { - n = recv(mSocket, tmp, sizeof(tmp), 0); - } while (n < 0 && errno == EINTR); - - status_t err = OK; - - if (n > 0) { - mInBuffer.append(tmp, n); - -#if 0 - ALOGI("in:"); - hexdump(tmp, n); -#endif - } else if (n < 0) { - err = -errno; - } else { - err = -ECONNRESET; - } - - if (mMode == MODE_DATAGRAM) { - // TCP stream carrying 16-bit length-prefixed datagrams. - - while (mInBuffer.size() >= 2) { - size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str()); - - if (mInBuffer.size() < packetSize + 2) { - break; - } - - sp packet = new ABuffer(packetSize); - memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize); - - int64_t nowUs = ALooper::GetNowUs(); - packet->meta()->setInt64("arrivalTimeUs", nowUs); - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatDatagram); - notify->setBuffer("data", packet); - notify->post(); - - mInBuffer.erase(0, packetSize + 2); - } - } else if (mMode == MODE_RTSP) { - for (;;) { - size_t length; - - if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') { - if (mInBuffer.size() < 4) { - break; - } - - length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2); - - if (mInBuffer.size() < 4 + length) { - break; - } - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatBinaryData); - notify->setInt32("channel", mInBuffer.c_str()[1]); - - sp data = new ABuffer(length); - memcpy(data->data(), mInBuffer.c_str() + 4, length); - - int64_t nowUs = ALooper::GetNowUs(); - data->meta()->setInt64("arrivalTimeUs", nowUs); - - notify->setBuffer("data", data); - notify->post(); - - mInBuffer.erase(0, 4 + length); - continue; - } - - sp msg = - ParsedMessage::Parse( - mInBuffer.c_str(), mInBuffer.size(), err != OK, &length); - - if (msg == NULL) { - break; - } - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatData); - notify->setObject("data", msg); - notify->post(); - -#if 1 - // XXX The (old) dongle sends the wrong content length header on a - // SET_PARAMETER request that signals a "wfd_idr_request". - // (17 instead of 19). - const char *content = msg->getContent(); - if (content - && !memcmp(content, "wfd_idr_request\r\n", 17) - && length >= 19 - && mInBuffer.c_str()[length] == '\r' - && mInBuffer.c_str()[length + 1] == '\n') { - length += 2; - } -#endif - - mInBuffer.erase(0, length); - - if (err != OK) { - break; - } - } - } else { - CHECK_EQ(mMode, MODE_WEBSOCKET); - - const uint8_t *data = (const uint8_t *)mInBuffer.c_str(); - // hexdump(data, mInBuffer.size()); - - while (mInBuffer.size() >= 2) { - size_t offset = 2; - - uint64_t payloadLen = data[1] & 0x7f; - if (payloadLen == 126) { - if (offset + 2 > mInBuffer.size()) { - break; - } - - payloadLen = U16_AT(&data[offset]); - offset += 2; - } else if (payloadLen == 127) { - if (offset + 8 > mInBuffer.size()) { - break; - } - - payloadLen = U64_AT(&data[offset]); - offset += 8; - } - - uint32_t mask = 0; - if (data[1] & 0x80) { - // MASK==1 - if (offset + 4 > mInBuffer.size()) { - break; - } - - mask = U32_AT(&data[offset]); - offset += 4; - } - - if (payloadLen > mInBuffer.size() || offset > mInBuffer.size() - payloadLen) { - break; - } - - // We have the full message. - - sp packet = new ABuffer(payloadLen); - memcpy(packet->data(), &data[offset], payloadLen); - - if (mask != 0) { - for (size_t i = 0; i < payloadLen; ++i) { - packet->data()[i] = - data[offset + i] - ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff); - } - } - - sp notify = mNotify->dup(); - notify->setInt32("sessionID", mSessionID); - notify->setInt32("reason", kWhatWebSocketMessage); - notify->setBuffer("data", packet); - notify->setInt32("headerByte", data[0]); - notify->post(); - - mInBuffer.erase(0, offset + payloadLen); - } - } - - if (err != OK) { - notifyError(false /* send */, err, "Recv failed."); - mSawReceiveFailure = true; - } - - return err; -} - -void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) { -#if 0 - int64_t nowUs = ALooper::GetNowUs(); - int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll; - - static const int64_t kMinDelayMs = 0; - static const int64_t kMaxDelayMs = 300; - - const char *kPattern = "########################################"; - size_t kPatternSize = strlen(kPattern); - - int n = (kPatternSize * (delayMs - kMinDelayMs)) - / (kMaxDelayMs - kMinDelayMs); - - if (n < 0) { - n = 0; - } else if ((size_t)n > kPatternSize) { - n = kPatternSize; - } - - ALOGI("[%lld]: (%4lld ms) %s\n", - frag.mTimeUs / 1000, - delayMs, - kPattern + kPatternSize - n); -#endif -} - -status_t ANetworkSession::Session::writeMore() { - if (mState == DATAGRAM) { - CHECK(!mOutFragments.empty()); - - status_t err; - do { - const Fragment &frag = *mOutFragments.begin(); - const sp &datagram = frag.mBuffer; - - int n; - do { - n = send(mSocket, datagram->data(), datagram->size(), 0); - } while (n < 0 && errno == EINTR); - - err = OK; - - if (n > 0) { - if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { - dumpFragmentStats(frag); - } - - mOutFragments.erase(mOutFragments.begin()); - } else if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } - } while (err == OK && !mOutFragments.empty()); - - if (err == -EAGAIN) { - if (!mOutFragments.empty()) { - ALOGI("%zu datagrams remain queued.", mOutFragments.size()); - } - err = OK; - } - - if (err != OK) { - if (!mUDPRetries) { - notifyError(true /* send */, err, "Send datagram failed."); - mSawSendFailure = true; - } else { - mUDPRetries--; - ALOGE("Send datagram failed, %d/%d retries left", - mUDPRetries, kMaxUDPRetries); - err = OK; - } - } else { - mUDPRetries = kMaxUDPRetries; - } - - return err; - } - - if (mState == CONNECTING) { - int err; - socklen_t optionLen = sizeof(err); - CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0); - CHECK_EQ(optionLen, (socklen_t)sizeof(err)); - - if (err != 0) { - notifyError(kWhatError, -err, "Connection failed"); - mSawSendFailure = true; - - return -err; - } - - mState = CONNECTED; - notify(kWhatConnected); - - return OK; - } - - CHECK_EQ(mState, CONNECTED); - CHECK(!mOutFragments.empty()); - - ssize_t n = -1; - while (!mOutFragments.empty()) { - const Fragment &frag = *mOutFragments.begin(); - - do { - n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0); - } while (n < 0 && errno == EINTR); - - if (n <= 0) { - break; - } - - frag.mBuffer->setRange( - frag.mBuffer->offset() + n, frag.mBuffer->size() - n); - - if (frag.mBuffer->size() > 0) { - break; - } - - if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { - dumpFragmentStats(frag); - } - - mOutFragments.erase(mOutFragments.begin()); - } - - status_t err = OK; - - if (n < 0) { - err = -errno; - } else if (n == 0) { - err = -ECONNRESET; - } - - if (err != OK) { - notifyError(true /* send */, err, "Send failed."); - mSawSendFailure = true; - } - -#if 0 - int numBytesQueued; - int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); - if (res == 0 && numBytesQueued > 50 * 1024) { - if (numBytesQueued > 409600) { - ALOGW("!!! numBytesQueued = %d", numBytesQueued); - } - - int64_t nowUs = ALooper::GetNowUs(); - - if (mLastStallReportUs < 0ll - || nowUs > mLastStallReportUs + 100000ll) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatNetworkStall); - msg->setSize("numBytesQueued", numBytesQueued); - msg->post(); - - mLastStallReportUs = nowUs; - } - } -#endif - - return err; -} - -status_t ANetworkSession::Session::sendRequest( - const void *data, ssize_t size, bool timeValid, int64_t timeUs) { - CHECK(mState == CONNECTED || mState == DATAGRAM); - - if (size < 0) { - size = strlen((const char *)data); - } - - if (size == 0) { - return OK; - } - - sp buffer; - - if (mState == CONNECTED && mMode == MODE_DATAGRAM) { - CHECK_LE(size, 65535); - - buffer = new ABuffer(size + 2); - buffer->data()[0] = size >> 8; - buffer->data()[1] = size & 0xff; - memcpy(buffer->data() + 2, data, size); - } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) { - static const bool kUseMask = false; // Chromium doesn't like it. - - size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0); - if (size > 65535) { - numHeaderBytes += 8; - } else if (size > 125) { - numHeaderBytes += 2; - } - - buffer = new ABuffer(numHeaderBytes + size); - buffer->data()[0] = 0x81; // FIN==1 | opcode=1 (text) - buffer->data()[1] = kUseMask ? 0x80 : 0x00; - - if (size > 65535) { - buffer->data()[1] |= 127; - buffer->data()[2] = 0x00; - buffer->data()[3] = 0x00; - buffer->data()[4] = 0x00; - buffer->data()[5] = 0x00; - buffer->data()[6] = (size >> 24) & 0xff; - buffer->data()[7] = (size >> 16) & 0xff; - buffer->data()[8] = (size >> 8) & 0xff; - buffer->data()[9] = size & 0xff; - } else if (size > 125) { - buffer->data()[1] |= 126; - buffer->data()[2] = (size >> 8) & 0xff; - buffer->data()[3] = size & 0xff; - } else { - buffer->data()[1] |= size; - } - - if (kUseMask) { - uint32_t mask = rand(); - - buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff; - buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff; - buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff; - buffer->data()[numHeaderBytes - 1] = mask & 0xff; - - for (size_t i = 0; i < (size_t)size; ++i) { - buffer->data()[numHeaderBytes + i] = - ((const uint8_t *)data)[i] - ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff); - } - } else { - memcpy(buffer->data() + numHeaderBytes, data, size); - } - } else { - buffer = new ABuffer(size); - memcpy(buffer->data(), data, size); - } - - Fragment frag; - - frag.mFlags = 0; - if (timeValid) { - frag.mFlags = FRAGMENT_FLAG_TIME_VALID; - frag.mTimeUs = timeUs; - } - - frag.mBuffer = buffer; - - mOutFragments.push_back(frag); - - return OK; -} - -void ANetworkSession::Session::notifyError( - bool send, status_t err, const char *detail) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", kWhatError); - msg->setInt32("send", send); - msg->setInt32("err", err); - msg->setString("detail", detail); - msg->post(); -} - -void ANetworkSession::Session::notify(NotificationReason reason) { - sp msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", reason); - msg->post(); -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::ANetworkSession() - : mNextSessionID(1) { - mPipeFd[0] = mPipeFd[1] = -1; -} - -ANetworkSession::~ANetworkSession() { - stop(); -} - -status_t ANetworkSession::start() { - if (mThread != NULL) { - return INVALID_OPERATION; - } - - int res = pipe(mPipeFd); - if (res != 0) { - mPipeFd[0] = mPipeFd[1] = -1; - return -errno; - } - - mThread = new NetworkThread(this); - - status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO); - - if (err != OK) { - mThread.clear(); - - close(mPipeFd[0]); - close(mPipeFd[1]); - mPipeFd[0] = mPipeFd[1] = -1; - - return err; - } - - return OK; -} - -status_t ANetworkSession::stop() { - if (mThread == NULL) { - return INVALID_OPERATION; - } - - mThread->requestExit(); - interrupt(); - mThread->requestExitAndWait(); - - mThread.clear(); - - close(mPipeFd[0]); - close(mPipeFd[1]); - mPipeFd[0] = mPipeFd[1] = -1; - - return OK; -} - -status_t ANetworkSession::createRTSPClient( - const char *host, unsigned port, const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateRTSPClient, - NULL /* addr */, - 0 /* port */, - host, - port, - notify, - sessionID); -} - -status_t ANetworkSession::createRTSPServer( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID) { - return createClientOrServer( - kModeCreateRTSPServer, - &addr, - port, - NULL /* remoteHost */, - 0 /* remotePort */, - notify, - sessionID); -} - -status_t ANetworkSession::createUDPSession( - unsigned localPort, const sp ¬ify, int32_t *sessionID) { - return createUDPSession(localPort, NULL, 0, notify, sessionID); -} - -status_t ANetworkSession::createUDPSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateUDPSession, - NULL /* addr */, - localPort, - remoteHost, - remotePort, - notify, - sessionID); -} - -status_t ANetworkSession::createTCPDatagramSession( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID) { - return createClientOrServer( - kModeCreateTCPDatagramSessionPassive, - &addr, - port, - NULL /* remoteHost */, - 0 /* remotePort */, - notify, - sessionID); -} - -status_t ANetworkSession::createTCPDatagramSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - return createClientOrServer( - kModeCreateTCPDatagramSessionActive, - NULL /* addr */, - localPort, - remoteHost, - remotePort, - notify, - sessionID); -} - -status_t ANetworkSession::destroySession(int32_t sessionID) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - mSessions.removeItemsAt(index); - - interrupt(); - - return OK; -} - -// static -status_t ANetworkSession::MakeSocketNonBlocking(int s) { - int flags = fcntl(s, F_GETFL, 0); - if (flags < 0) { - flags = 0; - } - - int res = fcntl(s, F_SETFL, flags | O_NONBLOCK); - if (res < 0) { - return -errno; - } - - return OK; -} - -status_t ANetworkSession::createClientOrServer( - Mode mode, - const struct in_addr *localAddr, - unsigned port, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID) { - Mutex::Autolock autoLock(mLock); - - *sessionID = 0; - status_t err = OK; - int s, res; - sp session; - - s = socket( - AF_INET, - (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM, - 0); - - if (s < 0) { - err = -errno; - goto bail; - } - - if (mode == kModeCreateRTSPServer - || mode == kModeCreateTCPDatagramSessionPassive) { - const int yes = 1; - res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } - - if (mode == kModeCreateUDPSession) { - int size = 256 * 1024; - - res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); - - if (res < 0) { - err = -errno; - goto bail2; - } - - res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } else if (mode == kModeCreateTCPDatagramSessionActive) { - int flag = 1; - res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); - - if (res < 0) { - err = -errno; - goto bail2; - } - - int tos = 224; // VOICE - res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - - if (res < 0) { - err = -errno; - goto bail2; - } - } - - err = MakeSocketNonBlocking(s); - - if (err != OK) { - goto bail2; - } - - struct sockaddr_in addr; - memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - addr.sin_family = AF_INET; - - if (mode == kModeCreateRTSPClient - || mode == kModeCreateTCPDatagramSessionActive) { - struct hostent *ent= gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - goto bail2; - } - - addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - addr.sin_port = htons(remotePort); - } else if (localAddr != NULL) { - addr.sin_addr = *localAddr; - addr.sin_port = htons(port); - } else { - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(port); - } - - if (mode == kModeCreateRTSPClient - || mode == kModeCreateTCPDatagramSessionActive) { - in_addr_t x = ntohl(addr.sin_addr.s_addr); - ALOGI("connecting socket %d to %d.%d.%d.%d:%d", - s, - (x >> 24), - (x >> 16) & 0xff, - (x >> 8) & 0xff, - x & 0xff, - ntohs(addr.sin_port)); - - res = connect(s, (const struct sockaddr *)&addr, sizeof(addr)); - - CHECK_LT(res, 0); - if (errno == EINPROGRESS) { - res = 0; - } - } else { - res = bind(s, (const struct sockaddr *)&addr, sizeof(addr)); - - if (res == 0) { - if (mode == kModeCreateRTSPServer - || mode == kModeCreateTCPDatagramSessionPassive) { - res = listen(s, 4); - } else { - CHECK_EQ(mode, kModeCreateUDPSession); - - if (remoteHost != NULL) { - struct sockaddr_in remoteAddr; - memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); - remoteAddr.sin_family = AF_INET; - remoteAddr.sin_port = htons(remotePort); - - struct hostent *ent= gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - goto bail2; - } - - remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - res = connect( - s, - (const struct sockaddr *)&remoteAddr, - sizeof(remoteAddr)); - } - } - } - } - - if (res < 0) { - err = -errno; - goto bail2; - } - - Session::State state; - switch (mode) { - case kModeCreateRTSPClient: - state = Session::CONNECTING; - break; - - case kModeCreateTCPDatagramSessionActive: - state = Session::CONNECTING; - break; - - case kModeCreateTCPDatagramSessionPassive: - state = Session::LISTENING_TCP_DGRAMS; - break; - - case kModeCreateRTSPServer: - state = Session::LISTENING_RTSP; - break; - - default: - CHECK_EQ(mode, kModeCreateUDPSession); - state = Session::DATAGRAM; - break; - } - - session = new Session( - mNextSessionID++, - state, - s, - notify); - - if (mode == kModeCreateTCPDatagramSessionActive) { - session->setMode(Session::MODE_DATAGRAM); - } else if (mode == kModeCreateRTSPClient) { - session->setMode(Session::MODE_RTSP); - } - - mSessions.add(session->sessionID(), session); - - interrupt(); - - *sessionID = session->sessionID(); - - goto bail; - -bail2: - close(s); - s = -1; - -bail: - return err; -} - -status_t ANetworkSession::connectUDPSession( - int32_t sessionID, const char *remoteHost, unsigned remotePort) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - const sp session = mSessions.valueAt(index); - int s = session->socket(); - - struct sockaddr_in remoteAddr; - memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero)); - remoteAddr.sin_family = AF_INET; - remoteAddr.sin_port = htons(remotePort); - - status_t err = OK; - struct hostent *ent = gethostbyname(remoteHost); - if (ent == NULL) { - err = -h_errno; - } else { - remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; - - int res = connect( - s, - (const struct sockaddr *)&remoteAddr, - sizeof(remoteAddr)); - - if (res < 0) { - err = -errno; - } - } - - return err; -} - -status_t ANetworkSession::sendRequest( - int32_t sessionID, const void *data, ssize_t size, - bool timeValid, int64_t timeUs) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - const sp session = mSessions.valueAt(index); - - status_t err = session->sendRequest(data, size, timeValid, timeUs); - - interrupt(); - - return err; -} - -status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) { - Mutex::Autolock autoLock(mLock); - - ssize_t index = mSessions.indexOfKey(sessionID); - - if (index < 0) { - return -ENOENT; - } - - const sp session = mSessions.valueAt(index); - return session->switchToWebSocketMode(); -} - -void ANetworkSession::interrupt() { - static const char dummy = 0; - - ssize_t n; - do { - n = write(mPipeFd[1], &dummy, 1); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - ALOGW("Error writing to pipe (%s)", strerror(errno)); - } -} - -void ANetworkSession::threadLoop() { - fd_set rs, ws; - FD_ZERO(&rs); - FD_ZERO(&ws); - - FD_SET(mPipeFd[0], &rs); - int maxFd = mPipeFd[0]; - - { - Mutex::Autolock autoLock(mLock); - - for (size_t i = 0; i < mSessions.size(); ++i) { - const sp &session = mSessions.valueAt(i); - - int s = session->socket(); - - if (s < 0) { - continue; - } - - if (session->wantsToRead()) { - FD_SET(s, &rs); - if (s > maxFd) { - maxFd = s; - } - } - - if (session->wantsToWrite()) { - FD_SET(s, &ws); - if (s > maxFd) { - maxFd = s; - } - } - } - } - - int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */); - - if (res == 0) { - return; - } - - if (res < 0) { - if (errno == EINTR) { - return; - } - - ALOGE("select failed w/ error %d (%s)", errno, strerror(errno)); - return; - } - - if (FD_ISSET(mPipeFd[0], &rs)) { - char c; - ssize_t n; - do { - n = read(mPipeFd[0], &c, 1); - } while (n < 0 && errno == EINTR); - - if (n < 0) { - ALOGW("Error reading from pipe (%s)", strerror(errno)); - } - - --res; - } - - { - Mutex::Autolock autoLock(mLock); - - List > sessionsToAdd; - - for (size_t i = mSessions.size(); res > 0 && i > 0;) { - i--; - const sp &session = mSessions.valueAt(i); - - int s = session->socket(); - - if (s < 0) { - continue; - } - - if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { - --res; - } - - if (FD_ISSET(s, &rs)) { - if (session->isRTSPServer() || session->isTCPDatagramServer()) { - struct sockaddr_in remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - int clientSocket = accept( - s, (struct sockaddr *)&remoteAddr, &remoteAddrLen); - - if (clientSocket >= 0) { - status_t err = MakeSocketNonBlocking(clientSocket); - - if (err != OK) { - ALOGE("Unable to make client socket non blocking, " - "failed w/ error %d (%s)", - err, strerror(-err)); - - close(clientSocket); - clientSocket = -1; - } else { - in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr); - - ALOGI("incoming connection from %d.%d.%d.%d:%d " - "(socket %d)", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff, - ntohs(remoteAddr.sin_port), - clientSocket); - - sp clientSession = - new Session( - mNextSessionID++, - Session::CONNECTED, - clientSocket, - session->getNotificationMessage()); - - clientSession->setMode( - session->isRTSPServer() - ? Session::MODE_RTSP - : Session::MODE_DATAGRAM); - - sessionsToAdd.push_back(clientSession); - } - } else { - ALOGE("accept returned error %d (%s)", - errno, strerror(errno)); - } - } else { - status_t err = session->readMore(); - if (err != OK) { - ALOGE("readMore on socket %d failed w/ error %d (%s)", - s, err, strerror(-err)); - } - } - } - - if (FD_ISSET(s, &ws)) { - status_t err = session->writeMore(); - if (err != OK) { - ALOGE("writeMore on socket %d failed w/ error %d (%s)", - s, err, strerror(-err)); - } - } - } - - while (!sessionsToAdd.empty()) { - sp session = *sessionsToAdd.begin(); - sessionsToAdd.erase(sessionsToAdd.begin()); - - mSessions.add(session->sessionID(), session); - - ALOGI("added clientSession %d", session->sessionID()); - } - } -} - -} // namespace android diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index 6b384c0182..5b7961d204 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -56,18 +56,15 @@ cc_library { "ABuffer.cpp", "ADebug.cpp", "AHandler.cpp", - "AHierarchicalStateMachine.cpp", "ALooper.cpp", "ALooperRoster.cpp", "AMessage.cpp", - "ANetworkSession.cpp", "AString.cpp", "AStringUtils.cpp", "ByteUtils.cpp", "ColorUtils.cpp", "MediaDefs.cpp", "MediaKeys.cpp", - "ParsedMessage.cpp", "avc_utils.cpp", "base64.cpp", "hexdump.cpp", diff --git a/media/libstagefright/foundation/ParsedMessage.cpp b/media/libstagefright/foundation/ParsedMessage.cpp deleted file mode 100644 index 049c9ad9f6..0000000000 --- a/media/libstagefright/foundation/ParsedMessage.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2012, 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 "ParsedMessage.h" - -#include -#include -#include -#include - -namespace android { - -// static -sp ParsedMessage::Parse( - const char *data, size_t size, bool noMoreData, size_t *length) { - sp msg = new ParsedMessage; - ssize_t res = msg->parse(data, size, noMoreData); - - if (res < 0) { - *length = 0; - return NULL; - } - - *length = res; - return msg; -} - -ParsedMessage::ParsedMessage() { -} - -ParsedMessage::~ParsedMessage() { -} - -bool ParsedMessage::findString(const char *name, AString *value) const { - AString key = name; - key.tolower(); - - ssize_t index = mDict.indexOfKey(key); - - if (index < 0) { - value->clear(); - - return false; - } - - *value = mDict.valueAt(index); - return true; -} - -bool ParsedMessage::findInt32(const char *name, int32_t *value) const { - AString stringValue; - - if (!findString(name, &stringValue)) { - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -const char *ParsedMessage::getContent() const { - return mContent.c_str(); -} - -ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) { - if (size == 0) { - return -1; - } - - ssize_t lastDictIndex = -1; - - size_t offset = 0; - bool headersComplete = false; - while (offset < size) { - size_t lineEndOffset = offset; - while (lineEndOffset + 1 < size - && (data[lineEndOffset] != '\r' - || data[lineEndOffset + 1] != '\n')) { - ++lineEndOffset; - } - - if (lineEndOffset + 1 >= size) { - return -1; - } - - AString line(&data[offset], lineEndOffset - offset); - - if (offset == 0) { - // Special handling for the request/status line. - - mDict.add(AString("_"), line); - offset = lineEndOffset + 2; - - continue; - } - - if (lineEndOffset == offset) { - // An empty line separates headers from body. - headersComplete = true; - offset += 2; - break; - } - - if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') { - // Support for folded header values. - - if (lastDictIndex >= 0) { - // Otherwise it's malformed since the first header line - // cannot continue anything... - - AString &value = mDict.editValueAt(lastDictIndex); - value.append(line); - } - - offset = lineEndOffset + 2; - continue; - } - - ssize_t colonPos = line.find(":"); - if (colonPos >= 0) { - AString key(line, 0, colonPos); - key.trim(); - key.tolower(); - - line.erase(0, colonPos + 1); - - lastDictIndex = mDict.add(key, line); - } - - offset = lineEndOffset + 2; - } - - if (!headersComplete && (!noMoreData || offset == 0)) { - // We either saw the empty line separating headers from body - // or we saw at least the status line and know that no more data - // is going to follow. - return -1; - } - - for (size_t i = 0; i < mDict.size(); ++i) { - mDict.editValueAt(i).trim(); - } - - int32_t contentLength; - if (!findInt32("content-length", &contentLength) || contentLength < 0) { - contentLength = 0; - } - - size_t totalLength = offset + contentLength; - - if (size < totalLength) { - return -1; - } - - mContent.setTo(&data[offset], contentLength); - - return totalLength; -} - -bool ParsedMessage::getRequestField(size_t index, AString *field) const { - AString line; - CHECK(findString("_", &line)); - - size_t prevOffset = 0; - size_t offset = 0; - for (size_t i = 0; i <= index; ++i) { - if (offset >= line.size()) { - return false; - } - - ssize_t spacePos = line.find(" ", offset); - - if (spacePos < 0) { - spacePos = line.size(); - } - - prevOffset = offset; - offset = spacePos + 1; - } - - field->setTo(line, prevOffset, offset - prevOffset - 1); - - return true; -} - -bool ParsedMessage::getStatusCode(int32_t *statusCode) const { - AString statusCodeString; - if (!getRequestField(1, &statusCodeString)) { - *statusCode = 0; - return false; - } - - char *end; - *statusCode = strtol(statusCodeString.c_str(), &end, 10); - - if (*end != '\0' || end == statusCodeString.c_str() - || (*statusCode) < 100 || (*statusCode) > 999) { - *statusCode = 0; - return false; - } - - return true; -} - -AString ParsedMessage::debugString() const { - AString line; - CHECK(findString("_", &line)); - - line.append("\n"); - - for (size_t i = 0; i < mDict.size(); ++i) { - const AString &key = mDict.keyAt(i); - const AString &value = mDict.valueAt(i); - - if (key == AString("_")) { - continue; - } - - line.append(key); - line.append(": "); - line.append(value); - line.append("\n"); - } - - line.append("\n"); - line.append(mContent); - - return line; -} - -// static -bool ParsedMessage::GetAttribute( - const char *s, const char *key, AString *value) { - value->clear(); - - size_t keyLen = strlen(key); - - for (;;) { - while (isspace(*s)) { - ++s; - } - - const char *colonPos = strchr(s, ';'); - - size_t len = - (colonPos == NULL) ? strlen(s) : colonPos - s; - - if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { - value->setTo(&s[keyLen + 1], len - keyLen - 1); - return true; - } - - if (colonPos == NULL) { - return false; - } - - s = colonPos + 1; - } -} - -// static -bool ParsedMessage::GetInt32Attribute( - const char *s, const char *key, int32_t *value) { - AString stringValue; - if (!GetAttribute(s, key, &stringValue)) { - *value = 0; - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -} // namespace android - diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h deleted file mode 100644 index fd3ebaaa28..0000000000 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2012, 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 A_NETWORK_SESSION_H_ - -#define A_NETWORK_SESSION_H_ - -#include -#include -#include -#include - -#include - -namespace android { - -struct AMessage; - -// Helper class to manage a number of live sockets (datagram and stream-based) -// on a single thread. Clients are notified about activity through AMessages. -struct ANetworkSession : public RefBase { - ANetworkSession(); - - status_t start(); - status_t stop(); - - status_t createRTSPClient( - const char *host, unsigned port, const sp ¬ify, - int32_t *sessionID); - - status_t createRTSPServer( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID); - - status_t createUDPSession( - unsigned localPort, const sp ¬ify, int32_t *sessionID); - - status_t createUDPSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - status_t connectUDPSession( - int32_t sessionID, const char *remoteHost, unsigned remotePort); - - // passive - status_t createTCPDatagramSession( - const struct in_addr &addr, unsigned port, - const sp ¬ify, int32_t *sessionID); - - // active - status_t createTCPDatagramSession( - unsigned localPort, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - status_t destroySession(int32_t sessionID); - - status_t sendRequest( - int32_t sessionID, const void *data, ssize_t size = -1, - bool timeValid = false, int64_t timeUs = -1ll); - - status_t switchToWebSocketMode(int32_t sessionID); - - enum NotificationReason { - kWhatError, - kWhatConnected, - kWhatClientConnected, - kWhatData, - kWhatDatagram, - kWhatBinaryData, - kWhatWebSocketMessage, - kWhatNetworkStall, - }; - -protected: - virtual ~ANetworkSession(); - -private: - struct NetworkThread; - struct Session; - - Mutex mLock; - sp mThread; - - int32_t mNextSessionID; - - int mPipeFd[2]; - - KeyedVector > mSessions; - - enum Mode { - kModeCreateUDPSession, - kModeCreateTCPDatagramSessionPassive, - kModeCreateTCPDatagramSessionActive, - kModeCreateRTSPServer, - kModeCreateRTSPClient, - }; - status_t createClientOrServer( - Mode mode, - const struct in_addr *addr, - unsigned port, - const char *remoteHost, - unsigned remotePort, - const sp ¬ify, - int32_t *sessionID); - - void threadLoop(); - void interrupt(); - - static status_t MakeSocketNonBlocking(int s); - - DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession); -}; - -} // namespace android - -#endif // A_NETWORK_SESSION_H_ diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h deleted file mode 100644 index 9d43a93319..0000000000 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012, 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 -#include -#include -#include - -namespace android { - -// Encapsulates an "HTTP/RTSP style" response, i.e. a status line, -// key/value pairs making up the headers and an optional body/content. -struct ParsedMessage : public RefBase { - static sp Parse( - const char *data, size_t size, bool noMoreData, size_t *length); - - bool findString(const char *name, AString *value) const; - bool findInt32(const char *name, int32_t *value) const; - - const char *getContent() const; - - bool getRequestField(size_t index, AString *field) const; - bool getStatusCode(int32_t *statusCode) const; - - AString debugString() const; - - static bool GetAttribute(const char *s, const char *key, AString *value); - - static bool GetInt32Attribute( - const char *s, const char *key, int32_t *value); - - -protected: - virtual ~ParsedMessage(); - -private: - KeyedVector mDict; - AString mContent; - - ParsedMessage(); - - ssize_t parse(const char *data, size_t size, bool noMoreData); - - DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage); -}; - -} // namespace android diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 1137cf180f..73f93d14f7 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h similarity index 100% rename from media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h rename to media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h -- GitLab From 0fc8f214ed433cf5e9d90e71988309809f8c536c Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Thu, 30 Aug 2018 15:46:58 -0700 Subject: [PATCH 0247/1530] Remove drm log spam test:compiles bug:77813983 Change-Id: Iae9c8d3a958cad74dd10395a5184a3b82d263200 --- drm/libmediadrm/DrmHal.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index cf08610173..ce9dc38c99 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -318,7 +318,6 @@ Vector> DrmHal::makeDrmFactories() { for (const auto &instance : registered) { auto factory = drm::V1_0::IDrmFactory::getService(instance); if (factory != NULL) { - ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str()); factories.push_back(factory); } } @@ -329,7 +328,6 @@ Vector> DrmHal::makeDrmFactories() { for (const auto &instance : registered) { auto factory = drm::V1_1::IDrmFactory::getService(instance); if (factory != NULL) { - ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str()); factories.push_back(factory); } } -- GitLab From fdcf28cbc7e2ba6318798888719cd407bd7f6620 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 31 Aug 2018 14:17:05 -0700 Subject: [PATCH 0248/1530] Remove kKeyNalLengthSize It was only used internally in MatroskaExtractor, so doesn't need to be exposed in MetaData. Test: build Change-Id: I078f441cd125d161b1cd22557675c7abfb64d7f2 --- media/extractors/mkv/MatroskaExtractor.cpp | 12 +++++++----- media/extractors/mkv/MatroskaExtractor.h | 1 + .../include/media/stagefright/MetaDataBase.h | 3 --- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index a387970dc8..3c555b4458 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -222,7 +222,8 @@ MatroskaSource::MatroskaSource( mExtractor->mTracks.itemAt(index).mTrackNum, index), mNALSizeLen(-1) { - MetaDataBase &meta = mExtractor->mTracks.editItemAt(index).mMeta; + MatroskaExtractor::TrackInfo &trackInfo = mExtractor->mTracks.editItemAt(index); + MetaDataBase &meta = trackInfo.mMeta; const char *mime; CHECK(meta.findCString(kKeyMIMEType, &mime)); @@ -235,9 +236,9 @@ MatroskaSource::MatroskaSource( uint32_t dummy; const uint8_t *avcc; size_t avccSize; - int32_t nalSizeLen = 0; - if (meta.findInt32(kKeyNalLengthSize, &nalSizeLen)) { - if (nalSizeLen >= 0 && nalSizeLen <= 4) { + int32_t nalSizeLen = trackInfo.mNalLengthSize; + if (nalSizeLen >= 0) { + if (nalSizeLen <= 4) { mNALSizeLen = nalSizeLen; } } else if (meta.findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize) @@ -1227,7 +1228,7 @@ status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) { } // Override the synthesized nal length size, which is arbitrary - trackInfo->mMeta.setInt32(kKeyNalLengthSize, 0); + trackInfo->mNalLengthSize = 0; return OK; } @@ -1344,6 +1345,7 @@ status_t MatroskaExtractor::initTrackInfo( trackInfo->mEncrypted = false; trackInfo->mHeader = NULL; trackInfo->mHeaderLen = 0; + trackInfo->mNalLengthSize = -1; for(size_t i = 0; i < track->GetContentEncodingCount(); i++) { const mkvparser::ContentEncoding *encoding = track->GetContentEncodingByIndex(i); diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 2c6ca850b2..854c36636e 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -69,6 +69,7 @@ private: // in ~MatroskaExtractor. unsigned char *mHeader; size_t mHeaderLen; + int32_t mNalLengthSize; const mkvparser::Track* getTrack() const; const mkvparser::CuePoint::TrackPosition *find(long long timeNs) const; diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h index 2e9aedecc4..aa1d69c7f2 100644 --- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h +++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h @@ -199,9 +199,6 @@ enum { // MPEG user data offsets kKeyMpegUserData = 'mpud', // size_t[] - // Size of NALU length in mkv/mp4 - kKeyNalLengthSize = 'nals', // int32_t - // HDR related kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo -- GitLab From f7fa2009b579aabf8275c8c96d3e2918b18b12bf Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Fri, 31 Aug 2018 14:24:18 -0700 Subject: [PATCH 0249/1530] Camera: Do not treat non-increasing timestamp as fatal Certain devices generate duplicate timestamps for cases like video recording. So do not treat non-increasing timestamps as fatal for now. Test: Failed camera CTS on Pixel device Bug: 113670946 Change-Id: I3708f46111edf031f48f460e4efce9f9c46f174e --- .../camera/libcameraservice/device3/Camera3OutputStream.cpp | 3 +-- services/camera/libcameraservice/device3/Camera3Stream.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index ec3f35f060..84767110c0 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -269,8 +269,7 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( } if (timestamp == 0) { - ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); - return BAD_VALUE; + ALOGW("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); } /* Certain consumers (such as AudioSource or HardwareComposer) use diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 53ff9fe080..b208d9ffec 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -663,9 +663,8 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, removeOutstandingBuffer(buffer); if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) { - ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64, + ALOGW("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64, __FUNCTION__, mId, timestamp, mLastTimestamp); - return BAD_VALUE; } mLastTimestamp = timestamp; -- GitLab From cf3d82c34b8a737408113dddad341b8c98251a8e Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Tue, 4 Sep 2018 15:44:45 -0700 Subject: [PATCH 0250/1530] NBLog: refactor Writer API for extensibility No new functionality was added, and no existing functionality was removed. Event types have been renamed and reorganized according to functionality. A compile-time mapping from Event to C++ data type allows for a generic Event log() template function to avoid the need to add a new logging function when a new Event is added to enum NBLog::Event. Most LockedWriter functions were deleted because they did not involve accessing shared variables. Only the functions that accessed shared variables were kept in LockedWriter. Test: dumpsys media.log -r Bug: 68148948 Change-Id: I8a27f1690795cdb0b84e92033b7cbf4588ba175e --- media/libnblog/NBLog.cpp | 342 ++++++++------------- media/libnblog/include/media/nblog/NBLog.h | 214 ++++++++----- services/audioflinger/FastThread.cpp | 2 +- services/audioflinger/TypedLogger.h | 10 +- 4 files changed, 263 insertions(+), 305 deletions(-) diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index f1d7523974..f73e2ae2e1 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -68,7 +68,7 @@ std::unique_ptr NBLog::AbstractEntry::buildEntry(const uin } const uint8_t type = EntryIterator(ptr)->type; switch (type) { - case EVENT_START_FMT: + case EVENT_FMT_START: return std::make_unique(FormatEntry(ptr)); case EVENT_AUDIO_STATE: case EVENT_HISTOGRAM_ENTRY_TS: @@ -107,7 +107,7 @@ NBLog::EntryIterator NBLog::FormatEntry::args() const ++it; // skip timestamp ++it; // skip hash // Skip author if present - if (it->type == EVENT_AUTHOR) { + if (it->type == EVENT_FMT_AUTHOR) { ++it; } return it; @@ -138,7 +138,7 @@ int NBLog::FormatEntry::author() const ++it; // skip timestamp ++it; // skip hash // if there is an author entry, return it, return -1 otherwise - return it->type == EVENT_AUTHOR ? it.payload() : -1; + return it->type == EVENT_FMT_AUTHOR ? it.payload() : -1; } NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( @@ -151,14 +151,14 @@ NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( // insert author entry size_t authorEntrySize = Entry::kOverhead + sizeof(author); uint8_t authorEntry[authorEntrySize]; - authorEntry[offsetof(entry, type)] = EVENT_AUTHOR; + authorEntry[offsetof(entry, type)] = EVENT_FMT_AUTHOR; authorEntry[offsetof(entry, length)] = authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] = sizeof(author); *(int*) (&authorEntry[offsetof(entry, data)]) = author; dst->write(authorEntry, authorEntrySize); // copy rest of entries - while ((++it)->type != EVENT_END_FMT) { + while ((++it)->type != EVENT_FMT_END) { it.copyTo(dst); } it.copyTo(dst); @@ -394,44 +394,10 @@ void NBLog::Writer::logTimestamp() if (!mEnabled) { return; } - int64_t ts = get_monotonic_ns(); - if (ts > 0) { + struct timespec ts; + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { log(EVENT_TIMESTAMP, &ts, sizeof(ts)); - } else { - ALOGE("Failed to get timestamp"); - } -} - -void NBLog::Writer::logTimestamp(const int64_t ts) -{ - if (!mEnabled) { - return; - } - log(EVENT_TIMESTAMP, &ts, sizeof(ts)); -} - -void NBLog::Writer::logInteger(const int x) -{ - if (!mEnabled) { - return; - } - log(EVENT_INTEGER, &x, sizeof(x)); -} - -void NBLog::Writer::logFloat(const float x) -{ - if (!mEnabled) { - return; - } - log(EVENT_FLOAT, &x, sizeof(x)); -} - -void NBLog::Writer::logPID() -{ - if (!mEnabled) { - return; } - log(EVENT_PID, mPidTag, mPidTagSize); } void NBLog::Writer::logStart(const char *fmt) @@ -443,24 +409,20 @@ void NBLog::Writer::logStart(const char *fmt) if (length > Entry::kMaxLength) { length = Entry::kMaxLength; } - log(EVENT_START_FMT, fmt, length); + log(EVENT_FMT_START, fmt, length); } -void NBLog::Writer::logEnd() +void NBLog::Writer::logTimestampFormat() { if (!mEnabled) { return; } - Entry entry = Entry(EVENT_END_FMT, NULL, 0); - log(entry, true); -} - -void NBLog::Writer::logHash(log_hash_t hash) -{ - if (!mEnabled) { - return; + int64_t ts = get_monotonic_ns(); + if (ts > 0) { + log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts)); + } else { + ALOGE("Failed to get timestamp"); } - log(EVENT_HASH, &hash, sizeof(hash)); } void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) @@ -478,22 +440,6 @@ void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) } } -void NBLog::Writer::logLatency(double latencyMs) -{ - if (!mEnabled) { - return; - } - log(EVENT_LATENCY, &latencyMs, sizeof(latencyMs)); -} - -void NBLog::Writer::logMonotonicCycleTime(uint32_t monotonicNs) -{ - if (!mEnabled) { - return; - } - log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(monotonicNs)); -} - void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) { if (!mEnabled) { @@ -512,11 +458,13 @@ void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) } Writer::logStart(fmt); int i; - double f; + double d; + float f; char* s; + size_t length; int64_t t; - Writer::logTimestamp(); - Writer::logHash(hash); + Writer::logTimestampFormat(); + log(EVENT_FMT_HASH, &hash, sizeof(hash)); for (const char *p = fmt; *p != '\0'; p++) { // TODO: implement more complex formatting such as %.3f if (*p != '%') { @@ -525,26 +473,31 @@ void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) switch(*++p) { case 's': // string s = va_arg(argp, char *); - Writer::log(s); + length = strlen(s); + if (length > Entry::kMaxLength) { + length = Entry::kMaxLength; + } + log(EVENT_FMT_STRING, s, length); break; case 't': // timestamp t = va_arg(argp, int64_t); - Writer::logTimestamp(t); + log(EVENT_FMT_TIMESTAMP, &t, sizeof(t)); break; case 'd': // integer i = va_arg(argp, int); - Writer::logInteger(i); + log(EVENT_FMT_INTEGER, &i, sizeof(i)); break; case 'f': // float - f = va_arg(argp, double); // float arguments are promoted to double in vararg lists - Writer::logFloat((float)f); + d = va_arg(argp, double); // float arguments are promoted to double in vararg lists + f = (float)d; + log(EVENT_FMT_FLOAT, &f, sizeof(f)); break; case 'p': // pid - Writer::logPID(); + log(EVENT_FMT_PID, mPidTag, mPidTagSize); break; // the "%\0" case finishes parsing @@ -560,7 +513,8 @@ void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) break; } } - Writer::logEnd(); + Entry etr(EVENT_FMT_END, nullptr, 0); + log(etr, true); } void NBLog::Writer::log(Event event, const void *data, size_t length) @@ -630,79 +584,6 @@ NBLog::LockedWriter::LockedWriter(void *shared, size_t size) { } -void NBLog::LockedWriter::log(const char *string) -{ - Mutex::Autolock _l(mLock); - Writer::log(string); -} - -void NBLog::LockedWriter::logf(const char *fmt, ...) -{ - // FIXME should not take the lock until after formatting is done - Mutex::Autolock _l(mLock); - va_list ap; - va_start(ap, fmt); - Writer::logvf(fmt, ap); - va_end(ap); -} - -void NBLog::LockedWriter::logvf(const char *fmt, va_list ap) -{ - // FIXME should not take the lock until after formatting is done - Mutex::Autolock _l(mLock); - Writer::logvf(fmt, ap); -} - -void NBLog::LockedWriter::logTimestamp() -{ - // FIXME should not take the lock until after the clock_gettime() syscall - Mutex::Autolock _l(mLock); - Writer::logTimestamp(); -} - -void NBLog::LockedWriter::logTimestamp(const int64_t ts) -{ - Mutex::Autolock _l(mLock); - Writer::logTimestamp(ts); -} - -void NBLog::LockedWriter::logInteger(const int x) -{ - Mutex::Autolock _l(mLock); - Writer::logInteger(x); -} - -void NBLog::LockedWriter::logFloat(const float x) -{ - Mutex::Autolock _l(mLock); - Writer::logFloat(x); -} - -void NBLog::LockedWriter::logPID() -{ - Mutex::Autolock _l(mLock); - Writer::logPID(); -} - -void NBLog::LockedWriter::logStart(const char *fmt) -{ - Mutex::Autolock _l(mLock); - Writer::logStart(fmt); -} - - -void NBLog::LockedWriter::logEnd() -{ - Mutex::Autolock _l(mLock); - Writer::logEnd(); -} - -void NBLog::LockedWriter::logHash(log_hash_t hash) -{ - Mutex::Autolock _l(mLock); - Writer::logHash(hash); -} - bool NBLog::LockedWriter::isEnabled() const { Mutex::Autolock _l(mLock); @@ -715,21 +596,47 @@ bool NBLog::LockedWriter::setEnabled(bool enabled) return Writer::setEnabled(enabled); } +void NBLog::LockedWriter::log(const Entry &entry, bool trusted) { + Mutex::Autolock _l(mLock); + Writer::log(entry, trusted); +} + // --------------------------------------------------------------------------- -const std::unordered_set NBLog::Reader::startingTypes { - NBLog::Event::EVENT_START_FMT, - NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, - NBLog::Event::EVENT_AUDIO_STATE, - NBLog::Event::EVENT_LATENCY, - NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME +// We make a set of the invalid types rather than the valid types when aligning +// Snapshot EntryIterators to valid entries during log corruption checking. +// This is done in order to avoid the maintenance overhead of adding a new NBLog::Event +// type to the two sets below whenever a new NBLog::Event type is created, as it is +// very likely that new types added will be valid types. +// Currently, invalidBeginTypes and invalidEndTypes are used to handle the special +// case of a Format Entry, which consists of a variable number of simple log entries. +// If a new NBLog::Event is added that consists of a variable number of simple log entries, +// then these sets need to be updated. + +// We want the beginning of a Snapshot to point to an entry that is not in +// the middle of a formatted entry and not an FMT_END. +const std::unordered_set NBLog::Reader::invalidBeginTypes { + NBLog::Event::EVENT_FMT_TIMESTAMP, + NBLog::Event::EVENT_FMT_HASH, + NBLog::Event::EVENT_FMT_STRING, + NBLog::Event::EVENT_FMT_INTEGER, + NBLog::Event::EVENT_FMT_FLOAT, + NBLog::Event::EVENT_FMT_PID, + NBLog::Event::EVENT_FMT_AUTHOR, + NBLog::Event::EVENT_FMT_END }; -const std::unordered_set NBLog::Reader::endingTypes { - NBLog::Event::EVENT_END_FMT, - NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, - NBLog::Event::EVENT_AUDIO_STATE, - NBLog::Event::EVENT_LATENCY, - NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME + +// We want the end of a Snapshot to point to an entry that is not in +// the middle of a formatted entry and not a FMT_START. +const std::unordered_set NBLog::Reader::invalidEndTypes { + NBLog::Event::EVENT_FMT_START, + NBLog::Event::EVENT_FMT_TIMESTAMP, + NBLog::Event::EVENT_FMT_HASH, + NBLog::Event::EVENT_FMT_STRING, + NBLog::Event::EVENT_FMT_INTEGER, + NBLog::Event::EVENT_FMT_FLOAT, + NBLog::Event::EVENT_FMT_PID, + NBLog::Event::EVENT_FMT_AUTHOR }; NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name) @@ -754,17 +661,22 @@ NBLog::Reader::~Reader() delete mFifo; } -const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, - const std::unordered_set &types) { +const uint8_t *NBLog::Reader::findLastValidEntry(const uint8_t *front, const uint8_t *back, + const std::unordered_set &invalidTypes) { + if (front == nullptr || back == nullptr) { + return nullptr; + } while (back + Entry::kPreviousLengthOffset >= front) { const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead; - if (prev < front || prev + prev[offsetof(entry, length)] + - Entry::kOverhead != back) { - + const Event type = (const Event)prev[offsetof(entry, type)]; + if (prev < front + || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back + || type <= NBLog::EVENT_RESERVED || type >= NBLog::EVENT_UPPER_BOUND) { // prev points to an out of limits or inconsistent entry return nullptr; } - if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) { + // if invalidTypes does not contain the type, then the type is valid. + if (invalidTypes.find(type) == invalidTypes.end()) { return prev; } back = prev; @@ -778,7 +690,7 @@ const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const u std::unique_ptr NBLog::Reader::getSnapshot() { if (mFifoReader == NULL) { - return std::make_unique(); + return std::unique_ptr(new Snapshot()); } // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the @@ -808,9 +720,20 @@ std::unique_ptr NBLog::Reader::getSnapshot() if (availToRead <= 0) { ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str()); - return std::make_unique(); + return std::unique_ptr(new Snapshot()); } + // Change to #if 1 for debugging. This statement is useful for checking buffer fullness levels + // (as seen by reader) and how much data was lost. If you find that the fullness level is + // getting close to full, or that data loss is happening to often, then you should + // probably try some of the following: + // - log less data + // - log less often + // - increase the initial shared memory allocation for the buffer +#if 0 + ALOGD("getSnapshot name=%s, availToRead=%zd, capacity=%zu, fullness=%.3f, lost=%zu", + name().c_str(), availToRead, capacity, (double)availToRead / (double)capacity, lost); +#endif std::unique_ptr snapshot(new Snapshot(availToRead)); memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength); if (iovec[1].mLength > 0) { @@ -821,28 +744,28 @@ std::unique_ptr NBLog::Reader::getSnapshot() // Handle corrupted buffer // Potentially, a buffer has corrupted data on both beginning (due to overflow) and end // (due to incomplete format entry). But even if the end format entry is incomplete, - // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards. + // it ends in a complete entry (which is not an FMT_END). So is safe to traverse backwards. // TODO: handle client corruption (in the middle of a buffer) const uint8_t *back = snapshot->mData + availToRead; const uint8_t *front = snapshot->mData; - // Find last END_FMT. is sitting on an entry which might be the middle of a FormatEntry. - // We go backwards until we find an EVENT_END_FMT. - const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes); + // Find last FMT_END. is sitting on an entry which might be the middle of a FormatEntry. + // We go backwards until we find an EVENT_FMT_END. + const uint8_t *lastEnd = findLastValidEntry(front, back, invalidEndTypes); if (lastEnd == nullptr) { snapshot->mEnd = snapshot->mBegin = EntryIterator(front); } else { - // end of snapshot points to after last END_FMT entry + // end of snapshot points to after last FMT_END entry snapshot->mEnd = EntryIterator(lastEnd).next(); - // find first START_FMT + // find first FMT_START const uint8_t *firstStart = nullptr; const uint8_t *firstStartTmp = snapshot->mEnd; - while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes)) + while ((firstStartTmp = findLastValidEntry(front, firstStartTmp, invalidBeginTypes)) != nullptr) { firstStart = firstStartTmp; } - // firstStart is null if no START_FMT entry was found before lastEnd + // firstStart is null if no FMT_START entry was found before lastEnd if (firstStart == nullptr) { snapshot->mBegin = snapshot->mEnd; } else { @@ -861,43 +784,30 @@ std::unique_ptr NBLog::Reader::getSnapshot() // writes the data to a map of class PerformanceAnalysis, based on their thread ID. void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author) { - for (const entry &etr : snapshot) { - switch (etr.type) { + // We don't do "auto it" because it reduces readability in this case. + for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) { + switch (it->type) { case EVENT_HISTOGRAM_ENTRY_TS: { - HistTsEntry *data = (HistTsEntry *) (etr.data); - // TODO This memcpies are here to avoid unaligned memory access crash. - // There's probably a more efficient way to do it - log_hash_t hash; - memcpy(&hash, &(data->hash), sizeof(hash)); - int64_t ts; - memcpy(&ts, &data->ts, sizeof(ts)); + HistTsEntry payload = it.payload(); // TODO: hash for histogram ts and audio state need to match // and correspond to audio production source file location - mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(ts); + mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts); } break; case EVENT_AUDIO_STATE: { - HistTsEntry *data = (HistTsEntry *) (etr.data); - // TODO This memcpies are here to avoid unaligned memory access crash. - // There's probably a more efficient way to do it - log_hash_t hash; - memcpy(&hash, &(data->hash), sizeof(hash)); mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange(); } break; case EVENT_LATENCY: { - double latencyMs; - memcpy(&latencyMs, etr.data, sizeof(latencyMs)); + double latencyMs = it.payload(); mPerformanceData.addLatencyEntry(author, latencyMs); } break; - case EVENT_MONOTONIC_CYCLE_TIME: { - uint32_t monotonicNs; - memcpy(&monotonicNs, etr.data, sizeof(monotonicNs)); + case EVENT_WORK_TIME: { + uint64_t monotonicNs = it.payload(); const double monotonicMs = monotonicNs * 1e-6; mPerformanceData.addCycleTimeEntry(author, monotonicMs); } break; - case EVENT_END_FMT: case EVENT_RESERVED: case EVENT_UPPER_BOUND: - ALOGW("warning: unexpected event %d", etr.type); + ALOGW("warning: unexpected event %d", it->type); default: break; } @@ -940,22 +850,20 @@ void NBLog::DumpReader::dump(int fd, size_t indent) String8 timestamp, body; // TODO all logged types should have a printable format. - for (auto it = snapshot->begin(); it != snapshot->end(); ++it) { + for (EntryIterator it = snapshot->begin(); it != snapshot->end(); ++it) { switch (it->type) { - case EVENT_START_FMT: + case EVENT_FMT_START: it = handleFormat(FormatEntry(it), ×tamp, &body); break; - case EVENT_MONOTONIC_CYCLE_TIME: { - uint32_t monotonicNs; - memcpy(&monotonicNs, it->data, sizeof(monotonicNs)); - body.appendFormat("Thread cycle: %u ns", monotonicNs); + case EVENT_WORK_TIME: { + uint64_t monotonicNs = it.payload(); + body.appendFormat("Thread cycle: %lu ns", (unsigned long)monotonicNs); } break; case EVENT_LATENCY: { - double latencyMs; - memcpy(&latencyMs, it->data, sizeof(latencyMs)); + double latencyMs = it.payload(); body.appendFormat("latency: %.3f ms", latencyMs); } break; - case EVENT_END_FMT: + case EVENT_FMT_END: case EVENT_RESERVED: case EVENT_UPPER_BOUND: body.appendFormat("warning: unexpected event %d", it->type); @@ -1078,7 +986,7 @@ NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry // TODO check length for event type is correct - if (event == EVENT_END_FMT) { + if (event == EVENT_FMT_END) { break; } @@ -1087,31 +995,31 @@ NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry switch(fmt[fmt_offset]) { case 's': // string - ALOGW_IF(event != EVENT_STRING, + ALOGW_IF(event != EVENT_FMT_STRING, "NBLog Reader incompatible event for string specifier: %d", event); body->append((const char*) datum, length); break; case 't': // timestamp - ALOGW_IF(event != EVENT_TIMESTAMP, + ALOGW_IF(event != EVENT_FMT_TIMESTAMP, "NBLog Reader incompatible event for timestamp specifier: %d", event); appendTimestamp(body, datum); break; case 'd': // integer - ALOGW_IF(event != EVENT_INTEGER, + ALOGW_IF(event != EVENT_FMT_INTEGER, "NBLog Reader incompatible event for integer specifier: %d", event); appendInt(body, datum); break; case 'f': // float - ALOGW_IF(event != EVENT_FLOAT, + ALOGW_IF(event != EVENT_FMT_FLOAT, "NBLog Reader incompatible event for float specifier: %d", event); appendFloat(body, datum); break; case 'p': // pid - ALOGW_IF(event != EVENT_PID, + ALOGW_IF(event != EVENT_FMT_PID, "NBLog Reader incompatible event for pid specifier: %d", event); appendPID(body, datum, length); break; @@ -1121,7 +1029,7 @@ NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry } ++arg; } - ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type); + ALOGW_IF(arg->type != EVENT_FMT_END, "Expected end of format, got %d", arg->type); return arg; } diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index 763d7433d9..561e8c7d41 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -20,6 +20,7 @@ #define ANDROID_MEDIA_NBLOG_H #include +#include #include #include @@ -45,33 +46,58 @@ public: class Writer; class Reader; + // TODO have a comment somewhere explaining the whole process for adding a new EVENT_ + + // NBLog Event types. The Events are named to provide contextual meaning for what is logged. + // If adding a new standalone Event here, update the event-to-type mapping by adding a + // MAP_EVENT_TO_TYPE statement below. enum Event : uint8_t { EVENT_RESERVED, EVENT_STRING, // ASCII string, not NUL-terminated // TODO: make timestamp optional EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC) - EVENT_INTEGER, // integer value entry - EVENT_FLOAT, // floating point value entry - EVENT_PID, // process ID and process name - EVENT_AUTHOR, // author index (present in merged logs) tracks entry's - // original log - EVENT_START_FMT, // logFormat start event: entry includes format string, + + // Types for Format Entry, i.e. formatted entry + EVENT_FMT_START, // logFormat start event: entry includes format string, // following entries contain format arguments - EVENT_HASH, // unique HASH of log origin, originates from hash of file name + // format arguments + EVENT_FMT_TIMESTAMP, // timestamp value entry + EVENT_FMT_HASH, // unique HASH of log origin, originates from hash of file name // and line number + EVENT_FMT_STRING, // string value entry + EVENT_FMT_INTEGER, // integer value entry + EVENT_FMT_FLOAT, // floating point value entry + EVENT_FMT_PID, // process ID and process name + EVENT_FMT_AUTHOR, // author index (present in merged logs) tracks entry's + // original log + // end of format arguments + EVENT_FMT_END, // end of logFormat argument list + + // Types for wakeup timestamp histograms EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call - EVENT_END_FMT, // end of logFormat argument list // Types representing audio performance metrics - EVENT_LATENCY, // TODO classify specifically what this is - EVENT_CPU_FREQUENCY, // instantaneous CPU frequency in kHz - EVENT_MONOTONIC_CYCLE_TIME, // thread per-cycle monotonic time - EVENT_CPU_CYCLE_TIME, // thread per-cycle cpu time + EVENT_LATENCY, // difference between frames presented by HAL and frames + // written to HAL output sink, divided by sample rate. + EVENT_WORK_TIME, // the time a thread takes to do work, e.g. read, write, etc. EVENT_UPPER_BOUND, // to check for invalid events }; + template struct get_mapped; +#define MAP_EVENT_TO_TYPE(E, T) \ + template<> struct get_mapped { \ + static_assert(std::is_trivially_copyable::value \ + && !std::is_pointer::value, \ + "NBLog::Event must map to trivially copyable, non-pointer type."); \ + typedef T type; \ + } + + // Maps an NBLog Event type to a C++ POD type. + MAP_EVENT_TO_TYPE(EVENT_LATENCY, double); + MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, uint64_t); + private: // --------------------------------------------------------------------------- @@ -119,10 +145,24 @@ private: void copyTo(std::unique_ptr &dst) const; void copyData(uint8_t *dst) const; + // memcpy preferred to reinterpret_cast to avoid potentially unsupported + // unaligned memory access. +#if 0 template inline const T& payload() { return *reinterpret_cast(mPtr + offsetof(entry, data)); } +#else + template + inline T payload() { + static_assert(std::is_trivially_copyable::value + && !std::is_pointer::value, + "NBLog::EntryIterator payload must be trivially copyable, non-pointer type."); + T payload; + memcpy(&payload, mPtr + offsetof(entry, data), sizeof(payload)); + return payload; + } +#endif inline operator const uint8_t*() const { return mPtr; @@ -169,14 +209,14 @@ private: // API for handling format entry operations // a formatted entry has the following structure: - // * START_FMT entry, containing the format string + // * FMT_START entry, containing the format string // * TIMESTAMP entry // * HASH entry // * author entry of the thread that generated it (optional, present in merged log) // * format arg1 // * format arg2 // * ... - // * END_FMT entry + // * FMT_END entry class FormatEntry : public AbstractEntry { public: // explicit FormatEntry(const EntryIterator &it); @@ -262,6 +302,7 @@ private: offsetof(ending, length); }; + // TODO move these somewhere else struct HistTsEntry { log_hash_t hash; int64_t ts; @@ -319,6 +360,8 @@ public: #endif }; + // --------------------------------------------------------------------------- + // NBLog Writer API // --------------------------------------------------------------------------- // Writer is thread-safe with respect to Reader, but not with respect to multiple threads @@ -335,24 +378,22 @@ public: virtual ~Writer(); // FIXME needs comments, and some should be private - virtual void log(const char *string); - virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - virtual void logvf(const char *fmt, va_list ap); - virtual void logTimestamp(); - virtual void logTimestamp(const int64_t ts); - virtual void logInteger(const int x); - virtual void logFloat(const float x); - virtual void logPID(); - virtual void logStart(const char *fmt); - virtual void logEnd(); - virtual void logHash(log_hash_t hash); - // The functions below are not in LockedWriter yet. - virtual void logFormat(const char *fmt, log_hash_t hash, ...); - virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap); - virtual void logEventHistTs(Event event, log_hash_t hash); - virtual void logLatency(double latencyMs); - virtual void logMonotonicCycleTime(uint32_t monotonicNs); - // End of functions that are not in LockedWriter yet. + void log(const char *string); + void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + void logTimestamp(); + void logFormat(const char *fmt, log_hash_t hash, ...); + void logEventHistTs(Event event, log_hash_t hash); + + // Log data related to Event E. See the event-to-type mapping for the type of data + // corresponding to the event. For example, if you see a mapping statement: + // MAP_TYPE_TO_EVENT(E, T); + // then the usage of this method would be: + // T data = doComputation(); + // tlNBLogWriter->log(data); + template + void log(typename get_mapped::type data) { + log(E, &data, sizeof(data)); + } virtual bool isEnabled() const; @@ -363,12 +404,24 @@ public: sp getIMemory() const { return mIMemory; } + // Public logging function implementations should always use one of the + // two log() function calls below to write to shared memory. + protected: + // Writes a single Entry to the FIFO if the writer is enabled. + // This is protected and virtual because LockedWriter uses a lock to protect + // writing to the FIFO before writing to this function. + virtual void log(const Entry &entry, bool trusted = false); + private: // 0 <= length <= kMaxLength - // writes a single Entry to the FIFO + // Log a single Entry with corresponding event, data, and length. void log(Event event, const void *data, size_t length); - // checks validity of an event before calling log above this one - void log(const Entry &entry, bool trusted = false); + + void logvf(const char *fmt, va_list ap); + // helper functions for logging parts of a formatted entry + void logStart(const char *fmt); + void logTimestampFormat(); + void logVFormat(const char *fmt, log_hash_t hash, va_list ap); Shared* const mShared; // raw pointer to shared memory sp mIMemory; // ref-counted version, initialized in constructor @@ -393,54 +446,20 @@ public: LockedWriter(); LockedWriter(void *shared, size_t size); - virtual void log(const char *string); - virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - virtual void logvf(const char *fmt, va_list ap); - virtual void logTimestamp(); - virtual void logTimestamp(const int64_t ts); - virtual void logInteger(const int x); - virtual void logFloat(const float x); - virtual void logPID(); - virtual void logStart(const char *fmt); - virtual void logEnd(); - virtual void logHash(log_hash_t hash); - - virtual bool isEnabled() const; - virtual bool setEnabled(bool enabled); + bool isEnabled() const override; + bool setEnabled(bool enabled) override; private: + // Lock needs to be obtained before writing to FIFO. + void log(const Entry &entry, bool trusted = false) override; mutable Mutex mLock; }; + // --------------------------------------------------------------------------- + // NBLog Reader API // --------------------------------------------------------------------------- - // A snapshot of a readers buffer - // This is raw data. No analysis has been done on it - class Snapshot { - public: - Snapshot() = default; - - explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} - - ~Snapshot() { delete[] mData; } - - // amount of data lost (given by audio_utils_fifo_reader) - size_t lost() const { return mLost; } - - // iterator to beginning of readable segment of snapshot - // data between begin and end has valid entries - EntryIterator begin() const { return mBegin; } - - // iterator to end of readable segment of snapshot - EntryIterator end() const { return mEnd; } - - private: - friend class Reader; - uint8_t * const mData{}; - size_t mLost{0}; - EntryIterator mBegin; - EntryIterator mEnd; - }; + class Snapshot; // Forward declaration needed for Reader::getSnapshot() class Reader : public RefBase { public: @@ -456,10 +475,12 @@ public: const std::string &name() const { return mName; } private: + // Amount of tries for reader to catch up with writer in getSnapshot(). static constexpr int kMaxObtainTries = 3; - // startingTypes and endingTypes are used to check for log corruption. - static const std::unordered_set startingTypes; - static const std::unordered_set endingTypes; + // invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and + // Snapshot::end() EntryIterators to valid entries. + static const std::unordered_set invalidBeginTypes; + static const std::unordered_set invalidEndTypes; // declared as const because audio_utils_fifo() constructor sp mIMemory; // ref-counted version, assigned only in constructor @@ -470,12 +491,39 @@ public: audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO, // non-NULL unless constructor fails - // Searches for the last entry of type in the range [front, back) + // Searches for the last valid entry in the range [front, back) // back has to be entry-aligned. Returns nullptr if none enconuntered. - static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, - const std::unordered_set &types); + static const uint8_t *findLastValidEntry(const uint8_t *front, const uint8_t *back, + const std::unordered_set &invalidTypes); + }; + + // A snapshot of a readers buffer + // This is raw data. No analysis has been done on it + class Snapshot { + public: + ~Snapshot() { delete[] mData; } + + // amount of data lost (given by audio_utils_fifo_reader) + size_t lost() const { return mLost; } + + // iterator to beginning of readable segment of snapshot + // data between begin and end has valid entries + EntryIterator begin() const { return mBegin; } + + // iterator to end of readable segment of snapshot + EntryIterator end() const { return mEnd; } + + private: + Snapshot() = default; + explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {} + friend std::unique_ptr Reader::getSnapshot(); + uint8_t * const mData = nullptr; + size_t mLost = 0; + EntryIterator mBegin; + EntryIterator mEnd; }; + // TODO move this to MediaLogService? class DumpReader : public Reader { public: DumpReader(const void *shared, size_t size, const std::string &name) @@ -491,7 +539,7 @@ public: static void appendFloat(String8 *body, const void *data); static void appendPID(String8 *body, const void *data, size_t length); static void appendTimestamp(String8 *body, const void *data); - //static size_t fmtEntryLength(const uint8_t *data); // TODO Eric remove if not used + // The bufferDump functions are used for debugging only. static String8 bufferDump(const uint8_t *buffer, size_t size); static String8 bufferDump(const EntryIterator &it); }; diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index e5870269d0..6f223dff14 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -339,7 +339,7 @@ bool FastThread::threadLoop() // these stores #1, #2, #3 are not atomic with respect to each other, // or with respect to store #4 below mDumpState->mMonotonicNs[i] = monotonicNs; - LOG_MONOTONIC_CYCLE_TIME(monotonicNs); + LOG_WORK_TIME(monotonicNs); mDumpState->mLoadNs[i] = loadNs; #ifdef CPU_FREQUENCY_STATISTICS mDumpState->mCpukHz[i] = kHz; diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h index 736ac603df..6677470450 100644 --- a/services/audioflinger/TypedLogger.h +++ b/services/audioflinger/TypedLogger.h @@ -97,13 +97,15 @@ constexpr uint64_t hash(const char (&file)[n], uint32_t line) { #define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0) -// Record a typed entry that represents a thread's cycle time in nanoseconds. +// Record a typed entry that represents a thread's work time in nanoseconds. // Parameter ns should be of type uint32_t. -#define LOG_MONOTONIC_CYCLE_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ - x->logMonotonicCycleTime(ns); } while (0) +#define LOG_WORK_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->log(ns); } while (0) +// Log the difference bewteen frames presented by HAL and frames written to HAL output sink, +// divided by the sample rate. Parameter ms is of type double. #define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ - x->logLatency(ms); } while (0) + x->log(ms); } while (0) namespace android { extern "C" { -- GitLab From 9ec33560e39c4234cf1d693fc246be90643f001c Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Tue, 4 Sep 2018 15:22:46 -0700 Subject: [PATCH 0251/1530] android.hardware.media.bufferpool@2.0 tests Bug: 112203066 Change-Id: I9ea8d3dcb7e3c27da0360942d61882cd3459d10a --- media/bufferpool/2.0/tests/Android.bp | 51 ++++++ media/bufferpool/2.0/tests/OWNERS | 9 + media/bufferpool/2.0/tests/allocator.cpp | 158 ++++++++++++++++ media/bufferpool/2.0/tests/allocator.h | 47 +++++ media/bufferpool/2.0/tests/multi.cpp | 219 +++++++++++++++++++++++ media/bufferpool/2.0/tests/single.cpp | 164 +++++++++++++++++ 6 files changed, 648 insertions(+) create mode 100644 media/bufferpool/2.0/tests/Android.bp create mode 100644 media/bufferpool/2.0/tests/OWNERS create mode 100644 media/bufferpool/2.0/tests/allocator.cpp create mode 100644 media/bufferpool/2.0/tests/allocator.h create mode 100644 media/bufferpool/2.0/tests/multi.cpp create mode 100644 media/bufferpool/2.0/tests/single.cpp diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp new file mode 100644 index 0000000000..8b44f617cc --- /dev/null +++ b/media/bufferpool/2.0/tests/Android.bp @@ -0,0 +1,51 @@ +/* + * 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. + */ + +cc_test { + name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "allocator.cpp", + "single.cpp", + ], + static_libs: [ + "android.hardware.media.bufferpool@2.0", + "libcutils", + "libstagefright_bufferpool@2.0", + ], + shared_libs: [ + "libfmq", + ], + compile_multilib: "both", +} + +cc_test { + name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "allocator.cpp", + "multi.cpp", + ], + static_libs: [ + "android.hardware.media.bufferpool@2.0", + "libcutils", + "libstagefright_bufferpool@2.0", + ], + shared_libs: [ + "libfmq", + ], + compile_multilib: "both", +} diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS new file mode 100644 index 0000000000..6733e0cbd1 --- /dev/null +++ b/media/bufferpool/2.0/tests/OWNERS @@ -0,0 +1,9 @@ +# Media team +lajos@google.com +pawin@google.com +taklee@google.com +wonsik@google.com + +# VTS team +yim@google.com +zhuoyao@google.com diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp new file mode 100644 index 0000000000..78f7edbd01 --- /dev/null +++ b/media/bufferpool/2.0/tests/allocator.cpp @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#include +#include "allocator.h" + +union Params { + struct { + uint32_t capacity; + } data; + uint8_t array[0]; + Params() : data{0} {} + Params(uint32_t size) + : data{size} {} +}; + + +namespace { + +struct HandleAshmem : public native_handle_t { + HandleAshmem(int ashmemFd, size_t size) + : native_handle_t(cHeader), + mFds{ ashmemFd }, + mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {} + + int ashmemFd() const { return mFds.mAshmem; } + size_t size() const { + return size_t(unsigned(mInts.mSizeLo)) + | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32); + } + +protected: + struct { + int mAshmem; + } mFds; + struct { + int mSizeLo; + int mSizeHi; + int mMagic; + } mInts; + +private: + enum { + kMagic = 'ahm\x00', + numFds = sizeof(mFds) / sizeof(int), + numInts = sizeof(mInts) / sizeof(int), + version = sizeof(native_handle_t) + }; + const static native_handle_t cHeader; +}; + +const native_handle_t HandleAshmem::cHeader = { + HandleAshmem::version, + HandleAshmem::numFds, + HandleAshmem::numInts, + {} +}; + +class AllocationAshmem { +private: + AllocationAshmem(int ashmemFd, size_t capacity, bool res) + : mHandle(ashmemFd, capacity), + mInit(res) {} + +public: + static AllocationAshmem *Alloc(size_t size) { + constexpr static const char *kAllocationTag = "bufferpool_test"; + int ashmemFd = ashmem_create_region(kAllocationTag, size); + return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0); + } + + ~AllocationAshmem() { + if (mInit) { + native_handle_close(&mHandle); + } + } + + const HandleAshmem *handle() { + return &mHandle; + } + +private: + HandleAshmem mHandle; + bool mInit; + // TODO: mapping and map fd +}; + +struct AllocationDtor { + AllocationDtor(const std::shared_ptr &alloc) + : mAlloc(alloc) {} + + void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; } + + const std::shared_ptr mAlloc; +}; + +} + + +ResultStatus TestBufferPoolAllocator::allocate( + const std::vector ¶ms, + std::shared_ptr *alloc, + size_t *allocSize) { + Params ashmemParams; + memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size())); + + std::shared_ptr ashmemAlloc = + std::shared_ptr( + AllocationAshmem::Alloc(ashmemParams.data.capacity)); + if (ashmemAlloc) { + BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle()); + if (ptr) { + *alloc = std::shared_ptr(ptr, AllocationDtor(ashmemAlloc)); + if (*alloc) { + *allocSize = ashmemParams.data.capacity; + return ResultStatus::OK; + } + delete ptr; + return ResultStatus::NO_MEMORY; + } + } + return ResultStatus::CRITICAL_ERROR; +} + +bool TestBufferPoolAllocator::compatible(const std::vector &newParams, + const std::vector &oldParams) { + size_t newSize = newParams.size(); + size_t oldSize = oldParams.size(); + if (newSize == oldSize) { + for (size_t i = 0; i < newSize; ++i) { + if (newParams[i] != oldParams[i]) { + return false; + } + } + return true; + } + return false; +} + +void getTestAllocatorParams(std::vector *params) { + constexpr static int kAllocationSize = 1024 * 10; + Params ashmemParams(kAllocationSize); + + 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 new file mode 100644 index 0000000000..216944ed0f --- /dev/null +++ b/media/bufferpool/2.0/tests/allocator.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H +#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H + +#include + +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation:: + BufferPoolAllocation; +using android::hardware::media::bufferpool::V2_0::implementation:: + BufferPoolAllocator; + +// buffer allocator for the tests +class TestBufferPoolAllocator : public BufferPoolAllocator { + public: + TestBufferPoolAllocator() {} + + ~TestBufferPoolAllocator() override {} + + ResultStatus allocate(const std::vector ¶ms, + std::shared_ptr *alloc, + size_t *allocSize) override; + + bool compatible(const std::vector &newParams, + const std::vector &oldParams) override; + +}; + +// retrieve buffer allocator paramters +void getTestAllocatorParams(std::vector *params); + +#endif // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp new file mode 100644 index 0000000000..220808722c --- /dev/null +++ b/media/bufferpool/2.0/tests/multi.cpp @@ -0,0 +1,219 @@ +/* + * 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 "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]; +}; + +// 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); + + receiveMessage(mCommandPipeFds, &message); + { + native_handle_t *rhandle = nullptr; + std::shared_ptr rbuffer; + 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; + } + } + message.data.command = PipeCommand::RECEIVE_OK; + 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; + + getTestAllocatorParams(&vecParams); + status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer); + ASSERT_TRUE(status == ResultStatus::OK); + + 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); + } + EXPECT_TRUE(receiveMessage(mResultPipeFds, &message)); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + setenv("TREBLE_TESTING_OVERRIDE", "true", 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/single.cpp b/media/bufferpool/2.0/tests/single.cpp new file mode 100644 index 0000000000..5c6d1c7e95 --- /dev/null +++ b/media/bufferpool/2.0/tests/single.cpp @@ -0,0 +1,164 @@ +/* + * 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 "allocator.h" + +using android::hardware::hidl_handle; +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 { + +// Number of iteration for buffer allocation test. +constexpr static int kNumAllocationTest = 3; + +// Number of iteration for buffer recycling test. +constexpr static int kNumRecycleTest = 3; + +// media.bufferpool test setup +class BufferpoolSingleTest : public ::testing::Test { + public: + virtual void SetUp() override { + ResultStatus status; + mConnectionValid = false; + + 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; + + status = mManager->registerSender(mManager, mConnectionId, &mReceiverId); + ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS && + mReceiverId == mConnectionId); + } + + virtual void TearDown() override { + 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; + ConnectionId mReceiverId; + +}; + +// Buffer allocation test. +// Check whether each buffer allocation is done successfully with +// unique buffer id. +TEST_F(BufferpoolSingleTest, AllocateBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + + std::shared_ptr buffer[kNumAllocationTest]; + native_handle_t *allocHandle = nullptr; + for (int i = 0; i < kNumAllocationTest; ++i) { + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]); + ASSERT_TRUE(status == ResultStatus::OK); + } + for (int i = 0; i < kNumAllocationTest; ++i) { + for (int j = i + 1; j < kNumAllocationTest; ++j) { + ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId); + } + } + EXPECT_TRUE(kNumAllocationTest > 1); +} + +// Buffer recycle test. +// Check whether de-allocated buffers are recycled. +TEST_F(BufferpoolSingleTest, RecycleBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + + BufferId bid[kNumRecycleTest]; + for (int i = 0; i < kNumRecycleTest; ++i) { + std::shared_ptr buffer; + native_handle_t *allocHandle = nullptr; + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer); + ASSERT_TRUE(status == ResultStatus::OK); + bid[i] = buffer->mId; + } + for (int i = 1; i < kNumRecycleTest; ++i) { + ASSERT_TRUE(bid[i - 1] == bid[i]); + } + EXPECT_TRUE(kNumRecycleTest > 1); +} + +// Buffer transfer test. +// Check whether buffer is transferred to another client successfully. +TEST_F(BufferpoolSingleTest, TransferBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + std::shared_ptr sbuffer, rbuffer; + native_handle_t *allocHandle = nullptr; + native_handle_t *recvHandle = nullptr; + + TransactionId transactionId; + int64_t postUs; + + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer); + ASSERT_TRUE(status == ResultStatus::OK); + status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs); + ASSERT_TRUE(status == ResultStatus::OK); + status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs, + &recvHandle, &rbuffer); + EXPECT_TRUE(status == ResultStatus::OK); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} -- GitLab From 15f97c996f29c780f52fe3504fabd08dbde066ba Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 4 Sep 2018 14:06:27 -0700 Subject: [PATCH 0252/1530] aaudio: set to SHARED when EXCLUSIVE fails SHARED streams and endpoints were being created with sharing mode set to EXCLUSIVE. This was confusing the close() code and caused a hang. Now we create a modified Request object with the sharing mode set correctly. Bug: 112709847 Test: Hack AAudioServiceEndpointMMAP so isBufferShareable is false. Test: 'adb shell write_sine_callback -pl -x -s2' should not hang. Change-Id: I7a5c8260beaffdd706f34d35ef00a61b072adb1d --- services/oboeservice/AAudioEndpointManager.cpp | 5 ++--- services/oboeservice/AAudioEndpointManager.h | 3 +-- services/oboeservice/AAudioService.cpp | 11 ++++++++--- services/oboeservice/AAudioServiceEndpointMMAP.cpp | 3 --- services/oboeservice/AAudioServiceStreamBase.cpp | 6 ++---- services/oboeservice/AAudioServiceStreamMMAP.cpp | 9 +++++++-- services/oboeservice/AAudioServiceStreamShared.cpp | 8 +++++++- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp index 04fee13258..b1854bfbf8 100644 --- a/services/oboeservice/AAudioEndpointManager.cpp +++ b/services/oboeservice/AAudioEndpointManager.cpp @@ -140,9 +140,8 @@ sp AAudioEndpointManager::findSharedEndpoint_l( } sp AAudioEndpointManager::openEndpoint(AAudioService &audioService, - const aaudio::AAudioStreamRequest &request, - aaudio_sharing_mode_t sharingMode) { - if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) { + const aaudio::AAudioStreamRequest &request) { + if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { return openExclusiveEndpoint(audioService, request); } else { return openSharedEndpoint(audioService, request); diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h index 193bdee02f..ba17853491 100644 --- a/services/oboeservice/AAudioEndpointManager.h +++ b/services/oboeservice/AAudioEndpointManager.h @@ -56,8 +56,7 @@ public: * @return endpoint or null */ android::sp openEndpoint(android::AAudioService &audioService, - const aaudio::AAudioStreamRequest &request, - aaudio_sharing_mode_t sharingMode); + const aaudio::AAudioStreamRequest &request); void closeEndpoint(android::sp serviceEndpoint); diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp index 94440b17b9..53ee14513e 100644 --- a/services/oboeservice/AAudioService.cpp +++ b/services/oboeservice/AAudioService.cpp @@ -118,11 +118,16 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req } } - // if SHARED requested or if EXCLUSIVE failed - if (sharingMode == AAUDIO_SHARING_MODE_SHARED - || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) { + // If SHARED requested or if EXCLUSIVE failed. + if (sharingMode == AAUDIO_SHARING_MODE_SHARED) { serviceStream = new AAudioServiceStreamShared(*this); result = serviceStream->open(request); + } else if (serviceStream.get() == nullptr && !sharingModeMatchRequired) { + aaudio::AAudioStreamRequest modifiedRequest = request; + // Overwrite the original EXCLUSIVE mode with SHARED. + modifiedRequest.getConfiguration().setSharingMode(AAUDIO_SHARING_MODE_SHARED); + serviceStream = new AAudioServiceStreamShared(*this); + result = serviceStream->open(modifiedRequest); } if (result != AAUDIO_OK) { diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index f30f9bbb8a..01caee4352 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -210,9 +210,6 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques uid_t audioServiceUid = getuid(); if ((mMmapClient.clientUid != audioServiceUid) && getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { - // Fallback is handled by caller but indicate what is possible in case - // this is used in the future - setSharingMode(AAUDIO_SHARING_MODE_SHARED); ALOGW("%s() - exclusive FD cannot be used by client", __func__); result = AAUDIO_ERROR_UNAVAILABLE; goto error; diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp index 9af8af3495..12be4a33e4 100644 --- a/services/oboeservice/AAudioServiceStreamBase.cpp +++ b/services/oboeservice/AAudioServiceStreamBase.cpp @@ -81,8 +81,7 @@ std::string AAudioServiceStreamBase::dump() const { return result.str(); } -aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request, - aaudio_sharing_mode_t sharingMode) { +aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) { AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance(); aaudio_result_t result = AAUDIO_OK; @@ -109,8 +108,7 @@ aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest // referenced until the service returns a handle to the client. // So only one thread can open a stream. mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, - request, - sharingMode); + request); if (mServiceEndpoint == nullptr) { ALOGE("%s() openEndpoint() failed", __func__); result = AAUDIO_ERROR_UNAVAILABLE; diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp index c845309140..9377945f08 100644 --- a/services/oboeservice/AAudioServiceStreamMMAP.cpp +++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp @@ -64,8 +64,13 @@ aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest sp keep(this); - aaudio_result_t result = AAudioServiceStreamBase::open(request, - AAUDIO_SHARING_MODE_EXCLUSIVE); + if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_EXCLUSIVE) { + ALOGE("%s() sharingMode mismatch %d", __func__, + request.getConstantConfiguration().getSharingMode()); + return AAUDIO_ERROR_INTERNAL; + } + + aaudio_result_t result = AAudioServiceStreamBase::open(request); if (result != AAUDIO_OK) { return result; } diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp index 05c5735b39..b645c698ae 100644 --- a/services/oboeservice/AAudioServiceStreamShared.cpp +++ b/services/oboeservice/AAudioServiceStreamShared.cpp @@ -120,7 +120,13 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques sp keep(this); - aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED); + if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_SHARED) { + ALOGE("%s() sharingMode mismatch %d", __func__, + request.getConstantConfiguration().getSharingMode()); + return AAUDIO_ERROR_INTERNAL; + } + + aaudio_result_t result = AAudioServiceStreamBase::open(request); if (result != AAUDIO_OK) { ALOGE("%s() returned %d", __func__, result); return result; -- GitLab From 0a07d65edfe43bae713e316babc2d419e6fed816 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 5 Sep 2018 10:36:32 -0700 Subject: [PATCH 0253/1530] stagefright: add TEST_MAPPING Test: atest --test-mapping frameworks/av/media/libstagefright:postsubmit Change-Id: Ie4e19b5d794e50774696c66d8105973f5c7beb3a --- media/libstagefright/TEST_MAPPING | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 media/libstagefright/TEST_MAPPING diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING new file mode 100644 index 0000000000..96818eb70d --- /dev/null +++ b/media/libstagefright/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "postsubmit": [ + { + "name": "CtsMediaTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.RequiresDevice" + } + ] + } + ] +} -- GitLab From f0c4a6b5cd282b7cdb19205bdcea4572d47c23e1 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 5 Sep 2018 09:36:14 -0700 Subject: [PATCH 0254/1530] Camera: Treat decreasing timestamp as BUFFER_ERROR Decreasing timestamp shouldn't happen in non ZSL or reprocessing case. When that happens, treat the buffer as error and return to buffer queue directly. Test: Camera CTS Bug: 113670946 Change-Id: I39d3417dd9307d6cc7c90ff357a82604566a9081 --- .../libcameraservice/device3/Camera3OutputStream.cpp | 10 ++++------ .../camera/libcameraservice/device3/Camera3Stream.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 84767110c0..ecbcf76c68 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -237,12 +237,14 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( /** * Return buffer back to ANativeWindow */ - if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers) { + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers || timestamp == 0) { // Cancel buffer if (mDropBuffers) { ALOGV("%s: Dropping a frame for stream %d.", __FUNCTION__, mId); - } else { + } else if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { ALOGW("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId); + } else { + ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); } res = currentConsumer->cancelBuffer(currentConsumer.get(), @@ -268,10 +270,6 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( mTraceFirstBuffer = false; } - if (timestamp == 0) { - ALOGW("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); - } - /* Certain consumers (such as AudioSource or HardwareComposer) use * MONOTONIC time, causing time misalignment if camera timestamp is * in BOOTTIME. Do the conversion if necessary. */ diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index b208d9ffec..9238590b8c 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -662,9 +662,12 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, removeOutstandingBuffer(buffer); + // Buffer status may be changed, so make a copy of the stream_buffer struct. + camera3_stream_buffer b = buffer; if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) { - ALOGW("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64, + ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64, __FUNCTION__, mId, timestamp, mLastTimestamp); + b.status = CAMERA3_BUFFER_STATUS_ERROR; } mLastTimestamp = timestamp; @@ -676,9 +679,9 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, * * Do this for getBuffer as well. */ - status_t res = returnBufferLocked(buffer, timestamp); + status_t res = returnBufferLocked(b, timestamp); if (res == OK) { - fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true); + fireBufferListenersLocked(b, /*acquired*/false, /*output*/true); } // Even if returning the buffer failed, we still want to signal whoever is waiting for the -- GitLab From 71115426b8befebf56d56a438c38c4f1da00d121 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 5 Sep 2018 09:50:55 -0700 Subject: [PATCH 0255/1530] stagefright: Handle DEAD_OBJECT correctly in conversion Bug: 113343878 Test: bug repro step Change-Id: Iab66ae73a8e57d11bd6979a299377711d2f83d93 --- media/libstagefright/bqhelper/Conversion.cpp | 24 +++++++++---------- .../media/stagefright/omx/1.0/Conversion.h | 7 +++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/media/libstagefright/bqhelper/Conversion.cpp b/media/libstagefright/bqhelper/Conversion.cpp index ffed005777..91d7c74dd6 100644 --- a/media/libstagefright/bqhelper/Conversion.cpp +++ b/media/libstagefright/bqhelper/Conversion.cpp @@ -97,6 +97,17 @@ int native_handle_read_fd(native_handle_t const* nh, int index) { * `convertTo()` do. */ +/** + * \brief Convert `Return` to `status_t`. This is for legacy binder calls. + * + * \param[in] t The source `Return`. + * \return The corresponding `status_t`. + */ +// convert: Return -> status_t +status_t toStatusT(Return const& t) { + return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); +} + /** * \brief Convert `Return` to `binder::Status`. * @@ -107,21 +118,10 @@ int native_handle_read_fd(native_handle_t const* nh, int index) { ::android::binder::Status toBinderStatus( Return const& t) { return ::android::binder::Status::fromExceptionCode( - t.isOk() ? OK : UNKNOWN_ERROR, + toStatusT(t), t.description().c_str()); } -/** - * \brief Convert `Return` to `status_t`. This is for legacy binder calls. - * - * \param[in] t The source `Return`. - * \return The corresponding `status_t`. - */ -// convert: Return -> status_t -status_t toStatusT(Return const& t) { - return t.isOk() ? OK : UNKNOWN_ERROR; -} - /** * \brief Wrap `native_handle_t*` in `hidl_handle`. * diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h index a9fce55471..0f229f7545 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h @@ -190,7 +190,12 @@ inline status_t toStatusT(Status const& t) { */ // convert: Status -> status_t inline status_t toStatusT(Return const& t) { - return t.isOk() ? toStatusT(static_cast(t)) : UNKNOWN_ERROR; + if (t.isOk()) { + return toStatusT(static_cast(t)); + } else if (t.isDeadObject()) { + return DEAD_OBJECT; + } + return UNKNOWN_ERROR; } /** -- GitLab From 1cb0ccb5b9ce176949fcd09460d58680d9f3382e Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 5 Sep 2018 14:47:36 -0700 Subject: [PATCH 0256/1530] bufferpool2.0: apply hidl interface update Bug: 112203066 Change-Id: I2f797f72c34d923f4b9efa5cf6eefad468458233 --- media/bufferpool/2.0/Accessor.cpp | 12 +++++++++--- media/bufferpool/2.0/Accessor.h | 2 +- media/bufferpool/2.0/AccessorImpl.cpp | 2 +- media/bufferpool/2.0/AccessorImpl.h | 2 +- media/bufferpool/2.0/BufferPoolClient.cpp | 6 ++++-- media/bufferpool/2.0/BufferStatus.cpp | 4 ++-- media/bufferpool/2.0/BufferStatus.h | 4 ++-- .../2.0/include/bufferpool/BufferPoolTypes.h | 6 +++++- 8 files changed, 25 insertions(+), 13 deletions(-) diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp index 3fd41f0b91..c1b62f8b6a 100644 --- a/media/bufferpool/2.0/Accessor.cpp +++ b/media/bufferpool/2.0/Accessor.cpp @@ -117,14 +117,20 @@ sp Accessor::getConnectionDeathRecipient() { Return Accessor::connect(connect_cb _hidl_cb) { sp connection; ConnectionId connectionId; - const QueueDescriptor* fmqDesc; + const StatusDescriptor* fmqDesc; ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false); if (status == ResultStatus::OK) { - _hidl_cb(status, connection, connectionId, *fmqDesc); + _hidl_cb(status, connection, connectionId, *fmqDesc, + android::hardware::MQDescriptorSync( + std::vector(), + nullptr /* nhandle */, 0 /* size */)); } else { _hidl_cb(status, nullptr, -1LL, android::hardware::MQDescriptorSync( + std::vector(), + nullptr /* nhandle */, 0 /* size */), + android::hardware::MQDescriptorSync( std::vector(), nullptr /* nhandle */, 0 /* size */)); } @@ -162,7 +168,7 @@ ResultStatus Accessor::fetch( ResultStatus Accessor::connect( sp *connection, ConnectionId *pConnectionId, - const QueueDescriptor** fmqDescPtr, bool local) { + const StatusDescriptor** fmqDescPtr, bool local) { if (mImpl) { ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr); if (!local && status == ResultStatus::OK) { diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h index 4fd8f5b2e2..fa2cb1b030 100644 --- a/media/bufferpool/2.0/Accessor.h +++ b/media/bufferpool/2.0/Accessor.h @@ -147,7 +147,7 @@ struct Accessor : public IAccessor { */ ResultStatus connect( sp *connection, ConnectionId *pConnectionId, - const QueueDescriptor** fmqDescPtr, bool local); + const StatusDescriptor** fmqDescPtr, bool local); /** * Closes the specified connection to the client. diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp index e5cf275ee3..ef62d030f5 100644 --- a/media/bufferpool/2.0/AccessorImpl.cpp +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -139,7 +139,7 @@ Accessor::Impl::~Impl() { ResultStatus Accessor::Impl::connect( const sp &accessor, sp *connection, - ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) { + ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr) { sp newConnection = new Connection(); ResultStatus status = ResultStatus::CRITICAL_ERROR; { diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h index 404394071c..1d33880e2a 100644 --- a/media/bufferpool/2.0/AccessorImpl.h +++ b/media/bufferpool/2.0/AccessorImpl.h @@ -41,7 +41,7 @@ public: ResultStatus connect( const sp &accessor, sp *connection, - ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr); + ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr); ResultStatus close(ConnectionId connectionId); diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp index 10158c375c..4eeebb442a 100644 --- a/media/bufferpool/2.0/BufferPoolClient.cpp +++ b/media/bufferpool/2.0/BufferPoolClient.cpp @@ -247,7 +247,7 @@ public: BufferPoolClient::Impl::Impl(const sp &accessor) : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0), mLastEvictCacheUs(getTimestampNow()) { - const QueueDescriptor *fmqDesc; + const StatusDescriptor *fmqDesc; ResultStatus status = accessor->connect( &mLocalConnection, &mConnectionId, &fmqDesc, true); if (status == ResultStatus::OK) { @@ -269,7 +269,9 @@ BufferPoolClient::Impl::Impl(const sp &accessor) Return transResult = accessor->connect( [&valid, &outConnection, &id, &outChannel] (ResultStatus status, sp connection, - ConnectionId connectionId, const QueueDescriptor& desc) { + ConnectionId connectionId, const StatusDescriptor& desc, + const InvalidationDescriptor& invDesc) { + (void) invDesc; if (status == ResultStatus::OK) { outConnection = connection; id = connectionId; diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp index 3379e21502..0d3f5a3f54 100644 --- a/media/bufferpool/2.0/BufferStatus.cpp +++ b/media/bufferpool/2.0/BufferStatus.cpp @@ -41,7 +41,7 @@ static constexpr int kNumElementsInQueue = 1024*16; static constexpr int kMinElementsToSyncInQueue = 128; ResultStatus BufferStatusObserver::open( - ConnectionId id, const QueueDescriptor** fmqDescPtr) { + ConnectionId id, const StatusDescriptor** fmqDescPtr) { if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) { // TODO: id collision log? return ResultStatus::CRITICAL_ERROR; @@ -91,7 +91,7 @@ void BufferStatusObserver::getBufferStatusChanges(std::vector queue = std::make_unique(fmqDesc); if (!queue || queue->isValid() == false) { diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h index a74f0a5d32..777a3200f8 100644 --- a/media/bufferpool/2.0/BufferStatus.h +++ b/media/bufferpool/2.0/BufferStatus.h @@ -56,7 +56,7 @@ public: * NO_MEMORY when there is no memory. * CRITICAL_ERROR otherwise. */ - ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr); + ResultStatus open(ConnectionId id, const StatusDescriptor** fmqDescPtr); /** Closes an FMQ for the specified connection(client). * @@ -89,7 +89,7 @@ public: * * @param fmqDesc Descriptor of the created FMQ. */ - BufferStatusChannel(const QueueDescriptor &fmqDesc); + BufferStatusChannel(const StatusDescriptor &fmqDesc); /** Returns whether the FMQ is connected successfully. */ bool isValid(); diff --git a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h index d2de62842c..eb845e16ae 100644 --- a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h +++ b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h @@ -58,7 +58,11 @@ enum : ConnectionId { }; typedef android::hardware::MessageQueue BufferStatusQueue; -typedef BufferStatusQueue::Descriptor QueueDescriptor; +typedef BufferStatusQueue::Descriptor StatusDescriptor; + +typedef android::hardware::MessageQueue + BufferInvalidationQueue; +typedef BufferInvalidationQueue::Descriptor InvalidationDescriptor; /** * Allocation wrapper class for buffer pool. -- GitLab From 6a59e4ae385b15bb3ef729b62bb34933ce3ce18d Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Thu, 30 Aug 2018 13:31:28 -0700 Subject: [PATCH 0257/1530] NdkMediaExtractor: setDataSourceWithHeaders API Bug: 109928575 Change-Id: I90e9f6c9d6408f5c577ba75c9792bed589cee304 --- media/ndk/NdkMediaExtractor.cpp | 25 +++++++++++++++++---- media/ndk/include/media/NdkMediaExtractor.h | 22 +++++++++++++++++- media/ndk/libmediandk.map.txt | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index b5e60a4be6..fa1292e7c2 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -84,8 +84,17 @@ media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, o EXPORT media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) { - ALOGV("setDataSource(%s)", location); - // TODO: add header support + return AMediaExtractor_setDataSourceWithHeaders(mData, location, 0, NULL, NULL); +} + +EXPORT +media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor *mData, + const char *uri, + int numheaders, + const char * const *keys, + const char * const *values) { + + ALOGV("setDataSource(%s)", uri); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject service = NULL; @@ -109,7 +118,7 @@ media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char return AMEDIA_ERROR_UNSUPPORTED; } - jstring jloc = env->NewStringUTF(location); + jstring jloc = env->NewStringUTF(uri); service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc); env->DeleteLocalRef(jloc); @@ -120,7 +129,15 @@ media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char httpService = interface_cast(binder); } - status_t err = mData->mImpl->setDataSource(httpService, location, NULL); + KeyedVector headers; + for (int i = 0; i < numheaders; ++i) { + String8 key8(keys[i]); + String8 value8(values[i]); + headers.add(key8, value8); + } + + status_t err; + err = mData->mImpl->setDataSource(httpService, uri, numheaders > 0 ? &headers : NULL); env->ExceptionClear(); return translate_error(err); } diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h index 6a1796f977..413bc1ae6b 100644 --- a/media/ndk/include/media/NdkMediaExtractor.h +++ b/media/ndk/include/media/NdkMediaExtractor.h @@ -72,7 +72,27 @@ media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t */ media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location) __INTRODUCED_IN(21); - // TODO support headers + +#if __ANDROID_API__ >= 29 +/** + * Set the |uri| from which the extractor will read, + * plus additional http headers when initiating the request. + * + * Headers will contain corresponding items from |keys| & |values| + * from indices 0 (inclusive) to numheaders-1 (inclusive): + * + * keys[0]:values[0] + * ... + * keys[numheaders - 1]:values[numheaders - 1] + * + */ +media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor*, + const char *uri, + int numheaders, + const char * const *keys, + const char * const *values) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ #if __ANDROID_API__ >= 28 diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index d828d6a2cb..34e8a4e5ac 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -184,6 +184,7 @@ LIBMEDIANDK { AMediaExtractor_setDataSource; AMediaExtractor_setDataSourceCustom; # introduced=28 AMediaExtractor_setDataSourceFd; + AMediaExtractor_setDataSourceWithHeaders; # introduced=29 AMediaExtractor_unselectTrack; AMediaFormat_delete; AMediaFormat_getBuffer; -- GitLab From 0f6e640075173b76c3925f2cf92d9faf5ce8caba Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 6 Sep 2018 11:20:45 -0700 Subject: [PATCH 0258/1530] AudioPolicyManager: Update property get Use built-in prop_get_bool and prop_get_int32 for consistent behavior. Test: Play Music and disable offload Change-Id: I0a76430ceffadac529c193f3b19b5dd1cd7aa467 --- .../managerdefault/AudioPolicyManager.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 2a8b39732e..e4f3cf12a0 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2811,12 +2811,9 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI } // Check if offload has been disabled - char propValue[PROPERTY_VALUE_MAX]; - if (property_get("audio.offload.disable", propValue, "0")) { - if (atoi(propValue) != 0) { - ALOGV("offload disabled by audio.offload.disable=%s", propValue ); - return false; - } + if (property_get_bool("audio.offload.disable", false /* default_value */)) { + ALOGV("offload disabled by audio.offload.disable"); + return false; } // Check if stream type is music, then only allow offload as of now. @@ -2835,9 +2832,12 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI } //If duration is less than minimum value defined in property, return false - if (property_get("audio.offload.min.duration.secs", propValue, NULL)) { - if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) { - ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue); + const int min_duration_secs = property_get_int32( + "audio.offload.min.duration.secs", -1 /* default_value */); + if (min_duration_secs >= 0) { + if (offloadInfo.duration_us < min_duration_secs * 1000000LL) { + ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%d)", + min_duration_secs); return false; } } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) { -- GitLab From c34b9b7ceb590d31a1ffb50e30f310fb76e81c66 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Thu, 6 Sep 2018 14:48:21 -0700 Subject: [PATCH 0259/1530] bufferpool2.0 tests: add read/write verification Bug: 112203066 Bug: 113798958 Change-Id: I3969903c7263654bf1fecf012cf9185acd60ef58 --- media/bufferpool/2.0/tests/allocator.cpp | 51 ++++++++++++++++++++++++ media/bufferpool/2.0/tests/allocator.h | 4 ++ media/bufferpool/2.0/tests/multi.cpp | 10 +++-- media/bufferpool/2.0/tests/single.cpp | 2 + 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp index 78f7edbd01..843f7ea971 100644 --- a/media/bufferpool/2.0/tests/allocator.cpp +++ b/media/bufferpool/2.0/tests/allocator.cpp @@ -15,6 +15,7 @@ */ #include +#include #include "allocator.h" union Params { @@ -42,6 +43,8 @@ struct HandleAshmem : public native_handle_t { | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32); } + static bool isValid(const native_handle_t * const o); + protected: struct { int mAshmem; @@ -69,6 +72,14 @@ const native_handle_t HandleAshmem::cHeader = { {} }; +bool HandleAshmem::isValid(const native_handle_t * const o) { + if (!o || memcmp(o, &cHeader, sizeof(cHeader))) { + return false; + } + const HandleAshmem *other = static_cast(o); + return other->mInts.mMagic == kMagic; +} + class AllocationAshmem { private: AllocationAshmem(int ashmemFd, size_t capacity, bool res) @@ -150,6 +161,46 @@ bool TestBufferPoolAllocator::compatible(const std::vector &newParams, return false; } +bool TestBufferPoolAllocator::Fill(const native_handle_t *handle, const unsigned char val) { + if (!HandleAshmem::isValid(handle)) { + return false; + } + const HandleAshmem *o = static_cast(handle); + unsigned char *ptr = (unsigned char *)mmap( + NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0); + + if (ptr != MAP_FAILED) { + for (size_t i = 0; i < o->size(); ++i) { + ptr[i] = val; + } + munmap(ptr, o->size()); + return true; + } + return false; +} + +bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsigned char val) { + if (!HandleAshmem::isValid(handle)) { + return false; + } + const HandleAshmem *o = static_cast(handle); + unsigned char *ptr = (unsigned char *)mmap( + NULL, o->size(), PROT_READ, MAP_SHARED, o->ashmemFd(), 0); + + if (ptr != MAP_FAILED) { + bool res = true; + for (size_t i = 0; i < o->size(); ++i) { + if (ptr[i] != val) { + res = false; + break; + } + } + munmap(ptr, o->size()); + return res; + } + return false; +} + void getTestAllocatorParams(std::vector *params) { constexpr static int kAllocationSize = 1024 * 10; Params ashmemParams(kAllocationSize); diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h index 216944ed0f..5281dc3144 100644 --- a/media/bufferpool/2.0/tests/allocator.h +++ b/media/bufferpool/2.0/tests/allocator.h @@ -39,6 +39,10 @@ class TestBufferPoolAllocator : public BufferPoolAllocator { bool compatible(const std::vector &newParams, const std::vector &oldParams) override; + static bool Fill(const native_handle_t *handle, const unsigned char val); + + static bool Verify(const native_handle_t *handle, const unsigned char val); + }; // retrieve buffer allocator paramters diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp index 220808722c..68b6992d96 100644 --- a/media/bufferpool/2.0/tests/multi.cpp +++ b/media/bufferpool/2.0/tests/multi.cpp @@ -161,9 +161,11 @@ class BufferpoolMultiTest : public ::testing::Test { 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::Verify(rhandle, 0x77)) { + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } } } message.data.command = PipeCommand::RECEIVE_OK; @@ -195,6 +197,8 @@ TEST_F(BufferpoolMultiTest, TransferBuffer) { status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer); ASSERT_TRUE(status == ResultStatus::OK); + ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77)); + status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs); ASSERT_TRUE(status == ResultStatus::OK); diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp index 5c6d1c7e95..777edcfef8 100644 --- a/media/bufferpool/2.0/tests/single.cpp +++ b/media/bufferpool/2.0/tests/single.cpp @@ -147,11 +147,13 @@ TEST_F(BufferpoolSingleTest, TransferBuffer) { status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer); ASSERT_TRUE(status == ResultStatus::OK); + ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77)); status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs); ASSERT_TRUE(status == ResultStatus::OK); status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs, &recvHandle, &rbuffer); EXPECT_TRUE(status == ResultStatus::OK); + ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77)); } } // anonymous namespace -- GitLab From bc2a77308b7c197a30a3289e5b75d0f089e65982 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Thu, 6 Sep 2018 12:04:44 -0700 Subject: [PATCH 0260/1530] media.metrics: add new record type "audiothread" Test: compile Bug: 68148948 Change-Id: I8cf96fdc218bd09d90916f2c96fa343ddd4f873b --- services/mediaanalytics/MediaAnalyticsService.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp index 4b05395686..45da56a84e 100644 --- a/services/mediaanalytics/MediaAnalyticsService.cpp +++ b/services/mediaanalytics/MediaAnalyticsService.cpp @@ -483,6 +483,7 @@ static std::string allowedKeys[] = { "audiopolicy", "audiorecord", + "audiothread", "audiotrack", "codec", "extractor", -- GitLab From 0127c1be07d63e046e136126cd465c3fa8977768 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 29 Mar 2018 13:48:06 -0700 Subject: [PATCH 0261/1530] aaudio: use new flowgraph to simplify processing Construct a flowgraph based on the source and destination format and channelCount. This is groundwork for supporting 24-bit PCM formats. Also cleaned up handling of device related format. This CL removes more code than it adds. Bug: 65067568 Test: write_sine_callback.cpp -pl Test: write_sine_callback.cpp -pl -x Test: input_monitor -pl Test: input_monitor -pl -x Change-Id: Ia155bff0164912011d09b61b54f983ccf4490bd1 --- media/libaaudio/src/Android.bp | 2 +- .../src/binding/AAudioStreamConfiguration.cpp | 14 +- .../src/binding/SharedMemoryParcelable.cpp | 5 +- .../src/binding/SharedMemoryParcelable.h | 4 +- .../libaaudio/src/client/AAudioFlowGraph.cpp | 116 +++++ media/libaaudio/src/client/AAudioFlowGraph.h | 64 +++ .../src/client/AudioStreamInternal.cpp | 13 +- .../src/client/AudioStreamInternal.h | 3 +- .../src/client/AudioStreamInternalCapture.cpp | 36 +- .../src/client/AudioStreamInternalPlay.cpp | 32 +- .../src/client/AudioStreamInternalPlay.h | 3 +- media/libaaudio/src/core/AAudioAudio.cpp | 8 +- .../src/core/AAudioStreamParameters.cpp | 26 +- .../src/core/AAudioStreamParameters.h | 11 +- media/libaaudio/src/core/AudioStream.h | 23 +- .../libaaudio/src/legacy/AudioStreamLegacy.h | 1 - .../src/legacy/AudioStreamRecord.cpp | 15 +- .../libaaudio/src/legacy/AudioStreamTrack.cpp | 10 +- .../libaaudio/src/utility/AAudioUtilities.cpp | 403 +----------------- media/libaaudio/src/utility/AAudioUtilities.h | 156 ------- media/libaaudio/src/utility/LinearRamp.cpp | 50 --- media/libaaudio/src/utility/LinearRamp.h | 97 ----- media/libaaudio/tests/Android.bp | 7 - media/libaaudio/tests/test_linear_ramp.cpp | 180 -------- .../oboeservice/AAudioServiceEndpoint.cpp | 1 + .../oboeservice/AAudioServiceEndpointMMAP.cpp | 13 +- .../AAudioServiceEndpointShared.cpp | 1 + .../oboeservice/AAudioServiceStreamShared.cpp | 16 +- 28 files changed, 315 insertions(+), 995 deletions(-) create mode 100644 media/libaaudio/src/client/AAudioFlowGraph.cpp create mode 100644 media/libaaudio/src/client/AAudioFlowGraph.h delete mode 100644 media/libaaudio/src/utility/LinearRamp.cpp delete mode 100644 media/libaaudio/src/utility/LinearRamp.h delete mode 100644 media/libaaudio/tests/test_linear_ramp.cpp diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp index 4b5f30dcaa..409028667f 100644 --- a/media/libaaudio/src/Android.bp +++ b/media/libaaudio/src/Android.bp @@ -26,9 +26,9 @@ cc_library { "utility/FixedBlockAdapter.cpp", "utility/FixedBlockReader.cpp", "utility/FixedBlockWriter.cpp", - "utility/LinearRamp.cpp", "fifo/FifoBuffer.cpp", "fifo/FifoControllerBase.cpp", + "client/AAudioFlowGraph.cpp", "client/AudioEndpoint.cpp", "client/AudioStreamInternal.cpp", "client/AudioStreamInternalCapture.cpp", diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp index 959db61a6e..3d1bc9b7a9 100644 --- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp +++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#define LOG_TAG "AAudioStreamConfiguration" +//#define LOG_NDEBUG 0 +#include + #include #include @@ -36,6 +40,7 @@ AAudioStreamConfiguration::~AAudioStreamConfiguration() {} status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const { status_t status; + status = parcel->writeInt32(getDeviceId()); if (status != NO_ERROR) goto error; status = parcel->writeInt32(getSampleRate()); @@ -46,6 +51,7 @@ status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const { if (status != NO_ERROR) goto error; status = parcel->writeInt32((int32_t) getFormat()); if (status != NO_ERROR) goto error; + status = parcel->writeInt32((int32_t) getDirection()); if (status != NO_ERROR) goto error; status = parcel->writeInt32(getBufferCapacity()); @@ -60,7 +66,7 @@ status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const { if (status != NO_ERROR) goto error; return NO_ERROR; error: - ALOGE("AAudioStreamConfiguration.writeToParcel(): write failed = %d", status); + ALOGE("%s(): write failed = %d", __func__, status); return status; } @@ -80,7 +86,8 @@ status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) { setSharingMode((aaudio_sharing_mode_t) value); status = parcel->readInt32(&value); if (status != NO_ERROR) goto error; - setFormat((aaudio_format_t) value); + setFormat((audio_format_t) value); + status = parcel->readInt32(&value); if (status != NO_ERROR) goto error; setDirection((aaudio_direction_t) value); @@ -99,8 +106,9 @@ status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) { status = parcel->readInt32(&value); if (status != NO_ERROR) goto error; setSessionId(value); + return NO_ERROR; error: - ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status); + ALOGE("%s(): read failed = %d", __func__, status); return status; } diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp index 0b0cf77da8..67955e8ffe 100644 --- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp +++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp @@ -43,7 +43,7 @@ SharedMemoryParcelable::~SharedMemoryParcelable() {}; void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) { mFd.reset(dup(fd.get())); // store a duplicate fd - ALOGV("setup(%d -> %d, %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this); + ALOGV("setup(fd = %d -> %d, size = %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this); mSizeInBytes = sizeInBytes; } @@ -104,7 +104,8 @@ aaudio_result_t SharedMemoryParcelable::resolveSharedMemory(const unique_fd& fd) mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) { - ALOGE("mmap() failed for fd = %d, errno = %s", fd.get(), strerror(errno)); + ALOGE("mmap() failed for fd = %d, nBytes = %d, errno = %s", + fd.get(), mSizeInBytes, strerror(errno)); return AAUDIO_ERROR_INTERNAL; } return AAUDIO_OK; diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h index 82c2240840..4ec38c579a 100644 --- a/media/libaaudio/src/binding/SharedMemoryParcelable.h +++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h @@ -70,8 +70,8 @@ protected: aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd); android::base::unique_fd mFd; - int32_t mSizeInBytes = 0; - uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS; + int32_t mSizeInBytes = 0; + uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS; private: diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp new file mode 100644 index 0000000000..3e43c6b609 --- /dev/null +++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp @@ -0,0 +1,116 @@ +/* + * 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 "AAudioFlowGraph" +//#define LOG_NDEBUG 0 +#include + +#include "AAudioFlowGraph.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace flowgraph; + +aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat, + int32_t sourceChannelCount, + audio_format_t sinkFormat, + int32_t sinkChannelCount) { + AudioFloatOutputPort *lastOutput = nullptr; + + ALOGD("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d", + __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount); + + switch (sourceFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + mSource = std::make_unique(sourceChannelCount); + break; + case AUDIO_FORMAT_PCM_16_BIT: + mSource = std::make_unique(sourceChannelCount); + break; + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + mSource = std::make_unique(sourceChannelCount); + break; + default: // TODO add I32 + ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat); + return AAUDIO_ERROR_UNIMPLEMENTED; + } + lastOutput = &mSource->output; + + // Apply volume as a ramp to avoid pops. + mVolumeRamp = std::make_unique(sourceChannelCount); + lastOutput->connect(&mVolumeRamp->input); + lastOutput = &mVolumeRamp->output; + + // For a pure float graph, there is chance that the data range may be very large. + // So we should clip to a reasonable value that allows a little headroom. + if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) { + mClipper = std::make_unique(sourceChannelCount); + lastOutput->connect(&mClipper->input); + lastOutput = &mClipper->output; + } + + // Expand the number of channels if required. + if (sourceChannelCount == 1 && sinkChannelCount > 1) { + mChannelConverter = std::make_unique(sinkChannelCount); + lastOutput->connect(&mChannelConverter->input); + lastOutput = &mChannelConverter->output; + } else if (sourceChannelCount != sinkChannelCount) { + ALOGE("%s() Channel reduction not supported.", __func__); + return AAUDIO_ERROR_UNIMPLEMENTED; + } + + switch (sinkFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + mSink = std::make_unique(sinkChannelCount); + break; + case AUDIO_FORMAT_PCM_16_BIT: + mSink = std::make_unique(sinkChannelCount); + break; + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + mSink = std::make_unique(sinkChannelCount); + break; + default: // TODO add I32 + ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat); + return AAUDIO_ERROR_UNIMPLEMENTED; + } + lastOutput->connect(&mSink->input); + + return AAUDIO_OK; +} + +void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) { + mSource->setData(source, numFrames); + mSink->read(destination, numFrames); +} + +/** + * @param volume between 0.0 and 1.0 + */ +void AAudioFlowGraph::setTargetVolume(float volume) { + mVolumeRamp->setTarget(volume); +} + +void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) { + mVolumeRamp->setLengthInFrames(numFrames); +} diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h new file mode 100644 index 0000000000..a49f64ebfe --- /dev/null +++ b/media/libaaudio/src/client/AAudioFlowGraph.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef ANDROID_AAUDIO_FLOW_GRAPH_H +#define ANDROID_AAUDIO_FLOW_GRAPH_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +class AAudioFlowGraph { +public: + /** Connect several modules together to convert from source to sink. + * This should only be called once for each instance. + * + * @param sourceFormat + * @param sourceChannelCount + * @param sinkFormat + * @param sinkChannelCount + * @return + */ + aaudio_result_t configure(audio_format_t sourceFormat, + int32_t sourceChannelCount, + audio_format_t sinkFormat, + int32_t sinkChannelCount); + + void process(const void *source, void *destination, int32_t numFrames); + + /** + * @param volume between 0.0 and 1.0 + */ + void setTargetVolume(float volume); + + void setRampLengthInFrames(int32_t numFrames); + +private: + std::unique_ptr mSource; + std::unique_ptr mVolumeRamp; + std::unique_ptr mClipper; + std::unique_ptr mChannelConverter; + std::unique_ptr mSink; +}; + + +#endif //ANDROID_AAUDIO_FLOW_GRAPH_H diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index 92048248b9..0a8021ad95 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -39,7 +39,6 @@ #include "core/AudioStreamBuilder.h" #include "fifo/FifoBuffer.h" #include "utility/AudioClock.h" -#include "utility/LinearRamp.h" #include "AudioStreamInternal.h" @@ -92,11 +91,11 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { } // We have to do volume scaling. So we prefer FLOAT format. - if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) { - setFormat(AAUDIO_FORMAT_PCM_FLOAT); + if (getFormat() == AUDIO_FORMAT_DEFAULT) { + setFormat(AUDIO_FORMAT_PCM_FLOAT); } // Request FLOAT for the shared mixer. - request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT); + request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT); // Build the request to send to the server. request.setUserId(getuid()); @@ -126,7 +125,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { // if that failed then try switching from mono to stereo if OUTPUT. // Only do this in the client. Otherwise we end up with a mono mixer in the service // that writes to a stereo MMAP stream. - ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO", + ALOGD("%s() - openStream() returned %d, try switching from MONO to STEREO", __func__, mServiceStreamHandle); request.getConfiguration().setSamplesPerFrame(2); // stereo mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput); @@ -212,9 +211,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { mCallbackFrames = mFramesPerBurst; } - int32_t bytesPerFrame = getSamplesPerFrame() - * AAudioConvert_formatToSizeInBytes(getFormat()); - int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame; + const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame(); mCallbackBuffer = new uint8_t[callbackBufferSize]; } diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h index 0425cd5d3e..3bb9e1ea1b 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.h +++ b/media/libaaudio/src/client/AudioStreamInternal.h @@ -27,7 +27,6 @@ #include "client/AudioEndpoint.h" #include "core/AudioStream.h" #include "utility/AudioClock.h" -#include "utility/LinearRamp.h" using android::sp; using android::IAAudioService; @@ -193,6 +192,8 @@ private: int64_t mServiceLatencyNanos = 0; + // Sometimes the hardware is operating with a different channel count from the app. + // Then we require conversion in AAudio. int32_t mDeviceChannelCount = 0; }; diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp index 0719fe1512..4a0e6da444 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include "client/AudioStreamInternalCapture.h" @@ -165,35 +166,36 @@ aaudio_result_t AudioStreamInternalCapture::readNowWithConversion(void *buffer, // Read data in one or two parts. for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) { int32_t framesToProcess = framesLeft; - int32_t framesAvailable = wrappingBuffer.numFrames[partIndex]; + const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex]; if (framesAvailable <= 0) break; if (framesToProcess > framesAvailable) { framesToProcess = framesAvailable; } - int32_t numBytes = getBytesPerFrame() * framesToProcess; - int32_t numSamples = framesToProcess * getSamplesPerFrame(); + const int32_t numBytes = getBytesPerFrame() * framesToProcess; + const int32_t numSamples = framesToProcess * getSamplesPerFrame(); + const audio_format_t sourceFormat = getDeviceFormat(); + const audio_format_t destinationFormat = getFormat(); // TODO factor this out into a utility function - if (getDeviceFormat() == getFormat()) { + if (sourceFormat == destinationFormat) { memcpy(destination, wrappingBuffer.data[partIndex], numBytes); - } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16 - && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) { - AAudioConvert_pcm16ToFloat( - (const int16_t *) wrappingBuffer.data[partIndex], + } else if (sourceFormat == AUDIO_FORMAT_PCM_16_BIT + && destinationFormat == AUDIO_FORMAT_PCM_FLOAT) { + memcpy_to_float_from_i16( (float *) destination, - numSamples, - 1.0f); - } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT - && getFormat() == AAUDIO_FORMAT_PCM_I16) { - AAudioConvert_floatToPcm16( - (const float *) wrappingBuffer.data[partIndex], + (const int16_t *) wrappingBuffer.data[partIndex], + numSamples); + } else if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT + && destinationFormat == AUDIO_FORMAT_PCM_16_BIT) { + memcpy_to_i16_from_float( (int16_t *) destination, - numSamples, - 1.0f); + (const float *) wrappingBuffer.data[partIndex], + numSamples); } else { - ALOGE("Format conversion not supported!"); + ALOGE("%s() - Format conversion not supported! audio_format_t source = %u, dest = %u", + __func__, sourceFormat, destinationFormat); return AAUDIO_ERROR_INVALID_FORMAT; } destination += numBytes; diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 795ba2c87f..2ae37a55d9 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -43,9 +43,17 @@ constexpr int kRampMSec = 10; // time to apply a change in volume aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) { aaudio_result_t result = AudioStreamInternal::open(builder); if (result == AAUDIO_OK) { + result = mFlowGraph.configure(getFormat(), + getSamplesPerFrame(), + getDeviceFormat(), + getDeviceChannelCount()); + + if (result != AAUDIO_OK) { + close(); + } // Sample rate is constrained to common values by now and should not overflow. int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND; - mVolumeRamp.setLengthInFrames(numFrames); + mFlowGraph.setRampLengthInFrames(numFrames); } return result; } @@ -216,22 +224,10 @@ aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buff } int32_t numBytes = getBytesPerFrame() * framesToWrite; - // Data conversion. - float levelFrom; - float levelTo; - mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo); - - AAudioDataConverter::FormattedData source( - (void *)byteBuffer, - getFormat(), - getSamplesPerFrame()); - AAudioDataConverter::FormattedData destination( - wrappingBuffer.data[partIndex], - getDeviceFormat(), - getDeviceChannelCount()); - - AAudioDataConverter::convert(source, destination, framesToWrite, - levelFrom, levelTo); + + mFlowGraph.process((void *)byteBuffer, + wrappingBuffer.data[partIndex], + framesToWrite); byteBuffer += numBytes; framesLeft -= framesToWrite; @@ -313,6 +309,6 @@ status_t AudioStreamInternalPlay::doSetVolume() { float combinedVolume = mStreamVolume * getDuckAndMuteVolume(); ALOGD("%s() mStreamVolume * duckAndMuteVolume = %f * %f = %f", __func__, mStreamVolume, getDuckAndMuteVolume(), combinedVolume); - mVolumeRamp.setTarget(combinedVolume); + mFlowGraph.setTargetVolume(combinedVolume); return android::NO_ERROR; } diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h index 977a9090a8..cab2942865 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.h +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h @@ -21,6 +21,7 @@ #include #include "binding/AAudioServiceInterface.h" +#include "client/AAudioFlowGraph.h" #include "client/AudioStreamInternal.h" using android::sp; @@ -93,7 +94,7 @@ private: int64_t mLastFramesRead = 0; // used to prevent retrograde motion - LinearRamp mVolumeRamp; + AAudioFlowGraph mFlowGraph; }; diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp index df0db79896..8dc31d072a 100644 --- a/media/libaaudio/src/core/AAudioAudio.cpp +++ b/media/libaaudio/src/core/AAudioAudio.cpp @@ -167,7 +167,9 @@ AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, aaudio_format_t format) { AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder); - streamBuilder->setFormat(format); + // Use audio_format_t everywhere internally. + const audio_format_t internalFormat = AAudioConvert_aaudioToAndroidDataFormat(format); + streamBuilder->setFormat(internalFormat); } AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder, @@ -408,7 +410,9 @@ AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream) AAUDIO_API aaudio_format_t AAudioStream_getFormat(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - return audioStream->getFormat(); + // Use audio_format_t internally. + audio_format_t internalFormat = audioStream->getFormat(); + return AAudioConvert_androidToAAudioDataFormat(internalFormat); } AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stream, diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp index d56701ba26..bd42697aca 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.cpp +++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp @@ -48,6 +48,20 @@ void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) { mInputPreset = other.mInputPreset; } +static aaudio_result_t isFormatValid(audio_format_t format) { + switch (format) { + case AUDIO_FORMAT_DEFAULT: + case AUDIO_FORMAT_PCM_16_BIT: + case AUDIO_FORMAT_PCM_FLOAT: + break; // valid + default: + ALOGE("audioFormat not valid, audio_format_t = 0x%08x", format); + return AAUDIO_ERROR_INVALID_FORMAT; + // break; + } + return AAUDIO_OK; +} + aaudio_result_t AAudioStreamParameters::validate() const { if (mSamplesPerFrame != AAUDIO_UNSPECIFIED && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) { @@ -79,16 +93,8 @@ aaudio_result_t AAudioStreamParameters::validate() const { // break; } - switch (mAudioFormat) { - case AAUDIO_FORMAT_UNSPECIFIED: - case AAUDIO_FORMAT_PCM_I16: - case AAUDIO_FORMAT_PCM_FLOAT: - break; // valid - default: - ALOGE("audioFormat not valid = %d", mAudioFormat); - return AAUDIO_ERROR_INVALID_FORMAT; - // break; - } + aaudio_result_t result = isFormatValid (mAudioFormat); + if (result != AAUDIO_OK) return result; if (mSampleRate != AAUDIO_UNSPECIFIED && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) { diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h index ce5dacd0ad..6beb4b2469 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.h +++ b/media/libaaudio/src/core/AAudioStreamParameters.h @@ -56,11 +56,11 @@ public: mSamplesPerFrame = samplesPerFrame; } - aaudio_format_t getFormat() const { + audio_format_t getFormat() const { return mAudioFormat; } - void setFormat(aaudio_format_t audioFormat) { + void setFormat(audio_format_t audioFormat) { mAudioFormat = audioFormat; } @@ -120,8 +120,11 @@ public: mSessionId = sessionId; } + /** + * @return bytes per frame of getFormat() + */ int32_t calculateBytesPerFrame() const { - return getSamplesPerFrame() * AAudioConvert_formatToSizeInBytes(getFormat()); + return getSamplesPerFrame() * audio_bytes_per_sample(getFormat()); } /** @@ -139,7 +142,7 @@ private: int32_t mSampleRate = AAUDIO_UNSPECIFIED; int32_t mDeviceId = AAUDIO_UNSPECIFIED; aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED; - aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED; + audio_format_t mAudioFormat = AUDIO_FORMAT_DEFAULT; aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT; aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED; aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h index 31b895c335..60200b255b 100644 --- a/media/libaaudio/src/core/AudioStream.h +++ b/media/libaaudio/src/core/AudioStream.h @@ -192,7 +192,7 @@ public: return mSampleRate; } - aaudio_format_t getFormat() const { + audio_format_t getFormat() const { return mFormat; } @@ -249,21 +249,14 @@ public: * This is only valid after setFormat() has been called. */ int32_t getBytesPerSample() const { - return AAudioConvert_formatToSizeInBytes(mFormat); + return audio_bytes_per_sample(mFormat); } /** * This is only valid after setSamplesPerFrame() and setDeviceFormat() have been called. */ int32_t getBytesPerDeviceFrame() const { - return mSamplesPerFrame * getBytesPerDeviceSample(); - } - - /** - * This is only valid after setDeviceFormat() has been called. - */ - int32_t getBytesPerDeviceSample() const { - return AAudioConvert_formatToSizeInBytes(getDeviceFormat()); + return getSamplesPerFrame() * audio_bytes_per_sample(getDeviceFormat()); } virtual int64_t getFramesWritten() = 0; @@ -478,18 +471,18 @@ protected: /** * This should not be called after the open() call. */ - void setFormat(aaudio_format_t format) { + void setFormat(audio_format_t format) { mFormat = format; } /** * This should not be called after the open() call. */ - void setDeviceFormat(aaudio_format_t format) { + void setDeviceFormat(audio_format_t format) { mDeviceFormat = format; } - aaudio_format_t getDeviceFormat() const { + audio_format_t getDeviceFormat() const { return mDeviceFormat; } @@ -565,7 +558,7 @@ private: int32_t mDeviceId = AAUDIO_UNSPECIFIED; aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED; bool mSharingModeMatchRequired = false; // must match sharing mode requested - aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED; + audio_format_t mFormat = AUDIO_FORMAT_DEFAULT; aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED; aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE; @@ -577,7 +570,7 @@ private: // Sometimes the hardware is operating with a different format from the app. // Then we require conversion in AAudio. - aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED; + audio_format_t mDeviceFormat = AUDIO_FORMAT_INVALID; // callback ---------------------------------- diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h index 494edbc2bb..8e78554434 100644 --- a/media/libaaudio/src/legacy/AudioStreamLegacy.h +++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h @@ -77,7 +77,6 @@ public: virtual int64_t incrementClientFrameCounter(int32_t frames) = 0; - virtual int64_t getFramesWritten() override { return mFramesWritten.get(); } diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp index 505f2ee64e..dbf00a926b 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp +++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp @@ -81,8 +81,8 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) } // Preserve behavior of API 26 - if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) { - setFormat(AAUDIO_FORMAT_PCM_FLOAT); + if (getFormat() == AUDIO_FORMAT_DEFAULT) { + setFormat(AUDIO_FORMAT_PCM_FLOAT); } // Maybe change device format to get a FAST path. @@ -99,12 +99,12 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) // We just may not get a FAST track. // But we wouldn't have anyway without this hack. constexpr int32_t kMostLikelySampleRateForFast = 48000; - if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT + if (getFormat() == AUDIO_FORMAT_PCM_FLOAT && perfMode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY && (samplesPerFrame <= 2) // FAST only for mono and stereo && (getSampleRate() == kMostLikelySampleRateForFast || getSampleRate() == AAUDIO_UNSPECIFIED)) { - setDeviceFormat(AAUDIO_FORMAT_PCM_I16); + setDeviceFormat(AUDIO_FORMAT_PCM_16_BIT); } else { setDeviceFormat(getFormat()); } @@ -147,8 +147,7 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) // ----------- open the AudioRecord --------------------- // Might retry, but never more than once. for (int i = 0; i < 2; i ++) { - audio_format_t requestedInternalFormat = - AAudioConvert_aaudioToAndroidDataFormat(getDeviceFormat()); + const audio_format_t requestedInternalFormat = getDeviceFormat(); mAudioRecord = new AudioRecord( mOpPackageName // const String16& opPackageName TODO does not compile @@ -214,8 +213,8 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) } // Allocate format conversion buffer if needed. - if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16 - && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) { + if (getDeviceFormat() == AUDIO_FORMAT_PCM_16_BIT + && getFormat() == AUDIO_FORMAT_PCM_FLOAT) { if (builder.getDataCallbackProc() != nullptr) { // If we have a callback then we need to convert the data into an internal float diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index 505cd77271..1572f0d28b 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -88,9 +88,9 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) int32_t notificationFrames = 0; - audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) + const audio_format_t format = (getFormat() == AUDIO_FORMAT_DEFAULT) ? AUDIO_FORMAT_PCM_FLOAT - : AAudioConvert_aaudioToAndroidDataFormat(getFormat()); + : getFormat(); // Setup the callback if there is one. AudioTrack::callback_t callback = nullptr; @@ -178,10 +178,8 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) // Get the actual values from the AudioTrack. setSamplesPerFrame(mAudioTrack->channelCount()); - aaudio_format_t aaudioFormat = - AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format()); - setFormat(aaudioFormat); - setDeviceFormat(aaudioFormat); + setFormat(mAudioTrack->format()); + setDeviceFormat(mAudioTrack->format()); int32_t actualSampleRate = mAudioTrack->getSampleRate(); ALOGW_IF(actualSampleRate != getSampleRate(), diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp index 40ebb76864..f5b3ad4fc1 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.cpp +++ b/media/libaaudio/src/utility/AAudioUtilities.cpp @@ -33,395 +33,6 @@ using namespace android; -// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data. -// It is designed to allow occasional transient peaks. -#define MAX_HEADROOM (1.41253754f) -#define MIN_HEADROOM (0 - MAX_HEADROOM) - -int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format) { - int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT; - switch (format) { - case AAUDIO_FORMAT_PCM_I16: - size = sizeof(int16_t); - break; - case AAUDIO_FORMAT_PCM_FLOAT: - size = sizeof(float); - break; - default: - break; - } - return size; -} - -// TODO expose and call clamp16_from_float function in primitives.h -static inline int16_t clamp16_from_float(float f) { - static const float scale = 1 << 15; - return (int16_t) roundf(fmaxf(fminf(f * scale, scale - 1.f), -scale)); -} - -// Clip to valid range of a float sample to prevent excessive volume. -// By using fmin and fmax we also protect against NaN. -static float clipToMinMaxHeadroom(float input) { - return fmin(MAX_HEADROOM, fmax(MIN_HEADROOM, input)); -} - -static float clipAndClampFloatToPcm16(float sample, float scaler) { - // Clip to valid range of a float sample to prevent excessive volume. - sample = clipToMinMaxHeadroom(sample); - - // Scale and convert to a short. - float fval = sample * scaler; - return clamp16_from_float(fval); -} - -void AAudioConvert_floatToPcm16(const float *source, - int16_t *destination, - int32_t numSamples, - float amplitude) { - const float scaler = amplitude; - for (int i = 0; i < numSamples; i++) { - float sample = *source++; - *destination++ = clipAndClampFloatToPcm16(sample, scaler); - } -} - -void AAudioConvert_floatToPcm16(const float *source, - int16_t *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2) { - float scaler = amplitude1; - // divide by numFrames so that we almost reach amplitude2 - float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) { - float sample = *source++; - *destination++ = clipAndClampFloatToPcm16(sample, scaler); - } - scaler += delta; - } -} - -#define SHORT_SCALE 32768 - -void AAudioConvert_pcm16ToFloat(const int16_t *source, - float *destination, - int32_t numSamples, - float amplitude) { - const float scaler = amplitude / SHORT_SCALE; - for (int i = 0; i < numSamples; i++) { - destination[i] = source[i] * scaler; - } -} - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudioConvert_pcm16ToFloat(const int16_t *source, - float *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2) { - float scaler = amplitude1 / SHORT_SCALE; - const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames); - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) { - *destination++ = *source++ * scaler; - } - scaler += delta; - } -} - - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudio_linearRamp(const float *source, - float *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2) { - float scaler = amplitude1; - const float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) { - float sample = *source++; - // Clip to valid range of a float sample to prevent excessive volume. - sample = clipToMinMaxHeadroom(sample); - - *destination++ = sample * scaler; - } - scaler += delta; - } -} - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudio_linearRamp(const int16_t *source, - int16_t *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2) { - // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768. - float scaler = amplitude1; - const float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) { - // No need to clip because int16_t range is inherently limited. - float sample = *source++ * scaler; - *destination++ = (int16_t) roundf(sample); - } - scaler += delta; - } -} - -// ************************************************************************************* -// Convert Mono To Stereo at the same time as converting format. -void AAudioConvert_formatMonoToStereo(const float *source, - int16_t *destination, - int32_t numFrames, - float amplitude) { - const float scaler = amplitude; - for (int i = 0; i < numFrames; i++) { - float sample = *source++; - int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler); - *destination++ = sample16; - *destination++ = sample16; - } -} - -void AAudioConvert_formatMonoToStereo(const float *source, - int16_t *destination, - int32_t numFrames, - float amplitude1, - float amplitude2) { - // divide by numFrames so that we almost reach amplitude2 - const float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - const float scaler = amplitude1 + (frameIndex * delta); - const float sample = *source++; - int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler); - *destination++ = sample16; - *destination++ = sample16; - } -} - -void AAudioConvert_formatMonoToStereo(const int16_t *source, - float *destination, - int32_t numFrames, - float amplitude) { - const float scaler = amplitude / SHORT_SCALE; - for (int i = 0; i < numFrames; i++) { - float sample = source[i] * scaler; - *destination++ = sample; - *destination++ = sample; - } -} - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudioConvert_formatMonoToStereo(const int16_t *source, - float *destination, - int32_t numFrames, - float amplitude1, - float amplitude2) { - const float scaler1 = amplitude1 / SHORT_SCALE; - const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames); - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - float scaler = scaler1 + (frameIndex * delta); - float sample = source[frameIndex] * scaler; - *destination++ = sample; - *destination++ = sample; - } -} - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudio_linearRampMonoToStereo(const float *source, - float *destination, - int32_t numFrames, - float amplitude1, - float amplitude2) { - const float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - float sample = *source++; - - // Clip to valid range of a float sample to prevent excessive volume. - sample = clipToMinMaxHeadroom(sample); - - const float scaler = amplitude1 + (frameIndex * delta); - float sampleScaled = sample * scaler; - *destination++ = sampleScaled; - *destination++ = sampleScaled; - } -} - -// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0 -void AAudio_linearRampMonoToStereo(const int16_t *source, - int16_t *destination, - int32_t numFrames, - float amplitude1, - float amplitude2) { - // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768. - const float delta = (amplitude2 - amplitude1) / numFrames; - for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { - const float scaler = amplitude1 + (frameIndex * delta); - // No need to clip because int16_t range is inherently limited. - const float sample = *source++ * scaler; - int16_t sample16 = (int16_t) roundf(sample); - *destination++ = sample16; - *destination++ = sample16; - } -} - -// ************************************************************************************* -void AAudioDataConverter::convert( - const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo) { - - if (source.channelCount == 1 && destination.channelCount == 2) { - convertMonoToStereo(source, - destination, - numFrames, - levelFrom, - levelTo); - } else { - // We only support mono to stereo conversion. Otherwise source and destination - // must match. - assert(source.channelCount == destination.channelCount); - convertChannelsMatch(source, - destination, - numFrames, - levelFrom, - levelTo); - } -} - -void AAudioDataConverter::convertMonoToStereo( - const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo) { - - // The formats are validated when the stream is opened so we do not have to - // check for illegal combinations here. - if (source.format == AAUDIO_FORMAT_PCM_FLOAT) { - if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) { - AAudio_linearRampMonoToStereo( - (const float *) source.data, - (float *) destination.data, - numFrames, - levelFrom, - levelTo); - } else if (destination.format == AAUDIO_FORMAT_PCM_I16) { - if (levelFrom != levelTo) { - AAudioConvert_formatMonoToStereo( - (const float *) source.data, - (int16_t *) destination.data, - numFrames, - levelFrom, - levelTo); - } else { - AAudioConvert_formatMonoToStereo( - (const float *) source.data, - (int16_t *) destination.data, - numFrames, - levelTo); - } - } - } else if (source.format == AAUDIO_FORMAT_PCM_I16) { - if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) { - if (levelFrom != levelTo) { - AAudioConvert_formatMonoToStereo( - (const int16_t *) source.data, - (float *) destination.data, - numFrames, - levelFrom, - levelTo); - } else { - AAudioConvert_formatMonoToStereo( - (const int16_t *) source.data, - (float *) destination.data, - numFrames, - levelTo); - } - } else if (destination.format == AAUDIO_FORMAT_PCM_I16) { - AAudio_linearRampMonoToStereo( - (const int16_t *) source.data, - (int16_t *) destination.data, - numFrames, - levelFrom, - levelTo); - } - } -} - -void AAudioDataConverter::convertChannelsMatch( - const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo) { - const int32_t numSamples = numFrames * source.channelCount; - - // The formats are validated when the stream is opened so we do not have to - // check for illegal combinations here. - if (source.format == AAUDIO_FORMAT_PCM_FLOAT) { - if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) { - AAudio_linearRamp( - (const float *) source.data, - (float *) destination.data, - numFrames, - source.channelCount, - levelFrom, - levelTo); - } else if (destination.format == AAUDIO_FORMAT_PCM_I16) { - if (levelFrom != levelTo) { - AAudioConvert_floatToPcm16( - (const float *) source.data, - (int16_t *) destination.data, - numFrames, - source.channelCount, - levelFrom, - levelTo); - } else { - AAudioConvert_floatToPcm16( - (const float *) source.data, - (int16_t *) destination.data, - numSamples, - levelTo); - } - } - } else if (source.format == AAUDIO_FORMAT_PCM_I16) { - if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) { - if (levelFrom != levelTo) { - AAudioConvert_pcm16ToFloat( - (const int16_t *) source.data, - (float *) destination.data, - numFrames, - source.channelCount, - levelFrom, - levelTo); - } else { - AAudioConvert_pcm16ToFloat( - (const int16_t *) source.data, - (float *) destination.data, - numSamples, - levelTo); - } - } else if (destination.format == AAUDIO_FORMAT_PCM_I16) { - AAudio_linearRamp( - (const int16_t *) source.data, - (int16_t *) destination.data, - numFrames, - source.channelCount, - levelFrom, - levelTo); - } - } -} - status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) { // This covers the case for AAUDIO_OK and for positive results. if (result >= 0) { @@ -513,6 +124,9 @@ audio_session_t AAudioConvert_aaudioToAndroidSessionId(aaudio_session_id_t sessi audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudioFormat) { audio_format_t androidFormat; switch (aaudioFormat) { + case AAUDIO_FORMAT_UNSPECIFIED: + androidFormat = AUDIO_FORMAT_DEFAULT; + break; case AAUDIO_FORMAT_PCM_I16: androidFormat = AUDIO_FORMAT_PCM_16_BIT; break; @@ -520,16 +134,19 @@ audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudioFor androidFormat = AUDIO_FORMAT_PCM_FLOAT; break; default: - androidFormat = AUDIO_FORMAT_DEFAULT; - ALOGE("AAudioConvert_aaudioToAndroidDataFormat 0x%08X unrecognized", aaudioFormat); + androidFormat = AUDIO_FORMAT_INVALID; + ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat); break; } return androidFormat; } aaudio_format_t AAudioConvert_androidToAAudioDataFormat(audio_format_t androidFormat) { - aaudio_format_t aaudioFormat = AAUDIO_FORMAT_INVALID; + aaudio_format_t aaudioFormat; switch (androidFormat) { + case AUDIO_FORMAT_DEFAULT: + aaudioFormat = AAUDIO_FORMAT_UNSPECIFIED; + break; case AUDIO_FORMAT_PCM_16_BIT: aaudioFormat = AAUDIO_FORMAT_PCM_I16; break; @@ -538,7 +155,7 @@ aaudio_format_t AAudioConvert_androidToAAudioDataFormat(audio_format_t androidFo break; default: aaudioFormat = AAUDIO_FORMAT_INVALID; - ALOGE("AAudioConvert_androidToAAudioDataFormat 0x%08X unrecognized", androidFormat); + ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat); break; } return aaudioFormat; diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h index 4b975e89c7..dc2b198d8e 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.h +++ b/media/libaaudio/src/utility/AAudioUtilities.h @@ -44,156 +44,6 @@ aaudio_result_t AAudioConvert_androidToAAudioResult(android::status_t status); */ audio_session_t AAudioConvert_aaudioToAndroidSessionId(aaudio_session_id_t sessionId); -/** - * Convert an array of floats to an array of int16_t. - * - * @param source - * @param destination - * @param numSamples number of values in the array - * @param amplitude level between 0.0 and 1.0 - */ -void AAudioConvert_floatToPcm16(const float *source, - int16_t *destination, - int32_t numSamples, - float amplitude); - -/** - * Convert floats to int16_t and scale by a linear ramp. - * - * The ramp stops just short of reaching amplitude2 so that the next - * ramp can start at amplitude2 without causing a discontinuity. - * - * @param source - * @param destination - * @param numFrames - * @param samplesPerFrame AKA number of channels - * @param amplitude1 level at start of ramp, between 0.0 and 1.0 - * @param amplitude2 level past end of ramp, between 0.0 and 1.0 - */ -void AAudioConvert_floatToPcm16(const float *source, - int16_t *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2); - -/** - * Convert int16_t array to float array ranging from -1.0 to +1.0. - * @param source - * @param destination - * @param numSamples - */ -//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, -// float *destination); - -/** - * - * Convert int16_t array to float array ranging from +/- amplitude. - * @param source - * @param destination - * @param numSamples - * @param amplitude - */ -void AAudioConvert_pcm16ToFloat(const int16_t *source, - float *destination, - int32_t numSamples, - float amplitude); - -/** - * Convert floats to int16_t and scale by a linear ramp. - * - * The ramp stops just short of reaching amplitude2 so that the next - * ramp can start at amplitude2 without causing a discontinuity. - * - * @param source - * @param destination - * @param numFrames - * @param samplesPerFrame AKA number of channels - * @param amplitude1 level at start of ramp, between 0.0 and 1.0 - * @param amplitude2 level at end of ramp, between 0.0 and 1.0 - */ -void AAudioConvert_pcm16ToFloat(const int16_t *source, - float *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2); - -/** - * Scale floats by a linear ramp. - * - * The ramp stops just short of reaching amplitude2 so that the next - * ramp can start at amplitude2 without causing a discontinuity. - * - * @param source - * @param destination - * @param numFrames - * @param samplesPerFrame - * @param amplitude1 - * @param amplitude2 - */ -void AAudio_linearRamp(const float *source, - float *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2); - -/** - * Scale int16_t's by a linear ramp. - * - * The ramp stops just short of reaching amplitude2 so that the next - * ramp can start at amplitude2 without causing a discontinuity. - * - * @param source - * @param destination - * @param numFrames - * @param samplesPerFrame - * @param amplitude1 - * @param amplitude2 - */ -void AAudio_linearRamp(const int16_t *source, - int16_t *destination, - int32_t numFrames, - int32_t samplesPerFrame, - float amplitude1, - float amplitude2); - -class AAudioDataConverter { -public: - - struct FormattedData { - - FormattedData(void *data, aaudio_format_t format, int32_t channelCount) - : data(data) - , format(format) - , channelCount(channelCount) {} - - const void *data = nullptr; - const aaudio_format_t format = AAUDIO_FORMAT_UNSPECIFIED; - const int32_t channelCount = 1; - }; - - static void convert(const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo); - -private: - static void convertMonoToStereo(const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo); - - static void convertChannelsMatch(const FormattedData &source, - const FormattedData &destination, - int32_t numFrames, - float levelFrom, - float levelTo); -}; - /** * Calculate the number of bytes and prevent numeric overflow. * The *sizeInBytes will be set to zero if there is an error. @@ -234,12 +84,6 @@ audio_content_type_t AAudioConvert_contentTypeToInternal(aaudio_content_type_t c */ audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t preset); -/** - * @return the size of a sample of the given format in bytes or AAUDIO_ERROR_ILLEGAL_ARGUMENT - */ -int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format); - - // Note that this code may be replaced by Settings or by some other system configuration tool. #define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy" diff --git a/media/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp deleted file mode 100644 index 1714bbf213..0000000000 --- a/media/libaaudio/src/utility/LinearRamp.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LinearRamp.h" - -bool LinearRamp::isRamping() { - float target = mTarget.load(); - if (target != mLevelTo) { - // Update target. Continue from previous level. - mLevelTo = target; - mRemaining = mLengthInFrames; - return true; - } else { - return mRemaining > 0; - } -} - -bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) { - bool ramping = isRamping(); - *levelFrom = mLevelFrom; - if (ramping) { - float level; - if (frames >= mRemaining) { - level = mLevelTo; - mRemaining = 0; - } else { - // Interpolate to a point along the full ramp. - level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining); - mRemaining -= frames; - } - mLevelFrom = level; // for next ramp - *levelTo = level; - } else { - *levelTo = mLevelTo; - } - return ramping; -} \ No newline at end of file diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h deleted file mode 100644 index 2b1b8e0e65..0000000000 --- a/media/libaaudio/src/utility/LinearRamp.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAUDIO_LINEAR_RAMP_H -#define AAUDIO_LINEAR_RAMP_H - -#include -#include - -/** - * Generate segments along a linear ramp. - * The ramp target can be updated from another thread. - * When the target is updated, a new ramp is started from the current position. - * - * The first ramp starts at 0.0. - * - */ -class LinearRamp { -public: - LinearRamp() { - mTarget.store(1.0f); - } - - void setLengthInFrames(int32_t frames) { - mLengthInFrames = frames; - } - - int32_t getLengthInFrames() { - return mLengthInFrames; - } - - /** - * This may be called by another thread. - * @param target - */ - void setTarget(float target) { - mTarget.store(target); - } - - float getTarget() { - return mTarget.load(); - } - - /** - * Force the nextSegment to start from this level. - * - * WARNING: this can cause a discontinuity if called while the ramp is being used. - * Only call this when setting the initial ramp. - * - * @param level - */ - void forceCurrent(float level) { - mLevelFrom = level; - mLevelTo = level; // forces a ramp if it does not match target - } - - float getCurrent() { - return mLevelFrom; - } - - /** - * Get levels for next ramp segment. - * - * @param frames number of frames in the segment - * @param levelFrom pointer to starting amplitude - * @param levelTo pointer to ending amplitude - * @return true if ramp is still moving towards the target - */ - bool nextSegment(int32_t frames, float *levelFrom, float *levelTo); - -private: - - bool isRamping(); - - std::atomic mTarget; - - int32_t mLengthInFrames = 48000 / 100; // 10 msec at 48000 Hz - int32_t mRemaining = 0; - float mLevelFrom = 0.0f; - float mLevelTo = 0.0f; -}; - - -#endif //AAUDIO_LINEAR_RAMP_H diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index 99aa4d6945..ef272b0ad5 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -33,13 +33,6 @@ cc_test { shared_libs: ["libaaudio"], } -cc_test { - name: "test_linear_ramp", - defaults: ["libaaudio_tests_defaults"], - srcs: ["test_linear_ramp.cpp"], - shared_libs: ["libaaudio"], -} - cc_test { name: "test_open_params", defaults: ["libaaudio_tests_defaults"], diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp deleted file mode 100644 index 93226bae01..0000000000 --- a/media/libaaudio/tests/test_linear_ramp.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include - -#include "utility/AAudioUtilities.h" -#include "utility/LinearRamp.h" - -TEST(test_linear_ramp, linear_ramp_segments) { - LinearRamp ramp; - const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f }; - float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f }; - - float levelFrom = -1.0f; - float levelTo = -1.0f; - ramp.setLengthInFrames(8); - ramp.setTarget(8.0f); - - EXPECT_EQ(8, ramp.getLengthInFrames()); - - bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo); - EXPECT_EQ(1, ramping); - EXPECT_EQ(0.0f, levelFrom); - EXPECT_EQ(4.0f, levelTo); - - AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo); - EXPECT_EQ(0.0f, destination[0]); - EXPECT_EQ(1.0f, destination[1]); - EXPECT_EQ(2.0f, destination[2]); - EXPECT_EQ(3.0f, destination[3]); - - ramping = ramp.nextSegment(4, &levelFrom, &levelTo); - EXPECT_EQ(1, ramping); - EXPECT_EQ(4.0f, levelFrom); - EXPECT_EQ(8.0f, levelTo); - - AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo); - EXPECT_EQ(4.0f, destination[0]); - EXPECT_EQ(5.0f, destination[1]); - EXPECT_EQ(6.0f, destination[2]); - EXPECT_EQ(7.0f, destination[3]); - - ramping = ramp.nextSegment(4, &levelFrom, &levelTo); - EXPECT_EQ(0, ramping); - EXPECT_EQ(8.0f, levelFrom); - EXPECT_EQ(8.0f, levelTo); - - AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo); - EXPECT_EQ(8.0f, destination[0]); - EXPECT_EQ(8.0f, destination[1]); - EXPECT_EQ(8.0f, destination[2]); - EXPECT_EQ(8.0f, destination[3]); - -}; - - -TEST(test_linear_ramp, linear_ramp_forced) { - LinearRamp ramp; - const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f }; - float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f }; - - float levelFrom = -1.0f; - float levelTo = -1.0f; - ramp.setLengthInFrames(4); - ramp.setTarget(8.0f); - ramp.forceCurrent(4.0f); - EXPECT_EQ(4.0f, ramp.getCurrent()); - - bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo); - EXPECT_EQ(1, ramping); - EXPECT_EQ(4.0f, levelFrom); - EXPECT_EQ(8.0f, levelTo); - - AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo); - EXPECT_EQ(4.0f, destination[0]); - EXPECT_EQ(5.0f, destination[1]); - EXPECT_EQ(6.0f, destination[2]); - EXPECT_EQ(7.0f, destination[3]); - - ramping = ramp.nextSegment(4, &levelFrom, &levelTo); - EXPECT_EQ(0, ramping); - EXPECT_EQ(8.0f, levelFrom); - EXPECT_EQ(8.0f, levelTo); - - AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo); - EXPECT_EQ(8.0f, destination[0]); - EXPECT_EQ(8.0f, destination[1]); - EXPECT_EQ(8.0f, destination[2]); - EXPECT_EQ(8.0f, destination[3]); - -}; - -constexpr int16_t kMaxI16 = INT16_MAX; -constexpr int16_t kMinI16 = INT16_MIN; -constexpr int16_t kHalfI16 = 16384; -constexpr int16_t kTenthI16 = 3277; - -//void AAudioConvert_floatToPcm16(const float *source, -// int16_t *destination, -// int32_t numSamples, -// float amplitude); -TEST(test_linear_ramp, float_to_i16) { - const float source[] = {12345.6f, 1.0f, 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f, -12345.6f}; - constexpr size_t count = sizeof(source) / sizeof(source[0]); - int16_t destination[count]; - const int16_t expected[count] = {kMaxI16, kMaxI16, kHalfI16, kTenthI16, 0, - -kTenthI16, -kHalfI16, kMinI16, kMinI16}; - - AAudioConvert_floatToPcm16(source, destination, count, 1.0f); - for (size_t i = 0; i < count; i++) { - EXPECT_EQ(expected[i], destination[i]); - } - -} - -//void AAudioConvert_pcm16ToFloat(const int16_t *source, -// float *destination, -// int32_t numSamples, -// float amplitude); -TEST(test_linear_ramp, i16_to_float) { - const int16_t source[] = {kMaxI16, kHalfI16, kTenthI16, 0, - -kTenthI16, -kHalfI16, kMinI16}; - constexpr size_t count = sizeof(source) / sizeof(source[0]); - float destination[count]; - const float expected[count] = {(32767.0f / 32768.0f), 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f}; - - AAudioConvert_pcm16ToFloat(source, destination, count, 1.0f); - for (size_t i = 0; i < count; i++) { - EXPECT_NEAR(expected[i], destination[i], 0.0001f); - } - -} - -//void AAudio_linearRamp(const int16_t *source, -// int16_t *destination, -// int32_t numFrames, -// int32_t samplesPerFrame, -// float amplitude1, -// float amplitude2); -TEST(test_linear_ramp, ramp_i16_to_i16) { - const int16_t source[] = {1, 1, 1, 1, 1, 1, 1, 1}; - constexpr size_t count = sizeof(source) / sizeof(source[0]); - int16_t destination[count]; - // Ramp will sweep from -1 to almost +1 - const int16_t expected[count] = { - -1, // from -1.00 - -1, // from -0.75 - -1, // from -0.55, round away from zero - 0, // from -0.25, round up to zero - 0, // from 0.00 - 0, // from 0.25, round down to zero - 1, // from 0.50, round away from zero - 1 // from 0.75 - }; - - // sweep across zero to test symmetry - constexpr float amplitude1 = -1.0; - constexpr float amplitude2 = 1.0; - AAudio_linearRamp(source, destination, count, 1, amplitude1, amplitude2); - for (size_t i = 0; i < count; i++) { - EXPECT_EQ(expected[i], destination[i]); - } - -} diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp index 03490344a9..24ab65efd7 100644 --- a/services/oboeservice/AAudioServiceEndpoint.cpp +++ b/services/oboeservice/AAudioServiceEndpoint.cpp @@ -59,6 +59,7 @@ std::string AAudioServiceEndpoint::dump() const { result << " Device Id: " << getDeviceId() << "\n"; result << " Sample Rate: " << getSampleRate() << "\n"; result << " Channel Count: " << getSamplesPerFrame() << "\n"; + result << " Format: " << getFormat() << "\n"; result << " Frames Per Burst: " << mFramesPerBurst << "\n"; result << " Usage: " << getUsage() << "\n"; result << " ContentType: " << getContentType() << "\n"; diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index 01caee4352..18fcd35b61 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -111,11 +111,11 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques mRequestedDeviceId = deviceId = getDeviceId(); // Fill in config - aaudio_format_t aaudioFormat = getFormat(); - if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) { - aaudioFormat = AAUDIO_FORMAT_PCM_I16; + audio_format_t audioFormat = getFormat(); + if (audioFormat == AUDIO_FORMAT_DEFAULT || audioFormat == AUDIO_FORMAT_PCM_FLOAT) { + audioFormat = AUDIO_FORMAT_PCM_16_BIT; } - config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat); + config.format = audioFormat; int32_t aaudioSampleRate = getSampleRate(); if (aaudioSampleRate == AAUDIO_UNSPECIFIED) { @@ -230,7 +230,7 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques goto error; } mFramesPerBurst = mMmapBufferinfo.burst_size_frames; - setFormat(AAudioConvert_androidToAAudioDataFormat(config.format)); + setFormat(config.format); setSampleRate(config.sample_rate); // Scale up the burst size to meet the minimum equivalent in microseconds. @@ -250,6 +250,9 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques ", deviceId = %d, capacity = %d\n", __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity()); + ALOGD("%s() format = =x%08x, frame size = %d", + __func__, getFormat(), calculateBytesPerFrame()); + return result; error: diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp index 63b998341e..a7cd128e35 100644 --- a/services/oboeservice/AAudioServiceEndpointShared.cpp +++ b/services/oboeservice/AAudioServiceEndpointShared.cpp @@ -78,6 +78,7 @@ aaudio_result_t AAudioServiceEndpointShared::open(const aaudio::AAudioStreamRequ setSamplesPerFrame(mStreamInternal->getSamplesPerFrame()); setDeviceId(mStreamInternal->getDeviceId()); setSessionId(mStreamInternal->getSessionId()); + setFormat(AUDIO_FORMAT_PCM_FLOAT); // force for mixer mFramesPerBurst = mStreamInternal->getFramesPerBurst(); return result; diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp index b645c698ae..dbc2c2ed15 100644 --- a/services/oboeservice/AAudioServiceStreamShared.cpp +++ b/services/oboeservice/AAudioServiceStreamShared.cpp @@ -142,10 +142,10 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques // Is the request compatible with the shared endpoint? setFormat(configurationInput.getFormat()); - if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) { - setFormat(AAUDIO_FORMAT_PCM_FLOAT); - } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) { - ALOGD("%s() mAudioFormat = %d, need FLOAT", __func__, getFormat()); + if (getFormat() == AUDIO_FORMAT_DEFAULT) { + setFormat(AUDIO_FORMAT_PCM_FLOAT); + } else if (getFormat() != AUDIO_FORMAT_PCM_FLOAT) { + ALOGE("%s() audio_format_t mAudioFormat = %d, need FLOAT", __func__, getFormat()); result = AAUDIO_ERROR_INVALID_FORMAT; goto error; } @@ -154,7 +154,7 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques if (getSampleRate() == AAUDIO_UNSPECIFIED) { setSampleRate(endpoint->getSampleRate()); } else if (getSampleRate() != endpoint->getSampleRate()) { - ALOGD("%s() mSampleRate = %d, need %d", + ALOGE("%s() mSampleRate = %d, need %d", __func__, getSampleRate(), endpoint->getSampleRate()); result = AAUDIO_ERROR_INVALID_RATE; goto error; @@ -164,7 +164,7 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) { setSamplesPerFrame(endpoint->getSamplesPerFrame()); } else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) { - ALOGD("%s() mSamplesPerFrame = %d, need %d", + ALOGE("%s() mSamplesPerFrame = %d, need %d", __func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame()); result = AAUDIO_ERROR_OUT_OF_RANGE; goto error; @@ -191,8 +191,8 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques } } - ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d", - getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId()); + ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d", + __func__, getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId()); result = endpoint->registerStream(keep); if (result != AAUDIO_OK) { -- GitLab From 97ac871cd4f075b3d4b47557a81e9e05ad7762ec Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 27 Jul 2018 18:59:02 -0700 Subject: [PATCH 0262/1530] audio policy: refactor preferred route implementation Remove SessionRoute class and replace by use of preferredDevice and active state in ClientDescriptor Also fix the behavior for other clients using same strategy as client with preferred route: the preferred route is only used if no other clients using the same strategy are active without a preferred route. If all clients have a preferred route, the route of the last active client is used. Also fixed getOutputForAttr() to return correct selected device ID when dynamic policies are used. Test: CTS RoutingTest, AudioPlayRoutingNative, AudioRecordRoutingNative Test: Manual: AudioExplicitRouting, UplinkMusicSampleApp Change-Id: Id342c2241ace3e1a359ea74f4a539a395f793b86 --- .../common/include/RoutingStrategy.h | 1 + .../common/managerdefinitions/Android.mk | 1 - .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyConfig.h | 1 - .../include/ClientDescriptor.h | 19 +- .../managerdefinitions/include/SessionRoute.h | 120 ----- .../src/AudioInputDescriptor.cpp | 16 +- .../src/AudioOutputDescriptor.cpp | 16 +- .../src/ClientDescriptor.cpp | 7 +- .../managerdefinitions/src/SessionRoute.cpp | 138 ------ .../managerdefault/AudioPolicyManager.cpp | 419 +++++++++--------- .../managerdefault/AudioPolicyManager.h | 29 +- 13 files changed, 282 insertions(+), 493 deletions(-) delete mode 100644 services/audiopolicy/common/managerdefinitions/include/SessionRoute.h delete mode 100644 services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h index d38967e12f..f8a1cd6c37 100644 --- a/services/audiopolicy/common/include/RoutingStrategy.h +++ b/services/audiopolicy/common/include/RoutingStrategy.h @@ -23,6 +23,7 @@ namespace android { #define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 enum routing_strategy { + STRATEGY_NONE = -1, STRATEGY_MEDIA, STRATEGY_PHONE, STRATEGY_SONIFICATION, diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index 9b8f0956c6..5ba2d4a700 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -17,7 +17,6 @@ LOCAL_SRC_FILES:= \ src/AudioCollections.cpp \ src/EffectDescriptor.cpp \ src/SoundTriggerSession.cpp \ - src/SessionRoute.cpp \ src/VolumeCurve.cpp \ src/TypeConverter.cpp \ src/AudioSession.cpp \ diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 44662e5f67..0d1d8c3296 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -89,8 +89,10 @@ public: void stop(); void close(); - RecordClientMap& clients() { return mClients; } + RecordClientMap& clientsMap() { return mClients; } RecordClientVector getClientsForSession(audio_session_t session); + RecordClientVector clientsList(bool activeOnly = false, + audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const; private: diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index ff0201a268..4cf16a0470 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -78,7 +78,9 @@ public: audio_patch_handle_t getPatchHandle() const override; void setPatchHandle(audio_patch_handle_t handle) override; - TrackClientMap& clients() { return mClients; } + TrackClientMap& clientsMap() { return mClients; } + TrackClientVector clientsList(bool activeOnly = false, + routing_strategy strategy = STRATEGY_NONE, bool preferredDeviceOnly = false) const; sp mPort; audio_devices_t mDevice; // current device this output is routed to diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index f861b9592b..78e7ec9d68 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -28,7 +28,6 @@ #include #include #include -#include namespace android { diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 9efe57fe38..cc751b1c50 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -27,6 +27,7 @@ #include #include #include "AudioPatch.h" +#include "RoutingStrategy.h" namespace android { @@ -53,8 +54,14 @@ public: audio_attributes_t attributes() const { return mAttributes; } audio_config_base_t config() const { return mConfig; } audio_port_handle_t preferredDeviceId() const { return mPreferredDeviceId; }; + void setPreferredDeviceId(audio_port_handle_t preferredDeviceId) { + mPreferredDeviceId = preferredDeviceId; + }; void setActive(bool active) { mActive = active; } bool active() const { return mActive; } + bool hasPreferredDevice(bool activeOnly = false) const { + return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive); + } private: const audio_port_handle_t mPortId; // unique Id for this client @@ -62,7 +69,7 @@ private: const audio_session_t mSessionId; // audio session ID const audio_attributes_t mAttributes; // usage... const audio_config_base_t mConfig; - const audio_port_handle_t mPreferredDeviceId; // selected input device port ID + audio_port_handle_t mPreferredDeviceId; // selected input device port ID bool mActive; protected: @@ -75,10 +82,10 @@ class TrackClientDescriptor: public ClientDescriptor public: TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, audio_attributes_t attributes, audio_config_base_t config, - audio_port_handle_t preferredDeviceId, - audio_stream_type_t stream, audio_output_flags_t flags) : + audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, + routing_strategy strategy, audio_output_flags_t flags) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mStream(stream), mFlags(flags) {} + mStream(stream), mStrategy(strategy), mFlags(flags) {} ~TrackClientDescriptor() override = default; using ClientDescriptor::dump; @@ -86,9 +93,11 @@ public: audio_output_flags_t flags() const { return mFlags; } audio_stream_type_t stream() const { return mStream; } + routing_strategy strategy() const { return mStrategy; } private: const audio_stream_type_t mStream; + const routing_strategy mStrategy; const audio_output_flags_t mFlags; }; @@ -119,7 +128,7 @@ class SourceClientDescriptor: public TrackClientDescriptor public: SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, const sp& srcDevice, - audio_stream_type_t stream); + audio_stream_type_t stream, routing_strategy strategy); ~SourceClientDescriptor() override = default; sp patchDesc() const { return mPatchDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h deleted file mode 100644 index 32b4440452..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace android { - -class DeviceDescriptor; -class DeviceVector; - -class SessionRoute : public RefBase -{ -public: - // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the - // streamType argument - static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT; - - // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the - // source argument - - static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT; - - SessionRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - sp deviceDescriptor, - uid_t uid) - : mUid(uid), - mSession(session), - mDeviceDescriptor(deviceDescriptor), - mRefCount(0), - mActivityCount(0), - mChanged(false), - mStreamType(streamType), - mSource(source) - {} - - void log(const char* prefix); - - bool isActiveOrChanged() { - return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0)); - } - - uid_t mUid; - audio_session_t mSession; - sp mDeviceDescriptor; - - // "reference" counting - int mRefCount; // +/- on references - int mActivityCount; // +/- on start/stop - bool mChanged; - // for outputs - const audio_stream_type_t mStreamType; - // for inputs - const audio_source_t mSource; -}; - -class SessionRouteMap: public KeyedVector > -{ -public: - // These constants identify the SessionRoutMap as holding EITHER input routes, - // or output routes. An error will occur if an attempt is made to add a SessionRoute - // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a - // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT) - // and similarly for output SessionRoutes and Input SessionRouteMaps. - typedef enum - { - MAPTYPE_INPUT = 0, - MAPTYPE_OUTPUT = 1 - } session_route_map_type_t; - - explicit SessionRouteMap(session_route_map_type_t mapType) : - mMapType(mapType) - {} - - bool hasRoute(audio_session_t session); - - void removeRoute(audio_session_t session); - - int incRouteActivity(audio_session_t session); - int decRouteActivity(audio_session_t session); - bool getAndClearRouteChanged(audio_session_t session); // also clears the changed flag - void log(const char* caption); - audio_devices_t getActiveDeviceForStream(audio_stream_type_t streamType, - const DeviceVector& availableDevices); - // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the - // source argument. - // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT - // in the streamType argument. - void addRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - const sp& deviceDescriptor, - uid_t uid); - -private: - // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input) - // or outputs (mMapType == kSessionRouteMap_Output) - const session_route_map_type_t mMapType; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 2770e74341..3c7562ecef 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -352,6 +352,20 @@ RecordClientVector AudioInputDescriptor::getClientsForSession( return clients; } +RecordClientVector AudioInputDescriptor::clientsList(bool activeOnly, audio_source_t source, + bool preferredDeviceOnly) const +{ + RecordClientVector clients; + for (const auto &client : mClients) { + if ((!activeOnly || client.second->active()) + && (source == AUDIO_SOURCE_DEFAULT || source == client.second->source()) + && (!preferredDeviceOnly || client.second->hasPreferredDevice())) { + clients.push_back(client.second); + } + } + return clients; +} + status_t AudioInputDescriptor::dump(int fd) { const size_t SIZE = 256; @@ -446,7 +460,7 @@ sp AudioInputCollection::getInputForClient(audio_port_hand { for (size_t i = 0; i < size(); i++) { sp inputDesc = valueAt(i); - for (const auto& client : inputDesc->clients()) { + for (const auto& client : inputDesc->clientsMap()) { if (client.second->portId() == portId) { return inputDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 39fce4db59..a39ac6da3a 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -201,6 +201,20 @@ void AudioOutputDescriptor::toAudioPort(struct audio_port *port) const port->ext.mix.hw_module = getModuleHandle(); } +TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, routing_strategy strategy, + bool preferredDeviceOnly) const +{ + TrackClientVector clients; + for (const auto &client : mClients) { + if ((!activeOnly || client.second->active()) + && (strategy == STRATEGY_NONE || strategy == client.second->strategy()) + && (!preferredDeviceOnly || client.second->hasPreferredDevice())) { + clients.push_back(client.second); + } + } + return clients; +} + status_t AudioOutputDescriptor::dump(int fd) { const size_t SIZE = 256; @@ -742,7 +756,7 @@ sp SwAudioOutputCollection::getOutputForClient(audio_po { for (size_t i = 0; i < size(); i++) { sp outputDesc = valueAt(i); - for (const auto& client : outputDesc->clients()) { + for (const auto& client : outputDesc->clientsMap()) { if (client.second->portId() == portId) { return outputDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 5aca3cc4a9..0cf206db14 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -29,6 +29,7 @@ namespace android { + status_t ClientDescriptor::dump(int fd, int spaces, int index) { String8 out; @@ -76,9 +77,11 @@ status_t RecordClientDescriptor::dump(String8& out, int spaces, int index) SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, - const sp& srcDevice, audio_stream_type_t stream) : + const sp& srcDevice, audio_stream_type_t stream, + routing_strategy strategy) : TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, - AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, stream, AUDIO_OUTPUT_FLAG_NONE), + AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, + stream, strategy, AUDIO_OUTPUT_FLAG_NONE), mPatchDesc(patchDesc), mSrcDevice(srcDevice) { } diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp deleted file mode 100644 index 440a4e7b2b..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM_SessionRoute" -//#define LOG_NDEBUG 0 - -#include "SessionRoute.h" -#include "HwModule.h" -#include "AudioGain.h" -#include "DeviceDescriptor.h" -#include - -namespace android { - -// --- SessionRoute class implementation -void SessionRoute::log(const char* prefix) -{ - ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d", - prefix, mStreamType, mSource, mSession, - mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE, - mRefCount, mActivityCount); -} - -// --- SessionRouteMap class implementation -bool SessionRouteMap::hasRoute(audio_session_t session) -{ - return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0; -} - -bool SessionRouteMap::getAndClearRouteChanged(audio_session_t session) -{ - if (indexOfKey(session) >= 0) { - if (valueFor(session)->mChanged) { - valueFor(session)->mChanged = false; - return true; - } - } - return false; -} - -void SessionRouteMap::removeRoute(audio_session_t session) -{ - sp route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - if (route != 0) { - ALOG_ASSERT(route->mRefCount > 0); - --route->mRefCount; - if (route->mRefCount <= 0) { - removeItem(session); - } - } -} - -int SessionRouteMap::incRouteActivity(audio_session_t session) -{ - sp route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - return route != 0 ? ++(route->mActivityCount) : -1; -} - -int SessionRouteMap::decRouteActivity(audio_session_t session) -{ - sp route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - if (route != 0 && route->mActivityCount > 0) { - return --(route->mActivityCount); - } else { - return -1; - } -} - -void SessionRouteMap::log(const char* caption) -{ - ALOGI("%s ----", caption); - for (size_t index = 0; index < size(); index++) { - valueAt(index)->log(" "); - } -} - -void SessionRouteMap::addRoute(audio_session_t session, - audio_stream_type_t streamType, - audio_source_t source, - const sp& descriptor, - uid_t uid) -{ - if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) { - ALOGE("Adding Output Route to InputRouteMap"); - return; - } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) { - ALOGE("Adding Input Route to OutputRouteMap"); - return; - } - - sp route = indexOfKey(session) >= 0 ? valueFor(session) : 0; - - if (route != 0) { - if (descriptor != 0 || route->mDeviceDescriptor != 0) { - route->mChanged = true; - } - route->mRefCount++; - route->mDeviceDescriptor = descriptor; - } else { - route = new SessionRoute(session, streamType, source, descriptor, uid); - route->mRefCount++; - if (descriptor != 0) { - route->mChanged = true; - } - add(session, route); - } -} - -audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType, - const DeviceVector& availableDevices) -{ - for (size_t index = 0; index < size(); index++) { - sp route = valueAt(index); - if (streamType == route->mStreamType && route->isActiveOrChanged() - && route->mDeviceDescriptor != 0) { - audio_devices_t device = route->mDeviceDescriptor->type(); - if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) { - return device; - } - } - } - return AUDIO_DEVICE_NONE; -} - -} // namespace android diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e4f3cf12a0..cd40bc5b35 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -788,7 +788,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, DeviceVector outputDevices; routing_strategy strategy; audio_devices_t device; - audio_port_handle_t requestedDeviceId = *selectedDeviceId; + const audio_port_handle_t requestedDeviceId = *selectedDeviceId; audio_devices_t msdDevice = getMsdAudioOutDeviceTypes(); if (attr != NULL) { @@ -808,19 +808,24 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" - " session %d selectedDeviceId %d", - attributes.usage, attributes.content_type, attributes.tags, attributes.flags, - session, *selectedDeviceId); + " session %d selectedDeviceId %d", + attributes.usage, attributes.content_type, attributes.tags, attributes.flags, + session, requestedDeviceId); // TODO: check for existing client for this port ID if (*portId == AUDIO_PORT_HANDLE_NONE) { *portId = AudioPort::getNextUniqueId(); } + *stream = streamTypefromAttributesInt(&attributes); + + strategy = getStrategyForAttr(&attributes); + // First check for explicit routing (eg. setPreferredDevice) - sp deviceDesc; - if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { - deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId); + if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { + sp deviceDesc = + mAvailableOutputDevices.getDeviceFromId(requestedDeviceId); + device = deviceDesc->type(); } else { // If no explict route, is there a matching dynamic policy that applies? sp desc; @@ -831,6 +836,10 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } *stream = streamTypefromAttributesInt(&attributes); *output = desc->mIoHandle; + AudioMix *mix = desc->mPolicyMix; + sp deviceDesc = + mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress); + *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE; ALOGV("getOutputForAttr() returns output %d", *output); goto exit; } @@ -840,25 +849,9 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); return BAD_VALUE; } + device = getDeviceForStrategy(strategy, false /*fromCache*/); } - // Virtual sources must always be dynamicaly or explicitly routed - if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { - ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); - return BAD_VALUE; - } - - *stream = streamTypefromAttributesInt(&attributes); - - // TODO: Should this happen only if an explicit route is active? - // the previous code structure meant that this would always happen which - // would appear to result in adding a null deviceDesc when not using an - // explicit route. Is that the intended and necessary behavior? - mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid); - - strategy = (routing_strategy) getStrategyForAttr(&attributes); - device = getDeviceForStrategy(strategy, false /*fromCache*/); - if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } @@ -871,9 +864,10 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, *stream == AUDIO_STREAM_MUSIC && audio_is_linear_pcm(config->format) && isInCall()) { - if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { + if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC; } else { + // Get the devce type directly from the engine to bypass preferred route logic device = mEngine->getDeviceForStrategy(strategy); } } @@ -897,7 +891,6 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, *output = getOutputForDevice(device, session, *stream, config, flags); } if (*output == AUDIO_IO_HANDLE_NONE) { - mOutputRoutes.removeRoute(session); return INVALID_OPERATION; } @@ -910,10 +903,12 @@ exit: .format = config->format, .channel_mask = config->channel_mask }; sp clientDesc = - new TrackClientDescriptor(*portId, uid, session, - attributes, clientConfig, requestedDeviceId, *stream, *flags); + new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig, + requestedDeviceId, *stream, + getStrategyForAttr(&attributes), + *flags); sp outputDesc = mOutputs.valueFor(*output); - outputDesc->clients().emplace(*portId, clientDesc); + outputDesc->clientsMap().emplace(*portId, clientDesc); ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d", *output, *selectedDeviceId, *portId); @@ -1325,60 +1320,23 @@ status_t AudioPolicyManager::startOutput(audio_port_handle_t portId) ALOGW("startOutput() no output for client %d", portId); return BAD_VALUE; } - sp client = outputDesc->clients()[portId]; - audio_stream_type_t stream = client->stream(); - audio_session_t session = client->session(); + sp client = outputDesc->clientsMap()[portId]; ALOGV("startOutput() output %d, stream %d, session %d", - outputDesc->mIoHandle, stream, session); + outputDesc->mIoHandle, client->stream(), client->session()); status_t status = outputDesc->start(); if (status != NO_ERROR) { return status; } - // Routing? - mOutputRoutes.incRouteActivity(session); - - audio_devices_t newDevice; - AudioMix *policyMix = NULL; - const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; - address = policyMix->mDeviceAddress.string(); - if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { - newDevice = policyMix->mDeviceType; - } else { - newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; - } - } else if (mOutputRoutes.getAndClearRouteChanged(session)) { - newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); - if (newDevice != outputDesc->device()) { - checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle); - } - } else { - newDevice = AUDIO_DEVICE_NONE; - } - - uint32_t delayMs = 0; - - status = startSource(outputDesc, stream, newDevice, address, &delayMs); + uint32_t delayMs; + status = startSource(outputDesc, client, &delayMs); if (status != NO_ERROR) { - mOutputRoutes.decRouteActivity(session); outputDesc->stop(); return status; } - // Automatically enable the remote submix input when output is started on a re routing mix - // of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(newDevice) && policyMix != NULL && - policyMix->mMixType == MIX_TYPE_RECORDERS) { - setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address, - "remote-submix"); - } - if (delayMs != 0) { usleep(delayMs * 1000); } @@ -1386,16 +1344,15 @@ status_t AudioPolicyManager::startOutput(audio_port_handle_t portId) return status; } -status_t AudioPolicyManager::startSource(const sp& outputDesc, - audio_stream_type_t stream, - audio_devices_t device, - const char *address, - uint32_t *delayMs) +status_t AudioPolicyManager::startSource(const sp& outputDesc, + const sp& client, + uint32_t *delayMs) { // cannot start playback of STREAM_TTS if any other output is being used uint32_t beaconMuteLatency = 0; *delayMs = 0; + audio_stream_type_t stream = client->stream(); if (stream == AUDIO_STREAM_TTS) { ALOGV("\t found BEACON stream"); if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { @@ -1413,6 +1370,19 @@ status_t AudioPolicyManager::startSource(const sp& output bool force = !outputDesc->isActive() && (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE); + audio_devices_t device = AUDIO_DEVICE_NONE; + AudioMix *policyMix = NULL; + const char *address = NULL; + if (outputDesc->mPolicyMix != NULL) { + policyMix = outputDesc->mPolicyMix; + address = policyMix->mDeviceAddress.string(); + if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { + device = policyMix->mDeviceType; + } else { + device = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + } + // requiresMuteCheck is false when we can bypass mute strategy. // It covers a common case when there is no materially active audio // and muting would result in unnecessary delay and dropped audio. @@ -1423,6 +1393,14 @@ status_t AudioPolicyManager::startSource(const sp& output // NOTE that the usage count is the same for duplicated output and hardware output which is // necessary for a correct control of hardware output routing by startOutput() and stopOutput() outputDesc->changeRefCount(stream, 1); + client->setActive(true); + + if (client->hasPreferredDevice(true)) { + device = getNewOutputDevice(outputDesc, false /*fromCache*/); + if (device != outputDesc->device()) { + checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle); + } + } if (stream == AUDIO_STREAM_MUSIC) { selectOutputForMusicEffects(); @@ -1514,6 +1492,16 @@ status_t AudioPolicyManager::startSource(const sp& output setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc); } + // Automatically enable the remote submix input when output is started on a re routing mix + // of type MIX_TYPE_RECORDERS + if (audio_is_remote_submix_device(device) && policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address, + "remote-submix"); + } + return NO_ERROR; } @@ -1526,37 +1514,12 @@ status_t AudioPolicyManager::stopOutput(audio_port_handle_t portId) ALOGW("stopOutput() no output for client %d", portId); return BAD_VALUE; } - sp client = outputDesc->clients()[portId]; - audio_stream_type_t stream = client->stream(); - audio_session_t session = client->session(); - - ALOGV("stopOutput() output %d, stream %d, session %d", outputDesc->mIoHandle, stream, session); - - if (outputDesc->mRefCount[stream] == 1) { - // Automatically disable the remote submix input when output is stopped on a - // re routing mix of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(outputDesc->mDevice) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { - setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, - "remote-submix"); - } - } + sp client = outputDesc->clientsMap()[portId]; - // Routing? - bool forceDeviceUpdate = false; - if (outputDesc->mRefCount[stream] > 0) { - int activityCount = mOutputRoutes.decRouteActivity(session); - forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0)); - - if (forceDeviceUpdate) { - checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE); - } - } + ALOGV("stopOutput() output %d, stream %d, session %d", + outputDesc->mIoHandle, client->stream(), client->session()); - status_t status = stopSource(outputDesc, stream, forceDeviceUpdate); + status_t status = stopSource(outputDesc, client); if (status == NO_ERROR ) { outputDesc->stop(); @@ -1564,16 +1527,36 @@ status_t AudioPolicyManager::stopOutput(audio_port_handle_t portId) return status; } -status_t AudioPolicyManager::stopSource(const sp& outputDesc, - audio_stream_type_t stream, - bool forceDeviceUpdate) +status_t AudioPolicyManager::stopSource(const sp& outputDesc, + const sp& client) { // always handle stream stop, check which stream type is stopping + audio_stream_type_t stream = client->stream(); + handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); if (outputDesc->mRefCount[stream] > 0) { + if (outputDesc->mRefCount[stream] == 1) { + // Automatically disable the remote submix input when output is stopped on a + // re routing mix of type MIX_TYPE_RECORDERS + if (audio_is_remote_submix_device(outputDesc->mDevice) && + outputDesc->mPolicyMix != NULL && + outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + outputDesc->mPolicyMix->mDeviceAddress, + "remote-submix"); + } + } + bool forceDeviceUpdate = false; + if (client->hasPreferredDevice(true)) { + checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE); + forceDeviceUpdate = true; + } + // decrement usage count of this stream on the output outputDesc->changeRefCount(stream, -1); + client->setActive(false); // store time at which the stream was stopped - see isStreamActive() if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) { @@ -1636,14 +1619,10 @@ void AudioPolicyManager::releaseOutput(audio_port_handle_t portId) ALOGW("releaseOutput() no output for client %d", portId); return; } - sp client = outputDesc->clients()[portId]; - audio_session_t session = client->session(); + sp client = outputDesc->clientsMap()[portId]; ALOGV("releaseOutput() %d", outputDesc->mIoHandle); - // Routing - mOutputRoutes.removeRoute(session); - if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { if (outputDesc->mDirectOpenCount <= 0) { ALOGW("releaseOutput() invalid open count %d for output %d", @@ -1655,7 +1634,7 @@ void AudioPolicyManager::releaseOutput(audio_port_handle_t portId) mpClientInterface->onAudioPortListUpdate(); } } - outputDesc->clients().erase(portId); + outputDesc->clientsMap().erase(portId); } @@ -1693,7 +1672,6 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId); } - mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid); // special case for mmap capture: if an input IO handle is specified, we reuse this input if // possible @@ -1763,7 +1741,11 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; address = String8(attr->tags + strlen("addr=")); } else { - device = getDeviceAndMixForInputSource(inputSource, &policyMix); + if (deviceDesc != 0) { + device = deviceDesc->type(); + } else { + device = getDeviceAndMixForInputSource(inputSource, &policyMix); + } if (device == AUDIO_DEVICE_NONE) { ALOGW("getInputForAttr() could not find device for source %d", inputSource); status = BAD_VALUE; @@ -1808,7 +1790,7 @@ exit: clientDesc = new RecordClientDescriptor(*portId, uid, session, *attr, *config, requestedDeviceId, inputSource, flags); inputDesc = mInputs.valueFor(*input); - inputDesc->clients().emplace(*portId, clientDesc); + inputDesc->clientsMap().emplace(*portId, clientDesc); ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d", *input, *inputType, *selectedDeviceId, *portId); @@ -1816,7 +1798,6 @@ exit: return NO_ERROR; error: - mInputRoutes.removeRoute(session); return status; } @@ -2027,7 +2008,7 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, ALOGW("startInput() no input for client %d", portId); return BAD_VALUE; } - sp client = inputDesc->clients()[portId]; + sp client = inputDesc->clientsMap()[portId]; audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; @@ -2137,11 +2118,9 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection inputDesc->changeRefCount(session, 1); + client->setActive(true); - // Routing? - mInputRoutes.incRouteActivity(session); - - if (audioSession->activeCount() == 1 || mInputRoutes.getAndClearRouteChanged(session)) { + if (audioSession->activeCount() == 1 || client->hasPreferredDevice(true)) { // indicate active capture to sound trigger service if starting capture from a mic on // primary HW module audio_devices_t device = getNewInputDevice(inputDesc); @@ -2149,8 +2128,8 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, status_t status = inputDesc->start(); if (status != NO_ERROR) { - mInputRoutes.decRouteActivity(session); inputDesc->changeRefCount(session, -1); + client->setActive(false); return status; } @@ -2201,7 +2180,7 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) ALOGW("stopInput() no input for client %d", portId); return BAD_VALUE; } - sp client = inputDesc->clients()[portId]; + sp client = inputDesc->clientsMap()[portId]; audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; @@ -2215,9 +2194,7 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) } inputDesc->changeRefCount(session, -1); - - // Routing? - mInputRoutes.decRouteActivity(session); + client->setActive(false); if (audioSession->activeCount() == 0) { inputDesc->stop(); @@ -2272,15 +2249,12 @@ void AudioPolicyManager::releaseInput(audio_port_handle_t portId) ALOGW("releaseInput() no input for client %d", portId); return; } - sp client = inputDesc->clients()[portId]; + sp client = inputDesc->clientsMap()[portId]; audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; ALOGV("releaseInput() %d", input); - // Routing - mInputRoutes.removeRoute(session); - sp audioSession = inputDesc->getAudioSession(session); if (audioSession == 0) { ALOGW("releaseInput() unknown session %d on input %d", session, input); @@ -2303,7 +2277,7 @@ void AudioPolicyManager::releaseInput(audio_port_handle_t portId) } closeInput(input); - inputDesc->clients().erase(portId); + inputDesc->clientsMap().erase(portId); mpClientInterface->onAudioPortListUpdate(); ALOGV("releaseInput() exit"); } @@ -2342,7 +2316,6 @@ void AudioPolicyManager::closeAllInputs() { } inputDesc->close(); } - mInputRoutes.clear(); mInputs.clear(); SoundTrigger::setCaptureState(false); nextAudioPortGeneration(); @@ -2619,12 +2592,13 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) sp rSubmixModule; // examine each mix's route type for (size_t i = 0; i < mixes.size(); i++) { + AudioMix mix = mixes[i]; // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination - if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) { + if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) { res = INVALID_OPERATION; break; } - if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) { + if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) { ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size()); if (rSubmixModule == 0) { rSubmixModule = mHwModules.getModuleFromName( @@ -2637,15 +2611,20 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) } } - String8 address = mixes[i].mDeviceAddress; + String8 address = mix.mDeviceAddress; + if (mix.mMixType == MIX_TYPE_PLAYERS) { + mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } else { + mix.mDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } - if (mPolicyMixes.registerMix(address, mixes[i], 0 /*output desc*/) != NO_ERROR) { + if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) { ALOGE(" Error registering mix %zu for address %s", i, address.string()); res = INVALID_OPERATION; break; } - audio_config_t outputConfig = mixes[i].mFormat; - audio_config_t inputConfig = mixes[i].mFormat; + audio_config_t outputConfig = mix.mFormat; + audio_config_t inputConfig = mix.mFormat; // NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in // stereo and let audio flinger do the channel conversion if needed. outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO; @@ -2655,7 +2634,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) rSubmixModule->addInputProfile(address, &inputConfig, AUDIO_DEVICE_IN_REMOTE_SUBMIX, address); - if (mixes[i].mMixType == MIX_TYPE_PLAYERS) { + if (mix.mMixType == MIX_TYPE_PLAYERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.string(), "remote-submix"); @@ -2664,9 +2643,9 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.string(), "remote-submix"); } - } else if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { - String8 address = mixes[i].mDeviceAddress; - audio_devices_t device = mixes[i].mDeviceType; + } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { + String8 address = mix.mDeviceAddress; + audio_devices_t device = mix.mDeviceType; ALOGV(" registerPolicyMixes() mix %zu of %zu is RENDER, dev=0x%X addr=%s", i, mixes.size(), device, address.string()); @@ -2679,7 +2658,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) && (patch->mPatch.sinks[0].ext.device.type == device) && (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) { - if (mPolicyMixes.registerMix(address, mixes[i], desc) != NO_ERROR) { + if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) { res = INVALID_OPERATION; } else { foundOutput = true; @@ -3391,12 +3370,13 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) { // remove output routes associated with this uid SortedVector affectedStrategies; - for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--) { - sp route = mOutputRoutes.valueAt(i); - if (route->mUid == uid) { - mOutputRoutes.removeItemsAt(i); - if (route->mDeviceDescriptor != 0) { - affectedStrategies.add(getStrategy(route->mStreamType)); + for (size_t i = 0; i < mOutputs.size(); i++) { + sp outputDesc = mOutputs.valueAt(i); + TrackClientMap clients = outputDesc->clientsMap(); + for (const auto& client : clients) { + if (client.second->hasPreferredDevice() && client.second->uid() == uid) { + client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE); + affectedStrategies.add(getStrategy(client.second->stream())); } } } @@ -3407,12 +3387,13 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) // remove input routes associated with this uid SortedVector affectedSources; - for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--) { - sp route = mInputRoutes.valueAt(i); - if (route->mUid == uid) { - mInputRoutes.removeItemsAt(i); - if (route->mDeviceDescriptor != 0) { - affectedSources.add(route->mSource); + for (size_t i = 0; i < mInputs.size(); i++) { + sp inputDesc = mInputs.valueAt(i); + RecordClientMap clients = inputDesc->clientsMap(); + for (const auto& client : clients) { + if (client.second->hasPreferredDevice() && client.second->uid() == uid) { + client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE); + affectedSources.add(client.second->source()); } } } @@ -3486,7 +3467,8 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so sp sourceDesc = new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc, - streamTypefromAttributesInt(attributes)); + streamTypefromAttributesInt(attributes), + getStrategyForAttr(attributes)); status_t status = connectAudioSource(sourceDesc); if (status == NO_ERROR) { @@ -3503,7 +3485,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp disconnectAudioSource(sourceDesc); audio_attributes_t attributes = sourceDesc->attributes(); - routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); + routing_strategy strategy = getStrategyForAttr(&attributes); audio_stream_type_t stream = sourceDesc->stream(); sp srcDeviceDesc = sourceDesc->srcDevice(); @@ -3557,7 +3539,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp return INVALID_OPERATION; } uint32_t delayMs = 0; - status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs); + status = startSource(outputDesc, sourceDesc, &delayMs); if (status != NO_ERROR) { mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0); @@ -3931,10 +3913,9 @@ status_t AudioPolicyManager::disconnectAudioSource(const sppatchDesc()->mHandle); - audio_stream_type_t stream = sourceDesc->stream(); sp swOutputDesc = sourceDesc->swOutput().promote(); if (swOutputDesc != 0) { - status_t status = stopSource(swOutputDesc, stream, false); + status_t status = stopSource(swOutputDesc, sourceDesc); if (status == NO_ERROR) { swOutputDesc->stop(); } @@ -3958,8 +3939,7 @@ sp AudioPolicyManager::getSourceForStrategyOnOutput( for (size_t i = 0; i < mAudioSources.size(); i++) { sp sourceDesc = mAudioSources.valueAt(i); audio_attributes_t attributes = sourceDesc->attributes(); - routing_strategy sourceStrategy = - (routing_strategy) getStrategyForAttr(&attributes); + routing_strategy sourceStrategy = getStrategyForAttr(&attributes); sp outputDesc = sourceDesc->swOutput().promote(); if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) { source = sourceDesc; @@ -4936,11 +4916,41 @@ void AudioPolicyManager::checkA2dpSuspend() } } +template +sp AudioPolicyManager::findPreferredDevice( + IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices) +{ + auto activeClients = desc->clientsList(true /*activeOnly*/); + auto activeClientsWithRoute = + desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/); + active = activeClients.size() > 0; + if (active && activeClients.size() == activeClientsWithRoute.size()) { + return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId()); + } + return nullptr; +} + +template +sp AudioPolicyManager::findPreferredDevice( + IoCollection& ioCollection, Filter filter, const DeviceVector& devices) +{ + sp device; + for (size_t i = 0; i < ioCollection.size(); i++) { + auto desc = ioCollection.valueAt(i); + bool active; + sp curDevice = findPreferredDevice(desc, filter, active, devices); + if (active && curDevice == nullptr) { + return nullptr; + } else if (curDevice != nullptr) { + device = curDevice; + } + } + return device; +} + audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp& outputDesc, bool fromCache) { - audio_devices_t device = AUDIO_DEVICE_NONE; - ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); @@ -4951,18 +4961,13 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(const spisStreamActive((audio_stream_type_t)stream)) { - audio_devices_t forcedDevice = - mOutputRoutes.getActiveDeviceForStream( - (audio_stream_type_t)stream, mAvailableOutputDevices); - - if (forcedDevice != AUDIO_DEVICE_NONE) { - return forcedDevice; - } - } + // Honor explicit routing requests only if no client using default routing is active on this + // input: a specific app can not force routing for other apps by setting a preferred device. + bool active; // unused + sp deviceDesc = + findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices); + if (deviceDesc != nullptr) { + return deviceDesc->type(); } // check the following by order of priority to request a routing change if necessary: @@ -4988,6 +4993,7 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(const spgetForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); @@ -5030,6 +5036,15 @@ audio_devices_t AudioPolicyManager::getNewInputDevice(const sp deviceDesc = + findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices); + if (deviceDesc != nullptr) { + return deviceDesc->type(); + } + // If we are not in call and no client is active on this input, this methods returns // AUDIO_DEVICE_NONE, causing the patch on the input stream to be released. audio_source_t source = inputDesc->getHighestPrioritySource(true /*activeOnly*/); @@ -5091,16 +5106,16 @@ routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) con return mEngine->getStrategyForStream(stream); } -uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { +routing_strategy AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { // flags to strategy mapping if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { - return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER; + return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; } if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { - return (uint32_t) STRATEGY_ENFORCED_AUDIBLE; + return STRATEGY_ENFORCED_AUDIBLE; } // usage to strategy mapping - return static_cast(mEngine->getStrategyForUsage(attr->usage)); + return mEngine->getStrategyForUsage(attr->usage); } void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) { @@ -5176,17 +5191,11 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) { audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, bool fromCache) { - // Check if an explicit routing request exists for a stream type corresponding to the - // specified strategy and use it in priority over default routing rules. - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - if (getStrategy((audio_stream_type_t)stream) == strategy) { - audio_devices_t forcedDevice = - mOutputRoutes.getActiveDeviceForStream( - (audio_stream_type_t)stream, mAvailableOutputDevices); - if (forcedDevice != AUDIO_DEVICE_NONE) { - return forcedDevice; - } - } + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp deviceDesc = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices); + if (deviceDesc != nullptr) { + return deviceDesc->type(); } if (fromCache) { @@ -5530,6 +5539,15 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, AudioMix **policyMix) { + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp deviceDesc = + findPreferredDevice(mInputs, inputSource, mAvailableInputDevices); + if (deviceDesc != nullptr) { + return deviceDesc->type(); + } + + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; audio_devices_t selectedDeviceFromMix = mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix); @@ -5542,20 +5560,7 @@ audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) { - // Routing - // Scan the whole RouteMap to see if we have an explicit route: - // if the input source in the RouteMap is the same as the argument above, - // and activity count is non-zero and the device in the route descriptor is available - // then select this device. - for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) { - sp route = mInputRoutes.valueAt(routeIndex); - if ((inputSource == route->mSource) && route->isActiveOrChanged() && - (mAvailableInputDevices.indexOf(route->mDeviceDescriptor) >= 0)) { - return route->mDeviceDescriptor->type(); - } - } - - return mEngine->getDeviceForInputSource(inputSource); + return mEngine->getDeviceForInputSource(inputSource); } float AudioPolicyManager::computeVolume(audio_stream_type_t stream, @@ -5860,7 +5865,7 @@ bool AudioPolicyManager::isStrategyActive(const sp& outpu } for (int i = 0; i < (int)AUDIO_STREAM_FOR_POLICY_CNT; i++) { if (((getStrategy((audio_stream_type_t)i) == strategy) || - (NUM_STRATEGIES == strategy)) && + (STRATEGY_NONE == strategy)) && outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { return true; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 943676772d..cc5739bb86 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -49,7 +49,6 @@ #include #include #include -#include #include namespace android { @@ -156,7 +155,7 @@ public: // return the strategy corresponding to a given stream type virtual uint32_t getStrategyForStream(audio_stream_type_t stream); // return the strategy corresponding to the given audio attributes - virtual uint32_t getStrategyForAttr(const audio_attributes_t *attr); + virtual routing_strategy getStrategyForAttr(const audio_attributes_t *attr); // return the enabled output devices for the given stream type virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); @@ -366,7 +365,7 @@ protected: bool on, const sp& outputDesc, int delayMs = 0, - audio_devices_t device = (audio_devices_t)0); + audio_devices_t device = AUDIO_DEVICE_NONE); // Mute or unmute the stream on the specified output void setStreamMute(audio_stream_type_t stream, @@ -422,6 +421,14 @@ protected: // manages A2DP output suspend/restore according to phone state and BT SCO usage void checkA2dpSuspend(); + template + sp findPreferredDevice(IoDescriptor& desc, Filter filter, + bool& active, const DeviceVector& devices); + + template + sp findPreferredDevice(IoCollection& ioCollection, Filter filter, + const DeviceVector& devices); + // selects the most appropriate device on output for current state // must be called every time a condition that affects the device choice for a given output is // changed: connected device, phone state, force use, output start, output stop.. @@ -508,16 +515,11 @@ protected: sp findDevice( const DeviceVector& devices, audio_devices_t device) const; - // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force - // the re-evaluation of the output device. - status_t startSource(const sp& outputDesc, - audio_stream_type_t stream, - audio_devices_t device, - const char *address, + status_t startSource(const sp& outputDesc, + const sp& client, uint32_t *delayMs); - status_t stopSource(const sp& outputDesc, - audio_stream_type_t stream, - bool forceDeviceUpdate); + status_t stopSource(const sp& outputDesc, + const sp& client); void clearAudioPatches(uid_t uid); void clearSessionRoutes(uid_t uid); @@ -561,9 +563,6 @@ protected: DeviceVector mAvailableOutputDevices; // all available output devices DeviceVector mAvailableInputDevices; // all available input devices - SessionRouteMap mOutputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_OUTPUT); - SessionRouteMap mInputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_INPUT); - bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; float mLastVoiceVolume; // last voice volume value sent to audio HAL -- GitLab From 8f42ea1fd1c813d9ed97ade72d7514e883cf7cb9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 8 Aug 2018 09:08:25 -0700 Subject: [PATCH 0263/1530] audio policy: remove AudioSession class Now that clients are tracked individually, tracking by audio session is no needed anymore. Modify management of client activity, preempted sound trigger sessions and silenced uids to rely on RecordClientDescriptor instead of AudioSession. Test: CTS test for AudioRecord Test: manual capture audio tests for all use cases Change-Id: Ieef3e05daea98aec9320412393f912bd6bd7e046 --- .../common/managerdefinitions/Android.mk | 1 - .../include/AudioInputDescriptor.h | 33 +- .../managerdefinitions/include/AudioSession.h | 96 ---- .../include/ClientDescriptor.h | 9 +- .../src/AudioInputDescriptor.cpp | 146 +++--- .../managerdefinitions/src/AudioSession.cpp | 230 ---------- .../src/ClientDescriptor.cpp | 1 - .../managerdefault/AudioPolicyManager.cpp | 425 ++++++++---------- .../managerdefault/AudioPolicyManager.h | 9 +- 9 files changed, 255 insertions(+), 695 deletions(-) delete mode 100644 services/audiopolicy/common/managerdefinitions/include/AudioSession.h delete mode 100644 services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk index 5ba2d4a700..bacb78005d 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -19,7 +19,6 @@ LOCAL_SRC_FILES:= \ src/SoundTriggerSession.cpp \ src/VolumeCurve.cpp \ src/TypeConverter.cpp \ - src/AudioSession.cpp \ src/ClientDescriptor.cpp LOCAL_SHARED_LIBRARIES := \ diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 0d1d8c3296..72d5a8cf97 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -16,19 +16,19 @@ #pragma once -#include "AudioIODescriptorInterface.h" -#include "AudioPort.h" -#include "AudioSession.h" -#include "ClientDescriptor.h" -#include #include +#include #include #include +#include "AudioIODescriptorInterface.h" +#include "AudioPort.h" +#include "ClientDescriptor.h" namespace android { class IOProfile; class AudioMix; +class AudioPolicyClientInterface; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. @@ -39,7 +39,6 @@ public: AudioPolicyClientInterface *clientInterface); audio_port_handle_t getId() const; audio_module_handle_t getModuleHandle() const; - uint32_t getOpenRefCount() const; status_t dump(int fd); @@ -56,19 +55,13 @@ public: SortedVector getPreemptedSessions() const; bool hasPreemptedSession(audio_session_t session) const; void clearPreemptedSessions(); - bool isActive() const; + bool isActive() const { return mGlobalActiveCount > 0; } bool isSourceActive(audio_source_t source) const; audio_source_t inputSource(bool activeOnly = false) const; bool isSoundTrigger() const; - status_t addAudioSession(audio_session_t session, - const sp& audioSession); - status_t removeAudioSession(audio_session_t session); - sp getAudioSession(audio_session_t session) const; - AudioSessionCollection getAudioSessions(bool activeOnly) const; - size_t getAudioSessionCount(bool activeOnly) const; audio_source_t getHighestPrioritySource(bool activeOnly) const; - void changeRefCount(audio_session_t session, int delta); - + void setClientActive(const sp& client, bool active); + int32_t activeCount() { return mGlobalActiveCount; } // implementation of AudioIODescriptorInterface audio_config_base_t getConfig() const override; @@ -82,10 +75,10 @@ public: audio_input_flags_t flags, audio_io_handle_t *input); // Called when a stream is about to be started. - // Note: called after changeRefCount(session, 1) + // Note: called after setClientActive(client, true) status_t start(); // Called after a stream is stopped - // Note: called after changeRefCount(session, -1) + // Note: called after setClientActive(client, false) void stop(); void close(); @@ -96,12 +89,10 @@ public: private: - void updateSessionRecordingConfiguration(int event, const sp& audioSession); + void updateClientRecordingConfiguration(int event, const sp& client); audio_patch_handle_t mPatchHandle; audio_port_handle_t mId; - // audio sessions attached to this input - AudioSessionCollection mSessions; // Because a preemptible capture session can preempt another one, we end up in an endless loop // situation were each session is allowed to restart after being preempted, // thus preempting the other one which restarts and so on. @@ -110,7 +101,7 @@ public: // We also inherit sessions from the preempted input to avoid a 3 way preemption loop etc... SortedVector mPreemptedSessions; AudioPolicyClientInterface *mClientInterface; - uint32_t mGlobalRefCount; // non-session-specific ref count + int32_t mGlobalActiveCount; // non-client-specific activity ref count RecordClientMap mClients; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h deleted file mode 100644 index 1636d3a480..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "AudioIODescriptorInterface.h" - -namespace android { - -class AudioPolicyClientInterface; - -class AudioSession : public RefBase -{ -public: - AudioSession(audio_session_t session, - audio_source_t inputSource, - audio_format_t format, - uint32_t sampleRate, - audio_channel_mask_t channelMask, - audio_input_flags_t flags, - uid_t uid, - bool isSoundTrigger); - - status_t dump(int fd, int spaces, int index) const; - - audio_session_t session() const { return mRecordClientInfo.session; } - audio_source_t inputSource()const { return mRecordClientInfo.source; } - audio_format_t format() const { return mConfig.format; } - uint32_t sampleRate() const { return mConfig.sample_rate; } - audio_channel_mask_t channelMask() const { return mConfig.channel_mask; } - audio_config_base config() const { return mConfig; } - record_client_info_t recordClientInfo() const { return mRecordClientInfo; } - audio_input_flags_t flags() const { return mFlags; } - uid_t uid() const { return mRecordClientInfo.uid; } - void setUid(uid_t uid) { mRecordClientInfo.uid = uid; } - bool matches(const sp &other) const; - bool isSoundTrigger() const { return mIsSoundTrigger; } - void setSilenced(bool silenced) { mSilenced = silenced; } - bool isSilenced() const { return mSilenced; } - uint32_t openCount() const { return mOpenCount; } ; - uint32_t activeCount() const { return mActiveCount; } ; - - uint32_t changeOpenCount(int delta); - uint32_t changeActiveCount(int delta); - -private: - record_client_info_t mRecordClientInfo; - const struct audio_config_base mConfig; - const audio_input_flags_t mFlags; - bool mIsSoundTrigger; - bool mSilenced; - uint32_t mOpenCount; - uint32_t mActiveCount; -}; - -class AudioSessionCollection : - public DefaultKeyedVector > -{ -public: - status_t addSession(audio_session_t session, - const sp& audioSession); - - status_t removeSession(audio_session_t session); - - uint32_t getOpenCount() const; - - AudioSessionCollection getActiveSessions() const; - size_t getActiveSessionCount() const; - bool hasActiveSession() const; - bool isSourceActive(audio_source_t source) const; - audio_source_t getHighestPrioritySource(bool activeOnly) const; - - status_t dump(int fd, int spaces) const; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index cc751b1c50..1a3300df11 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -107,9 +107,9 @@ public: RecordClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, audio_attributes_t attributes, audio_config_base_t config, audio_port_handle_t preferredDeviceId, - audio_source_t source, audio_input_flags_t flags) : + audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mSource(source), mFlags(flags) {} + mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {} ~RecordClientDescriptor() override = default; using ClientDescriptor::dump; @@ -117,10 +117,15 @@ public: audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } + bool isSoundTrigger() const { return mIsSoundTrigger; } + void setSilenced(bool silenced) { mSilenced = silenced; } + bool isSilenced() const { return mSilenced; } private: const audio_source_t mSource; const audio_input_flags_t mFlags; + const bool mIsSoundTrigger; + bool mSilenced; }; class SourceClientDescriptor: public TrackClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 3c7562ecef..e25b0fe829 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -17,13 +17,13 @@ #define LOG_TAG "APM::AudioInputDescriptor" //#define LOG_NDEBUG 0 +#include +#include #include #include "AudioInputDescriptor.h" #include "IOProfile.h" #include "AudioGain.h" #include "HwModule.h" -#include -#include namespace android { @@ -32,7 +32,7 @@ AudioInputDescriptor::AudioInputDescriptor(const sp& profile, : mIoHandle(0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mProfile(profile), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0), - mClientInterface(clientInterface), mGlobalRefCount(0) + mClientInterface(clientInterface), mGlobalActiveCount(0) { if (profile != NULL) { profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat); @@ -50,11 +50,6 @@ audio_module_handle_t AudioInputDescriptor::getModuleHandle() const return mProfile->getModuleHandle(); } -uint32_t AudioInputDescriptor::getOpenRefCount() const -{ - return mSessions.getOpenCount(); -} - audio_port_handle_t AudioInputDescriptor::getId() const { return mId; @@ -118,57 +113,45 @@ void AudioInputDescriptor::clearPreemptedSessions() mPreemptedSessions.clear(); } -bool AudioInputDescriptor::isActive() const { - return mSessions.hasActiveSession(); -} - bool AudioInputDescriptor::isSourceActive(audio_source_t source) const { - return mSessions.isSourceActive(source); + for (const auto &client : mClients) { + if (client.second->active() && + ((client.second->source() == source) || + ((source == AUDIO_SOURCE_VOICE_RECOGNITION) && + (client.second->source() == AUDIO_SOURCE_HOTWORD) && + client.second->isSoundTrigger()))) { + return true; + } + } + return false; } audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const { + audio_source_t source = AUDIO_SOURCE_DEFAULT; + int32_t priority = -1; - return mSessions.getHighestPrioritySource(activeOnly); -} - -bool AudioInputDescriptor::isSoundTrigger() const { - // sound trigger and non sound trigger sessions are not mixed - // on a given input - return mSessions.valueAt(0)->isSoundTrigger(); -} - -sp AudioInputDescriptor::getAudioSession( - audio_session_t session) const { - return mSessions.valueFor(session); -} - -AudioSessionCollection AudioInputDescriptor::getAudioSessions(bool activeOnly) const -{ - if (activeOnly) { - return mSessions.getActiveSessions(); - } else { - return mSessions; + for (const auto &client : mClients) { + if (activeOnly && !client.second->active() ) { + continue; + } + int32_t curPriority = source_priority(client.second->source()); + if (curPriority > priority) { + priority = curPriority; + source = client.second->source(); + } } + return source; } -size_t AudioInputDescriptor::getAudioSessionCount(bool activeOnly) const -{ - if (activeOnly) { - return mSessions.getActiveSessionCount(); - } else { - return mSessions.size(); +bool AudioInputDescriptor::isSoundTrigger() const { + // sound trigger and non sound trigger clients are not mixed on a given input + // so check only first client + if (mClients.size() == 0) { + return false; } -} - -status_t AudioInputDescriptor::addAudioSession(audio_session_t session, - const sp& audioSession) { - return mSessions.addSession(session, audioSession); -} - -status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) { - return mSessions.removeSession(session); + return mClients.cbegin()->second->isSoundTrigger(); } audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const @@ -179,9 +162,9 @@ audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle) { mPatchHandle = handle; - for (size_t i = 0; i < mSessions.size(); i++) { - if (mSessions[i]->activeCount() > 0) { - updateSessionRecordingConfiguration(RECORD_CONFIG_EVENT_START, mSessions[i]); + for (const auto &client : mClients) { + if (client.second->active()) { + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client.second); } } } @@ -243,7 +226,7 @@ status_t AudioInputDescriptor::open(const audio_config_t *config, status_t AudioInputDescriptor::start() { - if (getAudioSessionCount(true/*activeOnly*/) == 1) { + if (mGlobalActiveCount == 1) { if (!mProfile->canStartNewIo()) { ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount); return INVALID_OPERATION; @@ -270,7 +253,7 @@ void AudioInputDescriptor::close() LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u", __FUNCTION__, mProfile->curOpenCount); // do not call stop() here as stop() is supposed to be called after - // changeRefCount(session, -1) and we don't know how many sessions + // setClientActive(client, false) and we don't know how many clients // are still active at this time if (isActive()) { mProfile->curActiveCount--; @@ -280,27 +263,28 @@ void AudioInputDescriptor::close() } } -void AudioInputDescriptor::changeRefCount(audio_session_t session, int delta) +void AudioInputDescriptor::setClientActive(const sp& client, bool active) { - sp audioSession = mSessions.valueFor(session); - if (audioSession == 0) { + if (mClients.find(client->portId()) == mClients.end() + || active == client->active()) { return; } - // handle session-independent ref count - uint32_t oldGlobalRefCount = mGlobalRefCount; - if ((delta + (int)mGlobalRefCount) < 0) { - ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount); - delta = -((int)mGlobalRefCount); + + // Handle non-client-specific activity ref count + int32_t oldGlobalActiveCount = mGlobalActiveCount; + if (!active && mGlobalActiveCount < 1) { + ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount); + mGlobalActiveCount = 1; } - mGlobalRefCount += delta; - if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { + mGlobalActiveCount += active ? 1 : -1; + + if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) { if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, MIX_STATE_MIXING); } - - } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { + } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) { if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, @@ -308,32 +292,18 @@ void AudioInputDescriptor::changeRefCount(audio_session_t session, int delta) } } - uint32_t oldActiveCount = audioSession->activeCount(); - if ((delta + (int)oldActiveCount) < 0) { - ALOGW("changeRefCount() invalid delta %d for sesion %d active count %d", - delta, session, oldActiveCount); - delta = -((int)oldActiveCount); - } + client->setActive(active); - audioSession->changeActiveCount(delta); - - int event = RECORD_CONFIG_EVENT_NONE; - if ((oldActiveCount == 0) && (audioSession->activeCount() > 0)) { - event = RECORD_CONFIG_EVENT_START; - } else if ((oldActiveCount > 0) && (audioSession->activeCount() == 0)) { - event = RECORD_CONFIG_EVENT_STOP; - } - if (event != RECORD_CONFIG_EVENT_NONE) { - updateSessionRecordingConfiguration(event, audioSession); - } + int event = active ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_STOP; + updateClientRecordingConfiguration(event, client); } -void AudioInputDescriptor::updateSessionRecordingConfiguration( - int event, const sp& audioSession) { - - const audio_config_base_t sessionConfig = audioSession->config(); - const record_client_info_t recordClientInfo = audioSession->recordClientInfo(); +void AudioInputDescriptor::updateClientRecordingConfiguration( + int event, const sp& client) +{ + const audio_config_base_t sessionConfig = client->config(); + const record_client_info_t recordClientInfo{client->uid(), client->session(), client->source()}; const audio_config_base_t config = getConfig(); mClientInterface->onRecordingConfigurationUpdate(event, &recordClientInfo, &sessionConfig, @@ -385,8 +355,6 @@ status_t AudioInputDescriptor::dump(int fd) write(fd, result.string(), result.size()); - mSessions.dump(fd, 1); - size_t index = 0; result = " AudioRecord clients:\n"; for (const auto& client: mClients) { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp deleted file mode 100644 index 5ea4c926d6..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::AudioSession" -//#define LOG_NDEBUG 0 - -#include -#include "policy.h" -#include "AudioSession.h" -#include "AudioGain.h" -#include "TypeConverter.h" - -#include -#include - -namespace android { - -AudioSession::AudioSession(audio_session_t session, - audio_source_t inputSource, - audio_format_t format, - uint32_t sampleRate, - audio_channel_mask_t channelMask, - audio_input_flags_t flags, - uid_t uid, - bool isSoundTrigger) : - mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}), - mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), - mFlags(flags), mIsSoundTrigger(isSoundTrigger), - mOpenCount(1), mActiveCount(0) -{ -} - -uint32_t AudioSession::changeOpenCount(int delta) -{ - if ((delta + (int)mOpenCount) < 0) { - ALOGW("%s invalid delta %d, open count %d", - __FUNCTION__, delta, mOpenCount); - mOpenCount = (uint32_t)(-delta); - } - mOpenCount += delta; - ALOGV("%s open count %d", __FUNCTION__, mOpenCount); - return mOpenCount; -} - -uint32_t AudioSession::changeActiveCount(int delta) -{ - if ((delta + (int)mActiveCount) < 0) { - ALOGW("%s invalid delta %d, active count %d", - __FUNCTION__, delta, mActiveCount); - mActiveCount = (uint32_t)(-delta); - } - mActiveCount += delta; - ALOGV("%s active count %d", __FUNCTION__, mActiveCount); - - return mActiveCount; -} - -bool AudioSession::matches(const sp &other) const -{ - if (other->session() == mRecordClientInfo.session && - other->inputSource() == mRecordClientInfo.source && - other->format() == mConfig.format && - other->sampleRate() == mConfig.sample_rate && - other->channelMask() == mConfig.channel_mask && - other->flags() == mFlags && - other->uid() == mRecordClientInfo.uid) { - return true; - } - return false; -} - -status_t AudioSession::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%*sAudio session %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- session: %2d\n", spaces, "", mRecordClientInfo.session); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mRecordClientInfo.uid); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- input source: %d\n", spaces, "", mRecordClientInfo.source); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- format: %08x\n", spaces, "", mConfig.format); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- sample: %d\n", spaces, "", mConfig.sample_rate); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", - spaces, "", mConfig.channel_mask); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- is soundtrigger: %s\n", - spaces, "", mIsSoundTrigger ? "true" : "false"); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- open count: %d\n", spaces, "", mOpenCount); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- active count: %d\n", spaces, "", mActiveCount); - result.append(buffer); - - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -status_t AudioSessionCollection::addSession(audio_session_t session, - const sp& audioSession) -{ - ssize_t index = indexOfKey(session); - - if (index >= 0) { - ALOGW("addSession() session %d already in", session); - return ALREADY_EXISTS; - } - add(session, audioSession); - ALOGV("addSession() session %d client %d source %d", - session, audioSession->uid(), audioSession->inputSource()); - return NO_ERROR; -} - -status_t AudioSessionCollection::removeSession(audio_session_t session) -{ - ssize_t index = indexOfKey(session); - - if (index < 0) { - ALOGW("removeSession() session %d not in", session); - return ALREADY_EXISTS; - } - ALOGV("removeSession() session %d", session); - removeItemsAt(index); - return NO_ERROR; -} - -uint32_t AudioSessionCollection::getOpenCount() const -{ - uint32_t openCount = 0; - for (size_t i = 0; i < size(); i++) { - openCount += valueAt(i)->openCount(); - } - return openCount; -} - -AudioSessionCollection AudioSessionCollection::getActiveSessions() const -{ - AudioSessionCollection activeSessions; - for (size_t i = 0; i < size(); i++) { - if (valueAt(i)->activeCount() != 0) { - activeSessions.add(valueAt(i)->session(), valueAt(i)); - } - } - return activeSessions; -} - -size_t AudioSessionCollection::getActiveSessionCount() const -{ - size_t activeCount = 0; - for (size_t i = 0; i < size(); i++) { - if (valueAt(i)->activeCount() != 0) { - activeCount++; - } - } - return activeCount; -} - -bool AudioSessionCollection::hasActiveSession() const -{ - return getActiveSessionCount() != 0; -} - -bool AudioSessionCollection::isSourceActive(audio_source_t source) const -{ - for (size_t i = 0; i < size(); i++) { - const sp audioSession = valueAt(i); - // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it - // corresponds to an active capture triggered by a hardware hotword recognition - if (audioSession->activeCount() > 0 && - ((audioSession->inputSource() == source) || - ((source == AUDIO_SOURCE_VOICE_RECOGNITION) && - (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) && - audioSession->isSoundTrigger()))) { - return true; - } - } - return false; -} - -audio_source_t AudioSessionCollection::getHighestPrioritySource(bool activeOnly) const -{ - audio_source_t source = AUDIO_SOURCE_DEFAULT; - int32_t priority = -1; - - for (size_t i = 0; i < size(); i++) { - const sp audioSession = valueAt(i); - if (activeOnly && audioSession->activeCount() == 0) { - continue; - } - int32_t curPriority = source_priority(audioSession->inputSource()); - if (curPriority > priority) { - priority = curPriority; - source = audioSession->inputSource(); - } - } - return source; -} - -status_t AudioSessionCollection::dump(int fd, int spaces) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - snprintf(buffer, SIZE, "%*sAudio Sessions:\n", spaces, ""); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < size(); i++) { - valueAt(i)->dump(fd, spaces + 2, i); - } - return NO_ERROR; -} - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 0cf206db14..0d65a31743 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -29,7 +29,6 @@ namespace android { - status_t ClientDescriptor::dump(int fd, int spaces, int index) { String8 out; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cd40bc5b35..2335135eef 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -517,7 +517,7 @@ sp AudioPolicyManager::createTelephonyPatch( // symmetric to the one in startInput() for (const auto& activeDesc : mInputs.getActiveInputs()) { if (activeDesc->hasSameHwModuleAs(txSourceDeviceDesc)) { - closeSessions(activeDesc, true /*activeOnly*/); + closeActiveClients(activeDesc); } } } @@ -791,6 +791,11 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, const audio_port_handle_t requestedDeviceId = *selectedDeviceId; audio_devices_t msdDevice = getMsdAudioOutDeviceTypes(); + // The supplied portId must be AUDIO_PORT_HANDLE_NONE + if (*portId != AUDIO_PORT_HANDLE_NONE) { + return INVALID_OPERATION; + } + if (attr != NULL) { if (!isValidAttributes(attr)) { ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", @@ -812,11 +817,6 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, attributes.usage, attributes.content_type, attributes.tags, attributes.flags, session, requestedDeviceId); - // TODO: check for existing client for this port ID - if (*portId == AUDIO_PORT_HANDLE_NONE) { - *portId = AudioPort::getNextUniqueId(); - } - *stream = streamTypefromAttributesInt(&attributes); strategy = getStrategyForAttr(&attributes); @@ -902,6 +902,8 @@ exit: audio_config_base_t clientConfig = {.sample_rate = config->sample_rate, .format = config->format, .channel_mask = config->channel_mask }; + *portId = AudioPort::getNextUniqueId(); + sp clientDesc = new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig, requestedDeviceId, *stream, @@ -1662,6 +1664,13 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, sp inputDesc; sp clientDesc; audio_port_handle_t requestedDeviceId = *selectedDeviceId; + bool isSoundTrigger; + audio_devices_t device; + + // The supplied portId must be AUDIO_PORT_HANDLE_NONE + if (*portId != AUDIO_PORT_HANDLE_NONE) { + return INVALID_OPERATION; + } if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; @@ -1684,8 +1693,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, goto error; } sp inputDesc = mInputs.valueAt(index); - sp audioSession = inputDesc->getAudioSession(session); - if (audioSession == 0) { + RecordClientVector clients = inputDesc->getClientsForSession(session); + if (clients.size() == 0) { ALOGW("getInputForAttr() unknown session %d on input %d", session, *input); status = BAD_VALUE; goto error; @@ -1694,28 +1703,27 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, // The second call is for the first active client and sets the UID. Any further call // corresponds to a new client and is only permitted from the same UID. // If the first UID is silenced, allow a new UID connection and replace with new UID - if (audioSession->openCount() == 1) { - audioSession->setUid(uid); - } else if (audioSession->uid() != uid) { - if (!audioSession->isSilenced()) { - ALOGW("getInputForAttr() bad uid %d for session %d uid %d", - uid, session, audioSession->uid()); - status = INVALID_OPERATION; - goto error; - } - audioSession->setUid(uid); - audioSession->setSilenced(false); - } - audioSession->changeOpenCount(1); - *inputType = API_INPUT_LEGACY; - if (*portId == AUDIO_PORT_HANDLE_NONE) { - *portId = AudioPort::getNextUniqueId(); + if (clients.size() > 1) { + for (const auto& client : clients) { + // The client map is ordered by key values (portId) and portIds are allocated + // incrementaly. So the first client in this list is the one opened by audio flinger + // when the mmap stream is created and should be ignored as it does not correspond + // to an actual client + if (client == *clients.cbegin()) { + continue; + } + if (uid != client->uid() && !client->isSilenced()) { + ALOGW("getInputForAttr() bad uid %d for client %d uid %d", + uid, client->portId(), client->uid()); + status = INVALID_OPERATION; + goto error; + } + } } - inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(inputDesc->mDevice); - *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() - : AUDIO_PORT_HANDLE_NONE; - ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session); + *inputType = API_INPUT_LEGACY; + device = inputDesc->mDevice; + ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session); goto exit; } @@ -1724,13 +1732,6 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, halInputSource = inputSource; - // TODO: check for existing client for this port ID - if (*portId == AUDIO_PORT_HANDLE_NONE) { - *portId = AudioPort::getNextUniqueId(); - } - - audio_devices_t device; - if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix); @@ -1774,7 +1775,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } - *input = getInputForDevice(device, address, session, uid, inputSource, + *input = getInputForDevice(device, address, session, inputSource, config, flags, policyMix); if (*input == AUDIO_IO_HANDLE_NONE) { @@ -1782,13 +1783,19 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, goto error; } +exit: + inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() - : AUDIO_PORT_HANDLE_NONE; + : AUDIO_PORT_HANDLE_NONE; + + isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD && + mSoundTriggerSessions.indexOfKey(session) > 0; + *portId = AudioPort::getNextUniqueId(); -exit: clientDesc = new RecordClientDescriptor(*portId, uid, session, - *attr, *config, requestedDeviceId, inputSource, flags); + *attr, *config, requestedDeviceId, + inputSource,flags, isSoundTrigger); inputDesc = mInputs.valueFor(*input); inputDesc->clientsMap().emplace(*portId, clientDesc); @@ -1805,7 +1812,6 @@ error: audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, String8 address, audio_session_t session, - uid_t uid, audio_source_t inputSource, const audio_config_base_t *config, audio_input_flags_t flags, @@ -1867,15 +1873,6 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, return input; } - sp audioSession = new AudioSession(session, - inputSource, - config->format, - samplingRate, - config->channel_mask, - flags, - uid, - isSoundTrigger); - if (!profile->canOpenNewIo()) { return AUDIO_IO_HANDLE_NONE; } @@ -1911,7 +1908,6 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, } inputDesc->mPolicyMix = policyMix; - inputDesc->addAudioSession(session, audioSession); addInput(input, inputDesc); mpClientInterface->onAudioPortListUpdate(); @@ -1927,39 +1923,6 @@ bool AudioPolicyManager::isConcurrentSource(audio_source_t source) (source == AUDIO_SOURCE_FM_TUNER); } -bool AudioPolicyManager::isConcurentCaptureAllowed(const sp& inputDesc, - const sp& audioSession) -{ - // Do not allow capture if an active voice call is using a software patch and - // the call TX source device is on the same HW module. - // FIXME: would be better to refine to only inputs whose profile connects to the - // call TX device but this information is not in the audio patch - if (mCallTxPatch != 0 && - inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) { - return false; - } - - // starting concurrent capture is enabled if: - // 1) capturing for re-routing - // 2) capturing for HOTWORD source - // 3) capturing for FM TUNER source - // 3) All other active captures are either for re-routing or HOTWORD - - if (is_virtual_input_device(inputDesc->mDevice) || - isConcurrentSource(audioSession->inputSource())) { - return true; - } - - for (const auto& activeInput : mInputs.getActiveInputs()) { - if (!isConcurrentSource(activeInput->inputSource(true)) && - !is_virtual_input_device(activeInput->mDevice)) { - return false; - } - } - - return true; -} - // FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537. bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { if (!mHasComputedSoundTriggerSupportsConcurrentCapture) { @@ -2005,21 +1968,20 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, sp inputDesc = mInputs.getInputForClient(portId); if (inputDesc == 0) { - ALOGW("startInput() no input for client %d", portId); + ALOGW("%s no input for client %d", __FUNCTION__, portId); return BAD_VALUE; } - sp client = inputDesc->clientsMap()[portId]; - audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; + sp client = inputDesc->clientsMap()[portId]; + if (client->active()) { + ALOGW("%s input %d client %d already started", __FUNCTION__, input, client->portId()); + return INVALID_OPERATION; + } - ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)", - input, session, silenced, *concurrency); + audio_session_t session = client->session(); - sp audioSession = inputDesc->getAudioSession(session); - if (audioSession == 0) { - ALOGW("startInput() unknown session %d on input %d", session, input); - return BAD_VALUE; - } + ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)", + __FUNCTION__, input, session, silenced, *concurrency); if (!is_virtual_input_device(inputDesc->mDevice)) { if (mCallTxPatch != 0 && @@ -2036,47 +1998,48 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, // favor of the new one - "There can be only one" TM if (!silenced) { for (const auto& activeDesc : activeInputs) { - if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && + if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && activeDesc->getId() == inputDesc->getId()) { continue; } - AudioSessionCollection activeSessions = activeDesc->getAudioSessions( - true /*activeOnly*/); - sp activeSession = activeSessions.valueAt(0); - if (activeSession->isSilenced()) { - closeSession(activeDesc, activeSession); - ALOGV("startInput() session %d stopping silenced session %d", session, activeSession->session()); - activeInputs = mInputs.getActiveInputs(); + RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/); + for (const auto& activeClient : activeClients) { + if (activeClient->isSilenced()) { + closeClient(activeClient->portId()); + ALOGV("%s client %d stopping silenced client %d", __FUNCTION__, + portId, activeClient->portId()); + activeInputs = mInputs.getActiveInputs(); + } } } } for (const auto& activeDesc : activeInputs) { - if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && + if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && activeDesc->getId() == inputDesc->getId()) { continue; } audio_source_t activeSource = activeDesc->inputSource(true); - if (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) { + if (client->source() == AUDIO_SOURCE_HOTWORD) { if (activeSource == AUDIO_SOURCE_HOTWORD) { if (activeDesc->hasPreemptedSession(session)) { - ALOGW("startInput(%d) failed for HOTWORD: " - "other input %d already started for HOTWORD", + ALOGW("%s input %d failed for HOTWORD: " + "other input %d already started for HOTWORD", __FUNCTION__, input, activeDesc->mIoHandle); *concurrency |= API_INPUT_CONCURRENCY_HOTWORD; return INVALID_OPERATION; } } else { - ALOGV("startInput(%d) failed for HOTWORD: other input %d already started", - input, activeDesc->mIoHandle); + ALOGV("%s input %d failed for HOTWORD: other input %d already started", + __FUNCTION__, input, activeDesc->mIoHandle); *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; return INVALID_OPERATION; } } else { if (activeSource != AUDIO_SOURCE_HOTWORD) { - ALOGW("startInput(%d) failed: other input %d already started", + ALOGW("%s input %d failed: other input %d already started", __FUNCTION__, input, activeDesc->mIoHandle); *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; return INVALID_OPERATION; @@ -2095,78 +2058,75 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) { continue; } - - audio_source_t activeSource = activeDesc->inputSource(true); - if (activeSource == AUDIO_SOURCE_HOTWORD) { - AudioSessionCollection activeSessions = - activeDesc->getAudioSessions(true /*activeOnly*/); - sp activeSession = activeSessions[0]; + RecordClientVector activeHotwordClients = + activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD); + if (activeHotwordClients.size() > 0) { SortedVector sessions = activeDesc->getPreemptedSessions(); - *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; - sessions.add(activeSession->session()); + + for (const auto& activeClient : activeHotwordClients) { + *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; + sessions.add(activeClient->session()); + closeClient(activeClient->portId()); + ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__, + input, activeDesc->mIoHandle); + } + inputDesc->setPreemptedSessions(sessions); - closeSession(inputDesc, activeSession); - ALOGV("startInput(%d) for HOTWORD preempting HOTWORD input %d", - input, activeDesc->mIoHandle); } } } // Make sure we start with the correct silence state - audioSession->setSilenced(silenced); + client->setSilenced(silenced); // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection - inputDesc->changeRefCount(session, 1); - client->setActive(true); + inputDesc->setClientActive(client, true); - if (audioSession->activeCount() == 1 || client->hasPreferredDevice(true)) { - // indicate active capture to sound trigger service if starting capture from a mic on - // primary HW module - audio_devices_t device = getNewInputDevice(inputDesc); - setInputDevice(input, device, true /* force */); + // indicate active capture to sound trigger service if starting capture from a mic on + // primary HW module + audio_devices_t device = getNewInputDevice(inputDesc); + setInputDevice(input, device, true /* force */); - status_t status = inputDesc->start(); - if (status != NO_ERROR) { - inputDesc->changeRefCount(session, -1); - client->setActive(false); - return status; + status_t status = inputDesc->start(); + if (status != NO_ERROR) { + inputDesc->setClientActive(client, false); + return status; + } + + if (inputDesc->activeCount() == 1) { + // if input maps to a dynamic policy with an activity listener, notify of state change + if ((inputDesc->mPolicyMix != NULL) + && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + MIX_STATE_MIXING); } - if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) { - // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, - MIX_STATE_MIXING); - } + audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); + if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) { + SoundTrigger::setCaptureState(true); + } - audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); - if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && - mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) { - SoundTrigger::setCaptureState(true); + // automatically enable the remote submix output when input is started if not + // used by a policy mix of type MIX_TYPE_RECORDERS + // For remote submix (a virtual device), we open only one input per capture request. + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + String8 address = String8(""); + if (inputDesc->mPolicyMix == NULL) { + address = String8("0"); + } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { + address = inputDesc->mPolicyMix->mDeviceAddress; } - - // automatically enable the remote submix output when input is started if not - // used by a policy mix of type MIX_TYPE_RECORDERS - // For remote submix (a virtual device), we open only one input per capture request. - if (audio_is_remote_submix_device(inputDesc->mDevice)) { - String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { - address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; - } - if (address != "") { - setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address, "remote-submix"); - } + if (address != "") { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address, "remote-submix"); } } } - ALOGV("AudioPolicyManager::startInput() input source = %d", audioSession->inputSource()); + ALOGV("%s input %d source = %d exit", __FUNCTION__, input, client->source()); return NO_ERROR; } @@ -2177,65 +2137,56 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) sp inputDesc = mInputs.getInputForClient(portId); if (inputDesc == 0) { - ALOGW("stopInput() no input for client %d", portId); + ALOGW("%s no input for client %d", __FUNCTION__, portId); return BAD_VALUE; } - sp client = inputDesc->clientsMap()[portId]; - audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; - - ALOGV("stopInput() input %d", input); - - sp audioSession = inputDesc->getAudioSession(session); - - if (audioSession->activeCount() == 0) { - ALOGW("stopInput() input %d already stopped", input); + sp client = inputDesc->clientsMap()[portId]; + if (!client->active()) { + ALOGW("%s input %d client %d already stopped", __FUNCTION__, input, client->portId()); return INVALID_OPERATION; } - inputDesc->changeRefCount(session, -1); - client->setActive(false); + inputDesc->setClientActive(client, false); - if (audioSession->activeCount() == 0) { - inputDesc->stop(); - if (inputDesc->isActive()) { - setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); - } else { - // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, - MIX_STATE_IDLE); - } - - // automatically disable the remote submix output when input is stopped if not - // used by a policy mix of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(inputDesc->mDevice)) { - String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { - address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; - } - if (address != "") { - setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address, "remote-submix"); - } + inputDesc->stop(); + if (inputDesc->isActive()) { + setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); + } else { + // if input maps to a dynamic policy with an activity listener, notify of state change + if ((inputDesc->mPolicyMix != NULL) + && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + MIX_STATE_IDLE); + } + + // automatically disable the remote submix output when input is stopped if not + // used by a policy mix of type MIX_TYPE_RECORDERS + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + String8 address = String8(""); + if (inputDesc->mPolicyMix == NULL) { + address = String8("0"); + } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { + address = inputDesc->mPolicyMix->mDeviceAddress; + } + if (address != "") { + setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address, "remote-submix"); } + } - audio_devices_t device = inputDesc->mDevice; - resetInputDevice(input); + audio_devices_t device = inputDesc->mDevice; + resetInputDevice(input); - // indicate inactive capture to sound trigger service if stopping capture from a mic on - // primary HW module - audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); - if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && - mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { - SoundTrigger::setCaptureState(false); - } - inputDesc->clearPreemptedSessions(); + // indicate inactive capture to sound trigger service if stopping capture from a mic on + // primary HW module + audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); + if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { + SoundTrigger::setCaptureState(false); } + inputDesc->clearPreemptedSessions(); } return NO_ERROR; } @@ -2246,61 +2197,40 @@ void AudioPolicyManager::releaseInput(audio_port_handle_t portId) sp inputDesc = mInputs.getInputForClient(portId); if (inputDesc == 0) { - ALOGW("releaseInput() no input for client %d", portId); + ALOGW("%s no input for client %d", __FUNCTION__, portId); return; } sp client = inputDesc->clientsMap()[portId]; - audio_session_t session = client->session(); audio_io_handle_t input = inputDesc->mIoHandle; - ALOGV("releaseInput() %d", input); - - sp audioSession = inputDesc->getAudioSession(session); - if (audioSession == 0) { - ALOGW("releaseInput() unknown session %d on input %d", session, input); - return; - } - - if (audioSession->openCount() == 0) { - ALOGW("releaseInput() invalid open count %d on session %d", - audioSession->openCount(), session); - return; - } + ALOGV("%s %d", __FUNCTION__, input); - if (audioSession->changeOpenCount(-1) == 0) { - inputDesc->removeAudioSession(session); - } + inputDesc->clientsMap().erase(portId); - if (inputDesc->getOpenRefCount() > 0) { - ALOGV("releaseInput() exit > 0"); + if (inputDesc->clientsMap().size() > 0) { + ALOGV("%s %zu clients remaining", __FUNCTION__, inputDesc->clientsMap().size()); return; } closeInput(input); - inputDesc->clientsMap().erase(portId); mpClientInterface->onAudioPortListUpdate(); - ALOGV("releaseInput() exit"); + ALOGV("%s exit", __FUNCTION__); } -void AudioPolicyManager::closeSessions(const sp& input, bool activeOnly) +void AudioPolicyManager::closeActiveClients(const sp& input) { - AudioSessionCollection sessions = input->getAudioSessions(activeOnly /*activeOnly*/); - for (size_t i = 0; i < sessions.size(); i++) { - closeSession(input, sessions[i]); - } -} - -void AudioPolicyManager::closeSession(const sp& input, - const sp& session) -{ - RecordClientVector clients = input->getClientsForSession(session->session()); + RecordClientVector clients = input->clientsList(true); for (const auto& client : clients) { - stopInput(client->portId()); - releaseInput(client->portId()); + closeClient(client->portId()); } } +void AudioPolicyManager::closeClient(audio_port_handle_t portId) +{ + stopInput(portId); + releaseInput(portId); +} void AudioPolicyManager::closeAllInputs() { bool patchRemoved = false; @@ -3891,11 +3821,10 @@ void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced) Vector > activeInputs = mInputs.getActiveInputs(); for (size_t i = 0; i < activeInputs.size(); i++) { sp activeDesc = activeInputs[i]; - AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true); - for (size_t j = 0; j < activeSessions.size(); j++) { - sp activeSession = activeSessions.valueAt(j); - if (activeSession->uid() == uid) { - activeSession->setSilenced(silenced); + RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); + for (const auto& client : clients) { + if (uid == client->uid()) { + client->setSilenced(silenced); } } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index cc5739bb86..6f4cce1d28 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -539,15 +539,11 @@ protected: static bool isConcurrentSource(audio_source_t source); - bool isConcurentCaptureAllowed(const sp& inputDesc, - const sp& audioSession); - static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); - void closeSessions(const sp& input, bool activeOnly); - void closeSession(const sp& input, - const sp& session); + void closeActiveClients(const sp& input); + void closeClient(audio_port_handle_t portId); const uid_t mUidCached; // AID_AUDIOSERVER AudioPolicyClientInterface *mpClientInterface; // audio policy client interface @@ -667,7 +663,6 @@ private: audio_io_handle_t getInputForDevice(audio_devices_t device, String8 address, audio_session_t session, - uid_t uid, audio_source_t inputSource, const audio_config_base_t *config, audio_input_flags_t flags, -- GitLab From 592dd7bf150cee7990958632425c411366252083 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Sun, 5 Aug 2018 18:58:48 -0700 Subject: [PATCH 0264/1530] audiopolicy: refactor playback activity ref counting Playback activity ref couting is now based on active state of PlaybackClientDescriptor. The active reference count per stream on each output is still used because it provides a simple implementation for duplicated output. Test: CTS tests for AudioTrack and Routing Test: manual audio smoke test Change-Id: Ifadb1d24131581461aace14a5b25e7c7582c57cb --- .../include/AudioOutputDescriptor.h | 17 +-- .../src/AudioOutputDescriptor.cpp | 104 ++++++++++-------- .../managerdefault/AudioPolicyManager.cpp | 20 ++-- 3 files changed, 75 insertions(+), 66 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 4cf16a0470..27b1c93660 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -59,7 +59,10 @@ public: audio_devices_t device, uint32_t delayMs, bool force); - virtual void changeRefCount(audio_stream_type_t stream, int delta); + virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta); + uint32_t streamActiveCount(audio_stream_type_t stream) const + { return mActiveCount[stream]; } + void setClientActive(const sp& client, bool active); bool isActive(uint32_t inPastMs = 0) const; bool isStreamActive(audio_stream_type_t stream, @@ -84,15 +87,17 @@ public: sp mPort; audio_devices_t mDevice; // current device this output is routed to - uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output nsecs_t mStopTime[AUDIO_STREAM_CNT]; float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible // device selection. See checkDeviceMuteStrategies() AudioPolicyClientInterface *mClientInterface; + AudioMix *mPolicyMix; // non NULL when used by a dynamic policy protected: + uint32_t mActiveCount[AUDIO_STREAM_CNT]; // number of streams of each type active on this output + uint32_t mGlobalActiveCount; // non-client-specific active count audio_patch_handle_t mPatchHandle; audio_port_handle_t mId; TrackClientMap mClients; @@ -116,7 +121,7 @@ public: virtual bool isFixedVolume(audio_devices_t device); virtual sp subOutput1() { return mOutput1; } virtual sp subOutput2() { return mOutput2; } - virtual void changeRefCount(audio_stream_type_t stream, int delta); + virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta); virtual bool setVolume(float volume, audio_stream_type_t stream, audio_devices_t device, @@ -134,10 +139,10 @@ public: audio_output_flags_t flags, audio_io_handle_t *output); // Called when a stream is about to be started - // Note: called before changeRefCount(1); + // Note: called before setClientActive(true); status_t start(); // Called after a stream is stopped. - // Note: called after changeRefCount(-1); + // Note: called after setClientActive(false); void stop(); void close(); status_t openDuplicating(const sp& output1, @@ -148,12 +153,10 @@ public: audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy sp mOutput1; // used by duplicated outputs: first output sp mOutput2; // used by duplicated outputs: second output uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) audio_session_t mDirectClientSession; // session id of the direct output client - uint32_t mGlobalRefCount; // non-stream-specific ref count }; // Audio output driven by an input device directly. diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index a39ac6da3a..b6ff6eaeab 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -34,12 +34,12 @@ namespace android { AudioOutputDescriptor::AudioOutputDescriptor(const sp& port, AudioPolicyClientInterface *clientInterface) - : mPort(port), mDevice(AUDIO_DEVICE_NONE), - mClientInterface(clientInterface), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0) + : mPort(port), mDevice(AUDIO_DEVICE_NONE), mClientInterface(clientInterface), + mPolicyMix(NULL), mGlobalActiveCount(0), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0) { // clear usage count for all stream types for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - mRefCount[i] = 0; + mActiveCount[i] = 0; mCurVolume[i] = -1.0; mMuteCount[i] = 0; mStopTime[i] = 0; @@ -103,17 +103,51 @@ bool AudioOutputDescriptor::sharesHwModuleWith( } } -void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, +void AudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream, int delta) { - if ((delta + (int)mRefCount[stream]) < 0) { - ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", - delta, stream, mRefCount[stream]); - mRefCount[stream] = 0; + if ((delta + (int)mActiveCount[stream]) < 0) { + ALOGW("%s invalid delta %d for stream %d, active count %d", + __FUNCTION__, delta, stream, mActiveCount[stream]); + mActiveCount[stream] = 0; return; } - mRefCount[stream] += delta; - ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); + mActiveCount[stream] += delta; + ALOGV("%s stream %d, count %d", __FUNCTION__, stream, mActiveCount[stream]); +} + +void AudioOutputDescriptor::setClientActive(const sp& client, bool active) +{ + if (mClients.find(client->portId()) == mClients.end() + || active == client->active()) { + return; + } + + changeStreamActiveCount(client->stream(), active ? 1 : -1); + + // Handle non-client-specific activity ref count + int32_t oldGlobalActiveCount = mGlobalActiveCount; + if (!active && mGlobalActiveCount < 1) { + ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount); + mGlobalActiveCount = 1; + } + mGlobalActiveCount += active ? 1 : -1; + + if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { + mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + MIX_STATE_MIXING); + } + } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { + mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + MIX_STATE_IDLE); + } + } + + client->setActive(active); } bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const @@ -137,7 +171,7 @@ bool AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs, nsecs_t sysTime) const { - if (mRefCount[stream] != 0) { + if (mActiveCount[stream] != 0) { return true; } if (inPastMs == 0) { @@ -231,11 +265,11 @@ status_t AudioOutputDescriptor::dump(int fd) result.append(buffer); snprintf(buffer, SIZE, " Devices %08x\n", device()); result.append(buffer); - snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); + snprintf(buffer, SIZE, " Stream volume activeCount muteCount\n"); result.append(buffer); for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { - snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", - i, mCurVolume[i], mRefCount[i], mMuteCount[i]); + snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", + i, mCurVolume[i], streamActiveCount((audio_stream_type_t)i), mMuteCount[i]); result.append(buffer); } @@ -261,9 +295,9 @@ SwAudioOutputDescriptor::SwAudioOutputDescriptor(const sp& profile, AudioPolicyClientInterface *clientInterface) : AudioOutputDescriptor(profile, clientInterface), mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0), - mFlags((audio_output_flags_t)0), mPolicyMix(NULL), + mFlags((audio_output_flags_t)0), mOutput1(0), mOutput2(0), mDirectOpenCount(0), - mDirectClientSession(AUDIO_SESSION_NONE), mGlobalRefCount(0) + mDirectClientSession(AUDIO_SESSION_NONE) { if (profile != NULL) { mFlags = (audio_output_flags_t)profile->getFlags(); @@ -327,41 +361,17 @@ uint32_t SwAudioOutputDescriptor::latency() } } -void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, +void SwAudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream, int delta) { // forward usage count change to attached outputs if (isDuplicated()) { - mOutput1->changeRefCount(stream, delta); - mOutput2->changeRefCount(stream, delta); - } - AudioOutputDescriptor::changeRefCount(stream, delta); - - // handle stream-independent ref count - uint32_t oldGlobalRefCount = mGlobalRefCount; - if ((delta + (int)mGlobalRefCount) < 0) { - ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount); - mGlobalRefCount = 0; - } else { - mGlobalRefCount += delta; - } - if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) - { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, - MIX_STATE_MIXING); - } - - } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) - { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, - MIX_STATE_IDLE); - } + mOutput1->changeStreamActiveCount(stream, delta); + mOutput2->changeStreamActiveCount(stream, delta); } + AudioOutputDescriptor::changeStreamActiveCount(stream, delta); } - bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device) { // unit gain if rerouting to external policy @@ -537,7 +547,7 @@ void SwAudioOutputDescriptor::close() LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u", __FUNCTION__, mProfile->curOpenCount); - // do not call stop() here as stop() is supposed to be called after changeRefCount(-1) + // do not call stop() here as stop() is supposed to be called after setClientActive(false) // and we don't know how many streams are still active at this time if (isActive()) { mProfile->curActiveCount--; @@ -737,7 +747,7 @@ bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgno } for (size_t i = 0; i < size(); i++) { const sp outputDesc = valueAt(i); - if (outputDesc->mRefCount[s] != 0) { + if (outputDesc->streamActiveCount((audio_stream_type_t)s)!= 0) { return true; } } @@ -802,7 +812,7 @@ bool HwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgno } for (size_t i = 0; i < size(); i++) { const sp outputDesc = valueAt(i); - if (outputDesc->mRefCount[s] != 0) { + if (outputDesc->streamActiveCount((audio_stream_type_t)s) != 0) { return true; } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 2335135eef..5e98bee2bc 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1034,8 +1034,6 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( } return AUDIO_IO_HANDLE_NONE; } - outputDesc->mRefCount[stream] = 0; - outputDesc->mStopTime[stream] = 0; outputDesc->mDirectOpenCount = 1; outputDesc->mDirectClientSession = session; @@ -1394,8 +1392,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // increment usage count for this stream on the requested output: // NOTE that the usage count is the same for duplicated output and hardware output which is // necessary for a correct control of hardware output routing by startOutput() and stopOutput() - outputDesc->changeRefCount(stream, 1); - client->setActive(true); + outputDesc->setClientActive(client, true); if (client->hasPreferredDevice(true)) { device = getNewOutputDevice(outputDesc, false /*fromCache*/); @@ -1408,7 +1405,7 @@ status_t AudioPolicyManager::startSource(const sp& outp selectOutputForMusicEffects(); } - if (outputDesc->mRefCount[stream] == 1 || device != AUDIO_DEVICE_NONE) { + if (outputDesc->streamActiveCount(stream) == 1 || device != AUDIO_DEVICE_NONE) { // starting an output being rerouted? if (device == AUDIO_DEVICE_NONE) { device = getNewOutputDevice(outputDesc, false /*fromCache*/); @@ -1537,8 +1534,8 @@ status_t AudioPolicyManager::stopSource(const sp& outpu handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); - if (outputDesc->mRefCount[stream] > 0) { - if (outputDesc->mRefCount[stream] == 1) { + if (outputDesc->streamActiveCount(stream) > 0) { + if (outputDesc->streamActiveCount(stream) == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(outputDesc->mDevice) && @@ -1557,11 +1554,10 @@ status_t AudioPolicyManager::stopSource(const sp& outpu } // decrement usage count of this stream on the output - outputDesc->changeRefCount(stream, -1); - client->setActive(false); + outputDesc->setClientActive(client, false); // store time at which the stream was stopped - see isStreamActive() - if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) { + if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) { outputDesc->mStopTime[stream] = systemTime(); audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when @@ -4621,8 +4617,8 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) // the other output. bool wasActive = outputDesc2->isActive(); for (int j = 0; j < AUDIO_STREAM_CNT; j++) { - int refCount = dupOutputDesc->mRefCount[j]; - outputDesc2->changeRefCount((audio_stream_type_t)j,-refCount); + int activeCount = dupOutputDesc->streamActiveCount((audio_stream_type_t)j); + outputDesc2->changeStreamActiveCount((audio_stream_type_t)j,-activeCount); } // stop() will be a no op if the output is still active but is needed in case all // active streams refcounts where cleared above -- GitLab From b0688d699b8c2f406e001a5c991f3e0515c84ab9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 14 Aug 2018 15:49:18 -0700 Subject: [PATCH 0265/1530] audio policy: fix getDevicesForStream() with preferred route Fix getDevicesForStream() to return correct device when the stream is active with an explicit preferred device selection. Test: manual test with AudioExplicitRouting app. Change-Id: I3f135b98b50c570ac7123c723f195da64a2d1e20 --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5e98bee2bc..60bce9c61a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4999,6 +4999,7 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } + audio_devices_t activeDevices = AUDIO_DEVICE_NONE; audio_devices_t devices = AUDIO_DEVICE_NONE; for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { @@ -5007,15 +5008,20 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); audio_devices_t curDevices = getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/); + devices |= curDevices; for (audio_io_handle_t output : getOutputsForDevice(curDevices, mOutputs)) { sp outputDesc = mOutputs.valueFor(output); if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) { - curDevices |= outputDesc->device(); + activeDevices |= outputDesc->device(); } } - devices |= curDevices; } + // Favor devices selected on active streams if any to report correct device in case of + // explicit device selection + if (activeDevices != AUDIO_DEVICE_NONE) { + devices = activeDevices; + } /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it and doesn't really need to.*/ if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { -- GitLab From fefe316501e06c96ce0d905c793b4d872d60e976 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Fri, 7 Sep 2018 10:09:11 -0700 Subject: [PATCH 0266/1530] NBLog: log and store warmup times, underruns, overruns, and thread info Test: dumpsys media.log Bug: 68148948 Change-Id: Ib6ea96760f7886cba47c8e2f0334114237a2434b --- media/libnblog/Android.bp | 4 + media/libnblog/NBLog.cpp | 77 +++++++++++++++---- media/libnblog/PerformanceAnalysis.cpp | 35 +-------- media/libnblog/ReportPerformance.cpp | 22 +++++- media/libnblog/include/media/nblog/NBLog.h | 43 +++++++---- .../include/media/nblog/PerformanceAnalysis.h | 67 ++++++++++------ .../include/media/nblog/ReportPerformance.h | 12 +++ services/audioflinger/FastMixer.cpp | 3 + services/audioflinger/FastThread.cpp | 6 ++ services/audioflinger/TypedLogger.h | 17 ++++ 10 files changed, 200 insertions(+), 86 deletions(-) diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp index 74aaf77a18..b15e79bc29 100644 --- a/media/libnblog/Android.bp +++ b/media/libnblog/Android.bp @@ -16,6 +16,10 @@ cc_library_shared { "libutils", ], + static_libs: [ + "libjsoncpp", + ], + cflags: [ "-Werror", "-Wall", diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index f73e2ae2e1..cd967e417b 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -31,12 +32,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -417,7 +420,7 @@ void NBLog::Writer::logTimestampFormat() if (!mEnabled) { return; } - int64_t ts = get_monotonic_ns(); + const nsecs_t ts = systemTime(); if (ts > 0) { log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts)); } else { @@ -432,7 +435,7 @@ void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash) } HistTsEntry data; data.hash = hash; - data.ts = get_monotonic_ns(); + data.ts = systemTime(); if (data.ts > 0) { log(event, &data, sizeof(data)); } else { @@ -780,15 +783,17 @@ std::unique_ptr NBLog::Reader::getSnapshot() return snapshot; } +// TODO separate this method from the rest of NBLog // Takes raw content of the local merger FIFO, processes log entries, and // writes the data to a map of class PerformanceAnalysis, based on their thread ID. -void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author) +void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author) { + PerformanceData& data = mThreadPerformanceData[author]; // We don't do "auto it" because it reduces readability in this case. for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) { switch (it->type) { case EVENT_HISTOGRAM_ENTRY_TS: { - HistTsEntry payload = it.payload(); + const HistTsEntry payload = it.payload(); // TODO: hash for histogram ts and audio state need to match // and correspond to audio production source file location mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts); @@ -796,15 +801,37 @@ void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int au case EVENT_AUDIO_STATE: { mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange(); } break; + case EVENT_THREAD_INFO: { + const thread_info_t info = it.payload(); + // TODO make PerformanceData hold a type of thread_info_t. + // Currently, thread_info_t is defined in NBLog.h, which includes + // PerformanceAnalysis.h. PerformanceData is defined in PerformanceAnalysis.h, + // where including NBLog.h would result in circular includes. The organization + // of files will need to change to avoid this problem. + data.type = info.type; + data.frameCount = info.frameCount; + data.sampleRate = info.sampleRate; + } break; case EVENT_LATENCY: { - double latencyMs = it.payload(); - mPerformanceData.addLatencyEntry(author, latencyMs); + const double latencyMs = it.payload(); + data.latencyHist.add(latencyMs); } break; case EVENT_WORK_TIME: { - uint64_t monotonicNs = it.payload(); + const int64_t monotonicNs = it.payload(); const double monotonicMs = monotonicNs * 1e-6; - mPerformanceData.addCycleTimeEntry(author, monotonicMs); + data.workHist.add(monotonicMs); + data.active += monotonicNs; + } break; + case EVENT_WARMUP_TIME: { + const double timeMs = it.payload(); + data.warmupHist.add(timeMs); } break; + case EVENT_UNDERRUN: + data.underruns++; + break; + case EVENT_OVERRUN: + data.overruns++; + break; case EVENT_RESERVED: case EVENT_UPPER_BOUND: ALOGW("warning: unexpected event %d", it->type); @@ -826,7 +853,7 @@ void NBLog::MergeReader::getAndProcessSnapshot() // TODO unlock lock here for (size_t i = 0; i < nLogs; i++) { if (snapshots[i] != nullptr) { - getAndProcessSnapshot(*(snapshots[i]), i); + processSnapshot(*(snapshots[i]), i); } } } @@ -835,7 +862,19 @@ void NBLog::MergeReader::dump(int fd, int indent) { // TODO: add a mutex around media.log dump ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); - mPerformanceData.dump(fd); + Json::Value root(Json::arrayValue); + for (const auto& item : mThreadPerformanceData) { + const PerformanceData& data = item.second; + std::unique_ptr threadData = dumpToJson(data); + if (threadData == nullptr) { + continue; + } + (*threadData)["threadNum"] = item.first; + root.append(*threadData); + } + Json::StyledWriter writer; + std::string rootStr = writer.write(root); + dprintf(fd, "%s", rootStr.c_str()); } // TODO for future compatibility, would prefer to have a dump() go to string, and then go @@ -856,13 +895,23 @@ void NBLog::DumpReader::dump(int fd, size_t indent) it = handleFormat(FormatEntry(it), ×tamp, &body); break; case EVENT_WORK_TIME: { - uint64_t monotonicNs = it.payload(); - body.appendFormat("Thread cycle: %lu ns", (unsigned long)monotonicNs); + const int64_t monotonicNs = it.payload(); + body.appendFormat("Thread cycle: %ld ns", (long)monotonicNs); } break; case EVENT_LATENCY: { - double latencyMs = it.payload(); + const double latencyMs = it.payload(); body.appendFormat("latency: %.3f ms", latencyMs); } break; + case EVENT_WARMUP_TIME: { + const double timeMs = it.payload(); + body.appendFormat("warmup time: %.3f ms", timeMs); + } break; + case EVENT_UNDERRUN: + body.appendFormat("underrun"); + break; + case EVENT_OVERRUN: + body.appendFormat("overrun"); + break; case EVENT_FMT_END: case EVENT_RESERVED: case EVENT_UPPER_BOUND: @@ -947,7 +996,7 @@ NBLog::EntryIterator NBLog::DumpReader::handleFormat(const FormatEntry &fmtEntry String8 *body) { // log timestamp - int64_t ts = fmtEntry.timestamp(); + const int64_t ts = fmtEntry.timestamp(); timestamp->clear(); timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)), (int) ((ts / (1000 * 1000)) % 1000)); diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp index ce9e22a87b..999b22a18d 100644 --- a/media/libnblog/PerformanceAnalysis.cpp +++ b/media/libnblog/PerformanceAnalysis.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,7 @@ uint64_t Histogram::totalCount() const return mTotalCount; } -std::string Histogram::serializeToString() const { +std::string Histogram::toString() const { std::stringstream ss; static constexpr char kDivider = '|'; ss << mBinSize << "," << mNumBins << "," << mLow << ",{"; @@ -104,38 +105,6 @@ std::string Histogram::serializeToString() const { return ss.str(); } -// TODO make a hash map from Event type to std::pair> -// so that we don't have to create a "add histogram entry" method for every different metric. -void PerformanceData::addCycleTimeEntry(int author, double cycleTimeMs) -{ - if (mCycleTimeMsHists.count(author) == 0) { - mCycleTimeMsHists.emplace(author, Histogram(kCycleTimeConfig)); - } - mCycleTimeMsHists.at(author).add(cycleTimeMs); -} - -void PerformanceData::addLatencyEntry(int author, double latencyMs) -{ - if (mLatencyMsHists.count(author) == 0) { - mLatencyMsHists.emplace(author, Histogram(kLatencyConfig)); - } - mLatencyMsHists.at(author).add(latencyMs); -} - -void PerformanceData::dump(int fd, int indent __unused) -{ - // TODO add thread metadata for better context. - // Also output in a more machine-readable friendly format. - dprintf(fd, "Thread cycle time histograms:\n"); - for (const auto &item : mCycleTimeMsHists) { - dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str()); - } - dprintf(fd, "Latency histograms:\n"); - for (const auto &item : mLatencyMsHists) { - dprintf(fd, " Thread %d: %s\n", item.first, item.second.serializeToString().c_str()); - } -} - //------------------------------------------------------------------------------ namespace ReportPerformance { diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp index 827e731a88..98e11cd49b 100644 --- a/media/libnblog/ReportPerformance.cpp +++ b/media/libnblog/ReportPerformance.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,8 +37,26 @@ namespace android { -namespace ReportPerformance { +std::unique_ptr dumpToJson(const PerformanceData& data) +{ + std::unique_ptr rootPtr = std::make_unique(Json::objectValue); + Json::Value& root = *rootPtr; + root["type"] = data.type; + root["frameCount"] = (Json::Value::Int)data.frameCount; + root["sampleRate"] = (Json::Value::Int)data.sampleRate; + root["workMsHist"] = data.workHist.toString(); + root["latencyMsHist"] = data.latencyHist.toString(); + root["warmupMsHist"] = data.warmupHist.toString(); + root["underruns"] = (Json::Value::Int64)data.underruns; + root["overruns"] = (Json::Value::Int64)data.overruns; + root["activeMs"] = (Json::Value::Int64)ns2ms(data.active); + root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start); + return rootPtr; +} +//------------------------------------------------------------------------------ + +namespace ReportPerformance { // TODO: use a function like this to extract logic from writeToFile // https://stackoverflow.com/a/9279620 diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index 561e8c7d41..d6403d3450 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -20,6 +20,7 @@ #define ANDROID_MEDIA_NBLOG_H #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include namespace android { @@ -39,7 +41,7 @@ class NBLog { public: - using log_hash_t = ReportPerformance::log_hash_t; + using log_hash_t = uint64_t; // FIXME Everything needed for client (writer API and registration) should be isolated // from the rest of the implementation. @@ -78,13 +80,29 @@ public: EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call // Types representing audio performance metrics + EVENT_THREAD_INFO, // thread type, frameCount and sampleRate, which give context + // for the metrics below. EVENT_LATENCY, // difference between frames presented by HAL and frames // written to HAL output sink, divided by sample rate. EVENT_WORK_TIME, // the time a thread takes to do work, e.g. read, write, etc. + EVENT_WARMUP_TIME, // thread warmup time + EVENT_UNDERRUN, // predicted thread underrun event timestamp + EVENT_OVERRUN, // predicted thread overrun event timestamp EVENT_UPPER_BOUND, // to check for invalid events }; + // NBLog custom-defined structs. Some NBLog Event types map to these structs. + + // mapped from EVENT_THREAD_INFO + struct thread_info_t { + // TODO make type an enum + int type; // Thread type: 0 for MIXER, 1 for CAPTURE, + // 2 for FASTMIXER, 3 for FASTCAPTURE + size_t frameCount; // number of frames per read or write buffer + unsigned sampleRate; // in frames per second + }; + template struct get_mapped; #define MAP_EVENT_TO_TYPE(E, T) \ template<> struct get_mapped { \ @@ -95,8 +113,12 @@ public: } // Maps an NBLog Event type to a C++ POD type. + MAP_EVENT_TO_TYPE(EVENT_THREAD_INFO, thread_info_t); MAP_EVENT_TO_TYPE(EVENT_LATENCY, double); - MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, uint64_t); + MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, int64_t); + MAP_EVENT_TO_TYPE(EVENT_WARMUP_TIME, double); + MAP_EVENT_TO_TYPE(EVENT_UNDERRUN, int64_t); + MAP_EVENT_TO_TYPE(EVENT_OVERRUN, int64_t); private: @@ -154,7 +176,7 @@ private: } #else template - inline T payload() { + inline T payload() const { static_assert(std::is_trivially_copyable::value && !std::is_pointer::value, "NBLog::EntryIterator payload must be trivially copyable, non-pointer type."); @@ -545,6 +567,7 @@ public: }; // --------------------------------------------------------------------------- + // TODO move Merger, MergeReader, and MergeThread to a separate file. // This class is used to read data from each thread's individual FIFO in shared memory // and write it to a single FIFO in local memory. @@ -581,7 +604,7 @@ public: void dump(int fd, int indent = 0); // process a particular snapshot of the reader - void getAndProcessSnapshot(Snapshot &snap, int author); + void processSnapshot(Snapshot &snap, int author); // call getSnapshot of the content of the reader's buffer and process the data void getAndProcessSnapshot(); @@ -596,7 +619,8 @@ public: // location within each author ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis; - PerformanceData mPerformanceData; + // compresses and stores audio performance data from each thread's buffers. + std::map mThreadPerformanceData; // handle author entry by looking up the author's name and appending it to the body // returns number of bytes read from fmtEntry @@ -645,15 +669,6 @@ public: }; // class NBLog -// TODO put somewhere else -static inline int64_t get_monotonic_ns() { - timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; - } - return 0; // should not happen. -} - } // namespace android #endif // ANDROID_MEDIA_NBLOG_H diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h index f2c3a48765..6677cdeeb6 100644 --- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h +++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h @@ -19,10 +19,11 @@ #include #include -#include +#include #include #include +#include namespace android { @@ -106,7 +107,7 @@ public: * * \return the histogram serialized as a string. */ - std::string serializeToString() const; + std::string toString() const; private: const double mBinSize; // Size of each bucket @@ -119,32 +120,50 @@ private: uint64_t mTotalCount = 0; // Total number of values recorded }; -// TODO For now this is a holder of audio performance metrics. The idea is essentially the same -// as PerformanceAnalysis, but the design structure is different. There is a PerformanceAnalysis -// instance for each thread writer (see PerformanceAnalysisMap later in this file), while a -// PerformanceData instance already takes into account each thread writer in its calculations. -// Eventually, this class should be merged with PerformanceAnalysis into some single entity. -/* - * PerformanceData stores audio performance data from audioflinger threads as histograms, - * time series, or counts, and outputs them in a machine-readable format. - */ -class PerformanceData { -public: - void addCycleTimeEntry(int author, double cycleTimeMs); - void addLatencyEntry(int author, double latencyMs); - void addWarmupTimeEntry(int author, double warmupTimeMs); - void addWarmupCyclesEntry(int author, double warmupCycles); - void dump(int fd, int indent = 0); -private: - // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192 and - // mSampleRate = 48000, which correspond to 2 and 7 seconds. - static constexpr Histogram::Config kCycleTimeConfig = { 0.25, 20, 2.}; - std::unordered_map mCycleTimeMsHists; +// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis +// also does some additional analyzing of data, while the purpose of this struct is +// to hold data. +struct PerformanceData { + // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192 + // and mSampleRate = 48000, which correspond to 2 and 7 seconds. + static constexpr Histogram::Config kWorkConfig = { 0.25, 20, 2.}; // Values based on trial and error logging. Need a better way to determine // bin size and lower/upper limits. static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.}; - std::unordered_map mLatencyMsHists; + + // Values based on trial and error logging. Need a better way to determine + // bin size and lower/upper limits. + static constexpr Histogram::Config kWarmupConfig = { 5., 10, 10.}; + + // Thread Info + // TODO make type an enum + int type = -1; // Thread type: 0 for MIXER, 1 for CAPTURE, + // 2 for FASTMIXER, 3 for FASTCAPTURE + size_t frameCount = 0; + unsigned sampleRate = 0; + + // Performance Data + Histogram workHist{kWorkConfig}; + Histogram latencyHist{kLatencyConfig}; + Histogram warmupHist{kWarmupConfig}; + int64_t underruns = 0; + int64_t overruns = 0; + nsecs_t active = 0; + nsecs_t start{systemTime()}; + + // Reset the performance data. This does not represent a thread state change. + // Thread info is not reset here because the data is meant to be a continuation of the thread + // that struct PerformanceData is associated with. + void reset() { + workHist.clear(); + latencyHist.clear(); + warmupHist.clear(); + underruns = 0; + overruns = 0; + active = 0; + start = systemTime(); + } }; //------------------------------------------------------------------------------ diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h index 1b1119758a..da0c7ca9b7 100644 --- a/media/libnblog/include/media/nblog/ReportPerformance.h +++ b/media/libnblog/include/media/nblog/ReportPerformance.h @@ -19,10 +19,22 @@ #include #include +#include #include +namespace Json { +class Value; +} + namespace android { +struct PerformanceData; + +// Dumps performance data to a JSON format. +// Return by pointer instead of by value to avoid dependency side effects of including +// the header of an external library. +std::unique_ptr dumpToJson(const PerformanceData& data); + namespace ReportPerformance { constexpr int kMsPerSec = 1000; diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index fd784a427b..f650b66cfe 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -195,6 +195,9 @@ void FastMixer::onStateChange() // to avoid blocking here and to prevent possible priority inversion mMixer = new AudioMixer(frameCount, mSampleRate); // FIXME See the other FIXME at FastMixer::setNBLogWriter() + // TODO define an int to thread type mapping for the "2" below. + const NBLog::thread_info_t info = { 2 /*FastMixer*/, frameCount, mSampleRate }; + LOG_THREAD_INFO(info); const size_t mixerFrameSize = mSinkChannelCount * audio_bytes_per_sample(mMixerBufferFormat); mMixerBufferSize = mixerFrameSize * frameCount; diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index 6f223dff14..09101d931a 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -22,6 +22,7 @@ #include "Configuration.h" #include #include +#include #include #include #include @@ -260,6 +261,9 @@ bool FastThread::threadLoop() mIsWarm = true; mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs; mDumpState->mWarmupCycles = mWarmupCycles; + const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1e3) + + (mMeasuredWarmupTs.tv_nsec * 1e-6); + LOG_WARMUP_TIME(measuredWarmupMs); } } mSleepNs = -1; @@ -270,6 +274,7 @@ bool FastThread::threadLoop() ALOGV("underrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); mDumpState->mUnderruns++; + LOG_UNDERRUN(audio_utils_ns_from_timespec(&newTs)); mIgnoreNextOverrun = true; } else if (nsec < mOverrunNs) { if (mIgnoreNextOverrun) { @@ -279,6 +284,7 @@ bool FastThread::threadLoop() ALOGV("overrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); mDumpState->mOverruns++; + LOG_OVERRUN(audio_utils_ns_from_timespec(&newTs)); } // This forces a minimum cycle time. It: // - compensates for an audio HAL with jitter due to sample rate conversion diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h index 6677470450..dd2e7c9db8 100644 --- a/services/audioflinger/TypedLogger.h +++ b/services/audioflinger/TypedLogger.h @@ -107,6 +107,23 @@ constexpr uint64_t hash(const char (&file)[n], uint32_t line) { #define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ x->log(ms); } while (0) +// Record thread warmup time in milliseconds. Parameter ms is of type double. +#define LOG_WARMUP_TIME(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->log(ms); } while (0) + +// Record thread underrun event nanosecond timestamp. Parameter ns is an int64_t. +#define LOG_UNDERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->log(ns); } while (0) + +// Record thread overrun event nanosecond timestamp. Parameter ns is an int64_t. +#define LOG_OVERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \ + x->log(ns); } while (0) + +// Record thread info. This currently includes type, frameCount, and sampleRate. +// Parameter type is thread_info_t as defined in NBLog.h. +#define LOG_THREAD_INFO(info) do { NBLog::Writer *x = tlNBLogWriter; \ + if (x != nullptr) x->log(info); } while (0) + namespace android { extern "C" { extern thread_local NBLog::Writer *tlNBLogWriter; -- GitLab From d6eee717dffdd8ee5c9dabaed80940d6838c47b9 Mon Sep 17 00:00:00 2001 From: Eric Tan Date: Fri, 7 Sep 2018 10:58:19 -0700 Subject: [PATCH 0267/1530] periodically push AudioFlinger thread statistics to media metrics Test: dumpsys media.metrics Bug: 68148948 Change-Id: I9b1538a8cff3f89f4689dceda800132349338a0d --- media/libnblog/Android.bp | 1 + media/libnblog/NBLog.cpp | 19 ++++- media/libnblog/PerformanceAnalysis.cpp | 4 +- media/libnblog/ReportPerformance.cpp | 69 ++++++++++++++++++- media/libnblog/include/media/nblog/NBLog.h | 16 ++++- .../include/media/nblog/PerformanceAnalysis.h | 6 +- .../include/media/nblog/ReportPerformance.h | 13 +++- 7 files changed, 113 insertions(+), 15 deletions(-) diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp index b15e79bc29..4a4aac1f87 100644 --- a/media/libnblog/Android.bp +++ b/media/libnblog/Android.bp @@ -13,6 +13,7 @@ cc_library_shared { "libbinder", "libcutils", "liblog", + "libmediametrics", "libutils", ], diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp index cd967e417b..aee0ea39e0 100644 --- a/media/libnblog/NBLog.cpp +++ b/media/libnblog/NBLog.cpp @@ -788,7 +788,7 @@ std::unique_ptr NBLog::Reader::getSnapshot() // writes the data to a map of class PerformanceAnalysis, based on their thread ID. void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author) { - PerformanceData& data = mThreadPerformanceData[author]; + ReportPerformance::PerformanceData& data = mThreadPerformanceData[author]; // We don't do "auto it" because it reduces readability in this case. for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) { switch (it->type) { @@ -856,6 +856,19 @@ void NBLog::MergeReader::getAndProcessSnapshot() processSnapshot(*(snapshots[i]), i); } } + checkPushToMediaMetrics(); +} + +void NBLog::MergeReader::checkPushToMediaMetrics() +{ + const nsecs_t now = systemTime(); + for (auto& item : mThreadPerformanceData) { + ReportPerformance::PerformanceData& data = item.second; + if (now - data.start >= kPeriodicMediaMetricsPush) { + (void)ReportPerformance::sendToMediaMetrics(data); + data.reset(); // data is persistent per thread + } + } } void NBLog::MergeReader::dump(int fd, int indent) @@ -864,8 +877,8 @@ void NBLog::MergeReader::dump(int fd, int indent) ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis); Json::Value root(Json::arrayValue); for (const auto& item : mThreadPerformanceData) { - const PerformanceData& data = item.second; - std::unique_ptr threadData = dumpToJson(data); + const ReportPerformance::PerformanceData& data = item.second; + std::unique_ptr threadData = ReportPerformance::dumpToJson(data); if (threadData == nullptr) { continue; } diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp index 999b22a18d..ca9bc2c2ae 100644 --- a/media/libnblog/PerformanceAnalysis.cpp +++ b/media/libnblog/PerformanceAnalysis.cpp @@ -48,6 +48,8 @@ namespace android { +namespace ReportPerformance { + void Histogram::add(double value) { // TODO Handle domain and range error exceptions? @@ -107,8 +109,6 @@ std::string Histogram::toString() const { //------------------------------------------------------------------------------ -namespace ReportPerformance { - // Given an audio processing wakeup timestamp, buckets the time interval // since the previous timestamp into a histogram, searches for // outliers, analyzes the outlier series for unexpectedly diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp index 98e11cd49b..aab67c6317 100644 --- a/media/libnblog/ReportPerformance.cpp +++ b/media/libnblog/ReportPerformance.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ namespace android { +namespace ReportPerformance { + std::unique_ptr dumpToJson(const PerformanceData& data) { std::unique_ptr rootPtr = std::make_unique(Json::objectValue); @@ -54,16 +57,76 @@ std::unique_ptr dumpToJson(const PerformanceData& data) return rootPtr; } -//------------------------------------------------------------------------------ +bool sendToMediaMetrics(const PerformanceData& data) +{ + // See documentation for these metrics here: + // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing + static constexpr char kThreadType[] = "android.media.audiothread.type"; + static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount"; + static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate"; + static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist"; + static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist"; + static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist"; + static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns"; + static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns"; + static constexpr char kThreadActive[] = "android.media.audiothread.activeMs"; + static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs"; + + std::unique_ptr item(new MediaAnalyticsItem("audiothread")); + + const Histogram &workHist = data.workHist; + if (workHist.totalCount() > 0) { + item->setCString(kThreadWorkHist, workHist.toString().c_str()); + } -namespace ReportPerformance { + const Histogram &latencyHist = data.latencyHist; + if (latencyHist.totalCount() > 0) { + item->setCString(kThreadLatencyHist, latencyHist.toString().c_str()); + } + + const Histogram &warmupHist = data.warmupHist; + if (warmupHist.totalCount() > 0) { + item->setCString(kThreadWarmupHist, warmupHist.toString().c_str()); + } + + if (data.underruns > 0) { + item->setInt64(kThreadUnderruns, data.underruns); + } + + if (data.overruns > 0) { + item->setInt64(kThreadOverruns, data.overruns); + } + + // Send to Media Metrics if the record is not empty. + // The thread and time info are added inside the if statement because + // we want to send them only if there are performance metrics to send. + if (item->count() > 0) { + // Add thread info fields. + const int type = data.type; + // TODO have a int-to-string mapping defined somewhere else for other thread types. + if (type == 2) { + item->setCString(kThreadType, "FASTMIXER"); + } else { + item->setCString(kThreadType, "UNKNOWN"); + } + item->setInt32(kThreadFrameCount, data.frameCount); + item->setInt32(kThreadSampleRate, data.sampleRate); + // Add time info fields. + item->setInt64(kThreadActive, data.active / 1000000); + item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000); + return item->selfrecord(); + } + return false; +} + +//------------------------------------------------------------------------------ // TODO: use a function like this to extract logic from writeToFile // https://stackoverflow.com/a/9279620 // Writes outlier intervals, timestamps, and histograms spanning long time intervals to file. // TODO: write data in binary format -void writeToFile(const std::deque> &hists, +void writeToFile(const std::deque> &hists, const std::deque> &outlierData, const std::deque &peakTimestamps, const char * directory, bool append, int author, log_hash_t hash) { diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h index d6403d3450..e43b0262c5 100644 --- a/media/libnblog/include/media/nblog/NBLog.h +++ b/media/libnblog/include/media/nblog/NBLog.h @@ -440,6 +440,7 @@ public: void log(Event event, const void *data, size_t length); void logvf(const char *fmt, va_list ap); + // helper functions for logging parts of a formatted entry void logStart(const char *fmt); void logTimestampFormat(); @@ -499,10 +500,12 @@ public: private: // Amount of tries for reader to catch up with writer in getSnapshot(). static constexpr int kMaxObtainTries = 3; + // invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and // Snapshot::end() EntryIterators to valid entries. static const std::unordered_set invalidBeginTypes; static const std::unordered_set invalidEndTypes; + // declared as const because audio_utils_fifo() constructor sp mIMemory; // ref-counted version, assigned only in constructor @@ -561,6 +564,7 @@ public: static void appendFloat(String8 *body, const void *data); static void appendPID(String8 *body, const void *data, size_t length); static void appendTimestamp(String8 *body, const void *data); + // The bufferDump functions are used for debugging only. static String8 bufferDump(const uint8_t *buffer, size_t size); static String8 bufferDump(const EntryIterator &it); @@ -603,11 +607,17 @@ public: MergeReader(const void *shared, size_t size, Merger &merger); void dump(int fd, int indent = 0); + // process a particular snapshot of the reader void processSnapshot(Snapshot &snap, int author); + // call getSnapshot of the content of the reader's buffer and process the data void getAndProcessSnapshot(); + // check for periodic push of performance data to media metrics, and perform + // the send if it is time to do so. + void checkPushToMediaMetrics(); + private: // FIXME Needs to be protected by a lock, // because even though our use of it is read-only there may be asynchronous updates @@ -620,7 +630,11 @@ public: ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis; // compresses and stores audio performance data from each thread's buffers. - std::map mThreadPerformanceData; + // first parameter is author, i.e. thread index. + std::map mThreadPerformanceData; + + // how often to push data to Media Metrics + static constexpr nsecs_t kPeriodicMediaMetricsPush = s2ns((nsecs_t)2 * 60 * 60); // 2 hours // handle author entry by looking up the author's name and appending it to the body // returns number of bytes read from fmtEntry diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h index 6677cdeeb6..80eddb188b 100644 --- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h +++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h @@ -29,6 +29,8 @@ namespace android { class String8; +namespace ReportPerformance { + // TODO make this a templated class and put it in a separate file. // The templated parameters would be bin size and low limit. /* @@ -168,8 +170,6 @@ struct PerformanceData { //------------------------------------------------------------------------------ -namespace ReportPerformance { - class PerformanceAnalysis; // a map of PerformanceAnalysis instances @@ -223,7 +223,7 @@ private: std::deque mPeakTimestamps; // stores buffer period histograms with timestamp of first sample - std::deque> mHists; + std::deque> mHists; // Parameters used when detecting outliers struct BufferPeriod { diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h index da0c7ca9b7..09bb2a00b7 100644 --- a/media/libnblog/include/media/nblog/ReportPerformance.h +++ b/media/libnblog/include/media/nblog/ReportPerformance.h @@ -28,6 +28,8 @@ class Value; namespace android { +namespace ReportPerformance { + struct PerformanceData; // Dumps performance data to a JSON format. @@ -35,7 +37,12 @@ struct PerformanceData; // the header of an external library. std::unique_ptr dumpToJson(const PerformanceData& data); -namespace ReportPerformance { +// Send one thread's data to media metrics, if the performance data is nontrivial (i.e. not +// all zero values). Return true if data was sent, false if there is nothing to write +// or an error occurred while writing. +bool sendToMediaMetrics(const PerformanceData& data); + +//------------------------------------------------------------------------------ constexpr int kMsPerSec = 1000; constexpr int kSecPerMin = 60; @@ -43,7 +50,7 @@ constexpr int kSecPerMin = 60; constexpr int kJiffyPerMs = 10; // time unit for histogram as a multiple of milliseconds // stores a histogram: key: observed buffer period (multiple of jiffy). value: count -using Histogram = std::map; +using Hist = std::map; using msInterval = double; using jiffyInterval = double; @@ -66,7 +73,7 @@ static inline uint32_t log2(uint32_t x) { } // Writes outlier intervals, timestamps, peaks timestamps, and histograms to a file. -void writeToFile(const std::deque> &hists, +void writeToFile(const std::deque> &hists, const std::deque> &outlierData, const std::deque &peakTimestamps, const char * kDirectory, bool append, int author, log_hash_t hash); -- GitLab From 41929fb5f625b80fe5a492fdef3456a74987001e Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Sun, 9 Sep 2018 08:29:56 -0700 Subject: [PATCH 0268/1530] MediaPlayer2: use protobuf instead of parcel for notification message Test: MediaPlayer2Test Bug: 112767225 Change-Id: I7f5bd177c138a8ad5c005f4505d4d9c5042486a8 --- .../mediaplayer2/MediaPlayer2Interface.h | 5 +- .../include/mediaplayer2/mediaplayer2.h | 5 +- media/libmediaplayer2/mediaplayer2.cpp | 5 +- .../nuplayer2/GenericSource2.cpp | 15 ++++-- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 40 +++++++-------- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 2 +- .../nuplayer2/NuPlayer2Driver.cpp | 51 +++++++++---------- .../nuplayer2/NuPlayer2Driver.h | 4 +- .../nuplayer2/NuPlayer2Drm.cpp | 17 +++---- .../libmediaplayer2/nuplayer2/NuPlayer2Drm.h | 6 ++- 10 files changed, 78 insertions(+), 72 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index 715bbe0dde..55d812b440 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -60,7 +60,8 @@ struct ANativeWindowWrapper; class MediaPlayer2InterfaceListener: public RefBase { public: - virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0; + virtual void notify(int64_t srcId, int msg, int ext1, int ext2, + const PlayerMessage *obj) = 0; }; class MediaPlayer2Interface : public AHandler { @@ -240,7 +241,7 @@ public: mListener = listener; } - void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) { + void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const PlayerMessage *obj=NULL) { sp listener; { Mutex::Autolock autoLock(mListenerLock); diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 628e7328cf..e03b499cd6 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -42,7 +42,8 @@ class MediaPlayer2AudioOutput; class MediaPlayer2Listener: virtual public RefBase { public: - virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0; + virtual void notify(int64_t srcId, int msg, int ext1, int ext2, + const PlayerMessage *obj = NULL) = 0; }; class MediaPlayer2 : public MediaPlayer2InterfaceListener @@ -89,7 +90,7 @@ public: bool isLooping(); status_t setVolume(float leftVolume, float rightVolume); void notify(int64_t srcId, int msg, int ext1, int ext2, - const Parcel *obj = NULL); + const PlayerMessage *obj = NULL); status_t invoke(const PlayerMessage &request, PlayerMessage *reply); status_t setMetadataFilter(const Parcel& filter); status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 18cf040044..8d0df8ae96 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -223,7 +223,8 @@ public: ~proxyListener() { }; - virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override { + virtual void notify(int64_t srcId, int msg, int ext1, int ext2, + const PlayerMessage *obj) override { sp player = mPlayer.promote(); if (player != NULL) { player->notify(srcId, msg, ext1, ext2, obj); @@ -1271,7 +1272,7 @@ void MediaPlayer2::addNewMetadataUpdate(media::Metadata::Type metadata_type) { } } -void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) { +void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) { ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d", (long long)srcId, msg, ext1, ext2); diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp index e317e23d39..4ce1a881ed 100644 --- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp @@ -1590,15 +1590,20 @@ status_t NuPlayer2::GenericSource2::checkDrmInfo() return OK; // source without DRM info } - sp drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo); - ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size()); + PlayerMessage playerMsg; + status_t ret = NuPlayer2Drm::retrieveDrmInfo(psshInfo, &playerMsg); + ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)playerMsg.ByteSize()); - if (drmInfoBuffer->size() == 0) { - ALOGE("checkDrmInfo: Unexpected parcel size: 0"); + if (ret != OK) { + ALOGE("checkDrmInfo: failed to retrive DrmInfo %d", ret); return UNKNOWN_ERROR; } - notifyDrmInfo(drmInfoBuffer); + int size = playerMsg.ByteSize(); + sp drmInfoBuf = new ABuffer(size); + playerMsg.SerializeToArray(drmInfoBuf->data(), size); + drmInfoBuf->setRange(0, size); + notifyDrmInfo(drmInfoBuf); return OK; } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 2a0fbddaa6..2ec7bc063a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -2145,7 +2145,8 @@ void NuPlayer2::updateVideoSize( displayHeight); } -void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) { +void NuPlayer2::notifyListener( + int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) { if (mDriver == NULL) { return; } @@ -2616,15 +2617,15 @@ void NuPlayer2::onSourceNotify(const sp &msg) { // Modular DRM case Source::kWhatDrmInfo: { - Parcel parcel; + PlayerMessage playerMsg; sp drmInfo; CHECK(msg->findBuffer("drmInfo", &drmInfo)); - parcel.setData(drmInfo->data(), drmInfo->size()); + playerMsg.ParseFromArray(drmInfo->data(), drmInfo->size()); - ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p parcel size: %zu", - drmInfo.get(), parcel.dataSize()); + ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p playerMsg size: %d", + drmInfo.get(), playerMsg.ByteSize()); - notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel); + notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &playerMsg); break; } @@ -2845,28 +2846,24 @@ void NuPlayer2::sendSubtitleData(const sp &buffer, int32_t baseIndex) { CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); CHECK(buffer->meta()->findInt64("durationUs", &durationUs)); - Parcel in; - in.writeInt32(trackIndex + baseIndex); - in.writeInt64(timeUs); - in.writeInt64(durationUs); - in.writeInt32(buffer->size()); - in.writeInt32(buffer->size()); - in.write(buffer->data(), buffer->size()); + PlayerMessage playerMsg; + playerMsg.add_values()->set_int32_value(trackIndex + baseIndex); + playerMsg.add_values()->set_int64_value(timeUs); + playerMsg.add_values()->set_int64_value(durationUs); + playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size()); - notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in); + notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &playerMsg); } void NuPlayer2::sendTimedMetaData(const sp &buffer) { int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); - Parcel in; - in.writeInt64(timeUs); - in.writeInt32(buffer->size()); - in.writeInt32(buffer->size()); - in.write(buffer->data(), buffer->size()); + PlayerMessage playerMsg; + playerMsg.add_values()->set_int64_value(timeUs); + playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size()); - notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in); + notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &playerMsg); } void NuPlayer2::sendTimedTextData(const sp &buffer) { @@ -2896,7 +2893,8 @@ void NuPlayer2::sendTimedTextData(const sp &buffer) { } if ((parcel.dataSize() > 0)) { - notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel); + // TODO: convert text data to PlayerMessage + // notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel); } else { // send an empty timed text notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index c92d408941..77845ac5b4 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -296,7 +296,7 @@ private: const sp &inputFormat, const sp &outputFormat = NULL); - void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL); + void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL); void handleFlushComplete(bool audio, bool isDecoder); void finishFlushIfPossible(); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index fd231a3ec7..4d799b815e 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -44,40 +44,39 @@ static const int kDumpLockSleepUs = 20000; namespace android { -struct ParcelWrapper : public RefBase { - static sp Create(const Parcel *p) { +struct PlayerMessageWrapper : public RefBase { + static sp Create(const PlayerMessage *p) { if (p != NULL) { - sp pw = new ParcelWrapper(); - if (pw->appendFrom(p) == OK) { - return pw; - } + sp pw = new PlayerMessageWrapper(); + pw->copyFrom(p); + return pw; } return NULL; } - const Parcel *getParcel() { - return mParcel; + const PlayerMessage *getPlayerMessage() { + return mPlayerMessage; } protected: - virtual ~ParcelWrapper() { - if (mParcel != NULL) { - delete mParcel; + virtual ~PlayerMessageWrapper() { + if (mPlayerMessage != NULL) { + delete mPlayerMessage; } } private: - ParcelWrapper() - : mParcel(NULL) { } + PlayerMessageWrapper() + : mPlayerMessage(NULL) { } - status_t appendFrom(const Parcel *p) { - if (mParcel == NULL) { - mParcel = new Parcel; + void copyFrom(const PlayerMessage *p) { + if (mPlayerMessage == NULL) { + mPlayerMessage = new PlayerMessage; } - return mParcel->appendFrom(p, 0 /* start */, p->dataSize()); + mPlayerMessage->CopyFrom(*p); } - Parcel *mParcel; + PlayerMessage *mPlayerMessage; }; // key for media statistics @@ -828,12 +827,12 @@ void NuPlayer2Driver::onMessageReceived(const sp &msg) { CHECK(msg->findInt32("messageId", &msgId)); msg->findInt32("ext1", &ext1); msg->findInt32("ext2", &ext2); - sp in; + sp in; sp obj; - if (msg->findObject("parcel", &obj) && obj != NULL) { - in = static_cast(obj.get()); + if (msg->findObject("obj", &obj) && obj != NULL) { + in = static_cast(obj.get()); } - sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel())); + sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage())); break; } default: @@ -842,16 +841,16 @@ void NuPlayer2Driver::onMessageReceived(const sp &msg) { } void NuPlayer2Driver::notifyListener( - int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) { + int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) { Mutex::Autolock autoLock(mLock); notifyListener_l(srcId, msg, ext1, ext2, in); } void NuPlayer2Driver::notifyListener_l( - int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) { + int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) { ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)", this, (long long)srcId, msg, ext1, ext2, - (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping); + (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping); if (srcId == mSrcId) { switch (msg) { case MEDIA2_PLAYBACK_COMPLETE: @@ -918,7 +917,7 @@ void NuPlayer2Driver::notifyListener_l( notify->setInt32("messageId", msg); notify->setInt32("ext1", ext1); notify->setInt32("ext2", ext2); - notify->setObject("parcel", ParcelWrapper::Create(in)); + notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in)); notify->post(); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 8dfcaa165a..50ee1734c6 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -78,7 +78,7 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { void notifyRebufferingWhenExit(int64_t srcId, bool status); void notifySeekComplete(int64_t srcId); void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0, - const Parcel *in = NULL); + const PlayerMessage *in = NULL); void notifyFlagsChanged(int64_t srcId, uint32_t flags); // Modular DRM @@ -145,7 +145,7 @@ private: status_t start_l(); void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0, - const Parcel *in = NULL); + const PlayerMessage *in = NULL); DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver); }; diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp index 4853ae1eb9..57d1147b6f 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp @@ -133,9 +133,8 @@ sp NuPlayer2Drm::retrieveDrmInfo(const void *pssh, uint32_t psshsize) return drmInfoBuffer; } -sp NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo) +status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg) { - std::ostringstream pssh, drmInfo; // 0) Generate PSSH bytes @@ -153,22 +152,20 @@ sp NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo) ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %u %s", psshSize, psshHex); // 1) Write PSSH bytes - drmInfo.write(reinterpret_cast(&psshSize), sizeof(psshSize)); - drmInfo.write(reinterpret_cast(pssh.str().c_str()), psshSize); + playerMsg->add_values()->set_bytes_value( + reinterpret_cast(pssh.str().c_str()), psshSize); // 2) Write supportedDRMs uint32_t numentries = psshInfo->numentries; - drmInfo.write(reinterpret_cast(&numentries), sizeof(numentries)); + playerMsg->add_values()->set_int32_value(numentries); for (size_t i = 0; i < numentries; i++) { PsshEntry *entry = &psshInfo->entries[i]; - drmInfo.write(reinterpret_cast(&entry->uuid), sizeof(entry->uuid)); + playerMsg->add_values()->set_bytes_value( + reinterpret_cast(&entry->uuid), sizeof(entry->uuid)); ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i, DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string()); } - - sp drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp()); - drmInfoBuf->setRange(0, drmInfo.tellp()); - return drmInfoBuf; + return OK; } } // namespace android diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h index 99d2415ee7..968d1bea2d 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h @@ -23,6 +23,10 @@ #include #include +#include "mediaplayer2.pb.h" + +using android::media::MediaPlayer2Proto::PlayerMessage; + namespace android { struct DrmUUID { @@ -80,7 +84,7 @@ namespace android { public: static sp retrieveDrmInfo(const void *pssh, uint32_t psshsize); - static sp retrieveDrmInfo(PsshInfo *); + static status_t retrieveDrmInfo(PsshInfo *, PlayerMessage *); }; // NuPlayer2Drm -- GitLab From a0e816ac2a7b1394774776f334de9e066367ecc0 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 10 Sep 2018 19:46:49 -0700 Subject: [PATCH 0269/1530] MediaPlayer2: use protobuf instead of parcel for TimedText Test: MediaPlayer2Test Bug: 112767225 Change-Id: I3f6b06d49b2a245e8350d792c01bccb9fd44037f --- media/libmediaplayer2/Android.bp | 2 +- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 19 +- media/libstagefright/timedtext/Android.bp | 33 +++ .../timedtext/TextDescriptions2.cpp | 188 ++++++++++++++++++ .../timedtext/TextDescriptions2.h | 88 ++++++++ 5 files changed, 319 insertions(+), 11 deletions(-) create mode 100644 media/libstagefright/timedtext/TextDescriptions2.cpp create mode 100644 media/libstagefright/timedtext/TextDescriptions2.h diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 300adecd74..0672ca9337 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -60,7 +60,7 @@ cc_library { "libprotobuf-cpp-lite", "libstagefright_nuplayer2", "libstagefright_rtsp", - "libstagefright_timedtext", + "libstagefright_timedtext2", ], export_include_dirs: [ diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 2ec7bc063a..2f90825061 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -33,7 +33,7 @@ #include "NuPlayer2Source.h" #include "RTSPSource2.h" #include "GenericSource2.h" -#include "TextDescriptions.h" +#include "TextDescriptions2.h" #include "ATSParser.h" @@ -2870,7 +2870,7 @@ void NuPlayer2::sendTimedTextData(const sp &buffer) { const void *data; size_t size = 0; int64_t timeUs; - int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP; + int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP; AString mime; CHECK(buffer->meta()->findString("mime", &mime)); @@ -2879,22 +2879,21 @@ void NuPlayer2::sendTimedTextData(const sp &buffer) { data = buffer->data(); size = buffer->size(); - Parcel parcel; + PlayerMessage playerMsg; if (size > 0) { CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); int32_t global = 0; if (buffer->meta()->findInt32("global", &global) && global) { - flag |= TextDescriptions::GLOBAL_DESCRIPTIONS; + flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS; } else { - flag |= TextDescriptions::LOCAL_DESCRIPTIONS; + flag |= TextDescriptions2::LOCAL_DESCRIPTIONS; } - TextDescriptions::getParcelOfDescriptions( - (const uint8_t *)data, size, flag, timeUs / 1000, &parcel); + TextDescriptions2::getPlayerMessageOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg); } - if ((parcel.dataSize() > 0)) { - // TODO: convert text data to PlayerMessage - // notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel); + if (playerMsg.values_size() > 0) { + notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg); } else { // send an empty timed text notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0); } diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp index a5ad6c60b9..7c51333e7b 100644 --- a/media/libstagefright/timedtext/Android.bp +++ b/media/libstagefright/timedtext/Android.bp @@ -25,3 +25,36 @@ cc_library_static { shared_libs: ["libmedia"], } + +cc_library_static { + name: "libstagefright_timedtext2", + + srcs: ["TextDescriptions2.cpp"], + + static_libs: [ + "libmediaplayer2-protos", + "libprotobuf-cpp-lite", + ], + + cflags: [ + "-Wno-multichar", + "-Werror", + "-Wall", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + diag: { + cfi: true, + }, + }, + + include_dirs: [ + "frameworks/av/media/libstagefright", + ], + + shared_libs: ["libmedia"], +} diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp new file mode 100644 index 0000000000..f48eaccbe3 --- /dev/null +++ b/media/libstagefright/timedtext/TextDescriptions2.cpp @@ -0,0 +1,188 @@ +/* + * 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. + */ + +#include "TextDescriptions2.h" +#include +#include + +namespace android { + +TextDescriptions2::TextDescriptions2() { +} + +status_t TextDescriptions2::getPlayerMessageOfDescriptions( + const uint8_t *data, ssize_t size, + uint32_t flags, int timeMs, PlayerMessage *playerMsg) { + if (flags & IN_BAND_TEXT_3GPP) { + if (flags & GLOBAL_DESCRIPTIONS) { + return extract3GPPGlobalDescriptions(data, size, playerMsg); + } else if (flags & LOCAL_DESCRIPTIONS) { + return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg); + } + } else if (flags & OUT_OF_BAND_TEXT_SRT) { + if (flags & LOCAL_DESCRIPTIONS) { + return extractSRTLocalDescriptions(data, size, timeMs, playerMsg); + } + } + + return ERROR_UNSUPPORTED; +} + +// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage. +// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be +// parsed in TimedText.java. +status_t TextDescriptions2::extractSRTLocalDescriptions( + const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) { + playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING); + playerMsg->add_values()->set_int32_value(KEY_START_TIME); + playerMsg->add_values()->set_int32_value(timeMs); + + playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT); + playerMsg->add_values()->set_bytes_value(data, size); + + return OK; +} + +// Extract the local 3GPP display descriptions. 3GPP local descriptions +// are appended to the text sample if any. +status_t TextDescriptions2::extract3GPPLocalDescriptions( + const uint8_t *data, ssize_t size, + int timeMs, PlayerMessage *playerMsg) { + + playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING); + + // write start time to display this text sample + playerMsg->add_values()->set_int32_value(KEY_START_TIME); + playerMsg->add_values()->set_int32_value(timeMs); + + if (size < 2) { + return OK; + } + ssize_t textLen = (*data) << 8 | (*(data + 1)); + + if (size < textLen + 2) { + return OK; + } + + // write text sample length and text sample itself + playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT); + playerMsg->add_values()->set_bytes_value(data + 2, textLen); + + if (size > textLen + 2) { + data += (textLen + 2); + size -= (textLen + 2); + } else { + return OK; + } + + while (size >= 8) { + const uint8_t *tmpData = data; + ssize_t chunkSize = U32_AT(tmpData); // size includes size and type + uint32_t chunkType = U32_AT(tmpData + 4); + + if (chunkSize <= 8 || chunkSize > size) { + return OK; + } + + size_t remaining = chunkSize - 8; + + tmpData += 8; + + switch(chunkType) { + // 'tbox' box to indicate the position of the text with values + // of top, left, bottom and right + case FOURCC('t', 'b', 'o', 'x'): + { + if (remaining < 8) { + return OK; + } + playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6)); + + tmpData += 8; + remaining -= 8; + break; + } + default: + { + break; + } + } + + data += chunkSize; + size -= chunkSize; + } + + return OK; +} + +// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage +status_t TextDescriptions2::extract3GPPGlobalDescriptions( + const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) { + + playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING); + + while (size >= 8) { + ssize_t chunkSize = U32_AT(data); + uint32_t chunkType = U32_AT(data + 4); + const uint8_t *tmpData = data; + tmpData += 8; + size_t remaining = size - 8; + + if (size < chunkSize) { + return OK; + } + switch(chunkType) { + case FOURCC('t', 'x', '3', 'g'): + { + if (remaining < 18) { + return OK; + } + // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA + tmpData += 18; + remaining -= 18; + + if (remaining < 8) { + return OK; + } + playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4)); + playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6)); + + tmpData += 8; + remaining -= 18; + // Ignore remaining data. + break; + } + default: + { + break; + } + } + + data += chunkSize; + size -= chunkSize; + } + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h new file mode 100644 index 0000000000..7c7d2d053e --- /dev/null +++ b/media/libstagefright/timedtext/TextDescriptions2.h @@ -0,0 +1,88 @@ + /* + * 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. + */ + +#ifndef TEXT_DESCRIPTIONS2_H_ + +#define TEXT_DESCRIPTIONS2_H_ + +#include +#include + +#include "mediaplayer2.pb.h" + +using android::media::MediaPlayer2Proto::PlayerMessage; + +namespace android { + +class TextDescriptions2 { +public: + enum { + IN_BAND_TEXT_3GPP = 0x01, + OUT_OF_BAND_TEXT_SRT = 0x02, + + GLOBAL_DESCRIPTIONS = 0x100, + LOCAL_DESCRIPTIONS = 0x200, + }; + + static status_t getPlayerMessageOfDescriptions( + const uint8_t *data, ssize_t size, + uint32_t flags, int timeMs, PlayerMessage *playerMsg); +private: + TextDescriptions2(); + + enum { + // These keys must be in sync with the keys in TimedText.java + KEY_DISPLAY_FLAGS = 1, // int + KEY_STYLE_FLAGS = 2, // int + KEY_BACKGROUND_COLOR_RGBA = 3, // int + KEY_HIGHLIGHT_COLOR_RGBA = 4, // int + KEY_SCROLL_DELAY = 5, // int + KEY_WRAP_TEXT = 6, // int + KEY_START_TIME = 7, // int + KEY_STRUCT_BLINKING_TEXT_LIST = 8, // List + KEY_STRUCT_FONT_LIST = 9, // List + KEY_STRUCT_HIGHLIGHT_LIST = 10, // List + KEY_STRUCT_HYPER_TEXT_LIST = 11, // List + KEY_STRUCT_KARAOKE_LIST = 12, // List + KEY_STRUCT_STYLE_LIST = 13, // List - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/MediaComponents/res/values/styles.xml b/packages/MediaComponents/res/values/styles.xml deleted file mode 100644 index bde6900349..0000000000 --- a/packages/MediaComponents/res/values/styles.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/packages/MediaComponents/src/com/android/media/RoutePlayer.java b/packages/MediaComponents/src/com/android/media/RoutePlayer.java deleted file mode 100644 index ebff0e23a7..0000000000 --- a/packages/MediaComponents/src/com/android/media/RoutePlayer.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.content.Context; -import android.media.DataSourceDesc; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; - -import androidx.annotation.RequiresApi; - -import com.android.support.mediarouter.media.MediaItemStatus; -import com.android.support.mediarouter.media.MediaRouter; -import com.android.support.mediarouter.media.MediaSessionStatus; -import com.android.support.mediarouter.media.RemotePlaybackClient; -import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback; -import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback; -import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback; - -@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) -public class RoutePlayer extends MediaSession.Callback { - public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE - | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO - | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND; - - private RemotePlaybackClient mClient; - private String mSessionId; - private String mItemId; - private PlayerEventCallback mCallback; - - private StatusCallback mStatusCallback = new StatusCallback() { - @Override - public void onItemStatusChanged(Bundle data, - String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - updateSessionStatus(sessionId, sessionStatus); - updateItemStatus(itemId, itemStatus); - } - }; - - public RoutePlayer(Context context, MediaRouter.RouteInfo route) { - mClient = new RemotePlaybackClient(context, route); - mClient.setStatusCallback(mStatusCallback); - mClient.startSession(null, new SessionActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus) { - updateSessionStatus(sessionId, sessionStatus); - } - }); - } - - @Override - public void onPlay() { - mClient.resume(null, new SessionActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus) { - updateSessionStatus(sessionId, sessionStatus); - } - }); - } - - @Override - public void onPause() { - mClient.pause(null, new SessionActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus) { - updateSessionStatus(sessionId, sessionStatus); - } - }); - } - - @Override - public void onSeekTo(long pos) { - mClient.seek(mItemId, pos, null, new ItemActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - updateSessionStatus(sessionId, sessionStatus); - updateItemStatus(itemId, itemStatus); - } - }); - } - - @Override - public void onStop() { - mClient.stop(null, new SessionActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus) { - updateSessionStatus(sessionId, sessionStatus); - } - }); - } - - public void setPlayerEventCallback(PlayerEventCallback callback) { - mCallback = callback; - } - - public void openVideo(DataSourceDesc dsd) { - mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - updateSessionStatus(sessionId, sessionStatus); - updateItemStatus(itemId, itemStatus); - playInternal(dsd.getUri()); - } - }); - } - - public void release() { - if (mClient != null) { - mClient.release(); - mClient = null; - } - if (mCallback != null) { - mCallback = null; - } - } - - private void playInternal(Uri uri) { - mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() { - @Override - public void onResult(Bundle data, - String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - updateSessionStatus(sessionId, sessionStatus); - updateItemStatus(itemId, itemStatus); - } - }); - } - - private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) { - mSessionId = sessionId; - } - - private void updateItemStatus(String itemId, MediaItemStatus itemStatus) { - mItemId = itemId; - if (itemStatus == null || mCallback == null) return; - mCallback.onPlayerStateChanged(itemStatus); - } - - public static abstract class PlayerEventCallback { - public void onPlayerStateChanged(MediaItemStatus itemStatus) { } - } -} diff --git a/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java b/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java deleted file mode 100644 index ff7eec90af..0000000000 --- a/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.subtitle; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.media.MediaFormat; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextPaint; -import android.text.style.CharacterStyle; -import android.text.style.StyleSpan; -import android.text.style.UnderlineSpan; -import android.text.style.UpdateAppearance; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.accessibility.CaptioningManager; -import android.view.accessibility.CaptioningManager.CaptionStyle; -import android.view.accessibility.CaptioningManager.CaptioningChangeListener; -import android.widget.LinearLayout; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Vector; - -// Note: This is forked from android.media.ClosedCaptionRenderer since P -public class ClosedCaptionRenderer extends SubtitleController.Renderer { - private final Context mContext; - private Cea608CCWidget mCCWidget; - - public ClosedCaptionRenderer(Context context) { - mContext = context; - } - - @Override - public boolean supports(MediaFormat format) { - if (format.containsKey(MediaFormat.KEY_MIME)) { - String mimeType = format.getString(MediaFormat.KEY_MIME); - return MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType); - } - return false; - } - - @Override - public SubtitleTrack createTrack(MediaFormat format) { - String mimeType = format.getString(MediaFormat.KEY_MIME); - if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType)) { - if (mCCWidget == null) { - mCCWidget = new Cea608CCWidget(mContext); - } - return new Cea608CaptionTrack(mCCWidget, format); - } - throw new RuntimeException("No matching format: " + format.toString()); - } -} - -class Cea608CaptionTrack extends SubtitleTrack { - private final Cea608CCParser mCCParser; - private final Cea608CCWidget mRenderingWidget; - - Cea608CaptionTrack(Cea608CCWidget renderingWidget, MediaFormat format) { - super(format); - - mRenderingWidget = renderingWidget; - mCCParser = new Cea608CCParser(mRenderingWidget); - } - - @Override - public void onData(byte[] data, boolean eos, long runID) { - mCCParser.parse(data); - } - - @Override - public RenderingWidget getRenderingWidget() { - return mRenderingWidget; - } - - @Override - public void updateView(Vector activeCues) { - // Overriding with NO-OP, CC rendering by-passes this - } -} - -/** - * Abstract widget class to render a closed caption track. - */ -abstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget { - - interface ClosedCaptionLayout { - void setCaptionStyle(CaptionStyle captionStyle); - void setFontScale(float scale); - } - - private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; - - /** Captioning manager, used to obtain and track caption properties. */ - private final CaptioningManager mManager; - - /** Current caption style. */ - protected CaptionStyle mCaptionStyle; - - /** Callback for rendering changes. */ - protected OnChangedListener mListener; - - /** Concrete layout of CC. */ - protected ClosedCaptionLayout mClosedCaptionLayout; - - /** Whether a caption style change listener is registered. */ - private boolean mHasChangeListener; - - public ClosedCaptionWidget(Context context) { - this(context, null); - } - - public ClosedCaptionWidget(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { - this(context, attrs, defStyle, 0); - } - - public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - - // Cannot render text over video when layer type is hardware. - setLayerType(View.LAYER_TYPE_SOFTWARE, null); - - mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); - mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); - - mClosedCaptionLayout = createCaptionLayout(context); - mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); - mClosedCaptionLayout.setFontScale(mManager.getFontScale()); - addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - - requestLayout(); - } - - public abstract ClosedCaptionLayout createCaptionLayout(Context context); - - @Override - public void setOnChangedListener(OnChangedListener listener) { - mListener = listener; - } - - @Override - public void setSize(int width, int height) { - final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); - final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - - measure(widthSpec, heightSpec); - layout(0, 0, width, height); - } - - @Override - public void setVisible(boolean visible) { - if (visible) { - setVisibility(View.VISIBLE); - } else { - setVisibility(View.GONE); - } - - manageChangeListener(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - manageChangeListener(); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - manageChangeListener(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b); - } - - /** - * Manages whether this renderer is listening for caption style changes. - */ - private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { - @Override - public void onUserStyleChanged(CaptionStyle userStyle) { - mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); - mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); - } - - @Override - public void onFontScaleChanged(float fontScale) { - mClosedCaptionLayout.setFontScale(fontScale); - } - }; - - private void manageChangeListener() { - final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; - if (mHasChangeListener != needsListener) { - mHasChangeListener = needsListener; - - if (needsListener) { - mManager.addCaptioningChangeListener(mCaptioningListener); - } else { - mManager.removeCaptioningChangeListener(mCaptioningListener); - } - } - } -} - -/** - * CCParser processes CEA-608 closed caption data. - * - * It calls back into OnDisplayChangedListener upon - * display change with styled text for rendering. - * - */ -class Cea608CCParser { - public static final int MAX_ROWS = 15; - public static final int MAX_COLS = 32; - - private static final String TAG = "Cea608CCParser"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private static final int INVALID = -1; - - // EIA-CEA-608: Table 70 - Control Codes - private static final int RCL = 0x20; - private static final int BS = 0x21; - private static final int AOF = 0x22; - private static final int AON = 0x23; - private static final int DER = 0x24; - private static final int RU2 = 0x25; - private static final int RU3 = 0x26; - private static final int RU4 = 0x27; - private static final int FON = 0x28; - private static final int RDC = 0x29; - private static final int TR = 0x2a; - private static final int RTD = 0x2b; - private static final int EDM = 0x2c; - private static final int CR = 0x2d; - private static final int ENM = 0x2e; - private static final int EOC = 0x2f; - - // Transparent Space - private static final char TS = '\u00A0'; - - // Captioning Modes - private static final int MODE_UNKNOWN = 0; - private static final int MODE_PAINT_ON = 1; - private static final int MODE_ROLL_UP = 2; - private static final int MODE_POP_ON = 3; - private static final int MODE_TEXT = 4; - - private final DisplayListener mListener; - - private int mMode = MODE_PAINT_ON; - private int mRollUpSize = 4; - private int mPrevCtrlCode = INVALID; - - private CCMemory mDisplay = new CCMemory(); - private CCMemory mNonDisplay = new CCMemory(); - private CCMemory mTextMem = new CCMemory(); - - Cea608CCParser(DisplayListener listener) { - mListener = listener; - } - - public void parse(byte[] data) { - CCData[] ccData = CCData.fromByteArray(data); - - for (int i = 0; i < ccData.length; i++) { - if (DEBUG) { - Log.d(TAG, ccData[i].toString()); - } - - if (handleCtrlCode(ccData[i]) - || handleTabOffsets(ccData[i]) - || handlePACCode(ccData[i]) - || handleMidRowCode(ccData[i])) { - continue; - } - - handleDisplayableChars(ccData[i]); - } - } - - interface DisplayListener { - void onDisplayChanged(SpannableStringBuilder[] styledTexts); - CaptionStyle getCaptionStyle(); - } - - private CCMemory getMemory() { - // get the CC memory to operate on for current mode - switch (mMode) { - case MODE_POP_ON: - return mNonDisplay; - case MODE_TEXT: - // TODO(chz): support only caption mode for now, - // in text mode, dump everything to text mem. - return mTextMem; - case MODE_PAINT_ON: - case MODE_ROLL_UP: - return mDisplay; - default: - Log.w(TAG, "unrecoginized mode: " + mMode); - } - return mDisplay; - } - - private boolean handleDisplayableChars(CCData ccData) { - if (!ccData.isDisplayableChar()) { - return false; - } - - // Extended char includes 1 automatic backspace - if (ccData.isExtendedChar()) { - getMemory().bs(); - } - - getMemory().writeText(ccData.getDisplayText()); - - if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) { - updateDisplay(); - } - - return true; - } - - private boolean handleMidRowCode(CCData ccData) { - StyleCode m = ccData.getMidRow(); - if (m != null) { - getMemory().writeMidRowCode(m); - return true; - } - return false; - } - - private boolean handlePACCode(CCData ccData) { - PAC pac = ccData.getPAC(); - - if (pac != null) { - if (mMode == MODE_ROLL_UP) { - getMemory().moveBaselineTo(pac.getRow(), mRollUpSize); - } - getMemory().writePAC(pac); - return true; - } - - return false; - } - - private boolean handleTabOffsets(CCData ccData) { - int tabs = ccData.getTabOffset(); - - if (tabs > 0) { - getMemory().tab(tabs); - return true; - } - - return false; - } - - private boolean handleCtrlCode(CCData ccData) { - int ctrlCode = ccData.getCtrlCode(); - - if (mPrevCtrlCode != INVALID && mPrevCtrlCode == ctrlCode) { - // discard double ctrl codes (but if there's a 3rd one, we still take that) - mPrevCtrlCode = INVALID; - return true; - } - - switch(ctrlCode) { - case RCL: - // select pop-on style - mMode = MODE_POP_ON; - break; - case BS: - getMemory().bs(); - break; - case DER: - getMemory().der(); - break; - case RU2: - case RU3: - case RU4: - mRollUpSize = (ctrlCode - 0x23); - // erase memory if currently in other style - if (mMode != MODE_ROLL_UP) { - mDisplay.erase(); - mNonDisplay.erase(); - } - // select roll-up style - mMode = MODE_ROLL_UP; - break; - case FON: - Log.i(TAG, "Flash On"); - break; - case RDC: - // select paint-on style - mMode = MODE_PAINT_ON; - break; - case TR: - mMode = MODE_TEXT; - mTextMem.erase(); - break; - case RTD: - mMode = MODE_TEXT; - break; - case EDM: - // erase display memory - mDisplay.erase(); - updateDisplay(); - break; - case CR: - if (mMode == MODE_ROLL_UP) { - getMemory().rollUp(mRollUpSize); - } else { - getMemory().cr(); - } - if (mMode == MODE_ROLL_UP) { - updateDisplay(); - } - break; - case ENM: - // erase non-display memory - mNonDisplay.erase(); - break; - case EOC: - // swap display/non-display memory - swapMemory(); - // switch to pop-on style - mMode = MODE_POP_ON; - updateDisplay(); - break; - case INVALID: - default: - mPrevCtrlCode = INVALID; - return false; - } - - mPrevCtrlCode = ctrlCode; - - // handled - return true; - } - - private void updateDisplay() { - if (mListener != null) { - CaptionStyle captionStyle = mListener.getCaptionStyle(); - mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle)); - } - } - - private void swapMemory() { - CCMemory temp = mDisplay; - mDisplay = mNonDisplay; - mNonDisplay = temp; - } - - private static class StyleCode { - static final int COLOR_WHITE = 0; - static final int COLOR_GREEN = 1; - static final int COLOR_BLUE = 2; - static final int COLOR_CYAN = 3; - static final int COLOR_RED = 4; - static final int COLOR_YELLOW = 5; - static final int COLOR_MAGENTA = 6; - static final int COLOR_INVALID = 7; - - static final int STYLE_ITALICS = 0x00000001; - static final int STYLE_UNDERLINE = 0x00000002; - - static final String[] mColorMap = { - "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID" - }; - - final int mStyle; - final int mColor; - - static StyleCode fromByte(byte data2) { - int style = 0; - int color = (data2 >> 1) & 0x7; - - if ((data2 & 0x1) != 0) { - style |= STYLE_UNDERLINE; - } - - if (color == COLOR_INVALID) { - // WHITE ITALICS - color = COLOR_WHITE; - style |= STYLE_ITALICS; - } - - return new StyleCode(style, color); - } - - StyleCode(int style, int color) { - mStyle = style; - mColor = color; - } - - boolean isItalics() { - return (mStyle & STYLE_ITALICS) != 0; - } - - boolean isUnderline() { - return (mStyle & STYLE_UNDERLINE) != 0; - } - - int getColor() { - return mColor; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append("{"); - str.append(mColorMap[mColor]); - if ((mStyle & STYLE_ITALICS) != 0) { - str.append(", ITALICS"); - } - if ((mStyle & STYLE_UNDERLINE) != 0) { - str.append(", UNDERLINE"); - } - str.append("}"); - - return str.toString(); - } - } - - private static class PAC extends StyleCode { - final int mRow; - final int mCol; - - static PAC fromBytes(byte data1, byte data2) { - int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9}; - int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5); - int style = 0; - if ((data2 & 1) != 0) { - style |= STYLE_UNDERLINE; - } - if ((data2 & 0x10) != 0) { - // indent code - int indent = (data2 >> 1) & 0x7; - return new PAC(row, indent * 4, style, COLOR_WHITE); - } else { - // style code - int color = (data2 >> 1) & 0x7; - - if (color == COLOR_INVALID) { - // WHITE ITALICS - color = COLOR_WHITE; - style |= STYLE_ITALICS; - } - return new PAC(row, -1, style, color); - } - } - - PAC(int row, int col, int style, int color) { - super(style, color); - mRow = row; - mCol = col; - } - - boolean isIndentPAC() { - return (mCol >= 0); - } - - int getRow() { - return mRow; - } - - int getCol() { - return mCol; - } - - @Override - public String toString() { - return String.format("{%d, %d}, %s", - mRow, mCol, super.toString()); - } - } - - /** - * Mutable version of BackgroundSpan to facilitate text rendering with edge styles. - */ - public static class MutableBackgroundColorSpan extends CharacterStyle - implements UpdateAppearance { - private int mColor; - - public MutableBackgroundColorSpan(int color) { - mColor = color; - } - - public void setBackgroundColor(int color) { - mColor = color; - } - - public int getBackgroundColor() { - return mColor; - } - - @Override - public void updateDrawState(TextPaint ds) { - ds.bgColor = mColor; - } - } - - /* CCLineBuilder keeps track of displayable chars, as well as - * MidRow styles and PACs, for a single line of CC memory. - * - * It generates styled text via getStyledText() method. - */ - private static class CCLineBuilder { - private final StringBuilder mDisplayChars; - private final StyleCode[] mMidRowStyles; - private final StyleCode[] mPACStyles; - - CCLineBuilder(String str) { - mDisplayChars = new StringBuilder(str); - mMidRowStyles = new StyleCode[mDisplayChars.length()]; - mPACStyles = new StyleCode[mDisplayChars.length()]; - } - - void setCharAt(int index, char ch) { - mDisplayChars.setCharAt(index, ch); - mMidRowStyles[index] = null; - } - - void setMidRowAt(int index, StyleCode m) { - mDisplayChars.setCharAt(index, ' '); - mMidRowStyles[index] = m; - } - - void setPACAt(int index, PAC pac) { - mPACStyles[index] = pac; - } - - char charAt(int index) { - return mDisplayChars.charAt(index); - } - - int length() { - return mDisplayChars.length(); - } - - void applyStyleSpan( - SpannableStringBuilder styledText, - StyleCode s, int start, int end) { - if (s.isItalics()) { - styledText.setSpan( - new StyleSpan(android.graphics.Typeface.ITALIC), - start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - if (s.isUnderline()) { - styledText.setSpan( - new UnderlineSpan(), - start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - SpannableStringBuilder getStyledText(CaptionStyle captionStyle) { - SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars); - int start = -1, next = 0; - int styleStart = -1; - StyleCode curStyle = null; - while (next < mDisplayChars.length()) { - StyleCode newStyle = null; - if (mMidRowStyles[next] != null) { - // apply mid-row style change - newStyle = mMidRowStyles[next]; - } else if (mPACStyles[next] != null - && (styleStart < 0 || start < 0)) { - // apply PAC style change, only if: - // 1. no style set, or - // 2. style set, but prev char is none-displayable - newStyle = mPACStyles[next]; - } - if (newStyle != null) { - curStyle = newStyle; - if (styleStart >= 0 && start >= 0) { - applyStyleSpan(styledText, newStyle, styleStart, next); - } - styleStart = next; - } - - if (mDisplayChars.charAt(next) != TS) { - if (start < 0) { - start = next; - } - } else if (start >= 0) { - int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1; - int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1; - styledText.setSpan( - new MutableBackgroundColorSpan(captionStyle.backgroundColor), - expandedStart, expandedEnd, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - if (styleStart >= 0) { - applyStyleSpan(styledText, curStyle, styleStart, expandedEnd); - } - start = -1; - } - next++; - } - - return styledText; - } - } - - /* - * CCMemory models a console-style display. - */ - private static class CCMemory { - private final String mBlankLine; - private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2]; - private int mRow; - private int mCol; - - CCMemory() { - char[] blank = new char[MAX_COLS + 2]; - Arrays.fill(blank, TS); - mBlankLine = new String(blank); - } - - void erase() { - // erase all lines - for (int i = 0; i < mLines.length; i++) { - mLines[i] = null; - } - mRow = MAX_ROWS; - mCol = 1; - } - - void der() { - if (mLines[mRow] != null) { - for (int i = 0; i < mCol; i++) { - if (mLines[mRow].charAt(i) != TS) { - for (int j = mCol; j < mLines[mRow].length(); j++) { - mLines[j].setCharAt(j, TS); - } - return; - } - } - mLines[mRow] = null; - } - } - - void tab(int tabs) { - moveCursorByCol(tabs); - } - - void bs() { - moveCursorByCol(-1); - if (mLines[mRow] != null) { - mLines[mRow].setCharAt(mCol, TS); - if (mCol == MAX_COLS - 1) { - // Spec recommendation: - // if cursor was at col 32, move cursor - // back to col 31 and erase both col 31&32 - mLines[mRow].setCharAt(MAX_COLS, TS); - } - } - } - - void cr() { - moveCursorTo(mRow + 1, 1); - } - - void rollUp(int windowSize) { - int i; - for (i = 0; i <= mRow - windowSize; i++) { - mLines[i] = null; - } - int startRow = mRow - windowSize + 1; - if (startRow < 1) { - startRow = 1; - } - for (i = startRow; i < mRow; i++) { - mLines[i] = mLines[i + 1]; - } - for (i = mRow; i < mLines.length; i++) { - // clear base row - mLines[i] = null; - } - // default to col 1, in case PAC is not sent - mCol = 1; - } - - void writeText(String text) { - for (int i = 0; i < text.length(); i++) { - getLineBuffer(mRow).setCharAt(mCol, text.charAt(i)); - moveCursorByCol(1); - } - } - - void writeMidRowCode(StyleCode m) { - getLineBuffer(mRow).setMidRowAt(mCol, m); - moveCursorByCol(1); - } - - void writePAC(PAC pac) { - if (pac.isIndentPAC()) { - moveCursorTo(pac.getRow(), pac.getCol()); - } else { - moveCursorTo(pac.getRow(), 1); - } - getLineBuffer(mRow).setPACAt(mCol, pac); - } - - SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { - ArrayList rows = new ArrayList<>(MAX_ROWS); - for (int i = 1; i <= MAX_ROWS; i++) { - rows.add(mLines[i] != null ? - mLines[i].getStyledText(captionStyle) : null); - } - return rows.toArray(new SpannableStringBuilder[MAX_ROWS]); - } - - private static int clamp(int x, int min, int max) { - return x < min ? min : (x > max ? max : x); - } - - private void moveCursorTo(int row, int col) { - mRow = clamp(row, 1, MAX_ROWS); - mCol = clamp(col, 1, MAX_COLS); - } - - private void moveCursorToRow(int row) { - mRow = clamp(row, 1, MAX_ROWS); - } - - private void moveCursorByCol(int col) { - mCol = clamp(mCol + col, 1, MAX_COLS); - } - - private void moveBaselineTo(int baseRow, int windowSize) { - if (mRow == baseRow) { - return; - } - int actualWindowSize = windowSize; - if (baseRow < actualWindowSize) { - actualWindowSize = baseRow; - } - if (mRow < actualWindowSize) { - actualWindowSize = mRow; - } - - int i; - if (baseRow < mRow) { - // copy from bottom to top row - for (i = actualWindowSize - 1; i >= 0; i--) { - mLines[baseRow - i] = mLines[mRow - i]; - } - } else { - // copy from top to bottom row - for (i = 0; i < actualWindowSize; i++) { - mLines[baseRow - i] = mLines[mRow - i]; - } - } - // clear rest of the rows - for (i = 0; i <= baseRow - windowSize; i++) { - mLines[i] = null; - } - for (i = baseRow + 1; i < mLines.length; i++) { - mLines[i] = null; - } - } - - private CCLineBuilder getLineBuffer(int row) { - if (mLines[row] == null) { - mLines[row] = new CCLineBuilder(mBlankLine); - } - return mLines[row]; - } - } - - /* - * CCData parses the raw CC byte pair into displayable chars, - * misc control codes, Mid-Row or Preamble Address Codes. - */ - private static class CCData { - private final byte mType; - private final byte mData1; - private final byte mData2; - - private static final String[] mCtrlCodeMap = { - "RCL", "BS" , "AOF", "AON", - "DER", "RU2", "RU3", "RU4", - "FON", "RDC", "TR" , "RTD", - "EDM", "CR" , "ENM", "EOC", - }; - - private static final String[] mSpecialCharMap = { - "\u00AE", - "\u00B0", - "\u00BD", - "\u00BF", - "\u2122", - "\u00A2", - "\u00A3", - "\u266A", // Eighth note - "\u00E0", - "\u00A0", // Transparent space - "\u00E8", - "\u00E2", - "\u00EA", - "\u00EE", - "\u00F4", - "\u00FB", - }; - - private static final String[] mSpanishCharMap = { - // Spanish and misc chars - "\u00C1", // A - "\u00C9", // E - "\u00D3", // I - "\u00DA", // O - "\u00DC", // U - "\u00FC", // u - "\u2018", // opening single quote - "\u00A1", // inverted exclamation mark - "*", - "'", - "\u2014", // em dash - "\u00A9", // Copyright - "\u2120", // Servicemark - "\u2022", // round bullet - "\u201C", // opening double quote - "\u201D", // closing double quote - // French - "\u00C0", - "\u00C2", - "\u00C7", - "\u00C8", - "\u00CA", - "\u00CB", - "\u00EB", - "\u00CE", - "\u00CF", - "\u00EF", - "\u00D4", - "\u00D9", - "\u00F9", - "\u00DB", - "\u00AB", - "\u00BB" - }; - - private static final String[] mProtugueseCharMap = { - // Portuguese - "\u00C3", - "\u00E3", - "\u00CD", - "\u00CC", - "\u00EC", - "\u00D2", - "\u00F2", - "\u00D5", - "\u00F5", - "{", - "}", - "\\", - "^", - "_", - "|", - "~", - // German and misc chars - "\u00C4", - "\u00E4", - "\u00D6", - "\u00F6", - "\u00DF", - "\u00A5", - "\u00A4", - "\u2502", // vertical bar - "\u00C5", - "\u00E5", - "\u00D8", - "\u00F8", - "\u250C", // top-left corner - "\u2510", // top-right corner - "\u2514", // lower-left corner - "\u2518", // lower-right corner - }; - - static CCData[] fromByteArray(byte[] data) { - CCData[] ccData = new CCData[data.length / 3]; - - for (int i = 0; i < ccData.length; i++) { - ccData[i] = new CCData( - data[i * 3], - data[i * 3 + 1], - data[i * 3 + 2]); - } - - return ccData; - } - - CCData(byte type, byte data1, byte data2) { - mType = type; - mData1 = data1; - mData2 = data2; - } - - int getCtrlCode() { - if ((mData1 == 0x14 || mData1 == 0x1c) - && mData2 >= 0x20 && mData2 <= 0x2f) { - return mData2; - } - return INVALID; - } - - StyleCode getMidRow() { - // only support standard Mid-row codes, ignore - // optional background/foreground mid-row codes - if ((mData1 == 0x11 || mData1 == 0x19) - && mData2 >= 0x20 && mData2 <= 0x2f) { - return StyleCode.fromByte(mData2); - } - return null; - } - - PAC getPAC() { - if ((mData1 & 0x70) == 0x10 - && (mData2 & 0x40) == 0x40 - && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) { - return PAC.fromBytes(mData1, mData2); - } - return null; - } - - int getTabOffset() { - if ((mData1 == 0x17 || mData1 == 0x1f) - && mData2 >= 0x21 && mData2 <= 0x23) { - return mData2 & 0x3; - } - return 0; - } - - boolean isDisplayableChar() { - return isBasicChar() || isSpecialChar() || isExtendedChar(); - } - - String getDisplayText() { - String str = getBasicChars(); - - if (str == null) { - str = getSpecialChar(); - - if (str == null) { - str = getExtendedChar(); - } - } - - return str; - } - - private String ctrlCodeToString(int ctrlCode) { - return mCtrlCodeMap[ctrlCode - 0x20]; - } - - private boolean isBasicChar() { - return mData1 >= 0x20 && mData1 <= 0x7f; - } - - private boolean isSpecialChar() { - return ((mData1 == 0x11 || mData1 == 0x19) - && mData2 >= 0x30 && mData2 <= 0x3f); - } - - private boolean isExtendedChar() { - return ((mData1 == 0x12 || mData1 == 0x1A - || mData1 == 0x13 || mData1 == 0x1B) - && mData2 >= 0x20 && mData2 <= 0x3f); - } - - private char getBasicChar(byte data) { - char c; - // replace the non-ASCII ones - switch (data) { - case 0x2A: c = '\u00E1'; break; - case 0x5C: c = '\u00E9'; break; - case 0x5E: c = '\u00ED'; break; - case 0x5F: c = '\u00F3'; break; - case 0x60: c = '\u00FA'; break; - case 0x7B: c = '\u00E7'; break; - case 0x7C: c = '\u00F7'; break; - case 0x7D: c = '\u00D1'; break; - case 0x7E: c = '\u00F1'; break; - case 0x7F: c = '\u2588'; break; // Full block - default: c = (char) data; break; - } - return c; - } - - private String getBasicChars() { - if (mData1 >= 0x20 && mData1 <= 0x7f) { - StringBuilder builder = new StringBuilder(2); - builder.append(getBasicChar(mData1)); - if (mData2 >= 0x20 && mData2 <= 0x7f) { - builder.append(getBasicChar(mData2)); - } - return builder.toString(); - } - - return null; - } - - private String getSpecialChar() { - if ((mData1 == 0x11 || mData1 == 0x19) - && mData2 >= 0x30 && mData2 <= 0x3f) { - return mSpecialCharMap[mData2 - 0x30]; - } - - return null; - } - - private String getExtendedChar() { - if ((mData1 == 0x12 || mData1 == 0x1A) - && mData2 >= 0x20 && mData2 <= 0x3f){ - // 1 Spanish/French char - return mSpanishCharMap[mData2 - 0x20]; - } else if ((mData1 == 0x13 || mData1 == 0x1B) - && mData2 >= 0x20 && mData2 <= 0x3f){ - // 1 Portuguese/German/Danish char - return mProtugueseCharMap[mData2 - 0x20]; - } - - return null; - } - - @Override - public String toString() { - String str; - - if (mData1 < 0x10 && mData2 < 0x10) { - // Null Pad, ignore - return String.format("[%d]Null: %02x %02x", mType, mData1, mData2); - } - - int ctrlCode = getCtrlCode(); - if (ctrlCode != INVALID) { - return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode)); - } - - int tabOffset = getTabOffset(); - if (tabOffset > 0) { - return String.format("[%d]Tab%d", mType, tabOffset); - } - - PAC pac = getPAC(); - if (pac != null) { - return String.format("[%d]PAC: %s", mType, pac.toString()); - } - - StyleCode m = getMidRow(); - if (m != null) { - return String.format("[%d]Mid-row: %s", mType, m.toString()); - } - - if (isDisplayableChar()) { - return String.format("[%d]Displayable: %s (%02x %02x)", - mType, getDisplayText(), mData1, mData2); - } - - return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2); - } - } -} - -/** - * Widget capable of rendering CEA-608 closed captions. - */ -class Cea608CCWidget extends ClosedCaptionWidget implements Cea608CCParser.DisplayListener { - private static final Rect mTextBounds = new Rect(); - private static final String mDummyText = "1234567890123456789012345678901234"; - - public Cea608CCWidget(Context context) { - this(context, null); - } - - public Cea608CCWidget(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public Cea608CCWidget(Context context, AttributeSet attrs, int defStyle) { - this(context, attrs, defStyle, 0); - } - - public Cea608CCWidget(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - public ClosedCaptionLayout createCaptionLayout(Context context) { - return new CCLayout(context); - } - - @Override - public void onDisplayChanged(SpannableStringBuilder[] styledTexts) { - ((CCLayout) mClosedCaptionLayout).update(styledTexts); - - if (mListener != null) { - mListener.onChanged(this); - } - } - - @Override - public CaptionStyle getCaptionStyle() { - return mCaptionStyle; - } - - private static class CCLineBox extends TextView { - private static final float FONT_PADDING_RATIO = 0.75f; - private static final float EDGE_OUTLINE_RATIO = 0.1f; - private static final float EDGE_SHADOW_RATIO = 0.05f; - private float mOutlineWidth; - private float mShadowRadius; - private float mShadowOffset; - - private int mTextColor = Color.WHITE; - private int mBgColor = Color.BLACK; - private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE; - private int mEdgeColor = Color.TRANSPARENT; - - CCLineBox(Context context) { - super(context); - setGravity(Gravity.CENTER); - setBackgroundColor(Color.TRANSPARENT); - setTextColor(Color.WHITE); - setTypeface(Typeface.MONOSPACE); - setVisibility(View.INVISIBLE); - - final Resources res = getContext().getResources(); - - // get the default (will be updated later during measure) - mOutlineWidth = res.getDimensionPixelSize( - com.android.internal.R.dimen.subtitle_outline_width); - mShadowRadius = res.getDimensionPixelSize( - com.android.internal.R.dimen.subtitle_shadow_radius); - mShadowOffset = res.getDimensionPixelSize( - com.android.internal.R.dimen.subtitle_shadow_offset); - } - - void setCaptionStyle(CaptionStyle captionStyle) { - mTextColor = captionStyle.foregroundColor; - mBgColor = captionStyle.backgroundColor; - mEdgeType = captionStyle.edgeType; - mEdgeColor = captionStyle.edgeColor; - - setTextColor(mTextColor); - if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { - setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor); - } else { - setShadowLayer(0, 0, 0, 0); - } - invalidate(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; - setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); - - mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f; - mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;; - mShadowOffset = mShadowRadius; - - // set font scale in the X direction to match the required width - setScaleX(1.0f); - getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds); - float actualTextWidth = mTextBounds.width(); - float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec); - setScaleX(requiredTextWidth / actualTextWidth); - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onDraw(Canvas c) { - if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED - || mEdgeType == CaptionStyle.EDGE_TYPE_NONE - || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { - // these edge styles don't require a second pass - super.onDraw(c); - return; - } - - if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) { - drawEdgeOutline(c); - } else { - // Raised or depressed - drawEdgeRaisedOrDepressed(c); - } - } - - private void drawEdgeOutline(Canvas c) { - TextPaint textPaint = getPaint(); - - Paint.Style previousStyle = textPaint.getStyle(); - Paint.Join previousJoin = textPaint.getStrokeJoin(); - float previousWidth = textPaint.getStrokeWidth(); - - setTextColor(mEdgeColor); - textPaint.setStyle(Paint.Style.FILL_AND_STROKE); - textPaint.setStrokeJoin(Paint.Join.ROUND); - textPaint.setStrokeWidth(mOutlineWidth); - - // Draw outline and background only. - super.onDraw(c); - - // Restore original settings. - setTextColor(mTextColor); - textPaint.setStyle(previousStyle); - textPaint.setStrokeJoin(previousJoin); - textPaint.setStrokeWidth(previousWidth); - - // Remove the background. - setBackgroundSpans(Color.TRANSPARENT); - // Draw foreground only. - super.onDraw(c); - // Restore the background. - setBackgroundSpans(mBgColor); - } - - private void drawEdgeRaisedOrDepressed(Canvas c) { - TextPaint textPaint = getPaint(); - - Paint.Style previousStyle = textPaint.getStyle(); - textPaint.setStyle(Paint.Style.FILL); - - final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED; - final int colorUp = raised ? Color.WHITE : mEdgeColor; - final int colorDown = raised ? mEdgeColor : Color.WHITE; - final float offset = mShadowRadius / 2f; - - // Draw background and text with shadow up - setShadowLayer(mShadowRadius, -offset, -offset, colorUp); - super.onDraw(c); - - // Remove the background. - setBackgroundSpans(Color.TRANSPARENT); - - // Draw text with shadow down - setShadowLayer(mShadowRadius, +offset, +offset, colorDown); - super.onDraw(c); - - // Restore settings - textPaint.setStyle(previousStyle); - - // Restore the background. - setBackgroundSpans(mBgColor); - } - - private void setBackgroundSpans(int color) { - CharSequence text = getText(); - if (text instanceof Spannable) { - Spannable spannable = (Spannable) text; - Cea608CCParser.MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( - 0, spannable.length(), Cea608CCParser.MutableBackgroundColorSpan.class); - for (int i = 0; i < bgSpans.length; i++) { - bgSpans[i].setBackgroundColor(color); - } - } - } - } - - private static class CCLayout extends LinearLayout implements ClosedCaptionLayout { - private static final int MAX_ROWS = Cea608CCParser.MAX_ROWS; - private static final float SAFE_AREA_RATIO = 0.9f; - - private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS]; - - CCLayout(Context context) { - super(context); - setGravity(Gravity.START); - setOrientation(LinearLayout.VERTICAL); - for (int i = 0; i < MAX_ROWS; i++) { - mLineBoxes[i] = new CCLineBox(getContext()); - addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - } - - @Override - public void setCaptionStyle(CaptionStyle captionStyle) { - for (int i = 0; i < MAX_ROWS; i++) { - mLineBoxes[i].setCaptionStyle(captionStyle); - } - } - - @Override - public void setFontScale(float fontScale) { - // Ignores the font scale changes of the system wide CC preference. - } - - void update(SpannableStringBuilder[] textBuffer) { - for (int i = 0; i < MAX_ROWS; i++) { - if (textBuffer[i] != null) { - mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE); - mLineBoxes[i].setVisibility(View.VISIBLE); - } else { - mLineBoxes[i].setVisibility(View.INVISIBLE); - } - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - int safeWidth = getMeasuredWidth(); - int safeHeight = getMeasuredHeight(); - - // CEA-608 assumes 4:3 video - if (safeWidth * 3 >= safeHeight * 4) { - safeWidth = safeHeight * 4 / 3; - } else { - safeHeight = safeWidth * 3 / 4; - } - safeWidth *= SAFE_AREA_RATIO; - safeHeight *= SAFE_AREA_RATIO; - - int lineHeight = safeHeight / MAX_ROWS; - int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - lineHeight, MeasureSpec.EXACTLY); - int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - safeWidth, MeasureSpec.EXACTLY); - - for (int i = 0; i < MAX_ROWS; i++) { - mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // safe caption area - int viewPortWidth = r - l; - int viewPortHeight = b - t; - int safeWidth, safeHeight; - // CEA-608 assumes 4:3 video - if (viewPortWidth * 3 >= viewPortHeight * 4) { - safeWidth = viewPortHeight * 4 / 3; - safeHeight = viewPortHeight; - } else { - safeWidth = viewPortWidth; - safeHeight = viewPortWidth * 3 / 4; - } - safeWidth *= SAFE_AREA_RATIO; - safeHeight *= SAFE_AREA_RATIO; - int left = (viewPortWidth - safeWidth) / 2; - int top = (viewPortHeight - safeHeight) / 2; - - for (int i = 0; i < MAX_ROWS; i++) { - mLineBoxes[i].layout( - left, - top + safeHeight * i / MAX_ROWS, - left + safeWidth, - top + safeHeight * (i + 1) / MAX_ROWS); - } - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java index f75b75e067..b8d4908000 100644 --- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java +++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java @@ -41,7 +41,6 @@ import android.media.SessionCommandGroup2; import android.media.SessionToken2; import android.media.VolumeProvider2; import android.media.update.MediaBrowser2Provider; -import android.media.update.MediaControlView2Provider; import android.media.update.MediaController2Provider; import android.media.update.MediaItem2Provider; import android.media.update.MediaLibraryService2Provider.LibraryRootProvider; @@ -54,16 +53,9 @@ import android.media.update.MediaSessionService2Provider; import android.media.update.MediaSessionService2Provider.MediaNotificationProvider; import android.media.update.SessionToken2Provider; import android.media.update.StaticProvider; -import android.media.update.VideoView2Provider; -import android.media.update.ViewGroupProvider; import android.media.update.VolumeProvider2Provider; import android.os.Bundle; import android.os.IInterface; -import android.util.AttributeSet; -import android.widget.MediaControlView2; -import android.widget.VideoView2; - -import androidx.annotation.Nullable; import com.android.media.IMediaController2; import com.android.media.MediaBrowser2Impl; @@ -78,8 +70,6 @@ import com.android.media.MediaSessionService2Impl; import com.android.media.Rating2Impl; import com.android.media.SessionToken2Impl; import com.android.media.VolumeProvider2Impl; -import com.android.widget.MediaControlView2Impl; -import com.android.widget.VideoView2Impl; import java.util.concurrent.Executor; @@ -181,20 +171,6 @@ public final class ApiFactory implements StaticProvider { return new LibraryRootImpl(instance, rootId, extras); } - @Override - public MediaControlView2Provider createMediaControlView2(MediaControlView2 instance, - ViewGroupProvider superProvider, ViewGroupProvider privateProvider, - @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - return new MediaControlView2Impl(instance, superProvider, privateProvider); - } - - @Override - public VideoView2Provider createVideoView2( - VideoView2 instance, ViewGroupProvider superProvider, ViewGroupProvider privateProvider, - @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - return new VideoView2Impl(instance, superProvider, privateProvider); - } - @Override public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance, String packageName, String serviceName, int uid) { diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java index dc5e5e2aed..f918ed311a 100644 --- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java +++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java @@ -16,34 +16,11 @@ package com.android.media.update; -import android.annotation.Nullable; -import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.content.res.Resources.Theme; -import android.content.res.XmlResourceParser; -import android.util.AttributeSet; -import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.GuardedBy; -import androidx.appcompat.widget.ButtonBarLayout; -import androidx.legacy.widget.Space; - -import com.android.support.mediarouter.app.MediaRouteButton; -import com.android.support.mediarouter.app.MediaRouteExpandCollapseButton; -import com.android.support.mediarouter.app.MediaRouteVolumeSlider; -import com.android.support.mediarouter.app.OverlayListView; public final class ApiHelper { private static ApplicationInfo sUpdatableInfo; - @GuardedBy("this") - private static Theme sLibTheme; - private ApiHelper() { } static void initialize(ApplicationInfo updatableInfo) { @@ -53,90 +30,4 @@ public final class ApiHelper { sUpdatableInfo = updatableInfo; } - - public static Resources getLibResources(Context context) { - return getLibTheme(context).getResources(); - } - - public static Theme getLibTheme(Context context) { - if (sLibTheme != null) return sLibTheme; - - return getLibThemeSynchronized(context); - } - - public static Theme getLibTheme(Context context, int themeId) { - Theme theme = getLibResources(context).newTheme(); - theme.applyStyle(themeId, true); - return theme; - } - - public static LayoutInflater getLayoutInflater(Context context) { - return getLayoutInflater(context, null); - } - - public static LayoutInflater getLayoutInflater(Context context, Theme theme) { - if (theme == null) { - theme = getLibTheme(context); - } - - // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme. - LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext( - new ContextThemeWrapper(context, theme)); - layoutInflater.setFactory2(new LayoutInflater.Factory2() { - @Override - public View onCreateView( - View parent, String name, Context context, AttributeSet attrs) { - if (MediaRouteButton.class.getCanonicalName().equals(name)) { - return new MediaRouteButton(context, attrs); - } else if (MediaRouteVolumeSlider.class.getCanonicalName().equals(name)) { - return new MediaRouteVolumeSlider(context, attrs); - } else if (MediaRouteExpandCollapseButton.class.getCanonicalName().equals(name)) { - return new MediaRouteExpandCollapseButton(context, attrs); - } else if (OverlayListView.class.getCanonicalName().equals(name)) { - return new OverlayListView(context, attrs); - } else if (ButtonBarLayout.class.getCanonicalName().equals(name)) { - return new ButtonBarLayout(context, attrs); - } else if (Space.class.getCanonicalName().equals(name)) { - return new Space(context, attrs); - } - return null; - } - - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - return onCreateView(null, name, context, attrs); - } - }); - return layoutInflater; - } - - public static View inflateLibLayout(Context context, int libResId) { - return inflateLibLayout(context, getLibTheme(context), libResId, null, false); - } - - public static View inflateLibLayout(Context context, Theme theme, int libResId) { - return inflateLibLayout(context, theme, libResId, null, false); - } - - public static View inflateLibLayout(Context context, Theme theme, int libResId, - @Nullable ViewGroup root, boolean attachToRoot) { - try (XmlResourceParser parser = getLibResources(context).getLayout(libResId)) { - return getLayoutInflater(context, theme).inflate(parser, root, attachToRoot); - } - } - - private static synchronized Theme getLibThemeSynchronized(Context context) { - if (sLibTheme != null) return sLibTheme; - - if (sUpdatableInfo == null) { - throw new IllegalStateException("initialize hasn't been called yet"); - } - - try { - return sLibTheme = context.getPackageManager() - .getResourcesForApplication(sUpdatableInfo).newTheme(); - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - } } diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java b/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java deleted file mode 100644 index 1146af6067..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -// @@RequiresApi(24) -final class MediaRouterApi24 { - public static final class RouteInfo { - public static int getDeviceType(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java deleted file mode 100644 index 98c0d17f1e..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -import androidx.core.view.ActionProvider; - -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -import java.lang.ref.WeakReference; - -/** - * The media route action provider displays a {@link MediaRouteButton media route button} - * in the application's {@link ActionBar} to allow the user to select routes and - * to control the currently selected route. - *

    - * The application must specify the kinds of routes that the user should be allowed - * to select by specifying a {@link MediaRouteSelector selector} with the - * {@link #setRouteSelector} method. - *

    - * Refer to {@link MediaRouteButton} for a description of the button that will - * appear in the action bar menu. Note that instead of disabling the button - * when no routes are available, the action provider will instead make the - * menu item invisible. In this way, the button will only be visible when it - * is possible for the user to discover and select a matching route. - *

    - * - *

    Prerequisites

    - *

    - * To use the media route action provider, the activity must be a subclass of - * {@link AppCompatActivity} from the androidx.appcompat.appcompat - * support library. Refer to support library documentation for details. - *

    - * - *

    Example

    - *

    - *

    - * The application should define a menu resource to include the provider in the - * action bar options menu. Note that the support library action bar uses attributes - * that are defined in the application's resource namespace rather than the framework's - * resource namespace to configure each item. - *

    - * <menu xmlns:android="http://schemas.android.com/apk/res/android"
    - *         xmlns:app="http://schemas.android.com/apk/res-auto">
    - *     <item android:id="@+id/media_route_menu_item"
    - *         android:title="@string/media_route_menu_title"
    - *         app:showAsAction="always"
    - *         app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
    - * </menu>
    - * 

    - * Then configure the menu and set the route selector for the chooser. - *

    - * public class MyActivity extends AppCompatActivity {
    - *     private MediaRouter mRouter;
    - *     private MediaRouter.Callback mCallback;
    - *     private MediaRouteSelector mSelector;
    - *
    - *     protected void onCreate(Bundle savedInstanceState) {
    - *         super.onCreate(savedInstanceState);
    - *
    - *         mRouter = Mediarouter.getInstance(this);
    - *         mSelector = new MediaRouteSelector.Builder()
    - *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
    - *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
    - *                 .build();
    - *         mCallback = new MyCallback();
    - *     }
    - *
    - *     // Add the callback on start to tell the media router what kinds of routes
    - *     // the application is interested in so that it can try to discover suitable ones.
    - *     public void onStart() {
    - *         super.onStart();
    - *
    - *         mediaRouter.addCallback(mSelector, mCallback,
    - *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
    - *
    - *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
    - *         // do something with the route...
    - *     }
    - *
    - *     // Remove the selector on stop to tell the media router that it no longer
    - *     // needs to invest effort trying to discover routes of these kinds for now.
    - *     public void onStop() {
    - *         super.onStop();
    - *
    - *         mediaRouter.removeCallback(mCallback);
    - *     }
    - *
    - *     public boolean onCreateOptionsMenu(Menu menu) {
    - *         super.onCreateOptionsMenu(menu);
    - *
    - *         getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
    - *
    - *         MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
    - *         MediaRouteActionProvider mediaRouteActionProvider =
    - *                 (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
    - *         mediaRouteActionProvider.setRouteSelector(mSelector);
    - *         return true;
    - *     }
    - *
    - *     private final class MyCallback extends MediaRouter.Callback {
    - *         // Implement callback methods as needed.
    - *     }
    - * }
    - * 
    - * - * @see #setRouteSelector - */ -public class MediaRouteActionProvider extends ActionProvider { - private static final String TAG = "MediaRouteActionProvider"; - - private final MediaRouter mRouter; - private final MediaRouterCallback mCallback; - - private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; - private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault(); - private MediaRouteButton mButton; - - /** - * Creates the action provider. - * - * @param context The context. - */ - public MediaRouteActionProvider(Context context) { - super(context); - - mRouter = MediaRouter.getInstance(context); - mCallback = new MediaRouterCallback(this); - } - - /** - * Gets the media route selector for filtering the routes that the user can - * select using the media route chooser dialog. - * - * @return The selector, never null. - */ - @NonNull - public MediaRouteSelector getRouteSelector() { - return mSelector; - } - - /** - * Sets the media route selector for filtering the routes that the user can - * select using the media route chooser dialog. - * - * @param selector The selector, must not be null. - */ - public void setRouteSelector(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - if (!mSelector.equals(selector)) { - // FIXME: We currently have no way of knowing whether the action provider - // is still needed by the UI. Unfortunately this means the action provider - // may leak callbacks until garbage collection occurs. This may result in - // media route providers doing more work than necessary in the short term - // while trying to discover routes that are no longer of interest to the - // application. To solve this problem, the action provider will need some - // indication from the framework that it is being destroyed. - if (!mSelector.isEmpty()) { - mRouter.removeCallback(mCallback); - } - if (!selector.isEmpty()) { - mRouter.addCallback(selector, mCallback); - } - mSelector = selector; - refreshRoute(); - - if (mButton != null) { - mButton.setRouteSelector(selector); - } - } - } - - /** - * Gets the media route dialog factory to use when showing the route chooser - * or controller dialog. - * - * @return The dialog factory, never null. - */ - @NonNull - public MediaRouteDialogFactory getDialogFactory() { - return mDialogFactory; - } - - /** - * Sets the media route dialog factory to use when showing the route chooser - * or controller dialog. - * - * @param factory The dialog factory, must not be null. - */ - public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) { - if (factory == null) { - throw new IllegalArgumentException("factory must not be null"); - } - - if (mDialogFactory != factory) { - mDialogFactory = factory; - - if (mButton != null) { - mButton.setDialogFactory(factory); - } - } - } - - /** - * Gets the associated media route button, or null if it has not yet been created. - */ - @Nullable - public MediaRouteButton getMediaRouteButton() { - return mButton; - } - - /** - * Called when the media route button is being created. - *

    - * Subclasses may override this method to customize the button. - *

    - */ - public MediaRouteButton onCreateMediaRouteButton() { - return new MediaRouteButton(getContext()); - } - - @Override - @SuppressWarnings("deprecation") - public View onCreateActionView() { - if (mButton != null) { - Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " + - "with a menu item. Don't reuse MediaRouteActionProvider instances! " + - "Abandoning the old menu item..."); - } - - mButton = onCreateMediaRouteButton(); - mButton.setCheatSheetEnabled(true); - mButton.setRouteSelector(mSelector); - mButton.setDialogFactory(mDialogFactory); - mButton.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - return mButton; - } - - @Override - public boolean onPerformDefaultAction() { - if (mButton != null) { - return mButton.showDialog(); - } - return false; - } - - @Override - public boolean overridesItemVisibility() { - return true; - } - - @Override - public boolean isVisible() { - return mRouter.isRouteAvailable(mSelector, - MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); - } - - void refreshRoute() { - refreshVisibility(); - } - - private static final class MediaRouterCallback extends MediaRouter.Callback { - private final WeakReference mProviderWeak; - - public MediaRouterCallback(MediaRouteActionProvider provider) { - mProviderWeak = new WeakReference(provider); - } - - @Override - public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(router); - } - - @Override - public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(router); - } - - @Override - public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(router); - } - - @Override - public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(router); - } - - @Override - public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(router); - } - - @Override - public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(router); - } - - private void refreshRoute(MediaRouter router) { - MediaRouteActionProvider provider = mProviderWeak.get(); - if (provider != null) { - provider.refreshRoute(); - } else { - router.removeCallback(this); - } - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java deleted file mode 100644 index e82fcb9a5b..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.annotation.NonNull; -import android.app.Activity; -import android.app.FragmentManager; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.drawable.AnimationDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseArray; -import android.view.SoundEffectConstants; -import android.view.View; - -import androidx.appcompat.widget.TooltipCompat; -import androidx.core.graphics.drawable.DrawableCompat; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -/** - * The media route button allows the user to select routes and to control the - * currently selected route. - *

    - * The application must specify the kinds of routes that the user should be allowed - * to select by specifying a {@link MediaRouteSelector selector} with the - * {@link #setRouteSelector} method. - *

    - * When the default route is selected or when the currently selected route does not - * match the {@link #getRouteSelector() selector}, the button will appear in - * an inactive state indicating that the application is not connected to a - * route of the kind that it wants to use. Clicking on the button opens - * a {@link MediaRouteChooserDialog} to allow the user to select a route. - * If no non-default routes match the selector and it is not possible for an active - * scan to discover any matching routes, then the button is disabled and cannot - * be clicked. - *

    - * When a non-default route is selected that matches the selector, the button will - * appear in an active state indicating that the application is connected - * to a route of the kind that it wants to use. The button may also appear - * in an intermediary connecting state if the route is in the process of connecting - * to the destination but has not yet completed doing so. In either case, clicking - * on the button opens a {@link MediaRouteControllerDialog} to allow the user - * to control or disconnect from the current route. - *

    - * - *

    Prerequisites

    - *

    - * To use the media route button, the activity must be a subclass of - * {@link FragmentActivity} from the androidx.core./code> - * support library. Refer to support library documentation for details. - *

    - * - * @see MediaRouteActionProvider - * @see #setRouteSelector - */ -public class MediaRouteButton extends View { - private static final String TAG = "MediaRouteButton"; - - private static final String CHOOSER_FRAGMENT_TAG = - "androidx.mediarouter.media.outer:MediaRouteChooserDialogFragment"; - private static final String CONTROLLER_FRAGMENT_TAG = - "androidx.mediarouter.media.outer:MediaRouteControllerDialogFragment"; - - private final MediaRouter mRouter; - private final MediaRouterCallback mCallback; - - private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; - private int mRouteCallbackFlags; - private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault(); - - private boolean mAttachedToWindow; - - private static final SparseArray sRemoteIndicatorCache = - new SparseArray<>(2); - private RemoteIndicatorLoader mRemoteIndicatorLoader; - private Drawable mRemoteIndicator; - private boolean mRemoteActive; - private boolean mIsConnecting; - - private ColorStateList mButtonTint; - private int mMinWidth; - private int mMinHeight; - - // The checked state is used when connected to a remote route. - private static final int[] CHECKED_STATE_SET = { - android.R.attr.state_checked - }; - - // The checkable state is used while connecting to a remote route. - private static final int[] CHECKABLE_STATE_SET = { - android.R.attr.state_checkable - }; - - public MediaRouteButton(Context context) { - this(context, null); - } - - public MediaRouteButton(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.mediaRouteButtonStyle); - } - - public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr); - context = getContext(); - - mRouter = MediaRouter.getInstance(context); - mCallback = new MediaRouterCallback(); - - Resources.Theme theme = ApiHelper.getLibResources(context).newTheme(); - theme.applyStyle(MediaRouterThemeHelper.getRouterThemeId(context), true); - TypedArray a = theme.obtainStyledAttributes(attrs, - R.styleable.MediaRouteButton, defStyleAttr, 0); - - mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_mediaRouteButtonTint); - mMinWidth = a.getDimensionPixelSize( - R.styleable.MediaRouteButton_android_minWidth, 0); - mMinHeight = a.getDimensionPixelSize( - R.styleable.MediaRouteButton_android_minHeight, 0); - int remoteIndicatorResId = a.getResourceId( - R.styleable.MediaRouteButton_externalRouteEnabledDrawable, 0); - a.recycle(); - - if (remoteIndicatorResId != 0) { - Drawable.ConstantState remoteIndicatorState = - sRemoteIndicatorCache.get(remoteIndicatorResId); - if (remoteIndicatorState != null) { - setRemoteIndicatorDrawable(remoteIndicatorState.newDrawable()); - } else { - mRemoteIndicatorLoader = new RemoteIndicatorLoader(remoteIndicatorResId); - mRemoteIndicatorLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - updateContentDescription(); - setClickable(true); - } - - /** - * Gets the media route selector for filtering the routes that the user can - * select using the media route chooser dialog. - * - * @return The selector, never null. - */ - @NonNull - public MediaRouteSelector getRouteSelector() { - return mSelector; - } - - /** - * Sets the media route selector for filtering the routes that the user can - * select using the media route chooser dialog. - * - * @param selector The selector. - */ - public void setRouteSelector(MediaRouteSelector selector) { - setRouteSelector(selector, 0); - } - - /** - * Sets the media route selector for filtering the routes that the user can - * select using the media route chooser dialog. - * - * @param selector The selector. - * @param flags Flags to control the behavior of the callback. May be zero or a combination of - * {@link #MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and - * {@link #MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS}. - */ - public void setRouteSelector(MediaRouteSelector selector, int flags) { - if (mSelector.equals(selector) && mRouteCallbackFlags == flags) { - return; - } - if (!mSelector.isEmpty()) { - mRouter.removeCallback(mCallback); - } - if (selector == null || selector.isEmpty()) { - mSelector = MediaRouteSelector.EMPTY; - return; - } - - mSelector = selector; - mRouteCallbackFlags = flags; - - if (mAttachedToWindow) { - mRouter.addCallback(selector, mCallback, flags); - refreshRoute(); - } - } - - /** - * Gets the media route dialog factory to use when showing the route chooser - * or controller dialog. - * - * @return The dialog factory, never null. - */ - @NonNull - public MediaRouteDialogFactory getDialogFactory() { - return mDialogFactory; - } - - /** - * Sets the media route dialog factory to use when showing the route chooser - * or controller dialog. - * - * @param factory The dialog factory, must not be null. - */ - public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) { - if (factory == null) { - throw new IllegalArgumentException("factory must not be null"); - } - - mDialogFactory = factory; - } - - /** - * Show the route chooser or controller dialog. - *

    - * If the default route is selected or if the currently selected route does - * not match the {@link #getRouteSelector selector}, then shows the route chooser dialog. - * Otherwise, shows the route controller dialog to offer the user - * a choice to disconnect from the route or perform other control actions - * such as setting the route's volume. - *

    - * The application can customize the dialogs by calling {@link #setDialogFactory} - * to provide a customized dialog factory. - *

    - * - * @return True if the dialog was actually shown. - * - * @throws IllegalStateException if the activity is not a subclass of - * {@link FragmentActivity}. - */ - public boolean showDialog() { - if (!mAttachedToWindow) { - return false; - } - - final FragmentManager fm = getActivity().getFragmentManager(); - if (fm == null) { - throw new IllegalStateException("The activity must be a subclass of FragmentActivity"); - } - - MediaRouter.RouteInfo route = mRouter.getSelectedRoute(); - if (route.isDefaultOrBluetooth() || !route.matchesSelector(mSelector)) { - if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) { - Log.w(TAG, "showDialog(): Route chooser dialog already showing!"); - return false; - } - MediaRouteChooserDialogFragment f = - mDialogFactory.onCreateChooserDialogFragment(); - f.setRouteSelector(mSelector); - f.show(fm, CHOOSER_FRAGMENT_TAG); - } else { - if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) { - Log.w(TAG, "showDialog(): Route controller dialog already showing!"); - return false; - } - MediaRouteControllerDialogFragment f = - mDialogFactory.onCreateControllerDialogFragment(); - f.show(fm, CONTROLLER_FRAGMENT_TAG); - } - return true; - } - - - private Activity getActivity() { - // Gross way of unwrapping the Activity so we can get the FragmentManager - Context context = getContext(); - while (context instanceof ContextWrapper) { - if (context instanceof Activity) { - return (Activity)context; - } - context = ((ContextWrapper)context).getBaseContext(); - } - return null; - } - - /** - * Sets whether to enable showing a toast with the content descriptor of the - * button when the button is long pressed. - */ - void setCheatSheetEnabled(boolean enable) { - TooltipCompat.setTooltipText(this, enable - ? ApiHelper.getLibResources(getContext()) - .getString(R.string.mr_button_content_description) - : null); - } - - @Override - public boolean performClick() { - // Send the appropriate accessibility events and call listeners - boolean handled = super.performClick(); - if (!handled) { - playSoundEffect(SoundEffectConstants.CLICK); - } - return showDialog() || handled; - } - - @Override - protected int[] onCreateDrawableState(int extraSpace) { - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); - - // Technically we should be handling this more completely, but these - // are implementation details here. Checkable is used to express the connecting - // drawable state and it's mutually exclusive with check for the purposes - // of state selection here. - if (mIsConnecting) { - mergeDrawableStates(drawableState, CHECKABLE_STATE_SET); - } else if (mRemoteActive) { - mergeDrawableStates(drawableState, CHECKED_STATE_SET); - } - return drawableState; - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - - if (mRemoteIndicator != null) { - int[] myDrawableState = getDrawableState(); - mRemoteIndicator.setState(myDrawableState); - invalidate(); - } - } - - /** - * Sets a drawable to use as the remote route indicator. - */ - public void setRemoteIndicatorDrawable(Drawable d) { - if (mRemoteIndicatorLoader != null) { - mRemoteIndicatorLoader.cancel(false); - } - - if (mRemoteIndicator != null) { - mRemoteIndicator.setCallback(null); - unscheduleDrawable(mRemoteIndicator); - } - if (d != null) { - if (mButtonTint != null) { - d = DrawableCompat.wrap(d.mutate()); - DrawableCompat.setTintList(d, mButtonTint); - } - d.setCallback(this); - d.setState(getDrawableState()); - d.setVisible(getVisibility() == VISIBLE, false); - } - mRemoteIndicator = d; - - refreshDrawableState(); - if (mAttachedToWindow && mRemoteIndicator != null - && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) { - AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent(); - if (mIsConnecting) { - if (!curDrawable.isRunning()) { - curDrawable.start(); - } - } else if (mRemoteActive) { - if (curDrawable.isRunning()) { - curDrawable.stop(); - } - curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1); - } - } - } - - @Override - protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || who == mRemoteIndicator; - } - - @Override - public void jumpDrawablesToCurrentState() { - // We can't call super to handle the background so we do it ourselves. - //super.jumpDrawablesToCurrentState(); - if (getBackground() != null) { - DrawableCompat.jumpToCurrentState(getBackground()); - } - - // Handle our own remote indicator. - if (mRemoteIndicator != null) { - DrawableCompat.jumpToCurrentState(mRemoteIndicator); - } - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - - if (mRemoteIndicator != null) { - mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false); - } - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - mAttachedToWindow = true; - if (!mSelector.isEmpty()) { - mRouter.addCallback(mSelector, mCallback, mRouteCallbackFlags); - } - refreshRoute(); - } - - @Override - public void onDetachedFromWindow() { - mAttachedToWindow = false; - if (!mSelector.isEmpty()) { - mRouter.removeCallback(mCallback); - } - - super.onDetachedFromWindow(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int widthSize = MeasureSpec.getSize(widthMeasureSpec); - final int heightSize = MeasureSpec.getSize(heightMeasureSpec); - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - - final int width = Math.max(mMinWidth, mRemoteIndicator != null ? - mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0); - final int height = Math.max(mMinHeight, mRemoteIndicator != null ? - mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0); - - int measuredWidth; - switch (widthMode) { - case MeasureSpec.EXACTLY: - measuredWidth = widthSize; - break; - case MeasureSpec.AT_MOST: - measuredWidth = Math.min(widthSize, width); - break; - default: - case MeasureSpec.UNSPECIFIED: - measuredWidth = width; - break; - } - - int measuredHeight; - switch (heightMode) { - case MeasureSpec.EXACTLY: - measuredHeight = heightSize; - break; - case MeasureSpec.AT_MOST: - measuredHeight = Math.min(heightSize, height); - break; - default: - case MeasureSpec.UNSPECIFIED: - measuredHeight = height; - break; - } - - setMeasuredDimension(measuredWidth, measuredHeight); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mRemoteIndicator != null) { - final int left = getPaddingLeft(); - final int right = getWidth() - getPaddingRight(); - final int top = getPaddingTop(); - final int bottom = getHeight() - getPaddingBottom(); - - final int drawWidth = mRemoteIndicator.getIntrinsicWidth(); - final int drawHeight = mRemoteIndicator.getIntrinsicHeight(); - final int drawLeft = left + (right - left - drawWidth) / 2; - final int drawTop = top + (bottom - top - drawHeight) / 2; - - mRemoteIndicator.setBounds(drawLeft, drawTop, - drawLeft + drawWidth, drawTop + drawHeight); - mRemoteIndicator.draw(canvas); - } - } - - void refreshRoute() { - final MediaRouter.RouteInfo route = mRouter.getSelectedRoute(); - final boolean isRemote = !route.isDefaultOrBluetooth() && route.matchesSelector(mSelector); - final boolean isConnecting = isRemote && route.isConnecting(); - boolean needsRefresh = false; - if (mRemoteActive != isRemote) { - mRemoteActive = isRemote; - needsRefresh = true; - } - if (mIsConnecting != isConnecting) { - mIsConnecting = isConnecting; - needsRefresh = true; - } - - if (needsRefresh) { - updateContentDescription(); - refreshDrawableState(); - } - if (mAttachedToWindow) { - setEnabled(mRouter.isRouteAvailable(mSelector, - MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE)); - } - if (mRemoteIndicator != null - && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) { - AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent(); - if (mAttachedToWindow) { - if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) { - curDrawable.start(); - } - } else if (isRemote && !isConnecting) { - // When the route is already connected before the view is attached, show the last - // frame of the connected animation immediately. - if (curDrawable.isRunning()) { - curDrawable.stop(); - } - curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1); - } - } - } - - private void updateContentDescription() { - int resId; - if (mIsConnecting) { - resId = R.string.mr_cast_button_connecting; - } else if (mRemoteActive) { - resId = R.string.mr_cast_button_connected; - } else { - resId = R.string.mr_cast_button_disconnected; - } - setContentDescription(ApiHelper.getLibResources(getContext()).getString(resId)); - } - - private final class MediaRouterCallback extends MediaRouter.Callback { - MediaRouterCallback() { - } - - @Override - public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(); - } - - @Override - public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(); - } - - @Override - public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(); - } - - @Override - public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(); - } - - @Override - public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoute(); - } - - @Override - public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(); - } - - @Override - public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(); - } - - @Override - public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) { - refreshRoute(); - } - } - - private final class RemoteIndicatorLoader extends AsyncTask { - private final int mResId; - - RemoteIndicatorLoader(int resId) { - mResId = resId; - } - - @Override - protected Drawable doInBackground(Void... params) { - return ApiHelper.getLibResources(getContext()).getDrawable(mResId); - } - - @Override - protected void onPostExecute(Drawable remoteIndicator) { - cacheAndReset(remoteIndicator); - setRemoteIndicatorDrawable(remoteIndicator); - } - - @Override - protected void onCancelled(Drawable remoteIndicator) { - cacheAndReset(remoteIndicator); - } - - private void cacheAndReset(Drawable remoteIndicator) { - if (remoteIndicator != null) { - sRemoteIndicatorCache.put(mResId, remoteIndicator.getConstantState()); - } - mRemoteIndicatorLoader = null; - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java deleted file mode 100644 index f24028afcd..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import static com.android.support.mediarouter.media.MediaRouter.RouteInfo - .CONNECTION_STATE_CONNECTED; -import static com.android.support.mediarouter.media.MediaRouter.RouteInfo - .CONNECTION_STATE_CONNECTING; - -import android.annotation.NonNull; -import android.app.Dialog; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.Log; -import android.view.ContextThemeWrapper; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * This class implements the route chooser dialog for {@link MediaRouter}. - *

    - * This dialog allows the user to choose a route that matches a given selector. - *

    - * - * @see MediaRouteButton - * @see MediaRouteActionProvider - */ -public class MediaRouteChooserDialog extends Dialog { - static final String TAG = "MediaRouteChooserDialog"; - - // Do not update the route list immediately to avoid unnatural dialog change. - private static final long UPDATE_ROUTES_DELAY_MS = 300L; - static final int MSG_UPDATE_ROUTES = 1; - - private final MediaRouter mRouter; - private final MediaRouterCallback mCallback; - - private TextView mTitleView; - private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; - private ArrayList mRoutes; - private RouteAdapter mAdapter; - private ListView mListView; - private boolean mAttachedToWindow; - private long mLastUpdateTime; - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_UPDATE_ROUTES: - updateRoutes((List) message.obj); - break; - } - } - }; - - public MediaRouteChooserDialog(Context context) { - this(context, 0); - } - - public MediaRouteChooserDialog(Context context, int theme) { - // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme. - super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context, - MediaRouterThemeHelper.getRouterThemeId(context))), theme); - context = getContext(); - - mRouter = MediaRouter.getInstance(context); - mCallback = new MediaRouterCallback(); - } - - /** - * Gets the media route selector for filtering the routes that the user can select. - * - * @return The selector, never null. - */ - @NonNull - public MediaRouteSelector getRouteSelector() { - return mSelector; - } - - /** - * Sets the media route selector for filtering the routes that the user can select. - * - * @param selector The selector, must not be null. - */ - public void setRouteSelector(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - if (!mSelector.equals(selector)) { - mSelector = selector; - - if (mAttachedToWindow) { - mRouter.removeCallback(mCallback); - mRouter.addCallback(selector, mCallback, - MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - } - - refreshRoutes(); - } - } - - /** - * Called to filter the set of routes that should be included in the list. - *

    - * The default implementation iterates over all routes in the provided list and - * removes those for which {@link #onFilterRoute} returns false. - *

    - * - * @param routes The list of routes to filter in-place, never null. - */ - public void onFilterRoutes(@NonNull List routes) { - for (int i = routes.size(); i-- > 0; ) { - if (!onFilterRoute(routes.get(i))) { - routes.remove(i); - } - } - } - - /** - * Returns true if the route should be included in the list. - *

    - * The default implementation returns true for enabled non-default routes that - * match the selector. Subclasses can override this method to filter routes - * differently. - *

    - * - * @param route The route to consider, never null. - * @return True if the route should be included in the chooser dialog. - */ - public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) { - return !route.isDefaultOrBluetooth() && route.isEnabled() - && route.matchesSelector(mSelector); - } - - @Override - public void setTitle(CharSequence title) { - mTitleView.setText(title); - } - - @Override - public void setTitle(int titleId) { - mTitleView.setText(titleId); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(ApiHelper.inflateLibLayout(getContext(), ApiHelper.getLibTheme(getContext(), - MediaRouterThemeHelper.getRouterThemeId(getContext())), - R.layout.mr_chooser_dialog)); - - mRoutes = new ArrayList<>(); - mAdapter = new RouteAdapter(getContext(), mRoutes); - mListView = (ListView)findViewById(R.id.mr_chooser_list); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(mAdapter); - mListView.setEmptyView(findViewById(android.R.id.empty)); - mTitleView = findViewById(R.id.mr_chooser_title); - - updateLayout(); - } - - /** - * Sets the width of the dialog. Also called when configuration changes. - */ - void updateLayout() { - getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()), - ViewGroup.LayoutParams.WRAP_CONTENT); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - mAttachedToWindow = true; - mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - refreshRoutes(); - } - - @Override - public void onDetachedFromWindow() { - mAttachedToWindow = false; - mRouter.removeCallback(mCallback); - mHandler.removeMessages(MSG_UPDATE_ROUTES); - - super.onDetachedFromWindow(); - } - - /** - * Refreshes the list of routes that are shown in the chooser dialog. - */ - public void refreshRoutes() { - if (mAttachedToWindow) { - ArrayList routes = new ArrayList<>(mRouter.getRoutes()); - onFilterRoutes(routes); - Collections.sort(routes, RouteComparator.sInstance); - if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) { - updateRoutes(routes); - } else { - mHandler.removeMessages(MSG_UPDATE_ROUTES); - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes), - mLastUpdateTime + UPDATE_ROUTES_DELAY_MS); - } - } - } - - void updateRoutes(List routes) { - mLastUpdateTime = SystemClock.uptimeMillis(); - mRoutes.clear(); - mRoutes.addAll(routes); - mAdapter.notifyDataSetChanged(); - } - - private final class RouteAdapter extends ArrayAdapter - implements ListView.OnItemClickListener { - private final Drawable mDefaultIcon; - private final Drawable mTvIcon; - private final Drawable mSpeakerIcon; - private final Drawable mSpeakerGroupIcon; - - public RouteAdapter(Context context, List routes) { - super(context, 0, routes); - - TypedArray styledAttributes = ApiHelper.getLibTheme(context, - MediaRouterThemeHelper.getRouterThemeId(context)).obtainStyledAttributes( - new int[] { - R.attr.mediaRouteDefaultIconDrawable, - R.attr.mediaRouteTvIconDrawable, - R.attr.mediaRouteSpeakerIconDrawable, - R.attr.mediaRouteSpeakerGroupIconDrawable - }); - - mDefaultIcon = styledAttributes.getDrawable(0); - mTvIcon = styledAttributes.getDrawable(1); - mSpeakerIcon = styledAttributes.getDrawable(2); - mSpeakerGroupIcon = styledAttributes.getDrawable(3); - styledAttributes.recycle(); - } - - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int position) { - return getItem(position).isEnabled(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view = convertView; - if (view == null) { - view = ApiHelper.inflateLibLayout(getContext(), - ApiHelper.getLibTheme(getContext(), - MediaRouterThemeHelper.getRouterThemeId(getContext())), - R.layout.mr_chooser_list_item, parent, false); - } - - MediaRouter.RouteInfo route = getItem(position); - TextView text1 = (TextView) view.findViewById(R.id.mr_chooser_route_name); - TextView text2 = (TextView) view.findViewById(R.id.mr_chooser_route_desc); - text1.setText(route.getName()); - String description = route.getDescription(); - boolean isConnectedOrConnecting = - route.getConnectionState() == CONNECTION_STATE_CONNECTED - || route.getConnectionState() == CONNECTION_STATE_CONNECTING; - if (isConnectedOrConnecting && !TextUtils.isEmpty(description)) { - text1.setGravity(Gravity.BOTTOM); - text2.setVisibility(View.VISIBLE); - text2.setText(description); - } else { - text1.setGravity(Gravity.CENTER_VERTICAL); - text2.setVisibility(View.GONE); - text2.setText(""); - } - view.setEnabled(route.isEnabled()); - - ImageView iconView = (ImageView) view.findViewById(R.id.mr_chooser_route_icon); - if (iconView != null) { - iconView.setImageDrawable(getIconDrawable(route)); - } - return view; - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - MediaRouter.RouteInfo route = getItem(position); - if (route.isEnabled()) { - route.select(); - dismiss(); - } - } - - private Drawable getIconDrawable(MediaRouter.RouteInfo route) { - Uri iconUri = route.getIconUri(); - if (iconUri != null) { - try { - InputStream is = getContext().getContentResolver().openInputStream(iconUri); - Drawable drawable = Drawable.createFromStream(is, null); - if (drawable != null) { - return drawable; - } - } catch (IOException e) { - Log.w(TAG, "Failed to load " + iconUri, e); - // Falls back. - } - } - return getDefaultIconDrawable(route); - } - - private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) { - // If the type of the receiver device is specified, use it. - switch (route.getDeviceType()) { - case MediaRouter.RouteInfo.DEVICE_TYPE_TV: - return mTvIcon; - case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER: - return mSpeakerIcon; - } - - // Otherwise, make the best guess based on other route information. - if (route instanceof MediaRouter.RouteGroup) { - // Only speakers can be grouped for now. - return mSpeakerGroupIcon; - } - return mDefaultIcon; - } - } - - private final class MediaRouterCallback extends MediaRouter.Callback { - MediaRouterCallback() { - } - - @Override - public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoutes(); - } - - @Override - public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoutes(); - } - - @Override - public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { - refreshRoutes(); - } - - @Override - public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { - dismiss(); - } - } - - static final class RouteComparator implements Comparator { - public static final RouteComparator sInstance = new RouteComparator(); - - @Override - public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) { - return lhs.getName().compareToIgnoreCase(rhs.getName()); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java deleted file mode 100644 index 65e6b29668..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.Context; -import android.content.res.Configuration; -import android.os.Bundle; - -import com.android.support.mediarouter.media.MediaRouteSelector; - -/** - * Media route chooser dialog fragment. - *

    - * Creates a {@link MediaRouteChooserDialog}. The application may subclass - * this dialog fragment to customize the media route chooser dialog. - *

    - */ -public class MediaRouteChooserDialogFragment extends DialogFragment { - private final String ARGUMENT_SELECTOR = "selector"; - - private MediaRouteChooserDialog mDialog; - private MediaRouteSelector mSelector; - - /** - * Creates a media route chooser dialog fragment. - *

    - * All subclasses of this class must also possess a default constructor. - *

    - */ - public MediaRouteChooserDialogFragment() { - setCancelable(true); - } - - /** - * Gets the media route selector for filtering the routes that the user can select. - * - * @return The selector, never null. - */ - public MediaRouteSelector getRouteSelector() { - ensureRouteSelector(); - return mSelector; - } - - private void ensureRouteSelector() { - if (mSelector == null) { - Bundle args = getArguments(); - if (args != null) { - mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR)); - } - if (mSelector == null) { - mSelector = MediaRouteSelector.EMPTY; - } - } - } - - /** - * Sets the media route selector for filtering the routes that the user can select. - * This method must be called before the fragment is added. - * - * @param selector The selector to set. - */ - public void setRouteSelector(MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - ensureRouteSelector(); - if (!mSelector.equals(selector)) { - mSelector = selector; - - Bundle args = getArguments(); - if (args == null) { - args = new Bundle(); - } - args.putBundle(ARGUMENT_SELECTOR, selector.asBundle()); - setArguments(args); - - MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog(); - if (dialog != null) { - dialog.setRouteSelector(selector); - } - } - } - - /** - * Called when the chooser dialog is being created. - *

    - * Subclasses may override this method to customize the dialog. - *

    - */ - public MediaRouteChooserDialog onCreateChooserDialog( - Context context, Bundle savedInstanceState) { - return new MediaRouteChooserDialog(context); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - mDialog = onCreateChooserDialog(getContext(), savedInstanceState); - mDialog.setRouteSelector(getRouteSelector()); - return mDialog; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (mDialog != null) { - mDialog.updateLayout(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java deleted file mode 100644 index f6c1d2f25c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java +++ /dev/null @@ -1,1487 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP; - -import android.app.AlertDialog; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemClock; -import android.support.v4.media.MediaDescriptionCompat; -import android.support.v4.media.MediaMetadataCompat; -import android.support.v4.media.session.MediaControllerCompat; -import android.support.v4.media.session.MediaSessionCompat; -import android.support.v4.media.session.PlaybackStateCompat; -import android.text.TextUtils; -import android.util.Log; -import android.view.ContextThemeWrapper; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.MeasureSpec; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationSet; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.view.animation.Transformation; -import android.view.animation.TranslateAnimation; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.TextView; - -import androidx.core.util.ObjectsCompat; -import androidx.core.view.accessibility.AccessibilityEventCompat; -import androidx.palette.graphics.Palette; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; -import com.android.support.mediarouter.app.OverlayListView.OverlayObject; -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * This class implements the route controller dialog for {@link MediaRouter}. - *

    - * This dialog allows the user to control or disconnect from the currently selected route. - *

    - * - * @see MediaRouteButton - * @see MediaRouteActionProvider - */ -public class MediaRouteControllerDialog extends AlertDialog { - // Tags should be less than 24 characters long (see docs for android.util.Log.isLoggable()) - static final String TAG = "MediaRouteCtrlDialog"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - // Time to wait before updating the volume when the user lets go of the seek bar - // to allow the route provider time to propagate the change and publish a new - // route descriptor. - static final int VOLUME_UPDATE_DELAY_MILLIS = 500; - static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L); - - private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3; - static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2; - static final int BUTTON_STOP_RES_ID = android.R.id.button1; - - final MediaRouter mRouter; - private final MediaRouterCallback mCallback; - final MediaRouter.RouteInfo mRoute; - - Context mContext; - private boolean mCreated; - private boolean mAttachedToWindow; - - private int mDialogContentWidth; - - private View mCustomControlView; - - private Button mDisconnectButton; - private Button mStopCastingButton; - private ImageButton mPlaybackControlButton; - private ImageButton mCloseButton; - private MediaRouteExpandCollapseButton mGroupExpandCollapseButton; - - private FrameLayout mExpandableAreaLayout; - private LinearLayout mDialogAreaLayout; - FrameLayout mDefaultControlLayout; - private FrameLayout mCustomControlLayout; - private ImageView mArtView; - private TextView mTitleView; - private TextView mSubtitleView; - private TextView mRouteNameTextView; - - private boolean mVolumeControlEnabled = true; - // Layout for media controllers including play/pause button and the main volume slider. - private LinearLayout mMediaMainControlLayout; - private RelativeLayout mPlaybackControlLayout; - private LinearLayout mVolumeControlLayout; - private View mDividerView; - - OverlayListView mVolumeGroupList; - VolumeGroupAdapter mVolumeGroupAdapter; - private List mGroupMemberRoutes; - Set mGroupMemberRoutesAdded; - private Set mGroupMemberRoutesRemoved; - Set mGroupMemberRoutesAnimatingWithBitmap; - SeekBar mVolumeSlider; - VolumeChangeListener mVolumeChangeListener; - MediaRouter.RouteInfo mRouteInVolumeSliderTouched; - private int mVolumeGroupListItemIconSize; - private int mVolumeGroupListItemHeight; - private int mVolumeGroupListMaxHeight; - private final int mVolumeGroupListPaddingTop; - Map mVolumeSliderMap; - - MediaControllerCompat mMediaController; - MediaControllerCallback mControllerCallback; - PlaybackStateCompat mState; - MediaDescriptionCompat mDescription; - - FetchArtTask mFetchArtTask; - Bitmap mArtIconBitmap; - Uri mArtIconUri; - boolean mArtIconIsLoaded; - Bitmap mArtIconLoadedBitmap; - int mArtIconBackgroundColor; - - boolean mHasPendingUpdate; - boolean mPendingUpdateAnimationNeeded; - - boolean mIsGroupExpanded; - boolean mIsGroupListAnimating; - boolean mIsGroupListAnimationPending; - int mGroupListAnimationDurationMs; - private int mGroupListFadeInDurationMs; - private int mGroupListFadeOutDurationMs; - - private Interpolator mInterpolator; - private Interpolator mLinearOutSlowInInterpolator; - private Interpolator mFastOutSlowInInterpolator; - private Interpolator mAccelerateDecelerateInterpolator; - - final AccessibilityManager mAccessibilityManager; - - Runnable mGroupListFadeInAnimation = new Runnable() { - @Override - public void run() { - startGroupListFadeInAnimation(); - } - }; - - public MediaRouteControllerDialog(Context context) { - this(context, 0); - } - - public MediaRouteControllerDialog(Context context, int theme) { - // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme. - super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context, - MediaRouterThemeHelper.getRouterThemeId(context))), theme); - mContext = getContext(); - - mControllerCallback = new MediaControllerCallback(); - mRouter = MediaRouter.getInstance(mContext); - mCallback = new MediaRouterCallback(); - mRoute = mRouter.getSelectedRoute(); - setMediaSession(mRouter.getMediaSessionToken()); - mVolumeGroupListPaddingTop = ApiHelper.getLibResources(context).getDimensionPixelSize( - R.dimen.mr_controller_volume_group_list_padding_top); - mAccessibilityManager = - (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( - mContext, android.R.interpolator.linear_out_slow_in); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator( - mContext, android.R.interpolator.fast_out_slow_in); - mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); - } - - /** - * Gets the route that this dialog is controlling. - */ - public MediaRouter.RouteInfo getRoute() { - return mRoute; - } - - private MediaRouter.RouteGroup getGroup() { - if (mRoute instanceof MediaRouter.RouteGroup) { - return (MediaRouter.RouteGroup) mRoute; - } - return null; - } - - /** - * Provides the subclass an opportunity to create a view that will replace the default media - * controls for the currently playing content. - * - * @param savedInstanceState The dialog's saved instance state. - * @return The media control view, or null if none. - */ - public View onCreateMediaControlView(Bundle savedInstanceState) { - return null; - } - - /** - * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}. - * - * @return The media control view, or null if none. - */ - public View getMediaControlView() { - return mCustomControlView; - } - - /** - * Sets whether to enable the volume slider and volume control using the volume keys - * when the route supports it. - *

    - * The default value is true. - *

    - */ - public void setVolumeControlEnabled(boolean enable) { - if (mVolumeControlEnabled != enable) { - mVolumeControlEnabled = enable; - if (mCreated) { - update(false); - } - } - } - - /** - * Returns whether to enable the volume slider and volume control using the volume keys - * when the route supports it. - */ - public boolean isVolumeControlEnabled() { - return mVolumeControlEnabled; - } - - /** - * Set the session to use for metadata and transport controls. The dialog - * will listen to changes on this session and update the UI automatically in - * response to changes. - * - * @param sessionToken The token for the session to use. - */ - private void setMediaSession(MediaSessionCompat.Token sessionToken) { - if (mMediaController != null) { - mMediaController.unregisterCallback(mControllerCallback); - mMediaController = null; - } - if (sessionToken == null) { - return; - } - if (!mAttachedToWindow) { - return; - } - try { - mMediaController = new MediaControllerCompat(mContext, sessionToken); - } catch (RemoteException e) { - Log.e(TAG, "Error creating media controller in setMediaSession.", e); - } - if (mMediaController != null) { - mMediaController.registerCallback(mControllerCallback); - } - MediaMetadataCompat metadata = mMediaController == null ? null - : mMediaController.getMetadata(); - mDescription = metadata == null ? null : metadata.getDescription(); - mState = mMediaController == null ? null : mMediaController.getPlaybackState(); - updateArtIconIfNeeded(); - update(false); - } - - /** - * Gets the session to use for metadata and transport controls. - * - * @return The token for the session to use or null if none. - */ - public MediaSessionCompat.Token getMediaSession() { - return mMediaController == null ? null : mMediaController.getSessionToken(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getWindow().setBackgroundDrawableResource(android.R.color.transparent); - - setContentView(ApiHelper.inflateLibLayout(mContext, - ApiHelper.getLibTheme(mContext, MediaRouterThemeHelper.getRouterThemeId(mContext)), - R.layout.mr_controller_material_dialog_b)); - - // Remove the neutral button. - findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE); - - ClickListener listener = new ClickListener(); - - mExpandableAreaLayout = findViewById(R.id.mr_expandable_area); - mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - mDialogAreaLayout = findViewById(R.id.mr_dialog_area); - mDialogAreaLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // Eat unhandled touch events. - } - }); - int color = MediaRouterThemeHelper.getButtonTextColor(mContext); - mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID); - mDisconnectButton.setText( - ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_disconnect)); - mDisconnectButton.setTextColor(color); - mDisconnectButton.setOnClickListener(listener); - - mStopCastingButton = findViewById(BUTTON_STOP_RES_ID); - mStopCastingButton.setText( - ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_stop_casting)); - mStopCastingButton.setTextColor(color); - mStopCastingButton.setOnClickListener(listener); - - mRouteNameTextView = findViewById(R.id.mr_name); - mCloseButton = findViewById(R.id.mr_close); - mCloseButton.setOnClickListener(listener); - mCustomControlLayout = findViewById(R.id.mr_custom_control); - mDefaultControlLayout = findViewById(R.id.mr_default_control); - - // Start the session activity when a content item (album art, title or subtitle) is clicked. - View.OnClickListener onClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mMediaController != null) { - PendingIntent pi = mMediaController.getSessionActivity(); - if (pi != null) { - try { - pi.send(); - dismiss(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, pi + " was not sent, it had been canceled."); - } - } - } - } - }; - mArtView = findViewById(R.id.mr_art); - mArtView.setOnClickListener(onClickListener); - findViewById(R.id.mr_control_title_container).setOnClickListener(onClickListener); - - mMediaMainControlLayout = findViewById(R.id.mr_media_main_control); - mDividerView = findViewById(R.id.mr_control_divider); - - mPlaybackControlLayout = findViewById(R.id.mr_playback_control); - mTitleView = findViewById(R.id.mr_control_title); - mSubtitleView = findViewById(R.id.mr_control_subtitle); - mPlaybackControlButton = findViewById(R.id.mr_control_playback_ctrl); - mPlaybackControlButton.setOnClickListener(listener); - - mVolumeControlLayout = findViewById(R.id.mr_volume_control); - mVolumeControlLayout.setVisibility(View.GONE); - mVolumeSlider = findViewById(R.id.mr_volume_slider); - mVolumeSlider.setTag(mRoute); - mVolumeChangeListener = new VolumeChangeListener(); - mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener); - - mVolumeGroupList = findViewById(R.id.mr_volume_group_list); - mGroupMemberRoutes = new ArrayList(); - mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(), - mGroupMemberRoutes); - mVolumeGroupList.setAdapter(mVolumeGroupAdapter); - mGroupMemberRoutesAnimatingWithBitmap = new HashSet<>(); - - MediaRouterThemeHelper.setMediaControlsBackgroundColor(mContext, - mMediaMainControlLayout, mVolumeGroupList, getGroup() != null); - MediaRouterThemeHelper.setVolumeSliderColor(mContext, - (MediaRouteVolumeSlider) mVolumeSlider, mMediaMainControlLayout); - mVolumeSliderMap = new HashMap<>(); - mVolumeSliderMap.put(mRoute, mVolumeSlider); - - mGroupExpandCollapseButton = - findViewById(R.id.mr_group_expand_collapse); - mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mIsGroupExpanded = !mIsGroupExpanded; - if (mIsGroupExpanded) { - mVolumeGroupList.setVisibility(View.VISIBLE); - } - loadInterpolator(); - updateLayoutHeight(true); - } - }); - loadInterpolator(); - mGroupListAnimationDurationMs = ApiHelper.getLibResources(mContext).getInteger( - R.integer.mr_controller_volume_group_list_animation_duration_ms); - mGroupListFadeInDurationMs = ApiHelper.getLibResources(mContext).getInteger( - R.integer.mr_controller_volume_group_list_fade_in_duration_ms); - mGroupListFadeOutDurationMs = ApiHelper.getLibResources(mContext).getInteger( - R.integer.mr_controller_volume_group_list_fade_out_duration_ms); - - mCustomControlView = onCreateMediaControlView(savedInstanceState); - if (mCustomControlView != null) { - mCustomControlLayout.addView(mCustomControlView); - mCustomControlLayout.setVisibility(View.VISIBLE); - } - mCreated = true; - updateLayout(); - } - - /** - * Sets the width of the dialog. Also called when configuration changes. - */ - void updateLayout() { - int width = MediaRouteDialogHelper.getDialogWidth(mContext); - getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT); - - View decorView = getWindow().getDecorView(); - mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight(); - - Resources res = ApiHelper.getLibResources(mContext); - mVolumeGroupListItemIconSize = res.getDimensionPixelSize( - R.dimen.mr_controller_volume_group_list_item_icon_size); - mVolumeGroupListItemHeight = res.getDimensionPixelSize( - R.dimen.mr_controller_volume_group_list_item_height); - mVolumeGroupListMaxHeight = res.getDimensionPixelSize( - R.dimen.mr_controller_volume_group_list_max_height); - - // Fetch art icons again for layout changes to resize it accordingly - mArtIconBitmap = null; - mArtIconUri = null; - updateArtIconIfNeeded(); - update(false); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mAttachedToWindow = true; - - mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback, - MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS); - setMediaSession(mRouter.getMediaSessionToken()); - } - - @Override - public void onDetachedFromWindow() { - mRouter.removeCallback(mCallback); - setMediaSession(null); - mAttachedToWindow = false; - super.onDetachedFromWindow(); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1); - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - return true; - } - return super.onKeyUp(keyCode, event); - } - - void update(boolean animate) { - // Defer dialog updates if a user is adjusting a volume in the list - if (mRouteInVolumeSliderTouched != null) { - mHasPendingUpdate = true; - mPendingUpdateAnimationNeeded |= animate; - return; - } - mHasPendingUpdate = false; - mPendingUpdateAnimationNeeded = false; - if (!mRoute.isSelected() || mRoute.isDefaultOrBluetooth()) { - dismiss(); - return; - } - if (!mCreated) { - return; - } - - mRouteNameTextView.setText(mRoute.getName()); - mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE); - if (mCustomControlView == null && mArtIconIsLoaded) { - if (isBitmapRecycled(mArtIconLoadedBitmap)) { - Log.w(TAG, "Can't set artwork image with recycled bitmap: " + mArtIconLoadedBitmap); - } else { - mArtView.setImageBitmap(mArtIconLoadedBitmap); - mArtView.setBackgroundColor(mArtIconBackgroundColor); - } - clearLoadedBitmap(); - } - updateVolumeControlLayout(); - updatePlaybackControlLayout(); - updateLayoutHeight(animate); - } - - private boolean isBitmapRecycled(Bitmap bitmap) { - return bitmap != null && bitmap.isRecycled(); - } - - private boolean canShowPlaybackControlLayout() { - return mCustomControlView == null && (mDescription != null || mState != null); - } - - /** - * Returns the height of main media controller which includes playback control and master - * volume control. - */ - private int getMainControllerHeight(boolean showPlaybackControl) { - int height = 0; - if (showPlaybackControl || mVolumeControlLayout.getVisibility() == View.VISIBLE) { - height += mMediaMainControlLayout.getPaddingTop() - + mMediaMainControlLayout.getPaddingBottom(); - if (showPlaybackControl) { - height += mPlaybackControlLayout.getMeasuredHeight(); - } - if (mVolumeControlLayout.getVisibility() == View.VISIBLE) { - height += mVolumeControlLayout.getMeasuredHeight(); - } - if (showPlaybackControl && mVolumeControlLayout.getVisibility() == View.VISIBLE) { - height += mDividerView.getMeasuredHeight(); - } - } - return height; - } - - private void updateMediaControlVisibility(boolean canShowPlaybackControlLayout) { - // TODO: Update the top and bottom padding of the control layout according to the display - // height. - mDividerView.setVisibility((mVolumeControlLayout.getVisibility() == View.VISIBLE - && canShowPlaybackControlLayout) ? View.VISIBLE : View.GONE); - mMediaMainControlLayout.setVisibility((mVolumeControlLayout.getVisibility() == View.GONE - && !canShowPlaybackControlLayout) ? View.GONE : View.VISIBLE); - } - - void updateLayoutHeight(final boolean animate) { - // We need to defer the update until the first layout has occurred, as we don't yet know the - // overall visible display size in which the window this view is attached to has been - // positioned in. - mDefaultControlLayout.requestLayout(); - ViewTreeObserver observer = mDefaultControlLayout.getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - mDefaultControlLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); - if (mIsGroupListAnimating) { - mIsGroupListAnimationPending = true; - } else { - updateLayoutHeightInternal(animate); - } - } - }); - } - - /** - * Updates the height of views and hide artwork or metadata if space is limited. - */ - void updateLayoutHeightInternal(boolean animate) { - // Measure the size of widgets and get the height of main components. - int oldHeight = getLayoutHeight(mMediaMainControlLayout); - setLayoutHeight(mMediaMainControlLayout, ViewGroup.LayoutParams.MATCH_PARENT); - updateMediaControlVisibility(canShowPlaybackControlLayout()); - View decorView = getWindow().getDecorView(); - decorView.measure( - MeasureSpec.makeMeasureSpec(getWindow().getAttributes().width, MeasureSpec.EXACTLY), - MeasureSpec.UNSPECIFIED); - setLayoutHeight(mMediaMainControlLayout, oldHeight); - int artViewHeight = 0; - if (mCustomControlView == null && mArtView.getDrawable() instanceof BitmapDrawable) { - Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap(); - if (art != null) { - artViewHeight = getDesiredArtHeight(art.getWidth(), art.getHeight()); - mArtView.setScaleType(art.getWidth() >= art.getHeight() - ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER); - } - } - int mainControllerHeight = getMainControllerHeight(canShowPlaybackControlLayout()); - int volumeGroupListCount = mGroupMemberRoutes.size(); - // Scale down volume group list items in landscape mode. - int expandedGroupListHeight = getGroup() == null ? 0 : - mVolumeGroupListItemHeight * getGroup().getRoutes().size(); - if (volumeGroupListCount > 0) { - expandedGroupListHeight += mVolumeGroupListPaddingTop; - } - expandedGroupListHeight = Math.min(expandedGroupListHeight, mVolumeGroupListMaxHeight); - int visibleGroupListHeight = mIsGroupExpanded ? expandedGroupListHeight : 0; - - int desiredControlLayoutHeight = - Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight; - Rect visibleRect = new Rect(); - decorView.getWindowVisibleDisplayFrame(visibleRect); - // Height of non-control views in decor view. - // This includes title bar, button bar, and dialog's vertical padding which should be - // always shown. - int nonControlViewHeight = mDialogAreaLayout.getMeasuredHeight() - - mDefaultControlLayout.getMeasuredHeight(); - // Maximum allowed height for controls to fit screen. - int maximumControlViewHeight = visibleRect.height() - nonControlViewHeight; - - // Show artwork if it fits the screen. - if (mCustomControlView == null && artViewHeight > 0 - && desiredControlLayoutHeight <= maximumControlViewHeight) { - mArtView.setVisibility(View.VISIBLE); - setLayoutHeight(mArtView, artViewHeight); - } else { - if (getLayoutHeight(mVolumeGroupList) + mMediaMainControlLayout.getMeasuredHeight() - >= mDefaultControlLayout.getMeasuredHeight()) { - mArtView.setVisibility(View.GONE); - } - artViewHeight = 0; - desiredControlLayoutHeight = visibleGroupListHeight + mainControllerHeight; - } - // Show the playback control if it fits the screen. - if (canShowPlaybackControlLayout() - && desiredControlLayoutHeight <= maximumControlViewHeight) { - mPlaybackControlLayout.setVisibility(View.VISIBLE); - } else { - mPlaybackControlLayout.setVisibility(View.GONE); - } - updateMediaControlVisibility(mPlaybackControlLayout.getVisibility() == View.VISIBLE); - mainControllerHeight = getMainControllerHeight( - mPlaybackControlLayout.getVisibility() == View.VISIBLE); - desiredControlLayoutHeight = - Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight; - - // Limit the volume group list height to fit the screen. - if (desiredControlLayoutHeight > maximumControlViewHeight) { - visibleGroupListHeight -= (desiredControlLayoutHeight - maximumControlViewHeight); - desiredControlLayoutHeight = maximumControlViewHeight; - } - // Update the layouts with the computed heights. - mMediaMainControlLayout.clearAnimation(); - mVolumeGroupList.clearAnimation(); - mDefaultControlLayout.clearAnimation(); - if (animate) { - animateLayoutHeight(mMediaMainControlLayout, mainControllerHeight); - animateLayoutHeight(mVolumeGroupList, visibleGroupListHeight); - animateLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight); - } else { - setLayoutHeight(mMediaMainControlLayout, mainControllerHeight); - setLayoutHeight(mVolumeGroupList, visibleGroupListHeight); - setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight); - } - // Maximize the window size with a transparent layout in advance for smooth animation. - setLayoutHeight(mExpandableAreaLayout, visibleRect.height()); - rebuildVolumeGroupList(animate); - } - - void updateVolumeGroupItemHeight(View item) { - LinearLayout container = (LinearLayout) item.findViewById(R.id.volume_item_container); - setLayoutHeight(container, mVolumeGroupListItemHeight); - View icon = item.findViewById(R.id.mr_volume_item_icon); - ViewGroup.LayoutParams lp = icon.getLayoutParams(); - lp.width = mVolumeGroupListItemIconSize; - lp.height = mVolumeGroupListItemIconSize; - icon.setLayoutParams(lp); - } - - private void animateLayoutHeight(final View view, int targetHeight) { - final int startValue = getLayoutHeight(view); - final int endValue = targetHeight; - Animation anim = new Animation() { - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - int height = startValue - (int) ((startValue - endValue) * interpolatedTime); - setLayoutHeight(view, height); - } - }; - anim.setDuration(mGroupListAnimationDurationMs); - if (android.os.Build.VERSION.SDK_INT >= 21) { - anim.setInterpolator(mInterpolator); - } - view.startAnimation(anim); - } - - void loadInterpolator() { - mInterpolator = - mIsGroupExpanded ? mLinearOutSlowInInterpolator : mFastOutSlowInInterpolator; - } - - private void updateVolumeControlLayout() { - if (isVolumeControlAvailable(mRoute)) { - if (mVolumeControlLayout.getVisibility() == View.GONE) { - mVolumeControlLayout.setVisibility(View.VISIBLE); - mVolumeSlider.setMax(mRoute.getVolumeMax()); - mVolumeSlider.setProgress(mRoute.getVolume()); - mGroupExpandCollapseButton.setVisibility(getGroup() == null ? View.GONE - : View.VISIBLE); - } - } else { - mVolumeControlLayout.setVisibility(View.GONE); - } - } - - private void rebuildVolumeGroupList(boolean animate) { - List routes = getGroup() == null ? null : getGroup().getRoutes(); - if (routes == null) { - mGroupMemberRoutes.clear(); - mVolumeGroupAdapter.notifyDataSetChanged(); - } else if (MediaRouteDialogHelper.listUnorderedEquals(mGroupMemberRoutes, routes)) { - mVolumeGroupAdapter.notifyDataSetChanged(); - } else { - HashMap previousRouteBoundMap = animate - ? MediaRouteDialogHelper.getItemBoundMap(mVolumeGroupList, mVolumeGroupAdapter) - : null; - HashMap previousRouteBitmapMap = animate - ? MediaRouteDialogHelper.getItemBitmapMap(mContext, mVolumeGroupList, - mVolumeGroupAdapter) : null; - mGroupMemberRoutesAdded = - MediaRouteDialogHelper.getItemsAdded(mGroupMemberRoutes, routes); - mGroupMemberRoutesRemoved = MediaRouteDialogHelper.getItemsRemoved(mGroupMemberRoutes, - routes); - mGroupMemberRoutes.addAll(0, mGroupMemberRoutesAdded); - mGroupMemberRoutes.removeAll(mGroupMemberRoutesRemoved); - mVolumeGroupAdapter.notifyDataSetChanged(); - if (animate && mIsGroupExpanded - && mGroupMemberRoutesAdded.size() + mGroupMemberRoutesRemoved.size() > 0) { - animateGroupListItems(previousRouteBoundMap, previousRouteBitmapMap); - } else { - mGroupMemberRoutesAdded = null; - mGroupMemberRoutesRemoved = null; - } - } - } - - private void animateGroupListItems(final Map previousRouteBoundMap, - final Map previousRouteBitmapMap) { - mVolumeGroupList.setEnabled(false); - mVolumeGroupList.requestLayout(); - mIsGroupListAnimating = true; - ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this); - animateGroupListItemsInternal(previousRouteBoundMap, previousRouteBitmapMap); - } - }); - } - - void animateGroupListItemsInternal( - Map previousRouteBoundMap, - Map previousRouteBitmapMap) { - if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) { - return; - } - int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size(); - boolean listenerRegistered = false; - Animation.AnimationListener listener = new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - mVolumeGroupList.startAnimationAll(); - mVolumeGroupList.postDelayed(mGroupListFadeInAnimation, - mGroupListAnimationDurationMs); - } - - @Override - public void onAnimationEnd(Animation animation) { } - - @Override - public void onAnimationRepeat(Animation animation) { } - }; - - // Animate visible items from previous positions to current positions except routes added - // just before. Added routes will remain hidden until translate animation finishes. - int first = mVolumeGroupList.getFirstVisiblePosition(); - for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { - View view = mVolumeGroupList.getChildAt(i); - int position = first + i; - MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); - Rect previousBounds = previousRouteBoundMap.get(route); - int currentTop = view.getTop(); - int previousTop = previousBounds != null ? previousBounds.top - : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta); - AnimationSet animSet = new AnimationSet(true); - if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { - previousTop = currentTop; - Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); - alphaAnim.setDuration(mGroupListFadeInDurationMs); - animSet.addAnimation(alphaAnim); - } - Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0); - translationAnim.setDuration(mGroupListAnimationDurationMs); - animSet.addAnimation(translationAnim); - animSet.setFillAfter(true); - animSet.setFillEnabled(true); - animSet.setInterpolator(mInterpolator); - if (!listenerRegistered) { - listenerRegistered = true; - animSet.setAnimationListener(listener); - } - view.clearAnimation(); - view.startAnimation(animSet); - previousRouteBoundMap.remove(route); - previousRouteBitmapMap.remove(route); - } - - // If a member route doesn't exist any longer, it can be either removed or moved out of the - // ListView layout boundary. In this case, use the previously captured bitmaps for - // animation. - for (Map.Entry item - : previousRouteBitmapMap.entrySet()) { - final MediaRouter.RouteInfo route = item.getKey(); - final BitmapDrawable bitmap = item.getValue(); - final Rect bounds = previousRouteBoundMap.get(route); - OverlayObject object = null; - if (mGroupMemberRoutesRemoved.contains(route)) { - object = new OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f) - .setDuration(mGroupListFadeOutDurationMs) - .setInterpolator(mInterpolator); - } else { - int deltaY = groupSizeDelta * mVolumeGroupListItemHeight; - object = new OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY) - .setDuration(mGroupListAnimationDurationMs) - .setInterpolator(mInterpolator) - .setAnimationEndListener(new OverlayObject.OnAnimationEndListener() { - @Override - public void onAnimationEnd() { - mGroupMemberRoutesAnimatingWithBitmap.remove(route); - mVolumeGroupAdapter.notifyDataSetChanged(); - } - }); - mGroupMemberRoutesAnimatingWithBitmap.add(route); - } - mVolumeGroupList.addOverlayObject(object); - } - } - - void startGroupListFadeInAnimation() { - clearGroupListAnimation(true); - mVolumeGroupList.requestLayout(); - ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this); - startGroupListFadeInAnimationInternal(); - } - }); - } - - void startGroupListFadeInAnimationInternal() { - if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.size() != 0) { - fadeInAddedRoutes(); - } else { - finishAnimation(true); - } - } - - void finishAnimation(boolean animate) { - mGroupMemberRoutesAdded = null; - mGroupMemberRoutesRemoved = null; - mIsGroupListAnimating = false; - if (mIsGroupListAnimationPending) { - mIsGroupListAnimationPending = false; - updateLayoutHeight(animate); - } - mVolumeGroupList.setEnabled(true); - } - - private void fadeInAddedRoutes() { - Animation.AnimationListener listener = new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { } - - @Override - public void onAnimationEnd(Animation animation) { - finishAnimation(true); - } - - @Override - public void onAnimationRepeat(Animation animation) { } - }; - boolean listenerRegistered = false; - int first = mVolumeGroupList.getFirstVisiblePosition(); - for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { - View view = mVolumeGroupList.getChildAt(i); - int position = first + i; - MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); - if (mGroupMemberRoutesAdded.contains(route)) { - Animation alphaAnim = new AlphaAnimation(0.0f, 1.0f); - alphaAnim.setDuration(mGroupListFadeInDurationMs); - alphaAnim.setFillEnabled(true); - alphaAnim.setFillAfter(true); - if (!listenerRegistered) { - listenerRegistered = true; - alphaAnim.setAnimationListener(listener); - } - view.clearAnimation(); - view.startAnimation(alphaAnim); - } - } - } - - void clearGroupListAnimation(boolean exceptAddedRoutes) { - int first = mVolumeGroupList.getFirstVisiblePosition(); - for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { - View view = mVolumeGroupList.getChildAt(i); - int position = first + i; - MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); - if (exceptAddedRoutes && mGroupMemberRoutesAdded != null - && mGroupMemberRoutesAdded.contains(route)) { - continue; - } - LinearLayout container = (LinearLayout) view.findViewById(R.id.volume_item_container); - container.setVisibility(View.VISIBLE); - AnimationSet animSet = new AnimationSet(true); - Animation alphaAnim = new AlphaAnimation(1.0f, 1.0f); - alphaAnim.setDuration(0); - animSet.addAnimation(alphaAnim); - Animation translationAnim = new TranslateAnimation(0, 0, 0, 0); - translationAnim.setDuration(0); - animSet.setFillAfter(true); - animSet.setFillEnabled(true); - view.clearAnimation(); - view.startAnimation(animSet); - } - mVolumeGroupList.stopAnimationAll(); - if (!exceptAddedRoutes) { - finishAnimation(false); - } - } - - private void updatePlaybackControlLayout() { - if (canShowPlaybackControlLayout()) { - CharSequence title = mDescription == null ? null : mDescription.getTitle(); - boolean hasTitle = !TextUtils.isEmpty(title); - - CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle(); - boolean hasSubtitle = !TextUtils.isEmpty(subtitle); - - boolean showTitle = false; - boolean showSubtitle = false; - if (mRoute.getPresentationDisplayId() - != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) { - // The user is currently casting screen. - mTitleView.setText(ApiHelper.getLibResources(mContext).getString( - R.string.mr_controller_casting_screen)); - showTitle = true; - } else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) { - // Show "No media selected" as we don't yet know the playback state. - mTitleView.setText(ApiHelper.getLibResources(mContext).getString( - R.string.mr_controller_no_media_selected)); - showTitle = true; - } else if (!hasTitle && !hasSubtitle) { - mTitleView.setText(ApiHelper.getLibResources(mContext).getString( - R.string.mr_controller_no_info_available)); - showTitle = true; - } else { - if (hasTitle) { - mTitleView.setText(title); - showTitle = true; - } - if (hasSubtitle) { - mSubtitleView.setText(subtitle); - showSubtitle = true; - } - } - mTitleView.setVisibility(showTitle ? View.VISIBLE : View.GONE); - mSubtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE); - - if (mState != null) { - boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING - || mState.getState() == PlaybackStateCompat.STATE_PLAYING; - Context playbackControlButtonContext = mPlaybackControlButton.getContext(); - boolean visible = true; - int iconDrawableAttr = 0; - int iconDescResId = 0; - if (isPlaying && isPauseActionSupported()) { - iconDrawableAttr = R.attr.mediaRoutePauseDrawable; - iconDescResId = R.string.mr_controller_pause; - } else if (isPlaying && isStopActionSupported()) { - iconDrawableAttr = R.attr.mediaRouteStopDrawable; - iconDescResId = R.string.mr_controller_stop; - } else if (!isPlaying && isPlayActionSupported()) { - iconDrawableAttr = R.attr.mediaRoutePlayDrawable; - iconDescResId = R.string.mr_controller_play; - } else { - visible = false; - } - mPlaybackControlButton.setVisibility(visible ? View.VISIBLE : View.GONE); - if (visible) { - mPlaybackControlButton.setImageResource( - MediaRouterThemeHelper.getThemeResource( - playbackControlButtonContext, iconDrawableAttr)); - mPlaybackControlButton.setContentDescription( - playbackControlButtonContext.getResources() - .getText(iconDescResId)); - } - } - } - } - - private boolean isPlayActionSupported() { - return (mState.getActions() & (ACTION_PLAY | ACTION_PLAY_PAUSE)) != 0; - } - - private boolean isPauseActionSupported() { - return (mState.getActions() & (ACTION_PAUSE | ACTION_PLAY_PAUSE)) != 0; - } - - private boolean isStopActionSupported() { - return (mState.getActions() & ACTION_STOP) != 0; - } - - boolean isVolumeControlAvailable(MediaRouter.RouteInfo route) { - return mVolumeControlEnabled && route.getVolumeHandling() - == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE; - } - - private static int getLayoutHeight(View view) { - return view.getLayoutParams().height; - } - - static void setLayoutHeight(View view, int height) { - ViewGroup.LayoutParams lp = view.getLayoutParams(); - lp.height = height; - view.setLayoutParams(lp); - } - - private static boolean uriEquals(Uri uri1, Uri uri2) { - if (uri1 != null && uri1.equals(uri2)) { - return true; - } else if (uri1 == null && uri2 == null) { - return true; - } - return false; - } - - /** - * Returns desired art height to fit into controller dialog. - */ - int getDesiredArtHeight(int originalWidth, int originalHeight) { - if (originalWidth >= originalHeight) { - // For landscape art, fit width to dialog width. - return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f); - } - // For portrait art, fit height to 16:9 ratio case's height. - return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f); - } - - void updateArtIconIfNeeded() { - if (mCustomControlView != null || !isIconChanged()) { - return; - } - if (mFetchArtTask != null) { - mFetchArtTask.cancel(true); - } - mFetchArtTask = new FetchArtTask(); - mFetchArtTask.execute(); - } - - /** - * Clear the bitmap loaded by FetchArtTask. Will be called after the loaded bitmaps are applied - * to artwork, or no longer valid. - */ - void clearLoadedBitmap() { - mArtIconIsLoaded = false; - mArtIconLoadedBitmap = null; - mArtIconBackgroundColor = 0; - } - - /** - * Returns whether a new art image is different from an original art image. Compares - * Bitmap objects first, and then compares URIs only if bitmap is unchanged with - * a null value. - */ - private boolean isIconChanged() { - Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap(); - Uri newUri = mDescription == null ? null : mDescription.getIconUri(); - Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap(); - Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri(); - if (oldBitmap != newBitmap) { - return true; - } else if (oldBitmap == null && !uriEquals(oldUri, newUri)) { - return true; - } - return false; - } - - private final class MediaRouterCallback extends MediaRouter.Callback { - MediaRouterCallback() { - } - - @Override - public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) { - update(false); - } - - @Override - public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) { - update(true); - } - - @Override - public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) { - SeekBar volumeSlider = mVolumeSliderMap.get(route); - int volume = route.getVolume(); - if (DEBUG) { - Log.d(TAG, "onRouteVolumeChanged(), route.getVolume:" + volume); - } - if (volumeSlider != null && mRouteInVolumeSliderTouched != route) { - volumeSlider.setProgress(volume); - } - } - } - - private final class MediaControllerCallback extends MediaControllerCompat.Callback { - MediaControllerCallback() { - } - - @Override - public void onSessionDestroyed() { - if (mMediaController != null) { - mMediaController.unregisterCallback(mControllerCallback); - mMediaController = null; - } - } - - @Override - public void onPlaybackStateChanged(PlaybackStateCompat state) { - mState = state; - update(false); - } - - @Override - public void onMetadataChanged(MediaMetadataCompat metadata) { - mDescription = metadata == null ? null : metadata.getDescription(); - updateArtIconIfNeeded(); - update(false); - } - } - - private final class ClickListener implements View.OnClickListener { - ClickListener() { - } - - @Override - public void onClick(View v) { - int id = v.getId(); - if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) { - if (mRoute.isSelected()) { - mRouter.unselect(id == BUTTON_STOP_RES_ID ? - MediaRouter.UNSELECT_REASON_STOPPED : - MediaRouter.UNSELECT_REASON_DISCONNECTED); - } - dismiss(); - } else if (id == R.id.mr_control_playback_ctrl) { - if (mMediaController != null && mState != null) { - boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_PLAYING; - int actionDescResId = 0; - if (isPlaying && isPauseActionSupported()) { - mMediaController.getTransportControls().pause(); - actionDescResId = R.string.mr_controller_pause; - } else if (isPlaying && isStopActionSupported()) { - mMediaController.getTransportControls().stop(); - actionDescResId = R.string.mr_controller_stop; - } else if (!isPlaying && isPlayActionSupported()){ - mMediaController.getTransportControls().play(); - actionDescResId = R.string.mr_controller_play; - } - // Announce the action for accessibility. - if (mAccessibilityManager != null && mAccessibilityManager.isEnabled() - && actionDescResId != 0) { - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEventCompat.TYPE_ANNOUNCEMENT); - event.setPackageName(mContext.getPackageName()); - event.setClassName(getClass().getName()); - event.getText().add( - ApiHelper.getLibResources(mContext).getString(actionDescResId)); - mAccessibilityManager.sendAccessibilityEvent(event); - } - } - } else if (id == R.id.mr_close) { - dismiss(); - } - } - } - - private class VolumeChangeListener implements SeekBar.OnSeekBarChangeListener { - private final Runnable mStopTrackingTouch = new Runnable() { - @Override - public void run() { - if (mRouteInVolumeSliderTouched != null) { - mRouteInVolumeSliderTouched = null; - if (mHasPendingUpdate) { - update(mPendingUpdateAnimationNeeded); - } - } - } - }; - - VolumeChangeListener() { - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - if (mRouteInVolumeSliderTouched != null) { - mVolumeSlider.removeCallbacks(mStopTrackingTouch); - } - mRouteInVolumeSliderTouched = (MediaRouter.RouteInfo) seekBar.getTag(); - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // Defer resetting mVolumeSliderTouched to allow the media route provider - // a little time to settle into its new state and publish the final - // volume update. - mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) seekBar.getTag(); - if (DEBUG) { - Log.d(TAG, "onProgressChanged(): calling " - + "MediaRouter.RouteInfo.requestSetVolume(" + progress + ")"); - } - route.requestSetVolume(progress); - } - } - } - - private class VolumeGroupAdapter extends ArrayAdapter { - final float mDisabledAlpha; - - public VolumeGroupAdapter(Context context, List objects) { - super(context, 0, objects); - mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context); - } - - @Override - public boolean isEnabled(int position) { - return false; - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - View v = convertView; - if (v == null) { - v = LayoutInflater.from(parent.getContext()).inflate( - R.layout.mr_controller_volume_item, parent, false); - } else { - updateVolumeGroupItemHeight(v); - } - - MediaRouter.RouteInfo route = getItem(position); - if (route != null) { - boolean isEnabled = route.isEnabled(); - - TextView routeName = (TextView) v.findViewById(R.id.mr_name); - routeName.setEnabled(isEnabled); - routeName.setText(route.getName()); - - MediaRouteVolumeSlider volumeSlider = - (MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider); - MediaRouterThemeHelper.setVolumeSliderColor( - parent.getContext(), volumeSlider, mVolumeGroupList); - volumeSlider.setTag(route); - mVolumeSliderMap.put(route, volumeSlider); - volumeSlider.setHideThumb(!isEnabled); - volumeSlider.setEnabled(isEnabled); - if (isEnabled) { - if (isVolumeControlAvailable(route)) { - volumeSlider.setMax(route.getVolumeMax()); - volumeSlider.setProgress(route.getVolume()); - volumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener); - } else { - volumeSlider.setMax(100); - volumeSlider.setProgress(100); - volumeSlider.setEnabled(false); - } - } - - ImageView volumeItemIcon = - (ImageView) v.findViewById(R.id.mr_volume_item_icon); - volumeItemIcon.setAlpha(isEnabled ? 0xFF : (int) (0xFF * mDisabledAlpha)); - - // If overlay bitmap exists, real view should remain hidden until - // the animation ends. - LinearLayout container = (LinearLayout) v.findViewById(R.id.volume_item_container); - container.setVisibility(mGroupMemberRoutesAnimatingWithBitmap.contains(route) - ? View.INVISIBLE : View.VISIBLE); - - // Routes which are being added will be invisible until animation ends. - if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { - Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); - alphaAnim.setDuration(0); - alphaAnim.setFillEnabled(true); - alphaAnim.setFillAfter(true); - v.clearAnimation(); - v.startAnimation(alphaAnim); - } - } - return v; - } - } - - private class FetchArtTask extends AsyncTask { - // Show animation only when fetching takes a long time. - private static final long SHOW_ANIM_TIME_THRESHOLD_MILLIS = 120L; - - private final Bitmap mIconBitmap; - private final Uri mIconUri; - private int mBackgroundColor; - private long mStartTimeMillis; - - FetchArtTask() { - Bitmap bitmap = mDescription == null ? null : mDescription.getIconBitmap(); - if (isBitmapRecycled(bitmap)) { - Log.w(TAG, "Can't fetch the given art bitmap because it's already recycled."); - bitmap = null; - } - mIconBitmap = bitmap; - mIconUri = mDescription == null ? null : mDescription.getIconUri(); - } - - public Bitmap getIconBitmap() { - return mIconBitmap; - } - - public Uri getIconUri() { - return mIconUri; - } - - @Override - protected void onPreExecute() { - mStartTimeMillis = SystemClock.uptimeMillis(); - clearLoadedBitmap(); - } - - @Override - protected Bitmap doInBackground(Void... arg) { - Bitmap art = null; - if (mIconBitmap != null) { - art = mIconBitmap; - } else if (mIconUri != null) { - InputStream stream = null; - try { - if ((stream = openInputStreamByScheme(mIconUri)) == null) { - Log.w(TAG, "Unable to open: " + mIconUri); - return null; - } - // Query art size. - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(stream, null, options); - if (options.outWidth == 0 || options.outHeight == 0) { - return null; - } - // Rewind the stream in order to restart art decoding. - try { - stream.reset(); - } catch (IOException e) { - // Failed to rewind the stream, try to reopen it. - stream.close(); - if ((stream = openInputStreamByScheme(mIconUri)) == null) { - Log.w(TAG, "Unable to open: " + mIconUri); - return null; - } - } - // Calculate required size to decode the art and possibly resize it. - options.inJustDecodeBounds = false; - int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight); - int ratio = options.outHeight / reqHeight; - options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio)); - if (isCancelled()) { - return null; - } - art = BitmapFactory.decodeStream(stream, null, options); - } catch (IOException e){ - Log.w(TAG, "Unable to open: " + mIconUri, e); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - } - } - } - } - if (isBitmapRecycled(art)) { - Log.w(TAG, "Can't use recycled bitmap: " + art); - return null; - } - if (art != null && art.getWidth() < art.getHeight()) { - // Portrait art requires dominant color as background color. - Palette palette = new Palette.Builder(art).maximumColorCount(1).generate(); - mBackgroundColor = palette.getSwatches().isEmpty() - ? 0 : palette.getSwatches().get(0).getRgb(); - } - return art; - } - - @Override - protected void onPostExecute(Bitmap art) { - mFetchArtTask = null; - if (!ObjectsCompat.equals(mArtIconBitmap, mIconBitmap) - || !ObjectsCompat.equals(mArtIconUri, mIconUri)) { - mArtIconBitmap = mIconBitmap; - mArtIconLoadedBitmap = art; - mArtIconUri = mIconUri; - mArtIconBackgroundColor = mBackgroundColor; - mArtIconIsLoaded = true; - long elapsedTimeMillis = SystemClock.uptimeMillis() - mStartTimeMillis; - // Loaded bitmap will be applied on the next update - update(elapsedTimeMillis > SHOW_ANIM_TIME_THRESHOLD_MILLIS); - } - } - - private InputStream openInputStreamByScheme(Uri uri) throws IOException { - String scheme = uri.getScheme().toLowerCase(); - InputStream stream = null; - if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme) - || ContentResolver.SCHEME_CONTENT.equals(scheme) - || ContentResolver.SCHEME_FILE.equals(scheme)) { - stream = mContext.getContentResolver().openInputStream(uri); - } else { - URL url = new URL(uri.toString()); - URLConnection conn = url.openConnection(); - conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); - conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); - stream = conn.getInputStream(); - } - return (stream == null) ? null : new BufferedInputStream(stream); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java deleted file mode 100644 index 215d74f867..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.Context; -import android.content.res.Configuration; -import android.os.Bundle; - -/** - * Media route controller dialog fragment. - *

    - * Creates a {@link MediaRouteControllerDialog}. The application may subclass - * this dialog fragment to customize the media route controller dialog. - *

    - */ -public class MediaRouteControllerDialogFragment extends DialogFragment { - private MediaRouteControllerDialog mDialog; - /** - * Creates a media route controller dialog fragment. - *

    - * All subclasses of this class must also possess a default constructor. - *

    - */ - public MediaRouteControllerDialogFragment() { - setCancelable(true); - } - - /** - * Called when the controller dialog is being created. - *

    - * Subclasses may override this method to customize the dialog. - *

    - */ - public MediaRouteControllerDialog onCreateControllerDialog( - Context context, Bundle savedInstanceState) { - return new MediaRouteControllerDialog(context); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - mDialog = onCreateControllerDialog(getContext(), savedInstanceState); - return mDialog; - } - - @Override - public void onStop() { - super.onStop(); - if (mDialog != null) { - mDialog.clearGroupListAnimation(false); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (mDialog != null) { - mDialog.updateLayout(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java deleted file mode 100644 index b5ee63ec21..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import androidx.annotation.NonNull; - -/** - * The media route dialog factory is responsible for creating the media route - * chooser and controller dialogs as needed. - *

    - * The application can customize the dialogs by providing a subclass of the - * dialog factory to the {@link MediaRouteButton} using the - * {@link MediaRouteButton#setDialogFactory setDialogFactory} method. - *

    - */ -public class MediaRouteDialogFactory { - private static final MediaRouteDialogFactory sDefault = new MediaRouteDialogFactory(); - - /** - * Creates a default media route dialog factory. - */ - public MediaRouteDialogFactory() { - } - - /** - * Gets the default factory instance. - * - * @return The default media route dialog factory, never null. - */ - @NonNull - public static MediaRouteDialogFactory getDefault() { - return sDefault; - } - - /** - * Called when the chooser dialog is being opened and it is time to create the fragment. - *

    - * Subclasses may override this method to create a customized fragment. - *

    - * - * @return The media route chooser dialog fragment, must not be null. - */ - @NonNull - public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() { - return new MediaRouteChooserDialogFragment(); - } - - /** - * Called when the controller dialog is being opened and it is time to create the fragment. - *

    - * Subclasses may override this method to create a customized fragment. - *

    - * - * @return The media route controller dialog fragment, must not be null. - */ - @NonNull - public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() { - return new MediaRouteControllerDialogFragment(); - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java deleted file mode 100644 index 9aabf7b40e..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -final class MediaRouteDialogHelper { - /** - * The framework should set the dialog width properly, but somehow it doesn't work, hence - * duplicating a similar logic here to determine the appropriate dialog width. - */ - public static int getDialogWidth(Context context) { - DisplayMetrics metrics = ApiHelper.getLibResources(context).getDisplayMetrics(); - boolean isPortrait = metrics.widthPixels < metrics.heightPixels; - - TypedValue value = new TypedValue(); - ApiHelper.getLibResources(context).getValue(isPortrait - ? R.dimen.mr_dialog_fixed_width_minor - : R.dimen.mr_dialog_fixed_width_major, value, true); - if (value.type == TypedValue.TYPE_DIMENSION) { - return (int) value.getDimension(metrics); - } else if (value.type == TypedValue.TYPE_FRACTION) { - return (int) value.getFraction(metrics.widthPixels, metrics.widthPixels); - } - return ViewGroup.LayoutParams.WRAP_CONTENT; - } - - /** - * Compares two lists regardless of order. - * - * @param list1 A list - * @param list2 A list to be compared with {@code list1} - * @return True if two lists have exactly same items regardless of order, false otherwise. - */ - public static boolean listUnorderedEquals(List list1, List list2) { - HashSet set1 = new HashSet<>(list1); - HashSet set2 = new HashSet<>(list2); - return set1.equals(set2); - } - - /** - * Compares two lists and returns a set of items which exist - * after-list but before-list, which means newly added items. - * - * @param before A list - * @param after A list to be compared with {@code before} - * @return A set of items which contains newly added items while - * comparing {@code after} to {@code before}. - */ - public static Set getItemsAdded(List before, List after) { - HashSet set = new HashSet<>(after); - set.removeAll(before); - return set; - } - - /** - * Compares two lists and returns a set of items which exist - * before-list but after-list, which means removed items. - * - * @param before A list - * @param after A list to be compared with {@code before} - * @return A set of items which contains removed items while - * comparing {@code after} to {@code before}. - */ - public static Set getItemsRemoved(List before, List after) { - HashSet set = new HashSet<>(before); - set.removeAll(after); - return set; - } - - /** - * Generates an item-Rect map which indicates where member - * items are located in the given ListView. - * - * @param listView A list view - * @param adapter An array adapter which contains an array of items. - * @return A map of items and bounds of their views located in the given list view. - */ - public static HashMap getItemBoundMap(ListView listView, - ArrayAdapter adapter) { - HashMap itemBoundMap = new HashMap<>(); - int firstVisiblePosition = listView.getFirstVisiblePosition(); - for (int i = 0; i < listView.getChildCount(); ++i) { - int position = firstVisiblePosition + i; - E item = adapter.getItem(position); - View view = listView.getChildAt(i); - itemBoundMap.put(item, - new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); - } - return itemBoundMap; - } - - /** - * Generates an item-BitmapDrawable map which stores snapshots - * of member items in the given ListView. - * - * @param context A context - * @param listView A list view - * @param adapter An array adapter which contains an array of items. - * @return A map of items and snapshots of their views in the given list view. - */ - public static HashMap getItemBitmapMap(Context context, - ListView listView, ArrayAdapter adapter) { - HashMap itemBitmapMap = new HashMap<>(); - int firstVisiblePosition = listView.getFirstVisiblePosition(); - for (int i = 0; i < listView.getChildCount(); ++i) { - int position = firstVisiblePosition + i; - E item = adapter.getItem(position); - View view = listView.getChildAt(i); - itemBitmapMap.put(item, getViewBitmap(context, view)); - } - return itemBitmapMap; - } - - private static BitmapDrawable getViewBitmap(Context context, View view) { - Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - view.draw(canvas); - return new BitmapDrawable(context.getResources(), bitmap); - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java deleted file mode 100644 index 52aecd880c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.os.Bundle; - -import androidx.fragment.app.Fragment; - -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -/** - * Media route discovery fragment. - *

    - * This fragment takes care of registering a callback for media route discovery - * during the {@link Fragment#onStart onStart()} phase - * and removing it during the {@link Fragment#onStop onStop()} phase. - *

    - * The application must supply a route selector to specify the kinds of routes - * to discover. The application may also override {@link #onCreateCallback} to - * provide the {@link MediaRouter} callback to register. - *

    - * Note that the discovery callback makes the application be connected with all the - * {@link androidx.mediarouter.media.MediaRouteProviderService media route provider services} - * while it is registered. - *

    - */ -public class MediaRouteDiscoveryFragment extends Fragment { - private final String ARGUMENT_SELECTOR = "selector"; - - private MediaRouter mRouter; - private MediaRouteSelector mSelector; - private MediaRouter.Callback mCallback; - - public MediaRouteDiscoveryFragment() { - } - - /** - * Gets the media router instance. - */ - public MediaRouter getMediaRouter() { - ensureRouter(); - return mRouter; - } - - private void ensureRouter() { - if (mRouter == null) { - mRouter = MediaRouter.getInstance(getContext()); - } - } - - /** - * Gets the media route selector for filtering the routes to be discovered. - * - * @return The selector, never null. - */ - public MediaRouteSelector getRouteSelector() { - ensureRouteSelector(); - return mSelector; - } - - /** - * Sets the media route selector for filtering the routes to be discovered. - * This method must be called before the fragment is added. - * - * @param selector The selector to set. - */ - public void setRouteSelector(MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - ensureRouteSelector(); - if (!mSelector.equals(selector)) { - mSelector = selector; - - Bundle args = getArguments(); - if (args == null) { - args = new Bundle(); - } - args.putBundle(ARGUMENT_SELECTOR, selector.asBundle()); - setArguments(args); - - if (mCallback != null) { - mRouter.removeCallback(mCallback); - mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags()); - } - } - } - - private void ensureRouteSelector() { - if (mSelector == null) { - Bundle args = getArguments(); - if (args != null) { - mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR)); - } - if (mSelector == null) { - mSelector = MediaRouteSelector.EMPTY; - } - } - } - - /** - * Called to create the {@link androidx.mediarouter.media.MediaRouter.Callback callback} - * that will be registered. - *

    - * The default callback does nothing. The application may override this method to - * supply its own callback. - *

    - * - * @return The new callback, or null if no callback should be registered. - */ - public MediaRouter.Callback onCreateCallback() { - return new MediaRouter.Callback() { }; - } - - /** - * Called to prepare the callback flags that will be used when the - * {@link androidx.mediarouter.media.MediaRouter.Callback callback} is registered. - *

    - * The default implementation returns {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}. - *

    - * - * @return The desired callback flags. - */ - public int onPrepareCallbackFlags() { - return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY; - } - - @Override - public void onStart() { - super.onStart(); - - ensureRouteSelector(); - ensureRouter(); - mCallback = onCreateCallback(); - if (mCallback != null) { - mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags()); - } - } - - @Override - public void onStop() { - if (mCallback != null) { - mRouter.removeCallback(mCallback); - mCallback = null; - } - - super.onStop(); - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java deleted file mode 100644 index dcca6a000c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.content.Context; -import android.graphics.ColorFilter; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.AnimationDrawable; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageButton; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; - -/** - * Chevron/Caret button to expand/collapse group volume list with animation. - */ -public class MediaRouteExpandCollapseButton extends ImageButton { - final AnimationDrawable mExpandAnimationDrawable; - final AnimationDrawable mCollapseAnimationDrawable; - final String mExpandGroupDescription; - final String mCollapseGroupDescription; - boolean mIsGroupExpanded; - OnClickListener mListener; - - public MediaRouteExpandCollapseButton(Context context) { - this(context, null); - } - - public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mExpandAnimationDrawable = (AnimationDrawable) - ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_expand); - mCollapseAnimationDrawable = (AnimationDrawable) - ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_collapse); - - ColorFilter filter = new PorterDuffColorFilter( - MediaRouterThemeHelper.getControllerColor(context, defStyleAttr), - PorterDuff.Mode.SRC_IN); - mExpandAnimationDrawable.setColorFilter(filter); - mCollapseAnimationDrawable.setColorFilter(filter); - - mExpandGroupDescription = - ApiHelper.getLibResources(context).getString(R.string.mr_controller_expand_group); - mCollapseGroupDescription = - ApiHelper.getLibResources(context).getString(R.string.mr_controller_collapse_group); - - setImageDrawable(mExpandAnimationDrawable.getFrame(0)); - setContentDescription(mExpandGroupDescription); - - super.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - mIsGroupExpanded = !mIsGroupExpanded; - if (mIsGroupExpanded) { - setImageDrawable(mExpandAnimationDrawable); - mExpandAnimationDrawable.start(); - setContentDescription(mCollapseGroupDescription); - } else { - setImageDrawable(mCollapseAnimationDrawable); - mCollapseAnimationDrawable.start(); - setContentDescription(mExpandGroupDescription); - } - if (mListener != null) { - mListener.onClick(view); - } - } - }); - } - - @Override - public void setOnClickListener(OnClickListener listener) { - mListener = listener; - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java deleted file mode 100644 index d05d20ed2e..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; -import android.widget.SeekBar; - -/** - * Volume slider with showing, hiding, and applying alpha supports to the thumb. - */ -public class MediaRouteVolumeSlider extends SeekBar { - private static final String TAG = "MediaRouteVolumeSlider"; - - private final float mDisabledAlpha; - - private boolean mHideThumb; - private Drawable mThumb; - private int mColor; - - public MediaRouteVolumeSlider(Context context) { - this(context, null); - } - - public MediaRouteVolumeSlider(Context context, AttributeSet attrs) { - super(context, attrs); - mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context); - } - - public MediaRouteVolumeSlider(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context); - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - int alpha = isEnabled() ? 0xFF : (int) (0xFF * mDisabledAlpha); - - // The thumb drawable is a collection of drawables and its current drawables are changed per - // state. Apply the color filter and alpha on every state change. - if (mThumb != null) { - mThumb.setColorFilter(mColor, PorterDuff.Mode.SRC_IN); - mThumb.setAlpha(alpha); - } - - getProgressDrawable().setColorFilter(mColor, PorterDuff.Mode.SRC_IN); - getProgressDrawable().setAlpha(alpha); - } - - @Override - public void setThumb(Drawable thumb) { - mThumb = thumb; - super.setThumb(mHideThumb ? null : mThumb); - } - - /** - * Sets whether to show or hide thumb. - */ - public void setHideThumb(boolean hideThumb) { - if (mHideThumb == hideThumb) { - return; - } - mHideThumb = hideThumb; - super.setThumb(mHideThumb ? null : mThumb); - } - - /** - * Sets the volume slider color. The change takes effect next time drawable state is changed. - *

    - * The color cannot be translucent, otherwise the underlying progress bar will be seen through - * the thumb. - *

    - */ - public void setColor(int color) { - if (mColor == color) { - return; - } - if (Color.alpha(color) != 0xFF) { - Log.e(TAG, "Volume slider color cannot be translucent: #" + Integer.toHexString(color)); - } - mColor = color; - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java deleted file mode 100644 index b4bf8d1090..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.util.TypedValue; -import android.view.ContextThemeWrapper; -import android.view.View; - -import androidx.annotation.IntDef; -import androidx.core.graphics.ColorUtils; - -import com.android.media.update.R; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -final class MediaRouterThemeHelper { - private static final float MIN_CONTRAST = 3.0f; - - @IntDef({COLOR_DARK_ON_LIGHT_BACKGROUND, COLOR_WHITE_ON_DARK_BACKGROUND}) - @Retention(RetentionPolicy.SOURCE) - private @interface ControllerColorType {} - - static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0xDE000000; /* Opacity of 87% */ - static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE; - - private MediaRouterThemeHelper() { - } - - static Context createThemedButtonContext(Context context) { - // Apply base Media Router theme. - context = new ContextThemeWrapper(context, getRouterThemeId(context)); - - // Apply custom Media Router theme. - int style = getThemeResource(context, R.attr.mediaRouteTheme); - if (style != 0) { - context = new ContextThemeWrapper(context, style); - } - - return context; - } - - /* - * The following two methods are to be used in conjunction. They should be used to prepare - * the context and theme for a super class constructor (the latter method relies on the - * former method to properly prepare the context): - * super(context = createThemedDialogContext(context, theme), - * createThemedDialogStyle(context)); - * - * It will apply theme in the following order (style lookups will be done in reverse): - * 1) Current theme - * 2) Supplied theme - * 3) Base Media Router theme - * 4) Custom Media Router theme, if provided - */ - static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) { - // 1) Current theme is already applied to the context - - // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme) - if (theme == 0) { - theme = getThemeResource(context, - !alertDialog ? android.R.attr.dialogTheme : android.R.attr.alertDialogTheme); - } - // Apply it - context = new ContextThemeWrapper(context, theme); - - // 3) If a custom Media Router theme is provided then apply the base theme - if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) { - context = new ContextThemeWrapper(context, getRouterThemeId(context)); - } - - return context; - } - // This method should be used in conjunction with the previous method. - static int createThemedDialogStyle(Context context) { - // 4) Apply the custom Media Router theme - int theme = getThemeResource(context, R.attr.mediaRouteTheme); - if (theme == 0) { - // 3) No custom MediaRouter theme was provided so apply the base theme instead - theme = getRouterThemeId(context); - } - - return theme; - } - // END. Previous two methods should be used in conjunction. - - static int getThemeResource(Context context, int attr) { - TypedValue value = new TypedValue(); - return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0; - } - - static float getDisabledAlpha(Context context) { - TypedValue value = new TypedValue(); - return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true) - ? value.getFloat() : 0.5f; - } - - static @ControllerColorType int getControllerColor(Context context, int style) { - int primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary); - if (primaryColor == 0) { - primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary); - if (primaryColor == 0) { - primaryColor = 0xFF000000; - } - } - if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor) - >= MIN_CONTRAST) { - return COLOR_WHITE_ON_DARK_BACKGROUND; - } - return COLOR_DARK_ON_LIGHT_BACKGROUND; - } - - static int getButtonTextColor(Context context) { - int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary); - int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground); - - if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) { - // Default to colorAccent if the contrast ratio is low. - return getThemeColor(context, 0, android.R.attr.colorAccent); - } - return primaryColor; - } - - static void setMediaControlsBackgroundColor( - Context context, View mainControls, View groupControls, boolean hasGroup) { - int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary); - int primaryDarkColor = getThemeColor(context, 0, android.R.attr.colorPrimaryDark); - if (hasGroup && getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) { - // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model - // the white dialog and use the primary color for the group controls. - primaryDarkColor = primaryColor; - primaryColor = Color.WHITE; - } - mainControls.setBackgroundColor(primaryColor); - groupControls.setBackgroundColor(primaryDarkColor); - // Also store the background colors to the view tags. They are used in - // setVolumeSliderColor() below. - mainControls.setTag(primaryColor); - groupControls.setTag(primaryDarkColor); - } - - static void setVolumeSliderColor( - Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) { - int controllerColor = getControllerColor(context, 0); - if (Color.alpha(controllerColor) != 0xFF) { - // Composite with the background in order not to show the underlying progress bar - // through the thumb. - int backgroundColor = (int) backgroundView.getTag(); - controllerColor = ColorUtils.compositeColors(controllerColor, backgroundColor); - } - volumeSlider.setColor(controllerColor); - } - - private static boolean isLightTheme(Context context) { - TypedValue value = new TypedValue(); - // TODO(sungsoo): Switch to com.android.internal.R.attr.isLightTheme - return context.getTheme().resolveAttribute(androidx.appcompat.R.attr.isLightTheme, - value, true) && value.data != 0; - } - - private static int getThemeColor(Context context, int style, int attr) { - if (style != 0) { - int[] attrs = { attr }; - TypedArray ta = context.obtainStyledAttributes(style, attrs); - int color = ta.getColor(0, 0); - ta.recycle(); - if (color != 0) { - return color; - } - } - TypedValue value = new TypedValue(); - context.getTheme().resolveAttribute(attr, value, true); - if (value.resourceId != 0) { - return context.getResources().getColor(value.resourceId); - } - return value.data; - } - - static int getRouterThemeId(Context context) { - int themeId; - if (isLightTheme(context)) { - if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) { - themeId = R.style.Theme_MediaRouter_Light; - } else { - themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel; - } - } else { - if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) { - themeId = R.style.Theme_MediaRouter_LightControlPanel; - } else { - themeId = R.style.Theme_MediaRouter; - } - } - return themeId; - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java deleted file mode 100644 index b00dee228b..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.app; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.util.AttributeSet; -import android.view.animation.Interpolator; -import android.widget.ListView; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * A ListView which has an additional overlay layer. {@link BitmapDrawable} - * can be added to the layer and can be animated. - */ -public final class OverlayListView extends ListView { - private final List mOverlayObjects = new ArrayList<>(); - - public OverlayListView(Context context) { - super(context); - } - - public OverlayListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public OverlayListView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - /** - * Adds an object to the overlay layer. - * - * @param object An object to be added. - */ - public void addOverlayObject(OverlayObject object) { - mOverlayObjects.add(object); - } - - /** - * Starts all animations of objects in the overlay layer. - */ - public void startAnimationAll() { - for (OverlayObject object : mOverlayObjects) { - if (!object.isAnimationStarted()) { - object.startAnimation(getDrawingTime()); - } - } - } - - /** - * Stops all animations of objects in the overlay layer. - */ - public void stopAnimationAll() { - for (OverlayObject object : mOverlayObjects) { - object.stopAnimation(); - } - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mOverlayObjects.size() > 0) { - Iterator it = mOverlayObjects.iterator(); - while (it.hasNext()) { - OverlayObject object = it.next(); - BitmapDrawable bitmap = object.getBitmapDrawable(); - if (bitmap != null) { - bitmap.draw(canvas); - } - if (!object.update(getDrawingTime())) { - it.remove(); - } - } - } - } - - /** - * A class that represents an object to be shown in the overlay layer. - */ - public static class OverlayObject { - private BitmapDrawable mBitmap; - private float mCurrentAlpha = 1.0f; - private Rect mCurrentBounds; - private Interpolator mInterpolator; - private long mDuration; - private Rect mStartRect; - private int mDeltaY; - private float mStartAlpha = 1.0f; - private float mEndAlpha = 1.0f; - private long mStartTime; - private boolean mIsAnimationStarted; - private boolean mIsAnimationEnded; - private OnAnimationEndListener mListener; - - public OverlayObject(BitmapDrawable bitmap, Rect startRect) { - mBitmap = bitmap; - mStartRect = startRect; - mCurrentBounds = new Rect(startRect); - if (mBitmap != null && mCurrentBounds != null) { - mBitmap.setAlpha((int) (mCurrentAlpha * 255)); - mBitmap.setBounds(mCurrentBounds); - } - } - - /** - * Returns the bitmap that this object represents. - * - * @return BitmapDrawable that this object has. - */ - public BitmapDrawable getBitmapDrawable() { - return mBitmap; - } - - /** - * Returns the started status of the animation. - * - * @return True if the animation has started, false otherwise. - */ - public boolean isAnimationStarted() { - return mIsAnimationStarted; - } - - /** - * Sets animation for varying alpha. - * - * @param startAlpha Starting alpha value for the animation, where 1.0 means - * fully opaque and 0.0 means fully transparent. - * @param endAlpha Ending alpha value for the animation. - * @return This OverlayObject to allow for chaining of calls. - */ - public OverlayObject setAlphaAnimation(float startAlpha, float endAlpha) { - mStartAlpha = startAlpha; - mEndAlpha = endAlpha; - return this; - } - - /** - * Sets animation for moving objects vertically. - * - * @param deltaY Distance to move in pixels. - * @return This OverlayObject to allow for chaining of calls. - */ - public OverlayObject setTranslateYAnimation(int deltaY) { - mDeltaY = deltaY; - return this; - } - - /** - * Sets how long the animation will last. - * - * @param duration Duration in milliseconds - * @return This OverlayObject to allow for chaining of calls. - */ - public OverlayObject setDuration(long duration) { - mDuration = duration; - return this; - } - - /** - * Sets the acceleration curve for this animation. - * - * @param interpolator The interpolator which defines the acceleration curve - * @return This OverlayObject to allow for chaining of calls. - */ - public OverlayObject setInterpolator(Interpolator interpolator) { - mInterpolator = interpolator; - return this; - } - - /** - * Binds an animation end listener to the animation. - * - * @param listener the animation end listener to be notified. - * @return This OverlayObject to allow for chaining of calls. - */ - public OverlayObject setAnimationEndListener(OnAnimationEndListener listener) { - mListener = listener; - return this; - } - - /** - * Starts the animation and sets the start time. - * - * @param startTime Start time to be set in Millis - */ - public void startAnimation(long startTime) { - mStartTime = startTime; - mIsAnimationStarted = true; - } - - /** - * Stops the animation. - */ - public void stopAnimation() { - mIsAnimationStarted = true; - mIsAnimationEnded = true; - if (mListener != null) { - mListener.onAnimationEnd(); - } - } - - /** - * Calculates and updates current bounds and alpha value. - * - * @param currentTime Current time.in millis - */ - public boolean update(long currentTime) { - if (mIsAnimationEnded) { - return false; - } - float normalizedTime = (currentTime - mStartTime) / (float) mDuration; - normalizedTime = Math.max(0.0f, Math.min(1.0f, normalizedTime)); - if (!mIsAnimationStarted) { - normalizedTime = 0.0f; - } - float interpolatedTime = (mInterpolator == null) ? normalizedTime - : mInterpolator.getInterpolation(normalizedTime); - int deltaY = (int) (mDeltaY * interpolatedTime); - mCurrentBounds.top = mStartRect.top + deltaY; - mCurrentBounds.bottom = mStartRect.bottom + deltaY; - mCurrentAlpha = mStartAlpha + (mEndAlpha - mStartAlpha) * interpolatedTime; - if (mBitmap != null && mCurrentBounds != null) { - mBitmap.setAlpha((int) (mCurrentAlpha * 255)); - mBitmap.setBounds(mCurrentBounds); - } - if (mIsAnimationStarted && normalizedTime >= 1.0f) { - mIsAnimationEnded = true; - if (mListener != null) { - mListener.onAnimationEnd(); - } - } - return !mIsAnimationEnded; - } - - /** - * An animation listener that receives notifications when the animation ends. - */ - public interface OnAnimationEndListener { - /** - * Notifies the end of the animation. - */ - public void onAnimationEnd(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java deleted file mode 100644 index 5a0bc95442..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.os.Build; -import android.os.Handler; -import android.util.Log; -import android.view.Display; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -// @@RequiresApi(17) -final class MediaRouterJellybeanMr1 { - private static final String TAG = "MediaRouterJellybeanMr1"; - - public static Object createCallback(Callback callback) { - return new CallbackProxy(callback); - } - - public static final class RouteInfo { - public static boolean isEnabled(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled(); - } - - public static Display getPresentationDisplay(Object routeObj) { - // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was - // added in API 17. However, some factory releases of JB MR1 missed it. - try { - return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay(); - } catch (NoSuchMethodError ex) { - Log.w(TAG, "Cannot get presentation display for the route.", ex); - } - return null; - } - } - - public static interface Callback extends MediaRouterJellybean.Callback { - public void onRoutePresentationDisplayChanged(Object routeObj); - } - - /** - * Workaround the fact that the version of MediaRouter.addCallback() that accepts a - * flag to perform an active scan does not exist in JB MR1 so we need to force - * wifi display scans directly through the DisplayManager. - * Do not use on JB MR2 and above. - */ - public static final class ActiveScanWorkaround implements Runnable { - // Time between wifi display scans when actively scanning in milliseconds. - private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000; - - private final DisplayManager mDisplayManager; - private final Handler mHandler; - private Method mScanWifiDisplaysMethod; - - private boolean mActivelyScanningWifiDisplays; - - public ActiveScanWorkaround(Context context, Handler handler) { - if (Build.VERSION.SDK_INT != 17) { - throw new UnsupportedOperationException(); - } - - mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); - mHandler = handler; - try { - mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays"); - } catch (NoSuchMethodException ex) { - } - } - - public void setActiveScanRouteTypes(int routeTypes) { - // On JB MR1, there is no API to scan wifi display routes. - // Instead we must make a direct call into the DisplayManager to scan - // wifi displays on this version but only when live video routes are requested. - // See also the JellybeanMr2Impl implementation of this method. - // This was fixed in JB MR2 by adding a new overload of addCallback() to - // enable active scanning on request. - if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) { - if (!mActivelyScanningWifiDisplays) { - if (mScanWifiDisplaysMethod != null) { - mActivelyScanningWifiDisplays = true; - mHandler.post(this); - } else { - Log.w(TAG, "Cannot scan for wifi displays because the " - + "DisplayManager.scanWifiDisplays() method is " - + "not available on this device."); - } - } - } else { - if (mActivelyScanningWifiDisplays) { - mActivelyScanningWifiDisplays = false; - mHandler.removeCallbacks(this); - } - } - } - - @Override - public void run() { - if (mActivelyScanningWifiDisplays) { - try { - mScanWifiDisplaysMethod.invoke(mDisplayManager); - } catch (IllegalAccessException ex) { - Log.w(TAG, "Cannot scan for wifi displays.", ex); - } catch (InvocationTargetException ex) { - Log.w(TAG, "Cannot scan for wifi displays.", ex); - } - mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL); - } - } - } - - /** - * Workaround the fact that the isConnecting() method does not exist in JB MR1. - * Do not use on JB MR2 and above. - */ - public static final class IsConnectingWorkaround { - private Method mGetStatusCodeMethod; - private int mStatusConnecting; - - public IsConnectingWorkaround() { - if (Build.VERSION.SDK_INT != 17) { - throw new UnsupportedOperationException(); - } - - try { - Field statusConnectingField = - android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING"); - mStatusConnecting = statusConnectingField.getInt(null); - mGetStatusCodeMethod = - android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode"); - } catch (NoSuchFieldException ex) { - } catch (NoSuchMethodException ex) { - } catch (IllegalAccessException ex) { - } - } - - public boolean isConnecting(Object routeObj) { - android.media.MediaRouter.RouteInfo route = - (android.media.MediaRouter.RouteInfo)routeObj; - - if (mGetStatusCodeMethod != null) { - try { - int statusCode = (Integer)mGetStatusCodeMethod.invoke(route); - return statusCode == mStatusConnecting; - } catch (IllegalAccessException ex) { - } catch (InvocationTargetException ex) { - } - } - - // Assume not connecting. - return false; - } - } - - static class CallbackProxy - extends MediaRouterJellybean.CallbackProxy { - public CallbackProxy(T callback) { - super(callback); - } - - @Override - public void onRoutePresentationDisplayChanged(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route) { - mCallback.onRoutePresentationDisplayChanged(route); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java deleted file mode 100644 index 110354952d..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -// @@RequiresApi(18) -final class MediaRouterJellybeanMr2 { - public static Object getDefaultRoute(Object routerObj) { - return ((android.media.MediaRouter)routerObj).getDefaultRoute(); - } - - public static void addCallback(Object routerObj, int types, Object callbackObj, int flags) { - ((android.media.MediaRouter)routerObj).addCallback(types, - (android.media.MediaRouter.Callback)callbackObj, flags); - } - - public static final class RouteInfo { - public static CharSequence getDescription(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getDescription(); - } - - public static boolean isConnecting(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting(); - } - } - - public static final class UserRouteInfo { - public static void setDescription(Object routeObj, CharSequence description) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java deleted file mode 100644 index 0bb59b8949..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.media.AudioManager; -import android.os.Build; -import android.util.Log; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -// @@RequiresApi(16) -final class MediaRouterJellybean { - private static final String TAG = "MediaRouterJellybean"; - - // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP = 0x80; - // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100; - // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200; - public static final int DEVICE_OUT_BLUETOOTH = 0x80 | 0x100 | 0x200; - - public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1; - public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2; - public static final int ROUTE_TYPE_USER = 0x00800000; - - public static final int ALL_ROUTE_TYPES = - MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO - | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO - | MediaRouterJellybean.ROUTE_TYPE_USER; - - public static Object getMediaRouter(Context context) { - return context.getSystemService(Context.MEDIA_ROUTER_SERVICE); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getRoutes(Object routerObj) { - final android.media.MediaRouter router = (android.media.MediaRouter)routerObj; - final int count = router.getRouteCount(); - List out = new ArrayList(count); - for (int i = 0; i < count; i++) { - out.add(router.getRouteAt(i)); - } - return out; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getCategories(Object routerObj) { - final android.media.MediaRouter router = (android.media.MediaRouter)routerObj; - final int count = router.getCategoryCount(); - List out = new ArrayList(count); - for (int i = 0; i < count; i++) { - out.add(router.getCategoryAt(i)); - } - return out; - } - - public static Object getSelectedRoute(Object routerObj, int type) { - return ((android.media.MediaRouter)routerObj).getSelectedRoute(type); - } - - public static void selectRoute(Object routerObj, int types, Object routeObj) { - ((android.media.MediaRouter)routerObj).selectRoute(types, - (android.media.MediaRouter.RouteInfo)routeObj); - } - - public static void addCallback(Object routerObj, int types, Object callbackObj) { - ((android.media.MediaRouter)routerObj).addCallback(types, - (android.media.MediaRouter.Callback)callbackObj); - } - - public static void removeCallback(Object routerObj, Object callbackObj) { - ((android.media.MediaRouter)routerObj).removeCallback( - (android.media.MediaRouter.Callback)callbackObj); - } - - public static Object createRouteCategory(Object routerObj, - String name, boolean isGroupable) { - return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable); - } - - public static Object createUserRoute(Object routerObj, Object categoryObj) { - return ((android.media.MediaRouter)routerObj).createUserRoute( - (android.media.MediaRouter.RouteCategory)categoryObj); - } - - public static void addUserRoute(Object routerObj, Object routeObj) { - ((android.media.MediaRouter)routerObj).addUserRoute( - (android.media.MediaRouter.UserRouteInfo)routeObj); - } - - public static void removeUserRoute(Object routerObj, Object routeObj) { - ((android.media.MediaRouter)routerObj).removeUserRoute( - (android.media.MediaRouter.UserRouteInfo)routeObj); - } - - public static Object createCallback(Callback callback) { - return new CallbackProxy(callback); - } - - public static Object createVolumeCallback(VolumeCallback callback) { - return new VolumeCallbackProxy(callback); - } - - static boolean checkRoutedToBluetooth(Context context) { - try { - AudioManager audioManager = (AudioManager) context.getSystemService( - Context.AUDIO_SERVICE); - Method method = audioManager.getClass().getDeclaredMethod( - "getDevicesForStream", int.class); - int device = (Integer) method.invoke(audioManager, AudioManager.STREAM_MUSIC); - return (device & DEVICE_OUT_BLUETOOTH) != 0; - } catch (Exception e) { - return false; - } - } - - public static final class RouteInfo { - public static CharSequence getName(Object routeObj, Context context) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context); - } - - public static CharSequence getStatus(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus(); - } - - public static int getSupportedTypes(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes(); - } - - public static Object getCategory(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory(); - } - - public static Drawable getIconDrawable(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable(); - } - - public static int getPlaybackType(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType(); - } - - public static int getPlaybackStream(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream(); - } - - public static int getVolume(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume(); - } - - public static int getVolumeMax(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax(); - } - - public static int getVolumeHandling(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling(); - } - - public static Object getTag(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getTag(); - } - - public static void setTag(Object routeObj, Object tag) { - ((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag); - } - - public static void requestSetVolume(Object routeObj, int volume) { - ((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume); - } - - public static void requestUpdateVolume(Object routeObj, int direction) { - ((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction); - } - - public static Object getGroup(Object routeObj) { - return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup(); - } - - public static boolean isGroup(Object routeObj) { - return routeObj instanceof android.media.MediaRouter.RouteGroup; - } - } - - public static final class RouteGroup { - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getGroupedRoutes(Object groupObj) { - final android.media.MediaRouter.RouteGroup group = - (android.media.MediaRouter.RouteGroup)groupObj; - final int count = group.getRouteCount(); - List out = new ArrayList(count); - for (int i = 0; i < count; i++) { - out.add(group.getRouteAt(i)); - } - return out; - } - } - - public static final class UserRouteInfo { - public static void setName(Object routeObj, CharSequence name) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name); - } - - public static void setStatus(Object routeObj, CharSequence status) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status); - } - - public static void setIconDrawable(Object routeObj, Drawable icon) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon); - } - - public static void setPlaybackType(Object routeObj, int type) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type); - } - - public static void setPlaybackStream(Object routeObj, int stream) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream); - } - - public static void setVolume(Object routeObj, int volume) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume); - } - - public static void setVolumeMax(Object routeObj, int volumeMax) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax); - } - - public static void setVolumeHandling(Object routeObj, int volumeHandling) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling); - } - - public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback( - (android.media.MediaRouter.VolumeCallback)volumeCallbackObj); - } - - public static void setRemoteControlClient(Object routeObj, Object rccObj) { - ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient( - (android.media.RemoteControlClient)rccObj); - } - } - - public static final class RouteCategory { - public static CharSequence getName(Object categoryObj, Context context) { - return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List getRoutes(Object categoryObj) { - ArrayList out = new ArrayList(); - ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out); - return out; - } - - public static int getSupportedTypes(Object categoryObj) { - return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes(); - } - - public static boolean isGroupable(Object categoryObj) { - return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable(); - } - } - - public static interface Callback { - public void onRouteSelected(int type, Object routeObj); - public void onRouteUnselected(int type, Object routeObj); - public void onRouteAdded(Object routeObj); - public void onRouteRemoved(Object routeObj); - public void onRouteChanged(Object routeObj); - public void onRouteGrouped(Object routeObj, Object groupObj, int index); - public void onRouteUngrouped(Object routeObj, Object groupObj); - public void onRouteVolumeChanged(Object routeObj); - } - - public static interface VolumeCallback { - public void onVolumeSetRequest(Object routeObj, int volume); - public void onVolumeUpdateRequest(Object routeObj, int direction); - } - - /** - * Workaround for limitations of selectRoute() on JB and JB MR1. - * Do not use on JB MR2 and above. - */ - public static final class SelectRouteWorkaround { - private Method mSelectRouteIntMethod; - - public SelectRouteWorkaround() { - if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { - throw new UnsupportedOperationException(); - } - try { - mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod( - "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class); - } catch (NoSuchMethodException ex) { - } - } - - public void selectRoute(Object routerObj, int types, Object routeObj) { - android.media.MediaRouter router = (android.media.MediaRouter)routerObj; - android.media.MediaRouter.RouteInfo route = - (android.media.MediaRouter.RouteInfo)routeObj; - - int routeTypes = route.getSupportedTypes(); - if ((routeTypes & ROUTE_TYPE_USER) == 0) { - // Handle non-user routes. - // On JB and JB MR1, the selectRoute() API only supports programmatically - // selecting user routes. So instead we rely on the hidden selectRouteInt() - // method on these versions of the platform. - // This limitation was removed in JB MR2. - if (mSelectRouteIntMethod != null) { - try { - mSelectRouteIntMethod.invoke(router, types, route); - return; // success! - } catch (IllegalAccessException ex) { - Log.w(TAG, "Cannot programmatically select non-user route. " - + "Media routing may not work.", ex); - } catch (InvocationTargetException ex) { - Log.w(TAG, "Cannot programmatically select non-user route. " - + "Media routing may not work.", ex); - } - } else { - Log.w(TAG, "Cannot programmatically select non-user route " - + "because the platform is missing the selectRouteInt() " - + "method. Media routing may not work."); - } - } - - // Default handling. - router.selectRoute(types, route); - } - } - - /** - * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1. - * Do not use on JB MR2 and above. - */ - public static final class GetDefaultRouteWorkaround { - private Method mGetSystemAudioRouteMethod; - - public GetDefaultRouteWorkaround() { - if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { - throw new UnsupportedOperationException(); - } - try { - mGetSystemAudioRouteMethod = - android.media.MediaRouter.class.getMethod("getSystemAudioRoute"); - } catch (NoSuchMethodException ex) { - } - } - - public Object getDefaultRoute(Object routerObj) { - android.media.MediaRouter router = (android.media.MediaRouter)routerObj; - - if (mGetSystemAudioRouteMethod != null) { - try { - return mGetSystemAudioRouteMethod.invoke(router); - } catch (IllegalAccessException ex) { - } catch (InvocationTargetException ex) { - } - } - - // Could not find the method or it does not work. - // Return the first route and hope for the best. - return router.getRouteAt(0); - } - } - - static class CallbackProxy - extends android.media.MediaRouter.Callback { - protected final T mCallback; - - public CallbackProxy(T callback) { - mCallback = callback; - } - - @Override - public void onRouteSelected(android.media.MediaRouter router, - int type, android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteSelected(type, route); - } - - @Override - public void onRouteUnselected(android.media.MediaRouter router, - int type, android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteUnselected(type, route); - } - - @Override - public void onRouteAdded(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteAdded(route); - } - - @Override - public void onRouteRemoved(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteRemoved(route); - } - - @Override - public void onRouteChanged(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteChanged(route); - } - - @Override - public void onRouteGrouped(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route, - android.media.MediaRouter.RouteGroup group, int index) { - mCallback.onRouteGrouped(route, group, index); - } - - @Override - public void onRouteUngrouped(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route, - android.media.MediaRouter.RouteGroup group) { - mCallback.onRouteUngrouped(route, group); - } - - @Override - public void onRouteVolumeChanged(android.media.MediaRouter router, - android.media.MediaRouter.RouteInfo route) { - mCallback.onRouteVolumeChanged(route); - } - } - - static class VolumeCallbackProxy - extends android.media.MediaRouter.VolumeCallback { - protected final T mCallback; - - public VolumeCallbackProxy(T callback) { - mCallback = callback; - } - - @Override - public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route, - int volume) { - mCallback.onVolumeSetRequest(route, volume); - } - - @Override - public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route, - int direction) { - mCallback.onVolumeUpdateRequest(route, direction); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java deleted file mode 100644 index 1d9e7774b4..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java +++ /dev/null @@ -1,1228 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.app.PendingIntent; -import android.content.Intent; -import android.net.Uri; - -/** - * Constants for media control intents. - *

    - * This class declares a set of standard media control intent categories and actions that - * applications can use to identify the capabilities of media routes and control them. - *

    - * - *

    Media control intent categories

    - *

    - * Media control intent categories specify means by which applications can - * send media to the destination of a media route. Categories are sometimes referred - * to as describing "types" or "kinds" of routes. - *

    - * For example, if a route supports the {@link #CATEGORY_REMOTE_PLAYBACK remote playback category}, - * then an application can ask it to play media remotely by sending a - * {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} intent with the Uri of the - * media content to play. Such a route may then be referred to as - * a "remote playback route" because it supports remote playback requests. It is common - * for a route to support multiple categories of requests at the same time, such as - * live audio and live video. - *

    - * The following standard route categories are defined. - *

      - *
    • {@link #CATEGORY_LIVE_AUDIO Live audio}: The route supports streaming live audio - * from the device to the destination. Live audio routes include local speakers - * and Bluetooth headsets. - *
    • {@link #CATEGORY_LIVE_VIDEO Live video}: The route supports streaming live video - * from the device to the destination. Live video routes include local displays - * and wireless displays that support mirroring and - * {@link android.app.Presentation presentations}. Live video routes typically also - * support live audio capabilities. - *
    • {@link #CATEGORY_REMOTE_PLAYBACK Remote playback}: The route supports sending - * remote playback requests for media content to the destination. The content to be - * played is identified by a Uri and mime-type. - *

    - * Media route providers may define custom media control intent categories of their own in - * addition to the standard ones. Custom categories can be used to provide a variety - * of features to applications that recognize and know how to use them. For example, - * a media route provider might define a custom category to indicate that its routes - * support a special device-specific control interface in addition to other - * standard features. - *

    - * Applications can determine which categories a route supports by using the - * {@link MediaRouter.RouteInfo#supportsControlCategory MediaRouter.RouteInfo.supportsControlCategory} - * or {@link MediaRouter.RouteInfo#getControlFilters MediaRouter.RouteInfo.getControlFilters} - * methods. Applications can also specify the types of routes that they want to use by - * creating {@link MediaRouteSelector media route selectors} that contain the desired - * categories and are used to filter routes in several parts of the media router API. - *

    - * - *

    Media control intent actions

    - *

    - * Media control intent actions specify particular functions that applications - * can ask the destination of a media route to perform. Media route control requests - * take the form of intents in a similar manner to other intents used to start activities - * or send broadcasts. The difference is that media control intents are directed to - * routes rather than activity or broadcast receiver components. - *

    - * Each media route control intent specifies an action, a category and some number of parameters - * that are supplied as extras. Applications send media control requests to routes using the - * {@link MediaRouter.RouteInfo#sendControlRequest MediaRouter.RouteInfo.sendControlRequest} - * method and receive results via a callback. - *

    - * All media control intent actions are associated with the media control intent categories - * that support them. Thus only remote playback routes may perform remote playback actions. - * The documentation of each action specifies the category to which the action belongs, - * the parameters it requires, and the results it returns. - *

    - * - *

    Live audio and live video routes

    - *

    - * {@link #CATEGORY_LIVE_AUDIO Live audio} and {@link #CATEGORY_LIVE_VIDEO live video} - * routes present media using standard system interfaces such as audio streams, - * {@link android.app.Presentation presentations} or display mirroring. These routes are - * the easiest to use because applications simply render content locally on the device - * and the system streams it to the route destination automatically. - *

    - * In most cases, applications can stream content to live audio and live video routes in - * the same way they would play the content locally without any modification. However, - * applications may also be able to take advantage of more sophisticated features such - * as second-screen presentation APIs that are particular to these routes. - *

    - * - *

    Remote playback routes

    - *

    - * {@link #CATEGORY_REMOTE_PLAYBACK Remote playback} routes present media remotely - * by playing content from a Uri. - * These routes destinations take responsibility for fetching and rendering content - * on their own. Applications do not render the content themselves; instead, applications - * send control requests to initiate play, pause, resume, or stop media items and receive - * status updates as they change state. - *

    - * - *

    Sessions

    - *

    - * Each remote media playback action is conducted within the scope of a session. - * Sessions are used to prevent applications from accidentally interfering with one - * another because at most one session can be valid at a time. - *

    - * A session can be created using the {@link #ACTION_START_SESSION start session action} - * and terminated using the {@link #ACTION_END_SESSION end session action} when the - * route provides explicit session management features. - *

    - * Explicit session management was added in a later revision of the protocol so not - * all routes support it. If the route does not support explicit session management - * then implicit session management may still be used. Implicit session management - * relies on the use of the {@link #ACTION_PLAY play} and {@link #ACTION_ENQUEUE enqueue} - * actions which have the side-effect of creating a new session if none is provided - * as argument. - *

    - * When a new session is created, the previous session is invalidated and any ongoing - * media playback is stopped before the requested action is performed. Any attempt - * to use an invalidated session will result in an error. (Protocol implementations - * are encouraged to aggressively discard information associated with invalidated sessions - * since it is no longer of use.) - *

    - * Each session is identified by a unique session id that may be used to control - * the session using actions such as pause, resume, stop and end session. - *

    - * - *

    Media items

    - *

    - * Each successful {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} action - * returns a unique media item id that an application can use to monitor and control - * playback. The media item id may be passed to other actions such as - * {@link #ACTION_SEEK seek} or {@link #ACTION_GET_STATUS get status}. It will also appear - * as a parameter in status update broadcasts to identify the associated playback request. - *

    - * Each media item is scoped to the session in which it was created. Therefore media item - * ids are only ever used together with session ids. Media item ids are meaningless - * on their own. When the session is invalidated, all of its media items are also - * invalidated. - *

    - * - *

    The playback queue

    - *

    - * Each session has its own playback queue that consists of the media items that - * are pending, playing, buffering or paused. Items are added to the queue when - * a playback request is issued. Items are removed from the queue when they are no - * longer eligible for playback (enter terminal states). - *

    - * As described in the {@link MediaItemStatus} class, media items initially - * start in a pending state, transition to the playing (or buffering or paused) state - * during playback, and end in a finished, canceled, invalidated or error state. - * Once the current item enters a terminal state, playback proceeds on to the - * next item. - *

    - * The application should determine whether the route supports queuing by checking - * whether the {@link #ACTION_ENQUEUE} action is declared in the route's control filter - * using {@link MediaRouter.RouteInfo#supportsControlRequest RouteInfo.supportsControlRequest}. - *

    - * If the {@link #ACTION_ENQUEUE} action is supported by the route, then the route promises - * to allow at least two items (possibly more) to be enqueued at a time. Enqueued items play - * back to back one after the other as the previous item completes. Ideally there should - * be no audible pause between items for standard audio content types. - *

    - * If the {@link #ACTION_ENQUEUE} action is not supported by the route, then the queue - * effectively contains at most one item at a time. Each play action has the effect of - * clearing the queue and resetting its state before the next item is played. - *

    - * - *

    Impact of pause, resume, stop and play actions on the playback queue

    - *

    - * The pause, resume and stop actions affect the session's whole queue. Pause causes - * the playback queue to be suspended no matter which item is currently playing. - * Resume reverses the effects of pause. Stop clears the queue and also resets - * the pause flag just like resume. - *

    - * As described earlier, the play action has the effect of clearing the queue - * and completely resetting its state (like the stop action) then enqueuing a - * new media item to be played immediately. Play is therefore equivalent - * to stop followed by an action to enqueue an item. - *

    - * The play action is also special in that it can be used to create new sessions. - * An application with simple needs may find that it only needs to use play - * (and occasionally stop) to control playback. - *

    - * - *

    Resolving conflicts between applications

    - *

    - * When an application has a valid session, it is essentially in control of remote playback - * on the route. No other application can view or modify the remote playback state - * of that application's session without knowing its id. - *

    - * However, other applications can perform actions that have the effect of stopping - * playback and invalidating the current session. When this occurs, the former application - * will be informed that it has lost control by way of individual media item status - * update broadcasts that indicate that its queued media items have become - * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated}. This broadcast - * implies that playback was terminated abnormally by an external cause. - *

    - * Applications should handle conflicts conservatively to allow other applications to - * smoothly assume control over the route. When a conflict occurs, the currently playing - * application should release its session and allow the new application to use the - * route until such time as the user intervenes to take over the route again and begin - * a new playback session. - *

    - * - *

    Basic actions

    - *

    - * The following basic actions must be supported (all or nothing) by all remote - * playback routes. These actions form the basis of the remote playback protocol - * and are required in all implementations. - *

      - *
    • {@link #ACTION_PLAY Play}: Starts playing content specified by a given Uri - * and returns a new media item id to describe the request. Implicitly creates a new - * session if no session id was specified as a parameter. - *
    • {@link #ACTION_SEEK Seek}: Sets the content playback position of a specific media item. - *
    • {@link #ACTION_GET_STATUS Get status}: Gets the status of a media item - * including the item's current playback position and progress. - *
    • {@link #ACTION_PAUSE Pause}: Pauses playback of the queue. - *
    • {@link #ACTION_RESUME Resume}: Resumes playback of the queue. - *
    • {@link #ACTION_STOP Stop}: Stops playback, clears the queue, and resets the - * pause state. - *
    - * - *

    Queue actions

    - *

    - * The following queue actions must be supported (all or nothing) by remote - * playback routes that offer optional queuing capabilities. - *

      - *
    • {@link #ACTION_ENQUEUE Enqueue}: Enqueues content specified by a given Uri - * and returns a new media item id to describe the request. Implicitly creates a new - * session if no session id was specified as a parameter. - *
    • {@link #ACTION_REMOVE Remove}: Removes a specified media item from the queue. - *
    - * - *

    Session actions

    - *

    - * The following session actions must be supported (all or nothing) by remote - * playback routes that offer optional session management capabilities. - *

      - *
    • {@link #ACTION_START_SESSION Start session}: Starts a new session explicitly. - *
    • {@link #ACTION_GET_SESSION_STATUS Get session status}: Gets the status of a session. - *
    • {@link #ACTION_END_SESSION End session}: Ends a session explicitly. - *
    - * - *

    Implementation note

    - *

    - * Implementations of the remote playback protocol must implement all of the - * documented actions, parameters and results. Note that the documentation is written from - * the perspective of a client of the protocol. In particular, whenever a parameter - * is described as being "optional", it is only from the perspective of the client. - * Compliant media route provider implementations of this protocol must support all - * of the features described herein. - *

    - */ -public final class MediaControlIntent { - /* Route categories. */ - - /** - * Media control category: Live audio. - *

    - * A route that supports live audio routing will allow the media audio stream - * to be sent to supported destinations. This can include internal speakers or - * audio jacks on the device itself, A2DP devices, and more. - *

    - * When a live audio route is selected, audio routing is transparent to the application. - * All audio played on the media stream will be routed to the selected destination. - *

    - * Refer to the class documentation for details about live audio routes. - *

    - */ - public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO"; - - /** - * Media control category: Live video. - *

    - * A route that supports live video routing will allow a mirrored version - * of the device's primary display or a customized - * {@link android.app.Presentation Presentation} to be sent to supported - * destinations. - *

    - * When a live video route is selected, audio and video routing is transparent - * to the application. By default, audio and video is routed to the selected - * destination. For certain live video routes, the application may also use a - * {@link android.app.Presentation Presentation} to replace the mirrored view - * on the external display with different content. - *

    - * Refer to the class documentation for details about live video routes. - *

    - * - * @see MediaRouter.RouteInfo#getPresentationDisplay() - * @see android.app.Presentation - */ - public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO"; - - /** - * Media control category: Remote playback. - *

    - * A route that supports remote playback routing will allow an application to send - * requests to play content remotely to supported destinations. - *

    - * Remote playback routes destinations operate independently of the local device. - * When a remote playback route is selected, the application can control the content - * playing on the destination by sending media control actions to the route. - * The application may also receive status updates from the route regarding - * remote playback. - *

    - * Refer to the class documentation for details about remote playback routes. - *

    - * - * @see MediaRouter.RouteInfo#sendControlRequest - */ - public static final String CATEGORY_REMOTE_PLAYBACK = - "android.media.intent.category.REMOTE_PLAYBACK"; - - /* Remote playback actions that affect individual items. */ - - /** - * Remote playback media control action: Play media item. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes a remote playback route to start playing content with - * the {@link Uri} specified in the {@link Intent}'s {@link Intent#getData() data uri}. - * The action returns a media session id and media item id which can be used - * to control playback using other remote playback actions. - *

    - * Once initiated, playback of the specified content will be managed independently - * by the destination. The application will receive status updates as the state - * of the media item changes. - *

    - * If the data uri specifies an HTTP or HTTPS scheme, then the destination is - * responsible for following HTTP redirects to a reasonable depth of at least 3 - * levels as might typically be handled by a web browser. If an HTTP error - * occurs, then the destination should send a {@link MediaItemStatus status update} - * back to the client indicating the {@link MediaItemStatus#PLAYBACK_STATE_ERROR error} - * {@link MediaItemStatus#getPlaybackState() playback state}. - *

    - * - *

    One item at a time

    - *

    - * Each successful play action replaces the previous play action. - * If an item is already playing, then it is canceled, the session's playback queue - * is cleared and the new item begins playing immediately (regardless of - * whether the previously playing item had been paused). - *

    - * Play is therefore equivalent to {@link #ACTION_STOP stop} followed by an action - * to enqueue a new media item to be played immediately. - *

    - * - *

    Sessions

    - *

    - * This request has the effect of implicitly creating a media session whenever the - * application does not specify the {@link #EXTRA_SESSION_ID session id} parameter. - * Because there can only be at most one valid session at a time, creating a new session - * has the side-effect of invalidating any existing sessions and their media items, - * then handling the playback request with a new session. - *

    - * If the application specifies an invalid session id, then an error is returned. - * When this happens, the application should assume that its session - * is no longer valid. To obtain a new session, the application may try again - * and omit the session id parameter. However, the application should - * only retry requests due to an explicit action performed by the user, - * such as the user clicking on a "play" button in the UI, since another - * application may be trying to take control of the route and the former - * application should try to stay out of its way. - *

    - * For more information on sessions, queues and media items, please refer to the - * class documentation. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (optional): Specifies the session id of the - * session to which the playback request belongs. If omitted, a new session - * is created implicitly. - *
    • {@link #EXTRA_ITEM_CONTENT_POSITION} (optional): Specifies the initial - * content playback position as a long integer number of milliseconds from - * the beginning of the content. - *
    • {@link #EXTRA_ITEM_METADATA} (optional): Specifies metadata associated - * with the content such as the title of a song. - *
    • {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER} (optional): Specifies a - * {@link PendingIntent} for a broadcast receiver that will receive status updates - * about the media item. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (always returned): Specifies the session id of the - * session that was affected by the request. This will be a new session in - * the case where no session id was supplied as a parameter. - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    • {@link #EXTRA_ITEM_ID} (always returned): Specifies an opaque string identifier - * to use to refer to the media item in subsequent requests such as - * {@link #ACTION_GET_STATUS}. - *
    • {@link #EXTRA_ITEM_STATUS} (always returned): Specifies the initial status of - * the new media item. - *
    - * - *

    Status updates

    - *

    - * If the client supplies an - * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver} - * then the media route provider is responsible for sending status updates to the receiver - * when significant media item state changes occur such as when playback starts or - * stops. The receiver will not be invoked for content playback position changes. - * The application may retrieve the current playback position when necessary - * using the {@link #ACTION_GET_STATUS} request. - *

    - * Refer to {@link MediaItemStatus} for details. - *

    - * - *

    Errors

    - *

    - * This action returns an error if a session id was provided but is unknown or - * no longer valid, if the item Uri or content type is not supported, or if - * any other arguments are invalid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - *

    Example

    - *
    -     * MediaRouter mediaRouter = MediaRouter.getInstance(context);
    -     * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
    -     * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
    -     * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
    -     * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
    -     * if (route.supportsControlRequest(intent)) {
    -     *     MediaRouter.ControlRequestCallback callback = new MediaRouter.ControlRequestCallback() {
    -     *         public void onResult(Bundle data) {
    -     *             // The request succeeded.
    -     *             // Playback may be controlled using the returned session and item id.
    -     *             String sessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
    -     *             String itemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
    -     *             MediaItemStatus status = MediaItemStatus.fromBundle(data.getBundle(
    -     *                     MediaControlIntent.EXTRA_ITEM_STATUS));
    -     *             // ...
    -     *         }
    -     *
    -     *         public void onError(String message, Bundle data) {
    -     *             // An error occurred!
    -     *         }
    -     *     };
    -     *     route.sendControlRequest(intent, callback);
    -     * }
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - * @see #ACTION_SEEK - * @see #ACTION_GET_STATUS - * @see #ACTION_PAUSE - * @see #ACTION_RESUME - * @see #ACTION_STOP - */ - public static final String ACTION_PLAY = "android.media.intent.action.PLAY"; - - /** - * Remote playback media control action: Enqueue media item. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action works just like {@link #ACTION_PLAY play} except that it does - * not clear the queue or reset the pause state when it enqueues the - * new media item into the session's playback queue. This action only - * enqueues a media item with no other side-effects on the queue. - *

    - * If the queue is currently empty and then the item will play immediately - * (assuming the queue is not paused). Otherwise, the item will play - * after all earlier items in the queue have finished or been removed. - *

    - * The enqueue action can be used to create new sessions just like play. - * Its parameters and results are also the same. Only the queuing behavior - * is different. - *

    - * - * @see #ACTION_PLAY - */ - public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE"; - - /** - * Remote playback media control action: Seek media item to a new playback position. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes a remote playback route to modify the current playback position - * of the specified media item. - *

    - * This action only affects the playback position of the media item; not its playback state. - * If the playback queue is paused, then seeking sets the position but the item - * remains paused. Likewise if the item is playing, then seeking will cause playback - * to jump to the new position and continue playing from that point. If the item has - * not yet started playing, then the new playback position is remembered by the - * queue and used as the item's initial content position when playback eventually begins. - *

    - * If successful, the media item's playback position is changed. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * to which the media item belongs. - *
    • {@link #EXTRA_ITEM_ID} (required): Specifies the media item id of - * the media item to seek. - *
    • {@link #EXTRA_ITEM_CONTENT_POSITION} (required): Specifies the new - * content position for playback as a long integer number of milliseconds from - * the beginning of the content. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    • {@link #EXTRA_ITEM_STATUS} (always returned): Specifies the new status of - * the media item. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id or media item id are unknown - * or no longer valid, if the content position is invalid, or if the media item - * is in a terminal state. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - */ - public static final String ACTION_SEEK = "android.media.intent.action.SEEK"; - - /** - * Remote playback media control action: Get media item playback status - * and progress information. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action asks a remote playback route to provide updated playback status and progress - * information about the specified media item. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * to which the media item belongs. - *
    • {@link #EXTRA_ITEM_ID} (required): Specifies the media item id of - * the media item to query. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    • {@link #EXTRA_ITEM_STATUS} (always returned): Specifies the current status of - * the media item. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id or media item id are unknown - * or no longer valid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - * @see #EXTRA_ITEM_STATUS_UPDATE_RECEIVER - */ - public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS"; - - /** - * Remote playback media control action: Remove media item from session's queue. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action asks a remote playback route to remove the specified media item - * from the session's playback queue. If the current item is removed, then - * playback will proceed to the next media item (assuming the queue has not been - * paused). - *

    - * This action does not affect the pause state of the queue. If the queue was paused - * then it remains paused (even if it is now empty) until a resume, stop or play - * action is issued that causes the pause state to be cleared. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * to which the media item belongs. - *
    • {@link #EXTRA_ITEM_ID} (required): Specifies the media item id of - * the media item to remove. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    • {@link #EXTRA_ITEM_STATUS} (always returned): Specifies the new status of - * the media item. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id or media item id are unknown - * or no longer valid, or if the media item is in a terminal state (and therefore - * no longer in the queue). - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - */ - public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE"; - - /* Remote playback actions that affect the whole playback queue. */ - - /** - * Remote playback media control action: Pause media playback. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes the playback queue of the specified session to be paused. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * whose playback queue is to be paused. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id is unknown or no longer valid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - * @see #ACTION_RESUME - */ - public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE"; - - /** - * Remote playback media control action: Resume media playback (unpause). - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes the playback queue of the specified session to be resumed. - * Reverses the effects of {@link #ACTION_PAUSE}. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * whose playback queue is to be resumed. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id is unknown or no longer valid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - * @see #ACTION_PAUSE - */ - public static final String ACTION_RESUME = "android.media.intent.action.RESUME"; - - /** - * Remote playback media control action: Stop media playback (clear queue and unpause). - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes a remote playback route to stop playback, cancel and remove - * all media items from the session's media item queue and, reset the queue's - * pause state. - *

    - * If successful, the status of all media items in the queue is set to - * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent - * to the appropriate status update receivers indicating the new status of each item. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of - * the session whose playback queue is to be stopped (cleared and unpaused). - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id is unknown or no longer valid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - */ - public static final String ACTION_STOP = "android.media.intent.action.STOP"; - - /** - * Remote playback media control action: Start session. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes a remote playback route to invalidate the current session - * and start a new session. The new session initially has an empty queue. - *

    - * If successful, the status of all media items in the previous session's queue is set to - * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated} and a status update - * is sent to the appropriate status update receivers indicating the new status - * of each item. The previous session becomes no longer valid and the new session - * takes control of the route. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER} (optional): Specifies a - * {@link PendingIntent} for a broadcast receiver that will receive status updates - * about the media session. - *
    • {@link #EXTRA_MESSAGE_RECEIVER} (optional): Specifies a - * {@link PendingIntent} for a broadcast receiver that will receive messages from - * the media session. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (always returned): Specifies the session id of the - * session that was started by the request. This will always be a brand new session - * distinct from any other previously created sessions. - *
    • {@link #EXTRA_SESSION_STATUS} (always returned): Specifies the - * status of the media session. - *
    - * - *

    Status updates

    - *

    - * If the client supplies a - * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER status update receiver} - * then the media route provider is responsible for sending status updates to the receiver - * when significant media session state changes occur such as when the session's - * queue is paused or resumed or when the session is terminated or invalidated. - *

    - * Refer to {@link MediaSessionStatus} for details. - *

    - * - *

    Custom messages

    - *

    - * If the client supplies a {@link #EXTRA_MESSAGE_RECEIVER message receiver} - * then the media route provider is responsible for sending messages to the receiver - * when the session has any messages to send. - *

    - * Refer to {@link #EXTRA_MESSAGE} for details. - *

    - * - *

    Errors

    - *

    - * This action returns an error if the session could not be created. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - */ - public static final String ACTION_START_SESSION = "android.media.intent.action.START_SESSION"; - - /** - * Remote playback media control action: Get media session status information. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action asks a remote playback route to provide updated status information - * about the specified media session. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the - * session whose status is to be retrieved. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (always returned): Specifies the - * current status of the media session. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id is unknown or no longer valid. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - * @see #EXTRA_SESSION_STATUS_UPDATE_RECEIVER - */ - public static final String ACTION_GET_SESSION_STATUS = - "android.media.intent.action.GET_SESSION_STATUS"; - - /** - * Remote playback media control action: End session. - *

    - * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback} - * media control. - *

    - * This action causes a remote playback route to end the specified session. - * The session becomes no longer valid and the route ceases to be under control - * of the session. - *

    - * If successful, the status of the session is set to - * {@link MediaSessionStatus#SESSION_STATE_ENDED} and a status update is sent to - * the session's status update receiver. - *

    - * Additionally, the status of all media items in the queue is set to - * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent - * to the appropriate status update receivers indicating the new status of each item. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of - * the session to end. - *
    - * - *

    Result data

    - *
      - *
    • {@link #EXTRA_SESSION_STATUS} (always returned): Specifies the - * status of the media session. - *
    - * - *

    Errors

    - *

    - * This action returns an error if the session id is unknown or no longer valid. - * In other words, it is an error to attempt to end a session other than the - * current session. - *

      - *
    • {@link #EXTRA_ERROR_CODE} (optional): Specifies the cause of the error. - *
    - * - * @see MediaRouter.RouteInfo#sendControlRequest - * @see #CATEGORY_REMOTE_PLAYBACK - */ - public static final String ACTION_END_SESSION = "android.media.intent.action.END_SESSION"; - - /** - * Custom media control action: Send {@link #EXTRA_MESSAGE}. - *

    - * This action asks a route to handle a message described by EXTRA_MESSAGE. - *

    - * - *

    Request parameters

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of the session - * to which will handle this message. - *
    • {@link #EXTRA_MESSAGE} (required): Specifies the message to send. - *
    - * - *

    Result data

    - * Any messages defined by each media route provider. - * - *

    Errors

    - * Any error messages defined by each media route provider. - * - * @see MediaRouter.RouteInfo#sendControlRequest - */ - public static final String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE"; - - /* Extras and related constants. */ - - /** - * Bundle extra: Media session id. - *

    - * An opaque unique identifier that identifies the remote playback media session. - *

    - * Used with various actions to specify the id of the media session to be controlled. - *

    - * Included in broadcast intents sent to - * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to identify - * the session to which the item in question belongs. - *

    - * Included in broadcast intents sent to - * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to identify - * the session. - *

    - * The value is a unique string value generated by the media route provider - * to represent one particular media session. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_SEEK - * @see #ACTION_GET_STATUS - * @see #ACTION_PAUSE - * @see #ACTION_RESUME - * @see #ACTION_STOP - * @see #ACTION_START_SESSION - * @see #ACTION_GET_SESSION_STATUS - * @see #ACTION_END_SESSION - */ - public static final String EXTRA_SESSION_ID = - "android.media.intent.extra.SESSION_ID"; - - /** - * Bundle extra: Media session status. - *

    - * Returned as a result from media session actions such as {@link #ACTION_START_SESSION}, - * {@link #ACTION_PAUSE}, and {@link #ACTION_GET_SESSION_STATUS} - * to describe the status of the specified media session. - *

    - * Included in broadcast intents sent to - * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to provide - * updated status information. - *

    - * The value is a {@link android.os.Bundle} of data that can be converted into - * a {@link MediaSessionStatus} object using - * {@link MediaSessionStatus#fromBundle MediaSessionStatus.fromBundle}. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_SEEK - * @see #ACTION_GET_STATUS - * @see #ACTION_PAUSE - * @see #ACTION_RESUME - * @see #ACTION_STOP - * @see #ACTION_START_SESSION - * @see #ACTION_GET_SESSION_STATUS - * @see #ACTION_END_SESSION - */ - public static final String EXTRA_SESSION_STATUS = - "android.media.intent.extra.SESSION_STATUS"; - - /** - * Bundle extra: Media session status update receiver. - *

    - * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a - * broadcast receiver that will receive status updates about the media session. - *

    - * Whenever the status of the media session changes, the media route provider will - * send a broadcast to the pending intent with extras that identify the session - * id and its updated status. - *

    - * The value is a {@link PendingIntent}. - *

    - * - *

    Broadcast extras

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of - * the session. - *
    • {@link #EXTRA_SESSION_STATUS} (required): Specifies the status of the - * session as a bundle that can be decoded into a {@link MediaSessionStatus} object. - *
    - * - * @see #ACTION_START_SESSION - */ - public static final String EXTRA_SESSION_STATUS_UPDATE_RECEIVER = - "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER"; - - /** - * Bundle extra: Media message receiver. - *

    - * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a - * broadcast receiver that will receive messages from the media session. - *

    - * When the media session has a message to send, the media route provider will - * send a broadcast to the pending intent with extras that identify the session - * id and its message. - *

    - * The value is a {@link PendingIntent}. - *

    - * - *

    Broadcast extras

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of - * the session. - *
    • {@link #EXTRA_MESSAGE} (required): Specifies the message from - * the session as a bundle object. - *
    - * - * @see #ACTION_START_SESSION - */ - public static final String EXTRA_MESSAGE_RECEIVER = - "android.media.intent.extra.MESSAGE_RECEIVER"; - - /** - * Bundle extra: Media item id. - *

    - * An opaque unique identifier returned as a result from {@link #ACTION_PLAY} or - * {@link #ACTION_ENQUEUE} that represents the media item that was created by the - * playback request. - *

    - * Used with various actions to specify the id of the media item to be controlled. - *

    - * Included in broadcast intents sent to - * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receivers} to identify - * the item in question. - *

    - * The value is a unique string value generated by the media route provider - * to represent one particular media item. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - * @see #ACTION_SEEK - * @see #ACTION_GET_STATUS - */ - public static final String EXTRA_ITEM_ID = - "android.media.intent.extra.ITEM_ID"; - - /** - * Bundle extra: Media item status. - *

    - * Returned as a result from media item actions such as {@link #ACTION_PLAY}, - * {@link #ACTION_ENQUEUE}, {@link #ACTION_SEEK}, and {@link #ACTION_GET_STATUS} - * to describe the status of the specified media item. - *

    - * Included in broadcast intents sent to - * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to provide - * updated status information. - *

    - * The value is a {@link android.os.Bundle} of data that can be converted into - * a {@link MediaItemStatus} object using - * {@link MediaItemStatus#fromBundle MediaItemStatus.fromBundle}. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - * @see #ACTION_SEEK - * @see #ACTION_GET_STATUS - */ - public static final String EXTRA_ITEM_STATUS = - "android.media.intent.extra.ITEM_STATUS"; - - /** - * Long extra: Media item content position. - *

    - * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify the - * starting playback position. - *

    - * Used with {@link #ACTION_SEEK} to set a new playback position. - *

    - * The value is a long integer number of milliseconds from the beginning of the content. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - * @see #ACTION_SEEK - */ - public static final String EXTRA_ITEM_CONTENT_POSITION = - "android.media.intent.extra.ITEM_POSITION"; - - /** - * Bundle extra: Media item metadata. - *

    - * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify metadata - * associated with the content of a media item. - *

    - * The value is a {@link android.os.Bundle} of metadata key-value pairs as defined - * in {@link MediaItemMetadata}. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - */ - public static final String EXTRA_ITEM_METADATA = - "android.media.intent.extra.ITEM_METADATA"; - - /** - * Bundle extra: HTTP request headers. - *

    - * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify HTTP request - * headers to be included when fetching to the content indicated by the media - * item's data Uri. - *

    - * This extra may be used to provide authentication tokens and other - * parameters to the server separately from the media item's data Uri. - *

    - * The value is a {@link android.os.Bundle} of string based key-value pairs - * that describe the HTTP request headers. - *

    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - */ - public static final String EXTRA_ITEM_HTTP_HEADERS = - "android.media.intent.extra.HTTP_HEADERS"; - - /** - * Bundle extra: Media item status update receiver. - *

    - * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify - * a {@link PendingIntent} for a - * broadcast receiver that will receive status updates about a particular - * media item. - *

    - * Whenever the status of the media item changes, the media route provider will - * send a broadcast to the pending intent with extras that identify the session - * to which the item belongs, the session status, the item's id - * and the item's updated status. - *

    - * The same pending intent and broadcast receiver may be shared by any number of - * media items since the broadcast intent includes the media session id - * and media item id. - *

    - * The value is a {@link PendingIntent}. - *

    - * - *

    Broadcast extras

    - *
      - *
    • {@link #EXTRA_SESSION_ID} (required): Specifies the session id of - * the session to which the item in question belongs. - *
    • {@link #EXTRA_SESSION_STATUS} (optional, old implementations may - * omit this key): Specifies the status of the media session. - *
    • {@link #EXTRA_ITEM_ID} (required): Specifies the media item id of the - * media item in question. - *
    • {@link #EXTRA_ITEM_STATUS} (required): Specifies the status of the - * item as a bundle that can be decoded into a {@link MediaItemStatus} object. - *
    - * - * @see #ACTION_PLAY - * @see #ACTION_ENQUEUE - */ - public static final String EXTRA_ITEM_STATUS_UPDATE_RECEIVER = - "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER"; - - /** - * Bundle extra: Message. - *

    - * Used with {@link #ACTION_SEND_MESSAGE}, and included in broadcast intents sent to - * {@link #EXTRA_MESSAGE_RECEIVER message receivers} to describe a message between a - * session and a media route provider. - *

    - * The value is a {@link android.os.Bundle}. - *

    - */ - public static final String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE"; - - /** - * Integer extra: Error code. - *

    - * Used with all media control requests to describe the cause of an error. - * This extra may be omitted when the error is unknown. - *

    - * The value is one of: {@link #ERROR_UNKNOWN}, {@link #ERROR_UNSUPPORTED_OPERATION}, - * {@link #ERROR_INVALID_SESSION_ID}, {@link #ERROR_INVALID_ITEM_ID}. - *

    - */ - public static final String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE"; - - /** - * Error code: An unknown error occurred. - * - * @see #EXTRA_ERROR_CODE - */ - public static final int ERROR_UNKNOWN = 0; - - /** - * Error code: The operation is not supported. - * - * @see #EXTRA_ERROR_CODE - */ - public static final int ERROR_UNSUPPORTED_OPERATION = 1; - - /** - * Error code: The session id specified in the request was invalid. - * - * @see #EXTRA_ERROR_CODE - */ - public static final int ERROR_INVALID_SESSION_ID = 2; - - /** - * Error code: The item id specified in the request was invalid. - * - * @see #EXTRA_ERROR_CODE - */ - public static final int ERROR_INVALID_ITEM_ID = 3; - - private MediaControlIntent() { - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java deleted file mode 100644 index d52ddb6259..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.os.Bundle; - -/** - * Constants for specifying metadata about a media item as a {@link Bundle}. - *

    - * This class is part of the remote playback protocol described by the - * {@link MediaControlIntent MediaControlIntent} class. - *

    - * Media item metadata is described as a bundle of key/value pairs as defined - * in this class. The documentation specifies the type of value associated - * with each key. - *

    - * An application may specify additional custom metadata keys but there is no guarantee - * that they will be recognized by the destination. - *

    - */ -public final class MediaItemMetadata { - /* - * Note: MediaMetadataRetriever also defines a collection of metadata keys that can be - * retrieved from a content stream although the representation is somewhat different here - * since we are sending the data to a remote endpoint. - */ - - private MediaItemMetadata() { - } - - /** - * String key: Album artist name. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; - - /** - * String key: Album title. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE"; - - /** - * String key: Artwork Uri. - *

    - * The value is a string URI for an image file associated with the media item, - * such as album or cover art. - *

    - */ - public static final String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI"; - - /** - * String key: Artist name. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_ARTIST = "android.media.metadata.ARTIST"; - - /** - * String key: Author name. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_AUTHOR = "android.media.metadata.AUTHOR"; - - /** - * String key: Composer name. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_COMPOSER = "android.media.metadata.COMPOSER"; - - /** - * String key: Track title. - *

    - * The value is a string suitable for display. - *

    - */ - public static final String KEY_TITLE = "android.media.metadata.TITLE"; - - /** - * Integer key: Year of publication. - *

    - * The value is an integer year number. - *

    - */ - public static final String KEY_YEAR = "android.media.metadata.YEAR"; - - /** - * Integer key: Track number (such as a track on a CD). - *

    - * The value is a one-based integer track number. - *

    - */ - public static final String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; - - /** - * Integer key: Disc number within a collection. - *

    - * The value is a one-based integer disc number. - *

    - */ - public static final String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; - - /** - * Long key: Item playback duration in milliseconds. - *

    - * The value is a long number of milliseconds. - *

    - * The duration metadata is only a hint to enable a remote media player to - * guess the duration of the content before it actually opens the media stream. - * The remote media player should still determine the actual content duration from - * the media stream itself independent of the value that may be specified by this key. - *

    - */ - public static final String KEY_DURATION = "android.media.metadata.DURATION"; -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java deleted file mode 100644 index 92f608b6c8..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.app.PendingIntent; -import android.os.Bundle; -import android.os.SystemClock; - -import androidx.core.util.TimeUtils; - -/** - * Describes the playback status of a media item. - *

    - * This class is part of the remote playback protocol described by the - * {@link MediaControlIntent MediaControlIntent} class. - *

    - * As a media item is played, it transitions through a sequence of states including: - * {@link #PLAYBACK_STATE_PENDING pending}, {@link #PLAYBACK_STATE_BUFFERING buffering}, - * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused}, - * {@link #PLAYBACK_STATE_FINISHED finished}, {@link #PLAYBACK_STATE_CANCELED canceled}, - * {@link #PLAYBACK_STATE_INVALIDATED invalidated}, and - * {@link #PLAYBACK_STATE_ERROR error}. Refer to the documentation of each state - * for an explanation of its meaning. - *

    - * While the item is playing, the playback status may also include progress information - * about the {@link #getContentPosition content position} and - * {@link #getContentDuration content duration} although not all route destinations - * will report it. - *

    - * To monitor playback status, the application should supply a {@link PendingIntent} to use as the - * {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver} - * for a given {@link MediaControlIntent#ACTION_PLAY playback request}. Note that - * the status update receiver will only be invoked for major status changes such as a - * transition from playing to finished. - *

    - * The status update receiver will not be invoked for minor progress updates such as - * changes to playback position or duration. If the application wants to monitor - * playback progress, then it must use the - * {@link MediaControlIntent#ACTION_GET_STATUS get status request} to poll for changes - * periodically and estimate the playback position while playing. Note that there may - * be a significant power impact to polling so the application is advised only - * to poll when the screen is on and never more than about once every 5 seconds or so. - *

    - * This object is immutable once created using a {@link Builder} instance. - *

    - */ -public final class MediaItemStatus { - static final String KEY_TIMESTAMP = "timestamp"; - static final String KEY_PLAYBACK_STATE = "playbackState"; - static final String KEY_CONTENT_POSITION = "contentPosition"; - static final String KEY_CONTENT_DURATION = "contentDuration"; - static final String KEY_EXTRAS = "extras"; - - final Bundle mBundle; - - /** - * Playback state: Pending. - *

    - * Indicates that the media item has not yet started playback but will be played eventually. - *

    - */ - public static final int PLAYBACK_STATE_PENDING = 0; - - /** - * Playback state: Playing. - *

    - * Indicates that the media item is currently playing. - *

    - */ - public static final int PLAYBACK_STATE_PLAYING = 1; - - /** - * Playback state: Paused. - *

    - * Indicates that playback of the media item has been paused. Playback can be - * resumed using the {@link MediaControlIntent#ACTION_RESUME resume} action. - *

    - */ - public static final int PLAYBACK_STATE_PAUSED = 2; - - /** - * Playback state: Buffering or seeking to a new position. - *

    - * Indicates that the media item has been temporarily interrupted - * to fetch more content. Playback will continue automatically - * when enough content has been buffered. - *

    - */ - public static final int PLAYBACK_STATE_BUFFERING = 3; - - /** - * Playback state: Finished. - *

    - * Indicates that the media item played to the end of the content and finished normally. - *

    - * A finished media item cannot be resumed. To play the content again, the application - * must send a new {@link MediaControlIntent#ACTION_PLAY play} or - * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action. - *

    - */ - public static final int PLAYBACK_STATE_FINISHED = 4; - - /** - * Playback state: Canceled. - *

    - * Indicates that the media item was explicitly removed from the queue by the - * application. Items may be canceled and removed from the queue using - * the {@link MediaControlIntent#ACTION_REMOVE remove} or - * {@link MediaControlIntent#ACTION_STOP stop} action or by issuing - * another {@link MediaControlIntent#ACTION_PLAY play} action that has the - * side-effect of clearing the queue. - *

    - * A canceled media item cannot be resumed. To play the content again, the - * application must send a new {@link MediaControlIntent#ACTION_PLAY play} or - * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action. - *

    - */ - public static final int PLAYBACK_STATE_CANCELED = 5; - - /** - * Playback state: Invalidated. - *

    - * Indicates that the media item was invalidated permanently and involuntarily. - * This state is used to indicate that the media item was invalidated and removed - * from the queue because the session to which it belongs was invalidated - * (typically by another application taking control of the route). - *

    - * When invalidation occurs, the application should generally wait for the user - * to perform an explicit action, such as clicking on a play button in the UI, - * before creating a new media session to avoid unnecessarily interrupting - * another application that may have just started using the route. - *

    - * An invalidated media item cannot be resumed. To play the content again, the application - * must send a new {@link MediaControlIntent#ACTION_PLAY play} or - * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action. - *

    - */ - public static final int PLAYBACK_STATE_INVALIDATED = 6; - - /** - * Playback state: Playback halted or aborted due to an error. - *

    - * Examples of errors are no network connectivity when attempting to retrieve content - * from a server, or expired user credentials when trying to play subscription-based - * content. - *

    - * A media item in the error state cannot be resumed. To play the content again, - * the application must send a new {@link MediaControlIntent#ACTION_PLAY play} or - * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action. - *

    - */ - public static final int PLAYBACK_STATE_ERROR = 7; - - /** - * Integer extra: HTTP status code. - *

    - * Specifies the HTTP status code that was encountered when the content - * was requested after all redirects were followed. This key only needs to - * specified when the content uri uses the HTTP or HTTPS scheme and an error - * occurred. This key may be omitted if the content was able to be played - * successfully; there is no need to report a 200 (OK) status code. - *

    - * The value is an integer HTTP status code, such as 401 (Unauthorized), - * 404 (Not Found), or 500 (Server Error), or 0 if none. - *

    - */ - public static final String EXTRA_HTTP_STATUS_CODE = - "android.media.status.extra.HTTP_STATUS_CODE"; - - /** - * Bundle extra: HTTP response headers. - *

    - * Specifies the HTTP response headers that were returned when the content was - * requested from the network. The headers may include additional information - * about the content or any errors conditions that were encountered while - * trying to fetch the content. - *

    - * The value is a {@link android.os.Bundle} of string based key-value pairs - * that describe the HTTP response headers. - *

    - */ - public static final String EXTRA_HTTP_RESPONSE_HEADERS = - "android.media.status.extra.HTTP_RESPONSE_HEADERS"; - - MediaItemStatus(Bundle bundle) { - mBundle = bundle; - } - - /** - * Gets the timestamp associated with the status information in - * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base. - * - * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base. - */ - public long getTimestamp() { - return mBundle.getLong(KEY_TIMESTAMP); - } - - /** - * Gets the playback state of the media item. - * - * @return The playback state. One of {@link #PLAYBACK_STATE_PENDING}, - * {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED}, - * {@link #PLAYBACK_STATE_BUFFERING}, {@link #PLAYBACK_STATE_FINISHED}, - * {@link #PLAYBACK_STATE_CANCELED}, {@link #PLAYBACK_STATE_INVALIDATED}, - * or {@link #PLAYBACK_STATE_ERROR}. - */ - public int getPlaybackState() { - return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_ERROR); - } - - /** - * Gets the content playback position as a long integer number of milliseconds - * from the beginning of the content. - * - * @return The content playback position in milliseconds, or -1 if unknown. - */ - public long getContentPosition() { - return mBundle.getLong(KEY_CONTENT_POSITION, -1); - } - - /** - * Gets the total duration of the content to be played as a long integer number of - * milliseconds. - * - * @return The content duration in milliseconds, or -1 if unknown. - */ - public long getContentDuration() { - return mBundle.getLong(KEY_CONTENT_DURATION, -1); - } - - /** - * Gets a bundle of extras for this status object. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Bundle getExtras() { - return mBundle.getBundle(KEY_EXTRAS); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("MediaItemStatus{ "); - result.append("timestamp="); - TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result); - result.append(" ms ago"); - result.append(", playbackState=").append(playbackStateToString(getPlaybackState())); - result.append(", contentPosition=").append(getContentPosition()); - result.append(", contentDuration=").append(getContentDuration()); - result.append(", extras=").append(getExtras()); - result.append(" }"); - return result.toString(); - } - - private static String playbackStateToString(int playbackState) { - switch (playbackState) { - case PLAYBACK_STATE_PENDING: - return "pending"; - case PLAYBACK_STATE_BUFFERING: - return "buffering"; - case PLAYBACK_STATE_PLAYING: - return "playing"; - case PLAYBACK_STATE_PAUSED: - return "paused"; - case PLAYBACK_STATE_FINISHED: - return "finished"; - case PLAYBACK_STATE_CANCELED: - return "canceled"; - case PLAYBACK_STATE_INVALIDATED: - return "invalidated"; - case PLAYBACK_STATE_ERROR: - return "error"; - } - return Integer.toString(playbackState); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaItemStatus fromBundle(Bundle bundle) { - return bundle != null ? new MediaItemStatus(bundle) : null; - } - - /** - * Builder for {@link MediaItemStatus media item status objects}. - */ - public static final class Builder { - private final Bundle mBundle; - - /** - * Creates a media item status builder using the current time as the - * reference timestamp. - * - * @param playbackState The item playback state. - */ - public Builder(int playbackState) { - mBundle = new Bundle(); - setTimestamp(SystemClock.elapsedRealtime()); - setPlaybackState(playbackState); - } - - /** - * Creates a media item status builder whose initial contents are - * copied from an existing status. - */ - public Builder(MediaItemStatus status) { - if (status == null) { - throw new IllegalArgumentException("status must not be null"); - } - - mBundle = new Bundle(status.mBundle); - } - - /** - * Sets the timestamp associated with the status information in - * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base. - */ - public Builder setTimestamp(long elapsedRealtimeTimestamp) { - mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp); - return this; - } - - /** - * Sets the playback state of the media item. - */ - public Builder setPlaybackState(int playbackState) { - mBundle.putInt(KEY_PLAYBACK_STATE, playbackState); - return this; - } - - /** - * Sets the content playback position as a long integer number of milliseconds - * from the beginning of the content. - */ - public Builder setContentPosition(long positionMilliseconds) { - mBundle.putLong(KEY_CONTENT_POSITION, positionMilliseconds); - return this; - } - - /** - * Sets the total duration of the content to be played as a long integer number - * of milliseconds. - */ - public Builder setContentDuration(long durationMilliseconds) { - mBundle.putLong(KEY_CONTENT_DURATION, durationMilliseconds); - return this; - } - - /** - * Sets a bundle of extras for this status object. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Builder setExtras(Bundle extras) { - mBundle.putBundle(KEY_EXTRAS, extras); - return this; - } - - /** - * Builds the {@link MediaItemStatus media item status object}. - */ - public MediaItemStatus build() { - return new MediaItemStatus(mBundle); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java deleted file mode 100644 index 6bc84fc7ea..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.content.IntentFilter; -import android.content.IntentSender; -import android.net.Uri; -import android.os.Bundle; -import android.text.TextUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Describes the properties of a route. - *

    - * Each route is uniquely identified by an opaque id string. This token - * may take any form as long as it is unique within the media route provider. - *

    - * This object is immutable once created using a {@link Builder} instance. - *

    - */ -public final class MediaRouteDescriptor { - static final String KEY_ID = "id"; - static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds"; - static final String KEY_NAME = "name"; - static final String KEY_DESCRIPTION = "status"; - static final String KEY_ICON_URI = "iconUri"; - static final String KEY_ENABLED = "enabled"; - static final String KEY_CONNECTING = "connecting"; - static final String KEY_CONNECTION_STATE = "connectionState"; - static final String KEY_CONTROL_FILTERS = "controlFilters"; - static final String KEY_PLAYBACK_TYPE = "playbackType"; - static final String KEY_PLAYBACK_STREAM = "playbackStream"; - static final String KEY_DEVICE_TYPE = "deviceType"; - static final String KEY_VOLUME = "volume"; - static final String KEY_VOLUME_MAX = "volumeMax"; - static final String KEY_VOLUME_HANDLING = "volumeHandling"; - static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId"; - static final String KEY_EXTRAS = "extras"; - static final String KEY_CAN_DISCONNECT = "canDisconnect"; - static final String KEY_SETTINGS_INTENT = "settingsIntent"; - static final String KEY_MIN_CLIENT_VERSION = "minClientVersion"; - static final String KEY_MAX_CLIENT_VERSION = "maxClientVersion"; - - final Bundle mBundle; - List mControlFilters; - - MediaRouteDescriptor(Bundle bundle, List controlFilters) { - mBundle = bundle; - mControlFilters = controlFilters; - } - - /** - * Gets the unique id of the route. - *

    - * The route id associated with a route descriptor functions as a stable - * identifier for the route and must be unique among all routes offered - * by the provider. - *

    - */ - public String getId() { - return mBundle.getString(KEY_ID); - } - - /** - * Gets the group member ids of the route. - *

    - * A route descriptor that has one or more group member route ids - * represents a route group. A member route may belong to another group. - *

    - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public List getGroupMemberIds() { - return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS); - } - - /** - * Gets the user-visible name of the route. - *

    - * The route name identifies the destination represented by the route. - * It may be a user-supplied name, an alias, or device serial number. - *

    - */ - public String getName() { - return mBundle.getString(KEY_NAME); - } - - /** - * Gets the user-visible description of the route. - *

    - * The route description describes the kind of destination represented by the route. - * It may be a user-supplied string, a model number or brand of device. - *

    - */ - public String getDescription() { - return mBundle.getString(KEY_DESCRIPTION); - } - - /** - * Gets the URI of the icon representing this route. - *

    - * This icon will be used in picker UIs if available. - *

    - */ - public Uri getIconUri() { - String iconUri = mBundle.getString(KEY_ICON_URI); - return iconUri == null ? null : Uri.parse(iconUri); - } - - /** - * Gets whether the route is enabled. - */ - public boolean isEnabled() { - return mBundle.getBoolean(KEY_ENABLED, true); - } - - /** - * Gets whether the route is connecting. - * @deprecated Use {@link #getConnectionState} instead - */ - @Deprecated - public boolean isConnecting() { - return mBundle.getBoolean(KEY_CONNECTING, false); - } - - /** - * Gets the connection state of the route. - * - * @return The connection state of this route: - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. - */ - public int getConnectionState() { - return mBundle.getInt(KEY_CONNECTION_STATE, - MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED); - } - - /** - * Gets whether the route can be disconnected without stopping playback. - *

    - * The route can normally be disconnected without stopping playback when - * the destination device on the route is connected to two or more source - * devices. The route provider should update the route immediately when the - * number of connected devices changes. - *

    - * To specify that the route should disconnect without stopping use - * {@link MediaRouter#unselect(int)} with - * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}. - *

    - */ - public boolean canDisconnectAndKeepPlaying() { - return mBundle.getBoolean(KEY_CAN_DISCONNECT, false); - } - - /** - * Gets an {@link IntentSender} for starting a settings activity for this - * route. The activity may have specific route settings or general settings - * for the connected device or route provider. - * - * @return An {@link IntentSender} to start a settings activity. - */ - public IntentSender getSettingsActivity() { - return mBundle.getParcelable(KEY_SETTINGS_INTENT); - } - - /** - * Gets the route's {@link MediaControlIntent media control intent} filters. - */ - public List getControlFilters() { - ensureControlFilters(); - return mControlFilters; - } - - void ensureControlFilters() { - if (mControlFilters == null) { - mControlFilters = mBundle.getParcelableArrayList(KEY_CONTROL_FILTERS); - if (mControlFilters == null) { - mControlFilters = Collections.emptyList(); - } - } - } - - /** - * Gets the type of playback associated with this route. - * - * @return The type of playback associated with this route: - * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or - * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. - */ - public int getPlaybackType() { - return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE); - } - - /** - * Gets the route's playback stream. - */ - public int getPlaybackStream() { - return mBundle.getInt(KEY_PLAYBACK_STREAM, -1); - } - - /** - * Gets the type of the receiver device associated with this route. - * - * @return The type of the receiver device associated with this route: - * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or - * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. - */ - public int getDeviceType() { - return mBundle.getInt(KEY_DEVICE_TYPE); - } - - /** - * Gets the route's current volume, or 0 if unknown. - */ - public int getVolume() { - return mBundle.getInt(KEY_VOLUME); - } - - /** - * Gets the route's maximum volume, or 0 if unknown. - */ - public int getVolumeMax() { - return mBundle.getInt(KEY_VOLUME_MAX); - } - - /** - * Gets information about how volume is handled on the route. - * - * @return How volume is handled on the route: - * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or - * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. - */ - public int getVolumeHandling() { - return mBundle.getInt(KEY_VOLUME_HANDLING, - MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED); - } - - /** - * Gets the route's presentation display id, or -1 if none. - */ - public int getPresentationDisplayId() { - return mBundle.getInt( - KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE); - } - - /** - * Gets a bundle of extras for this route descriptor. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Bundle getExtras() { - return mBundle.getBundle(KEY_EXTRAS); - } - - /** - * Gets the minimum client version required for this route. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public int getMinClientVersion() { - return mBundle.getInt(KEY_MIN_CLIENT_VERSION, - MediaRouteProviderProtocol.CLIENT_VERSION_START); - } - - /** - * Gets the maximum client version required for this route. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public int getMaxClientVersion() { - return mBundle.getInt(KEY_MAX_CLIENT_VERSION, Integer.MAX_VALUE); - } - - /** - * Returns true if the route descriptor has all of the required fields. - */ - public boolean isValid() { - ensureControlFilters(); - if (TextUtils.isEmpty(getId()) - || TextUtils.isEmpty(getName()) - || mControlFilters.contains(null)) { - return false; - } - return true; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("MediaRouteDescriptor{ "); - result.append("id=").append(getId()); - result.append(", groupMemberIds=").append(getGroupMemberIds()); - result.append(", name=").append(getName()); - result.append(", description=").append(getDescription()); - result.append(", iconUri=").append(getIconUri()); - result.append(", isEnabled=").append(isEnabled()); - result.append(", isConnecting=").append(isConnecting()); - result.append(", connectionState=").append(getConnectionState()); - result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray())); - result.append(", playbackType=").append(getPlaybackType()); - result.append(", playbackStream=").append(getPlaybackStream()); - result.append(", deviceType=").append(getDeviceType()); - result.append(", volume=").append(getVolume()); - result.append(", volumeMax=").append(getVolumeMax()); - result.append(", volumeHandling=").append(getVolumeHandling()); - result.append(", presentationDisplayId=").append(getPresentationDisplayId()); - result.append(", extras=").append(getExtras()); - result.append(", isValid=").append(isValid()); - result.append(", minClientVersion=").append(getMinClientVersion()); - result.append(", maxClientVersion=").append(getMaxClientVersion()); - result.append(" }"); - return result.toString(); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaRouteDescriptor fromBundle(Bundle bundle) { - return bundle != null ? new MediaRouteDescriptor(bundle, null) : null; - } - - /** - * Builder for {@link MediaRouteDescriptor media route descriptors}. - */ - public static final class Builder { - private final Bundle mBundle; - private ArrayList mGroupMemberIds; - private ArrayList mControlFilters; - - /** - * Creates a media route descriptor builder. - * - * @param id The unique id of the route. - * @param name The user-visible name of the route. - */ - public Builder(String id, String name) { - mBundle = new Bundle(); - setId(id); - setName(name); - } - - /** - * Creates a media route descriptor builder whose initial contents are - * copied from an existing descriptor. - */ - public Builder(MediaRouteDescriptor descriptor) { - if (descriptor == null) { - throw new IllegalArgumentException("descriptor must not be null"); - } - - mBundle = new Bundle(descriptor.mBundle); - - descriptor.ensureControlFilters(); - if (!descriptor.mControlFilters.isEmpty()) { - mControlFilters = new ArrayList(descriptor.mControlFilters); - } - } - - /** - * Sets the unique id of the route. - *

    - * The route id associated with a route descriptor functions as a stable - * identifier for the route and must be unique among all routes offered - * by the provider. - *

    - */ - public Builder setId(String id) { - mBundle.putString(KEY_ID, id); - return this; - } - - /** - * Adds a group member id of the route. - *

    - * A route descriptor that has one or more group member route ids - * represents a route group. A member route may belong to another group. - *

    - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public Builder addGroupMemberId(String groupMemberId) { - if (TextUtils.isEmpty(groupMemberId)) { - throw new IllegalArgumentException("groupMemberId must not be empty"); - } - - if (mGroupMemberIds == null) { - mGroupMemberIds = new ArrayList<>(); - } - if (!mGroupMemberIds.contains(groupMemberId)) { - mGroupMemberIds.add(groupMemberId); - } - return this; - } - - /** - * Adds a list of group member ids of the route. - *

    - * A route descriptor that has one or more group member route ids - * represents a route group. A member route may belong to another group. - *

    - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public Builder addGroupMemberIds(Collection groupMemberIds) { - if (groupMemberIds == null) { - throw new IllegalArgumentException("groupMemberIds must not be null"); - } - - if (!groupMemberIds.isEmpty()) { - for (String groupMemberId : groupMemberIds) { - addGroupMemberId(groupMemberId); - } - } - return this; - } - - /** - * Sets the user-visible name of the route. - *

    - * The route name identifies the destination represented by the route. - * It may be a user-supplied name, an alias, or device serial number. - *

    - */ - public Builder setName(String name) { - mBundle.putString(KEY_NAME, name); - return this; - } - - /** - * Sets the user-visible description of the route. - *

    - * The route description describes the kind of destination represented by the route. - * It may be a user-supplied string, a model number or brand of device. - *

    - */ - public Builder setDescription(String description) { - mBundle.putString(KEY_DESCRIPTION, description); - return this; - } - - /** - * Sets the URI of the icon representing this route. - *

    - * This icon will be used in picker UIs if available. - *

    - * The URI must be one of the following formats: - *

      - *
    • content ({@link android.content.ContentResolver#SCHEME_CONTENT})
    • - *
    • android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) - *
    • - *
    • file ({@link android.content.ContentResolver#SCHEME_FILE})
    • - *
    - *

    - */ - public Builder setIconUri(Uri iconUri) { - if (iconUri == null) { - throw new IllegalArgumentException("iconUri must not be null"); - } - mBundle.putString(KEY_ICON_URI, iconUri.toString()); - return this; - } - - /** - * Sets whether the route is enabled. - *

    - * Disabled routes represent routes that a route provider knows about, such as paired - * Wifi Display receivers, but that are not currently available for use. - *

    - */ - public Builder setEnabled(boolean enabled) { - mBundle.putBoolean(KEY_ENABLED, enabled); - return this; - } - - /** - * Sets whether the route is in the process of connecting and is not yet - * ready for use. - * @deprecated Use {@link #setConnectionState} instead. - */ - @Deprecated - public Builder setConnecting(boolean connecting) { - mBundle.putBoolean(KEY_CONNECTING, connecting); - return this; - } - - /** - * Sets the route's connection state. - * - * @param connectionState The connection state of the route: - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or - * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. - */ - public Builder setConnectionState(int connectionState) { - mBundle.putInt(KEY_CONNECTION_STATE, connectionState); - return this; - } - - /** - * Sets whether the route can be disconnected without stopping playback. - */ - public Builder setCanDisconnect(boolean canDisconnect) { - mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect); - return this; - } - - /** - * Sets an intent sender for launching the settings activity for this - * route. - */ - public Builder setSettingsActivity(IntentSender is) { - mBundle.putParcelable(KEY_SETTINGS_INTENT, is); - return this; - } - - /** - * Adds a {@link MediaControlIntent media control intent} filter for the route. - */ - public Builder addControlFilter(IntentFilter filter) { - if (filter == null) { - throw new IllegalArgumentException("filter must not be null"); - } - - if (mControlFilters == null) { - mControlFilters = new ArrayList(); - } - if (!mControlFilters.contains(filter)) { - mControlFilters.add(filter); - } - return this; - } - - /** - * Adds a list of {@link MediaControlIntent media control intent} filters for the route. - */ - public Builder addControlFilters(Collection filters) { - if (filters == null) { - throw new IllegalArgumentException("filters must not be null"); - } - - if (!filters.isEmpty()) { - for (IntentFilter filter : filters) { - addControlFilter(filter); - } - } - return this; - } - - /** - * Sets the route's playback type. - * - * @param playbackType The playback type of the route: - * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or - * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. - */ - public Builder setPlaybackType(int playbackType) { - mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType); - return this; - } - - /** - * Sets the route's playback stream. - */ - public Builder setPlaybackStream(int playbackStream) { - mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream); - return this; - } - - /** - * Sets the route's receiver device type. - * - * @param deviceType The receive device type of the route: - * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or - * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. - */ - public Builder setDeviceType(int deviceType) { - mBundle.putInt(KEY_DEVICE_TYPE, deviceType); - return this; - } - - /** - * Sets the route's current volume, or 0 if unknown. - */ - public Builder setVolume(int volume) { - mBundle.putInt(KEY_VOLUME, volume); - return this; - } - - /** - * Sets the route's maximum volume, or 0 if unknown. - */ - public Builder setVolumeMax(int volumeMax) { - mBundle.putInt(KEY_VOLUME_MAX, volumeMax); - return this; - } - - /** - * Sets the route's volume handling. - * - * @param volumeHandling how volume is handled on the route: - * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or - * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. - */ - public Builder setVolumeHandling(int volumeHandling) { - mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling); - return this; - } - - /** - * Sets the route's presentation display id, or -1 if none. - */ - public Builder setPresentationDisplayId(int presentationDisplayId) { - mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId); - return this; - } - - /** - * Sets a bundle of extras for this route descriptor. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Builder setExtras(Bundle extras) { - mBundle.putBundle(KEY_EXTRAS, extras); - return this; - } - - /** - * Sets the route's minimum client version. - * A router whose version is lower than this will not be able to connect to this route. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public Builder setMinClientVersion(int minVersion) { - mBundle.putInt(KEY_MIN_CLIENT_VERSION, minVersion); - return this; - } - - /** - * Sets the route's maximum client version. - * A router whose version is higher than this will not be able to connect to this route. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public Builder setMaxClientVersion(int maxVersion) { - mBundle.putInt(KEY_MAX_CLIENT_VERSION, maxVersion); - return this; - } - - /** - * Builds the {@link MediaRouteDescriptor media route descriptor}. - */ - public MediaRouteDescriptor build() { - if (mControlFilters != null) { - mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters); - } - if (mGroupMemberIds != null) { - mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds); - } - return new MediaRouteDescriptor(mBundle, mControlFilters); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java deleted file mode 100644 index 039627fcbc..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.os.Bundle; - -/** - * Describes the kinds of routes that the media router would like to discover - * and whether to perform active scanning. - *

    - * This object is immutable once created. - *

    - */ -public final class MediaRouteDiscoveryRequest { - private static final String KEY_SELECTOR = "selector"; - private static final String KEY_ACTIVE_SCAN = "activeScan"; - - private final Bundle mBundle; - private MediaRouteSelector mSelector; - - /** - * Creates a media route discovery request. - * - * @param selector The route selector that specifies the kinds of routes to discover. - * @param activeScan True if active scanning should be performed. - */ - public MediaRouteDiscoveryRequest(MediaRouteSelector selector, boolean activeScan) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - mBundle = new Bundle(); - mSelector = selector; - mBundle.putBundle(KEY_SELECTOR, selector.asBundle()); - mBundle.putBoolean(KEY_ACTIVE_SCAN, activeScan); - } - - private MediaRouteDiscoveryRequest(Bundle bundle) { - mBundle = bundle; - } - - /** - * Gets the route selector that specifies the kinds of routes to discover. - */ - public MediaRouteSelector getSelector() { - ensureSelector(); - return mSelector; - } - - private void ensureSelector() { - if (mSelector == null) { - mSelector = MediaRouteSelector.fromBundle(mBundle.getBundle(KEY_SELECTOR)); - if (mSelector == null) { - mSelector = MediaRouteSelector.EMPTY; - } - } - } - - /** - * Returns true if active scanning should be performed. - * - * @see MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN - */ - public boolean isActiveScan() { - return mBundle.getBoolean(KEY_ACTIVE_SCAN); - } - - /** - * Returns true if the discovery request has all of the required fields. - */ - public boolean isValid() { - ensureSelector(); - return mSelector.isValid(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof MediaRouteDiscoveryRequest) { - MediaRouteDiscoveryRequest other = (MediaRouteDiscoveryRequest)o; - return getSelector().equals(other.getSelector()) - && isActiveScan() == other.isActiveScan(); - } - return false; - } - - @Override - public int hashCode() { - return getSelector().hashCode() ^ (isActiveScan() ? 1 : 0); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("DiscoveryRequest{ selector=").append(getSelector()); - result.append(", activeScan=").append(isActiveScan()); - result.append(", isValid=").append(isValid()); - result.append(" }"); - return result.toString(); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaRouteDiscoveryRequest fromBundle(Bundle bundle) { - return bundle != null ? new MediaRouteDiscoveryRequest(bundle) : null; - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java deleted file mode 100644 index 7ea328ccfe..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.Message; - -import androidx.core.util.ObjectsCompat; - -import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback; - -/** - * Media route providers are used to publish additional media routes for - * use within an application. Media route providers may also be declared - * as a service to publish additional media routes to all applications - * in the system. - *

    - * The purpose of a media route provider is to discover media routes that satisfy - * the criteria specified by the current {@link MediaRouteDiscoveryRequest} and publish a - * {@link MediaRouteProviderDescriptor} with information about each route by calling - * {@link #setDescriptor} to notify the currently registered {@link Callback}. - *

    - * The provider should watch for changes to the discovery request by implementing - * {@link #onDiscoveryRequestChanged} and updating the set of routes that it is - * attempting to discover. It should also handle route control requests such - * as volume changes or {@link MediaControlIntent media control intents} - * by implementing {@link #onCreateRouteController} to return a {@link RouteController} - * for a particular route. - *

    - * A media route provider may be used privately within the scope of a single - * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider} - * to add it to the local {@link MediaRouter}. A media route provider may also be made - * available globally to all applications by registering a {@link MediaRouteProviderService} - * in the provider's manifest. When the media route provider is registered - * as a service, all applications that use the media router API will be able to - * discover and used the provider's routes without having to install anything else. - *

    - * This object must only be accessed on the main thread. - *

    - */ -public abstract class MediaRouteProvider { - static final int MSG_DELIVER_DESCRIPTOR_CHANGED = 1; - static final int MSG_DELIVER_DISCOVERY_REQUEST_CHANGED = 2; - - private final Context mContext; - private final ProviderMetadata mMetadata; - private final ProviderHandler mHandler = new ProviderHandler(); - - private Callback mCallback; - - private MediaRouteDiscoveryRequest mDiscoveryRequest; - private boolean mPendingDiscoveryRequestChange; - - private MediaRouteProviderDescriptor mDescriptor; - private boolean mPendingDescriptorChange; - - /** - * Creates a media route provider. - * - * @param context The context. - */ - public MediaRouteProvider(@NonNull Context context) { - this(context, null); - } - - MediaRouteProvider(Context context, ProviderMetadata metadata) { - if (context == null) { - throw new IllegalArgumentException("context must not be null"); - } - - mContext = context; - if (metadata == null) { - mMetadata = new ProviderMetadata(new ComponentName(context, getClass())); - } else { - mMetadata = metadata; - } - } - - /** - * Gets the context of the media route provider. - */ - public final Context getContext() { - return mContext; - } - - /** - * Gets the provider's handler which is associated with the main thread. - */ - public final Handler getHandler() { - return mHandler; - } - - /** - * Gets some metadata about the provider's implementation. - */ - public final ProviderMetadata getMetadata() { - return mMetadata; - } - - /** - * Sets a callback to invoke when the provider's descriptor changes. - * - * @param callback The callback to use, or null if none. - */ - public final void setCallback(@Nullable Callback callback) { - MediaRouter.checkCallingThread(); - mCallback = callback; - } - - /** - * Gets the current discovery request which informs the provider about the - * kinds of routes to discover and whether to perform active scanning. - * - * @return The current discovery request, or null if no discovery is needed at this time. - * - * @see #onDiscoveryRequestChanged - */ - @Nullable - public final MediaRouteDiscoveryRequest getDiscoveryRequest() { - return mDiscoveryRequest; - } - - /** - * Sets a discovery request to inform the provider about the kinds of - * routes that its clients would like to discover and whether to perform active scanning. - * - * @param request The discovery request, or null if no discovery is needed at this time. - * - * @see #onDiscoveryRequestChanged - */ - public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) { - MediaRouter.checkCallingThread(); - - if (ObjectsCompat.equals(mDiscoveryRequest, request)) { - return; - } - - mDiscoveryRequest = request; - if (!mPendingDiscoveryRequestChange) { - mPendingDiscoveryRequestChange = true; - mHandler.sendEmptyMessage(MSG_DELIVER_DISCOVERY_REQUEST_CHANGED); - } - } - - void deliverDiscoveryRequestChanged() { - mPendingDiscoveryRequestChange = false; - onDiscoveryRequestChanged(mDiscoveryRequest); - } - - /** - * Called by the media router when the {@link MediaRouteDiscoveryRequest discovery request} - * has changed. - *

    - * Whenever an applications calls {@link MediaRouter#addCallback} to register - * a callback, it also provides a selector to specify the kinds of routes that - * it is interested in. The media router combines all of these selectors together - * to generate a {@link MediaRouteDiscoveryRequest} and notifies each provider when a change - * occurs by calling {@link #setDiscoveryRequest} which posts a message to invoke - * this method asynchronously. - *

    - * The provider should examine the {@link MediaControlIntent media control categories} - * in the discovery request's {@link MediaRouteSelector selector} to determine what - * kinds of routes it should try to discover and whether it should perform active - * or passive scans. In many cases, the provider may be able to save power by - * determining that the selector does not contain any categories that it supports - * and it can therefore avoid performing any scans at all. - *

    - * - * @param request The new discovery request, or null if no discovery is needed at this time. - * - * @see MediaRouter#addCallback - */ - public void onDiscoveryRequestChanged(@Nullable MediaRouteDiscoveryRequest request) { - } - - /** - * Gets the provider's descriptor. - *

    - * The descriptor describes the state of the media route provider and - * the routes that it publishes. Watch for changes to the descriptor - * by registering a {@link Callback callback} with {@link #setCallback}. - *

    - * - * @return The media route provider descriptor, or null if none. - * - * @see Callback#onDescriptorChanged - */ - @Nullable - public final MediaRouteProviderDescriptor getDescriptor() { - return mDescriptor; - } - - /** - * Sets the provider's descriptor. - *

    - * The provider must call this method to notify the currently registered - * {@link Callback callback} about the change to the provider's descriptor. - *

    - * - * @param descriptor The updated route provider descriptor, or null if none. - * - * @see Callback#onDescriptorChanged - */ - public final void setDescriptor(@Nullable MediaRouteProviderDescriptor descriptor) { - MediaRouter.checkCallingThread(); - - if (mDescriptor != descriptor) { - mDescriptor = descriptor; - if (!mPendingDescriptorChange) { - mPendingDescriptorChange = true; - mHandler.sendEmptyMessage(MSG_DELIVER_DESCRIPTOR_CHANGED); - } - } - } - - void deliverDescriptorChanged() { - mPendingDescriptorChange = false; - - if (mCallback != null) { - mCallback.onDescriptorChanged(this, mDescriptor); - } - } - - /** - * Called by the media router to obtain a route controller for a particular route. - *

    - * The media router will invoke the {@link RouteController#onRelease} method of the route - * controller when it is no longer needed to allow it to free its resources. - *

    - * - * @param routeId The unique id of the route. - * @return The route controller. Returns null if there is no such route or if the route - * cannot be controlled using the route controller interface. - */ - @Nullable - public RouteController onCreateRouteController(@NonNull String routeId) { - if (routeId == null) { - throw new IllegalArgumentException("routeId cannot be null"); - } - return null; - } - - /** - * Called by the media router to obtain a route controller for a particular route which is a - * member of {@link MediaRouter.RouteGroup}. - *

    - * The media router will invoke the {@link RouteController#onRelease} method of the route - * controller when it is no longer needed to allow it to free its resources. - *

    - * - * @param routeId The unique id of the member route. - * @param routeGroupId The unique id of the route group. - * @return The route controller. Returns null if there is no such route or if the route - * cannot be controlled using the route controller interface. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - @Nullable - public RouteController onCreateRouteController(@NonNull String routeId, - @NonNull String routeGroupId) { - if (routeId == null) { - throw new IllegalArgumentException("routeId cannot be null"); - } - if (routeGroupId == null) { - throw new IllegalArgumentException("routeGroupId cannot be null"); - } - return onCreateRouteController(routeId); - } - - /** - * Describes properties of the route provider's implementation. - *

    - * This object is immutable once created. - *

    - */ - public static final class ProviderMetadata { - private final ComponentName mComponentName; - - ProviderMetadata(ComponentName componentName) { - if (componentName == null) { - throw new IllegalArgumentException("componentName must not be null"); - } - mComponentName = componentName; - } - - /** - * Gets the provider's package name. - */ - public String getPackageName() { - return mComponentName.getPackageName(); - } - - /** - * Gets the provider's component name. - */ - public ComponentName getComponentName() { - return mComponentName; - } - - @Override - public String toString() { - return "ProviderMetadata{ componentName=" - + mComponentName.flattenToShortString() + " }"; - } - } - - /** - * Provides control over a particular route. - *

    - * The media router obtains a route controller for a route whenever it needs - * to control a route. When a route is selected, the media router invokes - * the {@link #onSelect} method of its route controller. While selected, - * the media router may call other methods of the route controller to - * request that it perform certain actions to the route. When a route is - * unselected, the media router invokes the {@link #onUnselect} method of its - * route controller. When the media route no longer needs the route controller - * it will invoke the {@link #onRelease} method to allow the route controller - * to free its resources. - *

    - * There may be multiple route controllers simultaneously active for the - * same route. Each route controller will be released separately. - *

    - * All operations on the route controller are asynchronous and - * results are communicated via callbacks. - *

    - */ - public static abstract class RouteController { - /** - * Releases the route controller, allowing it to free its resources. - */ - public void onRelease() { - } - - /** - * Selects the route. - */ - public void onSelect() { - } - - /** - * Unselects the route. - */ - public void onUnselect() { - } - - /** - * Unselects the route and provides a reason. The default implementation - * calls {@link #onUnselect()}. - *

    - * The reason provided will be one of the following: - *

      - *
    • {@link MediaRouter#UNSELECT_REASON_UNKNOWN}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_STOPPED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}
    • - *
    - * - * @param reason The reason for unselecting the route. - */ - public void onUnselect(int reason) { - onUnselect(); - } - - /** - * Requests to set the volume of the route. - * - * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}. - */ - public void onSetVolume(int volume) { - } - - /** - * Requests an incremental volume update for the route. - * - * @param delta The delta to add to the current volume. - */ - public void onUpdateVolume(int delta) { - } - - /** - * Performs a {@link MediaControlIntent media control} request - * asynchronously on behalf of the route. - * - * @param intent A {@link MediaControlIntent media control intent}. - * @param callback A {@link ControlRequestCallback} to invoke with the result - * of the request, or null if no result is required. - * @return True if the controller intends to handle the request and will - * invoke the callback when finished. False if the controller will not - * handle the request and will not invoke the callback. - * - * @see MediaControlIntent - */ - public boolean onControlRequest(Intent intent, @Nullable ControlRequestCallback callback) { - return false; - } - } - - /** - * Callback which is invoked when route information becomes available or changes. - */ - public static abstract class Callback { - /** - * Called when information about a route provider and its routes changes. - * - * @param provider The media route provider that changed, never null. - * @param descriptor The new media route provider descriptor, or null if none. - */ - public void onDescriptorChanged(@NonNull MediaRouteProvider provider, - @Nullable MediaRouteProviderDescriptor descriptor) { - } - } - - private final class ProviderHandler extends Handler { - ProviderHandler() { - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_DELIVER_DESCRIPTOR_CHANGED: - deliverDescriptorChanged(); - break; - case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED: - deliverDiscoveryRequestChanged(); - break; - } - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java deleted file mode 100644 index eb1ce09e76..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.os.Bundle; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Describes the state of a media route provider and the routes that it publishes. - *

    - * This object is immutable once created using a {@link Builder} instance. - *

    - */ -public final class MediaRouteProviderDescriptor { - private static final String KEY_ROUTES = "routes"; - - private final Bundle mBundle; - private List mRoutes; - - private MediaRouteProviderDescriptor(Bundle bundle, List routes) { - mBundle = bundle; - mRoutes = routes; - } - - /** - * Gets the list of all routes that this provider has published. - */ - public List getRoutes() { - ensureRoutes(); - return mRoutes; - } - - private void ensureRoutes() { - if (mRoutes == null) { - ArrayList routeBundles = mBundle.getParcelableArrayList(KEY_ROUTES); - if (routeBundles == null || routeBundles.isEmpty()) { - mRoutes = Collections.emptyList(); - } else { - final int count = routeBundles.size(); - mRoutes = new ArrayList(count); - for (int i = 0; i < count; i++) { - mRoutes.add(MediaRouteDescriptor.fromBundle(routeBundles.get(i))); - } - } - } - } - - /** - * Returns true if the route provider descriptor and all of the routes that - * it contains have all of the required fields. - *

    - * This verification is deep. If the provider descriptor is known to be - * valid then it is not necessary to call {@link #isValid} on each of its routes. - *

    - */ - public boolean isValid() { - ensureRoutes(); - final int routeCount = mRoutes.size(); - for (int i = 0; i < routeCount; i++) { - MediaRouteDescriptor route = mRoutes.get(i); - if (route == null || !route.isValid()) { - return false; - } - } - return true; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("MediaRouteProviderDescriptor{ "); - result.append("routes=").append( - Arrays.toString(getRoutes().toArray())); - result.append(", isValid=").append(isValid()); - result.append(" }"); - return result.toString(); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaRouteProviderDescriptor fromBundle(Bundle bundle) { - return bundle != null ? new MediaRouteProviderDescriptor(bundle, null) : null; - } - - /** - * Builder for {@link MediaRouteProviderDescriptor media route provider descriptors}. - */ - public static final class Builder { - private final Bundle mBundle; - private ArrayList mRoutes; - - /** - * Creates an empty media route provider descriptor builder. - */ - public Builder() { - mBundle = new Bundle(); - } - - /** - * Creates a media route provider descriptor builder whose initial contents are - * copied from an existing descriptor. - */ - public Builder(MediaRouteProviderDescriptor descriptor) { - if (descriptor == null) { - throw new IllegalArgumentException("descriptor must not be null"); - } - - mBundle = new Bundle(descriptor.mBundle); - - descriptor.ensureRoutes(); - if (!descriptor.mRoutes.isEmpty()) { - mRoutes = new ArrayList(descriptor.mRoutes); - } - } - - /** - * Adds a route. - */ - public Builder addRoute(MediaRouteDescriptor route) { - if (route == null) { - throw new IllegalArgumentException("route must not be null"); - } - - if (mRoutes == null) { - mRoutes = new ArrayList(); - } else if (mRoutes.contains(route)) { - throw new IllegalArgumentException("route descriptor already added"); - } - mRoutes.add(route); - return this; - } - - /** - * Adds a list of routes. - */ - public Builder addRoutes(Collection routes) { - if (routes == null) { - throw new IllegalArgumentException("routes must not be null"); - } - - if (!routes.isEmpty()) { - for (MediaRouteDescriptor route : routes) { - addRoute(route); - } - } - return this; - } - - /** - * Sets the list of routes. - */ - Builder setRoutes(Collection routes) { - if (routes == null || routes.isEmpty()) { - mRoutes = null; - mBundle.remove(KEY_ROUTES); - } else { - mRoutes = new ArrayList<>(routes); - } - return this; - } - - /** - * Builds the {@link MediaRouteProviderDescriptor media route provider descriptor}. - */ - public MediaRouteProviderDescriptor build() { - if (mRoutes != null) { - final int count = mRoutes.size(); - ArrayList routeBundles = new ArrayList(count); - for (int i = 0; i < count; i++) { - routeBundles.add(mRoutes.get(i).asBundle()); - } - mBundle.putParcelableArrayList(KEY_ROUTES, routeBundles); - } - return new MediaRouteProviderDescriptor(mBundle, mRoutes); - } - } -} \ No newline at end of file diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java deleted file mode 100644 index 6be9343e9c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.content.Intent; -import android.os.Messenger; - -/** - * Defines the communication protocol for media route provider services. - */ -abstract class MediaRouteProviderProtocol { - /** - * The {@link Intent} that must be declared as handled by the service. - * Put this in your manifest. - */ - public static final String SERVICE_INTERFACE = - "android.media.MediaRouteProviderService"; - - /* - * Messages sent from the client to the service. - * DO NOT RENUMBER THESE! - */ - - /** (client v1) - * Register client. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : client version - */ - public static final int CLIENT_MSG_REGISTER = 1; - - /** (client v1) - * Unregister client. - * - replyTo : client messenger - * - arg1 : request id - */ - public static final int CLIENT_MSG_UNREGISTER = 2; - - /** (client v1) - * Create route controller. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - * - CLIENT_DATA_ROUTE_ID : route id string - */ - public static final int CLIENT_MSG_CREATE_ROUTE_CONTROLLER = 3; - - /** (client v1) - * Release route controller. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - */ - public static final int CLIENT_MSG_RELEASE_ROUTE_CONTROLLER = 4; - - /** (client v1) - * Select route. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - */ - public static final int CLIENT_MSG_SELECT_ROUTE = 5; - - /** (client v1) - * Unselect route. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - */ - public static final int CLIENT_MSG_UNSELECT_ROUTE = 6; - - /** (client v1) - * Set route volume. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - * - CLIENT_DATA_VOLUME : volume integer - */ - public static final int CLIENT_MSG_SET_ROUTE_VOLUME = 7; - - /** (client v1) - * Update route volume. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - * - CLIENT_DATA_VOLUME : volume delta integer - */ - public static final int CLIENT_MSG_UPDATE_ROUTE_VOLUME = 8; - - /** (client v1) - * Route control request. - * - replyTo : client messenger - * - arg1 : request id - * - arg2 : route controller id - * - obj : media control intent - */ - public static final int CLIENT_MSG_ROUTE_CONTROL_REQUEST = 9; - - /** (client v1) - * Sets the discovery request. - * - replyTo : client messenger - * - arg1 : request id - * - obj : discovery request bundle, or null if none - */ - public static final int CLIENT_MSG_SET_DISCOVERY_REQUEST = 10; - - public static final String CLIENT_DATA_ROUTE_ID = "routeId"; - public static final String CLIENT_DATA_ROUTE_LIBRARY_GROUP = "routeGroupId"; - public static final String CLIENT_DATA_VOLUME = "volume"; - public static final String CLIENT_DATA_UNSELECT_REASON = "unselectReason"; - - /* - * Messages sent from the service to the client. - * DO NOT RENUMBER THESE! - */ - - /** (service v1) - * Generic failure sent in response to any unrecognized or malformed request. - * - arg1 : request id - */ - public static final int SERVICE_MSG_GENERIC_FAILURE = 0; - - /** (service v1) - * Generic failure sent in response to a successful message. - * - arg1 : request id - */ - public static final int SERVICE_MSG_GENERIC_SUCCESS = 1; - - /** (service v1) - * Registration succeeded. - * - arg1 : request id - * - arg2 : server version - * - obj : route provider descriptor bundle, or null - */ - public static final int SERVICE_MSG_REGISTERED = 2; - - /** (service v1) - * Route control request success result. - * - arg1 : request id - * - obj : result data bundle, or null - */ - public static final int SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED = 3; - - /** (service v1) - * Route control request failure result. - * - arg1 : request id - * - obj : result data bundle, or null - * - SERVICE_DATA_ERROR: error message - */ - public static final int SERVICE_MSG_CONTROL_REQUEST_FAILED = 4; - - /** (service v1) - * Route provider descriptor changed. (unsolicited event) - * - arg1 : reserved (0) - * - obj : route provider descriptor bundle, or null - */ - public static final int SERVICE_MSG_DESCRIPTOR_CHANGED = 5; - - public static final String SERVICE_DATA_ERROR = "error"; - - /* - * Recognized client version numbers. (Reserved for future use.) - * DO NOT RENUMBER THESE! - */ - - /** - * The client version used from the beginning. - */ - public static final int CLIENT_VERSION_1 = 1; - - /** - * The client version used from support library v24.1.0. - */ - public static final int CLIENT_VERSION_2 = 2; - - /** - * The current client version. - */ - public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_2; - - /* - * Recognized server version numbers. (Reserved for future use.) - * DO NOT RENUMBER THESE! - */ - - /** - * The service version used from the beginning. - */ - public static final int SERVICE_VERSION_1 = 1; - - /** - * The current service version. - */ - public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1; - - static final int CLIENT_VERSION_START = CLIENT_VERSION_1; - - /** - * Returns true if the messenger object is valid. - *

    - * The messenger constructor and unparceling code does not check whether the - * provided IBinder is a valid IMessenger object. As a result, it's possible - * for a peer to send an invalid IBinder that will result in crashes downstream. - * This method checks that the messenger is in a valid state. - *

    - */ - public static boolean isValidRemoteMessenger(Messenger messenger) { - try { - return messenger != null && messenger.getBinder() != null; - } catch (NullPointerException ex) { - // If the messenger was constructed with a binder interface other than - // IMessenger then the call to getBinder() will crash with an NPE. - return false; - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java deleted file mode 100644 index a186fee47c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_DATA_ROUTE_LIBRARY_GROUP; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_DATA_UNSELECT_REASON; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_CREATE_ROUTE_CONTROLLER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_ROUTE_CONTROL_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SELECT_ROUTE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SET_DISCOVERY_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SET_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UNREGISTER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UNSELECT_ROUTE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UPDATE_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_1; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_CONTROL_REQUEST_FAILED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_DESCRIPTOR_CHANGED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_GENERIC_FAILURE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_GENERIC_SUCCESS; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_REGISTERED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_VERSION_CURRENT; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .isValidRemoteMessenger; - -import android.app.Service; -import android.content.Intent; -import android.os.Bundle; -import android.os.DeadObjectException; -import android.os.Handler; -import android.os.IBinder; -import android.os.IBinder.DeathRecipient; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.VisibleForTesting; -import androidx.core.util.ObjectsCompat; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -/** - * Base class for media route provider services. - *

    - * A media router will bind to media route provider services when a callback is added via - * {@link MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} with a discovery - * flag: {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}, - * {@link MediaRouter#CALLBACK_FLAG_FORCE_DISCOVERY}, or - * {@link MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN}, and will unbind when the callback - * is removed via {@link MediaRouter#removeCallback(MediaRouter.Callback)}. - *

    - * To implement your own media route provider service, extend this class and - * override the {@link #onCreateMediaRouteProvider} method to return an - * instance of your {@link MediaRouteProvider}. - *

    - * Declare your media route provider service in your application manifest - * like this: - *

    - *
    - *   <service android:name=".MyMediaRouteProviderService"
    - *           android:label="@string/my_media_route_provider_service">
    - *       <intent-filter>
    - *           <action android:name="android.media.MediaRouteProviderService" />
    - *       </intent-filter>
    - *   </service>
    - * 
    - */ -public abstract class MediaRouteProviderService extends Service { - static final String TAG = "MediaRouteProviderSrv"; // max. 23 chars - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private final ArrayList mClients = new ArrayList(); - private final ReceiveHandler mReceiveHandler; - private final Messenger mReceiveMessenger; - final PrivateHandler mPrivateHandler; - private final ProviderCallback mProviderCallback; - - MediaRouteProvider mProvider; - private MediaRouteDiscoveryRequest mCompositeDiscoveryRequest; - - /** - * The {@link Intent} that must be declared as handled by the service. - * Put this in your manifest. - */ - public static final String SERVICE_INTERFACE = MediaRouteProviderProtocol.SERVICE_INTERFACE; - - /* - * Private messages used internally. (Yes, you can renumber these.) - */ - - static final int PRIVATE_MSG_CLIENT_DIED = 1; - - /** - * Creates a media route provider service. - */ - public MediaRouteProviderService() { - mReceiveHandler = new ReceiveHandler(this); - mReceiveMessenger = new Messenger(mReceiveHandler); - mPrivateHandler = new PrivateHandler(); - mProviderCallback = new ProviderCallback(); - } - - /** - * Called by the system when it is time to create the media route provider. - * - * @return The media route provider offered by this service, or null if - * this service has decided not to offer a media route provider. - */ - public abstract MediaRouteProvider onCreateMediaRouteProvider(); - - /** - * Gets the media route provider offered by this service. - * - * @return The media route provider offered by this service, or null if - * it has not yet been created. - * - * @see #onCreateMediaRouteProvider() - */ - public MediaRouteProvider getMediaRouteProvider() { - return mProvider; - } - - @Override - public IBinder onBind(Intent intent) { - if (intent.getAction().equals(SERVICE_INTERFACE)) { - if (mProvider == null) { - MediaRouteProvider provider = onCreateMediaRouteProvider(); - if (provider != null) { - String providerPackage = provider.getMetadata().getPackageName(); - if (!providerPackage.equals(getPackageName())) { - throw new IllegalStateException("onCreateMediaRouteProvider() returned " - + "a provider whose package name does not match the package " - + "name of the service. A media route provider service can " - + "only export its own media route providers. " - + "Provider package name: " + providerPackage - + ". Service package name: " + getPackageName() + "."); - } - mProvider = provider; - mProvider.setCallback(mProviderCallback); - } - } - if (mProvider != null) { - return mReceiveMessenger.getBinder(); - } - } - return null; - } - - @Override - public boolean onUnbind(Intent intent) { - if (mProvider != null) { - mProvider.setCallback(null); - } - return super.onUnbind(intent); - } - - boolean onRegisterClient(Messenger messenger, int requestId, int version) { - if (version >= CLIENT_VERSION_1) { - int index = findClient(messenger); - if (index < 0) { - ClientRecord client = new ClientRecord(messenger, version); - if (client.register()) { - mClients.add(client); - if (DEBUG) { - Log.d(TAG, client + ": Registered, version=" + version); - } - if (requestId != 0) { - MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor(); - sendReply(messenger, SERVICE_MSG_REGISTERED, - requestId, SERVICE_VERSION_CURRENT, - createDescriptorBundleForClientVersion(descriptor, - client.mVersion), null); - } - return true; - } - } - } - return false; - } - - boolean onUnregisterClient(Messenger messenger, int requestId) { - int index = findClient(messenger); - if (index >= 0) { - ClientRecord client = mClients.remove(index); - if (DEBUG) { - Log.d(TAG, client + ": Unregistered"); - } - client.dispose(); - sendGenericSuccess(messenger, requestId); - return true; - } - return false; - } - - void onBinderDied(Messenger messenger) { - int index = findClient(messenger); - if (index >= 0) { - ClientRecord client = mClients.remove(index); - if (DEBUG) { - Log.d(TAG, client + ": Binder died"); - } - client.dispose(); - } - } - - boolean onCreateRouteController(Messenger messenger, int requestId, - int controllerId, String routeId, String routeGroupId) { - ClientRecord client = getClient(messenger); - if (client != null) { - if (client.createRouteController(routeId, routeGroupId, controllerId)) { - if (DEBUG) { - Log.d(TAG, client + ": Route controller created, controllerId=" + controllerId - + ", routeId=" + routeId + ", routeGroupId=" + routeGroupId); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onReleaseRouteController(Messenger messenger, int requestId, - int controllerId) { - ClientRecord client = getClient(messenger); - if (client != null) { - if (client.releaseRouteController(controllerId)) { - if (DEBUG) { - Log.d(TAG, client + ": Route controller released" - + ", controllerId=" + controllerId); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onSelectRoute(Messenger messenger, int requestId, - int controllerId) { - ClientRecord client = getClient(messenger); - if (client != null) { - MediaRouteProvider.RouteController controller = - client.getRouteController(controllerId); - if (controller != null) { - controller.onSelect(); - if (DEBUG) { - Log.d(TAG, client + ": Route selected" - + ", controllerId=" + controllerId); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onUnselectRoute(Messenger messenger, int requestId, - int controllerId, int reason) { - ClientRecord client = getClient(messenger); - if (client != null) { - MediaRouteProvider.RouteController controller = - client.getRouteController(controllerId); - if (controller != null) { - controller.onUnselect(reason); - if (DEBUG) { - Log.d(TAG, client + ": Route unselected" - + ", controllerId=" + controllerId); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onSetRouteVolume(Messenger messenger, int requestId, - int controllerId, int volume) { - ClientRecord client = getClient(messenger); - if (client != null) { - MediaRouteProvider.RouteController controller = - client.getRouteController(controllerId); - if (controller != null) { - controller.onSetVolume(volume); - if (DEBUG) { - Log.d(TAG, client + ": Route volume changed" - + ", controllerId=" + controllerId + ", volume=" + volume); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onUpdateRouteVolume(Messenger messenger, int requestId, - int controllerId, int delta) { - ClientRecord client = getClient(messenger); - if (client != null) { - MediaRouteProvider.RouteController controller = - client.getRouteController(controllerId); - if (controller != null) { - controller.onUpdateVolume(delta); - if (DEBUG) { - Log.d(TAG, client + ": Route volume updated" - + ", controllerId=" + controllerId + ", delta=" + delta); - } - sendGenericSuccess(messenger, requestId); - return true; - } - } - return false; - } - - boolean onRouteControlRequest(final Messenger messenger, final int requestId, - final int controllerId, final Intent intent) { - final ClientRecord client = getClient(messenger); - if (client != null) { - MediaRouteProvider.RouteController controller = - client.getRouteController(controllerId); - if (controller != null) { - MediaRouter.ControlRequestCallback callback = null; - if (requestId != 0) { - callback = new MediaRouter.ControlRequestCallback() { - @Override - public void onResult(Bundle data) { - if (DEBUG) { - Log.d(TAG, client + ": Route control request succeeded" - + ", controllerId=" + controllerId - + ", intent=" + intent - + ", data=" + data); - } - if (findClient(messenger) >= 0) { - sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED, - requestId, 0, data, null); - } - } - - @Override - public void onError(String error, Bundle data) { - if (DEBUG) { - Log.d(TAG, client + ": Route control request failed" - + ", controllerId=" + controllerId - + ", intent=" + intent - + ", error=" + error + ", data=" + data); - } - if (findClient(messenger) >= 0) { - if (error != null) { - Bundle bundle = new Bundle(); - bundle.putString(SERVICE_DATA_ERROR, error); - sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, - requestId, 0, data, bundle); - } else { - sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED, - requestId, 0, data, null); - } - } - } - }; - } - if (controller.onControlRequest(intent, callback)) { - if (DEBUG) { - Log.d(TAG, client + ": Route control request delivered" - + ", controllerId=" + controllerId + ", intent=" + intent); - } - return true; - } - } - } - return false; - } - - boolean onSetDiscoveryRequest(Messenger messenger, int requestId, - MediaRouteDiscoveryRequest request) { - ClientRecord client = getClient(messenger); - if (client != null) { - boolean actuallyChanged = client.setDiscoveryRequest(request); - if (DEBUG) { - Log.d(TAG, client + ": Set discovery request, request=" + request - + ", actuallyChanged=" + actuallyChanged - + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest); - } - sendGenericSuccess(messenger, requestId); - return true; - } - return false; - } - - void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) { - final int count = mClients.size(); - for (int i = 0; i < count; i++) { - ClientRecord client = mClients.get(i); - sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0, - createDescriptorBundleForClientVersion(descriptor, client.mVersion), null); - if (DEBUG) { - Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor); - } - } - } - - @VisibleForTesting - static Bundle createDescriptorBundleForClientVersion(MediaRouteProviderDescriptor descriptor, - int clientVersion) { - if (descriptor == null) { - return null; - } - MediaRouteProviderDescriptor.Builder builder = - new MediaRouteProviderDescriptor.Builder(descriptor); - builder.setRoutes(null); - for (MediaRouteDescriptor route : descriptor.getRoutes()) { - if (clientVersion >= route.getMinClientVersion() - && clientVersion <= route.getMaxClientVersion()) { - builder.addRoute(route); - } - } - return builder.build().asBundle(); - } - - boolean updateCompositeDiscoveryRequest() { - MediaRouteDiscoveryRequest composite = null; - MediaRouteSelector.Builder selectorBuilder = null; - boolean activeScan = false; - final int count = mClients.size(); - for (int i = 0; i < count; i++) { - MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest; - if (request != null - && (!request.getSelector().isEmpty() || request.isActiveScan())) { - activeScan |= request.isActiveScan(); - if (composite == null) { - composite = request; - } else { - if (selectorBuilder == null) { - selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector()); - } - selectorBuilder.addSelector(request.getSelector()); - } - } - } - if (selectorBuilder != null) { - composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan); - } - if (!ObjectsCompat.equals(mCompositeDiscoveryRequest, composite)) { - mCompositeDiscoveryRequest = composite; - mProvider.setDiscoveryRequest(composite); - return true; - } - return false; - } - - private ClientRecord getClient(Messenger messenger) { - int index = findClient(messenger); - return index >= 0 ? mClients.get(index) : null; - } - - int findClient(Messenger messenger) { - final int count = mClients.size(); - for (int i = 0; i < count; i++) { - ClientRecord client = mClients.get(i); - if (client.hasMessenger(messenger)) { - return i; - } - } - return -1; - } - - static void sendGenericFailure(Messenger messenger, int requestId) { - if (requestId != 0) { - sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null); - } - } - - private static void sendGenericSuccess(Messenger messenger, int requestId) { - if (requestId != 0) { - sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null); - } - } - - static void sendReply(Messenger messenger, int what, - int requestId, int arg, Object obj, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - msg.arg1 = requestId; - msg.arg2 = arg; - msg.obj = obj; - msg.setData(data); - try { - messenger.send(msg); - } catch (DeadObjectException ex) { - // The client died. - } catch (RemoteException ex) { - Log.e(TAG, "Could not send message to " + getClientId(messenger), ex); - } - } - - static String getClientId(Messenger messenger) { - return "Client connection " + messenger.getBinder().toString(); - } - - private final class PrivateHandler extends Handler { - PrivateHandler() { - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case PRIVATE_MSG_CLIENT_DIED: - onBinderDied((Messenger)msg.obj); - break; - } - } - } - - private final class ProviderCallback extends MediaRouteProvider.Callback { - ProviderCallback() { - } - - @Override - public void onDescriptorChanged(MediaRouteProvider provider, - MediaRouteProviderDescriptor descriptor) { - sendDescriptorChanged(descriptor); - } - } - - private final class ClientRecord implements DeathRecipient { - public final Messenger mMessenger; - public final int mVersion; - public MediaRouteDiscoveryRequest mDiscoveryRequest; - - private final SparseArray mControllers = - new SparseArray(); - - public ClientRecord(Messenger messenger, int version) { - mMessenger = messenger; - mVersion = version; - } - - public boolean register() { - try { - mMessenger.getBinder().linkToDeath(this, 0); - return true; - } catch (RemoteException ex) { - binderDied(); - } - return false; - } - - public void dispose() { - int count = mControllers.size(); - for (int i = 0; i < count; i++) { - mControllers.valueAt(i).onRelease(); - } - mControllers.clear(); - - mMessenger.getBinder().unlinkToDeath(this, 0); - - setDiscoveryRequest(null); - } - - public boolean hasMessenger(Messenger other) { - return mMessenger.getBinder() == other.getBinder(); - } - - public boolean createRouteController(String routeId, String routeGroupId, - int controllerId) { - if (mControllers.indexOfKey(controllerId) < 0) { - MediaRouteProvider.RouteController controller = routeGroupId == null - ? mProvider.onCreateRouteController(routeId) - : mProvider.onCreateRouteController(routeId, routeGroupId); - if (controller != null) { - mControllers.put(controllerId, controller); - return true; - } - } - return false; - } - - public boolean releaseRouteController(int controllerId) { - MediaRouteProvider.RouteController controller = mControllers.get(controllerId); - if (controller != null) { - mControllers.remove(controllerId); - controller.onRelease(); - return true; - } - return false; - } - - public MediaRouteProvider.RouteController getRouteController(int controllerId) { - return mControllers.get(controllerId); - } - - public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) { - if (!ObjectsCompat.equals(mDiscoveryRequest, request)) { - mDiscoveryRequest = request; - return updateCompositeDiscoveryRequest(); - } - return false; - } - - // Runs on a binder thread. - @Override - public void binderDied() { - mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget(); - } - - @Override - public String toString() { - return getClientId(mMessenger); - } - } - - /** - * Handler that receives messages from clients. - *

    - * This inner class is static and only retains a weak reference to the service - * to prevent the service from being leaked in case one of the clients is holding an - * active reference to the server's messenger. - *

    - * This handler should not be used to handle any messages other than those - * that come from the client. - *

    - */ - private static final class ReceiveHandler extends Handler { - private final WeakReference mServiceRef; - - public ReceiveHandler(MediaRouteProviderService service) { - mServiceRef = new WeakReference(service); - } - - @Override - public void handleMessage(Message msg) { - final Messenger messenger = msg.replyTo; - if (isValidRemoteMessenger(messenger)) { - final int what = msg.what; - final int requestId = msg.arg1; - final int arg = msg.arg2; - final Object obj = msg.obj; - final Bundle data = msg.peekData(); - if (!processMessage(what, messenger, requestId, arg, obj, data)) { - if (DEBUG) { - Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what - + ", requestId=" + requestId + ", arg=" + arg - + ", obj=" + obj + ", data=" + data); - } - sendGenericFailure(messenger, requestId); - } - } else { - if (DEBUG) { - Log.d(TAG, "Ignoring message without valid reply messenger."); - } - } - } - - private boolean processMessage(int what, - Messenger messenger, int requestId, int arg, Object obj, Bundle data) { - MediaRouteProviderService service = mServiceRef.get(); - if (service != null) { - switch (what) { - case CLIENT_MSG_REGISTER: - return service.onRegisterClient(messenger, requestId, arg); - - case CLIENT_MSG_UNREGISTER: - return service.onUnregisterClient(messenger, requestId); - - case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: { - String routeId = data.getString(CLIENT_DATA_ROUTE_ID); - String routeGroupId = data.getString(CLIENT_DATA_ROUTE_LIBRARY_GROUP); - if (routeId != null) { - return service.onCreateRouteController( - messenger, requestId, arg, routeId, routeGroupId); - } - break; - } - - case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER: - return service.onReleaseRouteController(messenger, requestId, arg); - - case CLIENT_MSG_SELECT_ROUTE: - return service.onSelectRoute(messenger, requestId, arg); - - case CLIENT_MSG_UNSELECT_ROUTE: - int reason = data == null ? - MediaRouter.UNSELECT_REASON_UNKNOWN - : data.getInt(CLIENT_DATA_UNSELECT_REASON, - MediaRouter.UNSELECT_REASON_UNKNOWN); - return service.onUnselectRoute(messenger, requestId, arg, reason); - - case CLIENT_MSG_SET_ROUTE_VOLUME: { - int volume = data.getInt(CLIENT_DATA_VOLUME, -1); - if (volume >= 0) { - return service.onSetRouteVolume( - messenger, requestId, arg, volume); - } - break; - } - - case CLIENT_MSG_UPDATE_ROUTE_VOLUME: { - int delta = data.getInt(CLIENT_DATA_VOLUME, 0); - if (delta != 0) { - return service.onUpdateRouteVolume( - messenger, requestId, arg, delta); - } - break; - } - - case CLIENT_MSG_ROUTE_CONTROL_REQUEST: - if (obj instanceof Intent) { - return service.onRouteControlRequest( - messenger, requestId, arg, (Intent)obj); - } - break; - - case CLIENT_MSG_SET_DISCOVERY_REQUEST: { - if (obj == null || obj instanceof Bundle) { - MediaRouteDiscoveryRequest request = - MediaRouteDiscoveryRequest.fromBundle((Bundle)obj); - return service.onSetDiscoveryRequest( - messenger, requestId, - request != null && request.isValid() ? request : null); - } - } - } - } - return false; - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java deleted file mode 100644 index f20dcc0974..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.content.IntentFilter; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Describes the capabilities of routes that applications would like to discover and use. - *

    - * This object is immutable once created using a {@link Builder} instance. - *

    - * - *

    Example

    - *
    - * MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
    - *         .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
    - *         .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
    - *         .build();
    - *
    - * MediaRouter router = MediaRouter.getInstance(context);
    - * router.addCallback(selector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
    - * 
    - */ -public final class MediaRouteSelector { - static final String KEY_CONTROL_CATEGORIES = "controlCategories"; - - private final Bundle mBundle; - List mControlCategories; - - /** - * An empty media route selector that will not match any routes. - */ - public static final MediaRouteSelector EMPTY = new MediaRouteSelector(new Bundle(), null); - - MediaRouteSelector(Bundle bundle, List controlCategories) { - mBundle = bundle; - mControlCategories = controlCategories; - } - - /** - * Gets the list of {@link MediaControlIntent media control categories} in the selector. - * - * @return The list of categories. - */ - public List getControlCategories() { - ensureControlCategories(); - return mControlCategories; - } - - void ensureControlCategories() { - if (mControlCategories == null) { - mControlCategories = mBundle.getStringArrayList(KEY_CONTROL_CATEGORIES); - if (mControlCategories == null || mControlCategories.isEmpty()) { - mControlCategories = Collections.emptyList(); - } - } - } - - /** - * Returns true if the selector contains the specified category. - * - * @param category The category to check. - * @return True if the category is present. - */ - public boolean hasControlCategory(String category) { - if (category != null) { - ensureControlCategories(); - final int categoryCount = mControlCategories.size(); - for (int i = 0; i < categoryCount; i++) { - if (mControlCategories.get(i).equals(category)) { - return true; - } - } - } - return false; - } - - /** - * Returns true if the selector matches at least one of the specified control filters. - * - * @param filters The list of control filters to consider. - * @return True if a match is found. - */ - public boolean matchesControlFilters(List filters) { - if (filters != null) { - ensureControlCategories(); - final int categoryCount = mControlCategories.size(); - if (categoryCount != 0) { - final int filterCount = filters.size(); - for (int i = 0; i < filterCount; i++) { - final IntentFilter filter = filters.get(i); - if (filter != null) { - for (int j = 0; j < categoryCount; j++) { - if (filter.hasCategory(mControlCategories.get(j))) { - return true; - } - } - } - } - } - } - return false; - } - - /** - * Returns true if this selector contains all of the capabilities described - * by the specified selector. - * - * @param selector The selector to be examined. - * @return True if this selector contains all of the capabilities described - * by the specified selector. - */ - public boolean contains(MediaRouteSelector selector) { - if (selector != null) { - ensureControlCategories(); - selector.ensureControlCategories(); - return mControlCategories.containsAll(selector.mControlCategories); - } - return false; - } - - /** - * Returns true if the selector does not specify any capabilities. - */ - public boolean isEmpty() { - ensureControlCategories(); - return mControlCategories.isEmpty(); - } - - /** - * Returns true if the selector has all of the required fields. - */ - public boolean isValid() { - ensureControlCategories(); - if (mControlCategories.contains(null)) { - return false; - } - return true; - } - - @Override - public boolean equals(Object o) { - if (o instanceof MediaRouteSelector) { - MediaRouteSelector other = (MediaRouteSelector)o; - ensureControlCategories(); - other.ensureControlCategories(); - return mControlCategories.equals(other.mControlCategories); - } - return false; - } - - @Override - public int hashCode() { - ensureControlCategories(); - return mControlCategories.hashCode(); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("MediaRouteSelector{ "); - result.append("controlCategories=").append( - Arrays.toString(getControlCategories().toArray())); - result.append(" }"); - return result.toString(); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaRouteSelector fromBundle(@Nullable Bundle bundle) { - return bundle != null ? new MediaRouteSelector(bundle, null) : null; - } - - /** - * Builder for {@link MediaRouteSelector media route selectors}. - */ - public static final class Builder { - private ArrayList mControlCategories; - - /** - * Creates an empty media route selector builder. - */ - public Builder() { - } - - /** - * Creates a media route selector descriptor builder whose initial contents are - * copied from an existing selector. - */ - public Builder(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - selector.ensureControlCategories(); - if (!selector.mControlCategories.isEmpty()) { - mControlCategories = new ArrayList(selector.mControlCategories); - } - } - - /** - * Adds a {@link MediaControlIntent media control category} to the builder. - * - * @param category The category to add to the set of desired capabilities, such as - * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}. - * @return The builder instance for chaining. - */ - @NonNull - public Builder addControlCategory(@NonNull String category) { - if (category == null) { - throw new IllegalArgumentException("category must not be null"); - } - - if (mControlCategories == null) { - mControlCategories = new ArrayList(); - } - if (!mControlCategories.contains(category)) { - mControlCategories.add(category); - } - return this; - } - - /** - * Adds a list of {@link MediaControlIntent media control categories} to the builder. - * - * @param categories The list categories to add to the set of desired capabilities, - * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}. - * @return The builder instance for chaining. - */ - @NonNull - public Builder addControlCategories(@NonNull Collection categories) { - if (categories == null) { - throw new IllegalArgumentException("categories must not be null"); - } - - if (!categories.isEmpty()) { - for (String category : categories) { - addControlCategory(category); - } - } - return this; - } - - /** - * Adds the contents of an existing media route selector to the builder. - * - * @param selector The media route selector whose contents are to be added. - * @return The builder instance for chaining. - */ - @NonNull - public Builder addSelector(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - - addControlCategories(selector.getControlCategories()); - return this; - } - - /** - * Builds the {@link MediaRouteSelector media route selector}. - */ - @NonNull - public MediaRouteSelector build() { - if (mControlCategories == null) { - return EMPTY; - } - Bundle bundle = new Bundle(); - bundle.putStringArrayList(KEY_CONTROL_CATEGORIES, mControlCategories); - return new MediaRouteSelector(bundle, mControlCategories); - } - } -} \ No newline at end of file diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java deleted file mode 100644 index 4b56b19ff1..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java +++ /dev/null @@ -1,3000 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentSender; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.support.v4.media.session.MediaSessionCompat; -import android.text.TextUtils; -import android.util.Log; -import android.view.Display; - -import androidx.core.app.ActivityManagerCompat; -import androidx.core.hardware.display.DisplayManagerCompat; -import androidx.core.util.Pair; -import androidx.media.VolumeProviderCompat; - -import com.android.support.mediarouter.media.MediaRouteProvider.ProviderMetadata; -import com.android.support.mediarouter.media.MediaRouteProvider.RouteController; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -/** - * MediaRouter allows applications to control the routing of media channels - * and streams from the current device to external speakers and destination devices. - *

    - * A MediaRouter instance is retrieved through {@link #getInstance}. Applications - * can query the media router about the currently selected route and its capabilities - * to determine how to send content to the route's destination. Applications can - * also {@link RouteInfo#sendControlRequest send control requests} to the route - * to ask the route's destination to perform certain remote control functions - * such as playing media. - *

    - * See also {@link MediaRouteProvider} for information on how an application - * can publish new media routes to the media router. - *

    - * The media router API is not thread-safe; all interactions with it must be - * done from the main thread of the process. - *

    - */ -public final class MediaRouter { - static final String TAG = "MediaRouter"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - /** - * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} - * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route - * was unselected is unknown. - */ - public static final int UNSELECT_REASON_UNKNOWN = 0; - /** - * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} - * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed - * the disconnect button to disconnect and keep playing. - *

    - * - * @see MediaRouteDescriptor#canDisconnectAndKeepPlaying() - */ - public static final int UNSELECT_REASON_DISCONNECTED = 1; - /** - * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} - * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed - * the stop casting button. - */ - public static final int UNSELECT_REASON_STOPPED = 2; - /** - * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)} - * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected - * a different route. - */ - public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; - - // Maintains global media router state for the process. - // This field is initialized in MediaRouter.getInstance() before any - // MediaRouter objects are instantiated so it is guaranteed to be - // valid whenever any instance method is invoked. - static GlobalMediaRouter sGlobal; - - // Context-bound state of the media router. - final Context mContext; - final ArrayList mCallbackRecords = new ArrayList(); - - @IntDef(flag = true, - value = { - CALLBACK_FLAG_PERFORM_ACTIVE_SCAN, - CALLBACK_FLAG_REQUEST_DISCOVERY, - CALLBACK_FLAG_UNFILTERED_EVENTS - } - ) - @Retention(RetentionPolicy.SOURCE) - private @interface CallbackFlags {} - - /** - * Flag for {@link #addCallback}: Actively scan for routes while this callback - * is registered. - *

    - * When this flag is specified, the media router will actively scan for new - * routes. Certain routes, such as wifi display routes, may not be discoverable - * except when actively scanning. This flag is typically used when the route picker - * dialog has been opened by the user to ensure that the route information is - * up to date. - *

    - * Active scanning may consume a significant amount of power and may have intrusive - * effects on wireless connectivity. Therefore it is important that active scanning - * only be requested when it is actually needed to satisfy a user request to - * discover and select a new route. - *

    - * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing - * active scans is much more expensive than a normal discovery request. - *

    - * - * @see #CALLBACK_FLAG_REQUEST_DISCOVERY - */ - public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; - - /** - * Flag for {@link #addCallback}: Do not filter route events. - *

    - * When this flag is specified, the callback will be invoked for events that affect any - * route even if they do not match the callback's filter. - *

    - */ - public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; - - /** - * Flag for {@link #addCallback}: Request passive route discovery while this - * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}. - *

    - * When this flag is specified, the media router will try to discover routes. - * Although route discovery is intended to be efficient, checking for new routes may - * result in some network activity and could slowly drain the battery. Therefore - * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when - * they are running in the foreground and would like to provide the user with the - * option of connecting to new routes. - *

    - * Applications should typically add a callback using this flag in the - * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} - * method and remove it in the {@link android.app.Activity#onStop onStop} method. - * The {@link androidx.mediarouter.app.MediaRouteDiscoveryFragment} fragment may - * also be used for this purpose. - *

    - * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag - * will be ignored. Refer to - * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. - *

    - * - * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment - */ - public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; - - /** - * Flag for {@link #addCallback}: Request passive route discovery while this - * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}. - *

    - * This flag has a significant performance impact on low-RAM devices - * since it may cause many media route providers to be started simultaneously. - * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid - * performing passive discovery on these devices altogether. Refer to - * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. - *

    - * - * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment - */ - public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; - - /** - * Flag for {@link #isRouteAvailable}: Ignore the default route. - *

    - * This flag is used to determine whether a matching non-default route is available. - * This constraint may be used to decide whether to offer the route chooser dialog - * to the user. There is no point offering the chooser if there are no - * non-default choices. - *

    - */ - public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; - - /** - * Flag for {@link #isRouteAvailable}: Require an actual route to be matched. - *

    - * If this flag is not set, then {@link #isRouteAvailable} will return true - * if it is possible to discover a matching route even if discovery is not in - * progress or if no matching route has yet been found. This feature is used to - * save resources by removing the need to perform passive route discovery on - * {@link ActivityManager#isLowRamDevice low-RAM devices}. - *

    - * If this flag is set, then {@link #isRouteAvailable} will only return true if - * a matching route has actually been discovered. - *

    - */ - public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1; - - private MediaRouter(Context context) { - mContext = context; - } - - /** - * Gets an instance of the media router service associated with the context. - *

    - * The application is responsible for holding a strong reference to the returned - * {@link MediaRouter} instance, such as by storing the instance in a field of - * the {@link android.app.Activity}, to ensure that the media router remains alive - * as long as the application is using its features. - *

    - * In other words, the support library only holds a {@link WeakReference weak reference} - * to each media router instance. When there are no remaining strong references to the - * media router instance, all of its callbacks will be removed and route discovery - * will no longer be performed on its behalf. - *

    - * - * @return The media router instance for the context. The application must hold - * a strong reference to this object as long as it is in use. - */ - public static MediaRouter getInstance(@NonNull Context context) { - if (context == null) { - throw new IllegalArgumentException("context must not be null"); - } - checkCallingThread(); - - if (sGlobal == null) { - sGlobal = new GlobalMediaRouter(context.getApplicationContext()); - sGlobal.start(); - } - return sGlobal.getRouter(context); - } - - /** - * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to - * this media router. - */ - public List getRoutes() { - checkCallingThread(); - return sGlobal.getRoutes(); - } - - /** - * Gets information about the {@link MediaRouter.ProviderInfo route providers} - * currently known to this media router. - */ - public List getProviders() { - checkCallingThread(); - return sGlobal.getProviders(); - } - - /** - * Gets the default route for playing media content on the system. - *

    - * The system always provides a default route. - *

    - * - * @return The default route, which is guaranteed to never be null. - */ - @NonNull - public RouteInfo getDefaultRoute() { - checkCallingThread(); - return sGlobal.getDefaultRoute(); - } - - /** - * Gets a bluetooth route for playing media content on the system. - * - * @return A bluetooth route, if exist, otherwise null. - */ - public RouteInfo getBluetoothRoute() { - checkCallingThread(); - return sGlobal.getBluetoothRoute(); - } - - /** - * Gets the currently selected route. - *

    - * The application should examine the route's - * {@link RouteInfo#getControlFilters media control intent filters} to assess the - * capabilities of the route before attempting to use it. - *

    - * - *

    Example

    - *
    -     * public boolean playMovie() {
    -     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
    -     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
    -     *
    -     *     // First try using the remote playback interface, if supported.
    -     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
    -     *         // The route supports remote playback.
    -     *         // Try to send it the Uri of the movie to play.
    -     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
    -     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
    -     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
    -     *         if (route.supportsControlRequest(intent)) {
    -     *             route.sendControlRequest(intent, null);
    -     *             return true; // sent the request to play the movie
    -     *         }
    -     *     }
    -     *
    -     *     // If remote playback was not possible, then play locally.
    -     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
    -     *         // The route supports live video streaming.
    -     *         // Prepare to play content locally in a window or in a presentation.
    -     *         return playMovieInWindow();
    -     *     }
    -     *
    -     *     // Neither interface is supported, so we can't play the movie to this route.
    -     *     return false;
    -     * }
    -     * 
    - * - * @return The selected route, which is guaranteed to never be null. - * - * @see RouteInfo#getControlFilters - * @see RouteInfo#supportsControlCategory - * @see RouteInfo#supportsControlRequest - */ - @NonNull - public RouteInfo getSelectedRoute() { - checkCallingThread(); - return sGlobal.getSelectedRoute(); - } - - /** - * Returns the selected route if it matches the specified selector, otherwise - * selects the default route and returns it. If there is one live audio route - * (usually Bluetooth A2DP), it will be selected instead of default route. - * - * @param selector The selector to match. - * @return The previously selected route if it matched the selector, otherwise the - * newly selected default route which is guaranteed to never be null. - * - * @see MediaRouteSelector - * @see RouteInfo#matchesSelector - */ - @NonNull - public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "updateSelectedRoute: " + selector); - } - RouteInfo route = sGlobal.getSelectedRoute(); - if (!route.isDefaultOrBluetooth() && !route.matchesSelector(selector)) { - route = sGlobal.chooseFallbackRoute(); - sGlobal.selectRoute(route); - } - return route; - } - - /** - * Selects the specified route. - * - * @param route The route to select. - */ - public void selectRoute(@NonNull RouteInfo route) { - if (route == null) { - throw new IllegalArgumentException("route must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "selectRoute: " + route); - } - sGlobal.selectRoute(route); - } - - /** - * Unselects the current round and selects the default route instead. - *

    - * The reason given must be one of: - *

      - *
    • {@link MediaRouter#UNSELECT_REASON_UNKNOWN}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_STOPPED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}
    • - *
    - * - * @param reason The reason for disconnecting the current route. - */ - public void unselect(int reason) { - if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN || - reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { - throw new IllegalArgumentException("Unsupported reason to unselect route"); - } - checkCallingThread(); - - // Choose the fallback route if it's not already selected. - // Otherwise, select the default route. - RouteInfo fallbackRoute = sGlobal.chooseFallbackRoute(); - if (sGlobal.getSelectedRoute() != fallbackRoute) { - sGlobal.selectRoute(fallbackRoute, reason); - } else { - sGlobal.selectRoute(sGlobal.getDefaultRoute(), reason); - } - } - - /** - * Returns true if there is a route that matches the specified selector. - *

    - * This method returns true if there are any available routes that match the - * selector regardless of whether they are enabled or disabled. If the - * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then - * the method will only consider non-default routes. - *

    - *

    - * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method - * will return true if it is possible to discover a matching route even if - * discovery is not in progress or if no matching route has yet been found. - * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match. - *

    - * - * @param selector The selector to match. - * @param flags Flags to control the determination of whether a route may be - * available. May be zero or some combination of - * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and - * {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}. - * @return True if a matching route may be available. - */ - public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - checkCallingThread(); - - return sGlobal.isRouteAvailable(selector, flags); - } - - /** - * Registers a callback to discover routes that match the selector and to receive - * events when they change. - *

    - * This is a convenience method that has the same effect as calling - * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. - *

    - * - * @param selector A route selector that indicates the kinds of routes that the - * callback would like to discover. - * @param callback The callback to add. - * @see #removeCallback - */ - public void addCallback(MediaRouteSelector selector, Callback callback) { - addCallback(selector, callback, 0); - } - - /** - * Registers a callback to discover routes that match the selector and to receive - * events when they change. - *

    - * The selector describes the kinds of routes that the application wants to - * discover. For example, if the application wants to use - * live audio routes then it should include the - * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} - * in its selector when it adds a callback to the media router. - * The selector may include any number of categories. - *

    - * If the callback has already been registered, then the selector is added to - * the set of selectors being monitored by the callback. - *

    - * By default, the callback will only be invoked for events that affect routes - * that match the specified selector. Event filtering may be disabled by specifying - * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. - *

    - * Applications should use the {@link #isRouteAvailable} method to determine - * whether is it possible to discover a route with the desired capabilities - * and therefore whether the media route button should be shown to the user. - *

    - * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application - * is in the foreground to request that passive discovery be performed if there are - * sufficient resources to allow continuous passive discovery. - * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be - * ignored to conserve resources. - *

    - * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when - * passive discovery absolutely must be performed, even on low-RAM devices. - * This flag has a significant performance impact on low-RAM devices - * since it may cause many media route providers to be started simultaneously. - * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid - * performing passive discovery on these devices altogether. - *

    - * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the - * media route chooser dialog is showing to confirm the presence of available - * routes that the user may connect to. This flag may use substantially more - * power. - *

    - * - *

    Example

    - *
    -     * public class MyActivity extends Activity {
    -     *     private MediaRouter mRouter;
    -     *     private MediaRouter.Callback mCallback;
    -     *     private MediaRouteSelector mSelector;
    -     *
    -     *     protected void onCreate(Bundle savedInstanceState) {
    -     *         super.onCreate(savedInstanceState);
    -     *
    -     *         mRouter = Mediarouter.getInstance(this);
    -     *         mCallback = new MyCallback();
    -     *         mSelector = new MediaRouteSelector.Builder()
    -     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
    -     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
    -     *                 .build();
    -     *     }
    -     *
    -     *     // Add the callback on start to tell the media router what kinds of routes
    -     *     // the application is interested in so that it can try to discover suitable ones.
    -     *     public void onStart() {
    -     *         super.onStart();
    -     *
    -     *         mediaRouter.addCallback(mSelector, mCallback,
    -     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
    -     *
    -     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
    -     *         // do something with the route...
    -     *     }
    -     *
    -     *     // Remove the selector on stop to tell the media router that it no longer
    -     *     // needs to invest effort trying to discover routes of these kinds for now.
    -     *     public void onStop() {
    -     *         super.onStop();
    -     *
    -     *         mediaRouter.removeCallback(mCallback);
    -     *     }
    -     *
    -     *     private final class MyCallback extends MediaRouter.Callback {
    -     *         // Implement callback methods as needed.
    -     *     }
    -     * }
    -     * 
    - * - * @param selector A route selector that indicates the kinds of routes that the - * callback would like to discover. - * @param callback The callback to add. - * @param flags Flags to control the behavior of the callback. - * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and - * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. - * @see #removeCallback - */ - public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback, - @CallbackFlags int flags) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "addCallback: selector=" + selector - + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); - } - - CallbackRecord record; - int index = findCallbackRecord(callback); - if (index < 0) { - record = new CallbackRecord(this, callback); - mCallbackRecords.add(record); - } else { - record = mCallbackRecords.get(index); - } - boolean updateNeeded = false; - if ((flags & ~record.mFlags) != 0) { - record.mFlags |= flags; - updateNeeded = true; - } - if (!record.mSelector.contains(selector)) { - record.mSelector = new MediaRouteSelector.Builder(record.mSelector) - .addSelector(selector) - .build(); - updateNeeded = true; - } - if (updateNeeded) { - sGlobal.updateDiscoveryRequest(); - } - } - - /** - * Removes the specified callback. It will no longer receive events about - * changes to media routes. - * - * @param callback The callback to remove. - * @see #addCallback - */ - public void removeCallback(@NonNull Callback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "removeCallback: callback=" + callback); - } - - int index = findCallbackRecord(callback); - if (index >= 0) { - mCallbackRecords.remove(index); - sGlobal.updateDiscoveryRequest(); - } - } - - private int findCallbackRecord(Callback callback) { - final int count = mCallbackRecords.size(); - for (int i = 0; i < count; i++) { - if (mCallbackRecords.get(i).mCallback == callback) { - return i; - } - } - return -1; - } - - /** - * Registers a media route provider within this application process. - *

    - * The provider will be added to the list of providers that all {@link MediaRouter} - * instances within this process can use to discover routes. - *

    - * - * @param providerInstance The media route provider instance to add. - * - * @see MediaRouteProvider - * @see #removeCallback - */ - public void addProvider(@NonNull MediaRouteProvider providerInstance) { - if (providerInstance == null) { - throw new IllegalArgumentException("providerInstance must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "addProvider: " + providerInstance); - } - sGlobal.addProvider(providerInstance); - } - - /** - * Unregisters a media route provider within this application process. - *

    - * The provider will be removed from the list of providers that all {@link MediaRouter} - * instances within this process can use to discover routes. - *

    - * - * @param providerInstance The media route provider instance to remove. - * - * @see MediaRouteProvider - * @see #addCallback - */ - public void removeProvider(@NonNull MediaRouteProvider providerInstance) { - if (providerInstance == null) { - throw new IllegalArgumentException("providerInstance must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "removeProvider: " + providerInstance); - } - sGlobal.removeProvider(providerInstance); - } - - /** - * Adds a remote control client to enable remote control of the volume - * of the selected route. - *

    - * The remote control client must have previously been registered with - * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient - * AudioManager.registerRemoteControlClient} method. - *

    - * - * @param remoteControlClient The {@link android.media.RemoteControlClient} to register. - */ - public void addRemoteControlClient(@NonNull Object remoteControlClient) { - if (remoteControlClient == null) { - throw new IllegalArgumentException("remoteControlClient must not be null"); - } - checkCallingThread(); - - if (DEBUG) { - Log.d(TAG, "addRemoteControlClient: " + remoteControlClient); - } - sGlobal.addRemoteControlClient(remoteControlClient); - } - - /** - * Removes a remote control client. - * - * @param remoteControlClient The {@link android.media.RemoteControlClient} - * to unregister. - */ - public void removeRemoteControlClient(@NonNull Object remoteControlClient) { - if (remoteControlClient == null) { - throw new IllegalArgumentException("remoteControlClient must not be null"); - } - - if (DEBUG) { - Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient); - } - sGlobal.removeRemoteControlClient(remoteControlClient); - } - - /** - * Sets the media session to enable remote control of the volume of the - * selected route. This should be used instead of - * {@link #addRemoteControlClient} when using media sessions. Set the - * session to null to clear it. - * - * @param mediaSession The {@link android.media.session.MediaSession} to - * use. - */ - public void setMediaSession(Object mediaSession) { - if (DEBUG) { - Log.d(TAG, "addMediaSession: " + mediaSession); - } - sGlobal.setMediaSession(mediaSession); - } - - /** - * Sets a compat media session to enable remote control of the volume of the - * selected route. This should be used instead of - * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}. - * Set the session to null to clear it. - * - * @param mediaSession - */ - public void setMediaSessionCompat(MediaSessionCompat mediaSession) { - if (DEBUG) { - Log.d(TAG, "addMediaSessionCompat: " + mediaSession); - } - sGlobal.setMediaSessionCompat(mediaSession); - } - - public MediaSessionCompat.Token getMediaSessionToken() { - return sGlobal.getMediaSessionToken(); - } - - /** - * Ensures that calls into the media router are on the correct thread. - * It pays to be a little paranoid when global state invariants are at risk. - */ - static void checkCallingThread() { - if (Looper.myLooper() != Looper.getMainLooper()) { - throw new IllegalStateException("The media router service must only be " - + "accessed on the application's main thread."); - } - } - - static boolean equal(T a, T b) { - return a == b || (a != null && b != null && a.equals(b)); - } - - /** - * Provides information about a media route. - *

    - * Each media route has a list of {@link MediaControlIntent media control} - * {@link #getControlFilters intent filters} that describe the capabilities of the - * route and the manner in which it is used and controlled. - *

    - */ - public static class RouteInfo { - private final ProviderInfo mProvider; - private final String mDescriptorId; - private final String mUniqueId; - private String mName; - private String mDescription; - private Uri mIconUri; - private boolean mEnabled; - private boolean mConnecting; - private int mConnectionState; - private boolean mCanDisconnect; - private final ArrayList mControlFilters = new ArrayList<>(); - private int mPlaybackType; - private int mPlaybackStream; - private int mDeviceType; - private int mVolumeHandling; - private int mVolume; - private int mVolumeMax; - private Display mPresentationDisplay; - private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE; - private Bundle mExtras; - private IntentSender mSettingsIntent; - MediaRouteDescriptor mDescriptor; - - @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING, - CONNECTION_STATE_CONNECTED}) - @Retention(RetentionPolicy.SOURCE) - private @interface ConnectionState {} - - /** - * The default connection state indicating the route is disconnected. - * - * @see #getConnectionState - */ - public static final int CONNECTION_STATE_DISCONNECTED = 0; - - /** - * A connection state indicating the route is in the process of connecting and is not yet - * ready for use. - * - * @see #getConnectionState - */ - public static final int CONNECTION_STATE_CONNECTING = 1; - - /** - * A connection state indicating the route is connected. - * - * @see #getConnectionState - */ - public static final int CONNECTION_STATE_CONNECTED = 2; - - @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE}) - @Retention(RetentionPolicy.SOURCE) - private @interface PlaybackType {} - - /** - * The default playback type, "local", indicating the presentation of the media - * is happening on the same device (e.g. a phone, a tablet) as where it is - * controlled from. - * - * @see #getPlaybackType - */ - public static final int PLAYBACK_TYPE_LOCAL = 0; - - /** - * A playback type indicating the presentation of the media is happening on - * a different device (i.e. the remote device) than where it is controlled from. - * - * @see #getPlaybackType - */ - public static final int PLAYBACK_TYPE_REMOTE = 1; - - @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) - @Retention(RetentionPolicy.SOURCE) - private @interface DeviceType {} - - /** - * The default receiver device type of the route indicating the type is unknown. - * - * @see #getDeviceType - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public static final int DEVICE_TYPE_UNKNOWN = 0; - - /** - * A receiver device type of the route indicating the presentation of the media is happening - * on a TV. - * - * @see #getDeviceType - */ - public static final int DEVICE_TYPE_TV = 1; - - /** - * A receiver device type of the route indicating the presentation of the media is happening - * on a speaker. - * - * @see #getDeviceType - */ - public static final int DEVICE_TYPE_SPEAKER = 2; - - /** - * A receiver device type of the route indicating the presentation of the media is happening - * on a bluetooth device such as a bluetooth speaker. - * - * @see #getDeviceType - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public static final int DEVICE_TYPE_BLUETOOTH = 3; - - @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) - @Retention(RetentionPolicy.SOURCE) - private @interface PlaybackVolume {} - - /** - * Playback information indicating the playback volume is fixed, i.e. it cannot be - * controlled from this object. An example of fixed playback volume is a remote player, - * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather - * than attenuate at the source. - * - * @see #getVolumeHandling - */ - public static final int PLAYBACK_VOLUME_FIXED = 0; - - /** - * Playback information indicating the playback volume is variable and can be controlled - * from this object. - * - * @see #getVolumeHandling - */ - public static final int PLAYBACK_VOLUME_VARIABLE = 1; - - /** - * The default presentation display id indicating no presentation display is associated - * with the route. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public static final int PRESENTATION_DISPLAY_ID_NONE = -1; - - static final int CHANGE_GENERAL = 1 << 0; - static final int CHANGE_VOLUME = 1 << 1; - static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; - - // Should match to SystemMediaRouteProvider.PACKAGE_NAME. - static final String SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME = "android"; - - RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { - mProvider = provider; - mDescriptorId = descriptorId; - mUniqueId = uniqueId; - } - - /** - * Gets information about the provider of this media route. - */ - public ProviderInfo getProvider() { - return mProvider; - } - - /** - * Gets the unique id of the route. - *

    - * The route unique id functions as a stable identifier by which the route is known. - * For example, an application can use this id as a token to remember the - * selected route across restarts or to communicate its identity to a service. - *

    - * - * @return The unique id of the route, never null. - */ - @NonNull - public String getId() { - return mUniqueId; - } - - /** - * Gets the user-visible name of the route. - *

    - * The route name identifies the destination represented by the route. - * It may be a user-supplied name, an alias, or device serial number. - *

    - * - * @return The user-visible name of a media route. This is the string presented - * to users who may select this as the active route. - */ - public String getName() { - return mName; - } - - /** - * Gets the user-visible description of the route. - *

    - * The route description describes the kind of destination represented by the route. - * It may be a user-supplied string, a model number or brand of device. - *

    - * - * @return The description of the route, or null if none. - */ - @Nullable - public String getDescription() { - return mDescription; - } - - /** - * Gets the URI of the icon representing this route. - *

    - * This icon will be used in picker UIs if available. - *

    - * - * @return The URI of the icon representing this route, or null if none. - */ - public Uri getIconUri() { - return mIconUri; - } - - /** - * Returns true if this route is enabled and may be selected. - * - * @return True if this route is enabled. - */ - public boolean isEnabled() { - return mEnabled; - } - - /** - * Returns true if the route is in the process of connecting and is not - * yet ready for use. - * - * @return True if this route is in the process of connecting. - */ - public boolean isConnecting() { - return mConnecting; - } - - /** - * Gets the connection state of the route. - * - * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED}, - * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}. - */ - @ConnectionState - public int getConnectionState() { - return mConnectionState; - } - - /** - * Returns true if this route is currently selected. - * - * @return True if this route is currently selected. - * - * @see MediaRouter#getSelectedRoute - */ - public boolean isSelected() { - checkCallingThread(); - return sGlobal.getSelectedRoute() == this; - } - - /** - * Returns true if this route is the default route. - * - * @return True if this route is the default route. - * - * @see MediaRouter#getDefaultRoute - */ - public boolean isDefault() { - checkCallingThread(); - return sGlobal.getDefaultRoute() == this; - } - - /** - * Returns true if this route is a bluetooth route. - * - * @return True if this route is a bluetooth route. - * - * @see MediaRouter#getBluetoothRoute - */ - public boolean isBluetooth() { - checkCallingThread(); - return sGlobal.getBluetoothRoute() == this; - } - - /** - * Returns true if this route is the default route and the device speaker. - * - * @return True if this route is the default route and the device speaker. - */ - public boolean isDeviceSpeaker() { - int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier( - "default_audio_route_name", "string", "android"); - return isDefault() - && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName); - } - - /** - * Gets a list of {@link MediaControlIntent media control intent} filters that - * describe the capabilities of this route and the media control actions that - * it supports. - * - * @return A list of intent filters that specifies the media control intents that - * this route supports. - * - * @see MediaControlIntent - * @see #supportsControlCategory - * @see #supportsControlRequest - */ - public List getControlFilters() { - return mControlFilters; - } - - /** - * Returns true if the route supports at least one of the capabilities - * described by a media route selector. - * - * @param selector The selector that specifies the capabilities to check. - * @return True if the route supports at least one of the capabilities - * described in the media route selector. - */ - public boolean matchesSelector(@NonNull MediaRouteSelector selector) { - if (selector == null) { - throw new IllegalArgumentException("selector must not be null"); - } - checkCallingThread(); - return selector.matchesControlFilters(mControlFilters); - } - - /** - * Returns true if the route supports the specified - * {@link MediaControlIntent media control} category. - *

    - * Media control categories describe the capabilities of this route - * such as whether it supports live audio streaming or remote playback. - *

    - * - * @param category A {@link MediaControlIntent media control} category - * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, - * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, - * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined - * media control category. - * @return True if the route supports the specified intent category. - * - * @see MediaControlIntent - * @see #getControlFilters - */ - public boolean supportsControlCategory(@NonNull String category) { - if (category == null) { - throw new IllegalArgumentException("category must not be null"); - } - checkCallingThread(); - - int count = mControlFilters.size(); - for (int i = 0; i < count; i++) { - if (mControlFilters.get(i).hasCategory(category)) { - return true; - } - } - return false; - } - - /** - * Returns true if the route supports the specified - * {@link MediaControlIntent media control} category and action. - *

    - * Media control actions describe specific requests that an application - * can ask a route to perform. - *

    - * - * @param category A {@link MediaControlIntent media control} category - * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, - * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, - * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined - * media control category. - * @param action A {@link MediaControlIntent media control} action - * such as {@link MediaControlIntent#ACTION_PLAY}. - * @return True if the route supports the specified intent action. - * - * @see MediaControlIntent - * @see #getControlFilters - */ - public boolean supportsControlAction(@NonNull String category, @NonNull String action) { - if (category == null) { - throw new IllegalArgumentException("category must not be null"); - } - if (action == null) { - throw new IllegalArgumentException("action must not be null"); - } - checkCallingThread(); - - int count = mControlFilters.size(); - for (int i = 0; i < count; i++) { - IntentFilter filter = mControlFilters.get(i); - if (filter.hasCategory(category) && filter.hasAction(action)) { - return true; - } - } - return false; - } - - /** - * Returns true if the route supports the specified - * {@link MediaControlIntent media control} request. - *

    - * Media control requests are used to request the route to perform - * actions such as starting remote playback of a media item. - *

    - * - * @param intent A {@link MediaControlIntent media control intent}. - * @return True if the route can handle the specified intent. - * - * @see MediaControlIntent - * @see #getControlFilters - */ - public boolean supportsControlRequest(@NonNull Intent intent) { - if (intent == null) { - throw new IllegalArgumentException("intent must not be null"); - } - checkCallingThread(); - - ContentResolver contentResolver = sGlobal.getContentResolver(); - int count = mControlFilters.size(); - for (int i = 0; i < count; i++) { - if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { - return true; - } - } - return false; - } - - /** - * Sends a {@link MediaControlIntent media control} request to be performed - * asynchronously by the route's destination. - *

    - * Media control requests are used to request the route to perform - * actions such as starting remote playback of a media item. - *

    - * This function may only be called on a selected route. Control requests - * sent to unselected routes will fail. - *

    - * - * @param intent A {@link MediaControlIntent media control intent}. - * @param callback A {@link ControlRequestCallback} to invoke with the result - * of the request, or null if no result is required. - * - * @see MediaControlIntent - */ - public void sendControlRequest(@NonNull Intent intent, - @Nullable ControlRequestCallback callback) { - if (intent == null) { - throw new IllegalArgumentException("intent must not be null"); - } - checkCallingThread(); - - sGlobal.sendControlRequest(this, intent, callback); - } - - /** - * Gets the type of playback associated with this route. - * - * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} - * or {@link #PLAYBACK_TYPE_REMOTE}. - */ - @PlaybackType - public int getPlaybackType() { - return mPlaybackType; - } - - /** - * Gets the audio stream over which the playback associated with this route is performed. - * - * @return The stream over which the playback associated with this route is performed. - */ - public int getPlaybackStream() { - return mPlaybackStream; - } - - /** - * Gets the type of the receiver device associated with this route. - * - * @return The type of the receiver device associated with this route: - * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}. - */ - public int getDeviceType() { - return mDeviceType; - } - - - /** - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public boolean isDefaultOrBluetooth() { - if (isDefault() || mDeviceType == DEVICE_TYPE_BLUETOOTH) { - return true; - } - // This is a workaround for platform version 23 or below where the system route - // provider doesn't specify device type for bluetooth media routes. - return isSystemMediaRouteProvider(this) - && supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) - && !supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); - } - - /** - * Returns {@code true} if the route is selectable. - */ - boolean isSelectable() { - // This tests whether the route is still valid and enabled. - // The route descriptor field is set to null when the route is removed. - return mDescriptor != null && mEnabled; - } - - private static boolean isSystemMediaRouteProvider(MediaRouter.RouteInfo route) { - return TextUtils.equals(route.getProviderInstance().getMetadata().getPackageName(), - SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME); - } - - /** - * Gets information about how volume is handled on the route. - * - * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} - * or {@link #PLAYBACK_VOLUME_VARIABLE}. - */ - @PlaybackVolume - public int getVolumeHandling() { - return mVolumeHandling; - } - - /** - * Gets the current volume for this route. Depending on the route, this may only - * be valid if the route is currently selected. - * - * @return The volume at which the playback associated with this route is performed. - */ - public int getVolume() { - return mVolume; - } - - /** - * Gets the maximum volume at which the playback associated with this route is performed. - * - * @return The maximum volume at which the playback associated with - * this route is performed. - */ - public int getVolumeMax() { - return mVolumeMax; - } - - /** - * Gets whether this route supports disconnecting without interrupting - * playback. - * - * @return True if this route can disconnect without stopping playback, - * false otherwise. - */ - public boolean canDisconnect() { - return mCanDisconnect; - } - - /** - * Requests a volume change for this route asynchronously. - *

    - * This function may only be called on a selected route. It will have - * no effect if the route is currently unselected. - *

    - * - * @param volume The new volume value between 0 and {@link #getVolumeMax}. - */ - public void requestSetVolume(int volume) { - checkCallingThread(); - sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); - } - - /** - * Requests an incremental volume update for this route asynchronously. - *

    - * This function may only be called on a selected route. It will have - * no effect if the route is currently unselected. - *

    - * - * @param delta The delta to add to the current volume. - */ - public void requestUpdateVolume(int delta) { - checkCallingThread(); - if (delta != 0) { - sGlobal.requestUpdateVolume(this, delta); - } - } - - /** - * Gets the {@link Display} that should be used by the application to show - * a {@link android.app.Presentation} on an external display when this route is selected. - * Depending on the route, this may only be valid if the route is currently - * selected. - *

    - * The preferred presentation display may change independently of the route - * being selected or unselected. For example, the presentation display - * of the default system route may change when an external HDMI display is connected - * or disconnected even though the route itself has not changed. - *

    - * This method may return null if there is no external display associated with - * the route or if the display is not ready to show UI yet. - *

    - * The application should listen for changes to the presentation display - * using the {@link Callback#onRoutePresentationDisplayChanged} callback and - * show or dismiss its {@link android.app.Presentation} accordingly when the display - * becomes available or is removed. - *

    - * This method only makes sense for - * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. - *

    - * - * @return The preferred presentation display to use when this route is - * selected or null if none. - * - * @see MediaControlIntent#CATEGORY_LIVE_VIDEO - * @see android.app.Presentation - */ - @Nullable - public Display getPresentationDisplay() { - checkCallingThread(); - if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { - mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); - } - return mPresentationDisplay; - } - - /** - * Gets the route's presentation display id, or -1 if none. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public int getPresentationDisplayId() { - return mPresentationDisplayId; - } - - /** - * Gets a collection of extra properties about this route that were supplied - * by its media route provider, or null if none. - */ - @Nullable - public Bundle getExtras() { - return mExtras; - } - - /** - * Gets an intent sender for launching a settings activity for this - * route. - */ - @Nullable - public IntentSender getSettingsIntent() { - return mSettingsIntent; - } - - /** - * Selects this media route. - */ - public void select() { - checkCallingThread(); - sGlobal.selectRoute(this); - } - - @Override - public String toString() { - return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId - + ", name=" + mName - + ", description=" + mDescription - + ", iconUri=" + mIconUri - + ", enabled=" + mEnabled - + ", connecting=" + mConnecting - + ", connectionState=" + mConnectionState - + ", canDisconnect=" + mCanDisconnect - + ", playbackType=" + mPlaybackType - + ", playbackStream=" + mPlaybackStream - + ", deviceType=" + mDeviceType - + ", volumeHandling=" + mVolumeHandling - + ", volume=" + mVolume - + ", volumeMax=" + mVolumeMax - + ", presentationDisplayId=" + mPresentationDisplayId - + ", extras=" + mExtras - + ", settingsIntent=" + mSettingsIntent - + ", providerPackageName=" + mProvider.getPackageName() - + " }"; - } - - int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { - int changes = 0; - if (mDescriptor != descriptor) { - changes = updateDescriptor(descriptor); - } - return changes; - } - - int updateDescriptor(MediaRouteDescriptor descriptor) { - int changes = 0; - mDescriptor = descriptor; - if (descriptor != null) { - if (!equal(mName, descriptor.getName())) { - mName = descriptor.getName(); - changes |= CHANGE_GENERAL; - } - if (!equal(mDescription, descriptor.getDescription())) { - mDescription = descriptor.getDescription(); - changes |= CHANGE_GENERAL; - } - if (!equal(mIconUri, descriptor.getIconUri())) { - mIconUri = descriptor.getIconUri(); - changes |= CHANGE_GENERAL; - } - if (mEnabled != descriptor.isEnabled()) { - mEnabled = descriptor.isEnabled(); - changes |= CHANGE_GENERAL; - } - if (mConnecting != descriptor.isConnecting()) { - mConnecting = descriptor.isConnecting(); - changes |= CHANGE_GENERAL; - } - if (mConnectionState != descriptor.getConnectionState()) { - mConnectionState = descriptor.getConnectionState(); - changes |= CHANGE_GENERAL; - } - if (!mControlFilters.equals(descriptor.getControlFilters())) { - mControlFilters.clear(); - mControlFilters.addAll(descriptor.getControlFilters()); - changes |= CHANGE_GENERAL; - } - if (mPlaybackType != descriptor.getPlaybackType()) { - mPlaybackType = descriptor.getPlaybackType(); - changes |= CHANGE_GENERAL; - } - if (mPlaybackStream != descriptor.getPlaybackStream()) { - mPlaybackStream = descriptor.getPlaybackStream(); - changes |= CHANGE_GENERAL; - } - if (mDeviceType != descriptor.getDeviceType()) { - mDeviceType = descriptor.getDeviceType(); - changes |= CHANGE_GENERAL; - } - if (mVolumeHandling != descriptor.getVolumeHandling()) { - mVolumeHandling = descriptor.getVolumeHandling(); - changes |= CHANGE_GENERAL | CHANGE_VOLUME; - } - if (mVolume != descriptor.getVolume()) { - mVolume = descriptor.getVolume(); - changes |= CHANGE_GENERAL | CHANGE_VOLUME; - } - if (mVolumeMax != descriptor.getVolumeMax()) { - mVolumeMax = descriptor.getVolumeMax(); - changes |= CHANGE_GENERAL | CHANGE_VOLUME; - } - if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { - mPresentationDisplayId = descriptor.getPresentationDisplayId(); - mPresentationDisplay = null; - changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; - } - if (!equal(mExtras, descriptor.getExtras())) { - mExtras = descriptor.getExtras(); - changes |= CHANGE_GENERAL; - } - if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) { - mSettingsIntent = descriptor.getSettingsActivity(); - changes |= CHANGE_GENERAL; - } - if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) { - mCanDisconnect = descriptor.canDisconnectAndKeepPlaying(); - changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; - } - } - return changes; - } - - String getDescriptorId() { - return mDescriptorId; - } - - /** @hide */ - // @RestrictTo(LIBRARY_GROUP) - public MediaRouteProvider getProviderInstance() { - return mProvider.getProviderInstance(); - } - } - - /** - * Information about a route that consists of multiple other routes in a group. - * @hide - */ - // @RestrictTo(LIBRARY_GROUP) - public static class RouteGroup extends RouteInfo { - private List mRoutes = new ArrayList<>(); - - RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) { - super(provider, descriptorId, uniqueId); - } - - /** - * @return The number of routes in this group - */ - public int getRouteCount() { - return mRoutes.size(); - } - - /** - * Returns the route in this group at the specified index - * - * @param index Index to fetch - * @return The route at index - */ - public RouteInfo getRouteAt(int index) { - return mRoutes.get(index); - } - - /** - * Returns the routes in this group - * - * @return The list of the routes in this group - */ - public List getRoutes() { - return mRoutes; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(super.toString()); - sb.append('['); - final int count = mRoutes.size(); - for (int i = 0; i < count; i++) { - if (i > 0) sb.append(", "); - sb.append(mRoutes.get(i)); - } - sb.append(']'); - return sb.toString(); - } - - @Override - int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { - boolean changed = false; - if (mDescriptor != descriptor) { - mDescriptor = descriptor; - if (descriptor != null) { - List groupMemberIds = descriptor.getGroupMemberIds(); - List routes = new ArrayList<>(); - changed = groupMemberIds.size() != mRoutes.size(); - for (String groupMemberId : groupMemberIds) { - String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId); - RouteInfo groupMember = sGlobal.getRoute(uniqueId); - if (groupMember != null) { - routes.add(groupMember); - if (!changed && !mRoutes.contains(groupMember)) { - changed = true; - } - } - } - if (changed) { - mRoutes = routes; - } - } - } - return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor); - } - } - - /** - * Provides information about a media route provider. - *

    - * This object may be used to determine which media route provider has - * published a particular route. - *

    - */ - public static final class ProviderInfo { - private final MediaRouteProvider mProviderInstance; - private final List mRoutes = new ArrayList<>(); - - private final ProviderMetadata mMetadata; - private MediaRouteProviderDescriptor mDescriptor; - private Resources mResources; - private boolean mResourcesNotAvailable; - - ProviderInfo(MediaRouteProvider provider) { - mProviderInstance = provider; - mMetadata = provider.getMetadata(); - } - - /** - * Gets the provider's underlying {@link MediaRouteProvider} instance. - */ - public MediaRouteProvider getProviderInstance() { - checkCallingThread(); - return mProviderInstance; - } - - /** - * Gets the package name of the media route provider. - */ - public String getPackageName() { - return mMetadata.getPackageName(); - } - - /** - * Gets the component name of the media route provider. - */ - public ComponentName getComponentName() { - return mMetadata.getComponentName(); - } - - /** - * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. - */ - public List getRoutes() { - checkCallingThread(); - return mRoutes; - } - - Resources getResources() { - if (mResources == null && !mResourcesNotAvailable) { - String packageName = getPackageName(); - Context context = sGlobal.getProviderContext(packageName); - if (context != null) { - mResources = context.getResources(); - } else { - Log.w(TAG, "Unable to obtain resources for route provider package: " - + packageName); - mResourcesNotAvailable = true; - } - } - return mResources; - } - - boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { - if (mDescriptor != descriptor) { - mDescriptor = descriptor; - return true; - } - return false; - } - - int findRouteByDescriptorId(String id) { - final int count = mRoutes.size(); - for (int i = 0; i < count; i++) { - if (mRoutes.get(i).mDescriptorId.equals(id)) { - return i; - } - } - return -1; - } - - @Override - public String toString() { - return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() - + " }"; - } - } - - /** - * Interface for receiving events about media routing changes. - * All methods of this interface will be called from the application's main thread. - *

    - * A Callback will only receive events relevant to routes that the callback - * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} - * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. - *

    - * - * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) - * @see MediaRouter#removeCallback(Callback) - */ - public static abstract class Callback { - /** - * Called when the supplied media route becomes selected as the active route. - * - * @param router The media router reporting the event. - * @param route The route that has been selected. - */ - public void onRouteSelected(MediaRouter router, RouteInfo route) { - } - - /** - * Called when the supplied media route becomes unselected as the active route. - * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)} - * instead. - * - * @param router The media router reporting the event. - * @param route The route that has been unselected. - */ - public void onRouteUnselected(MediaRouter router, RouteInfo route) { - } - - /** - * Called when the supplied media route becomes unselected as the active route. - * The default implementation calls {@link #onRouteUnselected}. - *

    - * The reason provided will be one of the following: - *

      - *
    • {@link MediaRouter#UNSELECT_REASON_UNKNOWN}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_STOPPED}
    • - *
    • {@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}
    • - *
    - * - * @param router The media router reporting the event. - * @param route The route that has been unselected. - * @param reason The reason for unselecting the route. - */ - public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) { - onRouteUnselected(router, route); - } - - /** - * Called when a media route has been added. - * - * @param router The media router reporting the event. - * @param route The route that has become available for use. - */ - public void onRouteAdded(MediaRouter router, RouteInfo route) { - } - - /** - * Called when a media route has been removed. - * - * @param router The media router reporting the event. - * @param route The route that has been removed from availability. - */ - public void onRouteRemoved(MediaRouter router, RouteInfo route) { - } - - /** - * Called when a property of the indicated media route has changed. - * - * @param router The media router reporting the event. - * @param route The route that was changed. - */ - public void onRouteChanged(MediaRouter router, RouteInfo route) { - } - - /** - * Called when a media route's volume changes. - * - * @param router The media router reporting the event. - * @param route The route whose volume changed. - */ - public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { - } - - /** - * Called when a media route's presentation display changes. - *

    - * This method is called whenever the route's presentation display becomes - * available, is removed or has changes to some of its properties (such as its size). - *

    - * - * @param router The media router reporting the event. - * @param route The route whose presentation display changed. - * - * @see RouteInfo#getPresentationDisplay() - */ - public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { - } - - /** - * Called when a media route provider has been added. - * - * @param router The media router reporting the event. - * @param provider The provider that has become available for use. - */ - public void onProviderAdded(MediaRouter router, ProviderInfo provider) { - } - - /** - * Called when a media route provider has been removed. - * - * @param router The media router reporting the event. - * @param provider The provider that has been removed from availability. - */ - public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { - } - - /** - * Called when a property of the indicated media route provider has changed. - * - * @param router The media router reporting the event. - * @param provider The provider that was changed. - */ - public void onProviderChanged(MediaRouter router, ProviderInfo provider) { - } - } - - /** - * Callback which is invoked with the result of a media control request. - * - * @see RouteInfo#sendControlRequest - */ - public static abstract class ControlRequestCallback { - /** - * Called when a media control request succeeds. - * - * @param data Result data, or null if none. - * Contents depend on the {@link MediaControlIntent media control action}. - */ - public void onResult(Bundle data) { - } - - /** - * Called when a media control request fails. - * - * @param error A localized error message which may be shown to the user, or null - * if the cause of the error is unclear. - * @param data Error data, or null if none. - * Contents depend on the {@link MediaControlIntent media control action}. - */ - public void onError(String error, Bundle data) { - } - } - - private static final class CallbackRecord { - public final MediaRouter mRouter; - public final Callback mCallback; - public MediaRouteSelector mSelector; - public int mFlags; - - public CallbackRecord(MediaRouter router, Callback callback) { - mRouter = router; - mCallback = callback; - mSelector = MediaRouteSelector.EMPTY; - } - - public boolean filterRouteEvent(RouteInfo route) { - return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 - || route.matchesSelector(mSelector); - } - } - - /** - * Global state for the media router. - *

    - * Media routes and media route providers are global to the process; their - * state and the bulk of the media router implementation lives here. - *

    - */ - private static final class GlobalMediaRouter - implements SystemMediaRouteProvider.SyncCallback, - RegisteredMediaRouteProviderWatcher.Callback { - final Context mApplicationContext; - final ArrayList> mRouters = new ArrayList<>(); - private final ArrayList mRoutes = new ArrayList<>(); - private final Map, String> mUniqueIdMap = new HashMap<>(); - private final ArrayList mProviders = new ArrayList<>(); - private final ArrayList mRemoteControlClients = - new ArrayList<>(); - final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo = - new RemoteControlClientCompat.PlaybackInfo(); - private final ProviderCallback mProviderCallback = new ProviderCallback(); - final CallbackHandler mCallbackHandler = new CallbackHandler(); - private final DisplayManagerCompat mDisplayManager; - final SystemMediaRouteProvider mSystemProvider; - private final boolean mLowRam; - - private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; - private RouteInfo mDefaultRoute; - private RouteInfo mBluetoothRoute; - RouteInfo mSelectedRoute; - private RouteController mSelectedRouteController; - // A map from route descriptor ID to RouteController for the member routes in the currently - // selected route group. - private final Map mRouteControllerMap = new HashMap<>(); - private MediaRouteDiscoveryRequest mDiscoveryRequest; - private MediaSessionRecord mMediaSession; - MediaSessionCompat mRccMediaSession; - private MediaSessionCompat mCompatSession; - private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener = - new MediaSessionCompat.OnActiveChangeListener() { - @Override - public void onActiveChanged() { - if(mRccMediaSession != null) { - if (mRccMediaSession.isActive()) { - addRemoteControlClient(mRccMediaSession.getRemoteControlClient()); - } else { - removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); - } - } - } - }; - - GlobalMediaRouter(Context applicationContext) { - mApplicationContext = applicationContext; - mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); - mLowRam = ActivityManagerCompat.isLowRamDevice( - (ActivityManager)applicationContext.getSystemService( - Context.ACTIVITY_SERVICE)); - - // Add the system media route provider for interoperating with - // the framework media router. This one is special and receives - // synchronization messages from the media router. - mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); - } - - public void start() { - addProvider(mSystemProvider); - - // Start watching for routes published by registered media route - // provider services. - mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( - mApplicationContext, this); - mRegisteredProviderWatcher.start(); - } - - public MediaRouter getRouter(Context context) { - MediaRouter router; - for (int i = mRouters.size(); --i >= 0; ) { - router = mRouters.get(i).get(); - if (router == null) { - mRouters.remove(i); - } else if (router.mContext == context) { - return router; - } - } - router = new MediaRouter(context); - mRouters.add(new WeakReference(router)); - return router; - } - - public ContentResolver getContentResolver() { - return mApplicationContext.getContentResolver(); - } - - public Context getProviderContext(String packageName) { - if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { - return mApplicationContext; - } - try { - return mApplicationContext.createPackageContext( - packageName, Context.CONTEXT_RESTRICTED); - } catch (NameNotFoundException ex) { - return null; - } - } - - public Display getDisplay(int displayId) { - return mDisplayManager.getDisplay(displayId); - } - - public void sendControlRequest(RouteInfo route, - Intent intent, ControlRequestCallback callback) { - if (route == mSelectedRoute && mSelectedRouteController != null) { - if (mSelectedRouteController.onControlRequest(intent, callback)) { - return; - } - } - if (callback != null) { - callback.onError(null, null); - } - } - - public void requestSetVolume(RouteInfo route, int volume) { - if (route == mSelectedRoute && mSelectedRouteController != null) { - mSelectedRouteController.onSetVolume(volume); - } else if (!mRouteControllerMap.isEmpty()) { - RouteController controller = mRouteControllerMap.get(route.mDescriptorId); - if (controller != null) { - controller.onSetVolume(volume); - } - } - } - - public void requestUpdateVolume(RouteInfo route, int delta) { - if (route == mSelectedRoute && mSelectedRouteController != null) { - mSelectedRouteController.onUpdateVolume(delta); - } - } - - public RouteInfo getRoute(String uniqueId) { - for (RouteInfo info : mRoutes) { - if (info.mUniqueId.equals(uniqueId)) { - return info; - } - } - return null; - } - - public List getRoutes() { - return mRoutes; - } - - List getProviders() { - return mProviders; - } - - @NonNull RouteInfo getDefaultRoute() { - if (mDefaultRoute == null) { - // This should never happen once the media router has been fully - // initialized but it is good to check for the error in case there - // is a bug in provider initialization. - throw new IllegalStateException("There is no default route. " - + "The media router has not yet been fully initialized."); - } - return mDefaultRoute; - } - - RouteInfo getBluetoothRoute() { - return mBluetoothRoute; - } - - @NonNull RouteInfo getSelectedRoute() { - if (mSelectedRoute == null) { - // This should never happen once the media router has been fully - // initialized but it is good to check for the error in case there - // is a bug in provider initialization. - throw new IllegalStateException("There is no currently selected route. " - + "The media router has not yet been fully initialized."); - } - return mSelectedRoute; - } - - void selectRoute(@NonNull RouteInfo route) { - selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED); - } - - void selectRoute(@NonNull RouteInfo route, int unselectReason) { - if (!mRoutes.contains(route)) { - Log.w(TAG, "Ignoring attempt to select removed route: " + route); - return; - } - if (!route.mEnabled) { - Log.w(TAG, "Ignoring attempt to select disabled route: " + route); - return; - } - setSelectedRouteInternal(route, unselectReason); - } - - public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { - if (selector.isEmpty()) { - return false; - } - - // On low-RAM devices, do not rely on actual discovery results unless asked to. - if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) { - return true; - } - - // Check whether any existing routes match the selector. - final int routeCount = mRoutes.size(); - for (int i = 0; i < routeCount; i++) { - RouteInfo route = mRoutes.get(i); - if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 - && route.isDefaultOrBluetooth()) { - continue; - } - if (route.matchesSelector(selector)) { - return true; - } - } - - // It doesn't look like we can find a matching route right now. - return false; - } - - public void updateDiscoveryRequest() { - // Combine all of the callback selectors and active scan flags. - boolean discover = false; - boolean activeScan = false; - MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); - for (int i = mRouters.size(); --i >= 0; ) { - MediaRouter router = mRouters.get(i).get(); - if (router == null) { - mRouters.remove(i); - } else { - final int count = router.mCallbackRecords.size(); - for (int j = 0; j < count; j++) { - CallbackRecord callback = router.mCallbackRecords.get(j); - builder.addSelector(callback.mSelector); - if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { - activeScan = true; - discover = true; // perform active scan implies request discovery - } - if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { - if (!mLowRam) { - discover = true; - } - } - if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) { - discover = true; - } - } - } - } - MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; - - // Create a new discovery request. - if (mDiscoveryRequest != null - && mDiscoveryRequest.getSelector().equals(selector) - && mDiscoveryRequest.isActiveScan() == activeScan) { - return; // no change - } - if (selector.isEmpty() && !activeScan) { - // Discovery is not needed. - if (mDiscoveryRequest == null) { - return; // no change - } - mDiscoveryRequest = null; - } else { - // Discovery is needed. - mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); - } - if (DEBUG) { - Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); - } - if (discover && !activeScan && mLowRam) { - Log.i(TAG, "Forcing passive route discovery on a low-RAM device, " - + "system performance may be affected. Please consider using " - + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of " - + "CALLBACK_FLAG_FORCE_DISCOVERY."); - } - - // Notify providers. - final int providerCount = mProviders.size(); - for (int i = 0; i < providerCount; i++) { - mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); - } - } - - @Override - public void addProvider(MediaRouteProvider providerInstance) { - int index = findProviderInfo(providerInstance); - if (index < 0) { - // 1. Add the provider to the list. - ProviderInfo provider = new ProviderInfo(providerInstance); - mProviders.add(provider); - if (DEBUG) { - Log.d(TAG, "Provider added: " + provider); - } - mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); - // 2. Create the provider's contents. - updateProviderContents(provider, providerInstance.getDescriptor()); - // 3. Register the provider callback. - providerInstance.setCallback(mProviderCallback); - // 4. Set the discovery request. - providerInstance.setDiscoveryRequest(mDiscoveryRequest); - } - } - - @Override - public void removeProvider(MediaRouteProvider providerInstance) { - int index = findProviderInfo(providerInstance); - if (index >= 0) { - // 1. Unregister the provider callback. - providerInstance.setCallback(null); - // 2. Clear the discovery request. - providerInstance.setDiscoveryRequest(null); - // 3. Delete the provider's contents. - ProviderInfo provider = mProviders.get(index); - updateProviderContents(provider, null); - // 4. Remove the provider from the list. - if (DEBUG) { - Log.d(TAG, "Provider removed: " + provider); - } - mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); - mProviders.remove(index); - } - } - - void updateProviderDescriptor(MediaRouteProvider providerInstance, - MediaRouteProviderDescriptor descriptor) { - int index = findProviderInfo(providerInstance); - if (index >= 0) { - // Update the provider's contents. - ProviderInfo provider = mProviders.get(index); - updateProviderContents(provider, descriptor); - } - } - - private int findProviderInfo(MediaRouteProvider providerInstance) { - final int count = mProviders.size(); - for (int i = 0; i < count; i++) { - if (mProviders.get(i).mProviderInstance == providerInstance) { - return i; - } - } - return -1; - } - - private void updateProviderContents(ProviderInfo provider, - MediaRouteProviderDescriptor providerDescriptor) { - if (provider.updateDescriptor(providerDescriptor)) { - // Update all existing routes and reorder them to match - // the order of their descriptors. - int targetIndex = 0; - boolean selectedRouteDescriptorChanged = false; - if (providerDescriptor != null) { - if (providerDescriptor.isValid()) { - final List routeDescriptors = - providerDescriptor.getRoutes(); - final int routeCount = routeDescriptors.size(); - // Updating route group's contents requires all member routes' information. - // Add the groups to the lists and update them later. - List> addedGroups = new ArrayList<>(); - List> updatedGroups = - new ArrayList<>(); - for (int i = 0; i < routeCount; i++) { - final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); - final String id = routeDescriptor.getId(); - final int sourceIndex = provider.findRouteByDescriptorId(id); - if (sourceIndex < 0) { - // 1. Add the route to the list. - String uniqueId = assignRouteUniqueId(provider, id); - boolean isGroup = routeDescriptor.getGroupMemberIds() != null; - RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) : - new RouteInfo(provider, id, uniqueId); - provider.mRoutes.add(targetIndex++, route); - mRoutes.add(route); - // 2. Create the route's contents. - if (isGroup) { - addedGroups.add(new Pair<>(route, routeDescriptor)); - } else { - route.maybeUpdateDescriptor(routeDescriptor); - // 3. Notify clients about addition. - if (DEBUG) { - Log.d(TAG, "Route added: " + route); - } - mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); - } - - } else if (sourceIndex < targetIndex) { - Log.w(TAG, "Ignoring route descriptor with duplicate id: " - + routeDescriptor); - } else { - // 1. Reorder the route within the list. - RouteInfo route = provider.mRoutes.get(sourceIndex); - Collections.swap(provider.mRoutes, - sourceIndex, targetIndex++); - // 2. Update the route's contents. - if (route instanceof RouteGroup) { - updatedGroups.add(new Pair<>(route, routeDescriptor)); - } else { - // 3. Notify clients about changes. - if (updateRouteDescriptorAndNotify(route, routeDescriptor) - != 0) { - if (route == mSelectedRoute) { - selectedRouteDescriptorChanged = true; - } - } - } - } - } - // Update the new and/or existing groups. - for (Pair pair : addedGroups) { - RouteInfo route = pair.first; - route.maybeUpdateDescriptor(pair.second); - if (DEBUG) { - Log.d(TAG, "Route added: " + route); - } - mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); - } - for (Pair pair : updatedGroups) { - RouteInfo route = pair.first; - if (updateRouteDescriptorAndNotify(route, pair.second) != 0) { - if (route == mSelectedRoute) { - selectedRouteDescriptorChanged = true; - } - } - } - } else { - Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); - } - } - - // Dispose all remaining routes that do not have matching descriptors. - for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { - // 1. Delete the route's contents. - RouteInfo route = provider.mRoutes.get(i); - route.maybeUpdateDescriptor(null); - // 2. Remove the route from the list. - mRoutes.remove(route); - } - - // Update the selected route if needed. - updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged); - - // Now notify clients about routes that were removed. - // We do this after updating the selected route to ensure - // that the framework media router observes the new route - // selection before the removal since removing the currently - // selected route may have side-effects. - for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { - RouteInfo route = provider.mRoutes.remove(i); - if (DEBUG) { - Log.d(TAG, "Route removed: " + route); - } - mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); - } - - // Notify provider changed. - if (DEBUG) { - Log.d(TAG, "Provider changed: " + provider); - } - mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); - } - } - - private int updateRouteDescriptorAndNotify(RouteInfo route, - MediaRouteDescriptor routeDescriptor) { - int changes = route.maybeUpdateDescriptor(routeDescriptor); - if (changes != 0) { - if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { - if (DEBUG) { - Log.d(TAG, "Route changed: " + route); - } - mCallbackHandler.post( - CallbackHandler.MSG_ROUTE_CHANGED, route); - } - if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { - if (DEBUG) { - Log.d(TAG, "Route volume changed: " + route); - } - mCallbackHandler.post( - CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); - } - if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { - if (DEBUG) { - Log.d(TAG, "Route presentation display changed: " - + route); - } - mCallbackHandler.post(CallbackHandler. - MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); - } - } - return changes; - } - - private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { - // Although route descriptor ids are unique within a provider, it's - // possible for there to be two providers with the same package name. - // Therefore we must dedupe the composite id. - String componentName = provider.getComponentName().flattenToShortString(); - String uniqueId = componentName + ":" + routeDescriptorId; - if (findRouteByUniqueId(uniqueId) < 0) { - mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId); - return uniqueId; - } - Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName - + " or we're trying to assign a unique ID for an already added route"); - for (int i = 2; ; i++) { - String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); - if (findRouteByUniqueId(newUniqueId) < 0) { - mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), newUniqueId); - return newUniqueId; - } - } - } - - private int findRouteByUniqueId(String uniqueId) { - final int count = mRoutes.size(); - for (int i = 0; i < count; i++) { - if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { - return i; - } - } - return -1; - } - - private String getUniqueId(ProviderInfo provider, String routeDescriptorId) { - String componentName = provider.getComponentName().flattenToShortString(); - return mUniqueIdMap.get(new Pair<>(componentName, routeDescriptorId)); - } - - private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) { - // Update default route. - if (mDefaultRoute != null && !mDefaultRoute.isSelectable()) { - Log.i(TAG, "Clearing the default route because it " - + "is no longer selectable: " + mDefaultRoute); - mDefaultRoute = null; - } - if (mDefaultRoute == null && !mRoutes.isEmpty()) { - for (RouteInfo route : mRoutes) { - if (isSystemDefaultRoute(route) && route.isSelectable()) { - mDefaultRoute = route; - Log.i(TAG, "Found default route: " + mDefaultRoute); - break; - } - } - } - - // Update bluetooth route. - if (mBluetoothRoute != null && !mBluetoothRoute.isSelectable()) { - Log.i(TAG, "Clearing the bluetooth route because it " - + "is no longer selectable: " + mBluetoothRoute); - mBluetoothRoute = null; - } - if (mBluetoothRoute == null && !mRoutes.isEmpty()) { - for (RouteInfo route : mRoutes) { - if (isSystemLiveAudioOnlyRoute(route) && route.isSelectable()) { - mBluetoothRoute = route; - Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute); - break; - } - } - } - - // Update selected route. - if (mSelectedRoute == null || !mSelectedRoute.isSelectable()) { - Log.i(TAG, "Unselecting the current route because it " - + "is no longer selectable: " + mSelectedRoute); - setSelectedRouteInternal(chooseFallbackRoute(), - MediaRouter.UNSELECT_REASON_UNKNOWN); - } else if (selectedRouteDescriptorChanged) { - // In case the selected route is a route group, select/unselect route controllers - // for the added/removed route members. - if (mSelectedRoute instanceof RouteGroup) { - List routes = ((RouteGroup) mSelectedRoute).getRoutes(); - // Build a set of descriptor IDs for the new route group. - Set idSet = new HashSet<>(); - for (RouteInfo route : routes) { - idSet.add(route.mDescriptorId); - } - // Unselect route controllers for the removed routes. - Iterator> iter = - mRouteControllerMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - if (!idSet.contains(entry.getKey())) { - RouteController controller = entry.getValue(); - controller.onUnselect(); - controller.onRelease(); - iter.remove(); - } - } - // Select route controllers for the added routes. - for (RouteInfo route : routes) { - if (!mRouteControllerMap.containsKey(route.mDescriptorId)) { - RouteController controller = route.getProviderInstance() - .onCreateRouteController( - route.mDescriptorId, mSelectedRoute.mDescriptorId); - controller.onSelect(); - mRouteControllerMap.put(route.mDescriptorId, controller); - } - } - } - // Update the playback info because the properties of the route have changed. - updatePlaybackInfoFromSelectedRoute(); - } - } - - RouteInfo chooseFallbackRoute() { - // When the current route is removed or no longer selectable, - // we want to revert to a live audio route if there is - // one (usually Bluetooth A2DP). Failing that, use - // the default route. - for (RouteInfo route : mRoutes) { - if (route != mDefaultRoute - && isSystemLiveAudioOnlyRoute(route) - && route.isSelectable()) { - return route; - } - } - return mDefaultRoute; - } - - private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) { - return route.getProviderInstance() == mSystemProvider - && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) - && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); - } - - private boolean isSystemDefaultRoute(RouteInfo route) { - return route.getProviderInstance() == mSystemProvider - && route.mDescriptorId.equals( - SystemMediaRouteProvider.DEFAULT_ROUTE_ID); - } - - private void setSelectedRouteInternal(@NonNull RouteInfo route, int unselectReason) { - // TODO: Remove the following logging when no longer needed. - if (sGlobal == null || (mBluetoothRoute != null && route.isDefault())) { - final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); - StringBuilder sb = new StringBuilder(); - // callStack[3] is the caller of this method. - for (int i = 3; i < callStack.length; i++) { - StackTraceElement caller = callStack[i]; - sb.append(caller.getClassName()) - .append(".") - .append(caller.getMethodName()) - .append(":") - .append(caller.getLineNumber()) - .append(" "); - } - if (sGlobal == null) { - Log.w(TAG, "setSelectedRouteInternal is called while sGlobal is null: pkgName=" - + mApplicationContext.getPackageName() + ", callers=" + sb.toString()); - } else { - Log.w(TAG, "Default route is selected while a BT route is available: pkgName=" - + mApplicationContext.getPackageName() + ", callers=" + sb.toString()); - } - } - - if (mSelectedRoute != route) { - if (mSelectedRoute != null) { - if (DEBUG) { - Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: " - + unselectReason); - } - mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute, - unselectReason); - if (mSelectedRouteController != null) { - mSelectedRouteController.onUnselect(unselectReason); - mSelectedRouteController.onRelease(); - mSelectedRouteController = null; - } - if (!mRouteControllerMap.isEmpty()) { - for (RouteController controller : mRouteControllerMap.values()) { - controller.onUnselect(unselectReason); - controller.onRelease(); - } - mRouteControllerMap.clear(); - } - } - - mSelectedRoute = route; - mSelectedRouteController = route.getProviderInstance().onCreateRouteController( - route.mDescriptorId); - if (mSelectedRouteController != null) { - mSelectedRouteController.onSelect(); - } - if (DEBUG) { - Log.d(TAG, "Route selected: " + mSelectedRoute); - } - mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); - - if (mSelectedRoute instanceof RouteGroup) { - List routes = ((RouteGroup) mSelectedRoute).getRoutes(); - mRouteControllerMap.clear(); - for (RouteInfo r : routes) { - RouteController controller = - r.getProviderInstance().onCreateRouteController( - r.mDescriptorId, mSelectedRoute.mDescriptorId); - controller.onSelect(); - mRouteControllerMap.put(r.mDescriptorId, controller); - } - } - - updatePlaybackInfoFromSelectedRoute(); - } - } - - @Override - public void onSystemRouteSelectedByDescriptorId(String id) { - // System route is selected, do not sync the route we selected before. - mCallbackHandler.removeMessages(CallbackHandler.MSG_ROUTE_SELECTED); - int providerIndex = findProviderInfo(mSystemProvider); - if (providerIndex >= 0) { - ProviderInfo provider = mProviders.get(providerIndex); - int routeIndex = provider.findRouteByDescriptorId(id); - if (routeIndex >= 0) { - provider.mRoutes.get(routeIndex).select(); - } - } - } - - public void addRemoteControlClient(Object rcc) { - int index = findRemoteControlClientRecord(rcc); - if (index < 0) { - RemoteControlClientRecord record = new RemoteControlClientRecord(rcc); - mRemoteControlClients.add(record); - } - } - - public void removeRemoteControlClient(Object rcc) { - int index = findRemoteControlClientRecord(rcc); - if (index >= 0) { - RemoteControlClientRecord record = mRemoteControlClients.remove(index); - record.disconnect(); - } - } - - public void setMediaSession(Object session) { - setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null); - } - - public void setMediaSessionCompat(final MediaSessionCompat session) { - mCompatSession = session; - if (android.os.Build.VERSION.SDK_INT >= 21) { - setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null); - } else if (android.os.Build.VERSION.SDK_INT >= 14) { - if (mRccMediaSession != null) { - removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); - mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener); - } - mRccMediaSession = session; - if (session != null) { - session.addOnActiveChangeListener(mSessionActiveListener); - if (session.isActive()) { - addRemoteControlClient(session.getRemoteControlClient()); - } - } - } - } - - private void setMediaSessionRecord(MediaSessionRecord mediaSessionRecord) { - if (mMediaSession != null) { - mMediaSession.clearVolumeHandling(); - } - mMediaSession = mediaSessionRecord; - if (mediaSessionRecord != null) { - updatePlaybackInfoFromSelectedRoute(); - } - } - - public MediaSessionCompat.Token getMediaSessionToken() { - if (mMediaSession != null) { - return mMediaSession.getToken(); - } else if (mCompatSession != null) { - return mCompatSession.getSessionToken(); - } - return null; - } - - private int findRemoteControlClientRecord(Object rcc) { - final int count = mRemoteControlClients.size(); - for (int i = 0; i < count; i++) { - RemoteControlClientRecord record = mRemoteControlClients.get(i); - if (record.getRemoteControlClient() == rcc) { - return i; - } - } - return -1; - } - - private void updatePlaybackInfoFromSelectedRoute() { - if (mSelectedRoute != null) { - mPlaybackInfo.volume = mSelectedRoute.getVolume(); - mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax(); - mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling(); - mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream(); - mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType(); - - final int count = mRemoteControlClients.size(); - for (int i = 0; i < count; i++) { - RemoteControlClientRecord record = mRemoteControlClients.get(i); - record.updatePlaybackInfo(); - } - if (mMediaSession != null) { - if (mSelectedRoute == getDefaultRoute() - || mSelectedRoute == getBluetoothRoute()) { - // Local route - mMediaSession.clearVolumeHandling(); - } else { - @VolumeProviderCompat.ControlType int controlType = - VolumeProviderCompat.VOLUME_CONTROL_FIXED; - if (mPlaybackInfo.volumeHandling - == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { - controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; - } - mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax, - mPlaybackInfo.volume); - } - } - } else { - if (mMediaSession != null) { - mMediaSession.clearVolumeHandling(); - } - } - } - - private final class ProviderCallback extends MediaRouteProvider.Callback { - ProviderCallback() { - } - - @Override - public void onDescriptorChanged(MediaRouteProvider provider, - MediaRouteProviderDescriptor descriptor) { - updateProviderDescriptor(provider, descriptor); - } - } - - private final class MediaSessionRecord { - private final MediaSessionCompat mMsCompat; - - private @VolumeProviderCompat.ControlType int mControlType; - private int mMaxVolume; - private VolumeProviderCompat mVpCompat; - - public MediaSessionRecord(Object mediaSession) { - mMsCompat = MediaSessionCompat.fromMediaSession(mApplicationContext, mediaSession); - } - - public MediaSessionRecord(MediaSessionCompat mediaSessionCompat) { - mMsCompat = mediaSessionCompat; - } - - public void configureVolume(@VolumeProviderCompat.ControlType int controlType, - int max, int current) { - if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) { - // If we haven't changed control type or max just set the - // new current volume - mVpCompat.setCurrentVolume(current); - } else { - // Otherwise create a new provider and update - mVpCompat = new VolumeProviderCompat(controlType, max, current) { - @Override - public void onSetVolumeTo(final int volume) { - mCallbackHandler.post(new Runnable() { - @Override - public void run() { - if (mSelectedRoute != null) { - mSelectedRoute.requestSetVolume(volume); - } - } - }); - } - - @Override - public void onAdjustVolume(final int direction) { - mCallbackHandler.post(new Runnable() { - @Override - public void run() { - if (mSelectedRoute != null) { - mSelectedRoute.requestUpdateVolume(direction); - } - } - }); - } - }; - mMsCompat.setPlaybackToRemote(mVpCompat); - } - } - - public void clearVolumeHandling() { - mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream); - mVpCompat = null; - } - - public MediaSessionCompat.Token getToken() { - return mMsCompat.getSessionToken(); - } - } - - private final class RemoteControlClientRecord - implements RemoteControlClientCompat.VolumeCallback { - private final RemoteControlClientCompat mRccCompat; - private boolean mDisconnected; - - public RemoteControlClientRecord(Object rcc) { - mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc); - mRccCompat.setVolumeCallback(this); - updatePlaybackInfo(); - } - - public Object getRemoteControlClient() { - return mRccCompat.getRemoteControlClient(); - } - - public void disconnect() { - mDisconnected = true; - mRccCompat.setVolumeCallback(null); - } - - public void updatePlaybackInfo() { - mRccCompat.setPlaybackInfo(mPlaybackInfo); - } - - @Override - public void onVolumeSetRequest(int volume) { - if (!mDisconnected && mSelectedRoute != null) { - mSelectedRoute.requestSetVolume(volume); - } - } - - @Override - public void onVolumeUpdateRequest(int direction) { - if (!mDisconnected && mSelectedRoute != null) { - mSelectedRoute.requestUpdateVolume(direction); - } - } - } - - private final class CallbackHandler extends Handler { - private final ArrayList mTempCallbackRecords = - new ArrayList(); - - private static final int MSG_TYPE_MASK = 0xff00; - private static final int MSG_TYPE_ROUTE = 0x0100; - private static final int MSG_TYPE_PROVIDER = 0x0200; - - public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; - public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; - public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; - public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; - public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; - public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; - public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; - - public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; - public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; - public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; - - CallbackHandler() { - } - - public void post(int msg, Object obj) { - obtainMessage(msg, obj).sendToTarget(); - } - - public void post(int msg, Object obj, int arg) { - Message message = obtainMessage(msg, obj); - message.arg1 = arg; - message.sendToTarget(); - } - - @Override - public void handleMessage(Message msg) { - final int what = msg.what; - final Object obj = msg.obj; - final int arg = msg.arg1; - - if (what == MSG_ROUTE_CHANGED - && getSelectedRoute().getId().equals(((RouteInfo) obj).getId())) { - updateSelectedRouteIfNeeded(true); - } - - // Synchronize state with the system media router. - syncWithSystemProvider(what, obj); - - // Invoke all registered callbacks. - // Build a list of callbacks before invoking them in case callbacks - // are added or removed during dispatch. - try { - for (int i = mRouters.size(); --i >= 0; ) { - MediaRouter router = mRouters.get(i).get(); - if (router == null) { - mRouters.remove(i); - } else { - mTempCallbackRecords.addAll(router.mCallbackRecords); - } - } - - final int callbackCount = mTempCallbackRecords.size(); - for (int i = 0; i < callbackCount; i++) { - invokeCallback(mTempCallbackRecords.get(i), what, obj, arg); - } - } finally { - mTempCallbackRecords.clear(); - } - } - - private void syncWithSystemProvider(int what, Object obj) { - switch (what) { - case MSG_ROUTE_ADDED: - mSystemProvider.onSyncRouteAdded((RouteInfo) obj); - break; - case MSG_ROUTE_REMOVED: - mSystemProvider.onSyncRouteRemoved((RouteInfo) obj); - break; - case MSG_ROUTE_CHANGED: - mSystemProvider.onSyncRouteChanged((RouteInfo) obj); - break; - case MSG_ROUTE_SELECTED: - mSystemProvider.onSyncRouteSelected((RouteInfo) obj); - break; - } - } - - private void invokeCallback(CallbackRecord record, int what, Object obj, int arg) { - final MediaRouter router = record.mRouter; - final MediaRouter.Callback callback = record.mCallback; - switch (what & MSG_TYPE_MASK) { - case MSG_TYPE_ROUTE: { - final RouteInfo route = (RouteInfo)obj; - if (!record.filterRouteEvent(route)) { - break; - } - switch (what) { - case MSG_ROUTE_ADDED: - callback.onRouteAdded(router, route); - break; - case MSG_ROUTE_REMOVED: - callback.onRouteRemoved(router, route); - break; - case MSG_ROUTE_CHANGED: - callback.onRouteChanged(router, route); - break; - case MSG_ROUTE_VOLUME_CHANGED: - callback.onRouteVolumeChanged(router, route); - break; - case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: - callback.onRoutePresentationDisplayChanged(router, route); - break; - case MSG_ROUTE_SELECTED: - callback.onRouteSelected(router, route); - break; - case MSG_ROUTE_UNSELECTED: - callback.onRouteUnselected(router, route, arg); - break; - } - break; - } - case MSG_TYPE_PROVIDER: { - final ProviderInfo provider = (ProviderInfo)obj; - switch (what) { - case MSG_PROVIDER_ADDED: - callback.onProviderAdded(router, provider); - break; - case MSG_PROVIDER_REMOVED: - callback.onProviderRemoved(router, provider); - break; - case MSG_PROVIDER_CHANGED: - callback.onProviderChanged(router, provider); - break; - } - } - } - } - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java deleted file mode 100644 index 0e7514c951..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.app.PendingIntent; -import android.os.Bundle; -import android.os.SystemClock; - -import androidx.core.util.TimeUtils; - -/** - * Describes the playback status of a media session. - *

    - * This class is part of the remote playback protocol described by the - * {@link MediaControlIntent MediaControlIntent} class. - *

    - * When a media session is created, it is initially in the - * {@link #SESSION_STATE_ACTIVE active} state. When the media session ends - * normally, it transitions to the {@link #SESSION_STATE_ENDED ended} state. - * If the media session is invalidated due to another session forcibly taking - * control of the route, then it transitions to the - * {@link #SESSION_STATE_INVALIDATED invalidated} state. - * Refer to the documentation of each state for an explanation of its meaning. - *

    - * To monitor session status, the application should supply a {@link PendingIntent} to use as the - * {@link MediaControlIntent#EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receiver} - * for a given {@link MediaControlIntent#ACTION_START_SESSION session start request}. - *

    - * This object is immutable once created using a {@link Builder} instance. - *

    - */ -public final class MediaSessionStatus { - static final String KEY_TIMESTAMP = "timestamp"; - static final String KEY_SESSION_STATE = "sessionState"; - static final String KEY_QUEUE_PAUSED = "queuePaused"; - static final String KEY_EXTRAS = "extras"; - - final Bundle mBundle; - - /** - * Session state: Active. - *

    - * Indicates that the media session is active and in control of the route. - *

    - */ - public static final int SESSION_STATE_ACTIVE = 0; - - /** - * Session state: Ended. - *

    - * Indicates that the media session was ended normally using the - * {@link MediaControlIntent#ACTION_END_SESSION end session} action. - *

    - * A terminated media session cannot be used anymore. To play more media, the - * application must start a new session. - *

    - */ - public static final int SESSION_STATE_ENDED = 1; - - /** - * Session state: Invalidated. - *

    - * Indicates that the media session was invalidated involuntarily due to - * another session taking control of the route. - *

    - * An invalidated media session cannot be used anymore. To play more media, the - * application must start a new session. - *

    - */ - public static final int SESSION_STATE_INVALIDATED = 2; - - MediaSessionStatus(Bundle bundle) { - mBundle = bundle; - } - - /** - * Gets the timestamp associated with the status information in - * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base. - * - * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base. - */ - public long getTimestamp() { - return mBundle.getLong(KEY_TIMESTAMP); - } - - /** - * Gets the session state. - * - * @return The session state. One of {@link #SESSION_STATE_ACTIVE}, - * {@link #SESSION_STATE_ENDED}, or {@link #SESSION_STATE_INVALIDATED}. - */ - public int getSessionState() { - return mBundle.getInt(KEY_SESSION_STATE, SESSION_STATE_INVALIDATED); - } - - /** - * Returns true if the session's queue is paused. - * - * @return True if the session's queue is paused. - */ - public boolean isQueuePaused() { - return mBundle.getBoolean(KEY_QUEUE_PAUSED); - } - - /** - * Gets a bundle of extras for this status object. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Bundle getExtras() { - return mBundle.getBundle(KEY_EXTRAS); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - result.append("MediaSessionStatus{ "); - result.append("timestamp="); - TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result); - result.append(" ms ago"); - result.append(", sessionState=").append(sessionStateToString(getSessionState())); - result.append(", queuePaused=").append(isQueuePaused()); - result.append(", extras=").append(getExtras()); - result.append(" }"); - return result.toString(); - } - - private static String sessionStateToString(int sessionState) { - switch (sessionState) { - case SESSION_STATE_ACTIVE: - return "active"; - case SESSION_STATE_ENDED: - return "ended"; - case SESSION_STATE_INVALIDATED: - return "invalidated"; - } - return Integer.toString(sessionState); - } - - /** - * Converts this object to a bundle for serialization. - * - * @return The contents of the object represented as a bundle. - */ - public Bundle asBundle() { - return mBundle; - } - - /** - * Creates an instance from a bundle. - * - * @param bundle The bundle, or null if none. - * @return The new instance, or null if the bundle was null. - */ - public static MediaSessionStatus fromBundle(Bundle bundle) { - return bundle != null ? new MediaSessionStatus(bundle) : null; - } - - /** - * Builder for {@link MediaSessionStatus media session status objects}. - */ - public static final class Builder { - private final Bundle mBundle; - - /** - * Creates a media session status builder using the current time as the - * reference timestamp. - * - * @param sessionState The session state. - */ - public Builder(int sessionState) { - mBundle = new Bundle(); - setTimestamp(SystemClock.elapsedRealtime()); - setSessionState(sessionState); - } - - /** - * Creates a media session status builder whose initial contents are - * copied from an existing status. - */ - public Builder(MediaSessionStatus status) { - if (status == null) { - throw new IllegalArgumentException("status must not be null"); - } - - mBundle = new Bundle(status.mBundle); - } - - /** - * Sets the timestamp associated with the status information in - * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base. - */ - public Builder setTimestamp(long elapsedRealtimeTimestamp) { - mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp); - return this; - } - - /** - * Sets the session state. - */ - public Builder setSessionState(int sessionState) { - mBundle.putInt(KEY_SESSION_STATE, sessionState); - return this; - } - - /** - * Sets whether the queue is paused. - */ - public Builder setQueuePaused(boolean queuePaused) { - mBundle.putBoolean(KEY_QUEUE_PAUSED, queuePaused); - return this; - } - - /** - * Sets a bundle of extras for this status object. - * The extras will be ignored by the media router but they may be used - * by applications. - */ - public Builder setExtras(Bundle extras) { - mBundle.putBundle(KEY_EXTRAS, extras); - return this; - } - - /** - * Builds the {@link MediaSessionStatus media session status object}. - */ - public MediaSessionStatus build() { - return new MediaSessionStatus(mBundle); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java deleted file mode 100644 index eacf1c81c2..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java +++ /dev/null @@ -1,746 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_DATA_ROUTE_LIBRARY_GROUP; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_DATA_UNSELECT_REASON; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_CREATE_ROUTE_CONTROLLER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_ROUTE_CONTROL_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SELECT_ROUTE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SET_DISCOVERY_REQUEST; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_SET_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UNREGISTER; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UNSELECT_ROUTE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_MSG_UPDATE_ROUTE_VOLUME; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .CLIENT_VERSION_CURRENT; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_CONTROL_REQUEST_FAILED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_DESCRIPTOR_CHANGED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_GENERIC_FAILURE; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_GENERIC_SUCCESS; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .SERVICE_MSG_REGISTERED; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1; -import static com.android.support.mediarouter.media.MediaRouteProviderProtocol - .isValidRemoteMessenger; - -import android.annotation.NonNull; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.DeadObjectException; -import android.os.Handler; -import android.os.IBinder; -import android.os.IBinder.DeathRecipient; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.util.Log; -import android.util.SparseArray; - -import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * Maintains a connection to a particular media route provider service. - */ -final class RegisteredMediaRouteProvider extends MediaRouteProvider - implements ServiceConnection { - static final String TAG = "MediaRouteProviderProxy"; // max. 23 chars - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private final ComponentName mComponentName; - final PrivateHandler mPrivateHandler; - private final ArrayList mControllers = new ArrayList(); - - private boolean mStarted; - private boolean mBound; - private Connection mActiveConnection; - private boolean mConnectionReady; - - public RegisteredMediaRouteProvider(Context context, ComponentName componentName) { - super(context, new ProviderMetadata(componentName)); - - mComponentName = componentName; - mPrivateHandler = new PrivateHandler(); - } - - @Override - public RouteController onCreateRouteController(@NonNull String routeId) { - if (routeId == null) { - throw new IllegalArgumentException("routeId cannot be null"); - } - return createRouteController(routeId, null); - } - - @Override - public RouteController onCreateRouteController( - @NonNull String routeId, @NonNull String routeGroupId) { - if (routeId == null) { - throw new IllegalArgumentException("routeId cannot be null"); - } - if (routeGroupId == null) { - throw new IllegalArgumentException("routeGroupId cannot be null"); - } - return createRouteController(routeId, routeGroupId); - } - - @Override - public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { - if (mConnectionReady) { - mActiveConnection.setDiscoveryRequest(request); - } - updateBinding(); - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) { - Log.d(TAG, this + ": Connected"); - } - - if (mBound) { - disconnect(); - - Messenger messenger = (service != null ? new Messenger(service) : null); - if (isValidRemoteMessenger(messenger)) { - Connection connection = new Connection(messenger); - if (connection.register()) { - mActiveConnection = connection; - } else { - if (DEBUG) { - Log.d(TAG, this + ": Registration failed"); - } - } - } else { - Log.e(TAG, this + ": Service returned invalid messenger binder"); - } - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) { - Log.d(TAG, this + ": Service disconnected"); - } - disconnect(); - } - - @Override - public String toString() { - return "Service connection " + mComponentName.flattenToShortString(); - } - - public boolean hasComponentName(String packageName, String className) { - return mComponentName.getPackageName().equals(packageName) - && mComponentName.getClassName().equals(className); - } - - public void start() { - if (!mStarted) { - if (DEBUG) { - Log.d(TAG, this + ": Starting"); - } - - mStarted = true; - updateBinding(); - } - } - - public void stop() { - if (mStarted) { - if (DEBUG) { - Log.d(TAG, this + ": Stopping"); - } - - mStarted = false; - updateBinding(); - } - } - - public void rebindIfDisconnected() { - if (mActiveConnection == null && shouldBind()) { - unbind(); - bind(); - } - } - - private void updateBinding() { - if (shouldBind()) { - bind(); - } else { - unbind(); - } - } - - private boolean shouldBind() { - if (mStarted) { - // Bind whenever there is a discovery request. - if (getDiscoveryRequest() != null) { - return true; - } - - // Bind whenever the application has an active route controller. - // This means that one of this provider's routes is selected. - if (!mControllers.isEmpty()) { - return true; - } - } - return false; - } - - private void bind() { - if (!mBound) { - if (DEBUG) { - Log.d(TAG, this + ": Binding"); - } - - Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE); - service.setComponent(mComponentName); - try { - mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE); - if (!mBound && DEBUG) { - Log.d(TAG, this + ": Bind failed"); - } - } catch (SecurityException ex) { - if (DEBUG) { - Log.d(TAG, this + ": Bind failed", ex); - } - } - } - } - - private void unbind() { - if (mBound) { - if (DEBUG) { - Log.d(TAG, this + ": Unbinding"); - } - - mBound = false; - disconnect(); - getContext().unbindService(this); - } - } - - private RouteController createRouteController(String routeId, String routeGroupId) { - MediaRouteProviderDescriptor descriptor = getDescriptor(); - if (descriptor != null) { - List routes = descriptor.getRoutes(); - final int count = routes.size(); - for (int i = 0; i < count; i++) { - final MediaRouteDescriptor route = routes.get(i); - if (route.getId().equals(routeId)) { - Controller controller = new Controller(routeId, routeGroupId); - mControllers.add(controller); - if (mConnectionReady) { - controller.attachConnection(mActiveConnection); - } - updateBinding(); - return controller; - } - } - } - return null; - } - - void onConnectionReady(Connection connection) { - if (mActiveConnection == connection) { - mConnectionReady = true; - attachControllersToConnection(); - - MediaRouteDiscoveryRequest request = getDiscoveryRequest(); - if (request != null) { - mActiveConnection.setDiscoveryRequest(request); - } - } - } - - void onConnectionDied(Connection connection) { - if (mActiveConnection == connection) { - if (DEBUG) { - Log.d(TAG, this + ": Service connection died"); - } - disconnect(); - } - } - - void onConnectionError(Connection connection, String error) { - if (mActiveConnection == connection) { - if (DEBUG) { - Log.d(TAG, this + ": Service connection error - " + error); - } - unbind(); - } - } - - void onConnectionDescriptorChanged(Connection connection, - MediaRouteProviderDescriptor descriptor) { - if (mActiveConnection == connection) { - if (DEBUG) { - Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor); - } - setDescriptor(descriptor); - } - } - - private void disconnect() { - if (mActiveConnection != null) { - setDescriptor(null); - mConnectionReady = false; - detachControllersFromConnection(); - mActiveConnection.dispose(); - mActiveConnection = null; - } - } - - void onControllerReleased(Controller controller) { - mControllers.remove(controller); - controller.detachConnection(); - updateBinding(); - } - - private void attachControllersToConnection() { - int count = mControllers.size(); - for (int i = 0; i < count; i++) { - mControllers.get(i).attachConnection(mActiveConnection); - } - } - - private void detachControllersFromConnection() { - int count = mControllers.size(); - for (int i = 0; i < count; i++) { - mControllers.get(i).detachConnection(); - } - } - - private final class Controller extends RouteController { - private final String mRouteId; - private final String mRouteGroupId; - - private boolean mSelected; - private int mPendingSetVolume = -1; - private int mPendingUpdateVolumeDelta; - - private Connection mConnection; - private int mControllerId; - - public Controller(String routeId, String routeGroupId) { - mRouteId = routeId; - mRouteGroupId = routeGroupId; - } - - public void attachConnection(Connection connection) { - mConnection = connection; - mControllerId = connection.createRouteController(mRouteId, mRouteGroupId); - if (mSelected) { - connection.selectRoute(mControllerId); - if (mPendingSetVolume >= 0) { - connection.setVolume(mControllerId, mPendingSetVolume); - mPendingSetVolume = -1; - } - if (mPendingUpdateVolumeDelta != 0) { - connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta); - mPendingUpdateVolumeDelta = 0; - } - } - } - - public void detachConnection() { - if (mConnection != null) { - mConnection.releaseRouteController(mControllerId); - mConnection = null; - mControllerId = 0; - } - } - - @Override - public void onRelease() { - onControllerReleased(this); - } - - @Override - public void onSelect() { - mSelected = true; - if (mConnection != null) { - mConnection.selectRoute(mControllerId); - } - } - - @Override - public void onUnselect() { - onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN); - } - - @Override - public void onUnselect(int reason) { - mSelected = false; - if (mConnection != null) { - mConnection.unselectRoute(mControllerId, reason); - } - } - - @Override - public void onSetVolume(int volume) { - if (mConnection != null) { - mConnection.setVolume(mControllerId, volume); - } else { - mPendingSetVolume = volume; - mPendingUpdateVolumeDelta = 0; - } - } - - @Override - public void onUpdateVolume(int delta) { - if (mConnection != null) { - mConnection.updateVolume(mControllerId, delta); - } else { - mPendingUpdateVolumeDelta += delta; - } - } - - @Override - public boolean onControlRequest(Intent intent, ControlRequestCallback callback) { - if (mConnection != null) { - return mConnection.sendControlRequest(mControllerId, intent, callback); - } - return false; - } - } - - private final class Connection implements DeathRecipient { - private final Messenger mServiceMessenger; - private final ReceiveHandler mReceiveHandler; - private final Messenger mReceiveMessenger; - - private int mNextRequestId = 1; - private int mNextControllerId = 1; - private int mServiceVersion; // non-zero when registration complete - - private int mPendingRegisterRequestId; - private final SparseArray mPendingCallbacks = - new SparseArray(); - - public Connection(Messenger serviceMessenger) { - mServiceMessenger = serviceMessenger; - mReceiveHandler = new ReceiveHandler(this); - mReceiveMessenger = new Messenger(mReceiveHandler); - } - - public boolean register() { - mPendingRegisterRequestId = mNextRequestId++; - if (!sendRequest(CLIENT_MSG_REGISTER, - mPendingRegisterRequestId, - CLIENT_VERSION_CURRENT, null, null)) { - return false; - } - - try { - mServiceMessenger.getBinder().linkToDeath(this, 0); - return true; - } catch (RemoteException ex) { - binderDied(); - } - return false; - } - - public void dispose() { - sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null); - mReceiveHandler.dispose(); - mServiceMessenger.getBinder().unlinkToDeath(this, 0); - - mPrivateHandler.post(new Runnable() { - @Override - public void run() { - failPendingCallbacks(); - } - }); - } - - void failPendingCallbacks() { - int count = 0; - for (int i = 0; i < mPendingCallbacks.size(); i++) { - mPendingCallbacks.valueAt(i).onError(null, null); - } - mPendingCallbacks.clear(); - } - - public boolean onGenericFailure(int requestId) { - if (requestId == mPendingRegisterRequestId) { - mPendingRegisterRequestId = 0; - onConnectionError(this, "Registration failed"); - } - ControlRequestCallback callback = mPendingCallbacks.get(requestId); - if (callback != null) { - mPendingCallbacks.remove(requestId); - callback.onError(null, null); - } - return true; - } - - public boolean onGenericSuccess(int requestId) { - return true; - } - - public boolean onRegistered(int requestId, int serviceVersion, - Bundle descriptorBundle) { - if (mServiceVersion == 0 - && requestId == mPendingRegisterRequestId - && serviceVersion >= SERVICE_VERSION_1) { - mPendingRegisterRequestId = 0; - mServiceVersion = serviceVersion; - onConnectionDescriptorChanged(this, - MediaRouteProviderDescriptor.fromBundle(descriptorBundle)); - onConnectionReady(this); - return true; - } - return false; - } - - public boolean onDescriptorChanged(Bundle descriptorBundle) { - if (mServiceVersion != 0) { - onConnectionDescriptorChanged(this, - MediaRouteProviderDescriptor.fromBundle(descriptorBundle)); - return true; - } - return false; - } - - public boolean onControlRequestSucceeded(int requestId, Bundle data) { - ControlRequestCallback callback = mPendingCallbacks.get(requestId); - if (callback != null) { - mPendingCallbacks.remove(requestId); - callback.onResult(data); - return true; - } - return false; - } - - public boolean onControlRequestFailed(int requestId, String error, Bundle data) { - ControlRequestCallback callback = mPendingCallbacks.get(requestId); - if (callback != null) { - mPendingCallbacks.remove(requestId); - callback.onError(error, data); - return true; - } - return false; - } - - @Override - public void binderDied() { - mPrivateHandler.post(new Runnable() { - @Override - public void run() { - onConnectionDied(Connection.this); - } - }); - } - - public int createRouteController(String routeId, String routeGroupId) { - int controllerId = mNextControllerId++; - Bundle data = new Bundle(); - data.putString(CLIENT_DATA_ROUTE_ID, routeId); - data.putString(CLIENT_DATA_ROUTE_LIBRARY_GROUP, routeGroupId); - sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER, - mNextRequestId++, controllerId, null, data); - return controllerId; - } - - public void releaseRouteController(int controllerId) { - sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER, - mNextRequestId++, controllerId, null, null); - } - - public void selectRoute(int controllerId) { - sendRequest(CLIENT_MSG_SELECT_ROUTE, - mNextRequestId++, controllerId, null, null); - } - - public void unselectRoute(int controllerId, int reason) { - Bundle extras = new Bundle(); - extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason); - sendRequest(CLIENT_MSG_UNSELECT_ROUTE, - mNextRequestId++, controllerId, null, extras); - } - - public void setVolume(int controllerId, int volume) { - Bundle data = new Bundle(); - data.putInt(CLIENT_DATA_VOLUME, volume); - sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME, - mNextRequestId++, controllerId, null, data); - } - - public void updateVolume(int controllerId, int delta) { - Bundle data = new Bundle(); - data.putInt(CLIENT_DATA_VOLUME, delta); - sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME, - mNextRequestId++, controllerId, null, data); - } - - public boolean sendControlRequest(int controllerId, Intent intent, - ControlRequestCallback callback) { - int requestId = mNextRequestId++; - if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST, - requestId, controllerId, intent, null)) { - if (callback != null) { - mPendingCallbacks.put(requestId, callback); - } - return true; - } - return false; - } - - public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) { - sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST, - mNextRequestId++, 0, request != null ? request.asBundle() : null, null); - } - - private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - msg.arg1 = requestId; - msg.arg2 = arg; - msg.obj = obj; - msg.setData(data); - msg.replyTo = mReceiveMessenger; - try { - mServiceMessenger.send(msg); - return true; - } catch (DeadObjectException ex) { - // The service died. - } catch (RemoteException ex) { - if (what != CLIENT_MSG_UNREGISTER) { - Log.e(TAG, "Could not send message to service.", ex); - } - } - return false; - } - } - - private static final class PrivateHandler extends Handler { - PrivateHandler() { - } - } - - /** - * Handler that receives messages from the server. - *

    - * This inner class is static and only retains a weak reference to the connection - * to prevent the client from being leaked in case the service is holding an - * active reference to the client's messenger. - *

    - * This handler should not be used to handle any messages other than those - * that come from the service. - *

    - */ - private static final class ReceiveHandler extends Handler { - private final WeakReference mConnectionRef; - - public ReceiveHandler(Connection connection) { - mConnectionRef = new WeakReference(connection); - } - - public void dispose() { - mConnectionRef.clear(); - } - - @Override - public void handleMessage(Message msg) { - Connection connection = mConnectionRef.get(); - if (connection != null) { - final int what = msg.what; - final int requestId = msg.arg1; - final int arg = msg.arg2; - final Object obj = msg.obj; - final Bundle data = msg.peekData(); - if (!processMessage(connection, what, requestId, arg, obj, data)) { - if (DEBUG) { - Log.d(TAG, "Unhandled message from server: " + msg); - } - } - } - } - - private boolean processMessage(Connection connection, - int what, int requestId, int arg, Object obj, Bundle data) { - switch (what) { - case SERVICE_MSG_GENERIC_FAILURE: - connection.onGenericFailure(requestId); - return true; - - case SERVICE_MSG_GENERIC_SUCCESS: - connection.onGenericSuccess(requestId); - return true; - - case SERVICE_MSG_REGISTERED: - if (obj == null || obj instanceof Bundle) { - return connection.onRegistered(requestId, arg, (Bundle)obj); - } - break; - - case SERVICE_MSG_DESCRIPTOR_CHANGED: - if (obj == null || obj instanceof Bundle) { - return connection.onDescriptorChanged((Bundle)obj); - } - break; - - case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED: - if (obj == null || obj instanceof Bundle) { - return connection.onControlRequestSucceeded( - requestId, (Bundle)obj); - } - break; - - case SERVICE_MSG_CONTROL_REQUEST_FAILED: - if (obj == null || obj instanceof Bundle) { - String error = (data == null ? null : - data.getString(SERVICE_DATA_ERROR)); - return connection.onControlRequestFailed( - requestId, error, (Bundle)obj); - } - break; - } - return false; - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java deleted file mode 100644 index ba1f647fac..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.Handler; - -import java.util.ArrayList; -import java.util.Collections; - -/** - * Watches for media route provider services to be installed. - * Adds a provider to the media router for each registered service. - * - * @see RegisteredMediaRouteProvider - */ -final class RegisteredMediaRouteProviderWatcher { - private final Context mContext; - private final Callback mCallback; - private final Handler mHandler; - private final PackageManager mPackageManager; - - private final ArrayList mProviders = - new ArrayList(); - private boolean mRunning; - - public RegisteredMediaRouteProviderWatcher(Context context, Callback callback) { - mContext = context; - mCallback = callback; - mHandler = new Handler(); - mPackageManager = context.getPackageManager(); - } - - public void start() { - if (!mRunning) { - mRunning = true; - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - filter.addAction(Intent.ACTION_PACKAGE_REPLACED); - filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - filter.addDataScheme("package"); - mContext.registerReceiver(mScanPackagesReceiver, filter, null, mHandler); - - // Scan packages. - // Also has the side-effect of restarting providers if needed. - mHandler.post(mScanPackagesRunnable); - } - } - - public void stop() { - if (mRunning) { - mRunning = false; - - mContext.unregisterReceiver(mScanPackagesReceiver); - mHandler.removeCallbacks(mScanPackagesRunnable); - - // Stop all providers. - for (int i = mProviders.size() - 1; i >= 0; i--) { - mProviders.get(i).stop(); - } - } - } - - void scanPackages() { - if (!mRunning) { - return; - } - - // Add providers for all new services. - // Reorder the list so that providers left at the end will be the ones to remove. - int targetIndex = 0; - Intent intent = new Intent(MediaRouteProviderService.SERVICE_INTERFACE); - for (ResolveInfo resolveInfo : mPackageManager.queryIntentServices(intent, 0)) { - ServiceInfo serviceInfo = resolveInfo.serviceInfo; - if (serviceInfo != null) { - int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name); - if (sourceIndex < 0) { - RegisteredMediaRouteProvider provider = - new RegisteredMediaRouteProvider(mContext, - new ComponentName(serviceInfo.packageName, serviceInfo.name)); - provider.start(); - mProviders.add(targetIndex++, provider); - mCallback.addProvider(provider); - } else if (sourceIndex >= targetIndex) { - RegisteredMediaRouteProvider provider = mProviders.get(sourceIndex); - provider.start(); // restart the provider if needed - provider.rebindIfDisconnected(); - Collections.swap(mProviders, sourceIndex, targetIndex++); - } - } - } - - // Remove providers for missing services. - if (targetIndex < mProviders.size()) { - for (int i = mProviders.size() - 1; i >= targetIndex; i--) { - RegisteredMediaRouteProvider provider = mProviders.get(i); - mCallback.removeProvider(provider); - mProviders.remove(provider); - provider.stop(); - } - } - } - - private int findProvider(String packageName, String className) { - int count = mProviders.size(); - for (int i = 0; i < count; i++) { - RegisteredMediaRouteProvider provider = mProviders.get(i); - if (provider.hasComponentName(packageName, className)) { - return i; - } - } - return -1; - } - - private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - scanPackages(); - } - }; - - private final Runnable mScanPackagesRunnable = new Runnable() { - @Override - public void run() { - scanPackages(); - } - }; - - public interface Callback { - void addProvider(MediaRouteProvider provider); - void removeProvider(MediaRouteProvider provider); - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java deleted file mode 100644 index 65c5518824..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.content.Context; -import android.media.AudioManager; -import android.os.Build; - -import java.lang.ref.WeakReference; - -/** - * Provides access to features of the remote control client. - * - * Hidden for now but we might want to make this available to applications - * in the future. - */ -abstract class RemoteControlClientCompat { - protected final Context mContext; - protected final Object mRcc; - protected VolumeCallback mVolumeCallback; - - protected RemoteControlClientCompat(Context context, Object rcc) { - mContext = context; - mRcc = rcc; - } - - public static RemoteControlClientCompat obtain(Context context, Object rcc) { - if (Build.VERSION.SDK_INT >= 16) { - return new JellybeanImpl(context, rcc); - } - return new LegacyImpl(context, rcc); - } - - public Object getRemoteControlClient() { - return mRcc; - } - - /** - * Sets the current playback information. - * Must be called at least once to attach to the remote control client. - * - * @param info The playback information. Must not be null. - */ - public void setPlaybackInfo(PlaybackInfo info) { - } - - /** - * Sets a callback to receive volume change requests from the remote control client. - * - * @param callback The volume callback to use or null if none. - */ - public void setVolumeCallback(VolumeCallback callback) { - mVolumeCallback = callback; - } - - /** - * Specifies information about the playback. - */ - public static final class PlaybackInfo { - public int volume; - public int volumeMax; - public int volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED; - public int playbackStream = AudioManager.STREAM_MUSIC; - public int playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE; - } - - /** - * Called when volume updates are requested by the remote control client. - */ - public interface VolumeCallback { - /** - * Called when the volume should be increased or decreased. - * - * @param direction An integer indicating whether the volume is to be increased - * (positive value) or decreased (negative value). - * For bundled changes, the absolute value indicates the number of changes - * in the same direction, e.g. +3 corresponds to three "volume up" changes. - */ - public void onVolumeUpdateRequest(int direction); - - /** - * Called when the volume for the route should be set to the given value. - * - * @param volume An integer indicating the new volume value that should be used, - * always between 0 and the value set by {@link PlaybackInfo#volumeMax}. - */ - public void onVolumeSetRequest(int volume); - } - - /** - * Legacy implementation for platform versions prior to Jellybean. - * Does nothing. - */ - static class LegacyImpl extends RemoteControlClientCompat { - public LegacyImpl(Context context, Object rcc) { - super(context, rcc); - } - } - - /** - * Implementation for Jellybean. - * - * The basic idea of this implementation is to attach the RCC to a UserRouteInfo - * in order to hook up stream metadata and volume callbacks because there is no - * other API available to do so in this platform version. The UserRouteInfo itself - * is not attached to the MediaRouter so it is transparent to the user. - */ - // @@RequiresApi(16) - static class JellybeanImpl extends RemoteControlClientCompat { - private final Object mRouterObj; - private final Object mUserRouteCategoryObj; - private final Object mUserRouteObj; - private boolean mRegistered; - - public JellybeanImpl(Context context, Object rcc) { - super(context, rcc); - - mRouterObj = MediaRouterJellybean.getMediaRouter(context); - mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory( - mRouterObj, "", false); - mUserRouteObj = MediaRouterJellybean.createUserRoute( - mRouterObj, mUserRouteCategoryObj); - } - - @Override - public void setPlaybackInfo(PlaybackInfo info) { - MediaRouterJellybean.UserRouteInfo.setVolume( - mUserRouteObj, info.volume); - MediaRouterJellybean.UserRouteInfo.setVolumeMax( - mUserRouteObj, info.volumeMax); - MediaRouterJellybean.UserRouteInfo.setVolumeHandling( - mUserRouteObj, info.volumeHandling); - MediaRouterJellybean.UserRouteInfo.setPlaybackStream( - mUserRouteObj, info.playbackStream); - MediaRouterJellybean.UserRouteInfo.setPlaybackType( - mUserRouteObj, info.playbackType); - - if (!mRegistered) { - mRegistered = true; - MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj, - MediaRouterJellybean.createVolumeCallback( - new VolumeCallbackWrapper(this))); - MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc); - } - } - - private static final class VolumeCallbackWrapper - implements MediaRouterJellybean.VolumeCallback { - // Unfortunately, the framework never unregisters its volume observer from - // the audio service so the UserRouteInfo object may leak along with - // any callbacks that we attach to it. Use a weak reference to prevent - // the volume callback from holding strong references to anything important. - private final WeakReference mImplWeak; - - public VolumeCallbackWrapper(JellybeanImpl impl) { - mImplWeak = new WeakReference(impl); - } - - @Override - public void onVolumeUpdateRequest(Object routeObj, int direction) { - JellybeanImpl impl = mImplWeak.get(); - if (impl != null && impl.mVolumeCallback != null) { - impl.mVolumeCallback.onVolumeUpdateRequest(direction); - } - } - - @Override - public void onVolumeSetRequest(Object routeObj, int volume) { - JellybeanImpl impl = mImplWeak.get(); - if (impl != null && impl.mVolumeCallback != null) { - impl.mVolumeCallback.onVolumeSetRequest(volume); - } - } - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java deleted file mode 100644 index e76564eb2c..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java +++ /dev/null @@ -1,1045 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.support.mediarouter.media; - -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; - -import androidx.core.util.ObjectsCompat; - -/** - * A helper class for playing media on remote routes using the remote playback protocol - * defined by {@link MediaControlIntent}. - *

    - * The client maintains session state and offers a simplified interface for issuing - * remote playback media control intents to a single route. - *

    - */ -public class RemotePlaybackClient { - static final String TAG = "RemotePlaybackClient"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private final Context mContext; - private final MediaRouter.RouteInfo mRoute; - private final ActionReceiver mActionReceiver; - private final PendingIntent mItemStatusPendingIntent; - private final PendingIntent mSessionStatusPendingIntent; - private final PendingIntent mMessagePendingIntent; - - private boolean mRouteSupportsRemotePlayback; - private boolean mRouteSupportsQueuing; - private boolean mRouteSupportsSessionManagement; - private boolean mRouteSupportsMessaging; - - String mSessionId; - StatusCallback mStatusCallback; - OnMessageReceivedListener mOnMessageReceivedListener; - - /** - * Creates a remote playback client for a route. - * - * @param route The media route. - */ - public RemotePlaybackClient(Context context, MediaRouter.RouteInfo route) { - if (context == null) { - throw new IllegalArgumentException("context must not be null"); - } - if (route == null) { - throw new IllegalArgumentException("route must not be null"); - } - - mContext = context; - mRoute = route; - - IntentFilter actionFilter = new IntentFilter(); - actionFilter.addAction(ActionReceiver.ACTION_ITEM_STATUS_CHANGED); - actionFilter.addAction(ActionReceiver.ACTION_SESSION_STATUS_CHANGED); - actionFilter.addAction(ActionReceiver.ACTION_MESSAGE_RECEIVED); - mActionReceiver = new ActionReceiver(); - context.registerReceiver(mActionReceiver, actionFilter); - - Intent itemStatusIntent = new Intent(ActionReceiver.ACTION_ITEM_STATUS_CHANGED); - itemStatusIntent.setPackage(context.getPackageName()); - mItemStatusPendingIntent = PendingIntent.getBroadcast( - context, 0, itemStatusIntent, 0); - - Intent sessionStatusIntent = new Intent(ActionReceiver.ACTION_SESSION_STATUS_CHANGED); - sessionStatusIntent.setPackage(context.getPackageName()); - mSessionStatusPendingIntent = PendingIntent.getBroadcast( - context, 0, sessionStatusIntent, 0); - - Intent messageIntent = new Intent(ActionReceiver.ACTION_MESSAGE_RECEIVED); - messageIntent.setPackage(context.getPackageName()); - mMessagePendingIntent = PendingIntent.getBroadcast( - context, 0, messageIntent, 0); - detectFeatures(); - } - - /** - * Releases resources owned by the client. - */ - public void release() { - mContext.unregisterReceiver(mActionReceiver); - } - - /** - * Returns true if the route supports remote playback. - *

    - * If the route does not support remote playback, then none of the functionality - * offered by the client will be available. - *

    - * This method returns true if the route supports all of the following - * actions: {@link MediaControlIntent#ACTION_PLAY play}, - * {@link MediaControlIntent#ACTION_SEEK seek}, - * {@link MediaControlIntent#ACTION_GET_STATUS get status}, - * {@link MediaControlIntent#ACTION_PAUSE pause}, - * {@link MediaControlIntent#ACTION_RESUME resume}, - * {@link MediaControlIntent#ACTION_STOP stop}. - *

    - * - * @return True if remote playback is supported. - */ - public boolean isRemotePlaybackSupported() { - return mRouteSupportsRemotePlayback; - } - - /** - * Returns true if the route supports queuing features. - *

    - * If the route does not support queuing, then at most one media item can be played - * at a time and the {@link #enqueue} method will not be available. - *

    - * This method returns true if the route supports all of the basic remote playback - * actions and all of the following actions: - * {@link MediaControlIntent#ACTION_ENQUEUE enqueue}, - * {@link MediaControlIntent#ACTION_REMOVE remove}. - *

    - * - * @return True if queuing is supported. Implies {@link #isRemotePlaybackSupported} - * is also true. - * - * @see #isRemotePlaybackSupported - */ - public boolean isQueuingSupported() { - return mRouteSupportsQueuing; - } - - /** - * Returns true if the route supports session management features. - *

    - * If the route does not support session management, then the session will - * not be created until the first media item is played. - *

    - * This method returns true if the route supports all of the basic remote playback - * actions and all of the following actions: - * {@link MediaControlIntent#ACTION_START_SESSION start session}, - * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS get session status}, - * {@link MediaControlIntent#ACTION_END_SESSION end session}. - *

    - * - * @return True if session management is supported. - * Implies {@link #isRemotePlaybackSupported} is also true. - * - * @see #isRemotePlaybackSupported - */ - public boolean isSessionManagementSupported() { - return mRouteSupportsSessionManagement; - } - - /** - * Returns true if the route supports messages. - *

    - * This method returns true if the route supports all of the basic remote playback - * actions and all of the following actions: - * {@link MediaControlIntent#ACTION_START_SESSION start session}, - * {@link MediaControlIntent#ACTION_SEND_MESSAGE send message}, - * {@link MediaControlIntent#ACTION_END_SESSION end session}. - *

    - * - * @return True if session management is supported. - * Implies {@link #isRemotePlaybackSupported} is also true. - * - * @see #isRemotePlaybackSupported - */ - public boolean isMessagingSupported() { - return mRouteSupportsMessaging; - } - - /** - * Gets the current session id if there is one. - * - * @return The current session id, or null if none. - */ - public String getSessionId() { - return mSessionId; - } - - /** - * Sets the current session id. - *

    - * It is usually not necessary to set the session id explicitly since - * it is created as a side-effect of other requests such as - * {@link #play}, {@link #enqueue}, and {@link #startSession}. - *

    - * - * @param sessionId The new session id, or null if none. - */ - public void setSessionId(String sessionId) { - if (!ObjectsCompat.equals(mSessionId, sessionId)) { - if (DEBUG) { - Log.d(TAG, "Session id is now: " + sessionId); - } - mSessionId = sessionId; - if (mStatusCallback != null) { - mStatusCallback.onSessionChanged(sessionId); - } - } - } - - /** - * Returns true if the client currently has a session. - *

    - * Equivalent to checking whether {@link #getSessionId} returns a non-null result. - *

    - * - * @return True if there is a current session. - */ - public boolean hasSession() { - return mSessionId != null; - } - - /** - * Sets a callback that should receive status updates when the state of - * media sessions or media items created by this instance of the remote - * playback client changes. - *

    - * The callback should be set before the session is created or any play - * commands are issued. - *

    - * - * @param callback The callback to set. May be null to remove the previous callback. - */ - public void setStatusCallback(StatusCallback callback) { - mStatusCallback = callback; - } - - /** - * Sets a callback that should receive messages when a message is sent from - * media sessions created by this instance of the remote playback client changes. - *

    - * The callback should be set before the session is created. - *

    - * - * @param listener The callback to set. May be null to remove the previous callback. - */ - public void setOnMessageReceivedListener(OnMessageReceivedListener listener) { - mOnMessageReceivedListener = listener; - } - - /** - * Sends a request to play a media item. - *

    - * Clears the queue and starts playing the new item immediately. If the queue - * was previously paused, then it is resumed as a side-effect of this request. - *

    - * The request is issued in the current session. If no session is available, then - * one is created implicitly. - *

    - * Please refer to {@link MediaControlIntent#ACTION_PLAY ACTION_PLAY} for - * more information about the semantics of this request. - *

    - * - * @param contentUri The content Uri to play. - * @param mimeType The mime type of the content, or null if unknown. - * @param positionMillis The initial content position for the item in milliseconds, - * or 0 to start at the beginning. - * @param metadata The media item metadata bundle, or null if none. - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_PLAY} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws UnsupportedOperationException if the route does not support remote playback. - * - * @see MediaControlIntent#ACTION_PLAY - * @see #isRemotePlaybackSupported - */ - public void play(Uri contentUri, String mimeType, Bundle metadata, - long positionMillis, Bundle extras, ItemActionCallback callback) { - playOrEnqueue(contentUri, mimeType, metadata, positionMillis, - extras, callback, MediaControlIntent.ACTION_PLAY); - } - - /** - * Sends a request to enqueue a media item. - *

    - * Enqueues a new item to play. If the queue was previously paused, then will - * remain paused. - *

    - * The request is issued in the current session. If no session is available, then - * one is created implicitly. - *

    - * Please refer to {@link MediaControlIntent#ACTION_ENQUEUE ACTION_ENQUEUE} for - * more information about the semantics of this request. - *

    - * - * @param contentUri The content Uri to enqueue. - * @param mimeType The mime type of the content, or null if unknown. - * @param positionMillis The initial content position for the item in milliseconds, - * or 0 to start at the beginning. - * @param metadata The media item metadata bundle, or null if none. - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_ENQUEUE} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws UnsupportedOperationException if the route does not support queuing. - * - * @see MediaControlIntent#ACTION_ENQUEUE - * @see #isRemotePlaybackSupported - * @see #isQueuingSupported - */ - public void enqueue(Uri contentUri, String mimeType, Bundle metadata, - long positionMillis, Bundle extras, ItemActionCallback callback) { - playOrEnqueue(contentUri, mimeType, metadata, positionMillis, - extras, callback, MediaControlIntent.ACTION_ENQUEUE); - } - - private void playOrEnqueue(Uri contentUri, String mimeType, Bundle metadata, - long positionMillis, Bundle extras, - final ItemActionCallback callback, String action) { - if (contentUri == null) { - throw new IllegalArgumentException("contentUri must not be null"); - } - throwIfRemotePlaybackNotSupported(); - if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) { - throwIfQueuingNotSupported(); - } - - Intent intent = new Intent(action); - intent.setDataAndType(contentUri, mimeType); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER, - mItemStatusPendingIntent); - if (metadata != null) { - intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata); - } - if (positionMillis != 0) { - intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis); - } - performItemAction(intent, mSessionId, null, extras, callback); - } - - /** - * Sends a request to seek to a new position in a media item. - *

    - * Seeks to a new position. If the queue was previously paused then it - * remains paused but the item's new position is still remembered. - *

    - * The request is issued in the current session. - *

    - * Please refer to {@link MediaControlIntent#ACTION_SEEK ACTION_SEEK} for - * more information about the semantics of this request. - *

    - * - * @param itemId The item id. - * @param positionMillis The new content position for the item in milliseconds, - * or 0 to start at the beginning. - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_SEEK} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * - * @see MediaControlIntent#ACTION_SEEK - * @see #isRemotePlaybackSupported - */ - public void seek(String itemId, long positionMillis, Bundle extras, - ItemActionCallback callback) { - if (itemId == null) { - throw new IllegalArgumentException("itemId must not be null"); - } - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_SEEK); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis); - performItemAction(intent, mSessionId, itemId, extras, callback); - } - - /** - * Sends a request to get the status of a media item. - *

    - * The request is issued in the current session. - *

    - * Please refer to {@link MediaControlIntent#ACTION_GET_STATUS ACTION_GET_STATUS} for - * more information about the semantics of this request. - *

    - * - * @param itemId The item id. - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_GET_STATUS} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * - * @see MediaControlIntent#ACTION_GET_STATUS - * @see #isRemotePlaybackSupported - */ - public void getStatus(String itemId, Bundle extras, ItemActionCallback callback) { - if (itemId == null) { - throw new IllegalArgumentException("itemId must not be null"); - } - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_GET_STATUS); - performItemAction(intent, mSessionId, itemId, extras, callback); - } - - /** - * Sends a request to remove a media item from the queue. - *

    - * The request is issued in the current session. - *

    - * Please refer to {@link MediaControlIntent#ACTION_REMOVE ACTION_REMOVE} for - * more information about the semantics of this request. - *

    - * - * @param itemId The item id. - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_REMOVE} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * @throws UnsupportedOperationException if the route does not support queuing. - * - * @see MediaControlIntent#ACTION_REMOVE - * @see #isRemotePlaybackSupported - * @see #isQueuingSupported - */ - public void remove(String itemId, Bundle extras, ItemActionCallback callback) { - if (itemId == null) { - throw new IllegalArgumentException("itemId must not be null"); - } - throwIfQueuingNotSupported(); - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_REMOVE); - performItemAction(intent, mSessionId, itemId, extras, callback); - } - - /** - * Sends a request to pause media playback. - *

    - * The request is issued in the current session. If playback is already paused - * then the request has no effect. - *

    - * Please refer to {@link MediaControlIntent#ACTION_PAUSE ACTION_PAUSE} for - * more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_PAUSE} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * - * @see MediaControlIntent#ACTION_PAUSE - * @see #isRemotePlaybackSupported - */ - public void pause(Bundle extras, SessionActionCallback callback) { - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE); - performSessionAction(intent, mSessionId, extras, callback); - } - - /** - * Sends a request to resume (unpause) media playback. - *

    - * The request is issued in the current session. If playback is not paused - * then the request has no effect. - *

    - * Please refer to {@link MediaControlIntent#ACTION_RESUME ACTION_RESUME} for - * more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_RESUME} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * - * @see MediaControlIntent#ACTION_RESUME - * @see #isRemotePlaybackSupported - */ - public void resume(Bundle extras, SessionActionCallback callback) { - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_RESUME); - performSessionAction(intent, mSessionId, extras, callback); - } - - /** - * Sends a request to stop media playback and clear the media playback queue. - *

    - * The request is issued in the current session. If the queue is already - * empty then the request has no effect. - *

    - * Please refer to {@link MediaControlIntent#ACTION_STOP ACTION_STOP} for - * more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_STOP} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * - * @see MediaControlIntent#ACTION_STOP - * @see #isRemotePlaybackSupported - */ - public void stop(Bundle extras, SessionActionCallback callback) { - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_STOP); - performSessionAction(intent, mSessionId, extras, callback); - } - - /** - * Sends a request to start a new media playback session. - *

    - * The application must wait for the callback to indicate that this request - * is complete before issuing other requests that affect the session. If this - * request is successful then the previous session will be invalidated. - *

    - * Please refer to {@link MediaControlIntent#ACTION_START_SESSION ACTION_START_SESSION} - * for more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_START_SESSION} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws UnsupportedOperationException if the route does not support session management. - * - * @see MediaControlIntent#ACTION_START_SESSION - * @see #isRemotePlaybackSupported - * @see #isSessionManagementSupported - */ - public void startSession(Bundle extras, SessionActionCallback callback) { - throwIfSessionManagementNotSupported(); - - Intent intent = new Intent(MediaControlIntent.ACTION_START_SESSION); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER, - mSessionStatusPendingIntent); - if (mRouteSupportsMessaging) { - intent.putExtra(MediaControlIntent.EXTRA_MESSAGE_RECEIVER, mMessagePendingIntent); - } - performSessionAction(intent, null, extras, callback); - } - - /** - * Sends a message. - *

    - * The request is issued in the current session. - *

    - * Please refer to {@link MediaControlIntent#ACTION_SEND_MESSAGE} for - * more information about the semantics of this request. - *

    - * - * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}. - * @param callback A callback to invoke when the request has been processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * @throws UnsupportedOperationException if the route does not support messages. - * - * @see MediaControlIntent#ACTION_SEND_MESSAGE - * @see #isMessagingSupported - */ - public void sendMessage(Bundle message, SessionActionCallback callback) { - throwIfNoCurrentSession(); - throwIfMessageNotSupported(); - - Intent intent = new Intent(MediaControlIntent.ACTION_SEND_MESSAGE); - performSessionAction(intent, mSessionId, message, callback); - } - - /** - * Sends a request to get the status of the media playback session. - *

    - * The request is issued in the current session. - *

    - * Please refer to {@link MediaControlIntent#ACTION_GET_SESSION_STATUS - * ACTION_GET_SESSION_STATUS} for more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * @throws UnsupportedOperationException if the route does not support session management. - * - * @see MediaControlIntent#ACTION_GET_SESSION_STATUS - * @see #isRemotePlaybackSupported - * @see #isSessionManagementSupported - */ - public void getSessionStatus(Bundle extras, SessionActionCallback callback) { - throwIfSessionManagementNotSupported(); - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_GET_SESSION_STATUS); - performSessionAction(intent, mSessionId, extras, callback); - } - - /** - * Sends a request to end the media playback session. - *

    - * The request is issued in the current session. If this request is successful, - * the {@link #getSessionId session id property} will be set to null after - * the callback is invoked. - *

    - * Please refer to {@link MediaControlIntent#ACTION_END_SESSION ACTION_END_SESSION} - * for more information about the semantics of this request. - *

    - * - * @param extras A bundle of extra arguments to be added to the - * {@link MediaControlIntent#ACTION_END_SESSION} intent, or null if none. - * @param callback A callback to invoke when the request has been - * processed, or null if none. - * - * @throws IllegalStateException if there is no current session. - * @throws UnsupportedOperationException if the route does not support session management. - * - * @see MediaControlIntent#ACTION_END_SESSION - * @see #isRemotePlaybackSupported - * @see #isSessionManagementSupported - */ - public void endSession(Bundle extras, SessionActionCallback callback) { - throwIfSessionManagementNotSupported(); - throwIfNoCurrentSession(); - - Intent intent = new Intent(MediaControlIntent.ACTION_END_SESSION); - performSessionAction(intent, mSessionId, extras, callback); - } - - private void performItemAction(final Intent intent, - final String sessionId, final String itemId, - Bundle extras, final ItemActionCallback callback) { - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - if (sessionId != null) { - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId); - } - if (itemId != null) { - intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, itemId); - } - if (extras != null) { - intent.putExtras(extras); - } - logRequest(intent); - mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() { - @Override - public void onResult(Bundle data) { - if (data != null) { - String sessionIdResult = inferMissingResult(sessionId, - data.getString(MediaControlIntent.EXTRA_SESSION_ID)); - MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle( - data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS)); - String itemIdResult = inferMissingResult(itemId, - data.getString(MediaControlIntent.EXTRA_ITEM_ID)); - MediaItemStatus itemStatus = MediaItemStatus.fromBundle( - data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS)); - adoptSession(sessionIdResult); - if (sessionIdResult != null && itemIdResult != null && itemStatus != null) { - if (DEBUG) { - Log.d(TAG, "Received result from " + intent.getAction() - + ": data=" + bundleToString(data) - + ", sessionId=" + sessionIdResult - + ", sessionStatus=" + sessionStatus - + ", itemId=" + itemIdResult - + ", itemStatus=" + itemStatus); - } - callback.onResult(data, sessionIdResult, sessionStatus, - itemIdResult, itemStatus); - return; - } - } - handleInvalidResult(intent, callback, data); - } - - @Override - public void onError(String error, Bundle data) { - handleError(intent, callback, error, data); - } - }); - } - - private void performSessionAction(final Intent intent, final String sessionId, - Bundle extras, final SessionActionCallback callback) { - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - if (sessionId != null) { - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId); - } - if (extras != null) { - intent.putExtras(extras); - } - logRequest(intent); - mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() { - @Override - public void onResult(Bundle data) { - if (data != null) { - String sessionIdResult = inferMissingResult(sessionId, - data.getString(MediaControlIntent.EXTRA_SESSION_ID)); - MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle( - data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS)); - adoptSession(sessionIdResult); - if (sessionIdResult != null) { - if (DEBUG) { - Log.d(TAG, "Received result from " + intent.getAction() - + ": data=" + bundleToString(data) - + ", sessionId=" + sessionIdResult - + ", sessionStatus=" + sessionStatus); - } - try { - callback.onResult(data, sessionIdResult, sessionStatus); - } finally { - if (intent.getAction().equals(MediaControlIntent.ACTION_END_SESSION) - && sessionIdResult.equals(mSessionId)) { - setSessionId(null); - } - } - return; - } - } - handleInvalidResult(intent, callback, data); - } - - @Override - public void onError(String error, Bundle data) { - handleError(intent, callback, error, data); - } - }); - } - - void adoptSession(String sessionId) { - if (sessionId != null) { - setSessionId(sessionId); - } - } - - void handleInvalidResult(Intent intent, ActionCallback callback, - Bundle data) { - Log.w(TAG, "Received invalid result data from " + intent.getAction() - + ": data=" + bundleToString(data)); - callback.onError(null, MediaControlIntent.ERROR_UNKNOWN, data); - } - - void handleError(Intent intent, ActionCallback callback, - String error, Bundle data) { - final int code; - if (data != null) { - code = data.getInt(MediaControlIntent.EXTRA_ERROR_CODE, - MediaControlIntent.ERROR_UNKNOWN); - } else { - code = MediaControlIntent.ERROR_UNKNOWN; - } - if (DEBUG) { - Log.w(TAG, "Received error from " + intent.getAction() - + ": error=" + error - + ", code=" + code - + ", data=" + bundleToString(data)); - } - callback.onError(error, code, data); - } - - private void detectFeatures() { - mRouteSupportsRemotePlayback = routeSupportsAction(MediaControlIntent.ACTION_PLAY) - && routeSupportsAction(MediaControlIntent.ACTION_SEEK) - && routeSupportsAction(MediaControlIntent.ACTION_GET_STATUS) - && routeSupportsAction(MediaControlIntent.ACTION_PAUSE) - && routeSupportsAction(MediaControlIntent.ACTION_RESUME) - && routeSupportsAction(MediaControlIntent.ACTION_STOP); - mRouteSupportsQueuing = mRouteSupportsRemotePlayback - && routeSupportsAction(MediaControlIntent.ACTION_ENQUEUE) - && routeSupportsAction(MediaControlIntent.ACTION_REMOVE); - mRouteSupportsSessionManagement = mRouteSupportsRemotePlayback - && routeSupportsAction(MediaControlIntent.ACTION_START_SESSION) - && routeSupportsAction(MediaControlIntent.ACTION_GET_SESSION_STATUS) - && routeSupportsAction(MediaControlIntent.ACTION_END_SESSION); - mRouteSupportsMessaging = doesRouteSupportMessaging(); - } - - private boolean routeSupportsAction(String action) { - return mRoute.supportsControlAction(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK, action); - } - - private boolean doesRouteSupportMessaging() { - for (IntentFilter filter : mRoute.getControlFilters()) { - if (filter.hasAction(MediaControlIntent.ACTION_SEND_MESSAGE)) { - return true; - } - } - return false; - } - - private void throwIfRemotePlaybackNotSupported() { - if (!mRouteSupportsRemotePlayback) { - throw new UnsupportedOperationException("The route does not support remote playback."); - } - } - - private void throwIfQueuingNotSupported() { - if (!mRouteSupportsQueuing) { - throw new UnsupportedOperationException("The route does not support queuing."); - } - } - - private void throwIfSessionManagementNotSupported() { - if (!mRouteSupportsSessionManagement) { - throw new UnsupportedOperationException("The route does not support " - + "session management."); - } - } - - private void throwIfMessageNotSupported() { - if (!mRouteSupportsMessaging) { - throw new UnsupportedOperationException("The route does not support message."); - } - } - - private void throwIfNoCurrentSession() { - if (mSessionId == null) { - throw new IllegalStateException("There is no current session."); - } - } - - static String inferMissingResult(String request, String result) { - if (result == null) { - // Result is missing. - return request; - } - if (request == null || request.equals(result)) { - // Request didn't specify a value or result matches request. - return result; - } - // Result conflicts with request. - return null; - } - - private static void logRequest(Intent intent) { - if (DEBUG) { - Log.d(TAG, "Sending request: " + intent); - } - } - - static String bundleToString(Bundle bundle) { - if (bundle != null) { - bundle.size(); // force bundle to be unparcelled - return bundle.toString(); - } - return "null"; - } - - private final class ActionReceiver extends BroadcastReceiver { - public static final String ACTION_ITEM_STATUS_CHANGED = - "androidx.mediarouter.media.actions.ACTION_ITEM_STATUS_CHANGED"; - public static final String ACTION_SESSION_STATUS_CHANGED = - "androidx.mediarouter.media.actions.ACTION_SESSION_STATUS_CHANGED"; - public static final String ACTION_MESSAGE_RECEIVED = - "androidx.mediarouter.media.actions.ACTION_MESSAGE_RECEIVED"; - - ActionReceiver() { - } - - @Override - public void onReceive(Context context, Intent intent) { - String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID); - if (sessionId == null || !sessionId.equals(mSessionId)) { - Log.w(TAG, "Discarding spurious status callback " - + "with missing or invalid session id: sessionId=" + sessionId); - return; - } - - MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle( - intent.getBundleExtra(MediaControlIntent.EXTRA_SESSION_STATUS)); - String action = intent.getAction(); - if (action.equals(ACTION_ITEM_STATUS_CHANGED)) { - String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID); - if (itemId == null) { - Log.w(TAG, "Discarding spurious status callback with missing item id."); - return; - } - - MediaItemStatus itemStatus = MediaItemStatus.fromBundle( - intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_STATUS)); - if (itemStatus == null) { - Log.w(TAG, "Discarding spurious status callback with missing item status."); - return; - } - - if (DEBUG) { - Log.d(TAG, "Received item status callback: sessionId=" + sessionId - + ", sessionStatus=" + sessionStatus - + ", itemId=" + itemId - + ", itemStatus=" + itemStatus); - } - - if (mStatusCallback != null) { - mStatusCallback.onItemStatusChanged(intent.getExtras(), - sessionId, sessionStatus, itemId, itemStatus); - } - } else if (action.equals(ACTION_SESSION_STATUS_CHANGED)) { - if (sessionStatus == null) { - Log.w(TAG, "Discarding spurious media status callback with " - +"missing session status."); - return; - } - - if (DEBUG) { - Log.d(TAG, "Received session status callback: sessionId=" + sessionId - + ", sessionStatus=" + sessionStatus); - } - - if (mStatusCallback != null) { - mStatusCallback.onSessionStatusChanged(intent.getExtras(), - sessionId, sessionStatus); - } - } else if (action.equals(ACTION_MESSAGE_RECEIVED)) { - if (DEBUG) { - Log.d(TAG, "Received message callback: sessionId=" + sessionId); - } - - if (mOnMessageReceivedListener != null) { - mOnMessageReceivedListener.onMessageReceived(sessionId, - intent.getBundleExtra(MediaControlIntent.EXTRA_MESSAGE)); - } - } - } - } - - /** - * A callback that will receive media status updates. - */ - public static abstract class StatusCallback { - /** - * Called when the status of a media item changes. - * - * @param data The result data bundle. - * @param sessionId The session id. - * @param sessionStatus The session status, or null if unknown. - * @param itemId The item id. - * @param itemStatus The item status. - */ - public void onItemStatusChanged(Bundle data, - String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - } - - /** - * Called when the status of a media session changes. - * - * @param data The result data bundle. - * @param sessionId The session id. - * @param sessionStatus The session status, or null if unknown. - */ - public void onSessionStatusChanged(Bundle data, - String sessionId, MediaSessionStatus sessionStatus) { - } - - /** - * Called when the session of the remote playback client changes. - * - * @param sessionId The new session id. - */ - public void onSessionChanged(String sessionId) { - } - } - - /** - * Base callback type for remote playback requests. - */ - public static abstract class ActionCallback { - /** - * Called when a media control request fails. - * - * @param error A localized error message which may be shown to the user, or null - * if the cause of the error is unclear. - * @param code The error code, or {@link MediaControlIntent#ERROR_UNKNOWN} if unknown. - * @param data The error data bundle, or null if none. - */ - public void onError(String error, int code, Bundle data) { - } - } - - /** - * Callback for remote playback requests that operate on items. - */ - public static abstract class ItemActionCallback extends ActionCallback { - /** - * Called when the request succeeds. - * - * @param data The result data bundle. - * @param sessionId The session id. - * @param sessionStatus The session status, or null if unknown. - * @param itemId The item id. - * @param itemStatus The item status. - */ - public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, - String itemId, MediaItemStatus itemStatus) { - } - } - - /** - * Callback for remote playback requests that operate on sessions. - */ - public static abstract class SessionActionCallback extends ActionCallback { - /** - * Called when the request succeeds. - * - * @param data The result data bundle. - * @param sessionId The session id. - * @param sessionStatus The session status, or null if unknown. - */ - public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { - } - } - - /** - * A callback that will receive messages from media sessions. - */ - public interface OnMessageReceivedListener { - /** - * Called when a message received. - * - * @param sessionId The session id. - * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}. - */ - void onMessageReceived(String sessionId, Bundle message); - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java deleted file mode 100644 index 53901a4284..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java +++ /dev/null @@ -1,882 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.support.mediarouter.media; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.media.AudioManager; -import android.os.Build; -import android.view.Display; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * Provides routes for built-in system destinations such as the local display - * and speaker. On Jellybean and newer platform releases, queries the framework - * MediaRouter for framework-provided routes and registers non-framework-provided - * routes as user routes. - */ -abstract class SystemMediaRouteProvider extends MediaRouteProvider { - private static final String TAG = "SystemMediaRouteProvider"; - - public static final String PACKAGE_NAME = "android"; - public static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; - - protected SystemMediaRouteProvider(Context context) { - super(context, new ProviderMetadata(new ComponentName(PACKAGE_NAME, - SystemMediaRouteProvider.class.getName()))); - } - - public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) { - if (Build.VERSION.SDK_INT >= 24) { - return new Api24Impl(context, syncCallback); - } - if (Build.VERSION.SDK_INT >= 18) { - return new JellybeanMr2Impl(context, syncCallback); - } - if (Build.VERSION.SDK_INT >= 17) { - return new JellybeanMr1Impl(context, syncCallback); - } - if (Build.VERSION.SDK_INT >= 16) { - return new JellybeanImpl(context, syncCallback); - } - return new LegacyImpl(context); - } - - /** - * Called by the media router when a route is added to synchronize state with - * the framework media router. - */ - public void onSyncRouteAdded(MediaRouter.RouteInfo route) { - } - - /** - * Called by the media router when a route is removed to synchronize state with - * the framework media router. - */ - public void onSyncRouteRemoved(MediaRouter.RouteInfo route) { - } - - /** - * Called by the media router when a route is changed to synchronize state with - * the framework media router. - */ - public void onSyncRouteChanged(MediaRouter.RouteInfo route) { - } - - /** - * Called by the media router when a route is selected to synchronize state with - * the framework media router. - */ - public void onSyncRouteSelected(MediaRouter.RouteInfo route) { - } - - /** - * Callbacks into the media router to synchronize state with the framework media router. - */ - public interface SyncCallback { - void onSystemRouteSelectedByDescriptorId(String id); - } - - protected Object getDefaultRoute() { - return null; - } - - protected Object getSystemRoute(MediaRouter.RouteInfo route) { - return null; - } - - /** - * Legacy implementation for platform versions prior to Jellybean. - */ - static class LegacyImpl extends SystemMediaRouteProvider { - static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC; - - private static final ArrayList CONTROL_FILTERS; - static { - IntentFilter f = new IntentFilter(); - f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); - f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); - - CONTROL_FILTERS = new ArrayList(); - CONTROL_FILTERS.add(f); - } - - final AudioManager mAudioManager; - private final VolumeChangeReceiver mVolumeChangeReceiver; - int mLastReportedVolume = -1; - - public LegacyImpl(Context context) { - super(context); - mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); - mVolumeChangeReceiver = new VolumeChangeReceiver(); - - context.registerReceiver(mVolumeChangeReceiver, - new IntentFilter(VolumeChangeReceiver.VOLUME_CHANGED_ACTION)); - publishRoutes(); - } - - void publishRoutes() { - Resources r = getContext().getResources(); - int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM); - mLastReportedVolume = mAudioManager.getStreamVolume(PLAYBACK_STREAM); - MediaRouteDescriptor defaultRoute = new MediaRouteDescriptor.Builder( - DEFAULT_ROUTE_ID, r.getString(R.string.mr_system_route_name)) - .addControlFilters(CONTROL_FILTERS) - .setPlaybackStream(PLAYBACK_STREAM) - .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL) - .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) - .setVolumeMax(maxVolume) - .setVolume(mLastReportedVolume) - .build(); - - MediaRouteProviderDescriptor providerDescriptor = - new MediaRouteProviderDescriptor.Builder() - .addRoute(defaultRoute) - .build(); - setDescriptor(providerDescriptor); - } - - @Override - public RouteController onCreateRouteController(String routeId) { - if (routeId.equals(DEFAULT_ROUTE_ID)) { - return new DefaultRouteController(); - } - return null; - } - - final class DefaultRouteController extends RouteController { - @Override - public void onSetVolume(int volume) { - mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0); - publishRoutes(); - } - - @Override - public void onUpdateVolume(int delta) { - int volume = mAudioManager.getStreamVolume(PLAYBACK_STREAM); - int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM); - int newVolume = Math.min(maxVolume, Math.max(0, volume + delta)); - if (newVolume != volume) { - mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0); - } - publishRoutes(); - } - } - - final class VolumeChangeReceiver extends BroadcastReceiver { - // These constants come from AudioManager. - public static final String VOLUME_CHANGED_ACTION = - "android.media.VOLUME_CHANGED_ACTION"; - public static final String EXTRA_VOLUME_STREAM_TYPE = - "android.media.EXTRA_VOLUME_STREAM_TYPE"; - public static final String EXTRA_VOLUME_STREAM_VALUE = - "android.media.EXTRA_VOLUME_STREAM_VALUE"; - - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) { - final int streamType = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1); - if (streamType == PLAYBACK_STREAM) { - final int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1); - if (volume >= 0 && volume != mLastReportedVolume) { - publishRoutes(); - } - } - } - } - } - } - - /** - * Jellybean implementation. - */ - // @@RequiresApi(16) - static class JellybeanImpl extends SystemMediaRouteProvider - implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback { - private static final ArrayList LIVE_AUDIO_CONTROL_FILTERS; - static { - IntentFilter f = new IntentFilter(); - f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); - - LIVE_AUDIO_CONTROL_FILTERS = new ArrayList(); - LIVE_AUDIO_CONTROL_FILTERS.add(f); - } - - private static final ArrayList LIVE_VIDEO_CONTROL_FILTERS; - static { - IntentFilter f = new IntentFilter(); - f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); - - LIVE_VIDEO_CONTROL_FILTERS = new ArrayList(); - LIVE_VIDEO_CONTROL_FILTERS.add(f); - } - - private final SyncCallback mSyncCallback; - - protected final Object mRouterObj; - protected final Object mCallbackObj; - protected final Object mVolumeCallbackObj; - protected final Object mUserRouteCategoryObj; - protected int mRouteTypes; - protected boolean mActiveScan; - protected boolean mCallbackRegistered; - - // Maintains an association from framework routes to support library routes. - // Note that we cannot use the tag field for this because an application may - // have published its own user routes to the framework media router and already - // used the tag for its own purposes. - protected final ArrayList mSystemRouteRecords = - new ArrayList(); - - // Maintains an association from support library routes to framework routes. - protected final ArrayList mUserRouteRecords = - new ArrayList(); - - private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround; - private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround; - - public JellybeanImpl(Context context, SyncCallback syncCallback) { - super(context); - mSyncCallback = syncCallback; - mRouterObj = MediaRouterJellybean.getMediaRouter(context); - mCallbackObj = createCallbackObj(); - mVolumeCallbackObj = createVolumeCallbackObj(); - - Resources r = ApiHelper.getLibResources(context); - mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory( - mRouterObj, r.getString(R.string.mr_user_route_category_name), false); - - updateSystemRoutes(); - } - - @Override - public RouteController onCreateRouteController(String routeId) { - int index = findSystemRouteRecordByDescriptorId(routeId); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - return new SystemRouteController(record.mRouteObj); - } - return null; - } - - @Override - public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { - int newRouteTypes = 0; - boolean newActiveScan = false; - if (request != null) { - final MediaRouteSelector selector = request.getSelector(); - final List categories = selector.getControlCategories(); - final int count = categories.size(); - for (int i = 0; i < count; i++) { - String category = categories.get(i); - if (category.equals(MediaControlIntent.CATEGORY_LIVE_AUDIO)) { - newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO; - } else if (category.equals(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { - newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO; - } else { - newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_USER; - } - } - newActiveScan = request.isActiveScan(); - } - - if (mRouteTypes != newRouteTypes || mActiveScan != newActiveScan) { - mRouteTypes = newRouteTypes; - mActiveScan = newActiveScan; - updateSystemRoutes(); - } - } - - @Override - public void onRouteAdded(Object routeObj) { - if (addSystemRouteNoPublish(routeObj)) { - publishRoutes(); - } - } - - private void updateSystemRoutes() { - updateCallback(); - boolean changed = false; - for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) { - changed |= addSystemRouteNoPublish(routeObj); - } - if (changed) { - publishRoutes(); - } - } - - private boolean addSystemRouteNoPublish(Object routeObj) { - if (getUserRouteRecord(routeObj) == null - && findSystemRouteRecord(routeObj) < 0) { - String id = assignRouteId(routeObj); - SystemRouteRecord record = new SystemRouteRecord(routeObj, id); - updateSystemRouteDescriptor(record); - mSystemRouteRecords.add(record); - return true; - } - return false; - } - - private String assignRouteId(Object routeObj) { - // TODO: The framework media router should supply a unique route id that - // we can use here. For now we use a hash of the route name and take care - // to dedupe it. - boolean isDefault = (getDefaultRoute() == routeObj); - String id = isDefault ? DEFAULT_ROUTE_ID : - String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode()); - if (findSystemRouteRecordByDescriptorId(id) < 0) { - return id; - } - for (int i = 2; ; i++) { - String newId = String.format(Locale.US, "%s_%d", id, i); - if (findSystemRouteRecordByDescriptorId(newId) < 0) { - return newId; - } - } - } - - @Override - public void onRouteRemoved(Object routeObj) { - if (getUserRouteRecord(routeObj) == null) { - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - mSystemRouteRecords.remove(index); - publishRoutes(); - } - } - } - - @Override - public void onRouteChanged(Object routeObj) { - if (getUserRouteRecord(routeObj) == null) { - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - updateSystemRouteDescriptor(record); - publishRoutes(); - } - } - } - - @Override - public void onRouteVolumeChanged(Object routeObj) { - if (getUserRouteRecord(routeObj) == null) { - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj); - if (newVolume != record.mRouteDescriptor.getVolume()) { - record.mRouteDescriptor = - new MediaRouteDescriptor.Builder(record.mRouteDescriptor) - .setVolume(newVolume) - .build(); - publishRoutes(); - } - } - } - } - - @Override - public void onRouteSelected(int type, Object routeObj) { - if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj, - MediaRouterJellybean.ALL_ROUTE_TYPES)) { - // The currently selected route has already changed so this callback - // is stale. Drop it to prevent getting into sync loops. - return; - } - - UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj); - if (userRouteRecord != null) { - userRouteRecord.mRoute.select(); - } else { - // Select the route if it already exists in the compat media router. - // If not, we will select it instead when the route is added. - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - mSyncCallback.onSystemRouteSelectedByDescriptorId(record.mRouteDescriptorId); - } - } - } - - @Override - public void onRouteUnselected(int type, Object routeObj) { - // Nothing to do when a route is unselected. - // We only need to handle when a route is selected. - } - - @Override - public void onRouteGrouped(Object routeObj, Object groupObj, int index) { - // Route grouping is deprecated and no longer supported. - } - - @Override - public void onRouteUngrouped(Object routeObj, Object groupObj) { - // Route grouping is deprecated and no longer supported. - } - - @Override - public void onVolumeSetRequest(Object routeObj, int volume) { - UserRouteRecord record = getUserRouteRecord(routeObj); - if (record != null) { - record.mRoute.requestSetVolume(volume); - } - } - - @Override - public void onVolumeUpdateRequest(Object routeObj, int direction) { - UserRouteRecord record = getUserRouteRecord(routeObj); - if (record != null) { - record.mRoute.requestUpdateVolume(direction); - } - } - - @Override - public void onSyncRouteAdded(MediaRouter.RouteInfo route) { - if (route.getProviderInstance() != this) { - Object routeObj = MediaRouterJellybean.createUserRoute( - mRouterObj, mUserRouteCategoryObj); - UserRouteRecord record = new UserRouteRecord(route, routeObj); - MediaRouterJellybean.RouteInfo.setTag(routeObj, record); - MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj); - updateUserRouteProperties(record); - mUserRouteRecords.add(record); - MediaRouterJellybean.addUserRoute(mRouterObj, routeObj); - } else { - // If the newly added route is the counterpart of the currently selected - // route in the framework media router then ensure it is selected in - // the compat media router. - Object routeObj = MediaRouterJellybean.getSelectedRoute( - mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES); - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - if (record.mRouteDescriptorId.equals(route.getDescriptorId())) { - route.select(); - } - } - } - } - - @Override - public void onSyncRouteRemoved(MediaRouter.RouteInfo route) { - if (route.getProviderInstance() != this) { - int index = findUserRouteRecord(route); - if (index >= 0) { - UserRouteRecord record = mUserRouteRecords.remove(index); - MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null); - MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null); - MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj); - } - } - } - - @Override - public void onSyncRouteChanged(MediaRouter.RouteInfo route) { - if (route.getProviderInstance() != this) { - int index = findUserRouteRecord(route); - if (index >= 0) { - UserRouteRecord record = mUserRouteRecords.get(index); - updateUserRouteProperties(record); - } - } - } - - @Override - public void onSyncRouteSelected(MediaRouter.RouteInfo route) { - if (!route.isSelected()) { - // The currently selected route has already changed so this callback - // is stale. Drop it to prevent getting into sync loops. - return; - } - - if (route.getProviderInstance() != this) { - int index = findUserRouteRecord(route); - if (index >= 0) { - UserRouteRecord record = mUserRouteRecords.get(index); - selectRoute(record.mRouteObj); - } - } else { - int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId()); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - selectRoute(record.mRouteObj); - } - } - } - - protected void publishRoutes() { - MediaRouteProviderDescriptor.Builder builder = - new MediaRouteProviderDescriptor.Builder(); - int count = mSystemRouteRecords.size(); - for (int i = 0; i < count; i++) { - builder.addRoute(mSystemRouteRecords.get(i).mRouteDescriptor); - } - - setDescriptor(builder.build()); - } - - protected int findSystemRouteRecord(Object routeObj) { - final int count = mSystemRouteRecords.size(); - for (int i = 0; i < count; i++) { - if (mSystemRouteRecords.get(i).mRouteObj == routeObj) { - return i; - } - } - return -1; - } - - protected int findSystemRouteRecordByDescriptorId(String id) { - final int count = mSystemRouteRecords.size(); - for (int i = 0; i < count; i++) { - if (mSystemRouteRecords.get(i).mRouteDescriptorId.equals(id)) { - return i; - } - } - return -1; - } - - protected int findUserRouteRecord(MediaRouter.RouteInfo route) { - final int count = mUserRouteRecords.size(); - for (int i = 0; i < count; i++) { - if (mUserRouteRecords.get(i).mRoute == route) { - return i; - } - } - return -1; - } - - protected UserRouteRecord getUserRouteRecord(Object routeObj) { - Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj); - return tag instanceof UserRouteRecord ? (UserRouteRecord)tag : null; - } - - protected void updateSystemRouteDescriptor(SystemRouteRecord record) { - // We must always recreate the route descriptor when making any changes - // because they are intended to be immutable once published. - MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder( - record.mRouteDescriptorId, getRouteName(record.mRouteObj)); - onBuildSystemRouteDescriptor(record, builder); - record.mRouteDescriptor = builder.build(); - } - - protected String getRouteName(Object routeObj) { - // Routes should not have null names but it may happen for badly configured - // user routes. We tolerate this by using an empty name string here but - // such unnamed routes will be discarded by the media router upstream - // (with a log message so we can track down the problem). - CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext()); - return name != null ? name.toString() : ""; - } - - protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, - MediaRouteDescriptor.Builder builder) { - int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes( - record.mRouteObj); - if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) { - builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS); - } - if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) { - builder.addControlFilters(LIVE_VIDEO_CONTROL_FILTERS); - } - - builder.setPlaybackType( - MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj)); - builder.setPlaybackStream( - MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj)); - builder.setVolume( - MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj)); - builder.setVolumeMax( - MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj)); - builder.setVolumeHandling( - MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj)); - } - - protected void updateUserRouteProperties(UserRouteRecord record) { - MediaRouterJellybean.UserRouteInfo.setName( - record.mRouteObj, record.mRoute.getName()); - MediaRouterJellybean.UserRouteInfo.setPlaybackType( - record.mRouteObj, record.mRoute.getPlaybackType()); - MediaRouterJellybean.UserRouteInfo.setPlaybackStream( - record.mRouteObj, record.mRoute.getPlaybackStream()); - MediaRouterJellybean.UserRouteInfo.setVolume( - record.mRouteObj, record.mRoute.getVolume()); - MediaRouterJellybean.UserRouteInfo.setVolumeMax( - record.mRouteObj, record.mRoute.getVolumeMax()); - MediaRouterJellybean.UserRouteInfo.setVolumeHandling( - record.mRouteObj, record.mRoute.getVolumeHandling()); - } - - protected void updateCallback() { - if (mCallbackRegistered) { - mCallbackRegistered = false; - MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj); - } - - if (mRouteTypes != 0) { - mCallbackRegistered = true; - MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj); - } - } - - protected Object createCallbackObj() { - return MediaRouterJellybean.createCallback(this); - } - - protected Object createVolumeCallbackObj() { - return MediaRouterJellybean.createVolumeCallback(this); - } - - protected void selectRoute(Object routeObj) { - if (mSelectRouteWorkaround == null) { - mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround(); - } - mSelectRouteWorkaround.selectRoute(mRouterObj, - MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj); - } - - @Override - protected Object getDefaultRoute() { - if (mGetDefaultRouteWorkaround == null) { - mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround(); - } - return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj); - } - - @Override - protected Object getSystemRoute(MediaRouter.RouteInfo route) { - if (route == null) { - return null; - } - int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId()); - if (index >= 0) { - return mSystemRouteRecords.get(index).mRouteObj; - } - return null; - } - - /** - * Represents a route that is provided by the framework media router - * and published by this route provider to the support library media router. - */ - protected static final class SystemRouteRecord { - public final Object mRouteObj; - public final String mRouteDescriptorId; - public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation - - public SystemRouteRecord(Object routeObj, String id) { - mRouteObj = routeObj; - mRouteDescriptorId = id; - } - } - - /** - * Represents a route that is provided by the support library media router - * and published by this route provider to the framework media router. - */ - protected static final class UserRouteRecord { - public final MediaRouter.RouteInfo mRoute; - public final Object mRouteObj; - - public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) { - mRoute = route; - mRouteObj = routeObj; - } - } - - protected static final class SystemRouteController extends RouteController { - private final Object mRouteObj; - - public SystemRouteController(Object routeObj) { - mRouteObj = routeObj; - } - - @Override - public void onSetVolume(int volume) { - MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume); - } - - @Override - public void onUpdateVolume(int delta) { - MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta); - } - } - } - - /** - * Jellybean MR1 implementation. - */ - // @@RequiresApi(17) - private static class JellybeanMr1Impl extends JellybeanImpl - implements MediaRouterJellybeanMr1.Callback { - private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround; - private MediaRouterJellybeanMr1.IsConnectingWorkaround mIsConnectingWorkaround; - - public JellybeanMr1Impl(Context context, SyncCallback syncCallback) { - super(context, syncCallback); - } - - @Override - public void onRoutePresentationDisplayChanged(Object routeObj) { - int index = findSystemRouteRecord(routeObj); - if (index >= 0) { - SystemRouteRecord record = mSystemRouteRecords.get(index); - Display newPresentationDisplay = - MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj); - int newPresentationDisplayId = (newPresentationDisplay != null - ? newPresentationDisplay.getDisplayId() : -1); - if (newPresentationDisplayId - != record.mRouteDescriptor.getPresentationDisplayId()) { - record.mRouteDescriptor = - new MediaRouteDescriptor.Builder(record.mRouteDescriptor) - .setPresentationDisplayId(newPresentationDisplayId) - .build(); - publishRoutes(); - } - } - } - - @Override - protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, - MediaRouteDescriptor.Builder builder) { - super.onBuildSystemRouteDescriptor(record, builder); - - if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) { - builder.setEnabled(false); - } - - if (isConnecting(record)) { - builder.setConnecting(true); - } - - Display presentationDisplay = - MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj); - if (presentationDisplay != null) { - builder.setPresentationDisplayId(presentationDisplay.getDisplayId()); - } - } - - @Override - protected void updateCallback() { - super.updateCallback(); - - if (mActiveScanWorkaround == null) { - mActiveScanWorkaround = new MediaRouterJellybeanMr1.ActiveScanWorkaround( - getContext(), getHandler()); - } - mActiveScanWorkaround.setActiveScanRouteTypes(mActiveScan ? mRouteTypes : 0); - } - - @Override - protected Object createCallbackObj() { - return MediaRouterJellybeanMr1.createCallback(this); - } - - protected boolean isConnecting(SystemRouteRecord record) { - if (mIsConnectingWorkaround == null) { - mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround(); - } - return mIsConnectingWorkaround.isConnecting(record.mRouteObj); - } - } - - /** - * Jellybean MR2 implementation. - */ - // @@RequiresApi(18) - private static class JellybeanMr2Impl extends JellybeanMr1Impl { - public JellybeanMr2Impl(Context context, SyncCallback syncCallback) { - super(context, syncCallback); - } - - @Override - protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, - MediaRouteDescriptor.Builder builder) { - super.onBuildSystemRouteDescriptor(record, builder); - - CharSequence description = - MediaRouterJellybeanMr2.RouteInfo.getDescription(record.mRouteObj); - if (description != null) { - builder.setDescription(description.toString()); - } - } - - @Override - protected void selectRoute(Object routeObj) { - MediaRouterJellybean.selectRoute(mRouterObj, - MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj); - } - - @Override - protected Object getDefaultRoute() { - return MediaRouterJellybeanMr2.getDefaultRoute(mRouterObj); - } - - @Override - protected void updateUserRouteProperties(UserRouteRecord record) { - super.updateUserRouteProperties(record); - - MediaRouterJellybeanMr2.UserRouteInfo.setDescription( - record.mRouteObj, record.mRoute.getDescription()); - } - - @Override - protected void updateCallback() { - if (mCallbackRegistered) { - MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj); - } - - mCallbackRegistered = true; - MediaRouterJellybeanMr2.addCallback(mRouterObj, mRouteTypes, mCallbackObj, - MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS - | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0)); - } - - @Override - protected boolean isConnecting(SystemRouteRecord record) { - return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj); - } - } - - /** - * Api24 implementation. - */ - // @@RequiresApi(24) - private static class Api24Impl extends JellybeanMr2Impl { - public Api24Impl(Context context, SyncCallback syncCallback) { - super(context, syncCallback); - } - - @Override - protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, - MediaRouteDescriptor.Builder builder) { - super.onBuildSystemRouteDescriptor(record, builder); - - builder.setDeviceType(MediaRouterApi24.RouteInfo.getDeviceType(record.mRouteObj)); - } - } -} diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html b/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html deleted file mode 100644 index be2aaf266d..0000000000 --- a/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Contains APIs that control the routing of media channels and streams from the current device - to external speakers and destination devices.

    - - - diff --git a/packages/MediaComponents/src/com/android/widget/BaseLayout.java b/packages/MediaComponents/src/com/android/widget/BaseLayout.java deleted file mode 100644 index fb6471d6ff..0000000000 --- a/packages/MediaComponents/src/com/android/widget/BaseLayout.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.graphics.drawable.Drawable; -import android.media.update.ViewGroupProvider; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewGroup.MarginLayoutParams; - -import java.util.ArrayList; - -public class BaseLayout extends ViewGroupImpl { - private final ViewGroup mInstance; - private final ViewGroupProvider mSuperProvider; - private final ViewGroupProvider mPrivateProvider; - - private final ArrayList mMatchParentChildren = new ArrayList<>(1); - - public BaseLayout(ViewGroup instance, - ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { - super(instance, superProvider, privateProvider); - mInstance = instance; - mSuperProvider = superProvider; - mPrivateProvider = privateProvider; - } - - @Override - public boolean checkLayoutParams_impl(LayoutParams p) { - return p instanceof MarginLayoutParams; - } - - @Override - public LayoutParams generateDefaultLayoutParams_impl() { - return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - } - - @Override - public LayoutParams generateLayoutParams_impl(AttributeSet attrs) { - return new MarginLayoutParams(mInstance.getContext(), attrs); - } - - @Override - public LayoutParams generateLayoutParams_impl(LayoutParams lp) { - if (lp instanceof MarginLayoutParams) { - return lp; - } - return new MarginLayoutParams(lp); - } - - @Override - public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { - int count = mInstance.getChildCount(); - - final boolean measureMatchParentChildren = - View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY || - View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY; - mMatchParentChildren.clear(); - - int maxHeight = 0; - int maxWidth = 0; - int childState = 0; - - for (int i = 0; i < count; i++) { - final View child = mInstance.getChildAt(i); - if (child.getVisibility() != View.GONE) { - mPrivateProvider.measureChildWithMargins_impl( - child, widthMeasureSpec, 0, heightMeasureSpec, 0); - final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - maxWidth = Math.max(maxWidth, - child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); - maxHeight = Math.max(maxHeight, - child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); - childState = childState | child.getMeasuredState(); - if (measureMatchParentChildren) { - if (lp.width == LayoutParams.MATCH_PARENT || - lp.height == LayoutParams.MATCH_PARENT) { - mMatchParentChildren.add(child); - } - } - } - } - - // Account for padding too - maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); - maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); - - // Check against our minimum height and width - maxHeight = Math.max(maxHeight, mPrivateProvider.getSuggestedMinimumHeight_impl()); - maxWidth = Math.max(maxWidth, mPrivateProvider.getSuggestedMinimumWidth_impl()); - - // Check against our foreground's minimum height and width - final Drawable drawable = mInstance.getForeground(); - if (drawable != null) { - maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); - maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); - } - - mPrivateProvider.setMeasuredDimension_impl( - mInstance.resolveSizeAndState(maxWidth, widthMeasureSpec, childState), - mInstance.resolveSizeAndState(maxHeight, heightMeasureSpec, - childState << View.MEASURED_HEIGHT_STATE_SHIFT)); - - count = mMatchParentChildren.size(); - if (count > 1) { - for (int i = 0; i < count; i++) { - final View child = mMatchParentChildren.get(i); - final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - - final int childWidthMeasureSpec; - if (lp.width == LayoutParams.MATCH_PARENT) { - final int width = Math.max(0, mInstance.getMeasuredWidth() - - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - - lp.leftMargin - lp.rightMargin); - childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec( - width, View.MeasureSpec.EXACTLY); - } else { - childWidthMeasureSpec = mInstance.getChildMeasureSpec(widthMeasureSpec, - getPaddingLeftWithForeground() + getPaddingRightWithForeground() + - lp.leftMargin + lp.rightMargin, - lp.width); - } - - final int childHeightMeasureSpec; - if (lp.height == LayoutParams.MATCH_PARENT) { - final int height = Math.max(0, mInstance.getMeasuredHeight() - - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - - lp.topMargin - lp.bottomMargin); - childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec( - height, View.MeasureSpec.EXACTLY); - } else { - childHeightMeasureSpec = mInstance.getChildMeasureSpec(heightMeasureSpec, - getPaddingTopWithForeground() + getPaddingBottomWithForeground() + - lp.topMargin + lp.bottomMargin, - lp.height); - } - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - } - } - - @Override - public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { - final int count = mInstance.getChildCount(); - - final int parentLeft = getPaddingLeftWithForeground(); - final int parentRight = right - left - getPaddingRightWithForeground(); - - final int parentTop = getPaddingTopWithForeground(); - final int parentBottom = bottom - top - getPaddingBottomWithForeground(); - - for (int i = 0; i < count; i++) { - final View child = mInstance.getChildAt(i); - if (child.getVisibility() != View.GONE) { - final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - - final int width = child.getMeasuredWidth(); - final int height = child.getMeasuredHeight(); - - int childLeft; - int childTop; - - childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + - lp.leftMargin - lp.rightMargin; - - childTop = parentTop + (parentBottom - parentTop - height) / 2 + - lp.topMargin - lp.bottomMargin; - - child.layout(childLeft, childTop, childLeft + width, childTop + height); - } - } - } - - @Override - public boolean shouldDelayChildPressedState_impl() { - return false; - } - - private int getPaddingLeftWithForeground() { - return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingLeft(), 0) : - mInstance.getPaddingLeft() + 0; - } - - private int getPaddingRightWithForeground() { - return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingRight(), 0) : - mInstance.getPaddingRight() + 0; - } - - private int getPaddingTopWithForeground() { - return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingTop(), 0) : - mInstance.getPaddingTop() + 0; - } - - private int getPaddingBottomWithForeground() { - return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingBottom(), 0) : - mInstance.getPaddingBottom() + 0; - } -} diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java deleted file mode 100644 index ad85af434e..0000000000 --- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java +++ /dev/null @@ -1,1721 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.media.MediaMetadata; -import android.media.SessionToken2; -import android.media.session.MediaController; -import android.media.session.PlaybackState; -import android.media.update.MediaControlView2Provider; -import android.media.update.ViewGroupProvider; -import android.os.Bundle; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.MediaControlView2; -import android.widget.PopupWindow; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import androidx.annotation.Nullable; - -import com.android.media.update.ApiHelper; -import com.android.media.update.R; -import com.android.support.mediarouter.app.MediaRouteButton; -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Formatter; -import java.util.List; -import java.util.Locale; - -public class MediaControlView2Impl extends BaseLayout implements MediaControlView2Provider { - private static final String TAG = "MediaControlView2"; - - private final MediaControlView2 mInstance; - - static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen"; - - // TODO: Move these constants to public api to support custom video view. - // TODO: Combine these constants into one regarding TrackInfo. - static final String KEY_VIDEO_TRACK_COUNT = "VideoTrackCount"; - static final String KEY_AUDIO_TRACK_COUNT = "AudioTrackCount"; - static final String KEY_SUBTITLE_TRACK_COUNT = "SubtitleTrackCount"; - static final String KEY_PLAYBACK_SPEED = "PlaybackSpeed"; - static final String KEY_SELECTED_AUDIO_INDEX = "SelectedAudioIndex"; - static final String KEY_SELECTED_SUBTITLE_INDEX = "SelectedSubtitleIndex"; - static final String EVENT_UPDATE_TRACK_STATUS = "UpdateTrackStatus"; - - // TODO: Remove this once integrating with MediaSession2 & MediaMetadata2 - static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement"; - static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus"; - - // String for sending command to show subtitle to MediaSession. - static final String COMMAND_SHOW_SUBTITLE = "showSubtitle"; - // String for sending command to hide subtitle to MediaSession. - static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle"; - // TODO: remove once the implementation is revised - public static final String COMMAND_SET_FULLSCREEN = "setFullscreen"; - // String for sending command to select audio track to MediaSession. - static final String COMMAND_SELECT_AUDIO_TRACK = "SelectTrack"; - // String for sending command to set playback speed to MediaSession. - static final String COMMAND_SET_PLAYBACK_SPEED = "SetPlaybackSpeed"; - // String for sending command to mute audio to MediaSession. - static final String COMMAND_MUTE= "Mute"; - // String for sending command to unmute audio to MediaSession. - static final String COMMAND_UNMUTE = "Unmute"; - - private static final int SETTINGS_MODE_AUDIO_TRACK = 0; - private static final int SETTINGS_MODE_PLAYBACK_SPEED = 1; - private static final int SETTINGS_MODE_HELP = 2; - private static final int SETTINGS_MODE_SUBTITLE_TRACK = 3; - private static final int SETTINGS_MODE_VIDEO_QUALITY = 4; - private static final int SETTINGS_MODE_MAIN = 5; - private static final int PLAYBACK_SPEED_1x_INDEX = 3; - - private static final int MEDIA_TYPE_DEFAULT = 0; - private static final int MEDIA_TYPE_MUSIC = 1; - private static final int MEDIA_TYPE_ADVERTISEMENT = 2; - - private static final int SIZE_TYPE_EMBEDDED = 0; - private static final int SIZE_TYPE_FULL = 1; - // TODO: add support for Minimal size type. - private static final int SIZE_TYPE_MINIMAL = 2; - - private static final int MAX_PROGRESS = 1000; - private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000; - private static final int REWIND_TIME_MS = 10000; - private static final int FORWARD_TIME_MS = 30000; - private static final int AD_SKIP_WAIT_TIME_MS = 5000; - private static final int RESOURCE_NON_EXISTENT = -1; - private static final String RESOURCE_EMPTY = ""; - - private Resources mResources; - private MediaController mController; - private MediaController.TransportControls mControls; - private PlaybackState mPlaybackState; - private MediaMetadata mMetadata; - private int mDuration; - private int mPrevState; - private int mPrevWidth; - private int mPrevHeight; - private int mOriginalLeftBarWidth; - private int mVideoTrackCount; - private int mAudioTrackCount; - private int mSubtitleTrackCount; - private int mSettingsMode; - private int mSelectedSubtitleTrackIndex; - private int mSelectedAudioTrackIndex; - private int mSelectedVideoQualityIndex; - private int mSelectedSpeedIndex; - private int mEmbeddedSettingsItemWidth; - private int mFullSettingsItemWidth; - private int mEmbeddedSettingsItemHeight; - private int mFullSettingsItemHeight; - private int mSettingsWindowMargin; - private int mMediaType; - private int mSizeType; - private long mPlaybackActions; - private boolean mDragging; - private boolean mIsFullScreen; - private boolean mOverflowExpanded; - private boolean mIsStopped; - private boolean mSubtitleIsEnabled; - private boolean mSeekAvailable; - private boolean mIsAdvertisement; - private boolean mIsMute; - private boolean mNeedUXUpdate; - - // Relating to Title Bar View - private ViewGroup mRoot; - private View mTitleBar; - private TextView mTitleView; - private View mAdExternalLink; - private ImageButton mBackButton; - private MediaRouteButton mRouteButton; - private MediaRouteSelector mRouteSelector; - - // Relating to Center View - private ViewGroup mCenterView; - private View mTransportControls; - private ImageButton mPlayPauseButton; - private ImageButton mFfwdButton; - private ImageButton mRewButton; - private ImageButton mNextButton; - private ImageButton mPrevButton; - - // Relating to Minimal Extra View - private LinearLayout mMinimalExtraView; - - // Relating to Progress Bar View - private ProgressBar mProgress; - private View mProgressBuffer; - - // Relating to Bottom Bar View - private ViewGroup mBottomBar; - - // Relating to Bottom Bar Left View - private ViewGroup mBottomBarLeftView; - private ViewGroup mTimeView; - private TextView mEndTime; - private TextView mCurrentTime; - private TextView mAdSkipView; - private StringBuilder mFormatBuilder; - private Formatter mFormatter; - - // Relating to Bottom Bar Right View - private ViewGroup mBottomBarRightView; - private ViewGroup mBasicControls; - private ViewGroup mExtraControls; - private ViewGroup mCustomButtons; - private ImageButton mSubtitleButton; - private ImageButton mFullScreenButton; - private ImageButton mOverflowButtonRight; - private ImageButton mOverflowButtonLeft; - private ImageButton mMuteButton; - private ImageButton mVideoQualityButton; - private ImageButton mSettingsButton; - private TextView mAdRemainingView; - - // Relating to Settings List View - private ListView mSettingsListView; - private PopupWindow mSettingsWindow; - private SettingsAdapter mSettingsAdapter; - private SubSettingsAdapter mSubSettingsAdapter; - private List mSettingsMainTextsList; - private List mSettingsSubTextsList; - private List mSettingsIconIdsList; - private List mSubtitleDescriptionsList; - private List mAudioTrackList; - private List mVideoQualityList; - private List mPlaybackSpeedTextList; - private List mPlaybackSpeedList; - - public MediaControlView2Impl(MediaControlView2 instance, - ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { - super(instance, superProvider, privateProvider); - mInstance = instance; - } - - @Override - public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - mResources = ApiHelper.getLibResources(mInstance.getContext()); - // Inflate MediaControlView2 from XML - mRoot = makeControllerView(); - mInstance.addView(mRoot); - } - - @Override - public void setMediaSessionToken_impl(SessionToken2 token) { - // TODO: implement this - } - - @Override - public void setOnFullScreenListener_impl(MediaControlView2.OnFullScreenListener l) { - // TODO: implement this - } - - @Override - public void setController_impl(MediaController controller) { - mController = controller; - if (controller != null) { - mControls = controller.getTransportControls(); - // Set mMetadata and mPlaybackState to existing MediaSession variables since they may - // be called before the callback is called - mPlaybackState = mController.getPlaybackState(); - mMetadata = mController.getMetadata(); - updateDuration(); - updateTitle(); - - mController.registerCallback(new MediaControllerCallback()); - } - } - - @Override - public void setButtonVisibility_impl(int button, int visibility) { - // TODO: add member variables for Fast-Forward/Prvious/Rewind buttons to save visibility in - // order to prevent being overriden inside updateLayout(). - switch (button) { - case MediaControlView2.BUTTON_PLAY_PAUSE: - if (mPlayPauseButton != null && canPause()) { - mPlayPauseButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_FFWD: - if (mFfwdButton != null && canSeekForward()) { - mFfwdButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_REW: - if (mRewButton != null && canSeekBackward()) { - mRewButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_NEXT: - if (mNextButton != null) { - mNextButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_PREV: - if (mPrevButton != null) { - mPrevButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_SUBTITLE: - if (mSubtitleButton != null && mSubtitleTrackCount > 0) { - mSubtitleButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_FULL_SCREEN: - if (mFullScreenButton != null) { - mFullScreenButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_OVERFLOW: - if (mOverflowButtonRight != null) { - mOverflowButtonRight.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_MUTE: - if (mMuteButton != null) { - mMuteButton.setVisibility(visibility); - } - break; - case MediaControlView2.BUTTON_SETTINGS: - if (mSettingsButton != null) { - mSettingsButton.setVisibility(visibility); - } - break; - default: - break; - } - } - - @Override - public void requestPlayButtonFocus_impl() { - if (mPlayPauseButton != null) { - mPlayPauseButton.requestFocus(); - } - } - - @Override - public CharSequence getAccessibilityClassName_impl() { - return MediaControlView2.class.getName(); - } - - @Override - public boolean onTouchEvent_impl(MotionEvent ev) { - return false; - } - - // TODO: Should this function be removed? - @Override - public boolean onTrackballEvent_impl(MotionEvent ev) { - return false; - } - - @Override - public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec); - - // Update layout when this view's width changes in order to avoid any UI overlap between - // transport controls. - if (mPrevWidth != mInstance.getMeasuredWidth() - || mPrevHeight != mInstance.getMeasuredHeight() || mNeedUXUpdate) { - // Dismiss SettingsWindow if it is showing. - mSettingsWindow.dismiss(); - - // These views may not have been initialized yet. - if (mTransportControls.getWidth() == 0 || mTimeView.getWidth() == 0) { - return; - } - - int currWidth = mInstance.getMeasuredWidth(); - int currHeight = mInstance.getMeasuredHeight(); - WindowManager manager = (WindowManager) mInstance.getContext().getApplicationContext() - .getSystemService(Context.WINDOW_SERVICE); - Point screenSize = new Point(); - manager.getDefaultDisplay().getSize(screenSize); - int screenWidth = screenSize.x; - int screenHeight = screenSize.y; - int fullIconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_full_icon_size); - int embeddedIconSize = mResources.getDimensionPixelSize( - R.dimen.mcv2_embedded_icon_size); - int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin); - - // TODO: add support for Advertisement Mode. - if (mMediaType == MEDIA_TYPE_DEFAULT) { - // Max number of icons inside BottomBarRightView for Music mode is 4. - int maxIconCount = 4; - updateLayout(maxIconCount, fullIconSize, embeddedIconSize, marginSize, currWidth, - currHeight, screenWidth, screenHeight); - } else if (mMediaType == MEDIA_TYPE_MUSIC) { - if (mNeedUXUpdate) { - // One-time operation for Music media type - mBasicControls.removeView(mMuteButton); - mExtraControls.addView(mMuteButton, 0); - mVideoQualityButton.setVisibility(View.GONE); - if (mFfwdButton != null) { - mFfwdButton.setVisibility(View.GONE); - } - if (mRewButton != null) { - mRewButton.setVisibility(View.GONE); - } - } - mNeedUXUpdate = false; - - // Max number of icons inside BottomBarRightView for Music mode is 3. - int maxIconCount = 3; - updateLayout(maxIconCount, fullIconSize, embeddedIconSize, marginSize, currWidth, - currHeight, screenWidth, screenHeight); - } - mPrevWidth = currWidth; - mPrevHeight = currHeight; - } - // TODO: move this to a different location. - // Update title bar parameters in order to avoid overlap between title view and the right - // side of the title bar. - updateTitleBarLayout(); - } - - @Override - public void setEnabled_impl(boolean enabled) { - super.setEnabled_impl(enabled); - - // TODO: Merge the below code with disableUnsupportedButtons(). - if (mPlayPauseButton != null) { - mPlayPauseButton.setEnabled(enabled); - } - if (mFfwdButton != null) { - mFfwdButton.setEnabled(enabled); - } - if (mRewButton != null) { - mRewButton.setEnabled(enabled); - } - if (mNextButton != null) { - mNextButton.setEnabled(enabled); - } - if (mPrevButton != null) { - mPrevButton.setEnabled(enabled); - } - if (mProgress != null) { - mProgress.setEnabled(enabled); - } - disableUnsupportedButtons(); - } - - @Override - public void onVisibilityAggregated_impl(boolean isVisible) { - super.onVisibilityAggregated_impl(isVisible); - - if (isVisible) { - disableUnsupportedButtons(); - mInstance.removeCallbacks(mUpdateProgress); - mInstance.post(mUpdateProgress); - } else { - mInstance.removeCallbacks(mUpdateProgress); - } - } - - public void setRouteSelector(MediaRouteSelector selector) { - mRouteSelector = selector; - if (mRouteSelector != null && !mRouteSelector.isEmpty()) { - mRouteButton.setRouteSelector(selector, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - mRouteButton.setVisibility(View.VISIBLE); - } else { - mRouteButton.setRouteSelector(MediaRouteSelector.EMPTY); - mRouteButton.setVisibility(View.GONE); - } - } - - /////////////////////////////////////////////////// - // Protected or private methods - /////////////////////////////////////////////////// - - private boolean isPlaying() { - if (mPlaybackState != null) { - return mPlaybackState.getState() == PlaybackState.STATE_PLAYING; - } - return false; - } - - private int getCurrentPosition() { - mPlaybackState = mController.getPlaybackState(); - if (mPlaybackState != null) { - return (int) mPlaybackState.getPosition(); - } - return 0; - } - - private int getBufferPercentage() { - if (mDuration == 0) { - return 0; - } - mPlaybackState = mController.getPlaybackState(); - if (mPlaybackState != null) { - long bufferedPos = mPlaybackState.getBufferedPosition(); - return (bufferedPos == -1) ? -1 : (int) (bufferedPos * 100 / mDuration); - } - return 0; - } - - private boolean canPause() { - if (mPlaybackState != null) { - return (mPlaybackState.getActions() & PlaybackState.ACTION_PAUSE) != 0; - } - return true; - } - - private boolean canSeekBackward() { - if (mPlaybackState != null) { - return (mPlaybackState.getActions() & PlaybackState.ACTION_REWIND) != 0; - } - return true; - } - - private boolean canSeekForward() { - if (mPlaybackState != null) { - return (mPlaybackState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0; - } - return true; - } - - /** - * Create the view that holds the widgets that control playback. - * Derived classes can override this to create their own. - * - * @return The controller view. - * @hide This doesn't work as advertised - */ - protected ViewGroup makeControllerView() { - ViewGroup root = (ViewGroup) ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.media_controller); - initControllerView(root); - return root; - } - - private void initControllerView(ViewGroup v) { - // Relating to Title Bar View - mTitleBar = v.findViewById(R.id.title_bar); - mTitleView = v.findViewById(R.id.title_text); - mAdExternalLink = v.findViewById(R.id.ad_external_link); - mBackButton = v.findViewById(R.id.back); - if (mBackButton != null) { - mBackButton.setOnClickListener(mBackListener); - mBackButton.setVisibility(View.GONE); - } - mRouteButton = v.findViewById(R.id.cast); - - // Relating to Center View - mCenterView = v.findViewById(R.id.center_view); - mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls); - mCenterView.addView(mTransportControls); - - // Relating to Minimal Extra View - mMinimalExtraView = (LinearLayout) v.findViewById(R.id.minimal_extra_view); - LinearLayout.LayoutParams params = - (LinearLayout.LayoutParams) mMinimalExtraView.getLayoutParams(); - int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_embedded_icon_size); - int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin); - params.setMargins(0, (iconSize + marginSize * 2) * (-1), 0, 0); - mMinimalExtraView.setLayoutParams(params); - mMinimalExtraView.setVisibility(View.GONE); - - // Relating to Progress Bar View - mProgress = v.findViewById(R.id.progress); - if (mProgress != null) { - if (mProgress instanceof SeekBar) { - SeekBar seeker = (SeekBar) mProgress; - seeker.setOnSeekBarChangeListener(mSeekListener); - seeker.setProgressDrawable(mResources.getDrawable(R.drawable.custom_progress)); - seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb)); - } - mProgress.setMax(MAX_PROGRESS); - } - mProgressBuffer = v.findViewById(R.id.progress_buffer); - - // Relating to Bottom Bar View - mBottomBar = v.findViewById(R.id.bottom_bar); - - // Relating to Bottom Bar Left View - mBottomBarLeftView = v.findViewById(R.id.bottom_bar_left); - mTimeView = v.findViewById(R.id.time); - mEndTime = v.findViewById(R.id.time_end); - mCurrentTime = v.findViewById(R.id.time_current); - mAdSkipView = v.findViewById(R.id.ad_skip_time); - mFormatBuilder = new StringBuilder(); - mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); - - // Relating to Bottom Bar Right View - mBottomBarRightView = v.findViewById(R.id.bottom_bar_right); - mBasicControls = v.findViewById(R.id.basic_controls); - mExtraControls = v.findViewById(R.id.extra_controls); - mCustomButtons = v.findViewById(R.id.custom_buttons); - mSubtitleButton = v.findViewById(R.id.subtitle); - if (mSubtitleButton != null) { - mSubtitleButton.setOnClickListener(mSubtitleListener); - } - mFullScreenButton = v.findViewById(R.id.fullscreen); - if (mFullScreenButton != null) { - mFullScreenButton.setOnClickListener(mFullScreenListener); - // TODO: Show Fullscreen button when only it is possible. - } - mOverflowButtonRight = v.findViewById(R.id.overflow_right); - if (mOverflowButtonRight != null) { - mOverflowButtonRight.setOnClickListener(mOverflowRightListener); - } - mOverflowButtonLeft = v.findViewById(R.id.overflow_left); - if (mOverflowButtonLeft != null) { - mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener); - } - mMuteButton = v.findViewById(R.id.mute); - if (mMuteButton != null) { - mMuteButton.setOnClickListener(mMuteButtonListener); - } - mSettingsButton = v.findViewById(R.id.settings); - if (mSettingsButton != null) { - mSettingsButton.setOnClickListener(mSettingsButtonListener); - } - mVideoQualityButton = v.findViewById(R.id.video_quality); - if (mVideoQualityButton != null) { - mVideoQualityButton.setOnClickListener(mVideoQualityListener); - } - mAdRemainingView = v.findViewById(R.id.ad_remaining); - - // Relating to Settings List View - initializeSettingsLists(); - mSettingsListView = (ListView) ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.settings_list); - mSettingsAdapter = new SettingsAdapter(mSettingsMainTextsList, mSettingsSubTextsList, - mSettingsIconIdsList); - mSubSettingsAdapter = new SubSettingsAdapter(null, 0); - mSettingsListView.setAdapter(mSettingsAdapter); - mSettingsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - mSettingsListView.setOnItemClickListener(mSettingsItemClickListener); - - mEmbeddedSettingsItemWidth = mResources.getDimensionPixelSize( - R.dimen.mcv2_embedded_settings_width); - mFullSettingsItemWidth = mResources.getDimensionPixelSize(R.dimen.mcv2_full_settings_width); - mEmbeddedSettingsItemHeight = mResources.getDimensionPixelSize( - R.dimen.mcv2_embedded_settings_height); - mFullSettingsItemHeight = mResources.getDimensionPixelSize( - R.dimen.mcv2_full_settings_height); - mSettingsWindowMargin = (-1) * mResources.getDimensionPixelSize( - R.dimen.mcv2_settings_offset); - mSettingsWindow = new PopupWindow(mSettingsListView, mEmbeddedSettingsItemWidth, - ViewGroup.LayoutParams.WRAP_CONTENT, true); - } - - /** - * Disable pause or seek buttons if the stream cannot be paused or seeked. - * This requires the control interface to be a MediaPlayerControlExt - */ - private void disableUnsupportedButtons() { - try { - if (mPlayPauseButton != null && !canPause()) { - mPlayPauseButton.setEnabled(false); - } - if (mRewButton != null && !canSeekBackward()) { - mRewButton.setEnabled(false); - } - if (mFfwdButton != null && !canSeekForward()) { - mFfwdButton.setEnabled(false); - } - // TODO What we really should do is add a canSeek to the MediaPlayerControl interface; - // this scheme can break the case when applications want to allow seek through the - // progress bar but disable forward/backward buttons. - // - // However, currently the flags SEEK_BACKWARD_AVAILABLE, SEEK_FORWARD_AVAILABLE, - // and SEEK_AVAILABLE are all (un)set together; as such the aforementioned issue - // shouldn't arise in existing applications. - if (mProgress != null && !canSeekBackward() && !canSeekForward()) { - mProgress.setEnabled(false); - } - } catch (IncompatibleClassChangeError ex) { - // We were given an old version of the interface, that doesn't have - // the canPause/canSeekXYZ methods. This is OK, it just means we - // assume the media can be paused and seeked, and so we don't disable - // the buttons. - } - } - - private final Runnable mUpdateProgress = new Runnable() { - @Override - public void run() { - int pos = setProgress(); - boolean isShowing = mInstance.getVisibility() == View.VISIBLE; - if (!mDragging && isShowing && isPlaying()) { - mInstance.postDelayed(mUpdateProgress, - DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS)); - } - } - }; - - private String stringForTime(int timeMs) { - int totalSeconds = timeMs / 1000; - - int seconds = totalSeconds % 60; - int minutes = (totalSeconds / 60) % 60; - int hours = totalSeconds / 3600; - - mFormatBuilder.setLength(0); - if (hours > 0) { - return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); - } else { - return mFormatter.format("%02d:%02d", minutes, seconds).toString(); - } - } - - private int setProgress() { - if (mController == null || mDragging) { - return 0; - } - int positionOnProgressBar = 0; - int currentPosition = getCurrentPosition(); - if (mDuration > 0) { - positionOnProgressBar = (int) (MAX_PROGRESS * (long) currentPosition / mDuration); - } - if (mProgress != null && currentPosition != mDuration) { - mProgress.setProgress(positionOnProgressBar); - // If the media is a local file, there is no need to set a buffer, so set secondary - // progress to maximum. - if (getBufferPercentage() < 0) { - mProgress.setSecondaryProgress(MAX_PROGRESS); - } else { - mProgress.setSecondaryProgress(getBufferPercentage() * 10); - } - } - - if (mEndTime != null) { - mEndTime.setText(stringForTime(mDuration)); - - } - if (mCurrentTime != null) { - mCurrentTime.setText(stringForTime(currentPosition)); - } - - if (mIsAdvertisement) { - // Update the remaining number of seconds until the first 5 seconds of the - // advertisement. - if (mAdSkipView != null) { - if (currentPosition <= AD_SKIP_WAIT_TIME_MS) { - if (mAdSkipView.getVisibility() == View.GONE) { - mAdSkipView.setVisibility(View.VISIBLE); - } - String skipTimeText = mResources.getString( - R.string.MediaControlView2_ad_skip_wait_time, - ((AD_SKIP_WAIT_TIME_MS - currentPosition) / 1000 + 1)); - mAdSkipView.setText(skipTimeText); - } else { - if (mAdSkipView.getVisibility() == View.VISIBLE) { - mAdSkipView.setVisibility(View.GONE); - mNextButton.setEnabled(true); - mNextButton.clearColorFilter(); - } - } - } - // Update the remaining number of seconds of the advertisement. - if (mAdRemainingView != null) { - int remainingTime = - (mDuration - currentPosition < 0) ? 0 : (mDuration - currentPosition); - String remainingTimeText = mResources.getString( - R.string.MediaControlView2_ad_remaining_time, - stringForTime(remainingTime)); - mAdRemainingView.setText(remainingTimeText); - } - } - return currentPosition; - } - - private void togglePausePlayState() { - if (isPlaying()) { - mControls.pause(); - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_play_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_play_button_desc)); - } else { - mControls.play(); - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_pause_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_pause_button_desc)); - } - } - - // There are two scenarios that can trigger the seekbar listener to trigger: - // - // The first is the user using the touchpad to adjust the posititon of the - // seekbar's thumb. In this case onStartTrackingTouch is called followed by - // a number of onProgressChanged notifications, concluded by onStopTrackingTouch. - // We're setting the field "mDragging" to true for the duration of the dragging - // session to avoid jumps in the position in case of ongoing playback. - // - // The second scenario involves the user operating the scroll ball, in this - // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications, - // we will simply apply the updated position without suspending regular updates. - private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { - @Override - public void onStartTrackingTouch(SeekBar bar) { - if (!mSeekAvailable) { - return; - } - - mDragging = true; - - // By removing these pending progress messages we make sure - // that a) we won't update the progress while the user adjusts - // the seekbar and b) once the user is done dragging the thumb - // we will post one of these messages to the queue again and - // this ensures that there will be exactly one message queued up. - mInstance.removeCallbacks(mUpdateProgress); - - // Check if playback is currently stopped. In this case, update the pause button to - // show the play image instead of the replay image. - if (mIsStopped) { - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_play_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_play_button_desc)); - mIsStopped = false; - } - } - - @Override - public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) { - if (!mSeekAvailable) { - return; - } - if (!fromUser) { - // We're not interested in programmatically generated changes to - // the progress bar's position. - return; - } - if (mDuration > 0) { - int position = (int) (((long) mDuration * progress) / MAX_PROGRESS); - mControls.seekTo(position); - - if (mCurrentTime != null) { - mCurrentTime.setText(stringForTime(position)); - } - } - } - - @Override - public void onStopTrackingTouch(SeekBar bar) { - if (!mSeekAvailable) { - return; - } - mDragging = false; - - setProgress(); - - // Ensure that progress is properly updated in the future, - // the call to show() does not guarantee this because it is a - // no-op if we are already showing. - mInstance.post(mUpdateProgress); - } - }; - - private final View.OnClickListener mPlayPauseListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - togglePausePlayState(); - } - }; - - private final View.OnClickListener mRewListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - int pos = getCurrentPosition() - REWIND_TIME_MS; - mControls.seekTo(pos); - setProgress(); - } - }; - - private final View.OnClickListener mFfwdListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - int pos = getCurrentPosition() + FORWARD_TIME_MS; - mControls.seekTo(pos); - setProgress(); - } - }; - - private final View.OnClickListener mNextListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mControls.skipToNext(); - } - }; - - private final View.OnClickListener mPrevListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mControls.skipToPrevious(); - } - }; - - private final View.OnClickListener mBackListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - // TODO: implement - } - }; - - private final View.OnClickListener mSubtitleListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mSettingsMode = SETTINGS_MODE_SUBTITLE_TRACK; - mSubSettingsAdapter.setTexts(mSubtitleDescriptionsList); - mSubSettingsAdapter.setCheckPosition(mSelectedSubtitleTrackIndex); - displaySettingsWindow(mSubSettingsAdapter); - } - }; - - private final View.OnClickListener mVideoQualityListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mSettingsMode = SETTINGS_MODE_VIDEO_QUALITY; - mSubSettingsAdapter.setTexts(mVideoQualityList); - mSubSettingsAdapter.setCheckPosition(mSelectedVideoQualityIndex); - displaySettingsWindow(mSubSettingsAdapter); - } - }; - - private final View.OnClickListener mFullScreenListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - final boolean isEnteringFullScreen = !mIsFullScreen; - // TODO: Re-arrange the button layouts according to the UX. - if (isEnteringFullScreen) { - mFullScreenButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_fullscreen_exit, null)); - } else { - mFullScreenButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_fullscreen, null)); - } - Bundle args = new Bundle(); - args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen); - mController.sendCommand(COMMAND_SET_FULLSCREEN, args, null); - - mIsFullScreen = isEnteringFullScreen; - } - }; - - private final View.OnClickListener mOverflowRightListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mBasicControls.setVisibility(View.GONE); - mExtraControls.setVisibility(View.VISIBLE); - } - }; - - private final View.OnClickListener mOverflowLeftListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mBasicControls.setVisibility(View.VISIBLE); - mExtraControls.setVisibility(View.GONE); - } - }; - - private final View.OnClickListener mMuteButtonListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mIsMute) { - mMuteButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_mute, null)); - mMuteButton.setContentDescription( - mResources.getString(R.string.mcv2_muted_button_desc)); - mIsMute = true; - mController.sendCommand(COMMAND_MUTE, null, null); - } else { - mMuteButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_unmute, null)); - mMuteButton.setContentDescription( - mResources.getString(R.string.mcv2_unmuted_button_desc)); - mIsMute = false; - mController.sendCommand(COMMAND_UNMUTE, null, null); - } - } - }; - - private final View.OnClickListener mSettingsButtonListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - mSettingsMode = SETTINGS_MODE_MAIN; - mSettingsAdapter.setSubTexts(mSettingsSubTextsList); - displaySettingsWindow(mSettingsAdapter); - } - }; - - private final AdapterView.OnItemClickListener mSettingsItemClickListener - = new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - switch (mSettingsMode) { - case SETTINGS_MODE_MAIN: - if (position == SETTINGS_MODE_AUDIO_TRACK) { - mSubSettingsAdapter.setTexts(mAudioTrackList); - mSubSettingsAdapter.setCheckPosition(mSelectedAudioTrackIndex); - mSettingsMode = SETTINGS_MODE_AUDIO_TRACK; - } else if (position == SETTINGS_MODE_PLAYBACK_SPEED) { - mSubSettingsAdapter.setTexts(mPlaybackSpeedTextList); - mSubSettingsAdapter.setCheckPosition(mSelectedSpeedIndex); - mSettingsMode = SETTINGS_MODE_PLAYBACK_SPEED; - } else if (position == SETTINGS_MODE_HELP) { - // TODO: implement this. - mSettingsWindow.dismiss(); - return; - } - displaySettingsWindow(mSubSettingsAdapter); - break; - case SETTINGS_MODE_AUDIO_TRACK: - if (position != mSelectedAudioTrackIndex) { - mSelectedAudioTrackIndex = position; - if (mAudioTrackCount > 0) { - Bundle extra = new Bundle(); - extra.putInt(KEY_SELECTED_AUDIO_INDEX, position); - mController.sendCommand(COMMAND_SELECT_AUDIO_TRACK, extra, null); - } - mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK, - mSubSettingsAdapter.getMainText(position)); - } - mSettingsWindow.dismiss(); - break; - case SETTINGS_MODE_PLAYBACK_SPEED: - if (position != mSelectedSpeedIndex) { - mSelectedSpeedIndex = position; - Bundle extra = new Bundle(); - extra.putFloat(KEY_PLAYBACK_SPEED, mPlaybackSpeedList.get(position)); - mController.sendCommand(COMMAND_SET_PLAYBACK_SPEED, extra, null); - mSettingsSubTextsList.set(SETTINGS_MODE_PLAYBACK_SPEED, - mSubSettingsAdapter.getMainText(position)); - } - mSettingsWindow.dismiss(); - break; - case SETTINGS_MODE_HELP: - // TODO: implement this. - break; - case SETTINGS_MODE_SUBTITLE_TRACK: - if (position != mSelectedSubtitleTrackIndex) { - mSelectedSubtitleTrackIndex = position; - if (position > 0) { - Bundle extra = new Bundle(); - extra.putInt(KEY_SELECTED_SUBTITLE_INDEX, position - 1); - mController.sendCommand( - MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, extra, null); - mSubtitleButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_subtitle_on, null)); - mSubtitleButton.setContentDescription( - mResources.getString(R.string.mcv2_cc_is_on)); - mSubtitleIsEnabled = true; - } else { - mController.sendCommand( - MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, null); - mSubtitleButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_subtitle_off, null)); - mSubtitleButton.setContentDescription( - mResources.getString(R.string.mcv2_cc_is_off)); - - mSubtitleIsEnabled = false; - } - } - mSettingsWindow.dismiss(); - break; - case SETTINGS_MODE_VIDEO_QUALITY: - // TODO: add support for video quality - mSelectedVideoQualityIndex = position; - mSettingsWindow.dismiss(); - break; - } - } - }; - - private void updateDuration() { - if (mMetadata != null) { - if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { - mDuration = (int) mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); - // update progress bar - setProgress(); - } - } - } - - private void updateTitle() { - if (mMetadata != null) { - if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { - mTitleView.setText(mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE)); - } - } - } - - // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are - // greater than the length of the title bar, reduce the size of the left bar (which makes the - // TextView that contains the title of the media file shrink). - private void updateTitleBarLayout() { - if (mTitleBar != null) { - int titleBarWidth = mTitleBar.getWidth(); - - View leftBar = mTitleBar.findViewById(R.id.title_bar_left); - View rightBar = mTitleBar.findViewById(R.id.title_bar_right); - int leftBarWidth = leftBar.getWidth(); - int rightBarWidth = rightBar.getWidth(); - - RelativeLayout.LayoutParams params = - (RelativeLayout.LayoutParams) leftBar.getLayoutParams(); - if (leftBarWidth + rightBarWidth > titleBarWidth) { - params.width = titleBarWidth - rightBarWidth; - mOriginalLeftBarWidth = leftBarWidth; - } else if (leftBarWidth + rightBarWidth < titleBarWidth && mOriginalLeftBarWidth != 0) { - params.width = mOriginalLeftBarWidth; - mOriginalLeftBarWidth = 0; - } - leftBar.setLayoutParams(params); - } - } - - private void updateAudioMetadata() { - if (mMediaType != MEDIA_TYPE_MUSIC) { - return; - } - - if (mMetadata != null) { - String titleText = ""; - String artistText = ""; - if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { - titleText = mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); - } else { - titleText = mResources.getString(R.string.mcv2_music_title_unknown_text); - } - - if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) { - artistText = mMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); - } else { - artistText = mResources.getString(R.string.mcv2_music_artist_unknown_text); - } - - // Update title for Embedded size type - mTitleView.setText(titleText + " - " + artistText); - - // Set to true to update layout inside onMeasure() - mNeedUXUpdate = true; - } - } - - private void updateLayout() { - if (mIsAdvertisement) { - mRewButton.setVisibility(View.GONE); - mFfwdButton.setVisibility(View.GONE); - mPrevButton.setVisibility(View.GONE); - mTimeView.setVisibility(View.GONE); - - mAdSkipView.setVisibility(View.VISIBLE); - mAdRemainingView.setVisibility(View.VISIBLE); - mAdExternalLink.setVisibility(View.VISIBLE); - - mProgress.setEnabled(false); - mNextButton.setEnabled(false); - mNextButton.setColorFilter(R.color.gray); - } else { - mRewButton.setVisibility(View.VISIBLE); - mFfwdButton.setVisibility(View.VISIBLE); - mPrevButton.setVisibility(View.VISIBLE); - mTimeView.setVisibility(View.VISIBLE); - - mAdSkipView.setVisibility(View.GONE); - mAdRemainingView.setVisibility(View.GONE); - mAdExternalLink.setVisibility(View.GONE); - - mProgress.setEnabled(true); - mNextButton.setEnabled(true); - mNextButton.clearColorFilter(); - disableUnsupportedButtons(); - } - } - - private void updateLayout(int maxIconCount, int fullIconSize, int embeddedIconSize, - int marginSize, int currWidth, int currHeight, int screenWidth, int screenHeight) { - int fullBottomBarRightWidthMax = fullIconSize * maxIconCount - + marginSize * (maxIconCount * 2); - int embeddedBottomBarRightWidthMax = embeddedIconSize * maxIconCount - + marginSize * (maxIconCount * 2); - int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth() - + fullBottomBarRightWidthMax; - int embeddedWidth = mTimeView.getWidth() + embeddedBottomBarRightWidthMax; - int screenMaxLength = Math.max(screenWidth, screenHeight); - - if (fullWidth > screenMaxLength) { - // TODO: screen may be smaller than the length needed for Full size. - } - - boolean isFullSize = (mMediaType == MEDIA_TYPE_DEFAULT) ? (currWidth == screenMaxLength) : - (currWidth == screenWidth && currHeight == screenHeight); - - if (isFullSize) { - if (mSizeType != SIZE_TYPE_FULL) { - updateLayoutForSizeChange(SIZE_TYPE_FULL); - if (mMediaType == MEDIA_TYPE_MUSIC) { - mTitleView.setVisibility(View.GONE); - } - } - } else if (embeddedWidth <= currWidth) { - if (mSizeType != SIZE_TYPE_EMBEDDED) { - updateLayoutForSizeChange(SIZE_TYPE_EMBEDDED); - if (mMediaType == MEDIA_TYPE_MUSIC) { - mTitleView.setVisibility(View.VISIBLE); - } - } - } else { - if (mSizeType != SIZE_TYPE_MINIMAL) { - updateLayoutForSizeChange(SIZE_TYPE_MINIMAL); - if (mMediaType == MEDIA_TYPE_MUSIC) { - mTitleView.setVisibility(View.GONE); - } - } - } - } - - private void updateLayoutForSizeChange(int sizeType) { - mSizeType = sizeType; - RelativeLayout.LayoutParams timeViewParams = - (RelativeLayout.LayoutParams) mTimeView.getLayoutParams(); - SeekBar seeker = (SeekBar) mProgress; - switch (mSizeType) { - case SIZE_TYPE_EMBEDDED: - // Relating to Title Bar - mTitleBar.setVisibility(View.VISIBLE); - mBackButton.setVisibility(View.GONE); - - // Relating to Full Screen Button - mMinimalExtraView.setVisibility(View.GONE); - mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen); - mFullScreenButton.setOnClickListener(mFullScreenListener); - - // Relating to Center View - mCenterView.removeAllViews(); - mBottomBarLeftView.removeView(mTransportControls); - mBottomBarLeftView.setVisibility(View.GONE); - mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls); - mCenterView.addView(mTransportControls); - - // Relating to Progress Bar - seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb)); - mProgressBuffer.setVisibility(View.VISIBLE); - - // Relating to Bottom Bar - mBottomBar.setVisibility(View.VISIBLE); - if (timeViewParams.getRule(RelativeLayout.LEFT_OF) != 0) { - timeViewParams.removeRule(RelativeLayout.LEFT_OF); - timeViewParams.addRule(RelativeLayout.RIGHT_OF, R.id.bottom_bar_left); - } - break; - case SIZE_TYPE_FULL: - // Relating to Title Bar - mTitleBar.setVisibility(View.VISIBLE); - mBackButton.setVisibility(View.VISIBLE); - - // Relating to Full Screen Button - mMinimalExtraView.setVisibility(View.GONE); - mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen); - mFullScreenButton.setOnClickListener(mFullScreenListener); - - // Relating to Center View - mCenterView.removeAllViews(); - mBottomBarLeftView.removeView(mTransportControls); - mTransportControls = inflateTransportControls(R.layout.full_transport_controls); - mBottomBarLeftView.addView(mTransportControls, 0); - mBottomBarLeftView.setVisibility(View.VISIBLE); - - // Relating to Progress Bar - seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb)); - mProgressBuffer.setVisibility(View.VISIBLE); - - // Relating to Bottom Bar - mBottomBar.setVisibility(View.VISIBLE); - if (timeViewParams.getRule(RelativeLayout.RIGHT_OF) != 0) { - timeViewParams.removeRule(RelativeLayout.RIGHT_OF); - timeViewParams.addRule(RelativeLayout.LEFT_OF, R.id.bottom_bar_right); - } - break; - case SIZE_TYPE_MINIMAL: - // Relating to Title Bar - mTitleBar.setVisibility(View.GONE); - mBackButton.setVisibility(View.GONE); - - // Relating to Full Screen Button - mMinimalExtraView.setVisibility(View.VISIBLE); - mFullScreenButton = mMinimalExtraView.findViewById(R.id.fullscreen); - mFullScreenButton.setOnClickListener(mFullScreenListener); - - // Relating to Center View - mCenterView.removeAllViews(); - mBottomBarLeftView.removeView(mTransportControls); - mTransportControls = inflateTransportControls(R.layout.minimal_transport_controls); - mCenterView.addView(mTransportControls); - - // Relating to Progress Bar - seeker.setThumb(null); - mProgressBuffer.setVisibility(View.GONE); - - // Relating to Bottom Bar - mBottomBar.setVisibility(View.GONE); - break; - } - mTimeView.setLayoutParams(timeViewParams); - - if (isPlaying()) { - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_pause_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_pause_button_desc)); - } else { - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_play_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_play_button_desc)); - } - - if (mIsFullScreen) { - mFullScreenButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_fullscreen_exit, null)); - } else { - mFullScreenButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_fullscreen, null)); - } - } - - private View inflateTransportControls(int layoutId) { - View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId); - mPlayPauseButton = v.findViewById(R.id.pause); - if (mPlayPauseButton != null) { - mPlayPauseButton.requestFocus(); - mPlayPauseButton.setOnClickListener(mPlayPauseListener); - } - mFfwdButton = v.findViewById(R.id.ffwd); - if (mFfwdButton != null) { - mFfwdButton.setOnClickListener(mFfwdListener); - if (mMediaType == MEDIA_TYPE_MUSIC) { - mFfwdButton.setVisibility(View.GONE); - } - } - mRewButton = v.findViewById(R.id.rew); - if (mRewButton != null) { - mRewButton.setOnClickListener(mRewListener); - if (mMediaType == MEDIA_TYPE_MUSIC) { - mRewButton.setVisibility(View.GONE); - } - } - // TODO: Add support for Next and Previous buttons - mNextButton = v.findViewById(R.id.next); - if (mNextButton != null) { - mNextButton.setOnClickListener(mNextListener); - mNextButton.setVisibility(View.GONE); - } - mPrevButton = v.findViewById(R.id.prev); - if (mPrevButton != null) { - mPrevButton.setOnClickListener(mPrevListener); - mPrevButton.setVisibility(View.GONE); - } - return v; - } - - private void initializeSettingsLists() { - mSettingsMainTextsList = new ArrayList(); - mSettingsMainTextsList.add( - mResources.getString(R.string.MediaControlView2_audio_track_text)); - mSettingsMainTextsList.add( - mResources.getString(R.string.MediaControlView2_playback_speed_text)); - mSettingsMainTextsList.add( - mResources.getString(R.string.MediaControlView2_help_text)); - - mSettingsSubTextsList = new ArrayList(); - mSettingsSubTextsList.add( - mResources.getString(R.string.MediaControlView2_audio_track_none_text)); - mSettingsSubTextsList.add( - mResources.getStringArray( - R.array.MediaControlView2_playback_speeds)[PLAYBACK_SPEED_1x_INDEX]); - mSettingsSubTextsList.add(RESOURCE_EMPTY); - - mSettingsIconIdsList = new ArrayList(); - mSettingsIconIdsList.add(R.drawable.ic_audiotrack); - mSettingsIconIdsList.add(R.drawable.ic_play_circle_filled); - mSettingsIconIdsList.add(R.drawable.ic_help); - - mAudioTrackList = new ArrayList(); - mAudioTrackList.add( - mResources.getString(R.string.MediaControlView2_audio_track_none_text)); - - mVideoQualityList = new ArrayList(); - mVideoQualityList.add( - mResources.getString(R.string.MediaControlView2_video_quality_auto_text)); - - mPlaybackSpeedTextList = new ArrayList(Arrays.asList( - mResources.getStringArray(R.array.MediaControlView2_playback_speeds))); - // Select the "1x" speed as the default value. - mSelectedSpeedIndex = PLAYBACK_SPEED_1x_INDEX; - - mPlaybackSpeedList = new ArrayList(); - int[] speeds = mResources.getIntArray(R.array.speed_multiplied_by_100); - for (int i = 0; i < speeds.length; i++) { - float speed = (float) speeds[i] / 100.0f; - mPlaybackSpeedList.add(speed); - } - } - - private void displaySettingsWindow(BaseAdapter adapter) { - // Set Adapter - mSettingsListView.setAdapter(adapter); - - // Set width of window - int itemWidth = (mSizeType == SIZE_TYPE_EMBEDDED) - ? mEmbeddedSettingsItemWidth : mFullSettingsItemWidth; - mSettingsWindow.setWidth(itemWidth); - - // Calculate height of window and show - int itemHeight = (mSizeType == SIZE_TYPE_EMBEDDED) - ? mEmbeddedSettingsItemHeight : mFullSettingsItemHeight; - int totalHeight = adapter.getCount() * itemHeight; - mSettingsWindow.dismiss(); - mSettingsWindow.showAsDropDown(mInstance, mSettingsWindowMargin, - mSettingsWindowMargin - totalHeight, Gravity.BOTTOM | Gravity.RIGHT); - } - - private class MediaControllerCallback extends MediaController.Callback { - @Override - public void onPlaybackStateChanged(PlaybackState state) { - mPlaybackState = state; - - // Update pause button depending on playback state for the following two reasons: - // 1) Need to handle case where app customizes playback state behavior when app - // activity is resumed. - // 2) Need to handle case where the media file reaches end of duration. - if (mPlaybackState.getState() != mPrevState) { - switch (mPlaybackState.getState()) { - case PlaybackState.STATE_PLAYING: - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_pause_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_pause_button_desc)); - mInstance.removeCallbacks(mUpdateProgress); - mInstance.post(mUpdateProgress); - break; - case PlaybackState.STATE_PAUSED: - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_play_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_play_button_desc)); - break; - case PlaybackState.STATE_STOPPED: - mPlayPauseButton.setImageDrawable( - mResources.getDrawable(R.drawable.ic_replay_circle_filled, null)); - mPlayPauseButton.setContentDescription( - mResources.getString(R.string.mcv2_replay_button_desc)); - mIsStopped = true; - break; - default: - break; - } - mPrevState = mPlaybackState.getState(); - } - - if (mPlaybackActions != mPlaybackState.getActions()) { - long newActions = mPlaybackState.getActions(); - if ((newActions & PlaybackState.ACTION_PAUSE) != 0) { - mPlayPauseButton.setVisibility(View.VISIBLE); - } - if ((newActions & PlaybackState.ACTION_REWIND) != 0 - && mMediaType != MEDIA_TYPE_MUSIC) { - if (mRewButton != null) { - mRewButton.setVisibility(View.VISIBLE); - } - } - if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0 - && mMediaType != MEDIA_TYPE_MUSIC) { - if (mFfwdButton != null) { - mFfwdButton.setVisibility(View.VISIBLE); - } - } - if ((newActions & PlaybackState.ACTION_SEEK_TO) != 0) { - mSeekAvailable = true; - } else { - mSeekAvailable = false; - } - mPlaybackActions = newActions; - } - - // Add buttons if custom actions are present. - List customActions = mPlaybackState.getCustomActions(); - mCustomButtons.removeAllViews(); - if (customActions.size() > 0) { - for (PlaybackState.CustomAction action : customActions) { - ImageButton button = new ImageButton(mInstance.getContext(), - null /* AttributeSet */, 0 /* Style */); - // TODO: Apply R.style.BottomBarButton to this button using library context. - // Refer Constructor with argument (int defStyleRes) of View.java - button.setImageResource(action.getIcon()); - button.setTooltipText(action.getName()); - final String actionString = action.getAction().toString(); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // TODO: Currently, we are just sending extras that came from session. - // Is it the right behavior? - mControls.sendCustomAction(actionString, action.getExtras()); - mInstance.setVisibility(View.VISIBLE); - } - }); - mCustomButtons.addView(button); - } - } - } - - @Override - public void onMetadataChanged(MediaMetadata metadata) { - mMetadata = metadata; - updateDuration(); - updateTitle(); - updateAudioMetadata(); - } - - @Override - public void onSessionEvent(String event, Bundle extras) { - switch (event) { - case EVENT_UPDATE_TRACK_STATUS: - mVideoTrackCount = extras.getInt(KEY_VIDEO_TRACK_COUNT); - // If there is one or more audio tracks, and this information has not been - // reflected into the Settings window yet, automatically check the first track. - // Otherwise, the Audio Track selection will be defaulted to "None". - mAudioTrackCount = extras.getInt(KEY_AUDIO_TRACK_COUNT); - mAudioTrackList = new ArrayList(); - if (mAudioTrackCount > 0) { - // TODO: add more text about track info. - for (int i = 0; i < mAudioTrackCount; i++) { - String track = mResources.getString( - R.string.MediaControlView2_audio_track_number_text, i + 1); - mAudioTrackList.add(track); - } - // Change sub text inside the Settings window. - mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK, - mAudioTrackList.get(0)); - } else { - mAudioTrackList.add(mResources.getString( - R.string.MediaControlView2_audio_track_none_text)); - } - if (mVideoTrackCount == 0 && mAudioTrackCount > 0) { - mMediaType = MEDIA_TYPE_MUSIC; - } - - mSubtitleTrackCount = extras.getInt(KEY_SUBTITLE_TRACK_COUNT); - mSubtitleDescriptionsList = new ArrayList(); - if (mSubtitleTrackCount > 0) { - mSubtitleButton.setVisibility(View.VISIBLE); - mSubtitleButton.setEnabled(true); - mSubtitleDescriptionsList.add(mResources.getString( - R.string.MediaControlView2_subtitle_off_text)); - for (int i = 0; i < mSubtitleTrackCount; i++) { - String track = mResources.getString( - R.string.MediaControlView2_subtitle_track_number_text, i + 1); - mSubtitleDescriptionsList.add(track); - } - } else { - mSubtitleButton.setVisibility(View.GONE); - mSubtitleButton.setEnabled(false); - } - break; - case EVENT_UPDATE_MEDIA_TYPE_STATUS: - boolean newStatus = extras.getBoolean(KEY_STATE_IS_ADVERTISEMENT); - if (newStatus != mIsAdvertisement) { - mIsAdvertisement = newStatus; - updateLayout(); - } - break; - } - } - } - - private class SettingsAdapter extends BaseAdapter { - private List mIconIds; - private List mMainTexts; - private List mSubTexts; - - public SettingsAdapter(List mainTexts, @Nullable List subTexts, - @Nullable List iconIds) { - mMainTexts = mainTexts; - mSubTexts = subTexts; - mIconIds = iconIds; - } - - public void updateSubTexts(List subTexts) { - mSubTexts = subTexts; - notifyDataSetChanged(); - } - - public String getMainText(int position) { - if (mMainTexts != null) { - if (position < mMainTexts.size()) { - return mMainTexts.get(position); - } - } - return RESOURCE_EMPTY; - } - - @Override - public int getCount() { - return (mMainTexts == null) ? 0 : mMainTexts.size(); - } - - @Override - public long getItemId(int position) { - // Auto-generated method stub--does not have any purpose here - // TODO: implement this. - return 0; - } - - @Override - public Object getItem(int position) { - // Auto-generated method stub--does not have any purpose here - // TODO: implement this. - return null; - } - - @Override - public View getView(int position, View convertView, ViewGroup container) { - View row; - if (mSizeType == SIZE_TYPE_FULL) { - row = ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.full_settings_list_item); - } else { - row = ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.embedded_settings_list_item); - } - TextView mainTextView = (TextView) row.findViewById(R.id.main_text); - TextView subTextView = (TextView) row.findViewById(R.id.sub_text); - ImageView iconView = (ImageView) row.findViewById(R.id.icon); - - // Set main text - mainTextView.setText(mMainTexts.get(position)); - - // Remove sub text and center the main text if sub texts do not exist at all or the sub - // text at this particular position is empty. - if (mSubTexts == null || mSubTexts.get(position) == RESOURCE_EMPTY) { - subTextView.setVisibility(View.GONE); - } else { - // Otherwise, set sub text. - subTextView.setText(mSubTexts.get(position)); - } - - // Remove main icon and set visibility to gone if icons are set to null or the icon at - // this particular position is set to RESOURCE_NON_EXISTENT. - if (mIconIds == null || mIconIds.get(position) == RESOURCE_NON_EXISTENT) { - iconView.setVisibility(View.GONE); - } else { - // Otherwise, set main icon. - iconView.setImageDrawable(mResources.getDrawable(mIconIds.get(position), null)); - } - return row; - } - - public void setSubTexts(List subTexts) { - mSubTexts = subTexts; - } - } - - // TODO: extend this class from SettingsAdapter - private class SubSettingsAdapter extends BaseAdapter { - private List mTexts; - private int mCheckPosition; - - public SubSettingsAdapter(List texts, int checkPosition) { - mTexts = texts; - mCheckPosition = checkPosition; - } - - public String getMainText(int position) { - if (mTexts != null) { - if (position < mTexts.size()) { - return mTexts.get(position); - } - } - return RESOURCE_EMPTY; - } - - @Override - public int getCount() { - return (mTexts == null) ? 0 : mTexts.size(); - } - - @Override - public long getItemId(int position) { - // Auto-generated method stub--does not have any purpose here - // TODO: implement this. - return 0; - } - - @Override - public Object getItem(int position) { - // Auto-generated method stub--does not have any purpose here - // TODO: implement this. - return null; - } - - @Override - public View getView(int position, View convertView, ViewGroup container) { - View row; - if (mSizeType == SIZE_TYPE_FULL) { - row = ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.full_sub_settings_list_item); - } else { - row = ApiHelper.inflateLibLayout(mInstance.getContext(), - R.layout.embedded_sub_settings_list_item); - } - TextView textView = (TextView) row.findViewById(R.id.text); - ImageView checkView = (ImageView) row.findViewById(R.id.check); - - textView.setText(mTexts.get(position)); - if (position != mCheckPosition) { - checkView.setVisibility(View.INVISIBLE); - } - return row; - } - - public void setTexts(List texts) { - mTexts = texts; - } - - public void setCheckPosition(int checkPosition) { - mCheckPosition = checkPosition; - } - } -} diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java deleted file mode 100644 index 061bc5b341..0000000000 --- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW; - -import android.content.Context; -import android.graphics.Rect; -import android.media.MediaPlayer2; -import android.media.VideoSize; -import android.util.AttributeSet; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.View; - -import androidx.annotation.NonNull; - -class VideoSurfaceView extends SurfaceView implements VideoViewInterface, SurfaceHolder.Callback { - private static final String TAG = "VideoSurfaceView"; - private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG); - private SurfaceHolder mSurfaceHolder = null; - private SurfaceListener mSurfaceListener = null; - private MediaPlayer2 mMediaPlayer; - // A flag to indicate taking over other view should be proceed. - private boolean mIsTakingOverOldView; - private VideoViewInterface mOldView; - - - public VideoSurfaceView(Context context) { - this(context, null); - } - - public VideoSurfaceView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - getHolder().addCallback(this); - } - - //////////////////////////////////////////////////// - // implements VideoViewInterface - //////////////////////////////////////////////////// - - @Override - public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) { - Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceHolder: " + mSurfaceHolder); - if (mp == null || !hasAvailableSurface()) { - return false; - } - mp.setDisplay(mSurfaceHolder); - return true; - } - - @Override - public void setSurfaceListener(SurfaceListener l) { - mSurfaceListener = l; - } - - @Override - public int getViewType() { - return VIEW_TYPE_SURFACEVIEW; - } - - @Override - public void setMediaPlayer(MediaPlayer2 mp) { - mMediaPlayer = mp; - if (mIsTakingOverOldView) { - takeOver(mOldView); - } - } - - @Override - public void takeOver(@NonNull VideoViewInterface oldView) { - if (assignSurfaceToMediaPlayer(mMediaPlayer)) { - ((View) oldView).setVisibility(GONE); - mIsTakingOverOldView = false; - mOldView = null; - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceTakeOverDone(this); - } - } else { - mIsTakingOverOldView = true; - mOldView = oldView; - } - } - - @Override - public boolean hasAvailableSurface() { - return (mSurfaceHolder != null && mSurfaceHolder.getSurface() != null); - } - - //////////////////////////////////////////////////// - // implements SurfaceHolder.Callback - //////////////////////////////////////////////////// - - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.d(TAG, "surfaceCreated: mSurfaceHolder: " + mSurfaceHolder + ", new holder: " + holder); - mSurfaceHolder = holder; - if (mIsTakingOverOldView) { - takeOver(mOldView); - } else { - assignSurfaceToMediaPlayer(mMediaPlayer); - } - - if (mSurfaceListener != null) { - Rect rect = mSurfaceHolder.getSurfaceFrame(); - mSurfaceListener.onSurfaceCreated(this, rect.width(), rect.height()); - } - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceChanged(this, width, height); - } - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - // After we return from this we can't use the surface any more - mSurfaceHolder = null; - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceDestroyed(this); - } - } - - // TODO: Investigate the way to move onMeasure() code into FrameLayout. - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth(); - int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight(); - if (DEBUG) { - Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " - + MeasureSpec.toString(heightMeasureSpec) + ")"); - Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); - Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight()); - Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight); - } - - int width = getDefaultSize(videoWidth, widthMeasureSpec); - int height = getDefaultSize(videoHeight, heightMeasureSpec); - - if (videoWidth > 0 && videoHeight > 0) { - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - width = widthSpecSize; - height = heightSpecSize; - - // for compatibility, we adjust size based on aspect ratio - if (videoWidth * height < width * videoHeight) { - width = height * videoWidth / videoHeight; - if (DEBUG) { - Log.d(TAG, "image too wide, correcting. width: " + width); - } - } else if (videoWidth * height > width * videoHeight) { - height = width * videoHeight / videoWidth; - if (DEBUG) { - Log.d(TAG, "image too tall, correcting. height: " + height); - } - } - } else { - // no size yet, just adopt the given spec sizes - } - setMeasuredDimension(width, height); - if (DEBUG) { - Log.i(TAG, "end of onMeasure()"); - Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); - } - } - - @Override - public String toString() { - return "ViewType: SurfaceView / Visibility: " + getVisibility() - + " / surfaceHolder: " + mSurfaceHolder; - } -} diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java deleted file mode 100644 index c2c1ca6a58..0000000000 --- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW; - -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.media.MediaPlayer2; -import android.media.VideoSize; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Surface; -import android.view.TextureView; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; - -@RequiresApi(26) -class VideoTextureView extends TextureView - implements VideoViewInterface, TextureView.SurfaceTextureListener { - private static final String TAG = "VideoTextureView"; - private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG); - - private SurfaceTexture mSurfaceTexture; - private Surface mSurface; - private SurfaceListener mSurfaceListener; - private MediaPlayer2 mMediaPlayer; - // A flag to indicate taking over other view should be proceed. - private boolean mIsTakingOverOldView; - private VideoViewInterface mOldView; - - public VideoTextureView(Context context) { - this(context, null); - } - - public VideoTextureView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public VideoTextureView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public VideoTextureView( - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - setSurfaceTextureListener(this); - } - - //////////////////////////////////////////////////// - // implements VideoViewInterface - //////////////////////////////////////////////////// - - @Override - public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) { - Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture); - if (mp == null || !hasAvailableSurface()) { - // Surface is not ready. - return false; - } - mp.setSurface(mSurface); - return true; - } - - @Override - public void setSurfaceListener(SurfaceListener l) { - mSurfaceListener = l; - } - - @Override - public int getViewType() { - return VIEW_TYPE_TEXTUREVIEW; - } - - @Override - public void setMediaPlayer(MediaPlayer2 mp) { - mMediaPlayer = mp; - if (mIsTakingOverOldView) { - takeOver(mOldView); - } - } - - @Override - public void takeOver(@NonNull VideoViewInterface oldView) { - if (assignSurfaceToMediaPlayer(mMediaPlayer)) { - ((View) oldView).setVisibility(GONE); - mIsTakingOverOldView = false; - mOldView = null; - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceTakeOverDone(this); - } - } else { - mIsTakingOverOldView = true; - mOldView = oldView; - } - } - - @Override - public boolean hasAvailableSurface() { - return (mSurfaceTexture != null && !mSurfaceTexture.isReleased() && mSurface != null); - } - - //////////////////////////////////////////////////// - // implements TextureView.SurfaceTextureListener - //////////////////////////////////////////////////// - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { - Log.d(TAG, "onSurfaceTextureAvailable: mSurfaceTexture: " + mSurfaceTexture - + ", new surface: " + surfaceTexture); - mSurfaceTexture = surfaceTexture; - mSurface = new Surface(mSurfaceTexture); - if (mIsTakingOverOldView) { - takeOver(mOldView); - } else { - assignSurfaceToMediaPlayer(mMediaPlayer); - } - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceCreated(this, width, height); - } - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceChanged(this, width, height); - } - // requestLayout(); // TODO: figure out if it should be called here? - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - // no-op - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { - if (mSurfaceListener != null) { - mSurfaceListener.onSurfaceDestroyed(this); - } - mSurfaceTexture = null; - mSurface = null; - return true; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth(); - int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight(); - if (DEBUG) { - Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " - + MeasureSpec.toString(heightMeasureSpec) + ")"); - Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); - Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight()); - Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight); - } - - int width = getDefaultSize(videoWidth, widthMeasureSpec); - int height = getDefaultSize(videoHeight, heightMeasureSpec); - - if (videoWidth > 0 && videoHeight > 0) { - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - width = widthSpecSize; - height = heightSpecSize; - - // for compatibility, we adjust size based on aspect ratio - if (videoWidth * height < width * videoHeight) { - width = height * videoWidth / videoHeight; - if (DEBUG) { - Log.d(TAG, "image too wide, correcting. width: " + width); - } - } else if (videoWidth * height > width * videoHeight) { - height = width * videoHeight / videoWidth; - if (DEBUG) { - Log.d(TAG, "image too tall, correcting. height: " + height); - } - } - } else { - // no size yet, just adopt the given spec sizes - } - setMeasuredDimension(width, height); - if (DEBUG) { - Log.i(TAG, "end of onMeasure()"); - Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); - } - } - - @Override - public String toString() { - return "ViewType: TextureView / Visibility: " + getVisibility() - + " / surfaceTexture: " + mSurfaceTexture; - - } -} diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java deleted file mode 100644 index 3dae8f7f95..0000000000 --- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java +++ /dev/null @@ -1,1383 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Point; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.media.AudioAttributes; -import android.media.AudioFocusRequest; -import android.media.AudioManager; -import android.media.DataSourceDesc; -import android.media.MediaItem2; -import android.media.MediaMetadata; -import android.media.MediaMetadata2; -import android.media.MediaMetadataRetriever; -import android.media.MediaPlayer2; -import android.media.MediaPlayer2.EventCallback; -import android.media.MediaPlayer2; -import android.media.PlaybackParams; -import android.media.SessionToken2; -import android.media.SubtitleData; -import android.media.TimedText; -import android.media.VideoSize; -import android.media.session.MediaController; -import android.media.session.MediaController.PlaybackInfo; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; -import android.media.update.VideoView2Provider; -import android.media.update.ViewGroupProvider; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.ResultReceiver; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.Pair; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; -import android.widget.ImageView; -import android.widget.MediaControlView2; -import android.widget.TextView; -import android.widget.VideoView2; - -import androidx.annotation.Nullable; - -import com.android.internal.graphics.palette.Palette; -import com.android.media.RoutePlayer; -import com.android.media.subtitle.ClosedCaptionRenderer; -import com.android.media.subtitle.SubtitleController; -import com.android.media.subtitle.SubtitleTrack; -import com.android.media.update.ApiHelper; -import com.android.media.update.R; -import com.android.support.mediarouter.media.MediaControlIntent; -import com.android.support.mediarouter.media.MediaItemStatus; -import com.android.support.mediarouter.media.MediaRouteSelector; -import com.android.support.mediarouter.media.MediaRouter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -public class VideoView2Impl extends BaseLayout - implements VideoView2Provider, VideoViewInterface.SurfaceListener { - private static final String TAG = "VideoView2"; - private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG); - private static final long DEFAULT_SHOW_CONTROLLER_INTERVAL_MS = 2000; - - private final VideoView2 mInstance; - - private static final int STATE_ERROR = -1; - private static final int STATE_IDLE = 0; - private static final int STATE_PREPARING = 1; - private static final int STATE_PREPARED = 2; - private static final int STATE_PLAYING = 3; - private static final int STATE_PAUSED = 4; - private static final int STATE_PLAYBACK_COMPLETED = 5; - - private static final int INVALID_TRACK_INDEX = -1; - private static final float INVALID_SPEED = 0f; - - private static final int SIZE_TYPE_EMBEDDED = 0; - private static final int SIZE_TYPE_FULL = 1; - // TODO: add support for Minimal size type. - private static final int SIZE_TYPE_MINIMAL = 2; - - private AccessibilityManager mAccessibilityManager; - private AudioManager mAudioManager; - private AudioAttributes mAudioAttributes; - private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain - - private Pair mCustomActionListenerRecord; - private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener; - private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener; - - private VideoViewInterface mCurrentView; - private VideoTextureView mTextureView; - private VideoSurfaceView mSurfaceView; - - private MediaPlayer2 mMediaPlayer; - private DataSourceDesc mDsd; - private MediaControlView2 mMediaControlView; - private MediaSession mMediaSession; - private MediaController mMediaController; - private boolean mSeekable; - private MediaMetadata2 mMediaMetadata; - private MediaMetadataRetriever mRetriever; - private boolean mNeedUpdateMediaType; - private Bundle mMediaTypeData; - private String mTitle; - - // TODO: move music view inside SurfaceView/TextureView or implement VideoViewInterface. - private WindowManager mManager; - private Resources mResources; - private View mMusicView; - private Drawable mMusicAlbumDrawable; - private String mMusicTitleText; - private String mMusicArtistText; - private boolean mIsMusicMediaType; - private int mPrevWidth; - private int mPrevHeight; - private int mDominantColor; - private int mSizeType; - - private PlaybackState.Builder mStateBuilder; - private List mCustomActionList; - private int mTargetState = STATE_IDLE; - private int mCurrentState = STATE_IDLE; - private int mCurrentBufferPercentage; - private long mSeekWhenPrepared; // recording the seek position while preparing - - private int mVideoWidth; - private int mVideoHeight; - - private ArrayList mVideoTrackIndices; - private ArrayList mAudioTrackIndices; - private ArrayList> mSubtitleTrackIndices; - private SubtitleController mSubtitleController; - - // selected video/audio/subtitle track index as MediaPlayer2 returns - private int mSelectedVideoTrackIndex; - private int mSelectedAudioTrackIndex; - private int mSelectedSubtitleTrackIndex; - - private SubtitleView mSubtitleView; - private boolean mSubtitleEnabled; - - private float mSpeed; - // TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams(). - // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit - private float mFallbackSpeed; // keep the original speed before 'pause' is called. - private float mVolumeLevelFloat; - private int mVolumeLevel; - - private long mShowControllerIntervalMs; - - private MediaRouter mMediaRouter; - private MediaRouteSelector mRouteSelector; - private MediaRouter.RouteInfo mRoute; - private RoutePlayer mRoutePlayer; - - private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() { - @Override - public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { - if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { - // Stop local playback (if necessary) - resetPlayer(); - mRoute = route; - mRoutePlayer = new RoutePlayer(mInstance.getContext(), route); - mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() { - @Override - public void onPlayerStateChanged(MediaItemStatus itemStatus) { - PlaybackState.Builder psBuilder = new PlaybackState.Builder(); - psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS); - long position = itemStatus.getContentPosition(); - switch (itemStatus.getPlaybackState()) { - case MediaItemStatus.PLAYBACK_STATE_PENDING: - psBuilder.setState(PlaybackState.STATE_NONE, position, 0); - mCurrentState = STATE_IDLE; - break; - case MediaItemStatus.PLAYBACK_STATE_PLAYING: - psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1); - mCurrentState = STATE_PLAYING; - break; - case MediaItemStatus.PLAYBACK_STATE_PAUSED: - psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0); - mCurrentState = STATE_PAUSED; - break; - case MediaItemStatus.PLAYBACK_STATE_BUFFERING: - psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0); - mCurrentState = STATE_PAUSED; - break; - case MediaItemStatus.PLAYBACK_STATE_FINISHED: - psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0); - mCurrentState = STATE_PLAYBACK_COMPLETED; - break; - } - - PlaybackState pbState = psBuilder.build(); - mMediaSession.setPlaybackState(pbState); - - MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder(); - mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, - itemStatus.getContentDuration()); - mMediaSession.setMetadata(mmBuilder.build()); - } - }); - // Start remote playback (if necessary) - mRoutePlayer.openVideo(mDsd); - } - } - - @Override - public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) { - if (mRoute != null && mRoutePlayer != null) { - mRoutePlayer.release(); - mRoutePlayer = null; - } - if (mRoute == route) { - mRoute = null; - } - if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { - // TODO: Resume local playback (if necessary) - openVideo(mDsd); - } - } - }; - - public VideoView2Impl(VideoView2 instance, - ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { - super(instance, superProvider, privateProvider); - mInstance = instance; - } - - @Override - public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - mVideoWidth = 0; - mVideoHeight = 0; - mSpeed = 1.0f; - mFallbackSpeed = mSpeed; - mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX; - mSeekable = true; - // TODO: add attributes to get this value. - mShowControllerIntervalMs = DEFAULT_SHOW_CONTROLLER_INTERVAL_MS; - - mAccessibilityManager = AccessibilityManager.getInstance(mInstance.getContext()); - - mAudioManager = (AudioManager) mInstance.getContext() - .getSystemService(Context.AUDIO_SERVICE); - mAudioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA) - .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build(); - mInstance.setFocusable(true); - mInstance.setFocusableInTouchMode(true); - mInstance.requestFocus(); - - // TODO: try to keep a single child at a time rather than always having both. - mTextureView = new VideoTextureView(mInstance.getContext()); - mSurfaceView = new VideoSurfaceView(mInstance.getContext()); - LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - mTextureView.setLayoutParams(params); - mSurfaceView.setLayoutParams(params); - mTextureView.setSurfaceListener(this); - mSurfaceView.setSurfaceListener(this); - mInstance.addView(mTextureView); - mInstance.addView(mSurfaceView); - - mSubtitleView = new SubtitleView(mInstance.getContext()); - mSubtitleView.setLayoutParams(params); - mSubtitleView.setBackgroundColor(0); - mInstance.addView(mSubtitleView); - - boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue( - "http://schemas.android.com/apk/res/android", - "enableControlView", true); - if (enableControlView) { - mMediaControlView = new MediaControlView2(mInstance.getContext()); - } - - mSubtitleEnabled = (attrs == null) || attrs.getAttributeBooleanValue( - "http://schemas.android.com/apk/res/android", - "enableSubtitle", false); - - // TODO: Choose TextureView when SurfaceView cannot be created. - // Choose surface view by default - int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW - : attrs.getAttributeIntValue( - "http://schemas.android.com/apk/res/android", - "viewType", VideoView2.VIEW_TYPE_SURFACEVIEW); - if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) { - Log.d(TAG, "viewType attribute is surfaceView."); - mTextureView.setVisibility(View.GONE); - mSurfaceView.setVisibility(View.VISIBLE); - mCurrentView = mSurfaceView; - } else if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) { - Log.d(TAG, "viewType attribute is textureView."); - mTextureView.setVisibility(View.VISIBLE); - mSurfaceView.setVisibility(View.GONE); - mCurrentView = mTextureView; - } - - MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); - builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); - builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); - mRouteSelector = builder.build(); - } - - @Override - public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) { - mMediaControlView = mediaControlView; - mShowControllerIntervalMs = intervalMs; - // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe. - ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector); - - if (mInstance.isAttachedToWindow()) { - attachMediaControlView(); - } - } - - @Override - public MediaController getMediaController_impl() { - if (mMediaSession == null) { - throw new IllegalStateException("MediaSession instance is not available."); - } - return mMediaController; - } - - @Override - public SessionToken2 getMediaSessionToken_impl() { - // TODO: implement this - return null; - } - - @Override - public MediaControlView2 getMediaControlView2_impl() { - return mMediaControlView; - } - - @Override - public MediaMetadata2 getMediaMetadata_impl() { - return mMediaMetadata; - } - - @Override - public void setMediaMetadata_impl(MediaMetadata2 metadata) { - // TODO: integrate this with MediaSession2#MediaItem2 - mMediaMetadata = metadata; - - // TODO: add support for handling website link - mMediaTypeData = new Bundle(); - boolean isAd = metadata == null ? - false : metadata.getLong(MediaMetadata2.METADATA_KEY_ADVERTISEMENT) != 0; - mMediaTypeData.putBoolean( - MediaControlView2Impl.KEY_STATE_IS_ADVERTISEMENT, isAd); - - if (mMediaSession != null) { - mMediaSession.sendSessionEvent( - MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData); - } else { - // Update later inside OnPreparedListener after MediaSession is initialized. - mNeedUpdateMediaType = true; - } - } - - @Override - public void setSubtitleEnabled_impl(boolean enable) { - if (enable != mSubtitleEnabled) { - selectOrDeselectSubtitle(enable); - } - mSubtitleEnabled = enable; - } - - @Override - public boolean isSubtitleEnabled_impl() { - return mSubtitleEnabled; - } - - // TODO: remove setSpeed_impl once MediaController2 is ready. - @Override - public void setSpeed_impl(float speed) { - if (speed <= 0.0f) { - Log.e(TAG, "Unsupported speed (" + speed + ") is ignored."); - return; - } - mSpeed = speed; - if (mMediaPlayer != null && mMediaPlayer.getState() == MediaPlayer2.PLAYER_STATE_PLAYING) { - applySpeed(); - } - updatePlaybackState(); - } - - @Override - public void setAudioFocusRequest_impl(int focusGain) { - if (focusGain != AudioManager.AUDIOFOCUS_NONE - && focusGain != AudioManager.AUDIOFOCUS_GAIN - && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT - && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK - && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) { - throw new IllegalArgumentException("Illegal audio focus type " + focusGain); - } - mAudioFocusType = focusGain; - } - - @Override - public void setAudioAttributes_impl(AudioAttributes attributes) { - if (attributes == null) { - throw new IllegalArgumentException("Illegal null AudioAttributes"); - } - mAudioAttributes = attributes; - } - - @Override - public void setVideoPath_impl(String path) { - mInstance.setVideoUri(Uri.parse(path)); - } - - @Override - public void setVideoUri_impl(Uri uri) { - mInstance.setVideoUri(uri, null); - } - - @Override - public void setVideoUri_impl(Uri uri, Map headers) { - DataSourceDesc.Builder builder = new DataSourceDesc.Builder(); - builder.setDataSource(mInstance.getContext(), uri, headers, null); - mInstance.setDataSource(builder.build()); - } - - @Override - public void setMediaItem_impl(MediaItem2 mediaItem) { - // TODO: implement this - } - - @Override - public void setDataSource_impl(DataSourceDesc dsd) { - mDsd = dsd; - mSeekWhenPrepared = 0; - openVideo(dsd); - } - - @Override - public void setViewType_impl(int viewType) { - if (viewType == mCurrentView.getViewType()) { - return; - } - VideoViewInterface targetView; - if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) { - Log.d(TAG, "switching to TextureView"); - targetView = mTextureView; - } else if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) { - Log.d(TAG, "switching to SurfaceView"); - targetView = mSurfaceView; - } else { - throw new IllegalArgumentException("Unknown view type: " + viewType); - } - ((View) targetView).setVisibility(View.VISIBLE); - targetView.takeOver(mCurrentView); - mInstance.requestLayout(); - } - - @Override - public int getViewType_impl() { - return mCurrentView.getViewType(); - } - - @Override - public void setCustomActions_impl( - List actionList, - Executor executor, VideoView2.OnCustomActionListener listener) { - mCustomActionList = actionList; - mCustomActionListenerRecord = new Pair<>(executor, listener); - - // Create a new playback builder in order to clear existing the custom actions. - mStateBuilder = null; - updatePlaybackState(); - } - - @Override - public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) { - mViewTypeChangedListener = l; - } - - @Override - public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) { - mFullScreenRequestListener = l; - } - - @Override - public void onAttachedToWindow_impl() { - super.onAttachedToWindow_impl(); - - // Create MediaSession - mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession"); - mMediaSession.setCallback(new MediaSessionCallback()); - mMediaSession.setActive(true); - mMediaController = mMediaSession.getController(); - mMediaRouter = MediaRouter.getInstance(mInstance.getContext()); - mMediaRouter.setMediaSession(mMediaSession); - mMediaRouter.addCallback(mRouteSelector, mRouterCallback); - attachMediaControlView(); - // TODO: remove this after moving MediaSession creating code inside initializing VideoView2 - if (mCurrentState == STATE_PREPARED) { - extractTracks(); - extractMetadata(); - extractAudioMetadata(); - if (mNeedUpdateMediaType) { - mMediaSession.sendSessionEvent( - MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, - mMediaTypeData); - mNeedUpdateMediaType = false; - } - } - } - - @Override - public void onDetachedFromWindow_impl() { - super.onDetachedFromWindow_impl(); - - mMediaSession.release(); - mMediaSession = null; - mMediaController = null; - } - - @Override - public CharSequence getAccessibilityClassName_impl() { - return VideoView2.class.getName(); - } - - @Override - public boolean onTouchEvent_impl(MotionEvent ev) { - if (DEBUG) { - Log.d(TAG, "onTouchEvent(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState); - } - if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) { - if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) { - toggleMediaControlViewVisibility(); - } - } - - return super.onTouchEvent_impl(ev); - } - - @Override - public boolean onTrackballEvent_impl(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) { - if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) { - toggleMediaControlViewVisibility(); - } - } - - return super.onTrackballEvent_impl(ev); - } - - @Override - public boolean dispatchTouchEvent_impl(MotionEvent ev) { - // TODO: Test touch event handling logic thoroughly and simplify the logic. - return super.dispatchTouchEvent_impl(ev); - } - - @Override - public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec); - - if (mIsMusicMediaType) { - if (mPrevWidth != mInstance.getMeasuredWidth() - || mPrevHeight != mInstance.getMeasuredHeight()) { - int currWidth = mInstance.getMeasuredWidth(); - int currHeight = mInstance.getMeasuredHeight(); - Point screenSize = new Point(); - mManager.getDefaultDisplay().getSize(screenSize); - int screenWidth = screenSize.x; - int screenHeight = screenSize.y; - - if (currWidth == screenWidth && currHeight == screenHeight) { - int orientation = retrieveOrientation(); - if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { - inflateMusicView(R.layout.full_landscape_music); - } else { - inflateMusicView(R.layout.full_portrait_music); - } - - if (mSizeType != SIZE_TYPE_FULL) { - mSizeType = SIZE_TYPE_FULL; - // Remove existing mFadeOut callback - mMediaControlView.removeCallbacks(mFadeOut); - mMediaControlView.setVisibility(View.VISIBLE); - } - } else { - if (mSizeType != SIZE_TYPE_EMBEDDED) { - mSizeType = SIZE_TYPE_EMBEDDED; - inflateMusicView(R.layout.embedded_music); - // Add new mFadeOut callback - mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs); - } - } - mPrevWidth = currWidth; - mPrevHeight = currHeight; - } - } - } - - /////////////////////////////////////////////////// - // Implements VideoViewInterface.SurfaceListener - /////////////////////////////////////////////////// - - @Override - public void onSurfaceCreated(View view, int width, int height) { - if (DEBUG) { - Log.d(TAG, "onSurfaceCreated(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState + ", width/height: " + width + "/" + height - + ", " + view.toString()); - } - if (needToStart()) { - mMediaController.getTransportControls().play(); - } - } - - @Override - public void onSurfaceDestroyed(View view) { - if (DEBUG) { - Log.d(TAG, "onSurfaceDestroyed(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState + ", " + view.toString()); - } - } - - @Override - public void onSurfaceChanged(View view, int width, int height) { - // TODO: Do we need to call requestLayout here? - if (DEBUG) { - Log.d(TAG, "onSurfaceChanged(). width/height: " + width + "/" + height - + ", " + view.toString()); - } - } - - @Override - public void onSurfaceTakeOverDone(VideoViewInterface view) { - if (DEBUG) { - Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view); - } - mCurrentView = view; - if (mViewTypeChangedListener != null) { - mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType()); - } - if (needToStart()) { - mMediaController.getTransportControls().play(); - } - } - - /////////////////////////////////////////////////// - // Protected or private methods - /////////////////////////////////////////////////// - - private void attachMediaControlView() { - // Get MediaController from MediaSession and set it inside MediaControlView - mMediaControlView.setController(mMediaSession.getController()); - - LayoutParams params = - new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - mInstance.addView(mMediaControlView, params); - } - - private boolean isInPlaybackState() { - return (mMediaPlayer != null || mRoutePlayer != null) - && mCurrentState != STATE_ERROR - && mCurrentState != STATE_IDLE - && mCurrentState != STATE_PREPARING; - } - - private boolean needToStart() { - return (mMediaPlayer != null || mRoutePlayer != null) - && mCurrentState != STATE_PLAYING - && mTargetState == STATE_PLAYING; - } - - // Creates a MediaPlayer2 instance and prepare playback. - private void openVideo(DataSourceDesc dsd) { - Uri uri = dsd.getUri(); - Map headers = dsd.getUriHeaders(); - resetPlayer(); - if (isRemotePlayback()) { - mRoutePlayer.openVideo(dsd); - return; - } - if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { - // TODO this should have a focus listener - AudioFocusRequest focusRequest; - focusRequest = new AudioFocusRequest.Builder(mAudioFocusType) - .setAudioAttributes(mAudioAttributes) - .build(); - mAudioManager.requestAudioFocus(focusRequest); - } - - try { - final Context context = mInstance.getContext(); - - Log.d(TAG, "openVideo(): creating new MediaPlayer2 instance."); - mMediaPlayer = new MediaPlayer2(context); - mSurfaceView.setMediaPlayer(mMediaPlayer); - mTextureView.setMediaPlayer(mMediaPlayer); - mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer); - - // TODO: Add timely firing logic for more accurate sync between CC and video frame - mSubtitleController = new SubtitleController(context); - mSubtitleController.registerRenderer(new ClosedCaptionRenderer(context)); - mSubtitleController.setAnchor((SubtitleController.Anchor) mSubtitleView); - Executor executor = new Executor() { - @Override - public void execute(Runnable runnable) { - runnable.run(); - } - }; - mMediaPlayer.registerEventCallback(executor, mMediaPlayer2Callback); - - mCurrentBufferPercentage = -1; - mMediaPlayer.setDataSource(dsd); - mMediaPlayer.setAudioAttributes(mAudioAttributes); - // we don't set the target state here either, but preserve the - // target state that was there before. - mCurrentState = STATE_PREPARING; - mMediaPlayer.prepare(); - - // Save file name as title since the file may not have a title Metadata. - mTitle = uri.getPath(); - String scheme = uri.getScheme(); - if (scheme != null && scheme.equals("file")) { - mTitle = uri.getLastPathSegment(); - } - mRetriever = new MediaMetadataRetriever(); - mRetriever.setDataSource(mInstance.getContext(), uri); - - if (DEBUG) { - Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState); - } - } catch (IllegalArgumentException ex) { - Log.w(TAG, "Unable to open content: " + uri, ex); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mMediaPlayer2Callback.onError(mMediaPlayer, dsd, - MediaPlayer2.MEDIA_ERROR_UNKNOWN, MediaPlayer2.MEDIA_ERROR_IO); - } - } - - /* - * Reset the media player in any state - */ - private void resetPlayer() { - if (mMediaPlayer != null) { - final MediaPlayer2 player = mMediaPlayer; - new AsyncTask() { - @Override - protected Void doInBackground(MediaPlayer2... players) { - // TODO: Fix NPE while MediaPlayer2.close() - //players[0].close(); - return null; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player); - mMediaPlayer = null; - mTextureView.setMediaPlayer(null); - mSurfaceView.setMediaPlayer(null); - //mPendingSubtitleTracks.clear(); - mCurrentState = STATE_IDLE; - mTargetState = STATE_IDLE; - if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { - mAudioManager.abandonAudioFocus(null); - } - } - mVideoWidth = 0; - mVideoHeight = 0; - } - - private void updatePlaybackState() { - if (mStateBuilder == null) { - // Add Play action as default - long playbackActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE; - if (mSeekable) { - playbackActions |= (PlaybackState.ACTION_REWIND | - PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_SEEK_TO); - } - mStateBuilder = new PlaybackState.Builder(); - mStateBuilder.setActions(playbackActions); - - if (mCustomActionList != null) { - for (PlaybackState.CustomAction action : mCustomActionList) { - mStateBuilder.addCustomAction(action); - } - } - } - mStateBuilder.setState(getCorrespondingPlaybackState(), - mMediaPlayer.getCurrentPosition(), mSpeed); - if (mCurrentState != STATE_ERROR - && mCurrentState != STATE_IDLE - && mCurrentState != STATE_PREPARING) { - // TODO: this should be replaced with MediaPlayer2.getBufferedPosition() once it is - // implemented. - if (mCurrentBufferPercentage == -1) { - mStateBuilder.setBufferedPosition(-1); - } else { - mStateBuilder.setBufferedPosition( - (long) (mCurrentBufferPercentage / 100.0 * mMediaPlayer.getDuration())); - } - } - - // Set PlaybackState for MediaSession - if (mMediaSession != null) { - PlaybackState state = mStateBuilder.build(); - mMediaSession.setPlaybackState(state); - } - } - - private int getCorrespondingPlaybackState() { - switch (mCurrentState) { - case STATE_ERROR: - return PlaybackState.STATE_ERROR; - case STATE_IDLE: - return PlaybackState.STATE_NONE; - case STATE_PREPARING: - return PlaybackState.STATE_CONNECTING; - case STATE_PREPARED: - return PlaybackState.STATE_PAUSED; - case STATE_PLAYING: - return PlaybackState.STATE_PLAYING; - case STATE_PAUSED: - return PlaybackState.STATE_PAUSED; - case STATE_PLAYBACK_COMPLETED: - return PlaybackState.STATE_STOPPED; - default: - return -1; - } - } - - private final Runnable mFadeOut = new Runnable() { - @Override - public void run() { - if (mCurrentState == STATE_PLAYING) { - mMediaControlView.setVisibility(View.GONE); - } - } - }; - - private void showController() { - // TODO: Decide what to show when the state is not in playback state - if (mMediaControlView == null || !isInPlaybackState() - || (mIsMusicMediaType && mSizeType == SIZE_TYPE_FULL)) { - return; - } - mMediaControlView.removeCallbacks(mFadeOut); - mMediaControlView.setVisibility(View.VISIBLE); - if (mShowControllerIntervalMs != 0 - && !mAccessibilityManager.isTouchExplorationEnabled()) { - mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs); - } - } - - private void toggleMediaControlViewVisibility() { - if (mMediaControlView.getVisibility() == View.VISIBLE) { - mMediaControlView.removeCallbacks(mFadeOut); - mMediaControlView.setVisibility(View.GONE); - } else { - showController(); - } - } - - private void applySpeed() { - PlaybackParams params = mMediaPlayer.getPlaybackParams().allowDefaults(); - if (mSpeed != params.getSpeed()) { - try { - params.setSpeed(mSpeed); - mMediaPlayer.setPlaybackParams(params); - mFallbackSpeed = mSpeed; - } catch (IllegalArgumentException e) { - Log.e(TAG, "PlaybackParams has unsupported value: " + e); - // TODO: should revise this part after integrating with MP2. - // If mSpeed had an illegal value for speed rate, system will determine best - // handling (see PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT). - // Note: The pre-MP2 returns 0.0f when it is paused. In this case, VideoView2 will - // use mFallbackSpeed instead. - float fallbackSpeed = mMediaPlayer.getPlaybackParams().allowDefaults().getSpeed(); - if (fallbackSpeed > 0.0f) { - mFallbackSpeed = fallbackSpeed; - } - mSpeed = mFallbackSpeed; - } - } - } - - private boolean isRemotePlayback() { - if (mMediaController == null) { - return false; - } - PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo(); - return playbackInfo != null - && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; - } - - private void selectOrDeselectSubtitle(boolean select) { - if (!isInPlaybackState()) { - return; - } - if (select) { - if (mSubtitleTrackIndices.size() > 0) { - // TODO: make this selection dynamic - mSelectedSubtitleTrackIndex = mSubtitleTrackIndices.get(0).first; - mSubtitleController.selectTrack(mSubtitleTrackIndices.get(0).second); - mMediaPlayer.selectTrack(mSelectedSubtitleTrackIndex); - mSubtitleView.setVisibility(View.VISIBLE); - } - } else { - if (mSelectedSubtitleTrackIndex != INVALID_TRACK_INDEX) { - mMediaPlayer.deselectTrack(mSelectedSubtitleTrackIndex); - mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX; - mSubtitleView.setVisibility(View.GONE); - } - } - } - - private void extractTracks() { - List trackInfos = mMediaPlayer.getTrackInfo(); - mVideoTrackIndices = new ArrayList<>(); - mAudioTrackIndices = new ArrayList<>(); - mSubtitleTrackIndices = new ArrayList<>(); - mSubtitleController.reset(); - for (int i = 0; i < trackInfos.size(); ++i) { - int trackType = trackInfos.get(i).getTrackType(); - if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) { - mVideoTrackIndices.add(i); - } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) { - mAudioTrackIndices.add(i); - } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE - || trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { - SubtitleTrack track = mSubtitleController.addTrack(trackInfos.get(i).getFormat()); - if (track != null) { - mSubtitleTrackIndices.add(new Pair<>(i, track)); - } - } - } - // Select first tracks as default - if (mVideoTrackIndices.size() > 0) { - mSelectedVideoTrackIndex = 0; - } - if (mAudioTrackIndices.size() > 0) { - mSelectedAudioTrackIndex = 0; - } - if (mVideoTrackIndices.size() == 0 && mAudioTrackIndices.size() > 0) { - mIsMusicMediaType = true; - } - - Bundle data = new Bundle(); - data.putInt(MediaControlView2Impl.KEY_VIDEO_TRACK_COUNT, mVideoTrackIndices.size()); - data.putInt(MediaControlView2Impl.KEY_AUDIO_TRACK_COUNT, mAudioTrackIndices.size()); - data.putInt(MediaControlView2Impl.KEY_SUBTITLE_TRACK_COUNT, mSubtitleTrackIndices.size()); - if (mSubtitleTrackIndices.size() > 0) { - selectOrDeselectSubtitle(mSubtitleEnabled); - } - mMediaSession.sendSessionEvent(MediaControlView2Impl.EVENT_UPDATE_TRACK_STATUS, data); - } - - private void extractMetadata() { - // Get and set duration and title values as MediaMetadata for MediaControlView2 - MediaMetadata.Builder builder = new MediaMetadata.Builder(); - builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle); - builder.putLong( - MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration()); - - if (mMediaSession != null) { - mMediaSession.setMetadata(builder.build()); - } - } - - private void extractAudioMetadata() { - if (!mIsMusicMediaType) { - return; - } - - mResources = ApiHelper.getLibResources(mInstance.getContext()); - mManager = (WindowManager) mInstance.getContext().getApplicationContext() - .getSystemService(Context.WINDOW_SERVICE); - - byte[] album = mRetriever.getEmbeddedPicture(); - if (album != null) { - Bitmap bitmap = BitmapFactory.decodeByteArray(album, 0, album.length); - mMusicAlbumDrawable = new BitmapDrawable(bitmap); - - // TODO: replace with visualizer - Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { - public void onGenerated(Palette palette) { - // TODO: add dominant color for default album image. - mDominantColor = palette.getDominantColor(0); - if (mMusicView != null) { - mMusicView.setBackgroundColor(mDominantColor); - } - } - }); - } else { - mMusicAlbumDrawable = mResources.getDrawable(R.drawable.ic_default_album_image); - } - - String title = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); - if (title != null) { - mMusicTitleText = title; - } else { - mMusicTitleText = mResources.getString(R.string.mcv2_music_title_unknown_text); - } - - String artist = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST); - if (artist != null) { - mMusicArtistText = artist; - } else { - mMusicArtistText = mResources.getString(R.string.mcv2_music_artist_unknown_text); - } - - // Send title and artist string to MediaControlView2 - MediaMetadata.Builder builder = new MediaMetadata.Builder(); - builder.putString(MediaMetadata.METADATA_KEY_TITLE, mMusicTitleText); - builder.putString(MediaMetadata.METADATA_KEY_ARTIST, mMusicArtistText); - mMediaSession.setMetadata(builder.build()); - - // Display Embedded mode as default - mInstance.removeView(mSurfaceView); - mInstance.removeView(mTextureView); - inflateMusicView(R.layout.embedded_music); - } - - private int retrieveOrientation() { - DisplayMetrics dm = Resources.getSystem().getDisplayMetrics(); - int width = dm.widthPixels; - int height = dm.heightPixels; - - return (height > width) ? - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : - ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - } - - private void inflateMusicView(int layoutId) { - mInstance.removeView(mMusicView); - - View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId); - v.setBackgroundColor(mDominantColor); - - ImageView albumView = v.findViewById(R.id.album); - if (albumView != null) { - albumView.setImageDrawable(mMusicAlbumDrawable); - } - - TextView titleView = v.findViewById(R.id.title); - if (titleView != null) { - titleView.setText(mMusicTitleText); - } - - TextView artistView = v.findViewById(R.id.artist); - if (artistView != null) { - artistView.setText(mMusicArtistText); - } - - mMusicView = v; - mInstance.addView(mMusicView, 0); - } - - EventCallback mMediaPlayer2Callback = - new EventCallback() { - @Override - public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) { - if (DEBUG) { - Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex() - + ", getCurrentPosition: " + mp.getCurrentPosition() - + ", getStartTimeUs(): " + data.getStartTimeUs() - + ", diff: " - + (data.getStartTimeUs()/1000 - mp.getCurrentPosition()) - + "ms, getDurationUs(): " + data.getDurationUs() - ); - - } - final int index = data.getTrackIndex(); - if (index != mSelectedSubtitleTrackIndex) { - Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex() - + ", selected track index: " + mSelectedSubtitleTrackIndex); - return; - } - for (Pair p : mSubtitleTrackIndices) { - if (p.first == index) { - SubtitleTrack track = p.second; - track.onData(data); - } - } - } - - @Override - public void onVideoSizeChanged( - MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) { - if (DEBUG) { - Log.d(TAG, "onVideoSizeChanged(): size: " + size.getWidth() + "/" - + size.getHeight()); - } - mVideoWidth = mp.getVideoSize().getWidth(); - mVideoHeight = mp.getVideoSize().getHeight(); - if (DEBUG) { - Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/" - + mVideoHeight); - } - if (mVideoWidth != 0 && mVideoHeight != 0) { - mInstance.requestLayout(); - } - } - - // TODO: Remove timed text related code later once relevant Renderer is defined. - // This is just for debugging purpose. - @Override - public void onTimedText( - MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { - Log.d(TAG, "TimedText: " + text.getText()); - } - - @Override - public void onInfo( - MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { - if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) { - extractTracks(); - } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) { - this.onPrepared(mp, dsd); - } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) { - this.onCompletion(mp, dsd); - } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) { - this.onBufferingUpdate(mp, dsd, extra); - } else if (what == MediaPlayer2.MEDIA_INFO_NOT_SEEKABLE) { - mSeekable = false; - } - } - - @Override - public void onError( - MediaPlayer2 mp, DataSourceDesc dsd, int frameworkErr, int implErr) { - if (DEBUG) { - Log.d(TAG, "Error: " + frameworkErr + "," + implErr); - } - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mSeekable = true; - updatePlaybackState(); - - if (mMediaControlView != null) { - mMediaControlView.setVisibility(View.GONE); - } - } - - @Override - public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, - int status) { - if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO && status == 0) { - updatePlaybackState(); - } - } - - private void onPrepared(MediaPlayer2 mp, DataSourceDesc dsd) { - if (DEBUG) { - Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState); - } - mCurrentState = STATE_PREPARED; - // Create and set playback state for MediaControlView2 - updatePlaybackState(); - - // TODO: change this to send TrackInfos to MediaControlView2 - // TODO: create MediaSession when initializing VideoView2 - if (mMediaSession != null) { - extractTracks(); - extractMetadata(); - extractAudioMetadata(); - } - - if (mMediaControlView != null) { - mMediaControlView.setEnabled(true); - } - int videoWidth = mp.getVideoSize().getWidth(); - int videoHeight = mp.getVideoSize().getHeight(); - - // mSeekWhenPrepared may be changed after seekTo() call - long seekToPosition = mSeekWhenPrepared; - if (seekToPosition != 0) { - mMediaController.getTransportControls().seekTo(seekToPosition); - } - - if (videoWidth != 0 && videoHeight != 0) { - if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) { - if (DEBUG) { - Log.i(TAG, "OnPreparedListener() : "); - Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight); - Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/" - + mInstance.getMeasuredHeight()); - Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/" - + mInstance.getHeight()); - } - mVideoWidth = videoWidth; - mVideoHeight = videoHeight; - mInstance.requestLayout(); - } - - if (needToStart()) { - mMediaController.getTransportControls().play(); - } - } else { - // We don't know the video size yet, but should start anyway. - // The video size might be reported to us later. - if (needToStart()) { - mMediaController.getTransportControls().play(); - } - } - } - - private void onCompletion(MediaPlayer2 mp, DataSourceDesc dsd) { - mCurrentState = STATE_PLAYBACK_COMPLETED; - mTargetState = STATE_PLAYBACK_COMPLETED; - mSeekable = true; - updatePlaybackState(); - if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { - mAudioManager.abandonAudioFocus(null); - } - } - - private void onBufferingUpdate(MediaPlayer2 mp, DataSourceDesc dsd, int percent) { - mCurrentBufferPercentage = percent; - updatePlaybackState(); - } - }; - - private class MediaSessionCallback extends MediaSession.Callback { - @Override - public void onCommand(String command, Bundle args, ResultReceiver receiver) { - if (isRemotePlayback()) { - mRoutePlayer.onCommand(command, args, receiver); - } else { - switch (command) { - case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE: - int subtitleIndex = args.getInt( - MediaControlView2Impl.KEY_SELECTED_SUBTITLE_INDEX, - INVALID_TRACK_INDEX); - if (subtitleIndex != INVALID_TRACK_INDEX) { - int subtitleTrackIndex = mSubtitleTrackIndices.get(subtitleIndex).first; - if (subtitleTrackIndex != mSelectedSubtitleTrackIndex) { - mSelectedSubtitleTrackIndex = subtitleTrackIndex; - mInstance.setSubtitleEnabled(true); - } - } - break; - case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE: - mInstance.setSubtitleEnabled(false); - break; - case MediaControlView2Impl.COMMAND_SET_FULLSCREEN: - if (mFullScreenRequestListener != null) { - mFullScreenRequestListener.onFullScreenRequest( - mInstance, - args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN)); - } - break; - case MediaControlView2Impl.COMMAND_SELECT_AUDIO_TRACK: - int audioIndex = args.getInt(MediaControlView2Impl.KEY_SELECTED_AUDIO_INDEX, - INVALID_TRACK_INDEX); - if (audioIndex != INVALID_TRACK_INDEX) { - int audioTrackIndex = mAudioTrackIndices.get(audioIndex); - if (audioTrackIndex != mSelectedAudioTrackIndex) { - mSelectedAudioTrackIndex = audioTrackIndex; - mMediaPlayer.selectTrack(mSelectedAudioTrackIndex); - } - } - break; - case MediaControlView2Impl.COMMAND_SET_PLAYBACK_SPEED: - float speed = args.getFloat( - MediaControlView2Impl.KEY_PLAYBACK_SPEED, INVALID_SPEED); - if (speed != INVALID_SPEED && speed != mSpeed) { - mInstance.setSpeed(speed); - mSpeed = speed; - } - break; - case MediaControlView2Impl.COMMAND_MUTE: - mVolumeLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); - mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0); - break; - case MediaControlView2Impl.COMMAND_UNMUTE: - mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolumeLevel, 0); - break; - } - } - showController(); - } - - @Override - public void onCustomAction(String action, Bundle extras) { - mCustomActionListenerRecord.first.execute(() -> - mCustomActionListenerRecord.second.onCustomAction(action, extras)); - showController(); - } - - @Override - public void onPlay() { - if (isInPlaybackState() && (mCurrentView.hasAvailableSurface() || mIsMusicMediaType)) { - if (isRemotePlayback()) { - mRoutePlayer.onPlay(); - } else { - applySpeed(); - mMediaPlayer.play(); - mCurrentState = STATE_PLAYING; - updatePlaybackState(); - } - mCurrentState = STATE_PLAYING; - } - mTargetState = STATE_PLAYING; - if (DEBUG) { - Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState); - } - showController(); - } - - @Override - public void onPause() { - if (isInPlaybackState()) { - if (isRemotePlayback()) { - mRoutePlayer.onPause(); - mCurrentState = STATE_PAUSED; - } else if (mMediaPlayer.getState() == MediaPlayer2.PLAYER_STATE_PLAYING) { - mMediaPlayer.pause(); - mCurrentState = STATE_PAUSED; - updatePlaybackState(); - } - } - mTargetState = STATE_PAUSED; - if (DEBUG) { - Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState - + ", mTargetState=" + mTargetState); - } - showController(); - } - - @Override - public void onSeekTo(long pos) { - if (isInPlaybackState()) { - if (isRemotePlayback()) { - mRoutePlayer.onSeekTo(pos); - } else { - mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC); - mSeekWhenPrepared = 0; - } - } else { - mSeekWhenPrepared = pos; - } - showController(); - } - - @Override - public void onStop() { - if (isRemotePlayback()) { - mRoutePlayer.onStop(); - } else { - resetPlayer(); - } - showController(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java b/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java deleted file mode 100644 index 5a0682614f..0000000000 --- a/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.media.update.ViewGroupProvider; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; - -public abstract class ViewGroupImpl implements ViewGroupProvider { - private final ViewGroupProvider mSuperProvider; - - public ViewGroupImpl(ViewGroup instance, - ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { - mSuperProvider = superProvider; - } - - @Override - public void onAttachedToWindow_impl() { - mSuperProvider.onAttachedToWindow_impl(); - } - - @Override - public void onDetachedFromWindow_impl() { - mSuperProvider.onDetachedFromWindow_impl(); - } - - @Override - public CharSequence getAccessibilityClassName_impl() { - return mSuperProvider.getAccessibilityClassName_impl(); - } - - @Override - public boolean onTouchEvent_impl(MotionEvent ev) { - return mSuperProvider.onTouchEvent_impl(ev); - } - - @Override - public boolean onTrackballEvent_impl(MotionEvent ev) { - return mSuperProvider.onTrackballEvent_impl(ev); - } - - @Override - public void onFinishInflate_impl() { - mSuperProvider.onFinishInflate_impl(); - } - - @Override - public void setEnabled_impl(boolean enabled) { - mSuperProvider.setEnabled_impl(enabled); - } - - @Override - public void onVisibilityAggregated_impl(boolean isVisible) { - mSuperProvider.onVisibilityAggregated_impl(isVisible); - } - - @Override - public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { - mSuperProvider.onLayout_impl(changed, left, top, right, bottom); - } - - @Override - public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { - mSuperProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec); - } - - @Override - public int getSuggestedMinimumWidth_impl() { - return mSuperProvider.getSuggestedMinimumWidth_impl(); - } - - @Override - public int getSuggestedMinimumHeight_impl() { - return mSuperProvider.getSuggestedMinimumHeight_impl(); - } - - @Override - public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) { - mSuperProvider.setMeasuredDimension_impl(measuredWidth, measuredHeight); - } - - @Override - public boolean dispatchTouchEvent_impl(MotionEvent ev) { - return mSuperProvider.dispatchTouchEvent_impl(ev); - } - - @Override - public boolean checkLayoutParams_impl(ViewGroup.LayoutParams p) { - return mSuperProvider.checkLayoutParams_impl(p); - } - - @Override - public ViewGroup.LayoutParams generateDefaultLayoutParams_impl() { - return mSuperProvider.generateDefaultLayoutParams_impl(); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams_impl(AttributeSet attrs) { - return mSuperProvider.generateLayoutParams_impl(attrs); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams_impl(ViewGroup.LayoutParams lp) { - return mSuperProvider.generateLayoutParams_impl(lp); - } - - @Override - public boolean shouldDelayChildPressedState_impl() { - return mSuperProvider.shouldDelayChildPressedState_impl(); - } - - @Override - public void measureChildWithMargins_impl(View child, - int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { - mSuperProvider.measureChildWithMargins_impl(child, - parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); - } -} -- GitLab From f6f07f70d8382977b04e0c2a4c1932ada482b7a2 Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Thu, 15 Nov 2018 16:03:50 +0900 Subject: [PATCH 0482/1530] Remove unused MediaBrowser2 and relevant classes Bug: 119591238 Test: build Change-Id: I68bad2d01526b44f87f2a25269b7956c86bfdee9 --- .../com/android/media/IMediaController2.aidl | 12 - .../src/com/android/media/IMediaSession2.aidl | 13 - .../com/android/media/MediaBrowser2Impl.java | 236 ------------------ .../android/media/MediaController2Impl.java | 59 ----- .../android/media/MediaController2Stub.java | 148 ----------- .../media/MediaLibraryService2Impl.java | 173 ------------- .../com/android/media/MediaSession2Impl.java | 19 +- .../com/android/media/MediaSession2Stub.java | 226 ----------------- .../media/MediaSessionService2Impl.java | 169 ------------- .../com/android/media/SessionToken2Impl.java | 49 +--- .../com/android/media/update/ApiFactory.java | 54 ---- 11 files changed, 5 insertions(+), 1153 deletions(-) delete mode 100644 packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java diff --git a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl index 0488b70473..cc5acf9bf4 100644 --- a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl +++ b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl @@ -52,16 +52,4 @@ oneway interface IMediaController2 { void onAllowedCommandsChanged(in Bundle commands); void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // Browser sepcific - ////////////////////////////////////////////////////////////////////////////////////////////// - void onGetLibraryRootDone(in Bundle rootHints, String rootMediaId, in Bundle rootExtra); - void onGetItemDone(String mediaId, in Bundle result); - void onChildrenChanged(String rootMediaId, int itemCount, in Bundle extras); - void onGetChildrenDone(String parentId, int page, int pageSize, in List result, - in Bundle extras); - void onSearchResultChanged(String query, int itemCount, in Bundle extras); - void onGetSearchResultDone(String query, int page, int pageSize, in List result, - in Bundle extras); } diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl index 664467dc20..5761455743 100644 --- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl +++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl @@ -69,17 +69,4 @@ oneway interface IMediaSession2 { void skipToNextItem(IMediaController2 caller); void setRepeatMode(IMediaController2 caller, int repeatMode); void setShuffleMode(IMediaController2 caller, int shuffleMode); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // library service specific - ////////////////////////////////////////////////////////////////////////////////////////////// - void getLibraryRoot(IMediaController2 caller, in Bundle rootHints); - void getItem(IMediaController2 caller, String mediaId); - void getChildren(IMediaController2 caller, String parentId, int page, int pageSize, - in Bundle extras); - void search(IMediaController2 caller, String query, in Bundle extras); - void getSearchResult(IMediaController2 caller, String query, int page, int pageSize, - in Bundle extras); - void subscribe(IMediaController2 caller, String parentId, in Bundle extras); - void unsubscribe(IMediaController2 caller, String parentId); } diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java deleted file mode 100644 index 0327beb2c4..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.content.Context; -import android.media.MediaBrowser2; -import android.media.MediaBrowser2.BrowserCallback; -import android.media.MediaItem2; -import android.media.SessionToken2; -import android.media.update.MediaBrowser2Provider; -import android.os.Bundle; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.Log; - -import java.util.List; -import java.util.concurrent.Executor; - -public class MediaBrowser2Impl extends MediaController2Impl implements MediaBrowser2Provider { - private final String TAG = "MediaBrowser2"; - private final boolean DEBUG = true; // TODO(jaewan): change. - - private final MediaBrowser2 mInstance; - private final MediaBrowser2.BrowserCallback mCallback; - - public MediaBrowser2Impl(Context context, MediaBrowser2 instance, SessionToken2 token, - Executor executor, BrowserCallback callback) { - super(context, instance, token, executor, callback); - mInstance = instance; - mCallback = callback; - } - - @Override MediaBrowser2 getInstance() { - return (MediaBrowser2) super.getInstance(); - } - - @Override - public void getLibraryRoot_impl(Bundle rootHints) { - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.getLibraryRoot(getControllerStub(), rootHints); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void subscribe_impl(String parentId, Bundle extras) { - if (parentId == null) { - throw new IllegalArgumentException("parentId shouldn't be null"); - } - - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.subscribe(getControllerStub(), parentId, extras); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void unsubscribe_impl(String parentId) { - if (parentId == null) { - throw new IllegalArgumentException("parentId shouldn't be null"); - } - - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.unsubscribe(getControllerStub(), parentId); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void getItem_impl(String mediaId) { - if (mediaId == null) { - throw new IllegalArgumentException("mediaId shouldn't be null"); - } - - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.getItem(getControllerStub(), mediaId); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void getChildren_impl(String parentId, int page, int pageSize, Bundle extras) { - if (parentId == null) { - throw new IllegalArgumentException("parentId shouldn't be null"); - } - if (page < 1 || pageSize < 1) { - throw new IllegalArgumentException("Neither page nor pageSize should be less than 1"); - } - - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.getChildren(getControllerStub(), parentId, page, pageSize, extras); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void search_impl(String query, Bundle extras) { - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query shouldn't be empty"); - } - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.search(getControllerStub(), query, extras); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void getSearchResult_impl(String query, int page, int pageSize, Bundle extras) { - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query shouldn't be empty"); - } - if (page < 1 || pageSize < 1) { - throw new IllegalArgumentException("Neither page nor pageSize should be less than 1"); - } - final IMediaSession2 binder = getSessionBinder(); - if (binder != null) { - try { - binder.getSearchResult(getControllerStub(), query, page, pageSize, extras); - } catch (RemoteException e) { - // TODO(jaewan): Handle disconnect. - if (DEBUG) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - public void onGetLibraryRootDone( - final Bundle rootHints, final String rootMediaId, final Bundle rootExtra) { - getCallbackExecutor().execute(() -> { - mCallback.onGetLibraryRootDone(getInstance(), rootHints, rootMediaId, rootExtra); - }); - } - - public void onGetItemDone(String mediaId, MediaItem2 item) { - getCallbackExecutor().execute(() -> { - mCallback.onGetItemDone(getInstance(), mediaId, item); - }); - } - - public void onGetChildrenDone(String parentId, int page, int pageSize, List result, - Bundle extras) { - getCallbackExecutor().execute(() -> { - mCallback.onGetChildrenDone(getInstance(), parentId, page, pageSize, result, extras); - }); - } - - public void onSearchResultChanged(String query, int itemCount, Bundle extras) { - getCallbackExecutor().execute(() -> { - mCallback.onSearchResultChanged(getInstance(), query, itemCount, extras); - }); - } - - public void onGetSearchResultDone(String query, int page, int pageSize, List result, - Bundle extras) { - getCallbackExecutor().execute(() -> { - mCallback.onGetSearchResultDone(getInstance(), query, page, pageSize, result, extras); - }); - } - - public void onChildrenChanged(final String parentId, int itemCount, final Bundle extras) { - getCallbackExecutor().execute(() -> { - mCallback.onChildrenChanged(getInstance(), parentId, itemCount, extras); - }); - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java index 2883087e8c..1c4cf82af9 100644 --- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java +++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java @@ -45,7 +45,6 @@ import android.media.MediaMetadata2; import android.media.MediaPlaylistAgent.RepeatMode; import android.media.MediaPlaylistAgent.ShuffleMode; import android.media.MediaSession2.CommandButton; -import android.media.MediaSessionService2; import android.media.Rating2; import android.media.SessionCommand2; import android.media.SessionCommandGroup2; @@ -154,64 +153,6 @@ public class MediaController2Impl implements MediaController2Provider { // Session mServiceConnection = null; connectToSession(SessionToken2Impl.from(mToken).getSessionBinder()); - } else { - // Session service - if (Process.myUid() == Process.SYSTEM_UID) { - // It's system server (MediaSessionService) that wants to monitor session. - // Don't bind if able.. - IMediaSession2 binder = SessionToken2Impl.from(mToken).getSessionBinder(); - if (binder != null) { - // Use binder in the session token instead of bind by its own. - // Otherwise server will holds the binding to the service *forever* and service - // will never stop. - mServiceConnection = null; - connectToSession(SessionToken2Impl.from(mToken).getSessionBinder()); - return; - } else if (DEBUG) { - // Should happen only when system server wants to dispatch media key events to - // a dead service. - Log.d(TAG, "System server binds to a session service. Should unbind" - + " immediately after the use."); - } - } - mServiceConnection = new SessionServiceConnection(); - connectToService(); - } - } - - private void connectToService() { - // Service. Needs to get fresh binder whenever connection is needed. - SessionToken2Impl impl = SessionToken2Impl.from(mToken); - final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE); - intent.setClassName(mToken.getPackageName(), impl.getServiceName()); - - // Use bindService() instead of startForegroundService() to start session service for three - // reasons. - // 1. Prevent session service owner's stopSelf() from destroying service. - // With the startForegroundService(), service's call of stopSelf() will trigger immediate - // onDestroy() calls on the main thread even when onConnect() is running in another - // thread. - // 2. Minimize APIs for developers to take care about. - // With bindService(), developers only need to take care about Service.onBind() - // but Service.onStartCommand() should be also taken care about with the - // startForegroundService(). - // 3. Future support for UI-less playback - // If a service wants to keep running, it should be either foreground service or - // bounded service. But there had been request for the feature for system apps - // and using bindService() will be better fit with it. - boolean result; - if (Process.myUid() == Process.SYSTEM_UID) { - // Use bindServiceAsUser() for binding from system service to avoid following warning. - // ContextImpl: Calling a method in the system process without a qualified user - result = mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE, - UserHandle.getUserHandleForUid(mToken.getUid())); - } else { - result = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); - } - if (!result) { - Log.w(TAG, "bind to " + mToken + " failed"); - } else if (DEBUG) { - Log.d(TAG, "bind to " + mToken + " success"); } } diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java index ece4a0032c..5b71e65dc1 100644 --- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java +++ b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java @@ -54,14 +54,6 @@ public class MediaController2Stub extends IMediaController2.Stub { return controller; } - private MediaBrowser2Impl getBrowser() throws IllegalStateException { - final MediaController2Impl controller = getController(); - if (controller instanceof MediaBrowser2Impl) { - return (MediaBrowser2Impl) controller; - } - return null; - } - public void destroy() { mController.clear(); } @@ -327,144 +319,4 @@ public class MediaController2Stub extends IMediaController2.Stub { } controller.onCustomCommand(command, args, receiver); } - - //////////////////////////////////////////////////////////////////////////////////////////// - // MediaBrowser specific - //////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra) - throws RuntimeException { - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - browser.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra); - } - - - @Override - public void onGetItemDone(String mediaId, Bundle itemBundle) throws RuntimeException { - if (mediaId == null) { - Log.w(TAG, "onGetItemDone(): Ignoring null mediaId"); - return; - } - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - browser.onGetItemDone(mediaId, MediaItem2.fromBundle(itemBundle)); - } - - @Override - public void onGetChildrenDone(String parentId, int page, int pageSize, - List itemBundleList, Bundle extras) throws RuntimeException { - if (parentId == null) { - Log.w(TAG, "onGetChildrenDone(): Ignoring null parentId"); - return; - } - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - - List result = null; - if (itemBundleList != null) { - result = new ArrayList<>(); - for (Bundle bundle : itemBundleList) { - result.add(MediaItem2.fromBundle(bundle)); - } - } - browser.onGetChildrenDone(parentId, page, pageSize, result, extras); - } - - @Override - public void onSearchResultChanged(String query, int itemCount, Bundle extras) - throws RuntimeException { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "onSearchResultChanged(): Ignoring empty query"); - return; - } - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - browser.onSearchResultChanged(query, itemCount, extras); - } - - @Override - public void onGetSearchResultDone(String query, int page, int pageSize, - List itemBundleList, Bundle extras) throws RuntimeException { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "onGetSearchResultDone(): Ignoring empty query"); - return; - } - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - - List result = null; - if (itemBundleList != null) { - result = new ArrayList<>(); - for (Bundle bundle : itemBundleList) { - result.add(MediaItem2.fromBundle(bundle)); - } - } - browser.onGetSearchResultDone(query, page, pageSize, result, extras); - } - - @Override - public void onChildrenChanged(String parentId, int itemCount, Bundle extras) { - if (parentId == null) { - Log.w(TAG, "onChildrenChanged(): Ignoring null parentId"); - return; - } - final MediaBrowser2Impl browser; - try { - browser = getBrowser(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (browser == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - browser.onChildrenChanged(parentId, itemCount, extras); - } } diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java deleted file mode 100644 index cf34cd4a09..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.app.PendingIntent; -import android.content.Context; -import android.media.MediaLibraryService2; -import android.media.MediaLibraryService2.LibraryRoot; -import android.media.MediaLibraryService2.MediaLibrarySession; -import android.media.MediaLibraryService2.MediaLibrarySession.Builder; -import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback; -import android.media.MediaPlayerBase; -import android.media.MediaPlaylistAgent; -import android.media.MediaSession2; -import android.media.MediaSession2.ControllerInfo; -import android.media.MediaSessionService2; -import android.media.SessionToken2; -import android.media.VolumeProvider2; -import android.media.update.MediaLibraryService2Provider; -import android.os.Bundle; -import android.text.TextUtils; - -import com.android.media.MediaSession2Impl.BuilderBaseImpl; - -import java.util.concurrent.Executor; - -public class MediaLibraryService2Impl extends MediaSessionService2Impl implements - MediaLibraryService2Provider { - private final MediaSessionService2 mInstance; - private MediaLibrarySession mLibrarySession; - - public MediaLibraryService2Impl(MediaLibraryService2 instance) { - super(instance); - mInstance = instance; - } - - @Override - public void onCreate_impl() { - super.onCreate_impl(); - - // Effectively final - MediaSession2 session = getSession(); - if (!(session instanceof MediaLibrarySession)) { - throw new RuntimeException("Expected MediaLibrarySession, but returned MediaSession2"); - } - mLibrarySession = (MediaLibrarySession) getSession(); - } - - @Override - int getSessionType() { - return SessionToken2.TYPE_LIBRARY_SERVICE; - } - - public static class MediaLibrarySessionImpl extends MediaSession2Impl - implements MediaLibrarySessionProvider { - public MediaLibrarySessionImpl(Context context, - MediaPlayerBase player, String id, MediaPlaylistAgent playlistAgent, - VolumeProvider2 volumeProvider, - PendingIntent sessionActivity, Executor callbackExecutor, - MediaLibrarySessionCallback callback) { - super(context, player, id, playlistAgent, volumeProvider, sessionActivity, - callbackExecutor, callback); - // Don't put any extra initialization here. Here's the reason. - // System service will recognize this session inside of the super constructor and would - // connect to this session assuming that initialization is finished. However, if any - // initialization logic is here, calls from the server would fail. - // see: MediaSession2Stub#connect() - } - - @Override - MediaLibrarySession createInstance() { - return new MediaLibrarySession(this); - } - - @Override - MediaLibrarySession getInstance() { - return (MediaLibrarySession) super.getInstance(); - } - - @Override - MediaLibrarySessionCallback getCallback() { - return (MediaLibrarySessionCallback) super.getCallback(); - } - - @Override - public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, - int itemCount, Bundle extras) { - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (parentId == null) { - throw new IllegalArgumentException("parentId shouldn't be null"); - } - getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, itemCount, - extras); - } - - @Override - public void notifyChildrenChanged_impl(String parentId, int itemCount, Bundle extras) { - if (parentId == null) { - throw new IllegalArgumentException("parentId shouldn't be null"); - } - getSessionStub().notifyChildrenChangedNotLocked(parentId, itemCount, extras); - } - - @Override - public void notifySearchResultChanged_impl(ControllerInfo controller, String query, - int itemCount, Bundle extras) { - ensureCallingThread(); - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query shouldn't be empty"); - } - getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras); - } - } - - public static class BuilderImpl - extends BuilderBaseImpl { - public BuilderImpl(MediaLibraryService2 service, Builder instance, - Executor callbackExecutor, MediaLibrarySessionCallback callback) { - super(service); - setSessionCallback_impl(callbackExecutor, callback); - } - - @Override - public MediaLibrarySession build_impl() { - return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mPlaylistAgent, - mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance(); - } - } - - public static final class LibraryRootImpl implements LibraryRootProvider { - private final LibraryRoot mInstance; - private final String mRootId; - private final Bundle mExtras; - - public LibraryRootImpl(LibraryRoot instance, String rootId, Bundle extras) { - if (rootId == null) { - throw new IllegalArgumentException("rootId shouldn't be null."); - } - mInstance = instance; - mRootId = rootId; - mExtras = extras; - } - - @Override - public String getRootId_impl() { - return mRootId; - } - - @Override - public Bundle getExtras_impl() { - return mExtras; - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java index 72ecf546eb..cd9edb8421 100644 --- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java @@ -35,7 +35,6 @@ import android.media.DataSourceDesc; import android.media.MediaController2; import android.media.MediaController2.PlaybackInfo; import android.media.MediaItem2; -import android.media.MediaLibraryService2; import android.media.MediaMetadata2; import android.media.MediaPlayerBase; import android.media.MediaPlayerBase.PlayerEventCallback; @@ -48,7 +47,6 @@ import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSession2.OnDataSourceMissingHelper; import android.media.MediaSession2.SessionCallback; -import android.media.MediaSessionService2; import android.media.SessionCommand2; import android.media.SessionCommandGroup2; import android.media.SessionToken2; @@ -153,21 +151,8 @@ public class MediaSession2Impl implements MediaSession2Provider { mPlaylistEventCallback = new MyPlaylistEventCallback(this); // Infer type from the id and package name. - String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id); - String sessionService = getServiceName(context, MediaSessionService2.SERVICE_INTERFACE, id); - if (sessionService != null && libraryService != null) { - throw new IllegalArgumentException("Ambiguous session type. Multiple" - + " session services define the same id=" + id); - } else if (libraryService != null) { - mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_LIBRARY_SERVICE, - mContext.getPackageName(), libraryService, id, mSessionStub).getInstance(); - } else if (sessionService != null) { - mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION_SERVICE, - mContext.getPackageName(), sessionService, id, mSessionStub).getInstance(); - } else { - mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION, - mContext.getPackageName(), null, id, mSessionStub).getInstance(); - } + mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION, + mContext.getPackageName(), null, id, mSessionStub).getInstance(); updatePlayer(player, playlistAgent, volumeProvider); diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java index 11ccd9fbf8..53a5986d2e 100644 --- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java @@ -20,7 +20,6 @@ import android.app.PendingIntent; import android.content.Context; import android.media.MediaController2; import android.media.MediaItem2; -import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaMetadata2; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.ControllerInfo; @@ -43,7 +42,6 @@ import android.util.SparseArray; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; -import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl; import com.android.media.MediaSession2Impl.CommandButtonImpl; import com.android.media.MediaSession2Impl.CommandGroupImpl; import com.android.media.MediaSession2Impl.ControllerInfoImpl; @@ -123,14 +121,6 @@ public class MediaSession2Stub extends IMediaSession2.Stub { return session; } - private MediaLibrarySessionImpl getLibrarySession() throws IllegalStateException { - final MediaSession2Impl session = getSession(); - if (!(session instanceof MediaLibrarySessionImpl)) { - throw new RuntimeException("Session isn't a library session"); - } - return (MediaLibrarySessionImpl) session; - } - // Get controller if the command from caller to session is able to be handled. private ControllerInfo getControllerIfAble(IMediaController2 caller) { synchronized (mLock) { @@ -255,24 +245,6 @@ public class MediaSession2Stub extends IMediaSession2.Stub { }); } - private void onBrowserCommand(@NonNull IMediaController2 caller, - @NonNull LibrarySessionRunnable runnable) { - final MediaLibrarySessionImpl session = getLibrarySession(); - // TODO(jaewan): Consider command code - final ControllerInfo controller = getControllerIfAble(caller); - if (session == null || controller == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - // TODO(jaewan): Consider command code - if (getControllerIfAble(caller) == null) { - return; - } - runnable.run(session, controller); - }); - } - - private void notifyAll(int commandCode, @NonNull NotifyRunnable runnable) { List controllers = getControllers(); for (int i = 0; i < controllers.size(); i++) { @@ -750,161 +722,6 @@ public class MediaSession2Stub extends IMediaSession2.Stub { }); } - ////////////////////////////////////////////////////////////////////////////////////////////// - // AIDL methods for LibrarySession overrides - ////////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void getLibraryRoot(final IMediaController2 caller, final Bundle rootHints) - throws RuntimeException { - onBrowserCommand(caller, (session, controller) -> { - final LibraryRoot root = session.getCallback().onGetLibraryRoot(session.getInstance(), - controller, rootHints); - notify(controller, (unused, iController) -> { - iController.onGetLibraryRootDone(rootHints, - root == null ? null : root.getRootId(), - root == null ? null : root.getExtras()); - }); - }); - } - - @Override - public void getItem(final IMediaController2 caller, final String mediaId) - throws RuntimeException { - onBrowserCommand(caller, (session, controller) -> { - if (mediaId == null) { - if (DEBUG) { - Log.d(TAG, "mediaId shouldn't be null"); - } - return; - } - final MediaItem2 result = session.getCallback().onGetItem(session.getInstance(), - controller, mediaId); - notify(controller, (unused, iController) -> { - iController.onGetItemDone(mediaId, result == null ? null : result.toBundle()); - }); - }); - } - - @Override - public void getChildren(final IMediaController2 caller, final String parentId, - final int page, final int pageSize, final Bundle extras) throws RuntimeException { - onBrowserCommand(caller, (session, controller) -> { - if (parentId == null) { - if (DEBUG) { - Log.d(TAG, "parentId shouldn't be null"); - } - return; - } - if (page < 1 || pageSize < 1) { - if (DEBUG) { - Log.d(TAG, "Neither page nor pageSize should be less than 1"); - } - return; - } - List result = session.getCallback().onGetChildren(session.getInstance(), - controller, parentId, page, pageSize, extras); - if (result != null && result.size() > pageSize) { - throw new IllegalArgumentException("onGetChildren() shouldn't return media items " - + "more than pageSize. result.size()=" + result.size() + " pageSize=" - + pageSize); - } - final List bundleList; - if (result != null) { - bundleList = new ArrayList<>(); - for (MediaItem2 item : result) { - bundleList.add(item == null ? null : item.toBundle()); - } - } else { - bundleList = null; - } - notify(controller, (unused, iController) -> { - iController.onGetChildrenDone(parentId, page, pageSize, bundleList, extras); - }); - }); - } - - @Override - public void search(IMediaController2 caller, String query, Bundle extras) { - onBrowserCommand(caller, (session, controller) -> { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "search(): Ignoring empty query from " + controller); - return; - } - session.getCallback().onSearch(session.getInstance(), controller, query, extras); - }); - } - - @Override - public void getSearchResult(final IMediaController2 caller, final String query, - final int page, final int pageSize, final Bundle extras) { - onBrowserCommand(caller, (session, controller) -> { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "getSearchResult(): Ignoring empty query from " + controller); - return; - } - if (page < 1 || pageSize < 1) { - Log.w(TAG, "getSearchResult(): Ignoring negative page / pageSize." - + " page=" + page + " pageSize=" + pageSize + " from " + controller); - return; - } - List result = session.getCallback().onGetSearchResult(session.getInstance(), - controller, query, page, pageSize, extras); - if (result != null && result.size() > pageSize) { - throw new IllegalArgumentException("onGetSearchResult() shouldn't return media " - + "items more than pageSize. result.size()=" + result.size() + " pageSize=" - + pageSize); - } - final List bundleList; - if (result != null) { - bundleList = new ArrayList<>(); - for (MediaItem2 item : result) { - bundleList.add(item == null ? null : item.toBundle()); - } - } else { - bundleList = null; - } - notify(controller, (unused, iController) -> { - iController.onGetSearchResultDone(query, page, pageSize, bundleList, extras); - }); - }); - } - - @Override - public void subscribe(final IMediaController2 caller, final String parentId, - final Bundle option) { - onBrowserCommand(caller, (session, controller) -> { - if (parentId == null) { - Log.w(TAG, "subscribe(): Ignoring null parentId from " + controller); - return; - } - session.getCallback().onSubscribe(session.getInstance(), - controller, parentId, option); - synchronized (mLock) { - Set subscription = mSubscriptions.get(controller); - if (subscription == null) { - subscription = new HashSet<>(); - mSubscriptions.put(controller, subscription); - } - subscription.add(parentId); - } - }); - } - - @Override - public void unsubscribe(final IMediaController2 caller, final String parentId) { - onBrowserCommand(caller, (session, controller) -> { - if (parentId == null) { - Log.w(TAG, "unsubscribe(): Ignoring null parentId from " + controller); - return; - } - session.getCallback().onUnsubscribe(session.getInstance(), controller, parentId); - synchronized (mLock) { - mSubscriptions.remove(controller); - } - }); - } - ////////////////////////////////////////////////////////////////////////////////////////////// // APIs for MediaSession2Impl ////////////////////////////////////////////////////////////////////////////////////////////// @@ -1054,44 +871,6 @@ public class MediaSession2Stub extends IMediaSession2.Stub { }); } - ////////////////////////////////////////////////////////////////////////////////////////////// - // APIs for MediaLibrarySessionImpl - ////////////////////////////////////////////////////////////////////////////////////////////// - - public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount, - Bundle extras) { - notify(controller, (unused, iController) -> { - iController.onSearchResultChanged(query, itemCount, extras); - }); - } - - public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId, - int itemCount, Bundle extras) { - notify(controller, (unused, iController) -> { - if (isSubscribed(controller, parentId)) { - iController.onChildrenChanged(parentId, itemCount, extras); - } - }); - } - - public void notifyChildrenChangedNotLocked(String parentId, int itemCount, Bundle extras) { - notifyAll((controller, iController) -> { - if (isSubscribed(controller, parentId)) { - iController.onChildrenChanged(parentId, itemCount, extras); - } - }); - } - - private boolean isSubscribed(ControllerInfo controller, String parentId) { - synchronized (mLock) { - Set subscriptions = mSubscriptions.get(controller); - if (subscriptions == null || !subscriptions.contains(parentId)) { - return false; - } - } - return true; - } - ////////////////////////////////////////////////////////////////////////////////////////////// // Misc ////////////////////////////////////////////////////////////////////////////////////////////// @@ -1101,11 +880,6 @@ public class MediaSession2Stub extends IMediaSession2.Stub { void run(final MediaSession2Impl session, final ControllerInfo controller); } - @FunctionalInterface - private interface LibrarySessionRunnable { - void run(final MediaLibrarySessionImpl session, final ControllerInfo controller); - } - @FunctionalInterface private interface NotifyRunnable { void run(final ControllerInfo controller, diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java deleted file mode 100644 index d975839f6d..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.content.Context.NOTIFICATION_SERVICE; - -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Intent; -import android.media.MediaPlayerBase; -import android.media.MediaPlayerBase.PlayerEventCallback; -import android.media.MediaSession2; -import android.media.MediaSessionService2; -import android.media.MediaSessionService2.MediaNotification; -import android.media.SessionToken2; -import android.media.SessionToken2.TokenType; -import android.media.update.MediaSessionService2Provider; -import android.os.IBinder; -import android.util.Log; - -import androidx.annotation.GuardedBy; - -// TODO(jaewan): Need a test for session service itself. -public class MediaSessionService2Impl implements MediaSessionService2Provider { - - private static final String TAG = "MPSessionService"; // to meet 23 char limit in Log tag - private static final boolean DEBUG = true; // TODO(jaewan): Change this. (b/74094611) - - private final MediaSessionService2 mInstance; - private final PlayerEventCallback mCallback = new SessionServiceEventCallback(); - - private final Object mLock = new Object(); - @GuardedBy("mLock") - private NotificationManager mNotificationManager; - @GuardedBy("mLock") - private Intent mStartSelfIntent; - - private boolean mIsRunningForeground; - private MediaSession2 mSession; - - public MediaSessionService2Impl(MediaSessionService2 instance) { - if (DEBUG) { - Log.d(TAG, "MediaSessionService2Impl(" + instance + ")"); - } - mInstance = instance; - } - - @Override - public MediaSession2 getSession_impl() { - return getSession(); - } - - MediaSession2 getSession() { - synchronized (mLock) { - return mSession; - } - } - - @Override - public MediaNotification onUpdateNotification_impl() { - // Provide default notification UI later. - return null; - } - - @Override - public void onCreate_impl() { - mNotificationManager = (NotificationManager) mInstance.getSystemService( - NOTIFICATION_SERVICE); - mStartSelfIntent = new Intent(mInstance, mInstance.getClass()); - - SessionToken2 token = new SessionToken2(mInstance, mInstance.getPackageName(), - mInstance.getClass().getName()); - if (token.getType() != getSessionType()) { - throw new RuntimeException("Expected session service, but was " + token.getType()); - } - mSession = mInstance.onCreateSession(token.getId()); - if (mSession == null || !token.getId().equals(mSession.getToken().getId())) { - throw new RuntimeException("Expected session with id " + token.getId() - + ", but got " + mSession); - } - // TODO(jaewan): Uncomment here. - // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor()); - } - - @TokenType int getSessionType() { - return SessionToken2.TYPE_SESSION_SERVICE; - } - - public IBinder onBind_impl(Intent intent) { - if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) { - return ((MediaSession2Impl) mSession.getProvider()).getSessionStub().asBinder(); - } - return null; - } - - private void updateNotification(int playerState) { - MediaNotification mediaNotification = mInstance.onUpdateNotification(); - if (mediaNotification == null) { - return; - } - switch(playerState) { - case MediaPlayerBase.PLAYER_STATE_PLAYING: - if (!mIsRunningForeground) { - mIsRunningForeground = true; - mInstance.startForegroundService(mStartSelfIntent); - mInstance.startForeground(mediaNotification.getNotificationId(), - mediaNotification.getNotification()); - return; - } - break; - case MediaPlayerBase.PLAYER_STATE_IDLE: - case MediaPlayerBase.PLAYER_STATE_ERROR: - if (mIsRunningForeground) { - mIsRunningForeground = false; - mInstance.stopForeground(true); - return; - } - break; - } - mNotificationManager.notify(mediaNotification.getNotificationId(), - mediaNotification.getNotification()); - } - - private class SessionServiceEventCallback extends PlayerEventCallback { - @Override - public void onPlayerStateChanged(MediaPlayerBase player, int state) { - // TODO: Implement this - return; - } - } - - public static class MediaNotificationImpl implements MediaNotificationProvider { - private int mNotificationId; - private Notification mNotification; - - public MediaNotificationImpl(MediaNotification instance, int notificationId, - Notification notification) { - if (notification == null) { - throw new IllegalArgumentException("notification shouldn't be null"); - } - mNotificationId = notificationId; - mNotification = notification; - } - - @Override - public int getNotificationId_impl() { - return mNotificationId; - } - - @Override - public Notification getNotification_impl() { - return mNotification; - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java index f792712ff6..396ebbb382 100644 --- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java +++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java @@ -25,8 +25,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; -import android.media.MediaLibraryService2; -import android.media.MediaSessionService2; import android.media.SessionToken2; import android.media.SessionToken2.TokenType; import android.media.update.SessionToken2Provider; @@ -75,24 +73,8 @@ public class SessionToken2Impl implements SessionToken2Provider { } } mUid = uid; - - // Infer id and type from package name and service name - // TODO(jaewan): Handle multi-user. - String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE, - packageName, serviceName); - if (id != null) { - mId = id; - mType = TYPE_LIBRARY_SERVICE; - } else { - // retry with session service - mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE, - packageName, serviceName); - mType = TYPE_SESSION_SERVICE; - } - if (mId == null) { - throw new IllegalArgumentException("service " + serviceName + " doesn't implement" - + " session service nor library service. Use service's full name."); - } + mId = null; + mType = -1; mPackageName = packageName; mServiceName = serviceName; mSessionBinder = null; @@ -110,38 +92,13 @@ public class SessionToken2Impl implements SessionToken2Provider { mInstance = new SessionToken2(this); } - private static String getSessionIdFromService(PackageManager manager, String serviceInterface, - String packageName, String serviceName) { - Intent serviceIntent = new Intent(serviceInterface); - serviceIntent.setPackage(packageName); - // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE. - // We cannot use resolveService with intent specified class name, because resolveService - // ignores actions if Intent.setClassName() is specified. - List list = manager.queryIntentServices( - serviceIntent, PackageManager.GET_META_DATA); - if (list != null) { - for (int i = 0; i < list.size(); i++) { - ResolveInfo resolveInfo = list.get(i); - if (resolveInfo == null || resolveInfo.serviceInfo == null) { - continue; - } - if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) { - return getSessionId(resolveInfo); - } - } - } - return null; - } - public static String getSessionId(ResolveInfo resolveInfo) { if (resolveInfo == null || resolveInfo.serviceInfo == null) { return null; } else if (resolveInfo.serviceInfo.metaData == null) { return ""; - } else { - return resolveInfo.serviceInfo.metaData.getString( - MediaSessionService2.SERVICE_META_DATA, ""); } + return null; } public SessionToken2 getInstance() { diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java index b8d4908000..663b813cea 100644 --- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java +++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java @@ -19,38 +19,26 @@ package com.android.media.update; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.media.MediaBrowser2; -import android.media.MediaBrowser2.BrowserCallback; import android.media.MediaController2; import android.media.MediaController2.ControllerCallback; import android.media.MediaItem2; -import android.media.MediaLibraryService2; -import android.media.MediaLibraryService2.LibraryRoot; -import android.media.MediaLibraryService2.MediaLibrarySession; -import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback; import android.media.MediaMetadata2; import android.media.MediaPlaylistAgent; import android.media.MediaSession2; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSession2.SessionCallback; -import android.media.MediaSessionService2; -import android.media.MediaSessionService2.MediaNotification; import android.media.Rating2; import android.media.SessionCommand2; import android.media.SessionCommandGroup2; import android.media.SessionToken2; import android.media.VolumeProvider2; -import android.media.update.MediaBrowser2Provider; import android.media.update.MediaController2Provider; import android.media.update.MediaItem2Provider; -import android.media.update.MediaLibraryService2Provider.LibraryRootProvider; import android.media.update.MediaMetadata2Provider; import android.media.update.MediaPlaylistAgentProvider; import android.media.update.MediaSession2Provider; import android.media.update.MediaSession2Provider.BuilderBaseProvider; import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider; -import android.media.update.MediaSessionService2Provider; -import android.media.update.MediaSessionService2Provider.MediaNotificationProvider; import android.media.update.SessionToken2Provider; import android.media.update.StaticProvider; import android.media.update.VolumeProvider2Provider; @@ -58,15 +46,11 @@ import android.os.Bundle; import android.os.IInterface; import com.android.media.IMediaController2; -import com.android.media.MediaBrowser2Impl; import com.android.media.MediaController2Impl; import com.android.media.MediaItem2Impl; -import com.android.media.MediaLibraryService2Impl; -import com.android.media.MediaLibraryService2Impl.LibraryRootImpl; import com.android.media.MediaMetadata2Impl; import com.android.media.MediaPlaylistAgentImpl; import com.android.media.MediaSession2Impl; -import com.android.media.MediaSessionService2Impl; import com.android.media.Rating2Impl; import com.android.media.SessionToken2Impl; import com.android.media.VolumeProvider2Impl; @@ -88,12 +72,6 @@ public final class ApiFactory implements StaticProvider { return new MediaController2Impl(context, instance, token, executor, callback); } - @Override - public MediaBrowser2Provider createMediaBrowser2(Context context, MediaBrowser2 instance, - SessionToken2 token, Executor executor, BrowserCallback callback) { - return new MediaBrowser2Impl(context, instance, token, executor, callback); - } - @Override public MediaSession2Provider.CommandProvider createMediaSession2Command( SessionCommand2 instance, int commandCode, String action, Bundle extra) { @@ -139,38 +117,6 @@ public final class ApiFactory implements StaticProvider { return new MediaSession2Impl.BuilderImpl(context, instance); } - @Override - public MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance) { - return new MediaSessionService2Impl(instance); - } - - @Override - public MediaNotificationProvider createMediaSessionService2MediaNotification( - MediaNotification instance, int notificationId, Notification notification) { - return new MediaSessionService2Impl.MediaNotificationImpl( - instance, notificationId, notification); - } - - @Override - public MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance) { - return new MediaLibraryService2Impl(instance); - } - - @Override - public BuilderBaseProvider - createMediaLibraryService2Builder(MediaLibraryService2 service, - MediaLibrarySession.Builder instance, Executor callbackExecutor, - MediaLibrarySessionCallback callback) { - return new MediaLibraryService2Impl.BuilderImpl(service, instance, callbackExecutor, - callback); - } - - @Override - public LibraryRootProvider createMediaLibraryService2LibraryRoot( - LibraryRoot instance, String rootId, Bundle extras) { - return new LibraryRootImpl(instance, rootId, extras); - } - @Override public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance, String packageName, String serviceName, int uid) { -- GitLab From 110342bd776c2a96a8a7a168a34dbca91843ee16 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Mon, 19 Nov 2018 11:47:46 -0800 Subject: [PATCH 0483/1530] Camera: check stream state in useHalBufManager mode Test: Surface abandon CTS test fixed. Bug: 109829698 Change-Id: Ic2684098e129702f0ad8d8ce0af04a5195f29881 --- services/camera/libcameraservice/device3/Camera3Device.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index d8fbaf53f9..732164d482 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -5398,6 +5398,10 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { } if (mUseHalBufManager) { + if (outputStream->isAbandoned()) { + ALOGE("%s: stream %d is abandoned.", __FUNCTION__, streamId); + return TIMED_OUT; + } // HAL will request buffer through requestStreamBuffer API camera3_stream_buffer_t& buffer = outputBuffers->editItemAt(j); buffer.stream = outputStream->asHalStream(); -- GitLab From b70ddd45913cb9eb96cc9d1033df590c3741e05d Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 19 Nov 2018 12:33:34 -0800 Subject: [PATCH 0484/1530] Fix memory leak Bug: 111407253 Test: manual Change-Id: I3718f1c5b8f1699b4d68ae35e3d66b912cf6cf18 --- media/libstagefright/MediaTrack.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 9fbb20e85d..821605d207 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -110,6 +110,8 @@ MediaTrackCUnwrapperV2::MediaTrackCUnwrapperV2(CMediaTrackV2 *cmediatrack2) { } MediaTrackCUnwrapperV2::~MediaTrackCUnwrapperV2() { + wrapper->free(wrapper->data); + free(wrapper); } status_t MediaTrackCUnwrapperV2::start() { -- GitLab From b355f7cfd43d193057e547ff5e512fb14eb19b7b Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Mon, 19 Nov 2018 14:49:10 -0800 Subject: [PATCH 0485/1530] Assure pending invalidations are processed When an invalidation is pending, assure buffer status messages are processed even though there is no active thread. Per process invalidator thread periodically processes buffer status messages for pending invalidations. Bug: 112203066 Change-Id: If6bdebe7cd1b9af1ac3eadf57aabe27f57996c9f --- media/bufferpool/2.0/AccessorImpl.cpp | 32 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp index 84fcca2458..01653145f3 100644 --- a/media/bufferpool/2.0/AccessorImpl.cpp +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -308,7 +308,11 @@ void Accessor::Impl::BufferPool::Invalidation::onAck( ConnectionId conId, uint32_t msgId) { auto it = mAcks.find(conId); - if (it == mAcks.end() || isMessageLater(msgId, it->second)) { + if (it == mAcks.end()) { + ALOGW("ACK from inconsistent connection! %lld", (long long)conId); + return; + } + if (isMessageLater(msgId, it->second)) { mAcks[conId] = msgId; } } @@ -327,7 +331,6 @@ void Accessor::Impl::BufferPool::Invalidation::onBufferInvalidated( } } channel.postInvalidation(msgId, it->mFrom, it->mTo); - sInvalidator->addAccessor(mId, it->mImpl); it = mPendings.erase(it); continue; } @@ -342,24 +345,24 @@ void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest( size_t left, BufferInvalidationChannel &channel, const std::shared_ptr &impl) { - if (left == 0) { uint32_t msgId = 0; - if (needsAck) { + if (needsAck) { + msgId = ++mInvalidationId; + if (msgId == 0) { + // wrap happens msgId = ++mInvalidationId; - if (msgId == 0) { - // wrap happens - msgId = ++mInvalidationId; - } } - ALOGV("bufferpool invalidation requested and queued"); + } + ALOGV("bufferpool invalidation requested and queued"); + if (left == 0) { channel.postInvalidation(msgId, from, to); - sInvalidator->addAccessor(mId, impl); } else { // TODO: sending hint message? ALOGV("bufferpool invalidation requested and pending"); Pending pending(needsAck, from, to, left, impl); mPendings.push_back(pending); } + sInvalidator->addAccessor(mId, impl); } void Accessor::Impl::BufferPool::Invalidation::onHandleAck() { @@ -373,6 +376,9 @@ void Accessor::Impl::BufferPool::Invalidation::onHandleAck() { (long long)it->first, it->second, mInvalidationId); Return transResult = observer->onMessage(it->first, mInvalidationId); (void) transResult; + // N.B: ignore possibility of onMessage oneway call being + // lost. + it->second = mInvalidationId; } else { ALOGV("bufferpool observer died %lld", (long long)it->first); deads.insert(it->first); @@ -385,8 +391,10 @@ void Accessor::Impl::BufferPool::Invalidation::onHandleAck() { } } } - // All invalidation Ids are synced. - sInvalidator->delAccessor(mId); + if (mPendings.size() == 0) { + // All invalidation Ids are synced and no more pending invalidations. + sInvalidator->delAccessor(mId); + } } bool Accessor::Impl::BufferPool::handleOwnBuffer( -- GitLab From 84cfbfd3897f9b7fc17ad6355e822faa5a794901 Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Fri, 16 Nov 2018 15:46:36 +0900 Subject: [PATCH 0486/1530] Initial checkin for moving media Moving media for mainline module to see feasibility Bug: 119539695 Test: build Change-Id: If4dc7bf11d075e682757421db5951042ae666588 --- packages/MediaComponents/apex/Android.bp | 39 + .../media/IRemoteVolumeController.aidl | 32 + .../android/media/ISessionTokensListener.aidl | 27 + .../java/android/media/MediaDescription.aidl | 18 + .../java/android/media/MediaDescription.java | 382 ++++ .../java/android/media/MediaMetadata.aidl | 18 + .../java/android/media/MediaMetadata.java | 941 ++++++++++ .../apex/java/android/media/Rating.aidl | 19 + .../apex/java/android/media/Rating.java | 308 ++++ .../java/android/media/VolumeProvider.java | 161 ++ .../android/media/browse/MediaBrowser.aidl | 18 + .../android/media/browse/MediaBrowser.java | 1171 +++++++++++++ .../media/browse/MediaBrowserUtils.java | 72 + .../session/IActiveSessionsListener.aidl | 26 + .../java/android/media/session/ICallback.aidl | 35 + .../media/session/IOnMediaKeyListener.aidl | 28 + .../IOnVolumeKeyLongPressListener.aidl | 27 + .../java/android/media/session/ISession.aidl | 53 + .../media/session/ISessionCallback.aidl | 71 + .../media/session/ISessionController.aidl | 88 + .../session/ISessionControllerCallback.aidl | 39 + .../media/session/ISessionManager.aidl | 66 + .../media/session/MediaController.java | 1187 +++++++++++++ .../android/media/session/MediaSession.aidl | 19 + .../android/media/session/MediaSession.java | 1554 +++++++++++++++++ .../media/session/ParcelableVolumeInfo.aidl | 18 + .../media/session/ParcelableVolumeInfo.java | 80 + .../android/media/session/PlaybackState.aidl | 18 + .../android/media/session/PlaybackState.java | 1079 ++++++++++++ .../service/media/IMediaBrowserService.aidl | 27 + .../media/IMediaBrowserServiceCallbacks.aidl | 27 + .../service/media/MediaBrowserService.java | 864 +++++++++ 32 files changed, 8512 insertions(+) create mode 100644 packages/MediaComponents/apex/Android.bp create mode 100644 packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/MediaDescription.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/MediaDescription.java create mode 100644 packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/MediaMetadata.java create mode 100644 packages/MediaComponents/apex/java/android/media/Rating.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/Rating.java create mode 100644 packages/MediaComponents/apex/java/android/media/VolumeProvider.java create mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java create mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java create mode 100644 packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ICallback.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ISession.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaController.java create mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaSession.java create mode 100644 packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java create mode 100644 packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/session/PlaybackState.java create mode 100644 packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl create mode 100644 packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl create mode 100644 packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp new file mode 100644 index 0000000000..363a416cba --- /dev/null +++ b/packages/MediaComponents/apex/Android.bp @@ -0,0 +1,39 @@ +filegroup { + name: "media_aidl", + srcs: [ + "java/android/media/**/*.aidl", + "java/android/service/**/*.aidl", + ], + exclude_srcs: [ + // Exclude these aidls to avoid errors such as + // "Refusing to generate code with unstructured parcelables." + "java/android/media/MediaDescription.aidl", + "java/android/media/MediaMetadata.aidl", + "java/android/media/Rating.aidl", + "java/android/media/browse/MediaBrowser.aidl", + "java/android/media/session/MediaSession.aidl", + "java/android/media/session/ParcelableVolumeInfo.aidl", + "java/android/media/session/PlaybackState.aidl", + ], +} + +java_library { + name: "media", + installable: true, + sdk_version: "current", + srcs: [ + "java/android/media/**/*.java", + "java/android/service/**/*.java", + ":media_aidl", + ":framework-media-annotation-srcs", + ], + aidl: { + local_include_dirs: ["java"], + include_dirs: [ + "frameworks/base/core/java", + // for android.graphics.Bitmap + // from IMediaBrowserServiceCallback + "frameworks/base/graphics/java", + ], + }, +} diff --git a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl new file mode 100644 index 0000000000..e4a4a42380 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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.session.ISessionController; + +/** + * AIDL for the MediaSessionService to report interesting events on remote playback + * to a volume control dialog. See also IVolumeController for the AudioService half. + * TODO add in better support for multiple remote sessions. + * @hide + */ +oneway interface IRemoteVolumeController { + void remoteVolumeChanged(ISessionController session, int flags); + // sets the default session to use with the slider, replaces remoteSliderVisibility + // on IVolumeController + void updateRemoteController(ISessionController session); +} diff --git a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl new file mode 100644 index 0000000000..c83a19e64d --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.os.Bundle; + +/** + * Listens for changes to the list of session tokens. + * @hide + */ +oneway interface ISessionTokensListener { + void onSessionTokensChanged(in List tokens); +} diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl new file mode 100644 index 0000000000..6f934f75aa --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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; + +parcelable MediaDescription; diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java new file mode 100644 index 0000000000..e6aea99ef5 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.java @@ -0,0 +1,382 @@ +package android.media; + +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.media.browse.MediaBrowser; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A simple set of metadata for a media item suitable for display. This can be + * created using the Builder or retrieved from existing metadata using + * {@link MediaMetadata#getDescription()}. + */ +public class MediaDescription implements Parcelable { + /** + * A unique persistent id for the content or null. + */ + private final String mMediaId; + /** + * A primary title suitable for display or null. + */ + private final CharSequence mTitle; + /** + * A subtitle suitable for display or null. + */ + private final CharSequence mSubtitle; + /** + * A description suitable for display or null. + */ + private final CharSequence mDescription; + /** + * A bitmap icon suitable for display or null. + */ + private final Bitmap mIcon; + /** + * A Uri for an icon suitable for display or null. + */ + private final Uri mIconUri; + /** + * Extras for opaque use by apps/system. + */ + private final Bundle mExtras; + /** + * A Uri to identify this content. + */ + private final Uri mMediaUri; + + /** + * Used as a long extra field to indicate the bluetooth folder type of the media item as + * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for + * {@link MediaBrowser.MediaItem} with {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}. The value + * should be one of the following: + *
      + *
    • {@link #BT_FOLDER_TYPE_MIXED}
    • + *
    • {@link #BT_FOLDER_TYPE_TITLES}
    • + *
    • {@link #BT_FOLDER_TYPE_ALBUMS}
    • + *
    • {@link #BT_FOLDER_TYPE_ARTISTS}
    • + *
    • {@link #BT_FOLDER_TYPE_GENRES}
    • + *
    • {@link #BT_FOLDER_TYPE_PLAYLISTS}
    • + *
    • {@link #BT_FOLDER_TYPE_YEARS}
    • + *
    + * + * @see #getExtras() + */ + public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE"; + + /** + * The type of folder that is unknown or contains media elements of mixed types as specified in + * the section 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_MIXED = 0; + + /** + * The type of folder that contains media elements only as specified in the section 6.10.2.2 of + * the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_TITLES = 1; + + /** + * The type of folder that contains folders categorized by album as specified in the section + * 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_ALBUMS = 2; + + /** + * The type of folder that contains folders categorized by artist as specified in the section + * 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_ARTISTS = 3; + + /** + * The type of folder that contains folders categorized by genre as specified in the section + * 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_GENRES = 4; + + /** + * The type of folder that contains folders categorized by playlist as specified in the section + * 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_PLAYLISTS = 5; + + /** + * The type of folder that contains folders categorized by year as specified in the section + * 6.10.2.2 of the Bluetooth AVRCP 1.5. + */ + public static final long BT_FOLDER_TYPE_YEARS = 6; + + private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle, + CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) { + mMediaId = mediaId; + mTitle = title; + mSubtitle = subtitle; + mDescription = description; + mIcon = icon; + mIconUri = iconUri; + mExtras = extras; + mMediaUri = mediaUri; + } + + private MediaDescription(Parcel in) { + mMediaId = in.readString(); + mTitle = in.readCharSequence(); + mSubtitle = in.readCharSequence(); + mDescription = in.readCharSequence(); + mIcon = in.readParcelable(null); + mIconUri = in.readParcelable(null); + mExtras = in.readBundle(); + mMediaUri = in.readParcelable(null); + } + + /** + * Returns the media id or null. See + * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}. + */ + public @Nullable String getMediaId() { + return mMediaId; + } + + /** + * Returns a title suitable for display or null. + * + * @return A title or null. + */ + public @Nullable CharSequence getTitle() { + return mTitle; + } + + /** + * Returns a subtitle suitable for display or null. + * + * @return A subtitle or null. + */ + public @Nullable CharSequence getSubtitle() { + return mSubtitle; + } + + /** + * Returns a description suitable for display or null. + * + * @return A description or null. + */ + public @Nullable CharSequence getDescription() { + return mDescription; + } + + /** + * Returns a bitmap icon suitable for display or null. + * + * @return An icon or null. + */ + public @Nullable Bitmap getIconBitmap() { + return mIcon; + } + + /** + * Returns a Uri for an icon suitable for display or null. + * + * @return An icon uri or null. + */ + public @Nullable Uri getIconUri() { + return mIconUri; + } + + /** + * Returns any extras that were added to the description. + * + * @return A bundle of extras or null. + */ + public @Nullable Bundle getExtras() { + return mExtras; + } + + /** + * Returns a Uri representing this content or null. + * + * @return A media Uri or null. + */ + public @Nullable Uri getMediaUri() { + return mMediaUri; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mMediaId); + dest.writeCharSequence(mTitle); + dest.writeCharSequence(mSubtitle); + dest.writeCharSequence(mDescription); + dest.writeParcelable(mIcon, flags); + dest.writeParcelable(mIconUri, flags); + dest.writeBundle(mExtras); + dest.writeParcelable(mMediaUri, flags); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + + if (!(o instanceof MediaDescription)){ + return false; + } + + final MediaDescription d = (MediaDescription) o; + + if (!String.valueOf(mTitle).equals(String.valueOf(d.mTitle))) { + return false; + } + + if (!String.valueOf(mSubtitle).equals(String.valueOf(d.mSubtitle))) { + return false; + } + + if (!String.valueOf(mDescription).equals(String.valueOf(d.mDescription))) { + return false; + } + + return true; + } + + @Override + public String toString() { + return mTitle + ", " + mSubtitle + ", " + mDescription; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public MediaDescription createFromParcel(Parcel in) { + return new MediaDescription(in); + } + + @Override + public MediaDescription[] newArray(int size) { + return new MediaDescription[size]; + } + }; + + /** + * Builder for {@link MediaDescription} objects. + */ + public static class Builder { + private String mMediaId; + private CharSequence mTitle; + private CharSequence mSubtitle; + private CharSequence mDescription; + private Bitmap mIcon; + private Uri mIconUri; + private Bundle mExtras; + private Uri mMediaUri; + + /** + * Creates an initially empty builder. + */ + public Builder() { + } + + /** + * Sets the media id. + * + * @param mediaId The unique id for the item or null. + * @return this + */ + public Builder setMediaId(@Nullable String mediaId) { + mMediaId = mediaId; + return this; + } + + /** + * Sets the title. + * + * @param title A title suitable for display to the user or null. + * @return this + */ + public Builder setTitle(@Nullable CharSequence title) { + mTitle = title; + return this; + } + + /** + * Sets the subtitle. + * + * @param subtitle A subtitle suitable for display to the user or null. + * @return this + */ + public Builder setSubtitle(@Nullable CharSequence subtitle) { + mSubtitle = subtitle; + return this; + } + + /** + * Sets the description. + * + * @param description A description suitable for display to the user or + * null. + * @return this + */ + public Builder setDescription(@Nullable CharSequence description) { + mDescription = description; + return this; + } + + /** + * Sets the icon. + * + * @param icon A {@link Bitmap} icon suitable for display to the user or + * null. + * @return this + */ + public Builder setIconBitmap(@Nullable Bitmap icon) { + mIcon = icon; + return this; + } + + /** + * Sets the icon uri. + * + * @param iconUri A {@link Uri} for an icon suitable for display to the + * user or null. + * @return this + */ + public Builder setIconUri(@Nullable Uri iconUri) { + mIconUri = iconUri; + return this; + } + + /** + * Sets a bundle of extras. + * + * @param extras The extras to include with this description or null. + * @return this + */ + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Sets the media uri. + * + * @param mediaUri The content's {@link Uri} for the item or null. + * @return this + */ + public Builder setMediaUri(@Nullable Uri mediaUri) { + mMediaUri = mediaUri; + return this; + } + + public MediaDescription build() { + return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri, + mExtras, mMediaUri); + } + } +} diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl new file mode 100644 index 0000000000..66ee483041 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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; + +parcelable MediaMetadata; diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java new file mode 100644 index 0000000000..2721ad12b1 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java @@ -0,0 +1,941 @@ +/* + * Copyright (C) 2014 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.annotation.NonNull; +import android.annotation.StringDef; +import android.annotation.UnsupportedAppUsage; +import android.content.ContentResolver; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.media.browse.MediaBrowser; +import android.media.session.MediaController; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseArray; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Set; +import java.util.Objects; + +/** + * Contains metadata about an item, such as the title, artist, etc. + */ +public final class MediaMetadata implements Parcelable { + private static final String TAG = "MediaMetadata"; + + /** + * @hide + */ + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_TITLE, + METADATA_KEY_ARTIST, + METADATA_KEY_ALBUM, + METADATA_KEY_AUTHOR, + METADATA_KEY_WRITER, + METADATA_KEY_COMPOSER, + METADATA_KEY_COMPILATION, + METADATA_KEY_DATE, + METADATA_KEY_GENRE, + METADATA_KEY_ALBUM_ARTIST, + METADATA_KEY_ART_URI, + METADATA_KEY_ALBUM_ART_URI, + METADATA_KEY_DISPLAY_TITLE, + METADATA_KEY_DISPLAY_SUBTITLE, + METADATA_KEY_DISPLAY_DESCRIPTION, + METADATA_KEY_DISPLAY_ICON_URI, + METADATA_KEY_MEDIA_ID, + METADATA_KEY_MEDIA_URI, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TextKey {} + + /** + * @hide + */ + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_DURATION, + METADATA_KEY_YEAR, + METADATA_KEY_TRACK_NUMBER, + METADATA_KEY_NUM_TRACKS, + METADATA_KEY_DISC_NUMBER, + METADATA_KEY_BT_FOLDER_TYPE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LongKey {} + + /** + * @hide + */ + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_ART, + METADATA_KEY_ALBUM_ART, + METADATA_KEY_DISPLAY_ICON, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BitmapKey {} + + /** + * @hide + */ + @StringDef(prefix = { "METADATA_KEY_" }, value = { + METADATA_KEY_USER_RATING, + METADATA_KEY_RATING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RatingKey {} + + /** + * The title of the media. + */ + public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; + + /** + * The artist of the media. + */ + public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; + + /** + * The duration of the media in ms. A negative duration indicates that the + * duration is unknown (or infinite). + */ + public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; + + /** + * The album title for the media. + */ + public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; + + /** + * The author of the media. + */ + public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; + + /** + * The writer of the media. + */ + public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; + + /** + * The composer of the media. + */ + public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; + + /** + * The compilation status of the media. + */ + public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; + + /** + * The date the media was created or published. The format is unspecified + * but RFC 3339 is recommended. + */ + public static final String METADATA_KEY_DATE = "android.media.metadata.DATE"; + + /** + * The year the media was created or published as a long. + */ + public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; + + /** + * The genre of the media. + */ + public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; + + /** + * The track number for the media. + */ + public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; + + /** + * The number of tracks in the media's original source. + */ + public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; + + /** + * The disc number for the media's original source. + */ + public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; + + /** + * The artist for the album of the media's original source. + */ + public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; + + /** + * The artwork for the media as a {@link Bitmap}. + *

    + * The artwork should be relatively small and may be scaled down by the + * system if it is too large. For higher resolution artwork + * {@link #METADATA_KEY_ART_URI} should be used instead. + */ + public static final String METADATA_KEY_ART = "android.media.metadata.ART"; + + /** + * The artwork for the media as a Uri formatted String. The artwork can be + * loaded using a combination of {@link ContentResolver#openInputStream} and + * {@link BitmapFactory#decodeStream}. + *

    + * For the best results, Uris should use the content:// style and support + * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through + * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. + */ + public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; + + /** + * The artwork for the album of the media's original source as a + * {@link Bitmap}. + *

    + * The artwork should be relatively small and may be scaled down by the + * system if it is too large. For higher resolution artwork + * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead. + */ + public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; + + /** + * The artwork for the album of the media's original source as a Uri + * formatted String. The artwork can be loaded using a combination of + * {@link ContentResolver#openInputStream} and + * {@link BitmapFactory#decodeStream}. + *

    + * For the best results, Uris should use the content:// style and support + * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through + * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. + */ + public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; + + /** + * The user's rating for the media. + * + * @see Rating + */ + public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; + + /** + * The overall rating for the media. + * + * @see Rating + */ + public static final String METADATA_KEY_RATING = "android.media.metadata.RATING"; + + /** + * A title that is suitable for display to the user. This will generally be + * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats. + * When displaying media described by this metadata this should be preferred + * if present. + */ + public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"; + + /** + * A subtitle that is suitable for display to the user. When displaying a + * second line for media described by this metadata this should be preferred + * to other fields if present. + */ + public static final String METADATA_KEY_DISPLAY_SUBTITLE + = "android.media.metadata.DISPLAY_SUBTITLE"; + + /** + * A description that is suitable for display to the user. When displaying + * more information for media described by this metadata this should be + * preferred to other fields if present. + */ + public static final String METADATA_KEY_DISPLAY_DESCRIPTION + = "android.media.metadata.DISPLAY_DESCRIPTION"; + + /** + * An icon or thumbnail that is suitable for display to the user. When + * displaying an icon for media described by this metadata this should be + * preferred to other fields if present. This must be a {@link Bitmap}. + *

    + * The icon should be relatively small and may be scaled down by the system + * if it is too large. For higher resolution artwork + * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead. + */ + public static final String METADATA_KEY_DISPLAY_ICON + = "android.media.metadata.DISPLAY_ICON"; + + /** + * A Uri formatted String for an icon or thumbnail that is suitable for + * display to the user. When displaying more information for media described + * by this metadata the display description should be preferred to other + * fields when present. The icon can be loaded using a combination of + * {@link ContentResolver#openInputStream} and + * {@link BitmapFactory#decodeStream}. + *

    + * For the best results, Uris should use the content:// style and support + * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through + * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. + */ + public static final String METADATA_KEY_DISPLAY_ICON_URI + = "android.media.metadata.DISPLAY_ICON_URI"; + + /** + * A String key for identifying the content. This value is specific to the + * service providing the content. If used, this should be a persistent + * unique key for the underlying content. It may be used with + * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)} + * to initiate playback when provided by a {@link MediaBrowser} connected to + * the same app. + */ + public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"; + + /** + * A Uri formatted String representing the content. This value is specific to the + * service providing the content. It may be used with + * {@link MediaController.TransportControls#playFromUri(Uri, Bundle)} + * to initiate playback when provided by a {@link MediaBrowser} connected to + * the same app. + */ + public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI"; + + /** + * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth + * AVRCP 1.5. It should be one of the following: + *

      + *
    • {@link MediaDescription#BT_FOLDER_TYPE_MIXED}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_TITLES}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_ALBUMS}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_ARTISTS}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_GENRES}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_PLAYLISTS}
    • + *
    • {@link MediaDescription#BT_FOLDER_TYPE_YEARS}
    • + *
    + */ + public static final String METADATA_KEY_BT_FOLDER_TYPE + = "android.media.metadata.BT_FOLDER_TYPE"; + + private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = { + METADATA_KEY_TITLE, + METADATA_KEY_ARTIST, + METADATA_KEY_ALBUM, + METADATA_KEY_ALBUM_ARTIST, + METADATA_KEY_WRITER, + METADATA_KEY_AUTHOR, + METADATA_KEY_COMPOSER + }; + + private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = { + METADATA_KEY_DISPLAY_ICON, + METADATA_KEY_ART, + METADATA_KEY_ALBUM_ART + }; + + private static final @TextKey String[] PREFERRED_URI_ORDER = { + METADATA_KEY_DISPLAY_ICON_URI, + METADATA_KEY_ART_URI, + METADATA_KEY_ALBUM_ART_URI + }; + + private static final int METADATA_TYPE_INVALID = -1; + private static final int METADATA_TYPE_LONG = 0; + private static final int METADATA_TYPE_TEXT = 1; + private static final int METADATA_TYPE_BITMAP = 2; + private static final int METADATA_TYPE_RATING = 3; + private static final ArrayMap METADATA_KEYS_TYPE; + + static { + METADATA_KEYS_TYPE = new ArrayMap(); + METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING); + METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG); + METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT); + } + + private static final SparseArray EDITOR_KEY_MAPPING; + + static { + EDITOR_KEY_MAPPING = new SparseArray(); + EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART); + EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING); + EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, + METADATA_KEY_ALBUM_ARTIST); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, + METADATA_KEY_TRACK_NUMBER); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION, + METADATA_KEY_COMPILATION); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, + METADATA_KEY_DISC_NUMBER); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, + METADATA_KEY_NUM_TRACKS); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER); + EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR); + } + + private final Bundle mBundle; + private MediaDescription mDescription; + + private MediaMetadata(Bundle bundle) { + mBundle = new Bundle(bundle); + } + + private MediaMetadata(Parcel in) { + mBundle = Bundle.setDefusable(in.readBundle(), true); + } + + /** + * Returns true if the given key is contained in the metadata + * + * @param key a String key + * @return true if the key exists in this metadata, false otherwise + */ + public boolean containsKey(String key) { + return mBundle.containsKey(key); + } + + /** + * Returns the value associated with the given key, or null if no mapping of + * the desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key The key the value is stored under + * @return a CharSequence value, or null + */ + public CharSequence getText(@TextKey String key) { + return mBundle.getCharSequence(key); + } + + /** + * Returns the text value associated with the given key as a String, or null + * if no mapping of the desired type exists for the given key or a null + * value is explicitly associated with the key. This is equivalent to + * calling {@link #getText getText().toString()} if the value is not null. + * + * @param key The key the value is stored under + * @return a String value, or null + */ + public String getString(@TextKey String key) { + CharSequence text = getText(key); + if (text != null) { + return text.toString(); + } + return null; + } + + /** + * Returns the value associated with the given key, or 0L if no long exists + * for the given key. + * + * @param key The key the value is stored under + * @return a long value + */ + public long getLong(@LongKey String key) { + return mBundle.getLong(key, 0); + } + + /** + * Returns a {@link Rating} for the given key or null if no rating exists + * for the given key. + * + * @param key The key the value is stored under + * @return A {@link Rating} or null + */ + public Rating getRating(@RatingKey String key) { + Rating rating = null; + try { + rating = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a bitmap + Log.w(TAG, "Failed to retrieve a key as Rating.", e); + } + return rating; + } + + /** + * Returns a {@link Bitmap} for the given key or null if no bitmap exists + * for the given key. + * + * @param key The key the value is stored under + * @return A {@link Bitmap} or null + */ + public Bitmap getBitmap(@BitmapKey String key) { + Bitmap bmp = null; + try { + bmp = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a bitmap + Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); + } + return bmp; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBundle(mBundle); + } + + /** + * Returns the number of fields in this metadata. + * + * @return The number of fields in the metadata. + */ + public int size() { + return mBundle.size(); + } + + /** + * Returns a Set containing the Strings used as keys in this metadata. + * + * @return a Set of String keys + */ + public Set keySet() { + return mBundle.keySet(); + } + + /** + * Returns a simple description of this metadata for display purposes. + * + * @return A simple description of this metadata. + */ + public @NonNull MediaDescription getDescription() { + if (mDescription != null) { + return mDescription; + } + + String mediaId = getString(METADATA_KEY_MEDIA_ID); + + CharSequence[] text = new CharSequence[3]; + Bitmap icon = null; + Uri iconUri = null; + + // First handle the case where display data is set already + CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE); + if (!TextUtils.isEmpty(displayText)) { + // If they have a display title use only display data, otherwise use + // our best bets + text[0] = displayText; + text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE); + text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION); + } else { + // Use whatever fields we can + int textIndex = 0; + int keyIndex = 0; + while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) { + CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]); + if (!TextUtils.isEmpty(next)) { + // Fill in the next empty bit of text + text[textIndex++] = next; + } + } + } + + // Get the best art bitmap we can find + for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) { + Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]); + if (next != null) { + icon = next; + break; + } + } + + // Get the best Uri we can find + for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) { + String next = getString(PREFERRED_URI_ORDER[i]); + if (!TextUtils.isEmpty(next)) { + iconUri = Uri.parse(next); + break; + } + } + + Uri mediaUri = null; + String mediaUriStr = getString(METADATA_KEY_MEDIA_URI); + if (!TextUtils.isEmpty(mediaUriStr)) { + mediaUri = Uri.parse(mediaUriStr); + } + + MediaDescription.Builder bob = new MediaDescription.Builder(); + bob.setMediaId(mediaId); + bob.setTitle(text[0]); + bob.setSubtitle(text[1]); + bob.setDescription(text[2]); + bob.setIconBitmap(icon); + bob.setIconUri(iconUri); + bob.setMediaUri(mediaUri); + if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) { + Bundle bundle = new Bundle(); + bundle.putLong(MediaDescription.EXTRA_BT_FOLDER_TYPE, + getLong(METADATA_KEY_BT_FOLDER_TYPE)); + bob.setExtras(bundle); + } + mDescription = bob.build(); + + return mDescription; + } + + /** + * Helper for getting the String key used by {@link MediaMetadata} from the + * integer key that {@link MediaMetadataEditor} uses. + * + * @param editorKey The key used by the editor + * @return The key used by this class or null if no mapping exists + * @hide + */ + @UnsupportedAppUsage + public static String getKeyFromMetadataEditorKey(int editorKey) { + return EDITOR_KEY_MAPPING.get(editorKey, null); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public MediaMetadata createFromParcel(Parcel in) { + return new MediaMetadata(in); + } + + @Override + public MediaMetadata[] newArray(int size) { + return new MediaMetadata[size]; + } + }; + + /** + * Compares the contents of this object to another MediaMetadata object. It + * does not compare Bitmaps and Ratings as the media player can choose to + * forgo these fields depending on how you retrieve the MediaMetadata. + * + * @param o The Metadata object to compare this object against + * @return Whether or not the two objects have matching fields (excluding + * Bitmaps and Ratings) + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof MediaMetadata)) { + return false; + } + + final MediaMetadata m = (MediaMetadata) o; + + for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { + String key = METADATA_KEYS_TYPE.keyAt(i); + switch (METADATA_KEYS_TYPE.valueAt(i)) { + case METADATA_TYPE_TEXT: + if (!Objects.equals(getString(key), m.getString(key))) { + return false; + } + break; + case METADATA_TYPE_LONG: + if (getLong(key) != m.getLong(key)) { + return false; + } + break; + default: + // Ignore ratings and bitmaps when comparing + break; + } + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 17; + + for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { + String key = METADATA_KEYS_TYPE.keyAt(i); + switch (METADATA_KEYS_TYPE.valueAt(i)) { + case METADATA_TYPE_TEXT: + hashCode = 31 * hashCode + Objects.hash(getString(key)); + break; + case METADATA_TYPE_LONG: + hashCode = 31 * hashCode + Long.hashCode(getLong(key)); + break; + default: + // Ignore ratings and bitmaps when comparing + break; + } + } + + return hashCode; + } + + /** + * Use to build MediaMetadata objects. The system defined metadata keys must + * use the appropriate data type. + */ + public static final class Builder { + private final Bundle mBundle; + + /** + * Create an empty Builder. Any field that should be included in the + * {@link MediaMetadata} must be added. + */ + public Builder() { + mBundle = new Bundle(); + } + + /** + * Create a Builder using a {@link MediaMetadata} instance to set the + * initial values. All fields in the source metadata will be included in + * the new metadata. Fields can be overwritten by adding the same key. + * + * @param source + */ + public Builder(MediaMetadata source) { + mBundle = new Bundle(source.mBundle); + } + + /** + * Create a Builder using a {@link MediaMetadata} instance to set + * initial values, but replace bitmaps with a scaled down copy if they + * are larger than maxBitmapSize. + * + * @param source The original metadata to copy. + * @param maxBitmapSize The maximum height/width for bitmaps contained + * in the metadata. + * @hide + */ + public Builder(MediaMetadata source, int maxBitmapSize) { + this(source); + for (String key : mBundle.keySet()) { + Object value = mBundle.get(key); + if (value != null && value instanceof Bitmap) { + Bitmap bmp = (Bitmap) value; + if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { + putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); + } + } + } + } + + /** + * Put a CharSequence value into the metadata. Custom keys may be used, + * but if the METADATA_KEYs defined in this class are used they may only + * be one of the following: + *
      + *
    • {@link #METADATA_KEY_TITLE}
    • + *
    • {@link #METADATA_KEY_ARTIST}
    • + *
    • {@link #METADATA_KEY_ALBUM}
    • + *
    • {@link #METADATA_KEY_AUTHOR}
    • + *
    • {@link #METADATA_KEY_WRITER}
    • + *
    • {@link #METADATA_KEY_COMPOSER}
    • + *
    • {@link #METADATA_KEY_DATE}
    • + *
    • {@link #METADATA_KEY_GENRE}
    • + *
    • {@link #METADATA_KEY_ALBUM_ARTIST}
    • + *
    • {@link #METADATA_KEY_ART_URI}
    • + *
    • {@link #METADATA_KEY_ALBUM_ART_URI}
    • + *
    • {@link #METADATA_KEY_DISPLAY_TITLE}
    • + *
    • {@link #METADATA_KEY_DISPLAY_SUBTITLE}
    • + *
    • {@link #METADATA_KEY_DISPLAY_DESCRIPTION}
    • + *
    • {@link #METADATA_KEY_DISPLAY_ICON_URI}
    • + *
    + * + * @param key The key for referencing this value + * @param value The CharSequence value to store + * @return The Builder to allow chaining + */ + public Builder putText(@TextKey String key, CharSequence value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a CharSequence"); + } + } + mBundle.putCharSequence(key, value); + return this; + } + + /** + * Put a String value into the metadata. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + *
      + *
    • {@link #METADATA_KEY_TITLE}
    • + *
    • {@link #METADATA_KEY_ARTIST}
    • + *
    • {@link #METADATA_KEY_ALBUM}
    • + *
    • {@link #METADATA_KEY_AUTHOR}
    • + *
    • {@link #METADATA_KEY_WRITER}
    • + *
    • {@link #METADATA_KEY_COMPOSER}
    • + *
    • {@link #METADATA_KEY_DATE}
    • + *
    • {@link #METADATA_KEY_GENRE}
    • + *
    • {@link #METADATA_KEY_ALBUM_ARTIST}
    • + *
    • {@link #METADATA_KEY_ART_URI}
    • + *
    • {@link #METADATA_KEY_ALBUM_ART_URI}
    • + *
    • {@link #METADATA_KEY_DISPLAY_TITLE}
    • + *
    • {@link #METADATA_KEY_DISPLAY_SUBTITLE}
    • + *
    • {@link #METADATA_KEY_DISPLAY_DESCRIPTION}
    • + *
    • {@link #METADATA_KEY_DISPLAY_ICON_URI}
    • + *
    + *

    + * Uris for artwork should use the content:// style and support + * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork + * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri, + * String, Bundle)}. + * + * @param key The key for referencing this value + * @param value The String value to store + * @return The Builder to allow chaining + */ + public Builder putString(@TextKey String key, String value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a String"); + } + } + mBundle.putCharSequence(key, value); + return this; + } + + /** + * Put a long value into the metadata. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + *

      + *
    • {@link #METADATA_KEY_DURATION}
    • + *
    • {@link #METADATA_KEY_TRACK_NUMBER}
    • + *
    • {@link #METADATA_KEY_NUM_TRACKS}
    • + *
    • {@link #METADATA_KEY_DISC_NUMBER}
    • + *
    • {@link #METADATA_KEY_YEAR}
    • + *
    + * + * @param key The key for referencing this value + * @param value The long value to store + * @return The Builder to allow chaining + */ + public Builder putLong(@LongKey String key, long value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a long"); + } + } + mBundle.putLong(key, value); + return this; + } + + /** + * Put a {@link Rating} into the metadata. Custom keys may be used, but + * if the METADATA_KEYs defined in this class are used they may only be + * one of the following: + *
      + *
    • {@link #METADATA_KEY_RATING}
    • + *
    • {@link #METADATA_KEY_USER_RATING}
    • + *
    + * + * @param key The key for referencing this value + * @param value The Rating value to store + * @return The Builder to allow chaining + */ + public Builder putRating(@RatingKey String key, Rating value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a Rating"); + } + } + mBundle.putParcelable(key, value); + return this; + } + + /** + * Put a {@link Bitmap} into the metadata. Custom keys may be used, but + * if the METADATA_KEYs defined in this class are used they may only be + * one of the following: + *
      + *
    • {@link #METADATA_KEY_ART}
    • + *
    • {@link #METADATA_KEY_ALBUM_ART}
    • + *
    • {@link #METADATA_KEY_DISPLAY_ICON}
    • + *
    + *

    + * Large bitmaps may be scaled down by the system when + * {@link android.media.session.MediaSession#setMetadata} is called. + * To pass full resolution images {@link Uri Uris} should be used with + * {@link #putString}. + * + * @param key The key for referencing this value + * @param value The Bitmap to store + * @return The Builder to allow chaining + */ + public Builder putBitmap(@BitmapKey String key, Bitmap value) { + if (METADATA_KEYS_TYPE.containsKey(key)) { + if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a Bitmap"); + } + } + mBundle.putParcelable(key, value); + return this; + } + + /** + * Creates a {@link MediaMetadata} instance with the specified fields. + * + * @return The new MediaMetadata instance + */ + public MediaMetadata build() { + return new MediaMetadata(mBundle); + } + + private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { + float maxSizeF = maxSize; + float widthScale = maxSizeF / bmp.getWidth(); + float heightScale = maxSizeF / bmp.getHeight(); + float scale = Math.min(widthScale, heightScale); + int height = (int) (bmp.getHeight() * scale); + int width = (int) (bmp.getWidth() * scale); + return Bitmap.createScaledBitmap(bmp, width, height, true); + } + } +} diff --git a/packages/MediaComponents/apex/java/android/media/Rating.aidl b/packages/MediaComponents/apex/java/android/media/Rating.aidl new file mode 100644 index 0000000000..1dc336aeb7 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/Rating.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +parcelable Rating; diff --git a/packages/MediaComponents/apex/java/android/media/Rating.java b/packages/MediaComponents/apex/java/android/media/Rating.java new file mode 100644 index 0000000000..04d5364f7c --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/Rating.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A class to encapsulate rating information used as content metadata. + * A rating is defined by its rating style (see {@link #RATING_HEART}, + * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, + * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may + * be defined as "unrated"), both of which are defined when the rating instance is constructed + * through one of the factory methods. + */ +public final class Rating implements Parcelable { + private final static String TAG = "Rating"; + + /** + * @hide + */ + @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS, + RATING_5_STARS, RATING_PERCENTAGE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Style {} + + /** + * @hide + */ + @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS}) + @Retention(RetentionPolicy.SOURCE) + public @interface StarStyle {} + + /** + * Indicates a rating style is not supported. A Rating will never have this + * type, but can be used by other classes to indicate they do not support + * Rating. + */ + public final static int RATING_NONE = 0; + + /** + * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to + * indicate the content referred to is a favorite (or not). + */ + public final static int RATING_HEART = 1; + + /** + * A rating style for "thumb up" vs "thumb down". + */ + public final static int RATING_THUMB_UP_DOWN = 2; + + /** + * A rating style with 0 to 3 stars. + */ + public final static int RATING_3_STARS = 3; + + /** + * A rating style with 0 to 4 stars. + */ + public final static int RATING_4_STARS = 4; + + /** + * A rating style with 0 to 5 stars. + */ + public final static int RATING_5_STARS = 5; + + /** + * A rating style expressed as a percentage. + */ + public final static int RATING_PERCENTAGE = 6; + + private final static float RATING_NOT_RATED = -1.0f; + + private final int mRatingStyle; + + private final float mRatingValue; + + private Rating(@Style int ratingStyle, float rating) { + mRatingStyle = ratingStyle; + mRatingValue = rating; + } + + @Override + public String toString() { + return "Rating:style=" + mRatingStyle + " rating=" + + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue)); + } + + @Override + public int describeContents() { + return mRatingStyle; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRatingStyle); + dest.writeFloat(mRatingValue); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + /** + * Rebuilds a Rating previously stored with writeToParcel(). + * @param p Parcel object to read the Rating from + * @return a new Rating created from the data in the parcel + */ + @Override + public Rating createFromParcel(Parcel p) { + return new Rating(p.readInt(), p.readFloat()); + } + + @Override + public Rating[] newArray(int size) { + return new Rating[size]; + } + }; + + /** + * Return a Rating instance with no rating. + * Create and return a new Rating instance with no rating known for the given + * rating style. + * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, + * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, + * or {@link #RATING_PERCENTAGE}. + * @return null if an invalid rating style is passed, a new Rating instance otherwise. + */ + public static Rating newUnratedRating(@Style int ratingStyle) { + switch(ratingStyle) { + case RATING_HEART: + case RATING_THUMB_UP_DOWN: + case RATING_3_STARS: + case RATING_4_STARS: + case RATING_5_STARS: + case RATING_PERCENTAGE: + return new Rating(ratingStyle, RATING_NOT_RATED); + default: + return null; + } + } + + /** + * Return a Rating instance with a heart-based rating. + * Create and return a new Rating instance with a rating style of {@link #RATING_HEART}, + * and a heart-based rating. + * @param hasHeart true for a "heart selected" rating, false for "heart unselected". + * @return a new Rating instance. + */ + public static Rating newHeartRating(boolean hasHeart) { + return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f); + } + + /** + * Return a Rating instance with a thumb-based rating. + * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN} + * rating style, and a "thumb up" or "thumb down" rating. + * @param thumbIsUp true for a "thumb up" rating, false for "thumb down". + * @return a new Rating instance. + */ + public static Rating newThumbRating(boolean thumbIsUp) { + return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f); + } + + /** + * Return a Rating instance with a star-based rating. + * Create and return a new Rating instance with one of the star-base rating styles + * and the given integer or fractional number of stars. Non integer values can for instance + * be used to represent an average rating value, which might not be an integer number of stars. + * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, + * {@link #RATING_5_STARS}. + * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to + * the rating style. + * @return null if the rating style is invalid, or the rating is out of range, + * a new Rating instance otherwise. + */ + public static Rating newStarRating(@StarStyle int starRatingStyle, float starRating) { + float maxRating = -1.0f; + switch(starRatingStyle) { + case RATING_3_STARS: + maxRating = 3.0f; + break; + case RATING_4_STARS: + maxRating = 4.0f; + break; + case RATING_5_STARS: + maxRating = 5.0f; + break; + default: + Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); + return null; + } + if ((starRating < 0.0f) || (starRating > maxRating)) { + Log.e(TAG, "Trying to set out of range star-based rating"); + return null; + } + return new Rating(starRatingStyle, starRating); + } + + /** + * Return a Rating instance with a percentage-based rating. + * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE} + * rating style, and a rating of the given percentage. + * @param percent the value of the rating + * @return null if the rating is out of range, a new Rating instance otherwise. + */ + public static Rating newPercentageRating(float percent) { + if ((percent < 0.0f) || (percent > 100.0f)) { + Log.e(TAG, "Invalid percentage-based rating value"); + return null; + } else { + return new Rating(RATING_PERCENTAGE, percent); + } + } + + /** + * Return whether there is a rating value available. + * @return true if the instance was not created with {@link #newUnratedRating(int)}. + */ + public boolean isRated() { + return mRatingValue >= 0.0f; + } + + /** + * Return the rating style. + * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, + * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, + * or {@link #RATING_PERCENTAGE}. + */ + @Style + public int getRatingStyle() { + return mRatingStyle; + } + + /** + * Return whether the rating is "heart selected". + * @return true if the rating is "heart selected", false if the rating is "heart unselected", + * if the rating style is not {@link #RATING_HEART} or if it is unrated. + */ + public boolean hasHeart() { + if (mRatingStyle != RATING_HEART) { + return false; + } else { + return (mRatingValue == 1.0f); + } + } + + /** + * Return whether the rating is "thumb up". + * @return true if the rating is "thumb up", false if the rating is "thumb down", + * if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated. + */ + public boolean isThumbUp() { + if (mRatingStyle != RATING_THUMB_UP_DOWN) { + return false; + } else { + return (mRatingValue == 1.0f); + } + } + + /** + * Return the star-based rating value. + * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is + * not star-based, or if it is unrated. + */ + public float getStarRating() { + switch (mRatingStyle) { + case RATING_3_STARS: + case RATING_4_STARS: + case RATING_5_STARS: + if (isRated()) { + return mRatingValue; + } + default: + return -1.0f; + } + } + + /** + * Return the percentage-based rating value. + * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is + * not percentage-based, or if it is unrated. + */ + public float getPercentRating() { + if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) { + return -1.0f; + } else { + return mRatingValue; + } + } +} diff --git a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java new file mode 100644 index 0000000000..1c017c564b --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2014 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.annotation.IntDef; +import android.media.session.MediaSession; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Handles requests to adjust or set the volume on a session. This is also used + * to push volume updates back to the session. The provider must call + * {@link #setCurrentVolume(int)} each time the volume being provided changes. + *

    + * You can set a volume provider on a session by calling + * {@link MediaSession#setPlaybackToRemote}. + */ +public abstract class VolumeProvider { + + /** + * @hide + */ + @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE}) + @Retention(RetentionPolicy.SOURCE) + public @interface ControlType {} + + /** + * The volume is fixed and can not be modified. Requests to change volume + * should be ignored. + */ + public static final int VOLUME_CONTROL_FIXED = 0; + + /** + * The volume control uses relative adjustment via + * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific + * value should be ignored. + */ + public static final int VOLUME_CONTROL_RELATIVE = 1; + + /** + * The volume control uses an absolute value. It may be adjusted using + * {@link #onAdjustVolume(int)} or set directly using + * {@link #onSetVolumeTo(int)}. + */ + public static final int VOLUME_CONTROL_ABSOLUTE = 2; + + private final int mControlType; + private final int mMaxVolume; + private int mCurrentVolume; + private Callback mCallback; + + /** + * Create a new volume provider for handling volume events. You must specify + * the type of volume control, the maximum volume that can be used, and the + * current volume on the output. + * + * @param volumeControl The method for controlling volume that is used by + * this provider. + * @param maxVolume The maximum allowed volume. + * @param currentVolume The current volume on the output. + */ + public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) { + mControlType = volumeControl; + mMaxVolume = maxVolume; + mCurrentVolume = currentVolume; + } + + /** + * Get the volume control type that this volume provider uses. + * + * @return The volume control type for this volume provider + */ + @ControlType + public final int getVolumeControl() { + return mControlType; + } + + /** + * Get the maximum volume this provider allows. + * + * @return The max allowed volume. + */ + public final int getMaxVolume() { + return mMaxVolume; + } + + /** + * Gets the current volume. This will be the last value set by + * {@link #setCurrentVolume(int)}. + * + * @return The current volume. + */ + public final int getCurrentVolume() { + return mCurrentVolume; + } + + /** + * Notify the system that the current volume has been changed. This must be + * called every time the volume changes to ensure it is displayed properly. + * + * @param currentVolume The current volume on the output. + */ + public final void setCurrentVolume(int currentVolume) { + mCurrentVolume = currentVolume; + if (mCallback != null) { + mCallback.onVolumeChanged(this); + } + } + + /** + * Override to handle requests to set the volume of the current output. + * After the volume has been modified {@link #setCurrentVolume} must be + * called to notify the system. + * + * @param volume The volume to set the output to. + */ + public void onSetVolumeTo(int volume) { + } + + /** + * Override to handle requests to adjust the volume of the current output. + * Direction will be one of {@link AudioManager#ADJUST_LOWER}, + * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}. + * After the volume has been modified {@link #setCurrentVolume} must be + * called to notify the system. + * + * @param direction The direction to change the volume in. + */ + public void onAdjustVolume(int direction) { + } + + /** + * Sets a callback to receive volume changes. + * @hide + */ + public void setCallback(Callback callback) { + mCallback = callback; + } + + /** + * Listens for changes to the volume. + * @hide + */ + public static abstract class Callback { + public abstract void onVolumeChanged(VolumeProvider volumeProvider); + } +} diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl new file mode 100644 index 0000000000..782e09471a --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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.browse; + +parcelable MediaBrowser.MediaItem; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java new file mode 100644 index 0000000000..2bccd884be --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2014 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.browse; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ParceledListSlice; +import android.media.MediaDescription; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.service.media.IMediaBrowserService; +import android.service.media.IMediaBrowserServiceCallbacks; +import android.service.media.MediaBrowserService; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +/** + * Browses media content offered by a link MediaBrowserService. + *

    + * This object is not thread-safe. All calls should happen on the thread on which the browser + * was constructed. + *

    + *

    Standard Extra Data

    + * + *

    These are the current standard fields that can be used as extra data via + * {@link #subscribe(String, Bundle, SubscriptionCallback)}, + * {@link #unsubscribe(String, SubscriptionCallback)}, and + * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}. + * + *

      + *
    • {@link #EXTRA_PAGE} + *
    • {@link #EXTRA_PAGE_SIZE} + *
    + */ +public final class MediaBrowser { + private static final String TAG = "MediaBrowser"; + private static final boolean DBG = false; + + /** + * Used as an int extra field to denote the page number to subscribe. + * The value of {@code EXTRA_PAGE} should be greater than or equal to 0. + * + * @see #EXTRA_PAGE_SIZE + */ + public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE"; + + /** + * Used as an int extra field to denote the number of media items in a page. + * The value of {@code EXTRA_PAGE_SIZE} should be greater than or equal to 1. + * + * @see #EXTRA_PAGE + */ + public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE"; + + private static final int CONNECT_STATE_DISCONNECTING = 0; + private static final int CONNECT_STATE_DISCONNECTED = 1; + private static final int CONNECT_STATE_CONNECTING = 2; + private static final int CONNECT_STATE_CONNECTED = 3; + private static final int CONNECT_STATE_SUSPENDED = 4; + + private final Context mContext; + private final ComponentName mServiceComponent; + private final ConnectionCallback mCallback; + private final Bundle mRootHints; + private final Handler mHandler = new Handler(); + private final ArrayMap mSubscriptions = new ArrayMap<>(); + + private volatile int mState = CONNECT_STATE_DISCONNECTED; + private volatile String mRootId; + private volatile MediaSession.Token mMediaSessionToken; + private volatile Bundle mExtras; + + private MediaServiceConnection mServiceConnection; + private IMediaBrowserService mServiceBinder; + private IMediaBrowserServiceCallbacks mServiceCallbacks; + + /** + * Creates a media browser for the specified media browser service. + * + * @param context The context. + * @param serviceComponent The component name of the media browser service. + * @param callback The connection callback. + * @param rootHints An optional bundle of service-specific arguments to send + * to the media browser service when connecting and retrieving the root id + * for browsing, or null if none. The contents of this bundle may affect + * the information returned when browsing. + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE + * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED + */ + public MediaBrowser(Context context, ComponentName serviceComponent, + ConnectionCallback callback, Bundle rootHints) { + if (context == null) { + throw new IllegalArgumentException("context must not be null"); + } + if (serviceComponent == null) { + throw new IllegalArgumentException("service component must not be null"); + } + if (callback == null) { + throw new IllegalArgumentException("connection callback must not be null"); + } + mContext = context; + mServiceComponent = serviceComponent; + mCallback = callback; + mRootHints = rootHints == null ? null : new Bundle(rootHints); + } + + /** + * Connects to the media browser service. + *

    + * The connection callback specified in the constructor will be invoked + * when the connection completes or fails. + *

    + */ + public void connect() { + if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { + throw new IllegalStateException("connect() called while neither disconnecting nor " + + "disconnected (state=" + getStateLabel(mState) + ")"); + } + + mState = CONNECT_STATE_CONNECTING; + mHandler.post(new Runnable() { + @Override + public void run() { + if (mState == CONNECT_STATE_DISCONNECTING) { + return; + } + mState = CONNECT_STATE_CONNECTING; + // TODO: remove this extra check. + if (DBG) { + if (mServiceConnection != null) { + throw new RuntimeException("mServiceConnection should be null. Instead it" + + " is " + mServiceConnection); + } + } + if (mServiceBinder != null) { + throw new RuntimeException("mServiceBinder should be null. Instead it is " + + mServiceBinder); + } + if (mServiceCallbacks != null) { + throw new RuntimeException("mServiceCallbacks should be null. Instead it is " + + mServiceCallbacks); + } + + final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE); + intent.setComponent(mServiceComponent); + + mServiceConnection = new MediaServiceConnection(); + + boolean bound = false; + try { + bound = mContext.bindService(intent, mServiceConnection, + Context.BIND_AUTO_CREATE); + } catch (Exception ex) { + Log.e(TAG, "Failed binding to service " + mServiceComponent); + } + + if (!bound) { + // Tell them that it didn't work. + forceCloseConnection(); + mCallback.onConnectionFailed(); + } + + if (DBG) { + Log.d(TAG, "connect..."); + dump(); + } + } + }); + } + + /** + * Disconnects from the media browser service. + * After this, no more callbacks will be received. + */ + public void disconnect() { + // It's ok to call this any state, because allowing this lets apps not have + // to check isConnected() unnecessarily. They won't appreciate the extra + // assertions for this. We do everything we can here to go back to a sane state. + mState = CONNECT_STATE_DISCONNECTING; + mHandler.post(new Runnable() { + @Override + public void run() { + // connect() could be called before this. Then we will disconnect and reconnect. + if (mServiceCallbacks != null) { + try { + mServiceBinder.disconnect(mServiceCallbacks); + } catch (RemoteException ex) { + // We are disconnecting anyway. Log, just for posterity but it's not + // a big problem. + Log.w(TAG, "RemoteException during connect for " + mServiceComponent); + } + } + int state = mState; + forceCloseConnection(); + // If the state was not CONNECT_STATE_DISCONNECTING, keep the state so that + // the operation came after disconnect() can be handled properly. + if (state != CONNECT_STATE_DISCONNECTING) { + mState = state; + } + if (DBG) { + Log.d(TAG, "disconnect..."); + dump(); + } + } + }); + } + + /** + * Null out the variables and unbind from the service. This doesn't include + * calling disconnect on the service, because we only try to do that in the + * clean shutdown cases. + *

    + * Everywhere that calls this EXCEPT for disconnect() should follow it with + * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback + * for a clean shutdown, but everywhere else is a dirty shutdown and should + * notify the app. + *

    + * Also, mState should be updated properly. Mostly it should be CONNECT_STATE_DIACONNECTED + * except for disconnect(). + */ + private void forceCloseConnection() { + if (mServiceConnection != null) { + try { + mContext.unbindService(mServiceConnection); + } catch (IllegalArgumentException e) { + if (DBG) { + Log.d(TAG, "unbindService failed", e); + } + } + } + mState = CONNECT_STATE_DISCONNECTED; + mServiceConnection = null; + mServiceBinder = null; + mServiceCallbacks = null; + mRootId = null; + mMediaSessionToken = null; + } + + /** + * Returns whether the browser is connected to the service. + */ + public boolean isConnected() { + return mState == CONNECT_STATE_CONNECTED; + } + + /** + * Gets the service component that the media browser is connected to. + */ + public @NonNull ComponentName getServiceComponent() { + if (!isConnected()) { + throw new IllegalStateException("getServiceComponent() called while not connected" + + " (state=" + mState + ")"); + } + return mServiceComponent; + } + + /** + * Gets the root id. + *

    + * Note that the root id may become invalid or change when the + * browser is disconnected. + *

    + * + * @throws IllegalStateException if not connected. + */ + public @NonNull String getRoot() { + if (!isConnected()) { + throw new IllegalStateException("getRoot() called while not connected (state=" + + getStateLabel(mState) + ")"); + } + return mRootId; + } + + /** + * Gets any extras for the media service. + * + * @throws IllegalStateException if not connected. + */ + public @Nullable Bundle getExtras() { + if (!isConnected()) { + throw new IllegalStateException("getExtras() called while not connected (state=" + + getStateLabel(mState) + ")"); + } + return mExtras; + } + + /** + * Gets the media session token associated with the media browser. + *

    + * Note that the session token may become invalid or change when the + * browser is disconnected. + *

    + * + * @return The session token for the browser, never null. + * + * @throws IllegalStateException if not connected. + */ + public @NonNull MediaSession.Token getSessionToken() { + if (!isConnected()) { + throw new IllegalStateException("getSessionToken() called while not connected (state=" + + mState + ")"); + } + return mMediaSessionToken; + } + + /** + * Queries for information about the media items that are contained within + * the specified id and subscribes to receive updates when they change. + *

    + * The list of subscriptions is maintained even when not connected and is + * restored after the reconnection. It is ok to subscribe while not connected + * but the results will not be returned until the connection completes. + *

    + *

    + * If the id is already subscribed with a different callback then the new + * callback will replace the previous one and the child data will be + * reloaded. + *

    + * + * @param parentId The id of the parent media item whose list of children + * will be subscribed. + * @param callback The callback to receive the list of children. + */ + public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { + subscribeInternal(parentId, null, callback); + } + + /** + * Queries with service-specific arguments for information about the media items + * that are contained within the specified id and subscribes to receive updates + * when they change. + *

    + * The list of subscriptions is maintained even when not connected and is + * restored after the reconnection. It is ok to subscribe while not connected + * but the results will not be returned until the connection completes. + *

    + *

    + * If the id is already subscribed with a different callback then the new + * callback will replace the previous one and the child data will be + * reloaded. + *

    + * + * @param parentId The id of the parent media item whose list of children + * will be subscribed. + * @param options The bundle of service-specific arguments to send to the media + * browser service. The contents of this bundle may affect the + * information returned when browsing. + * @param callback The callback to receive the list of children. + */ + public void subscribe(@NonNull String parentId, @NonNull Bundle options, + @NonNull SubscriptionCallback callback) { + if (options == null) { + throw new IllegalArgumentException("options cannot be null"); + } + subscribeInternal(parentId, new Bundle(options), callback); + } + + /** + * Unsubscribes for changes to the children of the specified media id. + *

    + * The query callback will no longer be invoked for results associated with + * this id once this method returns. + *

    + * + * @param parentId The id of the parent media item whose list of children + * will be unsubscribed. + */ + public void unsubscribe(@NonNull String parentId) { + unsubscribeInternal(parentId, null); + } + + /** + * Unsubscribes for changes to the children of the specified media id through a callback. + *

    + * The query callback will no longer be invoked for results associated with + * this id once this method returns. + *

    + * + * @param parentId The id of the parent media item whose list of children + * will be unsubscribed. + * @param callback A callback sent to the media browser service to subscribe. + */ + public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + unsubscribeInternal(parentId, callback); + } + + /** + * Retrieves a specific {@link MediaItem} from the connected service. Not + * all services may support this, so falling back to subscribing to the + * parent's id should be used when unavailable. + * + * @param mediaId The id of the item to retrieve. + * @param cb The callback to receive the result on. + */ + public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) { + if (TextUtils.isEmpty(mediaId)) { + throw new IllegalArgumentException("mediaId cannot be empty."); + } + if (cb == null) { + throw new IllegalArgumentException("cb cannot be null."); + } + if (mState != CONNECT_STATE_CONNECTED) { + Log.i(TAG, "Not connected, unable to retrieve the MediaItem."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + return; + } + ResultReceiver receiver = new ResultReceiver(mHandler) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (!isConnected()) { + return; + } + if (resultCode != 0 || resultData == null + || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) { + cb.onError(mediaId); + return; + } + Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM); + if (item != null && !(item instanceof MediaItem)) { + cb.onError(mediaId); + return; + } + cb.onItemLoaded((MediaItem)item); + } + }; + try { + mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks); + } catch (RemoteException e) { + Log.i(TAG, "Remote error getting media item."); + mHandler.post(new Runnable() { + @Override + public void run() { + cb.onError(mediaId); + } + }); + } + } + + private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) { + // Check arguments. + if (TextUtils.isEmpty(parentId)) { + throw new IllegalArgumentException("parentId cannot be empty."); + } + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + // Update or create the subscription. + Subscription sub = mSubscriptions.get(parentId); + if (sub == null) { + sub = new Subscription(); + mSubscriptions.put(parentId, sub); + } + sub.putCallback(mContext, options, callback); + + // If we are connected, tell the service that we are watching. If we aren't connected, + // the service will be told when we connect. + if (isConnected()) { + try { + if (options == null) { + mServiceBinder.addSubscriptionDeprecated(parentId, mServiceCallbacks); + } + mServiceBinder.addSubscription(parentId, callback.mToken, options, + mServiceCallbacks); + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId); + } + } + } + + private void unsubscribeInternal(String parentId, SubscriptionCallback callback) { + // Check arguments. + if (TextUtils.isEmpty(parentId)) { + throw new IllegalArgumentException("parentId cannot be empty."); + } + + Subscription sub = mSubscriptions.get(parentId); + if (sub == null) { + return; + } + // Tell the service if necessary. + try { + if (callback == null) { + if (isConnected()) { + mServiceBinder.removeSubscriptionDeprecated(parentId, mServiceCallbacks); + mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks); + } + } else { + final List callbacks = sub.getCallbacks(); + final List optionsList = sub.getOptionsList(); + for (int i = callbacks.size() - 1; i >= 0; --i) { + if (callbacks.get(i) == callback) { + if (isConnected()) { + mServiceBinder.removeSubscription( + parentId, callback.mToken, mServiceCallbacks); + } + callbacks.remove(i); + optionsList.remove(i); + } + } + } + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId); + } + + if (sub.isEmpty() || callback == null) { + mSubscriptions.remove(parentId); + } + } + + /** + * For debugging. + */ + private static String getStateLabel(int state) { + switch (state) { + case CONNECT_STATE_DISCONNECTING: + return "CONNECT_STATE_DISCONNECTING"; + case CONNECT_STATE_DISCONNECTED: + return "CONNECT_STATE_DISCONNECTED"; + case CONNECT_STATE_CONNECTING: + return "CONNECT_STATE_CONNECTING"; + case CONNECT_STATE_CONNECTED: + return "CONNECT_STATE_CONNECTED"; + case CONNECT_STATE_SUSPENDED: + return "CONNECT_STATE_SUSPENDED"; + default: + return "UNKNOWN/" + state; + } + } + + private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback, + final String root, final MediaSession.Token session, final Bundle extra) { + mHandler.post(new Runnable() { + @Override + public void run() { + // Check to make sure there hasn't been a disconnect or a different + // ServiceConnection. + if (!isCurrent(callback, "onConnect")) { + return; + } + // Don't allow them to call us twice. + if (mState != CONNECT_STATE_CONNECTING) { + Log.w(TAG, "onConnect from service while mState=" + + getStateLabel(mState) + "... ignoring"); + return; + } + mRootId = root; + mMediaSessionToken = session; + mExtras = extra; + mState = CONNECT_STATE_CONNECTED; + + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + mCallback.onConnected(); + + // we may receive some subscriptions before we are connected, so re-subscribe + // everything now + for (Entry subscriptionEntry : mSubscriptions.entrySet()) { + String id = subscriptionEntry.getKey(); + Subscription sub = subscriptionEntry.getValue(); + List callbackList = sub.getCallbacks(); + List optionsList = sub.getOptionsList(); + for (int i = 0; i < callbackList.size(); ++i) { + try { + mServiceBinder.addSubscription(id, callbackList.get(i).mToken, + optionsList.get(i), mServiceCallbacks); + } catch (RemoteException ex) { + // Process is crashing. We will disconnect, and upon reconnect we will + // automatically reregister. So nothing to do here. + Log.d(TAG, "addSubscription failed with RemoteException parentId=" + + id); + } + } + } + } + }); + } + + private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) { + mHandler.post(new Runnable() { + @Override + public void run() { + Log.e(TAG, "onConnectFailed for " + mServiceComponent); + + // Check to make sure there hasn't been a disconnect or a different + // ServiceConnection. + if (!isCurrent(callback, "onConnectFailed")) { + return; + } + // Don't allow them to call us twice. + if (mState != CONNECT_STATE_CONNECTING) { + Log.w(TAG, "onConnect from service while mState=" + + getStateLabel(mState) + "... ignoring"); + return; + } + + // Clean up + forceCloseConnection(); + + // Tell the app. + mCallback.onConnectionFailed(); + } + }); + } + + private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, + final String parentId, final ParceledListSlice list, final Bundle options) { + mHandler.post(new Runnable() { + @Override + public void run() { + // Check that there hasn't been a disconnect or a different + // ServiceConnection. + if (!isCurrent(callback, "onLoadChildren")) { + return; + } + + if (DBG) { + Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId); + } + + // Check that the subscription is still subscribed. + final Subscription subscription = mSubscriptions.get(parentId); + if (subscription != null) { + // Tell the app. + SubscriptionCallback subscriptionCallback = + subscription.getCallback(mContext, options); + if (subscriptionCallback != null) { + List data = list == null ? null : list.getList(); + if (options == null) { + if (data == null) { + subscriptionCallback.onError(parentId); + } else { + subscriptionCallback.onChildrenLoaded(parentId, data); + } + } else { + if (data == null) { + subscriptionCallback.onError(parentId, options); + } else { + subscriptionCallback.onChildrenLoaded(parentId, data, options); + } + } + return; + } + } + if (DBG) { + Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId); + } + } + }); + } + + /** + * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. + */ + private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) { + if (mServiceCallbacks != callback || mState == CONNECT_STATE_DISCONNECTING + || mState == CONNECT_STATE_DISCONNECTED) { + if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { + Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" + + mServiceCallbacks + " this=" + this); + } + return false; + } + return true; + } + + private ServiceCallbacks getNewServiceCallbacks() { + return new ServiceCallbacks(this); + } + + /** + * Log internal state. + * @hide + */ + void dump() { + Log.d(TAG, "MediaBrowser..."); + Log.d(TAG, " mServiceComponent=" + mServiceComponent); + Log.d(TAG, " mCallback=" + mCallback); + Log.d(TAG, " mRootHints=" + mRootHints); + Log.d(TAG, " mState=" + getStateLabel(mState)); + Log.d(TAG, " mServiceConnection=" + mServiceConnection); + Log.d(TAG, " mServiceBinder=" + mServiceBinder); + Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks); + Log.d(TAG, " mRootId=" + mRootId); + Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken); + } + + /** + * A class with information on a single media item for use in browsing/searching media. + * MediaItems are application dependent so we cannot guarantee that they contain the + * right values. + */ + public static class MediaItem implements Parcelable { + private final int mFlags; + private final MediaDescription mDescription; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE }) + public @interface Flags { } + + /** + * Flag: Indicates that the item has children of its own. + */ + public static final int FLAG_BROWSABLE = 1 << 0; + + /** + * Flag: Indicates that the item is playable. + *

    + * The id of this item may be passed to + * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)} + * to start playing it. + *

    + */ + public static final int FLAG_PLAYABLE = 1 << 1; + + /** + * Create a new MediaItem for use in browsing media. + * @param description The description of the media, which must include a + * media id. + * @param flags The flags for this item. + */ + public MediaItem(@NonNull MediaDescription description, @Flags int flags) { + if (description == null) { + throw new IllegalArgumentException("description cannot be null"); + } + if (TextUtils.isEmpty(description.getMediaId())) { + throw new IllegalArgumentException("description must have a non-empty media id"); + } + mFlags = flags; + mDescription = description; + } + + /** + * Private constructor. + */ + private MediaItem(Parcel in) { + mFlags = in.readInt(); + mDescription = MediaDescription.CREATOR.createFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mFlags); + mDescription.writeToParcel(out, flags); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MediaItem{"); + sb.append("mFlags=").append(mFlags); + sb.append(", mDescription=").append(mDescription); + sb.append('}'); + return sb.toString(); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public MediaItem createFromParcel(Parcel in) { + return new MediaItem(in); + } + + @Override + public MediaItem[] newArray(int size) { + return new MediaItem[size]; + } + }; + + /** + * Gets the flags of the item. + */ + public @Flags int getFlags() { + return mFlags; + } + + /** + * Returns whether this item is browsable. + * @see #FLAG_BROWSABLE + */ + public boolean isBrowsable() { + return (mFlags & FLAG_BROWSABLE) != 0; + } + + /** + * Returns whether this item is playable. + * @see #FLAG_PLAYABLE + */ + public boolean isPlayable() { + return (mFlags & FLAG_PLAYABLE) != 0; + } + + /** + * Returns the description of the media. + */ + public @NonNull MediaDescription getDescription() { + return mDescription; + } + + /** + * Returns the media id in the {@link MediaDescription} for this item. + * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID + */ + public @Nullable String getMediaId() { + return mDescription.getMediaId(); + } + } + + /** + * Callbacks for connection related events. + */ + public static class ConnectionCallback { + /** + * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed. + */ + public void onConnected() { + } + + /** + * Invoked when the client is disconnected from the media browser. + */ + public void onConnectionSuspended() { + } + + /** + * Invoked when the connection to the media browser failed. + */ + public void onConnectionFailed() { + } + } + + /** + * Callbacks for subscription related events. + */ + public static abstract class SubscriptionCallback { + Binder mToken; + + public SubscriptionCallback() { + mToken = new Binder(); + } + + /** + * Called when the list of children is loaded or updated. + * + * @param parentId The media id of the parent media item. + * @param children The children which were loaded. + */ + public void onChildrenLoaded(@NonNull String parentId, @NonNull List children) { + } + + /** + * Called when the list of children is loaded or updated. + * + * @param parentId The media id of the parent media item. + * @param children The children which were loaded. + * @param options The bundle of service-specific arguments sent to the media + * browser service. The contents of this bundle may affect the + * information returned when browsing. + */ + public void onChildrenLoaded(@NonNull String parentId, @NonNull List children, + @NonNull Bundle options) { + } + + /** + * Called when the id doesn't exist or other errors in subscribing. + *

    + * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} + * called, because some errors may heal themselves. + *

    + * + * @param parentId The media id of the parent media item whose children could + * not be loaded. + */ + public void onError(@NonNull String parentId) { + } + + /** + * Called when the id doesn't exist or other errors in subscribing. + *

    + * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} + * called, because some errors may heal themselves. + *

    + * + * @param parentId The media id of the parent media item whose children could + * not be loaded. + * @param options The bundle of service-specific arguments sent to the media + * browser service. + */ + public void onError(@NonNull String parentId, @NonNull Bundle options) { + } + } + + /** + * Callback for receiving the result of {@link #getItem}. + */ + public static abstract class ItemCallback { + /** + * Called when the item has been returned by the connected service. + * + * @param item The item that was returned or null if it doesn't exist. + */ + public void onItemLoaded(MediaItem item) { + } + + /** + * Called there was an error retrieving it or the connected service doesn't support + * {@link #getItem}. + * + * @param mediaId The media id of the media item which could not be loaded. + */ + public void onError(@NonNull String mediaId) { + } + } + + /** + * ServiceConnection to the other app. + */ + private class MediaServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(final ComponentName name, final IBinder binder) { + postOrRun(new Runnable() { + @Override + public void run() { + if (DBG) { + Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name + + " binder=" + binder); + dump(); + } + + // Make sure we are still the current connection, and that they haven't called + // disconnect(). + if (!isCurrent("onServiceConnected")) { + return; + } + + // Save their binder + mServiceBinder = IMediaBrowserService.Stub.asInterface(binder); + + // We make a new mServiceCallbacks each time we connect so that we can drop + // responses from previous connections. + mServiceCallbacks = getNewServiceCallbacks(); + mState = CONNECT_STATE_CONNECTING; + + // Call connect, which is async. When we get a response from that we will + // say that we're connected. + try { + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + mServiceBinder.connect(mContext.getPackageName(), mRootHints, + mServiceCallbacks); + } catch (RemoteException ex) { + // Connect failed, which isn't good. But the auto-reconnect on the service + // will take over and we will come back. We will also get the + // onServiceDisconnected, which has all the cleanup code. So let that do + // it. + Log.w(TAG, "RemoteException during connect for " + mServiceComponent); + if (DBG) { + Log.d(TAG, "ServiceCallbacks.onConnect..."); + dump(); + } + } + } + }); + } + + @Override + public void onServiceDisconnected(final ComponentName name) { + postOrRun(new Runnable() { + @Override + public void run() { + if (DBG) { + Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name + + " this=" + this + " mServiceConnection=" + mServiceConnection); + dump(); + } + + // Make sure we are still the current connection, and that they haven't called + // disconnect(). + if (!isCurrent("onServiceDisconnected")) { + return; + } + + // Clear out what we set in onServiceConnected + mServiceBinder = null; + mServiceCallbacks = null; + + // And tell the app that it's suspended. + mState = CONNECT_STATE_SUSPENDED; + mCallback.onConnectionSuspended(); + } + }); + } + + private void postOrRun(Runnable r) { + if (Thread.currentThread() == mHandler.getLooper().getThread()) { + r.run(); + } else { + mHandler.post(r); + } + } + + /** + * Return true if this is the current ServiceConnection. Also logs if it's not. + */ + private boolean isCurrent(String funcName) { + if (mServiceConnection != this || mState == CONNECT_STATE_DISCONNECTING + || mState == CONNECT_STATE_DISCONNECTED) { + if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { + // Check mState, because otherwise this log is noisy. + Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" + + mServiceConnection + " this=" + this); + } + return false; + } + return true; + } + } + + /** + * Callbacks from the service. + */ + private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub { + private WeakReference mMediaBrowser; + + public ServiceCallbacks(MediaBrowser mediaBrowser) { + mMediaBrowser = new WeakReference(mediaBrowser); + } + + /** + * The other side has acknowledged our connection. The parameters to this function + * are the initial data as requested. + */ + @Override + public void onConnect(String root, MediaSession.Token session, + final Bundle extras) { + MediaBrowser mediaBrowser = mMediaBrowser.get(); + if (mediaBrowser != null) { + mediaBrowser.onServiceConnected(this, root, session, extras); + } + } + + /** + * The other side does not like us. Tell the app via onConnectionFailed. + */ + @Override + public void onConnectFailed() { + MediaBrowser mediaBrowser = mMediaBrowser.get(); + if (mediaBrowser != null) { + mediaBrowser.onConnectionFailed(this); + } + } + + @Override + public void onLoadChildren(String parentId, ParceledListSlice list) { + onLoadChildrenWithOptions(parentId, list, null); + } + + @Override + public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list, + final Bundle options) { + MediaBrowser mediaBrowser = mMediaBrowser.get(); + if (mediaBrowser != null) { + mediaBrowser.onLoadChildren(this, parentId, list, options); + } + } + } + + private static class Subscription { + private final List mCallbacks; + private final List mOptionsList; + + public Subscription() { + mCallbacks = new ArrayList<>(); + mOptionsList = new ArrayList<>(); + } + + public boolean isEmpty() { + return mCallbacks.isEmpty(); + } + + public List getOptionsList() { + return mOptionsList; + } + + public List getCallbacks() { + return mCallbacks; + } + + public SubscriptionCallback getCallback(Context context, Bundle options) { + if (options != null) { + options.setClassLoader(context.getClassLoader()); + } + for (int i = 0; i < mOptionsList.size(); ++i) { + if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) { + return mCallbacks.get(i); + } + } + return null; + } + + public void putCallback(Context context, Bundle options, SubscriptionCallback callback) { + if (options != null) { + options.setClassLoader(context.getClassLoader()); + } + for (int i = 0; i < mOptionsList.size(); ++i) { + if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) { + mCallbacks.set(i, callback); + return; + } + } + mCallbacks.add(callback); + mOptionsList.add(options); + } + } +} diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java new file mode 100644 index 0000000000..2943e60dbb --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.browse; + +import android.os.Bundle; + +/** + * @hide + */ +public class MediaBrowserUtils { + public static boolean areSameOptions(Bundle options1, Bundle options2) { + if (options1 == options2) { + return true; + } else if (options1 == null) { + return options2.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1 + && options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1; + } else if (options2 == null) { + return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1 + && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1; + } else { + return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) + == options2.getInt(MediaBrowser.EXTRA_PAGE, -1) + && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) + == options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); + } + } + + public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) { + int page1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE, -1); + int page2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE, -1); + int pageSize1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); + int pageSize2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); + + int startIndex1, startIndex2, endIndex1, endIndex2; + if (page1 == -1 || pageSize1 == -1) { + startIndex1 = 0; + endIndex1 = Integer.MAX_VALUE; + } else { + startIndex1 = pageSize1 * page1; + endIndex1 = startIndex1 + pageSize1 - 1; + } + + if (page2 == -1 || pageSize2 == -1) { + startIndex2 = 0; + endIndex2 = Integer.MAX_VALUE; + } else { + startIndex2 = pageSize2 * page2; + endIndex2 = startIndex2 + pageSize2 - 1; + } + + if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) { + return true; + } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) { + return true; + } + return false; + } +} diff --git a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl new file mode 100644 index 0000000000..4b9e4bd0fb --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl @@ -0,0 +1,26 @@ +/* Copyright (C) 2014 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.session; + +import android.media.session.MediaSession; + +/** + * Listens for changes to the list of active sessions. + * @hide + */ +oneway interface IActiveSessionsListener { + void onActiveSessionsChanged(in List sessions); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl new file mode 100644 index 0000000000..322bffa96c --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl @@ -0,0 +1,35 @@ +/* Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.media.session.MediaSession; +import android.view.KeyEvent; + +/** + * @hide + */ +oneway interface ICallback { + void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event, + in MediaSession.Token sessionToken); + void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event, + in ComponentName mediaButtonReceiver); + + void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken); + void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver); +} + diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl new file mode 100644 index 0000000000..aa98ea312a --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl @@ -0,0 +1,28 @@ +/* Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.os.ResultReceiver; +import android.view.KeyEvent; + +/** + * Listener to handle media key. + * @hide + */ +oneway interface IOnMediaKeyListener { + void onMediaKey(in KeyEvent event, in ResultReceiver result); +} + diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl new file mode 100644 index 0000000000..07b83471d1 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl @@ -0,0 +1,27 @@ +/* Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.session; + +import android.view.KeyEvent; + +/** + * Listener to handle volume key long-press. + * @hide + */ +oneway interface IOnVolumeKeyLongPressListener { + void onVolumeKeyLongPress(in KeyEvent event); +} + diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl new file mode 100644 index 0000000000..bd0019f0f1 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 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.session; + +import android.app.PendingIntent; +import android.content.pm.ParceledListSlice; +import android.media.AudioAttributes; +import android.media.MediaMetadata; +import android.media.session.ISessionController; +import android.media.session.PlaybackState; +import android.media.session.MediaSession; +import android.os.Bundle; +import android.os.ResultReceiver; + +/** + * Interface to a MediaSession in the system. + * @hide + */ +interface ISession { + void sendEvent(String event, in Bundle data); + ISessionController getController(); + void setFlags(int flags); + void setActive(boolean active); + void setMediaButtonReceiver(in PendingIntent mbr); + void setLaunchPendingIntent(in PendingIntent pi); + void destroy(); + + // These commands are for the TransportPerformer + void setMetadata(in MediaMetadata metadata); + void setPlaybackState(in PlaybackState state); + void setQueue(in ParceledListSlice queue); + void setQueueTitle(CharSequence title); + void setExtras(in Bundle extras); + void setRatingType(int type); + + // These commands relate to volume handling + void setPlaybackToLocal(in AudioAttributes attributes); + void setPlaybackToRemote(int control, int max); + void setCurrentVolume(int currentVolume); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl new file mode 100644 index 0000000000..626338d97c --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl @@ -0,0 +1,71 @@ +/* Copyright (C) 2014 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.session; + +import android.content.Intent; +import android.media.Rating; +import android.media.session.ISessionControllerCallback; +import android.net.Uri; +import android.os.Bundle; +import android.os.ResultReceiver; + +/** + * @hide + */ +oneway interface ISessionCallback { + void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller, + String command, in Bundle args, in ResultReceiver cb); + void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent, + int sequenceNumber, in ResultReceiver cb); + void onMediaButtonFromController(String packageName, int pid, int uid, + ISessionControllerCallback caller, in Intent mediaButtonIntent); + + // These callbacks are for the TransportPerformer + void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPrepareFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, in Bundle extras); + void onPrepareFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, in Bundle extras); + void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller, + String query, in Bundle extras); + void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller, + long id); + void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller); + void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller, + long pos); + void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, + in Rating rating); + void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller, + String action, in Bundle args); + + // These callbacks are for volume handling + void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller, + int direction); + void onSetVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl new file mode 100644 index 0000000000..861a8cea7d --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl @@ -0,0 +1,88 @@ +/* Copyright (C) 2014 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.session; + +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.media.MediaMetadata; +import android.media.Rating; +import android.media.session.ISessionControllerCallback; +import android.media.session.MediaSession; +import android.media.session.ParcelableVolumeInfo; +import android.media.session.PlaybackState; +import android.net.Uri; +import android.os.Bundle; +import android.os.ResultReceiver; +import android.view.KeyEvent; + +import java.util.List; + +/** + * Interface to MediaSessionRecord in the system. + * @hide + */ +interface ISessionController { + void sendCommand(String packageName, ISessionControllerCallback caller, + String command, in Bundle args, in ResultReceiver cb); + boolean sendMediaButton(String packageName, ISessionControllerCallback caller, + boolean asSystemService, in KeyEvent mediaButton); + void registerCallbackListener(String packageName, ISessionControllerCallback cb); + void unregisterCallbackListener(ISessionControllerCallback cb); + boolean isTransportControlEnabled(); + String getPackageName(); + String getTag(); + PendingIntent getLaunchPendingIntent(); + long getFlags(); + ParcelableVolumeInfo getVolumeAttributes(); + void adjustVolume(String packageName, ISessionControllerCallback caller, + boolean asSystemService, int direction, int flags); + void setVolumeTo(String packageName, ISessionControllerCallback caller, + int value, int flags); + + // These commands are for the TransportControls + void prepare(String packageName, ISessionControllerCallback caller); + void prepareFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void prepareFromSearch(String packageName, ISessionControllerCallback caller, + String string, in Bundle extras); + void prepareFromUri(String packageName, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void play(String packageName, ISessionControllerCallback caller); + void playFromMediaId(String packageName, ISessionControllerCallback caller, + String mediaId, in Bundle extras); + void playFromSearch(String packageName, ISessionControllerCallback caller, + String string, in Bundle extras); + void playFromUri(String packageName, ISessionControllerCallback caller, + in Uri uri, in Bundle extras); + void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id); + void pause(String packageName, ISessionControllerCallback caller); + void stop(String packageName, ISessionControllerCallback caller); + void next(String packageName, ISessionControllerCallback caller); + void previous(String packageName, ISessionControllerCallback caller); + void fastForward(String packageName, ISessionControllerCallback caller); + void rewind(String packageName, ISessionControllerCallback caller); + void seekTo(String packageName, ISessionControllerCallback caller, long pos); + void rate(String packageName, ISessionControllerCallback caller, in Rating rating); + void sendCustomAction(String packageName, ISessionControllerCallback caller, + String action, in Bundle args); + MediaMetadata getMetadata(); + PlaybackState getPlaybackState(); + ParceledListSlice getQueue(); + CharSequence getQueueTitle(); + Bundle getExtras(); + int getRatingType(); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl new file mode 100644 index 0000000000..cf3176706d --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 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.session; + +import android.content.pm.ParceledListSlice; +import android.media.MediaMetadata; +import android.media.session.ParcelableVolumeInfo; +import android.media.session.PlaybackState; +import android.media.session.MediaSession; +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ISessionControllerCallback { + void onEvent(String event, in Bundle extras); + void onSessionDestroyed(); + + // These callbacks are for the TransportController + void onPlaybackStateChanged(in PlaybackState state); + void onMetadataChanged(in MediaMetadata metadata); + void onQueueChanged(in ParceledListSlice queue); + void onQueueTitleChanged(CharSequence title); + void onExtrasChanged(in Bundle extras); + void onVolumeInfoChanged(in ParcelableVolumeInfo info); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl new file mode 100644 index 0000000000..3578c16036 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 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.session; + +import android.content.ComponentName; +import android.media.IRemoteVolumeController; +import android.media.ISessionTokensListener; +import android.media.session.IActiveSessionsListener; +import android.media.session.ICallback; +import android.media.session.IOnMediaKeyListener; +import android.media.session.IOnVolumeKeyLongPressListener; +import android.media.session.ISession; +import android.media.session.ISessionCallback; +import android.os.Bundle; +import android.view.KeyEvent; + +/** + * Interface to the MediaSessionManagerService + * @hide + */ +interface ISessionManager { + ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId); + List getSessions(in ComponentName compName, int userId); + void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, + boolean needWakeLock); + void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, + int stream, boolean musicOnly); + void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags); + void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, + int userId); + void removeSessionsListener(in IActiveSessionsListener listener); + + // This is for the system volume UI only + void setRemoteVolumeController(in IRemoteVolumeController rvc); + + // For PhoneWindowManager to precheck media keys + boolean isGlobalPriorityActive(); + + void setCallback(in ICallback callback); + void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); + void setOnMediaKeyListener(in IOnMediaKeyListener listener); + + // MediaSession2 + boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); + boolean createSession2(in Bundle sessionToken); + void destroySession2(in Bundle sessionToken); + List getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly, + String packageName); + + void addSessionTokensListener(in ISessionTokensListener listener, int userId, + String packageName); + void removeSessionTokensListener(in ISessionTokensListener listener, String packageName); +} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java new file mode 100644 index 0000000000..c4b82c3141 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -0,0 +1,1187 @@ +/* + * Copyright (C) 2014 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.session; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UnsupportedAppUsage; +import android.app.PendingIntent; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.MediaMetadata; +import android.media.Rating; +import android.media.VolumeProvider; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * Allows an app to interact with an ongoing media session. Media buttons and + * other commands can be sent to the session. A callback may be registered to + * receive updates from the session, such as metadata and play state changes. + *

    + * A MediaController can be created through {@link MediaSessionManager} if you + * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an + * enabled notification listener or by getting a {@link MediaSession.Token} + * directly from the session owner. + *

    + * MediaController objects are thread-safe. + */ +public final class MediaController { + private static final String TAG = "MediaController"; + + private static final int MSG_EVENT = 1; + private static final int MSG_UPDATE_PLAYBACK_STATE = 2; + private static final int MSG_UPDATE_METADATA = 3; + private static final int MSG_UPDATE_VOLUME = 4; + private static final int MSG_UPDATE_QUEUE = 5; + private static final int MSG_UPDATE_QUEUE_TITLE = 6; + private static final int MSG_UPDATE_EXTRAS = 7; + private static final int MSG_DESTROYED = 8; + + private final ISessionController mSessionBinder; + + private final MediaSession.Token mToken; + private final Context mContext; + private final CallbackStub mCbStub = new CallbackStub(this); + private final ArrayList mCallbacks = new ArrayList(); + private final Object mLock = new Object(); + + private boolean mCbRegistered = false; + private String mPackageName; + private String mTag; + + private final TransportControls mTransportControls; + + /** + * Call for creating a MediaController directly from a binder. Should only + * be used by framework code. + * + * @hide + */ + public MediaController(Context context, ISessionController sessionBinder) { + if (sessionBinder == null) { + throw new IllegalArgumentException("Session token cannot be null"); + } + if (context == null) { + throw new IllegalArgumentException("Context cannot be null"); + } + mSessionBinder = sessionBinder; + mTransportControls = new TransportControls(); + mToken = new MediaSession.Token(sessionBinder); + mContext = context; + } + + /** + * Create a new MediaController from a session's token. + * + * @param context The caller's context. + * @param token The token for the session. + */ + public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) { + this(context, token.getBinder()); + } + + /** + * Get a {@link TransportControls} instance to send transport actions to + * the associated session. + * + * @return A transport controls instance. + */ + public @NonNull TransportControls getTransportControls() { + return mTransportControls; + } + + /** + * Send the specified media button event to the session. Only media keys can + * be sent by this method, other keys will be ignored. + * + * @param keyEvent The media button event to dispatch. + * @return true if the event was sent to the session, false otherwise. + */ + public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { + return dispatchMediaButtonEventInternal(false, keyEvent); + } + + /** + * Dispatches the media button event as system service to the session. + *

    + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param keyEvent media key event + * @return {@code true} if the event was sent to the session, {@code false} otherwise + * @hide + */ + public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { + return dispatchMediaButtonEventInternal(true, keyEvent); + } + + private boolean dispatchMediaButtonEventInternal(boolean asSystemService, + @NonNull KeyEvent keyEvent) { + if (keyEvent == null) { + throw new IllegalArgumentException("KeyEvent may not be null"); + } + if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + return false; + } + try { + return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub, + asSystemService, keyEvent); + } catch (RemoteException e) { + // System is dead. =( + } + return false; + } + + /** + * Dispatches the volume button event as system service to the session. + *

    + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param keyEvent volume key event + * @hide + */ + public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { + switch (keyEvent.getAction()) { + case KeyEvent.ACTION_DOWN: { + int direction = 0; + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_VOLUME_UP: + direction = AudioManager.ADJUST_RAISE; + break; + case KeyEvent.KEYCODE_VOLUME_DOWN: + direction = AudioManager.ADJUST_LOWER; + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + direction = AudioManager.ADJUST_TOGGLE_MUTE; + break; + } + try { + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, + direction, AudioManager.FLAG_SHOW_UI); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy", e); + } + } + + case KeyEvent.ACTION_UP: { + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE + | AudioManager.FLAG_FROM_KEY; + try { + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0, + flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy", e); + } + } + } + } + + /** + * Get the current playback state for this session. + * + * @return The current PlaybackState or null + */ + public @Nullable PlaybackState getPlaybackState() { + try { + return mSessionBinder.getPlaybackState(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getPlaybackState.", e); + return null; + } + } + + /** + * Get the current metadata for this session. + * + * @return The current MediaMetadata or null. + */ + public @Nullable MediaMetadata getMetadata() { + try { + return mSessionBinder.getMetadata(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getMetadata.", e); + return null; + } + } + + /** + * Get the current play queue for this session if one is set. If you only + * care about the current item {@link #getMetadata()} should be used. + * + * @return The current play queue or null. + */ + public @Nullable List getQueue() { + try { + ParceledListSlice queue = mSessionBinder.getQueue(); + if (queue != null) { + return queue.getList(); + } + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getQueue.", e); + } + return null; + } + + /** + * Get the queue title for this session. + */ + public @Nullable CharSequence getQueueTitle() { + try { + return mSessionBinder.getQueueTitle(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getQueueTitle", e); + } + return null; + } + + /** + * Get the extras for this session. + */ + public @Nullable Bundle getExtras() { + try { + return mSessionBinder.getExtras(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getExtras", e); + } + return null; + } + + /** + * Get the rating type supported by the session. One of: + *

      + *
    • {@link Rating#RATING_NONE}
    • + *
    • {@link Rating#RATING_HEART}
    • + *
    • {@link Rating#RATING_THUMB_UP_DOWN}
    • + *
    • {@link Rating#RATING_3_STARS}
    • + *
    • {@link Rating#RATING_4_STARS}
    • + *
    • {@link Rating#RATING_5_STARS}
    • + *
    • {@link Rating#RATING_PERCENTAGE}
    • + *
    + * + * @return The supported rating type + */ + public int getRatingType() { + try { + return mSessionBinder.getRatingType(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getRatingType.", e); + return Rating.RATING_NONE; + } + } + + /** + * Get the flags for this session. Flags are defined in {@link MediaSession}. + * + * @return The current set of flags for the session. + */ + public @MediaSession.SessionFlags long getFlags() { + try { + return mSessionBinder.getFlags(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getFlags.", e); + } + return 0; + } + + /** + * Get the current playback info for this session. + * + * @return The current playback info or null. + */ + public @Nullable PlaybackInfo getPlaybackInfo() { + try { + ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes(); + return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType, + result.maxVolume, result.currentVolume); + + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getAudioInfo.", e); + } + return null; + } + + /** + * Get an intent for launching UI associated with this session if one + * exists. + * + * @return A {@link PendingIntent} to launch UI or null. + */ + public @Nullable PendingIntent getSessionActivity() { + try { + return mSessionBinder.getLaunchPendingIntent(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getPendingIntent.", e); + } + return null; + } + + /** + * Get the token for the session this is connected to. + * + * @return The token for the connected session. + */ + public @NonNull MediaSession.Token getSessionToken() { + return mToken; + } + + /** + * Set the volume of the output this session is playing on. The command will + * be ignored if it does not support + * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in + * {@link AudioManager} may be used to affect the handling. + * + * @see #getPlaybackInfo() + * @param value The value to set it to, between 0 and the reported max. + * @param flags Flags from {@link AudioManager} to include with the volume + * request. + */ + public void setVolumeTo(int value, int flags) { + try { + mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling setVolumeTo.", e); + } + } + + /** + * Adjust the volume of the output this session is playing on. The direction + * must be one of {@link AudioManager#ADJUST_LOWER}, + * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}. + * The command will be ignored if the session does not support + * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or + * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in + * {@link AudioManager} may be used to affect the handling. + * + * @see #getPlaybackInfo() + * @param direction The direction to adjust the volume in. + * @param flags Any flags to pass with the command. + */ + public void adjustVolume(int direction, int flags) { + try { + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction, + flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy.", e); + } + } + + /** + * Registers a callback to receive updates from the Session. Updates will be + * posted on the caller's thread. + * + * @param callback The callback object, must not be null. + */ + public void registerCallback(@NonNull Callback callback) { + registerCallback(callback, null); + } + + /** + * Registers a callback to receive updates from the session. Updates will be + * posted on the specified handler's thread. + * + * @param callback The callback object, must not be null. + * @param handler The handler to post updates on. If null the callers thread + * will be used. + */ + public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + if (handler == null) { + handler = new Handler(); + } + synchronized (mLock) { + addCallbackLocked(callback, handler); + } + } + + /** + * Unregisters the specified callback. If an update has already been posted + * you may still receive it after calling this method. + * + * @param callback The callback to remove. + */ + public void unregisterCallback(@NonNull Callback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + synchronized (mLock) { + removeCallbackLocked(callback); + } + } + + /** + * Sends a generic command to the session. It is up to the session creator + * to decide what commands and parameters they will support. As such, + * commands should only be sent to sessions that the controller owns. + * + * @param command The command to send + * @param args Any parameters to include with the command + * @param cb The callback to receive the result on + */ + public void sendCommand(@NonNull String command, @Nullable Bundle args, + @Nullable ResultReceiver cb) { + if (TextUtils.isEmpty(command)) { + throw new IllegalArgumentException("command cannot be null or empty"); + } + try { + mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in sendCommand.", e); + } + } + + /** + * Get the session owner's package name. + * + * @return The package name of of the session owner. + */ + public String getPackageName() { + if (mPackageName == null) { + try { + mPackageName = mSessionBinder.getPackageName(); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in getPackageName.", e); + } + } + return mPackageName; + } + + /** + * Get the session's tag for debugging purposes. + * + * @return The session's tag. + * @hide + */ + public String getTag() { + if (mTag == null) { + try { + mTag = mSessionBinder.getTag(); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in getTag.", e); + } + } + return mTag; + } + + /* + * @hide + */ + ISessionController getSessionBinder() { + return mSessionBinder; + } + + /** + * @hide + */ + @UnsupportedAppUsage + public boolean controlsSameSession(MediaController other) { + if (other == null) return false; + return mSessionBinder.asBinder() == other.getSessionBinder().asBinder(); + } + + private void addCallbackLocked(Callback cb, Handler handler) { + if (getHandlerForCallbackLocked(cb) != null) { + Log.w(TAG, "Callback is already added, ignoring"); + return; + } + MessageHandler holder = new MessageHandler(handler.getLooper(), cb); + mCallbacks.add(holder); + holder.mRegistered = true; + + if (!mCbRegistered) { + try { + mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub); + mCbRegistered = true; + } catch (RemoteException e) { + Log.e(TAG, "Dead object in registerCallback", e); + } + } + } + + private boolean removeCallbackLocked(Callback cb) { + boolean success = false; + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + mCallbacks.remove(i); + success = true; + handler.mRegistered = false; + } + } + if (mCbRegistered && mCallbacks.size() == 0) { + try { + mSessionBinder.unregisterCallbackListener(mCbStub); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in removeCallbackLocked"); + } + mCbRegistered = false; + } + return success; + } + + private MessageHandler getHandlerForCallbackLocked(Callback cb) { + if (cb == null) { + throw new IllegalArgumentException("Callback cannot be null"); + } + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + MessageHandler handler = mCallbacks.get(i); + if (cb == handler.mCallback) { + return handler; + } + } + return null; + } + + private final void postMessage(int what, Object obj, Bundle data) { + synchronized (mLock) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(what, obj, data); + } + } + } + + /** + * Callback for receiving updates from the session. A Callback can be + * registered using {@link #registerCallback}. + */ + public static abstract class Callback { + /** + * Override to handle the session being destroyed. The session is no + * longer valid after this call and calls to it will be ignored. + */ + public void onSessionDestroyed() { + } + + /** + * Override to handle custom events sent by the session owner without a + * specified interface. Controllers should only handle these for + * sessions they own. + * + * @param event The event from the session. + * @param extras Optional parameters for the event, may be null. + */ + public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) { + } + + /** + * Override to handle changes in playback state. + * + * @param state The new playback state of the session + */ + public void onPlaybackStateChanged(@Nullable PlaybackState state) { + } + + /** + * Override to handle changes to the current metadata. + * + * @param metadata The current metadata for the session or null if none. + * @see MediaMetadata + */ + public void onMetadataChanged(@Nullable MediaMetadata metadata) { + } + + /** + * Override to handle changes to items in the queue. + * + * @param queue A list of items in the current play queue. It should + * include the currently playing item as well as previous and + * upcoming items if applicable. + * @see MediaSession.QueueItem + */ + public void onQueueChanged(@Nullable List queue) { + } + + /** + * Override to handle changes to the queue title. + * + * @param title The title that should be displayed along with the play queue such as + * "Now Playing". May be null if there is no such title. + */ + public void onQueueTitleChanged(@Nullable CharSequence title) { + } + + /** + * Override to handle changes to the {@link MediaSession} extras. + * + * @param extras The extras that can include other information associated with the + * {@link MediaSession}. + */ + public void onExtrasChanged(@Nullable Bundle extras) { + } + + /** + * Override to handle changes to the audio info. + * + * @param info The current audio info for this session. + */ + public void onAudioInfoChanged(PlaybackInfo info) { + } + } + + /** + * Interface for controlling media playback on a session. This allows an app + * to send media transport commands to the session. + */ + public final class TransportControls { + private static final String TAG = "TransportController"; + + private TransportControls() { + } + + /** + * Request that the player prepare its playback. In other words, other sessions can continue + * to play during the preparation of this session. This method can be used to speed up the + * start of the playback. Once the preparation is done, the session will change its playback + * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to + * start playback. + */ + public void prepare() { + try { + mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling prepare.", e); + } + } + + /** + * Request that the player prepare playback for a specific media id. In other words, other + * sessions can continue to play during the preparation of this session. This method can be + * used to speed up the start of the playback. Once the preparation is done, the session + * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromMediaId} can be directly called without this method. + * + * @param mediaId The id of the requested media. + * @param extras Optional extras that can include extra information about the media item + * to be prepared. + */ + public void prepareFromMediaId(String mediaId, Bundle extras) { + if (TextUtils.isEmpty(mediaId)) { + throw new IllegalArgumentException( + "You must specify a non-empty String for prepareFromMediaId."); + } + try { + mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); + } + } + + /** + * Request that the player prepare playback for a specific search query. An empty or null + * query should be treated as a request to prepare any music. In other words, other sessions + * can continue to play during the preparation of this session. This method can be used to + * speed up the start of the playback. Once the preparation is done, the session will + * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromSearch} can be directly called without this method. + * + * @param query The search query. + * @param extras Optional extras that can include extra information + * about the query. + */ + public void prepareFromSearch(String query, Bundle extras) { + if (query == null) { + // This is to remain compatible with + // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH + query = ""; + } + try { + mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query, + extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling prepare(" + query + ").", e); + } + } + + /** + * Request that the player prepare playback for a specific {@link Uri}. In other words, + * other sessions can continue to play during the preparation of this session. This method + * can be used to speed up the start of the playback. Once the preparation is done, the + * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromUri} can be directly called without this method. + * + * @param uri The URI of the requested media. + * @param extras Optional extras that can include extra information about the media item + * to be prepared. + */ + public void prepareFromUri(Uri uri, Bundle extras) { + if (uri == null || Uri.EMPTY.equals(uri)) { + throw new IllegalArgumentException( + "You must specify a non-empty Uri for prepareFromUri."); + } + try { + mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); + } + } + + /** + * Request that the player start its playback at its current position. + */ + public void play() { + try { + mSessionBinder.play(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling play.", e); + } + } + + /** + * Request that the player start playback for a specific media id. + * + * @param mediaId The id of the requested media. + * @param extras Optional extras that can include extra information about the media item + * to be played. + */ + public void playFromMediaId(String mediaId, Bundle extras) { + if (TextUtils.isEmpty(mediaId)) { + throw new IllegalArgumentException( + "You must specify a non-empty String for playFromMediaId."); + } + try { + mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); + } + } + + /** + * Request that the player start playback for a specific search query. + * An empty or null query should be treated as a request to play any + * music. + * + * @param query The search query. + * @param extras Optional extras that can include extra information + * about the query. + */ + public void playFromSearch(String query, Bundle extras) { + if (query == null) { + // This is to remain compatible with + // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH + query = ""; + } + try { + mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling play(" + query + ").", e); + } + } + + /** + * Request that the player start playback for a specific {@link Uri}. + * + * @param uri The URI of the requested media. + * @param extras Optional extras that can include extra information about the media item + * to be played. + */ + public void playFromUri(Uri uri, Bundle extras) { + if (uri == null || Uri.EMPTY.equals(uri)) { + throw new IllegalArgumentException( + "You must specify a non-empty Uri for playFromUri."); + } + try { + mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling play(" + uri + ").", e); + } + } + + /** + * Play an item with a specific id in the play queue. If you specify an + * id that is not in the play queue, the behavior is undefined. + */ + public void skipToQueueItem(long id) { + try { + mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); + } + } + + /** + * Request that the player pause its playback and stay at its current + * position. + */ + public void pause() { + try { + mSessionBinder.pause(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling pause.", e); + } + } + + /** + * Request that the player stop its playback; it may clear its state in + * whatever way is appropriate. + */ + public void stop() { + try { + mSessionBinder.stop(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling stop.", e); + } + } + + /** + * Move to a new location in the media stream. + * + * @param pos Position to move to, in milliseconds. + */ + public void seekTo(long pos) { + try { + mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling seekTo.", e); + } + } + + /** + * Start fast forwarding. If playback is already fast forwarding this + * may increase the rate. + */ + public void fastForward() { + try { + mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling fastForward.", e); + } + } + + /** + * Skip to the next item. + */ + public void skipToNext() { + try { + mSessionBinder.next(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling next.", e); + } + } + + /** + * Start rewinding. If playback is already rewinding this may increase + * the rate. + */ + public void rewind() { + try { + mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling rewind.", e); + } + } + + /** + * Skip to the previous item. + */ + public void skipToPrevious() { + try { + mSessionBinder.previous(mContext.getOpPackageName(), mCbStub); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling previous.", e); + } + } + + /** + * Rate the current content. This will cause the rating to be set for + * the current user. The Rating type must match the type returned by + * {@link #getRatingType()}. + * + * @param rating The rating to set for the current content + */ + public void setRating(Rating rating) { + try { + mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling rate.", e); + } + } + + /** + * Send a custom action back for the {@link MediaSession} to perform. + * + * @param customAction The action to perform. + * @param args Optional arguments to supply to the {@link MediaSession} for this + * custom action. + */ + public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, + @Nullable Bundle args) { + if (customAction == null) { + throw new IllegalArgumentException("CustomAction cannot be null."); + } + sendCustomAction(customAction.getAction(), args); + } + + /** + * Send the id and args from a custom action back for the {@link MediaSession} to perform. + * + * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args) + * @param action The action identifier of the {@link PlaybackState.CustomAction} as + * specified by the {@link MediaSession}. + * @param args Optional arguments to supply to the {@link MediaSession} for this + * custom action. + */ + public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { + if (TextUtils.isEmpty(action)) { + throw new IllegalArgumentException("CustomAction cannot be null."); + } + try { + mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in sendCustomAction.", e); + } + } + } + + /** + * Holds information about the current playback and how audio is handled for + * this session. + */ + public static final class PlaybackInfo { + /** + * The session uses remote playback. + */ + public static final int PLAYBACK_TYPE_REMOTE = 2; + /** + * The session uses local playback. + */ + public static final int PLAYBACK_TYPE_LOCAL = 1; + + private final int mVolumeType; + private final int mVolumeControl; + private final int mMaxVolume; + private final int mCurrentVolume; + private final AudioAttributes mAudioAttrs; + + /** + * @hide + */ + public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) { + mVolumeType = type; + mAudioAttrs = attrs; + mVolumeControl = control; + mMaxVolume = max; + mCurrentVolume = current; + } + + /** + * Get the type of playback which affects volume handling. One of: + *
      + *
    • {@link #PLAYBACK_TYPE_LOCAL}
    • + *
    • {@link #PLAYBACK_TYPE_REMOTE}
    • + *
    + * + * @return The type of playback this session is using. + */ + public int getPlaybackType() { + return mVolumeType; + } + + /** + * Get the audio attributes for this session. The attributes will affect + * volume handling for the session. When the volume type is + * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the + * remote volume handler. + * + * @return The attributes for this session. + */ + public AudioAttributes getAudioAttributes() { + return mAudioAttrs; + } + + /** + * Get the type of volume control that can be used. One of: + *
      + *
    • {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}
    • + *
    • {@link VolumeProvider#VOLUME_CONTROL_RELATIVE}
    • + *
    • {@link VolumeProvider#VOLUME_CONTROL_FIXED}
    • + *
    + * + * @return The type of volume control that may be used with this + * session. + */ + public int getVolumeControl() { + return mVolumeControl; + } + + /** + * Get the maximum volume that may be set for this session. + * + * @return The maximum allowed volume where this session is playing. + */ + public int getMaxVolume() { + return mMaxVolume; + } + + /** + * Get the current volume for this session. + * + * @return The current volume where this session is playing. + */ + public int getCurrentVolume() { + return mCurrentVolume; + } + } + + private final static class CallbackStub extends ISessionControllerCallback.Stub { + private final WeakReference mController; + + public CallbackStub(MediaController controller) { + mController = new WeakReference(controller); + } + + @Override + public void onSessionDestroyed() { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_DESTROYED, null, null); + } + } + + @Override + public void onEvent(String event, Bundle extras) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_EVENT, event, extras); + } + } + + @Override + public void onPlaybackStateChanged(PlaybackState state) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null); + } + } + + @Override + public void onMetadataChanged(MediaMetadata metadata) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_UPDATE_METADATA, metadata, null); + } + } + + @Override + public void onQueueChanged(ParceledListSlice parceledQueue) { + List queue = parceledQueue == null ? null : parceledQueue + .getList(); + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_UPDATE_QUEUE, queue, null); + } + } + + @Override + public void onQueueTitleChanged(CharSequence title) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null); + } + } + + @Override + public void onExtrasChanged(Bundle extras) { + MediaController controller = mController.get(); + if (controller != null) { + controller.postMessage(MSG_UPDATE_EXTRAS, extras, null); + } + } + + @Override + public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) { + MediaController controller = mController.get(); + if (controller != null) { + PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, + pvi.controlType, pvi.maxVolume, pvi.currentVolume); + controller.postMessage(MSG_UPDATE_VOLUME, info, null); + } + } + + } + + private final static class MessageHandler extends Handler { + private final MediaController.Callback mCallback; + private boolean mRegistered = false; + + public MessageHandler(Looper looper, MediaController.Callback cb) { + super(looper, null, true); + mCallback = cb; + } + + @Override + public void handleMessage(Message msg) { + if (!mRegistered) { + return; + } + switch (msg.what) { + case MSG_EVENT: + mCallback.onSessionEvent((String) msg.obj, msg.getData()); + break; + case MSG_UPDATE_PLAYBACK_STATE: + mCallback.onPlaybackStateChanged((PlaybackState) msg.obj); + break; + case MSG_UPDATE_METADATA: + mCallback.onMetadataChanged((MediaMetadata) msg.obj); + break; + case MSG_UPDATE_QUEUE: + mCallback.onQueueChanged((List) msg.obj); + break; + case MSG_UPDATE_QUEUE_TITLE: + mCallback.onQueueTitleChanged((CharSequence) msg.obj); + break; + case MSG_UPDATE_EXTRAS: + mCallback.onExtrasChanged((Bundle) msg.obj); + break; + case MSG_UPDATE_VOLUME: + mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj); + break; + case MSG_DESTROYED: + mCallback.onSessionDestroyed(); + break; + } + } + + public void post(int what, Object obj, Bundle data) { + Message msg = obtainMessage(what, obj); + msg.setData(data); + msg.sendToTarget(); + } + } + +} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl new file mode 100644 index 0000000000..f657cef76b --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl @@ -0,0 +1,19 @@ +/* Copyright 2014, 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.session; + +parcelable MediaSession.Token; +parcelable MediaSession.QueueItem; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java new file mode 100644 index 0000000000..d43cd309d1 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -0,0 +1,1554 @@ +/* + * Copyright (C) 2014 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.session; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UnsupportedAppUsage; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.media.AudioAttributes; +import android.media.MediaDescription; +import android.media.MediaMetadata; +import android.media.Rating; +import android.media.VolumeProvider; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.UserHandle; +import android.media.session.MediaSessionManager.RemoteUserInfo; +import android.service.media.MediaBrowserService; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Objects; + +/** + * Allows interaction with media controllers, volume keys, media buttons, and + * transport controls. + *

    + * A MediaSession should be created when an app wants to publish media playback + * information or handle media keys. In general an app only needs one session + * for all playback, though multiple sessions can be created to provide finer + * grain controls of media. + *

    + * Once a session is created the owner of the session may pass its + * {@link #getSessionToken() session token} to other processes to allow them to + * create a {@link MediaController} to interact with the session. + *

    + * To receive commands, media keys, and other events a {@link Callback} must be + * set with {@link #setCallback(Callback)} and {@link #setActive(boolean) + * setActive(true)} must be called. + *

    + * When an app is finished performing playback it must call {@link #release()} + * to clean up the session and notify any controllers. + *

    + * MediaSession objects are thread safe. + */ +public final class MediaSession { + private static final String TAG = "MediaSession"; + + /** + * Set this flag on the session to indicate that it can handle media button + * events. + * @deprecated This flag is no longer used. All media sessions are expected to handle media + * button events now. + */ + @Deprecated + public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0; + + /** + * Set this flag on the session to indicate that it handles transport + * control commands through its {@link Callback}. + * @deprecated This flag is no longer used. All media sessions are expected to handle transport + * controls now. + */ + @Deprecated + public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; + + /** + * System only flag for a session that needs to have priority over all other + * sessions. This flag ensures this session will receive media button events + * regardless of the current ordering in the system. + * + * @hide + */ + public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16; + + /** + * @hide + */ + public static final int INVALID_UID = -1; + + /** + * @hide + */ + public static final int INVALID_PID = -1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + FLAG_HANDLES_MEDIA_BUTTONS, + FLAG_HANDLES_TRANSPORT_CONTROLS, + FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) + public @interface SessionFlags { } + + private final Object mLock = new Object(); + private final int mMaxBitmapSize; + + private final MediaSession.Token mSessionToken; + private final MediaController mController; + private final ISession mBinder; + private final CallbackStub mCbStub; + + // Do not change the name of mCallback. Support lib accesses this by using reflection. + @UnsupportedAppUsage + private CallbackMessageHandler mCallback; + private VolumeProvider mVolumeProvider; + private PlaybackState mPlaybackState; + + private boolean mActive = false; + + /** + * Creates a new session. The session will automatically be registered with + * the system but will not be published until {@link #setActive(boolean) + * setActive(true)} is called. You must call {@link #release()} when + * finished with the session. + * + * @param context The context to use to create the session. + * @param tag A short name for debugging purposes. + */ + public MediaSession(@NonNull Context context, @NonNull String tag) { + this(context, tag, UserHandle.myUserId()); + } + + /** + * Creates a new session as the specified user. To create a session as a + * user other than your own you must hold the + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} + * permission. + * + * @param context The context to use to create the session. + * @param tag A short name for debugging purposes. + * @param userId The user id to create the session as. + * @hide + */ + public MediaSession(@NonNull Context context, @NonNull String tag, int userId) { + if (context == null) { + throw new IllegalArgumentException("context cannot be null."); + } + if (TextUtils.isEmpty(tag)) { + throw new IllegalArgumentException("tag cannot be null or empty"); + } + mMaxBitmapSize = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); + mCbStub = new CallbackStub(this); + MediaSessionManager manager = (MediaSessionManager) context + .getSystemService(Context.MEDIA_SESSION_SERVICE); + try { + mBinder = manager.createSession(mCbStub, tag, userId); + mSessionToken = new Token(mBinder.getController()); + mController = new MediaController(context, mSessionToken); + } catch (RemoteException e) { + throw new RuntimeException("Remote error creating session.", e); + } + } + + /** + * Set the callback to receive updates for the MediaSession. This includes + * media button events and transport controls. The caller's thread will be + * used to post updates. + *

    + * Set the callback to null to stop receiving updates. + * + * @param callback The callback object + */ + public void setCallback(@Nullable Callback callback) { + setCallback(callback, null); + } + + /** + * Set the callback to receive updates for the MediaSession. This includes + * media button events and transport controls. + *

    + * Set the callback to null to stop receiving updates. + * + * @param callback The callback to receive updates on. + * @param handler The handler that events should be posted on. + */ + public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { + synchronized (mLock) { + if (mCallback != null) { + // We're updating the callback, clear the session from the old one. + mCallback.mCallback.mSession = null; + mCallback.removeCallbacksAndMessages(null); + } + if (callback == null) { + mCallback = null; + return; + } + if (handler == null) { + handler = new Handler(); + } + callback.mSession = this; + CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(), + callback); + mCallback = msgHandler; + } + } + + /** + * Set an intent for launching UI for this Session. This can be used as a + * quick link to an ongoing media screen. The intent should be for an + * activity that may be started using {@link Activity#startActivity(Intent)}. + * + * @param pi The intent to launch to show UI for this Session. + */ + public void setSessionActivity(@Nullable PendingIntent pi) { + try { + mBinder.setLaunchPendingIntent(pi); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e); + } + } + + /** + * Set a pending intent for your media button receiver to allow restarting + * playback after the session has been stopped. If your app is started in + * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via + * the pending intent. + * + * @param mbr The {@link PendingIntent} to send the media button event to. + */ + public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { + try { + mBinder.setMediaButtonReceiver(mbr); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e); + } + } + + /** + * Set any flags for the session. + * + * @param flags The flags to set for this session. + */ + public void setFlags(@SessionFlags int flags) { + try { + mBinder.setFlags(flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setFlags.", e); + } + } + + /** + * Set the attributes for this session's audio. This will affect the + * system's volume handling for this session. If + * {@link #setPlaybackToRemote} was previously called it will stop receiving + * volume commands and the system will begin sending volume changes to the + * appropriate stream. + *

    + * By default sessions use attributes for media. + * + * @param attributes The {@link AudioAttributes} for this session's audio. + */ + public void setPlaybackToLocal(AudioAttributes attributes) { + if (attributes == null) { + throw new IllegalArgumentException("Attributes cannot be null for local playback."); + } + try { + mBinder.setPlaybackToLocal(attributes); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); + } + } + + /** + * Configure this session to use remote volume handling. This must be called + * to receive volume button events, otherwise the system will adjust the + * appropriate stream volume for this session. If + * {@link #setPlaybackToLocal} was previously called the system will stop + * handling volume changes for this session and pass them to the volume + * provider instead. + * + * @param volumeProvider The provider that will handle volume changes. May + * not be null. + */ + public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) { + if (volumeProvider == null) { + throw new IllegalArgumentException("volumeProvider may not be null!"); + } + synchronized (mLock) { + mVolumeProvider = volumeProvider; + } + volumeProvider.setCallback(new VolumeProvider.Callback() { + @Override + public void onVolumeChanged(VolumeProvider volumeProvider) { + notifyRemoteVolumeChanged(volumeProvider); + } + }); + + try { + mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(), + volumeProvider.getMaxVolume()); + mBinder.setCurrentVolume(volumeProvider.getCurrentVolume()); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); + } + } + + /** + * Set if this session is currently active and ready to receive commands. If + * set to false your session's controller may not be discoverable. You must + * set the session to active before it can start receiving media button + * events or transport commands. + * + * @param active Whether this session is active or not. + */ + public void setActive(boolean active) { + if (mActive == active) { + return; + } + try { + mBinder.setActive(active); + mActive = active; + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setActive.", e); + } + } + + /** + * Get the current active state of this session. + * + * @return True if the session is active, false otherwise. + */ + public boolean isActive() { + return mActive; + } + + /** + * Send a proprietary event to all MediaControllers listening to this + * Session. It's up to the Controller/Session owner to determine the meaning + * of any events. + * + * @param event The name of the event to send + * @param extras Any extras included with the event + */ + public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) { + if (TextUtils.isEmpty(event)) { + throw new IllegalArgumentException("event cannot be null or empty"); + } + try { + mBinder.sendEvent(event, extras); + } catch (RemoteException e) { + Log.wtf(TAG, "Error sending event", e); + } + } + + /** + * This must be called when an app has finished performing playback. If + * playback is expected to start again shortly the session can be left open, + * but it must be released if your activity or service is being destroyed. + */ + public void release() { + try { + mBinder.destroy(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error releasing session: ", e); + } + } + + /** + * Retrieve a token object that can be used by apps to create a + * {@link MediaController} for interacting with this session. The owner of + * the session is responsible for deciding how to distribute these tokens. + * + * @return A token that can be used to create a MediaController for this + * session + */ + public @NonNull Token getSessionToken() { + return mSessionToken; + } + + /** + * Get a controller for this session. This is a convenience method to avoid + * having to cache your own controller in process. + * + * @return A controller for this session. + */ + public @NonNull MediaController getController() { + return mController; + } + + /** + * Update the current playback state. + * + * @param state The current state of playback + */ + public void setPlaybackState(@Nullable PlaybackState state) { + mPlaybackState = state; + try { + mBinder.setPlaybackState(state); + } catch (RemoteException e) { + Log.wtf(TAG, "Dead object in setPlaybackState.", e); + } + } + + /** + * Update the current metadata. New metadata can be created using + * {@link android.media.MediaMetadata.Builder}. This operation may take time proportional to + * the size of the bitmap to replace large bitmaps with a scaled down copy. + * + * @param metadata The new metadata + * @see android.media.MediaMetadata.Builder#putBitmap + */ + public void setMetadata(@Nullable MediaMetadata metadata) { + if (metadata != null) { + metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build(); + } + try { + mBinder.setMetadata(metadata); + } catch (RemoteException e) { + Log.wtf(TAG, "Dead object in setPlaybackState.", e); + } + } + + /** + * Update the list of items in the play queue. It is an ordered list and + * should contain the current item, and previous or upcoming items if they + * exist. Specify null if there is no current play queue. + *

    + * The queue should be of reasonable size. If the play queue is unbounded + * within your app, it is better to send a reasonable amount in a sliding + * window instead. + * + * @param queue A list of items in the play queue. + */ + public void setQueue(@Nullable List queue) { + try { + mBinder.setQueue(queue == null ? null : new ParceledListSlice(queue)); + } catch (RemoteException e) { + Log.wtf("Dead object in setQueue.", e); + } + } + + /** + * Set the title of the play queue. The UI should display this title along + * with the play queue itself. + * e.g. "Play Queue", "Now Playing", or an album name. + * + * @param title The title of the play queue. + */ + public void setQueueTitle(@Nullable CharSequence title) { + try { + mBinder.setQueueTitle(title); + } catch (RemoteException e) { + Log.wtf("Dead object in setQueueTitle.", e); + } + } + + /** + * Set the style of rating used by this session. Apps trying to set the + * rating should use this style. Must be one of the following: + *

      + *
    • {@link Rating#RATING_NONE}
    • + *
    • {@link Rating#RATING_3_STARS}
    • + *
    • {@link Rating#RATING_4_STARS}
    • + *
    • {@link Rating#RATING_5_STARS}
    • + *
    • {@link Rating#RATING_HEART}
    • + *
    • {@link Rating#RATING_PERCENTAGE}
    • + *
    • {@link Rating#RATING_THUMB_UP_DOWN}
    • + *
    + */ + public void setRatingType(@Rating.Style int type) { + try { + mBinder.setRatingType(type); + } catch (RemoteException e) { + Log.e(TAG, "Error in setRatingType.", e); + } + } + + /** + * Set some extras that can be associated with the {@link MediaSession}. No assumptions should + * be made as to how a {@link MediaController} will handle these extras. + * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts. + * + * @param extras The extras associated with the {@link MediaSession}. + */ + public void setExtras(@Nullable Bundle extras) { + try { + mBinder.setExtras(extras); + } catch (RemoteException e) { + Log.wtf("Dead object in setExtras.", e); + } + } + + /** + * Gets the controller information who sent the current request. + *

    + * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}. + * + * @throws IllegalStateException If this method is called outside of {@link Callback} methods. + * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) + */ + public final @NonNull RemoteUserInfo getCurrentControllerInfo() { + if (mCallback == null || mCallback.mCurrentControllerInfo == null) { + throw new IllegalStateException( + "This should be called inside of MediaSession.Callback methods"); + } + return mCallback.mCurrentControllerInfo; + } + + /** + * Notify the system that the remote volume changed. + * + * @param provider The provider that is handling volume changes. + * @hide + */ + public void notifyRemoteVolumeChanged(VolumeProvider provider) { + synchronized (mLock) { + if (provider == null || provider != mVolumeProvider) { + Log.w(TAG, "Received update from stale volume provider"); + return; + } + } + try { + mBinder.setCurrentVolume(provider.getCurrentVolume()); + } catch (RemoteException e) { + Log.e(TAG, "Error in notifyVolumeChanged", e); + } + } + + /** + * Returns the name of the package that sent the last media button, transport control, or + * command from controllers and the system. This is only valid while in a request callback, such + * as {@link Callback#onPlay}. + * + * @hide + */ + @UnsupportedAppUsage + public String getCallingPackage() { + if (mCallback != null && mCallback.mCurrentControllerInfo != null) { + return mCallback.mCurrentControllerInfo.getPackageName(); + } + return null; + } + + private void dispatchPrepare(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null); + } + + private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); + } + + private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras); + } + + private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras); + } + + private void dispatchPlay(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null); + } + + private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); + } + + private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras); + } + + private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { + postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras); + } + + private void dispatchSkipToItem(RemoteUserInfo caller, long id) { + postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null); + } + + private void dispatchPause(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null); + } + + private void dispatchStop(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null); + } + + private void dispatchNext(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null); + } + + private void dispatchPrevious(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null); + } + + private void dispatchFastForward(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null); + } + + private void dispatchRewind(RemoteUserInfo caller) { + postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null); + } + + private void dispatchSeekTo(RemoteUserInfo caller, long pos) { + postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null); + } + + private void dispatchRate(RemoteUserInfo caller, Rating rating) { + postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null); + } + + private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) { + postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); + } + + private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) { + postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null); + } + + private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent, + long delay) { + postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, + mediaButtonIntent, null, delay); + } + + private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) { + postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null); + } + + private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) { + postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null); + } + + private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args, + ResultReceiver resultCb) { + Command cmd = new Command(command, args, resultCb); + postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null); + } + + private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) { + postToCallbackDelayed(caller, what, obj, data, 0); + } + + private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data, + long delay) { + synchronized (mLock) { + if (mCallback != null) { + mCallback.post(caller, what, obj, data, delay); + } + } + } + + /** + * Return true if this is considered an active playback state. + * + * @hide + */ + public static boolean isActiveState(int state) { + switch (state) { + case PlaybackState.STATE_FAST_FORWARDING: + case PlaybackState.STATE_REWINDING: + case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: + case PlaybackState.STATE_SKIPPING_TO_NEXT: + case PlaybackState.STATE_BUFFERING: + case PlaybackState.STATE_CONNECTING: + case PlaybackState.STATE_PLAYING: + return true; + } + return false; + } + + /** + * Represents an ongoing session. This may be passed to apps by the session + * owner to allow them to create a {@link MediaController} to communicate with + * the session. + */ + public static final class Token implements Parcelable { + + private ISessionController mBinder; + + /** + * @hide + */ + public Token(ISessionController binder) { + mBinder = binder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mBinder.asBinder()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mBinder == null) ? 0 : mBinder.asBinder().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Token other = (Token) obj; + if (mBinder == null) { + if (other.mBinder != null) + return false; + } else if (!mBinder.asBinder().equals(other.mBinder.asBinder())) + return false; + return true; + } + + ISessionController getBinder() { + return mBinder; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public Token createFromParcel(Parcel in) { + return new Token(ISessionController.Stub.asInterface(in.readStrongBinder())); + } + + @Override + public Token[] newArray(int size) { + return new Token[size]; + } + }; + } + + /** + * Receives media buttons, transport controls, and commands from controllers + * and the system. A callback may be set using {@link #setCallback}. + */ + public abstract static class Callback { + + private MediaSession mSession; + private CallbackMessageHandler mHandler; + private boolean mMediaPlayPauseKeyPending; + + public Callback() { + } + + /** + * Called when a controller has sent a command to this session. + * The owner of the session may handle custom commands but is not + * required to. + * + * @param command The command name. + * @param args Optional parameters for the command, may be null. + * @param cb A result receiver to which a result may be sent by the command, may be null. + */ + public void onCommand(@NonNull String command, @Nullable Bundle args, + @Nullable ResultReceiver cb) { + } + + /** + * Called when a media button is pressed and this session has the + * highest priority or a controller sends a media button event to the + * session. The default behavior will call the relevant method if the + * action for it was set. + *

    + * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a + * KeyEvent in {@link Intent#EXTRA_KEY_EVENT} + * + * @param mediaButtonIntent an intent containing the KeyEvent as an + * extra + * @return True if the event was handled, false otherwise. + */ + public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) { + if (mSession != null && mHandler != null + && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) { + KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) { + PlaybackState state = mSession.mPlaybackState; + long validActions = state == null ? 0 : state.getActions(); + switch (ke.getKeyCode()) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: + if (ke.getRepeatCount() > 0) { + // Consider long-press as a single tap. + handleMediaPlayPauseKeySingleTapIfPending(); + } else if (mMediaPlayPauseKeyPending) { + // Consider double tap as the next. + mHandler.removeMessages(CallbackMessageHandler + .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); + mMediaPlayPauseKeyPending = false; + if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { + onSkipToNext(); + } + } else { + mMediaPlayPauseKeyPending = true; + mSession.dispatchMediaButtonDelayed( + mSession.getCurrentControllerInfo(), + mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout()); + } + return true; + default: + // If another key is pressed within double tap timeout, consider the + // pending play/pause as a single tap to handle media keys in order. + handleMediaPlayPauseKeySingleTapIfPending(); + break; + } + + switch (ke.getKeyCode()) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + if ((validActions & PlaybackState.ACTION_PLAY) != 0) { + onPlay(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_PAUSE: + if ((validActions & PlaybackState.ACTION_PAUSE) != 0) { + onPause(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_NEXT: + if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { + onSkipToNext(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) { + onSkipToPrevious(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_STOP: + if ((validActions & PlaybackState.ACTION_STOP) != 0) { + onStop(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) { + onFastForward(); + return true; + } + break; + case KeyEvent.KEYCODE_MEDIA_REWIND: + if ((validActions & PlaybackState.ACTION_REWIND) != 0) { + onRewind(); + return true; + } + break; + } + } + } + return false; + } + + private void handleMediaPlayPauseKeySingleTapIfPending() { + if (!mMediaPlayPauseKeyPending) { + return; + } + mMediaPlayPauseKeyPending = false; + mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); + PlaybackState state = mSession.mPlaybackState; + long validActions = state == null ? 0 : state.getActions(); + boolean isPlaying = state != null + && state.getState() == PlaybackState.STATE_PLAYING; + boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE + | PlaybackState.ACTION_PLAY)) != 0; + boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE + | PlaybackState.ACTION_PAUSE)) != 0; + if (isPlaying && canPause) { + onPause(); + } else if (!isPlaying && canPlay) { + onPlay(); + } + } + + /** + * Override to handle requests to prepare playback. During the preparation, a session should + * not hold audio focus in order to allow other sessions play seamlessly. The state of + * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is + * done. + */ + public void onPrepare() { + } + + /** + * Override to handle requests to prepare for playing a specific mediaId that was provided + * by your app's {@link MediaBrowserService}. During the preparation, a session should not + * hold audio focus in order to allow other sessions play seamlessly. The state of playback + * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. + * The playback of the prepared content should start in the implementation of + * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting + * playback without preparation. + */ + public void onPrepareFromMediaId(String mediaId, Bundle extras) { + } + + /** + * Override to handle requests to prepare playback from a search query. An empty query + * indicates that the app may prepare any music. The implementation should attempt to make a + * smart choice about what to play. During the preparation, a session should not hold audio + * focus in order to allow other sessions play seamlessly. The state of playback should be + * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback + * of the prepared content should start in the implementation of {@link #onPlay}. Override + * {@link #onPlayFromSearch} to handle requests for starting playback without preparation. + */ + public void onPrepareFromSearch(String query, Bundle extras) { + } + + /** + * Override to handle requests to prepare a specific media item represented by a URI. + * During the preparation, a session should not hold audio focus in order to allow + * other sessions play seamlessly. The state of playback should be updated to + * {@link PlaybackState#STATE_PAUSED} after the preparation is done. + * The playback of the prepared content should start in the implementation of + * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests + * for starting playback without preparation. + */ + public void onPrepareFromUri(Uri uri, Bundle extras) { + } + + /** + * Override to handle requests to begin playback. + */ + public void onPlay() { + } + + /** + * Override to handle requests to begin playback from a search query. An + * empty query indicates that the app may play any music. The + * implementation should attempt to make a smart choice about what to + * play. + */ + public void onPlayFromSearch(String query, Bundle extras) { + } + + /** + * Override to handle requests to play a specific mediaId that was + * provided by your app's {@link MediaBrowserService}. + */ + public void onPlayFromMediaId(String mediaId, Bundle extras) { + } + + /** + * Override to handle requests to play a specific media item represented by a URI. + */ + public void onPlayFromUri(Uri uri, Bundle extras) { + } + + /** + * Override to handle requests to play an item with a given id from the + * play queue. + */ + public void onSkipToQueueItem(long id) { + } + + /** + * Override to handle requests to pause playback. + */ + public void onPause() { + } + + /** + * Override to handle requests to skip to the next media item. + */ + public void onSkipToNext() { + } + + /** + * Override to handle requests to skip to the previous media item. + */ + public void onSkipToPrevious() { + } + + /** + * Override to handle requests to fast forward. + */ + public void onFastForward() { + } + + /** + * Override to handle requests to rewind. + */ + public void onRewind() { + } + + /** + * Override to handle requests to stop playback. + */ + public void onStop() { + } + + /** + * Override to handle requests to seek to a specific position in ms. + * + * @param pos New position to move to, in milliseconds. + */ + public void onSeekTo(long pos) { + } + + /** + * Override to handle the item being rated. + * + * @param rating + */ + public void onSetRating(@NonNull Rating rating) { + } + + /** + * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be + * performed. + * + * @param action The action that was originally sent in the + * {@link PlaybackState.CustomAction}. + * @param extras Optional extras specified by the {@link MediaController}. + */ + public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { + } + } + + /** + * @hide + */ + public static class CallbackStub extends ISessionCallback.Stub { + private WeakReference mMediaSession; + + public CallbackStub(MediaSession session) { + mMediaSession = new WeakReference<>(session); + } + + private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + return new RemoteUserInfo(packageName, pid, uid, + caller != null ? caller.asBinder() : null); + } + + @Override + public void onCommand(String packageName, int pid, int uid, + ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller), + command, args, cb); + } + } + + @Override + public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent, + int sequenceNumber, ResultReceiver cb) { + MediaSession session = mMediaSession.get(); + try { + if (session != null) { + session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null), + mediaButtonIntent); + } + } finally { + if (cb != null) { + cb.send(sequenceNumber, null); + } + } + } + + @Override + public void onMediaButtonFromController(String packageName, int pid, int uid, + ISessionControllerCallback caller, Intent mediaButtonIntent) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller), + mediaButtonIntent); + } + } + + @Override + public void onPrepare(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onPrepareFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, + Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrepareFromMediaId( + createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras); + } + } + + @Override + public void onPrepareFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, + Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrepareFromSearch( + createRemoteUserInfo(packageName, pid, uid, caller), query, extras); + } + } + + @Override + public void onPrepareFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller), + uri, extras); + } + } + + @Override + public void onPlay(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onPlayFromMediaId(String packageName, int pid, int uid, + ISessionControllerCallback caller, String mediaId, + Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller), + mediaId, extras); + } + } + + @Override + public void onPlayFromSearch(String packageName, int pid, int uid, + ISessionControllerCallback caller, String query, + Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller), + query, extras); + } + } + + @Override + public void onPlayFromUri(String packageName, int pid, int uid, + ISessionControllerCallback caller, Uri uri, Bundle extras) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller), + uri, extras); + } + } + + @Override + public void onSkipToTrack(String packageName, int pid, int uid, + ISessionControllerCallback caller, long id) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id); + } + } + + @Override + public void onPause(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onStop(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onNext(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onPrevious(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onFastForward(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onRewind(String packageName, int pid, int uid, + ISessionControllerCallback caller) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller)); + } + } + + @Override + public void onSeekTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, long pos) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos); + } + } + + @Override + public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, + Rating rating) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating); + } + } + + @Override + public void onCustomAction(String packageName, int pid, int uid, + ISessionControllerCallback caller, String action, Bundle args) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller), + action, args); + } + } + + @Override + public void onAdjustVolume(String packageName, int pid, int uid, + ISessionControllerCallback caller, int direction) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller), + direction); + } + } + + @Override + public void onSetVolumeTo(String packageName, int pid, int uid, + ISessionControllerCallback caller, int value) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller), + value); + } + } + } + + /** + * A single item that is part of the play queue. It contains a description + * of the item and its id in the queue. + */ + public static final class QueueItem implements Parcelable { + /** + * This id is reserved. No items can be explicitly assigned this id. + */ + public static final int UNKNOWN_ID = -1; + + private final MediaDescription mDescription; + @UnsupportedAppUsage + private final long mId; + + /** + * Create a new {@link MediaSession.QueueItem}. + * + * @param description The {@link MediaDescription} for this item. + * @param id An identifier for this item. It must be unique within the + * play queue and cannot be {@link #UNKNOWN_ID}. + */ + public QueueItem(MediaDescription description, long id) { + if (description == null) { + throw new IllegalArgumentException("Description cannot be null."); + } + if (id == UNKNOWN_ID) { + throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID"); + } + mDescription = description; + mId = id; + } + + private QueueItem(Parcel in) { + mDescription = MediaDescription.CREATOR.createFromParcel(in); + mId = in.readLong(); + } + + /** + * Get the description for this item. + */ + public MediaDescription getDescription() { + return mDescription; + } + + /** + * Get the queue id for this item. + */ + public long getQueueId() { + return mId; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + mDescription.writeToParcel(dest, flags); + dest.writeLong(mId); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = + new Creator() { + + @Override + public MediaSession.QueueItem createFromParcel(Parcel p) { + return new MediaSession.QueueItem(p); + } + + @Override + public MediaSession.QueueItem[] newArray(int size) { + return new MediaSession.QueueItem[size]; + } + }; + + @Override + public String toString() { + return "MediaSession.QueueItem {" + + "Description=" + mDescription + + ", Id=" + mId + " }"; + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + + if (!(o instanceof QueueItem)) { + return false; + } + + final QueueItem item = (QueueItem) o; + if (mId != item.mId) { + return false; + } + + if (!Objects.equals(mDescription, item.mDescription)) { + return false; + } + + return true; + } + } + + private static final class Command { + public final String command; + public final Bundle extras; + public final ResultReceiver stub; + + public Command(String command, Bundle extras, ResultReceiver stub) { + this.command = command; + this.extras = extras; + this.stub = stub; + } + } + + private class CallbackMessageHandler extends Handler { + private static final int MSG_COMMAND = 1; + private static final int MSG_MEDIA_BUTTON = 2; + private static final int MSG_PREPARE = 3; + private static final int MSG_PREPARE_MEDIA_ID = 4; + private static final int MSG_PREPARE_SEARCH = 5; + private static final int MSG_PREPARE_URI = 6; + private static final int MSG_PLAY = 7; + private static final int MSG_PLAY_MEDIA_ID = 8; + private static final int MSG_PLAY_SEARCH = 9; + private static final int MSG_PLAY_URI = 10; + private static final int MSG_SKIP_TO_ITEM = 11; + private static final int MSG_PAUSE = 12; + private static final int MSG_STOP = 13; + private static final int MSG_NEXT = 14; + private static final int MSG_PREVIOUS = 15; + private static final int MSG_FAST_FORWARD = 16; + private static final int MSG_REWIND = 17; + private static final int MSG_SEEK_TO = 18; + private static final int MSG_RATE = 19; + private static final int MSG_CUSTOM_ACTION = 20; + private static final int MSG_ADJUST_VOLUME = 21; + private static final int MSG_SET_VOLUME = 22; + private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; + + private MediaSession.Callback mCallback; + private RemoteUserInfo mCurrentControllerInfo; + + public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { + super(looper, null, true); + mCallback = callback; + mCallback.mHandler = this; + } + + public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) { + Pair objWithCaller = Pair.create(caller, obj); + Message msg = obtainMessage(what, objWithCaller); + msg.setData(data); + if (delayMs > 0) { + sendMessageDelayed(msg, delayMs); + } else { + sendMessage(msg); + } + } + + @Override + public void handleMessage(Message msg) { + mCurrentControllerInfo = ((Pair) msg.obj).first; + + VolumeProvider vp; + Object obj = ((Pair) msg.obj).second; + + switch (msg.what) { + case MSG_COMMAND: + Command cmd = (Command) obj; + mCallback.onCommand(cmd.command, cmd.extras, cmd.stub); + break; + case MSG_MEDIA_BUTTON: + mCallback.onMediaButtonEvent((Intent) obj); + break; + case MSG_PREPARE: + mCallback.onPrepare(); + break; + case MSG_PREPARE_MEDIA_ID: + mCallback.onPrepareFromMediaId((String) obj, msg.getData()); + break; + case MSG_PREPARE_SEARCH: + mCallback.onPrepareFromSearch((String) obj, msg.getData()); + break; + case MSG_PREPARE_URI: + mCallback.onPrepareFromUri((Uri) obj, msg.getData()); + break; + case MSG_PLAY: + mCallback.onPlay(); + break; + case MSG_PLAY_MEDIA_ID: + mCallback.onPlayFromMediaId((String) obj, msg.getData()); + break; + case MSG_PLAY_SEARCH: + mCallback.onPlayFromSearch((String) obj, msg.getData()); + break; + case MSG_PLAY_URI: + mCallback.onPlayFromUri((Uri) obj, msg.getData()); + break; + case MSG_SKIP_TO_ITEM: + mCallback.onSkipToQueueItem((Long) obj); + break; + case MSG_PAUSE: + mCallback.onPause(); + break; + case MSG_STOP: + mCallback.onStop(); + break; + case MSG_NEXT: + mCallback.onSkipToNext(); + break; + case MSG_PREVIOUS: + mCallback.onSkipToPrevious(); + break; + case MSG_FAST_FORWARD: + mCallback.onFastForward(); + break; + case MSG_REWIND: + mCallback.onRewind(); + break; + case MSG_SEEK_TO: + mCallback.onSeekTo((Long) obj); + break; + case MSG_RATE: + mCallback.onSetRating((Rating) obj); + break; + case MSG_CUSTOM_ACTION: + mCallback.onCustomAction((String) obj, msg.getData()); + break; + case MSG_ADJUST_VOLUME: + synchronized (mLock) { + vp = mVolumeProvider; + } + if (vp != null) { + vp.onAdjustVolume((int) obj); + } + break; + case MSG_SET_VOLUME: + synchronized (mLock) { + vp = mVolumeProvider; + } + if (vp != null) { + vp.onSetVolumeTo((int) obj); + } + break; + case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT: + mCallback.handleMediaPlayPauseKeySingleTapIfPending(); + break; + } + mCurrentControllerInfo = null; + } + } +} diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl new file mode 100644 index 0000000000..c4250f097f --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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.session; + +parcelable ParcelableVolumeInfo; diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java new file mode 100644 index 0000000000..f59c9756d4 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java @@ -0,0 +1,80 @@ +/* Copyright 2014, 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.session; + +import android.media.AudioAttributes; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Convenience class for passing information about the audio configuration of a + * session. The public implementation is {@link MediaController.PlaybackInfo}. + * + * @hide + */ +public class ParcelableVolumeInfo implements Parcelable { + public int volumeType; + public AudioAttributes audioAttrs; + public int controlType; + public int maxVolume; + public int currentVolume; + + public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType, + int maxVolume, + int currentVolume) { + this.volumeType = volumeType; + this.audioAttrs = audioAttrs; + this.controlType = controlType; + this.maxVolume = maxVolume; + this.currentVolume = currentVolume; + } + + public ParcelableVolumeInfo(Parcel from) { + volumeType = from.readInt(); + controlType = from.readInt(); + maxVolume = from.readInt(); + currentVolume = from.readInt(); + audioAttrs = AudioAttributes.CREATOR.createFromParcel(from); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(volumeType); + dest.writeInt(controlType); + dest.writeInt(maxVolume); + dest.writeInt(currentVolume); + audioAttrs.writeToParcel(dest, flags); + } + + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public ParcelableVolumeInfo createFromParcel(Parcel in) { + return new ParcelableVolumeInfo(in); + } + + @Override + public ParcelableVolumeInfo[] newArray(int size) { + return new ParcelableVolumeInfo[size]; + } + }; +} diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl new file mode 100644 index 0000000000..0876ebd2d4 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl @@ -0,0 +1,18 @@ +/* Copyright 2014, 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.session; + +parcelable PlaybackState; diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java new file mode 100644 index 0000000000..17d16b8966 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java @@ -0,0 +1,1079 @@ +/* + * Copyright (C) 2014 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.session; + +import android.annotation.DrawableRes; +import android.annotation.IntDef; +import android.annotation.LongDef; +import android.annotation.Nullable; +import android.media.RemoteControlClient; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.text.TextUtils; +import java.util.ArrayList; +import java.util.List; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Playback state for a {@link MediaSession}. This includes a state like + * {@link PlaybackState#STATE_PLAYING}, the current playback position, + * and the current control capabilities. + */ +public final class PlaybackState implements Parcelable { + private static final String TAG = "PlaybackState"; + + /** + * @hide + */ + @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, + ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, + ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, + ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, + ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI}) + @Retention(RetentionPolicy.SOURCE) + public @interface Actions {} + + /** + * Indicates this session supports the stop command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_STOP = 1 << 0; + + /** + * Indicates this session supports the pause command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PAUSE = 1 << 1; + + /** + * Indicates this session supports the play command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PLAY = 1 << 2; + + /** + * Indicates this session supports the rewind command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_REWIND = 1 << 3; + + /** + * Indicates this session supports the previous command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; + + /** + * Indicates this session supports the next command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_SKIP_TO_NEXT = 1 << 5; + + /** + * Indicates this session supports the fast forward command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_FAST_FORWARD = 1 << 6; + + /** + * Indicates this session supports the set rating command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_SET_RATING = 1 << 7; + + /** + * Indicates this session supports the seek to command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_SEEK_TO = 1 << 8; + + /** + * Indicates this session supports the play/pause toggle command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PLAY_PAUSE = 1 << 9; + + /** + * Indicates this session supports the play from media id command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; + + /** + * Indicates this session supports the play from search command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; + + /** + * Indicates this session supports the skip to queue item command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; + + /** + * Indicates this session supports the play from URI command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PLAY_FROM_URI = 1 << 13; + + /** + * Indicates this session supports the prepare command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PREPARE = 1 << 14; + + /** + * Indicates this session supports the prepare from media id command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15; + + /** + * Indicates this session supports the prepare from search command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16; + + /** + * Indicates this session supports the prepare from URI command. + * + * @see Builder#setActions(long) + */ + public static final long ACTION_PREPARE_FROM_URI = 1 << 17; + + /** + * @hide + */ + @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, + STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING, + STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM}) + @Retention(RetentionPolicy.SOURCE) + public @interface State {} + + /** + * This is the default playback state and indicates that no media has been + * added yet, or the performer has been reset and has no content to play. + * + * @see Builder#setState(int, long, float) + * @see Builder#setState(int, long, float, long) + */ + public final static int STATE_NONE = 0; + + /** + * State indicating this item is currently stopped. + * + * @see Builder#setState + */ + public final static int STATE_STOPPED = 1; + + /** + * State indicating this item is currently paused. + * + * @see Builder#setState + */ + public final static int STATE_PAUSED = 2; + + /** + * State indicating this item is currently playing. + * + * @see Builder#setState + */ + public final static int STATE_PLAYING = 3; + + /** + * State indicating this item is currently fast forwarding. + * + * @see Builder#setState + */ + public final static int STATE_FAST_FORWARDING = 4; + + /** + * State indicating this item is currently rewinding. + * + * @see Builder#setState + */ + public final static int STATE_REWINDING = 5; + + /** + * State indicating this item is currently buffering and will begin playing + * when enough data has buffered. + * + * @see Builder#setState + */ + public final static int STATE_BUFFERING = 6; + + /** + * State indicating this item is currently in an error state. The error + * message should also be set when entering this state. + * + * @see Builder#setState + */ + public final static int STATE_ERROR = 7; + + /** + * State indicating the class doing playback is currently connecting to a + * new destination. Depending on the implementation you may return to the previous + * state when the connection finishes or enter {@link #STATE_NONE}. + * If the connection failed {@link #STATE_ERROR} should be used. + * + * @see Builder#setState + */ + public final static int STATE_CONNECTING = 8; + + /** + * State indicating the player is currently skipping to the previous item. + * + * @see Builder#setState + */ + public final static int STATE_SKIPPING_TO_PREVIOUS = 9; + + /** + * State indicating the player is currently skipping to the next item. + * + * @see Builder#setState + */ + public final static int STATE_SKIPPING_TO_NEXT = 10; + + /** + * State indicating the player is currently skipping to a specific item in + * the queue. + * + * @see Builder#setState + */ + public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11; + + /** + * Use this value for the position to indicate the position is not known. + */ + public final static long PLAYBACK_POSITION_UNKNOWN = -1; + + private final int mState; + private final long mPosition; + private final long mBufferedPosition; + private final float mSpeed; + private final long mActions; + private List mCustomActions; + private final CharSequence mErrorMessage; + private final long mUpdateTime; + private final long mActiveItemId; + private final Bundle mExtras; + + private PlaybackState(int state, long position, long updateTime, float speed, + long bufferedPosition, long transportControls, + List customActions, long activeItemId, + CharSequence error, Bundle extras) { + mState = state; + mPosition = position; + mSpeed = speed; + mUpdateTime = updateTime; + mBufferedPosition = bufferedPosition; + mActions = transportControls; + mCustomActions = new ArrayList<>(customActions); + mActiveItemId = activeItemId; + mErrorMessage = error; + mExtras = extras; + } + + private PlaybackState(Parcel in) { + mState = in.readInt(); + mPosition = in.readLong(); + mSpeed = in.readFloat(); + mUpdateTime = in.readLong(); + mBufferedPosition = in.readLong(); + mActions = in.readLong(); + mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); + mActiveItemId = in.readLong(); + mErrorMessage = in.readCharSequence(); + mExtras = in.readBundle(); + } + + @Override + public String toString() { + StringBuilder bob = new StringBuilder("PlaybackState {"); + bob.append("state=").append(mState); + bob.append(", position=").append(mPosition); + bob.append(", buffered position=").append(mBufferedPosition); + bob.append(", speed=").append(mSpeed); + bob.append(", updated=").append(mUpdateTime); + bob.append(", actions=").append(mActions); + bob.append(", custom actions=").append(mCustomActions); + bob.append(", active item id=").append(mActiveItemId); + bob.append(", error=").append(mErrorMessage); + bob.append("}"); + return bob.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mState); + dest.writeLong(mPosition); + dest.writeFloat(mSpeed); + dest.writeLong(mUpdateTime); + dest.writeLong(mBufferedPosition); + dest.writeLong(mActions); + dest.writeTypedList(mCustomActions); + dest.writeLong(mActiveItemId); + dest.writeCharSequence(mErrorMessage); + dest.writeBundle(mExtras); + } + + /** + * Get the current state of playback. One of the following: + *

      + *
    • {@link PlaybackState#STATE_NONE}
    • + *
    • {@link PlaybackState#STATE_STOPPED}
    • + *
    • {@link PlaybackState#STATE_PLAYING}
    • + *
    • {@link PlaybackState#STATE_PAUSED}
    • + *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • + *
    • {@link PlaybackState#STATE_REWINDING}
    • + *
    • {@link PlaybackState#STATE_BUFFERING}
    • + *
    • {@link PlaybackState#STATE_ERROR}
    • + *
    • {@link PlaybackState#STATE_CONNECTING}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • + *
    + */ + @State + public int getState() { + return mState; + } + + /** + * Get the current playback position in ms. + */ + public long getPosition() { + return mPosition; + } + + /** + * Get the current buffered position in ms. This is the farthest playback + * point that can be reached from the current position using only buffered + * content. + */ + public long getBufferedPosition() { + return mBufferedPosition; + } + + /** + * Get the current playback speed as a multiple of normal playback. This + * should be negative when rewinding. A value of 1 means normal playback and + * 0 means paused. + * + * @return The current speed of playback. + */ + public float getPlaybackSpeed() { + return mSpeed; + } + + /** + * Get the current actions available on this session. This should use a + * bitmask of the available actions. + *
      + *
    • {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}
    • + *
    • {@link PlaybackState#ACTION_REWIND}
    • + *
    • {@link PlaybackState#ACTION_PLAY}
    • + *
    • {@link PlaybackState#ACTION_PAUSE}
    • + *
    • {@link PlaybackState#ACTION_STOP}
    • + *
    • {@link PlaybackState#ACTION_FAST_FORWARD}
    • + *
    • {@link PlaybackState#ACTION_SKIP_TO_NEXT}
    • + *
    • {@link PlaybackState#ACTION_SEEK_TO}
    • + *
    • {@link PlaybackState#ACTION_SET_RATING}
    • + *
    • {@link PlaybackState#ACTION_PLAY_PAUSE}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}
    • + *
    • {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_URI}
    • + *
    • {@link PlaybackState#ACTION_PREPARE}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_URI}
    • + *
    + */ + @Actions + public long getActions() { + return mActions; + } + + /** + * Get the list of custom actions. + */ + public List getCustomActions() { + return mCustomActions; + } + + /** + * Get a user readable error message. This should be set when the state is + * {@link PlaybackState#STATE_ERROR}. + */ + public CharSequence getErrorMessage() { + return mErrorMessage; + } + + /** + * Get the elapsed real time at which position was last updated. If the + * position has never been set this will return 0; + * + * @return The last time the position was updated. + */ + public long getLastPositionUpdateTime() { + return mUpdateTime; + } + + /** + * Get the id of the currently active item in the queue. If there is no + * queue or a queue is not supported by the session this will be + * {@link MediaSession.QueueItem#UNKNOWN_ID}. + * + * @return The id of the currently active item in the queue or + * {@link MediaSession.QueueItem#UNKNOWN_ID}. + */ + public long getActiveQueueItemId() { + return mActiveItemId; + } + + /** + * Get any custom extras that were set on this playback state. + * + * @return The extras for this state or null. + */ + public @Nullable Bundle getExtras() { + return mExtras; + } + + /** + * Get the {@link PlaybackState} state for the given + * {@link RemoteControlClient} state. + * + * @param rccState The state used by {@link RemoteControlClient}. + * @return The equivalent state used by {@link PlaybackState}. + * @hide + */ + public static int getStateFromRccState(int rccState) { + switch (rccState) { + case RemoteControlClient.PLAYSTATE_BUFFERING: + return STATE_BUFFERING; + case RemoteControlClient.PLAYSTATE_ERROR: + return STATE_ERROR; + case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: + return STATE_FAST_FORWARDING; + case RemoteControlClient.PLAYSTATE_NONE: + return STATE_NONE; + case RemoteControlClient.PLAYSTATE_PAUSED: + return STATE_PAUSED; + case RemoteControlClient.PLAYSTATE_PLAYING: + return STATE_PLAYING; + case RemoteControlClient.PLAYSTATE_REWINDING: + return STATE_REWINDING; + case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: + return STATE_SKIPPING_TO_PREVIOUS; + case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: + return STATE_SKIPPING_TO_NEXT; + case RemoteControlClient.PLAYSTATE_STOPPED: + return STATE_STOPPED; + default: + return -1; + } + } + + /** + * Get the {@link RemoteControlClient} state for the given + * {@link PlaybackState} state. + * + * @param state The state used by {@link PlaybackState}. + * @return The equivalent state used by {@link RemoteControlClient}. + * @hide + */ + public static int getRccStateFromState(int state) { + switch (state) { + case STATE_BUFFERING: + return RemoteControlClient.PLAYSTATE_BUFFERING; + case STATE_ERROR: + return RemoteControlClient.PLAYSTATE_ERROR; + case STATE_FAST_FORWARDING: + return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; + case STATE_NONE: + return RemoteControlClient.PLAYSTATE_NONE; + case STATE_PAUSED: + return RemoteControlClient.PLAYSTATE_PAUSED; + case STATE_PLAYING: + return RemoteControlClient.PLAYSTATE_PLAYING; + case STATE_REWINDING: + return RemoteControlClient.PLAYSTATE_REWINDING; + case STATE_SKIPPING_TO_PREVIOUS: + return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS; + case STATE_SKIPPING_TO_NEXT: + return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS; + case STATE_STOPPED: + return RemoteControlClient.PLAYSTATE_STOPPED; + default: + return -1; + } + } + + /** + * @hide + */ + public static long getActionsFromRccControlFlags(int rccFlags) { + long actions = 0; + long flag = 1; + while (flag <= rccFlags) { + if ((flag & rccFlags) != 0) { + actions |= getActionForRccFlag((int) flag); + } + flag = flag << 1; + } + return actions; + } + + /** + * @hide + */ + public static int getRccControlFlagsFromActions(long actions) { + int rccFlags = 0; + long action = 1; + while (action <= actions && action < Integer.MAX_VALUE) { + if ((action & actions) != 0) { + rccFlags |= getRccFlagForAction(action); + } + action = action << 1; + } + return rccFlags; + } + + private static long getActionForRccFlag(int flag) { + switch (flag) { + case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS: + return ACTION_SKIP_TO_PREVIOUS; + case RemoteControlClient.FLAG_KEY_MEDIA_REWIND: + return ACTION_REWIND; + case RemoteControlClient.FLAG_KEY_MEDIA_PLAY: + return ACTION_PLAY; + case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE: + return ACTION_PLAY_PAUSE; + case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE: + return ACTION_PAUSE; + case RemoteControlClient.FLAG_KEY_MEDIA_STOP: + return ACTION_STOP; + case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD: + return ACTION_FAST_FORWARD; + case RemoteControlClient.FLAG_KEY_MEDIA_NEXT: + return ACTION_SKIP_TO_NEXT; + case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE: + return ACTION_SEEK_TO; + case RemoteControlClient.FLAG_KEY_MEDIA_RATING: + return ACTION_SET_RATING; + } + return 0; + } + + private static int getRccFlagForAction(long action) { + // We only care about the lower set of actions that can map to rcc + // flags. + int testAction = action < Integer.MAX_VALUE ? (int) action : 0; + switch (testAction) { + case (int) ACTION_SKIP_TO_PREVIOUS: + return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS; + case (int) ACTION_REWIND: + return RemoteControlClient.FLAG_KEY_MEDIA_REWIND; + case (int) ACTION_PLAY: + return RemoteControlClient.FLAG_KEY_MEDIA_PLAY; + case (int) ACTION_PLAY_PAUSE: + return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; + case (int) ACTION_PAUSE: + return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE; + case (int) ACTION_STOP: + return RemoteControlClient.FLAG_KEY_MEDIA_STOP; + case (int) ACTION_FAST_FORWARD: + return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD; + case (int) ACTION_SKIP_TO_NEXT: + return RemoteControlClient.FLAG_KEY_MEDIA_NEXT; + case (int) ACTION_SEEK_TO: + return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE; + case (int) ACTION_SET_RATING: + return RemoteControlClient.FLAG_KEY_MEDIA_RATING; + } + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public PlaybackState createFromParcel(Parcel in) { + return new PlaybackState(in); + } + + @Override + public PlaybackState[] newArray(int size) { + return new PlaybackState[size]; + } + }; + + /** + * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of + * the standard transport controls by exposing app specific actions to + * {@link MediaController MediaControllers}. + */ + public static final class CustomAction implements Parcelable { + private final String mAction; + private final CharSequence mName; + private final int mIcon; + private final Bundle mExtras; + + /** + * Use {@link PlaybackState.CustomAction.Builder#build()}. + */ + private CustomAction(String action, CharSequence name, int icon, Bundle extras) { + mAction = action; + mName = name; + mIcon = icon; + mExtras = extras; + } + + private CustomAction(Parcel in) { + mAction = in.readString(); + mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mIcon = in.readInt(); + mExtras = in.readBundle(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mAction); + TextUtils.writeToParcel(mName, dest, flags); + dest.writeInt(mIcon); + dest.writeBundle(mExtras); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + + @Override + public PlaybackState.CustomAction createFromParcel(Parcel p) { + return new PlaybackState.CustomAction(p); + } + + @Override + public PlaybackState.CustomAction[] newArray(int size) { + return new PlaybackState.CustomAction[size]; + } + }; + + /** + * Returns the action of the {@link CustomAction}. + * + * @return The action of the {@link CustomAction}. + */ + public String getAction() { + return mAction; + } + + /** + * Returns the display name of this action. e.g. "Favorite" + * + * @return The display name of this {@link CustomAction}. + */ + public CharSequence getName() { + return mName; + } + + /** + * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package. + * + * @return The resource id of the icon in the {@link MediaSession MediaSession's} package. + */ + public int getIcon() { + return mIcon; + } + + /** + * Returns extras which provide additional application-specific information about the + * action, or null if none. These arguments are meant to be consumed by a + * {@link MediaController} if it knows how to handle them. + * + * @return Optional arguments for the {@link CustomAction}. + */ + public Bundle getExtras() { + return mExtras; + } + + @Override + public String toString() { + return "Action:" + + "mName='" + mName + + ", mIcon=" + mIcon + + ", mExtras=" + mExtras; + } + + /** + * Builder for {@link CustomAction} objects. + */ + public static final class Builder { + private final String mAction; + private final CharSequence mName; + private final int mIcon; + private Bundle mExtras; + + /** + * Creates a {@link CustomAction} builder with the id, name, and icon set. + * + * @param action The action of the {@link CustomAction}. + * @param name The display name of the {@link CustomAction}. This name will be displayed + * along side the action if the UI supports it. + * @param icon The icon resource id of the {@link CustomAction}. This resource id + * must be in the same package as the {@link MediaSession}. It will be + * displayed with the custom action if the UI supports it. + */ + public Builder(String action, CharSequence name, @DrawableRes int icon) { + if (TextUtils.isEmpty(action)) { + throw new IllegalArgumentException( + "You must specify an action to build a CustomAction."); + } + if (TextUtils.isEmpty(name)) { + throw new IllegalArgumentException( + "You must specify a name to build a CustomAction."); + } + if (icon == 0) { + throw new IllegalArgumentException( + "You must specify an icon resource id to build a CustomAction."); + } + mAction = action; + mName = name; + mIcon = icon; + } + + /** + * Set optional extras for the {@link CustomAction}. These extras are meant to be + * consumed by a {@link MediaController} if it knows how to handle them. + * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions. + * + * @param extras Optional extras for the {@link CustomAction}. + * @return this. + */ + public Builder setExtras(Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Build and return the {@link CustomAction} instance with the specified values. + * + * @return A new {@link CustomAction} instance. + */ + public CustomAction build() { + return new CustomAction(mAction, mName, mIcon, mExtras); + } + } + } + + /** + * Builder for {@link PlaybackState} objects. + */ + public static final class Builder { + private final List mCustomActions = new ArrayList<>(); + + private int mState; + private long mPosition; + private long mBufferedPosition; + private float mSpeed; + private long mActions; + private CharSequence mErrorMessage; + private long mUpdateTime; + private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID; + private Bundle mExtras; + + /** + * Creates an initially empty state builder. + */ + public Builder() { + } + + /** + * Creates a builder with the same initial values as those in the from + * state. + * + * @param from The state to use for initializing the builder. + */ + public Builder(PlaybackState from) { + if (from == null) { + return; + } + mState = from.mState; + mPosition = from.mPosition; + mBufferedPosition = from.mBufferedPosition; + mSpeed = from.mSpeed; + mActions = from.mActions; + if (from.mCustomActions != null) { + mCustomActions.addAll(from.mCustomActions); + } + mErrorMessage = from.mErrorMessage; + mUpdateTime = from.mUpdateTime; + mActiveItemId = from.mActiveItemId; + mExtras = from.mExtras; + } + + /** + * Set the current state of playback. + *

    + * The position must be in ms and indicates the current playback + * position within the item. If the position is unknown use + * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown + * position the time at which the position was updated must be provided. + * It is okay to use {@link SystemClock#elapsedRealtime()} if the + * current position was just retrieved. + *

    + * The speed is a multiple of normal playback and should be 0 when + * paused and negative when rewinding. Normal playback speed is 1.0. + *

    + * The state must be one of the following: + *

      + *
    • {@link PlaybackState#STATE_NONE}
    • + *
    • {@link PlaybackState#STATE_STOPPED}
    • + *
    • {@link PlaybackState#STATE_PLAYING}
    • + *
    • {@link PlaybackState#STATE_PAUSED}
    • + *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • + *
    • {@link PlaybackState#STATE_REWINDING}
    • + *
    • {@link PlaybackState#STATE_BUFFERING}
    • + *
    • {@link PlaybackState#STATE_ERROR}
    • + *
    • {@link PlaybackState#STATE_CONNECTING}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • + *
    + * + * @param state The current state of playback. + * @param position The position in the current item in ms. + * @param playbackSpeed The current speed of playback as a multiple of + * normal playback. + * @param updateTime The time in the {@link SystemClock#elapsedRealtime} + * timebase that the position was updated at. + * @return this + */ + public Builder setState(@State int state, long position, float playbackSpeed, + long updateTime) { + mState = state; + mPosition = position; + mUpdateTime = updateTime; + mSpeed = playbackSpeed; + return this; + } + + /** + * Set the current state of playback. + *

    + * The position must be in ms and indicates the current playback + * position within the item. If the position is unknown use + * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to + * the current {@link SystemClock#elapsedRealtime()}. + *

    + * The speed is a multiple of normal playback and should be 0 when + * paused and negative when rewinding. Normal playback speed is 1.0. + *

    + * The state must be one of the following: + *

      + *
    • {@link PlaybackState#STATE_NONE}
    • + *
    • {@link PlaybackState#STATE_STOPPED}
    • + *
    • {@link PlaybackState#STATE_PLAYING}
    • + *
    • {@link PlaybackState#STATE_PAUSED}
    • + *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • + *
    • {@link PlaybackState#STATE_REWINDING}
    • + *
    • {@link PlaybackState#STATE_BUFFERING}
    • + *
    • {@link PlaybackState#STATE_ERROR}
    • + *
    • {@link PlaybackState#STATE_CONNECTING}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • + *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • + *
    + * + * @param state The current state of playback. + * @param position The position in the current item in ms. + * @param playbackSpeed The current speed of playback as a multiple of + * normal playback. + * @return this + */ + public Builder setState(@State int state, long position, float playbackSpeed) { + return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); + } + + /** + * Set the current actions available on this session. This should use a + * bitmask of possible actions. + *
      + *
    • {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}
    • + *
    • {@link PlaybackState#ACTION_REWIND}
    • + *
    • {@link PlaybackState#ACTION_PLAY}
    • + *
    • {@link PlaybackState#ACTION_PAUSE}
    • + *
    • {@link PlaybackState#ACTION_STOP}
    • + *
    • {@link PlaybackState#ACTION_FAST_FORWARD}
    • + *
    • {@link PlaybackState#ACTION_SKIP_TO_NEXT}
    • + *
    • {@link PlaybackState#ACTION_SEEK_TO}
    • + *
    • {@link PlaybackState#ACTION_SET_RATING}
    • + *
    • {@link PlaybackState#ACTION_PLAY_PAUSE}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}
    • + *
    • {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}
    • + *
    • {@link PlaybackState#ACTION_PLAY_FROM_URI}
    • + *
    • {@link PlaybackState#ACTION_PREPARE}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}
    • + *
    • {@link PlaybackState#ACTION_PREPARE_FROM_URI}
    • + *
    + * + * @param actions The set of actions allowed. + * @return this + */ + public Builder setActions(@Actions long actions) { + mActions = actions; + return this; + } + + /** + * Add a custom action to the playback state. Actions can be used to + * expose additional functionality to {@link MediaController + * MediaControllers} beyond what is offered by the standard transport + * controls. + *

    + * e.g. start a radio station based on the current item or skip ahead by + * 30 seconds. + * + * @param action An identifier for this action. It can be sent back to + * the {@link MediaSession} through + * {@link MediaController.TransportControls#sendCustomAction(String, Bundle)}. + * @param name The display name for the action. If text is shown with + * the action or used for accessibility, this is what should + * be used. + * @param icon The resource action of the icon that should be displayed + * for the action. The resource should be in the package of + * the {@link MediaSession}. + * @return this + */ + public Builder addCustomAction(String action, String name, int icon) { + return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null)); + } + + /** + * Add a custom action to the playback state. Actions can be used to expose additional + * functionality to {@link MediaController MediaControllers} beyond what is offered by the + * standard transport controls. + *

    + * An example of an action would be to start a radio station based on the current item + * or to skip ahead by 30 seconds. + * + * @param customAction The custom action to add to the {@link PlaybackState}. + * @return this + */ + public Builder addCustomAction(PlaybackState.CustomAction customAction) { + if (customAction == null) { + throw new IllegalArgumentException( + "You may not add a null CustomAction to PlaybackState."); + } + mCustomActions.add(customAction); + return this; + } + + /** + * Set the current buffered position in ms. This is the farthest + * playback point that can be reached from the current position using + * only buffered content. + * + * @param bufferedPosition The position in ms that playback is buffered + * to. + * @return this + */ + public Builder setBufferedPosition(long bufferedPosition) { + mBufferedPosition = bufferedPosition; + return this; + } + + /** + * Set the active item in the play queue by specifying its id. The + * default value is {@link MediaSession.QueueItem#UNKNOWN_ID} + * + * @param id The id of the active item. + * @return this + */ + public Builder setActiveQueueItemId(long id) { + mActiveItemId = id; + return this; + } + + /** + * Set a user readable error message. This should be set when the state + * is {@link PlaybackState#STATE_ERROR}. + * + * @param error The error message for display to the user. + * @return this + */ + public Builder setErrorMessage(CharSequence error) { + mErrorMessage = error; + return this; + } + + /** + * Set any custom extras to be included with the playback state. + * + * @param extras The extras to include. + * @return this + */ + public Builder setExtras(Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Build and return the {@link PlaybackState} instance with these + * values. + * + * @return A new state instance. + */ + public PlaybackState build() { + return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition, + mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras); + } + } +} diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl new file mode 100644 index 0000000000..84f41f6c3a --- /dev/null +++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl @@ -0,0 +1,27 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +package android.service.media; + +import android.content.res.Configuration; +import android.service.media.IMediaBrowserServiceCallbacks; +import android.net.Uri; +import android.os.Bundle; +import android.os.ResultReceiver; + +/** + * Media API allows clients to browse through hierarchy of a user’s media collection, + * playback a specific media entry and interact with the now playing queue. + * @hide + */ +oneway interface IMediaBrowserService { + void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks); + void disconnect(IMediaBrowserServiceCallbacks callbacks); + + void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); + void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); + + void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks); + void addSubscription(String uri, in IBinder token, in Bundle options, + IMediaBrowserServiceCallbacks callbacks); + void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks); +} diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl new file mode 100644 index 0000000000..deeab1a8a2 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl @@ -0,0 +1,27 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +package android.service.media; + +import android.content.pm.ParceledListSlice; +import android.graphics.Bitmap; +import android.media.session.MediaSession; +import android.os.Bundle; + +/** + * Media API allows clients to browse through hierarchy of a user’s media collection, + * playback a specific media entry and interact with the now playing queue. + * @hide + */ +oneway interface IMediaBrowserServiceCallbacks { + /** + * Invoked when the connected has been established. + * @param root The root media id for browsing. + * @param session The {@link MediaSession.Token media session token} that can be used to control + * the playback of the media app. + * @param extra Extras returned by the media service. + */ + void onConnect(String root, in MediaSession.Token session, in Bundle extras); + void onConnectFailed(); + void onLoadChildren(String mediaId, in ParceledListSlice list); + void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options); +} diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java new file mode 100644 index 0000000000..2f2fbaf7e2 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2014 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.service.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageManager; +//import android.content.pm.ParceledListSlice; +import android.media.browse.MediaBrowser; +import android.media.browse.MediaBrowserUtils; +import android.media.session.MediaSession; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.media.session.MediaSessionManager; +import android.media.session.MediaSessionManager.RemoteUserInfo; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.service.media.IMediaBrowserService; +import android.service.media.IMediaBrowserServiceCallbacks; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +/** + * Base class for media browser services. + *

    + * Media browser services enable applications to browse media content provided by an application + * and ask the application to start playing it. They may also be used to control content that + * is already playing by way of a {@link MediaSession}. + *

    + * + * To extend this class, you must declare the service in your manifest file with + * an intent filter with the {@link #SERVICE_INTERFACE} action. + * + * For example: + *

    + * <service android:name=".MyMediaBrowserService"
    + *          android:label="@string/service_name" >
    + *     <intent-filter>
    + *         <action android:name="android.media.browse.MediaBrowserService" />
    + *     </intent-filter>
    + * </service>
    + * 
    + * + */ +public abstract class MediaBrowserService extends Service { + private static final String TAG = "MediaBrowserService"; + private static final boolean DBG = false; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; + + /** + * A key for passing the MediaItem to the ResultReceiver in getItem. + * @hide + */ + @UnsupportedAppUsage + public static final String KEY_MEDIA_ITEM = "media_item"; + + private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0; + private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1; + + private static final int RESULT_ERROR = -1; + private static final int RESULT_OK = 0; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED, + RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED }) + private @interface ResultFlags { } + + private final ArrayMap mConnections = new ArrayMap<>(); + private ConnectionRecord mCurConnection; + private final Handler mHandler = new Handler(); + private ServiceBinder mBinder; + MediaSession.Token mSession; + + /** + * All the info about a connection. + */ + private class ConnectionRecord implements IBinder.DeathRecipient { + String pkg; + int uid; + int pid; + Bundle rootHints; + IMediaBrowserServiceCallbacks callbacks; + BrowserRoot root; + HashMap>> subscriptions = new HashMap<>(); + + @Override + public void binderDied() { + mHandler.post(new Runnable() { + @Override + public void run() { + mConnections.remove(callbacks.asBinder()); + } + }); + } + } + + /** + * Completion handler for asynchronous callback methods in {@link MediaBrowserService}. + *

    + * Each of the methods that takes one of these to send the result must call + * {@link #sendResult} to respond to the caller with the given results. If those + * functions return without calling {@link #sendResult}, they must instead call + * {@link #detach} before returning, and then may call {@link #sendResult} when + * they are done. If more than one of those methods is called, an exception will + * be thrown. + * + * @see #onLoadChildren + * @see #onLoadItem + */ + public class Result { + private Object mDebug; + private boolean mDetachCalled; + private boolean mSendResultCalled; + @UnsupportedAppUsage + private int mFlags; + + Result(Object debug) { + mDebug = debug; + } + + /** + * Send the result back to the caller. + */ + public void sendResult(T result) { + if (mSendResultCalled) { + throw new IllegalStateException("sendResult() called twice for: " + mDebug); + } + mSendResultCalled = true; + onResultSent(result, mFlags); + } + + /** + * Detach this message from the current thread and allow the {@link #sendResult} + * call to happen later. + */ + public void detach() { + if (mDetachCalled) { + throw new IllegalStateException("detach() called when detach() had already" + + " been called for: " + mDebug); + } + if (mSendResultCalled) { + throw new IllegalStateException("detach() called when sendResult() had already" + + " been called for: " + mDebug); + } + mDetachCalled = true; + } + + boolean isDone() { + return mDetachCalled || mSendResultCalled; + } + + void setFlags(@ResultFlags int flags) { + mFlags = flags; + } + + /** + * Called when the result is sent, after assertions about not being called twice + * have happened. + */ + void onResultSent(T result, @ResultFlags int flags) { + } + } + + private class ServiceBinder extends IMediaBrowserService.Stub { + @Override + public void connect(final String pkg, final Bundle rootHints, + final IMediaBrowserServiceCallbacks callbacks) { + + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + if (!isValidPackage(pkg, uid)) { + throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid + + " package=" + pkg); + } + + mHandler.post(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Clear out the old subscriptions. We are getting new ones. + mConnections.remove(b); + + final ConnectionRecord connection = new ConnectionRecord(); + connection.pkg = pkg; + connection.pid = pid; + connection.uid = uid; + connection.rootHints = rootHints; + connection.callbacks = callbacks; + + mCurConnection = connection; + connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints); + mCurConnection = null; + + // If they didn't return something, don't allow this client. + if (connection.root == null) { + Log.i(TAG, "No root for client " + pkg + " from service " + + getClass().getName()); + try { + callbacks.onConnectFailed(); + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " + + "pkg=" + pkg); + } + } else { + try { + mConnections.put(b, connection); + b.linkToDeath(connection, 0); + if (mSession != null) { + callbacks.onConnect(connection.root.getRootId(), + mSession, connection.root.getExtras()); + } + } catch (RemoteException ex) { + Log.w(TAG, "Calling onConnect() failed. Dropping client. " + + "pkg=" + pkg); + mConnections.remove(b); + } + } + } + }); + } + + @Override + public void disconnect(final IMediaBrowserServiceCallbacks callbacks) { + mHandler.post(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Clear out the old subscriptions. We are getting new ones. + final ConnectionRecord old = mConnections.remove(b); + if (old != null) { + // TODO + old.callbacks.asBinder().unlinkToDeath(old, 0); + } + } + }); + } + + @Override + public void addSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { + // do-nothing + } + + @Override + public void addSubscription(final String id, final IBinder token, final Bundle options, + final IMediaBrowserServiceCallbacks callbacks) { + mHandler.post(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + // Get the record for the connection + final ConnectionRecord connection = mConnections.get(b); + if (connection == null) { + Log.w(TAG, "addSubscription for callback that isn't registered id=" + + id); + return; + } + + MediaBrowserService.this.addSubscription(id, connection, token, options); + } + }); + } + + @Override + public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { + // do-nothing + } + + @Override + public void removeSubscription(final String id, final IBinder token, + final IMediaBrowserServiceCallbacks callbacks) { + mHandler.post(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + + ConnectionRecord connection = mConnections.get(b); + if (connection == null) { + Log.w(TAG, "removeSubscription for callback that isn't registered id=" + + id); + return; + } + if (!MediaBrowserService.this.removeSubscription(id, connection, token)) { + Log.w(TAG, "removeSubscription called for " + id + + " which is not subscribed"); + } + } + }); + } + + @Override + public void getMediaItem(final String mediaId, final ResultReceiver receiver, + final IMediaBrowserServiceCallbacks callbacks) { + mHandler.post(new Runnable() { + @Override + public void run() { + final IBinder b = callbacks.asBinder(); + ConnectionRecord connection = mConnections.get(b); + if (connection == null) { + Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); + return; + } + performLoadItem(mediaId, connection, receiver); + } + }); + } + } + + @Override + public void onCreate() { + super.onCreate(); + mBinder = new ServiceBinder(); + } + + @Override + public IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mBinder; + } + return null; + } + + @Override + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + } + + /** + * Called to get the root information for browsing by a particular client. + *

    + * The implementation should verify that the client package has permission + * to access browse media information before returning the root id; it + * should return null if the client is not allowed to access this + * information. + *

    + * + * @param clientPackageName The package name of the application which is + * requesting access to browse media. + * @param clientUid The uid of the application which is requesting access to + * browse media. + * @param rootHints An optional bundle of service-specific arguments to send + * to the media browser service when connecting and retrieving the + * root id for browsing, or null if none. The contents of this + * bundle may affect the information returned when browsing. + * @return The {@link BrowserRoot} for accessing this app's content or null. + * @see BrowserRoot#EXTRA_RECENT + * @see BrowserRoot#EXTRA_OFFLINE + * @see BrowserRoot#EXTRA_SUGGESTED + */ + public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, + int clientUid, @Nullable Bundle rootHints); + + /** + * Called to get information about the children of a media item. + *

    + * Implementations must call {@link Result#sendResult result.sendResult} + * with the list of children. If loading the children will be an expensive + * operation that should be performed on another thread, + * {@link Result#detach result.detach} may be called before returning from + * this function, and then {@link Result#sendResult result.sendResult} + * called when the loading is complete. + *

    + * In case the media item does not have any children, call {@link Result#sendResult} + * with an empty list. When the given {@code parentId} is invalid, implementations must + * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke + * {@link MediaBrowser.SubscriptionCallback#onError}. + *

    + * + * @param parentId The id of the parent media item whose children are to be + * queried. + * @param result The Result to send the list of children to. + */ + public abstract void onLoadChildren(@NonNull String parentId, + @NonNull Result> result); + + /** + * Called to get information about the children of a media item. + *

    + * Implementations must call {@link Result#sendResult result.sendResult} + * with the list of children. If loading the children will be an expensive + * operation that should be performed on another thread, + * {@link Result#detach result.detach} may be called before returning from + * this function, and then {@link Result#sendResult result.sendResult} + * called when the loading is complete. + *

    + * In case the media item does not have any children, call {@link Result#sendResult} + * with an empty list. When the given {@code parentId} is invalid, implementations must + * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke + * {@link MediaBrowser.SubscriptionCallback#onError}. + *

    + * + * @param parentId The id of the parent media item whose children are to be + * queried. + * @param result The Result to send the list of children to. + * @param options The bundle of service-specific arguments sent from the media + * browser. The information returned through the result should be + * affected by the contents of this bundle. + */ + public void onLoadChildren(@NonNull String parentId, + @NonNull Result> result, @NonNull Bundle options) { + // To support backward compatibility, when the implementation of MediaBrowserService doesn't + // override onLoadChildren() with options, onLoadChildren() without options will be used + // instead, and the options will be applied in the implementation of result.onResultSent(). + result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED); + onLoadChildren(parentId, result); + } + + /** + * Called to get information about a specific media item. + *

    + * Implementations must call {@link Result#sendResult result.sendResult}. If + * loading the item will be an expensive operation {@link Result#detach + * result.detach} may be called before returning from this function, and + * then {@link Result#sendResult result.sendResult} called when the item has + * been loaded. + *

    + * When the given {@code itemId} is invalid, implementations must call + * {@link Result#sendResult result.sendResult} with {@code null}. + *

    + * The default implementation will invoke {@link MediaBrowser.ItemCallback#onError}. + *

    + * + * @param itemId The id for the specific + * {@link android.media.browse.MediaBrowser.MediaItem}. + * @param result The Result to send the item to. + */ + public void onLoadItem(String itemId, Result result) { + result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED); + result.sendResult(null); + } + + /** + * Call to set the media session. + *

    + * This should be called as soon as possible during the service's startup. + * It may only be called once. + * + * @param token The token for the service's {@link MediaSession}. + */ + public void setSessionToken(final MediaSession.Token token) { + if (token == null) { + throw new IllegalArgumentException("Session token may not be null."); + } + if (mSession != null) { + throw new IllegalStateException("The session token has already been set."); + } + mSession = token; + mHandler.post(new Runnable() { + @Override + public void run() { + Iterator iter = mConnections.values().iterator(); + while (iter.hasNext()){ + ConnectionRecord connection = iter.next(); + try { + connection.callbacks.onConnect(connection.root.getRootId(), token, + connection.root.getExtras()); + } catch (RemoteException e) { + Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); + iter.remove(); + } + } + } + }); + } + + /** + * Gets the session token, or null if it has not yet been created + * or if it has been destroyed. + */ + public @Nullable MediaSession.Token getSessionToken() { + return mSession; + } + + /** + * Gets the root hints sent from the currently connected {@link MediaBrowser}. + * The root hints are service-specific arguments included in an optional bundle sent to the + * media browser service when connecting and retrieving the root id for browsing, or null if + * none. The contents of this bundle may affect the information returned when browsing. + * + * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or + * {@link #onLoadChildren} or {@link #onLoadItem}. + * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT + * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE + * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED + */ + public final Bundle getBrowserRootHints() { + if (mCurConnection == null) { + throw new IllegalStateException("This should be called inside of onGetRoot or" + + " onLoadChildren or onLoadItem methods"); + } + return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); + } + + /** + * Gets the browser information who sent the current request. + * + * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or + * {@link #onLoadChildren} or {@link #onLoadItem}. + * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) + */ + public final RemoteUserInfo getCurrentBrowserInfo() { + if (mCurConnection == null) { + throw new IllegalStateException("This should be called inside of onGetRoot or" + + " onLoadChildren or onLoadItem methods"); + } + //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo + /* + return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid, + mCurConnection.callbacks.asBinder()); + */ + return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); + } + + /** + * Notifies all connected media browsers that the children of + * the specified parent id have changed in some way. + * This will cause browsers to fetch subscribed content again. + * + * @param parentId The id of the parent media item whose + * children changed. + */ + public void notifyChildrenChanged(@NonNull String parentId) { + notifyChildrenChangedInternal(parentId, null); + } + + /** + * Notifies all connected media browsers that the children of + * the specified parent id have changed in some way. + * This will cause browsers to fetch subscribed content again. + * + * @param parentId The id of the parent media item whose + * children changed. + * @param options The bundle of service-specific arguments to send + * to the media browser. The contents of this bundle may + * contain the information about the change. + */ + public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) { + if (options == null) { + throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged"); + } + notifyChildrenChangedInternal(parentId, options); + } + + private void notifyChildrenChangedInternal(final String parentId, final Bundle options) { + if (parentId == null) { + throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); + } + mHandler.post(new Runnable() { + @Override + public void run() { + for (IBinder binder : mConnections.keySet()) { + ConnectionRecord connection = mConnections.get(binder); + List> callbackList = + connection.subscriptions.get(parentId); + if (callbackList != null) { + for (Pair callback : callbackList) { + if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) { + performLoadChildren(parentId, connection, callback.second); + } + } + } + } + } + }); + } + + /** + * Return whether the given package is one of the ones that is owned by the uid. + */ + private boolean isValidPackage(String pkg, int uid) { + if (pkg == null) { + return false; + } + final PackageManager pm = getPackageManager(); + final String[] packages = pm.getPackagesForUid(uid); + final int N = packages.length; + for (int i=0; i> callbackList = connection.subscriptions.get(id); + if (callbackList == null) { + callbackList = new ArrayList<>(); + } + for (Pair callback : callbackList) { + if (token == callback.first + && MediaBrowserUtils.areSameOptions(options, callback.second)) { + return; + } + } + callbackList.add(new Pair<>(token, options)); + connection.subscriptions.put(id, callbackList); + // send the results + performLoadChildren(id, connection, options); + } + + /** + * Remove the subscription. + */ + private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) { + if (token == null) { + return connection.subscriptions.remove(id) != null; + } + boolean removed = false; + List> callbackList = connection.subscriptions.get(id); + if (callbackList != null) { + Iterator> iter = callbackList.iterator(); + while (iter.hasNext()){ + if (token == iter.next().first) { + removed = true; + iter.remove(); + } + } + if (callbackList.size() == 0) { + connection.subscriptions.remove(id); + } + } + return removed; + } + + /** + * Call onLoadChildren and then send the results back to the connection. + *

    + * Callers must make sure that this connection is still connected. + */ + private void performLoadChildren(final String parentId, final ConnectionRecord connection, + final Bundle options) { + final Result> result + = new Result>(parentId) { + @Override + void onResultSent(List list, @ResultFlags int flag) { + if (mConnections.get(connection.callbacks.asBinder()) != connection) { + if (DBG) { + Log.d(TAG, "Not sending onLoadChildren result for connection that has" + + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); + } + return; + } + + List filteredList = + (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 + ? applyOptions(list, options) : list; + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* + final ParceledListSlice pls = + filteredList == null ? null : new ParceledListSlice<>(filteredList); + try { + connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options); + } catch (RemoteException ex) { + // The other side is in the process of crashing. + Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId + + " package=" + connection.pkg); + } + */ + } + }; + + mCurConnection = connection; + if (options == null) { + onLoadChildren(parentId, result); + } else { + onLoadChildren(parentId, result, options); + } + mCurConnection = null; + + if (!result.isDone()) { + throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" + + " before returning for package=" + connection.pkg + " id=" + parentId); + } + } + + private List applyOptions(List list, + final Bundle options) { + if (list == null) { + return null; + } + int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1); + int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); + if (page == -1 && pageSize == -1) { + return list; + } + int fromIndex = pageSize * page; + int toIndex = fromIndex + pageSize; + if (page < 0 || pageSize < 1 || fromIndex >= list.size()) { + return Collections.EMPTY_LIST; + } + if (toIndex > list.size()) { + toIndex = list.size(); + } + return list.subList(fromIndex, toIndex); + } + + private void performLoadItem(String itemId, final ConnectionRecord connection, + final ResultReceiver receiver) { + final Result result = + new Result(itemId) { + @Override + void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { + if (mConnections.get(connection.callbacks.asBinder()) != connection) { + if (DBG) { + Log.d(TAG, "Not sending onLoadItem result for connection that has" + + " been disconnected. pkg=" + connection.pkg + " id=" + itemId); + } + return; + } + if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) { + receiver.send(RESULT_ERROR, null); + return; + } + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_MEDIA_ITEM, item); + receiver.send(RESULT_OK, bundle); + } + }; + + mCurConnection = connection; + onLoadItem(itemId, result); + mCurConnection = null; + + if (!result.isDone()) { + throw new IllegalStateException("onLoadItem must call detach() or sendResult()" + + " before returning for id=" + itemId); + } + } + + /** + * Contains information that the browser service needs to send to the client + * when first connected. + */ + public static final class BrowserRoot { + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for recently played media items. + * + *

    When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving media items that are recently played. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + *

    The root hint may contain multiple keys. + * + * @see #EXTRA_OFFLINE + * @see #EXTRA_SUGGESTED + */ + public static final String EXTRA_RECENT = "android.service.media.extra.RECENT"; + + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for offline media items. + * + *

    When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving media items that are can be played without an + * internet connection. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + *

    The root hint may contain multiple keys. + * + * @see #EXTRA_RECENT + * @see #EXTRA_SUGGESTED + */ + public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; + + /** + * The lookup key for a boolean that indicates whether the browser service should return a + * browser root for suggested media items. + * + *

    When creating a media browser for a given media browser service, this key can be + * supplied as a root hint for retrieving the media items suggested by the media browser + * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)} + * is considered ordered by relevance, first being the top suggestion. + * If the media browser service can provide such media items, the implementation must return + * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. + * + *

    The root hint may contain multiple keys. + * + * @see #EXTRA_RECENT + * @see #EXTRA_OFFLINE + */ + public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; + + final private String mRootId; + final private Bundle mExtras; + + /** + * Constructs a browser root. + * @param rootId The root id for browsing. + * @param extras Any extras about the browser service. + */ + public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { + if (rootId == null) { + throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + + "Use null for BrowserRoot instead."); + } + mRootId = rootId; + mExtras = extras; + } + + /** + * Gets the root id for browsing. + */ + public String getRootId() { + return mRootId; + } + + /** + * Gets any extras about the browser service. + */ + public Bundle getExtras() { + return mExtras; + } + } +} -- GitLab From e94f909e4017b00dba14db06ebe0b23cbeade7be Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Tue, 20 Nov 2018 10:53:19 +0900 Subject: [PATCH 0487/1530] Commented unresolved hidden API usages Bug: 119539695 Test: Build Change-Id: I6d464226eaf338e953b3a8e245af82b4cba5e221 --- .../java/android/media/MediaDescription.java | 10 ++- .../java/android/media/MediaMetadata.java | 4 +- .../android/media/browse/MediaBrowser.java | 8 ++- .../java/android/media/session/ISession.aidl | 8 ++- .../media/session/ISessionController.aidl | 5 +- .../session/ISessionControllerCallback.aidl | 5 +- .../media/session/MediaController.java | 71 +++++++++++-------- .../android/media/session/MediaSession.java | 29 ++++++-- .../android/media/session/PlaybackState.java | 13 ++-- .../media/IMediaBrowserServiceCallbacks.aidl | 7 +- 10 files changed, 106 insertions(+), 54 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java index e6aea99ef5..ad71d9f1c3 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaDescription.java +++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.java @@ -122,9 +122,10 @@ public class MediaDescription implements Parcelable { private MediaDescription(Parcel in) { mMediaId = in.readString(); - mTitle = in.readCharSequence(); - mSubtitle = in.readCharSequence(); - mDescription = in.readCharSequence(); + //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence + mTitle = ""; //in.readCharSequence(); + mSubtitle = ""; //in.readCharSequence(); + mDescription = ""; //in.readCharSequence(); mIcon = in.readParcelable(null); mIconUri = in.readParcelable(null); mExtras = in.readBundle(); @@ -210,9 +211,12 @@ public class MediaDescription implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mMediaId); + //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence + /* dest.writeCharSequence(mTitle); dest.writeCharSequence(mSubtitle); dest.writeCharSequence(mDescription); + */ dest.writeParcelable(mIcon, flags); dest.writeParcelable(mIconUri, flags); dest.writeBundle(mExtras); diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java index 2721ad12b1..33e6916173 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java +++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java @@ -422,7 +422,9 @@ public final class MediaMetadata implements Parcelable { } private MediaMetadata(Parcel in) { - mBundle = Bundle.setDefusable(in.readBundle(), true); + //TODO(b/119789387): Resolve hidden API usage: Bundle#setDefusable + //mBundle = Bundle.setDefusable(in.readBundle(), true); + mBundle = new Bundle(); //TODO:remove this. } /** diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java index 2bccd884be..4e091ad48d 100644 --- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java +++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java @@ -23,7 +23,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.media.MediaDescription; import android.media.session.MediaController; import android.media.session.MediaSession; @@ -652,6 +652,8 @@ public final class MediaBrowser { }); } + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, final String parentId, final ParceledListSlice list, final Bundle options) { mHandler.post(new Runnable() { @@ -697,6 +699,7 @@ public final class MediaBrowser { } }); } + */ /** * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. @@ -1106,6 +1109,8 @@ public final class MediaBrowser { } } + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* @Override public void onLoadChildren(String parentId, ParceledListSlice list) { onLoadChildrenWithOptions(parentId, list, null); @@ -1119,6 +1124,7 @@ public final class MediaBrowser { mediaBrowser.onLoadChildren(this, parentId, list, options); } } + */ } private static class Subscription { diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl index bd0019f0f1..cbd93cb817 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl @@ -17,7 +17,7 @@ package android.media.session; import android.app.PendingIntent; import android.content.pm.ParceledListSlice; -import android.media.AudioAttributes; +//import android.media.AudioAttributes; import android.media.MediaMetadata; import android.media.session.ISessionController; import android.media.session.PlaybackState; @@ -41,13 +41,15 @@ interface ISession { // These commands are for the TransportPerformer void setMetadata(in MediaMetadata metadata); void setPlaybackState(in PlaybackState state); - void setQueue(in ParceledListSlice queue); + //TODO(b/119750807): Resolve hidden API usage ParceledListSlice. + //void setQueue(in ParceledListSlice queue); void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); // These commands relate to volume handling - void setPlaybackToLocal(in AudioAttributes attributes); + //TODO(b/119751592): Decide if AudioAttributes should be updated. + //void setPlaybackToLocal(in AudioAttributes attributes); void setPlaybackToRemote(int control, int max); void setCurrentVolume(int currentVolume); } diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl index 861a8cea7d..031a388722 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl @@ -17,7 +17,7 @@ package android.media.session; import android.app.PendingIntent; import android.content.Intent; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; @@ -81,7 +81,8 @@ interface ISessionController { String action, in Bundle args); MediaMetadata getMetadata(); PlaybackState getPlaybackState(); - ParceledListSlice getQueue(); + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + //ParceledListSlice getQueue(); CharSequence getQueueTitle(); Bundle getExtras(); int getRatingType(); diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl index cf3176706d..173504b946 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl @@ -15,7 +15,7 @@ package android.media.session; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; @@ -32,7 +32,8 @@ oneway interface ISessionControllerCallback { // These callbacks are for the TransportController void onPlaybackStateChanged(in PlaybackState state); void onMetadataChanged(in MediaMetadata metadata); - void onQueueChanged(in ParceledListSlice queue); + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + //void onQueueChanged(in ParceledListSlice queue); void onQueueTitleChanged(CharSequence title); void onExtrasChanged(in Bundle extras); void onVolumeInfoChanged(in ParcelableVolumeInfo info); diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index c4b82c3141..1f2918595d 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaMetadata; @@ -149,11 +149,16 @@ public final class MediaController { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } + //TODO(b/119789707): Resolve hidden API usage: KeyEvent#isMediaKey + /* if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { return false; } + */ try { - return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub, + //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file. + // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling. + return mSessionBinder.sendMediaButton("mContext.getOpPackageName()", mCbStub, asSystemService, keyEvent); } catch (RemoteException e) { // System is dead. =( @@ -186,7 +191,7 @@ public final class MediaController { break; } try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, + mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, direction, AudioManager.FLAG_SHOW_UI); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); @@ -194,10 +199,11 @@ public final class MediaController { } case KeyEvent.ACTION_UP: { - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE - | AudioManager.FLAG_FROM_KEY; + //TODO(b/119790339): Resolve hidden API usage. AudioManager.FLAG_FROM_KEY + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; + //| AudioManager.FLAG_FROM_KEY; try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0, + mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); @@ -241,6 +247,8 @@ public final class MediaController { * @return The current play queue or null. */ public @Nullable List getQueue() { + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* try { ParceledListSlice queue = mSessionBinder.getQueue(); if (queue != null) { @@ -249,6 +257,7 @@ public final class MediaController { } catch (RemoteException e) { Log.wtf(TAG, "Error calling getQueue.", e); } + */ return null; } @@ -367,7 +376,7 @@ public final class MediaController { */ public void setVolumeTo(int value, int flags) { try { - mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags); + mSessionBinder.setVolumeTo("mContext.getOpPackageName()", mCbStub, value, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling setVolumeTo.", e); } @@ -388,7 +397,7 @@ public final class MediaController { */ public void adjustVolume(int direction, int flags) { try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction, + mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, false, direction, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); @@ -455,7 +464,7 @@ public final class MediaController { throw new IllegalArgumentException("command cannot be null or empty"); } try { - mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb); + mSessionBinder.sendCommand("mContext.getOpPackageName()", mCbStub, command, args, cb); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCommand.", e); } @@ -521,7 +530,7 @@ public final class MediaController { if (!mCbRegistered) { try { - mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub); + mSessionBinder.registerCallbackListener("mContext.getOpPackageName()", mCbStub); mCbRegistered = true; } catch (RemoteException e) { Log.e(TAG, "Dead object in registerCallback", e); @@ -668,7 +677,7 @@ public final class MediaController { */ public void prepare() { try { - mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub); + mSessionBinder.prepare("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare.", e); } @@ -692,7 +701,7 @@ public final class MediaController { "You must specify a non-empty String for prepareFromMediaId."); } try { - mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + mSessionBinder.prepareFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); @@ -719,7 +728,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query, + mSessionBinder.prepareFromSearch("mContext.getOpPackageName()", mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + query + ").", e); @@ -744,7 +753,7 @@ public final class MediaController { "You must specify a non-empty Uri for prepareFromUri."); } try { - mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + mSessionBinder.prepareFromUri("mContext.getOpPackageName()", mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); } @@ -755,7 +764,7 @@ public final class MediaController { */ public void play() { try { - mSessionBinder.play(mContext.getOpPackageName(), mCbStub); + mSessionBinder.play("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play.", e); } @@ -774,7 +783,7 @@ public final class MediaController { "You must specify a non-empty String for playFromMediaId."); } try { - mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + mSessionBinder.playFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); @@ -797,7 +806,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras); + mSessionBinder.playFromSearch("mContext.getOpPackageName()", mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + query + ").", e); } @@ -816,7 +825,7 @@ public final class MediaController { "You must specify a non-empty Uri for playFromUri."); } try { - mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + mSessionBinder.playFromUri("mContext.getOpPackageName()", mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + uri + ").", e); } @@ -828,7 +837,7 @@ public final class MediaController { */ public void skipToQueueItem(long id) { try { - mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id); + mSessionBinder.skipToQueueItem("mContext.getOpPackageName()", mCbStub, id); } catch (RemoteException e) { Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); } @@ -840,7 +849,7 @@ public final class MediaController { */ public void pause() { try { - mSessionBinder.pause(mContext.getOpPackageName(), mCbStub); + mSessionBinder.pause("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling pause.", e); } @@ -852,7 +861,7 @@ public final class MediaController { */ public void stop() { try { - mSessionBinder.stop(mContext.getOpPackageName(), mCbStub); + mSessionBinder.stop("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling stop.", e); } @@ -865,7 +874,7 @@ public final class MediaController { */ public void seekTo(long pos) { try { - mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos); + mSessionBinder.seekTo("mContext.getOpPackageName()", mCbStub, pos); } catch (RemoteException e) { Log.wtf(TAG, "Error calling seekTo.", e); } @@ -877,7 +886,7 @@ public final class MediaController { */ public void fastForward() { try { - mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub); + mSessionBinder.fastForward("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling fastForward.", e); } @@ -888,7 +897,7 @@ public final class MediaController { */ public void skipToNext() { try { - mSessionBinder.next(mContext.getOpPackageName(), mCbStub); + mSessionBinder.next("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling next.", e); } @@ -900,7 +909,7 @@ public final class MediaController { */ public void rewind() { try { - mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub); + mSessionBinder.rewind("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rewind.", e); } @@ -911,7 +920,7 @@ public final class MediaController { */ public void skipToPrevious() { try { - mSessionBinder.previous(mContext.getOpPackageName(), mCbStub); + mSessionBinder.previous("mContext.getOpPackageName()", mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling previous.", e); } @@ -926,7 +935,7 @@ public final class MediaController { */ public void setRating(Rating rating) { try { - mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating); + mSessionBinder.rate("mContext.getOpPackageName()", mCbStub, rating); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rate.", e); } @@ -961,7 +970,7 @@ public final class MediaController { throw new IllegalArgumentException("CustomAction cannot be null."); } try { - mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args); + mSessionBinder.sendCustomAction("mContext.getOpPackageName()", mCbStub, action, args); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCustomAction.", e); } @@ -1097,6 +1106,8 @@ public final class MediaController { } } + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* @Override public void onQueueChanged(ParceledListSlice parceledQueue) { List queue = parceledQueue == null ? null : parceledQueue @@ -1106,6 +1117,7 @@ public final class MediaController { controller.postMessage(MSG_UPDATE_QUEUE, queue, null); } } + */ @Override public void onQueueTitleChanged(CharSequence title) { @@ -1140,7 +1152,8 @@ public final class MediaController { private boolean mRegistered = false; public MessageHandler(Looper looper, MediaController.Callback cb) { - super(looper, null, true); + //TODO:(b/119539849) Uncomment below line and resolve the error. + // super(looper, null, true); mCallback = cb; } diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index d43cd309d1..b3ebbc8ac6 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -24,7 +24,7 @@ import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; @@ -150,7 +150,9 @@ public final class MediaSession { * @param tag A short name for debugging purposes. */ public MediaSession(@NonNull Context context, @NonNull String tag) { - this(context, tag, UserHandle.myUserId()); + //TODO(b/119749861): Resolve hidden API usage, UserHandle.myUserId + //this(context, tag, UserHandle.myUserId()); + this(context, tag, 0); //TODO: remove this. } /** @@ -171,13 +173,17 @@ public final class MediaSession { if (TextUtils.isEmpty(tag)) { throw new IllegalArgumentException("tag cannot be null or empty"); } - mMaxBitmapSize = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); + //TODO(b/119749798): Resolve hidden API usage. com.android.internal.R + //mMaxBitmapSize = context.getResources().getDimensionPixelSize( + //com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); + mMaxBitmapSize = 1024; //TODO: remove this. mCbStub = new CallbackStub(this); MediaSessionManager manager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); try { - mBinder = manager.createSession(mCbStub, tag, userId); + //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession + //mBinder = manager.createSession(mCbStub, tag, userId); + mBinder = null; //TODO: remove this. mSessionToken = new Token(mBinder.getController()); mController = new MediaController(context, mSessionToken); } catch (RemoteException e) { @@ -287,11 +293,14 @@ public final class MediaSession { if (attributes == null) { throw new IllegalArgumentException("Attributes cannot be null for local playback."); } + //TODO(b/119751592): Decide if AudioAttributes should be updated. + /* try { mBinder.setPlaybackToLocal(attributes); } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); } + */ } /** @@ -456,11 +465,14 @@ public final class MediaSession { * @param queue A list of items in the play queue. */ public void setQueue(@Nullable List queue) { + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + /* try { mBinder.setQueue(queue == null ? null : new ParceledListSlice(queue)); } catch (RemoteException e) { Log.wtf("Dead object in setQueue.", e); } + */ } /** @@ -1062,8 +1074,12 @@ public final class MediaSession { private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, ISessionControllerCallback caller) { + //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo + /* return new RemoteUserInfo(packageName, pid, uid, caller != null ? caller.asBinder() : null); + */ + return new RemoteUserInfo(packageName, pid, uid); } @Override @@ -1443,7 +1459,8 @@ public final class MediaSession { private RemoteUserInfo mCurrentControllerInfo; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { - super(looper, null, true); + //TODO:(b/119539849) Uncomment below line and resolve the error. + //super(looper, null, true); mCallback = callback; mCallback.mHandler = this; } diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java index 17d16b8966..38df10d726 100644 --- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java +++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java @@ -318,7 +318,9 @@ public final class PlaybackState implements Parcelable { mActions = in.readLong(); mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); mActiveItemId = in.readLong(); - mErrorMessage = in.readCharSequence(); + //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence + //mErrorMessage = in.readCharSequence(); + mErrorMessage = ""; //TODO: remove this. mExtras = in.readBundle(); } @@ -353,7 +355,8 @@ public final class PlaybackState implements Parcelable { dest.writeLong(mActions); dest.writeTypedList(mCustomActions); dest.writeLong(mActiveItemId); - dest.writeCharSequence(mErrorMessage); + //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence + //dest.writeCharSequence(mErrorMessage); dest.writeBundle(mExtras); } @@ -497,7 +500,8 @@ public final class PlaybackState implements Parcelable { return STATE_ERROR; case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: return STATE_FAST_FORWARDING; - case RemoteControlClient.PLAYSTATE_NONE: + //RemoteControlClient.PLAYSTATE_NONE is hidden + case 0: //RemoteControlClient.PLAYSTATE_NONE: return STATE_NONE; case RemoteControlClient.PLAYSTATE_PAUSED: return STATE_PAUSED; @@ -533,7 +537,8 @@ public final class PlaybackState implements Parcelable { case STATE_FAST_FORWARDING: return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; case STATE_NONE: - return RemoteControlClient.PLAYSTATE_NONE; + //RemoteControlClient.PLAYSTATE_NONE is hidden + return 0; //RemoteControlClient.PLAYSTATE_NONE; case STATE_PAUSED: return RemoteControlClient.PLAYSTATE_PAUSED; case STATE_PLAYING: diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl index deeab1a8a2..bcc282620d 100644 --- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl +++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl @@ -2,7 +2,7 @@ package android.service.media; -import android.content.pm.ParceledListSlice; +//import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; import android.media.session.MediaSession; import android.os.Bundle; @@ -22,6 +22,7 @@ oneway interface IMediaBrowserServiceCallbacks { */ void onConnect(String root, in MediaSession.Token session, in Bundle extras); void onConnectFailed(); - void onLoadChildren(String mediaId, in ParceledListSlice list); - void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options); + //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. + //void onLoadChildren(String mediaId, in ParceledListSlice list); + //void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options); } -- GitLab From b4dbc2d46603e43bd9d6f2bd76acb2842ce76498 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 20 Nov 2018 16:16:08 +0900 Subject: [PATCH 0488/1530] Replace hidden Parcel.(read|write)CharSequence usages This CL replaces above hidden API usages with below: - read: TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel - write: TextUtils.writeToParcel The hidden APIs actually call those replacements, so this is safe to do. Bug: 119783509 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: Ifb3b432bee7ee072bbd544cb067749f073e5917a --- .../java/android/media/MediaDescription.java | 17 +++++++---------- .../android/media/session/PlaybackState.java | 7 ++----- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java index ad71d9f1c3..31079e5c54 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaDescription.java +++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.java @@ -7,6 +7,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; /** * A simple set of metadata for a media item suitable for display. This can be @@ -122,10 +123,9 @@ public class MediaDescription implements Parcelable { private MediaDescription(Parcel in) { mMediaId = in.readString(); - //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence - mTitle = ""; //in.readCharSequence(); - mSubtitle = ""; //in.readCharSequence(); - mDescription = ""; //in.readCharSequence(); + mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mIcon = in.readParcelable(null); mIconUri = in.readParcelable(null); mExtras = in.readBundle(); @@ -211,12 +211,9 @@ public class MediaDescription implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mMediaId); - //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence - /* - dest.writeCharSequence(mTitle); - dest.writeCharSequence(mSubtitle); - dest.writeCharSequence(mDescription); - */ + TextUtils.writeToParcel(mTitle, dest, 0); + TextUtils.writeToParcel(mSubtitle, dest, 0); + TextUtils.writeToParcel(mDescription, dest, 0); dest.writeParcelable(mIcon, flags); dest.writeParcelable(mIconUri, flags); dest.writeBundle(mExtras); diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java index 38df10d726..ed4f9af72f 100644 --- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java +++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java @@ -318,9 +318,7 @@ public final class PlaybackState implements Parcelable { mActions = in.readLong(); mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); mActiveItemId = in.readLong(); - //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence - //mErrorMessage = in.readCharSequence(); - mErrorMessage = ""; //TODO: remove this. + mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mExtras = in.readBundle(); } @@ -355,8 +353,7 @@ public final class PlaybackState implements Parcelable { dest.writeLong(mActions); dest.writeTypedList(mCustomActions); dest.writeLong(mActiveItemId); - //TODO(b/119783509): Resolve hidden API Usage. Parcel#{read,write}CharSequence - //dest.writeCharSequence(mErrorMessage); + TextUtils.writeToParcel(mErrorMessage, dest, 0); dest.writeBundle(mExtras); } -- GitLab From ea38886d589a19676b528ccbf45ef8d240323bfb Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 20 Nov 2018 02:11:30 -0800 Subject: [PATCH 0489/1530] NdkMediaDataSource: fix getAvailableSize Bug: 119127316 Test: NativeDecoderTest#testExtractorCachedDurationNative Change-Id: I92b751f23d2598ea6c46ab79b80f9731f60b55e9 --- media/ndk/NdkMediaDataSource.cpp | 1 + media/ndk/NdkMediaDataSourceCallbacks.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp index 3c10024bb3..1abee93478 100644 --- a/media/ndk/NdkMediaDataSource.cpp +++ b/media/ndk/NdkMediaDataSource.cpp @@ -59,6 +59,7 @@ NdkDataSource::NdkDataSource(AMediaDataSource *dataSource) AMediaDataSource_setGetSize(mDataSource, dataSource->getSize); AMediaDataSource_setClose(mDataSource, dataSource->close); AMediaDataSource_setUserdata(mDataSource, dataSource->userdata); + AMediaDataSource_setGetAvailableSize(mDataSource, dataSource->getAvailableSize); mDataSource->mImpl = dataSource->mImpl; mDataSource->mFlags = dataSource->mFlags; } diff --git a/media/ndk/NdkMediaDataSourceCallbacks.cpp b/media/ndk/NdkMediaDataSourceCallbacks.cpp index f40387f152..86a57da2a8 100644 --- a/media/ndk/NdkMediaDataSourceCallbacks.cpp +++ b/media/ndk/NdkMediaDataSourceCallbacks.cpp @@ -43,8 +43,8 @@ void DataSource_close(void *userdata) { ssize_t DataSource_getAvailableSize(void *userdata, off64_t offset) { off64_t size = -1; DataSource *source = static_cast(userdata); - status_t err = source->getAvailableSize(offset, &size); - return err == OK ? size : -1; + source->getAvailableSize(offset, &size); + return size; } } // namespace android -- GitLab From c7c1542ea3fef663c2395404e6f111dc01e625b4 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 20 Nov 2018 16:49:14 +0900 Subject: [PATCH 0490/1530] Replace hidden KeyEvent.isMediaKey() usages This CL copies the hidden KeyEvent.isMediaKey() method into MediaController, and replace the usages with it. Bug: 119789707 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: I60e94c78f603378e5a4e3d54502cca1b36ef4d4e --- .../apex/java/android/media/MediaUtils.java | 28 +++++++++++++++++++ .../media/session/MediaController.java | 6 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 packages/MediaComponents/apex/java/android/media/MediaUtils.java diff --git a/packages/MediaComponents/apex/java/android/media/MediaUtils.java b/packages/MediaComponents/apex/java/android/media/MediaUtils.java new file mode 100644 index 0000000000..282c2185b3 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaUtils.java @@ -0,0 +1,28 @@ +package android.media; + +import android.view.KeyEvent; + +/** + * @hide + */ +public class MediaUtils { + + // Keep sync with KeyEvent#isMediaKey(). + public static boolean isMediaKey(int keyCode) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + return true; + } + return false; + } +} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 1f2918595d..1a2a5281ee 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -25,6 +25,7 @@ import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaMetadata; +import android.media.MediaUtils; import android.media.Rating; import android.media.VolumeProvider; import android.net.Uri; @@ -149,12 +150,9 @@ public final class MediaController { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } - //TODO(b/119789707): Resolve hidden API usage: KeyEvent#isMediaKey - /* - if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + if (!MediaUtils.isMediaKey(keyEvent.getKeyCode())) { return false; } - */ try { //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file. // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling. -- GitLab From f22dc8bf477546268155f6616a97cb8823f24dd4 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 20 Nov 2018 09:46:55 -0800 Subject: [PATCH 0491/1530] MPEG4Extractor: fix CTS test failure Update format keys to the correct ones Bug: 119780931 Bug: 111407253 Test: CTS Change-Id: Ib06fdd8ff7f78a06302416b439b8ad0d8e08809a --- media/extractors/mp4/MPEG4Extractor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index a11acc151e..c3bdc928cb 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -3642,7 +3642,7 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept if (year < 10000) { sprintf(tmp, "%u", year); - AMediaFormat_setString(mFileMetaData, "year", tmp); + AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_YEAR, tmp); } break; } @@ -4716,7 +4716,7 @@ status_t MPEG4Source::parseClearEncryptedSizes( off64_t offset, bool isSubsampleEncryption, uint32_t flags) { int32_t ivlength; - CHECK(AMediaFormat_getInt32(mFormat, "crypto-defaultivsize", &ivlength)); + CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &ivlength)); // only 0, 8 and 16 byte initialization vectors are supported if (ivlength != 0 && ivlength != 8 && ivlength != 16) { -- GitLab From 6ba3f5e527169386472f0fb0b8ccd3184ecfd435 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 20 Nov 2018 10:04:08 -0800 Subject: [PATCH 0492/1530] Camera: Do not update state at HAL starts up if state already exists This is to handle lazy hal, where cameraserver doesn't know HAL goes away. Test: Observe that the between QS Torch Tile and Camera App is correct. Bug: 79374634 Change-Id: I2f802b1c409ba3581f0fcacfc0ac5f6059391139 --- services/camera/libcameraservice/CameraService.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 4dacd0273e..ce35ad9186 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -190,7 +190,9 @@ status_t CameraService::enumerateProviders() { for (auto& cameraId : deviceIds) { String8 id8 = String8(cameraId.c_str()); - onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT); + if (getCameraState(id8) == nullptr) { + onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT); + } } return OK; -- GitLab From 092f74a609334e2f5e39d3afa1a35fbc69730c13 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 20 Nov 2018 10:25:13 -0800 Subject: [PATCH 0493/1530] capture frame rate for player records media.metrics gets the frame rate of playback within the nuplayer records. Bug: 119817586 Test: logcat --- media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp | 1 + media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 050e4fb292..a2ec699927 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -110,6 +110,7 @@ sp NuPlayer::Decoder::getStats() const { mStats->setInt64("frames-total", mNumFramesTotal); mStats->setInt64("frames-dropped-input", mNumInputFramesDropped); mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); + mStats->setFloat("frame-rate-total", mFrameRateTotal); return mStats; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index f2c8f64e03..44f223d29d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -53,6 +53,7 @@ static const char *kPlayerWidth = "android.media.mediaplayer.width"; static const char *kPlayerHeight = "android.media.mediaplayer.height"; static const char *kPlayerFrames = "android.media.mediaplayer.frames"; static const char *kPlayerFramesDropped = "android.media.mediaplayer.dropped"; +static const char *kPlayerFrameRate = "android.media.mediaplayer.fps"; static const char *kPlayerAMime = "android.media.mediaplayer.audio.mime"; static const char *kPlayerACodec = "android.media.mediaplayer.audio.codec"; static const char *kPlayerDuration = "android.media.mediaplayer.durationMs"; @@ -577,6 +578,10 @@ void NuPlayerDriver::updateMetrics(const char *where) { mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal); mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped); + float frameRate = 0; + if (stats->findFloat("frame-rate-total", &frameRate)) { + mAnalyticsItem->setDouble(kPlayerFrameRate, (double) frameRate); + } } else if (mime.startsWith("audio/")) { mAnalyticsItem->setCString(kPlayerAMime, mime.c_str()); @@ -602,6 +607,7 @@ void NuPlayerDriver::updateMetrics(const char *where) { mAnalyticsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 ); mAnalyticsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents); mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit); + } mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType()); -- GitLab From 7e741df76ce8eed1e6af42d7a0d7d74ef21f373b Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Sat, 17 Nov 2018 14:00:32 -0800 Subject: [PATCH 0494/1530] VideoView2Impl: corresponding change for DataSourceDesc Test: cts Bug: 112549021 Change-Id: Ie1bfea9b7a4375e709bd7e746a5e36ffd08f7087 --- .../tests/src/com/android/media/SessionPlaylistAgentTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java index beb0848dea..08076009a0 100644 --- a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java +++ b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; import android.content.Context; import android.media.AudioAttributes; import android.media.DataSourceDesc; +import android.media.UriDataSourceDesc; import android.media.MediaItem2; import android.media.MediaMetadata2; import android.media.MediaPlayerBase; @@ -119,7 +120,7 @@ public class SessionPlaylistAgentTest extends AndroidTestCase { if (item.getMediaId().contains("WITHOUT_DSD")) { return null; } - return new DataSourceDesc.Builder() + return new UriDataSourceDesc.Builder() .setDataSource(getContext(), Uri.parse("dsd://test")) .setMediaId(item.getMediaId()) .build(); -- GitLab From a0438423a43e55aa48872e420d753803b22190d7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 20 Nov 2018 10:59:32 -0800 Subject: [PATCH 0495/1530] Convert frame count metadata key AMEDIAFORMAT_KEY_FRAME_COUNT/kKeyFrameCount weren't being converted. Bug: 119781577 Bug: 111407253 Test: CTS Change-Id: I312cf8bac3b8471c0c2e10ff376ded7378086638 --- media/libstagefright/Utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index dd29474aa4..4aee9d5940 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -608,6 +608,7 @@ static std::vector> int32Mappings { { "temporal-layer-count", kKeyTemporalLayerCount }, { "thumbnail-width", kKeyThumbnailWidth }, { "thumbnail-height", kKeyThumbnailHeight }, + { "frame-count", kKeyFrameCount }, } }; -- GitLab From 68c09c94a3f22ed64e8bb0467ae4777924de9434 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Tue, 20 Nov 2018 12:24:39 -0800 Subject: [PATCH 0496/1530] Remove USE_LEGACY_LOCAL_AUDIO_HAL in libsoundtriggerservice This had been removed from libaudiohal last year in 9e79c70529c07f2157f8077f024f42fa5c9e22f9 Test: treehugger Change-Id: I8feb2eb88f8f0ba76b3417cf6f724cd6aa55bd0e --- services/soundtrigger/Android.mk | 13 -- .../soundtrigger/SoundTriggerHalLegacy.cpp | 127 ------------------ services/soundtrigger/SoundTriggerHalLegacy.h | 85 ------------ .../soundtrigger/SoundTriggerHwService.cpp | 4 - 4 files changed, 229 deletions(-) delete mode 100644 services/soundtrigger/SoundTriggerHalLegacy.cpp delete mode 100644 services/soundtrigger/SoundTriggerHalLegacy.h diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk index 65f1a44417..0f093934da 100644 --- a/services/soundtrigger/Android.mk +++ b/services/soundtrigger/Android.mk @@ -16,13 +16,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1) - ifneq ($(USE_LEGACY_LOCAL_AUDIO_HAL), true) - $(error Requires building with USE_LEGACY_LOCAL_AUDIO_HAL=true) - endif - LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE -endif - LOCAL_SRC_FILES:= \ SoundTriggerHwService.cpp @@ -36,11 +29,6 @@ LOCAL_SHARED_LIBRARIES:= \ libaudioclient \ libmediautils \ -ifeq ($(USE_LEGACY_LOCAL_AUDIO_HAL),true) -# libhardware configuration -LOCAL_SRC_FILES += \ - SoundTriggerHalLegacy.cpp -else # Treble configuration LOCAL_SRC_FILES += \ SoundTriggerHalHidl.cpp @@ -59,7 +47,6 @@ LOCAL_SHARED_LIBRARIES += \ android.hardware.audio.common@2.0 \ android.hidl.allocator@1.0 \ android.hidl.memory@1.0 -endif LOCAL_C_INCLUDES += \ diff --git a/services/soundtrigger/SoundTriggerHalLegacy.cpp b/services/soundtrigger/SoundTriggerHalLegacy.cpp deleted file mode 100644 index 2b78818a2b..0000000000 --- a/services/soundtrigger/SoundTriggerHalLegacy.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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. - */ - -#include -#include "SoundTriggerHalLegacy.h" - -namespace android { - -/* static */ -sp SoundTriggerHalInterface::connectModule(const char *moduleName) -{ - return new SoundTriggerHalLegacy(moduleName); -} - -SoundTriggerHalLegacy::SoundTriggerHalLegacy(const char *moduleName) - : mModuleName(moduleName), mHwDevice(NULL) -{ -} - -void SoundTriggerHalLegacy::onFirstRef() -{ - const hw_module_t *mod; - int rc; - - if (mModuleName == NULL) { - mModuleName = "primary"; - } - - rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, &mod); - if (rc != 0) { - ALOGE("couldn't load sound trigger module %s.%s (%s)", - SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc)); - return; - } - rc = sound_trigger_hw_device_open(mod, &mHwDevice); - if (rc != 0) { - ALOGE("couldn't open sound trigger hw device in %s.%s (%s)", - SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc)); - mHwDevice = NULL; - return; - } - if (mHwDevice->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 || - mHwDevice->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) { - ALOGE("wrong sound trigger hw device version %04x", mHwDevice->common.version); - return; - } -} - -SoundTriggerHalLegacy::~SoundTriggerHalLegacy() -{ - if (mHwDevice != NULL) { - sound_trigger_hw_device_close(mHwDevice); - } -} - -int SoundTriggerHalLegacy::getProperties(struct sound_trigger_properties *properties) -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - return mHwDevice->get_properties(mHwDevice, properties); -} - -int SoundTriggerHalLegacy::loadSoundModel(struct sound_trigger_sound_model *sound_model, - sound_model_callback_t callback, - void *cookie, - sound_model_handle_t *handle) -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - return mHwDevice->load_sound_model(mHwDevice, sound_model, callback, cookie, handle); -} - -int SoundTriggerHalLegacy::unloadSoundModel(sound_model_handle_t handle) -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - return mHwDevice->unload_sound_model(mHwDevice, handle); -} - -int SoundTriggerHalLegacy::startRecognition(sound_model_handle_t handle, - const struct sound_trigger_recognition_config *config, - recognition_callback_t callback, - void *cookie) -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - return mHwDevice->start_recognition(mHwDevice, handle, config, callback, cookie); -} - -int SoundTriggerHalLegacy::stopRecognition(sound_model_handle_t handle) -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - return mHwDevice->stop_recognition(mHwDevice, handle); -} - -int SoundTriggerHalLegacy::stopAllRecognitions() -{ - if (mHwDevice == NULL) { - return -ENODEV; - } - if (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 && - mHwDevice->stop_all_recognitions) { - return mHwDevice->stop_all_recognitions(mHwDevice); - } - return -ENOSYS; -} - -} // namespace android diff --git a/services/soundtrigger/SoundTriggerHalLegacy.h b/services/soundtrigger/SoundTriggerHalLegacy.h deleted file mode 100644 index 52488de844..0000000000 --- a/services/soundtrigger/SoundTriggerHalLegacy.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H -#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H - -#include "SoundTriggerHalInterface.h" - -namespace android { - -class SoundTriggerHalLegacy : public SoundTriggerHalInterface - -{ -public: - virtual ~SoundTriggerHalLegacy(); - - virtual int getProperties(struct sound_trigger_properties *properties); - - /* - * Load a sound model. Once loaded, recognition of this model can be started and stopped. - * Only one active recognition per model at a time. The SoundTrigger service will handle - * concurrent recognition requests by different users/applications on the same model. - * The implementation returns a unique handle used by other functions (unload_sound_model(), - * start_recognition(), etc... - */ - virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model, - sound_model_callback_t callback, - void *cookie, - sound_model_handle_t *handle); - - /* - * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome - * implementation limitations. - */ - virtual int unloadSoundModel(sound_model_handle_t handle); - - /* Start recognition on a given model. Only one recognition active at a time per model. - * Once recognition succeeds of fails, the callback is called. - * TODO: group recognition configuration parameters into one struct and add key phrase options. - */ - virtual int startRecognition(sound_model_handle_t handle, - const struct sound_trigger_recognition_config *config, - recognition_callback_t callback, - void *cookie); - - /* Stop recognition on a given model. - * The implementation does not have to call the callback when stopped via this method. - */ - virtual int stopRecognition(sound_model_handle_t handle); - - /* Stop recognition on all models. - * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above. - * If no implementation is provided, stop_recognition will be called for each running model. - */ - int stopAllRecognitions(); - - // RefBase - virtual void onFirstRef(); - -private: - - friend class SoundTriggerHalInterface; - - explicit SoundTriggerHalLegacy(const char *moduleName = NULL); - - const char *mModuleName; - struct sound_trigger_hw_device* mHwDevice; -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index 79150683a6..944abcc31d 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -36,11 +36,7 @@ #include #include "SoundTriggerHwService.h" -#ifdef SOUND_TRIGGER_USE_STUB_MODULE -#define HW_MODULE_PREFIX "stub" -#else #define HW_MODULE_PREFIX "primary" -#endif namespace android { SoundTriggerHwService::SoundTriggerHwService() -- GitLab From d1dbc788531a8a507327a8935967f234bc2a0c5d Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Tue, 20 Nov 2018 12:49:00 -0800 Subject: [PATCH 0497/1530] Convert libsoundtriggerservice to Android.bp See build/soong/README.md for more information. Removed AUDIOSERVER_MULTILIB because it should be unnecessary -- almost every device does not set it, and the only other value is "64" for 64-bit only devices, which will act the same as "" for libraries. Test: cd frameworks/av; mma Change-Id: I8f62a549bcd5ea9eaf9aa6ead48ca6748a31c873 --- services/soundtrigger/Android.bp | 54 ++++++++++++++++++++++++++++ services/soundtrigger/Android.mk | 61 -------------------------------- 2 files changed, 54 insertions(+), 61 deletions(-) create mode 100644 services/soundtrigger/Android.bp delete mode 100644 services/soundtrigger/Android.mk diff --git a/services/soundtrigger/Android.bp b/services/soundtrigger/Android.bp new file mode 100644 index 0000000000..1f2283acaa --- /dev/null +++ b/services/soundtrigger/Android.bp @@ -0,0 +1,54 @@ +// Copyright 2014 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. + +cc_library_shared { + name: "libsoundtriggerservice", + + srcs: [ + "SoundTriggerHwService.cpp", + "SoundTriggerHalHidl.cpp", + ], + + shared_libs: [ + "liblog", + "libutils", + "libbinder", + "libcutils", + "libhardware", + "libsoundtrigger", + "libaudioclient", + "libmediautils", + + "libhwbinder", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "libbase", + "libaudiohal", + "libaudiohal_deathhandler", + "android.hardware.soundtrigger@2.0", + "android.hardware.soundtrigger@2.1", + "android.hardware.soundtrigger@2.2", + "android.hardware.audio.common@2.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + ], + + include_dirs: ["frameworks/av/services/audioflinger"], + + cflags: [ + "-Wall", + "-Werror", + ], +} diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk deleted file mode 100644 index 0f093934da..0000000000 --- a/services/soundtrigger/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - SoundTriggerHwService.cpp - -LOCAL_SHARED_LIBRARIES:= \ - liblog \ - libutils \ - libbinder \ - libcutils \ - libhardware \ - libsoundtrigger \ - libaudioclient \ - libmediautils \ - -# Treble configuration -LOCAL_SRC_FILES += \ - SoundTriggerHalHidl.cpp - -LOCAL_SHARED_LIBRARIES += \ - libhwbinder \ - libhidlbase \ - libhidlmemory \ - libhidltransport \ - libbase \ - libaudiohal \ - libaudiohal_deathhandler \ - android.hardware.soundtrigger@2.0 \ - android.hardware.soundtrigger@2.1 \ - android.hardware.soundtrigger@2.2 \ - android.hardware.audio.common@2.0 \ - android.hidl.allocator@1.0 \ - android.hidl.memory@1.0 - - -LOCAL_C_INCLUDES += \ - frameworks/av/services/audioflinger - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_CFLAGS += -Wall -Werror - -LOCAL_MODULE:= libsoundtriggerservice - -include $(BUILD_SHARED_LIBRARY) -- GitLab From 28d27f5293c28fcbbba425311885e3d00eb5fbaa Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Mon, 19 Nov 2018 14:08:39 -0800 Subject: [PATCH 0498/1530] Audio attributes using public API Bug: 112549970 Test: MediaPlayer2Test Change-Id: Ia4091ff59172a9cd040d33bc6c2edd2ea7195f70 --- media/libmediaplayer2/JAudioTrack.cpp | 33 ++++++++++--------- .../include/mediaplayer2/JAudioTrack.h | 1 - 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index 3d6879e532..033ff4cb1d 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -34,7 +34,7 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen callback_t cbf, // Offload void* user, // Offload size_t frameCount, // bufferSizeInBytes - int32_t sessionId, // AudioTrack + int32_t sessionId, // AudioTrack const jobject attributes, // AudioAttributes float maxRequiredSpeed) { // bufferSizeInBytes @@ -65,18 +65,20 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "", "()V"); jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor); - if (attributes != NULL) { - mAudioAttributesObj = new JObjectHolder(attributes); - } else { - mAudioAttributesObj = new JObjectHolder( - JAudioAttributes::createAudioAttributesObj(env, NULL)); + { + sp audioAttributesObj; + if (attributes != NULL) { + audioAttributesObj = new JObjectHolder(attributes); + } else { + audioAttributesObj = new JObjectHolder( + JAudioAttributes::createAudioAttributesObj(env, NULL)); + } + jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes", + "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;"); + jBuilderObj = env->CallObjectMethod(jBuilderObj, + jSetAudioAttributes, audioAttributesObj->getJObject()); } - jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes", - "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;"); - jBuilderObj = env->CallObjectMethod(jBuilderObj, - jSetAudioAttributes, mAudioAttributesObj->getJObject()); - jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat", "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;"); jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat, @@ -513,15 +515,14 @@ status_t JAudioTrack::setPreferredDevice(jobject device) { } audio_stream_type_t JAudioTrack::getAudioStreamType() { - if (mAudioAttributesObj == NULL) { - return AUDIO_STREAM_DEFAULT; - } JNIEnv *env = JavaVMHelper::getJNIEnv(); + jmethodID jGetAudioAttributes = env->GetMethodID(mAudioTrackCls, "getAudioAttributes", + "()Landroid/media/AudioAttributes;"); + jobject jAudioAttributes = env->CallObjectMethod(mAudioTrackObj, jGetAudioAttributes); jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes"); jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls, "getVolumeControlStream", "()I"); - int javaAudioStreamType = env->CallIntMethod( - mAudioAttributesObj->getJObject(), jGetVolumeControlStream); + int javaAudioStreamType = env->CallIntMethod(jAudioAttributes, jGetVolumeControlStream); return (audio_stream_type_t)javaAudioStreamType; } diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h index 8ea70efe50..87dc889786 100644 --- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h +++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h @@ -446,7 +446,6 @@ private: jclass mAudioTrackCls; jobject mAudioTrackObj; - sp mAudioAttributesObj; /* Creates a Java VolumeShaper.Configuration object from VolumeShaper::Configuration */ jobject createVolumeShaperConfigurationObj( -- GitLab From 09a5aac9261a9d400e37041fc4655934bfd36267 Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Tue, 20 Nov 2018 10:39:25 +0900 Subject: [PATCH 0499/1530] Remove unused Media 2.0 APIs Bug: 119591238 Test: build Change-Id: I6c29b83cd5986bead0670cf33704299fcd51baa0 --- packages/MediaComponents/Android.mk | 70 - packages/MediaComponents/AndroidManifest.xml | 15 - packages/MediaComponents/proguard.cfg | 20 - packages/MediaComponents/runcts.sh | 224 --- .../com/android/media/IMediaController2.aidl | 55 - .../src/com/android/media/IMediaSession2.aidl | 72 - .../android/media/MediaController2Impl.java | 1085 ------------ .../android/media/MediaController2Stub.java | 322 ---- .../src/com/android/media/MediaItem2Impl.java | 223 --- .../com/android/media/MediaMetadata2Impl.java | 373 ---- .../android/media/MediaPlaylistAgentImpl.java | 224 --- .../com/android/media/MediaSession2Impl.java | 1541 ----------------- .../com/android/media/MediaSession2Stub.java | 888 ---------- .../src/com/android/media/Rating2Impl.java | 190 -- .../android/media/SessionPlaylistAgent.java | 494 ------ .../com/android/media/SessionToken2Impl.java | 217 --- .../android/media/VolumeProvider2Impl.java | 93 - .../media/subtitle/MediaTimeProvider.java | 89 - .../media/subtitle/SubtitleController.java | 507 ------ .../android/media/subtitle/SubtitleTrack.java | 696 -------- .../com/android/media/update/ApiFactory.java | 199 --- .../com/android/media/update/ApiHelper.java | 33 - .../src/com/android/widget/SubtitleView.java | 144 -- .../android/widget/VideoViewInterface.java | 65 - packages/MediaComponents/tests/Android.mk | 36 - .../MediaComponents/tests/AndroidManifest.xml | 33 - .../media/SessionPlaylistAgentTest.java | 643 ------- 27 files changed, 8551 deletions(-) delete mode 100644 packages/MediaComponents/Android.mk delete mode 100644 packages/MediaComponents/AndroidManifest.xml delete mode 100644 packages/MediaComponents/proguard.cfg delete mode 100644 packages/MediaComponents/runcts.sh delete mode 100644 packages/MediaComponents/src/com/android/media/IMediaController2.aidl delete mode 100644 packages/MediaComponents/src/com/android/media/IMediaSession2.aidl delete mode 100644 packages/MediaComponents/src/com/android/media/MediaController2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaController2Stub.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaItem2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaSession2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/MediaSession2Stub.java delete mode 100644 packages/MediaComponents/src/com/android/media/Rating2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java delete mode 100644 packages/MediaComponents/src/com/android/media/SessionToken2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java delete mode 100644 packages/MediaComponents/src/com/android/media/subtitle/MediaTimeProvider.java delete mode 100644 packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java delete mode 100644 packages/MediaComponents/src/com/android/media/subtitle/SubtitleTrack.java delete mode 100644 packages/MediaComponents/src/com/android/media/update/ApiFactory.java delete mode 100644 packages/MediaComponents/src/com/android/media/update/ApiHelper.java delete mode 100644 packages/MediaComponents/src/com/android/widget/SubtitleView.java delete mode 100644 packages/MediaComponents/src/com/android/widget/VideoViewInterface.java delete mode 100644 packages/MediaComponents/tests/Android.mk delete mode 100644 packages/MediaComponents/tests/AndroidManifest.xml delete mode 100644 packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk deleted file mode 100644 index fff3a620cb..0000000000 --- a/packages/MediaComponents/Android.mk +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -ifneq ($(TARGET_BUILD_PDK),true) # Build MediaComponents only if this is not a PDK build. MediaComponents won't -# build in PDK builds because frameworks/base/core/java is not available but -# IMediaSession2.aidl and IMediaController2.aidl are using classes from -# frameworks/base/core/java. - -include $(CLEAR_VARS) - -LOCAL_PACKAGE_NAME := MediaComponents -LOCAL_MODULE_OWNER := google - -# TODO: create a separate key for this package. -LOCAL_CERTIFICATE := platform - -# TODO: Use System SDK once public APIs are approved -# LOCAL_SDK_VERSION := system_current -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-Iaidl-files-under, src) - -LOCAL_PROGUARD_FLAG_FILES := proguard.cfg - -LOCAL_MULTILIB := first - -LOCAL_JAVA_LIBRARIES += androidx.annotation_annotation - -# To embed native libraries in package, uncomment the lines below. -#LOCAL_MODULE_TAGS := samples -#LOCAL_JNI_SHARED_LIBRARIES := \ -# libaacextractor \ -# libamrextractor \ -# libflacextractor \ -# libmidiextractor \ -# libmkvextractor \ -# libmp3extractor \ -# libmp4extractor \ -# libmpeg2extractor \ -# liboggextractor \ -# libwavextractor \ - -# TODO: Remove dependency with other support libraries. -LOCAL_STATIC_ANDROID_LIBRARIES += \ - androidx.legacy_legacy-support-v4 \ - androidx.appcompat_appcompat \ - androidx.palette_palette -LOCAL_USE_AAPT2 := true - -include $(BUILD_PACKAGE) - -endif # ifneq ($(TARGET_BUILD_PDK),true) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/MediaComponents/AndroidManifest.xml b/packages/MediaComponents/AndroidManifest.xml deleted file mode 100644 index 50fdca1e0f..0000000000 --- a/packages/MediaComponents/AndroidManifest.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/packages/MediaComponents/proguard.cfg b/packages/MediaComponents/proguard.cfg deleted file mode 100644 index d7bf730f97..0000000000 --- a/packages/MediaComponents/proguard.cfg +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Keep entry point for updatable Java classes --keep public class com.android.media.update.ApiFactory { - public static com.android.media.update.ApiFactory initialize(android.content.pm.ApplicationInfo); -} diff --git a/packages/MediaComponents/runcts.sh b/packages/MediaComponents/runcts.sh deleted file mode 100644 index 61b1a1e7b5..0000000000 --- a/packages/MediaComponents/runcts.sh +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/bash -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Usage '. runcts.sh' - -function _runtest_cts_mediacomponent_usage() { - echo 'runtest-cts-MediaComponents [option]: Build, flash device,' - echo ' and run subset of CtsMediaTestCases that MediaComponents covers.' - echo ' *Warning* This bypasses CTS setup (e.g. download media contents from server)' - echo ' For running CTS in official way, use atest or cts-tradefed ' - echo ' -h|--help: This help' - echo ' --skip: Skip build and flash. Just rerun-tests' - echo ' --min: Only rebuild tests and updatable library.' - echo ' --test: Only rebuild tests' - echo ' -s [device_id]: Specify a device name to run test against.' - echo ' You can define ${ADBHOST} instead.' - echo ' -r [count]: Repeat tests for given count. It will stop when fails.' - echo ' --ignore: Keep repeating tests even when it fails.' - echo ' -t [test]: Only run the specific test. Can be either a class or a method.' -} - -function runtest-cts-MediaComponents() { - # Edit here if you want to support other tests. - # List up libs and apks in the media_api needed for tests, and place test target at the last. - local TEST_PACKAGE_DIR=("frameworks/av/packages/MediaComponents/test") - local TEST_PACKAGE=("android.media.cts") - local BUILD_TARGETS=("MediaComponents" "CtsMediaTestCases") - # Don't include MediaComponents -- if we simply install it, system server - # wouldn't use the installed one. - local INSTALL_TARGETS=("CtsMediaTestCases") - local TEST_RUNNER="android.support.test.runner.AndroidJUnitRunner" - local DEPENDENCIES=("mockito-target-minus-junit4" "android-support-test" "compatibility-device-util") - local DEFAULT_TEST_TARGET="" - DEFAULT_TEST_TARGET+="android.media.cts.MediaBrowser2Test" - DEFAULT_TEST_TARGET+=",android.media.cts.MediaController2Test" - DEFAULT_TEST_TARGET+=",android.media.cts.MediaMetadata2Test" - DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2Test" - DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2_PermissionTest" - DEFAULT_TEST_TARGET+=",android.media.cts.MediaSessionManager_MediaSession2Test" - DEFAULT_TEST_TARGET+=",android.media.cts.SessionToken2Test" - if [[ -z "${ANDROID_BUILD_TOP}" ]]; then - echo "Needs to lunch a target first" - return - fi - - local old_path=${OLDPWD} - while true; do - local OPTION_SKIP="false" - local OPTION_MIN="false" - local OPTION_TEST="false" - local OPTION_REPEAT_COUNT="1" - local OPTION_IGNORE="false" - local OPTION_TEST_TARGET="${DEFAULT_TEST_TARGET}" - local adbhost_local - while (( "$#" )); do - case "${1}" in - -h|--help) - _runtest_cts_mediacomponent_usage - return - ;; - --skip) - OPTION_SKIP="true" - ;; - --min) - OPTION_MIN="true" - ;; - --test) - OPTION_TEST="true" - ;; - -s) - shift - adbhost_local=${1} - ;; - -r) - shift - OPTION_REPEAT_COUNT="${1}" - ;; - --ignore) - OPTION_IGNORE="true" - ;; - -t) - shift - OPTION_TEST_TARGET="${1}" - esac - shift - done - - # Build adb command. - local adb - if [[ -z "${adbhost_local}" ]]; then - adbhost_local=${ADBHOST} - fi - if [[ -z "${adbhost_local}" ]]; then - local device_count=$(adb devices | sed '/^[[:space:]]*$/d' | wc -l) - if [[ "${device_count}" != "2" ]]; then - echo "Too many devices. Specify a device." && break - fi - adb="adb" - else - adb="adb -s ${adbhost_local}" - fi - - local target_dir="${ANDROID_BUILD_TOP}/${TEST_PACKAGE_DIR}" - #local TEST_PACKAGE=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\.]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml) - - if [[ "${OPTION_SKIP}" != "true" ]]; then - # Build dependencies if needed. - local dependency - local build_dependency="" - for dependency in ${DEPENDENCIES[@]}; do - if [[ "${dependency}" == "out/"* ]]; then - if [[ ! -f ${ANDROID_BUILD_TOP}/${dependency} ]]; then - build_dependency="true" - break - fi - else - if [[ "$(find ${OUT} -name ${dependency}_intermediates | wc -l)" == "0" ]]; then - build_dependency="true" - break - fi - fi - done - if [[ "${build_dependency}" == "true" ]]; then - echo "Building dependencies. Will only print stderr." - m ${DEPENDENCIES[@]} -j > /dev/null - fi - - # Build test apk and required apk. - local build_targets - if [[ "${OPTION_TEST}" == "true" ]]; then - build_targets="${INSTALL_TARGETS[@]}" - elif [[ "${OPTION_MIN}" == "true" ]]; then - build_targets="${BUILD_TARGETS[@]}" - else - build_targets="${BUILD_TARGETS[@]} droid" - fi - m ${build_targets} -j || break - - if [[ "${OPTION_TEST}" != "true" ]]; then - # Flash only when needed - local device_build_type="$(${adb} shell getprop ro.build.type)" - if [[ "${device_build_type}" == "user" ]]; then - # User build. Cannot adb sync - ${adb} reboot bootloader - fastboot flashall - else - ${adb} root - local device_verity_mode="$(${adb} shell getprop ro.boot.veritymode)" - if [[ "${device_verity_mode}" != "disabled" ]]; then - ${adb} disable-verity - ${adb} reboot - ${adb} wait-for-device || break - ${adb} root - fi - ${adb} remount - ${adb} shell stop - ${adb} shell setprop log.tag.MediaSessionService DEBUG - ${adb} sync - ${adb} shell start - fi - ${adb} wait-for-device || break - # Ensure package manager is loaded. - # TODO(jaewan): Find better way to wait - sleep 15 - fi - - # Install apks - local install_failed="false" - for target in ${INSTALL_TARGETS[@]}; do - local apk_path=$(find ${OUT}/system ${OUT}/data -name ${target}.apk) - local apk_num=$(find ${OUT}/system ${OUT}/data -name ${target}.apk | wc -l) - if [[ "${apk_num}" != "1" ]]; then - echo "Cannot locate a ${target}.apk. Found ${apk_num} apks" && break - fi - echo "Installing ${target}.apk. path=${apk_path}" - ${adb} install -r ${apk_path} - if [[ "${?}" != "0" ]]; then - install_failed="true" - break - fi - done - if [[ "${install_failed}" == "true" ]]; then - echo "Failed to install. Test wouldn't run." - break - fi - fi - - local test_target="" - if [[ -n "${OPTION_TEST_TARGET}" ]]; then - test_target="-e class ${OPTION_TEST_TARGET}" - fi - - local i - local tmpfile=$(tempfile) - for ((i=1; i <= ${OPTION_REPEAT_COUNT}; i++)); do - echo "Run test ${i}/${OPTION_REPEAT_COUNT}" - ${adb} shell am instrument ${test_target} -w ${TEST_PACKAGE}/${TEST_RUNNER} >& ${tmpfile} - cat ${tmpfile} - if [[ "${OPTION_IGNORE}" != "true" ]]; then - if [[ -n "$(grep ${tmpfile} -e 'FAILURE\|crashed')" ]]; then - # am instrument doesn't return error code so need to grep result message instead - break - fi - fi - done - rm ${tmpfile} - break - done -} - -echo "Following functions are added to your environment:" -_runtest_cts_mediacomponent_usage diff --git a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl deleted file mode 100644 index cc5acf9bf4..0000000000 --- a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.app.PendingIntent; -import android.os.Bundle; -import android.os.ResultReceiver; - -import com.android.media.IMediaSession2; - -/** - * Interface from MediaSession2 to MediaController2. - *

    - * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, - * and holds calls from session to make session owner(s) frozen. - */ -// TODO(jaewan): (Post P) Handle when the playlist becomes too huge. -// Note that ParcelledSliceList isn't a good idea for the purpose. (see: b/37493677) -oneway interface IMediaController2 { - void onPlayerStateChanged(int state); - void onPositionChanged(long eventTimeMs, long positionMs); - void onPlaybackSpeedChanged(float speed); - void onBufferedPositionChanged(long bufferedPositionMs); - void onPlaylistChanged(in List playlist, in Bundle metadata); - void onPlaylistMetadataChanged(in Bundle metadata); - void onPlaybackInfoChanged(in Bundle playbackInfo); - void onRepeatModeChanged(int repeatMode); - void onShuffleModeChanged(int shuffleMode); - void onError(int errorCode, in Bundle extras); - - void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, - int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, - long bufferedPositionMs, in Bundle playbackInfo, int repeatMode, int shuffleMode, - in List playlist, in PendingIntent sessionActivity); - void onDisconnected(); - - void onCustomLayoutChanged(in List commandButtonlist); - void onAllowedCommandsChanged(in Bundle commands); - - void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver); -} diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl deleted file mode 100644 index 5761455743..0000000000 --- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.os.Bundle; -import android.os.ResultReceiver; -import android.net.Uri; - -import com.android.media.IMediaController2; - -/** - * Interface from MediaController2 to MediaSession2. - *

    - * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, - * and holds calls from session to make session owner(s) frozen. - */ - // TODO(jaewan): (Post P) Handle when the playlist becomes too huge. - // Note that ParcelledSliceList isn't a good idea for the purpose. (see: b/37493677) -oneway interface IMediaSession2 { - // TODO(jaewan): add onCommand() to send private command - - // TODO(jaewan): (Post P) We may consider to add another binder just for the connection - // not to expose other methods to the controller whose connection wasn't accepted. - // But this would be enough for now because it's the same as existing - // MediaBrowser and MediaBrowserService. - void connect(IMediaController2 caller, String callingPackage); - void release(IMediaController2 caller); - - void setVolumeTo(IMediaController2 caller, int value, int flags); - void adjustVolume(IMediaController2 caller, int direction, int flags); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // send command - ////////////////////////////////////////////////////////////////////////////////////////////// - void sendTransportControlCommand(IMediaController2 caller, - int commandCode, in Bundle args); - void sendCustomCommand(IMediaController2 caller, in Bundle command, in Bundle args, - in ResultReceiver receiver); - - void prepareFromUri(IMediaController2 caller, in Uri uri, in Bundle extras); - void prepareFromSearch(IMediaController2 caller, String query, in Bundle extras); - void prepareFromMediaId(IMediaController2 caller, String mediaId, in Bundle extras); - void playFromUri(IMediaController2 caller, in Uri uri, in Bundle extras); - void playFromSearch(IMediaController2 caller, String query, in Bundle extras); - void playFromMediaId(IMediaController2 caller, String mediaId, in Bundle extras); - void setRating(IMediaController2 caller, String mediaId, in Bundle rating); - - void setPlaylist(IMediaController2 caller, in List playlist, in Bundle metadata); - void updatePlaylistMetadata(IMediaController2 caller, in Bundle metadata); - void addPlaylistItem(IMediaController2 caller, int index, in Bundle mediaItem); - void removePlaylistItem(IMediaController2 caller, in Bundle mediaItem); - void replacePlaylistItem(IMediaController2 caller, int index, in Bundle mediaItem); - void skipToPlaylistItem(IMediaController2 caller, in Bundle mediaItem); - void skipToPreviousItem(IMediaController2 caller); - void skipToNextItem(IMediaController2 caller); - void setRepeatMode(IMediaController2 caller, int repeatMode); - void setShuffleMode(IMediaController2 caller, int shuffleMode); -} diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java deleted file mode 100644 index 1c4cf82af9..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE; -import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_URI; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH; -import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI; -import static android.media.SessionCommand2.COMMAND_CODE_SET_VOLUME; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.media.AudioAttributes; -import android.media.MediaController2; -import android.media.MediaController2.ControllerCallback; -import android.media.MediaController2.PlaybackInfo; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlaylistAgent.RepeatMode; -import android.media.MediaPlaylistAgent.ShuffleMode; -import android.media.MediaSession2.CommandButton; -import android.media.Rating2; -import android.media.SessionCommand2; -import android.media.SessionCommandGroup2; -import android.media.SessionToken2; -import android.media.update.MediaController2Provider; -import android.net.Uri; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.GuardedBy; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -public class MediaController2Impl implements MediaController2Provider { - private static final String TAG = "MediaController2"; - private static final boolean DEBUG = true; // TODO(jaewan): Change - - private final MediaController2 mInstance; - private final Context mContext; - private final Object mLock = new Object(); - - private final MediaController2Stub mControllerStub; - private final SessionToken2 mToken; - private final ControllerCallback mCallback; - private final Executor mCallbackExecutor; - private final IBinder.DeathRecipient mDeathRecipient; - - @GuardedBy("mLock") - private SessionServiceConnection mServiceConnection; - @GuardedBy("mLock") - private boolean mIsReleased; - @GuardedBy("mLock") - private List mPlaylist; - @GuardedBy("mLock") - private MediaMetadata2 mPlaylistMetadata; - @GuardedBy("mLock") - private @RepeatMode int mRepeatMode; - @GuardedBy("mLock") - private @ShuffleMode int mShuffleMode; - @GuardedBy("mLock") - private int mPlayerState; - @GuardedBy("mLock") - private long mPositionEventTimeMs; - @GuardedBy("mLock") - private long mPositionMs; - @GuardedBy("mLock") - private float mPlaybackSpeed; - @GuardedBy("mLock") - private long mBufferedPositionMs; - @GuardedBy("mLock") - private PlaybackInfo mPlaybackInfo; - @GuardedBy("mLock") - private PendingIntent mSessionActivity; - @GuardedBy("mLock") - private SessionCommandGroup2 mAllowedCommands; - - // Assignment should be used with the lock hold, but should be used without a lock to prevent - // potential deadlock. - // Postfix -Binder is added to explicitly show that it's potentially remote process call. - // Technically -Interface is more correct, but it may misread that it's interface (vs class) - // so let's keep this postfix until we find better postfix. - @GuardedBy("mLock") - private volatile IMediaSession2 mSessionBinder; - - // TODO(jaewan): Require session activeness changed listener, because controller can be - // available when the session's player is null. - public MediaController2Impl(Context context, MediaController2 instance, SessionToken2 token, - Executor executor, ControllerCallback callback) { - mInstance = instance; - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - if (executor == null) { - throw new IllegalArgumentException("executor shouldn't be null"); - } - mContext = context; - mControllerStub = new MediaController2Stub(this); - mToken = token; - mCallback = callback; - mCallbackExecutor = executor; - mDeathRecipient = () -> { - mInstance.close(); - }; - - mSessionBinder = null; - } - - @Override - public void initialize() { - // TODO(jaewan): More sanity checks. - if (mToken.getType() == SessionToken2.TYPE_SESSION) { - // Session - mServiceConnection = null; - connectToSession(SessionToken2Impl.from(mToken).getSessionBinder()); - } - } - - private void connectToSession(IMediaSession2 sessionBinder) { - try { - sessionBinder.connect(mControllerStub, mContext.getPackageName()); - } catch (RemoteException e) { - Log.w(TAG, "Failed to call connection request. Framework will retry" - + " automatically"); - } - } - - @Override - public void close_impl() { - if (DEBUG) { - Log.d(TAG, "release from " + mToken); - } - final IMediaSession2 binder; - synchronized (mLock) { - if (mIsReleased) { - // Prevent re-enterance from the ControllerCallback.onDisconnected() - return; - } - mIsReleased = true; - if (mServiceConnection != null) { - mContext.unbindService(mServiceConnection); - mServiceConnection = null; - } - binder = mSessionBinder; - mSessionBinder = null; - mControllerStub.destroy(); - } - if (binder != null) { - try { - binder.asBinder().unlinkToDeath(mDeathRecipient, 0); - binder.release(mControllerStub); - } catch (RemoteException e) { - // No-op. - } - } - mCallbackExecutor.execute(() -> { - mCallback.onDisconnected(mInstance); - }); - } - - IMediaSession2 getSessionBinder() { - return mSessionBinder; - } - - MediaController2Stub getControllerStub() { - return mControllerStub; - } - - Executor getCallbackExecutor() { - return mCallbackExecutor; - } - - Context getContext() { - return mContext; - } - - MediaController2 getInstance() { - return mInstance; - } - - // Returns session binder if the controller can send the command. - IMediaSession2 getSessionBinderIfAble(int commandCode) { - synchronized (mLock) { - if (!mAllowedCommands.hasCommand(commandCode)) { - // Cannot send because isn't allowed to. - Log.w(TAG, "Controller isn't allowed to call command, commandCode=" - + commandCode); - return null; - } - } - // TODO(jaewan): Should we do this with the lock hold? - final IMediaSession2 binder = mSessionBinder; - if (binder == null) { - // Cannot send because disconnected. - Log.w(TAG, "Session is disconnected"); - } - return binder; - } - - // Returns session binder if the controller can send the command. - IMediaSession2 getSessionBinderIfAble(SessionCommand2 command) { - synchronized (mLock) { - if (!mAllowedCommands.hasCommand(command)) { - Log.w(TAG, "Controller isn't allowed to call command, command=" + command); - return null; - } - } - // TODO(jaewan): Should we do this with the lock hold? - final IMediaSession2 binder = mSessionBinder; - if (binder == null) { - // Cannot send because disconnected. - Log.w(TAG, "Session is disconnected"); - } - return binder; - } - - @Override - public SessionToken2 getSessionToken_impl() { - return mToken; - } - - @Override - public boolean isConnected_impl() { - final IMediaSession2 binder = mSessionBinder; - return binder != null; - } - - @Override - public void play_impl() { - sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY); - } - - @Override - public void pause_impl() { - sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE); - } - - @Override - public void stop_impl() { - sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_STOP); - } - - @Override - public void skipToPlaylistItem_impl(MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final IMediaSession2 binder = mSessionBinder; - if (binder != null) { - try { - binder.skipToPlaylistItem(mControllerStub, item.toBundle()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void skipToPreviousItem_impl() { - final IMediaSession2 binder = mSessionBinder; - if (binder != null) { - try { - binder.skipToPreviousItem(mControllerStub); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void skipToNextItem_impl() { - final IMediaSession2 binder = mSessionBinder; - if (binder != null) { - try { - binder.skipToNextItem(mControllerStub); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - private void sendTransportControlCommand(int commandCode) { - sendTransportControlCommand(commandCode, null); - } - - private void sendTransportControlCommand(int commandCode, Bundle args) { - final IMediaSession2 binder = mSessionBinder; - if (binder != null) { - try { - binder.sendTransportControlCommand(mControllerStub, commandCode, args); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public PendingIntent getSessionActivity_impl() { - return mSessionActivity; - } - - @Override - public void setVolumeTo_impl(int value, int flags) { - // TODO(hdmoon): sanity check - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME); - if (binder != null) { - try { - binder.setVolumeTo(mControllerStub, value, flags); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void adjustVolume_impl(int direction, int flags) { - // TODO(hdmoon): sanity check - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME); - if (binder != null) { - try { - binder.adjustVolume(mControllerStub, direction, flags); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void prepareFromUri_impl(Uri uri, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PREPARE_FROM_URI); - if (uri == null) { - throw new IllegalArgumentException("uri shouldn't be null"); - } - if (binder != null) { - try { - binder.prepareFromUri(mControllerStub, uri, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void prepareFromSearch_impl(String query, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble( - COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH); - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query shouldn't be empty"); - } - if (binder != null) { - try { - binder.prepareFromSearch(mControllerStub, query, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void prepareFromMediaId_impl(String mediaId, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble( - COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID); - if (mediaId == null) { - throw new IllegalArgumentException("mediaId shouldn't be null"); - } - if (binder != null) { - try { - binder.prepareFromMediaId(mControllerStub, mediaId, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void playFromUri_impl(Uri uri, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_URI); - if (uri == null) { - throw new IllegalArgumentException("uri shouldn't be null"); - } - if (binder != null) { - try { - binder.playFromUri(mControllerStub, uri, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void playFromSearch_impl(String query, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH); - if (TextUtils.isEmpty(query)) { - throw new IllegalArgumentException("query shouldn't be empty"); - } - if (binder != null) { - try { - binder.playFromSearch(mControllerStub, query, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void playFromMediaId_impl(String mediaId, Bundle extras) { - final IMediaSession2 binder = getSessionBinderIfAble( - COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID); - if (mediaId == null) { - throw new IllegalArgumentException("mediaId shouldn't be null"); - } - if (binder != null) { - try { - binder.playFromMediaId(mControllerStub, mediaId, extras); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void setRating_impl(String mediaId, Rating2 rating) { - if (mediaId == null) { - throw new IllegalArgumentException("mediaId shouldn't be null"); - } - if (rating == null) { - throw new IllegalArgumentException("rating shouldn't be null"); - } - - final IMediaSession2 binder = mSessionBinder; - if (binder != null) { - try { - binder.setRating(mControllerStub, mediaId, rating.toBundle()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - // TODO(jaewan): Handle. - } - } - - @Override - public void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - final IMediaSession2 binder = getSessionBinderIfAble(command); - if (binder != null) { - try { - binder.sendCustomCommand(mControllerStub, command.toBundle(), args, cb); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public List getPlaylist_impl() { - synchronized (mLock) { - return mPlaylist; - } - } - - @Override - public void setPlaylist_impl(List list, MediaMetadata2 metadata) { - if (list == null) { - throw new IllegalArgumentException("list shouldn't be null"); - } - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_LIST); - if (binder != null) { - List bundleList = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) { - bundleList.add(list.get(i).toBundle()); - } - Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle(); - try { - binder.setPlaylist(mControllerStub, bundleList, metadataBundle); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public MediaMetadata2 getPlaylistMetadata_impl() { - synchronized (mLock) { - return mPlaylistMetadata; - } - } - - @Override - public void updatePlaylistMetadata_impl(MediaMetadata2 metadata) { - final IMediaSession2 binder = getSessionBinderIfAble( - COMMAND_CODE_PLAYLIST_SET_LIST_METADATA); - if (binder != null) { - Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle(); - try { - binder.updatePlaylistMetadata(mControllerStub, metadataBundle); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void prepare_impl() { - sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE); - } - - @Override - public void fastForward_impl() { - // TODO(jaewan): Implement this. Note that fast forward isn't a transport command anymore - //sendTransportControlCommand(MediaSession2.COMMAND_CODE_SESSION_FAST_FORWARD); - } - - @Override - public void rewind_impl() { - // TODO(jaewan): Implement this. Note that rewind isn't a transport command anymore - //sendTransportControlCommand(MediaSession2.COMMAND_CODE_SESSION_REWIND); - } - - @Override - public void seekTo_impl(long pos) { - if (pos < 0) { - throw new IllegalArgumentException("position shouldn't be negative"); - } - Bundle args = new Bundle(); - args.putLong(MediaSession2Stub.ARGUMENT_KEY_POSITION, pos); - sendTransportControlCommand(SessionCommand2.COMMAND_CODE_PLAYBACK_SEEK_TO, args); - } - - @Override - public void addPlaylistItem_impl(int index, MediaItem2 item) { - if (index < 0) { - throw new IllegalArgumentException("index shouldn't be negative"); - } - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_ADD_ITEM); - if (binder != null) { - try { - binder.addPlaylistItem(mControllerStub, index, item.toBundle()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void removePlaylistItem_impl(MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_REMOVE_ITEM); - if (binder != null) { - try { - binder.removePlaylistItem(mControllerStub, item.toBundle()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public void replacePlaylistItem_impl(int index, MediaItem2 item) { - if (index < 0) { - throw new IllegalArgumentException("index shouldn't be negative"); - } - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_REPLACE_ITEM); - if (binder != null) { - try { - binder.replacePlaylistItem(mControllerStub, index, item.toBundle()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public int getShuffleMode_impl() { - return mShuffleMode; - } - - @Override - public void setShuffleMode_impl(int shuffleMode) { - final IMediaSession2 binder = getSessionBinderIfAble( - COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE); - if (binder != null) { - try { - binder.setShuffleMode(mControllerStub, shuffleMode); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public int getRepeatMode_impl() { - return mRepeatMode; - } - - @Override - public void setRepeatMode_impl(int repeatMode) { - final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE); - if (binder != null) { - try { - binder.setRepeatMode(mControllerStub, repeatMode); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to the service or the session is gone", e); - } - } else { - Log.w(TAG, "Session isn't active", new IllegalStateException()); - } - } - - @Override - public PlaybackInfo getPlaybackInfo_impl() { - synchronized (mLock) { - return mPlaybackInfo; - } - } - - @Override - public int getPlayerState_impl() { - synchronized (mLock) { - return mPlayerState; - } - } - - @Override - public long getCurrentPosition_impl() { - synchronized (mLock) { - long timeDiff = System.currentTimeMillis() - mPositionEventTimeMs; - long expectedPosition = mPositionMs + (long) (mPlaybackSpeed * timeDiff); - return Math.max(0, expectedPosition); - } - } - - @Override - public float getPlaybackSpeed_impl() { - synchronized (mLock) { - return mPlaybackSpeed; - } - } - - @Override - public long getBufferedPosition_impl() { - synchronized (mLock) { - return mBufferedPositionMs; - } - } - - @Override - public MediaItem2 getCurrentMediaItem_impl() { - // TODO(jaewan): Implement - return null; - } - - void pushPlayerStateChanges(final int state) { - synchronized (mLock) { - mPlayerState = state; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onPlayerStateChanged(mInstance, state); - }); - } - - // TODO(jaewan): Rename to seek completed - void pushPositionChanges(final long eventTimeMs, final long positionMs) { - synchronized (mLock) { - mPositionEventTimeMs = eventTimeMs; - mPositionMs = positionMs; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onSeekCompleted(mInstance, positionMs); - }); - } - - void pushPlaybackSpeedChanges(final float speed) { - synchronized (mLock) { - mPlaybackSpeed = speed; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onPlaybackSpeedChanged(mInstance, speed); - }); - } - - void pushBufferedPositionChanges(final long bufferedPositionMs) { - synchronized (mLock) { - mBufferedPositionMs = bufferedPositionMs; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - // TODO(jaewan): Fix this -- it's now buffered state - //mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs); - }); - } - - void pushPlaybackInfoChanges(final PlaybackInfo info) { - synchronized (mLock) { - mPlaybackInfo = info; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onPlaybackInfoChanged(mInstance, info); - }); - } - - void pushPlaylistChanges(final List playlist, final MediaMetadata2 metadata) { - synchronized (mLock) { - mPlaylist = playlist; - mPlaylistMetadata = metadata; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onPlaylistChanged(mInstance, playlist, metadata); - }); - } - - void pushPlaylistMetadataChanges(MediaMetadata2 metadata) { - synchronized (mLock) { - mPlaylistMetadata = metadata; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onPlaylistMetadataChanged(mInstance, metadata); - }); - } - - void pushShuffleModeChanges(int shuffleMode) { - synchronized (mLock) { - mShuffleMode = shuffleMode; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onShuffleModeChanged(mInstance, shuffleMode); - }); - } - - void pushRepeatModeChanges(int repeatMode) { - synchronized (mLock) { - mRepeatMode = repeatMode; - } - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onRepeatModeChanged(mInstance, repeatMode); - }); - } - - void pushError(int errorCode, Bundle extras) { - mCallbackExecutor.execute(() -> { - if (!mInstance.isConnected()) { - return; - } - mCallback.onError(mInstance, errorCode, extras); - }); - } - - // Should be used without a lock to prevent potential deadlock. - void onConnectedNotLocked(IMediaSession2 sessionBinder, - final SessionCommandGroup2 allowedCommands, - final int playerState, - final long positionEventTimeMs, - final long positionMs, - final float playbackSpeed, - final long bufferedPositionMs, - final PlaybackInfo info, - final int repeatMode, - final int shuffleMode, - final List playlist, - final PendingIntent sessionActivity) { - if (DEBUG) { - Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder - + ", allowedCommands=" + allowedCommands); - } - boolean close = false; - try { - if (sessionBinder == null || allowedCommands == null) { - // Connection rejected. - close = true; - return; - } - synchronized (mLock) { - if (mIsReleased) { - return; - } - if (mSessionBinder != null) { - Log.e(TAG, "Cannot be notified about the connection result many times." - + " Probably a bug or malicious app."); - close = true; - return; - } - mAllowedCommands = allowedCommands; - mPlayerState = playerState; - mPositionEventTimeMs = positionEventTimeMs; - mPositionMs = positionMs; - mPlaybackSpeed = playbackSpeed; - mBufferedPositionMs = bufferedPositionMs; - mPlaybackInfo = info; - mRepeatMode = repeatMode; - mShuffleMode = shuffleMode; - mPlaylist = playlist; - mSessionActivity = sessionActivity; - mSessionBinder = sessionBinder; - try { - // Implementation for the local binder is no-op, - // so can be used without worrying about deadlock. - mSessionBinder.asBinder().linkToDeath(mDeathRecipient, 0); - } catch (RemoteException e) { - if (DEBUG) { - Log.d(TAG, "Session died too early.", e); - } - close = true; - return; - } - } - // TODO(jaewan): Keep commands to prevents illegal API calls. - mCallbackExecutor.execute(() -> { - // Note: We may trigger ControllerCallbacks with the initial values - // But it's hard to define the order of the controller callbacks - // Only notify about the - mCallback.onConnected(mInstance, allowedCommands); - }); - } finally { - if (close) { - // Trick to call release() without holding the lock, to prevent potential deadlock - // with the developer's custom lock within the ControllerCallback.onDisconnected(). - mInstance.close(); - } - } - } - - void onCustomCommand(final SessionCommand2 command, final Bundle args, - final ResultReceiver receiver) { - if (DEBUG) { - Log.d(TAG, "onCustomCommand cmd=" + command); - } - mCallbackExecutor.execute(() -> { - // TODO(jaewan): Double check if the controller exists. - mCallback.onCustomCommand(mInstance, command, args, receiver); - }); - } - - void onAllowedCommandsChanged(final SessionCommandGroup2 commands) { - mCallbackExecutor.execute(() -> { - mCallback.onAllowedCommandsChanged(mInstance, commands); - }); - } - - void onCustomLayoutChanged(final List layout) { - mCallbackExecutor.execute(() -> { - mCallback.onCustomLayoutChanged(mInstance, layout); - }); - } - - // This will be called on the main thread. - private class SessionServiceConnection implements ServiceConnection { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - // Note that it's always main-thread. - if (DEBUG) { - Log.d(TAG, "onServiceConnected " + name + " " + this); - } - // Sanity check - if (!mToken.getPackageName().equals(name.getPackageName())) { - Log.wtf(TAG, name + " was connected, but expected pkg=" - + mToken.getPackageName() + " with id=" + mToken.getId()); - return; - } - final IMediaSession2 sessionBinder = IMediaSession2.Stub.asInterface(service); - connectToSession(sessionBinder); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - // Temporal lose of the binding because of the service crash. System will automatically - // rebind, so just no-op. - // TODO(jaewan): Really? Either disconnect cleanly or - if (DEBUG) { - Log.w(TAG, "Session service " + name + " is disconnected."); - } - } - - @Override - public void onBindingDied(ComponentName name) { - // Permanent lose of the binding because of the service package update or removed. - // This SessionServiceRecord will be removed accordingly, but forget session binder here - // for sure. - mInstance.close(); - } - } - - public static final class PlaybackInfoImpl implements PlaybackInfoProvider { - - private static final String KEY_PLAYBACK_TYPE = - "android.media.playbackinfo_impl.playback_type"; - private static final String KEY_CONTROL_TYPE = - "android.media.playbackinfo_impl.control_type"; - private static final String KEY_MAX_VOLUME = - "android.media.playbackinfo_impl.max_volume"; - private static final String KEY_CURRENT_VOLUME = - "android.media.playbackinfo_impl.current_volume"; - private static final String KEY_AUDIO_ATTRIBUTES = - "android.media.playbackinfo_impl.audio_attrs"; - - private final PlaybackInfo mInstance; - - private final int mPlaybackType; - private final int mControlType; - private final int mMaxVolume; - private final int mCurrentVolume; - private final AudioAttributes mAudioAttrs; - - private PlaybackInfoImpl(int playbackType, AudioAttributes attrs, int controlType, - int max, int current) { - mPlaybackType = playbackType; - mAudioAttrs = attrs; - mControlType = controlType; - mMaxVolume = max; - mCurrentVolume = current; - mInstance = new PlaybackInfo(this); - } - - @Override - public int getPlaybackType_impl() { - return mPlaybackType; - } - - @Override - public AudioAttributes getAudioAttributes_impl() { - return mAudioAttrs; - } - - @Override - public int getControlType_impl() { - return mControlType; - } - - @Override - public int getMaxVolume_impl() { - return mMaxVolume; - } - - @Override - public int getCurrentVolume_impl() { - return mCurrentVolume; - } - - PlaybackInfo getInstance() { - return mInstance; - } - - Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putInt(KEY_PLAYBACK_TYPE, mPlaybackType); - bundle.putInt(KEY_CONTROL_TYPE, mControlType); - bundle.putInt(KEY_MAX_VOLUME, mMaxVolume); - bundle.putInt(KEY_CURRENT_VOLUME, mCurrentVolume); - bundle.putParcelable(KEY_AUDIO_ATTRIBUTES, mAudioAttrs); - return bundle; - } - - static PlaybackInfo createPlaybackInfo(int playbackType, AudioAttributes attrs, - int controlType, int max, int current) { - return new PlaybackInfoImpl(playbackType, attrs, controlType, max, current) - .getInstance(); - } - - static PlaybackInfo fromBundle(Bundle bundle) { - if (bundle == null) { - return null; - } - final int volumeType = bundle.getInt(KEY_PLAYBACK_TYPE); - final int volumeControl = bundle.getInt(KEY_CONTROL_TYPE); - final int maxVolume = bundle.getInt(KEY_MAX_VOLUME); - final int currentVolume = bundle.getInt(KEY_CURRENT_VOLUME); - final AudioAttributes attrs = bundle.getParcelable(KEY_AUDIO_ATTRIBUTES); - - return createPlaybackInfo(volumeType, attrs, volumeControl, maxVolume, currentVolume); - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java deleted file mode 100644 index 5b71e65dc1..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.app.PendingIntent; -import android.content.Context; -import android.media.MediaController2; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaSession2.CommandButton; -import android.media.SessionCommand2; -import android.media.SessionCommandGroup2; -import android.os.Bundle; -import android.os.ResultReceiver; -import android.text.TextUtils; -import android.util.Log; - -import com.android.media.MediaController2Impl.PlaybackInfoImpl; -import com.android.media.MediaSession2Impl.CommandButtonImpl; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -public class MediaController2Stub extends IMediaController2.Stub { - private static final String TAG = "MediaController2Stub"; - private static final boolean DEBUG = true; // TODO(jaewan): Change - - private final WeakReference mController; - - MediaController2Stub(MediaController2Impl controller) { - mController = new WeakReference<>(controller); - } - - private MediaController2Impl getController() throws IllegalStateException { - final MediaController2Impl controller = mController.get(); - if (controller == null) { - throw new IllegalStateException("Controller is released"); - } - return controller; - } - - public void destroy() { - mController.clear(); - } - - @Override - public void onPlayerStateChanged(int state) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - controller.pushPlayerStateChanges(state); - } - - @Override - public void onPositionChanged(long eventTimeMs, long positionMs) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (eventTimeMs < 0) { - Log.w(TAG, "onPositionChanged(): Ignoring negative eventTimeMs"); - return; - } - if (positionMs < 0) { - Log.w(TAG, "onPositionChanged(): Ignoring negative positionMs"); - return; - } - controller.pushPositionChanges(eventTimeMs, positionMs); - } - - @Override - public void onPlaybackSpeedChanged(float speed) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - controller.pushPlaybackSpeedChanges(speed); - } - - @Override - public void onBufferedPositionChanged(long bufferedPositionMs) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (bufferedPositionMs < 0) { - Log.w(TAG, "onBufferedPositionChanged(): Ignoring negative bufferedPositionMs"); - return; - } - controller.pushBufferedPositionChanges(bufferedPositionMs); - } - - @Override - public void onPlaylistChanged(List playlistBundle, Bundle metadataBundle) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (playlistBundle == null) { - Log.w(TAG, "onPlaylistChanged(): Ignoring null playlist from " + controller); - return; - } - List playlist = new ArrayList<>(); - for (Bundle bundle : playlistBundle) { - MediaItem2 item = MediaItem2.fromBundle(bundle); - if (item == null) { - Log.w(TAG, "onPlaylistChanged(): Ignoring null item in playlist"); - } else { - playlist.add(item); - } - } - MediaMetadata2 metadata = MediaMetadata2.fromBundle(metadataBundle); - controller.pushPlaylistChanges(playlist, metadata); - } - - @Override - public void onPlaylistMetadataChanged(Bundle metadataBundle) throws RuntimeException { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - MediaMetadata2 metadata = MediaMetadata2.fromBundle(metadataBundle); - controller.pushPlaylistMetadataChanges(metadata); - } - - @Override - public void onRepeatModeChanged(int repeatMode) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - controller.pushRepeatModeChanges(repeatMode); - } - - @Override - public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException { - if (DEBUG) { - Log.d(TAG, "onPlaybackInfoChanged"); - } - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - MediaController2.PlaybackInfo info = PlaybackInfoImpl.fromBundle(playbackInfo); - if (info == null) { - Log.w(TAG, "onPlaybackInfoChanged(): Ignoring null playbackInfo"); - return; - } - controller.pushPlaybackInfoChanges(info); - } - - @Override - public void onShuffleModeChanged(int shuffleMode) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - controller.pushShuffleModeChanges(shuffleMode); - } - - @Override - public void onError(int errorCode, Bundle extras) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - controller.pushError(errorCode, extras); - } - - @Override - public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup, - int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, - long bufferedPositionMs, Bundle playbackInfo, int shuffleMode, int repeatMode, - List itemBundleList, PendingIntent sessionActivity) { - final MediaController2Impl controller = mController.get(); - if (controller == null) { - if (DEBUG) { - Log.d(TAG, "onConnected after MediaController2.close()"); - } - return; - } - final Context context = controller.getContext(); - List itemList = null; - if (itemBundleList != null) { - itemList = new ArrayList<>(); - for (int i = 0; i < itemBundleList.size(); i++) { - MediaItem2 item = MediaItem2.fromBundle(itemBundleList.get(i)); - if (item != null) { - itemList.add(item); - } - } - } - controller.onConnectedNotLocked(sessionBinder, - SessionCommandGroup2.fromBundle(commandGroup), - playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, - PlaybackInfoImpl.fromBundle(playbackInfo), repeatMode, shuffleMode, - itemList, sessionActivity); - } - - @Override - public void onDisconnected() { - final MediaController2Impl controller = mController.get(); - if (controller == null) { - if (DEBUG) { - Log.d(TAG, "onDisconnected after MediaController2.close()"); - } - return; - } - controller.getInstance().close(); - } - - @Override - public void onCustomLayoutChanged(List commandButtonlist) { - if (commandButtonlist == null) { - Log.w(TAG, "onCustomLayoutChanged(): Ignoring null commandButtonlist"); - return; - } - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (controller == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - List layout = new ArrayList<>(); - for (int i = 0; i < commandButtonlist.size(); i++) { - CommandButton button = CommandButtonImpl.fromBundle(commandButtonlist.get(i)); - if (button != null) { - layout.add(button); - } - } - controller.onCustomLayoutChanged(layout); - } - - @Override - public void onAllowedCommandsChanged(Bundle commandsBundle) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - if (controller == null) { - // TODO(jaewan): Revisit here. Could be a bug - return; - } - SessionCommandGroup2 commands = SessionCommandGroup2.fromBundle(commandsBundle); - if (commands == null) { - Log.w(TAG, "onAllowedCommandsChanged(): Ignoring null commands"); - return; - } - controller.onAllowedCommandsChanged(commands); - } - - @Override - public void onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) { - final MediaController2Impl controller; - try { - controller = getController(); - } catch (IllegalStateException e) { - Log.w(TAG, "Don't fail silently here. Highly likely a bug"); - return; - } - SessionCommand2 command = SessionCommand2.fromBundle(commandBundle); - if (command == null) { - Log.w(TAG, "onCustomCommand(): Ignoring null command"); - return; - } - controller.onCustomCommand(command, args, receiver); - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java deleted file mode 100644 index 910a0f1bbf..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.MediaItem2.FLAG_BROWSABLE; -import static android.media.MediaItem2.FLAG_PLAYABLE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.media.DataSourceDesc; -import android.media.MediaItem2; -import android.media.MediaItem2.Builder; -import android.media.MediaItem2.Flags; -import android.media.MediaMetadata2; -import android.media.update.MediaItem2Provider; -import android.os.Bundle; -import android.text.TextUtils; - -import java.util.UUID; - -public class MediaItem2Impl implements MediaItem2Provider { - private static final String KEY_ID = "android.media.mediaitem2.id"; - private static final String KEY_FLAGS = "android.media.mediaitem2.flags"; - private static final String KEY_METADATA = "android.media.mediaitem2.metadata"; - private static final String KEY_UUID = "android.media.mediaitem2.uuid"; - - private final MediaItem2 mInstance; - private final String mId; - private final int mFlags; - private final UUID mUUID; - private MediaMetadata2 mMetadata; - private DataSourceDesc mDataSourceDesc; - - // From the public API - public MediaItem2Impl(@NonNull String mediaId, @Nullable DataSourceDesc dsd, - @Nullable MediaMetadata2 metadata, @Flags int flags) { - this(mediaId, dsd, metadata, flags, null); - } - - private MediaItem2Impl(@NonNull String mediaId, @Nullable DataSourceDesc dsd, - @Nullable MediaMetadata2 metadata, @Flags int flags, @Nullable UUID uuid) { - if (mediaId == null) { - throw new IllegalArgumentException("mediaId shouldn't be null"); - } - if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) { - throw new IllegalArgumentException("metadata's id should be matched with the mediaid"); - } - - mId = mediaId; - mDataSourceDesc = dsd; - mMetadata = metadata; - mFlags = flags; - mUUID = (uuid == null) ? UUID.randomUUID() : uuid; - - mInstance = new MediaItem2(this); - } - - @Override - public boolean equals_impl(Object obj) { - if (!(obj instanceof MediaItem2)) { - return false; - } - MediaItem2 other = (MediaItem2) obj; - return mUUID.equals(((MediaItem2Impl) other.getProvider()).mUUID); - } - - /** - * Return this object as a bundle to share between processes. - * - * @return a new bundle instance - */ - public Bundle toBundle_impl() { - Bundle bundle = new Bundle(); - bundle.putString(KEY_ID, mId); - bundle.putInt(KEY_FLAGS, mFlags); - if (mMetadata != null) { - bundle.putBundle(KEY_METADATA, mMetadata.toBundle()); - } - bundle.putString(KEY_UUID, mUUID.toString()); - return bundle; - } - - /** - * Create a MediaItem2 from the {@link Bundle}. - * - * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}. - * @return The newly created MediaItem2 - */ - public static MediaItem2 fromBundle_impl(@NonNull Bundle bundle) { - if (bundle == null) { - return null; - } - final String uuidString = bundle.getString(KEY_UUID); - return fromBundle(bundle, UUID.fromString(uuidString)); - } - - /** - * Create a MediaItem2 from the {@link Bundle} with the specified {@link UUID}. - * If {@link UUID} - * can be null for creating new. - * - * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}. - * @param uuid A {@link UUID} to override. Can be {@link null} for override. - * @return The newly created MediaItem2 - */ - static MediaItem2 fromBundle(@NonNull Bundle bundle, @Nullable UUID uuid) { - if (bundle == null) { - return null; - } - final String id = bundle.getString(KEY_ID); - final Bundle metadataBundle = bundle.getBundle(KEY_METADATA); - final MediaMetadata2 metadata = MediaMetadata2.fromBundle(metadataBundle); - final int flags = bundle.getInt(KEY_FLAGS); - return new MediaItem2Impl(id, null, metadata, flags, uuid).getInstance(); - } - - private MediaItem2 getInstance() { - return mInstance; - } - - @Override - public String toString_impl() { - final StringBuilder sb = new StringBuilder("MediaItem2{"); - sb.append("mFlags=").append(mFlags); - sb.append(", mMetadata=").append(mMetadata); - sb.append('}'); - return sb.toString(); - } - - @Override - public @Flags int getFlags_impl() { - return mFlags; - } - - @Override - public boolean isBrowsable_impl() { - return (mFlags & FLAG_BROWSABLE) != 0; - } - - @Override - public boolean isPlayable_impl() { - return (mFlags & FLAG_PLAYABLE) != 0; - } - - @Override - public void setMetadata_impl(@Nullable MediaMetadata2 metadata) { - if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) { - throw new IllegalArgumentException("metadata's id should be matched with the mediaId"); - } - mMetadata = metadata; - } - - @Override - public @Nullable MediaMetadata2 getMetadata_impl() { - return mMetadata; - } - - @Override - public @NonNull String getMediaId_impl() { - return mId; - } - - @Override - public @Nullable DataSourceDesc getDataSourceDesc_impl() { - return mDataSourceDesc; - } - - public static class BuilderImpl implements MediaItem2Provider.BuilderProvider { - private Builder mInstance; - private @Flags int mFlags; - private String mMediaId; - private MediaMetadata2 mMetadata; - private DataSourceDesc mDataSourceDesc; - - public BuilderImpl(Builder instance, int flags) { - mInstance = instance; - mFlags = flags; - } - - @Override - public Builder setMediaId_impl(@Nullable String mediaId) { - mMediaId = mediaId; - return mInstance; - } - - @Override - public Builder setMetadata_impl(@Nullable MediaMetadata2 metadata) { - mMetadata = metadata; - return mInstance; - } - - @Override - public Builder setDataSourceDesc_impl(@Nullable DataSourceDesc dataSourceDesc) { - mDataSourceDesc = dataSourceDesc; - return mInstance; - } - - @Override - public MediaItem2 build_impl() { - String id = (mMetadata != null) - ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null; - if (id == null) { - // TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?) - id = (mMediaId != null) ? mMediaId : toString(); - } - return new MediaItem2Impl(id, mDataSourceDesc, mMetadata, mFlags).getInstance(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java deleted file mode 100644 index cf1c5323cc..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.MediaMetadata2.*; - -import android.annotation.Nullable; -import android.graphics.Bitmap; -import android.media.MediaMetadata2; -import android.media.MediaMetadata2.BitmapKey; -import android.media.MediaMetadata2.Builder; -import android.media.MediaMetadata2.LongKey; -import android.media.MediaMetadata2.RatingKey; -import android.media.MediaMetadata2.TextKey; -import android.media.Rating2; -import android.media.update.MediaMetadata2Provider; -import android.os.Bundle; -import android.util.ArrayMap; -import android.util.Log; - -import java.util.Set; - -public class MediaMetadata2Impl implements MediaMetadata2Provider { - private static final String TAG = "MediaMetadata2"; - - static final int METADATA_TYPE_LONG = 0; - static final int METADATA_TYPE_TEXT = 1; - static final int METADATA_TYPE_BITMAP = 2; - static final int METADATA_TYPE_RATING = 3; - static final int METADATA_TYPE_FLOAT = 4; - static final ArrayMap METADATA_KEYS_TYPE; - - static { - METADATA_KEYS_TYPE = new ArrayMap(); - METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING); - METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG); - } - - private static final @TextKey - String[] PREFERRED_DESCRIPTION_ORDER = { - METADATA_KEY_TITLE, - METADATA_KEY_ARTIST, - METADATA_KEY_ALBUM, - METADATA_KEY_ALBUM_ARTIST, - METADATA_KEY_WRITER, - METADATA_KEY_AUTHOR, - METADATA_KEY_COMPOSER - }; - - private static final @BitmapKey - String[] PREFERRED_BITMAP_ORDER = { - METADATA_KEY_DISPLAY_ICON, - METADATA_KEY_ART, - METADATA_KEY_ALBUM_ART - }; - - private static final @TextKey - String[] PREFERRED_URI_ORDER = { - METADATA_KEY_DISPLAY_ICON_URI, - METADATA_KEY_ART_URI, - METADATA_KEY_ALBUM_ART_URI - }; - - private final MediaMetadata2 mInstance; - private final Bundle mBundle; - - public MediaMetadata2Impl(Bundle bundle) { - mInstance = new MediaMetadata2(this); - mBundle = bundle; - } - - public MediaMetadata2 getInstance() { - return mInstance; - } - - @Override - public boolean containsKey_impl(String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - return mBundle.containsKey(key); - } - - @Override - public CharSequence getText_impl(@TextKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - return mBundle.getCharSequence(key); - } - - @Override - public @Nullable String getMediaId_impl() { - return mInstance.getString(METADATA_KEY_MEDIA_ID); - } - - @Override - public String getString_impl(@TextKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - CharSequence text = mBundle.getCharSequence(key); - if (text != null) { - return text.toString(); - } - return null; - } - - @Override - public long getLong_impl(@LongKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - return mBundle.getLong(key, 0); - } - - @Override - public Rating2 getRating_impl(@RatingKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - // TODO(jaewan): Add backward compatibility - Rating2 rating = null; - try { - rating = Rating2.fromBundle(mBundle.getBundle(key)); - } catch (Exception e) { - // ignore, value was not a rating - Log.w(TAG, "Failed to retrieve a key as Rating.", e); - } - return rating; - } - - @Override - public float getFloat_impl(@FloatKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - return mBundle.getFloat(key); - } - - @Override - public Bitmap getBitmap_impl(@BitmapKey String key) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - Bitmap bmp = null; - try { - bmp = mBundle.getParcelable(key); - } catch (Exception e) { - // ignore, value was not a bitmap - Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); - } - return bmp; - } - - @Override - public Bundle getExtras_impl() { - try { - return mBundle.getBundle(METADATA_KEY_EXTRAS); - } catch (Exception e) { - // ignore, value was not an bundle - Log.w(TAG, "Failed to retrieve an extra"); - } - return null; - } - - @Override - public int size_impl() { - return mBundle.size(); - } - - @Override - public Set keySet_impl() { - return mBundle.keySet(); - } - - @Override - public Bundle toBundle_impl() { - return mBundle; - } - - public static MediaMetadata2 fromBundle_impl(Bundle bundle) { - return (bundle == null) ? null : new MediaMetadata2Impl(bundle).getInstance(); - } - - public static final class BuilderImpl implements MediaMetadata2Provider.BuilderProvider { - private final MediaMetadata2.Builder mInstance; - private final Bundle mBundle; - - public BuilderImpl(MediaMetadata2.Builder instance) { - mInstance = instance; - mBundle = new Bundle(); - } - - public BuilderImpl(MediaMetadata2.Builder instance, MediaMetadata2 source) { - if (source == null) { - throw new IllegalArgumentException("source shouldn't be null"); - } - mInstance = instance; - mBundle = new Bundle(source.toBundle()); - } - - public BuilderImpl(int maxBitmapSize) { - mInstance = new MediaMetadata2.Builder(this); - mBundle = new Bundle(); - - for (String key : mBundle.keySet()) { - Object value = mBundle.get(key); - if (value instanceof Bitmap) { - Bitmap bmp = (Bitmap) value; - if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { - mInstance.putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); - } - } - } - } - - @Override - public Builder putText_impl(@TextKey String key, CharSequence value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a CharSequence"); - } - } - mBundle.putCharSequence(key, value); - return mInstance; - } - - @Override - public Builder putString_impl(@TextKey String key, String value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a String"); - } - } - mBundle.putCharSequence(key, value); - return mInstance; - } - - @Override - public Builder putLong_impl(@LongKey String key, long value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a long"); - } - } - mBundle.putLong(key, value); - return mInstance; - } - - @Override - public Builder putRating_impl(@RatingKey String key, Rating2 value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a Rating"); - } - } - mBundle.putBundle(key, value.toBundle()); - return mInstance; - } - - @Override - public Builder putBitmap_impl(@BitmapKey String key, Bitmap value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a Bitmap"); - } - } - mBundle.putParcelable(key, value); - return mInstance; - } - - @Override - public Builder putFloat_impl(@FloatKey String key, float value) { - if (key == null) { - throw new IllegalArgumentException("key shouldn't be null"); - } - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_FLOAT) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a float"); - } - } - mBundle.putFloat(key, value); - return mInstance; - } - - @Override - public Builder setExtras_impl(Bundle bundle) { - mBundle.putBundle(METADATA_KEY_EXTRAS, bundle); - return mInstance; - } - - @Override - public MediaMetadata2 build_impl() { - return new MediaMetadata2Impl(mBundle).getInstance(); - } - - private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { - float maxSizeF = maxSize; - float widthScale = maxSizeF / bmp.getWidth(); - float heightScale = maxSizeF / bmp.getHeight(); - float scale = Math.min(widthScale, heightScale); - int height = (int) (bmp.getHeight() * scale); - int width = (int) (bmp.getWidth() * scale); - return Bitmap.createScaledBitmap(bmp, width, height, true); - } - } -} - diff --git a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java deleted file mode 100644 index dfd4e1a0b9..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.media.DataSourceDesc; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlaylistAgent; -import android.media.MediaPlaylistAgent.PlaylistEventCallback; -import android.media.update.MediaPlaylistAgentProvider; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.util.List; -import java.util.concurrent.Executor; - -public class MediaPlaylistAgentImpl implements MediaPlaylistAgentProvider { - private static final String TAG = "MediaPlaylistAgent"; - - private final MediaPlaylistAgent mInstance; - - private final Object mLock = new Object(); - @GuardedBy("mLock") - private final ArrayMap mCallbacks = new ArrayMap<>(); - - public MediaPlaylistAgentImpl(MediaPlaylistAgent instance) { - mInstance = instance; - } - - @Override - final public void registerPlaylistEventCallback_impl( - @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) { - if (executor == null) { - throw new IllegalArgumentException("executor shouldn't be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - - synchronized (mLock) { - if (mCallbacks.get(callback) != null) { - Log.w(TAG, "callback is already added. Ignoring."); - return; - } - mCallbacks.put(callback, executor); - } - } - - @Override - final public void unregisterPlaylistEventCallback_impl( - @NonNull PlaylistEventCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - synchronized (mLock) { - mCallbacks.remove(callback); - } - } - - @Override - final public void notifyPlaylistChanged_impl() { - ArrayMap callbacks = getCallbacks(); - List playlist= mInstance.getPlaylist(); - MediaMetadata2 metadata = mInstance.getPlaylistMetadata(); - for (int i = 0; i < callbacks.size(); i++) { - final PlaylistEventCallback callback = callbacks.keyAt(i); - final Executor executor = callbacks.valueAt(i); - executor.execute(() -> callback.onPlaylistChanged( - mInstance, playlist, metadata)); - } - } - - @Override - final public void notifyPlaylistMetadataChanged_impl() { - ArrayMap callbacks = getCallbacks(); - for (int i = 0; i < callbacks.size(); i++) { - final PlaylistEventCallback callback = callbacks.keyAt(i); - final Executor executor = callbacks.valueAt(i); - executor.execute(() -> callback.onPlaylistMetadataChanged( - mInstance, mInstance.getPlaylistMetadata())); - } - } - - @Override - final public void notifyShuffleModeChanged_impl() { - ArrayMap callbacks = getCallbacks(); - for (int i = 0; i < callbacks.size(); i++) { - final PlaylistEventCallback callback = callbacks.keyAt(i); - final Executor executor = callbacks.valueAt(i); - executor.execute(() -> callback.onShuffleModeChanged( - mInstance, mInstance.getShuffleMode())); - } - } - - @Override - final public void notifyRepeatModeChanged_impl() { - ArrayMap callbacks = getCallbacks(); - for (int i = 0; i < callbacks.size(); i++) { - final PlaylistEventCallback callback = callbacks.keyAt(i); - final Executor executor = callbacks.valueAt(i); - executor.execute(() -> callback.onRepeatModeChanged( - mInstance, mInstance.getRepeatMode())); - } - } - - @Override - public @Nullable List getPlaylist_impl() { - // empty implementation - return null; - } - - @Override - public void setPlaylist_impl(@NonNull List list, - @Nullable MediaMetadata2 metadata) { - // empty implementation - } - - @Override - public @Nullable MediaMetadata2 getPlaylistMetadata_impl() { - // empty implementation - return null; - } - - @Override - public void updatePlaylistMetadata_impl(@Nullable MediaMetadata2 metadata) { - // empty implementation - } - - @Override - public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) { - // empty implementation - } - - @Override - public void removePlaylistItem_impl(@NonNull MediaItem2 item) { - // empty implementation - } - - @Override - public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) { - // empty implementation - } - - @Override - public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) { - // empty implementation - } - - @Override - public void skipToPreviousItem_impl() { - // empty implementation - } - - @Override - public void skipToNextItem_impl() { - // empty implementation - } - - @Override - public int getRepeatMode_impl() { - return MediaPlaylistAgent.REPEAT_MODE_NONE; - } - - @Override - public void setRepeatMode_impl(int repeatMode) { - // empty implementation - } - - @Override - public int getShuffleMode_impl() { - // empty implementation - return MediaPlaylistAgent.SHUFFLE_MODE_NONE; - } - - @Override - public void setShuffleMode_impl(int shuffleMode) { - // empty implementation - } - - @Override - public @Nullable MediaItem2 getMediaItem_impl(@NonNull DataSourceDesc dsd) { - if (dsd == null) { - throw new IllegalArgumentException("dsd shouldn't be null"); - } - List itemList = mInstance.getPlaylist(); - if (itemList == null) { - return null; - } - for (int i = 0; i < itemList.size(); i++) { - MediaItem2 item = itemList.get(i); - if (item != null && item.getDataSourceDesc() == dsd) { - return item; - } - } - return null; - } - - private ArrayMap getCallbacks() { - ArrayMap callbacks = new ArrayMap<>(); - synchronized (mLock) { - callbacks.putAll(mCallbacks); - } - return callbacks; - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java deleted file mode 100644 index cd9edb8421..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +++ /dev/null @@ -1,1541 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.SessionCommand2.COMMAND_CODE_CUSTOM; -import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE; -import static android.media.SessionToken2.TYPE_SESSION; -import static android.media.SessionToken2.TYPE_SESSION_SERVICE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.media.AudioAttributes; -import android.media.AudioFocusRequest; -import android.media.AudioManager; -import android.media.DataSourceDesc; -import android.media.MediaController2; -import android.media.MediaController2.PlaybackInfo; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlayerBase; -import android.media.MediaPlayerBase.PlayerEventCallback; -import android.media.MediaPlayerBase.PlayerState; -import android.media.MediaPlaylistAgent; -import android.media.MediaPlaylistAgent.PlaylistEventCallback; -import android.media.MediaSession2; -import android.media.MediaSession2.Builder; -import android.media.MediaSession2.CommandButton; -import android.media.MediaSession2.ControllerInfo; -import android.media.MediaSession2.OnDataSourceMissingHelper; -import android.media.MediaSession2.SessionCallback; -import android.media.SessionCommand2; -import android.media.SessionCommandGroup2; -import android.media.SessionToken2; -import android.media.VolumeProvider2; -import android.media.session.MediaSessionManager; -import android.media.update.MediaSession2Provider; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcelable; -import android.os.Process; -import android.os.ResultReceiver; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.GuardedBy; - -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.Executor; - -public class MediaSession2Impl implements MediaSession2Provider { - private static final String TAG = "MediaSession2"; - private static final boolean DEBUG = true;//Log.isLoggable(TAG, Log.DEBUG); - - private final Object mLock = new Object(); - - private final MediaSession2 mInstance; - private final Context mContext; - private final String mId; - private final Executor mCallbackExecutor; - private final SessionCallback mCallback; - private final MediaSession2Stub mSessionStub; - private final SessionToken2 mSessionToken; - private final AudioManager mAudioManager; - private final PendingIntent mSessionActivity; - private final PlayerEventCallback mPlayerEventCallback; - private final PlaylistEventCallback mPlaylistEventCallback; - - // mPlayer is set to null when the session is closed, and we shouldn't throw an exception - // nor leave log always for using mPlayer when it's null. Here's the reason. - // When a MediaSession2 is closed, there could be a pended operation in the session callback - // executor that may want to access the player. Here's the sample code snippet for that. - // - // public void onFoo() { - // if (mPlayer == null) return; // first check - // mSessionCallbackExecutor.executor(() -> { - // // Error. Session may be closed and mPlayer can be null here. - // mPlayer.foo(); - // }); - // } - // - // By adding protective code, we can also protect APIs from being called after the close() - // - // TODO(jaewan): Should we put volatile here? - @GuardedBy("mLock") - private MediaPlayerBase mPlayer; - @GuardedBy("mLock") - private MediaPlaylistAgent mPlaylistAgent; - @GuardedBy("mLock") - private SessionPlaylistAgent mSessionPlaylistAgent; - @GuardedBy("mLock") - private VolumeProvider2 mVolumeProvider; - @GuardedBy("mLock") - private PlaybackInfo mPlaybackInfo; - @GuardedBy("mLock") - private OnDataSourceMissingHelper mDsmHelper; - - /** - * Can be only called by the {@link Builder#build()}. - * @param context - * @param player - * @param id - * @param playlistAgent - * @param volumeProvider - * @param sessionActivity - * @param callbackExecutor - * @param callback - */ - public MediaSession2Impl(Context context, MediaPlayerBase player, String id, - MediaPlaylistAgent playlistAgent, VolumeProvider2 volumeProvider, - PendingIntent sessionActivity, - Executor callbackExecutor, SessionCallback callback) { - // TODO(jaewan): Keep other params. - mInstance = createInstance(); - - // Argument checks are done by builder already. - // Initialize finals first. - mContext = context; - mId = id; - mCallback = callback; - mCallbackExecutor = callbackExecutor; - mSessionActivity = sessionActivity; - mSessionStub = new MediaSession2Stub(this); - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - mPlayerEventCallback = new MyPlayerEventCallback(this); - mPlaylistEventCallback = new MyPlaylistEventCallback(this); - - // Infer type from the id and package name. - mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION, - mContext.getPackageName(), null, id, mSessionStub).getInstance(); - - updatePlayer(player, playlistAgent, volumeProvider); - - // Ask server for the sanity check, and starts - // Sanity check for making session ID unique 'per package' cannot be done in here. - // Server can only know if the package has another process and has another session with the - // same id. Note that 'ID is unique per package' is important for controller to distinguish - // a session in another package. - MediaSessionManager manager = - (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); - if (!manager.createSession2(mSessionToken)) { - throw new IllegalStateException("Session with the same id is already used by" - + " another process. Use MediaController2 instead."); - } - } - - MediaSession2 createInstance() { - return new MediaSession2(this); - } - - private static String getServiceName(Context context, String serviceAction, String id) { - PackageManager manager = context.getPackageManager(); - Intent serviceIntent = new Intent(serviceAction); - serviceIntent.setPackage(context.getPackageName()); - List services = manager.queryIntentServices(serviceIntent, - PackageManager.GET_META_DATA); - String serviceName = null; - if (services != null) { - for (int i = 0; i < services.size(); i++) { - String serviceId = SessionToken2Impl.getSessionId(services.get(i)); - if (serviceId != null && TextUtils.equals(id, serviceId)) { - if (services.get(i).serviceInfo == null) { - continue; - } - if (serviceName != null) { - throw new IllegalArgumentException("Ambiguous session type. Multiple" - + " session services define the same id=" + id); - } - serviceName = services.get(i).serviceInfo.name; - } - } - } - return serviceName; - } - - @Override - public void updatePlayer_impl(@NonNull MediaPlayerBase player, MediaPlaylistAgent playlistAgent, - VolumeProvider2 volumeProvider) throws IllegalArgumentException { - ensureCallingThread(); - if (player == null) { - throw new IllegalArgumentException("player shouldn't be null"); - } - updatePlayer(player, playlistAgent, volumeProvider); - } - - private void updatePlayer(MediaPlayerBase player, MediaPlaylistAgent agent, - VolumeProvider2 volumeProvider) { - final MediaPlayerBase oldPlayer; - final MediaPlaylistAgent oldAgent; - final PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes()); - synchronized (mLock) { - oldPlayer = mPlayer; - oldAgent = mPlaylistAgent; - mPlayer = player; - if (agent == null) { - mSessionPlaylistAgent = new SessionPlaylistAgent(this, mPlayer); - if (mDsmHelper != null) { - mSessionPlaylistAgent.setOnDataSourceMissingHelper(mDsmHelper); - } - agent = mSessionPlaylistAgent; - } - mPlaylistAgent = agent; - mVolumeProvider = volumeProvider; - mPlaybackInfo = info; - } - if (player != oldPlayer) { - player.registerPlayerEventCallback(mCallbackExecutor, mPlayerEventCallback); - if (oldPlayer != null) { - // Warning: Poorly implement player may ignore this - oldPlayer.unregisterPlayerEventCallback(mPlayerEventCallback); - } - } - if (agent != oldAgent) { - agent.registerPlaylistEventCallback(mCallbackExecutor, mPlaylistEventCallback); - if (oldAgent != null) { - // Warning: Poorly implement player may ignore this - oldAgent.unregisterPlaylistEventCallback(mPlaylistEventCallback); - } - } - - if (oldPlayer != null) { - mSessionStub.notifyPlaybackInfoChanged(info); - notifyPlayerUpdatedNotLocked(oldPlayer); - } - // TODO(jaewan): Repeat the same thing for the playlist agent. - } - - private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) { - PlaybackInfo info; - if (volumeProvider == null) { - int stream; - if (attrs == null) { - stream = AudioManager.STREAM_MUSIC; - } else { - stream = attrs.getVolumeControlStream(); - if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) { - // It may happen if the AudioAttributes doesn't have usage. - // Change it to the STREAM_MUSIC because it's not supported by audio manager - // for querying volume level. - stream = AudioManager.STREAM_MUSIC; - } - } - info = MediaController2Impl.PlaybackInfoImpl.createPlaybackInfo( - PlaybackInfo.PLAYBACK_TYPE_LOCAL, - attrs, - mAudioManager.isVolumeFixed() - ? VolumeProvider2.VOLUME_CONTROL_FIXED - : VolumeProvider2.VOLUME_CONTROL_ABSOLUTE, - mAudioManager.getStreamMaxVolume(stream), - mAudioManager.getStreamVolume(stream)); - } else { - info = MediaController2Impl.PlaybackInfoImpl.createPlaybackInfo( - PlaybackInfo.PLAYBACK_TYPE_REMOTE /* ControlType */, - attrs, - volumeProvider.getControlType(), - volumeProvider.getMaxVolume(), - volumeProvider.getCurrentVolume()); - } - return info; - } - - @Override - public void close_impl() { - // Stop system service from listening this session first. - MediaSessionManager manager = - (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); - manager.destroySession2(mSessionToken); - - if (mSessionStub != null) { - if (DEBUG) { - Log.d(TAG, "session is now unavailable, id=" + mId); - } - // Invalidate previously published session stub. - mSessionStub.destroyNotLocked(); - } - final MediaPlayerBase player; - final MediaPlaylistAgent agent; - synchronized (mLock) { - player = mPlayer; - mPlayer = null; - agent = mPlaylistAgent; - mPlaylistAgent = null; - mSessionPlaylistAgent = null; - } - if (player != null) { - player.unregisterPlayerEventCallback(mPlayerEventCallback); - } - if (agent != null) { - agent.unregisterPlaylistEventCallback(mPlaylistEventCallback); - } - } - - @Override - public MediaPlayerBase getPlayer_impl() { - return getPlayer(); - } - - @Override - public MediaPlaylistAgent getPlaylistAgent_impl() { - return mPlaylistAgent; - } - - @Override - public VolumeProvider2 getVolumeProvider_impl() { - return mVolumeProvider; - } - - @Override - public SessionToken2 getToken_impl() { - return mSessionToken; - } - - @Override - public List getConnectedControllers_impl() { - return mSessionStub.getControllers(); - } - - @Override - public void setAudioFocusRequest_impl(AudioFocusRequest afr) { - // implement - } - - @Override - public void play_impl() { - ensureCallingThread(); - final MediaPlayerBase player = mPlayer; - if (player != null) { - player.play(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void pause_impl() { - ensureCallingThread(); - final MediaPlayerBase player = mPlayer; - if (player != null) { - player.pause(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void stop_impl() { - ensureCallingThread(); - final MediaPlayerBase player = mPlayer; - if (player != null) { - player.reset(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.skipToPlaylistItem(item); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void skipToPreviousItem_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.skipToPreviousItem(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void skipToNextItem_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.skipToNextItem(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void setCustomLayout_impl(@NonNull ControllerInfo controller, - @NonNull List layout) { - ensureCallingThread(); - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (layout == null) { - throw new IllegalArgumentException("layout shouldn't be null"); - } - mSessionStub.notifyCustomLayoutNotLocked(controller, layout); - } - - ////////////////////////////////////////////////////////////////////////////////////// - // TODO(jaewan): Implement follows - ////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void setAllowedCommands_impl(@NonNull ControllerInfo controller, - @NonNull SessionCommandGroup2 commands) { - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (commands == null) { - throw new IllegalArgumentException("commands shouldn't be null"); - } - mSessionStub.setAllowedCommands(controller, commands); - } - - @Override - public void sendCustomCommand_impl(@NonNull ControllerInfo controller, - @NonNull SessionCommand2 command, Bundle args, ResultReceiver receiver) { - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mSessionStub.sendCustomCommand(controller, command, args, receiver); - } - - @Override - public void sendCustomCommand_impl(@NonNull SessionCommand2 command, Bundle args) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mSessionStub.sendCustomCommand(command, args); - } - - @Override - public void setPlaylist_impl(@NonNull List list, MediaMetadata2 metadata) { - if (list == null) { - throw new IllegalArgumentException("list shouldn't be null"); - } - ensureCallingThread(); - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.setPlaylist(list, metadata); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void updatePlaylistMetadata_impl(MediaMetadata2 metadata) { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.updatePlaylistMetadata(metadata); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) { - if (index < 0) { - throw new IllegalArgumentException("index shouldn't be negative"); - } - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.addPlaylistItem(index, item); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void removePlaylistItem_impl(@NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.removePlaylistItem(item); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) { - if (index < 0) { - throw new IllegalArgumentException("index shouldn't be negative"); - } - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.replacePlaylistItem(index, item); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public List getPlaylist_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - return agent.getPlaylist(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return null; - } - - @Override - public MediaMetadata2 getPlaylistMetadata_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - return agent.getPlaylistMetadata(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return null; - } - - @Override - public MediaItem2 getCurrentPlaylistItem_impl() { - // TODO(jaewan): Implement - return null; - } - - @Override - public int getRepeatMode_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - return agent.getRepeatMode(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return MediaPlaylistAgent.REPEAT_MODE_NONE; - } - - @Override - public void setRepeatMode_impl(int repeatMode) { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.setRepeatMode(repeatMode); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public int getShuffleMode_impl() { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - return agent.getShuffleMode(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return MediaPlaylistAgent.SHUFFLE_MODE_NONE; - } - - @Override - public void setShuffleMode_impl(int shuffleMode) { - final MediaPlaylistAgent agent = mPlaylistAgent; - if (agent != null) { - agent.setShuffleMode(shuffleMode); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void prepare_impl() { - ensureCallingThread(); - final MediaPlayerBase player = mPlayer; - if (player != null) { - player.prepare(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public void seekTo_impl(long pos) { - ensureCallingThread(); - final MediaPlayerBase player = mPlayer; - if (player != null) { - player.seekTo(pos); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - } - - @Override - public @PlayerState int getPlayerState_impl() { - final MediaPlayerBase player = mPlayer; - if (player != null) { - return mPlayer.getPlayerState(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return MediaPlayerBase.PLAYER_STATE_ERROR; - } - - @Override - public long getCurrentPosition_impl() { - final MediaPlayerBase player = mPlayer; - if (player != null) { - return mPlayer.getCurrentPosition(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return MediaPlayerBase.UNKNOWN_TIME; - } - - @Override - public long getBufferedPosition_impl() { - final MediaPlayerBase player = mPlayer; - if (player != null) { - return mPlayer.getBufferedPosition(); - } else if (DEBUG) { - Log.d(TAG, "API calls after the close()", new IllegalStateException()); - } - return MediaPlayerBase.UNKNOWN_TIME; - } - - @Override - public void notifyError_impl(int errorCode, Bundle extras) { - mSessionStub.notifyError(errorCode, extras); - } - - @Override - public void setOnDataSourceMissingHelper_impl(@NonNull OnDataSourceMissingHelper helper) { - if (helper == null) { - throw new IllegalArgumentException("helper shouldn't be null"); - } - synchronized (mLock) { - mDsmHelper = helper; - if (mSessionPlaylistAgent != null) { - mSessionPlaylistAgent.setOnDataSourceMissingHelper(helper); - } - } - } - - @Override - public void clearOnDataSourceMissingHelper_impl() { - synchronized (mLock) { - mDsmHelper = null; - if (mSessionPlaylistAgent != null) { - mSessionPlaylistAgent.clearOnDataSourceMissingHelper(); - } - } - } - - /////////////////////////////////////////////////// - // Protected or private methods - /////////////////////////////////////////////////// - - // Enforces developers to call all the methods on the initially given thread - // because calls from the MediaController2 will be run on the thread. - // TODO(jaewan): Should we allow calls from the multiple thread? - // I prefer this way because allowing multiple thread may case tricky issue like - // b/63446360. If the {@link #setPlayer()} with {@code null} can be called from - // another thread, transport controls can be called after that. - // That's basically the developer's mistake, but they cannot understand what's - // happening behind until we tell them so. - // If enforcing callling thread doesn't look good, we can alternatively pick - // 1. Allow calls from random threads for all methods. - // 2. Allow calls from random threads for all methods, except for the - // {@link #setPlayer()}. - void ensureCallingThread() { - // TODO(jaewan): Uncomment or remove - /* - if (mHandler.getLooper() != Looper.myLooper()) { - throw new IllegalStateException("Run this on the given thread"); - }*/ - } - - private void notifyPlaylistChangedOnExecutor(MediaPlaylistAgent playlistAgent, - List list, MediaMetadata2 metadata) { - if (playlistAgent != mPlaylistAgent) { - // Ignore calls from the old agent. - return; - } - mCallback.onPlaylistChanged(mInstance, playlistAgent, list, metadata); - mSessionStub.notifyPlaylistChangedNotLocked(list, metadata); - } - - private void notifyPlaylistMetadataChangedOnExecutor(MediaPlaylistAgent playlistAgent, - MediaMetadata2 metadata) { - if (playlistAgent != mPlaylistAgent) { - // Ignore calls from the old agent. - return; - } - mCallback.onPlaylistMetadataChanged(mInstance, playlistAgent, metadata); - mSessionStub.notifyPlaylistMetadataChangedNotLocked(metadata); - } - - private void notifyRepeatModeChangedOnExecutor(MediaPlaylistAgent playlistAgent, - int repeatMode) { - if (playlistAgent != mPlaylistAgent) { - // Ignore calls from the old agent. - return; - } - mCallback.onRepeatModeChanged(mInstance, playlistAgent, repeatMode); - mSessionStub.notifyRepeatModeChangedNotLocked(repeatMode); - } - - private void notifyShuffleModeChangedOnExecutor(MediaPlaylistAgent playlistAgent, - int shuffleMode) { - if (playlistAgent != mPlaylistAgent) { - // Ignore calls from the old agent. - return; - } - mCallback.onShuffleModeChanged(mInstance, playlistAgent, shuffleMode); - mSessionStub.notifyShuffleModeChangedNotLocked(shuffleMode); - } - - private void notifyPlayerUpdatedNotLocked(MediaPlayerBase oldPlayer) { - final MediaPlayerBase player = mPlayer; - // TODO(jaewan): (Can be post-P) Find better way for player.getPlayerState() // - // In theory, Session.getXXX() may not be the same as Player.getXXX() - // and we should notify information of the session.getXXX() instead of - // player.getXXX() - // Notify to controllers as well. - final int state = player.getPlayerState(); - if (state != oldPlayer.getPlayerState()) { - mSessionStub.notifyPlayerStateChangedNotLocked(state); - } - - final long currentTimeMs = System.currentTimeMillis(); - final long position = player.getCurrentPosition(); - if (position != oldPlayer.getCurrentPosition()) { - mSessionStub.notifyPositionChangedNotLocked(currentTimeMs, position); - } - - final float speed = player.getPlaybackSpeed(); - if (speed != oldPlayer.getPlaybackSpeed()) { - mSessionStub.notifyPlaybackSpeedChangedNotLocked(speed); - } - - final long bufferedPosition = player.getBufferedPosition(); - if (bufferedPosition != oldPlayer.getBufferedPosition()) { - mSessionStub.notifyBufferedPositionChangedNotLocked(bufferedPosition); - } - } - - Context getContext() { - return mContext; - } - - MediaSession2 getInstance() { - return mInstance; - } - - MediaPlayerBase getPlayer() { - return mPlayer; - } - - MediaPlaylistAgent getPlaylistAgent() { - return mPlaylistAgent; - } - - Executor getCallbackExecutor() { - return mCallbackExecutor; - } - - SessionCallback getCallback() { - return mCallback; - } - - MediaSession2Stub getSessionStub() { - return mSessionStub; - } - - VolumeProvider2 getVolumeProvider() { - return mVolumeProvider; - } - - PlaybackInfo getPlaybackInfo() { - synchronized (mLock) { - return mPlaybackInfo; - } - } - - PendingIntent getSessionActivity() { - return mSessionActivity; - } - - private static class MyPlayerEventCallback extends PlayerEventCallback { - private final WeakReference mSession; - - private MyPlayerEventCallback(MediaSession2Impl session) { - mSession = new WeakReference<>(session); - } - - @Override - public void onCurrentDataSourceChanged(MediaPlayerBase mpb, DataSourceDesc dsd) { - MediaSession2Impl session = getSession(); - if (session == null || dsd == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - MediaItem2 item = getMediaItem(session, dsd); - if (item == null) { - return; - } - session.getCallback().onCurrentMediaItemChanged(session.getInstance(), mpb, item); - // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) - }); - } - - @Override - public void onMediaPrepared(MediaPlayerBase mpb, DataSourceDesc dsd) { - MediaSession2Impl session = getSession(); - if (session == null || dsd == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - MediaItem2 item = getMediaItem(session, dsd); - if (item == null) { - return; - } - session.getCallback().onMediaPrepared(session.getInstance(), mpb, item); - // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) - }); - } - - @Override - public void onPlayerStateChanged(MediaPlayerBase mpb, int state) { - MediaSession2Impl session = getSession(); - if (session == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - session.getCallback().onPlayerStateChanged(session.getInstance(), mpb, state); - session.getSessionStub().notifyPlayerStateChangedNotLocked(state); - }); - } - - @Override - public void onBufferingStateChanged(MediaPlayerBase mpb, DataSourceDesc dsd, int state) { - MediaSession2Impl session = getSession(); - if (session == null || dsd == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - MediaItem2 item = getMediaItem(session, dsd); - if (item == null) { - return; - } - session.getCallback().onBufferingStateChanged( - session.getInstance(), mpb, item, state); - // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) - }); - } - - private MediaSession2Impl getSession() { - final MediaSession2Impl session = mSession.get(); - if (session == null && DEBUG) { - Log.d(TAG, "Session is closed", new IllegalStateException()); - } - return session; - } - - private MediaItem2 getMediaItem(MediaSession2Impl session, DataSourceDesc dsd) { - MediaPlaylistAgent agent = session.getPlaylistAgent(); - if (agent == null) { - if (DEBUG) { - Log.d(TAG, "Session is closed", new IllegalStateException()); - } - return null; - } - MediaItem2 item = agent.getMediaItem(dsd); - if (item == null) { - if (DEBUG) { - Log.d(TAG, "Could not find matching item for dsd=" + dsd, - new NoSuchElementException()); - } - } - return item; - } - } - - private static class MyPlaylistEventCallback extends PlaylistEventCallback { - private final WeakReference mSession; - - private MyPlaylistEventCallback(MediaSession2Impl session) { - mSession = new WeakReference<>(session); - } - - @Override - public void onPlaylistChanged(MediaPlaylistAgent playlistAgent, List list, - MediaMetadata2 metadata) { - final MediaSession2Impl session = mSession.get(); - if (session == null) { - return; - } - session.notifyPlaylistChangedOnExecutor(playlistAgent, list, metadata); - } - - @Override - public void onPlaylistMetadataChanged(MediaPlaylistAgent playlistAgent, - MediaMetadata2 metadata) { - final MediaSession2Impl session = mSession.get(); - if (session == null) { - return; - } - session.notifyPlaylistMetadataChangedOnExecutor(playlistAgent, metadata); - } - - @Override - public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) { - final MediaSession2Impl session = mSession.get(); - if (session == null) { - return; - } - session.notifyRepeatModeChangedOnExecutor(playlistAgent, repeatMode); - } - - @Override - public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) { - final MediaSession2Impl session = mSession.get(); - if (session == null) { - return; - } - session.notifyShuffleModeChangedOnExecutor(playlistAgent, shuffleMode); - } - } - - public static final class CommandImpl implements CommandProvider { - private static final String KEY_COMMAND_CODE - = "android.media.media_session2.command.command_code"; - private static final String KEY_COMMAND_CUSTOM_COMMAND - = "android.media.media_session2.command.custom_command"; - private static final String KEY_COMMAND_EXTRAS - = "android.media.media_session2.command.extras"; - - private final SessionCommand2 mInstance; - private final int mCommandCode; - // Nonnull if it's custom command - private final String mCustomCommand; - private final Bundle mExtras; - - public CommandImpl(SessionCommand2 instance, int commandCode) { - mInstance = instance; - mCommandCode = commandCode; - mCustomCommand = null; - mExtras = null; - } - - public CommandImpl(SessionCommand2 instance, @NonNull String action, - @Nullable Bundle extras) { - if (action == null) { - throw new IllegalArgumentException("action shouldn't be null"); - } - mInstance = instance; - mCommandCode = COMMAND_CODE_CUSTOM; - mCustomCommand = action; - mExtras = extras; - } - - @Override - public int getCommandCode_impl() { - return mCommandCode; - } - - @Override - public @Nullable String getCustomCommand_impl() { - return mCustomCommand; - } - - @Override - public @Nullable Bundle getExtras_impl() { - return mExtras; - } - - /** - * @return a new Bundle instance from the Command - */ - @Override - public Bundle toBundle_impl() { - Bundle bundle = new Bundle(); - bundle.putInt(KEY_COMMAND_CODE, mCommandCode); - bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand); - bundle.putBundle(KEY_COMMAND_EXTRAS, mExtras); - return bundle; - } - - /** - * @return a new Command instance from the Bundle - */ - public static SessionCommand2 fromBundle_impl(@NonNull Bundle command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - int code = command.getInt(KEY_COMMAND_CODE); - if (code != COMMAND_CODE_CUSTOM) { - return new SessionCommand2(code); - } else { - String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND); - if (customCommand == null) { - return null; - } - return new SessionCommand2(customCommand, command.getBundle(KEY_COMMAND_EXTRAS)); - } - } - - @Override - public boolean equals_impl(Object obj) { - if (!(obj instanceof CommandImpl)) { - return false; - } - CommandImpl other = (CommandImpl) obj; - // TODO(jaewan): Compare Commands with the generated UUID, as we're doing for the MI2. - return mCommandCode == other.mCommandCode - && TextUtils.equals(mCustomCommand, other.mCustomCommand); - } - - @Override - public int hashCode_impl() { - final int prime = 31; - return ((mCustomCommand != null) - ? mCustomCommand.hashCode() : 0) * prime + mCommandCode; - } - } - - /** - * Represent set of {@link SessionCommand2}. - */ - public static class CommandGroupImpl implements CommandGroupProvider { - private static final String KEY_COMMANDS = - "android.media.mediasession2.commandgroup.commands"; - - // Prefix for all command codes - private static final String PREFIX_COMMAND_CODE = "COMMAND_CODE_"; - - // Prefix for command codes that will be sent directly to the MediaPlayerBase - private static final String PREFIX_COMMAND_CODE_PLAYBACK = "COMMAND_CODE_PLAYBACK_"; - - // Prefix for command codes that will be sent directly to the MediaPlaylistAgent - private static final String PREFIX_COMMAND_CODE_PLAYLIST = "COMMAND_CODE_PLAYLIST_"; - - private Set mCommands = new HashSet<>(); - private final SessionCommandGroup2 mInstance; - - public CommandGroupImpl(SessionCommandGroup2 instance, Object other) { - mInstance = instance; - if (other != null && other instanceof CommandGroupImpl) { - mCommands.addAll(((CommandGroupImpl) other).mCommands); - } - } - - public CommandGroupImpl() { - mInstance = new SessionCommandGroup2(this); - } - - @Override - public void addCommand_impl(@NonNull SessionCommand2 command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mCommands.add(command); - } - - @Override - public void addAllPredefinedCommands_impl() { - addCommandsWithPrefix(PREFIX_COMMAND_CODE); - } - - void addAllPlaybackCommands() { - addCommandsWithPrefix(PREFIX_COMMAND_CODE_PLAYBACK); - } - - void addAllPlaylistCommands() { - addCommandsWithPrefix(PREFIX_COMMAND_CODE_PLAYLIST); - } - - private void addCommandsWithPrefix(String prefix) { - // TODO(jaewan): (Can be post-P): Don't use reflection for this purpose. - final Field[] fields = MediaSession2.class.getFields(); - if (fields != null) { - for (int i = 0; i < fields.length; i++) { - if (fields[i].getName().startsWith(prefix)) { - try { - mCommands.add(new SessionCommand2(fields[i].getInt(null))); - } catch (IllegalAccessException e) { - Log.w(TAG, "Unexpected " + fields[i] + " in MediaSession2"); - } - } - } - } - } - - @Override - public void removeCommand_impl(@NonNull SessionCommand2 command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mCommands.remove(command); - } - - @Override - public boolean hasCommand_impl(@NonNull SessionCommand2 command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - return mCommands.contains(command); - } - - @Override - public boolean hasCommand_impl(int code) { - if (code == COMMAND_CODE_CUSTOM) { - throw new IllegalArgumentException("Use hasCommand(Command) for custom command"); - } - for (SessionCommand2 command : mCommands) { - if (command.getCommandCode() == code) { - return true; - } - } - return false; - } - - @Override - public Set getCommands_impl() { - return getCommands(); - } - - public Set getCommands() { - return Collections.unmodifiableSet(mCommands); - } - - /** - * @return new bundle from the CommandGroup - * @hide - */ - @Override - public Bundle toBundle_impl() { - ArrayList list = new ArrayList<>(); - for (SessionCommand2 command : mCommands) { - list.add(command.toBundle()); - } - Bundle bundle = new Bundle(); - bundle.putParcelableArrayList(KEY_COMMANDS, list); - return bundle; - } - - /** - * @return new instance of CommandGroup from the bundle - * @hide - */ - public static @Nullable SessionCommandGroup2 fromBundle_impl(Bundle commands) { - if (commands == null) { - return null; - } - List list = commands.getParcelableArrayList(KEY_COMMANDS); - if (list == null) { - return null; - } - SessionCommandGroup2 commandGroup = new SessionCommandGroup2(); - for (int i = 0; i < list.size(); i++) { - Parcelable parcelable = list.get(i); - if (!(parcelable instanceof Bundle)) { - continue; - } - Bundle commandBundle = (Bundle) parcelable; - SessionCommand2 command = SessionCommand2.fromBundle(commandBundle); - if (command != null) { - commandGroup.addCommand(command); - } - } - return commandGroup; - } - } - - public static class ControllerInfoImpl implements ControllerInfoProvider { - private final ControllerInfo mInstance; - private final int mUid; - private final String mPackageName; - private final boolean mIsTrusted; - private final IMediaController2 mControllerBinder; - - public ControllerInfoImpl(Context context, ControllerInfo instance, int uid, - int pid, @NonNull String packageName, @NonNull IMediaController2 callback) { - if (TextUtils.isEmpty(packageName)) { - throw new IllegalArgumentException("packageName shouldn't be empty"); - } - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - - mInstance = instance; - mUid = uid; - mPackageName = packageName; - mControllerBinder = callback; - MediaSessionManager manager = - (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); - // Ask server whether the controller is trusted. - // App cannot know this because apps cannot query enabled notification listener for - // another package, but system server can do. - mIsTrusted = manager.isTrustedForMediaControl( - new MediaSessionManager.RemoteUserInfo(packageName, pid, uid)); - } - - @Override - public String getPackageName_impl() { - return mPackageName; - } - - @Override - public int getUid_impl() { - return mUid; - } - - @Override - public boolean isTrusted_impl() { - return mIsTrusted; - } - - @Override - public int hashCode_impl() { - return mControllerBinder.hashCode(); - } - - @Override - public boolean equals_impl(Object obj) { - if (!(obj instanceof ControllerInfo)) { - return false; - } - return equals(((ControllerInfo) obj).getProvider()); - } - - @Override - public String toString_impl() { - return "ControllerInfo {pkg=" + mPackageName + ", uid=" + mUid + ", trusted=" - + mIsTrusted + "}"; - } - - @Override - public int hashCode() { - return mControllerBinder.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ControllerInfoImpl)) { - return false; - } - ControllerInfoImpl other = (ControllerInfoImpl) obj; - return mControllerBinder.asBinder().equals(other.mControllerBinder.asBinder()); - } - - ControllerInfo getInstance() { - return mInstance; - } - - IBinder getId() { - return mControllerBinder.asBinder(); - } - - IMediaController2 getControllerBinder() { - return mControllerBinder; - } - - static ControllerInfoImpl from(ControllerInfo controller) { - return (ControllerInfoImpl) controller.getProvider(); - } - } - - public static class CommandButtonImpl implements CommandButtonProvider { - private static final String KEY_COMMAND - = "android.media.media_session2.command_button.command"; - private static final String KEY_ICON_RES_ID - = "android.media.media_session2.command_button.icon_res_id"; - private static final String KEY_DISPLAY_NAME - = "android.media.media_session2.command_button.display_name"; - private static final String KEY_EXTRAS - = "android.media.media_session2.command_button.extras"; - private static final String KEY_ENABLED - = "android.media.media_session2.command_button.enabled"; - - private final CommandButton mInstance; - private SessionCommand2 mCommand; - private int mIconResId; - private String mDisplayName; - private Bundle mExtras; - private boolean mEnabled; - - public CommandButtonImpl(@Nullable SessionCommand2 command, int iconResId, - @Nullable String displayName, Bundle extras, boolean enabled) { - mCommand = command; - mIconResId = iconResId; - mDisplayName = displayName; - mExtras = extras; - mEnabled = enabled; - mInstance = new CommandButton(this); - } - - @Override - public @Nullable - SessionCommand2 getCommand_impl() { - return mCommand; - } - - @Override - public int getIconResId_impl() { - return mIconResId; - } - - @Override - public @Nullable String getDisplayName_impl() { - return mDisplayName; - } - - @Override - public @Nullable Bundle getExtras_impl() { - return mExtras; - } - - @Override - public boolean isEnabled_impl() { - return mEnabled; - } - - @NonNull Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putBundle(KEY_COMMAND, mCommand.toBundle()); - bundle.putInt(KEY_ICON_RES_ID, mIconResId); - bundle.putString(KEY_DISPLAY_NAME, mDisplayName); - bundle.putBundle(KEY_EXTRAS, mExtras); - bundle.putBoolean(KEY_ENABLED, mEnabled); - return bundle; - } - - static @Nullable CommandButton fromBundle(Bundle bundle) { - if (bundle == null) { - return null; - } - CommandButton.Builder builder = new CommandButton.Builder(); - builder.setCommand(SessionCommand2.fromBundle(bundle.getBundle(KEY_COMMAND))); - builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0)); - builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME)); - builder.setExtras(bundle.getBundle(KEY_EXTRAS)); - builder.setEnabled(bundle.getBoolean(KEY_ENABLED)); - try { - return builder.build(); - } catch (IllegalStateException e) { - // Malformed or version mismatch. Return null for now. - return null; - } - } - - /** - * Builder for {@link CommandButton}. - */ - public static class BuilderImpl implements CommandButtonProvider.BuilderProvider { - private final CommandButton.Builder mInstance; - private SessionCommand2 mCommand; - private int mIconResId; - private String mDisplayName; - private Bundle mExtras; - private boolean mEnabled; - - public BuilderImpl(CommandButton.Builder instance) { - mInstance = instance; - mEnabled = true; - } - - @Override - public CommandButton.Builder setCommand_impl(SessionCommand2 command) { - mCommand = command; - return mInstance; - } - - @Override - public CommandButton.Builder setIconResId_impl(int resId) { - mIconResId = resId; - return mInstance; - } - - @Override - public CommandButton.Builder setDisplayName_impl(String displayName) { - mDisplayName = displayName; - return mInstance; - } - - @Override - public CommandButton.Builder setEnabled_impl(boolean enabled) { - mEnabled = enabled; - return mInstance; - } - - @Override - public CommandButton.Builder setExtras_impl(Bundle extras) { - mExtras = extras; - return mInstance; - } - - @Override - public CommandButton build_impl() { - if (mEnabled && mCommand == null) { - throw new IllegalStateException("Enabled button needs Command" - + " for controller to invoke the command"); - } - if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM - && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) { - throw new IllegalStateException("Custom commands needs icon and" - + " and name to display"); - } - return new CommandButtonImpl(mCommand, mIconResId, mDisplayName, mExtras, mEnabled) - .mInstance; - } - } - } - - public static abstract class BuilderBaseImpl - implements BuilderBaseProvider { - final Context mContext; - MediaPlayerBase mPlayer; - String mId; - Executor mCallbackExecutor; - C mCallback; - MediaPlaylistAgent mPlaylistAgent; - VolumeProvider2 mVolumeProvider; - PendingIntent mSessionActivity; - - /** - * Constructor. - * - * @param context a context - * @throws IllegalArgumentException if any parameter is null, or the player is a - * {@link MediaSession2} or {@link MediaController2}. - */ - // TODO(jaewan): Also need executor - public BuilderBaseImpl(@NonNull Context context) { - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - mContext = context; - // Ensure non-null - mId = ""; - } - - @Override - public void setPlayer_impl(@NonNull MediaPlayerBase player) { - if (player == null) { - throw new IllegalArgumentException("player shouldn't be null"); - } - mPlayer = player; - } - - @Override - public void setPlaylistAgent_impl(@NonNull MediaPlaylistAgent playlistAgent) { - if (playlistAgent == null) { - throw new IllegalArgumentException("playlistAgent shouldn't be null"); - } - mPlaylistAgent = playlistAgent; - } - - @Override - public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) { - mVolumeProvider = volumeProvider; - } - - @Override - public void setSessionActivity_impl(PendingIntent pi) { - mSessionActivity = pi; - } - - @Override - public void setId_impl(@NonNull String id) { - if (id == null) { - throw new IllegalArgumentException("id shouldn't be null"); - } - mId = id; - } - - @Override - public void setSessionCallback_impl(@NonNull Executor executor, @NonNull C callback) { - if (executor == null) { - throw new IllegalArgumentException("executor shouldn't be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - mCallbackExecutor = executor; - mCallback = callback; - } - - @Override - public abstract T build_impl(); - } - - public static class BuilderImpl extends BuilderBaseImpl { - public BuilderImpl(Context context, Builder instance) { - super(context); - } - - @Override - public MediaSession2 build_impl() { - if (mCallbackExecutor == null) { - mCallbackExecutor = mContext.getMainExecutor(); - } - if (mCallback == null) { - mCallback = new SessionCallback() {}; - } - return new MediaSession2Impl(mContext, mPlayer, mId, mPlaylistAgent, - mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance(); - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java deleted file mode 100644 index 53a5986d2e..0000000000 --- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +++ /dev/null @@ -1,888 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.app.PendingIntent; -import android.content.Context; -import android.media.MediaController2; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaSession2.CommandButton; -import android.media.MediaSession2.ControllerInfo; -import android.media.Rating2; -import android.media.SessionCommand2; -import android.media.SessionCommandGroup2; -import android.media.VolumeProvider2; -import android.net.Uri; -import android.os.Binder; -import android.os.Bundle; -import android.os.DeadObjectException; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import androidx.annotation.GuardedBy; -import androidx.annotation.NonNull; - -import com.android.media.MediaSession2Impl.CommandButtonImpl; -import com.android.media.MediaSession2Impl.CommandGroupImpl; -import com.android.media.MediaSession2Impl.ControllerInfoImpl; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class MediaSession2Stub extends IMediaSession2.Stub { - - static final String ARGUMENT_KEY_POSITION = "android.media.media_session2.key_position"; - static final String ARGUMENT_KEY_ITEM_INDEX = "android.media.media_session2.key_item_index"; - static final String ARGUMENT_KEY_PLAYLIST_PARAMS = - "android.media.media_session2.key_playlist_params"; - - private static final String TAG = "MediaSession2Stub"; - private static final boolean DEBUG = true; // TODO(jaewan): Rename. - - private static final SparseArray sCommandsForOnCommandRequest = - new SparseArray<>(); - - private final Object mLock = new Object(); - private final WeakReference mSession; - - @GuardedBy("mLock") - private final ArrayMap mControllers = new ArrayMap<>(); - @GuardedBy("mLock") - private final Set mConnectingControllers = new HashSet<>(); - @GuardedBy("mLock") - private final ArrayMap mAllowedCommandGroupMap = - new ArrayMap<>(); - @GuardedBy("mLock") - private final ArrayMap> mSubscriptions = new ArrayMap<>(); - - public MediaSession2Stub(MediaSession2Impl session) { - mSession = new WeakReference<>(session); - - synchronized (sCommandsForOnCommandRequest) { - if (sCommandsForOnCommandRequest.size() == 0) { - CommandGroupImpl group = new CommandGroupImpl(); - group.addAllPlaybackCommands(); - group.addAllPlaylistCommands(); - Set commands = group.getCommands(); - for (SessionCommand2 command : commands) { - sCommandsForOnCommandRequest.append(command.getCommandCode(), command); - } - } - } - } - - public void destroyNotLocked() { - final List list; - synchronized (mLock) { - mSession.clear(); - list = getControllers(); - mControllers.clear(); - } - for (int i = 0; i < list.size(); i++) { - IMediaController2 controllerBinder = - ((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder(); - try { - // Should be used without a lock hold to prevent potential deadlock. - controllerBinder.onDisconnected(); - } catch (RemoteException e) { - // Controller is gone. Should be fine because we're destroying. - } - } - } - - private MediaSession2Impl getSession() { - final MediaSession2Impl session = mSession.get(); - if (session == null && DEBUG) { - Log.d(TAG, "Session is closed", new IllegalStateException()); - } - return session; - } - - // Get controller if the command from caller to session is able to be handled. - private ControllerInfo getControllerIfAble(IMediaController2 caller) { - synchronized (mLock) { - final ControllerInfo controllerInfo = mControllers.get(caller.asBinder()); - if (controllerInfo == null && DEBUG) { - Log.d(TAG, "Controller is disconnected", new IllegalStateException()); - } - return controllerInfo; - } - } - - // Get controller if the command from caller to session is able to be handled. - private ControllerInfo getControllerIfAble(IMediaController2 caller, int commandCode) { - synchronized (mLock) { - final ControllerInfo controllerInfo = getControllerIfAble(caller); - if (controllerInfo == null) { - return null; - } - SessionCommandGroup2 allowedCommands = mAllowedCommandGroupMap.get(controllerInfo); - if (allowedCommands == null) { - Log.w(TAG, "Controller with null allowed commands. Ignoring", - new IllegalStateException()); - return null; - } - if (!allowedCommands.hasCommand(commandCode)) { - if (DEBUG) { - Log.d(TAG, "Controller isn't allowed for command " + commandCode); - } - return null; - } - return controllerInfo; - } - } - - // Get controller if the command from caller to session is able to be handled. - private ControllerInfo getControllerIfAble(IMediaController2 caller, SessionCommand2 command) { - synchronized (mLock) { - final ControllerInfo controllerInfo = getControllerIfAble(caller); - if (controllerInfo == null) { - return null; - } - SessionCommandGroup2 allowedCommands = mAllowedCommandGroupMap.get(controllerInfo); - if (allowedCommands == null) { - Log.w(TAG, "Controller with null allowed commands. Ignoring", - new IllegalStateException()); - return null; - } - if (!allowedCommands.hasCommand(command)) { - if (DEBUG) { - Log.d(TAG, "Controller isn't allowed for command " + command); - } - return null; - } - return controllerInfo; - } - } - - // Return binder if the session is able to send a command to the controller. - private IMediaController2 getControllerBinderIfAble(ControllerInfo controller) { - if (getSession() == null) { - // getSession() already logged if session is closed. - return null; - } - final ControllerInfoImpl impl = ControllerInfoImpl.from(controller); - synchronized (mLock) { - if (mControllers.get(impl.getId()) != null - || mConnectingControllers.contains(impl.getId())) { - return impl.getControllerBinder(); - } - if (DEBUG) { - Log.d(TAG, controller + " isn't connected nor connecting", - new IllegalArgumentException()); - } - return null; - } - } - - // Return binder if the session is able to send a command to the controller. - private IMediaController2 getControllerBinderIfAble(ControllerInfo controller, - int commandCode) { - synchronized (mLock) { - SessionCommandGroup2 allowedCommands = mAllowedCommandGroupMap.get(controller); - if (allowedCommands == null) { - Log.w(TAG, "Controller with null allowed commands. Ignoring"); - return null; - } - if (!allowedCommands.hasCommand(commandCode)) { - if (DEBUG) { - Log.d(TAG, "Controller isn't allowed for command " + commandCode); - } - return null; - } - return getControllerBinderIfAble(controller); - } - } - - private void onCommand(@NonNull IMediaController2 caller, int commandCode, - @NonNull SessionRunnable runnable) { - final MediaSession2Impl session = getSession(); - final ControllerInfo controller = getControllerIfAble(caller, commandCode); - if (session == null || controller == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - if (getControllerIfAble(caller, commandCode) == null) { - return; - } - SessionCommand2 command = sCommandsForOnCommandRequest.get(commandCode); - if (command != null) { - boolean accepted = session.getCallback().onCommandRequest(session.getInstance(), - controller, command); - if (!accepted) { - // Don't run rejected command. - if (DEBUG) { - Log.d(TAG, "Command (code=" + commandCode + ") from " - + controller + " was rejected by " + session); - } - return; - } - } - runnable.run(session, controller); - }); - } - - private void notifyAll(int commandCode, @NonNull NotifyRunnable runnable) { - List controllers = getControllers(); - for (int i = 0; i < controllers.size(); i++) { - notifyInternal(controllers.get(i), - getControllerBinderIfAble(controllers.get(i), commandCode), runnable); - } - } - - private void notifyAll(@NonNull NotifyRunnable runnable) { - List controllers = getControllers(); - for (int i = 0; i < controllers.size(); i++) { - notifyInternal(controllers.get(i), - getControllerBinderIfAble(controllers.get(i)), runnable); - } - } - - private void notify(@NonNull ControllerInfo controller, @NonNull NotifyRunnable runnable) { - notifyInternal(controller, getControllerBinderIfAble(controller), runnable); - } - - private void notify(@NonNull ControllerInfo controller, int commandCode, - @NonNull NotifyRunnable runnable) { - notifyInternal(controller, getControllerBinderIfAble(controller, commandCode), runnable); - } - - // Do not call this API directly. Use notify() instead. - private void notifyInternal(@NonNull ControllerInfo controller, - @NonNull IMediaController2 iController, @NonNull NotifyRunnable runnable) { - if (controller == null || iController == null) { - return; - } - try { - runnable.run(controller, iController); - } catch (DeadObjectException e) { - if (DEBUG) { - Log.d(TAG, controller.toString() + " is gone", e); - } - onControllerClosed(iController); - } catch (RemoteException e) { - // Currently it's TransactionTooLargeException or DeadSystemException. - // We'd better to leave log for those cases because - // - TransactionTooLargeException means that we may need to fix our code. - // (e.g. add pagination or special way to deliver Bitmap) - // - DeadSystemException means that errors around it can be ignored. - Log.w(TAG, "Exception in " + controller.toString(), e); - } - } - - private void onControllerClosed(IMediaController2 iController) { - ControllerInfo controller; - synchronized (mLock) { - controller = mControllers.remove(iController.asBinder()); - if (DEBUG) { - Log.d(TAG, "releasing " + controller); - } - mSubscriptions.remove(controller); - } - final MediaSession2Impl session = getSession(); - if (session == null || controller == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - session.getCallback().onDisconnected(session.getInstance(), controller); - }); - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - // AIDL methods for session overrides - ////////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void connect(final IMediaController2 caller, final String callingPackage) - throws RuntimeException { - final MediaSession2Impl session = getSession(); - if (session == null) { - return; - } - final Context context = session.getContext(); - final ControllerInfo controllerInfo = new ControllerInfo(context, - Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller); - session.getCallbackExecutor().execute(() -> { - if (getSession() == null) { - return; - } - synchronized (mLock) { - // Keep connecting controllers. - // This helps sessions to call APIs in the onConnect() (e.g. setCustomLayout()) - // instead of pending them. - mConnectingControllers.add(ControllerInfoImpl.from(controllerInfo).getId()); - } - SessionCommandGroup2 allowedCommands = session.getCallback().onConnect( - session.getInstance(), controllerInfo); - // Don't reject connection for the request from trusted app. - // Otherwise server will fail to retrieve session's information to dispatch - // media keys to. - boolean accept = allowedCommands != null || controllerInfo.isTrusted(); - if (accept) { - ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controllerInfo); - if (DEBUG) { - Log.d(TAG, "Accepting connection, controllerInfo=" + controllerInfo - + " allowedCommands=" + allowedCommands); - } - if (allowedCommands == null) { - // For trusted apps, send non-null allowed commands to keep connection. - allowedCommands = new SessionCommandGroup2(); - } - synchronized (mLock) { - mConnectingControllers.remove(controllerImpl.getId()); - mControllers.put(controllerImpl.getId(), controllerInfo); - mAllowedCommandGroupMap.put(controllerInfo, allowedCommands); - } - // If connection is accepted, notify the current state to the controller. - // It's needed because we cannot call synchronous calls between session/controller. - // Note: We're doing this after the onConnectionChanged(), but there's no guarantee - // that events here are notified after the onConnected() because - // IMediaController2 is oneway (i.e. async call) and Stub will - // use thread poll for incoming calls. - final int playerState = session.getInstance().getPlayerState(); - final long positionEventTimeMs = System.currentTimeMillis(); - final long positionMs = session.getInstance().getCurrentPosition(); - final float playbackSpeed = session.getInstance().getPlaybackSpeed(); - final long bufferedPositionMs = session.getInstance().getBufferedPosition(); - final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl) - session.getPlaybackInfo().getProvider()).toBundle(); - final int repeatMode = session.getInstance().getRepeatMode(); - final int shuffleMode = session.getInstance().getShuffleMode(); - final PendingIntent sessionActivity = session.getSessionActivity(); - final List playlist = - allowedCommands.hasCommand(SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST) - ? session.getInstance().getPlaylist() : null; - final List playlistBundle; - if (playlist != null) { - playlistBundle = new ArrayList<>(); - // TODO(jaewan): Find a way to avoid concurrent modification exception. - for (int i = 0; i < playlist.size(); i++) { - final MediaItem2 item = playlist.get(i); - if (item != null) { - final Bundle itemBundle = item.toBundle(); - if (itemBundle != null) { - playlistBundle.add(itemBundle); - } - } - } - } else { - playlistBundle = null; - } - - // Double check if session is still there, because close() can be called in another - // thread. - if (getSession() == null) { - return; - } - try { - caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(), - playerState, positionEventTimeMs, positionMs, playbackSpeed, - bufferedPositionMs, playbackInfoBundle, repeatMode, shuffleMode, - playlistBundle, sessionActivity); - } catch (RemoteException e) { - // Controller may be died prematurely. - // TODO(jaewan): Handle here. - } - } else { - synchronized (mLock) { - mConnectingControllers.remove(ControllerInfoImpl.from(controllerInfo).getId()); - } - if (DEBUG) { - Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo); - } - try { - caller.onDisconnected(); - } catch (RemoteException e) { - // Controller may be died prematurely. - // Not an issue because we'll ignore it anyway. - } - } - }); - } - - @Override - public void release(final IMediaController2 caller) throws RemoteException { - onControllerClosed(caller); - } - - @Override - public void setVolumeTo(final IMediaController2 caller, final int value, final int flags) - throws RuntimeException { - onCommand(caller, SessionCommand2.COMMAND_CODE_SET_VOLUME, - (session, controller) -> { - VolumeProvider2 volumeProvider = session.getVolumeProvider(); - if (volumeProvider == null) { - // TODO(jaewan): Set local stream volume - } else { - volumeProvider.onSetVolumeTo(value); - } - }); - } - - @Override - public void adjustVolume(IMediaController2 caller, int direction, int flags) - throws RuntimeException { - onCommand(caller, SessionCommand2.COMMAND_CODE_SET_VOLUME, - (session, controller) -> { - VolumeProvider2 volumeProvider = session.getVolumeProvider(); - if (volumeProvider == null) { - // TODO(jaewan): Adjust local stream volume - } else { - volumeProvider.onAdjustVolume(direction); - } - }); - } - - @Override - public void sendTransportControlCommand(IMediaController2 caller, - int commandCode, Bundle args) throws RuntimeException { - onCommand(caller, commandCode, (session, controller) -> { - switch (commandCode) { - case SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY: - session.getInstance().play(); - break; - case SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE: - session.getInstance().pause(); - break; - case SessionCommand2.COMMAND_CODE_PLAYBACK_STOP: - session.getInstance().stop(); - break; - case SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE: - session.getInstance().prepare(); - break; - case SessionCommand2.COMMAND_CODE_PLAYBACK_SEEK_TO: - session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION)); - break; - default: - // TODO(jaewan): Resend unknown (new) commands through the custom command. - } - }); - } - - @Override - public void sendCustomCommand(final IMediaController2 caller, final Bundle commandBundle, - final Bundle args, final ResultReceiver receiver) { - final MediaSession2Impl session = getSession(); - if (session == null) { - return; - } - final SessionCommand2 command = SessionCommand2.fromBundle(commandBundle); - if (command == null) { - Log.w(TAG, "sendCustomCommand(): Ignoring null command from " - + getControllerIfAble(caller)); - return; - } - final ControllerInfo controller = getControllerIfAble(caller, command); - if (controller == null) { - return; - } - session.getCallbackExecutor().execute(() -> { - if (getControllerIfAble(caller, command) == null) { - return; - } - session.getCallback().onCustomCommand(session.getInstance(), - controller, command, args, receiver); - }); - } - - @Override - public void prepareFromUri(final IMediaController2 caller, final Uri uri, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI, - (session, controller) -> { - if (uri == null) { - Log.w(TAG, "prepareFromUri(): Ignoring null uri from " + controller); - return; - } - session.getCallback().onPrepareFromUri(session.getInstance(), controller, uri, - extras); - }); - } - - @Override - public void prepareFromSearch(final IMediaController2 caller, final String query, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH, - (session, controller) -> { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "prepareFromSearch(): Ignoring empty query from " + controller); - return; - } - session.getCallback().onPrepareFromSearch(session.getInstance(), - controller, query, extras); - }); - } - - @Override - public void prepareFromMediaId(final IMediaController2 caller, final String mediaId, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID, - (session, controller) -> { - if (mediaId == null) { - Log.w(TAG, "prepareFromMediaId(): Ignoring null mediaId from " + controller); - return; - } - session.getCallback().onPrepareFromMediaId(session.getInstance(), - controller, mediaId, extras); - }); - } - - @Override - public void playFromUri(final IMediaController2 caller, final Uri uri, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_URI, - (session, controller) -> { - if (uri == null) { - Log.w(TAG, "playFromUri(): Ignoring null uri from " + controller); - return; - } - session.getCallback().onPlayFromUri(session.getInstance(), controller, uri, - extras); - }); - } - - @Override - public void playFromSearch(final IMediaController2 caller, final String query, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH, - (session, controller) -> { - if (TextUtils.isEmpty(query)) { - Log.w(TAG, "playFromSearch(): Ignoring empty query from " + controller); - return; - } - session.getCallback().onPlayFromSearch(session.getInstance(), - controller, query, extras); - }); - } - - @Override - public void playFromMediaId(final IMediaController2 caller, final String mediaId, - final Bundle extras) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID, - (session, controller) -> { - if (mediaId == null) { - Log.w(TAG, "playFromMediaId(): Ignoring null mediaId from " + controller); - return; - } - session.getCallback().onPlayFromMediaId(session.getInstance(), controller, - mediaId, extras); - }); - } - - @Override - public void setRating(final IMediaController2 caller, final String mediaId, - final Bundle ratingBundle) { - onCommand(caller, SessionCommand2.COMMAND_CODE_SESSION_SET_RATING, - (session, controller) -> { - if (mediaId == null) { - Log.w(TAG, "setRating(): Ignoring null mediaId from " + controller); - return; - } - if (ratingBundle == null) { - Log.w(TAG, "setRating(): Ignoring null ratingBundle from " + controller); - return; - } - Rating2 rating = Rating2.fromBundle(ratingBundle); - if (rating == null) { - if (ratingBundle == null) { - Log.w(TAG, "setRating(): Ignoring null rating from " + controller); - return; - } - return; - } - session.getCallback().onSetRating(session.getInstance(), controller, mediaId, - rating); - }); - } - - @Override - public void setPlaylist(final IMediaController2 caller, final List playlist, - final Bundle metadata) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST, (session, controller) -> { - if (playlist == null) { - Log.w(TAG, "setPlaylist(): Ignoring null playlist from " + controller); - return; - } - List list = new ArrayList<>(); - for (int i = 0; i < playlist.size(); i++) { - // Recreates UUID in the playlist - MediaItem2 item = MediaItem2Impl.fromBundle(playlist.get(i), null); - if (item != null) { - list.add(item); - } - } - session.getInstance().setPlaylist(list, MediaMetadata2.fromBundle(metadata)); - }); - } - - @Override - public void updatePlaylistMetadata(final IMediaController2 caller, final Bundle metadata) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA, - (session, controller) -> { - session.getInstance().updatePlaylistMetadata(MediaMetadata2.fromBundle(metadata)); - }); - } - - @Override - public void addPlaylistItem(IMediaController2 caller, int index, Bundle mediaItem) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM, - (session, controller) -> { - // Resets the UUID from the incoming media id, so controller may reuse a media - // item multiple times for addPlaylistItem. - session.getInstance().addPlaylistItem(index, - MediaItem2Impl.fromBundle(mediaItem, null)); - }); - } - - @Override - public void removePlaylistItem(IMediaController2 caller, Bundle mediaItem) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM, - (session, controller) -> { - MediaItem2 item = MediaItem2.fromBundle(mediaItem); - // Note: MediaItem2 has hidden UUID to identify it across the processes. - session.getInstance().removePlaylistItem(item); - }); - } - - @Override - public void replacePlaylistItem(IMediaController2 caller, int index, Bundle mediaItem) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM, - (session, controller) -> { - // Resets the UUID from the incoming media id, so controller may reuse a media - // item multiple times for replacePlaylistItem. - session.getInstance().replacePlaylistItem(index, - MediaItem2Impl.fromBundle(mediaItem, null)); - }); - } - - @Override - public void skipToPlaylistItem(IMediaController2 caller, Bundle mediaItem) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, - (session, controller) -> { - if (mediaItem == null) { - Log.w(TAG, "skipToPlaylistItem(): Ignoring null mediaItem from " - + controller); - } - // Note: MediaItem2 has hidden UUID to identify it across the processes. - session.getInstance().skipToPlaylistItem(MediaItem2.fromBundle(mediaItem)); - }); - } - - @Override - public void skipToPreviousItem(IMediaController2 caller) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM, - (session, controller) -> { - session.getInstance().skipToPreviousItem(); - }); - } - - @Override - public void skipToNextItem(IMediaController2 caller) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM, - (session, controller) -> { - session.getInstance().skipToNextItem(); - }); - } - - @Override - public void setRepeatMode(IMediaController2 caller, int repeatMode) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE, - (session, controller) -> { - session.getInstance().setRepeatMode(repeatMode); - }); - } - - @Override - public void setShuffleMode(IMediaController2 caller, int shuffleMode) { - onCommand(caller, SessionCommand2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE, - (session, controller) -> { - session.getInstance().setShuffleMode(shuffleMode); - }); - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - // APIs for MediaSession2Impl - ////////////////////////////////////////////////////////////////////////////////////////////// - - // TODO(jaewan): (Can be Post-P) Need a way to get controller with permissions - public List getControllers() { - ArrayList controllers = new ArrayList<>(); - synchronized (mLock) { - for (int i = 0; i < mControllers.size(); i++) { - controllers.add(mControllers.valueAt(i)); - } - } - return controllers; - } - - // Should be used without a lock to prevent potential deadlock. - public void notifyPlayerStateChangedNotLocked(int state) { - notifyAll((controller, iController) -> { - iController.onPlayerStateChanged(state); - }); - } - - // TODO(jaewan): Rename - public void notifyPositionChangedNotLocked(long eventTimeMs, long positionMs) { - notifyAll((controller, iController) -> { - iController.onPositionChanged(eventTimeMs, positionMs); - }); - } - - public void notifyPlaybackSpeedChangedNotLocked(float speed) { - notifyAll((controller, iController) -> { - iController.onPlaybackSpeedChanged(speed); - }); - } - - public void notifyBufferedPositionChangedNotLocked(long bufferedPositionMs) { - notifyAll((controller, iController) -> { - iController.onBufferedPositionChanged(bufferedPositionMs); - }); - } - - public void notifyCustomLayoutNotLocked(ControllerInfo controller, List layout) { - notify(controller, (unused, iController) -> { - List layoutBundles = new ArrayList<>(); - for (int i = 0; i < layout.size(); i++) { - Bundle bundle = ((CommandButtonImpl) layout.get(i).getProvider()).toBundle(); - if (bundle != null) { - layoutBundles.add(bundle); - } - } - iController.onCustomLayoutChanged(layoutBundles); - }); - } - - public void notifyPlaylistChangedNotLocked(List playlist, MediaMetadata2 metadata) { - final List bundleList; - if (playlist != null) { - bundleList = new ArrayList<>(); - for (int i = 0; i < playlist.size(); i++) { - if (playlist.get(i) != null) { - Bundle bundle = playlist.get(i).toBundle(); - if (bundle != null) { - bundleList.add(bundle); - } - } - } - } else { - bundleList = null; - } - final Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle(); - notifyAll((controller, iController) -> { - if (getControllerBinderIfAble(controller, - SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST) != null) { - iController.onPlaylistChanged(bundleList, metadataBundle); - } else if (getControllerBinderIfAble(controller, - SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST_METADATA) != null) { - iController.onPlaylistMetadataChanged(metadataBundle); - } - }); - } - - public void notifyPlaylistMetadataChangedNotLocked(MediaMetadata2 metadata) { - final Bundle metadataBundle = (metadata == null) ? null : metadata.toBundle(); - notifyAll(SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST_METADATA, - (unused, iController) -> { - iController.onPlaylistMetadataChanged(metadataBundle); - }); - } - - public void notifyRepeatModeChangedNotLocked(int repeatMode) { - notifyAll((unused, iController) -> { - iController.onRepeatModeChanged(repeatMode); - }); - } - - public void notifyShuffleModeChangedNotLocked(int shuffleMode) { - notifyAll((unused, iController) -> { - iController.onShuffleModeChanged(shuffleMode); - }); - } - - public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) { - final Bundle playbackInfoBundle = - ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle(); - notifyAll((unused, iController) -> { - iController.onPlaybackInfoChanged(playbackInfoBundle); - }); - } - - public void setAllowedCommands(ControllerInfo controller, SessionCommandGroup2 commands) { - synchronized (mLock) { - mAllowedCommandGroupMap.put(controller, commands); - } - notify(controller, (unused, iController) -> { - iController.onAllowedCommandsChanged(commands.toBundle()); - }); - } - - public void sendCustomCommand(ControllerInfo controller, SessionCommand2 command, Bundle args, - ResultReceiver receiver) { - if (receiver != null && controller == null) { - throw new IllegalArgumentException("Controller shouldn't be null if result receiver is" - + " specified"); - } - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - notify(controller, (unused, iController) -> { - Bundle commandBundle = command.toBundle(); - iController.onCustomCommand(commandBundle, args, null); - }); - } - - public void sendCustomCommand(SessionCommand2 command, Bundle args) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - Bundle commandBundle = command.toBundle(); - notifyAll((unused, iController) -> { - iController.onCustomCommand(commandBundle, args, null); - }); - } - - public void notifyError(int errorCode, Bundle extras) { - notifyAll((unused, iController) -> { - iController.onError(errorCode, extras); - }); - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - // Misc - ////////////////////////////////////////////////////////////////////////////////////////////// - - @FunctionalInterface - private interface SessionRunnable { - void run(final MediaSession2Impl session, final ControllerInfo controller); - } - - @FunctionalInterface - private interface NotifyRunnable { - void run(final ControllerInfo controller, - final IMediaController2 iController) throws RemoteException; - } -} diff --git a/packages/MediaComponents/src/com/android/media/Rating2Impl.java b/packages/MediaComponents/src/com/android/media/Rating2Impl.java deleted file mode 100644 index e2b9f0a9be..0000000000 --- a/packages/MediaComponents/src/com/android/media/Rating2Impl.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.Rating2.*; - -import android.media.Rating2; -import android.media.Rating2.Style; -import android.media.update.Rating2Provider; -import android.os.Bundle; -import android.util.Log; - -import java.util.Objects; - -public final class Rating2Impl implements Rating2Provider { - private static final String TAG = "Rating2"; - - private static final String KEY_STYLE = "android.media.rating2.style"; - private static final String KEY_VALUE = "android.media.rating2.value"; - - private final static float RATING_NOT_RATED = -1.0f; - - private final Rating2 mInstance; - private final int mRatingStyle; - private final float mRatingValue; - - private Rating2Impl(@Style int ratingStyle, float rating) { - mRatingStyle = ratingStyle; - mRatingValue = rating; - mInstance = new Rating2(this); - } - - @Override - public String toString_impl() { - return "Rating2:style=" + mRatingStyle + " rating=" - + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue)); - } - - @Override - public boolean equals_impl(Object obj) { - if (!(obj instanceof Rating2)) { - return false; - } - Rating2Impl other = (Rating2Impl) ((Rating2) obj).getProvider(); - return mRatingStyle == other.mRatingStyle - && mRatingValue == other.mRatingValue; - } - - @Override - public int hashCode_impl() { - return Objects.hash(mRatingStyle, mRatingValue); - } - - Rating2 getInstance() { - return mInstance; - } - - public static Rating2 fromBundle_impl(Bundle bundle) { - if (bundle == null) { - return null; - } - return new Rating2Impl(bundle.getInt(KEY_STYLE), bundle.getFloat(KEY_VALUE)).getInstance(); - } - - public Bundle toBundle_impl() { - Bundle bundle = new Bundle(); - bundle.putInt(KEY_STYLE, mRatingStyle); - bundle.putFloat(KEY_VALUE, mRatingValue); - return bundle; - } - - public static Rating2 newUnratedRating_impl(@Style int ratingStyle) { - switch(ratingStyle) { - case RATING_HEART: - case RATING_THUMB_UP_DOWN: - case RATING_3_STARS: - case RATING_4_STARS: - case RATING_5_STARS: - case RATING_PERCENTAGE: - return new Rating2Impl(ratingStyle, RATING_NOT_RATED).getInstance(); - default: - return null; - } - } - - public static Rating2 newHeartRating_impl(boolean hasHeart) { - return new Rating2Impl(RATING_HEART, hasHeart ? 1.0f : 0.0f).getInstance(); - } - - public static Rating2 newThumbRating_impl(boolean thumbIsUp) { - return new Rating2Impl(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f).getInstance(); - } - - public static Rating2 newStarRating_impl(int starRatingStyle, float starRating) { - float maxRating = RATING_NOT_RATED; - switch(starRatingStyle) { - case RATING_3_STARS: - maxRating = 3.0f; - break; - case RATING_4_STARS: - maxRating = 4.0f; - break; - case RATING_5_STARS: - maxRating = 5.0f; - break; - default: - Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); - return null; - } - if ((starRating < 0.0f) || (starRating > maxRating)) { - Log.e(TAG, "Trying to set out of range star-based rating"); - return null; - } - return new Rating2Impl(starRatingStyle, starRating).getInstance(); - } - - public static Rating2 newPercentageRating_impl(float percent) { - if ((percent < 0.0f) || (percent > 100.0f)) { - Log.e(TAG, "Invalid percentage-based rating value"); - return null; - } else { - return new Rating2Impl(RATING_PERCENTAGE, percent).getInstance(); - } - } - - @Override - public boolean isRated_impl() { - return mRatingValue >= 0.0f; - } - - @Override - public int getRatingStyle_impl() { - return mRatingStyle; - } - - @Override - public boolean hasHeart_impl() { - if (mRatingStyle != RATING_HEART) { - return false; - } else { - return (mRatingValue == 1.0f); - } - } - - @Override - public boolean isThumbUp_impl() { - if (mRatingStyle != RATING_THUMB_UP_DOWN) { - return false; - } else { - return (mRatingValue == 1.0f); - } - } - - @Override - public float getStarRating_impl() { - switch (mRatingStyle) { - case RATING_3_STARS: - case RATING_4_STARS: - case RATING_5_STARS: - if (mInstance.isRated()) { - return mRatingValue; - } - default: - return -1.0f; - } - } - - @Override - public float getPercentRating_impl() { - if ((mRatingStyle != RATING_PERCENTAGE) || !mInstance.isRated()) { - return -1.0f; - } else { - return mRatingValue; - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java deleted file mode 100644 index 1c570aafaa..0000000000 --- a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.media.DataSourceDesc; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlayerBase; -import android.media.MediaPlayerBase.PlayerEventCallback; -import android.media.MediaPlaylistAgent; -import android.media.MediaSession2.OnDataSourceMissingHelper; -import android.util.ArrayMap; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - -public class SessionPlaylistAgent extends MediaPlaylistAgent { - private static final String TAG = "SessionPlaylistAgent"; - @VisibleForTesting - static final int END_OF_PLAYLIST = -1; - @VisibleForTesting - static final int NO_VALID_ITEMS = -2; - - private final PlayItem mEopPlayItem = new PlayItem(END_OF_PLAYLIST, null); - - private final Object mLock = new Object(); - private final MediaSession2Impl mSessionImpl; - private final MyPlayerEventCallback mPlayerCallback; - - @GuardedBy("mLock") - private MediaPlayerBase mPlayer; - @GuardedBy("mLock") - private OnDataSourceMissingHelper mDsmHelper; - // TODO: Check if having the same item is okay (b/74090741) - @GuardedBy("mLock") - private ArrayList mPlaylist = new ArrayList<>(); - @GuardedBy("mLock") - private ArrayList mShuffledList = new ArrayList<>(); - @GuardedBy("mLock") - private Map mItemDsdMap = new ArrayMap<>(); - @GuardedBy("mLock") - private MediaMetadata2 mMetadata; - @GuardedBy("mLock") - private int mRepeatMode; - @GuardedBy("mLock") - private int mShuffleMode; - @GuardedBy("mLock") - private PlayItem mCurrent; - - // Called on session callback executor. - private class MyPlayerEventCallback extends PlayerEventCallback { - public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb, - @Nullable DataSourceDesc dsd) { - if (mPlayer != mpb) { - return; - } - synchronized (mLock) { - if (dsd == null && mCurrent != null) { - mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1); - updateCurrentIfNeededLocked(); - } - } - } - } - - private class PlayItem { - int shuffledIdx; - DataSourceDesc dsd; - MediaItem2 mediaItem; - - PlayItem(int shuffledIdx) { - this(shuffledIdx, null); - } - - PlayItem(int shuffledIdx, DataSourceDesc dsd) { - this.shuffledIdx = shuffledIdx; - if (shuffledIdx >= 0) { - this.mediaItem = mShuffledList.get(shuffledIdx); - if (dsd == null) { - synchronized (mLock) { - this.dsd = retrieveDataSourceDescLocked(this.mediaItem); - } - } else { - this.dsd = dsd; - } - } - } - - boolean isValid() { - if (this == mEopPlayItem) { - return true; - } - if (mediaItem == null) { - return false; - } - if (dsd == null) { - return false; - } - if (shuffledIdx >= mShuffledList.size()) { - return false; - } - if (mediaItem != mShuffledList.get(shuffledIdx)) { - return false; - } - if (mediaItem.getDataSourceDesc() != null - && !mediaItem.getDataSourceDesc().equals(dsd)) { - return false; - } - return true; - } - } - - public SessionPlaylistAgent(@NonNull MediaSession2Impl sessionImpl, - @NonNull MediaPlayerBase player) { - if (sessionImpl == null) { - throw new IllegalArgumentException("sessionImpl shouldn't be null"); - } - if (player == null) { - throw new IllegalArgumentException("player shouldn't be null"); - } - mSessionImpl = sessionImpl; - mPlayer = player; - mPlayerCallback = new MyPlayerEventCallback(); - mPlayer.registerPlayerEventCallback(mSessionImpl.getCallbackExecutor(), mPlayerCallback); - } - - public void setPlayer(@NonNull MediaPlayerBase player) { - if (player == null) { - throw new IllegalArgumentException("player shouldn't be null"); - } - synchronized (mLock) { - if (player == mPlayer) { - return; - } - mPlayer.unregisterPlayerEventCallback(mPlayerCallback); - mPlayer = player; - mPlayer.registerPlayerEventCallback( - mSessionImpl.getCallbackExecutor(), mPlayerCallback); - updatePlayerDataSourceLocked(); - } - } - - public void setOnDataSourceMissingHelper(OnDataSourceMissingHelper helper) { - synchronized (mLock) { - mDsmHelper = helper; - } - } - - public void clearOnDataSourceMissingHelper() { - synchronized (mLock) { - mDsmHelper = null; - } - } - - @Override - public @Nullable List getPlaylist() { - synchronized (mLock) { - return Collections.unmodifiableList(mPlaylist); - } - } - - @Override - public void setPlaylist(@NonNull List list, @Nullable MediaMetadata2 metadata) { - if (list == null) { - throw new IllegalArgumentException("list shouldn't be null"); - } - - synchronized (mLock) { - mItemDsdMap.clear(); - - mPlaylist.clear(); - mPlaylist.addAll(list); - applyShuffleModeLocked(); - - mMetadata = metadata; - mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1); - updatePlayerDataSourceLocked(); - } - notifyPlaylistChanged(); - } - - @Override - public @Nullable MediaMetadata2 getPlaylistMetadata() { - return mMetadata; - } - - @Override - public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) { - synchronized (mLock) { - if (metadata == mMetadata) { - return; - } - mMetadata = metadata; - } - notifyPlaylistMetadataChanged(); - } - - @Override - public void addPlaylistItem(int index, @NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - synchronized (mLock) { - index = clamp(index, mPlaylist.size()); - int shuffledIdx = index; - mPlaylist.add(index, item); - if (mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_NONE) { - mShuffledList.add(index, item); - } else { - // Add the item in random position of mShuffledList. - shuffledIdx = ThreadLocalRandom.current().nextInt(mShuffledList.size() + 1); - mShuffledList.add(shuffledIdx, item); - } - if (!hasValidItem()) { - mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1); - updatePlayerDataSourceLocked(); - } else { - updateCurrentIfNeededLocked(); - } - } - notifyPlaylistChanged(); - } - - @Override - public void removePlaylistItem(@NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - synchronized (mLock) { - if (!mPlaylist.remove(item)) { - return; - } - mShuffledList.remove(item); - mItemDsdMap.remove(item); - updateCurrentIfNeededLocked(); - } - notifyPlaylistChanged(); - } - - @Override - public void replacePlaylistItem(int index, @NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - synchronized (mLock) { - if (mPlaylist.size() <= 0) { - return; - } - index = clamp(index, mPlaylist.size() - 1); - int shuffledIdx = mShuffledList.indexOf(mPlaylist.get(index)); - mItemDsdMap.remove(mShuffledList.get(shuffledIdx)); - mShuffledList.set(shuffledIdx, item); - mPlaylist.set(index, item); - if (!hasValidItem()) { - mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1); - updatePlayerDataSourceLocked(); - } else { - updateCurrentIfNeededLocked(); - } - } - notifyPlaylistChanged(); - } - - @Override - public void skipToPlaylistItem(@NonNull MediaItem2 item) { - if (item == null) { - throw new IllegalArgumentException("item shouldn't be null"); - } - synchronized (mLock) { - if (!hasValidItem() || item.equals(mCurrent.mediaItem)) { - return; - } - int shuffledIdx = mShuffledList.indexOf(item); - if (shuffledIdx < 0) { - return; - } - mCurrent = new PlayItem(shuffledIdx); - updateCurrentIfNeededLocked(); - } - } - - @Override - public void skipToPreviousItem() { - synchronized (mLock) { - if (!hasValidItem()) { - return; - } - PlayItem prev = getNextValidPlayItemLocked(mCurrent.shuffledIdx, -1); - if (prev != mEopPlayItem) { - mCurrent = prev; - } - updateCurrentIfNeededLocked(); - } - } - - @Override - public void skipToNextItem() { - synchronized (mLock) { - if (!hasValidItem() || mCurrent == mEopPlayItem) { - return; - } - PlayItem next = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1); - if (next != mEopPlayItem) { - mCurrent = next; - } - updateCurrentIfNeededLocked(); - } - } - - @Override - public int getRepeatMode() { - return mRepeatMode; - } - - @Override - public void setRepeatMode(int repeatMode) { - if (repeatMode < MediaPlaylistAgent.REPEAT_MODE_NONE - || repeatMode > MediaPlaylistAgent.REPEAT_MODE_GROUP) { - return; - } - synchronized (mLock) { - if (mRepeatMode == repeatMode) { - return; - } - mRepeatMode = repeatMode; - switch (repeatMode) { - case MediaPlaylistAgent.REPEAT_MODE_ONE: - if (mCurrent != null && mCurrent != mEopPlayItem) { - mPlayer.loopCurrent(true); - } - break; - case MediaPlaylistAgent.REPEAT_MODE_ALL: - case MediaPlaylistAgent.REPEAT_MODE_GROUP: - if (mCurrent == mEopPlayItem) { - mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1); - updatePlayerDataSourceLocked(); - } - // pass through - case MediaPlaylistAgent.REPEAT_MODE_NONE: - mPlayer.loopCurrent(false); - break; - } - } - notifyRepeatModeChanged(); - } - - @Override - public int getShuffleMode() { - return mShuffleMode; - } - - @Override - public void setShuffleMode(int shuffleMode) { - if (shuffleMode < MediaPlaylistAgent.SHUFFLE_MODE_NONE - || shuffleMode > MediaPlaylistAgent.SHUFFLE_MODE_GROUP) { - return; - } - synchronized (mLock) { - if (mShuffleMode == shuffleMode) { - return; - } - mShuffleMode = shuffleMode; - applyShuffleModeLocked(); - updateCurrentIfNeededLocked(); - } - notifyShuffleModeChanged(); - } - - @VisibleForTesting - int getCurShuffledIndex() { - return hasValidItem() ? mCurrent.shuffledIdx : NO_VALID_ITEMS; - } - - private boolean hasValidItem() { - return mCurrent != null; - } - - private DataSourceDesc retrieveDataSourceDescLocked(MediaItem2 item) { - DataSourceDesc dsd = item.getDataSourceDesc(); - if (dsd != null) { - mItemDsdMap.put(item, dsd); - return dsd; - } - dsd = mItemDsdMap.get(item); - if (dsd != null) { - return dsd; - } - OnDataSourceMissingHelper helper = mDsmHelper; - if (helper != null) { - // TODO: Do not call onDataSourceMissing with the lock (b/74090741). - dsd = helper.onDataSourceMissing(mSessionImpl.getInstance(), item); - if (dsd != null) { - mItemDsdMap.put(item, dsd); - } - } - return dsd; - } - - // TODO: consider to call updateCurrentIfNeededLocked inside (b/74090741) - private PlayItem getNextValidPlayItemLocked(int curShuffledIdx, int direction) { - int size = mPlaylist.size(); - if (curShuffledIdx == END_OF_PLAYLIST) { - curShuffledIdx = (direction > 0) ? -1 : size; - } - for (int i = 0; i < size; i++) { - curShuffledIdx += direction; - if (curShuffledIdx < 0 || curShuffledIdx >= mPlaylist.size()) { - if (mRepeatMode == REPEAT_MODE_NONE) { - return (i == size - 1) ? null : mEopPlayItem; - } else { - curShuffledIdx = curShuffledIdx < 0 ? mPlaylist.size() - 1 : 0; - } - } - DataSourceDesc dsd = retrieveDataSourceDescLocked(mShuffledList.get(curShuffledIdx)); - if (dsd != null) { - return new PlayItem(curShuffledIdx, dsd); - } - } - return null; - } - - private void updateCurrentIfNeededLocked() { - if (!hasValidItem() || mCurrent.isValid()) { - return; - } - int shuffledIdx = mShuffledList.indexOf(mCurrent.mediaItem); - if (shuffledIdx >= 0) { - // Added an item. - mCurrent.shuffledIdx = shuffledIdx; - return; - } - - if (mCurrent.shuffledIdx >= mShuffledList.size()) { - mCurrent = getNextValidPlayItemLocked(mShuffledList.size() - 1, 1); - } else { - mCurrent.mediaItem = mShuffledList.get(mCurrent.shuffledIdx); - if (retrieveDataSourceDescLocked(mCurrent.mediaItem) == null) { - mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1); - } - } - updatePlayerDataSourceLocked(); - return; - } - - private void updatePlayerDataSourceLocked() { - if (mCurrent == null || mCurrent == mEopPlayItem) { - return; - } - if (mPlayer.getCurrentDataSource() != mCurrent.dsd) { - mPlayer.setDataSource(mCurrent.dsd); - mPlayer.loopCurrent(mRepeatMode == MediaPlaylistAgent.REPEAT_MODE_ONE); - } - // TODO: Call setNextDataSource (b/74090741) - } - - private void applyShuffleModeLocked() { - mShuffledList.clear(); - mShuffledList.addAll(mPlaylist); - if (mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_ALL - || mShuffleMode == MediaPlaylistAgent.SHUFFLE_MODE_GROUP) { - Collections.shuffle(mShuffledList); - } - } - - // Clamps value to [0, size] - private static int clamp(int value, int size) { - if (value < 0) { - return 0; - } - return (value > size) ? size : value; - } -} diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java deleted file mode 100644 index 396ebbb382..0000000000 --- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE; -import static android.media.SessionToken2.TYPE_SESSION; -import static android.media.SessionToken2.TYPE_SESSION_SERVICE; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.media.SessionToken2; -import android.media.SessionToken2.TokenType; -import android.media.update.SessionToken2Provider; -import android.os.Bundle; -import android.os.IBinder; -import android.text.TextUtils; - -import java.util.List; - -public class SessionToken2Impl implements SessionToken2Provider { - private static final String KEY_UID = "android.media.token.uid"; - private static final String KEY_TYPE = "android.media.token.type"; - private static final String KEY_PACKAGE_NAME = "android.media.token.package_name"; - private static final String KEY_SERVICE_NAME = "android.media.token.service_name"; - private static final String KEY_ID = "android.media.token.id"; - private static final String KEY_SESSION_BINDER = "android.media.token.session_binder"; - - private final SessionToken2 mInstance; - private final int mUid; - private final @TokenType int mType; - private final String mPackageName; - private final String mServiceName; - private final String mId; - private final IMediaSession2 mSessionBinder; - - /** - * Public constructor for the legacy support (i.e. browser can try connecting to any browser - * service if it knows the service name) - */ - public SessionToken2Impl(Context context, SessionToken2 instance, - String packageName, String serviceName, int uid) { - if (TextUtils.isEmpty(packageName)) { - throw new IllegalArgumentException("packageName shouldn't be empty"); - } - if (TextUtils.isEmpty(serviceName)) { - throw new IllegalArgumentException("serviceName shouldn't be empty"); - } - mInstance = instance; - // Calculate uid if it's not specified. - final PackageManager manager = context.getPackageManager(); - if (uid < 0) { - try { - uid = manager.getPackageUid(packageName, 0); - } catch (NameNotFoundException e) { - throw new IllegalArgumentException("Cannot find package " + packageName); - } - } - mUid = uid; - mId = null; - mType = -1; - mPackageName = packageName; - mServiceName = serviceName; - mSessionBinder = null; - } - - SessionToken2Impl(int uid, int type, String packageName, String serviceName, String id, - IMediaSession2 sessionBinder) { - // TODO(jaewan): Add sanity check (b/73863865) - mUid = uid; - mType = type; - mPackageName = packageName; - mServiceName = serviceName; - mId = id; - mSessionBinder = sessionBinder; - mInstance = new SessionToken2(this); - } - - public static String getSessionId(ResolveInfo resolveInfo) { - if (resolveInfo == null || resolveInfo.serviceInfo == null) { - return null; - } else if (resolveInfo.serviceInfo.metaData == null) { - return ""; - } - return null; - } - - public SessionToken2 getInstance() { - return mInstance; - } - - @Override - public String getPackageName_impl() { - return mPackageName; - } - - @Override - public int getUid_impl() { - return mUid; - } - - @Override - public String getId_imp() { - return mId; - } - - @Override - public int getType_impl() { - return mType; - } - - String getServiceName() { - return mServiceName; - } - - IMediaSession2 getSessionBinder() { - return mSessionBinder; - } - - public static SessionToken2 fromBundle_impl(Bundle bundle) { - if (bundle == null) { - return null; - } - final int uid = bundle.getInt(KEY_UID); - final @TokenType int type = bundle.getInt(KEY_TYPE, -1); - final String packageName = bundle.getString(KEY_PACKAGE_NAME); - final String serviceName = bundle.getString(KEY_SERVICE_NAME); - final String id = bundle.getString(KEY_ID); - final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER); - - // Sanity check. - switch (type) { - case TYPE_SESSION: - if (sessionBinder == null) { - throw new IllegalArgumentException("Unexpected sessionBinder for session," - + " binder=" + sessionBinder); - } - break; - case TYPE_SESSION_SERVICE: - case TYPE_LIBRARY_SERVICE: - if (TextUtils.isEmpty(serviceName)) { - throw new IllegalArgumentException("Session service needs service name"); - } - break; - default: - throw new IllegalArgumentException("Invalid type"); - } - if (TextUtils.isEmpty(packageName) || id == null) { - throw new IllegalArgumentException("Package name nor ID cannot be null."); - } - return new SessionToken2Impl(uid, type, packageName, serviceName, id, - sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null) - .getInstance(); - } - - @Override - public Bundle toBundle_impl() { - Bundle bundle = new Bundle(); - bundle.putInt(KEY_UID, mUid); - bundle.putString(KEY_PACKAGE_NAME, mPackageName); - bundle.putString(KEY_SERVICE_NAME, mServiceName); - bundle.putString(KEY_ID, mId); - bundle.putInt(KEY_TYPE, mType); - bundle.putBinder(KEY_SESSION_BINDER, - mSessionBinder != null ? mSessionBinder.asBinder() : null); - return bundle; - } - - @Override - public int hashCode_impl() { - final int prime = 31; - return mType - + prime * (mUid - + prime * (mPackageName.hashCode() - + prime * (mId.hashCode() - + prime * (mServiceName != null ? mServiceName.hashCode() : 0)))); - } - - @Override - public boolean equals_impl(Object obj) { - if (!(obj instanceof SessionToken2)) { - return false; - } - SessionToken2Impl other = from((SessionToken2) obj); - return mUid == other.mUid - && TextUtils.equals(mPackageName, other.mPackageName) - && TextUtils.equals(mServiceName, other.mServiceName) - && TextUtils.equals(mId, other.mId) - && mType == other.mType; - } - - @Override - public String toString_impl() { - return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType - + " service=" + mServiceName + " binder=" + mSessionBinder + "}"; - } - - static SessionToken2Impl from(SessionToken2 token) { - return ((SessionToken2Impl) token.getProvider()); - } -} diff --git a/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java b/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java deleted file mode 100644 index bf22e1b47a..0000000000 --- a/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.media; - -import static android.media.VolumeProvider2.VOLUME_CONTROL_ABSOLUTE; -import static android.media.VolumeProvider2.VOLUME_CONTROL_FIXED; -import static android.media.VolumeProvider2.VOLUME_CONTROL_RELATIVE; - -import android.media.VolumeProvider2; -import android.media.update.VolumeProvider2Provider; - -public class VolumeProvider2Impl implements VolumeProvider2Provider { - private final VolumeProvider2 mInstance; - private final int mControlType; - private final int mMaxVolume; - - private int mCurrentVolume; - private Callback mCallback; - - public VolumeProvider2Impl(VolumeProvider2 instance, - @VolumeProvider2.ControlType int controlType, int maxVolume, int currentVolume) { - if (controlType != VOLUME_CONTROL_FIXED && controlType != VOLUME_CONTROL_RELATIVE - && controlType != VOLUME_CONTROL_ABSOLUTE) { - throw new IllegalArgumentException("wrong controlType " + controlType); - } - if (maxVolume < 0 || currentVolume < 0) { - throw new IllegalArgumentException("volume shouldn't be negative" - + ", maxVolume=" + maxVolume + ", currentVolume=" + currentVolume); - } - if (currentVolume > maxVolume) { - throw new IllegalArgumentException("currentVolume shouldn't be greater than maxVolume" - + ", maxVolume=" + maxVolume + ", currentVolume=" + currentVolume); - } - mInstance = instance; - mControlType = controlType; - mMaxVolume = maxVolume; - mCurrentVolume = currentVolume; - } - - @Override - public int getControlType_impl() { - return mControlType; - } - - @Override - public int getMaxVolume_impl() { - return mMaxVolume; - } - - @Override - public int getCurrentVolume_impl() { - return mCurrentVolume; - } - - @Override - public void setCurrentVolume_impl(int currentVolume) { - if (currentVolume < 0) { - throw new IllegalArgumentException("currentVolume shouldn't be negative" - + ", currentVolume=" + currentVolume); - } - mCurrentVolume = currentVolume; - if (mCallback != null) { - mCallback.onVolumeChanged(mInstance); - } - } - - /** - * Sets a callback to receive volume changes. - */ - public void setCallback(Callback callback) { - mCallback = callback; - } - - /** - * Listens for changes to the volume. - */ - public static abstract class Callback { - public abstract void onVolumeChanged(VolumeProvider2 volumeProvider); - } -} diff --git a/packages/MediaComponents/src/com/android/media/subtitle/MediaTimeProvider.java b/packages/MediaComponents/src/com/android/media/subtitle/MediaTimeProvider.java deleted file mode 100644 index af36d7f69e..0000000000 --- a/packages/MediaComponents/src/com/android/media/subtitle/MediaTimeProvider.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.subtitle; - -// Note: This is just copied from android.media.MediaTimeProvider. -public interface MediaTimeProvider { - // we do not allow negative media time - /** - * Presentation time value if no timed event notification is requested. - */ - public final static long NO_TIME = -1; - - /** - * Cancels all previous notification request from this listener if any. It - * registers the listener to get seek and stop notifications. If timeUs is - * not negative, it also registers the listener for a timed event - * notification when the presentation time reaches (becomes greater) than - * the value specified. This happens immediately if the current media time - * is larger than or equal to timeUs. - * - * @param timeUs presentation time to get timed event callback at (or - * {@link #NO_TIME}) - */ - public void notifyAt(long timeUs, OnMediaTimeListener listener); - - /** - * Cancels all previous notification request from this listener if any. It - * registers the listener to get seek and stop notifications. If the media - * is stopped, the listener will immediately receive a stop notification. - * Otherwise, it will receive a timed event notificaton. - */ - public void scheduleUpdate(OnMediaTimeListener listener); - - /** - * Cancels all previous notification request from this listener if any. - */ - public void cancelNotifications(OnMediaTimeListener listener); - - /** - * Get the current presentation time. - * - * @param precise Whether getting a precise time is important. This is - * more costly. - * @param monotonic Whether returned time should be monotonic: that is, - * greater than or equal to the last returned time. Don't - * always set this to true. E.g. this has undesired - * consequences if the media is seeked between calls. - * @throws IllegalStateException if the media is not initialized - */ - public long getCurrentTimeUs(boolean precise, boolean monotonic) - throws IllegalStateException; - - public static interface OnMediaTimeListener { - /** - * Called when the registered time was reached naturally. - * - * @param timeUs current media time - */ - void onTimedEvent(long timeUs); - - /** - * Called when the media time changed due to seeking. - * - * @param timeUs current media time - */ - void onSeek(long timeUs); - - /** - * Called when the playback stopped. This is not called on pause, only - * on full stop, at which point there is no further current media time. - */ - void onStop(); - } -} - diff --git a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java b/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java deleted file mode 100644 index 97d392783c..0000000000 --- a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.subtitle; - -import android.content.Context; -import android.media.MediaFormat; -import android.media.MediaPlayer2.TrackInfo; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.view.accessibility.CaptioningManager; - -import com.android.media.subtitle.SubtitleTrack.RenderingWidget; - -import java.util.Locale; -import java.util.Vector; - -// Note: This is forked from android.media.SubtitleController since P -/** - * The subtitle controller provides the architecture to display subtitles for a - * media source. It allows specifying which tracks to display, on which anchor - * to display them, and also allows adding external, out-of-band subtitle tracks. - */ -public class SubtitleController { - private MediaTimeProvider mTimeProvider; - private Vector mRenderers; - private Vector mTracks; - private SubtitleTrack mSelectedTrack; - private boolean mShowing; - private CaptioningManager mCaptioningManager; - private Handler mHandler; - - private static final int WHAT_SHOW = 1; - private static final int WHAT_HIDE = 2; - private static final int WHAT_SELECT_TRACK = 3; - private static final int WHAT_SELECT_DEFAULT_TRACK = 4; - - private final Handler.Callback mCallback = new Handler.Callback() { - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case WHAT_SHOW: - doShow(); - return true; - case WHAT_HIDE: - doHide(); - return true; - case WHAT_SELECT_TRACK: - doSelectTrack((SubtitleTrack)msg.obj); - return true; - case WHAT_SELECT_DEFAULT_TRACK: - doSelectDefaultTrack(); - return true; - default: - return false; - } - } - }; - - private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener = - new CaptioningManager.CaptioningChangeListener() { - @Override - public void onEnabledChanged(boolean enabled) { - selectDefaultTrack(); - } - - @Override - public void onLocaleChanged(Locale locale) { - selectDefaultTrack(); - } - }; - - public SubtitleController(Context context) { - this(context, null, null); - } - - /** - * Creates a subtitle controller for a media playback object that implements - * the MediaTimeProvider interface. - * - * @param timeProvider - */ - public SubtitleController( - Context context, - MediaTimeProvider timeProvider, - Listener listener) { - mTimeProvider = timeProvider; - mListener = listener; - - mRenderers = new Vector(); - mShowing = false; - mTracks = new Vector(); - mCaptioningManager = - (CaptioningManager)context.getSystemService(Context.CAPTIONING_SERVICE); - } - - @Override - protected void finalize() throws Throwable { - mCaptioningManager.removeCaptioningChangeListener( - mCaptioningChangeListener); - super.finalize(); - } - - /** - * @return the available subtitle tracks for this media. These include - * the tracks found by {@link MediaPlayer} as well as any tracks added - * manually via {@link #addTrack}. - */ - public SubtitleTrack[] getTracks() { - synchronized(mTracks) { - SubtitleTrack[] tracks = new SubtitleTrack[mTracks.size()]; - mTracks.toArray(tracks); - return tracks; - } - } - - /** - * @return the currently selected subtitle track - */ - public SubtitleTrack getSelectedTrack() { - return mSelectedTrack; - } - - private RenderingWidget getRenderingWidget() { - if (mSelectedTrack == null) { - return null; - } - return mSelectedTrack.getRenderingWidget(); - } - - /** - * Selects a subtitle track. As a result, this track will receive - * in-band data from the {@link MediaPlayer}. However, this does - * not change the subtitle visibility. - * - * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} - * - * @param track The subtitle track to select. This must be one of the - * tracks in {@link #getTracks}. - * @return true if the track was successfully selected. - */ - public boolean selectTrack(SubtitleTrack track) { - if (track != null && !mTracks.contains(track)) { - return false; - } - - processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track)); - return true; - } - - private void doSelectTrack(SubtitleTrack track) { - mTrackIsExplicit = true; - if (mSelectedTrack == track) { - return; - } - - if (mSelectedTrack != null) { - mSelectedTrack.hide(); - mSelectedTrack.setTimeProvider(null); - } - - mSelectedTrack = track; - if (mAnchor != null) { - mAnchor.setSubtitleWidget(getRenderingWidget()); - } - - if (mSelectedTrack != null) { - mSelectedTrack.setTimeProvider(mTimeProvider); - mSelectedTrack.show(); - } - - if (mListener != null) { - mListener.onSubtitleTrackSelected(track); - } - } - - /** - * @return the default subtitle track based on system preferences, or null, - * if no such track exists in this manager. - * - * Supports HLS-flags: AUTOSELECT, FORCED & DEFAULT. - * - * 1. If captioning is disabled, only consider FORCED tracks. Otherwise, - * consider all tracks, but prefer non-FORCED ones. - * 2. If user selected "Default" caption language: - * a. If there is a considered track with DEFAULT=yes, returns that track - * (favor the first one in the current language if there are more than - * one default tracks, or the first in general if none of them are in - * the current language). - * b. Otherwise, if there is a track with AUTOSELECT=yes in the current - * language, return that one. - * c. If there are no default tracks, and no autoselectable tracks in the - * current language, return null. - * 3. If there is a track with the caption language, select that one. Prefer - * the one with AUTOSELECT=no. - * - * The default values for these flags are DEFAULT=no, AUTOSELECT=yes - * and FORCED=no. - */ - public SubtitleTrack getDefaultTrack() { - SubtitleTrack bestTrack = null; - int bestScore = -1; - - Locale selectedLocale = mCaptioningManager.getLocale(); - Locale locale = selectedLocale; - if (locale == null) { - locale = Locale.getDefault(); - } - boolean selectForced = !mCaptioningManager.isEnabled(); - - synchronized(mTracks) { - for (SubtitleTrack track: mTracks) { - MediaFormat format = track.getFormat(); - String language = format.getString(MediaFormat.KEY_LANGUAGE); - boolean forced = - format.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0; - boolean autoselect = - format.getInteger(MediaFormat.KEY_IS_AUTOSELECT, 1) != 0; - boolean is_default = - format.getInteger(MediaFormat.KEY_IS_DEFAULT, 0) != 0; - - boolean languageMatches = - (locale == null || - locale.getLanguage().equals("") || - locale.getISO3Language().equals(language) || - locale.getLanguage().equals(language)); - // is_default is meaningless unless caption language is 'default' - int score = (forced ? 0 : 8) + - (((selectedLocale == null) && is_default) ? 4 : 0) + - (autoselect ? 0 : 2) + (languageMatches ? 1 : 0); - - if (selectForced && !forced) { - continue; - } - - // we treat null locale/language as matching any language - if ((selectedLocale == null && is_default) || - (languageMatches && - (autoselect || forced || selectedLocale != null))) { - if (score > bestScore) { - bestScore = score; - bestTrack = track; - } - } - } - } - return bestTrack; - } - - private boolean mTrackIsExplicit = false; - private boolean mVisibilityIsExplicit = false; - - /** should be called from anchor thread */ - public void selectDefaultTrack() { - processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK)); - } - - private void doSelectDefaultTrack() { - if (mTrackIsExplicit) { - // If track selection is explicit, but visibility - // is not, it falls back to the captioning setting - if (!mVisibilityIsExplicit) { - if (mCaptioningManager.isEnabled() || - (mSelectedTrack != null && - mSelectedTrack.getFormat().getInteger( - MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0)) { - show(); - } else if (mSelectedTrack != null - && mSelectedTrack.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { - hide(); - } - mVisibilityIsExplicit = false; - } - return; - } - - // We can have a default (forced) track even if captioning - // is not enabled. This is handled by getDefaultTrack(). - // Show this track unless subtitles were explicitly hidden. - SubtitleTrack track = getDefaultTrack(); - if (track != null) { - selectTrack(track); - mTrackIsExplicit = false; - if (!mVisibilityIsExplicit) { - show(); - mVisibilityIsExplicit = false; - } - } - } - - /** must be called from anchor thread */ - public void reset() { - checkAnchorLooper(); - hide(); - selectTrack(null); - mTracks.clear(); - mTrackIsExplicit = false; - mVisibilityIsExplicit = false; - mCaptioningManager.removeCaptioningChangeListener( - mCaptioningChangeListener); - } - - /** - * Adds a new, external subtitle track to the manager. - * - * @param format the format of the track that will include at least - * the MIME type {@link MediaFormat@KEY_MIME}. - * @return the created {@link SubtitleTrack} object - */ - public SubtitleTrack addTrack(MediaFormat format) { - synchronized(mRenderers) { - for (Renderer renderer: mRenderers) { - if (renderer.supports(format)) { - SubtitleTrack track = renderer.createTrack(format); - if (track != null) { - synchronized(mTracks) { - if (mTracks.size() == 0) { - mCaptioningManager.addCaptioningChangeListener( - mCaptioningChangeListener); - } - mTracks.add(track); - } - return track; - } - } - } - } - return null; - } - - /** - * Show the selected (or default) subtitle track. - * - * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} - */ - public void show() { - processOnAnchor(mHandler.obtainMessage(WHAT_SHOW)); - } - - private void doShow() { - mShowing = true; - mVisibilityIsExplicit = true; - if (mSelectedTrack != null) { - mSelectedTrack.show(); - } - } - - /** - * Hide the selected (or default) subtitle track. - * - * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} - */ - public void hide() { - processOnAnchor(mHandler.obtainMessage(WHAT_HIDE)); - } - - private void doHide() { - mVisibilityIsExplicit = true; - if (mSelectedTrack != null) { - mSelectedTrack.hide(); - } - mShowing = false; - } - - /** - * Interface for supporting a single or multiple subtitle types in {@link - * MediaPlayer}. - */ - public abstract static class Renderer { - /** - * Called by {@link MediaPlayer}'s {@link SubtitleController} when a new - * subtitle track is detected, to see if it should use this object to - * parse and display this subtitle track. - * - * @param format the format of the track that will include at least - * the MIME type {@link MediaFormat@KEY_MIME}. - * - * @return true if and only if the track format is supported by this - * renderer - */ - public abstract boolean supports(MediaFormat format); - - /** - * Called by {@link MediaPlayer}'s {@link SubtitleController} for each - * subtitle track that was detected and is supported by this object to - * create a {@link SubtitleTrack} object. This object will be created - * for each track that was found. If the track is selected for display, - * this object will be used to parse and display the track data. - * - * @param format the format of the track that will include at least - * the MIME type {@link MediaFormat@KEY_MIME}. - * @return a {@link SubtitleTrack} object that will be used to parse - * and render the subtitle track. - */ - public abstract SubtitleTrack createTrack(MediaFormat format); - } - - /** - * Add support for a subtitle format in {@link MediaPlayer}. - * - * @param renderer a {@link SubtitleController.Renderer} object that adds - * support for a subtitle format. - */ - public void registerRenderer(Renderer renderer) { - synchronized(mRenderers) { - // TODO how to get available renderers in the system - if (!mRenderers.contains(renderer)) { - // TODO should added renderers override existing ones (to allow replacing?) - mRenderers.add(renderer); - } - } - } - - public boolean hasRendererFor(MediaFormat format) { - synchronized(mRenderers) { - // TODO how to get available renderers in the system - for (Renderer renderer: mRenderers) { - if (renderer.supports(format)) { - return true; - } - } - return false; - } - } - - /** - * Subtitle anchor, an object that is able to display a subtitle renderer, - * e.g. a VideoView. - */ - public interface Anchor { - /** - * Anchor should use the supplied subtitle rendering widget, or - * none if it is null. - */ - public void setSubtitleWidget(RenderingWidget subtitleWidget); - - /** - * Anchors provide the looper on which all track visibility changes - * (track.show/hide, setSubtitleWidget) will take place. - */ - public Looper getSubtitleLooper(); - } - - private Anchor mAnchor; - - /** - * called from anchor's looper (if any, both when unsetting and - * setting) - */ - public void setAnchor(Anchor anchor) { - if (mAnchor == anchor) { - return; - } - - if (mAnchor != null) { - checkAnchorLooper(); - mAnchor.setSubtitleWidget(null); - } - mAnchor = anchor; - mHandler = null; - if (mAnchor != null) { - mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback); - checkAnchorLooper(); - mAnchor.setSubtitleWidget(getRenderingWidget()); - } - } - - private void checkAnchorLooper() { - assert mHandler != null : "Should have a looper already"; - assert Looper.myLooper() == mHandler.getLooper() - : "Must be called from the anchor's looper"; - } - - private void processOnAnchor(Message m) { - assert mHandler != null : "Should have a looper already"; - if (Looper.myLooper() == mHandler.getLooper()) { - mHandler.dispatchMessage(m); - } else { - mHandler.sendMessage(m); - } - } - - public interface Listener { - /** - * Called when a subtitle track has been selected. - * - * @param track selected subtitle track or null - */ - public void onSubtitleTrackSelected(SubtitleTrack track); - } - - private Listener mListener; -} diff --git a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleTrack.java b/packages/MediaComponents/src/com/android/media/subtitle/SubtitleTrack.java deleted file mode 100644 index 6b9064a4c0..0000000000 --- a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleTrack.java +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.subtitle; - -import android.graphics.Canvas; -import android.media.MediaFormat; -import android.media.MediaPlayer2.TrackInfo; -import android.media.SubtitleData; -import android.os.Handler; -import android.util.Log; -import android.util.LongSparseArray; -import android.util.Pair; - -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.Vector; - -// Note: This is forked from android.media.SubtitleTrack since P -/** - * A subtitle track abstract base class that is responsible for parsing and displaying - * an instance of a particular type of subtitle. - */ -public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeListener { - private static final String TAG = "SubtitleTrack"; - private long mLastUpdateTimeMs; - private long mLastTimeMs; - - private Runnable mRunnable; - - final private LongSparseArray mRunsByEndTime = new LongSparseArray(); - final private LongSparseArray mRunsByID = new LongSparseArray(); - - private CueList mCues; - final private Vector mActiveCues = new Vector(); - protected boolean mVisible; - - public boolean DEBUG = false; - - protected Handler mHandler = new Handler(); - - private MediaFormat mFormat; - - public SubtitleTrack(MediaFormat format) { - mFormat = format; - mCues = new CueList(); - clearActiveCues(); - mLastTimeMs = -1; - } - - public final MediaFormat getFormat() { - return mFormat; - } - - private long mNextScheduledTimeMs = -1; - - public void onData(SubtitleData data) { - long runID = data.getStartTimeUs() + 1; - onData(data.getData(), true /* eos */, runID); - setRunDiscardTimeMs( - runID, - (data.getStartTimeUs() + data.getDurationUs()) / 1000); - } - - /** - * Called when there is input data for the subtitle track. The - * complete subtitle for a track can include multiple whole units - * (runs). Each of these units can have multiple sections. The - * contents of a run are submitted in sequential order, with eos - * indicating the last section of the run. Calls from different - * runs must not be intermixed. - * - * @param data subtitle data byte buffer - * @param eos true if this is the last section of the run. - * @param runID mostly-unique ID for this run of data. Subtitle cues - * with runID of 0 are discarded immediately after - * display. Cues with runID of ~0 are discarded - * only at the deletion of the track object. Cues - * with other runID-s are discarded at the end of the - * run, which defaults to the latest timestamp of - * any of its cues (with this runID). - */ - protected abstract void onData(byte[] data, boolean eos, long runID); - - /** - * Called when adding the subtitle rendering widget to the view hierarchy, - * as well as when showing or hiding the subtitle track, or when the video - * surface position has changed. - * - * @return the widget that renders this subtitle track. For most renderers - * there should be a single shared instance that is used for all - * tracks supported by that renderer, as at most one subtitle track - * is visible at one time. - */ - public abstract RenderingWidget getRenderingWidget(); - - /** - * Called when the active cues have changed, and the contents of the subtitle - * view should be updated. - */ - public abstract void updateView(Vector activeCues); - - protected synchronized void updateActiveCues(boolean rebuild, long timeMs) { - // out-of-order times mean seeking or new active cues being added - // (during their own timespan) - if (rebuild || mLastUpdateTimeMs > timeMs) { - clearActiveCues(); - } - - for(Iterator > it = - mCues.entriesBetween(mLastUpdateTimeMs, timeMs).iterator(); it.hasNext(); ) { - Pair event = it.next(); - Cue cue = event.second; - - if (cue.mEndTimeMs == event.first) { - // remove past cues - if (DEBUG) Log.v(TAG, "Removing " + cue); - mActiveCues.remove(cue); - if (cue.mRunID == 0) { - it.remove(); - } - } else if (cue.mStartTimeMs == event.first) { - // add new cues - // TRICKY: this will happen in start order - if (DEBUG) Log.v(TAG, "Adding " + cue); - if (cue.mInnerTimesMs != null) { - cue.onTime(timeMs); - } - mActiveCues.add(cue); - } else if (cue.mInnerTimesMs != null) { - // cue is modified - cue.onTime(timeMs); - } - } - - /* complete any runs */ - while (mRunsByEndTime.size() > 0 && - mRunsByEndTime.keyAt(0) <= timeMs) { - removeRunsByEndTimeIndex(0); // removes element - } - mLastUpdateTimeMs = timeMs; - } - - private void removeRunsByEndTimeIndex(int ix) { - Run run = mRunsByEndTime.valueAt(ix); - while (run != null) { - Cue cue = run.mFirstCue; - while (cue != null) { - mCues.remove(cue); - Cue nextCue = cue.mNextInRun; - cue.mNextInRun = null; - cue = nextCue; - } - mRunsByID.remove(run.mRunID); - Run nextRun = run.mNextRunAtEndTimeMs; - run.mPrevRunAtEndTimeMs = null; - run.mNextRunAtEndTimeMs = null; - run = nextRun; - } - mRunsByEndTime.removeAt(ix); - } - - @Override - protected void finalize() throws Throwable { - /* remove all cues (untangle all cross-links) */ - int size = mRunsByEndTime.size(); - for(int ix = size - 1; ix >= 0; ix--) { - removeRunsByEndTimeIndex(ix); - } - - super.finalize(); - } - - private synchronized void takeTime(long timeMs) { - mLastTimeMs = timeMs; - } - - protected synchronized void clearActiveCues() { - if (DEBUG) Log.v(TAG, "Clearing " + mActiveCues.size() + " active cues"); - mActiveCues.clear(); - mLastUpdateTimeMs = -1; - } - - protected void scheduleTimedEvents() { - /* get times for the next event */ - if (mTimeProvider != null) { - mNextScheduledTimeMs = mCues.nextTimeAfter(mLastTimeMs); - if (DEBUG) Log.d(TAG, "sched @" + mNextScheduledTimeMs + " after " + mLastTimeMs); - mTimeProvider.notifyAt( - mNextScheduledTimeMs >= 0 ? - (mNextScheduledTimeMs * 1000) : MediaTimeProvider.NO_TIME, - this); - } - } - - @Override - public void onTimedEvent(long timeUs) { - if (DEBUG) Log.d(TAG, "onTimedEvent " + timeUs); - synchronized (this) { - long timeMs = timeUs / 1000; - updateActiveCues(false, timeMs); - takeTime(timeMs); - } - updateView(mActiveCues); - scheduleTimedEvents(); - } - - @Override - public void onSeek(long timeUs) { - if (DEBUG) Log.d(TAG, "onSeek " + timeUs); - synchronized (this) { - long timeMs = timeUs / 1000; - updateActiveCues(true, timeMs); - takeTime(timeMs); - } - updateView(mActiveCues); - scheduleTimedEvents(); - } - - @Override - public void onStop() { - synchronized (this) { - if (DEBUG) Log.d(TAG, "onStop"); - clearActiveCues(); - mLastTimeMs = -1; - } - updateView(mActiveCues); - mNextScheduledTimeMs = -1; - mTimeProvider.notifyAt(MediaTimeProvider.NO_TIME, this); - } - - protected MediaTimeProvider mTimeProvider; - - public void show() { - if (mVisible) { - return; - } - - mVisible = true; - RenderingWidget renderingWidget = getRenderingWidget(); - if (renderingWidget != null) { - renderingWidget.setVisible(true); - } - if (mTimeProvider != null) { - mTimeProvider.scheduleUpdate(this); - } - } - - public void hide() { - if (!mVisible) { - return; - } - - if (mTimeProvider != null) { - mTimeProvider.cancelNotifications(this); - } - RenderingWidget renderingWidget = getRenderingWidget(); - if (renderingWidget != null) { - renderingWidget.setVisible(false); - } - mVisible = false; - } - - protected synchronized boolean addCue(Cue cue) { - mCues.add(cue); - - if (cue.mRunID != 0) { - Run run = mRunsByID.get(cue.mRunID); - if (run == null) { - run = new Run(); - mRunsByID.put(cue.mRunID, run); - run.mEndTimeMs = cue.mEndTimeMs; - } else if (run.mEndTimeMs < cue.mEndTimeMs) { - run.mEndTimeMs = cue.mEndTimeMs; - } - - // link-up cues in the same run - cue.mNextInRun = run.mFirstCue; - run.mFirstCue = cue; - } - - // if a cue is added that should be visible, need to refresh view - long nowMs = -1; - if (mTimeProvider != null) { - try { - nowMs = mTimeProvider.getCurrentTimeUs( - false /* precise */, true /* monotonic */) / 1000; - } catch (IllegalStateException e) { - // handle as it we are not playing - } - } - - if (DEBUG) Log.v(TAG, "mVisible=" + mVisible + ", " + - cue.mStartTimeMs + " <= " + nowMs + ", " + - cue.mEndTimeMs + " >= " + mLastTimeMs); - - if (mVisible && - cue.mStartTimeMs <= nowMs && - // we don't trust nowMs, so check any cue since last callback - cue.mEndTimeMs >= mLastTimeMs) { - if (mRunnable != null) { - mHandler.removeCallbacks(mRunnable); - } - final SubtitleTrack track = this; - final long thenMs = nowMs; - mRunnable = new Runnable() { - @Override - public void run() { - // even with synchronized, it is possible that we are going - // to do multiple updates as the runnable could be already - // running. - synchronized (track) { - mRunnable = null; - updateActiveCues(true, thenMs); - updateView(mActiveCues); - } - } - }; - // delay update so we don't update view on every cue. TODO why 10? - if (mHandler.postDelayed(mRunnable, 10 /* delay */)) { - if (DEBUG) Log.v(TAG, "scheduling update"); - } else { - if (DEBUG) Log.w(TAG, "failed to schedule subtitle view update"); - } - return true; - } - - if (mVisible && - cue.mEndTimeMs >= mLastTimeMs && - (cue.mStartTimeMs < mNextScheduledTimeMs || - mNextScheduledTimeMs < 0)) { - scheduleTimedEvents(); - } - - return false; - } - - public synchronized void setTimeProvider(MediaTimeProvider timeProvider) { - if (mTimeProvider == timeProvider) { - return; - } - if (mTimeProvider != null) { - mTimeProvider.cancelNotifications(this); - } - mTimeProvider = timeProvider; - if (mTimeProvider != null) { - mTimeProvider.scheduleUpdate(this); - } - } - - - static class CueList { - private static final String TAG = "CueList"; - // simplistic, inefficient implementation - private SortedMap > mCues; - public boolean DEBUG = false; - - private boolean addEvent(Cue cue, long timeMs) { - Vector cues = mCues.get(timeMs); - if (cues == null) { - cues = new Vector(2); - mCues.put(timeMs, cues); - } else if (cues.contains(cue)) { - // do not duplicate cues - return false; - } - - cues.add(cue); - return true; - } - - private void removeEvent(Cue cue, long timeMs) { - Vector cues = mCues.get(timeMs); - if (cues != null) { - cues.remove(cue); - if (cues.size() == 0) { - mCues.remove(timeMs); - } - } - } - - public void add(Cue cue) { - // ignore non-positive-duration cues - if (cue.mStartTimeMs >= cue.mEndTimeMs) - return; - - if (!addEvent(cue, cue.mStartTimeMs)) { - return; - } - - long lastTimeMs = cue.mStartTimeMs; - if (cue.mInnerTimesMs != null) { - for (long timeMs: cue.mInnerTimesMs) { - if (timeMs > lastTimeMs && timeMs < cue.mEndTimeMs) { - addEvent(cue, timeMs); - lastTimeMs = timeMs; - } - } - } - - addEvent(cue, cue.mEndTimeMs); - } - - public void remove(Cue cue) { - removeEvent(cue, cue.mStartTimeMs); - if (cue.mInnerTimesMs != null) { - for (long timeMs: cue.mInnerTimesMs) { - removeEvent(cue, timeMs); - } - } - removeEvent(cue, cue.mEndTimeMs); - } - - public Iterable> entriesBetween( - final long lastTimeMs, final long timeMs) { - return new Iterable >() { - @Override - public Iterator > iterator() { - if (DEBUG) Log.d(TAG, "slice (" + lastTimeMs + ", " + timeMs + "]="); - try { - return new EntryIterator( - mCues.subMap(lastTimeMs + 1, timeMs + 1)); - } catch(IllegalArgumentException e) { - return new EntryIterator(null); - } - } - }; - } - - public long nextTimeAfter(long timeMs) { - SortedMap> tail = null; - try { - tail = mCues.tailMap(timeMs + 1); - if (tail != null) { - return tail.firstKey(); - } else { - return -1; - } - } catch(IllegalArgumentException e) { - return -1; - } catch(NoSuchElementException e) { - return -1; - } - } - - class EntryIterator implements Iterator > { - @Override - public boolean hasNext() { - return !mDone; - } - - @Override - public Pair next() { - if (mDone) { - throw new NoSuchElementException(""); - } - mLastEntry = new Pair( - mCurrentTimeMs, mListIterator.next()); - mLastListIterator = mListIterator; - if (!mListIterator.hasNext()) { - nextKey(); - } - return mLastEntry; - } - - @Override - public void remove() { - // only allow removing end tags - if (mLastListIterator == null || - mLastEntry.second.mEndTimeMs != mLastEntry.first) { - throw new IllegalStateException(""); - } - - // remove end-cue - mLastListIterator.remove(); - mLastListIterator = null; - if (mCues.get(mLastEntry.first).size() == 0) { - mCues.remove(mLastEntry.first); - } - - // remove rest of the cues - Cue cue = mLastEntry.second; - removeEvent(cue, cue.mStartTimeMs); - if (cue.mInnerTimesMs != null) { - for (long timeMs: cue.mInnerTimesMs) { - removeEvent(cue, timeMs); - } - } - } - - public EntryIterator(SortedMap > cues) { - if (DEBUG) Log.v(TAG, cues + ""); - mRemainingCues = cues; - mLastListIterator = null; - nextKey(); - } - - private void nextKey() { - do { - try { - if (mRemainingCues == null) { - throw new NoSuchElementException(""); - } - mCurrentTimeMs = mRemainingCues.firstKey(); - mListIterator = - mRemainingCues.get(mCurrentTimeMs).iterator(); - try { - mRemainingCues = - mRemainingCues.tailMap(mCurrentTimeMs + 1); - } catch (IllegalArgumentException e) { - mRemainingCues = null; - } - mDone = false; - } catch (NoSuchElementException e) { - mDone = true; - mRemainingCues = null; - mListIterator = null; - return; - } - } while (!mListIterator.hasNext()); - } - - private long mCurrentTimeMs; - private Iterator mListIterator; - private boolean mDone; - private SortedMap > mRemainingCues; - private Iterator mLastListIterator; - private Pair mLastEntry; - } - - CueList() { - mCues = new TreeMap>(); - } - } - - public static class Cue { - public long mStartTimeMs; - public long mEndTimeMs; - public long[] mInnerTimesMs; - public long mRunID; - - public Cue mNextInRun; - - public void onTime(long timeMs) { } - } - - /** update mRunsByEndTime (with default end time) */ - protected void finishedRun(long runID) { - if (runID != 0 && runID != ~0) { - Run run = mRunsByID.get(runID); - if (run != null) { - run.storeByEndTimeMs(mRunsByEndTime); - } - } - } - - /** update mRunsByEndTime with given end time */ - public void setRunDiscardTimeMs(long runID, long timeMs) { - if (runID != 0 && runID != ~0) { - Run run = mRunsByID.get(runID); - if (run != null) { - run.mEndTimeMs = timeMs; - run.storeByEndTimeMs(mRunsByEndTime); - } - } - } - - /** whether this is a text track who fires events instead getting rendered */ - public int getTrackType() { - return getRenderingWidget() == null - ? TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT - : TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE; - } - - - private static class Run { - public Cue mFirstCue; - public Run mNextRunAtEndTimeMs; - public Run mPrevRunAtEndTimeMs; - public long mEndTimeMs = -1; - public long mRunID = 0; - private long mStoredEndTimeMs = -1; - - public void storeByEndTimeMs(LongSparseArray runsByEndTime) { - // remove old value if any - int ix = runsByEndTime.indexOfKey(mStoredEndTimeMs); - if (ix >= 0) { - if (mPrevRunAtEndTimeMs == null) { - assert(this == runsByEndTime.valueAt(ix)); - if (mNextRunAtEndTimeMs == null) { - runsByEndTime.removeAt(ix); - } else { - runsByEndTime.setValueAt(ix, mNextRunAtEndTimeMs); - } - } - removeAtEndTimeMs(); - } - - // add new value - if (mEndTimeMs >= 0) { - mPrevRunAtEndTimeMs = null; - mNextRunAtEndTimeMs = runsByEndTime.get(mEndTimeMs); - if (mNextRunAtEndTimeMs != null) { - mNextRunAtEndTimeMs.mPrevRunAtEndTimeMs = this; - } - runsByEndTime.put(mEndTimeMs, this); - mStoredEndTimeMs = mEndTimeMs; - } - } - - public void removeAtEndTimeMs() { - Run prev = mPrevRunAtEndTimeMs; - - if (mPrevRunAtEndTimeMs != null) { - mPrevRunAtEndTimeMs.mNextRunAtEndTimeMs = mNextRunAtEndTimeMs; - mPrevRunAtEndTimeMs = null; - } - if (mNextRunAtEndTimeMs != null) { - mNextRunAtEndTimeMs.mPrevRunAtEndTimeMs = prev; - mNextRunAtEndTimeMs = null; - } - } - } - - /** - * Interface for rendering subtitles onto a Canvas. - */ - public interface RenderingWidget { - /** - * Sets the widget's callback, which is used to send updates when the - * rendered data has changed. - * - * @param callback update callback - */ - public void setOnChangedListener(OnChangedListener callback); - - /** - * Sets the widget's size. - * - * @param width width in pixels - * @param height height in pixels - */ - public void setSize(int width, int height); - - /** - * Sets whether the widget should draw subtitles. - * - * @param visible true if subtitles should be drawn, false otherwise - */ - public void setVisible(boolean visible); - - /** - * Renders subtitles onto a {@link Canvas}. - * - * @param c canvas on which to render subtitles - */ - public void draw(Canvas c); - - /** - * Called when the widget is attached to a window. - */ - public void onAttachedToWindow(); - - /** - * Called when the widget is detached from a window. - */ - public void onDetachedFromWindow(); - - /** - * Callback used to send updates about changes to rendering data. - */ - public interface OnChangedListener { - /** - * Called when the rendering data has changed. - * - * @param renderingWidget the widget whose data has changed - */ - public void onChanged(RenderingWidget renderingWidget); - } - } -} diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java deleted file mode 100644 index 663b813cea..0000000000 --- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.update; - -import android.app.Notification; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.media.MediaController2; -import android.media.MediaController2.ControllerCallback; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlaylistAgent; -import android.media.MediaSession2; -import android.media.MediaSession2.ControllerInfo; -import android.media.MediaSession2.SessionCallback; -import android.media.Rating2; -import android.media.SessionCommand2; -import android.media.SessionCommandGroup2; -import android.media.SessionToken2; -import android.media.VolumeProvider2; -import android.media.update.MediaController2Provider; -import android.media.update.MediaItem2Provider; -import android.media.update.MediaMetadata2Provider; -import android.media.update.MediaPlaylistAgentProvider; -import android.media.update.MediaSession2Provider; -import android.media.update.MediaSession2Provider.BuilderBaseProvider; -import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider; -import android.media.update.SessionToken2Provider; -import android.media.update.StaticProvider; -import android.media.update.VolumeProvider2Provider; -import android.os.Bundle; -import android.os.IInterface; - -import com.android.media.IMediaController2; -import com.android.media.MediaController2Impl; -import com.android.media.MediaItem2Impl; -import com.android.media.MediaMetadata2Impl; -import com.android.media.MediaPlaylistAgentImpl; -import com.android.media.MediaSession2Impl; -import com.android.media.Rating2Impl; -import com.android.media.SessionToken2Impl; -import com.android.media.VolumeProvider2Impl; - -import java.util.concurrent.Executor; - -public final class ApiFactory implements StaticProvider { - private ApiFactory() { } - - public static StaticProvider initialize(ApplicationInfo updatableInfo) { - ApiHelper.initialize(updatableInfo); - return new ApiFactory(); - } - - @Override - public MediaController2Provider createMediaController2( - Context context, MediaController2 instance, SessionToken2 token, - Executor executor, ControllerCallback callback) { - return new MediaController2Impl(context, instance, token, executor, callback); - } - - @Override - public MediaSession2Provider.CommandProvider createMediaSession2Command( - SessionCommand2 instance, int commandCode, String action, Bundle extra) { - if (action == null && extra == null) { - return new MediaSession2Impl.CommandImpl(instance, commandCode); - } - return new MediaSession2Impl.CommandImpl(instance, action, extra); - } - - @Override - public SessionCommand2 fromBundle_MediaSession2Command(Bundle command) { - return MediaSession2Impl.CommandImpl.fromBundle_impl(command); - } - - @Override - public MediaSession2Provider.CommandGroupProvider createMediaSession2CommandGroup( - SessionCommandGroup2 instance, SessionCommandGroup2 other) { - return new MediaSession2Impl.CommandGroupImpl(instance, - (other == null) ? null : other.getProvider()); - } - - @Override - public SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle commands) { - return MediaSession2Impl.CommandGroupImpl.fromBundle_impl(commands); - } - - @Override - public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfo( - Context context, ControllerInfo instance, int uid, int pid, String packageName, - IInterface callback) { - return new MediaSession2Impl.ControllerInfoImpl(context, - instance, uid, pid, packageName, (IMediaController2) callback); - } - - @Override - public BuilderProvider createMediaSession2CommandButtonBuilder( - MediaSession2.CommandButton.Builder instance) { - return new MediaSession2Impl.CommandButtonImpl.BuilderImpl(instance); - } - - public BuilderBaseProvider createMediaSession2Builder( - Context context, MediaSession2.Builder instance) { - return new MediaSession2Impl.BuilderImpl(context, instance); - } - - @Override - public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance, - String packageName, String serviceName, int uid) { - return new SessionToken2Impl(context, instance, packageName, serviceName, uid); - } - - @Override - public SessionToken2 fromBundle_SessionToken2(Bundle bundle) { - return SessionToken2Impl.fromBundle_impl(bundle); - } - - @Override - public MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance, - int flags) { - return new MediaItem2Impl.BuilderImpl(instance, flags); - } - - @Override - public MediaItem2 fromBundle_MediaItem2(Bundle bundle) { - return MediaItem2Impl.fromBundle_impl(bundle); - } - - @Override - public VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType, - int maxVolume, int currentVolume) { - return new VolumeProvider2Impl(instance, controlType, maxVolume, currentVolume); - } - - @Override - public MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle) { - return MediaMetadata2Impl.fromBundle_impl(bundle); - } - - @Override - public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder( - MediaMetadata2.Builder instance) { - return new MediaMetadata2Impl.BuilderImpl(instance); - } - - @Override - public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder( - MediaMetadata2.Builder instance, MediaMetadata2 source) { - return new MediaMetadata2Impl.BuilderImpl(instance, source); - } - - @Override - public Rating2 fromBundle_Rating2(Bundle bundle) { - return Rating2Impl.fromBundle_impl(bundle); - } - - @Override - public Rating2 newUnratedRating_Rating2(int ratingStyle) { - return Rating2Impl.newUnratedRating_impl(ratingStyle); - } - - @Override - public Rating2 newHeartRating_Rating2(boolean hasHeart) { - return Rating2Impl.newHeartRating_impl(hasHeart); - } - - @Override - public Rating2 newThumbRating_Rating2(boolean thumbIsUp) { - return Rating2Impl.newThumbRating_impl(thumbIsUp); - } - - @Override - public Rating2 newStarRating_Rating2(int starRatingStyle, float starRating) { - return Rating2Impl.newStarRating_impl(starRatingStyle, starRating); - } - - @Override - public Rating2 newPercentageRating_Rating2(float percent) { - return Rating2Impl.newPercentageRating_impl(percent); - } - - @Override - public MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance) { - return new MediaPlaylistAgentImpl(instance); - } -} diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java deleted file mode 100644 index f918ed311a..0000000000 --- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media.update; - -import android.content.pm.ApplicationInfo; - -public final class ApiHelper { - private static ApplicationInfo sUpdatableInfo; - - private ApiHelper() { } - - static void initialize(ApplicationInfo updatableInfo) { - if (sUpdatableInfo != null) { - throw new IllegalStateException("initialize should only be called once"); - } - - sUpdatableInfo = updatableInfo; - } -} diff --git a/packages/MediaComponents/src/com/android/widget/SubtitleView.java b/packages/MediaComponents/src/com/android/widget/SubtitleView.java deleted file mode 100644 index db0ae331c9..0000000000 --- a/packages/MediaComponents/src/com/android/widget/SubtitleView.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.content.Context; -import android.graphics.Canvas; -import android.os.Looper; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -import androidx.annotation.Nullable; - -import com.android.media.subtitle.SubtitleController.Anchor; -import com.android.media.subtitle.SubtitleTrack.RenderingWidget; - -class SubtitleView extends FrameLayout implements Anchor { - private static final String TAG = "SubtitleView"; - - private RenderingWidget mSubtitleWidget; - private RenderingWidget.OnChangedListener mSubtitlesChangedListener; - - public SubtitleView(Context context) { - this(context, null); - } - - public SubtitleView(Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - public SubtitleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public SubtitleView( - Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - public void setSubtitleWidget(RenderingWidget subtitleWidget) { - if (mSubtitleWidget == subtitleWidget) { - return; - } - - final boolean attachedToWindow = isAttachedToWindow(); - if (mSubtitleWidget != null) { - if (attachedToWindow) { - mSubtitleWidget.onDetachedFromWindow(); - } - - mSubtitleWidget.setOnChangedListener(null); - } - mSubtitleWidget = subtitleWidget; - - if (subtitleWidget != null) { - if (mSubtitlesChangedListener == null) { - mSubtitlesChangedListener = new RenderingWidget.OnChangedListener() { - @Override - public void onChanged(RenderingWidget renderingWidget) { - invalidate(); - } - }; - } - - setWillNotDraw(false); - subtitleWidget.setOnChangedListener(mSubtitlesChangedListener); - - if (attachedToWindow) { - subtitleWidget.onAttachedToWindow(); - requestLayout(); - } - } else { - setWillNotDraw(true); - } - - invalidate(); - } - - @Override - public Looper getSubtitleLooper() { - return Looper.getMainLooper(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mSubtitleWidget != null) { - mSubtitleWidget.onAttachedToWindow(); - } - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - if (mSubtitleWidget != null) { - mSubtitleWidget.onDetachedFromWindow(); - } - } - - @Override - public void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (mSubtitleWidget != null) { - final int width = getWidth() - getPaddingLeft() - getPaddingRight(); - final int height = getHeight() - getPaddingTop() - getPaddingBottom(); - - mSubtitleWidget.setSize(width, height); - } - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - - if (mSubtitleWidget != null) { - final int saveCount = canvas.save(); - canvas.translate(getPaddingLeft(), getPaddingTop()); - mSubtitleWidget.draw(canvas); - canvas.restoreToCount(saveCount); - } - } - - @Override - public CharSequence getAccessibilityClassName() { - return SubtitleView.class.getName(); - } -} diff --git a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java b/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java deleted file mode 100644 index 854d47e9c1..0000000000 --- a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.widget; - -import android.annotation.NonNull; -import android.media.MediaPlayer2; -import android.view.View; - -interface VideoViewInterface { - /** - * Assigns the view's surface to the given MediaPlayer2 instance. - * - * @param mp MediaPlayer2 - * @return true if the surface is successfully assigned, false if not. It will fail to assign - * if any of MediaPlayer2 or surface is unavailable. - */ - boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp); - void setSurfaceListener(SurfaceListener l); - int getViewType(); - void setMediaPlayer(MediaPlayer2 mp); - - /** - * Takes over oldView. It means that the MediaPlayer2 will start rendering on this view. - * The visibility of oldView will be set as {@link View.GONE}. If the view doesn't have a - * MediaPlayer2 instance or its surface is not available, the actual execution is deferred until - * a MediaPlayer2 instance is set by {@link #setMediaPlayer} or its surface becomes available. - * {@link SurfaceListener.onSurfaceTakeOverDone} will be called when the actual execution is - * done. - * - * @param oldView The view that MediaPlayer2 is currently rendering on. - */ - void takeOver(@NonNull VideoViewInterface oldView); - - /** - * Indicates if the view's surface is available. - * - * @return true if the surface is available. - */ - boolean hasAvailableSurface(); - - /** - * An instance of VideoViewInterface calls these surface notification methods accordingly if - * a listener has been registered via {@link #setSurfaceListener(SurfaceListener)}. - */ - interface SurfaceListener { - void onSurfaceCreated(View view, int width, int height); - void onSurfaceDestroyed(View view); - void onSurfaceChanged(View view, int width, int height); - void onSurfaceTakeOverDone(VideoViewInterface view); - } -} diff --git a/packages/MediaComponents/tests/Android.mk b/packages/MediaComponents/tests/Android.mk deleted file mode 100644 index dddfd2a09d..0000000000 --- a/packages/MediaComponents/tests/Android.mk +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android.test.runner.stubs \ - android.test.base.stubs \ - mockito-target-minus-junit4 \ - junit - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := MediaComponentsTest - -LOCAL_INSTRUMENTATION_FOR := MediaComponents - -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) diff --git a/packages/MediaComponents/tests/AndroidManifest.xml b/packages/MediaComponents/tests/AndroidManifest.xml deleted file mode 100644 index 72552657eb..0000000000 --- a/packages/MediaComponents/tests/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - diff --git a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java deleted file mode 100644 index 08076009a0..0000000000 --- a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.media; - -import static org.mockito.Mockito.*; - -import android.content.Context; -import android.media.AudioAttributes; -import android.media.DataSourceDesc; -import android.media.UriDataSourceDesc; -import android.media.MediaItem2; -import android.media.MediaMetadata2; -import android.media.MediaPlayerBase; -import android.media.MediaPlayerBase.PlayerEventCallback; -import android.media.MediaPlaylistAgent; -import android.media.MediaSession2; -import android.media.MediaSession2.OnDataSourceMissingHelper; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.test.AndroidTestCase; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Matchers; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * Tests {@link SessionPlaylistAgent}. - */ -public class SessionPlaylistAgentTest extends AndroidTestCase { - private static final String TAG = "SessionPlaylistAgentTest"; - private static final int WAIT_TIME_MS = 1000; - private static final int INVALID_REPEAT_MODE = -100; - private static final int INVALID_SHUFFLE_MODE = -100; - - private Handler mHandler; - private Executor mHandlerExecutor; - - private Object mWaitLock = new Object(); - private Context mContext; - private MediaSession2Impl mSessionImpl; - private MediaPlayerBase mPlayer; - private PlayerEventCallback mPlayerEventCallback; - private SessionPlaylistAgent mAgent; - private OnDataSourceMissingHelper mDataSourceHelper; - private MyPlaylistEventCallback mEventCallback; - - public class MyPlaylistEventCallback extends MediaPlaylistAgent.PlaylistEventCallback { - boolean onPlaylistChangedCalled; - boolean onPlaylistMetadataChangedCalled; - boolean onRepeatModeChangedCalled; - boolean onShuffleModeChangedCalled; - - private Object mWaitLock; - - public MyPlaylistEventCallback(Object waitLock) { - mWaitLock = waitLock; - } - - public void clear() { - onPlaylistChangedCalled = false; - onPlaylistMetadataChangedCalled = false; - onRepeatModeChangedCalled = false; - onShuffleModeChangedCalled = false; - } - - public void onPlaylistChanged(MediaPlaylistAgent playlistAgent, List list, - MediaMetadata2 metadata) { - synchronized (mWaitLock) { - onPlaylistChangedCalled = true; - mWaitLock.notify(); - } - } - - public void onPlaylistMetadataChanged(MediaPlaylistAgent playlistAgent, - MediaMetadata2 metadata) { - synchronized (mWaitLock) { - onPlaylistMetadataChangedCalled = true; - mWaitLock.notify(); - } - } - - public void onRepeatModeChanged(MediaPlaylistAgent playlistAgent, int repeatMode) { - synchronized (mWaitLock) { - onRepeatModeChangedCalled = true; - mWaitLock.notify(); - } - } - - public void onShuffleModeChanged(MediaPlaylistAgent playlistAgent, int shuffleMode) { - synchronized (mWaitLock) { - onShuffleModeChangedCalled = true; - mWaitLock.notify(); - } - } - } - - public class MyDataSourceHelper implements OnDataSourceMissingHelper { - @Override - public DataSourceDesc onDataSourceMissing(MediaSession2 session, MediaItem2 item) { - if (item.getMediaId().contains("WITHOUT_DSD")) { - return null; - } - return new UriDataSourceDesc.Builder() - .setDataSource(getContext(), Uri.parse("dsd://test")) - .setMediaId(item.getMediaId()) - .build(); - } - } - - public class MockPlayer extends MediaPlayerBase { - @Override - public void play() { - } - - @Override - public void prepare() { - } - - @Override - public void pause() { - } - - @Override - public void reset() { - } - - @Override - public void skipToNext() { - } - - @Override - public void seekTo(long pos) { - } - - @Override - public int getPlayerState() { - return 0; - } - - @Override - public int getBufferingState() { - return 0; - } - - @Override - public void setAudioAttributes(AudioAttributes attributes) { - } - - @Override - public AudioAttributes getAudioAttributes() { - return null; - } - - @Override - public void setDataSource(DataSourceDesc dsd) { - } - - @Override - public void setNextDataSource(DataSourceDesc dsd) { - } - - @Override - public void setNextDataSources(List dsds) { - } - - @Override - public DataSourceDesc getCurrentDataSource() { - return null; - } - - @Override - public void loopCurrent(boolean loop) { - } - - @Override - public void setPlaybackSpeed(float speed) { - } - - @Override - public void setPlayerVolume(float volume) { - } - - @Override - public float getPlayerVolume() { - return 0; - } - - @Override - public void registerPlayerEventCallback(Executor e, PlayerEventCallback cb) { - } - - @Override - public void unregisterPlayerEventCallback(PlayerEventCallback cb) { - } - - @Override - public void close() throws Exception { - } - } - - @Before - public void setUp() throws Exception { - mContext = getContext(); - // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 - // Dexmaker is used by mockito. - System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath()); - - HandlerThread handlerThread = new HandlerThread("SessionPlaylistAgent"); - handlerThread.start(); - mHandler = new Handler(handlerThread.getLooper()); - mHandlerExecutor = (runnable) -> { - mHandler.post(runnable); - }; - - mPlayer = mock(MockPlayer.class); - doAnswer(invocation -> { - Object[] args = invocation.getArguments(); - mPlayerEventCallback = (PlayerEventCallback) args[1]; - return null; - }).when(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any()); - - mSessionImpl = mock(MediaSession2Impl.class); - mDataSourceHelper = new MyDataSourceHelper(); - mAgent = new SessionPlaylistAgent(mSessionImpl, mPlayer); - mAgent.setOnDataSourceMissingHelper(mDataSourceHelper); - mEventCallback = new MyPlaylistEventCallback(mWaitLock); - mAgent.registerPlaylistEventCallback(mHandlerExecutor, mEventCallback); - } - - @After - public void tearDown() throws Exception { - mHandler.getLooper().quitSafely(); - mHandler = null; - mHandlerExecutor = null; - } - - @Test - public void testSetAndGetShuflleMode() throws Exception { - int shuffleMode = mAgent.getShuffleMode(); - if (shuffleMode != MediaPlaylistAgent.SHUFFLE_MODE_NONE) { - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_NONE); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onShuffleModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_NONE, mAgent.getShuffleMode()); - } - - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_ALL); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onShuffleModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_ALL, mAgent.getShuffleMode()); - - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setShuffleMode(MediaPlaylistAgent.SHUFFLE_MODE_GROUP); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onShuffleModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_GROUP, mAgent.getShuffleMode()); - - // INVALID_SHUFFLE_MODE will not change the shuffle mode. - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setShuffleMode(INVALID_SHUFFLE_MODE); - mWaitLock.wait(WAIT_TIME_MS); - assertFalse(mEventCallback.onShuffleModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.SHUFFLE_MODE_GROUP, mAgent.getShuffleMode()); - } - - @Test - public void testSetAndGetRepeatMode() throws Exception { - int repeatMode = mAgent.getRepeatMode(); - if (repeatMode != MediaPlaylistAgent.REPEAT_MODE_NONE) { - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onRepeatModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.REPEAT_MODE_NONE, mAgent.getRepeatMode()); - } - - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ONE); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onRepeatModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.REPEAT_MODE_ONE, mAgent.getRepeatMode()); - - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onRepeatModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.REPEAT_MODE_ALL, mAgent.getRepeatMode()); - - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_GROUP); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onRepeatModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.REPEAT_MODE_GROUP, mAgent.getRepeatMode()); - - // INVALID_SHUFFLE_MODE will not change the shuffle mode. - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setRepeatMode(INVALID_REPEAT_MODE); - mWaitLock.wait(WAIT_TIME_MS); - assertFalse(mEventCallback.onRepeatModeChangedCalled); - } - assertEquals(MediaPlaylistAgent.REPEAT_MODE_GROUP, mAgent.getRepeatMode()); - } - - @Test - public void testSetPlaylist() throws Exception { - int listSize = 10; - createAndSetPlaylist(10); - assertEquals(listSize, mAgent.getPlaylist().size()); - assertEquals(0, mAgent.getCurShuffledIndex()); - } - - @Test - public void testSkipItems() throws Exception { - int listSize = 5; - List playlist = createAndSetPlaylist(listSize); - - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE); - // Test skipToPlaylistItem - for (int i = listSize - 1; i >= 0; --i) { - mAgent.skipToPlaylistItem(playlist.get(i)); - assertEquals(i, mAgent.getCurShuffledIndex()); - } - - // Test skipToNextItem - // curPlayPos = 0 - for (int curPlayPos = 0; curPlayPos < listSize - 1; ++curPlayPos) { - mAgent.skipToNextItem(); - assertEquals(curPlayPos + 1, mAgent.getCurShuffledIndex()); - } - mAgent.skipToNextItem(); - assertEquals(listSize - 1, mAgent.getCurShuffledIndex()); - - // Test skipToPrevious - // curPlayPos = listSize - 1 - for (int curPlayPos = listSize - 1; curPlayPos > 0; --curPlayPos) { - mAgent.skipToPreviousItem(); - assertEquals(curPlayPos - 1, mAgent.getCurShuffledIndex()); - } - mAgent.skipToPreviousItem(); - assertEquals(0, mAgent.getCurShuffledIndex()); - - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL); - // Test skipToPrevious with repeat mode all - // curPlayPos = 0 - mAgent.skipToPreviousItem(); - assertEquals(listSize - 1, mAgent.getCurShuffledIndex()); - - // Test skipToNext with repeat mode all - // curPlayPos = listSize - 1 - mAgent.skipToNextItem(); - assertEquals(0, mAgent.getCurShuffledIndex()); - - mAgent.skipToPreviousItem(); - // curPlayPos = listSize - 1, nextPlayPos = 0 - // Test next play pos after setting repeat mode none. - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_NONE); - assertEquals(listSize - 1, mAgent.getCurShuffledIndex()); - } - - @Test - public void testEditPlaylist() throws Exception { - int listSize = 5; - List playlist = createAndSetPlaylist(listSize); - - // Test add item: [0 (cur), 1, 2, 3, 4] -> [0 (cur), 1, 5, 2, 3, 4] - mEventCallback.clear(); - MediaItem2 item_5 = generateMediaItem(5); - synchronized (mWaitLock) { - playlist.add(2, item_5); - mAgent.addPlaylistItem(2, item_5); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - - mEventCallback.clear(); - // Move current: [0 (cur), 1, 5, 2, 3, 4] -> [0, 1, 5 (cur), 2, 3, 4] - mAgent.skipToPlaylistItem(item_5); - // Remove current item: [0, 1, 5 (cur), 2, 3, 4] -> [0, 1, 2 (cur), 3, 4] - synchronized (mWaitLock) { - playlist.remove(item_5); - mAgent.removePlaylistItem(item_5); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(2, mAgent.getCurShuffledIndex()); - - // Remove previous item: [0, 1, 2 (cur), 3, 4] -> [0, 2 (cur), 3, 4] - mEventCallback.clear(); - MediaItem2 previousItem = playlist.get(1); - synchronized (mWaitLock) { - playlist.remove(previousItem); - mAgent.removePlaylistItem(previousItem); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Remove next item: [0, 2 (cur), 3, 4] -> [0, 2 (cur), 4] - mEventCallback.clear(); - MediaItem2 nextItem = playlist.get(2); - synchronized (mWaitLock) { - playlist.remove(nextItem); - mAgent.removePlaylistItem(nextItem); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Replace item: [0, 2 (cur), 4] -> [0, 2 (cur), 5] - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.set(2, item_5); - mAgent.replacePlaylistItem(2, item_5); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Move last and remove the last item: [0, 2 (cur), 5] -> [0, 2, 5 (cur)] -> [0, 2 (cur)] - MediaItem2 lastItem = playlist.get(1); - mAgent.skipToPlaylistItem(lastItem); - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.remove(lastItem); - mAgent.removePlaylistItem(lastItem); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Remove all items - for (int i = playlist.size() - 1; i >= 0; --i) { - MediaItem2 item = playlist.get(i); - mAgent.skipToPlaylistItem(item); - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.remove(item); - mAgent.removePlaylistItem(item); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - } - assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex()); - } - - - @Test - public void testPlaylistWithInvalidItem() throws Exception { - int listSize = 2; - List playlist = createAndSetPlaylist(listSize); - - // Add item: [0 (cur), 1] -> [0 (cur), 3 (no_dsd), 1] - mEventCallback.clear(); - MediaItem2 invalidItem2 = generateMediaItemWithoutDataSourceDesc(2); - synchronized (mWaitLock) { - playlist.add(1, invalidItem2); - mAgent.addPlaylistItem(1, invalidItem2); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(0, mAgent.getCurShuffledIndex()); - - // Test skip to next item: [0 (cur), 2 (no_dsd), 1] -> [0, 2 (no_dsd), 1 (cur)] - mAgent.skipToNextItem(); - assertEquals(2, mAgent.getCurShuffledIndex()); - - // Test skip to previous item: [0, 2 (no_dsd), 1 (cur)] -> [0 (cur), 2 (no_dsd), 1] - mAgent.skipToPreviousItem(); - assertEquals(0, mAgent.getCurShuffledIndex()); - - // Remove current item: [0 (cur), 2 (no_dsd), 1] -> [2 (no_dsd), 1 (cur)] - mEventCallback.clear(); - MediaItem2 item = playlist.get(0); - synchronized (mWaitLock) { - playlist.remove(item); - mAgent.removePlaylistItem(item); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Remove current item: [2 (no_dsd), 1 (cur)] -> [2 (no_dsd)] - mEventCallback.clear(); - item = playlist.get(1); - synchronized (mWaitLock) { - playlist.remove(item); - mAgent.removePlaylistItem(item); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex()); - - // Add invalid item: [2 (no_dsd)] -> [0 (no_dsd), 2 (no_dsd)] - MediaItem2 invalidItem0 = generateMediaItemWithoutDataSourceDesc(0); - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.add(0, invalidItem0); - mAgent.addPlaylistItem(0, invalidItem0); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(SessionPlaylistAgent.NO_VALID_ITEMS, mAgent.getCurShuffledIndex()); - - // Add valid item: [0 (no_dsd), 2 (no_dsd)] -> [0 (no_dsd), 1, 2 (no_dsd)] - MediaItem2 invalidItem1 = generateMediaItem(1); - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.add(1, invalidItem1); - mAgent.addPlaylistItem(1, invalidItem1); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(1, mAgent.getCurShuffledIndex()); - - // Replace the valid item with an invalid item: - // [0 (no_dsd), 1 (cur), 2 (no_dsd)] -> [0 (no_dsd), 3 (no_dsd), 2 (no_dsd)] - MediaItem2 invalidItem3 = generateMediaItemWithoutDataSourceDesc(3); - mEventCallback.clear(); - synchronized (mWaitLock) { - playlist.set(1, invalidItem3); - mAgent.replacePlaylistItem(1, invalidItem3); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - assertPlaylistEquals(playlist, mAgent.getPlaylist()); - assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex()); - } - - @Test - public void testPlaylistAfterOnCurrentDataSourceChanged() throws Exception { - int listSize = 2; - verify(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any()); - - createAndSetPlaylist(listSize); - assertEquals(0, mAgent.getCurShuffledIndex()); - - mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null); - assertEquals(1, mAgent.getCurShuffledIndex()); - mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null); - assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex()); - - mAgent.skipToNextItem(); - assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex()); - - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ONE); - assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex()); - - mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL); - assertEquals(0, mAgent.getCurShuffledIndex()); - mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null); - assertEquals(1, mAgent.getCurShuffledIndex()); - mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null); - assertEquals(0, mAgent.getCurShuffledIndex()); - } - - private List createAndSetPlaylist(int listSize) throws Exception { - List items = new ArrayList<>(); - for (int i = 0; i < listSize; ++i) { - items.add(generateMediaItem(i)); - } - mEventCallback.clear(); - synchronized (mWaitLock) { - mAgent.setPlaylist(items, null); - mWaitLock.wait(WAIT_TIME_MS); - assertTrue(mEventCallback.onPlaylistChangedCalled); - } - return items; - } - - private void assertPlaylistEquals(List expected, List actual) { - if (expected == actual) { - return; - } - assertTrue(expected != null && actual != null); - assertEquals(expected.size(), actual.size()); - for (int i = 0; i < expected.size(); ++i) { - assertTrue(expected.get(i).equals(actual.get(i))); - } - } - - private MediaItem2 generateMediaItemWithoutDataSourceDesc(int key) { - return new MediaItem2.Builder(0) - .setMediaId("TEST_MEDIA_ID_WITHOUT_DSD_" + key) - .build(); - } - - private MediaItem2 generateMediaItem(int key) { - return new MediaItem2.Builder(0) - .setMediaId("TEST_MEDIA_ID_" + key) - .build(); - } -} -- GitLab From 4515129c59206406322d2efb4581cf0dcd66096d Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 20 Nov 2018 16:37:29 -0800 Subject: [PATCH 0500/1530] audiopolicy: use an exact match for MMAP input AudioFlinger was returning a stereo input if mono was requested but not supported. But AAudio did not know it was stereo so the data was corrupted. So if MMAP input is requested, do an exact match in isCompatibleProfile(). Bug: 119518992 Test: see bug for repo steps Test: adb shell aaudio_loopback -tm -pl -Pl -x -X -C1 Change-Id: I925efecca9c99b8a2b7bde3826da4d9e9e663ee1 --- .../managerdefinitions/src/IOProfile.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp index 8660624bfe..3788244783 100644 --- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "APM::IOProfile" //#define LOG_NDEBUG 0 +#include #include "IOProfile.h" #include "HwModule.h" #include "AudioGain.h" @@ -66,19 +67,23 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device, audio_format_t myUpdatedFormat = format; audio_channel_mask_t myUpdatedChannelMask = channelMask; uint32_t myUpdatedSamplingRate = samplingRate; + const struct audio_port_config config = { + .config_mask = AUDIO_PORT_CONFIG_ALL & ~AUDIO_PORT_CONFIG_GAIN, + .sample_rate = samplingRate, + .channel_mask = channelMask, + .format = format, + }; if (isRecordThread) { - if (checkCompatibleAudioProfile( + if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) { + if (checkExactAudioProfile(&config) != NO_ERROR) { + return false; + } + } else if (checkCompatibleAudioProfile( myUpdatedSamplingRate, myUpdatedChannelMask, myUpdatedFormat) != NO_ERROR) { return false; } } else { - const struct audio_port_config config = { - .config_mask = AUDIO_PORT_CONFIG_ALL & ~AUDIO_PORT_CONFIG_GAIN, - .sample_rate = samplingRate, - .channel_mask = channelMask, - .format = format, - }; if (checkExactAudioProfile(&config) != NO_ERROR) { return false; } -- GitLab From afc436f6c0eb2be3697482f5c5561d1e7b3420ae Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Tue, 20 Nov 2018 19:00:52 -0800 Subject: [PATCH 0501/1530] JAudioTrack: replace MediaPlayer2Impl with MediaPlayer2 Test: cts Bug: 112549970 Change-Id: I29205dbab0bb09187d0c7c2b5c8ca2d5c3853f32 --- media/libmediaplayer2/JAudioTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index 3d6879e532..768e59e9a3 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -755,7 +755,7 @@ jobject JAudioTrack::createVolumeShaperOperationObj( jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) { JNIEnv *env = JavaVMHelper::getJNIEnv(); - jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback"); + jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2$StreamEventCallback"); jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "", "(JJJ)V"); jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user); return jCallbackObj; -- GitLab From 03e1a19e0ba734994f9565b0f7dc588e4d7ae804 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Wed, 21 Nov 2018 11:52:43 +0900 Subject: [PATCH 0502/1530] Replace hidden AudioManager.FLAG_FROM_KEY usage This CL copies the hidden AudioManager.FLAG_FROM_KEY usage into MediaUtils, and replace the usages with it. Bug: 119790339 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: I1a5ba5bc06e10fea9bf5fca0f88c8ca4f6ed2a7d --- .../MediaComponents/apex/java/android/media/MediaUtils.java | 6 ++++++ .../apex/java/android/media/session/MediaController.java | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/MediaUtils.java b/packages/MediaComponents/apex/java/android/media/MediaUtils.java index 282c2185b3..0f0cb002b6 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaUtils.java +++ b/packages/MediaComponents/apex/java/android/media/MediaUtils.java @@ -7,6 +7,12 @@ import android.view.KeyEvent; */ public class MediaUtils { + /** + * Adjusting the volume due to a hardware key press. + * (Copied version of hidden AudioManager.FLAG_FROM_KEY) + */ + public static final int AUDIO_MANAGER_FLAG_FROM_KEY = 1 << 12; + // Keep sync with KeyEvent#isMediaKey(). public static boolean isMediaKey(int keyCode) { switch (keyCode) { diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 1a2a5281ee..fab57e394d 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -197,9 +197,8 @@ public final class MediaController { } case KeyEvent.ACTION_UP: { - //TODO(b/119790339): Resolve hidden API usage. AudioManager.FLAG_FROM_KEY - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; - //| AudioManager.FLAG_FROM_KEY; + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE + | MediaUtils.AUDIO_MANAGER_FLAG_FROM_KEY; try { mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0, flags); -- GitLab From aa1b815ead94f8c393ec62b7ed1842d4f788418d Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Wed, 21 Nov 2018 10:02:25 +0900 Subject: [PATCH 0503/1530] fix indentation for coding rule follow the coding convention(line width, indentation) Test: m -j Change-Id: I617392e88bb772909e45f3a77809c9074b74cff8 --- services/camera/libcameraservice/CameraService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 2f50e3abe2..3951479f65 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1592,7 +1592,8 @@ Status CameraService::notifySystemEvent(int32_t eventId, ALOGE("Permission Denial: cannot send updates to camera service about system" " events from pid=%d, uid=%d", pid, uid); return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED, - "No permission to send updates to camera service about system events from pid=%d, uid=%d", pid, uid); + "No permission to send updates to camera service about system events" + " from pid=%d, uid=%d", pid, uid); } } -- GitLab From 61da4d355daa25df0ef4cd111b186e089773b583 Mon Sep 17 00:00:00 2001 From: Pin-chih Lin Date: Wed, 21 Nov 2018 21:14:58 +0800 Subject: [PATCH 0504/1530] SimpleDecodingSource: add max retry times for while loop Add a maximum retry times to break the loop to avoid infinite loop while output buffer stack is stuck. Bug: 112677333 Test: build and run stagefright cmdtool Change-Id: Ie4f68075f5297acf67eda22f2f822a7878a18c66 --- media/libstagefright/SimpleDecodingSource.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp index 404c53718f..babdc7a669 100644 --- a/media/libstagefright/SimpleDecodingSource.cpp +++ b/media/libstagefright/SimpleDecodingSource.cpp @@ -37,6 +37,7 @@ using namespace android; const int64_t kTimeoutWaitForOutputUs = 500000; // 0.5 seconds const int64_t kTimeoutWaitForInputUs = 5000; // 5 milliseconds +const int kTimeoutMaxRetries = 20; //static sp SimpleDecodingSource::Create( @@ -242,7 +243,7 @@ status_t SimpleDecodingSource::doRead( return ERROR_END_OF_STREAM; } - for (int retries = 0; ++retries; ) { + for (int retries = 0; retries < kTimeoutMaxRetries; ++retries) { // If we fill all available input buffers, we should expect that // the codec produces at least one output buffer. Also, the codec // should produce an output buffer in at most 1 seconds. Retry a -- GitLab From 0248a2a3ae176e4f306dbf3d869792bb93312ed5 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 21 Nov 2018 10:05:57 -0800 Subject: [PATCH 0505/1530] Fix double track thread initialization Bug: 119870459 Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.AudioTrackOffloadTest Change-Id: I15f314ee5696ead90a09991bfe10e1ae3ff013e0 --- media/libaudioclient/AudioTrack.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 96fccaee88..df9aea67eb 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -811,12 +811,9 @@ void AudioTrack::stop() if (!isOffloaded_l()) { t->pause(); } else if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) { - const sp t = mAudioTrackThread; - if (t != 0) { - // causes wake up of the playback thread, that will callback the client for - // EVENT_STREAM_END in processAudioBuffer() - t->wake(); - } + // causes wake up of the playback thread, that will callback the client for + // EVENT_STREAM_END in processAudioBuffer() + t->wake(); } } else { setpriority(PRIO_PROCESS, 0, mPreviousPriority); -- GitLab From 32b4f494fb4c032a8c9e8f3b8c9ed1d7ccd8ef22 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 14 Nov 2018 18:38:21 -0800 Subject: [PATCH 0506/1530] libmediautils: move AImageReader_getHGBPFromHandle from libmediandk. This allows us to remove camerserver's dependency on libmediandk, thereby reducing its vss increase (brought about due to many of libmediandk's dependencies, which cameraserver does not need) Bug: 119582723 Test: mm -j64 Test: showmap vss before change: 50628 Test: showmap vss after change: 31256 Test: AImageReaderWindowHandleTest Change-Id: I8dec6430adda538cb293cdd0d087b9ae1767b900 Signed-off-by: Jayant Chowdhary --- media/ndk/Android.bp | 1 + media/ndk/NdkImageReader.cpp | 25 --------- media/ndk/NdkImageReaderPriv.h | 4 -- .../tests/AImageReaderWindowHandleTest.cpp | 3 ++ media/utils/AImageReaderUtils.cpp | 52 +++++++++++++++++++ media/utils/Android.bp | 4 ++ .../include/mediautils/AImageReaderUtils.h | 35 +++++++++++++ 7 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 media/utils/AImageReaderUtils.cpp create mode 100644 media/utils/include/mediautils/AImageReaderUtils.h diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index b21a1bb13e..541ba3eeee 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -161,6 +161,7 @@ cc_test { shared_libs: [ "libbinder", "libmediandk", + "libmediautils", "libnativewindow", "libgui", "libutils", diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index 1adecb969b..b86ab424e0 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -647,31 +647,6 @@ static native_handle_t *convertHalTokenToNativeHandle( return nh; } -static sp convertNativeHandleToHGBP ( - const native_handle_t *handle) { - // Read the size of the halToken vec - hidl_vec halToken; - halToken.setToExternal( - reinterpret_cast(const_cast(&(handle->data[1]))), - handle->data[0]); - sp hgbp = - HGraphicBufferProducer::castFrom(retrieveHalInterface(halToken)); - return hgbp; -} - -EXPORT -sp AImageReader_getHGBPFromHandle( - const native_handle_t *handle) { - if (handle == nullptr) { - return nullptr; - } - if (handle->numFds != 0 || - handle->numInts < ceil(sizeof(size_t) / sizeof(int))) { - return nullptr; - } - return convertNativeHandleToHGBP(handle); -} - EXPORT media_status_t AImageReader_new( int32_t width, int32_t height, int32_t format, int32_t maxImages, diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h index d9ddfd9302..78152d2946 100644 --- a/media/ndk/NdkImageReaderPriv.h +++ b/media/ndk/NdkImageReaderPriv.h @@ -169,8 +169,4 @@ struct AImageReader : public RefBase { Mutex mLock; }; -// Retrieves HGraphicBufferProducer corresponding to the native_handle_t -// provided (this native handle MUST have been obtained by AImageReader_getWindowNativeHandle()). -sp AImageReader_getHGBPFromHandle(const native_handle_t *handle); - #endif // _NDK_IMAGE_READER_PRIV_H diff --git a/media/ndk/tests/AImageReaderWindowHandleTest.cpp b/media/ndk/tests/AImageReaderWindowHandleTest.cpp index 3b5e3585b9..ef0ff675a5 100644 --- a/media/ndk/tests/AImageReaderWindowHandleTest.cpp +++ b/media/ndk/tests/AImageReaderWindowHandleTest.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ namespace android { using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer; +using aimg::AImageReader_getHGBPFromHandle; + typedef IGraphicBufferProducer::QueueBufferInput QueueBufferInput; typedef IGraphicBufferProducer::QueueBufferOutput QueueBufferOutput; diff --git a/media/utils/AImageReaderUtils.cpp b/media/utils/AImageReaderUtils.cpp new file mode 100644 index 0000000000..d97c3409c5 --- /dev/null +++ b/media/utils/AImageReaderUtils.cpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#include + +#include +#include + +namespace android { +namespace aimg { + +using hardware::hidl_vec; + +static sp convertNativeHandleToHGBP ( + const native_handle_t *handle) { + // Read the size of the halToken vec + hidl_vec halToken; + halToken.setToExternal( + reinterpret_cast(const_cast(&(handle->data[1]))), + handle->data[0]); + sp hgbp = + HGraphicBufferProducer::castFrom(retrieveHalInterface(halToken)); + return hgbp; +} + +sp AImageReader_getHGBPFromHandle( + const native_handle_t *handle) { + if (handle == nullptr) { + return nullptr; + } + if (handle->numFds != 0 || + handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) { + return nullptr; + } + return convertNativeHandleToHGBP(handle); +} + +} //namespace aimg +} //namespace android diff --git a/media/utils/Android.bp b/media/utils/Android.bp index f5b3f929c2..b05e022862 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -16,6 +16,7 @@ cc_library { name: "libmediautils", srcs: [ + "AImageReaderUtils.cpp", "BatteryNotifier.cpp", "ISchedulingPolicyService.cpp", "MemoryLeakTrackUtil.cpp", @@ -30,6 +31,9 @@ cc_library { "liblog", "libutils", "libmemunreachable", + "libhidlbase", + "android.hardware.graphics.bufferqueue@1.0", + "android.hidl.token@1.0-utils", ], logtags: ["EventLogTags.logtags"], diff --git a/media/utils/include/mediautils/AImageReaderUtils.h b/media/utils/include/mediautils/AImageReaderUtils.h new file mode 100644 index 0000000000..c9bde1fa50 --- /dev/null +++ b/media/utils/include/mediautils/AImageReaderUtils.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef AIMAGE_READER_UTILS_H +#define AIMAGE_READER_UTILS_H + +#include +#include + +namespace android { +namespace aimg { + +using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer; + +// Retrieves HGraphicBufferProducer corresponding to the native_handle_t +// provided (this native handle MUST have been obtained by AImageReader_getWindowNativeHandle()). +sp AImageReader_getHGBPFromHandle(const native_handle_t *handle); + +}// namespace aimg +}// namespace android + +#endif //AIMAGE_READER_UTILS_H -- GitLab From e0bb61b64b14eabd8ab8c7667ba2a7ab4a82b7e1 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 14 Nov 2018 23:59:12 -0800 Subject: [PATCH 0507/1530] camerserver: Remove dependency on libmediandk. libmediandk brings in many unneeded dependencies which results in an abnormal increase in vss. AImageReader_getHGBPFromHandle has been moved to libmediautils. Test: mm -j64 Test: showmap vss before change: 50628 Test: showmap vss after change: 31256 Change-Id: I4de95d08ae514c252a1e01f3b03e0021c821b72a Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/Android.bp | 1 - services/camera/libcameraservice/hidl/Convert.cpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 9f2515ebb0..025f0b2b21 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -72,7 +72,6 @@ cc_library_shared { "libbinder", "libcutils", "libmedia", - "libmediandk", "libmediautils", "libcamera_client", "libcamera_metadata", diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index 22e879e69a..d2a4ed660b 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -16,7 +16,8 @@ #include #include -#include +#include +#include namespace android { namespace hardware { @@ -25,6 +26,7 @@ namespace utils { namespace conversion { using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer; +using aimg::AImageReader_getHGBPFromHandle; // Note: existing data in dst will be gone. Caller still owns the memory of src void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) { -- GitLab From 70da577d31190279f8950a7d546539ef1bacb1fb Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 20 Nov 2018 18:50:31 -0800 Subject: [PATCH 0508/1530] cameraserver: Fix AIDL->HIDL conversion for CaptureResultExtras. Bug: 119838731 Test: AImageReaderVendorTest Change-Id: Iccfaa953a27faf450bc88e0505dcb9d66af0597a Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/hidl/Convert.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index d2a4ed660b..76ed6f6d2b 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -134,6 +134,7 @@ HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status) { HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtras) { HCaptureResultExtras hCaptureResultExtras; + hCaptureResultExtras.requestId = captureResultExtras.requestId; hCaptureResultExtras.burstId = captureResultExtras.burstId; hCaptureResultExtras.frameNumber = captureResultExtras.frameNumber; hCaptureResultExtras.partialResultCount = captureResultExtras.partialResultCount; -- GitLab From 1627871a05435f99a5abf2ad0736c13595035c24 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 20 Nov 2018 18:25:33 -0800 Subject: [PATCH 0509/1530] AImageReaderVendorTest: Add check for callbacks. Bug: 119838731 Test: AImageReaderVendorTest Change-Id: I6bea0aa21aa8159aabf03ba1dd8cba27d7db1355 Signed-off-by: Jayant Chowdhary --- .../tests/AImageReaderVendorTest.cpp | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index b1e501b215..579412ecf4 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -209,7 +209,18 @@ class CameraHelper { int takePicture() { int seqId; - return ACameraCaptureSession_capture(mSession, nullptr, 1, &mStillRequest, &seqId); + return ACameraCaptureSession_capture(mSession, &mCaptureCallbacks, 1, &mStillRequest, + &seqId); + } + + bool checkCallbacks(int pictureCount) { + std::lock_guard lock(mMutex); + if (mCompletedCaptureCallbackCount != pictureCount) { + ALOGE("Completed capture callaback count not as expected. expected %d actual %d", + pictureCount, mCompletedCaptureCallbackCount); + return false; + } + return true; } static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {} @@ -246,6 +257,26 @@ class CameraHelper { bool mIsCameraReady = false; const char* mCameraId; + int mCompletedCaptureCallbackCount = 0; + std::mutex mMutex; + ACameraCaptureSession_captureCallbacks mCaptureCallbacks = { + // TODO: Add tests for other callbacks + this, // context + nullptr, // onCaptureStarted + nullptr, // onCaptureProgressed + // onCaptureCompleted, called serially, so no lock needed. + [](void* ctx , ACameraCaptureSession *, ACaptureRequest *, + const ACameraMetadata *) { + CameraHelper *ch = static_cast(ctx); + std::lock_guard lock(ch->mMutex); + ch->mCompletedCaptureCallbackCount++; + }, + nullptr, // onCaptureFailed + nullptr, // onCaptureSequenceCompleted + nullptr, // onCaptureSequenceAborted + nullptr, // onCaptureBufferLost + }; + }; class ImageReaderTestCase { @@ -445,34 +476,36 @@ class ImageReaderTestCase { AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved}; }; -int takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) { +bool takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) { int ret = 0; - ImageReaderTestCase testCase( kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, readerAsync); ret = testCase.initImageReader(); if (ret < 0) { - return ret; + ALOGE("Unable to initialize ImageReader"); + return false; } CameraHelper cameraHelper(testCase.getNativeWindow()); ret = cameraHelper.initCamera(); if (ret < 0) { - return ret; + ALOGE("Unable to initialize camera helper"); + return false; } if (!cameraHelper.isCameraReady()) { ALOGW("Camera is not ready after successful initialization. It's either due to camera on " "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on " "board."); - return 0; + return true; } for (int i = 0; i < pictureCount; i++) { ret = cameraHelper.takePicture(); if (ret < 0) { - return ret; + ALOGE("Unable to take picture"); + return false; } } @@ -485,7 +518,8 @@ int takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, in break; } } - return testCase.getAcquiredImageCount() == pictureCount ? 0 : -1; + return testCase.getAcquiredImageCount() == pictureCount && + cameraHelper.checkCallbacks(pictureCount); } class AImageReaderWindowHandleTest : public ::testing::Test { @@ -504,7 +538,7 @@ bool testTakePicturesNative() { for (auto& readerMaxImages : {1, 4, 8}) { for (auto& readerAsync : {true, false}) { for (auto& pictureCount : {1, 4, 8}) { - if (takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) { + if (!takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) { ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, " "async=%d, pictureCount=%d", readerUsage, readerMaxImages, readerAsync, pictureCount); -- GitLab From 8cf929268cdb2cede142ebca0932fa4ac59d0f60 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 19 Nov 2018 15:45:17 -0800 Subject: [PATCH 0510/1530] cameraserver: Allow cameraserver HIDL interface to get provider ids with VendorTagSections. Bug: 110364143 Test: mm -j64 Test: AImageReaderVendorTest (sanity) Test: GCA (sanity) Change-Id: Iddcd5c1edb6cbe4b83968f01e46056a30c720219 Signed-off-by: Jayant Chowdhary --- camera/VendorTagDescriptor.cpp | 5 ++ camera/include/camera/VendorTagDescriptor.h | 3 + .../hidl/HidlCameraService.cpp | 77 +++++++++++-------- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp index 38ff37f51a..d713d2dd86 100644 --- a/camera/VendorTagDescriptor.cpp +++ b/camera/VendorTagDescriptor.cpp @@ -411,6 +411,11 @@ status_t VendorTagDescriptorCache::readFromParcel(const Parcel* parcel) { return res; } +const std::unordered_map> & + VendorTagDescriptorCache::getVendorIdsAndTagDescriptors() { + return mVendorMap; +} + int VendorTagDescriptorCache::getTagCount(metadata_vendor_id_t id) const { int ret = 0; auto desc = mVendorMap.find(id); diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h index c718c93aba..6f55890149 100644 --- a/camera/include/camera/VendorTagDescriptor.h +++ b/camera/include/camera/VendorTagDescriptor.h @@ -211,6 +211,9 @@ class VendorTagDescriptorCache : public Parcelable { */ void dump(int fd, int verbosity, int indentation) const; + const std::unordered_map> & + getVendorIdsAndTagDescriptors(); + protected: std::unordered_map> mVendorMap; struct vendor_tag_cache_ops mVendorCacheOps; diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp index 31bdf6de20..48f1d374bf 100644 --- a/services/camera/libcameraservice/hidl/HidlCameraService.cpp +++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp @@ -40,9 +40,11 @@ using hardware::Void; using device::V2_0::implementation::H2BCameraDeviceCallbacks; using device::V2_0::implementation::HidlCameraDeviceUser; using service::V2_0::implementation::H2BCameraServiceListener; -using HCameraMetadataType = android::frameworks::cameraservice::common::V2_0::CameraMetadataType; -using HVendorTag = android::frameworks::cameraservice::common::V2_0::VendorTag; -using HVendorTagSection = android::frameworks::cameraservice::common::V2_0::VendorTagSection; +using HCameraMetadataType = frameworks::cameraservice::common::V2_0::CameraMetadataType; +using HVendorTag = frameworks::cameraservice::common::V2_0::VendorTag; +using HVendorTagSection = frameworks::cameraservice::common::V2_0::VendorTagSection; +using HProviderIdAndVendorTagSections = + frameworks::cameraservice::common::V2_0::ProviderIdAndVendorTagSections; sp gHidlCameraService; @@ -215,39 +217,50 @@ Return HidlCameraService::removeListener(const sp HidlCameraService::getCameraVendorTagSections(getCameraVendorTagSections_cb _hidl_cb) { - hidl_vec hVendorTagSections; - // TODO: Could this be just created on the stack since we don't set it to - // global cache or anything ? - HStatus hStatus = HStatus::NO_ERROR; - sp desc = new VendorTagDescriptor(); - binder::Status serviceRet = mAidlICameraService->getCameraVendorTagDescriptor(desc.get()); - - if (!serviceRet.isOk()) { - ALOGE("%s: Failed to get VendorTagDescriptor", __FUNCTION__); - _hidl_cb(B2HStatus(serviceRet), hVendorTagSections); + sp gCache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + if (gCache == nullptr) { + _hidl_cb(HStatus::UNKNOWN_ERROR, {}); return Void(); } - - const SortedVector* sectionNames = desc->getAllSectionNames(); - size_t numSections = sectionNames->size(); - std::vector> tagsBySection(numSections); - int tagCount = desc->getTagCount(); - std::vector tags(tagCount); - desc->getTagArray(tags.data()); - for (int i = 0; i < tagCount; i++) { - HVendorTag vt; - vt.tagId = tags[i]; - vt.tagName = desc->getTagName(tags[i]); - vt.tagType = (HCameraMetadataType) desc->getTagType(tags[i]); - ssize_t sectionIdx = desc->getSectionIndex(tags[i]); - tagsBySection[sectionIdx].push_back(vt); + const std::unordered_map> + &vendorIdsAndTagDescs = gCache->getVendorIdsAndTagDescriptors(); + if (vendorIdsAndTagDescs.size() == 0) { + _hidl_cb(HStatus::UNKNOWN_ERROR, {}); + return Void(); } - hVendorTagSections.resize(numSections); - for (size_t s = 0; s < numSections; s++) { - hVendorTagSections[s].sectionName = (*sectionNames)[s].string(); - hVendorTagSections[s].tags = tagsBySection[s]; + + hidl_vec hTagIdsAndVendorTagSections; + hTagIdsAndVendorTagSections.resize(vendorIdsAndTagDescs.size()); + size_t j = 0; + for (auto &vendorIdAndTagDescs : vendorIdsAndTagDescs) { + hidl_vec hVendorTagSections; + sp desc = vendorIdAndTagDescs.second; + const SortedVector* sectionNames = desc->getAllSectionNames(); + size_t numSections = sectionNames->size(); + std::vector> tagsBySection(numSections); + int tagCount = desc->getTagCount(); + std::vector tags(tagCount); + desc->getTagArray(tags.data()); + for (int i = 0; i < tagCount; i++) { + HVendorTag vt; + vt.tagId = tags[i]; + vt.tagName = desc->getTagName(tags[i]); + vt.tagType = (HCameraMetadataType) desc->getTagType(tags[i]); + ssize_t sectionIdx = desc->getSectionIndex(tags[i]); + tagsBySection[sectionIdx].push_back(vt); + } + hVendorTagSections.resize(numSections); + for (size_t s = 0; s < numSections; s++) { + hVendorTagSections[s].sectionName = (*sectionNames)[s].string(); + hVendorTagSections[s].tags = tagsBySection[s]; + } + HProviderIdAndVendorTagSections &hProviderIdAndVendorTagSections = + hTagIdsAndVendorTagSections[j]; + hProviderIdAndVendorTagSections.providerId = vendorIdAndTagDescs.first; + hProviderIdAndVendorTagSections.vendorTagSections = std::move(hVendorTagSections); + j++; } - _hidl_cb(hStatus, hVendorTagSections); + _hidl_cb(HStatus::NO_ERROR, hTagIdsAndVendorTagSections); return Void(); } -- GitLab From 29c10564c083f574a0f34fdd5d9b70f5f1664f79 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Thu, 22 Nov 2018 09:58:38 +0900 Subject: [PATCH 0511/1530] AML: Make UserHandle.myUserId() available Since the method UserHandle.myUserId() is a @SystemApi, build error happens when building the mainline module. This CL makes it available by building the APK with the system SDK. Bug: 119749861 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: Iedecf1a7c7de1279161601c1e88f959e89af73ac --- packages/MediaComponents/apex/Android.bp | 2 +- .../apex/java/android/media/session/MediaSession.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp index 363a416cba..e797e149fe 100644 --- a/packages/MediaComponents/apex/Android.bp +++ b/packages/MediaComponents/apex/Android.bp @@ -20,7 +20,7 @@ filegroup { java_library { name: "media", installable: true, - sdk_version: "current", + sdk_version: "system_current", srcs: [ "java/android/media/**/*.java", "java/android/service/**/*.java", diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index b3ebbc8ac6..b40e3b0659 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -150,9 +150,7 @@ public final class MediaSession { * @param tag A short name for debugging purposes. */ public MediaSession(@NonNull Context context, @NonNull String tag) { - //TODO(b/119749861): Resolve hidden API usage, UserHandle.myUserId - //this(context, tag, UserHandle.myUserId()); - this(context, tag, 0); //TODO: remove this. + this(context, tag, UserHandle.myUserId()); } /** -- GitLab From d5020c785d64b7df0fbc1d5195142357f6af731a Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Thu, 22 Nov 2018 11:54:32 +0900 Subject: [PATCH 0512/1530] Add main contributers to OWNERS Bug: N/A Test: N/A Change-Id: I2c8f904def633e86d561a6d3c5a5c34fd44dad6c --- packages/OWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/OWNERS b/packages/OWNERS index bbc4cef4b0..3b9fd2beea 100644 --- a/packages/OWNERS +++ b/packages/OWNERS @@ -1,6 +1,9 @@ akersten@google.com dwkang@google.com +hdmoon@google.com +insun@google.com jaewan@google.com +jinpark@google.com marcone@google.com sungsoo@google.com wjia@google.com -- GitLab From 644a3e1cb3287a5a863968998fd77efccc915241 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 23 Nov 2018 13:52:39 +0000 Subject: [PATCH 0513/1530] Camera: Clear camera 3.5 device session Cached 3.5 HIDL session needs to be released in order to destroy the session implementation on provider side. Test: Camera CTS Bug: 119939541 Change-Id: I8b4c403b7bb9f3ac481e240701b5aaad505fdf2e --- services/camera/libcameraservice/device3/Camera3Device.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 32952cc12c..856af13834 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -3903,6 +3903,7 @@ bool Camera3Device::HalInterface::valid() { } void Camera3Device::HalInterface::clear() { + mHidlSession_3_5.clear(); mHidlSession_3_4.clear(); mHidlSession_3_3.clear(); mHidlSession.clear(); -- GitLab From cc1b4763eef2ae3e95d5ee4709dbf6519baf0351 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Mon, 26 Nov 2018 00:46:29 -0800 Subject: [PATCH 0514/1530] Create libcodec2_headers Also, move libcodec2 to media/codec2/core. Test: Builds Bug: 112362730 Bug: 119853704 Change-Id: Ie2d03724a052ff55d607aa04417280682e51ae69 --- media/codec2/{ => core}/Android.bp | 19 +++++++++++-------- media/codec2/{ => core}/C2.cpp | 0 media/codec2/{ => core}/include/C2.h | 0 media/codec2/{ => core}/include/C2Buffer.h | 0 .../codec2/{ => core}/include/C2BufferBase.h | 0 media/codec2/{ => core}/include/C2Component.h | 0 media/codec2/{ => core}/include/C2Config.h | 0 media/codec2/{ => core}/include/C2Enum.h | 0 media/codec2/{ => core}/include/C2Param.h | 0 media/codec2/{ => core}/include/C2ParamDef.h | 0 media/codec2/{ => core}/include/C2Work.h | 0 .../codec2/{ => core}/include/_C2MacroUtils.h | 0 .../{ => core}/include/android-C2Buffer.h | 0 .../stagefright/codec2/1.0/InputSurface.h | 0 .../codec2/1.0/InputSurfaceConnection.h | 0 media/codec2/tests/Android.bp | 9 ++++++--- media/codec2/vndk/Android.bp | 6 +++--- media/libstagefright/Android.bp | 6 ++++++ 18 files changed, 26 insertions(+), 14 deletions(-) rename media/codec2/{ => core}/Android.bp (71%) rename media/codec2/{ => core}/C2.cpp (100%) rename media/codec2/{ => core}/include/C2.h (100%) rename media/codec2/{ => core}/include/C2Buffer.h (100%) rename media/codec2/{ => core}/include/C2BufferBase.h (100%) rename media/codec2/{ => core}/include/C2Component.h (100%) rename media/codec2/{ => core}/include/C2Config.h (100%) rename media/codec2/{ => core}/include/C2Enum.h (100%) rename media/codec2/{ => core}/include/C2Param.h (100%) rename media/codec2/{ => core}/include/C2ParamDef.h (100%) rename media/codec2/{ => core}/include/C2Work.h (100%) rename media/codec2/{ => core}/include/_C2MacroUtils.h (100%) rename media/codec2/{ => core}/include/android-C2Buffer.h (100%) rename media/codec2/{ => core}/include/media/stagefright/codec2/1.0/InputSurface.h (100%) rename media/codec2/{ => core}/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h (100%) diff --git a/media/codec2/Android.bp b/media/codec2/core/Android.bp similarity index 71% rename from media/codec2/Android.bp rename to media/codec2/core/Android.bp index 576677ae8c..b723755b1d 100644 --- a/media/codec2/Android.bp +++ b/media/codec2/core/Android.bp @@ -1,3 +1,9 @@ +cc_library_headers { + name: "libcodec2_headers", + vendor_available: true, + export_include_dirs: ["include"], +} + cc_library_shared { name: "libcodec2", vendor_available: true, @@ -9,22 +15,18 @@ cc_library_shared { "-Werror", ], - include_dirs: [ - "frameworks/native/include/media/hardware", - ], - - export_include_dirs: [ - "include", - ], - header_libs: [ + "libcodec2_headers", "libhardware_headers", "libutils_headers", + "media_plugin_headers", ], export_header_lib_headers: [ + "libcodec2_headers", "libhardware_headers", "libutils_headers", + "media_plugin_headers", ], sanitize: { @@ -40,3 +42,4 @@ cc_library_shared { ldflags: ["-Wl,-Bsymbolic"], } + diff --git a/media/codec2/C2.cpp b/media/codec2/core/C2.cpp similarity index 100% rename from media/codec2/C2.cpp rename to media/codec2/core/C2.cpp diff --git a/media/codec2/include/C2.h b/media/codec2/core/include/C2.h similarity index 100% rename from media/codec2/include/C2.h rename to media/codec2/core/include/C2.h diff --git a/media/codec2/include/C2Buffer.h b/media/codec2/core/include/C2Buffer.h similarity index 100% rename from media/codec2/include/C2Buffer.h rename to media/codec2/core/include/C2Buffer.h diff --git a/media/codec2/include/C2BufferBase.h b/media/codec2/core/include/C2BufferBase.h similarity index 100% rename from media/codec2/include/C2BufferBase.h rename to media/codec2/core/include/C2BufferBase.h diff --git a/media/codec2/include/C2Component.h b/media/codec2/core/include/C2Component.h similarity index 100% rename from media/codec2/include/C2Component.h rename to media/codec2/core/include/C2Component.h diff --git a/media/codec2/include/C2Config.h b/media/codec2/core/include/C2Config.h similarity index 100% rename from media/codec2/include/C2Config.h rename to media/codec2/core/include/C2Config.h diff --git a/media/codec2/include/C2Enum.h b/media/codec2/core/include/C2Enum.h similarity index 100% rename from media/codec2/include/C2Enum.h rename to media/codec2/core/include/C2Enum.h diff --git a/media/codec2/include/C2Param.h b/media/codec2/core/include/C2Param.h similarity index 100% rename from media/codec2/include/C2Param.h rename to media/codec2/core/include/C2Param.h diff --git a/media/codec2/include/C2ParamDef.h b/media/codec2/core/include/C2ParamDef.h similarity index 100% rename from media/codec2/include/C2ParamDef.h rename to media/codec2/core/include/C2ParamDef.h diff --git a/media/codec2/include/C2Work.h b/media/codec2/core/include/C2Work.h similarity index 100% rename from media/codec2/include/C2Work.h rename to media/codec2/core/include/C2Work.h diff --git a/media/codec2/include/_C2MacroUtils.h b/media/codec2/core/include/_C2MacroUtils.h similarity index 100% rename from media/codec2/include/_C2MacroUtils.h rename to media/codec2/core/include/_C2MacroUtils.h diff --git a/media/codec2/include/android-C2Buffer.h b/media/codec2/core/include/android-C2Buffer.h similarity index 100% rename from media/codec2/include/android-C2Buffer.h rename to media/codec2/core/include/android-C2Buffer.h diff --git a/media/codec2/include/media/stagefright/codec2/1.0/InputSurface.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h similarity index 100% rename from media/codec2/include/media/stagefright/codec2/1.0/InputSurface.h rename to media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h diff --git a/media/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h similarity index 100% rename from media/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h rename to media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp index 5540f7bd92..fce6e2102c 100644 --- a/media/codec2/tests/Android.bp +++ b/media/codec2/tests/Android.bp @@ -6,10 +6,13 @@ cc_test { ], include_dirs: [ - "frameworks/av/media/codec2/include", "frameworks/av/media/codec2/vndk/include", ], + header_libs: [ + "libcodec2_headers", + ], + // param tests must not depend on any codec2 libraries as all params should be templated shared_libs: [ ], @@ -57,8 +60,8 @@ cc_test { "C2ComponentInterface_test.cpp", ], - include_dirs: [ - "frameworks/native/include/media/openmax", + header_libs: [ + "media_plugin_headers", ], shared_libs: [ diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index 5a7c98cae6..0eb90be638 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -42,9 +42,9 @@ cc_library_shared { "internal", ], - include_dirs: [ - "frameworks/native/include/media/hardware", - "frameworks/av/media/codec2/include", + header_libs: [ + "media_plugin_headers", + "libcodec2_headers", ], shared_libs: [ diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index d96d3581aa..9d77985b7d 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -207,6 +207,12 @@ cc_library_shared { "include", ], + // This is needed to make sure libcodec2 exists in all devices. + // TODO: Remove this once the public CCodec is enabled. + required: [ + "libcodec2", + ], + cflags: [ "-Wno-multichar", "-Werror", -- GitLab From 128d05d62a767ff1eed11f6e3068d6f2f5bc6336 Mon Sep 17 00:00:00 2001 From: ybai Date: Fri, 16 Nov 2018 14:06:55 +0800 Subject: [PATCH 0515/1530] Add AC-4 type into kMimeToRole array and M3UParser Bug: 119312182 Test: n/a Change-Id: Ic81ffa9a1c97cacf089981df1187b17b340d9671 --- media/libstagefright/httplive/M3UParser.cpp | 1 + media/libstagefright/omx/OMXUtils.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 8ab33f752f..d06d9f276e 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -1391,6 +1391,7 @@ bool M3UParser::codecIsType(const AString &codec, const char *type) { case 'QDMC': case 'ulaw': case 'vdva': + case 'ac-4': return !strcmp("audio", type); case 'avc1': diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp index 9ed4a99aeb..c499c77100 100644 --- a/media/libstagefright/omx/OMXUtils.cpp +++ b/media/libstagefright/omx/OMXUtils.cpp @@ -164,6 +164,8 @@ const char *GetComponentRole(bool isEncoder, const char *mime) { "audio_decoder.ac3", "audio_encoder.ac3" }, { MEDIA_MIMETYPE_AUDIO_EAC3, "audio_decoder.eac3", "audio_encoder.eac3" }, + { MEDIA_MIMETYPE_AUDIO_AC4, + "audio_decoder.ac4", "audio_encoder.ac4" }, { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, "image_decoder.heic", "image_encoder.heic" }, }; -- GitLab From 1b848c068aab4d7f0ee74b76afe879d511e72229 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 21 Nov 2018 16:49:07 -0800 Subject: [PATCH 0516/1530] stagefright cmd: add HEVC mime type Test: m stagefright Change-Id: I9830005b56a9b1fc6396df5caebb14008ea72810 --- cmds/stagefright/stagefright.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 28a218cd82..86e90408a8 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -650,7 +650,7 @@ static void dumpCodecProfiles(bool queryDecoders) { MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9, - MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, + MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_VIDEO_HEVC, MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4 }; -- GitLab From 102a4220722d36e52fc665506ff2daa32d574c05 Mon Sep 17 00:00:00 2001 From: Mark Hong Date: Tue, 23 Oct 2018 18:57:31 -0700 Subject: [PATCH 0517/1530] Add dump permission check in the MediaExtractorFactory::dump(int fd, const Vector&) same as dumpExtractors(int fd, const Vector&) The purpose of TC is to check if the dump produces minimal output, but it fails because the output of dump() has more than one line. BUG: 118364777 Test: run cts -m CtsSecurityTestCases -t android.security.cts.ServicePermissionsTest#testDumpProtected Change-Id: Ifd15f4e7a6626feadb9debe5c1d4f3fbeedfb964 Signed-off-by: Mark Hong --- .../libstagefright/MediaExtractorFactory.cpp | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 8f1dd362e9..318c1eb94c 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "MediaExtractorFactory" #include +#include +#include #include #include #include @@ -353,18 +355,29 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { status_t MediaExtractorFactory::dump(int fd, const Vector&) { Mutex::Autolock autoLock(gPluginMutex); String8 out; - out.append("Available extractors:\n"); - if (gPluginsRegistered) { - for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) { - out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)\n", - (*it)->def.extractor_name, + + const IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { + // dumpExtractors() will append the following string. + // out.appendFormat("Permission Denial: " + // "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid); + ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid); + } else { + out.append("Available extractors:\n"); + if (gPluginsRegistered) { + for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) { + out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)\n", + (*it)->def.extractor_name, (*it)->def.def_version, - (*it)->uuidString.c_str(), - (*it)->def.extractor_version, - (*it)->libPath.c_str()); + (*it)->uuidString.c_str(), + (*it)->def.extractor_version, + (*it)->libPath.c_str()); + } + } else { + out.append(" (no plugins registered)\n"); } - } else { - out.append(" (no plugins registered)\n"); } write(fd, out.string(), out.size()); return OK; -- GitLab From f32108e7bab9ce2eb8107678c74701d1428a7b2c Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 4 Oct 2018 17:22:04 -0700 Subject: [PATCH 0518/1530] audio policy: extend API used to silence capture from a given app In preparation for concurrent capture policy, extend current audio policy interface setRecordSilenced(uid...) to a more generic API setAppState(uid...) Bug: 111438757 Test: CTS test AudioRecord.testRecordNoDataForIdleUids Change-Id: Idba540e2724a6c71ec275a5ff73c312e5b6040a4 --- services/audiopolicy/AudioPolicyInterface.h | 2 +- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 6 ++++-- services/audiopolicy/managerdefault/AudioPolicyManager.h | 2 +- services/audiopolicy/service/AudioPolicyService.cpp | 9 ++++++--- services/audiopolicy/service/AudioPolicyService.h | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index d4c49d9f0e..3c3a82b38f 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -242,7 +242,7 @@ public: bool reported) = 0; virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0; - virtual void setRecordSilenced(uid_t uid, bool silenced); + virtual void setAppState(uid_t uid, app_state_t state); }; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d1184599fa..e9e1a22bf8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3715,11 +3715,13 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat return profileUpdated ? NO_ERROR : INVALID_OPERATION; } -void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced) +void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { + Vector > activeInputs = mInputs.getActiveInputs(); + bool silenced = state == APP_STATE_IDLE; + ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced); - Vector > activeInputs = mInputs.getActiveInputs(); for (size_t i = 0; i < activeInputs.size(); i++) { sp activeDesc = activeInputs[i]; RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index f559b7fc96..0436b1d0f8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -242,7 +242,7 @@ public: // return the strategy corresponding to a given stream type routing_strategy getStrategy(audio_stream_type_t stream) const; - virtual void setRecordSilenced(uid_t uid, bool silenced); + virtual void setAppState(uid_t uid, app_state_t state); protected: // A constructor that allows more fine-grained control over initialization process, diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index b1ed522c7d..6da1f9f3a5 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -340,17 +340,18 @@ status_t AudioPolicyService::dumpInternals(int fd) return NO_ERROR; } -void AudioPolicyService::setRecordSilenced(uid_t uid, bool silenced) +void AudioPolicyService::setAppState(uid_t uid, app_state_t state) { { Mutex::Autolock _l(mLock); if (mAudioPolicyManager) { AutoCallerClear acc; - mAudioPolicyManager->setRecordSilenced(uid, silenced); + mAudioPolicyManager->setAppState(uid, state); } } sp af = AudioSystem::get_audio_flinger(); if (af) { + bool silenced = state == APP_STATE_IDLE; af->setRecordSilenced(uid, silenced); } } @@ -585,7 +586,9 @@ void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) void AudioPolicyService::UidPolicy::notifyService(uid_t uid, bool active) { sp service = mService.promote(); if (service != nullptr) { - service->setRecordSilenced(uid, !active); + service->setAppState(uid, active ? + APP_STATE_FOREGROUND : + APP_STATE_IDLE); } } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 6c5564728a..a341145380 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -250,7 +250,7 @@ private: virtual status_t shellCommand(int in, int out, int err, Vector& args); // Sets whether the given UID records only silence - virtual void setRecordSilenced(uid_t uid, bool silenced); + virtual void setAppState(uid_t uid, app_state_t state); // Overrides the UID state as if it is idle status_t handleSetUidState(Vector& args, int err); -- GitLab From b78763e6098baa33f441abef74d53636f72c6d6e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 17 Oct 2018 10:08:02 -0700 Subject: [PATCH 0519/1530] audio policy: receive assistant and a11y service UIDs Add APIs to communicate assistant and accessibility service UIDs to audio policy service in order to implement the new concurrent capture policy. Bug: 111438757 Test: enable and disable Google Assistant and Voice Access. Change-Id: Ia804613051edab2ebd57d317b5b7509c98e7c6b4 --- media/libaudioclient/AudioSystem.cpp | 18 +++++ media/libaudioclient/IAudioPolicyService.cpp | 71 ++++++++++++++++++- .../include/media/AudioSystem.h | 3 + .../include/media/IAudioPolicyService.h | 6 +- .../service/AudioPolicyInterfaceImpl.cpp | 14 ++++ .../service/AudioPolicyService.cpp | 6 ++ .../audiopolicy/service/AudioPolicyService.h | 11 ++- 7 files changed, 124 insertions(+), 5 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index e260fd838a..ec36ed7dc8 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1298,6 +1298,24 @@ status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool return aps->setSurroundFormatEnabled(audioFormat, enabled); } + +status_t AudioSystem::setAssistantUid(uid_t uid) +{ + const sp & aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + return aps->setAssistantUid(uid); +} + +status_t AudioSystem::setA11yServicesUids(const std::vector& uids) +{ + const sp & aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + return aps->setA11yServicesUids(uids); +} + + // --------------------------------------------------------------------------- int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback( diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 32a71f3b6a..a406658a16 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -85,7 +85,9 @@ enum { ADD_STREAM_DEFAULT_EFFECT, REMOVE_STREAM_DEFAULT_EFFECT, ADD_SOURCE_DEFAULT_EFFECT, - REMOVE_SOURCE_DEFAULT_EFFECT + REMOVE_SOURCE_DEFAULT_EFFECT, + SET_ASSISTANT_UID, + SET_A11Y_SERVICES_UIDS, }; #define MAX_ITEMS_PER_LIST 1024 @@ -941,6 +943,33 @@ public: return static_cast (reply.readInt32()); } + virtual status_t setAssistantUid(uid_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(uid); + status_t status = remote()->transact(SET_ASSISTANT_UID, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + + virtual status_t setA11yServicesUids(const std::vector& uids) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(uids.size()); + for (auto uid : uids) { + data.writeInt32(uid); + } + status_t status = remote()->transact(SET_A11Y_SERVICES_UIDS, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -997,7 +1026,9 @@ status_t BnAudioPolicyService::onTransact( case START_AUDIO_SOURCE: case STOP_AUDIO_SOURCE: case GET_SURROUND_FORMATS: - case SET_SURROUND_FORMAT_ENABLED: { + case SET_SURROUND_FORMAT_ENABLED: + case SET_ASSISTANT_UID: + case SET_A11Y_SERVICES_UIDS: { if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), @@ -1710,6 +1741,42 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case SET_ASSISTANT_UID: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + int32_t uid; + status_t status = data.readInt32(&uid); + if (status != NO_ERROR) { + return status; + } + status = setAssistantUid(uid); + reply->writeInt32(static_cast (status)); + return NO_ERROR; + } + + case SET_A11Y_SERVICES_UIDS: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + std::vector uids; + int32_t size; + status_t status = data.readInt32(&size); + if (status != NO_ERROR) { + return status; + } + if (size > MAX_ITEMS_PER_LIST) { + size = MAX_ITEMS_PER_LIST; + } + for (int32_t i = 0; i < size; i++) { + int32_t uid; + status = data.readInt32(&uid); + if (status != NO_ERROR) { + return status; + } + uids.push_back(uid); + } + status = setA11yServicesUids(uids); + reply->writeInt32(static_cast (status)); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index adfee8b7c4..76a79c9536 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -343,6 +343,9 @@ public: bool reported); static status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); + static status_t setAssistantUid(uid_t uid); + static status_t setA11yServicesUids(const std::vector& uids); + // ---------------------------------------------------------------------------- class AudioPortCallback : public RefBase diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index fdd8d57969..a246df68ea 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -20,15 +20,14 @@ #include #include #include - #include #include #include #include #include #include - #include +#include namespace android { @@ -180,6 +179,9 @@ public: bool *surroundFormatsEnabled, bool reported) = 0; virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0; + + virtual status_t setAssistantUid(uid_t uid) = 0; + virtual status_t setA11yServicesUids(const std::vector& uids) = 0; }; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 02ab07fbed..59c8f10075 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1135,4 +1135,18 @@ status_t AudioPolicyService::setSurroundFormatEnabled(audio_format_t audioFormat return mAudioPolicyManager->setSurroundFormatEnabled(audioFormat, enabled); } +status_t AudioPolicyService::setAssistantUid(uid_t uid) +{ + Mutex::Autolock _l(mLock); + mUidPolicy->setAssistantUid(uid); + return NO_ERROR; +} + +status_t AudioPolicyService::setA11yServicesUids(const std::vector& uids) +{ + Mutex::Autolock _l(mLock); + mUidPolicy->setA11yUids(uids); + return NO_ERROR; +} + } // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 6da1f9f3a5..018d9e36d6 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -637,6 +637,12 @@ void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid); + return it != mA11yUids.end(); +} + // ----------- AudioPolicyService::AudioCommandThread implementation ---------- AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index a341145380..ec32511387 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -214,6 +214,9 @@ public: bool reported); virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); + virtual status_t setAssistantUid(uid_t uid); + virtual status_t setA11yServicesUids(const std::vector& uids); + status_t doStopOutput(audio_port_handle_t portId); void doReleaseOutput(audio_port_handle_t portId); @@ -277,7 +280,7 @@ private: class UidPolicy : public BnUidObserver, public virtual IBinder::DeathRecipient { public: explicit UidPolicy(wp service) - : mService(service), mObserverRegistered(false) {} + : mService(service), mObserverRegistered(false), mAssistantUid(0) {} void registerSelf(); void unregisterSelf(); @@ -286,6 +289,10 @@ private: void binderDied(const wp &who) override; bool isUidActive(uid_t uid); + void setAssistantUid(uid_t uid) { mAssistantUid = uid; } + bool isAssistantUid(uid_t uid) { return uid == mAssistantUid; } + void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } + bool isA11yUid(uid_t uid); // BnUidObserver implementation void onUidActive(uid_t uid) override; @@ -307,6 +314,8 @@ private: bool mObserverRegistered; std::unordered_map mOverrideUids; std::unordered_map mCachedUids; + uid_t mAssistantUid; + std::vector mA11yUids; }; // Thread used to send audio config commands to audio flinger -- GitLab From 0ac73a5dd0db5f6b67b4c21a9ab5e28d5ab532bd Mon Sep 17 00:00:00 2001 From: Sampath Shetty Date: Tue, 27 Mar 2018 15:16:35 +1100 Subject: [PATCH 0520/1530] Extract AudioPresentations from AC4 in MP4 and TS Translates MPEG-4 AC4 DSI AC4Presentations to AudioPresentation. Parses MPEG2-TS audio preselection descriptors from MPEG2-TS. Adds AudioPresentationInfo interface to native MediaExtractor. Test: Manually test Bug: 119312182 Change-Id: I61286b38543e114aeaef331aa013bc2d2d7626c3 --- include/media/AudioPresentationInfo.h | 79 ---------- media/extractors/mp4/MPEG4Extractor.cpp | 70 +++++++++ .../include/media/stagefright/MetaDataBase.h | 3 + media/libstagefright/Android.bp | 1 - .../libstagefright/AudioPresentationInfo.cpp | 45 ------ media/libstagefright/NuMediaExtractor.cpp | 28 ++++ media/libstagefright/Utils.cpp | 1 + media/libstagefright/foundation/Android.bp | 1 + .../foundation/AudioPresentationInfo.cpp | 136 ++++++++++++++++++ .../foundation/AudioPresentationInfo.h | 68 +++++++++ .../media/stagefright/NuMediaExtractor.h | 4 + media/libstagefright/mpeg2ts/ATSParser.cpp | 95 +++++++++++- media/libstagefright/mpeg2ts/ATSParser.h | 9 +- .../mpeg2ts/AnotherPacketSource.cpp | 19 +++ .../mpeg2ts/AnotherPacketSource.h | 2 + media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + 17 files changed, 431 insertions(+), 132 deletions(-) delete mode 100644 include/media/AudioPresentationInfo.h delete mode 100644 media/libstagefright/AudioPresentationInfo.cpp create mode 100644 media/libstagefright/foundation/AudioPresentationInfo.cpp create mode 100644 media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h diff --git a/include/media/AudioPresentationInfo.h b/include/media/AudioPresentationInfo.h deleted file mode 100644 index e91a992950..0000000000 --- a/include/media/AudioPresentationInfo.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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. - */ - -#ifndef AUDIO_PRESENTATION_INFO_H_ -#define AUDIO_PRESENTATION_INFO_H_ - -#include -#include - -#include -#include -#include -#include - -namespace android { - -enum mastering_indication { - MASTERING_NOT_INDICATED, - MASTERED_FOR_STEREO, - MASTERED_FOR_SURROUND, - MASTERED_FOR_3D, - MASTERED_FOR_HEADPHONE, -}; - -struct AudioPresentation : public RefBase { - int32_t mPresentationId; - int32_t mProgramId; - KeyedVector mLabels; - String8 mLanguage; - int32_t mMasteringIndication; - bool mAudioDescriptionAvailable; - bool mSpokenSubtitlesAvailable; - bool mDialogueEnhancementAvailable; - - AudioPresentation() { - mPresentationId = -1; - mProgramId = -1; - mLanguage = ""; - mMasteringIndication = MASTERING_NOT_INDICATED; - mAudioDescriptionAvailable = false; - mSpokenSubtitlesAvailable = false; - mDialogueEnhancementAvailable = false; - } -}; - -typedef Vector> AudioPresentations; - -class AudioPresentationInfo : public RefBase { - public: - AudioPresentationInfo(); - - ~AudioPresentationInfo(); - - void addPresentation(sp presentation); - - size_t countPresentations() const; - - const sp getPresentation(size_t index) const; - - private: - AudioPresentations mPresentations; -}; - -} // namespace android - -#endif // AUDIO_PRESENTATION_INFO_H_ diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index c3bdc928cb..337ff2a0e1 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -2753,6 +2754,75 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC4); AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount); AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate); + + AudioPresentationCollection presentations; + // translate the AC4 presentation information to audio presentations for this track + AC4DSIParser::AC4Presentations ac4Presentations = parser.getPresentations(); + if (!ac4Presentations.empty()) { + for (const auto& ac4Presentation : ac4Presentations) { + auto& presentation = ac4Presentation.second; + if (!presentation.mEnabled) { + continue; + } + AudioPresentationV1 ap; + ap.mPresentationId = presentation.mGroupIndex; + ap.mProgramId = presentation.mProgramID; + ap.mLanguage = presentation.mLanguage; + if (presentation.mPreVirtualized) { + ap.mMasteringIndication = MASTERED_FOR_HEADPHONE; + } else { + switch (presentation.mChannelMode) { + case AC4Parser::AC4Presentation::kChannelMode_Mono: + case AC4Parser::AC4Presentation::kChannelMode_Stereo: + ap.mMasteringIndication = MASTERED_FOR_STEREO; + break; + case AC4Parser::AC4Presentation::kChannelMode_3_0: + case AC4Parser::AC4Presentation::kChannelMode_5_0: + case AC4Parser::AC4Presentation::kChannelMode_5_1: + case AC4Parser::AC4Presentation::kChannelMode_7_0_34: + case AC4Parser::AC4Presentation::kChannelMode_7_1_34: + case AC4Parser::AC4Presentation::kChannelMode_7_0_52: + case AC4Parser::AC4Presentation::kChannelMode_7_1_52: + ap.mMasteringIndication = MASTERED_FOR_SURROUND; + break; + case AC4Parser::AC4Presentation::kChannelMode_7_0_322: + case AC4Parser::AC4Presentation::kChannelMode_7_1_322: + case AC4Parser::AC4Presentation::kChannelMode_7_0_4: + case AC4Parser::AC4Presentation::kChannelMode_7_1_4: + case AC4Parser::AC4Presentation::kChannelMode_9_0_4: + case AC4Parser::AC4Presentation::kChannelMode_9_1_4: + case AC4Parser::AC4Presentation::kChannelMode_22_2: + ap.mMasteringIndication = MASTERED_FOR_3D; + break; + default: + ALOGE("Invalid channel mode in AC4 presentation"); + return ERROR_MALFORMED; + } + } + + ap.mAudioDescriptionAvailable = (presentation.mContentClassifier == + AC4Parser::AC4Presentation::kVisuallyImpaired); + ap.mSpokenSubtitlesAvailable = (presentation.mContentClassifier == + AC4Parser::AC4Presentation::kVoiceOver); + ap.mDialogueEnhancementAvailable = presentation.mHasDialogEnhancements; + if (!ap.mLanguage.empty()) { + ap.mLabels.emplace(ap.mLanguage, presentation.mDescription); + } + presentations.push_back(std::move(ap)); + } + } + + if (presentations.empty()) { + // Clear audio presentation info in metadata. + AMediaFormat_setBuffer( + mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, nullptr, 0); + } else { + std::ostringstream outStream(std::ios::out); + serializeAudioPresentations(presentations, &outStream); + AMediaFormat_setBuffer( + mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, + outStream.str().data(), outStream.str().size()); + } return OK; } diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h index 72efcdfcab..9f2deda471 100644 --- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h +++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h @@ -225,6 +225,9 @@ enum { // Key for ALAC Magic Cookie kKeyAlacMagicCookie = 'almc', // raw data + + // AC-4 AudioPresentationInfo + kKeyAudioPresentationInfo = 'audP', // raw data }; enum { diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index d96d3581aa..55b0862dcc 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -96,7 +96,6 @@ cc_library_shared { "AHierarchicalStateMachine.cpp", "AMRWriter.cpp", "AudioPlayer.cpp", - "AudioPresentationInfo.cpp", "AudioSource.cpp", "BufferImpl.cpp", "CallbackDataSource.cpp", diff --git a/media/libstagefright/AudioPresentationInfo.cpp b/media/libstagefright/AudioPresentationInfo.cpp deleted file mode 100644 index 86e1859ce8..0000000000 --- a/media/libstagefright/AudioPresentationInfo.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "AudioPresentationInfo" - -#include - -namespace android { - -AudioPresentationInfo::AudioPresentationInfo() { -} - -AudioPresentationInfo::~AudioPresentationInfo() { - mPresentations.clear(); -} - -void AudioPresentationInfo::addPresentation(sp presentation) { - mPresentations.push(presentation); -} - -size_t AudioPresentationInfo::countPresentations() const { - return mPresentations.size(); -} - -// Returns an AudioPresentation for the given valid index -// index must be >=0 and < countPresentations() -const sp AudioPresentationInfo::getPresentation(size_t index) const { - return mPresentations[index]; -} - -} // namespace android diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index f94648c571..f5178ddb32 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -794,4 +794,32 @@ bool NuMediaExtractor::getCachedDuration( return false; } +// Return OK if we have received an audio presentation info. +// Return ERROR_UNSUPPORTED if the track has no audio presentation. +// Return INVALID_OPERATION if audio presentation metadata version does not match. +status_t NuMediaExtractor::getAudioPresentations( + size_t trackIndex, AudioPresentationCollection *presentations) const { + Mutex::Autolock autoLock(mLock); + + if (mImpl == NULL) { + return -EINVAL; + } + + if (trackIndex >= mImpl->countTracks()) { + return -ERANGE; + } + + sp meta = mImpl->getTrackMetaData(trackIndex); + + uint32_t type; + const void *data; + size_t size; + if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) { + std::istringstream inStream(std::string(static_cast(data), size)); + return deserializeAudioPresentations(&inStream, presentations); + } + ALOGE("Source does not contain any audio presentation"); + return ERROR_UNSUPPORTED; +} + } // namespace android diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 4aee9d5940..53c32b2594 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -615,6 +615,7 @@ static std::vector> int32Mappings { static std::vector> bufferMappings { { { "albumart", kKeyAlbumArt }, + { "audio-presentation-info", kKeyAudioPresentationInfo }, { "pssh", kKeyPssh }, { "crypto-iv", kKeyCryptoIV }, { "crypto-key", kKeyCryptoKey }, diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index 5b7961d204..861528e163 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -61,6 +61,7 @@ cc_library { "AMessage.cpp", "AString.cpp", "AStringUtils.cpp", + "AudioPresentationInfo.cpp", "ByteUtils.cpp", "ColorUtils.cpp", "MediaDefs.cpp", diff --git a/media/libstagefright/foundation/AudioPresentationInfo.cpp b/media/libstagefright/foundation/AudioPresentationInfo.cpp new file mode 100644 index 0000000000..4b8e969cd7 --- /dev/null +++ b/media/libstagefright/foundation/AudioPresentationInfo.cpp @@ -0,0 +1,136 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "AudioPresentationInfo" + +#include + +#include "AudioPresentationInfo.h" + +#include + +namespace android { + +void serializeAudioPresentations(const AudioPresentationCollection& presentations, + std::ostream* serializedOutput) { + uint32_t numPresentations = presentations.size(); + serializedOutput->write(reinterpret_cast(&numPresentations), sizeof(numPresentations)); + for (const auto& ap : presentations) { + if (ap.mVersion == PRESENTATION_VERSION_1) { + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mVersion)), + sizeof(ap.mVersion)); + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mPresentationId)), + sizeof(ap.mPresentationId)); + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mProgramId)), + sizeof(ap.mProgramId)); + + uint32_t numLabels = ap.mLabels.size(); + serializedOutput->write( + const_cast(reinterpret_cast(&numLabels)), + sizeof(numLabels)); + for (const auto& label : ap.mLabels) { + uint32_t labelKeySize = label.first.size(); + serializedOutput->write( + const_cast(reinterpret_cast(&labelKeySize)), + sizeof(labelKeySize)); + serializedOutput->write(label.first.c_str(), labelKeySize); + + uint32_t labelValSize = label.second.size(); + serializedOutput->write( + const_cast(reinterpret_cast(&labelValSize)), + sizeof(labelValSize)); + serializedOutput->write(label.second.c_str(), labelValSize); + } + + uint32_t langSize = ap.mLanguage.size(); + serializedOutput->write( + const_cast(reinterpret_cast(&langSize)), + sizeof(langSize)); + serializedOutput->write(ap.mLanguage.c_str(), langSize); + + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mMasteringIndication)), + sizeof(ap.mMasteringIndication)); + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mAudioDescriptionAvailable)), + sizeof(ap.mAudioDescriptionAvailable)); + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mSpokenSubtitlesAvailable)), + sizeof(ap.mSpokenSubtitlesAvailable)); + serializedOutput->write( + const_cast(reinterpret_cast(&ap.mDialogueEnhancementAvailable)), + sizeof(ap.mDialogueEnhancementAvailable)); + } + } +} + +status_t deserializeAudioPresentations(std::istream* serializedInput, + AudioPresentationCollection *presentations) { + uint32_t numPresentations; + serializedInput->read(reinterpret_cast(&numPresentations), sizeof(numPresentations)); + for (uint32_t i = 0; i < numPresentations; ++i) { + uint32_t version; + serializedInput->read(reinterpret_cast(&version), sizeof(version)); + if (version == PRESENTATION_VERSION_1) { + AudioPresentationV1 ap; + serializedInput->read( + reinterpret_cast(&ap.mPresentationId), + sizeof(ap.mPresentationId)); + serializedInput->read(reinterpret_cast(&ap.mProgramId), sizeof(ap.mProgramId)); + + uint32_t numLabels; + serializedInput->read(reinterpret_cast(&numLabels), sizeof(numLabels)); + for (uint32_t j = 0; j < numLabels; ++j) { + uint32_t labelKeySize; + serializedInput->read(reinterpret_cast(&labelKeySize), sizeof(labelKeySize)); + std::vector labelKey(labelKeySize); + serializedInput->read(labelKey.data(), labelKeySize); + + uint32_t labelValSize; + serializedInput->read(reinterpret_cast(&labelValSize), sizeof(labelValSize)); + std::vector labelVal(labelValSize); + serializedInput->read(labelVal.data(), labelValSize); + ap.mLabels.emplace( + std::string(reinterpret_cast(labelKey.data()), labelKeySize), + std::string(reinterpret_cast(labelVal.data()), labelValSize)); + } + uint32_t languageSize; + serializedInput->read(reinterpret_cast(&languageSize), sizeof(languageSize)); + std::vector language(languageSize); + serializedInput->read(language.data(), languageSize); + ap.mLanguage = std::string(reinterpret_cast(language.data()), languageSize); + serializedInput->read(reinterpret_cast(&ap.mMasteringIndication), + sizeof(ap.mMasteringIndication)); + serializedInput->read(reinterpret_cast(&ap.mAudioDescriptionAvailable), + sizeof(ap.mAudioDescriptionAvailable)); + serializedInput->read(reinterpret_cast(&ap.mSpokenSubtitlesAvailable), + sizeof(ap.mSpokenSubtitlesAvailable)); + serializedInput->read(reinterpret_cast(&ap.mDialogueEnhancementAvailable), + sizeof(ap.mDialogueEnhancementAvailable)); + presentations->push_back(std::move(ap)); + } else { + ALOGE("Audio presentation info version is not supported"); + return INVALID_OPERATION; + } + } + return OK; +} + +} // namespace android diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h new file mode 100644 index 0000000000..4bd4d9f2d8 --- /dev/null +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AudioPresentationInfo.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef AUDIO_PRESENTATION_INFO_H_ +#define AUDIO_PRESENTATION_INFO_H_ + +#include +#include +#include +#include + +#include + +namespace android { + +enum AudioPresentationVersion { + PRESENTATION_VERSION_UNDEFINED = 0, + PRESENTATION_VERSION_1, +}; + +enum MasteringIndication { + MASTERING_NOT_INDICATED, + MASTERED_FOR_STEREO, + MASTERED_FOR_SURROUND, + MASTERED_FOR_3D, + MASTERED_FOR_HEADPHONE, +}; + +struct AudioPresentation { + uint32_t mVersion = PRESENTATION_VERSION_UNDEFINED; + int32_t mPresentationId = -1; + int32_t mProgramId = -1; + std::map mLabels; + std::string mLanguage; + MasteringIndication mMasteringIndication = MASTERING_NOT_INDICATED; + bool mAudioDescriptionAvailable = false; + bool mSpokenSubtitlesAvailable = false; + bool mDialogueEnhancementAvailable = false; +}; + +struct AudioPresentationV1 : public AudioPresentation { + AudioPresentationV1() { + mVersion = PRESENTATION_VERSION_1; + } +}; + +typedef std::vector AudioPresentationCollection; + +void serializeAudioPresentations(const AudioPresentationCollection& presentations, + std::ostream* serializedOutput); +status_t deserializeAudioPresentations(std::istream* serializedInput, + AudioPresentationCollection *presentations); +} // namespace android + +#endif // AUDIO_PRESENTATION_INFO_H_ diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h index 641ccfa289..8dc8d388ee 100644 --- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,9 @@ struct NuMediaExtractor : public RefBase { bool getCachedDuration(int64_t *durationUs, bool *eos) const; + status_t getAudioPresentations(size_t trackIdx, + AudioPresentationCollection *presentations) const; + protected: virtual ~NuMediaExtractor(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index aa4a4dbbda..cf93fcf5e7 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -210,6 +210,7 @@ private: sp mDescrambledBuffer; List mSubSamples; sp mDescrambler; + AudioPresentationCollection mAudioPresentations; // Flush accumulated payload if necessary --- i.e. at EOS or at the start of // another payload. event is set if the flushed payload is PES with a sync @@ -525,6 +526,8 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { CADescriptor streamCA; info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX; + + info.mAudioPresentations.clear(); bool hasStreamCA = false; while (ES_info_length > 2 && infoBytesRemaining >= 0) { unsigned descriptor_tag = br->getBits(8); @@ -549,12 +552,94 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) { unsigned descTagExt = br->getBits(8); ALOGV(" tag_ext = 0x%02x", descTagExt); + ES_info_length -= descriptor_length; + descriptor_length--; + // The AC4 descriptor is used in the PSI PMT to identify streams which carry AC4 + // audio. if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) { info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4; + br->skipBits(descriptor_length * 8); + } else if (descTagExt == EXT_DESCRIPTOR_DVB_AUDIO_PRESELECTION && + descriptor_length >= 1) { + // DVB BlueBook A038 Table 110 + unsigned num_preselections = br->getBits(5); + br->skipBits(3); // reserved + for (unsigned i = 0; i < num_preselections; ++i) { + if (br->numBitsLeft() < 16) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + AudioPresentationV1 ap; + ap.mPresentationId = br->getBits(5); // preselection_id + + // audio_rendering_indication + ap.mMasteringIndication = static_cast(br->getBits(3)); + ap.mAudioDescriptionAvailable = (br->getBits(1) == 1); + ap.mSpokenSubtitlesAvailable = (br->getBits(1) == 1); + ap.mDialogueEnhancementAvailable = (br->getBits(1) == 1); + + bool interactivity_enabled = (br->getBits(1) == 1); + MY_LOGV(" interactivity_enabled = %d", interactivity_enabled); + + bool language_code_present = (br->getBits(1) == 1); + bool text_label_present = (br->getBits(1) == 1); + + bool multi_stream_info_present = (br->getBits(1) == 1); + bool future_extension = (br->getBits(1) == 1); + if (language_code_present) { + if (br->numBitsLeft() < 24) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + char language[4]; + language[0] = br->getBits(8); + language[1] = br->getBits(8); + language[2] = br->getBits(8); + language[3] = 0; + ap.mLanguage = String8(language); + } + + // This maps the presentation id to the message id in the + // EXT_DESCRIPTOR_DVB_MESSAGE so that we can get the presentation label. + if (text_label_present) { + if (br->numBitsLeft() < 8) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + unsigned message_id = br->getBits(8); + MY_LOGV(" message_id = %u", message_id); + } + + if (multi_stream_info_present) { + if (br->numBitsLeft() < 8) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + unsigned num_aux_components = br->getBits(3); + br->skipBits(5); // reserved + if (br->numBitsLeft() < (num_aux_components * 8)) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + br->skipBits(num_aux_components * 8); // component_tag + } + if (future_extension) { + if (br->numBitsLeft() < 8) { + return ERROR_MALFORMED; + } + br->skipBits(3); // reserved + unsigned future_extension_length = br->getBits(5); + if (br->numBitsLeft() < (future_extension_length * 8)) { + ALOGE("Not enough data left in bitreader!"); + return ERROR_MALFORMED; + } + br->skipBits(future_extension_length * 8); // future_extension_byte + } + info.mAudioPresentations.push_back(std::move(ap)); + } + } else { + br->skipBits(descriptor_length * 8); } - ES_info_length -= descriptor_length; - descriptor_length--; - br->skipBits(descriptor_length * 8); } else { ES_info_length -= descriptor_length; br->skipBits(descriptor_length * 8); @@ -754,7 +839,8 @@ ATSParser::Stream::Stream( mEOSReached(false), mPrevPTS(0), mQueue(NULL), - mScrambled(info.mCADescriptor.mSystemID >= 0) { + mScrambled(info.mCADescriptor.mSystemID >= 0), + mAudioPresentations(info.mAudioPresentations) { mSampleEncrypted = mStreamType == STREAMTYPE_H264_ENCRYPTED || mStreamType == STREAMTYPE_AAC_ENCRYPTED || @@ -1633,6 +1719,7 @@ void ATSParser::Stream::onPayloadData( } mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); + mSource->convertAudioPresentationInfoToMetadata(mAudioPresentations); ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x", mElementaryPID, mStreamType); } diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 9ece21e377..0ff2d7e771 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -163,7 +164,7 @@ struct ATSParser : public RefBase { }; enum { - // From ISO/IEC 13818-1: 2007 (E), Table 2-29 + // From ISO/IEC 13818-1: 2007 (E), Table 2-45 DESCRIPTOR_CA = 0x09, // DVB BlueBook A038 Table 12 @@ -172,8 +173,9 @@ struct ATSParser : public RefBase { // DVB BlueBook A038 Table 109 enum { - EXT_DESCRIPTOR_DVB_AC4 = 0x15, - EXT_DESCRIPTOR_DVB_RESERVED_MAX = 0x7F, + EXT_DESCRIPTOR_DVB_AC4 = 0x15, + EXT_DESCRIPTOR_DVB_AUDIO_PRESELECTION = 0x19, + EXT_DESCRIPTOR_DVB_RESERVED_MAX = 0x7F, }; protected: @@ -196,6 +198,7 @@ private: unsigned mTypeExt; unsigned mPID; CADescriptor mCADescriptor; + AudioPresentationCollection mAudioPresentations; }; sp mCasManager; diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 9e154a3d2c..e2c50314fb 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -697,4 +697,23 @@ sp AnotherPacketSource::trimBuffersBeforeMeta( return firstMeta; } +void AnotherPacketSource::convertAudioPresentationInfoToMetadata( + const AudioPresentationCollection& presentations) { + sp meta = getFormat(); + if (meta == NULL) { + return; + } + if (presentations.empty()) { + // Clear audio presentation info in metadata. + Mutex::Autolock autoLock(mLock); + meta->remove(kKeyAudioPresentationInfo); + } else { + std::ostringstream outStream(std::ios::out); + serializeAudioPresentations(presentations, &outStream); + Mutex::Autolock autoLock(mLock); + meta->setData(kKeyAudioPresentationInfo, MetaData::TYPE_NONE, + outStream.str().data(), outStream.str().size()); + } +} + } // namespace android diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index f4a6acbe27..57a6c33981 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -85,6 +85,8 @@ struct AnotherPacketSource : public MediaSource { void trimBuffersAfterMeta(const sp &meta); sp trimBuffersBeforeMeta(const sp &meta); + void convertAudioPresentationInfoToMetadata(const AudioPresentationCollection &presentations); + protected: virtual ~AnotherPacketSource(); diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index b282ed8e19..bf9725c1a5 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -272,6 +272,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_ALBUM = "album"; EXPORT const char* AMEDIAFORMAT_KEY_ALBUMART = "albumart"; EXPORT const char* AMEDIAFORMAT_KEY_ALBUMARTIST = "albumartist"; EXPORT const char* AMEDIAFORMAT_KEY_ARTIST = "artist"; +EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO = "audio-presentation-info"; EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_SESSION_ID = "audio-session-id"; EXPORT const char* AMEDIAFORMAT_KEY_AUTHOR = "author"; EXPORT const char* AMEDIAFORMAT_KEY_BITRATE_MODE = "bitrate-mode"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 89cfd5e04e..658cbac2ba 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -180,6 +180,7 @@ extern const char* AMEDIAFORMAT_KEY_ALBUM __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ALBUMART __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ALBUMARTIST __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ARTIST __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29); -- GitLab From b4ca5ee2d890e68eb7eaa5fab1875bfac5d680a5 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 26 Nov 2018 17:47:11 -0800 Subject: [PATCH 0521/1530] Ignore some integer overflows the IDCT routine triggers signed integer overflows occasionally. This disables the traps for idctrow_intra() Bug: 70898652 Test: poc on oc-dev --- media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp index a75483a3dd..3d1008625e 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp @@ -617,6 +617,7 @@ void idctrow( return; } +__attribute__((no_sanitize("signed-integer-overflow"))) void idctrow_intra( int16 *blk, PIXEL *comp, int width ) -- GitLab From 5a593a1868df2261f44e0de163fde53ad2dc28f0 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 27 Nov 2018 06:19:16 +0000 Subject: [PATCH 0522/1530] Revert "Replace hidden AudioManager.FLAG_FROM_KEY usage" This reverts commit 03e1a19e0ba734994f9565b0f7dc588e4d7ae804. Reason for revert: We'd better not to copy the hidden APIs, but instead we need to make them public or @SystemApi. Change-Id: I53db4247a2fa9d949747588a73fe75c8f2dbb716 --- .../MediaComponents/apex/java/android/media/MediaUtils.java | 6 ------ .../apex/java/android/media/session/MediaController.java | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/MediaUtils.java b/packages/MediaComponents/apex/java/android/media/MediaUtils.java index 0f0cb002b6..282c2185b3 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaUtils.java +++ b/packages/MediaComponents/apex/java/android/media/MediaUtils.java @@ -7,12 +7,6 @@ import android.view.KeyEvent; */ public class MediaUtils { - /** - * Adjusting the volume due to a hardware key press. - * (Copied version of hidden AudioManager.FLAG_FROM_KEY) - */ - public static final int AUDIO_MANAGER_FLAG_FROM_KEY = 1 << 12; - // Keep sync with KeyEvent#isMediaKey(). public static boolean isMediaKey(int keyCode) { switch (keyCode) { diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index fab57e394d..1a2a5281ee 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -197,8 +197,9 @@ public final class MediaController { } case KeyEvent.ACTION_UP: { - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE - | MediaUtils.AUDIO_MANAGER_FLAG_FROM_KEY; + //TODO(b/119790339): Resolve hidden API usage. AudioManager.FLAG_FROM_KEY + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; + //| AudioManager.FLAG_FROM_KEY; try { mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0, flags); -- GitLab From 78c972934e738a199df1ea9e5f52f4ccf7ba66f0 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 27 Nov 2018 07:40:19 +0000 Subject: [PATCH 0523/1530] Revert "Replace hidden KeyEvent.isMediaKey() usages" This reverts commit c7c1542ea3fef663c2395404e6f111dc01e625b4. Bug: 119789707 Reason for revert: We'd better not to copy the hidden APIs, but instead we need to make them public or @SystemApi. Change-Id: I4083d533f863cdda8b6de05cdea333082b84ec97 --- .../apex/java/android/media/MediaUtils.java | 28 ------------------- .../media/session/MediaController.java | 6 ++-- 2 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaUtils.java diff --git a/packages/MediaComponents/apex/java/android/media/MediaUtils.java b/packages/MediaComponents/apex/java/android/media/MediaUtils.java deleted file mode 100644 index 282c2185b3..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package android.media; - -import android.view.KeyEvent; - -/** - * @hide - */ -public class MediaUtils { - - // Keep sync with KeyEvent#isMediaKey(). - public static boolean isMediaKey(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - return true; - } - return false; - } -} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 1a2a5281ee..1f2918595d 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -25,7 +25,6 @@ import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaMetadata; -import android.media.MediaUtils; import android.media.Rating; import android.media.VolumeProvider; import android.net.Uri; @@ -150,9 +149,12 @@ public final class MediaController { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } - if (!MediaUtils.isMediaKey(keyEvent.getKeyCode())) { + //TODO(b/119789707): Resolve hidden API usage: KeyEvent#isMediaKey + /* + if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { return false; } + */ try { //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file. // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling. -- GitLab From 8c8b57a94840bbcf4852eb97195c83bfb8b05bf3 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 28 Nov 2018 11:09:50 -0800 Subject: [PATCH 0524/1530] NuPlayer2: fix looper name Test: cts Bug: 112549021 Change-Id: Ied2b37b4812722aacef634be318c79f868c9f692 --- media/libmediaplayer2/nuplayer2/GenericSource2.cpp | 2 +- media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp | 2 +- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 2 +- media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp | 2 +- media/libmediaplayer2/nuplayer2/RTSPSource2.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp index 1860b0ca1a..f01361b8b0 100644 --- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp @@ -322,7 +322,7 @@ void NuPlayer2::GenericSource2::prepareAsync(int64_t startTimeUs) { if (mLooper == NULL) { mLooper = new ALooper; - mLooper->setName("generic"); + mLooper->setName("generic2"); mLooper->start(false, /* runOnCallingThread */ true, /* canCallJava */ PRIORITY_DEFAULT); diff --git a/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp b/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp index 175be5333d..2ea55f664f 100644 --- a/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp @@ -103,7 +103,7 @@ status_t NuPlayer2::HTTPLiveSource2::setBufferingSettings(const BufferingSetting void NuPlayer2::HTTPLiveSource2::prepareAsync(int64_t /* startTimeUs */) { if (mLiveLooper == NULL) { mLiveLooper = new ALooper; - mLiveLooper->setName("http live"); + mLiveLooper->setName("http live2"); mLiveLooper->start(false, /* runOnCallingThread */ true /* canCallJava */); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 5ea600f180..5613aeaf9c 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -1719,7 +1719,7 @@ void NuPlayer2::onStart(bool play) { notify->setInt32("generation", mRendererGeneration); mRenderer = new Renderer(mAudioSink, mMediaClock, notify, flags); mRendererLooper = new ALooper; - mRendererLooper->setName("NuPlayerRenderer"); + mRendererLooper->setName("NuPlayer2Renderer"); mRendererLooper->start(false, true, ANDROID_PRIORITY_AUDIO); mRendererLooper->registerHandler(mRenderer); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp index 9c1988f499..1f1b69e12a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp @@ -38,7 +38,7 @@ NuPlayer2::DecoderBase::DecoderBase(const sp ¬ify) // Every decoder has its own looper because MediaCodec operations // are blocking, but NuPlayer2 needs asynchronous operations. mDecoderLooper = new ALooper; - mDecoderLooper->setName("NPDecoder"); + mDecoderLooper->setName("NP2Decoder"); mDecoderLooper->start(false, /* runOnCallingThread */ true, /* canCallJava */ ANDROID_PRIORITY_AUDIO); diff --git a/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp b/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp index 53c66c0175..aed925b309 100644 --- a/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp @@ -105,7 +105,7 @@ void NuPlayer2::RTSPSource2::prepareAsync(int64_t /* startTimeUs */) { if (mLooper == NULL) { mLooper = new ALooper; - mLooper->setName("rtsp"); + mLooper->setName("rtsp2"); mLooper->start(); mLooper->registerHandler(this); -- GitLab From ac917aca235ff5170f00c2ee933f41f07a40232d Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 28 Nov 2018 14:03:52 -0800 Subject: [PATCH 0525/1530] Add selectPresentation API to IAudioTrack The Java AudioTrack interface has a setPresentation API. This calls the setParameters API in IAudioTrack. However, this does not eventuate into a call into the android.hardware.audio@4.0 IStreamOut selectPresentation API. Add selectPresentation API to IAudioTrack and call down to the android.hardware.audio@4.0 IStreamOut selectPresentation API. Translate into calls to setParameters API for legacy HAL interfaces. Bug: 63901775 Test: compile Change-Id: Id634a107f3f93ab18dc80d2bd0af67bda35e859f --- media/libaudioclient/AudioTrack.cpp | 9 ++------- media/libaudioclient/IAudioTrack.cpp | 19 +++++++++++++++++++ .../include/media/IAudioTrack.h | 3 +++ media/libaudiohal/impl/StreamHalHidl.cpp | 19 +++++++++++++++++++ media/libaudiohal/impl/StreamHalHidl.h | 3 +++ media/libaudiohal/impl/StreamHalLocal.cpp | 8 ++++++++ media/libaudiohal/impl/StreamHalLocal.h | 3 +++ .../media/audiohal/StreamHalInterface.h | 3 +++ services/audioflinger/AudioFlinger.h | 1 + services/audioflinger/PlaybackTracks.h | 1 + services/audioflinger/Threads.cpp | 8 ++++++++ services/audioflinger/Threads.h | 2 ++ services/audioflinger/Tracks.cpp | 17 +++++++++++++++++ 13 files changed, 89 insertions(+), 7 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index df9aea67eb..8607ee1eca 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -2452,13 +2452,8 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) status_t AudioTrack::selectPresentation(int presentationId, int programId) { AutoMutex lock(mLock); - AudioParameter param = AudioParameter(); - param.addInt(String8(AudioParameter::keyPresentationId), presentationId); - param.addInt(String8(AudioParameter::keyProgramId), programId); - ALOGV("%s(%d): PresentationId/ProgramId[%s]", - __func__, mId, param.toString().string()); - - return mAudioTrack->setParameters(param.toString()); + ALOGV("%s(%d): PresentationId:%d ProgramId:%d", __func__, mId, presentationId, programId); + return mAudioTrack->selectPresentation(presentationId, programId); } VolumeShaper::Status AudioTrack::applyVolumeShaper( diff --git a/media/libaudioclient/IAudioTrack.cpp b/media/libaudioclient/IAudioTrack.cpp index adff05787a..83a568a554 100644 --- a/media/libaudioclient/IAudioTrack.cpp +++ b/media/libaudioclient/IAudioTrack.cpp @@ -39,6 +39,7 @@ enum { PAUSE, ATTACH_AUX_EFFECT, SET_PARAMETERS, + SELECT_PRESENTATION, GET_TIMESTAMP, SIGNAL, APPLY_VOLUME_SHAPER, @@ -127,6 +128,19 @@ public: return status; } + /* Selects the presentation (if available) */ + virtual status_t selectPresentation(int presentationId, int programId) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeInt32(presentationId); + data.writeInt32(programId); + status_t status = remote()->transact(SELECT_PRESENTATION, data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } + return status; + } + virtual status_t getTimestamp(AudioTimestamp& timestamp) { Parcel data, reply; data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); @@ -239,6 +253,11 @@ status_t BnAudioTrack::onTransact( reply->writeInt32(setParameters(keyValuePairs)); return NO_ERROR; } break; + case SELECT_PRESENTATION: { + CHECK_INTERFACE(IAudioTrack, data, reply); + reply->writeInt32(selectPresentation(data.readInt32(), data.readInt32())); + return NO_ERROR; + } break; case GET_TIMESTAMP: { CHECK_INTERFACE(IAudioTrack, data, reply); AudioTimestamp timestamp; diff --git a/media/libaudioclient/include/media/IAudioTrack.h b/media/libaudioclient/include/media/IAudioTrack.h index 94afe3ce66..06e786d294 100644 --- a/media/libaudioclient/include/media/IAudioTrack.h +++ b/media/libaudioclient/include/media/IAudioTrack.h @@ -70,6 +70,9 @@ public: /* Send parameters to the audio hardware */ virtual status_t setParameters(const String8& keyValuePairs) = 0; + /* Selects the presentation (if available) */ + virtual status_t selectPresentation(int presentationId, int programId) = 0; + /* Return NO_ERROR if timestamp is valid. timestamp is undefined otherwise. */ virtual status_t getTimestamp(AudioTimestamp& timestamp) = 0; diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index bfa80e8c71..5ac2d9af08 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -347,6 +348,24 @@ status_t StreamOutHalHidl::setVolume(float left, float right) { return processReturn("setVolume", mStream->setVolume(left, right)); } +#if MAJOR_VERSION == 2 +status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) { + if (mStream == 0) return NO_INIT; + std::vector parameters; + String8 halParameters; + parameters.push_back({AudioParameter::keyPresentationId, std::to_string(presentationId)}); + parameters.push_back({AudioParameter::keyProgramId, std::to_string(programId)}); + parametersToHal(hidl_vec(parameters), &halParameters); + return setParameters(halParameters); +} +#elif MAJOR_VERSION == 4 +status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) { + if (mStream == 0) return NO_INIT; + return processReturn("selectPresentation", + mStream->selectPresentation(presentationId, programId)); +} +#endif + status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) { if (mStream == 0) return NO_INIT; *written = 0; diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h index 95ec7f1d17..74101d7964 100644 --- a/media/libaudiohal/impl/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -131,6 +131,9 @@ class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl { // Use this method in situations where audio mixing is done in the hardware. virtual status_t setVolume(float left, float right); + // Selects the audio presentation (if available). + virtual status_t selectPresentation(int presentationId, int programId); + // Write audio buffer to driver. virtual status_t write(const void *buffer, size_t bytes, size_t *written); diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp index b134f5797c..0aba7c119b 100644 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #include +#include #include #include "DeviceHalLocal.h" @@ -138,6 +139,13 @@ status_t StreamOutHalLocal::setVolume(float left, float right) { return mStream->set_volume(mStream, left, right); } +status_t StreamOutHalLocal::selectPresentation(int presentationId, int programId) { + AudioParameter param; + param.addInt(String8(AudioParameter::keyPresentationId), presentationId); + param.addInt(String8(AudioParameter::keyProgramId), programId); + return setParameters(param.toString()); +} + status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) { ssize_t writeResult = mStream->write(mStream, buffer, bytes); if (writeResult > 0) { diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h index cea4229aa0..4fd1960887 100644 --- a/media/libaudiohal/impl/StreamHalLocal.h +++ b/media/libaudiohal/impl/StreamHalLocal.h @@ -103,6 +103,9 @@ class StreamOutHalLocal : public StreamOutHalInterface, public StreamHalLocal { // Use this method in situations where audio mixing is done in the hardware. virtual status_t setVolume(float left, float right); + // Selects the audio presentation (if available). + virtual status_t selectPresentation(int presentationId, int programId); + // Write audio buffer to driver. virtual status_t write(const void *buffer, size_t bytes, size_t *written); diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h index c969e2853c..bd71dc0986 100644 --- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h @@ -109,6 +109,9 @@ class StreamOutHalInterface : public virtual StreamHalInterface { // Use this method in situations where audio mixing is done in the hardware. virtual status_t setVolume(float left, float right) = 0; + // Selects the audio presentation (if available). + virtual status_t selectPresentation(int presentationId, int programId) = 0; + // Write audio buffer to driver. virtual status_t write(const void *buffer, size_t bytes, size_t *written) = 0; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index b6b381593b..1b20693d31 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -553,6 +553,7 @@ using effect_buffer_t = int16_t; virtual void pause(); virtual status_t attachAuxEffect(int effectId); virtual status_t setParameters(const String8& keyValuePairs); + virtual status_t selectPresentation(int presentationId, int programId); virtual media::VolumeShaper::Status applyVolumeShaper( const sp& configuration, const sp& operation) override; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 53ea9a43b0..971f6a5077 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -67,6 +67,7 @@ public: bool isStatic() const { return mSharedBuffer.get() != nullptr; } status_t setParameters(const String8& keyValuePairs); + status_t selectPresentation(int presentationId, int programId); status_t attachAuxEffect(int EffectId); void setAuxBuffer(int EffectId, int32_t *buffer); int32_t *auxBuffer() const { return mAuxBuffer; } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f833cf7bf2..cec08196ee 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2413,6 +2413,14 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) return String8(); } +status_t AudioFlinger::DirectOutputThread::selectPresentation(int presentationId, int programId) { + Mutex::Autolock _l(mLock); + if (mOutput == nullptr || mOutput->stream == nullptr) { + return NO_INIT; + } + return mOutput->stream->selectPresentation(presentationId, programId); +} + void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { sp desc = new AudioIoDescriptor(); ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 49fc2345f1..7f3ea0f4e7 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1199,6 +1199,8 @@ public: audio_io_handle_t id, audio_devices_t device, bool systemReady); virtual ~DirectOutputThread(); + status_t selectPresentation(int presentationId, int programId); + // Thread virtuals virtual bool checkForNewParameter_l(const String8& keyValuePair, diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index f2617ae730..91a3286892 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -326,6 +326,10 @@ status_t AudioFlinger::TrackHandle::setParameters(const String8& keyValuePairs) return mTrack->setParameters(keyValuePairs); } +status_t AudioFlinger::TrackHandle::selectPresentation(int presentationId, int programId) { + return mTrack->selectPresentation(presentationId, programId); +} + VolumeShaper::Status AudioFlinger::TrackHandle::applyVolumeShaper( const sp& configuration, const sp& operation) { @@ -976,6 +980,19 @@ status_t AudioFlinger::PlaybackThread::Track::setParameters(const String8& keyVa } } +status_t AudioFlinger::PlaybackThread::Track::selectPresentation(int presentationId, + int programId) { + sp thread = mThread.promote(); + if (thread == 0) { + ALOGE("thread is dead"); + return FAILED_TRANSACTION; + } else if ((thread->type() == ThreadBase::DIRECT) || (thread->type() == ThreadBase::OFFLOAD)) { + DirectOutputThread *directOutputThread = static_cast(thread.get()); + return directOutputThread->selectPresentation(presentationId, programId); + } + return INVALID_OPERATION; +} + VolumeShaper::Status AudioFlinger::PlaybackThread::Track::applyVolumeShaper( const sp& configuration, const sp& operation) -- GitLab From 95213bf7d0d5960cd76e64a44b59b49637fefd22 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 8 Nov 2018 17:16:57 -0800 Subject: [PATCH 0526/1530] libaudiohal: Factorize more between version Use macro in include to avoid duplicating all the headers. That will be useful when a third version is added. Bug: 118203066 Test: compile Change-Id: I8972b9ac9d098b32c9e7de48ff0544440c29adbf Signed-off-by: Kevin Rocard --- media/libaudiohal/impl/ConversionHelperHidl.h | 3 +-- media/libaudiohal/impl/DeviceHalHidl.cpp | 3 +-- media/libaudiohal/impl/DeviceHalHidl.h | 6 ++---- media/libaudiohal/impl/DevicesFactoryHalHidl.cpp | 3 +-- media/libaudiohal/impl/DevicesFactoryHalHidl.h | 3 +-- media/libaudiohal/impl/EffectBufferHalHidl.h | 3 +-- media/libaudiohal/impl/EffectHalHidl.h | 3 +-- media/libaudiohal/impl/EffectsFactoryHalHidl.h | 6 ++---- media/libaudiohal/impl/StreamHalHidl.cpp | 3 +-- media/libaudiohal/impl/StreamHalHidl.h | 9 +++------ media/libaudiohal/impl/VersionMacro.h | 9 +++++++++ media/libaudiohal/impl/VersionUtils.h | 3 +-- 12 files changed, 24 insertions(+), 30 deletions(-) diff --git a/media/libaudiohal/impl/ConversionHelperHidl.h b/media/libaudiohal/impl/ConversionHelperHidl.h index 1a9319f632..52a14564a7 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.h +++ b/media/libaudiohal/impl/ConversionHelperHidl.h @@ -17,8 +17,7 @@ #ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H #define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/types.h) #include #include #include diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index 723e2eb127..e0947ff2e9 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -19,8 +19,7 @@ #define LOG_TAG "DeviceHalHidl" //#define LOG_NDEBUG 0 -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h) #include #include #include diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h index fb5e7e7302..291c88fbe8 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -17,10 +17,8 @@ #ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_H #define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H -#include -#include -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h) +#include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h) #include #include "ConversionHelperHidl.h" diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp index 28001dafdf..5d97000b7d 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp @@ -20,8 +20,7 @@ #define LOG_TAG "DevicesFactoryHalHidl" //#define LOG_NDEBUG 0 -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h) #include #include diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.h b/media/libaudiohal/impl/DevicesFactoryHalHidl.h index a4282b0713..27e0649346 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.h @@ -17,8 +17,7 @@ #ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H #define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h) #include #include #include diff --git a/media/libaudiohal/impl/EffectBufferHalHidl.h b/media/libaudiohal/impl/EffectBufferHalHidl.h index 029d71a74c..0c99a02f2c 100644 --- a/media/libaudiohal/impl/EffectBufferHalHidl.h +++ b/media/libaudiohal/impl/EffectBufferHalHidl.h @@ -17,8 +17,7 @@ #ifndef ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H #define ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H -#include -#include +#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h) #include #include #include diff --git a/media/libaudiohal/impl/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h index 04f40d36f8..cd447ff3e2 100644 --- a/media/libaudiohal/impl/EffectHalHidl.h +++ b/media/libaudiohal/impl/EffectHalHidl.h @@ -17,8 +17,7 @@ #ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_H #define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H -#include -#include +#include PATH(android/hardware/audio/effect/FILE_VERSION/IEffect.h) #include #include #include diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h index c6fced7104..702715367b 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h @@ -17,10 +17,8 @@ #ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H #define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H -#include -#include -#include -#include +#include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h) +#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h) #include #include "ConversionHelperHidl.h" diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index bfa80e8c71..f0ca384cea 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -17,8 +17,7 @@ #define LOG_TAG "StreamHalHidl" //#define LOG_NDEBUG 0 -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IStreamOutCallback.h) #include #include #include diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h index 95ec7f1d17..c7bfc45409 100644 --- a/media/libaudiohal/impl/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -19,12 +19,9 @@ #include -#include -#include -#include -#include -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/IStream.h) +#include PATH(android/hardware/audio/FILE_VERSION/IStreamIn.h) +#include PATH(android/hardware/audio/FILE_VERSION/IStreamOut.h) #include #include #include diff --git a/media/libaudiohal/impl/VersionMacro.h b/media/libaudiohal/impl/VersionMacro.h index 98e9c07eed..68fc9f6169 100644 --- a/media/libaudiohal/impl/VersionMacro.h +++ b/media/libaudiohal/impl/VersionMacro.h @@ -21,6 +21,15 @@ #error "MAJOR_VERSION and MINOR_VERSION must be defined" #endif +/** Allows macro expansion for x and add surrounding `<>`. + * Is intended to be used for version dependant includes as + * `#include` do not macro expand if starting with < or " + * Example usage: + * #include PATH(path/to/FILE_VERSION/file) + * @note: uses the implementation-define "Computed Includes" feature. + */ +#define PATH(x) + #define CONCAT_3(a,b,c) a##b##c #define EXPAND_CONCAT_3(a,b,c) CONCAT_3(a,b,c) /** The directory name of the version: . */ diff --git a/media/libaudiohal/impl/VersionUtils.h b/media/libaudiohal/impl/VersionUtils.h index 500489594f..b764e40b0a 100644 --- a/media/libaudiohal/impl/VersionUtils.h +++ b/media/libaudiohal/impl/VersionUtils.h @@ -17,8 +17,7 @@ #ifndef ANDROID_HARDWARE_VERSION_UTILS_H #define ANDROID_HARDWARE_VERSION_UTILS_H -#include -#include +#include PATH(android/hardware/audio/FILE_VERSION/types.h) #include using ::android::hardware::audio::CPP_VERSION::ParameterValue; -- GitLab From bb165dcf6bfb61dc0a1a09a73d52af7cfe127ed2 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Sat, 10 Nov 2018 06:28:41 -0800 Subject: [PATCH 0527/1530] libaudiohal: Factorize with audio HAL utils Use VersionMacro from the HAL utils. Bug: 118203066 Test: compile Change-Id: If74b73eef7735d3f5a0a4b135a0476c4a77daddc Signed-off-by: Kevin Rocard --- media/libaudiohal/impl/Android.bp | 4 +-- media/libaudiohal/impl/VersionMacro.h | 43 --------------------------- 2 files changed, 2 insertions(+), 45 deletions(-) delete mode 100644 media/libaudiohal/impl/VersionMacro.h diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp index 3827336302..e86d924eb5 100644 --- a/media/libaudiohal/impl/Android.bp +++ b/media/libaudiohal/impl/Android.bp @@ -68,7 +68,7 @@ cc_library_shared { cflags: [ "-DMAJOR_VERSION=2", "-DMINOR_VERSION=0", - "-include VersionMacro.h", + "-include common/all-versions/VersionMacro.h", ] } @@ -81,6 +81,6 @@ cc_library_shared { cflags: [ "-DMAJOR_VERSION=4", "-DMINOR_VERSION=0", - "-include VersionMacro.h", + "-include common/all-versions/VersionMacro.h", ] } diff --git a/media/libaudiohal/impl/VersionMacro.h b/media/libaudiohal/impl/VersionMacro.h deleted file mode 100644 index 68fc9f6169..0000000000 --- a/media/libaudiohal/impl/VersionMacro.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_HARDWARE_VERSION_MACRO_H -#define ANDROID_HARDWARE_VERSION_MACRO_H - -#if !defined(MAJOR_VERSION) || !defined(MINOR_VERSION) -#error "MAJOR_VERSION and MINOR_VERSION must be defined" -#endif - -/** Allows macro expansion for x and add surrounding `<>`. - * Is intended to be used for version dependant includes as - * `#include` do not macro expand if starting with < or " - * Example usage: - * #include PATH(path/to/FILE_VERSION/file) - * @note: uses the implementation-define "Computed Includes" feature. - */ -#define PATH(x) - -#define CONCAT_3(a,b,c) a##b##c -#define EXPAND_CONCAT_3(a,b,c) CONCAT_3(a,b,c) -/** The directory name of the version: . */ -#define FILE_VERSION EXPAND_CONCAT_3(MAJOR_VERSION,.,MINOR_VERSION) - -#define CONCAT_4(a,b,c,d) a##b##c##d -#define EXPAND_CONCAT_4(a,b,c,d) CONCAT_4(a,b,c,d) -/** The c++ namespace of the version: V_ */ -#define CPP_VERSION EXPAND_CONCAT_4(V,MAJOR_VERSION,_,MINOR_VERSION) - -#endif // ANDROID_HARDWARE_VERSION_MACRO_H -- GitLab From 7a9f05a68512926f81339d3e42def2ecd9adffa0 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 28 Nov 2018 16:52:25 -0800 Subject: [PATCH 0528/1530] libaudiohal: EnumConverter renamed to EnumBitfield EnumConverter scope was reduced to just convert to/from bitfield and its underlying type. This allows for operator= conversion support. As part of this rework and to better express the new class behaviour, it was renamed as EnumBitfield. Test: compile Bug: 118203066 Change-Id: I2dc108cb3e1757e685490cae978cb62cbf276423 Signed-off-by: Kevin Rocard --- media/libaudiohal/impl/DeviceHalHidl.cpp | 6 +++--- media/libaudiohal/impl/EffectHalHidl.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index e0947ff2e9..ce806bfd05 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -41,7 +41,7 @@ using ::android::hardware::audio::common::CPP_VERSION::AudioPortConfig; using ::android::hardware::audio::common::CPP_VERSION::AudioMode; using ::android::hardware::audio::common::CPP_VERSION::AudioSource; using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; -using ::android::hardware::audio::common::utils::mkEnumConverter; +using ::android::hardware::audio::common::utils::EnumBitfield; using ::android::hardware::audio::CPP_VERSION::DeviceAddress; using ::android::hardware::audio::CPP_VERSION::IPrimaryDevice; using ::android::hardware::audio::CPP_VERSION::ParameterValue; @@ -261,7 +261,7 @@ status_t DeviceHalHidl::openOutputStream( handle, hidlDevice, hidlConfig, - mkEnumConverter(flags), + EnumBitfield(flags), #if MAJOR_VERSION == 4 {} /* metadata */, #endif @@ -301,7 +301,7 @@ status_t DeviceHalHidl::openInputStream( handle, hidlDevice, hidlConfig, - mkEnumConverter(flags), + EnumBitfield(flags), sourceMetadata, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp index 12649a1f13..7b867b48c9 100644 --- a/media/libaudiohal/impl/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -34,7 +34,7 @@ using ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; using ::android::hardware::audio::common::CPP_VERSION::AudioChannelMask; using ::android::hardware::audio::common::CPP_VERSION::AudioFormat; -using ::android::hardware::audio::common::utils::mkEnumConverter; +using ::android::hardware::audio::common::utils::EnumBitfield; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; @@ -77,10 +77,10 @@ void EffectHalHidl::effectDescriptorToHal( void EffectHalHidl::effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config) { config->samplingRateHz = halConfig.samplingRate; - config->channels = mkEnumConverter(halConfig.channels); + config->channels = EnumBitfield(halConfig.channels); config->format = AudioFormat(halConfig.format); config->accessMode = EffectBufferAccess(halConfig.accessMode); - config->mask = mkEnumConverter(halConfig.mask); + config->mask = EnumBitfield(halConfig.mask); } // static -- GitLab From 70b92a4301340712a5abc682dab1f5721ea25ae8 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Fri, 23 Nov 2018 11:46:51 +0900 Subject: [PATCH 0529/1530] AML: Use config_mediaMetadataBitmapMaxSize which is @SystemApi Bug: 119749798 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: Ib4f46c9adb61f6d6bdacdba43e706c8a1a735ab1 --- .../apex/java/android/media/session/MediaSession.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index b40e3b0659..deb02035c9 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -171,10 +171,8 @@ public final class MediaSession { if (TextUtils.isEmpty(tag)) { throw new IllegalArgumentException("tag cannot be null or empty"); } - //TODO(b/119749798): Resolve hidden API usage. com.android.internal.R - //mMaxBitmapSize = context.getResources().getDimensionPixelSize( - //com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize); - mMaxBitmapSize = 1024; //TODO: remove this. + mMaxBitmapSize = context.getResources().getDimensionPixelSize( + android.R.dimen.config_mediaMetadataBitmapMaxSize); mCbStub = new CallbackStub(this); MediaSessionManager manager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); -- GitLab From 2091949018103af337a5cd48de789d51fa3a6dc0 Mon Sep 17 00:00:00 2001 From: Nadav Bar Date: Tue, 20 Nov 2018 10:20:51 +0200 Subject: [PATCH 0530/1530] Support playing to uplink stream using the VOICE_CALL stream as well Added support for playing audio to the uplink stream also using the VOICE_CALL stream. This will ensure the uplink playback have the same characteristics as other audio which is being played back during a call, and can by pass the DND mute. Change-Id: I1830bebb5fca724de561fbde8445fcee95f9c930 Test: tested manually. Bug: 117866278. --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 58acad3b89..6fe7fc1116 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -840,8 +840,9 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, // chosen by the engine if not. // FIXME: provide a more generic approach which is not device specific and move this back // to getOutputForDevice. + // TODO: Remove check of AUDIO_STREAM_MUSIC once migration is completed on the app side. if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX && - *stream == AUDIO_STREAM_MUSIC && + (*stream == AUDIO_STREAM_MUSIC || attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) && audio_is_linear_pcm(config->format) && isInCall()) { if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { @@ -930,7 +931,8 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if (stream == AUDIO_STREAM_TTS) { *flags = AUDIO_OUTPUT_FLAG_TTS; } else if (stream == AUDIO_STREAM_VOICE_CALL && - audio_is_linear_pcm(config->format)) { + audio_is_linear_pcm(config->format) && + (*flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) == 0) { *flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_VOIP_RX | AUDIO_OUTPUT_FLAG_DIRECT); ALOGV("Set VoIP and Direct output flags for PCM format"); -- GitLab From 02743e21156fa3063dc3718982e291cf60ec37cd Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 28 Nov 2018 15:56:55 -0800 Subject: [PATCH 0531/1530] Audio policy: Use surround formats from audio config instead of hardcoded This is a follow-up to ag/3835377, where the intention was to use SURROND_FORMATS array, which is lately been replaced by an array from APM's config. Bug: 67479735 Bug: 117602867 Test: make Change-Id: I31f63856d8fb06ff9ac0f03b9ec1600f0fcee30b --- .../managerdefault/AudioPolicyManager.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 61405bc72a..18c5567b28 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5895,18 +5895,8 @@ void AudioPolicyManager::filterSurroundFormats(FormatVector *formatsPtr) { // Add reported surround sound formats to enabled surround formats. for (size_t formatIndex = 0; formatIndex < formats.size(); formatIndex++) { audio_format_t format = formats[formatIndex]; - switch(format) { - case AUDIO_FORMAT_AC3: - case AUDIO_FORMAT_E_AC3: - case AUDIO_FORMAT_DTS: - case AUDIO_FORMAT_DTS_HD: - case AUDIO_FORMAT_AAC_LC: - case AUDIO_FORMAT_DOLBY_TRUEHD: - case AUDIO_FORMAT_E_AC3_JOC: - mSurroundFormats.insert(format); - break; - default: - break; + if (mConfig.getSurroundFormats().count(format) != 0) { + mSurroundFormats.insert(format); } } } -- GitLab From d5c7eec288e30dec656d44d6c9e2534ae7f16323 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 29 Nov 2018 16:33:00 -0800 Subject: [PATCH 0532/1530] C++17 compatibility: add missing `constexpr`. Fixes this error when we move the global default to C++17: hardware/google/av/codec2/tests/C2_test.cpp:142:19: error: static_assert expression is not an integral constant expression static_assert(c2_cntr32_t(1) == c2_cntr32_t(c2_cntr64_t(0x100000001ul)), "1 == 1"); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Bug: http://b/111067277 Test: builds Change-Id: I66b8573d5dbdb02f953ec54b77eff274333c6f83 --- media/codec2/core/include/C2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/codec2/core/include/C2.h b/media/codec2/core/include/C2.h index 8a55f8de89..ef3466d844 100644 --- a/media/codec2/core/include/C2.h +++ b/media/codec2/core/include/C2.h @@ -289,7 +289,7 @@ public: * Convert to a smaller counter type. This is always safe. */ template::type> - inline operator c2_cntr_t() { + inline constexpr operator c2_cntr_t() { return c2_cntr_t(mValue); } -- GitLab From 039224f18894843ea9395d474c0d2051d036f329 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Thu, 29 Nov 2018 17:25:17 -0800 Subject: [PATCH 0533/1530] NuPlayer: add missing getSelectedTrack in CCDecoder Test: cts Bug: 119938763 Change-Id: I0dcc35ec65f0912d43aa5b0e3a797334d2581a78 --- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 27 +++++++++++++------ .../nuplayer2/NuPlayer2CCDecoder.cpp | 12 +++++++++ .../nuplayer2/NuPlayer2CCDecoder.h | 1 + .../nuplayer/NuPlayer.cpp | 27 +++++++++++++------ .../nuplayer/NuPlayerCCDecoder.cpp | 12 +++++++++ .../nuplayer/NuPlayerCCDecoder.h | 1 + 6 files changed, 64 insertions(+), 16 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 5ea600f180..b487c013da 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -825,20 +825,31 @@ void NuPlayer2::onMessageReceived(const sp &msg) { case kWhatGetSelectedTrack: { + int32_t type32; + CHECK(msg->findInt32("type", (int32_t*)&type32)); + media_track_type type = (media_track_type)type32; + + size_t inbandTracks = 0; status_t err = INVALID_OPERATION; + ssize_t selectedTrack = -1; if (mCurrentSourceInfo.mSource != NULL) { err = OK; + inbandTracks = mCurrentSourceInfo.mSource->getTrackCount(); + selectedTrack = mCurrentSourceInfo.mSource->getSelectedTrack(type); + } - int32_t type32; - CHECK(msg->findInt32("type", (int32_t*)&type32)); - media_track_type type = (media_track_type)type32; - ssize_t selectedTrack = mCurrentSourceInfo.mSource->getSelectedTrack(type); - - PlayerMessage* reply; - CHECK(msg->findPointer("reply", (void**)&reply)); - reply->add_values()->set_int32_value(selectedTrack); + if (selectedTrack == -1 && mCCDecoder != NULL) { + err = OK; + selectedTrack = mCCDecoder->getSelectedTrack(type); + if (selectedTrack != -1) { + selectedTrack += inbandTracks; + } } + PlayerMessage* reply; + CHECK(msg->findPointer("reply", (void**)&reply)); + reply->add_values()->set_int32_value(selectedTrack); + sp response = new AMessage; response->setInt32("err", err); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp index e2159659f5..a9f21045ec 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp @@ -199,6 +199,18 @@ status_t NuPlayer2::CCDecoder::selectTrack(size_t index, bool select) { return OK; } +ssize_t NuPlayer2::CCDecoder::getSelectedTrack(media_track_type type) const { + if (mSelectedTrack != -1) { + CCTrack track = mTracks[mSelectedTrack]; + if (track.mTrackType == kTrackTypeCEA608 || track.mTrackType == kTrackTypeCEA708) { + return (type == MEDIA_TRACK_TYPE_SUBTITLE ? mSelectedTrack : -1); + } + return (type == MEDIA_TRACK_TYPE_UNKNOWN ? mSelectedTrack : -1); + } + + return -1; +} + bool NuPlayer2::CCDecoder::isSelected() const { return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)getTrackCount(); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h index 57d5ea27c4..97834d1bd6 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h @@ -38,6 +38,7 @@ struct NuPlayer2::CCDecoder : public RefBase { size_t getTrackCount() const; sp getTrackInfo(size_t index) const; status_t selectTrack(size_t index, bool select); + ssize_t getSelectedTrack(media_track_type type) const; bool isSelected() const; void decode(const sp &accessUnit); void display(int64_t timeUs); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 0784939b81..3922767940 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -659,20 +659,31 @@ void NuPlayer::onMessageReceived(const sp &msg) { case kWhatGetSelectedTrack: { + int32_t type32; + CHECK(msg->findInt32("type", (int32_t*)&type32)); + media_track_type type = (media_track_type)type32; + + size_t inbandTracks = 0; status_t err = INVALID_OPERATION; + ssize_t selectedTrack = -1; if (mSource != NULL) { err = OK; + inbandTracks = mSource->getTrackCount(); + selectedTrack = mSource->getSelectedTrack(type); + } - int32_t type32; - CHECK(msg->findInt32("type", (int32_t*)&type32)); - media_track_type type = (media_track_type)type32; - ssize_t selectedTrack = mSource->getSelectedTrack(type); - - Parcel* reply; - CHECK(msg->findPointer("reply", (void**)&reply)); - reply->writeInt32(selectedTrack); + if (selectedTrack == -1 && mCCDecoder != NULL) { + err = OK; + selectedTrack = mCCDecoder->getSelectedTrack(type); + if (selectedTrack != -1) { + selectedTrack += inbandTracks; + } } + Parcel* reply; + CHECK(msg->findPointer("reply", (void**)&reply)); + reply->writeInt32(selectedTrack); + sp response = new AMessage; response->setInt32("err", err); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp index fb12360026..ec30d0c6e4 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp @@ -200,6 +200,18 @@ status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { return OK; } +ssize_t NuPlayer::CCDecoder::getSelectedTrack(media_track_type type) const { + if (mSelectedTrack != -1) { + CCTrack track = mTracks[mSelectedTrack]; + if (track.mTrackType == kTrackTypeCEA608 || track.mTrackType == kTrackTypeCEA708) { + return (type == MEDIA_TRACK_TYPE_SUBTITLE ? mSelectedTrack : -1); + } + return (type == MEDIA_TRACK_TYPE_UNKNOWN ? mSelectedTrack : -1); + } + + return -1; +} + bool NuPlayer::CCDecoder::isSelected() const { return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)getTrackCount(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h index f310f37f25..37b82309e5 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h @@ -38,6 +38,7 @@ struct NuPlayer::CCDecoder : public RefBase { size_t getTrackCount() const; sp getTrackInfo(size_t index) const; status_t selectTrack(size_t index, bool select); + ssize_t getSelectedTrack(media_track_type type) const; bool isSelected() const; void decode(const sp &accessUnit); void display(int64_t timeUs); -- GitLab From e72cb66313a21ecd218f2b9a01491bc1ebbe2c39 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Fri, 30 Nov 2018 13:43:22 +0900 Subject: [PATCH 0534/1530] AML: Use AudioManager.FLAG_FROM_KEY which is @SystemApi Bug: 119790339 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: Ie115151326f042f3b9759b3b6010eb9e65856f48 --- .../apex/java/android/media/session/MediaController.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 1f2918595d..a8e77c7fc8 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -199,9 +199,8 @@ public final class MediaController { } case KeyEvent.ACTION_UP: { - //TODO(b/119790339): Resolve hidden API usage. AudioManager.FLAG_FROM_KEY - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE; - //| AudioManager.FLAG_FROM_KEY; + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE + | AudioManager.FLAG_FROM_KEY; try { mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0, flags); -- GitLab From dfa1e8ab20ce54f3cde479e1305861867abcdbea Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 26 Oct 2018 16:42:07 -0700 Subject: [PATCH 0535/1530] Policy config parser: migrate to BP and drop legacy format Migrate to android.bp as test need to use the library. As bp do not support conditional statement, and this format has been deprecated for 3 years, drop the pre-treble .conf parser. AUDIOSERVER_MULTILIB is no longer use as it does not make sense for a static lib. Bug: 114303641 Bug: 111681420 Test: compile, boots Change-Id: I9f429b306ecfbb80116445ba6297a4b3d6dc57f6 Signed-off-by: Kevin Rocard --- services/audiopolicy/Android.mk | 2 +- services/audiopolicy/common/Android.bp | 4 + services/audiopolicy/common/Android.mk | 9 - .../common/managerdefinitions/Android.bp | 53 +++ .../common/managerdefinitions/Android.mk | 66 --- .../include/ConfigParsingUtils.h | 59 --- .../common/managerdefinitions/include/Gains.h | 60 --- .../include/StreamDescriptor.h | 110 ----- .../src/ConfigParsingUtils.cpp | 422 ------------------ .../common/managerdefinitions/src/Gains.cpp | 256 ----------- .../src/StreamDescriptor.cpp | 210 --------- .../managerdefault/AudioPolicyManager.cpp | 25 +- 12 files changed, 66 insertions(+), 1210 deletions(-) create mode 100644 services/audiopolicy/common/Android.bp delete mode 100644 services/audiopolicy/common/Android.mk create mode 100644 services/audiopolicy/common/managerdefinitions/Android.bp delete mode 100644 services/audiopolicy/common/managerdefinitions/Android.mk delete mode 100644 services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/Gains.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h delete mode 100644 services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp delete mode 100644 services/audiopolicy/common/managerdefinitions/src/Gains.cpp delete mode 100644 services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index f23e426877..02ab8ad615 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -81,9 +81,9 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_SHARED_LIBRARIES += libmedia_helper LOCAL_SHARED_LIBRARIES += libmediametrics -ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) LOCAL_SHARED_LIBRARIES += libhidlbase libicuuc libxml2 +ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) diff --git a/services/audiopolicy/common/Android.bp b/services/audiopolicy/common/Android.bp new file mode 100644 index 0000000000..a925b9a32f --- /dev/null +++ b/services/audiopolicy/common/Android.bp @@ -0,0 +1,4 @@ +cc_library_headers { + name: "libaudiopolicycommon", + export_include_dirs: ["include"], +} diff --git a/services/audiopolicy/common/Android.mk b/services/audiopolicy/common/Android.mk deleted file mode 100644 index dcce8e3d28..0000000000 --- a/services/audiopolicy/common/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp new file mode 100644 index 0000000000..d0b4973885 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -0,0 +1,53 @@ +cc_library_static { + name: "libaudiopolicycomponents", + + srcs: [ + "src/AudioCollections.cpp", + "src/AudioGain.cpp", + "src/AudioInputDescriptor.cpp", + "src/AudioOutputDescriptor.cpp", + "src/AudioPatch.cpp", + "src/AudioPolicyMix.cpp", + "src/AudioPort.cpp", + "src/AudioProfile.cpp", + "src/AudioRoute.cpp", + "src/ClientDescriptor.cpp", + "src/DeviceDescriptor.cpp", + "src/EffectDescriptor.cpp", + "src/HwModule.cpp", + "src/IOProfile.cpp", + "src/Serializer.cpp", + "src/SoundTriggerSession.cpp", + "src/TypeConverter.cpp", + "src/VolumeCurve.cpp", + ], + shared_libs: [ + "libcutils", + "libhidlbase", + "libicuuc", + "liblog", + "libmedia", + "libutils", + "libxml2", + ], + export_shared_lib_headers: ["libmedia"], + static_libs: [ + "libaudioutils", + ], + header_libs: [ + "libaudiopolicycommon", + ], + export_header_lib_headers: ["libaudiopolicycommon"], + + include_dirs: [ + "frameworks/av/services/audiopolicy", + ], + + export_include_dirs: ["include"], + + cflags: [ + "-Wall", + "-Werror", + ], + +} diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk deleted file mode 100644 index 3336b794db..0000000000 --- a/services/audiopolicy/common/managerdefinitions/Android.mk +++ /dev/null @@ -1,66 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - src/DeviceDescriptor.cpp \ - src/AudioGain.cpp \ - src/HwModule.cpp \ - src/IOProfile.cpp \ - src/AudioPort.cpp \ - src/AudioProfile.cpp \ - src/AudioRoute.cpp \ - src/AudioPolicyMix.cpp \ - src/AudioPatch.cpp \ - src/AudioInputDescriptor.cpp \ - src/AudioOutputDescriptor.cpp \ - src/AudioCollections.cpp \ - src/EffectDescriptor.cpp \ - src/SoundTriggerSession.cpp \ - src/VolumeCurve.cpp \ - src/TypeConverter.cpp \ - src/ClientDescriptor.cpp - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libmedia \ - libutils \ - liblog \ - -LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libmedia - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - frameworks/av/services/audiopolicy/common/include \ - frameworks/av/services/audiopolicy \ - $(call include-path-for, audio-utils) \ - -ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) - -LOCAL_SRC_FILES += src/Serializer.cpp - -LOCAL_SHARED_LIBRARIES += libhidlbase libicuuc libxml2 - -LOCAL_C_INCLUDES += \ - external/libxml2/include \ - external/icu/icu4c/source/common - -else - -LOCAL_SRC_FILES += \ - src/ConfigParsingUtils.cpp \ - src/StreamDescriptor.cpp \ - src/Gains.cpp - -endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) - -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(LOCAL_PATH)/include - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_CFLAGS := -Wall -Werror - -LOCAL_MODULE := libaudiopolicycomponents - -include $(BUILD_STATIC_LIBRARY) diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h deleted file mode 100644 index a00785429f..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "AudioPolicyConfig.h" -#include "DeviceDescriptor.h" -#include "HwModule.h" -#include "audio_policy_conf.h" -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -// ---------------------------------------------------------------------------- -// Definitions for audio_policy.conf file parsing -// ---------------------------------------------------------------------------- - -class ConfigParsingUtils -{ -public: - static status_t loadConfig(const char *path, AudioPolicyConfig &config); - -private: - static void loadAudioPortGain(cnode *root, AudioPort &audioPort, int index); - static void loadAudioPortGains(cnode *root, AudioPort &audioPort); - static void loadDeviceDescriptorGains(cnode *root, sp &deviceDesc); - static status_t loadHwModuleDevice(cnode *root, DeviceVector &devices); - static status_t loadHwModuleProfile(cnode *root, sp &module, audio_port_role_t role); - static void loadDevicesFromTag(const char *tag, DeviceVector &devices, - const DeviceVector &declaredDevices); - static void loadHwModules(cnode *root, HwModuleCollection &hwModules, - AudioPolicyConfig &config); - static void loadGlobalConfig(cnode *root, AudioPolicyConfig &config, - const sp &primaryModule); - static void loadModuleGlobalConfig(cnode *root, const sp &module, - AudioPolicyConfig &config); - static status_t loadHwModule(cnode *root, sp &module, AudioPolicyConfig &config); -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/Gains.h b/services/audiopolicy/common/managerdefinitions/include/Gains.h deleted file mode 100644 index cb229a492c..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/Gains.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android { - -class StreamDescriptor; - -class Gains -{ -public : - static float volIndexToDb(const VolumeCurvePoint *point, int indexMin, int indexMax, - int indexInUi); - - // default volume curve - static const VolumeCurvePoint sDefaultVolumeCurve[Volume::VOLCNT]; - // default volume curve for media strategy - static const VolumeCurvePoint sDefaultMediaVolumeCurve[Volume::VOLCNT]; - // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) - static const VolumeCurvePoint sExtMediaSystemVolumeCurve[Volume::VOLCNT]; - // volume curve for media strategy on speakers - static const VolumeCurvePoint sSpeakerMediaVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[Volume::VOLCNT]; - // volume curve for sonification strategy on speakers - static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[Volume::VOLCNT]; - static const VolumeCurvePoint sDefaultSystemVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[Volume::VOLCNT]; - static const VolumeCurvePoint sHeadsetSystemVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sDefaultVoiceVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sLinearVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sSilentVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sFullScaleVolumeCurve[Volume::VOLCNT]; - static const VolumeCurvePoint sHearingAidVolumeCurve[Volume::VOLCNT]; - // default volume curves per stream and device category. See initializeVolumeCurves() - static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][DEVICE_CATEGORY_CNT]; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h deleted file mode 100644 index 626631344f..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "IVolumeCurvesCollection.h" -#include -#include -#include -#include - -namespace android { - -// stream descriptor used for volume control -class StreamDescriptor -{ -public: - StreamDescriptor(); - - int getVolumeIndex(audio_devices_t device) const; - bool canBeMuted() const { return mCanBeMuted; } - void clearCurrentVolumeIndex(); - void addCurrentVolumeIndex(audio_devices_t device, int index); - int getVolumeIndexMin() const { return mIndexMin; } - int getVolumeIndexMax() const { return mIndexMax; } - void setVolumeIndexMin(int volIndexMin); - void setVolumeIndexMax(int volIndexMax); - bool hasVolumeIndexForDevice(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - return mIndexCur.indexOfKey(device) >= 0; - } - - void dump(String8 *dst) const; - - void setVolumeCurvePoint(device_category deviceCategory, const VolumeCurvePoint *point); - const VolumeCurvePoint *getVolumeCurvePoint(device_category deviceCategory) const - { - return mVolumeCurve[deviceCategory]; - } - -private: - const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; - KeyedVector mIndexCur; /**< current volume index per device. */ - int mIndexMin; /**< min volume index. */ - int mIndexMax; /**< max volume index. */ - bool mCanBeMuted; /**< true is the stream can be muted. */ -}; - -/** - * stream descriptors collection for volume control - */ -class StreamDescriptorCollection : public DefaultKeyedVector, - public IVolumeCurvesCollection -{ -public: - StreamDescriptorCollection(); - - virtual void clearCurrentVolumeIndex(audio_stream_type_t stream); - virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, - int index); - virtual bool canBeMuted(audio_stream_type_t stream); - virtual int getVolumeIndexMin(audio_stream_type_t stream) const - { - return valueFor(stream).getVolumeIndexMin(); - } - virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) - { - return valueFor(stream).getVolumeIndex(device); - } - virtual int getVolumeIndexMax(audio_stream_type_t stream) const - { - return valueFor(stream).getVolumeIndexMax(); - } - virtual float volIndexToDb(audio_stream_type_t stream, device_category device, - int indexInUi) const; - virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax); - virtual void initializeVolumeCurves(bool isSpeakerDrcEnabled); - virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); - virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, - audio_devices_t device) const - { - return valueFor(stream).hasVolumeIndexForDevice(device); - } - - void dump(String8 *dst) const override; - -private: - void setVolumeCurvePoint(audio_stream_type_t stream, device_category deviceCategory, - const VolumeCurvePoint *point); - const VolumeCurvePoint *getVolumeCurvePoint(audio_stream_type_t stream, - device_category deviceCategory) const; - void setVolumeIndexMin(audio_stream_type_t stream,int volIndexMin); - void setVolumeIndexMax(audio_stream_type_t stream,int volIndexMax); -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp deleted file mode 100644 index 59db81caf4..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::ConfigParsingUtils" -//#define LOG_NDEBUG 0 - -#include "ConfigParsingUtils.h" -#include "AudioGain.h" -#include "IOProfile.h" -#include -#include -#include -#include -#include - -namespace android { - -// --- audio_policy.conf file parsing - -//static -void ConfigParsingUtils::loadAudioPortGain(cnode *root, AudioPort &audioPort, int index) -{ - cnode *node = root->first_child; - - sp gain = new AudioGain(index, audioPort.useInputChannelMask()); - - while (node) { - if (strcmp(node->name, GAIN_MODE) == 0) { - gain->setMode(GainModeConverter::maskFromString(node->value)); - } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { - audio_channel_mask_t mask; - if (audioPort.useInputChannelMask()) { - if (InputChannelConverter::fromString(node->value, mask)) { - gain->setChannelMask(mask); - } - } else { - if (OutputChannelConverter::fromString(node->value, mask)) { - gain->setChannelMask(mask); - } - } - } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { - gain->setMinValueInMb(atoi(node->value)); - } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { - gain->setMaxValueInMb(atoi(node->value)); - } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { - gain->setDefaultValueInMb(atoi(node->value)); - } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { - gain->setStepValueInMb(atoi(node->value)); - } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { - gain->setMinRampInMs(atoi(node->value)); - } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { - gain->setMaxRampInMs(atoi(node->value)); - } - node = node->next; - } - - ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", - gain->getMode(), gain->getChannelMask(), gain->getMinValueInMb(), - gain->getMaxValueInMb()); - - if (gain->getMode() == 0) { - return; - } - audioPort.mGains.add(gain); -} - -void ConfigParsingUtils::loadAudioPortGains(cnode *root, AudioPort &audioPort) -{ - cnode *node = root->first_child; - int index = 0; - while (node) { - ALOGV("loadGains() loading gain %s", node->name); - loadAudioPortGain(node, audioPort, index++); - node = node->next; - } -} - -//static -void ConfigParsingUtils::loadDeviceDescriptorGains(cnode *root, sp &deviceDesc) -{ - loadAudioPortGains(root, *deviceDesc); - if (deviceDesc->mGains.size() > 0) { - deviceDesc->mGains[0]->getDefaultConfig(&deviceDesc->mGain); - } -} - -//static -status_t ConfigParsingUtils::loadHwModuleDevice(cnode *root, DeviceVector &devices) -{ - cnode *node = root->first_child; - - audio_devices_t type = AUDIO_DEVICE_NONE; - while (node) { - if (strcmp(node->name, APM_DEVICE_TYPE) == 0) { - deviceFromString(node->value, type); - break; - } - node = node->next; - } - if (type == AUDIO_DEVICE_NONE || - (!audio_is_input_device(type) && !audio_is_output_device(type))) { - ALOGW("loadDevice() bad type %08x", type); - return BAD_VALUE; - } - sp deviceDesc = new DeviceDescriptor(type, String8(root->name)); - - node = root->first_child; - while (node) { - if (strcmp(node->name, APM_DEVICE_ADDRESS) == 0) { - deviceDesc->mAddress = String8((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - if (audio_is_input_device(type)) { - deviceDesc->addAudioProfile( - new AudioProfile(gDynamicFormat, - inputChannelMasksFromString(node->value), - SampleRateVector())); - } else { - deviceDesc->addAudioProfile( - new AudioProfile(gDynamicFormat, - outputChannelMasksFromString(node->value), - SampleRateVector())); - } - } else if (strcmp(node->name, GAINS_TAG) == 0) { - loadDeviceDescriptorGains(node, deviceDesc); - } - node = node->next; - } - - ALOGV("loadDevice() adding device tag (literal type) %s type %08x address %s", - deviceDesc->getTagName().string(), type, deviceDesc->mAddress.string()); - - devices.add(deviceDesc); - return NO_ERROR; -} - -//static -status_t ConfigParsingUtils::loadHwModuleProfile(cnode *root, sp &module, - audio_port_role_t role) -{ - cnode *node = root->first_child; - - sp profile = new IOProfile(String8(root->name), role); - - AudioProfileVector audioProfiles; - SampleRateVector sampleRates; - ChannelsVector channels; - FormatVector formats; - - while (node) { - if (strcmp(node->name, FORMATS_TAG) == 0 && - strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) { - formats = formatsFromString(node->value); - } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0 && - strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) { - collectionFromString(node->value, sampleRates); - } else if (strcmp(node->name, CHANNELS_TAG) == 0 && - strcmp(node->value, DYNAMIC_VALUE_TAG) != 0) { - if (role == AUDIO_PORT_ROLE_SINK) { - channels = inputChannelMasksFromString(node->value); - } else { - channels = outputChannelMasksFromString(node->value); - } - } else if (strcmp(node->name, DEVICES_TAG) == 0) { - DeviceVector devices; - loadDevicesFromTag(node->value, devices, module->getDeclaredDevices()); - profile->setSupportedDevices(devices); - } else if (strcmp(node->name, FLAGS_TAG) == 0) { - if (role == AUDIO_PORT_ROLE_SINK) { - profile->setFlags(InputFlagConverter::maskFromString(node->value)); - } else { - profile->setFlags(OutputFlagConverter::maskFromString(node->value)); - } - } else if (strcmp(node->name, GAINS_TAG) == 0) { - loadAudioPortGains(node, *profile); - } - node = node->next; - } - if (formats.isEmpty()) { - sp profileToAdd = new AudioProfile(gDynamicFormat, channels, sampleRates); - profileToAdd->setDynamicFormat(true); - profileToAdd->setDynamicChannels(channels.isEmpty()); - profileToAdd->setDynamicRate(sampleRates.isEmpty()); - audioProfiles.add(profileToAdd); - } else { - for (size_t i = 0; i < formats.size(); i++) { - // For compatibility reason, for each format, creates a profile with the same - // collection of rate and channels. - sp profileToAdd = new AudioProfile(formats[i], channels, sampleRates); - profileToAdd->setDynamicFormat(formats[i] == gDynamicFormat); - profileToAdd->setDynamicChannels(channels.isEmpty()); - profileToAdd->setDynamicRate(sampleRates.isEmpty()); - audioProfiles.add(profileToAdd); - } - } - profile->setAudioProfiles(audioProfiles); - ALOGW_IF(!profile->hasSupportedDevices(), "load%s() invalid supported devices", - role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output"); - if (profile->hasSupportedDevices()) { - ALOGV("load%s() adding Supported Devices %04x, mFlags %04x", - role == AUDIO_PORT_ROLE_SINK ? "Input" : "Output", - profile->getSupportedDevicesType(), profile->getFlags()); - return module->addProfile(profile); - } - return BAD_VALUE; -} - -//static -status_t ConfigParsingUtils::loadHwModule(cnode *root, sp &module, - AudioPolicyConfig &config) -{ - status_t status = NAME_NOT_FOUND; - cnode *node = config_find(root, DEVICES_TAG); - if (node != NULL) { - node = node->first_child; - DeviceVector devices; - while (node) { - ALOGV("loadHwModule() loading device %s", node->name); - status_t tmpStatus = loadHwModuleDevice(node, devices); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - module->setDeclaredDevices(devices); - } - node = config_find(root, OUTPUTS_TAG); - if (node != NULL) { - node = node->first_child; - while (node) { - ALOGV("loadHwModule() loading output %s", node->name); - status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SOURCE); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - } - node = config_find(root, INPUTS_TAG); - if (node != NULL) { - node = node->first_child; - while (node) { - ALOGV("loadHwModule() loading input %s", node->name); - status_t tmpStatus = loadHwModuleProfile(node, module, AUDIO_PORT_ROLE_SINK); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - } - loadModuleGlobalConfig(root, module, config); - return status; -} - -//static -void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules, - AudioPolicyConfig &config) -{ - cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); - if (node == NULL) { - return; - } - - node = node->first_child; - while (node) { - ALOGV("loadHwModules() loading module %s", node->name); - sp module = new HwModule(node->name); - if (loadHwModule(node, module, config) == NO_ERROR) { - hwModules.add(module); - } - node = node->next; - } -} - -//static -void ConfigParsingUtils::loadDevicesFromTag(const char *tag, DeviceVector &devices, - const DeviceVector &declaredDevices) -{ - char *tagLiteral = strndup(tag, strlen(tag)); - char *devTag = strtok(tagLiteral, AudioParameter::valueListSeparator); - while (devTag != NULL) { - if (strlen(devTag) != 0) { - audio_devices_t type; - if (deviceFromString(devTag, type)) { - uint32_t inBit = type & AUDIO_DEVICE_BIT_IN; - type &= ~AUDIO_DEVICE_BIT_IN; - while (type) { - audio_devices_t singleType = - inBit | (1 << (31 - __builtin_clz(type))); - type &= ~singleType; - sp dev = new DeviceDescriptor(singleType); - devices.add(dev); - } - } else { - sp deviceDesc = - declaredDevices.getDeviceFromTagName(String8(devTag)); - if (deviceDesc != 0) { - devices.add(deviceDesc); - } - } - } - devTag = strtok(NULL, AudioParameter::valueListSeparator); - } - free(tagLiteral); -} - -//static -void ConfigParsingUtils::loadModuleGlobalConfig(cnode *root, const sp &module, - AudioPolicyConfig &config) -{ - cnode *node = config_find(root, GLOBAL_CONFIG_TAG); - - if (node == NULL) { - return; - } - DeviceVector declaredDevices; - if (module != NULL) { - declaredDevices = module->getDeclaredDevices(); - } - - node = node->first_child; - while (node) { - if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { - DeviceVector availableOutputDevices; - loadDevicesFromTag(node->value, availableOutputDevices, declaredDevices); - ALOGV("loadGlobalConfig() Attached Output Devices %08x", - availableOutputDevices.types()); - config.addAvailableOutputDevices(availableOutputDevices); - } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { - audio_devices_t device = AUDIO_DEVICE_NONE; - deviceFromString(node->value, device); - if (device != AUDIO_DEVICE_NONE) { - sp defaultOutputDevice = new DeviceDescriptor(device); - config.setDefaultOutputDevice(defaultOutputDevice); - ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type()); - } else { - ALOGW("loadGlobalConfig() default device not specified"); - } - } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { - DeviceVector availableInputDevices; - loadDevicesFromTag(node->value, availableInputDevices, declaredDevices); - ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types()); - config.addAvailableInputDevices(availableInputDevices); - } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { - uint32_t major, minor; - sscanf((char *)node->value, "%u.%u", &major, &minor); - module->setHalVersion(major, minor); - ALOGV("loadGlobalConfig() mHalVersion = major %u minor %u", major, minor); - } - node = node->next; - } -} - -//static -void ConfigParsingUtils::loadGlobalConfig(cnode *root, AudioPolicyConfig &config, - const sp& primaryModule) -{ - cnode *node = config_find(root, GLOBAL_CONFIG_TAG); - - if (node == NULL) { - return; - } - node = node->first_child; - while (node) { - if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { - bool speakerDrcEnabled; - if (utilities::convertTo(node->value, speakerDrcEnabled)) { - ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled); - config.setSpeakerDrcEnabled(speakerDrcEnabled); - } - } - node = node->next; - } - loadModuleGlobalConfig(root, primaryModule, config); -} - -//static -status_t ConfigParsingUtils::loadConfig(const char *path, AudioPolicyConfig &config) -{ - cnode *root; - char *data; - - data = (char *)load_file(path, NULL); - if (data == NULL) { - return -ENODEV; - } - root = config_node("", ""); - config_load(root, data); - - HwModuleCollection hwModules; - loadHwModules(root, hwModules, config); - - // legacy audio_policy.conf files have one global_configuration section, attached to primary. - loadGlobalConfig(root, config, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)); - - config.setHwModules(hwModules); - - config.setDefaultSurroundFormats(); - - config_free(root); - free(root); - free(data); - - ALOGI("loadAudioPolicyConfig() loaded %s\n", path); - config.setSource(path); - - return NO_ERROR; -} - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/Gains.cpp b/services/audiopolicy/common/managerdefinitions/src/Gains.cpp deleted file mode 100644 index 6407a17660..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/Gains.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::Gains" -//#define LOG_NDEBUG 0 - -//#define VERY_VERBOSE_LOGGING -#ifdef VERY_VERBOSE_LOGGING -#define ALOGVV ALOGV -#else -#define ALOGVV(a...) do { } while(0) -#endif - -#include "Gains.h" -#include -#include -#include - -namespace android { - -// Enginedefault -const VolumeCurvePoint -Gains::sDefaultVolumeCurve[Volume::VOLCNT] = { - {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} -}; - - -const VolumeCurvePoint -Gains::sDefaultMediaVolumeCurve[Volume::VOLCNT] = { - {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sExtMediaSystemVolumeCurve[Volume::VOLCNT] = { - {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} -}; - -const VolumeCurvePoint -Gains::sSpeakerMediaVolumeCurve[Volume::VOLCNT] = { - {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sSpeakerMediaVolumeCurveDrc[Volume::VOLCNT] = { - {1, -55.0f}, {20, -43.0f}, {86, -12.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sSpeakerSonificationVolumeCurve[Volume::VOLCNT] = { - {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sSpeakerSonificationVolumeCurveDrc[Volume::VOLCNT] = { - {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} -}; - -// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks -// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. -// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). -// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. - -const VolumeCurvePoint -Gains::sDefaultSystemVolumeCurve[Volume::VOLCNT] = { - {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} -}; - -const VolumeCurvePoint -Gains::sDefaultSystemVolumeCurveDrc[Volume::VOLCNT] = { - {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} -}; - -const VolumeCurvePoint -Gains::sHeadsetSystemVolumeCurve[Volume::VOLCNT] = { - {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} -}; - -const VolumeCurvePoint -Gains::sDefaultVoiceVolumeCurve[Volume::VOLCNT] = { - {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sSpeakerVoiceVolumeCurve[Volume::VOLCNT] = { - {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sLinearVolumeCurve[Volume::VOLCNT] = { - {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sSilentVolumeCurve[Volume::VOLCNT] = { - {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.0f} -}; - -const VolumeCurvePoint -Gains::sFullScaleVolumeCurve[Volume::VOLCNT] = { - {0, 0.0f}, {1, 0.0f}, {2, 0.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint -Gains::sHearingAidVolumeCurve[Volume::VOLCNT] = { - {1, -128.0f}, {20, -80.0f}, {60, -40.0f}, {100, 0.0f} -}; - -const VolumeCurvePoint *Gains::sVolumeProfiles[AUDIO_STREAM_CNT] - [DEVICE_CATEGORY_CNT] = { - { // AUDIO_STREAM_VOICE_CALL - Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_SYSTEM - Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_RING - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_MUSIC - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_ALARM - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_NOTIFICATION - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_BLUETOOTH_SCO - Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_ENFORCED_AUDIBLE - Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_DTMF - Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sExtMediaSystemVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_TTS - // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER - Gains::sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sSilentVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_ACCESSIBILITY - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sHearingAidVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_REROUTING - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sFullScaleVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, - { // AUDIO_STREAM_PATCH - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE - Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EXT_MEDIA - Gains::sFullScaleVolumeCurve // DEVICE_CATEGORY_HEARING_AID - }, -}; - -//static -float Gains::volIndexToDb(const VolumeCurvePoint *curve, int indexMin, int indexMax, int indexInUi) -{ - // the volume index in the UI is relative to the min and max volume indices for this stream type - int nbSteps = 1 + curve[Volume::VOLMAX].mIndex - curve[Volume::VOLMIN].mIndex; - int volIdx = (nbSteps * (indexInUi - indexMin)) / (indexMax - indexMin); - - // find what part of the curve this index volume belongs to, or if it's out of bounds - int segment = 0; - if (volIdx < curve[Volume::VOLMIN].mIndex) { // out of bounds - return VOLUME_MIN_DB; - } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) { - segment = 0; - } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) { - segment = 1; - } else if (volIdx <= curve[Volume::VOLMAX].mIndex) { - segment = 2; - } else { // out of bounds - return 0.0f; - } - - // linear interpolation in the attenuation table in dB - float decibels = curve[segment].mDBAttenuation + - ((float)(volIdx - curve[segment].mIndex)) * - ( (curve[segment+1].mDBAttenuation - - curve[segment].mDBAttenuation) / - ((float)(curve[segment+1].mIndex - - curve[segment].mIndex)) ); - - ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", - curve[segment].mIndex, volIdx, - curve[segment+1].mIndex, - curve[segment].mDBAttenuation, - decibels, - curve[segment+1].mDBAttenuation); - - return decibels; -} - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp deleted file mode 100644 index c311a4fc96..0000000000 --- a/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::Volumes" -//#define LOG_NDEBUG 0 - -//#define VERY_VERBOSE_LOGGING -#ifdef VERY_VERBOSE_LOGGING -#define ALOGVV ALOGV -#else -#define ALOGVV(a...) do { } while(0) -#endif - -#include "StreamDescriptor.h" -#include "Gains.h" -#include "policy.h" -#include -#include - -namespace android { - -// --- StreamDescriptor class implementation - -StreamDescriptor::StreamDescriptor() - : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) -{ - // Initialize the current stream's index to mIndexMax so volume isn't 0 in - // cases where the Java layer doesn't call into the audio policy service to - // set the default volume. - mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, mIndexMax); -} - -int StreamDescriptor::getVolumeIndex(audio_devices_t device) const -{ - device = Volume::getDeviceForVolume(device); - // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME - if (mIndexCur.indexOfKey(device) < 0) { - device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; - } - return mIndexCur.valueFor(device); -} - -void StreamDescriptor::clearCurrentVolumeIndex() -{ - mIndexCur.clear(); -} - -void StreamDescriptor::addCurrentVolumeIndex(audio_devices_t device, int index) -{ - mIndexCur.add(device, index); -} - -void StreamDescriptor::setVolumeIndexMin(int volIndexMin) -{ - mIndexMin = volIndexMin; -} - -void StreamDescriptor::setVolumeIndexMax(int volIndexMax) -{ - mIndexMax = volIndexMax; -} - -void StreamDescriptor::setVolumeCurvePoint(device_category deviceCategory, - const VolumeCurvePoint *point) -{ - mVolumeCurve[deviceCategory] = point; -} - -void StreamDescriptor::dump(String8 *dst) const -{ - dst->appendFormat("%s %02d %02d ", - mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); - for (size_t i = 0; i < mIndexCur.size(); i++) { - dst->appendFormat("%04x : %02d, ", - mIndexCur.keyAt(i), - mIndexCur.valueAt(i)); - } - dst->append("\n"); -} - -StreamDescriptorCollection::StreamDescriptorCollection() -{ - for (size_t stream = 0 ; stream < AUDIO_STREAM_CNT; stream++) { - add(static_cast(stream), StreamDescriptor()); - } -} - -bool StreamDescriptorCollection::canBeMuted(audio_stream_type_t stream) -{ - return valueAt(stream).canBeMuted(); -} - -void StreamDescriptorCollection::clearCurrentVolumeIndex(audio_stream_type_t stream) -{ - editValueAt(stream).clearCurrentVolumeIndex(); -} - -void StreamDescriptorCollection::addCurrentVolumeIndex(audio_stream_type_t stream, - audio_devices_t device, int index) -{ - editValueAt(stream).addCurrentVolumeIndex(device, index); -} - -void StreamDescriptorCollection::setVolumeCurvePoint(audio_stream_type_t stream, - device_category deviceCategory, - const VolumeCurvePoint *point) -{ - editValueAt(stream).setVolumeCurvePoint(deviceCategory, point); -} - -const VolumeCurvePoint *StreamDescriptorCollection::getVolumeCurvePoint(audio_stream_type_t stream, - device_category deviceCategory) const -{ - return valueAt(stream).getVolumeCurvePoint(deviceCategory); -} - -void StreamDescriptorCollection::setVolumeIndexMin(audio_stream_type_t stream,int volIndexMin) -{ - return editValueAt(stream).setVolumeIndexMin(volIndexMin); -} - -void StreamDescriptorCollection::setVolumeIndexMax(audio_stream_type_t stream,int volIndexMax) -{ - return editValueAt(stream).setVolumeIndexMax(volIndexMax); -} - -float StreamDescriptorCollection::volIndexToDb(audio_stream_type_t stream, device_category category, - int indexInUi) const -{ - const StreamDescriptor &streamDesc = valueAt(stream); - return Gains::volIndexToDb(streamDesc.getVolumeCurvePoint(category), - streamDesc.getVolumeIndexMin(), streamDesc.getVolumeIndexMax(), - indexInUi); -} - -status_t StreamDescriptorCollection::initStreamVolume(audio_stream_type_t stream, - int indexMin, int indexMax) -{ - ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); - if (indexMin < 0 || indexMin >= indexMax) { - ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", - stream , indexMin, indexMax); - return BAD_VALUE; - } - setVolumeIndexMin(stream, indexMin); - setVolumeIndexMax(stream, indexMax); - return NO_ERROR; -} - -void StreamDescriptorCollection::initializeVolumeCurves(bool isSpeakerDrcEnabled) -{ - for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { - setVolumeCurvePoint(static_cast(i), - static_cast(j), - Gains::sVolumeProfiles[i][j]); - } - } - - // Check availability of DRC on speaker path: if available, override some of the speaker curves - if (isSpeakerDrcEnabled) { - setVolumeCurvePoint(AUDIO_STREAM_SYSTEM, DEVICE_CATEGORY_SPEAKER, - Gains::sDefaultSystemVolumeCurveDrc); - setVolumeCurvePoint(AUDIO_STREAM_RING, DEVICE_CATEGORY_SPEAKER, - Gains::sSpeakerSonificationVolumeCurveDrc); - setVolumeCurvePoint(AUDIO_STREAM_ALARM, DEVICE_CATEGORY_SPEAKER, - Gains::sSpeakerSonificationVolumeCurveDrc); - setVolumeCurvePoint(AUDIO_STREAM_NOTIFICATION, DEVICE_CATEGORY_SPEAKER, - Gains::sSpeakerSonificationVolumeCurveDrc); - setVolumeCurvePoint(AUDIO_STREAM_MUSIC, DEVICE_CATEGORY_SPEAKER, - Gains::sSpeakerMediaVolumeCurveDrc); - setVolumeCurvePoint(AUDIO_STREAM_ACCESSIBILITY, DEVICE_CATEGORY_SPEAKER, - Gains::sSpeakerMediaVolumeCurveDrc); - } -} - -void StreamDescriptorCollection::switchVolumeCurve(audio_stream_type_t streamSrc, - audio_stream_type_t streamDst) -{ - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { - setVolumeCurvePoint(streamDst, static_cast(j), - Gains::sVolumeProfiles[streamSrc][j]); - } -} - -void StreamDescriptorCollection::dump(String8 *dst) const -{ - dst->append("\nStreams dump:\n"); - dst->append( - " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); - for (size_t i = 0; i < size(); i++) { - dst->appendFormat(" %02zu ", i); - valueAt(i).dump(dst); - } -} - -} // namespace android diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 3909202b36..a4e6cc48f1 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -49,10 +49,6 @@ #include #include #include "AudioPolicyManager.h" -#ifndef USE_XML_AUDIO_POLICY_CONF -#include -#include -#endif #include #include "TypeConverter.h" #include @@ -3794,7 +3790,6 @@ uint32_t AudioPolicyManager::nextAudioPortGeneration() return mAudioPortGeneration++; } -#ifdef USE_XML_AUDIO_POLICY_CONF // Treblized audio policy xml config will be located in /odm/etc or /vendor/etc. static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"}; @@ -3826,7 +3821,6 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { } return ret; } -#endif AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface, bool /*forTesting*/) @@ -3835,15 +3829,9 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mpClientInterface(clientInterface), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mA2dpSuspended(false), -#ifdef USE_XML_AUDIO_POLICY_CONF mVolumeCurves(new VolumeCurvesCollection()), mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice, static_cast(mVolumeCurves.get())), -#else - mVolumeCurves(new StreamDescriptorCollection()), - mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, - mDefaultOutputDevice), -#endif mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), @@ -3862,13 +3850,16 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa initialize(); } +// This check is to catch any legacy platform updating to Q without having +// switched to XML since its deprecation on O. +// TODO: after Q release, remove this check and flag as XML is now the only +// option and all legacy platform should have transitioned to XML. +#ifndef USE_XML_AUDIO_POLICY_CONF +#error Audio policy no longer supports legacy .conf configuration format +#endif + void AudioPolicyManager::loadConfig() { -#ifdef USE_XML_AUDIO_POLICY_CONF if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) { -#else - if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR) - && (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) { -#endif ALOGE("could not load audio policy configuration file, setting defaults"); getConfig().setDefault(); } -- GitLab From 3d48dcea93b4ff5df265c32f0a61e572e7132415 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 8 Nov 2018 17:16:57 -0800 Subject: [PATCH 0536/1530] libaudiohal: Introduce support HAL V5 identical to V4 The V5 HAL is identical to V4 for now. Difference will be added in followup patches. Bug: 118203066 Test: compile Change-Id: I3db5d62cc2412766d8d0a78dab57455236a5019e Signed-off-by: Kevin Rocard --- media/libaudiohal/Android.bp | 3 ++ .../DevicesFactoryHalInterface.cpp | 4 +++ .../EffectsFactoryHalInterface.cpp | 4 +++ media/libaudiohal/impl/Android.bp | 28 +++++++++++++++---- .../libaudiohal/impl/ConversionHelperHidl.cpp | 4 +-- media/libaudiohal/impl/ConversionHelperHidl.h | 2 +- media/libaudiohal/impl/DeviceHalHidl.cpp | 8 +++--- media/libaudiohal/impl/DeviceHalLocal.cpp | 2 +- .../impl/DevicesFactoryHalHidl.cpp | 2 +- media/libaudiohal/impl/StreamHalHidl.cpp | 8 +++--- media/libaudiohal/impl/StreamHalLocal.cpp | 2 +- media/libaudiohal/impl/VersionUtils.h | 2 +- .../impl/include/libaudiohal/FactoryHalHidl.h | 5 ++++ 13 files changed, 53 insertions(+), 21 deletions(-) diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp index 0ff0d4a0fd..584c2c04de 100644 --- a/media/libaudiohal/Android.bp +++ b/media/libaudiohal/Android.bp @@ -15,10 +15,13 @@ cc_library_shared { shared_libs: [ "android.hardware.audio.effect@2.0", "android.hardware.audio.effect@4.0", + "android.hardware.audio.effect@5.0", "android.hardware.audio@2.0", "android.hardware.audio@4.0", + "android.hardware.audio@5.0", "libaudiohal@2.0", "libaudiohal@4.0", + "libaudiohal@5.0", "libutils", ], diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp index e631acef9a..f86009c0cc 100644 --- a/media/libaudiohal/DevicesFactoryHalInterface.cpp +++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -23,6 +24,9 @@ namespace android { // static sp DevicesFactoryHalInterface::create() { + if (hardware::audio::V5_0::IDevicesFactory::getService() != nullptr) { + return V5_0::createDevicesFactoryHal(); + } if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) { return V4_0::createDevicesFactoryHal(); } diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index f7734a8d16..e21c23525f 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -23,6 +24,9 @@ namespace android { // static sp EffectsFactoryHalInterface::create() { + if (hardware::audio::effect::V5_0::IEffectsFactory::getService() != nullptr) { + return V5_0::createEffectsFactoryHal(); + } if (hardware::audio::effect::V4_0::IEffectsFactory::getService() != nullptr) { return V4_0::createEffectsFactoryHal(); } diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp index e86d924eb5..88533da5e3 100644 --- a/media/libaudiohal/impl/Android.bp +++ b/media/libaudiohal/impl/Android.bp @@ -25,12 +25,6 @@ cc_defaults { ], shared_libs: [ "android.hardware.audio.common-util", - "android.hardware.audio.common@2.0", - "android.hardware.audio.common@4.0", - "android.hardware.audio.effect@2.0", - "android.hardware.audio.effect@4.0", - "android.hardware.audio@2.0", - "android.hardware.audio@4.0", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libaudiohal_deathhandler", @@ -63,7 +57,10 @@ cc_library_shared { name: "libaudiohal@2.0", defaults: ["libaudiohal_default"], shared_libs: [ + "android.hardware.audio.common@2.0", "android.hardware.audio.common@2.0-util", + "android.hardware.audio.effect@2.0", + "android.hardware.audio@2.0", ], cflags: [ "-DMAJOR_VERSION=2", @@ -76,7 +73,10 @@ cc_library_shared { name: "libaudiohal@4.0", defaults: ["libaudiohal_default"], shared_libs: [ + "android.hardware.audio.common@4.0", "android.hardware.audio.common@4.0-util", + "android.hardware.audio.effect@4.0", + "android.hardware.audio@4.0", ], cflags: [ "-DMAJOR_VERSION=4", @@ -84,3 +84,19 @@ cc_library_shared { "-include common/all-versions/VersionMacro.h", ] } + +cc_library_shared { + name: "libaudiohal@5.0", + defaults: ["libaudiohal_default"], + shared_libs: [ + "android.hardware.audio.common@5.0", + "android.hardware.audio.common@5.0-util", + "android.hardware.audio.effect@5.0", + "android.hardware.audio@5.0", + ], + cflags: [ + "-DMAJOR_VERSION=5", + "-DMINOR_VERSION=0", + "-include common/all-versions/VersionMacro.h", + ] +} diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp index 5d12fadbd8..97478592f8 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.cpp +++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp @@ -24,7 +24,7 @@ using ::android::hardware::audio::CPP_VERSION::Result; -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneChannelMapping; using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneDirectionality; using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneLocation; @@ -109,7 +109,7 @@ void ConversionHelperHidl::emitError(const char* funcName, const char* descripti ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); } -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 // TODO: Use the same implementation in the hal when it moves to a util library. static std::string deviceAddressToHal(const DeviceAddress& address) { // HAL assumes that the address is NUL-terminated. diff --git a/media/libaudiohal/impl/ConversionHelperHidl.h b/media/libaudiohal/impl/ConversionHelperHidl.h index 52a14564a7..fb3bb9dc80 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.h +++ b/media/libaudiohal/impl/ConversionHelperHidl.h @@ -82,7 +82,7 @@ class ConversionHelperHidl { void emitError(const char* funcName, const char* description); }; -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; void microphoneInfoToHal(const MicrophoneInfo& src, audio_microphone_characteristic_t *pDst); diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index ce806bfd05..b5ed1987e0 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -49,7 +49,7 @@ using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 using ::android::hardware::audio::CPP_VERSION::SinkMetadata; #endif @@ -262,7 +262,7 @@ status_t DeviceHalHidl::openOutputStream( hidlDevice, hidlConfig, EnumBitfield(flags), -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 {} /* metadata */, #endif [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { @@ -292,7 +292,7 @@ status_t DeviceHalHidl::openInputStream( Result retval = Result::NOT_INITIALIZED; #if MAJOR_VERSION == 2 auto sourceMetadata = AudioSource(source); -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 // TODO: correctly propagate the tracks sources and volume // for now, only send the main source at 1dbfs SinkMetadata sourceMetadata = {{{AudioSource(source), 1}}}; @@ -374,7 +374,7 @@ status_t DeviceHalHidl::getMicrophones( if (mDevice == 0) return NO_INIT; return INVALID_OPERATION; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 status_t DeviceHalHidl::getMicrophones(std::vector *microphonesInfo) { if (mDevice == 0) return NO_INIT; Result retval; diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp index 14e26f55fe..dffe9da95a 100644 --- a/media/libaudiohal/impl/DeviceHalLocal.cpp +++ b/media/libaudiohal/impl/DeviceHalLocal.cpp @@ -190,7 +190,7 @@ status_t DeviceHalLocal::getMicrophones( std::vector *microphones __unused) { return INVALID_OPERATION; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 status_t DeviceHalLocal::getMicrophones(std::vector *microphones) { if (mDev->get_microphones == NULL) return INVALID_OPERATION; size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp index 5d97000b7d..5e01e42ce3 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp @@ -75,7 +75,7 @@ static IDevicesFactory::Device idFromHal(const char *name, status_t* status) { *status = BAD_VALUE; return {}; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 static const char* idFromHal(const char *name, status_t* status) { *status = OK; return name; diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index c7d9b62a4a..04d636beef 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -44,7 +44,7 @@ using ::android::hardware::Return; using ::android::hardware::Void; using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand; -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 using ::android::hardware::audio::common::CPP_VERSION::AudioContentType; using ::android::hardware::audio::common::CPP_VERSION::AudioSource; using ::android::hardware::audio::common::CPP_VERSION::AudioUsage; @@ -192,7 +192,7 @@ status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, const native_handle *handle = hidlInfo.sharedMemory.handle(); if (handle->numFds > 0) { info->shared_memory_fd = handle->data[0]; -#if MAJOR_VERSION == 4 +#if MAJOR_VERSION >= 4 info->flags = audio_mmap_buffer_flag(hidlInfo.flags); #endif info->buffer_size_frames = hidlInfo.bufferSizeFrames; @@ -603,7 +603,7 @@ status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& /* sourceM // Audio HAL V2.0 does not support propagating source metadata return INVALID_OPERATION; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 /** Transform a standard collection to an HIDL vector. */ template static auto transformToHidlVec(const Values& values, ElementConverter converter) { @@ -833,7 +833,7 @@ status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& /* sinkMetadata return INVALID_OPERATION; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 status_t StreamInHalHidl::getActiveMicrophones( std::vector *microphonesInfo) { if (!mStream) return NO_INIT; diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp index 0aba7c119b..26d30d4746 100644 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -354,7 +354,7 @@ status_t StreamInHalLocal::getActiveMicrophones( std::vector *microphones __unused) { return INVALID_OPERATION; } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 status_t StreamInHalLocal::getActiveMicrophones(std::vector *microphones) { if (mStream->get_active_microphones == NULL) return INVALID_OPERATION; size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; diff --git a/media/libaudiohal/impl/VersionUtils.h b/media/libaudiohal/impl/VersionUtils.h index b764e40b0a..eb0a42a7e7 100644 --- a/media/libaudiohal/impl/VersionUtils.h +++ b/media/libaudiohal/impl/VersionUtils.h @@ -42,7 +42,7 @@ Return setParameters(T& object, hidl_vec /*context*/, hidl_vec keys) { return object->setParameters(keys); } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 template Return getParameters(T& object, hidl_vec context, hidl_vec keys, Callback callback) { diff --git a/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h index fa0effc719..1d912a0413 100644 --- a/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h +++ b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h @@ -35,6 +35,11 @@ sp createEffectsFactoryHal(); sp createDevicesFactoryHal(); } // namespace V4_0 +namespace V5_0 { +sp createEffectsFactoryHal(); +sp createDevicesFactoryHal(); +} // namespace V5_0 + } // namespace android #endif // ANDROID_HARDWARE_FACTORY_HAL_HIDL_H -- GitLab From 1cf6b4dff62ffa9a3a329ceda23a0a55870adbb4 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 20 Nov 2018 18:05:44 -0800 Subject: [PATCH 0537/1530] libaudiohal: move to Stream Metadata to common So that it can be used by the Bluetooth HAL. Test: compile Change-Id: I5b122d329811de2c10fc25f457a0eed7b5ece7b6 Signed-off-by: Kevin Rocard --- media/libaudiohal/impl/DeviceHalHidl.cpp | 4 +++- media/libaudiohal/impl/StreamHalHidl.cpp | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index b5ed1987e0..b3ff7572b5 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -49,8 +49,10 @@ using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; -#if MAJOR_VERSION >= 4 +#if MAJOR_VERSION == 4 using ::android::hardware::audio::CPP_VERSION::SinkMetadata; +#elif MAJOR_VERSION == 5 +using ::android::hardware::audio::common::CPP_VERSION::SinkMetadata; #endif namespace android { diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 04d636beef..6b59f5cb98 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -49,8 +49,18 @@ using ::android::hardware::audio::common::CPP_VERSION::AudioContentType; using ::android::hardware::audio::common::CPP_VERSION::AudioSource; using ::android::hardware::audio::common::CPP_VERSION::AudioUsage; using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; +#endif + +#if MAJOR_VERSION == 4 using ::android::hardware::audio::CPP_VERSION::PlaybackTrackMetadata; using ::android::hardware::audio::CPP_VERSION::RecordTrackMetadata; +using HalSinkMetadata = ::android::hardware::audio::CPP_VERSION::SinkMetadata; +using HalSourceMetadata = ::android::hardware::audio::CPP_VERSION::SourceMetadata; +#elif MAJOR_VERSION == 5 +using ::android::hardware::audio::common::CPP_VERSION::PlaybackTrackMetadata; +using ::android::hardware::audio::common::CPP_VERSION::RecordTrackMetadata; +using HalSinkMetadata = ::android::hardware::audio::common::CPP_VERSION::SinkMetadata; +using HalSourceMetadata = ::android::hardware::audio::common::CPP_VERSION::SourceMetadata; #endif namespace android { @@ -357,7 +367,7 @@ status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) parametersToHal(hidl_vec(parameters), &halParameters); return setParameters(halParameters); } -#elif MAJOR_VERSION == 4 +#elif MAJOR_VERSION >= 4 status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) { if (mStream == 0) return NO_INIT; return processReturn("selectPresentation", @@ -614,7 +624,7 @@ static auto transformToHidlVec(const Values& values, ElementConverter converter) } status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& sourceMetadata) { - hardware::audio::CPP_VERSION::SourceMetadata halMetadata = { + HalSourceMetadata halMetadata = { .tracks = transformToHidlVec(sourceMetadata.tracks, [](const playback_track_metadata& metadata) -> PlaybackTrackMetadata { return { @@ -853,7 +863,7 @@ status_t StreamInHalHidl::getActiveMicrophones( } status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& sinkMetadata) { - hardware::audio::CPP_VERSION::SinkMetadata halMetadata = { + HalSinkMetadata halMetadata = { .tracks = transformToHidlVec(sinkMetadata.tracks, [](const record_track_metadata& metadata) -> RecordTrackMetadata { return { -- GitLab From 7094005b99b27bb3650ff529d3196c32f60be31e Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Fri, 30 Nov 2018 19:40:45 +0900 Subject: [PATCH 0538/1530] AML: Use KeyEvent.isMediaSessionKey() which is public Bug: 119789707 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: I60f3271b205348ed64241851304fe7213a535c8e --- .../apex/java/android/media/session/MediaController.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index a8e77c7fc8..60f74ab071 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -149,12 +149,9 @@ public final class MediaController { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } - //TODO(b/119789707): Resolve hidden API usage: KeyEvent#isMediaKey - /* - if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { + if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { return false; } - */ try { //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file. // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling. -- GitLab From 58e523a8cc22a696c56c55fab58137b24966391a Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Mon, 3 Dec 2018 18:40:34 +0900 Subject: [PATCH 0539/1530] AML: Use MediaSessionManager.createSession() which is @SystemApi Bug: 119749862 Test: mmm . (under frameworks/av/packages/MediaComponents/apex) Change-Id: I6266321ed9dea7707b8a80efe2fdac021674e25e --- .../apex/java/android/media/session/MediaSession.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index b40e3b0659..42d92d1935 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -179,9 +179,7 @@ public final class MediaSession { MediaSessionManager manager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); try { - //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession - //mBinder = manager.createSession(mCbStub, tag, userId); - mBinder = null; //TODO: remove this. + mBinder = manager.createSession(mCbStub, tag, userId); mSessionToken = new Token(mBinder.getController()); mController = new MediaController(context, mSessionToken); } catch (RemoteException e) { -- GitLab From a3901e95412eacedb5682eb4cda9d63d3ef9f734 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 8 Oct 2018 13:54:38 -0700 Subject: [PATCH 0540/1530] aaudio: reduce log spam Remove logging that is redundant or no longer needed. Test: write_sine_callback -pl Test: write_sine_callback -pl -r44100 Change-Id: I9004d32509757847764b9533c5cda743c6bd81a0 --- media/libaaudio/src/binding/IAAudioService.cpp | 1 - .../libaaudio/src/client/AudioStreamInternal.cpp | 2 -- media/libaaudio/src/core/AAudioAudio.cpp | 16 ++++++++-------- media/libaaudio/src/core/AudioStream.cpp | 1 - services/oboeservice/AAudioEndpointManager.cpp | 2 +- services/oboeservice/AAudioService.cpp | 5 ++--- services/oboeservice/AAudioServiceEndpoint.cpp | 4 ---- services/oboeservice/AAudioServiceEndpoint.h | 2 +- .../oboeservice/AAudioServiceEndpointMMAP.cpp | 6 ++---- .../oboeservice/AAudioServiceEndpointPlay.cpp | 4 ---- services/oboeservice/AAudioServiceEndpointPlay.h | 1 - .../oboeservice/AAudioServiceEndpointShared.h | 3 +-- services/oboeservice/AAudioServiceStreamBase.cpp | 2 -- .../oboeservice/AAudioServiceStreamShared.cpp | 1 - 14 files changed, 15 insertions(+), 35 deletions(-) diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp index 620edc75df..9b32543830 100644 --- a/media/libaaudio/src/binding/IAAudioService.cpp +++ b/media/libaaudio/src/binding/IAAudioService.cpp @@ -72,7 +72,6 @@ public: ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err); return AAudioConvert_androidToAAudioResult(err); } else if (stream < 0) { - ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream); return stream; } err = configurationOutput.readFromParcel(&reply); diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index 0a8021ad95..fffcda016e 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -131,7 +131,6 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput); } if (mServiceStreamHandle < 0) { - ALOGE("%s - openStream() returned %d", __func__, mServiceStreamHandle); return mServiceStreamHandle; } @@ -693,7 +692,6 @@ aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) { } aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(adjustedFrames, &actualFrames); - ALOGD("setBufferSize() req = %d => %d", requestedFrames, actualFrames); if (result < 0) { return result; } else { diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp index e272f2a70c..2fb39868f7 100644 --- a/media/libaaudio/src/core/AAudioAudio.cpp +++ b/media/libaaudio/src/core/AAudioAudio.cpp @@ -241,7 +241,7 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* { AudioStream *audioStream = nullptr; // Please leave these logs because they are very helpful when debugging. - ALOGD("AAudioStreamBuilder_openStream() called ----------------------------------------"); + ALOGD("%s() called ----------------------------------------", __func__); AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr); aaudio_result_t result = streamBuilder->build(&audioStream); ALOGD("AAudioStreamBuilder_openStream() returns %d = %s for (%p) ----------------", @@ -269,7 +269,7 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) { aaudio_result_t result = AAUDIO_ERROR_NULL; AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("AAudioStream_close(%p) called ---------------", stream); + ALOGD("%s(%p) called ---------------", __func__, stream); if (audioStream != nullptr) { result = audioStream->safeClose(); // Close will only fail if called illegally, for example, from a callback. @@ -284,7 +284,7 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) // We're potentially freeing `stream` above, so its use here makes some // static analysis tools unhappy. Casting to uintptr_t helps assure // said tools that we're not doing anything bad here. - ALOGD("AAudioStream_close(%#" PRIxPTR ") returned %d ---------", + ALOGD("%s(%#" PRIxPTR ") returned %d ---------", __func__, reinterpret_cast(stream), result); return result; } @@ -292,30 +292,30 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("AAudioStream_requestStart(%p) called --------------", stream); + ALOGD("%s(%p) called --------------", __func__, stream); aaudio_result_t result = audioStream->systemStart(); - ALOGD("AAudioStream_requestStart(%p) returned %d ---------", stream, result); + ALOGD("%s(%p) returned %d ---------", __func__, stream, result); return result; } AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("AAudioStream_requestPause(%p)", stream); + ALOGD("%s(%p) called", __func__, stream); return audioStream->systemPause(); } AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("AAudioStream_requestFlush(%p)", stream); + ALOGD("%s(%p) called", __func__, stream); return audioStream->safeFlush(); } AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("AAudioStream_requestStop(%p)", stream); + ALOGD("%s(%p) called", __func__, stream); return audioStream->systemStop(); } diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp index 358021b4cb..391af29c3b 100644 --- a/media/libaaudio/src/core/AudioStream.cpp +++ b/media/libaaudio/src/core/AudioStream.cpp @@ -37,7 +37,6 @@ AudioStream::AudioStream() } AudioStream::~AudioStream() { - ALOGD("destroying %p, state = %s", this, AAudio_convertStreamStateToText(getState())); // If the stream is deleted when OPEN or in use then audio resources will leak. // This would indicate an internal error. So we want to find this ASAP. LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp index b1854bfbf8..cca1895658 100644 --- a/services/oboeservice/AAudioEndpointManager.cpp +++ b/services/oboeservice/AAudioEndpointManager.cpp @@ -172,7 +172,7 @@ sp AAudioEndpointManager::openExclusiveEndpoint( aaudio_result_t result = endpoint->open(request); if (result != AAUDIO_OK) { - ALOGE("openExclusiveEndpoint(), open failed"); + ALOGV("openExclusiveEndpoint(), open failed"); endpoint.clear(); } else { mExclusiveStreams.push_back(endpointMMap); diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp index 5ec997c624..2fbaeb453e 100644 --- a/services/oboeservice/AAudioService.cpp +++ b/services/oboeservice/AAudioService.cpp @@ -118,7 +118,7 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req } } - // If SHARED requested or if EXCLUSIVE failed. + // Try SHARED if SHARED requested or if EXCLUSIVE failed. if (sharingMode == AAUDIO_SHARING_MODE_SHARED) { serviceStream = new AAudioServiceStreamShared(*this); result = serviceStream->open(request); @@ -132,8 +132,7 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req if (result != AAUDIO_OK) { serviceStream.clear(); - ALOGE("openStream(): failed, return %d = %s", - result, AAudio_convertResultToText(result)); + ALOGW("openStream(): failed, return %d = %s", result, AAudio_convertResultToText(result)); return result; } else { aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get()); diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp index 24ab65efd7..539735ada2 100644 --- a/services/oboeservice/AAudioServiceEndpoint.cpp +++ b/services/oboeservice/AAudioServiceEndpoint.cpp @@ -38,10 +38,6 @@ using namespace android; // TODO just import names needed using namespace aaudio; // TODO just import names needed -AAudioServiceEndpoint::~AAudioServiceEndpoint() { - ALOGD("%s(%p) destroyed", __func__, this); -} - std::string AAudioServiceEndpoint::dump() const { std::stringstream result; diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h index a134a13566..43b0a373da 100644 --- a/services/oboeservice/AAudioServiceEndpoint.h +++ b/services/oboeservice/AAudioServiceEndpoint.h @@ -41,7 +41,7 @@ class AAudioServiceEndpoint , public AAudioStreamParameters { public: - virtual ~AAudioServiceEndpoint(); + virtual ~AAudioServiceEndpoint() = default; virtual std::string dump() const; diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index 18fcd35b61..e4dbee1890 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -101,8 +101,6 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques .flags = AUDIO_FLAG_LOW_LATENCY, .tags = "" }; - ALOGD("%s(%p) MMAP attributes.usage = %d, content_type = %d, source = %d", - __func__, this, attributes.usage, attributes.content_type, attributes.source); mMmapClient.clientUid = request.getUserId(); mMmapClient.clientPid = request.getProcessId(); @@ -163,12 +161,12 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n", __func__, mMmapClient.clientUid, mMmapClient.clientPid, mPortHandle); if (status != OK) { - ALOGE("%s() openMmapStream() returned status %d", __func__, status); + ALOGE("%s() - openMmapStream() returned status %d", __func__, status); return AAUDIO_ERROR_UNAVAILABLE; } if (deviceId == AAUDIO_UNSPECIFIED) { - ALOGW("%s() openMmapStream() failed to set deviceId", __func__); + ALOGW("%s() - openMmapStream() failed to set deviceId", __func__); } setDeviceId(deviceId); diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp index a274466c3e..923a1a4427 100644 --- a/services/oboeservice/AAudioServiceEndpointPlay.cpp +++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp @@ -47,10 +47,6 @@ AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService mStreamInternal = &mStreamInternalPlay; } -AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() { - ALOGD("%s(%p) destroyed", __func__, this); -} - aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) { aaudio_result_t result = AAudioServiceEndpointShared::open(request); if (result == AAUDIO_OK) { diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h index a0a383cd9e..981e43073a 100644 --- a/services/oboeservice/AAudioServiceEndpointPlay.h +++ b/services/oboeservice/AAudioServiceEndpointPlay.h @@ -39,7 +39,6 @@ namespace aaudio { class AAudioServiceEndpointPlay : public AAudioServiceEndpointShared { public: explicit AAudioServiceEndpointPlay(android::AAudioService &audioService); - virtual ~AAudioServiceEndpointPlay(); aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override; diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h index d671710447..bfc1744253 100644 --- a/services/oboeservice/AAudioServiceEndpointShared.h +++ b/services/oboeservice/AAudioServiceEndpointShared.h @@ -52,8 +52,7 @@ public: aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override; - virtual void *callbackLoop() = 0; - + virtual void *callbackLoop() = 0; protected: diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp index 12be4a33e4..354b36a933 100644 --- a/services/oboeservice/AAudioServiceStreamBase.cpp +++ b/services/oboeservice/AAudioServiceStreamBase.cpp @@ -51,7 +51,6 @@ AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService) } AAudioServiceStreamBase::~AAudioServiceStreamBase() { - ALOGD("~AAudioServiceStreamBase() destroying %p", this); // If the stream is deleted when OPEN or in use then audio resources will leak. // This would indicate an internal error. So we want to find this ASAP. LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED @@ -110,7 +109,6 @@ aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, request); if (mServiceEndpoint == nullptr) { - ALOGE("%s() openEndpoint() failed", __func__); result = AAUDIO_ERROR_UNAVAILABLE; goto error; } diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp index dbc2c2ed15..d5450fe987 100644 --- a/services/oboeservice/AAudioServiceStreamShared.cpp +++ b/services/oboeservice/AAudioServiceStreamShared.cpp @@ -128,7 +128,6 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques aaudio_result_t result = AAudioServiceStreamBase::open(request); if (result != AAUDIO_OK) { - ALOGE("%s() returned %d", __func__, result); return result; } -- GitLab From e8c8b4359835a12a3d1bf84f281c36bce031bb33 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 17 Oct 2018 10:08:02 -0700 Subject: [PATCH 0541/1530] audio policy: implement uid state management for capture Receive app state updates from ActivityManager and use it to implement concurrent capture policy. Bug: 111438757 Test: Manual test with solotester app concurrently with Camera, Duo and Assistant Change-Id: I979ad4ecc8b926abb64e1b321b43bd7bd442a8f1 --- .../service/AudioPolicyService.cpp | 191 +++++++++++++----- .../audiopolicy/service/AudioPolicyService.h | 26 ++- .../camera/libcameraservice/CameraService.h | 1 + 3 files changed, 161 insertions(+), 57 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 018d9e36d6..78dbf5f16b 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -340,14 +340,41 @@ status_t AudioPolicyService::dumpInternals(int fd) return NO_ERROR; } -void AudioPolicyService::setAppState(uid_t uid, app_state_t state) +void AudioPolicyService::updateUidStates() { - { - Mutex::Autolock _l(mLock); - if (mAudioPolicyManager) { - AutoCallerClear acc; - mAudioPolicyManager->setAppState(uid, state); - } + Mutex::Autolock _l(mLock); + updateUidStates_l(); +} + +void AudioPolicyService::updateUidStates_l() +{ + //TODO: implement real concurrent capture policy: for now just apply each app state directly + for (size_t i =0; i < mAudioRecordClients.size(); i++) { + sp current = mAudioRecordClients[i]; + if (!current->active) continue; + setAppState_l(current->uid, apmStatFromAmState(mUidPolicy->getUidState(current->uid))); + } +} + +/* static */ +app_state_t AudioPolicyService::apmStatFromAmState(int amState) { + switch (amState) { + case ActivityManager::PROCESS_STATE_UNKNOWN: + return APP_STATE_IDLE; + case ActivityManager::PROCESS_STATE_TOP: + return APP_STATE_TOP; + default: + break; + } + return APP_STATE_FOREGROUND; +} + +void AudioPolicyService::setAppState_l(uid_t uid, app_state_t state) +{ + AutoCallerClear acc; + + if (mAudioPolicyManager) { + mAudioPolicyManager->setAppState(uid, state); } sp af = AudioSystem::get_audio_flinger(); if (af) { @@ -511,7 +538,8 @@ void AudioPolicyService::UidPolicy::registerSelf() { ActivityManager am; am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_IDLE - | ActivityManager::UID_OBSERVER_ACTIVE, + | ActivityManager::UID_OBSERVER_ACTIVE + | ActivityManager::UID_OBSERVER_PROCSTATE, ActivityManager::PROCESS_STATE_UNKNOWN, String16("audioserver")); status_t res = am.linkToDeath(this); @@ -538,8 +566,7 @@ void AudioPolicyService::UidPolicy::binderDied(__unused const wp &who) mObserverRegistered = false; } -bool AudioPolicyService::UidPolicy::isUidActive(uid_t uid) { - if (isServiceUid(uid)) return true; +void AudioPolicyService::UidPolicy::checkRegistered() { bool needToReregister = false; { Mutex::Autolock _l(mLock); @@ -549,91 +576,157 @@ bool AudioPolicyService::UidPolicy::isUidActive(uid_t uid) { // Looks like ActivityManager has died previously, attempt to re-register. registerSelf(); } +} + +bool AudioPolicyService::UidPolicy::isUidActive(uid_t uid) { + if (isServiceUid(uid)) return true; + checkRegistered(); { Mutex::Autolock _l(mLock); auto overrideIter = mOverrideUids.find(uid); if (overrideIter != mOverrideUids.end()) { - return overrideIter->second; + return overrideIter->second.first; } // In an absense of the ActivityManager, assume everything to be active. if (!mObserverRegistered) return true; auto cacheIter = mCachedUids.find(uid); if (cacheIter != mCachedUids.end()) { - return cacheIter->second; + return cacheIter->second.first; } } ActivityManager am; bool active = am.isUidActive(uid, String16("audioserver")); { Mutex::Autolock _l(mLock); - mCachedUids.insert(std::pair(uid, active)); + mCachedUids.insert(std::pair>(uid, std::pair(active, + ActivityManager::PROCESS_STATE_UNKNOWN))); } return active; } +int AudioPolicyService::UidPolicy::getUidState(uid_t uid) { + if (isServiceUid(uid)) { + return ActivityManager::PROCESS_STATE_TOP; + } + checkRegistered(); + { + Mutex::Autolock _l(mLock); + auto overrideIter = mOverrideUids.find(uid); + if (overrideIter != mOverrideUids.end()) { + if (overrideIter->second.first) { + if (overrideIter->second.second != ActivityManager::PROCESS_STATE_UNKNOWN) { + return overrideIter->second.second; + } else { + auto cacheIter = mCachedUids.find(uid); + if (cacheIter != mCachedUids.end()) { + return cacheIter->second.second; + } + } + } + return ActivityManager::PROCESS_STATE_UNKNOWN; + } + // In an absense of the ActivityManager, assume everything to be active. + if (!mObserverRegistered) { + return ActivityManager::PROCESS_STATE_TOP; + } + auto cacheIter = mCachedUids.find(uid); + if (cacheIter != mCachedUids.end()) { + if (cacheIter->second.first) { + return cacheIter->second.second; + } else { + return ActivityManager::PROCESS_STATE_UNKNOWN; + } + } + } + ActivityManager am; + bool active = am.isUidActive(uid, String16("audioserver")); + int state = ActivityManager::PROCESS_STATE_UNKNOWN; + if (active) { + state = am.getUidProcessState(uid, String16("audioserver")); + } + { + Mutex::Autolock _l(mLock); + mCachedUids.insert(std::pair>(uid, std::pair(active, state))); + } + return state; +} + void AudioPolicyService::UidPolicy::onUidActive(uid_t uid) { - updateUidCache(uid, true, true); + updateUid(&mCachedUids, uid, true, ActivityManager::PROCESS_STATE_UNKNOWN, true); } void AudioPolicyService::UidPolicy::onUidGone(uid_t uid, __unused bool disabled) { - updateUidCache(uid, false, false); + updateUid(&mCachedUids, uid, false, ActivityManager::PROCESS_STATE_UNKNOWN, false); } void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) { - updateUidCache(uid, false, true); + updateUid(&mCachedUids, uid, false, ActivityManager::PROCESS_STATE_UNKNOWN, true); } -void AudioPolicyService::UidPolicy::notifyService(uid_t uid, bool active) { - sp service = mService.promote(); - if (service != nullptr) { - service->setAppState(uid, active ? - APP_STATE_FOREGROUND : - APP_STATE_IDLE); +void AudioPolicyService::UidPolicy::onUidStateChanged(uid_t uid, + int32_t procState, + int64_t procStateSeq __unused) { + if (procState != ActivityManager::PROCESS_STATE_UNKNOWN) { + updateUid(&mCachedUids, uid, true, procState, true); } } void AudioPolicyService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) { - if (isServiceUid(uid)) return; - bool wasOverridden = false, wasActive = false; - { - Mutex::Autolock _l(mLock); - updateUidLocked(&mOverrideUids, uid, active, insert, &wasOverridden, &wasActive); - } - if (!wasOverridden && insert) { - notifyService(uid, active); // Started to override. - } else if (wasOverridden && !insert) { - notifyService(uid, isUidActive(uid)); // Override ceased, notify with ground truth. - } else if (wasActive != active) { - notifyService(uid, active); // Override updated. + updateUid(&mOverrideUids, uid, active, ActivityManager::PROCESS_STATE_UNKNOWN, insert); +} + +void AudioPolicyService::UidPolicy::notifyService() { + sp service = mService.promote(); + if (service != nullptr) { + service->updateUidStates(); } } -void AudioPolicyService::UidPolicy::updateUidCache(uid_t uid, bool active, bool insert) { - if (isServiceUid(uid)) return; - bool wasActive = false; +void AudioPolicyService::UidPolicy::updateUid(std::unordered_map> *uids, + uid_t uid, + bool active, + int state, + bool insert) { + if (isServiceUid(uid)) { + return; + } + bool wasActive = isUidActive(uid); + int previousState = getUidState(uid); { Mutex::Autolock _l(mLock); - updateUidLocked(&mCachedUids, uid, active, insert, nullptr, &wasActive); - // Do not notify service if currently overridden. - if (mOverrideUids.find(uid) != mOverrideUids.end()) return; + updateUidLocked(uids, uid, active, state, insert); + } + if (wasActive != isUidActive(uid) || state != previousState) { + notifyService(); } - bool nowActive = active && insert; - if (wasActive != nowActive) notifyService(uid, nowActive); } -void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map *uids, - uid_t uid, bool active, bool insert, bool *wasThere, bool *wasActive) { +void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map> *uids, + uid_t uid, + bool active, + int state, + bool insert) { auto it = uids->find(uid); if (it != uids->end()) { - if (wasThere != nullptr) *wasThere = true; - if (wasActive != nullptr) *wasActive = it->second; if (insert) { - it->second = active; + if (state == ActivityManager::PROCESS_STATE_UNKNOWN) { + it->second.first = active; + } + if (it->second.first) { + it->second.second = state; + } else { + it->second.second = ActivityManager::PROCESS_STATE_UNKNOWN; + } } else { uids->erase(it); } - } else if (insert) { - uids->insert(std::pair(uid, active)); + } else if (insert && (state == ActivityManager::PROCESS_STATE_UNKNOWN)) { + uids->insert(std::pair>(uid, + std::pair(active, state))); } } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index ec32511387..4d7235fce8 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -253,7 +253,7 @@ private: virtual status_t shellCommand(int in, int out, int err, Vector& args); // Sets whether the given UID records only silence - virtual void setAppState(uid_t uid, app_state_t state); + virtual void setAppState_l(uid_t uid, app_state_t state); // Overrides the UID state as if it is idle status_t handleSetUidState(Vector& args, int err); @@ -271,6 +271,11 @@ private: status_t getAudioPolicyEffects(sp& audioPolicyEffects); + app_state_t apmStatFromAmState(int amState); + + void updateUidStates(); + void updateUidStates_l(); + // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID // transitions to an active state we will start reporting buffers with data. This approach @@ -289,6 +294,7 @@ private: void binderDied(const wp &who) override; bool isUidActive(uid_t uid); + int getUidState(uid_t uid); void setAssistantUid(uid_t uid) { mAssistantUid = uid; } bool isAssistantUid(uid_t uid) { return uid == mAssistantUid; } void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } @@ -298,22 +304,26 @@ private: void onUidActive(uid_t uid) override; void onUidGone(uid_t uid, bool disabled) override; void onUidIdle(uid_t uid, bool disabled) override; + void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq); void addOverrideUid(uid_t uid, bool active) { updateOverrideUid(uid, active, true); } void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); } - private: - void notifyService(uid_t uid, bool active); + void updateUid(std::unordered_map> *uids, + uid_t uid, bool active, int state, bool insert); + + private: + void notifyService(); void updateOverrideUid(uid_t uid, bool active, bool insert); - void updateUidCache(uid_t uid, bool active, bool insert); - void updateUidLocked(std::unordered_map *uids, - uid_t uid, bool active, bool insert, bool *wasThere, bool *wasActive); + void updateUidLocked(std::unordered_map> *uids, + uid_t uid, bool active, int state, bool insert); + void checkRegistered(); wp mService; Mutex mLock; bool mObserverRegistered; - std::unordered_map mOverrideUids; - std::unordered_map mCachedUids; + std::unordered_map> mOverrideUids; + std::unordered_map> mCachedUids; uid_t mAssistantUid; std::vector mA11yUids; }; diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 064863f983..d332f6e6d3 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -536,6 +536,7 @@ private: void onUidGone(uid_t uid, bool disabled); void onUidActive(uid_t uid); void onUidIdle(uid_t uid, bool disabled); + void onUidStateChanged(uid_t uid __unused, int32_t procState __unused, int64_t procStateSeq __unused) {} void addOverrideUid(uid_t uid, String16 callingPackage, bool active); void removeOverrideUid(uid_t uid, String16 callingPackage); -- GitLab From 3170810aeee3f7059e5515855385e3d7df7799a4 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 4 Dec 2018 20:40:41 +0900 Subject: [PATCH 0542/1530] AML: Use 4-params consturctor of RemoteUserInfo Bug: 119752205 Test: mmm . (under frameworks/av/packages/MediaComponents/apex) Change-Id: Ic9a1df35f7c8db12e5cf9c7981395a46226ca936 --- .../apex/java/android/media/session/MediaSession.java | 4 ---- .../apex/java/android/service/media/MediaBrowserService.java | 4 ---- 2 files changed, 8 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index b40e3b0659..04dc0b8866 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -1072,12 +1072,8 @@ public final class MediaSession { private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, ISessionControllerCallback caller) { - //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo - /* return new RemoteUserInfo(packageName, pid, uid, caller != null ? caller.asBinder() : null); - */ - return new RemoteUserInfo(packageName, pid, uid); } @Override diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java index 2f2fbaf7e2..fa7696e827 100644 --- a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java +++ b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java @@ -544,12 +544,8 @@ public abstract class MediaBrowserService extends Service { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo - /* return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid, mCurConnection.callbacks.asBinder()); - */ - return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); } /** -- GitLab From f4a342abd2959904caa18b5a0671e8107581ff24 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 4 Dec 2018 08:55:41 -0800 Subject: [PATCH 0543/1530] Fix potential nullptr dereference in RecordThread::dumpInternals The code introduced in ag/5195499 can deref a nullptr. Bug: 117299316 Test: make Change-Id: I6d38f769e583a11d09c137d7b7a1ffd0b547f161 --- services/audioflinger/Threads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 46e40c75ef..45d3a065aa 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7562,7 +7562,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a (void)input->stream->dump(fd); } - const double latencyMs = RecordTrack::checkServerLatencySupported(mFormat, input->flags) + const double latencyMs = RecordTrack::checkServerLatencySupported(mFormat, flags) ? - mTimestamp.getOutputServerLatencyMs(mSampleRate) : 0.; if (latencyMs != 0.) { dprintf(fd, " NormalRecord latency ms: %.2lf\n", latencyMs); -- GitLab From 2524fe520eecf1f3796ef91bfa65ae028efaf887 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 4 Dec 2018 08:58:55 -0800 Subject: [PATCH 0544/1530] Convert capture frame rate metadata Bug: 119779455 Bug: 111407253 Test: CTS Change-Id: I27442b189c0d31b77a9a8b3799e96efdad16fb9d --- media/libstagefright/Utils.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 53c32b2594..ec043f264f 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -588,6 +588,12 @@ static std::vector> stringMappings { } }; +static std::vector> floatMappings { + { + { "capture-rate", kKeyCaptureFramerate }, + } +}; + static std::vector> int64Mappings { { { "exif-offset", kKeyExifOffset }, @@ -632,6 +638,13 @@ void convertMessageToMetaDataFromMappings(const sp &msg, sp } } + for (auto elem : floatMappings) { + float value; + if (msg->findFloat(elem.first, &value)) { + meta->setFloat(elem.second, value); + } + } + for (auto elem : int64Mappings) { int64_t value; if (msg->findInt64(elem.first, &value)) { @@ -663,6 +676,13 @@ void convertMetaDataToMessageFromMappings(const MetaDataBase *meta, sp } } + for (auto elem : floatMappings) { + float value; + if (meta->findFloat(elem.second, &value)) { + format->setFloat(elem.first, value); + } + } + for (auto elem : int64Mappings) { int64_t value; if (meta->findInt64(elem.second, &value)) { -- GitLab From 973db02ac18fa1de9ce6221f47b01af1bdc4bec2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 20 Nov 2018 14:54:31 -0800 Subject: [PATCH 0545/1530] audio flinger: return port ID as track ID to client Return the port ID allocated by audio policy manager instead of the internal track ID allocated by audio flinger when an AudioTrack or AudioRecord is created. This information is more useful for logs and allows to associate information coming from audiopolicy manager with a specific client instance. Bug: 111438757 Test: Manual playback and capture tests Change-Id: Ib467d8fcc34d9a8aa7bcaac0770a741982b847c5 --- media/libaudioclient/AudioRecord.cpp | 64 ++++---- media/libaudioclient/AudioTrack.cpp | 141 +++++++++--------- .../include/media/AudioRecord.h | 14 +- .../libaudioclient/include/media/AudioTrack.h | 11 +- .../include/media/IAudioFlinger.h | 12 +- services/audioflinger/AudioFlinger.cpp | 4 +- services/audioflinger/Tracks.cpp | 15 +- 7 files changed, 141 insertions(+), 120 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 038c854be8..3223647f54 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -211,7 +211,7 @@ AudioRecord::~AudioRecord() mBufferMemory.clear(); IPCThreadState::self()->flushCommands(); ALOGV("%s(%d): releasing session id %d", - __func__, mId, mSessionId); + __func__, mPortId, mSessionId); AudioSystem::releaseAudioSessionId(mSessionId, -1 /*pid*/); } } @@ -239,7 +239,7 @@ status_t AudioRecord::set( pid_t callingPid; pid_t myPid; - // Note mId is not valid until the track is created, so omit mId in ALOG for set. + // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set. ALOGV("%s(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s " "uid %d, pid %d", @@ -356,7 +356,7 @@ status_t AudioRecord::set( // create the IAudioRecord status = createRecord_l(0 /*epoch*/, mOpPackageName); - ALOGV("%s(%d): status %d", __func__, mId, status); + ALOGV("%s(%d): status %d", __func__, mPortId, status); if (status != NO_ERROR) { if (mAudioRecordThread != 0) { @@ -393,7 +393,7 @@ exit: status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) { - ALOGV("%s(%d): sync event %d trigger session %d", __func__, mId, event, triggerSession); + ALOGV("%s(%d): sync event %d trigger session %d", __func__, mPortId, event, triggerSession); AutoMutex lock(mLock); if (mActive) { @@ -434,7 +434,7 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri if (status != NO_ERROR) { mActive = false; - ALOGE("%s(%d): status %d", __func__, mId, status); + ALOGE("%s(%d): status %d", __func__, mPortId, status); } else { sp t = mAudioRecordThread; if (t != 0) { @@ -458,7 +458,7 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri void AudioRecord::stop() { AutoMutex lock(mLock); - ALOGV("%s(%d): mActive:%d\n", __func__, mId, mActive); + ALOGV("%s(%d): mActive:%d\n", __func__, mPortId, mActive); if (!mActive) { return; } @@ -638,7 +638,7 @@ status_t AudioRecord::dump(int fd, const Vector& args __unused) const result.append(" AudioRecord::dump\n"); result.appendFormat(" id(%d) status(%d), active(%d), session Id(%d)\n", - mId, mStatus, mActive, mSessionId); + mPortId, mStatus, mActive, mSessionId); result.appendFormat(" flags(%#x), req. flags(%#x), audio source(%d)\n", mFlags, mOrigFlags, mAttributes.source); result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u), sample rate(%u)\n", @@ -680,7 +680,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String status_t status; if (audioFlinger == 0) { - ALOGE("%s(%d): Could not get audioflinger", __func__, mId); + ALOGE("%s(%d): Could not get audioflinger", __func__, mPortId); status = NO_INIT; goto exit; } @@ -708,7 +708,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String (mTransfer == TRANSFER_OBTAIN); if (!useCaseAllowed) { ALOGW("%s(%d): AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s", - __func__, mId, + __func__, mPortId, convertTransferToText(mTransfer)); mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST | AUDIO_INPUT_FLAG_RAW)); @@ -744,7 +744,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String if (status != NO_ERROR) { ALOGE("%s(%d): AudioFlinger could not create record track, status: %d", - __func__, mId, status); + __func__, mPortId, status); goto exit; } ALOG_ASSERT(record != 0); @@ -755,7 +755,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String mAwaitBoost = false; if (output.flags & AUDIO_INPUT_FLAG_FAST) { ALOGI("%s(%d): AUDIO_INPUT_FLAG_FAST successful; frameCount %zu -> %zu", - __func__, mId, + __func__, mPortId, mReqFrameCount, output.frameCount); mAwaitBoost = true; } @@ -765,13 +765,13 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String mSampleRate = output.sampleRate; if (output.cblk == 0) { - ALOGE("%s(%d): Could not get control block", __func__, mId); + ALOGE("%s(%d): Could not get control block", __func__, mPortId); status = NO_INIT; goto exit; } iMemPointer = output.cblk ->pointer(); if (iMemPointer == NULL) { - ALOGE("%s(%d): Could not get control block pointer", __func__, mId); + ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -786,7 +786,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String } else { buffers = output.buffers->pointer(); if (buffers == NULL) { - ALOGE("%s(%d): Could not get buffer pointer", __func__, mId); + ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -800,14 +800,14 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String mAudioRecord = record; mCblkMemory = output.cblk; mBufferMemory = output.buffers; - mId = output.trackId; + mPortId = output.portId; IPCThreadState::self()->flushCommands(); mCblk = cblk; // note that output.frameCount is the (possibly revised) value of mReqFrameCount if (output.frameCount < mReqFrameCount || (mReqFrameCount == 0 && output.frameCount == 0)) { ALOGW("%s(%d): Requested frameCount %zu but received frameCount %zu", - __func__, mId, + __func__, mPortId, mReqFrameCount, output.frameCount); } @@ -815,7 +815,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String // The computation is done on server side. if (mNotificationFramesReq > 0 && output.notificationFrameCount != mNotificationFramesReq) { ALOGW("%s(%d): Server adjusted notificationFrames from %u to %zu for frameCount %zu", - __func__, mId, + __func__, mPortId, mNotificationFramesReq, output.notificationFrameCount, output.frameCount); } mNotificationFramesAct = (uint32_t)output.notificationFrameCount; @@ -883,7 +883,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_ timeout.tv_nsec = (long) (ms % 1000) * 1000000; requested = &timeout; } else { - ALOGE("%s(%d): invalid waitCount %d", __func__, mId, waitCount); + ALOGE("%s(%d): invalid waitCount %d", __func__, mPortId, waitCount); requested = NULL; } return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig); @@ -993,7 +993,7 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking) // sanity-check. user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("%s(%d) (buffer=%p, size=%zu (%zu)", - __func__, mId, buffer, userSize, userSize); + __func__, mPortId, buffer, userSize, userSize); return BAD_VALUE; } @@ -1050,7 +1050,7 @@ nsecs_t AudioRecord::processAudioBuffer() pollUs <<= 1; } while (tryCounter-- > 0); if (tryCounter < 0) { - ALOGE("%s(%d): did not receive expected priority boost on time", __func__, mId); + ALOGE("%s(%d): did not receive expected priority boost on time", __func__, mPortId); } // Run again immediately return 0; @@ -1174,7 +1174,7 @@ nsecs_t AudioRecord::processAudioBuffer() timeout.tv_sec = ns / 1000000000LL; timeout.tv_nsec = ns % 1000000000LL; ALOGV("%s(%d): timeout %ld.%03d", - __func__, mId, timeout.tv_sec, (int) timeout.tv_nsec / 1000000); + __func__, mPortId, timeout.tv_sec, (int) timeout.tv_nsec / 1000000); requested = &timeout; } @@ -1187,17 +1187,17 @@ nsecs_t AudioRecord::processAudioBuffer() status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig); LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0), "%s(%d): obtainBuffer() err=%d frameCount=%zu", - __func__, mId, err, audioBuffer.frameCount); + __func__, mPortId, err, audioBuffer.frameCount); requested = &ClientProxy::kNonBlocking; size_t avail = audioBuffer.frameCount + nonContig; ALOGV("%s(%d): obtainBuffer(%u) returned %zu = %zu + %zu err %d", - __func__, mId, mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); + __func__, mPortId, mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); if (err != NO_ERROR) { if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) { break; } ALOGE("%s(%d): Error %d obtaining an audio buffer, giving up.", - __func__, mId, err); + __func__, mPortId, err); return NS_NEVER; } @@ -1220,7 +1220,7 @@ nsecs_t AudioRecord::processAudioBuffer() // Sanity check on returned size if (ssize_t(readSize) < 0 || readSize > reqSize) { ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes", - __func__, mId, reqSize, ssize_t(readSize)); + __func__, mPortId, reqSize, ssize_t(readSize)); return NS_NEVER; } @@ -1280,7 +1280,7 @@ nsecs_t AudioRecord::processAudioBuffer() status_t AudioRecord::restoreRecord_l(const char *from) { - ALOGW("%s(%d): dead IAudioRecord, creating a new one from %s()", __func__, mId, from); + ALOGW("%s(%d): dead IAudioRecord, creating a new one from %s()", __func__, mPortId, from); ++mSequence; const int INITIAL_RETRIES = 3; @@ -1311,7 +1311,7 @@ retry: } if (result != NO_ERROR) { - ALOGW("%s(%d): failed status %d, retries %d", __func__, mId, result, retries); + ALOGW("%s(%d): failed status %d, retries %d", __func__, mPortId, result, retries); if (--retries > 0) { // leave time for an eventual race condition to clear before retrying usleep(500000); @@ -1330,18 +1330,18 @@ retry: status_t AudioRecord::addAudioDeviceCallback(const sp& callback) { if (callback == 0) { - ALOGW("%s(%d): adding NULL callback!", __func__, mId); + ALOGW("%s(%d): adding NULL callback!", __func__, mPortId); return BAD_VALUE; } AutoMutex lock(mLock); if (mDeviceCallback.unsafe_get() == callback.get()) { - ALOGW("%s(%d): adding same callback!", __func__, mId); + ALOGW("%s(%d): adding same callback!", __func__, mPortId); return INVALID_OPERATION; } status_t status = NO_ERROR; if (mInput != AUDIO_IO_HANDLE_NONE) { if (mDeviceCallback != 0) { - ALOGW("%s(%d): callback already present!", __func__, mId); + ALOGW("%s(%d): callback already present!", __func__, mPortId); AudioSystem::removeAudioDeviceCallback(this, mInput); } status = AudioSystem::addAudioDeviceCallback(this, mInput); @@ -1354,12 +1354,12 @@ status_t AudioRecord::removeAudioDeviceCallback( const sp& callback) { if (callback == 0) { - ALOGW("%s(%d): removing NULL callback!", __func__, mId); + ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } AutoMutex lock(mLock); if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s(%d): removing different callback!", __func__, mId); + ALOGW("%s(%d): removing different callback!", __func__, mPortId); return INVALID_OPERATION; } mDeviceCallback.clear(); diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 8607ee1eca..02324ac6ca 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -344,7 +344,7 @@ AudioTrack::~AudioTrack() mSharedBuffer.clear(); IPCThreadState::self()->flushCommands(); ALOGV("%s(%d), releasing session id %d from %d on behalf of %d", - __func__, mId, + __func__, mPortId, mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid); AudioSystem::releaseAudioSessionId(mSessionId, mClientPid); } @@ -377,7 +377,7 @@ status_t AudioTrack::set( pid_t callingPid; pid_t myPid; - // Note mId is not valid until the track is created, so omit mId in ALOG for set. + // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set. ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d", __func__, @@ -658,7 +658,7 @@ exit: status_t AudioTrack::start() { AutoMutex lock(mLock); - ALOGV("%s(%d): prior state:%s", __func__, mId, stateToString(mState)); + ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState)); if (mState == STATE_ACTIVE) { return INVALID_OPERATION; @@ -699,7 +699,7 @@ status_t AudioTrack::start() // It is possible since flush and stop are asynchronous that the server // is still active at this point. ALOGV("%s(%d): server read:%lld cumulative flushed:%lld client written:%lld", - __func__, mId, + __func__, mPortId, (long long)(mFramesWrittenServerOffset + mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]), (long long)mStartEts.mFlushed, @@ -760,7 +760,7 @@ status_t AudioTrack::start() // Start our local VolumeHandler for restoration purposes. mVolumeHandler->setStarted(); } else { - ALOGE("%s(%d): status %d", __func__, mId, status); + ALOGE("%s(%d): status %d", __func__, mPortId, status); mState = previousState; if (t != 0) { if (previousState != STATE_STOPPING) { @@ -778,7 +778,7 @@ status_t AudioTrack::start() void AudioTrack::stop() { AutoMutex lock(mLock); - ALOGV("%s(%d): prior state:%s", __func__, mId, stateToString(mState)); + ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState)); if (mState != STATE_ACTIVE && mState != STATE_PAUSED) { return; @@ -789,7 +789,7 @@ void AudioTrack::stop() } else { mState = STATE_STOPPED; ALOGD_IF(mSharedBuffer == nullptr, - "%s(%d): called with %u frames delivered", __func__, mId, mReleased.value()); + "%s(%d): called with %u frames delivered", __func__, mPortId, mReleased.value()); mReleased = 0; } @@ -830,7 +830,7 @@ bool AudioTrack::stopped() const void AudioTrack::flush() { AutoMutex lock(mLock); - ALOGV("%s(%d): prior state:%s", __func__, mId, stateToString(mState)); + ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState)); if (mSharedBuffer != 0) { return; @@ -863,7 +863,7 @@ void AudioTrack::flush_l() void AudioTrack::pause() { AutoMutex lock(mLock); - ALOGV("%s(%d): prior state:%s", __func__, mId, stateToString(mState)); + ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState)); if (mState == STATE_ACTIVE) { mState = STATE_PAUSED; @@ -891,7 +891,7 @@ void AudioTrack::pause() uint32_t halFrames; AudioSystem::getRenderPosition(mOutput, &halFrames, &mPausedPosition); ALOGV("%s(%d): for offload, cache current position %u", - __func__, mId, mPausedPosition); + __func__, mPortId, mPausedPosition); } } } @@ -945,7 +945,7 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const status_t AudioTrack::setSampleRate(uint32_t rate) { AutoMutex lock(mLock); - ALOGV("%s(%d): prior state:%s rate:%u", __func__, mId, stateToString(mState), rate); + ALOGV("%s(%d): prior state:%s rate:%u", __func__, mPortId, stateToString(mState), rate); if (rate == mSampleRate) { return NO_ERROR; @@ -1013,7 +1013,7 @@ status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) } ALOGV("%s(%d): mSampleRate:%u mSpeed:%f mPitch:%f", - __func__, mId, mSampleRate, playbackRate.mSpeed, playbackRate.mPitch); + __func__, mPortId, mSampleRate, playbackRate.mSpeed, playbackRate.mPitch); // pitch is emulated by adjusting speed and sampleRate const uint32_t effectiveRate = adjustSampleRate(mSampleRate, playbackRate.mPitch); const float effectiveSpeed = adjustSpeed(playbackRate.mSpeed, playbackRate.mPitch); @@ -1023,17 +1023,17 @@ status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) playbackRateTemp.mPitch = effectivePitch; ALOGV("%s(%d) (effective) mSampleRate:%u mSpeed:%f mPitch:%f", - __func__, mId, effectiveRate, effectiveSpeed, effectivePitch); + __func__, mPortId, effectiveRate, effectiveSpeed, effectivePitch); if (!isAudioPlaybackRateValid(playbackRateTemp)) { ALOGW("%s(%d) (%f, %f) failed (effective rate out of bounds)", - __func__, mId, playbackRate.mSpeed, playbackRate.mPitch); + __func__, mPortId, playbackRate.mSpeed, playbackRate.mPitch); return BAD_VALUE; } // Check if the buffer size is compatible. if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) { ALOGW("%s(%d) (%f, %f) failed (buffer size)", - __func__, mId, playbackRate.mSpeed, playbackRate.mPitch); + __func__, mPortId, playbackRate.mSpeed, playbackRate.mPitch); return BAD_VALUE; } @@ -1041,13 +1041,13 @@ status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) if ((uint64_t)effectiveRate > (uint64_t)mSampleRate * (uint64_t)AUDIO_RESAMPLER_DOWN_RATIO_MAX) { ALOGW("%s(%d) (%f, %f) failed. Resample rate exceeds max accepted value", - __func__, mId, playbackRate.mSpeed, playbackRate.mPitch); + __func__, mPortId, playbackRate.mSpeed, playbackRate.mPitch); return BAD_VALUE; } if ((uint64_t)effectiveRate * (uint64_t)AUDIO_RESAMPLER_UP_RATIO_MAX < (uint64_t)mSampleRate) { ALOGW("%s(%d) (%f, %f) failed. Resample rate below min accepted value", - __func__, mId, playbackRate.mSpeed, playbackRate.mPitch); + __func__, mPortId, playbackRate.mSpeed, playbackRate.mPitch); return BAD_VALUE; } mPlaybackRate = playbackRate; @@ -1249,7 +1249,7 @@ status_t AudioTrack::getPosition(uint32_t *position) if (isOffloaded_l() && ((mState == STATE_PAUSED) || (mState == STATE_PAUSED_STOPPING))) { ALOGV("%s(%d): called in paused state, return cached position %u", - __func__, mId, mPausedPosition); + __func__, mPortId, mPausedPosition); *position = mPausedPosition; return NO_ERROR; } @@ -1395,7 +1395,7 @@ void AudioTrack::updateLatency_l() { status_t status = AudioSystem::getLatency(mOutput, &mAfLatency); if (status != NO_ERROR) { - ALOGW("%s(%d): getLatency(%d) failed status %d", __func__, mId, mOutput, status); + ALOGW("%s(%d): getLatency(%d) failed status %d", __func__, mPortId, mOutput, status); } else { // FIXME don't believe this lie mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate; @@ -1425,7 +1425,7 @@ status_t AudioTrack::createTrack_l() const sp& audioFlinger = AudioSystem::get_audio_flinger(); if (audioFlinger == 0) { ALOGE("%s(%d): Could not get audioflinger", - __func__, mId); + __func__, mPortId); status = NO_INIT; goto exit; } @@ -1451,7 +1451,7 @@ status_t AudioTrack::createTrack_l() if (!fastAllowed) { ALOGW("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by client," " not shared buffer and transfer = %s", - __func__, mId, + __func__, mPortId, convertTransferToText(mTransfer)); mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST); } @@ -1501,7 +1501,7 @@ status_t AudioTrack::createTrack_l() if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) { ALOGE("%s(%d): AudioFlinger could not create track, status: %d output %d", - __func__, mId, status, output.outputId); + __func__, mPortId, status, output.outputId); if (status == NO_ERROR) { status = NO_INIT; } @@ -1522,7 +1522,7 @@ status_t AudioTrack::createTrack_l() mAfFrameCount = output.afFrameCount; mAfSampleRate = output.afSampleRate; mAfLatency = output.afLatencyMs; - mId = output.trackId; + mPortId = output.portId; mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate; @@ -1532,13 +1532,13 @@ status_t AudioTrack::createTrack_l() // FIXME compare to AudioRecord sp iMem = track->getCblk(); if (iMem == 0) { - ALOGE("%s(%d): Could not get control block", __func__, mId); + ALOGE("%s(%d): Could not get control block", __func__, mPortId); status = NO_INIT; goto exit; } void *iMemPointer = iMem->pointer(); if (iMemPointer == NULL) { - ALOGE("%s(%d): Could not get control block pointer", __func__, mId); + ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -1558,13 +1558,13 @@ status_t AudioTrack::createTrack_l() if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { if (output.flags & AUDIO_OUTPUT_FLAG_FAST) { ALOGI("%s(%d): AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu -> %zu", - __func__, mId, mReqFrameCount, mFrameCount); + __func__, mPortId, mReqFrameCount, mFrameCount); if (!mThreadCanCallJava) { mAwaitBoost = true; } } else { ALOGW("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu", - __func__, mId, mReqFrameCount, mFrameCount); + __func__, mPortId, mReqFrameCount, mFrameCount); } } mFlags = output.flags; @@ -1592,7 +1592,7 @@ status_t AudioTrack::createTrack_l() } else { buffers = mSharedBuffer->pointer(); if (buffers == NULL) { - ALOGE("%s(%d): Could not get buffer pointer", __func__, mId); + ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -1681,7 +1681,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t timeout.tv_nsec = (long) (ms % 1000) * 1000000; requested = &timeout; } else { - ALOGE("%s(%d): invalid waitCount %d", __func__, mId, waitCount); + ALOGE("%s(%d): invalid waitCount %d", __func__, mPortId, waitCount); requested = NULL; } return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig); @@ -1792,7 +1792,7 @@ void AudioTrack::restartIfDisabled() int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); if ((mState == STATE_ACTIVE) && (flags & CBLK_DISABLED)) { ALOGW("%s(%d): releaseBuffer() track %p disabled due to previous underrun, restarting", - __func__, mId, this); + __func__, mPortId, this); // FIXME ignoring status mAudioTrack->start(); } @@ -1820,7 +1820,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) // Sanity-check: user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("%s(%d): AudioTrack::write(buffer=%p, size=%zu (%zd)", - __func__, mId, buffer, userSize, userSize); + __func__, mPortId, buffer, userSize, userSize); return BAD_VALUE; } @@ -1893,7 +1893,7 @@ nsecs_t AudioTrack::processAudioBuffer() } while (tryCounter-- > 0); if (tryCounter < 0) { ALOGE("%s(%d): did not receive expected priority boost on time", - __func__, mId); + __func__, mPortId); } // Run again immediately return 0; @@ -2135,7 +2135,7 @@ nsecs_t AudioTrack::processAudioBuffer() timeout.tv_sec = ns / 1000000000LL; timeout.tv_nsec = ns % 1000000000LL; ALOGV("%s(%d): timeout %ld.%03d", - __func__, mId, timeout.tv_sec, (int) timeout.tv_nsec / 1000000); + __func__, mPortId, timeout.tv_sec, (int) timeout.tv_nsec / 1000000); requested = &timeout; } @@ -2148,11 +2148,11 @@ nsecs_t AudioTrack::processAudioBuffer() status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig); LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0), "%s(%d): obtainBuffer() err=%d frameCount=%zu", - __func__, mId, err, audioBuffer.frameCount); + __func__, mPortId, err, audioBuffer.frameCount); requested = &ClientProxy::kNonBlocking; size_t avail = audioBuffer.frameCount + nonContig; ALOGV("%s(%d): obtainBuffer(%u) returned %zu = %zu + %zu err %d", - __func__, mId, mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); + __func__, mPortId, mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err); if (err != NO_ERROR) { if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR || (isOffloaded() && (err == DEAD_OBJECT))) { @@ -2160,7 +2160,7 @@ nsecs_t AudioTrack::processAudioBuffer() return 1000000; } ALOGE("%s(%d): Error %d obtaining an audio buffer, giving up.", - __func__, mId, err); + __func__, mPortId, err); return NS_NEVER; } @@ -2192,7 +2192,7 @@ nsecs_t AudioTrack::processAudioBuffer() // Sanity check on returned size if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) { ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes", - __func__, mId, reqSize, ssize_t(writtenSize)); + __func__, mPortId, reqSize, ssize_t(writtenSize)); return NS_NEVER; } @@ -2296,7 +2296,7 @@ nsecs_t AudioTrack::processAudioBuffer() status_t AudioTrack::restoreTrack_l(const char *from) { ALOGW("%s(%d): dead IAudioTrack, %s, creating a new one from %s()", - __func__, mId, isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from); + __func__, mPortId, isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from); ++mSequence; // refresh the audio configuration cache in this process to make sure we get new @@ -2354,7 +2354,7 @@ retry: } else { mStaticProxy->setBufferPosition(bufferPosition); if (bufferPosition == mFrameCount) { - ALOGD("%s(%d): restoring track at end of static buffer", __func__, mId); + ALOGD("%s(%d): restoring track at end of static buffer", __func__, mPortId); } } } @@ -2384,7 +2384,7 @@ retry: mFramesWrittenAtRestore = mFramesWrittenServerOffset; } if (result != NO_ERROR) { - ALOGW("%s(%d): failed status %d, retries %d", __func__, mId, result, retries); + ALOGW("%s(%d): failed status %d, retries %d", __func__, mPortId, result, retries); if (--retries > 0) { // leave time for an eventual race condition to clear before retrying usleep(500000); @@ -2414,7 +2414,7 @@ Modulo AudioTrack::updateAndGetPosition_l() // in which case the use of uint32_t for these counters has bigger issues. ALOGE_IF(delta < 0, "%s(%d): detected illegal retrograde motion by the server: mServer advanced by %d", - __func__, mId, delta); + __func__, mPortId, delta); mServer = newServer; if (delta > 0) { // avoid retrograde mPosition += delta; @@ -2437,7 +2437,7 @@ bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) "%s(%d): denied " "mAfLatency:%u mAfFrameCount:%zu mAfSampleRate:%u sampleRate:%u speed:%f " "mFrameCount:%zu < minFrameCount:%zu", - __func__, mId, + __func__, mPortId, mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed, mFrameCount, minFrameCount); return allowed; @@ -2452,8 +2452,13 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs) status_t AudioTrack::selectPresentation(int presentationId, int programId) { AutoMutex lock(mLock); - ALOGV("%s(%d): PresentationId:%d ProgramId:%d", __func__, mId, presentationId, programId); - return mAudioTrack->selectPresentation(presentationId, programId); + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyPresentationId), presentationId); + param.addInt(String8(AudioParameter::keyProgramId), programId); + ALOGV("%s(%d): PresentationId/ProgramId[%s]", + __func__, mPortId, param.toString().string()); + + return mAudioTrack->setParameters(param.toString()); } VolumeShaper::Status AudioTrack::applyVolumeShaper( @@ -2478,7 +2483,7 @@ VolumeShaper::Status AudioTrack::applyVolumeShaper( } else { // warn only if not an expected restore failure. ALOGW_IF(!((isOffloadedOrDirect_l() || mDoNotReconnect) && status == DEAD_OBJECT), - "%s(%d): applyVolumeShaper failed: %d", __func__, mId, status); + "%s(%d): applyVolumeShaper failed: %d", __func__, mPortId, status); } return status; } @@ -2520,7 +2525,7 @@ status_t AudioTrack::getTimestamp_l(ExtendedTimestamp *timestamp) } status_t status = mProxy->getTimestamp(timestamp); LOG_ALWAYS_FATAL_IF(status != OK, "%s(%d): status %d not allowed from proxy getTimestamp", - __func__, mId, status); + __func__, mPortId, status); bool found = false; timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesWritten; timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0; @@ -2564,7 +2569,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) break; // offloaded tracks handled below default: LOG_ALWAYS_FATAL("%s(%d): Invalid mState in getTimestamp(): %d", - __func__, mId, mState); + __func__, mPortId, mState); break; } @@ -2599,7 +2604,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) if (location == ExtendedTimestamp::LOCATION_SERVER) { ALOGW_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_KERNEL, "%s(%d): location moved from kernel to server", - __func__, mId); + __func__, mPortId); // check that the last kernel OK time info exists and the positions // are valid (if they predate the current track, the positions may // be zero or negative). @@ -2615,7 +2620,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) (ets.mPosition[ExtendedTimestamp::LOCATION_SERVER_LASTKERNELOK] - ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL_LASTKERNELOK]); ALOGV("%s(%d): frame adjustment:%lld timestamp:%s", - __func__, mId, (long long)frames, ets.toString().c_str()); + __func__, mPortId, (long long)frames, ets.toString().c_str()); if (frames >= ets.mPosition[location]) { timestamp.mPosition = 0; } else { @@ -2624,7 +2629,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) } else if (location == ExtendedTimestamp::LOCATION_KERNEL) { ALOGV_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_SERVER, "%s(%d): location moved from server to kernel", - __func__, mId); + __func__, mPortId); } // We update the timestamp time even when paused. @@ -2648,7 +2653,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) mPreviousLocation = location; } else { // right after AudioTrack is started, one may not find a timestamp - ALOGV("%s(%d): getBestTimestamp did not find timestamp", __func__, mId); + ALOGV("%s(%d): getBestTimestamp did not find timestamp", __func__, mPortId); } } if (status == INVALID_OPERATION) { @@ -2659,7 +2664,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // "zero" for NuPlayer). We don't convert for track restoration as position // does not reset. ALOGV("%s(%d): timestamp server offset:%lld restore frames:%lld", - __func__, mId, + __func__, mPortId, (long long)mFramesWrittenServerOffset, (long long)mFramesWrittenAtRestore); if (mFramesWrittenServerOffset != mFramesWrittenAtRestore) { status = WOULD_BLOCK; @@ -2667,7 +2672,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) } } if (status != NO_ERROR) { - ALOGV_IF(status != WOULD_BLOCK, "%s(%d): getTimestamp error:%#x", __func__, mId, status); + ALOGV_IF(status != WOULD_BLOCK, "%s(%d): getTimestamp error:%#x", __func__, mPortId, status); return status; } if (isOffloadedOrDirect_l()) { @@ -2708,7 +2713,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) ALOGW_IF(!mTimestampStartupGlitchReported, "%s(%d): startup glitch detected" " deltaTimeUs(%lld) deltaPositionUs(%lld) tsmPosition(%u)", - __func__, mId, + __func__, mPortId, (long long)deltaTimeUs, (long long)deltaPositionByUs, timestamp.mPosition); mTimestampStartupGlitchReported = true; @@ -2778,7 +2783,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) if (currentTimeNanos < limitNs) { ALOGD("%s(%d): correcting timestamp time for pause, " "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld", - __func__, mId, + __func__, mPortId, (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs); timestamp.mTime = convertNsToTimespec(limitNs); currentTimeNanos = limitNs; @@ -2787,7 +2792,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // retrograde check if (currentTimeNanos < previousTimeNanos) { ALOGW("%s(%d): retrograde timestamp time corrected, %lld < %lld", - __func__, mId, + __func__, mPortId, (long long)currentTimeNanos, (long long)previousTimeNanos); timestamp.mTime = mPreviousTimestamp.mTime; // currentTimeNanos not used below. @@ -2801,7 +2806,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // Only report once per position instead of spamming the log. if (!mRetrogradeMotionReported) { ALOGW("%s(%d): retrograde timestamp position corrected, %d = %u - %u", - __func__, mId, + __func__, mPortId, deltaPosition, timestamp.mPosition, mPreviousTimestamp.mPosition); @@ -2822,7 +2827,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) const int64_t computedSampleRate = deltaPosition * (long long)NANOS_PER_SECOND / deltaTime; ALOGD("%s(%d): computedSampleRate:%u sampleRate:%u", - __func__, mId, + __func__, mPortId, (unsigned)computedSampleRate, mSampleRate); } #endif @@ -2869,7 +2874,7 @@ status_t AudioTrack::dump(int fd, const Vector& args __unused) const result.append(" AudioTrack::dump\n"); result.appendFormat(" id(%d) status(%d), state(%d), session Id(%d), flags(%#x)\n", - mId, mStatus, mState, mSessionId, mFlags); + mPortId, mStatus, mState, mSessionId, mFlags); result.appendFormat(" stream type(%d), left - right volume(%f, %f)\n", (mStreamType == AUDIO_STREAM_DEFAULT) ? audio_attributes_to_stream_type(&mAttributes) : mStreamType, @@ -2911,18 +2916,18 @@ uint32_t AudioTrack::getUnderrunFrames() const status_t AudioTrack::addAudioDeviceCallback(const sp& callback) { if (callback == 0) { - ALOGW("%s(%d): adding NULL callback!", __func__, mId); + ALOGW("%s(%d): adding NULL callback!", __func__, mPortId); return BAD_VALUE; } AutoMutex lock(mLock); if (mDeviceCallback.unsafe_get() == callback.get()) { - ALOGW("%s(%d): adding same callback!", __func__, mId); + ALOGW("%s(%d): adding same callback!", __func__, mPortId); return INVALID_OPERATION; } status_t status = NO_ERROR; if (mOutput != AUDIO_IO_HANDLE_NONE) { if (mDeviceCallback != 0) { - ALOGW("%s(%d): callback already present!", __func__, mId); + ALOGW("%s(%d): callback already present!", __func__, mPortId); AudioSystem::removeAudioDeviceCallback(this, mOutput); } status = AudioSystem::addAudioDeviceCallback(this, mOutput); @@ -2935,12 +2940,12 @@ status_t AudioTrack::removeAudioDeviceCallback( const sp& callback) { if (callback == 0) { - ALOGW("%s(%d): removing NULL callback!", __func__, mId); + ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } AutoMutex lock(mLock); if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s(%d): removing different callback!", __func__, mId); + ALOGW("%s(%d): removing different callback!", __func__, mPortId); return INVALID_OPERATION; } mDeviceCallback.clear(); @@ -3046,7 +3051,7 @@ bool AudioTrack::hasStarted() case STATE_FLUSHED: return false; // we're not active default: - LOG_ALWAYS_FATAL("%s(%d): Invalid mState in hasStarted(): %d", __func__, mId, mState); + LOG_ALWAYS_FATAL("%s(%d): Invalid mState in hasStarted(): %d", __func__, mPortId, mState); break; } @@ -3063,7 +3068,7 @@ bool AudioTrack::hasStarted() wait = (ts.mPosition == 0 || ts.mPosition == mStartTs.mPosition); } ALOGV("%s(%d): hasStarted wait:%d ts:%u start position:%lld", - __func__, mId, + __func__, mPortId, (int)wait, ts.mPosition, (long long)mStartTs.mPosition); @@ -3085,7 +3090,7 @@ bool AudioTrack::hasStarted() } } ALOGV("%s(%d): hasStarted wait:%d ets:%lld start position:%lld", - __func__, mId, + __func__, mPortId, (int)wait, (long long)ets.mPosition[location], (long long)mStartEts.mPosition[location]); @@ -3161,7 +3166,7 @@ bool AudioTrack::AudioTrackThread::threadLoop() FALLTHROUGH_INTENDED; default: LOG_ALWAYS_FATAL_IF(ns < 0, "%s(%d): processAudioBuffer() returned %lld", - __func__, mReceiver.mId, (long long)ns); + __func__, mReceiver.mPortId, (long long)ns); pauseInternal(ns); return true; } diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index c226557c5f..35a7e0583f 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -534,9 +534,15 @@ public: */ status_t getActiveMicrophones(std::vector* activeMicrophones); - /* - * Dumps the state of an audio record. - */ + /* Get the unique port ID assigned to this AudioRecord instance by audio policy manager. + * The ID is unique across all audioserver clients and can change during the life cycle + * of a given AudioRecord instance if the connection to audioserver is restored. + */ + audio_port_handle_t getPortId() const { return mPortId; }; + + /* + * Dumps the state of an audio record. + */ status_t dump(int fd, const Vector& args) const; private: @@ -654,7 +660,7 @@ private: audio_input_flags_t mOrigFlags; // as specified in constructor or set(), const audio_session_t mSessionId; - int mId; // Id from AudioFlinger + audio_port_handle_t mPortId; // Id from Audio Policy Manager transfer_type mTransfer; // Next 5 fields may be changed if IAudioRecord is re-created, but always != 0 diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 4b84fd172a..8238ea2671 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -912,7 +912,14 @@ public: AutoMutex lock(mLock); return mState == STATE_ACTIVE || mState == STATE_STOPPING; } -protected: + + /* Get the unique port ID assigned to this AudioTrack instance by audio policy manager. + * The ID is unique across all audioserver clients and can change during the life cycle + * of a given AudioTrack instance if the connection to audioserver is restored. + */ + audio_port_handle_t getPortId() const { return mPortId; }; + + protected: /* copying audio tracks is not allowed */ AudioTrack(const AudioTrack& other); AudioTrack& operator = (const AudioTrack& other); @@ -1166,7 +1173,7 @@ protected: audio_session_t mSessionId; int mAuxEffectId; - int mId; // Id from AudioFlinger. + audio_port_handle_t mPortId; // Id from Audio Policy Manager mutable Mutex mLock; diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index 52cc860bdb..a34b207cc6 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -146,7 +146,7 @@ public: afSampleRate = parcel->readInt64(); afLatencyMs = parcel->readInt32(); (void)parcel->read(&outputId, sizeof(audio_io_handle_t)); - (void)parcel->readInt32(&trackId); + (void)parcel->read(&portId, sizeof(audio_port_handle_t)); return NO_ERROR; } @@ -164,7 +164,7 @@ public: (void)parcel->writeInt64(afSampleRate); (void)parcel->writeInt32(afLatencyMs); (void)parcel->write(&outputId, sizeof(audio_io_handle_t)); - (void)parcel->writeInt32(trackId); + (void)parcel->write(&portId, sizeof(audio_port_handle_t)); return NO_ERROR; } @@ -181,7 +181,7 @@ public: uint32_t afSampleRate; uint32_t afLatencyMs; audio_io_handle_t outputId; - int32_t trackId; + audio_port_handle_t portId; }; /* CreateRecordInput contains all input arguments sent by AudioRecord to AudioFlinger @@ -274,7 +274,7 @@ public: return BAD_VALUE; } } - (void)parcel->readInt32(&trackId); + (void)parcel->read(&portId, sizeof(audio_port_handle_t)); return NO_ERROR; } @@ -301,7 +301,7 @@ public: } else { (void)parcel->writeInt32(0); } - (void)parcel->writeInt32(trackId); + (void)parcel->write(&portId, sizeof(audio_port_handle_t)); return NO_ERROR; } @@ -318,7 +318,7 @@ public: audio_io_handle_t inputId; sp cblk; sp buffers; - int32_t trackId; + audio_port_handle_t portId; }; // invariant on exit for all APIs that return an sp<>: diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 06975acc29..26f76c03a6 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -746,7 +746,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, output.afFrameCount = thread->frameCount(); output.afSampleRate = thread->sampleRate(); output.afLatencyMs = thread->latency(); - output.trackId = track == nullptr ? -1 : track->id(); + output.portId = portId; // move effect chain to this output thread if an effect on same session was waiting // for a track to be created @@ -1766,7 +1766,7 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu output.cblk = recordTrack->getCblk(); output.buffers = recordTrack->getBuffers(); - output.trackId = recordTrack->id(); + output.portId = portId; // return handle to client recordHandle = new RecordHandle(recordTrack); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index dd81c71026..9a7f1f1c94 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -502,7 +502,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) { - result.appendFormat("Type Id Active Client Session S Flags " + result.appendFormat("Type Id Active Client Session Port Id S Flags " " Format Chn mask SRate " "ST Usg CT " " G db L dB R dB VS dB " @@ -588,7 +588,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ ? 'r' /* buffer reduced */: bufferSizeInFrames > mFrameCount ? 'e' /* error */ : ' ' /* identical */; - result.appendFormat("%7s %6u %7u %2s 0x%03X " + result.appendFormat("%7s %6u %7u %7u %2s 0x%03X " "%08X %08X %6u " "%2u %3x %2x " "%5.2g %5.2g %5.2g %5.2g%c " @@ -596,6 +596,7 @@ void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool activ active ? "yes" : "no", (mClient == 0) ? getpid() : mClient->pid(), mSessionId, + mPortId, getTrackStateString(), mCblk->mFlags, @@ -1886,7 +1887,7 @@ void AudioFlinger::RecordThread::RecordTrack::invalidate() void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) { - result.appendFormat("Active Id Client Session S Flags " + result.appendFormat("Active Id Client Session Port Id S Flags " " Format Chn mask SRate Source " " Server FrmCnt FrmRdy Sil%s\n", isServerLatencySupported() ? " Latency" : ""); @@ -1894,7 +1895,7 @@ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool active) { - result.appendFormat("%c%5s %6d %6u %7u %2s 0x%03X " + result.appendFormat("%c%5s %6d %6u %7u %7u %2s 0x%03X " "%08X %08X %6u %6X " "%08X %6zu %6zu %3c", isFastTrack() ? 'F' : ' ', @@ -1902,6 +1903,7 @@ void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool a mId, (mClient == 0) ? getpid() : mClient->pid(), mSessionId, + mPortId, getTrackStateString(), mCblk->mFlags, @@ -2142,15 +2144,16 @@ void AudioFlinger::MmapThread::MmapTrack::onTimestamp(const ExtendedTimestamp &t void AudioFlinger::MmapThread::MmapTrack::appendDumpHeader(String8& result) { - result.appendFormat("Client Session Format Chn mask SRate Flags %s\n", + result.appendFormat("Client Session Port Id Format Chn mask SRate Flags %s\n", isOut() ? "Usg CT": "Source"); } void AudioFlinger::MmapThread::MmapTrack::appendDump(String8& result, bool active __unused) { - result.appendFormat("%6u %7u %08X %08X %6u 0x%03X ", + result.appendFormat("%6u %7u %7u %08X %08X %6u 0x%03X ", mPid, mSessionId, + mPortId, mFormat, mChannelMask, mSampleRate, -- GitLab From 4c1ef4b64d113be6ee1106375bd10cdc643e80d8 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 13 Nov 2018 16:46:26 -0800 Subject: [PATCH 0546/1530] audio policy: concurrent capture Implement concurrent capture in audio policy manager: - Attach AudioRecord client to already opened input when possible instead of systematically opening a new input for each client. - Always allow inputs to start even in case of concurrency. - Clients are selectively silenced based on their app state by audio policy service. - In case of concurrency on a given input stream, device and source is chosen based app states and source priority. Bug: 111438757 Test: Manual capture tests with solotester and Camera, Assistant and Duo Test: CTS tests for AudioRecord Change-Id: I302710ff545f67361d9aca89e81de40771ce7fb0 --- media/libaudioclient/AudioSystem.cpp | 4 +- media/libaudioclient/IAudioPolicyService.cpp | 9 +- .../include/media/AudioSystem.h | 3 +- .../include/media/IAudioPolicyService.h | 3 +- services/audioflinger/Threads.cpp | 23 +- services/audiopolicy/AudioPolicyInterface.h | 18 +- services/audiopolicy/common/include/policy.h | 25 -- .../include/AudioInputDescriptor.h | 5 +- .../include/ClientDescriptor.h | 11 +- .../managerdefinitions/include/IOProfile.h | 2 +- .../src/AudioInputDescriptor.cpp | 55 ++--- .../managerdefault/AudioPolicyManager.cpp | 216 ++++-------------- .../managerdefault/AudioPolicyManager.h | 10 +- .../service/AudioPolicyInterfaceImpl.cpp | 107 ++++----- .../service/AudioPolicyService.cpp | 117 +++++++++- .../audiopolicy/service/AudioPolicyService.h | 11 +- 16 files changed, 262 insertions(+), 357 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index ec36ed7dc8..efe65bb299 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -917,11 +917,11 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, config, flags, selectedDeviceId, portId); } -status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced) +status_t AudioSystem::startInput(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->startInput(portId, silenced); + return aps->startInput(portId); } status_t AudioSystem::stopInput(audio_port_handle_t portId) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index a406658a16..7f2e5e5805 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -329,16 +329,13 @@ public: return NO_ERROR; } - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced) + virtual status_t startInput(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(portId); - data.writeInt32(*silenced ? 1 : 0); remote()->transact(START_INPUT, data, &reply); status_t status = static_cast (reply.readInt32()); - *silenced = reply.readInt32() == 1; return status; } @@ -1219,10 +1216,8 @@ status_t BnAudioPolicyService::onTransact( case START_INPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_port_handle_t portId = static_cast (data.readInt32()); - bool silenced = data.readInt32() == 1; - status_t status = startInput(portId, &silenced); + status_t status = startInput(portId); reply->writeInt32(static_cast (status)); - reply->writeInt32(silenced ? 1 : 0); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 76a79c9536..ca1879fc4e 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -241,8 +241,7 @@ public: audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - static status_t startInput(audio_port_handle_t portId, - bool *silenced); + static status_t startInput(audio_port_handle_t portId); static status_t stopInput(audio_port_handle_t portId); static void releaseInput(audio_port_handle_t portId); static status_t initStreamVolume(audio_stream_type_t stream, diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index a246df68ea..e5fcfb5c2f 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -78,8 +78,7 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId) = 0; - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced) = 0; + virtual status_t startInput(audio_port_handle_t portId) = 0; virtual status_t stopInput(audio_port_handle_t portId) = 0; virtual void releaseInput(audio_port_handle_t portId) = 0; virtual status_t initStreamVolume(audio_stream_type_t stream, diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 45d3a065aa..e70e567099 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7367,8 +7367,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac status_t status = NO_ERROR; if (recordTrack->isExternalTrack()) { mLock.unlock(); - bool silenced; - status = AudioSystem::startInput(recordTrack->portId(), &silenced); + status = AudioSystem::startInput(recordTrack->portId()); mLock.lock(); if (recordTrack->isInvalid()) { recordTrack->clearSyncStartEvent(); @@ -7396,7 +7395,6 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac recordTrack->clearSyncStartEvent(); return status; } - recordTrack->setSilenced(silenced); } // Catch up with current buffer indices if thread is already running. // This is what makes a new client discard all buffered data. If the track's mRsmpInFront @@ -8346,11 +8344,10 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return BAD_VALUE; } - bool silenced = false; if (isOutput()) { ret = AudioSystem::startOutput(portId); } else { - ret = AudioSystem::startInput(portId, &silenced); + ret = AudioSystem::startInput(portId); } Mutex::Autolock _l(mLock); @@ -8371,21 +8368,21 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return PERMISSION_DENIED; } + // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? + sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, + isOutput(), client.clientUid, client.clientPid, portId); + if (isOutput()) { // force volume update when a new track is added mHalVolFloat = -1.0f; - } else if (!silenced) { - for (const sp &track : mActiveTracks) { - if (track->isSilenced_l() && track->uid() != client.clientUid) - track->invalidate(); + } else if (!track->isSilenced_l()) { + for (const sp &t : mActiveTracks) { + if (t->isSilenced_l() && t->uid() != client.clientUid) + t->invalidate(); } } - // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? - sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, - isOutput(), client.clientUid, client.clientPid, portId); - track->setSilenced_l(silenced); mActiveTracks.add(track); sp chain = getEffectChain_l(mSessionId); if (chain != 0) { diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 3c3a82b38f..3fb505d7b2 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -65,20 +65,6 @@ public: API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path } input_type_t; - enum { - API_INPUT_CONCURRENCY_NONE = 0, - API_INPUT_CONCURRENCY_CALL = (1 << 0), // Concurrency with a call - API_INPUT_CONCURRENCY_CAPTURE = (1 << 1), // Concurrency with another capture - API_INPUT_CONCURRENCY_HOTWORD = (1 << 2), // Concurrency with a hotword - API_INPUT_CONCURRENCY_PREEMPT = (1 << 3), // pre-empted someone - // NB: preempt is marked on a successful return, others are on failing calls - API_INPUT_CONCURRENCY_LAST = (1 << 4), - - API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_LAST - 1), - }; - - typedef uint32_t concurrency_type__mask_t; - public: virtual ~AudioPolicyInterface() {} // @@ -141,9 +127,7 @@ public: input_type_t *inputType, audio_port_handle_t *portId) = 0; // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency) = 0; + virtual status_t startInput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId) = 0; // releases the input. diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 9bd68e1ffa..30b0044440 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -31,14 +31,6 @@ static const audio_format_t gDynamicFormat = AUDIO_FORMAT_DEFAULT; // Do not limit channel count otherwise #define MAX_MIXER_CHANNEL_COUNT FCC_8 -/** - * A device mask for all audio input devices that are considered "virtual" when evaluating - * active inputs in getActiveInputs() - */ -#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX|\ - AUDIO_DEVICE_IN_BUS|AUDIO_DEVICE_IN_FM_TUNER) - - /** * A device mask for all audio input and output devices where matching inputs/outputs on device * type alone is not enough: the address must match too @@ -67,23 +59,6 @@ static inline bool is_state_in_call(int state) return (state == AUDIO_MODE_IN_CALL) || (state == AUDIO_MODE_IN_COMMUNICATION); } -/** - * Check if the input device given is considered as a virtual device. - * - * @param[in] device to consider - * - * @return true if the device is a virtual one, false otherwise. - */ -static inline bool is_virtual_input_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) - return true; - } - return false; -} - /** * Check whether the device type is one * where addresses are used to distinguish between one connected device and another diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 6e4c044bb5..9f8b8c0580 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -58,9 +58,8 @@ public: void clearPreemptedSessions(); bool isActive() const { return mGlobalActiveCount > 0; } bool isSourceActive(audio_source_t source) const; - audio_source_t inputSource(bool activeOnly = false) const; + audio_source_t source() const; bool isSoundTrigger() const; - audio_source_t getHighestPrioritySource(bool activeOnly) const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } @@ -121,7 +120,7 @@ public: * Only considers inputs from physical devices (e.g. main mic, headset mic) when * ignoreVirtualInputs is true. */ - Vector > getActiveInputs(bool ignoreVirtualInputs = true); + Vector > getActiveInputs(); audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 030bf4bd31..986d109122 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -106,7 +107,7 @@ public: audio_port_handle_t preferredDeviceId, audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {} + mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mAppState(APP_STATE_IDLE) {} ~RecordClientDescriptor() override = default; using ClientDescriptor::dump; @@ -115,14 +116,16 @@ public: audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } bool isSoundTrigger() const { return mIsSoundTrigger; } - void setSilenced(bool silenced) { mSilenced = silenced; } - bool isSilenced() const { return mSilenced; } + void setAppState(app_state_t appState) { mAppState = appState; } + app_state_t appState() { return mAppState; } + bool isSilenced() const { return mAppState == APP_STATE_IDLE; } private: const audio_source_t mSource; const audio_input_flags_t mFlags; const bool mIsSoundTrigger; - bool mSilenced; + app_state_t mAppState; + }; class SourceClientDescriptor: public TrackClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index eb329597da..8ff823860f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -35,7 +35,7 @@ class IOProfile : public AudioPort public: IOProfile(const String8 &name, audio_port_role_t role) : AudioPort(name, AUDIO_PORT_TYPE_MIX, role), - maxOpenCount((role == AUDIO_PORT_ROLE_SOURCE) ? 1 : 0), + maxOpenCount(1), curOpenCount(0), maxActiveCount(1), curActiveCount(0) {} diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 1f29874b5c..559274f8c3 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -53,9 +53,32 @@ audio_port_handle_t AudioInputDescriptor::getId() const return mId; } -audio_source_t AudioInputDescriptor::inputSource(bool activeOnly) const +audio_source_t AudioInputDescriptor::source() const { - return getHighestPrioritySource(activeOnly); + audio_source_t source = AUDIO_SOURCE_DEFAULT; + + for (bool activeOnly : { true, false }) { + int32_t topPriority = -1; + app_state_t topState = APP_STATE_IDLE; + for (const auto &client : getClientIterable()) { + if (activeOnly && !client->active()) { + continue; + } + app_state_t curState = client->appState(); + if (curState >= topState) { + int32_t curPriority = source_priority(client->source()); + if (curPriority > topPriority) { + source = client->source(); + topPriority = curPriority; + } + topState = curState; + } + } + if (source != AUDIO_SOURCE_DEFAULT) { + break; + } + } + return source; } void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, @@ -76,7 +99,7 @@ void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig dstConfig->type = AUDIO_PORT_TYPE_MIX; dstConfig->ext.mix.hw_module = getModuleHandle(); dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.source = inputSource(); + dstConfig->ext.mix.usecase.source = source(); } void AudioInputDescriptor::toAudioPort(struct audio_port *port) const @@ -125,24 +148,6 @@ bool AudioInputDescriptor::isSourceActive(audio_source_t source) const return false; } -audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const -{ - audio_source_t source = AUDIO_SOURCE_DEFAULT; - int32_t priority = -1; - - for (const auto &client : getClientIterable()) { - if (activeOnly && !client->active() ) { - continue; - } - int32_t curPriority = source_priority(client->source()); - if (curPriority > priority) { - priority = curPriority; - source = client->source(); - } - } - return source; -} - bool AudioInputDescriptor::isSoundTrigger() const { // sound trigger and non sound trigger clients are not mixed on a given input // so check only first client @@ -224,7 +229,7 @@ status_t AudioInputDescriptor::open(const audio_config_t *config, status_t AudioInputDescriptor::start() { - if (mGlobalActiveCount == 1) { + if (!isActive()) { if (!mProfile->canStartNewIo()) { ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount); return INVALID_OPERATION; @@ -388,15 +393,13 @@ uint32_t AudioInputCollection::activeInputsCountOnDevices(audio_devices_t device return count; } -Vector > AudioInputCollection::getActiveInputs(bool ignoreVirtualInputs) +Vector > AudioInputCollection::getActiveInputs() { Vector > activeInputs; for (size_t i = 0; i < size(); i++) { const sp inputDescriptor = valueAt(i); - if ((inputDescriptor->isActive()) - && (!ignoreVirtualInputs || - !is_virtual_input_device(inputDescriptor->mDevice))) { + if (inputDescriptor->isActive()) { activeInputs.add(inputDescriptor); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6ec6a767dd..1b088bb477 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1876,7 +1876,38 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, } if (!profile->canOpenNewIo()) { - return AUDIO_IO_HANDLE_NONE; + for (size_t i = 0; i < mInputs.size(); ) { + sp desc = mInputs.valueAt(i); + if (desc->mProfile != profile) { + continue; + } + // if sound trigger, reuse input if used by other sound trigger on same session + // else + // reuse input if active client app is not in IDLE state + // + RecordClientVector clients = desc->clientsList(); + bool doClose = false; + for (const auto& client : clients) { + if (isSoundTrigger != client->isSoundTrigger()) { + continue; + } + if (client->isSoundTrigger()) { + if (session == client->session()) { + return desc->mIoHandle; + } + continue; + } + if (client->active() && client->appState() != APP_STATE_IDLE) { + return desc->mIoHandle; + } + doClose = true; + } + if (doClose) { + closeInput(desc->mIoHandle); + } else { + i++; + } + } } sp inputDesc = new AudioInputDescriptor(profile, mpClientInterface); @@ -1917,55 +1948,8 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, return input; } -//static -bool AudioPolicyManager::isConcurrentSource(audio_source_t source) -{ - return (source == AUDIO_SOURCE_HOTWORD) || - (source == AUDIO_SOURCE_VOICE_RECOGNITION) || - (source == AUDIO_SOURCE_FM_TUNER); -} - -// FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537. -bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { - if (!mHasComputedSoundTriggerSupportsConcurrentCapture) { - bool soundTriggerSupportsConcurrentCapture = false; - unsigned int numModules = 0; - struct sound_trigger_module_descriptor* nModules = NULL; - - status_t status = SoundTrigger::listModules(nModules, &numModules); - if (status == NO_ERROR && numModules != 0) { - nModules = (struct sound_trigger_module_descriptor*) calloc( - numModules, sizeof(struct sound_trigger_module_descriptor)); - if (nModules == NULL) { - // We failed to malloc the buffer, so just say no for now, and hope that we have more - // ram the next time this function is called. - ALOGE("Failed to allocate buffer for module descriptors"); - return false; - } - - status = SoundTrigger::listModules(nModules, &numModules); - if (status == NO_ERROR) { - soundTriggerSupportsConcurrentCapture = true; - for (size_t i = 0; i < numModules; ++i) { - soundTriggerSupportsConcurrentCapture &= - nModules[i].properties.concurrent_capture; - } - } - free(nModules); - } - mSoundTriggerSupportsConcurrentCapture = soundTriggerSupportsConcurrentCapture; - mHasComputedSoundTriggerSupportsConcurrentCapture = true; - } - return mSoundTriggerSupportsConcurrentCapture; -} - - -status_t AudioPolicyManager::startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency) +status_t AudioPolicyManager::startInput(audio_port_handle_t portId) { - *concurrency = API_INPUT_CONCURRENCY_NONE; - ALOGV("%s portId %d", __FUNCTION__, portId); sp inputDesc = mInputs.getInputForClient(portId); @@ -1982,106 +1966,16 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, audio_session_t session = client->session(); - ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)", - __FUNCTION__, input, session, silenced, *concurrency); + ALOGV("%s input:%d, session:%d)", __FUNCTION__, input, session); - if (!is_virtual_input_device(inputDesc->mDevice)) { - if (mCallTxPatch != 0 && - inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) { - ALOGW("startInput(%d) failed: call in progress", input); - *concurrency |= API_INPUT_CONCURRENCY_CALL; - return INVALID_OPERATION; - } - - Vector> activeInputs = mInputs.getActiveInputs(); - - // If a UID is idle and records silence and another not silenced recording starts - // from another UID (idle or active) we stop the current idle UID recording in - // favor of the new one - "There can be only one" TM - if (!silenced) { - for (const auto& activeDesc : activeInputs) { - if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && - activeDesc->getId() == inputDesc->getId()) { - continue; - } - - RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/); - for (const auto& activeClient : activeClients) { - if (activeClient->isSilenced()) { - closeClient(activeClient->portId()); - ALOGV("%s client %d stopping silenced client %d", __FUNCTION__, - portId, activeClient->portId()); - activeInputs = mInputs.getActiveInputs(); - } - } - } - } + Vector> activeInputs = mInputs.getActiveInputs(); - for (const auto& activeDesc : activeInputs) { - if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && - activeDesc->getId() == inputDesc->getId()) { - continue; - } - - audio_source_t activeSource = activeDesc->inputSource(true); - if (client->source() == AUDIO_SOURCE_HOTWORD) { - if (activeSource == AUDIO_SOURCE_HOTWORD) { - if (activeDesc->hasPreemptedSession(session)) { - ALOGW("%s input %d failed for HOTWORD: " - "other input %d already started for HOTWORD", __FUNCTION__, - input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_HOTWORD; - return INVALID_OPERATION; - } - } else { - ALOGV("%s input %d failed for HOTWORD: other input %d already started", - __FUNCTION__, input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; - return INVALID_OPERATION; - } - } else { - if (activeSource != AUDIO_SOURCE_HOTWORD) { - ALOGW("%s input %d failed: other input %d already started", __FUNCTION__, - input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; - return INVALID_OPERATION; - } - } - } - - // We only need to check if the sound trigger session supports concurrent capture if the - // input is also a sound trigger input. Otherwise, we should preempt any hotword stream - // that's running. - const bool allowConcurrentWithSoundTrigger = - inputDesc->isSoundTrigger() ? soundTriggerSupportsConcurrentCapture() : false; - - // if capture is allowed, preempt currently active HOTWORD captures - for (const auto& activeDesc : activeInputs) { - if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) { - continue; - } - RecordClientVector activeHotwordClients = - activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD); - if (activeHotwordClients.size() > 0) { - SortedVector sessions = activeDesc->getPreemptedSessions(); - - for (const auto& activeClient : activeHotwordClients) { - *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; - sessions.add(activeClient->session()); - closeClient(activeClient->portId()); - ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__, - input, activeDesc->mIoHandle); - } - - inputDesc->setPreemptedSessions(sessions); - } - } + status_t status = inputDesc->start(); + if (status != NO_ERROR) { + return status; } - // Make sure we start with the correct silence state - client->setSilenced(silenced); - - // increment activity count before calling getNewInputDevice() below as only active sessions + // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection inputDesc->setClientActive(client, true); @@ -2090,12 +1984,6 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, audio_devices_t device = getNewInputDevice(inputDesc); setInputDevice(input, device, true /* force */); - status_t status = inputDesc->start(); - if (status != NO_ERROR) { - inputDesc->setClientActive(client, false); - return status; - } - if (inputDesc->activeCount() == 1) { // if input maps to a dynamic policy with an activity listener, notify of state change if ((inputDesc->mPolicyMix != NULL) @@ -3344,7 +3232,7 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) SortedVector inputsToClose; for (size_t i = 0; i < mInputs.size(); i++) { sp inputDesc = mInputs.valueAt(i); - if (affectedSources.indexOf(inputDesc->inputSource()) >= 0) { + if (affectedSources.indexOf(inputDesc->source()) >= 0) { inputsToClose.add(inputDesc->mIoHandle); } } @@ -3721,16 +3609,15 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { Vector > activeInputs = mInputs.getActiveInputs(); - bool silenced = state == APP_STATE_IDLE; - ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced); + ALOGV("%s(uid:%d, state:%d)", __func__, uid, state); for (size_t i = 0; i < activeInputs.size(); i++) { sp activeDesc = activeInputs[i]; RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); for (const auto& client : clients) { if (uid == client->uid()) { - client->setSilenced(silenced); + client->setAppState(state); } } } @@ -3840,8 +3727,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mBeaconMuted(false), mTtsOutputAvailable(false), mMasterMono(false), - mMusicEffectOutput(AUDIO_IO_HANDLE_NONE), - mHasComputedSoundTriggerSupportsConcurrentCapture(false) + mMusicEffectOutput(AUDIO_IO_HANDLE_NONE) { } @@ -4894,7 +4780,7 @@ audio_devices_t AudioPolicyManager::getNewInputDevice(const spgetHighestPrioritySource(true /*activeOnly*/); + audio_source_t source = inputDesc->source(); if (source == AUDIO_SOURCE_DEFAULT && isInCall()) { source = AUDIO_SOURCE_VOICE_COMMUNICATION; } @@ -5233,20 +5119,6 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou } installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } - - // inform all input as well - for (size_t i = 0; i < mInputs.size(); i++) { - const sp inputDescriptor = mInputs.valueAt(i); - if (!is_virtual_input_device(inputDescriptor->mDevice)) { - AudioParameter inputCmd = AudioParameter(); - ALOGV("%s: inform input %d of device:%d", __func__, - inputDescriptor->mIoHandle, device); - inputCmd.addInt(String8(AudioParameter::keyRouting),device); - mpClientInterface->setParameters(inputDescriptor->mIoHandle, - inputCmd.toString(), - delayMs); - } - } } // update stream volumes according to new device diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 0436b1d0f8..22bfab0915 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -134,9 +134,7 @@ public: audio_port_handle_t *portId); // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency); + virtual status_t startInput(audio_port_handle_t portId); // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId); @@ -539,8 +537,6 @@ protected: void clearAudioSources(uid_t uid); - static bool isConcurrentSource(audio_source_t source); - static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); @@ -704,10 +700,6 @@ private: int delayMs, uid_t uid, sp *patchDescPtr); - - bool soundTriggerSupportsConcurrentCapture(); - bool mSoundTriggerSupportsConcurrentCapture; - bool mHasComputedSoundTriggerSupportsConcurrentCapture; }; }; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 59c8f10075..c2ce754616 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -454,23 +454,6 @@ static std::string audioSourceString(audio_source_t value) { return rawbuffer; } -static std::string audioConcurrencyString( - AudioPolicyInterface::concurrency_type__mask_t concurrency) -{ - char buffer[64]; // oversized - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL) { - snprintf(buffer, sizeof(buffer), "%s%s%s%s", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL)? ",call":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE)? ",capture":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_HOTWORD)? ",hotword":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_PREEMPT)? ",preempt":""); - } else { - snprintf(buffer, sizeof(buffer), ",none"); - } - - return &buffer[1]; -} - std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t portId) { std::string typeStr; struct audio_port port = {}; @@ -482,7 +465,7 @@ std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t po return typeStr; } -status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced) +status_t AudioPolicyService::startInput(audio_port_handle_t portId) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -505,17 +488,16 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc return PERMISSION_DENIED; } - // If UID inactive it records silence until becoming active - *silenced = !mUidPolicy->isUidActive(client->uid) && !client->isVirtualDevice; - Mutex::Autolock _l(mLock); - AudioPolicyInterface::concurrency_type__mask_t concurrency = - AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE; + + client->active = true; + client->startTimeNs = systemTime(); + updateUidStates_l(); status_t status; { AutoCallerClear acc; - status = mAudioPolicyManager->startInput(portId, *silenced, &concurrency); + status = mAudioPolicyManager->startInput(portId); } @@ -524,7 +506,6 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc static constexpr char kAudioPolicy[] = "audiopolicy"; - static constexpr char kAudioPolicyReason[] = "android.media.audiopolicy.reason"; static constexpr char kAudioPolicyStatus[] = "android.media.audiopolicy.status"; static constexpr char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src"; static constexpr char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg"; @@ -541,7 +522,6 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy); if (item != NULL) { - item->setCString(kAudioPolicyReason, audioConcurrencyString(concurrency).c_str()); item->setInt32(kAudioPolicyStatus, status); item->setCString(kAudioPolicyRqstSrc, @@ -556,54 +536,35 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc item->setCString( kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str()); - // figure out who is active - // NB: might the other party have given up the microphone since then? how sure. - // perhaps could have given up on it. - // we hold mLock, so perhaps we're safe for this looping - if (concurrency != AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE) { - int count = mAudioRecordClients.size(); - for (int i = 0; i other = mAudioRecordClients.valueAt(i); - if (other->active) { - // keeps the last of the clients marked active - item->setCString(kAudioPolicyActiveSrc, - audioSourceString(other->attributes.source).c_str()); - item->setInt32(kAudioPolicyActiveSession, other->session); - if (other->opPackageName.size() != 0) { - item->setCString(kAudioPolicyActivePkg, - std::string(String8(other->opPackageName).string()).c_str()); - } else { - item->setCString(kAudioPolicyRqstPkg, - std::to_string(other->uid).c_str()); - } - item->setCString(kAudioPolicyActiveDevice, - getDeviceTypeStrForPortId(other->deviceId).c_str()); + int count = mAudioRecordClients.size(); + for (int i = 0; i < count ; i++) { + if (portId == mAudioRecordClients.keyAt(i)) { + continue; + } + sp other = mAudioRecordClients.valueAt(i); + if (other->active) { + // keeps the last of the clients marked active + item->setCString(kAudioPolicyActiveSrc, + audioSourceString(other->attributes.source).c_str()); + item->setInt32(kAudioPolicyActiveSession, other->session); + if (other->opPackageName.size() != 0) { + item->setCString(kAudioPolicyActivePkg, + std::string(String8(other->opPackageName).string()).c_str()); + } else { + item->setCString(kAudioPolicyRqstPkg, + std::to_string(other->uid).c_str()); } + item->setCString(kAudioPolicyActiveDevice, + getDeviceTypeStrForPortId(other->deviceId).c_str()); } } item->selfrecord(); delete item; item = NULL; } - } - - if (status == NO_ERROR) { - LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL, - "startInput(): invalid concurrency type %d", (int)concurrency); - - // enforce permission (if any) required for each type of concurrency - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL) { - //TODO: check incall capture permission - } - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE) { - //TODO: check concurrent capture permission - } - - client->active = true; - } else { + client->active = false; + client->startTimeNs = 0; + updateUidStates_l(); finishRecording(client->opPackageName, client->uid); } @@ -615,6 +576,7 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) if (mAudioPolicyManager == NULL) { return NO_INIT; } + Mutex::Autolock _l(mLock); ssize_t index = mAudioRecordClients.indexOfKey(portId); @@ -624,6 +586,9 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) sp client = mAudioRecordClients.valueAt(index); client->active = false; + client->startTimeNs = 0; + + updateUidStates_l(); // finish the recording app op finishRecording(client->opPackageName, client->uid); @@ -646,6 +611,14 @@ void AudioPolicyService::releaseInput(audio_port_handle_t portId) return; } client = mAudioRecordClients.valueAt(index); + + if (client->active) { + ALOGW("%s releasing active client portId %d", __FUNCTION__, portId); + client->active = false; + client->startTimeNs = 0; + updateUidStates_l(); + } + mAudioRecordClients.removeItem(portId); } if (client == 0) { diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 78dbf5f16b..2893872346 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -348,11 +348,91 @@ void AudioPolicyService::updateUidStates() void AudioPolicyService::updateUidStates_l() { - //TODO: implement real concurrent capture policy: for now just apply each app state directly +// Go over all active clients and allow capture (does not force silence) in the +// following cases: +// - The client is the assistant AND +// an accessibility service is on TOP AND the source is VOICE_RECOGNITION or HOTWORD +// OR +// is on TOP AND uses VOICE_RECOGNITION +// OR uses HOTWORD AND there is no privacy sensitive active capture +// - The client is an accessibility service AND +// is on TOP AND the source is VOICE_RECOGNITION or HOTWORD +// - Any other client AND +// The assistant is not on TOP AND +// is on TOP OR latest started AND +// there is no privacy sensitive active capture +//TODO: mamanage pre processing effects according to use case priority + + sp topActive; + sp latestActive; + nsecs_t latestStartNs = 0; + sp latestSensitiveActive; + nsecs_t latestSensitiveStartNs = 0; + bool isA11yOnTop = mUidPolicy->isA11yOnTop(); + bool isAssistantOnTop = false; + bool isSensitiveActive = false; + for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; - setAppState_l(current->uid, apmStatFromAmState(mUidPolicy->getUidState(current->uid))); + if (isPrivacySensitive(current->attributes.source)) { + if (current->startTimeNs > latestSensitiveStartNs) { + latestSensitiveActive = current; + latestSensitiveStartNs = current->startTimeNs; + } + isSensitiveActive = true; + } + if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) { + topActive = current; + latestActive = nullptr; + if (mUidPolicy->isAssistantUid(current->uid)) { + isAssistantOnTop = true; + } + } + if (current->startTimeNs > latestStartNs) { + latestActive = current; + latestStartNs = current->startTimeNs; + } + } + + if (topActive == nullptr && latestActive == nullptr) { + return; + } + + for (size_t i =0; i < mAudioRecordClients.size(); i++) { + sp current = mAudioRecordClients[i]; + if (!current->active) continue; + + audio_source_t source = current->attributes.source; + bool isOnTop = mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP; + bool isLatest = current == latestActive; + bool isLatestSensitive = current == latestSensitiveActive; + bool forceIdle = true; + if (mUidPolicy->isAssistantUid(current->uid)) { + if (isA11yOnTop) { + if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { + forceIdle = false; + } + } else { + if (((isOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) || + source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) { + forceIdle = false; + } + } + } else if (mUidPolicy->isA11yUid(current->uid)) { + if (isOnTop && + (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { + forceIdle = false; + } + } else { + if (!isAssistantOnTop && (isOnTop || (topActive == nullptr && isLatest)) && + (!isSensitiveActive || isLatestSensitive)) { + forceIdle = false; + } + } + setAppState_l(current->uid, + forceIdle ? APP_STATE_IDLE : + apmStatFromAmState(mUidPolicy->getUidState(current->uid))); } } @@ -369,6 +449,22 @@ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { return APP_STATE_FOREGROUND; } +/* static */ +bool AudioPolicyService::isPrivacySensitive(audio_source_t source) +{ + switch (source) { + case AUDIO_SOURCE_VOICE_UPLINK: + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + case AUDIO_SOURCE_CAMCORDER: + case AUDIO_SOURCE_VOICE_COMMUNICATION: + return true; + default: + break; + } + return false; +} + void AudioPolicyService::setAppState_l(uid_t uid, app_state_t state) { AutoCallerClear acc; @@ -548,6 +644,7 @@ void AudioPolicyService::UidPolicy::registerSelf() { mObserverRegistered = true; } else { ALOGE("UidPolicy::registerSelf linkToDeath failed: %d", res); + am.unregisterUidObserver(this); } } @@ -650,6 +747,7 @@ int AudioPolicyService::UidPolicy::getUidState(uid_t uid) { mCachedUids.insert(std::pair>(uid, std::pair(active, state))); } + return state; } @@ -730,6 +828,21 @@ void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid.first); + if (it == mA11yUids.end()) { + continue; + } + if (uid.second.second == ActivityManager::PROCESS_STATE_TOP || + uid.second.second == ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE || + uid.second.second == ActivityManager::PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + return true; + } + } + return false; +} + bool AudioPolicyService::UidPolicy::isA11yUid(uid_t uid) { std::vector::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 4d7235fce8..dc5a36d34c 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -94,8 +94,7 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId = NULL, audio_port_handle_t *portId = NULL); - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced); + virtual status_t startInput(audio_port_handle_t portId); virtual status_t stopInput(audio_port_handle_t portId); virtual void releaseInput(audio_port_handle_t portId); virtual status_t initStreamVolume(audio_stream_type_t stream, @@ -276,6 +275,8 @@ private: void updateUidStates(); void updateUidStates_l(); + static bool isPrivacySensitive(audio_source_t source); + // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID // transitions to an active state we will start reporting buffers with data. This approach @@ -299,6 +300,7 @@ private: bool isAssistantUid(uid_t uid) { return uid == mAssistantUid; } void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } bool isA11yUid(uid_t uid); + bool isA11yOnTop(); // BnUidObserver implementation void onUidActive(uid_t uid) override; @@ -650,12 +652,11 @@ private: const audio_session_t session, const audio_port_handle_t deviceId, const String16& opPackageName) : AudioClient(attributes, io, uid, pid, session, deviceId), - opPackageName(opPackageName), isConcurrent(false), isVirtualDevice(false) {} + opPackageName(opPackageName), startTimeNs(0) {} ~AudioRecordClient() override = default; const String16 opPackageName; // client package name - bool isConcurrent; // is allowed to concurrent capture - bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr() + nsecs_t startTimeNs; }; // --- AudioPlaybackClient --- -- GitLab From 9e29ea8bd0be596c65d93ae196afac8339e00bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 8 Nov 2018 14:28:10 +0100 Subject: [PATCH 0547/1530] Fix build issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: I5563ff049ddc384556976f577a6d0ebc020516c8 Signed-off-by: François Gaffie --- services/audiopolicy/tests/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index 513312eee3..2ccb542eb9 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -41,6 +41,9 @@ LOCAL_SHARED_LIBRARIES := \ libmedia_helper \ libutils +LOCAL_HEADER_LIBRARIES := \ + libmedia_headers + LOCAL_SRC_FILES := \ systemaudio_tests.cpp \ -- GitLab From 3c3728de60aba0d98c7fa3576a17b1a79582dd33 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 4 Dec 2018 17:06:36 -0800 Subject: [PATCH 0548/1530] mediaplayer2: per-source DRM prepare/release iface Bug: 119675660 Test: MediaPlayer2DrmTest Change-Id: I583be4de45cb7de4e1638fb3442511dbabc661b3 --- .../include/mediaplayer2/MediaPlayer2Interface.h | 4 ++-- .../libmediaplayer2/include/mediaplayer2/mediaplayer2.h | 6 ++++-- media/libmediaplayer2/mediaplayer2.cpp | 9 +++++---- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 7 +++++-- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 4 ++-- media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp | 9 +++++---- media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h | 5 +++-- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index 4b19e38ef1..5e9858960d 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -260,11 +260,11 @@ public: virtual void onMessageReceived(const sp & /* msg */) override { } // Modular DRM - virtual status_t prepareDrm(const uint8_t /* uuid */[16], + virtual status_t prepareDrm(int64_t /*srcId*/, const uint8_t /* uuid */[16], const Vector& /* drmSessionId */) { return INVALID_OPERATION; } - virtual status_t releaseDrm() { + virtual status_t releaseDrm(int64_t /*srcId*/) { return INVALID_OPERATION; } diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 4bc1a4a7ad..c7cd7d2d61 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -105,8 +105,10 @@ public: status_t getParameter(int key, Parcel* reply); // Modular DRM - status_t prepareDrm(const uint8_t uuid[16], const Vector& drmSessionId); - status_t releaseDrm(); + status_t prepareDrm(int64_t srcId, + const uint8_t uuid[16], + const Vector& drmSessionId); + status_t releaseDrm(int64_t srcId); // AudioRouting status_t setPreferredDevice(jobject device); jobject getRoutedDevice(); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 921a5b760d..2ae5a8ca30 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -1094,7 +1094,8 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play } // Modular DRM -status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector& drmSessionId) { +status_t MediaPlayer2::prepareDrm( + int64_t srcId, const uint8_t uuid[16], const Vector& drmSessionId) { // TODO change to ALOGV ALOGD("prepareDrm: uuid: %p drmSessionId: %p(%zu)", uuid, drmSessionId.array(), drmSessionId.size()); @@ -1118,7 +1119,7 @@ status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector& } // Passing down to mediaserver mainly for creating the crypto - status_t status = mPlayer->prepareDrm(uuid, drmSessionId); + status_t status = mPlayer->prepareDrm(srcId, uuid, drmSessionId); ALOGE_IF(status != OK, "prepareDrm: Failed at mediaserver with ret: %d", status); // TODO change to ALOGV @@ -1127,7 +1128,7 @@ status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector& return status; } -status_t MediaPlayer2::releaseDrm() { +status_t MediaPlayer2::releaseDrm(int64_t srcId) { Mutex::Autolock _l(mLock); if (mPlayer == NULL) { return NO_INIT; @@ -1142,7 +1143,7 @@ status_t MediaPlayer2::releaseDrm() { return INVALID_OPERATION; } - status_t status = mPlayer->releaseDrm(); + status_t status = mPlayer->releaseDrm(srcId); // TODO change to ALOGV ALOGD("releaseDrm: mediaserver::releaseDrm ret: %d", status); if (status != OK) { diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 6c4f0db180..d4ffdfeb4f 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -3046,7 +3046,8 @@ const char *NuPlayer2::getDataSourceType() { } // Modular DRM begin -status_t NuPlayer2::prepareDrm(const uint8_t uuid[16], const Vector &drmSessionId) +status_t NuPlayer2::prepareDrm( + int64_t srcId, const uint8_t uuid[16], const Vector &drmSessionId) { ALOGV("prepareDrm "); @@ -3056,6 +3057,7 @@ status_t NuPlayer2::prepareDrm(const uint8_t uuid[16], const Vector &dr uint8_t UUID[16]; memcpy(UUID, uuid, sizeof(UUID)); Vector sessionId = drmSessionId; + msg->setInt64("srcId", srcId); msg->setPointer("uuid", (void*)UUID); msg->setPointer("drmSessionId", (void*)&sessionId); @@ -3072,11 +3074,12 @@ status_t NuPlayer2::prepareDrm(const uint8_t uuid[16], const Vector &dr return status; } -status_t NuPlayer2::releaseDrm() +status_t NuPlayer2::releaseDrm(int64_t srcId) { ALOGV("releaseDrm "); sp msg = new AMessage(kWhatReleaseDrm, this); + msg->setInt64("srcId", srcId); sp response; status_t status = msg->postAndAwaitResponse(&response); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index 3ecdb01239..93f9f007f2 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -92,8 +92,8 @@ struct NuPlayer2 : public AHandler { float getFrameRate(); // Modular DRM - status_t prepareDrm(const uint8_t uuid[16], const Vector &drmSessionId); - status_t releaseDrm(); + status_t prepareDrm(int64_t srcId, const uint8_t uuid[16], const Vector &drmSessionId); + status_t releaseDrm(int64_t srcId); const char *getDataSourceType(); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 821dc9f22d..eff8866bb8 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -976,24 +976,25 @@ void NuPlayer2Driver::notifyFlagsChanged(int64_t /* srcId */, uint32_t flags) { } // Modular DRM -status_t NuPlayer2Driver::prepareDrm(const uint8_t uuid[16], const Vector &drmSessionId) +status_t NuPlayer2Driver::prepareDrm( + int64_t srcId, const uint8_t uuid[16], const Vector &drmSessionId) { ALOGV("prepareDrm(%p) state: %d", this, mState); // leaving the state verification for mediaplayer.cpp - status_t ret = mPlayer->prepareDrm(uuid, drmSessionId); + status_t ret = mPlayer->prepareDrm(srcId, uuid, drmSessionId); ALOGV("prepareDrm ret: %d", ret); return ret; } -status_t NuPlayer2Driver::releaseDrm() +status_t NuPlayer2Driver::releaseDrm(int64_t srcId) { ALOGV("releaseDrm(%p) state: %d", this, mState); // leaving the state verification for mediaplayer.cpp - status_t ret = mPlayer->releaseDrm(); + status_t ret = mPlayer->releaseDrm(srcId); ALOGV("releaseDrm ret: %d", ret); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 50ee1734c6..bb30c76839 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -82,8 +82,9 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { void notifyFlagsChanged(int64_t srcId, uint32_t flags); // Modular DRM - virtual status_t prepareDrm(const uint8_t uuid[16], const Vector &drmSessionId); - virtual status_t releaseDrm(); + virtual status_t prepareDrm( + int64_t srcId, const uint8_t uuid[16], const Vector &drmSessionId); + virtual status_t releaseDrm(int64_t srcId); protected: virtual ~NuPlayer2Driver(); -- GitLab From c357121a87e3f1e0a81fdbe22311ccb8720acfa9 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 5 Dec 2018 09:16:41 -0800 Subject: [PATCH 0549/1530] Add missing format key conversion Bug: 111407253 Test: build Change-Id: Ib1ef6682ee39adea5706244e5cab8ae58e8c2158 --- media/libstagefright/Utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index ec043f264f..df929ae077 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -598,6 +598,7 @@ static std::vector> int64Mappings { { { "exif-offset", kKeyExifOffset }, { "exif-size", kKeyExifSize }, + { "thumbnail-time", kKeyThumbnailTime }, } }; -- GitLab From e6faa59bd4806a2200f59304ecba080569d0dc0d Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 3 Dec 2018 10:10:21 -0800 Subject: [PATCH 0550/1530] aaudio: improve test_atomic_fifo Check for the effects of setting a bogus counter. Check for writes to memory surrounding the FIFO. Check for negative array indices. Bug: 120147915 Test: this is a test Change-Id: Ia30cdef7f9d60f0a98b9af964cb3b3159df37dc9 --- media/libaaudio/tests/test_atomic_fifo.cpp | 219 ++++++++++++++++++--- 1 file changed, 189 insertions(+), 30 deletions(-) diff --git a/media/libaaudio/tests/test_atomic_fifo.cpp b/media/libaaudio/tests/test_atomic_fifo.cpp index a09b74c3ec..130ef43fff 100644 --- a/media/libaaudio/tests/test_atomic_fifo.cpp +++ b/media/libaaudio/tests/test_atomic_fifo.cpp @@ -23,12 +23,12 @@ #include "fifo/FifoController.h" using android::fifo_frames_t; +using android::fifo_counter_t; using android::FifoController; using android::FifoBuffer; using android::WrappingBuffer; -//void foo() { -TEST(test_fifi_controller, fifo_indices) { +TEST(test_fifo_controller, fifo_indices) { // Values are arbitrary primes designed to trigger edge cases. constexpr int capacity = 83; constexpr int threshold = 47; @@ -73,18 +73,59 @@ TEST(test_fifi_controller, fifo_indices) { ASSERT_EQ(threshold - advance2, fifoController.getEmptyFramesAvailable()); } +TEST(test_fifo_controller, fifo_wrap_around_zero) { + constexpr int capacity = 7; // arbitrary prime + constexpr int threshold = capacity; + FifoController fifoController(capacity, threshold); + ASSERT_EQ(capacity, fifoController.getCapacity()); + ASSERT_EQ(threshold, fifoController.getThreshold()); + + fifoController.setReadCounter(-10); // a bit less than negative capacity + for (int i = 0; i < 20; i++) { + EXPECT_EQ(i - 10, fifoController.getReadCounter()); + EXPECT_GE(fifoController.getReadIndex(), 0); + EXPECT_LT(fifoController.getReadIndex(), capacity); + fifoController.advanceReadIndex(1); + } + + fifoController.setWriteCounter(-10); + for (int i = 0; i < 20; i++) { + EXPECT_EQ(i - 10, fifoController.getWriteCounter()); + EXPECT_GE(fifoController.getWriteIndex(), 0); + EXPECT_LT(fifoController.getWriteIndex(), capacity); + fifoController.advanceWriteIndex(1); + } +} + + // TODO consider using a template for other data types. + +// Create a big array and then use a region in the middle for the unit tests. +// Then we can scan the rest of the array to see if it got clobbered. +static constexpr fifo_frames_t kBigArraySize = 1024; +static constexpr fifo_frames_t kFifoDataOffset = 128; // starting index of FIFO data +static constexpr int16_t kSafeDataValue = 0x7654; // original value of BigArray + class TestFifoBuffer { public: explicit TestFifoBuffer(fifo_frames_t capacity, fifo_frames_t threshold = 0) - : mFifoBuffer(sizeof(int16_t), capacity) { + : mFifoBuffer(sizeof(int16_t), capacity, + &mReadIndex, + &mWriteIndex, + &mVeryBigArray[kFifoDataOffset]) // address of start of FIFO data + { + + // Assume a frame is one int16_t. // For reading and writing. - mData = new int16_t[capacity]; if (threshold <= 0) { threshold = capacity; } mFifoBuffer.setThreshold(threshold); mThreshold = threshold; + + for (fifo_frames_t i = 0; i < kBigArraySize; i++) { + mVeryBigArray[i] = kSafeDataValue; + } } void checkMisc() { @@ -92,26 +133,70 @@ public: ASSERT_EQ(mThreshold, mFifoBuffer.getThreshold()); } + void verifyAddressInRange(void *p, void *valid, size_t numBytes) { + uintptr_t p_int = (uintptr_t) p; + uintptr_t valid_int = (uintptr_t) valid; + EXPECT_GE(p_int, valid_int); + EXPECT_LT(p_int, (valid_int + numBytes)); + } + + void verifyStorageIntegrity() { + for (fifo_frames_t i = 0; i < kFifoDataOffset; i++) { + EXPECT_EQ(mVeryBigArray[i], kSafeDataValue); + } + fifo_frames_t firstFrameAfter = kFifoDataOffset + mFifoBuffer.getBufferCapacityInFrames(); + for (fifo_frames_t i = firstFrameAfter; i < kBigArraySize; i++) { + EXPECT_EQ(mVeryBigArray[i], kSafeDataValue); + } + } + // Verify that the available frames in each part add up correctly. - void checkWrappingBuffer() { + void verifyWrappingBuffer() { WrappingBuffer wrappingBuffer; + + + // Does the sum of the two parts match the available value returned? + // For EmptyRoom fifo_frames_t framesAvailable = mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t wrapAvailable = mFifoBuffer.getEmptyRoomAvailable(&wrappingBuffer); EXPECT_EQ(framesAvailable, wrapAvailable); fifo_frames_t bothAvailable = wrappingBuffer.numFrames[0] + wrappingBuffer.numFrames[1]; EXPECT_EQ(framesAvailable, bothAvailable); - + // For FullData framesAvailable = mFifoBuffer.getFullFramesAvailable(); wrapAvailable = mFifoBuffer.getFullDataAvailable(&wrappingBuffer); EXPECT_EQ(framesAvailable, wrapAvailable); bothAvailable = wrappingBuffer.numFrames[0] + wrappingBuffer.numFrames[1]; EXPECT_EQ(framesAvailable, bothAvailable); + + // Are frame counts in legal range? + fifo_frames_t capacity = mFifoBuffer.getBufferCapacityInFrames(); + EXPECT_GE(wrappingBuffer.numFrames[0], 0); + EXPECT_LE(wrappingBuffer.numFrames[0], capacity); + EXPECT_GE(wrappingBuffer.numFrames[1], 0); + EXPECT_LE(wrappingBuffer.numFrames[1], capacity); + + // Are addresses within the FIFO data area? + size_t validBytes = capacity * sizeof(int16_t); + if (wrappingBuffer.numFrames[0]) { + verifyAddressInRange(wrappingBuffer.data[0], mFifoStorage, validBytes); + uint8_t *last = ((uint8_t *)wrappingBuffer.data[0]) + + mFifoBuffer.convertFramesToBytes(wrappingBuffer.numFrames[0]) - 1; + verifyAddressInRange(last, mFifoStorage, validBytes); + } + if (wrappingBuffer.numFrames[1]) { + verifyAddressInRange(wrappingBuffer.data[1], mFifoStorage, validBytes); + uint8_t *last = ((uint8_t *)wrappingBuffer.data[1]) + + mFifoBuffer.convertFramesToBytes(wrappingBuffer.numFrames[1]) - 1; + verifyAddressInRange(last, mFifoStorage, validBytes); + } + } // Write data but do not overflow. - void writeData(fifo_frames_t numFrames) { + void writeMultipleDataFrames(fifo_frames_t numFrames) { fifo_frames_t framesAvailable = mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t framesToWrite = std::min(framesAvailable, numFrames); @@ -122,8 +207,8 @@ public: ASSERT_EQ(framesToWrite, actual); } - // Read data but do not underflow. - void verifyData(fifo_frames_t numFrames) { + // Read whatever data is available, Do not underflow. + void verifyMultipleDataFrames(fifo_frames_t numFrames) { fifo_frames_t framesAvailable = mFifoBuffer.getFullFramesAvailable(); fifo_frames_t framesToRead = std::min(framesAvailable, numFrames); @@ -134,20 +219,35 @@ public: } } + // Read specified number of frames + void verifyRequestedData(fifo_frames_t numFrames) { + fifo_frames_t framesAvailable = + mFifoBuffer.getFullFramesAvailable(); + ASSERT_LE(numFrames, framesAvailable); + fifo_frames_t framesToRead = std::min(framesAvailable, numFrames); + fifo_frames_t actual = mFifoBuffer.read(mData, framesToRead); + ASSERT_EQ(actual, numFrames); + for (int i = 0; i < actual; i++) { + ASSERT_EQ(mNextVerifyIndex++, mData[i]); + } + } + // Wrap around the end of the buffer. void checkWrappingWriteRead() { constexpr int frames1 = 43; constexpr int frames2 = 15; - writeData(frames1); - checkWrappingBuffer(); - verifyData(frames1); - checkWrappingBuffer(); + writeMultipleDataFrames(frames1); + verifyWrappingBuffer(); + verifyRequestedData(frames1); + verifyWrappingBuffer(); - writeData(frames2); - checkWrappingBuffer(); - verifyData(frames2); - checkWrappingBuffer(); + writeMultipleDataFrames(frames2); + verifyWrappingBuffer(); + verifyRequestedData(frames2); + verifyWrappingBuffer(); + + verifyStorageIntegrity(); } // Write and Read a specific amount of data. @@ -156,10 +256,12 @@ public: // Wrap around with the smaller region in the second half. const int frames1 = capacity - 4; const int frames2 = 7; // arbitrary, small - writeData(frames1); - verifyData(frames1); - writeData(frames2); - verifyData(frames2); + writeMultipleDataFrames(frames1); + verifyRequestedData(frames1); + writeMultipleDataFrames(frames2); + verifyRequestedData(frames2); + + verifyStorageIntegrity(); } // Write and Read a specific amount of data. @@ -168,10 +270,12 @@ public: // Wrap around with the larger region in the second half. const int frames1 = capacity - 4; const int frames2 = capacity - 9; // arbitrary, large - writeData(frames1); - verifyData(frames1); - writeData(frames2); - verifyData(frames2); + writeMultipleDataFrames(frames1); + verifyRequestedData(frames1); + writeMultipleDataFrames(frames2); + verifyRequestedData(frames2); + + verifyStorageIntegrity(); } // Randomly read or write up to the maximum amount of data. @@ -180,30 +284,67 @@ public: fifo_frames_t framesEmpty = mFifoBuffer.getEmptyFramesAvailable(); fifo_frames_t numFrames = (fifo_frames_t)(drand48() * framesEmpty); - writeData(numFrames); + writeMultipleDataFrames(numFrames); fifo_frames_t framesFull = mFifoBuffer.getFullFramesAvailable(); numFrames = (fifo_frames_t)(drand48() * framesFull); - verifyData(numFrames); + verifyMultipleDataFrames(numFrames); } + + verifyStorageIntegrity(); + } + + // Write and Read a specific amount of data. + void checkNegativeCounters() { + fifo_counter_t counter = -9876; + mFifoBuffer.setWriteCounter(counter); + mFifoBuffer.setReadCounter(counter); + checkWrappingWriteRead(); + } + + // Wrap over the boundary at 0x7FFFFFFFFFFFFFFF + // Note that the behavior of a signed overflow is technically undefined. + void checkHalfWrap() { + fifo_counter_t counter = INT64_MAX - 10; + mFifoBuffer.setWriteCounter(counter); + mFifoBuffer.setReadCounter(counter); + ASSERT_GT(mFifoBuffer.getWriteCounter(), 0); + checkWrappingWriteRead(); + ASSERT_LT(mFifoBuffer.getWriteCounter(), 0); // did we wrap past INT64_MAX? + } + + // Wrap over the boundary at 0xFFFFFFFFFFFFFFFF + void checkFullWrap() { + fifo_counter_t counter = -10; + mFifoBuffer.setWriteCounter(counter); + mFifoBuffer.setReadCounter(counter); + ASSERT_LT(mFifoBuffer.getWriteCounter(), 0); + writeMultipleDataFrames(20); + ASSERT_GT(mFifoBuffer.getWriteCounter(), 0); // did we wrap past zero? + verifyStorageIntegrity(); } FifoBuffer mFifoBuffer; - int16_t *mData; fifo_frames_t mNextWriteIndex = 0; fifo_frames_t mNextVerifyIndex = 0; fifo_frames_t mThreshold; + + fifo_counter_t mReadIndex = 0; + fifo_counter_t mWriteIndex = 0; + int16_t mVeryBigArray[kBigArraySize]; // Use the middle of this array for the FIFO. + int16_t *mFifoStorage = &mVeryBigArray[kFifoDataOffset]; // Start here for storage. + int16_t mData[kBigArraySize]{}; }; -TEST(test_fifo_buffer, fifo_read_write) { +TEST(test_fifo_buffer, fifo_write_read) { constexpr int capacity = 51; // arbitrary TestFifoBuffer tester(capacity); tester.checkMisc(); tester.checkWriteRead(); } -TEST(test_fifo_buffer, fifo_wrapping_read_write) { +TEST(test_fifo_buffer, fifo_wrapping_write_read) { constexpr int capacity = 59; // arbitrary, a little bigger this time TestFifoBuffer tester(capacity); tester.checkWrappingWriteRead(); @@ -227,3 +368,21 @@ TEST(test_fifo_buffer, fifo_random_threshold) { TestFifoBuffer tester(capacity, threshold); tester.checkRandomWriteRead(); } + +TEST(test_fifo_buffer, fifo_negative_counters) { + constexpr int capacity = 49; // arbitrary + TestFifoBuffer tester(capacity); + tester.checkNegativeCounters(); +} + +TEST(test_fifo_buffer, fifo_half_wrap) { + constexpr int capacity = 57; // arbitrary + TestFifoBuffer tester(capacity); + tester.checkHalfWrap(); +} + +TEST(test_fifo_buffer, fifo_full_wrap) { + constexpr int capacity = 57; // arbitrary + TestFifoBuffer tester(capacity); + tester.checkFullWrap(); +} -- GitLab From cd7466f4bc56c1d8b568d79499f288cee9f4fe21 Mon Sep 17 00:00:00 2001 From: Yanning Bai Date: Tue, 4 Dec 2018 17:10:12 +0800 Subject: [PATCH 0551/1530] Add AC-4 support for ACodec Bug: 119312182 Test: make Change-Id: I96ae9822914fb84041df25b3120d6744fd502e6a --- media/libstagefright/ACodec.cpp | 60 +++++++++++++++++++ .../include/media/stagefright/ACodec.h | 2 + 2 files changed, 62 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 3080db595d..f4b5600366 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2237,6 +2237,15 @@ status_t ACodec::configureCodec( } else { err = setupEAC3Codec(encoder, numChannels, sampleRate); } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4)) { + int32_t numChannels; + int32_t sampleRate; + if (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate)) { + err = INVALID_OPERATION; + } else { + err = setupAC4Codec(encoder, numChannels, sampleRate); + } } if (err != OK) { @@ -2893,6 +2902,38 @@ status_t ACodec::setupEAC3Codec( (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3, &def, sizeof(def)); } +status_t ACodec::setupAC4Codec( + bool encoder, int32_t numChannels, int32_t sampleRate) { + status_t err = setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels); + + if (err != OK) { + return err; + } + + if (encoder) { + ALOGW("AC4 encoding is not supported."); + return INVALID_OPERATION; + } + + OMX_AUDIO_PARAM_ANDROID_AC4TYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + + err = mOMXNode->getParameter( + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc4, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + def.nChannels = numChannels; + def.nSampleRate = sampleRate; + + return mOMXNode->setParameter( + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc4, &def, sizeof(def)); +} + static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate( bool isAMRWB, int32_t bps) { if (isAMRWB) { @@ -5246,6 +5287,25 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp ¬ify) { break; } + case OMX_AUDIO_CodingAndroidAC4: + { + OMX_AUDIO_PARAM_ANDROID_AC4TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = portIndex; + + err = mOMXNode->getParameter( + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc4, + ¶ms, sizeof(params)); + if (err != OK) { + return err; + } + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC4); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + case OMX_AUDIO_CodingAndroidOPUS: { OMX_AUDIO_PARAM_ANDROID_OPUSTYPE params; diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 73f93d14f7..5dd1966c4f 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -461,6 +461,8 @@ private: status_t setupEAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t setupAC4Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t selectAudioPortFormat( OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); -- GitLab From ddfbcedc35da56dabe6c30c62432c81ef4d4a759 Mon Sep 17 00:00:00 2001 From: Yanning Bai Date: Wed, 28 Nov 2018 15:07:15 +0800 Subject: [PATCH 0552/1530] Add eac3-joc MIME type support Bug: 120549104 Test: make Change-Id: I6b809e752330566d3fad81065a4f2e7418d8bea6 --- media/libstagefright/Utils.cpp | 1 + media/libstagefright/foundation/MediaDefs.cpp | 1 + .../foundation/include/media/stagefright/foundation/MediaDefs.h | 1 + media/libstagefright/omx/OMXUtils.cpp | 2 ++ 4 files changed, 5 insertions(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index ec043f264f..ac40b99aa8 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1821,6 +1821,7 @@ static const struct mime_conv_t mimeLookup[] = { { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS}, { MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3}, { MEDIA_MIMETYPE_AUDIO_EAC3, AUDIO_FORMAT_E_AC3}, + { MEDIA_MIMETYPE_AUDIO_EAC3_JOC, AUDIO_FORMAT_E_AC3_JOC}, { MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4}, { MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC}, { MEDIA_MIMETYPE_AUDIO_ALAC, AUDIO_FORMAT_ALAC }, diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index f93ae65170..aba44bbd21 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -50,6 +50,7 @@ const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3"; const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3"; +const char *MEDIA_MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc"; const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4"; const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; const char *MEDIA_MIMETYPE_AUDIO_ALAC = "audio/alac"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index 523378e566..8edddcc0a4 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -52,6 +52,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM; extern const char *MEDIA_MIMETYPE_AUDIO_AC3; extern const char *MEDIA_MIMETYPE_AUDIO_EAC3; +extern const char *MEDIA_MIMETYPE_AUDIO_EAC3_JOC; extern const char *MEDIA_MIMETYPE_AUDIO_AC4; extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED; extern const char *MEDIA_MIMETYPE_AUDIO_ALAC; diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp index c499c77100..b1870359bc 100644 --- a/media/libstagefright/omx/OMXUtils.cpp +++ b/media/libstagefright/omx/OMXUtils.cpp @@ -164,6 +164,8 @@ const char *GetComponentRole(bool isEncoder, const char *mime) { "audio_decoder.ac3", "audio_encoder.ac3" }, { MEDIA_MIMETYPE_AUDIO_EAC3, "audio_decoder.eac3", "audio_encoder.eac3" }, + { MEDIA_MIMETYPE_AUDIO_EAC3_JOC, + "audio_decoder.eac3_joc", "audio_encoder.eac3_joc" }, { MEDIA_MIMETYPE_AUDIO_AC4, "audio_decoder.ac4", "audio_encoder.ac4" }, { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, -- GitLab From 58f7ff570d4e06189fe9c1fa4bc2988ebb7473a0 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 3 Dec 2018 14:16:46 -0800 Subject: [PATCH 0553/1530] aaudio: Fix converting negative FIFO counters to index The index into the FIFO is calculated by using: counter % capacity But the counter is signed and when it is negative the modulo can have a negative result. That can cause a negative array index and an access out of bounds. This is not normally a problem because the counter is 64 bits and it will not overflow until the audio has run for a few million years. But a hacker might be able to modify this internal counter and force an error. The solution involves casting to a uint64_t before doing the modulo. Note that there may still be a discontinuity when the counter wraps from -1 to 0. But that will not result in an out-of-bounds access. It may cause a noise but an app could simply create a noise directly. So that is not considered an exploit. Bug: 120147915 Test: test_atomic_fifo.cpp Change-Id: I6fe57bcb44528d29b5edb817c592e5e9a8aaf8eb --- media/libaaudio/src/fifo/FifoControllerBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp index 9885cb0205..66e247f575 100644 --- a/media/libaaudio/src/fifo/FifoControllerBase.cpp +++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp @@ -38,7 +38,7 @@ fifo_frames_t FifoControllerBase::getFullFramesAvailable() { fifo_frames_t FifoControllerBase::getReadIndex() { // % works with non-power of two sizes - return (fifo_frames_t) (getReadCounter() % mCapacity); + return (fifo_frames_t) ((uint64_t)getReadCounter() % mCapacity); } void FifoControllerBase::advanceReadIndex(fifo_frames_t numFrames) { @@ -51,7 +51,7 @@ fifo_frames_t FifoControllerBase::getEmptyFramesAvailable() { fifo_frames_t FifoControllerBase::getWriteIndex() { // % works with non-power of two sizes - return (fifo_frames_t) (getWriteCounter() % mCapacity); + return (fifo_frames_t) ((uint64_t)getWriteCounter() % mCapacity); } void FifoControllerBase::advanceWriteIndex(fifo_frames_t numFrames) { -- GitLab From a94fbb22404ef26d12fbb3ad0376df15f84f47ec Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 24 Apr 2018 14:31:19 +1000 Subject: [PATCH 0554/1530] Implement AudioTrack.isDirectPlaybackSupported method The method checks whether the provided AudioFormat can be played via currently connected output devices w/o SRC or downmixing by the framework. The implementation methods are called 'isDirectOutputSupported' to match APM's terminology. Bug: 120044865 Test: atest CtsMediaTestCases:AudioTrackTest#testBuilderValidate Change-Id: I11769f796c34583fb2641415d2faab00ac526123 --- media/libaudioclient/AudioTrack.cpp | 22 +++--- media/libaudioclient/IAudioPolicyService.cpp | 23 ++++++ .../include/media/AudioPolicyHelper.h | 17 +++++ .../libaudioclient/include/media/AudioTrack.h | 6 ++ .../include/media/IAudioPolicyService.h | 4 ++ services/audiopolicy/AudioPolicyInterface.h | 2 + .../managerdefault/AudioPolicyManager.cpp | 71 +++++++++++++------ .../managerdefault/AudioPolicyManager.h | 14 ++-- .../service/AudioPolicyInterfaceImpl.cpp | 11 +++ .../audiopolicy/service/AudioPolicyService.h | 2 + 10 files changed, 134 insertions(+), 38 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 02324ac6ca..8f06ee7748 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -30,9 +30,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -157,6 +159,15 @@ status_t AudioTrack::getMinFrameCount( return NO_ERROR; } +// static +bool AudioTrack::isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) { + ALOGV("%s()", __FUNCTION__); + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return false; + return aps->isDirectOutputSupported(config, attributes); +} + // --------------------------------------------------------------------------- static std::string audioContentTypeString(audio_content_type_t value) { @@ -465,16 +476,7 @@ status_t AudioTrack::set( __func__, mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags); mStreamType = AUDIO_STREAM_DEFAULT; - if ((mAttributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { - flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); - } - if ((mAttributes.flags & AUDIO_FLAG_LOW_LATENCY) != 0) { - flags = (audio_output_flags_t) (flags | AUDIO_OUTPUT_FLAG_FAST); - } - // check deep buffer after flags have been modified above - if (flags == AUDIO_OUTPUT_FLAG_NONE && (mAttributes.flags & AUDIO_FLAG_DEEP_BUFFER) != 0) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } + audio_attributes_flags_to_audio_output_flags(mAttributes.flags, flags); } // these below should probably come from the audioFlinger too... diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index a406658a16..5f47761c23 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -62,6 +62,7 @@ enum { SET_EFFECT_ENABLED, IS_STREAM_ACTIVE_REMOTELY, IS_OFFLOAD_SUPPORTED, + IS_DIRECT_OUTPUT_SUPPORTED, LIST_AUDIO_PORTS, GET_AUDIO_PORT, CREATE_AUDIO_PATCH, @@ -526,6 +527,16 @@ public: return reply.readInt32(); } + virtual bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&config, sizeof(audio_config_base_t)); + data.write(&attributes, sizeof(audio_attributes_t)); + status_t status = remote()->transact(IS_DIRECT_OUTPUT_SUPPORTED, data, &reply); + return status == NO_ERROR ? static_cast(reply.readInt32()) : false; + } + virtual status_t listAudioPorts(audio_port_role_t role, audio_port_type_t type, unsigned int *num_ports, @@ -1393,6 +1404,18 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case IS_DIRECT_OUTPUT_SUPPORTED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_config_base_t config = {}; + audio_attributes_t attributes = {}; + status_t status = data.read(&config, sizeof(audio_config_base_t)); + if (status != NO_ERROR) return status; + status = data.read(&attributes, sizeof(audio_attributes_t)); + if (status != NO_ERROR) return status; + reply->writeInt32(isDirectOutputSupported(config, attributes)); + return NO_ERROR; + } + case LIST_AUDIO_PORTS: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_port_role_t role = (audio_port_role_t)data.readInt32(); diff --git a/media/libaudioclient/include/media/AudioPolicyHelper.h b/media/libaudioclient/include/media/AudioPolicyHelper.h index 49432b7e40..46de6b397d 100644 --- a/media/libaudioclient/include/media/AudioPolicyHelper.h +++ b/media/libaudioclient/include/media/AudioPolicyHelper.h @@ -123,4 +123,21 @@ void stream_type_to_audio_attributes(audio_stream_type_t streamType, } } +// Convert flags sent from Java AudioAttributes.getFlags() method to audio_output_flags_t +static inline +void audio_attributes_flags_to_audio_output_flags(const audio_flags_mask_t audioAttributeFlags, + audio_output_flags_t &flags) { + if ((audioAttributeFlags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + flags = static_cast(flags | + AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_DIRECT); + } + if ((audioAttributeFlags & AUDIO_FLAG_LOW_LATENCY) != 0) { + flags = static_cast(flags | AUDIO_OUTPUT_FLAG_FAST); + } + // check deep buffer after flags have been modified above + if (flags == AUDIO_OUTPUT_FLAG_NONE && (audioAttributeFlags & AUDIO_FLAG_DEEP_BUFFER) != 0) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } +} + #endif //AUDIO_POLICY_HELPER_H_ diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 8238ea2671..7fdf7cc7bb 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -147,6 +147,12 @@ public: audio_stream_type_t streamType, uint32_t sampleRate); + /* Check if direct playback is possible for the given audio configuration and attributes. + * Return true if output is possible for the given parameters. Otherwise returns false. + */ + static bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes); + /* How data is transferred to AudioTrack */ enum transfer_type { diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index a246df68ea..f8debb2c11 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -126,6 +126,10 @@ public: // bit rate, duration, video and streaming or offload property is enabled virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0; + // Check if direct playback is possible for given format, sample rate, channel mask and flags. + virtual bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) = 0; + /* List available audio ports and their attributes */ virtual status_t listAudioPorts(audio_port_role_t role, audio_port_type_t type, diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 3c3a82b38f..bc7cf5e573 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -197,6 +197,8 @@ public: virtual status_t dump(int fd) = 0; virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0; + virtual bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) = 0; virtual status_t listAudioPorts(audio_port_role_t role, audio_port_type_t type, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6ec6a767dd..e8a1db41d4 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -692,22 +692,25 @@ void AudioPolicyManager::setSystemProperty(const char* property, const char* val ALOGV("setSystemProperty() property %s, value %s", property, value); } -// Find a direct output profile compatible with the parameters passed, even if the input flags do -// not explicitly request a direct output -sp AudioPolicyManager::getProfileForDirectOutput( - audio_devices_t device, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags) +// Find an output profile compatible with the parameters passed. When "directOnly" is set, restrict +// search to profiles for direct outputs. +sp AudioPolicyManager::getProfileForOutput( + audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + bool directOnly) { - // only retain flags that will drive the direct output profile selection - // if explicitly requested - static const uint32_t kRelevantFlags = - (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | - AUDIO_OUTPUT_FLAG_VOIP_RX); - flags = - (audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT); + if (directOnly) { + // only retain flags that will drive the direct output profile selection + // if explicitly requested + static const uint32_t kRelevantFlags = + (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | + AUDIO_OUTPUT_FLAG_VOIP_RX); + flags = + (audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT); + } sp profile; @@ -724,7 +727,9 @@ sp AudioPolicyManager::getProfileForDirectOutput( if ((mAvailableOutputDevices.types() & curProfile->getSupportedDevicesType()) == 0) { continue; } - // if several profiles are compatible, give priority to one with offload capability + if (!directOnly) return curProfile; + // when searching for direct outputs, if several profiles are compatible, give priority + // to one with offload capability if (profile != 0 && ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0)) { continue; } @@ -959,11 +964,12 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if (((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) { - profile = getProfileForDirectOutput(device, - config->sample_rate, - config->format, - config->channel_mask, - (audio_output_flags_t)*flags); + profile = getProfileForOutput(device, + config->sample_rate, + config->format, + config->channel_mask, + (audio_output_flags_t)*flags, + true /* directOnly */); } if (profile != 0) { @@ -2781,15 +2787,34 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI // See if there is a profile to support this. // AUDIO_DEVICE_NONE - sp profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */, + sp profile = getProfileForOutput(AUDIO_DEVICE_NONE /*ignore device */, offloadInfo.sample_rate, offloadInfo.format, offloadInfo.channel_mask, - AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, + true /* directOnly */); ALOGV("isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT "); return (profile != 0); } +bool AudioPolicyManager::isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) { + audio_output_flags_t output_flags = AUDIO_OUTPUT_FLAG_NONE; + audio_attributes_flags_to_audio_output_flags(attributes.flags, output_flags); + sp profile = getProfileForOutput(AUDIO_DEVICE_NONE /*ignore device */, + config.sample_rate, + config.format, + config.channel_mask, + output_flags, + true /* directOnly */); + ALOGV("%s() profile %sfound with name: %s, " + "sample rate: %u, format: 0x%x, channel_mask: 0x%x, output flags: 0x%x", + __FUNCTION__, profile != 0 ? "" : "NOT ", + (profile != 0 ? profile->getTagName().string() : "null"), + config.sample_rate, config.format, config.channel_mask, output_flags); + return (profile != 0); +} + status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role, audio_port_type_t type, unsigned int *num_ports, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 0436b1d0f8..a61c918ec3 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -192,6 +192,9 @@ public: virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo); + virtual bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes); + virtual status_t listAudioPorts(audio_port_role_t role, audio_port_type_t type, unsigned int *num_ports, @@ -479,11 +482,12 @@ protected: audio_format_t& format, audio_channel_mask_t& channelMask, audio_input_flags_t flags); - sp getProfileForDirectOutput(audio_devices_t device, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags); + sp getProfileForOutput(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + bool directOnly); audio_io_handle_t selectOutputForMusicEffects(); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 59c8f10075..cf665a9aae 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -936,6 +936,17 @@ bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) return mAudioPolicyManager->isOffloadSupported(info); } +bool AudioPolicyService::isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes) { + if (mAudioPolicyManager == NULL) { + ALOGV("mAudioPolicyManager == NULL"); + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isDirectOutputSupported(config, attributes); +} + + status_t AudioPolicyService::listAudioPorts(audio_port_role_t role, audio_port_type_t type, unsigned int *num_ports, diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 4d7235fce8..e62424d0d0 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -168,6 +168,8 @@ public: int delayMs = 0); virtual status_t setVoiceVolume(float volume, int delayMs = 0); virtual bool isOffloadSupported(const audio_offload_info_t &config); + virtual bool isDirectOutputSupported(const audio_config_base_t& config, + const audio_attributes_t& attributes); virtual status_t listAudioPorts(audio_port_role_t role, audio_port_type_t type, -- GitLab From 0d4c354adb905523e47a6e53680de22d4529a327 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 5 Dec 2018 14:12:36 -0800 Subject: [PATCH 0555/1530] FLACExtractor: Extend bit-depth and sample rate Test: Downloaded and sox-created FLAC files up to 192kHz Bug: 120564563 Change-Id: I9232ae2a11422b3e447df5a4f3a3478dd2ab53cb --- media/extractors/flac/FLACExtractor.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index d54aaef992..0b62fcfe7f 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -514,8 +514,10 @@ status_t FLACParser::init() case 8: case 16: case 24: + case 32: // generally not expected for FLAC break; default: + // Note: internally the FLAC extractor supports 2-32 bits. ALOGE("unsupported bits per sample %u", getBitsPerSample()); return NO_INIT; } @@ -532,8 +534,11 @@ status_t FLACParser::init() case 48000: case 88200: case 96000: + case 176400: + case 192000: break; default: + // Note: internally we support arbitrary sample rates from 8kHz to 192kHz. ALOGE("unsupported sample rate %u", getSampleRate()); return NO_INIT; } -- GitLab From 6a746a4dc73c8acdce39442bbc1dd26dbd739587 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 5 Dec 2018 14:07:04 -0800 Subject: [PATCH 0556/1530] FLACExtractor: Fix 24-bit compressed to 16-bit raw handling Test: Instrumented test with 24 bit FLAC files. Bug: 120562758 Change-Id: I64e90148869e1318f9123f23753e36316f6f473d --- media/extractors/flac/FLACExtractor.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index d54aaef992..2e6810edb8 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -401,6 +401,7 @@ void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) // Copy samples from FLAC native 32-bit non-interleaved to 16-bit signed // or 32-bit float interleaved. +// TODO: Consider moving to audio_utils. // These are candidates for optimization if needed. static void copyTo16Signed( short *dst, @@ -408,10 +409,19 @@ static void copyTo16Signed( unsigned nSamples, unsigned nChannels, unsigned bitsPerSample) { - const unsigned leftShift = 16 - bitsPerSample; - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i] << leftShift; + const int leftShift = 16 - (int)bitsPerSample; // cast to int to prevent unsigned overflow. + if (leftShift >= 0) { + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i] << leftShift; + } + } + } else { + const int rightShift = -leftShift; + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i] >> rightShift; + } } } } -- GitLab From fe9689ab162c7efaa468cab3eaadf585ca08ec16 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 5 Dec 2018 15:45:47 -0800 Subject: [PATCH 0557/1530] hdr10+: adding C2 params for HDR10+ bug: 118507186 Change-Id: I6e7d9abd76c5974eb53e3797df321272946572bc --- media/codec2/core/include/C2Config.h | 9 +++++++++ .../include/media/stagefright/MediaCodecConstants.h | 4 ++++ media/ndk/NdkMediaFormat.cpp | 1 + 3 files changed, 14 insertions(+) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index 799ade4302..27aa064b9a 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -194,6 +194,7 @@ enum C2ParamIndexKind : C2Param::type_index_t { kParamIndexLayerIndex, kParamIndexLayerCount, kParamIndexIntraRefresh, + kParamIndexHdr10PlusMetadata, /* ------------------------------------ image components ------------------------------------ */ @@ -1560,6 +1561,14 @@ typedef C2StreamParam + C2StreamHdr10PlusInfo; +constexpr char C2_PARAMKEY_INPUT_HDR10_PLUS_INFO[] = "input.hdr10-plus-info"; +constexpr char C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO[] = "output.hdr10-plus-info"; + /* ------------------------------------ block-based coding ----------------------------------- */ /** diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index a462ae7d0a..704bfddedd 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -135,6 +135,8 @@ constexpr int32_t VP9Profile2 = 0x04; constexpr int32_t VP9Profile3 = 0x08; constexpr int32_t VP9Profile2HDR = 0x1000; constexpr int32_t VP9Profile3HDR = 0x2000; +constexpr int32_t VP9Profile2HDR10Plus = 0x4000; +constexpr int32_t VP9Profile3HDR10Plus = 0x8000; constexpr int32_t VP9Level1 = 0x1; constexpr int32_t VP9Level11 = 0x2; @@ -155,6 +157,7 @@ constexpr int32_t HEVCProfileMain = 0x01; constexpr int32_t HEVCProfileMain10 = 0x02; constexpr int32_t HEVCProfileMainStill = 0x04; constexpr int32_t HEVCProfileMain10HDR10 = 0x1000; +constexpr int32_t HEVCProfileMain10HDR10Plus = 0x2000; constexpr int32_t HEVCMainTierLevel1 = 0x1; constexpr int32_t HEVCHighTierLevel1 = 0x2; @@ -343,6 +346,7 @@ constexpr char KEY_FRAME_RATE[] = "frame-rate"; constexpr char KEY_GRID_COLUMNS[] = "grid-cols"; constexpr char KEY_GRID_ROWS[] = "grid-rows"; constexpr char KEY_HDR_STATIC_INFO[] = "hdr-static-info"; +constexpr char KEY_HDR10_PLUS_INFO[] = "hdr10-plus-info"; constexpr char KEY_HEIGHT[] = "height"; constexpr char KEY_I_FRAME_INTERVAL[] = "i-frame-interval"; constexpr char KEY_INTRA_REFRESH_PERIOD[] = "intra-refresh-period"; diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index bf9725c1a5..38cb65de3e 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -320,6 +320,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_GENRE = "genre"; EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLUMNS = "grid-cols"; EXPORT const char* AMEDIAFORMAT_KEY_GRID_ROWS = "grid-rows"; EXPORT const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO = "hdr-static-info"; +EXPORT const char* AMEDIAFORMAT_KEY_HDR10_PLUS_INFO = "hdr10-plus-info"; EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height"; EXPORT const char* AMEDIAFORMAT_KEY_ICC_PROFILE = "icc-profile"; EXPORT const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; -- GitLab From 070f41ead172f0944351906ae8073fff01e854c9 Mon Sep 17 00:00:00 2001 From: Previr Rangroo Date: Thu, 22 Nov 2018 18:33:57 +1100 Subject: [PATCH 0558/1530] Update AC-4 support in MPEG-4 extractor - Add support for AC-4 Immersive Stereo (IMS) which has presentation version as 2. - Update AC-4 specific box size for AC4 IMS Bug: 119312182 Test: compile Change-Id: I988e1721265985d1036984efad1ca2ab549ac6c6 --- media/extractors/mp4/AC4Parser.cpp | 12 ++++++------ media/extractors/mp4/MPEG4Extractor.cpp | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp index a95c2db8c1..59a2e9bd9e 100644 --- a/media/extractors/mp4/AC4Parser.cpp +++ b/media/extractors/mp4/AC4Parser.cpp @@ -310,13 +310,13 @@ bool AC4DSIParser::parse() { pres_bytes += mBitReader.getBits(16); } ALOGV("%u: pres_bytes = %u\n", presentation, pres_bytes); - if (presentation_version > 1) { + if (presentation_version > 2) { CHECK_BITS_LEFT(pres_bytes * 8); mBitReader.skipBits(pres_bytes * 8); continue; } - // ac4_presentation_v0_dsi() and ac4_presentation_v1_dsi() both - // start with a presentation_config of 5 bits + // ac4_presentation_v0_dsi(), ac4_presentation_v1_dsi() and ac4_presentation_v2_dsi() + // all start with a presentation_config of 5 bits CHECK_BITS_LEFT(5); presentation_config = mBitReader.getBits(5); b_single_substream_group = (presentation_config == 0x1f); @@ -363,7 +363,7 @@ bool AC4DSIParser::parse() { uint32_t dsi_frame_rate_multiply_info = mBitReader.getBits(2); ALOGV("%u: dsi_frame_rate_multiply_info = %d\n", presentation, dsi_frame_rate_multiply_info); - if (ac4_dsi_version == 1 && presentation_version == 1) { + if (ac4_dsi_version == 1 && (presentation_version == 1 || presentation_version == 2)) { CHECK_BITS_LEFT(2); uint32_t dsi_frame_rate_fraction_info = mBitReader.getBits(2); ALOGV("%u: dsi_frame_rate_fraction_info = %d\n", presentation, @@ -386,7 +386,7 @@ bool AC4DSIParser::parse() { ALOGV("%u: b_presentation_channel_coded = %s\n", presentation, BOOLSTR(b_presentation_channel_coded)); if (b_presentation_channel_coded) { - if (presentation_version == 1) { + if (presentation_version == 1 || presentation_version == 2) { CHECK_BITS_LEFT(5); uint32_t dsi_presentation_ch_mode = mBitReader.getBits(5); mPresentations[presentation].mChannelMode = dsi_presentation_ch_mode; @@ -411,7 +411,7 @@ bool AC4DSIParser::parse() { ALOGV("%u: presentation_channel_mask_v1 = 0x%06x\n", presentation, presentation_channel_mask_v1); } - if (presentation_version == 1) { + if (presentation_version == 1 || presentation_version == 2) { CHECK_BITS_LEFT(1); bool b_presentation_core_differs = (mBitReader.getBits(1) == 1); ALOGV("%u: b_presentation_core_differs = %s\n", presentation, diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 337ff2a0e1..9fb2e35ab3 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -2733,8 +2733,7 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { // + 4-byte type offset += 4; - // at least for AC4 DSI v1 this is big enough - const uint32_t kAC4SpecificBoxPayloadSize = 256; + const uint32_t kAC4SpecificBoxPayloadSize = 1176; uint8_t chunk[kAC4SpecificBoxPayloadSize]; ssize_t dsiSize = size - 8; // size of box - size and type fields if (dsiSize >= (ssize_t)kAC4SpecificBoxPayloadSize || -- GitLab From f054fc342205c670b0b88c5040da196aefd0164a Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 6 Dec 2018 09:45:59 -0800 Subject: [PATCH 0559/1530] AudioFlinger: add check for NULL mInput in MMAP Thread exitStandby() was crashing if headphones were unplugged between the open and start of a MMAP stream. Bug: 120564294 Test: see repro steps in bug Change-Id: Ic42743682ffeb48f2869eaa90581b4e4f7fcb1ab --- services/audioflinger/Threads.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 45d3a065aa..27aec9e308 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -9146,7 +9146,13 @@ AudioFlinger::MmapCaptureThread::MmapCaptureThread( status_t AudioFlinger::MmapCaptureThread::exitStandby() { - mInput->stream->setGain(1.0f); + { + // mInput might have been cleared by clearInput() + Mutex::Autolock _l(mLock); + if (mInput != nullptr && mInput->stream != nullptr) { + mInput->stream->setGain(1.0f); + } + } return MmapThread::exitStandby(); } -- GitLab From 0e043b67ee430bc807971a5ffb29c653501ce99c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 14 Nov 2018 11:26:05 -0800 Subject: [PATCH 0560/1530] C-ify MediaBuffer Bug: 111407253 Test: CTS, manual Change-Id: Id20094f23d9d0dc0ec23127bbedc62c6e29944bd --- include/media/MediaExtractorPluginApi.h | 58 ++++ include/media/MediaExtractorPluginHelper.h | 256 +++++++++++++++++- include/media/MediaTrack.h | 20 ++ include/media/NdkMediaErrorPriv.h | 6 +- include/media/NdkMediaFormatPriv.h | 19 +- media/extractors/wav/Android.bp | 1 - media/extractors/wav/WAVExtractor.cpp | 45 ++- media/extractors/wav/WAVExtractor.h | 4 +- media/libmedia/NdkMediaFormatPriv.cpp | 17 +- media/libmediaextractor/MediaBufferGroup.cpp | 8 +- .../include/media/DataSource.h | 2 +- .../media/stagefright/MediaBufferBase.h | 46 ++++ .../media/stagefright/MediaBufferGroup.h | 44 +++ media/libstagefright/MediaExtractor.cpp | 53 ++++ .../libstagefright/MediaExtractorFactory.cpp | 11 +- media/libstagefright/MediaTrack.cpp | 72 +++++ .../codecs/m4v_h263/enc/SoftMPEG4Encoder.h | 1 - .../media/stagefright/MediaExtractor.h | 16 ++ media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + 20 files changed, 625 insertions(+), 56 deletions(-) diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h index 38962e5f0b..b480bbe54c 100644 --- a/include/media/MediaExtractorPluginApi.h +++ b/include/media/MediaExtractorPluginApi.h @@ -69,6 +69,40 @@ struct CMediaTrackV2 { bool (*supportsNonBlockingRead)(void *data); }; +/** + * only use CMediaBufferV3 allocated from the CMediaBufferGroupV3 that is + * provided to CMediaTrack::start() + */ +struct CMediaBufferV3 { + void *handle; + void (*release)(void *handle); + void* (*data)(void *handle); + size_t (*size)(void *handle); + size_t (*range_offset)(void *handle); + size_t (*range_length)(void *handle); + void (*set_range)(void *handle, size_t offset, size_t length); + AMediaFormat* (*meta_data)(void *handle); +}; + +struct CMediaBufferGroupV3 { + void *handle; + bool (*init)(void *handle, size_t buffers, size_t buffer_size, size_t growthLimit); + void (*add_buffer)(void *handle, size_t size); + media_status_t (*acquire_buffer)(void *handle, + CMediaBufferV3 **buffer, bool nonBlocking, size_t requestedSize); + bool (*has_buffers)(void *handle); +}; + +struct CMediaTrackV3 { + void *data; + void (*free)(void *data); + + media_status_t (*start)(void *data, CMediaBufferGroupV3 *bufferGroup); + media_status_t (*stop)(void *data); + media_status_t (*getFormat)(void *data, AMediaFormat *format); + media_status_t (*read)(void *data, CMediaBufferV3 **buffer, uint32_t options, int64_t seekPosUs); + bool (*supportsNonBlockingRead)(void *data); +}; struct CMediaExtractorV1 { void *data; @@ -104,6 +138,23 @@ struct CMediaExtractorV2 { const char * (*name)(void *data); }; +struct CMediaExtractorV3 { + void *data; + + void (*free)(void *data); + size_t (*countTracks)(void *data); + CMediaTrackV3* (*getTrack)(void *data, size_t index); + media_status_t (*getTrackMetaData)( + void *data, + AMediaFormat *meta, + size_t index, uint32_t flags); + + media_status_t (*getMetaData)(void *data, AMediaFormat *meta); + uint32_t (*flags)(void *data); + media_status_t (*setMediaCas)(void *data, const uint8_t* casToken, size_t size); + const char * (*name)(void *data); +}; + typedef CMediaExtractorV1* (*CreatorFuncV1)(CDataSource *source, void *meta); typedef void (*FreeMetaFunc)(void *meta); @@ -121,6 +172,12 @@ typedef CreatorFuncV2 (*SnifferFuncV2)( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta); +typedef CMediaExtractorV3* (*CreatorFuncV3)(CDataSource *source, void *meta); + +typedef CreatorFuncV3 (*SnifferFuncV3)( + CDataSource *source, float *confidence, + void **meta, FreeMetaFunc *freeMeta); + typedef CMediaExtractorV1 CMediaExtractor; typedef CreatorFuncV1 CreatorFunc; @@ -148,6 +205,7 @@ struct ExtractorDef { union { SnifferFuncV1 v1; SnifferFuncV2 v2; + SnifferFuncV3 v3; } sniff; }; diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h index 858c575b05..292ec93ee8 100644 --- a/include/media/MediaExtractorPluginHelper.h +++ b/include/media/MediaExtractorPluginHelper.h @@ -20,12 +20,13 @@ #include #include -#include +#include #include #include #include #include +#include namespace android { @@ -171,6 +172,176 @@ inline CMediaTrackV2 *wrapV2(MediaTrackHelperV2 *track) { return wrapper; } +class MediaTrackHelperV3; + +class MediaBufferHelperV3 { +private: + friend CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *); + CMediaBufferV3 *mBuffer; +public: + MediaBufferHelperV3(CMediaBufferV3 *buf) { + mBuffer = buf; + } + + ~MediaBufferHelperV3() {} + + void release() { + mBuffer->release(mBuffer->handle); + } + + void* data() { + return mBuffer->data(mBuffer->handle); + } + + size_t size() { + return mBuffer->size(mBuffer->handle); + } + + size_t range_offset() { + return mBuffer->range_offset(mBuffer->handle); + } + + size_t range_length() { + return mBuffer->range_length(mBuffer->handle); + } + + void set_range(size_t offset, size_t length) { + mBuffer->set_range(mBuffer->handle, offset, length); + } + AMediaFormat *meta_data() { + return mBuffer->meta_data(mBuffer->handle); + } +}; + +class MediaBufferGroupHelperV3 { +private: + CMediaBufferGroupV3 *mGroup; + std::map mBufferHelpers; +public: + MediaBufferGroupHelperV3(CMediaBufferGroupV3 *group) { + mGroup = group; + } + ~MediaBufferGroupHelperV3() { + // delete all entries in map + ALOGV("buffergroup %p map has %zu entries", this, mBufferHelpers.size()); + for (auto it = mBufferHelpers.begin(); it != mBufferHelpers.end(); ++it) { + delete it->second; + } + } + bool init(size_t buffers, size_t buffer_size, size_t growthLimit = 0) { + return mGroup->init(mGroup->handle, buffers, buffer_size, growthLimit); + } + void add_buffer(size_t size) { + mGroup->add_buffer(mGroup->handle, size); + } + media_status_t acquire_buffer( + MediaBufferHelperV3 **buffer, bool nonBlocking = false, size_t requestedSize = 0) { + CMediaBufferV3 *buf = nullptr; + media_status_t ret = + mGroup->acquire_buffer(mGroup->handle, &buf, nonBlocking, requestedSize); + if (ret == AMEDIA_OK && buf != nullptr) { + auto helper = mBufferHelpers.find(buf); + if (helper == mBufferHelpers.end()) { + MediaBufferHelperV3* newHelper = new MediaBufferHelperV3(buf); + mBufferHelpers.insert(std::make_pair(buf, newHelper)); + *buffer = newHelper; + } else { + *buffer = helper->second; + } + } else { + *buffer = nullptr; + } + return ret; + } + bool has_buffers() { + return mGroup->has_buffers(mGroup->handle); + } +}; + +class MediaTrackHelperV3 { +public: + MediaTrackHelperV3() : mBufferGroup(nullptr) { + } + virtual ~MediaTrackHelperV3() { + delete mBufferGroup; + } + virtual media_status_t start() = 0; + virtual media_status_t stop() = 0; + virtual media_status_t getFormat(AMediaFormat *format) = 0; + + class ReadOptions { + public: + enum SeekMode : int32_t { + SEEK_PREVIOUS_SYNC, + SEEK_NEXT_SYNC, + SEEK_CLOSEST_SYNC, + SEEK_CLOSEST, + SEEK_FRAME_INDEX, + }; + + ReadOptions(uint32_t options, int64_t seekPosUs) { + mOptions = options; + mSeekPosUs = seekPosUs; + } + bool getSeekTo(int64_t *time_us, SeekMode *mode) const { + if ((mOptions & CMediaTrackReadOptions::SEEK) == 0) { + return false; + } + *time_us = mSeekPosUs; + *mode = (SeekMode) (mOptions & 7); + return true; + } + bool getNonBlocking() const { + return mOptions & CMediaTrackReadOptions::NONBLOCKING; + } + private: + uint32_t mOptions; + int64_t mSeekPosUs; + }; + + virtual media_status_t read( + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL) = 0; + virtual bool supportsNonBlockingRead() { return false; } +protected: + friend CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *track); + MediaBufferGroupHelperV3 *mBufferGroup; +}; + +inline CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *track) { + CMediaTrackV3 *wrapper = (CMediaTrackV3*) malloc(sizeof(CMediaTrackV3)); + wrapper->data = track; + wrapper->free = [](void *data) -> void { + delete (MediaTrackHelperV3*)(data); + }; + wrapper->start = [](void *data, CMediaBufferGroupV3 *bufferGroup) -> media_status_t { + if (((MediaTrackHelperV3*)data)->mBufferGroup) { + // this shouldn't happen, but handle it anyway + delete ((MediaTrackHelperV3*)data)->mBufferGroup; + } + ((MediaTrackHelperV3*)data)->mBufferGroup = new MediaBufferGroupHelperV3(bufferGroup); + return ((MediaTrackHelperV3*)data)->start(); + }; + wrapper->stop = [](void *data) -> media_status_t { + return ((MediaTrackHelperV3*)data)->stop(); + }; + wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t { + return ((MediaTrackHelperV3*)data)->getFormat(meta); + }; + wrapper->read = [](void *data, CMediaBufferV3 **buffer, uint32_t options, int64_t seekPosUs) + -> media_status_t { + MediaTrackHelperV3::ReadOptions opts(options, seekPosUs); + MediaBufferHelperV3 *buf = NULL; + media_status_t ret = ((MediaTrackHelperV3*)data)->read(&buf, &opts); + if (ret == AMEDIA_OK && buf != nullptr) { + *buffer = buf->mBuffer; + } + return ret; + }; + wrapper->supportsNonBlockingRead = [](void *data) -> bool { + return ((MediaTrackHelperV3*)data)->supportsNonBlockingRead(); + }; + return wrapper; +} // extractor plugins can derive from this class which looks remarkably @@ -341,6 +512,89 @@ inline CMediaExtractorV2 *wrapV2(MediaExtractorPluginHelperV2 *extractor) { return wrapper; } +class MediaExtractorPluginHelperV3 +{ +public: + virtual ~MediaExtractorPluginHelperV3() {} + virtual size_t countTracks() = 0; + virtual MediaTrackHelperV3 *getTrack(size_t index) = 0; + + enum GetTrackMetaDataFlags { + kIncludeExtensiveMetaData = 1 + }; + virtual media_status_t getTrackMetaData( + AMediaFormat *meta, + size_t index, uint32_t flags = 0) = 0; + + // Return container specific meta-data. The default implementation + // returns an empty metadata object. + virtual media_status_t getMetaData(AMediaFormat *meta) = 0; + + enum Flags { + CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" + CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button" + CAN_PAUSE = 4, + CAN_SEEK = 8, // the "seek bar" + }; + + // If subclasses do _not_ override this, the default is + // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE + virtual uint32_t flags() const { + return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE; + }; + + virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { + return AMEDIA_ERROR_INVALID_OPERATION; + } + + virtual const char * name() { return ""; } + +protected: + MediaExtractorPluginHelperV3() {} + +private: + MediaExtractorPluginHelperV3(const MediaExtractorPluginHelperV2 &); + MediaExtractorPluginHelperV3 &operator=(const MediaExtractorPluginHelperV2 &); +}; + +inline CMediaExtractorV3 *wrapV3(MediaExtractorPluginHelperV3 *extractor) { + CMediaExtractorV3 *wrapper = (CMediaExtractorV3*) malloc(sizeof(CMediaExtractorV3)); + wrapper->data = extractor; + wrapper->free = [](void *data) -> void { + delete (MediaExtractorPluginHelperV3*)(data); + }; + wrapper->countTracks = [](void *data) -> size_t { + return ((MediaExtractorPluginHelperV3*)data)->countTracks(); + }; + wrapper->getTrack = [](void *data, size_t index) -> CMediaTrackV3* { + return wrapV3(((MediaExtractorPluginHelperV3*)data)->getTrack(index)); + }; + wrapper->getTrackMetaData = []( + void *data, + AMediaFormat *meta, + size_t index, uint32_t flags) -> media_status_t { + return ((MediaExtractorPluginHelperV3*)data)->getTrackMetaData(meta, index, flags); + }; + wrapper->getMetaData = []( + void *data, + AMediaFormat *meta) -> media_status_t { + return ((MediaExtractorPluginHelperV3*)data)->getMetaData(meta); + }; + wrapper->flags = []( + void *data) -> uint32_t { + return ((MediaExtractorPluginHelperV3*)data)->flags(); + }; + wrapper->setMediaCas = []( + void *data, const uint8_t *casToken, size_t size) -> media_status_t { + return ((MediaExtractorPluginHelperV3*)data)->setMediaCas(casToken, size); + }; + wrapper->name = []( + void *data) -> const char * { + return ((MediaExtractorPluginHelperV3*)data)->name(); + }; + return wrapper; +} + /* adds some convience methods */ class DataSourceHelper { public: diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h index ee3591e303..baa3410cb4 100644 --- a/include/media/MediaTrack.h +++ b/include/media/MediaTrack.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,25 @@ private: CMediaTrackV2 *wrapper; }; +class MediaTrackCUnwrapperV3 : public MediaTrack { +public: + explicit MediaTrackCUnwrapperV3(CMediaTrackV3 *wrapper); + + virtual status_t start(); + virtual status_t stop(); + virtual status_t getFormat(MetaDataBase& format); + virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL); + + virtual bool supportNonblockingRead(); + +protected: + virtual ~MediaTrackCUnwrapperV3(); + +private: + CMediaTrackV3 *wrapper; + MediaBufferGroup *bufferGroup; +}; + } // namespace android #endif // MEDIA_SOURCE_BASE_H_ diff --git a/include/media/NdkMediaErrorPriv.h b/include/media/NdkMediaErrorPriv.h index f5e2f02f41..3bbba79edb 100644 --- a/include/media/NdkMediaErrorPriv.h +++ b/include/media/NdkMediaErrorPriv.h @@ -20,10 +20,8 @@ #include #include -using namespace android; +media_status_t translate_error(android::status_t); -media_status_t translate_error(status_t); - -status_t reverse_translate_error(media_status_t); +android::status_t reverse_translate_error(media_status_t); #endif // _NDK_MEDIA_ERROR_PRIV_H diff --git a/include/media/NdkMediaFormatPriv.h b/include/media/NdkMediaFormatPriv.h index 6c452c3649..1fda4a8c81 100644 --- a/include/media/NdkMediaFormatPriv.h +++ b/include/media/NdkMediaFormatPriv.h @@ -27,6 +27,7 @@ #ifndef _NDK_MEDIA_FORMAT_PRIV_H #define _NDK_MEDIA_FORMAT_PRIV_H +#include #include #include #include @@ -35,20 +36,22 @@ extern "C" { #endif -using namespace android; - struct AMediaFormat { - sp mFormat; - String8 mDebug; - KeyedVector mStringCache; + android::sp mFormat; + android::String8 mDebug; + android::KeyedVector mStringCache; }; -AMediaFormat* AMediaFormat_fromMsg(const void*); -void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest); - #ifdef __cplusplus } // extern "C" #endif +namespace android { + +AMediaFormat* AMediaFormat_fromMsg(sp *); +void AMediaFormat_getFormat(const AMediaFormat* mData, sp *dest); + +} // namespace android + #endif // _NDK_MEDIA_FORMAT_PRIV_H diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp index 185bb32775..dcc73bbc57 100644 --- a/media/extractors/wav/Android.bp +++ b/media/extractors/wav/Android.bp @@ -9,7 +9,6 @@ cc_library_shared { shared_libs: [ "libbinder_ndk", "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 86500ef69a..17b5f81523 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -22,9 +22,7 @@ #include // for AIBinder_getCallingUid #include -#include #include -#include #include #include #include @@ -68,7 +66,7 @@ static uint16_t U16_LE_AT(const uint8_t *ptr) { return ptr[1] << 8 | ptr[0]; } -struct WAVSource : public MediaTrackHelperV2 { +struct WAVSource : public MediaTrackHelperV3 { WAVSource( DataSourceHelper *dataSource, AMediaFormat *meta, @@ -81,7 +79,7 @@ struct WAVSource : public MediaTrackHelperV2 { virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); virtual bool supportNonblockingRead() { return true; } @@ -101,7 +99,6 @@ private: off64_t mOffset; size_t mSize; bool mStarted; - MediaBufferGroup *mGroup; off64_t mCurrentPos; WAVSource(const WAVSource &); @@ -134,7 +131,7 @@ size_t WAVExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV2 *WAVExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *WAVExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; } @@ -379,14 +376,14 @@ WAVSource::WAVSource( mOutputFloat(outputFloat), mOffset(offset), mSize(size), - mStarted(false), - mGroup(NULL) { + mStarted(false) { CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)); CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels)); CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample)); } WAVSource::~WAVSource() { + ALOGI("~WAVSource"); if (mStarted) { stop(); } @@ -398,7 +395,9 @@ media_status_t WAVSource::start() { CHECK(!mStarted); // some WAV files may have large audio buffers that use shared memory transfer. - mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize); + if (!mBufferGroup->init(4 /* buffers */, kMaxFrameSize)) { + return AMEDIA_ERROR_UNKNOWN; + } mCurrentPos = mOffset; @@ -412,9 +411,6 @@ media_status_t WAVSource::stop() { CHECK(mStarted); - delete mGroup; - mGroup = NULL; - mStarted = false; return AMEDIA_OK; @@ -433,10 +429,10 @@ media_status_t WAVSource::getFormat(AMediaFormat *meta) { } media_status_t WAVSource::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; - if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) { + if (options != nullptr && options->getNonBlocking() && !mBufferGroup->has_buffers()) { return AMEDIA_ERROR_WOULD_BLOCK; } @@ -459,10 +455,10 @@ media_status_t WAVSource::read( mCurrentPos = pos + mOffset; } - MediaBufferBase *buffer; - status_t err = mGroup->acquire_buffer(&buffer); + MediaBufferHelperV3 *buffer; + media_status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { - return AMEDIA_ERROR_UNKNOWN; + return err; } // maxBytesToRead may be reduced so that in-place data conversion will fit in buffer size. @@ -573,9 +569,10 @@ media_status_t WAVSource::read( / (mNumChannels * bytesPerSample) / mSampleRate; } - buffer->meta_data().setInt64(kKeyTime, timeStampUs); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeStampUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); - buffer->meta_data().setInt32(kKeyIsSyncFrame, 1); mCurrentPos += n; *out = buffer; @@ -585,13 +582,13 @@ media_status_t WAVSource::read( //////////////////////////////////////////////////////////////////////////////// -static CMediaExtractorV2* CreateExtractor( +static CMediaExtractorV3* CreateExtractor( CDataSource *source, void *) { - return wrapV2(new WAVExtractor(new DataSourceHelper(source))); + return wrapV3(new WAVExtractor(new DataSourceHelper(source))); } -static CreatorFuncV2 Sniff( +static CreatorFuncV3 Sniff( CDataSource *source, float *confidence, void **, @@ -625,11 +622,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("7d613858-5837-4a38-84c5-332d1cddee27"), 1, // version "WAV Extractor", - { .v2 = Sniff } + { .v3 = Sniff } }; } diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h index ce34881a97..9b7dfde018 100644 --- a/media/extractors/wav/WAVExtractor.h +++ b/media/extractors/wav/WAVExtractor.h @@ -29,12 +29,12 @@ struct AMessage; struct CDataSource; class String8; -class WAVExtractor : public MediaExtractorPluginHelperV2 { +class WAVExtractor : public MediaExtractorPluginHelperV3 { public: explicit WAVExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/libmedia/NdkMediaFormatPriv.cpp b/media/libmedia/NdkMediaFormatPriv.cpp index 3c84d6a455..3a9fb8b727 100644 --- a/media/libmedia/NdkMediaFormatPriv.cpp +++ b/media/libmedia/NdkMediaFormatPriv.cpp @@ -19,27 +19,22 @@ #include -//#include - #include #include #include -#include #include -//#include -//#include #include using namespace android; -extern "C" { +namespace android { // private functions for conversion to/from AMessage -AMediaFormat* AMediaFormat_fromMsg(const void* data) { +AMediaFormat* AMediaFormat_fromMsg(sp *data) { ALOGV("private ctor"); AMediaFormat* mData = new AMediaFormat(); - mData->mFormat = *((sp*)data); + mData->mFormat = *data; if (mData->mFormat == NULL) { ALOGW("got NULL format"); mData->mFormat = new AMessage; @@ -47,10 +42,10 @@ AMediaFormat* AMediaFormat_fromMsg(const void* data) { return mData; } -void AMediaFormat_getFormat(const AMediaFormat* mData, void* dest) { - *((sp*)dest) = mData->mFormat; +void AMediaFormat_getFormat(const AMediaFormat* mData, sp *dest) { + *dest = mData->mFormat; } -} // extern "C" +} // namespace android diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp index 62b83cc7c4..4e6beca19d 100644 --- a/media/libmediaextractor/MediaBufferGroup.cpp +++ b/media/libmediaextractor/MediaBufferGroup.cpp @@ -44,12 +44,16 @@ struct MediaBufferGroup::InternalData { }; MediaBufferGroup::MediaBufferGroup(size_t growthLimit) - : mInternal(new InternalData()) { + : mWrapper(nullptr), mInternal(new InternalData()) { mInternal->mGrowthLimit = growthLimit; } MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit) - : mInternal(new InternalData()) { + : mWrapper(nullptr), mInternal(new InternalData()) { + init(buffers, buffer_size, growthLimit); +} + +void MediaBufferGroup::init(size_t buffers, size_t buffer_size, size_t growthLimit) { mInternal->mGrowthLimit = growthLimit; if (mInternal->mGrowthLimit > 0 && buffers > mInternal->mGrowthLimit) { diff --git a/media/libmediaextractor/include/media/DataSource.h b/media/libmediaextractor/include/media/DataSource.h index cb96ff5d4c..1f7a473786 100644 --- a/media/libmediaextractor/include/media/DataSource.h +++ b/media/libmediaextractor/include/media/DataSource.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h index 6c8d94ab98..d67ddbda41 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h @@ -18,6 +18,10 @@ #define MEDIA_BUFFER_BASE_H_ +#include +#include +#include + namespace android { class MediaBufferBase; @@ -77,6 +81,48 @@ public: virtual int remoteRefcount() const = 0; virtual ~MediaBufferBase() {}; + + CMediaBufferV3 *wrap() { + if (mWrapper) { + return mWrapper; + } + mWrapper = new CMediaBufferV3; + mWrapper->handle = this; + + mWrapper->release = [](void *handle) -> void { + ((MediaBufferBase*)handle)->release(); + }; + + mWrapper->data = [](void *handle) -> void * { + return ((MediaBufferBase*)handle)->data(); + }; + + mWrapper->size = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->size(); + }; + + mWrapper->set_range = [](void *handle, size_t offset, size_t length) -> void { + return ((MediaBufferBase*)handle)->set_range(offset, length); + }; + + mWrapper->meta_data = [](void *handle) -> AMediaFormat* { + if (((MediaBufferBase*)handle)->mFormat == nullptr) { + sp msg = new AMessage(); + ((MediaBufferBase*)handle)->mFormat = AMediaFormat_fromMsg(&msg); + } + return ((MediaBufferBase*)handle)->mFormat; + }; + + return mWrapper; + } +protected: + MediaBufferBase() { + mWrapper = nullptr; + mFormat = nullptr; + } +private: + CMediaBufferV3 *mWrapper; + AMediaFormat *mFormat; }; } // namespace android diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h index 75d5df746e..dc045569f5 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h @@ -20,6 +20,8 @@ #include +#include +#include #include #include #include @@ -57,12 +59,54 @@ public: // If buffer is nullptr, have acquire_buffer() check for remote release. virtual void signalBufferReturned(MediaBufferBase *buffer); + CMediaBufferGroupV3 *wrap() { + if (mWrapper) { + return mWrapper; + } + + mWrapper = new CMediaBufferGroupV3; + mWrapper->handle = this; + + mWrapper->add_buffer = [](void *handle, size_t size) -> void { + MediaBufferBase *buf = MediaBufferBase::Create(size); + ((MediaBufferGroup*)handle)->add_buffer(buf); + }; + + mWrapper->init = [](void *handle, + size_t buffers, size_t buffer_size, size_t growthLimit) -> bool { + ((MediaBufferGroup*)handle)->init(buffers, buffer_size, growthLimit); + // ((MediaBufferGroup*)handle)->mWrapper->init = nullptr; // enforce call-once + return true; + }; + + mWrapper->acquire_buffer = [](void *handle, + CMediaBufferV3 **buf, bool nonBlocking, size_t requestedSize) -> media_status_t { + MediaBufferBase *acquiredBuf = nullptr; + status_t err = ((MediaBufferGroup*)handle)->acquire_buffer( + &acquiredBuf, nonBlocking, requestedSize); + if (err == OK && acquiredBuf != nullptr) { + *buf = acquiredBuf->wrap(); + } else { + *buf = nullptr; + } + return translate_error(err); + }; + + mWrapper->has_buffers = [](void *handle) -> bool { + return ((MediaBufferGroup*)handle)->has_buffers(); + }; + + return mWrapper; + } + private: + CMediaBufferGroupV3 *mWrapper; struct InternalData; InternalData *mInternal; MediaBufferGroup(const MediaBufferGroup &); MediaBufferGroup &operator=(const MediaBufferGroup &); + void init(size_t buffers, size_t buffer_size, size_t growthLimit); }; } // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index ac9eb0b5bc..ea818ff59d 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -134,4 +134,57 @@ status_t MediaExtractorCUnwrapperV2::setMediaCas(const uint8_t* casToken, size_t return plugin->setMediaCas(plugin->data, casToken, size); } +// -------------------------------------------------------------------------------- +MediaExtractorCUnwrapperV3::MediaExtractorCUnwrapperV3(CMediaExtractorV3 *plugin) { + this->plugin = plugin; +} + +MediaExtractorCUnwrapperV3::~MediaExtractorCUnwrapperV3() { + plugin->free(plugin->data); + free(plugin); +} + +size_t MediaExtractorCUnwrapperV3::countTracks() { + return plugin->countTracks(plugin->data); +} + +MediaTrack *MediaExtractorCUnwrapperV3::getTrack(size_t index) { + return new MediaTrackCUnwrapperV3(plugin->getTrack(plugin->data, index)); +} + +status_t MediaExtractorCUnwrapperV3::getTrackMetaData( + MetaDataBase& meta, size_t index, uint32_t flags) { + sp msg = new AMessage(); + AMediaFormat *format = AMediaFormat_fromMsg(&msg); + media_status_t ret = plugin->getTrackMetaData(plugin->data, format, index, flags); + sp newMeta = new MetaData(); + convertMessageToMetaData(msg, newMeta); + delete format; + meta = *newMeta; + return reverse_translate_error(ret); +} + +status_t MediaExtractorCUnwrapperV3::getMetaData(MetaDataBase& meta) { + sp msg = new AMessage(); + AMediaFormat *format = AMediaFormat_fromMsg(&msg); + media_status_t ret = plugin->getMetaData(plugin->data, format); + sp newMeta = new MetaData(); + convertMessageToMetaData(msg, newMeta); + delete format; + meta = *newMeta; + return reverse_translate_error(ret); +} + +const char * MediaExtractorCUnwrapperV3::name() { + return plugin->name(plugin->data); +} + +uint32_t MediaExtractorCUnwrapperV3::flags() const { + return plugin->flags(plugin->data); +} + +status_t MediaExtractorCUnwrapperV3::setMediaCas(const uint8_t* casToken, size_t size) { + return plugin->setMediaCas(plugin->data, casToken, size); +} + } // namespace android diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 318c1eb94c..1f8ccb33fc 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -100,6 +100,12 @@ sp MediaExtractorFactory::CreateFromService( freeMeta(meta); } ex = ret != nullptr ? new MediaExtractorCUnwrapperV2(ret) : nullptr; + } else if (creatorVersion == 3) { + CMediaExtractorV3 *ret = ((CreatorFuncV3)creator)(source->wrap(), meta); + if (meta != nullptr && freeMeta != nullptr) { + freeMeta(meta); + } + ex = ret != nullptr ? new MediaExtractorCUnwrapperV3(ret) : nullptr; } ALOGV("Created an extractor '%s' with confidence %.2f", @@ -170,6 +176,9 @@ void *MediaExtractorFactory::sniff( } else if ((*it)->def.def_version == 2) { curCreator = (void*) (*it)->def.sniff.v2( source->wrap(), &newConfidence, &newMeta, &newFreeMeta); + } else if ((*it)->def.def_version == 3) { + curCreator = (void*) (*it)->def.sniff.v3( + source->wrap(), &newConfidence, &newMeta, &newFreeMeta); } if (curCreator) { @@ -199,7 +208,7 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, std::list> &pluginList) { // sanity check check struct version, uuid, name if (plugin->def.def_version == 0 - || plugin->def.def_version > EXTRACTORDEF_VERSION_CURRENT) { + || plugin->def.def_version > EXTRACTORDEF_VERSION_CURRENT + 1) { ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version); return; } diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 821605d207..daed5a0aa2 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -155,4 +155,76 @@ bool MediaTrackCUnwrapperV2::supportNonblockingRead() { return wrapper->supportsNonBlockingRead(wrapper->data); } +/* -------------- unwrapper v3 --------------- */ + +MediaTrackCUnwrapperV3::MediaTrackCUnwrapperV3(CMediaTrackV3 *cmediatrack3) { + wrapper = cmediatrack3; + bufferGroup = nullptr; +} + +MediaTrackCUnwrapperV3::~MediaTrackCUnwrapperV3() { + wrapper->free(wrapper->data); + free(wrapper); +} + +status_t MediaTrackCUnwrapperV3::start() { + if (bufferGroup == nullptr) { + bufferGroup = new MediaBufferGroup(); + } + return reverse_translate_error(wrapper->start(wrapper->data, bufferGroup->wrap())); +} + +status_t MediaTrackCUnwrapperV3::stop() { + return reverse_translate_error(wrapper->stop(wrapper->data)); +} + +status_t MediaTrackCUnwrapperV3::getFormat(MetaDataBase& format) { + sp msg = new AMessage(); + AMediaFormat *tmpFormat = AMediaFormat_fromMsg(&msg); + media_status_t ret = wrapper->getFormat(wrapper->data, tmpFormat); + sp newMeta = new MetaData(); + convertMessageToMetaData(msg, newMeta); + delete tmpFormat; + format = *newMeta; + return reverse_translate_error(ret); +} + +status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOptions *options) { + + uint32_t opts = 0; + + if (options && options->getNonBlocking()) { + opts |= CMediaTrackReadOptions::NONBLOCKING; + } + + int64_t seekPosition = 0; + MediaTrack::ReadOptions::SeekMode seekMode; + if (options && options->getSeekTo(&seekPosition, &seekMode)) { + opts |= SEEK; + opts |= (uint32_t) seekMode; + } + CMediaBufferV3 *buf = nullptr; + media_status_t ret = wrapper->read(wrapper->data, &buf, opts, seekPosition); + if (ret == AMEDIA_OK && buf != nullptr) { + *buffer = (MediaBufferBase*)buf->handle; + MetaDataBase &meta = (*buffer)->meta_data(); + AMediaFormat *format = buf->meta_data(buf->handle); + // only convert the keys we're actually expecting, as doing + // the full convertMessageToMetadata() for every buffer is + // too expensive + int64_t val; + if (format->mFormat->findInt64("timeUs", &val)) { + meta.setInt64(kKeyTime, val); + } + } else { + *buffer = nullptr; + } + + return reverse_translate_error(ret); +} + +bool MediaTrackCUnwrapperV3::supportNonblockingRead() { + return wrapper->supportsNonBlockingRead(wrapper->data); +} + } // namespace android diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h index 00f2dd3e39..71e11700f3 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h @@ -17,7 +17,6 @@ #ifndef SOFT_MPEG4_ENCODER_H_ #define SOFT_MPEG4_ENCODER_H_ -#include #include #include #include "mp4enc_api.h" diff --git a/media/libstagefright/include/media/stagefright/MediaExtractor.h b/media/libstagefright/include/media/stagefright/MediaExtractor.h index 71343d5ed7..6f3e57eb0a 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractor.h @@ -135,6 +135,22 @@ private: CMediaExtractorV2 *plugin; }; +class MediaExtractorCUnwrapperV3 : public MediaExtractorCUnwrapper { +public: + explicit MediaExtractorCUnwrapperV3(CMediaExtractorV3 *plugin); + virtual size_t countTracks(); + virtual MediaTrack *getTrack(size_t index); + virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0); + virtual status_t getMetaData(MetaDataBase& meta); + virtual const char * name(); + virtual uint32_t flags() const; + virtual status_t setMediaCas(const uint8_t* casToken, size_t size); +protected: + virtual ~MediaExtractorCUnwrapperV3(); +private: + CMediaExtractorV3 *plugin; +}; + } // namespace android #endif // MEDIA_EXTRACTOR_H_ diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index bf9725c1a5..bc140bfd33 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -327,6 +327,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts"; EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect"; EXPORT const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default"; EXPORT const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle"; +EXPORT const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME = "is-sync-frame"; EXPORT const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval"; EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language"; EXPORT const char* AMEDIAFORMAT_KEY_LATENCY = "latency"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 658cbac2ba..4be1928b5b 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -209,6 +209,7 @@ extern const char* AMEDIAFORMAT_KEY_FRAME_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_GENRE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ICC_PROFILE __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOOP __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LYRICIST __INTRODUCED_IN(29); -- GitLab From d5e180520a5b7aeb970bc796abc3ef8070d5174c Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 30 Nov 2018 14:55:45 -0800 Subject: [PATCH 0561/1530] Audio Policy: Parametrize encoded formats supported by device For various reasons, we need to store the list of audio formats supported by devices. Here we add support for this into DeviceDescriptor. We don't read them yet from the APM config file, but we will eventually. Also in this change, use the formats list for parametrizing the behavior of surround sound configuration. The special logic for AC3 and IEC69137 formats used to be hardcoded into 'filterSurroundFormats' function, now it is parametrized by the device descriptor information. Fix behavior of 'filterSurroundFormats' in 'manual' mode, where it was removing PCM formats from the list. Rename 'filterSurround...' functions to 'modifySurround...' since they can both add and remove items from the provided list. Bug: 67479735 Bug: 117602867 Test: compare A/B APM dumps while modifying Surround Sound settings; tested with a HDMI sink that doesn't support any surround, and with an AV receiver Change-Id: Ie46480d5e9519366b1f3553645c9ca20f64bdc80 --- .../include/DeviceDescriptor.h | 5 + .../src/DeviceDescriptor.cpp | 13 +- .../managerdefault/AudioPolicyManager.cpp | 132 ++++++------------ .../managerdefault/AudioPolicyManager.h | 6 +- 4 files changed, 61 insertions(+), 95 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index 83fb10c8e2..6f99bf3016 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -31,6 +31,8 @@ class DeviceDescriptor : public AudioPort, public AudioPortConfig public: // Note that empty name refers by convention to a generic device. explicit DeviceDescriptor(audio_devices_t type, const String8 &tagName = String8("")); + DeviceDescriptor(audio_devices_t type, const FormatVector &encodedFormats, + const String8 &tagName = String8("")); virtual ~DeviceDescriptor() {} @@ -38,6 +40,8 @@ public: audio_devices_t type() const { return mDeviceType; } + const FormatVector& encodedFormats() const { return mEncodedFormats; } + bool equals(const sp& other) const; // AudioPortConfig @@ -59,6 +63,7 @@ public: private: String8 mTagName; // Unique human readable identifier for a device port found in conf file. audio_devices_t mDeviceType; + FormatVector mEncodedFormats; audio_port_handle_t mId; friend class DeviceVector; diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index a9f7220504..9e5f9440d8 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -26,14 +26,25 @@ namespace android { DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const String8 &tagName) : + DeviceDescriptor(type, FormatVector{}, tagName) +{ +} + +DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &encodedFormats, + const String8 &tagName) : AudioPort(String8(""), AUDIO_PORT_TYPE_DEVICE, audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE), - mAddress(""), mTagName(tagName), mDeviceType(type), mId(0) + mAddress(""), mTagName(tagName), mDeviceType(type), mEncodedFormats(encodedFormats), mId(0) { if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { mAddress = String8("0"); } + /* FIXME: read from APM config file */ + if (type == AUDIO_DEVICE_OUT_HDMI) { + mEncodedFormats.add(AUDIO_FORMAT_AC3); + mEncodedFormats.add(AUDIO_FORMAT_IEC61937); + } } audio_port_handle_t DeviceDescriptor::getId() const diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 1b088bb477..c8dd362c48 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -4114,7 +4115,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d mpClientInterface->setParameters(output, String8(param)); free(param); } - updateAudioProfiles(device, output, profile->getAudioProfiles()); + updateAudioProfiles(devDesc, output, profile->getAudioProfiles()); if (!profile->hasValidAudioProfile()) { ALOGW("checkOutputsForDevice() missing param"); desc->close(); @@ -4322,7 +4323,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de mpClientInterface->setParameters(input, String8(param)); free(param); } - updateAudioProfiles(device, input, profile->getAudioProfiles()); + updateAudioProfiles(devDesc, input, profile->getAudioProfiles()); if (!profile->hasValidAudioProfile()) { ALOGW("checkInputsForDevice() direct input missing param"); desc->close(); @@ -5661,117 +5662,65 @@ void AudioPolicyManager::cleanUpForDevice(const sp& deviceDesc } } -// Modify the list of surround sound formats supported. -void AudioPolicyManager::filterSurroundFormats(FormatVector *formatsPtr) { - FormatVector &formats = *formatsPtr; - // TODO Set this based on Config properties. - const bool alwaysForceAC3 = true; +void AudioPolicyManager::modifySurroundFormats( + const sp& devDesc, FormatVector *formatsPtr) { + // Use a set because FormatVector is unsorted. + std::unordered_set enforcedSurround( + devDesc->encodedFormats().begin(), devDesc->encodedFormats().end()); audio_policy_forced_cfg_t forceUse = mEngine->getForceUse( AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND); ALOGD("%s: forced use = %d", __FUNCTION__, forceUse); + std::unordered_set formatSet; + if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL + || forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { + // Only copy non-surround formats to formatSet. + for (auto formatIter = formatsPtr->begin(); formatIter != formatsPtr->end(); ++formatIter) { + if (mConfig.getSurroundFormats().count(*formatIter) == 0 && + enforcedSurround.count(*formatIter) == 0) { + formatSet.insert(*formatIter); + } + } + } else { + formatSet.insert(formatsPtr->begin(), formatsPtr->end()); + } + formatsPtr->clear(); // Re-filled from the formatSet in the end. + // If MANUAL, keep the supported surround sound formats as current enabled ones. if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) { - formats.clear(); - for (auto it = mSurroundFormats.begin(); it != mSurroundFormats.end(); it++) { - formats.add(*it); + formatSet.insert(mSurroundFormats.begin(), mSurroundFormats.end()); + // Enable IEC61937 when in MANUAL mode if it's enforced for this device. + if (enforcedSurround.count(AUDIO_FORMAT_IEC61937) != 0) { + formatSet.insert(AUDIO_FORMAT_IEC61937); } - // Always enable IEC61937 when in MANUAL mode. - formats.add(AUDIO_FORMAT_IEC61937); } else { // NEVER, AUTO or ALWAYS - // Analyze original support for various formats. - bool supportsAC3 = false; - bool supportsOtherSurround = false; - bool supportsIEC61937 = false; mSurroundFormats.clear(); - for (ssize_t formatIndex = 0; formatIndex < (ssize_t)formats.size(); formatIndex++) { - audio_format_t format = formats[formatIndex]; - switch (format) { - case AUDIO_FORMAT_AC3: - supportsAC3 = true; - break; - case AUDIO_FORMAT_E_AC3: - case AUDIO_FORMAT_DTS: - case AUDIO_FORMAT_DTS_HD: - // If ALWAYS, remove all other surround formats here - // since we will add them later. - if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS) { - formats.removeAt(formatIndex); - formatIndex--; - } - supportsOtherSurround = true; - break; - case AUDIO_FORMAT_IEC61937: - supportsIEC61937 = true; - break; - default: - break; - } - } - // Modify formats based on surround preferences. - // If NEVER, remove support for surround formats. - if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { - if (supportsAC3 || supportsOtherSurround || supportsIEC61937) { - // Remove surround sound related formats. - for (size_t formatIndex = 0; formatIndex < formats.size(); ) { - audio_format_t format = formats[formatIndex]; - switch(format) { - case AUDIO_FORMAT_AC3: - case AUDIO_FORMAT_E_AC3: - case AUDIO_FORMAT_DTS: - case AUDIO_FORMAT_DTS_HD: - case AUDIO_FORMAT_IEC61937: - formats.removeAt(formatIndex); - break; - default: - formatIndex++; // keep it - break; - } - } - supportsAC3 = false; - supportsOtherSurround = false; - supportsIEC61937 = false; - } - } else { // AUTO or ALWAYS - // Most TVs support AC3 even if they do not report it in the EDID. - if ((alwaysForceAC3 || (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS)) - && !supportsAC3) { - formats.add(AUDIO_FORMAT_AC3); - supportsAC3 = true; - } - + if (forceUse != AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { // AUTO or ALWAYS // If ALWAYS, add support for raw surround formats if all are missing. // This assumes that if any of these formats are reported by the HAL // then the report is valid and should not be modified. if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS) { - formats.add(AUDIO_FORMAT_E_AC3); - formats.add(AUDIO_FORMAT_DTS); - formats.add(AUDIO_FORMAT_DTS_HD); - supportsOtherSurround = true; - } - - // Add support for IEC61937 if any raw surround supported. - // The HAL could do this but add it here, just in case. - if ((supportsAC3 || supportsOtherSurround) && !supportsIEC61937) { - formats.add(AUDIO_FORMAT_IEC61937); - supportsIEC61937 = true; + for (const auto& format : mConfig.getSurroundFormats()) { + formatSet.insert(format.first); + } } + formatSet.insert(enforcedSurround.begin(), enforcedSurround.end()); - // Add reported surround sound formats to enabled surround formats. - for (size_t formatIndex = 0; formatIndex < formats.size(); formatIndex++) { - audio_format_t format = formats[formatIndex]; + for (const auto& format : formatSet) { if (mConfig.getSurroundFormats().count(format) != 0) { mSurroundFormats.insert(format); } } } } + for (const auto& format : formatSet) { + formatsPtr->push(format); + } } -// Modify the list of channel masks supported. -void AudioPolicyManager::filterSurroundChannelMasks(ChannelsVector *channelMasksPtr) { +void AudioPolicyManager::modifySurroundChannelMasks(ChannelsVector *channelMasksPtr) { ChannelsVector &channelMasks = *channelMasksPtr; audio_policy_forced_cfg_t forceUse = mEngine->getForceUse( AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND); @@ -5806,11 +5755,12 @@ void AudioPolicyManager::filterSurroundChannelMasks(ChannelsVector *channelMasks } } -void AudioPolicyManager::updateAudioProfiles(audio_devices_t device, +void AudioPolicyManager::updateAudioProfiles(const sp& devDesc, audio_io_handle_t ioHandle, AudioProfileVector &profiles) { String8 reply; + audio_devices_t device = devDesc->type(); // Format MUST be checked first to update the list of AudioProfile if (profiles.hasDynamicFormat()) { @@ -5825,7 +5775,7 @@ void AudioPolicyManager::updateAudioProfiles(audio_devices_t device, } FormatVector formats = formatsFromString(reply.string()); if (device == AUDIO_DEVICE_OUT_HDMI) { - filterSurroundFormats(&formats); + modifySurroundFormats(devDesc, &formats); } profiles.setFormats(formats); } @@ -5858,7 +5808,7 @@ void AudioPolicyManager::updateAudioProfiles(audio_devices_t device, String8(AudioParameter::keyStreamSupportedChannels), reply) == NO_ERROR) { channelMasks = channelMasksFromString(reply.string()); if (device == AUDIO_DEVICE_OUT_HDMI) { - filterSurroundChannelMasks(&channelMasks); + modifySurroundChannelMasks(&channelMasks); } } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 22bfab0915..8f0e9229f6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -608,8 +608,8 @@ protected: std::unordered_set mSurroundFormats; private: // Add or remove AC3 DTS encodings based on user preferences. - void filterSurroundFormats(FormatVector *formatsPtr); - void filterSurroundChannelMasks(ChannelsVector *channelMasksPtr); + void modifySurroundFormats(const sp& devDesc, FormatVector *formatsPtr); + void modifySurroundChannelMasks(ChannelsVector *channelMasksPtr); // Support for Multi-Stream Decoder (MSD) module sp getMsdAudioInDevice() const; @@ -623,7 +623,7 @@ private: status_t setMsdPatch(audio_devices_t outputDevice = AUDIO_DEVICE_NONE); // If any, resolve any "dynamic" fields of an Audio Profiles collection - void updateAudioProfiles(audio_devices_t device, audio_io_handle_t ioHandle, + void updateAudioProfiles(const sp& devDesc, audio_io_handle_t ioHandle, AudioProfileVector &profiles); // Notify the policy client of any change of device state with AUDIO_IO_HANDLE_NONE, -- GitLab From 930bbf99ed405fc17c18383d425b46eadd01d805 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 11 Sep 2018 18:44:27 -0700 Subject: [PATCH 0562/1530] BufferProvider: Clear out mBuffer when changing upstream BufferProvider Test: QCOM internal stress test Bug: 111313529 Change-Id: Ieb7a61eddeb85528fabf7ad8f74b5104d50c2339 --- media/libaudioprocessing/BufferProviders.cpp | 23 ++++++++++++++++++- .../libmedia/include/media/BufferProviders.h | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp index 2d9e1cb16b..e06a1aaaee 100644 --- a/media/libaudioprocessing/BufferProviders.cpp +++ b/media/libaudioprocessing/BufferProviders.cpp @@ -63,7 +63,8 @@ CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize, CopyBufferProvider::~CopyBufferProvider() { - ALOGV("~CopyBufferProvider(%p)", this); + ALOGV("%s(%p) %zu %p %p", + __func__, this, mBuffer.frameCount, mTrackBufferProvider, mLocalBufferData); if (mBuffer.frameCount != 0) { mTrackBufferProvider->releaseBuffer(&mBuffer); } @@ -133,6 +134,16 @@ void CopyBufferProvider::reset() mConsumed = 0; } +void CopyBufferProvider::setBufferProvider(AudioBufferProvider *p) { + ALOGV("%s(%p): mTrackBufferProvider:%p mBuffer.frameCount:%zu", + __func__, p, mTrackBufferProvider, mBuffer.frameCount); + if (mTrackBufferProvider == p) { + return; + } + mBuffer.frameCount = 0; + PassthruBufferProvider::setBufferProvider(p); +} + DownmixerBufferProvider::DownmixerBufferProvider( audio_channel_mask_t inputChannelMask, audio_channel_mask_t outputChannelMask, audio_format_t format, @@ -528,6 +539,16 @@ void TimestretchBufferProvider::reset() mRemaining = 0; } +void TimestretchBufferProvider::setBufferProvider(AudioBufferProvider *p) { + ALOGV("%s(%p): mTrackBufferProvider:%p mBuffer.frameCount:%zu", + __func__, p, mTrackBufferProvider, mBuffer.frameCount); + if (mTrackBufferProvider == p) { + return; + } + mBuffer.frameCount = 0; + PassthruBufferProvider::setBufferProvider(p); +} + status_t TimestretchBufferProvider::setPlaybackRate(const AudioPlaybackRate &playbackRate) { mPlaybackRate = playbackRate; diff --git a/media/libmedia/include/media/BufferProviders.h b/media/libmedia/include/media/BufferProviders.h index d6a9cfbb99..2f1a91ca5a 100644 --- a/media/libmedia/include/media/BufferProviders.h +++ b/media/libmedia/include/media/BufferProviders.h @@ -78,6 +78,7 @@ public: // Overrides PassthruBufferProvider virtual void reset(); + void setBufferProvider(AudioBufferProvider *p) override; // this function should be supplied by the derived class. It converts // #frames in the *src pointer to the *dst pointer. It is public because @@ -186,6 +187,7 @@ public: // Overrides PassthruBufferProvider virtual void reset(); + void setBufferProvider(AudioBufferProvider *p) override; virtual status_t setPlaybackRate(const AudioPlaybackRate &playbackRate); -- GitLab From c3fca0e0d6430ea3eb2a3d710790f32f4a14f3d1 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 4 Dec 2018 17:08:04 -0800 Subject: [PATCH 0563/1530] nuplayer2: per-source DRM prepare/release impl Bug: 119675660 Test: MediaPlayer2DrmTest Change-Id: I6252f33560190854b3dde218e303edd0331f30ac --- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 127 ++++++++++-------- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 12 +- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index d4ffdfeb4f..1561850447 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -240,8 +240,7 @@ NuPlayer2::NuPlayer2(pid_t pid, uid_t uid, const sp &mediaClock) mVideoDecoderError(false), mPaused(false), mPausedByClient(true), - mPausedForBuffering(false), - mIsDrmProtected(false) { + mPausedForBuffering(false) { CHECK(mediaClock != NULL); clearFlushComplete(); } @@ -1630,7 +1629,7 @@ void NuPlayer2::onMessageReceived(const sp &msg) { case kWhatReleaseDrm: { - status_t status = onReleaseDrm(); + status_t status = onReleaseDrm(msg); sp response = new AMessage; response->setInt32("status", status); @@ -1672,7 +1671,7 @@ void NuPlayer2::onResume() { } void NuPlayer2::onStart(bool play) { - ALOGV("onStart: mCrypto: %p", mCrypto.get()); + ALOGV("onStart: mCrypto: %p", mCurrentSourceInfo.mCrypto.get()); if (!mSourceStarted) { mSourceStarted = true; @@ -1716,7 +1715,7 @@ void NuPlayer2::onStart(bool play) { && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f); // Modular DRM: Disabling audio offload if the source is protected - if (mOffloadAudio && mIsDrmProtected) { + if (mOffloadAudio && mCurrentSourceInfo.mIsDrmProtected) { mOffloadAudio = false; ALOGV("onStart: Disabling mOffloadAudio now that the source is protected."); } @@ -2010,7 +2009,7 @@ void NuPlayer2::determineAudioModeChange(const sp &audioFormat) { && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f); // Modular DRM: Disabling audio offload if the source is protected - if (canOffload && mIsDrmProtected) { + if (canOffload && mCurrentSourceInfo.mIsDrmProtected) { canOffload = false; ALOGV("determineAudioModeChange: Disabling mOffloadAudio b/c the source is protected."); } @@ -2117,10 +2116,11 @@ status_t NuPlayer2::instantiateDecoder( (*decoder)->init(); // Modular DRM - if (mIsDrmProtected) { - format->setObject("crypto", mCrypto); + if (mCurrentSourceInfo.mIsDrmProtected) { + format->setObject("crypto", mCurrentSourceInfo.mCrypto); ALOGV("instantiateDecoder: mCrypto: %p isSecure: %d", - mCrypto.get(), (mCurrentSourceInfo.mSourceFlags & Source::FLAG_SECURE) != 0); + mCurrentSourceInfo.mCrypto.get(), + (mCurrentSourceInfo.mSourceFlags & Source::FLAG_SECURE) != 0); } (*decoder)->configure(format); @@ -2506,12 +2506,8 @@ void NuPlayer2::performReset() { mRenderer.clear(); ++mRendererGeneration; - if (mCurrentSourceInfo.mSource != NULL) { - mCurrentSourceInfo.mSource->stop(); - - Mutex::Autolock autoLock(mSourceLock); - mCurrentSourceInfo.mSource.clear(); - } + resetSourceInfo(mCurrentSourceInfo); + resetSourceInfo(mNextSourceInfo); if (mDriver != NULL) { sp driver = mDriver.promote(); @@ -2525,14 +2521,6 @@ void NuPlayer2::performReset() { mResetting = false; mSourceStarted = false; - // Modular DRM - if (mCrypto != NULL) { - // decoders will be flushed before this so their mCrypto would go away on their own - // TODO change to ALOGV - ALOGD("performReset mCrypto: %p", mCrypto.get()); - mCrypto.clear(); - } - mIsDrmProtected = false; } void NuPlayer2::performPlayNextDataSource() { @@ -2586,15 +2574,6 @@ void NuPlayer2::performPlayNextDataSource() { addEndTimeMonitor(); - // Modular DRM - if (mCrypto != NULL) { - // decoders will be flushed before this so their mCrypto would go away on their own - // TODO change to ALOGV - ALOGD("performReset mCrypto: %p", mCrypto.get()); - mCrypto.clear(); - } - mIsDrmProtected = false; - if (mRenderer != NULL) { mRenderer->resume(); } @@ -3045,6 +3024,31 @@ const char *NuPlayer2::getDataSourceType() { } } +NuPlayer2::SourceInfo* NuPlayer2::getSourceInfoByIdInMsg(const sp &msg) { + int64_t srcId; + CHECK(msg->findInt64("srcId", &srcId)); + if (mCurrentSourceInfo.mSrcId == srcId) { + return &mCurrentSourceInfo; + } else if (mNextSourceInfo.mSrcId == srcId) { + return &mNextSourceInfo; + } else { + return NULL; + } +} + +void NuPlayer2::resetSourceInfo(NuPlayer2::SourceInfo &srcInfo) { + if (srcInfo.mSource != NULL) { + srcInfo.mSource->stop(); + + Mutex::Autolock autoLock(mSourceLock); + srcInfo.mSource.clear(); + } + // Modular DRM + ALOGD("performReset mCrypto: %p", srcInfo.mCrypto.get()); + srcInfo.mCrypto.clear(); + srcInfo.mIsDrmProtected = false; +} + // Modular DRM begin status_t NuPlayer2::prepareDrm( int64_t srcId, const uint8_t uuid[16], const Vector &drmSessionId) @@ -3100,8 +3104,15 @@ status_t NuPlayer2::onPrepareDrm(const sp &msg) ALOGD("onPrepareDrm "); status_t status = INVALID_OPERATION; - if (mCurrentSourceInfo.mSource == NULL) { - ALOGE("onPrepareDrm: No source. onPrepareDrm failed with %d.", status); + SourceInfo *srcInfo = getSourceInfoByIdInMsg(msg); + if (srcInfo == NULL) { + return status; + } + + int64_t srcId = srcInfo->mSrcId; + if (srcInfo->mSource == NULL) { + ALOGE("onPrepareDrm: srcInfo(%lld) No source. onPrepareDrm failed with %d.", + (long long)srcId, status); return status; } @@ -3113,42 +3124,50 @@ status_t NuPlayer2::onPrepareDrm(const sp &msg) status = OK; sp crypto = NULL; - status = mCurrentSourceInfo.mSource->prepareDrm(uuid, *drmSessionId, &crypto); + status = srcInfo->mSource->prepareDrm(uuid, *drmSessionId, &crypto); if (crypto == NULL) { - ALOGE("onPrepareDrm: mCurrentSourceInfo.mSource->prepareDrm failed. status: %d", status); + ALOGE("onPrepareDrm: srcInfo(%lld).mSource->prepareDrm failed. status: %d", + (long long)srcId, status); return status; } - ALOGV("onPrepareDrm: mCurrentSourceInfo.mSource->prepareDrm succeeded"); + ALOGV("onPrepareDrm: srcInfo(%lld).mSource->prepareDrm succeeded", (long long)srcId); - if (mCrypto != NULL) { - ALOGE("onPrepareDrm: Unexpected. Already having mCrypto: %p", mCrypto.get()); - mCrypto.clear(); + if (srcInfo->mCrypto != NULL) { + ALOGE("onPrepareDrm: srcInfo(%lld) Unexpected. Already having mCrypto: %p", + (long long)srcId, srcInfo->mCrypto.get()); + srcInfo->mCrypto.clear(); } - mCrypto = crypto; - mIsDrmProtected = true; + srcInfo->mCrypto = crypto; + srcInfo->mIsDrmProtected = true; // TODO change to ALOGV - ALOGD("onPrepareDrm: mCrypto: %p", mCrypto.get()); + ALOGD("onPrepareDrm: mCrypto: %p", srcInfo->mCrypto.get()); return status; } -status_t NuPlayer2::onReleaseDrm() +status_t NuPlayer2::onReleaseDrm(const sp &msg) { // TODO change to ALOGV ALOGD("onReleaseDrm "); + SourceInfo *srcInfo = getSourceInfoByIdInMsg(msg);; + if (srcInfo == NULL) { + return INVALID_OPERATION; + } - if (!mIsDrmProtected) { - ALOGW("onReleaseDrm: Unexpected. mIsDrmProtected is already false."); + int64_t srcId = srcInfo->mSrcId; + if (!srcInfo->mIsDrmProtected) { + ALOGW("onReleaseDrm: srcInfo(%lld) Unexpected. mIsDrmProtected is already false.", + (long long)srcId); } - mIsDrmProtected = false; + srcInfo->mIsDrmProtected = false; status_t status; - if (mCrypto != NULL) { + if (srcInfo->mCrypto != NULL) { // notifying the source first before removing crypto from codec - if (mCurrentSourceInfo.mSource != NULL) { - mCurrentSourceInfo.mSource->releaseDrm(); + if (srcInfo->mSource != NULL) { + srcInfo->mSource->releaseDrm(); } status=OK; @@ -3169,9 +3188,9 @@ status_t NuPlayer2::onReleaseDrm() } // TODO change to ALOGV - ALOGD("onReleaseDrm: mCrypto: %p", mCrypto.get()); - mCrypto.clear(); - } else { // mCrypto == NULL + ALOGD("onReleaseDrm: mCrypto: %p", srcInfo->mCrypto.get()); + srcInfo->mCrypto.clear(); + } else { // srcInfo->mCrypto == NULL ALOGE("onReleaseDrm: Unexpected. There is no crypto."); status = INVALID_OPERATION; } @@ -3243,11 +3262,13 @@ NuPlayer2::SourceInfo::SourceInfo() NuPlayer2::SourceInfo & NuPlayer2::SourceInfo::operator=(const NuPlayer2::SourceInfo &other) { mSource = other.mSource; + mCrypto = other.mCrypto; mDataSourceType = (DATA_SOURCE_TYPE)other.mDataSourceType; mSrcId = other.mSrcId; mSourceFlags = other.mSourceFlags; mStartTimeUs = other.mStartTimeUs; mEndTimeUs = other.mEndTimeUs; + mIsDrmProtected = other.mIsDrmProtected; return *this; } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index 93f9f007f2..e9b5f11635 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -178,6 +178,9 @@ private: uint32_t mSourceFlags; int64_t mStartTimeUs; int64_t mEndTimeUs; + // Modular DRM + sp mCrypto; + bool mIsDrmProtected = false; }; wp mDriver; @@ -269,10 +272,6 @@ private: // Pause state as requested by source (internally) due to buffering bool mPausedForBuffering; - // Modular DRM - sp mCrypto; - bool mIsDrmProtected; - inline const sp &getDecoder(bool audio) { return audio ? mAudioDecoder : mVideoDecoder; } @@ -351,7 +350,10 @@ private: void writeTrackInfo(PlayerMessage* reply, const sp& format) const; status_t onPrepareDrm(const sp &msg); - status_t onReleaseDrm(); + status_t onReleaseDrm(const sp &msg); + + SourceInfo* getSourceInfoByIdInMsg(const sp &msg); + void resetSourceInfo(SourceInfo &srcInfo); DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2); }; -- GitLab From 2a4c2c9539f79e78cd520cd5bd4056a08bb992d1 Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Thu, 6 Dec 2018 12:13:26 -0800 Subject: [PATCH 0564/1530] Remove CFI diagnostics. Remove diagnostics mode from CFI across the board. This should reduce performance overhead and also allows the minimal runtime to work when other ubsan sanitizers are enabled. CFI stack dumps should include a CFI related function, so it should be apparent when a crash is CFI-related. Bug: 117417735 Test: make -j Change-Id: I4f5ecde8fa2aa08c0f95f92c4c8fff6b99e74c34 --- media/codec2/components/base/Android.bp | 3 --- media/codec2/components/cmds/Android.bp | 3 --- media/codec2/faultinjection/Android.bp | 3 --- media/codec2/sfplugin/Android.bp | 3 --- media/codec2/sfplugin/utils/Android.bp | 3 --- media/ndk/Android.bp | 3 --- 6 files changed, 18 deletions(-) diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp index 06bb81af40..d02f541555 100644 --- a/media/codec2/components/base/Android.bp +++ b/media/codec2/components/base/Android.bp @@ -31,9 +31,6 @@ cc_library_shared { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/codec2/components/cmds/Android.bp b/media/codec2/components/cmds/Android.bp index 994dfee090..6b0977b0a8 100644 --- a/media/codec2/components/cmds/Android.bp +++ b/media/codec2/components/cmds/Android.bp @@ -33,8 +33,5 @@ cc_binary { "unsigned-integer-overflow", "signed-integer-overflow", ], - diag: { - cfi: true, - }, }, } diff --git a/media/codec2/faultinjection/Android.bp b/media/codec2/faultinjection/Android.bp index ade638f6f0..a0ad3cef82 100644 --- a/media/codec2/faultinjection/Android.bp +++ b/media/codec2/faultinjection/Android.bp @@ -22,9 +22,6 @@ cc_library_shared { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index f5578726cb..2870d391eb 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -54,8 +54,5 @@ cc_library_shared { "unsigned-integer-overflow", "signed-integer-overflow", ], - diag: { - cfi: true, - }, }, } diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp index bd4983c25c..eb6c3e917e 100644 --- a/media/codec2/sfplugin/utils/Android.bp +++ b/media/codec2/sfplugin/utils/Android.bp @@ -32,8 +32,5 @@ cc_library_shared { "unsigned-integer-overflow", "signed-integer-overflow", ], - diag: { - cfi: true, - }, }, } diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 541ba3eeee..8267ba528d 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -149,9 +149,6 @@ cc_library { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, } -- GitLab From df628924e691e01da190c1ac5db173304442e54a Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 6 Dec 2018 21:45:51 +0000 Subject: [PATCH 0565/1530] Revert "audio policy: concurrent capture" This reverts commit 4c1ef4b64d113be6ee1106375bd10cdc643e80d8. Reason for revert: b/120588242 Bug: 120588242 Change-Id: Iac41f371cb739c54d5ce519232688bebe2285c72 Test: Launch QSB and capture from mic icon --- media/libaudioclient/AudioSystem.cpp | 4 +- media/libaudioclient/IAudioPolicyService.cpp | 9 +- .../include/media/AudioSystem.h | 3 +- .../include/media/IAudioPolicyService.h | 3 +- services/audioflinger/Threads.cpp | 23 +- services/audiopolicy/AudioPolicyInterface.h | 18 +- services/audiopolicy/common/include/policy.h | 25 ++ .../include/AudioInputDescriptor.h | 5 +- .../include/ClientDescriptor.h | 11 +- .../managerdefinitions/include/IOProfile.h | 2 +- .../src/AudioInputDescriptor.cpp | 55 +++-- .../managerdefault/AudioPolicyManager.cpp | 216 ++++++++++++++---- .../managerdefault/AudioPolicyManager.h | 10 +- .../service/AudioPolicyInterfaceImpl.cpp | 107 +++++---- .../service/AudioPolicyService.cpp | 117 +--------- .../audiopolicy/service/AudioPolicyService.h | 11 +- 16 files changed, 357 insertions(+), 262 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index efe65bb299..ec36ed7dc8 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -917,11 +917,11 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, config, flags, selectedDeviceId, portId); } -status_t AudioSystem::startInput(audio_port_handle_t portId) +status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->startInput(portId); + return aps->startInput(portId, silenced); } status_t AudioSystem::stopInput(audio_port_handle_t portId) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 7f2e5e5805..a406658a16 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -329,13 +329,16 @@ public: return NO_ERROR; } - virtual status_t startInput(audio_port_handle_t portId) + virtual status_t startInput(audio_port_handle_t portId, + bool *silenced) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(portId); + data.writeInt32(*silenced ? 1 : 0); remote()->transact(START_INPUT, data, &reply); status_t status = static_cast (reply.readInt32()); + *silenced = reply.readInt32() == 1; return status; } @@ -1216,8 +1219,10 @@ status_t BnAudioPolicyService::onTransact( case START_INPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_port_handle_t portId = static_cast (data.readInt32()); - status_t status = startInput(portId); + bool silenced = data.readInt32() == 1; + status_t status = startInput(portId, &silenced); reply->writeInt32(static_cast (status)); + reply->writeInt32(silenced ? 1 : 0); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index ca1879fc4e..76a79c9536 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -241,7 +241,8 @@ public: audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - static status_t startInput(audio_port_handle_t portId); + static status_t startInput(audio_port_handle_t portId, + bool *silenced); static status_t stopInput(audio_port_handle_t portId); static void releaseInput(audio_port_handle_t portId); static status_t initStreamVolume(audio_stream_type_t stream, diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index e5fcfb5c2f..a246df68ea 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -78,7 +78,8 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId) = 0; - virtual status_t startInput(audio_port_handle_t portId) = 0; + virtual status_t startInput(audio_port_handle_t portId, + bool *silenced) = 0; virtual status_t stopInput(audio_port_handle_t portId) = 0; virtual void releaseInput(audio_port_handle_t portId) = 0; virtual status_t initStreamVolume(audio_stream_type_t stream, diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index e70e567099..45d3a065aa 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7367,7 +7367,8 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac status_t status = NO_ERROR; if (recordTrack->isExternalTrack()) { mLock.unlock(); - status = AudioSystem::startInput(recordTrack->portId()); + bool silenced; + status = AudioSystem::startInput(recordTrack->portId(), &silenced); mLock.lock(); if (recordTrack->isInvalid()) { recordTrack->clearSyncStartEvent(); @@ -7395,6 +7396,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac recordTrack->clearSyncStartEvent(); return status; } + recordTrack->setSilenced(silenced); } // Catch up with current buffer indices if thread is already running. // This is what makes a new client discard all buffered data. If the track's mRsmpInFront @@ -8344,10 +8346,11 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return BAD_VALUE; } + bool silenced = false; if (isOutput()) { ret = AudioSystem::startOutput(portId); } else { - ret = AudioSystem::startInput(portId); + ret = AudioSystem::startInput(portId, &silenced); } Mutex::Autolock _l(mLock); @@ -8368,21 +8371,21 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return PERMISSION_DENIED; } - // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? - sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, - isOutput(), client.clientUid, client.clientPid, portId); - if (isOutput()) { // force volume update when a new track is added mHalVolFloat = -1.0f; - } else if (!track->isSilenced_l()) { - for (const sp &t : mActiveTracks) { - if (t->isSilenced_l() && t->uid() != client.clientUid) - t->invalidate(); + } else if (!silenced) { + for (const sp &track : mActiveTracks) { + if (track->isSilenced_l() && track->uid() != client.clientUid) + track->invalidate(); } } + // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? + sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, + isOutput(), client.clientUid, client.clientPid, portId); + track->setSilenced_l(silenced); mActiveTracks.add(track); sp chain = getEffectChain_l(mSessionId); if (chain != 0) { diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 3fb505d7b2..3c3a82b38f 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -65,6 +65,20 @@ public: API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path } input_type_t; + enum { + API_INPUT_CONCURRENCY_NONE = 0, + API_INPUT_CONCURRENCY_CALL = (1 << 0), // Concurrency with a call + API_INPUT_CONCURRENCY_CAPTURE = (1 << 1), // Concurrency with another capture + API_INPUT_CONCURRENCY_HOTWORD = (1 << 2), // Concurrency with a hotword + API_INPUT_CONCURRENCY_PREEMPT = (1 << 3), // pre-empted someone + // NB: preempt is marked on a successful return, others are on failing calls + API_INPUT_CONCURRENCY_LAST = (1 << 4), + + API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_LAST - 1), + }; + + typedef uint32_t concurrency_type__mask_t; + public: virtual ~AudioPolicyInterface() {} // @@ -127,7 +141,9 @@ public: input_type_t *inputType, audio_port_handle_t *portId) = 0; // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId) = 0; + virtual status_t startInput(audio_port_handle_t portId, + bool silenced, + concurrency_type__mask_t *concurrency) = 0; // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId) = 0; // releases the input. diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 30b0044440..9bd68e1ffa 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -31,6 +31,14 @@ static const audio_format_t gDynamicFormat = AUDIO_FORMAT_DEFAULT; // Do not limit channel count otherwise #define MAX_MIXER_CHANNEL_COUNT FCC_8 +/** + * A device mask for all audio input devices that are considered "virtual" when evaluating + * active inputs in getActiveInputs() + */ +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX|\ + AUDIO_DEVICE_IN_BUS|AUDIO_DEVICE_IN_FM_TUNER) + + /** * A device mask for all audio input and output devices where matching inputs/outputs on device * type alone is not enough: the address must match too @@ -59,6 +67,23 @@ static inline bool is_state_in_call(int state) return (state == AUDIO_MODE_IN_CALL) || (state == AUDIO_MODE_IN_COMMUNICATION); } +/** + * Check if the input device given is considered as a virtual device. + * + * @param[in] device to consider + * + * @return true if the device is a virtual one, false otherwise. + */ +static inline bool is_virtual_input_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + /** * Check whether the device type is one * where addresses are used to distinguish between one connected device and another diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 9f8b8c0580..6e4c044bb5 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -58,8 +58,9 @@ public: void clearPreemptedSessions(); bool isActive() const { return mGlobalActiveCount > 0; } bool isSourceActive(audio_source_t source) const; - audio_source_t source() const; + audio_source_t inputSource(bool activeOnly = false) const; bool isSoundTrigger() const; + audio_source_t getHighestPrioritySource(bool activeOnly) const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } @@ -120,7 +121,7 @@ public: * Only considers inputs from physical devices (e.g. main mic, headset mic) when * ignoreVirtualInputs is true. */ - Vector > getActiveInputs(); + Vector > getActiveInputs(bool ignoreVirtualInputs = true); audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 986d109122..030bf4bd31 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -107,7 +106,7 @@ public: audio_port_handle_t preferredDeviceId, audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mAppState(APP_STATE_IDLE) {} + mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {} ~RecordClientDescriptor() override = default; using ClientDescriptor::dump; @@ -116,16 +115,14 @@ public: audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } bool isSoundTrigger() const { return mIsSoundTrigger; } - void setAppState(app_state_t appState) { mAppState = appState; } - app_state_t appState() { return mAppState; } - bool isSilenced() const { return mAppState == APP_STATE_IDLE; } + void setSilenced(bool silenced) { mSilenced = silenced; } + bool isSilenced() const { return mSilenced; } private: const audio_source_t mSource; const audio_input_flags_t mFlags; const bool mIsSoundTrigger; - app_state_t mAppState; - + bool mSilenced; }; class SourceClientDescriptor: public TrackClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index 8ff823860f..eb329597da 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -35,7 +35,7 @@ class IOProfile : public AudioPort public: IOProfile(const String8 &name, audio_port_role_t role) : AudioPort(name, AUDIO_PORT_TYPE_MIX, role), - maxOpenCount(1), + maxOpenCount((role == AUDIO_PORT_ROLE_SOURCE) ? 1 : 0), curOpenCount(0), maxActiveCount(1), curActiveCount(0) {} diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 559274f8c3..1f29874b5c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -53,32 +53,9 @@ audio_port_handle_t AudioInputDescriptor::getId() const return mId; } -audio_source_t AudioInputDescriptor::source() const +audio_source_t AudioInputDescriptor::inputSource(bool activeOnly) const { - audio_source_t source = AUDIO_SOURCE_DEFAULT; - - for (bool activeOnly : { true, false }) { - int32_t topPriority = -1; - app_state_t topState = APP_STATE_IDLE; - for (const auto &client : getClientIterable()) { - if (activeOnly && !client->active()) { - continue; - } - app_state_t curState = client->appState(); - if (curState >= topState) { - int32_t curPriority = source_priority(client->source()); - if (curPriority > topPriority) { - source = client->source(); - topPriority = curPriority; - } - topState = curState; - } - } - if (source != AUDIO_SOURCE_DEFAULT) { - break; - } - } - return source; + return getHighestPrioritySource(activeOnly); } void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, @@ -99,7 +76,7 @@ void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig dstConfig->type = AUDIO_PORT_TYPE_MIX; dstConfig->ext.mix.hw_module = getModuleHandle(); dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.source = source(); + dstConfig->ext.mix.usecase.source = inputSource(); } void AudioInputDescriptor::toAudioPort(struct audio_port *port) const @@ -148,6 +125,24 @@ bool AudioInputDescriptor::isSourceActive(audio_source_t source) const return false; } +audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const +{ + audio_source_t source = AUDIO_SOURCE_DEFAULT; + int32_t priority = -1; + + for (const auto &client : getClientIterable()) { + if (activeOnly && !client->active() ) { + continue; + } + int32_t curPriority = source_priority(client->source()); + if (curPriority > priority) { + priority = curPriority; + source = client->source(); + } + } + return source; +} + bool AudioInputDescriptor::isSoundTrigger() const { // sound trigger and non sound trigger clients are not mixed on a given input // so check only first client @@ -229,7 +224,7 @@ status_t AudioInputDescriptor::open(const audio_config_t *config, status_t AudioInputDescriptor::start() { - if (!isActive()) { + if (mGlobalActiveCount == 1) { if (!mProfile->canStartNewIo()) { ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount); return INVALID_OPERATION; @@ -393,13 +388,15 @@ uint32_t AudioInputCollection::activeInputsCountOnDevices(audio_devices_t device return count; } -Vector > AudioInputCollection::getActiveInputs() +Vector > AudioInputCollection::getActiveInputs(bool ignoreVirtualInputs) { Vector > activeInputs; for (size_t i = 0; i < size(); i++) { const sp inputDescriptor = valueAt(i); - if (inputDescriptor->isActive()) { + if ((inputDescriptor->isActive()) + && (!ignoreVirtualInputs || + !is_virtual_input_device(inputDescriptor->mDevice))) { activeInputs.add(inputDescriptor); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 1b088bb477..6ec6a767dd 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1876,38 +1876,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, } if (!profile->canOpenNewIo()) { - for (size_t i = 0; i < mInputs.size(); ) { - sp desc = mInputs.valueAt(i); - if (desc->mProfile != profile) { - continue; - } - // if sound trigger, reuse input if used by other sound trigger on same session - // else - // reuse input if active client app is not in IDLE state - // - RecordClientVector clients = desc->clientsList(); - bool doClose = false; - for (const auto& client : clients) { - if (isSoundTrigger != client->isSoundTrigger()) { - continue; - } - if (client->isSoundTrigger()) { - if (session == client->session()) { - return desc->mIoHandle; - } - continue; - } - if (client->active() && client->appState() != APP_STATE_IDLE) { - return desc->mIoHandle; - } - doClose = true; - } - if (doClose) { - closeInput(desc->mIoHandle); - } else { - i++; - } - } + return AUDIO_IO_HANDLE_NONE; } sp inputDesc = new AudioInputDescriptor(profile, mpClientInterface); @@ -1948,8 +1917,55 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, return input; } -status_t AudioPolicyManager::startInput(audio_port_handle_t portId) +//static +bool AudioPolicyManager::isConcurrentSource(audio_source_t source) +{ + return (source == AUDIO_SOURCE_HOTWORD) || + (source == AUDIO_SOURCE_VOICE_RECOGNITION) || + (source == AUDIO_SOURCE_FM_TUNER); +} + +// FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537. +bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { + if (!mHasComputedSoundTriggerSupportsConcurrentCapture) { + bool soundTriggerSupportsConcurrentCapture = false; + unsigned int numModules = 0; + struct sound_trigger_module_descriptor* nModules = NULL; + + status_t status = SoundTrigger::listModules(nModules, &numModules); + if (status == NO_ERROR && numModules != 0) { + nModules = (struct sound_trigger_module_descriptor*) calloc( + numModules, sizeof(struct sound_trigger_module_descriptor)); + if (nModules == NULL) { + // We failed to malloc the buffer, so just say no for now, and hope that we have more + // ram the next time this function is called. + ALOGE("Failed to allocate buffer for module descriptors"); + return false; + } + + status = SoundTrigger::listModules(nModules, &numModules); + if (status == NO_ERROR) { + soundTriggerSupportsConcurrentCapture = true; + for (size_t i = 0; i < numModules; ++i) { + soundTriggerSupportsConcurrentCapture &= + nModules[i].properties.concurrent_capture; + } + } + free(nModules); + } + mSoundTriggerSupportsConcurrentCapture = soundTriggerSupportsConcurrentCapture; + mHasComputedSoundTriggerSupportsConcurrentCapture = true; + } + return mSoundTriggerSupportsConcurrentCapture; +} + + +status_t AudioPolicyManager::startInput(audio_port_handle_t portId, + bool silenced, + concurrency_type__mask_t *concurrency) { + *concurrency = API_INPUT_CONCURRENCY_NONE; + ALOGV("%s portId %d", __FUNCTION__, portId); sp inputDesc = mInputs.getInputForClient(portId); @@ -1966,16 +1982,106 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) audio_session_t session = client->session(); - ALOGV("%s input:%d, session:%d)", __FUNCTION__, input, session); + ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)", + __FUNCTION__, input, session, silenced, *concurrency); - Vector> activeInputs = mInputs.getActiveInputs(); + if (!is_virtual_input_device(inputDesc->mDevice)) { + if (mCallTxPatch != 0 && + inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) { + ALOGW("startInput(%d) failed: call in progress", input); + *concurrency |= API_INPUT_CONCURRENCY_CALL; + return INVALID_OPERATION; + } - status_t status = inputDesc->start(); - if (status != NO_ERROR) { - return status; + Vector> activeInputs = mInputs.getActiveInputs(); + + // If a UID is idle and records silence and another not silenced recording starts + // from another UID (idle or active) we stop the current idle UID recording in + // favor of the new one - "There can be only one" TM + if (!silenced) { + for (const auto& activeDesc : activeInputs) { + if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && + activeDesc->getId() == inputDesc->getId()) { + continue; + } + + RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/); + for (const auto& activeClient : activeClients) { + if (activeClient->isSilenced()) { + closeClient(activeClient->portId()); + ALOGV("%s client %d stopping silenced client %d", __FUNCTION__, + portId, activeClient->portId()); + activeInputs = mInputs.getActiveInputs(); + } + } + } + } + + for (const auto& activeDesc : activeInputs) { + if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && + activeDesc->getId() == inputDesc->getId()) { + continue; + } + + audio_source_t activeSource = activeDesc->inputSource(true); + if (client->source() == AUDIO_SOURCE_HOTWORD) { + if (activeSource == AUDIO_SOURCE_HOTWORD) { + if (activeDesc->hasPreemptedSession(session)) { + ALOGW("%s input %d failed for HOTWORD: " + "other input %d already started for HOTWORD", __FUNCTION__, + input, activeDesc->mIoHandle); + *concurrency |= API_INPUT_CONCURRENCY_HOTWORD; + return INVALID_OPERATION; + } + } else { + ALOGV("%s input %d failed for HOTWORD: other input %d already started", + __FUNCTION__, input, activeDesc->mIoHandle); + *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; + return INVALID_OPERATION; + } + } else { + if (activeSource != AUDIO_SOURCE_HOTWORD) { + ALOGW("%s input %d failed: other input %d already started", __FUNCTION__, + input, activeDesc->mIoHandle); + *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; + return INVALID_OPERATION; + } + } + } + + // We only need to check if the sound trigger session supports concurrent capture if the + // input is also a sound trigger input. Otherwise, we should preempt any hotword stream + // that's running. + const bool allowConcurrentWithSoundTrigger = + inputDesc->isSoundTrigger() ? soundTriggerSupportsConcurrentCapture() : false; + + // if capture is allowed, preempt currently active HOTWORD captures + for (const auto& activeDesc : activeInputs) { + if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) { + continue; + } + RecordClientVector activeHotwordClients = + activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD); + if (activeHotwordClients.size() > 0) { + SortedVector sessions = activeDesc->getPreemptedSessions(); + + for (const auto& activeClient : activeHotwordClients) { + *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; + sessions.add(activeClient->session()); + closeClient(activeClient->portId()); + ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__, + input, activeDesc->mIoHandle); + } + + inputDesc->setPreemptedSessions(sessions); + } + } } - // increment activity count before calling getNewInputDevice() below as only active sessions + // Make sure we start with the correct silence state + client->setSilenced(silenced); + + // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection inputDesc->setClientActive(client, true); @@ -1984,6 +2090,12 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) audio_devices_t device = getNewInputDevice(inputDesc); setInputDevice(input, device, true /* force */); + status_t status = inputDesc->start(); + if (status != NO_ERROR) { + inputDesc->setClientActive(client, false); + return status; + } + if (inputDesc->activeCount() == 1) { // if input maps to a dynamic policy with an activity listener, notify of state change if ((inputDesc->mPolicyMix != NULL) @@ -3232,7 +3344,7 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) SortedVector inputsToClose; for (size_t i = 0; i < mInputs.size(); i++) { sp inputDesc = mInputs.valueAt(i); - if (affectedSources.indexOf(inputDesc->source()) >= 0) { + if (affectedSources.indexOf(inputDesc->inputSource()) >= 0) { inputsToClose.add(inputDesc->mIoHandle); } } @@ -3609,15 +3721,16 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { Vector > activeInputs = mInputs.getActiveInputs(); + bool silenced = state == APP_STATE_IDLE; - ALOGV("%s(uid:%d, state:%d)", __func__, uid, state); + ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced); for (size_t i = 0; i < activeInputs.size(); i++) { sp activeDesc = activeInputs[i]; RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); for (const auto& client : clients) { if (uid == client->uid()) { - client->setAppState(state); + client->setSilenced(silenced); } } } @@ -3727,7 +3840,8 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mBeaconMuted(false), mTtsOutputAvailable(false), mMasterMono(false), - mMusicEffectOutput(AUDIO_IO_HANDLE_NONE) + mMusicEffectOutput(AUDIO_IO_HANDLE_NONE), + mHasComputedSoundTriggerSupportsConcurrentCapture(false) { } @@ -4780,7 +4894,7 @@ audio_devices_t AudioPolicyManager::getNewInputDevice(const spsource(); + audio_source_t source = inputDesc->getHighestPrioritySource(true /*activeOnly*/); if (source == AUDIO_SOURCE_DEFAULT && isInCall()) { source = AUDIO_SOURCE_VOICE_COMMUNICATION; } @@ -5119,6 +5233,20 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou } installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } + + // inform all input as well + for (size_t i = 0; i < mInputs.size(); i++) { + const sp inputDescriptor = mInputs.valueAt(i); + if (!is_virtual_input_device(inputDescriptor->mDevice)) { + AudioParameter inputCmd = AudioParameter(); + ALOGV("%s: inform input %d of device:%d", __func__, + inputDescriptor->mIoHandle, device); + inputCmd.addInt(String8(AudioParameter::keyRouting),device); + mpClientInterface->setParameters(inputDescriptor->mIoHandle, + inputCmd.toString(), + delayMs); + } + } } // update stream volumes according to new device diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 22bfab0915..0436b1d0f8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -134,7 +134,9 @@ public: audio_port_handle_t *portId); // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId); + virtual status_t startInput(audio_port_handle_t portId, + bool silenced, + concurrency_type__mask_t *concurrency); // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId); @@ -537,6 +539,8 @@ protected: void clearAudioSources(uid_t uid); + static bool isConcurrentSource(audio_source_t source); + static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); @@ -700,6 +704,10 @@ private: int delayMs, uid_t uid, sp *patchDescPtr); + + bool soundTriggerSupportsConcurrentCapture(); + bool mSoundTriggerSupportsConcurrentCapture; + bool mHasComputedSoundTriggerSupportsConcurrentCapture; }; }; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index c2ce754616..59c8f10075 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -454,6 +454,23 @@ static std::string audioSourceString(audio_source_t value) { return rawbuffer; } +static std::string audioConcurrencyString( + AudioPolicyInterface::concurrency_type__mask_t concurrency) +{ + char buffer[64]; // oversized + if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL) { + snprintf(buffer, sizeof(buffer), "%s%s%s%s", + (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL)? ",call":"", + (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE)? ",capture":"", + (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_HOTWORD)? ",hotword":"", + (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_PREEMPT)? ",preempt":""); + } else { + snprintf(buffer, sizeof(buffer), ",none"); + } + + return &buffer[1]; +} + std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t portId) { std::string typeStr; struct audio_port port = {}; @@ -465,7 +482,7 @@ std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t po return typeStr; } -status_t AudioPolicyService::startInput(audio_port_handle_t portId) +status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -488,16 +505,17 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) return PERMISSION_DENIED; } - Mutex::Autolock _l(mLock); + // If UID inactive it records silence until becoming active + *silenced = !mUidPolicy->isUidActive(client->uid) && !client->isVirtualDevice; - client->active = true; - client->startTimeNs = systemTime(); - updateUidStates_l(); + Mutex::Autolock _l(mLock); + AudioPolicyInterface::concurrency_type__mask_t concurrency = + AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE; status_t status; { AutoCallerClear acc; - status = mAudioPolicyManager->startInput(portId); + status = mAudioPolicyManager->startInput(portId, *silenced, &concurrency); } @@ -506,6 +524,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) static constexpr char kAudioPolicy[] = "audiopolicy"; + static constexpr char kAudioPolicyReason[] = "android.media.audiopolicy.reason"; static constexpr char kAudioPolicyStatus[] = "android.media.audiopolicy.status"; static constexpr char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src"; static constexpr char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg"; @@ -522,6 +541,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy); if (item != NULL) { + item->setCString(kAudioPolicyReason, audioConcurrencyString(concurrency).c_str()); item->setInt32(kAudioPolicyStatus, status); item->setCString(kAudioPolicyRqstSrc, @@ -536,35 +556,54 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) item->setCString( kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str()); - int count = mAudioRecordClients.size(); - for (int i = 0; i < count ; i++) { - if (portId == mAudioRecordClients.keyAt(i)) { - continue; - } - sp other = mAudioRecordClients.valueAt(i); - if (other->active) { - // keeps the last of the clients marked active - item->setCString(kAudioPolicyActiveSrc, - audioSourceString(other->attributes.source).c_str()); - item->setInt32(kAudioPolicyActiveSession, other->session); - if (other->opPackageName.size() != 0) { - item->setCString(kAudioPolicyActivePkg, - std::string(String8(other->opPackageName).string()).c_str()); - } else { - item->setCString(kAudioPolicyRqstPkg, - std::to_string(other->uid).c_str()); + // figure out who is active + // NB: might the other party have given up the microphone since then? how sure. + // perhaps could have given up on it. + // we hold mLock, so perhaps we're safe for this looping + if (concurrency != AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE) { + int count = mAudioRecordClients.size(); + for (int i = 0; i other = mAudioRecordClients.valueAt(i); + if (other->active) { + // keeps the last of the clients marked active + item->setCString(kAudioPolicyActiveSrc, + audioSourceString(other->attributes.source).c_str()); + item->setInt32(kAudioPolicyActiveSession, other->session); + if (other->opPackageName.size() != 0) { + item->setCString(kAudioPolicyActivePkg, + std::string(String8(other->opPackageName).string()).c_str()); + } else { + item->setCString(kAudioPolicyRqstPkg, + std::to_string(other->uid).c_str()); + } + item->setCString(kAudioPolicyActiveDevice, + getDeviceTypeStrForPortId(other->deviceId).c_str()); } - item->setCString(kAudioPolicyActiveDevice, - getDeviceTypeStrForPortId(other->deviceId).c_str()); } } item->selfrecord(); delete item; item = NULL; } - client->active = false; - client->startTimeNs = 0; - updateUidStates_l(); + } + + if (status == NO_ERROR) { + LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL, + "startInput(): invalid concurrency type %d", (int)concurrency); + + // enforce permission (if any) required for each type of concurrency + if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL) { + //TODO: check incall capture permission + } + if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE) { + //TODO: check concurrent capture permission + } + + client->active = true; + } else { finishRecording(client->opPackageName, client->uid); } @@ -576,7 +615,6 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) if (mAudioPolicyManager == NULL) { return NO_INIT; } - Mutex::Autolock _l(mLock); ssize_t index = mAudioRecordClients.indexOfKey(portId); @@ -586,9 +624,6 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) sp client = mAudioRecordClients.valueAt(index); client->active = false; - client->startTimeNs = 0; - - updateUidStates_l(); // finish the recording app op finishRecording(client->opPackageName, client->uid); @@ -611,14 +646,6 @@ void AudioPolicyService::releaseInput(audio_port_handle_t portId) return; } client = mAudioRecordClients.valueAt(index); - - if (client->active) { - ALOGW("%s releasing active client portId %d", __FUNCTION__, portId); - client->active = false; - client->startTimeNs = 0; - updateUidStates_l(); - } - mAudioRecordClients.removeItem(portId); } if (client == 0) { diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 2893872346..78dbf5f16b 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -348,91 +348,11 @@ void AudioPolicyService::updateUidStates() void AudioPolicyService::updateUidStates_l() { -// Go over all active clients and allow capture (does not force silence) in the -// following cases: -// - The client is the assistant AND -// an accessibility service is on TOP AND the source is VOICE_RECOGNITION or HOTWORD -// OR -// is on TOP AND uses VOICE_RECOGNITION -// OR uses HOTWORD AND there is no privacy sensitive active capture -// - The client is an accessibility service AND -// is on TOP AND the source is VOICE_RECOGNITION or HOTWORD -// - Any other client AND -// The assistant is not on TOP AND -// is on TOP OR latest started AND -// there is no privacy sensitive active capture -//TODO: mamanage pre processing effects according to use case priority - - sp topActive; - sp latestActive; - nsecs_t latestStartNs = 0; - sp latestSensitiveActive; - nsecs_t latestSensitiveStartNs = 0; - bool isA11yOnTop = mUidPolicy->isA11yOnTop(); - bool isAssistantOnTop = false; - bool isSensitiveActive = false; - + //TODO: implement real concurrent capture policy: for now just apply each app state directly for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; - if (isPrivacySensitive(current->attributes.source)) { - if (current->startTimeNs > latestSensitiveStartNs) { - latestSensitiveActive = current; - latestSensitiveStartNs = current->startTimeNs; - } - isSensitiveActive = true; - } - if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) { - topActive = current; - latestActive = nullptr; - if (mUidPolicy->isAssistantUid(current->uid)) { - isAssistantOnTop = true; - } - } - if (current->startTimeNs > latestStartNs) { - latestActive = current; - latestStartNs = current->startTimeNs; - } - } - - if (topActive == nullptr && latestActive == nullptr) { - return; - } - - for (size_t i =0; i < mAudioRecordClients.size(); i++) { - sp current = mAudioRecordClients[i]; - if (!current->active) continue; - - audio_source_t source = current->attributes.source; - bool isOnTop = mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP; - bool isLatest = current == latestActive; - bool isLatestSensitive = current == latestSensitiveActive; - bool forceIdle = true; - if (mUidPolicy->isAssistantUid(current->uid)) { - if (isA11yOnTop) { - if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { - forceIdle = false; - } - } else { - if (((isOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) || - source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) { - forceIdle = false; - } - } - } else if (mUidPolicy->isA11yUid(current->uid)) { - if (isOnTop && - (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { - forceIdle = false; - } - } else { - if (!isAssistantOnTop && (isOnTop || (topActive == nullptr && isLatest)) && - (!isSensitiveActive || isLatestSensitive)) { - forceIdle = false; - } - } - setAppState_l(current->uid, - forceIdle ? APP_STATE_IDLE : - apmStatFromAmState(mUidPolicy->getUidState(current->uid))); + setAppState_l(current->uid, apmStatFromAmState(mUidPolicy->getUidState(current->uid))); } } @@ -449,22 +369,6 @@ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { return APP_STATE_FOREGROUND; } -/* static */ -bool AudioPolicyService::isPrivacySensitive(audio_source_t source) -{ - switch (source) { - case AUDIO_SOURCE_VOICE_UPLINK: - case AUDIO_SOURCE_VOICE_DOWNLINK: - case AUDIO_SOURCE_VOICE_CALL: - case AUDIO_SOURCE_CAMCORDER: - case AUDIO_SOURCE_VOICE_COMMUNICATION: - return true; - default: - break; - } - return false; -} - void AudioPolicyService::setAppState_l(uid_t uid, app_state_t state) { AutoCallerClear acc; @@ -644,7 +548,6 @@ void AudioPolicyService::UidPolicy::registerSelf() { mObserverRegistered = true; } else { ALOGE("UidPolicy::registerSelf linkToDeath failed: %d", res); - am.unregisterUidObserver(this); } } @@ -747,7 +650,6 @@ int AudioPolicyService::UidPolicy::getUidState(uid_t uid) { mCachedUids.insert(std::pair>(uid, std::pair(active, state))); } - return state; } @@ -828,21 +730,6 @@ void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid.first); - if (it == mA11yUids.end()) { - continue; - } - if (uid.second.second == ActivityManager::PROCESS_STATE_TOP || - uid.second.second == ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE || - uid.second.second == ActivityManager::PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { - return true; - } - } - return false; -} - bool AudioPolicyService::UidPolicy::isA11yUid(uid_t uid) { std::vector::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index dc5a36d34c..4d7235fce8 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -94,7 +94,8 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId = NULL, audio_port_handle_t *portId = NULL); - virtual status_t startInput(audio_port_handle_t portId); + virtual status_t startInput(audio_port_handle_t portId, + bool *silenced); virtual status_t stopInput(audio_port_handle_t portId); virtual void releaseInput(audio_port_handle_t portId); virtual status_t initStreamVolume(audio_stream_type_t stream, @@ -275,8 +276,6 @@ private: void updateUidStates(); void updateUidStates_l(); - static bool isPrivacySensitive(audio_source_t source); - // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID // transitions to an active state we will start reporting buffers with data. This approach @@ -300,7 +299,6 @@ private: bool isAssistantUid(uid_t uid) { return uid == mAssistantUid; } void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } bool isA11yUid(uid_t uid); - bool isA11yOnTop(); // BnUidObserver implementation void onUidActive(uid_t uid) override; @@ -652,11 +650,12 @@ private: const audio_session_t session, const audio_port_handle_t deviceId, const String16& opPackageName) : AudioClient(attributes, io, uid, pid, session, deviceId), - opPackageName(opPackageName), startTimeNs(0) {} + opPackageName(opPackageName), isConcurrent(false), isVirtualDevice(false) {} ~AudioRecordClient() override = default; const String16 opPackageName; // client package name - nsecs_t startTimeNs; + bool isConcurrent; // is allowed to concurrent capture + bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr() }; // --- AudioPlaybackClient --- -- GitLab From 639ed1204cec08fe779d11728d1ae33c90c3151e Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Thu, 6 Dec 2018 14:42:52 -0800 Subject: [PATCH 0566/1530] Camera: VNDK: Fix deferred endConfigure due to missing sessionParam In case the app doesn't provide sessionParameters, the VNDK should still allow endConfigure() to be called to the camera service. Test: Run test app and observe camera streams properly Bug: 120505813 Change-Id: Ifa72dca61b8e25cd433d2c02c70b1c8e5e097228 --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 2 +- services/camera/libcameraservice/hidl/Convert.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index b86f854e04..26e6b3c50f 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -754,8 +754,8 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu const camera_metadata_t *params_metadata = params.getAndLock(); utils::convertToHidl(params_metadata, &hidlParams); params.unlock(params_metadata); - remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams); } + remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams); if (!remoteRet.isOk()) { ALOGE("Transaction error: endConfigure failed %s", remoteRet.description().c_str()); } diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index 76ed6f6d2b..582ce347f9 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -101,12 +101,14 @@ hardware::camera2::params::OutputConfiguration convertFromHidl( bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst) { const camera_metadata_t *buffer = reinterpret_cast(src.data()); size_t expectedSize = src.size(); - int res = validate_camera_metadata_structure(buffer, &expectedSize); - if (res == OK || res == CAMERA_METADATA_VALIDATION_SHIFTED) { - *dst = buffer; - } else { - ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__); - return false; + if (buffer != nullptr) { + int res = validate_camera_metadata_structure(buffer, &expectedSize); + if (res == OK || res == CAMERA_METADATA_VALIDATION_SHIFTED) { + *dst = buffer; + } else { + ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__); + return false; + } } return true; } -- GitLab From e55ed3fa5995380ff8a703136912642e5617d333 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 28 Nov 2018 03:39:57 -0800 Subject: [PATCH 0567/1530] Make Codec2 software service public This CL makes the software Codec2 service present the public Codec2 HAL interface, and makes the framework use it. Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 112362730 Bug: 119853704 Change-Id: I047c6948a883a6e085d988a3fa542129ebb9571f --- media/codec2/hidl/client/client.cpp | 14 ++- media/codec2/hidl/services/Android.bp | 55 --------- .../vndk/C2PlatformStorePluginLoader.cpp | 8 +- media/codec2/vndk/C2Store.cpp | 112 +++++++++--------- media/libstagefright/Android.bp | 7 +- .../StagefrightPluginLoader.cpp | 13 +- services/mediacodec/registrant/Android.bp | 53 +++++++++ .../registrant/CodecServiceRegistrant.cpp | 16 ++- 8 files changed, 150 insertions(+), 128 deletions(-) create mode 100644 services/mediacodec/registrant/Android.bp rename media/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp => services/mediacodec/registrant/CodecServiceRegistrant.cpp (74%) diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index ddeb4ff1b6..26bd96c23f 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -76,7 +76,11 @@ typedef std::array, kNumClients> ClientList; // Convenience methods to obtain known clients. std::shared_ptr getClient(size_t index) { - return Codec2Client::CreateFromService(kClientNames[index]); + uint32_t serviceMask = ::android::base::GetUintProperty( + "debug.media.codec2", uint32_t(0)); + return Codec2Client::CreateFromService( + kClientNames[index], + (serviceMask & (1 << index)) != 0); } ClientList getClientList() { @@ -633,9 +637,13 @@ std::shared_ptr Codec2Client::CreateFromService( Base::tryGetService(instanceName); if (!baseStore) { if (waitForService) { - ALOGE("Codec2.0 service inaccessible. Check the device manifest."); + ALOGW("Codec2.0 service \"%s\" inaccessible. " + "Check the device manifest.", + instanceName); } else { - ALOGW("Codec2.0 service not available right now. Try again later."); + ALOGD("Codec2.0 service \"%s\" unavailable right now. " + "Try again later.", + instanceName); } return nullptr; } diff --git a/media/codec2/hidl/services/Android.bp b/media/codec2/hidl/services/Android.bp index 965971e95b..216525e907 100644 --- a/media/codec2/hidl/services/Android.bp +++ b/media/codec2/hidl/services/Android.bp @@ -37,58 +37,3 @@ cc_binary { compile_multilib: "32", } -cc_library_shared { - name: "libcodec2_serviceregistrant", - // need vendor version for update packaging, system version may have more dependencies - vendor_available: true, - srcs: [ - "C2SoftwareCodecServiceRegistrant.cpp", - ], - - header_libs: [ - "libmedia_headers", - ], - - shared_libs: [ - "android.hardware.media.c2@1.0", - "libcodec2_hidl@1.0", - "libcodec2_vndk", - "liblog", - "libutils", - ], - - // Codecs - runtime_libs: [ - "libcodec2_soft_avcdec", - "libcodec2_soft_avcenc", - "libcodec2_soft_aacdec", - "libcodec2_soft_aacenc", - "libcodec2_soft_amrnbdec", - "libcodec2_soft_amrnbenc", - "libcodec2_soft_amrwbdec", - "libcodec2_soft_amrwbenc", - "libcodec2_soft_hevcdec", - "libcodec2_soft_g711alawdec", - "libcodec2_soft_g711mlawdec", - "libcodec2_soft_mpeg2dec", - "libcodec2_soft_h263dec", - "libcodec2_soft_h263enc", - "libcodec2_soft_mpeg4dec", - "libcodec2_soft_mpeg4enc", - "libcodec2_soft_mp3dec", - "libcodec2_soft_vorbisdec", - "libcodec2_soft_opusdec", - "libcodec2_soft_vp8dec", - "libcodec2_soft_vp9dec", - "libcodec2_soft_vp8enc", - "libcodec2_soft_vp9enc", - "libcodec2_soft_rawdec", - "libcodec2_soft_flacdec", - "libcodec2_soft_flacenc", - "libcodec2_soft_gsmdec", - "libcodec2_soft_xaacdec", - ], - - compile_multilib: "32", -} - diff --git a/media/codec2/vndk/C2PlatformStorePluginLoader.cpp b/media/codec2/vndk/C2PlatformStorePluginLoader.cpp index 7a460f4cc1..4c330e522a 100644 --- a/media/codec2/vndk/C2PlatformStorePluginLoader.cpp +++ b/media/codec2/vndk/C2PlatformStorePluginLoader.cpp @@ -26,6 +26,12 @@ /* static */ android::Mutex C2PlatformStorePluginLoader::sMutex; /* static */ std::unique_ptr C2PlatformStorePluginLoader::sInstance; +namespace /* unnamed */ { + +constexpr const char kStorePluginPath[] = "libc2plugin_store.so"; + +} // unnamed + C2PlatformStorePluginLoader::C2PlatformStorePluginLoader(const char *libPath) : mCreateBlockPool(nullptr) { mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE); @@ -89,7 +95,7 @@ const std::unique_ptr& C2PlatformStorePluginLoader: android::Mutex::Autolock _l(sMutex); if (!sInstance) { ALOGV("Loading library"); - sInstance.reset(new C2PlatformStorePluginLoader("libstagefright_ccodec_ext.so")); + sInstance.reset(new C2PlatformStorePluginLoader(kStorePluginPath)); } return sInstance; } diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 33019ed856..2d4e19e43c 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -798,65 +798,65 @@ C2PlatformComponentStore::C2PlatformComponentStore() mComponentsList.emplace_back(alias); }; // TODO: move this also into a .so so it can be updated - emplace("c2.android.avc.decoder", "libstagefright_soft_c2avcdec.so"); - emplace("c2.android.avc.encoder", "libstagefright_soft_c2avcenc.so"); - emplace("c2.android.aac.decoder", "libstagefright_soft_c2aacdec.so"); - emplace("c2.android.aac.encoder", "libstagefright_soft_c2aacenc.so"); - emplace("c2.android.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so"); - emplace("c2.android.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so"); - emplace("c2.android.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so"); - emplace("c2.android.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so"); - emplace("c2.android.hevc.decoder", "libstagefright_soft_c2hevcdec.so"); - emplace("c2.android.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so"); - emplace("c2.android.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so"); - emplace("c2.android.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so"); - emplace("c2.android.h263.decoder", "libstagefright_soft_c2h263dec.so"); - emplace("c2.android.h263.encoder", "libstagefright_soft_c2h263enc.so"); - emplace("c2.android.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so"); - emplace("c2.android.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so"); - emplace("c2.android.mp3.decoder", "libstagefright_soft_c2mp3dec.so"); - emplace("c2.android.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so"); - emplace("c2.android.opus.decoder", "libstagefright_soft_c2opusdec.so"); - emplace("c2.android.vp8.decoder", "libstagefright_soft_c2vp8dec.so"); - emplace("c2.android.vp9.decoder", "libstagefright_soft_c2vp9dec.so"); - emplace("c2.android.vp8.encoder", "libstagefright_soft_c2vp8enc.so"); - emplace("c2.android.vp9.encoder", "libstagefright_soft_c2vp9enc.so"); - emplace("c2.android.raw.decoder", "libstagefright_soft_c2rawdec.so"); - emplace("c2.android.flac.decoder", "libstagefright_soft_c2flacdec.so"); - emplace("c2.android.flac.encoder", "libstagefright_soft_c2flacenc.so"); - emplace("c2.android.gsm.decoder", "libstagefright_soft_c2gsmdec.so"); - emplace("c2.android.xaac.decoder", "libstagefright_soft_c2xaacdec.so"); + emplace("c2.android.avc.decoder", "libcodec2_soft_avcdec.so"); + emplace("c2.android.avc.encoder", "libcodec2_soft_avcenc.so"); + emplace("c2.android.aac.decoder", "libcodec2_soft_aacdec.so"); + emplace("c2.android.aac.encoder", "libcodec2_soft_aacenc.so"); + emplace("c2.android.amrnb.decoder", "libcodec2_soft_amrnbdec.so"); + emplace("c2.android.amrnb.encoder", "libcodec2_soft_amrnbenc.so"); + emplace("c2.android.amrwb.decoder", "libcodec2_soft_amrwbdec.so"); + emplace("c2.android.amrwb.encoder", "libcodec2_soft_amrwbenc.so"); + emplace("c2.android.hevc.decoder", "libcodec2_soft_hevcdec.so"); + emplace("c2.android.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so"); + emplace("c2.android.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so"); + emplace("c2.android.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so"); + emplace("c2.android.h263.decoder", "libcodec2_soft_h263dec.so"); + emplace("c2.android.h263.encoder", "libcodec2_soft_h263enc.so"); + emplace("c2.android.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so"); + emplace("c2.android.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so"); + emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so"); + emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so"); + emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so"); + emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so"); + emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so"); + emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so"); + emplace("c2.android.vp9.encoder", "libcodec2_soft_vp9enc.so"); + emplace("c2.android.raw.decoder", "libcodec2_soft_rawdec.so"); + emplace("c2.android.flac.decoder", "libcodec2_soft_flacdec.so"); + emplace("c2.android.flac.encoder", "libcodec2_soft_flacenc.so"); + emplace("c2.android.gsm.decoder", "libcodec2_soft_gsmdec.so"); + emplace("c2.android.xaac.decoder", "libcodec2_soft_xaacdec.so"); // "Aliases" // TODO: use aliases proper from C2Component::Traits - emplace("OMX.google.h264.decoder", "libstagefright_soft_c2avcdec.so"); - emplace("OMX.google.h264.encoder", "libstagefright_soft_c2avcenc.so"); - emplace("OMX.google.aac.decoder", "libstagefright_soft_c2aacdec.so"); - emplace("OMX.google.aac.encoder", "libstagefright_soft_c2aacenc.so"); - emplace("OMX.google.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so"); - emplace("OMX.google.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so"); - emplace("OMX.google.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so"); - emplace("OMX.google.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so"); - emplace("OMX.google.hevc.decoder", "libstagefright_soft_c2hevcdec.so"); - emplace("OMX.google.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so"); - emplace("OMX.google.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so"); - emplace("OMX.google.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so"); - emplace("OMX.google.h263.decoder", "libstagefright_soft_c2h263dec.so"); - emplace("OMX.google.h263.encoder", "libstagefright_soft_c2h263enc.so"); - emplace("OMX.google.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so"); - emplace("OMX.google.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so"); - emplace("OMX.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so"); - emplace("OMX.google.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so"); - emplace("OMX.google.opus.decoder", "libstagefright_soft_c2opusdec.so"); - emplace("OMX.google.vp8.decoder", "libstagefright_soft_c2vp8dec.so"); - emplace("OMX.google.vp9.decoder", "libstagefright_soft_c2vp9dec.so"); - emplace("OMX.google.vp8.encoder", "libstagefright_soft_c2vp8enc.so"); - emplace("OMX.google.vp9.encoder", "libstagefright_soft_c2vp9enc.so"); - emplace("OMX.google.raw.decoder", "libstagefright_soft_c2rawdec.so"); - emplace("OMX.google.flac.decoder", "libstagefright_soft_c2flacdec.so"); - emplace("OMX.google.flac.encoder", "libstagefright_soft_c2flacenc.so"); - emplace("OMX.google.gsm.decoder", "libstagefright_soft_c2gsmdec.so"); - emplace("OMX.google.xaac.decoder", "libstagefright_soft_c2xaacdec.so"); + emplace("OMX.google.h264.decoder", "libcodec2_soft_avcdec.so"); + emplace("OMX.google.h264.encoder", "libcodec2_soft_avcenc.so"); + emplace("OMX.google.aac.decoder", "libcodec2_soft_aacdec.so"); + emplace("OMX.google.aac.encoder", "libcodec2_soft_aacenc.so"); + emplace("OMX.google.amrnb.decoder", "libcodec2_soft_amrnbdec.so"); + emplace("OMX.google.amrnb.encoder", "libcodec2_soft_amrnbenc.so"); + emplace("OMX.google.amrwb.decoder", "libcodec2_soft_amrwbdec.so"); + emplace("OMX.google.amrwb.encoder", "libcodec2_soft_amrwbenc.so"); + emplace("OMX.google.hevc.decoder", "libcodec2_soft_hevcdec.so"); + emplace("OMX.google.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so"); + emplace("OMX.google.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so"); + emplace("OMX.google.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so"); + emplace("OMX.google.h263.decoder", "libcodec2_soft_h263dec.so"); + emplace("OMX.google.h263.encoder", "libcodec2_soft_h263enc.so"); + emplace("OMX.google.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so"); + emplace("OMX.google.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so"); + emplace("OMX.google.mp3.decoder", "libcodec2_soft_mp3dec.so"); + emplace("OMX.google.vorbis.decoder", "libcodec2_soft_vorbisdec.so"); + emplace("OMX.google.opus.decoder", "libcodec2_soft_opusdec.so"); + emplace("OMX.google.vp8.decoder", "libcodec2_soft_vp8dec.so"); + emplace("OMX.google.vp9.decoder", "libcodec2_soft_vp9dec.so"); + emplace("OMX.google.vp8.encoder", "libcodec2_soft_vp8enc.so"); + emplace("OMX.google.vp9.encoder", "libcodec2_soft_vp9enc.so"); + emplace("OMX.google.raw.decoder", "libcodec2_soft_rawdec.so"); + emplace("OMX.google.flac.decoder", "libcodec2_soft_flacdec.so"); + emplace("OMX.google.flac.encoder", "libcodec2_soft_flacenc.so"); + emplace("OMX.google.gsm.decoder", "libcodec2_soft_gsmdec.so"); + emplace("OMX.google.xaac.decoder", "libcodec2_soft_xaacdec.so"); } c2_status_t C2PlatformComponentStore::copyBuffer( diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index cc5b7da890..9572fb1180 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -146,6 +146,7 @@ cc_library_shared { shared_libs: [ "libaudioutils", + "libbase", "libbinder", "libcamera_client", "libcutils", @@ -206,12 +207,6 @@ cc_library_shared { "include", ], - // This is needed to make sure libcodec2 exists in all devices. - // TODO: Remove this once the public CCodec is enabled. - required: [ - "libcodec2", - ], - cflags: [ "-Wno-multichar", "-Werror", diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index dd5903add3..26d7dffdeb 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "StagefrightPluginLoader" #include +#include #include #include "StagefrightPluginLoader.h" @@ -27,9 +28,19 @@ namespace android { /* static */ Mutex StagefrightPluginLoader::sMutex; /* static */ std::unique_ptr StagefrightPluginLoader::sInstance; +namespace /* unnamed */ { + +constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so"; + +} // unnamed namespace + StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) : mCreateCodec(nullptr), mCreateBuilder(nullptr) { + if (android::base::GetIntProperty("debug.media.codec2", 0) == 0) { + ALOGD("CCodec is disabled."); + return; + } mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE); if (mLibHandle == nullptr) { ALOGD("Failed to load library: %s (%s)", libPath, dlerror()); @@ -87,7 +98,7 @@ const std::unique_ptr &StagefrightPluginLoader::GetCCod Mutex::Autolock _l(sMutex); if (!sInstance) { ALOGV("Loading library"); - sInstance.reset(new StagefrightPluginLoader("libstagefright_ccodec.so")); + sInstance.reset(new StagefrightPluginLoader(kCCodecPluginPath)); } return sInstance; } diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp new file mode 100644 index 0000000000..653317b4dc --- /dev/null +++ b/services/mediacodec/registrant/Android.bp @@ -0,0 +1,53 @@ +cc_library_shared { + name: "libmedia_codecserviceregistrant", + srcs: [ + "CodecServiceRegistrant.cpp", + ], + + header_libs: [ + "libmedia_headers", + ], + + shared_libs: [ + "android.hardware.media.c2@1.0", + "libbase", + "libcodec2_hidl@1.0", + "libcodec2_vndk", + "libutils", + ], + + // Codecs + runtime_libs: [ + "libcodec2_soft_avcdec", + "libcodec2_soft_avcenc", + "libcodec2_soft_aacdec", + "libcodec2_soft_aacenc", + "libcodec2_soft_amrnbdec", + "libcodec2_soft_amrnbenc", + "libcodec2_soft_amrwbdec", + "libcodec2_soft_amrwbenc", + "libcodec2_soft_hevcdec", + "libcodec2_soft_g711alawdec", + "libcodec2_soft_g711mlawdec", + "libcodec2_soft_mpeg2dec", + "libcodec2_soft_h263dec", + "libcodec2_soft_h263enc", + "libcodec2_soft_mpeg4dec", + "libcodec2_soft_mpeg4enc", + "libcodec2_soft_mp3dec", + "libcodec2_soft_vorbisdec", + "libcodec2_soft_opusdec", + "libcodec2_soft_vp8dec", + "libcodec2_soft_vp9dec", + "libcodec2_soft_vp8enc", + "libcodec2_soft_vp9enc", + "libcodec2_soft_rawdec", + "libcodec2_soft_flacdec", + "libcodec2_soft_flacenc", + "libcodec2_soft_gsmdec", + "libcodec2_soft_xaacdec", + ], + + compile_multilib: "32", +} + diff --git a/media/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp b/services/mediacodec/registrant/CodecServiceRegistrant.cpp similarity index 74% rename from media/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp rename to services/mediacodec/registrant/CodecServiceRegistrant.cpp index e10ae6e42c..706ebee13a 100644 --- a/media/codec2/hidl/services/C2SoftwareCodecServiceRegistrant.cpp +++ b/services/mediacodec/registrant/CodecServiceRegistrant.cpp @@ -15,26 +15,30 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "C2SoftwareCodecServiceRegistrant" +#define LOG_TAG "CodecServiceRegistrant" + +#include #include #include #include -#include extern "C" void RegisterCodecServices() { using namespace ::android::hardware::media::c2::V1_0; + LOG(INFO) << "Creating software Codec2 service..."; android::sp store = new utils::ComponentStore( android::GetCodec2PlatformComponentStore()); if (store == nullptr) { - ALOGE("Cannot create Codec2's IComponentStore software service."); + LOG(ERROR) << + "Cannot create software Codec2 service."; } else { if (store->registerAsService("software") != android::OK) { - ALOGE("Cannot register Codec2's " - "IComponentStore software service."); + LOG(ERROR) << + "Cannot register software Codec2 service."; } else { - ALOGI("Codec2's IComponentStore software service created."); + LOG(INFO) << + "Software Codec2 service created."; } } } -- GitLab From 459376a43a76fd9e5d79c47edf96f249138edd35 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 7 Dec 2018 14:10:47 +0000 Subject: [PATCH 0568/1530] Revert "AML: Use MediaSessionManager.createSession() which is @SystemApi" This reverts commit 58e523a8cc22a696c56c55fab58137b24966391a. Reason for revert: Return and parameter types of added SystemApi are not themselves part of the API. Change-Id: I4898fd6cda7ec57f386d1c382e55c9f3cfcaa43e --- .../apex/java/android/media/session/MediaSession.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index 42d92d1935..b40e3b0659 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -179,7 +179,9 @@ public final class MediaSession { MediaSessionManager manager = (MediaSessionManager) context .getSystemService(Context.MEDIA_SESSION_SERVICE); try { - mBinder = manager.createSession(mCbStub, tag, userId); + //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession + //mBinder = manager.createSession(mCbStub, tag, userId); + mBinder = null; //TODO: remove this. mSessionToken = new Token(mBinder.getController()); mController = new MediaController(context, mSessionToken); } catch (RemoteException e) { -- GitLab From b8aca87eb2367136b10f5d7264f12b389af28d23 Mon Sep 17 00:00:00 2001 From: dimitry Date: Tue, 4 Dec 2018 15:58:40 +0100 Subject: [PATCH 0569/1530] Apply version-script to libmediandk library This change also remove some redundancies in include file and actualizes list of exported AMEDIAFORMAT_ variables for 29 sdk version. Also adds AMediaCodec_getBufferFormat to the list. Bug: http://b/69603741 Test: make Change-Id: I1fd7d1d3dfdd567aa7398c48117c60344bd5e109 --- media/ndk/Android.bp | 1 + media/ndk/include/media/NdkMediaFormat.h | 4 -- media/ndk/libmediandk.map.txt | 47 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 8267ba528d..e3551833e5 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -104,6 +104,7 @@ cc_library_shared { enabled: false, }, }, + version_script: "libmediandk.map.txt", } llndk_library { diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 4be1928b5b..13d9135c15 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -184,9 +184,6 @@ extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29); -extern const char* AMEDIAFORMAT_KEY_COLOR_RANGE __INTRODUCED_IN(29); -extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD __INTRODUCED_IN(29); -extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_COMPILATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_COMPOSER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE __INTRODUCED_IN(29); @@ -207,7 +204,6 @@ extern const char* AMEDIAFORMAT_KEY_EXIF_OFFSET __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_EXIF_SIZE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_FRAME_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_GENRE __INTRODUCED_IN(29); -extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ICC_PROFILE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index d3bdbaefb4..88736ab5f8 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -35,64 +35,110 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT; # var introduced=28 AMEDIAFORMAT_KEY_AAC_PROFILE; # var introduced=21 AMEDIAFORMAT_KEY_AAC_SBR_MODE; # var introduced=28 + AMEDIAFORMAT_KEY_ALBUM; # var introduced=29 + AMEDIAFORMAT_KEY_ALBUMART; # var introduced=29 + AMEDIAFORMAT_KEY_ALBUMARTIST; # var introduced=29 + AMEDIAFORMAT_KEY_ARTIST; # var introduced=29 + AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO; # var introduced=29 AMEDIAFORMAT_KEY_AUDIO_SESSION_ID; # var introduced=28 + AMEDIAFORMAT_KEY_AUTHOR; # var introduced=29 AMEDIAFORMAT_KEY_BITRATE_MODE; # var introduced=28 AMEDIAFORMAT_KEY_BIT_RATE; # var introduced=21 + AMEDIAFORMAT_KEY_BITS_PER_SAMPLE; # var introduced=29 AMEDIAFORMAT_KEY_CAPTURE_RATE; # var introduced=28 + AMEDIAFORMAT_KEY_CDTRACKNUMBER; # var introduced=29 AMEDIAFORMAT_KEY_CHANNEL_COUNT; # var introduced=21 AMEDIAFORMAT_KEY_CHANNEL_MASK; # var introduced=21 AMEDIAFORMAT_KEY_COLOR_FORMAT; # var introduced=21 AMEDIAFORMAT_KEY_COLOR_RANGE; # var introduced=28 AMEDIAFORMAT_KEY_COLOR_STANDARD; # var introduced=28 AMEDIAFORMAT_KEY_COLOR_TRANSFER; # var introduced=28 + AMEDIAFORMAT_KEY_COMPILATION; # var introduced=29 AMEDIAFORMAT_KEY_COMPLEXITY; # var introduced=28 + AMEDIAFORMAT_KEY_COMPOSER; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_IV; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_KEY; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_MODE; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK; # var introduced=29 AMEDIAFORMAT_KEY_CSD; # var introduced=28 AMEDIAFORMAT_KEY_CSD_0; # var introduced=28 AMEDIAFORMAT_KEY_CSD_1; # var introduced=28 AMEDIAFORMAT_KEY_CSD_2; # var introduced=28 + AMEDIAFORMAT_KEY_CSD_AVC; # var introduced=29 + AMEDIAFORMAT_KEY_CSD_HEVC; # var introduced=29 + AMEDIAFORMAT_KEY_D263; # var introduced=29 + AMEDIAFORMAT_KEY_DATE; # var introduced=29 + AMEDIAFORMAT_KEY_DISCNUMBER; # var introduced=29 AMEDIAFORMAT_KEY_DISPLAY_CROP; # var introduced=28 AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_DISPLAY_WIDTH; # var introduced=28 AMEDIAFORMAT_KEY_DURATION; # var introduced=21 + AMEDIAFORMAT_KEY_ENCODER_DELAY; # var introduced=29 + AMEDIAFORMAT_KEY_ENCODER_PADDING; # var introduced=29 + AMEDIAFORMAT_KEY_ESDS; # var introduced=29 + AMEDIAFORMAT_KEY_EXIF_OFFSET; # var introduced=29 + AMEDIAFORMAT_KEY_EXIF_SIZE; # var introduced=29 + AMEDIAFORMAT_KEY_FRAME_COUNT; # var introduced=29 AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var introduced=21 AMEDIAFORMAT_KEY_FRAME_RATE; # var introduced=21 + AMEDIAFORMAT_KEY_GENRE; # var introduced=29 AMEDIAFORMAT_KEY_GRID_COLUMNS; # var introduced=28 AMEDIAFORMAT_KEY_GRID_ROWS; # var introduced=28 AMEDIAFORMAT_KEY_HDR_STATIC_INFO; # var introduced=28 AMEDIAFORMAT_KEY_HEIGHT; # var introduced=21 + AMEDIAFORMAT_KEY_ICC_PROFILE; # var introduced=29 AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD; # var introduced=28 AMEDIAFORMAT_KEY_IS_ADTS; # var introduced=21 AMEDIAFORMAT_KEY_IS_AUTOSELECT; # var introduced=21 AMEDIAFORMAT_KEY_IS_DEFAULT; # var introduced=21 AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE; # var introduced=21 + AMEDIAFORMAT_KEY_IS_SYNC_FRAME; # var introduced=29 AMEDIAFORMAT_KEY_I_FRAME_INTERVAL; # var introduced=21 AMEDIAFORMAT_KEY_LANGUAGE; # var introduced=21 AMEDIAFORMAT_KEY_LATENCY; # var introduced=28 AMEDIAFORMAT_KEY_LEVEL; # var introduced=28 + AMEDIAFORMAT_KEY_LOCATION; # var introduced=29 + AMEDIAFORMAT_KEY_LOOP; # var introduced=29 + AMEDIAFORMAT_KEY_LYRICIST; # var introduced=29 + AMEDIAFORMAT_KEY_MAX_BIT_RATE; # var introduced=29 AMEDIAFORMAT_KEY_MAX_HEIGHT; # var introduced=21 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var introduced=21 AMEDIAFORMAT_KEY_MAX_WIDTH; # var introduced=21 AMEDIAFORMAT_KEY_MIME; # var introduced=21 AMEDIAFORMAT_KEY_MPEG_USER_DATA; # var introduced=28 + AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER; # var introduced=29 AMEDIAFORMAT_KEY_OPERATING_RATE; # var introduced=28 + AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN; # var introduced=29 AMEDIAFORMAT_KEY_PCM_ENCODING; # var introduced=28 AMEDIAFORMAT_KEY_PRIORITY; # var introduced=28 AMEDIAFORMAT_KEY_PROFILE; # var introduced=28 + AMEDIAFORMAT_KEY_PSSH; # var introduced=29 AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP; # var introduced=21 AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER; # var introduced=21 AMEDIAFORMAT_KEY_ROTATION; # var introduced=28 AMEDIAFORMAT_KEY_SAMPLE_RATE; # var introduced=21 + AMEDIAFORMAT_KEY_SAR_HEIGHT; # var introduced=29 + AMEDIAFORMAT_KEY_SAR_WIDTH; # var introduced=29 AMEDIAFORMAT_KEY_SEI; # var introduced=28 AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_STRIDE; # var introduced=21 + AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT; # var introduced=29 AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; # var introduced=28 AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28 + AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA; # var introduced=29 + AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT; # var introduced=29 + AMEDIAFORMAT_KEY_THUMBNAIL_TIME; # var introduced=29 + AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH; # var introduced=29 + AMEDIAFORMAT_KEY_TITLE; # var introduced=28 AMEDIAFORMAT_KEY_TILE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_TILE_WIDTH; # var introduced=28 AMEDIAFORMAT_KEY_TIME_US; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_INDEX; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28 AMEDIAFORMAT_KEY_WIDTH; # var introduced=21 + AMEDIAFORMAT_KEY_YEAR; # var introduced=29 AMediaCodecActionCode_isRecoverable; # introduced=28 AMediaCodecActionCode_isTransient; # introduced=28 AMediaCodecCryptoInfo_delete; @@ -112,6 +158,7 @@ LIBMEDIANDK { AMediaCodec_dequeueInputBuffer; AMediaCodec_dequeueOutputBuffer; AMediaCodec_flush; + AMediaCodec_getBufferFormat; # introduced=21 AMediaCodec_getInputBuffer; AMediaCodec_getInputFormat; # introduced=28 AMediaCodec_getName; # introduced=28 -- GitLab From 60bfbd7bd10417da75d6e507211f651b14bb1861 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 7 Dec 2018 11:55:33 -0800 Subject: [PATCH 0570/1530] Convert MP3Extractor to V3 format Bug: 111407253 Test: manual, CTS Change-Id: Id3a70df7512d45f8d37b125f1eb65d23f3bd68f7 --- media/extractors/mp3/Android.bp | 3 +-- media/extractors/mp3/MP3Extractor.cpp | 36 +++++++++++---------------- media/extractors/mp3/MP3Extractor.h | 4 +-- media/extractors/wav/WAVExtractor.cpp | 1 - media/libstagefright/MediaTrack.cpp | 10 +++++--- 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp index 97b687f7ed..4e2f2480ac 100644 --- a/media/extractors/mp3/Android.bp +++ b/media/extractors/mp3/Android.bp @@ -12,14 +12,13 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", - "libstagefright_foundation", ], static_libs: [ "libutils", "libstagefright_id3", + "libstagefright_foundation", ], name: "libmp3extractor", diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 0e1ffb45d8..7abec549cb 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -207,7 +207,7 @@ static bool Resync( return valid; } -class MP3Source : public MediaTrackHelperV2 { +class MP3Source : public MediaTrackHelperV3 { public: MP3Source( AMediaFormat *meta, DataSourceHelper *source, @@ -220,7 +220,7 @@ public: virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~MP3Source(); @@ -235,7 +235,6 @@ private: int64_t mCurrentTimeUs; bool mStarted; MP3Seeker *mSeeker; - MediaBufferGroup *mGroup; int64_t mBasisTimeUs; int64_t mSamplesRead; @@ -414,7 +413,7 @@ size_t MP3Extractor::countTracks() { return mInitCheck != OK ? 0 : 1; } -MediaTrackHelperV2 *MP3Extractor::getTrack(size_t index) { +MediaTrackHelperV3 *MP3Extractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -455,7 +454,6 @@ MP3Source::MP3Source( mCurrentTimeUs(0), mStarted(false), mSeeker(seeker), - mGroup(NULL), mBasisTimeUs(0), mSamplesRead(0) { } @@ -469,9 +467,7 @@ MP3Source::~MP3Source() { media_status_t MP3Source::start() { CHECK(!mStarted); - mGroup = new MediaBufferGroup; - - mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize)); + mBufferGroup->add_buffer(kMaxFrameSize); mCurrentPos = mFirstFramePos; mCurrentTimeUs = 0; @@ -487,9 +483,6 @@ media_status_t MP3Source::start() { media_status_t MP3Source::stop() { CHECK(mStarted); - delete mGroup; - mGroup = NULL; - mStarted = false; return AMEDIA_OK; @@ -500,7 +493,7 @@ media_status_t MP3Source::getFormat(AMediaFormat *meta) { } media_status_t MP3Source::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -530,8 +523,8 @@ media_status_t MP3Source::read( mSamplesRead = 0; } - MediaBufferBase *buffer; - status_t err = mGroup->acquire_buffer(&buffer); + MediaBufferHelperV3 *buffer; + status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; } @@ -597,8 +590,9 @@ media_status_t MP3Source::read( buffer->set_range(0, frame_size); - buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs); - buffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, mCurrentTimeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); mCurrentPos += frame_size; @@ -674,14 +668,14 @@ media_status_t MP3Extractor::getMetaData(AMediaFormat *meta) { return AMEDIA_OK; } -static CMediaExtractorV2* CreateExtractor( +static CMediaExtractorV3* CreateExtractor( CDataSource *source, void *meta) { Mp3Meta *metaData = static_cast(meta); - return wrapV2(new MP3Extractor(new DataSourceHelper(source), metaData)); + return wrapV3(new MP3Extractor(new DataSourceHelper(source), metaData)); } -static CreatorFuncV2 Sniff( +static CreatorFuncV3 Sniff( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; @@ -718,11 +712,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("812a3f6c-c8cf-46de-b529-3774b14103d4"), 1, // version "MP3 Extractor", - { .v2 = Sniff } + { .v3 = Sniff } }; } diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h index 22547e21d8..fe72cff2c0 100644 --- a/media/extractors/mp3/MP3Extractor.h +++ b/media/extractors/mp3/MP3Extractor.h @@ -32,13 +32,13 @@ struct MP3Seeker; class String8; struct Mp3Meta; -class MP3Extractor : public MediaExtractorPluginHelperV2 { +class MP3Extractor : public MediaExtractorPluginHelperV3 { public: MP3Extractor(DataSourceHelper *source, Mp3Meta *meta); ~MP3Extractor(); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 17b5f81523..1f0aae5a9b 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -383,7 +383,6 @@ WAVSource::WAVSource( } WAVSource::~WAVSource() { - ALOGI("~WAVSource"); if (mStarted) { stop(); } diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index daed5a0aa2..6c0f989815 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -212,9 +212,13 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption // only convert the keys we're actually expecting, as doing // the full convertMessageToMetadata() for every buffer is // too expensive - int64_t val; - if (format->mFormat->findInt64("timeUs", &val)) { - meta.setInt64(kKeyTime, val); + int64_t val64; + if (format->mFormat->findInt64("timeUs", &val64)) { + meta.setInt64(kKeyTime, val64); + } + int32_t val32; + if (format->mFormat->findInt32("is-sync-frame", &val32)) { + meta.setInt32(kKeyIsSyncFrame, val32); } } else { *buffer = nullptr; -- GitLab From 1b3c907bb2cb9d1e2f91baf37709d935f687bf96 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Wed, 7 Nov 2018 12:41:53 -0800 Subject: [PATCH 0571/1530] Camera: Setup vendors tags when initializing ProviderInfo Add new VendorTagDescriptor member to ProviderInfo, allowing it to cache the descriptor Test: Use torch, camera, and run cameraservice_test Change-Id: Iba5af5dc890b3188a9e55ff5c71421aceec14cbb --- .../common/CameraProviderManager.cpp | 75 +++++++++++-------- .../common/CameraProviderManager.h | 6 ++ 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index a82f0f76e3..ceca0f8ac9 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -254,37 +254,7 @@ status_t CameraProviderManager::setUpVendorTags() { sp tagCache = new VendorTagDescriptorCache(); for (auto& provider : mProviders) { - hardware::hidl_vec vts; - Status status; - hardware::Return ret; - ret = provider->mInterface->getVendorTags( - [&](auto s, const auto& vendorTagSecs) { - status = s; - if (s == Status::OK) { - vts = vendorTagSecs; - } - }); - if (!ret.isOk()) { - ALOGE("%s: Transaction error getting vendor tags from provider '%s': %s", - __FUNCTION__, provider->mProviderName.c_str(), ret.description().c_str()); - return DEAD_OBJECT; - } - if (status != Status::OK) { - return mapToStatusT(status); - } - - // Read all vendor tag definitions into a descriptor - sp desc; - status_t res; - if ((res = HidlVendorTagDescriptor::createDescriptorFromHidl(vts, /*out*/desc)) - != OK) { - ALOGE("%s: Could not generate descriptor from vendor tag operations," - "received error %s (%d). Camera clients will not be able to use" - "vendor tags", __FUNCTION__, strerror(res), res); - return res; - } - - tagCache->addVendorDescriptor(provider->mProviderTagid, desc); + tagCache->addVendorDescriptor(provider->mProviderTagid, provider->mVendorTagDescriptor); } VendorTagDescriptorCache::setAsGlobalVendorTagCache(tagCache); @@ -750,6 +720,13 @@ status_t CameraProviderManager::ProviderInfo::initialize() { } } + res = setUpVendorTags(); + if (res != OK) { + ALOGE("%s: Unable to set up vendor tags from provider '%s'", + __FUNCTION__, mProviderName.c_str()); + return res; + } + ALOGI("Camera provider %s ready with %zu camera devices", mProviderName.c_str(), mDevices.size()); @@ -994,6 +971,42 @@ void CameraProviderManager::ProviderInfo::serviceDied(uint64_t cookie, mManager->removeProvider(mProviderName); } +status_t CameraProviderManager::ProviderInfo::setUpVendorTags() { + if (mVendorTagDescriptor != nullptr) + return OK; + + hardware::hidl_vec vts; + Status status; + hardware::Return ret; + ret = mInterface->getVendorTags( + [&](auto s, const auto& vendorTagSecs) { + status = s; + if (s == Status::OK) { + vts = vendorTagSecs; + } + }); + if (!ret.isOk()) { + ALOGE("%s: Transaction error getting vendor tags from provider '%s': %s", + __FUNCTION__, mProviderName.c_str(), ret.description().c_str()); + return DEAD_OBJECT; + } + if (status != Status::OK) { + return mapToStatusT(status); + } + + // Read all vendor tag definitions into a descriptor + status_t res; + if ((res = HidlVendorTagDescriptor::createDescriptorFromHidl(vts, /*out*/mVendorTagDescriptor)) + != OK) { + ALOGE("%s: Could not generate descriptor from vendor tag operations," + "received error %s (%d). Camera clients will not be able to use" + "vendor tags", __FUNCTION__, strerror(res), res); + return res; + } + + return OK; +} + template std::unique_ptr CameraProviderManager::ProviderInfo::initializeDeviceInfo( diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 99b87fbc0c..1473b8fbe7 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -266,6 +266,7 @@ private: const std::string mProviderName; const sp mInterface; const metadata_vendor_id_t mProviderTagid; + sp mVendorTagDescriptor; ProviderInfo(const std::string &providerName, sp& interface, @@ -294,6 +295,11 @@ private: // hidl_death_recipient interface - this locks the parent mInterfaceMutex virtual void serviceDied(uint64_t cookie, const wp& who) override; + /** + * Setup vendor tags for this provider + */ + status_t setUpVendorTags(); + // Basic device information, common to all camera devices struct DeviceInfo { const std::string mName; // Full instance name -- GitLab From b7bd4383c9dbebc0d094b33cfe0fc4b8f9b313b2 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Thu, 15 Nov 2018 17:19:48 -0800 Subject: [PATCH 0572/1530] Camera: Remove superfluous hardware::camera Test: Ensure camera works as before Bug: 79374634 Change-Id: Ie8f0da2ec6e6b855329983a2ec39ef2acf0f3eff --- .../common/CameraProviderManager.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index ceca0f8ac9..2b3fe9e1d3 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -263,9 +263,9 @@ status_t CameraProviderManager::setUpVendorTags() { } status_t CameraProviderManager::openSession(const std::string &id, - const sp& callback, + const sp& callback, /*out*/ - sp *session) { + sp *session) { std::lock_guard lock(mInterfaceMutex); @@ -293,9 +293,9 @@ status_t CameraProviderManager::openSession(const std::string &id, } status_t CameraProviderManager::openSession(const std::string &id, - const sp& callback, + const sp& callback, /*out*/ - sp *session) { + sp *session) { std::lock_guard lock(mInterfaceMutex); @@ -711,8 +711,7 @@ status_t CameraProviderManager::ProviderInfo::initialize() { sp listener = mManager->getStatusListener(); for (auto& device : devices) { std::string id; - status_t res = addDevice(device, - hardware::camera::common::V1_0::CameraDeviceStatus::PRESENT, &id); + status_t res = addDevice(device, common::V1_0::CameraDeviceStatus::PRESENT, &id); if (res != OK) { ALOGE("%s: Unable to enumerate camera device '%s': %s (%d)", __FUNCTION__, device.c_str(), strerror(-res), res); @@ -1275,7 +1274,7 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& ALOGV("%s: Unable to convert ICameraDevice instance to version 3.5", __FUNCTION__); return; } - sp interface_3_5 = castResult; + sp interface_3_5 = castResult; if (interface_3_5 == nullptr) { ALOGE("%s: Converted ICameraDevice instance to nullptr", __FUNCTION__); return; @@ -1686,7 +1685,7 @@ const char* CameraProviderManager::torchStatusToString(const TorchModeStatus& s) status_t HidlVendorTagDescriptor::createDescriptorFromHidl( - const hardware::hidl_vec& vts, + const hardware::hidl_vec& vts, /*out*/ sp& descriptor) { @@ -1714,7 +1713,7 @@ status_t HidlVendorTagDescriptor::createDescriptorFromHidl( int idx = 0; for (size_t s = 0; s < vts.size(); s++) { - const hardware::camera::common::V1_0::VendorTagSection& section = vts[s]; + const common::V1_0::VendorTagSection& section = vts[s]; const char *sectionName = section.sectionName.c_str(); if (sectionName == NULL) { ALOGE("%s: no section name defined for vendor tag section %zu.", __FUNCTION__, s); -- GitLab From 8c928088feadc57b40ff08d048e1c1e289b1b654 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 7 Dec 2018 16:05:22 -0800 Subject: [PATCH 0573/1530] Convert AAC, AMR and Midi Extractors to V3 format Bug: 111407253 Test: manual, CTS Change-Id: Id2168fa3dffb070538592fe2c28c285b1a2bf081 --- media/extractors/aac/AACExtractor.cpp | 35 +++++++++----------- media/extractors/aac/AACExtractor.h | 4 +-- media/extractors/aac/Android.bp | 1 - media/extractors/amr/AMRExtractor.cpp | 33 +++++++++---------- media/extractors/amr/AMRExtractor.h | 4 +-- media/extractors/amr/Android.bp | 1 - media/extractors/midi/Android.bp | 1 - media/extractors/midi/MidiExtractor.cpp | 43 ++++++++++++------------- media/extractors/midi/MidiExtractor.h | 10 +++--- 9 files changed, 59 insertions(+), 73 deletions(-) diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index f278107a95..9384ebf0ec 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -31,7 +31,7 @@ namespace android { -class AACSource : public MediaTrackHelperV2 { +class AACSource : public MediaTrackHelperV3 { public: AACSource( DataSourceHelper *source, @@ -45,7 +45,7 @@ public: virtual media_status_t getFormat(AMediaFormat*); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~AACSource(); @@ -58,7 +58,6 @@ private: off64_t mOffset; int64_t mCurrentTimeUs; bool mStarted; - MediaBufferGroup *mGroup; Vector mOffsetVector; int64_t mFrameDurationUs; @@ -196,7 +195,7 @@ size_t AACExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV2 *AACExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *AACExtractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -227,7 +226,6 @@ AACSource::AACSource( mOffset(0), mCurrentTimeUs(0), mStarted(false), - mGroup(NULL), mOffsetVector(offset_vector), mFrameDurationUs(frame_duration_us) { } @@ -248,8 +246,7 @@ media_status_t AACSource::start() { } mCurrentTimeUs = 0; - mGroup = new MediaBufferGroup; - mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize)); + mBufferGroup->add_buffer(kMaxFrameSize); mStarted = true; return AMEDIA_OK; @@ -258,9 +255,6 @@ media_status_t AACSource::start() { media_status_t AACSource::stop() { CHECK(mStarted); - delete mGroup; - mGroup = NULL; - mStarted = false; return AMEDIA_OK; } @@ -270,7 +264,7 @@ media_status_t AACSource::getFormat(AMediaFormat *meta) { } media_status_t AACSource::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -293,8 +287,8 @@ media_status_t AACSource::read( return AMEDIA_ERROR_END_OF_STREAM; } - MediaBufferBase *buffer; - status_t err = mGroup->acquire_buffer(&buffer); + MediaBufferHelperV3 *buffer; + status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; } @@ -309,8 +303,9 @@ media_status_t AACSource::read( } buffer->set_range(0, frameSizeWithoutHeader); - buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs); - buffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, mCurrentTimeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); mOffset += frameSize; mCurrentTimeUs += mFrameDurationUs; @@ -321,14 +316,14 @@ media_status_t AACSource::read( //////////////////////////////////////////////////////////////////////////////// -static CMediaExtractorV2* CreateExtractor( +static CMediaExtractorV3* CreateExtractor( CDataSource *source, void *meta) { off64_t offset = *static_cast(meta); - return wrapV2(new AACExtractor(new DataSourceHelper(source), offset)); + return wrapV3(new AACExtractor(new DataSourceHelper(source), offset)); } -static CreatorFuncV2 Sniff( +static CreatorFuncV3 Sniff( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; @@ -388,11 +383,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("4fd80eae-03d2-4d72-9eb9-48fa6bb54613"), 1, // version "AAC Extractor", - { .v2 = Sniff } + { .v3 = Sniff } }; } diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h index 3b11f95beb..be33bf566e 100644 --- a/media/extractors/aac/AACExtractor.h +++ b/media/extractors/aac/AACExtractor.h @@ -29,12 +29,12 @@ namespace android { struct AMessage; class String8; -class AACExtractor : public MediaExtractorPluginHelperV2 { +class AACExtractor : public MediaExtractorPluginHelperV3 { public: AACExtractor(DataSourceHelper *source, off64_t offset); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp index a5e91bb9bc..a58167a655 100644 --- a/media/extractors/aac/Android.bp +++ b/media/extractors/aac/Android.bp @@ -8,7 +8,6 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index 511b14d48a..7fd2a417ec 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -29,7 +29,7 @@ namespace android { -class AMRSource : public MediaTrackHelperV2 { +class AMRSource : public MediaTrackHelperV3 { public: AMRSource( DataSourceHelper *source, @@ -44,7 +44,7 @@ public: virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~AMRSource(); @@ -209,7 +209,7 @@ size_t AMRExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV2 *AMRExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *AMRExtractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -255,8 +255,7 @@ media_status_t AMRSource::start() { mOffset = mIsWide ? 9 : 6; mCurrentTimeUs = 0; - mGroup = new MediaBufferGroup; - mGroup->add_buffer(MediaBufferBase::Create(128)); + mBufferGroup->add_buffer(128); mStarted = true; return AMEDIA_OK; @@ -265,9 +264,6 @@ media_status_t AMRSource::start() { media_status_t AMRSource::stop() { CHECK(mStarted); - delete mGroup; - mGroup = NULL; - mStarted = false; return AMEDIA_OK; } @@ -277,7 +273,7 @@ media_status_t AMRSource::getFormat(AMediaFormat *meta) { } media_status_t AMRSource::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -326,8 +322,8 @@ media_status_t AMRSource::read( return AMEDIA_ERROR_MALFORMED; } - MediaBufferBase *buffer; - status_t err = mGroup->acquire_buffer(&buffer); + MediaBufferHelperV3 *buffer; + status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; } @@ -348,8 +344,9 @@ media_status_t AMRSource::read( } buffer->set_range(0, frameSize); - buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs); - buffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, mCurrentTimeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); mOffset += frameSize; mCurrentTimeUs += 20000; // Each frame is 20ms @@ -366,22 +363,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("c86639c9-2f31-40ac-a715-fa01b4493aaf"), 1, "AMR Extractor", { - .v2 = []( + .v3 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV2 { + FreeMetaFunc *) -> CreatorFuncV3 { DataSourceHelper helper(source); if (SniffAMR(&helper, nullptr, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV2* { - return wrapV2(new AMRExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new AMRExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h index 44b2cbd40b..b50ce8122a 100644 --- a/media/extractors/amr/AMRExtractor.h +++ b/media/extractors/amr/AMRExtractor.h @@ -29,12 +29,12 @@ struct AMessage; class String8; #define OFFSET_TABLE_LEN 300 -class AMRExtractor : public MediaExtractorPluginHelperV2 { +class AMRExtractor : public MediaExtractorPluginHelperV3 { public: explicit AMRExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp index 0791fa378f..4bd933deb5 100644 --- a/media/extractors/amr/Android.bp +++ b/media/extractors/amr/Android.bp @@ -8,7 +8,6 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp index 937e545a16..7d42e703f3 100644 --- a/media/extractors/midi/Android.bp +++ b/media/extractors/midi/Android.bp @@ -8,7 +8,6 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index cd39cec0e0..43f394c63a 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -25,15 +25,14 @@ #include #include #include -#include #include namespace android { -// how many Sonivox output buffers to aggregate into one MediaBufferBase +// how many Sonivox output buffers to aggregate into one MediaBuffer static const int NUM_COMBINE_BUFFERS = 4; -class MidiSource : public MediaTrackHelperV2 { +class MidiSource : public MediaTrackHelperV3 { public: MidiSource( @@ -45,7 +44,7 @@ public: virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~MidiSource(); @@ -93,7 +92,7 @@ media_status_t MidiSource::start() CHECK(!mStarted); mStarted = true; - mEngine.allocateBuffers(); + mEngine.allocateBuffers(mBufferGroup); return AMEDIA_OK; } @@ -114,10 +113,10 @@ media_status_t MidiSource::getFormat(AMediaFormat *meta) } media_status_t MidiSource::read( - MediaBufferBase **outBuffer, const ReadOptions *options) + MediaBufferHelperV3 **outBuffer, const ReadOptions *options) { ALOGV("MidiSource::read"); - MediaBufferBase *buffer; + MediaBufferHelperV3 *buffer; // process an optional seek request int64_t seekTimeUs; ReadOptions::SeekMode mode; @@ -144,7 +143,6 @@ status_t MidiSource::init() MidiEngine::MidiEngine(CDataSource *dataSource, AMediaFormat *fileMetadata, AMediaFormat *trackMetadata) : - mGroup(NULL), mEasData(NULL), mEasHandle(NULL), mEasConfig(NULL), @@ -194,7 +192,6 @@ MidiEngine::~MidiEngine() { if (mEasData) { EAS_Shutdown(mEasData); } - delete mGroup; delete mIoWrapper; } @@ -202,22 +199,20 @@ status_t MidiEngine::initCheck() { return mIsInitialized ? OK : UNKNOWN_ERROR; } -status_t MidiEngine::allocateBuffers() { +status_t MidiEngine::allocateBuffers(MediaBufferGroupHelperV3 *group) { // select reverb preset and enable EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); - mGroup = new MediaBufferGroup; int bufsize = sizeof(EAS_PCM) * mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS; ALOGV("using %d byte buffer", bufsize); - mGroup->add_buffer(MediaBufferBase::Create(bufsize)); + mGroup = group; + mGroup->add_buffer(bufsize); return OK; } status_t MidiEngine::releaseBuffers() { - delete mGroup; - mGroup = NULL; return OK; } @@ -227,13 +222,13 @@ status_t MidiEngine::seekTo(int64_t positionUs) { return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR; } -MediaBufferBase* MidiEngine::readBuffer() { +MediaBufferHelperV3* MidiEngine::readBuffer() { EAS_STATE state; EAS_State(mEasData, mEasHandle, &state); if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) { return NULL; } - MediaBufferBase *buffer; + MediaBufferHelperV3 *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { ALOGE("readBuffer: no buffer"); @@ -242,7 +237,9 @@ MediaBufferBase* MidiEngine::readBuffer() { EAS_I32 timeMs; EAS_GetLocation(mEasData, mEasHandle, &timeMs); int64_t timeUs = 1000ll * timeMs; - buffer->meta_data().setInt64(kKeyTime, timeUs); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); EAS_PCM* p = (EAS_PCM*) buffer->data(); int numBytesOutput = 0; @@ -289,7 +286,7 @@ size_t MidiExtractor::countTracks() return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV2 *MidiExtractor::getTrack(size_t index) +MediaTrackHelperV3 *MidiExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; @@ -334,21 +331,21 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("ef6cca0a-f8a2-43e6-ba5f-dfcd7c9a7ef2"), 1, "MIDI Extractor", { - .v2 = []( + .v3 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV2 { + FreeMetaFunc *) -> CreatorFuncV3 { if (SniffMidi(source, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV2* { - return wrapV2(new MidiExtractor(source));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new MidiExtractor(source));}; } return NULL; } diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h index 412d6eb230..ad345b8b1a 100644 --- a/media/extractors/midi/MidiExtractor.h +++ b/media/extractors/midi/MidiExtractor.h @@ -38,26 +38,26 @@ public: status_t initCheck(); - status_t allocateBuffers(); + status_t allocateBuffers(MediaBufferGroupHelperV3 *group); status_t releaseBuffers(); status_t seekTo(int64_t positionUs); - MediaBufferBase* readBuffer(); + MediaBufferHelperV3* readBuffer(); private: MidiIoWrapper *mIoWrapper; - MediaBufferGroup *mGroup; + MediaBufferGroupHelperV3 *mGroup; EAS_DATA_HANDLE mEasData; EAS_HANDLE mEasHandle; const S_EAS_LIB_CONFIG* mEasConfig; bool mIsInitialized; }; -class MidiExtractor : public MediaExtractorPluginHelperV2 { +class MidiExtractor : public MediaExtractorPluginHelperV3 { public: explicit MidiExtractor(CDataSource *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); -- GitLab From a29c135cab39ff25d8394d0fa31e0ab2c0d08d6a Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Wed, 10 Oct 2018 12:05:42 -0700 Subject: [PATCH 0574/1530] Camera: Support lazy HALs Drop camera HAL references when unused. Use ro.camera.enableLazyHal property to toggle behavior on or off. * If true, the cameraserver drops references to ICameraProvider HALs when they are not being used for either camera or torch. * If false (or unset), stores a strong reference to each ICameraProvider that is registered. This is the same as the old behavior. Bug: 79374634 Test: Apply CL that lets camera HAL exit when unused. Open camera/enable torch, close camera/disable torch, check ps -A to see if HAL exits. Change-Id: I1842f9bf9e862ab74e4ec8aa72fc46fc47782ed0 --- .../camera/libcameraservice/CameraService.cpp | 2 + .../common/CameraProviderManager.cpp | 335 ++++++++++++++---- .../common/CameraProviderManager.h | 99 +++++- 3 files changed, 352 insertions(+), 84 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 32b07fa493..6003607600 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -2215,6 +2215,8 @@ binder::Status CameraService::BasicClient::disconnect() { sCameraService->removeByClient(this); sCameraService->logDisconnected(mCameraIdStr, mClientPid, String8(mClientPackageName)); + sCameraService->mCameraProviderManager->removeRef(CameraProviderManager::DeviceMode::CAMERA, + mCameraIdStr.c_str()); sp remote = getRemote(); if (remote != nullptr) { diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 2b3fe9e1d3..a9cbe72926 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -24,23 +24,37 @@ #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include namespace android { using namespace ::android::hardware::camera; using namespace ::android::hardware::camera::common::V1_0; +using std::literals::chrono_literals::operator""s; namespace { // Hardcoded name for the passthrough HAL implementation, since it can't be discovered via the // service manager const std::string kLegacyProviderName("legacy/0"); const std::string kExternalProviderName("external/0"); +const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false)); + +// The extra amount of time to hold a reference to an ICameraProvider after it is no longer needed. +// Hold the reference for this extra time so that if the camera is unreferenced and then referenced +// again quickly, we do not let the HAL exit and then need to immediately restart it. An example +// when this could happen is switching from a front-facing to a rear-facing camera. If the HAL were +// to exit during the camera switch, the camera could appear janky to the user. +const std::chrono::system_clock::duration kCameraKeepAliveDelay = 3s; } // anonymous namespace @@ -74,6 +88,8 @@ status_t CameraProviderManager::initialize(wpflushCommands(); + return OK; } @@ -219,26 +235,15 @@ status_t CameraProviderManager::getHighestSupportedVersion(const std::string &id return OK; } -bool CameraProviderManager::supportSetTorchMode(const std::string &id) { +bool CameraProviderManager::supportSetTorchMode(const std::string &id) const { std::lock_guard lock(mInterfaceMutex); - bool support = false; for (auto& provider : mProviders) { auto deviceInfo = findDeviceInfoLocked(id); if (deviceInfo != nullptr) { - auto ret = provider->mInterface->isSetTorchModeSupported( - [&support](auto status, bool supported) { - if (status == Status::OK) { - support = supported; - } - }); - if (!ret.isOk()) { - ALOGE("%s: Transaction error checking torch mode support '%s': %s", - __FUNCTION__, provider->mProviderName.c_str(), ret.description().c_str()); - } - break; + return provider->mSetTorchModeSupported; } } - return support; + return false; } status_t CameraProviderManager::setTorchMode(const std::string &id, bool enabled) { @@ -247,6 +252,15 @@ status_t CameraProviderManager::setTorchMode(const std::string &id, bool enabled auto deviceInfo = findDeviceInfoLocked(id); if (deviceInfo == nullptr) return NAME_NOT_FOUND; + // Pass the camera ID to start interface so that it will save it to the map of ICameraProviders + // that are currently in use. + const sp interface = + deviceInfo->mParentProvider->startProviderInterface(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + saveRef(DeviceMode::TORCH, deviceInfo->mId, interface); + return deviceInfo->setTorchMode(enabled); } @@ -274,10 +288,22 @@ status_t CameraProviderManager::openSession(const std::string &id, if (deviceInfo == nullptr) return NAME_NOT_FOUND; auto *deviceInfo3 = static_cast(deviceInfo); + const sp provider = + deviceInfo->mParentProvider->startProviderInterface(); + if (provider == nullptr) { + return DEAD_OBJECT; + } + saveRef(DeviceMode::CAMERA, id, provider); Status status; hardware::Return ret; - ret = deviceInfo3->mInterface->open(callback, [&status, &session] + auto interface = deviceInfo3->startDeviceInterface< + CameraProviderManager::ProviderInfo::DeviceInfo3::InterfaceT>(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + + ret = interface->open(callback, [&status, &session] (Status s, const sp& cameraSession) { status = s; if (status == Status::OK) { @@ -285,6 +311,7 @@ status_t CameraProviderManager::openSession(const std::string &id, } }); if (!ret.isOk()) { + removeRef(DeviceMode::CAMERA, id); ALOGE("%s: Transaction error opening a session for camera device %s: %s", __FUNCTION__, id.c_str(), ret.description().c_str()); return DEAD_OBJECT; @@ -304,19 +331,82 @@ status_t CameraProviderManager::openSession(const std::string &id, if (deviceInfo == nullptr) return NAME_NOT_FOUND; auto *deviceInfo1 = static_cast(deviceInfo); + const sp provider = + deviceInfo->mParentProvider->startProviderInterface(); + if (provider == nullptr) { + return DEAD_OBJECT; + } + saveRef(DeviceMode::CAMERA, id, provider); - hardware::Return status = deviceInfo1->mInterface->open(callback); + auto interface = deviceInfo1->startDeviceInterface< + CameraProviderManager::ProviderInfo::DeviceInfo1::InterfaceT>(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + hardware::Return status = interface->open(callback); if (!status.isOk()) { + removeRef(DeviceMode::CAMERA, id); ALOGE("%s: Transaction error opening a session for camera device %s: %s", __FUNCTION__, id.c_str(), status.description().c_str()); return DEAD_OBJECT; } if (status == Status::OK) { - *session = deviceInfo1->mInterface; + *session = interface; } return mapToStatusT(status); } +void CameraProviderManager::saveRef(DeviceMode usageType, const std::string &cameraId, + sp provider) { + if (!kEnableLazyHal) { + return; + } + ALOGI("Saving camera provider %s for camera device %s", provider->descriptor, cameraId.c_str()); + std::lock_guard lock(mProviderInterfaceMapLock); + std::unordered_map> *primaryMap, *alternateMap; + if (usageType == DeviceMode::TORCH) { + primaryMap = &mTorchProviderByCameraId; + alternateMap = &mCameraProviderByCameraId; + } else { + primaryMap = &mCameraProviderByCameraId; + alternateMap = &mTorchProviderByCameraId; + } + auto id = cameraId.c_str(); + (*primaryMap)[id] = provider; + auto search = alternateMap->find(id); + if (search != alternateMap->end()) { + ALOGW("%s: Camera device %s is using both torch mode and camera mode simultaneously. " + "That should not be possible", __FUNCTION__, id); + } + ALOGV("%s: Camera device %s connected", __FUNCTION__, id); +} + +void CameraProviderManager::removeRef(DeviceMode usageType, const std::string &cameraId) { + if (!kEnableLazyHal) { + return; + } + ALOGI("Removing camera device %s", cameraId.c_str()); + std::unordered_map> *providerMap; + if (usageType == DeviceMode::TORCH) { + providerMap = &mTorchProviderByCameraId; + } else { + providerMap = &mCameraProviderByCameraId; + } + std::lock_guard lock(mProviderInterfaceMapLock); + auto search = providerMap->find(cameraId.c_str()); + if (search != providerMap->end()) { + auto ptr = search->second; + auto future = std::async(std::launch::async, [ptr] { + std::this_thread::sleep_for(kCameraKeepAliveDelay); + IPCThreadState::self()->flushCommands(); + }); + providerMap->erase(cameraId.c_str()); + } else { + ALOGE("%s: Asked to remove reference for camera %s, but no reference to it was found. This " + "could mean removeRef was called twice for the same camera ID.", __FUNCTION__, + cameraId.c_str()); + } +} hardware::Return CameraProviderManager::onRegistration( const hardware::hidl_string& /*fqName*/, @@ -334,6 +424,8 @@ hardware::Return CameraProviderManager::onRegistration( listener->onNewProviderRegistered(); } + IPCThreadState::self()->flushCommands(); + return hardware::Return(); } @@ -581,9 +673,8 @@ status_t CameraProviderManager::addProviderLocked(const std::string& newProvider } } - sp providerInfo = - new ProviderInfo(newProvider, interface, this); - status_t res = providerInfo->initialize(); + sp providerInfo = new ProviderInfo(newProvider, this); + status_t res = providerInfo->initialize(interface); if (res != OK) { return res; } @@ -635,27 +726,26 @@ sp CameraProviderManager::getStatusListen CameraProviderManager::ProviderInfo::ProviderInfo( const std::string &providerName, - sp& interface, CameraProviderManager *manager) : mProviderName(providerName), - mInterface(interface), mProviderTagid(generateVendorTagId(providerName)), mUniqueDeviceCount(0), mManager(manager) { (void) mManager; } -status_t CameraProviderManager::ProviderInfo::initialize() { +status_t CameraProviderManager::ProviderInfo::initialize( + sp& interface) { status_t res = parseProviderName(mProviderName, &mType, &mId); if (res != OK) { ALOGE("%s: Invalid provider name, ignoring", __FUNCTION__); return BAD_VALUE; } ALOGI("Connecting to new camera provider: %s, isRemote? %d", - mProviderName.c_str(), mInterface->isRemote()); + mProviderName.c_str(), interface->isRemote()); // cameraDeviceStatusChange callbacks may be called (and causing new devices added) // before setCallback returns - hardware::Return status = mInterface->setCallback(this); + hardware::Return status = interface->setCallback(this); if (!status.isOk()) { ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s", __FUNCTION__, mProviderName.c_str(), status.description().c_str()); @@ -667,7 +757,7 @@ status_t CameraProviderManager::ProviderInfo::initialize() { return mapToStatusT(status); } - hardware::Return linked = mInterface->linkToDeath(this, /*cookie*/ mId); + hardware::Return linked = interface->linkToDeath(this, /*cookie*/ mId); if (!linked.isOk()) { ALOGE("%s: Transaction error in linking to camera provider '%s' death: %s", __FUNCTION__, mProviderName.c_str(), linked.description().c_str()); @@ -679,7 +769,7 @@ status_t CameraProviderManager::ProviderInfo::initialize() { // Get initial list of camera devices, if any std::vector devices; - hardware::Return ret = mInterface->getCameraIdList([&status, this, &devices]( + hardware::Return ret = interface->getCameraIdList([&status, this, &devices]( Status idStatus, const hardware::hidl_vec& cameraDeviceNames) { status = idStatus; @@ -708,6 +798,20 @@ status_t CameraProviderManager::ProviderInfo::initialize() { return mapToStatusT(status); } + ret = interface->isSetTorchModeSupported( + [this](auto status, bool supported) { + if (status == Status::OK) { + mSetTorchModeSupported = supported; + } + }); + if (!ret.isOk()) { + ALOGE("%s: Transaction error checking torch mode support '%s': %s", + __FUNCTION__, mProviderName.c_str(), ret.description().c_str()); + return DEAD_OBJECT; + } + + mIsRemote = interface->isRemote(); + sp listener = mManager->getStatusListener(); for (auto& device : devices) { std::string id; @@ -730,9 +834,42 @@ status_t CameraProviderManager::ProviderInfo::initialize() { mProviderName.c_str(), mDevices.size()); mInitialized = true; + if (!kEnableLazyHal) { + // Save HAL reference indefinitely + mSavedInterface = interface; + } return OK; } +const sp +CameraProviderManager::ProviderInfo::startProviderInterface() { + ATRACE_CALL(); + ALOGI("Request to start camera provider: %s", mProviderName.c_str()); + if (mSavedInterface != nullptr) { + return mSavedInterface; + } + auto interface = mActiveInterface.promote(); + if (interface == nullptr) { + ALOGI("Could not promote, calling getService(%s)", mProviderName.c_str()); + interface = mManager->mServiceProxy->getService(mProviderName); + interface->setCallback(this); + hardware::Return linked = interface->linkToDeath(this, /*cookie*/ mId); + if (!linked.isOk()) { + ALOGE("%s: Transaction error in linking to camera provider '%s' death: %s", + __FUNCTION__, mProviderName.c_str(), linked.description().c_str()); + mManager->removeProvider(mProviderName); + return nullptr; + } else if (!linked) { + ALOGW("%s: Unable to link to provider '%s' death notifications", + __FUNCTION__, mProviderName.c_str()); + } + mActiveInterface = interface; + } else { + ALOGI("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str()); + } + return interface; +} + const std::string& CameraProviderManager::ProviderInfo::getType() const { return mType; } @@ -814,7 +951,7 @@ void CameraProviderManager::ProviderInfo::removeDevice(std::string id) { status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector&) const { dprintf(fd, "== Camera Provider HAL %s (v2.4, %s) static info: %zu devices: ==\n", - mProviderName.c_str(), mInterface->isRemote() ? "remote" : "passthrough", + mProviderName.c_str(), mIsRemote ? "remote" : "passthrough", mDevices.size()); for (auto& device : mDevices) { @@ -942,6 +1079,9 @@ hardware::Return CameraProviderManager::ProviderInfo::torchModeStatusChang torchStatusToString(newStatus)); id = deviceInfo->mId; known = true; + if (TorchModeStatus::AVAILABLE_ON != newStatus) { + mManager->removeRef(DeviceMode::TORCH, id); + } break; } } @@ -977,7 +1117,11 @@ status_t CameraProviderManager::ProviderInfo::setUpVendorTags() { hardware::hidl_vec vts; Status status; hardware::Return ret; - ret = mInterface->getVendorTags( + const sp interface = startProviderInterface(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + ret = interface->getVendorTags( [&](auto s, const auto& vendorTagSecs) { status = s; if (s == Status::OK) { @@ -1010,11 +1154,11 @@ template std::unique_ptr CameraProviderManager::ProviderInfo::initializeDeviceInfo( const std::string &name, const metadata_vendor_id_t tagId, - const std::string &id, uint16_t minorVersion) const { + const std::string &id, uint16_t minorVersion) { Status status; auto cameraInterface = - getDeviceInterface(name); + startDeviceInterface(name); if (cameraInterface == nullptr) return nullptr; CameraResourceCost resourceCost; @@ -1041,13 +1185,13 @@ std::unique_ptr } return std::unique_ptr( - new DeviceInfoT(name, tagId, id, minorVersion, resourceCost, + new DeviceInfoT(name, tagId, id, minorVersion, resourceCost, this, mProviderPublicCameraIds, cameraInterface)); } template sp -CameraProviderManager::ProviderInfo::getDeviceInterface(const std::string &name) const { +CameraProviderManager::ProviderInfo::startDeviceInterface(const std::string &name) { ALOGE("%s: Device %s: Unknown HIDL device HAL major version %d:", __FUNCTION__, name.c_str(), InterfaceT::version.get_major()); return nullptr; @@ -1055,12 +1199,16 @@ CameraProviderManager::ProviderInfo::getDeviceInterface(const std::string &name) template<> sp -CameraProviderManager::ProviderInfo::getDeviceInterface - (const std::string &name) const { +CameraProviderManager::ProviderInfo::startDeviceInterface + (const std::string &name) { Status status; sp cameraInterface; hardware::Return ret; - ret = mInterface->getCameraDeviceInterface_V1_x(name, [&status, &cameraInterface]( + const sp interface = startProviderInterface(); + if (interface == nullptr) { + return nullptr; + } + ret = interface->getCameraDeviceInterface_V1_x(name, [&status, &cameraInterface]( Status s, sp interface) { status = s; cameraInterface = interface; @@ -1080,12 +1228,16 @@ CameraProviderManager::ProviderInfo::getDeviceInterface template<> sp -CameraProviderManager::ProviderInfo::getDeviceInterface - (const std::string &name) const { +CameraProviderManager::ProviderInfo::startDeviceInterface + (const std::string &name) { Status status; sp cameraInterface; hardware::Return ret; - ret = mInterface->getCameraDeviceInterface_V3_x(name, [&status, &cameraInterface]( + const sp interface = startProviderInterface(); + if (interface == nullptr) { + return nullptr; + } + ret = interface->getCameraDeviceInterface_V3_x(name, [&status, &cameraInterface]( Status s, sp interface) { status = s; cameraInterface = interface; @@ -1105,6 +1257,18 @@ CameraProviderManager::ProviderInfo::getDeviceInterface CameraProviderManager::ProviderInfo::DeviceInfo::~DeviceInfo() {} +template +sp CameraProviderManager::ProviderInfo::DeviceInfo::startDeviceInterface() { + sp device; + ATRACE_CALL(); + if (mSavedInterface == nullptr) { + device = mParentProvider->startDeviceInterface(mName); + } else { + device = (InterfaceT *) mSavedInterface.get(); + } + return device; +} + template status_t CameraProviderManager::ProviderInfo::DeviceInfo::setTorchMode(InterfaceT& interface, bool enabled) { @@ -1116,31 +1280,31 @@ CameraProviderManager::ProviderInfo::DeviceInfo1::DeviceInfo1(const std::string& const metadata_vendor_id_t tagId, const std::string &id, uint16_t minorVersion, const CameraResourceCost& resourceCost, + sp parentProvider, const std::vector& publicCameraIds, sp interface) : DeviceInfo(name, tagId, id, hardware::hidl_version{1, minorVersion}, - publicCameraIds, resourceCost), - mInterface(interface) { + publicCameraIds, resourceCost, parentProvider) { // Get default parameters and initialize flash unit availability // Requires powering on the camera device - hardware::Return status = mInterface->open(nullptr); + hardware::Return status = interface->open(nullptr); if (!status.isOk()) { ALOGE("%s: Transaction error opening camera device %s to check for a flash unit: %s", - __FUNCTION__, mId.c_str(), status.description().c_str()); + __FUNCTION__, id.c_str(), status.description().c_str()); return; } if (status != Status::OK) { ALOGE("%s: Unable to open camera device %s to check for a flash unit: %s", __FUNCTION__, - mId.c_str(), CameraProviderManager::statusToString(status)); + id.c_str(), CameraProviderManager::statusToString(status)); return; } hardware::Return ret; - ret = mInterface->getParameters([this](const hardware::hidl_string& parms) { + ret = interface->getParameters([this](const hardware::hidl_string& parms) { mDefaultParameters.unflatten(String8(parms.c_str())); }); if (!ret.isOk()) { ALOGE("%s: Transaction error reading camera device %s params to check for a flash unit: %s", - __FUNCTION__, mId.c_str(), status.description().c_str()); + __FUNCTION__, id.c_str(), status.description().c_str()); return; } const char *flashMode = @@ -1149,27 +1313,43 @@ CameraProviderManager::ProviderInfo::DeviceInfo1::DeviceInfo1(const std::string& mHasFlashUnit = true; } - ret = mInterface->close(); + status_t res = cacheCameraInfo(interface); + if (res != OK) { + ALOGE("%s: Could not cache CameraInfo", __FUNCTION__); + return; + } + + ret = interface->close(); if (!ret.isOk()) { ALOGE("%s: Transaction error closing camera device %s after check for a flash unit: %s", - __FUNCTION__, mId.c_str(), status.description().c_str()); + __FUNCTION__, id.c_str(), status.description().c_str()); + } + + if (!kEnableLazyHal) { + // Save HAL reference indefinitely + mSavedInterface = interface; } } CameraProviderManager::ProviderInfo::DeviceInfo1::~DeviceInfo1() {} status_t CameraProviderManager::ProviderInfo::DeviceInfo1::setTorchMode(bool enabled) { - return DeviceInfo::setTorchMode(mInterface, enabled); + return setTorchModeForDevice(enabled); } status_t CameraProviderManager::ProviderInfo::DeviceInfo1::getCameraInfo( hardware::CameraInfo *info) const { if (info == nullptr) return BAD_VALUE; + *info = mInfo; + return OK; +} +status_t CameraProviderManager::ProviderInfo::DeviceInfo1::cacheCameraInfo( + sp interface) { Status status; device::V1_0::CameraInfo cInfo; hardware::Return ret; - ret = mInterface->getCameraInfo([&status, &cInfo](Status s, device::V1_0::CameraInfo camInfo) { + ret = interface->getCameraInfo([&status, &cInfo](Status s, device::V1_0::CameraInfo camInfo) { status = s; cInfo = camInfo; }); @@ -1184,27 +1364,31 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo1::getCameraInfo( switch(cInfo.facing) { case device::V1_0::CameraFacing::BACK: - info->facing = hardware::CAMERA_FACING_BACK; + mInfo.facing = hardware::CAMERA_FACING_BACK; break; case device::V1_0::CameraFacing::EXTERNAL: // Map external to front for legacy API case device::V1_0::CameraFacing::FRONT: - info->facing = hardware::CAMERA_FACING_FRONT; + mInfo.facing = hardware::CAMERA_FACING_FRONT; break; default: ALOGW("%s: Device %s: Unknown camera facing: %d", __FUNCTION__, mId.c_str(), cInfo.facing); - info->facing = hardware::CAMERA_FACING_BACK; + mInfo.facing = hardware::CAMERA_FACING_BACK; } - info->orientation = cInfo.orientation; + mInfo.orientation = cInfo.orientation; return OK; } -status_t CameraProviderManager::ProviderInfo::DeviceInfo1::dumpState(int fd) const { +status_t CameraProviderManager::ProviderInfo::DeviceInfo1::dumpState(int fd) { native_handle_t* handle = native_handle_create(1,0); handle->data[0] = fd; - hardware::Return s = mInterface->dumpState(handle); + const sp interface = startDeviceInterface(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + hardware::Return s = interface->dumpState(handle); native_handle_delete(handle); if (!s.isOk()) { return INVALID_OPERATION; @@ -1216,15 +1400,15 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& const metadata_vendor_id_t tagId, const std::string &id, uint16_t minorVersion, const CameraResourceCost& resourceCost, + sp parentProvider, const std::vector& publicCameraIds, sp interface) : DeviceInfo(name, tagId, id, hardware::hidl_version{3, minorVersion}, - publicCameraIds, resourceCost), - mInterface(interface) { + publicCameraIds, resourceCost, parentProvider) { // Get camera characteristics and initialize flash unit availability Status status; hardware::Return ret; - ret = mInterface->getCameraCharacteristics([&status, this](Status s, + ret = interface->getCameraCharacteristics([&status, this](Status s, device::V3_2::CameraMetadata metadata) { status = s; if (s == Status::OK) { @@ -1243,13 +1427,13 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& }); if (!ret.isOk()) { ALOGE("%s: Transaction error getting camera characteristics for device %s" - " to check for a flash unit: %s", __FUNCTION__, mId.c_str(), + " to check for a flash unit: %s", __FUNCTION__, id.c_str(), ret.description().c_str()); return; } if (status != Status::OK) { ALOGE("%s: Unable to get camera characteristics for device %s: %s (%d)", - __FUNCTION__, mId.c_str(), CameraProviderManager::statusToString(status), status); + __FUNCTION__, id.c_str(), CameraProviderManager::statusToString(status), status); return; } status_t res = fixupMonochromeTags(); @@ -1269,7 +1453,7 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& queryPhysicalCameraIds(); // Get physical camera characteristics if applicable - auto castResult = device::V3_5::ICameraDevice::castFrom(mInterface); + auto castResult = device::V3_5::ICameraDevice::castFrom(interface); if (!castResult.isOk()) { ALOGV("%s: Unable to convert ICameraDevice instance to version 3.5", __FUNCTION__); return; @@ -1308,7 +1492,7 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& if (!ret.isOk()) { ALOGE("%s: Transaction error getting physical camera %s characteristics for %s: %s", - __FUNCTION__, id.c_str(), mId.c_str(), ret.description().c_str()); + __FUNCTION__, id.c_str(), id.c_str(), ret.description().c_str()); return; } if (status != Status::OK) { @@ -1319,12 +1503,17 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& } } } + + if (!kEnableLazyHal) { + // Save HAL reference indefinitely + mSavedInterface = interface; + } } CameraProviderManager::ProviderInfo::DeviceInfo3::~DeviceInfo3() {} status_t CameraProviderManager::ProviderInfo::DeviceInfo3::setTorchMode(bool enabled) { - return DeviceInfo::setTorchMode(mInterface, enabled); + return setTorchModeForDevice(enabled); } status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getCameraInfo( @@ -1375,10 +1564,14 @@ bool CameraProviderManager::ProviderInfo::DeviceInfo3::isAPI1Compatible() const return isBackwardCompatible; } -status_t CameraProviderManager::ProviderInfo::DeviceInfo3::dumpState(int fd) const { +status_t CameraProviderManager::ProviderInfo::DeviceInfo3::dumpState(int fd) { native_handle_t* handle = native_handle_create(1,0); handle->data[0] = fd; - auto ret = mInterface->dumpState(handle); + const sp interface = startDeviceInterface(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + auto ret = interface->dumpState(handle); native_handle_delete(handle); if (!ret.isOk()) { return INVALID_OPERATION; @@ -1408,8 +1601,14 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getPhysicalCameraChar status_t CameraProviderManager::ProviderInfo::DeviceInfo3::isSessionConfigurationSupported( const hardware::camera::device::V3_4::StreamConfiguration &configuration, - bool *status /*out*/) const { - auto castResult = device::V3_5::ICameraDevice::castFrom(mInterface); + bool *status /*out*/) { + + const sp interface = + this->startDeviceInterface(); + if (interface == nullptr) { + return DEAD_OBJECT; + } + auto castResult = device::V3_5::ICameraDevice::castFrom(interface); sp interface_3_5 = castResult; if (interface_3_5 == nullptr) { return INVALID_OPERATION; diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 1473b8fbe7..096674314b 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -18,6 +18,7 @@ #define ANDROID_SERVERS_CAMERA_CAMERAPROVIDER_H #include +#include #include #include #include @@ -110,6 +111,14 @@ public: virtual void onNewProviderRegistered() = 0; }; + /** + * Represents the mode a camera device is currently in + */ + enum class DeviceMode { + TORCH, + CAMERA + }; + /** * Initialize the manager and give it a status listener; optionally accepts a service * interaction proxy. @@ -182,7 +191,7 @@ public: /** * Check if a given camera device support setTorchMode API. */ - bool supportSetTorchMode(const std::string &id); + bool supportSetTorchMode(const std::string &id) const; /** * Turn on or off the flashlight on a given camera device. @@ -212,6 +221,17 @@ public: /*out*/ sp *session); + /** + * Save the ICameraProvider while it is being used by a camera or torch client + */ + void saveRef(DeviceMode usageType, const std::string &cameraId, + sp provider); + + /** + * Notify that the camera or torch is no longer being used by a camera client + */ + void removeRef(DeviceMode usageType, const std::string &cameraId); + /** * IServiceNotification::onRegistration * Invoked by the hardware service manager when a new camera provider is registered @@ -259,21 +279,43 @@ private: static HardwareServiceInteractionProxy sHardwareServiceInteractionProxy; + // Mapping from CameraDevice IDs to CameraProviders. This map is used to keep the + // ICameraProvider alive while it is in use by the camera with the given ID for camera + // capabilities + std::unordered_map> + mCameraProviderByCameraId; + + // Mapping from CameraDevice IDs to CameraProviders. This map is used to keep the + // ICameraProvider alive while it is in use by the camera with the given ID for torch + // capabilities + std::unordered_map> + mTorchProviderByCameraId; + + // Lock for accessing mCameraProviderByCameraId and mTorchProviderByCameraId + std::mutex mProviderInterfaceMapLock; + struct ProviderInfo : virtual public hardware::camera::provider::V2_4::ICameraProviderCallback, virtual public hardware::hidl_death_recipient { const std::string mProviderName; - const sp mInterface; const metadata_vendor_id_t mProviderTagid; sp mVendorTagDescriptor; + bool mSetTorchModeSupported; + bool mIsRemote; + + // This pointer is used to keep a reference to the ICameraProvider that was last accessed. + wp mActiveInterface; + + sp mSavedInterface; ProviderInfo(const std::string &providerName, - sp& interface, CameraProviderManager *manager); ~ProviderInfo(); - status_t initialize(); + status_t initialize(sp& interface); + + const sp startProviderInterface(); const std::string& getType() const; @@ -308,16 +350,20 @@ private: const metadata_vendor_id_t mProviderTagid; bool mIsLogicalCamera; std::vector mPhysicalIds; + hardware::CameraInfo mInfo; + sp mSavedInterface; const hardware::camera::common::V1_0::CameraResourceCost mResourceCost; hardware::camera::common::V1_0::CameraDeviceStatus mStatus; + sp mParentProvider; + bool hasFlashUnit() const { return mHasFlashUnit; } virtual status_t setTorchMode(bool enabled) = 0; virtual status_t getCameraInfo(hardware::CameraInfo *info) const = 0; virtual bool isAPI1Compatible() const = 0; - virtual status_t dumpState(int fd) const = 0; + virtual status_t dumpState(int fd) = 0; virtual status_t getCameraCharacteristics(CameraMetadata *characteristics) const { (void) characteristics; return INVALID_OPERATION; @@ -331,19 +377,23 @@ private: virtual status_t isSessionConfigurationSupported( const hardware::camera::device::V3_4::StreamConfiguration &/*configuration*/, - bool * /*status*/) - const { + bool * /*status*/) { return INVALID_OPERATION; } + template + sp startDeviceInterface(); + DeviceInfo(const std::string& name, const metadata_vendor_id_t tagId, const std::string &id, const hardware::hidl_version& version, const std::vector& publicCameraIds, - const hardware::camera::common::V1_0::CameraResourceCost& resourceCost) : + const hardware::camera::common::V1_0::CameraResourceCost& resourceCost, + sp parentProvider) : mName(name), mId(id), mVersion(version), mProviderTagid(tagId), mIsLogicalCamera(false), mResourceCost(resourceCost), mStatus(hardware::camera::common::V1_0::CameraDeviceStatus::PRESENT), - mHasFlashUnit(false), mPublicCameraIds(publicCameraIds) {} + mParentProvider(parentProvider), mHasFlashUnit(false), + mPublicCameraIds(publicCameraIds) {} virtual ~DeviceInfo(); protected: bool mHasFlashUnit; @@ -351,6 +401,14 @@ private: template static status_t setTorchMode(InterfaceT& interface, bool enabled); + + template + status_t setTorchModeForDevice(bool enabled) { + // Don't save the ICameraProvider interface here because we assume that this was + // called from CameraProviderManager::setTorchMode(), which does save it. + const sp interface = startDeviceInterface(); + return DeviceInfo::setTorchMode(interface, enabled); + } }; std::vector> mDevices; std::unordered_set mUniqueCameraIds; @@ -366,32 +424,32 @@ private: // HALv1-specific camera fields, including the actual device interface struct DeviceInfo1 : public DeviceInfo { typedef hardware::camera::device::V1_0::ICameraDevice InterfaceT; - const sp mInterface; virtual status_t setTorchMode(bool enabled) override; virtual status_t getCameraInfo(hardware::CameraInfo *info) const override; //In case of Device1Info assume that we are always API1 compatible virtual bool isAPI1Compatible() const override { return true; } - virtual status_t dumpState(int fd) const override; + virtual status_t dumpState(int fd) override; DeviceInfo1(const std::string& name, const metadata_vendor_id_t tagId, const std::string &id, uint16_t minorVersion, const hardware::camera::common::V1_0::CameraResourceCost& resourceCost, + sp parentProvider, const std::vector& publicCameraIds, sp interface); virtual ~DeviceInfo1(); private: CameraParameters2 mDefaultParameters; + status_t cacheCameraInfo(sp interface); }; // HALv3-specific camera fields, including the actual device interface struct DeviceInfo3 : public DeviceInfo { typedef hardware::camera::device::V3_2::ICameraDevice InterfaceT; - const sp mInterface; virtual status_t setTorchMode(bool enabled) override; virtual status_t getCameraInfo(hardware::CameraInfo *info) const override; virtual bool isAPI1Compatible() const override; - virtual status_t dumpState(int fd) const override; + virtual status_t dumpState(int fd) override; virtual status_t getCameraCharacteristics( CameraMetadata *characteristics) const override; virtual status_t getPhysicalCameraCharacteristics(const std::string& physicalCameraId, @@ -399,11 +457,12 @@ private: virtual status_t isSessionConfigurationSupported( const hardware::camera::device::V3_4::StreamConfiguration &configuration, bool *status /*out*/) - const override; + override; DeviceInfo3(const std::string& name, const metadata_vendor_id_t tagId, const std::string &id, uint16_t minorVersion, const hardware::camera::common::V1_0::CameraResourceCost& resourceCost, + sp parentProvider, const std::vector& publicCameraIds, sp interface); virtual ~DeviceInfo3(); private: @@ -430,11 +489,11 @@ private: template std::unique_ptr initializeDeviceInfo(const std::string &name, const metadata_vendor_id_t tagId, const std::string &id, - uint16_t minorVersion) const; + uint16_t minorVersion); // Helper for initializeDeviceInfo to use the right CameraProvider get method. template - sp getDeviceInterface(const std::string &name) const; + sp startDeviceInterface(const std::string &name); // Parse provider instance name for type and id static status_t parseProviderName(const std::string& name, @@ -468,6 +527,14 @@ private: std::vector> mProviders; + void addProviderToMap( + const std::string &cameraId, + sp provider, + bool isTorchUsage); + void removeCameraIdFromMap( + std::unordered_map> &map, + const std::string &cameraId); + static const char* deviceStatusToString( const hardware::camera::common::V1_0::CameraDeviceStatus&); static const char* torchStatusToString( -- GitLab From 100f0121068605a10b0a254b7ee5784409d09229 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 29 Nov 2018 11:22:16 -0800 Subject: [PATCH 0575/1530] APM: Improve surround formats management, include MSD This is what this change does: 1) Renames APM::mSurroundFormats into mManualSurroundFormats and avoids modifying it in modifySurroundFormats, because multiple surround-capable devices are possible. modifySurroundFormats logic is also a bit simplified. 2) Changes the way APM::getSurroundFormats reports enabled formats, after the change 1). 3) Includes MSD module into consideration as a surround-capable device. 4) Changes APM::dump to only show mManualSurroundFormats when surround sound is enforced into manual management. 5) Adds Dolby AC-4 to the list of surround formats. 6) Declares surround formats on the devicePort of MSD. Bug: 117602867 Test: Surround Sound settings with MSD module Change-Id: I5cdd3c012a3febcf7170cc5fa7378224b3ef9e4f --- .../include/AudioPolicyConfig.h | 3 +- .../config/audio_policy_configuration.xml | 3 +- .../config/msd_audio_policy_configuration.xml | 15 ++ .../managerdefault/AudioPolicyManager.cpp | 228 +++++++++--------- .../managerdefault/AudioPolicyManager.h | 13 +- 5 files changed, 144 insertions(+), 118 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index d1a0f9b815..5099ebb94f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -176,7 +176,8 @@ public: AUDIO_FORMAT_AAC_HE_V1, AUDIO_FORMAT_AAC_HE_V2, AUDIO_FORMAT_AAC_ELD, AUDIO_FORMAT_AAC_XHE}}, {AUDIO_FORMAT_DOLBY_TRUEHD, {}}, - {AUDIO_FORMAT_E_AC3_JOC, {}}}; + {AUDIO_FORMAT_E_AC3_JOC, {}}, + {AUDIO_FORMAT_AC4, {}}}; } private: diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index 1d037c331a..b5ecbf90c7 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -198,7 +198,7 @@ - @@ -214,6 +214,7 @@ + diff --git a/services/audiopolicy/config/msd_audio_policy_configuration.xml b/services/audiopolicy/config/msd_audio_policy_configuration.xml index a811f5e29d..db17bc62d1 100644 --- a/services/audiopolicy/config/msd_audio_policy_configuration.xml +++ b/services/audiopolicy/config/msd_audio_policy_configuration.xml @@ -32,6 +32,9 @@ + @@ -50,6 +53,18 @@ + + + + desc = mOutputs.valueAt(i); if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) { @@ -526,6 +527,24 @@ sp AudioPolicyManager::findDevice( return deviceList.itemAt(0); } +audio_devices_t AudioPolicyManager::getModuleDeviceTypes( + const DeviceVector& devices, const char *moduleId) const { + sp mod = mHwModules.getModuleFromName(moduleId); + return mod != 0 ? devices.getDeviceTypesFromHwModule(mod->getHandle()) : AUDIO_DEVICE_NONE; +} + +bool AudioPolicyManager::isDeviceOfModule( + const sp& devDesc, const char *moduleId) const { + sp module = mHwModules.getModuleFromName(moduleId); + if (module != 0) { + return mAvailableOutputDevices.getDevicesFromHwModule(module->getHandle()) + .indexOf(devDesc) != NAME_NOT_FOUND + || mAvailableInputDevices.getDevicesFromHwModule(module->getHandle()) + .indexOf(devDesc) != NAME_NOT_FOUND; + } + return false; +} + void AudioPolicyManager::setPhoneState(audio_mode_t state) { ALOGV("setPhoneState() state %d", state); @@ -771,7 +790,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, routing_strategy strategy; audio_devices_t device; const audio_port_handle_t requestedDeviceId = *selectedDeviceId; - audio_devices_t msdDevice = getMsdAudioOutDeviceTypes(); + audio_devices_t msdDevice = + getModuleDeviceTypes(mAvailableOutputDevices, AUDIO_HARDWARE_MODULE_ID_MSD); // The supplied portId must be AUDIO_PORT_HANDLE_NONE if (*portId != AUDIO_PORT_HANDLE_NONE) { @@ -1084,14 +1104,6 @@ sp AudioPolicyManager::getMsdAudioInDevice() const { return 0; } -audio_devices_t AudioPolicyManager::getMsdAudioOutDeviceTypes() const { - sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); - if (msdModule != 0) { - return mAvailableOutputDevices.getDeviceTypesFromHwModule(msdModule->getHandle()); - } - return AUDIO_DEVICE_NONE; -} - const AudioPatchCollection AudioPolicyManager::getMsdPatches() const { AudioPatchCollection msdPatches; sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); @@ -2669,6 +2681,19 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) return res; } +void AudioPolicyManager::dumpManualSurroundFormats(String8 *dst) const +{ + size_t i = 0; + constexpr size_t audioFormatPrefixLen = sizeof("AUDIO_FORMAT_"); + for (const auto& fmt : mManualSurroundFormats) { + if (i++ != 0) dst->append(", "); + std::string sfmt; + FormatConverter::toString(fmt, sfmt); + dst->append(sfmt.size() >= audioFormatPrefixLen ? + sfmt.c_str() + audioFormatPrefixLen - 1 : sfmt.c_str()); + } +} + void AudioPolicyManager::dump(String8 *dst) const { dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this); @@ -2682,8 +2707,15 @@ void AudioPolicyManager::dump(String8 *dst) const "HDMI system audio", "encoded surround output", "vibrate ringing" }; for (audio_policy_force_use_t i = AUDIO_POLICY_FORCE_FOR_COMMUNICATION; i < AUDIO_POLICY_FORCE_USE_CNT; i = (audio_policy_force_use_t)((int)i + 1)) { - dst->appendFormat(" Force use for %s: %d\n", - forceUses[i], mEngine->getForceUse(i)); + audio_policy_forced_cfg_t forceUseValue = mEngine->getForceUse(i); + dst->appendFormat(" Force use for %s: %d", forceUses[i], forceUseValue); + if (i == AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND && + forceUseValue == AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) { + dst->append(" (MANUAL: "); + dumpManualSurroundFormats(dst); + dst->append(")"); + } + dst->append("\n"); } dst->appendFormat(" TTS output %savailable\n", mTtsOutputAvailable ? "" : "not "); dst->appendFormat(" Master mono: %s\n", mMasterMono ? "on" : "off"); @@ -2698,17 +2730,6 @@ void AudioPolicyManager::dump(String8 *dst) const mAudioPatches.dump(dst); mPolicyMixes.dump(dst); mAudioSources.dump(dst); - if (!mSurroundFormats.empty()) { - dst->append("\nEnabled Surround Formats:\n"); - size_t i = 0; - for (const auto& fmt : mSurroundFormats) { - dst->append(i++ == 0 ? " " : ", "); - std::string sfmt; - FormatConverter::toString(fmt, sfmt); - dst->append(sfmt.c_str()); - } - dst->append("\n"); - } } status_t AudioPolicyManager::dump(int fd) @@ -3568,63 +3589,56 @@ status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats (surroundFormats == NULL || surroundFormatsEnabled == NULL))) { return BAD_VALUE; } - ALOGV("getSurroundFormats() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p" - " reported %d", *numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported); - - // Only return value if there is HDMI output. - if ((mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_HDMI) == 0) { - return INVALID_OPERATION; - } + ALOGV("%s() numSurroundFormats %d surroundFormats %p surroundFormatsEnabled %p reported %d", + __func__, *numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported); size_t formatsWritten = 0; size_t formatsMax = *numSurroundFormats; - *numSurroundFormats = 0; - std::unordered_set formats; + std::unordered_set formats; // Uses primary surround formats only if (reported) { - // Return formats from HDMI profiles, that have already been resolved by + // Return formats from all device profiles that have already been resolved by // checkOutputsForDevice(). - DeviceVector hdmiOutputDevs = mAvailableOutputDevices.getDevicesFromTypeMask( - AUDIO_DEVICE_OUT_HDMI); - for (size_t i = 0; i < hdmiOutputDevs.size(); i++) { - FormatVector supportedFormats = - hdmiOutputDevs[i]->getAudioPort()->getAudioProfiles().getSupportedFormats(); - for (size_t j = 0; j < supportedFormats.size(); j++) { - if (mConfig.getSurroundFormats().count(supportedFormats[j]) != 0) { - formats.insert(supportedFormats[j]); - } else { - for (const auto& pair : mConfig.getSurroundFormats()) { - if (pair.second.count(supportedFormats[j]) != 0) { - formats.insert(pair.first); - break; - } - } - } - } + for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { + sp device = mAvailableOutputDevices[i]; + FormatVector supportedFormats = + device->getAudioPort()->getAudioProfiles().getSupportedFormats(); + for (size_t j = 0; j < supportedFormats.size(); j++) { + if (mConfig.getSurroundFormats().count(supportedFormats[j]) != 0) { + formats.insert(supportedFormats[j]); + } else { + for (const auto& pair : mConfig.getSurroundFormats()) { + if (pair.second.count(supportedFormats[j]) != 0) { + formats.insert(pair.first); + break; + } + } + } + } } } else { for (const auto& pair : mConfig.getSurroundFormats()) { formats.insert(pair.first); } } + *numSurroundFormats = formats.size(); + audio_policy_forced_cfg_t forceUse = mEngine->getForceUse( + AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND); for (const auto& format: formats) { if (formatsWritten < formatsMax) { surroundFormats[formatsWritten] = format; - bool formatEnabled = false; - if (mConfig.getSurroundFormats().count(format) == 0) { - // Check sub-formats - for (const auto& pair : mConfig.getSurroundFormats()) { - for (const auto& subformat : pair.second) { - formatEnabled = mSurroundFormats.count(subformat) != 0; - if (formatEnabled) break; - } - if (formatEnabled) break; - } - } else { - formatEnabled = mSurroundFormats.count(format) != 0; + bool formatEnabled = true; + switch (forceUse) { + case AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL: + formatEnabled = mManualSurroundFormats.count(format) != 0; + break; + case AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER: + formatEnabled = false; + break; + default: // AUTO or ALWAYS => true + break; } surroundFormatsEnabled[formatsWritten++] = formatEnabled; } - (*numSurroundFormats)++; } return NO_ERROR; } @@ -3632,41 +3646,32 @@ status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) { ALOGV("%s() format 0x%X enabled %d", __func__, audioFormat, enabled); - // Check if audio format is a surround formats. const auto& formatIter = mConfig.getSurroundFormats().find(audioFormat); if (formatIter == mConfig.getSurroundFormats().end()) { ALOGW("%s() format 0x%X is not a known surround format", __func__, audioFormat); return BAD_VALUE; } - // Should only be called when MANUAL. - audio_policy_forced_cfg_t forceUse = mEngine->getForceUse( - AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND); - if (forceUse != AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) { + if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND) != + AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) { ALOGW("%s() not in manual mode for surround sound format selection", __func__); return INVALID_OPERATION; } - if ((mSurroundFormats.count(audioFormat) != 0) == enabled) { + if ((mManualSurroundFormats.count(audioFormat) != 0) == enabled) { return NO_ERROR; } - // The operation is valid only when there is HDMI output available. - if ((mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_HDMI) == 0) { - ALOGW("%s() no HDMI out devices found", __func__); - return INVALID_OPERATION; - } - - std::unordered_set surroundFormatsBackup(mSurroundFormats); + std::unordered_set surroundFormatsBackup(mManualSurroundFormats); if (enabled) { - mSurroundFormats.insert(audioFormat); + mManualSurroundFormats.insert(audioFormat); for (const auto& subFormat : formatIter->second) { - mSurroundFormats.insert(subFormat); + mManualSurroundFormats.insert(subFormat); } } else { - mSurroundFormats.erase(audioFormat); + mManualSurroundFormats.erase(audioFormat); for (const auto& subFormat : formatIter->second) { - mSurroundFormats.erase(subFormat); + mManualSurroundFormats.erase(subFormat); } } @@ -3691,6 +3696,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat name.c_str()); profileUpdated |= (status == NO_ERROR); } + // FIXME: Why doing this for input HDMI devices if we don't augment their reported formats? DeviceVector hdmiInputDevices = mAvailableInputDevices.getDevicesFromTypeMask( AUDIO_DEVICE_IN_HDMI); for (size_t i = 0; i < hdmiInputDevices.size(); i++) { @@ -3713,7 +3719,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat if (!profileUpdated) { ALOGW("%s() no audio profiles updated, undoing surround formats change", __func__); - mSurroundFormats = std::move(surroundFormatsBackup); + mManualSurroundFormats = std::move(surroundFormatsBackup); } return profileUpdated ? NO_ERROR : INVALID_OPERATION; @@ -4091,7 +4097,7 @@ AudioPolicyManager::~AudioPolicyManager() mInputs.clear(); mHwModules.clear(); mHwModulesAll.clear(); - mSurroundFormats.clear(); + mManualSurroundFormats.clear(); } status_t AudioPolicyManager::initCheck() @@ -4557,7 +4563,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) // MSD patches may have been released to support a non-MSD direct output. Reset MSD patch if // no direct outputs are open. - if (getMsdAudioOutDeviceTypes() != AUDIO_DEVICE_NONE) { + if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) { bool directOutputOpen = false; for (size_t i = 0; i < mOutputs.size(); i++) { if (mOutputs[i]->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { @@ -5792,56 +5798,52 @@ void AudioPolicyManager::cleanUpForDevice(const sp& deviceDesc void AudioPolicyManager::modifySurroundFormats( const sp& devDesc, FormatVector *formatsPtr) { - // Use a set because FormatVector is unsorted. std::unordered_set enforcedSurround( devDesc->encodedFormats().begin(), devDesc->encodedFormats().end()); + std::unordered_set allSurround; // A flat set of all known surround formats + for (const auto& pair : mConfig.getSurroundFormats()) { + allSurround.insert(pair.first); + for (const auto& subformat : pair.second) allSurround.insert(subformat); + } audio_policy_forced_cfg_t forceUse = mEngine->getForceUse( AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND); ALOGD("%s: forced use = %d", __FUNCTION__, forceUse); + // This is the resulting set of formats depending on the surround mode: + // 'all surround' = allSurround + // 'enforced surround' = enforcedSurround [may include IEC69137 which isn't raw surround fmt] + // 'non-surround' = not in 'all surround' and not in 'enforced surround' + // 'manual surround' = mManualSurroundFormats + // AUTO: formats v 'enforced surround' + // ALWAYS: formats v 'all surround' v 'enforced surround' + // NEVER: formats ^ 'non-surround' + // MANUAL: formats ^ ('non-surround' v 'manual surround' v (IEC69137 ^ 'enforced surround')) std::unordered_set formatSet; if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL || forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { - // Only copy non-surround formats to formatSet. + // formatSet is (formats ^ 'non-surround') for (auto formatIter = formatsPtr->begin(); formatIter != formatsPtr->end(); ++formatIter) { - if (mConfig.getSurroundFormats().count(*formatIter) == 0 && - enforcedSurround.count(*formatIter) == 0) { + if (allSurround.count(*formatIter) == 0 && enforcedSurround.count(*formatIter) == 0) { formatSet.insert(*formatIter); } } } else { formatSet.insert(formatsPtr->begin(), formatsPtr->end()); } - formatsPtr->clear(); // Re-filled from the formatSet in the end. + formatsPtr->clear(); // Re-filled from the formatSet at the end. - // If MANUAL, keep the supported surround sound formats as current enabled ones. if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL) { - formatSet.insert(mSurroundFormats.begin(), mSurroundFormats.end()); + formatSet.insert(mManualSurroundFormats.begin(), mManualSurroundFormats.end()); // Enable IEC61937 when in MANUAL mode if it's enforced for this device. if (enforcedSurround.count(AUDIO_FORMAT_IEC61937) != 0) { formatSet.insert(AUDIO_FORMAT_IEC61937); } - } else { // NEVER, AUTO or ALWAYS - mSurroundFormats.clear(); - // Modify formats based on surround preferences. - if (forceUse != AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { // AUTO or ALWAYS - // If ALWAYS, add support for raw surround formats if all are missing. - // This assumes that if any of these formats are reported by the HAL - // then the report is valid and should not be modified. - if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS) { - for (const auto& format : mConfig.getSurroundFormats()) { - formatSet.insert(format.first); - } - } - formatSet.insert(enforcedSurround.begin(), enforcedSurround.end()); - - for (const auto& format : formatSet) { - if (mConfig.getSurroundFormats().count(format) != 0) { - mSurroundFormats.insert(format); - } - } + } else if (forceUse != AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER) { // AUTO or ALWAYS + if (forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS) { + formatSet.insert(allSurround.begin(), allSurround.end()); } + formatSet.insert(enforcedSurround.begin(), enforcedSurround.end()); } for (const auto& format : formatSet) { formatsPtr->push(format); @@ -5878,7 +5880,7 @@ void AudioPolicyManager::modifySurroundChannelMasks(ChannelsVector *channelMasks // If not then add 5.1 support. if (!supports5dot1) { channelMasks.add(AUDIO_CHANNEL_OUT_5POINT1); - ALOGI("%s: force ALWAYS, so adding channelMask for 5.1 surround", __FUNCTION__); + ALOGI("%s: force MANUAL or ALWAYS, so adding channelMask for 5.1 surround", __func__); } } } @@ -5902,7 +5904,8 @@ void AudioPolicyManager::updateAudioProfiles(const sp& devDesc return; } FormatVector formats = formatsFromString(reply.string()); - if (device == AUDIO_DEVICE_OUT_HDMI) { + if (device == AUDIO_DEVICE_OUT_HDMI + || isDeviceOfModule(devDesc, AUDIO_HARDWARE_MODULE_ID_MSD)) { modifySurroundFormats(devDesc, &formats); } profiles.setFormats(formats); @@ -5935,7 +5938,8 @@ void AudioPolicyManager::updateAudioProfiles(const sp& devDesc if (repliedParameters.get( String8(AudioParameter::keyStreamSupportedChannels), reply) == NO_ERROR) { channelMasks = channelMasksFromString(reply.string()); - if (device == AUDIO_DEVICE_OUT_HDMI) { + if (device == AUDIO_DEVICE_OUT_HDMI + || isDeviceOfModule(devDesc, AUDIO_HARDWARE_MODULE_ID_MSD)) { modifySurroundChannelMasks(&channelMasks); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 2b893806ed..8618e0cba8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -186,7 +186,9 @@ public: virtual bool isSourceActive(audio_source_t source) const; - void dump(String8 *dst) const; // helper for dump(int fd) + // helpers for dump(int fd) + void dumpManualSurroundFormats(String8 *dst) const; + void dump(String8 *dst) const; status_t dump(int fd) override; @@ -516,6 +518,9 @@ protected: sp createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs); sp findDevice( const DeviceVector& devices, audio_devices_t device) const; + audio_devices_t getModuleDeviceTypes( + const DeviceVector& devices, const char *moduleId) const; + bool isDeviceOfModule(const sp& devDesc, const char *moduleId) const; status_t startSource(const sp& outputDesc, const sp& client, @@ -608,8 +613,9 @@ protected: // Audio Policy Engine Interface. AudioPolicyManagerInterface *mEngine; - // Surround formats that are enabled. - std::unordered_set mSurroundFormats; + // Surround formats that are enabled manually. Taken into account when + // "encoded surround" is forced into "manual" mode. + std::unordered_set mManualSurroundFormats; private: // Add or remove AC3 DTS encodings based on user preferences. void modifySurroundFormats(const sp& devDesc, FormatVector *formatsPtr); @@ -617,7 +623,6 @@ private: // Support for Multi-Stream Decoder (MSD) module sp getMsdAudioInDevice() const; - audio_devices_t getMsdAudioOutDeviceTypes() const; const AudioPatchCollection getMsdPatches() const; status_t getBestMsdAudioProfileFor(audio_devices_t outputDevice, bool hwAvSync, -- GitLab From 88bd26d835247ea061c1b261bddb0d3c50abc9df Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 10 Dec 2018 10:54:20 -0800 Subject: [PATCH 0576/1530] Convert FLAC Extractor to V3 format Bug: 111407253 Test: manual, CTS Change-Id: Ie3c4ae68a04c950ebc62aca60b502536a05169ca --- media/extractors/flac/FLACExtractor.cpp | 48 ++++++++++++------------- media/extractors/flac/FLACExtractor.h | 4 +-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 671fa5acf5..22b96e5af3 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include // for AID_MEDIA #include @@ -53,7 +52,7 @@ static inline bool shouldExtractorOutputFloat(int bitsPerSample) class FLACParser; -class FLACSource : public MediaTrackHelperV2 { +class FLACSource : public MediaTrackHelperV3 { public: FLACSource( @@ -66,7 +65,7 @@ public: virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~FLACSource(); @@ -125,12 +124,12 @@ public: } // media buffers - void allocateBuffers(); + void allocateBuffers(MediaBufferGroupHelperV3 *group); void releaseBuffers(); - MediaBufferBase *readBuffer() { + MediaBufferHelperV3 *readBuffer() { return readBuffer(false, 0LL); } - MediaBufferBase *readBuffer(FLAC__uint64 sample) { + MediaBufferHelperV3 *readBuffer(FLAC__uint64 sample) { return readBuffer(true, sample); } @@ -143,7 +142,7 @@ private: // media buffers size_t mMaxBufferSize; - MediaBufferGroup *mGroup; + MediaBufferGroupHelperV3 *mGroup; void (*mCopy)(int16_t *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels); // handle to underlying libFLAC parser @@ -167,7 +166,7 @@ private: FLAC__StreamDecoderErrorStatus mErrorStatus; status_t init(); - MediaBufferBase *readBuffer(bool doSeek, FLAC__uint64 sample); + MediaBufferHelperV3 *readBuffer(bool doSeek, FLAC__uint64 sample); // no copy constructor or assignment FLACParser(const FLACParser &); @@ -577,12 +576,12 @@ status_t FLACParser::init() return OK; } -void FLACParser::allocateBuffers() +void FLACParser::allocateBuffers(MediaBufferGroupHelperV3 *group) { CHECK(mGroup == NULL); - mGroup = new MediaBufferGroup; + mGroup = group; mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize(); - mGroup->add_buffer(MediaBufferBase::Create(mMaxBufferSize)); + mGroup->add_buffer(mMaxBufferSize); } void FLACParser::releaseBuffers() @@ -592,7 +591,7 @@ void FLACParser::releaseBuffers() mGroup = NULL; } -MediaBufferBase *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) +MediaBufferHelperV3 *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) { mWriteRequested = true; mWriteCompleted = false; @@ -629,7 +628,7 @@ MediaBufferBase *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) } // acquire a media buffer CHECK(mGroup != NULL); - MediaBufferBase *buffer; + MediaBufferHelperV3 *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { return NULL; @@ -656,8 +655,9 @@ MediaBufferBase *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number; int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate(); - buffer->meta_data().setInt64(kKeyTime, timeUs); - buffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); return buffer; } @@ -691,7 +691,7 @@ media_status_t FLACSource::start() ALOGV("FLACSource::start"); CHECK(!mStarted); - mParser->allocateBuffers(); + mParser->allocateBuffers(mBufferGroup); mStarted = true; return AMEDIA_OK; @@ -719,9 +719,9 @@ media_status_t FLACSource::getFormat(AMediaFormat *meta) } media_status_t FLACSource::read( - MediaBufferBase **outBuffer, const ReadOptions *options) + MediaBufferHelperV3 **outBuffer, const ReadOptions *options) { - MediaBufferBase *buffer; + MediaBufferHelperV3 *buffer; // process an optional seek request int64_t seekTimeUs; ReadOptions::SeekMode mode; @@ -775,7 +775,7 @@ size_t FLACExtractor::countTracks() return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV2 *FLACExtractor::getTrack(size_t index) +MediaTrackHelperV3 *FLACExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; @@ -831,22 +831,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("1364b048-cc45-4fda-9934-327d0ebf9829"), 1, "FLAC Extractor", { - .v2 = []( + .v3 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV2 { + FreeMetaFunc *) -> CreatorFuncV3 { DataSourceHelper helper(source); if (SniffFLAC(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV2* { - return wrapV2(new FLACExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new FLACExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h index 323307b4d9..9604e4ac4e 100644 --- a/media/extractors/flac/FLACExtractor.h +++ b/media/extractors/flac/FLACExtractor.h @@ -27,13 +27,13 @@ namespace android { class FLACParser; -class FLACExtractor : public MediaExtractorPluginHelperV2 { +class FLACExtractor : public MediaExtractorPluginHelperV3 { public: explicit FLACExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); -- GitLab From 7fa7c86510509efe0af098893a661516794e0490 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 6 Dec 2018 09:07:58 -0800 Subject: [PATCH 0577/1530] mediaextractor: load extractor plug-ins from apex Note: media apex is not added in the default build. The new code path will be no-op for now. Test: boot & adb shell dumpsys media.extractor Bug: 112766843 Change-Id: I1bdf2c41cc5f88c80c2fd20677dce1d58420e947 --- .../libstagefright/MediaExtractorFactory.cpp | 87 +++++++++++++++++++ .../media/stagefright/MediaExtractorFactory.h | 4 + services/mediaextractor/Android.mk | 18 ++++ .../mediaextractor/MediaExtractorService.cpp | 5 ++ .../mediaextractor/MediaExtractorService.h | 2 +- 5 files changed, 115 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 1f8ccb33fc..81fc4ae56d 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "MediaExtractorFactory" #include +#include #include #include #include @@ -36,6 +37,23 @@ #include #include +// Copied from GraphicsEnv.cpp +// TODO(b/37049319) Get this from a header once one exists +extern "C" { + android_namespace_t* android_create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent); + bool android_link_namespaces(android_namespace_t* from, + android_namespace_t* to, + const char* shared_libs_sonames); + enum { + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + }; +} + namespace android { // static @@ -145,6 +163,13 @@ Mutex MediaExtractorFactory::gPluginMutex; std::shared_ptr>> MediaExtractorFactory::gPlugins; bool MediaExtractorFactory::gPluginsRegistered = false; bool MediaExtractorFactory::gIgnoreVersion = false; +std::string MediaExtractorFactory::gLinkedLibraries; + +// static +void MediaExtractorFactory::SetLinkedLibraries(const std::string& linkedLibraries) { + Mutex::Autolock autoLock(gPluginMutex); + gLinkedLibraries = linkedLibraries; +} // static void *MediaExtractorFactory::sniff( @@ -328,6 +353,62 @@ void MediaExtractorFactory::RegisterExtractorsInSystem( } } +//static +void MediaExtractorFactory::RegisterExtractorsInApex( + const char *libDirPath, std::list> &pluginList) { + ALOGV("search for plugins at %s", libDirPath); + ALOGV("linked libs %s", gLinkedLibraries.c_str()); + + android_namespace_t *extractorNs = android_create_namespace("extractor", + nullptr, // ld_library_path + libDirPath, + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); // parent + if (!android_link_namespaces(extractorNs, nullptr, gLinkedLibraries.c_str())) { + ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex."); + return; + } + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = extractorNs, + }; + + DIR *libDir = opendir(libDirPath); + if (libDir) { + struct dirent* libEntry; + while ((libEntry = readdir(libDir))) { + if (libEntry->d_name[0] == '.') { + continue; + } + String8 libPath = String8(libDirPath) + "/" + libEntry->d_name; + if (!libPath.contains("extractor.so")) { + continue; + } + void *libHandle = android_dlopen_ext( + libPath.string(), + RTLD_NOW | RTLD_LOCAL, &dlextinfo); + if (libHandle) { + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + if (getDef) { + ALOGV("registering sniffer for %s", libPath.string()); + RegisterExtractor( + new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); + } else { + ALOGW("%s does not contain sniffer", libPath.string()); + dlclose(libHandle); + } + } else { + ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); + } + } + closedir(libDir); + } else { + ALOGE("couldn't opendir(%s)", libDirPath); + } +} + static bool compareFunc(const sp& first, const sp& second) { return strcmp(first->def.extractor_name, second->def.extractor_name) < 0; } @@ -346,6 +427,12 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { std::shared_ptr>> newList(new std::list>()); + RegisterExtractorsInApex("/apex/com.android.media/lib" +#ifdef __LP64__ + "64" +#endif + , *newList); + RegisterExtractorsInSystem("/system/lib" #ifdef __LP64__ "64" diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index ef9f7ed03f..84e01f30c5 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -35,17 +35,21 @@ public: const sp &source, const char *mime = NULL); static void LoadPlugins(const ::std::string& apkPath); static status_t dump(int fd, const Vector& args); + static void SetLinkedLibraries(const std::string& linkedLibraries); private: static Mutex gPluginMutex; static std::shared_ptr>> gPlugins; static bool gPluginsRegistered; static bool gIgnoreVersion; + static std::string gLinkedLibraries; static void RegisterExtractorsInApk( const char *apkPath, std::list> &pluginList); static void RegisterExtractorsInSystem( const char *libDirPath, std::list> &pluginList); + static void RegisterExtractorsInApex( + const char *libDirPath, std::list> &pluginList); static void RegisterExtractor( const sp &plugin, std::list> &pluginList); diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 73c953597b..19ce7e9af8 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -9,6 +9,24 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog LOCAL_MODULE:= libmediaextractorservice + +sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\ + $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ + $(UBSAN_RUNTIME_LIBRARY) \ + $(TSAN_RUNTIME_LIBRARY))) + +# $(info Sanitizer: $(sanitizer_runtime_libraries)) + +ndk_libraries := $(call normalize-path-list,$(addprefix lib,$(addsuffix .so,\ + $(NDK_PREBUILT_SHARED_LIBRARIES)))) + +# $(info NDK: $(ndk_libraries)) + +LOCAL_CFLAGS += -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(ndk_libraries)"' + +sanitizer_runtime_libraries := +ndk_libraries := + include $(BUILD_SHARED_LIBRARY) diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp index f4d8b43dbe..8b26178640 100644 --- a/services/mediaextractor/MediaExtractorService.cpp +++ b/services/mediaextractor/MediaExtractorService.cpp @@ -29,6 +29,11 @@ namespace android { +MediaExtractorService::MediaExtractorService() + : BnMediaExtractorService() { + MediaExtractorFactory::SetLinkedLibraries(std::string(LINKED_LIBRARIES)); +} + sp MediaExtractorService::makeExtractor( const sp &remoteSource, const char *mime) { ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime); diff --git a/services/mediaextractor/MediaExtractorService.h b/services/mediaextractor/MediaExtractorService.h index 9df3ecd79e..60070041cf 100644 --- a/services/mediaextractor/MediaExtractorService.h +++ b/services/mediaextractor/MediaExtractorService.h @@ -27,7 +27,7 @@ class MediaExtractorService : public BinderService, publi { friend class BinderService; // for MediaExtractorService() public: - MediaExtractorService() : BnMediaExtractorService() { } + MediaExtractorService(); virtual ~MediaExtractorService() { } virtual void onFirstRef() { } -- GitLab From 4eb58f1f36606ad7af392a920e1d029a5fb1e797 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 7 Dec 2018 16:41:02 -0800 Subject: [PATCH 0578/1530] Revert "Revert "audio policy: concurrent capture"" This reverts commit df628924e691e01da190c1ac5db173304442e54a. Bug: 120588242 Bug: 111438757 Test: make Change-Id: If58ff2143fdb744678bb84394598104ced01f5b9 --- media/libaudioclient/AudioSystem.cpp | 4 +- media/libaudioclient/IAudioPolicyService.cpp | 9 +- .../include/media/AudioSystem.h | 3 +- .../include/media/IAudioPolicyService.h | 3 +- services/audioflinger/Threads.cpp | 23 +- services/audiopolicy/AudioPolicyInterface.h | 18 +- services/audiopolicy/common/include/policy.h | 25 -- .../include/AudioInputDescriptor.h | 5 +- .../include/ClientDescriptor.h | 11 +- .../managerdefinitions/include/IOProfile.h | 2 +- .../src/AudioInputDescriptor.cpp | 55 ++--- .../managerdefault/AudioPolicyManager.cpp | 216 ++++-------------- .../managerdefault/AudioPolicyManager.h | 10 +- .../service/AudioPolicyInterfaceImpl.cpp | 107 ++++----- .../service/AudioPolicyService.cpp | 117 +++++++++- .../audiopolicy/service/AudioPolicyService.h | 11 +- 16 files changed, 262 insertions(+), 357 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index ec36ed7dc8..efe65bb299 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -917,11 +917,11 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, config, flags, selectedDeviceId, portId); } -status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced) +status_t AudioSystem::startInput(audio_port_handle_t portId) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->startInput(portId, silenced); + return aps->startInput(portId); } status_t AudioSystem::stopInput(audio_port_handle_t portId) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index a406658a16..7f2e5e5805 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -329,16 +329,13 @@ public: return NO_ERROR; } - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced) + virtual status_t startInput(audio_port_handle_t portId) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(portId); - data.writeInt32(*silenced ? 1 : 0); remote()->transact(START_INPUT, data, &reply); status_t status = static_cast (reply.readInt32()); - *silenced = reply.readInt32() == 1; return status; } @@ -1219,10 +1216,8 @@ status_t BnAudioPolicyService::onTransact( case START_INPUT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_port_handle_t portId = static_cast (data.readInt32()); - bool silenced = data.readInt32() == 1; - status_t status = startInput(portId, &silenced); + status_t status = startInput(portId); reply->writeInt32(static_cast (status)); - reply->writeInt32(silenced ? 1 : 0); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 76a79c9536..ca1879fc4e 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -241,8 +241,7 @@ public: audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId); - static status_t startInput(audio_port_handle_t portId, - bool *silenced); + static status_t startInput(audio_port_handle_t portId); static status_t stopInput(audio_port_handle_t portId); static void releaseInput(audio_port_handle_t portId); static status_t initStreamVolume(audio_stream_type_t stream, diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index a246df68ea..e5fcfb5c2f 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -78,8 +78,7 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId) = 0; - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced) = 0; + virtual status_t startInput(audio_port_handle_t portId) = 0; virtual status_t stopInput(audio_port_handle_t portId) = 0; virtual void releaseInput(audio_port_handle_t portId) = 0; virtual status_t initStreamVolume(audio_stream_type_t stream, diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 27aec9e308..3dae1e96d1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7367,8 +7367,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac status_t status = NO_ERROR; if (recordTrack->isExternalTrack()) { mLock.unlock(); - bool silenced; - status = AudioSystem::startInput(recordTrack->portId(), &silenced); + status = AudioSystem::startInput(recordTrack->portId()); mLock.lock(); if (recordTrack->isInvalid()) { recordTrack->clearSyncStartEvent(); @@ -7396,7 +7395,6 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac recordTrack->clearSyncStartEvent(); return status; } - recordTrack->setSilenced(silenced); } // Catch up with current buffer indices if thread is already running. // This is what makes a new client discard all buffered data. If the track's mRsmpInFront @@ -8346,11 +8344,10 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return BAD_VALUE; } - bool silenced = false; if (isOutput()) { ret = AudioSystem::startOutput(portId); } else { - ret = AudioSystem::startInput(portId, &silenced); + ret = AudioSystem::startInput(portId); } Mutex::Autolock _l(mLock); @@ -8371,21 +8368,21 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, return PERMISSION_DENIED; } + // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? + sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, + isOutput(), client.clientUid, client.clientPid, portId); + if (isOutput()) { // force volume update when a new track is added mHalVolFloat = -1.0f; - } else if (!silenced) { - for (const sp &track : mActiveTracks) { - if (track->isSilenced_l() && track->uid() != client.clientUid) - track->invalidate(); + } else if (!track->isSilenced_l()) { + for (const sp &t : mActiveTracks) { + if (t->isSilenced_l() && t->uid() != client.clientUid) + t->invalidate(); } } - // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? - sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, - isOutput(), client.clientUid, client.clientPid, portId); - track->setSilenced_l(silenced); mActiveTracks.add(track); sp chain = getEffectChain_l(mSessionId); if (chain != 0) { diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 3c3a82b38f..3fb505d7b2 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -65,20 +65,6 @@ public: API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path } input_type_t; - enum { - API_INPUT_CONCURRENCY_NONE = 0, - API_INPUT_CONCURRENCY_CALL = (1 << 0), // Concurrency with a call - API_INPUT_CONCURRENCY_CAPTURE = (1 << 1), // Concurrency with another capture - API_INPUT_CONCURRENCY_HOTWORD = (1 << 2), // Concurrency with a hotword - API_INPUT_CONCURRENCY_PREEMPT = (1 << 3), // pre-empted someone - // NB: preempt is marked on a successful return, others are on failing calls - API_INPUT_CONCURRENCY_LAST = (1 << 4), - - API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_LAST - 1), - }; - - typedef uint32_t concurrency_type__mask_t; - public: virtual ~AudioPolicyInterface() {} // @@ -141,9 +127,7 @@ public: input_type_t *inputType, audio_port_handle_t *portId) = 0; // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency) = 0; + virtual status_t startInput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId) = 0; // releases the input. diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 9bd68e1ffa..30b0044440 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -31,14 +31,6 @@ static const audio_format_t gDynamicFormat = AUDIO_FORMAT_DEFAULT; // Do not limit channel count otherwise #define MAX_MIXER_CHANNEL_COUNT FCC_8 -/** - * A device mask for all audio input devices that are considered "virtual" when evaluating - * active inputs in getActiveInputs() - */ -#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX|\ - AUDIO_DEVICE_IN_BUS|AUDIO_DEVICE_IN_FM_TUNER) - - /** * A device mask for all audio input and output devices where matching inputs/outputs on device * type alone is not enough: the address must match too @@ -67,23 +59,6 @@ static inline bool is_state_in_call(int state) return (state == AUDIO_MODE_IN_CALL) || (state == AUDIO_MODE_IN_COMMUNICATION); } -/** - * Check if the input device given is considered as a virtual device. - * - * @param[in] device to consider - * - * @return true if the device is a virtual one, false otherwise. - */ -static inline bool is_virtual_input_device(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) - return true; - } - return false; -} - /** * Check whether the device type is one * where addresses are used to distinguish between one connected device and another diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 6e4c044bb5..9f8b8c0580 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -58,9 +58,8 @@ public: void clearPreemptedSessions(); bool isActive() const { return mGlobalActiveCount > 0; } bool isSourceActive(audio_source_t source) const; - audio_source_t inputSource(bool activeOnly = false) const; + audio_source_t source() const; bool isSoundTrigger() const; - audio_source_t getHighestPrioritySource(bool activeOnly) const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } @@ -121,7 +120,7 @@ public: * Only considers inputs from physical devices (e.g. main mic, headset mic) when * ignoreVirtualInputs is true. */ - Vector > getActiveInputs(bool ignoreVirtualInputs = true); + Vector > getActiveInputs(); audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 030bf4bd31..986d109122 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -106,7 +107,7 @@ public: audio_port_handle_t preferredDeviceId, audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {} + mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mAppState(APP_STATE_IDLE) {} ~RecordClientDescriptor() override = default; using ClientDescriptor::dump; @@ -115,14 +116,16 @@ public: audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } bool isSoundTrigger() const { return mIsSoundTrigger; } - void setSilenced(bool silenced) { mSilenced = silenced; } - bool isSilenced() const { return mSilenced; } + void setAppState(app_state_t appState) { mAppState = appState; } + app_state_t appState() { return mAppState; } + bool isSilenced() const { return mAppState == APP_STATE_IDLE; } private: const audio_source_t mSource; const audio_input_flags_t mFlags; const bool mIsSoundTrigger; - bool mSilenced; + app_state_t mAppState; + }; class SourceClientDescriptor: public TrackClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index eb329597da..8ff823860f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -35,7 +35,7 @@ class IOProfile : public AudioPort public: IOProfile(const String8 &name, audio_port_role_t role) : AudioPort(name, AUDIO_PORT_TYPE_MIX, role), - maxOpenCount((role == AUDIO_PORT_ROLE_SOURCE) ? 1 : 0), + maxOpenCount(1), curOpenCount(0), maxActiveCount(1), curActiveCount(0) {} diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 1f29874b5c..559274f8c3 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -53,9 +53,32 @@ audio_port_handle_t AudioInputDescriptor::getId() const return mId; } -audio_source_t AudioInputDescriptor::inputSource(bool activeOnly) const +audio_source_t AudioInputDescriptor::source() const { - return getHighestPrioritySource(activeOnly); + audio_source_t source = AUDIO_SOURCE_DEFAULT; + + for (bool activeOnly : { true, false }) { + int32_t topPriority = -1; + app_state_t topState = APP_STATE_IDLE; + for (const auto &client : getClientIterable()) { + if (activeOnly && !client->active()) { + continue; + } + app_state_t curState = client->appState(); + if (curState >= topState) { + int32_t curPriority = source_priority(client->source()); + if (curPriority > topPriority) { + source = client->source(); + topPriority = curPriority; + } + topState = curState; + } + } + if (source != AUDIO_SOURCE_DEFAULT) { + break; + } + } + return source; } void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, @@ -76,7 +99,7 @@ void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig dstConfig->type = AUDIO_PORT_TYPE_MIX; dstConfig->ext.mix.hw_module = getModuleHandle(); dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.source = inputSource(); + dstConfig->ext.mix.usecase.source = source(); } void AudioInputDescriptor::toAudioPort(struct audio_port *port) const @@ -125,24 +148,6 @@ bool AudioInputDescriptor::isSourceActive(audio_source_t source) const return false; } -audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const -{ - audio_source_t source = AUDIO_SOURCE_DEFAULT; - int32_t priority = -1; - - for (const auto &client : getClientIterable()) { - if (activeOnly && !client->active() ) { - continue; - } - int32_t curPriority = source_priority(client->source()); - if (curPriority > priority) { - priority = curPriority; - source = client->source(); - } - } - return source; -} - bool AudioInputDescriptor::isSoundTrigger() const { // sound trigger and non sound trigger clients are not mixed on a given input // so check only first client @@ -224,7 +229,7 @@ status_t AudioInputDescriptor::open(const audio_config_t *config, status_t AudioInputDescriptor::start() { - if (mGlobalActiveCount == 1) { + if (!isActive()) { if (!mProfile->canStartNewIo()) { ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount); return INVALID_OPERATION; @@ -388,15 +393,13 @@ uint32_t AudioInputCollection::activeInputsCountOnDevices(audio_devices_t device return count; } -Vector > AudioInputCollection::getActiveInputs(bool ignoreVirtualInputs) +Vector > AudioInputCollection::getActiveInputs() { Vector > activeInputs; for (size_t i = 0; i < size(); i++) { const sp inputDescriptor = valueAt(i); - if ((inputDescriptor->isActive()) - && (!ignoreVirtualInputs || - !is_virtual_input_device(inputDescriptor->mDevice))) { + if (inputDescriptor->isActive()) { activeInputs.add(inputDescriptor); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 004157096d..c8dd362c48 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1877,7 +1877,38 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, } if (!profile->canOpenNewIo()) { - return AUDIO_IO_HANDLE_NONE; + for (size_t i = 0; i < mInputs.size(); ) { + sp desc = mInputs.valueAt(i); + if (desc->mProfile != profile) { + continue; + } + // if sound trigger, reuse input if used by other sound trigger on same session + // else + // reuse input if active client app is not in IDLE state + // + RecordClientVector clients = desc->clientsList(); + bool doClose = false; + for (const auto& client : clients) { + if (isSoundTrigger != client->isSoundTrigger()) { + continue; + } + if (client->isSoundTrigger()) { + if (session == client->session()) { + return desc->mIoHandle; + } + continue; + } + if (client->active() && client->appState() != APP_STATE_IDLE) { + return desc->mIoHandle; + } + doClose = true; + } + if (doClose) { + closeInput(desc->mIoHandle); + } else { + i++; + } + } } sp inputDesc = new AudioInputDescriptor(profile, mpClientInterface); @@ -1918,55 +1949,8 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, return input; } -//static -bool AudioPolicyManager::isConcurrentSource(audio_source_t source) -{ - return (source == AUDIO_SOURCE_HOTWORD) || - (source == AUDIO_SOURCE_VOICE_RECOGNITION) || - (source == AUDIO_SOURCE_FM_TUNER); -} - -// FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537. -bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() { - if (!mHasComputedSoundTriggerSupportsConcurrentCapture) { - bool soundTriggerSupportsConcurrentCapture = false; - unsigned int numModules = 0; - struct sound_trigger_module_descriptor* nModules = NULL; - - status_t status = SoundTrigger::listModules(nModules, &numModules); - if (status == NO_ERROR && numModules != 0) { - nModules = (struct sound_trigger_module_descriptor*) calloc( - numModules, sizeof(struct sound_trigger_module_descriptor)); - if (nModules == NULL) { - // We failed to malloc the buffer, so just say no for now, and hope that we have more - // ram the next time this function is called. - ALOGE("Failed to allocate buffer for module descriptors"); - return false; - } - - status = SoundTrigger::listModules(nModules, &numModules); - if (status == NO_ERROR) { - soundTriggerSupportsConcurrentCapture = true; - for (size_t i = 0; i < numModules; ++i) { - soundTriggerSupportsConcurrentCapture &= - nModules[i].properties.concurrent_capture; - } - } - free(nModules); - } - mSoundTriggerSupportsConcurrentCapture = soundTriggerSupportsConcurrentCapture; - mHasComputedSoundTriggerSupportsConcurrentCapture = true; - } - return mSoundTriggerSupportsConcurrentCapture; -} - - -status_t AudioPolicyManager::startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency) +status_t AudioPolicyManager::startInput(audio_port_handle_t portId) { - *concurrency = API_INPUT_CONCURRENCY_NONE; - ALOGV("%s portId %d", __FUNCTION__, portId); sp inputDesc = mInputs.getInputForClient(portId); @@ -1983,106 +1967,16 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, audio_session_t session = client->session(); - ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)", - __FUNCTION__, input, session, silenced, *concurrency); + ALOGV("%s input:%d, session:%d)", __FUNCTION__, input, session); - if (!is_virtual_input_device(inputDesc->mDevice)) { - if (mCallTxPatch != 0 && - inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) { - ALOGW("startInput(%d) failed: call in progress", input); - *concurrency |= API_INPUT_CONCURRENCY_CALL; - return INVALID_OPERATION; - } - - Vector> activeInputs = mInputs.getActiveInputs(); - - // If a UID is idle and records silence and another not silenced recording starts - // from another UID (idle or active) we stop the current idle UID recording in - // favor of the new one - "There can be only one" TM - if (!silenced) { - for (const auto& activeDesc : activeInputs) { - if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && - activeDesc->getId() == inputDesc->getId()) { - continue; - } - - RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/); - for (const auto& activeClient : activeClients) { - if (activeClient->isSilenced()) { - closeClient(activeClient->portId()); - ALOGV("%s client %d stopping silenced client %d", __FUNCTION__, - portId, activeClient->portId()); - activeInputs = mInputs.getActiveInputs(); - } - } - } - } + Vector> activeInputs = mInputs.getActiveInputs(); - for (const auto& activeDesc : activeInputs) { - if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 && - activeDesc->getId() == inputDesc->getId()) { - continue; - } - - audio_source_t activeSource = activeDesc->inputSource(true); - if (client->source() == AUDIO_SOURCE_HOTWORD) { - if (activeSource == AUDIO_SOURCE_HOTWORD) { - if (activeDesc->hasPreemptedSession(session)) { - ALOGW("%s input %d failed for HOTWORD: " - "other input %d already started for HOTWORD", __FUNCTION__, - input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_HOTWORD; - return INVALID_OPERATION; - } - } else { - ALOGV("%s input %d failed for HOTWORD: other input %d already started", - __FUNCTION__, input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; - return INVALID_OPERATION; - } - } else { - if (activeSource != AUDIO_SOURCE_HOTWORD) { - ALOGW("%s input %d failed: other input %d already started", __FUNCTION__, - input, activeDesc->mIoHandle); - *concurrency |= API_INPUT_CONCURRENCY_CAPTURE; - return INVALID_OPERATION; - } - } - } - - // We only need to check if the sound trigger session supports concurrent capture if the - // input is also a sound trigger input. Otherwise, we should preempt any hotword stream - // that's running. - const bool allowConcurrentWithSoundTrigger = - inputDesc->isSoundTrigger() ? soundTriggerSupportsConcurrentCapture() : false; - - // if capture is allowed, preempt currently active HOTWORD captures - for (const auto& activeDesc : activeInputs) { - if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) { - continue; - } - RecordClientVector activeHotwordClients = - activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD); - if (activeHotwordClients.size() > 0) { - SortedVector sessions = activeDesc->getPreemptedSessions(); - - for (const auto& activeClient : activeHotwordClients) { - *concurrency |= API_INPUT_CONCURRENCY_PREEMPT; - sessions.add(activeClient->session()); - closeClient(activeClient->portId()); - ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__, - input, activeDesc->mIoHandle); - } - - inputDesc->setPreemptedSessions(sessions); - } - } + status_t status = inputDesc->start(); + if (status != NO_ERROR) { + return status; } - // Make sure we start with the correct silence state - client->setSilenced(silenced); - - // increment activity count before calling getNewInputDevice() below as only active sessions + // increment activity count before calling getNewInputDevice() below as only active sessions // are considered for device selection inputDesc->setClientActive(client, true); @@ -2091,12 +1985,6 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId, audio_devices_t device = getNewInputDevice(inputDesc); setInputDevice(input, device, true /* force */); - status_t status = inputDesc->start(); - if (status != NO_ERROR) { - inputDesc->setClientActive(client, false); - return status; - } - if (inputDesc->activeCount() == 1) { // if input maps to a dynamic policy with an activity listener, notify of state change if ((inputDesc->mPolicyMix != NULL) @@ -3345,7 +3233,7 @@ void AudioPolicyManager::clearSessionRoutes(uid_t uid) SortedVector inputsToClose; for (size_t i = 0; i < mInputs.size(); i++) { sp inputDesc = mInputs.valueAt(i); - if (affectedSources.indexOf(inputDesc->inputSource()) >= 0) { + if (affectedSources.indexOf(inputDesc->source()) >= 0) { inputsToClose.add(inputDesc->mIoHandle); } } @@ -3722,16 +3610,15 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { Vector > activeInputs = mInputs.getActiveInputs(); - bool silenced = state == APP_STATE_IDLE; - ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced); + ALOGV("%s(uid:%d, state:%d)", __func__, uid, state); for (size_t i = 0; i < activeInputs.size(); i++) { sp activeDesc = activeInputs[i]; RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); for (const auto& client : clients) { if (uid == client->uid()) { - client->setSilenced(silenced); + client->setAppState(state); } } } @@ -3841,8 +3728,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mBeaconMuted(false), mTtsOutputAvailable(false), mMasterMono(false), - mMusicEffectOutput(AUDIO_IO_HANDLE_NONE), - mHasComputedSoundTriggerSupportsConcurrentCapture(false) + mMusicEffectOutput(AUDIO_IO_HANDLE_NONE) { } @@ -4895,7 +4781,7 @@ audio_devices_t AudioPolicyManager::getNewInputDevice(const spgetHighestPrioritySource(true /*activeOnly*/); + audio_source_t source = inputDesc->source(); if (source == AUDIO_SOURCE_DEFAULT && isInCall()) { source = AUDIO_SOURCE_VOICE_COMMUNICATION; } @@ -5234,20 +5120,6 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou } installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } - - // inform all input as well - for (size_t i = 0; i < mInputs.size(); i++) { - const sp inputDescriptor = mInputs.valueAt(i); - if (!is_virtual_input_device(inputDescriptor->mDevice)) { - AudioParameter inputCmd = AudioParameter(); - ALOGV("%s: inform input %d of device:%d", __func__, - inputDescriptor->mIoHandle, device); - inputCmd.addInt(String8(AudioParameter::keyRouting),device); - mpClientInterface->setParameters(inputDescriptor->mIoHandle, - inputCmd.toString(), - delayMs); - } - } } // update stream volumes according to new device diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 2b893806ed..8f0e9229f6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -134,9 +134,7 @@ public: audio_port_handle_t *portId); // indicates to the audio policy manager that the input starts being used. - virtual status_t startInput(audio_port_handle_t portId, - bool silenced, - concurrency_type__mask_t *concurrency); + virtual status_t startInput(audio_port_handle_t portId); // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId); @@ -539,8 +537,6 @@ protected: void clearAudioSources(uid_t uid); - static bool isConcurrentSource(audio_source_t source); - static bool streamsMatchForvolume(audio_stream_type_t stream1, audio_stream_type_t stream2); @@ -704,10 +700,6 @@ private: int delayMs, uid_t uid, sp *patchDescPtr); - - bool soundTriggerSupportsConcurrentCapture(); - bool mSoundTriggerSupportsConcurrentCapture; - bool mHasComputedSoundTriggerSupportsConcurrentCapture; }; }; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 59c8f10075..c2ce754616 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -454,23 +454,6 @@ static std::string audioSourceString(audio_source_t value) { return rawbuffer; } -static std::string audioConcurrencyString( - AudioPolicyInterface::concurrency_type__mask_t concurrency) -{ - char buffer[64]; // oversized - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL) { - snprintf(buffer, sizeof(buffer), "%s%s%s%s", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL)? ",call":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE)? ",capture":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_HOTWORD)? ",hotword":"", - (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_PREEMPT)? ",preempt":""); - } else { - snprintf(buffer, sizeof(buffer), ",none"); - } - - return &buffer[1]; -} - std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t portId) { std::string typeStr; struct audio_port port = {}; @@ -482,7 +465,7 @@ std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t po return typeStr; } -status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced) +status_t AudioPolicyService::startInput(audio_port_handle_t portId) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -505,17 +488,16 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc return PERMISSION_DENIED; } - // If UID inactive it records silence until becoming active - *silenced = !mUidPolicy->isUidActive(client->uid) && !client->isVirtualDevice; - Mutex::Autolock _l(mLock); - AudioPolicyInterface::concurrency_type__mask_t concurrency = - AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE; + + client->active = true; + client->startTimeNs = systemTime(); + updateUidStates_l(); status_t status; { AutoCallerClear acc; - status = mAudioPolicyManager->startInput(portId, *silenced, &concurrency); + status = mAudioPolicyManager->startInput(portId); } @@ -524,7 +506,6 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc static constexpr char kAudioPolicy[] = "audiopolicy"; - static constexpr char kAudioPolicyReason[] = "android.media.audiopolicy.reason"; static constexpr char kAudioPolicyStatus[] = "android.media.audiopolicy.status"; static constexpr char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src"; static constexpr char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg"; @@ -541,7 +522,6 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy); if (item != NULL) { - item->setCString(kAudioPolicyReason, audioConcurrencyString(concurrency).c_str()); item->setInt32(kAudioPolicyStatus, status); item->setCString(kAudioPolicyRqstSrc, @@ -556,54 +536,35 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenc item->setCString( kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str()); - // figure out who is active - // NB: might the other party have given up the microphone since then? how sure. - // perhaps could have given up on it. - // we hold mLock, so perhaps we're safe for this looping - if (concurrency != AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE) { - int count = mAudioRecordClients.size(); - for (int i = 0; i other = mAudioRecordClients.valueAt(i); - if (other->active) { - // keeps the last of the clients marked active - item->setCString(kAudioPolicyActiveSrc, - audioSourceString(other->attributes.source).c_str()); - item->setInt32(kAudioPolicyActiveSession, other->session); - if (other->opPackageName.size() != 0) { - item->setCString(kAudioPolicyActivePkg, - std::string(String8(other->opPackageName).string()).c_str()); - } else { - item->setCString(kAudioPolicyRqstPkg, - std::to_string(other->uid).c_str()); - } - item->setCString(kAudioPolicyActiveDevice, - getDeviceTypeStrForPortId(other->deviceId).c_str()); + int count = mAudioRecordClients.size(); + for (int i = 0; i < count ; i++) { + if (portId == mAudioRecordClients.keyAt(i)) { + continue; + } + sp other = mAudioRecordClients.valueAt(i); + if (other->active) { + // keeps the last of the clients marked active + item->setCString(kAudioPolicyActiveSrc, + audioSourceString(other->attributes.source).c_str()); + item->setInt32(kAudioPolicyActiveSession, other->session); + if (other->opPackageName.size() != 0) { + item->setCString(kAudioPolicyActivePkg, + std::string(String8(other->opPackageName).string()).c_str()); + } else { + item->setCString(kAudioPolicyRqstPkg, + std::to_string(other->uid).c_str()); } + item->setCString(kAudioPolicyActiveDevice, + getDeviceTypeStrForPortId(other->deviceId).c_str()); } } item->selfrecord(); delete item; item = NULL; } - } - - if (status == NO_ERROR) { - LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL, - "startInput(): invalid concurrency type %d", (int)concurrency); - - // enforce permission (if any) required for each type of concurrency - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CALL) { - //TODO: check incall capture permission - } - if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_CAPTURE) { - //TODO: check concurrent capture permission - } - - client->active = true; - } else { + client->active = false; + client->startTimeNs = 0; + updateUidStates_l(); finishRecording(client->opPackageName, client->uid); } @@ -615,6 +576,7 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) if (mAudioPolicyManager == NULL) { return NO_INIT; } + Mutex::Autolock _l(mLock); ssize_t index = mAudioRecordClients.indexOfKey(portId); @@ -624,6 +586,9 @@ status_t AudioPolicyService::stopInput(audio_port_handle_t portId) sp client = mAudioRecordClients.valueAt(index); client->active = false; + client->startTimeNs = 0; + + updateUidStates_l(); // finish the recording app op finishRecording(client->opPackageName, client->uid); @@ -646,6 +611,14 @@ void AudioPolicyService::releaseInput(audio_port_handle_t portId) return; } client = mAudioRecordClients.valueAt(index); + + if (client->active) { + ALOGW("%s releasing active client portId %d", __FUNCTION__, portId); + client->active = false; + client->startTimeNs = 0; + updateUidStates_l(); + } + mAudioRecordClients.removeItem(portId); } if (client == 0) { diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 78dbf5f16b..2893872346 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -348,11 +348,91 @@ void AudioPolicyService::updateUidStates() void AudioPolicyService::updateUidStates_l() { - //TODO: implement real concurrent capture policy: for now just apply each app state directly +// Go over all active clients and allow capture (does not force silence) in the +// following cases: +// - The client is the assistant AND +// an accessibility service is on TOP AND the source is VOICE_RECOGNITION or HOTWORD +// OR +// is on TOP AND uses VOICE_RECOGNITION +// OR uses HOTWORD AND there is no privacy sensitive active capture +// - The client is an accessibility service AND +// is on TOP AND the source is VOICE_RECOGNITION or HOTWORD +// - Any other client AND +// The assistant is not on TOP AND +// is on TOP OR latest started AND +// there is no privacy sensitive active capture +//TODO: mamanage pre processing effects according to use case priority + + sp topActive; + sp latestActive; + nsecs_t latestStartNs = 0; + sp latestSensitiveActive; + nsecs_t latestSensitiveStartNs = 0; + bool isA11yOnTop = mUidPolicy->isA11yOnTop(); + bool isAssistantOnTop = false; + bool isSensitiveActive = false; + for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; - setAppState_l(current->uid, apmStatFromAmState(mUidPolicy->getUidState(current->uid))); + if (isPrivacySensitive(current->attributes.source)) { + if (current->startTimeNs > latestSensitiveStartNs) { + latestSensitiveActive = current; + latestSensitiveStartNs = current->startTimeNs; + } + isSensitiveActive = true; + } + if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) { + topActive = current; + latestActive = nullptr; + if (mUidPolicy->isAssistantUid(current->uid)) { + isAssistantOnTop = true; + } + } + if (current->startTimeNs > latestStartNs) { + latestActive = current; + latestStartNs = current->startTimeNs; + } + } + + if (topActive == nullptr && latestActive == nullptr) { + return; + } + + for (size_t i =0; i < mAudioRecordClients.size(); i++) { + sp current = mAudioRecordClients[i]; + if (!current->active) continue; + + audio_source_t source = current->attributes.source; + bool isOnTop = mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP; + bool isLatest = current == latestActive; + bool isLatestSensitive = current == latestSensitiveActive; + bool forceIdle = true; + if (mUidPolicy->isAssistantUid(current->uid)) { + if (isA11yOnTop) { + if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { + forceIdle = false; + } + } else { + if (((isOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) || + source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) { + forceIdle = false; + } + } + } else if (mUidPolicy->isA11yUid(current->uid)) { + if (isOnTop && + (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { + forceIdle = false; + } + } else { + if (!isAssistantOnTop && (isOnTop || (topActive == nullptr && isLatest)) && + (!isSensitiveActive || isLatestSensitive)) { + forceIdle = false; + } + } + setAppState_l(current->uid, + forceIdle ? APP_STATE_IDLE : + apmStatFromAmState(mUidPolicy->getUidState(current->uid))); } } @@ -369,6 +449,22 @@ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { return APP_STATE_FOREGROUND; } +/* static */ +bool AudioPolicyService::isPrivacySensitive(audio_source_t source) +{ + switch (source) { + case AUDIO_SOURCE_VOICE_UPLINK: + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + case AUDIO_SOURCE_CAMCORDER: + case AUDIO_SOURCE_VOICE_COMMUNICATION: + return true; + default: + break; + } + return false; +} + void AudioPolicyService::setAppState_l(uid_t uid, app_state_t state) { AutoCallerClear acc; @@ -548,6 +644,7 @@ void AudioPolicyService::UidPolicy::registerSelf() { mObserverRegistered = true; } else { ALOGE("UidPolicy::registerSelf linkToDeath failed: %d", res); + am.unregisterUidObserver(this); } } @@ -650,6 +747,7 @@ int AudioPolicyService::UidPolicy::getUidState(uid_t uid) { mCachedUids.insert(std::pair>(uid, std::pair(active, state))); } + return state; } @@ -730,6 +828,21 @@ void AudioPolicyService::UidPolicy::updateUidLocked(std::unordered_map::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid.first); + if (it == mA11yUids.end()) { + continue; + } + if (uid.second.second == ActivityManager::PROCESS_STATE_TOP || + uid.second.second == ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE || + uid.second.second == ActivityManager::PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + return true; + } + } + return false; +} + bool AudioPolicyService::UidPolicy::isA11yUid(uid_t uid) { std::vector::iterator it = find(mA11yUids.begin(), mA11yUids.end(), uid); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 4d7235fce8..dc5a36d34c 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -94,8 +94,7 @@ public: audio_input_flags_t flags, audio_port_handle_t *selectedDeviceId = NULL, audio_port_handle_t *portId = NULL); - virtual status_t startInput(audio_port_handle_t portId, - bool *silenced); + virtual status_t startInput(audio_port_handle_t portId); virtual status_t stopInput(audio_port_handle_t portId); virtual void releaseInput(audio_port_handle_t portId); virtual status_t initStreamVolume(audio_stream_type_t stream, @@ -276,6 +275,8 @@ private: void updateUidStates(); void updateUidStates_l(); + static bool isPrivacySensitive(audio_source_t source); + // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID // transitions to an active state we will start reporting buffers with data. This approach @@ -299,6 +300,7 @@ private: bool isAssistantUid(uid_t uid) { return uid == mAssistantUid; } void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } bool isA11yUid(uid_t uid); + bool isA11yOnTop(); // BnUidObserver implementation void onUidActive(uid_t uid) override; @@ -650,12 +652,11 @@ private: const audio_session_t session, const audio_port_handle_t deviceId, const String16& opPackageName) : AudioClient(attributes, io, uid, pid, session, deviceId), - opPackageName(opPackageName), isConcurrent(false), isVirtualDevice(false) {} + opPackageName(opPackageName), startTimeNs(0) {} ~AudioRecordClient() override = default; const String16 opPackageName; // client package name - bool isConcurrent; // is allowed to concurrent capture - bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr() + nsecs_t startTimeNs; }; // --- AudioPlaybackClient --- -- GitLab From 142598f2a35355c6d025a3f4a86c1615f85bfb71 Mon Sep 17 00:00:00 2001 From: Sampath Shetty Date: Mon, 10 Dec 2018 13:52:30 +1100 Subject: [PATCH 0579/1530] Handle audio presentation switching in AC-4 TS Audio preselection descriptors(APD) can switch mid-stream in AC-4 TS. Detect change in APD by monitoring PMT version & CRC. Update new audio presentation information to the stream metadata so that MediaExtractor.getAudioPresentations() always returns a latest audio presentation available. Bug: 63901775 Bug: 119312182 Test: atest android.media.cts.MediaExtractorTest#testGetAudioPresentations Change-Id: I7f29de6fb90b4ac1bddd4ddb47f83d35dc7f2345 --- media/libstagefright/mpeg2ts/ATSParser.cpp | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index cf93fcf5e7..590131e308 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -121,6 +121,8 @@ private: ATSParser *mParser; unsigned mProgramNumber; unsigned mProgramMapPID; + uint32_t mPMTVersion; + uint32_t mPMT_CRC; KeyedVector > mStreams; bool mFirstPTSValid; uint64_t mFirstPTS; @@ -143,6 +145,9 @@ struct ATSParser::Stream : public RefBase { unsigned typeExt() const { return mStreamTypeExt; } unsigned pid() const { return mElementaryPID; } void setPID(unsigned pid) { mElementaryPID = pid; } + void setAudioPresentations(AudioPresentationCollection audioPresentations) { + mAudioPresentations = audioPresentations; + } void setCasInfo( int32_t systemId, @@ -293,6 +298,8 @@ ATSParser::Program::Program( : mParser(parser), mProgramNumber(programNumber), mProgramMapPID(programMapPID), + mPMTVersion(0xffffffff), + mPMT_CRC(0xffffffff), mFirstPTSValid(false), mFirstPTS(0), mLastRecoveredPTS(lastRecoveredPTS) { @@ -480,7 +487,13 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { MY_LOGV(" program_number = %u", br->getBits(16)); MY_LOGV(" reserved = %u", br->getBits(2)); - MY_LOGV(" version_number = %u", br->getBits(5)); + bool audioPresentationsChanged = false; + unsigned pmtVersion = br->getBits(5); + if (pmtVersion != mPMTVersion) { + audioPresentationsChanged = true; + mPMTVersion = pmtVersion; + } + MY_LOGV(" version_number = %u", pmtVersion); MY_LOGV(" current_next_indicator = %u", br->getBits(1)); MY_LOGV(" section_number = %u", br->getBits(8)); MY_LOGV(" last_section_number = %u", br->getBits(8)); @@ -661,7 +674,12 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { if (infoBytesRemaining != 0) { ALOGW("Section data remains unconsumed"); } - MY_LOGV(" CRC = 0x%08x", br->getBits(32)); + unsigned crc = br->getBits(32); + if (crc != mPMT_CRC) { + audioPresentationsChanged = true; + mPMT_CRC = crc; + } + MY_LOGV(" CRC = 0x%08x", crc); bool PIDsChanged = false; for (size_t i = 0; i < infos.size(); ++i) { @@ -722,6 +740,10 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { isAddingScrambledStream |= info.mCADescriptor.mSystemID >= 0; mStreams.add(info.mPID, stream); } + else if (index >= 0 && mStreams.editValueAt(index)->isAudio() + && audioPresentationsChanged) { + mStreams.editValueAt(index)->setAudioPresentations(info.mAudioPresentations); + } } if (isAddingScrambledStream) { @@ -1732,6 +1754,7 @@ void ATSParser::Stream::onPayloadData( mSource->setFormat(mQueue->getFormat()); } mSource->queueAccessUnit(accessUnit); + mSource->convertAudioPresentationInfoToMetadata(mAudioPresentations); } // Every access unit has a pesStartOffset queued in |mPesStartOffsets|. -- GitLab From a46bedb84a6c972f128e17e7bed114ad0863b0b3 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 7 Dec 2018 18:01:26 -0800 Subject: [PATCH 0580/1530] audio policy: fix capture policy for assistant Fix concurrent capture policy for assistant: allow assistant to capture if running as foreground service too and not only if on top. Same fix for accessibility service. Also refine logic for single active client on top by retaining the latest started capture from an app on top (in case several apps can be on top). Also fix priority between app on top and app with foreground service. Bug: 120588242 Bug: 111438757 Test: Manual capture tests with solotester and Camera, Assistant and Duo Test: CTS tests for AudioReco Change-Id: I2fc54853a584b9e9e48c012b0c3a2bcc8436a340 --- .../service/AudioPolicyService.cpp | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 2893872346..ee5d6ffee9 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -350,23 +350,26 @@ void AudioPolicyService::updateUidStates_l() { // Go over all active clients and allow capture (does not force silence) in the // following cases: -// - The client is the assistant AND -// an accessibility service is on TOP AND the source is VOICE_RECOGNITION or HOTWORD -// OR -// is on TOP AND uses VOICE_RECOGNITION -// OR uses HOTWORD AND there is no privacy sensitive active capture -// - The client is an accessibility service AND -// is on TOP AND the source is VOICE_RECOGNITION or HOTWORD -// - Any other client AND -// The assistant is not on TOP AND -// is on TOP OR latest started AND -// there is no privacy sensitive active capture +// The client is the assistant +// AND an accessibility service is on TOP +// AND the source is VOICE_RECOGNITION or HOTWORD +// OR uses VOICE_RECOGNITION AND is on TOP OR latest started +// OR uses HOTWORD +// AND there is no privacy sensitive active capture +// OR The client is an accessibility service +// AND is on TOP OR latest started +// AND the source is VOICE_RECOGNITION or HOTWORD +// OR Any other client +// AND The assistant is not on TOP +// AND is on TOP OR latest started +// AND there is no privacy sensitive active capture //TODO: mamanage pre processing effects according to use case priority sp topActive; sp latestActive; - nsecs_t latestStartNs = 0; sp latestSensitiveActive; + nsecs_t topStartNs = 0; + nsecs_t latestStartNs = 0; nsecs_t latestSensitiveStartNs = 0; bool isA11yOnTop = mUidPolicy->isA11yOnTop(); bool isAssistantOnTop = false; @@ -383,8 +386,10 @@ void AudioPolicyService::updateUidStates_l() isSensitiveActive = true; } if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) { - topActive = current; - latestActive = nullptr; + if (current->startTimeNs > topStartNs) { + topActive = current; + topStartNs = current->startTimeNs; + } if (mUidPolicy->isAssistantUid(current->uid)) { isAssistantOnTop = true; } @@ -399,12 +404,16 @@ void AudioPolicyService::updateUidStates_l() return; } + if (topActive != nullptr) { + latestActive = nullptr; + } + for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; audio_source_t source = current->attributes.source; - bool isOnTop = mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP; + bool isOnTop = current == topActive; bool isLatest = current == latestActive; bool isLatestSensitive = current == latestSensitiveActive; bool forceIdle = true; @@ -414,18 +423,18 @@ void AudioPolicyService::updateUidStates_l() forceIdle = false; } } else { - if (((isOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) || + if ((((isOnTop || isLatest) && source == AUDIO_SOURCE_VOICE_RECOGNITION) || source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) { forceIdle = false; } } } else if (mUidPolicy->isA11yUid(current->uid)) { - if (isOnTop && + if ((isOnTop || isLatest) && (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { forceIdle = false; } } else { - if (!isAssistantOnTop && (isOnTop || (topActive == nullptr && isLatest)) && + if (!isAssistantOnTop && (isOnTop || isLatest) && (!isSensitiveActive || isLatestSensitive)) { forceIdle = false; } -- GitLab From f66521bbda90858dabcac6b3193e14ce235a4714 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Mon, 10 Dec 2018 19:33:05 -0800 Subject: [PATCH 0581/1530] frameSize using public API Bug: 112549970 Test: MediaPlayer2Test Change-Id: Idde6460d18cfdd59de2cf47531ebd6e0004a5e62 --- media/libmediaplayer2/JAudioTrack.cpp | 29 +++++++-------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index 7c2191b026..a01afa3acf 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -439,31 +439,16 @@ audio_format_t JAudioTrack::format() { size_t JAudioTrack::frameSize() { JNIEnv *env = JavaVMHelper::getJNIEnv(); - - // TODO: Calculated here implementing the logic in AudioTrack.java - // wait for AudioTrack.java exposing this parameter (i.e. getFrameSizeInBtytes()) - jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I"); - int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat); + jmethodID jGetFormat = env->GetMethodID(mAudioTrackCls, + "getFormat", "()Landroid/media/AudioFormat;"); + jobject jAudioFormatObj = env->CallObjectMethod(mAudioTrackObj, jGetFormat); jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat"); - jmethodID jIsEncodingLinearFrames = env->GetStaticMethodID( - jAudioFormatCls, "isEncodingLinearFrames", "(I)Z"); - jboolean javaIsEncodingLinearFrames = env->CallStaticBooleanMethod( - jAudioFormatCls, jIsEncodingLinearFrames, javaFormat); - - if (javaIsEncodingLinearFrames == false) { - return 1; - } - - jmethodID jGetBytesPerSample = env->GetStaticMethodID(jAudioFormatCls, - "getBytesPerSample", "(I)I"); - int javaBytesPerSample = env->CallStaticIntMethod(jAudioFormatCls, - jGetBytesPerSample, javaFormat); - - jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I"); - int javaChannelCount = env->CallIntMethod(mAudioTrackObj, jGetChannelCount); + jmethodID jGetFrameSizeInBytes = env->GetMethodID( + jAudioFormatCls, "getFrameSizeInBytes", "()I"); + jint javaFrameSizeInBytes = env->CallIntMethod(jAudioFormatObj, jGetFrameSizeInBytes); - return javaChannelCount * javaBytesPerSample; + return (size_t)javaFrameSizeInBytes; } status_t JAudioTrack::dump(int fd, const Vector& args __unused) const -- GitLab From 620084c71e2bbaeb43860d15e116f0be63fbf3b6 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 10 Dec 2018 19:46:45 -0800 Subject: [PATCH 0582/1530] Remove libmediaextractor from FlacExtractor Test: build Bug: 111407253 Change-Id: Iefb22fe3c26702bce5cea81c546290fb5f532b61 --- media/extractors/flac/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp index 4bf1295154..3a3d051b70 100644 --- a/media/extractors/flac/Android.bp +++ b/media/extractors/flac/Android.bp @@ -10,7 +10,6 @@ cc_library_shared { shared_libs: [ "libbinder_ndk", "liblog", - "libmediaextractor", "libmediandk", ], -- GitLab From 62d60f07425936dddbdd567ea902089d07e41139 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 10 Dec 2018 22:06:56 -0800 Subject: [PATCH 0583/1530] Fix the api level annotation for AMediaCodec_getBufferFormat Test: build Change-Id: I7e36b34e45b5eafce2f79e89c704f562afc4d653 --- media/ndk/include/media/NdkMediaCodec.h | 12 ++++++------ media/ndk/libmediandk.map.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h index 9dc120df29..b3ee85398c 100644 --- a/media/ndk/include/media/NdkMediaCodec.h +++ b/media/ndk/include/media/NdkMediaCodec.h @@ -240,12 +240,6 @@ ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *inf int64_t timeoutUs) __INTRODUCED_IN(21); AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*) __INTRODUCED_IN(21); -/** - * Get format of the buffer. The specified buffer index must have been previously obtained from - * dequeueOutputBuffer. - */ -AMediaFormat* AMediaCodec_getBufferFormat(AMediaCodec*, size_t index) __INTRODUCED_IN(21); - /** * If you are done with a buffer, use this call to return the buffer to * the codec. If you previously specified a surface when configuring this @@ -352,6 +346,12 @@ media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) __INTRODUC #if __ANDROID_API__ >= 28 +/** + * Get format of the buffer. The specified buffer index must have been previously obtained from + * dequeueOutputBuffer. + */ +AMediaFormat* AMediaCodec_getBufferFormat(AMediaCodec*, size_t index) __INTRODUCED_IN(28); + /** * Get the component name. If the codec was created by createDecoderByType * or createEncoderByType, what component is chosen is not known beforehand. diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 88736ab5f8..3567899b01 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -158,7 +158,7 @@ LIBMEDIANDK { AMediaCodec_dequeueInputBuffer; AMediaCodec_dequeueOutputBuffer; AMediaCodec_flush; - AMediaCodec_getBufferFormat; # introduced=21 + AMediaCodec_getBufferFormat; # introduced=28 AMediaCodec_getInputBuffer; AMediaCodec_getInputFormat; # introduced=28 AMediaCodec_getName; # introduced=28 -- GitLab From df27b04b52fd6a2ba9d9a5180adc2847386632a8 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 27 Nov 2018 18:55:09 -0800 Subject: [PATCH 0584/1530] Plumbing for OPUS encoding. Webm, OGG container extensions to hold opus. hooks so we can say that we're recording in opus format. OMX-specific changes are omitted since our target is Codec2 Bug: 111850384 Test: with separate omx routines Change-Id: Iecb8b53df3fbd8506d2e6f007602284eb2d0decc --- media/libmedia/MediaProfiles.cpp | 3 +- media/libmedia/include/media/mediarecorder.h | 6 +- .../StagefrightRecorder.cpp | 40 +- .../StagefrightRecorder.h | 1 + media/libstagefright/Android.bp | 3 + media/libstagefright/MediaMuxer.cpp | 5 + media/libstagefright/OggWriter.cpp | 397 ++++++++++++++++++ .../include/media/stagefright/MediaMuxer.h | 1 + .../include/media/stagefright/OggWriter.h | 73 ++++ media/libstagefright/opus/Android.bp | 21 + media/libstagefright/opus/OpusHeader.cpp | 185 ++++++++ .../libstagefright/opus/include/OpusHeader.h | 41 ++ media/libstagefright/webm/Android.bp | 1 + media/libstagefright/webm/WebmElement.cpp | 3 +- media/libstagefright/webm/WebmElement.h | 1 + media/libstagefright/webm/WebmWriter.cpp | 124 ++++-- 16 files changed, 858 insertions(+), 47 deletions(-) create mode 100644 media/libstagefright/OggWriter.cpp create mode 100644 media/libstagefright/include/media/stagefright/OggWriter.h create mode 100644 media/libstagefright/opus/Android.bp create mode 100644 media/libstagefright/opus/OpusHeader.cpp create mode 100644 media/libstagefright/opus/include/OpusHeader.h diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index e0f5a40092..08c6a50006 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -48,7 +48,8 @@ const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { {"amrwb", AUDIO_ENCODER_AMR_WB}, {"aac", AUDIO_ENCODER_AAC}, {"heaac", AUDIO_ENCODER_HE_AAC}, - {"aaceld", AUDIO_ENCODER_AAC_ELD} + {"aaceld", AUDIO_ENCODER_AAC_ELD}, + {"opus", AUDIO_ENCODER_OPUS} }; const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = { diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index d8b0fe7191..bdf1aaed5f 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -67,7 +67,7 @@ enum output_format { OUTPUT_FORMAT_AAC_ADTS = 6, OUTPUT_FORMAT_AUDIO_ONLY_END = 7, // Used in validating the output format. Should be the - // at the end of the audio only output formats. + // at the end of the audio only output formats. /* Stream over a socket, limited to a single stream */ OUTPUT_FORMAT_RTP_AVP = 7, @@ -81,6 +81,9 @@ enum output_format { /* HEIC data in a HEIF container */ OUTPUT_FORMAT_HEIF = 10, + /* Opus data in a OGG container */ + OUTPUT_FORMAT_OGG = 11, + OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; @@ -92,6 +95,7 @@ enum audio_encoder { AUDIO_ENCODER_HE_AAC = 4, AUDIO_ENCODER_AAC_ELD = 5, AUDIO_ENCODER_VORBIS = 6, + AUDIO_ENCODER_OPUS = 7, AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type }; diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index e3ae02eae0..eae52c2d72 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -948,6 +949,10 @@ status_t StagefrightRecorder::prepareInternal() { status = setupMPEG2TSRecording(); break; + case OUTPUT_FORMAT_OGG: + status = setupOggRecording(); + break; + default: ALOGE("Unsupported output file format: %d", mOutputFormat); status = UNKNOWN_ERROR; @@ -1013,6 +1018,7 @@ status_t StagefrightRecorder::start() { case OUTPUT_FORMAT_AAC_ADTS: case OUTPUT_FORMAT_RTP_AVP: case OUTPUT_FORMAT_MPEG2TS: + case OUTPUT_FORMAT_OGG: { sp meta = new MetaData; int64_t startTimeUs = systemTime() / 1000; @@ -1113,6 +1119,9 @@ sp StagefrightRecorder::createAudioSource() { format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC); format->setInt32("aac-profile", OMX_AUDIO_AACObjectELD); break; + case AUDIO_ENCODER_OPUS: + format->setString("mime", MEDIA_MIMETYPE_AUDIO_OPUS); + break; default: ALOGE("Unknown audio encoder: %d", mAudioEncoder); @@ -1169,6 +1178,13 @@ status_t StagefrightRecorder::setupAACRecording() { return setupRawAudioRecording(); } +status_t StagefrightRecorder::setupOggRecording() { + CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_OGG); + + mWriter = new OggWriter(mOutputFd); + return setupRawAudioRecording(); +} + status_t StagefrightRecorder::setupAMRRecording() { CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB || mOutputFormat == OUTPUT_FORMAT_AMR_WB); @@ -1813,6 +1829,7 @@ status_t StagefrightRecorder::setupAudioEncoder(const sp& writer) { case AUDIO_ENCODER_AAC: case AUDIO_ENCODER_HE_AAC: case AUDIO_ENCODER_AAC_ELD: + case AUDIO_ENCODER_OPUS: break; default: @@ -1863,19 +1880,18 @@ status_t StagefrightRecorder::setupMPEG4orWEBMRecording() { mTotalBitRate += mVideoBitRate; } - if (mOutputFormat != OUTPUT_FORMAT_WEBM) { - // Audio source is added at the end if it exists. - // This help make sure that the "recoding" sound is suppressed for - // camcorder applications in the recorded files. - // TODO Audio source is currently unsupported for webm output; vorbis encoder needed. - // disable audio for time lapse recording - bool disableAudio = mCaptureFpsEnable && mCaptureFps < mFrameRate; - if (!disableAudio && mAudioSource != AUDIO_SOURCE_CNT) { - err = setupAudioEncoder(writer); - if (err != OK) return err; - mTotalBitRate += mAudioBitRate; - } + // Audio source is added at the end if it exists. + // This help make sure that the "recoding" sound is suppressed for + // camcorder applications in the recorded files. + // disable audio for time lapse recording + const bool disableAudio = mCaptureFpsEnable && mCaptureFps < mFrameRate; + if (!disableAudio && mAudioSource != AUDIO_SOURCE_CNT) { + err = setupAudioEncoder(writer); + if (err != OK) return err; + mTotalBitRate += mAudioBitRate; + } + if (mOutputFormat != OUTPUT_FORMAT_WEBM) { if (mCaptureFpsEnable) { mp4writer->setCaptureRate(mCaptureFps); } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index faa2e59428..2ada30197c 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -166,6 +166,7 @@ private: void setupMPEG4orWEBMMetaData(sp *meta); status_t setupAMRRecording(); status_t setupAACRecording(); + status_t setupOggRecording(); status_t setupRawAudioRecording(); status_t setupRTPRecording(); status_t setupMPEG2TSRecording(); diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 02bb4e0e38..9aea88a599 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -120,6 +120,7 @@ cc_library_shared { "MediaMuxer.cpp", "NuCachedSource2.cpp", "NuMediaExtractor.cpp", + "OggWriter.cpp", "OMXClient.cpp", "OmxInfoBuilder.cpp", "RemoteMediaExtractor.cpp", @@ -159,6 +160,7 @@ cc_library_shared { "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx_utils", + "libstagefright_opus_common", "libstagefright_xmlparser", "libRScpp", "libhidlallocatorutils", @@ -179,6 +181,7 @@ cc_library_shared { "libstagefright_webm", "libstagefright_timedtext", "libvpx", + "libogg", "libwebm", "libstagefright_esds", "libstagefright_id3", diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp index 98f59b5d4b..9ba2add491 100644 --- a/media/libstagefright/MediaMuxer.cpp +++ b/media/libstagefright/MediaMuxer.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace android { @@ -52,6 +53,8 @@ MediaMuxer::MediaMuxer(int fd, OutputFormat format) mWriter = new MPEG4Writer(fd); } else if (format == OUTPUT_FORMAT_WEBM) { mWriter = new WebmWriter(fd); + } else if (format == OUTPUT_FORMAT_OGG) { + mWriter = new OggWriter(fd); } if (mWriter != NULL) { @@ -59,6 +62,8 @@ MediaMuxer::MediaMuxer(int fd, OutputFormat format) if (format == OUTPUT_FORMAT_HEIF) { // Note that the key uses recorder file types. mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_HEIF); + } else if (format == OUTPUT_FORMAT_OGG) { + mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_OGG); } mState = INITIALIZED; } diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp new file mode 100644 index 0000000000..ad55c56ca6 --- /dev/null +++ b/media/libstagefright/OggWriter.cpp @@ -0,0 +1,397 @@ +/* + * 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 "OggWriter" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "OpusHeader.h" + +extern "C" { +#include +} + +// store the int32 value in little-endian order. +static inline void writeint(char *buf, int base, int32_t val) { + buf[base + 3] = ((val) >> 24) & 0xff; + buf[base + 2] = ((val) >> 16) & 0xff; + buf[base + 1] = ((val) >> 8) & 0xff; + buf[base] = (val)&0xff; +} + +// linkage between our header OggStreamState and the underlying ogg_stream_state +// so that consumers of our interface do not require the ogg headers themselves. +struct OggStreamState : public ogg_stream_state {}; + +namespace android { + +OggWriter::OggWriter(int fd) + : mFd(dup(fd)), + mInitCheck(mFd < 0 ? NO_INIT : OK) { + // empty +} + +OggWriter::~OggWriter() { + if (mStarted) { + reset(); + } + + if (mFd != -1) { + close(mFd); + mFd = -1; + } + + free(mOs); +} + +status_t OggWriter::initCheck() const { + return mInitCheck; +} + +status_t OggWriter::addSource(const sp& source) { + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mSource != NULL) { + return UNKNOWN_ERROR; + } + + // Support is limited to single track of Opus audio. + const char* mime; + source->getFormat()->findCString(kKeyMIMEType, &mime); + const char* opus = MEDIA_MIMETYPE_AUDIO_OPUS; + if (strncasecmp(mime, opus, strlen(opus))) { + ALOGE("Track (%s) other than %s is not supported", mime, opus); + return ERROR_UNSUPPORTED; + } + + mOs = (OggStreamState*) malloc(sizeof(ogg_stream_state)); + if (ogg_stream_init((ogg_stream_state*)mOs, rand()) == -1) { + ALOGE("ogg stream init failed"); + return UNKNOWN_ERROR; + } + + // Write Ogg headers. + int32_t nChannels = 0; + if (!source->getFormat()->findInt32(kKeyChannelCount, &nChannels)) { + ALOGE("Missing format keys for audio track"); + source->getFormat()->dumpToLog(); + return BAD_VALUE; + } + source->getFormat()->dumpToLog(); + + int32_t sampleRate = 0; + if (!source->getFormat()->findInt32(kKeySampleRate, &sampleRate)) { + ALOGE("Missing format key for sample rate"); + source->getFormat()->dumpToLog(); + return UNKNOWN_ERROR; + } + + mSampleRate = sampleRate; + + OpusHeader header; + header.channels = nChannels; + header.num_streams = nChannels; + header.num_coupled = 0; + header.channel_mapping = ((nChannels > 8) ? 255 : (nChannels > 2)); + header.gain_db = 0; + header.skip_samples = 0; + + // headers are 21-bytes + something driven by channel count + // expect numbers in the low 30's here. WriteOpusHeader() will tell us + // if things are bad. + unsigned char header_data[100]; + ogg_packet op; + ogg_page og; + + const int packet_size = WriteOpusHeader(header, mSampleRate, (uint8_t*)header_data, + sizeof(header_data)); + + if (packet_size < 0) { + ALOGE("opus header writing failed"); + return UNKNOWN_ERROR; + } + op.packet = header_data; + op.bytes = packet_size; + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = 0; + ogg_stream_packetin((ogg_stream_state*)mOs, &op); + + int ret; + while ((ret = ogg_stream_flush((ogg_stream_state*)mOs, &og))) { + if (!ret) break; + write(mFd, og.header, og.header_len); + write(mFd, og.body, og.body_len); + } + + + const char* vendor_string = "libopus"; + const int vendor_length = strlen(vendor_string); + int user_comment_list_length = 0; + + const int comments_length = 8 + 4 + vendor_length + 4 + user_comment_list_length; + char* comments = (char*)malloc(comments_length); + if (comments == NULL) { + ALOGE("failed to allocate ogg comment buffer"); + return UNKNOWN_ERROR; + } + memcpy(comments, "OpusTags", 8); + writeint(comments, 8, vendor_length); + memcpy(comments + 12, vendor_string, vendor_length); + writeint(comments, 12 + vendor_length, user_comment_list_length); + + op.packet = (unsigned char*)comments; + op.bytes = comments_length; + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = 1; + ogg_stream_packetin((ogg_stream_state*)mOs, &op); + + while ((ret = ogg_stream_flush((ogg_stream_state*)mOs, &og))) { + if (!ret) break; + write(mFd, og.header, og.header_len); + write(mFd, og.body, og.body_len); + } + + mSource = source; + free(comments); + return OK; +} + +status_t OggWriter::start(MetaData* /* params */) { + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mSource == NULL) { + return UNKNOWN_ERROR; + } + + if (mStarted && mPaused) { + mPaused = false; + mResumed = true; + return OK; + } else if (mStarted) { + // Already started, does nothing + return OK; + } + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + mReachedEOS = false; + mDone = false; + + pthread_create(&mThread, &attr, ThreadWrapper, this); + pthread_attr_destroy(&attr); + + mStarted = true; + + return OK; +} + +status_t OggWriter::pause() { + if (!mStarted) { + return OK; + } + mPaused = true; + return OK; +} + +status_t OggWriter::reset() { + if (!mStarted) { + return OK; + } + + mDone = true; + + void* dummy; + pthread_join(mThread, &dummy); + + status_t err = static_cast(reinterpret_cast(dummy)); + { + status_t status = mSource->stop(); + if (err == OK && (status != OK && status != ERROR_END_OF_STREAM)) { + err = status; + } + } + + mStarted = false; + return err; +} + +bool OggWriter::exceedsFileSizeLimit() { + if (mMaxFileSizeLimitBytes == 0) { + return false; + } + return mEstimatedSizeBytes > mMaxFileSizeLimitBytes; +} + +bool OggWriter::exceedsFileDurationLimit() { + if (mMaxFileDurationLimitUs == 0) { + return false; + } + return mEstimatedDurationUs > mMaxFileDurationLimitUs; +} + +// static +void* OggWriter::ThreadWrapper(void* me) { + return (void*)(uintptr_t) static_cast(me)->threadFunc(); +} + +status_t OggWriter::threadFunc() { + mEstimatedDurationUs = 0; + mEstimatedSizeBytes = 0; + bool stoppedPrematurely = true; + int64_t previousPausedDurationUs = 0; + int64_t maxTimestampUs = 0; + status_t err = OK; + + prctl(PR_SET_NAME, (unsigned long)"OggWriter", 0, 0, 0); + + while (!mDone) { + MediaBufferBase* buffer = nullptr; + err = mSource->read(&buffer); + + if (err != OK) { + ALOGW("failed to read next buffer"); + break; + } + + if (mPaused) { + buffer->release(); + buffer = nullptr; + continue; + } + mEstimatedSizeBytes += buffer->range_length(); + if (exceedsFileSizeLimit()) { + buffer->release(); + buffer = nullptr; + notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); + ALOGW("estimated size(%" PRId64 ") exceeds limit (%" PRId64 ")", + mEstimatedSizeBytes, mMaxFileSizeLimitBytes); + break; + } + int64_t timestampUs; + CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs)); + if (timestampUs > mEstimatedDurationUs) { + mEstimatedDurationUs = timestampUs; + } + if (mResumed) { + previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000); + mResumed = false; + } + + timestampUs -= previousPausedDurationUs; + + ALOGV("time stamp: %" PRId64 ", previous paused duration: %" PRId64, timestampUs, + previousPausedDurationUs); + if (timestampUs > maxTimestampUs) { + maxTimestampUs = timestampUs; + } + + if (exceedsFileDurationLimit()) { + buffer->release(); + buffer = nullptr; + notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); + ALOGW("estimated duration(%" PRId64 " us) exceeds limit(%" PRId64 " us)", + mEstimatedDurationUs, mMaxFileDurationLimitUs); + break; + } + + ogg_packet op; + ogg_page og; + op.packet = (uint8_t*)buffer->data() + buffer->range_offset(); + op.bytes = (long)buffer->range_length(); + op.b_o_s = 0; + op.e_o_s = mReachedEOS ? 1 : 0; + // granulepos is the total number of PCM audio samples @ 48 kHz, up to and + // including the current packet. + ogg_int64_t granulepos = (48000 * mEstimatedDurationUs) / 1000000; + op.granulepos = granulepos; + + // Headers are at packets 0 and 1. + op.packetno = 2 + (ogg_int32_t)mCurrentPacketId++; + ogg_stream_packetin((ogg_stream_state*)mOs, &op); + size_t n = 0; + + while (ogg_stream_flush((ogg_stream_state*)mOs, &og) > 0) { + write(mFd, og.header, og.header_len); + write(mFd, og.body, og.body_len); + n = n + og.header_len + og.body_len; + } + + if (n < buffer->range_length()) { + buffer->release(); + buffer = nullptr; + err = ERROR_IO; + break; + } + + if (err != OK) { + break; + } + + stoppedPrematurely = false; + + buffer->release(); + buffer = nullptr; + } + + // end of stream is an ok thing + if (err == ERROR_END_OF_STREAM) { + err = OK; + } + + if (err == OK && stoppedPrematurely) { + err = ERROR_MALFORMED; + } + + close(mFd); + mFd = -1; + mReachedEOS = true; + + return err; +} + +bool OggWriter::reachedEOS() { + return mReachedEOS; +} + +} // namespace android diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h index 66f4d72f09..69d6cde06f 100644 --- a/media/libstagefright/include/media/stagefright/MediaMuxer.h +++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h @@ -49,6 +49,7 @@ public: OUTPUT_FORMAT_WEBM = 1, OUTPUT_FORMAT_THREE_GPP = 2, OUTPUT_FORMAT_HEIF = 3, + OUTPUT_FORMAT_OGG = 4, OUTPUT_FORMAT_LIST_END // must be last - used to validate format type }; diff --git a/media/libstagefright/include/media/stagefright/OggWriter.h b/media/libstagefright/include/media/stagefright/OggWriter.h new file mode 100644 index 0000000000..e3837cd9bb --- /dev/null +++ b/media/libstagefright/include/media/stagefright/OggWriter.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef OGG_WRITER_H_ + +#define OGG_WRITER_H_ + +#include + +#include +#include + +struct OggStreamState; + +namespace android { + +struct OggWriter : public MediaWriter { + OggWriter(int fd); + + status_t initCheck() const; + + virtual status_t addSource(const sp& source); + virtual bool reachedEOS(); + virtual status_t start(MetaData* params = NULL); + virtual status_t stop() { return reset(); } + virtual status_t pause(); + +protected: + ~OggWriter(); + +private: + int mFd; + status_t mInitCheck; + sp mSource; + bool mStarted = false; + volatile bool mPaused = false; + volatile bool mResumed = false; + volatile bool mDone; + volatile bool mReachedEOS; + pthread_t mThread; + int64_t mSampleRate; + int64_t mEstimatedSizeBytes; + int64_t mEstimatedDurationUs; + + static void* ThreadWrapper(void*); + status_t threadFunc(); + bool exceedsFileSizeLimit(); + bool exceedsFileDurationLimit(); + status_t reset(); + + int32_t mCurrentPacketId; + OggStreamState* mOs = nullptr; + + OggWriter(const OggWriter&); + OggWriter& operator=(const OggWriter&); +}; + +} // namespace android + +#endif // OGG_WRITER_H_ diff --git a/media/libstagefright/opus/Android.bp b/media/libstagefright/opus/Android.bp new file mode 100644 index 0000000000..c5086ec3c0 --- /dev/null +++ b/media/libstagefright/opus/Android.bp @@ -0,0 +1,21 @@ +cc_library_shared { + name: "libstagefright_opus_common", + vendor_available: true, + + export_include_dirs: ["include"], + + srcs: ["OpusHeader.cpp"], + + shared_libs: ["liblog"], + + cflags: ["-Werror"], + + sanitize: { + integer_overflow: true, + cfi: true, + diag: { + integer_overflow: true, + cfi: true, + }, + }, +} \ No newline at end of file diff --git a/media/libstagefright/opus/OpusHeader.cpp b/media/libstagefright/opus/OpusHeader.cpp new file mode 100644 index 0000000000..e4a460c683 --- /dev/null +++ b/media/libstagefright/opus/OpusHeader.cpp @@ -0,0 +1,185 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "SoftOpus" + +#include +#include + +#include + +#include "OpusHeader.h" + +namespace android { + +// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies +// mappings for up to 8 channels. This information is part of the Vorbis I +// Specification: +// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html +constexpr int kMaxChannels = 8; + +constexpr uint8_t kOpusChannelMap[kMaxChannels][kMaxChannels] = { + {0}, + {0, 1}, + {0, 2, 1}, + {0, 1, 2, 3}, + {0, 4, 1, 2, 3}, + {0, 4, 1, 2, 3, 5}, + {0, 4, 1, 2, 3, 5, 6}, + {0, 6, 1, 2, 3, 4, 5, 7}, +}; + +// Opus always has a 48kHz output rate. This is true for all Opus, not just this +// implementation. +constexpr int kRate = 48000; +// Size of the Opus header excluding optional mapping information. +constexpr size_t kOpusHeaderSize = 19; +// Offset to magic string that starts Opus header. +constexpr size_t kOpusHeaderLabelOffset = 0; +// Offset to Opus version in the Opus header. +constexpr size_t kOpusHeaderVersionOffset = 8; +// Offset to the channel count byte in the Opus header. +constexpr size_t kOpusHeaderChannelsOffset = 9; +// Offset to the pre-skip value in the Opus header. +constexpr size_t kOpusHeaderSkipSamplesOffset = 10; +// Offset to sample rate in the Opus header. +constexpr size_t kOpusHeaderSampleRateOffset = 12; +// Offset to the gain value in the Opus header. +constexpr size_t kOpusHeaderGainOffset = 16; +// Offset to the channel mapping byte in the Opus header. +constexpr size_t kOpusHeaderChannelMappingOffset = 18; +// Opus Header contains a stream map. The mapping values are in the header +// beyond the always present |kOpusHeaderSize| bytes of data. The mapping +// data contains stream count, coupling information, and per channel mapping +// values: +// - Byte 0: Number of streams. +// - Byte 1: Number coupled. +// - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping +// values. +// Offset to the number of streams in the Opus header. +constexpr size_t kOpusHeaderNumStreamsOffset = 19; +// Offset to the number of streams that are coupled in the Opus header. +constexpr size_t kOpusHeaderNumCoupledStreamsOffset = 20; +// Offset to the stream to channel mapping in the Opus header. +constexpr size_t kOpusHeaderStreamMapOffset = 21; +// Maximum packet size used in Xiph's opusdec. +constexpr int kMaxOpusOutputPacketSizeSamples = 960 * 6; + +// Default audio output channel layout. Used to initialize |stream_map| in +// OpusHeader, and passed to opus_multistream_decoder_create() when the header +// does not contain mapping information. The values are valid only for mono and +// stereo output: Opus streams with more than 2 channels require a stream map. +constexpr int kMaxChannelsWithDefaultLayout = 2; +constexpr uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = {0, 1}; + +static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_offset) { + // check whether the 2nd byte is within the buffer + if (read_offset + 1 >= data_size) return 0; + uint16_t val; + val = data[read_offset]; + val |= data[read_offset + 1] << 8; + return val; +} + +// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header +bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header) { + if (data_size < kOpusHeaderSize) { + ALOGV("Header size is too small."); + return false; + } + header->channels = data[kOpusHeaderChannelsOffset]; + + if (header->channels < 1 || header->channels > kMaxChannels) { + ALOGV("Invalid Header, bad channel count: %d", header->channels); + return false; + } + header->skip_samples = ReadLE16(data, data_size, kOpusHeaderSkipSamplesOffset); + header->gain_db = static_cast(ReadLE16(data, data_size, kOpusHeaderGainOffset)); + header->channel_mapping = data[kOpusHeaderChannelMappingOffset]; + if (!header->channel_mapping) { + if (header->channels > kMaxChannelsWithDefaultLayout) { + ALOGV("Invalid Header, missing stream map."); + return false; + } + header->num_streams = 1; + header->num_coupled = header->channels > 1; + header->stream_map[0] = 0; + header->stream_map[1] = 1; + return true; + } + if (data_size < kOpusHeaderStreamMapOffset + header->channels) { + ALOGV("Invalid stream map; insufficient data for current channel " + "count: %d", + header->channels); + return false; + } + header->num_streams = data[kOpusHeaderNumStreamsOffset]; + header->num_coupled = data[kOpusHeaderNumCoupledStreamsOffset]; + if (header->num_streams + header->num_coupled != header->channels) { + ALOGV("Inconsistent channel mapping."); + return false; + } + for (int i = 0; i < header->channels; ++i) + header->stream_map[i] = data[kOpusHeaderStreamMapOffset + i]; + return true; +} + +int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, + uint8_t* output, size_t output_size) { + // See https://wiki.xiph.org/OggOpus#ID_Header. + const size_t total_size = kOpusHeaderStreamMapOffset + header.channels; + if (output_size < total_size) { + ALOGE("Output buffer too small for header."); + return -1; + } + + // ensure entire header is cleared, even though we overwrite much of it below + memset(output, 0, output_size); + + // Set magic signature. + memcpy(output + kOpusHeaderLabelOffset, "OpusHead", 8); + // Set Opus version. + output[kOpusHeaderVersionOffset] = 1; + // Set channel count. + output[kOpusHeaderChannelsOffset] = (uint8_t)header.channels; + // Set pre-skip + memcpy(output + kOpusHeaderSkipSamplesOffset, &header.skip_samples, sizeof(uint16_t)); + // Set original input sample rate in Hz. + memcpy(output + kOpusHeaderSampleRateOffset, &input_sample_rate, sizeof(uint32_t)); + // Set output gain in dB. + memcpy(output + kOpusHeaderGainOffset, &header.gain_db, sizeof(uint16_t)); + + if (header.channels > 2) { + // Set channel mapping + output[kOpusHeaderChannelMappingOffset] = 1; + // Assuming no coupled streams. This should actually be + // channels() - |coupled_streams|. + output[kOpusHeaderNumStreamsOffset] = header.channels; + output[kOpusHeaderNumCoupledStreamsOffset] = 0; + + // Set the actual stream map. + for (int i = 0; i < header.channels; ++i) { + output[kOpusHeaderStreamMapOffset + i] = kOpusChannelMap[header.channels - 1][i]; + } + return kOpusHeaderStreamMapOffset + header.channels + 1; + } else { + output[kOpusHeaderChannelMappingOffset] = 0; + return kOpusHeaderChannelMappingOffset + 1; + } +} + +} // namespace android diff --git a/media/libstagefright/opus/include/OpusHeader.h b/media/libstagefright/opus/include/OpusHeader.h new file mode 100644 index 0000000000..f9f79cd905 --- /dev/null +++ b/media/libstagefright/opus/include/OpusHeader.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +/* + * The Opus specification is part of IETF RFC 6716: + * http://tools.ietf.org/html/rfc6716 + */ + +#ifndef OPUS_HEADER_H_ +#define OPUS_HEADER_H_ + +namespace android { + +struct OpusHeader { + int channels; + int channel_mapping; + int num_streams; + int num_coupled; + int16_t gain_db; + int skip_samples; + uint8_t stream_map[8]; +}; + +bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header); +int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size); +} // namespace android + +#endif // OPUS_HEADER_H_ diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp index 64ecc2db19..1f840b78ca 100644 --- a/media/libstagefright/webm/Android.bp +++ b/media/libstagefright/webm/Android.bp @@ -28,6 +28,7 @@ cc_library_static { shared_libs: [ "libstagefright_foundation", + "libstagefright_opus_common", "libutils", "liblog", ], diff --git a/media/libstagefright/webm/WebmElement.cpp b/media/libstagefright/webm/WebmElement.cpp index a5120b9617..4d504e08e6 100644 --- a/media/libstagefright/webm/WebmElement.cpp +++ b/media/libstagefright/webm/WebmElement.cpp @@ -305,6 +305,7 @@ sp WebmElement::SegmentInfo(uint64_t scale, double dur) { } sp WebmElement::AudioTrackEntry( + const char *codec, int chans, double rate, const sp &buf, @@ -322,7 +323,7 @@ sp WebmElement::AudioTrackEntry( uid, lacing, lang, - "A_VORBIS", + codec, kAudioType, trackEntryFields); diff --git a/media/libstagefright/webm/WebmElement.h b/media/libstagefright/webm/WebmElement.h index ffbba1bf9e..a94c23fa93 100644 --- a/media/libstagefright/webm/WebmElement.h +++ b/media/libstagefright/webm/WebmElement.h @@ -50,6 +50,7 @@ struct WebmElement : public LightRefBase { static sp SegmentInfo(uint64_t scale = 1000000, double dur = 0); static sp AudioTrackEntry( + const char *codec, int chans, double rate, const sp &buf, diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp index 4d73eb8ea4..7b4b23a799 100644 --- a/media/libstagefright/webm/WebmWriter.cpp +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include @@ -112,46 +114,102 @@ sp WebmWriter::videoTrack(const sp& md) { // static sp WebmWriter::audioTrack(const sp& md) { int32_t nChannels, samplerate; - uint32_t type; - const void *headerData1; - const char headerData2[] = { 3, 'v', 'o', 'r', 'b', 'i', 's', 7, 0, 0, 0, - 'a', 'n', 'd', 'r', 'o', 'i', 'd', 0, 0, 0, 0, 1 }; - const void *headerData3; - size_t headerSize1, headerSize2 = sizeof(headerData2), headerSize3; + const char* mimeType; if (!md->findInt32(kKeyChannelCount, &nChannels) - || !md->findInt32(kKeySampleRate, &samplerate) - || !md->findData(kKeyVorbisInfo, &type, &headerData1, &headerSize1) - || !md->findData(kKeyVorbisBooks, &type, &headerData3, &headerSize3)) { + || !md->findInt32(kKeySampleRate, &samplerate) + || !md->findCString(kKeyMIMEType, &mimeType)) { ALOGE("Missing format keys for audio track"); md->dumpToLog(); return NULL; } - size_t codecPrivateSize = 1; - codecPrivateSize += XiphLaceCodeLen(headerSize1); - codecPrivateSize += XiphLaceCodeLen(headerSize2); - codecPrivateSize += headerSize1 + headerSize2 + headerSize3; + if (!strncasecmp(mimeType, MEDIA_MIMETYPE_AUDIO_OPUS, strlen(MEDIA_MIMETYPE_AUDIO_OPUS))) { + // Opus in WebM is a well-known, yet under-documented, format. The codec private data + // of the track is an Opus Ogg header (https://tools.ietf.org/html/rfc7845#section-5.1) + // The name of the track isn't standardized, its value should be "A_OPUS". + OpusHeader header; + header.channels = nChannels; + header.num_streams = nChannels; + header.num_coupled = 0; + // - Channel mapping family (8 bits unsigned) + // -- 0 = one stream: mono or L,R stereo + // -- 1 = channels in vorbis spec order: mono or L,R stereo or ... or FL,C,FR,RL,RR,LFE, ... + // -- 2..254 = reserved (treat as 255) + // -- 255 = no defined channel meaning + // + // our implementation encodes: 0, 1, or 255 + header.channel_mapping = ((nChannels > 8) ? 255 : (nChannels > 2)); + header.gain_db = 0; + header.skip_samples = 0; + + // headers are 21-bytes + something driven by channel count + // expect numbers in the low 30's here. WriteOpusHeader() will tell us + // if things are bad. + unsigned char header_data[100]; + int headerSize = WriteOpusHeader(header, samplerate, (uint8_t*)header_data, + sizeof(header_data)); + + if (headerSize < 0) { + // didn't fill out that header for some reason + ALOGE("failed to generate OPUS header"); + return NULL; + } + + size_t codecPrivateSize = 0; + codecPrivateSize += headerSize; + + off_t off = 0; + sp codecPrivateBuf = new ABuffer(codecPrivateSize); + uint8_t* codecPrivateData = codecPrivateBuf->data(); + + memcpy(codecPrivateData + off, (uint8_t*)header_data, headerSize); + sp entry = + WebmElement::AudioTrackEntry("A_OPUS", nChannels, samplerate, codecPrivateBuf); + return entry; + } else if (!strncasecmp(mimeType, + MEDIA_MIMETYPE_AUDIO_VORBIS, + strlen(MEDIA_MIMETYPE_AUDIO_VORBIS))) { + uint32_t type; + const void *headerData1; + const char headerData2[] = { 3, 'v', 'o', 'r', 'b', 'i', 's', 7, 0, 0, 0, + 'a', 'n', 'd', 'r', 'o', 'i', 'd', 0, 0, 0, 0, 1 }; + const void *headerData3; + size_t headerSize1, headerSize2 = sizeof(headerData2), headerSize3; + + if (!md->findData(kKeyVorbisInfo, &type, &headerData1, &headerSize1) + || !md->findData(kKeyVorbisBooks, &type, &headerData3, &headerSize3)) { + ALOGE("Missing header format keys for vorbis track"); + md->dumpToLog(); + return NULL; + } - off_t off = 0; - sp codecPrivateBuf = new ABuffer(codecPrivateSize); - uint8_t *codecPrivateData = codecPrivateBuf->data(); - codecPrivateData[off++] = 2; + size_t codecPrivateSize = 1; + codecPrivateSize += XiphLaceCodeLen(headerSize1); + codecPrivateSize += XiphLaceCodeLen(headerSize2); + codecPrivateSize += headerSize1 + headerSize2 + headerSize3; - off += XiphLaceEnc(codecPrivateData + off, headerSize1); - off += XiphLaceEnc(codecPrivateData + off, headerSize2); + off_t off = 0; + sp codecPrivateBuf = new ABuffer(codecPrivateSize); + uint8_t *codecPrivateData = codecPrivateBuf->data(); + codecPrivateData[off++] = 2; - memcpy(codecPrivateData + off, headerData1, headerSize1); - off += headerSize1; - memcpy(codecPrivateData + off, headerData2, headerSize2); - off += headerSize2; - memcpy(codecPrivateData + off, headerData3, headerSize3); + off += XiphLaceEnc(codecPrivateData + off, headerSize1); + off += XiphLaceEnc(codecPrivateData + off, headerSize2); - sp entry = WebmElement::AudioTrackEntry( - nChannels, - samplerate, - codecPrivateBuf); - return entry; + memcpy(codecPrivateData + off, headerData1, headerSize1); + off += headerSize1; + memcpy(codecPrivateData + off, headerData2, headerSize2); + off += headerSize2; + memcpy(codecPrivateData + off, headerData3, headerSize3); + + sp entry = + WebmElement::AudioTrackEntry("A_VORBIS", nChannels, samplerate, codecPrivateBuf); + return entry; + } else { + ALOGE("Track (%s) is not a supported audio format", mimeType); + return NULL; + } } size_t WebmWriter::numTracks() { @@ -382,16 +440,18 @@ status_t WebmWriter::addSource(const sp &source) { const char *vp8 = MEDIA_MIMETYPE_VIDEO_VP8; const char *vp9 = MEDIA_MIMETYPE_VIDEO_VP9; const char *vorbis = MEDIA_MIMETYPE_AUDIO_VORBIS; + const char* opus = MEDIA_MIMETYPE_AUDIO_OPUS; size_t streamIndex; if (!strncasecmp(mime, vp8, strlen(vp8)) || !strncasecmp(mime, vp9, strlen(vp9))) { streamIndex = kVideoIndex; - } else if (!strncasecmp(mime, vorbis, strlen(vorbis))) { + } else if (!strncasecmp(mime, vorbis, strlen(vorbis)) || + !strncasecmp(mime, opus, strlen(opus))) { streamIndex = kAudioIndex; } else { - ALOGE("Track (%s) other than %s, %s or %s is not supported", - mime, vp8, vp9, vorbis); + ALOGE("Track (%s) other than %s, %s, %s, or %s is not supported", + mime, vp8, vp9, vorbis, opus); return ERROR_UNSUPPORTED; } -- GitLab From ce82a5f11bca88c8acc7e516a6f7b84df9cec284 Mon Sep 17 00:00:00 2001 From: ybai Date: Thu, 1 Nov 2018 11:19:19 +0800 Subject: [PATCH 0585/1530] Add AudioPresentation selection support for ACodec Bug: 119312182 Test: compile Change-Id: Ie3505f3f92c1277dbd88a5f345cebdb837e23974 --- media/libstagefright/ACodec.cpp | 23 +++++++++++++++++++ .../include/media/stagefright/ACodec.h | 1 + media/ndk/NdkMediaFormat.cpp | 3 +++ media/ndk/include/media/NdkMediaFormat.h | 2 ++ 4 files changed, 29 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index f4b5600366..114f49223d 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2357,6 +2357,17 @@ status_t ACodec::getLatency(uint32_t *latency) { return err; } +status_t ACodec::setAudioPresentation(int32_t presentationId, int32_t programId) { + OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION config; + InitOMXParams(&config); + config.nPresentationId = (OMX_S32)presentationId; + config.nProgramId = (OMX_S32)programId; + status_t err = mOMXNode->setConfig( + (OMX_INDEXTYPE)OMX_IndexConfigAudioPresentation, + &config, sizeof(config)); + return err; +} + status_t ACodec::setPriority(int32_t priority) { if (priority < 0) { return BAD_VALUE; @@ -7452,6 +7463,18 @@ status_t ACodec::setParameters(const sp ¶ms) { } } + int32_t presentationId = -1; + if (params->findInt32("audio-presentation-presentation-id", &presentationId)) { + int32_t programId = -1; + params->findInt32("audio-presentation-program-id", &programId); + status_t err = setAudioPresentation(presentationId, programId); + if (err != OK) { + ALOGI("[%s] failed setAudioPresentation. Failure is fine since this key is optional", + mComponentName.c_str()); + err = OK; + } + } + // Ignore errors as failure is expected for codecs that aren't video encoders. (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat); diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 5dd1966c4f..80125d433c 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -479,6 +479,7 @@ private: status_t setPriority(int32_t priority); status_t setLatency(uint32_t latency); status_t getLatency(uint32_t *latency); + status_t setAudioPresentation(int32_t presentationId, int32_t programId); status_t setOperatingRate(float rateFloat, bool isVideo); status_t getIntraRefreshPeriod(uint32_t *intraRefreshPeriod); status_t setIntraRefreshPeriod(uint32_t intraRefreshPeriod, bool inConfigure); diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 200439cfbe..e0af80d088 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -273,6 +273,9 @@ EXPORT const char* AMEDIAFORMAT_KEY_ALBUMART = "albumart"; EXPORT const char* AMEDIAFORMAT_KEY_ALBUMARTIST = "albumartist"; EXPORT const char* AMEDIAFORMAT_KEY_ARTIST = "artist"; EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO = "audio-presentation-info"; +EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PRESENTATION_ID = + "audio-presentation-presentation-id"; +EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PROGRAM_ID = "audio-presentation-program-id"; EXPORT const char* AMEDIAFORMAT_KEY_AUDIO_SESSION_ID = "audio-session-id"; EXPORT const char* AMEDIAFORMAT_KEY_AUTHOR = "author"; EXPORT const char* AMEDIAFORMAT_KEY_BITRATE_MODE = "bitrate-mode"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 13d9135c15..2cd1d04e64 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -181,6 +181,8 @@ extern const char* AMEDIAFORMAT_KEY_ALBUMART __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ALBUMARTIST __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ARTIST __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PRESENTATION_ID __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_PROGRAM_ID __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29); -- GitLab From fd8212e7ce78001c11ef12b3d52470cdcfd587d9 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 11 Dec 2018 14:58:30 -0800 Subject: [PATCH 0586/1530] Fix double free Bug: 111407253 Test: manual, CTS Change-Id: I1515771b8f86d0865085df5d3803aa6cb61201f4 --- media/extractors/flac/FLACExtractor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 22b96e5af3..4e04605947 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -586,9 +586,6 @@ void FLACParser::allocateBuffers(MediaBufferGroupHelperV3 *group) void FLACParser::releaseBuffers() { - CHECK(mGroup != NULL); - delete mGroup; - mGroup = NULL; } MediaBufferHelperV3 *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) -- GitLab From 8a33265d83ca06dadaf27a60328da4a844757b2e Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Tue, 11 Dec 2018 14:11:18 -0800 Subject: [PATCH 0587/1530] Initialize all members of StagefrightPluginLoader Function pointers should all be initialized to null. Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 120785121 Change-Id: If9aa3c0ebd8888ea6e0b550d55397633657802fc --- media/libstagefright/StagefrightPluginLoader.cpp | 4 +--- media/libstagefright/StagefrightPluginLoader.h | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index 26d7dffdeb..b90649cc7b 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -34,9 +34,7 @@ constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so"; } // unnamed namespace -StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) - : mCreateCodec(nullptr), - mCreateBuilder(nullptr) { +StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) { if (android::base::GetIntProperty("debug.media.codec2", 0) == 0) { ALOGD("CCodec is disabled."); return; diff --git a/media/libstagefright/StagefrightPluginLoader.h b/media/libstagefright/StagefrightPluginLoader.h index 999d30c3c4..78effbf798 100644 --- a/media/libstagefright/StagefrightPluginLoader.h +++ b/media/libstagefright/StagefrightPluginLoader.h @@ -40,10 +40,10 @@ private: static Mutex sMutex; static std::unique_ptr sInstance; - void *mLibHandle; - CodecBase::CreateCodecFunc mCreateCodec; - MediaCodecListBuilderBase::CreateBuilderFunc mCreateBuilder; - CodecBase::CreateInputSurfaceFunc mCreateInputSurface; + void *mLibHandle{nullptr}; + CodecBase::CreateCodecFunc mCreateCodec{nullptr}; + MediaCodecListBuilderBase::CreateBuilderFunc mCreateBuilder{nullptr}; + CodecBase::CreateInputSurfaceFunc mCreateInputSurface{nullptr}; }; } // namespace android -- GitLab From dd6da3e10b2757a059165d636b0927392e745a9d Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 11 Dec 2018 14:41:41 -0800 Subject: [PATCH 0588/1530] Audio Policy: enable surround sound configuration in xml The surround sound configuration used to be disabled. This patch enables it, and moves out the surround sound configuration into a dedicated file. Bug: 116356348 Test: xmllint --noout --xinclude --schema \ hardware/interfaces/audio/5.0/config/audio_policy_configuration.xsd \ frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml Change-Id: I26eb55b818d813494fc31812d00e33851b79f02b --- .../config/audio_policy_configuration.xml | 27 +++------------- .../surround_sound_configuration_5_0.xml | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 services/audiopolicy/config/surround_sound_configuration_5_0.xml diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index b5ecbf90c7..42c52deec9 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -198,27 +198,10 @@ - - - - - - - - - - - - - - - - - - ?> + + + + + diff --git a/services/audiopolicy/config/surround_sound_configuration_5_0.xml b/services/audiopolicy/config/surround_sound_configuration_5_0.xml new file mode 100644 index 0000000000..590a1812ec --- /dev/null +++ b/services/audiopolicy/config/surround_sound_configuration_5_0.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + -- GitLab From 6012f913f0772cf0b73f379778efc671ce72c557 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 2 Nov 2018 17:06:30 -0700 Subject: [PATCH 0589/1530] Querying capability of haptic playback. Bug: 111454766 Test: Manually test Change-Id: Ib200d6b8f02354a01cf599a96fb1a9062dcd3050 --- media/libaudioclient/AudioSystem.cpp | 7 +++++++ media/libaudioclient/IAudioPolicyService.cpp | 19 +++++++++++++++++++ .../include/media/AudioSystem.h | 2 ++ .../include/media/IAudioPolicyService.h | 2 ++ services/audiopolicy/AudioPolicyInterface.h | 2 ++ .../managerdefault/AudioPolicyManager.cpp | 17 +++++++++++++++++ .../managerdefault/AudioPolicyManager.h | 2 ++ .../service/AudioPolicyInterfaceImpl.cpp | 11 +++++++++++ .../audiopolicy/service/AudioPolicyService.h | 2 ++ 9 files changed, 64 insertions(+) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index ec36ed7dc8..78f3cae5ec 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1315,6 +1315,13 @@ status_t AudioSystem::setA11yServicesUids(const std::vector& uids) return aps->setA11yServicesUids(uids); } +bool AudioSystem::isHapticPlaybackSupported() +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return false; + return aps->isHapticPlaybackSupported(); +} + // --------------------------------------------------------------------------- diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index a406658a16..555ac87a58 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -88,6 +88,7 @@ enum { REMOVE_SOURCE_DEFAULT_EFFECT, SET_ASSISTANT_UID, SET_A11Y_SERVICES_UIDS, + IS_HAPTIC_PLAYBACK_SUPPORTED, }; #define MAX_ITEMS_PER_LIST 1024 @@ -970,6 +971,17 @@ public: return static_cast (reply.readInt32()); } + virtual bool isHapticPlaybackSupported() + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + status_t status = remote()->transact(IS_HAPTIC_PLAYBACK_SUPPORTED, data, &reply); + if (status != NO_ERROR) { + return false; + } + return reply.readBool(); + } + }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1777,6 +1789,13 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case IS_HAPTIC_PLAYBACK_SUPPORTED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + bool isSupported = isHapticPlaybackSupported(); + reply->writeBool(isSupported); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 76a79c9536..a04b494909 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -346,6 +346,8 @@ public: static status_t setAssistantUid(uid_t uid); static status_t setA11yServicesUids(const std::vector& uids); + static bool isHapticPlaybackSupported(); + // ---------------------------------------------------------------------------- class AudioPortCallback : public RefBase diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index a246df68ea..b74d25be27 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -182,6 +182,8 @@ public: virtual status_t setAssistantUid(uid_t uid) = 0; virtual status_t setA11yServicesUids(const std::vector& uids) = 0; + + virtual bool isHapticPlaybackSupported() = 0; }; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 3c3a82b38f..7c7fe854fa 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -242,6 +242,8 @@ public: bool reported) = 0; virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0; + virtual bool isHapticPlaybackSupported() = 0; + virtual void setAppState(uid_t uid, app_state_t state); }; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6ec6a767dd..6a5a646c66 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3736,6 +3736,23 @@ void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) } } +bool AudioPolicyManager::isHapticPlaybackSupported() +{ + for (const auto& hwModule : mHwModules) { + const OutputProfileCollection &outputProfiles = hwModule->getOutputProfiles(); + for (const auto &outProfile : outputProfiles) { + struct audio_port audioPort; + outProfile->toAudioPort(&audioPort); + for (size_t i = 0; i < audioPort.num_channel_masks; i++) { + if (audioPort.channel_masks[i] & AUDIO_CHANNEL_HAPTIC_ALL) { + return true; + } + } + } + } + return false; +} + status_t AudioPolicyManager::disconnectAudioSource(const sp& sourceDesc) { ALOGV("%s port Id %d", __FUNCTION__, sourceDesc->portId()); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 0436b1d0f8..c85ecf68a3 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -244,6 +244,8 @@ public: virtual void setAppState(uid_t uid, app_state_t state); + virtual bool isHapticPlaybackSupported(); + protected: // A constructor that allows more fine-grained control over initialization process, // used in automatic tests. diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 59c8f10075..cf7243110d 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1149,4 +1149,15 @@ status_t AudioPolicyService::setA11yServicesUids(const std::vector& uids) return NO_ERROR; } +bool AudioPolicyService::isHapticPlaybackSupported() +{ + if (mAudioPolicyManager == NULL) { + ALOGW("%s, mAudioPolicyManager == NULL", __func__); + return false; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->isHapticPlaybackSupported(); +} + } // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index ec32511387..7638c0ca54 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -217,6 +217,8 @@ public: virtual status_t setAssistantUid(uid_t uid); virtual status_t setA11yServicesUids(const std::vector& uids); + virtual bool isHapticPlaybackSupported(); + status_t doStopOutput(audio_port_handle_t portId); void doReleaseOutput(audio_port_handle_t portId); -- GitLab From 405733256a9cf62c9e3be2d8dbd5ccb9cb7c5d97 Mon Sep 17 00:00:00 2001 From: jiabin Date: Thu, 8 Nov 2018 12:08:02 -0800 Subject: [PATCH 0590/1530] Support open output stream with haptic channel mask. When haptic playback is supported according to audio policy configuration file, try to open the output stream with haptic channel mask. When trying to creat a track with haptic channel mask, use haptic output if it is available. Bug: 111454766 Test: Manually test Change-Id: Ia8c70dd7f602a134d0509630eb734b8c540dea7d --- media/libmedia/TypeConverter.cpp | 6 +++ services/audioflinger/AudioFlinger.h | 17 +++---- .../managerdefault/AudioPolicyManager.cpp | 47 ++++++++++++++----- .../managerdefault/AudioPolicyManager.h | 6 ++- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index 514c7957ab..fb861d766c 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -233,6 +233,12 @@ const OutputChannelConverter::Table OutputChannelConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1), MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT2), MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1POINT4), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_HAPTIC_A), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO_HAPTIC_A), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_HAPTIC_AB), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB), + MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB), TERMINATOR }; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 1b20693d31..6c698f63fc 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -365,16 +365,17 @@ private: static inline bool isValidPcmSinkChannelMask(audio_channel_mask_t channelMask) { switch (audio_channel_mask_get_representation(channelMask)) { case AUDIO_CHANNEL_REPRESENTATION_POSITION: { - uint32_t channelCount = FCC_2; // stereo is default - if (kEnableExtendedChannels) { - channelCount = audio_channel_count_from_out_mask(channelMask); - if (channelCount < FCC_2 // mono is not supported at this time - || channelCount > AudioMixer::MAX_NUM_CHANNELS) { - return false; - } + // Haptic channel mask is only applicable for channel position mask. + const uint32_t channelCount = audio_channel_count_from_out_mask( + channelMask & ~AUDIO_CHANNEL_HAPTIC_ALL); + const uint32_t maxChannelCount = kEnableExtendedChannels + ? AudioMixer::MAX_NUM_CHANNELS : FCC_2; + if (channelCount < FCC_2 // mono is not supported at this time + || channelCount > maxChannelCount) { + return false; } // check that channelMask is the "canonical" one we expect for the channelCount. - return channelMask == audio_channel_out_mask_from_count(channelCount); + return audio_channel_position_mask_is_out_canonical(channelMask); } case AUDIO_CHANNEL_REPRESENTATION_INDEX: if (kEnableExtendedChannels) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6ec6a767dd..4c339bcb92 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -482,7 +482,7 @@ sp AudioPolicyManager::createTelephonyPatch( audio_devices_t outputDevice = isRx ? device : AUDIO_DEVICE_OUT_TELEPHONY_TX; SortedVector outputs = getOutputsForDevice(outputDevice, mOutputs); - audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); + audio_io_handle_t output = selectOutput(outputs); // request to reuse existing output stream if one is already opened to reach the target device if (output != AUDIO_IO_HANDLE_NONE) { sp outputDesc = mOutputs.valueFor(output); @@ -749,7 +749,7 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) // and AudioSystem::getOutputSamplingRate(). SortedVector outputs = getOutputsForDevice(device, mOutputs); - audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); + audio_io_handle_t output = selectOutput(outputs); ALOGV("getOutput() stream %d selected device %08x, output %d", stream, device, output); return output; @@ -1064,7 +1064,8 @@ non_direct_output: // at this stage we should ignore the DIRECT flag as no direct output could be found earlier *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT); - output = selectOutput(outputs, *flags, config->format); + output = selectOutput(outputs, *flags, config->format, + config->channel_mask, config->sample_rate); } ALOGW_IF((output == 0), "getOutputForDevice() could not find output for stream %d, " "sampling rate %d, format %#x, channels %#x, flags %#x", @@ -1233,15 +1234,18 @@ status_t AudioPolicyManager::setMsdPatch(audio_devices_t outputDevice) { audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector& outputs, audio_output_flags_t flags, - audio_format_t format) + audio_format_t format, + audio_channel_mask_t channelMask, + uint32_t samplingRate) { // select one output among several that provide a path to a particular device or set of // devices (the list was previously build by getOutputsForDevice()). // The priority is as follows: - // 1: the output with the highest number of requested policy flags - // 2: the output with the bit depth the closest to the requested one - // 3: the primary output - // 4: the first output in the list + // 1: the output supporting haptic playback when requesting haptic playback + // 2: the output with the highest number of requested policy flags + // 3: the output with the bit depth the closest to the requested one + // 4: the primary output + // 5: the first output in the list if (outputs.size() == 0) { return AUDIO_IO_HANDLE_NONE; @@ -1251,6 +1255,8 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVectormFlags & AUDIO_OUTPUT_FLAG_DIRECT) { continue; } + // If haptic channel is specified, use the haptic output if present. + // When using haptic output, same audio format and sample rate are required. + if (hapticChannelCount > 0) { + // If haptic channel is specified, use the first output that + // support haptic playback. + if (audio_channel_count_from_out_mask( + outputDesc->mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) >= hapticChannelCount + && format == outputDesc->mFormat + && samplingRate == outputDesc->mSamplingRate) { + return output; + } + } else { + // When haptic channel is not specified, skip haptic output. + if (outputDesc->mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) { + continue; + } + } + // if a valid format is specified, skip output if not compatible if (format != AUDIO_FORMAT_INVALID) { if (!audio_is_linear_pcm(format)) { @@ -3101,9 +3125,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, getOutputsForDevice(sinkDeviceDesc->type(), mOutputs); // if the sink device is reachable via an opened output stream, request to go via // this output stream by adding a second source to the patch description - audio_io_handle_t output = selectOutput(outputs, - AUDIO_OUTPUT_FLAG_NONE, - AUDIO_FORMAT_INVALID); + audio_io_handle_t output = selectOutput(outputs); if (output != AUDIO_IO_HANDLE_NONE) { sp outputDesc = mOutputs.valueFor(output); if (outputDesc->isDuplicated()) { @@ -3447,8 +3469,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp // create Hwoutput and add to mHwOutputs } else { SortedVector outputs = getOutputsForDevice(sinkDevice, mOutputs); - audio_io_handle_t output = - selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); + audio_io_handle_t output = selectOutput(outputs); if (output == AUDIO_IO_HANDLE_NONE) { ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevice); return INVALID_OPERATION; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 0436b1d0f8..46f8b8ce4f 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -470,8 +470,10 @@ protected: uint32_t delayMs); audio_io_handle_t selectOutput(const SortedVector& outputs, - audio_output_flags_t flags, - audio_format_t format); + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + audio_format_t format = AUDIO_FORMAT_INVALID, + audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE, + uint32_t samplingRate = 0); // samplingRate, format, channelMask are in/out and so may be modified sp getInputProfile(audio_devices_t device, const String8& address, -- GitLab From d33180840632318a8dcf1a1f37f89b7554b07a88 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Fri, 7 Sep 2018 15:52:43 -0700 Subject: [PATCH 0591/1530] Use bufferpool@2.0 Use bufferpool2.0 instead of bufferpool 1.0 from CCodec Change-Id: I842b6723e83eb537f2b2eea0f30a1f36f2b17fa9 --- media/bufferpool/2.0/AccessorImpl.cpp | 14 +++++++------- media/bufferpool/2.0/BufferPoolClient.cpp | 2 +- media/codec2/hidl/1.0/utils/Android.bp | 6 +++--- media/codec2/hidl/1.0/utils/Component.cpp | 2 +- media/codec2/hidl/1.0/utils/ComponentStore.cpp | 2 +- .../utils/include/codec2/hidl/1.0/Component.h | 4 ++-- .../include/codec2/hidl/1.0/ComponentStore.h | 4 ++-- .../1.0/utils/include/codec2/hidl/1.0/types.h | 18 +++++++++--------- media/codec2/hidl/1.0/utils/types.cpp | 8 ++++---- media/codec2/hidl/client/Android.bp | 4 ++-- media/codec2/hidl/client/client.cpp | 6 +++--- .../hidl/client/include/codec2/hidl/client.h | 6 +++--- media/codec2/vndk/Android.bp | 6 +++--- media/codec2/vndk/C2Buffer.cpp | 12 ++++++------ media/codec2/vndk/include/C2BufferPriv.h | 4 ++-- 15 files changed, 49 insertions(+), 49 deletions(-) diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp index 01653145f3..2c734ac565 100644 --- a/media/bufferpool/2.0/AccessorImpl.cpp +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -282,7 +282,7 @@ std::atomic Accessor::Impl::BufferPool::Invalidation::sInvSeqId(0 Accessor::Impl::Impl::BufferPool::~BufferPool() { std::lock_guard lock(mMutex); - ALOGD("Destruction - bufferpool %p " + ALOGD("Destruction - bufferpool2 %p " "cached: %zu/%zuM, %zu/%d%% in use; " "allocs: %zu, %d%% recycled; " "transfers: %zu, %d%% unfetced", @@ -353,12 +353,12 @@ void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest( msgId = ++mInvalidationId; } } - ALOGV("bufferpool invalidation requested and queued"); + ALOGV("bufferpool2 invalidation requested and queued"); if (left == 0) { channel.postInvalidation(msgId, from, to); } else { // TODO: sending hint message? - ALOGV("bufferpool invalidation requested and pending"); + ALOGV("bufferpoo2 invalidation requested and pending"); Pending pending(needsAck, from, to, left, impl); mPendings.push_back(pending); } @@ -380,7 +380,7 @@ void Accessor::Impl::BufferPool::Invalidation::onHandleAck() { // lost. it->second = mInvalidationId; } else { - ALOGV("bufferpool observer died %lld", (long long)it->first); + ALOGV("bufferpool2 observer died %lld", (long long)it->first); deads.insert(it->first); } } @@ -682,7 +682,7 @@ void Accessor::Impl::BufferPool::cleanUp(bool clearCache) { mLastCleanUpUs = mTimestampUs; if (mTimestampUs > mLastLogUs + kLogDurationUs) { mLastLogUs = mTimestampUs; - ALOGD("bufferpool %p : %zu(%zu size) total buffers - " + ALOGD("bufferpool2 %p : %zu(%zu size) total buffers - " "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - " "%zu/%zu (fetch/transfer)", this, mStats.mBuffersCached, mStats.mSizeCached, @@ -703,7 +703,7 @@ void Accessor::Impl::BufferPool::cleanUp(bool clearCache) { freeIt = mFreeBuffers.erase(freeIt); } else { ++freeIt; - ALOGW("bufferpool inconsistent!"); + ALOGW("bufferpool2 inconsistent!"); } } } @@ -722,7 +722,7 @@ void Accessor::Impl::BufferPool::invalidate( freeIt = mFreeBuffers.erase(freeIt); continue; } else { - ALOGW("bufferpool inconsistent!"); + ALOGW("bufferpool2 inconsistent!"); } } ++freeIt; diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp index 5564a1354c..f907de5306 100644 --- a/media/bufferpool/2.0/BufferPoolClient.cpp +++ b/media/bufferpool/2.0/BufferPoolClient.cpp @@ -811,7 +811,7 @@ ResultStatus BufferPoolClient::getAccessor(sp *accessor) { } void BufferPoolClient::receiveInvalidation(uint32_t msgId) { - ALOGV("bufferpool client recv inv %u", msgId); + ALOGV("bufferpool2 client recv inv %u", msgId); if (isValid()) { mImpl->receiveInvalidation(msgId); } diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index 455de50e79..c5ad6a01c8 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -24,7 +24,7 @@ cc_library { "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.common@1.0", "android.hardware.media@1.0", - "android.hardware.media.bufferpool@1.0", + "android.hardware.media.bufferpool@2.0", "android.hardware.media.c2@1.0", "android.hardware.media.omx@1.0", "libbase", @@ -35,7 +35,7 @@ cc_library { "libhidltransport", "libhwbinder", "liblog", - "libstagefright_bufferpool@1.0", + "libstagefright_bufferpool@2.0", "libstagefright_bufferqueue_helper", "libui", "libutils", @@ -49,7 +49,7 @@ cc_library { "android.hardware.media.c2@1.0", "libcodec2", "libhidlbase", - "libstagefright_bufferpool@1.0", + "libstagefright_bufferpool@2.0", "libstagefright_bufferqueue_helper", "libui", ], diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp index aa4c6b22de..5ae1972a28 100644 --- a/media/codec2/hidl/1.0/utils/Component.cpp +++ b/media/codec2/hidl/1.0/utils/Component.cpp @@ -410,7 +410,7 @@ Component::Component( const std::shared_ptr& component, const sp& listener, const sp& store, - const sp<::android::hardware::media::bufferpool::V1_0:: + const sp<::android::hardware::media::bufferpool::V2_0:: IClientManager>& clientPoolManager) : Configurable(new CachedConfigurable( std::make_unique(component->intf()))), diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp index 1d1bbe081c..9c05014f00 100644 --- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp @@ -57,7 +57,7 @@ namespace utils { using namespace ::android; using ::android::GraphicBufferSource; -using namespace ::android::hardware::media::bufferpool::V1_0::implementation; +using namespace ::android::hardware::media::bufferpool::V2_0::implementation; namespace /* unnamed */ { diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h index 36dec1c8f2..0908226b87 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include @@ -71,7 +71,7 @@ struct Component : public Configurable { const std::shared_ptr&, const sp& listener, const sp& store, - const sp<::android::hardware::media::bufferpool::V1_0:: + const sp<::android::hardware::media::bufferpool::V2_0:: IClientManager>& clientPoolManager); c2_status_t status() const; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h index 5821e8a239..41e14160f1 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -40,7 +40,7 @@ namespace c2 { namespace V1_0 { namespace utils { -using ::android::hardware::media::bufferpool::V1_0::IClientManager; +using ::android::hardware::media::bufferpool::V2_0::IClientManager; using ::android::hardware::hidl_array; using ::android::hardware::hidl_handle; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index 58f5c96a11..d8a50b609a 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -20,8 +20,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -46,7 +46,7 @@ using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::status_t; using ::android::sp; -using ::android::hardware::media::bufferpool::V1_0::implementation:: +using ::android::hardware::media::bufferpool::V2_0::implementation:: ConnectionId; using ::android::IGraphicBufferProducer; @@ -131,9 +131,9 @@ c2_status_t objcpy( // Abstract class to be used in // objcpy(std::list> -> WorkBundle). struct BufferPoolSender { - typedef ::android::hardware::media::bufferpool::V1_0:: + typedef ::android::hardware::media::bufferpool::V2_0:: ResultStatus ResultStatus; - typedef ::android::hardware::media::bufferpool::V1_0:: + typedef ::android::hardware::media::bufferpool::V2_0:: BufferStatusMessage BufferStatusMessage; typedef ::android::hardware::media::bufferpool:: BufferPoolData BufferPoolData; @@ -151,7 +151,7 @@ struct BufferPoolSender { * other means so it can call receive() properly. * \return ResultStatus value that determines the success of the operation. * (See the possible values of ResultStatus in - * hardware/interfaces/media/bufferpool/1.0/types.hal.) + * hardware/interfaces/media/bufferpool/2.0/types.hal.) */ virtual ResultStatus send( const std::shared_ptr& bpData, @@ -168,9 +168,9 @@ struct BufferPoolSender { // IClientManager::registerSender() to establish the bufferpool connection when // send() is called. struct DefaultBufferPoolSender : BufferPoolSender { - typedef ::android::hardware::media::bufferpool::V1_0::implementation:: + typedef ::android::hardware::media::bufferpool::V2_0::implementation:: ClientManager ClientManager; - typedef ::android::hardware::media::bufferpool::V1_0:: + typedef ::android::hardware::media::bufferpool::V2_0:: IClientManager IClientManager; // Set the IClientManager instance of the receiving process and the refresh @@ -278,7 +278,7 @@ c2_status_t updateParamsFromBlob( * \param BufferPool status * \return Corresponding c2_status_t */ -c2_status_t toC2Status(::android::hardware::media::bufferpool::V1_0:: +c2_status_t toC2Status(::android::hardware::media::bufferpool::V2_0:: ResultStatus rs); // BufferQueue-Based Block Operations diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 5827504155..c053bc1257 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -49,11 +49,11 @@ namespace utils { using namespace ::android; using ::android::hardware::Return; using ::android::hardware::media::bufferpool::BufferPoolData; -using ::android::hardware::media::bufferpool::V1_0::BufferStatusMessage; -using ::android::hardware::media::bufferpool::V1_0::ResultStatus; -using ::android::hardware::media::bufferpool::V1_0::implementation:: +using ::android::hardware::media::bufferpool::V2_0::BufferStatusMessage; +using ::android::hardware::media::bufferpool::V2_0::ResultStatus; +using ::android::hardware::media::bufferpool::V2_0::implementation:: ClientManager; -using ::android::hardware::media::bufferpool::V1_0::implementation:: +using ::android::hardware::media::bufferpool::V2_0::implementation:: TransactionId; using ::android::TWGraphicBufferProducer; diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index bdc02d21ed..a2a498d2b7 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -7,7 +7,7 @@ cc_library { shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", - "android.hardware.media.bufferpool@1.0", + "android.hardware.media.bufferpool@2.0", "android.hardware.media.c2@1.0", "libbase", "libbinder", @@ -19,7 +19,7 @@ cc_library { "libhidlbase", "libhidltransport", "liblog", - "libstagefright_bufferpool@1.0", + "libstagefright_bufferpool@2.0", "libstagefright_bufferqueue_helper", "libui", "libutils", diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 26bd96c23f..ff3e5340f9 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -34,7 +34,7 @@ #include #undef LOG -#include +#include #include #include #include @@ -55,8 +55,8 @@ using ::android::TWGraphicBufferProducer; using namespace ::android::hardware::media::c2::V1_0; using namespace ::android::hardware::media::c2::V1_0::utils; -using namespace ::android::hardware::media::bufferpool::V1_0; -using namespace ::android::hardware::media::bufferpool::V1_0::implementation; +using namespace ::android::hardware::media::bufferpool::V2_0; +using namespace ::android::hardware::media::bufferpool::V2_0::implementation; namespace /* unnamed */ { diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 3ab3967445..c48bf0cd51 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -85,9 +85,9 @@ namespace android { namespace hardware { namespace media { namespace bufferpool { -namespace V1_0 { +namespace V2_0 { struct IClientManager; -} // namespace V1_0 +} // namespace V2_0 } // namespace bufferpool } // namespace media } // namespace hardware @@ -237,7 +237,7 @@ protected: mutable std::vector>> mAliasesBuffer; - sp<::android::hardware::media::bufferpool::V1_0::IClientManager> + sp<::android::hardware::media::bufferpool::V2_0::IClientManager> mHostPoolManager; }; diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index 0eb90be638..e0b1355da4 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -35,7 +35,7 @@ cc_library_shared { export_shared_lib_headers: [ "libbase", - "android.hardware.media.bufferpool@1.0", + "android.hardware.media.bufferpool@2.0", ], local_include_dirs: [ @@ -51,7 +51,7 @@ cc_library_shared { "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.mapper@2.0", - "android.hardware.media.bufferpool@1.0", + "android.hardware.media.bufferpool@2.0", "libbase", "libbinder", "libcutils", @@ -63,7 +63,7 @@ cc_library_shared { "liblog", "libstagefright_bufferqueue_helper", "libstagefright_foundation", - "libstagefright_bufferpool@1.0", + "libstagefright_bufferpool@2.0", "libui", "libutils", ], diff --git a/media/codec2/vndk/C2Buffer.cpp b/media/codec2/vndk/C2Buffer.cpp index 47366ca52a..710b536ef7 100644 --- a/media/codec2/vndk/C2Buffer.cpp +++ b/media/codec2/vndk/C2Buffer.cpp @@ -33,12 +33,12 @@ namespace { using android::C2AllocatorGralloc; using android::C2AllocatorIon; using android::hardware::media::bufferpool::BufferPoolData; -using android::hardware::media::bufferpool::V1_0::ResultStatus; -using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocation; -using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocator; -using android::hardware::media::bufferpool::V1_0::implementation::ClientManager; -using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId; -using android::hardware::media::bufferpool::V1_0::implementation::INVALID_CONNECTIONID; +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation::BufferPoolAllocation; +using android::hardware::media::bufferpool::V2_0::implementation::BufferPoolAllocator; +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::INVALID_CONNECTIONID; // This anonymous namespace contains the helper classes that allow our implementation to create // block/buffer objects. diff --git a/media/codec2/vndk/include/C2BufferPriv.h b/media/codec2/vndk/include/C2BufferPriv.h index d0b9152c1b..be5f69cdb8 100644 --- a/media/codec2/vndk/include/C2BufferPriv.h +++ b/media/codec2/vndk/include/C2BufferPriv.h @@ -20,7 +20,7 @@ #include #include -#include +#include class C2BasicLinearBlockPool : public C2BlockPool { public: @@ -112,7 +112,7 @@ public: * \return true IAcessor is writen successfully. * \return false IAccessor is not written. */ - bool getAccessor(android::sp *accessor); + bool getAccessor(android::sp *accessor); private: const std::shared_ptr mAllocator; -- GitLab From 5e65a6ad370c34b42e85007af0934afbd00bf57f Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 2 Nov 2018 14:25:45 -0700 Subject: [PATCH 0592/1530] Add haptic metadata for Ogg file. Add a key "haptic" in Ogg file metadata, which indicating the haptic channel count of the Ogg file. When it is present as a valid number, OggExtractor will set haptic channel mask accordingly to support haptic playback via audio. This is part of haptic playback support. Bug: 111454766 Test: Manually test Change-Id: I9f9ba22358b7c538382f23f24143057970f6d7ad --- media/extractors/ogg/Android.bp | 4 ++++ media/extractors/ogg/OggExtractor.cpp | 33 +++++++++++++++++++++++++- media/libstagefright/MetaDataUtils.cpp | 12 ++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp index c6deb18129..2600947169 100644 --- a/media/extractors/ogg/Android.bp +++ b/media/extractors/ogg/Android.bp @@ -7,6 +7,10 @@ cc_library_shared { "external/tremolo", ], + header_libs: [ + "libaudio_system_headers", + ], + shared_libs: [ "liblog", "libmediaextractor", diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index a52ccb15d1..cc2c792191 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include extern "C" { @@ -133,6 +134,8 @@ protected: Vector mTableOfContents; + int32_t mHapticChannelCount; + ssize_t readPage(off64_t offset, Page *page); status_t findNextPage(off64_t startOffset, off64_t *pageOffset); @@ -163,6 +166,8 @@ protected: void buildTableOfContents(); + void setChannelMask(int channelCount); + MyOggExtractor(const MyOggExtractor &); MyOggExtractor &operator=(const MyOggExtractor &); }; @@ -310,7 +315,8 @@ MyOggExtractor::MyOggExtractor( mMimeType(mimeType), mNumHeaders(numHeaders), mSeekPreRollUs(seekPreRollUs), - mFirstDataOffset(-1) { + mFirstDataOffset(-1), + mHapticChannelCount(0) { mCurrentPage.mNumSegments = 0; vorbis_info_init(&mVi); @@ -1083,6 +1089,7 @@ media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) { } parseFileMetaData(); + setChannelMask(mChannelCount); return AMEDIA_OK; } @@ -1157,6 +1164,7 @@ media_status_t MyVorbisExtractor::verifyHeader( } parseFileMetaData(); + setChannelMask(mVi.channels); break; } @@ -1192,6 +1200,29 @@ void MyOggExtractor::parseFileMetaData() { parseVorbisComment(mFileMeta, comment, commentLength); //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); } + + AMediaFormat_getInt32(mFileMeta, "haptic", &mHapticChannelCount); +} + +void MyOggExtractor::setChannelMask(int channelCount) { + // Set channel mask according to channel count. When haptic channel count is found in + // file meta, set haptic channel mask to try haptic playback. + if (mHapticChannelCount > 0) { + const audio_channel_mask_t hapticChannelMask = + haptic_channel_mask_from_count(mHapticChannelCount); + const int32_t audioChannelCount = channelCount - mHapticChannelCount; + if (hapticChannelMask == AUDIO_CHANNEL_INVALID + || audioChannelCount <= 0 || audioChannelCount > FCC_8) { + ALOGE("Invalid haptic channel count found in metadata: %d", mHapticChannelCount); + } else { + const audio_channel_mask_t channelMask = audio_channel_out_mask_from_count( + audioChannelCount) | hapticChannelMask; + AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, channelMask); + } + } else { + AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, + audio_channel_out_mask_from_count(channelCount)); + } } diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp index a3259fd4e7..dbc287eb16 100644 --- a/media/libstagefright/MetaDataUtils.cpp +++ b/media/libstagefright/MetaDataUtils.cpp @@ -308,6 +308,8 @@ static void extractAlbumArt( void parseVorbisComment( AMediaFormat *fileMeta, const char *comment, size_t commentLength) { + // Haptic tag is only kept here as it will only be used in extractor to generate channel mask. + const char* const haptic = "haptic"; struct { const char *const mTag; const char *mKey; @@ -328,6 +330,7 @@ void parseVorbisComment( { "LYRICIST", AMEDIAFORMAT_KEY_LYRICIST }, { "METADATA_BLOCK_PICTURE", AMEDIAFORMAT_KEY_ALBUMART }, { "ANDROID_LOOP", AMEDIAFORMAT_KEY_LOOP }, + { "ANDROID_HAPTIC", haptic }, }; for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { @@ -343,6 +346,15 @@ void parseVorbisComment( if (!strcasecmp(&comment[tagLen + 1], "true")) { AMediaFormat_setInt32(fileMeta, AMEDIAFORMAT_KEY_LOOP, 1); } + } else if (kMap[j].mKey == haptic) { + char *end; + errno = 0; + const int hapticChannelCount = strtol(&comment[tagLen + 1], &end, 10); + if (errno == 0) { + AMediaFormat_setInt32(fileMeta, haptic, hapticChannelCount); + } else { + ALOGE("Error(%d) when parsing haptic channel count", errno); + } } else { AMediaFormat_setString(fileMeta, kMap[j].mKey, &comment[tagLen + 1]); } -- GitLab From b99e1bcec4b2cf23a6d8dfe847fa57dedcecc172 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Wed, 21 Feb 2018 17:10:34 +0530 Subject: [PATCH 0593/1530] libeffects: Add multichannel processing support Multichannel processing support added for Bundled effects including Bass Enhancement (DBE) N Band Equalizer (EQNB) Concert Surround / Stereo Widening (CS) Parametric Spectrum Analysis (PSA) DC removal Treble Boost Test: Solo Tester, CTS Effects Test, Local Native Test Bug: 72223862 Change-Id: Ide86b529a7574c26306098fedd7b276b3688998f --- media/libeffects/lvm/lib/Android.bp | 5 +- media/libeffects/lvm/lib/Bass/lib/LVDBE.h | 3 + .../lvm/lib/Bass/src/LVDBE_Control.c | 2 +- .../libeffects/lvm/lib/Bass/src/LVDBE_Init.c | 2 +- .../lvm/lib/Bass/src/LVDBE_Private.h | 5 + .../lvm/lib/Bass/src/LVDBE_Process.c | 139 +++- media/libeffects/lvm/lib/Bundle/lib/LVM.h | 3 + .../lvm/lib/Bundle/src/LVM_Buffers.c | 93 ++- .../lvm/lib/Bundle/src/LVM_Control.c | 27 +- .../libeffects/lvm/lib/Bundle/src/LVM_Init.c | 9 +- .../lvm/lib/Bundle/src/LVM_Private.h | 13 +- .../lvm/lib/Bundle/src/LVM_Process.c | 86 ++- media/libeffects/lvm/lib/Common/lib/AGC.h | 9 + media/libeffects/lvm/lib/Common/lib/BIQUAD.h | 58 +- .../libeffects/lvm/lib/Common/lib/LVM_Timer.h | 10 +- .../libeffects/lvm/lib/Common/lib/LVM_Types.h | 9 + .../lvm/lib/Common/lib/VectorArithmetic.h | 16 + .../Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c | 146 ++++ .../Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c | 81 +++ media/libeffects/lvm/lib/Common/src/Copy_16.c | 60 ++ .../lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c | 54 +- .../Common/src/DC_2I_D16_TRC_WRA_01_Init.c | 11 + .../Common/src/DC_2I_D16_TRC_WRA_01_Private.h | 7 + .../src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c | 87 +++ .../lvm/lib/Common/src/From2iToMono_32.c | 42 ++ .../src/LVC_Core_MixInSoft_D16C31_SAT.c | 141 +++- .../src/LVC_Core_MixSoft_1St_D16C31_WRA.c | 121 +++- .../lib/Common/src/LVC_MixInSoft_D16C31_SAT.c | 112 ++- .../Common/src/LVC_MixSoft_1St_D16C31_SAT.c | 95 +++ .../Common/src/LVC_MixSoft_2St_D16C31_SAT.c | 74 +- .../libeffects/lvm/lib/Common/src/LVC_Mixer.h | 58 +- .../lvm/lib/Common/src/LVC_Mixer_GetTarget.c | 2 +- .../lvm/lib/Common/src/LVC_Mixer_Private.h | 14 + .../src/PK_2I_D32F32C14G11_TRC_WRA_01.c | 74 ++ media/libeffects/lvm/lib/Eq/lib/LVEQNB.h | 4 +- .../lvm/lib/Eq/src/LVEQNB_Private.h | 5 + .../lvm/lib/Eq/src/LVEQNB_Process.c | 85 ++- .../lvm/lib/Reverb/src/LVREV_Process.c | 2 +- .../Reverb/src/LVREV_SetControlParameters.c | 11 +- .../lvm/lib/StereoWidening/lib/LVCS.h | 3 + .../lvm/lib/StereoWidening/src/LVCS_Private.h | 9 + .../lvm/lib/StereoWidening/src/LVCS_Process.c | 75 +- .../lvm/lib/StereoWidening/src/LVCS_Tables.c | 40 +- media/libeffects/lvm/tests/Android.bp | 46 ++ .../lvm/tests/build_and_run_all_unit_tests.sh | 49 ++ media/libeffects/lvm/tests/lvmtest.cpp | 682 ++++++++++++++++++ media/libeffects/lvm/wrapper/Android.bp | 1 + .../lvm/wrapper/Bundle/EffectBundle.cpp | 65 +- 48 files changed, 2542 insertions(+), 203 deletions(-) create mode 100644 media/libeffects/lvm/tests/Android.bp create mode 100755 media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh create mode 100644 media/libeffects/lvm/tests/lvmtest.cpp diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp index 5c57c43768..7a32d3f9a8 100644 --- a/media/libeffects/lvm/lib/Android.bp +++ b/media/libeffects/lvm/lib/Android.bp @@ -129,11 +129,14 @@ cc_library_static { "Common/lib", "Bundle/lib", ], - + shared_libs: [ + "liblog", + ], cflags: [ "-fvisibility=hidden", "-DBUILD_FLOAT", "-DHIGHER_FS", + "-DSUPPORT_MC", "-Wall", "-Werror", diff --git a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h index 4c2b954a7f..a1fa79ae7c 100644 --- a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h +++ b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h @@ -256,6 +256,9 @@ typedef struct LVDBE_Volume_en VolumeControl; LVM_INT16 VolumedB; LVM_INT16 HeadroomdB; +#ifdef SUPPORT_MC + LVM_INT16 NrChannels; +#endif } LVDBE_Params_t; diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c index fd4016bc6e..0ba2c8604e 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Control.c @@ -118,7 +118,7 @@ void LVDBE_SetFilters(LVDBE_Instance_t *pInstance, * Calculate the table offsets */ LVM_UINT16 Offset = (LVM_UINT16)((LVM_UINT16)pParams->SampleRate + \ - (LVM_UINT16)(pParams->CentreFrequency * (1+LVDBE_FS_48000))); + (LVM_UINT16)(pParams->CentreFrequency * (1+LVDBE_FS_48000))); #endif /* diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c index 3fff2a2718..2946734d7e 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.c @@ -95,7 +95,7 @@ LVDBE_ReturnStatus_en LVDBE_Memory(LVDBE_Handle_t hInstance, #ifdef BUILD_FLOAT pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].Size = sizeof(LVDBE_Coef_FLOAT_t); #else - pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].Size = sizeof(LVDBE_Coef_t); + pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].Size = sizeof(LVDBE_Coef_t); #endif pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].Alignment = LVDBE_PERSISTENT_COEF_ALIGN; pMemoryTable->Region[LVDBE_MEMREGION_PERSISTENT_COEF].Type = LVDBE_PERSISTENT_COEF; diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h index 4e5207f50e..4225a30a4e 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Private.h @@ -64,7 +64,12 @@ extern "C" { #define LVDBE_PERSISTENT_COEF_ALIGN 4 /* 32-bit alignment for coef */ #define LVDBE_SCRATCH_ALIGN 4 /* 32-bit alignment for long data */ +#ifdef SUPPORT_MC +/* Number of buffers required for inplace processing */ +#define LVDBE_SCRATCHBUFFERS_INPLACE (LVM_MAX_CHANNELS * 3) +#else #define LVDBE_SCRATCHBUFFERS_INPLACE 6 /* Number of buffers required for inplace processing */ +#endif #define LVDBE_MIXER_TC 5 /* Mixer time */ #define LVDBE_BYPASS_MIXER_TC 100 /* Bypass mixer time */ diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c index 10ea70001e..c4d34031a0 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Process.c @@ -21,11 +21,13 @@ /* */ /****************************************************************************************/ +#include // memset #include "LVDBE.h" #include "LVDBE_Private.h" #include "VectorArithmetic.h" #include "AGC.h" #include "LVDBE_Coeffs.h" /* Filter coefficients */ +#include /********************************************************************************************/ /* */ @@ -187,41 +189,48 @@ LVDBE_ReturnStatus_en LVDBE_Process(LVDBE_Handle_t hInstance, LVDBE_ReturnStatus_en LVDBE_Process(LVDBE_Handle_t hInstance, const LVM_FLOAT *pInData, LVM_FLOAT *pOutData, - LVM_UINT16 NumSamples) + const LVM_UINT16 NrFrames) // updated to use samples = frames * channels. { - LVDBE_Instance_t *pInstance =(LVDBE_Instance_t *)hInstance; - LVM_FLOAT *pScratch_in = (LVM_FLOAT *)pInstance->MemoryTable.Region - [LVDBE_MEMREGION_SCRATCH].pBaseAddress; - LVM_FLOAT *pScratch = pScratch_in + 2 * NumSamples; - LVM_FLOAT *pMono; - LVM_INT32 ii = 0; - /* Scratch for Volume Control starts at offset of 4*NumSamples float values from pScratch */ - LVM_FLOAT *pScratchVol = (LVM_FLOAT *)(&pScratch_in[4 * NumSamples]); -// LVM_INT16 *pScratchVol_int = (LVM_INT16 *)(pScratchVol); + /*Extract number of Channels info*/ +#ifdef SUPPORT_MC + // Mono passed in as stereo + const LVM_INT32 NrChannels = pInstance->Params.NrChannels == 1 + ? 2 : pInstance->Params.NrChannels; +#else + const LVM_INT32 NrChannels = 2; // FCC_2 +#endif + const LVM_INT32 NrSamples = NrChannels * NrFrames; - /* Scratch for Mono path starts at offset of 6*NumSamples 32-bit values from pScratch */ - pMono = &pScratch_in[4 * NumSamples]; + /* Space to store DBE path computation */ + LVM_FLOAT * const pScratch = + (LVM_FLOAT *)pInstance->MemoryTable.Region[LVDBE_MEMREGION_SCRATCH].pBaseAddress; /* - * Check the number of samples is not too large + * Scratch for Mono path starts at offset of + * NrSamples float values from pScratch. */ - if (NumSamples > pInstance->Capabilities.MaxBlockSize) - { - return(LVDBE_TOOMANYSAMPLES); - } + LVM_FLOAT * const pMono = pScratch + NrSamples; /* - * Convert 16-bit samples to Float + * TRICKY: pMono is used and discarded by the DBE path. + * so it is available for use for the pScratchVol + * path which is computed afterwards. + * + * Space to store Volume Control path computation. + * This is identical to pMono (see TRICKY comment). */ - Copy_Float(pInData, /* Source 16-bit data */ - pScratch_in, /* Dest. 32-bit data */ - (LVM_INT16)(2 * NumSamples)); /* Left and right */ + LVM_FLOAT * const pScratchVol = pMono; - for (ii = 0; ii < 2 * NumSamples; ii++) { - pScratch[ii] = pScratch_in[ii]; + /* + * Check the number of frames is not too large + */ + if (NrFrames > pInstance->Capabilities.MaxBlockSize) + { + return LVDBE_TOOMANYSAMPLES; } + /* * Check if the algorithm is enabled */ @@ -230,50 +239,81 @@ LVDBE_ReturnStatus_en LVDBE_Process(LVDBE_Handle_t hInstance, (LVC_Mixer_GetCurrent(&pInstance->pData->BypassMixer.MixerStream[0]) !=LVC_Mixer_GetTarget(&pInstance->pData->BypassMixer.MixerStream[0]))) { + // make copy of input data + Copy_Float(pInData, + pScratch, + (LVM_INT16)NrSamples); /* * Apply the high pass filter if selected */ if (pInstance->Params.HPFSelect == LVDBE_HPF_ON) { +#ifdef SUPPORT_MC + BQ_MC_D32F32C30_TRC_WRA_01(&pInstance->pCoef->HPFInstance, /* Filter instance */ + pScratch, /* Source */ + pScratch, /* Destination */ + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else BQ_2I_D32F32C30_TRC_WRA_01(&pInstance->pCoef->HPFInstance,/* Filter instance */ - (LVM_FLOAT *)pScratch, /* Source */ - (LVM_FLOAT *)pScratch, /* Destination */ - (LVM_INT16)NumSamples); /* Number of samples */ + pScratch, /* Source */ + pScratch, /* Destination */ + (LVM_INT16)NrFrames); +#endif } /* * Create the mono stream */ - From2iToMono_Float((LVM_FLOAT *)pScratch, /* Stereo source */ +#ifdef SUPPORT_MC + FromMcToMono_Float(pScratch, /* Source */ + pMono, /* Mono destination */ + (LVM_INT16)NrFrames, /* Number of frames */ + (LVM_INT16)NrChannels); +#else + From2iToMono_Float(pScratch, /* Stereo source */ pMono, /* Mono destination */ - (LVM_INT16)NumSamples); /* Number of samples */ + (LVM_INT16)NrFrames); +#endif /* * Apply the band pass filter */ BP_1I_D32F32C30_TRC_WRA_02(&pInstance->pCoef->BPFInstance, /* Filter instance */ - (LVM_FLOAT *)pMono, /* Source */ - (LVM_FLOAT *)pMono, /* Destination */ - (LVM_INT16)NumSamples); /* Number of samples */ + pMono, /* Source */ + pMono, /* Destination */ + (LVM_INT16)NrFrames); /* * Apply the AGC and mix */ +#ifdef SUPPORT_MC + AGC_MIX_VOL_Mc1Mon_D32_WRA(&pInstance->pData->AGCInstance, /* Instance pointer */ + pScratch, /* Source */ + pMono, /* Mono band pass source */ + pScratch, /* Destination */ + NrFrames, /* Number of frames */ + NrChannels); /* Number of channels */ +#else AGC_MIX_VOL_2St1Mon_D32_WRA(&pInstance->pData->AGCInstance, /* Instance pointer */ pScratch, /* Stereo source */ pMono, /* Mono band pass source */ pScratch, /* Stereo destination */ - NumSamples); /* Number of samples */ + NrFrames); +#endif - for (ii = 0; ii < 2 * NumSamples; ii++) { + for (LVM_INT32 ii = 0; ii < NrSamples; ++ii) { //TODO: replace with existing clamping function - if(pScratch[ii] < -1.0) { + if (pScratch[ii] < -1.0) { pScratch[ii] = -1.0; - } else if(pScratch[ii] > 1.0) { + } else if (pScratch[ii] > 1.0) { pScratch[ii] = 1.0; } } + } else { + // clear DBE processed path + memset(pScratch, 0, sizeof(*pScratch) * NrSamples); } /* Bypass Volume path is processed when DBE is OFF or during On/Off transitions */ @@ -286,21 +326,40 @@ LVDBE_ReturnStatus_en LVDBE_Process(LVDBE_Handle_t hInstance, * The algorithm is disabled but volume management is required to compensate for * headroom and volume (if enabled) */ +#ifdef SUPPORT_MC + LVC_MixSoft_Mc_D16C31_SAT(&pInstance->pData->BypassVolume, + pInData, + pScratchVol, + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else LVC_MixSoft_1St_D16C31_SAT(&pInstance->pData->BypassVolume, - pScratch_in, + pInData, pScratchVol, - (LVM_INT16)(2 * NumSamples)); /* Left and right */ + (LVM_INT16)NrSamples); /* Left and right, really # samples */ +#endif + } else { + // clear bypass volume path + memset(pScratchVol, 0, sizeof(*pScratchVol) * NrSamples); } /* * Mix DBE processed path and bypass volume path */ +#ifdef SUPPORT_MC + LVC_MixSoft_2Mc_D16C31_SAT(&pInstance->pData->BypassMixer, + pScratch, + pScratchVol, + pOutData, + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else LVC_MixSoft_2St_D16C31_SAT(&pInstance->pData->BypassMixer, pScratch, pScratchVol, pOutData, - (LVM_INT16)(2 * NumSamples)); - - return(LVDBE_SUCCESS); + (LVM_INT16)NrSamples); +#endif + return LVDBE_SUCCESS; } #endif diff --git a/media/libeffects/lvm/lib/Bundle/lib/LVM.h b/media/libeffects/lvm/lib/Bundle/lib/LVM.h index 9b6da31ca8..83ecae1477 100644 --- a/media/libeffects/lvm/lib/Bundle/lib/LVM.h +++ b/media/libeffects/lvm/lib/Bundle/lib/LVM.h @@ -296,6 +296,9 @@ typedef struct /* Spectrum Analyzer parameters Control */ LVM_PSA_Mode_en PSA_Enable; LVM_PSA_DecaySpeed_en PSA_PeakDecayRate; /* Peak value decay rate*/ +#ifdef SUPPORT_MC + LVM_INT32 NrChannels; +#endif } LVM_ControlParams_t; diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c index 0a3c30e424..37272e311e 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c @@ -25,6 +25,8 @@ #include "LVM_Private.h" #include "VectorArithmetic.h" +#include + /****************************************************************************************/ /* */ /* FUNCTION: LVM_BufferManagedIn */ @@ -62,8 +64,11 @@ void LVM_BufferManagedIn(LVM_Handle_t hInstance, LVM_Instance_t *pInstance = (LVM_Instance_t *)hInstance; LVM_Buffer_t *pBuffer; LVM_FLOAT *pDest; +#ifdef SUPPORT_MC + LVM_INT16 NumChannels = pInstance->NrChannels; +#else LVM_INT16 NumChannels = 2; - +#endif /* * Set the processing address pointers @@ -207,8 +212,7 @@ void LVM_BufferManagedIn(LVM_Handle_t hInstance, LVM_Instance_t *pInstance = (LVM_Instance_t *)hInstance; LVM_Buffer_t *pBuffer; LVM_INT16 *pDest; - LVM_INT16 NumChannels =2; - + LVM_INT16 NumChannels = 2; /* * Set the processing address pointers @@ -499,7 +503,7 @@ void LVM_BufferOptimisedIn(LVM_Handle_t hInstance, if (pBuffer->OutDelaySamples != 0) { Copy_16(&pBuffer->OutDelayBuffer[0], /* Source */ - pDest, /* Detsination */ + pDest, /* Destination */ (LVM_INT16)(2*pBuffer->OutDelaySamples)); /* Number of delay samples */ pDest += 2 * pBuffer->OutDelaySamples; /* Update the output pointer */ pBuffer->SamplesToOutput = (LVM_INT16)(pBuffer->SamplesToOutput - pBuffer->OutDelaySamples); /* Update the numbr of samples to output */ @@ -750,7 +754,11 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, LVM_INT16 NumSamples; LVM_FLOAT *pStart; LVM_FLOAT *pDest; - +#ifdef SUPPORT_MC + LVM_INT32 NrChannels = pInstance->NrChannels; +#define NrFrames NumSamples // alias for clarity +#define FrameCount SampleCount +#endif /* * Set the pointers @@ -758,7 +766,6 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, NumSamples = pBuffer->SamplesToOutput; pStart = pBuffer->pScratch; - /* * check if it is the first call of a block */ @@ -786,14 +793,25 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, /* * Copy all output delay samples to the output */ +#ifdef SUPPORT_MC Copy_Float(&pBuffer->OutDelayBuffer[0], /* Source */ - pDest, /* Detsination */ + pDest, /* Destination */ + /* Number of delay samples */ + (LVM_INT16)(NrChannels * pBuffer->OutDelaySamples)); +#else + Copy_Float(&pBuffer->OutDelayBuffer[0], /* Source */ + pDest, /* Destination */ (LVM_INT16)(2 * pBuffer->OutDelaySamples)); /* Number of delay samples */ +#endif /* * Update the pointer and sample counts */ +#ifdef SUPPORT_MC + pDest += NrChannels * pBuffer->OutDelaySamples; /* Output sample pointer */ +#else pDest += 2 * pBuffer->OutDelaySamples; /* Output sample pointer */ +#endif NumSamples = (LVM_INT16)(NumSamples - pBuffer->OutDelaySamples); /* Samples left \ to send */ pBuffer->OutDelaySamples = 0; /* No samples left in the buffer */ @@ -803,23 +821,40 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, /* * Copy only some of the ouput delay samples to the output */ +#ifdef SUPPORT_MC Copy_Float(&pBuffer->OutDelayBuffer[0], /* Source */ - pDest, /* Detsination */ + pDest, /* Destination */ + (LVM_INT16)(NrChannels * NrFrames)); /* Number of delay samples */ +#else + Copy_Float(&pBuffer->OutDelayBuffer[0], /* Source */ + pDest, /* Destination */ (LVM_INT16)(2 * NumSamples)); /* Number of delay samples */ +#endif /* * Update the pointer and sample counts */ +#ifdef SUPPORT_MC + pDest += NrChannels * NrFrames; /* Output sample pointer */ +#else pDest += 2 * NumSamples; /* Output sample pointer */ +#endif /* No samples left in the buffer */ pBuffer->OutDelaySamples = (LVM_INT16)(pBuffer->OutDelaySamples - NumSamples); /* * Realign the delay buffer data to avoid using circular buffer management */ +#ifdef SUPPORT_MC + Copy_Float(&pBuffer->OutDelayBuffer[NrChannels * NrFrames], /* Source */ + &pBuffer->OutDelayBuffer[0], /* Destination */ + /* Number of samples to move */ + (LVM_INT16)(NrChannels * pBuffer->OutDelaySamples)); +#else Copy_Float(&pBuffer->OutDelayBuffer[2 * NumSamples], /* Source */ &pBuffer->OutDelayBuffer[0], /* Destination */ (LVM_INT16)(2 * pBuffer->OutDelaySamples)); /* Number of samples to move */ +#endif NumSamples = 0; /* Samples left to send */ } } @@ -836,13 +871,23 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, /* * Copy all processed samples to the output */ +#ifdef SUPPORT_MC + Copy_Float(pStart, /* Source */ + pDest, /* Destination */ + (LVM_INT16)(NrChannels * FrameCount)); /* Number of processed samples */ +#else Copy_Float(pStart, /* Source */ - pDest, /* Detsination */ + pDest, /* Destination */ (LVM_INT16)(2 * SampleCount)); /* Number of processed samples */ +#endif /* * Update the pointer and sample counts */ +#ifdef SUPPORT_MC + pDest += NrChannels * FrameCount; /* Output sample pointer */ +#else pDest += 2 * SampleCount; /* Output sample pointer */ +#endif NumSamples = (LVM_INT16)(NumSamples - SampleCount); /* Samples left to send */ SampleCount = 0; /* No samples left in the buffer */ } @@ -851,14 +896,25 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, /* * Copy only some processed samples to the output */ +#ifdef SUPPORT_MC + Copy_Float(pStart, /* Source */ + pDest, /* Destination */ + (LVM_INT16)(NrChannels * NrFrames)); /* Number of processed samples */ +#else Copy_Float(pStart, /* Source */ pDest, /* Destination */ (LVM_INT16)(2 * NumSamples)); /* Number of processed samples */ +#endif /* * Update the pointers and sample counts */ +#ifdef SUPPORT_MC + pStart += NrChannels * NrFrames; /* Processed sample pointer */ + pDest += NrChannels * NrFrames; /* Output sample pointer */ +#else pStart += 2 * NumSamples; /* Processed sample pointer */ pDest += 2 * NumSamples; /* Output sample pointer */ +#endif SampleCount = (LVM_INT16)(SampleCount - NumSamples); /* Processed samples left */ NumSamples = 0; /* Clear the sample count */ } @@ -870,9 +926,16 @@ void LVM_BufferManagedOut(LVM_Handle_t hInstance, */ if (SampleCount != 0) { +#ifdef SUPPORT_MC + Copy_Float(pStart, /* Source */ + /* Destination */ + &pBuffer->OutDelayBuffer[NrChannels * pBuffer->OutDelaySamples], + (LVM_INT16)(NrChannels * FrameCount)); /* Number of processed samples */ +#else Copy_Float(pStart, /* Source */ &pBuffer->OutDelayBuffer[2 * pBuffer->OutDelaySamples], /* Destination */ (LVM_INT16)(2 * SampleCount)); /* Number of processed samples */ +#endif /* Update the buffer count */ pBuffer->OutDelaySamples = (LVM_INT16)(pBuffer->OutDelaySamples + SampleCount); } @@ -1063,14 +1126,24 @@ void LVM_BufferUnmanagedOut(LVM_Handle_t hInstance, { LVM_Instance_t *pInstance = (LVM_Instance_t *)hInstance; - LVM_INT16 NumChannels =2; +#ifdef SUPPORT_MC + LVM_INT16 NumChannels = pInstance->NrChannels; +#undef NrFrames +#define NrFrames (*pNumSamples) // alias for clarity +#else + LVM_INT16 NumChannels = 2; +#endif /* * Update sample counts */ pInstance->pInputSamples += (LVM_INT16)(*pNumSamples * NumChannels); /* Update the I/O pointers */ +#ifdef SUPPORT_MC + pInstance->pOutputSamples += (LVM_INT16)(NrFrames * NumChannels); +#else pInstance->pOutputSamples += (LVM_INT16)(*pNumSamples * 2); +#endif pInstance->SamplesToProcess = (LVM_INT16)(pInstance->SamplesToProcess - *pNumSamples); /* Update the sample count */ /* diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c index cfe53b81c2..7b85f230f2 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c @@ -28,6 +28,8 @@ #include "LVM_Tables.h" #include "LVM_Private.h" +#include + /****************************************************************************************/ /* */ /* FUNCTION: LVM_SetControlParameters */ @@ -75,12 +77,22 @@ LVM_ReturnStatus_en LVM_SetControlParameters(LVM_Handle_t hInstance, (pParams->SampleRate != LVM_FS_16000) && (pParams->SampleRate != LVM_FS_22050) && (pParams->SampleRate != LVM_FS_24000) && (pParams->SampleRate != LVM_FS_32000) && (pParams->SampleRate != LVM_FS_44100) && (pParams->SampleRate != LVM_FS_48000)) || #endif +#ifdef SUPPORT_MC + ((pParams->SourceFormat != LVM_STEREO) && + (pParams->SourceFormat != LVM_MONOINSTEREO) && + (pParams->SourceFormat != LVM_MONO) && + (pParams->SourceFormat != LVM_MULTICHANNEL)) || +#else ((pParams->SourceFormat != LVM_STEREO) && (pParams->SourceFormat != LVM_MONOINSTEREO) && (pParams->SourceFormat != LVM_MONO)) || +#endif (pParams->SpeakerType > LVM_EX_HEADPHONES)) { return (LVM_OUTOFRANGE); } +#ifdef SUPPORT_MC + pInstance->Params.NrChannels = pParams->NrChannels; +#endif /* * Cinema Sound parameters */ @@ -569,6 +581,10 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) } while ((pInstance->ControlPending != LVM_FALSE) && (Count > 0)); +#ifdef SUPPORT_MC + pInstance->NrChannels = LocalParams.NrChannels; +#endif + /* Clear all internal data if format change*/ if(LocalParams.SourceFormat != pInstance->Params.SourceFormat) { @@ -719,6 +735,9 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) DBE_Params.HeadroomdB = 0; DBE_Params.VolumeControl = LVDBE_VOLUME_OFF; DBE_Params.VolumedB = 0; +#ifdef SUPPORT_MC + DBE_Params.NrChannels = LocalParams.NrChannels; +#endif /* * Make the changes @@ -775,7 +794,9 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) { EQNB_Params.SourceFormat = LVEQNB_MONOINSTEREO; /* Force to Mono-in-Stereo mode */ } - +#ifdef SUPPORT_MC + EQNB_Params.NrChannels = LocalParams.NrChannels; +#endif /* * Set the control flag @@ -849,7 +870,9 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) CS_Params.SampleRate = LocalParams.SampleRate; CS_Params.ReverbLevel = LocalParams.VirtualizerReverbLevel; CS_Params.EffectLevel = LocalParams.CS_EffectLevel; - +#ifdef SUPPORT_MC + CS_Params.NrChannels = LocalParams.NrChannels; +#endif /* * Set the control flag diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c index 26c1c4f357..ade329b675 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c @@ -584,8 +584,11 @@ LVM_ReturnStatus_en LVM_GetInstanceHandle(LVM_Handle_t *phInstance, /* * DC removal filter */ +#ifdef SUPPORT_MC + DC_Mc_D16_TRC_WRA_01_Init(&pInstance->DC_RemovalInstance); +#else DC_2I_D16_TRC_WRA_01_Init(&pInstance->DC_RemovalInstance); - +#endif /* * Treble Enhancement @@ -1039,7 +1042,11 @@ LVM_ReturnStatus_en LVM_ClearAudioBuffers(LVM_Handle_t hInstance) LVM_SetHeadroomParams(hInstance, &HeadroomParams); /* DC removal filter */ +#ifdef SUPPORT_MC + DC_Mc_D16_TRC_WRA_01_Init(&pInstance->DC_RemovalInstance); +#else DC_2I_D16_TRC_WRA_01_Init(&pInstance->DC_RemovalInstance); +#endif return LVM_SUCCESS; } diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h index b453222289..19d15328ac 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h @@ -144,12 +144,19 @@ typedef struct LVM_FLOAT *pScratch; /* Bundle scratch buffer */ LVM_INT16 BufferState; /* Buffer status */ +#ifdef SUPPORT_MC + LVM_FLOAT InDelayBuffer[3 * LVM_MAX_CHANNELS * MIN_INTERNAL_BLOCKSIZE]; +#else LVM_FLOAT InDelayBuffer[6 * MIN_INTERNAL_BLOCKSIZE]; /* Input buffer delay line, \ left and right */ +#endif LVM_INT16 InDelaySamples; /* Number of samples in the input delay buffer */ - +#ifdef SUPPORT_MC + LVM_FLOAT OutDelayBuffer[LVM_MAX_CHANNELS * MIN_INTERNAL_BLOCKSIZE]; +#else LVM_FLOAT OutDelayBuffer[2 * MIN_INTERNAL_BLOCKSIZE]; /* Output buffer delay \ line */ +#endif LVM_INT16 OutDelaySamples; /* Number of samples in the output delay buffer, \ left and right */ LVM_INT16 SamplesToOutput; /* Samples to write to the output */ @@ -282,6 +289,10 @@ typedef struct LVM_INT16 NoSmoothVolume; /* Enable or disable smooth volume changes*/ +#ifdef SUPPORT_MC + LVM_INT16 NrChannels; +#endif + } LVM_Instance_t; diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c index 4a19a131cf..94ba278dc2 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c @@ -65,6 +65,10 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, LVM_FLOAT *pToProcess = (LVM_FLOAT *)pInData; LVM_FLOAT *pProcessed = pOutData; LVM_ReturnStatus_en Status; +#ifdef SUPPORT_MC + LVM_INT32 NrChannels = pInstance->NrChannels; +#define NrFrames SampleCount // alias for clarity +#endif /* * Check if the number of samples is zero @@ -112,6 +116,10 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, if (pInstance->ControlPending == LVM_TRUE) { Status = LVM_ApplyNewSettings(hInstance); +#ifdef SUPPORT_MC + /* Update the local variable NrChannels from pInstance->NrChannels value */ + NrChannels = pInstance->NrChannels; +#endif if(Status != LVM_SUCCESS) { @@ -130,6 +138,9 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, (LVM_INT16)NumSamples); /* Number of input samples */ pInput = pOutData; pToProcess = pOutData; +#ifdef SUPPORT_MC + NrChannels = 2; +#endif } @@ -153,7 +164,6 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, */ if (SampleCount != 0) { - /* * Apply ConcertSound if required */ @@ -171,10 +181,18 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, */ if (pInstance->VC_Active!=0) { +#ifdef SUPPORT_MC + LVC_MixSoft_Mc_D16C31_SAT(&pInstance->VC_Volume, + pToProcess, + pProcessed, + (LVM_INT16)(NrFrames), + NrChannels); +#else LVC_MixSoft_1St_D16C31_SAT(&pInstance->VC_Volume, pToProcess, pProcessed, (LVM_INT16)(2 * SampleCount)); /* Left and right*/ +#endif pToProcess = pProcessed; } @@ -221,20 +239,33 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, /* * Apply the filter */ +#ifdef SUPPORT_MC + FO_Mc_D16F32C15_LShx_TRC_WRA_01(&pInstance->pTE_State->TrebleBoost_State, + pProcessed, + pProcessed, + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else FO_2I_D16F32C15_LShx_TRC_WRA_01(&pInstance->pTE_State->TrebleBoost_State, pProcessed, pProcessed, (LVM_INT16)SampleCount); +#endif } - - /* - * Volume balance - */ - LVC_MixSoft_1St_2i_D16C31_SAT(&pInstance->VC_BalanceMix, - pProcessed, - pProcessed, - SampleCount); +#ifdef SUPPORT_MC + /* TODO - Multichannel support to be added */ + if (NrChannels == 2) +#endif + { + /* + * Volume balance + */ + LVC_MixSoft_1St_2i_D16C31_SAT(&pInstance->VC_BalanceMix, + pProcessed, + pProcessed, + SampleCount); + } /* * Perform Parametric Spectum Analysis @@ -242,28 +273,39 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, if ((pInstance->Params.PSA_Enable == LVM_PSA_ON) && (pInstance->InstParams.PSA_Included == LVM_PSA_ON)) { - From2iToMono_Float(pProcessed, - pInstance->pPSAInput, - (LVM_INT16)(SampleCount)); - - LVPSA_Process(pInstance->hPSAInstance, - pInstance->pPSAInput, - (LVM_UINT16)(SampleCount), - AudioTime); +#ifdef SUPPORT_MC + FromMcToMono_Float(pProcessed, + pInstance->pPSAInput, + (LVM_INT16)(NrFrames), + NrChannels); +#else + From2iToMono_Float(pProcessed, + pInstance->pPSAInput, + (LVM_INT16)(SampleCount)); +#endif + + LVPSA_Process(pInstance->hPSAInstance, + pInstance->pPSAInput, + (LVM_UINT16)(SampleCount), + AudioTime); } - /* * DC removal */ +#ifdef SUPPORT_MC + DC_Mc_D16_TRC_WRA_01(&pInstance->DC_RemovalInstance, + pProcessed, + pProcessed, + (LVM_INT16)NrFrames, + NrChannels); +#else DC_2I_D16_TRC_WRA_01(&pInstance->DC_RemovalInstance, pProcessed, pProcessed, (LVM_INT16)SampleCount); - - +#endif } - /* * Manage the output buffer */ @@ -497,4 +539,4 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, return(LVM_SUCCESS); } -#endif \ No newline at end of file +#endif diff --git a/media/libeffects/lvm/lib/Common/lib/AGC.h b/media/libeffects/lvm/lib/Common/lib/AGC.h index 9a3d35d4af..06e742eaf3 100644 --- a/media/libeffects/lvm/lib/Common/lib/AGC.h +++ b/media/libeffects/lvm/lib/Common/lib/AGC.h @@ -78,6 +78,15 @@ void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance, /* const LVM_FLOAT *pMonoSrc, /* Mono source */ LVM_FLOAT *pDst, /* Stereo destination */ LVM_UINT16 n); /* Number of samples */ +#ifdef SUPPORT_MC +void AGC_MIX_VOL_Mc1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance, /* Instance pointer */ + const LVM_FLOAT *pStSrc, /* Source */ + const LVM_FLOAT *pMonoSrc, /* Mono source */ + LVM_FLOAT *pDst, /* Destination */ + LVM_UINT16 NrFrames, /* Number of frames */ + LVM_UINT16 NrChannels); /* Number of channels */ +#endif + #else void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_D32_t *pInstance, /* Instance pointer */ const LVM_INT32 *pStSrc, /* Stereo source */ diff --git a/media/libeffects/lvm/lib/Common/lib/BIQUAD.h b/media/libeffects/lvm/lib/Common/lib/BIQUAD.h index 3ee7f6363e..01539b26a0 100644 --- a/media/libeffects/lvm/lib/Common/lib/BIQUAD.h +++ b/media/libeffects/lvm/lib/Common/lib/BIQUAD.h @@ -30,8 +30,17 @@ extern "C" { #ifdef BUILD_FLOAT typedef struct { +#ifdef SUPPORT_MC + /* The memory region created by this structure instance is typecast + * into another structure containing a pointer and an array of filter + * coefficients. In one case this memory region is used for storing + * DC component of channels + */ + LVM_FLOAT *pStorage; + LVM_FLOAT Storage[LVM_MAX_CHANNELS]; +#else LVM_FLOAT Storage[6]; - +#endif } Biquad_FLOAT_Instance_t; #else typedef struct @@ -179,7 +188,12 @@ typedef struct typedef struct { - LVM_FLOAT Storage[ (2 * 2) ]; /* Two channels, two taps of size LVM_INT32 */ +#ifdef SUPPORT_MC + /* LVM_MAX_CHANNELS channels, two taps of size LVM_FLOAT */ + LVM_FLOAT Storage[ (LVM_MAX_CHANNELS * 2) ]; +#else + LVM_FLOAT Storage[ (2 * 2) ]; /* Two channels, two taps of size LVM_FLOAT */ +#endif } Biquad_2I_Order1_FLOAT_Taps_t; #else typedef struct @@ -197,12 +211,17 @@ typedef struct #ifdef BUILD_FLOAT typedef struct { - LVM_FLOAT Storage[ (1 * 4) ]; /* One channel, four taps of size LVM_INT32 */ + LVM_FLOAT Storage[ (1 * 4) ]; /* One channel, four taps of size LVM_FLOAT */ } Biquad_1I_Order2_FLOAT_Taps_t; typedef struct { - LVM_FLOAT Storage[ (2 * 4) ]; /* Two channels, four taps of size LVM_INT32 */ +#ifdef SUPPORT_MC + /* LVM_MAX_CHANNELS, four taps of size LVM_FLOAT */ + LVM_FLOAT Storage[ (LVM_MAX_CHANNELS * 4) ]; +#else + LVM_FLOAT Storage[ (2 * 4) ]; /* Two channels, four taps of size LVM_FLOAT */ +#endif } Biquad_2I_Order2_FLOAT_Taps_t; #else typedef struct @@ -366,6 +385,13 @@ void BQ_2I_D32F32C30_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, LVM_FLOAT *pDataIn, LVM_FLOAT *pDataOut, LVM_INT16 NrSamples); +#ifdef SUPPORT_MC +void BQ_MC_D32F32C30_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void BQ_2I_D32F32Cll_TRC_WRA_01_Init ( Biquad_Instance_t *pInstance, Biquad_2I_Order2_Taps_t *pTaps, @@ -434,6 +460,13 @@ void FO_1I_D32F32C31_TRC_WRA_01( Biquad_FLOAT_Instance_t *pInstance, LVM_FLOAT *pDataIn, LVM_FLOAT *pDataOut, LVM_INT16 NrSamples); +#ifdef SUPPORT_MC +void FO_Mc_D16F32C15_LShx_TRC_WRA_01(Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void FO_1I_D32F32Cll_TRC_WRA_01_Init( Biquad_Instance_t *pInstance, Biquad_1I_Order1_Taps_t *pTaps, @@ -527,6 +560,13 @@ void PK_2I_D32F32C14G11_TRC_WRA_01( Biquad_FLOAT_Instance_t *pInstance, LVM_FLOAT *pDataIn, LVM_FLOAT *pDataOut, LVM_INT16 NrSamples); +#ifdef SUPPORT_MC +void PK_Mc_D32F32C14G11_TRC_WRA_01(Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void PK_2I_D32F32C14G11_TRC_WRA_01 ( Biquad_Instance_t *pInstance, LVM_INT32 *pDataIn, @@ -540,12 +580,22 @@ void PK_2I_D32F32C14G11_TRC_WRA_01 ( Biquad_Instance_t *pInstance, /*** 16 bit data path STEREO ******************************************************/ #ifdef BUILD_FLOAT +#ifdef SUPPORT_MC +void DC_Mc_D16_TRC_WRA_01_Init ( Biquad_FLOAT_Instance_t *pInstance); + +void DC_Mc_D16_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#else void DC_2I_D16_TRC_WRA_01_Init ( Biquad_FLOAT_Instance_t *pInstance); void DC_2I_D16_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, LVM_FLOAT *pDataIn, LVM_FLOAT *pDataOut, LVM_INT16 NrSamples); +#endif #else void DC_2I_D16_TRC_WRA_01_Init ( Biquad_Instance_t *pInstance); diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h b/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h index 81e288cf4d..a76354d449 100644 --- a/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h +++ b/media/libeffects/lvm/lib/Common/lib/LVM_Timer.h @@ -44,7 +44,15 @@ extern "C" { typedef struct { - LVM_INT32 Storage[6]; + /* + * The memory area created using this structure is internally + * typecast to LVM_Timer_Instance_Private_t and used. + * The LVM_Timer_Instance_Private_t structure has 3 pointer type elements + * 2 elements of type LVM_INT32 and one element of type LVM_INT16. + * Inorder to cater both 32 and 64 bit builds, Storage array should + * have a minimum of 9 elements of type LVM_INT32. + */ + LVM_INT32 Storage[9]; } LVM_Timer_Instance_t; diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h index ea16072289..303b62d92e 100644 --- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h +++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h @@ -122,6 +122,12 @@ typedef int16_t effect_buffer_t; #endif // NATIVE_FLOAT_BUFFER +#ifdef SUPPORT_MC +#define LVM_MAX_CHANNELS 8 // FCC_8 +#else +#define LVM_MAX_CHANNELS 2 // FCC_2 +#endif + /****************************************************************************************/ /* */ /* Standard Enumerated types */ @@ -143,6 +149,9 @@ typedef enum LVM_STEREO = 0, LVM_MONOINSTEREO = 1, LVM_MONO = 2, +#ifdef SUPPORT_MC + LVM_MULTICHANNEL = 3, +#endif LVM_SOURCE_DUMMY = LVM_MAXENUM } LVM_Format_en; diff --git a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h index 0ba20a3c71..7468a90d95 100644 --- a/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h +++ b/media/libeffects/lvm/lib/Common/lib/VectorArithmetic.h @@ -47,6 +47,16 @@ void LoadConst_32( const LVM_INT32 val, void Copy_Float( const LVM_FLOAT *src, LVM_FLOAT *dst, LVM_INT16 n ); +#ifdef SUPPORT_MC +void Copy_Float_Mc_Stereo( const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT32 NrChannels); +void Copy_Float_Stereo_Mc( const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT32 NrChannels); +#endif #else void Copy_16( const LVM_INT16 *src, LVM_INT16 *dst, @@ -181,6 +191,12 @@ void MonoTo2I_32( const LVM_INT32 *src, void From2iToMono_Float( const LVM_FLOAT *src, LVM_FLOAT *dst, LVM_INT16 n); +#ifdef SUPPORT_MC +void FromMcToMono_Float(const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void From2iToMono_32( const LVM_INT32 *src, LVM_INT32 *dst, diff --git a/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c b/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c index fa9f01f3e9..5c8655fc6a 100644 --- a/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c +++ b/media/libeffects/lvm/lib/Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c @@ -305,4 +305,150 @@ void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance, /* return; } +#ifdef SUPPORT_MC +/****************************************************************************************/ +/* */ +/* FUNCTION: AGC_MIX_VOL_Mc1Mon_D32_WRA */ +/* */ +/* DESCRIPTION: */ +/* Apply AGC and mix signals */ +/* */ +/* */ +/* McSrc ------------------| */ +/* | */ +/* ______ _|_ ________ */ +/* | | | | | | */ +/* MonoSrc -->| AGC |---->| + |----->| Volume |------------------------------+---> */ +/* | Gain | |___| | Gain | | */ +/* |______| |________| | */ +/* /|\ __________ ________ | */ +/* | | | | | | */ +/* |-------------------------------| AGC Gain |<--| Peak |<--| */ +/* | Update | | Detect | */ +/* |__________| |________| */ +/* */ +/* */ +/* PARAMETERS: */ +/* pInstance Instance pointer */ +/* pMcSrc Multichannel source */ +/* pMonoSrc Mono band pass source */ +/* pDst Multichannel destination */ +/* NrFrames Number of frames */ +/* NrChannels Number of channels */ +/* */ +/* RETURNS: */ +/* Void */ +/* */ +/* NOTES: */ +/* */ +/****************************************************************************************/ +void AGC_MIX_VOL_Mc1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t *pInstance, + const LVM_FLOAT *pMcSrc, + const LVM_FLOAT *pMonoSrc, + LVM_FLOAT *pDst, + LVM_UINT16 NrFrames, + LVM_UINT16 NrChannels) +{ + + /* + * General variables + */ + LVM_UINT16 i, jj; /* Sample index */ + LVM_FLOAT SampleVal; /* Sample value */ + LVM_FLOAT Mono; /* Mono sample */ + LVM_FLOAT AbsPeak; /* Absolute peak signal */ + LVM_FLOAT AGC_Mult; /* Short AGC gain */ + LVM_FLOAT Vol_Mult; /* Short volume */ + + + /* + * Instance control variables + */ + LVM_FLOAT AGC_Gain = pInstance->AGC_Gain; /* Get the current AGC gain */ + LVM_FLOAT AGC_MaxGain = pInstance->AGC_MaxGain; /* Get maximum AGC gain */ + LVM_FLOAT AGC_Attack = pInstance->AGC_Attack; /* Attack scaler */ + /* Decay scaler */ + LVM_FLOAT AGC_Decay = (pInstance->AGC_Decay * (1 << (DECAY_SHIFT))); + LVM_FLOAT AGC_Target = pInstance->AGC_Target; /* Get the target level */ + LVM_FLOAT Vol_Current = pInstance->Volume; /* Actual volume setting */ + LVM_FLOAT Vol_Target = pInstance->Target; /* Target volume setting */ + LVM_FLOAT Vol_TC = pInstance->VolumeTC; /* Time constant */ + + + /* + * Process on a sample by sample basis + */ + for (i = 0; i < NrFrames; i++) /* For each frame */ + { + + /* + * Get the scalers + */ + AGC_Mult = (LVM_FLOAT)(AGC_Gain); /* Get the AGC gain */ + Vol_Mult = (LVM_FLOAT)(Vol_Current); /* Get the volume gain */ + + AbsPeak = 0.0f; + /* + * Get the input samples + */ + for (jj = 0; jj < NrChannels; jj++) + { + SampleVal = *pMcSrc++; /* Get the sample value of jj Channel*/ + Mono = *pMonoSrc; /* Get the mono sample */ + + /* + * Apply the AGC gain to the mono input and mix with the input signal + */ + SampleVal += (Mono * AGC_Mult); /* Mix in the mono signal */ + + /* + * Apply the volume and write to the output stream + */ + SampleVal = SampleVal * Vol_Mult; + + *pDst++ = SampleVal; /* Save the results */ + + /* + * Update the AGC gain + */ + AbsPeak = Abs_Float(SampleVal) > AbsPeak ? Abs_Float(SampleVal) : AbsPeak; + } + if (AbsPeak > AGC_Target) + { + /* + * The signal is too large so decrease the gain + */ + AGC_Gain = AGC_Gain * AGC_Attack; + } + else + { + /* + * The signal is too small so increase the gain + */ + if (AGC_Gain > AGC_MaxGain) + { + AGC_Gain -= (AGC_Decay); + } + else + { + AGC_Gain += (AGC_Decay); + } + } + pMonoSrc++; + /* + * Update the gain + */ + Vol_Current += (Vol_Target - Vol_Current) * ((LVM_FLOAT)Vol_TC / VOL_TC_FLOAT); + } + + + /* + * Update the parameters + */ + pInstance->Volume = Vol_Current; /* Actual volume setting */ + pInstance->AGC_Gain = AGC_Gain; + + return; +} +#endif /*SUPPORT_MC*/ #endif /*BUILD_FLOAT*/ diff --git a/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c index 960de79859..d63365c609 100644 --- a/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c +++ b/media/libeffects/lvm/lib/Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c @@ -123,6 +123,87 @@ void BQ_2I_D32F32C30_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInst } } + +#ifdef SUPPORT_MC +/************************************************************************** + ASSUMPTIONS: + COEFS- + pBiquadState->coefs[0] is A2, pBiquadState->coefs[1] is A1 + pBiquadState->coefs[2] is A0, pBiquadState->coefs[3] is -B2 + pBiquadState->coefs[4] is -B1 + + DELAYS- + pBiquadState->pDelays[0] to + pBiquadState->pDelays[NrChannels - 1] is x(n-1) for all NrChannels + + pBiquadState->pDelays[NrChannels] to + pBiquadState->pDelays[2*NrChannels - 1] is x(n-2) for all NrChannels + + pBiquadState->pDelays[2*NrChannels] to + pBiquadState->pDelays[3*NrChannels - 1] is y(n-1) for all NrChannels + + pBiquadState->pDelays[3*NrChannels] to + pBiquadState->pDelays[4*NrChannels - 1] is y(n-2) for all NrChannels +***************************************************************************/ +void BQ_MC_D32F32C30_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) + + + { + LVM_FLOAT yn, temp; + LVM_INT16 ii, jj; + PFilter_State_FLOAT pBiquadState = (PFilter_State_FLOAT) pInstance; + + for (ii = NrFrames; ii != 0; ii--) + { + /************************************************************************** + PROCESSING CHANNEL-WISE + ***************************************************************************/ + for (jj = 0; jj < NrChannels; jj++) + { + /* yn= (A2 * x(n-2)) */ + yn = pBiquadState->coefs[0] * pBiquadState->pDelays[NrChannels + jj]; + + /* yn+= (A1 * x(n-1)) */ + temp = pBiquadState->coefs[1] * pBiquadState->pDelays[jj]; + yn += temp; + + /* yn+= (A0 * x(n)) */ + temp = pBiquadState->coefs[2] * (*pDataIn); + yn += temp; + + /* yn+= (-B2 * y(n-2)) */ + temp = pBiquadState->coefs[3] * pBiquadState->pDelays[NrChannels*3 + jj]; + yn += temp; + + /* yn+= (-B1 * y(n-1)) */ + temp = pBiquadState->coefs[4] * pBiquadState->pDelays[NrChannels*2 + jj]; + yn += temp; + + /************************************************************************** + UPDATING THE DELAYS + ***************************************************************************/ + pBiquadState->pDelays[NrChannels * 3 + jj] = + pBiquadState->pDelays[NrChannels * 2 + jj]; /* y(n-2)=y(n-1)*/ + pBiquadState->pDelays[NrChannels * 1 + jj] = + pBiquadState->pDelays[jj]; /* x(n-2)=x(n-1)*/ + pBiquadState->pDelays[NrChannels * 2 + jj] = (LVM_FLOAT)yn; /* Update y(n-1)*/ + pBiquadState->pDelays[jj] = (*pDataIn); /* Update x(n-1)*/ + pDataIn++; + /************************************************************************** + WRITING THE OUTPUT + ***************************************************************************/ + *pDataOut = (LVM_FLOAT)yn; /* Write jj Channel output */ + pDataOut++; + } + } + + } +#endif /*SUPPORT_MC*/ + #else void BQ_2I_D32F32C30_TRC_WRA_01 ( Biquad_Instance_t *pInstance, LVM_INT32 *pDataIn, diff --git a/media/libeffects/lvm/lib/Common/src/Copy_16.c b/media/libeffects/lvm/lib/Common/src/Copy_16.c index e4890313aa..1f9f659964 100644 --- a/media/libeffects/lvm/lib/Common/src/Copy_16.c +++ b/media/libeffects/lvm/lib/Common/src/Copy_16.c @@ -84,5 +84,65 @@ void Copy_Float( const LVM_FLOAT *src, return; } +#ifdef SUPPORT_MC +// Extract out the stereo channel pair from multichannel source. +void Copy_Float_Mc_Stereo(const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, /* Number of frames */ + LVM_INT32 NrChannels) +{ + LVM_INT16 ii; + + if (NrChannels >= 2) + { + for (ii = NrFrames; ii != 0; ii--) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst += 2; + src += NrChannels; + } + } + else if (NrChannels == 1) + { // not expected to occur, provided for completeness. + src += (NrFrames - 1); + dst += 2 * (NrFrames - 1); + for (ii = NrFrames; ii != 0; ii--) + { + dst[0] = src[0]; + dst[1] = src[0]; + dst -= 2; + src --; + } + } +} + +// Merge a multichannel source with stereo contained in dst, to dst. +void Copy_Float_Stereo_Mc(const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, /* Number of frames*/ + LVM_INT32 NrChannels) +{ + LVM_INT16 ii, jj; + LVM_FLOAT *src_st = dst + 2 * (NrFrames - 1); + + // repack dst which carries stereo information + // together with the upper channels of src. + dst += NrChannels * (NrFrames - 1); + src += NrChannels * (NrFrames - 1); + for (ii = NrFrames; ii != 0; ii--) + { + dst[0] = src_st[0]; + dst[1] = src_st[1]; + for (jj = 2; jj < NrChannels; jj++) + { + dst[jj] = src[jj]; + } + dst -= NrChannels; + src -= NrChannels; + src_st -= 2; + } +} +#endif #endif /**********************************************************************************/ diff --git a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c index d261c9eb9a..13fac5e6b9 100644 --- a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c +++ b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01.c @@ -33,7 +33,7 @@ void DC_2I_D16_TRC_WRA_01( Biquad_FLOAT_Instance_t *pInstance, RightDC = pBiquadState->RightDC; for(j = NrSamples-1; j >= 0; j--) { - /* Subtract DC an saturate */ + /* Subtract DC and saturate */ Diff =* (pDataIn++) - (LeftDC); if (Diff > 1.0f) { Diff = 1.0f; } @@ -64,6 +64,58 @@ void DC_2I_D16_TRC_WRA_01( Biquad_FLOAT_Instance_t *pInstance, } +#ifdef SUPPORT_MC +/* + * FUNCTION: DC_Mc_D16_TRC_WRA_01 + * + * DESCRIPTION: + * DC removal from all channels of a multichannel input + * + * PARAMETERS: + * pInstance Instance pointer + * pDataIn Input/Source + * pDataOut Output/Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void DC_Mc_D16_TRC_WRA_01(Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) + { + LVM_FLOAT *ChDC; + LVM_FLOAT Diff; + LVM_INT32 j; + LVM_INT32 i; + PFilter_FLOAT_State_Mc pBiquadState = (PFilter_FLOAT_State_Mc) pInstance; + + ChDC = &pBiquadState->ChDC[0]; + for (j = NrFrames - 1; j >= 0; j--) + { + /* Subtract DC and saturate */ + for (i = NrChannels - 1; i >= 0; i--) + { + Diff = *(pDataIn++) - (ChDC[i]); + if (Diff > 1.0f) { + Diff = 1.0f; + } else if (Diff < -1.0f) { + Diff = -1.0f; } + *(pDataOut++) = (LVM_FLOAT)Diff; + if (Diff < 0) { + ChDC[i] -= DC_FLOAT_STEP; + } else { + ChDC[i] += DC_FLOAT_STEP; } + } + + } + + } +#endif #else void DC_2I_D16_TRC_WRA_01( Biquad_Instance_t *pInstance, LVM_INT16 *pDataIn, diff --git a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c index 4f4fcd8100..0f941a0b20 100644 --- a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c +++ b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Init.c @@ -24,6 +24,17 @@ void DC_2I_D16_TRC_WRA_01_Init(Biquad_FLOAT_Instance_t *pInstance) pBiquadState->LeftDC = 0.0f; pBiquadState->RightDC = 0.0f; } +#ifdef SUPPORT_MC +void DC_Mc_D16_TRC_WRA_01_Init(Biquad_FLOAT_Instance_t *pInstance) +{ + PFilter_FLOAT_State_Mc pBiquadState = (PFilter_FLOAT_State_Mc) pInstance; + LVM_INT32 i; + for (i = 0; i < LVM_MAX_CHANNELS; i++) + { + pBiquadState->ChDC[i] = 0.0f; + } +} +#endif #else void DC_2I_D16_TRC_WRA_01_Init(Biquad_Instance_t *pInstance) { diff --git a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Private.h b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Private.h index fa6b729723..db3a6d3fa0 100644 --- a/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Private.h +++ b/media/libeffects/lvm/lib/Common/src/DC_2I_D16_TRC_WRA_01_Private.h @@ -34,6 +34,13 @@ typedef struct _Filter_FLOAT_State_ LVM_FLOAT RightDC; /* RightDC */ }Filter_FLOAT_State; typedef Filter_FLOAT_State * PFilter_FLOAT_State ; +#ifdef SUPPORT_MC +typedef struct _Filter_FLOAT_State_Mc_ +{ + LVM_FLOAT ChDC[LVM_MAX_CHANNELS]; /* ChannelDC */ +} Filter_FLOAT_State_Mc; +typedef Filter_FLOAT_State_Mc * PFilter_FLOAT_State_Mc ; +#endif #else typedef struct _Filter_State_ { diff --git a/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c index 192927ca32..2a50f186c3 100644 --- a/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c +++ b/media/libeffects/lvm/lib/Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c @@ -117,6 +117,93 @@ void FO_2I_D16F32C15_LShx_TRC_WRA_01(Biquad_FLOAT_Instance_t *pInstance, } } +#ifdef SUPPORT_MC +/************************************************************************** +ASSUMPTIONS: +COEFS- +pBiquadState->coefs[0] is A1, +pBiquadState->coefs[1] is A0, +pBiquadState->coefs[2] is -B1, +DELAYS- +pBiquadState->pDelays[2*ch + 0] is x(n-1) of the 'ch' - channel +pBiquadState->pDelays[2*ch + 1] is y(n-1) of the 'ch' - channel +The index 'ch' runs from 0 to (NrChannels - 1) + +PARAMETERS: + pInstance Pointer Instance + pDataIn Input/Source + pDataOut Output/Destination + NrFrames Number of frames + NrChannels Number of channels + +RETURNS: + void +***************************************************************************/ +void FO_Mc_D16F32C15_LShx_TRC_WRA_01(Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) + { + LVM_FLOAT yn; + LVM_FLOAT Temp; + LVM_INT16 ii; + LVM_INT16 ch; + PFilter_Float_State pBiquadState = (PFilter_Float_State) pInstance; + + LVM_FLOAT *pDelays = pBiquadState->pDelays; + LVM_FLOAT *pCoefs = &pBiquadState->coefs[0]; + LVM_FLOAT A0 = pCoefs[1]; + LVM_FLOAT A1 = pCoefs[0]; + LVM_FLOAT B1 = pCoefs[2]; + + + + + for (ii = NrFrames; ii != 0; ii--) + { + + /************************************************************************** + PROCESSING OF THE CHANNELS + ***************************************************************************/ + for (ch = 0; ch < NrChannels; ch++) + { + // yn =A1 * x(n-1) + yn = (LVM_FLOAT)A1 * pDelays[0]; + + // yn+=A0 * x(n) + yn += (LVM_FLOAT)A0 * (*pDataIn); + + // yn += (-B1 * y(n-1)) + Temp = B1 * pDelays[1]; + yn += Temp; + + + /************************************************************************** + UPDATING THE DELAYS + ***************************************************************************/ + pDelays[1] = yn; // Update y(n-1) + pDelays[0] = (*pDataIn++); // Update x(n-1) + + /************************************************************************** + WRITING THE OUTPUT + ***************************************************************************/ + + /*Saturate results*/ + if (yn > 1.0f) + { + yn = 1.0f; + } else if (yn < -1.0f) { + yn = -1.0f; + } + + *pDataOut++ = (LVM_FLOAT)yn; + pDelays += 2; + } + pDelays -= NrChannels * 2; + } + } +#endif #else void FO_2I_D16F32C15_LShx_TRC_WRA_01(Biquad_Instance_t *pInstance, LVM_INT16 *pDataIn, diff --git a/media/libeffects/lvm/lib/Common/src/From2iToMono_32.c b/media/libeffects/lvm/lib/Common/src/From2iToMono_32.c index ac1eea8733..d02af88cbc 100644 --- a/media/libeffects/lvm/lib/Common/src/From2iToMono_32.c +++ b/media/libeffects/lvm/lib/Common/src/From2iToMono_32.c @@ -68,5 +68,47 @@ void From2iToMono_Float( const LVM_FLOAT *src, return; } +#ifdef SUPPORT_MC +/* + * FUNCTION: FromMcToMono_Float + * + * DESCRIPTION: + * Creates a mono stream from a multichannel input taking the avergae of + * sample values of all channels + * + * PARAMETERS: + * src Source + * dst Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void FromMcToMono_Float(const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + LVM_INT16 ii, jj; + LVM_FLOAT Temp; + + for (ii = NrFrames; ii != 0; ii--) + { + Temp = 0.0f; + for (jj = NrChannels; jj !=0; jj--) + { + Temp += (*src); + src++; + } + *dst = Temp / NrChannels; + dst++; + } + + return; +} +#endif + #endif /**********************************************************************************/ diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c index d2694cc610..419c7c5069 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixInSoft_D16C31_SAT.c @@ -26,10 +26,10 @@ FUNCTION LVCore_MIXSOFT_1ST_D16C31_WRA ***********************************************************************************/ #ifdef BUILD_FLOAT -void LVC_Core_MixInSoft_D16C31_SAT( LVMixer3_FLOAT_st *ptrInstance, - const LVM_FLOAT *src, - LVM_FLOAT *dst, - LVM_INT16 n) +void LVC_Core_MixInSoft_D16C31_SAT(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 n) { LVM_INT16 OutLoop; @@ -114,6 +114,139 @@ void LVC_Core_MixInSoft_D16C31_SAT( LVMixer3_FLOAT_st *ptrInstance, } pInstance->Current = Current; } +#ifdef SUPPORT_MC +/* + * FUNCTION: LVC_Core_MixInSoft_Mc_D16C31_SAT + * + * DESCRIPTION: + * Mixer function with support for processing multichannel input. + * + * PARAMETERS: + * ptrInstance Instance pointer + * src Source + * dst Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void LVC_Core_MixInSoft_Mc_D16C31_SAT(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + + LVM_INT16 OutLoop; + LVM_INT16 InLoop; + LVM_INT32 ii, jj; + Mix_Private_FLOAT_st *pInstance = (Mix_Private_FLOAT_st *)(ptrInstance->PrivateParams); + LVM_FLOAT Delta = pInstance->Delta; + LVM_FLOAT Current = pInstance->Current; + LVM_FLOAT Target = pInstance->Target; + LVM_FLOAT Temp; + + /* + * Same operation is performed on consecutive frames. + * So two frames are processed in one iteration and + * the loop will run only for half the NrFrames value times. + */ + InLoop = (LVM_INT16)(NrFrames >> 1); + /* OutLoop is calculated to handle cases where NrFrames value can be odd.*/ + OutLoop = (LVM_INT16)(NrFrames - (InLoop << 1)); + + if (Current < Target) { + if (OutLoop) { + Temp = Current + Delta; + Current = Temp; + if (Current > Target) + Current = Target; + + for (ii = OutLoop*NrChannels; ii != 0; ii--) { + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + } + } + + for (ii = InLoop; ii != 0; ii--) { + Temp = Current + Delta; + Current = Temp; + if (Current > Target) + Current = Target; + + for (jj = NrChannels; jj != 0 ; jj--) { + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + + } + } + } + else{ + if (OutLoop) { + Current -= Delta; + if (Current < Target) + Current = Target; + + for (ii = OutLoop*NrChannels; ii != 0; ii--) { + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + } + } + + for (ii = InLoop; ii != 0; ii--) { + Current -= Delta; + if (Current < Target) + Current = Target; + + for (jj = NrChannels; jj != 0 ; jj--) { + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + + Temp = (*dst) + (*(src++) * Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = Temp; + + } + } + } + pInstance->Current = Current; +} + +#endif #else void LVC_Core_MixInSoft_D16C31_SAT( LVMixer3_st *ptrInstance, const LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c index b5e7f5caf1..5bfdad8766 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c @@ -27,10 +27,10 @@ FUNCTION LVCore_MIXSOFT_1ST_D16C31_WRA ***********************************************************************************/ #ifdef BUILD_FLOAT -void LVC_Core_MixSoft_1St_D16C31_WRA( LVMixer3_FLOAT_st *ptrInstance, - const LVM_FLOAT *src, - LVM_FLOAT *dst, - LVM_INT16 n) +void LVC_Core_MixSoft_1St_D16C31_WRA(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 n) { LVM_INT16 OutLoop; LVM_INT16 InLoop; @@ -105,6 +105,119 @@ void LVC_Core_MixSoft_1St_D16C31_WRA( LVMixer3_FLOAT_st *ptrInstance, } pInstance->Current=Current; } + + +#ifdef SUPPORT_MC +/* + * FUNCTION: LVC_Core_MixSoft_Mc_D16C31_WRA + * + * DESCRIPTION: + * Mixer function with support for processing multichannel input + * + * PARAMETERS: + * ptrInstance Instance pointer + * src Source + * dst Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void LVC_Core_MixSoft_Mc_D16C31_WRA(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + LVM_INT16 OutLoop; + LVM_INT16 InLoop; + LVM_INT32 ii, jj; + Mix_Private_FLOAT_st *pInstance=(Mix_Private_FLOAT_st *)(ptrInstance->PrivateParams); + LVM_FLOAT Delta= (LVM_FLOAT)pInstance->Delta; + LVM_FLOAT Current = (LVM_FLOAT)pInstance->Current; + LVM_FLOAT Target= (LVM_FLOAT)pInstance->Target; + LVM_FLOAT Temp; + + /* + * Same operation is performed on consecutive frames. + * So two frames are processed in one iteration and + * the loop will run only for half the NrFrames value times. + */ + InLoop = (LVM_INT16)(NrFrames >> 1); + /* OutLoop is calculated to handle cases where NrFrames value can be odd.*/ + OutLoop = (LVM_INT16)(NrFrames - (InLoop << 1)); + + if (Current 1.0f) + Temp = 1.0f; + else if (Temp < -1.0f) + Temp = -1.0f; + + Current=Temp; + if (Current > Target) + Current = Target; + + for (ii = OutLoop; ii != 0; ii--) { + for (jj = NrChannels; jj !=0; jj--) { + *(dst++) = (((LVM_FLOAT)*(src++) * (LVM_FLOAT)Current)); + } + } + } + + for (ii = InLoop; ii != 0; ii--) { + + Temp = Current + Delta; + + if (Temp > 1.0f) + Temp = 1.0f; + else if (Temp < -1.0f) + Temp = -1.0f; + + Current=Temp; + if (Current > Target) + Current = Target; + + for (jj = NrChannels; jj != 0 ; jj--) + { + *(dst++) = (((LVM_FLOAT)*(src++) * Current)); + *(dst++) = (((LVM_FLOAT)*(src++) * Current)); + } + } + } + else{ + if (OutLoop) { + Current -= Delta; + if (Current < Target) + Current = Target; + + for (ii = OutLoop; ii != 0; ii--) { + for (jj = NrChannels; jj !=0; jj--) { + *(dst++) = (((LVM_FLOAT)*(src++) * (LVM_FLOAT)Current)); + } + } + } + + for (ii = InLoop; ii != 0; ii--) { + Current -= Delta; + if (Current < Target) + Current = Target; + + for (jj = NrChannels; jj != 0 ; jj--) + { + *(dst++) = (((LVM_FLOAT)*(src++) * Current)); + *(dst++) = (((LVM_FLOAT)*(src++) * Current)); + } + } + } + pInstance->Current=Current; +} +#endif + #else void LVC_Core_MixSoft_1St_D16C31_WRA( LVMixer3_st *ptrInstance, const LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c index 192f12613b..65956f7f89 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_MixInSoft_D16C31_SAT.c @@ -34,10 +34,10 @@ FUNCTION MIXINSOFT_D16C31_SAT ***********************************************************************************/ #ifdef BUILD_FLOAT -void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_FLOAT_st *ptrInstance, - LVM_FLOAT *src, - LVM_FLOAT *dst, - LVM_INT16 n) +void LVC_MixInSoft_D16C31_SAT(LVMixer3_1St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 n) { char HardMixing = TRUE; LVM_FLOAT TargetGain; @@ -106,6 +106,110 @@ void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_FLOAT_st *ptrInstance, } } + + + +#ifdef SUPPORT_MC +/* + * FUNCTION: LVC_MixInSoft_Mc_D16C31_SAT + * + * DESCRIPTION: + * Mixer function with support for processing multichannel input + * + * PARAMETERS: + * ptrInstance Instance pointer + * src Source + * dst Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void LVC_MixInSoft_Mc_D16C31_SAT(LVMixer3_1St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + char HardMixing = TRUE; + LVM_FLOAT TargetGain; + Mix_Private_FLOAT_st *pInstance = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[0].PrivateParams); + + if (NrFrames <= 0) return; + + /****************************************************************************** + SOFT MIXING + *******************************************************************************/ + if (pInstance->Current != pInstance->Target) + { + if (pInstance->Delta == 1.0f) { + pInstance->Current = pInstance->Target; + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[0]), TargetGain); + }else if (Abs_Float(pInstance->Current - pInstance->Target) < pInstance->Delta) { + pInstance->Current = pInstance->Target; /* Difference is not significant anymore. \ + Make them equal. */ + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[0]), TargetGain); + }else{ + /* Soft mixing has to be applied */ + HardMixing = FALSE; + LVC_Core_MixInSoft_Mc_D16C31_SAT(&(ptrInstance->MixerStream[0]), + src, + dst, + NrFrames, + NrChannels); + } + } + + /****************************************************************************** + HARD MIXING + *******************************************************************************/ + + if (HardMixing) { + if (pInstance->Target != 0) { /* Nothing to do in case Target = 0 */ + if ((pInstance->Target) == 1.0f) { + Add2_Sat_Float(src, dst, NrFrames*NrChannels); + } + else{ + Mac3s_Sat_Float(src, + (pInstance->Target), + dst, + NrFrames * NrChannels); + /* In case the LVCore function would have changed the Current value */ + pInstance->Current = pInstance->Target; + } + } + } + + + /****************************************************************************** + CALL BACK + *******************************************************************************/ + + if (ptrInstance->MixerStream[0].CallbackSet) { + if (Abs_Float(pInstance->Current - pInstance->Target) < pInstance->Delta) { + pInstance->Current = pInstance->Target; /* Difference is not significant anymore. \ + Make them equal. */ + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(ptrInstance->MixerStream, TargetGain); + ptrInstance->MixerStream[0].CallbackSet = FALSE; + if (ptrInstance->MixerStream[0].pCallBack != 0) { + (*ptrInstance->MixerStream[0].pCallBack) (\ + ptrInstance->MixerStream[0].pCallbackHandle, + ptrInstance->MixerStream[0].pGeneralPurpose, + ptrInstance->MixerStream[0].CallbackParam); + } + } + } + +} +#endif + + #else void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_st *ptrInstance, LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c index 1017de36b5..0678ae04aa 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_D16C31_SAT.c @@ -103,6 +103,101 @@ void LVC_MixSoft_1St_D16C31_SAT( LVMixer3_1St_FLOAT_st *ptrInstance, } } } +#ifdef SUPPORT_MC +/* + * FUNCTION: LVC_MixSoft_Mc_D16C31_SAT + * + * DESCRIPTION: + * Mixer function with support for processing multichannel input + * + * PARAMETERS: + * ptrInstance Instance pointer + * src Source + * dst Destination + * NrFrames Number of Frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void LVC_MixSoft_Mc_D16C31_SAT(LVMixer3_1St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + char HardMixing = TRUE; + LVM_FLOAT TargetGain; + Mix_Private_FLOAT_st *pInstance = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[0].PrivateParams); + + if (NrFrames <= 0) return; + + /****************************************************************************** + SOFT MIXING + *******************************************************************************/ + if (pInstance->Current != pInstance->Target) + { + if (pInstance->Delta == 1.0f) { + pInstance->Current = pInstance->Target; + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[0]), TargetGain); + }else if (Abs_Float(pInstance->Current - pInstance->Target) < pInstance->Delta) { + pInstance->Current = pInstance->Target; /* Difference is not significant anymore. \ + Make them equal. */ + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[0]), TargetGain); + }else{ + /* Soft mixing has to be applied */ + HardMixing = FALSE; + LVC_Core_MixSoft_Mc_D16C31_WRA(&(ptrInstance->MixerStream[0]), + src, + dst, + NrFrames, + NrChannels); + } + } + + /****************************************************************************** + HARD MIXING + *******************************************************************************/ + + if (HardMixing) { + if (pInstance->Target == 0) + LoadConst_Float(0.0, dst, NrFrames * NrChannels); + else { + if ((pInstance->Target) != 1.0f) + Mult3s_Float(src, (pInstance->Target), dst, NrFrames * NrChannels); + else if (src != dst) + Copy_Float(src, dst, NrFrames * NrChannels); + } + + } + + /****************************************************************************** + CALL BACK + *******************************************************************************/ + + if (ptrInstance->MixerStream[0].CallbackSet) { + if (Abs_Float(pInstance->Current - pInstance->Target) < pInstance->Delta) { + pInstance->Current = pInstance->Target; /* Difference is not significant anymore. \ + Make them equal. */ + TargetGain = pInstance->Target; + LVC_Mixer_SetTarget(ptrInstance->MixerStream, TargetGain); + ptrInstance->MixerStream[0].CallbackSet = FALSE; + if (ptrInstance->MixerStream[0].pCallBack != 0) { + (*ptrInstance->MixerStream[0].pCallBack) (\ + ptrInstance->MixerStream[0].pCallbackHandle, + ptrInstance->MixerStream[0].pGeneralPurpose, + ptrInstance->MixerStream[0].CallbackParam); + } + } + } +} + +#endif + #else void LVC_MixSoft_1St_D16C31_SAT( LVMixer3_1St_st *ptrInstance, const LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c index 3c90071110..8a89de19d0 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_2St_D16C31_SAT.c @@ -26,11 +26,11 @@ FUNCTION LVC_MixSoft_2St_D16C31_SAT.c ***********************************************************************************/ #ifdef BUILD_FLOAT -void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_FLOAT_st *ptrInstance, - const LVM_FLOAT *src1, - LVM_FLOAT *src2, - LVM_FLOAT *dst, - LVM_INT16 n) +void LVC_MixSoft_2St_D16C31_SAT(LVMixer3_2St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src1, + const LVM_FLOAT *src2, + LVM_FLOAT *dst, + LVM_INT16 n) { Mix_Private_FLOAT_st *pInstance1 = \ (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[0].PrivateParams); @@ -67,6 +67,70 @@ void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_FLOAT_st *ptrInstance, src1, src2, dst, n); } } + +#ifdef SUPPORT_MC +/* + * FUNCTION: LVC_MixSoft_2Mc_D16C31_SAT + * + * DESCRIPTION: + * 2 stream Mixer function with support for processing multichannel input + * + * PARAMETERS: + * ptrInstance Instance pointer + * src1 First multichannel source + * src2 Second multichannel source + * dst Destination + * NrFrames Number of frames + * NrChannels Number of channels + * + * RETURNS: + * void + * + */ +void LVC_MixSoft_2Mc_D16C31_SAT(LVMixer3_2St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src1, + const LVM_FLOAT *src2, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + Mix_Private_FLOAT_st *pInstance1 = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[0].PrivateParams); + Mix_Private_FLOAT_st *pInstance2 = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[1].PrivateParams); + + if (NrFrames <= 0) return; + + /****************************************************************************** + SOFT MIXING + *******************************************************************************/ + if ((pInstance1->Current == pInstance1->Target) && (pInstance1->Current == 0)) { + LVC_MixSoft_Mc_D16C31_SAT((LVMixer3_1St_FLOAT_st *)(&ptrInstance->MixerStream[1]), + src2, dst, NrFrames, NrChannels); + } + else if ((pInstance2->Current == pInstance2->Target) && (pInstance2->Current == 0)) { + LVC_MixSoft_Mc_D16C31_SAT((LVMixer3_1St_FLOAT_st *)(&ptrInstance->MixerStream[0]), + src1, dst, NrFrames, NrChannels); + } + else if ((pInstance1->Current != pInstance1->Target) || \ + (pInstance2->Current != pInstance2->Target)) + { + LVC_MixSoft_Mc_D16C31_SAT((LVMixer3_1St_FLOAT_st *)(&ptrInstance->MixerStream[0]), + src1, dst, NrFrames, NrChannels); + LVC_MixInSoft_Mc_D16C31_SAT((LVMixer3_1St_FLOAT_st *)(&ptrInstance->MixerStream[1]), + src2, dst, NrFrames, NrChannels); + } + else{ + /****************************************************************************** + HARD MIXING + *******************************************************************************/ + LVC_Core_MixHard_2St_D16C31_SAT(&ptrInstance->MixerStream[0], + &ptrInstance->MixerStream[1], + src1, src2, dst, NrFrames * NrChannels); + } +} +#endif + #else void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_st *ptrInstance, const LVM_INT16 *src1, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h index f904915d86..7f18747ed6 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h @@ -157,10 +157,18 @@ void LVC_Mixer_VarSlope_SetTimeConstant( LVMixer3_st *pStream, /*** 16 bit functions *************************************************************/ #ifdef BUILD_FLOAT -void LVC_MixSoft_1St_D16C31_SAT( LVMixer3_1St_FLOAT_st *pInstance, - const LVM_FLOAT *src, - LVM_FLOAT *dst, - LVM_INT16 n); +void LVC_MixSoft_1St_D16C31_SAT(LVMixer3_1St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 n); +#ifdef SUPPORT_MC +void LVC_MixSoft_Mc_D16C31_SAT(LVMixer3_1St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif + #else void LVC_MixSoft_1St_D16C31_SAT( LVMixer3_1St_st *pInstance, const LVM_INT16 *src, @@ -169,10 +177,18 @@ void LVC_MixSoft_1St_D16C31_SAT( LVMixer3_1St_st *pInstance, #endif #ifdef BUILD_FLOAT -void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_FLOAT_st *pInstance, - LVM_FLOAT *src, - LVM_FLOAT *dst, - LVM_INT16 n); +void LVC_MixInSoft_D16C31_SAT(LVMixer3_1St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 n); +#ifdef SUPPORT_MC +void LVC_MixInSoft_Mc_D16C31_SAT(LVMixer3_1St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif + #else void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_st *pInstance, LVM_INT16 *src, @@ -181,11 +197,19 @@ void LVC_MixInSoft_D16C31_SAT( LVMixer3_1St_st *pInstance, #endif #ifdef BUILD_FLOAT -void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_FLOAT_st *pInstance, - const LVM_FLOAT *src1, - LVM_FLOAT *src2, - LVM_FLOAT *dst, /* dst cannot be equal to src2 */ - LVM_INT16 n); +void LVC_MixSoft_2St_D16C31_SAT(LVMixer3_2St_FLOAT_st *pInstance, + const LVM_FLOAT *src1, + const LVM_FLOAT *src2, + LVM_FLOAT *dst, /* dst cannot be equal to src2 */ + LVM_INT16 n); +#ifdef SUPPORT_MC +void LVC_MixSoft_2Mc_D16C31_SAT(LVMixer3_2St_FLOAT_st *pInstance, + const LVM_FLOAT *src1, + const LVM_FLOAT *src2, + LVM_FLOAT *dst, /* dst cannot be equal to src2 */ + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_st *pInstance, const LVM_INT16 *src1, @@ -200,10 +224,10 @@ void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_st *pInstance, /* Gain values should not be more that 1.0 */ /**********************************************************************************/ #ifdef BUILD_FLOAT -void LVC_MixSoft_1St_2i_D16C31_SAT( LVMixer3_2St_FLOAT_st *pInstance, - const LVM_FLOAT *src, - LVM_FLOAT *dst, /* dst can be equal to src */ - LVM_INT16 n); /* Number of stereo samples */ +void LVC_MixSoft_1St_2i_D16C31_SAT(LVMixer3_2St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, /* dst can be equal to src */ + LVM_INT16 n); /* Number of stereo samples */ #else void LVC_MixSoft_1St_2i_D16C31_SAT( LVMixer3_2St_st *pInstance, const LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c index c67455a1e1..507eefa30b 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_GetTarget.c @@ -35,7 +35,7 @@ LVM_FLOAT LVC_Mixer_GetTarget( LVMixer3_FLOAT_st *pStream) { LVM_FLOAT TargetGain; Mix_Private_FLOAT_st *pInstance = (Mix_Private_FLOAT_st *)pStream->PrivateParams; - + TargetGain = pInstance->Target; // TargetGain return TargetGain; } diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h index d0d0e1f11b..f10094bd3d 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h @@ -62,6 +62,13 @@ void LVC_Core_MixInSoft_D16C31_SAT( LVMixer3_FLOAT_st *ptrInstance, const LVM_FLOAT *src, LVM_FLOAT *dst, LVM_INT16 n); +#ifdef SUPPORT_MC +void LVC_Core_MixInSoft_Mc_D16C31_SAT(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void LVC_Core_MixInSoft_D16C31_SAT( LVMixer3_st *pInstance, const LVM_INT16 *src, @@ -73,6 +80,13 @@ void LVC_Core_MixSoft_1St_D16C31_WRA( LVMixer3_FLOAT_st *ptrInstance, const LVM_FLOAT *src, LVM_FLOAT *dst, LVM_INT16 n); +#ifdef SUPPORT_MC +void LVC_Core_MixSoft_Mc_D16C31_WRA(LVMixer3_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif #else void LVC_Core_MixSoft_1St_D16C31_WRA( LVMixer3_st *pInstance, const LVM_INT16 *src, diff --git a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c index 9c17a05b6a..6c8b2dbf46 100644 --- a/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c +++ b/media/libeffects/lvm/lib/Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c @@ -119,6 +119,80 @@ void PK_2I_D32F32C14G11_TRC_WRA_01 ( Biquad_FLOAT_Instance_t *pInstance, } } + +#ifdef SUPPORT_MC +/************************************************************************** +DELAYS- +pBiquadState->pDelays[0] to +pBiquadState->pDelays[NrChannels - 1] is x(n-1) for all NrChannels + +pBiquadState->pDelays[NrChannels] to +pBiquadState->pDelays[2*NrChannels - 1] is x(n-2) for all NrChannels + +pBiquadState->pDelays[2*NrChannels] to +pBiquadState->pDelays[3*NrChannels - 1] is y(n-1) for all NrChannels + +pBiquadState->pDelays[3*NrChannels] to +pBiquadState->pDelays[4*NrChannels - 1] is y(n-2) for all NrChannels +***************************************************************************/ + +void PK_Mc_D32F32C14G11_TRC_WRA_01 (Biquad_FLOAT_Instance_t *pInstance, + LVM_FLOAT *pDataIn, + LVM_FLOAT *pDataOut, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) + { + LVM_FLOAT yn, ynO, temp; + LVM_INT16 ii, jj; + PFilter_State_Float pBiquadState = (PFilter_State_Float) pInstance; + + for (ii = NrFrames; ii != 0; ii--) + { + + for (jj = 0; jj < NrChannels; jj++) + { + /************************************************************************** + PROCESSING OF THE jj CHANNEL + ***************************************************************************/ + /* yn= (A0 * (x(n) - x(n-2)))*/ + temp = (*pDataIn) - pBiquadState->pDelays[NrChannels + jj]; + yn = temp * pBiquadState->coefs[0]; + + /* yn+= ((-B2 * y(n-2))) */ + temp = pBiquadState->pDelays[NrChannels*3 + jj] * pBiquadState->coefs[1]; + yn += temp; + + /* yn+= ((-B1 * y(n-1))) */ + temp = pBiquadState->pDelays[NrChannels*2 + jj] * pBiquadState->coefs[2]; + yn += temp; + + /* ynO= ((Gain * yn)) */ + ynO = yn * pBiquadState->coefs[3]; + + /* ynO=(ynO + x(n))*/ + ynO += (*pDataIn); + + /************************************************************************** + UPDATING THE DELAYS + ***************************************************************************/ + pBiquadState->pDelays[NrChannels * 3 + jj] = + pBiquadState->pDelays[NrChannels * 2 + jj]; /* y(n-2)=y(n-1)*/ + pBiquadState->pDelays[NrChannels * 1 + jj] = + pBiquadState->pDelays[jj]; /* x(n-2)=x(n-1)*/ + pBiquadState->pDelays[NrChannels * 2 + jj] = yn; /* Update y(n-1) */ + pBiquadState->pDelays[jj] = (*pDataIn); /* Update x(n-1)*/ + pDataIn++; + + /************************************************************************** + WRITING THE OUTPUT + ***************************************************************************/ + *pDataOut = ynO; /* Write output*/ + pDataOut++; + } + } + + } +#endif #else void PK_2I_D32F32C14G11_TRC_WRA_01 ( Biquad_Instance_t *pInstance, LVM_INT32 *pDataIn, diff --git a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h index 8e0b7387ef..e7fdbf60da 100644 --- a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h +++ b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h @@ -267,7 +267,9 @@ typedef struct /* Equaliser parameters */ LVM_UINT16 NBands; /* Number of bands */ LVEQNB_BandDef_t *pBandDefinition; /* Pointer to equaliser definitions */ - +#ifdef SUPPORT_MC + LVM_INT16 NrChannels; +#endif } LVEQNB_Params_t; diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h index 56b02e0587..a9cd5fd313 100644 --- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h +++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Private.h @@ -46,7 +46,12 @@ extern "C" { #define LVEQNB_INSTANCE_ALIGN 4 /* 32-bit alignment for instance structures */ #define LVEQNB_DATA_ALIGN 4 /* 32-bit alignment for structures */ #define LVEQNB_COEF_ALIGN 4 /* 32-bit alignment for long words */ +#ifdef SUPPORT_MC +/* Number of buffers required for inplace processing */ +#define LVEQNB_SCRATCHBUFFERS (LVM_MAX_CHANNELS * 2) +#else #define LVEQNB_SCRATCHBUFFERS 4 /* Number of buffers required for inplace processing */ +#endif #define LVEQNB_SCRATCH_ALIGN 4 /* 32-bit alignment for long data */ #define LVEQNB_BYPASS_MIXER_TC 100 /* Bypass Mixer TC */ diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c index 6328181b3d..d188c0e1ae 100644 --- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c +++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Process.c @@ -26,6 +26,7 @@ #include "VectorArithmetic.h" #include "BIQUAD.h" +#include /****************************************************************************************/ /* */ @@ -61,14 +62,18 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, const LVM_FLOAT *pInData, LVM_FLOAT *pOutData, - LVM_UINT16 NumSamples) -{ - - LVM_UINT16 i; - Biquad_FLOAT_Instance_t *pBiquad; + const LVM_UINT16 NrFrames) +{ // updated to use samples = frames * channels. LVEQNB_Instance_t *pInstance = (LVEQNB_Instance_t *)hInstance; - LVM_FLOAT *pScratch; +#ifdef SUPPORT_MC + // Mono passed in as stereo + const LVM_INT32 NrChannels = pInstance->Params.NrChannels == 1 + ? 2 : pInstance->Params.NrChannels; +#else + const LVM_INT32 NrChannels = 2; // FCC_2 +#endif + const LVM_INT32 NrSamples = NrChannels * NrFrames; /* Check for NULL pointers */ if((hInstance == LVM_NULL) || (pInData == LVM_NULL) || (pOutData == LVM_NULL)) @@ -82,14 +87,14 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, return LVEQNB_ALIGNMENTERROR; } - pScratch = (LVM_FLOAT *)pInstance->pFastTemporary; + LVM_FLOAT * const pScratch = (LVM_FLOAT *)pInstance->pFastTemporary; /* - * Check the number of samples is not too large + * Check the number of frames is not too large */ - if (NumSamples > pInstance->Capabilities.MaxBlockSize) + if (NrFrames > pInstance->Capabilities.MaxBlockSize) { - return(LVEQNB_TOOMANYSAMPLES); + return LVEQNB_TOOMANYSAMPLES; } if (pInstance->Params.OperatingMode == LVEQNB_ON) @@ -97,16 +102,16 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, /* * Copy input data in to scratch buffer */ + Copy_Float(pInData, /* Source */ + pScratch, /* Destination */ + (LVM_INT16)NrSamples); - Copy_Float((LVM_FLOAT *)pInData, /* Source */ - pScratch, /* Destination */ - (LVM_INT16)(2 * NumSamples)); /* Left and Right */ /* * For each section execte the filter unless the gain is 0dB */ if (pInstance->NBands != 0) { - for (i = 0; i < pInstance->NBands; i++) + for (LVM_UINT16 i = 0; i < pInstance->NBands; i++) { /* * Check if band is non-zero dB gain @@ -116,7 +121,7 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, /* * Get the address of the biquad instance */ - pBiquad = &pInstance->pEQNB_FilterState_Float[i]; + Biquad_FLOAT_Instance_t *pBiquad = &pInstance->pEQNB_FilterState_Float[i]; /* @@ -126,10 +131,18 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, { case LVEQNB_SinglePrecision_Float: { +#ifdef SUPPORT_MC + PK_Mc_D32F32C14G11_TRC_WRA_01(pBiquad, + pScratch, + pScratch, + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else PK_2I_D32F32C14G11_TRC_WRA_01(pBiquad, - (LVM_FLOAT *)pScratch, - (LVM_FLOAT *)pScratch, - (LVM_INT16)NumSamples); + pScratch, + pScratch, + (LVM_INT16)NrFrames); +#endif break; } default: @@ -141,19 +154,29 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, if(pInstance->bInOperatingModeTransition == LVM_TRUE){ +#ifdef SUPPORT_MC + LVC_MixSoft_2Mc_D16C31_SAT(&pInstance->BypassMixer, + pScratch, + pInData, + pScratch, + (LVM_INT16)NrFrames, + (LVM_INT16)NrChannels); +#else LVC_MixSoft_2St_D16C31_SAT(&pInstance->BypassMixer, - (LVM_FLOAT *)pScratch, - (LVM_FLOAT *)pInData, - (LVM_FLOAT *)pScratch, - (LVM_INT16)(2 * NumSamples)); - Copy_Float((LVM_FLOAT*)pScratch, /* Source */ - pOutData, /* Destination */ - (LVM_INT16)(2 * NumSamples)); /* Left and Right samples */ + pScratch, + pInData, + pScratch, + (LVM_INT16)NrSamples); +#endif + // duplicate with else clause(s) + Copy_Float(pScratch, /* Source */ + pOutData, /* Destination */ + (LVM_INT16)NrSamples); /* All channel samples */ } else{ Copy_Float(pScratch, /* Source */ pOutData, /* Destination */ - (LVM_INT16 )(2 * NumSamples)); /* Left and Right */ + (LVM_INT16)NrSamples); /* All channel samples */ } } else @@ -163,12 +186,12 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, */ if (pInData != pOutData) { - Copy_Float(pInData, /* Source */ - pOutData, /* Destination */ - (LVM_INT16)(2 * NumSamples)); /* Left and Right samples */ + Copy_Float(pInData, /* Source */ + pOutData, /* Destination */ + (LVM_INT16)NrSamples); /* All channel samples */ } } - return(LVEQNB_SUCCESS); + return LVEQNB_SUCCESS; } #else @@ -312,4 +335,4 @@ LVEQNB_ReturnStatus_en LVEQNB_Process(LVEQNB_Handle_t hInstance, return(LVEQNB_SUCCESS); } -#endif \ No newline at end of file +#endif diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c index 566d84f9eb..1d1283ee9b 100644 --- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c +++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Process.c @@ -579,7 +579,7 @@ void ReverbBlock(LVM_FLOAT *pInput, LVM_FLOAT *pOutput, pTemp, pTemp, (LVM_INT16)NumSamples); - + /* * Process all delay lines */ diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c index a7190531b8..8c7807f6db 100644 --- a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c +++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c @@ -64,12 +64,21 @@ LVREV_ReturnStatus_en LVREV_SetControlParameters(LVREV_Handle_t hInsta ( (pNewParams->SampleRate != LVM_FS_8000) && (pNewParams->SampleRate != LVM_FS_11025) && (pNewParams->SampleRate != LVM_FS_12000) && (pNewParams->SampleRate != LVM_FS_16000) && (pNewParams->SampleRate != LVM_FS_22050) && (pNewParams->SampleRate != LVM_FS_24000) && - (pNewParams->SampleRate != LVM_FS_32000) && (pNewParams->SampleRate != LVM_FS_44100) && (pNewParams->SampleRate != LVM_FS_48000) + (pNewParams->SampleRate != LVM_FS_32000) && + (pNewParams->SampleRate != LVM_FS_44100) && + (pNewParams->SampleRate != LVM_FS_48000) #ifdef HIGHER_FS && (pNewParams->SampleRate != LVM_FS_96000) && (pNewParams->SampleRate != LVM_FS_192000) #endif ) +#ifdef SUPPORT_MC + || ((pNewParams->SourceFormat != LVM_STEREO) && + (pNewParams->SourceFormat != LVM_MONOINSTEREO) && + (pNewParams->SourceFormat != LVM_MONO) && + (pNewParams->SourceFormat != LVM_MULTICHANNEL))) +#else || ((pNewParams->SourceFormat != LVM_STEREO) && (pNewParams->SourceFormat != LVM_MONOINSTEREO) && (pNewParams->SourceFormat != LVM_MONO)) ) +#endif { return (LVREV_OUTOFRANGE); } diff --git a/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h b/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h index e75695e229..e507a7c492 100644 --- a/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h +++ b/media/libeffects/lvm/lib/StereoWidening/lib/LVCS.h @@ -205,6 +205,9 @@ typedef struct LVM_Fs_en SampleRate; /* Sampling rate */ LVM_INT16 EffectLevel; /* Effect level */ LVM_UINT16 ReverbLevel; /* Reverb level in % */ +#ifdef SUPPORT_MC + LVM_INT32 NrChannels; +#endif } LVCS_Params_t; diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h index a97e4f0684..ab8ccd1b86 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Private.h @@ -61,6 +61,15 @@ extern "C" { /* Memory */ #define LVCS_SCRATCHBUFFERS 6 /* Number of buffers required for inplace processing */ +#ifdef SUPPORT_MC +/* + * The Concert Surround module applies processing only on the first two + * channels of a multichannel input. The data of first two channels is copied + * from the multichannel input into scratch buffer. The buffers added here + * are used for this purpose + */ +#define LVCS_MC_SCRATCHBUFFERS 2 +#endif /* General */ #define LVCS_INVALID 0xFFFF /* Invalid init parameter */ diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c index 3956d4d0b3..ef1d9ebb5b 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Process.c @@ -76,6 +76,22 @@ LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance, LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance; LVM_FLOAT *pScratch; LVCS_ReturnStatus_en err; +#ifdef SUPPORT_MC + LVM_FLOAT *pStIn; + LVM_INT32 channels = pInstance->Params.NrChannels; +#define NrFrames NumSamples // alias for clarity + + /*In case of mono processing, stereo input is created from mono + *and stored in pInData before applying any of the effects. + *However we do not update the value pInstance->Params.NrChannels + *at this point. + *So to treat the pInData as stereo we are setting channels to 2 + */ + if (channels == 1) + { + channels = 2; + } +#endif pScratch = (LVM_FLOAT *) \ pInstance->MemoryTable.Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress; @@ -83,6 +99,25 @@ LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance, /* * Check if the processing is inplace */ +#ifdef SUPPORT_MC + /* + * The pInput buffer holds the first 2 (Left, Right) channels information. + * Hence the memory required by this buffer is 2 * NumFrames. + * The Concert Surround module carries out processing only on L, R. + */ + pInput = pScratch + (2 * NrFrames); + pStIn = pScratch + (LVCS_SCRATCHBUFFERS * NrFrames); + /* The first two channel data is extracted from the input data and + * copied into pInput buffer + */ + Copy_Float_Mc_Stereo((LVM_FLOAT *)pInData, + (LVM_FLOAT *)pInput, + NrFrames, + channels); + Copy_Float((LVM_FLOAT *)pInput, + (LVM_FLOAT *)pStIn, + (LVM_INT16)(2 * NrFrames)); +#else if (pInData == pOutData) { /* Processing inplace */ @@ -96,14 +131,21 @@ LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance, /* Processing outplace */ pInput = pInData; } - +#endif /* * Call the stereo enhancer */ +#ifdef SUPPORT_MC + err = LVCS_StereoEnhancer(hInstance, /* Instance handle */ + pStIn, /* Pointer to the input data */ + pOutData, /* Pointer to the output data */ + NrFrames); /* Number of frames to process */ +#else err = LVCS_StereoEnhancer(hInstance, /* Instance handle */ pInData, /* Pointer to the input data */ pOutData, /* Pointer to the output data */ NumSamples); /* Number of samples to process */ +#endif /* * Call the reverb generator @@ -112,7 +154,7 @@ LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance, pOutData, /* Pointer to the input data */ pOutData, /* Pointer to the output data */ NumSamples); /* Number of samples to process */ - + /* * Call the equaliser */ @@ -239,7 +281,15 @@ LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance, LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance; LVCS_ReturnStatus_en err; - +#ifdef SUPPORT_MC + /*Extract number of Channels info*/ + LVM_INT32 channels = pInstance->Params.NrChannels; +#define NrFrames NumSamples // alias for clarity + if (channels == 1) + { + channels = 2; + } +#endif /* * Check the number of samples is not too large */ @@ -260,8 +310,8 @@ LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance, pInData, pOutData, NumSamples); - - + + /* * Compress to reduce expansion effect of Concert Sound and correct volume * differences for difference settings. Not applied in test modes @@ -376,17 +426,32 @@ LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance, (LVM_INT16)NumSamples); } } +#ifdef SUPPORT_MC + Copy_Float_Stereo_Mc(pInData, + pOutData, + NrFrames, + channels); +#endif } else { if (pInData != pOutData) { +#ifdef SUPPORT_MC + /* + * The algorithm is disabled so just copy the data + */ + Copy_Float((LVM_FLOAT *)pInData, /* Source */ + (LVM_FLOAT *)pOutData, /* Destination */ + (LVM_INT16)(channels * NrFrames)); /* All Channels*/ +#else /* * The algorithm is disabled so just copy the data */ Copy_Float((LVM_FLOAT *)pInData, /* Source */ (LVM_FLOAT *)pOutData, /* Destination */ (LVM_INT16)(2 * NumSamples)); /* Left and right */ +#endif } } diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c index e154e29ac9..07657647ce 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c @@ -515,16 +515,16 @@ const LVCS_VolCorrect_t LVCS_VolCorrectTable[] = { #if defined(BUILD_FLOAT) && defined(HIGHER_FS) const LVM_INT16 LVCS_VolumeTCTable[11] = {LVCS_VOL_TC_Fs8000, - LVCS_VOL_TC_Fs11025, - LVCS_VOL_TC_Fs12000, - LVCS_VOL_TC_Fs16000, - LVCS_VOL_TC_Fs22050, - LVCS_VOL_TC_Fs24000, - LVCS_VOL_TC_Fs32000, - LVCS_VOL_TC_Fs44100, - LVCS_VOL_TC_Fs48000, - LVCS_VOL_TC_Fs96000, - LVCS_VOL_TC_Fs192000 + LVCS_VOL_TC_Fs11025, + LVCS_VOL_TC_Fs12000, + LVCS_VOL_TC_Fs16000, + LVCS_VOL_TC_Fs22050, + LVCS_VOL_TC_Fs24000, + LVCS_VOL_TC_Fs32000, + LVCS_VOL_TC_Fs44100, + LVCS_VOL_TC_Fs48000, + LVCS_VOL_TC_Fs96000, + LVCS_VOL_TC_Fs192000 }; #else const LVM_INT16 LVCS_VolumeTCTable[9] = {LVCS_VOL_TC_Fs8000, @@ -546,16 +546,16 @@ const LVM_INT16 LVCS_VolumeTCTable[9] = {LVCS_VOL_TC_Fs8000, /************************************************************************************/ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) const LVM_INT32 LVCS_SampleRateTable[11] = {8000, - 11025, - 12000, - 16000, - 22050, - 24000, - 32000, - 44100, - 48000, - 96000, - 192000 + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 96000, + 192000 }; #else const LVM_INT16 LVCS_SampleRateTable[9] = {8000, diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp new file mode 100644 index 0000000000..8ee807c317 --- /dev/null +++ b/media/libeffects/lvm/tests/Android.bp @@ -0,0 +1,46 @@ +// Build the unit tests for effects + +cc_test { + name: "lvmtest", + host_supported: false, + proprietary: true, + + include_dirs: [ + "frameworks/av/media/libeffects/lvm/lib/Bass/lib", + "frameworks/av/media/libeffects/lvm/lib/Bass/src", + "frameworks/av/media/libeffects/lvm/lib/Bundle/src", + "frameworks/av/media/libeffects/lvm/lib/Common/src", + "frameworks/av/media/libeffects/lvm/lib/Eq/lib", + "frameworks/av/media/libeffects/lvm/lib/Eq/src", + "frameworks/av/media/libeffects/lvm/lib/SpectrumAnalyzer/lib", + "frameworks/av/media/libeffects/lvm/lib/SpectrumAnalyzer/src", + "frameworks/av/media/libeffects/lvm/lib/StereoWidening/lib", + "frameworks/av/media/libeffects/lvm/lib/StereoWidening/src", + "frameworks/av/media/libeffects/lvm/wrapper/Bundle", + ], + + header_libs: [ + "libaudioeffects", + ], + + shared_libs: [ + "libaudioutils", + "liblog", + ], + + static_libs: [ + "libmusicbundle", + ], + + srcs: ["lvmtest.cpp"], + + cflags: [ + "-DBUILD_FLOAT", + "-DHIGHER_FS", + "-DSUPPORT_MC", + + "-Wall", + "-Werror", + "-Wextra", + ], +} diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000000..340469a2bb --- /dev/null +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# +# Run tests in this directory. +# + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +mm -j + +echo "waiting for device" + +adb root && adb wait-for-device remount + +# location of test files +testdir="/data/local/tmp/lvmTest" + +#flags="-bE -tE -eqE -csE" +flags="-csE -tE -eqE" + + +echo "========================================" +echo "testing lvm" +adb shell mkdir $testdir +adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir +adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir + +# run multichannel effects at different channel counts, saving only the stereo channel pair. +adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_1.raw\ + -ch:1 -fs:44100 $flags +adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_2.raw\ + -ch:2 -fs:44100 $flags +adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_4.raw\ + -ch:4 -fs:44100 $flags +adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_6.raw\ + -ch:6 -fs:44100 $flags +adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_8.raw\ + -ch:8 -fs:44100 $flags + +# two channel files should be identical to higher channel computation (first 2 channels). +adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_2.raw +adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_4.raw +adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_6.raw +adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_8.raw diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp new file mode 100644 index 0000000000..01c5955234 --- /dev/null +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -0,0 +1,682 @@ +/* + * 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. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "EffectBundle.h" +#include "LVM_Private.h" + +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) \ + do { \ + } while (false) +#endif + +#define CHECK_ARG(cond) \ + { \ + if (!(cond)) { \ + ALOGE("\tLVM_ERROR : Invalid argument: " #cond); \ + return -EINVAL; \ + } \ + \ +} + +#define LVM_ERROR_CHECK(LvmStatus, callingFunc, calledFunc) \ + { \ + if ((LvmStatus) == LVM_NULLADDRESS) { \ + ALOGE( \ + "\tLVM_ERROR : Parameter error - " \ + "null pointer returned by %s in %s\n\n\n\n", \ + callingFunc, calledFunc); \ + } \ + if ((LvmStatus) == LVM_ALIGNMENTERROR) { \ + ALOGE( \ + "\tLVM_ERROR : Parameter error - " \ + "bad alignment returned by %s in %s\n\n\n\n", \ + callingFunc, calledFunc); \ + } \ + if ((LvmStatus) == LVM_INVALIDNUMSAMPLES) { \ + ALOGE( \ + "\tLVM_ERROR : Parameter error - " \ + "bad number of samples returned by %s in %s\n\n\n\n", \ + callingFunc, calledFunc); \ + } \ + if ((LvmStatus) == LVM_OUTOFRANGE) { \ + ALOGE( \ + "\tLVM_ERROR : Parameter error - " \ + "out of range returned by %s in %s\n", \ + callingFunc, calledFunc); \ + } \ + } + +struct lvmConfigParams_t { + int samplingFreq = 44100; + int nrChannels = 2; + int fChannels = 2; + int bassEffectLevel = 0; + int eqPresetLevel = 0; + int frameLength = 256; + LVM_BE_Mode_en bassEnable = LVM_BE_OFF; + LVM_TE_Mode_en trebleEnable = LVM_TE_OFF; + LVM_EQNB_Mode_en eqEnable = LVM_EQNB_OFF; + LVM_Mode_en csEnable = LVM_MODE_OFF; +}; + +void printUsage() { + printf("\nUsage: "); + printf("\n -i: -o: [options]\n"); + printf("\nwhere, \n is the input file name"); + printf("\n on which LVM effects are applied"); + printf("\n processed output file"); + printf("\n and options are mentioned below"); + printf("\n"); + printf("\n -help (or) -h"); + printf("\n Prints this usage information"); + printf("\n"); + printf("\n -ch: (1 through 8)\n\n"); + printf("\n -fch: (1 through 8)\n\n"); + printf("\n -basslvl:"); + printf("\n A value that ranges between 0 - 15 default 0"); + printf("\n"); + printf("\n -eqPreset:"); + printf("\n 0 - Normal"); + printf("\n 1 - Classical"); + printf("\n 2 - Dance"); + printf("\n 3 - Flat"); + printf("\n 4 - Folk"); + printf("\n 5 - Heavy Metal"); + printf("\n 6 - Hip Hop"); + printf("\n 7 - Jazz"); + printf("\n 8 - Pop"); + printf("\n 9 - Rock"); + printf("\n default 0"); + printf("\n -bE "); + printf("\n Enable Dynamic Bass Enhancement"); + printf("\n"); + printf("\n -tE "); + printf("\n Enable Treble Boost"); + printf("\n"); + printf("\n -csE "); + printf("\n Enable Concert Surround"); + printf("\n"); + printf("\n -eqE "); + printf("\n Enable Equalizer"); +} + +//---------------------------------------------------------------------------- +// LvmEffect_free() +//---------------------------------------------------------------------------- +// Purpose: Free all memory associated with the Bundle. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- + +void LvmEffect_free(struct EffectContext *pContext) { + LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ + LVM_MemTab_t MemTab; + + /* Free the algorithm memory */ + LvmStatus = LVM_GetMemoryTable(pContext->pBundledContext->hInstance, &MemTab, + LVM_NULL); + + LVM_ERROR_CHECK(LvmStatus, "LVM_GetMemoryTable", "LvmEffect_free") + + for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { + if (MemTab.Region[i].Size != 0) { + if (MemTab.Region[i].pBaseAddress != NULL) { + ALOGV("\tLvmEffect_free - START freeing %" PRIu32 + " bytes for region %u at %p\n", + MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress); + + free(MemTab.Region[i].pBaseAddress); + + ALOGV("\tLvmEffect_free - END freeing %" PRIu32 + " bytes for region %u at %p\n", + MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress); + } else { + ALOGE( + "\tLVM_ERROR : LvmEffect_free - trying to free with NULL pointer " + "%" PRIu32 " bytes for region %u at %p ERROR\n", + MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress); + } + } + } +} /* end LvmEffect_free */ + +//---------------------------------------------------------------------------- +// LvmBundle_init() +//---------------------------------------------------------------------------- +// Purpose: Initialize engine with default configuration, creates instance +// with all effects disabled. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int LvmBundle_init(struct EffectContext *pContext, LVM_ControlParams_t *params) { + ALOGV("\tLvmBundle_init start"); + + pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pContext->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + pContext->config.inputCfg.format = EFFECT_BUFFER_FORMAT; + pContext->config.inputCfg.samplingRate = 44100; + pContext->config.inputCfg.bufferProvider.getBuffer = NULL; + pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL; + pContext->config.inputCfg.bufferProvider.cookie = NULL; + pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL; + pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + pContext->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + pContext->config.outputCfg.format = EFFECT_BUFFER_FORMAT; + pContext->config.outputCfg.samplingRate = 44100; + pContext->config.outputCfg.bufferProvider.getBuffer = NULL; + pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL; + pContext->config.outputCfg.bufferProvider.cookie = NULL; + pContext->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + if (pContext->pBundledContext->hInstance != NULL) { + ALOGV( + "\tLvmBundle_init pContext->pBassBoost != NULL " + "-> Calling pContext->pBassBoost->free()"); + + LvmEffect_free(pContext); + + ALOGV( + "\tLvmBundle_init pContext->pBassBoost != NULL " + "-> Called pContext->pBassBoost->free()"); + } + + LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ + LVM_InstParams_t InstParams; /* Instance parameters */ + LVM_EQNB_BandDef_t BandDefs[MAX_NUM_BANDS]; /* Equaliser band definitions */ + LVM_HeadroomParams_t HeadroomParams; /* Headroom parameters */ + LVM_HeadroomBandDef_t HeadroomBandDef[LVM_HEADROOM_MAX_NBANDS]; + LVM_MemTab_t MemTab; /* Memory allocation table */ + bool bMallocFailure = LVM_FALSE; + + /* Set the capabilities */ + InstParams.BufferMode = LVM_UNMANAGED_BUFFERS; + InstParams.MaxBlockSize = MAX_CALL_SIZE; + InstParams.EQNB_NumBands = MAX_NUM_BANDS; + InstParams.PSA_Included = LVM_PSA_ON; + + /* Allocate memory, forcing alignment */ + LvmStatus = LVM_GetMemoryTable(LVM_NULL, &MemTab, &InstParams); + + LVM_ERROR_CHECK(LvmStatus, "LVM_GetMemoryTable", "LvmBundle_init"); + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + ALOGV("\tCreateInstance Succesfully called LVM_GetMemoryTable\n"); + + /* Allocate memory */ + for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { + if (MemTab.Region[i].Size != 0) { + MemTab.Region[i].pBaseAddress = malloc(MemTab.Region[i].Size); + + if (MemTab.Region[i].pBaseAddress == LVM_NULL) { + ALOGE( + "\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate " + "%" PRIu32 " bytes for region %u\n", + MemTab.Region[i].Size, i); + bMallocFailure = LVM_TRUE; + break; + } else { + ALOGV("\tLvmBundle_init CreateInstance allocated %" PRIu32 + " bytes for region %u at %p\n", + MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress); + } + } + } + + /* If one or more of the memory regions failed to allocate, free the regions + * that were + * succesfully allocated and return with an error + */ + if (bMallocFailure == LVM_TRUE) { + for (int i = 0; i < LVM_NR_MEMORY_REGIONS; i++) { + if (MemTab.Region[i].pBaseAddress == LVM_NULL) { + ALOGE( + "\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate " + "%" PRIu32 " bytes for region %u Not freeing\n", + MemTab.Region[i].Size, i); + } else { + ALOGE( + "\tLVM_ERROR :LvmBundle_init CreateInstance Failed: but allocated " + "%" PRIu32 " bytes for region %u at %p- free\n", + MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress); + free(MemTab.Region[i].pBaseAddress); + } + } + return -EINVAL; + } + ALOGV("\tLvmBundle_init CreateInstance Succesfully malloc'd memory\n"); + + /* Initialise */ + pContext->pBundledContext->hInstance = LVM_NULL; + + /* Init sets the instance handle */ + LvmStatus = LVM_GetInstanceHandle(&pContext->pBundledContext->hInstance, + &MemTab, &InstParams); + + LVM_ERROR_CHECK(LvmStatus, "LVM_GetInstanceHandle", "LvmBundle_init"); + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + ALOGV( + "\tLvmBundle_init CreateInstance Succesfully called " + "LVM_GetInstanceHandle\n"); + + /* Set the initial process parameters */ + /* General parameters */ + params->OperatingMode = LVM_MODE_ON; + params->SampleRate = LVM_FS_44100; + params->SourceFormat = LVM_STEREO; + params->SpeakerType = LVM_HEADPHONES; + + pContext->pBundledContext->SampleRate = LVM_FS_44100; + + /* Concert Sound parameters */ + params->VirtualizerOperatingMode = LVM_MODE_OFF; + params->VirtualizerType = LVM_CONCERTSOUND; + params->VirtualizerReverbLevel = 100; + params->CS_EffectLevel = LVM_CS_EFFECT_NONE; + + /* N-Band Equaliser parameters */ + params->EQNB_OperatingMode = LVM_EQNB_ON; + params->EQNB_NBands = FIVEBAND_NUMBANDS; + params->pEQNB_BandDefinition = &BandDefs[0]; + + for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { + BandDefs[i].Frequency = EQNB_5BandPresetsFrequencies[i]; + BandDefs[i].QFactor = EQNB_5BandPresetsQFactors[i]; + BandDefs[i].Gain = EQNB_5BandSoftPresets[i]; + } + + /* Volume Control parameters */ + params->VC_EffectLevel = 0; + params->VC_Balance = 0; + + /* Treble Enhancement parameters */ + params->TE_OperatingMode = LVM_TE_OFF; + params->TE_EffectLevel = 0; + + /* PSA Control parameters */ + params->PSA_Enable = LVM_PSA_OFF; + params->PSA_PeakDecayRate = (LVM_PSA_DecaySpeed_en)0; + + /* Bass Enhancement parameters */ + params->BE_OperatingMode = LVM_BE_ON; + params->BE_EffectLevel = 0; + params->BE_CentreFreq = LVM_BE_CENTRE_90Hz; + params->BE_HPF = LVM_BE_HPF_ON; + + /* PSA Control parameters */ + params->PSA_Enable = LVM_PSA_OFF; + params->PSA_PeakDecayRate = LVM_PSA_SPEED_MEDIUM; + + /* TE Control parameters */ + params->TE_OperatingMode = LVM_TE_OFF; + params->TE_EffectLevel = 0; + + /* Activate the initial settings */ + LvmStatus = + LVM_SetControlParameters(pContext->pBundledContext->hInstance, params); + + LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "LvmBundle_init"); + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + ALOGV( + "\tLvmBundle_init CreateInstance Succesfully called " + "LVM_SetControlParameters\n"); + + /* Set the headroom parameters */ + HeadroomBandDef[0].Limit_Low = 20; + HeadroomBandDef[0].Limit_High = 4999; + HeadroomBandDef[0].Headroom_Offset = 0; + HeadroomBandDef[1].Limit_Low = 5000; + HeadroomBandDef[1].Limit_High = 24000; + HeadroomBandDef[1].Headroom_Offset = 0; + HeadroomParams.pHeadroomDefinition = &HeadroomBandDef[0]; + HeadroomParams.Headroom_OperatingMode = LVM_HEADROOM_ON; + HeadroomParams.NHeadroomBands = 2; + + LvmStatus = LVM_SetHeadroomParams(pContext->pBundledContext->hInstance, + &HeadroomParams); + + LVM_ERROR_CHECK(LvmStatus, "LVM_SetHeadroomParams", "LvmBundle_init"); + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + ALOGV( + "\tLvmBundle_init CreateInstance Succesfully called " + "LVM_SetHeadroomParams\n"); + ALOGV("\tLvmBundle_init End"); + return 0; +} /* end LvmBundle_init */ + +int lvmCreate(struct EffectContext *pContext, + lvmConfigParams_t *plvmConfigParams, + LVM_ControlParams_t *params) { + int ret = 0; + pContext->pBundledContext = NULL; + pContext->pBundledContext = (BundledEffectContext *)malloc(sizeof(struct BundledEffectContext)); + if (NULL == pContext->pBundledContext) { + return -EINVAL; + } + + pContext->pBundledContext->SessionNo = 0; + pContext->pBundledContext->SessionId = 0; + pContext->pBundledContext->hInstance = NULL; + pContext->pBundledContext->bVolumeEnabled = LVM_FALSE; + pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE; + pContext->pBundledContext->bBassEnabled = LVM_FALSE; + pContext->pBundledContext->bBassTempDisabled = LVM_FALSE; + pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE; + pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE; + pContext->pBundledContext->nOutputDevice = AUDIO_DEVICE_NONE; + pContext->pBundledContext->nVirtualizerForcedDevice = AUDIO_DEVICE_NONE; + pContext->pBundledContext->NumberEffectsEnabled = 0; + pContext->pBundledContext->NumberEffectsCalled = 0; + pContext->pBundledContext->firstVolume = LVM_TRUE; + pContext->pBundledContext->volume = 0; + + /* Saved strength is used to return the exact strength that was used in the + * set to the get + * because we map the original strength range of 0:1000 to 1:15, and this will + * avoid + * quantisation like effect when returning + */ + pContext->pBundledContext->BassStrengthSaved = 0; + pContext->pBundledContext->VirtStrengthSaved = 0; + pContext->pBundledContext->CurPreset = PRESET_CUSTOM; + pContext->pBundledContext->levelSaved = 0; + pContext->pBundledContext->bMuteEnabled = LVM_FALSE; + pContext->pBundledContext->bStereoPositionEnabled = LVM_FALSE; + pContext->pBundledContext->positionSaved = 0; + pContext->pBundledContext->workBuffer = NULL; + pContext->pBundledContext->frameCount = -1; + pContext->pBundledContext->SamplesToExitCountVirt = 0; + pContext->pBundledContext->SamplesToExitCountBb = 0; + pContext->pBundledContext->SamplesToExitCountEq = 0; +#if defined(BUILD_FLOAT) && !defined(NATIVE_FLOAT_BUFFER) + pContext->pBundledContext->pInputBuffer = NULL; + pContext->pBundledContext->pOutputBuffer = NULL; +#endif + for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { + pContext->pBundledContext->bandGaindB[i] = EQNB_5BandSoftPresets[i]; + } + pContext->config.inputCfg.channels = plvmConfigParams->nrChannels; + ALOGV("\tEffectCreate - Calling LvmBundle_init"); + ret = LvmBundle_init(pContext, params); + + if (ret < 0) { + ALOGE("\tLVM_ERROR : lvmCreate() Bundle init failed"); + return ret; + } + return 0; +} + +int lvmControl(struct EffectContext *pContext, + lvmConfigParams_t *plvmConfigParams, + LVM_ControlParams_t *params) { + LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ + LVM_EQNB_BandDef_t BandDefs[MAX_NUM_BANDS]; /* Equaliser band definitions */ + int eqPresetLevel = plvmConfigParams->eqPresetLevel; + int nrChannels = plvmConfigParams->nrChannels; + params->NrChannels = nrChannels; + + /* Set the initial process parameters */ + /* General parameters */ + params->OperatingMode = LVM_MODE_ON; + params->SampleRate = LVM_FS_44100; + params->SourceFormat = LVM_STEREO; + params->SpeakerType = LVM_HEADPHONES; + + pContext->pBundledContext->SampleRate = LVM_FS_44100; + + /* Concert Sound parameters */ + params->VirtualizerOperatingMode = plvmConfigParams->csEnable; + params->VirtualizerType = LVM_CONCERTSOUND; + params->VirtualizerReverbLevel = 100; + params->CS_EffectLevel = LVM_CS_EFFECT_NONE; + + /* N-Band Equaliser parameters */ + params->EQNB_OperatingMode = plvmConfigParams->eqEnable; + params->pEQNB_BandDefinition = &BandDefs[0]; + for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { + BandDefs[i].Frequency = EQNB_5BandPresetsFrequencies[i]; + BandDefs[i].QFactor = EQNB_5BandPresetsQFactors[i]; + BandDefs[i].Gain = + EQNB_5BandSoftPresets[(FIVEBAND_NUMBANDS * eqPresetLevel) + i]; + } + + /* Volume Control parameters */ + params->VC_EffectLevel = 0; + params->VC_Balance = 0; + + /* Treble Enhancement parameters */ + params->TE_OperatingMode = plvmConfigParams->trebleEnable; + + /* PSA Control parameters */ + params->PSA_Enable = LVM_PSA_ON; + + /* Bass Enhancement parameters */ + params->BE_OperatingMode = plvmConfigParams->bassEnable; + + if (nrChannels == 1) { + params->SourceFormat = LVM_MONO; + } + if (nrChannels == 2) { + params->SourceFormat = LVM_STEREO; + } + if ((nrChannels > 2) && (nrChannels <= 8)) { + params->SourceFormat = LVM_MULTICHANNEL; + } + + /* Activate the initial settings */ + LvmStatus = + LVM_SetControlParameters(pContext->pBundledContext->hInstance, params); + + LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "LvmBundle_init"); + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + LvmStatus = LVM_ApplyNewSettings(pContext->pBundledContext->hInstance); + + if (LvmStatus != LVM_SUCCESS) return -EINVAL; + + return 0; +} + +int lvmExecute(float *floatIn, float *floatOut, struct EffectContext *pContext, + lvmConfigParams_t *plvmConfigParams) { + const int frameLength = plvmConfigParams->frameLength; + return + LVM_Process(pContext->pBundledContext->hInstance, /* Instance handle */ + floatIn, /* Input buffer */ + floatOut, /* Output buffer */ + (LVM_UINT16)frameLength, /* Number of samples to read */ + 0); /* Audio Time */ +} + +int lvmMainProcess(lvmConfigParams_t *plvmConfigParams, FILE *finp, FILE *fout) { + struct EffectContext context; + LVM_ControlParams_t params; + + int errCode = lvmCreate(&context, plvmConfigParams, ¶ms); + if (errCode) { + ALOGE("Error: lvmCreate returned with %d\n", errCode); + return errCode; + } + + errCode = lvmControl(&context, plvmConfigParams, ¶ms); + if (errCode) { + ALOGE("Error: lvmControl returned with %d\n", errCode); + return errCode; + } + + const int channelCount = plvmConfigParams->nrChannels; + const int frameLength = plvmConfigParams->frameLength; + const int frameSize = channelCount * sizeof(float); // processing size + const int ioChannelCount = plvmConfigParams->fChannels; + const int ioFrameSize = ioChannelCount * sizeof(short); // file load size + const int maxChannelCount = std::max(channelCount, ioChannelCount); + /* + * Mono input will be converted to 2 channels internally in the process call + * by copying the same data into the second channel. + * Hence when channelCount is 1, output buffer should be allocated for + * 2 channels. The memAllocChCount takes care of allocation of sufficient + * memory for the output buffer. + */ + const int memAllocChCount = (channelCount == 1 ? 2 : channelCount); + + std::vector in(frameLength * maxChannelCount); + std::vector out(frameLength * maxChannelCount); + std::vector floatIn(frameLength * channelCount); + std::vector floatOut(frameLength * memAllocChCount); + + int frameCounter = 0; + while (fread(in.data(), ioFrameSize, frameLength, finp) == (size_t)frameLength) { + if (ioChannelCount != channelCount) { + adjust_channels(in.data(), ioChannelCount, in.data(), channelCount, + sizeof(short), frameLength * ioFrameSize); + } + memcpy_to_float_from_i16(floatIn.data(), in.data(), frameLength * channelCount); + +#if 1 + errCode = lvmExecute(floatIn.data(), floatOut.data(), &context, plvmConfigParams); + if (errCode) { + printf("\nError: lvmExecute returned with %d\n", errCode); + return errCode; + } + + (void)frameSize; // eliminate warning +#else + memcpy(floatOut.data(), floatIn.data(), frameLength * frameSize); +#endif + memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * channelCount); + if (ioChannelCount != channelCount) { + adjust_channels(out.data(), channelCount, out.data(), ioChannelCount, + sizeof(short), frameLength * channelCount * sizeof(short)); + } + (void) fwrite(out.data(), ioFrameSize, frameLength, fout); + frameCounter += frameLength; + } + printf("frameCounter: [%d]\n", frameCounter); + return 0; +} + +int main(int argc, const char *argv[]) { + if (argc == 1) { + printUsage(); + return -1; + } + + lvmConfigParams_t lvmConfigParams{}; // default initialize + FILE *finp = nullptr, *fout = nullptr; + + for (int i = 1; i < argc; i++) { + printf("%s ", argv[i]); + if (!strncmp(argv[i], "-i:", 3)) { + finp = fopen(argv[i] + 3, "rb"); + } else if (!strncmp(argv[i], "-o:", 3)) { + fout = fopen(argv[i] + 3, "wb"); + } else if (!strncmp(argv[i], "-fs:", 4)) { + const int samplingFreq = atoi(argv[i] + 4); + if (samplingFreq != 8000 && samplingFreq != 11025 && + samplingFreq != 12000 && samplingFreq != 16000 && + samplingFreq != 22050 && samplingFreq != 24000 && + samplingFreq != 32000 && samplingFreq != 44100 && + samplingFreq != 48000 && samplingFreq != 96000) { + ALOGE("\nError: Unsupported Sampling Frequency : %d\n", samplingFreq); + return -1; + } + lvmConfigParams.samplingFreq = samplingFreq; + } else if (!strncmp(argv[i], "-ch:", 4)) { + const int nrChannels = atoi(argv[i] + 4); + if (nrChannels > 8 || nrChannels < 1) { + ALOGE("\nError: Unsupported number of channels : %d\n", nrChannels); + return -1; + } + lvmConfigParams.nrChannels = nrChannels; + } else if (!strncmp(argv[i], "-fch:", 5)) { + const int fChannels = atoi(argv[i] + 5); + if (fChannels > 8 || fChannels < 1) { + ALOGE("\nError: Unsupported number of file channels : %d\n", fChannels); + return -1; + } + lvmConfigParams.fChannels = fChannels; + } else if (!strncmp(argv[i], "-basslvl:", 9)) { + const int bassEffectLevel = atoi(argv[i] + 9); + if (bassEffectLevel > 15 || bassEffectLevel < 0) { + ALOGE("\nError: Unsupported Bass Effect Level : %d\n", + bassEffectLevel); + printUsage(); + return -1; + } + lvmConfigParams.bassEffectLevel = bassEffectLevel; + } else if (!strncmp(argv[i], "-eqPreset:", 10)) { + const int eqPresetLevel = atoi(argv[i] + 10); + if (eqPresetLevel > 9 || eqPresetLevel < 0) { + ALOGE("\nError: Unsupported Equalizer Preset : %d\n", eqPresetLevel); + printUsage(); + return -1; + } + lvmConfigParams.eqPresetLevel = eqPresetLevel; + } else if (!strcmp(argv[i], "-bE")) { + lvmConfigParams.bassEnable = LVM_BE_ON; + } else if (!strcmp(argv[i], "-eqE")) { + lvmConfigParams.eqEnable = LVM_EQNB_ON; + } else if (!strcmp(argv[i], "-tE")) { + lvmConfigParams.trebleEnable = LVM_TE_ON; + } else if (!strcmp(argv[i], "-csE")) { + lvmConfigParams.csEnable = LVM_MODE_ON; + } else if (!strcmp(argv[i], "-h")) { + printUsage(); + return 0; + } + } + + if (finp == nullptr || fout == nullptr) { + ALOGE("\nError: missing input/output files\n"); + printUsage(); + // ok not to close. + return -1; + } + + const int errCode = lvmMainProcess(&lvmConfigParams, finp, fout); + fclose(finp); + fclose(fout); + + if (errCode) { + ALOGE("Error: lvmMainProcess returns with the error: %d \n", errCode); + return -1; + } + return 0; +} diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp index 10fd970d61..16fa1262fc 100644 --- a/media/libeffects/lvm/wrapper/Android.bp +++ b/media/libeffects/lvm/wrapper/Android.bp @@ -18,6 +18,7 @@ cc_library_shared { "-fvisibility=hidden", "-DBUILD_FLOAT", "-DHIGHER_FS", + "-DSUPPORT_MC", "-Wall", "-Werror", diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 53d266a51b..09e99645a9 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -435,7 +435,7 @@ extern "C" int EffectRelease(effect_handle_t handle){ (pSessionContext->bEqualizerInstantiated ==LVM_FALSE) && (pSessionContext->bVirtualizerInstantiated==LVM_FALSE)) { - #ifdef LVM_PCM +#ifdef LVM_PCM if (pContext->pBundledContext->PcmInPtr != NULL) { fclose(pContext->pBundledContext->PcmInPtr); pContext->pBundledContext->PcmInPtr = NULL; @@ -444,7 +444,7 @@ extern "C" int EffectRelease(effect_handle_t handle){ fclose(pContext->pBundledContext->PcmOutPtr); pContext->pBundledContext->PcmOutPtr = NULL; } - #endif +#endif // Clear the SessionIndex @@ -751,19 +751,21 @@ int LvmBundle_process(effect_buffer_t *pIn, LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ effect_buffer_t *pOutTmp; + const LVM_INT32 NrChannels = + audio_channel_count_from_out_mask(pContext->config.inputCfg.channels); #ifndef NATIVE_FLOAT_BUFFER if (pContext->pBundledContext->pInputBuffer == nullptr || pContext->pBundledContext->frameCount < frameCount) { free(pContext->pBundledContext->pInputBuffer); pContext->pBundledContext->pInputBuffer = - (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * FCC_2); + (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * NrChannels); } if (pContext->pBundledContext->pOutputBuffer == nullptr || pContext->pBundledContext->frameCount < frameCount) { free(pContext->pBundledContext->pOutputBuffer); pContext->pBundledContext->pOutputBuffer = - (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * FCC_2); + (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * NrChannels); } if (pContext->pBundledContext->pInputBuffer == nullptr || @@ -784,7 +786,7 @@ int LvmBundle_process(effect_buffer_t *pIn, free(pContext->pBundledContext->workBuffer); } pContext->pBundledContext->workBuffer = - (effect_buffer_t *)calloc(frameCount, sizeof(effect_buffer_t) * FCC_2); + (effect_buffer_t *)calloc(frameCount, sizeof(effect_buffer_t) * NrChannels); if (pContext->pBundledContext->workBuffer == NULL) { return -ENOMEM; } @@ -798,13 +800,15 @@ int LvmBundle_process(effect_buffer_t *pIn, #ifdef LVM_PCM fwrite(pIn, - frameCount*sizeof(effect_buffer_t) * FCC_2, 1, pContext->pBundledContext->PcmInPtr); + frameCount * sizeof(effect_buffer_t) * NrChannels, + 1, + pContext->pBundledContext->PcmInPtr); fflush(pContext->pBundledContext->PcmInPtr); #endif #ifndef NATIVE_FLOAT_BUFFER /* Converting input data from fixed point to float point */ - memcpy_to_float_from_i16(pInputBuff, pIn, frameCount * FCC_2); + memcpy_to_float_from_i16(pInputBuff, pIn, frameCount * NrChannels); /* Process the samples */ LvmStatus = LVM_Process(pContext->pBundledContext->hInstance, /* Instance handle */ @@ -814,7 +818,7 @@ int LvmBundle_process(effect_buffer_t *pIn, 0); /* Audio Time */ /* Converting output data from float point to fixed point */ - memcpy_to_i16_from_float(pOutTmp, pOutputBuff, frameCount * FCC_2); + memcpy_to_i16_from_float(pOutTmp, pOutputBuff, frameCount * NrChannels); #else /* Process the samples */ @@ -829,12 +833,14 @@ int LvmBundle_process(effect_buffer_t *pIn, #ifdef LVM_PCM fwrite(pOutTmp, - frameCount*sizeof(effect_buffer_t) * FCC_2, 1, pContext->pBundledContext->PcmOutPtr); + frameCount * sizeof(effect_buffer_t) * NrChannels, + 1, + pContext->pBundledContext->PcmOutPtr); fflush(pContext->pBundledContext->PcmOutPtr); #endif if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){ - for (int i = 0; i < frameCount * FCC_2; i++) { + for (int i = 0; i < frameCount * NrChannels; i++) { #ifndef NATIVE_FLOAT_BUFFER pOut[i] = clamp16((LVM_INT32)pOut[i] + (LVM_INT32)pOutTmp[i]); #else @@ -1232,45 +1238,50 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate); CHECK_ARG(pConfig->inputCfg.channels == pConfig->outputCfg.channels); CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format); +#ifdef SUPPORT_MC + CHECK_ARG(audio_channel_count_from_out_mask(pConfig->inputCfg.channels) <= LVM_MAX_CHANNELS); +#else CHECK_ARG(pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_STEREO); +#endif CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); CHECK_ARG(pConfig->inputCfg.format == EFFECT_BUFFER_FORMAT); pContext->config = *pConfig; + const LVM_INT16 NrChannels = audio_channel_count_from_out_mask(pConfig->inputCfg.channels); switch (pConfig->inputCfg.samplingRate) { case 8000: SampleRate = LVM_FS_8000; - pContext->pBundledContext->SamplesPerSecond = 8000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 8000 * NrChannels; break; case 16000: SampleRate = LVM_FS_16000; - pContext->pBundledContext->SamplesPerSecond = 16000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 16000 * NrChannels; break; case 22050: SampleRate = LVM_FS_22050; - pContext->pBundledContext->SamplesPerSecond = 22050*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 22050 * NrChannels; break; case 32000: SampleRate = LVM_FS_32000; - pContext->pBundledContext->SamplesPerSecond = 32000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 32000 * NrChannels; break; case 44100: SampleRate = LVM_FS_44100; - pContext->pBundledContext->SamplesPerSecond = 44100*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 44100 * NrChannels; break; case 48000: SampleRate = LVM_FS_48000; - pContext->pBundledContext->SamplesPerSecond = 48000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 48000 * NrChannels; break; #if defined(BUILD_FLOAT) && defined(HIGHER_FS) case 96000: SampleRate = LVM_FS_96000; - pContext->pBundledContext->SamplesPerSecond = 96000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 96000 * NrChannels; break; case 192000: SampleRate = LVM_FS_192000; - pContext->pBundledContext->SamplesPerSecond = 192000*2; // 2 secs Stereo + pContext->pBundledContext->SamplesPerSecond = 192000 * NrChannels; break; #endif default: @@ -1294,6 +1305,10 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ ActiveParams.SampleRate = SampleRate; +#ifdef SUPPORT_MC + ActiveParams.NrChannels = NrChannels; +#endif + LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "Effect_setConfig") @@ -1498,6 +1513,7 @@ int VirtualizerIsDeviceSupported(audio_devices_t deviceType) { case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: case AUDIO_DEVICE_OUT_USB_HEADSET: + // case AUDIO_DEVICE_OUT_USB_DEVICE: // For USB testing of the virtualizer only. return 0; default : return -EINVAL; @@ -1520,10 +1536,9 @@ int VirtualizerIsDeviceSupported(audio_devices_t deviceType) { int VirtualizerIsConfigurationSupported(audio_channel_mask_t channelMask, audio_devices_t deviceType) { uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); - if ((channelCount == 0) || (channelCount > 2)) { + if (channelCount < 1 || channelCount > LVM_MAX_CHANNELS) { return -EINVAL; } - return VirtualizerIsDeviceSupported(deviceType); } @@ -3216,6 +3231,7 @@ int Effect_process(effect_handle_t self, EffectContext * pContext = (EffectContext *) self; int status = 0; int processStatus = 0; + const int NrChannels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels); //ALOGV("\tEffect_process Start : Enabled = %d Called = %d (%8d %8d %8d)", //pContext->pBundledContext->NumberEffectsEnabled,pContext->pBundledContext->NumberEffectsCalled, @@ -3246,7 +3262,7 @@ int Effect_process(effect_handle_t self, (pContext->EffectType == LVM_BASS_BOOST)){ //ALOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountBb > 0){ - pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * 2; // STEREO + pContext->pBundledContext->SamplesToExitCountBb -= outBuffer->frameCount * NrChannels; //ALOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left", // pContext->pBundledContext->SamplesToExitCountBb); } @@ -3266,7 +3282,7 @@ int Effect_process(effect_handle_t self, (pContext->EffectType == LVM_EQUALIZER)){ //ALOGV("\tEffect_process() LVM_EQUALIZER Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountEq > 0){ - pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * 2; // STEREO + pContext->pBundledContext->SamplesToExitCountEq -= outBuffer->frameCount * NrChannels; //ALOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left", // pContext->pBundledContext->SamplesToExitCountEq); } @@ -3280,7 +3296,8 @@ int Effect_process(effect_handle_t self, (pContext->EffectType == LVM_VIRTUALIZER)){ //ALOGV("\tEffect_process() LVM_VIRTUALIZER Effect is not enabled"); if(pContext->pBundledContext->SamplesToExitCountVirt > 0){ - pContext->pBundledContext->SamplesToExitCountVirt -= outBuffer->frameCount * 2;// STEREO + pContext->pBundledContext->SamplesToExitCountVirt -= + outBuffer->frameCount * NrChannels; //ALOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left", // pContext->pBundledContext->SamplesToExitCountVirt); } @@ -3331,7 +3348,7 @@ int Effect_process(effect_handle_t self, //pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType); if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { - for (size_t i = 0; i < outBuffer->frameCount * FCC_2; ++i){ + for (size_t i = 0; i < outBuffer->frameCount * NrChannels; ++i) { #ifdef NATIVE_FLOAT_BUFFER outBuffer->f32[i] += inBuffer->f32[i]; #else -- GitLab From 8be9311918a3c96d31c345e2b1b912365f96400a Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Tue, 11 Dec 2018 14:01:42 -0800 Subject: [PATCH 0594/1530] Upstream changes from hardware/google/av This includes changes up to commit b3c2c12135ad6e96ca3cfa258bba24eb7b7b92ca. Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 112362730 Bug: 119853704 Bug: 120792228 Change-Id: I4611fca4d65a02c5595dd613d7ddc4096f48e8f5 --- .../components/base/SimpleC2Component.cpp | 7 ++ media/codec2/components/raw/C2SoftRawDec.cpp | 13 +++ media/codec2/components/vpx/C2SoftVpxDec.cpp | 59 +++++++++++- media/codec2/sfplugin/CCodec.cpp | 14 +-- media/codec2/sfplugin/CCodecBufferChannel.cpp | 94 +++++++++++++------ media/codec2/sfplugin/CCodecConfig.cpp | 24 ++++- media/codec2/sfplugin/Codec2Buffer.cpp | 4 +- .../sfplugin/utils/Codec2BufferUtils.cpp | 32 +++++-- 8 files changed, 193 insertions(+), 54 deletions(-) diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index 7990ee5cd4..50b4d20f39 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -489,6 +489,13 @@ bool SimpleC2Component::processQueue() { } ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku()); + // If input buffer list is not empty, it means we have some input to process on. + // However, input could be a null buffer. In such case, clear the buffer list + // before making call to process(). + if (!work->input.buffers.empty() && !work->input.buffers[0]) { + ALOGD("Encountered null input buffer. Clearing the input buffer"); + work->input.buffers.clear(); + } process(work, mOutputBlockPool); ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku()); { diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp index 8d2a652404..5c834814b8 100644 --- a/media/codec2/components/raw/C2SoftRawDec.cpp +++ b/media/codec2/components/raw/C2SoftRawDec.cpp @@ -83,6 +83,18 @@ public: DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 64 * 1024)) .build()); + + addParameter( + DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING) + .withDefault(new C2StreamPcmEncodingInfo::output(0u, C2Config::PCM_16)) + .withFields({C2F(mPcmEncodingInfo, value).oneOf({ + C2Config::PCM_16, + C2Config::PCM_8, + C2Config::PCM_FLOAT}) + }) + .withSetter((Setter::StrictValueWithNoDeps)) + .build()); + } private: @@ -94,6 +106,7 @@ private: std::shared_ptr mChannelCount; std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; + std::shared_ptr mPcmEncodingInfo; }; C2SoftRawDec::C2SoftRawDec( diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp index 01de681349..8ecbf5d8e3 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.cpp +++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp @@ -97,6 +97,26 @@ public: .withSetter(ProfileLevelSetter, mSize) .build()); + mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0); + addParameter( + DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO) + .withDefault(mHdr10PlusInfoInput) + .withFields({ + C2F(mHdr10PlusInfoInput, m.value).any(), + }) + .withSetter(Hdr10PlusInfoInputSetter) + .build()); + + mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0); + addParameter( + DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO) + .withDefault(mHdr10PlusInfoOutput) + .withFields({ + C2F(mHdr10PlusInfoOutput, m.value).any(), + }) + .withSetter(Hdr10PlusInfoOutputSetter) + .build()); + #if 0 // sample BT.2020 static info mHdrStaticInfo = std::make_shared(); @@ -217,6 +237,18 @@ public: return C2R::Ok(); } + static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + (void)me; // TODO: validate + return C2R::Ok(); + } + + static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + (void)me; // TODO: validate + return C2R::Ok(); + } + private: std::shared_ptr mProfileLevel; std::shared_ptr mSize; @@ -228,6 +260,8 @@ private: #if 0 std::shared_ptr mHdrStaticInfo; #endif + std::shared_ptr mHdr10PlusInfoInput; + std::shared_ptr mHdr10PlusInfoOutput; #endif }; @@ -370,7 +404,8 @@ void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr &wor const std::shared_ptr &block) { std::shared_ptr buffer = createGraphicBuffer(block, C2Rect(mWidth, mHeight)); - auto fillWork = [buffer, index](const std::unique_ptr &work) { + auto fillWork = [buffer, index, intf = this->mIntf]( + const std::unique_ptr &work) { uint32_t flags = 0; if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) && (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) { @@ -382,6 +417,28 @@ void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr &wor work->worklets.front()->output.buffers.push_back(buffer); work->worklets.front()->output.ordinal = work->input.ordinal; work->workletsProcessed = 1u; + + for (const std::unique_ptr ¶m: work->input.configUpdate) { + if (param) { + C2StreamHdr10PlusInfo::input *hdr10PlusInfo = + C2StreamHdr10PlusInfo::input::From(param.get()); + + if (hdr10PlusInfo != nullptr) { + std::vector> failures; + std::unique_ptr outParam = C2Param::CopyAsStream( + *param.get(), true /*output*/, param->stream()); + c2_status_t err = intf->config( + { outParam.get() }, C2_MAY_BLOCK, &failures); + if (err == C2_OK) { + work->worklets.front()->output.configUpdate.push_back( + C2Param::Copy(*outParam.get())); + } else { + ALOGE("finishWork: Config update size failed"); + } + break; + } + } + } }; if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { fillWork(work); diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index f903bbb240..852d6d6b12 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -1348,9 +1348,7 @@ void CCodec::signalResume() { } void CCodec::signalSetParameters(const sp ¶ms) { - sp msg = new AMessage(kWhatSetParameters, this); - msg->setMessage("params", params); - msg->post(); + setParameters(params); } void CCodec::setParameters(const sp ¶ms) { @@ -1515,13 +1513,6 @@ void CCodec::onMessageReceived(const sp &msg) { setInputSurface(surface); break; } - case kWhatSetParameters: { - setDeadline(now, 50ms, "setParameters"); - sp params; - CHECK(msg->findMessage("params", ¶ms)); - setParameters(params); - break; - } case kWhatWorkDone: { std::unique_ptr work; size_t numDiscardedInputBuffers; @@ -1594,6 +1585,7 @@ void CCodec::onMessageReceived(const sp &msg) { C2StreamColorAspectsInfo::output::PARAM_TYPE, C2StreamDataSpaceInfo::output::PARAM_TYPE, C2StreamHdrStaticInfo::output::PARAM_TYPE, + C2StreamHdr10PlusInfo::output::PARAM_TYPE, C2StreamPixelAspectRatioInfo::output::PARAM_TYPE, C2StreamSurfaceScalingInfo::output::PARAM_TYPE }; @@ -1677,7 +1669,7 @@ void CCodec::onWorkQueued(bool eos) { deadline->set(std::chrono::steady_clock::now() + 3s, "eos"); } // TODO: query and use input/pipeline/output delay combined - if (count >= 8) { + if (count >= 4) { CCodecWatchdog::getInstance()->watch(this); Mutexed::Locked deadline(mQueueDeadline); deadline->set(std::chrono::steady_clock::now() + 3s, "queue"); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 01b9c1ed8f..55a97d8860 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -787,8 +787,13 @@ public: std::unique_ptr toArrayMode( size_t size) final { int32_t capacity = kLinearBufferSize; - (void)mFormat->findInt32(C2_NAME_STREAM_MAX_BUFFER_SIZE_SETTING, &capacity); - + (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); + if ((size_t)capacity > kMaxLinearBufferSize) { + ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); + capacity = kMaxLinearBufferSize; + } + // TODO: proper max input size + // TODO: read usage from intf std::unique_ptr array( new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]")); array->setPool(mPool); @@ -1807,17 +1812,29 @@ void CCodecBufferChannel::feedInputBufferIfAvailableInternal() { status_t CCodecBufferChannel::renderOutputBuffer( const sp &buffer, int64_t timestampNs) { + ALOGV("[%s] renderOutputBuffer: %p", mName, buffer.get()); std::shared_ptr c2Buffer; + bool released = false; { Mutexed>::Locked buffers(mOutputBuffers); if (*buffers) { - (*buffers)->releaseBuffer(buffer, &c2Buffer); + released = (*buffers)->releaseBuffer(buffer, &c2Buffer); } } + // NOTE: some apps try to releaseOutputBuffer() with timestamp and/or render + // set to true. + sendOutputBuffers(); + // input buffer feeding may have been gated by pending output buffers + feedInputBufferIfAvailable(); if (!c2Buffer) { + if (released) { + ALOGD("[%s] The app is calling releaseOutputBuffer() with " + "timestamp or render=true with non-video buffers. Apps should " + "call releaseOutputBuffer() with render=false for those.", + mName); + } return INVALID_OPERATION; } - sendOutputBuffers(); #if 0 const std::vector> infoParams = c2Buffer->info(); @@ -1871,6 +1888,11 @@ status_t CCodecBufferChannel::renderOutputBuffer( std::static_pointer_cast( c2Buffer->getInfo(C2StreamHdrStaticInfo::output::PARAM_TYPE)); + // HDR10 plus info + std::shared_ptr hdr10PlusInfo = + std::static_pointer_cast( + c2Buffer->getInfo(C2StreamHdr10PlusInfo::output::PARAM_TYPE)); + { Mutexed::Locked output(mOutputSurface); if (output->surface == nullptr) { @@ -1898,35 +1920,45 @@ status_t CCodecBufferChannel::renderOutputBuffer( videoScalingMode, transform, Fence::NO_FENCE, 0); - if (hdrStaticInfo) { - struct android_smpte2086_metadata smpte2086_meta = { - .displayPrimaryRed = { - hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y - }, - .displayPrimaryGreen = { - hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y - }, - .displayPrimaryBlue = { - hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y - }, - .whitePoint = { - hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y - }, - .maxLuminance = hdrStaticInfo->mastering.maxLuminance, - .minLuminance = hdrStaticInfo->mastering.minLuminance, - }; - - struct android_cta861_3_metadata cta861_meta = { - .maxContentLightLevel = hdrStaticInfo->maxCll, - .maxFrameAverageLightLevel = hdrStaticInfo->maxFall, - }; - + if (hdrStaticInfo || hdr10PlusInfo) { HdrMetadata hdr; - hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3; - hdr.smpte2086 = smpte2086_meta; - hdr.cta8613 = cta861_meta; + if (hdrStaticInfo) { + struct android_smpte2086_metadata smpte2086_meta = { + .displayPrimaryRed = { + hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y + }, + .displayPrimaryGreen = { + hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y + }, + .displayPrimaryBlue = { + hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y + }, + .whitePoint = { + hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y + }, + .maxLuminance = hdrStaticInfo->mastering.maxLuminance, + .minLuminance = hdrStaticInfo->mastering.minLuminance, + }; + + struct android_cta861_3_metadata cta861_meta = { + .maxContentLightLevel = hdrStaticInfo->maxCll, + .maxFrameAverageLightLevel = hdrStaticInfo->maxFall, + }; + + hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3; + hdr.smpte2086 = smpte2086_meta; + hdr.cta8613 = cta861_meta; + } + if (hdr10PlusInfo) { + hdr.validTypes |= HdrMetadata::HDR10PLUS; + hdr.hdr10plus.assign( + hdr10PlusInfo->m.value, + hdr10PlusInfo->m.value + hdr10PlusInfo->flexCount()); + } qbi.setHdrMetadata(hdr); } + // we don't have dirty regions + qbi.setSurfaceDamage(Region::INVALID_REGION); android::IGraphicBufferProducer::QueueBufferOutput qbo; status_t result = mComponent->queueToOutputSurface(block, qbi, &qbo); if (result != OK) { @@ -1961,8 +1993,8 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) } } if (released) { - feedInputBufferIfAvailable(); sendOutputBuffers(); + feedInputBufferIfAvailable(); } else { ALOGD("[%s] MediaCodec discarded an unknown buffer", mName); } diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index 8dbfd0e7e4..ef02e74e1c 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -570,6 +570,12 @@ void CCodecConfig::initializeStandardParams() { add(ConfigMapper("csd-0", C2_PARAMKEY_INIT_DATA, "value") .limitTo(D::OUTPUT & D::READ)); + add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO, "value") + .limitTo(D::VIDEO & D::PARAM & D::INPUT)); + + add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO, "value") + .limitTo(D::VIDEO & D::OUTPUT)); + add(ConfigMapper(C2_PARAMKEY_TEMPORAL_LAYERING, C2_PARAMKEY_TEMPORAL_LAYERING, "") .limitTo(D::ENCODER & D::VIDEO & D::OUTPUT)); @@ -624,7 +630,23 @@ void CCodecConfig::initializeStandardParams() { .limitTo(D::AUDIO & D::CODED)); add(ConfigMapper(KEY_PCM_ENCODING, C2_PARAMKEY_PCM_ENCODING, "value") - .limitTo(D::AUDIO)); + .limitTo(D::AUDIO) + .withMappers([](C2Value v) -> C2Value { + int32_t value; + C2Config::pcm_encoding_t to; + if (v.get(&value) && C2Mapper::map(value, &to)) { + return to; + } + return C2Value(); + }, [](C2Value v) -> C2Value { + C2Config::pcm_encoding_t value; + int32_t to; + using C2ValueType=typename _c2_reduce_enum_to_underlying_type::type; + if (v.get((C2ValueType*)&value) && C2Mapper::map(value, &to)) { + return to; + } + return C2Value(); + })); add(ConfigMapper(KEY_IS_ADTS, C2_PARAMKEY_AAC_PACKAGING, "value") .limitTo(D::AUDIO & D::CODED) diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index bf6062efd5..1113ae838c 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -109,9 +109,11 @@ bool LocalLinearBuffer::copy(const std::shared_ptr &buffer) { // DummyContainerBuffer +static uint8_t sDummyByte[1] = { 0 }; + DummyContainerBuffer::DummyContainerBuffer( const sp &format, const std::shared_ptr &buffer) - : Codec2Buffer(format, new ABuffer(nullptr, 1)), + : Codec2Buffer(format, new ABuffer(sDummyByte, 1)), mBufferRef(buffer) { setRange(0, buffer ? 1 : 0); } diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp index b7519da389..84d22a377a 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp @@ -88,16 +88,30 @@ static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBa uint32_t planeW = img->mWidth / plane.colSampling; uint32_t planeH = img->mHeight / plane.rowSampling; - for (uint32_t row = 0; row < planeH; ++row) { - decltype(imgRow) imgPtr = imgRow; - decltype(viewRow) viewPtr = viewRow; - for (uint32_t col = 0; col < planeW; ++col) { - MemCopier::copy(imgPtr, viewPtr, bpp); - imgPtr += img->mPlane[i].mColInc; - viewPtr += plane.colInc; + + bool canCopyByRow = (plane.colInc == 1) && (img->mPlane[i].mColInc == 1); + bool canCopyByPlane = canCopyByRow && (plane.rowInc == img->mPlane[i].mRowInc); + if (canCopyByPlane) { + MemCopier::copy(imgRow, viewRow, plane.rowInc * planeH); + } else if (canCopyByRow) { + for (uint32_t row = 0; row < planeH; ++row) { + MemCopier::copy( + imgRow, viewRow, std::min(plane.rowInc, img->mPlane[i].mRowInc)); + imgRow += img->mPlane[i].mRowInc; + viewRow += plane.rowInc; + } + } else { + for (uint32_t row = 0; row < planeH; ++row) { + decltype(imgRow) imgPtr = imgRow; + decltype(viewRow) viewPtr = viewRow; + for (uint32_t col = 0; col < planeW; ++col) { + MemCopier::copy(imgPtr, viewPtr, bpp); + imgPtr += img->mPlane[i].mColInc; + viewPtr += plane.colInc; + } + imgRow += img->mPlane[i].mRowInc; + viewRow += plane.rowInc; } - imgRow += img->mPlane[i].mRowInc; - viewRow += plane.rowInc; } } return OK; -- GitLab From 32f45bfc862d6d2f6588e74d31d08b27377fe99a Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 12 Dec 2018 09:02:47 -0800 Subject: [PATCH 0595/1530] Remove MediaBuffer::clone and other dead code. Change I570374f936e434b214e9cd55e2a72f12ebeafad2 removed some dead code, but in turn created more dead code. Remove that too. Bug: 111407253 Test: manual, CTS Change-Id: I4ac01935c03257857456d8e460f98f6d4f7eb430 --- media/extractors/mp4/MPEG4Extractor.cpp | 104 ++---------------- media/libmediaextractor/MediaBuffer.cpp | 25 +---- .../include/media/stagefright/MediaBuffer.h | 7 -- .../media/stagefright/MediaBufferBase.h | 5 - 4 files changed, 11 insertions(+), 130 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 9fb2e35ab3..52213bd6e3 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5352,7 +5352,7 @@ media_status_t MPEG4Source::read( } } - if ((!mIsAVC && !mIsHEVC && !mIsAC4)) { + if (!mIsAVC && !mIsHEVC && !mIsAC4) { if (newBuffer) { if (mIsPcm) { // The twos' PCM block reader assumes that all samples has the same size. @@ -5429,58 +5429,11 @@ media_status_t MPEG4Source::read( } } - if (!mIsAVC && !mIsHEVC && !mIsAC4) { - *out = mBuffer; - mBuffer = NULL; - - return AMEDIA_OK; - } - - if (mIsAC4) { - mBuffer->release(); - mBuffer = NULL; - - return AMEDIA_ERROR_IO; - } - - // Each NAL unit is split up into its constituent fragments and - // each one of them returned in its own buffer. - - CHECK(mBuffer->range_length() >= mNALLengthSize); - - const uint8_t *src = - (const uint8_t *)mBuffer->data() + mBuffer->range_offset(); - - size_t nal_size = parseNALSize(src); - if (mNALLengthSize > SIZE_MAX - nal_size) { - ALOGE("b/24441553, b/24445122"); - } - if (mBuffer->range_length() - mNALLengthSize < nal_size) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return AMEDIA_ERROR_MALFORMED; - } - - MediaBufferBase *clone = mBuffer->clone(); - CHECK(clone != NULL); - clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size); - - CHECK(mBuffer != NULL); - mBuffer->set_range( - mBuffer->range_offset() + mNALLengthSize + nal_size, - mBuffer->range_length() - mNALLengthSize - nal_size); - - if (mBuffer->range_length() == 0) { - mBuffer->release(); - mBuffer = NULL; - } - - *out = clone; + *out = mBuffer; + mBuffer = NULL; return AMEDIA_OK; + } else if (mIsAC4) { CHECK(mBuffer != NULL); // Make sure there is enough space to write the sync header and the raw frame @@ -5773,7 +5726,7 @@ media_status_t MPEG4Source::fragmentedRead( } - if ((!mIsAVC && !mIsHEVC)) { + if (!mIsAVC && !mIsHEVC) { if (newBuffer) { if (!isInRange((size_t)0u, mBuffer->size(), size)) { mBuffer->release(); @@ -5825,52 +5778,11 @@ media_status_t MPEG4Source::fragmentedRead( ++mCurrentSampleIndex; } - if (!mIsAVC && !mIsHEVC) { - *out = mBuffer; - mBuffer = NULL; - - return AMEDIA_OK; - } - - // Each NAL unit is split up into its constituent fragments and - // each one of them returned in its own buffer. - - CHECK(mBuffer->range_length() >= mNALLengthSize); - - const uint8_t *src = - (const uint8_t *)mBuffer->data() + mBuffer->range_offset(); - - size_t nal_size = parseNALSize(src); - if (mNALLengthSize > SIZE_MAX - nal_size) { - ALOGE("b/24441553, b/24445122"); - } - - if (mBuffer->range_length() - mNALLengthSize < nal_size) { - ALOGE("incomplete NAL unit."); - - mBuffer->release(); - mBuffer = NULL; - - return AMEDIA_ERROR_MALFORMED; - } - - MediaBufferBase *clone = mBuffer->clone(); - CHECK(clone != NULL); - clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size); - - CHECK(mBuffer != NULL); - mBuffer->set_range( - mBuffer->range_offset() + mNALLengthSize + nal_size, - mBuffer->range_length() - mNALLengthSize - nal_size); - - if (mBuffer->range_length() == 0) { - mBuffer->release(); - mBuffer = NULL; - } - - *out = clone; + *out = mBuffer; + mBuffer = NULL; return AMEDIA_OK; + } else { ALOGV("whole NAL"); // Whole NAL units are returned but each fragment is prefixed by diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp index d197b3f0a7..26d0bd40fb 100644 --- a/media/libmediaextractor/MediaBuffer.cpp +++ b/media/libmediaextractor/MediaBuffer.cpp @@ -39,8 +39,7 @@ MediaBuffer::MediaBuffer(void *data, size_t size) mRangeOffset(0), mRangeLength(size), mOwnsData(false), - mMetaData(new MetaDataBase), - mOriginal(NULL) { + mMetaData(new MetaDataBase) { } MediaBuffer::MediaBuffer(size_t size) @@ -51,8 +50,7 @@ MediaBuffer::MediaBuffer(size_t size) mRangeOffset(0), mRangeLength(size), mOwnsData(true), - mMetaData(new MetaDataBase), - mOriginal(NULL) { + mMetaData(new MetaDataBase) { if (size < kSharedMemThreshold || std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) { mData = malloc(size); @@ -84,8 +82,7 @@ MediaBuffer::MediaBuffer(const sp &buffer) mRangeLength(mSize), mBuffer(buffer), mOwnsData(false), - mMetaData(new MetaDataBase), - mOriginal(NULL) { + mMetaData(new MetaDataBase) { } void MediaBuffer::release() { @@ -162,11 +159,6 @@ MediaBuffer::~MediaBuffer() { mData = NULL; } - if (mOriginal != NULL) { - mOriginal->release(); - mOriginal = NULL; - } - if (mMemory.get() != nullptr) { getSharedControl()->setDeadObject(); } @@ -178,15 +170,4 @@ void MediaBuffer::setObserver(MediaBufferObserver *observer) { mObserver = observer; } -MediaBufferBase *MediaBuffer::clone() { - MediaBuffer *buffer = new MediaBuffer(mData, mSize); - buffer->set_range(mRangeOffset, mRangeLength); - buffer->mMetaData = new MetaDataBase(*mMetaData); - - add_ref(); - buffer->mOriginal = this; - - return buffer; -} - } // namespace android diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h index 5a25965b86..5b362a4ad8 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h @@ -80,11 +80,6 @@ public: virtual void setObserver(MediaBufferObserver *group); - // Returns a clone of this MediaBuffer increasing its reference count. - // The clone references the same data but has its own range and - // MetaData. - virtual MediaBufferBase *clone(); - // sum of localRefcount() and remoteRefcount() // Result should be treated as approximate unless the result precludes concurrent accesses. virtual int refcount() const { @@ -158,8 +153,6 @@ private: MetaDataBase* mMetaData; - MediaBuffer *mOriginal; - static std::atomic_int_least32_t mUseSharedMemory; MediaBuffer(const MediaBuffer &); diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h index d67ddbda41..eb49f4c2e1 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h @@ -70,11 +70,6 @@ public: virtual void setObserver(MediaBufferObserver *group) = 0; - // Returns a clone of this MediaBufferBase increasing its reference - // count. The clone references the same data but has its own range and - // MetaData. - virtual MediaBufferBase *clone() = 0; - virtual int refcount() const = 0; virtual int localRefcount() const = 0; -- GitLab From 9ccaa1669136b37c6dec8a9fb701be61b8ce1bf0 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 12 Dec 2018 10:27:29 -0800 Subject: [PATCH 0596/1530] Audio HAL: Simplify types updates for 'common' and 'audio' packages Instead of importing every single type individually ("using "), import entire namespaces of ::android::hardware::audio::CPP_VERSION and ::android::hardware::audio::common::CPP_VERSION inside the "implementation" namespace in the default implementation. This simplifies modifying types, as it is not needed anymore to surround "using " directives with #if MAJOR_VERSION ... Note that the contents of the namespaces are imported inside the "implementation" namespace, which reduces risks of name collision Also, fixed the namespace for HidlUtils--it needs to be in 'implementation'. Test: make Change-Id: I8461d2ac3ad5d8ad4df4a15d99430ae0d7a5ccef --- media/libaudiohal/impl/DeviceHalHidl.cpp | 24 ++------- media/libaudiohal/impl/EffectHalHidl.cpp | 6 +-- .../impl/EffectsFactoryHalHidl.cpp | 5 +- media/libaudiohal/impl/StreamHalHidl.cpp | 52 ++++++------------- 4 files changed, 25 insertions(+), 62 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index b3ff7572b5..8537608323 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -31,33 +31,17 @@ #include "StreamHalHidl.h" #include "VersionUtils.h" -using ::android::hardware::audio::common::CPP_VERSION::AudioConfig; -using ::android::hardware::audio::common::CPP_VERSION::AudioDevice; -using ::android::hardware::audio::common::CPP_VERSION::AudioInputFlag; -using ::android::hardware::audio::common::CPP_VERSION::AudioOutputFlag; -using ::android::hardware::audio::common::CPP_VERSION::AudioPatchHandle; -using ::android::hardware::audio::common::CPP_VERSION::AudioPort; -using ::android::hardware::audio::common::CPP_VERSION::AudioPortConfig; -using ::android::hardware::audio::common::CPP_VERSION::AudioMode; -using ::android::hardware::audio::common::CPP_VERSION::AudioSource; -using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; +using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils; using ::android::hardware::audio::common::utils::EnumBitfield; -using ::android::hardware::audio::CPP_VERSION::DeviceAddress; -using ::android::hardware::audio::CPP_VERSION::IPrimaryDevice; -using ::android::hardware::audio::CPP_VERSION::ParameterValue; -using ::android::hardware::audio::CPP_VERSION::Result; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; -#if MAJOR_VERSION == 4 -using ::android::hardware::audio::CPP_VERSION::SinkMetadata; -#elif MAJOR_VERSION == 5 -using ::android::hardware::audio::common::CPP_VERSION::SinkMetadata; -#endif - namespace android { namespace CPP_VERSION { +using namespace ::android::hardware::audio::common::CPP_VERSION; +using namespace ::android::hardware::audio::CPP_VERSION; + namespace { status_t deviceAddressFromHal( diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp index 7b867b48c9..df79b95dab 100644 --- a/media/libaudiohal/impl/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -31,9 +31,7 @@ using ::android::hardware::audio::effect::CPP_VERSION::EffectBufferAccess; using ::android::hardware::audio::effect::CPP_VERSION::EffectConfigParameters; using ::android::hardware::audio::effect::CPP_VERSION::MessageQueueFlagBits; using ::android::hardware::audio::effect::CPP_VERSION::Result; -using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; -using ::android::hardware::audio::common::CPP_VERSION::AudioChannelMask; -using ::android::hardware::audio::common::CPP_VERSION::AudioFormat; +using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils; using ::android::hardware::audio::common::utils::EnumBitfield; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; @@ -42,6 +40,8 @@ using ::android::hardware::Return; namespace android { namespace CPP_VERSION { +using namespace ::android::hardware::audio::common::CPP_VERSION; + EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { } diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp index b8804332cd..7fea46699f 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp @@ -25,8 +25,7 @@ #include "EffectHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::common::CPP_VERSION::HidlUtils; -using ::android::hardware::audio::common::CPP_VERSION::Uuid; +using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils; using ::android::hardware::audio::effect::CPP_VERSION::IEffect; using ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::Return; @@ -34,6 +33,8 @@ using ::android::hardware::Return; namespace android { namespace CPP_VERSION { +using namespace ::android::hardware::audio::common::CPP_VERSION; + EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { mEffectsFactory = IEffectsFactory::getService(); if (mEffectsFactory == 0) { diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 6b59f5cb98..9765f1ea69 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -28,44 +28,18 @@ #include "StreamHalHidl.h" #include "VersionUtils.h" -using ::android::hardware::audio::common::CPP_VERSION::AudioChannelMask; -using ::android::hardware::audio::common::CPP_VERSION::AudioFormat; -using ::android::hardware::audio::common::CPP_VERSION::ThreadInfo; -using ::android::hardware::audio::CPP_VERSION::AudioDrain; -using ::android::hardware::audio::CPP_VERSION::IStreamOutCallback; -using ::android::hardware::audio::CPP_VERSION::MessageQueueFlagBits; -using ::android::hardware::audio::CPP_VERSION::MmapBufferInfo; -using ::android::hardware::audio::CPP_VERSION::MmapPosition; -using ::android::hardware::audio::CPP_VERSION::ParameterValue; -using ::android::hardware::audio::CPP_VERSION::Result; -using ::android::hardware::audio::CPP_VERSION::TimeSpec; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; using ::android::hardware::Void; -using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand; - -#if MAJOR_VERSION >= 4 -using ::android::hardware::audio::common::CPP_VERSION::AudioContentType; -using ::android::hardware::audio::common::CPP_VERSION::AudioSource; -using ::android::hardware::audio::common::CPP_VERSION::AudioUsage; -using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; -#endif - -#if MAJOR_VERSION == 4 -using ::android::hardware::audio::CPP_VERSION::PlaybackTrackMetadata; -using ::android::hardware::audio::CPP_VERSION::RecordTrackMetadata; -using HalSinkMetadata = ::android::hardware::audio::CPP_VERSION::SinkMetadata; -using HalSourceMetadata = ::android::hardware::audio::CPP_VERSION::SourceMetadata; -#elif MAJOR_VERSION == 5 -using ::android::hardware::audio::common::CPP_VERSION::PlaybackTrackMetadata; -using ::android::hardware::audio::common::CPP_VERSION::RecordTrackMetadata; -using HalSinkMetadata = ::android::hardware::audio::common::CPP_VERSION::SinkMetadata; -using HalSourceMetadata = ::android::hardware::audio::common::CPP_VERSION::SourceMetadata; -#endif namespace android { namespace CPP_VERSION { +using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand; + +using namespace ::android::hardware::audio::common::CPP_VERSION; +using namespace ::android::hardware::audio::CPP_VERSION; + StreamHalHidl::StreamHalHidl(IStream *stream) : ConversionHelperHidl("Stream"), mStream(stream), @@ -609,7 +583,8 @@ status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct time } #if MAJOR_VERSION == 2 -status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& /* sourceMetadata */) { +status_t StreamOutHalHidl::updateSourceMetadata( + const StreamOutHalInterface::SourceMetadata& /* sourceMetadata */) { // Audio HAL V2.0 does not support propagating source metadata return INVALID_OPERATION; } @@ -623,8 +598,9 @@ static auto transformToHidlVec(const Values& values, ElementConverter converter) return result; } -status_t StreamOutHalHidl::updateSourceMetadata(const SourceMetadata& sourceMetadata) { - HalSourceMetadata halMetadata = { +status_t StreamOutHalHidl::updateSourceMetadata( + const StreamOutHalInterface::SourceMetadata& sourceMetadata) { + CPP_VERSION::SourceMetadata halMetadata = { .tracks = transformToHidlVec(sourceMetadata.tracks, [](const playback_track_metadata& metadata) -> PlaybackTrackMetadata { return { @@ -838,7 +814,8 @@ status_t StreamInHalHidl::getActiveMicrophones( return INVALID_OPERATION; } -status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& /* sinkMetadata */) { +status_t StreamInHalHidl::updateSinkMetadata( + const StreamInHalInterface::SinkMetadata& /* sinkMetadata */) { // Audio HAL V2.0 does not support propagating sink metadata return INVALID_OPERATION; } @@ -862,8 +839,9 @@ status_t StreamInHalHidl::getActiveMicrophones( return processReturn("getActiveMicrophones", ret, retval); } -status_t StreamInHalHidl::updateSinkMetadata(const SinkMetadata& sinkMetadata) { - HalSinkMetadata halMetadata = { +status_t StreamInHalHidl::updateSinkMetadata(const + StreamInHalInterface::SinkMetadata& sinkMetadata) { + CPP_VERSION::SinkMetadata halMetadata = { .tracks = transformToHidlVec(sinkMetadata.tracks, [](const record_track_metadata& metadata) -> RecordTrackMetadata { return { -- GitLab From ee39a7f0b500d318971f056d03048d85a6917b00 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 12 Dec 2018 08:14:42 -0800 Subject: [PATCH 0597/1530] Convert MPEG4Extractor to V3 format Bug: 111407253 Test: manual, CTS Change-Id: Ic59ebc1b8093a2fd33485550625bb6083cff54d7 --- media/extractors/mp4/Android.bp | 1 - media/extractors/mp4/MPEG4Extractor.cpp | 160 +++++++++--------- media/extractors/mp4/MPEG4Extractor.h | 4 +- .../media/stagefright/MediaBufferBase.h | 8 + media/libstagefright/MediaTrack.cpp | 42 +++++ media/libstagefright/Utils.cpp | 6 + media/ndk/NdkMediaFormat.cpp | 3 + media/ndk/include/media/NdkMediaFormat.h | 3 + media/ndk/libmediandk.map.txt | 3 + 9 files changed, 145 insertions(+), 85 deletions(-) diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp index 91de353f3f..1b308aa7ef 100644 --- a/media/extractors/mp4/Android.bp +++ b/media/extractors/mp4/Android.bp @@ -15,7 +15,6 @@ cc_defaults { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk" ], diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 52213bd6e3..c4b539d760 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -44,10 +44,9 @@ #include #include #include -#include #include #include -#include +#include #include #include @@ -70,7 +69,7 @@ enum { kMaxAtomSize = 64 * 1024 * 1024, }; -class MPEG4Source : public MediaTrackHelperV2 { +class MPEG4Source : public MediaTrackHelperV3 { static const size_t kMaxPcmFrameSize = 8192; public: // Caller retains ownership of both "dataSource" and "sampleTable". @@ -89,10 +88,10 @@ public: virtual media_status_t getFormat(AMediaFormat *); - virtual media_status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL); + virtual media_status_t read(MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); virtual bool supportNonblockingRead() { return true; } virtual media_status_t fragmentedRead( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); virtual ~MPEG4Source(); @@ -137,9 +136,7 @@ private: bool mStarted; - MediaBufferGroup *mGroup; - - MediaBufferBase *mBuffer; + MediaBufferHelperV3 *mBuffer; uint8_t *mSrcBuffer; @@ -3827,7 +3824,7 @@ void MPEG4Extractor::parseID3v2MetaData(off64_t offset) { } } -MediaTrackHelperV2 *MPEG4Extractor::getTrack(size_t index) { +MediaTrackHelperV3 *MPEG4Extractor::getTrack(size_t index) { status_t err; if ((err = readMetaData()) != OK) { return NULL; @@ -4329,7 +4326,6 @@ MPEG4Source::MPEG4Source( mIsPcm(false), mNALLengthSize(0), mStarted(false), - mGroup(NULL), mBuffer(NULL), mSrcBuffer(NULL), mIsHeif(itemTable != NULL), @@ -4458,12 +4454,10 @@ media_status_t MPEG4Source::start() { const size_t kInitialBuffers = 2; const size_t kMaxBuffers = 8; const size_t realMaxBuffers = min(kMaxBufferSize / max_size, kMaxBuffers); - mGroup = new MediaBufferGroup(kInitialBuffers, max_size, realMaxBuffers); + mBufferGroup->init(kInitialBuffers, max_size, realMaxBuffers); mSrcBuffer = new (std::nothrow) uint8_t[max_size]; if (mSrcBuffer == NULL) { // file probably specified a bad max size - delete mGroup; - mGroup = NULL; return AMEDIA_ERROR_MALFORMED; } @@ -4485,9 +4479,6 @@ media_status_t MPEG4Source::stop() { delete[] mSrcBuffer; mSrcBuffer = NULL; - delete mGroup; - mGroup = NULL; - mStarted = false; mCurrentSampleIndex = 0; @@ -5184,12 +5175,12 @@ int32_t MPEG4Source::parseHEVCLayerId(const uint8_t *data, size_t size) { } media_status_t MPEG4Source::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { Mutex::Autolock autoLock(mLock); CHECK(mStarted); - if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) { + if (options != nullptr && options->getNonBlocking() && !mBufferGroup->has_buffers()) { *out = nullptr; return AMEDIA_ERROR_WOULD_BLOCK; } @@ -5338,7 +5329,7 @@ media_status_t MPEG4Source::read( return AMEDIA_ERROR_UNKNOWN; } - err = mGroup->acquire_buffer(&mBuffer); + err = mBufferGroup->acquire_buffer(&mBuffer); if (err != OK) { CHECK(mBuffer == NULL); @@ -5377,9 +5368,11 @@ media_status_t MPEG4Source::read( return AMEDIA_ERROR_IO; } - mBuffer->meta_data().clear(); - mBuffer->meta_data().setInt64(kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat *meta = mBuffer->meta_data(); + AMediaFormat_clear(meta); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); int32_t byteOrder; AMediaFormat_getInt32(mFormat, @@ -5410,19 +5403,20 @@ media_status_t MPEG4Source::read( CHECK(mBuffer != NULL); mBuffer->set_range(0, size); - mBuffer->meta_data().clear(); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + AMediaFormat *meta = mBuffer->meta_data(); + AMediaFormat_clear(meta); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } if (isSyncSample) { - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } ++mCurrentSampleIndex; @@ -5465,19 +5459,20 @@ media_status_t MPEG4Source::read( } mBuffer->set_range(0, dstOffset + size); - mBuffer->meta_data().clear(); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + AMediaFormat *meta = mBuffer->meta_data(); + AMediaFormat_clear(meta); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } if (isSyncSample) { - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } ++mCurrentSampleIndex; @@ -5545,31 +5540,32 @@ media_status_t MPEG4Source::read( CHECK(mBuffer != NULL); mBuffer->set_range(0, dstOffset); - mBuffer->meta_data().clear(); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)stts * 1000000) / mTimescale); + AMediaFormat *meta = mBuffer->meta_data(); + AMediaFormat_clear(meta); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64( + meta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } if (mIsAVC) { uint32_t layerId = FindAVCLayerId( (const uint8_t *)mBuffer->data(), mBuffer->range_length()); - mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId); } else if (mIsHEVC) { int32_t layerId = parseHEVCLayerId( (const uint8_t *)mBuffer->data(), mBuffer->range_length()); if (layerId >= 0) { - mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId); } } if (isSyncSample) { - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } ++mCurrentSampleIndex; @@ -5582,7 +5578,7 @@ media_status_t MPEG4Source::read( } media_status_t MPEG4Source::fragmentedRead( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { ALOGV("MPEG4Source::fragmentedRead"); @@ -5685,7 +5681,7 @@ media_status_t MPEG4Source::fragmentedRead( mCurrentTime += smpl->duration; isSyncSample = (mCurrentSampleIndex == 0); - status_t err = mGroup->acquire_buffer(&mBuffer); + status_t err = mBufferGroup->acquire_buffer(&mBuffer); if (err != OK) { CHECK(mBuffer == NULL); @@ -5701,19 +5697,21 @@ media_status_t MPEG4Source::fragmentedRead( } const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex]; - MetaDataBase &bufmeta = mBuffer->meta_data(); - bufmeta.clear(); + AMediaFormat *bufmeta = mBuffer->meta_data(); + AMediaFormat_clear(bufmeta); if (smpl->encryptedsizes.size()) { // store clear/encrypted lengths in metadata - bufmeta.setData(kKeyPlainSizes, 0, + AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, smpl->clearsizes.array(), smpl->clearsizes.size() * 4); - bufmeta.setData(kKeyEncryptedSizes, 0, + AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4); - bufmeta.setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize); - bufmeta.setInt32(kKeyCryptoMode, mCryptoMode); - bufmeta.setData(kKeyCryptoKey, 0, mCryptoKey, 16); - bufmeta.setInt32(kKeyEncryptedByteBlock, mDefaultEncryptedByteBlock); - bufmeta.setInt32(kKeySkipByteBlock, mDefaultSkipByteBlock); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, mDefaultIVSize); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_MODE, mCryptoMode); + AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_KEY, mCryptoKey, 16); + AMediaFormat_setInt32(bufmeta, + AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, mDefaultEncryptedByteBlock); + AMediaFormat_setInt32(bufmeta, + AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, mDefaultSkipByteBlock); void *iv = NULL; size_t ivlength = 0; @@ -5722,8 +5720,7 @@ media_status_t MPEG4Source::fragmentedRead( iv = (void *) smpl->iv; ivlength = 16; // use 16 or the actual size? } - bufmeta.setData(kKeyCryptoIV, 0, iv, ivlength); - + AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_IV, iv, ivlength); } if (!mIsAVC && !mIsHEVC) { @@ -5749,30 +5746,29 @@ media_status_t MPEG4Source::fragmentedRead( CHECK(mBuffer != NULL); mBuffer->set_range(0, size); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale); + AMediaFormat_setInt64(bufmeta, + AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt64(bufmeta, + AMEDIAFORMAT_KEY_DURATION, ((int64_t)smpl->duration * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64(bufmeta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } if (mIsAVC) { uint32_t layerId = FindAVCLayerId( (const uint8_t *)mBuffer->data(), mBuffer->range_length()); - mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId); } else if (mIsHEVC) { int32_t layerId = parseHEVCLayerId( (const uint8_t *)mBuffer->data(), mBuffer->range_length()); if (layerId >= 0) { - mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID, layerId); } } if (isSyncSample) { - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } ++mCurrentSampleIndex; @@ -5864,18 +5860,18 @@ media_status_t MPEG4Source::fragmentedRead( CHECK(mBuffer != NULL); mBuffer->set_range(0, dstOffset); - mBuffer->meta_data().setInt64( - kKeyTime, ((int64_t)cts * 1000000) / mTimescale); - mBuffer->meta_data().setInt64( - kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale); + AMediaFormat *bufmeta = mBuffer->meta_data(); + AMediaFormat_setInt64(bufmeta, + AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMediaFormat_setInt64(bufmeta, + AMEDIAFORMAT_KEY_DURATION, ((int64_t)smpl->duration * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { - mBuffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64(bufmeta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } if (isSyncSample) { - mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(bufmeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); } ++mCurrentSampleIndex; @@ -6075,11 +6071,11 @@ static bool BetterSniffMPEG4(DataSourceHelper *source, float *confidence) { return true; } -static CMediaExtractorV2* CreateExtractor(CDataSource *source, void *) { - return wrapV2(new MPEG4Extractor(new DataSourceHelper(source))); +static CMediaExtractorV3* CreateExtractor(CDataSource *source, void *) { + return wrapV3(new MPEG4Extractor(new DataSourceHelper(source))); } -static CreatorFuncV2 Sniff( +static CreatorFuncV3 Sniff( CDataSource *source, float *confidence, void **, FreeMetaFunc *) { DataSourceHelper helper(source); @@ -6100,11 +6096,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("27575c67-4417-4c54-8d3d-8e626985a164"), 2, // version "MP4 Extractor", - { .v2 = Sniff } + { .v3 = Sniff } }; } diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 56b641d5ef..a9a46357c1 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -53,12 +53,12 @@ struct Trex { uint32_t default_sample_flags; }; -class MPEG4Extractor : public MediaExtractorPluginHelperV2 { +class MPEG4Extractor : public MediaExtractorPluginHelperV3 { public: explicit MPEG4Extractor(DataSourceHelper *source, const char *mime = NULL); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h index eb49f4c2e1..3682368b25 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h @@ -96,6 +96,14 @@ public: return ((MediaBufferBase*)handle)->size(); }; + mWrapper->range_offset = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->range_offset(); + }; + + mWrapper->range_length = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->range_length(); + }; + mWrapper->set_range = [](void *handle, size_t offset, size_t length) -> void { return ((MediaBufferBase*)handle)->set_range(offset, length); }; diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 6c0f989815..5f2e60123e 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -16,6 +16,7 @@ #include +#include #include #include @@ -216,10 +217,51 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption if (format->mFormat->findInt64("timeUs", &val64)) { meta.setInt64(kKeyTime, val64); } + if (format->mFormat->findInt64("duration", &val64)) { + meta.setInt64(kKeyDuration, val64); + } + if (format->mFormat->findInt64("target-time", &val64)) { + meta.setInt64(kKeyTargetTime, val64); + } int32_t val32; if (format->mFormat->findInt32("is-sync-frame", &val32)) { meta.setInt32(kKeyIsSyncFrame, val32); } + if (format->mFormat->findInt32("temporal-layer-id", &val32)) { + meta.setInt32(kKeyTemporalLayerId, val32); + } + if (format->mFormat->findInt32("temporal-layer-count", &val32)) { + meta.setInt32(kKeyTemporalLayerCount, val32); + } + if (format->mFormat->findInt32("crypto-default-iv-size", &val32)) { + meta.setInt32(kKeyCryptoDefaultIVSize, val32); + } + if (format->mFormat->findInt32("crypto-mode", &val32)) { + meta.setInt32(kKeyCryptoMode, val32); + } + if (format->mFormat->findInt32("crypto-encrypted-byte-block", &val32)) { + meta.setInt32(kKeyEncryptedByteBlock, val32); + } + if (format->mFormat->findInt32("crypto-skip-byte-block", &val32)) { + meta.setInt32(kKeySkipByteBlock, val32); + } + sp valbuf; + if (format->mFormat->findBuffer("crypto-plain-sizes", &valbuf)) { + meta.setData(kKeyPlainSizes, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } + if (format->mFormat->findBuffer("crypto-encrypted-sizes", &valbuf)) { + meta.setData(kKeyEncryptedSizes, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } + if (format->mFormat->findBuffer("crypto-key", &valbuf)) { + meta.setData(kKeyCryptoKey, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } + if (format->mFormat->findBuffer("crypto-iv", &valbuf)) { + meta.setData(kKeyCryptoIV, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } } else { *buffer = nullptr; } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 670b607e1c..cfa9fd9041 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -598,7 +598,10 @@ static std::vector> int64Mappings { { { "exif-offset", kKeyExifOffset }, { "exif-size", kKeyExifSize }, + { "target-time", kKeyTargetTime }, { "thumbnail-time", kKeyThumbnailTime }, + { "timeUs", kKeyTime }, + { "durationUs", kKeyDuration }, } }; @@ -613,6 +616,7 @@ static std::vector> int32Mappings { { "max-bitrate", kKeyMaxBitRate }, { "pcm-big-endian", kKeyPcmBigEndian }, { "temporal-layer-count", kKeyTemporalLayerCount }, + { "temporal-layer-id", kKeyTemporalLayerId }, { "thumbnail-width", kKeyThumbnailWidth }, { "thumbnail-height", kKeyThumbnailHeight }, { "frame-count", kKeyFrameCount }, @@ -626,6 +630,8 @@ static std::vector> bufferMappings { { "pssh", kKeyPssh }, { "crypto-iv", kKeyCryptoIV }, { "crypto-key", kKeyCryptoKey }, + { "crypto-encrypted-sizes", kKeyEncryptedSizes }, + { "crypto-plain-sizes", kKeyPlainSizes }, { "icc-profile", kKeyIccProfile }, { "text-format-data", kKeyTextFormatData }, } diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 200439cfbe..a8ae6fbf8f 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -291,9 +291,11 @@ EXPORT const char* AMEDIAFORMAT_KEY_COMPLEXITY = "complexity"; EXPORT const char* AMEDIAFORMAT_KEY_COMPOSER = "composer"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE = "crypto-default-iv-size"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK = "crypto-encrypted-byte-block"; +EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES = "crypto-encrypted-sizes"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_IV = "crypto-iv"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_KEY = "crypto-key"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_MODE = "crypto-mode"; +EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES = "crypto-encrypted-sizes"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK = "crypto-skip-byte-block"; EXPORT const char* AMEDIAFORMAT_KEY_CSD = "csd"; EXPORT const char* AMEDIAFORMAT_KEY_CSD_0 = "csd-0"; @@ -358,6 +360,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width"; EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei"; EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height"; EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride"; +EXPORT const char* AMEDIAFORMAT_KEY_TARGET_TIME = "target-time"; EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count"; EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id"; EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 13d9135c15..4958deaf28 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -188,9 +188,11 @@ extern const char* AMEDIAFORMAT_KEY_COMPILATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_COMPOSER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_IV __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_KEY __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_MODE __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CSD_AVC __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CSD_HEVC __INTRODUCED_IN(29); @@ -215,6 +217,7 @@ extern const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_PSSH __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_SAR_HEIGHT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_SAR_WIDTH __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_TARGET_TIME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 3567899b01..d24cc9b029 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -58,9 +58,11 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_COMPOSER; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_IV; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_KEY; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_MODE; # var introduced=29 + AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK; # var introduced=29 AMEDIAFORMAT_KEY_CSD; # var introduced=28 AMEDIAFORMAT_KEY_CSD_0; # var introduced=28 @@ -124,6 +126,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_SEI; # var introduced=28 AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28 AMEDIAFORMAT_KEY_STRIDE; # var introduced=21 + AMEDIAFORMAT_KEY_TARGET_TIME; # var introduced=29 AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT; # var introduced=29 AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; # var introduced=28 AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28 -- GitLab From dce8f8cc41cb6be63d13ad0099d61807746ff5d7 Mon Sep 17 00:00:00 2001 From: jiabin Date: Mon, 10 Dec 2018 17:49:31 -0800 Subject: [PATCH 0598/1530] Add buffer provider that can adjust channels. 1. Add adjust channels buffer provider, which will expand or contract sample data from one interleaved channel format to another. Expanded channels are filled with zeros and put at the end of each frames. Contracted channels are omitted from the end of each audio frame. 2. Add adjust channels non destructive buffer provider, which could expand or contract sample data from one interleaved channel format to another while not destructing the buffer. Extra expanded channels are interleaved in from the end of the input buffer. Contracted channels are copied to the end of the output buffer. Contracted channels could be written to an output buffer with certain audio format. Test: Manually Bug: 111454766 Change-Id: I3f963307e73b3f7aa662d4127f78f0c61ac84510 --- .../libaudioclient/include/media/AudioMixer.h | 33 ++++++- media/libaudioprocessing/AudioMixer.cpp | 93 +++++++++++++++++++ media/libaudioprocessing/BufferProviders.cpp | 79 ++++++++++++++++ .../libmedia/include/media/BufferProviders.h | 47 ++++++++++ 4 files changed, 248 insertions(+), 4 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index aa036a8e1f..0532232073 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -137,6 +137,13 @@ public: void setBufferProvider(int name, AudioBufferProvider* bufferProvider); void process() { + for (const auto &pair : mTracks) { + // Clear contracted buffer before processing if contracted channels are saved + const std::shared_ptr &t = pair.second; + if (t->mKeepContractedChannels) { + t->clearContractedBuffer(); + } + } (this->*mHook)(); } @@ -235,6 +242,8 @@ private: mPostDownmixReformatBufferProvider.reset(nullptr); mDownmixerBufferProvider.reset(nullptr); mReformatBufferProvider.reset(nullptr); + mAdjustChannelsNonDestructiveBufferProvider.reset(nullptr); + mAdjustChannelsBufferProvider.reset(nullptr); } bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; } @@ -249,6 +258,11 @@ private: void unprepareForDownmix(); status_t prepareForReformat(); void unprepareForReformat(); + status_t prepareForAdjustChannels(); + void unprepareForAdjustChannels(); + status_t prepareForAdjustChannelsNonDestructive(size_t frames); + void unprepareForAdjustChannelsNonDestructive(); + void clearContractedBuffer(); bool setPlaybackRate(const AudioPlaybackRate &playbackRate); void reconfigureBufferProviders(); @@ -302,17 +316,21 @@ private: * all pre-mixer track buffer conversions outside the AudioMixer class. * * 1) mInputBufferProvider: The AudioTrack buffer provider. - * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to + * 2) mAdjustChannelsBufferProvider: Expend or contracts data + * 3) mAdjustChannelsNonDestructiveBufferProvider: Non-destructively adjust sample data + * 4) mReformatBufferProvider: If not NULL, performs the audio reformat to * match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer * requires reformat. For example, it may convert floating point input to * PCM_16_bit if that's required by the downmixer. - * 3) mDownmixerBufferProvider: If not NULL, performs the channel remixing to match + * 5) mDownmixerBufferProvider: If not NULL, performs the channel remixing to match * the number of channels required by the mixer sink. - * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from + * 6) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from * the downmixer requirements to the mixer engine input requirements. - * 5) mTimestretchBufferProvider: Adds timestretching for playback rate + * 7) mTimestretchBufferProvider: Adds timestretching for playback rate */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. + std::unique_ptr mAdjustChannelsBufferProvider; + std::unique_ptr mAdjustChannelsNonDestructiveBufferProvider; std::unique_ptr mReformatBufferProvider; std::unique_ptr mDownmixerBufferProvider; std::unique_ptr mPostDownmixReformatBufferProvider; @@ -341,6 +359,13 @@ private: AudioPlaybackRate mPlaybackRate; + // Haptic + uint32_t mAdjustInChannelCount; + uint32_t mAdjustOutChannelCount; + uint32_t mAdjustNonDestructiveInChannelCount; + uint32_t mAdjustNonDestructiveOutChannelCount; + bool mKeepContractedChannels; + private: // hooks void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index af54b21986..365af75c1c 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -162,6 +162,12 @@ status_t AudioMixer::create( AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO); t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; + // haptic + t->mAdjustInChannelCount = 0; + t->mAdjustOutChannelCount = 0; + t->mAdjustNonDestructiveInChannelCount = 0; + t->mAdjustNonDestructiveOutChannelCount = 0; + t->mKeepContractedChannels = false; // Check the downmixing (or upmixing) requirements. status_t status = t->prepareForDownmix(); if (status != OK) { @@ -171,6 +177,8 @@ status_t AudioMixer::create( // prepareForDownmix() may change mDownmixRequiresFormat ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); t->prepareForReformat(); + t->prepareForAdjustChannelsNonDestructive(mFrameCount); + t->prepareForAdjustChannels(); mTracks[name] = t; return OK; @@ -212,6 +220,9 @@ bool AudioMixer::setChannelMasks(int name, // do it after downmix since track format may change! track->prepareForReformat(); + track->prepareForAdjustChannelsNonDestructive(mFrameCount); + track->prepareForAdjustChannels(); + if (track->mResampler.get() != nullptr) { // resampler channels may have changed. const uint32_t resetToSampleRate = track->sampleRate; @@ -335,10 +346,82 @@ status_t AudioMixer::Track::prepareForReformat() return NO_ERROR; } +void AudioMixer::Track::unprepareForAdjustChannels() +{ + ALOGV("AUDIOMIXER::unprepareForAdjustChannels"); + if (mAdjustChannelsBufferProvider.get() != nullptr) { + mAdjustChannelsBufferProvider.reset(nullptr); + reconfigureBufferProviders(); + } +} + +status_t AudioMixer::Track::prepareForAdjustChannels() +{ + ALOGV("AudioMixer::prepareForAdjustChannels(%p) with inChannelCount: %u, outChannelCount: %u", + this, mAdjustInChannelCount, mAdjustOutChannelCount); + unprepareForAdjustChannels(); + if (mAdjustInChannelCount != mAdjustOutChannelCount) { + mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider( + mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, kCopyBufferFrameCount)); + reconfigureBufferProviders(); + } + return NO_ERROR; +} + +void AudioMixer::Track::unprepareForAdjustChannelsNonDestructive() +{ + ALOGV("AUDIOMIXER::unprepareForAdjustChannelsNonDestructive"); + if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { + mAdjustChannelsNonDestructiveBufferProvider.reset(nullptr); + reconfigureBufferProviders(); + } +} + +status_t AudioMixer::Track::prepareForAdjustChannelsNonDestructive(size_t frames) +{ + ALOGV("AudioMixer::prepareForAdjustChannelsNonDestructive(%p) with inChannelCount: %u, " + "outChannelCount: %u, keepContractedChannels: %d", + this, mAdjustNonDestructiveInChannelCount, mAdjustNonDestructiveOutChannelCount, + mKeepContractedChannels); + unprepareForAdjustChannelsNonDestructive(); + if (mAdjustNonDestructiveInChannelCount != mAdjustNonDestructiveOutChannelCount) { + uint8_t* buffer = mKeepContractedChannels + ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame( + mMixerChannelCount, mMixerFormat) + : NULL; + mAdjustChannelsNonDestructiveBufferProvider.reset( + new AdjustChannelsNonDestructiveBufferProvider( + mFormat, + mAdjustNonDestructiveInChannelCount, + mAdjustNonDestructiveOutChannelCount, + mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID, + frames, + buffer)); + reconfigureBufferProviders(); + } + return NO_ERROR; +} + +void AudioMixer::Track::clearContractedBuffer() +{ + if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { + static_cast( + mAdjustChannelsNonDestructiveBufferProvider.get())->clearContractedFrames(); + } +} + void AudioMixer::Track::reconfigureBufferProviders() { // configure from upstream to downstream buffer providers. bufferProvider = mInputBufferProvider; + if (mAdjustChannelsBufferProvider.get() != nullptr) { + mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mAdjustChannelsBufferProvider.get(); + } + if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { + mAdjustChannelsNonDestructiveBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mAdjustChannelsNonDestructiveBufferProvider.get(); + } if (mReformatBufferProvider.get() != nullptr) { mReformatBufferProvider->setBufferProvider(bufferProvider); bufferProvider = mReformatBufferProvider.get(); @@ -542,6 +625,9 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) if (track->mainBuffer != valueBuf) { track->mainBuffer = valueBuf; ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf); + if (track->mKeepContractedChannels) { + track->prepareForAdjustChannelsNonDestructive(mFrameCount); + } invalidate(); } break; @@ -571,6 +657,9 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) if (track->mMixerFormat != format) { track->mMixerFormat = format; ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format); + if (track->mKeepContractedChannels) { + track->prepareForAdjustChannelsNonDestructive(mFrameCount); + } } } break; case MIXER_CHANNEL_MASK: { @@ -823,6 +912,10 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider track->mDownmixerBufferProvider->reset(); } else if (track->mReformatBufferProvider.get() != nullptr) { track->mReformatBufferProvider->reset(); + } else if (track->mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { + track->mAdjustChannelsNonDestructiveBufferProvider->reset(); + } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) { + track->mAdjustChannelsBufferProvider->reset(); } track->mInputBufferProvider = bufferProvider; diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp index 2d9e1cb16b..8b9ee0b0ab 100644 --- a/media/libaudioprocessing/BufferProviders.cpp +++ b/media/libaudioprocessing/BufferProviders.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -609,5 +610,83 @@ void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames } } } + +AdjustChannelsBufferProvider::AdjustChannelsBufferProvider(audio_format_t format, + size_t inChannelCount, size_t outChannelCount, size_t frameCount) : + CopyBufferProvider( + audio_bytes_per_frame(inChannelCount, format), + audio_bytes_per_frame(outChannelCount, format), + frameCount), + mFormat(format), + mInChannelCount(inChannelCount), + mOutChannelCount(outChannelCount), + mSampleSizeInBytes(audio_bytes_per_sample(format)) +{ + ALOGV("AdjustBufferProvider(%p)(%#x, %zu, %zu, %zu)", + this, format, inChannelCount, outChannelCount, frameCount); +} + +void AdjustChannelsBufferProvider::copyFrames(void *dst, const void *src, size_t frames) +{ + adjust_channels(src, mInChannelCount, dst, mOutChannelCount, mSampleSizeInBytes, + frames * mInChannelCount * mSampleSizeInBytes); +} + +AdjustChannelsNonDestructiveBufferProvider::AdjustChannelsNonDestructiveBufferProvider( + audio_format_t format, size_t inChannelCount, size_t outChannelCount, + audio_format_t contractedFormat, size_t contractedFrameCount, void* contractedBuffer) : + CopyBufferProvider( + audio_bytes_per_frame(inChannelCount, format), + audio_bytes_per_frame(outChannelCount, format), + 0 /*bufferFrameCount*/), + mFormat(format), + mInChannelCount(inChannelCount), + mOutChannelCount(outChannelCount), + mSampleSizeInBytes(audio_bytes_per_sample(format)), + mContractedChannelCount(inChannelCount - outChannelCount), + mContractedFormat(contractedFormat), + mContractedFrameCount(contractedFrameCount), + mContractedBuffer(contractedBuffer), + mContractedWrittenFrames(0) +{ + ALOGV("AdjustChannelsNonDestructiveBufferProvider(%p)(%#x, %zu, %zu, %#x, %p)", + this, format, inChannelCount, outChannelCount, contractedFormat, contractedBuffer); + if (mContractedFormat != AUDIO_FORMAT_INVALID && mInChannelCount > mOutChannelCount) { + mContractedFrameSize = audio_bytes_per_frame(mContractedChannelCount, mContractedFormat); + } +} + +status_t AdjustChannelsNonDestructiveBufferProvider::getNextBuffer( + AudioBufferProvider::Buffer* pBuffer) +{ + const size_t outFramesLeft = mContractedFrameCount - mContractedWrittenFrames; + if (outFramesLeft < pBuffer->frameCount) { + // Restrict the frame count so that we don't write over the size of the output buffer. + pBuffer->frameCount = outFramesLeft; + } + return CopyBufferProvider::getNextBuffer(pBuffer); +} + +void AdjustChannelsNonDestructiveBufferProvider::copyFrames( + void *dst, const void *src, size_t frames) +{ + adjust_channels_non_destructive(src, mInChannelCount, dst, mOutChannelCount, mSampleSizeInBytes, + frames * mInChannelCount * mSampleSizeInBytes); + if (mContractedFormat != AUDIO_FORMAT_INVALID && mContractedBuffer != NULL + && mInChannelCount > mOutChannelCount) { + const size_t contractedIdx = frames * mOutChannelCount * mSampleSizeInBytes; + memcpy_by_audio_format( + (uint8_t*)mContractedBuffer + mContractedWrittenFrames * mContractedFrameSize, + mContractedFormat, (uint8_t*)dst + contractedIdx, mFormat, + mContractedChannelCount * frames); + mContractedWrittenFrames += frames; + } +} + +void AdjustChannelsNonDestructiveBufferProvider::reset() +{ + mContractedWrittenFrames = 0; + CopyBufferProvider::reset(); +} // ---------------------------------------------------------------------------- } // namespace android diff --git a/media/libmedia/include/media/BufferProviders.h b/media/libmedia/include/media/BufferProviders.h index d6a9cfbb99..38603e3d26 100644 --- a/media/libmedia/include/media/BufferProviders.h +++ b/media/libmedia/include/media/BufferProviders.h @@ -216,6 +216,53 @@ private: bool mAudioPlaybackRateValid; // flag for current parameters validity }; +// AdjustBufferProvider derives from CopyBufferProvider to adjust sample data. +// Expands or contracts sample data from one interleaved channel format to another. +// Expanded channels are filled with zeros and put at the end of each audio frame. +// Contracted channels are omitted from the end of each audio frame. +class AdjustChannelsBufferProvider : public CopyBufferProvider { +public: + AdjustChannelsBufferProvider(audio_format_t format, size_t inChannelCount, + size_t outChannelCount, size_t frameCount); + //Overrides + void copyFrames(void *dst, const void *src, size_t frames) override; + +protected: + const audio_format_t mFormat; + const size_t mInChannelCount; + const size_t mOutChannelCount; + const size_t mSampleSizeInBytes; +}; + +// AdjustChannelsNonDestructiveBufferProvider derives from CopyBufferProvider to adjust sample data. +// Expands or contracts sample data from one interleaved channel format to another. +// Extra expanded channels are interleaved in from the end of the input buffer. +// Contracted channels are copied to the end of the output buffer. +// Contracted channels could be written to output buffer. +class AdjustChannelsNonDestructiveBufferProvider : public CopyBufferProvider { +public: + AdjustChannelsNonDestructiveBufferProvider(audio_format_t format, size_t inChannelCount, + size_t outChannelCount, audio_format_t contractedFormat, size_t contractedFrameCount, + void* contractedBuffer); + //Overrides + status_t getNextBuffer(Buffer* pBuffer) override; + void copyFrames(void *dst, const void *src, size_t frames) override; + void reset() override; + + void clearContractedFrames() { mContractedWrittenFrames = 0; } + +protected: + const audio_format_t mFormat; + const size_t mInChannelCount; + const size_t mOutChannelCount; + const size_t mSampleSizeInBytes; + const size_t mContractedChannelCount; + const audio_format_t mContractedFormat; + const size_t mContractedFrameCount; + void *mContractedBuffer; + size_t mContractedWrittenFrames; + size_t mContractedFrameSize; +}; // ---------------------------------------------------------------------------- } // namespace android -- GitLab From 245cdd91559873aeacf21b7c780655ef43c01682 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 7 Dec 2018 17:55:15 -0800 Subject: [PATCH 0599/1530] Support audio-haptic coupled playback. When trying to play with haptic channel mask, use adjust channels buffer provider to make the haptic channel the same as the output one. If haptic playback is supported, use adjust channel non destructive buffer provider to output haptic data to the end of the sink buffer. Otherwise, haptic data will be ignored. Test: Manually Bug: 111454766 Change-Id: Ic5f780de48c1e71de6ba5c4774d1ed2e9c8c51a0 --- .../libaudioclient/include/media/AudioMixer.h | 8 ++ media/libaudioprocessing/AudioMixer.cpp | 62 +++++++++-- services/audioflinger/FastMixer.cpp | 24 +++- services/audioflinger/FastMixer.h | 2 + services/audioflinger/FastMixerState.h | 4 + services/audioflinger/PlaybackTracks.h | 10 ++ services/audioflinger/Threads.cpp | 103 ++++++++++++++++-- services/audioflinger/Threads.h | 5 + 8 files changed, 196 insertions(+), 22 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 0532232073..3ae7104459 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -78,6 +78,8 @@ public: DOWNMIX_TYPE = 0X4004, MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT) MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output + // for haptic + HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not. // for target RESAMPLE SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; // parameter 'value' is the new sample rate in Hz. @@ -329,6 +331,7 @@ private: * 7) mTimestretchBufferProvider: Adds timestretching for playback rate */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. + // TODO: combine AdjustChannelsBufferProvider and AdjustChannelsNonDestructiveBufferProvider std::unique_ptr mAdjustChannelsBufferProvider; std::unique_ptr mAdjustChannelsNonDestructiveBufferProvider; std::unique_ptr mReformatBufferProvider; @@ -360,6 +363,11 @@ private: AudioPlaybackRate mPlaybackRate; // Haptic + bool mHapticPlaybackEnabled; + audio_channel_mask_t mHapticChannelMask; + uint32_t mHapticChannelCount; + audio_channel_mask_t mMixerHapticChannelMask; + uint32_t mMixerHapticChannelCount; uint32_t mAdjustInChannelCount; uint32_t mAdjustOutChannelCount; uint32_t mAdjustNonDestructiveInChannelCount; diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 365af75c1c..2567b3b238 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -136,6 +136,9 @@ status_t AudioMixer::create( // no initialization needed // t->frameCount + t->mHapticChannelMask = channelMask & AUDIO_CHANNEL_HAPTIC_ALL; + t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask); + channelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; t->channelCount = audio_channel_count_from_out_mask(channelMask); t->enabled = false; ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO, @@ -163,10 +166,13 @@ status_t AudioMixer::create( t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // haptic - t->mAdjustInChannelCount = 0; - t->mAdjustOutChannelCount = 0; - t->mAdjustNonDestructiveInChannelCount = 0; - t->mAdjustNonDestructiveOutChannelCount = 0; + t->mHapticPlaybackEnabled = false; + t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE; + t->mMixerHapticChannelCount = 0; + t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount; + t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount; + t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount; + t->mAdjustNonDestructiveOutChannelCount = t->channelCount; t->mKeepContractedChannels = false; // Check the downmixing (or upmixing) requirements. status_t status = t->prepareForDownmix(); @@ -193,13 +199,20 @@ bool AudioMixer::setChannelMasks(int name, LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); const std::shared_ptr &track = mTracks[name]; - if (trackChannelMask == track->channelMask - && mixerChannelMask == track->mMixerChannelMask) { + if (trackChannelMask == (track->channelMask | track->mHapticChannelMask) + && mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) { return false; // no need to change } + const audio_channel_mask_t hapticChannelMask = trackChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + trackChannelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; + const audio_channel_mask_t mixerHapticChannelMask = mixerChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + mixerChannelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; // always recompute for both channel masks even if only one has changed. const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask); const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask); + const uint32_t hapticChannelCount = audio_channel_count_from_out_mask(hapticChannelMask); + const uint32_t mixerHapticChannelCount = + audio_channel_count_from_out_mask(mixerHapticChannelMask); ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && trackChannelCount @@ -208,6 +221,24 @@ bool AudioMixer::setChannelMasks(int name, track->channelCount = trackChannelCount; track->mMixerChannelMask = mixerChannelMask; track->mMixerChannelCount = mixerChannelCount; + track->mHapticChannelMask = hapticChannelMask; + track->mHapticChannelCount = hapticChannelCount; + track->mMixerHapticChannelMask = mixerHapticChannelMask; + track->mMixerHapticChannelCount = mixerHapticChannelCount; + + if (track->mHapticChannelCount > 0) { + track->mAdjustInChannelCount = track->channelCount + track->mHapticChannelCount; + track->mAdjustOutChannelCount = track->channelCount + track->mMixerHapticChannelCount; + track->mAdjustNonDestructiveInChannelCount = track->mAdjustOutChannelCount; + track->mAdjustNonDestructiveOutChannelCount = track->channelCount; + track->mKeepContractedChannels = track->mHapticPlaybackEnabled; + } else { + track->mAdjustInChannelCount = 0; + track->mAdjustOutChannelCount = 0; + track->mAdjustNonDestructiveInChannelCount = 0; + track->mAdjustNonDestructiveOutChannelCount = 0; + track->mKeepContractedChannels = false; + } // channel masks have changed, does this track need a downmixer? // update to try using our desired format (if we aren't already using it) @@ -616,7 +647,8 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case CHANNEL_MASK: { const audio_channel_mask_t trackChannelMask = static_cast(valueInt); - if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) { + if (setChannelMasks(name, trackChannelMask, + (track->mMixerChannelMask | track->mMixerHapticChannelMask))) { ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask); invalidate(); } @@ -665,11 +697,21 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case MIXER_CHANNEL_MASK: { const audio_channel_mask_t mixerChannelMask = static_cast(valueInt); - if (setChannelMasks(name, track->channelMask, mixerChannelMask)) { + if (setChannelMasks(name, track->channelMask | track->mHapticChannelMask, + mixerChannelMask)) { ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask); invalidate(); } } break; + case HAPTIC_ENABLED: { + const bool hapticPlaybackEnabled = static_cast(valueInt); + if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) { + track->mHapticPlaybackEnabled = hapticPlaybackEnabled; + track->mKeepContractedChannels = hapticPlaybackEnabled; + track->prepareForAdjustChannelsNonDestructive(mFrameCount); + track->prepareForAdjustChannels(); + } + } break; default: LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } @@ -1359,8 +1401,8 @@ void AudioMixer::process__nop() const std::shared_ptr &t = mTracks[group[0]]; memset(t->mainBuffer, 0, - mFrameCount * t->mMixerChannelCount - * audio_bytes_per_sample(t->mMixerFormat)); + mFrameCount * audio_bytes_per_frame( + t->mMixerChannelCount + t->mMixerHapticChannelCount, t->mMixerFormat)); // now consume data for (const int name : group) { diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index d15841fcf2..f328577456 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -37,8 +37,9 @@ #include #endif #endif -#include +#include #include +#include #include #include "FastMixer.h" #include "TypedLogger.h" @@ -159,20 +160,24 @@ void FastMixer::onStateChange() if (current->mOutputSinkGen != mOutputSinkGen) { mOutputSink = current->mOutputSink; mOutputSinkGen = current->mOutputSinkGen; + mSinkChannelMask = current->mSinkChannelMask; if (mOutputSink == NULL) { mFormat = Format_Invalid; mSampleRate = 0; mSinkChannelCount = 0; mSinkChannelMask = AUDIO_CHANNEL_NONE; + mAudioChannelCount = 0; } else { mFormat = mOutputSink->format(); mSampleRate = Format_sampleRate(mFormat); mSinkChannelCount = Format_channelCount(mFormat); LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); - // TODO: Add channel mask to NBAIO_Format - // We assume that the channel mask must be a valid positional channel mask. - mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); + if (mSinkChannelMask == AUDIO_CHANNEL_NONE) { + mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); + } + mAudioChannelCount = mSinkChannelCount - audio_channel_count_from_out_mask( + mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL); } dumpState->mSampleRate = mSampleRate; } @@ -288,6 +293,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)fastTrack->mChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, + (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); mMixer->enable(name); } mGenerations[i] = fastTrack->mGeneration; @@ -324,6 +331,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)fastTrack->mChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, + (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); // already enabled } mGenerations[i] = fastTrack->mGeneration; @@ -468,6 +477,13 @@ void FastMixer::onWork() memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, frameCount * Format_channelCount(mFormat)); } + if (mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) { + // When there are haptic channels, the sample data is partially interleaved. + // Make the sample data fully interleaved here. + adjust_channels_non_destructive(buffer, mAudioChannelCount, buffer, mSinkChannelCount, + audio_bytes_per_sample(mFormat.mFormat), + frameCount * audio_bytes_per_frame(mAudioChannelCount, mFormat.mFormat)); + } // if non-NULL, then duplicate write() to this non-blocking sink #ifdef TEE_SINK mTee.write(buffer, frameCount); diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 1c86d9ae7d..1d332e01af 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -76,6 +76,8 @@ private: size_t mMixerBufferSize; audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + uint32_t mAudioChannelCount; // audio channel count, excludes haptic channels. + enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; NBAIO_Format mFormat; unsigned mSampleRate; diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index c7fcbd83ce..9d2a733670 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -47,6 +47,7 @@ struct FastTrack { audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned + bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not }; // Represents a single state of the fast mixer @@ -69,6 +70,9 @@ struct FastMixerState : FastThreadState { NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated int mOutputSinkGen; // increment when mOutputSink is assigned size_t mFrameCount; // number of frames per fast mix buffer + audio_channel_mask_t mSinkChannelMask; // If not AUDIO_CHANNEL_NONE, specifies sink channel + // mask when it cannot be directly calculated from + // channel count // Extends FastThreadState::Command static const Command diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 971f6a5077..d9f570dfe3 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -112,6 +112,14 @@ public: /** Copy the track metadata in the provided iterator. Thread safe. */ virtual void copyMetadataTo(MetadataInserter& backInserter) const; + /** Return haptic playback of the track is enabled or not, used in mixer. */ + bool getHapticPlaybackEnabled() const { return mHapticPlaybackEnabled; } + /** Set haptic playback of the track is enabled or not, should be + * set after query or get callback from vibrator service */ + void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) { + mHapticPlaybackEnabled = hapticPlaybackEnabled; + } + protected: // for numerous friend class PlaybackThread; @@ -188,6 +196,8 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations + bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not + private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 46e40c75ef..1fdb27e88e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -751,6 +752,7 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { audio_channel_mask_get_representation(mask); switch (representation) { + // Travel all single bit channel mask to convert channel mask to string. case AUDIO_CHANNEL_REPRESENTATION_POSITION: { if (output) { if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, "); @@ -773,6 +775,8 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " ); if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT) s.append("top-side-left, " ); if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT) s.append("top-side-right, " ); + if (mask & AUDIO_CHANNEL_OUT_HAPTIC_B) s.append("haptic-B, " ); + if (mask & AUDIO_CHANNEL_OUT_HAPTIC_A) s.append("haptic-A, " ); if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, "); } else { if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, "); @@ -1845,6 +1849,10 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector& dumpBase(fd, args); dprintf(fd, " Master mute: %s\n", mMasterMute ? "on" : "off"); + if (mHapticChannelMask != AUDIO_CHANNEL_NONE) { + dprintf(fd, " Haptic channel mask: %#x (%s)\n", mHapticChannelMask, + channelMaskToString(mHapticChannelMask, true /* output */).c_str()); + } dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); dprintf(fd, " Last write occurred (msecs): %llu\n", (unsigned long long) ns2ms(systemTime() - mLastWriteTime)); @@ -1946,7 +1954,7 @@ sp AudioFlinger::PlaybackThread::createTrac audio_is_linear_pcm(format) && // TODO: extract as a data library function that checks that a computationally // expensive downmixer is not required: isFastOutputChannelConversion() - (channelMask == mChannelMask || + (channelMask == (mChannelMask | mHapticChannelMask) || mChannelMask != AUDIO_CHANNEL_OUT_STEREO || (channelMask == AUDIO_CHANNEL_OUT_MONO /* && mChannelMask == AUDIO_CHANNEL_OUT_STEREO */)) && @@ -2348,6 +2356,17 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING; } + // Disable all haptic playback for all other active tracks when haptic playback is supported + // and the track contains haptic channels. Enable haptic playback for current track. + // TODO: Request actual haptic playback status from vibrator service + if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE + && mHapticChannelMask != AUDIO_CHANNEL_NONE) { + for (auto &t : mActiveTracks) { + t->setHapticPlaybackEnabled(false); + } + track->setHapticPlaybackEnabled(true); + } + track->mResetDone = false; track->mPresentationCompleteFrames = 0; mActiveTracks.add(track); @@ -2635,6 +2654,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() (void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize); } + mHapticChannelMask = mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + mChannelMask &= ~mHapticChannelMask; + mHapticChannelCount = audio_channel_count_from_out_mask(mHapticChannelMask); + mChannelCount -= mHapticChannelCount; + // force reconfiguration of effect chains and engines to take new buffer size and audio // parameters into account // Note that mLock is not held when readOutputParameters_l() is called from the constructor @@ -3007,7 +3031,7 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp& c // Only one effect chain can be present in direct output thread and it uses // the sink buffer as input if (mType != DIRECT) { - size_t numSamples = mNormalFrameCount * mChannelCount; + size_t numSamples = mNormalFrameCount * (mChannelCount + mHapticChannelCount); status_t result = mAudioFlinger->mEffectsFactoryHal->allocateBuffer( numSamples * sizeof(effect_buffer_t), &halInBuffer); @@ -3506,7 +3530,17 @@ bool AudioFlinger::PlaybackThread::threadLoop() } memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, - mNormalFrameCount * mChannelCount); + mNormalFrameCount * (mChannelCount + mHapticChannelCount)); + + // If we're going directly to the sink and there are haptic channels, + // we should adjust channels as the sample data is partially interleaved + // in this case. + if (!mEffectBufferValid && mHapticChannelCount > 0) { + adjust_channels_non_destructive(buffer, mChannelCount, buffer, + mChannelCount + mHapticChannelCount, + audio_bytes_per_sample(format), + audio_bytes_per_frame(mChannelCount, format) * mNormalFrameCount); + } } mBytesRemaining = mCurrentWriteLength; @@ -3550,7 +3584,15 @@ bool AudioFlinger::PlaybackThread::threadLoop() } memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, - mNormalFrameCount * mChannelCount); + mNormalFrameCount * (mChannelCount + mHapticChannelCount)); + // The sample data is partially interleaved when haptic channels exist, + // we need to adjust channels here. + if (mHapticChannelCount > 0) { + adjust_channels_non_destructive(mSinkBuffer, mChannelCount, mSinkBuffer, + mChannelCount + mHapticChannelCount, + audio_bytes_per_sample(mFormat), + audio_bytes_per_frame(mChannelCount, mFormat) * mNormalFrameCount); + } } // enable changes in effect chain @@ -3716,6 +3758,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() // removeTracks_l() must be called with ThreadBase::mLock held void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tracksToRemove) { + bool enabledHapticTracksRemoved = false; for (const auto& track : tracksToRemove) { mActiveTracks.remove(track); ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId()); @@ -3737,6 +3780,18 @@ void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tra // remove from our tracks vector removeTrack_l(track); } + enabledHapticTracksRemoved |= track->getHapticPlaybackEnabled(); + } + // If the thread supports haptic playback and the track playing haptic data was removed, + // enable haptic playback on the first active track that contains haptic channels. + // TODO: Query vibrator service to know which track should enable haptic playback. + if (enabledHapticTracksRemoved && mHapticChannelMask != AUDIO_CHANNEL_NONE) { + for (auto &t : mActiveTracks) { + if (t->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) { + t->setHapticPlaybackEnabled(true); + break; + } + } } } @@ -3942,7 +3997,8 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // create an NBAIO sink for the HAL output stream, and negotiate mOutputSink = new AudioStreamOutSink(output->stream); size_t numCounterOffers = 0; - const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; + const NBAIO_Format offers[1] = {Format_from_SR_C( + mSampleRate, mChannelCount + mHapticChannelCount, mFormat)}; #if !LOG_NDEBUG ssize_t index = #else @@ -3984,7 +4040,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // change our Sink format to accept our intermediate precision mFormat = fastMixerFormat; free(mSinkBuffer); - mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); + mFrameSize = audio_bytes_per_frame(mChannelCount + mHapticChannelCount, mFormat); const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize); } @@ -4026,8 +4082,10 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // wrap the source side of the MonoPipe to make it an AudioBufferProvider fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe)); fastTrack->mVolumeProvider = NULL; - fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer + fastTrack->mChannelMask = mChannelMask | mHapticChannelMask; // mPipeSink channel mask for + // audio to FastMixer fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer + fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE; fastTrack->mGeneration++; state->mFastTracksGen++; state->mTrackMask = 1; @@ -4035,6 +4093,10 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud state->mOutputSink = mOutputSink.get(); state->mOutputSinkGen++; state->mFrameCount = mFrameCount; + // specify sink channel mask when haptic channel mask present as it can not + // be calculated directly from channel count + state->mSinkChannelMask = mHapticChannelMask == AUDIO_CHANNEL_NONE + ? AUDIO_CHANNEL_NONE : mChannelMask | mHapticChannelMask; state->mCommand = FastMixerState::COLD_IDLE; // already done in constructor initialization list //mFastMixerFutex = 0; @@ -4411,6 +4473,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac std::vector, size_t>> mUnderrunFrames; } deferredOperations(&mixerStatus); // implicit nested scope for variable capture + bool noFastHapticTrack = true; for (size_t i=0 ; i t = mActiveTracks[i]; @@ -4419,6 +4482,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // process fast tracks if (track->isFastTrack()) { + if (track->getHapticPlaybackEnabled()) { + noFastHapticTrack = false; + } // It's theoretically possible (though unlikely) for a fast track to be created // and then removed within the same normal mix cycle. This is not a problem, as @@ -4544,6 +4610,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mVolumeProvider = vp; fastTrack->mChannelMask = track->mChannelMask; fastTrack->mFormat = track->mFormat; + fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -4589,6 +4656,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // Avoids a misleading display in dumpsys track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL; } + if (fastTrack->mHapticPlaybackEnabled != track->getHapticPlaybackEnabled()) { + fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); + didModify = true; + } continue; } @@ -4796,7 +4867,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac mAudioMixer->setParameter( trackId, AudioMixer::TRACK, - AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask); + AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)(mChannelMask | mHapticChannelMask)); // limit track sample rate to 2 x output sample rate, which changes at re-configuration uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX; uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate(); @@ -4857,6 +4929,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac trackId, AudioMixer::TRACK, AudioMixer::AUX_BUFFER, (void *)track->auxBuffer()); + mAudioMixer->setParameter( + trackId, + AudioMixer::TRACK, + AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled()); // reset retry count track->mRetryCount = kMaxTrackRetries; @@ -4924,6 +5000,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } + if (mHapticChannelMask != AUDIO_CHANNEL_NONE && sq != NULL) { + // When there is no fast track playing haptic and FastMixer exists, + // enabling the first FastTrack, which provides mixed data from normal + // tracks, to play haptic data. + FastTrack *fastTrack = &state->mFastTracks[0]; + if (fastTrack->mHapticPlaybackEnabled != noFastHapticTrack) { + fastTrack->mHapticPlaybackEnabled = noFastHapticTrack; + didModify = true; + } + } + // Push the new FastMixer state if necessary bool pauseAudioWatchdog = false; if (didModify) { diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 7f3ea0f4e7..e8b2158e71 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -907,6 +907,11 @@ protected: int64_t mBytesWritten; int64_t mFramesWritten; // not reset on standby int64_t mSuspendedFrames; // not reset on standby + + // mHapticChannelMask and mHapticChannelCount will only be valid when the thread support + // haptic playback. + audio_channel_mask_t mHapticChannelMask = AUDIO_CHANNEL_NONE; + uint32_t mHapticChannelCount = 0; private: // mMasterMute is in both PlaybackThread and in AudioFlinger. When a // PlaybackThread needs to find out if master-muted, it checks it's local -- GitLab From f652118da25973feedf3593f50a0854d97e951bd Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 10 Dec 2018 19:04:01 -0800 Subject: [PATCH 0600/1530] Move WAV/FLAC/MIDI/AAC/MP3/AMR extractors to APEX Test: build & boot & dumpsys media.extractor Bug: 112766425 Change-Id: I6207c370f830e85830115787a0d5b2958a53869d --- apex/Android.bp | 6 ------ services/mediaextractor/Android.mk | 6 ------ 2 files changed, 12 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 575603f5a8..eee26aec7d 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -21,14 +21,8 @@ apex { "libamrextractor", "libflacextractor", "libmidiextractor", - "libmkvextractor", "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", "libwavextractor", - // MediaPlayer2 - "libmedia2_jni", ], key: "com.android.media.key", } diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 19ce7e9af8..7c9c727589 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -40,16 +40,10 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy # extractor libraries LOCAL_REQUIRED_MODULES += \ - libaacextractor \ - libamrextractor \ - libflacextractor \ - libmidiextractor \ libmkvextractor \ - libmp3extractor \ libmp4extractor \ libmpeg2extractor \ liboggextractor \ - libwavextractor \ LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ -- GitLab From bb93dfb7823e6abe4d4d7862559599803da1ad01 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Tue, 23 Oct 2018 13:54:22 -0700 Subject: [PATCH 0601/1530] In favor of SW patch in AudioPolicyManager::connectAudioSource In case the dynamic routing rule is present, creates the SW patch from audio source device to Android mixer (specified by dynamic routing rule) A typical use case is to route broadcast radio to the same media output as other media applications. Bug: 118763832 Test: connectAudioSource from FM Radio and dumpsys media.audio_flinger Change-Id: Id07d3a6dd9fc5f322f4eb38efe02c302018253cd --- .../managerdefault/AudioPolicyManager.cpp | 153 ++++++++++++------ .../managerdefault/AudioPolicyManager.h | 15 ++ 2 files changed, 116 insertions(+), 52 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index f07b797ad9..02f6f5a2b4 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -780,17 +780,39 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) return output; } -status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, - audio_io_handle_t *output, - audio_session_t session, - audio_stream_type_t *stream, - uid_t uid, - const audio_config_t *config, - audio_output_flags_t *flags, - audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) +status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr, + const audio_attributes_t *srcAttr, + audio_stream_type_t srcStream) +{ + if (srcAttr != NULL) { + if (!isValidAttributes(srcAttr)) { + ALOGE("%s invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", + __func__, + srcAttr->usage, srcAttr->content_type, srcAttr->flags, + srcAttr->tags); + return BAD_VALUE; + } + *dstAttr = *srcAttr; + } else { + if (srcStream < AUDIO_STREAM_MIN || srcStream >= AUDIO_STREAM_PUBLIC_CNT) { + ALOGE("%s: invalid stream type", __func__); + return BAD_VALUE; + } + stream_type_to_audio_attributes(srcStream, dstAttr); + } + return NO_ERROR; +} + +status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, + audio_io_handle_t *output, + audio_session_t session, + const audio_attributes_t *attr, + audio_stream_type_t *stream, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t *flags, + audio_port_handle_t *selectedDeviceId) { - audio_attributes_t attributes; DeviceVector outputDevices; routing_strategy strategy; audio_devices_t device; @@ -798,35 +820,20 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, audio_devices_t msdDevice = getModuleDeviceTypes(mAvailableOutputDevices, AUDIO_HARDWARE_MODULE_ID_MSD); - // The supplied portId must be AUDIO_PORT_HANDLE_NONE - if (*portId != AUDIO_PORT_HANDLE_NONE) { - return INVALID_OPERATION; - } - - if (attr != NULL) { - if (!isValidAttributes(attr)) { - ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]", - attr->usage, attr->content_type, attr->flags, - attr->tags); - return BAD_VALUE; - } - attributes = *attr; - } else { - if (*stream < AUDIO_STREAM_MIN || *stream >= AUDIO_STREAM_PUBLIC_CNT) { - ALOGE("getOutputForAttr(): invalid stream type"); - return BAD_VALUE; - } - stream_type_to_audio_attributes(*stream, &attributes); + status_t status = getAudioAttributes(resultAttr, attr, *stream); + if (status != NO_ERROR) { + return status; } - ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" + ALOGV("%s usage=%d, content=%d, tag=%s flags=%08x" " session %d selectedDeviceId %d", - attributes.usage, attributes.content_type, attributes.tags, attributes.flags, + __func__, + resultAttr->usage, resultAttr->content_type, resultAttr->tags, resultAttr->flags, session, requestedDeviceId); - *stream = streamTypefromAttributesInt(&attributes); + *stream = streamTypefromAttributesInt(resultAttr); - strategy = getStrategyForAttr(&attributes); + strategy = getStrategyForAttr(resultAttr); // First check for explicit routing (eg. setPreferredDevice) if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { @@ -836,30 +843,30 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } else { // If no explict route, is there a matching dynamic policy that applies? sp desc; - if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) { + if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) { ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); if (!audio_has_proportional_frames(config->format)) { return BAD_VALUE; } - *stream = streamTypefromAttributesInt(&attributes); + *stream = streamTypefromAttributesInt(resultAttr); *output = desc->mIoHandle; AudioMix *mix = desc->mPolicyMix; sp deviceDesc = mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress); *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE; - ALOGV("getOutputForAttr() returns output %d", *output); - goto exit; + ALOGV("%s returns output %d", __func__, *output); + return NO_ERROR; } // Virtual sources must always be dynamicaly or explicitly routed - if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { - ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); + if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) { + ALOGW("%s no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE", __func__); return BAD_VALUE; } device = getDeviceForStrategy(strategy, false /*fromCache*/); } - if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + if ((resultAttr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); } @@ -869,7 +876,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, // to getOutputForDevice. // TODO: Remove check of AUDIO_STREAM_MUSIC once migration is completed on the app side. if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX && - (*stream == AUDIO_STREAM_MUSIC || attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) && + (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) && audio_is_linear_pcm(config->format) && isInCall()) { if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { @@ -880,9 +887,9 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } } - ALOGV("getOutputForAttr() device 0x%x, sampling rate %d, format %#x, channel mask %#x, " + ALOGV("%s device 0x%x, sampling rate %d, format %#x, channel mask %#x, " "flags %#x", - device, config->sample_rate, config->format, config->channel_mask, *flags); + __func__, device, config->sample_rate, config->format, config->channel_mask, *flags); *output = AUDIO_IO_HANDLE_NONE; if (msdDevice != AUDIO_DEVICE_NONE) { @@ -906,22 +913,48 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; -exit: + ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId); + + return NO_ERROR; +} + +status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t *flags, + audio_port_handle_t *selectedDeviceId, + audio_port_handle_t *portId) +{ + // The supplied portId must be AUDIO_PORT_HANDLE_NONE + if (*portId != AUDIO_PORT_HANDLE_NONE) { + return INVALID_OPERATION; + } + const audio_port_handle_t requestedDeviceId = *selectedDeviceId; + audio_attributes_t resultAttr; + status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid, + config, flags, selectedDeviceId); + if (status != NO_ERROR) { + return status; + } + audio_config_base_t clientConfig = {.sample_rate = config->sample_rate, .format = config->format, .channel_mask = config->channel_mask }; *portId = AudioPort::getNextUniqueId(); sp clientDesc = - new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig, + new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, requestedDeviceId, *stream, - getStrategyForAttr(&attributes), + getStrategyForAttr(&resultAttr), *flags); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); - ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d", - *output, *selectedDeviceId, *portId); + ALOGV("%s returns output %d selectedDeviceId %d for port ID %d", + __func__, *output, requestedDeviceId, *portId); return NO_ERROR; } @@ -3400,11 +3433,20 @@ status_t AudioPolicyManager::connectAudioSource(const sp srcDeviceDesc->getAudioPort()->mModule->getHalVersionMajor() >= 3 && srcDeviceDesc->getAudioPort()->mGains.size() > 0) { ALOGV("%s AUDIO_DEVICE_API_VERSION_3_0", __FUNCTION__); - // create patch between src device and output device - // create Hwoutput and add to mHwOutputs + // TODO: may explicitly specify whether we should use HW or SW patch + // create patch between src device and output device + // create Hwoutput and add to mHwOutputs } else { - SortedVector outputs = getOutputsForDevice(sinkDevice, mOutputs); - audio_io_handle_t output = selectOutput(outputs); + audio_attributes_t resultAttr; + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = sourceDesc->config().sample_rate; + config.channel_mask = sourceDesc->config().channel_mask; + config.format = sourceDesc->config().format; + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE; + getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, + &attributes, &stream, sourceDesc->uid(), &config, &flags, &selectedDeviceId); if (output == AUDIO_IO_HANDLE_NONE) { ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevice); return INVALID_OPERATION; @@ -3437,6 +3479,13 @@ status_t AudioPolicyManager::connectAudioSource(const sp __FUNCTION__, status); return INVALID_OPERATION; } + + if (outputDesc->getClient(sourceDesc->portId()) != nullptr) { + ALOGW("%s source portId has already been attached to outputDesc", __func__); + return INVALID_OPERATION; + } + outputDesc->addClient(sourceDesc); + uint32_t delayMs = 0; status = startSource(outputDesc, sourceDesc, &delayMs); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index d0708b89e5..aa7ffc8e29 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -661,6 +661,21 @@ private: const String8& address /*in*/, SortedVector& outputs /*out*/); uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; } + // internal method, get audio_attributes_t from either a source audio_attributes_t + // or audio_stream_type_t, respectively. + status_t getAudioAttributes(audio_attributes_t *dstAttr, + const audio_attributes_t *srcAttr, + audio_stream_type_t srcStream); + // internal method, called by getOutputForAttr() and connectAudioSource. + status_t getOutputForAttrInt(audio_attributes_t *resultAttr, + audio_io_handle_t *output, + audio_session_t session, + const audio_attributes_t *attr, + audio_stream_type_t *stream, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t *flags, + audio_port_handle_t *selectedDeviceId); // internal method to return the output handle for the given device and format audio_io_handle_t getOutputForDevice( audio_devices_t device, -- GitLab From 1d1da3d5be8eddef6311e2f825d58ca3ee5d0421 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 12 Dec 2018 13:07:45 -0800 Subject: [PATCH 0602/1530] Convert MatroskaExtractor to V3 format Bug: 111407253 Test: manual, CTS Change-Id: I16e1fa8296c5780165d8a434f7fbe7236036d02d --- media/extractors/mkv/Android.bp | 1 - media/extractors/mkv/MatroskaExtractor.cpp | 89 +++++++++++++--------- media/extractors/mkv/MatroskaExtractor.h | 4 +- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp index 8d028e1621..1744d3d63f 100644 --- a/media/extractors/mkv/Android.bp +++ b/media/extractors/mkv/Android.bp @@ -11,7 +11,6 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 1fdac055f4..4a307407e8 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -29,10 +29,8 @@ #include #include #include -#include #include #include -#include #include #include @@ -127,7 +125,7 @@ private: BlockIterator &operator=(const BlockIterator &); }; -struct MatroskaSource : public MediaTrackHelperV2 { +struct MatroskaSource : public MediaTrackHelperV3 { MatroskaSource(MatroskaExtractor *extractor, size_t index); virtual media_status_t start(); @@ -136,7 +134,7 @@ struct MatroskaSource : public MediaTrackHelperV2 { virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options); + MediaBufferHelperV3 **buffer, const ReadOptions *options); protected: virtual ~MatroskaSource(); @@ -156,11 +154,11 @@ private: BlockIterator mBlockIter; ssize_t mNALSizeLen; // for type AVC or HEVC - List mPendingFrames; + List mPendingFrames; status_t advance(); - status_t setWebmBlockCryptoInfo(MediaBufferBase *mbuf); + status_t setWebmBlockCryptoInfo(MediaBufferHelperV3 *mbuf); media_status_t readBlock(); void clearPendingFrames(); @@ -265,6 +263,8 @@ media_status_t MatroskaSource::start() { return AMEDIA_ERROR_MALFORMED; } + // allocate one small initial buffer, but leave plenty of room to grow + mBufferGroup->init(1 /* number of buffers */, 1024 /* buffer size */, 64 /* growth limit */); mBlockIter.reset(); return AMEDIA_OK; @@ -569,7 +569,7 @@ static AString uriDebugString(const char *uri) { void MatroskaSource::clearPendingFrames() { while (!mPendingFrames.empty()) { - MediaBufferBase *frame = *mPendingFrames.begin(); + MediaBufferHelperV3 *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); frame->release(); @@ -577,7 +577,7 @@ void MatroskaSource::clearPendingFrames() { } } -status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { +status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferHelperV3 *mbuf) { if (mbuf->range_length() < 1 || mbuf->range_length() - 1 > INT32_MAX) { // 1-byte signal return ERROR_MALFORMED; @@ -591,7 +591,7 @@ status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { return ERROR_MALFORMED; } - MetaDataBase &meta = mbuf->meta_data(); + AMediaFormat *meta = mbuf->meta_data(); if (encrypted) { uint8_t ctrCounter[16] = { 0 }; const uint8_t *keyId; @@ -599,9 +599,9 @@ status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { AMediaFormat *trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta; AMediaFormat_getBuffer(trackMeta, AMEDIAFORMAT_KEY_CRYPTO_KEY, (void**)&keyId, &keyIdSize); - meta.setData(kKeyCryptoKey, 0, keyId, keyIdSize); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_KEY, keyId, keyIdSize); memcpy(ctrCounter, data + 1, 8); - meta.setData(kKeyCryptoIV, 0, ctrCounter, 16); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_IV, ctrCounter, 16); if (partitioned) { /* 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -654,8 +654,10 @@ status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { } uint32_t sizeofPlainSizes = sizeof(uint32_t) * plainSizes.size(); uint32_t sizeofEncryptedSizes = sizeof(uint32_t) * encryptedSizes.size(); - meta.setData(kKeyPlainSizes, 0, plainSizes.data(), sizeofPlainSizes); - meta.setData(kKeyEncryptedSizes, 0, encryptedSizes.data(), sizeofEncryptedSizes); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, + plainSizes.data(), sizeofPlainSizes); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, + encryptedSizes.data(), sizeofEncryptedSizes); mbuf->set_range(frameOffset, mbuf->range_length() - frameOffset); } else { /* @@ -675,8 +677,10 @@ status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { */ int32_t plainSizes[] = { 0 }; int32_t encryptedSizes[] = { static_cast(mbuf->range_length() - 9) }; - meta.setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes)); - meta.setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes)); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, + plainSizes, sizeof(plainSizes)); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, + encryptedSizes, sizeof(encryptedSizes)); mbuf->set_range(9, mbuf->range_length() - 9); } } else { @@ -693,8 +697,10 @@ status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) { */ int32_t plainSizes[] = { static_cast(mbuf->range_length() - 1) }; int32_t encryptedSizes[] = { 0 }; - meta.setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes)); - meta.setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes)); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, + plainSizes, sizeof(plainSizes)); + AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, + encryptedSizes, sizeof(encryptedSizes)); mbuf->set_range(1, mbuf->range_length() - 1); } @@ -721,14 +727,17 @@ media_status_t MatroskaSource::readBlock() { } len += trackInfo->mHeaderLen; - MediaBufferBase *mbuf = MediaBufferBase::Create(len); + MediaBufferHelperV3 *mbuf; + mBufferGroup->acquire_buffer(&mbuf, false /* nonblocking */, len /* requested size */); + mbuf->set_range(0, len); uint8_t *data = static_cast(mbuf->data()); if (trackInfo->mHeader) { memcpy(data, trackInfo->mHeader, trackInfo->mHeaderLen); } - mbuf->meta_data().setInt64(kKeyTime, timeUs); - mbuf->meta_data().setInt32(kKeyIsSyncFrame, block->IsKey()); + AMediaFormat *meta = mbuf->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, block->IsKey()); status_t err = frame.Read(mExtractor->mReader, data + trackInfo->mHeaderLen); if (err == OK @@ -754,7 +763,7 @@ media_status_t MatroskaSource::readBlock() { } media_status_t MatroskaSource::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t targetSampleTimeUs = -1ll; @@ -790,13 +799,13 @@ media_status_t MatroskaSource::read( } } - MediaBufferBase *frame = *mPendingFrames.begin(); + MediaBufferHelperV3 *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); if ((mType != AVC && mType != HEVC) || mNALSizeLen == 0) { if (targetSampleTimeUs >= 0ll) { - frame->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64(frame->meta_data(), + AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } *out = frame; @@ -819,7 +828,7 @@ media_status_t MatroskaSource::read( size_t srcSize = frame->range_length(); size_t dstSize = 0; - MediaBufferBase *buffer = NULL; + MediaBufferHelperV3 *buffer = NULL; uint8_t *dstPtr = NULL; for (int32_t pass = 0; pass < 2; ++pass) { @@ -879,16 +888,20 @@ media_status_t MatroskaSource::read( // each 4-byte nal size with a 4-byte start code buffer = frame; } else { - buffer = MediaBufferBase::Create(dstSize); + mBufferGroup->acquire_buffer( + &buffer, false /* nonblocking */, dstSize /* requested size */); + buffer->set_range(0, dstSize); } + AMediaFormat *frameMeta = frame->meta_data(); int64_t timeUs; - CHECK(frame->meta_data().findInt64(kKeyTime, &timeUs)); + CHECK(AMediaFormat_getInt64(frameMeta, AMEDIAFORMAT_KEY_TIME_US, &timeUs)); int32_t isSync; - CHECK(frame->meta_data().findInt32(kKeyIsSyncFrame, &isSync)); + CHECK(AMediaFormat_getInt32(frameMeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, &isSync)); - buffer->meta_data().setInt64(kKeyTime, timeUs); - buffer->meta_data().setInt32(kKeyIsSyncFrame, isSync); + AMediaFormat *bufMeta = buffer->meta_data(); + AMediaFormat_setInt64(bufMeta, AMEDIAFORMAT_KEY_TIME_US, timeUs); + AMediaFormat_setInt32(bufMeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, isSync); dstPtr = (uint8_t *)buffer->data(); } @@ -900,8 +913,8 @@ media_status_t MatroskaSource::read( } if (targetSampleTimeUs >= 0ll) { - buffer->meta_data().setInt64( - kKeyTargetTime, targetSampleTimeUs); + AMediaFormat_setInt64(buffer->meta_data(), + AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } *out = buffer; @@ -992,7 +1005,7 @@ size_t MatroskaExtractor::countTracks() { return mTracks.size(); } -MediaTrackHelperV2 *MatroskaExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *MatroskaExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } @@ -1660,22 +1673,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("abbedd92-38c4-4904-a4c1-b3f45f899980"), 1, "Matroska Extractor", { - .v2 = []( + .v3 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV2 { + FreeMetaFunc *) -> CreatorFuncV3 { DataSourceHelper helper(source); if (SniffMatroska(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV2* { - return wrapV2(new MatroskaExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new MatroskaExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 2fa8881968..a09256ae40 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -35,12 +35,12 @@ class MetaData; struct DataSourceBaseReader; struct MatroskaSource; -struct MatroskaExtractor : public MediaExtractorPluginHelperV2 { +struct MatroskaExtractor : public MediaExtractorPluginHelperV3 { explicit MatroskaExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); -- GitLab From 20147323b745dcc8ee395b6815082e456ee7d1d6 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Sat, 17 Nov 2018 09:08:39 -0800 Subject: [PATCH 0603/1530] C interface to MediaMetrics C interface is more stable across upgrades than C++ and simplifies cross-package interfaces in upgradeable components. Bug: 119675363 Test: parallel metrics generation from nuplayer Change-Id: I36beeade82a208be64af0860eb6f175a2801b27f --- include/media/MediaMetrics.h | 1 + media/libmediametrics/Android.bp | 18 +- media/libmediametrics/MediaAnalyticsItem.cpp | 21 ++ media/libmediametrics/MediaMetrics.cpp | 204 ++++++++++++++++++ .../include/MediaAnalyticsItem.h | 3 + media/libmediametrics/include/MediaMetrics.h | 98 +++++++++ 6 files changed, 341 insertions(+), 4 deletions(-) create mode 120000 include/media/MediaMetrics.h create mode 100644 media/libmediametrics/MediaMetrics.cpp create mode 100644 media/libmediametrics/include/MediaMetrics.h diff --git a/include/media/MediaMetrics.h b/include/media/MediaMetrics.h new file mode 120000 index 0000000000..5f757e43ad --- /dev/null +++ b/include/media/MediaMetrics.h @@ -0,0 +1 @@ +../../media/libmediametrics/include/MediaMetrics.h \ No newline at end of file diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp index 0a342b8a40..8f8c47891e 100644 --- a/media/libmediametrics/Android.bp +++ b/media/libmediametrics/Android.bp @@ -6,15 +6,16 @@ cc_library { srcs: [ "IMediaAnalyticsService.cpp", "MediaAnalyticsItem.cpp", + "MediaMetrics.cpp", ], shared_libs: [ - "liblog", - "libcutils", - "libutils", + "libbase", "libbinder", + "libcutils", + "liblog", "libstagefright_foundation", - "libbase", + "libutils", ], export_include_dirs: ["include"], @@ -32,4 +33,13 @@ cc_library { ], cfi: true, }, + + // enumerate the stable interface +// this would mean nobody can use the C++ interface. have to rework some things. +// stubs: { +// symbol_file: "libmediametrics.map.txt", +// versions: [ +// "1" , +// ] +// }, } diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp index d3de01ea75..448e2d9959 100644 --- a/media/libmediametrics/MediaAnalyticsItem.cpp +++ b/media/libmediametrics/MediaAnalyticsItem.cpp @@ -487,6 +487,18 @@ bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) return true; } +bool MediaAnalyticsItem::getString(MediaAnalyticsItem::Attr name, std::string *value) { + Prop *prop = findProp(name); + if (prop == NULL || prop->mType != kTypeCString) { + return false; + } + if (value != NULL) { + // std::string makes a copy for us + *value = prop->u.CStringValue; + } + return true; +} + // remove indicated keys and their values // return value is # keys removed int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) { @@ -726,6 +738,15 @@ int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) { } +const char *MediaAnalyticsItem::toCString() { + return toCString(PROTO_LAST); +} + +const char * MediaAnalyticsItem::toCString(int version) { + std::string val = toString(version); + return strdup(val.c_str()); +} + std::string MediaAnalyticsItem::toString() { return toString(PROTO_LAST); } diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp new file mode 100644 index 0000000000..9b08aa74fd --- /dev/null +++ b/media/libmediametrics/MediaMetrics.cpp @@ -0,0 +1,204 @@ +/* + * 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 "MediaMetrics" + +#include +#include +#include +#include + +#include +#include + +// +// provide a C-ish interface that is easier to stabilize than the existing C++ +// interface +// +// ALL functions returning a char * give responsibility for the allocated buffer +// to the caller. The caller is responsible to call free() on that pointer. +// + +// manage the overall record +mediametrics_handle_t mediametrics_create(mediametricskey_t key) { + android::MediaAnalyticsItem *item = new android::MediaAnalyticsItem(key); + return (mediametrics_handle_t) item; +} + +void mediametrics_delete(mediametrics_handle_t handle) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return; + delete item; +} + +mediametricskey_t mediametrics_getKey(mediametrics_handle_t handle) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return NULL; + return strdup(item->getKey().c_str()); +} + +// nuplayer, et al use it when acting as proxies +void mediametrics_setUid(mediametrics_handle_t handle, uid_t uid) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setUid(uid); +} + +// set attributes +// + +void mediametrics_setInt32(mediametrics_handle_t handle, attr_t attr, + int32_t value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setInt32(attr, value); +} + +void mediametrics_setInt64(mediametrics_handle_t handle, attr_t attr, + int64_t value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setInt64(attr, value); +} + +void mediametrics_setDouble(mediametrics_handle_t handle, attr_t attr, + double value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setDouble(attr, value); +} + +void mediametrics_setRate(mediametrics_handle_t handle, attr_t attr, + int64_t count, int64_t duration) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setRate(attr, count, duration); +} + +void mediametrics_setCString(mediametrics_handle_t handle, attr_t attr, + const char *value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->setCString(attr, value); +} + +// fused get/add/set; if attr wasn't there, it's a simple set. +// + +void mediametrics_addInt32(mediametrics_handle_t handle, attr_t attr, + int32_t value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->addInt32(attr, value); +} + +void mediametrics_addInt64(mediametrics_handle_t handle, attr_t attr, + int64_t value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->addInt64(attr, value); +} + +void mediametrics_addDouble(mediametrics_handle_t handle, attr_t attr, + double value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->addDouble(attr, value); +} + +void mediametrics_addRate(mediametrics_handle_t handle, attr_t attr, + int64_t count, int64_t duration) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item != NULL) item->addRate(attr, count, duration); +} + +// find & extract values +// return indicates whether attr exists (and thus whether value filled in) +// NULL parameter value suppresses storage of value. +// + +bool mediametrics_getInt32(mediametrics_handle_t handle, attr_t attr, + int32_t * value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + return item->getInt32(attr, value); +} + +bool mediametrics_getInt64(mediametrics_handle_t handle, attr_t attr, + int64_t * value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + return item->getInt64(attr, value); +} + +bool mediametrics_getDouble(mediametrics_handle_t handle, attr_t attr, + double *value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + return item->getDouble(attr, value); +} + +bool mediametrics_getRate(mediametrics_handle_t handle, attr_t attr, + int64_t * count, int64_t * duration, double *rate) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + return item->getRate(attr, count, duration, rate); +} + +// NB: caller owns the string that comes back, is responsible for freeing it +bool mediametrics_getCString(mediametrics_handle_t handle, attr_t attr, + char **value) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + + return item->getCString(attr, value); +} + +// to release strings returned via getCString() +void mediametrics_freeCString(char *value) { + free(value); +} + +bool mediametrics_selfRecord(mediametrics_handle_t handle) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return false; + return item->selfrecord(); +} + + +const char *mediametrics_readable(mediametrics_handle_t handle) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return ""; + return item->toCString(); +} + +int32_t mediametrics_count(mediametrics_handle_t handle) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) return 0; + return item->count(); +} + +bool mediametrics_isEnabled() { + // static, so doesn't need an instance + return android::MediaAnalyticsItem::isEnabled(); +} + +#if 0 +// do not expose this as is. +// need to revisit (or redefine) how the android::Parcel parameter is handled +// so that it meets the stable-API criteria for updateable components. +// +int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel) { + android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; + if (item == NULL) { + return -1; + } + return item->writeToParcel(parcel); +} +#endif + + diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h index 263cde7858..b99cd91c1c 100644 --- a/media/libmediametrics/include/MediaAnalyticsItem.h +++ b/media/libmediametrics/include/MediaAnalyticsItem.h @@ -134,6 +134,7 @@ class MediaAnalyticsItem { bool getRate(Attr, int64_t *count, int64_t *duration, double *rate); // Caller owns the returned string bool getCString(Attr, char **value); + bool getString(Attr, std::string *value); // parameter indicates whether to close any existing open // record with same key before establishing a new record @@ -176,6 +177,8 @@ class MediaAnalyticsItem { std::string toString(); std::string toString(int version); + const char *toCString(); + const char *toCString(int version); // are we collecting analytics data static bool isEnabled(); diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/MediaMetrics.h new file mode 100644 index 0000000000..4d2f352984 --- /dev/null +++ b/media/libmediametrics/include/MediaMetrics.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEDIA_MEDIAMETRICS_H +#define ANDROID_MEDIA_MEDIAMETRICS_H + +// +// define a C interface to the media metrics functionality +// +// All functions that return a char * or const char * also give responsibility +// for that string to the caller. The caller is responsible for calling free() +// on that pointer when done using the value. + +__BEGIN_DECLS + +// internally re-cast to the behind-the-scenes C++ class instance +typedef int64_t mediametrics_handle_t; +typedef const char *mediametricskey_t; +typedef const char *attr_t; + +mediametrics_handle_t mediametrics_create(mediametricskey_t key); +void mediametrics_delete(mediametrics_handle_t handle); + +mediametricskey_t mediametrics_getKey(mediametrics_handle_t handle); + + +// set +void mediametrics_setInt32(mediametrics_handle_t handle, attr_t attr, + int32_t value); +void mediametrics_setInt64(mediametrics_handle_t handle, attr_t attr, + int64_t value); +void mediametrics_setDouble(mediametrics_handle_t handle, attr_t attr, + double value); +void mediametrics_setRate(mediametrics_handle_t handle, attr_t attr, + int64_t count, int64_t duration); +void mediametrics_setCString(mediametrics_handle_t handle, attr_t attr, + const char * value); + +// fused get/add/set; if attr wasn't there, it's a simple set. +// these do not provide atomicity or mutual exclusion, only simpler code sequences. +void mediametrics_addInt32(mediametrics_handle_t handle, attr_t attr, + int32_t value); +void mediametrics_addInt64(mediametrics_handle_t handle, attr_t attr, + int64_t value); +void mediametrics_addDouble(mediametrics_handle_t handle, attr_t attr, + double value); +void mediametrics_addRate(mediametrics_handle_t handle, attr_t attr, + int64_t count, int64_t duration); + +// find & extract values +// return indicates whether attr exists (and thus whether value filled in) +// NULL parameter value suppresses storage of value. +bool mediametrics_getInt32(mediametrics_handle_t handle, attr_t attr, + int32_t * value); +bool mediametrics_getInt64(mediametrics_handle_t handle, attr_t attr, + int64_t * value); +bool mediametrics_getDouble(mediametrics_handle_t handle, attr_t attr, + double *value); +bool mediametrics_getRate(mediametrics_handle_t handle, attr_t attr, + int64_t * count, int64_t * duration, double *rate); +bool mediametrics_getCString(mediametrics_handle_t handle, attr_t attr, + char **value); +// to release strings returned via getCString() +void mediametrics_freeCString(char *value); + +// # of attributes set within this record. +int32_t mediametrics_count(mediametrics_handle_t handle); + +bool mediametrics_selfRecord(mediametrics_handle_t handle); + +const char *mediametrics_readable(mediametrics_handle_t handle); +void mediametrics_setUid(mediametrics_handle_t handle, uid_t uid); +bool mediametrics_isEnabled(); + +#if 0 +// do not expose this as is. +// need to revisit (or redefine) how the android::Parcel parameter is handled +// so that it meets the stable-API criteria for updateable components. +// +int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel); +#endif + +__END_DECLS + +#endif -- GitLab From 6d34bed943d38b87d0644b2e77c8dea3d5854623 Mon Sep 17 00:00:00 2001 From: Hangyu Kuang Date: Tue, 4 Dec 2018 13:39:55 -0800 Subject: [PATCH 0604/1530] MPEG4Writer: Fix the bug in MPEG4 metadata track. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HACK to make the metadata track compliant with the iso standard. Metadata track is added from API 26 and the original implementation does not fully followed the TextMetaDataSampleEntry specified in ISO/IEC 14496-12-2015 in that only the mime_format is written out. content_encoding and data_reference_index have not been written out. This leads to the failure when some MP4 parser tries to parse the metadata track according to the standard. The hack here will make the metadata track compliant with the standard while still maintaining backwards compatibility. This would enable Android versions before API 29 still read out the standard compliant Metadata track generated with Android API 29 and upward. The trick is based on the fact that the Metadata track must start with prefix “application/†and those missing fields are not used in Android's Metadata track. By writting out the mime_format twice, the first mime_format will be used to fill out the missing reserved, data_reference_index and content encoding fields. On the parser side, the extracter before API 29 will read out the first mime_format correctly and drop the second mime_format. The extractor from API 29 will check if the reserved, data_reference_index and content encoding are filled with “application†to detect if this is a standard compliant metadata track and read out the data accordingly. Bug: 117950811 Test: CTS test. Change-Id: I405216c08e2f8cb28c985d9460d8cf2b16ba1931 --- media/libstagefright/MPEG4Writer.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 6ff3d78a25..a48466ade3 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -3674,6 +3674,29 @@ void MPEG4Writer::Track::writeMetadataFourCCBox() { TRESPASS(); } mOwner->beginBox(fourcc); // TextMetaDataSampleEntry + + // HACK to make the metadata track compliant with the ISO standard. + // + // Metadata track is added from API 26 and the original implementation does not + // fully followed the TextMetaDataSampleEntry specified in ISO/IEC 14496-12-2015 + // in that only the mime_format is written out. content_encoding and + // data_reference_index have not been written out. This leads to the failure + // when some MP4 parser tries to parse the metadata track according to the + // standard. The hack here will make the metadata track compliant with the + // standard while still maintaining backwards compatibility. This would enable + // Android versions before API 29 to be able to read out the standard compliant + // Metadata track generated with Android API 29 and upward. The trick is based + // on the fact that the Metadata track must start with prefix “application/†and + // those missing fields are not used in Android's Metadata track. By writting + // out the mime_format twice, the first mime_format will be used to fill out the + // missing reserved, data_reference_index and content encoding fields. On the + // parser side, the extracter before API 29 will read out the first mime_format + // correctly and drop the second mime_format. The extractor from API 29 will + // check if the reserved, data_reference_index and content encoding are filled + // with “application†to detect if this is a standard compliant metadata track + // and read out the data accordingly. + mOwner->writeCString(mime); + mOwner->writeCString(mime); // metadata mime_format mOwner->endBox(); // mett } -- GitLab From 0b490550b864b8b39a4a72c2544f33f2af03d91c Mon Sep 17 00:00:00 2001 From: Hangyu Kuang Date: Tue, 4 Dec 2018 18:21:32 -0800 Subject: [PATCH 0605/1530] MPEG4Extractor: Read out the Metadata track correctly. The metadata track before API29 is not compliant with the standard. This CL adds the support for reading out the mime_format correctly from compliant and non-compliant metadata track. Bug: 117950811 Test: CTS test. Change-Id: I9a7033549962711b625f5a008a22930691ab3e5e --- media/extractors/mp4/MPEG4Extractor.cpp | 35 +++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 9fb2e35ab3..aeccfcc169 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1556,9 +1556,40 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } - String8 mimeFormat((const char *)(buffer.get()), chunk_data_size); - AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, mimeFormat.string()); + // Prior to API 29, the metadata track was not compliant with ISO/IEC + // 14496-12-2015. This led to some ISO-compliant parsers failing to read the + // metatrack. As of API 29 and onwards, a change was made to metadata track to + // make it compliant with the standard. The workaround is to write the + // null-terminated mime_format string twice. This allows compliant parsers to + // read the missing reserved, data_reference_index, and content_encoding fields + // from the first mime_type string. The actual mime_format field would then be + // read correctly from the second string. The non-compliant Android frameworks + // from API 28 and earlier would still be able to read the mime_format correctly + // as it would only read the first null-terminated mime_format string. To enable + // reading metadata tracks generated from both the non-compliant and compliant + // formats, a check needs to be done to see which format is used. + int null_pos = 0; + const unsigned char *str = buffer.get(); + while (null_pos < chunk_data_size) { + if (*(str + null_pos) == '\0') { + break; + } + ++null_pos; + } + if (null_pos == chunk_data_size - 1) { + // This is not a standard ompliant metadata track. + String8 mimeFormat((const char *)(buffer.get()), chunk_data_size); + AMediaFormat_setString(mLastTrack->meta, + AMEDIAFORMAT_KEY_MIME, mimeFormat.string()); + } else { + // This is a standard compliant metadata track. + String8 contentEncoding((const char *)(buffer.get() + 8)); + String8 mimeFormat((const char *)(buffer.get() + 8 + contentEncoding.size() + 1), + chunk_data_size - 8 - contentEncoding.size() - 1); + AMediaFormat_setString(mLastTrack->meta, + AMEDIAFORMAT_KEY_MIME, mimeFormat.string()); + } break; } -- GitLab From 707c1468b52b683753cf5d29811d67eb42a6a8ee Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 5 Dec 2018 15:21:35 -0800 Subject: [PATCH 0606/1530] Plumbing to recognize AV1 content Recognize AV1 content in various container formats, so we can extract and pass to the AV1 codec. Bug: 111936705 Test: playback of AV1 videos Change-Id: I8a5523741481586b3569006236bd1fc92869ee76 --- cmds/stagefright/stagefright.cpp | 3 +- media/codec2/components/aom/Android.bp | 14 + media/codec2/components/aom/C2SoftAomDec.cpp | 750 ++++++++++++++++++ media/codec2/components/aom/C2SoftAomDec.h | 137 ++++ media/codec2/core/include/C2Config.h | 31 + media/codec2/sfplugin/Codec2InfoBuilder.cpp | 7 + media/codec2/sfplugin/utils/Codec2Mapper.cpp | 35 + media/codec2/vndk/C2Config.cpp | 3 + media/codec2/vndk/C2Store.cpp | 1 + media/extractors/mkv/MatroskaExtractor.cpp | 2 + media/extractors/mp4/MPEG4Extractor.cpp | 4 + media/libstagefright/foundation/MediaDefs.cpp | 1 + .../media/stagefright/foundation/MediaDefs.h | 1 + .../media/stagefright/MediaCodecConstants.h | 30 + media/libstagefright/omx/OMXUtils.cpp | 2 + services/mediacodec/registrant/Android.bp | 1 + 16 files changed, 1021 insertions(+), 1 deletion(-) create mode 100644 media/codec2/components/aom/Android.bp create mode 100644 media/codec2/components/aom/C2SoftAomDec.cpp create mode 100644 media/codec2/components/aom/C2SoftAomDec.h diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 86e90408a8..edf2a6ce86 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -651,7 +651,8 @@ static void dumpCodecProfiles(bool queryDecoders) { MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_VIDEO_HEVC, - MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4 + MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4, + MEDIA_MIMETYPE_VIDEO_AV1 }; const char *codecType = queryDecoders? "decoder" : "encoder"; diff --git a/media/codec2/components/aom/Android.bp b/media/codec2/components/aom/Android.bp new file mode 100644 index 0000000000..0fabf5cf87 --- /dev/null +++ b/media/codec2/components/aom/Android.bp @@ -0,0 +1,14 @@ +cc_library_shared { + name: "libcodec2_soft_av1dec", + defaults: [ + "libcodec2_soft-defaults", + "libcodec2_soft_sanitize_all-defaults", + ], + + srcs: ["C2SoftAomDec.cpp"], + static_libs: ["libaom"], + + include_dirs: [ + "external/libaom/", + ], +} diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp new file mode 100644 index 0000000000..6be18078d9 --- /dev/null +++ b/media/codec2/components/aom/C2SoftAomDec.cpp @@ -0,0 +1,750 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "C2SoftAomDec" +#include + +#include +#include + +#include +#include +#include + +#include "C2SoftAomDec.h" + +namespace android { + +constexpr char COMPONENT_NAME[] = "c2.android.av1.decoder"; + +class C2SoftAomDec::IntfImpl : public SimpleInterface::BaseParams { + public: + explicit IntfImpl(const std::shared_ptr& helper) + : SimpleInterface::BaseParams( + helper, COMPONENT_NAME, C2Component::KIND_DECODER, + C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) { + noPrivateBuffers(); // TODO: account for our buffers here + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + + addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) + .build()); + + addParameter( + DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) + .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240)) + .withFields({ + C2F(mSize, width).inRange(2, 2048, 2), + C2F(mSize, height).inRange(2, 2048, 2), + }) + .withSetter(SizeSetter) + .build()); + + addParameter( + DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) + .withDefault(new C2StreamProfileLevelInfo::input(0u, + C2Config::PROFILE_AV1_0, C2Config::LEVEL_AV1_2_1)) + .withFields({ + C2F(mProfileLevel, profile).oneOf({ + C2Config::PROFILE_AV1_0, + C2Config::PROFILE_AV1_1}), + C2F(mProfileLevel, level).oneOf({ + C2Config::LEVEL_AV1_2, + C2Config::LEVEL_AV1_2_1, + C2Config::LEVEL_AV1_2_2, + C2Config::LEVEL_AV1_3, + C2Config::LEVEL_AV1_3_1, + C2Config::LEVEL_AV1_3_2, + }) + }) + .withSetter(ProfileLevelSetter, mSize) + .build()); + + addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE) + .withDefault(new C2StreamMaxPictureSizeTuning::output( + 0u, 320, 240)) + .withFields({ + C2F(mSize, width).inRange(2, 2048, 2), + C2F(mSize, height).inRange(2, 2048, 2), + }) + .withSetter(MaxPictureSizeSetter, mSize) + .build()); + + addParameter( + DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) + .withDefault( + new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4)) + .withFields({ + C2F(mMaxInputSize, value).any(), + }) + .calculatedAs(MaxInputSizeSetter, mMaxSize) + .build()); + + C2ChromaOffsetStruct locations[1] = { + C2ChromaOffsetStruct::ITU_YUV_420_0()}; + std::shared_ptr defaultColorInfo = + C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */, + C2Color::YUV_420); + memcpy(defaultColorInfo->m.locations, locations, sizeof(locations)); + + defaultColorInfo = C2StreamColorInfo::output::AllocShared( + {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */, + C2Color::YUV_420); + helper->addStructDescriptors(); + + addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO) + .withConstValue(defaultColorInfo) + .build()); + + addParameter( + DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS) + .withDefault(new C2StreamColorAspectsTuning::output( + 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED, + C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED)) + .withFields({ + C2F(mDefaultColorAspects, range).inRange( + C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER), + C2F(mDefaultColorAspects, primaries).inRange( + C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER), + C2F(mDefaultColorAspects, transfer).inRange( + C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER), + C2F(mDefaultColorAspects, matrix).inRange( + C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER) + }) + .withSetter(DefaultColorAspectsSetter) + .build()); + + // TODO: support more formats? + addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT) + .withConstValue(new C2StreamPixelFormatInfo::output( + 0u, HAL_PIXEL_FORMAT_YCBCR_420_888)) + .build()); + } + + static C2R SizeSetter(bool mayBlock, + const C2P& oldMe, + C2P& me) { + (void)mayBlock; + C2R res = C2R::Ok(); + if (!me.F(me.v.width).supportsAtAll(me.v.width)) { + res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width))); + me.set().width = oldMe.v.width; + } + if (!me.F(me.v.height).supportsAtAll(me.v.height)) { + res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height))); + me.set().height = oldMe.v.height; + } + return res; + } + + static C2R MaxPictureSizeSetter( + bool mayBlock, C2P& me, + const C2P& size) { + (void)mayBlock; + // TODO: get max width/height from the size's field helpers vs. + // hardcoding + me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u); + me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u); + return C2R::Ok(); + } + + static C2R MaxInputSizeSetter( + bool mayBlock, C2P& me, + const C2P& maxSize) { + (void)mayBlock; + // assume compression ratio of 2 + me.set().value = (((maxSize.v.width + 63) / 64) * + ((maxSize.v.height + 63) / 64) * 3072); + return C2R::Ok(); + } + static C2R DefaultColorAspectsSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + if (me.v.range > C2Color::RANGE_OTHER) { + me.set().range = C2Color::RANGE_OTHER; + } + if (me.v.primaries > C2Color::PRIMARIES_OTHER) { + me.set().primaries = C2Color::PRIMARIES_OTHER; + } + if (me.v.transfer > C2Color::TRANSFER_OTHER) { + me.set().transfer = C2Color::TRANSFER_OTHER; + } + if (me.v.matrix > C2Color::MATRIX_OTHER) { + me.set().matrix = C2Color::MATRIX_OTHER; + } + return C2R::Ok(); + } + + static C2R ProfileLevelSetter(bool mayBlock, C2P &me, + const C2P &size) { + (void)mayBlock; + (void)size; + (void)me; // TODO: validate + return C2R::Ok(); + } + std::shared_ptr getDefaultColorAspects_l() { + return mDefaultColorAspects; + } + + private: + std::shared_ptr mProfileLevel; + std::shared_ptr mSize; + std::shared_ptr mMaxSize; + std::shared_ptr mMaxInputSize; + std::shared_ptr mColorInfo; + std::shared_ptr mPixelFormat; + std::shared_ptr mDefaultColorAspects; +}; + +C2SoftAomDec::C2SoftAomDec(const char* name, c2_node_id_t id, + const std::shared_ptr& intfImpl) + : SimpleC2Component( + std::make_shared>(name, id, intfImpl)), + mIntf(intfImpl), + mCodecCtx(nullptr){ + + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); + CREATE_DUMP_FILE(mOutFile); + + gettimeofday(&mTimeStart, nullptr); + gettimeofday(&mTimeEnd, nullptr); +} + +C2SoftAomDec::~C2SoftAomDec() { + onRelease(); +} + +c2_status_t C2SoftAomDec::onInit() { + status_t err = initDecoder(); + return err == OK ? C2_OK : C2_CORRUPTED; +} + +c2_status_t C2SoftAomDec::onStop() { + mSignalledError = false; + mSignalledOutputEos = false; + return C2_OK; +} + +void C2SoftAomDec::onReset() { + (void)onStop(); + c2_status_t err = onFlush_sm(); + if (err != C2_OK) { + ALOGW("Failed to flush decoder. Try to hard reset decoder."); + destroyDecoder(); + (void)initDecoder(); + } +} + +void C2SoftAomDec::onRelease() { + destroyDecoder(); +} + +c2_status_t C2SoftAomDec::onFlush_sm() { + if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) { + ALOGE("Failed to flush av1 decoder."); + return C2_CORRUPTED; + } + + aom_codec_iter_t iter = nullptr; + while (aom_codec_get_frame(mCodecCtx, &iter)) { + } + + mSignalledError = false; + mSignalledOutputEos = false; + + return C2_OK; +} + +static int GetCPUCoreCount() { + int cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %d", cpuCoreCount); + return cpuCoreCount; +} + +status_t C2SoftAomDec::initDecoder() { + mSignalledError = false; + mSignalledOutputEos = false; + if (!mCodecCtx) { + mCodecCtx = new aom_codec_ctx_t; + } + + if (!mCodecCtx) { + ALOGE("mCodecCtx is null"); + return NO_MEMORY; + } + + aom_codec_dec_cfg_t cfg; + memset(&cfg, 0, sizeof(aom_codec_dec_cfg_t)); + cfg.threads = GetCPUCoreCount(); + cfg.allow_lowbitdepth = 1; + + aom_codec_flags_t flags; + memset(&flags, 0, sizeof(aom_codec_flags_t)); + + aom_codec_err_t err; + if ((err = aom_codec_dec_init(mCodecCtx, aom_codec_av1_dx(), &cfg, 0))) { + ALOGE("av1 decoder failed to initialize. (%d)", err); + return UNKNOWN_ERROR; + } + + return OK; +} + +status_t C2SoftAomDec::destroyDecoder() { + if (mCodecCtx) { + aom_codec_destroy(mCodecCtx); + delete mCodecCtx; + mCodecCtx = nullptr; + } + return OK; +} + +void fillEmptyWork(const std::unique_ptr& work) { + uint32_t flags = 0; + if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) { + flags |= C2FrameData::FLAG_END_OF_STREAM; + ALOGV("signalling eos"); + } + work->worklets.front()->output.flags = (C2FrameData::flags_t)flags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; +} + +void C2SoftAomDec::finishWork(uint64_t index, + const std::unique_ptr& work, + const std::shared_ptr& block) { + std::shared_ptr buffer = + createGraphicBuffer(block, C2Rect(mWidth, mHeight)); + auto fillWork = [buffer, index](const std::unique_ptr& work) { + uint32_t flags = 0; + if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) && + (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) { + flags |= C2FrameData::FLAG_END_OF_STREAM; + ALOGV("signalling eos"); + } + work->worklets.front()->output.flags = (C2FrameData::flags_t)flags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.buffers.push_back(buffer); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; + }; + if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { + fillWork(work); + } else { + finish(index, fillWork); + } +} + +void C2SoftAomDec::process(const std::unique_ptr& work, + const std::shared_ptr& pool) { + work->result = C2_OK; + work->workletsProcessed = 0u; + work->worklets.front()->output.configUpdate.clear(); + work->worklets.front()->output.flags = work->input.flags; + if (mSignalledError || mSignalledOutputEos) { + work->result = C2_BAD_VALUE; + return; + } + + size_t inOffset = 0u; + size_t inSize = 0u; + C2ReadView rView = mDummyReadView; + if (!work->input.buffers.empty()) { + rView = + work->input.buffers[0]->data().linearBlocks().front().map().get(); + inSize = rView.capacity(); + if (inSize && rView.error()) { + ALOGE("read view map failed %d", rView.error()); + work->result = C2_CORRUPTED; + return; + } + } + + bool codecConfig = + ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0); + bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + + ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", + inSize, (int)work->input.ordinal.timestamp.peeku(), + (int)work->input.ordinal.frameIndex.peeku(), work->input.flags); + + if (codecConfig) { + fillEmptyWork(work); + return; + } + + int64_t frameIndex = work->input.ordinal.frameIndex.peekll(); + if (inSize) { + uint8_t* bitstream = const_cast(rView.data() + inOffset); + int32_t decodeTime = 0; + int32_t delay = 0; + + DUMP_TO_FILE(mOutFile, bitstream, inSize); + GETTIME(&mTimeStart, nullptr); + TIME_DIFF(mTimeEnd, mTimeStart, delay); + + aom_codec_err_t err = + aom_codec_decode(mCodecCtx, bitstream, inSize, &frameIndex); + + GETTIME(&mTimeEnd, nullptr); + TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); + ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay); + + if (err != AOM_CODEC_OK) { + ALOGE("av1 decoder failed to decode frame err: %d", err); + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + mSignalledError = true; + return; + } + + } else { + if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) { + ALOGE("Failed to flush av1 decoder."); + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + mSignalledError = true; + return; + } + } + + (void)outputBuffer(pool, work); + + if (eos) { + drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work); + mSignalledOutputEos = true; + } else if (!inSize) { + fillEmptyWork(work); + } +} + +static void copyOutputBufferToYV12Frame(uint8_t *dst, + const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride, + uint32_t width, uint32_t height) { + size_t dstYStride = align(width, 16); + size_t dstUVStride = align(dstYStride / 2, 16); + uint8_t* dstStart = dst; + + + for (size_t i = 0; i < height; ++i) { + memcpy(dst, srcY, width); + srcY += srcYStride; + dst += dstYStride; + } + + dst = dstStart + dstYStride * height; + for (size_t i = 0; i < height / 2; ++i) { + memcpy(dst, srcV, width / 2); + srcV += srcVStride; + dst += dstUVStride; + } + + dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2); + for (size_t i = 0; i < height / 2; ++i) { + memcpy(dst, srcU, width / 2); + srcU += srcUStride; + dst += dstUVStride; + } +} + +static void convertYUV420Planar16ToY410(uint32_t *dst, + const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstStride, size_t width, size_t height) { + + // Converting two lines at a time, slightly faster + for (size_t y = 0; y < height; y += 2) { + uint32_t *dstTop = (uint32_t *) dst; + uint32_t *dstBot = (uint32_t *) (dst + dstStride); + uint16_t *ySrcTop = (uint16_t*) srcY; + uint16_t *ySrcBot = (uint16_t*) (srcY + srcYStride); + uint16_t *uSrc = (uint16_t*) srcU; + uint16_t *vSrc = (uint16_t*) srcV; + + uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1; + size_t x = 0; + for (; x < width - 3; x += 4) { + + u01 = *((uint32_t*)uSrc); uSrc += 2; + v01 = *((uint32_t*)vSrc); vSrc += 2; + + y01 = *((uint32_t*)ySrcTop); ySrcTop += 2; + y23 = *((uint32_t*)ySrcTop); ySrcTop += 2; + y45 = *((uint32_t*)ySrcBot); ySrcBot += 2; + y67 = *((uint32_t*)ySrcBot); ySrcBot += 2; + + uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20); + uv1 = (u01 >> 16) | ((v01 >> 16) << 20); + + *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0; + *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0; + *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1; + *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1; + + *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0; + *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0; + *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1; + *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1; + } + + // There should be at most 2 more pixels to process. Note that we don't + // need to consider odd case as the buffer is always aligned to even. + if (x < width) { + u01 = *uSrc; + v01 = *vSrc; + y01 = *((uint32_t*)ySrcTop); + y45 = *((uint32_t*)ySrcBot); + uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20); + *dstTop++ = ((y01 & 0x3FF) << 10) | uv0; + *dstTop++ = ((y01 >> 16) << 10) | uv0; + *dstBot++ = ((y45 & 0x3FF) << 10) | uv0; + *dstBot++ = ((y45 >> 16) << 10) | uv0; + } + + srcY += srcYStride * 2; + srcU += srcUStride; + srcV += srcVStride; + dst += dstStride * 2; + } + + return; +} + +static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, + const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstStride, size_t width, size_t height) { + + uint8_t *dstY = (uint8_t *)dst; + size_t dstYSize = dstStride * height; + size_t dstUVStride = align(dstStride / 2, 16); + size_t dstUVSize = dstUVStride * height / 2; + uint8_t *dstV = dstY + dstYSize; + uint8_t *dstU = dstV + dstUVSize; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + dstY[x] = (uint8_t)(srcY[x] >> 2); + } + + srcY += srcYStride; + dstY += dstStride; + } + + for (size_t y = 0; y < (height + 1) / 2; ++y) { + for (size_t x = 0; x < (width + 1) / 2; ++x) { + dstU[x] = (uint8_t)(srcU[x] >> 2); + dstV[x] = (uint8_t)(srcV[x] >> 2); + } + + srcU += srcUStride; + srcV += srcVStride; + dstU += dstUVStride; + dstV += dstUVStride; + } + return; +} +bool C2SoftAomDec::outputBuffer( + const std::shared_ptr &pool, + const std::unique_ptr &work) +{ + if (!(work && pool)) return false; + + aom_codec_iter_t iter = nullptr; + aom_image_t* img = aom_codec_get_frame(mCodecCtx, &iter); + + if (!img) return false; + + if (img->d_w != mWidth || img->d_h != mHeight) { + mWidth = img->d_w; + mHeight = img->d_h; + + C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + std::vector> failures; + c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); + if (err == C2_OK) { + work->worklets.front()->output.configUpdate.push_back( + C2Param::Copy(size)); + } else { + ALOGE("Config update size failed"); + mSignalledError = true; + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + return false; + } + } + + CHECK(img->fmt == AOM_IMG_FMT_I420 || img->fmt == AOM_IMG_FMT_I42016); + + std::shared_ptr block; + uint32_t format = HAL_PIXEL_FORMAT_YV12; + if (img->fmt == AOM_IMG_FMT_I42016) { + IntfImpl::Lock lock = mIntf->lock(); + std::shared_ptr defaultColorAspects = mIntf->getDefaultColorAspects_l(); + + if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 && + defaultColorAspects->matrix == C2Color::MATRIX_BT2020 && + defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) { + format = HAL_PIXEL_FORMAT_RGBA_1010102; + } + } + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + + c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, + format, usage, &block); + + if (err != C2_OK) { + ALOGE("fetchGraphicBlock for Output failed with status %d", err); + work->result = err; + return false; + } + + C2GraphicView wView = block->map().get(); + + if (wView.error()) { + ALOGE("graphic view map failed %d", wView.error()); + work->result = C2_CORRUPTED; + return false; + } + + ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d", + block->width(), block->height(), mWidth, mHeight, + (int)*(int64_t*)img->user_priv); + + uint8_t* dst = const_cast(wView.data()[C2PlanarLayout::PLANE_Y]); + size_t srcYStride = img->stride[AOM_PLANE_Y]; + size_t srcUStride = img->stride[AOM_PLANE_U]; + size_t srcVStride = img->stride[AOM_PLANE_V]; + + if (img->fmt == AOM_IMG_FMT_I42016) { + const uint16_t *srcY = (const uint16_t *)img->planes[AOM_PLANE_Y]; + const uint16_t *srcU = (const uint16_t *)img->planes[AOM_PLANE_U]; + const uint16_t *srcV = (const uint16_t *)img->planes[AOM_PLANE_V]; + + if (format == HAL_PIXEL_FORMAT_RGBA_1010102) { + convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, + srcUStride / 2, srcVStride / 2, + align(mWidth, 16), + mWidth, mHeight); + } else { + convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2, + srcUStride / 2, srcVStride / 2, + align(mWidth, 16), + mWidth, mHeight); + } + } else { + const uint8_t *srcY = (const uint8_t *)img->planes[AOM_PLANE_Y]; + const uint8_t *srcU = (const uint8_t *)img->planes[AOM_PLANE_U]; + const uint8_t *srcV = (const uint8_t *)img->planes[AOM_PLANE_V]; + copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, + srcYStride, srcUStride, srcVStride, mWidth, mHeight); + } + finishWork(*(int64_t*)img->user_priv, work, std::move(block)); + block = nullptr; + return true; +} + +c2_status_t C2SoftAomDec::drainInternal( + uint32_t drainMode, const std::shared_ptr& pool, + const std::unique_ptr& work) { + if (drainMode == NO_DRAIN) { + ALOGW("drain with NO_DRAIN: no-op"); + return C2_OK; + } + if (drainMode == DRAIN_CHAIN) { + ALOGW("DRAIN_CHAIN not supported"); + return C2_OMITTED; + } + + if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) { + ALOGE("Failed to flush av1 decoder."); + return C2_CORRUPTED; + } + + while ((outputBuffer(pool, work))) { + } + + if (drainMode == DRAIN_COMPONENT_WITH_EOS && work && + work->workletsProcessed == 0u) { + fillEmptyWork(work); + } + + return C2_OK; +} + +c2_status_t C2SoftAomDec::drain(uint32_t drainMode, + const std::shared_ptr& pool) { + return drainInternal(drainMode, pool, nullptr); +} + +class C2SoftAomFactory : public C2ComponentFactory { + public: + C2SoftAomFactory() + : mHelper(std::static_pointer_cast( + GetCodec2PlatformComponentStore()->getParamReflector())) {} + + virtual c2_status_t createComponent( + c2_node_id_t id, std::shared_ptr* const component, + std::function deleter) override { + *component = std::shared_ptr( + new C2SoftAomDec(COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual c2_status_t createInterface( + c2_node_id_t id, std::shared_ptr* const interface, + std::function deleter) override { + *interface = std::shared_ptr( + new SimpleInterface( + COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual ~C2SoftAomFactory() override = default; + + private: + std::shared_ptr mHelper; +}; + +} // namespace android + +extern "C" ::C2ComponentFactory* CreateCodec2Factory() { + ALOGV("in %s", __func__); + return new ::android::C2SoftAomFactory(); +} + +extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; +} diff --git a/media/codec2/components/aom/C2SoftAomDec.h b/media/codec2/components/aom/C2SoftAomDec.h new file mode 100644 index 0000000000..4c82647d7b --- /dev/null +++ b/media/codec2/components/aom/C2SoftAomDec.h @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#ifndef ANDROID_C2_SOFT_AV1_DEC_H_ +#define ANDROID_C2_SOFT_AV1_DEC_H_ + +#include +#include "aom/aom_decoder.h" +#include "aom/aomdx.h" + +#define GETTIME(a, b) gettimeofday(a, b); +#define TIME_DIFF(start, end, diff) \ + diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ + ((end).tv_usec - (start).tv_usec); + +namespace android { + +struct C2SoftAomDec : public SimpleC2Component { + class IntfImpl; + + C2SoftAomDec(const char* name, c2_node_id_t id, + const std::shared_ptr& intfImpl); + virtual ~C2SoftAomDec(); + + // From SimpleC2Component + c2_status_t onInit() override; + c2_status_t onStop() override; + void onReset() override; + void onRelease() override; + c2_status_t onFlush_sm() override; + void process(const std::unique_ptr& work, + const std::shared_ptr& pool) override; + c2_status_t drain(uint32_t drainMode, + const std::shared_ptr& pool) override; + + private: + std::shared_ptr mIntf; + aom_codec_ctx_t* mCodecCtx; + + uint32_t mWidth; + uint32_t mHeight; + bool mSignalledOutputEos; + bool mSignalledError; + + #ifdef FILE_DUMP_ENABLE + char mInFile[200]; + char mOutFile[200]; + #endif /* FILE_DUMP_ENABLE */ + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + status_t initDecoder(); + status_t destroyDecoder(); + void finishWork(uint64_t index, const std::unique_ptr& work, + const std::shared_ptr& block); + bool outputBuffer(const std::shared_ptr& pool, + const std::unique_ptr& work); + + c2_status_t drainInternal(uint32_t drainMode, + const std::shared_ptr& pool, + const std::unique_ptr& work); + + C2_DO_NOT_COPY(C2SoftAomDec); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/data/local/tmp/temp/av1" +#define INPUT_DUMP_EXT "webm" +#define OUTPUT_DUMP_PATH "/data/local/tmp/temp/av1" +#define OUTPUT_DUMP_EXT "av1" +#define GENERATE_FILE_NAMES() \ + { \ + GETTIME(&mTimeStart, NULL); \ + strcpy(mInFile, ""); \ + ALOGD("GENERATE_FILE_NAMES"); \ + sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \ + mTimeStart.tv_usec, INPUT_DUMP_EXT); \ + strcpy(mOutFile, ""); \ + sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH, \ + mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT); \ + } + +#define CREATE_DUMP_FILE(m_filename) \ + { \ + FILE* fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + ALOGD("Opened file %s", m_filename); \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ + } +#define DUMP_TO_FILE(m_filename, m_buf, m_size) \ + { \ + FILE* fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL) { \ + int i; \ + ALOGD("Dump to file!"); \ + i = fwrite(m_buf, 1, m_size, fp); \ + if (i != (int)m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + fclose(fp); \ + } else { \ + ALOGD("Could not write to file %s", m_filename); \ + if (fp != NULL) fclose(fp); \ + } \ + } +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // ANDROID_C2_SOFT_AV1_DEC_H_ diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index 27aa064b9a..cf1f6cf0de 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -392,6 +392,7 @@ enum : uint32_t { _C2_PL_HEVC_BASE = 0x6000, _C2_PL_VP9_BASE = 0x7000, _C2_PL_DV_BASE = 0x8000, + _C2_PL_AV1_BASE = 0x9000, C2_PROFILE_LEVEL_VENDOR_START = 0x70000000, }; @@ -539,6 +540,11 @@ enum C2Config::profile_t : uint32_t { PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7, ///< Dolby Vision dvhe.07 profile PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8, ///< Dolby Vision dvhe.08 profile PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9, ///< Dolby Vision dvav.09 profile + + // AV1 profiles + PROFILE_AV1_0 = _C2_PL_AV1_BASE, ///< AV1 Profile 0 (4:2:0, 8 to 10 bit) + PROFILE_AV1_1, ///< AV1 Profile 1 (8 to 10 bit) + PROFILE_AV1_2, ///< AV1 Profile 2 (8 to 12 bit) }; enum C2Config::level_t : uint32_t { @@ -652,6 +658,31 @@ enum C2Config::level_t : uint32_t { LEVEL_DV_HIGH_UHD_30, ///< Dolby Vision high tier uhd30 LEVEL_DV_HIGH_UHD_48, ///< Dolby Vision high tier uhd48 LEVEL_DV_HIGH_UHD_60, ///< Dolby Vision high tier uhd60 + + LEVEL_AV1_2 = _C2_PL_AV1_BASE , ///< AV1 Level 2 + LEVEL_AV1_2_1, ///< AV1 Level 2.1 + LEVEL_AV1_2_2, ///< AV1 Level 2.2 + LEVEL_AV1_2_3, ///< AV1 Level 2.3 + LEVEL_AV1_3, ///< AV1 Level 3 + LEVEL_AV1_3_1, ///< AV1 Level 3.1 + LEVEL_AV1_3_2, ///< AV1 Level 3.2 + LEVEL_AV1_3_3, ///< AV1 Level 3.3 + LEVEL_AV1_4, ///< AV1 Level 4 + LEVEL_AV1_4_1, ///< AV1 Level 4.1 + LEVEL_AV1_4_2, ///< AV1 Level 4.2 + LEVEL_AV1_4_3, ///< AV1 Level 4.3 + LEVEL_AV1_5, ///< AV1 Level 5 + LEVEL_AV1_5_1, ///< AV1 Level 5.1 + LEVEL_AV1_5_2, ///< AV1 Level 5.2 + LEVEL_AV1_5_3, ///< AV1 Level 5.3 + LEVEL_AV1_6, ///< AV1 Level 6 + LEVEL_AV1_6_1, ///< AV1 Level 6.1 + LEVEL_AV1_6_2, ///< AV1 Level 6.2 + LEVEL_AV1_6_3, ///< AV1 Level 6.3 + LEVEL_AV1_7, ///< AV1 Level 7 + LEVEL_AV1_7_1, ///< AV1 Level 7.1 + LEVEL_AV1_7_2, ///< AV1 Level 7.2 + LEVEL_AV1_7_3, ///< AV1 Level 7.3 }; struct C2ProfileLevelStruct { diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index f36027eea8..5d0ccd2039 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -517,6 +517,13 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { caps->addProfileLevel(VP9Profile2, VP9Level5); caps->addProfileLevel(VP9Profile2HDR, VP9Level5); } + } else if (mediaType == MIMETYPE_VIDEO_AV1 && !encoder) { + caps->addProfileLevel(AV1Profile0, AV1Level2); + caps->addProfileLevel(AV1Profile0, AV1Level21); + caps->addProfileLevel(AV1Profile1, AV1Level22); + caps->addProfileLevel(AV1Profile1, AV1Level3); + caps->addProfileLevel(AV1Profile2, AV1Level31); + caps->addProfileLevel(AV1Profile2, AV1Level32); } else if (mediaType == MIMETYPE_VIDEO_HEVC && !encoder) { caps->addProfileLevel(HEVCProfileMain, HEVCMainTierLevel51); caps->addProfileLevel(HEVCProfileMainStill, HEVCMainTierLevel51); diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index 97e17e8d1d..b1b33e1eb4 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -326,6 +326,41 @@ ALookup sVp9Profiles = { { C2Config::PROFILE_VP9_3, VP9Profile3 }, }; +ALookup sAv1Levels = { + { C2Config::LEVEL_AV1_2, AV1Level2 }, + { C2Config::LEVEL_AV1_2_1, AV1Level21 }, + { C2Config::LEVEL_AV1_2_2, AV1Level22 }, + { C2Config::LEVEL_AV1_2_3, AV1Level23 }, + { C2Config::LEVEL_AV1_3, AV1Level3 }, + { C2Config::LEVEL_AV1_3_1, AV1Level31 }, + { C2Config::LEVEL_AV1_3_2, AV1Level32 }, + { C2Config::LEVEL_AV1_3_3, AV1Level33 }, + { C2Config::LEVEL_AV1_4, AV1Level4 }, + { C2Config::LEVEL_AV1_4_1, AV1Level41 }, + { C2Config::LEVEL_AV1_4_2, AV1Level42 }, + { C2Config::LEVEL_AV1_4_3, AV1Level43 }, + { C2Config::LEVEL_AV1_5, AV1Level5 }, + { C2Config::LEVEL_AV1_5_1, AV1Level51 }, + { C2Config::LEVEL_AV1_5_2, AV1Level52 }, + { C2Config::LEVEL_AV1_5_3, AV1Level53 }, + { C2Config::LEVEL_AV1_6, AV1Level6 }, + { C2Config::LEVEL_AV1_6_1, AV1Level61 }, + { C2Config::LEVEL_AV1_6_2, AV1Level62 }, + { C2Config::LEVEL_AV1_6_3, AV1Level63 }, + { C2Config::LEVEL_AV1_7, AV1Level7 }, + { C2Config::LEVEL_AV1_7_1, AV1Level71 }, + { C2Config::LEVEL_AV1_7_2, AV1Level72 }, + { C2Config::LEVEL_AV1_7_3, AV1Level73 }, +}; + + +ALookup sAv1Profiles = { + { C2Config::PROFILE_AV1_0, AV1Profile0 }, + { C2Config::PROFILE_AV1_1, AV1Profile1 }, + { C2Config::PROFILE_AV1_2, AV1Profile2 }, +}; + + /** * A helper that passes through vendor extension profile and level values. */ diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp index da1290329c..782bec5fbc 100644 --- a/media/codec2/vndk/C2Config.cpp +++ b/media/codec2/vndk/C2Config.cpp @@ -139,6 +139,9 @@ DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::profile_t, ({ { "vp9-1", C2Config::PROFILE_VP9_1 }, { "vp9-2", C2Config::PROFILE_VP9_2 }, { "vp9-3", C2Config::PROFILE_VP9_3 }, + { "av1-0", C2Config::PROFILE_AV1_0 }, + { "av1-1", C2Config::PROFILE_AV1_1 }, + { "av1-2", C2Config::PROFILE_AV1_2 }, })) DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::level_t, ({ diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 2d4e19e43c..a5dd203db0 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -821,6 +821,7 @@ C2PlatformComponentStore::C2PlatformComponentStore() emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so"); emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so"); emplace("c2.android.vp9.encoder", "libcodec2_soft_vp9enc.so"); + emplace("c2.android.av1.decoder", "libcodec2_soft_av1dec.so"); emplace("c2.android.raw.decoder", "libcodec2_soft_rawdec.so"); emplace("c2.android.flac.decoder", "libcodec2_soft_flacdec.so"); emplace("c2.android.flac.encoder", "libcodec2_soft_flacenc.so"); diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 1fdac055f4..89d5a9f5bd 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1450,6 +1450,8 @@ void MatroskaExtractor::addTracks() { AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize); } + } else if (!strcmp("V_AV1", codecID)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); } else { ALOGW("%s is not supported.", codecID); continue; diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 52213bd6e3..0579ff0f51 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -341,6 +341,8 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC('a', 'l', 'a', 'c'): return MEDIA_MIMETYPE_AUDIO_ALAC; + case FOURCC('a', 'v', '0', '1'): + return MEDIA_MIMETYPE_VIDEO_AV1; default: ALOGW("Unknown fourcc: %c%c%c%c", (fourcc >> 24) & 0xff, @@ -1712,6 +1714,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('a', 'v', 'c', '1'): case FOURCC('h', 'v', 'c', '1'): case FOURCC('h', 'e', 'v', '1'): + case FOURCC('a', 'v', '0', '1'): { uint8_t buffer[78]; if (chunk_data_size < (ssize_t)sizeof(buffer)) { @@ -5933,6 +5936,7 @@ static bool isCompatibleBrand(uint32_t fourcc) { FOURCC('a', 'v', 'c', '1'), FOURCC('h', 'v', 'c', '1'), FOURCC('h', 'e', 'v', '1'), + FOURCC('a', 'v', '0', '1'), FOURCC('3', 'g', 'p', '4'), FOURCC('m', 'p', '4', '1'), FOURCC('m', 'p', '4', '2'), diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index aba44bbd21..9d1ec1fdbb 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -23,6 +23,7 @@ const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; +const char *MEDIA_MIMETYPE_VIDEO_AV1 = "video/av01"; const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; const char *MEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc"; const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index 8edddcc0a4..e68852dce6 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -25,6 +25,7 @@ extern const char *MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC; extern const char *MEDIA_MIMETYPE_VIDEO_VP8; extern const char *MEDIA_MIMETYPE_VIDEO_VP9; +extern const char *MEDIA_MIMETYPE_VIDEO_AV1; extern const char *MEDIA_MIMETYPE_VIDEO_AVC; extern const char *MEDIA_MIMETYPE_VIDEO_HEVC; extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4; diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index 704bfddedd..984c23d61a 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -153,6 +153,35 @@ constexpr int32_t VP9Level6 = 0x800; constexpr int32_t VP9Level61 = 0x1000; constexpr int32_t VP9Level62 = 0x2000; +constexpr int32_t AV1Profile0 = 0x01; +constexpr int32_t AV1Profile1 = 0x02; +constexpr int32_t AV1Profile2 = 0x04; + +constexpr int32_t AV1Level2 = 0x1; +constexpr int32_t AV1Level21 = 0x2; +constexpr int32_t AV1Level22 = 0x4; +constexpr int32_t AV1Level23 = 0x8; +constexpr int32_t AV1Level3 = 0x10; +constexpr int32_t AV1Level31 = 0x20; +constexpr int32_t AV1Level32 = 0x40; +constexpr int32_t AV1Level33 = 0x80; +constexpr int32_t AV1Level4 = 0x100; +constexpr int32_t AV1Level41 = 0x200; +constexpr int32_t AV1Level42 = 0x400; +constexpr int32_t AV1Level43 = 0x800; +constexpr int32_t AV1Level5 = 0x1000; +constexpr int32_t AV1Level51 = 0x2000; +constexpr int32_t AV1Level52 = 0x4000; +constexpr int32_t AV1Level53 = 0x8000; +constexpr int32_t AV1Level6 = 0x10000; +constexpr int32_t AV1Level61 = 0x20000; +constexpr int32_t AV1Level62 = 0x40000; +constexpr int32_t AV1Level63 = 0x80000; +constexpr int32_t AV1Level7 = 0x100000; +constexpr int32_t AV1Level71 = 0x200000; +constexpr int32_t AV1Level72 = 0x400000; +constexpr int32_t AV1Level73 = 0x800000; + constexpr int32_t HEVCProfileMain = 0x01; constexpr int32_t HEVCProfileMain10 = 0x02; constexpr int32_t HEVCProfileMainStill = 0x04; @@ -273,6 +302,7 @@ constexpr char FEATURE_TunneledPlayback[] = "tunneled-playback"; // from MediaFormat.java constexpr char MIMETYPE_VIDEO_VP8[] = "video/x-vnd.on2.vp8"; constexpr char MIMETYPE_VIDEO_VP9[] = "video/x-vnd.on2.vp9"; +constexpr char MIMETYPE_VIDEO_AV1[] = "video/av01"; constexpr char MIMETYPE_VIDEO_AVC[] = "video/avc"; constexpr char MIMETYPE_VIDEO_HEVC[] = "video/hevc"; constexpr char MIMETYPE_VIDEO_MPEG4[] = "video/mp4v-es"; diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp index b1870359bc..1b8493acc6 100644 --- a/media/libstagefright/omx/OMXUtils.cpp +++ b/media/libstagefright/omx/OMXUtils.cpp @@ -150,6 +150,8 @@ const char *GetComponentRole(bool isEncoder, const char *mime) { "video_decoder.vp8", "video_encoder.vp8" }, { MEDIA_MIMETYPE_VIDEO_VP9, "video_decoder.vp9", "video_encoder.vp9" }, + { MEDIA_MIMETYPE_VIDEO_AV1, + "video_decoder.av1", "video_encoder.av1" }, { MEDIA_MIMETYPE_AUDIO_RAW, "audio_decoder.raw", "audio_encoder.raw" }, { MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 653317b4dc..f1194723a2 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -39,6 +39,7 @@ cc_library_shared { "libcodec2_soft_opusdec", "libcodec2_soft_vp8dec", "libcodec2_soft_vp9dec", + "libcodec2_soft_av1dec", "libcodec2_soft_vp8enc", "libcodec2_soft_vp9enc", "libcodec2_soft_rawdec", -- GitLab From 1c8e68217df47f0f80bbcb3d1048955af22930af Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 12 Dec 2018 07:55:50 -0800 Subject: [PATCH 0607/1530] Upstream changes from Codec2 MTS This includes changes up to commit b3c2c12135ad6e96ca3cfa258bba24eb7b7b92ca. Test: Builds Bug: 112362730 Bug: 119853704 Change-Id: I97adbe52258d72d18446f12a9698462174e89b19 --- .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 261 +++++++-------- .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 127 ++------ .../audio/media_c2_audio_hidl_test_common.h | 3 - .../vts/common/media_c2_hidl_test_common.cpp | 126 ++++++-- .../vts/common/media_c2_hidl_test_common.h | 23 +- .../VtsHidlC2V1_0TargetComponentTest.cpp | 61 +++- .../hidl/1.0/vts/res/bbb_av1_176_144.av1 | Bin 0 -> 307385 bytes .../hidl/1.0/vts/res/bbb_av1_176_144.info | 300 ++++++++++++++++++ .../hidl/1.0/vts/res/bbb_av1_640_360.av1 | Bin 0 -> 250646 bytes .../hidl/1.0/vts/res/bbb_av1_640_360.info | 167 ++++++++++ .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 298 ++++++++--------- .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 114 +------ .../video/media_c2_video_hidl_test_common.h | 3 - 13 files changed, 937 insertions(+), 546 deletions(-) create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index 6fd92001fd..1e87f38646 100644 --- a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -156,63 +156,31 @@ class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { // callback function to process onWorkDone received by Listener void handleWorkDone(std::list>& workItems) { for (std::unique_ptr& work : workItems) { - // handle configuration changes in work done - if (!work->worklets.empty() && - (work->worklets.front()->output.configUpdate.size() != 0)) { - ALOGV("Config Update"); - std::vector> updates = - std::move(work->worklets.front()->output.configUpdate); - std::vector configParam; - std::vector> failures; - for (size_t i = 0; i < updates.size(); ++i) { - C2Param* param = updates[i].get(); - if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) || - (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE)) { - configParam.push_back(param); + if (!work->worklets.empty()) { + // For decoder components current timestamp always exceeds + // previous timestamp + bool codecConfig = ((work->worklets.front()->output.flags & + C2FrameData::FLAG_CODEC_CONFIG) != 0); + if (!codecConfig && + !work->worklets.front()->output.buffers.empty()) { + EXPECT_GE(work->worklets.front()->output.ordinal.timestamp.peeku(), + mTimestampUs); + mTimestampUs = + work->worklets.front()->output.ordinal.timestamp.peeku(); + uint32_t rangeLength = + work->worklets.front()->output.buffers[0]->data() + .linearBlocks().front().map().get().capacity(); + //List of timestamp values and output size to calculate timestamp + if (mTimestampDevTest) { + outputMetaData meta = {mTimestampUs, rangeLength}; + oBufferMetaData.push_back(meta); } } - mComponent->config(configParam, C2_DONT_BLOCK, &failures); - ASSERT_EQ(failures.size(), 0u); - } - mFramesReceived++; - mEos = (work->worklets.front()->output.flags & - C2FrameData::FLAG_END_OF_STREAM) != 0; - auto frameIndexIt = - std::find(mFlushedIndices.begin(), mFlushedIndices.end(), - work->input.ordinal.frameIndex.peeku()); - ALOGV("WorkDone: frameID received %d", - (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); - - // For decoder components current timestamp always exceeds - // previous timestamp - bool codecConfig = ((work->worklets.front()->output.flags & - C2FrameData::FLAG_CODEC_CONFIG) != 0); - if (!codecConfig && - !work->worklets.front()->output.buffers.empty()) { - EXPECT_GE(work->worklets.front()->output.ordinal.timestamp.peeku(), - mTimestampUs); - mTimestampUs = - work->worklets.front()->output.ordinal.timestamp.peeku(); - uint32_t rangeLength = - work->worklets.front()->output.buffers[0]->data() - .linearBlocks().front().map().get().capacity(); - //List of timestamp values and output size to calculate timestamp - if (mTimestampDevTest) { - outputMetaData meta = {mTimestampUs, rangeLength}; - oBufferMetaData.push_back(meta); - } - } - - work->input.buffers.clear(); - work->worklets.clear(); - { - typedef std::unique_lock ULock; - ULock l(mQueueLock); - mWorkQueue.push_back(std::move(work)); - if (!mFlushedIndices.empty()) { - mFlushedIndices.erase(frameIndexIt); - } - mQueueCondition.notify_all(); + bool mCsd = false; + workDone(mComponent, work, mFlushedIndices, mQueueLock, + mQueueCondition, mWorkQueue, mEos, mCsd, + mFramesReceived); + (void)mCsd; } } } @@ -418,11 +386,11 @@ void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, } } -void decodeNFrames(const std::shared_ptr &component, - std::mutex &queueLock, std::condition_variable &queueCondition, - std::list> &workQueue, - std::list &flushedIndices, - std::shared_ptr &linearPool, +void decodeNFrames(const std::shared_ptr& component, + std::mutex &queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, + std::list& flushedIndices, + std::shared_ptr& linearPool, std::ifstream& eleStream, android::Vector* Info, int offset, int range, bool signalEOS = true) { @@ -462,34 +430,37 @@ void decodeNFrames(const std::shared_ptr &comp } int size = (*Info)[frameID].bytesCount; char* data = (char*)malloc(size); + ASSERT_NE(data, nullptr); eleStream.read(data, size); ASSERT_EQ(eleStream.gcount(), size); - std::shared_ptr block; - ASSERT_EQ(C2_OK, - linearPool->fetchLinearBlock( - size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, - &block)); - ASSERT_TRUE(block); - - // Write View - C2WriteView view = block->map().get(); - if (view.error() != C2_OK) { - fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error()); - break; - } - ASSERT_EQ((size_t)size, view.capacity()); - ASSERT_EQ(0u, view.offset()); - ASSERT_EQ((size_t)size, view.size()); + work->input.buffers.clear(); + if (size) { + std::shared_ptr block; + ASSERT_EQ(C2_OK, + linearPool->fetchLinearBlock( + size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, + &block)); + ASSERT_TRUE(block); + + // Write View + C2WriteView view = block->map().get(); + if (view.error() != C2_OK) { + fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error()); + break; + } + ASSERT_EQ((size_t)size, view.capacity()); + ASSERT_EQ(0u, view.offset()); + ASSERT_EQ((size_t)size, view.size()); - memcpy(view.base(), data, size); + memcpy(view.base(), data, size); - work->input.buffers.clear(); - work->input.buffers.emplace_back(new LinearBuffer(block)); + work->input.buffers.emplace_back(new LinearBuffer(block)); + free(data); + } work->worklets.clear(); work->worklets.emplace_back(new C2Worklet); - free(data); std::list> items; items.push_back(std::move(work)); @@ -502,29 +473,6 @@ void decodeNFrames(const std::shared_ptr &comp } } -void waitOnInputConsumption(std::mutex& queueLock, - std::condition_variable& queueCondition, - std::list>& workQueue, - size_t bufferCount = MAX_INPUT_BUFFERS) { - typedef std::unique_lock ULock; - uint32_t queueSize; - uint32_t maxRetry = 0; - { - ULock l(queueLock); - queueSize = workQueue.size(); - } - while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) { - ULock l(queueLock); - if (queueSize != workQueue.size()) { - queueSize = workQueue.size(); - maxRetry = 0; - } else { - queueCondition.wait_for(l, TIME_OUT); - maxRetry++; - } - } -} - TEST_F(Codec2AudioDecHidlTest, validateCompName) { if (mDisableTest) return; ALOGV("Checks if the given component is a valid audio component"); @@ -718,7 +666,6 @@ TEST_F(Codec2AudioDecHidlTest, EOSTest) { ASSERT_EQ(mComponent->queue(&items), C2_OK); { - typedef std::unique_lock ULock; ULock l(mQueueLock); if (mWorkQueue.size() != MAX_INPUT_BUFFERS) { mQueueCondition.wait_for(l, TIME_OUT); @@ -729,46 +676,6 @@ TEST_F(Codec2AudioDecHidlTest, EOSTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } -TEST_F(Codec2AudioDecHidlTest, EmptyBufferTest) { - description("Tests empty input buffer"); - if (mDisableTest) return; - typedef std::unique_lock ULock; - ASSERT_EQ(mComponent->start(), C2_OK); - std::unique_ptr work; - // Prepare C2Work - { - ULock l(mQueueLock); - if (!mWorkQueue.empty()) { - work.swap(mWorkQueue.front()); - mWorkQueue.pop_front(); - } else { - ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test"; - } - } - ASSERT_NE(work, nullptr); - - work->input.flags = (C2FrameData::flags_t)0; - work->input.ordinal.timestamp = 0; - work->input.ordinal.frameIndex = 0; - work->input.buffers.clear(); - work->worklets.clear(); - work->worklets.emplace_back(new C2Worklet); - - std::list> items; - items.push_back(std::move(work)); - ASSERT_EQ(mComponent->queue(&items), C2_OK); - - { - typedef std::unique_lock ULock; - ULock l(mQueueLock); - if (mWorkQueue.size() != MAX_INPUT_BUFFERS) { - mQueueCondition.wait_for(l, TIME_OUT); - } - } - ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS); - ASSERT_EQ(mComponent->stop(), C2_OK); -} - TEST_F(Codec2AudioDecHidlTest, FlushTest) { description("Tests Flush calls"); if (mDisableTest) return; @@ -891,6 +798,72 @@ TEST_F(Codec2AudioDecHidlTest, FlushTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } +TEST_F(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) { + description("Decode with multiple empty input frames"); + if (mDisableTest) return; + + ASSERT_EQ(mComponent->start(), C2_OK); + + char mURL[512], info[512]; + std::ifstream eleStream, eleInfo; + + strcpy(mURL, gEnv->getRes().c_str()); + strcpy(info, gEnv->getRes().c_str()); + GetURLForComponent(mCompName, mURL, info); + + eleInfo.open(info); + ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found"; + android::Vector Info; + int bytesCount = 0; + uint32_t frameId = 0; + uint32_t flags = 0; + uint32_t timestamp = 0; + bool codecConfig = false; + // This test introduces empty CSD after every 20th frame + // and empty input frames at an interval of 5 frames. + while (1) { + if (!(frameId % 5)) { + if (!(frameId % 20)) flags = 32; + else flags = 0; + bytesCount = 0; + } else { + if (!(eleInfo >> bytesCount)) break; + eleInfo >> flags; + eleInfo >> timestamp; + codecConfig = flags ? + ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0; + } + Info.push_back({bytesCount, flags, timestamp}); + frameId++; + } + eleInfo.close(); + + ALOGV("mURL : %s", mURL); + eleStream.open(mURL, std::ifstream::binary); + ASSERT_EQ(eleStream.is_open(), true); + ASSERT_NO_FATAL_FAILURE(decodeNFrames( + mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, + mLinearPool, eleStream, &Info, 0, (int)Info.size())); + + // blocking call to ensures application to Wait till all the inputs are + // consumed + if (!mEos) { + ALOGV("Waiting for input consumption"); + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue)); + } + + eleStream.close(); + if (mFramesReceived != Info.size()) { + ALOGE("Input buffer count and Output buffer count mismatch"); + ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, + Info.size()); + ASSERT_TRUE(false); + } + + ASSERT_EQ(mComponent->stop(), C2_OK); +} + } // anonymous namespace int main(int argc, char** argv) { diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp index 4f86aad389..5d66ee5376 100644 --- a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp @@ -96,6 +96,7 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { const StringToName kStringToName[] = { {"aac", aac}, {"flac", flac}, + {"opus", opus}, {"amrnb", amrnb}, {"amrwb", amrwb}, }; @@ -135,45 +136,17 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { // callback function to process onWorkDone received by Listener void handleWorkDone(std::list>& workItems) { for (std::unique_ptr& work : workItems) { - // handle configuration changes in work done - if (!work->worklets.empty() && - (work->worklets.front()->output.configUpdate.size() != 0)) { - ALOGV("Config Update"); - std::vector> updates = - std::move(work->worklets.front()->output.configUpdate); - std::vector configParam; - std::vector> failures; - for (size_t i = 0; i < updates.size(); ++i) { - C2Param* param = updates[i].get(); - if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) { - mCsd = true; - } - } - } - mFramesReceived++; - mEos = (work->worklets.front()->output.flags & - C2FrameData::FLAG_END_OF_STREAM) != 0; - auto frameIndexIt = - std::find(mFlushedIndices.begin(), mFlushedIndices.end(), - work->input.ordinal.frameIndex.peeku()); - ALOGV("WorkDone: frameID received %d", - (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); - work->input.buffers.clear(); - work->worklets.clear(); - { - typedef std::unique_lock ULock; - ULock l(mQueueLock); - mWorkQueue.push_back(std::move(work)); - if (!mFlushedIndices.empty()) { - mFlushedIndices.erase(frameIndexIt); - } - mQueueCondition.notify_all(); + if (!work->worklets.empty()) { + workDone(mComponent, work, mFlushedIndices, mQueueLock, + mQueueCondition, mWorkQueue, mEos, mCsd, + mFramesReceived); } } } enum standardComp { aac, flac, + opus, amrnb, amrwb, unknown_comp, @@ -275,6 +248,8 @@ void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) { "bbb_raw_1ch_16khz_s16le.raw"}, {Codec2AudioEncHidlTest::standardComp::flac, "bbb_raw_2ch_48khz_s16le.raw"}, + {Codec2AudioEncHidlTest::standardComp::opus, + "bbb_raw_2ch_48khz_s16le.raw"}, }; for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) { @@ -285,11 +260,11 @@ void GetURLForComponent(Codec2AudioEncHidlTest::standardComp comp, char* mURL) { } } -void encodeNFrames(const std::shared_ptr &component, - std::mutex &queueLock, std::condition_variable &queueCondition, - std::list> &workQueue, - std::list &flushedIndices, - std::shared_ptr &linearPool, +void encodeNFrames(const std::shared_ptr& component, + std::mutex &queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, + std::list& flushedIndices, + std::shared_ptr& linearPool, std::ifstream& eleStream, uint32_t nFrames, int32_t samplesPerFrame, int32_t nChannels, int32_t nSampleRate, bool flushed = false, @@ -334,6 +309,7 @@ void encodeNFrames(const std::shared_ptr &comp flushedIndices.emplace_back(frameID); } char* data = (char*)malloc(bytesCount); + ASSERT_NE(data, nullptr); eleStream.read(data, bytesCount); ASSERT_EQ(eleStream.gcount(), bytesCount); std::shared_ptr block; @@ -372,29 +348,6 @@ void encodeNFrames(const std::shared_ptr &comp } } -void waitOnInputConsumption(std::mutex& queueLock, - std::condition_variable& queueCondition, - std::list>& workQueue, - size_t bufferCount = MAX_INPUT_BUFFERS) { - typedef std::unique_lock ULock; - uint32_t queueSize; - uint32_t maxRetry = 0; - { - ULock l(queueLock); - queueSize = workQueue.size(); - } - while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) { - ULock l(queueLock); - if (queueSize != workQueue.size()) { - queueSize = workQueue.size(); - maxRetry = 0; - } else { - queueCondition.wait_for(l, TIME_OUT); - maxRetry++; - } - } -} - TEST_F(Codec2AudioEncHidlTest, validateCompName) { if (mDisableTest) return; ALOGV("Checks if the given component is a valid audio component"); @@ -425,6 +378,11 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) { nSampleRate = 48000; samplesPerFrame = 1152; break; + case opus: + nChannels = 2; + nSampleRate = 48000; + samplesPerFrame = 960; + break; case amrnb: nChannels = 1; nSampleRate = 8000; @@ -458,7 +416,7 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) { ALOGE("framesReceived : %d inputFrames : %u", mFramesReceived, numFrames); ASSERT_TRUE(false); } - if ((mCompName == flac || mCompName == aac)) { + if ((mCompName == flac || mCompName == opus || mCompName == aac)) { if (!mCsd) { ALOGE("CSD buffer missing"); ASSERT_TRUE(false); @@ -508,46 +466,6 @@ TEST_F(Codec2AudioEncHidlTest, EOSTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } -TEST_F(Codec2AudioEncHidlTest, EmptyBufferTest) { - description("Tests empty input buffer"); - if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); - - typedef std::unique_lock ULock; - std::unique_ptr work; - { - ULock l(mQueueLock); - if (!mWorkQueue.empty()) { - work.swap(mWorkQueue.front()); - mWorkQueue.pop_front(); - } else { - ALOGE("mWorkQueue Empty is not expected at the start of the test"); - ASSERT_TRUE(false); - } - } - ASSERT_NE(work, nullptr); - work->input.flags = (C2FrameData::flags_t)0; - work->input.ordinal.timestamp = 0; - work->input.ordinal.frameIndex = 0; - work->input.buffers.clear(); - work->worklets.clear(); - work->worklets.emplace_back(new C2Worklet); - - std::list> items; - items.push_back(std::move(work)); - ASSERT_EQ(mComponent->queue(&items), C2_OK); - uint32_t queueSize; - { - ULock l(mQueueLock); - queueSize = mWorkQueue.size(); - if (queueSize < MAX_INPUT_BUFFERS) { - mQueueCondition.wait_for(l, TIME_OUT); - } - } - ASSERT_EQ(mWorkQueue.size(), (uint32_t)MAX_INPUT_BUFFERS); - ASSERT_EQ(mComponent->stop(), C2_OK); -} - TEST_F(Codec2AudioEncHidlTest, FlushTest) { description("Test Request for flush"); if (mDisableTest) return; @@ -574,6 +492,11 @@ TEST_F(Codec2AudioEncHidlTest, FlushTest) { nSampleRate = 48000; samplesPerFrame = 1152; break; + case opus: + nChannels = 2; + nSampleRate = 48000; + samplesPerFrame = 960; + break; case amrnb: nChannels = 1; nSampleRate = 8000; diff --git a/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h b/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h index 89eb69e3a2..4d773ceaf5 100644 --- a/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h @@ -17,8 +17,5 @@ #ifndef MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H #define MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H -#define MAX_RETRY 20 -#define TIME_OUT 200ms -#define MAX_INPUT_BUFFERS 8 #endif // MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp index fdccdbb568..64a458cb7a 100644 --- a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp +++ b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp @@ -14,39 +14,115 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 #define LOG_TAG "media_c2_hidl_test_common" #include #include "media_c2_hidl_test_common.h" -using ::android::hardware::media::c2::V1_0::FieldSupportedValues; -void dumpFSV(const FieldSupportedValues& sv) { - ALOGD("Dumping FSV data"); - using namespace std; - if (sv.type == FieldSupportedValues::Type::EMPTY) { - ALOGD("FSV Value is Empty"); +// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set +void testInputBuffer( + const std::shared_ptr& component, + std::mutex& queueLock, std::list>& workQueue, + uint32_t flags, bool isNullBuffer) { + std::unique_ptr work; + { + typedef std::unique_lock ULock; + ULock l(queueLock); + if (!workQueue.empty()) { + work.swap(workQueue.front()); + workQueue.pop_front(); + } else { + ASSERT_TRUE(false) << "workQueue Empty at the start of test"; + } + } + ASSERT_NE(work, nullptr); + + work->input.flags = (C2FrameData::flags_t)flags; + work->input.ordinal.timestamp = 0; + work->input.ordinal.frameIndex = 0; + work->input.buffers.clear(); + if (isNullBuffer) { + work->input.buffers.emplace_back(nullptr); } - if (sv.type == FieldSupportedValues::Type::RANGE) { - ALOGD("Dumping FSV range"); - cout << ".range(" << sv.range.min; - if (sv.range.step != 0) { - cout << ":" << sv.range.step; + work->worklets.clear(); + work->worklets.emplace_back(new C2Worklet); + + std::list> items; + items.push_back(std::move(work)); + ASSERT_EQ(component->queue(&items), C2_OK); +} + +// Wait for all the inputs to be consumed by the plugin. +void waitOnInputConsumption(std::mutex& queueLock, + std::condition_variable& queueCondition, + std::list>& workQueue, + size_t bufferCount) { + typedef std::unique_lock ULock; + uint32_t queueSize; + uint32_t maxRetry = 0; + { + ULock l(queueLock); + queueSize = workQueue.size(); + } + while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) { + ULock l(queueLock); + if (queueSize != workQueue.size()) { + queueSize = workQueue.size(); + maxRetry = 0; + } else { + queueCondition.wait_for(l, TIME_OUT); + maxRetry++; } - if (sv.range.num != 1 || sv.range.denom != 1) { - cout << ":" << sv.range.num << "/" << sv.range.denom; + } +} + +// process onWorkDone received by Listener +void workDone( + const std::shared_ptr& component, + std::unique_ptr& work, std::list& flushedIndices, + std::mutex& queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, bool& eos, bool& csd, + uint32_t& framesReceived) { + // handle configuration changes in work done + if (work->worklets.front()->output.configUpdate.size() != 0) { + ALOGV("Config Update"); + std::vector> updates = + std::move(work->worklets.front()->output.configUpdate); + std::vector configParam; + std::vector> failures; + for (size_t i = 0; i < updates.size(); ++i) { + C2Param* param = updates[i].get(); + if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) { + csd = true; + } else if ((param->index() == + C2StreamSampleRateInfo::output::PARAM_TYPE) || + (param->index() == + C2StreamChannelCountInfo::output::PARAM_TYPE) || + (param->index() == + C2VideoSizeStreamInfo::output::PARAM_TYPE)) { + configParam.push_back(param); + } } - cout << " " << sv.range.max << ")"; + component->config(configParam, C2_DONT_BLOCK, &failures); + ASSERT_EQ(failures.size(), 0u); } - if (sv.values.size()) { - ALOGD("Dumping FSV value"); - cout << (sv.type == FieldSupportedValues::Type::FLAGS ? ".flags(" - : ".list("); - const char* sep = ""; - for (const auto& p : sv.values) { - cout << sep << p; - sep = ","; + framesReceived++; + eos = (work->worklets.front()->output.flags & + C2FrameData::FLAG_END_OF_STREAM) != 0; + auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(), + work->input.ordinal.frameIndex.peeku()); + ALOGV("WorkDone: frameID received %d", + (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); + work->input.buffers.clear(); + work->worklets.clear(); + { + typedef std::unique_lock ULock; + ULock l(queueLock); + workQueue.push_back(std::move(work)); + if (!flushedIndices.empty()) { + flushedIndices.erase(frameIndexIt); } - cout << ")"; + queueCondition.notify_all(); } - cout << endl; -} +} \ No newline at end of file diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h index f765baa9ef..a68853018d 100644 --- a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,10 @@ using ::android::hardware::hidl_string; #include +#define MAX_RETRY 20 +#define TIME_OUT 400ms +#define MAX_INPUT_BUFFERS 8 + /* * Handle Callback functions onWorkDone(), onTripped(), * onError(), onDeath(), onFramesRendered() @@ -176,5 +181,21 @@ class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { /* * common functions declarations */ -void dumpFSV(const FieldSupportedValues& sv); +void testInputBuffer( + const std::shared_ptr& component, + std::mutex& queueLock, std::list>& workQueue, + uint32_t flags, bool isNullBuffer); + +void waitOnInputConsumption(std::mutex& queueLock, + std::condition_variable& queueCondition, + std::list>& workQueue, + size_t bufferCount = MAX_INPUT_BUFFERS); + +void workDone( + const std::shared_ptr& component, + std::unique_ptr& work, std::list& flushedIndices, + std::mutex& queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, bool& eos, bool& csd, + uint32_t& framesReceived); + #endif // MEDIA_C2_HIDL_TEST_COMMON_H diff --git a/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp index b7fb65593f..ec803d7015 100644 --- a/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp +++ b/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp @@ -38,14 +38,21 @@ class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase { public: virtual void SetUp() override { Super::SetUp(); + mEos = false; mClient = android::Codec2Client::CreateFromService( gEnv->getInstance().c_str()); ASSERT_NE(mClient, nullptr); - mListener.reset(new CodecListener()); + mListener.reset(new CodecListener( + [this](std::list>& workItems) { + handleWorkDone(workItems); + })); ASSERT_NE(mListener, nullptr); mClient->createComponent(gEnv->getComponent().c_str(), mListener, &mComponent); ASSERT_NE(mComponent, nullptr); + for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) { + mWorkQueue.emplace_back(new C2Work); + } } virtual void TearDown() override { @@ -59,6 +66,23 @@ class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase { } Super::TearDown(); } + // callback function to process onWorkDone received by Listener + void handleWorkDone(std::list>& workItems) { + for (std::unique_ptr& work : workItems) { + if (!work->worklets.empty()) { + bool mCsd = false; + uint32_t mFramesReceived = 0; + std::list mFlushedIndices; + workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, + mWorkQueue, mEos, mCsd, mFramesReceived); + } + } + } + + bool mEos; + std::mutex mQueueLock; + std::condition_variable mQueueCondition; + std::list> mWorkQueue; std::shared_ptr mClient; std::shared_ptr mListener; @@ -135,8 +159,6 @@ TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) { ALOGV("Multiple Start Stop and Reset Test"); c2_status_t err = C2_OK; -#define MAX_RETRY 16 - for (size_t i = 0; i < MAX_RETRY; i++) { err = mComponent->start(); ASSERT_EQ(err, C2_OK); @@ -184,13 +206,44 @@ TEST_F(Codec2ComponentHidlTest, MultipleRelease) { ASSERT_EQ(err, C2_OK); ASSERT_EQ(failures.size(), 0u); -#define MAX_RETRY 16 for (size_t i = 0; i < MAX_RETRY; i++) { err = mComponent->release(); ASSERT_EQ(err, C2_OK); } } +class Codec2ComponentInputTests : public Codec2ComponentHidlTest, + public ::testing::WithParamInterface > { +}; + +TEST_P(Codec2ComponentInputTests, InputBufferTest) { + description("Tests for different inputs"); + + uint32_t flags = GetParam().first; + bool isNullBuffer = GetParam().second; + if (isNullBuffer) ALOGD("Testing for null input buffer with flag : %u", flags); + else ALOGD("Testing for empty input buffer with flag : %u", flags); + mEos = false; + ASSERT_EQ(mComponent->start(), C2_OK); + ASSERT_NO_FATAL_FAILURE(testInputBuffer( + mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer)); + + ALOGD("Waiting for input consumption"); + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue)); + + if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true); + ASSERT_EQ(mComponent->stop(), C2_OK); + ASSERT_EQ(mComponent->reset(), C2_OK); +} + +INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Values( + std::make_pair(0, true), + std::make_pair(C2FrameData::FLAG_END_OF_STREAM, true), + std::make_pair(0, false), + std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false), + std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false))); + } // anonymous namespace // TODO: Add test for Invalid work, diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 b/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 new file mode 100644 index 0000000000000000000000000000000000000000..1d67af96d30b5cee509393b298914a87217d8e3b GIT binary patch literal 307385 zcmV)CK*GNg0168L000BMe0%@%0Dv<2E)W5Qzyu&-3;;3R2*3aVfJ6{5ToIfQoHF(= z?4br-d<Kqp`jIuVd`(14GQtD7o#6MCei_cSf*x)ETSLqbnnQAsVqY`X!%3;N6NP)ZnZF>7-QR7ONL-*{oWV z_{_lweLR@y75*8lhFdf9xnk^iuLVXqx|#D9_;YXWR-}?loPr<@LLX-Z;Et}~U;&cg zz|Zt59RSB{=r|rSRfRO z8vqfBN84-xMng`uQPs2aBucOw-g!T%w^=c2xm#41ab;N#WVLfx%Yl?7;NIc zW(=_57*YaK48`ttEsq8ty1TAwiRFx3+Km8z#qXv6LuB7WnttN>d_Ms&2~x5)!$ZN7 zMzy6Ed36IQUxx%P8kUEWd*c`)k^==ZxO@)0oH*t#1pi<4-9pP!FZ4aivk=*{++aHw zj?udNB-cO|#;&AC6*cqg2_u$hH``f(QC}+2?1?gnHsWK^)xQwXuJKYK+xVS~g?M^= z>Yp|6o&Vq>-#Hfir#4!RqWTmAJiT-Kig%1mlzXoH!anMGkJITYbJq*6-}i@5 z$!jJtUx&)EsVg{L=Tc=3$GjY+k@dA|V4#R&;>JN*6W?P(n$#=yrHj!jElqcY^ijWS z&Lo2)aXcjrOyfF7VSMXOcL{ph ztd`LQva*LdP;2J*H^(TuN8ZfUEeB_SR-jJ`?AqTJf&6Ou!oxP#gBr_J8CmV#zz?U-o(J6@~)O-lTU zjRaIIxq-HDKAXfwGlDLCvifoXR)kt|r6d&oujLFP-PXAI_{sq;wV?`Oi^=3t4l=PQ zX8-bI5!e%_mF8aAEAjB84DP0$z_F4YNpJ^?GIH&<{UoIbK%i##%_G~5wM>PEOf?P_ z%ES44VzS5pa2GTC`$T@u1jAE$~C!&5Gfa4n`+4>A2X%x)@2U2|sqw!nG zyWtXzfjt2U#;E`*d!VDfrlVfZMnj)u?*B;N2(UG*EKgL-(TGdAr-uE;${Vo1LcW{~NndT5cG#lJtCwE6Zw^l>88M$pfQZ z#pe*YrC7yt#9Ntb?DUH9VC}R&$Q@@J-zoLUiRmC!_1c&ffhzk|Sr@2M2rU0|6GEAQ zBG)d`Bwt~W?%Z?3CTWa?R-CLP#uKG{!@{7CEy3qn*Eyy;QD-K974T^3ubaGY!%YaL zdgCBRzaUKp!yM>6p>47>6c9G(`IplzR(t%rZ3+bE_}T(8zJqf*nM};8-3NIU#jo>_ zLQT*Soi&@s=C$58wx>2Z3oVsi(O9Hg-Q7fzT{Ae}rxbZb?tc(#8AmHT^2g~-Vf!zw zWL^6$u4}LS5YH0YFYXrfM6sdGyF2YQJgQ7<5i$iC+ZkZXX3QO3GysE$36AwaBM6bs zyTY6PN-;nR%|i}+0$%Jb{p{u@2S?UK2>(!^%h!2{of2KEd=f`1pNS8l0}fNSBWQ<~ zq2{RSeYgoet1F^@fwpY!7UhiZsu?FIKDeHc*%XDZOVEpZ?_Cob65541LVA8`)Pcd( z9U6-AQ`jzdGX26zMJ5jVi!tc$29wasi>5gh{`acjo>hX$mvkcy2S(iM{E+%y2P-#_@f1 zZg((2Nz#3dw0q~SRxnznz+dt0sBhOM0N1i69VVX+W_0L4 z298Pe`A&36=W|{ve0ib>Vg@)8b%j-~*DZyf40p}stRsJQROXo&+6w2^<~h@ZSG4#T zP=-0@y!@N=@jF}-L`ECh@-k>dKgl?L67v=YMfpbMSeLAo#@L+Cr4OYY>J?#y0?n&Q zlpQQs=OcXPZD!i1-j#Zalh;_>)faGG#{4nw2?)7b9M{nXhsmWq(@a=~8WHa|>C`Si zDEX^!FFKH-D63u4es1QIbl3CU{ri8ulIn_N&|Fa*?qjo4!%yM{MhzeXWIZXXdSXA! zpr%^9>B6?rH1L`hHSDnB>erAojhL07INyP z*UCz$AYo~HC&-z40MvVSJm`*1iiH?bo^FaNom$&>Rh}q2-L26938vaPK#$H+MKq`K z1GA#CGEYwo(`;ME@{j75ZtNwBvE%yzD8w?2Z|8v%WJiOO3s9G|U3ytcZiU9Z&KgK_XPdys1@-gum#WT4Xqn<#2$kx@Nika~K1Tt){ojwB zvpqMkYru+TOXj2yDTo4Wme)1Y%27o+3m`x7Ko`^V{(*El8){23OaU}9V;=o>agb?p zk#fh0Df72Tb*`LT0&KtBo7j&;{3Xh@3rOe0ord4#EjZ2GhJTgJM{91=uvr1U*YV0H z=(wA?J3%X30VY9LRMlJGbJZOOd+tx|W!G-86JIL1UCcJcbsw`7Z-NA zPIYluur<85^zTb^nZn=aO`!AX`$u8^Fa#MyV%0nq-^QsHU+EyNLHPioF+0D;lXUf7 zsqVr7t@-$D^Q)v|wyf=5DoS2La-*?7rcOiXYF0I!p#5M8RNHkX+c--41GBe%RC z{OcXM=I0Z>h$SmhX_%{yIoGr5x;#?j`n2%%OVj0%XQTp91JvScVJ)@zh|xMcV5Of& z0sic)MHH^!I^@r80``g^17k7&jIU7?e;tTRPVSEXb|_M^Oy(NEi9TH^W%-Wn z(Zx1`6REgi8*Xt%G>VA2czFe_f0ia5LM_zt@NO54?s2C0ve*${y-I>1TAG`6}WRHgz}ASCWrlPYC6B|jTjBy*En6RDUerq?I6Cxpx2 zz>zC>{|qV;n{v_P)p)}`l&+8RgD~9VZ^bAL3Lf3E_O_jQw~~gm;B8fqOuY}(a39rm z?>m>XX|#2-2>8(CPUM`Fy@M%2G#g5PJBhM;2iGu&A?S-Z**ZaPI7>TY(2Y#f#^xJM zV?ZK;^kb4&jFJ_lBE7#A-ty{Fx?YJnCxG1m>DR#>;OUQc~@wkHzZ>nsWiXL8~yUKQPQvm#riR(+&_Ey8B;WXZEr>OemTl>C>vaif$% zOg#~wtYb;G*BzME9~ffwYk7#s5U4a&P=t_CJ>*cp-s%(5`gXNuAja~Nriz)A1DCLc z*DNvmS8R^6Z-M53rO1|y+4notFn5P#O{Lp*fxyFi*16-4?b6mxZfDQ7v5w8((GqL* zDUE}QAjsqXeWZc#86K9Bba8d&9O?cz;RQcY7zydL2KNV}ei^yTa<;yI7(pKVF^?uQ zA(Oqhq8hdp9Nnx(dscbY_i#7O-ngL8E=sFMx_b1H!RFOT#i^I>@vPq8*8Tl3Xm08A zjkEMHPu@g(SHjJTg`jV$Y%#%n7U}h(K1@PRh?<5jW_lgkATQ=%;yJJhX$4U$-GGP6 zuntA2eaHNF!7TEei)8q(h%&<8fI#UcP;Mru?UOdk9<4PEhlx&?HsB|^J?fjCU5O-M zXdX&)YG$Y7-_fpeSAj{2F_7&Qsw3+Li9U@ofEq!E-5}brvh|6)?{RCf(#`@S95e~` zUAx!bD@yV(>s9u;;jp zMAJ}i;LSqmt|~%7eh&!y&6HN#4Egkxi+_g1O6CsutNJEZxclVwLCxf(-09Zy)hFGv zZdW#2Jz1BQ6j+(8;Kc~HIC5&Wud(U;tr=-s9M-p0rnsp4)V%IOmejrY|5$Iugw0LA zWIz!QZWDIvLYV^yg}dhX08F?EiYs8@!s&r$KJGdMvoQ;fnE0BD74VasKSL~thqhC> zRxJ(+6?i$q2XS9r-)`XXNlsn{qLu#9*7JWjMT&+#N42r-i&*ND!F=Amvw#d@U!8G2 z99rgKEsM+-sT6aDw;9@NW=?VS)|uI9N?b=Dxh7PkJTg`+8j9X(rEO}20tT$~mUDhf zd90&UZsrd2@*!pC5W{DT)>2+$r^9TpdMr6zY;pqdkbhR!0LfO?|I$fPSMqcNd`}Q^ z(7GBj%~=cdL6Y@54E9*c`y6CPZuSx2M85}K@+f_#-^b%-NIw~6of;8Awkk>um!rT7 zh$S-{8II6xdD7Q5O=y>Ml=z#Y(7lFKnw*m&q%1!E?#GSqQimI1^L$nR%f}E8TN$W`^dXjYr=KJSMtiGMUPy(wA?3PgjqfVC1q+jYyVBJd{~J^& z>5$k5OCFgje3!Ebk8Vo|Erv6f^U^?wk8o*MjC#YP=mSviRqdmSMo&&Xct5^9IaHs( zAB31j|1e9Ucp6*d zs-Q9}z2S_{(p(X6C*6n4ivM5FprQGT{j;6PPlb!;A2+xSLW%+KYy-(l_(&4PY9VXf zu2=KZx42*id7;TTK`)YSDeFgU^OuH}lYhHG3>bq$rd~@!!z?T>*T3I)_VK>ZKbfwS zeMenkS>_WI+9=sT^YHU_bX$Mq)=*FF0xiC^qbNI5ALnEw< z2iZBOLcBJ#O>qu&?7a>eRTZ?v1K)-k&Ih&vr-js`kpNzrXy^0rDAz*$>hMZRJqqnDa-2o!tQd7|0PLao zc2#KJ?PsY&$FOs16;4dts{4Tpg+V{(-^je zn9v>O1RZJRH4biMwkTwoAnQ*Nf8EDI+c0423}&-%a-K^qN|cTe9Mk)DKz^DRN=Jhh zG*q#>L>}#AlPF*Ev4yk*!fM!vE)(^2tzPD=)`(dvc(TOj6dXn`-qdD*%n4OXJt^Y9434C|W zHySV1Kvv~<=vmA1*OXp<2ks>%p!Xk+vwJU!3@&w7E%)jM36eA9loPO(paaoCeb?M~ z&|F|_OKf_HvbeugFlkUzabIg0R|FEM9D;$_mon%x{-4ScRpoaX*UV)!w;{w}YEr>4 zAO0Cf>j4HX#PYb@7e=w2qkRcL0s{z8JaLtU`v_d|-IT14^DuR9ypO*E#CZmi;^tA0 zoJPPBh3eCG%l<|;j3;)&LX7T|*z-%akL$_sM~+g4XGMl!57*P8Jd>^@$ISm&U54Cn zucs?}K*}-5LPtXkr?u&aPC;lzJvXO$?IqaQ94+$OY;N)t6HQ6KB{W}79%CnEP#lt) zSS6vo4WZ9nZU$GSoEb-~xYwGs?+7VEXwsUs+_D%p(C$H%-}oWT)!^mr!F*(M+ptar zy6Cp!SPU3h05lqKaz6dopQ)eR^b+eLGC8tNOB+qS7?jcF3`;X_Fy5Tpj`?;7WOuEg zLjxzj+?K2gBYPufNCK%U)E;*BD9?c1qrPzW0^n=-^sRXDN?uYd=y(jJ2wy>uk8J&o zw%R1|5FuAdbW~Sw8+&{^z_h&M*9*)7rLQ)l7R>XSg3}XoloO(zhl3W-pvv+a2P&i@ zMs{|W05-#V+ULsG*Y6RxR`9f#S`ME8{LC-pXt!6o-WnF( zoq!SmGJ+u>4Cnw;mp=%A1OP%906+i;VF)CLlmLj#zKfX*vEh0*lRU9FFfykO{6*(v zHae_92FiA#>Kn%$K@Tf#Lu|7`iu}rNy}vH4FHzme7d>U6@(^`tzl|1qUdn`iJ5AYJ zt%NV@g;7-Q>AikFUuvETNOkgx*&5#8@1m|!BJ1$^UF=u6q5oup2zg8w=1 z0H!sZeKE> z(SXE84Q}w}|HW`uLN(1t?qP0c$b{?TNh@+9LEp?98_ea+S%%2R3@-!nt}$&9p-*sf zr{~Pr(G9@0al&Q8srzzoB}1S|&^^x55JT#XC(p{O71z}`R4c323jrOe64dcv|s7oI+_gI`Ns!RrJbB^$*XD-19H^CH#rmLI*!Z|fofMkc#CU^P;g zVlVx3aX@oFs6!`Oie0=YaE9{m0=gOfCR~=;XL{7Sr)xHO+$yLvQPcLC*bQ<#O>|5a;$JI_we0&6- z1xNIYdIlrOj%21@`R#O=w}6ts8RXt-W;=d8fvH6ODU7FqlFIh zQ~ez)1AH{d%uO*ci>qHrxO<+(4(Hp+f2mF-l=vLqG*jn#}Q3RO|K4`JQ_zg&91JQc_g(h z-r$uFll|44xQv<e@E{)&HvR?gz{>eDiOB*Q6Yy>?L6a4#?xAjDRhKX6a+MFccJp zplVnq#d#J!IitDlMv?15Qw4+StT~2Ri7mHXDD!|&6A>wm^zLMzyVLeWvMY9zeCh23 z80|30BwM`7aSl@Dbw2&lLGqfN5wP)+8u+s`aEY_Z&D)`uhLY>!+7{^npPWQO2st|J zRA`$6LVkA7WUDk}y0Uw%ajsES^}TZP$9P%>?w?h5q<6Z&olio)@emk`4{n{P+eGMF{?_ABlpU4QY)hUq@-e!+ zR|vG|Oeth^W_}l1RmDj_n)ATk9EZy#2J{NQU$pzAZkn0=nS`{z^ z0q7OHnxVPYj{nDBUdFTb`4%El#Bvq$vj$dQ*{}6s-%n``|Btmc!01Q=7IAQ(+nX)@ zBlVD_mv~>4>n0e6OSaCQ>tY>ua*HO(+2Go{1k?oJ$uAu&L{9vgmP@<2s#iIEY1w?v zhl)HENezJ7Z}1`C7^@hX4-jak10pRS$a$!Rs8v6F9Ab+IbBi&q8dnogvpwLUIx0p| zyk?bMQ5Ii*HJrvs6zUVf0^j7~={Qf=`?+99f;x9Z;%%AZ82Y3gr(<|cmuzCTw6s-Z zi(!IY3;nK)Bb2KoS5Box;?*A3c^tF-f`k4k5CtlPbE0wcOU6EF2%0LuM*u)gKClC( zaXzDEgf4laaR$pL{q0hXi-X%W@#ZpS<7cz>WimXsS3k}ouiKo zf?$!MOeV{hp+^Ah%QUBHfS!5=U;-$Fs6}W1QwGm?YfmH0N-M|WBQ;yl(=%qvN?r;X z0WN<*fIL)$Jq;!af{^<(XnKoF6G_I<{a>+lp&LG*emdfq07UJaMLh@C{+aZpa-gQ)oPG9`&-dzG@8N5 zh1BJIc{q_BVQXz6#s{K(&_1=HgD%MPkyW4pQV~4VMgDVhyYzBV^&D@4+Q}1#gZ*wG zt&~|JqtEgq`=AwHL~ObE^PDTK^u2%OSO??>Ove!hAY0i;PR*<{QIjjwqzf0i(Kwcb z!~ZGBv~Lr}Yr9_l#9u8IrR=pGf0PeT)Goi_B0komztp=rD@?9uAZ$cugW=(EciGI097sBd=1zJEgFeA*|h%1M)w~ z9@Ui7RXjLN&epePK4G-E%DgM~BD({%HU!A+4aF`wWv{DN)-WJrz{<-QWzS#nxUKvb z8GRKthVBV7?pk1EJ?I0B&WjudNGl7TSnSvRgQI|NI4uNbN!X`KW4ja2T`WeCAC`8(c#Tq%yFQ&`ZeJcD zZ><&bp0F&aO49yhT4c&J+zl17Qv-A@98tU}XLT|OHkrSogwpLm>$-Mv%jebPJ{k>U zWJC4ohFdv=WRI9-p%=%==!r!eO32HXQuvfUoZ26=jLH=^@}U?FC#MQ^mGBaBAGKan z0R|a$yv|EDtpBw_C?3OxA-Ocw>y63`_wJCS*+ehlAT_!9Y|hLVsv0j&0u{wc)w0#6|w1gEK7sfWAfk%J#rUewR{}< zNt?01ZR)13ZZSfC!0jKiT&IQIm$B%tj!(`v^|6Wv>zs(cT^Sj=F5E0t>=$yeO4|_l zPJ!lh@peHM#Yp$e=!%E)T>lKSCiHeuS!-DAZwSxurkBqnH^1W>rVOSdhCgxm;DOJO zZIGA(iMJqk9s_@p3Stt3j8J=up%dWTI69Y}4U(%MD?@OUd&v2_Jjda%ET)&}G-S8L zK@0Jj&a?@HI{oUpVXw$7CnPut(pN!LS1hUqE8ypQZ#5|vkt?#{c|gTYM#;dfsB5*#WR96H8Kb zLrPgRv;NLJ&y=Gr9(RfIq}n8i&GR6(*Resl< zHuWv*9$L=-hBOlr3BCU7sd=q-wrh`e{GXi#r`%?Kxl$0@_TMxpYV!K;zE!W=_9ytK z>6632Buo!k>8i+dDfIL%ei@wiB0i9C7fX3m81XeEnqGiogDPBJdFW8K36~TiZ(7ca zmzMjf(tTdNB_V;=hf{6V{0{Ed6-tLlFwoOtB=Y6SjujY!rRyli-NM{sUESoYDoDYJ z+LRA|T4CY$x=kaz7ZqO1;h+qQ(hcs+ zkao7`?A-oT(a#WhzO@Y>5Xw==#dh*csW+1e$DMzR7IsaK^3*@`gq&En{RPf)6`x?E zi|Q6U^R^KMY$P>^{l9uThq$AH#p631V*cqV{i-z}Hr$RFCQ=87Yk*-YB%T}#2O zwcF0+?o^DWcs!yXfRb6?Dhu7=0r!Ubt*J84@M~ zR=nZ=&a-cRiC}8!bOK|;7x40;Ms_M_%+tUae*lxbWxN7C59on^Nt3t zemj&tIEzT_BLtzjGg`reIXCS&7Nt4l#KP0~t)Dktf~BvX+?}n}ZwdQ3{&cuJevk%6 zUWuS@a4xfi@riE>PH}_|BR@WT^4gK)&=)&d>GNT1!m|uo&&1?D^pc zavDXw3TKIA?*;SY0|;G)`l*cCv9!asB=!XZWzN6fehr?S%PYX1OCxgfR*%cIt;UuC zS+`U9sW$wene(hMiv=A)=UdjhP>!W0$tJu?P=Q|%{%!Fb3WgT^lHQ6SdWV*xU=LS1 zk(ebiX}d}keCYEACHTqI`YwHRseW3&!vL_m3m+7R#Ynq?B@xdUnkb5=*Vk_so_N@2 zld;=`CGaUaT(;a++T;AQqXZiryk9@Q9y$LM`uzNE@#ywA!y>n_FKKc!1RYCbtE2K; zc|L;2cVP!ink%#lqCoku#?6Lw|7UaLP7ervT>p4F5mFpjMc28ltoHnxB{3EijPuEm zLN9EbSHhntIjGqvxp$YYXi|Y0;8Cy9cANS5w>mnTU?crY_tQz9!=kv`0@5J#m6$}TVKAA!B*F}cCzo3T^=xUcG^DKH%Qc=BB* ztdoL1&zMKC1-+qcgty1&Qaxp+r(lt2t{PR{X3Xlv`}#2noIh$WQ&T~7)dE$fe~3eR z9MDR>3;efwJ0Fx3^gT@ex|1j<3Z$X<3uZP$vVLu`fZ0*#)eoM)A?i7h5OC?tNuy~F zv;Poct4FOXBTmYpcm2TvTA7UY{s+Y{@4SQG=6HH)J&f3=SM5=*GaJb;Ok-5&uRy=0 zU)z~6O@&SKu{a^tHE?3!{zmPqy5-o!E=~wKRpM;hc}!`X?PVFhWqR&9{(5;Cvg&@t zOP;i?D0)NyLnlYSm5WhN+-CY#5;DmNC^VWPQM z%wD=S`MM#<`!iCp1aLwh5w4V8p%s@FN)mXpZgy_`^-R5$ZPK(|Oh2kRM2H5+&(NVy z7O8;Pj7d*tPbhF3J9GHl^Zv1WhUC!Fz;`rP%Oug&2zXw@+?*(EHffXR#;SSfl+Wjj zq5Yvz5y;!97svlqokCD`R_=vZctkm#Vt7faowKI%TNi9h$c=B8YTPb>%NurN3^?6cZRz2XttUT~$jxi!#+iV->iF0$~?%lwc-#{oK=-w z3{;qd_A4pLa!GsV?CaoGbE5uhkPgnBqkfC*+SjK^Bm|`+Dz@t=UWROGIoD3|ocr!O z`PR2PG5Q6c(gTRJnO+`zsD@($5w2i_#I2E%E0GW`mJL>Dzr`)#1I+{z(I~;-pw!6w zOIu2f^R0?4Py8Mrj?90^aQNewe7vuqkbAMlw*B6RDyA43i0sBTQ=cr4cFF;CDn_#1 z%I9HQ0dsjhc97%muhcq8%^hsnJ03|aRlua$X3Ul$EP$h5wH&A5rbY96)%ezrzfP-@ zvO*>@Li+BjWJksk*{I4M{gC8w)FeP?4H0-(^0(BCKD0inc76vj_HX&#a%9DmYI;*? zC=_|J6@QJtjK|W4T9#@wV)?eM@Ol)5jqJ_BwSuK(bkNxD)+I;V!8OE5Un{o@!*1qd zIqNW~UB_^d*&&&QjPUaNr%xK?{2UtSKqDw&X(S5UXXX}_RchxdKK`5tR_ImTB>ygK zoOQ$*hj+&fT(C8dI$o7CYY5vq=t|4br-CclDu80`sPEF=~N@I{45j3DJm)#IYC z3Zz=zQe&nmXs%Ok4t$=!{LRD?)nK{nN{k=Nd|)t`2ri`N)+uhWji|Adh+@_soN%_Z ztt-rk=C3X4K~WJWwf%No6Yc~{CW^pwaa290IJk2Nx^cOzg60FDcb7iK9wgYy6jF`7 z<_+c9)E=qt&~!GFw!74FA-_mNA3x*swl@6Q-Y0lu#2@09ud>IGq3 zkTT{5C<5XDQk7y!VqY5|A{fOr7;s&e~L@f*FY+n~6ik*|SN#nVs?BFzmv zz@EF<92T*g{rX;SEVl*l8N zx1@ZSENOeV*B$6>d6eIz0CG^EI%Thnve9G_s3O_nnWMdl$C&5uq@y+<`G?OiXFu<~2#&R>J zTz$m@X*cXt-8Uirs7e-VXwSBcMe--5zQYC9^9OysuexRKpqb$5DOh}Jk64dtU4&Z= zBhHD!7-cPDZ8x1r!(0C1v1gv?Ud@I(ZMu?nk8g6-N@dzy0!9VrHOoCC<-($G+t5iS zPRV9JyL!i8M>=JIRJN>IbaTTSq9@!=Ap^)tg(H|Y?!Ycnrg=TqwrZWaegbC!gy=~X z=w9=1|FNXzN=3in^^&GJZk@e9@b&3qLR7o2TdgN!kf(5iw)hLw-5TUAQZwHx>f^tS zkY{tgza)&{x<56~2d^m1QAU=tAAET55L%F;XmJv9F3_Bta)dx;@N8-ymz)tBHZy(< z;E{6H>CNOMbc!RPWvtpvYupqh3rR6wu&*5uY1Ntnb9 z2II`6y3xKA;Ssnp8xwCp2Ggq@Pz#o{B@eCTX?;8Bn1if%IwSIeEdwm+^fo-0R<*Xr zt~C2gPMtp4cW#sOlGBhzybSJ5E;J+66uH+0{(_f`CN%kw>nXAY6V3&87pep_bWzB< z0T`l*n{7+}^M0br@#bM27OcXZsX{aS)##S&n9CUCDF>_J+9DYXj$ksN1TX>t0H-c~ zIRGJH0wxr|06k!h1Hb_KTTRVQPO*7BwRPk4gx<#%uvQ6fss<%1G3YnWq8?9UeT*q# z1(00vld)ipRhbuvU!?gwklmzLv@bl5`Qn;KBN!7`ya_MD4EfKIh^En`>nbi@eO;EC z>OAhQ-1od>7`9{-%kY2v07_!JD_^`WehY4zbc<1%{ z4kIdamqhn3#s&>Kp-=(7Z3ZI%5tPG(&UMQEgqHvb!s4T>LTzXq96ExCt@FNTY&pIz zPzZU?=kmozpOy|2^ZYTe4FXj<|LI869*B=i`}H#%>d30Vz7VgZuFjtL-f59mF1aPh zBFDU!c_S5VzlQE{jt?7Nd~OkCXsK=;aS13Yw|+OUd<}rf3xaG#{RQ7LkBE+{-Pj8H zUc({x(Z7@(#FJd7Pkg&;O-+bg7#t^b#c!ImgLkF;EYu6j6kfwA1> zOG}DIk|Pu7KkB`HER@+rLY^|Eo^%28xIMNvMXMI0(jhxN3!DEe?8*kv=3NFiWiGY$ zgok$-tq1UHbnhrNNk2(8&=LR|0mu>nGLHl>20&2;ZhkocDS!Yb6d=HX0|NuV0Qn1r zML|nN`zuBRUEj7$UNw#^3liqYG!CG{6^Q_%8xPA^^mHXkuQ%-}m70eEJbrqXxBs2! z%_kai4Crz*7;lGE$te!N^fg@%TLf4;32m;^0N{~*D^W(L6aOb*v(0eK6g^JAwK(sf zBiD85>1PBIa9xM?3UMJ|cg5?~6U4}L4v8J)-Cx89NyI`HFz@5gl4&6fXHkEWolzqy zA3qEzb%WXq)oF@_5`$y0d${zneC&?%2ggZQuwK@24=dPIkvazvx>}VfAwH%wnBvnZ z{2O2%k4DdVv{}c& zUi6~ysGgrKGI)`9F^{OHh_FpmJiP0fw-8Ej#&R%rFivGMdoE%439!4hc*A-l%el%v ztl+z3byGb_wtpmkbbe=N2}g8Hv6$|O&^a5mO{borQ;aTE`SLwcd2s>)T)CEjAzs$= zKoU-s_husxEM2RXn?52(_O9v3D$exb3r#I7<4)-|uP915BopQ{X2y$V^-*e7L>c z(47v^i}#PVz(>3M#1s)#vC(3Sh=4G5`{CmAM6ykXzk(6~8Uff605YivCCSt#qD`8)R_ zNekQ$7*|L3FPE(S8(hfJM~`a^a(!~gh@(h|qi!j=Rmd4$&m%t&!AObd(Zr)H#O1n- ze1y#@P=kxlBGt2u802afYyO)HEh2d~r3~?l*`efMvn)?qe$2!GE+x9ck0jhe@7PKK z{2;1d77>sL2le3F03w96M^o*#WjfmPJYtEQrmr~%!gB{Q_REqlTRR#wAkK(XqIat| zy(x4Ox@Q?h>Q_GCQL%ZWBbqOtBag>cq%Wx~LQSp7S(OzTN%CMO^>mPNIgx>GZ8 z6kih)v=nW}kYm!zUvRsS$3LR)sd#MMQwfnupvcO$vu0)*6Q+!=6}$eW2eU*QsP&AB zPH%(a~Ch1K1^#2cElg(5BNU&Cy(N71*0bXhvKYUZYXd^i@9`w+#KtR1xBb$or+5vuvi zH->U(;9T6TOmLA|oO5IPo|=k&rVt7S2S-cC7SRK0a&Y7L8VSXmj|z?|jwNcbqq`^S zWt-(DXG{rl{qG3uXUiq6;EQHqI%6g_v3OWh6FVr_+b=>%`AA0S${*K5)ZtrQhhp`f zJ}M9(9&yU&7xPC1XM~EjT#uPlcEB9M*#&{3l&Bl%c+cJfvs~|!YhY+lwv-d9KAI4@ z%)d`_UqE>rda4k!3w+%3xi+iP`+S*0ZfwYV$%JT^q<2Io^LP%KGJtVfd9A*5SaA}) z&}WZu%KT836;2;R%|@?d*fQS+CIJ1oYb9^%646~_r+iM;+AmzeDyH85>_XMur;OanH8zwC5UVGE&XZw#|jDjO$F!%JrSDuEyTEr?-yW4Synw=pK0a zafc(!k=wA%htW|~hW4mJ8H_`+#|GgUEn@ri$rb$Zd?W=v=~pFBd+2G^X|Xd%tI2F$BA^GpHw_raY+g0O8i-#APCLz zj?(vUIHWtWy6#@*C$m6JZV8f4WYuVJH%vLXE;#H28MQriL*Z8b>jDt7NF%cq+v6Cb z3RG!REL(Le0FrZq&$b=SF9e9jhJGmG~?N#N%f8Bf(A+_Xqf7>@%`Y@omN$U>K>O z%e>rEL|2C=P=vF(vn*baA#uZEhSr9jw&&Bk%v^xXr#)Lpl6~f#X$n_xK(3UW`~?l= z-~w3-%aw90OKZKa4QE$ZDICunts4W3HTiCD8 ztT3lZ(4{{mzgz{0e@m2eT~)xLcHA=(B(p!t`ZB=`<;6JRueyPKc#VO+3s*rOPry+D zTYWH5=`oZs4El@}n=1LGzlFz81bUbw3930M^qx zD}0e~oQ)&*1;S5X>XMEZcgt@rYPXTcMg!#aMYiT)ui}yCO9mqhzEl9-eG6BJ+9QB6 zhXgPRL=;9=emMXcVvZ&efj~GA00Y1P_pdEMt2{!l`AN{#LYgGroob1+S1AS~U6o8O zr^zCzBB($LTEtu3BbX&wQA_=q@K*o>aj)Mf}5Yfj>Y^v3LwvD6k%9bazli3bZ(_+s(Zmmzgo!MhKGhVwG z9=qA{_vs{Nnf`n>Hp|IP^aO9OC%o(ZBmgw2sOWWFGL8BzSlu`|uq=YI&C9>&8-@}E z#(!KKc{A70gS}g`=4ufqJEPpi-mSlMH8ulG>rk}^H#Y_{GuKsKaZD3iJP>8*mS|Dz z9xBrJe=76u+~^NluiSp+xAgX&mwwwFnF6ZC$cwc{YF62%nJ*k_t^|g#vE=CVq84t& zL8E`t(0gdE0U)D{AglEwk+(q4!;k&lI4){zvS-^;QGujr8(!|qKD@Ez$hu~gauFXY z)p`xnX#ox_Xap4qNxr2V8?ry?uhKkDTO)&Nyq2^LKMUajhDSG7banA>RYC7g<2jLF z_zRtQnpBxi8!DIlFTQ>oQ$_O@G5XB4&D7Kh!w;@1P!uYvYJh0gk)79)_&`yNW8yx+ zBoY7`0hkg1GS&ky4oC)87JfMZ86b!zAOI8+0|W!W0Q#Cfm^RDJO6>4Yt$5oIn=%fj zr05m3Kc(7)*S=ZicxFr~h zX_o$LUUmT!^+q85Z}D0GFiWN~@r}q}^}+Z4|8t2Zgxj`*1vYt-N3PX{+v2b253Wj1h+<3Ihs1ZDU$4tDm-zO#4NaIuA$Ux;GcFl=s?1Q z#E97co4A8|6?cOncbkLWr-ktJw!x8li4yrW3f1yWV-Aw$rpz5LrUldb^I{Yd@-Y2u z#ql<@o9o3h-ZCgS*>MEJ)1NpuEnxaEL_mN5+r`9ToM2p0jLrHGK&T%3P2@G6F(Y25Fr8+0w7=*U;u&O0QiUN`8Jcd zXUIL?;4Ltw%(=-aw`tQwP3V0K0G>c$zv(85XPy8pa7BjH71sQFP`URr4$@ZVOKCc66As;=qXfP+uhH1Ln}7XXpTXbRh#~><~$}=s8|bzaP$A$1;ng znw?;~IZ^ml6Z0_2tq6}Lx)p-tLU4HB@kVZx8X4zJC~(2W+(yJ0H`jy+sXZ|j2((-C zy-xvP^-3#T@J!}BGMVy){%PmtfMvYeJC;aop5y_~t0X<=mOd~uAcRT%>mIj}&i3U<$-AxE)8=GWgLDR=! zQiX!$ckECznEYwAB0!4vyu&txGZ)ArE`ZWMEFTt7l{)!K%zmEx%fg3J-P^97j))qR z99e|^o2YAUU=Y0pv?Q)kX@aTs&t)q7pHI|Uh;_vxI0>Y+d8x83TywDq1r3g{DhbxY z$LHCGRdNb*cB_?=r}o?Fs;bZ@>kMy&LS!uG)~R`*?_VVt21XcHYtk>*MYG`24uZ|; z2GrdFj z$5=`nE1i<4p^-^)0DqA)13TG9FY9g${?mdas55gMU36?4Y zw8*&a!rBZ78`_UV{_TX8$51@JPB8?qJx6W^dh!ld|4q@at{d;j>eb&bF$png1-jnZ z2ZPVsW-acozx<8*G3bT%-=R`5>8m1aIlrWxp^QMR#Dm1m;5 zTCN;W(!P{ktCT?Qi0(QHhDv6}>~?%(1v`Q2Hp(5#<-${#v+x)p*rzNypPcSAgdauF zu36UVo6c?3#{ANF%EV{4MKHOEs<9i=cu^=?L5JhJuo4R+*Uwy$#y%O-J&N#U(?1W1 zEyVGN0I|Hz^LKTceUBJ~qms&y5yHXoZuh|kxk7UB|4)kuIz2B(j@!mYs*vq z@;Mz}udUG1xD}_|(}7m+0}hF-|EGgo5pWk(%d4jPMVZAr zR`Sv@GwS`P(bI5yJ|_ojNMnk?|6`aIP5rw^Al(K}g02cZ#r4#hRh&0X7Zt%+29k)i zHv-cLYvEl|W5YVIhW8x9&y>o`So%x`adBA(u_}93o}S+uIKaKL{>e%nW_2@&z1-=2dGC@QM47ILCkrT6k0hQ zIMWpeC1mTg@5$B(M}D3jBx8-uhKqTB^XwmL+1adArnWO(O>pDOjSfVez}9^0HJ>tS zX3}1Tw>l%zB-SKS6ND$v_8D#yHUw-r+IdNw*nJUZDTAD7l`Z^`_#}6hFD%z+@KB4+ zEU{1bdvquu>+tb&os!<~L?7G}IqNE5)qvj4&9Ty;wjAD3#lndQ$dx0Fit0K}bT5UBVwxw!g6gpCgNk_gOL&rCky~zW0$uQ%lC@*{51Oi!ZG>cLF6EVv z1_Y15;TKH_{|VZM%zi0@YFKoJGgq$UG5Kjum=jS9)F!LSxFpyoV8a!0n=r#dSwZay z1B`FXCZ1wxq4k6XjZ7YEOlm!dyqwW9P({hGLPP+d-r`2E84&oGCQwpCl>e$KU(6S5 z3yj6zfP|NADJ9Q}+}fQB$f`OxuFVBM?Ur>E@AaB5&$w6Fd5JY7iLF|pbAJp3;;j?0Du75n^k}k z05Z%RAQ)i;4za%nzytsT5dZ)n00ZcDjY&Sr=w}xclyqb$z{#LKwi05an()#_n|y`A2)-P_@g6M^esS?0 zeZ&B9mAK?m`-T})4Dm?EzM{c*c88jlNO*w-!BowHF~HP+{rO3%_X$LkE;1oxKO&N3 zLRzHBgzyO!Y^&J_4&BD#1Q(P-Z$m1Orv0?HwpUVgkwcA{h{ZK8XvgA*OTkf+Y@MzE z;qd>evHfIIREZQ4UiHkj8g9;$v1e|zphvp4((tI8E7;p;L1hkE!Rar17~Sb(OzFi5 zSL+zthVl<}U?FDkYrvjcVzRJV)P5=i{yjJjm$xPD>swc2p_uV7^|lFVHWg4nrEE8) z5E-FnM68jS*a76Fza$DI_eF~iY1!}R+}wkw@sT&pmmHp8;(y1(qD3aI`&l(Kg`=wT zP4p*hO7eV64u=}~YkchGj)NXo3u+OHd1B;8-1@3#QEqau>f9@u-J*|^Y}FMT^5YVn zKDJvNTjBvV079RSm$(MKiMXX~#D9y##g8r%Lk=cg(MV~YE^tP)wDBoAspDu2i^p`o z*)+5p3hqKqO#bnu4>u*}W+yz1^?!P?~h| zDf>B7k6EdGoSUykTd#)r#1SG5d zY8vN7i1kf^WO8<~!q3Z<1Qh|XUaN=p>*E^VAhWWp?;A3YWgKEPv^m=R*;LvPUSq8k zGQWkEje^yM_)_PaSiwQHll)jP!+GY%WO4R7EHD!UqSS4( z!3{9L%S|J;HJydf4vbYHP$Vk3tAxws{VqdnVn>A(Vc z;2-s&oLW3!>9CJEA)OMoN$}ycso@>fT&*?^jm}9E5IOVNmd=ZY*K^}*En@4d$Ip++ z|EPt-xU2 z&dV{y_drdxOmEGv-THbuLttO8v#QbmG>}R*?LaB`Njm|XgLKI3o}Xok8posVufP-- zIL_nH!ty2FVXvDxh)Dcj#%@PryQ|nOjW$?j#lIDKB|a1l>}6^fQor2rx8fHTisV|U zHOrhg3Nvjt#9@1sct8+AB6|f;y3Awi5^W|Vi3;M@_PC^HSo@BcnZPT-NJG*hgu7?^Una;#o58auGU>w) zgJmZpztI#lj})%2{wIB>$x$p)U76xYXvD&2E@b~Z^>lmme&0(3%w14vxW~ugZx#No zRgmgsGZ3n`37L#{C2e3WaT&C?aSj58uqFt9K&GQ-m|HjpQeTo06eO;O*_wtfvZnsqhm9|{mW(p+xXdk zm}U?|buN-0LBR>y5)A6kd%f9DMT6nseRL^?Ih^5S?8celxvLbo=_Ca*%5oF5^H~_x zP&5!pt1YN>uw}DFH~-DHiyBaO+IB?`bK?6KW$yOuh>*-M;y}B&oc=RO_sxmzjYSdN zU~g$yLB}3#kH7F(B}-h`6q-H~+l5Jdk9gr7<_GkMls!lix~kvO@NYH?TChhi7;mqp zXY)w0R1y;c=%xbj?K7ItG5S8Dm+v+MD-@qsvSErB`~TfRNFY$Q!_RsY=$SpAeHC>; z<4ToYj!8$6Krd2pe4+b^e4HrygCq(Sr6|UABJ=hX=Ina&={zk9x`I?q_v0R6rsI8y zkis$+2=8TSI6cktwJUzL(v&IM_1SrA_4K#ym&%5GWV3xuSEI55T{>717-(Kr;hM)A z2^-Gd11Qs((BdUH)a1HG_1i_LPsD}o*o?5MK?+NCd5UFBN{F@*+Kuz`w{(7>#)li? zD-)P87KJTu$*Z@z&WDt~a@Pm2Iu>x#;rUX~m5&yvlBytbkX1*(j2DMKcp#!tUBrJ;D55c(Vu}*7$m^yzKEing)(I+L1B)ZAdd8V zn!dvm3fVKG2dcxPN!PqdeBDWgrov-_XveSD%Zs8$a#Zm1u8Bgr|g>i!;z zb0H4ixW*luAyz&JfZ$=IAMyyKpE6{r*$!{4+o#=hmJ>HS5AoE#%hGc-D4T zH405y(GfDbM`;=ABDSRA`j5lHz*fz z8>W~RP~lPrj@3#qs1+Ez_6cYy&UNL~dfQax2R9gUVo9LWQ3X9xpFLP~ab6( zD@~JwS>o(XEUP{U9T23Npp{{7(xuH63>oQ2X998!M22ViEEGJS85d9J;)OxRYAdOh zlrWmDUgLtGoC2wH1FkNw(5Hv9pWX&7m;jD-52ciMlxtFVxZ%K zFMjH{Jcd4LMh;+9i?Pze%&T4fps9<7$%e9;V$RY^$@Q))5b6u!=QZIW>&CJbC8kna&)ZETjKOmU1ekBtn}Q(84#j`OcA0u&4H)%RPR>5ggnyEo z>&sRLTx=-dPSq$dury<~PH%298v~^l$^m&dF~p03>mV-mdYdIytP3^;cQPL`@@FQ&D+%aqDp60 zci<}@u*^d^6#P+g>1`JImDV~aQjxO$_#I?EQ!MXceT*4RZJq;!ij{j@Om&3Q`vNGU zH5ES@#e*}bSeawA46GXVT(EOG?p?6&Oim80soDnsE~@yq_@V0~bXx4BLYvF(qFsZS zw2(_2^fUw}55qyCaWSkC#avwL2N}31-K29uoKjBMt+WZV(5&6GX;*E)j=*}=%Th?f z0W!nUhBO9cs6Qh<5Xjr$PVa+SNEG8@$>{RFw!37iV=>dhV#5`uOzRq#jDx>K?l2xH z(>9MVv--peHDA{$A&6Y~vk0fwdl9G(X)#UFb2bht>NC4vGDy?0iGakyt~ntU9Prll z`E7H;Bq4*kiV2R5)z6R%mnEYb-&Ia2DgnzdtJsl5&=^`42X3);v_5i6NwxD}OAFR` zoZ=v5MiVPWEDH=Uj>S`6tQQmPjgmxN+nT_h*8U}-x?V~pnZ-O)BN)*F*{V+ z`pg7d1qWweKiV1kx1K#SjsmQgb+{@R`6|^HKd%xv?aI%sdFO^*awD+u8M7~X^E+bc zqvf7qBhjyH5PH)X;dUb90=WEv2(XmbZM`c!W-jSTJ5t=bJr*Y5l2f@a_#EpTFF(ig z>pA~Gw)rwS{KAR*Fs1WeMc39N@n<`@8g5o3{>M8)^i<5?!bg*Uq=@zt{78@{#J~G@ zRKiqaH|>R>Y_IT?=+OD7M=v_h9Q%#7pdsZdkW>ncH{Wj}tVD<%XEQncdBXS(>Dpc1 z50lBjMOhSQ-nsa&>*%pUOc;8VM8C1$WShWsL@n0a01R5LE(luVx=HI7ZZTP+i6IY8 zX;kUT%>D?iYVq6jxx5-7T&e6*XhYO#3qHqU&{wPsJos+eP?Wbx3`D+}S@VOH!C`GP zolYnOh5~hfMcw@z^~|sC*&cZPQEh^8C$)d*=xryQg%PV!=3#vbrq_M9`Z0%e!F}gl zPvG1&0#>8cP~FwWiAfVh8dV<0cas$yvM*&dv>9E*m3q4b_J%g#zL_T^L;*W9FwtF7 z@FFmcd);>%0sc1HN8w0FaTW0G9@PS~CeiJ3#bUBB{TAy|=bOLIC+DeK)dcFF5t!|U z*EVvpMVdcHuT{Uc$FW;OrL`4A#PpzDiq@!5#m^Y+vEh1;obk;uiimX#MJ7Lc@dkqr(d+oC5IOC!qe4hE?)wWQdn(hp{ z-QhZCx6rCG-O0GbV!=u%tkeWAS`j&N-e@p%$MGG7(IBC#aCs48whYEGY$l71-7aKbDi`{YFk$&_1CKR* zuAZyUal=wP+o zX8j5mmSE%JYpM2mN?|$gG|iA~Hp*866qioLNXk!Ke~-#~d?loPnOC5=Wwz4x6nE!b zj3J~WP-8t39W)?xU(7%%hJ!8gqxlRvB2}`Zg^nVQnx*djiLl^}J*RS2bAD{Ei zIhWm-bPUlzyv|z+|89Ai@BanAz7y67q4~}-oqQFNOzfQ@jz$`eo>+t~fSNW7HD_-$ zV-&U#=V!<26Dd5=-CEP~<08%W6xa=KDgH|ot6hG|?*tkF?G|m*wTWpg$vHeA@a9r~ znbCoO#WEF~`(Q}_8BQ$IjXc5Xy{Kq7bJ_)6eALK?5btP5Yr5gP4=e*5wE>kQ8i8mi z|1f~TBi*%Swj7QmeW3P9mePGdb^p{%d4DOxnz>YE5j|&07B*;u@#dnMQ+pJ#31BaX z=5VskI~tyA>#$6B85?8N`dM5RgW-E;Z%gZxnF(3RWB(WR`9HHT7)$cvb^xQ35$lRP zHHhDr8HXF^Y7(B*40EDpZ0L&acJYmScIIFL_6l@cnR^^rQ5bI0%+p@p2vY8xKJ(&~ z9ANzo*_f=#qyjEj{o6|%WN5H;cqQt-{eaUSXy_s2LFqNh z4`lrTavu7Te4w9IuM|VfaVI||scuF}cKQ-v&bK(g#-o?7@$*t$(Q%6i>q=0k($+sIISgYtx*`ur6jDoBe-`CdgG#Q1r=f%a#EN6+ z|J#QP@EpidHX!jTsg#U+Q?1hpL*-hdGGy$!Y8Yii!L!0!=GdZhWEv+YP_s7g3a*~h zo(q^nZckC6X61WbL2La(;S&uD3XsAzkAX(AoC{((N1;iZGShQwQ9fWR==0ztZO3hK zm^Q(y36NS1-{MiuRueZSWO|7nP0*au`#0F-2nVTT-zS$5ZO%h1JI2ApRkNmJ#LFMS zWer!dtxL{bb7H~KITGmiPyfSWi+~s`dw*$D^<%(K0b%F~|nxIrlUf zDijGcr|01(e2BDI&OAE)H@qA`xu7KTUP6`$qqWB3PKYC(0b|?uANu9o8~d=&XrR|I z0+|+k$6o>;L1F0>4&%8uUeqGMGPVdP4kQE)vA-Dr45I)O00{sNpnw730QvtO+&S0{ z0+O&9>AC@)R8ijOmkJLCdf!pzpUm}y(p;Biwi6^?zmUj4ShJ;%LsC(P|3O_TCdWO( z^rF?DD&Jq7wiyLn@|}rBz3koxNR-Z43zmn5hTE^N_?c3g^ofmNrs`SUI#DZ(qJt7b z*Md_w;CO`>+}Z?84d`7%X|+6ThxmE%=&aFh)7Z);3W44^7dlsAl#rKj12K+&Dcp%2 zXnA0qZYZ6nIUbc5CK{}c(h?WWJ+Kfhkl8TL@S$X!4P+fy1dtk%!f9&gJGFlYddqZw zlmO*zd7Ut>$@3OCJIgUk7mTGMph_r$;%s~zvRA}&AKSk)dey&$TS>K0=v-!)?yyoCa%T(e5hvjY&=s#40tRWATk_dM>sF& zF!u8tt94&&M$*HpMW#AZ+y|G-2IuvsP-;W1rlNOB#_8<#4bjg_Gk2D(dP@AC&x|$5 z=LTMCVg+DJN=20Ej-Y%ZtQqhh&Dt7X`}EPirGAyCag^SG_qh8=0C}KDr@4=P0?dV+ zOCTpn{-epn-jQ+Pm7Nf;qTP8$o+Ks)%~_^h6SiLDR&^4 zK#JQiMWd2tV(cN(NPnz|qO!&r9a>a;%Yd*dyEISM6SGH&W*%uGRyW<~G7XQdr$b7h zw&i8tS}};lPuA-blCqe$lMDhqic@elz51PPrsncx@hOHmH4R>E7w#928$aR?9VutS zc8cqKfIKzR-libOu!#gH6pBUduJ;Ziq`oi6)ywtWqoWr=`20)EV1Wy3yH^E>qjVlP zFcw`+T;Q4s0QEg{S=@HMMS$L)=@dhg#gJZ~9*jKNoV`59^6eWmRxmk$070F1&3zVK zH2Z&Sykd;!Ul&%2+ijnHcbP?Y7 z2KKTL)jC*|hi&;obBBwemZ)HS50CdOa!U9QMnmG6lOtj1HE3wV3FLFYKcb6--$Vb^ z9=c@pHKt>b>Kw1QV6QaKXc8xykfimC3OJOS+K@t&X44u2MIN9a3%MZcn15e>ZNZXS z$KSGOAyo=2h?AG)!8gpDk5&09gBpq&SXOB&r1(q>7LrOaeP5<3Vu9Mf!cM7;EQoN7EL+YR&MFGTG!orjVQH4%H%|-o-@ujSB@d1JKPigw7R$sXfo> zL>f^l;2KOOI1AS7(PsOEtI(1f&hF5a4oH$Er0}9w6Xx+*+{7)T8N8(_w4G+&cA#;A zKOYk*nT2@>V}So+TQgy^y0ITj46aPSdtJpCFRw{oQQ!I_OI#^pnX$Rer|mW=Ot7fkkp^u&5s83j#6*1S6o+!0-s{x2w1! ze3T6AJ3NK?nQl0U1P$KIJn?cs!>6z|){zAfhck4qOFU^bD5^rR1xy)D+;E`z{hkiy z@)}iA^_Sx35LudziyCc85*Y|(bwH%+BTfYt_AMCQByko~TwFl{B?FcA`WcWq>-JUv z+kg@P8Uff605a4BFdjh^tRsFlfEgi@B?Jdl1Hb_J3!7q>DB}R8TZYh972+=ApnR{e z-u)KZhj~P@v}&2?5lwZjhkCpWr?!lgwbi+ys>8Pmhicp0(=iuu(nWG}%(YJW##XG%41oNsH zPf#oLBsvHh#w<~wZv3v6kwW#;2T~YG`QpCxIJf?0d2-{i4h~>Puf6Nn9tGrqD_TLL z8(YsKyp@?MC(NAm;#ILowK1uDixDe#xx%!CvUK!Kv6mou~k(EP1#FM5y zLsFbTMHvdjF{;$)v`f39GR0)KX5kuago7ev*N}1!Cj$(w!8l>9#>l!!x{^{<@lUB> zhQUx)&zV*mfUY8w)V|fy?W?wiX0U{%rFo2F^tPl=1GJyosIYV-N6;>DX=34%exr-!AG7z zs=H&p7NFPTzmH}-#on1uJkS8E z+R%^3T!wnZ5&VGBQ3O?J5^+VJjyI2tS1%Q3Ex?Ur=O1+M8!@lg(6j4Zafy0~k3X`G z=93b&P+KC$a%VASsBMlWDl>w^}dTFf*0 z`(-w9nuK4#<9|~f?u{IOY3R)$#=nPhx5B2zk5mx^Vg_KhBGTQY`$@3=e?z}E`g9Xv zGDBI2-fqFP#MQx@>PVk`^?z>>W zO^a<8xan22d-UQFYt2`EmZGJ#75?lm+9#ROc$8b>vDjeHak7JI;`&fqJ~@7C|Ee~* z(3xom9@m|+>vBwAS<*+!n?vr~i`h?<9 z#^+=A%N~x@-Ebc`P95fU{XPBHLJR|*DzHJnFzCDg|4+g#5S)`2lOGJbP`HL)&dTPr zK_Nfk&=_Ugr5(D>z(z9wt&@l1Gi>Jes-U>)I+TkjKC1B(vBcxoyr`sbNy{t-9$Lia zma86fn^_TT7D-9#v~=YeVSYfITV3e?EhlwTYvE&MaUpr5`;9tvotAw@;=ts;QuAwr z+*q~U3Q2A)?PCIqm zngj*-r6kIHZ6x61DjtRoyornRP(>-9lcd%lNUJ72qLdvZh=6wiI6>iA2S8bLlTfR72tLd zgjV%_dy+-+@~cYXmWI&vnoQdSVkUd-H4FZ4u6<5IK6rj(P zHv{xF$mPTV1`_(88-Di4uup56${6NRzn0EM*8U~ z7h6a;t`0Eq7P4+F5`j+Ek4}UyTtc8e9hsP&a8AiZhM&dxqN_%L=>7%zK-iYzCIjpw zo7C{LR-auOubPH>|@Y(FB5`n!!J`BWD>DOt*slwbHq>yaZW466`7zmD!d7p+DdF z^WqB)$_9(LdJF0qXN}R*vOH}wGboJ$)%T^(c*796(NgKYfMl=Em(vGER@*m|iz72X zkqp2x(+DUMKq#&SzZpOTl0_2^ARrnbpaI|j`Ehd~K^WS5w#XiGpj4?|f{bd+_O|S; z#R43}uS*_EOnsdM`iv&Q*j(}NpgGmbYfF@*Go)bKLtuWgKk=6hE&O7e7>WIyvgtDq z)x{cV)5lFCMsck9pWU_47u=f94cYgJ=`!}4f)dD4mJ4De7LhVBZ%T@J9_i>;&igfN zL?KQq(K>D9?NkBAw_q36;b7{A1 z_K{YE-l$Y;d)_l2sz(bSEV>#P2KN83;n>X#k-BJ)q10BZP1*VasEvYAz4^-eglh)^ z;Ch#zl`Afda_~|JnJI&sZi~3Y8jPz@vw|Y587T#rf9@pxSfFTz1pGRlwdDH$7DCw* zBC@GPS(5Yo+BmUksYVUmX%7obnhk`8n#ZvC=BT(<-YFO$2taRpI@C+}?yMVq$6J;4 z^hv`-o4DjrgU~tXTp*wD4teQ)E!a1_inv=B3TXGo&mD&=+YaqGck@`E62OFX=GB^F zAu|GIrfpfgUAR-wsiIsnSB6=sJA+NCTfN@bEUqDvXl%9M*w_#e#vrUrZ66bJ0O0-O z%>0Qa0nO9oLrj^4t{7@MUNHyXw;dS5zA4E$-I&C`b|i81)LV`xRnglns=k>kNX^N` zIJP)O)Ju5o3WZlII^U$4=~a86`1-CvK^<)y_rq;&n{4?DesZ6@0HT%PZs#x+R0k88Y)^D)SBP z(X=}}O@ies$S(EKzWRB${&%?xF(4u+O|F`aZQhjD27!sM%C5Uv`~hcoYUtyR_Hk1Y^CuZ-sUvnx=C}U(D;Jn@TSw0O2$Y z5Rfkf?h_r;{zS*3HG5HTyZmr$oY8PrLk|oSQzVu7JbHLNzqre9<3eDL>N70tYu9{S z&2F40V)X<_qfND%xKUJ~1z!*e`NC9h$#=6j#C_K{pg*uv-waMUE<4vv$O8*r0?VCJ zQaTZ@Eyy?yn&l=Oy@l7-jR78Xp;k{Dkag10Ix>!-L;z|GYR=HH9hxZ#@x2li7OS6& z+i}V5Hu+8jQtCy5gHBcBg1Kc7h21ekvJAc;0q;}MU7N0kdOjl!`jXi$tuvd64Dhok zu28&dVdeow*fiST^ft(o-`o#ypfa@uFd|41Tnm0SfEdCCCKNyeG_ZpMzySIGh0W-U ztkhJ7>jq+;AI2B#+UD>+$ySNB`LLVO;GHaZ%8mL`r#W;qlD~#BMZQD^G4ef!DmdJk zMhUA?_G4c~5g(3N=7SoRj~9Ry=a3Y$pdS*114MP-Q|G?hDt-#xnr6VFQe`@!%eM~W z?+xJ9`mSDNI0F6v_NIy>Ln%_mHGJ&l89K>~TsTt|xNAPtV5JO5`8$6=Zt}H3VzoR* zJ-(k}rgY5puaGew-l{diCt;*el7)yLw?pP+ zQ=oOWfdW^7QuXEt)k!YbjZ;8(_jO$AuOAyDPHh8HdycH*CY&Z);CAgF#Z`mS1R^6u z`iQCpX$U5L6XF4gyBK=@tI0JFp3yXsF$(dJhG4J01c(quA3}{g>=B zzTI&)!aUx(1dpW?Ks|WQ8v0R zSZxQvWGpo;CVcTdmL>k`eCXkT==no+S5BtouRP(@gUsZKMxzv+r}GW&@KoraI7Eer zZd$SZKal7!vmsq-kF!WT z5f#SQBSNjJHu1u}oiy!+v@9fwK;A<*)19g^BQ<(Cw<><1f)YINc;d*#ayG`-FoViz z4AWsL&xoB$VAbYiFQcnm>pS=l{k(z8dq|ckrunlNSka zwDr`_Cj7Ce`p@gVk(}v&b4Py8?irc>1-|xFi3^yU&Xmuu18~*^Qx(>rgYWcOS{cn5?Nua#GpIC)1zjL6NgYH2w8J(EJ|432Z(XD`xgU(4} zO8g5@5g1TL0gR+(?BN1^s)M8syd|BJaj`mF1P$me&&y-M@4p*8VeMW#eG?WStYeVN zQZzb32hQ8*FiiT?aG1I7I^$_FbO)(qxe++n%QI}7dUfZCzKB* z$B)LRapFhfF;l?8Vg)gMI=yuWD7zt!C)1w3W@n#c$^uj_l$^%9;lsi7qw{=?1Q0vE zwj>KMEC6_4=KfPC$tsV2f~@mItUzg1q7akKMS;hmnyoUEqH9-`gI`%c{lM(G%G`98b9p75&#+jm=XXo;Rh%dK!XNVzZpOr5k(Uc z0EfZ=eF5MA`IFBmYFHIyZ>K5CdDGgDQE9lDbiZybu<{E1zoSzkQXS+<5GRW%5wrx zTRoX~E{*HF7{9XL4O9q-63ym2?d;N1K~hT8u*>%*YTy078nq$9RIgo*9odqdp@`+e z7ZQ11C}p5(3`Jl7)m$(*gz(e;Ha0VS`Z0VmG$3Z6J1~JuFXrRFVuguXR_BM(+XYZ5 zqT0I!m*guOT1uFArlS2!fSsow`aKkj^tPnm&&V0&zeec$mUcg>CnGK$37$t)Fk%pW zPh@UHndz3W7A$RHSDI9bdoMB|U#A@F0|;HFsb*Lr%;OxBO=_Qzr2l&BwPY|-3=%AV z_M{6-lpDOSZ!0nr60ZXUtyy~vWC-aNfaT$&YDJ?jr8PbQ9bYRs0sOK*WxRcX{g(CE zQ%j7AI=%J_Hg1==x+NyJEQ!2RQjQ&s$)CYVqAav-*!mnSSc6s{3>>u$vDN4$Go*Ya zopD?Efj_i=sN$n%wb+V2XclE?Mrx=8BHW2IUgy83RvGoX+v>;bpj%vcWx}b|ebF&EsZ?fyZymZ%;71sD<(%(o1ZL3SrNyrIC~`@N?`|{i{qU(7 z^?!W5QOm>sLVzPrF805);g9C;Etwl^jBd8;J@FiNwmG6_2yCXS`vNyBiFdCVHpcUe zm<`3dH`@pvPX5d?ACaLlnoO+2Cxj0ZP(g3fs0T2xhGq;Pzdt&o@$Y&U+;$+6L3Hz$ zrJ>bXN%N$I<03kC(i^Vhq^%sqqQ4hnAy@XeE0*a9A|ltp2$87t*f2X6*cm)YmgGdR z9EADpw!B(t{6nPJRC(nZ2l1_hO7QvV=DJjlcGII3IFd59_qF! zel~z0;{YZU5QMWc2m`Q2l1~?)gASojPP`_b&y<@mUgY*Sfx9D zz&4z4X2RDBA^9cy&WrA4a?NVMCUb3RO;?hp!KZ2#>Z?v)u8!TmJJ+GSze!x&M@jvg z{uw&C%wF>$Of+a@Jh!@L%WRRrZ{YOB(|eFxx4ANeSuAYLjsRl}s`GZuz9{42+;HGL zoEvP?bd5kvlA(Mg1k8(3`)t?%$33c=uGz;4AHaK;ESgV`qVz%bt80>uGo*7TRoEif zXg**pE5Z|G_eNn{Ut&tHwmfXOPNj|(%s~^P#_>~M$0Kmj^NJPt2r}9HTP5kg1H%X7 zRQV9+sA`0Bbqw1g@Jv`N)!kXEwc2TOF6Zsbu@ zt3dEB&0@251;)(mw4S-ZVvQ$CFaH=#ku8x^&@H7KM{m*?*52<+@*V) ziGR;IaUcwR&F$Ufx~GOiE_n2Zi$!IhRHFu$m&*|WHR6&qOs|;LqR&hNrqdnhxnrBz z*6mrx&g*k{w&)KpXZ`5S7BSy`6TsWofozI$pLZa&&YmmOI}+L-=H>!S-dDM8vWa1f z9Vt!ySnoH%&s(i(vT6Br*7}0D*W9f9f?Nq+llsEPE(|j3@AXQmA39_bK&=LR| z0jLrHGTj6)E~qSEbX_3}W=Up4487YY?*;s+lj4oGZrT&YluN!;V$`Y=y14imJT#vIS%g zvDnBN4AfwT=Iz>^EuT_kaiT(F+dXmPm}+#B+7z7=8^QzQQh~QAyLK)D7HMvPCElRm zcicMRd0jVGA#Z%G)|}oFcS$c*`Wd?QQ38GJp8WGAu-x+e8pIw9a~}-p;Bat_E40K5 zVdP&cHp;nM&vHmwC0Y~rLt2>0!$4CK%EpP~0XW`&lvB~A1mWnmvIQwUbqK~{>dh#G zKJ&dsv=sF$>ghaG&KgNYt$w(_=0lSD!R~2&qo9PN7kct(t@d?(`Eq&@obIC#Ps@;l z8Kl0tloXOqlY9`n2Q}*+lB0cn4Ip`a(P#98?e{^kFl)K+d+S6MU~8?hLuv2xysXpF zRpw*)z=S{{+L`y>Jy@RPVz8-2;jS$^_0xT*G_K=%+azb+j>ttAHkQuOKoJe_Stk(P*R&6rdoXoc z+%CQ3IRu4`L~f#ZCb=Vo`yjPaflJ@b+oK)WCk|ushodU*E5eC3=8TL=gf200J^8tMtA)yq(|Gg z#hHM}th1tcHSL%d3NChdQ^)R}K1-XsX!|v?1X{mKWC*lvFiVU0KFaf|Jccg;^;yIE z$xhe<%6xzGi)WvK=3{B`Tz)9PPleobyAaJ_-$t3><<8(ahGEtapN}=adUdO7AZ}n5 zj@2+?o52nD+>heCS&Y9u@gPHIF&2FHYfAW)ZH*7tlfqnI@i5}{dS`-pmr5nQ2fwHs z_Q{0Gwl|cOWrV(D#>RYkN+1sNYWF|gvWRn}e8Sa#pz(0tPlN?RjN_v~uQcChje9yG zQVP94VLhh8iVEQ++dTcO*&gdo$XfQ*tF~IfnCTfKDL6yyn+u zeu@`0mUdNhKCryJ-Xir{RM;}Den_p`bhvTZET$8HSiU928X9r&`oEu*Sy@Q~9EBV3 zZBDI5f+kZsY?MTS8`^4{_I(y(`snPH7`7DKnDQXSbIu_bTt!{wqXDeQwNXrp+AJ$i ze+HV4E1$hxeBr^P{RWwr-xK-bBw@&H`^e0a+yb<}|wJ~E&rAW#~&+R3*2me~Kbl5;zVKrZ& zL4Nn3Eib&5sQ2W92zj`yDDVJ#M!dr1<#2Q&lSL}39KQ4kf^Zi6#j3N6)(9|ffQokAcYMy zTX~TSsH@gWFhytw3z-t++ZB3M4v-N={3g9CF<-AE3Uv8@tRl-rJ^<`9E3RQ1L7kPo zamSnt9D&4FN*iBKvBzDY7eApgjRiREK0z1;oE!ppJ`(<&Yj8(*j^>Njmu z^5g%NyDD8K(y^K@Q5IK$LWKPea-kRZ6G9VmT-M=qb1Cwf;f1?hd^e-k9KAfN6=KSq z%_oyB{A90EUD4@Vp6-`KW1KCQz2xO?n`h4%ysJ)7kOgF_iVY)<6+C7j#Y@L;#D zJ}p4e`x%mpUVV;OlPYhFVYcd3R8LVI9X>A~F?zWhJw(0v8xt`Cv8~UuDY=tstJQf_ zHHGK&RP5@-^zTy`C+X1=ep1W4(ha08?GvK4A`fU14=6O7AjJ*L@h+JaSFd4g_68Ll z9w@j}+Mg=wsApj;xGQkTGz<01cq7_(3UPlIMIVNxO}rAeYky!rI~HhQz@J#Y zS(ZH(5nDi$*byZT?Qf0Ot`&qw{cKtCEdgXQWAN5Ahwj+NgDH&_F3(i?ankntY{PDV9YRZPS>O%9>Ilo3f)=T*Zw{(%#L133no!Jhms%%YBTn+?xtE;J_#C5-3e zFR8za6J5c?R18lE15g(2D^!aiMTjt+X536CW)OhU-bgJNbQx>q7(}bBlqy^j8wGbw zW?|A_RlY)Bm^(X{QDwN!bc!vd9}7Q%46zpeBNCnowDZ#3JUPgf&;q=s~YX-iFXDOUiI3 z?NP$!_^%U$ZlpWBv-jl6V$pn9-rvPy7^T3a(A~fDX<*)+|o`{Og>-w zsYOlg7nM>|gSt7QLO$BN_~Zb;(%$G~ZF@%-d=$1%Y4VEpQT~DPx%{fc?B9kN^0>oKuY%tLdAHV7I4)?5zTnL5 z{M+tpDz1iSld-MpYU2fvE`t$e-1*n;{WV2wZ?*oJP684wU;l8K97J2@DD8DES4Nnt z@!EA~U!9RYZPq&ZGb1uLe``l98;0!fIK{2A=yCTspi`#ZKDD>{~RxO?d>cG!y9)sAIDfK&vmJVc9i2bv@(inPbp$X*V zT1fkPMM8?I+)04(t_4e2exOR|lRIPpYhKEIlIDpA>hYsp`4&AMJ(3HxxKk2+w54w| zJ@3QPD<7ZY#td^fut5pMhmkgEn7YB3sh-T3E-XDBu#$D<6=GbDJR+Tg1NrzIz^QTK zFC#nK5@;GBFueN9Q{j_{IclhZXH^rcpFsmf0#$a?PTNf01>U_E{OmO_()%b24o8BU z+_eZDJv#ANp)(J{JsPHtRAMO=#L1_TtFx7Q2Nx*x%AZ9;op#rdWdEzv<&5d9|9oQ@ z(|7IzG}xpDmyh_lcyMESf=>cJ2!_u_DqV!=p-Q9F#n0k=7}R2?R@Urx z_-bBLALa^)A5D2I(*=_04NvJ5rcJuC?E1Qnl7ZACSZW~G1(~{J^4{cEketK$MyhCp z^%NrZKXBH4DOIs>PVlJx;)+g(e5j;xo>wcYXn-;;Ilcn zUfHlnSCyr3%;n(Qhz*?ZGa*v`TMWY>OxX3- z>pt>mM7`<$-$Z|QpyLtt3TH4;zE%R!M*>@sd`Q*E?ptWd-OSevf0ve8Z9>y9QXoB8 zjLyr^p(X2Jm5u4>N5yrmr8cw-%(sV$nJK9e-fc)q|4qGcckfH?-2&cfUvj{&YhneB z5XTh^hNaTOJIB$SrFUsG)aI`3|EhNLsjBwJ942 z=aU`L3v+-$X$o`8$7m@*yWj63ad7FtaVU(~YsqD?Yfc3^ng9kTF)c%9>082nkWgKp zGP=qT$*Z3se4*D5f(Mo^Zj-ZDSUlK>!uHVw_t5CZQq6D(4iF{NYYDj zlJRiVXn!wMiPtdwOBiv1a?2#}c@lHweC!W(+qJ+@Q#!Ih{uDEArZAK>F}i^cA`Kk1 zVdosc7It2){P?&6Xtk-6xE4q!EhBts6)<1BmK$0}2x3Wcg}uylfp6S6G#mU&YP$af zUV4bC&;qZIcKjD+92KQxOpvp9lS+ja=}iwS!Y$f`gIfO!y`KuhbV<@Deo)CA##pr^ zsAI-fgs``*F&1_aBGIja#nLck;V8h@TjidZ-T;%3_*^Vxos673f$=uTiaa>GK2={T+jGi}-djs6 zk%hkN>e3`gau4Z-*7~vy=MuSRezRIt_IXNR<9*FN|JCZjChB6o$Fxf_UslRR(12eaRa?0wjElMC-@xT80jy@VXF{ZgASW+6vKki~FsA9) zKcHy|!|+3)-KVH8MRz;zvwX!;MV5aAwTNCArnF9#jr9Is-{Uht&<@tlBgWph&8DnC zL(1NR_7J6^!hp?QRO8YTwvrn8ti4#aNLMYhZP+LUb+E@p@==(TS7k;ZQ7;^H9oz6E zkz=wpTNw1?+xLa61_p`swlvE_XDOYIb<0k0-f`jbFjlv#JTlM#U=gO@)T02Fcu z69_<}ga82npaA*#eP>#}J07k)<->e@XJcSL{2fEl0g|Q%RM5VS{lqxoT?wowJBwRD z{fu5ho6b~UuIF9LvNi@s#Zejs@Nph8lmFQ6UFI2tZco~Zs2~F?)Gm)j`$efmIYQ`#Vk#>W z)tC(Wad3VN&<6s5X=*9>z;f5NoROo8Z!qph86sR*DZ@qm^IMvH=2CVJxLLZ7sDYEhV+f!XPZm6 z9r*^SOVqsG!TFRFVCRuVZHSaV>t3?QRPjD~rTb_8(V$F2LRKujZV8J+qcf?tdR6A~ z1sbf*9H)}yzL5+U<$;8bi-hmj;pv59CMTeobIBfR3Vnf-00z&NRd%k(H7s>|moHFP7=FoBCljgsKLDV>zp|8a~$ z$_}SOu0+ZqGOM(mO3mPpkE;s~54()q1tAp>{JPf_b3DA8I>s~tbUP1WY+kCYpCstJ z`pCf9qmxmQTdhbQR}|C(5y%ggJPffO;ezxIE${<2`UOlt>Rof|RT%iO_E+F#fz3#^!5n9YShXFuZvFD|#Tud6ZG*gUj;!aQQ|6B3Mm zVbp-GQWj;-wx`Z}`$4|BCMoDp)NFN$?`nK`{8sB=zTWX&rqI9YiagZCp}ahhW1?K4 z{R4rdgHzB1^>SDIY}^lor7)uOTjPwR-1C~UJ>&9Iu7NDqUIIG?gKt?2I5Bt5R zwcxN`&+tkB(qu-vG(z*it~JvZSsL4Q>GzQd7LWFQjXTUTPbI1PGF#L&_ZZA7lqSz3 z@%lyL4ZT3%RVjQ%V3^wlGOZV2@}Qmg{9M*x8&g;Eaaw5Nd;W~qp&u)?I9Oo6?7L*= zu9ZbHWbrSVI^2=4v%L$;#<(%&d#_C+odq!}T5LNRX>;EJndYv`gU`2rz@YO#x(8Kk z%m1#X#{yzTUcj|yNU$N*wfVqZG8BJ-9AFdH9no1!OSBBw*OOpvtNh zh1wv9D1F~c4ybKS>7e-el6W!zaN0VMI(+sP$Wr;q^V0Jn8Qd>U!082Go1B* zAmgbYe|&4r6^3+Iphxgb%U@+u`gA$N;F$8rK;ZYyJ1X2?ypJrZDkuCk|6CnKq41=E z_JRY1QWBAQK;$bx;2t~VwD6I(ZBCpmY$9WFV_4yKYH;3DjaF7^D!kIlDDi2qGSCMo z8X^LNmp>Um7=gW?P{B*!C*+6ohsfWp`veu^S2)95i?NFVs=zNs z^GG2(!8RegHIpyRIml&c&z67-5DM*b!4`1;Uo|e0s$vJqcpTL=#Bxb}(=Vvmwqm3g z7+`s)DF~@vMmdVn4*kmb#c?SuC`o;4=9x8+@aI2FMQNzIr~=Qzwesg}pgci*?QI?z zJ}gRyyQIDHOw;k1$84oLc)H8TXF&XnwrrhDxOO_H!l6n_cEu!5W9<~6Q^ohTBeB8K zJO0A=j0SHgS4EEn@l$%Y%H{7CMgL34lmG?rILn+D&^Y0xNNL0ZF7%g^4155;kS7Li z3b5P)Ii=LYvfz2%HqkuZ=kt6hjV`>nei6^b)d3Fm7jU-tm&}^TA|W%tRx*s&_CpN_ zgJy}p(qsYC{7Z|G>fV-{XX!8lPB>8gGx3)R+^VUdPR(38nu8}9LyM0f9rFbxxL25# z<74O~;3w~E%8nfelj3jiqyKU+dez%hi|brIx$)!rN593*vjEs^)r*6iAdXFca4$qA zvXjca1z*Hhr&ZigB@7kbqjeVqUI(sVXgY6{Pc~Co8BC61U>lHD^R($&q**>-NwhNH zq9%J-OUBgK;rMVYnI3QZN@aml`zdjjSCR1fF4pb|W)$A=s5r_u^ndC%^RSG-QA4aI zL=H6KE_u~HbH`ckyFG~rk-f29B<@%`?X%o;byWUQsI})MZ|fq(m0Lc*WIiW=mRg zuD^T|Aj#ymX}-NOSc07hBoxRybS%(BSpJWS=T=9adiQpYX{Q{-BrTaJd<$I z!!#9@8yquqW)i=7`+5jW?|07VBbel~zG zK!zm<5m*Dj0Qn12ZNuV8YL^QRLbK8IVWbPe^3Y{#;%#P3*ckUP&x&|}JNYdwU;(sa z)f0Et(axFq>pVC~L3UhzItOPv?CkyA{$+wShakiR(3)p2LFq%U%V2sNk~Tt)Pk~AA zoj(wt{H;>!Astlb!#b9qi7Ycf!2LtGwq`fBJT|x9-zRfO*bY=A;4I)4f^RjXU|9-JPXoL z2|3ihLcbY`&rtFk^48?Ao2NeLux9vpw7C(s?k)r^j36ajJFqbSq z?u}0ubo=Ea!Ipph=y9dB-CDX#=w9uQJC73j0SFfSdIh8Gwttx+c_Z{h3H#8skjv4`OvhW$Pxe=0mu>nGWP{AHb9UT zu6{CrFv12V2p9qlkV6B&0Qvt|;j5#dq0qUrtg>J9c{hp93tN!grfm(H%EO}hmgUx- zp&6Ba9-ewMFXPWp%cJv-Is>ony94KQJ__QfH8?SdZfc9ms@PGy!wUb2@6~l+(ymcq z2y{ihPu56;O>~Jf>tiW-f!rZf1$(X)KY0)Wd!kco2l7*coOmGR_ju~z)L<|xvu+Y5 z4C>{oEt^`iMP2g2g?IFlzi8LI(DrGvQZy1|htjz2`%R03kc)W$*VQ9+Wah{`1w*e) za!*hKD(*2CmhUs4OFb^$P#eCPBhwHe5P^;t$LKM3^j;$hyM&zG(yP#2zsk+3-({$p#)4r}4x}6^XYp;7HQ4mO-qt-ye|A zklXz%o7drAQoRqQy4SGLh6k$a2E8y+EIv8m4=@}(#uG2Bxpc!-J0wHzDR~Pec=oEA zd~3@M=m0-cJGKCr(+I@GL{D8A8aTJI>;F2Tw&jK!hj>kApHUYI>@SHs><>N5FD8L` z1jwjT#bNZ9x;8CQ&R#1f?TV&<)&BlHvkG8x&P&Lv+T@p zW{X$tE@75n%xwz0FmBUp%%N>vEvnC4Nll(7flvlK53SFGqK!MzCkrQV3&e}tFuK>6 zKZvB4KlfNuR8Sb^ZizaT_)V%Qa8ban;gRPi6cFB!;V~LRyK(yQhHRXRSj}HDuj&mB z2TLf@jz_}bK5E{%Y8?y^0&w%vz()$N>ZDi7v#z}GydUyLO|MDOvu`>Cphez;Z<)A~ znXy9Ke4!(=bPg8%s7?f?wu=Go9)fN7W(E+|uMF11D<$+55JvYI(;*klfC`Fs_rhpt zJjRPamaT&Zio+Ybug&69co}%9>*IFR5Ko^V{15H{T&YQfrXkss2hq@AcCY zwXQ~H!=(maloCoDy~I>K^h1YYqPG)Ea70rm>%L-Qny9@$Mz1tlZEjkTYvSG;l09Q2 z7}+6tMd$7Cgc}d>J^(%A3%TM_xS7lw$uzG8Q3`S$Mn5DXzjNQV6~s6Qtt5x;y%@() z)?uM51=~W&^~lux=VLa0zYW>rXdCQe1yb~=b&@HX*JlcTPoaN{B zqF#qgkiJ#^LjWV^;n8VvA%HBxsyS8Ecb}R0qF4W)k`;ke6ajau-s1VDc1`2*0uBpK zi!SU>ucactvPF#<44HP{VY_|r_KQq-*G{}X=vqSO?#ql{7+ll0A-VOpSTF+R?gCxO zw3e(JE*EzVmH;YX2PeT0M@s@qW?ttGfS*M>f@X43@jbm|O~*s;{j zcy-;|n(6#PLo=MCO`1Y=tQ~u+=L?pv3iU!vv+YFUL7MZL=R|AlA0nnL(}p6Yn+*wtmIYu>8GXPLfFHIPPrd zWkF#6&vEc3f-?TjD{4AN`6Y4TR-ukvI=J!AXv8j=Z4EQC?H)1`Hqst|x?2cG!u*y+ z*x@6rRBBe;=g8q=Bg1cHPHrn-rL1@5MBV^0!=xa3KALS&yJl=fLzXAduCVtM2ouw( znfB2*k80{OWY3ke+OPFDEn9k1x8T12B@<9rEFh zjK|9AHXC@$=%3$Y%=Dth!NP46%Ym5#V2-GFTXgA0q7F3is5!XJcY7Zfyutn6d9Z@X zyW@_yxyV(1vsqO zNp`djRovzc<%ocb+=3ens(elUt0r-)fTAlA)<+n6wXu_bR4Gy|r~)a3WBEOn!=aEL z>XP1oH0#|C?5Ufk4AT_iy*F}zNJJtXbVJRp(f*kPj^U-~IO|qNw@IW&y~E`yL7Yrq z?ty5&N2d^G&c?LJBYnl(2~~I1yzPSoA!pO=j?fMW38jZhzoVj*_{TW9(;uLTbh3vW z-r*T)xF9OW7p`8Tup?XU3pxgLFPLO@-8}n0NfSwN@o&$}h^OZ#+G2r>c(fp93!^U& z`rut0J%!n2(aBLiWzCM!G6aMLAzP4AzR}p(0el&DlpmjA!yrQ+PW=qC`@^U@5{@zq zc9MN@Zd6Ef#d@z_UbVb!AyKKZ3e#JjRl_jv(%+j%U$~@`R~n9`2pgC~H=#Z}(wIy& zOEp8Q{o*5#;GcIw@`V)BfxZoxHuG)c2S|RyZ|*%@E5+K-N6k!-9jj{`Q4Byylx>=b zwhvFaHRI$!-fE?2mnBka?cq--MmbBfO^ltnHIB1TYV$J{T(1P4j>CfNAqPV`#pnuJ z>jp!ZX>Ze(ZXQvQTW0tRaKd>9afgRo?`)!Z?uaGrUeGri>R6q^30_-IP!FDe$cuR5 zzt^y0_$9AO+?Mp>t#qI{dAzyc(eFy>glc5l9hV#LQiMzBS8aHwJ{+rRagI#D-twf1 zV1yyvU*};4F#mkjWF1je!o1U~r2lKMCOuR#pav)&K#F5SKN&y}z{e8=0Pg?*!-3!c z`EZ1UNuBjUfE#)^mM!GfT|MQ-Q?~0TUEg8)bidKkI*jiHC!f&mj zB|MpSs~vbdZ)3pWb@N0j-K8s$qO21zJEhp4gDl0S;!hp$n#aRtYSv7<6WpptXp7TV zJ$yP}a%jC&QcXQa<1WdYQ5g%U7!h!ocALwq+e=MKhzFqNWiZj{wUEJ&Rl!Lu9ZLn? zk|b;j1rQ$239_C?lQ6R0_>D^{VbOhr>C=sJkru&B}Hj+Wpg#n@li;* zLH-LCkLdQ(oGVy(T0V|%RXf6vuN1<=b|Wt{`1t5z$@y2p84)J{FSA+fX=ORveMx>j z!^IkCCN0x^2 zqG5i8lg@34TIf{70uEGg)$>pzOGEV*HgEB6eaLnvQz~f*leDB>Y5KUh=_N4wcL1B*St3*JqKxtU1ejSBuk!9gJ-I za3m=HCZZ*mXMeV_>%v;glX3P84M<|7HsHF;A3+LFQYI%pyfztAiU3SkOW?h1jjX-P z1N<{ePKg~dmvx)c)eJvU(ap#5CX{py7?*U25tVw5o87yMGHe*8oWL9XF#s(v(zj?KiuYdROF*s)M;iu^@zDGsZ#+@CM}3|#d{?6pqlD*FVca)Wfn;+&9l z`+JWb+=IUCq8M>zg%gP3f%6&vlL6b=l-z(VkeU`2xhP+76FkecF0U$k?eFd6L;frHh?)m0wxp?074*u1Hb_J|Ao!!@dNXUFw#~*ss#8~POu9(-?bc@qHIIAVsKgfwHcZ`d2Z13A~mMEWGC&n-x{hg#i&<1)s&jb{8DDq{_FPXjwy)xu-0O5R$>>ER z=5$)lK2zeMUGB}^sf%~`wA8ZV`NG=8b4OCIeG$>316rb6)K}%6!t;N>-zqpJ z37UpbQN#kTZBUI`Gg9YCwOdB@k_B)tzV+uxxUTfI5D2#t02%?P5&$yE128^F3RXsb zGypLofF>j)Bme|K1Hb_J3wU&}0cL~w`U>35yu)uR?!;zFX8=^#x<7%-op-uvyj6WBZo+LsI{xB9rtF$0T^rL* z*div+8F7es`w$98a$E~(%r~hUyFUQdMcy0&SAp8RmV{(mbE$p-rAZj`b{4l8whK#8G?S2qgP2N2WQN~2Hg+=l0^Xo zQbMjunD4QSoU{HQJoKzFivY}nj!@KK7f*?jN{bFs7eAOTpT5LE-2DXTsupO+g@KtU zs#BumkJ+lp>Kz|Nr#{K03TJM0YQsGYuRoIzf6&qP1xI*ozaU_?cu@=k-EUNd>hoqu zbSgr{`Ed1C$z55WJpvH@4lfDT!E_CJtiA+^`)8lArIw^j=V{j}zOsG2uj|aENKJ&_ zh|2q?)p3Vs15WGc0~Pf)rZu?AdPyycv|5%-R`qIqnBEcq8UctB05ZM@C?Y^XQ!_sq zKoo$669O2900Dx5-~jfP9L$71M@Go(f{C%bx z`tPV)zZBcXw5}jD=C=BmXtCMgiOUm2sPexsG8kifXEi+;fm}f_16ac^AqsoB`!1Sz z4sIvDvzl?x43mT%_u32f|0Y%VADYC`;MNR$X>@{Mq=sAVX?QqJK;GI2GZ2;8oH9~%#3goRA8J{TQu`wCpp{CUaKt9$J4X=E;B`kx~el1j8f zI6C>foL(}wQ^#w>gc}s|Mp?w)<^wr9A92GR{Xww@@V2^FIV_6@?Lzb7?gu>^H*Alr zkbpZMf{l+~Om>4_IB;1q9r58BkwbdO`0udSBvJ3iStFsP;khFBc&mg|J9Pnx8(GED zNe(;QZB)YmlAm&Y5mn3ph!4V@#@3_$!oKI2N3~LshRtc0mD5k6H%pcJlBjpJ zb)E{<(*R;`1^Bu1f86JtkO_a8E#Z`$kQ0lFjv!@s8Dyv?f9Tv}5`5_tYJ@b;X81JW zicauM$C5G%OhH9z#Qequmc6R5kKUpL<4)CY(4O(AHVF**2=h6?aNyfp5b7A0%zM5^ z-m{|nHyO=`eVpmZu0T}*i~iHKxY6Wc@cO#nU0zbMaqFECEZPcGl$Kgq3i>1C-dosr zw*7zw&;Z_q`%#7zJ=UXhv;5yT>bAXy^4=zJ#*Z(uvs0U-U)-2-9cVhT^beYGs0dgdRSjOie%g#8QKv(Cj<;E^1)YuH%NDX^*WZnls z^+A3~-Ezj}Npxg732H_56An5?%(Bn7d6zyCcWJv0r?tc>5hfXNM+YM@G?T}u2o_p} z!Ho0SRtoloJ=HI*Y40-a1295C0;XnuHh>`j0woASZv(&p`3kJDD^$c)!fV<;*}r4R z|4#j|IG#(&;1KcgR!zdp5WzsSxnZgkK-L99u^>?*wuffi4&y)|+ofSsoE0hTaQ-UG z`@c=t-(NG{GK8<0XZV~fgNYs&lE+D5xFeyjI!VbEF7Kuxl94QM6&(8x zQZUlT2-48M4<&-=S%q$b2|!HadFDC4%y!E9Av3M7L^as!-+aiWdy%^;oB!;Tz0RBy z#gvzJ-03!>ahApoApfqN8f@taRp;8m_RR_tr1~=-SIn74)r`eEt{sUfV`)mAq9d$n zmFlox;ZSH}WKJW`OD$2mo$;iAl(DLO_iWa1&e|dCAVY8=1(CMNC#r(9{pD8oA7r%} zj}*i7o;Z%$)3U|!e=ShOAIYgxRjt6PNKFH0EE zP~sA7FK9=eSUGQbZ*CuzyVUr9oL5O3mr2LAwJjr@2x#+Z;SBiYzepw8iRB`Vn5&>O z3a(hQv|>u|P8WcgEuRN?dS$2X9nID)NN-uk-`~=#RDTuCkbEyt5dw*`cE=fY9aFN0 zrzqVvCmB28_Z8T9c2x4k_A*p3*+M=S`8EMh6r142!xOY`!(f|P0ZNR(7QcI#G5`` zuz{q!=e`>}qJ!~VaS`>Cn?k}`=R6qU-Y}+TvlsgJIt#w!#S3$|RXzVly|zNCB4uUy zSf1xr6{u@49;IznL5sqvXYKh1qy*6r6F!fhX;KUzAoT=$lSWH-weafJ|Ln`0&c$9+ zVND1H;w}~9vF*`o#AiNGGM>rAfzFM}?tG;(d|*>Pr#-btL$0OUa$)-2STkIL2Ug zw5>Gi>q%Sl9{;yW%?8fJrRxO>I&3Uw5td#|>0-?i05TLXNYDVMW=D?T07)VV00062 z*_q%H05Yc?ATVJ7RsyC!#%Q+_mjT&>CS zXBJRzukmgs+F#`{;NH=0)#?#-KssEjN&Cb4B3Ey?O?X(l#Slf|@D*l1n>~+Wm=J=r zpqCAYrv=0fTv9D{%yO~0+oXJc|IfV*aMUj@a8q+uLdmsd$W$R za%C0-V{srx0_sZR&A3xuYJ$&?*H#7^Aa|CTY&$VH zDL#Ip4(kaKzH}KE7$W%nQ4@LT@ofMMOZc3O??;Bok}RRytz_6Htnt&tHu`edG#9+V zt`AHKQ06JM<;BHId1;bT$7p9aSs(7X){u4K34P@XJb0%je`Wy$LBz zK+PWq?I7$7CYjlT?OB5Lq0a7sq+8tvGsOwk@Xqq?kpfmkJ=&3o9TL4CBbVsouaS6w z_8Us&ZF#0Poj#C^0)lChz)W&ex7jvY$sEdopT*jqM&|NWC9P0o%*SmgwYc?`UX*tJ z91WV`M%AMny?gU947~Axssf8TQ5WKH3=Z42Kj&4;kWs1Z35=Lo0Mt&uR!1kEl$<(B(4nIJTT8a*$_y@h4d`+#k+=rRD!)X zFM|?GBnVS;DL;}WFO(m#+INCoHqv2}7PErxHiMJIE@+n2#U{7O*<4^IRT{L6YS-1w zR=RgGJ-RVYu325rWo#gM8S$Yl7B@!kjhqD?cc!*5z+jFE>LRhJ>b(hHEDjm@XRB}?8%oV%INhHa3; zZq1e91t9+j%%NDzXn^ukyPHUd?iAAJP>iTytcJnNATJ>c?3Jq0v*F57RwH36^pZA5 z?jo5B(g@oee=V#b9vFZ`3^lt_I=xba|6B`)YqlI?E;HCD@OX+%VR8Gg(#RvXom_XK zsAT-)Yy9DDh2qaKL8BnL!to*+gkX&3M1vV5IrlI}9I0r`p_2<**{)V7(v06cwa7ld z##FBpiu{diN{Y`2OrDX$moD#AyQuIgHBm|{K0@0%N?2jZ#lT*U+{<$?=F-Q+6Ftq{ z=kx*b{tX$0WVP?W)-b4;rj>*q$;6Bq3nQpvXLQIC<^+U`B} zeglW}iWNd&;@KsMwx@?`-az|S&flu&#?@%Fejbbqk~*#LAW2?U7_s?Si^c}Ff;|Hm z9I~Q79m+4NNlH%TPWTE;SI?9J)UO4o9);yF<-i@RBH|QB#34*xrPv6-^_1BB>mJy9 z8&kp3d7!djS}B;CIO0n;e^fp_%ENeYXwtKk%|l^$(pGCSOrORG*k2xO-T_^& z0gN?@7>FQWwmmq-T45?xpll@?V0sDs!!@b+KvXkC8 z`ok@Y{rfcluPrZ)CMk$GiSIev+;Q_>o!#8N^Jw}F5!Q|0Fj?uIy;RT6+K;QU?iaWU zp*$LHmcU+T{@S5K5jtRl?|k5g%@5{C8{)wSfQZ!+N{H&$nR&=2!nW&tL(9m5OD2Ez zn;mq&!fOF?je=7=ApHYps09q&lI)gHhRd^%nDzc*$nIUrU0BdT{b!L1 zF&uA|6RG+2tCMf8VDe(?Hm)t;k&R5^kf@+ntV25ca&{Ml2chOxrEbLqL;=8iP{INB zj8ph1w*r@%Hu^U02tI_2c9_KW>oH$|evV+wTSjNr_zT_gw>qp$jGb#o`F5J>!2hz` zb)}7iyyxdV?5&I@MhpISpr=(kv8qdgx|{uRCtUS8EJ%tongXJf0o;d0QCLa0v5qwM z8%@)}lh;kSVvVJK2kf1GBR;Wp_O@>o7zHWQtc3bZ7BR6qO-LZR^-T0Twa@JWE){ep z%}M6TD|$leo+zY>>+b*7fmxPG4l4zMGk&f5#hd?m`aA4#-EH znWP~F)0f2iGLO2O6%#ev3O!%+l}Q!Q%Z+dhsru*rT^!I&OG*%je0VXzcpqv1W^=Q; zkng-wzcRoCU2(olW5U(QG7JRz5`)Nw413N{e{`q@Lo;Tbun-$Oi+gSa&4jKElojf+IIySNx6Mx%-9{sIN|5Wm6~PNZZv z+AHqwrPBGy9m|KgmN`$(_a@puaG>Umi_tg;Tt+Gy5QFJahtOw>7jplCaYtmS@An%9 zJRac^{6s~}9cKoNh=_J=p)#qGDT6o@Lb_ZN2)I^O;Y9hVSM`3b`%E`bKgsPNMjj&_4)RCE-;MN*@eS39K(buDigFCxOGyrOa z_;>_|q5@!<8y%K~d8KGfwb6v8<@v01bwF@8`09O#^Wlq^7P(qZsQV#_SaUcotP+#9 zLiZ_gnvw;1l=)!1MzSwV`#UnE=2bWZaAQ}c?_f9)n+AhZ>d{$ovgstlF<2a{_2N^E zJ$6?Mhg&`~H5N@qJb`9>U)F;e6^p7xXGAnqm?j@(MVY+PF~pj}Q0=&$YFyi&(Rv~z zaC9~C4)Fg9o!>x3Tuu(uH(12yVXZUB8t~ogd6WWWgr=}VYtUM?{!9-8#GT!VL+KVf zG54_eH%e)qsF@B-i^t}=W6M!pD!-`2 zDX@*UcaMo1hC(UOI>pb;V8z86&wpQX6Lojq!Y$|=gXV$&4xjb#<7lW`UKR%n4`dPQ zQjSMT`&Tknj}cERu(x1Z$5J9YA~sZB6Gq#2ch$f~4Q z9{fbg(Z|CKoL&znr!K5)?2oIlc7MbE1r&~W-)s#AIC!76M=#NwQ*FW9)q87J&Kavd zNeO%_)UHZd>jVw^0y&CFv^{mR6H?#a@IZKx!hAh#)+nSP|WT@+`ZNPUc==TzR(WGAXRZ;6oC;r#)+a8K=W zeOG(J*mLva!JSMd_S=a12A`sZ7XE;Ao!>W7sW7kVBaYE))<&>zBPRwhuMj%VB+HQz z!!bdsa{YV+&s_aJ(T+yTPXGVI<86K%Wqf<+>30QBWyUB(|1_k6(&R4hFkEhN9E$>C zS23EfpAKV3Bq zu6s`Mp5FMRAYc*k6nC9MvD1r%F!3m${Jo9Fn}%u=P9pmo0(tVRLVUuI;@*Sk`{u|T zzB8ebqcX29HZ}#%tw|(ad+>`L6@N0)p=-pA5-vFJXV|-<>?9_~!|l$g^6-GUYrk;v z#HzXeXO?M7@#X8OwT?vth;=Fn=|z@h{z#ZMU5ToJF z);A_`7B|C4yw8ur;-R0JXU)6VGU^+e$0!N|x``zm9Rqe7aiUv0EF`-OggjW(8e`za zkQgbI5OUc;?6L3a%bSJWp@FrFHj3M{Lhh64H4@_;=~IysyY=>pH;ffx{!ju1<98DJ z(~6=dhzxq#2K0Pk3TE|u2LDw*e#~8Uz>yh&ru`M081k&28N+IVGPmQ$>!y9EK_WXb>%&|cZQK(Jtr z4Il~ei$~esC7xMj7JG(ZGJgJ?@nkkkPIj&0svOAzZw7((uEU&fS>>XMg}8{06-xDpaA)B6E(z9 z>s4!o&1ETvLCs-Q+ehkm$fbAq^Z=!NNZs`D`R!%*8*@b9SD z?Z7xFsUVJL2JT+o9Y42l@22S&F!%xLx89LP)-%XERJZk>b)6EHgl`LIXZP)G2uoD)*(B`L)l&aj0xStpP$o z+;^fWmP?qLRS7+VD^pj~84Qr2KwfHh;?mx|NU>SW+MIwHGn2y|t#E#Xf|LkcQqjC| zRFUt0uK{o>Elx16>mBaKs041_il=ITJ(mnl%_12fjRxtyTv6xSG!%HoO%UP}A6+Z# zo5`&PCH1Cb>sbszrf~lRGETr4bK#J{Q=1kdbM#Np6PGCZ;(pxRK_gb8 zs!*a}F-U|?Aqln(ppl;;O7Wo}P>o!RAj!2SecYOC;@(2H2_;3LV6UyY2!lR{42k7S zt7jx8-L%TsBl3LMF$2Zdei{7ufvcj>y`c1fl5llWRpnR9M%}A7RU34L>4Z80!IqWA z?@3bmJD=lDHdUnkHAdpGcXA>WMf&HKqfVKXiPQB&Cfi@BFwsOl#4T|#8pPRd@i6pg zf}8X4y#22#ophb4erlYU(N^87kw%w0J-jbpVeFBQY)>5ChBKAdh2H0ptG6C{)-tt; z?WHZLd~t2c90`?bLTN?m4s1MHo1cy%rsZGYBpxpPnWd)i5ckyGXkGt0BSj*+$UNu^ z)j3Y3xfB6GrE=yd6gC9WDNTc7+fL5(9J;VK8XZJGZQ;p3MfT}dfvFb|$VQsUJj#Bt zj?Cq^qFD7~i0+JpY^Hk>rE)!;>Xa5JUo8foTq6CuhxD^AbX2_r0Qk7k0a8eb$3I|} zpt^=hiiZ>XM7Q8&ar_Cca)1(3?I|SiLBRCTTCx5Vmo>woH+ahVj@3%OtmCD;pbhF? zr|lW-NbRv<2p^vl^KwH2BX8A$PK1B!t>*qOu5THNY7DmV$sEWA<@9wRJMr#ORHWV68VpsOu zOb3imF_us1gdRcDBOg?*rR}ks35_N=Nlo;HW^ z*r)>LDJ%4`^~)i$$>-CEeSY}gJR*FvHmsEY(eH2TDR@>bQCz0D><-d!dOj%6rT6*s zV_tvIoUB23Vxa$604Z`M)IfhvC2>O*Ta}m1M)a~bk6E>H=OAcTK8X9VUrbvh z|I~UmC>M8JI#n5MV(7(d!0;@9s9j?=BApq#te~Y=3JmQ|1BZ{Y z)@VFVHKE?5+|FTb>?ZGEt)Ly|p}EMzMtqj-IAfp>#3J7HU>{3zW}x=xX>>&{v$Saf2zG$O ziND=RpQNJ|!WGT5(ED&!V0JWf(#)EEM6btre|yg<`hYq7dCX2jX?cG*b|US2O4?{sEk_>LwLX>S((jei5izZ*aV;YSky0xpUG9D(2f`da5Myvjc~iY2+A z)91nt)UEoF<$418*B_q(ZxaSn+C^r@!W>2 ztf%eka>q}cmYG^XiCS1X#R$cMH+qQibgI3f8yI@F!lQd`Y>u3Q)!+^g#i+-%(bwPK z8@i&pJLFy2vlj9;*v~ry7^YURzxjOaz=vYw7OOa9Sj4{*f>EvaufwkxKG7 zTv+iJnq$iUc+HV_yH2y76*a|NEE($j-wbjb15n+`4-8iwxLJqQ?4vo-*qMo73%;C5 zW9u-^#4R9};mg8*?k*b>)1w`eH0)YoVd1AYt%*SCD!;5-|Is;wOIS3R!$e>#%9{== zNNu~7q*m_-lnmj>N_LJ~q%)J0t?pfF+9dkMDh`A{{JfOaU~3$!Ym^z>afWK;QO&Bv z=JB`VD9Wf`JBC)+28RMS7O(Q_!KKZ=Yv_3xpB7mIOSvFYp>T4W3Q&1ABF?N_9G5r7 z1hqtMu>Wi*k|1sy!(n;>)!*G!g_k2&3>+Fru+YN1l903YFLf$rax};L{^bUQB5U2t zHNqo+`8rJ0hu1VpfiZA3pEs_dX6ruZbw%YQ$%mQqo#wY0!3<_)u_2A~cwae(7D_4N zw_4=cC*Fm0q{7RJ-ziEZ6a;Bj-hdx$;AOj8hi+k-95*eRg8AGV0vemsoy!;k2 z;YlJw4b_OrjB;1APw(ugAq5lKpd~yOIs@a; zH!-=OewUy?2|#z3Tm+W0C_67E{xBY!rTVObL;;VH=&l8`$YZ{!_y+uCnkq-gBlEK zwuyiQ-ym;;M`qXl*(gn|88&UbV|`^Y$^k!8hBhaS6O7W~oP0T-nA(PoQ9B)>Zg(O^&N7Ay{HD~JXzEb^lGy=bO+P~ul#+|)qg{wtl&iPDuGpQ{{hue!KeWiHH+oA!z>HnpM`q}ppv`I4q?P$NMab-1I7{8(Vb*g zwhwRLt_ln0=X$T8dob*iB-}w~g=0dskjP#k+wTTisQN25)4K$g-e>5TpgEyCCircL zM4*A-V+AEAZUtAYZH>O;<`TI_-COOjBgMsxaZnxY%AyvR>>(bhHHFGf3qYXQ5NQ<1 zJC}d^;y4{9YLdyZV{*p@(iKudQlJgiK8Igned9tMX*Hn{%V20EU#>-vC4|dct&1*2 zw8zMDj#-uvTMxV`2t7gMCYaH9XlKgv-z$n}8Ak8Uff605Z%4Fit>K3}b#afEi&7CKQ0U z7;wM?zySFPfYp1RL&yt@7;3H3AX!r2cPbLv|CC-$bGV55sLoSy1=jB_#Q6jiSnxdF{?*GQNwkp4mpn3sIA;H#?KFXe|7&F^~%3KcB)u4Q-9W zKF^KiP#DA0i_>H6Z>{=Z^ zu`uhCm4LdjMY(cZXKd-EltY6s+@E@Ux?Dwg*=;WA$4w4KnI*U^qK`b1*=8z!0glq8 z6ds(6tEcZ!We%out7Beo6nmAVafOn)l;|sL^yf5lny_CR!+g1sQ#=BQ5f1nyk`LaV zjGo@2qP;ffKc?(yc@r?uY;&2(+WEz#=A`DKMn2m2&L2b542s=_8iv1GOdff@g*|1` zlOzXjHUS3ef;XFfPRt`lCu&fcO>zz=>edzl$K#Kkoiok2)H4AT%HH`bxUDa~Ca?VG z5=9c*E144|Cp^a1j5O$jjDdl;z^L~s(HoDNLv^s7kF>s6ihvCz9aygHeR)wB^hu%X zDNnW}2V_h(lhbgOI=tsdZ2VI<^xfFN7r~`vMiWB?>M8*I;mpM<+mpC^aUS`EW=OF7 zm)n12*g+wvA?5T)$LrkFK{k!r*a~Z#w;;&y;~ihAfieqP0e6)}A_mRCEEBuh+#s-% zKDCO!bn9T%E&hdDi; z=fGINxeR<**{RuBw&WRmiBH!iU&YjV>G4F~^ShT0wAf}lR#<|-SsSLoC0eDhiQ%qW zXN#%(Zco0Zz4#ZBeCX#`2-?IpT(V1`5&#+j$PxfD*9s^s0EjLrzZw7(azzscIN|~T zCjsC9_-%TCtdS4`W1;!;?i!1VNU)*jW(@UI!FmjgXi9j2I#yF|6k#;9&*jw8>x0aCYC&) zq5a8{h+^ql0B_C;3+qi!12q_{s_bkJntsYYl5n!Z@p@qN zvUuO5e)IF1bZo#S4DveIt|8cBVQIGKyo6366rlr24#&ct>}|A#lUn7?a4{%PHy<>L z0}w%F_>uAsHPJfaD@XUj=adIC0*kD~9mCk4<*XRQsDW#8nKjk6posUs0dA&0*p;}3vyPI*gbczqTV z=Tv*dDc+jrgZltch-T5aLEcQ>@q;PXo3ojVr-#GNul+jSA>A*;4Z$w%3;#$rb*2m? zEE}%g$bF8Mn-Q)cAOl>0vXvjuDGU@5-5I|e00J-sr>Woz2P(zqfsO1+2>bvo)eMtMajL9Zg!3MG|bjLR@@uJ=S(j@F6n-(TcYgG zW_o#ELc#N_YZQ3sT&M!p!&2sY^j?SP3+1tr~%6L35191|0(!Omc5 zumw*QV%X@5UuEw+acjBb(NHCkq8%827i1!kyJYMxYz#)t0?GK@m2~&KH9G4Ne_9_s zJj`q^s>fKE1AO_&J6%(W{cKqgUK#I5yufFoAL1+=5Rj{8aCFL@g=qN$on5P%7ivHm zfFEKI5^P#@q?$A2zV1x_?vBPn1d>vpRr~hbBT1h^=>xC%#ERe~8PSsP%!LKeP))N* zPU9Os&dR$A!*RCDc_5*h=rPqP>vHDmhpU`uO3~AAP^@o9O^I_-H0+*j=Dv zt5c!>@Q{kodI)@<8_Dgjn#!9qKoj9FT z`GGYo$I0_fcxM~6Pg}e4OW7>ZsZ#i`&A6X@$@m4($_HPJe+VX|yN+>3awg$o)SqvE zPKh6fIl(SCzD{utjarjeFesBP#-i%bjbt1zNNNhw_3gNS7KT`Y)2cIY6NK`T+X;ry z9s%hFaiEh;dn@$Zfv(EFDkAxkXi9|$`#g(tb&dg#;^nTMzKxI$imG^*z3Nkj-&W^u zJhdXf{)^`=mduvx6YoE(rq2S&M`b`t124$m99CR&3F3RD8q0Mt;(UQCO>A)g#&iP` zeI+pGUH%f|X_TwWY4f|PV1j2Xpp{oa{`UrH1*tN#ii*8zuLVCD;>SvRZZhg)~?gKfSl`v~-?Wty44ZhI?7q9?sg@TMs~iiUF4)kJK(4@JgkE8n zW^@H=59#xm;$?3)0PgA3o4DI_*`;6{MIamSth@j+`~pRKSEE>j@tAruRJ9Q-wavcn z-vH96tJlL$VZ2PLne5rFdj+5I<7&*ji8isv>9twtXl{nUY6gd zZ~_uLU&%I4Ga5s|i!fEHAX_!k$9@)6@B>Nh1oWlT?lC3o+8~HWIx6bn%}6m#_8w> z@wv`NdoAG%FynHFsc@x5>0^peONQ^T3xR3oyk9rT z!w`ShJ#jAMT%s=7YOXB~L7pBUA;KA*0fR6MyiUldCAU#wmy7<|Im?PPMpp-dqb^(^ zA|i@O`>Hr;R}5W6&|m+AdOLpzy`%Ejyf!9h1e`!-HTp35EV~PM_Ygwv{~CNY)}}n) z2rf;{i<>o3QQre5*$P^VzzM16MBz$1A$GHK%Ah4@WPMYdXjO2$&546V61$Z_^7zHJv{sc47u$++5*j>!I1L7xTlmx(z?gpwu~bN$s6o05GN&K?f8- zJMh>~y=dCz&*~#o6SPZ6EwwEG40#dlV_Q?klezza%9I`Z{p)-uZtawdifbEm0>gx3hlZdNa ze{nkgVZN1(+79?o@vk`YIgjGQ-)mN1d>4BR{=X}86JPxV!#pbwr!cSB6BMdp(vYIA zmL09lvOU^s^%B>%WtekXb285bFj7bmTvL8KfFa=yCNKaDECGcBzyS6I9YISnk~{>i z0bH0nOB8%4P2|tYe1l1sNDI({4mt}tal`F2Xuru~>@^>rUUY`>$rapitPRw;g#z=j z?jRzJ)=Z~;(9WN=4t>`TKPY(E!^nLx4?x2V5yGgL8VgHu2I%^D$tL+fx-^e~0_=8N zjua3P)6BKE>N(7QTM~Wvv-CI7A-^9+x4U{6<|U5$QoL%`@+*K}t}DYh(33u2uM0yT z)6_uj5j=qUM`ZOY)Ysa-PgB5vZ10x$gSdm&Is?TBXJ&jV3{wN>*u>3Z8i#RL0r@ki z!VaLUslS@z9yAn6a!54F)JO#abzUg59i;SQEI2dPsu8lF|G|0C^K)ct$Wm9d9Vp$> zVJLYLAcF>(LlwK2rSsf#>tA2! zRK~1B>9F8NF|#nHV6&8ZUC!H_>na8Y|5cRH^A%sQF;n2OE%m_`gq5R?1iaa%#&de; zcIu>CQ;X{HitnO=AO<2CK%vSEBea%8s!&vRZ2wOf&++78^*_uhjo?4%@{n8R!rf56 zMx~_)Sv8&3mNUzb13)drh|@%BhG9vZn!vIxyq4<-=(!k!^!}Py61>&2awLzmNg%~a zKiou8fqnqh25h?~%?Fe^D--5@4Cl58AQ%=yL`xb7Vd&pBo5??yQ3FA^A>F{cva`6_$V=s98dv_xysu)n6hE&2mnPKBdlk(2gd^GYDVr+%9dj> z^<>BdswHplg#XzHY#7B-P&F&bDYL#v<(+D{{a86#=GEltgHLO`ht?Ys8_+U|i9jl& zQ*yEb`=j{jMRZ6Ik~43zX@n$>fru-vB@l|$c)aKS73g(eQ#DF6jB|qxCdkliu zM*+zqI5t%|LP)G|*4qtMLpsUODF}Qaxl)T)yddF{y$*59nwiXwq7`vw92=`+2r*6j zu*dIOS0Ez;k6W*|xxPGB3fE4U3c7*RB3svZc(&~Jf*~KuQdrpTiZ)}*lpuBP%csSH zI=Rat$yL~k7Duq8<@!x1v)%o8_6vP>ezYw_Jr$au*IfhuwU36R>K!R1uJF3VgPg%T z51Z1PNR+n0fv#1yARQY+bUSJ~@}*8LBkJj0`M(`48p4#0T8O1D?1Sg93Bnc%)2qW9 zhTYHE(Is!QwpE3Iu`Xxc>nN%A^>`o$TQ-=m-eu>_(@Oq_J-#~>SO;ubRDEb@Z-#L2Zw7A~v-8KHEdaF`eHB*LV1*T0)+n^}H| zkasWo7nK++dGPyW2lCwB4~RT-VtRYdvfN}K zBuR~FJxT43_q_uo1H^UdXc4m6pTH>oX)w9O^uNVIm zhjNYR&GA(QDasQ}E#c@2UJF5fFbpHoj;v6CHzd5wc)`X(>=~JV?~%p8=(^1sdc86= zeH4atlt4#)#>w)bOVu>$PDXjS(n_FnbZy_z@p)3V*q=8F41Ep#>opO3Ed)rjzgu5yFnTLT#_#4H;*A z0`N38!us%!32GLu`uf#riQn?5qr9t-x*EM;DqKvUvGmeW!wbmD?KJ+K9Tx*G{}I3& z>h;0S%L%~q{0oum0iH_RXpHji*5Y`iX;69~kC)1POs%0?`?+8G&sBwl31Q0ls|iEH zV%N=wRMWHwpxj-sO@)Z`m93?SKOi#a1u$AbiX1C`JAg4_3MLFTUGYK_1Hb_J3aqg! zRio#MFm*WrznmUu#E`=6tOTglcYR+Iojbf@^8;%K^%l=3G%g!$KYm#4h!m1PSGv6; z%$@-csKmdzc%6?3Kn(^6zGA##rbb7GDyteN3$-X8jzaeOLS_L%rCJQNdX^`4b;aOA(YlRL`PZunWXbsO z{7dVr>cSe~;>wSYG~?9@($@zAm}icXgn%H9VX#{@Ym|EoZwU+duqO7bjP&8uql)^$%fvm9@6;bsV1 zz}vt$DOiJw=R9r{dEG|#-znTS+Ecgce%$ml9g zpO&)ZAY}-1@S}R~r&*}hCvjp+en|>~K>1!n^Hq|piCcZ`KNl0egU0dHn0KW>+|A`1 zgm@#_KG#*&G(t4o_aiZYl)2Qel~zXB9JB&KtUW300Y1P`EUHY$G%%KM0EumQeb`WA}kx3RD$7vGXHh9vSBC2 z0K3#Yz6}%d65ouDGe7I1j?%n|b@j;8XHaelUcSv^qreft@XAyePP`J;IUseH{3{nF z#}sg-`rosXLDUTK)B?5YpzWPgEjM+*C2;HYQj#m2V3fNP_6F(K86{R-lGaLVA4qE2 zyO_3D;7p`OjdP{s8fmm^ykV(QDF!Q1YF)G{XJl-dH{M`ie$^5K%=MFXW7v=A>zn2I z0kdHd!9Jm>&E>usJI^f7 zRDY^*B!KT7{KW&=@e4BHe}TPVtU6c=^Tp@Sacy-1wo$o}S$@7_NTz~@qHD5CyVPjE zL_h0@(V-_MLoB9Kv7mQig>kE9LQG|%yO39a_~-3(V-s*A_W-SWNdLMqOeN-RM{~~N zH&Q#m`JeOn8ujJCh7I6AuUYU&s#n#q>QcV(FZ$b0Bg>jbz%RzJ2Q|UP?AQbATjd$@ zTy)6;3IjS}Mla>d#_6y6OTGL*DPBiql?o?TX~VO)ANq=0y>sV6KY#(;su!^hYM>`tD4*Z2+?zs8dd%o ze&_W3yUtiboc6M$U>_>ai1>3pqf~l4ZT_?CoWZW1e!!GAp_!&@;$eiy`#_K&4aA=A zT&PPvDAu8%VJ2yRxL{~;w^BGb(fw@ml$)JDj;)l*p(*VE%C zU!%Kdmx9K1$%Rp<{B0NI^bU=jc_;U6G8 zXaozFKM8;cFvAf503d=OL}q9JiA$krs%}*M9VJ>;&FCM8IfEI{L^g}4xfz^wa@2>) zk1T;wWxkq2vgHpd%Yv0_nKV#OB{mA{n;8E+_sY2Jq<0AwW>=0+uiM521l*kTu2Wl* z6SU8b3kV!3_qJt37_1cpwYCbPhNPryWC_dVdXMiXjf}UGk0TE@|NHXhAFX(nLS(5=Wq6YUUbXjga+$ zzr)+5@E@Z>)MLhFTBvg0qtmAkR9iTAU=x6N18v-K!@0UhVu&rxn5dq7=# z#y~Io#nW8_w}~MvUgg@9@)yXXYFB)@-cgERcyWuKyZIo%EBH7D+veq* zjcGercx*AN_bxo1xs59_(=}Qbfft6Q{6|fgAanUa>y?ISdC=KJ1U!8WgYd4Ji4Zt7 z>0n==x`b_B_K4`8gs?(onW37C?LO>VG#B z3g3D%EC(sPrLdd;`tYbFq1xR?(MspStC*neUsev5~Ru%SkvI_1Rr-YmB ze{=L15rX<8{F<$Gm66_RT%5;3fFfT%%SAuEN38zLD)cAo^$;-o)O4^E+#B**@U(lx za@=zZ!sQt2=U@16qugpT>=MH%#Q*r<`Sm^3qw+@XkL#qOiUDNklW%2MSfx`f+KrYz zoy)Y}jj=Sx7~sK+gZrdzQ_DiDPkY2tZaXc(|MVR z@d6Jb>ofEHx%<_Nw*(%T3`TWmsnQy zU4-!zz`R|o?>*|%112r~j(;(@&o@}TT^B*YMnGF_%>Qzl??c9hPz-6ifynBD;8{Fr z9iuRop_SCOXEPIN3Rj*&sOw?~Tb=}75m582^gQ3}q0^4XLHrQ9M9HZib=G>zU<7z^ zgD*2jX*(#YnGhA!4za7O48DkdiMn^^Kls#;LHKewwI`m0B&*pfxO6e(q}%Pp)6C`) z@+axVlQz9nTkt-ir{_r01D;?nYn}7l7M|NL528%tuK(8s*mF* zl28D0R1FQC9ew*A5Bam`fSXFNg|i}rCy(qDc}pP!zQdt_y$BsnGn~ZXp(N9!-B)&i z6wtZ<;5EK{Y`3Q&P#^S$Rq;T48xMQJ5vrqo3?X%o8dE%YN~)cob_&`rE!R1B&2UE+ zV_n^uXRiyd%!dXMWk7Vr4K{5bZf)u;2-ohnSr>>5(zSbXI0=bRNgKlZAmtw%lr&xD zzanzm`}oa|eBj>=8}%M|no zi@BC7g49XryUb!8Ui|P-Y?}@=I-6>Fg%s_~*8rZ!141J_2^hPitQ6IRf!4ZHLKOYI zkZR*c+xi&2knaGpI49BX0SQZDbgh$2>sz19RObf&FbA%$5z@`Ojs#ZTM>5jekla<_ zq*RLpvI{~t=u-8vBH2V|JJ&Z9PYjTw79HTb|L zp?kR14et9-6bG+1=>Qip?Y1$+q>;(?s#I{%@rZnZI;8PdVcowpA*M@wqP}I1zu$z` zW1?{#1`Bz-f~lmF8vP;}xKV*dRaWNLcrqpv(%yxj*#tmn0P$eTOXgt_TD84<^K=n! zqU?S$sn{b>aab<_aO8=|7)SB)rQ@k=tQbjwmQ7zH6Q;;y5vxE*QDY* zzm_z;fO6D$vOR+EH*a#p5PSn$XEH*}a7H9x(BZoz^9I=m*R49>Mdo2lAjFp^Vzb%# z{{!yWKSyo-;q`s5>V3OtA!^8tJ=bhoHg|d=b&!w`d)0Lor^Y3#^HICOjz0n<4jkG0 zfTJKBW#DyuxC*&C1+L}+t<*n`tXS=>7Q9k#4QZVqEoqJs_NJ_wxEL*^Tf-xpq~P0h zul|vpx2V11i2aSBRq*iyxxFgzeJh9ZtCN6bFIZqr*p3JbTvymmc zcZ170@YwrSEK|g@4zI+k*nr zYo8Jus&z!i80`IyFW`>J9JpRM$q&;^!Wnc8#+65M#$wK=)p8+MD%&bZ8HxGDA9tR5 zRh9@tm+wpgi{{CgUevGGk4^uxxTmrucLT<;T;EJcc-;{aaO9_f{?(9?Ob;#rn7Up@ zdpW?_KO_AL#R}}2bWaSuJ!#Ob2a@Y7k*7$3hwKl@!a~N zgX+HH`;!iH+>5~uIAaALul-zFATvQkKgQ$2(^xPqFQ%XM{=sbsm!)Ln#szoCk zcao)`PrE4Ph0xo_k;IvkfUd>`_S{ZDj#XT?j+)NMte7M5Th;{fWf?>UlMYx7Bl2?X z&*5N<4ewd5}aoh1k|JNmyRaT0X6>nE!;g&|hpom*mjU<+|N zk#(7t;WfK*=%EgZSDEI#Et0~9K0voFb=_mZ8sfq1jDhX}N`n9she9A+>TSt)>J>(? zO|Y$zQuw;K{L79*Xwyxb0i~ef5?4WE2R1VhpFhdueORk0Rfze>55Dd8c@D+ET}fN8HEd8H(D`lf+vmq z7?9DZPy} zsfHnehYs%*o#9gz_mWdVlAo4z+%GL)AuXV>B0lyDvdaaInrG8UYMtL)7KeV4S4mOH zJJef|Kkb^{Hu%jC5k#*g2_$ zgr-;ZLMVxKmh#Xhnjk?N(>~WW!2$&^86Zy|a##55)Ybt>`4{tQW4v|EV0!+kR$#oY z7sx(`<12qR+Ahi@@(wIIff2mf1#054lc}cO@$58-t<;nlz2YqJi9N0QmTH`_T^n1m zWdQ=pDB8@U1TPh;zV*t9F;(?O9BMHD;Z-;rNDdXu5O>w=mU%K1ssk9UZ8Az5yKqn# zdk=ci8+PY(jG4>N_Von4)M%!H^d8fi+Yc@cVC=-z2Hi~v79)Ep3>pZ)oLQtUj$pua zv=^(!;YkE-U&~ZeIX{;bkB&w)WCA{zZ!O1grB97AB|> z_(d^C`3hT{0IYXA9sI5e)o4S|I9&LZgoCr2q#{mbUF2_e^!of&K$awHOVr=0-x;MZ zOj|y~+Lp2R2hi-_2vGW0`CF#4yOJC7U40)gRDHB*Hdbu*+GNETZ|-MhWIgER0(LF( z{{7HsPZjDz8tZ^qtgU?YIvj_dH${s<$G-B9I&y}2)Mr0!;~g2cwtTx7IhGz8heLE? z@6^w%1;(4kYymIbLDH3Q6}ZQ`(O|aKtX_1FJMvTM6xR2Lj6gaGRgfC&_F#O9z?$== zj{u`lfePy0&j3ZDZbWs+yHI(i%zkrAzq@AjOf5R>Nj6u5`IOV4Na(g9%zFnM*tp|M zk4~j(qMi?mFy|!5_m_FC`M=yiz9BE%pa8^-AOR~m>C!AUMBzW=>gai`FX?Tto^ju; z^!@;!EB?UpWdVkZnBuKcDZ3`mA5mj&Gxq6Rg5VmRaU)8HoRs7foosxLeLc8@hS>Vl z5M7Qr3aq+rYN54hub5!hY|t}I&vHliOil!@nuXzmlpLT)%2UamD3h^j>%z|JS@H++ zsgTXc3L1t<{AY&>D8BHJQ3O{opVoAij(h5{fuM^iVqf>L9Sv(Eci4na^{R}SQ6Qz- z*325@hDa<3?#I$#UK*76V$YdeeI3R&^04}wb28+S--Qj_c*yca-(Ds%bf%H-kUmxz zw-t=LhOSP(w73Zl9}GTLGnr+ijhR_uz+as*1;Ci}+sDpFGOIl~Th#s~=|#%; zuH@c3t$WQ33@}cvg8}O{Gw}?H9!2(~VGbf`0&361aGYT_Fouw_`$6nHQeHdMIT3|+ z1dOW^s=;$Z;Wr+S+ACLoO?CHpjQN8<=$&9%m95R7CHxM%tB>#T@h!?JK|;-+kkdH8 zY84KS`v-tBp$#ZB1OyA0KN&y}vPTn`AxaDYB7*n#pGxr|%8|qYLc!}Sm(mJT~m3~lV z-$?K+4?#ic`n=vxW~PX`YJ!SLN^*In&S=5Rri%N2=b0vV3}h_jeJX>e6doJS2#~p^ z`@&YD!bAQHt*L=zYNHE&NRiHGdEqL{p~DUA31qfu+`5F>Z?`q8{3KlR)WTY29#xr& zeK3&0BRa%9%8aANI)?}UsoCWImyQ$nd@USweHDnEUNlrgcmq;F^|u5`dIo3_APtg; zScoQ?rz1*_LJz!pb9o@dGR5h?*8W*)GugUE0JwhC;X{X3>25;FtZzAB^c;6;B z_~H#%lBP};7yjT)fM7Q_6Kao(TF&6^0&hUV^U0~CsYI8$B8@vV{+GcUs+K#8mym>% z(;d@K4UM0iE@Im-4iFm)MicBtGo)g|m6e!GH~KpNQ8xAI zEX}1nD&9O+RqcvyI3M339qdVP69?8U7JouJ50Dv`TW$SEst?Dli&{x+Zq^=ffgjLE zp(F@v171bFF8<|p#h&qo1{#UVY5Qj7^%d%%lZx`n9>WlUC)5BGuklm20R+DkB{{I& zDa!A-sIWCmUa^lJ(ku=2reYShuT~>?#Eqvy&w2=@S7BAsy1T3z!Q?O61`+Fc{j7O? z?Hp)wdn>^df5OWUc@cktd6t0)Vn~V;s9d^9627U(wQhnakr44g7Z2sk1xY3aVo_*k zHR!#vFFJpd;XUp@;!(I)HkvE@6aQl<@j=G3Wx{lp;TW4OGi!JG70x*ivuLkbCuvrn zK}y=y8XbkL9yD1SJ3f2a3ZkCD6c1?#d^a z05pjK8PD|d(KQSYlSm?Y+Li!G?s)2FzUR}^pb03CbIwi#B z7l6^?_Yj47`TBf5KV7Q>s02C;O>P9;8aQRz$@nd zo92Oc*!tdG$Xbphoh=C=QaZd+ZGzC9=FR9MLFiCfU0Bov19qo?@qQS{>Ic-yT7K)``d+igKB2rO@&{@c->(a%9$>jQm%Hdafg+0}TScNR~hY zz3zjyz*9Mc=m)QXfCUITCaY4K22hriIlw5o!<>SBViZz#>irAVTM9u=Qlk02=6i%+ zQha#D=0i+N-^-O7d+r-Mt>_J2#678B$A_QeB++1A{m}roT}MjNO;NlWb~Y?MC}Wwj z7H~R6E+~x{($Xqp+^ZaP&e;j!yCJ^tmZw|91$qS!D5rRIp~%9<=W0`KHE?*BiC(T) zekZT*5py}M+tiF`3I~NJ*Sy;)dQ$9+z2Q#A_NPU zKN^4(vBeV-AOJupg8|?G`Fp13f4+ENhZ^&Ia6Mpje)T#5A7Swzf#{MH7x10PhTi2R z9o9k#E*_ySyuXW9<*7w=I8)E5Eu>xE3ya1*WL1#(b_`O2;&voG(yb{%jX+=tt_h<= z8@cs7xRko1aYW7nf3^W5wmV8t?k$#2H3h!K(J(9{Df_7_$e?y{DT#ZodCs|Zz^ZB? z(y^eXc;)D)SQliIn^u2G`0_pTqiQC(vFvYuN?jX(fsHL#2GJScz7?iGVmfsGb|mnt zI2)(=p9zW`^BdpLmIW4n)I9@m-hj9KDuH3N%JhS?Y?gRRE$KeRSLFlXmHlVo@bU9| z$Z6RuZ##V+&e@)E+X`8&QBg&yfF~*H5$Hm-2^uR&9d4vc4?C=qpf`${!=O&HBjk__ zXPmF=0O5IUxHb+lw<7S(A%S&N5mg@#EF{f3X7oIj!wU2V^JNlC z8!ni*GKq@f>X0=l=BD-!@yAd|$`&o`+6AJR^g#?iQBd8eN@hX{Ro*`^3d(2tr@ zD5KEyG?Be70@`n?uPX{2O~}o7p_7i+Pm6^N%eraFnKAkgMpS8varm3iM0tz57W&9r zs8>@NeApHV9%+k&z%R*g_?Oq_*dpyY3$v&Qj{~sj?S?F!CIdW`k?gGbRO#}zvA_V5 zm_0G!;QJ%c3BBVF?yTxq)#K#iHs#<5+YuPKMuqOSTA!@6oPw`qwNJV*I>T+y+7D_9 z(l+{Zi-PviHJ=M4^|l|iPhDFb+IX zPuL4JczbtmEhm?{i=^?tJmafRXCjNWwPO@WfyV!dIrC!K9_VpcJ zDBLLuT%-XpuoRmT;#uV6$7VA91u$X&h!-w?Ie;-D2qqLT1Pm~M1Hb_J3LuwTC_$GW zEnwSt9U6IjGYx^A-Nx!2V5mE(`NGxc{ zLrjU6geHctUoMN7+D>59Gb~=`bxYsHO36q=cO4B9Lr{U!Z%6U4YXPaoh&j#*H}qFF zM~z~|+1*E`1$9yfAUO^AwzQ7Dk{i$(HAB3%su7>J=R_X3JxhtyV&u_9R2GlZfZ(ya6$wK>F?VWzK?r*{j^QPm z&Um1!zCplx3ar5keQmTq-{yRUPa9wLuI1n}=6$Dy$piqx1gu;qenh8fiI z^x>tR9{ki_!NJ(Abh<^o^Zs7F_hTuIA($WW)<1<;waT=uN?S-PMTW2FEYFACS)isz zxTo|zCCC2=aD}MPi|7|^hm{d7*~DlGoRk6tF(oGWH<1yx(BKWQe4?UP$yeo zV2~70L8hXL1fBLuc2^8#ADM?+zK6o#gGVgh@#yN44K!aFkoRF@nCQP+5)i_PD>$h| zX6nJfOVjKHW-oEeXiXCV>iW-$yD^?W9;Opt18#%Z#5`^oa3TQ$L}IPzp%jdU705l` zkHx6CTmC|3$?PfGo5;V;|Gc*MQ%SxlELdRF>i2Ht1%+4tUs`Ss zjKNG0p_I;#6@bw=fD8g~7=U5mzvKgSSsPA{*YIcgRWk9IF(>hmOTWas93{_INOC!$ z#t&zH8mg5^)++Q%Rq+7Dv;|zJAwxonGv+!W8ZF&YTf5!bB#fcq6H$hBqJKsuwKF|! zS~4k}BTXlv(kZ`6TjGrz&0QCG6qUp%oBmkBpTF~Gq>R*jh@1^1wolcs9{W~b+_xR7 zxs6U7gg)3PH(c!RUeayvF)J#v*H(Qt*od2%!tX8038yt}IAgxs{$=S4~cz;=91=L`CzcCl$02uZfx?NfC(RJIV4 zc5+JmB6oFCoeq&VU0BcM;gar&I5J~MB*Zs)E^{F+TB^u#+_B*BAwkWm0+Y&8sWiD4 zC$1c?V1bav|Bf{wry&~+8+tcDRdfhcNG)2+`l&Zr!IT#AC7~RJ)002=yL~=e zgJR8Hli!K9HAAO3pvXE_33**Ep)?fQYwXz`q|eH~9CeZJ``Yn2?@4Gy2)-&qsfmUu zm$%2j&X&shQ;hK`85%qnE$=f^u|mLSNv4RDni!<9&h-Skv}PuQD=?J3*N40%~AK5nFWJpLAP zg+$K?_y;gO|B5~Kn5lTma-xEXl7s5>MjKOQUf9->oLS2CuUHTytEpKqiXybQBO}VL z!t~WsAu;eQvn!d4{3FljoPG*1goNc`NULi@UXAVxRB+}p(Vj5gI9|s}AT$C4Z=`M1 zT)!WB00`x{l7r;&m4NDq+|(TTosMawgsDM?18<&JjqZ-wX`)zV%u&tpluFJ=eUx|t z0p7R{`AC=@#H!zpJxiIue-GAw985!Rv6dfjQBv6>=A)H8 zI5`b_${C&|fPwbUy>Z1`NCw8^FsFh|CrE%DpIzRg&-RU;+@qkF?^VqqPA11`T)R$S zw%~$$-HL?i?k+TZdRKFUCg``Wek-}5LvUNHmw>g*meiCD?JG?(==f!o<1MBnYDZpN zM1otAO<795-^?Ls8Q!_80YyebjVS&BB03O5~#{Mb|DMf3B^lWPkoDVx4JP5 zJlG6`l8(wm>sPR7fqZP1_VNh=f?AaqA>wGJdEX6|J^L_=4!!3J8FR3jP8|8&Ta<@u zZWpZYpV5lf0KPfP{0_7qFss(zN&o_;9s{9T=R)A7uvo1u{K+_A1jy`kX$%W~n@g7>fYf05sC8**;q;N#&joYgQS7+|`-b#5lkY z^uct2W)%0P;x7Urjbq|}tIcGNQpEo(vr!H~uz`^}`J1EfL$B6)~X3e z>aSl+Mua}ST#|xSet2Q!{l)^#zb6h5dE7?Zd$b`fyNDxLb}$W!rd!z$aN2MJkv~B%d>MIxYGZvmBz4%h_|A zf6HR<7h)LJqHc^biKWz{{y7oJa*L-1I-uueNabujiE-b9-m5wvu!?`mxkrLA*f%u- zT#6p;75S>Q?G8BV5Vww7@z~xhnjk03&JC(bwlB`cq^^q{3LCZ3K4CdU5Mlck#VV0L z4Ai;b+1X1V6+9F<8Sl;5Ax)GNSfW@zksVukwi4_edcw@OY;Lq}E41p@$|LjC#iec2 zda5R??-BY}+B)yT+_&ox#B)tXglDzYrvX4ibEmp)R1|!3m=nwa8-af4pLQ*Q7WiH& zbr5Mg1-v{Dhh;GEGz6>RrDL_^P^Y&?2_&>PH=gbM)|hLmnmF>B+X{Z@=^Kn@lP@=N zMPqmWa_4+4=JWK0VPEf{Hv5d zCp6rj-uScVHXQHoh=~v~^Q%Qg*f)PegX<0O0p)0yR(^%UVPfw7mcg6Ve36 z)Yo>3_E)S>zX<9e3r(HV0}fLyBn8_)jtTs~rN)8?G6W=*;XJ{=dGH)rTN?;gF`o1= zi&+wFmthV$^J`%*X^98$acRnWYapr%hWuhjtmA=`)qw1>IA*$ml7aEAbNny?F+;(hbAhSI20)%5_=!ol3tmH@q9AIn(h zs;s6&V(h`X*ElUTc)vjqrq-MIYZf*(=2Qw9#YgyX zaJCw*Fk4Mte8fzOWzh-6-&7RXx)=Jd{E;qd0NZwiu!tn&fCbcip|IK zX5bPdo5>|iis-y2)yzTyEHkFn$>V95%`(LGA*5Pnd+ z#Qi>~Q9`ZcHc!^0Z(+Hl?icWmy}d&Dqx@7+x7*$3edL6QSE9822S#l?;Si54$kTis zno(Wow@{cl)xki*+PZh>5Hdp?whc$Ou<|HfH{xHZO8OjAv;@o64gE0W8QthYBwj@R zv+p^d|4R<@vM^kMc#x+H>Dr)|V8SjsYDoh{ zXRBw-9iK7V%qmET!jLoJy$OVSA(P(OY1l3kGTM+9REO@!B$}nDVLXNdZMOs$_tSam zdD-2HTe1mrKfGM#k~4a-gx~MAwNHic!$FexxvZQ+21prifI_~=AbW*$O@EQ2&{#cU zP3=`5yNwE)TvLk{hY)(_LYKXXzmZLubM>Lt9>Lh=?7`u~`XiJKU=Rz6^J`>JWMwKE zXuYdQhxG%DP888QF#*e)J)#L;b?mKJpkghjSi8-=O?V@69i$Po8kSAoG;Ka;$Gw%cnUJq0kbrM)Hc4F<0K zJ%OhvFMZa42G?8+n`dJ zX*g@$MQFo2NE~));(r3Gt58(>CYrxS-abiWKJq{(q+EbKUK8f7uLX~7JDiSz3r)Gq zh)5q8J1TEl^>0ys7YzEP^CJ;srH%G}o{P?P8{voigJhut%hq|sj+c+CB@Y2DF4`t? zx-Z?R1>i0__Ua%o$;o=|y}M_1Ns-1B3dUe1f+`00Z%smY1VDy>0Ki8q@b7;3KMXK4 zdPq8SKyHuoVYEV#M6*4bgJ4Iz(=yRT2YJuzhk410Wu;|5LTj_O0)Z$iNs%gg@5Vvy z^#{iZl|Kp!wD>`fY7=(Fr(}+&;kIcI=*pTs<3q=jrez+oQ~}$fX}sM<%>MbqXzx>a zx0mi>%hoBvmTa#RVg)C+Rb;Mq*97v~yrFwXFm&r<-9T#gSw3%huS1d$V`6q$js~p1 z*N5B_pEaM8P?DajXMBO(1X6H7SmnG@MCNP@v&mzJAwy?9hyIPaf_WI8S~GoO(W9W? zL4qk0X3?A*Mm?xSk_6X+*egQn;}UGY(W2dM7``$@jhr$~O*%`4ZWf!+CZ^88c*X%$ zpO3_*kmu9Q-zhDsaG4B1mVV^CUt|kMFuOr=ZAuJ7t>bBQO(P6TY~8$lj{kQtfMNEL$oi$$4MGE#|P$S5Es0Jk=DBpAiy=8NK&kkwwN%&Sdy&)^gj zmmzK`hNuk}5{&QTK$TGSI`@pyoAu+aqw}RpsPI>8@yO{AVx0SXgfbbpxVlg7WL06b z*s`car+lo+r4&Y5t@C?I3O5l^tYoAnON0J$;fEqH53g!5=WoDl*mXy&?BczVp0cL0 zXz>eGz))Ic4@j{nldzF0sm3kZfK_;Xo=}avkruZ*n_HHq7%_-T&-iqVwuvpsg&@N|PA9|SrAxNA~2 zg8>ex{;n7i7!xOfxM;M{9Ry1h<@2sAJ>H@JUai2u8T?ud5OqK+_Tc3FAe~(|uqeKk z7_P|1DN(T%a8rWzQyZ5g;QtpC`B&^_Cjq1u``_bW2tbCxg#v)^zm-{Z=>=JQ5N3C7 zLorC_gD-pux``ICGRY2LLOis7FgUk+exMe}`Cj zZy*DK#I3h+2%O0(J&w2M%(fBnYCBfOoFCeQc&(}II zq;Ag%3~8FgqvOfdXT{!P;cdBPxSQHMU*CD_I_7QgcKsRxR;>g%O-|+&T+POh{G8vC| zm++b;;|2un!A>^W$J!_N3N~(nbEDn=^sHww%q3rJVprJsAX~?WSP?+U15ltvpT!KV=pe3Z zmnK0&TkTgMVKm@x#T$6=do%L;=77^8Voaxhdn*QjY&L6PKL-;lJC^vilT#;#zBc7C zoSZoS`Rg$I!$?4ez+XCoZHmyM`~_Ep1O`e-d?fh`-I^x&=Z_zdAhk+733g0Y%cgZ^ zgrdUJ#~n(%7sQmJXrejTJ^;vTn}peK4OLq?|F3{2j*~lIO~#@Os+3;m2w7LrW>ema z6#w}3>=W?H;3v_@hA-;dHqfFoGbSipKe9Qoa|to@z*=%`H*K|^|b%PZJ4w8XHLcg}8k=Xx_q^E!zVCSJyS zqQi~6QRf0*coc6Z7nCJ`Ac( zaM6mm6GS-Ss;Yy4I)IJGVFMdino#nK}uGg_Zs{@(KXR3=pPK0RM-LgsQH$ zusmA#U)N;q)BR@!T=pAcu6#M%9QI{Am_L=Zvoi7OUg10N8Lb|zP8Bn(X27~Lenfcl5@{PVeNXG!d5^f-y+lou!ts{cM8ib5O)G{KpG9`5s) zm}ABR!s*9yX*NK@GYZ=BIp6!*cxI$L)n*mx=~CGR0D~zv1$Yu%%c|0nCBUVuqX!Wq z%@|BU^u)x7@AV1#W%?g4U2s@T0?etO%>M?@6!*}RzJQUnft35L7)JZYTWHmr#RYSP z_%D+=kcHsa>>BpOD|Fm$vXi0gSNypHtqXnCwX@gz$V2Yv!io^`23wH?+qiT_ zmi>L2dSVJSbX=Ku1YfzZ#1IqQtQ@iqAM2D zq^wU1nGCz+aNTLB1+MX--QYhYTkY8aWSZdu;+PqNNBgUaO-*iP^2Bzrb_N}IRZAlI z7-N!c=+~dcZfi4+Z^9}#UR&EpyK+0j!g{c*NdAm$E`SkU_7)LA%A9Gsy3fjm9kry^ zC?C)XLo@C54S*M)zL4`;DsUj@94)?i$4mBE0w8TdahUi$EAN~ZviovQ2yOfpR4+~j z7hw`Xs4D%^*n5yuBK<{VJtH`FOQp9u40X?5w4o8v%Csd7&6wh;pM(vUgo+uVhLt8@ zYTj+p3T4sSeIuiQV7305=_n%R^1cbp&AOhX&-zfzo03ieDuK)pb3ETr%^!qNf z&hjEQUpazhe_F!9GJ@453SeGF7FhBfQea$g?N9w<*SHdOBXq!Ha4q0bC^{gac(p4j zmB*OL?fv}~*xhRR!i22HyH&8mVTNtWNUR~1tNhZ%r(Vyv*dUf5i!`GIL9H_LK}kt2 zyo@MPdh+BUvIo}i_AOAO)pQf|E*LyTwD2!Nr64)GP&)*QOgDXvVGW%28xW_U&W(Km z8;yBpGJ}ENpX6khY|k989n-%Mu2+(Da#YL}_iMzc)TnE);?-Exi&tkE&qc&)j=&m2 z0W~uIysMan%Iwp)x>FN&SWhS`FB?N*7=atp4r;qMh2Q2THw%DOy1CSrFAskx@{P*Y zN~tq*4Pe^|)~aKuF6auUWtgHh`Cw(LnT`h+z1zA|FPU(!xiKq$Og?3<2g#N~MCj6& z#DKOoZ4vQ=eAq}cVwe$2$Q!nyv6EIAvtkMfy-X~DSskDT5!t_*?*=UVmJK{b5jAu* zYf~|tAMGr7B{?tpp+6gZnI|k!un3tlG@+8agxFuOg0x}J#SIoQQYhYyng&T7dR4v@ zn0c#-;z)$tD5B8j1Iq+m_Gk=bA;Dht-8Do!d8ZZBw`n>>ZG2gba25dEt-AsHh?vmy ziKVj=Ig|ve(S*cxY*N|3*9yc}j!_l>#)+?K%}D0V#=58n54E(I|HPWN&AVgHMCfUS zCq4b|q@XZ(%!7iPiO2Kr=9W6w}*y|CJrSEYSQm-5HCqqTmd-6>F*Ey{Vt?pGQI{A(Bz-1w{ zGWV*-bFSsvP|${A(iYQi7lRej>@L`^`>AYi!ioO~Bc2~+a_Z8Hs$s`EPLR>iNL<;F zIM%4K+zBAl{+@c{dbxKnEzacs#|szrKh8n@pXdATI7L|=4daQ5_J|Da*SY4|j_ zElS0offYc=H}+<}@~@onA-ywaxJ|^Lb*&j9@AUflY#~?!)}AV|s4`nD9U;}9YQa_G zG&0`IaC39o0NL@Ys_Z+-;bXj1`#@ctP+?Ow(brzGH6-byt>E5Curb+A_&aBarv_u=l{*qvB3D; zNBl%;??5r|QDt!Ej5y^^QG^R}@>?r`>A#k)iUyJW01x7&sMS$!k1ZD;GDKbE&a)W% zO=+FW(vv)^)EZ5iv|IwKcF{H8lG9TCDYQ(_&9HevB=WAc$q2T3*c54}8=|-lXUh=K zk`0z6s)6Fx3tAAkJwgyJqN~{_sR2MLzrv=uA0YTEx(C;>K1#$OA?a_6g4rf8jau5f zq&?NPcOL!N{zD~V^)%`QE1$(!^k&k};0~}aQkF*>lo=R@n;ym!otJ-Mxr}aU=Q_3i z<=Z>RqNZt;#AoNPOX#z68nxmA1YD!>FI4{euHv_MfG>}cN$oL1nXsp;0AHS0=uj@& z>p5I`8(i#k1H?I#YI)F>#r(o&oWC{u`(utrTbcxfT5a2l1+XpO}b^x zvdYFM-`|%h6zE;ILdW(a+Wj6`EwJxM$Bjna*eVMcs(wIqGJvH^&-~yLZ7E1*jM6Tq z3se4}RE#9_PLiuWRxu1zt>KhJYf>uAx&U4B5P^NjM$^;McK1g=EGb;W7EX-1^H1PO zKvUiV7K570w_(j-x1v^W3am4>Jj@Av@`XVdrF2OM`7iC!*#La1wIpr3!o3lq<(cp_ zQVDL(Zf&#asJ{?}8j?p-r>%(t6*5D^eBf)GFbtH%WM;UbkiMz-<*;#R*a-5@L3dTp zTc=2@2&S}ND68MOC1Bn#yFaH#F2AJQyj|ULw-cf|i|cr{gOuo7Z0)C13Hp_&TX}bk zr8DZDgZj|Sz|Zy7i6l-WNbYHk)_pb{e%jTIL4CdDIhOhHG8dc4Su#GMtGuS8ddx*P1xX#v%ZX?ctxPDKEN=SMkz}A&tMO#2(e})7 zjKu+A%no0_&ul@dkF8n!@GOLlDxd-HXfCXE*$h}Zi*Gmf=rDM55kY0gNlHBWkfZ+9 z-%McWyJNcdQ;zeNmdwTItv!@7zU|HgC-`IjMdGzF#Z)FXpfU17+u+?Jlg)e#1@siG zaMVIHqO}e+1RMlnQAAlcq8z4`uS+)>EUtwPY-G-B)4c1roW5Ph*Cy|OO{A2K4Z;cu z=*?IU!(Vwf=0CGR56$LW4rN>vp786o!pbR$Vy}(o7j-!-^{C~dT1XDh#bx@cISdEg z8G47)jMR1(V)FX_%$%3l&<-SHVis(xas|jA9OPFqXx70``8zZHIf&Y&eSBYz+Bh` zx;*7dnh`(zOJSjQ&UfIm9*0tRrq>KpaK+s9tz~IB(ayemd?re|2%#dni2m=l4hai%QuLg6w8}H$nW$_|(xtfytzw3 z7s5!sj6rO!)3~NBs0LpRi)o|!w+5qXE|l-IfKOu1X~%fU_!V>QK8yv`R(58Mu82;x z-*YUDxOG>8js;CkR$)(uD6f3;wG^|g@f;;m3hUMAR(9B8|1kDc4 z0TNL#g6f(;(b7FJdft4y3)0U})QPqqw~IYCsTB9*E%sCxOdR2`()#Dt>0*QOfhs34 zLar4VOzNAHrjv9;y6GxDW+OuiZ+PJx-CAQomT4=iD8V-Zlz(7w`#TU8VC)z1HywZ6 z1Xoef+O7n@{wpB@;OS=pPXM&A@rd=LNEsxxh8UC{^~O_WnEP{W$$=pGVTDim#WsA7VsIO2jJzpJNziJCLLOxFBgSz!=7r5) z1&!?#B7{G=STQH6OHjBw7Kyk*`y_Y3D*bLMu-DhX2jTY@aXZRr-ujC16OEQze(LXpvd6JJm-Zi*2;2dZr0?pG)X z03ECsgCihpOtIrn6k*%$69NPYjAAeWVCL;m@HCQ_$s^N1Rq{LxFR=EVcd}EJ5w)_? zuy<7hn%yqY>Yjja;0mggKb@d~#BwaPkQ+*NV(=Hzqsj$P@V3lQ#uf*r1gN2gT}|qIW+3RJ4U;APchV$G z`S^3FQ^SW7arsh6WbK1Tm->d}FCB$G=ex;1DwBh?dNwVqdr5kKGYhrMHUOr2YH>ci zKZv$ruu_u{C0ut@Nw&?#GRLpKy`Z|8d6N8ariq2j8{s5+nQL527wqJL+GKH)x-gCgBCHEUgm!BSPQSueV<}rX zR`GE793$p4NopQA`=*Zq;eS&-|NM)9%CEDOxCb95bsOE_2?WlN6v{c)j#%FiSSZsn zY>&Cv9c%yb^$AEmX&(Ly+*>IQexN(nZSSK$07^WCDxF4pM(Tc#@I%k`-i=0vB|H@! zkzHaS-@Lsn%#IL(u?{%7776eY7gglRSr+k)^_4DiGU@+Waj3vtU%T>#wDXiym?8gU z|277dsr+C+9GG(*v1bO&=)RZfaHWE3o-c=W}W`1eOaLdht+64s8@N6wMzl`kEBehP$tu$|x=mTCf-i9Odr1->?>Jq3s zKsRCrBlu4M#ks!yI!sFJIlrLsxFt3#Iu(R#%Ce3OPhADMo+J`$U6ot!+Nmz%*8a5* z-sn*)ncHSJ4w?w9Bw61N8+pFX$- zhjio|f&2^9X`Dvcg&qc_%@`VE)~=rBj$>(Gkz9{?UseFK3dJ@VPAN*OItVxgB9EXC z5geje9-oKUJ!Cu> zxW!k+kj2gaLfP_wL2rqohBkuvu9c8%7Pr)J?gQ4 zu@kzh!pf)#SN}0X0KSo_ZD4aT$&tIC)y-Na0W68-PZPuT9`#ypdw=@nz=sa5g^=5_J`?W)e}Wn5zt zdN%4&*y-Q#kP*JCC&5!!{>$cmz=xm%E<71CI6qmM|n7TI2K!m2wD%d)neGd}{zo5=ZPA^isp zmmv&ERtJD67nt$yDWtJ7i}1xzS%Jn}6u1UknA+q+YK%?o<@b#b%DXKFGzZ5cht&E* zonwGj@wK6>3h7YB0_vf%u!CT$@LJ=v+}Nx3wwsNA0v-F^7=WhUS3^>g`A8fvqP5y3 zImgbIRdG0rt!D$G7v?S!A{*7_FX|sqLEHyq6xWW>mDkyMCuz$18MoH` z?%i(}aRj8ntjU;)@2mXoJvt<*@GlGEJ0(#;2<18Q3I}*>X}nJBSVf|KU*7<9qyrgp z5Q~}}8m7%*lFhYSC=KX4cPr=p(D@;3qXRRA{oB8*2TO3p z`gi?x_Ut}r5ChR(4HJGslNAC1d|r|rPu-eY*>0q1~&VDHol8=ALQv5Y3t}d|9E4H00vo7y_aUf{U%q? z4x7=wgi2e1D^%s~CsHe3=;$nGNWt#J1rnV=Ama;tQ`Nm3>lk^ZK=gu|3^NVGPzV71 zw~)dRKqweP1_CI=0ucBgJUezepM6U~S!XO$7CL&9lUYc~xd@ETgkm68+aYt;Q4IxcV@*i+ zBh%u|vHd@L_k0dR?-sPZW3FGtTFbZp3FXfWzh(PdHA@4B&w0r5)XDH9DbCq+c!t}b z{I!7#dfRFtln~1Ae9BL;t#z=WQgQstve#zJS92tB<$BGhM z6?P*$l|)Ci2^YPuc{mae(^_2ucgXCi#^QG|<@!T2@xAFsNsW^{0NOmApS%ak)*>lB zAv*;$qriTpV%befbqybcFFmtZ>X&1Uu(nKFlJfk3$Zmj<0$KE(X2RGah4Lb2B~BH1 z1?-f3pO^Sr9kRA#=H3hdzASO7Lcgr-LQ;%1c*&qdk z>?0kqPFNV`1RJC*>LB$sbpN7<&?r@h_uH}J)V;IW^2fGh4sy)!Bd^y3I{p49lCXTL zDzJMMI8f1cb`j<~yIL`*u_%w_kF0#JGhrm>J<@rMM3tXetTrE6%Z+_Qk|<-UCb1ur z16c9dLd)Xq+EV@16A{IOF?(cC+BDP|!WUB~PLM6L%%!_a?4>g&i-{U-!ZWMnRBT0- z;UMj_!Q|{`?3)gHfWyI(BK;`xgA_bb5gvHghYx<@lV6lz~~mkDsU?j666fm|vpbMdwn@ zDOduL8DgY#-8=g+~3=^iv?Y1O_JvGAAy=?4@JR^hlehY_w z>APnzoy~ALI4ON3Qn!uXXz7GB^q8l%1eq65+|IiCX2D0}CtSQ_5!H;Cblel`=eI(0 zFR!38;tZe{`o9Sa3y9%A8T`URaFAMVbW>h}@H@?xxRQz{Iuvs6R-Z5QB|>v5i69{_ z$LT;4pf!Eq_@*{Nqv5h$$mNn1Df^ZTu8|nkDaFSFezN*ZPfD?}`u^_C`kU8u)a%A0 zbn;Whaddb~HKo8w0^^y6Ru+|9z9$t&%>+pl9GG*z3F9-GIqOa&8A_C!=*Zo`7_~iB zI;<2AVtlHxz#&(^3+9bc_tCh;rm4VV7U>ih1Fmi6!fYPPHS%v<47O3!1D{#Z3y!ba zs#f`LUGlJ4=7Z;)|5(um2F#Y@UF%Ihr5FHa*?d93TDCNG&r}~vbopww7Ww`W$wtHx^e)M0OF3D3hU>9z^=SNfWN!_^kFW^B$G68Pv$si+8`D32tjQuJby=!`)F zew=%uU)v+~Y9M+5{c{}u=Mpq>ESvf_8h0ZV`pdyMae(G4JYc+15QNN1gA7k5N~O<6 zc;tx9q?-!$DoOwZLtek#j}Aa8iIn4Cv+LiiK zi4;wv4%Cn%oU^2t?18tDh3f%zP`y0jhE<+Y+H4Wt^-HXq% zA3eEI0HyFunTL_P3b?-xs{$5DIVgbfPH87_%$Jy8IqftjD`1cjDa|P#Y@VsZO4G!Y z15)Y>x5$Bg7#mx;AAfD51A0+{LQKiVjNDoc8TzwxnRF}Nt9KGs#am4jgZ_0CcDFoc z>zZk|GXooPy;~1M@A)1?h}mj}B$q34Px+9nzpU=Oy_m<5rV_Lpqf9_DbIM=#()wLEBWR2g13Dpl5`Lh zB$CNM7&y=o$l8-~aT4u&gji4IA-`GLUM9k&+ITp?>v}ao@H(LXY_ZV^=r$oxm_OM6 znh>+TBA260mNf2fJ{d&ifOpjG-i337YksG|=$rtK29mcg3HZr`NX&3Ef==e{UQ zC_Nx-1bbK}W2gAU``8kS&;# zGk+TWILqaT$EgHQ2N)zc%4~b-0{RE?GEsj(`SeOFoUM9z3+GZZ9EVx`l@rx+{8tPm zoEVp^j(+S|lmf*7id#?Ea4jCT z0GtpHj2k7d>OgZzqct$-TqkgKOVO7i{Qa{ofs32elX^LypedEjCfeyHM8P8Mt}|6z zrYyVg$Ws}PXBPRlPg6)yseK}WynKHs{9B=pB5>9bpQsB#sJ*^E=mdF?`QmOH@yd;Tc2)Ed3_fJ( zoUm(WN&!g!B_6VY4gKQJ{u~S4_*f2)XXxlpzSdv*bxUuGQxycVl-e5mg)g)Ge`>>8Z?sOQY}!%&TWc zs}C(mVYXKE=sP#h;#`Niaje1nMF#2EBI>GWiWBjOsV)159c@l6y{R4T7Eo;ZaP~|v zyGBjD0h1`PO(oZ9S{qb4V4AqqN$kK!3jKX<(NvD+%D28a$xz`&LYuV>!kArg$pC{P zOxEJDjDP`?GGOV)yyqs?W<1k|J@k8)i;Ic;-iksO*|L3R)Mj2!fpVK(sFxy19hWfD-+ATDfSIcdHL zz^nR8jfePB_Y>&QQJu>7dV@_Adg2p3dfig}kMDU%!Owid{(^fAv=37Vx@F@LxpZKO%Azl)+CR8K>wUY7A%u zM24UN2($P2zo>ENx^p=M<5Hhx6D5x;9~$-*$~M7pN^a5w(gdVV{tT0*s8jSo_lO9y z0<>k_^3L=~Cg-?W`qR<=yV1FgGX?U84*Q6BH>rOO#~&#ZG7+hPx#3P=Z>3o&#Cp$a ztx}Y8{O<&xS>>7fpT(v2Ea_ukay{4xfSc6HTlO0h#j2xx7p3PXZsmz?XO_WnW zvB|dQ*LbuBiEfD$B)&H>nLLz|sVMwF^5Nsj%xkl?ONjmlYGemw3viok*a=!bKckRv z311v2f}1aBJ$kYUHXX>V9phWa?{3+4$20wgGkG5&6u7uBu+X3s z?yleTc8x1L2`=0h|NQKRQdP_q&ni<3%Av)oxm!4%K7-@6quDhqnK4sJ}WN*zObcEC~hQ zdB6^2tjZ;s7i{_U;Cm76{|C!JG{2kgU~rUjA+TzmngPtU+HOU^WO+|`yHt5A?f-os zvw=tsmi>5XsBsl8Arr9c&!dqZ5k~xXth8m>l|g+Z=`-l80x!3mGx{-cZM1F>cU;Zt zOOpmo9U|-_zP;{vVd;E@mB0n=#y>0}8t@<1e4XD9#-unHBZ=peCBcxYiK^|r zEAi0iR_z#4VD`&HnG?v|JdcZ}E#w88+)NSx8Ue@>05YBoC`14#Zb82oKp3Hi6B@9h zB47Xk-~jrXKfglwt(X;Pj}p#Nhg$VngD)^(@L;HoTgsjC!f6 z@A31oM5HfcXqQo`AkGt+h6v8aRK1Y9p~6WS)`N&;gA;~o6=6BDqRqWav<=O=DKpnf zt4^;GhR6v`pEJA6WoWsVa7aUKevy*v;7fsU(P4qpc}5n7n;aTAtK3luZI8T2OGU2e z!xi2f-T=$mxyA_Zeh*K)pC7;c3mpnbsLePg=gMyXI7p%gla1$#cNr{rD#7z(-MHc2 z2%y#Jj2rJ6Ko9~O&Fa&402X1LJ8RSChVF=hug1F#BN#jn$S-7r;n;u&e_yfI&dd}w ziD=ZvWxN|(dH~2~6_XkbOx9UinRzQ|2DCz98XdzPBQx)d!cRr&4_*M*`gl*K2~ejpF>w;1aB8>R{g+ru%)DAdpCawCM>Yj_d zz@}b4WU!ALuW}YPNU{Nd>vqy9q}@SZ47 z0k&!90P^Sc7Tu4Q@A>xV%}|JTBz$IJfSnDKmwcn3Jg449mG3gABk^}+8_0HXTRfeK z3uQte$9*r4XqDL{bX9kSNAZ#<*i55c>cCeK$uGoErvdC;;=0=kubg=ET&Ny=2I`>d z4zgn!tbwBD<}GYVe}UMc4TD$^2>OxSTw5gyO|B!(F_g-O)`}4O4Fm2lNb6s_oXV2O z6zMEi{Qw0Pnu;S7**Alnz-7h{Ww!$i4)@eBG79EHYgYRJS4_~K9yNMV!Cr4*o#`MAr|_RKE5Ue6m&|z z@DJZ5E|5($!Z~a@z>Ag^7?ReaJ|dYnGHcLl-J%R*d4%d}+3uSf^h$W@YQQ^+;SZ+> zL>He}fe^`sdTVF7+nT|&-^lA+`|&2G=!kp%&ul8=BV))^D9#RgDxpvM(;#-7(gy$qm^_V9BD*m#S3`6o- z+<@SxF-`_jqfb?JGuzlQJI4M1_42W*)-aG}?fN3~&2^HkByiB#Es!$r2q;28h;Bi@ z8Gsl;M-u`dqHq8L0pI}mpx(6^*tSUn7w#?;YD$b84D{T}X%=&SDMu$7jAlAURb&^t zcAdJ5J-*|$z|nmE{oBo;L7`RQ3oX5*<~E6GmV)3<(5G^YR&lrC8JVVmx0O7jReh(R z|7^5?HAljI4MQ1o7RT(`*3>K8EJU7AJVGiB15TEB@Hi!NzP=1QQfKm>5BPx@)7sS- zsu(JG^CfSVMS>uO5R}S2XQnDg-fjExsH${f&*8CWukv=3XzPb#z@8S1*_5u9>W}~O zgco-n$*$2MAp4lhwLeMSF+J4)nILW~m=edrYG{S(_~QC1A9%9NFG>z{vm~iq2akP3 zl75(Jj0+36M66J<0w^(;v37LLD5Zl8!X5Wk^dk#(!Y60AUx?SHcYC;0K~zOL3%*Jj z;xmP*J*xJk$GCCiLEMo$K==o>FhT6u_C-=Ap-=Odag7&U?<_|q4J@P zGkY?522TZL3xz$QA-@Y&XDe<7Eqr+q46}yf#=8=$5jsi5lT>6Fh)>l?^b1z&IWYfR z4E#%yfd{cnhv9;|YtwORI+0Z;_KUxBe1@z*#}1E^LDF1E!%FI`f<)Yg-{^k1MEI{G8zYEAr5F|EG1b8U zNbtLJS5zm*lD2Z4{#!;~E%|%5w>Z*&`yH8W*!Jn#T`yHJqaHbV`W+Jw;@Qq%pKqLZ zmSpn@w^g~Z4`J0d_N+wuw&+p7#^gZ%6MjY-7@L!P$uDQuq_Vw1_-n`d4r`eaMZ_Vu z#CkF_#Ra1R{oL8WQ5>2|W7q8##|lhLuM?h09a8p8#iUSsn$;jg0Qy@_B@B$LcM2RH_*=?ng&kC3GQKTJnA$K*4z0QuJ&{#MZ0bZ1;$RQ{ z1v@1RA9uKA@BO^Li@ihj4OMrskF~-iB}XHyT8GWQr-7>p`eOoB+vLfAFI3Lp5jq;H zdl`mu=bfAZ@yY$MtjH50s`oaFC5?Zy3S~K^oEj4B{pdPyAkg_N`d5zLM&4$Hi?|@g zsD7R^9r<7*6I*jKE8~0{R0&z^JQ005wKur;4zZIAoX>9|`HnSI?F;mjG~a@W`s=1C zs&Z;;H#bmwG+|r#&r(*C+y?ylkl3hi$QF_sV%&mHEz~86m(33A9N499}+!^%xPVCr_y=OB=ZqL4GfWLsz~{^0-eTm^D*`HfGTUd;(k} zreAjlo`DyL(p}zMr#0@w@!|`SUm|Zht!9*q3RLiHSKo?@hWm@_EpX-0(~G&v*wRm& z>zx~Y9pHNK<^{R4qtC9P|A;Z9E*LbyGj1)jq=^rEoSj6{WSBN0GOOQgbEr0$CfEM`lCZh*naT=<)=6=P^fGxZ-Fn`qdH&;!#++ydVZ$MnQ(Ozo zb{`Ip8Vpnjhwu6MA1akr%Mt2HPCT1Nefl;4Pk+p}0Y@^%_9-?#ks_Lk2$?`B(Bi31 z*>z}dcy!_Xs>~zN`QKcNnPn8@_c}#bK8I2=H~Wdvy>F4CVUMsdY_X>P{JP=819$fm zqbdBJWQvh$3InF}&5*!LCO$V@i)__>KPN>bpS~s6%2iOQ#x9vKnDBpG4%!CSG3tK8 zBPFyeW~5fZt)9}!#GO3y%^n*ktsWGryaSq~GB)(p*N}0jxJ$wDbV|u?t&kD`8Ud&h z05aPJFor}5%tL-R05L)eCL{q^2u9cgzySFPE|{U3oIMikCiJf|-hpTx)3c|tbctTR zi8wp#nZV1@k!!4#qz4X_gdxl`SV?F&ew(9NR*&?_t*o%O8z=-V=;qP0DSO}q{Lm)Z zRSLwwP}a>5(fd*o?Zlz-SfzjZXN_?;sNfVWmc?#S@D+M11G&rq-%?1obV4r_rvn^ZXlW zAkWA-Sj)cRsvYDr3M{|DgN=(Lw@D~ZphWt?YG+h`htBcZgX6Ag6D`Gt;&`T7#3x`t zpr)M?P(4wWvHveg*H>LAP27MpxOilS@utnfpJ1iBgDfD@Y*m&C>QUlA`T|H3%mv1`BJ>c_8k?)0%Q~dDc|Bl*ah2{G> z{WfHd;9p(THMwD+yToZuc!&7_dQKhVt1?+_F56tHe@Yx&IN7o*a*{%}pTc?KGn9i9sa!zz*{j3AtpU{v`;uH9a$bh4r_Y`8S&0!~)a`D}tPBgBBg(9S? z`Nw2#MHglg02%>^5&$x^2q;ECK^8T?8Gs0Z2onGSA%X#*f#3l2>e`jb_)XUX&_pGmC6KK>4&OZSC$|VW;#V^rsM&q$?Vj!Kh92#yJ zU0E2AY;GkH5S|;`O`RkTUSCt~+61?Z%HO+hU{{(Ag56^mj@p3eJ{LQRpVij{l&$!M zBY+NsN8V|FodJIbe&c4RD7SaRj;TB5EC>lQ+v*;Xm(cu?x{I`n^guUC^QexRPYWrK zycpU6OC{$w%X_4X^Huw+vK;Q5i;j1Uw^+ zuQrFRYPW7@oR?#DUz6vFUps$#v+eWYeV2aNnE97BbCbjz^_olDAMnLfe`^v$R2uWO zgFh?qQJoSopZ5BUl?VdnY$<24W0p9WuUJv{LbZ{blBSM+LGYW?+ab{2NDBXuz3;#i ziR>ThuD=v$*-`8Kzkl;oA(;fyf$73IMmlbFkh4;V{_ zPr*{A0%`{YXVXxbHZG+y>{x?a;;|flEJ%8briY%gmF@4;?fii`Z%|CgG|Cjd*%Nyj z3Nk@q;X1h(*H*IG!E5L;ah1}=arREQCcg$Udz>yzSn*Mwy~@|egPkFylrk`u(xRl$ zh}uIL!^W*~UbiwLfTL$UWUxcU|L;cX@tP`ccLvRg2|Gt@eMaXxOg#vl*nPKCwvZ2O z62i+?52Udt>35`VCJ5P8XMre{CNhVv{sCJCMB$SaOG!mu3=M>pzo`Z`dZi#|W0-C@ zFqn>sGa(s{q%93ilmJaM3H%Y*<%{S$A+Vtv1Bbi}zy@XTfag8zZ)lO_-FJCRlK+6G zd-I0QRyvvBHY!RG8E>xHEMft?>)q!Y8Yi20L+)IH7Ue{%=A;<1YDjc`0~G=3AAj}0 z&2P=DjX5{O%_J>9Q~Dbyx{O*i}Q?0T|r%JkNf)zq;e)l+200gp(-wm&Q1J2^v)@-ru3eS;O zu)1Q%zS4~az|Rif)JAl?_ML z*?%HA{@O0E&Nm{sGwHg~`pFtveF(UzOIlub0L$css87e>wqAUyPdEqDxd7vLAK>d{ zTi6>`X69tv;vDbxmE~SHq@`k5jKM%L0o0@6o2z!oF`D~iNtE8wRJ;QshPnfZs4>kV zHLd!V*5z)U5kkhx6*ve#oWso(R=THD2_`#@e}u!Ax#8 zTw+=#P^^#_Lo%$oB#joTME~!B?9}Z-#Am8BM_(d=#rIBnYx-_y$*_d++%L)quD{UX z8q|)ajkdVn5-v(oy&4)j1p+U<6;l9zYUxtdB%2nzJmH{SAYWR?@L5&|pkXQ# z{$fT5c`kWW6cZk1S;Wd(j)iKRI=})bmrPPvN22IYu13Wqi6G_ zE|`0-7X6pZxKZPBPGT=`v*Ja@%@==n!B2v+-QNUS9swVWgz;c%3iw-M?)@$nz+DJonB>=zzpm?0ey8tX}UR@g2Ju=iv;7IU5t9vaC z5+9K1<_n%fTeM>ZFU_k5D*$wBzfs!K8N0*#H>zUs~A?U!K=_1M) zCx((D9>TnoY@8(r6?59QAcZKz$w*I z?)~-WskPygq#X209EKr2uymON`wJKlv0>9q{5qXb0G*lOlidGjp|1|5WtY??SoZdD zu;)3D&Un1vn2jnpoU4h#H&r$!Dzi8VS8ai*0qarcRU^Yd0)Y+2^b!Cv6EKj%0Ie%W zjbH!(kN^N60NI(~5&$xBK@ormAP5Ko0vH5<003ra0ES@CdV4wT zADsQF>kOxtmETu#R+a4xPV_iebLO?Q`*K~CA~mJ!d_{`NED#NrzGvX#eJ+i3FB<5`qL!97?Y$=40vV`IU|_0v*doB~708wi#MS6(d( z)K4N<2>6~@ z)#ip-_LakT(Sbv6j7N0Rv!!%cCX*=XnfzlBP*yGJS*u2p2tUZ?6_p{bpsGi?Vo~A_P6L!1?bhA%QTbsta~+fS zJpe*)2!tT}7$fqqW4_?wI;(u$+r$$XZz><0%u!|o=ib}t7!k<88?P9rB;mI zNoLBg@#V+vYh`HWlq}-dvsZTP=yJv+4AT&8Q+kxx=t_6`p9%G6BW$@PDnLIpQzYso zxUA*a-81^6OnWlUrEacnr+bB-eb(3&oiSh*q`nDPu6_#Z9Fg85>F$`YHbeEGA*i67 zot_y6e|Q>w9YD-l&*N(wY4pP0(iyb&Hb*X8|aYNrEFl1<#O72_iC-$kD@?Vj_Y6ExTh75P#r7SF8!INC(ft7 zI#L}!Z2`MR7x-ibj?aS7DOu+S);$LUzJ4%8ljHcUJ75?T$cuiMt>2aBDb*jZk$y8& zRmxXw>ee!)>|I}oR(n<}$biXd!)@A*UzIhUC6WM2|D9fR2)3}^>baUM+Wiolfj;_k z!ZoQit25w`lD(vqW2{Y2gSml6YbdQ3y^zC@4zgipgZuej{Y31T)a+fkB$aCWpeQTt zD-~Yd$X{*09#|^IDZisJuNN1iXI)8n+)@3#6rQHiA;Dj3<1PHM)5{E|Edmo4n;xQy z;CA7L?4EAHb?<86*=pN!>QS%qLz?OW#Qhj6H@Lgg(aU`Eve((%2zAKX@ZKYx zR)Dp^X0h7mk4d!lVkhF#_eWORbwM0BcLH}71v8?&yMSJoY3F{;I^jvz0Q#_7UU7G> z?WV&GcVb>^<2gxy$H$$~!UMP9%D->;6dQXs$y8x+(X!BJR~DPrAJ{v?HDEp?o%!1S z0To(LK#)?66F3UBJBnVy#Y_IblJg>xoN2}8RVItC_1mbZjR{ji2|PCM^(;ZU6tSiq zKxB~~IEi^gh&EfLw{2g`=m#yrGRq}OLWMZ8bQQD&N?0#N2>su1y|!%Jr%QZC1Xm+= zq)4M?1h_*tzujjNv!3_Lox4|r$(fhi06VmjX)04*?~#4R9j+Wa5#d$i?krqxb45jL zP0==HKPC1*)f9v+9%Otmfh<1|quI3>-tQAV)`RHAmpV+nPC<%hQD5hc zok7suy4{q-d&@IZp^+178wvx0BsOZ921`u7Df7>_w6PBq4UmB@T4S?~C5xS-1!d4I zWAg+=60ddGK|vGU_`g#ZVdrmfA4P@!3#+>(qo;x`fIxTTm%wwzO~Sj#-+Ek&wM2$% z6dkvre!RkKEY1?U)D9cO)18g7#tMCbnqf0QP6A)>S;1*sg=hWLsaISD2+vGypHl21 zH-A>ah;1*#11JfyA}$+%8qQVF1r>n`SKB>g?UTijqrgc%N~i+55l%wf29Q}Ny(}d} zE5Bc`?A+eoq+?@PdK@y?98J9+42fybezOxfM(MRZ3*jmSF;5c|p|W-m`LS?Qn=Ji0 zV4>mXs|-q)r0yB=tx>J2BscF=D!@RACI2OhQ@@&2u|L67&R&VJ&b=! zIGKvL1;=vswYTKHKK&Lc_~l%Dyv~@n57H3_DGgGJdyh%i1g0@eVc^>#n$H76{d6)) z?fA3gzV}YnD>4gN%gby-u2ISMlwI-DtK1s#o?otT)GevONJG2aTi+rX?6f4tkNtY3`BS%D5L}&R5K!T=X@BKf1hZE6yNGX7RjBc2#j={)Ym`p z1f@KvS-%MML^o(HRzS9suSvcRCuVTPNROIa{D4|!;S&z(poQa5943S4S@{{#yWxbV^S52xU006%~t?#O%V(*?C(hY4S^i*iWohJ%a z-v1U_AN12Pxz=pS zB_^lW#^!8prNIz_{l=v=L~dAK7<;Z`kYn}=qkw=uwXTmvIgwTnMWx}1;JU>}$&^cqus?hL_v2^5H8S3HmqT;9n; zK$RMw_^bbJFPwu+rmlMP4lZ8`j5YO2;;X4oc-25fLfrsHiz_@4)vCN!l>6vRDPYX= z1s(?wnO-EcB9t6NAGHs$xZr=jDhkcLn~YzN7g+$&vhK4>A(HP{@z-RF>vzcn*UI>ImUs*e5 zkJ`#d1`S490T3R$BWI2|akXG`u{$H1<*|v~UFgGy*mCTMSiI82`~uqMw@xc_5oC8_vM z+1D=+64(8}#p?LUr$NN4E6EN}qrq;D_pPmHOG%AC4*$%Cl@6R`!%muD;ty%s(;#e| zxJ|?Ya9+MC04mQX#*Qmdg(VuYSK}l>OBT?ua3Ty6+3=Q|k{loSSt+wjR+|0MD+nlk z3^Gk~YhLt(z^SDSRLG3tsH@fnvVYZE*o8{Nr(T=jpXyuTv z8-YQD(#-;1wH-kK++dL;7)?&MJjPIeq-(R+hhCJ0M{y6151r?JASEqn7xxKYb-fHi z@`__?LpIR^|4ctDMk1t7f_Id?D2^@3rkzfy_L3jSx`K|0 zA5l1)Z#J-V)>QnGf0P5d^z--I@R-WW>QF2@(v!3#(|o(tq&wC!Uya05h){tL&3k>x z_e^2>i1iSCfUq$xDb(M2EzQI{Xm7@2D;t#+qQPTXcFxa9-t~Dv(6sdy6x~3HiF9x% zCk};h(&&%!{xd{8u$5k00{{oy$O#_N8VWwF6pCBA)C%yPcH)4%3GHpBHGeLvnwD}a zK3|}nRY#_&{!FvVwFa#tI@4SvnV2M$$jAnkQW>kp(N;>lYpKv6fq~Wjo=yQh>JApl zHv{2LJNw_}*M`>BwWL*7eDDk|-%%`Kly+YpQ+NX;ITj{u4zlSmbZ_o(G)l(5-QD?X zd*>wHZx~cxy&I)B2Sa-9i?IMQ~PX=BeBcBG&R|r z-j8yBRhZE>nvtf|xKi+^qL2Ud0NwJbzK3#QOvD6|2oZyDq^WlWw<$)3P|4%=icy@y z%R1`qVo{Y3IM1xMs&5VI1L-uQCoYr4O#jalmCrHJw!r6NIo}*xeqEsgz6{pV`O=!O zSp-G-9+dQ+GTacCv~%o1CPWo4VrfgD1T1i~3^t$YnD*`=8?dnkVLcPyXKeMujdJ}d z>$9>K0(MIJ60jkPS+v=Nhf}mR0oIktRiT@lBZb>6#46z~4{ZuiqM%ZjPevZfEm}EB zHFs!$795ZE!71T~6()_t0OX9=g(0nSfMk|(nrz^Rph*8t!j8c&Lc{W6MH;M!7n#_> z?t03Q^W_%i_APoy^n;sdmHb3BI?k;Lm|F1B>yRN#^JFeo$Sk4^VXC2i;Lif3bzx01 z;fVR}4KV;DYuOB(>1o;5QbU2`39CfbiW?j{BR=|4L;VX+5t(V~P6amz@hg!d3@GlT3|Fx}y6#-5sO#@*S{#}XDmy(-3; z#T}56@KhOt&`s3-vmIm?xP({49ov5w=mXSkqT7oZ1u}?3&sHzZPV#8zsl`NnIDV&? zdH*P#kcFoO2EdPyV?D}RyfY2}mAHy`p)n`GhPr+D78({<8gD8IAS~qt$EpEx4s~aUBh3+-NgE&D zyjCh+m0<2aY%vfaO+od7uXryCY>w&L7O@<)W~2uoTT}T4mDFEgiYrcTd98X_=mlS) zgR(mtiBLrGmMie{=>0IKc$-pD?g*aH(%ly_<8(lcZ_$70%7+sIU=R;J$A0QfPO}p| zfk!U8rJe%vn;bDSQJt3B5p)c zU59JqMC=7hhX8&CHN^1jY+|0^GM@`5OauT^mp>PP41tCd03e{C5Cj3B0Qvup%%C@| z;>LDxVV=zuL7mRQF8^_RL56}yth6>|g>b*SSfW|seq~pnN6$dtLaIWsEj7 zWUxqRsB7%UvUdthHV}(Brv+Jsg$}ACeB;4D*$yL%M0{ilafwFoTUw3klZhzV>AxGT z_cVRy;P{$UYpQ|*wzY2RxhkZ#rt4z}K4w$7e&-J!fa7kefs``OZ?uEUY!YMse<_r? zzRV@ksw#@B&WkHeg3)kEO@%5v8aFi<{m=gXm*_ayQOUCpq000^14OBrPIN89BTbIb zHG8?+ny|S_C$FpYi`G1|Y_(K3kxe9yap$Z(5@Q_Ke+G$64p=u?BjG=44N@OIU+Ulvh+%>$mVsOgY(axNBS- zcsmye$uD&GEjyc$z?Az9e@dnsM!tws8dQ8@3svmhy zwLAn(9y_#q^H0x9Rek;?$tj8 zue4axK>0wa;z)w9ODmt!TS#Hqg`NPeI@w)IU7jD*$E>%7YDypZ= zfLy1^Pc-EU7I@b1>NG_{W}^ItBO`)_D?Jts8Zs5`V*>lmSrL@q~Lvs~gQZJri$P3P9_N>)lXF7HBnOQwC;bkGgKk{o``vN9c93C(r;zu)n&SD&$$WQ22bKGAZl zlH_~3X268yL1q6{;gCD=>XsNsdLTLJz&ov;Y@I?xJR~N|Cw~!9bK{Ry&PRJ6h%}=| z*WlIV9s4MQYo~*owmXU+B;N@WAR0*s6g>{wYvk2g7_E~pq*Tj3|1voCO50!li_Z;5U<`FxF~V2Z0?*;AU^^XoEsV z?fF!-AeSY?U7G@=m7i*fw9T5dR1Wf1WnJ{`Yi}-^%CY4EaEJO~ zo=GCyuVDngyIOsd_r2Uu1SlBA`S{;g!tE8X_8z(k3gU>!C9)eCF4Mvg$qB?Tk4y7b zt2eJwPf__IN{gNss=~}3Wv?Ht3Z2FkW_=yxGU0=m;;RzCdOHH-%k^m#k>@!_${yS8 zvH!CWMv$X6XlC5;w9gV{rT7Ub1xJi^lB7z$c~+w|F}KmdB)3JkeQ=mC48lNr9wqxa zHj#|C0l+eq2q;P-08^Jg7{DBGKobA}ff_(40pI}n{~p{q*diPlZa}{oC?0&C=3lLF z66>$(Eo|#Fw_wlnVMiCHAU|N+7%raPx!!!%7spV0lw${A(}y9vd&b7Fl*+mF9T8b2 zG;M!WNpKUm*C7W9S;b~YcShs~zZU6D)snZ;=*1=Y0$Vsld2u1NVrDC09Mmyt*LAn* z{w!1F75!khx1Q|!sj~FO{e6_jB_FAwxjx#U0f}M61Y9vsj9nN(5&^)YWztFgz$enk zbgqwFxgsY!o`9o>kjdR=!E{4P3(TNkKVsua9=(?GcNmw>TqJ@%J#t<>b#f`9bODR}mZU^Ift=_?5LF$Hh22vxf`Vn&a)tpEnTD|t%oBKuSx z@Ag+I_WctjyZt!-F&Vak4mvQFZvorQhGGR&N4zr5g&AQXt?IA)d}!YZt9gn33yuAP z-wsiSH^={VuL$rhc{d)#1?%ozIMRs}HId=|H+{*tbU9V6vj2X=?j{o|gEyLBtDEmn z*DqcY@XOJ}RPM@!i({-JP!MJ0LQm#9%GrNJm6g$9>N;}dezF7kk``JGl)~)i*SV5I zMGAsjv_nLvMaN|STfuQ)bFfG4*8X5m^fJ~4&l(}c-G-3k zAWF|W0tr+4kh^7z%FCKUCNXK~wo1#HFDlYcoT6epe%^rXFN&@Z;?n=Xwa`vuQU#8J z^_*i(Xvnemaos;v>*REQR;6j?X07$5i$OKexAVnCGcL0<{vpMzoNq=@3inFQ-YAIc z!z_$86VaKNZ9scAybP^5W}X2~5jz}_R{!bQZRF=;oIWEvtHXgzKq;&R2hf|c!gy{)dB-K8O9ZVX%w}X^!u<_LD;|49lBYdeb zw*nGp-D4%YVLwb+<(M+^1Tc~T0H-c~HGm*sgeCw2fF;UeC`E)Ex5Rk}fGmKpT(j6`@!;Ek%92JxTKuiNRMN! z8;_m%#;bcT1cGN%%*{wEyU^4Kyy>-R_^V6I2!(A?Jb`%KZ6(bRMA@Sg(i46$vx;bu zj<%xXnb~sH;=dbcp!+>e`xlwFu|C=iW~>zP)Fy4A;IiOYf$U2ABz%9blY0+WVkl(GAH z>4z&t^fm;%IFD-)t>{~V-)f`f2(s_Q#yZq+5)u-d#5(E*N338AG2|XteAQa9S@uP9 z@vw=j@yv4e1I&JU99pJ-}_FrN5#h}PkKhjvZmM8Hu z`7`vWkAjf++uh0d_!4(NVLdm~t5|li_{p6k!w$gUmVpMT_Yg!=eXFdq}hHNs6m9ND3 zTL+dvFH%=6NMOi5J{O4B3qh-5d_7i1Xy$twE|62xc48}N4RFWno^7OsFA=KaLG`Mx zd#pT{QiR+M($e48swvp10V5yXhjq8#!PMAzv<7mh5brORyQt1z8goRsdVg2iv` zqF#U{#7mm~G)2D*dsrye@S7foEbt(rx|GhI@ZStK1)~uXhumw70-lGcnF_NLXI|{( zqh&X-D9;gucgz}@eE@EH1m+s`6{o~5l(^$l22N6Fa0{fu3Eb$Ku6i*jx%bJ=haUyn zemhy~)-hy7zXy2OGUU-4LhF}i?PeXY2`faIUuQO`39*+kvz=pS3ldp`z#s&}+S@@@ zxY2W(cJid--d9=O8pyV%juj(^yFtxSAWb>K zdzEhwDh!Mh-khm$;P-_J?MwU_!fy4P?xmM|0(<>>zsQ=t{>AK?_A=bnN%BhNQdcGD zH?~dZSt}~2-<@XSV~30WreBlxE=ct zFDkfj!0{c_4t&PDT|C{`R|v^iADE*eh&%*wMpWm5G}m2HO*njHp}Pg;{rcLJ=T zO`A%~;cW8U6iHIA0%sG0huc@wiX}=j*e9=AN+9!T)feeOY3E|t9;}2JwL+%mVuyTF z6g8a?Da?}Toq3QclN05`Jy*?TiN>8#^o1YxO06{-2kuJ^7H#OZ3$Mi%*{9cUA7{^V z4T-ALC6&$+vp@oJ_3KQ+0Bm36oZ(YE!w(hW#ftxUcELCQ4LoDm>22dfIi!i>T)1qA z0^rENE8PH?;D)l;h2QaJD?cfcRrdx@=Gyc0i-hUhwK`p(jKjUGVP7{2?n z8VxOi1EYV$lc5&1K*0WIaG!vNkXNN;l|x0LNypPus0@eU_7@emwo9!F;jjGET(b=r zca5&!-E$p z)f|xz8|*p;5hC-_C@mKKH!h~zTPBuacE!iGNeMRM|2cmf3%ul|)QzZqc4n>({ob5%s ztnv!h1ya9Lc=BYmc|Ds#=Rjr*hCAfas4k(u!rnPExqq1XCLpdh7{u-|7d!Y_zB%$@ z385Ub4XBpw7U-UnX&v{U^l>y0ka=kCyr#=8vUV zNXCh?(RTRReApyU48EeLz#6O;Zb{Q|4c=HSib*R;-0lMi2X5WvzpKZ{+W{s%IVHlWNs#!PyLUQw$>p$6LCJtP}Y1DYpqXcv=H~ zo5IsI*{NwnMPwTpRM7U?r{d%G;wPMVr6woV5K|nE-+g=mkoTevkgG5a;9}Z%iA)cO zKfmWG&rgqio_vtmB|rvr>`l}f+JIifvZo37+Fm)%;< z2$69I^CIXH0(}OJGMG{+-TX0J!u)8?`9QnD z@h;mspFXAH7`$tznJgi`9}6#&;+0bivVNTW;4-WSC{931BP%}`zyy#06953PfEd65 z-~jn>goH_*`y#?o^?!h|0ST?7^|G=6R|4W4hY#*j11);yh8ek}8mA2Id`aS&B-;!g z*!XiXdX#?g8_~0#mL4N5xZq|RxB9{*UrtMwf(}!7Lr5LuHDp@2>93~YJAeV#uBV^t zLnF)gY>hOYv`3#qvV)qbO9KRCHUK+pR+WW^Yn7#@2}mUNba>gcIKys%X)J4c#9-~o z7oSs?@gvPWf}}S;s5mMWQQdn#!f%^_=Ic?tl^s@V!`Hbd{!c?|S}O{lV@1ULw;IgO z&t}5A%FexJ=(*e$Hf%is*|A93H>9#aTp{9OR_&1l!7IJ1-CCTlL-?YZDFpvw-qFC0+!ziD-}R46L>xQ)Y$BFqWac z!tS#&rIczP_l=!Zx(YT1sgdCmnLt!Ie03A#J~O@$Wtxpk75d6|3Jr8>Qz)G2q2rG| zmhZ_2n@o9tp##B{3QTteFM}`Ak7*2~(OIndA_LoK;ZIx?R`t~~bMXqby7=oN z>y?atat*?ilPCu#8m51b8t1;~mZ)=KoOat-3$zeQmcGL6X3a#cd9SO8W~b$7Bvb)Z zlKUzqkRBIsPRRwpr=Cl0Aoa zEIZ%OV1Q^Xgx7@(m?`@(TAUq+x=N(%=9>%-C#os%Nm-Jj?lkgmWkQ(zNeqCA6T&*$ zmv7eec5H6HocQ6b*OK!dVWP#&i`H!XlU%uI&-1!g%`@(PiyJXNGi^6YU%<^Rm4m?^ zsCI8h^wsEv6X6PYiOWwAczfXCR8i0Do5K-VVP6MQFiUqZ%NEk`8CkEj>4R4v(A%e! z(DpZ%Z^;vB8+uUtX~h@pYF87OTfkivlN!4(QcczdC;F!T{qlRT2CWi{9Pk9<5Z^of z0=uDCe44><9gnW@Dm^;BqntfcEw*C_MHvi^S7cB`8ZfNkmiGn~0g-lDKE2bnq7V3_ z3q>%0+ZPKgF`UoHB?IGyf6F5HHLZ!576=j8ujwI3$-2)cSfo@?$l(+PN?SVY8Y`;j?p zAl(j{VvNQ9T||qIF8MEl$dC^;Zl<7geeUNR-GdG4}2TjyH{RL^JySTN$d6$HJGXl^Q81`FV$0KX>t|~0 zf95sb{&o-|Oor`O6Efc1(^yPpe|F8v40Uc@2$mZTA`7`KOgQyD1m1V``L3qT`>wzN*8HY&N0HWlgfBf&9z1;dY7gZ4`FH);^^ ze2^6+IE{y~4mS^=Ow;rPZ2YWoHg9J-;EBTY#BFVAC^zq*YX_(G@vmkyxNB^)p>lIZ z^>lZ~mQ08Qj0b*W|3QIYCGin2f(Xh;Am9JuYKpl%1m#7Wy&Mkr6r$#@w3m2^AN@fQ zJ(x6*l0VnaS7976;6d@0$XAh*I0(hQU*SR3uu@ldJ0DtJHe33c7E! z_HB=y-(^SC#u5M;0hkg1GN%MEo=65(7JfB=2myd56hpvqK|=$;0Qm_nn4y}U$wv8y z>Wge+E@>gl^_b)QkcUu$5Y7zsN-x61i+wi0Y^a{NPA4%W>tZItLtZVc(jH^g%Wuy{ zaIi9aWr@QHYX^~4!8ob#kkmKcRhrOEZ4>ZHsyi8Z;1<8Tjre`5y*F3%viU;L0@s&))1zjqA-+rvse@DOq~$j~7GP!U(MM%sIY#>_ac+cnmK$pT6INy|J88%K7*(PL zf6O?u2Ee6oPKYCnT-FX%zKcLwT&{6Vn?x?zh-B)sdZ~#+{=#}3WB6eqhr5Ug$P{Xs z4hq=0h6Z*UUSG)>DI1i`o+Vv)rclz-$=KKr|CE1onKPC|+9}$Ve8ZlC3nRXoA|et1 z8Ud&h05X*ZC{jQrOA|jBzz}c)697O)0w{q2-~jkENclZTyiw&fEsJLJIYwDw4aNt- zk#OTt^#47%f^0RezwBoR(5Lu9blK)bUu9X@UUpXN;&BMO5_iu7w!-wJi z?n`FKZiHfmcO5*YDrw6Dhn1O}hPFLHU^%-0$Gy=DC}10B0aB`;-{Xl~_9e<>BP}MG z?r+eiLT9i7%@Vo+$D09OF`?Px$RDpz3y<1aF`ltE#W-0M1IBstd%=umVv%#$a48~$ zzmnb?b}IE~Zw3Qx2i`Qu4Fu{|CQmq)^3SE6qvA7^s(zcTF4%_3;ts_GGdfBnjt3Du z0q`o+Mbo-`=N)CI`Moj4ie0}z)wT)r6^?%K;9(Q0dDj_*`8z^EGjx5_Zjlb%tghS2 zyGPTKhx=3aK%F1J6^z?=j>!17X8{N=qd8H}?t6(Su36oT59 z69tMC24FuK@l|YFxN~PULAj9ZlCg-~9mSu1AAPH};@c>%xcd*^0{}&!>O=1TBN|E_ z3Ji3Ma3`@mpRgj7)vhWPnNI}($8rkt2eL?)6tdbnZ~!eaTp&HNvRC0>2b2=Cv|{T* za0*J<_?||b(#ns?rf;ifpr%C&{nWOU_8gbyv>}Fqa!rDTB}}x^@uKf*6e1VCozQ*+ zk<)&IDQd_0?!ij8f96d?E~$zi(~)H1;C@mZPX#nn!z&#;%0M!M^AFIUOfV{*D4-85 z7K=Tz%%lJTrLF2#I#3D}cV)ECh(xemWPq0z3RqF2JWbxKbB#_FN-xN|d&6b0F6J~I z%e;X;ZF0rVVbi91(DLz<2k)q>n*c4zfAo`6K5PHN@}2^8|DWiPuiaVg4sJxz8h+Ba z(>sP1PvF*)E$9AX+GD;OdjU8LlSb5N)qMdR#+10RFR69p&wbOc@MGaRCb`D&m68sy zsMhXgdg#RTgim_{C+8GS4p?iZv`9yLsD5Pih-#`8{vV5Y#O&dv`6yd++#4#rU_Kg{ z)yV>5QO4_;r21g~j(9S0ATpW+Frq+;mL`5RfFOVbCJ-?=F;YMSzySFRg)cxf)pcHm zC$R^(;2Bs)$C-%{hQ3NS5wrZGtCY*B2@e!Jma8R!HJS4ou&l4W_>&cx?GwmSh{7OZ zhXfa4y$x)-3NQuoTtCfJL#IA9$6L|h7%C^$Zex1SyaNqFa#j{m=N*jLIPkP-TRR4o zLCj)ksZKE(alPpJR$*%+tHmp7fr?+Ojjyd!jWgo}am#`cXSDlw$Y)-$1fu3BYD%SU zOPT8r*Ow%GW)s>0y;0AbmQ`+eq#Sp)^swiyxU?ed#S&oBpudH(r}{<^X}UFbt7mWI zzU=rpIS{fyi6PUUnnGug@HCPwV^PRo{L<{X!$@Hwbu*YGJH-D%7nuVi&kG7`sJs{N zEW6XKdkLNO9HpuHNl4QZqHMx$!hz(7WBHT# zG$JKAmQ;I#e=--m@40Nlpqu*C!Gu>zHx}51r}tIx&z}w}?62k9?!-7Fty!1K278Pa z0+$5~Qt_=|5ySyKx%BXkJ^*uI5@kr1n2Mbi6YxzlK`&-cVW!y4?miVNI@x(k?Ns#M z&BzCYlqUjB_=A-Quad-Z^B^O4Q3@{^%sklGPnYem;n=164~RXZF(8jTefEPqbD;8d zncO$*cbgVAvBfPrR2Z+Y)$yX|U38dCvtTq)>Jp4SKw%QW5At=R|Vk*WsYYY9HW zdcF&BDL(B4e7m=vV~3J1jKWzcAs2asE^f-)G<}D5+^G=yhnCRlFyc<+r9^GY?{72j zRP*Xer;pouI?klyz)f_Q!ArU76okiiIs-)7tnSN(DhoGaqWJhvke063mLAmE4m2^( z84&fbQqf(-6E2ik@#MR=|HL^}nD$md_bmI@@`X2^Wq0{b+)I1#NA4%ed6>n6tJ31k z8RyuEf!kVb<{B0NI(~5&$yY3?Nrw1P-yk4L}G`#1R4j0160#fuI2O;QEG@@(1H7 zGl-yv)4p`pSV^*WpTpX5+`Hx#3j57$VrkEwwALOcK|J{GM|@>_sG?`yA4SglO!U#_ zT;F0Qk4?zNL-~{P{{_xnI#yZ6CA)}Iolp8KnjjqT$a#IlkH-yeLeAadm8&U_ZCB7S zoTZ`Ce&g97M%K*`l;(m6mW6e&wNFg)%&5v-HPr|qW$kKmVh2HXD$^2O=4OBwDaW3F z0Nvt$?WK=GU4=&bo;_>J;(gX^Tq2luC;aJr)P<@gS(LZ>=xF%$WfB%joLB zYJR0N)uyut{2h77^}yqN&1xrVaME6pWUR6Th;yZ+^6jYb zwzVMzC>zXSghKQNHeEB%wgrF`^c#ygzE}<cQaS_CSykOF(*G-S3qNG1_|7ElUkl>C32NbGc8S*1#mdisvgjJ zyJyOquW*7hiWJcb;0T-W03Bmamhj0cOqjcnw1>2N+$}JOTP6ji^EdS6&2_IJ5`{_3 zH+SRPM9)4ph0VW}M;xdDg~{ozDAbe-SoOCz6hGzNWdS)61V>H&v3mTtN;dZq9@-!= z7`$o#)9DHgz!LcG0LSKWA7!*&3>QC!mj<+~(t;#2UCN%<;7qc0s@+>NZ)62hB%2H9TaSky z75%uRwr&kU|4K3 zy{eyJIOus_Hqs3^&|AxW=FzUpz=Qh*=Na9-DwpD{Y`e!)yn)3&rxc}*z47dxtg zDidr6fCzV;6W>bRZX?>pKrRrABdRxLOTR>JM~?^KMjtBq*zy|!!bCHeFYnar`%|-O z1mSt!Q@_89h<5;{^NeS^YMGAzzUhN?{ImY1Lx=P;PMEj4UVcHgGk~35D;e{lJs~~3 zSq&EPPas34?GV*B)J-&qkBbi+*f$`Q`3H(f31~=nZl`j>UT-Ie(i$M(>!z1ALQv~M$X1`+wqae* zG?#niWmBK$d4&K2cZDa~puyO>!stoZBBx^91PJ7zvfP;sQwaAmV06~nl-B&2qJW2x zs5+p#(mUoYeN&~-Zc&Z{TFfcJC*c)e+8z-LY~bo(?lQZlr>^)i%YACD*RsYM|>P)NW`s?q>WMfKRtEZQ%2bWmaq&!-Bp!$}Ls$ zuZBC-Mku7`lH|JCIOS6-YeYNe{(`p4913*?e&wqXZ(Io?hZ(A`fEw59$~3U#Pe0`A z4v>y;q?7=9%G$E;I`!49c15=|X0(r%n|J{+toGKWnLN)l3v}{8Aw|i2MDL7gJYfQ1v<4g7uAwY@326o0|S<3fPg{rof>A9uiW13oEg0&(oQwem33266NL z$i((>cBss5?S(54Y0WF)0NROU7x9uV&oB0`zS#d{4Rgc6K%PE^*p$M}*q0qH=Hp#! zjGt`770_vyQc;0)ShgkGOkqjD$mf?L@@MSCQts@TGR^B&$UdC+;|T^{h8tR=LOn-h zy0+qZ_S~`3*mDq)-(mK^=~*7C&+LqTQ3t^BacodISowk}5AJv6JS;62wD9ehc1uDv zHtP*znGTgE7-2(H)NUZG<{w=D0F!y6WrP+oO&B#`F(u+k;hturZFGzMpDsXfScREV z>uWFDG6S_VqeHXF_%Nw{#U|x)f8hWsqOiq&OgU*fl9SuURd` zU_}jYNvlq5lx;k17J~d;92qqkxA&~_m`lg&pI^#Rq3g(S523SM9-!cP5q8y|;#9D% z?73ohYY=lmhV5Kka>I&O=EFMaZs{_wWFk2^}>A^m6+EAE(Ej=Tsgf7aG>zKvG&ih=SkEg6Uy&)Jnh-h2< zt83CYW~*Yq9DEPU=iW+{uAn?4%*o0AD4x%e zMH%0cg=Or(N#8!QzR|hB|3(L_`pwxR6f zy=iW=n&wB=EX7r^!8^JZo{f}v)|+=t+Yd`VeElG3+}h=%A$B9slWrFXub5Ak9~RoN z;z&D#EFrPJ_UgGAontx~hjwS`s@Z4_)PUD9+wX|nqHlyx#SQ^?G%$KabCIOM!HCoD zqM#B0G8Hhc!T_yfM~vVCC_)JU00IEnomij}0168L000BMe0%@%0Dv;SFc1NRfB*o% z0{{TS4FCfG0F4BI0Wg4&kSZKB;g|vrF=3?T^ClaLLUIo6!d0H&6VeWP1TTEoxW)T- zQFcMsj>^k(r)OQBFM)u#D)?ORSGzLftZO8sRV7I`z26n7sx4;v_xDk?7y50Xxt}^P4W`$;2N8;5V^BoU9&X!bbqk~ zU^qb2lXlxG9O)Hi9e*dH{E9T zW|dww&uZR^`>!M#7nF|Q)ZPkYOE%)hn$0sI+#RMHuwkv=^XrEy`Jy|K94y^)`+BFTrz#G| zO)sEh{sjf5Vv3E z00y1BcP0ovsch5P^*yUS)V%|dLkldp7TM=^AEN@9B?4XpSb+yXA!7)JSu%2>1WmJ-ngo|^t>c+=DLV3W#y=00qF6Gw{w2Rd7c9}_pQmTE& zZ01n$l>G!AH4`tlT) zd$Bw81lom!Gh*3*7V2G;2NLC93%TJh?{NDIVp=iZ6SP(YV!+D_L4NDAI*NiwJU*7< zp)aSpawF?@z85T4Sag-c=jpDt!DWMwCUbO|V7uLrN*6OqQw+vppc zmY&EXHnB?~hyMOze?5NLjOme4wX zSdZ%=%V^WlrxRWZ(dRXXU#;3#7&!LSAV4vT?2})=uC#hZMxE zC1+_}jXZv3+bOO8Df1xHFRVfzLliuft`Wm%v*%=t5Jx^QEZJXK{ zL2}giCr+^ux1jT7#h>w;s6g6Hl#V}A- z>VG+dCyTYDIt0o?W#?-*J{$YcPuSrI|Gvv}N>atk6&;#)Ryy}9h;aP=dYcaPFGct_ z?t0o|E2hK~5PZHCz;DF96}87X4+9@=DFWxmh8T9pQ@`}#GY*JG>L2PkO%T2@l-$4S z{B$taDqcDFzz3VGg*SO;#L`yr96L&LRrM`7t~I8(aW+6$05$aKCM+1u1YU87(2nC| zcD>O+@*suydamZLh;!S~8l5R-MT zx%l@R{OeAR#iGsu$ZZXeh2j=luIOoveOs@_*hLZg29^<%*cD~CQJdy`I`s)_>c!;A zzeW!`Hl{|LYv`(tY!eZ?ya4S$U{htS<~Pe-9%t&WV1oUj{SytTqWd}MA{iPq^ABkl zLP3>z_&+_668YW1I>d$zD)S;2xNQ+^tyqHmiXPorU)S=Pn8%6IGmefg67e2 zbE=E8x$Nit<$s8jS*d`xx)m~b9jBCmBP7%7$qRg|7SMV-4<{E>5J3Mzq1p=&yFJxO z`U)x^=G3e~tUY#D`NGpzw`@%+iTu#evF`CjRz=MQ|GqXUmCE+o7EC@i$Ej(}13(fM zJXOoF$P+!ck=FS=TZy@g2EbH~Y!fI3E|-F}KO%u)D`25DoSY7n9D!^upV2P2tfs5Y z+~*oUKlX*?DQ2HeNXBtQ4ka_*f`DGOmHMA?zE9H!Fej8n$YYuYIbBs>hO?AbU#Yqhy9b8L$O}>^GudGfAfs7BEV|-1@yR zt%y)L^@ng3vY(s-q4IrID>ST2%uw1>hYsTO70DW)?!V5TaoH-7d_WdNGat%{S(w%-PDS8%7U`wA9!x2TQkyu@k@O zC+1&zj&<0Wr!G-~DBj9B5)hm5HfLeiZ_!4m78ZAMTR*^t&xq|sx{Ox9lbbCGfc zYT96S*ZQ3FDpl^Fr-=aUr5#&)h6e@#;JdKlw2ro4T`{n}RwrbZI>P>!I}8V8fpvnJ2Bs zk6sfmVx~Enx0n6Fw;!rE(k#M@VgQl!nY@H+!hnp+yWgP(!F5eo>;S(XF=^v2Z4LWJ zbwZ*ttBJ6h0ic{CziRsPrRvJHNs6hqfgFAhI8It8y+D6F(8G+*Td|fYaub5LgwH%I z_o$D}AMI|?#obZddx83Ryp}~Euobj&CuEEf3;~%UmV{3cx|uK3Ytyv8NM2)5=A01O zLGH1Y(MRx3$V!QR*e;n8MA>o8?Nf2cI8GaFP+u9P>q)YNI=#_HQNw63)-857FlGfO zLl4ZoisR<=>UTD+O}CRwWfwH-Kj9iaQk0xz9wqUoI+)m1(!&Xmx69**Q=ln@!lrpf zZFYHRL>TZu;D(}P8t58c9Gmt)mI2Tl=W#Dx9gMD+f>Z$8n7WKJ)cEI zcNT~fz#eg2|A&!!$197=3tWM*W^#LQ01D|d|J~)a#z`6-eTBP#!w=UdVu`6Le_;JQ zBap9OTTvuu;VJCq8YSdRrf?@~<f8C-wp7l_#`zLxjsGTX zp#WALMLC^ZW??-3Yr@tEZEJ$T%o3G>22|C_bDQj<&BmwL?*<%z@4TEa2wy-VjpTgl>;lJz*QK%*5JsC}M5k#PxwpY|6!eloWSt@R|M=rFcL= z*CIm$d`q3;gidl`iGv1RcNPN?s~oYD9pX4AYR0r^Q(M#tz4ICk%z8+bxlq)BvUN?} zQ0PK}@@JNejoDIp+jLV$mGfb{T=zV7+BAWyuCL<#RL_AO7^cJA!cHKH4_uk$zmu3$ zT-JNU5|nPj>U4o!ZJC1nyrC2vqEJ-31N4m2NMR>8wV5aRjX~^7dmjbF!`2c-(s{rf zKxuvk>(`ijna{vwebrLrn`jA5Ski6S@msK61GDuhI9dlPwt?V3q4&$lDcf!O=|`YH zToowVID;Ii8zWx7m3N4SOk(_Zx&-t}w9=Wvtwhg8B(@0heW|`S&sqnDA#g|Q+n!H~h4+nmhxGd15SL^y@2jciZBHr&E+%Mb z5afZKptKIbyN%~zMH%RC0lSSLd98{HUlGP)a&y#8+9@t6>DZzi;ioS55lF4g_%;!=I-(+6-$MyKJ@SaXGl?{TgQShIk{b}N` zmhk>|sE1QU&N)r-i9J*&)%3;NZr?+xv5A6|L~`>>(AOEbT~cXX68K!qwlejX>6>9x z%i9Lkx*;K_w{f-?2RWUk+kpUE%{nC7&SLd6nP`!#2XC_fUR*^dJDHaaheX8gr}QB5OoG(n7U}p3I)1N{PR=2yzkk{8hia;3tPp$jM%-~2xTZZ@70mevFg_W1a!mG` z#@)2XpKqQFUW*IFX@yvmzkIG<+NU!jUtAt?5C|0;o%<}RV>676bs zLH&zf5scLg-#Aq#2X$s0Qg8;RFeYAeumiM4zWf&sY`#Tuwe=(vt)j4)k`{QCapI8Cv0kl2G-0og&4HOdy1p@}vHrG{R|}W0&BSP*)!Eh?lH1LNZ8o%9 zLS)7qsPhi)O2j>Q_^0=|TX884hwZ<||27{v(kP9695ze0tPfN4Y5$?2dT3(x{U=hZ zl)DRQ-m6(CFezMCXDQh+tF+ki3ILg7%RdtIA9;g#UC@|M^dt6x%6twt7k4@qB?Vy) zdR{E(0j13nIXX9P8M;+@H<-6SxH=Hq0mhh0rO6eG-?pkwqjc`G8Ea0FCAi>e{;4?( zHl{qZ1_uD2=Smkr3Amp1X@fB!Pc+IWY9d}=oCRFCQ!)pc3i!4}Kw9^h#Z zCVia38Z>O$F`8-ilxiR-qx}wBl;4c`A2iv`AZGP)OC(vC4S`eWEr?yThj&_=(<*Qd zwNT>^wVql@V?K6nPUAn5FjF>Y>Jzl>KjoC!Z4rAz@y-ywP?&{X2(Mu$tZlmwh8z)# zPK?M_%0jyz)7rxy)>~Yx3J+l`MQ?MhuGo%CLWt}YdhI%&{no+yXomR7W7EX?nv9hKypO@hJ`!Rgelw8Ge>(h5?u8(!}p{*%d!>pP}>rcqGT8NNc(k#%#)+|H;r z8&0UyW$Le;l{0GyddxyGz+=TJ)fQ6g>h2QQdK#ozh2Q@rqDDN%7Y?kDd?BEB4z@K@ z6*?jzgz!Spv4GN>ijt=8*|AGbjuhu$n*u+L^>U+Kd-P<(|5`}7tw6Inhrf7#Z~j9} ziJl_D9Cn{UAy$177K#6CyA?!fCk@mvGN3%dFuFN0J;`lVhYA_6g@N6RahTy#%on|j zE;f-fM`dX5yzh^_j{yKk?i(i#=0%TK>W{N%?bp2J5gw;aDZpHaI+w2?zlWMg8|!!o zSe~?m@(;;EpF@H2N^KY2Q(b{et%EpzKST(U8sZrHKeU;cUJROE(9SO^$`!BqZE zgLV}XjxQ}boc;~o@NsjqcdVWAPxrrPf+fIH6vI8F0;Tq7_oag#Oe&vOdI%gVnD|{Y zlxMdL#stAqS|=J z++ZJ&(<+KzbE*p3{G}GQ^KvYh#fPa5G^-AYA7Ef~^Ccc1r?oo%icL%*E^7>9Nz_y9 zxE=UlNu(b4q{ls#x@M&tC=}OkN_FU{OFudAxYn&6EgpA0Mb?LCU9_c{(u5X#iDJzD zqyL?2UbRNy;1Hp~I$0CvIEG6^5)73f%#)A0=GIxe02A3d6dXYI68n*iN0j!u-i66? zZ8JZKNO<|#8t%5=^A(tGm6KRd?mG98 zwQEf@qhMm{T<41a`ci{zba4|Ddj$I70pM9Vd2U@w->Y9mJi1hM(83?Lrlvk(hMSYJ zbOuMiDsO)9>ZhQhj)EtR<|Im98sU}HOrhZ#2rIM{zESDNNj;u4ebw!P4nb<#NMH2icu`ICt|gB(Hp7Ky9fFh_b$J52oPCtFw#O^4U@0@{&2iO)Zc~-M;0* z|2F&xUi7BA9+$z~HT3xxz+RfEop#}36G*uTotvlz+cEt z_G*DjJ_)C)`WaLPcx6FIjnf=aY?oY*KzrU!vVzl?P2T4jM!-1~yb~qCC)byk#2U(rmGuhMR2`%6C=M z!4=>g0*sw4Vp+Q&TI|7fnd_-SoZn}H(xi~GFBDwDHXKmrraFqjCfi?Eyok_Ww<5`F zTn8EItdo_dvrzi>9{lw&dNmGMC<6xYJ&-);SDUtg3|ct(`YrVY?Ie*5hz80GqKpjq zk=k;QV>Qbn^!3a?3IFZLl+OES8_;WFNSsR1%~7>m7gWCYX+ zT_>0{s7bD?FQT&(>-;?w_(^XZ@Ac^Nk+w+#45Zd+MAZJz*mP5h#1P@9WNHTt(&WRM zUQC;|zlZPZ)}VyqED;y{MU^bFyy0vgu1(Me3Vx8UL|vq(3^L=*3h@}An`ZJ{3h<|i9u0@{1Y&$@^P?p^MoK5S>4 zUmF%fwz83iMnJ}=Bgo6x7QHnJbB^3-p$&n5QEZuFAuhB)$9G4QcurdT3}$!8Ot6t= zB*Pj%{*T~p(kPa`;=N3#kSUgX<`{XBgqV^bZuaM!`29N4x}qcFAZ|<1+2-3b26%O= zposIK#r~xpR>pzU>>=aaCe1rJz9&b1i&;UIgoliTaa?-br^1 zg)YUh#;AuoObdBO+p2p^rOhaM4C4rg)rK@vjp2X&!S@SqF$LOk4^_$cXxukL(X{Ye zY(7ol0t)Gn$b~c@VK1#2dy8t+K+er&S@6=3l$*}gVP4sUz)+G@geq|+S(d+wF~+axm!aH> z4N===6U|^z<)|@P-2e5c+gW24xqfjkX|ax17xQ<}Nh*1U7nzjn%x?XlqLo3a5_}++ zFGfv*UbYbOS+=y0%nBJT>n>GQ;FB{n>X zL%0TV^iqrBGYDK_0J8&!p^XnV zl7g5#rnNh^fzLJ7fr?Wx0+{lZ^7 zyJ5RUuq}?YwqSiz$eY@@gGgDl3B3&h_U1h-|0RVJ z0mg6<4)qqF!|9Y63duVUyhlO|tiOc)oyg1nDu9ZFQxL0nCpId?&&IrbxzLso5lIjJSYHKVDDmon7{OaHX};U$2Bfz*DV zW{~%+1`eU(YEruM;L4UylZ0pXI+{b1-9sIZ>jVtE%1O8Zs5p$d*Uv4&i+Z7nY?vyz zZX=ulL4(%$ep8_Q@4H~nz`lGmKq{1m)nCtkuOr0j@0vz^HR*~x$=aYP-pdtO7pMo}xC>j^M`j43h zy$xIHN4Zzv)G9*PIh^`;zs&s(&*^ZxlkQy!YbN{bQ=wMakc-%j_CR@(F87a)J1Re$CGD{cdjDwzzElG>=V4?L=2% zFu~jdq_7j(4v18Oi2#{Mq)#X*(ue4opwxHlPxF?rxt8F&CbHy&zU5Lac9}*jqK_Rw zFJ$_6#j9wC9=LnuDg!cdzCO1vfh`)ELbT_D2IBzi2S_|wAQN{okzId#JW`Oq%v=d#o-W432}^;;&6YvPT^>i-YrT7CxRod-5h=paQxqw@Hh; zWFlTrHHiUgQJ0gkc>w|c3m6Qo<%adTyna{J_qHNG<=HH)Rsm*46%h?W+F$%{DOhBL zOWNIV~pLmeGb#qf;5)GC&$G1W^6l}$v*;j=?S->sWlGd}Q3qiyb$ zs8KMjqTmzC@Fd`lb+5v1Ln9Pw)iMBAHBsN0^7c86w+dv|Q3fp8jRA%@sL$MogCC*_ zc z+|;e@(I?`r&UjHEY>L3C<`-Dzd*1|98mFMAk@N(08)xLrN&_j9i*sE(dusROpX0Yx z=HqNLa<(XXe<%}}s`wv74UPEHrc&$YvvbNm59*2Sj(tHnBH#2k3YlE{%Bhhr4+W{8 zs?@{{G;UWnUMk#+AG(H0tq>+7`AifmJid%lAaC&q?)>c%xuwD?!d%HDg3Gy zFFZ-e*Smv|XjAvEP_7)M+$zu?&QQqN%(Ls)RVJ$p7rw^@6&?smNDxoc?!jU@ZA#Ay z;ZvSfr4{D)!}?J(%l13!$LuUS)d-6+*Cum0PIQAIJGQjyH)fM}B z0!y3pW69`J<z1)q?CnKGBzDg^1A59bY`e?W+)F_{TD48gT8PP92zxWTYRDgRJ=X?G&QJ&fcuhy zx1bQ6L77|mb1K}sES|H~#+&?m!F+-Jo-LI`hy!Z)=q#HI>bamOT4rRYjVT3Mg-Q#A zsuWK&EDY}v2`H+2u>>BUc@;#tW|nchn4 zumvn48{AsW*b%)4*w%|HHwO3)K%P6neWR#NtJ+=}RR}~nCjrD5iD(;MGi0WjB}`JO zCYN)ZyvHRN0vUlS#Z;I9^tHqXl3n&RP+KH617s-Yz6?x%5tzT6A;+IV^`()mL~*J= z#@p(ZwUlXMFOVCL6S%6UbKn>Z8x+DRKZ_>Vn%5UNR+?fG}|*cZs)7VO##anvT{d<{&z^2ONFZ zM^d)gISMtu!U%bqxOnn%rC4)rkq5o*@a0QB@->7 zPM`DrN^GiMDC+e?K@WvcN_w-;!WB4q&EzKE_w9sd(nXxBkK8c0@qgP_m}9`DFTxe_ z_Jr&-*K8r6M`T?z#h3Wg<&uIKNLTd0mW==j%|#07FYP~JlMs1cz?5cZmWuN#cE=LQ zyY;OgI~tzC9A79W75o))20Jp;N7Ox%*ml6x>6 z=~sE=rp^tpv#vwgESz4r6B~K>SZU5mgyD1f?~}Ty@yH#cPRT)hFrD7Q31$3Z8iOSf z{s+iDrHWMr7)w?f#azZB)Ml^TcJ~hbRHFVj+Q{AixI#qG3T@spcw4TS_wqmj3z?o7NBWvd`4( zkhTRS*5$B%sHwo3dfcnPt z3s~3(-e?`X$6sy7<8Zc_9KfD3&YD;9gP6Xf$$weii8`{#8u)@R65;6xakAr2DkXR_ zOQ%me1~pA8*j{qn1BN@&e=WMo^W!`g-N)5uz*Ov3{v>F3p_UA47t$&n<>4LOj^%vn z42j<&v)9NMZtG*IAp^LMc3#|`EJ2i6txhHTq7}cI~ zL;tC>o0TW(mf|VxRWFE5OyZ-$^YOqvGc*(ucncN<{)mV5c7$=_dy$k#C7MAnL9-1? z-5tY>S8Ac;i3!S_2S4BQOEh&}F{Bytaa=Y8AR9SeAGsZ(AGA8EY3C*7fV6LS9r4y^ zW+u~E9VKmZUxr;O@&n{W0(BA422U^2U{XcT#>ZXA-`$kT)A+o##_%MP)sKe0Jim#8 zkJ$@IXuc$vq$Vk)+eY>Yh(BODLQL%-UI8s;nS?> zdfawyp$D?)eDSx1#-jt86;EeYa0Q*A;^_t0sfE5T7!F8A3v9CuQ~jz4ZZ2iET}6+qP}nn%K5& z+qR99_xqo7y)SF`s_v=^8CxcgTp9gb^U&06s1PU)nA6SoTwC)O?&CgNn{n z5e|BBo%caGT=uoYEvAY(?1<0j$jjQ;y^76i0q3qO7}cBAe?wb1 zxmKw(5#nB=4nqAH7A{Z#1xYlQilNi?J$Lyg7QtAoVbVoC+00NMZV>5tM20w{t3 z&>H~+;>E9L2JkjH@JLR8*_k#`1WTI-NQOZXSzr$i4}@aS?rsjwff2nF8c{i9N090s z@c3Q0LT~%ijW8BzfIZg!r1Vb8y29mfDZ8y;gy@TlF^5grjt_h6+pYsS(gm4AAspPn{P@TR1 zfPVSP$-U1}j<-Po!I{Ld$sjS`aKn3}HGR%n4mkxijaNHmmq7U}W~A5-D>x(4X#vs6 zNv%PHG_#0!AEs zwS@qg8xgMt7#sRu>4^MpG@tCqpBL z`*?ZDC=OIoK{!fG#r~0=BF_bMijG-n&s}0hNqrxG$-hf$VAYw1B#9y75y(anhZWvC z1??OtU0xBsd>N4hP-%Too(n_lh`Zd`O3ENLj@Qn5-9X{=P8WtBNS&A9I@X0HQG}hW zrLHH6eKU{9+lPVe6;gNxZ z|M!ur64aKsyy^ZF?r0gxBR0N@7jw&y!-)O!*H;&*U7(;SQ_EIl3``ra=ZpA-%8p2tL+ z9|#Td)EC1{^lGoeIk8+7`MlJwxA9QhzA#vtBe`tek{3znyOffYw*{H} zJMHN&L682rdVcpTkkduX=;jM=m)?-j)NJNIroGi*D(j z9Go{@w<~JzDayf}O`R`$t1Osk*fp29ioEQNP>`K*EF5AE<20KG~Z|M*wT zXqr2BS8!1_ z?YH{zErt4bc=3a{s966uQFObM-!R?-rtmFz1=2?`%<|G!gxx%u##R zs>4(3jv2Si>5jgqa+KaUhP840;FH7E@y{osfqaB#@*I_B8Bke^LMb{h5#Obi>XSZv z)d_&tt~yOay()CXE_ucZb^_+wu&{+6o2yp_;43bTXEPxaSeVeGo{Y4T>jf zWM~uiyMaK^7`)%M9E4e)PKo25t7bED-6is*ij{B?ZrqJI^k0Sr@VK5Bi(zcGvn@t6 z@Ym+S)YknB2KV{)19_N&fyL8C2R4-(Y`hUPq@IM&o+~XD0ZUZF!D{!*55grwW=t<= z12nENg2L93q7?fPmpU*?a&&u}EX?Kj5(kY&B@6_@K4bG>&K75f|- zUQnd?+ArXUZ#g@k7R{w-J^bhcbbP_u|8I!b0n7l#uMGBIF#v$%g<%l50p2qN(B?^= zAO)26D_@sRfxicK1XjC8S$1%0g>8H!>iJL8KJDTyKZ08S?IOihhR|GK1UM81oVXLckP(qT61NZigWU?CbUQj!`MGz;AUPYp?u)>k2D;Z38F&+)aHzOG!LcdS%grpcq$&V)#;xONKJt&Za5iNM{#igF;L&&Y4HnoVF75CCD+)W4Sw00DlaAOrxwJb;&vKxX{~GhIrmDtEGIp1iH0%`FWX z+uBZv^;k`TvD3%VW;seNQ2lr7eTsrElAQxH2E$kwRj+$zk!oe^X)V*V`|m$_YbZTjUw0n$^{nxE$_05u6Jg#F$9NrJRVj~QxqDM!BNPJ_( zi3YIn&Jg&s5FqtSOp5fdgO-M!(<&Ju9+?o-!XyYPtL{|}f<`TmoFYOpL-=CK>ixMz zO2cVY!WgY5xH1m$j1K~*4LFX->DxMo+V+|-gqb7oS~YRPEi#X-E(+w|lBWKWo;Yak z`Gv$W5b$92slk@nNQtd=paYYR<9$pS@7Ga4XoHU}ZMF-S2|30NSoAn7VDnp1ySJ+L z-)Ju!2=RAGTB~8(YVw0R8cJ9!CrQ(x^nz6B`mR3 zz)Y~O97}qM6DK7A$#hto5{oTaDN=Je6Azu^d_|0mejMVJErEUg(iNEyw6B)@LUi4D?1{Oo|mh4PJ(C}1I1Zm;=PRA;3K;i95>*V(2* zy!mM1EGc6-J``y7y^`YWZ!Us#w#0p!XHw?$?%$3dAld(Hd6eZhA+3}#JtvtI6~x1N z(O$O^VxI&(EgrWWeC>3FieashL&Y*e(9$$?<52>Be3h-UaV@k;O?QLE4`nhvDX|=U z8D;f*uEW$q=lVX|$G(tjqmqG*=ZV+LzO8lv#I*i&R}*7$t;@YzGUrcgt(5fMt3YSO z*#*qgf|TihD^S@re+!@qGw0vuDd4_@DxbFzlyy{5NW6?nL%TM0iMGt6B88xsOk04W zVOa2y9B8+;`%x++3%Mg_tA-amo^!RcF9v`Gmigs#zJ7xmBd5-=wd`!i0(9OC(5yv8i_i>S@fwgxYu>t~i~*guKR*-k?BBo0O+Uo2Zc9 z<8K&UqSNxKARm7P{++w?(;3cSD9=u~O{aSW!TA?j6)V-%L;d-uP^RGB&l%}Wl#2hw zhAeMB^7oJW@A#_@Eo zy?=+*OOBS9d;=9*4r3#o`1$ohqfQI=5Vr$cx};nHq3J3!y0VqsY#$7uQ=Mo(5H%H; zxI))Z(|)k3+3`7ut({qv_hIcsO1J}=_F0p%Jl$)NyuHxYYJD~H7oqfp%pw$ugY-^J?qHl`% z%mrhM*bYI9{bQ-pCd+Y9rS;GnR(rt2t<9&(g=7o7W|UzE|5$ux&JpPgofML7x*Oc~%UJ}5467R07rG`%L4U#>oay-$wS z=4*yJe@@6le-weT(M!ahR?S#<1W|24vbd-zF8m!4yKH?k;Sp*WrE5h(MkL{o2mbUW zDf4TE;?$bso0#oIe<)40a$U0Prc~lFTsWy>@aKe6fu_Lg$64#M??s zCDkT4+yl8!w)W&Dql3f3KRaP=&?^J6sBub0o?p z&C7!-C&BPe;gKC!^*MGH9RkJVZBJ6&Z(sBmi*z7bcZ^Hby}e7JW&?%v$6zLmxt8cr z9McwQ6}Qn|aoeu6gptIn;|CeZN0K-!`9)-R7i~6V3e3sLZxO(oE$lcpgX*XdePI-D z$<@I~7|gVBz%7wzXA!nMcfk31;Vk8q=+Xiu2nW|T440h}?&BsTOwp*9c-)URE@s#f$j7fw770!t3nk&H9r z-m((o5-lRmK3uh|92aktf>V@UP}W3SHPXP!9H5~Y3Y-oUVV=i597K@B5lZqe)_G3t z#N=CNVVy6?fu18qJP?yYDx}D>1F=}T!4SkPJBe45oaTg-cm=Z4^d2%<aY70HcP% zD=m|SH}M`q-oPeV62ZP`-k;^>4W_)r{>CA@a`;&aJ%4NnpwUc z;e@>w!Gs0io)u1HxcS1ZvtZT7Hky%ZdK}b40W59Rb(iQhWy4-sc zdA1-B^sP`=aK`e0UC1v4<`dji+_&&CC3ovZ?WA2lieHF!VVm?8YM$5-S<9(?584fn~&nq62u7*-x%$))@jJsoakw+ygu5ZpStxZ){;j+&hc zkuyru1mN_-f9a@EIR-r{k%Cs?vflJ^g?AE!S@$o4R}%7V{vXo)k`8_h>Vn0Z24iQ!0&dJp%+g`4&t2sF~$N6$H-P<(u4adHns;G z3NPt+uYesPy0D1nZ_17am5AhAf;o?ye76%W@N~a2ZtD#1*gnkmTrfmAAW+-^1Rlq7 z11lYv1{xk7LNL=VFar{Q1RCA{+J}M=@o)Qh&v3;4v^?~MI|bjC=Ovb_!DG{R=S}o^ z~o=H==9_O;Drc-0IM z^gas-ZWLjgX>t69sQZ;|^JIoBs&5?5hy{0*NHgJQ7WeSd1ag68AS<|sM~Sr{g+6b6 zci!BTAz&YmcvO~UDVF0oU^=eXd*xdFhozgnWS`trt(ClD<4@{lvqjmhK(PXb$~N37 zYUZj_I(Elg=I!@GqYmw$cKXC#?_?eO!8j=ZeeCisCRYB-*PcUUtZ_Y2BZDvbn-Dbr%u ztm<9_U}%Luu1Y~qrXrCHs7s=T%FL;F-{xZ{i=E|f&Q>%abpn9TCTE^7q*UEPF&B#! ze>Z8y3P~|nyfND_M3f`o$F?pOiXP&ZqFYtdX#7nGQxy3h=?)MSMX8O|~}mdWA2yTllD^FwHT ztx{9X+l^0SWUO{A*{8FfQ+Ly)V?WNGk&6RvZivkZ${Eu0ieLY|Kk7rxlTYM%m>=hS z90H9Orx0o9T`X0uy%*Y&taiQpO`s z#bPP*a7MM-ax&-*ezoDRCS{?jBukqjX?*Xp;y(k|;#y~aw1mFpceUH#JPXnrG5VaJ zxf@BjTfO?M)CQfotU-7MtYxxH6Aa#VRnn2DG%+OlC8ctHB)L6veBC>2Gpy7h@*Bm> zOp(!ODf`rj{$W3JVMiX#M#qF9NXAPqyID8t+)+C)Lrqn4sFg_J69GA~mGgi~nOej$ zIG`70jkDd&soYS!VAa6~kxhY{5UAp`tl291wJCt}RM7N8kCu2Lxm)!Y?TqAZBu7O{ z!CJWgxhf?lDVHcnLoVD?osLk0m5XY4D`-brN3?a!0<9LPu!Y|miGZj!YFJ6@s}(4% zrAgDYUB*OW7okwj*S`tz1WQ$%_)2z(>rP>p26mVl)g1bW74o)blqND7r#<&X$hw%? zE$6c)T33&cNRPU!&N<*suUO6CvOLe z_2c_LAe84v?&St}Z2A-&kGdho!;ZlxU5Ivu={(fCGI1&V2}>oitv3w{VpD_tsNEmO z1J8c_c^Kd*RP0Kx)(iCzS?YxFVCgHJ3Ww3H9_RlUW9 zjSJ>{_9gav6?0WRf?07wd6%3gJ_AG`E5Z#A<1hFqbSIEMlXWxMjF2)z_ot#BF3mG7 zn@&L{=Vt3~y!`2NK7;FwFtIL5bSVv+%E@U9DY)xW4srvIxl4j%ou}*1;JEi?3_h!Q z3sHJ8bQGq`z$q!U9*+^)q4#a)N~yD7C<^xHuOaw*z5K`PG^#rT;C6hJED0$PiVcf1 z6$<@bCpSbF1hJWHHYLLq2y?;LgVj_T=!iX%+c!KpXOfpzgfCS%G)pF+Dchsz*)43U zoeS&A{{g682m=DqKld^ZOU-yTA(>fK^=$u1PGdpVMj@l@Bl3>uyWm|D+8x9&x6X&b zq9i^~w#sMr>^P&QOqkQyLzR105-0dYP>&`f04Oiy^30E#AuB$m!jN{`&tGUaT^}SU zN}qsRuzVjGkSG7MoG2B6Y)FOoW%x`#hUihv>A z!rU=jqZjM}&+Djr`Qx!1i5g>3Afz3Q7tRsFjd%GJGy{lSj?hGmx`3{k`r%uLs@We* zn0qNGKbS~=q1$G|hD!xA$}RNHP|@9Hz)0)l2jw)`PTJ$(Z-dYc7c-^0HT{G_YOzG} zUKd(-A(+27khsmM7IJ=BMUIIX#<4t#xxz3DugmMG^O4#_m9e`uKcd*lE)6QmHD+LiaGWF$eWHU_if6+fFHnG`tI+S1_A;seLytU9xy)uKm-6F2q*vu6F-36gW=0NuriGC zJ9KCEFI+$LmbrI1pnw}kZunQ``<_8o_)Feos^8#OP2xf%8t1@M8DznS5*!g`9FQfp zeYn&7?l)Y{z1wYR;r>1L_(E8`u1eG*P4YfCgO)j}iu+iV6euGRTM?>!IoQ*KQ62?j z;S5Q$8k4Z#x_($53biMEoZ-#3QGlg9|J2?Ezj%&$kt;E!aYGp=U&`M3K`6;HK_C3d z7y{n_J5+|xaxR@qHedk5-{%zr3a--y!G@|9T|04}z**I;pW@ncOb4`$zpcCkF7uo4 zhn@6}&3*YKU^Q6=KO)uqO5mGc?P5rvvnirMW5P>};Zl1B*$tVpZG|T60S>%db(pfj z(#|i=O!Re1b4gH@2})IC(eCSgp{PaUGZL;I{yhBEZ+Di-#m=Gqj)1LG*<5}6xX@1L zVB{7T^u|)nrv>pzgII)ZM#8P(okZ04bC;^vfLB1KhDP&fLyxh{1sd~;*ty)$ZCoz{!^5q%T6lDLX-#QfTgo{_RZk(s1>XTux9^oECd zZ$V|(wnsFtE}tlfMU?(LPx6?!i*rO^IioR3yD|437-P7?c-c9kl#|gkFVpZgyuLmg zlzt|RN3*NQsT13>xNVxbPYCu^lBi#oV7K3~Oo|4*@A!vbmTq~AJZW}n0HtUp3QiO&x2{V*mkxzX(q z%R>gp6V7MGsDKOZQ5)!-g1COzH<-Q=)*sOqx|9VHd_J! z5$&lx`r|&%NpR}1a-4MBALxg~%;9f~Uq3%hL`ZiRDarR;dGwdl<+PQ`cVtDIa&}=*|orNHpy1 z91cj+#qslvLFT_?pk-w&^jMV_V*-vnh4YH_X8P%OPGmw*CY*68Gh|K*Fp`1Pb@)wn z#kZT%raump3h%D8t1~W?5Co2#+$IqX-ISci=O6(m)`-1#lat$zry{B>QVD>nh)F*4 zS8iVor~0DeM!gh7fa?{Yhb3V!^|@~ev_Q`EI8%s?iQ|$GZ>;N`SmNChQVYxjq_M2B zS{B@kTR7r=_mccUWFtYh9yN?m-I~kma!Pvkt{l;ypi~BFDshvqIx53>Ku7uU1VD(p zCOZhN#$)1HzG4rd@JYN&p~WzVJi7F0B|UJsuQZ0e%A>0MGp04O+e9=ZI&aDVc*NTaQ}2`}kaLYf})|Mj!Mn$K17E z-#+Jonik08&@^FDQ5?;jHz+ZYHprpTsQ13Es&d}*U#1QN@;Ai7LgV7Mtp2JKtSDes zn{KA_m8~e40c?bX{U7muo98~v=~O8+8TvR2qvOlB7`+=28}C# z3?$^M{y^>OnB)P*{#D)1o3|yrMUhpbe}qaqRUPyI=m?o*+zfa(=ZK3oHtxLABR!Ak zb=sDwh|!Iuu|P(kqb!Vw4_*-76uJPl0T;8*|0_TdNWR7kNX>26y?~;21jTL=(nQcl zE`Iq%xXDIUdi!YW+Yvo4Kxg(%p^97fsoxJbiOY<6JbF z?c$p5!j`)GQ~eXnIHZwmwx&g3h$`ZCHO`ol&``yGg#o?c-xe)U8AcNwu83KUw`QTNTC zOv!-=G~2Z?k?B*^0i`UiuMx^3eMS)nnb0#?1RDSKdpA%=4Gl?O6``(^6J^Ffg#lcx zH_1mapVQ^4S@IR%E2F-SrOY2=k$$JbTCydDdj5=v#jVkc553+jxQ^^|HeEG5m7D~w z`^hK*!NKxJ6dQLmzJRvB5-X|^CwE7WLG>rg`P2%{K-29c$igxFA|7XL=Ce%?l_j>A z+Mn}2#`TnD)%U7DpXeM#fr+h*pvERs5kdyx@6p?Ov{nE)$Makb-lQ}Trl%^jSWP88 zs<}v>Y|6QKcJe}n3jFkH!sOUtPO+Kdsst6Sj-iF_1G#0s>zEd=N57ko43ZnvaBMU7#+0bwr>> zs_4wP@Z(R4WrNgcx1-Kn?O|~w2%3lZU$1SR?JS`H`ic#;`M~zhsrQ?ViI-bA2)a0^ zY6cn`OG+Ipq_q-~Ek^pYEX0n}Mus^^I8|0Nx-yfS_IvP12eDq7&fz3oDKij3iKj1- z9_erp%|O~~0P8PhctOGJ0|sEGpg>RcAq-GPAd)u2Y7V}P^Ab`snY!P-#E;81saCG) z@)Z>+hlotrRXdC8Vm_um(fS z)ZhCqGl|q-%n{kffEmdR{;~_ozv#c1F9}&~c*6;_{yK~ry1coxmkY?fZJ1C{H=$(h zCeOlU+S%b|GuY%g&E}$Bw2S-fU=AbJdnl6ejx3X(kwj5aovQb}vZB^{ zsL|_&^0^OU1E>Z!E8p|CA3+-s^HC8g%^2}CLQPb3f#*WBf9bvIl%Vb*+WsA9Y2$nE z@Ff^UZ1B9mXREPAA%A2HvUBu_MIIR%$Hg{T*izy9XyC=y1@BaFP{(8xNb?vdgBHvd z?DOwK%iqPVwMl_MjcPdT#U=#~KY%Gj+w87aGTvZk7rlnQ1f4sh4fTpYsBsXO#$ngm zz zfVx_C^FOExsgWt*`-^Kz&N=eZ=NMKF^RGB#8=vGpKsQZ}Yfj^7n>5{zZYH|m!gRYX z7x~vY>4Wa!6r@8;8l+Z(O!OU6qGoGI05JBp!Lf3;5hDFbUk@DxF!0BKC<_BT^>Em| zt_Zi=-x_+WaLD2(Hc%GHqyl;E@bK?`-EhnHZF>YP!-XmptfBZ-$eQn+O$p%fjDSa+ z6%O;GP#ZURK|-AEY&?qE)x@92Ul#7+*e@eS!BSCAG=oe+G_%y3+wq(eQ{R@y|*{eFlF;Zxetf0)|}O6$mOn)pABo!NajK#~8R{>=H-$Y`{u zvkSK2La(;9&cX;o-^pBj78i@h&gTI3)4sl2MD`e84(5;tmJAX4Nv3=pu!&6VW6 z@6{qB%S{-;R0RW&lMSnsL7XE1?wH}tS*3OAd}SgCZ$(G(%ey*ts?1gbtO-#!jnTtCTHczdx1DEB z_8(hNJS4CPu$)3#Ped>$zlo5V;v{}Rr8~||3S`Uq&IwrvJ@FLlu!);rcFzvnc7Y>W z*6ryAC}^DoGu(dv9Euj&R$c&HPKoM2qL;`wWXx}!V(^gc9P%&8#EPOSdM<(%f>*rT z-^PpisQUB{Pkfl!)LhbUcVx+SkxsN_GwPo2bgucn00xG(fr8H-oPpkUuj{=AzPDhU zmUDWL16b#eSS5jO=-B`Yt$aS+Ou!)-eH?Al4iht^zD9e$B52Y`hOr9|>TnckzlzjH z$a?})nA37_CNAw=(Iif?U~5KEp5;Dx=FRkVd7N5Oyh_Iq&D&GnZj*jq0&Vj?_r`S_ zhgzlMnr>N@4WgnyHPmta%3x6T6+BT@H7v976lxi1>j2-co%CZCKDd=4rZ06`$uN2~ zR}eM6nXP{7*2DHQ65n=^NaHV~GQ!@w&_ zycw`ZFM0z+Z(J8EsnE?{u#mh#XU9{?qv?$;{S zQE=T=9{V-;0dmF9x#(>sd4iqM0s%cGuX08`vF^)nD~S?hd%ZFWb+~-?C*b;I<{Bf8 z`@I8qpjiK1Mrwo>Ql))DWu(k37dyK%+P!5(s-~Npc5V{3EYa z7YrAT1o?&{>yRTyh^yf{3+{2Jc)x*L%zlj}KuC{KH;(z(RNT=BG1_=?li=_GvS@>Y zUnUW12}36cAPnrs^aeqJB>dmh0EPdTbz%nhJ2=4j-9c{JeHhFy%-hO%G&iV;ZAFxX zG|E|n+cSrY^z7lcA}nFJ#C;0hx0o~U%KxK2LG1h)-fLv{WS&(85mB;!L5`iCnF3uv z7NN5AFUKTHL|K?;zX$SL|0DLq3B%$}CbA7?@tgLEOT_eusTr4B9AGJp?{=7XuRsdn znxZ!E2CYRt9O4m%Vt4OWeSwR_0FP!fs$!&4SzTMgOJJRhd1_H}q9PurSsN^UT~8tf zT(ogXy1Wl+tc*14_V>m7ov&%s9JQgR8Kjs+&i!l3XTi@jW93gW%!i+Lr{&DfWZ%|) zp;uRPeyFb(Utn5=4dpGy7E3IHj#P~ zvc6l71Od%Jq{*$`<)>tzms&d5d*-mFj!gw<@uWqEMK>ysh-EKe3}(J|82b)PN>aI# zShEc_kHsv~+IFvGz}vYfg6M5aHP>JBObC7uUsF+HMawKyF|J`K=SvSy{!9Zk9ao^yo zbj`3

    e}JA(b7fBHD0{Ch@#ctgeb#LPX+khVzjYMATOCd)(cEnf+%Rxpo=Z`SNSs-Ba0O%Q!#o!nxzzAK$ zcauf*?w!vs*hSipW|0CLxz8!wy%Mb+!TJqff2>CgaV&=|9kMwXt{wMdkW5V^e~x}@ zqQm_j;W}DpDEah5g6^?EOVG<#7`F@Q1Izj+_Ss$&&(uT*M;Z$2W0 z8)a4P0@eg8%ht6nBC9G_q}Sc^sDr=*m^SB#R;>Q8r1z%JePU#GVO}nnTTJE$#mV4& z9GI{3O?BrrT&^V%aO%l5m4Iu|&k^@ORna@=ZB61#fpvk7d_v8>tfp#_gK_FKoq51R zeJ>O*^U)$?5c*fuR;3z%?2xLfG4D6yoEUfh#=UQ+kSBZYvJ%hAR#Rz;U zppqAs>ejbSkL7SdLGe}J!*i37=u^WHM=c1u?B{QhLnne-(J^eI-6tN!tUls|Yac?; zXG1nX6)axB5yZlKI@Ia!p*XfT;k{4hlez8r53wL;)0w)P*n@r!g!I6{g4ODu0*5(a z+186x77s_2{n#XanAT%Vlfo#a-shI9I@4D1IJ6-|-m>CvpVEX{5RTybLrMSWG%h++ z1phF@MU`^6)LCU)3ZwZafiG5JwMxha16dNj*SYawZvnyPA!<@pPiXZ*h6M=V@LjA$ zVu-6CZ-*c4HT-KIqjDg>iIAQ1S)9<*QVp&$yO_+1=2`BQ{x3%C{|4VX7%ROLqx zMyqH<8I1)(x3vr7D|o~hnJ_l!$Z)@5ddgO6xwlm)z6h@@p+r*j_XeLafO%M2SU}9# z1o2jc^PdZ`l(;!T!t(z6a0@pO$G>%J+i)k0Aoe5DViA83F`dfuq1p0a21+Z_#116c+E1( zoiVN8+5(#|o9j^BS7XwiDF}_qsTSujEP$B+Q9<7r2AWKlM2T%yDqyx8$nEJ6SjJs) zIFASZO=aLXc&{0v3`isrzM?aW;$Q!VR*^TB!%Op$1{uVH@^v+-?gy0!FHof-{7?Q0 zZd#~51)ZJNk-N$OiKMIzN{R*O!K-*nsC{qO?ATfz!6ypAH6^Apo3k%GZsr&4@X;2ewiG<)GsY$@qzsEJr zPLUx~CJgmTPT^&+WNtTT4^`_dzpyz5wRDVWQZqneXL5z=iCTC5FoD=&z<*fl&evC) zy>4auK(ts7EC4!?(35`{l2=3U@G(z4IRqlftGk``9V-ssFgREq6GSI^tddiQG3zkt zCWj}VLC+C3njWC4%d!tYDO?HLwMvwz;s;2zWDW;EgA7*)AJIt%^4OgiQNqwrE4tog zf1M))M(`E;A9q3Ymnh-k@)|YKWJT!oIhb1y@h}N_J3My9L}4@Mm{(S+?*lF*inBZm zAMl-|9C7Rg5>R2)E_YV}?Fw0f7aRU0em>9C?T=)SB$#V_QpY2t_A)hsFyH|K(OCL0 z`u&pP1^!2h&x{&KxwvbC5~s8u<@(;4@-Nm^A9wQ)#o3a8S}R9e*6wbHmfiOZC~8Ne z6By5@EnK$~(UEs?c1wm5lKR=+lrP`MmGS&Bqh4d`nxaf0OLcRaKq7H)jR3vn=OxK( zs%-2itzAvnZGjGef^5WGvIoqwtn8|6dY2-=hnST(4y=)w`#OvW8`AukLm4s+&r}v&1(Uf9EUZ-Z3}3HmLO8_sQi?~VEr_g1Y5D`ciW4HTdhMl?~dPu2Q7)=O@Hno^z8*b(~jHi zvB*%j$Mpsz-5^Xc0n`4QT7_e=(V)W9UJ%*`8YAx&B}bHdVcQk0UpMcX)jjWse7P7n zK~{T`kJubV^Vc%H>~0m*+~I9g{CsJ$&H4P@_Ly5grVm=X7s%xpWfm;pua<>wL1s7- z@`t~Ti(5es94;qy1DIpPY~**nx48=;Aq;n}HY5;QQk`hh_l?=EW^ztO8$y0<07Ne% zm>xWaB6~&DZ5GUSJ))9e6Hp{%(Y+v}PnG9Z#uOyR{x(WRgs31PJSmm?AV04`>$ZTg zTLf|_IW+yw2g3^Lhjr058Lhb;@GyY4?17g^y1Vm|cL^Qbh81UFUb)xJVY(3m zMZsgn^$G9{HIr)q%_}7(QZ6z{>fc-{5OeUdgzo0YJv{TXOF~fx3`ZcEn6b@aSFlJO zRvs3st{rNl(tb=3w`dQFO06i_i;3eD6*=OUacfF;V7;R)UDYw4FZq|HgrL+SbfAX) zeqhxa8>vaIu%Sf{a-P2XK!wFwi04h*@*_1!7zGgDq*%7OtW za$I^I`a`S2x&XS%Z(Zz_n`mqTh+QAt}%R zBH+UR@AeJ=fRRs7`rAZXsD1Y3bLEuAcnjcPKG9Dp`D2eHu*F`l9?=sGM)#@G*WwZu z+zdX!w7a&y(<-d1Q|SaxMjQ|EK+Za#vcy{T=L=b403^9Y#IvQ0w>%Qp@$Q5@onS5u z?fhSE#u3aW+!O)gC)k8FE(SWx@12Vv^JA5jKog?zD_0_*mH;6+n;9t1^lIInqY4J8 zIDK=TtwTZAOpUjUWy0T)B$%g*d^8B+mBm~`t?3ATmE&x~f)?eiO8^mA85D_7Un=6~ zshZeWL!C9VZ68@+6rb3FL;TPRd053QC$(X9-PsL(gMG8R?*5@jBJX+15K+Xyf%3{b?pWXqb0e>UiQVdH zOgXdZ1n(Y(sb$4469#6h>=))pcmEYRR)^o6#lc2$*J+XCj}KOsh+X-NJ$&sj(}~J2 zoD&}&&^l_R{6kQ9pgPKxc7a)b`myKz2YazWh4@oPv?7o#qxkJsXOQS{6WECFTfddT zMR-sdk=LS?Z0&P8J|6Lw*!5@#e#l|2qf;ple_fC~no{u~WC8ReLC&O^;B+AI60;T! z+V@IoY~9D*i`E;vfsj&0b`g5&4@ztSis^G=LXkkM!sv=R4s;Xf*@_lIO(O;Us@FEB z(TsFydhZsG?Npu}62WXcq<`4R*CzB z&gF%Z~_82jUIwuk1-N$QpTpoH#wphSgno$>=h8nBOAq`Z3f_2(*wfG zLod!>qo#F8rCslVkUEC1ulItNoSW*2<(mZJZyc>Fh#r$^0-B0j%=hf$Pz*D$QCn-~ z`?KR@6oIO<9B|Yehm-0pB2R#(za!vshTy*GI*Z%K!y!KI-4%w-*JimkXAqfl;I=Eu zrE$XS%&23OX*Wk(-o@fK(Fi`E0V3kMVD}MxtdHH^Af}eb7tcFo%6adVhmQoT2bmEM z*?QR#EQVMn%-9%)6(F;Sx-X4Q0nIp*pw5c>U}QCZc@UdSoDsQFcJ7s6eC|6J?dP7% z>}v1nqHH_7G!uLzt#&@TI7XTE+_hdn60nVPN`D47Tu+2Qq;j+oe46$^I1UfFWG=-V zk0ZX5q)=ZH9*R-?OXnh)Agpr8Jz8ei8$+D z|5kg&kqkLCy009I00PvL;dJDQc0W05|KU$$UQlQ$rr7>fhUiN(Ux5+S^&v^Q;A1qD zF-5^HDbjx;xGaZST2^?`k2`PsGX>US;-uQ!fFgUsjy+INsDd)3(;^$4ixNapyY^5( z`*+$Dwbtn^%pq;(OYA`|8Z+AS=G%ADemCZeMrahF${)fNQbPq->jh<&ykH-otaZ$I6UQ%Z}kz}S`GKPEDi=t66B@u~tSaW~T94XWi>k$RjS^>kIpXf`DB z^Rgw<4e(oluzo+7nFhU}V!0He*SOZy{p-oad`e=u$NQD_Kk6i?SLAp955GV_zpkaG z(}0B@Ea)jG70$^bfXL)uKRI6EpV0;(5Sz;sfgZ2#6)@oG!w0ShzI{_p;?Ec+MzY8uCC8{r zQmwr(0-L$UTZ5@C_%f0oznro*r{a9(^G|u~$ zcJY9;u$2fj-Gle2zY@bHZgwt!h##SujZi@PQiCJw^$qC`;QH6jxYs2;K+MznwL=>j z8W5_htDTr5XJrTR01wN;(|+?@mm1QQ@X0ci1285)lpPa(F@PWd044|(fdm*h1Hb_J zl%H$)TYmT{ z^i&Fs5%>t~_fGul-M`jazC0_d3T;>nLmu|NiC2@nO1pSGjFV0XSmH4$mOw4Z=;T>% z);>v`Amkn3rEP_jR(7|ixpy%4^`wbj)0(!XNwuk+O^y=G__VlFp2wF1ytCb0qY-O5 zu#8)(Y}Or#Nl+gW6LdK|SSZnk+A^=Ja9`&L^j@USidJdo^PMe?W-c4iJyqi&)y&g9 zM#?iwmsUm-juLE2F+?}|Tj=7lhj#fu7Vz(+nHlE{^t+CzEfwmZq5h+7oDAWcK@4Y0 z^3$hli|vT4Vv)@ktw~}Q315Md1xF`#IibCFxq+(d2RyuL$7jRsF{N&$DdIZ2Dek18 zXD4x%rp1O5=WOQ>Nf2df5E1|y0mu>nGN=PEDo7CA6Miv(00K}z0GJ*C(GU#zirikOAJT+lq3+iAk(!mB4+L>JIPv#9!RX-jIa? zQj_HLls-`11bEp&ST?=@%3+gg%NX~~{}%)0HazX@g4DJPjH|9eVm?u6=KtIk)A$Y@ zk?H2Btj%;C7)X8N5`qk{ndqu$160rH&f$ZH8&z@H$u zVNwa+=MBrBdHO>8P3lAHv38oR5N|%Sy&52|O00ZJg;B$nTbo=LiOeBuV`A@uu|-Fk zt9(ALi)U`VQL)!7Zi9r<)4%ww2NOZl_D>XK(5qOvR%lh~SbF`lzlhZNdneNiz^yjB zpiRGH3q#t0`0K4fH+;5wHu@MLLa^`X}h~j*LCuSanO});fzRfLNd-L4==ehf(hbp2@@IGFM0$Us; zXVpLJomObROJV32mbA(dmkrv8jtp^cPM=d8%`mm&zpnL&3lVFZ7sw(>4{g@#LBkDy zjbYM*)~NoSaGwuTtf$Ck|51&{>bV)y$9n(p(37U(E>S}>VB%R#wdEdKk+Txcv)JQ}?6EL#)dHI`=_+ z$D6>9x_U+_8DvKb%cJplrXHz*1b_87V{Z1KTBMBqs!Pur1{qw9X@Ew`PeP+ln<7np z(w`>UlyITS;y5RV^*=m2@4@kmRW3g}tQ_A2bU3+VO2-MEUBr>ceOs21wzynI_rfzd zLc|JmJSC?;ezx4_BD2rDP;A9>e_aesVn<$*wyB#!bUwh0)p<2FJ=|h3AM2;(G4SX# z2|_MPgr-hzC1BjPIu9t&v~ZsJSit*REB9RWgi*TTqu!!Gu3N@dSZ$aur&=vnX^~Ub z>WTIOeBL@_bqG4UOt+fLzdB_hFR7~t-l7i)HEo4JV{@OPKdo}gy_~l(c4Lv8{LS`n z9p)j#TZRel+%Sfze(Rc}1KFx~93(wpWFb*;H)5)R$5AB-E$unMzl;|m_uV%PV17K5 z*S+8ztI^)#T}#wbsFSe~OO>%s1k~9Q%=XhOj)U>!NOwI8#(Ysb{a(=X5_N107P87I zPK= z1s2n*z&jsU(?Uf0;kOO|<**Phk+6d|LoXZhk;xBkUUOZF&CRYuvI@X= zk{blhTzmd=*T!0dZDDLCtA*@G|0wRQ*hLolhx}Jg_7eiy+OYIn06UH$dg0b4s_Bx- zBtbXI6#KNmvsyxmvaa*L+gb30zgvCJIS+Ze2ud>R128T`3d{?BF@OO8044wc1|SfD zf#3ksCYkq!ThY5QGtKQv7zo4@kHCu-hW2}8HSIcpdq@@i%5@Bd^@b4;=vKg-q(mNf zca3Q~hH_e}B9N6p>zZd4Y1Ar62^;gCn^FBI;Bh&z4Noff<^@ZUTEaoIv;5F`;8M{C zeSpgmjx+vmpC_FXX&u)+t^$IKZyqnmMTBE5r|hk}=vB*IAzGEuv%)oq0Tv}wwz6G; z%5ijf#|P|HWu}ooZLUgGQrEIU0UDn!#1}WEhJJb(4rAAYgGL9!#+1ULNU_7c!YC;l zwnhxEjZb#`6ynh%33)y?piW_XzT4nM?{%fttRg5Pgults4N$h`eoF_3e9aeF2iT#; z&zTXPaU1|)fy)LVq)@Sasjiu2!FbWgVPd|2zAXY$R@k? z8rw>Auq#;9)$k>T0s9pPtcl|N_{LiOw;Wj- z+pzdlhv1cwoc7!LQQAg;TA%mHH-;@Ek1g?%sr8)Ri4z$7<{2TzkFTko)J9-dQPg#$ zLtLtkPf{sHel36i0zd$P-~iw#Q}nL{s8M$I80{)!17bxhb}7;Z zZWu9LifA>s1Y#zkG zZK`0bK=D;M4M`MTc#vLy^s&`;(d;@PwfywuD7 zDZdG;tU&G-`Ltsfh{(%pf>fv+FYYJpVL{gtMfbCR$%}Z5uIm!a6+&ArsIM9CfK&Ln z5J3lWUY*eyVa=nQpQJy#r!@?zEhVq%*vY`FOIf+h{0kgR{3MAS8t-*HV1^v zqeafRdjXq~uZHvCkfqBdimZv?$^kRfz;<&k0W4=N?B*tNrq;2?=jdyQ4X-g_Z!XlV zI`yq8VYiL5zin7GH$xlu=HAao%0P4s)0zCeNHl33Pe6MFeyvHiOwUW4kRA2Nr8d&b z+OB`rQUy|lF)MW;lvLYfBYo}$5&$w8Ff_slO-o0N-~b>12>=2C0N^MD6jth@fD!;Q z;3*&`XapmtKL-E=000pH2!a3r1cqn;pBj=ZdKn)ct}Kc$^4!1qDvP|*p0z_G^14>+ zKv20>sNOoYFMX6;(4Ezv(oxfloxKmAeh!0OZ}rbaKqf}0gmrVz_Q2h7x36s&eid;I zjdwvpV{pg*(H;7`e#D(*LgSM&55?uCV>=7I>GH2o-$yUdS6h&X8%SbSW6gr5a5kk&mFFkOU6L}tMf{&H8`)S-tumb9&hCoI zDtvIs-~6hSLJ6aJivjD_AVdn<2Tfpau{U2mg~B(Pr$3_FOQ`oc9T_&fO)I62w9o|< z3Qq3aR@-r3VjQ)pzhSvr8RU9S2vgR4>V~MXpK>~bSZeCHT=bVM=QSDQ1?b=aV2*bK z6~s`4_<(MlhVkbnO*4(;sK_VL)WL92%)~_e7gs8-EB99H`zxJ1w;a+`e?-(>8qZ9C z3yX4v7N{!tF<-kZQIJ>C)gKYL`9@pUkfU#rt;Av7oVFGS@}|)I&9qSrP0Wb}J3a!Y zo2N-I9K&c3?Hj;08(L8DY*&M;_=T2!LIivuvEa(Do^iQ{84UW(=kpSP;Q{s2N=?`4YBNL+lMgTQNY z)cRM!Ic>dB3i&HdH#v(rQJsI>E+10XfdjWJ=ow{J-!WkHX$(*7wNURIX<=v)Asz^D5MiL>$H zTCAYYHH+&@$})q=(EEGK8)?$}mURYAghD5Lf&uqOg7-D@a-^xW2#JI*0E>PmFSMOQ z^BmmZQw90A6kLhk@+JyM?$xqx3~{LjFDdk{S+5{&x)JWq-hrb+CT?hKG$fu+d|&$7 zpEZ*7G7z4)8w{9o!y|e(E%jIi7X^js!4mD`I_hmjvNiQ%lN6Qo{!xO;NL;^NkBJ=X6TC$xo4POtTg4}Cw~ak3`wqi7^!S0bN*2fQG+eF{y%SMpNwy9u+3KsaS-*P^m&K_soN)Q!5s4Hlte62~D z&o_CX={)W;Ce3tfSXNB@|Esrktl2^UmuyaFgKrDFTn>Qt& z9V>0^W+WhFWr{LC{HS=q#NN)vyV&80!AwgLoL4^LULO)<8=zsp&tAjB10Pg@zmID@ zRU$a+Vt!0}nPu%9@e{~jI1q4DAMXJ$fQ1^G-iAWx1I=*o4j9_^zN>(K^F%DMBeoL! ziAKU{hT{}w?-YO?F2deVecYrwc4jcRy<%nOL=Hv%x<15Ir8?C(?p)Qy(~pmTB{uSn z_;p=C8Q6EdOB4xN%L~8`kxcKbL3sJR6tBYmv;V;t(=H|!l&n!N{cT<1m%pMkZcgct z>J3<`fvFR#_-oYW0_fiBeUvvL>ic@f#UB5aXu{D65^e5wwB~@~IS0M#5+7m^ zs)6mi+`S!uR>qr0dehe2Jacb2Uxs&)8T^uZQ(%YO-W6|`%hkSA9<@FOl+)D~BWXs7 zLu#1!L&AW&R_KkTt3?1onx*mP@&X?cq@^5X zi_k4J?toznjIO$@Ty+HX4og)NF?!DAvdP*4pW=IpWx`+kshFI378OiqRVs)i#4<>d z#x?^Q$R7#$da6QH2#wRT8>^Hat;l%l%L3>+s+AJ$nreVY<`ei|$wVRCN6eL-R@WY! z%Njar7FJ;NSzzYkK2z2WlOsEtaF|>7rVd9D+5sQMAM=~_{8xuuoK6aE7EOqT{dQfi zqHS&BYIR0)`mUp0FqfR`W2MWoelMhNhG25N3j^D?-XsRgH_#Ag*F$J?Zby&ZfCXuH zF$`RFmq0Ttem+l6-y9MI`33Ncw^58|TK%G09=WXA{n&+4cV&!^@zsFSdO&&4XtD_a zTVosPHMvxYLl1|e+E*NslOP?7?55J|Z-}8}~$D*Lbs6y-!onqW)0=z9VmX z2d~x^L|J+M$SZiZr|TOVO2}No>)DQqIZB9Gq;wW)sRW~{Md zMSve&ko@Maq$I91D+=I(>)3w9;ot!X#xnvh$J9V32Gzc3(17vPZ@vhDP`U8X2daDO@UPK~0KDVb3eO z5)|saHCTQZYSQA`ygWPK7PEh=zM&RwRRI0U4hzg2F1ZFQ#pEhpA@yDnQ;qS2{ZwEL z#XhsyUEVL^G{p2eQMJxy2PL-en*C^sN%=un)IWQPi)O`EFtW<+g&VqqbzbVa#xg-A zb)|g%Xo8v;$$d+MU(mw>w5{Nx(s=m8+{h4L0OQO+gvfpnz*)zvOqIm5nj4B@g?)NX z_*UwOM*tvr>WMH?~s$FVD!NXv4$WK zOv}4^btW3d3%F^cg{lAZrb5!=laM`+wbN;&C z+Is*5I6J2bmUnmiOo46C3tl+0lY8=qe5JW3A(1(GtY~M~0d}w5rrrFcdA4x>cPQEW zi$I75hn-pQPsa-@m^4E z06!o^@1W^EbHQHjp-Zjw%N7B#~}vX4_L=I(1tODP(FNHY#*atgCgUS#g)Vxx8h9JGU?gYW>;Jro8!m*|v*;prz60&M5z}MZq z$o2FPoFaWY$4p&h_Si|?tvdX7xokp(v*M?soC+VQnc^H!-_2_(6kA_GeA*43o-gAI zm$SQwerWus66J9nyAax~(C@iJEfZuQ&XO86F^c=Z3)+C14%&~)>VO9RYAPS?j4r`3boRypc|0PpBE+>KZ} z@b4rU)o@DTkrEmN{#jF0aF{!EpBy;eYyP*0q<{&L%{r}SZECvXsj>LD5mFmrxd6r( zIPpjCo$>Yo??+VFr8jnLgt$%;Zsv3$zs%={g+xIhe)=?0ft*NB}ZkJm8Qz9^uVXsH^ z$<+ENLMr82)`XpSnCK8U+cm>`Y~LQii%xPF3+mB(fhY5Ukj5QZ5#F&?nz->ms&y7p ze_1=meIdX#bYq}}>5!5b(T|($MXF&-+%ww-cZ2`; z!hRVIJJ!dp4@=$1`=}oj}v&=%0t2;A{y*{=0_)}J#PZW2X$Eg{hq4bL$ z;B-b-nJs`AgL-;kDC)Vrg>{vP_$t2AP(MX0c)qyXJLt6)N$Z1duK|y8Q%4!tu<3yP zaYHi^qoKZ_-MeZEwViJbOS#-PR|g5?oBW;}0-c6Lt!y}oIX?$(ErmB*mn&!ld9tX97m*kd#1W%wPN!Jj=S%pi4^fr)&w8j*j}(i&I_KNe1;LTy`< zRFvcla8iwXpx&@~+bd!o9Et%JVYe-AVTO@4)XYm0%Mt(@Kg92RX-DLW?AZ^=C)wEL zFv{`bB44!Fl!DPP;r$Y%r2itnJ>3aswNPx4#jtfzBW``yeIkvSX-O;CL9lcUHws_y zEUwW?SbOHlff;CU;}UR=g!pvxRTNTbrAhysjQ}V1r;B2<+co;Tc|<++aL*#w%&n#Eh` zC*v~vAv6xC#KZTJe>PMjg+)D{RPKFD^F#nYc3t3cVdHQ^Zf|prcw;xw`H3!gGF7O4 z;UZJ03WUwz^vU>pU<4L+msCCTB~5wJ=Ic&wMPnDXoUR0M`tTPZ8Ez$k=XZ~G5ikxT z1!uL0!(hnAEuN(>Vhlh<_(61Zp)F1gYd({($_((xKFtOT=`cig5?z zli)91;n6iR?4H?@Y@W3vyU|3B|Ce-EVCBlRe;O7fglD$x>wQV`@Wk&93&bD;=~yk% zmyCL-w5BcY&Z@7f)+wy!D$Nk!RIx^ft0h64ieNUGk6A?nM~I5cGci zY!-s@a!axbjY6In>DMSm0=?*&$Jf2aXOu-OP=r!o`URh~79*&vQ4U-UfrFNLX+Qlt zrGOn2o#Aa-W){OE_q@v zvzXpx>b~Re=EKg(H5q!3a&xG+S+#ockgy#6Z!XXI?@ui=>Sr*>ul5M`)1eh6Vdrke zQ$P)0IcUbCKZCpav2NZ=`JwioX+SAmRz5VILj;wwi;_w~eZ4-!79PZ^Y0$c)r~I&P zGyKCi!8k>sB=Dg$2oEgIFMV2>g`9=;`4CA2z0c)pk2qW=XFcG&lzf`o z7A@^^P8ukCYrCv|!F5>f{og|Nj2|HmnQPUTK{TW-#bh2b3Bg* z7Nhe0eOn@xw3SHxjdD_8O#_C=+o#$$v9UD8u zh@NZ_*Y?}O$lC`CCWrJArR-Y!SDYz+PpSb0Tq-gMGmxCSV~tLw1n@G)3@9D|L?fp^ z761TXg%bh@VSoT3fuI2SxZNysx>Q>~yqY~0h%V(XV^RlOnc;q#02q9UM)5UsIbgM9 zC473}?in_+Mu@ZlLx04XRb@0;5yxptYu8jO#-r1R!g*5igP2zgmxV3&UXueXBgDAR zb$cliVy=2dmbPxh$zqF%d3=3ke`+xc2#CRum1;REXSIz~y|TD4sGZ}W7Mge6lp}=s z6TV}k5$J#fj#mg3qdKf+BP%mW{>_|z`yC&Wd#s9wCD8-@e%A3QHxXH>y{>q4!10U~ zdumteW#tk2IpP6Blc9&tDXiK>%|~|stZJ&S4&4Z?PLH+<|RIgkF4e8H^?)h3Wd!QN_u67wa zxJQ&Z3VEH|ReFMwo*4@rv{SV#Ajl+to7^pdw3`Jv@N!<}z)44@KExJ|H&=XHu5h`kxKNH0h+fk$lbA=Iki@FcF zn2Zq|C6NFt;SVG}Io>E!F`^fQl0Y?3Jc674h;K8;WF-E`skepJBWzZ;emI74(=)bK z#IUhsA2`=vEdCRs7r@BwAeP)1Ic&wEd^hE?ikgvRJphEHICkRqU(uHCYsi5_+ZE<` z0^N)S>2n{v2X#~B{g<^ZnFvjQB~&m(fz2U{6;GhAGem)v<4K`mixnKU?}Laq(@x8g zUGOX2y#ss=qGtJp-CW`{=9;t~`Nj4fF-86n1u??i3b<<K)54Tt z`!}mRHei$_)qUU?pNyBPscd?|!-c*a!s7;1dkKk1Qtv;RgROne%`CWi{)h#LN9<-4 z1EfHsWBQCVurc8O<}q3^wD(^+@?vfe&|g?Nx9$a_|NS&ckVn^;*e)Qf=54Hw?O4xm z@*!Q96QI!ckQJ3n5E+5IjSQqtQ!EvYIhL@30VBP?OI|P+$?V{K>pT){i4A7tXRSPkWwET3`K2Z&D+KLNp*{dZ&9r4%~d{L#sEl zqm!H7S(%iG`h^d4fKPVe_F_31hY$N`V+XRa(GVvvGfqljXh_x%;td z|8ot{Hm?rEK53*Y_@2mmAzEpM9{VF~heB-O-iW*qexd`cA z?_x6j)ioOXL(lP1@-~r3@A|x{YOn=oj3#8_@5F{aAy|Ew6 zv*wU*##45uMa@}X1##b1YAhjJO#9t{UfA2|sYqgyW^kPA>nH z6fa)?weePNT;+QQ(Sw8iZQ2p2Az_QDJz%76hS)TfXV~Zzt~>&Szi*S|xODhj0&l-6 zE5HAw2vEetU~db<0o?8ZB;%~b(k}C40sq%cLRc6^3h?WvC>8hR zj!OV;MVvGR)UCy5-7v_RsVWWskSXDFGpJ@<_rE;nc-Rkm&ZrT^b_tsRY~qbf1fsjN zHGE=|K_B9oS2@0dR{SC)5F6Sb>lJ?Xk@f; z%ySR!x(>E%gPPoWUa@8Kn~q4+_ATv9k*Z>$5gd}fF^mUNtLyY`FM+St$)Bg5qr)J4 zaZj@$Yt!@xrK!e~^D)Uz-c1s$y;nYwV}BrIx5{M0G6g8i=iuYCIf?wB?1u-@X&pg!0K>b)jZrq+jhto}6I z&f6|aNfU?b0*XBq_$rTmHdh$*0k+P75qDx)3Gi04Hq(hxtU;hgGW!Q893%uIr#~0~ z0DwRf001GluwVh;0Qt3UkP(>4$7%^H29QlB&8vI?;v)j1pUKu_99#!N)#&S%m*Uw- zDrn!y(KSWt=O`G-Nj4%mjETQ;a)8Mp=Kx%@+lP`Wv8}i6$Kp?F1#z$njL*Z6=5Cyjmo9ydHqIS1D%xll(uIWboCl<8 z+^0W0jtZ6-ev{`#$_+U{NL6DL^egY{KBa)HG-`x=I`(>Wg~#xb`>|o!Ca}ce!OfT9 zFG9LBg+&-*HSKT4ab`q^S3=Ikd8b@OUqz;YZtpa2`La1d#5FlZL%E72jT(FJMp4f| zjen{t8Xx|E)HuQw81C?cO+DRaoe>9DI9#TQIXvuo5ovD1RBHED5;5eub?m4tIgym{!4I4CE|`|jMi}zXdQz9kLl+7r)wK zte&CXXmBB`NauM+Iq^ZZ^q7euy4SUC7}_h}Rzyi$B=AW>-9u}kn8R0{SG(MW#iBqC zI87}McHt^+)DZjR1<2Hr?yrIu70MD`R++0(Rn+{hVLL=F`L_-8!^c>DDX}MXJuI*H&Wu ziYhRNS7_w9@7Td8)`2O;xz~-s0OR6OECvm&9e07U>gl;}6?6E_{PTn%Jl>}A%yC@= z#Ihb{vjFbQ^ED7c=bWXK_aiV(TaYsR128rO2uDtSGk^dBKmY^40QrzwMCF`X4zEMl zV&Utl9{EWm*!yWkBN4}N$kVYA8`vY1i?Zva=B+hx$#l`Y@<^Vf<`2x+$*uD1)66Mk z>X$&Esmqbi2^}{22hy(#H~E!#VMnpho+sT>CmU|GZxs-QT3(2Rz^y?#Eu98stjC)x zl1Wf82N@-7`5cN=Axu34ym4kh<^n`PUsuR3?WtK(v=EB)GuVoyH+Ov2GtmIsyB#Mc z--3IQPNhiduK-iyKmJ>mZ&8EMW7Jz#*;xE>giNF?I&JUQ0(RdObQR?qCVCqY5^=5b zT416)NO5GeTlU=Y4iSqT!q9lQuLZcfvIX^>TvUTQpWmrje>~I zBs35so~LFuNZPyxcHpUdCAra8Kk$0A(a{woQUhg$?CLK=}fK2R0Z5b+#(JzZY z;@clFI3YBrDK=#f@=FDj%lOSGsd2vJ&wfEd4Y!7U`d$))siSf} zRGCoPf>{!p%akQVI14Gd@s6_jJ`|*-ucrEZpbL8%P{G)H;@gt@zsOhqo+D`T#J~%p zP4c)phqSnaX#P(qXwVK{M*n~(^xR)X(glyC5G?+DR^LP8-UTTU(t^B>2V zt$|(o)5j12{jJ}jz4qRxk9d0%=y{h$pP3fY0l(6X>Or|p%}9~sYdlZ#BRO|{hm1dQ@D$S01I8K9xs zSIJp3x2-Z4?A)xWSH6x1czOt5$n|IJ?V(w{#t&@>B!o)FvnwIt2`z8hc`XY@?+KUo zKKj@|yP57Rz(VC`psofV=+tbl>stX%?1jbjCL24Kb0iJ1av_|qV*&co^c$-3mo5~I zFA#&ReujB4?gvhi1B#)gqW4|#au$(UIYG(#&&Uz0s5qB5k(%|qDx~EsZLbXDbHc02 za}G9>=7Vq(ux{g$ApqN};G=7nKFHlme75gpeDLglPy6@*8!|;JCWVwNS1C$xQ@~>p z36Om5dwA75IUy-0Gs6V!u6)Njg?tmg)_ZF}4y)OGS`Hh+3gUIf#Xwf0$Zix4DvErg z5D$OwCZuY@*0BL()79DF-oQm%71S113s zLHViMtbcu{NtOj5Eau zL!be$?>kuaeLuw03#zJF2FpS))Yg2zI|v4=$XJ+%57jXCb%u!|R&t7xgUt+7Q9jk~AH|ipEzdHb;}v%om0EOtt~?G^tMlljeTn`? zd&2}tu(sp;$~tt-RxaG}cQ+e#yc3tsQPlyH;|m88jP0lBxk-j{eOFBNbVGoEq2%8! z>u2M-76twOb^P=7Sf3TUrG~im4N`4k5pfmjhbYw>TsBo?>j&L5f19Q|**>p|#tN;B zp}eJvj!oP5pf)j%GTrhAe4~;ZFF+-fKN}9e%@1D)MlB~TORrTnad-adXp@WE`xS^qN zWpst3T!=%vty}Y~;KLg~M)^a9I{ob=Ye>5AyWkmJidN>2((T=y08BTAv=*)9%Q+Us zv`#2y<;ER-R^^jxEDoiKF~5;di+PClpSFS|eoL-O2l}r49CjChW;6BVZBi3y;QweQ z3hz`>BT}Tzkin?{RRb&EB`9eo&p9uQ;VYpkhYM&-Qs3iY?oOl;T4K;g^M}#CUgk%3 ziIx%VkDYXYe%_1$$gLNOxo;f8Su5PJ+lG?a;j1#mO#!r#%P;T-!8?sedUe$$&w)I# zNk*2DkyoAm3k|&L5}O7jbaCnC_xu~bus8mM zlKp6Sa4(Y8E7@^iue!xiiBh@_t?gCmXuHc%0n%qrqo!_8*lodcTJhEOl64EzCV2d1 z7{3Z!V>fRV2Wuc)BR|+#aTSp{?dz}_>N$1xVNBdJi%(bZWY|E)=vy+gz+P%u9y~M? zw7fw;(>DrI4);{s3C4c-cxTIuNp&%Y3a5ht18iCwQh%GW3T-nU+<0Kq9{O^XFFkTT zaol>pZ59R~feOevE!k{!#-0+OgN_>#td>W?2VuGvP{|@dqSiUxXk55KD7s;c z89N+ub73XZ@PjoWu5qg@V(@A`;5BHlGgKu<%kGdrS#vBF=Fby?-e@+3(rI^&vqb5d z7&M|eo}aBiqP*Y?o>*K;kFv^s$8AV!K~d48*vxn@k)w&YFpF8)LuZ z4x+W9hp&e3)fD22fwHwqEcB-)EbID#-BtPaTo@>^ADz4rYmjrMexF^LHe zSgQl)-;Bfg`=!W!6q)}?;*CH+o>lVF{>r&lqUA!(|J`QHhUlI+8-z>5wzFW zg@Rzee+FV;54T2?$f(z@#dTh}DN1-&9N~NG#|0xdgpXasfj&9ss#v9-nk#307BgQi zw-{tMl4hGH4$vM(=X_yTMQ@@-!jo z<>%Sk;QA|w)llf`ApDrvhOjwtw2fxTlJ?Ll-!?E6K66m6ZTqVgOorN$0ESu6?IXLa z@aSBHwy_seR4xUKmEaGjc;{kmx=bh0>-SanRX*GmFEgUB@jTjd`O-f7~TIy%Go)##Pj74wTW%|+W)tEVckjpxV)u)LWr_sni9 zQj|f3BCqs`4s&|hV#lUAXN|4wvQ)RFGTwwgPE6IYr#k-#{k!8EvDwUIas=dn!w1F* z2dvUP6nc50)=?E6#do=^}j z(ZH{UEn8i?wyAdB{CUai$LaW8Eoh{wi#+SzzMiikRJV8*A~6gmGenPN0ssTrHf|R^ zv)sK{^C;Ka9O%9?|@odtGszSW|q*kL}7ZcKXQ3CuFF^uG;4RQQ;7q^ z!{yvtCA0%CbADD#5o*l$^+8Mf?YB66GvIusU-=nTt;MGFB)G636SCAiYI+CE}TDorOdC-56XG%;j3P4sL(; zVCBh3PAHw1C{Y*9vzU~;#xKNs?^02V$mpW;CPG3z6phqE8sqVBijaEiN8_{~k6pQ{ z(iY((9`Ysgw)E0n_F1*=hMPY3P+S?r_+@2TjstZIyJ76KwTyW?+oC!zLYTup7u*md z76e(`4HW^<&b&j2IwVU|HFLZ8?iMRkd=GnEXrz#Kpkw0mRCf+ zteqIt&|W5tNmZl&QZKDas)*Yda6c%6S^vE+47=pS50yY9=C;=~mNfLvmrIE)oka6` zK@XOp2#b(EzZ@2I1f1csBaz0&5OyG&JUjo#B~nY?9`ZrRzHLaEC}fgUi+a?jdxxY;}>r=U|@5ZjHK)!}u-# z-{~U11z@A4&{a~*x^oSGKBbZvb)`ReFKV#$?%%q;J*g`FeF;ZQ-6u#Of+%pV5Nsdl z>F3}+4m9gNN+_VVj{Q!e(e3@_Wg&lrdVsX^ft#%EwjC9o9ES%N--# zWY`nxs|`BeiOQ47tbWVn@`o0MIAeXmrit0~VAgBFA-1=56FVj`UP}vhiyN&kDHGkq z-`bs;f>GbMB1*mNP2PCz_c2g>)K4)o*)j|WJ0}r!pdES6({^PmAOpA(QToCn(A*-v zlM^8vLh)U6@?X)y4jFjH`(f~v{TCQq#RnE9XVC#SdrbW4mi4DH01?NeMBj;FMD&5v zd$NdqX?E)wo;UGH(f2Mj`8_xk~paI4(Wk!ms-c}wZg%=y%5*?Z~L#)?@>#=|c_|+`$^UNWnx*P$4 zIO1Z(1fwz^cL@5c89S70Vod?5bEVf?2Xd07`%9D8_MY1(tu3CB)eJ@{Fx&c$un7p~ z`KyeurfHX}jW&-OcYP!>&;>9)NCj3VelvgozyKx$0Yh96!2`em`L`ySpd3^~T6={XmM}4PnWd#*z`SKet~STnB&4p}C^< zi`3#49&(1w<2kj~(23)ceM+ihN#kEZ?ImMJ@!FlGS~f|wK+&-|F`kk@!60#esDS|| zQ*a;}zIES{s}RptOGIl|ogz$8CHnP7Et5;2{~zdBdD^wG+fO!{SrxwSs#*h`8_cbf z<&G!7fr=u^rZWL&Y$`-k&MRxs zF-{7cNZzq?&4FIVSiC*Cc5hLv_8lHBqkD3*t(o*rii7ocsN(@$W!XF6b!wh~ZDAE) zu8O~XboV)MF~27huiH3K7Nj~XP0+C+VE4Bl()nLpAtIUKkS((khj~+5FWg*-5J-?P zqOXO1z}Q77nV>Dz_$f;Zz{N$S-XDzifPLXSi5g@J`{Tl2FE8~qwFF4CN|d_uM=}Tl zTnJr9%^hRJGHa}O*zsbJ5o-37O%lJR_5W9shHEvlFThQN_rt~1HR$*T6ZO_A+BRuh zs9DN2=M|ASzA%p(Foi#|jZj2&@-h$@b@QEQk-n=p;%xj8g?U^I3{WAf9$DxUEYXZV zm{0e1MSG*0lc*8^8UctB05a$VFhW3rmZp9)fB=91CIkV%TyOvbKmhr=MFK|J{R5{; zt;F&PU`l`lczA_|mau^@QE{6r4D#Dj(vH_?`kTWcxUFRFLfl zj9XRld!q=MI{gRWgi4l`&^+0KGs$za;k?6+}3j_*c;h zIj1Es;})BKo(#k>Mu^237kp?$gYE;;Z}{ML0e)z8Zp84SI7Go=8Hj4+9Sw@^=7Rz&HYC&%rB$4MjpQ8~@)y za`J`jc{*E5{pY9mmau_IUzMvB#_)K1IPJyf(UXS6I;33|kGTA#J&WTE{e9TS4-Vgl zd|~@TG)78{Tg_bHQmqZK^ECZtF%wo@KqK#fddn&dxw8CBy-wDCus8mT2JS(<%#J{4 zluPU&nreeabF=lHPy$zZM+Fip1#2l%=IdEMWKEqL0<92Q-39P6n>)4!fSfe0 znkf*In;BIpb)#Mso|c;UJIgXmkK!CMl=Y|-0nGM5S{BtVEmS3ej40AR@z00SWy;2;oa0OK%2 z5XLeuj8v%T{*BjdtjMq5a1)$h9TE7M7sCM?ufbuKTxJzeTU-<;$cMlKk|ekFN3Q7+ z(rCV&zD4(r8}KKpaQkWNi3RYacUI=2c0Z)xm;oqFc1?2>Lz^)g`*T6@=JSlAX*6zX zwVSnoXzqnt2GD_7%A=9_ag)R+@2H2G$~IbOA?@Ft`}99_k(>BtYI zYj9Y3BGvw+6%MIMc^L7*TtLaNVRzX9efQUHO@itA6v5jG3B*WGl20k~iIStv+Vm$} zE;WxJR2UG8lSOYOLQsz<`AQpu?YpyjcG6B-Bm4-;A+@JMhZB)02O2n56;T}S<|7A7 zm2VC)_O{M+&kIk=i4vzhlf z(YX_!62D59RtC*w)mtjJ3TOqTHhyt5_9fmbNCkfK_mn6}QE7Q)UZ zGJ%|thjK<2(9Vi$bnWXSy|Z6U5nt~S*-}=)TfP5F|Kj}wZEVfqs$&*y2T3JGLR^@} z---Z(d|o+^M2zlRbzFew4-^Mf1EPGvb~F4yqyI962C?v|8|ena&za-cU=PU=AV=kN z3LsBXrZFcVxddrDo=Om?Xrhs?!6(v*1oi=gK~r1Rmp}r>xvW=J$puPMSWx(p?ke#Mvy%gZu0QNC z$gI9(E&N46D{boOc~l3~eX%kD>Jj<3HRNC40iNfsVpm)xw|uJ{Elp#T8Q!T<8_1V| z^@&M~wD*Gq5r}gxOR4dHfQq7gIW{_N)vvxn>TU#MO1&atExjc=VfF_6mmEV=Wsd%} z+ykQ)K(d4@2H(Z^TnNTrq0e-m&UN<-gw6A>5fdr;P^b3tuqCH2Z>L2YIr6Pe-rSlAq z$~s9ZJvv!`DohKWPNtxI!RibJiLpamJ?==6;HWi*kP}-3O>e7@IGoZod=^_Of_haO z>VkcXKFZxA=ix#>DmZ#RVD|O;Of%Lun*M}>pl~vh1u#ZHP=>C4Gk^g=1SJFj5Cgyf z`M4@%`p`QC`>%I=A~M7;(Tz1(85iZ*1D(WW`O1jD=4rMkE@$6LkQo)!ChFD}`JGt~ z;~s9jC`5|RCQ3OJ$^y7>Sx=mYiv4krSRuql;(ib_rl^IbmW4ABNC~hy;Zqa*$(h@s ztOaVXQOOhh8%WzS+^Ad9V-0DTqbdDXE@KedCXgKL&6VI@u`m08Zb79*9b2VGc<@g) zjMl^#jVlqFNPR=VPdW1LDcbZmQbBTAJwFj~Ot|qj zmg4M{T(=Y?%tz{zhJ$c~x}ms=p?|5=sKE0NB!arI)PTMV7bsOBLaER9A>mW+b^gA< zbNhra{WXlR^=!6>4?VVI)-?Cq&RBdotP?;&IAR-05CCpFB`qX4p(XQV&F~V2fRlEa z^_0|pIbt{T|6l?jc~8s`>VdpIIPa%b4I+&!*u}qe#;(Lg+&|)rekuEcU1CFALv^BD zl;WN*N9GaLjQ0K>JUIUnlfSp1WFAP9vNT#SuG3CEH=bDM;i^4$7nu7=jc(ktZzxN? z3~{CRNYV*rZ^vAzVAQ4#@}~zXQrUj2HrErvk;h+D%fX=jvSFjR+iBTD65+QuE(f|7q>;Lsk7Gq4a#p?jPGh^BYri(qC3O9Wr+(N*B>#BYdMAdi{o1ixOB;{&NY zZG^*kNggFa$1q`>;}SXkjjE#~CSfY6W5ec;OQMEzAwjk|Vg>ROIay8EwQkUI7x+?X zsQC{V-yx*Gt#dvm7XY9V02%?P5&$wSFiJ!SMs7!p001xni2(cp0Qt8YtTP!6CM`3Z znJgI@ji-W$6zUlBQ7l|)fD!;Q6);TD5e9BYjUWhM0to;B0sz^aSlAK(GUz8DGGPdY zHNOZz009sHfFKG08Rmchz(fVAc{}9kA6<=v--VOZnuH$#ROW|OCD-=BbYLMgVBL6n zDROR@^`qkw%Gog)xVI_j8u^9JFyt#@(7JA4meNE=V6w=*JtBj$)Mq72FE+dO#*Tr7tf=_jJ+ z^3-Q@Fty5gn`z_)kmq9JK6CF6E|h##H!i}}pb$F5jC{LjX{FgK$}HCEa< zD6yx*00^n{ADKP>W27FM&q(V7#Q|r)-Xalz_#NqgsZ-~}(B zgcJVb=3EurfC?=)puWz&8z3GuptE4()zrs(53s%Rg4MyM-0g)*h_B#ZF=ot8{M}y> z8ZIs&pR5es!QEMt249R*u8V1q46l`+FOPgA*%o&LF$@lr(6jLuTl0D zJ+x)#9a*U=Mf^+IZM`)R6jl#zIfI*b9Sk^^!_jE9Yl(pMi-*L{#epZcG z9p0;UGw-OmwgX!fs#yA8Ci7LBbSUi+qYNvv=F)!=fo1*h_MOZSDRZ+lzba8J42WO6 zKI_3^S@GtQ^ZU=X;Ncn4a(Y|ZJgaC&RnKG?dHaw3^|u;Iph<9~|{SaT=rtIgpE@9-ZwJ_l*3 zUgtC;0Fzd^&P;QhSL{*|p35y-<>bQpMZ^UX#LqG*#yo?4_1NlLp}09D8w%7jCE6>lhR~$&2aAOaSyP^ng3IdpDQh+`@{w% zl!Lq$FGujQh=}S4M7o{_W~72ppTsJaAF=@&N>}EOj!xdI!T1de^1VIc@!Z?->&(jo zRk}rlQT7ly84Jq7J-iGAPjVo+=@Mh_dMt-(6h#z409F$I68NlXEx9K><}3Z%8|2oo zSX3r7#qM@AU#s5s3Doq~I7Y~P%KP&_xJ&*$c6yucF38BM)d7%@w)cC~$N{E|GCLUx z2fgJ&E8TIN|&;SxtGtyIree(oY&vepLBcpE<;~@kg z^$oAbbn&con~-$7zK;7Osx0v0K-UHT`s_XzeMDrpUQ)sn9P?I{w_c8ml6+BIs z00yU3S$B1fA{@zgN2+O><0G0sg{m>H-vXC~?)>gElMAeaM3VDzl(#Ccnc9=drrOIP z0UBBX*ik9vB(y*(rBx7u!9llGR=lNiP0=2WIv9R#Gi)&u6oNamMiQ>7WFvy}gsYA3 zQ(Y4eP*z2kjk1v69RX4_lz^8Og>RZ&JtVCzQxObIs=UtaWtTO_buz#b%MNV}1!q6% z8~bzz@EY5JFi|sTDEftS`~M?GtdT%_pv8~PRrB1-vbFu+S#A)IDzb+Nx^Mjo_%%+{w3l44!eAJ<01Q&l?M^!nr|$~q)t zsdRBVP@gDJb`Qy~RDHv{(-}o&dGM`4cM4N5jyLDbVO4M=0B+CA((;pqoM+CLcb}+k zPrXs#_TJc+vdis?KF7^@U2peA`XEbdUsB94(hCpO9_{|rta^U9!-5AZ=0pxSY$LV= zGt{=h^~Y4ZZCmHe!W#8>leP)k@}X{Q2p`Q(L?G|)OHgzpMCm&{XDwGg-X?S!GspLBT z03j?0X-&V+0|4!r0yHuY=y7TLDR~Ucf*^ zB*3{I4BO;F;f>o1{(P!FeVUYrNs1Hoii-G;r4AuTSmLx5`@MaqHpBfcpPI0Z)?j(I zW7EN3M?lTgXr2SlDbPdz6YJIY*ZDN0$`YSYi*Ao$tPiDMIxx2U*wD~j2MB_kji&9e zgS$|a>n@Qj9m)i2p#=C>?_2afu9!V-V^+luWbXFh=gRu}&c^8YliRk-ZNUkgA^w*m z?fekk5jV~0J`c`K5MS?akB_e!+ozgbw+zvEo-&Td_~06k`B&41%%IzJDw9ItL%8!- zyNN#2-x#H42GYh?I;k^lBFVrs`IXR}NL9FU`R9pR<7Gmqm@xX(<#HS28TJOfRj?Mo z+*QVDBo;p@LdW;n2?ka=Q70gn@1Yq8NIQ*8DesJy2=^Q)aIsW||C8RVAAGB?K;`6@ z09)k_@eGgv7fNH1ScAJL5Tdey z!8wBpVwhU*wtT!GyQ+|@W-B==+w|Q-V5^=$NU+Gqc#@$@ zHq;8j&wta57df_{IKm>pm+*9j-uT|r#w9sNcst-i_ylpBQo9ejNI8GHHA$!a8vQPPle+c=fCiE+$|B7&Vq$i)7uW(nF+uKqnP!)~!aC9lCJn`)HxqtybX}pUzu3{yogMS?_XSr)i94i()P?2r z9Y${#v+RVaiMv#t+F?4lG1Zy(G428)Fgbl~~*Jv-e!E@DjOKcyN7=>BUwX1cm z&l_!X_G82~Nyueve*&tKQTG>*()GL__~!CY9BfsdxBon-t%v()^5a43n9;1Z#0kSN zq3moQlUA^a0|<8QAE=iHK!SL1K-3K@&pZ;8o5Xt~PmkAA1})!>R+Gd4990C>nu5@9 zrsE9FllmOwCw?FEDM;j1)d+yF09oe>mY-TWO@+iUod~uTWURq9W03LrLy9c|iwQ6w zY9LL^)W0!bGD--jT|_yNBZ8v^Y1Lyn;-u)(4`=}-t5~ke4|W^6%%nm+2Em5y{YXA> z3oMp+jJ+maup&4Fy zNj^^P{JOR>Y=F-OhbP={{*|o>*J3k@yi}Lz37AnDEuBYa;6{Oiph(q014kr_Ffoa_BAA;6;E))Ky8a!w=BXuA(%Ogpoxi zmK+Cry?bgQ`Qi}(PiWoF8^puU)h$XF$}PGuH8Q6UB|W* zw7BCW_EC{${aPx~v|#K8#d6iL=xVq6zzwn@REb^Nb7d^)(cg1> zJ;osVO-o!Z<`GaQ;N!G#1sw1tJsT6>Z#X}Iiv-emVXgoHCRe?5;`hgCEE-xp`PGVN zMS@YNf4S1F09_|60g`5@XeB&FfXFjx4COFKG0 z$GUTy^RJVCW%l+`dm{&b?V3ySZ!NO5(y9E*HGBe)$xpcYwG|&o-$bHX@*H5(+tEK! z%$UBrijZ*86i<%*`R zi7}4Jb>EM~&<^NFaC3o%sYJTVl_Ec&8Yt!w7RpOAM7=hv)mpOLmcqw6H~0%K98RN0 zb%OZwZ45fqmr0)Fm+A$AI!W^^y6ICM?r%59klHG_%b#T^UlVN>7h(w9qi`XcIYEW* zEy%5wIJXkJfPco8r%u_0xbnab-eJ&NEg25JgDRNJ5j+zpEaql;*dt&~Y!(>gF&pFk z+k!KxX2nMOJ!d%r(LXz9eRK|!3U_mlFp-69{b{J&nGZfVKhJ@$mM@v6Ur%F}UI505 zpeIL70tBaf&T=(dw_$sbM*1I7^K&;4Q;bIO)>^x-F!I1~ljXSR?-$MWd&4 z3gd5hTjGBf7D)C;6#NAT5tt#90m~`nMgqO~Fzu$be)Sag_09H-zIT+PR zurcts(p_Vz9+|x0)`MMBd6qpl7~U2@#lt_0@fZ}i9deOjGw62THBg&0Cspi=U-2=_ zuZk?nq`glD$NEGW(xTlDl{XZgd&O_}CTSsI+!7Lo>Gv$&z@&TFbqqg48~cDi?Fp)) zvklH;6Bye0Zf5ZFgsEAstvOZ#XEFbFFmysr$fc-- zY@%nh0N{DsiTl7G8&b;-qU|wf~-Ry79%UwIrySV<{{#KQ1Ul>;$hEZe+}}h zhh|CzjoX`K5|&gY-aj^yHV?Itn+|RR4JW8pVGxWg+P<`j0_;jga&x?c$qz_>jC=z* z>r7m~2YeNPC?HI>OfB)tI243DgX$V|d{C_6Teli^?%TBNe(9>dh=%fcbhXg^1IhHy zTa5;WJeZm9*Tb(huuH^75Tsrp%PqWddfp7t=k(axx9|jgyXwHjAAzmZTKLOY7XzGx zHrOT+O)nv<`d@!nn$`u7#90xUj;Sy2D|>`Blsdf*0!JGMRW4xG6v`V>d-3N{JVt1| zLn6ITWAZuRrHmbJL~hzpILUw`jw`&Z;M38}wc_~$DjDj%ZRWk*%rcL(bW(RC=WJQM zJb!i{;Lka1gKlWJ={y3*-s0C&t;(4%E0jmza46pFAmEbD17xUn*K9Y7}tLg2f9;KqGo=8 zIjLd;2A>jy#!ncL9$w)Q1Tzlk?i8^6k8|?AyB!dMPvLT@@qMDiaJyjSeM=ki+uaKe z&s*gcY|utP>X#c7(YBKpY?`ng#9hdR?tHcq0MQiT#xCa7kLa=qVeX?ull(T(fczs4 zM6P)pFmKc{^iyN%cK^WlR-xCyoOGGvFChUk3(Y1#;(YWttytZLx05G90;jSNs1c?3 zD|6bns?3In@uR2wfo<5G572=J{8^YKo%u}QFx3%JLjx0#Wx}Q}v=O1T88gU0^WS(P zL~@yJGT7}Qdb~Y(xPPM+R4b-#(9P#HI;pV&c=ti&eH2Uz6i9s%PN?eFHJ)Qq^cC6_ zmUu9;-?<+m^y2~vp-aEiB>DNeL3Vl)S0ka#`n9FVAB^{Vv9((%Xy{lW_!v^d`5rI zisi8YJXi_!a(;HnTc8!D`%Vh=flc4!`Tl9sDnAa2s@3EJif1>e9i7jN)5tQLT#!Yc zcskShh@g>{Z?I|HLqe!R+(!_{M1@GwQ6hjGqA!@syia9n*~Z=2xOTGRUIo-4ck${f z5G~Oi@hU=pL)4DBl#i>?le4-aSjMofA@miWy1z5HPjw^HHk6bF4l>6DC@3IEhBdz! zKmY+T4EKS6if9@D`L`n27QfkpPQ^hH;8RtAdsC1C;a1j9nDbJk!Mz=GAYjGiopbBS z5alEdd^sS)kddxR$y-W|h@QSO-!q?SZX2V1{FBHwE>ZF&@azqka z^dwX7GT}l->6ka7^30P>;cBIVJev)*rpS*0kRCjW;gjc7;))9~u@v3KhQ|8Ab*CY9h$}Xc_geiWKzI*@X}&jYoo{yvGhs~b3Hil)i6-dL zdc``IIs&rhOxIdHw{#B|iH99&hgofvHj^3u?-?I#jOu?OInQG(itlW}2enLgbWQht z09n_al`}Z=-XGfFY(l5Ev3L;eR34ZT=g`@a>zHvFOV%jz_N2|<#^!9|_SJFdk>Ywv zSb#=+3{Y+Nh^wWMj2VTHGGiPqM=k94AOZ)AnD`mfi4?H#jSRE;-1qy2d zz%+^?wQ(E6i=#USW}1U71l?KoY$ZaoTbfFq2YnV$JI!|pC%mxxDi&e|0O-iSYG&h{ zImSSBGrSQn6myX|+EFG0gOK|>-Us2m4_)#_qREd}t)64~96h8*tRLJ^b-*7cT61vu zKC5&3-@)h{#_T>Y;1^>Ms9?ixV++CQ8E$6af^0*8ef7ijdLQ%W3!n-fAG&-^&_=I& zf+W)fX^)(7N0#GEUq7&Dzu5i_Dag&^xha4$Eig_1Q4DKGj35F4APE#~2LSoC$T2QY zX4yIe9%(kJR6kqZ8O+Y*u_YQS7q$`r8UdIR05YxzFj7PWOsjq{fB@hCB>(_J13&=z zx3tV$(W50FJsyi;}=G`1;ASWW?O? zS=b&Y7fGuQz4}@0U6_d;GV?-Xe_n#5j*fPzKlRwLCo~Mu1Hr~3=kUsm0;uD^kdX|T zOWb$(z3$FlOFBY4!|A%ke=S|&f?zV^LHY_5@zb*Vyish#SUpqPt3!~IY)>|7W%qwR z7o>q!EXhl24q!ey!f0F>6tKL@0V8WvJ=3q)5u5~$)N?pHXaCmOeRC*aGw*&p`RCwm z??q~EpuX1B&{rD+)cpOg#`CjBJ151A74m=}JFi2AAB=AzvP^Csuo1bgb81P1sw}5^ zunr!MvQlWL?XWftq3@CJwy4gAjp;K0W3Z~3G;r7>xoh#c7$TY$D56WeQTJF-7sB$| z&z)hLn!-@PLLz&@HBD*G&|S|Kas&vb0So(|6~i-nQQJJ`&uIUa0Ep+nOhYSJKCd>6)lIF0L)A5u zHisMClqeSlRwQI7F6}H#D|6uGi34=iNzU3p`;IO=9{dSq%6JX|u!!`yPcJTSO_lW6lu(-h${hvB(PJ;5@3tfJ*b#PxOF;h7u1w@-0V3$N|O@aeK)(?$OF(s4? zfbPN#di|;)bL(^T-pwBwpCAWKB}g@pTbYA}e)0R3tZBYzhk1a$az-_j)pV=E3bq+(AlO;WQeWO~$`>MMbY*a}BnKBcsxHk3H4S)bC(e;z5(!j9&~ z5=Hrp<(Q>p-kMZVD#z@s=l-5i23a<$hcOS;2nN=&G2TDD?MO6>Wnn&$lj^T}kmug$v zl#T$qIA3j$j34EKrICcdRBoK{F8wHgxNH7ZONtGPFMVbRrq3`tCW)qtb&X<+90XU^ zd)r-jYiLEC{{AL?QePy%@|K>6{ki)>gDLgtuA#wOGbU!-U2PHo8Uff605ZM|C@ugi z4ne;bzyJX#00Jl)0QtT$&bCGaVyl0$-41wbgSBcHD~9Fi!ul+lB#P8MY}AqDK%B^R zT`a(Pj%#bGSf%zC(DFM@rVpayOEozYr42$mPve`-)U;uRC6gYX{y+Wf=G+pzSqj}t zE_Dr57syP8p{soZTRx~@htKmBmeO2(b926yY)n-1CzVq-U74?=7j101Iu%Qwqao*s zxj(MT);W5TBOm9vEXRi>_%%Uqsrv2{=#FtXGg=Sr7$YJ7rpk;dkMmGWch&(E24OOd z^mc42F-;#w%gb;uv|`>p9Df!R80Bj)mQaaC;E3$HEt2Fe%g=Oe5sszyjdve@H{a37 zD<)(W902t4g?6+yW^a(8{L)XXfcOt!^AtANf$@$rvpeJa z2XRe_uknfA;K_vx2Mk{g-iW=Jd`YxKN!1PwI&_mF8&OCJfz7o9ojH4rkbI!-RTM6v!{7-5wOLQnkRJ znheEO3w8M>`kaGqed`-XOPM;7sjF&JpA~`$w8l2jkFBx_j9y=EDWr8|6yM&K`)gOk zUAt3rz~%4Tw4~bGaZQoCsjmW;EP5nUmjm+%6yCmm;dVTh7+K{Sg**g&n9H-d<+3cy z)o1WoeXcRvBLQ3t4BMlVvFVFjaBQI~V@IW)Faa>z`%YNS$oHTo)n8Wrys`-QVt>7^ zy$T0lpkm0O;ReSmgrpa4afgAL<_0` zyZPVK;4+!R=NQYyf627EHPo#HY)y(xGOP-B-8>}QTlp=$P z7koVMpfk!~yg-o>biLrR7?FL^d}+-W-(@!y6Qw5$@pze3-yk2ky4AsBb(fa)VTgup z3-la~*}(AYbXAMDOH>+S!WNeYfeqLfVnljH1dXS2d$*nCui(2KkS z=}jrnTs~A%68MwYy|223q9$NgPSlXMCXmYcn8LH{yju{I1~KN=x6j9b?Z{~k?FIgF zVfsn7a9>4pdeGULa6qHcZYp^oNf4i##KJuRrgS{!N<3yMwHe-U6pzPz9x&@p2ipdV z-@SpIy$4tv1PX9un55Azv)gVk@rfFNwiX#}*p|JI$WBuP#He1_`uh)^oXq)qqC9_J zezC!oc@h9V$ZOL;d%jV&1L36QJSj_7yZ57P>aA+UA-`M^~WC+D@=!R5=si9Y8kZ@nxB1(rg8;Tve*k)PtwJV;fChKgTCZBE%{HR)WnlXreZFNuJIqp55@@XSbz*Eixj@U?R?WqUTS11C0 zTN!cyh4eJLX(?osSqN7PK*U3gI>P7k#=@t!!6r^?LRWAbsEHE{1ES1hq&EY zR@JG~L@9b7Nm%i&1SkFt>2d-)&rokpidI|7z3o99iHtn4T)`e>VZ!FPhC%4aurQU2 zv;tbXiE5YyAQK{%(c+@LVUG2P%Dvq77u(=G1sJAC%ANLaHDnO1%|*b}-Jn$KnbbvE zYV!wI%Yw1~KB|?DcUd@q?Zr^15%v4L8N}?zj6KTUs!$TmCmhdVf62=Xk5kZ2z+YGSD85AnNNitaaO}0MD5*;9lf6dkzo+kH2ks z$~^aw+j`~!uAd545cmhR4CGl8m2S7lvF;EPO+V$?kh?@aSxY&p#OL7zSy2;X8w=ch zsI2{J^@@XN?-)$iaGqr_aL3cKT4zKy=|i@?eh)Yg#UL`l2`DT;iw;4*7(f64FaQO> z0)_w@0QtwuKMduna1&MQ$xA>DM*-aov?FuLs+7hv0gRH3=2c(_&21AW&bmRYKqBji z&Cy=7h|)@FQK8@JVP_Bv+U&Tt_u!=1ez^x}w7(TG`75FYqwq6393DCDooulLmAtyc zu(3(gtIS|QPwN|WahhQ0} zO+yVcCk9`MjQC$qqWlx5%i57+kGcNEsp;aV;JFWO)I92nh6;LOfoO1m`y<0UmC6y`$(D;;6|vkBbqlAyEKr=a*+=~pe#_0cz!2} z%8h_2XRiT5*mq1uDe!-riZ5?jq_k6?&#mrZaHv=l|FaPN`s((q-E3mZbmaMTiSOvYc!?ZUxy;wBlG+P~VUi~$x7v6n(-!-x@sd@DCE`AIjkMKdE zg8L&%m{{CkS*`q=6$q#4^pP}lx=08kDNdKGD2eS0A1o04Jb%r9`Xp;$hl|TXT$`8( zJ1^!F^IIEz%1LJnL;jsjM|FG@BLqm|n2zG=2=aIG_BFuvjey&gZ(y#*c51UaKQn<_ z{4vmMvpevQ_vUxdC)5>HGn-;Mma3#plX$3=mn~RpX;_Fi{i;ro>DIlg*AH#%*hO)6 zqmY|hAo9{;f>5B5$W5 zU^Qx`1kZVfACmqFrd?4SAY&oWeq9ALAEnOD;+PK%oQGQg=s5zK&Do<)CY>=GKz^c5 zoAs&LFlgwdgH!bc*YfaJoJ-^(f8^hcp~mmOO|GX)?t%y$s#YgcS9^U7<7H#TIaXX% zmbbs>6Pws%j1W7ZkJikkrsuk<6-dNK4n!!#kYW9~d-Ge!Qn5SnN~lmKc8p1xQPs73 zrirZQk-lW6ApXAcdKDyIH4Mwy;at;QTaqhm>-1!x{;OCumVB1{x<%$3sNm#V8f2)w zrE|RFPdSM*lAFT{-`f!EfUOWT;t;%zdPmWCsIARIi>kp3{ZSMCO{-Zl z<{v@qg~9`{^-D!-LiHRS!1_S_h0L|ePA0>2DhIZvxpBsB+=cUMM_@`$S7o7RU0M&N z!G}wtU{~KPlo_%TbqyGe^nmotYnCmX=#!AF}|oPAno^T&nMgyNgqimf`&?*9G+Xr00CWwO*qXv)2**(J@^MJ;@D- z_K5p_hk`Qq1~67YmK=kAG=Kmh5+witfCIn)`Pt&^dr9ZE2u{*-F;7V8fhOi4S++r~ zJ*W~@vcVS=6W8@UNsSXR>VX`I#xLsH{{&F`ucT&c9Y$38p5Z#Mr~2bAd?`8h(CnZB zQNswwq8TbJN?u;TNi;lzhb98NLfkfAINF)Hhl@mw+8WsieTcZH^8RU$q5^*m!qaO` z@+8LFs?tnI-w-c*vdDJ$fjB$NqAG_V{7OGCkuX%t2a1O7%||$@8m8kBJzJBRg7gh* z0-~|z1V7`$J8p{0eZIDcib|q#l4HUAo;qD?{zIa$tubAGwg*h~u%h*~nnh~sh_%ur zu~XB@B`Joui2a-uPgntvX7YplCuL3Laeu2Xn6A)li0M9eey+CFy#Hi*WhjlB_GgH( z;bPJ@v#$HqcOW5Wo96?2 z93_0#p_^me{owR>Q_nJ1mj|+TNwKThsMhpzNB!I0)ne>99>+^bo*`ND5(`%Enq{I@ zuI0z*PhyGgl!^$txRPyeN--znic(2zXl@Y(k=GN#7(Ba+c3XT74!Fy+3|73Z-2A!JiMF7Zf5qo7 zmjorfM1y`~rFj`3?cW0ah2=-G-cVY6I;WyH>8nIR^RD@-E<{c#0ypluer#@n2EzNO zPrZmX&}QdstO3v23_j?hZ$9GDk&&WDA3nOX8ZFryRu|IO4O9E9-BjF5x%pMO@GzES zzx0KcS5BDI8{C?UNY=ElS$8p>v>CA+(R?iDM;g?SODubkN3y6_+Bd&^cVhM~)v7ReSg103+5g+0-hDX6v~GPB zC&-Khy6GDwpHv=Yi&YPMp8+Qk?K$6bthgot7B$QKLZm41r5wo|IB2_!nQE^0K?%qK z*u3Ko9IW+2&d&-b0+>qqzy6M+Oa8OkUIR?oY9j*-W z#4Y;~fk#1I<`G`bJH%b^cpc%PH$rA(9hNjh{V-j4p3=tcD^>8pc%MyRSviB*#H!+I3*K-#BZ)qSc}Yj4-^ zWC?&002%>^5&$y82`Df^0ak^-7(f64Fad@*00Lkh0QuFnr>>PL7-jQ`9>e&9j0*v| zxV{MptRjKeq12T1(zGP7UxoE^3cz_E0MTE%sCaTJf+SV8lbmH*&`5%^=Z{9@m4ul! zym@{dU+8gQ4MUBi?s{+7+Lu7M4Z5!QtRJSGG(KJd{}s?d1%L!pZ1Jo4f0b z6T`XGM~>%85Q#w8OG|tO;Ugi7R2duQLF!Rq24Z-`VS~%eT61UyzVDcWd^msI_xgv|ls{!o542y_3U2)W=_hT!uD6 z^=sXJy_@?~_Gvhf3kMn)8@5P>xD)M(LXp*RxO@kh-pLt*pCO{^L4G|#yf8gLMyKrP zD|55U{Bx(#mT(GXCvm;5JEUom5~e9@vE@^&@Y3+$IB$OtdjH>n!pp$Rhz<8T-=u3y zI!%_l&P?S=``4DdFbKm6vzM`CR5l5MEAK55#yz@g{m+`o{Ai#YE1Ou2SSv!S25mOe zBk{yhj$L~W;Idm8smw+$$IQ`s@l=-{{3RcJe9YuHb$ehbsoyPR2Dg)Q9SFzM`iC?%>XRaP~-nxR^#>OTq$jPW`A!Rlh^?m&mRsR<&i3Tg1D{I&6H(;!g9d4YBN z@Wk*%ncFNVieImsm4 z;&jG}@&?6$aDOO@;BYj3T7xHI(e|qk;-ylKhnj;$zM2E5b;#y-+>tO{JGy9qZBWc4 zduEjPJP6PJX@bhjuCepqJ{oh)fLiRdNu6}PhRV~*IMNLtsdXCiUA~ve zSAEXThEo3-*2E#3h5uMVn>|dtJU^~L+1=ZBf@Gu!L;HX zs5^k{@aR)q7`6Dz1_-(>_iHN=>B<7sQ(0*OvyamGgq<#hZ<-p&C?-K8=sdo0gvR;G zG|$g+A z5)M*G0(u#*i}EP!&!v~5ij=t|0iRO(Lr_LTAbN_^I5qLR3!IQ*A^RwvzGWoZBg^vBO&?D=0j|B?=74C3alUP?O5tDq zN46$U0`BXVg=F{ETQwARtEcVqj!@L=vR268LC|04{DebtGKdB+UO@t_3w|_!03ZY= z2qQ2608InH0QuB_J@k;d#2e!FJwKQrgbo0tKwH07-T7vjxx_H8TY2H$nos`qioYtV zU)6x2+OoXNl2s!nF-XlSmzL#?1Y)O>T(0VJCD8g_P)<*rGq}}QCf<< zGE9oBxm{o~<6c*R5bNfv(zy9s9izzEIhj!LwQd<1*$s^kV5o~Ag+8&16`XR9i5VUQ zeJ=$+7>vS1sk#zr5n~AC|6IXNNFCVa8Gai-uQiJ2$J1Lg5TCISd=wl>26~Le@9k0fw2Ynu1E3Y$_4}YpR_Aa&+6* z#A2u}Oilh_5+7qOjuTwP?vpvS71(%4G3))b4=Q>Pm8rZW8lfr}2#3cC(k-@TPP8k< z6;xhGBy>w!LBrw%!gT+K?xfy+0H9IhYZI^u$W25GOd_;1=XWl)DxYof(Gn7k&5~jm zstMNnndf$kxVA>5Qj7V$pME1ypzSi#9L$Hn7weZUQKTA_GahLs(#=SPYM3GKCnh%5 zcCdYbKfdhG2>0HkIl7(b2xRDPeMco%60bxq#-p;49pIC_u zXHT`W1vG8#r5#Mg=T_)ciZ9vM5*$kU`N6f;%`0FQ72NXi&5X}T0AsjKQ0XfrmvHm{ z7?XQUxB0DFe5Ow9>5afa-_xW~VU3`t1}Puf`ptrlwaqoz^E@vAZl#{*RvfLs0N7;^ zI*$3w(xyG$j`xX6Zuqlnuo#Thy?q}oRU6*i?!P&KHBwAzMFm^A5&#+j$PxfD=>;%i zKv4}#el&mpzz`(_K|{fy0QC9hbFYng<1@6@n!P0#PBsJye#dL0p~{h9$O)SsNKa68 z&sPAGn}Qu_<{|}f2C6IVJfgr+Cj*TxKf#60Sz>f9+e0FZsVDbY|V^`TH&8YwjvuR-9gPtJp7ENs+4Gzk(x&l8pAOATTWg7yURiE6-xln0Onqq%husu zZ5TmgS2nC0oG7PN*5&ejKgqtpp6s9T9&)u1_^9%1p;MD+qY4p>BT}5El!HqRjDoI< zK-VH}IH5heB7TNaKNY%|2CQGYuHG+ZCLV+I0jygp&iC#uQ`KDe8Xvi-v6;cVwWlK# zg^%G}ekbi=Mq3xJEMwi^GR%iNbSE1Jf`p4cZ^!eLhqf$8d7uGzqjK%8>?ImG){3n4 zx9?S4S)QlTj9`cPk~hZko{nr z5pJQc!FYQL|3%^@5=OVyyW7+1rIWT{k1bz6jev&@gjS>`G$CVEU&QWQD63o(x8<=E z_I4VKYL9@CJFnFLG=#ts05TOYWWoY1OGk~s2oL}X00062*_~Pn5&$ymBp^O$0TZV` z3V;9*#1Q}h3JTyL0iXc+xh~yT&q>p|QTwv^v-aeGtN86_x05W@IpSMr^Z8eu9hQek zQA~Mcny9NLi~7QLui+xqF=SZA=_$H^`Q#b3PuJ0nZ}n*u*jI&xB7GI3z6X3zjxqOu z$gD){knnVu+2n5!J1k1FrqB$7xK*r#MyW<54&*^?b`^b=+@zG_9={i%;&SrB;KD+Q zafp_~Rbp!G1jZll!rhB4zQ^?}l@iOPspx=x(59dhR}_|zM7aYHL{X8p`e0v{BbW>C zt8!-&-sT5Xy#$}^TZ66*At%%P<2p}E-=+)!$x*kNu#=BQe;99Sxp&?3+5iW>NOTFe zl0)*=8`4;%uMzxW+^`l5(0_+uWv1zk!1@&uJ&}Texan1ZpDat8w7F{BC z(hf5Ti&WYPPL`~%K^!})L;!DZO95w78f;Tqo|#gpV#6^vckIpbyvweVin*-~8j)-} z4t!^qs|0rlE)45jozE$zz_`d!mWOiBFM$A)Pml#?T=FCZ zx2t*v;*tk8NVj{&P7aWto{Y&kB)82)v`ghA8GA}yX((zg`FzKKM2P!EI}%wT0krgR zKd(8eE!K1xo&Y~>&GQ4Co??hL+2Fx1`}lQ?m& zTG0S0lCTk;y+-GC{R;iXD>OkDp32wh4m1|u^A6$s?I*1<-fcri-tOSzH3h^N=;nUJ z=~xoA*|*FDPc1*F6C>WOMDkN%#uHrW=XD;%1;S|!3P^Y>Bbrl@nYw!s<~weL7+*0l zUNg(9*(su4g3oyVxfdgV8C#D>^@Q~T$?ZqYFquACCn!>flfx(Pi&?vgJ)p6gD();a zw4satyVk!xeebjQ6th?Do#-b5kN4WG)GE9f1TKJ*hMyK($7N%7ob{`!gx#YADpEegU9hL0MN_oE$wE^KtqPyMjO zJ}fGfd0VqdVBu(m6ZbKpLi4k2MCFLh{9;NL97fOq5r|VwkYBQMcJmfy<%Caf#sO=M zTHPJ9Jm*`vEwG}LCg2)Ly6Eg^Mkykjj&7nDhr|OlMD~db!@YNQD&8piNnwpPwxOt# zoFh{;36u{j_8ndU_uzN?rpB0Au~O@6@6LN1nSqkw3&O)SNM@HGY7Yg5{t~_qD!Dl8 zm-ga9SUB6&h;teWEYNnYAILhzND((YeOR|Ho28y$6y0f#;|sE{o@qC0xsSm7EaOo0 zv5ta_R=8H)s1h0L8mza~rxe)qgZCkrH{`l4&^AZg$i6u0k1Ha>6PVF>zBaiuHJWte zb4lK6e-Y`m%YWC~j_~X@V%c%q0sFCWR^ZggNAkwmbErb}#tytfvzm5R&S6|Hy7>%+ z5=>Mur;+4a7(P_#M&L!t#lW41{b^u)FjDi6)+4$nSN>=#Qi*u|#=^A*r!sP>#I4n7 ziHYLeC+qLpgt_%>j*Wc-pdOZkojj6XJ^F7)8obBJ+Y-IFIe2bf1?IFu4(wMq4nbI+ zS%!+6o>(q)Rt?4C5TWbvV$SvKauK-Uthd@fp6Bg)#}?)Hd1S5xYqTrdHTmsrVxryA z@+y$)YKq0TJ!PwFai=IXU$M8Z7td(v4Pz);-MIo^*4VRzgw(ZU%!$BIR2smgQhgTE?Ym`3@y z^@5Tq+ggB?lFLLs(aO6TP`$6)~}OoA{ihoOtd+Ps1H)x47Z_asBZ`-%5hO`B_z zAioBKur_4WN!%KRlL6M4R1|7uM#1Q|lsnBPCo5Qk63&Z8v27y6KyX5xENpHz7Gi0D z9<=->jTBQ^@T!n%1?O82kk?kmn`MMR_z5>0JKanx9w2YpM!TUnRGva9XoyR@#kXcgONb*ZHH zHCjjXBO7j0y+8KsK;O*Xw!A_oIrAun%(e23^xFfpj(-jQm-m`Nm7nbC66Thv1mN2+&$F2T?pTbxEv&j=af*c9%=P;OD+u5MxXx8HB| z+m1jkyab-vl)|Zm0@6gosFgvQrwtCR!0}~$C1+^_*WXyT#7V1YbmsCN7n(jcvip4k z9yQIusd7D#_yWY>fUl#J4neSTsq?h+u#j^xJKY4B9kI|6_6cV_jpD@^A?6axf5aln z{$0NmoaI{FDq$>Jn)GY4tHx4q1Y*#cLzwG4qeHzvz?@cm=Hv=LglzOhKdSQwfX521cyae6lJx&(gdn7&D zLp5hK^@_yH(Mz12Fa_K;Bm))_s$>pmKzAlt3=qDdlSzx(Js~47NhV73FoSC`lU%ZwX3}%PsOD+ zihqj!d=_W^C!@D&tP^1jS!}EX)Z5^}>C>V3k(a0kh7yIyF;8*9oIxbQ#EZbnYDTRv z-($gkA7)x7J$--P%+~!N$dW0p5+7&yise3#`XvnR9IOzGCYc_`6SZrIE89fu=RWEh z;pKShT6(Ry=H|d5o_~s1+m4~Ppgom^C#KpQ24Tc3)!DaxovqPh4PbtU-^##Z<-OCj>M5J7SZn@WiQVq}%RJSXyJ_J{UuNB)0 z%H^$9vWNHD8^-PDtWr8YCjh6@WmCkDRNYN_ZoVgorkgHsB#>bI#dvO50V3EciBIhG zm>i|ToHIU=yNqh%heD!%0oxPPGT7|S6I`Y;ep>d6N$?HBSIfU;3b%e(TJ$mKXs z$e;?q4l%aSQ}-RNd6b*F05v^&Lkn)ORh@5sBz?Tv`Id$-LF2sTtriZrD)hV6`p;Lj zEgeE+V@r8ApCry3iTejYvZ~uSZwfD; zeLa#{u;=0bjeQlOKg!(n^AdeV=hbVY7Z(nkkH0uFT_mq}!1_D8{&JeenylGxUE2!^;9vbydknOP;&f!9n($xbH1D#M>gGyrB5 zZg2h5wzD-w!L?98?rk3t9<9DuAmdj3s#O+$eeLE$6eQs45p zkuW&F`XAj;sI|#XQHwDeoXpWsgsuQelup#1|+?$uM9(la#VlW6Y zD=(OW#2(I$UcMEqTue?l`rU*5Z@b9VYE*hqObu1?YgSiNn3NR}OB397Ua)JkNHaS@ ziixr39$0XqyNl)jdOU{@z$Z^$#VEL=TU?ht0FR)5EMgByTUvZ@ToE#-j;;3cwsGdf zH^HSL9r);Zpyg&p@-?o*3RvSP9vljRnw@zIT8%Sui9<$Uf#X`jEFjJFg;auOp9vde zjYunQpI?xo85dTR#$^`7;;Qi2r?y^%K;c{~2Uvd^ znV0<)`|Zyj*UI&P5@p&&BlRH@JH33i4qrVTyIDO2Gh|MRwJD9?OR*=tv%&1xu`^H!#5gm>B!sPSwx7G?%dq{Vu>!fs)azOq?mC%* zcOk=C^EEJdZa<2j236XA(w37KS)VHUr&(#c*(Zs1{rz#E=+!&=b98TXu<*=V6$xn2LPzc zyC&=?Gqf`h=LPRUP;n>KlGA7LxCJXn@w1war@)8e(H%6vB7DFmBK-YGc_azy5zpas zK5wpCu}(_pS3>{yuZ^)SA~{8&vDw|#dkpmk=RRVg=%v>svi=A?n2U`c zzJh_oVx@^L39>*Yh)2TfCLTFivp&31?GZ0^^;O#q)1p3246$m#wKpoY`EbF1HmFr3 zVLFP0a{e$ewF?z!l>&&-P^JdV4)@f~sOYO?Vg&srCdKkare8>4>@o?X+5bU<4O6a` z9q@v7F3d1g>W-TmuJnA7e+@l?AQ;F4Mn$BUkW@~h6e|?M>_;pca!+Z%P>yug;9XJc z2|SFKKb4P|L*H`PM^h0AqdP2PKhnRD;>VXHZ)JqiF@N}Hz7xC|NDlI#^|W#DX`&9u zJ>2;viSts9xvYzM{AzQaWfc<}0-}0RZ%wz^ELtE_bN*!uwc3R}(IS=?SKR2Y@HwK? z-9fmy z(f-Tslv6@NSJ>3`7o$ypL?rT)lPYN0cE7RfQ=a$QsW8V&{b&%a;qt!^=UcEb@v*HT zAvjSgf|)2Bct~cl2zz%g*dQ*r9DrHuXf>bKkLKJlQI+edgcz`%7&10OuE^3ue!XW| zO4$j-AkVM)dZFe87G?~;o%^$h)ji@Om?OEz`^z=}lJe*9!M;*1jWCZcwu_7{0+A1N zg5tCO#_B3p)q@_?rluq`ffw=1En+Pcd6%1OUxBo>UPuVB*3J(8_F3{`X-*j)P^m5g z6SxdSa#5=|(oX7$e^jsF9Vd*Hy?gzY?o%eX6h`GFW^WV9_3tr!3)I-=Z#%DyJyL)` z$zpI!(c4YyDd`Lr0G@T)2wQJo^8ZULWfIsBGO`XRHUL2rr#~1#0O3ay001F?z{3Hc z0Qs-+uFD47%8bnvS_;}A`G?9n%ng3Xn>@s=FV@?@PrBhYJiLQVBGm8)GQZHB%?x3@ z^WlJyyRbo0e`1-_VJ!(>ugF1>xkpB%-kq#TI^TP$bYi={;cv^djaPBBDHZwzHOQ`n z_*8FM4pxWt7P7IE0F>3qJ}7`b)djuvS`EKe#fh%L%oGp;s2|Cn!9D7lj-e%Gmc>Vs`TZv?;$GCk@-DzSZ_prXcdhf- z>x%H^_N&@g^^uB7_(2Tam5?%6%oRd{jWyUvrYpG#ydcI#|KXRsm*Q#-t(&Ua%VdVd zpUqWTOE8_%R4^BMTB;X|9xtHE+qXL#@l5WHfRZgL+-I~CJlUJzTgSW5g8wc;L1AqKA`EAN4ej428s9_7#{vIAzov7MU<40cFe53;^ONLx4>thPW~M_j1K_1R*9S#5AS7`> zrupU6MrRavXFjmUuE63>P<0`!Hm^9%fVjSK2SDHC)dYLQbx^ zeut}dp;452Fq}k49cz+ny<|L>jzBB%Nacdd&3cn;SwTq>%Ray)zb{;-#3u4N?9b8R zTk)0l==UKr&ir_Pe9iRx8OK#rf<=;N?I?Caf$RC!%7LVsEUX zzY(G(<>vB4wf{G>8mO#?C^G^Z`uB)E zG4|`J!I)^ZrqnEATS4D(mr}V3^KEtb#3Hi(-ss})Qc5QScF*B%bJ0_D%Uh#VyYRYq zqSOhZzo_d<-eLPZc3kLR%ga*tO<7Ekm`^b5Hhp$X=YopLWyr0&hmVNo;RiDgZ6EX> z8T@4@`0-zPFI-Wm&=H;a<#k39j_Du)*&xc*1}^8qxe9WN*Dh_7FcVLEQ#5a>oBf_* z?~NhHcN)I!`1pPB;RJwa`;)=5νCs=7w`c5jt+c}9%w<=!y4*$l+~A7)@K3;ck^ z+OmdW-!S*&{XSZ@bg^HR!8f^xCX~>*se)_me5ddLq;^{kt*N<9g-d^kL%ThPefvPLF&Ul*_V_4r=jgqHehhB^0wJ=Iguoe$bsY#wC z^p&R=1-*sht+j0a3{FWSnGh6r{_Yw2K9*%Bp`!}B@L)uH=bVIb6PG2**e~r=W!OhY zf#yeVe4-yHaYkOWv1E~oM2qltXkZI@>8`P>=S4Rwe+clwA-7|S*AVK=pEt79Rd^2C zkqN+y&k*NpAOsdZQ9T($0!gl!6nU_Nf^PxUZ9+g3Ljp);!V@lo#h%%UX{5SjvIK-h z8dLdRbfaQ*Z88-svR1vK<<5<

    LAyNF|)z45sH|S6)%NA?NJddu#8Sado9>M-Rud ze}7|21Cw{{fY;{!%v=wi_=+j`S{aAget$KVVh-w}H7orWu-L^)mxbjw->D$&Ew;5V z?~`=oHNpg;k)>=W22~c-p(9)KQdYy~5zL+svE_eajz$o{j665`Vp*X&utw9b!}Sfm z<)O1aGI=kcU!miwCKzx7oxtm174!03K1IDa-cMr~kkMeX+KTl_{=;m=`SITl$bS$6 z(New5dL&QlKrn#4;GLz-`TE=+%t}Rk%*g`TQXY$%766Fh_&zMVPbbk1hbhUVp4dMK zebxaDBBQZ_3}TwyL+{;^~kLZ9i)4QdWMyc)WO;FJqa* z6R((~`8 zy6fR{M~WsuE$DVWgs^m42%cHrKwkZEfZH40$T13Mn?wE(i6^|$E$_^XZF(Cm*I3!yP z#_Lq0X}tw>zH~c{*^45)g%@eo*{hR4BDl3}29E;mdx@?g#~w+51nsF_t0Ab z&d4Nm6X&|#Wom=*4f?O84a7PUu1o~WP0%A&eOMpbQb+ND+Cp{6w@%Xg5Zk|kL{^5H$K7%`6oiv-e=iG z2ClI|shvuPXF!Y459AwSy_WvmRl(|EP1wk{JYVOL zqW><(z9zEc-*o)taAnNJ!}h1~%tq<58g*ZrA|KF1iweX3?ntX#;P_h#>(2WYQg>ir zFK91tods&ak7I(%r@l1mEDn@Sf=mvM!whw8{*WQypP<8C8N zEtX&z;sA@^E zlEFX;eS@vfskfMDS3M0^Y}F7!Rob*O=w9tFz#B`80BI7QT5L2Y0NeE49gq$a7BqPk zz{QN&WKul}zk`PpkIuA>G$;|%TP1H)JND@~Xco6=3$wG$U~bnSvu>iyU=F)0S^t-* zO;V$0sH$wpo={35G_l*#1>R|T9tUdPRc44`c`W6xDb&J@*}B~svt?i>?UFYPN7HAY zF7bIsxhIq3fgKt&HvkAD3?&Q@00Y1P`PFOR1F)(_W)h)Z7vHBb zXiVoNNm2c+KKzM3Pay~Sz^&J9@KrThXGz@Z|InC1b_W|+R@DPreK$;UDlRCW1Qz(1{4Qj##Y2h6vGJ3+ zrbYARcv%cxxq{LnEx~r2>@+-fdCWj4?ZepHB;ChG()3Eg#Yo~r?4>hl2~hFO>1rl2 z*QOxK$~xFq+#locw2#C#F7k?)?!VEtZbkN2bDkaRNu?m?U%{wqII|A#`GDI&%P@peMP0sjj+y+6>C0f3I(``Oh38rc!MQL zNsx(uOHATeV18doqjh!JL0BATFtNxZYdC|Skhdc1ukYF@8>Tsed~5&h=T1Rwm<2u_ zwaY08g(zbowX^0Cgv?G--7MF5riJU#kU|5vit8Uff605aSKFlsNJ9MOs~ z=tD4pQ6f7?f$ClrT3^4ktQ%RTMl8R5x=jU!kIaEJXDb`4MX=e5^?c#l;JIZ>2dg&< z9|E=xi)EF=@S!)g8M6El!X3&6X_~TQl!R{_9rs1d?hOQfTG(#OG`-o{4%ix-5WAYi z8QG{v6$C`_1$MR;l-7*l+Xw-xPhC{GL44};LrTRx*;=CwAuZ)7jg5H@Z?C`*vzs?o zmipGNK8L)?d5k$U>0AaJ>87rg^>n%^OLwi)pc_dIYXn;AFr?q0uzK6fNx@K?nUQ11 z+Wo$oUP&0uN;6#65IM{F3Ehs-8d>ZBahTS|Q&ocEPijq$pqI z+mZ|#b$Z|XRi!vKNvt5fyEO503e7MDg<}{-Tht~yNTDdKM<$W0$u`z_NN-_cp0(s zXuVWV`+pyis*wX_oKXJgu1Hn(cA7i;Fh?N#9xF#;F$j~Jn?*x*jQTkIQ$fH=nepS( znjpFA52#jv4kH!DXPpu?L~wf7onb&L{wR!WS%YskHIP>V=iU0+u-R(WjeDYHfy7E1 zvTCl!4lf35SoA=`Vs(=)`n_& z0~#mZh`2ssy$fi+GKbpKKxlEnfSYXj$qesEMWD<&c^gt!ypK7`^f?uXcdsxY+MfFL zmq;AJT|w1G$SQ}6P>{qR(Jx{l$QM-%OzJUfim+#Pb%VzC;Zi+>^K|-gRz0FQ-6{eK zDdLPu1+1EBv65F8xQ{J?`3ybzTA3&RzuXs)~6J9+;n|By@ue^ePVBNo|{F^JgU zKFLihnDLzA6Zh~(pRnDHO5I!u8WqD_C^-xPf?T$f^>Mz_ZS@)jG2fvK`A?<;NvUZ& z6D^N*za)|qyCWxuRwr129Uvban$G^F0i|=^yWB6mZ%N95stpdxLwT4)=~&-25U&;A zEUn=GQ9xkSR=$!Du~Ku#)FH`4<5U*uOseGaRWC+Nq7c|ripBfbpWx0bnA2sKbITXb zdo| zUKOWh8oLR_qa;pjayoq&J;}5H)M{oAAsN8teV7nB<~arl4ZURXDo!P7{mSvnJ%R(| zi6C|(W8EJIeJ7@Y1n2I#o^t3WqAx$sc2%|Kq<)u2lPVLO2Az39?7@spw|8CWa4;!^IVDm9u--ff@|p!o>3LDcV+T#jVO;NX1RGmE&fY zN3eaRArM}b^*H{Q&vL&J8}`Nq$r(>Zb)XwgDu@;6fD6|yVpqiAlpn+QkqPxW;w)*~ zdi~75H#=l>ZQGR+{O;Jau%Af~9>(6)yYpkX9%I(Rk|Gqo;BN>0VQg7bq(6q0Sd(_P zGBtxT;XG^6Lp52Gp48;)vyubm2^nsh#6F>8!CRU33T3R_+&N#0y-Y`EEKz72p4lnM zy7`PWzL_?J7h{6CBBAzGV=u3eRX7<ycD)Y^fW(P(+gzlRA`G;s`;KUb8ke^jZCve+9~ZgJ zK!jSbZAk)eGa8oQu%2x7@JsNXlq+x}AklV0UZ_1OvN&3@MiZqYe~^zW*o{HY8mABM zy@2*6X8w0886zDo7?6=k0MxP4Y8}A3fQ>^E^=549tO4B?(rxLlGT@Lym&)OgiuE+v zc3*u{H813{>ZL53cDzu{7d@Vm>-x^dH|*0R^Yf?85#wyU+KlPyT~Y<_;XR6c&)994 zBv~8fdUb=sWgM^ks2+d^I`v>&mowhX%SKQ)g&VX4s^u284ZDAs$OK@0IGF@CDm2)*b3|6#MdKndI|k2U8|>vvYosq`cjtsT3E@nK zV`PznZwCh4&IFGu#1ibLpps(5EK0O1Nd)e`Fe1{8*_AU>?dbyYqeaFgr(4Fox&#l zYpF@iIwMZO?5z-D}j}yspG+$wm}QR*yxd7o>Z& zhn6Dv4^Xlq0}-QsJWdxq2gPYz1y`fu=v|oT;7J|ZJGu9tZngJmsyhlXxz>EEtfU*E zPWqa!_jRfpr)1{tih;iOXC&Z_V}fqf+=GrXlta(DW^te!>*#}}kEqr)C}CyBMccRZ z3M}tpbJbze?*JKdf%or3bg+A68;RTQg~;yfbC@4T-C9CPH-WuEAz9QTrn>Jn+} zk*-f)Jd}rEEbKorYn^7I+Bjmmitld=G8sb5vUAPaxyG)T7FSyfP#%~Lmr1PMQ*K4o z`@nd8+`$1lY(24qvkgmdS-?4m7A+X5YPOo^EMKwaijnD!MNyBJZV`W=yP|x!`dF=X z-?kRE?yoz1&DQLJB7Yz1gdc=f(7b79VYKCj#%})lkd$JU)L8ZyE9UU$#Xh)l=sKi? z`+h3r5%&UqjQBerqkfSF?pEX_8T>K26eM7yD+m_wS_{Lo^`i8?3?CRKK!ZiYD!B{U zN!i^y?Ym*Cu}M3|?w7b0L z`O+1)2RKc>Juf&){K2{0g&?UT^Bmib7A-F+9F15vY2*`5wzG@e9&H_#=b|NYPp+0e zsn!{TDpTrYk`;CFFZsYrRWL5Z9Xm9Z(I?Wc;~`QFkDSdy^;3mSGv_Gc{_>!91Ix_X zX1sT(oXDl=-E6**uxSEcc*7(0a-Akt8<{{ahp)AV8Ai-H&<2M)c?ueKkItnXLCmSN zQTr*D7j?9!T>%C%S(YE7b>{VVD?EkkMDp7ifWb9#Ek(jhTxOeKI$j+|+((}Rm{u+9 zYz!;5{h}9)(c*VXB?&$$Mi>N=| z1vQ?}Cto)K!63iUI%1@Yio%?8iJbU?7InnhF(2aQVbp^O+!%;b3Hk)$2WiC_I+}}j zja>N_5S-Q^Di43|d#3Jp*sQq|_PBi|z9~jfrkblcphavXX}w{+s=EokP))}h0ChVF zb{g~35J$bC8gf@Qxe_K21k7NdIqyZWb(;`mWZ%enBfih!>LHF16WEhWSdSo_(qy;e z)x;E9ia0XR#H)RB&^>h}5)>)|1iwl$gvF0LypN`cylJk)8pqhM@Y-=~A{E(<3Aclq z@Vqq$Ee+pMp}z`X?}-W=(j!4*I5L|CFm6Z$R~CLZ002M=B?J=#1Hb_J%PiU)7g+>v zS=ChrqxtrY6dSbzFQN?S2OY2O*%WEi9efpC>KxcBLLim>!!$M9s zNX>V4HQ9MtCIa_8CT2Q0cy?*Y0F%179e%oQe0#`t(OR;0YIWwzk@`AQ?dP%~) zVctJhL`SX9N`guR<5HxS8qDMtb@<6;+&C)!c`usg=X7p$ zu#qx~tWEl_;KAdnzWnv4t~p!JvAvgYHjTD2)^Qt-@(rC+6*1#UiH&AQT6(V@Dt)T; z?vVwX87)BzkED;M^h?^s{Z+ghNeQfHij?V+Jv!5Rn`!pQ4jf*chlvjf4g}_j*a7Sa zrn3KNoIL&Vi2$vy3)^^IEe7O;+oTH$r4)_1+P|bp8xN_OG`#yJCdiy=MU$TA$`4c4 zNpxbT2c-ww&8MS0@ENd!^cKoZ0>IuS~CUy17|Y+yNA?(!Y^SktEdy+98bE~=Hci)G%b zst`n^d7jLV@~m^T5N4gQ-A0#D6$9tI#S|t3;?D9L%D#Ts``v&N02%?v5&$yx2QYF# zl9m>JGXMbqm?j7y0YDJL1Hb_J$ZBoDRODxdSb^V{P7KoFv4~yEV2=kXfktLG&!pt{ z1*AnpKu_MSb55=H!56)qLNug3IRm~-f#!4)j4PY&>G3&Kbzf`K>nM>cRCf=QHU~Nv z(V>1UM%-D2RUH$%xD3gWVUbG0mDAn;y-^oej36MT)WvLOsj|NDDx@8RvJ;@wag)7p zh^q@+CiC=&t?g2sWD-12FT68iQIwPu{IwNf-%8D9Wi6xZSl@^x*1nQB6hJAc;9B)J zlyi-t1W=X6QnG|0pgdHR0&C!jOliJ;=UlTJLqXZ^vg%IfBBP-T5OD(Y zO5(%AA|Y_RWqy{}G-_zS`sBX~YxoONV!B}J)*mPZ4*&rsisr}5eKfi$zRG=sY%ozx z!kZrfbg21!&X%SA)6TeYjcDI7JO=BM(A_?!9_*N;2znaXZ@HAODV}{F7*!7yStzW= z!Fsh>DywB^`6t5<^kZ za;XAk{zX&$_VfLGUu<|&YyKA(ajUp$P=bbzM<@nTgrZq63qPDQ?h%{?)BfcNp1`pM zc`MzOWboIL^hWq^97rn@VQP(H(zU!&`>GWJKDjlE+v*;N%M-T;!!^pQO@Oo`Ng(-T z^^|7=`&*UuwKd`?cLJtCjo=60C{&uv7sPEmaXzf zykKMA=HBkW9JbT2_`!Tz*gmp7{RLV7e$th-U`qqQO&|<U52Q9>)5)XsvOJV)mB&ERL&x76!l zCA>-kw9@N-H6RgHk()khbYAmnCOok8Kb<*R)kdym$ZghXP0& zf-}iaw}#90aLp4nEH%7A+>p-fTIqfsn0hAQzr9yVTw})oarzI3vgH2~tV9^;=sv#@ zF#{nNbPrNN0rHO&%=7nstr2>g)76{J!>dGkk^(IREyN9(j5GSsvv=N12qJ1cFlF;b zZU*3_WC@pZL5weWl_NJGBNU`rIH7nF02%?P5&$x+3Mf24Bx4sp8GrzCMiU4C0mC66 zf#3l7$XUsuY9v#xD@s(<46BZU%HT_%2yk3iXZ8r+GYh#?8ccHU`4d2~R>Kx_9kQ!T z(B*$Dfu+Rj%=$$T)NQhkwy=dvh7y3IN|9?n-^X68VAeBr#dM4+Uc)zMl<2#PkM*x} z;FRlH#)Kys8m~A>dQ9_!DmFuwnhM}6W8mNq9~3VhG=ibj4JC5j0Ktx-a6AgO0O}VF zw%kAAG}zGwDq~DPB2{G^^;l1;-OV2x3scpPm>}n27N~6z0unfOXCVW9#-GZg&;ws6 z<+Z((_NQXn7xNh^68Qoq_*dKc$Xk>K2yXohaKUsKAFHL(d2E*!Q zRk!<4VSPbd%jH50UxQe~HaYffldY2m2#0rx8kZGwi(-=U{l6vz;9Q z_2rrD>6SK1^Rb;m@Grpt=1oLn&;nbgWL0l94QpIfw;8HKp_n9d*3ykA_Tg?hx?fcd zqhT6$3ymtv3uF^US8_$pBGf)MG^p(kEHd@t}P z27}DvgkaXGE=CszwUjJnZE75lX2r~@eK|rzLUTgwEY5gcoIIATU7K4U9T1cC2>TY{ke((VXe5>I@DVLoIqp0Ll~1{ zjg#vFV(1@F#mFJovRq~1z|7g?(HVdR)a}jn9%gS*htw;PkX#2$PAk^X=M${k>YCsV z)gW>v+yky7{79TE6UVQqvDJ+$RCf@1PXwx(G_chiX)3>+V_wF33Ir8BxpdP$GB7>6 ztE3r@SbGSbtw1<%DJf7HYh4AWb`jU^sKXa`Ypwk|on{Xs-_j-W+#~$cCcHg;BgJD0 zJ0@Y+)9?i4G55}vR|d@9I^kGyWy_>^H5w)4%&pM~O+wjQUM(Oi%5KsWX~@be3|rZl z*PxY$2JDjr^)InI#QCuQyG$g00I`^}d!&GZs<-6pt9Jv(Y%f0EhDzVxAy}Y+z%sQw z)V42AhJv^W=7W-i7@u#R#DxM z`K`H@JBoo#1Yz

    QCT!flM>R{@vUZ9jsb=QD(I7{=0+ubLmX!5b>Vipx)PJH3O2E zDVAapWojE~>^+*+%)6(Ql1j_r2x0=9(~--DJ8R;I`MJeR0sp$*Z2^i|y$r(! zM8Uqfeg<%XRnnk?w! z;sEqyjQ&cF%t94 z{mRd>eZVfcuJmkE_sVFtgH7kulmz2(FZGB-1)*P$49`-~;(o(%qtd?rYn%^uA^GR? zB;2!?-6%^mx7)Z%=Q=PSfBo@IcR{cgp=wPLiE1>EE*@CC$80&f4OCzIk9A@G1)KD= zJ~*OYScLC-hQKns1~7I+2*xgcHvjN~7Q#0J{BImf*tOiWTO{HlgNi>r|wzPGrycZ1iqZ8GcbU zGV7#V_O_40`mkrc(xZqnH=UDVw9 zK5YQYAhiMCg&m$dfw7p8Ik`1gG(89-$8`P}la^`!0@ANDiw_+h^j zEf5R%ivUj)t90o_Y=e!=n}u~rY*K@wu|CTKhNh!Gyn=?-=xEhF=cCrVt9eaNUVzuR*bB?q~6OxZc_M%X2g z_ETA`)~ms`%a>vBSnc*P8iV0E{Rz1L`>m#s6lo6!b*CB$)s7QbaU*e7kL`D?TqUOr zad?~TytFzYh-Uwpn|@NjUd{|Q?>lp^I|?6Z za(LLBYU3A|dypE#QC5FTpk8wC1jFK9G0CVcsC(u|HUvQMz2PWKN>$@n2P_Gzab0oR2 z5r5)@G$bMM*b)F50f-U+GPedWdO-pvZhkiaAq0#j6hHwG5y%6;0QtCzT%QTuFM7dO zZ?Hl3AvZ#IYJ4FH*4nEDw{HUuVBDPn17!*q(LQWL9@!5EbpJ~BEQ*Rla78NPEhJm< zBDOEvedj=zB9r^UeXfztqIK~U7`dz!CG!|?bW&XmU_j9%vTC=8GC0Uex8#>! zi(2HwE^l)~p&?@1IpzffEG9H2Y|ttjSu-p!4TDFqUL9S57lR}^(q(ITh=#~)#HE`+ zVKJ}0|HkzX@)aK0g|_9^`OuHcRGg9iupR%&4+r?Yp0ra(+^VW&phQR;;!3eer8#hS zUm5zG^!K)Q9Mq|pgIX;?dvM?kC9*GkDU6uZ(ijoNokfVz4M0gWMTsbHA7l_>mt#gJ zxs(cZ{3-=RE44uyo-aD;>%WbPWdA@EgPGGLLw5$n0O3>-@##hdkQwe@qJ>nzF62cf ztyZnX{%HhSJ*Cjg#B#UJ*GeYL4a~D_@PE`3MUf&2cb*D%)1R}S?g{UBB&n)tj|0uM zs!(V!Y0-H<*>pTSKhZW%t8uBfXHFxX?FVL0`Ryu!MihrZ~u^krvFzOL| zp(d;~dFJCUw0}tc>^i06NhPaeX`LU(=o^gZrtO2wcO;o&q30N6{WxP~6hSg(mByoY z+o+^@tt26qk1=$&Tmj4!yEEf}=k6DC{>9zTz0P)3FF}N! zXl)r(LZ>w~e`2G1I*b++MiVJmpL@gcD@O(F0_vVp+yM^`p9Cm8jQp-a%Yr<~n1mOM zEVMgNF#^*|kF(Z-Gb(RQw-qIh5a%6;AEI4uh24l=LKkVRbRgTkPB6s(Kb} z^xOkRz^u~<`F8-+I6Ea2=JjZ|IpR0g)xMSL77%$JZ#-0+@*R-|rCvVp+W@rCtDDr| zWL#NHzCSl^9tw`PBDc^Dza$nqDNtT>vANzMDUjGD5P=|;4x9$6#{S1PM3ftt*67||7E^s|xCxr; z6iy00#l-^);s-V7FwPhWXU$HO4ubPejY**Y7J4TgZX2Uv@?a_^>ij*jC4Oh)392vhGJOyRFVuJmxXNYY4ch1Fm@^z zWwg}uM;9s()GLB18zma~1FDSYSr(WAtLR{JpnLynlTtu<`hW8}NO+fSLkaKG@hZBT zca;}WU$ki3>bksI5#X$5dvGvw_2}qLh-8tSZmKnc0%L{!OSIaW`dm|GjYE(I16L9p zXMNa_q24Q<93BYU%`ouS-z{&$jnV=1{gU5Ci>gATf6`LIqKZWL+W?OlN`etl9r#~l z&iVU=7mcPW`YQeemvfAIgX*-6ZW=1jO=7H(n*$3u1 zQa!a?`hiZmo6S=wvb6k6=yIE1)HAN0Si!d8i00K2-_I-TnQQv5s&KapPY*_tyy8{k zimAwF&U^(<=Zqz|w7Fd9aA_KjBaR+GUjyo>1%V)_kg+on-tPmj*_-s}N7wk5 zFA-0)RDOsev(MGf6KSM8P%6MYw7IDX`rVi-Rs!~W*e3!gECVI;AL2s>+^dS%)@!YT zg@97ih{icu6|iz!Xpc;O&(pak_Uu zYP62$0@9z&N>~ zm!?pcW0A|^t?gqTh2tT){{-S7sNC)v=ekA%AGS_yRSnW+M5MI}yv4aH+uEm`;~z1Q z_6P$FaV#O>-&me;1NG*IQJ?>?B>vr>Va}*z!Jw=|kN)B{PlM?u_ft?`&?mx9R>o%e zDxZAiXVPLr03s7C0|?3fBd8%e860Mw8i#dJ3d>_x-=HaW8%cQ$W$EfjFU0dOg_4p?qV2vImaQ7vsj46Zd zv0D0o>^0;Wy9D6N&j0tkS%eQecZnAEgr_!g9Xpyvr`SVDtZ@~|Y{1?55eQwo+>C>{e z4NEQBDAMQa<=6x*(%AKeH!s_n^5P}ez#kMHTEJOu{a#c+PaS1C z(7bwzURzVkWAeeyFdn9GZa}|q)!zU=@DcBO{Aw~^Ud3L&YuD#c~}?Z z-Ym_)1~36Opl5V;qNpwE6s7av)1FRCNC0r}l$_W^$XjndxX^2cV@&WYwoL7p(PL-% z@m7Fn9Uz;q7e5ti%V`(!ILMjA#kELYa;)Jg80qiYH|34}tu!otbF8iq6Zq0oraRi` zeZJGGre3LS76}Eu%DX(3%~%8QqNAXjF9f+5&w@2O?>K*}aMsP={5dWy62E}D=`;Rp zhzonM?Mw1d8aRF?-*BPJ#;iD^9V3zRp0NI|no$(*Nw=>+U&Tf1WXb2pTG%`N>7;&g zT_8?HGqmJ2U>ij~j}38r#8y+v*<3W zhce=ZBbBi8nC?xXfzkT6-oU5*Z2qn5X2H9ysPRk2zVjoBiou?x@t5sAr=JdDvu3rc z#%LC9VsxNcBw-R~X|6W}U6tkJ1}J#)1+Y&C>k=mQiWTfxjz9PG)b5&oJ`(<+)T%+G z%CbWI?J7|5$&93+WqhX)cE~mi_)m1C%$Lf>gC!8NML(F{Pe96Y`5o3z0=tkg@?Sw_ z-BhR0u~MxJ!o#?r`YQq4Lh4X6M9EiTGTV8E0d!g=V14iQBH`BfhdMhnrrUck)7kUf zV)lb=j^@n{XBg^u;|CGr*`buKkW~(*ZMO_H(iBsTWL>)Nu0RIe1N8D97`-&(`>d88 zw0B~T4V7kf1_m&kWOjrF@?dMO)HhJ@#i}a%X^3kW>6Iu+Qb4rgCOqNbAoFR$tDj)b zr&vtKleXzVAaeCLenigk&>=0+q0n&4ize2>wzxg&a7d8=q?&SIlz@?oA@-ji-!qcZ z6-@dP21RtYFNkJp1}}g&lAy4>j6p>$Kum2#Z8y5Yl#zPLIf$o(MYP%m?LIFU2-yGxn=Y0TEGrdu|BCMQX(*ah7b>9e^ZsyeG3?;<@xr+h3phMd$+=5z z_clSSJ+@%-W}XVE`jJ5y{M}J*^p0#GQZ+rutT5d zi@;>R)Q5s1-m4*{9Ta6olK}L%Dc~0edBhZC$&avoU3s8*LZiM~@wALFPn>oCHw_zP z?HT))kR@D0A%x@nBb!OQK=-vMeF`N>m20awKt~cAI_BlB{74jykmYNv+Z2`~8?S3N z9p82tu=3KVLL%J0q1kTIhdkItd3lC%mLqO6-X|e+E2M*98>-iI05w94{8e;>O4u~@ zGWi0RAmFE@{o#%n^I1(~Cm|MtgqbAceQ87T>L~iUrLGx{4lLmkYJ6U zztzf-QJJ}Rj_%!!iAK{OKjI zUijm#1vTsoIf~!;GtZ(zVB{(crL{xODJell8EkC$Y|eRks)wZpt9 z%O=jLR8Yzp=!k3U!Td#yF6&l$dUVK8P{D1TS_9>zrf1z_rPSubR@ayA@YrknhW1-) z)Vn2rh~;ywMN76i?;qn1YKcyG8~nG(7rT61rphfhQ-YrDv5DZDd=`l%;cLN za)&}|FEL26ExT}0;fkmgJztwy3i!bvrX1}eTk9&dNPUUNmOw}?HP-WwG92Bz#;t4& z-}I_PdU}B%+@U)j4Zb)Jz{bVe!JBXqZO^!ZBh{C1*4^+03OvF&_z&C#{} zRQY)BDLNs8qL#qOUQQJ+F97c~NN4FIg3eL-vcwL{K8=K_X{gvIC-MA=p=$BBt%9aR z?xIP6j`an)qNt6f++qU8?Y&x!lh=AUMRt<7v-I85ia()8Twa5|jb2TmbS_I!pHCnj zLpcpMoVB0&Zt=q0Z98+w?PM(+p560dk?TtACmLc+>Ga9@9&B%{l=&x6akoe=kmq~@ z222n9u#=R6C*DLnq7pufiwOM*O$)k9EgH)YJ(F$-r_Jd&K99NP1kriigALAa9>|Pv zdxo@Wg9$7ZFPCvH^;Kl%>~!9__Cr%>kKfyIQp;-zWPB_kr4VvhUee25*T<69@et|e z7O>|YcF|^!~foGqL}v zvdV@CwMdb}r~gEyj-P2~5YY+)_rz@SoGhyReA0Ii_wEZBpMK}WTcPu|I%|egj7ml9 zy~`N|wZ+T3`zBpa%5YBYE|`osIzf?WZE%zchN`1h|BYjCxs!E4Q=sr{n=Fwy;b^Rb z!EDvZqx*IUb%S76H@p~Y^rc9RU%&~2BHrX$Zs%Rz2a@LeCqA42dRH0QZy7^*V<_fU z3z@eqQ$zM@U?TYMPFwmdBU`PcGOZX$4*(7FHw?UDzJm?}{E~e=s%*mPEl1%! z=pZ^d1t!3`sPwT3_SGxV7<9K_mZaf^4m~9qGZiN`u5Ypm7H}S-r~dw}%~{IaRQljJ zm}-!`l74}eq15jA2sjz%$X7ZiN?)1vWZObeUk2bS*DDZ(c3Jf{0Syu8Fm?lD=pkC* zo5WQkB#F{U39PC9%ooRIE!dv_FnYV@V7Ny5k~?yt2@5UTg$>qX!q3m6BLPaA?^@y z@TmgbO_4o)&JlPRd{D7It+pW7vA!Qm#aa*QEt4|I`)tkSdBiUv9ZmXPmLit~h7OZF zpI#Jhs%;ocm!gU{j-)Ql_JH(}JM;3HAtwaPq3%?46A}|fa1InqXK+Mx5yDbM1p?wJ zXlw!X3ZhQgpG{-QJu_x-`;k}-8z3ZaBHgS^{BJvjEP9{A@~**{x(D;~{a)RL0U4VD>gw$4ll~D2+iJp?nX;4NkXJS+zpbsD(yjP;|Jof>1W2T-F z1EvY2?Qq~kmVX)MJwjTV-2@{m%E&|Ud?(kY3_>&}Nx=jTF6A6vhO(2qW=$G)$jYZW zupuiP0Iha@U7&;C@b>d~Y;L0NauCYDL$5P_=(R>Hw8kTFm2k!84lo2^i+Z-18`*fV&p3Ao=2mfRD z)yr!xlP z;`x?lm!1am*a>rEKy4rVIrQE|=d(JQj%l-3Q5C$jx`zu7VbGLA4I3J`yO zpdQ&^(=`MD^8Rx9ts!Vg8bJCbqaXgQO#+i>d6W41`(t^R zI5hh%n{DBnM%{yj>8$3DJA^5ijUq3jP?%%Fo1)f{d|@IXms`&}Uz*6a9fv$iQqXkq zPQ=3VMEF_Y{OU6}mBf3jACk&+`^K1dJ#41U~LkcYnj>W4I)+6YFt5s+f9L=aDL$sy<78z~(uFwYAVQiWb-tytO9M8rZZ zbnZR_8C4}PBPUg2rg0#YuOR~)8CU`Q^ckiT*QtO}b(TjemQ?crUG&SyNc8F->DV71 zJfimsSkw`=-Wc45is0FdD^VP4H13SFGjrr6COH&mH22d+HJa7cY3es`yPGX<_C0`F z!7a&8XxWqM%1;7i^4AO>Pv?<)6rSg+C$YVo2`&Zr4PqJH0o)fGs50OVC_)4TCN;kq z01Sf#6952003c=opaA*F$5}cjOM_~~qwpg$V~)u6mp-}Sy}O`qw;>y}sofyYa}o4t ze9b3$OAz6k=anL=i)p=#E(V-~RVb)Tn90+;ZNQopH7r&-?Ehoj{yd=+>k(^zKE>+( zA0)f1-Sv|1efUW~K5Y@xlTuR6Gwl=;oUil(;boY`Z`AUa`kRPRNu^P=n* zG%>#1O;8ZKBde_g7JA?qu;LVJ5y3+V`H}uRRfRd!@HLZBC$Z@DWj!;&T#5!Wj*lxR z^G6MRoJug=m%|Z~y@eoYJTArWXNd0nrk}00_zW%5Z6>qg)s9F@N z5S^B9cfjO*`P!N^D5l8k`G15;`UXie|AZ_>>xEFVC?%U6qf45R`9MZWALkfiuU6*P zym|OTi@7VRwe3DBaVe%XSb>1RhfJ-lXgkaT@#$S+lcSZ_1M~3)DU!|_{GJQbg>8@- zzK?o)vXJ8k-^B4&Y;BNn+n?ir^90{l)j#aL?{oWce2d5zn(>^)Lwt+o4}Vw!^r6Z- zrQ;nm&!%o`j+6=yOS*YakXsPl-y<4Cz8)lDQspH7TA@bMuAbK(PnAiiMvLrGt=Hv> zKA#lQn#KhXrO`%bN{NHl6oqZz@nb*#eXH`A`(;5{sPe-=WsG1}?JEfcCTLkdeshDR zT$NAJo;`TsTR$x0gt(eo7GX|nRyO|DE~v?s2;(>iI)v-}Q!skhN|6Uw1!uCLvm%~yI-v8i(Z%6z3h z*8n>^28=AxGxTXmb<`~D%qgc<4#g0dD!y|v?GPWtRo(bI&wI^nrLflFJ!1$MTa=F8 zE4Dv^S4ZPs$ey%*T|fH!kH_AVczsuEWalTk2z4wcYUKlsUp8;nPUhH2+CxC1CEu*H zS@HCW0)NG$Ns;_xN-WJy@bwC9AFAdwWXCml5*+L7Nj*B`(AGNeb6H3jI*Bc)|yv*)$N z(5W&?i;I-@AKY(-MFCsnI7PwFoSh+Jy}4S~UqeEzHRIBcUWYS7B@n{}y+c@aFD1G| zmWU<6KdEwbQcVg>D^pw1{Es?LQZsnULG7Mb6uc0}(67#kKDP1RB_A;IRP}&vH(cgO zf^%<_Waf2Pg#0@BH;6-4!rzv=9&J#oi$aw*rGE6_6W7k(0<;;Uc20%ktVdQstk5!! z0;95M5v;bBKVqys?7Mi2&OA<&a;3eAK_~yQC<0fq(mwG`T;MA?!@dX?1+Qhj>F$}q zNMSt)%7iw^gIiIGc{*^Rnrh=73U>U7r6(%)Gcs?7>8*#t{}hNqd|LDnm-u}vOF8D> zSm1Yr6ud>BWA-aRUOyC!?lBXc=($9O5o^(;QucmWauX|Gl#8mwXU@(p;I3?cp(PBU}apX&j48)(MFim_hDG}@mV{D1Dp~iN4aYz(|k@>fO9wDZaA$Zqm zlcjOtDAC-cuPpeJ>MF;&L{8WDiz;**F5TfqL|3%SX z@z%r32Nz^SDg^M<*Sw zOUe6&xX+Dg^K&jMf+y4f?-%Y%zP>uH{d|L^Q<4GLp^jg2>nOR-Ts_i@0RcxA+v;!& zYtaM@yIi3TFfp996|w91^uuhS4r=7iosr|0uxWivCv#r2QQ4x+Qx-Y-v6$(5-PCpA zjyt>{eklLMdonwFY2%rww2MDNB3N6leJ0_rc~o^v7q^4=FhT<3%@`0yQFdw#SEE!H z)%VaryAAbTGQ0>VKp;dWHNP4_2onbr05AXq03dYbjy;MNG{0BSC&mQdm)(`8 zqSq@SBFqAVlsEHfeh-$m+ZLnyiWohHbswsz!`Zv(X60t&J}z~vuj_XZyg(M!2Qu%k zO47+C{0p2^X4)6JN4BKSnS*wcmgd3v@2Nru@E9GQg;KH4N|{Hn`kbqi(YaZ}=9X^` zPh{iR|G&txy1#&#nTWPkjKDu&A-=r~l(y;ux`w;&byPav!5!XzoHPB==fkQ8Uq8*R~ zJdEP>y9#}|HLr8roZ>)eHG*g)mCB5-$2EVT!gl-hdZE3wY*y&LIec&EdUh)A#p^8= z-FnD*z2t&>h3sD>f{$CEmJp-!&%~{T3~d+Q(j{;H6AFZrZ0Gq5AacDULakb=R!-yx z*~U6r^*!9NAix&8?ieS0SCdeUEsFI+OwKZcJGx)=4nppCiZd`o$?C+X%R z!7QYYa4>N!ZJ>j!xZVQ_xG@{U35Yy$CL&C{@;WUuTnblBTo|0@W*`8TKu-n zyCR5IN_piElh|1GhFcxf5UfFaM3FcEsm?O<8Cs%0So@aNZstU|n3bouVdzlbjbQPw zdq*xHG6?r=3m4*?xl7!KY)=0Uoy%96n03qB(cdi{8Dpd6qsbsB5&9_p%Wx{#ny2@w zOi!r`auuyk^EsKdApMKBYtJ{ltXd8qvBD+J(boUN#**d-$SJWt*QfSY0}6jaQmgv) znlFXURoYbRmG;k~-HNxwJHY;BH?6Uh^G?+U>5LL<0QaYtkK}imBxlJ zJ$hZu`<%fvyoYerKrK`#u1Appl(LbHKvMU1G$@JS4K+Sq*a0QsGid^hQq91_J}Pl!zaU7q_;)s(5>aMLs$q#-UyOnN z2noP4js`G(1PDxPemMXsA^;`;K!ku03j@Fa`L#$pt(J$rKwkg|H2)ejQ%g$hP25zv zJ&qe)I4N+|X{+s?@}JiEh(fVeWnI8g_pER^^bA04PH#92n`RTe*QcCE;MP2b*h)X9 z+Z=CDXq$#cz>wu9@tp_e{Obe%ZahAvOmv6##n>Ph>H9v-N~ux|Ako(?O!H^B@ihBk z1hJ%c%UD(-eo}}#0Bs`@0bi`&7-Dn27s?k)ll2TEP=9HcAfG!;eRh&DIhd;MtFpwJ zda<;fje0whyLr~FqM-{aL6ljr{Y5*J)h#ZdR@fNEuIq~m$sd(9x6|JWUK3`hP|O$o zz0|NR>b5X;sizqFW*7b{WD<}>j*LQ)?l*G#pcewBlX2Cz9)#R5gMj&mBr zfrl^)s-EBo zGwfx5DxY)cTE}LSTT`2- zPj&5U$>Y8E&Gf<$2G~rQH!sh4zWu*EZl3CSaekR3Q%Ga$?O~Bu;6$?SN=okD5IsZE zT1&;Stedydh6=D@6B^i3P`Y?Oz!UzbmrR1bM!~@5UU&`$zUtH7nOVJBs*F=1u$4U? zU`#X=v|mFhbzo~2=zbR^H*U5DvibmpwLV`KkRrR&tezMgx^Og~FTDlykY|fNx8f&Y zw$zda&$DnOvAEy1EHn?{L#p)}MTu1>MaWKs`ITLB6d^6OJ-ioB+xNM00nIxvE_2ln zJ7^BBm7G$_9XOhguz8XHc%N~FziM8KH|+di+^~8efR10^wPGyHK5J;v^BP&W^Sy3J z5UpwbHF>wZIV;EO0ARS$(Fjd_^J!h_KtEYf(;)S|5w=(yK_en%V5ORe0bYP8Y+bp; ziIwMhy%a?`LrAGQgQQm!FONu zyCSE>#MnH@JLsG=W^6ms1T_mr)^?be6n0@{k5BK0S^99tkJ-!tDaeln!-n^Sm|B{w z3V%ieJy?wuICaA~P+!^>ZAVAC0m~9$`n2PQS@pp(D{;S;Kr~uf?|^g7egV!rfp>5; znQx~H?wpT835Hk@JtjbA=s%dp+VG}j2T4E9gif+tMJ<2-F58U;K!wEXAjT<iGwY?J&ulzVOeg&bmad}~jwpg5X!P&L?xSyMN0H))#4W0AtkzV+1 z;{?a61^=6#*kK4|seq0Rhp~3$O9&oP3AsB!*)Nu&$l`!1sB{zu``z)*-ghCO-^%asW_y_L`9b-7V0}%Jj~@Eh;Oyd2=CJYQgyIQEt_!vp zeK-|3zkucVx%R_!RIvv9#~)n4^z@#5s*j}0(DSMgc=((fC`CdYOpKDt>|=wggE?vB zZ)O5j-}`gYq#gk;EiJwpQZQgXy}jKLNlex?KHoe#g1HF4NT9bh=12;(5&#+j*b)FT z_6;aT0t${1zZn1kazqn|0YZQP00II+fCy*=cmUo9sAz^rn&KoR3n^AtnQpxDcb6?Rg-9tCAKJ)^zt~=A@j^a=k(Pc zwzeMOuU%Ki()D`wrm*VFFPoGmSTY`=8WKp3{Zxl~`R5+n6!X>B*zluI5l$_ldsb|a0~QA5@nL%3Rr?P|!H5-1y}GL5!DkA1iOxVXPL zItUIVu$Sqe)Fq-q0jLjXoxy2-XKhJh-+>xR-77F>d9%bc%EI`$%6{mSd)olAayzHV zUAx57R|qVhE7D&J2Z!L$l?L$IWYbJKhZ|rZgBfmgGe_?BJ8WmJ^1d}tDHV>L>dC1T2#P=E!c{NSJlYXn{xlI zMa$~IOz+k-rrx+eJS}YO&n)m7^ji~^`md4}L-p|faB)HG>DBJ!rCsVsA>fQ=^Dag6 zlhQv+Yo$gfPFX%#gMP+AxQx!60SANfFhHxnXAv3K9#O~S_3kOe%iff!i0y?lE^-;w z(Cg4K;ixBR7(?Pno0)BNqM_6SN1QO&lecPJ%N=`XKgU9mXtK)|L59tw2S&?Ub(6aZ z_Uw;lg{ZrYainrras3{rCzAVx4xr)u{l6Xj;Anr4F91l{`hB+-{J`a2s?irmdmUBH zx8oA5K<6-qEgHd1WLaI0{#DSu3f_8!cp_2v8hyes(~;EF5E|VgP^4Sn9M$!4#Y4*< zW`Tae^zsEn!Ov?6=yKxeF!8a~tl*IK+zV&ki~RPOh%aw?HhF!(LwvgnYca&_WL3TT zW99$5IU46=L~vwRNCiHy9flg8|9RaD*alCf4#^^f(6oSHmf7-~SoxIy?kCU9S2{XW z3r+3fmUrX?%#WMtb3TMnpu`+VP2_lW4J7W4UIGzubHg(c(Vr?B58$Z<-k1CaPgC#J*>P;$GnP1*JvWAxFV!iw?46@g~0qk4%3n@dgq3n zkz@O`RWxWma@xTC0 z+e?GojQz)49J#_Xa)lM0@=?u=N;{F5P*m`*&f7>rTnKM!~@BWcU zY`>T{wzfL3N z&6{@>uj~1nPieWJcF;U=-{9GPyP1Pa&sgEcQa3O~l?%gE z5NXyFdWilyg`9ZDkCc5I@932HS0K!KX&*!NflmhP?CE!~ESRTXCq0{l%^~WumeYr)H ztehJ!sfOAkC0LZ=sqebgB|b1(=ip$-*AOXrHUv`*nUel`R|6Lz^X`dbo^v8kJ5H2y zo82>@GH(h8@Zt=92BV_D^)S`bo0Ju%Bp`GR(vyf9T zAr=!rP>_LTP9xR6y|j2yqb0Rz20Dt@TQr}>PM-nH5G_e!6Lo*#Oi>-&*Z|P%fxC0t zp)Yf#Tr`CGxg&Gtz3y{Qnrw>@15zzHqW;%SSK5IQAM^0Jh>Oa|>2c4#S3Pv{(Q}|? z<$8elL^$B%q3`AliyQc80s*Gh09`iRLr8`9Fe3D+V%B|n#gw7V-aEjVV&1Ps@&XhL zl8Nrt$XrHtS{D0=Ez=lMpuULv!9x8XX`uAwFlH;gPGv6M*FJe24<~f(Lwui;cIsE% z(>0n=67=Bv@$lmK!~hAyB+X8mc^;U{+(Vgbsr)4Cp1Xp1etgbdZk|}>Lc@q-(xVU! ztqJZNx!a;YeBy}QwHb*AZuclv=nrqt!P5&T4Co-K`YbvS>4X$r$;sf=M?k8uF!|rj zV&H{-lDTKOkz5tUJ#vwo4plQpkU|*0vq+}jc+AkXh%1DVso_MOZnwclIlfk+LnI|T zF7`s$d9uQ{iOL;$;*oUW%VZftYn{) z%MabokgsHvnHlz3*8Mw}RWJYkPXYi< zLIdUNN4%h6E&=)%Ccn%G)bb;i>5qBoU}KcmkTP&lS?r$UW7Ac7vX;0D1p&5bMg45V z#ufgqO&zJa)FM?ra_&21xxzke0LiwpD8w<8aiLfh0o(+k#bBnGItPMyA_00_8-taC zl+n|FPI)Vy_qK5_b=aAJ5i4eXfKRRrzV1Kxvmr-o5giq(lf`bO_avChu}C)5ExuIY z2jJ;&p9u@O|1_JdBSBDDKIi0&v189gWGR))Tg~q$+jD2S0p12r2P1WM46K&f{&3(E zfrA)4FP@+6ooYe{MLh+;tfUiB!;*gIlU_i8vC4dD_%g&79NVVW?zP4%=g3t8{%RFP z?a7RQGNA=9hCr1ZBYrsm7()Uk2m%2l000BP0Qtzg95e@?{{-^hY+$tXetG9M3GF^p zq42U*evnDj^-2V5kINoq^%~+bL??<(a&o#5KHu3#RLm@7PVAm5>;%5)ABik1o?o-| z+fak@=bMb3<>NzRp3B|a_WoH2yMo(t{G{T+0$#{IV|&%>nw6(fJVl;W5TF%g6(b*_FtO;t7Bp4S4B(adNH(}*k01hvsFhN41EqfR#+km3QV0X?G zLIMbG8um$LNkPbnh~KiO@2?J{9STpDVsU+TEvcP&%gNa~MvD7{-afh}Yh4$XMyo$Y zKFiCr6-bSmI_iQZsud5!mwTx+;qW`ikMyCK%fF{!*$W6;dJxLk-Y1gvmA-L5ijCG# zMF)rrOF;#^YMeQYdxQ5u5|<8Hrxj4@XYS-orez#RYHWbi)sjL>1UwjW9D2M%HTZM2 zcZq0W+K`=CAl(!zfZv24(5_Q3CI$NEdzvB?!GX`~e-Y^j&@bB|ds=cjj9@lAv+2Bd zJ`@Q*5&#+jh!OxYh6ON+NC4cUemMXDAOa>30n`XYCIi3#`L*T_n!Jp3p!*_4?XoIC z6O9x#ak=oTzO|Oe94v#Hfs39grkwWa^hd+}BAYc%(qWr0DI zIw$|aaH;Tdd)3no=tbV!0;7BnrK6zPsjywD@kh`?b-~LX+HIVfPCQ=j^@A#Hu&gW8 z=MLZY=GFi(i|^c`B`ZqCv8jIUtx_)TjNIXqd(3lw2;$_d?V{E?qutbXM>!l|oRA-N}%ga~<|m;VWp$HZbfxOxh&{%qNL8@MnEd!$`GuRGs2L+WyQc=8#8h6mC?ckl=IhLLoC(IfQK5ppCSus;Zske=U@6G@&Tu2yg|8HyWT`1Nndsk#W_9KUOoiub> z-p)GY^^6v$u|(jX3FyXXkQD`O&-@iv*V)ZXnhPqpovj=G_>?Ga!Hq%q^x(yBRCs*q z@?=A@lS%OB+9$_OF};zZ3z9Edbw7&v!Vxx4W-upV)!^Gy5a8;VpBadG(p3X#9uK)T>MN zuKR;W!sM9$TZa0m^Oc{>S7%`@eW%cX9<|M+bOXMc+p4kx#uNiuJxPHWfctmc!S?ZD z29yuvMuZc~3AL%}gq2vVDW!4emjexX@OrOR0j0x$(7@<7Nilo`fnM5L#XdF$gtXLg zk8Kbp0$~i5VW4%A0)>lcL)L0yCgw?Dw2rUa^IR6DxI$>)Z7~W$hKIGULzGn}<*nlV zuoPS57>fNzC8G|iKjglMh`c&!!#*}tIm(S!>tkg6UAcu*2;j+jsvG4hC6+9Dh43ZO zfsKC@9oK&K<~!IcQ$cLQZOgvI@(3-fUM8^rjC4h}hWhi>LsZ(!Z^{K(R0S1H!o4#2 z=YsSEgm)JhWiIMtT)-c){y0mS1WouV87Pf{2Ai7j(D}}Vu%pf(>-PUD#EL7+o zetJovHYP|v6yIP+dY_44kq`|RgsmymxG}`R$ltg-NV14Gx`t=P$=;ChW*Gd>P_6!O z1}^AWo}!{@1!BPx{i-Jn5bZ!=&PZs?8eR}Xv)c(j<7+luW9qeoomHWVO4^Eq!PUwV z=sGiL`g0>PsBiEdF{E>!AUjT=dbHSj2VK!sv&^>(U-RGYwP*yYVo$=%Q2oDdt z!?MlMymdc`JdOIJL*)3 zka!+-wfaZIP;A#uJ!4ZB2BMexKIC2`*LL&^sU`fwJM9wd7{Z>9=H8Z1RObbPpp_>X zNQ5pZ??yLTQdE9(MN7>WQ~=__G2f1OJvA?|L;h2KCkDp7bpsl#o$( zsa6DT%DBHwz!T*Wt*~JHHy3r6RXoXqv)^}@%25=F3GL|&z?#3ktXFbfN)I0i&(rdH zFtk!V7VenCjB(cIc}h;$(yxQCYEp(x1AT*)Vy13ezk)vuqjwi3yg6dg?bdF3k=IeWG+}>xQ9h^)A^j zLZ5Dfm}IZ{9+kK%!ror9{e|kIFRxxUkPvH=CHBG1BD@4I$iLu?1q3K#NVbDfrBnYi z9CHt+wx$)8*K%H2;)V%3NsF-<5pa!6{#FX8hlkb$krqQ*n?x3bb(DYG7=eI07n&4` zK6*3iP?jI>zvqP`xLwDbK{RUU_iZbW1wEW)PLxpa4=GT~J?emA>=L*&QET$14HD3a zA|Er2$U{J7wa5CfEF-o0V{}4}G#AT(Zq^zbbW3ss&If`}Q#4ELa3z$jTRpyg;q6!( zh9FR$!n>^BI4Hl$Gww>-wYaIn=DDpvad4|rvw>nUAMPMS1)prM(CHg*ArE~FTSNx3 zJB2iE@2}DYgjiUcJj29GqWt^LBN8uU9x{4R4+^aBv?Sc|f&avs7%(Wg|xwJE^#c#cS=!E)UkY zu^n5q%>4IRwlxRn>cz&m)lJHD?Cv*Mm-;_cT?{v*qx&+vod!8|1-)Z=kjpipfto7-D5&#+js1g7&kp(c4LYpIm{rnZIJ#Ms})!449GmoD8p7(tb+KogAL%v&hR-1`qdpPBesM@ouu&wb7osvAe z^CwYK$zFVi+7b0p$OiS1P}Uq(q!d*QGyIJt{!yaB=qHVHVLgM$#2#pA>rJZ&jW+6n za$Y4k=fq%q3SNpFH>Nff?q$U;qhYR-Et79C2||aqd&NA>yQ;_IT=U(}xCcIGm`TFJ zu69&BpWIgm1mzIwDf7jAXl^cwG@Pb$`zs3v?Sie-=VmJ8ApH)Rp>cdwFyi z$u7C{*2}ni${pSr0qhTxLjUJ5pHki6u)4^Q)duTP67K^&_Od>@Dyq_tR`fjv7Yw`M z^FB>}1iz~Q0`^h#0V7fsp01F}Yp{7QmyNZXf?J&b9 z_kxiw@|Mjg$jczxJIEl_H>AK405TLXl)@1WOGl4j0058x03ZO_omij}05a<^AXaDy zgQq_WKnwuG5r7~-2p~X!fFKB<0GMb1uQuTmJ#yCAWtT2y=@bTY4P_iZA&uE(^`S)w8@i%tL(<6{$qo@0m z#y1dK(CvV^$41}|s&8^fpHc;(vz(~+U^!%ct3n+-QI|8`Wy7(WNh6T&#EcU;yFXS0 zC*6GjEuW$`CnXU{CWc}@LU!N3^lmiErvmtd18Zu(-nr`!djL?lqxCwS(RM&$ z&z@P^m1UyYpVy?|=&-8JOK!xb*$WR!_*CFboa~WMel#+KOL#?L640`R(D8YyCbqTh zgiM^j9?au%53F@zfV#0VOH7$ri|_GsG-)qF=-P&wsWwuElxCQ;Isg`4H%i#15y=uJ zh`Tzy{`{(9ck6+~DD4R88uCV}Z<4%Ia>TXP;P~|@-;Q4f>O=TTpIRp+iTN7 z?zhhuw~|c=crIZh^EQpxZLdp-HTs1ncCj<`iX{4TvdmW#9&|mC91#4*$8&V31C~e` zYkS9w66_XFz-)rywAnqa!7b14*#E91CT?dDkrt~cu)ppe_NL8Yh-b6#>^C^EvA_%$ zrU+%DhC(14Gexme57h!5Hd4FfS9b=_qFJBrQ^ng z&a})~_Y_dHSXoDp>)Y+HvuC~>dXR+1V@!5e*h#GP$ojMPcqgve2-w~scyfofuoU&T zg~g1eEee~Z!QzJp>FrQUB@5yVKhL*gSNnU-?XBV;eZsI1LYXt45>Nm;+0CSBN0m)x zf`2KiR?c$gPW-LC1?qr8O}oG-0vV8AD>YHq=5Gx0f@Bq((J+V4*0)$nQvBbW@d@(SLaBq^l)_0t^xmT3pR9D zcwi#|x@4>UCg!W=zax9#-hAtHq_ReXb^jh0$Fa|AtrNCPXIwm8oO`4usrmmLP|V&1u0>eoqHO4f0>gtiI~1zeh>ojB_N}zh7av;y+0^VP-c@SK0EH}FJ?s>#MqGVJMY*6l?odkQ zXnOPnwsI4^mdw!@wJAsowQ=`-rZiyvW_hLo1@P00004VgWADQE?F%2l^?uE`!-p?Q zT|^Qo*)?jCa&~_dadozOK2iEd0@TUh+@zYMWp}YUmULpzCJp5kZhXl#VfD{MaGDZmlUR;^=Q|r*<>W zsU*Zn9gz5JgLPILy*XjR!n{#hU6_pP3nC;fdyWQ~s|>9uhIf}(*m%GPs%Af)gy<%s zlzcVi15SCQ*5kbG3n9utoxPM?mOdP<`#Bs2b z0VVuq<~y<6GodT??9d7p@o;*?KosSsGKu+eU$yZaD{`&u!T?hm+!CYaQj%!?zf0Jw+5es7aMP21xl++yG-> z`d`3%4Pb&>7T$Z;73?8fwzx$~ot?g}>=^Ep%@EF+ExyIG@qVBiR*Tk(psV-_q3?(& zcTW1^4-ob3?xvY#Zn1$w?bzCo<&p7s4K*=|>(@fYy-bGDg;haiMra}A2?}`-ja5It zli71*OOj(*vs1w^D|97bM+?96<-FE?Wo$w}qnFRZwB4hWfYv2QrP3BDg%>S<1?hRm z=`2POKUKiZw1m8^hP9b7q-(?GcAi?C!tML8$sW;w=zVYqr;oNExhzAkQU7|hPO2la zS&cTv5#T*!FjyjwKPmOrD5akZMg>T<>d1Zwk0i9#1G)1d!Hf~tt%?(FXP$|zc=&n` zL1_LDh38S$5P0j1nGHj-uU&eP*n&~satueQA&c$1(Q^+Yh%}8(-vxVgQnkQJoC@63 z{F)+~{@JC@m4yZWTU@@e-51X8IJ?1h6sL7g{-90N6603p4XO2%p>#N(pn5(B&NOEr z;_SRT=IR;NwR=mP=bofsV7x%!eZqCV<;-&IE?SEepJd~w7qf|#Q9mgULlcfO z1-NA%7xiH8&qKBQ>`^>U3VtUCNCOkRS;oUh2Dmzt?J0EDuM7QgPoL`lpCZ_fVuNXo zl##1fJtatIp!4 z5uR4&&f1Dt1yje}&?V#@W}6=meQ(p)FPyl}txJLgREhNSj1Rb`C!b8z)mr3jugC(- z8vt)Ia(^5;KhXbG|N3>)1W_XjoU>@qg2awI!nsxawe0NQIrc&skfpmhOFHX2x`Ae+ zyS6aWB*qu>Nuv3r6t*peEwWf|W0CoHLHnjNc7@gH+7H8D3zfuYAE}vpFWdAXk-*#@ z2XJ*z;QKjAbgulK?R!z{z*0QbRRPac2mgsB@T_HYo~PQQL6;lT)-*1+Sp_mf{sV9C zz-i%4pSNm>7&H25`Aq38BIpw7V4{?ypP3MSA`Wub(PU-|d0wYmezlG_E`Koh`PW;% zakfF~PxiV){3m=9`vUND(rY>}`zeFu%7dXf?_1QRy<~vmC<6227eU2~{ZL=ZO`OXX zI%I1u{>4sCPvYfE6o=iL4?HmU`uU~1Y z`u=#d)EZmOzFp2quQVvflH+^eSu?VTtvLWN%GWWIfA(Y3zl2?h;y+Gbl;=p#>d&ZB zECboZxXg_kq&1W&W8|MQSoRU+Diw9=K}Owx;{y3xZH68N z^$uBo)NGYV++Ygwd-XG@e7Tc&INLRJMrV%`A(?Rtg+kP(Xirx7OCuEbfux8Z`Co>^ zJ5a~2r(gfQ=nwnRCsdl7O%?-LUru>J)C6ovPM#eBuolIbwk03iu&6Ye^v!)QKEM&_ zh4>)OQl7`+eU%h2vrwYRnSlKPPT9f*;1ngK2g!&ZRJr<@V>WOJb<;ibZCIiLT9X(? zeLy?z=jsM#i(gXn^Hxn+^-k-hMZ!(6CwPUTAsU{chVhMoq;FXBwf-wf=}by2HxM30J?+cTz`53};8@=?ZzBvtfJ9^Xq}nGF#rMdXq|@whH8>oQYE+&J{L7)AN{+0f z*M&MRbdxI*NnKEd;ZIYNZ$x#I)nx@7C5UO5GSh*I5+$%l$9M#lXi!j^AtXoD$U-#F z%&O3RFbCUubdC!d8+|GpLPw??iau)l>X{Vora6uk9etw7?IrWM@AUIsqc9?dI}q`7 z5wgGi7{Fu}mEIn$W`gWfC^+g?R2GxM)#940=l7w6o@)3%6wpI)RH`InV+^(UDBi=+T!VngJk}tbDfhcR0*u z5Gx6j3$Nq7MD~aMLnqlH_AgHG7sRN;j(GL>6sf6jKDC|WVRYKFOWMPfyJRfRZ=rpa z7_=i-L6V3*X=*>3DX9j&^4jgBH+PHur#+fU zFYK}7!KiY?F7lr5lC(XIs9`R^{$?iPt<&gO$xlL>(H_#F@>;qD$VtnP2^V-mYRTJ$ zP1`%x$x{LHC&4EX}%3OLuxLsV>azq{D_ZL`&!RH|^!#Y{#3KpbcCuA{m# zmP&32`iD$1&wrhoI7XUvWI< z)|;~MB@P~uJRknS0#j)!rf6lMmLWe6a%pbeD)KLmW>_hQP0dLKL8G3zvG8188&(Mk zk?Lam7NO%9E7mp1r;dJPpU{E3@O9{aAmBnmke~jZ*wVsjRwlkJrR^Hes4g6!Q1u3Ls z&4p=iu2!~cG_2M$iAaak!Mfr33KA&=VfXHzXtL2loIrTS)eEFa`A`?%_n zF|>VytxMna0iNEn4&snZ15AL+o4z((x3KS5E0B^YbnyasQ+T z23R$TIY8z$?|%*0)zjh31k-p}6RqU`#$?+4A{xUHRXiaNCUx)1A#+eLSgH^FaB05B*(C?pN;VMN+J7B zrj!cKo=m?%=WK4)KxjEt_}-dG;vtsuj=AOwDiA`_Hb4m!hE!ANK2~M#pdKl1l|7DT z2qX57Zm7-Fl9F`S{60Smo{8fuKJ*`zn#zx|Jtl%(O}BxAKKHnBfTx@Dh%~j4i_5?Z zlpMVh5j%O2u$Ln7N@`J7_i2%La-mdGNzM@I5x`v399EB5N(~Mdn!?J# z@1*q`8jS*f!R>;c#73m~%krX%e-W$lg^{GJ=o9yQPT!8-OePyicp^VcyMJCFc^SuP ztyn7X;DnH*1HG!9Z1=Dre;Rxl5R_T7?5w)k+ftL=(_Xfw_PN(_fOP0Tn?S#VE{c9G zQxf7Wfzcf8_bR_UI`?&0qH#33=H3VG?g%)>axI#Q_C}xzHngc;4Xlu0itt56xh$4( zv@&Ov;+ZXhk6o(NebZz75xAT2>@yE2j=Q$#%6Q@9MVp*7 zse#rBa9pAe7SluimZb3~{dK3 zB?x-UfFfDf#ybxvK!;Rq0IrV3IeRdTx@*hZT(Nv*=wh}ko7GfUl1}CN;O5y!R?(F5 z0Ywu|X>vaYd9m*qn?nN0pNt0EqX(e`V&dfI(+=7@kZ_MBHVVZ>0;qXM;K`y;=%GGx zAzO+%Qube~$W>-^-(6NzSV^3%;74*Yh+c4a|w zGbzSP+6lT*q!^_UH1lHp@=gS8}Ai8zw;BNKD8hb2iPZ)#5)eTD zsan?Li6u1zxEu=F6ZSav^z_PPaXDPl7)j=vh1Q~Y5$l?v2^39^KYt3U{}Ys+R~r+q zF8a`pXq{C3(l<| zub50ozc-M5YWkyAqX}832gEM#>F3o_e9rVhlT`p6#;>Y)boro6EkI~EkY3OVM?2gZ zy6$6+MsC{A{&1S85>cRl>jfk-QP85&SDv6JUbo@8z(}b9ozvzl9p|4MC~f=OoNmra zO;wlHX@-TpJ_`9vf1sMuo5&Ckh1q8EIx z2dC=RsnE75(!%80f4p@t_u)|n!sZR+JNs;JqQ=9aRlxjV>4XIrGjD~C99r^uXjc#< zJahr^sb||yC$jlC{ zCGd!Twy?}fyeR!;lNtlsw}!J9lv89t!rd!mqTZ{QzLSbptZ#N!$m%wrn)p76HKd1q zb~=Jp2(7FnYC|h#W!nOApFw5Ge|c}msd_tA`3sz#d^QQ-Ze&rDxq{cJ8fGnD_l{lW z#-@uw(*uB2VTP}+NNinJsZRK@(4x!HYg}l+=^}e{W{@AcQE+VZS2ix$ zngwBN0LW@lYkVAE0)al;eC86r(>sRcchzJi`~A%rx07fb`8x%D2iexag)-Vy5)Dhr zvu{;SS%St>T-|QVl49>blE#Gwss)5t&8urwQ|i;{P@br! z-(e2*EY=NIQb~pCtDthRn1|L`BuSD-gvt!U_ zA8~|-{0t?1B_xw8jk7IKD9HJt4+|S-%_WBwP0Lkmig)MEFf7a=nZI;we6w#jNRB*6yIROx9b@$x*{Sf6ic7N?m)4$` zOFfGSqC{5AXhr%}(UD|@bJYay0*My9hNfza8&b06oUCa+CcCGTHP*g6oDA~5P zvBmN>bJv#GNrBdy!*_OshI3YRRZ0ASnI{W=%flIjvXJ;5=IVVRX+0AWGP3=)-val^ zL;tWzp9G2eb59mJo*UIDP-ZuJnBmG9?gaV@kXIMx`7|{JcDEfyYgRQx=z2;iNvr8Y z#oein8bBz$Xjkxj`W06s#G$w4R|Y07A9^nOUiEDqN+(4*jKW=bmQ$9?N}GcciXqXS z`usR(DH{s>E4};=DqT&SY<7JI#%Pn?i$=@xr#!8x^inZ`(n6`gL;pZ6_eDZLzRg(h zAsUqyhBO*y1RJw-gWS?^8swir62cOazwFNkKNuA4lafnt-?2g3b_c1f;;97Tg&)SD zKa&7t6#H2N65qjyq2A9Z7I6^#^5bIgZaRnn#~Br02!p3T9Do>6gcAY* zA^;=?0iXc+n`(yc)fuV6UG^aX!bs^qp;1jNLUuOFt#KiDLY=}G()RZV&h3^$`QI2G z%CRg+2Yqkd)q`pMbwXWdzuc7y#j z#Y)`jP9xf+p7Ny?V`fcNeUVI>+&pi=j(uEo8B-<^qD$G_+nl*Mr^~ zywjeP$U+>JJ5vReH=7f5xc*{pE8;dS|Cb7CJ{b|~Q*S$KZRMTCZu|S&^}e@DTVV=J zsLo*ekM_793cvjraiH46C!6g1l`jh~kXpBD?8{z0nKCYz_j+Krglept5VZdhQKt@G z^=@y*CKXdh1^>C}u9U9VT6p)>Yq>c}zKie@_Bb|o(pIMZF=zrn;%WScMJoSV>nbL= z%7ecKy+c}KkEAoK4iET>&)V+!6){cxt4zG-^@k+T> zE1CsSKW~8EEVxV8+khZ|w0Ck{>n9@?)N-z}-IAwoNNWd9$_}ucZFp0-h{xzChv51C z0+CX~hZ3hyrnA2s`J7wb>D-=+EVqeCBD3l8rc<{C@ToWIAU{OL;-<=7m;ArHZK7PL zWxeC?kLBC6s-W4&Xd1K8qyq7*giV*wF6B;b=>I|&{ES-LopazajHi&XFpFQT?;O@Q zvQ73@qpRrh9@QV_qu*!QVpfo{8;@Z@NP$4zAHp`?mtjmT~Q#S~^qMvJEKJ#gA}_Ch10G%l(3 zK7?KUe4{7;dA}O?xb1LuDtx#il%4n+OmV z1-_wr+g+Hqc_=9m#j0;9vy0NiX^>abZ^$5Rl|j;igfKw^N`ld#w8w40;6-j)$pGAT z@F*%0cPrwUR84lwbkBAb`0-?yZZl00fKKAH(bYtBj&c=*?acW$k(b%bFNZsEq$E4} zurVT#3!3eTl}2**`P82iaGk6}MS{e^4((nb=}i8gNydEjW-`7|`AP-%e5|CuP!xSj zOr*tWR}S&y^PxlWcnnHn2U!Fh#K=if!rd-~qBV-)?&3RF`T zOpE`YRQz~~m}kByE$TAfosk~Tg5l9{CNzh(zwQhsJre~(gzd7nWl|WIq$jvD;?^LA z;9u@NUa69Fz0S)@#sXQR#VL9~g4i~!EMIDGP+G_N2CR1?qNgD`y~d(NBDRDvhq=2n zk_U9kBzWl_>n}fh%9^m;;_JevafZRZmb#)SAT(Z{e+~+F;vx{xChp0VVyco|3BiWqap06qI8iR>8 zXXU#_@6Eb$W&}Gt^ym3GCn+V8+TPbAp!K7ElvW5B8XWDffpHxXX85a!GunxAc+{02%?<5&$yf z1TdOJ6h@AIJ%9mZ3MLeWH~v7_%}PSq|9MfVKvP zt1Vgq-!I&qrF|J#Ugvw<&cCjd8}fK8_3MR^S%|v$x(A`kVh-x@X5Q0_B}bf;QH@$7 z8^u}WRU2;U_7K}0>R(11emB6Y3qT@*Zq(%(tw%&E$tE`gkvnTG+wyHKNKe9zT|d|% znp427yb&H%0I?_d_#brddcQqa_;wKa2R#CZa>5_$6m@hNaCUTx+l zht_VRc_KPkAPpOFZ84*)1i>1Z5F90PAk9ufdiOMOp;HO*NSMHoH6@C@P|L%N;Y#w; zE8BsA3B=*D%kIv)3QhduQrd%2_<6lM1osP#K@>iRn)8Brkeq}9zY_;Di6**&GAPGg zLxM_yqMbSh%_*j6GR%G>`Mym-j{c)!^BKiBLmj$&56e~idFkgxlCQiY^^B)KEPD&d`=lD zkf>`En9ctjPnwpkB-q@j;sW>7D)(m2?G_L+c6VG@xTu7I))Gh$19ymRqGFt-;1pN2 zNJsu?8o(gb3Y{+(q-*_MW%C#wcqIF861w!fFx;(lUgn5=8*rSE#E3($`5O*_>vTfR}ugk0hkg1GNTJ9 zQUD_>Lq8ip6d}eF8aM+4A%GBg0Nv>4{66vxV{5|E04ENw?)@gZzLfe)<@gAxj1-2G zXHoHmks--OLHswtryo!jEv_Wr$g!t3TyO_Tb@mA!80>8!LFa5w$8iHvYJC#EnnCWb z1nqD#o{%fuN2S}jH6%RR1!>Zd|KeUb`q8uNHqmV|zS0gjeY;O9T;ISdx~yc)hJ8gP zTr-7&T(cOPX`ONssJxtUrdKp6OyUqVV2w^?w9~4heeuf<-zLLPN@%1*x)JLiF-jjj|=~mxNzWe0e<)6diWuJISv0?ewWmyX_s0G z`tlarfq&0bVAe`*zoI8-hVUpH2(051qB*|0W7!3>!=gdi)=RTQmL=1X|54K+o1?RE zrze~Oa9lg_7>QC@)hQuJw8+l6I-ncjh%USr&b0Zx>wlc&JLoF?YxfwFa(>=}HV~J+ z`&mCnttw+UPHG}k?&P|2u%+I!Tu_Z1yd4ptE zh^Id~|Lp$JzAyJQHfgrOM-E+dfjC8Xp~2$&%IdyE{&ZA}$H&w~U+N`OK}^41l3xMw zC`9%O=54sTFlAbuJgLcmt-^qWKRL*Mtl3Z!<8_O%v#{#ZH2N|~N_enff6e5rPH>+oV{%YdMTjcLeh1Yg4${S81^95xO zD1u>(|NKY9OgmTP(#&J|2p2&hqMGj1S=>>q8}+q{6uY43xX&Nm+<7g=WANvR8(dN3 zRNY)fR~Y8d1IF6DFh@%gY##71F_giF4h$PN@S)OhCwsc0&7aP73zI&0gmy%9`&K;I znlX!TR$j=1L?xe1L_fpSpFki8Mf7AX&u3k=@zMLC3%qCm8N;smjptDfi zua}*(98EsK+TzPx`yHZmc3hH)COb{jAIsTOV$Zy>(Zmt&%7kD7=ILNN_^R$So z0b0F)zb;alSDco}^P;!|C0?v!yeffYT6Rb7Q@&AqlIL?EwcidHgAjF*LlWL}u+ME9 z3L2L0LX^$0rRa46N7&P*y|q784$F0@1r*&i~YV#tJo~kg`g^~ zhY(1!!JOja;tW2S0AJ+UeL~d6GuCu|YTk`VT2GbTqC{6yCtI0ube;KoIM#R6MxcfL zzvrjc6t%dFP1+D^;Uj|Q@`yaIPv0@qllW{D4Y&^XW!L{K^OghwMSD7s(R!Nf6n3(`^nW()Z$4|+0b4i)$?LA zp3N!KZ7UcYyTc0-Vx+=6SNFN=k=U=(-HEy|&sw0?vTTw-jAwqx#^-X_1#4Qr){VZ& z=WHsLll!NNjO>d-0v~f-8cgCK>5%&9%CmQ0K3j`EPzOC_$nAgr)m8r7gy%1BFmxdQ zYc(QSI^YWrUKw#6m7}wUF8$|(s*c-ocy6IkkWKO3)wqggEs?z!BHc*p8$)@gy$+*& zFOSo{nfOq5EFMW8rMlz!Zh^CG@l*q4g7A*fh2OQ$&kVgVvzCA5-S9G)2q;iMgDXQn9Do>6 z2@?tcU>rbz0pI}n9=M3v-+ix&MgxvUqzBJ`lg3{2DgD*T7Y&78K`@v6$qFi$gY2C$ zSQ94~g^PX>T!4OZS`7hUaqBy;PS50K*`5?{!ujWq%q>D@rMl0i&s0MiT5lwo8q(C^ zqraKmf+~?1`Rr@-rteVD0yFzV1bh=Z+IV7R%0me$=J3;L+R%Ghp&T;%g%dNW2rmtq6X7U@ak8p zktn);jW+i(S64>1{Nn=PfZ5CfY6s#dc?S1dLUqyB{3AGLc*3cI@y8Xsid zg5#tBxkMV%3F&e7@-YQl&_oB_`2Qn`40z$}3Yi>}C2+%VX`tmZQ+k7Cz2d02Vx&ci zsKWjnEabvmaDcQ~r@GfVxojXWC8VoGC38XpT`?L^u)^Nt@uxwjER;jQ z!}S^cLhznzhcDl%3P$ybQs&qYrO;WhWfbr}fWAJk`R6<%oBAU3I&jA0>GevtB`Fa7 zs^2JML6Y&Q_6#9V?2Sl6_<7iB4}v!{gDbu<==`#$1k=`?)81Wb!;itfg=lX5l$SOS zYxx5!Av8g=Ebbi2x);_6^%*-12vze>d_344Q5n_tLyZ}@oP+nmwZB!Wjq&mdRXJYG z=$CmYaV1f({I^v#EAs@9B@;6bU=?*9f@I`LKaDSrD0`A?t=g}i_w{#iqctbm{6e5i zaC_CEl#*b0K=0(YAYOz;zp-h}L8XEV#nDTyxdKG$96$$4QKoa6VOBmUDQ}`P$TEWk zFrG*TR)&5(fEa+DCIA8ffN@B{-~h+%Ee4bv3TvD196fH(YOQjx& z)cI1Oq5T&R62k1ib&GsE2S_-gRRS#XQtdDln5S2MXm9*vS-lQ=`Lo$Y9J5 zyL#CLpIc;IJ4lU&Y)y;?9*E}g>Z0s#5FF;0YLJS}#kWg*Xwg|}Qi2Qj$gOY^BD3d( zse*pBHx0^Sco5x-iyfMxqMy2M1NoFDhL_%kD4LaX^t&XHPn8kHcZz&f9L!+`9f=uMIIm zC71t1#h^3ul_TQqHo?m+{1aTWMyHRTq!zZD@1Q0!qhIA*MR7C~D>?1Al|2>ORq$8P zyD8b2YNsj5kLxP%V(TK7?Yv=w5TN{08aeS37_$F|KZCmf8kf{@rG&}y`(BL+V6k-x z=^d5ly0z-&^VXM{GWze@_XzM302%?P5&$y21TdmNikAj{J%AvAh$cjU!8eVL!QcSS z)LI{(j4^eK?taE4Q)lsEL6-)94+{Pnc5oe1w$;fQL<8yWO?)GFVI5^=`B1}jdzr&Y zZS>Zzi<|`A$Nb6Zi6#JoY_HDT^P>=@HWuIb;u-udJ}Ka(5=(!AjK7TW4xD`0xpnOp z2LTW$GD4Hy^gAjC%#At2Ri|yX5M-d1;n#Cs=dwI(WOG%z?-NTtL6Irod#LdlX6D)x zcD^3hG^&JLE(bO%#B|h!8p%UqSPtLZS40F0dSf#Ll zY!wb3@;J^Uy2eR2Xz34d)WYp6>T0<$if_!gw;!>Uv+$sBR^^m#xnDtw z3BEd6w`|z+WfM9vl9e9!G;hmfxi^=Ws@)-gMSUR<-l?#Yq7~&On}9BVTxTR4jLyZ3 z@K>Co5FwzFHsovNv&oMcUC_m*tsdX7&E|h}F=^^*UTxnZh%E2kag@~N==8~}iA0`C zW3f?rBrrTcI+SEI8tQ|MbE2#ak+xmKI5sZ&d;XbSG~Rl7<0`||$Znxk?wnk0*6?K! zSS`50<%ClPq7{ZHIa6MO*=YcyKwQ5x;n|rqZ$0?Z2g`+()Nb*yMKJG(sZf&P5fq`C zXXO61$U9sKDKy#css)(2(oQv+2_=-B^5&$yc2PjlR08^Jg z9DocUL=yl20g`CQ1b6`0e(RECr%|~&G^=$YX+P%EUfB(w+l@%ayZ1F03N;ejD%db7 z`%`4=Oi33t$`y~4sVdyhhX5e+IwHAkFLF1;+_S#>NJY{SH3Y!4!rN1{Qp~A6!V*>X zsxk$$opNn_@x5W#yK%(67{<<7sH4q8p_8+#!ufc}=@WtM-?z&ntWkJJAxKo-s+B`? z;wKFb@K+J>y^X6tKQd1<%`?`2Z?1>x3|eSciPH%N77HqzU}a%90gfrjij`}DRAee; zlDKo)Ynp~VL{feDTAv25ef%<;$%|}|jH)##Z@x6`Z>2#;NdjedG}cnE5hofzK(eh2 z5JS$Ce0%o0`tW!c7%j!+dkw0PxHXm5hL)u>T&}H`*Tw%mx`;mwJS@rqNz_VA?FY51 zL*^_*0@>uX)wg=n&0Q?mb4*AiVp2^q23_N#WbUkagVF^I^O5~GAaE`4o@JlH2$p?i zmb)^J?-%SK=e1CdUM6&L-8(1{A-)dpW`%02quO$=`DRQG94vpIbZ{V6_-_TNMrqj8 zU>b1q3eFCSB({YY`_d;(8TzTu|7FoL-yh6)rePa^QyJ|@)Y&xg5V~@|pJ{9v{dXDV zum|xuS;P*E>8xUIJYiuTgkM2c04HP~EExaSpG$a1Td!q`l`2|m$|uDwId=~`*kLL} zfKfaW{s24-YV z{O$#lHfIwXBxj_X+P$faE!A;kz9{bDBQPUnrd3xzl%%1edh}(p`AO6mwC*@b=Vrd8 zJ)}Y0H`>4>&X3kpb!)(BKMd^?=g=K!}FduDuht9&+r*GlvS#nf9}}nmD644Txg| zClTUczN@Hlj;n@&v3yXatDR3Ji;$#%V&YJ*7E_4uRCS~zDBdK9FaZ}7*eznzT8XhffeHBn2TZW9*T~y$m7j6h~gN=cn zO`p2AK-$r1c%_D#cTs85`tAXP^d@p^potR6fA@P(NdG{}*KY!cP%F~+4vEyCm48*j zAE@xdw1@JUXcDReic%y18-LZm}6$A+;Cm zl;{ShiDJ})uDQ4csL+x|FP7gLb1mE7B2WcL@9XVu050uA07xpWAK?>4>_XgZ7`VAw zq#`_3E=a3-|FFKAZtMd^8`2;ur&ZGLH*m6+HEm6F4K=OT;$~P5)U$jj*)A`_WtqA2 zseXi~wmia_OgywVfj7FJGmg@(3k|6>s@CT6;R@-B2U?eD7P0H(@zN)(rv9UsJ3$~W z@M(aGUCF@x_+js54I%ht?g_dql3-&pmhuc9*&*#Ny@Xn(6Hnx7e4)^BZP4bz00T^r zKo&CX+0X*aT8vMTfWf|3x?+uAlm`^ScUg4ytsSpPqZ*;kzv3Ex8dDN4hdwHP2>jN( zN1sS~qc-$S*lpmZCTaZI0kL8|0k2wzV#M@MN!7$KIJDnKAs*6EeVOHNk7B!XuwmvC z57)f}OdW~ORT5h#++=7Gouzhw5&#+j$PxfDv;;7!Kv4#6em#H~KmsKQ!omZ<0Qt8Z zWLqN$kV9}5TtU7AM7UBnLD^rP=MLhK4TK9(`)`}S4Nw6bHcDF+!y5k zOr7JLsF>a)dC@Wj0DT=Hl!WK%&1^-vo!3hPU&Q_DOFJY9Jqrx0gbA_YyNdCul}b_v zymyR;d0Z~`j^We+@k~Q0P8qd1R3)&ooii&B~7LG^_>l36NXsNh)4? zq+ra$sqFZiBs{yngH}IWEs{) zbab2bMzMxz_{nG#{v-8EZ10Dk-y@RmNx_FZ*{d_xyc%D7?Uivz{(BNS@Z8lO_sKsT zCthaFZ?aOSgzn!{)yFPZ15oRV*E$jaG8Hha&;llIM~>hIFae1G00IEnomij}05b71 zAYx$w7B#;KfB*p)0s;sDh#&v}KqLSM=70c*R(@N|LnD;A;7jr&8_Q8Q6E)t+S>{g_ zg|B`_7L|z=E-cvQuz>Z8tine_qiRy1IgnTsjg-F0Qq(hc+PPhO}eoE?K>@h@6DGCrj&wn_hvsq~b z=kW18T!+e2AFheam2=cQXO>nBPbH8iP5My*qxm&a=C(f^+(q*30RL&JK5c*9q*WGX z9E;@V@OcSyYh>E=uo(7?y;c+@9`W)^U#O3!zV~wqasT3GS0t{JeitoMMSV4HK~5QV zaT4N4lKzY~_%$g6#hDDZGY(bupVUw3|J|P!9QX?ij?5LReC;^y3bDI+nYi$Q-}u2W z!0b4$$%OOej=C_s+c3pPU@`+>HRruYiTT`nARKS6+5GnjO;k))lI$g(?cB)IxIl*- zUD>VkovQr#hOnkVG%r^>ssC+oc8d{5`=Fv%O2s1+Y1^L4I9WVGIB-(DHvDe6J-Eig zOc->kjT7Hm8)@`!#pyrHl^8#Mi`im>^`U)z*p89cP?|OQ3s)ko@H+Aqi~E_yu%*H* z;sJdi3M_fyG8Dt@onJhYqInD<0+h>f1plF}%K*Pzg*T!TFFR$>qf}7APy&G}N=u=w zBra#TX&FPLs#7*G%DA@^R-0zp$^w@lh^*~nr@V-LvcJ}TQx$p7uGpgrr)QSy0imuHU!b#_Kks2*f;#HBF=?R#gTE#Sp5(id`JUZvZmFNSGz zZW7DS+Q~_+WX!QmsHX*iYvwGS=|mq;-*fa zmk~#R7oH6BZ3sVZrK{PZ|JZ>g4Kp)Tg4Ey5yVgOj$6&HG&*d^lwr*3X@Qzj^8Q3zu zXC@IJ`sodIORgox4>F_-fu)J!$5kJKB)}Xg!!xJ}v^;1ZC?AAI>eN=@g>!}}Wxp?p zkL42JrlRkWi7^>fvhDu`jMzJRMbZpCsHpyr37wAZWFBp(Fb5)tZE!_d?q{|mAwthe zOeqq3raW+@$%1umR-l?4Ju)iBF~}?@XlQs>Y*$N|7ZEV- z8(Q`29FQ(g5+v_}j8>aKN83IZ5J|d3W$3ZiEZRcmn!KQ^(v6lW%XQqSBj8#8q9iiCPui~M0*yKFB^&+L)O)uDA^?$Z2JgD8p5 z5f2f_E7kKev}WX%qjWOP93;7uzbq1~1pLt?%B9UP!P7%l4#LJ7EL8Jb9D*^O^e^u% z33={F*47s~A|8ij66Mv$C~bt}+QWySz&5Cm#^$M4-Ke=pD4%+#>k-oJLQk>SG=yP4 z+M1S2>8%R`fIg|&F8CCe{N5tE{J=EvU#vQl0ainD;vq)ZFt}BVOc+$OB&{PRwhL;Y zmpLksJZRX$JW4dM(0zU}%0ouiDD469d8vOs%W6`NGV<|6GvT8g`|MZa1?1IA#24$Z zvZQ00a~6EMih~K$<$#las4hrcU<}D1VXwPpT`F^Ca z6$cKzc?pq*`er?pN*gbZCK(cbW%5dEWru6Hb=e^zUL1mnQ0> z8;`BjDNOqmq3VT%aIj~O4;j`5@w1kQ+7D&$JvLVEzq2@0N&137Z@B10VlHKY(YCdK zmtYm))y+~v_nnO`%Q4a1zSl$KlcWPmdUDpF{YXoj&3Lj?gxkU-#^b6Ums~Xd1P4F! zZ$y+5rJt|JUc^nlv6Jqp|9+=?kUQYPBFM~EKmSf$sBrE5)s*;%g>W&NI*qgm9P8bN zvdiN~TeP^mX4C9Co)|Y{K?Se)OeaC(#;|N*u;$@Khfhbnuc%^?{#m09c2?1RW2Vh{ z@_MmN6ffWVg9LDcf5*xgSUKJ$8+xE@XX7R4l*cOV|1|~L?HX9&E`b~nH&Ru#Ea-6? zKz5`lP9{Y6vhoqCVlqUIBuhIQ+X}WpOwHa=XvRDJVHoy7{ma|-pPe_T9*@>xDSFA- zo*By-9YUKuu=zl2*(7PD!}=YWYU2gm?irbUR_GE_zq2n9X-d(0UfhvndV%dXa=@i& z{@X<4M6DOLB3G~5rY48IJNtp1v#~LbaIu+=E&V%VcF4elqU~BF7W2kL&zZCpM_61M zv3#q2F|U2aSCxOjBsn~#jUOVbEfs+b+Ys5S#ZbV8@&jrSRJKXHZvyjjF?*9z#Vob7 z=;Tm69hs`g-lvVZ$_anjFWHm~6YIee@^LU7wC;vkqroF2j02-#xCB%b;gq#NFHjo8 zZ!fpgztd1HN8`CH4U)J9;or9Is&*5#KA*H}WlGCa752-+PI~Zl`$q^c;fou(J4X}< zH?`A++oHPUtIhKc25g;J9!$=%v0NHxPjpUzk5pDws+|u;zu&8iUQbT!rpB9YT)DVA z9b8uNx^bb(DSNej8YRV|T#00KWS%Cg$<^ei;^lHi#aY%(L8k*p{AwYUr;D58>g+`N z@Hk?_|I=+o#aA6egKuU#tchwD55TNroR~d-IY4x&Jwd89@jPiM+~omPbDhnhtIRcs zeCtL&7B+Nk&O7sVP!$wd=oQsS8H%tu)n?FwtA&UMq#+FNz8DjgBGG`lQ}QQtmv30j z^9MVYFMc1C?uIga8Csq|Xk3uOHLFCOg|q;EIla9>VpZt?LGxI)Jv4id*QYQ&P;#j< zzmg{F?pH5Im`F9YSlTsTjOG-dj5 zPaGP_X>Q>`Ub$D#> z>qDTo8X)0p1x4(h6s5(hPgF_)wBhRsSAxXC(wVJ}ORP_op@z=ow<|1WImQ`6g=Ehk zKzuJ2E9z8Kjlo!%2xsOA`6)MSJ{grhqSl>T7|%vqgE2Jn|M7F^lPF&AZWxSXBw}z^ z5$ayFn_zDqV2S*ck5-FpmawBtw+6(l4C=Mf^+$=i6ouAaZ}rVsF&_x?Rq&y9sfltQ z$^0WU^>EbfaG_|7H_Brbq!M?Hw}=O3vK@3^tkD2BX>(YNEk{m*4kk0oKkLU`>7s@x zb+{yk!1~20H+9Ls`8DW>7+F}e=EPWwk>w!=(%(lZuW#JX^(_=RAq3^|NZMc3DFxqW zC<58IhR0!~Azql1Oc^$xrZy8VK@?WaNDZem?a^auH3mZFF&#VKdivi?GgQocVECes zr>?$vkvQegKfl%n!F%TQ>6);q_S;{A_SGd)3dTI=*Picp_=enLQg?Ljdau%85C#N- zOy;?t%z(N1zzYbi2f+9?<_)Yf>#+zWnod6?@mlx&;7df+BR_;@9^cfUuc?Snc*%mn zea3$g+^3?v&s-WU6sG%w!qX$HE4MUrUU)+(Sn=x1Sz278YqY1epf{ypf1fjNO)!bF zIbaY}7grn|t$;T}-w6$xg7WXEKJ2XD zy3oge4h-~kPQAJItR8j&4Lo_dx)Wn5zG<+7BWJ!9LYJX?{S(``f2&-lD>E?pe+oZ7 zo(H`7MK`6`X5&h5AIm?(6vE!Ot56qz2f5jVSaw}B(1?RnNE3*l+-^ZH{?35O@zTv& zx0+}g`JEItbp{GqFZu5_0U2-GUi3uH2lcsEIJvsyt}$uw{__h>`}Zp~1<3H{{%@%! z3U`RNyZ$gm9H$cDsDi7DWqQ%WD@9ft@-{Ty*sxBP_K{Xhhg8b*Dw9@%Un@Nz-nt~L z1$7JAR!xmiacre-+$6A1n{uz};LsR7KR_3k!)|>Aa$(h5m96}7ExM)7YEtugj5Wua zHPlxudH?!Jrf<2B0`fna3!vzrX@SH(`-g0kvnJs1NGMo}JzFWl-0E29yCzF$CF@GxJBZXl(-sep^+tG_2;DAwRf&l-n;_I-lww0 zP8I&_I>m)ul;+wu>iR301$9xvrcqaH5G%95qbPh1fYI;B8e1jw9R1f`6Xpw4+NWi? zjrdcPoknDg#dtd%jW?~e%9ic(P3IvyT(_|ovYlAEQz`Sm=ulzApMkO@vIwQYHbRUv z@2VM5>tPpV<=j6X@le^+CsAqQ$x_r%TR5qL4GuB z+{sjcu>E+#;|y+z#nn~)mIc_W*4)Ss^Uf>du3`Q> z%c1*X(ewCibLKA!y*o3?b%%OK**pK28Lo}G8>=IiDdZe3JL!F!rIb0t!UHuU`n#-( zUxCs@dkV8#Y`WcyuVBQ-P?(+=x%0S^LS-^OKACWM)F)HWVQ?97Z(ek;;ET}W$g`YP zAeo=A9hP|Zzl+SZdao>04?Th%w$Ec&15HNqsiOa_CF;13VV56L2QuJhoQ8QqK-2tr z+On|kYU?!<8ie@H%Cl)WnQ*9rLz3f(B%+JFXU);)>&Z9!Q*)Dj-0bxDT4UP>1GQbxB^DV0Z=waTt!7(32Ax)vp(uN#=TnP(u241 zDqK{if>?LTyJqQHsxW*efz_CH*2u8Z&(%e^I)j3fQV=RF*r4o+sW`(Kr zG>LU7GWzbE00JgNO&Qe=_I?^e6@0rzwyv@;p-D>b?5S&%kN_ke>@OppePv|$Ii8mz z!D)u`%~g?@krJMWY;B0x5D=N0ZlT`FTs4u$`ct1?4fmo?aO){6iOy+J`7?$dNkM@}S0Kj<`F@TZjwO-0|diBS9cHr_6G)I&L zAJNe2cl(tcCX2Uy6kA8fqKMwRyYNqy!{XF`WEM;Ni+xCY`^qV&LA9J90*;KNiaLFo zHuh)URPc`RE}sr6kQp%VNmAb25hqYMSHY85Kye`Qvx)k7Ft-`2oz z-3gEWqGh_H)$l9|>>iUVwx;YbbCLj$91lqP33}iuX;U2W6i&w-sNc0JLJ*yk*vkz8Obw^tncF>aLj~oW}4nux>9dLM=9Z z`tR)zr_r~Uc46*^=i7u`f@0mEn%n<4K7xPSz}Yq@s#?i6pQ|05MZ}Lvii+9ML&raR z-zGU-RJb0lVZ-WS<793J>xh9IzW6^;j5Gu}H=L4nK>YipnD!>_3q0St)6G5^Q83;Sw^z?u1#- zSc17i?b3Y$;;4>oJI}NzM3^L>M;dZ8w|1;pz0ZqRdrR{{Nf{kZKG*S0bfbROWw2c6 z-BQJ-+MX8%J!>|JrfAyU%q)Y~hVdmL&2&Fu?H-OK-c2=*OHVsvIQ|p3a{S*!GsP&z zHq8*|mu(b0w+nvKJ)_JHeEpePaiXQ041xr5f!n@97_~^)a@tSY&Zjb~24W6L;gQhs zv&hNs8=e^GpG80iJ{U{QZg^slev&`)#x^X|(iG^$2tJ&4)gIxVmHQ{G$Ml^m_x2;o zP~^b(Vb_TgCvwbhpYe0X4aejD!Lyn*)kU-*iri4^3B%A1Sj&Y2cWkweIP+xXWy5gLI{$F*t_w5j>7kd*o zL|!YH(0Sy0O;uxoD6XFRdV1i8pl>hU`tA0--0)IfoAEy z2!HneOvKP39?;5UyE&sfU6b&MxeZG%m^kdMtD?zajsOdq-$(u4y}AeP<#UNW?{t4#BUIrQCa zX9?X%F2SYNc9b?#ZQW&(>`slwsU^T(ePCj8^aZEWA?gJey8Ts@#W?6#y&5qFw64(2{q+nla|CUJOX{)>Nd-q z2p%@;(^7c{wc*QfZAcvTVQWEl@wm-*uMg#?4mH=ylx_z4tk2pw5{rAuIZO$8RehP% z_VEt3(G0C-%SFS3ugTu__$>^vlU$bNSR?>&DI z6*0OK3acQPWrpxZT%|0EXWY5?l1Lm^2`%8&7iJpBCI)}a93KbzEmJRQdggBZ&t5S6 z%P1YcS@OSZ;tr^Sy}kHl#KR*3`0^v!vN;~DBt>jp59_{!Yhv^>IK+hevwt=0^fF1} z{Yepv73n>lceU*WuO>v6j@`I>ZH;HmI^E@&^6w<8e|GtHqn|A;mvzt4R-f5*>@`C4 zf`0GQJFiDv$m2MtJujXeK8PEim>-&ALLveO?wSGO&hW`Zx;+cStWqhpEQ40$X(<~D zI2R8lc`>>x2by=uY)jfWQlll2i7CJjl6(D8X;%&R5mCc(>08Pn+RNe-?PKh!o z7$vxnvAo67r{^s)s8eOFeG#I1%*WcZ_VF!o;pE;DbN;%G;1$+vJ>vJ)LU^EwmEP1l z@WGQVX2;Z}bG9hOug2$rN`S+W~tS9N{xgAk#u+vg#->L zdH1UhkInf6fHAGJ-LROzy4q~S8Znu)^ZQlxgcM$knqt?**jCJdf)qx2Pzej(%RC?G zLSF5(S2wTKKe)i`pMw?#zQax~&v8YvOo4^jn)4r}05C=OMAuX`cE8-Ht)C4i2ITp~ z=2Y}$shQ+kfvBV#m7+~7auascT+Z@!%;d6T|7jT_Y+t?c61lHPCZRZtIpm04O4o!5 zKWUz}M)$?Kmr ze^~EXw~B+KX*?DSQ|O9lpRX+cQOnbdXc?Q>B5SEz)Oa2M8?nahd)gsijF2~2%)ED9 zNq}j>BO7WIYGRY1JkA1|wlMg)qtZxvI`^fB(0AfbCLUSrfqu$Qt z#-r6i1W6oFNQx;IxH7*DC|U#o7B#;b01QCH69gy$gaDz0XaLD0N5mwPJbBc&foz&I zwlIiu(XVN~{`={Rcf>h}L689bK?-n-x%JkeXe|4UmV8h9`<@$cHIv_TZu1{zWraK_ zrPiITr0oP4ZQhJa{YnT&CAi8UuG7AOi5xT9El^Plj)~-f{;_up_L{N{Otv{jczcx0 zsx)J*P**RvcI!^nu`n{<^+RsXw}(L=1cau1F^;L79)pj4mjMoblTK#B%Gyicg4L$( zlFct@PFLTnG48N6g>b)dW38dod|FWRYd$dbm)b|Z2RY@k zrpk8RXAFq?TWKD}I@pve>T_uyb8Y&J9}UTstPmKGzitcqaZbg&^rCGUsD433=kHo< zdSCaYMiaOjK3Em+!0b94!XJeWILMDIVp*c&ew;l+bhcSx0-OQit0nSqNL>p~*T4bc z4|-aZ#_*zKIdF2KM^K9+aION0M^BWUD*4O>y7ow0V#r@$zgeAGP@KwF6@GGlskL;Q zzh^I}emUXF&bfAkd-ae4W{M5VsE@|Q7ysL0>cU32O9%Q&k=2$%NOR0c7#3g z+RRV&zG`#df{Z7njPF_%49%?el;(~!^009cyr`-Ot|7{o?0m853;mi%1P09P$ZDn+ zlugbMQ@vKC;m*!po1#a94svbS4-B$K?y+ON>5j6*9b0gGIH;VJjv#=a&=eL@t+0Qv z@FdGwms2S#nftp?W4npfKAV+h(@+N?Nl z<(&1{EZ;aJKU${+6laFS`sQL^oJEsgry*_1ACSq~3x|rb{jV~y05z=`8RU+SE9yHa zkTtk3^e+q*K@D6ViblENu!l2*$Vc<7eW`-W4HKyI8?^}iF|vIoAFlA>OyjGe=$1ho zam6~Mxqh%SVlRt3D0HXFwQbdsbG24lO3~s1S)F}a2Psu4h^qo%i~;+LF_jlvAd_~; zyRpVuzKcfDFQ zZ!t4V7^Ztc77H|ZB$l*{v*6K$!^nQnwUuuS_fAam18sL;4rhgwD=?OS$0@xb9IOze zj*-oPwoC2u|Ga~USCV4-e^)kMq@7En7FlDd^0H4#4{ICwJky0o?1(%$hH1jmpBt<5 zkncp55aZmU;m4fO(obiv^H|3!L#DF>=cfkZ7FdD{zK-n1vm!M{uzLmtl$Tq{@y3t5 zQcc*xB+0k~l5r6=AYXI1aMinpQK2+b;xb0Eu}N!D`?cJ?rxXF}ni8v%z_TZ(KvCEs z>Emc{lAF`gZ2|2#NY^Bc%qjf zOzDL#BQtkP3C23&Hq8;sD}Or1U(mwkb$I8tbvL6udz>S{^YCD72#SP_5y=i)2Dx>77?xQ|^$_p~U}+J|e?^(qn+4<>M=AnrBy!x%MS; z!cyeeX}*X3?}yl+&&eGJW=W$=M;jrgB4&qRtxdg(-m~E_(0279TLRF8h$BK(Bq(E) z)Zmz0C}Dv^&-^BKYc?nVwX)E{M+!{eneg`~0==B!17%b?9#j=8eWIs8{8m}_Vhd|R zN4Jaf5A;#m`KF0_A8pgq089i%>BfumUnQ_ou`>O>2|rrWgLeRYzr$pd*$pV8iMPkR zy(a$b8Sed!l1n_YjYt`t>WGcq6CLxvRJXO_0}V==aZJ!aTLS_c={o}T4$^c{>4Dh` z;eFXiqGVkWGt!9I>k;nugSwJA_A+k@vm3=S+XyIFAVC&2zZ*aVP{Np8S4}Nc<1eS zD_3oGUc-wSVf(y}^o%@=@|YOjvp~i`u4$&<#yH2&Ry^Z!&6qju9S+OV7H+>Qh4w{5 zWgWi%!*5et%Ga=tKrG=C&wi%DLc&>AE+x3&=Wd|^c^M~6RdsdxjmdnEWV3Ie0pzd& zj2ypvv0~L|Fc7fWqk$_Fdn|l+y=(vf@T` z7w3uEb2q(W#D)F@}0dxf3M+*8j%0{oAi(mme~&TGM!y(*S$aXO+vM>{bwpla z$1FaTdjp6l_eLR}e;po6>LxM47xvCgQgcErL%^+eY~i&6Ek~tZ zGl84sL9M{<3-oc6vkw*CloWP=*?R-c$xd6Z^dYKP=gD|NMpx2ZyH3eny8e&Cqdtj0 zVU&Tbpjhl&lrqGdH&(~CtmJYh#-E1h^^XGi{J% zuFl*}iKqUQrlyJoV+qNXc6<2ZuRn^D{?oI!v~JSq+b4|3&P=;iLAFLgt=5tDe;k5K>zp0D~ z0RD&PRNP!CZC;>VT1*BQ9 zfHH;!Fs=atENgx{fDpilCIA4#5x_tLzySHRlH3&(#G%a7oQ+(lAWYUCK9=L@I=Iee z(kK?9a{SAkmtA>hvw`_22B9uSQ|mN)_%BgQRrlf&$O0cJ83O^4c%-|4?oIS)U7p}; z#KS)mZZkIRkg(dQ=EZD%+-2T`j5^qx1;0TfVH|7y>g46Jd?iz^#7PLeCQg;LjZos= z8>1Oc*WN@FQAj$VGqc9aA-2GB_)s2}4JUIBqr^k9P}^k{X_;;zAT7KduEE?;20FIv zuj?n{qbL4tD8^hii&~N~s)8`7PU{@uGNFVH1pJ*5s{o%b$y4+^bdIOKl{Xr#n_^4_ zjf+nmq0Sz7PBr?O7{FZoZw4K(JdeAQ(6B`DyZ3sZlSAP>?V(Qp6)Yh+?-h z#u*neQU?C2n{d%&eHaijS@Dg1^-=vOj&4TOIh6ZST$viw2l_sW!PtI2&wTT*=RxX1Kd_@+RcPta`9Dowd7t9Slp0}(u#BJ0EawS4&6_iNi|2m zX8kC79G|$wy*Z96Q!-#Y;2{(VG=+=l6v>MrV>z(xmm5gIWVVHMqfu}%J>QOkJp!wn z82I$~=(rI8s_&izw@reB?sPL|n27cvB$7fWA^#%@QZPL=?3fM{eizU`R5iX`Uv{Oj zVQi2I+ktdYC>#kOEkI`??l6wW?0N^Ug)87Dflk>KbwUm}bWW}I<%Rm8PQD|K>X^N| za3+EXC_Y8grIA}1K&P;yjS3qfn5YQ*L%-fGA)DCKNLf zkO6{$-~iHrk-|aVDTF85dCSV8)JkJjGoLd-HS)P3RaEa|43J8}oXPupB<0Jio4$xI zQ_sMxOq>K)IK&{ZAkc-}UDkmLbi^tu!Smi*dMN3c!r|Ia^Lz@8@$_~4h0FHhNh7ja7@poD`|SJLnB2#T5X@G2WGt zO!Zg8Jb5zMrIM^LUyyxN&z=&gG`y*5)0F_fg&B!!HHwQv&4cblxVs?)|8Qv1rzEtgOmFUtRHuIQlPla}&*h6WYI zB^qyN1IOFMeyLvKkEO^r25A0O*omLl>RKSKSE;3g{hml*M?1w`IMN3|yv9k~-qdns zB}_c*EF?${ zr}_tf*v=8>k*PH*YX}7YLO&p|b*|kb_Tq*cm2$G?)%Z)X9*g`F0PiH#*-dK6_cwCT z$C;T?eZmfy>;B#L$Jh2w2Vt&TN!HPRw%qM2d_l2_rup258Y-?jb*$=MGHp{+%)8{5C3FsMl zX7+G!PpOR@(N7dK7>FttV>Zt%z;}`5s&^j4DHVtH_3xaEb$KWIkv87O0DJLN%OAE- z#>#*hREN)|O*04MA>^OF)Y2ht5(O8*;<%&!e$J_R`9D9&xkjl zM zv%K9BaUQ3>wI#AMt>&w~)k7VZ4alVG;ZKAV)qNVZPSI08CL3 zji_#J=LnoKR(Y-eYZ=jw)N+|TDYH8XppG&13fnD!A4pwH6L|o16J4purfEs{_nWP+ zdW3Al`JZ$__=Ni`C=xom0P&u_==+@?p~ms-_)W@BMWM9%F|c2G2OGMH2v^}}YVxv# zekWi$7Q~+nvJi!%JpU@{9ELOLM};_zc8cQ*be7+Sqr{RVUPdpK(EA8|tax2HZmwed zxJ_(Y9^wI~@U5SVd`@dkYa?oKkCS6#_Can~#{h>{m+}UxVH7854))(F5>oNfZ((2C zF6j56lIUkxURXE+Q<>9VNx_$PExO!=eCmg=NB|xyTdRFOzxfWuMNTgNH$o#R%brhwMt2z5OyodJ4sd-9fK4m(!Hr71XVe-!`$0VK4r3^J9_!`~0s6P40;9`ZP;smy2 z5p}feu+Bn8BoL2)<~8meaJ`8T6YzGQblce`yGb#mH*qxTW)4uv8}Q6MV7y>aQ84D_ z%z|f6$naQDKFojt8&P`W=>qb%)A|$2+|{YT z0pe>`Vkk!q%;T*T>~$nX-CftPYh_BFFZPc>xTYFQm64uSSr!p)gp=8Zu8joW? z$xnq^+&h!P)gs#tm^Yw#B-OZ?N!Li{EKR7hh9{R-F1r6HA_XZ*aNI}S47+GITU#<_ zIP;o2C5T}$QAl_sX4Z@iS|< z)#vodLpwKkhfyI4Km)GWgl!2Wb5hdFHo%%Y1^2cpy(->9jc>Ke(pYDXswAHQ*}r^X z6rUP4TAM!`xb8Ao8L9rK&qI*)k#Tov0GR&*eHyE)TS8of`jX3ULMhh8G-R}ItO&Xk z^`Eh(8+uapaLv861aks%zftjLU?Dz?%Q7}Rk6Rg@Qi$Swb9Y z13}L=ZagFQT)PKJIFr)tn;t~5VQvat7RSv_m>if$_@k$?m;$%)9A_KB4dgpwKkgDW zv^h0mt@MYWglz`}Ja6zg9w%u3K^pEVNj0n1@B)eSB9qi~midWG!zb04-iRTEiy+vCmAEw@LQ(5WL>5dJ9 zMMQ~8K5J#{qchgNa50M()XGEq;Wkd^fR48!w=Dzu&@iebVFNTEz$LN31j{<3PF>L%KNmNV z3#~$uZQ~hilRSzuK*)yo{h74J#ri-MA=p+9bM!vRl=C(&+62ez((?Jp($Le+7Z#*x;!V$z;dY`(DFXQN>A4R z$H7`@T57y?LSe|9654bILFonV{AX0oJzs90U%Y*gfloIR@QV;;Kb+ZCFKv1zGkHYj zN$U@MILp1^9~ZEdg~AteUbS z3va#*7Q$?G9b^o#$7y`wwm>UXqwQb6ZKK-8iJ^eXVRe?%vskOz94F-6;fk5EgUE{3 z)o4>mCY0{g-^|0%BF5IZWZiibo=g8|Gf=v9lQ=Q3iUU~HY zhbPh~)3GBu{NGzGh6ZCeMpVp<5)*eusCOH4H!VY_sS^`+u#YAt9!YPX3z;&}fbaG5 zJApV1FrZp71ri^TTelZ4*$l1yp{;foK|>#WO%*DPXK8pD@5<6RulsHb*O9 zfcf$6f@0e0iA>T+|DA$9A&}E`vBUgF|1;`bP)1BXJ+nqRUu}&`x$fAGv%1DjkZ-*e z{*+=uFOziVZ*DBC%ahrObN5~@iop6K^v(2Pn;+Z<7C(y#> zGj0nl+l6Ge^U6arSK|vVTaCcgc8GTaUmK$HoAw4dkEU@8XJ#g`tt;n&m#cJ9ya;#!AwUL5j*a~&P(jAhDATc{OrwuQ0N1LgjG#YeXf*J zOS7ye@aVfNEAY`Jzc`W>zh^r6{di&)U5+F$Ned!f^vgDbv1W1L`Q*w-lA{>o(Hf{l z$%YcCJOjU1b3uz%UTduYkn{4$LVVz8_1}Qmteap4@iLL(A%90KXE%T{-32hVK$IO5 zemj6T1q>w|K)}J^0Lr#UKD+D!dZ+?=i2C;W9iWHJ!IS%CtmEuw^GRy}jYPqfPAAXZ zPGzBsD^SBy1u-iggf?mS7& z^iWWj!P&B|UZX{D4T-DHx>X%z;Wg<%0P9lqG~VX(p1G)3KYnBjZcPUoJ{W|=+MoDI zM=S00@aJD^cyCN)5Nb$p3uEAU{Ahn@`tD?@Y73tV*`I?m5vjrz$}>XlW|v;{;HaKs88Y?gVy63RntC=zbftY$gYVbS~Ag_u7=QjS#y! zLkwT*`Hh65a73zuN?J};L)@}YlC2=}xr&h1YekWFQ#uEa$wi_5O@(VSMJ+>2z_u^_ z2CmJ+?U3g0vBc$>&N8*3bT4KJ!$#{k{yppR!mUY}7whwnD;Zy_LFuWyqXiM3u38`W zyFafoL4!JE@=6!vcn1xC)@led$R5DN5p`deh40yF$8Ir6gY(QQ$3+OA9ekr-2Sh!S zP-nH(FpE75&EU!dbll1_(HP{t6&Tm?WNUXIOnYT>-ZIbFj2}!`K>2bExIa{Zr}O&E zvyTCKHk!RmfgmrTu)N@Usbeg${gG_dHZoVA%;OjgJ z;`t`O%JP}^7^xu6Gn#RY2HKnM!%oI$sAw{lJY)Mt6or}tZ+sezmQtAjdhn5=vQX_# z^0h-S!`OzJdWxgZs;+(OKyb13$=>%8skv zHXx?r>yzGY*=&}8#jw>7c8^0r`dw?&9vH)nyLmRXi==F+Q@aA@!UPGsWVwQdNOQGSrbL>0-4o>aq9-(Tzxe~2`~)tfD-ci6bL{j^rlo*k z969O_K%(GXE~PGZ;`8b`nB9$@rVb$`6cND`qrKw%Wo_ZSKWkF3mtxO*07}P`9d`D# z+)mhkRbm@fDSzdME;s`s zW1wr0S#dQi_-VVFNHGm4t)G~u`y{p@vd%fe?z7VB+20l2MIiGSgSR*w`7jq;Xn9Io z__u1h3PcS1!HKj`(FP+-UOi|Tn5eGY1$%0WwEJ|%TCc% zd&|@T7Zgnn0VPkbjCBIfuSq`;m?cQg3p_U$|xNEZp zh7dK7k)OkmgYaT9*~%&uSZx46jaHZi>4%6%a`T{<#H5UEzkTIXSc-+Pn^pnJDJsYK z5)ox4uXxTHJBilh4O;vo-fnGyI|(v*KV?Om;rkJhBLJtCBEsb_B8%`p#uHG^w>KxW zJ2Qd-7s>KKZsWhqdH$fBrtkN2s|)1rc9aZlcnJTJE?W3A9Lo?}vX3|`cq1aQu$fR) zy3U<@o{>gWUQ3w;?(>UqC;E(@cOaw#aY>Fa_E}Smy(lNdkc1+3K zE1fn~-*^P$lP5)7{j>E7nmfp4qzQt=o1f!>sax?%Ck~k%vf7^?oSRk<2BB*=(XK}) z$~)=MI2wydWW1@*fYlq}&#-$kq55jlaT4?6`}ft*_UFM?2!!hrGNh%ypGZa|Yg}2~ z&#b^Y*Muxd!5<1Qq~KOu!eMU)2bazUMyV~eN5GdM*Tv>U5yjpxrci#NcE^YxVUw5QM$}s zA@SinWjalZlnPQPzzM1hkvo7gjs-BjL<-Ccemj5w0%QazfISERJOKH&f#uc&IJdoa z_~~-~G}&tF*g$S*c)DPOvdeg|eRCWNKER*@AO`-g@t;)bj65Z?1+$wWTWSjz@sj?G zuY-}VwujzDU1pI@i4@hBYIAV6w_(ED3HYr2N$AiQSlc_r`o2>+9Z5TPwm|2z!w4NE zg{q!zch>X>BBlq2Ux|00i+ z*-fP-KUQzhHINy4@oJxKWb=-SXq>t;$+8`x1%^6;T1J7JhWV1*W~6B!oy8O&C+#!b z`Bs(b?;iVG%i=5$-Q-z>A|dN>Psn|UxqI773mnl{qvzcWdq37|aw4FzAU<#LAk#=z zL-HSIIJX$anuHsrinL3#2LL9}f;iWF*JIg=;PY=5JJJjRQ1Im&u@4UbzOfWFK*^>3 zaLUDP+x)Z^(MAodVvNV_Hj|L#1w~#Q%pvtvsId;zNjv<5f_*Bu+im0W%7H%@uD1yb zVCHwUuS~{g&x%{Cx&6sLE67W(uR(3j-*eRMmQ!}@up7n`?D*=gzKu9yW? zI`u7*%T{CQ4OKDhbPugKa=cVYnNSeELI0Ma`EmiIO%dr8RTu&DIUMsNEBh}{-QW3O z5&#+jh!OxYp#(6(K>{sHemj5w0%C|X%qJW$JOKIH66|u?xPl`z=BCAPB$o>2Fm^+~ zKWK&n*kUpAUP&l&`8>(^{J?JBeY4>j!nC>GokhJq`vaU@CO#$UyCo2)eP9Vd&SnFt z{z}hn9h}SSf&K32koal-lS<-xQgA^z_!_h+!a^pYWJt+YO;G5+7;P~ZanT=a>Qk9p zy8bn^LO)eX0dDkZact$Zc-GK{OwIP7{e+z@aLIA1m0=>&aG?oKXueta#fE-g>jO;FyN5}8!0wIy`D{;}GxB!C)DM+|~_@FNKsp-3_NjB2vN>2One zc|4p_E@5FEnH5aT_tJBli*HX2SKRJKcGAca4&kGNBY#TSXT==Weni5hQe)F(`-u1b zbKWqLeSMs41TaWthtYl34fmR2nx=dR4kr+QA*<)UEx^u&HWwP1RpZBk|WMoximgxdIob9SO}v zCczTI-yiBAo+f%Ah%rvbqA>?E63uDC@$G@f$>hsm$QYXtB>{ft%n#{Y)ej{;D)x0* zh(8+q?cRs87`G|HQcbnn8lJjaV89Ug6(O)O|E|SJ($7E=05TLX#KH(oOGl0X1OQ

    YaVG;aRHC|so58nLr= z>wD>v1&l{~GMgO2x#I;c(FaQ&)hG~d)R`Sp03SF-`mU_b=qwdi4{seFid)d7=ZC`a ziH5(?M0>hZVvn$BIdAaVrl5z>TI-O+EPPvH;4?pN=|U8-CCGk7TvwH-nMG9cSM~RU z&NISv5MGk0TYuw=EN?G znR@5znW`|iP4B(Z;Vfs~`e+4;G`3y09dlE1B21Csn-oyMP5uS%YF`4faVs%y$EF>5 zOkI7$Wzio1{+NfZpE)bVUP?uR0;hV{3U3ezYe%_fE#!WCqWGXD_Ln?9% zePP4(rMlskhAzSIaUee)y9`I);@r!qIYf%_#bymiIP^eb@_}w{LBSD}^C6)jbxgnfbDt5}JGf z!MY?oX(D)Y+MM|%q&&vm2(m>A_fZT6*Nq^&G8ZYNhBWc>zU-qq)SHoEz8Mcn{GY}{ zh-h9EVa$YPvt5CuRdGqdUXOMm5oUXsM!p^D`*Qz5HtHm7W>DVhmdtH|{cW_?c6kfB z=V&B?42H@=*QivhNoUJ<#dzVHlI zG}R2`9x>t|aJ2jW3oN>MqS~BwDgCA01xO8_#^|);&;*T(gcmKCvPt#)di_gmV{FxL zD0k3rUjkTb=Jc9-)C-(}Dy&tT57G1Ypvswy}dCWt~{IYn%he+5zxw zjS~`%9=wL81S|qub()0Go98GW7?kVdvo?}Btg1V1ZjKYSPw3KcROekY*8V4}w`DM3 z@%Wg4iBqKy9zNKU58eTi-#|W4imWR8&f^4!ibOvMU@{P^(ok0gg{qn5)BbBY9U3hS zTh#`pXjT%e4A$n>a!2b-pyfm z-o9Np^fAzRtwYefO~8WAhbb|2yph_;;Adaf?52JLIm#4e-VcmV=PQfbY zHJaaBN2ABp6>~|mraM2UNBu?S{opqCL#|2gs1&PmXRA4A38z^mW=adr2F`wpk|9Db zUV9gjzc)^>f%JuWmvj&6NhfWU2Jg{}g1~}{Ncl1?dMol6!(k20DOw~V3mNE|?| zK5IEt^-!OWc6Lb4WO|<9f3g{1(4mdqmI}Xi_PqhO=_QGDy$@T)jgf;R5>t=<)bbCz z3o8rbIRzLrk~X70P&y%cLsqS~aZh2eAt%8@0MJfvx5^jN^l{0t@}Ox6<)hmM+5k3M z9PoS)teR$3O)JeF%?>J4(=xE-TOMQ-sc?oF2U{szo8K#)Sg5AkejHD%WZ3VuGQl^A z)-1?`S-#SpFqi@f7}%)&#SLm(DXIEPTeJb`kpa)ZHt!Qz0xS74sPoLcNr#i)4*3Fc zCc{#4E(3V30~Hz2H<}3EmaqFUipflmZ5>eFQkZy09LPl9orwvOPL)C~81R@(@TBNT zZ*ZXxORdO%`1;Ns7)ST?mzuxW@#Q&t0#T4!6D@ni+*VEVwo`H>awL(KgvxRdWFhJk z(mw5IxPC$<%g=oh+NV@H+l~T%sRF}d9Xsye|7lV?2%%$?5pr{HKKib|aF4Abr`0$O zM4Whq6!udZn>p9^ z%lxGYgxE;x^2v(%v-lp#NYo@(Q|_b0?2_uZZ*S1D3Z`Dey|&AiAXIW226`OTau4lk zM0-9&hh)I76?h&O?8A^(nu5|q`0@5PDhek3Yh?yor*Q_6bZyLkx>jlrbY6Yoxm8S;MVYgdfVY8 zXumE`^Vby;Vi#uGYST2KBPz`-wY;OrLqZ8Z*|22MWt3;3iY_?8W*#9aU}MI*X$Jsg zuvq9+J2THH&SK@{Q@Gr67&3$dulbu&$j<4%siSs>gW=Ee%eeT>1 zEkUH7pgTO9nMKBW%!|IjLA7WiP{#R1TB>1!yLH-~E{%-_6KGqJXC4gve@mC1#NJ@D&6>+`vLTqTDPZ^5!|T5-t9XcWN5@)d}qg< zxvqp3%QVv81Lo#izj}&XjJ{7Y=Lv7K?>mg?qJ9okBXll8sKG8{-bW4e?C?GQrL(%=jC@0`&geuBu%jfJ0XI@8D^VG9TB!9z^MI(TsW5__Uk z7P=HFJ{7=>Np_>o0~hLeE!9}}3yG)#vv#!K2)gP*F2>%{i2K8$eGWo;v7uns(14hC}G&sFtM>Z38N?ZNl(uWtx6US56n;J zpP2i;L&76_`AJYMyAS&rqH<6!0e9tP(x;PcAA*)+xOeeS6yU0aW4n5?%b=1A|L?~i z7^YpAP!{%o0YsL^1l9LNmEha2!wdCGWur%c#pyggfJVuhxEs>mbj^t<&u7acVJePC z?2mg_ee1jQR51Md{L$c8>5FC+3-Iz1FByJi*&y4+pM zIi&Cri&s?vhD_b}_OdiT;ug^X`DcZj{c}V8-cTp(8s9g@nhRl&=}PA^l6Qo6UB=ny zr8_|&I1Mf@G4& zhlnD6%0s@vG(wjA3ttElSo@4U41yF1?yt5vek5I;0#CsO3u4EIPGeM~MME!A%=eSd zzEG8E7x#v}XX?_av#?ZE^d^#rW9>P@LsM?|4<&ek68wvOO~?tDL+R?0y`x#`k$GwN z$KII+eqpAO*lq*~3g{nUyW&#!ORgdIp$MD8thH_kkf~frR zL1j%lT(9<0ZkmuytOL$!IY3)sC#I631WKwa@n1zLbsr{6lT<@QO?(oo2oT~1^fcTrLz@)?Y=_grY1IL1LCDof$l7RmAzh-(P|JJRz5ZQL5ifo zxMkH?b~W+VQxadDean(XhXOGLd^q{H`dqifq)Onsdf@_FXqxFz?s7@yU^nrIFx+Wn zHF4|+V0|sDNWre`9~d$4y@Pp|KY$7x%{6jD#gvo;4)sasK)`mJyMzmljQ(42kEaC^ zuRVIPKp&6a8Z{g0z>dmD>Ie@a+5Z_`tMZd*s#5VGmiw{K3(l}^y#&HsueiSvO&+r( zqa`@r?!xd0G8qx;QR;H7-x=T8oC*_#)m|4Upzcly%s(I@Cd4fGF@9 zLnc+!3P-sKpq8=${A>N%9}(j^Q==xRM8ggiMpW!Rr5_+Tv1F8xL^5Hs^?j77zrhDx ziaMi9=wDdH;W_c)g;F6k{JRL?Y!~`0g~OwR0I3tVo^6jRs&I)%j?7ZbIK{Pk^fqM0Zr(&C^F$hzCLn(4J`%Y|fK<3-OrBvlU}`AcsjUxsUf zO^F}aoq;Bi!>2UknVcZV${JG~M|ZQW?oSbzi7Es^k30IcXYDFwrZy{m2p-IW*!A}DM^ zX}}NcuH(I^cxVoIYrq~O+l&*~dHS>cha9?OAc0bZpsD;Sok6E9?M_9pL6!>*Vt8ZU zPF|=sbS)8FH0~lgs!e5uQYrrRt`F}j1?_RCV4`0GX&e?o=c)}Qidt$oyrtDp;mebH z>aVoJnMvF?*A?!UQ!yNdYH1aTn=~2%RNm}hv82#nyn=Hz2S3{{2Bz}eFtUtzoiW*s z+Pp^{k#vIY%Cx!@!M#wayPMDK^FXOC#BeGVVyoF;i9CEU>M;B$K3fcq<_P1N+WDsI zPrT*EzJb5aX{bnhTGPqEtQU&7N?)EhlzfmjHw&nP3!`FSc4R?PCbxwvQk3ihRj>>2 zR#u%92?3obf8{y>wdk=xlv$3YtjQ1tA?>|Zhpq5dpgA>=+vtqrrs}9^ilO^@Eohqy z-b_i&I1cnM`=nLS#Y`7J=x~q-osZH}$$i^j9Ze zBMn#QbyQRCYwfKuaQsugy<1Yx%4YrxYHKXEKhx@oeBPVy)DY^kbkBve)_jf;TcW6y zx0UEcqM3=S|LTsld~D@S4lea(G72*pILCN`FUB z?Q?__$Os?SOSU5+nLQEfya?NP>!cJ`@g8DbC*wOH$JZth91V})uY%MMM}ucQ;}&S2 zvzSl2E1lj&ZKZ*H#~-jeMj3kg-D{YAh1L-e5ES|Mw3Ll;K5{C`aSV+fw|W6X>#>D0 zLt4FuL@+2fobM5-ljnx*>S?f?vrsyr+g7gvXj%I2_VQM5GY2y^VeykeO(l$dkW`uG zD*8^MidKm6J)1_5o(4}jxJTq~*XveF(Z z+r0@iz2k)gs*$PW4S0cfyxG-*DXXLk>k$ZYQBLG}c)8zDLP@8$B3Za<>um<3S%m=N z`ZkGI#t6X`65I($A^xS8EP(pVJ1~HEBOen9v0jWjI02MqVTLvg1)gu&7r1if*#54B zaEF;t1gGQPeQ(*Cb`}CJ?SUbMwby6z|Le*ey@9DRZB^)Z& zPRY*kKMcX(!?^&H{-~n-U-R@GXS1Cn;W;HRt2^%<5kQPp25Q1>4P>;Q#}A`=HdHAM zqU%Sy^TO2;=`{63Ia1DY+#<(<6b3PG{pJ5P!l1!ucQ!#Xg7Aiwp!NUjXR^!*u0QH6 zcKu35W;7=Dh@b1MC3|l&J(|eaelbv$4Zq<2v^&YL+V)~H3Uzlrr z7@gCVG5B8gvVJxFIsr^2(|5D09XnqCM`Nv10#m%m`NIFWsZ`#c33e68T-tk=u*?mW z5s(hm*m^~z5yvgk2_9m-^;fPK*prk;V7t8HEBI#R7&8=^4=>bJVz1Rkv}@6q8eO@96$mRWxd*^6 zlQ^1*7>BL08^}BOu3z^*mb1P1f6aBDO>0%EP}fKnX6@ivel8MA8miwqgWHZFuAq>^ zZ_LSC1++E=p3~Je-J43GZ;%U2Y$+8J>~Q#CM_%0O`(#?;gUm6?Dt{0t~&07N6FKN!FeP=yl$0fK;FfdQZZ`LzB)YgF|# zCPfjziTiFH3p0YOc3IqBZs~ESMhhqIvnj#)=v1EO!=33{+pd!5+|=trk;^sEwoahM zAxNbNY^8y)!?>HPgmRUOZpWs7vUTwF$!7I@KAGzdRJ(>+V;O%nGs7Pgn@r!S>vqCv zev|%HblKCEC7+{Bv4Y>v*zZeN%9$_$OlR(K5Hprw^#k{B;-V&0n7?|~aTG*h7NEvg z4|;js4E9>!Xm6g-T65`2b4ut^rhgmkwR4E1VlBj{Yw1G7wYK|ON{cTs=JN@_dRpOJ z?~n#P{Hfw!m}N3c=QXX$hu~V&uOm}n$x`q>SJ4DF*_dRtfH3e9|6(!yK}!@=z769c zasRrb+HP_z?<(fY+3T4)|texTbcU8+&gUCWu^=lVMeyO$AH%$NqGuM z09;vpraL5#wGIdp8K#r>gAN_HVJlTggt0sx(le9pLg_&Vp;nFCn4AIIo$b6IHO>ZH zq-U$>U?M2e9$ER0R~|)T)6|qy@(~k=hi)K#1u}XJ16=aupz;<2D9uE3)i^~auwGsw z)UwDT*#PK;7)1U!jsMyY9`{=0G$MisN5xlCS{J-w;+E9K{?h+)WGr!MotXBCfOCd) z+)hZpS0h63#2c$a{ZxPDiu0#z+idnpts}ltbZI56%4(059Xq8mdDhVOBdUqUex{r> zLjKl+D^%C=D%BYp)K8z(uQ+1{-uAXjr2ssT@jd{nmcS-0;_o5pHhW_|d3c>~p5H_| z@7m&J9*qLYS4)?Osle%cpwsWztt=1D5J5z9Wqu!0CyL*HyR>xC-Ij@2oCHw^sTXm; z1EIz#WsICaET)-B#EOawjIL8evuyI=PhNL8Ww4bs zrWq0ZsnZpOfE_GyIz9Z+vJ+V0v_uGHKJll&6~f(tdfiDqvF$%5~>nj2~z(QscxcC+r^ z14E-}=u9M?-hFTnJ?im&kiK)dFbfb zXMFKDe<80kk_@$PH{m>J2+yc5&LIF_AfEF!X!f>#(SFN@63|VLiz0rVRD8Hx-d<%u zh?_^r=7I?JE9r>nSh_B?7=nEVhtOl3dULBEr3v0F zh_o;pjESaU-($CZ%5?on4%qgNFROxRMe|&W1T#R#mpLoY0aRyd1+9^NCzQ(Fk*Q&F z1vxS>A;gauVm}>mp*FnW|4tl`C3jAz#+Nl5ptvAZX6xj&{onQy1w;&YwP+^(;NkoO z_=iG5U{wQhNM-V);3NP36xa-*eF%Vy)d>e{1NB{F+$)X(j_0OTS2J_b=i^q&H8oZ^w*i|k z=NgHjlOfD3wUKHN~t1-Zr$?kNrCI)-$n7SuYME7Sq&s_{)PI zzh!^k3%7Gq#Sg!Ffcq)J!u@)Cm>eN_HtV13e!+V8d3}M(O}U*((K<+voX|?}cTa{2 zWT-ms<}k+?HZO1jxa#vNcWn$r-sctUjFgl)_>B?+2wzD;Nh3g3v5|_fAqU>GN<;+J zea)49|mQTE3Lp z3~&}fv{t3KAUjs1JI@Nymg4ko3?xl*;G)raw6pa=nMLJx1awx64KWyP#&b*-{BK#| zW~Cb`yOF~Xn!e|iUgB7ifH!R>t<_2AiSshf+cr%_muDj;P~$iLbU(cHZxnY1;?C?6 z)|#GDJmPlg#P&PcwWG|WLBKNe2PkAD1S6+E8Nd(`1rq=P1`%k0f#3l7w<_=;FlA~m zu;FYLvB@BD?RHwi`5S~hB2K6{u%xfg=^yo?v6gHu8Il^8`Y7ond711dZyecZCvL9d zAbhxTZB6P9uMo>UjiuWb&8mm{m9U_zDu1jL&@9{VrRT35X3aIJas)?U-((XXASn!` zphGldL;Bq4azKIQYQOv%cL6Ck%g^-zP`b7H9!Of)T)T|yJAN*{*Xy#+;F9{R-^F?4 zmNcQRwK{I(G-DgPq3-Y;_dX-RjoxNO(Pm-B;iZQpk3>hDl}dB}sJqt%wl519Qlq~I z*X17`{xK2$%qunE42sdzsU8Xd)Yw>iKn>agwbXpDogy8O2ZScfN_+|N zOk)c79$lLDNPN4G&M%RQdSVs1kTUm0>9`utV(j#=_K?0wS~a$PNeuH$%&37*xcDCG zj?$7-aG(W%sBB8Hh{e2bIXaXklfOi+@^0h6rCl-Fr7Y_UM_Tya;2L%zmma^}uIbYY ze~o3^ihAN-z!~33*ua~sIl|MMb*#YybP)z2YQSv?qLA0l6L#aLRTerp<(QV=1j5+! zK}`PR=-+eGbU2|-n>x5w@abSByX$@hbRgGQ9RL9BW%g3guJANB3s^4XmH#VpWIgk# z*G_HE7=$yDTXkMnFkSSQ2oX&}46A@x@12EHarTuLRY0fV{M~u?HyU+aQ`UQX^4Ibw z0Rl02>1(mA`Vl#1+a-Vf-nv|_RnFZw2#_MMfOwc0g_2@jzgQ-8z{2*z_yY$LEcf*j zO{7rzTH4ZDa(bH9AG772@Z7Q>?XJ{Xx*3h)_UCrO7`#3z-&nT&8SLr@T?|aAPI_-~ zJWTy0>(P@ zTsh&AkXA7j3}P}dVF*jU#n^~ZMIk+~cPMG<0*>2oiMN-<4}2s>PrEg4MI>p6f-!3{ zYtUQb&zdr~G6zPCJNr2e8viR}(MiRY!g*t$OKtKw(6^$G3M=GTH<%#smmQ zPJTE55Ws>a3`Pq?0hj~80Qt8esnu5HBaAIYkn+kND+!(tK_{arzA)1m$mOYnbM|&F zTFRS`0uCaKzc3FP6>INzWD+UER>(A*X_auNsLfM#5j@lx=P35$yqt*tUj-(GnnD7! z&jX$(pT>gWsr-&M1FutqGtSv_<^H!5`ByE+$vW=)qe_mT{rkjyV2Yu#^e(jxXU&=7 zc}#O#4zI{GcZ%yJB7mdVDf$*Iv&1}wEUb)K2Cc^7mKfQO45p?5^jNf}#Srz=toko< z)V-Q6AIl#aTFUI9^+PN*4^~KL=Wp?og2i`&AnEM&E3fg=lksjJ_?uSC@pB#_&Qm4u zdUUxZEc01x09ot6*W##K6^zA!nZ7$Y!X`u|zER#k&#H|!zPbQuH+aFUggO0soUW7n z2qo!;*)4?`phA(p)Xw`@7 z`E)`u#J0n2bV=pQB^3CsV=(XD5@Na;^6H>8WGg%k)yzjtzrBi>&cSX&he|O{zLA|X z&9hze#22$svMu7H0W;NK&jNIYWP>l#fD!;20oW1%GVKE}%0U(;4t_WQ2mk~o3<7X` zV}t|10Qt8W+0>p%LxK|YhCN2VNLh=X$l!@7fHv|h4+!gDsq}t5y&Ba8Kc#~fkTSm{ z-yJ4MTiu|OVLg7r2Af$KOdWnSbl9X4si^XA$Sb`5ZSbJ}m344vpt#(wA{>t-*(dhW z;xx`s$e8hKsh^vTm+!$V=mhj=Rcu7YUJM!tJX0Q2mxsrZ^c(BjtGj?FI6gJP_Olb* zzz5Y9&%M&zQ@LGQ+)79-#I3J8=c3hVouW8r*LdVmt-19g!`Hi8a;y+x!DX7GuYV6G zWuN`p9ILeSmV8A5ZGA%)yXmpS!XfVyXX8Yz18b6+ay8`DR!+7&<(>Lae)$sT+Q7#_ zmu;e4O_BRSU@~{3ZwTbKJ-x+j;qi>Y@Y;C5^Oh&ToKQj`4>!>=e6HN7w1ASN; z!A2#y+Fe7DP1tCC*emO;=t{7s0bAmBIoH@=09I`-&Uj=!Zw?T$M`@KIkub?inLW32 zZ|vIpS@>=^j$7R3|K@da;oNs-+6s3fg87;;YX!KB@)f5Nvr-J z(gM=dCA^4vy0}%IkATxO_%lOnap+MO26FQ^`rzG#w{O13SfsbwWhX(khS_y#F#-Mj zil}fZiHM=;sLYP&YL%9csN=pARxDgV^8Q6zS{4X-i_HYel2!+T4to2`z~1-92)VLS z^lTx_RMaHLc>}V@u?L1xQX~ibEo-8;+_R4S2|sGe=kbhB2M_*$ZUElTp0O!Dw1g`G zMguqllOFQEw1taSfKFI@Zl8q$EaNrMK$Kr)&C-k9m#OrDyG*PbdYCW7FOG$&0<_m*Hi(zOx!8k}v#;i?0uDmjsog)B-sSlSA`9vh#5jfm4GW ze^`1Yj=Kn;6>DL4N-Fd>sqLZut#QOJFa^DmQMXoz0gc3W520c4PVP;<3ge!C6TsDbMMI zr_zC}R?bECM()d+jTIQb8hY=`WefSdb1L1>rJWY(vhiRqKfxTt)M}B~Pbt+v94myn zJYg1TTF#VKgij*LHEe3O4zC@is&3T0wXSK;$aQJ1vFgU&2rsjaI)6qyuBnw_GnEJ( zvSh;*N{eaq4?zq{$0`$hE*d_u6PT-a5=*anYrtn6r}eBCmDztx(S?QNVBz-%U(_qU z3kc2A9gGb=8R6&3E`VSKU%~k?>wQn7E_q$8ZN$o)S>=V1o`sGX1Bh(v?$Cx2=1}kdUde-C$TPSC#?3G)RUOm&~7M+$G^w z9$P(2NBK3j)Q^0S1R~B-ZbD_mp5~K;g8JN^6O&$^1SNWB8F=_H^aTJIaKV`N$r1aP zlkT@0zYHygz4cYHy-_a0vN@>&L6Dd_J5*1^*EW5Fb-vqu>n^#FbL0Tytr7@N0+p;K zx_(j7w2~)t13IlZf=lUQh|6OA)pKP-a`5w(zJ4j`&kw%_s4KHB1J@WcB8)VbXJ+;j zpPVq*0#Coz8U!WqD`1Mg^?Om|=-@kNWOBMHhe}{h#h@^mGPv|Mv=GdY#TCB0E60@W$H)AGB`wJKp<+!qlnZW)M% zSRt+31eKXO(dexsvjvhRbeFnf+f*f8cv?}ZO9e*zqX(Q3bu`?RbaDlGl5{BP5PdFiW(0axbaPmy@gJ10^HnBf zoV$I1O%45h_6$xA=fQ0&R$jX5!z<~N^ig)>sn%2reDzgV1?@+xe~WZpB>%|KPI%B} z6`5e!OW<^y;M4CHuAPme*BOyWqMLK${_@9=y*Ohkh^ZnQ}@ z-I)+>Lokd9k84^usM`lE>YW}4r=!K^VZb~V&QkWy9Zl?L+YiZ#gmK=W_o@$`uqFdq_$b~*KBcHMWMJ14tyiFdO?0-w{qw6;5Ch@g=u+=Tm z)&Vk5smTJXSdeH32{$#CSDUQ9B)DGPb`J6uda2*K&vIB>w>}U2i=a2R*_B8oJ=mTp znM|P~DqH~U7wi!?A6D#=-L_H9vTo4LtFj<+VxC9eYokn$s>Ms}> zsoa-O>nh~l^IaoS@b94`f3OlKCQT<3i#|GWedV(P0nuoW0&QPq5TOmXfcmm({HcoW z%DHp8>YL3?4u(ZM=B%{JVv0TDjb-^b{?30uu$0|gxXY~w##*AdZR8i^gd z*$cR3dZ87KbA>JZ?$-T9<$VXeoFKlj`w(7n7-Ey1VQb4t%^72FeDiNaSu7YK0~A#z z-2;_FAGYkj&Ld{gJ}d;2wx(wn5p;Wz*}Za>2kqf7Ao(TXIae587~5-GvYaRdafxNL zyzPdDpQgRVATzf^T@=J^xejd=O=v zs#BYXo3sj|InD^@+9szhgW3z4fT)7x7*l7%gNNb|jyo<7#B>g6FgihQ+em20FiJ?o ziUYdoW|nlwBjJYQRrV^+9q>CJWqhqf)GfnZO>=$^+RpW3es}!n>)}ZXFz;>zA8%5{ z0*7~fy;wpWl;SB4D1ads&2=e5xzlrBzl)TYh}jUwXe&v3N5=5q>*8Nkk>8C49x`T) z&?ZgT?bam^Uty{(z({p9lDC~<$;)o0gM2MqP`V1%szlrqYtIyW$&7np%(QWs44 zY_7%k6+wH8bjX+BI^qscjQ1db%?(w-xLE37BKxaB0$Y)I5SW{-{e|XJHD?ef-9pLQ zr(-#87Ohj@??#6^)a_xnv`N8JnI3EtmC^PSC(iUz&kK9a@6PEh$GPPqE&tFo-+-jC zT$4R1!GJId4V2-Us;ghExk9=-ly>AUo+kP7=W3lzDwPmFBEpsQ?FMS)Q#*?;2S7I@eLs{9WsBwWvrW7^xwxV9ovS7Kx~K3Uo-&@Fps1y&}0H~;_wV1--o01PlZ0QtMSi=`QRCD8X`2uh15`P1Cy#bgfy zYAyztiG@s|&$F(s&#j$WwV6EQc&8^_ZON~oV?Pl7*gT<~i^5r}7?rr19{_6E22E*e z4}d)zaCCGfU-L#%RJ#!emwY)xH&7aSv2p)tpQjLLanNl9-akHLfpqWU@Ve&+e;OxgN_f zW`PLfB$USsXiLotaO5&Aoo9ic)wCNQQl>`Tx>E7Ld9Q0vfhI&Nrr41ZZHrKzdqtF-HOLGL_Ff0jd6v&R- zZC+O8+z79xCenT=D`?pWh(0_rz|5_=gQ_7;9J{LE-10}4SH{z0wrw{kh+&Nky zlS#Tm9}-A%qN;&z(l0cC};{Z1VK&c!f49AXuNor&?(sEelP1*}1`RX(IKU_-y^uMHD*y*!L@B9_fva zeahtd@0Pu^#O!tD5e)l{-{?h)pind6DCcDZA$8_u z^f7kYFA8`S2aPv(4H^;v8UctB05Z@7Fw#JRmZp9<01yBKChUTOcYsZS-~iJon6V)5 zWY%urB_M=9nV3ElhVhV({ZxfBKe(pJw9=uT*_72CmKF8*1e@J#n7N$YsGZu!U<`BD zYzRb|EQ00;@x~a?KMx5=^DjlZu@PT50)(r%@uVCO)ZdM-bFLMi7K$el@o&~tZ8|Xj zu|gLGgmayVX#B66+RnpAYgLMM0*xpF^u9Zp;tYx`?isIGyBf(rc&O#!a_&6J0+0iA z6y>C{9TI*H8G>w;Y4nC)-n*+%7fc~-r{y)!d`~wcJ6Gtz(ceVLUYE50KVNZWl(T;R zel}uFjPNu^8NQ->8nj{@@9Z%#hmkvIT0DuJ54C`dUhLOcDV%Lc>+^dUSB;lPWzQU zmlf`>FFM1i>tNnc&=k^#V0G|FNUG(!>E>e>lIoXs)B-(ApA$DjAXK7$caCM*A4Jrw zb!bcY3j48Lj75FfHfqtia@;fKmoSI9Yp}1U|2djrS@LrC62c|w;))nK!`(EKN-Lf5I_?I8YTh&9s%G0`L*Kf=sce_N)c1%nu)#>T;mGG7T<@ENEp?- zKz)BZWRIZEfbi(WxERbIfuc4y32%6wY@ef$)jr+d6-#`sy^rQ`Z}1B*^`W3sQf0Y| zQr$ixI9Ob%#~Y*GVN~Fb*MXDIJi5^YUD-C9K2yBPgmYro@u2)i(D!SW@db zNI_CFZ&!FB6fUN-92kAVuJis`wt9?kI4F0eO)6*XVVze^kx9>Z34^ifrL$kXuH zRinK50$73&f7#nMPb#PrYDrrp`)$QUzV=oyz#32V2mzi*D&`H=GV^4%@BIzYuAvR{ zzT^T00LMiBF~Hv*%nDeeL3D8X9r#9jc|=(y-On*%fU4_e@BWMzAw)%5L3K~p@EbRk zq3*ts6mVdqk{{3dkNIiDVHCldvx>r0Dv30$mz}77^Bu0Ah*6-Fhgb>5(}w$8y;FE5 zZLl@`#I|i46Wg{Xww(zkwryi#+qP}nwv#{a-rqj?Py495*VWZ^S1lZvaVhcvVHXg2 zl&2Hl6fh0Ru_24Mqo`EI8FQv0;+mseM4~h@oLeW|a@|2)&&V9uu})4_uK+y?6(>{v zW@`d30H|TrJPaN=2mM3nykXu3VO$bW2(J3?pWNe14hfLq?h*FO{jZQ_(pl+s1s9bI zJo>tz-&CTV2*`48cDdqWTgZBpH;!cOz;LM}f~X(XWovOfo;ykoIij%JWm&zb~JPklcqGIEoXg7r`Z zWp8EWr#;kBop;xo;1M6cPZlR0%WUoPq60_HD_96Otml)+01dhHgtl#}9-W}3bJQ0b z(#_H*`(?aRnjj5CztZvVriTq{SUD*Hrpk;PsZBFG(VX0ETgooT8b==6)epA=8#{AQq|llMakYd%LNQ23oB*%Yst>mY!9Rf41i9m|k{s%N6BpD)L z@V{AP$D>9b!t0sbRm^-Lg0+H0>E`7ycg5mPDQ_jE8{obnoHLCXzHUo2JJ{Q$f1dIV zLlIO{$;)Fkfg*KW^dTDC!1=A9l=ZghZv$AAh{4ZgeBv&BB?k>#Tn2^oBJM`s$(~9g*F^C77 zhkwNqSArvqw;Jjd!u(0Avp1;20cSGVox%{v1X`(c(`#{=Dq}iwR#L zB6K%^4E~7;2mELcK>?VAp@gQsfJ_!gRz{`U$yfn3!dnu~2y-MtwKXCrHrWtdw@p`u zkEw-7&1vQi5*f}+B<`?o)F*TdX8eX(&#nZyDAaNrcs4JwFbqP;8M855K}TeWg{J0C zRZ9tHwx?u1NSZp$VVVB8z(P;V%XN0^afkUvsX}Vl{ND^p6&r*ie+0^}Z1XFT>!Yw~ zbC6eSeEbZdlX%0Pm&8c*z{|so#jnc0SMY>%Ud-@MdkWjbk;e_4Zm4=ZB0>bN9UJ_W z$l0LUOu|{Y`^`m%OCF5Yzx^sWpdc{e!0eYghm+u2t|m(x-DV&~LtL_(TqLhy}9k18ap3*gDpV1qbp^_o%#X)~;snBxsCr`4`Mn zGP|s}-;SRKBA6!KRv!86Dz610qB?%6sCOV)(FO&ODnvl|<&vPj7ap*tKml!o0mqRR zE^>oL6%?qzb#z3QsDK(`cBAk;J%ug_pExB3Th`E(%J6yiPS{XS$W^t8wMhO7xF0|6 zcS^4+AVEDMsv6T!FM8Y95FTzMBo6gVkQAy>%^j)r|GTr%Xa%0{67yBgT@*xv6^n*L z4_?bWbf+^0eL%I{&}WMJZ&8r0TagttLt0e#F;{~Qr23-6AHUSz(?0e z$0OcqT}5j%Dt2o{e$n+C{px;TGUrN`Yzj!AL>3Xw-(y3+o}}Mppt9~6bhni;jUB|` z^L@hc?7(Ki4U+kd1M@fJqc=~_OXZ?s;Cdi@UWHE&t$>k+qZ9g6pUZa)@g;>Q4ULFa zN_3i6$(*vETjt&ZVMyN)pH~Okj(L)U7baC0*)d$fV|SU4-99@Md&U5)^UPj(4a(9dpvGAoY4t6#r5fZv4(SmiQvb!F{%Zukuw4FsR>UhXP=`<`7W7eHQkafz{PIUJp&m{DVpT_z(7uioA)Ye5lZWc9W6I#B(%#@E zvw8V#?ZKxIk9UjREDq8+>1atv%`AgN9+~gv)HeJhV6(36qGb3jQi1uj@&T{vyULM~ zW1gx=$mFbkp2vm+GYeJPsHd9)FSHbgJBm_{B53E{0!t5W-1canL!X^WK%@Yq5!yQ$ z3cG6@{m%>u$zAdvW|wK3Vf3()4FsA=ibZIN=?e8XCwsQgBaweiYULcauLSfRWolF^ z&z$Mz^%e$*LbO8o*9?`vS7Cg3lj3%@O6PCsG9K^Qko2n8zg?jZ*YAM4E=bnmx#c~)^HbL-spC> z-A0T+>)(B%@#|8s)w_0>$1X}$R$8VfH)gi~6j>vi>qeegO2!-cUR5&ac7I!9>N$9; zDYqx51Tva??TK%Jq>CuEeVt?bQ-(BJVco!J7c)P-py}6Yp|rzZJ+g@DLXl&r&PJ*; z{V~QXzx}JFhjSS?FjWt&vADgtQyp#VmoS-`fD=NLHDrJm8IIUfY?KX=^{S$6TG>7o zdFLKD^+rFe8L)Rji%IK$ayLA`!>YQ`n}bf&5q?}LRRhSM>ZQIEABKk(Rx2TZaE~H! z!aYx-Au%Nco?LXU4oEzsUZX(?fWx@1Q}>wR2h(xPfV3ZTz{~T;nFjLqSG99L3 zQ^NNP`X#^Do;?oAC!B^Eay;amO%aaIdwY}Z&BgM4c%gqGwEa52% zNu_!&VmT6Oe3v_l8_jDT7i=nl+YC|Vo}9Xe9hSg@mxAwW``6aw)+^v*R>ODrgAsb}5==fQ%La2f?haqgwBPz__~8A^VFy*MpgYSTb@$DN8$ zsi+@Fm2x$Mw0MW$3~@*qTTZN!TgmROl`)Gv9yV!;c@^38h5>%P(uKQZCq_GpEKKG6 z=fS`qY6WW z_pujTTDGVgN@S3SI3d7ip3nJ=c?N(PuS-OSY9dH;s@&@-uuRZu1bA?P{mmQK2v_b> z$vD_(y|1*{?LBhBq_%sTZgDs<%vxNef&;%IX6tY@%PMS54x@r=HOc}<;C+KPirlXp z8Kj0SyKCx$W73tV#z<0}I&iZifgkW}POL6#!k?OFf&mFmYOk4WuJo2auRL+B`kM#X zWp%|dt%r}zaLMc+)#odl?itD0*ImQ4(w6i-Sr@e{B&eI`^1=89tbr^UVK3keci)&<0^+s#Nq>ODf4I+ppOO2%ihvY2>qzIv8j zt+RTLD8i=IS}fv_hM&fNq>&jufv|TcTFbT9{A4Bnl=dV=qv8(-Jepb$-WPc?Y|=>` z8Z567OmfG@iG1hAK&oEQ zdABU$%GwRV_T%-1DFj7Zp6ta!g*XIr@TA z-&9-aou~+o#3NX0To{1~PM4lbta-veqv>I4vvah@@Bsc53h~MGqgot%x*11c7k?;^ zuHLlD&=XI*GI&o)X*b76t5J7o+jL}-c&`*v=(bwSM(ZuV9CJOFdkBuKy0`@Yn{dQ7 z=clx8@LOf^=B1$5NMc1xHN*N#sC0OF<5ND#kI%#p%keK}VD-8raGFt# z*dnpo{xG0t*?Qr{b5R@AM6orVgyb>@&mwb!>zMV{#dt?#T_PYpdt*H4&7Qi3VE6TU z-^zSmRc3r>XSE@2u-p>nr?Ln&2n)x7&MU^ror3e?yLu@Yq?r_4?haE38n@+q9pWf_7 zxx8WD=iZTz;E+N{0yWeu$&qrts17vD_(2hrr}K9+GK6G31ev9|`61N4H^P@P|I&%s z?q$d@CLVeS<92?LVau|tM9D8H_|leiFa8OeV35BTEK7{oFG3k3;ST+GzCjPi25kY7t^b!R3_o&}7hQr1d4aPAYxPSEBD700Zm=#JeWE9p=IGr8LFK>2tS*-fNg{xYU zDiU+^T`;mb>s8pyY|v$v#=pjuRWoXu&v9253Zn|*fGL<}u9!VWl~xk8xix9! z?~*EFFy&eU3YSwu0kv~pKxk+(|GHwqq>bDidD@v-^#!Q5NU|o_u~)oGdbqBeVxHmWqikOeqYyg)1h`dQwtrZ zGD+Rr9VNOEW*51>m%tGj%A$1!%1n5IdGXlvI3nt4&X3*5xbjqJZ{<_L{b~w&Ppe;H zX5tkP(EG28Hx@k^Q#p6awEB(N9;u9>x6fiH)$mB((FTS9EC2`vKu8>t3;0jOvzFh^+lwk5rniu}(+-6M3v3XGi*!qtK zcgsReXK*u15-ewny@Mtc$#KJ$A|HIldP=NZu!D(_e5jh|;0u1c3(MN5mP9$=y-vEs8W(a@yHI_AA(I91nkb?MVhisRn(Zpj)k^pqhd5797Z|<T7WkJcI_54Ls2IJUL7}L(vM9y_y<hFDsm z7Hrxny!s64Ih!zA>liyoYqr&b(8pPq3dC$~_b0r~mqLoF$pHr)88_rjx~YZS9lQRU zB{0g(Vv9Z@C?O*r>-6dqqdisw4+TSiAk?n^#QxGa`S2uM5lBfCSmziAzGaYRqd1$V z)LUIj*DLvzJsEPV-PT=oLEJIA^F{}!@6EkoJ!>`NhvR|{)wnLa#8>*5l}k9&Yw0A_ z#gOYjDDPL!{UZ5c&e=XClq}f%wd9ScKSkv#Cn$0z%*EwDC~p(wx89uEIH}wrXER9} z`C@fSGzT0Tr7L&x!e?{Ywo1s)ir84>UE%c@Mg17Mf=qa_;(Y;VIEi|^%i2Nw%Z-D~ zI~)RfOH$^6Uit}K(f1K>a2}3~x&+Aah``usih*c5oTG3NkI)Op^n$2}DHy9}kEV9a zwEr2!RK8oSNaD{`4_X8k9hRKa2WpG|i&0y$-D^AFT7qK@c_y|LHg}g|HYk(aPcb@h z@J09pmU5~_YRy7veKy7ZP-(+5DF$a%I6ue`JNoS_hA99wixsKX-HSUvSNZ5nwFZ0G zCq43`Z~A8=MRNZZ`o~rjGq*yPq6u#?z*iLN-9FRccOMY8CQJQ9c-Bg$1jm8ydxULf z2{!Rdf<{8fj}5xqU3J{vDizqdchgX%$_T2TtONaJ-=u4uBT+71fIIL{vfM1=(qG*~ zn4BZ4{xFPsu;hoVFb6)8i4{m=tf(@cr$9j-!i_@@U?ea7-_^0!EK!-x5pBA+Cqi^b zrtH?dad085u3e<_YZVvbZf>O1VU z@Yp&o)>=|U&nB-;53yna->PArq|nyg(L-qX49VBV^BkgPxfP$|Kh0HK8U&;Y=c3l_l8m0?5N^oaE+XS(G zAJ=t#yX-d+5zdb8f02W`smnXJIk>@spla97Mcfb6oZRv34;qQ*ez(NgM?)c-0rCCE zcaT_luJ+8X8YJ^5ZJwwkhBN~PtY;OOK0>`Wcs&CM0^na|GzJDl;bUuXCd_Q-0EPd08@u z=E3KtRpswW(b9=c1M8>Oa{?S2cJ-Jm{;Vi@V7Hr{+V;e79Yy$~*!!UgQ-$jGWJ5N- zG9m>KNbz?+rC{ggumko<5`K9cf4>jPq*$5TAuyX^K#RCkgoNQXtODmzhT$rz9Z+k~)iW-VD< zLlLpC*~dvJ?A{IQSR15(a4&?C;Sek-q*?CkTyqGb1rkfvO#H$30vtU?wzHm~kR7To z6(}=Nn%L0WkAbCgn|XY5x9t2Bk>gjt!L^|9ESL!c~yujmuqEjOXy)H@Dhi;0J;fJ%hl%_1YsAnIb9xRYt9ZL&}Zqh2wJZN52Fqh zedb6I#w~y%td;jK03g?&95HeQ6qw?FH425iAD@i&?{dcudcO2Xa==o*CKZ%F$jai4 z0D2Jd3rI_MMT*G%Yp8^2M0J0z|_T0dOc|=2P<@@S?~tU2ujuyf2>;0o9778UFKD_o0i!MWo5hRzvz0av+J78m~s$0l7DBi;$6&l0hi>caYv8}Cy=C6 zO#N{Cc(GACTv*}z^0x+-bX|HjzZoJ87NW@sUBv>uppW+{yUq~B+HlH1X}4*$W3bY@t^6t8mi|C1hovl(V1$b44Dkd!2IdtECE!O&@Wh{+EqBs-|vJ?=^1KAZCa+ z8etD1AynIdg3m{YJj~YZ(iZK2B|Fm0Y^XV7fc5wME}C4?M{44H@WSDc4JlU4ly3I* zSJ^wwn`D*Na3DGlzNVx_5$s=If=IcWJNj)0rQ=gULVnM{CNI*z9#zypq51g|bUil~ z@bH3Hv8Ra}jnOy3k}_~!m!Y}XJ<)C~O<*>hpH&$zq83W+jCOF4t0O!9m%=(jq&i4t zb=iRMw9uNYj_@3nG1H2vFm96EB8UNv=l*}gn5N76&4D5HbN}Og^iag0(o5e%<2@KH2@!Q;88B!BSqZEePB}pK*@SXcJ zno26;%4iwEVzuwPd;}3Gqvs#sxeq+Z81*^zou$Wjek#Y*->2MUI+hQLVbyIIgn*Y} zF9-JOZb)JKcSdaJkt?V$h_^N~3;XLG4MZhZ*gHdW;x7;Dg_IW!#jDY7;Gz$2?1YNf zotQ6+pD_2oCwg>`oCdR`OqDltZ($dQa*vlAI~K#@mWBOC3D<1I)i8^HUPF5GC@x$c zYaDdu+ipBzYA!QQesVN02icy-eOi>R#Quq04jLLw4nJ*Z^}w1nv~q3+{0kvr~XSFM-EtUD6Cl15_` z0^@Qj3GsKg^^dP9Pj|+9n9RaPN$jurjwRkz`ynvRbw*!4N>C{T@%uF>IdUg`E$($l zQzCN@hLyg1df#9K1fqI@Mw*L*yJea`cnnj9tjIM6tFrz!#UI13??}zE z5SvS0B@oWNCltJMpMONv$^VVBXS*93|9vU!=+Wo0$fTSGth4;{p1l8!W(@verGGY9 zd=k*!UQVn087+zh*WDqkWlY-ln7lGtxHw}Er9Ep^L;FTcp4bC#0`R2oa}!sSezh9(jHA% zAz0ZE>BEH42DEa&&mDoL!`5syBC#9wz(P-0*)>Cp{q?3}1peKm^ve?{Bn6~ldUg^R z>A{m4hmkD(LvsIG{SCs}GObSTs^mmz_&h_R2_yPCynmw!&wt%xiPGC-dG;j2Py;4)tN!NC08xZJGI>)-zuzwkd;$8ep2Yup@XLzZ9$}cJtI0dF ziuY7hj%mw8RmpD`Lr^g|V0nO25d8`Juw*cJDc1zY!_fO321tFUe77^);?*-q6${)g zte5tpJ!P$COv}9T4t%=|Az{1>UDP46r@Vdr{Tv#P6TtRu;W{~07pik4l8tNF(7C9i z#K?~g_1iQ-?AytA@`7Bo;ArX3dt_Vr3O#H!Ye<6Lo+FSqCLNakkzevHDD&|zWwWGp4) zVZCHgfX)j$FlWagbW+%Pt?wJa^oRq@KgABnnDD|06U~91L@e1R(EcTB3G{4L)}B5q zalHCbJ_z(J`AI`99`4&B!}ND;NdL;5P@C)@v0_i~pY@pS(&a(lc1m0s?>$%L8d-f- zx47*X*}lv;Q$_ZfbY*I4L@6@ysd=!GLqLhfsS63-5s+3u@eM|0A(~chU~dqVJ5HRx z?)|o0WSrN7q%;Lq2nv(F%A@gvDML{3^wxsy*ppIm+F5ZR<6DQn)&=lur0!a14~;+= zTYI)VtUVp?8m_-49P1j#kZ~pcon@Un^&rskvw<^*S9!vArqv}x0wPFO!9BH#EqN3B zJadp^=sE(?0R38N%9ILcvp%c^}b*3p*$V57il_s5QXTBxJ2k-a~;44ti8_0>* z)UPThz=qg^*u^iH96blSZSlzP9p*=1#v_4b+<^a@E?|8~qS;))f3EjatT-`8IF)xm z=P$f!V9ej4+5q7Sl3(Yk;Ah_u_6Z#{=Ekjf+)i%%8~(*JgCu#94;mp8IZdLs7pJl5 zr7O}a(7QopC%$lup-;%#kuYPH9Cq*@5$+s0%$f3Nj;>=_@#LgBEYW(|ZSQcag3(&r zvD5u6NOgBj^7su&hBbabdH6NNc5f0B2R&DxvJ$wJ|1bkk4`YRMpJfE%DeKQ7*g4PIxXz73tKUE{MqTw><`YYnGmm1DasNcJF*0`3eW1$R zCMsgz{K<_;L5Os^>?{W7G|(R+jZ+|$E?c~ZyrVh!Kax^52;-RqqjE=B`5NY zEChi54^QbMzPwqCj)yDXPli$_=NDSHb*GaBu`#!cPzd4)*#d83fV&+irm7j$WKyl? z7jjb>cor!%2Zlrc^wc)-jjt!UlrlFL5YZnAOH>zp8P9V&+Jj;OJAWCS*?=ZkzT9PI8J1-*-o=*&nA z|F8KVn%AGrA~-2iUmU1Gzo-1YLMGEWg`aj0Q7s_y6ceXO;$}tSaO?B$FGBnobmH9w zzvHNTB7FXN_hdE zl{2*oT!%E7toj0uJ)@{XO%JI4>K6T^3+AZN45f1AzL!m;JfFGrg_sSWQ7gwZN?4<4 zeY*Wy?a($FC7cbDvTfx#cbHlNN$w<731Hm3ycM?uwVL-mvlTq>24nvlYh+ zVA=Y5Kyi`;r_j>NB;CNcMtaCY#Wkt#qwJa)%WgvK=6^W-XTm=ULg^+1q!#pFfffLu z0PE|+2>|_fu$~fad9KJs)yxpnc$8q?bLB+?hVfe#g>QW1bointi5jqQ#7hl|dUUb1 z2fUgwOYSdCU}k&5)Q@?yr3=p%GRmRdEuhx7Qjs!WNI7DF5?-j1jZiycLLcwGJX{Nt znm3Hapv|*M{h9WvQiN+DzSk;kL8?z~D38qo?DY(vKrd-v`&PJ=QnDt&k zlh0&xnUZ?%W2=XUc=S!StD9Wuv+A#NhG&&L%x}PqSAxKmF#l8X6g3chf9hQmR4Xan z0?XJ*MRFyoA-n?1ejp7(k zS{go(1PBBia+k!VhB<;_F2!0YcVF_DO+(9+wC=HL@>fK`p(xXS3ObCv^* zyI2_>+5yQDGS_51YUy(rf8PqUMT~0(&qL_*=;@s3s%||Z+EO(ap|7IlIojs~WY@4x zp;ti@i3}u)PWZhPGly_^9ayW2M#(!MB_r)&%1(o>kOt`DF&;kiFo{wNPQIvTpd-Ni zZk4&4PDm2rjVLm)xhy!Jpz&Iq$QKq7Gxr1AJf)>89zN-+414Un(QBCZ>a{KWO!>&7 z`Xz-p1U$-|wt_0ZIFGxg`)RMIYMCJ8dSpj9hxJLTCBO{9kKDD8y9z!3`=0(Iou2?P z-U%qeN&gQ9<{1IL02Bq>A2$MOuiH>3FpS z6r-l*z0SP~TecAsUZ_#G!q9}6f5uhPE6bJ_BIC6DHps9N`D;%1bhYQ5f3@R5njZWx z_vA$8T;{tDVHsKM>p*>yBSaU3s4e#7y#MiD-IPmbp+}cZ<9m3TOvp#9odM&lM~1Zx z&9WUt1BY5vXyjKbo|G>ji7U>0?#DSmLCRUW0rnwVK{p_GVcKLx9u@Nq{(_n{oeq6g zjOEZ+H{Pk`m>uz5z#R3>=5HZp|1UrCq3bI)MEZ|8 ziHQ_!4(QY^K(&btHy38?I6mQ_kxaIxm?5u1C@@A4V(&o%5pjF?8&>>p^civ2|EMGi zW{O8jm+I-&N<$d-0UKUtDdluqL|KxuU*Mh5>Pl{bo?@QBM(fpe$0t}oMSB~)6TXP8 znOP5ba$20@hGwTbp+Z)(@(ZvcGPY-2`b28XvD$A z90oB@E*U%H{E-+EXqfAb9?I=z~K{vn&6I?hdx<=^ILz@xa!FRG>yfXsd}j_`qsT~oYDqVmy67{b@Cp+6bTM><>XQNUr8J~L%jC7bC z>j1z=U5&KDpT`6Ig$$_l)gw;=i%I$u*BWKu7&qn)#F}7zBWXOdJ<>3M^#CVDQXI7J zWp;uVOQd07xTI;koBhKItJY2Dm$JJ@HiMVP;?hF{vh=7Ss<%7j;hZ6{^~P)Dg-p{6 ztdp_tvjo{Qp8I91$FFq9l1sJWsO`p6X+o}$N8ujX5p`Jf_CVrJfMSjU6aYo~do7!P zomby?zU`Rwy9KGZ?hKlw*vzVzMZjF$(bFz`J{)F?Z_hg<_wn_<{(z@fhLlKAN9Cit zbVhaQBCmhT^^%ES&p3;vS|pG^VP73n0$DQNScvdocLA;8pmpI2^oSeONf3_n!c?B1 z(RGH^=RymoAkISNlrXYBukBz1g=kM@c4ON3qI_Fqau`Sh-sEt<7F0ZWXjKZC!_@LI z_(l6s`^X}tOC(PER?=g4M9sUDjlg|0Zr*WR;%=MFW(Y*ZeNd2oE7!FZ9v6EeGWK?# z#Z38F&4(Zb`!*k(ene#BtCoyz+cMI@{WkIi96##JRBs!nx~j9#G|i#rXjn%)V!Nd8 zK(a<(vCqG9LRcxF485?+bwP;AuI&!E)R)&_}+ovM*h>N7-W781G=lC2S#DR|fW1Oo$uZ3=EgI>3_SYNR!< zPi38?A+t|?rkq}iOj*#spV(49?II#y7pWV+nA;8V-BUF-qgbv{@!Ab$m*t`swyaR< z9530zV6J8Hrka?pUc9q8p>qmJ=kfVGJO@vgy}%c$cwE&j*bJ+l{%`{m+KjnauQpf5 zyihS zFJJbG&{hdLuY+QNG;8LrA}1<0CHPXd&TwT+BHw)vY2(^Uj3Su+SM3;&MIIkCCc$@9 zlnF2Ft9(lP2cN!EBM^Y!Q*1#+q7cjk215mN}i1?$!cWt);sr|bYkmlRtKSj z49gMUAs7C!9QQ4Q2queD?kL6PnjHoGQmCQWlJBc!XEZ!*)K8~(@RY^OnW71q>o5vG zbZnj1BF*q6#+E$hI^e$D=n%^%r7}92u$}?2T)n10Hx(b7L#yt{p#%zRm{b|_sT3fL zW;0z?sFm;?rz9U5Q`Kt}7}x}xPRhWwJqn31CR}-8SV&dA#bwUjb9nIJx6{>`w9Xoe zJd}@-D!Dq+=2s6!tKR3aQHsDGx0O6|Uts)zrA&6}l3iDe+!81pR>?)95m{JFf2S`DwO+0`<~TLxP%7Fub1Z$#GueJ-jAn)hLEg0vAN%EC|GWj!Z6-gpf!F=wSIxjgJxDt&8$ANrAcS+o9aP6cOKr>~w` zDn)D>_bF6u)ehIIN^HDjho~{qFHF)EE+fk*DAw_IPO1tHMk&*JIt4EG#JmJd=}G!u zM}iJO1^6QS0!awW0CpVPH1HquaB5YV5NppJw~rXXZkCbC&uld)xr2<7uF6x;d| zh*?n2WskFw`XlWTL1SRchS0CV7ZHtuGR?NhsLf|kf8K)m6gHf5BVZps|wuyPQ|I zW?Phlkw$NMqOiij(M26+8vlk3wtK!>$Ue?l#3}iXcJ$Wlxl<=O1>`9+ETJh;NzGR_#0a%V zGlagzp2mHhv9DenD3>j8;Ci2`!-_C^0-h7pKHR7wZ)(>fb^EXSlEkeT$|W)=q6}K! zv`6p$0F`Xvh4OZJqy&^G>mM(d8eCayeHc%VJ;*FUbIIU)2O}#nUOXo@JWX%X-Mqfy z=M=erqx1LR+f3P#R_H;kb1-lc|HSp_|W~HEMu)kR!PgGyv zZ0YKmei23_8oQR^5A_73v5iU$vaE==keor{^rIkKIcP&cqs7m2gaa`G0BYd>JqMxy zfDBR_+;`{yRg>krpf}gc_ODo}Wf|f;w>Bh8U*i@DG<$W>An#B(i2_M(!7N1}^fX~L z*s;Z2YQOIA^4+Ynrn_TiNRa;ec33Wk%dPzUZe~qy{S~wjL5^1`8r@JwSA7HhlOrW9 zYthS;pi9d-VMDgqdg4l&3kQLp+d*Rlo&g?;{yBz0A_D$5A}>oL7a0Hz0{{R(Iz2tU z?*YEdrG|(=zCAzMP@tcSz^|=o8C$K#$oOcA4|LoVzgATF=aJ1<`Y$7YX&WBj+m*A? zuXG<1Zq!8-G>iuP^tq~HkT@w1g^e7fM3-O8U&t9HAjQ;-cq&tH_1(g+;@$nf*Cj>b zq6JKB|4v{}CA&4KYjpiky1q>*AjTzPRc`8iTM+x7!l1zwie7PqC0IHqy4^;S)wB(L zMoGt%L+f@Umpyr41rVaX11=scd1pc33~LR|4s(N!<`Ni4x-HpFso;cPjZk$L5iOw5 z`$aKKa7?ywiI4&a0Y0-RMG>{_K%>(ns+$2-uOowf{1Y3aQwxQA73{aQt4!x}PsuR+ zVN^ z4X1N8wr26>gC_P1%KR7+sl$-xgk9J_!0!?v6N6CL2{(2*Q4TB40JdigE9Uu8ovmdP zI=Z)q!*ERmU8ozcOuh^v-e5^^$?-$b+*j6mo{|wHU(=+W&YT?XD@U6X4uCceZc0i( zLIFMMZ@Et8HiyxPmlv*{s3F?4?-2B_HlYv}=zv&dBp7DIp1)(1L1mDa#%G6w)d9d%_O8?kjZpWg5Rtvr^ zajEr6#>zUzDx|uy;s9bs30@-#z@HMUUan47lL}&xFxbMv2c)m>a-Kfccp&-o?=|;A zCy3;_2CovOh62|VE{#@{KV|&EN*9b5lUZoss#Q*DlxaVfJqh|ARFk~pi#I@mLGZJN4kxGbdPSjkkc-ZkKJUGXQ3>gQOA$1I<{|i8Yc~>mNJ$o!o z!Lh~W$Vp%TeY=#b?X2=n)r+t1s@s;xy9Uf54q%+eDjZDjrZjC)t6KAZU(`fP8(|#k?3xeJ1y^@2^qv6|HT@ zc~iI#i_M8COT}qJ*y`f#lv@JEEu-bi_oLG++!UDnTUsnuO?NIFwr-CloB*}}*0`hF zf_Z=k$C#gGm-w3+%6F5A9fE<6(DNw09+9pfP8%?caHol)}ka!YD|G_`^S|hhkLCOlhsf zFX!b-fenJ+gQs%)^YNCyBfj6GFlDq+ zL-7W2$vvtR>bjQrIf+3Ot2^-e*GHrfE}*t}C3)Ctp24rn-h$d!m6S1C2E4%%YZi4% z`x%p-ux!s&?;?@mJLh$aR5odMi5X*j5%RUCYvnX;qJ-!;^+e2@;s}im_+5p&e)aFJ zWECxyPSn?#~2tn&!z9gEiHTY z%l<2A1Wc35IhqWF?vpiI)|u_sKdMqq-bkc~VXhB~Yy2dm7=CMX`g9UzDY}9+2fVZL9)FK5-cz3r6X-I4d2RKp{cdiiZ~OagW5k%mtIv4%7+qh}qusc{dz| zOxpAOTH~;!RHt@k$RpuM1rc4E0*wNNJX~^OPYuGM`-eKGHC=%xtoTD+(Yop*n^!Fy zbsrfO;Vs^X53!Cx%1ph2N;-oTz3ekJ)}|b_tp-UtZ)~J;+VHwGH{3{glA9g9bG|3p z_^K{ECI+NxRIV};4#VfzPAhA03zzG8P?BGz72RddE4HcyKDiv9R}s#3z}A0$9om0} zp}0~6Gf0nH%&az?;~cv>X<&-2f~S5Jnjd+cZ4+IUNO)z?$0Q<8RGAzo|9?ciW00sl z6D>NnZQHhO+uCE>o;|j0+qSvKwr%sw``vTu{(UN4-PK8+N_Vo>+A(G6uEc?C{3vZx zzDCjo7TD7LRqy0Gi8BaaCw?2>{^H}?sDOlA(9sy12-VFaM+k^a$V4Iso74a24ko21 z+X;riOWyhDEK(d=E$>b-UGajA)}s2>j=RAroXflmUHR_DGdWi!!3~*%KCQ6eXp5*v zRNpD+m@~g*zfy(mzr5(~o zfmrCw7F0#Yp3BMGU{kc9NH2t3;PNE zto9-QfQchtBE{?}ev>=4^1e>4i1gfT?39Htp0?5|&2?S{1#7xSP&eQ=dEHS)Uk#eR z2Y&h&`oe$7RDP(3W-zrrxeN@=&;o}oq6vBahJ)&NmN|VWQbg}(<9&V^`17Yu$^S4g z)Dp|DP_`rujnpDxI zq9YYtHH(pEMLjnfb^nff1C{;2BJ-LNR|hy&Ws-ufgs>66C@Vp@;ey8|tCntLz-SHG zH9iGn3Cx#H5wPNoZG&6WO~ki&$=)uYQsx-4HdiRx2{ z@FuPgR(<0cq-q5L7c0lPzle4{eRTCIcgd!3}VqU)M^RpLnS>zP$yUPHE?jx1v_a9Axe;(yBelfRtg1t`i0g3 zPBL596tNmo2Mp#C1fS3F2V?{CA&?r|!Y!lsPM0sQj32_^2ZUlNd|!gT<2F8%xE z)&uo##0c#Lf;M&d9Wsb(T>Q8d>+q&P!mg|}i+FUzvnC3aD^!pYJI0BOp=|PG@;SSbG<$sG zJ|O9PR;l|D2$tTAUeEmy+q$q6BvT!-tvZojhHPJde}n{J9QWJrUf$oQDm>#OV8SnHweoiTlO`IUU$4Q#yjvGCLN9M}0r zQnF^^B$JLTuQFRX6+H+T+4d(pDJ*Ng`~)ic4VYF2RgfOciqO)Jl8?kXtULJT!F8qI zFZ7E4E~b2GPBBUub0BYb@~p=JS5L(QE(l~L#T#3tZRf6A2=mOQ6|=fmmNdf@<(dCz zoc5Dg@Tr?c6&W|W&mA$tda=yXMbtNN@+m`;sS4iWolWL7v1ncpJTY)-Nkm_D_u9?qxjj##+uOSg5{zb41tR zraKk4MDKp43&!YQs~(0-HnytApstSx$rS%1byzYKft(wB4=F7`%E+d!6t()8pv)Q& zAW)*O-T_m1-{h0MBaX$^5T_Ap(}z5TnX#i@P2kIAyDea@#hP#Gal~z-2>e`Q9ag@trwB=dW?A4%gYx8VNjnySr7`iAcKSU}QK}LXK zuqsT`j8M);4dD;x#b}yQ(Xj=P!pNO$T77RKAxlX>^hx zb)d6JWso57fLS5gH1Tt6{@Xk*(D(QJhNfyd@E*nOsVBI-ll5dGe{$50@(QzF8v%*& zdFG`RJ+=(jW+i)ghZY$rIG2>3r(hkB+7{nAEmmW$^+8^UxVG1$cJOC2Er!p-s{3BJ zTeVcJ5FDiNvOiW-Tvmu2?;=~sT@ZSh2eVy_q_V|p+7TR)#u|I=ksI$7_D1FKib~Bs z#T*xu{8}7XFp3MPsTv%^pT>8GBpi^vauBb-O4vpTP6rxsW5R$O6m(QfPGLh!>;wCD zagf6TCf4I&#^1(|enBqSvs%)rMEqt^L?_o%hys zH>qhlwZv!OsnM?FSK*ANU~^1Ua5LkGUw|U#e#mHpyenf*QC5$&#@iXblqlBDuGp|j zYGmdAs2+Zdh3*Tg(5pUm^M7f6wyS`KRaDafqzMu3pp5;O zr$(8u{x)gVZRt!viZ1^1m$m1VG0vz{XZ1#o`FLDsiUfjNzYl{f){OkC{)5qoPMd_H?Y-!GXf z%0gx!=71>sK{QKOy7;8e?&s!tn7XVmP|OJni-7=M*c|kXVs9Bgm$(u4^6N}8U-&0M z)z6tu_jQi2d3j9W zOM9WzAa8$~s>9|Ucy5b!6EkA9!K_2K_wLezh z>=?7-gBn<`lr_SAE+{?Fy63OE03oyq;b1srUse1HQ|?&Dh;bjicpx@-P6wrDY`?S_ zBu%}xdh*~H7iIRPhd;}i63U={b)F3V*@Ko5KxY?<+aN22yK#|>wsm`) z@LPrlfL5l)f03=2LPouQQT1s}upnDKlIosHnzQ=GR3}$+|#7zA+8JVTzV99 zTzyzp%Jrev{tD4Q3$E0|n}FilBs3gV+@zj9YIy~)Ju7`t1Udmfd2!{95~@0vHZpn~ zriP}QIV`nqqsu4rWdZ2^gDoz~&jB_^gKIeIz~^_=5#jdV=8eYdOH+x#WqhYPN|?*_ z?^K~iDeS0p@5}>w@1jsor{E9Wt0=C0b?(PAR&|C)V}x24X~tz%V385lcDc})_7dI! z+!^SY;EhQ0R;+KEDMMoJzQ3%Z);1DI-(aqI^*+~i^55Rns+0*>9CBr zc=%CZ+6YN_8jDu)y@0*F;)3UZ;Og|*1HfkK`giV%WFoV_pxqgyta>h4v_oOMZE>En z9-ZIa*PgicXp@y*ke<5>hpJAlIx@DUA&r!dMKV**p zMzuz_C;t4F&<8Th4@(Z=44!&$nR@?NOw`yCG~}gySpOm)_g~VMd(*B=bnlbeXL2sk zya1n^50ZEmoI@l&yU=FFc&X#0rb)tb)N5Pus#T*KL)&R^;$1^jAz_x=wsImlUMfFd56b1!4w=o5;>pQljHxG@^rNiz-P1137 z+lhOeU~Fd#|Lhz|`-Y^dG=!~WROm0lK--f(;}dw}J(h{M1n6}9TrrFji!iRxU#>l< z^ev}K5Xv~6oDFL?XZT407M}VDco*%N5h4%yU zdmh0d){9dBT;m<&UqIYgMSGXS%X-Q&g4W&Vaq0 zubU_Ysm`ybTK2vde6aFxHPmlQR(qT(46_5Mt5xRJ4N6C5g~1nb4(hHN`y@g>i|mCq zPHlH06!Vt!4-es20f7X#Oe1VicKr|b@=9*3vbHy?FvK?@gk^sQWRQ97Y(ym7`OH8C{ zbfo`%t7)sv0e!g4+@%QtK@BlDUlAk;q(j=5bP|`D>8}!~Q{IiAe3BA2 zA6OFr=LKLVvs{=hU1{Fy&Q|3XwQ1Mxz4i=#v2Gtnkb5wk?uMVYktkK)(zrVI{ zTqo9Xp$+IwyYqJl3!$kAR=VmjtYSJDrj9LB`52d*(M3!2AYazu=$PC5*|jSUAnme0 zfoKS;hGA{~%;9aHO8Zj1=iiojB!p>D?02`W=&qLW_o&IEzDl`?dyGCd`+K~LA(6$` z>6VL!1@nNKXvN&FdO(zveoe%}ji(7sMb#@cq$XH~a^244Esy7snc(1%p8%FeJW(x zaDg_Gz?@Bzw|~RexgBj9Zc$A*eo0)j;rMMYc&WcVBM;8sJj}in<w|Y2HTX5Cm^IR45E@1DWvzGTeJ-GVf~PO4(LOyWOa{W~KpW z*@5}!t9s3MiSE5c3Syw!7T!4@+nNTbFP{?qmHjg(8;b4-i_tq=kdzBeaHuCOM!fI= znA<0!*Ak1KE*hSf8^hek3b26UF@i5d!nW5TEW{eq);HNdTD3N#T9;9 zQ!E^*XvCj!2{Rg~H&EA!G@N?7CzJv4W^qj@5I43WnTKcvj@T5|TW0wvbMd;aWrw~j z1{r@he|t3<%N#6#1(B^ruYO&i<*S6BdUHUoOFV$qi)lf!l^vVO2lKXnK0Tktvu)Qj zoMcEOdT!yiHTRQa!)a^ge2%40(F_dvje{Gb9d)9(Q96Evd-9m5+E}sbCvh|Mw7C2E z3rBiKY+0Qgt(_0l9#ex?D>hM!6Z5PVnqq6CngjU!QOgkC>TL&)aED!z^Iv zr3yo@63F*B4Kw_58AVu!6FjT?Wb$nJhm}W?(1K(>>!Pn!X-0FI56wvLZTl~9h)68} zt?&3Z8?&SSyMnza0B?cKr{x>AnLL^?5hmLqJlfta6BtpINY*5xgXhZAI8`)9`B1htOKwiPZ6 z<$yK7E|He{z*0b&$2_IXR z=M;Z!F{i zClpUeICIrSNNL?k^wiASm+Y_-^fUB;BkdeG>nXANBnBPRu=oB|U$p7a-CQ!V9qPzZ z;&YS@r66I-0&lHL%WPqE)=c5^?jkl3(@DPW38Z!r8kNJB&0A2akQ+G(2v8-S72698 zTX8~*CmSF_v~9`C1}CEy^(?#YqejofI!tpzcnRY6Mk(;WNs(_9sK0XY8WX@|!Nyb> z?v&bnIEAKC2E{(NE#WgbTg4b+{&hI8fuGVkz-ntY?H?v@uk_0k1*%lkB+>XJGraRC zmtv)0^yn<$fTxkycg7FGNrbLcy+=$~h`iH>t)np)#Y*h5Ump*|h)4TJV%SHiy7{#0 zzA*&q1GqO__OTjNZ9iZJv|qNzH@x$MZf!RJKgNIGdx768Q~{pNLqs)uu|ml1R~rI6 zx}3HCDg`k16`?jjmwz~Clgma{4eGYqDjTez(k&b$(=^!-%Q?a*XQjdbW1+1ye?j4^ z{B+_=ef7{*aS}Q#ED)r5d4%2Dq2DJVzb7MBaRz<69&5tm{rQTT6ld<*CMTwwa#)Ga zm>Zy3Vu*g7r&%Lgw!BF1SsMl`H6s}+G_G%K*rUW2+x053PwmY_twO2R{Z6)PHJ$5O zzED{N?dgJiyMV(=>MqwqXJW#FjGY3ow!oWx2T4!skU`v;nXV*vA02Gx)Of0>Px~y= z-0zK%`)P?eIt=I*x7%mIj-)Gf2hh|Er*qMq0glZm$_!@a3bV(u|D=2CL?_eo#{6~{ z|DTv|5iUiQXK;$B-JXE-9{(v=RTd*Clob6hu8>RD8Ch&HoGWxr-1o5 zJ|A$}`uryqg`O(y{sRs_?E2u+Y3F4DOte4FhR={liH;CHpbe2!=N` zIyTuDy&N@~@Nb_q_@{*b^Ogfd7k&tTm41;gR`LVZ?pV|G=UQsB4dK8o3jOs&eFgMm z#Zs?0Hnx;P@v$PSZf^P9ZIUA0SD+TB33C|v@$mC6MgcNi4ZpH7x&6(3?_azR=5;in zQ#cs4_nlga1BL9Cr5KrOCoOuEJ!^+uPMXUl|VS3;9PKGVzHj;KG3J5hLTky$1i{ zdDGwlvWEe&1#`j~2yq3g*iVg?W=`U~*fKe>2Qh3YZY8mta=epLC~;7nxoH;52P!+( zTy>t>IQXpTLQicMZ8Z*ST#iGorBFVjys@=-SS?d!XmFNh5H`V6Ir(DK-xifTJz2Kw zDvH4N8v)S6A2i}gNv=0oz`BA6c91Mf{USHtU9W!nP=3Nan4^hn!RTdBg1Hy4<l z>E9Xo+kW;RgIMNTK-X6!-MYmX#`wEH2v&B>d(MD8?@bKTpv^6`%}XxqT9cnyii<$e zrb%>&)ojpnfe?neNP~7%a$oG0?pgRfd}>YluQ~?M;@o3QfW#w{MrQ8NOtwM|*pcVc z0ayW1l=~imzm%QeXVPI*4`4)&sIJ98&iQVHS`R)2e-63_CfFoM}Ihvyjgbww!iA+6Z&`xi|e ziE17@$yI4?ib4N$X|7=1trssDSU>IZ{kpF0p&rWh-D>Z369+w}*S1ZqEj$t%9genN zX(r-`bh2IQ6p6!25=vwne?x+mp%PvqC^QcOPC3amq%?t)PkcM3p$sewPfQSRF73E0 ztYsF2Pa{Z=D-|O*L>7{ckD1VDn0;FNZ?R?Ulq7$p$X!BVDPeVWyhE$Tr3wtl7HZn? zqfhWULEP264tN<4!!UifV(}wjqbh|&mc;@0Ifn(5Xy0%ORDZ%#2Xy-ZW#+i*I;)e_ z$p=83$T^E0^Lr?{w4q3CJ2_Ms0*Y=R#Ut!N)hda}K=>xl;Ebo;JFfYEA1Gsiea(cF zk0>gHy0))CpRps!c-8Er6?q~+Al9SJ@oWrG$O6=syfXne=| zTc+XLPM!adcfu|a3AdHRIh_!*iL{#E82){Qb7sd@6Iii**CMv>jUNT?q%)>tL5%k{UX+Ej2Z~%oPJs#&UV5}YxR7Iy*nnD7VIcw zu7*$m>Q2l}1wuiqnLySOGF6CQ1cOF&8qYJ+D`JLvbh)cAbN=^{ct&K;xF)-_PlINC1+MjHi_2c&xvQ6mHK z2Gp4*5!7SWry!G5w#^S0A~l?Ima~8UnDxrAKn#Vh^__7cl2IRcslH+GP1SiXB<^+^ zF_u#eG#%G9JAiwx8Xev0PLCbWzI-Km(D5yIFxF?2R_R%^^zAg5A}(~!q!B5F?0PqC zd4w=+ic{rYJWKGU|5ml{cx_3^9MF~Gn8EX@v@$C{B69J`)hA**3gM7Da*>2$^-IK- z72(Fk5y-y|m)u?WjFHro$zP!4a(YuvAbh%}9eJFuv{TSnYNN>wAVD7HL&1`Gn^o1H z`N5Jc8{8pabs)E#tUHJP<+-5+P=lA_7(0iWT*mVc% z8yd>)Q4Kx`1AC@RlvFk-h6Pc)`MLE0^$fi-w3bqUvG zi%*S%eI8L4Q=Cr=*6B>wydr(;4!6;yg=p40TgULggqSXPs70vqRj1?#dzjSYfW(_D zI96O^4l2CIXaSBssgG}CO1LNxZ3~AL4;NPlUdRHVBmHVA-SX%p$yu%wJ%`w@c6tbX zIm!z2*({z@rtA$zLd2p=nP%j=4JYkgyZyPIXYDUY9=WeFrfLWH>}{2Pq6^^8Ef~4s zlt>6PUi&U0h>?3`;l8WgPwcpIQ%%v469@s<CrTRWUcWx$q(D)zmeskZBa!2xdz{0cS6hi7oNT&k4tDTXI0%j;|3(}}Q(zoQ}`F+9>auBnT zU<&XnYJU@nvtl;JmMCVySM=bOI7eS4d0Go1y&*d7QZnKF;Fq~&InWK`jvAMnUPF`y zgy;d%0rDbq!JQJpa8LSgbTiw;bzpu! zTH#I@=4eGyAN9@cp1`L*&W=6cLu-Wo52s{<}DW4=gg7=Yvz4jqB$5w&IbLd2tkzb zkvB26^Zi79)=xed2j)1LLq!%d3-zQ#_#fn7G7}0<{+$syPF(2LpM7nk_90G!u!BamSMcTDi>dcR`!Z71yQL@^IT8vlGsr8HBoPcj@*!(3xeR z9!>0^ZC)N89+wTLGmc7xap_8B5N|A&unDe@?UEW5vfS5{Eais1hnwA_J@c*_x

    A z?DqJ!#ef!9%N6deh?p9CY&{+I;> z7p_M*g$N>PwVSE0MvPb(+TYXC?6cLt(8jXv7OUM;Li0LL)T^lG>*%Pnn z1@-Nfemr#TG!(B<@Dgjp@McbMaf&NTQpKE*Xh&gzYt`EyZFfj zr~V5vso95`@X1QFN$*x_j(h#9vEx)CMaTQq6M4P+?pLR8ol^DltKpO0IfIOCP_Ml< zI61HtjnzPIeot+)vqfBdwL&Gpz}SP7v}zF737~@)1MTP=*|=*I$ht~7puOeOk-{}o z&);Tx5^4E+LYVy1=$#apsj98g7~jI-7eZ-dI!4I#?{y<%H?~!v3fNN=a!q#bp_Bp~NTFHf)-%~eC>46+$rrG%Ff@)~ zToIWCj|)W;$1V-K!`Sbs=}RB_UNAYw+vhSe@yBiX$oFM$8%K`tkaCQc;FS6@jT;m-h4i@S>9j)6QopTHv6KljL3J3+IHNH< zYkK^SA)1mCsUe-yXN}sSiNg`^cxCNm`a7hR_J2=of2WNDngS?G#+@IcfbU!tzdb}f zXG9DgEcK*ZX3~>n%NApwU9EJ&DzyGaQdS4gaOkgl&XSYP;_c9Ei^3d{1s8YKBvPfyl%wD^8=88{cHZL) z@Q^1!w`gjK*_mGSP~56G>j*hf&9)4gCZaDSNord}wyU^AX(5T`F!bDH@KE%d`U#`T zF64Pj3_^)8-$B+bR(&g!)2z^nMJw|m>qhi7^ufZN6~wJdaa&wb_v}Tzv&Hd zH0F87X&kxhAZiplpqWp>h7YL_{d6N8NCQVGRE}ll%ZGa1;ufxpeZ>oI$BmUNXBt^C z4Z2W{wpyVLKr1MzY29TneVX8-zr|g2Ib}hXzY3f)Ks)2i`#EXiEo<)q+w-!50YlU* z2nn#=msJpYizu50y!%}CpLmY+OkB!xveAEIxVQnY6RcUYn7*i2+)c_9RHu!aU$K9j zTk#Xct697$=`hv{*)PF4C;qGLv_y97~-(>`#Z4(WFt zRUrf;Ay3p&h^hEnF!i2p`i#!XF583OAHf3{5NtPEn0eRMi+~*#T~^uP>+o=%xtE^C z&}Hjet34$9SQF&&k%z^r%;r0irAKq)5g25_==XILEKbwh`rY`PQ<()yZ3G@l*k^8wu+)mO5u(F=*q_sO+PQ+1$|b zw|uVbRkj=Q=0SdKJM$iWNO9uDV{$2?@zPm;bzcIpEG$K`!%m!umV^87j0l2CY?|Ueu896 zjRCvlqU#zXDqett6RUeATYi<3G!zytKD_@YYV8{leyxAFC?zHq_#JpwT{Flc3_4L$ zoAG0MH)@0O(f_>}|6)v0a0{cj(ME-(HQA zV1u&)mmVo56jYRXEd4yk7Q#zW$?7#9K%FC5>FP&@#@pN8eXULZ%a(a*tR`1`m^DTm z*Yjj0Fj7^^X(N%4W(McwZFKy&mhxh^bkpf*P@9YiM0qn8z_6u;GOJ2gvR$ufS>ioN zw$BhkX{o1PZ`Qz8d+o`KLHZo;(we+K5oC`VA@yt*vr`5rlZ8+hM0`)FN?wb`w0KY6 z2m0su%{ZUdE40r7&ALI{uY@sFqa{p#CH0cR8|)C1%q2xGD8CJ!BARK=*Dca|Y|=E3 zjP$1=+RUE_&Axa+_Yi_Xn~Kt&%=I+SjR5L^qyd47?(rw$mAoWVw+yJu}m zy76O}(Q03USpo(S6}7ST4b^jMxk?eamowcN8L_(>l%zXbnQWm$QE?&tyt|M^X4}*6 z*bI!E&5UvEwY%l|x#bCAgi@bZ zooY$(539Xh^FX%zUl2FSPN=qIz~FW9b}(p*yYR`Xd-SRCe!@L$Emu}P3!G8LYl6pr zMAZ?bR3@gX3{f~cago&^?EKR+Qk5L+s!L?&5)CHYLYCx~YY%Qv0ZR{}zf@-@q;;|& zbRI_x!v*=?bc=6B_=4pjraSW;l%>9r^hJDk^T`8MPYjZ3Oe1l2Ep{e)47}?rZ;f zF*sUNn_tO?|7Aq&FRWU$cMT{Ti8c_l=Mb}}Sew8`jYbQ73v|LLfjc1u=(G#26^l$1 z4;c3+u*?`)$*al_hg6u~?gIg<83V5q*qFuULWwWF(^AifstUK#DaH<>9>yu=}X_OL>WdU&}H~9V6yPXBp*;{*TcwJ8_S)nq3@|c<`L7_n1nc%(<415 zAt~r1jsw!Xm+-DhbVy*bp*_pIs13b2(~R_WMQy7)l4`jhR+;sBGZ$Ne=lYU5b@(=3 zl$AX|;dQ7{d)r#;3QP(VK)V5^qJ={U000I+{sc(@zretban_$@$#d0xu-8@<2S*ol z9anO_e(p13n2_*dgAMf*&bYGU$~HOn`WZ@qOB%pBLFPtuk&chA98>ID{JLw`@s+l}wz0Xi9_M;7zsDZ&;ZnF??#}qx1{pk0V!B%@h}1afFuBfTUda10LUxpYU_UDq#U7|3AmbZ){gkd zS$jLC2r-kz%7^)5eS!as|D&|b2Bd%h0z#|r1^Z1r1OS2r9>80*{2UgWt7N8i(T51v zr~(zk4ivKTMv-viop-if)Gp-&iS(fYN-=d61(8wcep65=}O{24zEPHr8=!2%8xR9Gsc2*>iYiXy%H#%3T zOe)0C2Lj@mzig=a&|vg+u|mnkxYkU}6ksC1;r%c35)c3!0B`mY@k?P$?{0p!Pz{M+ zK*n(hR3~(gL}b2%MLRNhKt8$tOdJeQf*`cU|2Mf%0xSS;KSgA`@J870K*)DnwlRJ;J--+g1`cJ8wa#DHjTpiw`?@N0%>f| zV-XHBw6;UaVa5AzLQbpORW2$tIWOBjp#U1)`O7s=MA~h&A&r>1z0ne>WWxXND8)`YY=2 zOPcs~1SQgIo{O$!?ixPo`g_vz#I!8ty;Yz7>kil=VOc5m`JLu`v7b)T^pYkS<~1ew z)$#QR5WM4W2n`1#5!;QDPH6v(+<@T<%Us89#I`dL#}vveX=m?;*TKD0tQ(zryRY|t zgU(CaoKpG9(RHH1raT6o|nS%(?dyd4J%T<>}$XRDC@Chgs} zxq_M6`F3!rYr(lP9=|myE(0#}yl8%%8e-o7@ha4|NzSUTPRKq7Gl3d5xjP8#WEx>Q zT~o%I&A_oqCxB3%8eOf^#XFGgP92Fr6>=RiF{w-%GCehXmK>`C3H2Q;b`OYa)%TtVu)17vtew>@t`#&G7f2pHwJ!hx4T7KssywnwSEi=oxww*O}0Wn&iEMs zg)d(*HYbf_5RU9X{DZXg#8d#I^q=!K_2s{YIu#__|=Mm8joxkEa81U zj)ZNeC=|-*?tqe=-vjl!KG!uyKTqntGcZ53`MUo%_qRPtkSfhu?%!YLbVmHmd4#3s)?ZE0NBglBUVD- z>$t#J)4b0aXa)Wy4J;-MCSiL9(hD#(5|CP6m3;2^1YW31i5($VB>zDNk-pUeGPyJC2$ z6HGbw# z6{bW9&hIw7tNsv+m66y|k|vfkn2zk9Y%-XEZwIC@#L>hc_L9ZgAIogh9i}1&`qtox zRid!u^YCj30a;p2Xg%%5+W+33(U>d?N|k|CAgcq~a7{SWYVi(a`G^*~;gOU*8v<8$ zORIT6Cvol($-`n25ewzH+X++dvXJ|1%VI|54G4uW;T$yE?PCiCM@mP}J6hyl=90D= zftF`8se;q9{P;Plt5??>(pWq05RfZ8uggL%w9W}9wh&N7dHI7Cl{sJ49%}t)tlUQc zW?=8`^^%x)>`TeUA2F6l{!~9;D5o)<;^l_}B75xWffyC6;r2nv6SUx*Tq<}3G;=B; zy}WcAJam`942%Z;S?McWQ!bL4n9%s;noNLPmtM32-`@nu@^1Ku*hGH&2m@UxP{>j{ zS68KEw(|sl%h1A#%WAP1BR*4HY_@1Ei$=`)cdJZ{HBt1)-X z7W_-v&v1+}GjFE0ULMvH6_+|rd9au-dtk5Vqk|EsXIg70PD;#@okdT@Xj*IvcfPZ- zFb3oK!W6ngo*jVs&xgj6^W24GscjpZg`mwqhf^qZu(-+PbOWrI5@7!^EUe63>aE=0 zCTI9ZxY;}Zpyn5ZimyW2yRP+w8)*%4mhWR+zgU#!PF)yVuR9n>zD^g9$TTFy>XGdN z=Wh;}>C!E^axk#9F13X>O2Ed&DF1-h3R9^w&ceKA^LF7x zeN-*FRLz=VAHP`SL`U%N75#}Pz*3xWRH+?09g6OR zAQoCwFCZ+LO}d&h;`QlvciZ^APauDN0UV!(FHQ9N9X$Xew zq1A-SpKZC!+(WKP z5-a|K1Lt2#?RQ-M`IU5x-nmvFso6Fi3tWE=s7-CQ;OfgxFo(0neYIZv>R-JC9D5@@ znKogOlNU+~eZ{Q;pzkQY9vdd!jQ3G}PT{HlPlN3%#QiSog+hT7$jPZTvw`uSYZ~Tg z{wA#E_7Q)u@#6a2kn3(#xQz> z;;LNCXK!IQa<>3o*j_K?cn4$kG@Xkz(g_a4K6xW|20OL>2T|U>s&TD0&N>wF#aDlh zD)P@^CAHP3U2;wkM+fcnbsIGGbHr{iuyT`>mKhkmaR=bGP0#^;1Z4pXQwWBs#9&6Fy zyuKo5#O|I-=~)R7`DF`5-;_(#KT*=HScX&*&w@n)ppbh`(Iu%iA6P{O}oUbjsTU zzRv}!nLy*js#O~~sLh(qLN;Ay=CL3|gUcQ*)Cc7ZD4 zCUw%Szs0N^DFAW7-(BXM5Hyr~XGNSkDxGXu^o9f*oG!d~;p^hH8QWbnmbZt$v;$E1 zr3+kEF~rf;_cE;|Q49wGBB+z-!cF>sle2%Sfa8y=Mk&gMkxxd-0p2a@CU>_1v^^w%Rdqvpz7ZCyCgCyD-)%jHa37)jfDsv7!uDme5CSJ5_$n zv=G2gUrK|k8w_~&ANe>n9WyCO7p(*q%6bo+>2pKsQIWLMp)ZgBkVLHSs|(?=*F?Qd zF14sTf5&GkK_*0BJEst7)WEfFK4g%Ym6kN&6+y2lzhl)F*2gk|a@%mOUqjN<4ABUJ z0jeIot%v33+YcM}u=kHF*bjB2PQ%ytks2+@<=g12M8*!dudv>7ti>Q7%UNUw@#Vb@ zLOt2kQ7Qt7;Dk$$Y{+LdIYz`NNj6VF>bHr$b^tg!6>;a2gphO%*;UEO0) zzS!)Da)xwSABkaU6}_twf~*hPH|u_6)XIIW1cL3p9(V8Al$=ZS4$-PtxiV0$il;1L%6)6oIUn ziu7SkkdpwBp$0)8I0MlgL(ac3{>ZTd{XQpqV1@Jej@w26y4^(8y&6ou$Q}>v_bTAF zY^ao|XLM5}@}?a%jGld*_{9>}*V1B-?6#1Ml2K>x4!%Sy*D_FeXw4EC>WOwf|L!iQ)ja0AJgzAxnqNG~aOtIc4l4JZEg= z!SnyGSwO{rCjvHt_P^1;PHulaXc~FnP#p>-#Z@c)o$Gv_bQ~p!*?ar)|IeiCx73US zp+Ervg+}KG@B3eWf#A2lkhs09n#>yI!^st;y)SFkpbq3E7M8}Behf?BtFv3v=9YKKtA(O(Ds1&BeVGV4v&6d5uiHX` zXp`sz%I&|)JYMsG+%1l>G5M=!+xKLl9JEI67_#Qa+Fij^LDl;S9qJMvopH-9vY=Yr z?(l{N#4O|sVqb65jbfHL*Uf(rt(HVGN$$f7D^~>j?VUiHu})vS7G?R=l|c>gRTj?C zdY&Iy_R*+gbQ*k@x-=m+n(6AZQC|q=HBnV-@R6^b$a8RuDz?OyXF*iaT`6- zVR1J-aEUAjiHn7OZfioqs+>~R2eEIlI`GsY#J1N^o14B=V+=JC@^e#y+B?d>P+sA3 zHHIM7=PHWU1CS6;$sNXZnJA5*qPVroGDx?4Alr5oUpSp$u^VG8Fk+w)+_rK3PIYQL z^oF?2>%$t`3N_GGX8>jgi1~Z|w>hLCd{OKjR6+$Sq`4j{bH|ke6LB2L>*aLk{YS{w z($-C`PWo^FCv4Q!E!1Hjf4)$_iF{K&GkiMQu+ZDi3V8Ad;^54ZWb#?kdYm@ilzYJA z7}zx^^ue7P)GGYk1-g@XP-w=2uF=C3WCbPw|haC~zS1^CwS4 zK7p|Bz==qYO!mU2sC6S|q@^e34@0N@ZyxIUe8V5mQFqJK;||;wd;Kt-UXx8FLA_Ge zv5mTd4(Na6|A(4z(31-QifO&5zcDv{ARvCg3skD#iCX%Swz{aE@qUAQK|D)+okiwa zJu!g|Cf!U;k+iuTNya&L^S^KS=M$0zN`Wr;AB8Y}fPx^hAED$wg|IIc?=^n%*mMXO zGC#^W;6V+~XLouaOfWsdG+u^+DF%U3Nqi)nX!4=*#r4%JRQzJjT1+(~hNk!HUkq1t zNT!q5h>n_<31sa-%&=?~JEWc6(YgA;Kob{x{{#2L*=u&xR(T+|CMA!zpXO!7$ za=X&H@vEh^*wzo)FDu*jyIQC2LVUnn(Qv^{oFOASL00y&jn=;nR0{nT}iWl?OPLG%BG+%kq$EnM(}RL&#nUg8n3!aHzBM!x#rl|MNdG zY2P{a%?rcq27Jqvz3&fD&ApwoW|fgTng32$y*~d-Z)k5OZ%CfCCnkjGqVgz_{aUTZ z81mbiwP}T9Kvz`?3$(}Gl*DlaPKU2mM5@(Ga5-Xft!z7gRxG7!k!Lpt12J;;Et!i& zYyTCwP2)V~(0e?`2r9VudyO5&oU;diy|9qpuo3t=-=>a%Ygv&}dT|LuvUWNgkF0UM zy?^nYdD8+l-0MY)y1u09m5t-HY;Ig1VJpC*BD=l!%IZYMyZh44nRzc>K=2PusiwS3Ljl(JC-{+W$(q`4Eu+$RP;a4i@!?$-y5W_lu}3vybr zCa|`V^w=8al-#D+B_E$(`g4Qm1@Fnz5BvEwXiMz4)^ZCBHaPg! z5s4tJd(m7=pKw*&fiee^F{*-oaqIck4Rcc`<(T5-Q( z01=82FgV-hxk!e)!xII|!h~RXLpT!PUVadtCefFe<@@(1c|*rk-C_8va)K}wqA%$w z+#{t7S73)*0R{iL60cG)7MW{niPEUtL@f3$`#Wxb>LPes_H{~lTma6od532!c>hyH zhHvC+!i_1x0L0^#Gu3ig2jK@PQbctUGxrS%Q=G5xm9Y~GW-WU02}p=2whTuXQxiww z^L_|JWbS4?NgtHbAUb-)RCzCwx2>GqVl91>`*v#%?TPdcNOfea2i_pm1$Ihhr;3wi zs$x@HUWh!!xxAHD!A!NImD-@14GeqklQkn+A>Dw z@5?>(RqfVCFz}-DW_je}e9RNTw%_{fj2QIv)9Bz)Vv+m+LVl=kxk|uFrUJT0#`j0n zNQ9*NFyJCxVBgo+H-`T2{kvhc(Y#}9(MXAYA5_24ic93&J6jFbz!^&ul;fa;BY8f! zEd+K3GgXDRa>+CRf`>zGet8;D$YHuq>*-IpmK_66DLG+rTbs- z_2#Nh=@Hr;ZNQRwni$;3Dr2pNiEBJ=c+lKIuEISNIhG5BBA02}tu$aDIx>58GveXa zy7!=QYe%6Q4MyLKQdTlWuTI@bPpHAfzRotZU`EZp3e}n2g%8KMYey@3z1>Xf+$ zUt1=V&)FKz!2xubct&2-L-NazP1p;fu532Tmh6+ryH_rUY-7{Olk3wcdI=a#)xFQ{ z74XwBIw(=8*HQS7<)i%oeS6=6VgNH|6IJjQ{2f<_Pho=acPT zu*pypvX@+c)pIsr?PMx=gO80+)UoB_%I^_E2oC>I({M?>dt!y;hSClOUc!v*0#hCJ zxfp>*7?~egl5Ed5Q|^#4jj4$-C>qs6fzcFO}YI&~2M! zO9yQ^MF}mNCm1jP=U3+|3oF=Xop8)e&{!seBZA-z+2?BUf5aJHE z6+eg&8@-hfY9?((01tZU&opCAMo~6;o7v2s$u3bzpQ2s88DqZFgld3LSj_}0sHV_px>QCx}@k)ZS?|+Ai z5ju_!kiHOJd(7|J53zw?92ipQvy(F7pnt=%8EF+NWgbF+R)XZ~`# zfg0GL<|ZpHv5DE{n1BtYcRsE3t2gvWM12FF&avbE%@3}Bi;!{qK1(+`Vg)-QYwYSM zYMh~HW5gfwC_ZWJi)`ZGO0g>=(SlmhBGT&Qdgm(tR0f1mV*kY7n*6F)iBLVS-8-J2 zu_WEgP(Yg=Rd*S;cgfGYP|qaO_e^i!JZD(rVu8FVr!);hi2Uoy;Oci{=|VW}-a`tp#ZJcMYS1_AU|T*o`#_&wni5cq=jHf#fFUbdAB4o6h#1W*5N#nxbE;G z4N=PmkN0cA>NF4&YJ8cOje|BD>Ccq-+fNE7>sKecI+(I3WecaCymXaicK_DQdGlL1 zLEq}YYzPE-t?_{dUo8#iZeb30?)-;C^aUd)QBtO4x_wgx4PTCyxgs1X$8dq)Bk2>iLfY2b<(@)acq6&= z6A{$x@I#^5$6Rhjvrp%VnZi=+gU)SXv)CpsqFCqP^|uafnssX-I_7Y$s^+u-nJ>tF zC|;2em+&CV5W?lAB`WdR>PVdkTM_o5UIMwRgOa5fRs-SqBfv}!Y9Q@{H;h6ZmI-=I zR5p^4$eWBw5or!b_FW&Spw_1zqz^OI7%lO{yZQG|PQf>zwv;m2*6^W^JV`0RZpmM6f&@` zE>iBbKU+-ljd0i1IE-5V8Dd| zED`QCm`Tcw;W*^osD}+wnn_g4Nm{Na{rZ^4;spaW;i{(lcnLl;VCxF z<*c>R9h#0!M(_vl=I3c{j&5sCYr#)HbsmaKxP+2PSD|*XXSZqLCv#k|lD%Nrg>gHJ z>|cLJbAJ=%T-?*E{BD4NO@0tK8q4@RHX<3Dxj%8dNn#_>QluqC8kV}ZLo{7_%TSZj z7-g>r`U(*m7)^YN9s<~CT}EoC*aKBN zJ#n2$*hIT&=)_7+$Ao`O&*o4ziW~)WjzH0FB&b+mv_C%j!*8O0THq(-#G5{}9MbW) zcpiD8Kb~`E*>8I{obib3miIT>a;mR??Zd}qFj($>?tFCy3qbml?$t0Bf>YoEq`qG) z-zJx~0w}jSd zY{??X&YDB4p@jZ+l%({?!tZ;7U+qDjRS*jqeyYrLNvyau?Yc*^?PrZU_ysli>ErK( z74o<&GLxK^|FqPwCu&&)UEC|Q<Ysd{$V1Tva>uBTyPck9*t+er^VMBxTlha6+F+t2Wr7` z&#s?CaOSrmS%o@6x6D{Nv~{qCOrE9tQ>axega^Lb1Y}DJ8@Vrri+s3F__4PN4IC#Y z!l>(VSwDyW8?(l%qs3l6`_>oHx5?v=)TT~{>DPR3gtw?WLl1p!KV zXHb3yg(g&2l2x~tt97C-TFU&XN`eSh*!WzyC>Z%xUbY#^zL^ydIFvhGrk+C|=)2m} zs4C#sKeRhPy!!6r#u4clvxvGM$`03PnQ#Zgj8g4Zcmy$|&#pua*FoI8&b$P8ptKN? zf@D|OrBLhhe%XhWa5Z{`ZbE_6d)M*me12jJ303T+eN z!PgLuj?o&x!MglH|KiYT`m^Gv3x_e^4NPSctNpx2+e!BddOGMdWtVGT99|fdqmFi_ zd5Dd8A1!M&fB{6U%g_>eGR}a=I0r_7O&I=NCzw8<^57_l(B!}Xy>Fc7eTT;0WNn&h zai%j`y5oWv%hya2FZ}SQ-Nf4GdUUc9f_F?Orh1rStwO^nGh+;Ozg*?!4H?f46sEs+0goY%s8ad3*{w}4#W=ATq9I_dx_IzWk3H-wYqtC0yb?q`({21wLSkL-*Zg5hx1=o>JYrl74 zjx=vg-jY60780Vzx5^CUO~opK4ngU$^}Ez@Cp9Be|ObEL)lvh1TNE1(k#5!MOQ=l z>&KJS2pE8tdV2*;A;_@_@xEAZs@B<;T~68UtrcA1A^fL#uL5n(0uEtY@e=%(?f6`q zX!k_JvD6Ayyg-Lh%NSs+*cnT}=@|j4u;Je>V?cZGHa)m;lUKSpp#?pM>U z7?~(-$+eP^5rYs>mh~_{XlntKd=#X1rMz(uL`L|Bw_zwgQlAL!xiF>hfz)9}qy;q=SinR0xuePWBQ5#tx6k zw_Xjxe9s2mJC?UtH>^UTDzagmLrcdfgzebepsZ6u2JiXmhihuy2}R4-d(agyICD=P zEAAVGFjKPo#@-GYmN2cFE;I>Walib{OA5YPcZ0HmT3j6eWZwq-JY8KT5oC<% zg>^m2nw=o^)iyUs4cesb3E6B*N9IFi>T<{&9Ep|+E~A06YHZTKb--rQZzyp_?F>C1 z7VN}I#=0{x6e`I*FftzlH3O&!mElCs^k->Ig4l{qse7LNcG09uIPo(=QDAjy0yptT z!-pvhhEf$xX8xFD1v;O=8{%VSgOrbGTIeHS<6~NBipWE{2y}GP@@jVvBQKJTK+9ZOiB9PTU~TkKuz(+mwzu~%55HEWOOy*uN)Hx?)w78?H$p`zX1}(QrkPfL5u$3O z{xORjCbZGLo(AWS1j|PT6=5bf(yg);Se&LP%d_xu)28{@$Sz~5&da`&Fs?^kyS5qb z9gshJ0T~6~{Gs50yQ%MYSvR%~6PqEbubc(JI6u$niF|@o>5V-@v?1;P!$HYS8&Va^ z>f$~SXO3=V2rNJbF0Seq%Qg#l8z1|wRE$L^{3v!6p!_9>^=N)tuy za7?AId#Kwmw&yDS;Y1JUTY-oVh=Exi*Elu%gc^X)U>i-uPol`!Y#RPiJr`VUz~}8tuqH+**r&hpD|rZAwnG>+dwH0PkHtN#UR` zIP(b$rB;nNzONPxeT4H#_m7=$xM9{@VgV<%xnEE`0~H=Q2?TV94J?FlbOVk@ETa>5 zdZkQNtkj@{`rw_fb8D;#I6ll4;;n&F;YZ_fkYeiYJcJy&nPTV-6)Z`I2qP~~L8Oe% z%q`uR8*cFK2DnUhQ@HZoeY>_|1Jx|m$}OLmnT}2_^-JI6AS$wyaXi*3$bL93Rb$gd z^6ZqsQ-a_*reH)2?o+NNc9eTVqbkoMQGBVNdywZO0T~w~JHo@`If|)c01kXjGvnl# z)^u{w`xd9qi))(VyVkWZ4h|rXe#KU4d0(&LW@NUY&xWlz7ztA&BDTSIx_5DyXUY^q zuAv*p`Gelsz4U^`z6sH@JKp+A{t`WoA0qh@6n0_v#as%%?K?wJH;zycy3vGafS+E} z|3&xT;&Ut*1tEY$)%V|P7>M34;TqtghYxtpVvsW|yJxXn7VtXz!gbGlh)dSbB-rXJ zeryRztDtkvr&3CUXq`GmNDoump9QW=6=lgVj_W&by1NKmU&?eKkogco02<#`z znid}&_BS~ty!HI*@u|RxJlBI`PKDa#eWOG`wS)W3fC#j#=8oLk9Xyibm-Xp&y*?FU zh!;7zPy{FP%+>oSJ)J8cFfrrkZzH6#u3}|iuGBL3IVh7JDRq-E=GFZ7y>GFZZCK&} zm>R2Uq|pvRRf@|A9ns+UBkWYlKLX>i(|0x}pKbR+(??Cpx|C{K^n^V-|Jo&53ork_YmA9T6nlqnZmtEogaDXFC(XO&A%{-dsRtZ*HyX6}7CZZ6{B#2OA) zE)(1)Vb?0*NJrh~PUgj7Wzd(anH8Bnz|CJlTm(@Y$HyB8CWfH1-G03($7Y3k9EoKD zAyH{XTFG+vM2D0N^bLkg_j>=6LOrec)f|3skQSP|Ne*~J`^1%W)n!9YI$E}xZWP_) zWWTp0Wb^jNvA{FoPBx`ki$9#1m*_{`u69D!9zhpnc*eloV&T1{=b$cn{dC>xlNSr5^X zPC6X?r=>?6oVoE%x$|*PD|_-BdjEm8q825hvQFL;K$reC^*oM=%Y{id`a11)#Cnit z=LbHTUH(y^`q~63XSr&lYXdo>#c)Z*6;h-lHHUpoUPknzOszI5l&a3W54l7)CDZ_J z(I4=tFNr^Imr_3`Ll&`qHnh7843VM2_p~f#!$5`Z=p@iEf03;L{;HH?Tf?Kk82208 z%Q}~D&u*SY-38yzs1>KiP}Aev6VLgYKk!2j!%-MAeJN(cpcznIKM-n1>?u-kb;4}L z58dSAMkcBw0ZF_2uT9zfy;+ zEt$b5S#=UTm7a7h5rQJkAXvC8$Vb0&WO4!jiT)SuGlXU9NXHIr*oC4sL|4g&LEmO$>&@TXh zKL|hIrkIquA{8qFiF&5>kAX7+uC-TJ)HD8jqNS1Byn%I~vM~;!v0{(E$Tg@2k!(!; z(XYF&ACXA=nE@>A$Mh{TB?*As8==tJ`1taH4!_pKU4UWCs8)7hEz8xSPdq3mg>^k! z;rf_uR9EHctc9W004t+&!v5E5_Ot$y3o9m|ca`K=o>Si@lo^NH3V4p51PyK+?^y#>p}C<17zM!D6gu>uj+DTWBX*{qp5TRCc)6 ztaAZ|oA~-4fiqTg@!XSaQD7jT!t~3dVWo<#8oCo3n&N19oynHx93gSn=&vQ~?j;PL zrfFuBkSYw_R$F(ng!(|SBGq7SI|&g%mOWn|pSG(DU-g;tBl=(Ld`a`hPRw8#B;8 zCo0M`)VR5?z64alH>@&*rM<={45mo89t=u3HY)T|o{*Ys7`MUqj=X-Y7jAGgUCbF7 zy~bhm(XheoveBMneUkvSESF?lL3ai^+`sB^auwPp%xtzcJ#83?{CSo4me*u#es$gySL#s}JpyqXmsj=ygU45Ml2W|hZ>LGrz3VVC zmj&N#5%$K&meQQy+u&2GSRL0?D(dNBAaQ7S%`l{Q?EXzZj}W8pl<%A|W4kZq@uZ zmZSCTZ=#sWA%bL+Kl^bpJy0{f@q31Df@KPMcuQ0_wlk6+)3Rg+!f;T}ayPL#>Olow zNF_Wo((XmXzc~IQ-2{PB5DNoLO8@zu(*sZdm_A55_xu z{g^G3IYVs}be80$SRb@k1`EiOslCV5KIFT>g`&4v7zY7W3O6h|&|4PwhjHt6S|7*3 zR|RUyDfXiRc8{$h#4wVlW}T%B86oXQh^C4tP>CPGfp>idoP*#NY1i`$p(gycWZSjR zJn<(=C4Avt%97F0*(Bq6TzCxUy)=nF@A|Tdp$C##a~Z#{hp<`* zIyL3Wh!E&(>IYl8Fh_Mt3w?1)Hf2e+|FrTTh}no5v#WWhgG5iu_{6K`=^B{)j2KmO zxB!?CB1oIrv*bzeUtN>21byH0elc;*Rr&5eMi+nb3A$2_@fLuvAl+uahlV(ZN977^ z*2HqYL-HJD&hv)aa7>bFQI(4+d7=`X30{SndPa3v(bSEyI0s?{#dT8mxMlGUbU#ZE zftneS-={Swu=&(K3IisT{*8qp0Z_gGg=v7V&o$ttN>}bR#_;RsU_JcS6MTsE z5xvll!}EGXfqCOb+#y+Ccxj^O+^-(|M^AmeK}9QW`w54B&sx;T@)7sk8Vz>BDcSrS z*oPCsazg~eiPUjn2S&nfNy0&yn8oQ8eKFqJ35C4{wjbD|DqEfAULc><(uB z3*^@@?r^_}m!gkNqOL4EQ(tG=^(fbi6|MZ&$;@b1&?Mf!Tt(E^4rJUc&ZcuYG2L2 zfYQxb2F=6Cog+7yZw%qKEDsW3ZC?Nbq<+jODakANw=N6j(%G!9Av00yrsvk5QFyD$ zDzUNL7L~niwF!iX*a6ilf5MfA?Y)C1*u8uYso#P2#>WTk{zdS%>*a*yvQ)^zJk+)n6x7paSzu z0tHyA95G|hO^m=vm1!Dy%;IL22yijrf$W6x6!W5XEj~uW^$Y|I_nhkXO-sQ+xOMy~fh(C| zL3O-HM}*Gt4wM>infqG@`zjcMCjAFK2&xDdPXvxS@8_vk`ve2t(l4KZiT*abJE39^ z!>|j`*;~F*A3IH?U@V;PlcLg#_x>Xbh7ke~0ipj{V)zdWa0ZEx-VDf)iVxHW0EGC> z0wnVJM+8iI>=24m8ONfh?6n4GXC*W*-}cFZ1{NgZZ}4b$EVmZ3A5i(NNs%Q!JgT*Zh%5e5|1(IzqsSM*_Bi9_gnv$7)T-V#$OY-NjVMU%%j!%^|tz@_^;Q z9r%$Bhfu2fdn9}B6uMNWdGTX1Vsv=ArK|`wCkGg*l~A7$1)_Ub*XEC{0McanIy~v$ z+q+ndUdL;T(zR0eZj^NiECB4!QTvolgckd=zc;^1R#6Bz!a7uM1@}xfZdun>G z^DZCiBjb5JTV;9J791-+_O@JF!cO?jI5VoNw!qO%{gmTfLAa-SWaZ|$1#T<29G9Pf z=B-m|OJq#P-*SJ+vb%o5a$}o^N!X6BIIu98%H6q=Z~L8i!O9kirm>nZDOU#W>hbEi zfsZ?_)F8yzo1zRh7U73?=We;xrsZ|A{Q0x_h*&Ppo-lS33;=2k_Rli*@5L+`7ZW3s zU~!EOrgNSNW3Dh0rQ>yq2b1S$ww_8jI{FPLYeC@BmtO@UTwbWp8Mh0I+I7~yZM?mE zTL46M-GfUx#biFYDgSkI+Bg=zxcbSN2Vr|upOmN0iwC;2>f$3qXPr}-v%$OmF3BI# zwsi0YL9EJ5#g94F^eJx%Q$JwyoOS#Inj<7(njgdY{2{Q@df2IkJd>CTQVXt?@0xNL zht~AQ!d&rp+wyg}lUFhfn~#%U!;m8hWm-;j9#u;)FiMk`#woqD-|XMJo-D40_uht` z*Y&r*&FCMAya*%M%G7NLDsEU5n{PC{O(vA%;etd7!yzcNhmMrj(z zNkohHxsk9G=7IitV_YrG^@F3?R|PdN*;54ge9A&>wKH>T4zzEW8{-Re?bC~Q4t>T} zYH7MhL%dRD#<)$6-8_Rd5j-8q4cE#R=Ow5-bCo6Mp~fw6PtLnP#?K|~03mqX4u^iG zAhNWDWuGTvb9Ie$n9}*8?-@o&sOA=ke^Z`S#TlE?Tj#LiYHnp{Xd$d;zz?)*Yu@C= z5~@6022kM%f%Hg>4U`MWuF@(@R&njbGl7yhR(tWnh1V)ky_ThigY&S4E>G09drKRC zDapL7nkn{O{_aZWLen*WRS6I%8_vZUKcffU@c*a|>XEpmum6(G8?Al~fo+$m&&2=p z>|E&Isy9sY=>L2?8FhZA$_#q2`WG;YOrY+6S#4z%d=L;Nmp+C}F(opw49;13HsN<0H7cduN}n_ca-YP6=KX0s?$tMo5YxT0Oo4*Z3pcr| zc|iy^Fk(!>;)Vz<(p!270d;d)%CcNcBPV+{Or`2F2e#iL9v6xP z&z!k;)XlcZ^@Fpnlnef0l^W$sv9iPVKoB7%X>Yrv4m-j7PHCf(rX#-ZCpERsyRK*p z-AR(%TKN}#T;;07B?iYn$o3y{1yQaibPhSj0jmpXTz|CKT=ik;a=W1N=SQzXFxzYQ?kDPAk1_pX)Rz}+WXF-@ZD^=(H~|RqrOG@Lqe1u3 zB`T@plrqkFcqEfa{chQo{|KcC5%8wz>BOgXYL>C}BF84j6NQ-)M>bLeONRNuf%Sl{ z2{LJsRT##r7jlW^m4Bwp$tyMHJ)(!^CB5dY{d$4H$vI}gaHSwdK?$7mS3H`lc|$

    @E9itCYY0fES?jj475dxE4V#ZZsfL=MDsbQI_iaxW917!84MxoTZ4 zd;_IWC}o~3SZKd!pUlhy)edP=YW79U!3NxEZ{=Dg)6-3D>%g>%_Bx~hb*=i`eL3KB zVYGoy;xy{4( zX)eOGDb@69(_2ZF{4}o>6+#%5eWy3XZTpDWxiGc8BQlui(fX5T@PQXxNchf&mtA z-p<5RYXfLgMS2Dq#U3F6QopCOYSxL(qt$1Zg*N^5E?XmKsUkwf=hcVf1$_+OGEo8Q zb&xEAOhzhD%d!vS2gtWGuJ8Ao=3A%(hytK^j172ATT27a;ev>DM!hJjv`gahL=U`Z zsd5L=4@@VopSc!PL?jA}hU#z+HDZF<6DaQc%$=rLgUEEpc#m9bYvjlcVa(&Wu*v}* z*y9<-{mPqA>7GJH7uLP>h3!NivAM^rtiOmZ|8WoY?NTzUipYIp^1|l7-?r6gwIKD5 zM4Qic(IJGG+DOV%W*CN%h5+HeP#^EK-vGO{@>k+ZhA!|7!Lpv3V2#n3IpY8e$MO?J zIL=vr{A>4zXLX&2vjE!S$Y~HnLT$K_vVDX)`6-iL8I`N$*ku(nWJHPHHecmp6v{$& zEIQd!T)iE`P<7xa-sw)WmVR;20 zw6&7}eXq_xyngE;HDn;(Ikd)ef%M$adYLqb2_>r}r{d|r41Dnyt3!H@4aSx~1G1;5 zaVB8b%zt_4Lgh!r$dJG!Lu#ZsKXMiw3?`X_YE&rm9k^0#Yw1rWau-;GgXuhzDm$9c zk%+fzS21$zY@7wo^b^MZ8Y6w-EoAO{7kB=dg?EQP$RitB2qvA2wmC(hFs`_OdvSo_ zY;!l`J}?D2p}=?ik-7mugn)nSjQBRx%D0*Q$I?1BSCHqjQPr+(+sR$%zdO>6v@|P} zcV^yMdGf+ai6Cj(ReGgAn#~dfv2Ro=5D>L0EMl+RBec1-Cth`5`1fIb50^MDF(QWH9X}2(ovh7k3%ag7`9eS26#SIj} zE_R@?vMKG!4DD()@m%=p<*{!en!?BH-7{M}KGJ|s9yPuCEM*2p>&4m<(wQB$I6|kepxgat8e%CjK6ANFV|x8kAay*2wQouf*S5zLsMH(xSFdu!-Hd=@OI4^ z6Xcp%+cq_CvwOcb0+BM)aah_rb`x#~$vVP87kCmxcvn3H=X0%009QF9gUW@C*|pN^ zUSuW1hcy}zsKBb5admG~AI^78<)L`-}`MGQEXlr53*H@U0=H zce-7U!c3Nk;JVS`2biD=3h^lxA+o%(Zp1h%vJNv8Gp}t#XZT=V;%{1T!w2WSRr~ZF zZM}09yRY3=Yu3aGb@!snZ)-Fr_x(h+pOG83jT9X-f|{@hZcLLu0D2Cj=cJgw(9foq6W%j+7WL82{YvNz2e;IR&DV#|z3;`>2tsLkvI$$drVWoYNoF1=rTXH{jf{0IW}ZhU*4Rda4q z6VCcv3WVD&kd3$n#*Op6T{WT;%en401}*Q$`ys_^ z)kro}b10k#O~V@h*V*d{qjEF8kcAX1UR_9{3v0EHEh>uvXEkxfsRBQ&{G=AsM}O1- z7Uxi}m$zj9X@jF`>6CpS8mXG=2rmJ`9}<>e-sKOPnZdnp(_)>?t=$#u+#0K_GT{aS z-Ho7OtEj?;PCbj4eZFvRIw7jlu^;T&9`NqnywHncke%t3eJ#z#{g^arEWhNY{|J4& zWpduc(*F*M-mTkejX=D=&3)41D#skmYM&;G>@CMM*JkexAF*4N3s6I5McER*NVel! zKPXqfPXv$ZT*vciUMD?He&!dRQwkO>gFog;VD)jX~p9DQb71ETmT{O@P{*F`A4 zcl+@HubS_4%j%Pact<$dwzZ^eGL=Tm3Y#GJvoNF^1qZ+ndv>eEDcCI%t}DRKSpz&Z z<<&iIg>J(fIJq(D7v1%RSii#5Y||VMEccacqB=W9rxx0RO`a7xJ77$bw^dg~ZW+eq!kYfi{7(6ChjF(}1d|XUEYMXa$ zk)_aTieGexKhbZ#PfT}Of&YP!$MHnL26M*!k<;EDS(~_FG5qJ=C4)_WXwF5%%f3$%wBN|5P_%n#=?1OyV8DBd%0RS|>v8mPIO(bL zua5rVEuQqM0;omgUL^lI1_1P*CeuImU0@<2h^--L)7>c?OB}jG0**mVxXp!J6!YFa z*#Xu~dp?6bn3_jzCn06fF~V0$xj?-SF>)W}R#Q@VeGqg4%(LB}7n1v6=Bc{=Jmx#^ z7oo24*IW)*ov;25U~{fI82OBhQhomE|6%d5z!bFn6y*^AB1;HB_JQ>I2jG4&YYSlv zKJ&Jlpw=G2lmpR6b^0j29AG#RMmd%XHmq;YG343bQqbgV0~bV%40RM&L7r(umBBb$ zKCZ1o-xmoz58J7?1+p}^rA|4(>nbKF>MNcp1%qv3hsuE|G9}6KfOXZ;DK?>43h|Vn zz7^6Mo$K)TdxDmZI4Yl*q=LJg!&`QL?GX$?qK0-PWl12lVE+9j`|DZ?w<*-5P};F& zz2`&=%HY@gZ0G3yi(_ zppqUFQZ8`%@68GotP`Dny0x<;Z)6wmz9Wb(evbR3FR|Y=meCe}t^cNO_teW6Xg3W% zbZ<`2#0KRu>hJiOb3+sIUhQFrMd&+TpvKETI)z*85OwNbI-m7E9vT$tQ6Xo(J5@kl}iaUQtU%TY;@p zPgDb@cZv{VKCie81`=b8wg0SHekVT)z1}pUYLr-LHvsTWoF@D~9j_U5zJ}e9I3W{> znXFyqsB{l2y(nm)QLi2q@%x7ek+RzkY;3Xb!AwCAS_l<2dOmku<{V3jcP2>m_~)xr zA=b+9L1kr6@*@J?zF9D7+Y773#}iwk06%dA7hdc${b8A^vuQai5-=opo=lc|WCFV( z8)S!OntrN=*;$xf{rQxtmQqt$G~R?z^A}fcDz@(XvGKWQ&)Oi=D`wc;rJWj(%_JSZ z>C5u0#_Bv2sRDjy8B*mQN+T_iE?H)A87(JDl$l63?}lV`5aEyzmCdwFT9lyv{^fr5 z6qMW0ah}^lQNw1idpCMi15`0WQU1vZSL8NG&z5&_^G*J38Q$^Z*zjF^!T^OzVv^=# zxA-LgEoe006Q5kYP1Sw31f`CkC=ym!5ut!g15p(|*9Tt58u*i8s_K8(#O9XpNEYz#8G7kihLQ37H5cP!k1hg|(&p+ZqmWyx(b|1Z zMBF0O27i5d2!$z9{jVe0d8BMag{sue|A3kW&BiMpvhUsWT&75 zmElOffrb(JbLh3=0oeL4tipr97EvHm1&^ObFo{g*J+WJ{lF#F+wddfz=6-620aC6>U)@w3>G z;L8xqaKpS?H2P!hzec1h2Yse528dQuo=#?RaMkZ2}^6PbTg;ydk1znXv zZqf|4eujVJ%@nI{%EYe3A}egz>EPSu?}7B>D)69-IR17hbZFIpUS6wx+-*>7NUVw| zo@kv^G?K%*F#b3h+6BKTTK?&rMN1t#D!!BjTJC~iuUwhos{O`WD{0wxSs!q2|0g0~ z%Xzg>ac@zObCUuJ#Nd=9n~Z@+ zB3ok=@Zl~5J*KEEzp~g!q@F1l1CHdMUQ}HK$&w<{P#$egSJ>bKzh4lThfF-pB<5u7 zu;Dc@>_@C@zc7gihqPnrvvs)(J5R%mruJ2iGU%KOy((w9=#g&1g9GBXwLWRIU~dL} z*mPLI z33%Bvk^OKB)7z0H~=n6GkDkEBJGTumYD zojU&v|G{jmrrEJ3<*=S8+u4zbVSU8d~j^!dFu^k znJ_L#>qf7-6`Ih%oe`-EBwJrrFjRIXk2haSoap-AFF|e!?Gs!(uKuC6)6 z@*=#&Fr!_`Vct3n=-Ik`b;Er>fy?5YCMyxbcqrXT7x*?WzKB>T-He+e!R1N7K`J|v zokpPlUsZ1z6<5MUBEV>^8fvb@Ur0QdN0ZBkedkSwzlm1;w0;A7Y zrgRP#Bp#lIcV=U-PT-X?{V`3$UxFC5L9C(>q_+C#9tv^?{M^j{$Tq@Eopwby(trPRL zzWOt8c51EgP6UEVJ#kEo0;Y6yJ0dsaw}u<_Ar0wH#x5jyMVixVBK($CPa0`|Xe9$l z*6MmtYqP%qGH-+^vf_e@DX#BSY70SfgZ-H)WhC^kPXlP|aD?~#qeF(uu%eF^XsY&^ z22lp+YBhqFFN-C1)qODAR@86;br=+T(DHbU2%*XGSh~KTnjQ$^b>TKp9LcbesLP?L zzaFKR%=2kRA)Ybx(|5~YZhDK(>u%CZS~5Eflw4|tko=pT@vrnB8*f{&jX_lCYp+5G zf&o1bSGPtvO6Y+k$&{|e6YmmlFw#@2CXbghTqB{rjCPS|?*^ozvF}}@mlJ4He0W%j zl`LDNS15l&g5kkj09n|PiY@w5mKzBx%l-4kEOEe0k__CBBQw)IBThZxJzl7#R6_QK z`v!<|ccfFJZ-F~{GC_^|r;V(<9QUiv-~+v*g&w?dfgeU>(3E&2ozNi0AQj6vVtQHy zSS~;XW`xq85|D*5?>KR|*i{!2!+hPE{y~sNL&RD;aY{!6NIb%>Wo_^Y?wM~cCu{fz zoDR9psb*>UX8XCE;YYWCLTy)Jy%F!I!lC20t=&f1vB1?#aiYyLyT7BmZYj9b826c6 z0!e+NgZ2vj#gY4)n~ZC4>6Gn-*{3I=lqu9&UzI6!=51_5F_%**Y3X7_^DQ4MpLaSh z4!u7oWEfjFi%9=fKh0nJ-H-CB1V}=@i(-6OX=2}oubdm5Jcki#L(GXwEk_%RtFqU0 z$;WX+pipb|7=rhjy?>X-x}m@FI)BV!MaUOLzrJ`$muYso7D^J^;#b>$^j8bV)Z5}K zlXLhxC5dM!jirtBTxJu=!u#Scidp$hxvoD?7yAfFAgOR*2q=;r)-P8S0Fa0ECxwaF z?w_eODhc(~usg;`P@gC()V&U+5{YJlDoZ8wBnopoeATTCUSYz!#fmwrdLON7Jgl=h zn&~_jrI%hSsLIbbs8rsD4V%?hq;?bgMNjn_oon$AHS0-t+;?uYx~u*OTrPRSn=-E= z!mfX9Eu~vkF4Vh&`iY#l9&H?9`Vy}&*5a_)1t4kK3W(*NRZ$@N#-U7MTvGS53PTS~ z$9AV@d=bU# z%VEHh!wdz-AljelB?)8zg!04S*WcxCTw^(H^Sf{DyYCeFk>10RLO8LH7?i@LEWcW{ zCjK(ssP2t$pN6pP-#-5g0&v-p#h8e3)hv#4n&;$mx|lV~x269=oa91VH}t1#O#n1G z`Q}|`NVXx@{#NnsvGFAeZOXmLe|B7OlI-4=WWv_?ro0Pn@Twg>noxQ^Of%03HN)A4 zcS)2t&8R~93ik%tL;Ab;YKw45MN)NToYfvep+6td*R~3q_TaGt=5+K9@IhC6Kn@t6 zTlrUE2-F`dssm(&(fyL6zqI`M2IR1mL>ouGO&)AX--r86a#+9I z?749fjXe2t!0lCcOIQT!T4kS#7!Jt;L2Z3A#mxp(ex8`cUM)RR9OL+Nvns63zIaoP zY%~dKH{2MHV~k_mI>V2CypjgrieU(5;(1Yb-Jnk%Z}az!ttE>eOU68AZoIj;W=j>L zI>ShA2QL5**Amqc>(d$+>vspB#rARyB+%N)bHQeU@^TSoG$ z-J$ZKCos$G7uJ$^b4i)k>)|=c} z8dA-HqsKANW1Y@f{>0afPIgqE)H(V5)EvhDBEm)(>LC|nZ6F@`TT`Ia;U2hC^2!8l zd~%M0aRmeSIpqL)Mz;QO;UxLqM4&p^34C!_jeZ4T$qIij^sXG;-L^2%;yoMBO?44fMfw6ua~ybktYF* zxIHG6>r3T=w35R=S-%GwqMg`Y7T8_B>A>oZAzn)$9~CN+CP!~Zw@zTsxPNx*c+|{3T<`96eal7+#g@KUgP5+h z$)su@iw=Th5+c!yQx}+vj0A+=0pg{ak2h`n2lFTWlrk+rgFRxeF$}d;A3@rF=)=0A zdjZRz=q^CBH&-PA_9gONl;=W>a0={EqTdb~rWe!u9(+l4r#gO~iFtudY&=FgjI)1S z8Z7$HXN_}4;J-r-VE)3dO_XH*Fl{^YSvlma_wyr#*me1yN_E-0(HjsX1IS@3hV+W{ zCHBA=7@8Lb1}1mmS89Rkmb)j~Il)(-c+nJr-c9E*(Fq#t6>8=QEpVH<@BNK$!93C- z_4MUR#E}%4mO#ya6F7QZaL+y6Q8H1G%XuUMINHSUz7HnPjbZx^>AYGX&SDyiZR@F> zA0!pT+WkSb`kUT3Tl`Ry5f_66E-unF*)5B&~;%KvV+tf9Sp#$l)j&M9KPJ zH%A~E8H(z| z5&F%=#3l;g;>_z;+F>i^^QR^%l>%(WrKuM1Q+;4Na4K+fDPbd_s~q<%MdK>{V4Ck` zyf2*A6C4;urbvJ4!UeqWgtjm8nP+uFjod#7)`;D563Ti6Z;Y%m#vO+*eJaKulgp4s zLWJx|@u@-rK1h{O?a4+AApvRL)KK_?+l#hhC*ro= zvNp}5(d{&ZYGK9@g*`5@h2-Y_+c1Db;lxEB^4)lsBR($eGaK7{NlP_d%7X3o(WjV$ zz=4jG?vb}npTyF96fd(Q=vhW`C#0i{e0C)&iKqaSJ7Rj#vAyO}4l7~b9v2!=uB?-P zZx5m-CJ*HQ{cT=@%F>CK zD~Rk^BXhmAt>h`|8s}w=^`Mgk>xe>TfE=!(8nO8Q)-;UE><0YN|L{Q2)~tLtT({B! z63G+QA2U=i&8)VRSu|YgN@6wKsrbd#e$|@A#}>{HeOJD6(9`*4&O>_!=1sc|$JyhT zqI|szE6Rj#UajPl{t^BM)%BCt$)fnNSpdtQ3PqEu(#+&>8zK7!$-*a(3yk+Bxghhj zao_#tn=a9C1mR=wrXa`|EA!Ojt-CLPV{U4fHXl_H+th%}Kt+g00<@`6ThORxm!>4W zr7Q#0c8o-rAxQY!7`Xs~)odTR#G|RatgV-BNqayJ4_R3A_J4ap6bQ=`{u8GyexWy{ z@eaEta5-wBMb+e1vFmO<^*YXPRC6np+XB;7p30wJhUzCw`=W8efc+`*>&$0@AN+2H zD(VH^s4YRM@yeDio3e{=F@702*!|FqeO|T}Z|^L!QFU*@<5z3Rv_cG}fJz3cothqg zx^KL`Zaa2=#+7*+qP*grmY2xwzq$#GklHIz1$8OdW7jx~D`GTSFUUfFUJ$0*D|USB z{c!J}CluF)-JK&hW&ik)W2=h%t~4Bg?3zxrQK?Gz%OJG=`IRhlsRI3b4v0tq?Ct_a zk9qYHVZi#-1O9*XSThPp)*2li)}}u2g&qTdK*6H_=&=i+ZbYumLL#AOYM0CKYuL`KUKlxgCdrk)(mWN^cNhWKCTx-Yk(?X_*pStNb^%xLRhV_=^oV40xf15APhe9Z5 zhK-eP4Nk@&O-@2h8%XJO%kmZ~LeB5|K zQ;G>qaUWO-GSvn(ViJ3b97VqW+e|jo`K``H@}Ao+IZBD~m*A(_Mz8m6B0z~X9;pPw z`|*t;YMhWKuGc9Fk04pE&3x60v@7v-g`P!V`#i?@VofLB(QDQ7|ZVk{rBkYr(km2{ocVFHeqcbL? zZWoOx3EIyRR>jCeYoFM=`{gcazC^p5#gzC5r!se%HK`gDYEE7sVs7!4-%BB%c4YCd zD$1*kiQw$`waGio<3Vd9GBIqKIB z=sxJn^W;E7r{03g^N`7yy64o)4C8@e8XjD`>!3QW#Ua&^r`Du%XqQSZGX*VXx2(R5 z5EJju+!V1C>}Qtz&f9aMleEVG+DHwEs{cZ5<^aEmmvd#Sn0#dhLC(!PTsw0!%kG0dz^Y{9=# z#o>><^+Gy=AcA{SzHI3>;0Z!UuL5`XxzSxeYU%A~ElL|UNXq^1;+XEL-@HS6`Yg6` zLIR$J#*XfZmUqD1SeCDze1bxM6*edm>p6w1iP`DjwQ2P>p(C!cJyoSQ`UJ=w%78;w)=5a{g=6W)9>em32G3^=e0S%#Iyl7V zDf1Ti%I`c?@aO(XP&S;jeleT7uQ9;v`hocOtt$VN@%M7i@$XsFfGF!qk0&##$eh${ zie01LwhepPNtSkrl9dB;hHq8uQ8ck~E@J%A$8L&1uW>iDYcT!Eaz+WS9a9y=EqQZ+ z3TYGa6#*;?h=zp@Tu~U%!pA`o1Avf%V7)Hv-!{&+uAU*KB`h*2a*T0J1g5lZJRxQ# z3f%H+3s6YQVMx$!-s{?aAfr#+>Kpw@tneH(51%5d*xi=WX(wc{8;8iiYfUSh zNyXafbsc2xM(P#;3gQfM8I|i_bt4x?BHqq-Eb_x$lijOd{q@Mc26@Yr|M@$&8AgeX zhCXJf$AclTN`&9@1JP}5S$ZYhxB?%sh~2plbwS)N!)EiQtU%8?eiBlMd=g_b`6(6eTbfkX7p~fTgHm%^?LGS!M(S*>&_l0 zAuLt;?mTmJufnq!*4t)zX;5#1SH-ZX1FsB{E%Tl!`eD0=AwvI+=9CgS%J|kB& zP}4=@8KK%2_l6XcMhn{P7KtMueWjG#08m-*Mne|6u-7`-Zm;( z%LUOQy^(6c^MB~uzEI~QgsOP@A*K6s$bZg2`<J8B6K&fejDrrL#&5hM{FEDTbBEf0JcOT+;{V9XB0fkC}zY8>O$ zM8FeH;mBPq=xa4a6>*U(x>MJ9P(f@&I89ks&^s~Tdx@&)270!Eh{m%5j}bn4?4l{v zE~R!u%#6Ndztjs&r$>&>K`4LIv9^O)zwhX?;^3rzKyZqb2G1aQjy*^tmxIZer~#Z{ z*T(qqv_^N z5aQ_D=Jqs2>p^<&ajxnN=?|dP--gtX&bFXKDKh#oTnPdx-usw>*B@@sKdrd6>q9{( zmgKjQJW#bAl~e~t+BkM5gs9`bX72H1L(znwNh>VDD-lZLKa=m4YKSI7B+B9~71*8Nm>ph+$t_5Vqp)dN)_g~{FD*ta#2U@VCFH~C4Zb~e^e3!~p#7+_ z*G0a+hN(gmj-KN++=}!+!4=;pHCgIs`Dq4e3W4~h98z*@p*YfxdKqUbnpk|oZB(Ob z2Y^@NUQ6>9+F~My6LF86R=UOE!NUEjtRl`vaDqLyBIieyqQtBhIjh5>SiLn|)P!@n zQ5WSilj;W%rKQK}RLqCb+AHQM7ym-VkU7~n;BcE^aG& z9wfl4H!P{Crjpsf_dl@rcBSY2r9m@RMLl9_+BPgdxb4IY@<&J3&-F7?(^*3U1}j`9 z@GX(QDsfaxxOTQ*X72^StBSUVlFFrKD4UA02{g`WAXeDOd$axlQCt}2n$wkwTK3~N za z^B-3=0t8C}7!*S2`ZfUoaR4~{A5$s8wzTOREgasmv!L?V2b`h$MhG{OfbF~1g=MeU zPEcThrwU{jULrO)|6$d{(qBvKmi6XX_a#GUwl*qBpb!uxl*^Yx1S8Kj` zd3-s9NAZsNpgcOScxO0#+oWbxkFYngWigOY`|c)tg(F8I(T~ii$#^Jxj=#zSA@y)z zadAZ0T>7VB)x&^54B1TpY&s9r9y&3RqG)%-tU)if6JpzW6&B~)lyhqFJ zAv0L&vI~b#a{iO>MU*%+wL7ko(V=+W@EhUfah-5+{zpWE+VH}$Q>a!HL)k|IveY#F zGflD5gLxyKUCZ&RsM4mGL@QTKf5R-OPalP-y{3pF-DEA*K0kUKGOp|0;i};)J4sp@7-Dn((Sx7{x{q3;PQhTP$I@OY?TO5(JSW@rD?##S?sQ!KKGJ(t zs?sg#*;g5o#N{$h1JyNlKWeYde$+@Y;9WlJZ?A_YIIp%P1&R=UmY%+{eg!#$75|Gy zr6Ik99XJ(96dah84H~mM3h^%z*82D-%u;l5w4UT2=s%y-E<3%vU;s!+4YScf=1@{2<9Q@4$W7aNmGH@4__MJ ziqO7x;A!#H*B<7jFy>dFe)SN-T<_S)?>B_;s=p(m-}a!9kl6C1GfwKFQn>}k145hf-tlij4HPy5bV+O&k@-DOJd^-`7X$#8!AV-uWP)7M53WiBr&ah#mWiWh!{$vn3*4A=tq2M75G+h7Xq<>qS?46^YiDQO? zwmENb-Bz>QdB-d7fmm~`LYzf?HCzhy$8!lOilgsb?9TzRjM*QN!IhHd2c5X_=QBxe zRW1K1B}bz|AHVfyR(L4k>2_6`+cKoM#*JGn5>)fGe)*9v?3NKnoTwI3+{^NS`^T-k zI28JPN}r@l0)NiGDLb#lFO!D{-Oc*1VL|os&08cjBX)WMC0p4rf5JsWyKR9pywm$k zHOHmp&{y(yqOeB!1>5Gvna`y}FN9kt# z=TMgh7ohiu1$@_QdKfqgs9K7LU#V;=40^nKwW)#IT2C`+Kf%zfEVRzGiP$sV`36Qx68Nb?9F{-aTPFTo{SUqGgnp z$;!7?5c3n=iXebC>O+k%LBZ9UlX>I462v8%%`<(RA_}glpN8wUo+6_;N;zC1YCyuD z0P8kV91G>@l*4GoA*fXEi(2%@Lo~kgFEl4RpS=LHI%cIev?h96Rv-MwY$QI=FyrG} z^Yr`_NAX*T;SP~_7zoqmv-=*5J)Jl_8JIZJ$}MYg#yX~p!=~z4u6&Vna(NBMm$~V3 zUgk-6;GP(NGk?uw>;!X!%dV{t*fiabo6He#8z7pik3-?68t1$<^aJ^};^LqY)w%@- zHEHdeFk{wf++bjNdCT#M$IzaP>0N2J;Ke;Q2fBXewlLJM6EKReU|`j1_OJ-MS@lDC zT{Qtb^Dj)oO>EakCR?IRziqF~j!^TEd5t1%VpZimE9T;KZx?z3F6pY5ceOxl{?Hf8 z`Z}-8!6&DZfhld_Fuc+E8*$lnfjk>aUf zRI$iYb8m0F=nXWk+@{nH&uwVoow@K~wRfVwXAmmRkloqdMR$3fZS_UY8PmfeG1U9i zv^j6TPP=hc3290sN!%UDloVz%PA<6>EB1{?Zzxsonv7lAOj z9#|3ZVwL)`J5Ye3BrxFe8SiP!-FGKm(F zrB+`Qq70S?y%?tcmsVlO|KA00v;xEDT_BP--4LWaj7ZM^W{&%mfbLGbP|U;t`3{0v zF<$(;i+pmO&`9n_!vHJPIll2C>?dr}LL5micBwKE-7t+DJ-``uMsux$qA3oKhE(@h zaR)cJ2{8oo>{%zvH&abR=V)e3=SM+$mO$sGAMXF}#Uvv~>OZ5@e=h;RUUKi^1{&cn zRT2xmFU;9QhW;0o zZ8)-DtV#W;0kA_G^xx&GSU$lHR?A%}`FZ<1#|b0RsWT$y61tQ!zfD@8t1afP;8~pn zu>?-8wC||?Bi$qPCep25>6Z7JRqW_Ho_2+_Z0t9cr*ft^l*kn6tv?j5=_2plmPBE{ zK$9aEh*bISW1#>0STgk3J9nT+cOWRsj#w20D(Svu7(zR5me4H1fe%G&WZMpOPC`>H zFbg-W9)D|QgzWbKBE?>-vk{pa+)$_F@8#{9cqbn87Ll0IQ&fWnM3e$o~fb C8yaT- literal 0 HcmV?d00001 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info b/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info new file mode 100644 index 0000000000..cc51168e16 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info @@ -0,0 +1,300 @@ +6027 1 0 +6879 0 33000 +5 0 66000 +532 0 100000 +5 0 133000 +2458 0 166000 +5 0 200000 +475 0 233000 +5 0 266000 +1262 0 300000 +5 0 333000 +554 0 366000 +27 0 400000 +6971 0 433000 +5 0 466000 +601 0 500000 +5 0 533000 +3276 0 566000 +5 0 600000 +658 0 633000 +5 0 666000 +1680 0 700000 +5 0 733000 +610 0 766000 +24 0 800000 +6728 0 833000 +5 0 866000 +764 0 900000 +5 0 933000 +2656 0 966000 +5 0 1000000 +462 0 1033000 +5 0 1066000 +1459 0 1100000 +5 0 1133000 +608 0 1166000 +24 0 1200000 +7038 0 1233000 +5 0 1266000 +721 0 1300000 +5 0 1333000 +3102 0 1366000 +5 0 1400000 +752 0 1433000 +5 0 1466000 +1815 0 1500000 +5 0 1533000 +755 0 1566000 +25 0 1600000 +7657 0 1633000 +5 0 1666000 +852 0 1700000 +5 0 1733000 +3537 0 1766000 +5 0 1800000 +673 0 1833000 +5 0 1866000 +1774 0 1900000 +5 0 1933000 +554 0 1966000 +24 0 2000000 +8028 0 2033000 +5 0 2066000 +715 0 2100000 +5 0 2133000 +3395 0 2166000 +5 0 2200000 +736 0 2233000 +5 0 2266000 +1759 0 2300000 +5 0 2333000 +605 0 2366000 +23 0 2400000 +7651 0 2433000 +5 0 2466000 +619 0 2500000 +5 0 2533000 +2788 0 2566000 +5 0 2600000 +556 0 2633000 +5 0 2666000 +1335 0 2700000 +5 0 2733000 +521 0 2766000 +24 0 2800000 +2274 0 2833000 +676 0 2866000 +25 0 2900000 +6224 0 2933000 +5798 0 2966000 +5 0 3000000 +448 0 3033000 +5 0 3066000 +1950 0 3100000 +5 0 3133000 +386 0 3166000 +5 0 3200000 +1218 0 3233000 +5 0 3266000 +1316 0 3300000 +5 0 3333000 +580 0 3366000 +26 0 3400000 +6673 0 3433000 +5 0 3466000 +473 0 3500000 +5 0 3533000 +2467 0 3566000 +5 0 3600000 +429 0 3633000 +5 0 3666000 +1420 0 3700000 +5 0 3733000 +583 0 3766000 +29 0 3800000 +8492 0 3833000 +5 0 3866000 +720 0 3900000 +5 0 3933000 +3635 0 3966000 +5 0 4000000 +621 0 4033000 +5 0 4066000 +1969 0 4100000 +5 0 4133000 +49 0 4166000 +25 0 4200000 +7416 0 4233000 +5 0 4266000 +947 0 4300000 +5 0 4333000 +3713 0 4366000 +5 0 4400000 +714 0 4433000 +5 0 4466000 +2003 0 4500000 +5 0 4533000 +750 0 4566000 +25 0 4600000 +8470 0 4633000 +5 0 4666000 +737 0 4700000 +5 0 4733000 +4094 0 4766000 +5 0 4800000 +1019 0 4833000 +5 0 4866000 +2160 0 4900000 +5 0 4933000 +828 0 4966000 +24 0 5000000 +9282 0 5033000 +5 0 5066000 +655 0 5100000 +5 0 5133000 +3491 0 5166000 +5 0 5200000 +651 0 5233000 +5 0 5266000 +1906 0 5300000 +5 0 5333000 +662 0 5366000 +24 0 5400000 +9724 0 5433000 +5 0 5466000 +617 0 5500000 +5 0 5533000 +3145 0 5566000 +5 0 5600000 +578 0 5633000 +5 0 5666000 +1592 0 5700000 +5 0 5733000 +569 0 5766000 +25 0 5800000 +10015 0 5833000 +5 0 5866000 +609 0 5900000 +5 0 5933000 +3618 0 5966000 +5 0 6000000 +734 0 6033000 +5 0 6066000 +1748 0 6100000 +5 0 6133000 +550 0 6166000 +24 0 6200000 +8806 0 6233000 +5 0 6266000 +498 0 6300000 +5 0 6333000 +2913 0 6366000 +5 0 6400000 +597 0 6433000 +5 0 6466000 +1235 0 6500000 +5 0 6533000 +362 0 6566000 +24 0 6600000 +6592 0 6633000 +5 0 6666000 +468 0 6700000 +5 0 6733000 +1920 0 6766000 +5 0 6800000 +419 0 6833000 +5 0 6866000 +843 0 6900000 +5 0 6933000 +237 0 6966000 +24 0 7000000 +2687 0 7033000 +5 0 7066000 +399 0 7100000 +5 0 7133000 +200 0 7166000 +143 0 7200000 +25 0 7233000 +12603 0 7266000 +1139 0 7300000 +5 0 7333000 +56 0 7366000 +5 0 7400000 +273 0 7433000 +5 0 7466000 +48 0 7500000 +5 0 7533000 +102 0 7566000 +5 0 7600000 +39 0 7633000 +24 0 7666000 +3635 0 7700000 +5 0 7733000 +46 0 7766000 +5 0 7800000 +647 0 7833000 +5 0 7866000 +61 0 7900000 +5 0 7933000 +824 0 7966000 +5 0 8000000 +691 0 8033000 +27 0 8066000 +4573 0 8100000 +5 0 8133000 +473 0 8166000 +5 0 8200000 +1637 0 8233000 +5 0 8266000 +451 0 8300000 +5 0 8333000 +969 0 8366000 +5 0 8400000 +234 0 8433000 +24 0 8466000 +3361 0 8500000 +5 0 8533000 +168 0 8566000 +5 0 8600000 +662 0 8633000 +5 0 8666000 +129 0 8700000 +5 0 8733000 +443 0 8766000 +5 0 8800000 +183 0 8833000 +23 0 8866000 +2769 0 8900000 +5 0 8933000 +182 0 8966000 +5 0 9000000 +890 0 9033000 +5 0 9066000 +171 0 9100000 +5 0 9133000 +599 0 9166000 +5 0 9200000 +236 0 9233000 +23 0 9266000 +2316 0 9300000 +5 0 9333000 +333 0 9366000 +5 0 9400000 +759 0 9433000 +5 0 9466000 +210 0 9500000 +5 0 9533000 +324 0 9566000 +5 0 9600000 +98 0 9633000 +23 0 9666000 +1107 0 9700000 +5 0 9733000 +42 0 9766000 +5 0 9800000 +145 0 9833000 +5 0 9866000 +109 0 9900000 +89 0 9933000 +24 0 9966000 \ No newline at end of file diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 b/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 new file mode 100644 index 0000000000000000000000000000000000000000..529bace7a1135949a4c83aca3e44de6c06768b33 GIT binary patch literal 250646 zcmV(rK<>X10168L000cc|7U;xU=T8iViW*l&;=w>g8&@F3KT*#_z@mp&>WGf&3qtl z51#?Db&WYvI>{NltujOiLtjxgk;fzl1e%-ySqyh{J`eJh3?!%!&m;QDjL$;>m)3on zaY=Zv2sq9vJ1>#)GJJ|tAgtC%K;=QJQqL*Gmf{u73didcJ>W1G@0Ox|uhC#b3Y=v3 z?xP|$^w~s#V%lxIr$dtNw~YsP?K1-G>j8l;(6RU;<;)!rsU8$60hNP=01)h zcz&aUuu=nUsJw;%q)tPa+3GtIaBEbl*8heVAZ&dTR$gG-PiluW0y66!-i5_AqU%-HI6|rw{hA%om1a{iQ4w^Jl^3fP zA0kP!^*}Lwpcs)D`3~4CmF0ZctzEqDvdqUc$|ufq4MUHZq6r~WgJw3SOTQve(q%)* z2h&-##eJtDkUDHxM9UCYp|VpFO*x*YQ($7PfEPd}Xa=H@yX@IjtFaSGT4u0%eiM=9 z8GNc$<^_s3XiJf9NHszN+_1gbZ0d?2f$UA`zc-YYxW99|0rMVpKb5>UvL1mcYkq21 zFJUD&uOZvG0VNj#9Jqp(xzr@U>7DBvQ9boSHOc>NBx}N)BZa;zG$sR`f#n(8{Mk&j z^>N)Umhpeu7u1SbhXsABB|;416iqe?Qv)wv&g#);wf)BN1p@pdPPC|DBV+Yru>5Tm z$jzpo{A4CoHJ2iG)`z@0*g7pBcEyTH5AbUlu^t=#H5UG2%MZy{Y>-Fv<4L?yz8 z)=Vb5z221WgkwEnLzhR%UqSGD_OxCTL#&_qODlDm2z+rdvSGU3_dYP(tG!liUgQsd zr?jRKqbB2w+o8ySMM9kE-CmkpI)K1W_wHn@9xr)?XG>iCcw;#2bA zPGtDwQI?5o7XT2<^y_MC_%cNlwG^@#J- z^HJs`8l}o;@kX{j)5-6W({LW5c5%t{Xh}0p_QBHYb=bc(l~_|?Db~|bnhB)Ijx64V zMAPOd20AS>O;Xt+(PYwsidm3aGAM3z=}z2EOB|(4*pojj&2PcdJ{x zCg{o%@2M8GvnEh?5Y$YQ=V$=98ZiUttz7~h!h1XrkgsI!Il9VK)NQ)mJbrm_?2$-P zDPuu1%I#JsFmRk;90s?fq@mk=V)X}B)&cyc;SvoKdSEj08Iz348}3+=$v~JoR&pYy zY5Ofsr~zN#BN`GSvxxo2YP-deI;k+XOf2@5cwP_%@0?Zb- z)Ea~kmX1rl;Jl+sl#U@64x@Dm-+WLPP--jTP^1{VP_l~{JnL~8q&Q!dE$&a8Hkn_6ZLWJx#+;{q* zDV=Ip-jEJgoUA)bn(}SM`wqh~GkZ8lCIKHL8uGcQv~Io>g-s>DfYqyjD}I3Ip_N%m zSvzabnJsZpp5I9E(^a8>M6<3%u;Dz8@pkTZl<}Hyc2~kZN!VLLxMb;Qj-K8%5#>{6 zI&at)M?x;*-h*{Z!cA-AhNx8DvnC-QHciD}Uwx{|Xd0u?Vb-R5WM+NIqaPb%lvj51 z{LigQX4k=vYdRN|&NZ!>LYR9)!9RHpI^6kRBwqf+px^5FZ1lC_< zAHc8eH5%Pg>U(uLh+l4GxJIN{k>dUqa?1T7ot83`m*!ZxFyqJa`(+)C=TWXFP#ei} zV9$aP1pxlICO*hath@O=9$ExLix-%^SA$nLIcR&2Qa1uBC7Q-Wz3D1;c{sWiFgV*x z6jfK&$OoeqLPO41T(sqE_&2LUnrkSNXV;_(tG^2(bCEz)H0-NAXZ8^0V6ec6|Gey> z_IX^+CCBa?7a!gpo8avQQWu-C>x-PAj*=LQ-cC)+*Kmb_qSzCS`NQj*6J;q0nmE-; zLCs#*uxugp5o%bJ%dwEVTH=fuP6U`$3Gc3cRmm&Jp&5f)LmLmYB^z;`bUe<4xQqS6 zR$-GeNbVeIb=F~c*>^L8qcYsIzd&!pLC9D&e>phK zxl*Y>-z(e7)#LvhgQWXo;O}`1YhVIwiT|L!o7ub$EKMf}yTOZXH<^0CBr*}@zfCCo zOj`e{U-enm@LoWP_l{p<(tyEtO=t`53R&XRtsaFlZk2E+r`R=5^j>z^J?y*!V$!Qe z9jvOWY3NSzmihJo^c=3yamB1CeB6XVtUsZOXj}~sdg)M;`0OT!hvkp^N6%WVpS@dL zE9qY|5UoSMIG6eKs+a;%;o}(ue)tD3&+B-07cOSXTAVZT&FIv3b> zVQfzkWbtXftpP#>FlMGe$Dd2iA3dTQqHHoO?3HT76kR}>_si0JzU<0y`|@*&>Z%YN z`aIj9G7A)lXo}jwF#l0Y|f-rD2r&R9}p@z|YBaGpK@z$ZL+1eB)B-|CN z7}(=L?NQ@n=#!2amNIEIombD!KBNVs+iDvG_J$*>NMrKjDjXcu{ht!?h6y@wW%0>b zHP>V`Y(uB*BEti0vg8lv6%D1ze+Z*0yI`exh+ZjXmh}2I1CbT{eJIg`{VJc$(aq(_ zy)cgv`;Ejx((P)@f=3XU@>w#RrYWB`d_XKAVU$Y#nhmGGBRcg+U1bQDgyny8lrRP{ zo++rGcLvFK6Lc4*8*)^5b7@RQGxsbJq%dD&R=eLP*oA%X40lta%M};h{h0$LJY!B! z#z$$ll|z;fQXiT);HEE#=!Zhmxgi_A=G!#3 zTo8ftGTcAYQS4#JKm#4F7xF2I9Ww7;<$+$s(TO@DR%CWP0yyR3KjewCMq7WVmpmY0 zCnr-;ziQSw??U>f1WYIw#9aY`F&2b#PCY?&ZA*PW@UdcImD9v$rPQw__?=OUfPDVS zA1ktwbK3}Oq&iuWu*6D1ZUK#L$9=WLW(NzA~Iw}y5G%8MMn(qUEMy(bG;*8kS_4P84(ep#sa!U zrT#~W1Q4(W*XrP9QIFz;nc2B0aA287x3?`VxE@q~&h|G_=W|5`e$=Linz4x?e@#M~ zs)YfCm4O~X2OR2w+T?J7Fw^>9>e0s4qHGi9@;1$Pbyg9RVKC02F;a%B0>TSPhLZdwM1)AeyN#CFe`&EAW*W--}=v!WcpioMI zXtW%tGOcJ@OMy0zf7Z@$9XAyi?N9ym-?R7**qRl~x>r{xQoTshxdH#=?CcxqZ$Fz? zho?d{8p6@6i_(iqq0o^`R7+J=Rki9juJDDf9CIpbS`6vKDkSPf1rGuCLP zh(#aAZED;Yy|)LT8?|uW6`(90TJQ%(X1e^D!{~a={!B6WB}h>w^x}o5`E-S1aiO3! z54Hofl!JYLE-w$Czqu=bDFfNq6k?GjW6$mU*yHB(1;At+<)#vXq77B`JNX}V`G{#Y#*y4L# z!mKkxF{-%1d~DT7_X1V`eib?bZ6nsb`4xm;#Qx{iK!YvRWxdt*+?y?2t)l^IB=h1~ zydkvI-O_6}_zGqXiQ{mFb5u51)PHvw=MfhUl`OH=)3)uh8H0&Jv>key+>`!)mISr3 zxEyDupp?(aTIa8p((3!^VcZKZf}aE|K9`K7szp_3$GG`gvZ+s>;gG%nN5)-DBES-0 z)oU|(irDDyYaeAadIpO#;3eA|w-yHoZ9$O9=xX%?myZ*VremFO}kst z=<*GWFK07-y1X_m0xJ=nWAM{EIUrSuSt#)dGEa9lP31nc)f{&NAXl{e)k|FmQ(JJb zj4u~JpMoihH&I#<&OqlR8HwJ-KER{qLYK^F(Gkf~$$2CuE-43p7-3OwCx7P^#eP0! z04|r#e>=lBd(f*#=YIb$3kCRU}G4OeiQo6AH$_%C07IY$~h5ABN2)(ahNf`hIwfDCUu2 zX0zF1ViNag|K05r>HXWPfU1bS54jFb18V zfq!poGL8!&g>zfHmk1}c&D1k|*%Lov%y&d)?@8=s5PY2^ zQXv1a&2PWpn~;6fPcIZdpy0ehyQ}H0ldx^w?~TG&qlT%pUM}pS2#o|=cOcbYC$+}- zvNM9YT0|_8@05dkEJL1$EgtAC&Y+c4UT#w(5JK!;;qE09iOPrYs7aH!8}jtc;j0W` z2A8O54rk-9i)N?NAViADHW=cZ zC`s#QYA!zbpTcAwa6Oz4<~v_fKrn*E@^iQ{1{~DRcpt^=St(cuEI(YKM1DYw6~!Jm zVT#ZdzS}8Pcms2o>>Gw4KGQtuT+zPm03}R?`agV(I&;={10fn51{@=F>adcrl=u=r zqnUkVc7j&*24KYTZp6yz_R+N_M2|Eez7JUkC?khw(?R{??Gvgxy%|ZI_i06j36DO* zLy@Fe2B9aZ#HvH$j4LL-0oI%hr66)$jR2G!Gm}XDlOomynMqx{?cY8!#DMqK8A5Dx z9}?%Tlum84GhenzHFf~^ii*=0!HkbWm_D4}X5=|<&7$#M?zff)J$<1sO8|bCqUb~Z zEeXJHN*E{iv)Mwx3*52Rvo@)-@is6-{v>2=E?`CQa3PJ#(H8VNo-}g800u9~U}YFA zDJ)$sf@=$ zc@@8<=6Y>3Z0WzSrNK=^f{@{_RL8(3h`4>2AKlpU6>dN>15I}D^aE0WA&e>r%d@e! zPkvMdTjdE)(>5u~&nm8?X9BTIKw0@7Gk6TP0jMf}2)IR#K|r-IIn^PJ(T1c0v~AQA zx*LD1$LFhu80X2-4cg}w`lxo+2PR4p-ELewF({;|>cb8l2$ZmHi~wv>w+>d{Lu4}k z2y5+K-3-^f6ziRHTL5p>FV21RA9I0w+SoRd#w5?XaF^-qd*o!5N_l}HPC9mneD~uG zx^YrPY7VoEjcIe3f3$Ioqyo_K%b3eTvt&Y@V$?d*sHFP)BrcTbkodvbOKhQ}Krg$0 z+wC6wOzDaR<-FXGa!&~t@EqOEwx4WtNCr1WiBfLQnK`a^qC|TJCwiLhGqgbu4%8OF z1FnCv;*IRjbuU#ad-cLE>P#{YmYf~jr;8Di&)wM5Mfn2SRWuPRVKALw{zxEc zPvLeGvyx+<=#0RTS;iQrR;XZ6Wo%Q)#uEG@E$K2nm8KP2JyU-%>7?5wHr~fr^W*Ut z+XXaYlr{&5O2YfH*MlC9LMsj2n=6tP!PL(YV0N?`$dcU(7T5qY3G0E9d_=vhn*^Pz zIGBmM!U|)O`ItZWoEQM=;nL=l2acdbyF<2GKu&LiRzn*5nO*grPK2CX1_Q0haEOlK zv8;73EeC@f*kl64obT4&G&5Qj7UpV)p9F(mUBQH7Iw)Vso|-wUP0Savu|~jQ!&@X; zDk`U@8g7tJs_22dI)KheO@G0VE!I>bh-{qJcIWs0p57kknE}Svgmvw)`PvmZqC)h} z`DNzG%l`5wg-XPBVy!lO6+pR<6K9uKZOe;7)iUff!_Ob|`?qmrDC-VV)O8&18@i9P zp@oA9aGkb-eP?iT$19g41U~^ThZhBwC-%ptLxRi|q8|WRc$9KoADSB-3M3;c7Cil_ z(A9Id7!wCbc4<{s>~Erl{m*F5ILo3DBEN$b{T>#(Gj$Y71um`ZI?Z!h_pU@(9X1BT zS`3NURSdaHK(EY_+vsK=nF{`s+E#hSCaK_@C90APXw(eu-^C|tc8__+Y6sCE&A0T6 z6D4Q*41#Hnwq?P3cvuyoQ=zny<(5tZ;qWcnN=lS|;SHT^&uHYUfttMjqXbng*ldV; zZfqX~wpVURlqI*I&G+|>!o&Y18K_B4CcZPs$K%0KQ$ej6WkfNXx{*hpDabp~k^k?{;1OvM7%d!J8c!mEvBp+R7^H z?Q%CdVV(39>iZ9+8~Q_Q*91l_pA=n=O$k?1yQl&NZz^H09Pau4Sl_OgPspaFwBz&V zF2tx=FzB$ohE)~7l3%aYXuA~G?Z#}i_99t;(=)u;?NMv|B6yCtI`1T|G*l*5cXYva z(RN&-1mV^oRE&47Z*q%~M`F7m7igu|Z-FMQ?jNG-1&hw;#*x1*#m4o+oyB!wBp~xb zvZ9qe(_VH46XOxVR({k@yroaco!iNY?#q#fcX){A7vyH5d;|Q}fHQg6ss5{(sgPE* zNb<8eFejwPRW`E9h5hPDVlenljU}ANxtA%GQ+szB5#rnRCDlI)I1!p08|AYnk=(nqa}9`v5^I3JYvd)iTt-i9kVk-VclQ8WVHR%KU-W8_}@d#r1POAtzt zM{}85K{}qBq7c#4+&YKdh-)PehJ<9E0+sjy-dF4S?*BslN42Y!Fc_EsCUM5@*hu<$ zNYanf#sTz905n>|G50xhsY0Sg*}B;-?5VoHC|ZOIp8B()|7ZzG8+4Y3B&*#|ld2=0kqIo*xr za;jledG;k5CJeWq|5kHyJU@eQ;yt+nA4Qd_Tom4as);sbOZ6hlHd^5dO%Y*1EHW$Cku#&cCw+*@=E=O^loBI0bIteZr}FQW$TWwvBWgo8tTY zFKVeT9BxH(_nF3p6$MW!WmvLLz&t4WbjhIuK5o`HjrB2kC&^~;TlSV@^Kv-OM%oV5fFX*CQb*2c^1S5p!IEMhF2Y*BK!`FDUR@#4&XGZ?d+| z&bFh#NAcSv^E^}6Q=lt0@xexK(SsN2S4(Q861vf|yDqx$*lI7+0XgA)rmfDC|C_J2 z2;VH1^I7kAhhC_?77BLQqxQLHs)I^56&9OdN^J4EhHK~h|KiWBMU)RYToN9OISAT_ z(IBwIn3{BEYn*Vfp49GCvSHVzw4tzgN;DV#8zxbhfA0eAHBMEI45ikjyh_V z2v{H=41?e2KU~9WPqqU+3dlHG#v@On!dYv$vjKs^&CTXUZP{~UtZYT4Ls4p*7$A?7 z1py2z;r0wNN~_RbEuFXCs!_Oszf&DdI#;}FN`c#sR41QSk9w;iDR8S(-s)K99fZNwi#z~f6U{N{>a$ECCi(ta?wW%`FsP#e5;K@;Bfx@Z^==uO06fK zfmw^f;y7m~PRo0TXVuwRlFX1(n<{HNvT+>ETj)`1ew3IPct^JX> z?D>!}-k9k0y4ks=(l=?}v7F0Mcef@E-YN^)9kIv=@4RmIRRorV`fT3h;B5Omx)~_6 zgNR{E1q+H^!tNOwqleP{P?a_gZm^|dWp1)Qt_3#gg0pWtuwHH4#$`S$36F zzoS^f9IUXi2%Jx;x~%`iEs{W)xt&e4R!)S+u`AKTU>|w~_1_fbGfuZBg)13fc(<~o zh2$6Je*ySA;92>o@f2c6de_YR=m^Jc3D(s8mW8&wyO*0Z3T5KLhtPifu|ggm#9 zQi?FF2o)r(3sutiHE7N$AcREi3ELMuQamwg5nZ~PD>g5DJLW@I0v5qr#@CVgzkOZHX)SN$E;izx*SB~<`W$5tI2HKJ9S(3rq34RQNfay$INyg;a4N;YU_ zaW)k#NrL<}cRc8nEo@1;b-*oQi;Dc^ox`x9vG4;^exIzwX>Ix`?E63S=bHIvG5Z7$ zABAmn@KIDypiCw~0rA3eo=s+DwhFNx6+kKY(xo;d*h#8&d14vF-D+gcL?}saC1+2) zrdffH`1hiL$Oz3`8|a=-Rd|E<6i==(61egm`G*h}=f^R&LgHk~7?Q#FUo2QY5p-+8 z9?kWZYPe+ed}vu-o%%@91~hdH)Z~b8v^(+Ri?62h2O0o7EKttUib>m4maXU#(f}+0 zLegZHzYUbygFV!Ty}(9soC7;dG@tm_bU3V&Fez@sPR`fHO$^_yD=iLyCV>24hcm9W znMv*~r%t-49jwg3Gq&*gLx=%&IN__{y-gna{c72?rfNV@U^Oh`-Gzu@sgie{g+IKT zcEi2&qk@~tw|2U%(XK%M8!vl9ce$8ftz>xY|$7*@dRL$^?x!S26iMP$LS=@`~ z*^r3^Hi6fkdI!q+SN02DRRUz!O4>hg_2C7i8zY2W=K_V7IRc!WWpbUMK68`yEHPw= zD1u@00)8FgR`${}Rw%Q*AK92$rJUo|l6%O;NEMgfm+oxserw|%1E$F&`0#kzJ9dU+ z-xG)GI}n22U=eKK$lj&l-26pVyIE8^^>1ymq!;2GXI+065s-mOZ`TIje3e`*PDXua z^|>w!RjDW}c-?zIHne;06+7`0kKCuS^=7bC-P~ZD{vw&LR^?3<5?ITcz@U#fp1Rj7L43C`E zRA-){PRyGRONpJ4M;t@$w1CW7b|hiV^#jB0<-QM6WQvU$DgDP;SqQnyh^u5o$;Go0 zby@+y%NeWLhZCYaI)kpHX81GA!AThwDsVQo+}CEKwyg&*s-kX& z5h`XSFU{{$XSvxjWv1bN32HM!k&dq)isW|^DxBEV$K+MD~-KxdiDI)r_^+xfoV zJeTngrIa70!e-Q3qM+A+3EDG5i7rVzeA!)uf&q4wgYi|45nM&KB^MSu^Z^ur#Q$_k zU@qIjigEVeR>7ARTx2w;YgO%ce&HMn}k2-0p%p81@D)w=DC=YEkBh) zBq{mw$v@o}2^y;VT6kcNkDCF9JCss`57H^E4}e!WLomPBp`In+oWq86S_%*^v4&HW z84e-@zTG`FmuQI{j5K$~nC1#|tII*RCcF$gthOx$SQqJSbWN4tynKZM$*|1ncJkE* zLlJD?SFZ2UG}vR6IwWOq&X}|+sD&$ev(*OHsw{JOGC7h(S(Sle%GG2VN8 zhV=}To-%Bcx1M{Pv1Hp<{Tfb)2?E#aU&@;mQ>YIC-BWjGY1*`gIK+2`TXTv$(`G<@ zYan#PImLSgzRcEzulVg7;V)2nTFr#SL`^;}m$m9EO}$9ZQfWv3M zzsWh&Tm*EIbU?BZ*Ltv`c%CjCJ1RLG))^_DgQO~C#IIuNI#&B;I8fhAc38H_gX*AA z0xq^~%vEEHhDU^7HCEt{>eyBZ@OpXEL(c zu>8m{%@JAAT{rvnPTJWYEd*qdL4{M^1h?3YetQDB^PW}r25qNvKk?wJJ~{2`vdu?u z-(><}N@c6{po>`P@q07}5-FMKLQaqd#AU5)XBJrStFILJu83iI0g-f6tZ8n=D*ufn_^vq_m(TydEqs={d=K>2>dU&SaZf_% zC;w{>I5zvwA1T4L_W_CGg?|{7p}Qa6Py7W*mUAaP($Pm@dfY6&h<*!)Dod zs-~Y69P>zoXK9?(Rrj9D+9iH(@Q&~d9A8kzFDrK2%Kw-gKO~(OBr9E~DXm8HDgMiC z(Km2I+i`!CyJ_5*F#yttsZU z;f47OK=_8ikuGslr6L1j-+z79sIB^4{US04+K}FW2Juj!7D&lSX1EgERxIm$mG1-& zGyD$Cw$5K5f>lhQWoEteFkG?bZNJDaJs5JuHWSS_*69+|^dohI1DNjkDRbj2enn_?L6VuohO2{H#%w5_T?1*qFPp z;*a0Ghb#N`RWLFGq743Y6wU)$?iXGi(+5yv08hi+twA=UNHP?xZ7BTbcNRz#YB5X# z7*H4KV?og<0Ir#IztmUA4ku$cAnIN|)6CqH3`-uR6B$Cz6PdvIl9kRZT4JpPr56oX z*P~WrtLh|KU|CuSCg8G+HCq?50S6{PkDY>V5fRV0ZGqj2baulK2-6`xKDHz23t-a5 z2bc?a${b8pObD18)oE{W)=cPgQu+N$^uve&ZowB0b>6`_b}lh@^$4g8a)9}DX(oG_ zt%Ls{cmtBlDT%ZZ(35q4qvY2Od)yjsb$-NLs>QuZ!oMzmjm2w)97Th#0WR{9xPa!#@_s#49Sy}++&J641@iv3yiQ-V z4+QYB@aIr|@)Y6S%Et%Dat1TWi^S_RRSXX>2{0L2R~Oa#0FXN@&IJ%<C1qQ3X+(_zrdKX{QbeD`5xqcW49%_ir!NZ_BklDcF3-Zx%mng1-m2o#(}^;#+Q4x^n-8BS;;nmh&vO`>cBF!Y@~5v2=>dl#ok7rkPL6~LU?e6lhIlAQbCk^fr`o3qe(Qob3EieRv+CfUl(s=h zJg!-Y8!5Uj;wNZcNEAQd0EV|^mKX5!;Qvglbd^4dGnt4$u6SZ}F&1&<&(s`u@6F@S z?sFQ+dkQ;r5=-^5JRq6VF#Z(e`+*Q}QS zp_%PnoGNbSSF%mett=A z@y5b40sHc$p;v20(OpcK;~Zd6e{Nr-@-M{VL9seGw-TS>77=@@^mWoTWVSO0(&o4SN92E0wkh|0Ii&>yYXFn6~Az2iL`r$0f%)>~#i;-fa$a=@@Lm#6h@ru`*tv zRFO(dHv5IuW(19u}4D`?a0qFZ4>7TVl!UfP@ z<{QKKN8y8PUq$B8F1kLin}jL_xqD>f?Jn{A49R4WRSGrUz#m*%Nwmw#0?Sg42_noW z<5iZ=y(?kj$jB4{)oVp|-*}%W4U#5cxCfRLe@)?i42U(0@PazeA)*w;cFX=w9({h2 zDxaNwdm_gYAHIRwyt2!%H=i@97|^(=NS55TD0S)(Yjn3R{_}@Ebrn^`&Wir(!QxGh z&B-jz1P|{GcQ%LhJhq#nT?|Ei$~`BcrsZ$d$vK_`h&FF{9`Z%@L#52w<8I?uCE!BTFp;_^@?E{_q0_?hq)6tCs>fJCe|s zhao&woQ6NiCo~T1B;iazIvP5zmNm(=5c7dTR2xHpmC4M2mA?5&rm)bi^8c_`*R*gZ z{#Zm%HCR>DUh|eP;2ulAXmCTYzXh9+G?}pZtYGZL#M`~jkv>u)=27%$)NoyiIFeDP z0tFtG_Wo1f@?8(yD&L&)C7xdFHMy7`I&~;T;$w-gT~qm?fD8%>Eyar9TKNnz=>**S z2rs2MQQrE1GuB8jI++KJIydL+sOo!_@8v;^t8xCWiXB}g*JOyELDi(ZKvx~O4l zjkcNq9os^)SBVg1SbFhdzreH8W}KW@8gIE{E86gWPmk$x7Q_hyuZn(W}oONVKPM(zMG_bbZ~ zF}_{T&75H@@heG#{p?>1Bm65BfOLG#ePT(2m!2~nrX3$DP)vL?{#M`$=1GB9e|f*K5R+L--;4{4 zzzFv|>%q4F#LU65esBpXy{{A8ErWZfgQIg$vCP?I@bmEW-Sd5^>#T)8m>ogVF))AQ zwN57_E}SQU zmY@sf-WkklqZUAFV%F=x(Kubtetrd0yN~Ui7P^w)G)XRHb_PgP<=Z;BcV=4ON(MdD zTw+d;nAs8F-KIIn$b1>YS{8Z`7>KQ{0sZcV6jz5Tk1GcF7s9ShqIq?k|9lHL+n?&2 z$h-72f4Jk3msF92T?&%YCNeZLO5lHlHg+Nkp?1v8Qj`k!JU5{;q@#AR$J{9bq*-0* z;xB{!q~7Ioz#89YvcDvkunW4WS#Gb$?u9#e&pFoc76*bOxB4`fW&b4e0~Jt03(40b zv7d{_!_PZq@jR)q#+5z@Wt`97)ko6<)^TItzlVLDwDVRuGz@b+4z$?St(hOHAR9!m zl?L_A6j74NsjOKv6h!zb$y=X}q?Llme2N&7++2BJlu3@*gYu@rW%3JYVNXC~JhJ|$ zC#a|I8`16wSd3kGsueX^Twe#0>>}m4>|?&F@C^e%dy)u|CIf29`;5%N9L3NN{;1^> zqRWz#covGsFeu~mXi`@ib8%9ra{WDK>k2B zD?X%AT?(OR6n>jOeDSNAqOZ5Av(KYlQX z$50BWHVp3s|caU^BEOTpY<@rRc% z2$|HDdvL{gmQ?3nWcu}H%SH>jy~(-0Dr>Pu^zgOP)Fv=+0)HiA1lg78-3UD@ zoDObAxt>9Q?Ba&kEmPOz>X>l5ba%uJy)N^MTB8cTF)-pbtsU1iKhQc^xZ9GT#`kX@ zbg~*j2;H}v$c^$MEc`s|e>&I`E!-V$5mWry(wz(pWh`p#sLUQNsP5x@qYVLTKmP1cA$j4x9xyD&DR$ zF^+K)E#{Y`r10RYWkRL6Q?PvdLFOImv4D7rlJ3fFuE^42M{$E8-x(-=W6XOVG05vo znU}x4ENtM*xq_Wv@`*jL9fApx=8u6}D$=psk~ZUE>GHQf?{n=fz<^^dxoF1ndGjS5 z+%w7invfVNV^&1K#E$EqQ-4_SQDZMK%jqY(0}RFrmaOxJfbT$s?(>MVWk;8YAg&KKS`ZW;!}u zKb_4ss|O{rQCR?2*5kM%;VLEA&*AR`9BH%&O(2*MnvC_G07OOkDn$hSjBjFxn*GA& z5~nqY)MxXflnVh2R=3u;)9;J*p%|dRJv`MJnP={HZhTed*55rLJx8cBIzxEB8D``*CrU!jJ8?!@^vh)=#Np7L7vB5GPK=dKKYNWV2;pCA7(67gB zTE1Q;ML5?h7asW3_8v6wBECy_`2)17E<92wxxB`<>Zj*(7lYDPKr%T&ZJXu2-pz1g z(`yEiGKsWI*?jE;!&zidMz*W9_U+}c>7J)6CUB7bYwGasjHfLjiPOAcyULW&(3#W- z>F?QJ{Lv9BEy~n$keHI1hg>!Io^(kEI$T7PWYme)rHay)EcVANMHqO=q=HrnJGBalZ9)|Ew=-DGj8}aZsKyWhJE>TqyAaS^I55Mi@WDep6Jw4K{454etnWEoEE4qN&l=(-IH-*@9bcmTddBnLdrg8RnOtj zTuiVq$1t2x$b0N%U&OnBIVy6wTE=-S3WX z6cU=xqiwDNZAyqZP|;7zdRXz)%&c}?V_CEt0$rrA8a^2RKqk^fT~6p>^QuawOOOOE zSA7R!SSm;6D@%a)=G=ot2DXF$f`zO`Br9@{sj_tB6IN9S+#nT*Nq{CerHaYBX&0Fk z^#BiB5d(^UiM!&EQMI&4)&Izr_2dnDq42fJyhI>G#fOj} z$&JvFw+!MR!4D^q>ltACb1Ym(OS03!q$@XN`#AO+pH4*BrHFptT{EuFER*~4S#B)7 zQ*pF*6gH_wUvr-KP)sPq&|{cE>nA@uDnsjitf)H|FRd1$1ZShE{mV8jE%~F&5tqz~ z35%F9=5q|z1kM5A;4mM*HZO}Hx6@@fF;*rQ?c?^i^`Wt8_2sX<1CYUZW=?xAc#7`X zATO3C?yvvn`=Te1wJyGq@af~cZuT%avUQh$8h)J4WpvTR_^6Up{H}V^zoZHMJ^?B; zA~&)?;Twz&b@F zCf6H6B3GRl7Vo84qzALKU(eNLT`dz_ zMtG|bg9_3EsoQ-MZ|Bxi5wM*{RT>mwQ#~HASSPr=!n&NMq~pqDq)+^C8W1e-qDl>5 z@q(5duP+)v6c?zQ#CL_TzLVBR^G-#rd7TxM#vG+pMbBFyI&clBW^7msq+UQ9iS;wW zGG}8KRH>hGKY&?^B}~*}F{L$Pm3ICJl+(bO3GI>r)WZ8lD+0WDCpLw&wno32saOyY z{WCp6e13$1q7fQTkae+_4lPcDJuG*O8Y7}>UbUr)G&TE$XeU@GjG9${d=t|Yz5k1X zSw_6QtKm=1oVNx?G%pPO{Nw-1X!!f&Y{X zCPn)E|NI$Q`D!0%)M?J4`+RoVxUHD%r%)4sJVmlKRZ3~fK2kxi1`m%DA2oN91kv_+`WAjQLKR{Cm}_GeH2tiO`$dfX`ux*D6J$C&XO$xwgCV{bI4|VE)lT|n%(9l+vQM5yw7gT3koqr#u3&b zaOei3vX_Hk@qw0qT$aeh!My+(5t-1y*X+K$Z_X3k`yvri`#mblVhr+-hCK zf-S1W5OHC(;Xw7j%Z(w9^TfV=%%u-M(IHyN?%B+y`}j%W;I1zc*8k0CKW>BwKAEDd z+I$OLwoNL;bSMbv?DKzfbXy8y!9Bz~9=l$soa$XY%@Z4#*4%}FBUmD# z^z!nT{2kt*ucxS z42UG~z*gl`s{Rv-AmR1gjYR?~=x61j*&M z`yKmN;Bga}F<;_YT5j1z_Z6Ylpa|{ugQ{{EvVaqn zP5vi{f>f7Rxewx2N7JGo8ECI$xT>Z;4nQ`OIox`wPgQnE5V2+$z~&kB*(26i5P&vN ze=&CG9o_fod_-hUKz{$1ALQnLLr7O=15?^)IMsab*LxwI#Qz5_*S$Vxo=7fGXT%p| zJ`sRCoDZ=0K)&bK&ALE=>N?W$CgRx)(56Eg$F;6C}GpsGqp!UK< zyAEVr6o*rTtQ|vi>~n;xX1%SJ=0NzTH*izAkFhZCgV3KaCL))V+ei*#8PX;%ZroSl zgF=P!D`%XhfhuW7b%fTaDg0bMkhv|4W&6Rm@P&HnFym}}djsw2#Ojh#v(J6EAI!;T zlVqqWA=8X^7#UZheIUjxPq@1$AMAXnE<)Zp`B}IJb+UQu;ak%5h8B#v@T2=qy;_kQ zChB=}9Pwsyln(=he`hay!YNpkD{X=FlkhSrE>y#)K!fR3IoJJLV5q-BzvxEXKHWWX z)2PRKlxpRRlw>fer{6GoLKztsi{9NFmQ4fIczNB;t0QZhm8ZLOSx56?Z8YNiK{ko9 zg)e~hYrbTK?NaduElzWncH0PWXON-IvYf_LqLukJiLK!U);5uU4A5hSSZ}bj`bAT9 z!-?_XJWe65HRhjnbhVrtKGIQpu{;xkIEY$PYF7vBSop1`PsODCG^T_c{o^6MrG@I; zDJLC3hA*8%={f#dl_*Auw7xp_;?fE44aA~M4A)OC8rkkG3?_nW#267cynZE_<9 z>7(FNCp8Up60eI;ylwurO?p*bVv2_eqB(n}u--*h+*I3Tmue+~+h5N6i%5Bd4~tqQ zI}F|l6jDjR*=cL0OVFtw^dW$q3aIy02NKyIq;zN1IQzicYCm^q7j6upuDAKfCmVL= zpQUM5xB@p~M-1JbW{#Gah#Px}JF3y6f-t>nXSLQ;(V`ZNLoYeW&3q57OBET2k zSXiW62=L{;Uu4$E#ztl0t$8;A&d4FQUnxl5F(5WYaNgXow2bPnR$;sOJdJNMRjcCU zJVk)ugo2!IDmUZy{giQu_(Z07DA?c!1aX&s|DH*yxThB)su)-1p-DR3FhaO!7#u3) zd{p-^xtdy&^9C>zEem)M3fNNm*4luWaNA^5;HD!k$(j>v_KSM+Ht91At)K{*v&3~# zb*rLdZkIVw)`+unR??rPz?y00 zpgW_lp!=6xBZ#Y3GD+dA1y&|1a&T$*Ym{D}+&6?aGB9W|bV|luZGuA}ML0`h_Zo~l zft|kz50bn-v?&rEAz+%O;aivHFu{{>`xD=%z$x37S3-`RctaVAa(>(nJ4||IikFKg z%yp zzk{LZ)dDNZELr%cUh%i6ogHOOY@8pz<<*hs3q8B*S1buq!I1 ztfdh#Ld0PR?e~q)P}D%&0oAdUGiyjy`dZ-FqEaw&{|d+;=zwa1 zLO(<2K%pr4*h)-64mygB6Z;cEKGSvWr;``_zVGdh1n>F9Kf&cC!eqL7g#sGYg}n8_ zX9M(k;S6BnOH7CXMdHH0FwXoyHAvgs+R0jrE1~u<5a613Tvw(~n>65;7Dw0<@UwD8 zQgi~U7BAa)*%zFC^meHr02^5p6Q#5{p1{8&)@kBF+c*x(&|*i}#!GwvuaSOZpI5sX z5JZvl?@LES=??bc2q6eXVXPc7K=1V@fWPx1?mX?E91Yy|qX2BD{7}plGrxAt#!VMj(gK2VlYn1qAJN@#M-TL!ROEmx%eG+I zA(_YLE^4wto3Y|T6Zd7!8J(85T8BI!^eGL|jr{S+r75U~3Ty}s4#lG`x(3*ZF#t6- z{+01?u=9QR)s$TH_HUb`#6g$C}04d9# zm;h5+DhvSlJe`jF!|#YmP0RtzIskylG5inqle@q}=;vWb6xkGZN;yHFz`~(jE&90s!dAtLKXHd2{>MAt7gWq zumH{fG|8-Xg>hcif%Vd#ch=L9=<@2m(D%$Z)X7p~5TIM`C`oS645L@r7O8O()(gk#t(GDq1KWWYwY~3 z9RYuE@=*+JwFp&q3vm&HBF(S*^m{U^#I5-@b!c}~{^9ZUv5J)3!1IQjErc1$By0S8 zHV8;M#PM5QJ^l;1$_@aTcZ^kPCwdu%C2vwO*iuZk*M6V#$WYBFHpgorYqQBej5yDt z%^1^6uspf7?xQez2WJH3rlq(Cx>jcN=Mxc|Z#P|m`>B3A_~9j50NrDNY9?;q0;YX0 zp6js*BD2`>;0mYnX4cas3IW;!3P}}do>VIrKYkzkgAuZ9@pypy*%eF+M%yGPt+@^Q z*&s`oN;HxW_YE{{0I<*YvglVLDkkQM$_}_ZU-MLM%hAIQ z-i_`L=31ngU$Gd7gcZL$A2DotXLD9l>4E0&c79Zq01n+J0ig3jv=5xa$5@S%B6>+E zNej0QBBelFN0=R$y%7ia`$9bDUsBi5+&GH}*1V6h1y7v!U6hCJrh-sh{po{{T!(BT zB7a7pGw?AqBAaMiDh|f?2xO8mrwqnIaXb{cy)-)3+lm~P+Y0G1kk9soGGtERjH3;d z;W(>n42;s%1Ty`L;xv3+?Eva0{R?k5qUYAI^}azOi$4J|d)79<~|vA|@wf zg=_q-^rbbeV{eh6Kzr2YS*O9oGAs&itEcl@+1;EBNcDkAKdM2ypad@lB!9vzTlj)u z1sN(8jmtn0kYrGW5P^C#rHTK4n0ZW|{U-g9G=W8~Fj4T!j+EBu_sq<NK`-e zb4ZP}6MH;l6uM4cX!NR_U0>hTy_^`!eitEJq@R{qD3I zEInU1*4NyKo^~q))>3Y0i%YYJgKK{~Z?$gD;v6S~vuIQ5ZJN!Ca+nga|qDfS9vVmBvGY z)Ekm=G~ua}wNymH8A*pWoLHZW2J=MYNZ*#62$x3WF@vkDGc;8Sh}8;prmA&(K3Iak z>u0QXmZE;x5stC7ApT-)7cA%CD;;2P(%g4@d|6d4kx%mjROyT0z7ZUi11ldChjYX! zB;ST{*1Fu$0wp&@Ety|bs2G5Po?qfDpp>P+9&=QSj2Z;^_0x3ptZ}pwEr9A3F{M9F zg`=&j*wrO>^LhyMhf(o1b6!#oT52JjtQnHd3;^bYuhR_i&^!S8o5t%c-flN%k!Y^&*t0$*LzJ~WV%7kS zvKT70Q;c8N=9_E#XrtMdAOVDp+-uwYmKKDt;9CqHk`)^8?lz}dBly%w19c)oDj^OI z{DH(R<=-x4!PzhWJ+34%pb8B&2Na(Q6a%=l$|SWM!}OM|h4jjIoTxiSMt=27Jr1E9 zs3VOG@$vlbLqX?gM<`mfB~%7Q&^#2)jL*)slg|)S!EVidca0tYwf++NLc);m*^$(j z549M6P>sevfQ}->h%h~=JFrYV7-(2vQn={xt24<}8&u!j^+t=9-eF<%gr-_c*xZC? z)ZGJk^=4M4kY8{G`(dsKgNnO!SF%PEcV>;Xw|lLjcl>>>>o<9YLod`)OdLSPAB%IZ zD3T`u+0z)-4Q^S zl(IAzn7XBK)Ef^AKtV?^HMv8Xi%3Wd`z=z2kN173G#_hZ{>f_PWZmTNjQAXd?h2NkF)USOT`*q9PVve7Wo;I;c5|RHH`c0VA^SM=oT zxcki5xGaZ#@pGE#lXl#{k=-hJ>WAEyB_V!R`m6DqsM8-#;}lbk*FDr>EW!Zn;4Xa+ zf)gx_5_+>sKV{C-FRpxum1Qsu4zpWmxQ*o~N|4#rGilHeNJ&1FZJ1ekpP)HSlrgN_ zP>n6*@3@?Rto$;b=Vd&|dx|ev2FXo>A);PQ!2y^~aKx9b`@05%RhEAjVL{9qv$OAYryi0SM5Sk|_dUmX}Ws*`@A5)bJZ@$bA$Uo>P5P-T zpAh}4F*#Mi2jH@=Xv#Ii0i}m@t@!6Tab}a_Je#lwde*4t#?W~xgFbu6GQk8g0RVtg zmmf0#rq@gsFkl`4@spi^8l2Ly7bxL^dC!zz3t)uQ`}Zohl+Vu_uPuX?m*v{Mp(WDS z7<|t%Q}p72-e`RCOEVrpp2``-Su+)Z(KFfp6|cKmf%PBQT6c8v3H6pSIA7>}LX#NrAH?vXmwXoluS z;a1X5i%I>Mq$^^SX-*m>@YE{(Vl~a+55i%qP1t|f{b(!Jw^-gvsq@H+HALIh*GwMi zO95n9#sa!N{K2J0ec&~I1UUU(q@WE)5|VcKq{QX8hC{c8I!K8Moc5 z_9};9pA$H0);U=JY)Dk%a}kYyEqAGhqM{q|Pbib)(JM&TboTN?>w~TXiMBHogs}a% z&s7SjNdme4A*V6t^9HmbbMm=3wt2^vDuPdQ`wE)-t)mbx)=ZZSECHU4bv_m>hRE|X zueF>e5(q$XU#hxVeC1wky7j@hZ~?GxW}`jNTP>VB&U4b&98 zrG4D{e4AqiP1m|8yfzRyw(0La@gyV)_&Q-Q8*&AO23VGE;eCR6LTjgfwLTaOf8{lm z?C9w${_v@ky9CYmR@7ojCQyY_3JY2L23txWX!ponrPsuSkjZnIv zKV$m}Izp3y&s*;;R!lk<7PzNiJHG1}+Q6i=N_#FzMH6eMwqi|LpNYP!fjo;ZCjxsf zKA;&6xMlxudW3!;fk8~*jvBV_z%5d1oEI=8dqeL_n6*0`iLR-{+_=jM^)*{fS4US! z`(b+>1aPst(b2bx=H7DR4bMiacPYf9IxKuxSuVj}I8b{=<`jynfxRNW`Jl$;azW3^ zJp<)ekbnDk+F!JQuT($aF62qZjs;Ugkybq;hjM5%Ys7*IEPRozr#?aa6F8Qc4AYlL z!}45l)C^?qj}D20KTX=?0DA^t)Z?GcHc8NAuayibkZV|=c=6#6hT6FPJVzq$o<{Ybx9!>I0gN6i@vozSD zexKt)K{vox^gtIn{a&+MezOMT-gYU|$M1om#4$}YZ}ZghmX9_d`B-%MEC~aOhr_TJ zH6eqAEklSWk(aB6WfN`QAkHBa9p5RoBLgSN%F=?rJ|{C~Od)T2oFp z=p;1VQVEKscW(*#ZX7NYj8`h6L$jAhYtlFGGk|klPP@`Jj8fpovd*JIPFUS~-r6lv z@>@T>OS?B(cL?Lvd=RMz#UgaE$b_9-V@pv}Z_?Tq$lg$`s7%SG?RWqC;=ne$0RV)R2^9cN+gKEw=)tJi` zEAcN!)5X^>w#MSkddYXrKiZg0%@cx76je&J2^qAqq?f1n`G6wv%ow!w*TdZLtQn7> zRIS^T<4&4q>_W_JZbN$3BrkteHfd6jBIKfLC>*mB!t17S%L7!rNf)zU@rros8?I%g^YywUywrARqaM$= zIq31HsxOd=#gNukJ`LaDoNcX~uKcH5=Bh!LOD=DPy%jW|nBqePrWx~O9XrvUj8_?S zW-fOB9_59+B%=fv9nTXqVi~sUqyI%ubrEIIRRRZb#7~?K1Ud*NsvsdQU=`5g7rI{q zeTo|H?lauGsLR;WqEFwhMhh`3^+nL@E9e++s|MN8V0MTW{-)LHBQCRtCo<&V6Io!P zLA$p>&p%JgyrUSR4xR$MsRrO3SWPf2<6#U4_Z7XTU_bF8t6~>bSX!7pd7FGui`SXuWmSiu$@b z%RLGzfy&aq+>>1%h=CO(@U-)XpF^!TR49I<;?ZIPnt-%5|Cpm+dX^By;X8_Q@@in7 zFtn})@+?_Tc^AVHFBs};CmajMjaJD;djV#k9GJhwHnH9}S~0;JB$ z`D6Xx+~R=C&SVui#Om*c`QIVnslK6ByY^XKhh{ZgX6?STSZ3e*gCr72`C9JHkKG7> zH{`G^8`Bz8O@NgE@h1W8_?y-sq{rTe&dsuqL9^w_iJzpiFVrKe9fbzVTy?RB zNml6G-xZAyZ)nw*6Gn?zy+p6sD&y-4$Zq6TFuIa8UsGGOhIm8e@Y-dIvY6SPW~L_SJ4h^l$P|qq^WG4Bp!_*n~3`1%cQ}W;t&Es9+>v4BxJD zMwbR1jP(XM+11QPCXl2!XLUzzP9QosR;;Lx)+v6gsMl=hdnJA-OB^TwNiP!>Wl}8f zp}9i@FEo|N*iN^u$0bRbRMYnRG5+uP|N6nTgHtHhwjU=~{vU4VWs`~s&Yqx$h+kw` zNR3ipT{``fCsa8!jklXx7~O2vH8mk=`BY5=;K5c5d1I(hx6nv}?{~>1zv5RI#LOg) z+-dz5iAk8L{JBPzaD?DbFg~_eZdTa>Mm%VsBW8dxol#y+|KLg=$sbZFY~oL&(P*T@ z4EVDy$lN_vy%9cJ0TJSwoQg)`Pi*-s)4f{p_z?Urj9M+6rb+dBRYoM1#YyL8${iMM z*&fOwsCOzPdicG*UlHAP>SQ0X65qkV|Eu+>n*hesuCKs;h*tHiLB#578rzsS}|i~&O!}#E?Xs_ zj^f$GMM%QO(ii=C2&;{i6*m!i`(SLKQ*dCAs(`tGPiRrhQ(6-$}K5I&4*WFP7t)!~U4@blf29CPTxwet2@q|7O zw(VRL8br$HvBx3&5S*P7X441Yuf=8;ica?t>lhUao$#TS$|8ktKoP|b3=BJ?N^UL$ zls0SRtexIeMB%S z0Geb{7AU|T0P&NZe?W%@0{3Z_R!bo*l8JSNty3<2ZG&55_*tKxP;i zi71NrGIIJvj-5|0B0SrWv%ZS+Ds;VT%%0S$YAU)J>5M8Hk`NNa8>LAsi(IwphNV>q zQ;{CuLz&=5zaE_Yi_1#NsBo0$kRLofnGu%)l&^dS^6)wEE&gPFuUUMns_3pxUhym!j-g;z;SpR7SUdbWg(+}ksw#K_A<*aIuqI`0&W^g@Ol`ky zaFQ=C9@_UV^;5KV2=51*?K&6~GaGn6d4~{%!!|GRed! z?5q3QQ&Y>?y4tL!Oue!AzfLJ^B0QcTH&0L;zhXm zQGANkkg>~C6|!N!FpVpW{CdN0D=C|U4q)h*N8CLu z0DOsE6$>q5)T2y&onnwUPFrb*4&?w2#~fAz9ZCnTA7>^lbJ(p}=%4U)Jz2@@;umo4Pann=MZ!@zR1d&N*qH>eokN5h7fDHyEYvEDbB8 z)cI4ne`+tCbXoH14v$ow?vk~&k{kaRy*#L$uR_WUkC?ml%<>t17mab7%%L}{Dn|V+ zZ~PZ@Lq6nD5>rA(5Lu#clS44N>0O_YK)3pG@qzSw**!19R2Jo2H8tu#Q8_9&Ysi80 zxHkFQeo_hcP^+b9LIDEdtAkL=CRAz$+PHsBnoP7@+hcviWz~gBB2M(1!|nrD@cXvA zo{S*WJSJxE3}Bi+hCd80xj~eFm}y(~1|jIjO!+=wNd$JX@9_6CHC76*(gMlFvO7<6 zd2X!X*x4^x5ewi`UU|Hb)RhQwdYdwOfYiDD($cP2Iz+9%JW_2bWAaP;YQ9lMGM`Vl zavq+X@T2Zo6ew`aqrl&~A|x$tyX0*422k1ofz7C4$&4j}dgXgq5&#+js1g7&@CGRb zAQGjCpO^qoGG~jNBME>I@^JV7eQn&5775nQW6KB@?P&fA<8{^O%ME7f+l*F%zrj$# zR$ODwtvmc_J~7hF^gC@h8F;PjHB z$%v6g<2R7oD~kS%R_HuKi7Fec2~hPf)e8gHo^^tTop7 z{l5pM*^g%f;Jlbj;~UZ#o)D8KL8=*^mciviIb2<@$jhr6<)rv8VXai8Y}lymVhh%r zXDs#v>+?o64}H*N?@le+w{+14Sx>OAw^=Uv=5_nT>#3 z_-$ki$y2uyR88T_ny|L@PEae;U9J0PVl=|p zQR&WKqNP}emf05j%DeBJi}4Ml#S{5*E~rt;BZNjd?Lndy+OIf8zM4hpF{d^I;K?D+ z5G*{dE|19LE#M3XhKSnK&XG_mFIw80X-`82>MtfzCO4ouZQrnX5KWQtwHN7JoZ$9Y z^&P@DbaAHS^XqG^@VG#PSMTxl{$QHU6O6kWDVJYI5GxtjO=S1lbQD;(8y6K%?&0p3 z#$W(W^dQd?hvXsy;ULS8Vm=fHu@uG5<(MO~oC=dexaeP7bZY@2YDf+#*-#yJCOu)o zMrH45)$8}^{7y#?p9u&Q3F>>8HkI+*>J30;*cE8KnA#B$`Kkrs<=J$DiR~W=xSFdg zNFWt+m!T>pyF^4W{0{4P74j=@hxcYH_|!BJ2+|rnm!fWbQkG_1C>%0v;lPCTp~OlY zOMzsJN~cY|Ik?idrir#7!EKmUf;!7Sm$%P^xW#6}?c(QHfNkN--NMK*M+e%WNmnDX zbo?}?Y9WpfaLzCddr@p8h;s4v$1|g?ZsTDYUayZ@=mk;fLp==(;@Y|v&rpq2`Axrg zq{Ux@H*0Y5M3CG`=`PwxDOzt+T86(HrgdhH^59`eADB2{RPVPd`xLmk!dPx2hYw7Na4wZ#@}Hbc*X2PeWzwQ&nIH_|h`h12PF9 zMN1POGXRQ{kQRUh9s%G0@vS9vq2KziKEv?^52a7~?UuH34O&60nI0i|{4)|j+ z+t^1sH4ymp40QnFPO!Om0a27N$s_3QS!> zgVb$;ULYt~e4vCc zRx%Q`)ZG(zl5!j5E4g1vwaym{l$gKs4DfAl#~uq*ggcBeB&8`?NYwmp!?~Dy>6+Z% zqn_la_EINN;m=f*nuJDzp|mKm?Mi+2cH&fLK;l&74va6G4OsWS5vTpI&$3?B3BE1E zxWCnz(&7DZi@`H=bVWmG-f87Y6`LlxM%HRQ23WDo-o{_noDmiUS0$K-UYZTZo10+W zxwc=LNi`avwMPsgeZ6i`a{8(2=dh`ka7rDn-xyQ)kp|Y6L8vO$bQqM4W+`)P;_hfr zJ{#%KbR;hf>6lJZUy?zCb5u?a&u}jX*iDm?N=lbHo}zt-OXpYS;0%6gj4(<^ug5r$ z5&#+jh!OxYq69JvAb?Y|A2R@^LsS-*#SVd>0P&|aSPXAOi3@28>!fLIMt@J+y?r{l z6o42Jdos(!CZFriWnR4t;a6}T8|t_=z%{?*U*!uq1TT*s)MQDWSNy#i8(~MxB@4{6 zm8J_0#LoLJ;8uZa$kBe!g)Jp}rO4l46X?#t0QM&S8D@-A{^);eVp#)@7PCUN0p$Zf{_LEk+iZ@MMR>6bRUUql_~%CCDgQ9D3TH zpbH4Fcjb&;!n1W;hh_)mPbC-IFR3*&6(&(6z_5hip`Dk8uZ? z;4oGMYrK9ki(_^XD=hiF?Yvf_1@a|{0k%c!{$Hn^}IeHt3RicIoN33W7ovBe^5&$w7G7RVh3$r84KoCg6 zh5%py06+lQo1Ce@5&$yUHz5o#f(KaNjQ|p8!->QI00}U|K_Edu@Nqh{0G4Au(<%Ea zd73y5gNB|<6T%lg#QAZFJu4-Dn8B#s%ic4oBnUn*}vjQ&DlwfUJ>At=*U8BWZ zwN|bIfwdPrER=}2y-DTsLoh%7%Ly^Lo~(;(zYAq^=(tB?4%#SFIYSB2a>Cd%6-Jnc z+O~^KGWkI?wZ*eR6XfbgP#oELJLlAN$E1smc_Uu{@9bq|0+Xs z164Qj`gABnsR>s{Vf_SEMj*X$LS6esjq{haZKi{JcO?gjLx1B3AtWFTqPgK)4hM#y z-w(J{ruz0>+qFb|3Uj*c!X~~TN(Heb(V>Ek9$^0a*;oI7w&9q~vpod4)&7nMq3BBa z6Puu^>$bS}eL4QD+cv<&?ps-D!*(HBDXQrLtWSY9$)36=6#LAO0$OA3M$BcSiK6Pj zW;nRoe5dfhS=2L&y;wVPOJn~~R)BiEYKD2fM7xsGKyh`>!ajE}kOKa0 zdpXR*3GH|fK;I(*?lemo2GWH&_Tyqa`RSo(of$|3<{F92g>lPjkSh&E^#*s3&}Nw} zB8n*8DX1dV1|?V<_tGVUlNIvQjm_K7j|Stu_-=5j2KfO#ymHR8yYA1Hi9& z$ew7uQM6vPo0ZeQwR)qHp>n@)?6zB_)Z6(Kiw9kG-)Dc$_!X=S{;ATOPuG(?Pn{=cB-70EEP zhISd@Oy)m~T*MpPV;)fTG%3{=Bvy-kTAq;oZyJd4$!q$T@$i1F~#Q4&EO^q0g zvK6YB_&D=!PD})n0G)>lnluBoxZ?7s`%v}I{_I+L4YyWGWizfzZ4CcA=sA6i4E}F# zw_OfRf2YieS11vnE|L9pWEJM_6coQBf_VuRw&cFlJa0G7zKwf;O{0YD?Ko@bcZ8)H z-tD9Qzewi8%$!dOh><8X@4I9ZJL|934zW6XS}y`WQ1_V-;m}rMt(vyYmFpEGaBiQR zFulX@6PpJy#(fQhVoS@+8rP!D3nmOMx>_qiqjd4^nY3|YNh@-{HB zKYu$?9YxPwxJ2-BfQ%Tl1|&Ey4FsvQ0}6B+6FjlPy-4<(o>){P#YD`f@OVJ3yzq#l zr{!!9*_x?lyLcoqOBKLYdVvHvVvcfk#}6-K&ns$&6Xv#+Emh6O)qzpMbLPOs$-ina zm&*FECl>>GBPYyA5G=M_*1Q&4*F>$!BLPhj(@-0 zV;{?lrKxsu0gI>cPwN!MK&W5VLj)O#UKNHBISQJ(AV6w2PQ>g$R?B0afJYNMi8{MK zqmXk!kx`GFuNSu6;e<7AJa&+E9uKJgDl&8~`@Q#$w-vEidaj@AIyv)Pbau{fA^ZN4 z?MNp&w)J_-QLgCP<61d*e0WdXW5G9gMA)t)ZCtN`wOMRztl8Ir+cDkNv6mw~>Gw{g zy6ZTDIW+lic)RF}VgNx0Z%Yc9A62+F=>Q6~p3P+To$YAr*{SA%;OfEXsp-7Mgq!Sa zR>gW(>L7dVyHk}7)H-KI;}7l8-mY!_osIL?WqNME4fg}PDSfF3#p3%{Ali|-SY?VP zaNWo0=^eZteDy8hOHJabGzQolhUacVed12`l6@|tB!YEvS9~0fk{+xpg%gv;S3_0- zH1Z+l1ew%v$wls7x}rTwIIX#*+`7B?x-6i=+yQM*)wvG@jh`uYwybvtTGYs-u@EcSLPU=~Snw-P_fjzr>C=AlIj+4}Z0 zd)Rnm1WJDX@Mt7T^kt4)j>6d}jX>i_E1`pds|xK(#&!^aV2Qi3b9QH-r6on{FjLsZ zk&AZctw3;XMhpmDvG$x0&?~JqppfMoF!Z|YQ~luUt|3eC0NW7>K6r8XBaesNTF77HE=xl%t9~yMT{DoRBJhwtiH^F(;S=@_ z@L5G0k#}^M%i%pQU?`u;liXUmbGi@Mrr4=$xT$3&53<$*>0(|$217PWS{_~C9ac^0 z!=GznBI~hGZ0JXmJL2Q|&54oYNn%Fsk>Yh+#R~Yu9#}cg1L0TUHs6J*6rwOupah}vDNm*XadpsPo+U!O|#P6gkR zXvXcm>&)CWMB*Dry+0H3b%+xz7SNvf8-gOffzZlcYx3?U`8}8L-Yy$j&K0|4HOcGJ zfBngEgQ^hq4bUgCAm5GHQif9i*qreg5X8@j)4l7$)KLBFgbk}l0-;~gdtt~Rvc!D` z`J=Na1zGEjt+hw}WS5!>Ds~jcrsDSL<@N4oG3#kfCzYa|>Hp=e*kFE4K)g70?M&!N zd0#6iG8v9Xas|a$9<%hFn;^?^MBSEOrGFFDNgOPp^xJXIyguysGW8^Lvqu6F+XUDZ zI7#e!mstJJML7*!eEd}Ae)|OGzktzbOXsOcGwY>bdE{4Z?Pr~7V6Xb_G{DFsbqY=D z;%M}!$ORbR;__Bk{rn#mY~K}~aLl)~kk_d_)k1jrfhx(Xlqs2nHZ5!9?r>6*8;#b; zSHHJvj6D_AcGTLxMJq&32Fs(J(YDr&S3FW#B8;TllMIgvr}Qw@w941f?}|1OgqPmN z>>5v0fpIUqG7`~M0Fye*_vDswB5qqI&xPmfZt&5WzdVyh=ZS6ibVmRyo|cE{oMtbD zhUdURvLG_)WAWy2LGY$43mr(0B?gj`2kq5BIWn4@RdlM%%Q8* z7$-)1Kb$Nk4e`=6HYG%}^Wyo++Z^U_#XpVJz!+C*;sBjF#52QFMxhHS!@dSfas&{d!}ACPZrC{-Vsw zI(tideE0>Ka=!)=|6-7CN1z@Kn{m;n+oNu4l5VTE%_45Fw?zV|15_&SEcj^su1wSj zMc_lpw949jB~XxmdWVj7?2m28_5*dSd?y5l%bvlJ;yxvjfequOfXA zO}Vg$81yPLojeS-)Zf<5s}N6Qp3V)PU=q^N2n|JttzK0>;>KmmurN z0=&Q6+f{buM&5Bq!;WExzK4UA=b$-B+&_l?Q(4#nJC?9p7N@GrFjg_`d{}3REBTnY z^7%!N$de~;+-IDC#->XT-E!kds?rNI3oQFPjU_%n_oBNZ9`i*{m-?st zZ_7Dorwdk4ub65Pk5?!;*oE2)o*RRMWzzit(WY^KFTLwAdYj^B`TZxGB%t6h@xox} z?pFJNg0iPYn=3*EtW{!P9X-_EGjRw6Ot*S~l6&vCG&4r@uG(K|)8|;M``lI~OUP!HgGO%@;J&0vjA1u( zXp?N6kjd>7{WoK~du#k-%#13GTPrdC@id-w5(EPHe%Aj05MEJK{8@luxq?ahAWkzW zCssI;Tv<~E$@;)e?>W!qgCKWnyGrkBll_NR&gkbLS(!#qcPFYlq%_|-&>$_vd8S9n zyy(GUSeYypqna;idjimO3-~oEy9PV1)Zho|8Lvxf*maFE&euIr4d1~*qqtkU;zgQ_nl1k;UQi#J$_3A6(J%a`)&#|N^85DTT9b^>fm-Y zOF)dbd{a;|`UTpQpFMQIW@@L99^06}VPX!ROrp?k_FhqmdTmikrES3WMoEy&7*6*k zHM$!jiEYs@OvO~H-FXT6)N4ed4_@75jdZ6jm#C`iPIJ9D$xW zfD?n5P~WW7u${>86+_7z)pPMN4YQnd+BW_>bKo;=>eSL1OCSSc5H#_h7kPMVT~D)J zG1}zBPCknuo)6N*sosL86bbVqJfPAei7Gf;dU>lTjwEpuo8a7U z-(R!NWS^)&(TaQi1+OQ%fTy3^&w8d^+l>>IPMJVQwDR-ZY zD*eE_7ui_*zA9GQ>MC$Q`70H}Ga`j(QD3)gImMDk1)8cI+Mxwk zU{Y~6u6HJTl>;zx^YZ>;7J8FnXcD5~jittWC=hbodYq0jTqY4QXJTt8GbS8g&K#At zD#b1b>R2~3h-5Ai4E@eX-*8wqTthMDTlU`~@$~WnNd>z5My=1hy#aWA^;D1>RvUpp zFmxK!ss`j@+7G!DfW0-s7-1s7m3J&f(eq;oB$7h=n({8^X`zNo0kQ?yw3XKGCFvCJ z+aWIxA|Q-^A}wG#&K5LaH8*C}g-E}$YTq8IwaKWt_9!l{n<|&1&n5qUW zO>KII4hDbY%~{Vv-JGJYf0*k|Am1;a#g7sTcynD& zZFc53hizVm74*iqqQE8Yurg8fIjNuov90KIi2F>4fVQzn7$=vVATBq-eJR`Agcx)( zvT4&aH1N+URxK|cMo0W1#EwW5w9u*%u{PNhU%Z=c836nolZgTfyaL_{3EMB|w3|q* zIb0)+D%C-^w_}(>I&-|-3zm>)Vo#PcDQf2s3YDHv zOBL#oX*YOu+Y=2+vDtOTg!gaXkvT!_EkLCqzMUY=1s>62KD9`E{xv5)@ zOyRa;IFEBc2>A}!L1rCZIQ*+Fm&&^KD6Uekqt|LI#w7rt67-?tJ7F?W z*hj!QAU%(c&r5P`Fk8h`YhbFwKV4TY$q$heLZ5~mr~M|@b+bvO=Wn-^*a@bMw`?zH zp>G~c(O8-_W_`ye+@mqjeIWic{qhf(?+$=xkxU2H=2MfOXoKNC!MI@)wcr=s#P&tI z5Ua(yMz**GfyH!$g+oc2rLEwl@5lxwOjpcCXrWE;{I36CPJB*X}!?D?B z&RsBde12f3l)P$c{4L6x5cbmK*YHxyGmI3Yi7Jn!b}6{8{lLS+BXxQ`dtE=arF$xiXRlf?Ev9rEh6Ro6< zp8WQQuD!8cvmK!@B|{_b&nlJ*&^n9l_$-iCc#YE@0lr=ZHDa7^!j~9WqU-z$Z(Lcy z$+SC{r_75ByT$6vofP>e{0}a4>48LuC3C@1nX9cSL^a5s5AvoEM$c(dl!~Wa6>XK_ z<8BK)>+EU)FC&PV%=qD_Z=5r9s#Z?8jwNb-;P#7u+`fU0{Q$Pkg$WVuz;H}_BF|W< zzcHrpaX^NuC3+AVlz}K@1kj7RK(%%K%r9D2kvZVNnpL4lde&TFs zuwGe2y*v(5jp@leX!WC*BYOfbLVP`)*yZ`T_PeQZ zTm7sS%fc%f`UQ#IE{r%6hu(a!FGd?l=5r~m)>$AzztQ;auha!d6;N-Yo4|=*2C$D^ zhMG=qGmmZpmdiPx5rVV5c(^o3in40T+kzn22qk#lA;;!m>~MOSW}2Uq@*wNc{6rys z+ryauu$=9Ua@M%n@7YK2#COvC`C2_|F_;_J(0BJ-fl0Nt&3S@dx>-JK1UDrH6FrX{Ii$P&GLaO+B zm)gtR(P}$foM$eJSmF%n08sN)_#K8@s^~up*EvYhM-#N(8P8bj!LDM-B4DcZdrT$1 z4id~gcKYMvgdits%HKxeaHsKc5Oz%%RS{?oLtyHh6=N~c1J8)qAVPbt**v`OD7UfP z6`#-D;FrcPD``ve+?dhrp5CFF z9c{X^J;+7v_ePEDE0#I!j*5aPNmZ%%mH%ld#fOempqh`gHTvZ~@n;DJa<(9CeOC@M z3$TR>J|nS+k=_|(zK2|t0qqW(y#*esSM%WJDm5UO%qcI<( zNJ76Y-+_5JLU1a(IMi{Gzx9RYeg?nTz~<@HT~yM!H6;cWcL+(h&cmjXRKT|vmugi9 zm6fE>r6W3)CH;l*niH9Yn%sr3C9(w~3Q^+wHb9e$!t%fc%6HCQ-Uc<>(SsL@zV%1| z)Y+b4E|^O>P+3oN&%*bh1JyJG`gxVE;gFaF)vAMQyBkh=ge_=JWr|nU2dO~8(WRWq zEXG#j8RhF1vh|^f5pyhx%rEF?krmyi_?T#4jbk_p`c@Q@kGKZWWEICIh;XbL>|un^#l!X_ z0DFtls{5qy*&>HmZgpC%ma(H@%eGD=vLra4&jBv2RT?uI!%|K_Y#m+KxVoJsN9rOT zcJUA#*q?3UeWZ5xM$IcM$QZ`HHg^ws)1p`#A&>O(j*lwX6b`;S6-Ox)jCd2Hb1<&%zrQ^1C>mbcOz+hz3mL0tn z9sdQ&vujS@OVNv+7&P+U&>)!U|9&J1;Omviai!)K3);i?ovJvdgU8KFx4PxU?-7)c z9AY7>)W|~Eg{}t;^SS1pR7UwX77L|DA5wVY4-kTFKONqI5>+XS3(WES?q4DUSrFfe zeK$Q*N~;3}g_Q3=;{qKBRrG@9Od>WN$tprgf|`mUMy6k`?CDem=S#+mpQLU1d{*cF zf?LZrQpzXEI;0|l;7>-Rf{^|X((^jNadJR1w+$%>0RjhD-bjPjaY_?E=R30pOaag4UyB z*vn8B{KFlp1sY^seb{T_s(;`D)!#Aar#7+6OO(kxb<8gz_#{LRHaOJNmZ zQji-?oQn+O{n7J89mkA9=(TYyXW2V{T|tw@Snge+i8qBtMAgYp6yjhUY57_+tEdzx zXbCioBA#I8=rgTlVfTkFP%D+T`?#Q}HrwZ&i$P4@;{*hjocDv7c1~vD@N@&#!Pu${ zcB8x*rv|j!xI~^sDS-4oD}ML#p*1FKd&ZV4v95=%;PI*(jlry34y-G;HLWShr#rc* z4s5|+Xd)24uxsKmm{tg3bE(twm)M_Z{1N5v@)60r0GwgHY=Ph~v?$StV_^TycO`*~ zI<&wyKup95uxK~J^ZNp%p!KdW3BmEHiI6`VhqK>of zGfBl)QyZ%grA1pMx`m!{zXV7?$xnTqx$Uo%O*yNck0x)MPy3GakR7|7ZX@8x!Zg`F zl*qp#v&jYJERIvr50>w9j+<6O*VES-k&$8xTRqxKR%0?8LOBc7@PzEE^Pvgd7RCRSnS!PRt0_2Ip~kFeAfL92DH zhMI&}#1_DS=*;77d5~DYP&bkyn8Cr0&mlFtr_iDp|_0q(JCsoHmhcB#GZ{DsCp!3^BUq|%SA z*(dU>zhVih?sJCv0xfq_4wr3jWx3Vv#85nC3R?4RFDW|R1MHO>@G-(0+jf zD!@W?E8%k=jL&r(N==pqyQ@dNmf|n>nF?J4fBOlZgSv z7!=S!6youjQ~L__*zXlPLbks$#;F{HNLC7H{9OgnbUzEw*$cGxH7**VY;20pwnvV} zir`C)JPSp4tvqW(+&EH_=(hU3uEL4nOn{;21$figxU86&JhPuX;OVyN1@Q%EQ~ zjz(BUcGN%!D0% zI9!=}d$$m>pV7kee2t);{L67?a@dxvJlhW~fW+|+hg8*}pBj|g62&rH3~6p|qMN)C zwBt!{bBb89ini07*LMil7id zW2-em4{kzJ{S&)fs-ZEySN6>qbOmxS z*e_WO*!YjSQ^+Q2DJ&8f0u`Uae2o$ioKkCWp}dPabKrPNH+Hgbl=_WGTYp=}#MtNf zCxNHY@POnBObajjbZwa0l}i93O)GRIl&q$+O;y*v_;q={s=+-<@5}vHZDE8+c|04R zdaE@|jJYfV=~AmyQ@Gm=?Vh9(mgG@gztyQmN?5Mvzu{a7D(D9V#PY&w(%YgF27sfD z-$1c~_dJRnJ4R>c=JDG_vT-2Dc!!U&(e|mc&8MFz_xHH@=BbH}WPl5N0bkZnN|ncB zCu(&1f`GiZ=38y+jT%qJfrvYdA^SoXPlSv4Jb(#505kTXtN3g+^%i6-P~-seuudoV z11XN-YHm)sNbaqm;5&G4w(FCNhw{bB`l9M9pdrD9VNInWKvOqStc&zI49Kf!o)FK& z_04MC?MM{TN<673GSiz4d#AUf5OQ7*YHKWZ!Z!T55d*4?@tbm$G8H#^HkY(Cx)!h_!^z_C*v0$p^4{uFy>D!G*0Qc$SxsRN6u`GUDp`)*?T%p-nQ|}w z@aKcV2^bQrQ)%w}N=^N%j1^6GtOPeQ_E}bdz7h1)j)e+U*lG^_T%uNl)dGu#%I4+P zx&}RvPj(~xPep894>N-2ac0%H;p$e6{{3^SHo8u9X333)q$y1Zs+iY?W%cthEKwi< zYc1P~1p0+vjy4ldTYRblBx@l+D!L;hREdkQIdg<-E zBT6*a`kNSxPs6*SS6y!TU-@3O_`sCyUCjxpSFZb(|VBzEGaf&_EJ${AM+B)RUQRU@RjN9 zF18&@V$pYpzWuoB&Tf9A+HekOC;$xk1~%0VZKi3d7y>TJBoCIDqGiLa6DW}U{r#Vn z7!^e?vym2WwG};KK#g5QBWBQ8a}30NdnC&E#_~g9dnqS<;6ijCk@sMDNsq7_51BX1 zg~38LC|U#S!V=8&@%|I^&Wu){-t|BW#m8x(n%LCpVOtpsgjWs2ME-u>3Ps4x=WEaX@++66KV07MS4zcB!+ zMavejCdq-|0P&Na(2JYQTdw=wP^hRsg|j45Gum@0oNMSwcN7$=*4;#(3Ad`=P5Z9p zQX|K0N)r}{>e~&&vo*-Wt)$~{ij*?-+9+$cq3p*Un^luHi$v)E6z z9Zsy$;Q^?jh#``YT=UoAOsYjsDN4Cnsi=#Z*4VItg|?*&n(#BRgS3DeQUwcNc7$9) z8|ib1G)ITnyW)|prb+4BTHGDNZ&{)Mq+oMRPdK*JfXLV)Ef{w-98P-aIXj5GvZK1r zuf~4o$w0j6DpwIYSjV)98S^UCazd0x920RMOS^264Di40qr=I`uDH!sG4T2|&HtEZ zqP^+jjcjX2a$W1Tmznpb}-N8}IYENI2G zuFM3m*G4FsT7ljCBW&h4c8Y7W|3~N62FnjXPY*2!kk44N51CcC)-;AgME!Neqbu;O zTU*Ou^k{(6#+Mut*`*_sDmGM{5tJ$K_&9x!3okI->#J-eopjL7B6h=Bozj)r2#YLD zWT-d-&xq>WvTsIm*?@AyV%ss`w+S_0NN`Dh+`LRG7)+CJe)}2-G9{Qa>6QrD$1~tG z>aM?Em)ed+>t29i^hh%WBvlgyUY+$rE^WHAK0UPl`^I>XM!j7*Tk2^?JiA<{Pj)rV z=&glO>OT0?<;1&6PQmFbJ+sfjf8WU+(wd%k9%co&uxz;s0QNgtDnr~rSP6AMLV|dPKI&DRRyrn7fI4zFOmF(rYJgR2O?!+sL7Elc^qc`zu| zniVz@%&xQcU}>B;Gf%T(Z~7Uf_z((XQWt2u1lf5z&kp;8rW)KjA^CP8Ga_j}PGWvE73SDGbu8Kr!!q5&$5f}$@!1PUryVAFZdd;UU?Ng7pprOiBK$6CTu{1S&>P9TzpyK#uJ z*EB%Af35f{Po~S@LQSe(^l^b+vKiG1-cQXfMyAO^`at${AyXfc40Y>8C&gDGAUNSy z=O=4#-yjNwb~#%kOU3bOmDCuI2*3olsVR6uQ!H9I;FbLRnL+~6a9=I=FJ1h9o*Xlf z0Myp7*r;~8lRofo2_ZYDS?qGe2LJagN=i)oNUr?nrExMU73Aat>Mvv@W6JJ9gxDm5B~SY z4<%<8$FIXmoc#H0LEXV;#o2A^t86&-;ZH#JjSM&kYr61fe&ErDTNCu(#zQ(7p`Fdoo%p8$c@X(BKb8NT$cAOn+8LlGqE|e zP)zT{)3L96Q+3|@rA>4CC6wm7a%KD-UprzG9?bEe`j#RZIEbVr7b9fYaM`yn36w8; zWElq@QCB!b2RO!<-~!M5YZQq<<-aA$q7<_CUjKl7X#nGD=DA*TX+>&Fj;kY~{>KlH z0Dj}*$kJI16t9W$(RWeo3kiHMh&yM?;QO4I^SMB9IX&M0n;nE<&1t$$20I?Mpe;$U z^}{*#6oIEv4tHq)jm{h=97+@f9b0T0$_WXWHgq2F`4Hw2sPok`5RMEy3y_=l(9>A^ z(B>ZshV@2B+=EkdlR?H72LF57g-553hWY!#tx{~~j}eF>Pnz1X+j_mUc)1^0URe&Q zvU=tgYz*G$tad5#(>3GTe9-)J6IW@3FIUy8p;&I76>eEP^t4jb;H|f+hGIzsN}~Uo zhMvMSkgkf}LG1S`-zBul16oYT=zZq0_Lv$k9F8<7;zT99n}Ro zM`@D^!nP*FWQC(F?cqs1Id2mgLhG<5UuYtRO|?d3IN(E=y*azpgih!PyVvcMEk5m& zA5@>7?WB1+6%iN)@E5h!R!w}S{f=vck0cbh;Kd}^Q;kF2=06M48EyMBj~nF@|5^zOB$mDUB5SW*W~M zCLhtrj;d9m845@Erno3~bv1^XM!V8`T@haCya~|N!neorwEG}F?lQMkb#fX1MO_CD z@FYD&{R;dP?p>IJTJSC0V?)i*%dyK{$y5c3Xu@j)K>Qwd%NK-|&}1wDP!Zsd(8)^G z!RCxEg>_tmZJeVb-DCpw==26*!n?d^BO4C}1?zN(Y$N%U{fUyQj2-ea5mkO!x3y-g zm3xy=ZagHmof4d3v&N1>ptGE~D~`Ie?Y5K${LP7QLef=%xn3ex_c_Z9H>Bjsfhs2|Q-jHcGT%ITIX zm~~BdH8y|s-L2ZAxqOv8;1q=C5Z<``a%_eu1@ z$=#l&h+!;@q<*8>Jm0Ep*eQ;M9??f}4ur#E&L!mgexDP)Z36p&8HTOLj}0;L$*x>K z;vGdFkxEl5YKI@GiQ4V0pEt%NamcR^h6~FLEk&ihF9+g9V@AOki?P>Om|h(L$OaJ% zAb$wa9QiDPduw9wNeX=B0YTNbP{s{&J{7X)A{e&2NQZsB(*e{Q8VsrwG>-qXnEjnH zIhvnv{3H{cJ*a+H03Gx^CbWiU@Lff}@^aQHRZd(?=A{#^zEetUPkh zam;8e^4#}D*m6y6)1)IQpfz)Y%HGZwD(>r87A(d++>M`M7Y(n07ke#S>B^TW0UdU< zBN@o9zusGy*%gCZN>|n?{hOt*ZD-GN#+iM^jnws^&p0?$cFpbPLjo9U9%8IV9iIPZ zv57v}ISc|4pz_ax)M~GYq7OhUya~smz89~p-9G|?tsi&9up|x~dj&iJAAbldBMx80 zmWUrN5}ch@^DP#TbD0Asc6>WYg6vkX(fF!%s`vb;IfmR3A_`PZ*}S+H?WVCKxVgr|qa#-kof z#doiT+R8ksC2}#N=6kUo&ef`zzV&$@bM9pHew4kFagM)$JPQTG9-w1S5|i?b)WYvm zxTvH6>on|d&1AsZ>TH4B2hqpHxC~B!t9bR;r>*|neY-+wo5gJR29! zIl@$MfK}Qwg>BwIqY|@y2dXXoniwVS+Yr#X`b+`97>mSa3ELg&da|bukcl%YW2G;3 zZFsoD z)Vgh4Oyk>`#&V^pWML&~Iv{`oSa2*w(`yVP*Eur36Wg^*W68T!sH`}X)k%r!sA~+t z9NmBFb{!boh=KzNan+)Q0Psr3=3-=B(VFe5r`4}DN58>3KoD>g;7osxDD1aY@jp(D zAvzIMj*=jIOD%fVv&gCbj9pBPsIaKU2hGu4rT&hw3u(?|I%EJ!;U~PRrSMGcygwqT z3i~pdh|{-6HLYV5jpkM*f_criQOg5rW}sCW7~gU+2ks7Uh9#v<*viq>i^g1;uEzdus($nIdxi6l8NLT9<`BNB3#C45 zU3FXvSW-gixfv^*pEbQ0DzgpH*+YL~wdmQiTA!pw|2&9*6qi=C2x+|^*+V2NyZnLE z@YV%+(<;T2o&QY+&Rn$L28vrG=F#V%N0|M^6s5zHYTGE&0|U{POpLkViyQ%xW1ks9mQPWWx#k3JRza0+}z0L`4}=t`gydi z!ZlcpP(JS(^uV{WOG5MAOx5xTcm3p8>juI&|6(Y{hmvK<%PSS!@2cW{#IQCc}?hBvAxzH5X9*#++Erce`e0{dvVX>Uc3NNzl}Metu_p z<%xWkdu35IcQ;xP&vd6QkKIN4w!aB92AUg0$N|h)15ZC=V})%~H540w*vPq1(szlo zZsXmLB`QQWj>^jv^DEg$1@gZBdSoe7Z`~$5e9Jbk?&C6az@etsezXNCu~t5mGmi#y zMP8}4OLHB7x1Vc$?vJrKJkozl2TQ!SaW7uRu7PXZ_gSlQG6JugMNwuEtXmh@v?Cvl zh+cdKfwQ+5ksCTjUEoNxT#75&I%*QFU5G9S{C0gZg=_{Y$OIxbA`pTYXCs|I;G50@ zVvwc3G3a}xG{kG$Uv>m3DbEwZEUeZ{hC3r|{XL{XE*!QmFu8W6VYn->eUH~10OsXg0iRM9^HEpunj&LJOOc9;% ze_K;gWp@89O|rqfh#_s~(^7Z{bU@Wc*$=afBj@j1e+pP%h9(jy$qxvsr*Pn4*YUTz ztY16LaL8kox?s3?Z*7Nb4}Y`@|NAPp@81pRzA=AVN^Yl?-Fwx;4j=wJyl2DBh_njK zZ%a}wG~%tb={j?zR1$+BXT3j5I!p4mPqlwQ5KMN9BlW@{M@c;S&h1x~hroU{e#z!s zgYdIjoRRl3Z&vdgn36*=ix|hf(Xiq(#iT!MYlSbcW*od20Y1{-)R;SR!{a>X&BwcR zVT9E1zWV&-)F^UJEs(dR$)@$&FtkVa3;KK{Ich0tA(2&*hqHH-eANlgTOkLfVnz%U zvTsT{d*uKUsul`R%ouLx6#h<(KC%H!m>S5=Y5YY?GG`lS6=%o1%&)R{Hq0l z8A5GbWo8y#ki1-mbGmEdn9GdMBB4X&#b@!Ja0))XdZ-ez1>+vmhoE{KIXEQ5 zGdHAQ6I8itAbg$Vj6J$sD{5cT!=*wM0mF&R$Ht07fF?u2Pg!EU2K*_q3vCU!x$RuF zw8e6^ctw=9N<|i8sAlQ|K5Ikhj55#kFUt&j&9;b3;*EngRaTD^&2EyYjYIVjA^o3* ztlM@$@$|H)=^&#*s5N#sVBsM+oN?-2O~TbY#k=B);*GoTUx8UctB05ZY_G7};JW<|d-0Fu^>7L-MS;qU-_f4(EU%aNl9nWcGM_3AJ^ zvPivWwLjra*|Jmo)&A<8u#FWFyBhEGYwAoa`@cN9U-!*l z`3(KGW0cDJ@F7DXio^&A`%!e{az9~ke*L<0n?8I<^0MxXEP-SLIAXb>#vE-p$i%pr ze-IinL<3s}hYrBdGYId|KJ><~kmKj`gy}4~`%XN8|6X#@8TJ@LnrY%rs@5FOa~axA zKj}z;jMIMI4zCS%gW8;>HCQX2u8(VGbaz)8ot;w>02^#*e^iD`OQC%%;5OmZGNDS& zGn9Z4UEvZIL=PI{wWr(mgn!bNHKJD7`PFf0 z>se8+VDvOov=E-(M9*XII!LR3iKEdMgvU2{_w9W?`_tWi{WB$Gd479j3#qI80ZN zukkc2k z=kC-F3QwP}W<()#h^^~@JE?#I2SqV+5hJdVV7pAK9U@<6k^j4l8bnT-)sUs?$I~f7 z3^oN6NcBe6sqH=RIWe{wpD{aYoYyR|kS4pcJiFCcuNu2~B8Se+c+!cQ9(0Waf|YHzY3D zb5j#&xjFz)*P!*aAEpX6u%RtR-%AGuYzWYa$l;KWZTC>i$$A?-O1Lc}7qk)p8UdIR z05Zx6DGMM$gDT&b09QKcijE&r5J9sb_yAdAv6kycZ3jubYLZjjhT`q1dVfuy0ga5{ z*vB!>($fZtIueI4BjYI?)QuKZ+OdD>PR+JqRj$=^@|Klm=f%xS<9Yoggm(lt#A?os zQ9!8i>djg5?uAT5iJOhvg+3^3`Dax{f?^Y|My!8@zyLPF9rIARHBJi6Xo}46SVJH; z(mJgJS?&exynh-N4JYY_^a&z{MItT=%6X_7l3k8RaUKEhZ#TgtkO%EUZJ@;o@(Dcx zzBvT|7Z=`zEzc>5J59Dfq=(Dp^fl3qJcQe`Wno%RCLk~F%TaOov%z8Zi`*B)RJ!Q} z?NbzGTT)@rmJ+DUVJvS_ne}kR8%h`li1h$R;=`GWQ>kWw>QhKUjZuw%!~bbdIh69= zrgU0x7x^XK0UZcrbW*wgDVwujNeb6R%Pu}-zKQYiB8@)*ncoylEp!sqrrLlf zN01VSOhb%gRj?dwG7?)spGs@LB_xxgCMQ5+B=J+p_S z)C_J@PFeqphtn=*X-1AaIyBj8<AMtC7liQMZfs!P5k`EV!p1i$h5yrk3161#2?uonwZpF7v}%{9 z|6B+9P0zG0oCv=WJ={SR@{2h)ipnrld1JTw=t%$Ve9eC6!(ql=E8s$=v5d>&4Po|x}SFdD|`u>UoCY@z#0rfOTaRM1~L^OBL-E!F#wX64HlS% zoMG?)e4_M{t{MTFcCHzJ{q}m2WT*1kuW(0sO88yEsGz3T`6BC97dF1%&WT*}N#{Qg zWtL##^Y&bebU3JV3N4+>Re^oq1kmM1blM^VbCK@coU0(_de6XtP&ISCzn#XBC_#=5 zwo3y(ltrV8&SH&Zhok3p?laI*26~ba!vE}fIG)j zjCiUlp4gBA2AOjq`~E2WW8=jBw&TLE^i$zdve|nLCyjGI|F#+YStWpvcCOzG7Q)<| zC(Dx5TigP-E3iWyTOjYf9NCn&Cf=k0p3TlFYSZaF7 z=@WEqir;`$H59`*R}t#P;yY%7cIR_v_3{nWK)n0Zr*bVB$bl;(0)_a~`ca}UXzorQ z1zLPVY7?f$jO_5cE(NLGs}O}aw(LxR+sQd|hy~39u$j~UL7ZN5$<|5QHP8T|`u28_ zN)IwO;?j{wB~mMdTI;Z(?bh?^7qeJzaF9h7WWgDa^i$ld_E!(xjn(y3yliX*4Ol2< zGB&MqPqP$HSSYh`o;G#eVtbc`erxZifQvk$r4l7>_`TlZLBdyIiSkPSb~KQhg;SYp zn+4YZ))LgF3VF4y2zutLL?Ib*M`PUb&eay)#pLVD(>A&w%7%R-%IC-PXD;_yCr5` z4iAgD?WgG7GdYdX-ZC9hIM%u{iiCK9X4X|Ukc5dF^-?IcP5o;3;fifnAgOjaUUWu}{h~`AUq$SZLmoKifU1 ztJN;2)Mo86xWS6_?*#-^gA@Z5<0{z?%KxAe02%?P5&$xc1u_>PL=Kg|F#xK`QWr3! zLINRWfuI2KdxXtHtsl`sSs&ISftB`h4YwYfvxd5U12aZ3aQk@MyORa6M`fRkrzR9e z!VX=SRKionqH<}};_)_CR z*H{<%2#()_{Y_2!d`g&bz*d*{eLm-TqivP|A;>r!i;&PJT_<`zTPRU;E} znvIkYwO)I~BgTXd_cjZi#l_@^h^fTYv$ynIcWQB(naf7g0UMoHspQ#uq%~%~7W3rM z%xD2f6?x8%SZ^ijVkC^^U(i{f6VC1Jp+dD_6xzNK+SDq(_(Dz$O2)6N7j{}tM6e+w z9Af;=O8l$JU*w07PONy#ObbOQaK$x|ZC()<_~GEIiJT_pPQ2ZNqm2~Y{Qm#cNQtUY z;Z0UL-S4>U^Z4!GH$s5O+`nOKBgmwS)gdRAkW||DatzBGswqoyz|UP?@uF2!uQ}E0 zpy3fG5}qFqzB%!&mRz_%Po#e_7QSp z*|%hbSXjmMI8JR1DqiPfNCP4lIQH4lo9`(A>_|g*VwX6S2U()$wZ-D=9E%~L%H#0c zMZyo!BBzCYIcQ>@s$xG{LEq5i$quUX9-v-S+o6{Z>T*Ou8 zwu9V7^=S5mS*OEm;1U2b7BU!N0)~}?%&-6~qeXxK0RY*XpBX?B05ak(ArdqJLCc?w z;1q#HiNXlczySdT5t9Qz#uKyvC|J5@H=^*q#vcT3-;y1Vt%g`N12&RyMTR&$N_6GW zZK9%P?(`g>_?dlw?xrr>uL;+y8_Y9ZFQ@S29Ih*i!$zby;okVKO?Pa*97`(3_a2bO zy5$baZAY&XiaIwU6qi_jRp&7HbIHU0HRX1ZvhXDinLCDW>T9{MH6R9O$g_l6f<1^( zVtn=Y4kGyNPE8z`7WO|?)3AC$bmp>Q67H+N41C@4w9u7e>tNbq?Wr>SsR}~p>IK_a zp6!P8dpekQhywEWYscNT&x<^ZzpJEcHbvpFM7OQ8YnLTjAtIp4^=R3ZN}8lALZlOH zUV`+r89BN2bE=5tl8H+jfzC2)a#T)_!daQ(SvFxFh7x+d>86rG(#-iAV?e(1VUal= z<7+W=ybLTN{*;hLuk|=S4$fcMAOHCV{-!x%OmlqabHAPws&J9*xN2r2Y%Qd9Bf>ND zkb2#wpXriOZM))s`Avm#oz*^wF|;Beqb&jS6_>De`~&$0dR5!r8bykzmQ-Wf>_+%u`>hOXpR@bmUi7hNM^BmtU}~t@+Th z#b>c4AvD{v8?rD~J(7JwO8@O|X0>XB{aIrcd}ZS%&{MGXUz94?Za|J9 zw091PAXJ5bO(oD0xW`K#c*v?PB12^OGh|WpTopc_ph#5)cLWp=zo(PwQm;THfl|dM+ z>we#gtB*(xQ}@9{wTdfW~6}v<+0XtyzDR?KsLiYaJFoO zvJ$SLvPfws|A|oAv(34BHrr^6SI7@LtdQL!y-j>5R8P~hWR-3eC%sY~6O1AM69Fzr zXN>zx$|FRyOI#z~kLKW~aq(HA>1tUz^xFSVSP*I z+?d_P+;9fsi#^UYH?9>Us~L+#umDpz0L*0$LVvkvkuCV6hQwD6T9~Qj=mw9671Mrq zq5xFAwuvStj~}(3VlJGL}#l7X2|TysqQdE6tQl&S`O zwF#`6r@vsbU(Z1^s)0IFxzDvvFhx=yDX4=+afz&?ud>BZvugJ+$tB$4c3i(W{Y9g* z*$mAOxzb|j?2zh8YQsD-1rs2*6$DC%^s!1BScnN#@Dnwl6|Ba1nD4?8b{KL;+Tyl> zIasNb4B2Q?CEc20`35pB?qX^A;~KD8bv;?Zd4~}Jh>4%e0TZ*%QG6zyR?8u}$)8|Zq=5IPQdE?XBk(#Ln}3@aoTjnYHL*(6>J7`>@d zv=m_NOnlrk=3~?(cKHMtAVKe|qr3JmX2-D^m=Y%7Z;2i3O!TK0AF5B&29+;nUPc&) z$~IdXqPV_)Tx?0LvuGaCc~HhMB!_GlBvk<)fL(1_6mS{)sqxcMsiwOLecsIK$M|sn z?uPN(A5%Y$tG9Mc?G79hO}0lT4|bLJxib~bNn-m)B%pc8*=$2+OlaScDBXb{w*A)X zVu#!3(apq=>fc;l3D4NnL)R*tmyV;62H*E=fn7I!x0fNW#cka%stqkA329nqtB!&p z@Ei#(>_rXJiw`(e5GZ}_9_CW=yVJVRMYPqf$g>5YclA_c8a z+FvJ6XS7J(8Kv7bj+cxNxN>TLoF(FlSDlP<0~wNRy3XCH%5IOyE9o|Y_YfQU#kiOU zw@)vDG^O&+35$>{AxkVpdkSso)v_oCV?ElH*3O|UB4*Y7FelVoAs#8x zI!xxVHE#{W5U2VA(ISS*oSC|QgQeZv)^U<~##|=VguZb+CG6NAWI5vilBFTJYzkBC z$+8iJ>=f+fWE339P#4P~wyESQPLG&XLvSECC3+>)28!ju6% z${%>PqM1xximb9wU40wsi&3J;E&B3l3RmX+0pxCFeLZDkY%t4JsFJ!Acsy-~V=9H{ zt}06OdWMOuRv1n~*EzUBOr#08(GNQ(t0ZKD(#R3kfn*uOaf?e~#5frH0=5!(sy#z! zAw8;_C`;cjQw}el195$6TI&_<0LbyS1sk0XPm_Z|S)=Q6ETd zQ*MwcuVtovqZXfcY=2&3pO+yWRZ0N&FEv@op$a<*jdVJ_$pj7QGgd^j}3=s%JF$t|a>V zeKmZv^&~&s%}c_Szg8W#DOVzQGhf8t+C=^r9MF)PyL38G1GnKW32fH>)P+@4Wx#a= zEOI&0y`>*d(;!9^>=H1Vk_n>`3Oh^rpAs_sT0l|lQi&JinJ3?! zge5ODqsj52(!f`10p;|pX~$O;Dqi>eu5yKCV&z)ujwa+}gCU$$vojuh-QA*OtPg<$ zaN@AZPo&aG?pe9=jaE%sJ8B?JW^M9yW^>ojk+2=> zjyu?}>dp?6s5st_mW5IcJ@}#uiCF;b2p6x{f)Dk(?YVCP%utTOP-4{Pi=FO$(s}pt z^JJpBK)-ApWfBLrXNzxnPYsk->)AQ|jzr;vu!zHl+&s^8+(u|>?@W5xHB36z;!Q6r zA!0?M@JY7)ubvRH&NaM;s95+SH3`t>BP?ma|KzJY0k zL*`cXqYV)9AI28y?g%YiQbjarj5?Je7DQ@eT2Zgo?2QRpSUSmUT%&S#AF z^+{B<>n_x%L(##tmkQ+VllIpCSJ4Vf;jXWqwN)e>Uy6`6=dWrF)I%ZZZ4g|scO05` zl-fURNkwo9H;>cMI5&y)_818B-U+;f7yOY^K!MY=|(r+mS6> z`j^4um4x$AU3LHNv^_%=9L9r9(EBb~Z7(`EBN0D46@C;=NYbCz)^QQyN(H9fUF=7V?N_Ay<1)y?SZ@r6IxPmeMXPP2~|e1C(4Z z^ML$}__AB|my}3^(J=LFkw7VKs{nRX|3T8>H=h3+&}GHS84CT;V6@E!MM{SW;^Vf+ zF7>~76~17D;&EEvBOa^`Io7h!&cf+5IxekcR&Tg!zM=TmIuWcBbhJ~;X8SDLcvE2(jgvo4TWOTUerhT3T3Ipfe9|#^LDcgfia>0fkH`{_QQpR#(5Ej8ce2$ouD;5< zWHGq;b9uOsq}|Vh3YhA93QU;MkPnM3(hbaPmpu!>bnZLZX7T}z{aZk)Er0~>r?pwN zwA+%odUeG?zQN*EA#8KgWD>4Y(w)5`mJKWL?lu23M`Gm0-%}2#pwszwcOevm7G5P( zc}fq++92=yB2yD2lefA&v&*5$8Dk<6ih?nHhZ-Yw^wU7K6CtixDF^LpECxt4kPy-s(A1iEA|3mx z6-?|3xkqy49DI<>n;^o9N$}X<9X2nmJuj#M1=liUV**A-zt84~bdy+$%4#YQM=O4MhXP(g$u;aaUr)d=Dc)90h-3EY<6aXsaTQgaLX;97@ax%nuLWx2yQ?Yi$4jQ{yEjs!tnJU z9NQH~4MMR-4NPpe8Yx?-Fy}rQVNn>mfMH?^_dQb*6C=wHA!)v&l6bMibSqIO(!uG! zQ$VloX(rOicwwCMIAv;T3~^d4rSoAnxmiWG$#1UnF52qBH?hF&dX&5~bet=p3(VF+ zO4(p()>Q4LDbS8X;eGGh6OhpW7Jzy#ayq@QSIE5zmtXHVW}sLwpl1v-z4v4-fH4VU zC$;@XCOn{BA4(LQ;VQY7vv`^9I)Y7d(1aGtiQ=xKquSR?zin<$uB`a3hoF)35(7qc zRr~G-l!plchMo4W)6tTn;eh<{IID2bW&N2kbf-x^wFE{vOcn>J~z{>B3k6g@7 z5Y?iBTe{>9-(SbbkP)B3zY`Mc(zNhH28s5I%J+#}m7i_y1!kh5Qo11a4S6HKK`6WOy9*bnT{>U|Ws<5MWgy8jn(>ilj%Y#bQLNQoG z?riIOxtCZuEo&R4rdE>}#trB@`QUj$-DW$vD`G)tB=`=hhMk8ETF#RTIj@hX3bFBk z^5C&Osv3~=$?(>8iRfZC_F*PaP*JRIEqd0N&-vjcCGVPwqF*7y+sV&>!v#=chp#YA zk)#2Kkj*8hni61IeIvhTgG`H0g3K6*q^@mOE_YVaALKgEL+pP^l2GygigC`*y~2xO z)Fns5bB&P|^!DNJy`=TPNHwe~pAbQdU&##>rMZ^HMkRBBaLwF6C3A|LDc-!D?90t} z+3+JY5j-??uqsv5lPxzcD^NA7NEF!i`(B(LrlwZh%h?G4L~9$K4|z`@$cAKxqmjQt zP2~b7RiPTF4P($E#P`PT9QiJC-WZ++&V@tQdZUs6tiMmF3(Pjpb7xJKqMW;Z=|A;A zG>sDSHdC`$RcpDsh#^Mwzfuz%$DNlFX0QtHyyXyz$d*y#^ksm5yQ)DNkW*9vx)e}dY*#h1If%%s{ z^A?0*(rg*=69w9|Ky4;VwCIUf(0!BDxq3~US~r_CLt(x|C7lVveA+Jvh*63B)o19H zsX(M*e7r*vbAf-T|3KXbSbjNv1#%rb*j%>(Ze-s|*=m=gMB;|P>jFl+AGHFe#2IAi zTppezGI*5EX>g_F&<+G@cuu)gStxd5;F?rwE03j|o=cKmg;>R>&@fs77R&#q8YA)m zNB9klTv~uvf5jja#e+EL(3@k;*kG0r6%iq)F&yiy#=`ezSaCQ`DUy(G&KkA+$RseL zj5}XTgyOqyyu(jRcw-43rvyz?)RmqUud(h6-K!f+ignI4n!Xfc+m#p=?V=%1O+9NJ z;+ceRm#GI7zJ$@H4}SORBPj#ME*;uP@0+o2a0elg0CmriZJ$3_RbYfzJYmRA$Z0e# zrEH3Or9rAOptMVoDnzChvY*SRwSGU-{){+hVq3Y2^kS+Dg8z#1uMG85ATP*Bv97+e znM`T#NpJdlT`|=!de#07{27PKI`aF^3~WeXgU0dW^?g5UHTn!fsDjwZ;JKc7@X3lL z4A#Xrb@%7Q%)6b0_$fE%1QwU`bGj{-^PcY~JeQu9YcD*TNlQUu(;xW(IqDgDc8@w!{ zv^iUNHmI)3mWKJ6-gz^N<92Y*#cjWKuG{z$jB_T^QcXTV!l@r;bx6f*g?SV;+d#(< zmQ!C@j%sUI6~0^1ghsAHTQTR-?p|zn+}d+kIiN*0*f*cWGA|NNN(^9vKI{I%Q9%qbmW z45e`^)=>EUi1!kbwfH1k5X@?=TH=hP#`c*@X7=ZmY5}6vDkZUy-uA}1( zGYa&K?C%}+>gwSz>vdCJ<4i&K*Rp2Ch}58+xkSl^FZW<(jIP@HE4UObTqqM;6*%Tw zg~OGbdgTiI0CNJz2^#tR@jq*N#NCs6MtPt&DmIF?#Up(MY583To+}O|K#KR0rAIKp zfSm$`GBQvD%(#J6=AHb53g2XB$8KZ1(7@cv@*F_v(53@{yEhYbfsK+(tc{S zTz&kjokTl5hsfI4R-2Jpim}X-#8+sZ?YllR%bNs6G~}zh>zQlf+<@eb>%Gkm5U*+`DN`Xlk_|*ayX-U5VYVOJmu%=G|}Yi3c{YVa2r}ub^P2} zt71*&n+;_W`=Q1t4ylJ*)kTeQvovkbL_N7L;O((|6?>>XC}%5zNTn=vh%Q;4fjemi zSbTa4kc^i6k+H?Tr}fEfELo{SD^+NIt`#54b4&YK;;pt8anJanVi-znR3L;Au%b3w z2hJm)IW5K-EG2YT>=-*|;AZeH*PzK9n17ZKiJRth=4(SZP!es=mwWH}y-c_P61CRajyZ2ojt{WHRKoaf$%~T)FRKmVp$M zMrL}tJle*r&FGKusL*FY5LHs-VKi&pU>)0Z0CHWoAfr!cY^(wRfmBnVcbw-}tNEvK zrJEg#@}*TYbH^jLlY?V3bWw0Av)#eppbA&H3#rQH2_jsnPhpa_yQ85!&7o9s%r|eU z;%m=-wkt(+v-#`00x3#4h$wR}ax_q&E)cw{J!oD>WnL zwFYewxU8>ip(aKO)a+@QCWP;B%~ z!G1aCZYWP%>TT$Iox0>SI|Eae=?q9W%V{G%HnahTul*X)Z^&^1r^sF{OqKwuXK08r zh6pJQ5dlHVpO@fGEGi0E7CJEa0DWzFrUs9tg^xANmwlv$9uDgI5Up*NH{|}JE6o!JjdrM|G2gGA)Gi~wbRpK1IZVg`j+!0(P#FNTRcGi!L+x&QA=$bInjRD^T z+!b)tu^M82pBbrCI4yVu`AkY?4znvi2|jXpF}sVc#wE5)vcF zhs=q{nyN4n;hq{xn$NSp^(DzH&>YCSO0ajcqVDX_hZ&|zQ8#3<@l>4Ml*B*&#XRcF zCctagh~*|YkOxoU2n?LUQEyRhqV=e+ZVK^J;%RC(7w7CG(i=r1Z$<>5o^U{6(PT!z zY?CVJarxH3b84J@MVLFKnQLsq6-aK%1&b~IIyCq9FJwVz_E@x70vFuuIFx@l znY$qOUD)PoO))(fhC{yyVltkda~|!5n%+|_MWFuTmYSyN+vM@_>S7#*?U!FTOUeu1| z%QS9AaxyI_1qQZ)$QXq{ZiZipI@DTKyW-%vL3j6zZ+;c2AX|=Mk6f{2$drB~s&=NZ ze&^7mmp&S81=BI;^CtiGSUPx82}=K zmme~~mU>GTumM(K@Bn<3^}p!wiS#g@*4kIm9kaYC;k_0D#i*!S21_=`9?D^7XG7ew zW(R|hP*t5Tm&~ODEo&rt*pZyZO5rS@N#GXUzIquT5BZI6Riqv~Pe@V~g#o)2Byj{< zKH#z2KW>X0f`n4GmNePyMD1d}_PZ>@S(*(J#H(Z@%FQD>BqjHEHGZj&AHR_-6hRc`EfuiVkblB!D6cOz3jUI|hB zT;=3KlfMg#T{*_E)DER-h=zuync=N6!T&b}fuORMM#tH;(SA=j1E(-`rJ}QC{5u*E zOtF(B-L0$m%lc&mu(u!AWm{86oO8PYGo%~OGeIq8v>b92O85xHwYFc9Fe6S!RlUUo z_(crtHpJx73VxT6p%$uq*E+<>pIv?yJIA0p2|lCaK9zu|cGQ(O8ze9Utxd( z^j6k1$zl0a$BbFthkF+2ZkJfXXi%vo{&J`!3sU5>gem^4YP{#O0&nQRyjuEa1=ujn z>`V2$*SyeVVKk=t1+j^jk`e$K0mu>nGVug58z4vvS06IKe)rH8fOdXi@Bn;=TeWDy z`nQbWtCXc~HuiDdX^5BLZ|e(+g7*xleDSE06j|lxSf+D0FIPqfAFo0!+0 zl?49i?etn;-}0AcQD~>^^!9cyZ!|KRlF(13Xr|{4N--WCHP;K^UI(wLeieEE6vyV= zNxe}tG#ngDn^Z61Tp*}S+b~QkurL_{e@OXVlsXi{Syd9?^fFd&DzZo50_ONWp;P7H zggt8SDlAh4CgqK8;m>+wKw;f|)w$kb?)eBX=HZ|kX;N`#BQoK#RR!j%Y3=aL3c|4= zn51`A&6OpTf8F2fdGch!7}xU+Flp^!DN*ToV#~<3Vhh}Wt0Xd2(2e%z7!+QED}n%0 z-h5S{ky(7to!afLUMzv{_lo9gt57&6sDt`QhaC!Nz>~ut0xsY0H(3qf8DSG<96D<* zLOGG06=2&AvGUGP+hnz<1X z=n1{*^8a4Q>Z4wNXXCb%^`GUj5*XhC54{0^5&#+j*b)FT<_akg02MKzpO@fIbI1y` zz#%(84KVlsf?w?T$?)**g~PJrNajMN3Ts9>ELDb*bc*4gn3mAsuBd~&$Dv&i`wKzl zE_4+jZjHZkk@-`6O&#xI@N%tXw1@82+Azm++&DUftVq0svTmUDlX5Sj(fXc1So0Yj zq*HdF%1@qao30`TL0ig9IS#m1Wg~NfIC}nGLFwydM~{I-qcLt2 zEjV*uTLHo(PlUG-0-~ciq#4?WXaNFwLH7u&C=}SPJ=T9*tZ5)Bt<%1OtL@r3}Ke&1Z+My8#nz*Ad4=QsX9V(aR}v(`R>k zxM+0wF1AtPHK#bDmaeRdPGdCf*ue*UQHQpMYHGi7ckA1_0M@F^kOffBe~&@TUuf@y zX(5HUt-4Gz)5e$k37N7KBS{C#t`sT|jVS25tF_Kbp$I+RvgX+?bL~bKXrjtDhynde z-&~F9o4gxuH?i^}$lED8uQAK+YX~6_5AJ^dP}%sA*Q=p2XdHOUdhRl)icl>9v6d2q zF-wUZk-K`88@>Ekuc&NSl~UpQ2A*XMiTkVzw)em=opQ7FgQFh542~e(av(jfaOA83 zQ}Y;oR59)`UuA^uu*hwkUx(tTy?xp(5~rp049P*7l<;SXBHh^c>zM9CVW4Sq#3z=( zu)1`&C1SrsE4#ml4L1gT8}@}|(!n#zDnNZYA|Uc%V<7x_Yn(M2f(v?q_IXA^k@N@= za?C#T<uBw1b;^)7S)Y*S7|{J~g$T8-3Z$gg!_HPX>) zZvEC*dFoR*!gtl&S>37oas&lphY7}&#~`8o(IL8B)AL3$(uCKAL>@B;^RZ(MGO66A zuWoS8`C9ex-R7@Rj$Va$U8z!Dk9URx6LBv}RTm%36!v$9eo9RWKa+=(9>qr-(tux` zWOCvlg*=-3STd-QS?3WbEC<%1gRk?zF3$J%yx=qWEiU}&2OE&&BJa4k4?fWasJSjrx|43>M|0is$EMFg?Zp0+vkOxG{|GWg|fi zg~5Uoi``G09gn`xWlP^FwhHNDwib|_5wPFkKO44ooe!iTi!M6dPGm-8qq#%XOQiR! z0Y{5$6-`NYHS-GxF{mkX+w`Z_;jJxr9B~KV#-l-s4SIJ;8>C%|>7#F$-;BiZ^)@J*k`(BDSRY$g z*T~i-@clxm*~W8eouS777gg53tv@h9J|p`LDW-bmHPlh?9mN-sXT_>OZ&T&~OW_n4 zvD9^l-W6~TcbKOXOe3~DxV$#dvz0?ybXi;;C}GkvDt0!{YWLUx05ZKO9fwI(DIDUW z%`$FrlqeiXp_?%V`tYJGowp$OQG=vJ)fT8CeItu{IzpJDw;_RpNA!0dP!U+p-+D^` zOaD(ln^l zr*P&!@Q+jTi4+O~Qwy6s>lhj6U_PN+bcJcx;5~Tw>_}{t{MqS#vBM!*q@^?pZ?o?= zk9xozX?Q5}oXePZI5?`^ETY+EyK_Xji5@0%ZxJ?u{qK;`AZ2MH=%B1*@(*R43v}fj zbG5yNv#KxCnJcq4E}Z}62gw7ESF5q>)~`Y`!JC`6aoW_l@`_dmaLp$WJWcuEiQh*{$-GnCy6l{B7<92reow(5P?Os0!+%4%b&agb$fMQf%3>g_mnrQkF(pk6C zXv8hjDl_yeTTOZl{%a@OoVP@PxnDuH_WzDF%*r%1QqyAAxq_C%HCt;Q(60&RL|E)Y zr9#$ZNIT8#0MIeG^5^p~C?6KwbRR;-G1NPS$45fN>^17%(e7x28Fr6Br0Y#=L!wg) zT#m_*iBIDLT4vY!%x;Knlg~QnB67)m=CKMA^*Pz7!xC_GJt^p;}Z{~SY zV%D6EmHG#w+25rf;nRnDB@;zWNeXCZzDx*-=PZ0Wia4hPTF$o2@qczE~haVsq; zliUW;r4C}bH3gW}fi=B zs`$d(bTt^I{D>dq++xM)ms7X$GDM#|?;J@!1>W^QKk>;cgN+QkivSqpl5!J@sZj!& z%(&*293M|eWX6YNzq}`5`uKhJY8dSWSL2w0ctO(2b|R}50|Fu+rahk;+^xa7{dMWX z(eL2UDK2Y8t*CCw@(&q5ECfusT?kvi+%m)iG94lcV?!S@z=DDX7LW#t0iXc!ryYU1 zQjgh|RrJAo9cr`&MaAPD&G58eYmc}&dAqK1qO_za#H?Tz=e*XJ^Dj!^#!}>wcF(fcLcX9ifpCK@>*K0 z>swl!PCkZ5cpe0@Y##NZ0gWu-T#mgE@mk{=2JJ1E6bns!pz4E;Nkm*zS3StAD^3gr z0q`7v>nho=sJAS5`x&W(%VyZEE7$L!b<2=bDFQ>`;;_l1WSn?(QX@_o4!ih-i5&jv zVctWx7;0tMX!!wgIs3o6CLtN$&&koA@%?E`DZ+GgC&%^sOLvwCkjhDVkCo?Qjq^Cs z{|eb(kx@s&#IeGTLvH=+xdIqs2JT*amxa2H&?zu#%B$eD9IiBAWw{|S0Ekacbiz|z zu1&^BNoP?7+`HmjNWiAjs|Tbsw&9{MMTU!03Cd-4+M@WKJtT#?CLOB%fy&|dcw7Lr zroY)(nxBh%w&Yy85&#+js1g7&j{`CvBn2xYA2Ps^&@=Sz0}_wAqaW0!fM1ywU3y;@_oBxreeq;ksVhH|3ypqm`8mKq zs{TIfQ*`$MbtB(-6akV{!MAa2Y6e?t_uf!jz41hx!Tu?{knQXD8!f_%qY(x(`9(oz z9sFS@3M``(vB!+RKM+ddOOw%W8r0HgWpZJZ<&ue*`)CUf3sYd(g#DD&)!TL@6*&;X#BfZ>IjXGxO8gi>DkmZc0QFN`M+nl(D z3Y~yY!4d!(0f-U+GK&Z)5g;I`nV*;7RWgVQSPRZD=m3IXCn|#0r8r;MBI5{c{(L#4 z8iBLHuT54klp+PmUQx3+f%G2TpjK9+X3|9fF7+v|t@IdqV*_EqBp zUYC?ewEi0Xrby{IOk)E}JaJ{dT0AIv!ⅈX_mHAK(?$IC;y|@;nVIQ33nq?RI=VFcFh1 zo0N+gBc0m5cb?`Y7pdPa*s`ILkfWE>bJsD?=KT{7VZJ#W03Mcmwmxy79EG9HK=~Hhv*bxaZSMR8 zzCmxri9je}fFA2OIR)`i209-zlOqYSW_5i8G6A=dj3AN+Oxu+mw#|ku+)SRDk6C7zIt5;fcq7 zoOZ1-XEwF=Dm<>Qr;l{SPj#UaBN}Mxc>Xz;y=h2MW?HPTnq)1(V0EC$bLi65(Jvm?U3Y{j$$s4$OJ$}& zDr2NwnW2^Z7?U!!uutTsxVhh=40&IlvBY#@V%3&1I$&Tj@2Hm;E(@9wXVSV~NpNME z|GYc1rD^*o9VmE~GTo`UT1qzm!XW5l}~KqBr<0c8BYvidS3zNj#qM z9G7*BOqyVUr%>^N%G~@?hDMVBG$E&;I=JJ47Tvn_-oF?j3}fh0y@}|z5Bn6D`~=x+ zIqx3Zl_ZcpALS(6scQ;3=_^IV$TI+yoh5!K585Dk)EFIkE=GAmMn{Ei4ie)WfL}x^ zLZjIiV~HJ`{9C4QP2`E72Ik@y?0EEc*vh=uV%wQk9vJn-cis^7O)kHZgTJTT+f`KK z%SX#C%0nC>=o?Cp8h~N+Z-9m*d)h1{jf3{tae+b5mF!)FyGE)V6;$kkS-x`jI4ypS z1~&sV{KYG`?~F3r12Q2Xfm1UdGQfg|j}|!~9su#JC3K<4FBvO&dIN)8IF2Tf-I0l^ zIT?WqtLVij(P*axx38Oraps7}Ha-;cxb@juO=e10T zM}E*qzc|w-b>C*sS*75z-+At^Kv<9nu=Bw_!KE;+#zO%r(3tmu;?vF=*l$J zehhM@@EjR~-ThY`mYP&g-l?2t-sgIQ^WJoHCkJ+hI_vC4L5j{X`ur>CHK%~vXQ?;> zgn_J$;k+3ld52Syn<>Ni+|`4i5&#+jm=XXo=mRn%AR>daA2Ps^;|dm#FTMfb0P&Nx zROe?^%tZQ$yH8|N9XNk$q<_(U`KBx?EQ^~1R`jZqaA=iDg$HG+)=EvY`ql)&NuMx$ zG!lu}lOkz9x|Ms4>+>&lGBdUdKQC-({#SXy~-Z%&uD zZ#>iFh;h=VKgO>yz^fW`v~22~vf0G+K%@zTUK%DHjWL;-BDR1C7<46aDM9MnS+Y^B z%V`Q!gmz{7CAU7HSb9`#td5r+=JfYCE-zAMjN(0lpV~jc?X3!i;j_1~b<2ut zuUqmhMUh1*F%Z>I+Ke)kf@0;Ih{X^DtzH3DIWXH<#DcHCzNsMLday^m7!748}vHf3~G3e3+2EjhS0#|-n9W1F?|2(**3I7)0H_+%2CI> zyMkK|1BL+lWO&`grS=}ODJYvD)NaGW{24q(S`-s*Id0<6I9EMIWs#T-fD7m`qwo>{ zG8Qr^_0^AAJg`1VE#XjTaX`gU9JTW0N7j)-AafhPnbwYh*cNMv+>CcJ#KAv1r?((g8^|GxHXvP^5H#kJ(+Cjz~! z%OrwEG2S#|@Rv7l|E;Sgf+V(6>1ZZ7pENFfz6)4z_>j^x%B9dRXGW-VY`+62g>Q&9^w3J=6xyc(90M#-%(b5FPeE;L8`$e9PhQy^YmD&tWEE9 zP3t!9(du#@s4YeF0TOI`-awW#5$^mz&my%TR~xPMg22!^#~gn4Djp1DNO*0Yh+cxm z4WHVS8%rV%OpmuKn%?RddH?OyT*wu`O$xq`AHAVFue#wl>iE+u1*Rc!QICsu%f0uq zfix}Q{Vp1lVOINTB8E$>x1&-bxfkF0Q0tEK)uq{$$i=fS%HiFX{f3^& z;wBskBU28KC$qkDyQbQ|G;S|2JJL#=JkO)e4IY_ftj|RMNXH`V&SvML&o1^RTbDVA zfG^Gc&CiJcp2d1M2*{M*9EK>8Ry z6*z1FbnrvZ_(V`5+e6dj{0F4^0#v;$^931wqm0=5>|xoEHJ;~^c@9+v+!sb}Lkxzh zYa-Xf9HwV&^hUHZLi1S9bDhP(O1jkbilgD0HfVOypLqy{4b=R6vGg-iAK$wA0E^vU zgtJlZb`|mfNjhm^RKEt~-5a7G*iCVOX?`QG^~bc&V=WU1T$&USjANoL2}c3dLUp5d z*ka_PTmh|;!`)Zwm8fajJ7Gh}7^9#0RL?-LPqp{ZIV#eXy|#z2&drVnqKAZA>xjiZ zm3dn`!K`C!C#Re`-6b4nukXCaQUDZ=kRAR^Hgw~T@OGJD`w#D|p@u5xPqPXy@_a`t zohS*455iNL8+O#!84A84^U91_cB3}w^J-3rk?2^z*9Y2#-M|06N%T<lIxwt2b1sBf9y2<2k5wtyg@ zs&pY!sx87PBua_m_{WI{*!>{=T?m5D%Y0t-MQBfP=HHum*U(Vb3QDhh3S>WSx&2IB zXtKa;MuT`nkX?c{^F@KcA!zP9j7UmzHm&sn@db6=Yz#)m^zRuYvwJ#2pmoTY?(BtH z(2D+}-zJZc1klfaZQB4Bm^SjKH;HLpfQ7u<|GoTcqD9jn#qfCCTkCs$+v;93nh_%~ zPeu9zXPBdSot|8S-gLqA#>w$w3pCAXenL#Pfv(&K7xUixbF7jT6~5T}fPSVw0iv-g z7I-k}pfeV*lnAdv?y0`{iK7qvTNsW_&Z{DBSXy6lARx#ThX5UoUJht39a&(hQ4#?j z-q0Xxq{8c5XI%GviCtiaXyMBUKx7Gx6@q;4&o@z>@r5qfHIkJK8J&!{gZH)14Suy&kl6lMmF}l9KXns z16Vh5)>Gv@0RXy+#R|ax(HF6E)Zi))fUwk^tS9( ziH=DTfjC;|$D|BnMZbi*ayr~H4Cjzo)>(lG(z0IgUx<9NbX;-VnmX>R!cy6qhwYIOgBSJL`b&{WE5LlDeHLPNjb( z8%&lK3m|39Zhcs+WBV8Wo`87|#2h@;fPdha z7L*foJCpfEZVrH0WIN?dzrR2`&bxhbQ?O7bDHzw9*8pV8<^cFOpJ&BYgAr6vM$(s? zL|0LwmoPjj6bs+B_0pPE{;OSiojm`B(-h=hfMLxk)-%|C({n~4tWRa8Z3pj)UrH4jS|{%n9$lyL`*aLU>1ap+y*2C@=(Hk(hv5Put?L}a{Bg}E-q8fiFb?sERfL4*PlGr$8%d2G` zBE`z^Pc+XS#uClQtjFJzU1<&>R`QDq@k*o+qtWI4inxUrX&!Y+dEyz54vvuCZC~h* zu_v@m<75{ET&s)t)&#UlCVhCX?u0*}_%8ZjLVDYR&zog$jO|xRzZxxcRQLWNu$)T- zLYJ!}^?q{aGQiPXGY=Mpa*7(omNW)S@iGD-T@Pnm8vi~bxzR|Bq+;oN*%GC@bB_uS zUKv-U2R>o35I3n0`d0Ud4Ul(gXv23AXX`1lUfKjXFusi`p<{CShtzDKjVOJppd7r0 zP+f1e{B+0vxu@axt>`Vs1-2~lu)xw!Br`RwLq+q9)6FcYhBNLbw+&vVzy3SV@<*kSBbhets|P6Gk9x3W{PhRKFKB$2fzY650VE4fMpvX(8rW);F0kt4VWGK#>5u zuIVCw_?!#(y|~{C5^xe;Fq5PELe^kenYZ6wI3t< z>kB+im6A=Yg}%o^^=&|h{N;wR5r9*dN8H%H^rByrZXZ-Y>_S~EqM2ocGQVrt$ZC9B z#_$8&Upu*0Tj7B_so+ZekH$g2C$leha)r*>o>gQ{MxthV5x>fs+SMTE7n{)J+%z1q zZ{^{9%m`-uY zOC^hMnGe3?Sfgir=TGhDABsgtpZ=*C55o z!sL2G|1pm_%;Gm98y88mpK-UKpqgKO3%Y{gLyp6dJs$c;!ObVnijj6j?ZEuE#@!3K zz>Vs4`0)A13Yt#BRcC3c!r~<8vxREpfEkuXUhmqu6c1 zV5T@3HK$Whs$j(SEuYe_lI+FdQm6htcEMCpueLaWn%zPCQdt{65sohPX_*t%phTag zo`+mM`fFGFC0#2;+*c7nbeU5IE%78kUE!_&9%((bxWD)Nd~g_2=YA&aSdk!#^zq>; zs>%xh_|_x!M|&MOsIDNU_IvnQH{IBFi={#Dm*>OHPJxA&O*0`&lQ9MN0gp1W^rt}$ z@N5rM+owv3EErY-dnz>#6TtkhSlhw8sDq)SAmmG|0-c3TvV3r+1W5>zEH5)@%c4_kl#EgtuT(m@w;6c# z$`dLncBM;K2N7I#Yz5Fv>0WezPCMkDA&nWgb{YzCPgumUdTK}dg#Iwl6hwc#=0*Pb z^1B@DB44mT?#`D|4k=#f58jz?0+cwEf!zp*Y5CqXj8_jNuuB6-#v0XeZvyr8<0F%= z(}YI10zVK;5-vXzh;@*gv7z+4FH6&G3Q%Q=r{@Icw-SLg1;wTb|4%-zJg5hz!#7q} zr_`r5sF8-W?{S2uZACuwR>y8AjaJW%LNMyda zH7lbyr_x1@nMe5ParFSa=25|6_s$@4An|Wf59;;`uw&sQlFo#CbTcUzj}8-G7AMK?aBi}yfUEzbgbMq%$>8+Vm4ywm8mZSPdc(l>7c-|*i z7jXp4$TvtUx_Id&Fm+AAHL}&PgHhYys4y#I-Y(^sT1e{E`e#gdreLlj1-FIEr~eN@ zmiK!#v9TO0(C%&j{(t2}>H^_TDoNzsK)E4m*jA;55-`*JYctZDVI0$G5mVUSC5W=# z>J(jKTW_l)G=n7XM*Qx0n~1ZJYVh1~V{WGbE1rVH(KRvF90eV+xh)n9=W`KPzqF*} zG)bYYL?RcYIdb%mONi~Pw55hTQ}5Vk5b&-!9xRXpMgX8dU%y?>8;`;$A&(a3EPqS_ zC~k3}I869f$N566p&83ohEZMs7n+yr=ed^NCq%;B73?qm8aneDD1Igg6(x*V-3W<1(!6OL}?P728by}c9huRZN61TxQI$u)Rx<3kV7B^Y4eyPN09J6;MbhFQ2ZAR-1BIYEdp_DhFN`&MEpNA#@R-8 z&)9z1sxD@hxYSHA0%r4sRA3JSTs$PxG5aW}Wom>ziRw-jAQl@iO*tMfp#-e%gSfvP zvA=pB#K8=vjBM!p3x}_e1IZU?V>E+2%}3T4DnnB9;H*?S9`ptnII6BI`+cRu#Z#Y( z?v6g6Fw5U84R?i&rBwYHwg|%5)D~r_oBs}M(G%89?7s=Km?}!_rL5tN+sAX}JxW5< zBD%CNa6a3Fl6`g$VR{6m((ZA@KvSqRbx`H(m*X^kwz=iRM}DO4*TD3bP`8!7WgzbJ zmJ%{s)xZlh-i%e0VWZUoP{jZ_C#JG?^LMwIrtX^%E&~_fM8=u$%aQ1>6D-;<%29&iA`VwvABzu4;;4p(knjGL}H3|~Uv&2K7y0XK> z%2huBaTj0lf9*g5Uo_CT%D2_!=GQt~C=q|C%Ca@)G0gu{pmUP|Wy7*#YKnEdS4v<+ z^E7SRVT)?!iokoWn3$1>Nhi!gG!&~X8e+ffr4Rw0%vNxz#K#O1Q)Tq<;LzD%u z4gzQl8{j&~NX4&ovZ_RD%FvzGC)v?Yk2mMH$`w>3%W{D1UTr_0n38IJ%vXtXaO>Pr z9!PQCk3(>2r4v&pB$kg8B&fyB2+E6lBs~OgOtn}Ko`C&KMfL83T}E~4*rLENXMoC{ z?gorC_S%#Lu2i#^V`Ew?vNf#T4_15IkJ8LdzpNK{Q{~aX9Y`}N^88e3>Y^nO>sLK0 zqurkGQ?XmMntGkV?Is3QX~sh8I!(Ml&NxHlD{V;^%Gb}cJkQ=1Wo3|L3d04?Kj6ABL(boM@u9GW9q zcnDmwREZF<bkRB=-<` zBos^9oey|>grU=%Tl{=3-BUywSLhj7xNl|xK>Xv(@DfOUslQBp^*D`WvLa^k(+FRR zdgfW44AsZ5@B;X$k=>z9ag{+y>s{{~Z46`@FXpA9=IK_qT(Ip2=Y+Z2zXJ{Dm@lB^ ziU+4Q(1pNhoTtot!bz)Pps&O15v*3}oe?Au%WS(VUWp~6=#XRG^M^i_z6`0D*WN&0 zA+uIg4p$uSL1OX9?A`&2_)z21qTFWqnYKG`4$;+AHS$l8LQg@U^zDIX7&ku(<_+}g zq$O)wrBObMIrtzWk7=sU*XCxUdDh>i!>l5_soY!mI{f^#itMq-F_onNp}M?8D8#y3 z(X5tUGa|)+q}xTNA+6okbCAT+7;uJDr3bkH2>j_2j07uUYoa3xnHoAB`?M|HY?+DA zq@K&*_LM77t5gfOf28Y|Bus!x{Hx(0(#!td!(gk#@r2>;AFVmkbgCRE9SWxScnij^ z{a{uLtPq3-!xHp7l1r3y6qbLUPr3|VftmJKX2Ng^0lj)*O{DIF%^hFcg|5bGPO6xt zLb2aDWoGz|bL*ObqxMKZ@w2ugun!b-ismqs(m8C+Hz|U()smAbIuiA@P&h{PfEiDp z>F}_sfqF?8k~zrdbcNvwVXMif*XH3?fFDvfVzFw#dRZa{C}~^Ib=i#@F+4!}P(iXm zqmRt#g!>Pb&ZFx9F{f@gEGcg0BVxHAjQ@X z9f0P?^?6!8h5wq*@iSX&O1w<26V#VS8}idbdF_+Qa&-F9>lr*W$6?H_pU}kh38(C& z^5~F4-9Rg`Q8_*kJ!}(d^8Pc$GnPAt3%kY!AkYZ=5aP9KAlmXGT#I>|JAc9bF-TT~ z>S4GPubG@);pR|Qa?$qKoPT^4 zT4-`H@jsHLEyP1rx-8}Q^I0hq9PTZfNqN8YcKRV6_2?Wl;u^NTOa zewlVfWwG+2zvi?$Q=AEE2x(g{30RSsl&zT@+ln4vdL%#$8b95ljZS$}&=m30$ zD38>|%j1XJp``>pOczhLZyp}Z)m#s$MPOmy*CcYZ!rB+fV@oG=DMXD_>O&Ftf!qzW z(uC?kE%DVPMoh?0bdI`5f9gskzGW5QVI>o7ILBkxU#^G6(&n&Z=v;>}E4W`3hD5*; zwJU+_U!I(0q4*Qs5O6yRV%Rm$*nkI`_@bFCzA1e9Se-ZMD&m)(XaG)YgtnwG2^(+VJ_K+dKDvw1wq# zayDgcA$89-MfLa84}h|!;ToYFZ9<=rt6m0xK)AY{v#i+mCGkplvnnpD5ig?bbQCb@ z%wwwXz!WxVbBv$1K@dBM=ZN*Xf-GmeJblQGJ*hqm-)LxZ2f09l0p4y~G}p6DOHCQl zZm-6raTSE`p{eS)0{{yF@18dIvE3%o7{a5`A9MH}sw4(@<;~3e@u8|)UR(nOOK2}FI79VvJiH}im=OmNLI@ZPZ>dMNLskmn+ zm+O^RqhI5C3kl_Z3t{2KfuU_Hc>|=T&FXiVLb0rGKfqgVT#T|l5=f>%URCQ;#^x7- zI?XY0?&2imrMN)z=BG@7Cb!2_JAyvdaLpLHp07EJT?G`Q&$nC|rYQ)QE0is6Y$d(_ z416-2n^u&=a_S*0Cu?@eBW^;8D`}2sqG>nl5iVF(Ntxyfjvj)=YW-|W@yM}m!Qmr{ z_j|TIe4_v{lQ78B2SEC3uGzRI0yU7b%E~R#BQg zv)Y0^dfQ3!g{)ju_eW+Ath^+vI2Hl38ahEK#U4JxR(|fbZ{eubXh+S?s0jY7SiF`a z;QxQgTCJ*>CA&&R`@ScDh$M_>;RZx&nqXfRVBVdp^fhFO z$3khWqOUHSq#d|6Li&o{F|uv{;FGk65L@cu_@dH+R^P6eRD+ zhK*=~aPa#?cqmNBMR;9EVSJSTg1h9e9U!NDo%d!g;luWdCTn=?&n{Ao7Epu^WhKio zyG~zrPYa5X62VI0kE{iM##{{8%J$VgG6?VHEOgsuiqgSWq?%&iQ+nqNBs5(bJ?TJm((aW|Df2g2%{+(FS$gSoHb8@GC^|5D zSvC>ZOiCg$QuD94?w$N0j} zk8AYdhT^a)@iA=4y$Hk0O(yMZh-MJwdE$TBfh(ES0B-{_DT;cp@V}&1RbXQD!YxIn zB!K*mlri_Uwr7q8+jg7r|5*4d7JMTd9@?s~a80DYDJq84W&S0cQ0|zJh;$KNe~Fmi zWd=Vykx51iTtWhvwWi2h?w|PlB(cUzk6hZy-{I#lJRw`4dm$=6qT=399q+>4!Tr;` z#>+UkFGj7-zZ*&+yv19VDm*N@lgQU?`FwKgsNX~+T;VIgihD5N zf64escjOw`08Y%Na>+JMtAH#{(;%1@u7@^m;IofO_eNCjMvc7fPEksZLN@(N|3_ke zt?l;ci$$I2*}cFl$CTM60@>6IVEtJD zc#Zejsk*=6nt@o_Wy1x-b>nUtKYd@nadBU0N2bXAqZ6L22Tc%)?L!iY@4apI=(n;=d}P$%o?a*u@~Z+0g3dO>j4Jd^ zRjQWrt?3;~UwlKqr#gn^)VXNZ8?LeX<1D+l&3L)I(6da;FFpq`8I;NHC@+q@+J~1RP3XEI}E#! z^%im|&x9t_`*N1Yk6)l6c{kfxe6$U$Ygz|E51C2|W9}47(V&5bt}r7f%r88LED`P7 zeto@u7Bwk2KBbHE`u~?mqv;!o4dvl`294&_4Tw9}WbYsfLqx8jyBUXmv^526gH=9h zb+oVZ=m|iFY*mv+l(gFq{QTBcq47L!2(>P3N zSmL;NGWKI4P&pUqv`eL!e6=H+4~r*;>{tXFY{B}1E#B=$Z1~=^oYqN=sYsDo*tD4? zx&sVE1*(R#7I8X!7iViUc-HW%0ER9~fxsiABx}djpLV&_ou>e2S30hvvH8m53s<pThfFC?}wd$3mHPMy@lAMoL)1zbRAasF=5(c$` zYFo^w8$Fau8tgCD;YH9h!9Yw!UHN8bN0zz?wqI3Xf5K>}tR%Kt@vA1U;``DC@Uni> z^+$BAxZQh`V<|dv>L}J0$X{~CNfR=$HNub(TnjGv z$zani4wKd|ZkiVfZP$c}q?4p;^uU6ww(zC>cPh+2E499E5xE7h!2ay*CdE}!d-}8> z>$ZM6lUV{)YUs6#h~(+kuN!Mq7&km_I=%Zfs5H|eTJdpC;;)E0w8GiJ@3p7ZA;z`| z?v!O*%$sl|JZO;_T?p2HPa+>5&Y{4ge&-nQdy$vk5SXuZbf;5kb{eebODb8grBJ26 zwG+m!s~7#lGW#9$5;BnmG9>_jRW}jm zi%I9+2h)!+K-?)jNyixMHJu$Zb2ir>jnV{?}g8(p5Td=u#b1 z224iRa}9>fM0-f>-Alg&aF0~I7OnR*i_+BbLVaC?&OLuHwL?%Yh`ep4@l->B!Ob|F zClyg@Y8UC847X36`~HM>&t5;7$lwCTA86@^`$?p#G0*~=j+GvU3(I>1(HWA`HbgU8 z8QO{_glPm2oR`RQ&iP{g)9>D` zso;2?v6FC3NqvY;8PV26r%!u1V2!4sV#r&smO9Rt++XmAB+A3r@{|z~f(G+aV zO92wMpgh#3@W@--iHndYy^eC96nze5!A}-E%Qv~6k}O@{6^Cnop{okeq2+(aTRp_K zIIT%aYZQo4GG3Oo$ufHAUO(hPSQK|M##Rokc);1U2D z0oW1%GUo&`CmvVPAb=hK@vS9tuH7S*mWZr}YU8gF%VRI0wjHpEuecKY z`_7I^K18kfOa@!p3J^fvE6SkP?{tc~a9Qg;SRj|$bs-S#R=)a{ZMVhtAf6bF{Be!% zs)pXxIfV?J*^`fO)wo06`gC-xvB{UikQ+wj?3uBNw`XQyzGv*i`g@n%xIiCe*98QW z_j0(%lz{v&ml=FHMG!R=8Q3Ck#JPmutnCk;R^(};iT-5p;cXjh>OE4qsV$8Q7BXqb z?Er!1mHIHSC%24B|Fe8y;&(`pHOn<#Q_zxD>RC1ThrjO2WOFJVv@+Zjqr7r^Z}&^r zszd>r#KkUxmCUM0?<%M`6Zk=)P4COdtwKN3&sci4*DBYdFu*A{?b2mqPp2a=vSP(- z+KhfAdOt?ym@>YeOov34$-^yaZY(F@QQBK>t4%TCl=AzNM`Q_%(a^g3t=OCUt zSFPvnqGT^WLX`i}DzAA)(2x&E$#>fHH+VG)nBU|GE83fppJ2JgAG!};or!#t^O(+| zo6C=hit{RFhy!MTc*e%(pd>VLKzxv_VsPAbCh_$8P&Kaq z;W*akF)~GJSF0~Deu+$T;=_hWi)3&%Kd?|eFi_x}R0(UGVdGg=d3S~8Fhc$ONeB`E z8Ue@>05a4HDHZ@k7ZlH#AX%I#i+@3dF!%s{ZYYy@ryIkW0WZHM?CNC7vqNPY*>+?6 zMM#M)-!go2DvBKc-*A{U(}jUvGvD`tfRa|GEN*doX%gS-1Av!(1H=?sYJ)#H(>2gp ztN!znZ(%HH#w0tGDzm3T&AOJ<6$P;9CVBNoqC|rU5gy!F!vW>$brED&L!s9(Q1M~} z92eut)ohiYvdK(F6v|?wTX4=?XFQyXqLkzkQd9-?S?UOO3b?^RCM*4Pkxni7j0Q$& zgoSNZMM+3-So>xoR-8JD{Nx=oy6jtF*GF>mFFZ~BGtt2+YORq1ozqTMnh}7TL;z&P z!MCV4=ijKsiwp&~x=h*wxCN1Q&4WGCq_ zwil70S>vs)km&CefcmG=MB8TO$(&TplK)1!K-u%Ak?1N>)E(nB*s-B19N0+*VnbkB zNk!=f8XmvwHox3iyQB5hlug-T;#HqP!IV0F^hqSJxadjxNBvGY7yh`^1>$<-G?gzv zmG+Us?{*CsKQ|ikjl+4{d=c?=*VJg|4T1T>#bVcm?&okpfXcJIXmdx$_-ad*`|ZFA zzgH--$PKa~l!h;<-(x~`54APTiaS0VhsZobL~ohPN}f7H5Y$VC$nI9 ztD8KO5F7SMLpSi~VY7`7(=`m2gbAvSu7!3WN_072{NP*weVBJxUUaL1(huhNk%1mf zh?3u+IBhJD!^O9{d(+07S|O`fhg+(+)2@~_LNv&1Cv-JhNGKVnz%=DW(H>Tg1Hf}~ zzYccXDCGZKia{rQpElGg-@azy_wCQ%h>^5tNUxg)eMVfue z4+nD70S=~2Ui?l1JU>LFQ>GVe*cp6L#5>(zv5I$$%*6CNOKWME=IaQwqEO~#F z8H~J)QabbiT@;-9d zHvy9E5HwcGFK{mh75mm@oLjAxQxm^a6#;Kw95TZ!F6Yfyd+?H#gHlvH8-MJY+AE(d z-4wg|A<4$m5rjp^wFf(DHAH<;Y$m%rp$?xKusbf2K8UJ%J>-+? z8C9$hfafG10kECC3mdjpVBA(;#WKUQ@y%#NG{O?!yJAeOvS1p261^#UXgpReyFS5_ ze((9=4E?vo&xM0qJV5J6ftfWS}tRjet<%boi})O1}~5D9QnkyXkbsebzUi@0rn!^dZsc zuL}xjPAGS4scn$&wbzKM`oT6(CI`!LWpmBco&Hs(O#wi^8UA(@Yuq6_mb9Y09sDg# zujtK}wH@rVGGF#ikU+NBPg{<49<5>J6!I4=2W-AGzwVI$2y95;<)u}3t}%W6uN-x` zKSX!He}47G?Kw?^DFpE7Les!+ftH9{B=0@AgZpXy=YI(##n5_dh2OKOA$ImK#$YwK zu#0L5$9rZGWZ866(A%R6wjF;O*`j{v^TT~Faj|60DPkGVkOw( zeppbKWqO)=K%ZKvx`{qNz}4;(Fiii!sW>i>O_-VIK$Y0m7%ET)hS`5k5jI|*p@02X zLWPj)V^>3fmA8_Pl1xwW{k9LPIO`jGc-`6OQ{%ycfh%pS^ZI{xeDzKP?@fPhvy=qJ z;sf!xH9O4G?42}pSm98VJ?cb2fF|IH|Cn5sI5=YF_4U+B@ox4p-Qg(qxuGKtq(Lvk z+K>+^P&j#inLJpe)m)|QiCJe9$d|YeLgFfEL~@}~2po6ZVN_~Yfd*>^^$!GiI9smi zjBn9S(YDbpUS-R!`(y(gL(7+YhE%>0gU1wDQ&ppIO$89Z6 zgFtuNTnX|kS#nE^NZo;N$9zgT)?ldLH8!!a*c%D5tyf~o>S&kSNL6KK>M%u-=@oTG z6IVk~Qsm!ESV#L31g#>0;C>4! z%bk~JS5WSqY5EQ1#7#P9=f&lOBl>lWGmnXCTnvMgj^`p2k$Jdw0ycf5M!G^&!aE9! z^5bMb$;8EMK}6_haH{KLQa~TM@O+UB)Yz4Q8kR{@$;~(XWa$9z01>!0v+PCoj8gl| zmtb_AV@-gnaWaV!$OAlv0EMKf`6{0Vmc$KZYwlsfU z$@YON`jaDKS``B6p@FYMgQ&2T3Iq=Bj!2NRk}Ttj@@UmQXc)Kf9^j_!(4>oPC*q6A z99&+*7E6`^brb&KONZVGvb_pQy^)53<{!Yz8lYMhclL=cm|V$d%Ux>VgB6y1g2!*9 zLNe_T36Kb5b-=4x3_L9JAZC0TObM#)5D|S+5^o9v3N{SZ`(dmsdk1c?6}na~bW z(qKgT8A9ZL7jZ(Y&BDDMQg7K!w4(T=qrLECXK}WYGj8z3e(C8~ zq66yR5=DN~gZVn|xIZxsE$8;Uhr^o~!P?WS1?(;NQXeDJuuQRv0{+FTrQ(lc-(RaK zTn9GWY;qIwz0d2JVF%~iZkYwckF$(d%Zk^TO?$zOU&8RH-gRU%>pA%`v0O!}CU!q# zy=6J}oGO8K)~dF5D~WQ6l%_Cn8t!`rd;<3{L`|>}b(8EdFTjXRKIT3Jk+XKSvfpQ1N>PfT=&Sn3bR( zAdEve!bPL~$xSio;jp6H{v1IvEb4N{O)w)Fp&v_%V+PEpKPRR$^KJh0rj)tm^WCiY!pUG6LOuA6O3-v)eZcaHN7kUmHKET&d-dQKvnT5D4u?5!N!ba%c-ICx(*FjfX-%}^$p^@XM>(3pGxe1GdtV>{P(?F)NE;Ej=i z>^2dao>T$s=1;%;oV0GyH*ZvQKwxHx_kk^iWf2JV4wUa3aK!w{!f*zd4T44jCE4Ug z4pDKqW2z^vyq}-0Xt?%|o8aqSO!o+lO`SZI?vJ{N5pdWnh2jNyO#@>`{31ZngwUH~ zF;Y$}_U&yh#lx@T%6m^oYS=5J@yrnIMIZ^A42FptnI=efFVXQqA`JExZKUvsrWEsV zui4-ON~$5tP_@tZ8y2&>-7vmsGv=&~;W18PAf@TX{umvvT35+MhghIL;iD5G!m(*u zK*sFn+|>z2OtbUItD}EzT=9O9YZ>=aXHMo=fx@5bbhBuHvcMJlz^8bZt}6AJ5tlyH zMAfyke-)0;R{#1Sl(i?Y&}fF(wozMo9ec!#hZ0Udt3|pgOW!e}=bOdmMgTyvy4rXF z?(@OBv$~*-?tR>m^yJBVT-_MC&7s?GSAG|)uI-t>y?{E^r)6B(TEozhYbo6%p2D>w zm+;_jh6Jds@wpUS?cvBg(tprj&%K02v2H@5wPAbFYCu3~xc(kg`1e#?tOep3iqpPO zgOV&nE=sLXAljqwb<388 zwhW1mfu^#LOfJQJ=SCiYA6r$1bx!sNu(t~*ceLd7?z6(KqehCMm2AT;*Cgrh6F4!> z*9iXa6F;zqu7wrZr?4MxGfTiu)wGRyhHsYG z(PU4sLCFFZ{!5&#+js1g7&-UuldAQFcP&zT@!W9o~P6Se?!-mvHZeQt|IVoM|Vp?-;pRSXlu zKm~1nob=NQ+SEiTwzQ))Vq1(a5`}!PeG+br833aR=Hi!7FefD|J+u*P9eT`K2*6=} zh6@?nQZ{fe=UOqu!(w3vziqU95(6%YoYX0R$m6a+>c}1ob?R{lWRukn;LbSbc_a>f zkp8VqtmUG2c^7|mOmGUIY_hwP`aYeks0+eJQzHGo4!TTwjujM zQ(vNQEU(Vn(Tb^3m!ffAwlk(Un(Q237q-201n4j&|B?Pm^>8k?QxmW0v_X)hlSBfo z3^Q%Yz^M|ncGvm(Tkb1w6eVqt0ChrvyCgP#im#wU(Ea$tg4-OB{S!OfdYjhzaRmD+ zHN?GqrSolPNB-LY%nN305*)Jo*Kb*E6s4ljd;Z(Q5(b3m1ac6q6swV|)QfQsz-9Y}%cH_Rvrg##KC_tdMyHkvgKK zY)$|V{dgfcy}VIG#bqJ5R>n6PAq~|{4X9~aSz-he9nnMy!~^ZmuLgRyb09kS}&1K`Q5$f^ax%(r|f`Qw$d7b-~azvK((i%Ca zY!qwzYQlU}L57M3Wy-um2U^WPf()g~SLk4x^DrBuOgL-OI97c^wG)*?rRB5X*Y7n9 zp9^pzxkHMnL$56`wUg zlPuB}Fen}X@vS9vN1gCVobg0z?nG)MjGrBv%&n@o4LY_uC*M9F?p(s^@yb`T&+69F zw@nDorI9jA7<|KjC<75@+Jzdv35e-~k`Jg|{>8WF){HCl*{VAw)T$P18n@uiJ8zus|K^b_l#|WTetc>{ui=PFVx{}wj z@&0S>wnLf_XA#opKqpxrz?uUq}$|MSZKkS8U?T?jH5i|!BArTWHCpG}@1-sW{2?^xRDyl8n zVhzbI-xceQ>ZQN*zAnPAruFSXTtizmiBDbhpZ#lF-s!`Lr`&t0aM}FwF3GvUC~x6w zN}6)VPq6CjMeJNuW4K$DXxT?Acecn4h==hhM&PiB-8y8 zJo>1fS=?ICcvEAmQTk{@5pHiRV=4(%Savi4E>~BZ64F|`0_XC-3&n^A&GxNdB!Jpp z?Q|jg*)(vD>nEQ9OD=W2C!RGRVU5TVhFHb(pYqBl8Mc9$TIj=pY2$17G{6nQ1T7x7%I%?97 zg;|wLbb5R(5YQhw49-b@@j>p@)|Qlu5ev17J$eu_a2G7j9UIUozC(;dcX~k>qy4ov{RJpIw2t>OEP9TB z9}|kR(GvBNy4#x1GM<<_(_74sPcI_4@pp?- zWR>CYhWKKC>%9yDcYlMq*y+{!kP-kg7BVnl1P+yh%}@piKm~vR0RY*XoZvtb05Y~Q zAs#dW12ceiPSL@009mO5b>dWfn2lzEca_iPJ{s?oT?O6u``dabX<$a1J7Qa z7Tt3XuzEr55r3Rrg#$e)*QeUk&mq@)JaC=!v6^i-fB& z=4jV$4pTpxHuemPOt#tFtkqIWb0H2cAImuNGM>BTNwGuk2@ya65yR>!wK1c+>0wOs z0*uLSX7*F(H5;@2 zK5ezhm{3hi%D@l@121;=sm%qS&1}0tzVKNOJAz+_eA5F)eg>ZNfPTIia&TCmlMX=^ z!r!1UoWV&jJSFC#+c=Ix-hNraEEaMc!xP;O2}#oE7OVFVpL(JV?-|4lH^adV$1JNA zt$21fPnPznT42t6KYzb#;LlR3(8Wyc%-EP4+{km#}p9c z-R@h~Kb4no%M~02v14IjP&Ax^`L=Apb;ibQeaS}(GrpuWA;Si2kD7Zg;rIoul6;{W z!vbr7HI3KOQv~gu3>3PSD9g`t&Ru&jH>|JRljSuPCRn;yHOExD6ZAq5akl8yHuCF6 zcr|d#_ZE6Mc+JztfrWu;)L;hprT1t(c(lN3iY&-{*RM5TZerpcdh1Iz0A~>>5l8my z!Ui!1XC;V$2MAUYg{CK$O>MkPV)+;+8RV7+>hF3(F`k2^4xP-ZReX8lsF&>KsG6z+ zM>BaD?N?n3mA>G!3}&N> zvOMcT>24ghAtJguqH7=|DOOJGhNk#axvkR%j$T6C(=dndr%sxjkouTVb5}>$(P@N4 z4vm0n`Ph3;Pvd>nX2=|nGr~hxv`GBvP0D;xhR|^?M-)e$Cf$3Tf)BQ>#Vg(RZ} zt&bXNcLnV&!!}Dwm}iiojF(L7DZeE>@+(vu)-U2o4xjO%fHP2rFi{&OIBuzm^5uR| z5iVv^fnc$OAIY94oT}(7d~Q125SkLHRR05f3R^kHpPt)}mGv^9AfLbD6*9IZmMNl= zGL&@0Xi~jY>|KDNBw#3q(-<-;WOitwngk2lBKG*FTg4Fvi8Yua)oBu%$02L{#?iNC zIb&G3*;|7zM>~>RWO{hqE~IhzHx86==m`v#etR~8cf{Q}3UKupaM=9G5sX$@+7yDc z0cG9X>(hISeKi24p?A<{CbPd7T9K+94Z%4S{19ba`&^m#ZU#3zY&t4bWCyBaUFbL! zU402~PLE1O2^z(O0F+k2_Gbzn){#qE2h6@*WGNn9MC&r28$D*}g81*)ynYCVWO_-X zchJ7JRfT+lg ztS_ro*5p;AXeKz+&5A&ubd+3Iq@yrnB?KEIGcG>md^xr*y$lqY%Ch+UkSnYCE8N31 zRvMIRJVrVc)ap@)AC1Ea5Qz25U*ASxudWNC%fysy{{bV1M10RmweKExyAJbEe^l2{ z7o1dtW|!$?roX@Wr+pf#uo?Mi@nN(hgUmdt>QtJ^t`W1Q_IV3EW|&~q;lO^bQ`+XB zwRBXu^ptwQQjVkN_&?y4eH}e=)||I@Mdxj?{KW?(9-s_mECxa)03q|5hU_1OpZ=?C zl|+wN=08^*5HmN|rMOr{1 za&(=;o&AZpr%6~HU}j_Mv8j2}wiT%i$tTKqy2X;@zrBEvG?;PD+QMLuboHJfg*ioB z?Oqx(3?kl7n^q^tp!Q+rU+nm|J7>|oK0lZb`{bf6YwDiKcAQg`6NLhwwUMj94n$|+ zlQD^qcj;Bo>0!^bJs{M!tavHDOk35Lgnh4X`Qs!EcCOSE+a7(bVSczRQ=Gz9#%fn; zO-=4r4iQ;x%1%Gj9MsFoy%-;(o1~TnI*4Z4Geev$-QL8cml`Ouv(+1A#rYFpVgCo~ zq+MF^HDiti-w#9oesbhKt8UW@PIY-nWn2{J7nca_NY@^Vh{0GtCLSZA2wQwzw1(~a z{c5!iPTN<)yaf9H;D?In6Wx4Aaq9tTl@{}>;>x#ixZ^=^r!c_@U~)q-^oBao^#>-8 z>FAZN*h!z?waT5`(?&zxB7Xf+p8`p$B?X$tC;(0aR|2QlW2Z%VucQCq2l_h9uY*{8 zv&Fj$l_P9sx%kcre{P$e&p3N7ec;x$8KW;!Nq32PwWcFyrRA*c&pU|9gatA)kgfr_ zde$Mn$Zqb23-`^o1@4QbgFPY{&AmzdPVwXs3c8zaS2hT-(YeU=R0$r!tPI`_b8$}w zql>IUEYLa;`XHwK54ENoOD(i%m;&X;pj(hb1gipY;A?3L6QybFZiSr(JJsPVsv)F& zR&%AZB09q51hj3_=6swnP4>?-C=iA0Tyy4SRL!{JP9SJS6bIMBxXywX1(3-MKDvNL z4V9)Gurj2c;wQcm$p55uaAt`Os`~=b4XHQMWulPc^hnfgogTU0lAeGMxQb3#deKC9 zHXEbX#~UWgJ#ij>OY89l@Ko~#4hHumL4oyZyV}{NBW^Us2l)?(XU;r@%q;0$8p(R6 zK*e!fOIN>-p&TsE{;?2BLjP)S8risnZb|w161s1z0C=UI9?`grQ{>@e18PEZN-vs* ztgwS&78a#Eri!Efv621Or^eGH<>5d#K&b*h;!7wv`tC^-F^i8KB>dX8Qmi6I_Mo4w zVhF|Eakfv9!eKYLYN@+;D;M~(!)y`X;(BwqbVmq_@fQD#S8dIP@i2Nj-kxgHe>&=n zBE_lR)Z`hR+Y}fS8AB~H*J{)xo~7;X<=irAFk~TLsl@}21ViVCiix^})qR}W| zfo#TpD8v|RF8UMYk+pFuV_ojgtU!J)H4enpXFr1IgVRor*XY;`=|`UDx5sSjc$P>X zVDp{rJVJK%$pbkUHyy*d+CSzJ!UL+PqCG}G{7rpR@-JL=tz+trJky$ID=Qe~_ISnLeOHwG!i+dR^St%A3HiwgC^`z; zLQV}Q;7V7#IgM0~eR*-aTu-i{tn}Jar1LuTfQa4$VFs$33e%7IhgE82c=6D#6sH37H3U zHkRh}s(?K@6X%!3Ua@1ZKgGgx56w&)?J5uhJ0=v^?#_Oxka*~lEfn9~b!hk|u<9Qy z1(_%(H?weQ$Zpvu+h zzURWwjvLLUNrSJFNqCexhvCDfI@2dA3lKjCe`=|3Ui5FM1YY#^+q4Sb7g`)FLqVGK z1qf4z9AZb4UPKN=4I97l5h;LxoR~HFIa*MG-^{V!d4tCiEC6bMZg{*uSY-A~2S*yV zsTYK)%T*_ozQ$O}FUM+6vgo22iryelzxghse>)n(L>*ZM5~&f*)CT-FQpuQ$`lJbg zuo`?sI_Nxm_MG-~>a!^dnCrmd7coH_wm(Zg^m{FgVbWBhn6ULxxT1J%Q*}2`zU_WB z#-PTd5&#nuN2y?W>F?BmPG7UC{oiV~`{LiLs~YiSe7sH9TwQ=Dj)ob$OHUOd5cYc`%@ZFs-7S+mnrK(?|G)WlE9KXz(pXLwu) z*EPqo&~&I}&Am}z*$2|yU+D8>OYlR&vaRUUbm-mv;lZ_ipZ4A&0ZP55x>Rh-_FLqv z$EAr3<=yP;Hrk!!_xr6n)qVv32!8Q$=C(me8?865*zDi479Uu<>=-TPDKwQmyAsJn zm;;CkCdDeaJJz2PNro=F>=0_GE6T82H8@lSxGF$dAE+pM>zMPv_7O^6G>orTJtA9;Zw>7#A8ktBch_D*Da9njiMV?C#8NTI$Ll z1dA^Z*}kgp44zdw5#p&44dN_~D--2w2Yk7O6uaiuNKym9Q7&(Lh_SwU4B#4@6Dqi- zXoMum{(Kgk=z_BH} z1ZlNCOuNVQCEf-7H&RJdaP}cfrcQxVZ=m+hczeRW?#dpbUX)TM$ERaBoWjkL7=o`! zxyV}jnDislhgIB7Ner#j>=iG-d2TNlo88V(Sh>zZFkm#vpOtl`Y@UhIpZM+txkTjS zV#_|E#Kr&8mvzoZ=lJbOeA9mCsfDqY@Rq5~2_)XN-r)S~tGtkO)V)i2ei1c98(7Kvuu= z_MwhKSom}9(aqa7!IoHVnt!q7WM*-^?%~aR*m?qT*LC7&o&zcbf`R0h+$sj~rN<{- z50#Sq4xc>q-_L{Rl)a-?ocloZ5*6K^8^?a(1r!{}osQ8&2z=RHRxJHMNuf8^TgWTmE=UmkQri>Zpp^nsks zTh)-YDHrj71er4?f&esNj6z+h~ov8U^vc5q~4Od6hqOg(>ZxEo*I7 zYHA_dWN>~Br_8jAL@sm9pJXcy`R#cxz<3y!4v3T)<(L)OT(bvP$F+VfL%&2ElNz9!GPaGgza_Z1aCv4VB;%khD^2rd zH$;H`&e_ETT~H943Z>W?Pmx#n9U2d61bWGkE*(`K14R7D__O9~!Z)NOQp!-!strq3 z$*~iaZhBt73&x;wjhx(ljIwABrKD}<1!rTmP4v4%jrCPSlQGDo=|m@XX8z;I=cJlh zM|%kQA$rjsg@FZ@FrB;#3*%1FAcZ+A5)rQ;9KZuC5PV2P?}@7-{j!h~p91lGx?d{} zs&J=hg8D|6(fME7U68>jAJFmjTB?gAWINw3TwRyH^uei3{$aWHNhE;lq|<}Fd&&Ep zy`Xd@!&fI|+`weTlRFJ~)2UC_pu$ZmriQhTMjJEy`zi^d3naL}CJzhM?Dn2w_m&({ zMY;~6omQB@DV(`8t?vH;h+jZ!H^L~h2NWA8KI#&uJH7}}zN3XHhwxE~{qH){cGGtY+)Oa! zxo9qh>o;0HN%Z4al}?^`YmbJsx4o!|Y3N4Mm+DH4F>y9rfNXvlYUr%iq{)jbJTx)bwq0&&avzsHhR z*kkh|cX@-$FBYo3r|fjH;I}iqR(V~&g{vwaZ&_zL)Sds7+E4&=LpPKI#mO_oUxvVo zB~}W~>l%zMq~QK(|4x_{5deLrv0Ces1@lHrP(8*z<$VJ|I9J0T%5 zgjLrPc-L{v-WD!!d=wRqhe1*%NjFc7YJQZtTFbe>(0lHQXO(x#Tm=7v9$LwA`u zdeHIWLq5AUR=4}UQJE%LuYQ`IQLjsvEg_Aj?ZC#w$yj&bH9+L(JbO?hMS<)~Sk4cy z$e~=9t3Qfk$oc9~ekSsXV=$pzf;%nyBNsMY+}QJv zG<@p-yU2@z|rlSFrG4)k>SvBdru(cv`S_!HFo??)yGP> zhKnc=w99>W;hz8S4}o84m7H=nn2A;W%AMN4ttPa>w<0$c54kgg42ix)@aen!v6dB$ z+W$@KqDg)3;0QQ~BuKlps%h0tR#5{rV{2k;;|H2_N9qr6F2vo6e0Hpq%ShO98dW+I zp!0|a!Yk^`56Ubpmk-Bxu_{3=77>>`0?CkQr9cA#rLh!ZBvUzJj?X z5Ccw;L*J3aKx#Ip9i(3pqm07u7f4r1oRmqH}Q7EWmjxnDWX%9uDFjF*+8>&bh z8Bm*Dw{=}X(_>l)A3*SliK-tEa+> zovKt}z_eHS5JO_;l0#|${uF2Ofg>prpIUrDr&045`H}6}N>+Y&I& zx0vM0hR@@%11>q3zJmh~wYeMO6;3^=9P*EA%5a02KqAf#%Mw5`gA6Ge0s;lgkC`A} znu!aj2me3;vvBADe1#=a7i5ex1Pby0#&(hfj|-acT@Qynll%((Hxdy{f_<#UDtmvR z@XDMf{F;^7?k}!W7VSdlCLFwIrpGfL=Hk7~qB%Pi{7q~?wUDz zCt|-776$Lxz-P#%98YpMRFFL?sIxau!QuDZeJw}T(pXKYRWV_4b?F94#m=f-%HSLc z=R4umJ~z2(XfXHF(pk#O|E|cRJQq&K9voseAngd$O*5;!ri?+bC#O=@Ce5spzB)7N(khmQi?^^@EVs zl;Rpg;<2t58&EOwKpFRWQtD)J%)4+52aTea90X^>V$0@$+H6T(K;y^698%gU9(3Sl zR#%K6gu-A1wMOPYv+PH?;H+u)O8(MT69Zpbmwn!5@6jR2z=Z4kjfGmowj!Kp5Ot$q zw*T=FNLvqh)jdM*mD(6HDE3xP5}q)xw?g&=Q{I{}r*R}pfOGr}j!l~lusEd(&3_2( z>aBZ&7Z~tB>JSOqQ5q-56YIp%x$?oB1YW+(i14|LfLN$C?#A1QWsqRq-!-4y^Ac$1 zy&8_yarUvTmZ?4ihesYoUVB~o0Csj7B&wT0B?wAqJfDm*(_fJhEzx6l(#G4*O=fvn zv#y)FkKt!aAo%w6f?29gFqpX%uq9doy>Lhz?(ZR~9qMBF88zJ@zN9Owft!6rf=fVB zfKaM^!WZnqbnr=kj?w$Nb4+}OyAMobD`P`CI79W;wL z1PgA2G)%v6pHsk`oo-w?MzY9|?Zr(yOz{L1?2k2#q?-(a+Z*Y^Za2!I#qU=aKjIx7 z!Iuzyd^`kjTxMewoQN}TOUfz&) zbLF2Oy^2HP5iC;DSF7;sqj3}gXYk@?@@0q%QnWB5vP zJ7Hx|jb>Uzi(lyG)@b8wnmx<~>8s&Dt_M_qjXf0S2<#$9pTKLFZhW({Iq zwKC%&J^9)2S}nkAC$hnm?oCED?y-mW_A+MaYt)-r*=8B|0&iceFlw@&8Y7wdd_m%j zCCBb<<7BCVoby~Gij1LgNM=f7z%qvjDH#z01baQFaxb<5!qU8iLB z8erycJ(43C3N5RPgJ4in(Etg#U{pxi*2ekr#iYfjb3?q*L*9?))QvO$2R_&xQt>O4 zxD|J>R9^a~nY4ixDL@u{;GCTJtcb%0-x=pM{f_CB!qn`xQ}4d+!CBCLP8|#zj_ZQb zD5e0;VGod#s@_U9o@UYr3`(DMgmO2k9EO}|*(s2n(qtrQNUN$X8NJ|cOw}hEQ!~PL z&$L5Sz}BZ902$t@AuNvHmE*S~q+C83iPI9DMy~d6vwWv&6Tk)MXX*kfl6Um}0PV#M zH(@Oig08M&(s@tDaX0HmVtc+;YhFGf^6t+q$ck(X^Pwk8ms8AlJ7j85+N(HfJXAt(96mW+L@hHqUxOj_fTc;pA&#{DN|bMZ ztdD}w0b-q)1-)X27qiAa_;R|tg@fuu@mq$rM}~ewDjgr?0f*?=xIvX4!kYs`Y>dVF zx$ez)KfyQpLC;GD{`cXfrw~N=Yk4O@fnEl?2Z9&smm4XQxE8;%D}2ejlJC>l-OGgJ~LT6 zoNK-nQeZl(M5y~Ciq66Mi5%XII!5L=jP`QErUCGWR3hU&@8%TwXNgbM)`ax9nZo?g z(M5Grc;j3ep;oj|9?NyqBR`uIRw1QsILtS3NA^WALE+e$ocHLe=AbNK%CCIwSsv4~ z$grG?!#L^s#d9O-!J`F@WRF(7asBlry_D#!F!#=F$N<}Ts2XDmyc9z{2#ei<#0zgI zxO^aQVGP!j(MYh}Y?h-Krv$h%3Q3>s0`1Kr2|X>)P?iDXLzzM@sMvh*$MM^ErvmSj zN8V3B=emxygzjmq6A7_UvwTj4DNgIi%{m}hCBq{-ug zC{&i2o#Q!u=>!K+ykmE$FE&Z2jV6BjdfX==1L`iBwALUta>n1Fi7aP`F+1Z#R9?71 zhxRQKxotafPPuHsf4N)Qz__SU`%MR-yMGH2vUeR(6{+4svL1CXNGaPu<|gP){&do_ z7zh*hnL(~BV3k&Ky^LP7MNd&EyXF622aW(^$YX4&w{7K1S-l}Z{&CXvU28+rbgL{I zRfM)3qpF}!S;R+(wyxP61f-3bPj2QZR?lZcHeI72S_YffVGyo=zv0_UCoF|6@fHXM zCTXwHATwEmHPNjT_=})2iUl$;07MIyA2mRpH53*yU>*SRlbwKozp~;risJdOv&O~~ z=Q46>h`r;f^Ra&K-flof0%|VX*2PN((@$!4iFq3olPEYCCb}TQ(A-wj3&|+6T7kT1 zwk~`bLCelf({mO~IK+A$pi+&KbjokaS2SjTh1zHf$!HzZ;&*BbQ5jP`ifjCaQ>ux5$34DCn6gfa zQ)u-^o{g`BF2PLf+Q>8{R1JTWr3K}sBXtuLw!}8Ei(0|JhLAh9-jlGAFR{Am#9%kv z_HN<8r={7|sL9p=m;HDanJaqo`GA|5BO!Jki{pI;WtQ zh3Ro3z2th^i0-)SWo+}IjtslS$RHqgv=sTG795ZzK*bND6LwX#)H5i4Y+X<`B(7Y2 z%TulmRlF0E=TX@8HMi?m=uO7V9+%#mU^cN^e?qdKR80A0&z48D;09A9}{344R90;lLA4}xA*q!~;gW^icb51JCN@Vz( znAgU0%XeTA4Q6n?2?`P&u@CqFqtof%o#_;cb@RRh4#%Cw*<8c*H9^&ayVldF06>;~ z!<$VPL2;4zHbc1T31!wB#Z*eK=5g6eqwhmC6E;0XVL_b8#9hyqbmWfVi4n>0aP$ky zcoH>Fj$JLB0{tAPYt~8SJJ$JXvUuu9MBWj4FX+$cVofJ>%?14tJ47)04(c(>M99rN zQ*qW=YoOM(-!c);nJEyw<$t)DN&B`(dF4># zXpwGESq0m}7N@O43vWcJVxjm7mCxwbIKQt3PUzJOt>Oq}T0uz`HiUbrCqm06;z0;T znRD3>%9n?Lmnzw2NHuwbA1aIw`T_E$P!Ye>HWK3`5Ej1=(~<~^KTeKtp=N?aT3jorOBG;Q{)!Nja+Ak77$|iq{PUyNQ z-=>y@TREJ5z}p22kgA7UepM;r!h}#vNWDU%Q!PJL!LB?VkQ@y(Jc$A0=SA2 zZEjun8!h76m!b}gg^iSWJ-pi(_MR_u)c3pD-YzL}?m*(b5+^|}8bRgoCj4WeHWx*N z1mMmZa4oMfSLj5QrtRk^3yvsnbPjbzhR~{>IhHo1?OoTd$ z7wurZZq_P^gKUtYhb_^WrCDivBpMbhgZXskR+`{3+z`7*sHdATN^R?9b8Dbx+m=Xy zLKkKtTnRI~e>t7zCw*@~RN3^+cE>OV22bFNd_7=bm?OG5gDeT^V$O8G34LTmP_G zySSX$eU4)`u>Wg0FqCML(v_)%oz7c9e5>*_o@XM`n7=Gf;Pn|hOgfq?KE0I}O8Q;+A)wfd z{f`uov|rq?wD&3*Zh=bj1>+GFkDyI9gdfoZrMeFWpxzmfe+hbBj2nYU1-A6_;33>P zV1jE>7l$z^yopg}@=G__q&OT-*y{b&b&+18Pq2Mv?8XijRf*#%LPQOZgGy$s-~(LZ zYWG*=Or`=o$i_b00$Ie=MIy$pl`DPTb|XjhV)iry0fzrIR@9VB08k6G8-tS7MNYP^ zDqu}Fzr4jbm?+PEfNEku1hfB*F(eVe^`QA4&87GoxTQVl_nG`V{4DScZB_yhh4gJg z<7EAjofgGjEpelSoGm~E6UpirXE{ry{?+rw=JJgWH_9<=#-EzUUnyAy02Kosg|Fq| zf{Rkww(X->S)3aYhX3<+b-~s&d2WD41bN#x0(%s$Dj)@7dxS|4;VCpL;6f^*VEVWi z_~Y7i@cxEX4w>!KY4|FJC2kD!?m=qyu-wbsZ5p-9^g_4}bejTFt(YmecQg}XtCf%I z$Vl=gM|VxM?C!eoVtmQCBwdh*ThVY6OLQu8<7KlRP`W&R;b8zAqCDsr{)XJbWRrP? zBmwC26eUWRnEVS;j#>xVZs}v>%Rb9wUz8thQq*MeNUZ4e(!d&&aXi|t*m}bQMiPE?+Q*^9=OzdPs z6U@_+_7_qw-@>5sfnZofHM-YfH>_eY_(szOBi}h~knk0hhgBd%LQ3~f2oPxK{yk|F zmd~ELk_4vcY6h+2UAB}L_^oe&fR}mP&%V4rRqgMI#JD!ah`XeJ-8%D(R1c{PFK-J< zmGZWUoedWwO@&hS@e}Kz;Pi^Zjx2gXW+;KyJ(+ANryV{>yh^Bw=y*{?mREn-h%vb9 zdpYp=Nigu>NA(T><(tlYrasSo%C?&Fj+I<%i#D7;|1Y`Hl< zd`6lxQ9*d#K3fPSwn2+e-hFcKPj27_a?O$>(73AgToW<~wq$^QH);^I|4Y4Zhne*U zzr>=rL?WH!o!3F;sDO)q>w5~CM8|AWI1xRr6JVa@ec50Iu`pJTuGF%!riS*YWJ)~b zm*o~V6EKJ7Bwq364M7G|eKocoBG2~13T(|N7Y%DdQK_qL4h~JZQHv`#__uPrinC%uW}dfI;Y!dkx;T(r+^P zNa-Cl)2O&^EmJKQ%M6~!q2a{1|IK$3JPB$g3!#Gpu}mww%ko)p7E7X)Yt@PHYIz1R z%tB&zWh89A-;;Dd1&Yr(1;_)1DgSYvu#rl2ARio* z#P|1WS6;EOW^9p=1{ptBoxKdrEiwQ?@7wYP2cV()*_4`;^pu~!Xd;o+N(1_cG#DTBc_k-NkScpysh8B+DEjt1%i!Qdp zDaxAr^*s6Tw&UfWlmebwVF0%U(%qKDd*!9l zRL?KyptD0C#gmh|sd9O|sYRFASrLiBh9Uy7x$r|h;!Exc+5UO0Dp4UzVVU@PWNte) zy}|W2vgjWd$ddG}B(iyFM$Tu8A&~fi?0q6)dGy4{Y4({U&|;ab9Tq=T>z?oE_>82M zd8_xPGVqo09e#_KBP!jf3Z7#>9cE;H{^AbgTc?mP(LgfA1u``v3Zn-fH9)$>4;IzH z00H0t@spi@OvR(D;0@X)u&W^m{y3UA5aQ#2tu+}7WKA(Fai6;bKvxOE4rKr~Y%uPc z4rWS4#_kuG@@3^oE3Lww-R6SD4GK(kD)ccddi}U=)*te(k4}{n9HJ1}dkKZFh;v(d zf&n80m28fMBSxE%_Ee+|P^pRI@Ub+`e!CvVWX!F6M_mluvr;J!RK{MGKI?BSoB<{3 zYc+;$mwT4S&~{`QVIbY3s{J?sKltRiD!Fi$geOC(gW;ujj=sa;NHueSWdaS(SR2X$ zI>bakhi^^`va)|&A?atqduX@Uvk3?q7jtVdmzdKvZ^!sGTZM^_(l4VzeLB3vWPAfX z=vbi%I5Iu#9c(;TTsc$Fz}vd?kc@Qu@yKrAWNBAdk%l#m zMpuN=BVD9 z8LPb|-mUjQZZs&$3r5#ep&6;0hv749UZMeHEk~@S-^G1 znVik&uoaLUe}6@8fSZ`io;uAFQ|MXo&JaFCBoqx9ajeK}E1Z)GF8gqm7}*djphCSs zD$s4{F?|j-d(?Lv2dTS_aiuRmhZoVumS_pv-w`uBK9vVAr$&TakTEEIQ zY-h0P(wq1!JvdP|b3N56Jj_cZ23FXuDXo1sfwh0^6zuKAi8|L3%USHX&aP~Eu9H96 z5}~i3+EeI)C7C&$St4(+x7W!O!zDI?#&quCDw~&X`OKi+m<+#Q!cSiXgFe#&03@-V zI`}dJW^_s4fw-O%;$|#qKNQCvHVnngKJ3Hjby%t&`H6enlmRg4$nm?R2%uw1HPAwd z+dXTZLp$-JXP-@kJF@d>G>$tK6Gr0e!(6di@;pjT(6b>629ikI zyL*PKPh^hAQN%j~LVTCBOwwvRu{0{NNMa-gTV%38T76T@c#i&zKuP&&I*itBbh~6^ zuljZ6;c_RO&O@I;q<>gE=cX@f%?6X`yL4m_i=2jX3{SMZU2&xP#MYeaeiy;Y^+N!CvEhCI{)z@Gn zG7;8R=hH0oY03wxHflEjU5)a|&ETsjWU>gNbyBBHpS5Vye_Zz<5&#+jm=XXo#Rw@K zAVHz2kC`A^j}YhpMa57>@W18Nl+u|$DNbf%K%taa>79q#eC+JI$mN*7c>99g8|3>R zchkznk8OQv(Q)@|Z_<4D-1q2Jpmf)@wzPdWcY@ah{_VOXRm4QbN^j><4xeMVzD#w1 zeh1!QzbLcOY$vT&U&5(#d(Lg<9DuC4ktAjMb=3P&D=|>3MSWP3)PlbFda3u2C(`V8 zqdvdRl|)!L#ZoQ_mc%Pyu1K&hU;8sC)5FV&uxWpJuqC|ZG5%9_T^1!|mBCsfaON1Z)CYjLXxekLV#O(BCj2*#KEwiZv$ z^>8FtAm9Odw1ER^;?beH5(&bX}MF)rc9a|B#iIHN~7ZDe+oZ5gu8sn@&A(op`5cjk8BFHXJ~T0 z>VVaMF*%_~d$>W6>-my=uMpxJ+K76M>hx?sY}Fz5Me~vZf>CLf39#c8KTpj66gNVE zvo+G_F*MFv`-N>lmW)z&%9gBsD_1d}Rv+_4xmy7`a`Z#WflbuUx2%sSd5Vtn!+}kQ}E0>^Fz&@JuByJtDHH8bPO+q(`IkbpdE7K)KDI#KT@ATTO;aqWT$Mi7Ydx|k4qa&{ii}P%JeyM^R~8+|3*^hI}vc$ zmWzH|aWi$JFrK9;)rAbhzn$O1;3nj8Xrey<0dtZdo74H?e(4VZw&js$3>k@5mH6f? ze)*GwsScGfbEILZ@`)@si0EzMXn8OVU_1fey{(+zDXi%SlMV({iApbpD_ z6d{U0lT{TGZlUO$;7Rw)i+?_wI~_^nzWP@xMmF~R%KZHn=9hr1@d_7~7e)`cM=dP1 zA*)P_FJ!K%>lQ?p?^l`geSC??`cViW=t?8M-ieFQ38qv?1Qewzv6Eb7k1|n7?=pT} z;I}%Kxv2K0X*;8;6iHmfq-b8{NWn&q72+B_4wU0Xkl#?DG*JqI=~RBMP=T~3uLJ;ZxjQeT*owv9u!Kkl%7z=;Pm}-kiwAL_iV#2%2DMaL!#mx1|K%yOMn*aw(W{ zA>BwS>^wZ9?i%r94UU|G3=OJvUlIUrniNN$tl__0`t-LzS30*$e2RUsb>-#YoEGYp zniLZW(wmf2i2);Z|K8w*uctb=RGjIhC$!d_`Xn&at;(j|G3O}B*NjP6r@#x9Xk!)LbbD$3rB20*iftn*XuquC9?T$5`E^Y-J6C~v?J&z{&1rm*|OR9DoKW(cO> zMIN#zry&)0n~UvgFbG4rlU|5*^!PE)bJS7iV`%_v{+o{3fsjIA8ikYR?s(Qor4kYSX!C8l!Y z!n0+U^K~I&4HzH3{FaZbuR@Lu4gciCMBBYkq2yF7u=KKar&IW&GWWn}($d@m#*<06h`uUnk;1U2D0jLrHGM@x8 zJ0L_0vmZ4;l6ejmumF33paAiwHCPO9MS2oxfw(WPoCjulUj^q!Fwp3~_O~hG?LmDD z00p3{b|OAY$J011t^UY@6TnQDUyK3rJ5~pB+yrtBhX1T%+nhKU_MiqQcG=lpet(7x$>$CHAAHk1Y zz#86{EVv;zejiXvQuBycs|*EGNs#2x=c&TI)T>ffxhe;woT9q%3IVVNWI|Yat~y2o zZBpTma9!I{;<>}%+dAgIQ(1^Ky@V|Y&!Y~|n%a2w(cF{UQy^J{VaHJ8Mbr%(|jyeU9v-&e6e|zpBO!kd$ow~8@ z6q|p!^4Zq_wMJNO>jY6V8Dp=9Z^{w?G8QsCXaa+?1IWr@fjaUHJwj zc0;rlOSKPjZ=@Z{Z`tJKyu4q^y@O>aO5$D_nO7j`7woskwY8&rj+8)*y8Zb7eWGf(D*+iDMmoW z2H(cv65vObQ&{recPqc-!r$tI7nXNd=)GuzUonmm+cbE#U0QhK{-z1l^_c-NF<%vP zvU`=g+R1Zj;!VuXb`_@x5E*YD08JuGa(rC@Dzh-1UUgR9udj!FS``I0L!m+DVsvb~Za7c)pWYFu@ zXt3I<>T9e`9oG6GuILba$@8SQh%cT?4DD~1qvwX6sJF6r1 zJ#A^l1PFGWnb|#sz1>4(VNP+QGXZ9?crbx?5!~x16f2Ds@Q+v_F-~od(fj2e86kX# zOHNe^7sOUXew#U>#o`ZK-Hp9t)#R|Ht5R{L5$Gym>JXfXPqm7XHgw#NMs11*?T(mI zWUziVXtsV*EbU)2bhIwND*GfA7=ujoI+KIxC@mNJ)V@h6xk&(b$gkYVE)lb>WUSYE|&4WJSEbve{cY?~$=D%_$##bhjeUbYM zQu2vt@Cq7O$0ggfJjbW*EP^>?<>!FV?atyE{p!ppdGThsdoV(XD#DSfN@E`r)Za84 zrVtvr&R%W)E8Fx}akn?F)G~S`)6gs6+K^vruE-k@G1djZi$d`3>`e&^&m!iWce(yQ z9Mkp2XyLf>`1<#GPof*>#u5o=jB6?Gsa*Y==KgNv_!QU6&St@|>NtQq^n{&g3Ri@c z+L<-G>(tc4nM3;icOr(-S|eOHr;MNRy?w1^&Y!8@vYmpj)5J4P8>+eHsbe-!it+If z4VaJ>ute=q0AJ@r*!!ILCif5YZ-{S18V-gcuv9vq!e1KPe;#ioaA;$F7bJmyT#!a zt+J8X2DTVJj^9w*2Wt#j#sVp5Cx~dag<7Owa2P{ibhbnGsrojw3o7~gw2lqpZyay7 zNK}uid^YfbI}cn!w@F8BHS^sBfGZUsos~?~?IAZeNt>m7>!PZNX)8XW z7@pE`ajd}vm_v!-|Iy)0YoN62MQMXC0SG8atJ?5*-jk&-J9m!=&FQN0Jh~fn_@P4< zYSN{BBMy&GjuUc)siSI!Yb>sm_qN7(T8DjJmzho+q7oA-O1Ax^e#r zpU>)Uu@qN~p%ll%!=$C~h~)nGF*67AmA6Nzf!F4Ia*u)ENM>55?;$PEi}?WZy74R< zu6H^BJE=J=`MbTDOAG5Ajv4&)Q$xa}vPK)@JWb1WMw9&|LJkS04AD(;i~o3H9I9My zVv^KOXLNgXZOll_p6+cj6CyNQUqyDPE_hCRkEe*{ThUGsDOGCk80)1rO2zV$@wj>1 zYjsd~y@4bNtubGr6YkQ+tx(Y>r$H>lX+q5POCq14uy)ly2STBG?i6+~zN#D)5Q=JX zd#J}ng7W%51A#0;t=(dZ69BOWf+IT#>mdHLbD-6o&u>d^Qh;#jIJtHVO zx;x)O%oJNUP#T;K|8^&TbwkR)%tQW{)Fxt&J%cJ#`e>h-2L{@K5_FNGH_72Sq;IHT zLe4bQ!iiP}6O`<}85by(97%%}S=P#nGqQ+w%r~f_fL#2I`SaLu9;|E-Y4gXHOIhf* zaCf&K#J!hLO`@Zak^}tJWPF! z3{XB}WB%9o7CVuqZ1b;vW$qT*N|*$s5S5i$Cp9PVX}RJfk)1wmSW2N{JpB_Q(BrTm ztO308&CD1LzHPo@W!FudCTw0By#+$S`s+~4d_}xe_?oy3haHd%SjvPVu^WH>VpM|8 zgNOzz=7#f_EIqf&8CqbsC~LPEMCO#RfxnB24RTCdrqw~_S(RHYFF0etN}o)7NqMfm zmx490sqtCj?+-T+!zOdyNv=dZWhEkg{=IT?n$lur>M_*3bzPtS4$%u@SEJy}#=W|> zVPDpbnYq1C(s&f3b`GIU5B>ne;XJbH)zXFHv%{#=wfK6I!6wPA^8 zr2%4zsOgrL5Q>J}w^U1+vhJAQU9qYt1Z)e8?oiSzPJN5T^4D$K6A6C$i3u9sX<)2)s&g2CWo98Z28Cj(uQ8Atl;qIW2u+ZZ9n#cp9qqmviQhL%k4@%ZHV zJl8aEXKtm>rFGYFRhy#4C9qQsqu56C)X!XT811@=Ffq9hczk^>o)4fy9GyQsVnw9a z*~L^z;x(U%h&JkG9mFccmx$0hTZ@gfm@GZoDiqHfQs}Z?)G|gtKMP43H8ovU*Z%!m zr3HEEhEuVD8+CyT{~wnLM;{P7V@Bu_x1WC(GPHP!U=0Hqq2tV3XDnE#ZN3f{?S^{t z9?Lq6-~0+NJ|0#0*gZx&4=wh1B^EP@lk3T&2e4+eh;0R917R-Bpm`^c*vA`oUlv*Y{VtObp1X2$v)%y(X8(W#HJ<>( zYODGa#ow8_8;FtJd~kHZxIqYHN%SVwN5~i=KcW4G+`lJr$H+bkoOB?q>x{Ul(k{bW z9xr^^N6>q=*${-k4i|KE#&;G;!IN_fIZj{j@tF-pWdS}{MmJloWKD@rFE^mir~K1t zeIP|%sR?!$zM*)HV-)a*?Jvre9pSpdgpvI02zhjY(e{41V>_jwBdCsg5jqL>53 zv7PD4vo~pKU1i1>4-ph-Pbn8}3%tB+viQI33?7hugc&i1LYZNX@^TdTNWRdk`Ngj+ z)~TVu4ASywmJ63f8OCZ(eNOX#4#pa(PDwsU(4gB-M3dsl>FArX0`d>jl(D}L$PfVx znM;+L<@tX0W$V#Tl)b>X;ZW-Th3?dKFBYZ*Yx^xkz@*=dZD9GRDxvnJ50uf*6(^BESKa#Uo2f5+rw(7XxFw)xTtU^o~N}1SMrY+oasi zJ6!nSa8%_iXpgw%!>OW!ex1$^snXqKMWhh-w=~i(u&+axrQlI(!eKGx_XV+=PGEU* zhM~x=5PQCR(R$9pbO1s@?&#d1oxKGut$Zbi{C&#q7!)6V1whOjX~1|9}H&=}7IO z@|+C<1V&$gSy9DND}dv0-ki)WqQkfY69?B0GbGX%a2W}I4zriVFP`iwaMo!>3Oqf0 zH3rB<)u)g=B&$WSeJFlPzLY0N|0Qcq40B<@Z}w^~zm8|sn4yHbX-}iUPM?{DXHid1 zK%Z{!Mi77gS8Jc-4kjsF6}j<(KKMCoDMv z)&_77wM~uqvoc@3toOnh{o1kgi9;n5-__r;V!c|u2(BJ0c6mjL_(?!-8I~|%Eu_Aw zZxpCQp~lQO7`_lb1f@m;BpiZQHgqo=yA%+&vq~wZ_AY~R_yMPZl;!x=3(6{1%$BOG zZEX$F+Uw>rVochbXwX|;`EnXb5a*jJ*U>R;Ua^DEgu5Y3L?@=ZEX`TM|1>RR!S?ASqr~Ya~tmxFRhpyUmw$~ilWkozOJdEj2`6n(UAKt*}6qnlU{$hG~@C!(yIfRjYwQ+XCq3iGEEge)2UXx>RkxJYMHFz(n=#y^6~9a2?X7R z=ukvi*yIrM;8RxZVWA_n5G9PCd$)YT-6kO(yYo27EopYRcjuu{2Meb}^7V^XRbzXJ z8nG$#?skNli;a_W&nn*w%j=YVHI}0imJ$w;#?C+3wejG`@$Jlfu~=GRC-AbU9x;az zBS=(EfRr2gdGvFZE#WO2j?(CzDm82_;ip8dO8%gxiAaxJsrJ6H${evN)62sSAM6f8 zSYQmAAuc#I!nWNd!$ZF4nh@Fl+-13{B8Kwd&IOxqXX3&;c?*lo(I*d>9h(4^qk?#{HFngOI|{T;gY|Mx11i zfDx}?3K6@(ZJ;*E1ioWDPCTMRepU6V5H&Vb@Qp>ruHG^#nVztER@<0VG0)l&O502{ zLSIP#OIx!t5k#z<6+ERV>yCgLNZ6W9F zUhoG-d?5*&y4qN4U)r{ESoLIH-755tPBvmE!&X`OLR0#PgMntbcp=of&5N>`0jY<% z2KxsEhhXDk?!^Z=1tGY=7ml0uF{HBRF%cRdu1JaGyN@z%lfRB)!a@TCHxzJSq z?Ez(nbp2g4-z-r<@G4a>P#51VL|TXDFB!Y_Tvl_4_AU_m zwFDZFCu13u1tv5u^X)y2|1vLf)BNfEB0`%t?N;@^OXA`F2H!FV7g~98Y0ViY?a#Gj`(Sc?Ikq30f2B=W|ypXj=7U`pu2<4szx-FuAoB^{lS~5qjp4W*&Z&cJ-X5iI^Q}roZXv=juH-szIWWpe_mIv>~*{e)=Im* z`>q0mT-VmR74{NkV&T$yV?lY8$DE_h*)9^f_F^EXlLDqqMg_sw`%Zl$;{iX!6{SLDm6+XdX@) z9ag|j;bP**2;y8zNS53IL(UBOw`>@pgz9wRiD>xl5XyEk77BJy^i)7N5&yznqveg6 z9bW?zTK@Nn&xy{gd0f}uXH3Q43!E{QVt|I|izXHeuW=D9bRdN0%abfhVhx046Q|%W zOxr&8Gar9>2UeWK?eZrG_Of*Gchjk$RKm$1r{Cf+2>G1i)#&1IV*t-s#Hao}1j#))v1d0EH(Pwaz&9Vu zem>M`l!r+|jbJmna`2#Y{4pGYCj*YuTjo-5sAxC-k7E^-*cc9Q5!gJxw;-4DuC-*q zzYPl+O#F_5<^zsu{{yY`>S-%*ZdQlb`H&e>5qPXFaDlb&t?`1J&tXF`|7KX)@AZM{kj#rh&saO?c~p*{FdikcDgtG;&srCf4_Vwc1{UB* z?Mk>54`5VO6i8T=|1S4ICb*mU*3A~to9=CaJrJlv@Z}mmxScIwmhzPYTUmv;oVK#A zk}*wKc1jot{L8Z#_QSu2*mb{lRKhCmqpDiPP;+RDkcrvg2a4)NJ1%O1q0(e9(2*~^ zlmwJT1lW6lDRyFR)vW8QRPG8PhZ0fLe?9`ZzI?N}nbHm^`f8AUIJak?iE|O*+nyqt z5^W+2ZH}Gj2r(uE!NrQV6xU6^{j;m0)*MsDgoAQvM%I<1u^s&98-BcAwbFf&+k>{b zCpWZ>_QoQOdLuvT8n&Y*tw^v&HvteCZPbDW*3|4n_@dbeR*+XpvC^`O4^?42AST|N zPiu-iUB|T%K$gy+DSy+c;M1VeUxI^M-kz-`b3uq~s%xp;<*@(T{v-W=KW~PVGp21{ zZ6X28;(INPnfwib9a^<5|8I%s{5I|}znaDePf4C?=DroE16!IoSBFpDbR?$fDNVu# zwN01Q=)!KZzRDUVxDvw*bx5)>Vc*OR8rGvx$N2po+k(=H8|uJ=$CO>bJ_*y;-|2r(d*K_B}vZ+1LXjW zjX>`evJtc#CjbK`|s%HXg4-< z6wD(%>p0xR*-w&xj|$>6SpKJ53y^vJAnlL+M}op5_y#K+ro>n2KbHafI*^Z)=G2#s zLe8{4uJ%IYXL>HmF|VAG^A%Yojw+HXMN3%#l4sOyKV{}B!r)Hplrd%d@?(w}q%(KT zq`dfwRnh1ud50d&K|oEO_Hf#fBoYL3Wu?tHTnx85pu2n>($aF$%2 zpy$xvOAF1Naa}M5-aT%YR68E*T#ia!@X$;7qn|MGY<_z1u~Pb)tQoHKSK|KHU|Q_> z5Qg>ac`Q_lo8hHbrrhQoqdrI&gw?y79j=S9*yNwF!}{$}LQ)E+X0 z!=f<9H7xQP7th<80C z%#00{<=eVHTQ}nx3-~pFdN~jfe;M5U1i?m5`4XYM`P48LP=4I>YR!zw|b66l^@5NOXCK?m_YLig=wh>=V2TNqb2t+g93uB%$E`5Y@q} zDbHHK{U^p=pZAxFj2(}#-_Kjr#b}KLz|4v#VK6>sSyhJI`llyu3L>s5b+i@c zXB+)Tq<0usrOW;v><@E15s^``q1H*TDa+mXfGjo2^U<#lbzR3?#+-r)9v^{l{a1v( zpE94gdx(pPb`J-j;M554V+msY+T4RG%W!E%Iz{_*qG)XVMr_vh7VlMHe>R&PlF%Rk zxGs;%g?rsyv|vp%ydx>vz^1d5NeWO!*D<8RBV+5s#XBF@sRV|w8cY(!D2&^2QmtH zE%bB@UAndnc|WN|U0UPIc3cioWhHTM;E)7-WMCy)Ju72y8;DSN+g@VZj+`tRURT!z zKwC}m@=@jYocxt4)G}7IBQEsT2+pi(8jO;VPg;w7+x+GvoxvE@$GmNpk)Q5SB>y=| zw_`CI(Ji1ReA?btU*uDEw@;m$Fa^$8?N@r7P^BXm_h9PLLb!YE=Ppa`zUi~0ObY)? z*2b3sTf|kNiOW}_mP)1oE0+|qaH{&6tYbejEvz%{Jt~!epq)TIDC$PBpTU@J5KvBF z1$_IRJc<^KI}EFT%Uga_l1^ugb+;~S6=kUlD2igTd5^yAvK{A~)?D-a((ZvrOl4SR z3i4#~QC6~Z*7$W$AEYZ|E#E4~a+ks%u?+a5y{xUpn6N~6ecV4p+}Vhf83<2Blo;cn z7mJ!RkuW01GpGm39zRhEd8x4SShtOqF{5&=V4&zT@qg~y9RTpzk=Ih=n%@IO`&eK5yV1icR-V9G5#rzg~>^J@`;-bDTs?Nb9#+_Qq zdGSb#i;7k5vW$*osnep$NGD)IEjXp5oHGYqiM2zw`Hl>l0)YOtn#Ot`jI5*=@54Pw zho5wAX~Og#zUER%kOg&rvXE}x2Ft~oHk3K*S)3CAW`OD>9p&P7?RG!5dm;f;#-{`_ zGtA8aGjS|c*7eKZLe(YMn}RC@_*j6decnGBLlF&8ecigw6 zj_?b7R?_d@&^FxdEGo0`kX0tT+PoK~*MRwj&fq1q|GNrk?Da0bIIW}KPsHOZ?2Mqr zEH&L6V2pOxZc0e{ONf6i*$_j)-h*KuaqnvwXQyGC5l%bC4)D7{<-A{>FB8VUQ1;_M&qD13(x3*(q|b=zFWizYU#(8c}{Xgm`BU0Uu`D|6#) z-#CS|9~NL@W7}NWHD@0SA(6g~TC1wj9JllRqb2~sfY#5J%bHC%1+F3Q<$hz?|4Wf@ z#MD+EZ{CVlaXZ#=(!xD#bm%zU3ChV#D&&(N$o?1WJW5W(06i*tc+P+1v0n%7GsC_s zhv1!+uYp;-=kLA0`XLNZd>~N-!|@5SUQ6$ZfvJM%ivsI`Dp?*n#2`RMPv25`Y}a#! zf)}93WP%wKnA+b-+7k2)Ogry%_5EyIg*dIDIM*8**g^A3a?jerk$Y7!APb3r#W38T z9!svUYQe3f4N9&D6jCR2GC=;E(EyIpu&B_fM{q!|@?Njo;Ym_!@+=*O2Vx{wok{J={Oiw66mG!(6U~Z0 zS;H@cx8{*Z8Q4H@@9hhxL?;@Wco#Q_4x#|P^f=(K<;|DW8AU1K9Nxy3& z6B%H6WY(U>Ix|#RwT~#+1zm$=uamT0Io8K1TsAUB8IV_H9)S24;COm z9su!^oq!(}V;01*00KM&`5<3axzScyN&0LEbSb5juq#jJ6(W{rjCprc4@L9r9*%jd zdcH>WW2axfCbUf0gW)AL${jLb++Mji$3`|E6;KS-F&13GKwR8bQ&_LZtnJIArzvNo z4dCQ&oQf^DB6lY|jEgdCH~*FX)?ou)W@r{ml8lfk0ghy4qX5 zFcGT%QPe$|YwDQMDUTnR5LX@Y)41u79<^zP&Rg@#F8U2AOJW+Y;lARsG6e;Z>nBoU4<~x7()7&Af_ZlJrQL zdDTP8DwFj+2(_#L-Y&VfH;i|lC2LSMd{E0!D9!#K@Np)0r0CZ1Dk+TX+&z=;`JzN->Ex zYcDs->|U&4A5bVEUa33NzuLeMyd)F(Yn*yRn`9F$Ue_M?@B9Uyx2R*E?tQYXLYrbD z=ApbIOmqV8?<8f15KWOC8dfM#w&Zlmehqb{0xN%lA^V zV$v%!eZOMyRr<|<@CgKYnD+l>nyPCHwnZ5)vRUKtvV=D^Hpc}Cm6jVkCxsvf)|3+& z$h?l%e_j%W7DHfpOXZMYZ=VCxR9o~!}^izbY?lKwWfeO(7$m&UBh z0p6)T>~Xo{C#$@p_o+p!#I)lH_G9W!>?)t69(9`J^QoaXmQDnF)tCKXLkzrtdR*UM zwPzliTs(DVbUyKv%DZI!ShMY&5;-b|@za$qB#}0&GVL^HsZeNVM<=D{QvmRtSd;d*i#db<9-0q(F;BVxtIf*Jb7BT)G0z0{Fay$RUOj{;r?yobExrI{^c zi3xMf?&qX*)y-nY*FXjG4!7#B7ZLk`XX}YOqAeduK<^?aynuH8x*(YhhC;g2&>uQ~ z7dIAPADx_1HJ9(}5Z}El_~+(+Do(etIPW(|Y{o&w%uW#d+y}X>6U_-WG%|BQP_$Fu z6P_iH{3?gRqN9KV(uWf-OZR7KoEVhg%cAGglX~eqaUV&K!#Vs@2DLJ*`-2hy8Ue@> z05ZG^DWU)rHz32zkSD@}MZtmpfCYmXd;olZ=Fbc%C|^;?Oa!)spVuN&?XhpJl_V@; z8SdyA$Zg4l1tMamtYqU@t%|Qzp+9$Bb6`O(>&|Aw8qrR)_eubc%eLnAOCc3za1_Nk z=*jVCE%qbU0EyRq4tPOA785*b&OQ^n786mt%6#IR5%Gw((B4xE6!DH<)H(} zL0sKSExI5rD~tb>Dqt$Oxf?O&CS!sinA$)ft~XEBZV#tQ6?Lddw3)XW6~z7_a)=;y zIFq$`UU%4`5xDTuCS%y63Ex4uIdpnH!JN5vc&oDqhlGEBt`mRqxqz-|A7ojF*y+=o zi2czw_DL!8)#uZb7SUSpFcD0bGMx8JHNvc{c&KN!g>)oSi8`er?1_+(AK!kGosgR# z&j10@%=6F1029SZU|IR^VKJ^t`Y)9p zRq0Tbii%bn?a2t0O$|#H8wc?38iO==aLGO9v(B` zzs)3?FbszZL{Sl}0LrxcJpRvaktBiU)xFZax1_k8#eJd>;(+MxziX})Rs>^j^pgt2Frui?J5Dy<*}(xY&zvZ zuP^c*$jVR$^NP;QH883xD+VNEgM^p#PqYgd1ubau7JBJ;?YW*ujtJnRMr_z3R@$%s z-*Ma-YZQY(P{eW9Orbs8_W8=tZ{EWNcFdS^!%;W31}uZCWrGRkf%ND1Uv=4mdh45w zn(mfGAt#q+&9nnH(ln^gaDL9+R$uX|0ijsSXUIYw*ojgZE^fa^`CwK1eZ&!o9_sEp9C#ZDWSY0mL@kL?1`Q} zXdhyol@>JnS_!A!rW>WKVf1_046DO%4x&3)8?qS`T|i;D%k~lEv6bOOprMkM4>c&y zS9%f|Mq;!fJ&SxBsD)6?rIo%=JWvf}y!fcBYe%*DWL*vACxjIFuJ4)oxja}f#f8}U z9M?l8%&eu`{?)ysu3o;Xv>8sk3!(2ynh<;^6NT{;_q^wjwL-3<$EfX#c&q8@wl*hH+TIPmHvW>iR6 z<(hQKaBCz;*JR{T@}{NuLo4`susoe@mxFSk@xcS@e&0}gM!6=qi5A!MC*><6`03I> z^pqBx^A*c5b2c9uJb4j51;ml zGf8YX5ny<~`=PnzbT&mHXDnFjDnzVL`eG-qJpg196=$8BxvcivivYBkGO7qEAs|FI zAkUc~Sto{zkuU&$BFix70DQx`OjNK_Njb3t0qPlx7@@H?QtB|ipmrjVQ)jJfh*Xu} z?M*UcC-3w@WtOIYMSW1$X#(xX4$8UzSP=%{k7h1uSvbh7a1EBx+010Etl_i3whbA> zW#5C4d{qoQyvqxmQlPsMdSG3JYg-)kJa%hYW500wZ2!TXT9e>l+_<3ciucJPe=N%r zQbxdfB+=fvgQRN_pHt!KG054stRoDW8W0%9rV*Xs0JVnOsz%JE=+sX_O09sNxpW#uVq;Vgr(Th?4$>caDk zv{E3&u9$=77iWJTdw)#{J;liU!_cM4W`%kKcbvq08RikGAQdmbo{w6Dgk0^-RS3-M z*?EN9g^*jazma~M8Ekw${J}H%_^$`yVhjB|(nTJQdGfCyiXF_}$wG&bg9cE`hE+tq z>R1gN6=6o$TPRj=0wt;p*x9>E*ZboPmTJ1?1&<6FHF=t~k=O6Fk+5N|Qe4$Lu)$V< zHN|pe(>EN79BkA7)MddRa$}-_7;-H(5O6-o6*Pc|6vY*V_$(BsBdbaD#!1X1ByuF? zCRoD9VLjdR@o&XCj}hMpv;LZ(ae5fnq=Tu0p{U^#)+f2XXv_J^lln6WMVv1q`3#N? z6YlvPcFX;+4J9vK#qb*Dnav*PnMr9coL|;*kXkRvU)+NPyr;=!f8b#P^}uC`vG91pXdt$0;hzo zEfo_#6bB%kzDw)SSX=d>;!}66}3%!8dQ&d_If?t#pg0WHGPO6H!VF*1^ z7{JCPF3Hf4lXe4pg2r&kW~(XmSMRTxl1hvHqH6v8xVu-OV)`@JotraT}k3wLU%-J9Ef)=#_LUkokW)kh#kkzAd1iw z?ZWSjtoIaZ{ks+Juy(XfK?dLRke$@M&kE~fHe$Tw@uL(xDX0KX*n{lF`Cc%qx{g+E zfy5)NTCf$HOlA^@l}8GHOQg~;mETRh7b}vFj2|im25NI1JKT^d1qmLk9izsr{rVC+ zJw!%Qu06}*FFO=}&_skF26|)>o?#Rr#?A%d^fVk}mR8{*{IIU=0fMb1mFsLzVN?I+ zV}R*X(%q(IZC~V`Uls*bU+v1;2XCn6=R` z3lZ<{;mv}8iWLaS+{YXW;@EXJcmFyorJ=){(=7ale?@U z?F^=5OjmKu30xCt^My8}hFc#NT>e9msz@;3yAoS3nbA-c$>Z7kZdCtO0b&co6q1hIR9B>dq2of}GsSiHfiukqLOApR5Hw+;aaJTv~56C@EH>4qfk+9Quk*+7Ec>6-5aHu4Ss04A)Fj#y$k~C3Ri~z_y##8(rj$JDz+yod`BB_f_J|X*Y(5 zf68FI7;NSUIW$! zARvnx&zT@mJ<1DIV_^XB0P$bEorK@CmywIAO@Zq1*#BNvQZ}O^iMYfqVW4+Y?CCy} zk0--MDy%bO7D1EbLlAmM2F)R6m$Vf%_SD7Pe@t#@^~?$s5sf&&O^o9wO5I4kOcZe6 zseU)_*hT5!_O198E7x6e|3_oiql(rEQFif(5%w(C4e&Wtwlfq1%;*ubI1BL3_EFrY z9?fGH2FXtNjEj<$YO zw&~wen>m6er7)gos8D%w2WKp$KVRd^$N0OCRW!NDSnv~Vu7jzJ7heg+Xjw(2`nKf2 z_-+Gqzp)XHltEs2z)rD;+zHNwJ>Ipt27TZBGBzK;jlcjHZlhDpPxZaN-e&7s_of^P zkME_mFuLGzass|vJkjfiLaM|&4n?~2heG1%QUYX<50r8|ETrpmDXi5(jIGR~hCcYK zNXf<&L4bwy$N7R-3CZ&Y zb=!-=DPeWw5M7s6$3bq!HrH}6dX?1cA1n8z(z?lvg(&?X|ALLaDD+sA;<())5gg_0 z>G3`Zy@-`kALA4BbLIJYUxircD#n!8(DvWZOC2>?BWzHs&f0_jC{iiY@-if!N*?ZK zZXX}E7Y>O9i}N(-S(H%CS;H|=LeNTbzl@q&oc6pk7~N$~`yu{Kq`_-9Ql&tOse%b+ zLA(^^Vb!P&_Hn->Mbpa3?BHN?SD{1)nzF(Wt)`Ii?fs_uzGe&T#aD}%jncvz@A_0D zU))N3o9J9gli{fD28O)D|8>cx%P-J7P1tMCw_N=dh~aok0RjXd)^xY6oy9U$NFJrw z%fNaP{Cf-s``INe3@}6eDcOh}QYFvz=5PU(1Q&NHI`9|U z0Pz0NVjq|t`tqD`2*^fee~XMKdX+E}WJEg|i4v9m{xEnHgaQ*LH}U_kcJ3_s&WUOz zY#jC^z4mFgXN_z%5miR?^Mp|BU5V#p1XPywtjx8&5)KO=awpn)5zhezkY2c-VeP`e z3v#u*XaTnx2mE^E!qim&%AxEoAybTTne~7dpK4hJR1GYZ*c(P_rxZStQ&`jzJ6R_i z)RfwHWgK>Pf-KY0BhToe@9OEHm~o;tAsKjvNq8l&V%_T#=wyISYh83Ja@Br#~F2y&1B0+-J#n2)RRy{bt z-QfoPZ8Bf8IIvsZJGq1wyB(Yg-<~0m!TZ>E5J~}qx>q(9=nf+O(m1O6=Ss=j;F$WK z%1=LAHlv1m;HypgNVSOgw;mEFXM{Q8EfbvV@9e5Qu_NI70H!}tirR3U@f6IBh*Lz6 zv5FM5MoaYHOw4~={*sIC@^Z@}k-w`@G2&V2QkEmgJw2qii#Irvy*rCeYoM1OD z>d{4m`qbf9`K5wxcSic#&9M(d3o{~-&?WzwCJbkdB(LlEj%<=PHoYArn~~iA12C;# zuZd?JH*pi!^=exiC=<-mk1+o+zoA8$iv<-*HGhCbMD^XZuV6breMa6G{NZt)VgWXX z)6(0=lN1+3Tns@B4jMz|Zgs;7%1SWE%iw`Y`9D5?c&Z zlZF%A3+w_bTe(zfWydPr675)>k@4IpoHkcpge`@44V|VFnz*vhbfaLEg#RL#=p`XV zVamW^4beN%AwiK9tW-7sPhJ>L`IdiX<Z z)A5i3S`1y$J9&k!On?#q8UdIR05XRJGDjdHhLxW+K(_S_7J@d8f#3k~leJXmXH~Rz z3K+DBQBm#yrsURfYpO{vkw87YvRf_P<+GZ82~_3d3cOPoyz=V$pppr;_H+ls%GFBW zgtaL02=O$#x{&ClgUTY2b*;EtVD+J6dx5Hk>73FUhw4;o$NA{IyA!0k6&XP~VR!~r z(~bC8qI){b5S0(VNZW_$oZo-H8^BofZp=*qkIU8Wd=WxKtG~~F%Nx;*^X6iwhj^s_gA;cOUQSp_UTY5@HmM7VX;|Y=Q^T-qtrBEW=jXl8Lf3J{8SGMe5=DoC zmYJ*nZ9X`v&3T|RS4D?GA$`jsgoy1t?QW3$7VZp6j4uv(8ZI23{nv~Fyp1oOGIcWF zY2JtQjt8cF+HRkyq?>z^dL$>q7zy6sQ2}~gDP*!hSuZdl zaCe<$$=Lsam~<+3`Iup7@4q9vZE>z&aw@SW*V8DG7g$U3p6_wIu5s7P$W(X^?IhB2 z-n9HS;INxPutOTLFEaXU@$LGT?}^JkNxTd>a z3U-xea*xH4y9ul}|7TSUK^n#z@vH6njrS?Q{sj1Pe z#ofRec*ZoC(P<5IDh6NIRMXDM#=PJQ;;CA=tctT{AVkF|{juB0tpwpDt^Nl04fLXC zP7%BhxYc##PtI}$bsbPtW11RPNo^=ow%D45aosJ=Pv;BAuuRa-zQXrg={NmP#*SL*T!;Lm!6_1~dt!_BZE{brHw!+?00$rsQ zNG_N!ppBY4Wxxj*wBwF9rj;X@IByer9e2-MV|?M+%<*D&?RZH!7)TL&{x~0}96x_J z92FCqx)HdQr9?k2MFwGy+^uVw*aCW*gC3-E-U^OcxeE@_pYA0AjOiBWI{X!uquwMGW#C-&!%gA@K}vdg(ODh^`?)_sh{FJyYnQS zw!VNJ+2_)^QmU8H;C+q(1*_SPX2Ov=hB?Eig4aA4Ke#xc)`uiX3$sPNJLcmRZQ-cj z*pYL#W%}K=i`{} zwn3~D(8Ronq5}UN5Gtk-ZW<)_5UTal;nW=;rc~XVooEnwsvaX2Fl+X#;v--p2VEKJ zfu-PoD5-c9I_i>mUJb${l%ya4V#m zXFQYMGJh-B`4j#y=%cpC*ZrTpYfYj<$>^k8l!o6|NeYLnuC25!9zMvr4SX8)C-Kt;4t{DpPNkFt< z5_^XR2Q+ZD0%)iPh|8wkP0haLn7c9g%uy~SW9N}C_5{MZR=~;Nt@Bp`%+l62qN2`G zksLh@0QP3Stw%&YwE5Z>MC1)8f*GEY*(RRHe7KGnA#Qkv>kRKay^yC?5ivIu&81-S zW#QZvT>0wl=C(Pg6|a#z77ei@PKcw6+H)87XV{Epf;z%o{-!0%u06A!9$3=|D&UrwUsgAhi8$f|;jAfY;CpjzCb!c<>+gX&tg+JR#) z8@VG$D3yDt4ns_%Cn|@rl~JSZ_V~rnNvh6SVHve&X1V`jsjx)eb$dKC~38>Er*sVn2WZSD~#05{# z@$~Eiv z@509_=MmpSZ>K+vu*qA78M>q%zzAGn+8(K+p2sl>n$&6Nh3XP8t04dyo;;(_D~2C` z{a#P;aky1;J~JSH^1Xix);r{RYp&dv85RCIJ@-t)*r_2m{OC0^(G2FYHTtYr95o4< z4Iol9N+7z};I`6HrJ{<7e5X8ogX>J@(t-~HK6hXsM<~s4LElrwyuTfiqa zOT9$8&M|Qio4ROZ<^yGxz47j>`bEOKMafdQIT@W5w-gWiC%p1eV&RM6#Lg*&JtmSN z9?kxmpItBP`YfF|ZS1WenI^tGs3UQiJ$2hU-*fVYE166_D)JPV+i`#;`WhHq?afEm zgwY9fOUF-lKt{@cdw&K}2zKkWWsd+6E6_n9#WP7SBw9-MT^?dxIglB!eg6-*`j9Gm z5ZS#zZzF9zgJ%^*TYxRkcs-j#)6GoKeIiZigBFOS38c+rpf*IhW@oCo3FhW3C%3MJsjRN9p9pOrhD?h;P z1yly~g3mot2~q7=IJ8XV`8KnV3~A?)HdbMK=+?bl+IQF3wvOWkrkdcm+NuBLG`pjX z#QrMtv}_{|FRe#jcLM`302ow}3u60+50TN>&K|F?YAB z9BWpeJWHX#AA~LZ2I*P!b5iRVA~jqSChTfoq{$*=_#BmM8n&ic-=DuL$4he1WrAZ1 zm*UEPflfG;@Y58zLSa{Iy49h{Bt(xI73S`JKkXWUq-St+LWj@agqe~H&nwJUNC{|t zK3^t#&{Z!4g)q7Izk*&YbO9w-cRDUCIJEVRC%5P_Z^Ps}ZjtJA3#*xHvnR{DJ>rF| zF!1%HS!RqUdQdT%^#3}eXOoT5Px6C-fFd0U?F+D+x&F3YRL8-oiOLLgF15IKkp6{p z`AvGs?Yfv_C3je&sjP}-;XR|Rr&2N|We3pFy!ka>1h6c{m-I~k+8A}Y%rYzRAp(`{wIu1N373nJek5)=BfterIZwt7qUB{-KHhN5 z-~;HTQ)qp6f^$_@`V|SiV43J4$)An$G0^{uB+`*$*L7YAM%EAv;J(QovLv0f`pmF~ zpo?Y5+qs4f2WkvRX0ljh_n_1asknh&q7b&@ivysRLq!Jhu}a|hnRbQJ2IPNq&UH-j zI?nl3FoWSVRyx9Y#)pOJkaK)TGD;SzCFb~@6pANgd5?5C=?dWtdQM+N?b z$T)rlzylP%@Vjn|B%4A5)s||*M!z5Z059j&9BFb#koG9d~SBN?XG$J*oYk z(^D(mxVK{(i0$@Zt~g~Ld1J6$_1jXh#n;++#=HzHYfxmrp|MJ4X~A+AaIQ*buF4wj zgOx7ccekzAhJMbvuk4-XGdg3yDG04Qp&&C-N|e!E9-fsV{BLU5yK?XIb;a9k-8ed)-cAaB+grDiCI4a}$|QJGgcBscJjBva3UKRP{f z!3*WLhvnN_W7Xj2)pkfJW%{))h4By*m8P*fXfK)6;)Qj0&sF5zi1WSPR{nKchg*d5 ziHq+c`dr8wF1oyD0fEQq^R(dpecY2-N@>E5H$wRQdL5rPN~xTBSO|rYWT6^`IF$48 z^tx#7-Mmfhe%6$lET9X`dlSie@OEUXEuZm{pV-Zx71Rt;@c^eF#ltX=M;=pMSn(JS z-nUSJwi6H-KCNX4`#i5swN}$swU?ry**d^ja#+21B{|h0(rt}vrRX!PeN@rHa{HNThQ-~NG)R&oB7=XC7J zv>8hbrk^N~Ib=hR4oEr?q>YhXKVXxcaV3MCzYnttgZ>h1_8GE=@hkF?g~oCcUbv*s zJ8_{EdIgu4#Moq}TYx=7>aP?9y&Q@)1N7hH&B~ObPL3A3Vf(cLKk_VoYE#Bq5~3t% zV<#%mHU^1#Bmq5qcJ;Gs=_vuhCe5znekhca+SFgXf=G2y#&j8@)!c0;m0aLGCYIfR z%~r>%Eg>L7(1|adJiRjToQf#U^*YrkcRY&D*m@rPfwlxBirra#bkH|)d{lR(JlC3z zRt($7uwwf!7(K?k@ToyTQ?Lx^T;ZL}9DK{eKgk!cHh*;?j**vJH>Q-2Kea zUSn(3J$3t7^Jhjo&oFqcna5Z}-m4)V1KlwCaH4ms*7rJ}ZFMk-nZ$(v(Ltmw_R!i% zm4M5kLJbiT*2lw}Es4ygU_DtM57PA-8zl*;`E)(@T5}~Th1r2{;cfc?+XnM?rjaFj z8t%oAtJOi)5m@Kk`*0*{AM&0t!@m9Q%=GJ*fNGVhrr?Z<67vEF04_s1T#tKo4qD=0 zAdd4d%Ys4aoiJq$h83KEj~B@K87VJon7Fe*{{fT|g9uYUpXs0Dwa6FfeBX2%d@*(F z37=)aXxWyN8dGL@y1wj?YRn5?Hlye_0LDN$zXt+3KxmGFcj9&jw?t4yzZUZLu$Z)D-OWX6*;ReI_n#PNenS+0LSi33xY6|g^+;Fh}0eynDLw?Y|A z(_zM~c-HfYewgQpT{epu!M;0O>98}*4d=#ax2yt9PW&0?jFA4Z_e){-{-w(wcKy4@ z;>?U!XLMgUx?UcX15C$4d?hx(Px#dXA8lJfp)wxXJYkKjbfB-Ph^L548c52Iw#zv;fC%dAr@RLnnWhhYBGLV*4_)^mTZCqXYG zuSDx2xd(kyr9K!=Y!?v-5;hpQT{BD{8-mo~=s;mUsO9YM!f(fn%TFkRA*>~#8H+f& z-H|Ek*hjE^a}2$3dPhqqQrvY|ayQlz(aVgjr!sk>vJE0GLiZiS3l>C`7CHC1=>USh z$bPJCeKi6gqlA%fLQ*>FmG=>eMBxQMS=hM0oDIUu-4^>akn_7GIJ6~Tu0mg^ow7&s_>;87splpO|C%Zj>8BaL zcV>Octu;^KRPZ8TeOsUWG7-@$eqRj35XJ)he}}ARGCRBax%JEjd6~4PoCZsEAJPLK zeiCjSF(fT1OpPL*p0~edW0Xa^YQlkKOVxNAENQ2SrvDOh>4?Y_!S9&%$FQD86T&m3 zd05Jfr)ub|`ZwMz>BJpom7!wOa;?y__Zn2hQG!+zZz!E*Dt zLjBKm``wsR%hlbV|G1$39{`mKph}c5^nwch($&rFW4X1_V}yKyY;fn?xi4Y?);fdH z8)9X%BP>Z=^qm}IwMYpSvPk-RmLY3pVEWksIiU&E%jda8?P~2@3%z$*J zva-6VSz`!5ne^Z+E9R#VJo()xa(?Xzv}zvk9JKsY)-@R6o$ZaO6>6qgzXqgHw##1+ zfzuEO)J=gcbc?MND zuv~S)kEUc?_0QBS_xgJzFTuNz&=wuN6Y?756_92LIRyp3LBZPwB$dB<)H9|WoU8w% z+q>=>BqpO#1o2m;dV33No!pD9!axMi=P9bB{_q;7pc}$Bi|@RRz1&bt`|kT+^!)9^YoO5G zij(|2OhdVswAF7a6}}smzomjj{0mfWdYjL!-fSpr2AWBl^}HYR8APZMU3*o$&OT@E z1kxX{a2JesXH&J^jYd)GJ|`^2lXqt9e{dRwXIHf>M-&&zfucj5X(g1BiV<(kk9UBe zqJbyij+G-6zsDZNW3Jb1{EV>8eR*N++BH(rlb`E8==i#<5`XRgXHq+O+CU# z;=OY45g_TfXMp|Xg^S)tlb`PQFpx^4j-6UiK6<`YtF9E1yrd>c2rN;`@1k~4Ak)yE z7j4{Rh5#um#IX4M&xJgy0qiK*OeUF}Qa#JzY)UsnR#3o?nkQc@Z+vHC#slT@WbVS{ z>s!?X@*YX^fCzDe8Y|II#+8tWOH2F@aDz$~6BjWFK)Z-!Z?CEIR9w2Xo59HS;mLtb0 zjezfn2Tulhh>P*Mg5$O)fyHgANFww@r9%98Ebk$P?co|yXI@K)n^`F^gkBkil5KO-<sBaB(U`nM>#c<-9s?_G3CMJ_Jdl@s`q{DvqEOJpda!8dsV$ z2`CP?B5>76T(wy$1eI!vjIc#JLU2~e>1w`VX|ubt;)gdbER=1P*Z|`A(=lEo|Ei@n z#S5{0uU1pGDrh`aDG_cF5uL*D0+5HvyS;aJ^v_0kP{=6D=>=>*2q8`5gb}|L5u=G@ zi+O`pbAjg#l0YVt9XX&(B{DA4PnAUB9+^=QY#Sk4{OTWUF!mDs2okG+k!B%3$H#dk zSCC0s>e2LQ7-X;sNGUDryDCcB*>@I47dW=6#ac07RM05Ne%n>rYZv8L#c^PTcP$mvcND?+B9FJ$NGm@Ffeul6#p1TskgfK!(rH2}86P!<>_9{_xn z^}l^?($11JV&IA5`bTJQl$fgc;e?b<%Vt*9Q{m*%|NryHOG)TdE6O{itp{~Kq9uc- zS|+&`OPOk0_nvJL8$unIC{C3Eglum6Nw*$;x3metlv@}z-E^itz`&PJT$8#cT&^%a zDSX-)^Xv)H)1z6gBYJ1La;p?WS5Nz_x;^4MU<*Uw`fjoaiJ8f@VBpchgDB zCE%Zp{HUyQ=!PU+=lOi}Bjw_ehzaBkvcH>0flWzg_L;aAhjc)Eemu?f_by)vDTd7M zMO#AJVOaePWV)4UE9rf_6Ku3Pv2EHnGT`)B>!GL<&pe)a!Pu9X&nBhSyS7J zDLs;fAH_k5j{RZT@|9q)jUgqte^PNX{&2+|4W*su3pt><0NWd)K{N(U84VgNh@>+C zY@>K-iA7Z4z{I`u=l@mX)Srs5N+gHcw{V=);hy0pv()b0;Muu*v)KX~Qxgm49WcAz z63oUe>JGMxkA4Reb{p3@?lv}tTQ4G|2)JcbY*?m3Ndvk;N2fCYq&=2JMipL-4MEJ$ zQ+qf=)WJrpH>a;J0A1{dhNilJ5&#+j$PxfDqXaTbASi=3A2k4qv``kHCfZ@}0DOmA zwP?9QG5lokHHlT@^S4v&-^PPB4$s7E4qGLgd>U3Te#WD5IKoi zr0*U_?YAksL@PIS1`+87Rfu3X>2)g%HXjf>@dq@z>Q?X)ma@jF+%jXM5LF7GBV_-I=yD;~yU;ZG_WNqsN~tQk^BQM=nHAWNbnV+_6WRZl(5kLxcoykJMe;ez z2nqa=Bo&UYGv7eg6R8B#{9r#XU;v@q<0roqybAl(pHtlP$jhhaa!e-Hkv%~6x6(A! zy?N!tgeNhnP9B(B#IPyQXw?_xxlz1yZ<2#~5z0bP&dJ&g9kl%%Jl4b2mvKsWB}=)c zBAMVH%;l6UF$wPlCLA@GgFN7r2089tHh%>hJ)F>E;*vP&&tlU(-1iG+Ud58WN;F{N z+816Yuo3_o0oW1%GQbEaC;&wfm5-SKPcny#m@_s2472d~0DOPFk{3pR2d#1V6f+T? zDsQDyNVYbez_*KJD)l;k`#4sIM0vFKHc zaWOsRn_V_y7COj=SbH?)4+*4ZTKZ>Q(|(UAHtIDqUe!@0`d{5BYqhrkxpEG4 zsXC_blT!V>ooYFriPm*RvrkPzvIZ<84~BV94TTNYA2X$#g?wk&2Qe0wSS_>~;yG||ty&c;Frh@L<|$e8 zYQowSW~(tr=(s$Xv>~K`CS9)>4NJHR33iYcb73O0^B&-nc~4u!h>pR|zE+X&)JKC% z?vW8fPo(dG?R)fUUYUI_I6I>zE5&wS1ev-_qnZvYlY^L2h9*4Hblv8$@`M4vNJcj{ zW<&th;~YM+=b|IMV1<)f(tp>Fuc#vX$Juhgi1^^J3^M-Ex0J?6WP}~NkmLcgaGIfM zIE`z%93Zi}GnymMNsUd5kaq_Ps&VVTeGj6Y|G7*vapM4jH`9bk*qdjL-k|*Y#`=SK zA|cg&2pli7LaD`IW>~F2#wuWto2Ls0TZv|-(RYnsV_N_eFk^ck*xP-aw{ zH>vnwVKhroL;>39KS;cS$R1R1V*~bn*T93=-uPa1!fugZ4SqiPfnmw5L2CVq3Hg^# zqTd>s=nH_KZR6?Arx#FIa|2wgGePnnFSuRsIJkDg3n^E1y)ex?`6^gfI>do9wchnQ zN6`pVSW6jn`^<60+9eXSl1AKWQT)H?Qw@?Y#fgH6_z~|!*q>$>)k6m|v2jCa5m{4Y-Bl9Kj0fOiBKP<(#X(q>i=jyO zXzX2x4wt@wr5=O2gu|2)|L-pSw$yi}ke=>zZ|H-X3vu(YiHgwe2%Dl{DP-c8N6Ek5 zMrkZwuq1e~9im3=+r}N!1|)rgyYRA z7l{vp-YwQ&Ly*Ri3Qmv@{r5+88=8FvWs`I#yq$r3QECOO=dF+?M0?jmDB2@7gDVI`BI?$T zaG<3ns{tiE+|xiZ)&?mjASDr%kC^~jLWc`LA;U2E0DQT*rXlzeJD6X}r(7!r6uxYy zr8~blhvlN0;?m3)!Wk-7gpen@?|Z6s7m1njano9CIFZC_s0S%~sW$($$1u>=T|@AZ z9FVPo4-iE=v$*5E)OG&pI=4=V*lWmStaZcj(Rz`8=fEZ?gCjdx;yDgsW(^Wkp0{16 zb8ZK5$f?R+A@kEqxJM}6-3OOYtuJib+7z|U#u4Fh|88MQP9^$Pb4g@3H?_>z2E$lQ zeSf863j8NZVvH74O-Cd70 zOYTwKY|o526%7F*%0RmliTe(JLu|<|WswKBTC~C`56{YRW_f zo6fPBL9q>xPI2SVRYqEp3XCy6dfazD=QYeZX2)IKGVaVBZK?Q&YW^_0Y6I0}7f@E_ z*)#xE`+Ty5PeP0?bELhN_ds|HVwWt}sUK{5h7>q{ZZtL&OY z>qQn8@gDy;@J|0D)OQlI)5>UL5JBxZ z0q(c=F8S&?_11de1V<)H(RbHL7px|*GYRc`KFvNtP?RTY4cHhpxL*zKocr-Qk^*}w zx%P4#ao%i&S(KgB7YaF`iuixA%A)wOa(@^RRcBXR+UQm7cfO~VaU?}|Sj<$P`{cB1 zy>9rC$N6-)SZ@Couna5`zpmm4)n!$MeOH#JW{BdPU9}{fGRp%pO(F^-D<3rghBS*7 z*q|VR-~jQHoqtTf$%U=AfccHjGvydQXCxpf%^)!Io}9ZlIgRokjGUfcc07~UAFLE+ z~6?$uhYN!66&}g9uej z7vCJIa4ZllHrhi)b!ojpo_tk)(_Le~S_;M;5lBEi3NBWcWlt zweuzEoa1t*B($OFyZPh8>GiEO$06Q);qbs*Gn$=b! z*FiM+PI@e}G1Zw#w_zV#WdziQp4+{sfk@zLIYW+Z>e69yP|?ZC0#$dOMkSvEmf+2g zW!1-C1IP_z)EJrMIwj<^j}U=BxsMYyxkPQzdXN$T8UdIR05aqQGEXD}D+?br0G340 z7C2xY0P$z{`nh5LZ~l`wV1nrJgyEk7Q7C&n+XbLSO=o1Dj;F5Ud4Mrfe!J4SRZBdgXlZ^)-?djs(w!fq4TWg0p~r@^vgBF;#SsGi?~q}| zuUskCtF&7QlwGFU=(-=B*(~FBQfHp&yPXS$du-5e6!?23*Bu?@&g|8Og--(&*xV;g zkfq_Epxeq|F`8DJpGCe&;v$4g{6^w?@fIp$jtXuMRL-su^t9i`Y;Z8=%}MZGgmQju z#Z?h@tVo8Fdzd|Y3G4)EuF`09WM;#Ev>!;YodI#F7}rB1#AvH!NV}OTIFs&oXSvHWqkqIhF6st(xpt-}!g@T}ZiCx}1TspiGd_ z7l+v}5&#+js1g7&-32KrAQGjCkC^~Hdg%+a@bcUOtpM-<@n5{1btgg9^!zqzv=|6% z!$9?im|`iySquv}PV5H4>x(|hzw?-(RD4DCE)L;Hq8hHsAIcv#FLrsH zp0X1f06T6{6)$!13#V+uP36kwrm}iB;J4aF#2|_jh^4QKQbqT`JXUdAq0oWqx4-t@ ziwsq)LhOa*9*Li(t=e3AlwNOSPQJx{g-Q@e7I-72g0m!1mC>ZWpJg>K$&R-l0J4z9 z!c*2@B*c|!q)mfN#KGsQQZqsRG^W%~>bYttwF)3q8BSD9?NoIyYf?i3fHG9?h08r& zaf|0QVOQ-aTaMP%2tGFsDcr1fv2ET8Uu`^6PYV9?NL8$q8J4viIv%$XIZoZ9y2)it>-HYXET!ZPPk zz_Ax+H?`_!8xQ!wi{CBK2cb6Y-fg0rJ9_I_62#;4-_T521YBIE66OGz52?e?$w(8s zehAKka_)rpDyM2lfNjMteC0?o<^wWOAVo_PA2k5JM-djVXMTa;0P(FQc0$GPbvyK- z-d+}$a`=@GNN<8@&8q$&LSnR`&?krbO>SP?hghwo7$9McV0!bFvQ0u znz0Z=bRsyo%5>&E-)79NREo2($)-D`fbuhz(tjGQP*pCrOog)3vjA)FV({ z4R*Dwht5a*XP!N{j`(QWUk=dt0NK758B(hP>Go*}XYDw&F0*2s*{0eiQBGyLyQ;{f z+oP}HK(x0>3`x=cSA`No-j$+Q-0z(Ax)DOH$h$XiXTMP*;E|mE4A%_gU@Rv$Wb@1u zv36~6#EaLBN(x-%CwmU!=sf8Iq%)fP^kjhAze0oZ3PST>wZc_^1@SRGb!k)CS>+Bd z*8dZAe`p3Dw)I5tX`fBGeCp>3Hxe)2f(daM`VuBYBFqn)v6G})eQISTB9@3xZi>8(MPu3w;B}PYe-)8PtC-Yh8SmEBok* zhQeTx7m$Kfx5JC*86YZjhqJeG>6|dxL7UbuVp!= zu`X_VicXYZiC}LHJ|NE!;_g>S`0tKf!7bHUD{0N1P-9OZen1!XPFJ95!KcYP_kE(|-e7AWlTpSRL0K8f=6S8bJkoT{5(BFoLr*9Nt&P z0WoMuIy{C&wy zra|V=dBE^6J0RY*XpEE!b05a|iAuF(g2Uy>gKpwe= ziqK#X8}~v73;?|2K1j3x$slkPyNf+kzaq`E^k8*bG@&FE?;M3Vx%+-pMKftDm$oe^ za_fI~=PH5yso^tqm~c^?Qrzpzt3q43-c?s`FA&Qc5o{5W97n#ey0~8+?SQ-XD3%Ho zQbf*N;LPR3F+_0|pQwbIMj;Q$ZdlobkRgfH@}nlQ%{DZ&lP!#PqG>vA?aeE-8!0w( zYNn4xv;SPI7zfA6rUDG-&F45Hp@5HA3`b#AKujgkl-1hWdyOBOwBsGG%cHT%DRe^} zEL}Fv#V^9t!HAe<2y3sp2%ueZbsPmc^ZBgbc8HauXbdLye&-&J$5=#exSl{lPW#y? zcU=+0ZoVcBq~;kr^47_#mRftQeyZq{I|-VM}-$OFrsSAqIshH&(h4 zImu=J*l?{L8Qg--IW{s8*4(qNwfs#~e(vlgJ83f{IyK4_J~B{zuK{v64R^l@c>82o zzYs+neQs#xN&2?jGOm8sWV;nh6zb20mtSl&sR35$lC7&`$NrRO(1gh;f>y(w)BAP7 z#r(@<#sL^#`fY}+!zur?AU=D%u#V;K{x5~CY?;CO2ne<%pksIPzhm}qRy-Rz2Onf^ z;~i1z)M2Y|Ck~KqNNBF$C#i=^SWBZI_2*bL`a z=M*>RbCLBKNFI)%(Yq?EAWQF5lm4$6BCt?JPv3VA>|V)QX(Bp0YGz9ELxLMpn&l^n?0za3jCPuY_ulYXY~SOY4)L0HO*H128$_(POFTp0*;hHWP$E=MAD zD=z2;nLL<`gq^=Kv|rImH6x1VVK@+FYD$0)lR~8c zo+}HK8IpMop#i#W7pJI9oG0bX@?qi$A5?lb?(^WTXhinCaSQ6x(1FfurZk*H96}bfC{}}yf#TW`xH&B(PVkYthWjyNCq>8#7c{! zmJ+=ckO15!XI{ajaPHFHN2Q6X~n2n1Ck|AB(7iN>!tkn202m|JcG3p2QcXr>?0T6hI!OG6B6q)cR zZf}s{kCd1U40I6Wfa{rEpIfFG_Xb95$prf4gAP}x<=Ss6p{y46X_ zw}ctNB$AOwY_y*vXI~C~ZVi-7N-Ng-MA8KFmr&%0Onkpob1DI~&)szmZzpmr`r}bK zR4c>*#{su(!3;ts|GZ_iQnD`*b4Q}%Mr=u*KNx(SB=1tqOvp#=JcmM>Fa0jjm0&XK z12RZs z__(s)kkKGztkiG@{Ct};3BLK(LBqR9fkL7zCJMmUr z`GHg&uQuvfvf|Ec*IlQ*8lLd26;sHvqZ`zu>D!;7cF?eKUoByo)Z%q{dWOI~9h`Mq zznF*Up%u_J-|I!4EP*B(YxnHt=obd2!hJokPe1|kB_%5o&`1C(_kcpo;6;?uA)c!z zB}FX#(SwNN4ZG`S!n|_97f|IO5QX>&=xeAy2}6PmPpo7#8qCgMr`x^F1*r zeq~k$9d)a zLh{i-U3wX;S(}DY{8C#sqg7$V8;{U>;aQ8O&RdG0ws78esDUTNZf26lVG$o%*~gwA zL9gS0mVoWB)O4x6E5+hymKri4JF}#Ck?AZs7?5f3k98)n^mE= zw8w`1@ME-A7a7AT>E63-?jo<^$kb-uwf(#-Us$%$wPP28z50&6t zM|&V!)}Tfvh&EcK#`O6lL)0>_#)V$WZJp|sQAuN1OKX@OlFEH%<%_&ucC}8K55EZ` zYND$cjBNU1B?_rfF_0iH@#RI_BsFCoXy{mjVkHJJ&RZEFH}^V%A(CC)(TPcxCDk*_ zcQUluAH>QhYH5M=b9Gr5k|WjXVNF5xNIP>wq9_f8tM(9Dygta8umxg)kDE)iI}|t> z*pacCY`Hv%6G;6t{lht20~Da35&$w5GFM>$Rt{%z;X%fgU8^GU;g6qnU>gazq5#?QQoJ`-RIx_v zS}E96M*M59?S9hIfm5F+K#x-H23~MuUYI{rkFxVedX6DnuE4Y_`@4d95daq@ixWPb6#?Q2AJ@(mc=WWQTGH$Xr3acMxA6=6rF+N?}JaIDKC|T~7HiAGW z;mbZV#zzMPIF09SnFX2}jBL_EZwy2?UW{u7m^+2@&TlO+KDT{}s2N3km4QYu2heMQ z{tu|S4kH%rX+z%T$4s-OI5`thaFqxowDAelk`M=jG2;LfZl4sAG_Q7q0I~RpP-wmJ zlPILzr6d+BIarx5F>;C+eKKqd{>Q0mV>W~VL2ThlcJzFfQdv-S94H&h?R z2`l*SDtBd8n}k*DTa<%GM)E2Ky(sLE5&|4W`WM)tqu%Ov-8wg?`*vmIeml9Qsq??I zz4d?n?iiYSv=B^iC~p;2Q#Iqk9e27DyY7#>;HRkH=pFgYYBUKDodDlY25sO!+=9(6 z^?_JV>{rc@efp5ug|oS-ELf>Sv1ty!Sa}@(V9!Njt2AoQZ~SM=QMkg`cMU*Wxsg&e zAhngT{F#iUX7KP{Z@KdM&Z`mRv-_8D>0gserU4Iu_?Cxq7D;>+$yn%}r+6GWK_RO4 zS@Es-!0AmP0PJ9E?+#7Ev2nIt7wFvz?^6y~A_N@+8cWbPSGd7v$7>@VM@##hl=$>O zeBQkXV0wPgRXqpC{_8a(_p%S+v6Vlw$Uv3Eds<#~s;*3cgGo++s~jq+nH*c9L&{~E z`MUn1X^|G2G8QQmb4Se%!eM7wvb<)r6fd8^9+CP;Kq$svYGR_FW%MN)vg~|vi*cVY zKBK70i^pz1(G8r2aOH%JQhBstaTULNIH=iWJEbkMSxD-MPHN4}#+ta5KRig`8LZw| zIS*<*u8d~wV!W(`z74H#hM9i{23qcb?BR8c)L@{HGFYn9PfcXMC2Bk$@mBC8&L*U8 zI1qbEl$p{i|Mj(+MA8$tDF=b-rOQr=O|fNF=M7UG{_mN#W7nySx8(agg4W>g9$hS1 zN$@pmE@CvdwBDgDaY^pMKR19)ft?CRtv>!Q*?kfNT*!qXS0RmgXegqgiW1anF;xHu zs}L8T_l0JEh0yotMwg~QkOWZ2D)+S^&6-f`l#{1z{Dt6p_#3Nr8LinJ)j!yDY$Qab z!Lu^QHMvY^9jhAw zxKt#qx7Y_?I>M+H3l4U-NEw(^w1M;kErq#V5t3gr_C=(2G{`Wz?!#D{{x5+1;QqOD z$p}l{PHHn!bR)W;V3`G|ef?vi5TSTsAE0pGGjTKhzN4MdQis0WAr2E>=bK}G&I&qv zA!Py)YaIU*Z4$R@P%;>U+R|!rccO#}H`_qKMMcz5M^yv{B^?|crp>|->AFRXz<&#F z`uT(2mF^PVc7mvUxf843AN;mZk6wCgz)gtZFh#}}K9&0e-5qgjE-r|vk3)X`|7$n1 zq+mTxhDNS8)P+if_}!OpqauBWy1ap~rC?x-hUOxR)NS3&P)j!3aOLIrz725k4e@~< zj~kSk;63=J$7pKl;xTSEn)*#j0X@0y3^iLuonoos1iuB%7HZcaa3-js zai7$pd6`vs`bB*_W5p}!c_=phv^d)sUIDCZ=H#hy7%ao`oF~L?;%8NCQ`TTA+LzY4 zPb@#NMUAV^8Ll_s) z*Huwy%de|@BTq8ZR_7Gmv22J)J({q67Zg~z9ryG%;A>q+bfGJQsv{?jCnmn-X0nZN zHcWt6CVpzI*s^;8{oc(U5sgv``>vP(tIn%>ms*K4&PWEc#0}zit~=GI{wn5cmpSEV zAC(C0S29)>V6EemafR?3KfWx9`<^f zB9SiTyIy+_B5zo}LON#|=kLE+r3R z=Nqiw#9abgy-cZX=ub%M)T>U84VKKPKt({On~Q*C9_n=dtJ$l5q11vZ9P0BNtO(3z z7Hg`Ao94Wm^5iBBJW!(-bR;sgVxy{`)5-t3%k5+wcUWO9`;i6T1Ggdfpq{b-fRKP@^Ms$ex*yOjh}K6#;tnYdJK%V zq>>g#$H-@}K?*?9cHOfcmpH=tGg)9uCTZQqt(hb7*bLe85{<=0#-!IovqegdC3grm z6b+7ZWCp|XLwaAMx%O{sA}~l@W|-BjoNl#{{e~8398vtnnwZhF%VzsZ1agt}Ky!I_ z5gCRfY^POCw>gXdfM1bo=8M`0C+{y&hjXK8!HEpA1|-!rKgg(etP1e;hsp}$*gYnQ z{lK~?!+9$u{Iy*Fvp=TrbH^0MS^I^yqVEEIY%M(@SN|Uo49znbdbOsO4#v@GNLps& zbGzGAuOJ$cm(H;?dn?h2yU!lqd8%@UkR}Kf-r5b;qL-3&D-Z{&)q|fYZ(JBchuSZ) zE%!WaC0vc)10@if9>zzN9w12O43X*mMZPc|$f5QoqaxY-=eBR*DgRM_r)upLRkdW5!4GWCd1n7Hui zLc+c1s1-0Wx@pTpEEJvur|sw2&q&gs3(yoS3Ucv~q6U@`q24z<-e4bFiCuzc27JEX zC2^|s39=V0`v8itzdJjw(gy;)8oM=F>FhLT7E8ST>EC)$AQ~8e%W7m=Y@vQ&(#FT5 z4~_as%tl=H$=r(LM5T=;@aOq;z#iu^1{1qSII++@Z&&W+sdnVp33E$Ae z0arP1jf24qsnu$EeB=n`4#oI}YEa!*brBT&>2yohw}H7Bhh@2Q(fSYg2d@Nb8zcsM zK_2w_B9e6u0}F=%40+$cnSk=TJW_%v69OaS0NOO;S-jyF+w$1zgXqzC9;GAFGgOS# z5D~QdrL`egVXba(lEOQ{+!LW%s{9f>GhlP|bnKl#mPR2*a&P264H#Ac9iVW_M;+xp z6-%Ytf7=yHQTV-G^J+EY)z*8u;Elb+8E?-)l#FJ%|LB(Oq3xv!66sZ;ZQ8U=)KkbN zHJUX#g|%R>FnbR>yErWfAKJZj%unDtyse$#*Ye>@n{<>$7E5h3{sM>Rj^z!r4GIYe zPy*Tk{h$c*cmJ+jlWRF&DciwiEHJlPw_zTWR&L0hEsk9MNal|aXXQ>+3W; zZtU+&fU%9`mlC^>TDKW#EPzraJ3G+ky=tjmT{1$46#0hXl;@UKEPB=~iN8qKnmt`ehidv*P z34^Wh%X^@1wthMgh7E@F6r`U8MVi#}LH^jaRs^yvqSDb)${Y%rE({5w(*|_ep|ij> z{TS!!o7|HOBcLeFr2H}tb8WovHvF=sl7FMJpp|d; z%qQpTw|lJsu`(cc(=#B`6w(y3g;qxuY`TA`V$-?C0B`s5exL3Ele(%9fgGP0+Kq^c znICyt=ktyRk!bAZA;IbF9`@J1j^&iXl%OR;>n1Agl11!A=#n-qMYk494xYJUW-Ubt z*8gH?a<#LmX%T@v%v}yG*1Xh&-Hg5hu|c`ylhI8_T5@5SR#dhf`MM&nm~klLBQzTP zl+sIV8dC$ZB&En;?a!dwVxsVDD`kZH$!*i`$sL+@Oh`6g$~B*!3U(7WGIcYrGzeDu zJ%#Tm6sRMulpnO*3Q2QD3D-X2LvMhN^trCM8sK)WhSm7I$$mS`6_Lg z4%UbL_(>kud8YoC&VbLDw3Sp3g2G#m9K{SHmr!2$kR@TdJ36+D^V?K%Y903HCrhe#KBC-9vfG_a#Nx4d3_SMZWI(GuYiq=*EEkt3i8as`2baXcn66+E-ipz?7 zVzL%CcXQu#$iJdUozXj(5W!`0|K(3$X-?qljk+9T&ab64H8gEz(eb!b{;I zzsNk_C|(Vk#leY;Ik!(rCYaxka3@!PJK*o+izPA96%y~^AQ3b#*W^lk1YSSYRQlUK zl`3kv&Q*wN`Vr5AexY zFYG_hzR}n$85X)hbE#|a;i$dv*tyYl@yUjrxZeNWw3ltKR>44nQzCOPqzHn}2>OQ( zO~TaUzI8V97)RL69a?&k62o(zbQTihmQbbbN38%5c$uZF>^ms?^bz@px2A(Q54aB` z>2&_Y-hZdhllo_-*H{g2+fO)yMKn({2{Wlqq9@x#6x54^!415Vj2D4~xHx!=SS7`k4RrR&u$0-`+e8;y=8R_O10bsYB|=7@X8 z*Mwap_#HsP#5w9^IF3(%El!0_r}RCsbsoC5Zh4~bOa9$Vx=1r|zG=_B9`T2TmJgBj z--|~L)fQvf_RfgI@_QjsR`dv~caBo`)t=Oe1tm;!zmrqU`>{gO=sXC~{C=%v zq)2h1wZ2<1#5IIPl}{s(aSmkJM=ji1=}ljbn6g3T9&s)BV}&Dfj0jL?QYXF~nrJ7S z6_OrxhZMus`B93Ya{vjeD8@8ft%P;StkTtzRpL?RsS+xM-n>EmZT!yh!6fDEsVjEb zw+zlfO+0)Lz~Gy!XQlGdJ8Dq8MLVFzfcbSWCR#}yqRFV+y3oS1#!11t?!9MP=n~(% zdxj1E4*cr+=$oWt_*)}%G1p*q&hW0(E<&ElJk`!ng-0;zKd~{l z@Qlm=vKjR$(#Jw>vNQjeL<8m)yX(2uOAJtf#};wb&JJtgjQeL~DUUoSN-O9@)*87~~c8Zu4^0kFJ z`MH#@TTn(Uz-Fv%=M`jm!iUN?W?pPN;$&BXyW}75Mzc!@!R6M;=7uOmL*8XA?tyD| z9>@y_djz}6x=EXfq^CBf8~%1~Bm>j)TI30X0=?Lm_9y#-Yq`cos9WXE`1R%FHn-k> z*6%e$;PE!rqZ+t!Vi@j}uzBjl8Ior;_jTt)IXMO?`(fD2>tv^Y%S|{9*bXZ{RQlc{ zSRKUz|IN-*EmHEsSgTiInrP7)hyBUS>}UgUQ>LEn3A=@Ze!S6%_Pc^klifIx4-DUp zwT)uQLw%u+es%`BVlDw3C6qasO4j*^7ve6tpThWvCc&~$5a_#zj!}>wv8kG~EK;rN zZXfhC&4u=qfbv8H6B~S&YDiw@iz&?8QFsc8Zak<7s<*}?U%DT}kUc;5BrW+o7Yx2- zBcoonb&EB`#di%tA!mF^=mxKJqI<;nM-iXy!u#(pOQ_h!^%9%+QgK5r1u_PPl*7G6yk11s z2>wo}x>-J~y7@v8F4MJ?>yc&HWa#r~tpCOXz>c1vIU<(v9ESGENVL6nH#7O=Gw zGsRfb=17&4BkX$yp`>$VfxtqXd72}FLJsOL=rgDZO#cqAcECR9Zt2PnhXXE6!GHM* zGB}6}y#C6f>wks_$sQdwe9-2Zw7AL%6%=o`S^vhcNxj)G-+~5k*cGg41$Ad%?n;~( zmvbfA2Ez!>W$tb5ac#LIX`9sAPAin6#BR_H<%&#GM1Z9H&JL$Mf^&QAH*|n^1_7VK z4ZX-Kws1iH8*H(mW0EFNKVcUOV9v3xOCGVioQRW;a=*%CviccMfG!2aZJjlr;1*~^ zh1WTFPVyH-j#dP^KGEEohdYB(MDA%VYsgO@D##twRzq+Zu|HUf^~lPC>+n=&^)Anq z+9Z`~09qgH4%tVGC>5r7FyvwB)tJPoo0}?}z|^tl(eroED-~|uQO`!wf+C-)N8X() z^#06m?UYF6eazNof$~ju4Q2{!?OyavFVWaZ22~=J*@xyRMpcF9eNJPR-^=Hhj|kyq zPU~nr9qe+A7Ue|7?y140NmTFtRAw9q;Y6=rZQ9Kl_{*#WAO%3-%rML9#`?A* zd6d7V!?ZkS%U;zy?JS9jM5Nl+@cFbLtu(1??{N0>58|vu%wwax$O&`}F)U$vu+3(J z2Ly$6Q)-Q%z%%0$t)MD)kmEfk0YQPxa)sAO>+CNIiw~Lf2ggEaq3FxlXV`M9H+S=RW^C8>Pd z=vmig4j>L*=>T%9E;;jI9q`FMkj^4D_RdO(?>BbrbD}M&E2$Q8aHvr(DUNFdwWh;! z%h#V3@J{Bfn$)$-I&`}zcf-r(2+hAsg-^nKF-knkYcz^w!_R}z{9tLea^@fUC;B>L zIG(pJ#>6sOc^}XP$|{+$P0AElY%ETn6$gKQ9*QoK8?Bf5YWBxMXHaSli7ktG2#PcX zpUoi?I4Hd)>Dhd#b8-Mw>wX%1J`c)qcy$nn2Ze9q$WdKKO}TQ(AZG>Udyynu69{nw zbqetCjk#gK-;|f0;~nt*yYczsS`f8T>v*^qk7jpaBT!szt&t~Hi6nI3VkMCyjH#uI zdq;&RTef>K!rsm$$NB)jdK|Ou5;aU6B^gD$3s840T;|GJ_bLK4yJhE8wqk)80)GwH zvvZGj6bNx)ILpR3@w@+r!yrlPz9{^v)|u>VDsKJ{Nx$mt^^g^Hu?>_oFW9FqOWIdZ zs)nMWM@0I9+GWleh6$Nvaa^PdcvWJ1O*?CL z$$pkJ%tr6x?VgDxx)@BwD#rNH?QwUPE(xKWfCz0}L za&8m>fCj(dAd;%*U7C%SS#)Mx+=jiGLdRG!a6zsA+j|uBtFK&l1S2yT8>#LyKTD6(z`Lyy;HJukFpFvPy0H?%ww2>bPeRD01KN7iDG}|A)8k56cJ{@HI9Y~a+&hH4TD%(qIZHzVjFox22ZyShbx~loV-9eU**aKx zG@k{GstBYQXAiD)(w@uf9YuXRc&XIbf;pZLdt4sS!?y1 z`vf=`ZiDR6I6sGwdQ)w-4DHYXIRnrKm0u+k8fN<=Ii2tK1d%F>#)*)E%@=Y?I~-J#4SyHRjJL{ln0h|}$0pD_943OeE`|k3 zAA(6t4T%}l#Fhn%9mW^hq@#++5C2^!ux)%9b;@x*yPJQ4&1+_CQ00@aIgsaqXmNgE z4xu+0QZeEG!B2}1wOYBT%xo1_iMrduCL|dINgL1bRYb^eYjpW&iN5*3z#nG!HXaeV zp<)7e@oI{JFGa|*oH*pg!4xuuo_HcZP3}nW0T@jLri!8cMk27s78zc~bBJg+rwAPJ ze8})@?K5>(C*7cM2kd5(7a>JNP%L5!06VWP9AkX2_94Qv7cPvi?@C(64gZiQ=u~H_ zAnVUV-qSP(-W##S)J8adDPs}JSSXMx9tPdFoO2=0E-a!7Z-2BoXSVsbT086wG?J`G z78^@wf->hTv{r=(ntdGE_xH|ZKwg5;+nnWZUFCFFpR`@T*VWqjoSU8pD<4wwY4p|T z@ZYt|@sFE|#>0acLU4Kau=2Kx?wt~A7IhMvI$e2*woJcrH9)6nw1hd?^soR$8h`Ig za5=@IJ!aiv(*L1B(rZU%Ag_{MoO@7Zz)WIYv$tH&3Whx#zt^%6<7pjiF%Lc=L{Tt+ zxw3>?RCx96mj)ufLFf=oiJ_y9Qk|T_tq5UFg%wtNK>S}>xJrMN1BZOhkAbgFz(h3} zi2+A6`h)n}kTe4B2-X4Y6XOkb!#oHSIiH*I(CwOAI%xdj9OJpAD{y5^nEYU&TXf1i zkln6&ywXBMe;@`90uH)Nlz_m=cLTw|LpR9Uay8e025oxxr|Pep@7i39898Bdvi_vJ zQFDtJ9|lj|`cfB*6!>FnaFlA&N%zmlx6%xzSmyl+>LTfB(@Ws% zo2f-wszb!H%XA%x0L+BHiA^sa_VxzGS4-mC3tSU=mkNZ%+RaUFn)?=6qOa4c{3o}| zyWr2L15zbbmJu4oazN~kQ7p4jjR-_&p&QoEee=7~QqMpzA|r4mAe~)9BpB6v~gZ*L^~eIgq$O4}M{#n!~+O%VbsrEA_>$F=@nh#q;tCvKTr* zHk0`5q15^2c#^K&lgWCdxA6 z8&8|*N>&7$&sn4OGR#_7k)x3p&T&o!u<0yU?h2dGqYcd7s!+1WslAx~ zDHy$TVSe6*AgZAo zr&D()xlO8y_l&^anDP5E|3L5|B~7$hol@3mrit*!np(=)GiWd9WpVv{&kLFBI_yK| z7GTRSiv6d{ib$%~dQ){Du(e2k93acV$S(psH$iY}FnpUHv53#k8s^{-HFK^VMR7$@ zoz-2>5EP-R=0zz}W^hY9`sN%}+^HrFq~PcPx8>WzF{Bac7wx zY4aEaamQN-G=4RA*vbnKE*>d}Z?t9KM6c*Rv%uFSL1_3~=K2MKdD^dUv>fSR0f~Vx z(goaARne+o)PgU>g9wsfYjZEX|JrzWOX zsDlHLn|#jBNEQ&oLebn4fmexlKz$h$JXEw89;I4(c*FJy15nMKpn%WI#@ynHJ7ZSur z3KZd`O?>QMmT5^T19}ar0hFl!xT52}n8hLPeiAYI;6EkLyEPe%NTrxd|Ho0ei7*WF zKhUsowhG4oNnN!2A|w^BL%6!`u+4Hd`#87E2DbOI@pz=)wPONP4-jdeHOs97==>}S zA-(p@s#4hN5}sYWxfrb)Eajz9FStnv=nKKV^%#J+)wTPT=(3QJ+DpdB4he!;c0zva z0jp+Yc7dfit7#nf)@}z9{}MCn+23arCcW+f$$&m%lwDD&e&Qw#!a54#hFyw+$xldT_QQlmrG0u`v4OJ z4pv$K&!q9iIfTFU_$HDH6RTL_O?5j%q!y*S6>|w^`sUIu;+F{9JZ6%Z?{!Q6eSfAy zT{tAXBaN71W)*t&r4#SszozZ1z)ZYI)Dmef9PmoSqHb}?lAknj#5WUNRcJ({bGGIl z8IMwe$k{�@UYN`?Ut8c*mcZ7%+xm#cY>fxN_c~PNh{I|U5nl;;>dItW7N)d;)+Omgb z+w%CYdzpRnP52<{DDrm0KCs`MVkMs1-s{A0%spohQyX3ska&==$oo6o$yAc0G`y3C z1;)izk8_5U;V{oxtzS z$=WFjuduJEB77F3R*bigcC6Q)CtGTKjD_H(bz)3UC1br3uic3obrXUn|M(UCp1=u7 za}osAYB3rJbzLvHq}9WT*tR@UR(rd8IIlJYp`UD5j5F=Udf18Y9mu4a`D82WP=UFs zkW6tbiudbyCB$g#!448SuPZ@dB9hKyBz@<&maQZ^6Q!E*r;KgQeYo)Apu-i9h!gFc z?ziJBZ~0oisz(wLYRGGL@PA?l%F{*7#tY<+|Dc4ZRb)%!(hEmii)b8^W=v1oIJ=Q& zp<`v@5U)?>bbfWn1j@-juZvLZN;930H>Z8YolkO9xdv}mc0n%dxLOu#8OsTiL~B}o z(2V~Lq-Z_VeyD7!s5ebEZ2n_i>=)J<|k`tI*_zPKQ+Q`!PByGL|`#@4AGYnD%oG z&OtFrxYv8|>iM`IOMc4uC9+B|N!^cM@->{rOJ5O+s%HxL9Q;u_oVYZJ%DFTaifDXyy+$(U^$}Mu@ks^6wsk z^>PvAP|H)a_jqNtM!G~%5@;trLux!iv(JJ|XFplKoxv!>nHRzZK9K4WY-CWAtl#K=dX<)9krY#fI zo-HojS%WB@>`Q1i{xujRW(z7$fZEuNFm+MCr~4@);$ESoyc#9>B|}07$Y76lG|447 z<*xyRcfC7$M1$Lsi~L1wt;@^3y|rgc6MQgG6PGxE)jk(iRVVNxI=3=6Nhb$OJots9 zwTkd_ezO0^6z4`f!^T%1dJDa@33zM6PVCL>rHkVvXW!#&5Y3i&(gvm-N)P8COhD;v zZ`@ZBmxVUpB&*W*nuD8VZ~fWj?6mRSfW?w})~@UMk0gv@W%{c6F#ZPQeLk-+W|>b} zz1`Fv8c-9PP6KT7O_j#MwV{L2s~S_$3W}VY*`gc5v2A2(y?6fJ7*d&39KV827K=yS z$&tW?R;c8-caD=jS)bHS>~KR0qZ!jGaT@*%mNMz2>sxjM#KGJ@lvnLW1g-+8RnT!5+V4qMW5LWvc95 zrPuIEuS!9wl(gskhxJ+Ch{hnR3R{7|WJMV*4#HVs)jECD5WLZ6=#HZ*W~&5VA*8MX z*%M)WCE_y%+B2i!j2l}EFyNmcLHwFZ;s|&F4IDI+%4 z9&s|3lTf3YtUs*dObTc4yMsO@j}9)-?SHoD6X1xnwwH)O3q4t#HY^zZ<4cKYE+QPW_4t|t#`)D#kC=0bd1eiWf!|Xbw zz#p#&tWLHu&eMsdKU_dx7_1iSJt~&++oP~$xmV+BE>d$(gqw@mbqFMl?k!0W^Q>-Sn4k>OLGqp9aG6LstiO)xN=e8iaJ{XkN zI9|yIBrV}6hW$*~vheCW?!xf+5A3*>)Z(VW1XpGli!w9qvHy9h9@x|nf&VxT%z(v1 zCcR%yj+e~i{&@ezGU-FSj1Lu}Hk758k1=U%0Wn9%fXPv}o_6wa6~6?*!pxhIa7X4) zO{dCO-6K2;FF|Cma=BVn3mhnVjTYlaVpDuz^!TGxxyl1R5_eD4gY>f43^OF083_>e z-yTWS4`W(6Txxm*3YFhN{Cl3XkfCAYM51iBU=3W&JP;>8bA>uwa`1nA76f>XdMVMu z6XqxNf(Z#-Pg51_IE;-WI0WZ20Pp+(RJiKXh}wc>{ZW|HpJW~{F}<9X_;0FMO>X z@$q`D^UKjVXJQb++!fGZ&Xki23&~2;E%Dk|i?@a4+D-4SRr#SG)FokofwO$A-*bQL za_QwHWVP{&d`Fc4&g>;>CE5eq{j_knN%1I`gex&!o-byyeGFU^JuV>q1dDL>O z?NE7YMUG@j0le&a7XDjK7WoFp^b-f<7ge=W@W55?|2+pm+>#Zk;C#kU^nHkAutBWzsX5(0qc7uXJg1uky0;mz!OI&*tFp_iCX?O z!L5l3VUFN6mlk{9n2IK>QXhKx5?QtxV-p+12H4@p_K^M391*GvkW;eZSmh~BlFiD1 zp{MdFFv^|3Vi~KUcMM(p6h$kybI~PdPBlQm+Xj-H84#9U z{1jEFKI4&h-+F^@T1AF&P^$D71;Cr*g&RggFxm&Cd`Sx^F{F4;GYQKWq4p2+04P_`?5cVIiOlrU$p#vUk184HZy6*lxoc_4lrIj*;zSQWCVu^|?*#1I2#a(mf&J9S7+ z8d1kkLpx4sdb$lUwLTwwiMYfMoTiiDL2~n(4rIf+ZC?;$K(6uKrg|dqSPCV zv|BVP@{|`&mQ)89?sVPI?gdEQ=~Fc~9;z?wJY}{U@TfYG=_Jy>jgUJ+F!{ z%p#V8sp88SI(GFR8zj7_*KHuZpW>x58d6jyal9gS)s>)Sw$H7c-U^BLZwlOw^=9QJ zo8q3XYh@w2;M1qIn?riWDgjxi;stTN`~`r2knQ`}8{R5xocL7&4w68Zl?Mw(1LKZ( zl~Ix4u8S3BUfL{-NeJc@I$CPm5o)sA%h~Z1iiE@04*%6Hh*l^+csRqdWY<_ur}@5n zdEXASRQ}iRevAGboZfK_??@Y&_C=_BbtN%AE3Cs_*uQzx|Fn^97VD!FB&QSZ^hyp# zSdh|JjTjC?aqa=$(*$Nk;KI{CC-?XuF=?3e-sVDoW zG0H(-kKX zF`5OB$izYmBo|k??b+D-HHhUb-#?Uk5mEz7)(q!DGoHpBnw-g*;IVElsM~zdN$M$$ z4^XFP@$1IVb59dzk<9mpj|bJ{NQr2%fnTjts0`HA+U~^Z&N+@g@T$683l~XktUTeb zx316itfyjdVKw6Up3I4>n!{#_#?qwl#_r&R%0dMBMzO}mLBu|b>kGAn= zF!7`+gKb^M_H+wa(-N=L(dnI7DXf+$CXZv19D1(x#SGRDzxC2*30!O&VR$#wf?5yo zKK@}=yOY(7c#^BWGRO$(IB-GlV5VWQUUccmxV`iVoJGHrrt9ztHH2ZlPY#HWc`~yd z8jOD=ut^BHUSX=o#zEktdHnQ}8REW4ir4KRsK99Ala4{rmh!x2#nofu~0n)ts$tm?E>dESEIWSC~>k2ZafF4cHt;8N8oT2rL zHF-MGFO>szW>LVYWGrOBqLx+eUa5fHblP-3sQLGq*N>B;Q!DTblZ8Z+S)UG&!W_Ei8QavQ9>KkyMb=~|d*dL?B5%a_UL&}0b4=e5l2fLZ@vR2vAB z<0M*v(H4LW!(QV&g5^Lmm+Mi5XU$^&nMEAj&M3eD5vsRpKG*I$#MGr5P;g|%T2st- z2oCZzNQ8r|w}Xr$Y&?CxPkqnRud1boi(mbujkCx-a4gOHd<&+xKbWB>fpPO`2qezC z?||aTBz_al;NUpA*TvAKk!0++N8onES`NgDZJ5bfi?11|@}&43YI;grqbuRs&R)yc z^oH&0_SlSkNRG65L_iEOC$BCgM|FG31Y)Lz-{FleuK$sD*JG;oPE+HGgQ%@v38woZ zs}FOsqR#uua$IJ@l3z={v6V|K(_uZ;{O%XR!-+Sl!j?jwU zu7Y%{X0;5upg)_#{;}gL#Mx1i7yY^9$qdvx+YK0;`Cz`V-rSon^E4GkzYtO=C|24lBxg5QF>;1>?ABf&gaj=Gzo~B(OtlH%4%QDO`o`JZk}y?C;^( zdpNIL-#xry9beJT-&Jw8>=i+8nU|uPo`!_jhVU7~!eHqB;UMgE{e*k|=ppvf~>Wi%F;*PMoX3*eNQEU+{=On{jgzUsjy-*fsd)RQ1R+pbr-5ZoWIT8H6Gp< zo_Odi>n!L0RcN&l?L?LGRO3D6%FW^x;m zQW>B65K#0us*8Q3KnTs0RSE;ILGI6x@52_jwfcmaUip#tlYP!S=lKBRDSaWF7^P;rld{r+s=z0R(X{Tugw#MV;Ji z4aJ*D>gM^JqbS~>_#W3q<5wXRIUTl;cUy!*q=!@90|!+totel$nMip7Hu1ezGKyv# z7S25?lII_ec$jZFY4RAIYTd+r|-ea*BmzYTG z(DKT!#e#WGdDMgodoHiHds&?sJ|3{kuY)6|%bFrq@s$q1=m0CzkfQ=~>G4;g37n;U zkhS{lC+)k|bsjaYXPpnz8|?k4-8*DcG)$f=rL0WxI6(n3T=t4eQW{qE<&$@fLd ziGHd^{5x_P-}obHf41=jb??H})NDJy5mnq`uEAG2V^=B%TZ<&mD;&|D1i$A$<@bs} z5{tBK0qI8aUGWTi`^5y;77P6{pz@tAPW8vTkK@;?)zbT!kkzfA++Yr7moUGDZAFH@ zBTHcn4bz{zOE>peOA`kR)pHL?3>hL7FNGf0oM)|UkQxt&9NB3s+jgqKioyq@a)sP7 z80jRBQ-YI+0WsEBaXd4>@f&Vn2xtjCH^}k$sl#N4!;ha3J{uPnv8-Wt>mYU@r&#<_`z#R0-h}bap zI-X#R>%^LzWuMwbaCLo#+^Xm8*xN~G$9G{+5Oy*u|4bnKP^dY*jbXjUN2{xmDcNR3 zh9e7*+qHBcc0%d4H=I6rzbj6W35L`>1%cY~t2<5{8Nj_mtvn~9{@samY}`yOE%?`Q zGFoTKq8u)@>pYi7N^YTl_`*#2HX=G5^vSpnf&N8< zUm^rXy)ellT(NVjrfW(kEGqR)C(_m8c>uU<=6*;TN3pdmN-EVU_N|}jM_Ow|n~SX# zMhu*cw%KLZ>e&vVVF28UF?WTZM4Q83N({l^pea-^o%fUfYDaNnj7sW+|c-AmV-E%m1tjlai*bj`*MM^!+< z*c!4{!fJKY1y`-fEvq4H3!x-CA;>CNxC$YRw`A zl<>hSFQ-E0n6ik~=eCjJwbWM+Hq;J&XyZJ5rXz}u-5zqCbCh4JFDx3Rq|N|3e+oC^ zMGBzEc6bVLHy%2N)2b9jTjIT~U_87JUMx^!gbB(DX4gJ7mvtbT^Ye9}j}Ec!KBhXz z2RK$Dvy#}X+3|Q?5;ZJ-3*9?-VDE;`$!BPa2=du9v5VRd)fA%KY!Z^9{5BL7oZc6} z391bqpZs-t+q;yYB8!P2mU~>=77M9pnY%A4raRma^@FJQ1sa|4@SN0*+haG~(n17%p zjm0YYHEQwN9lQ>HCkY$T;}en;EA?boYs&~rpXcCbOj0$<`*SJxZ3@} zoec{miXAE(f~w=`<>$MKLgzYorOA_Yigp3kM@8lo0kW@S-5e`JYyg=Hncg86XT|dD zSC@B16$XxC)D#QhraV4gCBGzLY}w^B3QkPUhSMugVhu)pGSl@Zh5Cqn?x6bnh5bl~ zc&dJ&qQS4tJO_P#-7wWy!5S&Hz3h}hP@ku!R{Me3Hf0P;5! zqJf+!-px0=CrtC2fr1fyGNcs4^wQXmahWE=1Vo0h`*3HBho}8tpF^HpUIX$7ceqyZ zdtOrCTG#Em$yB&Gx+VUnzK&)2g4gezgvXY(?vzB{v3O-rMG|wf$cc)XtbLdo7-j(% zF^!tp+xON*#n#)|gf9ybs;YOdDIz>ZE>cJUS2C`Bfw!mc2#GWP1&wbo$Z58MAw$@1 zpqJ=p{S+X38Y+eH-x|+cvF2@Mk!~wLwZ70QvWA@r0^kJJorOu7`+^TI=&JOe=oGes zBpRi?RmxRX_)1OEoiZBDeuZ9Hg1uwRM(1H(_2wNNfK?sR*wdqB1S9Y}(Z16ZQuu>s z>?7=9yu5?TgLCZu4>=UL}$@XV~BCA+1(!iS{x`7Ed6u>89sfWl5BtDW5kSgZt2q< zA+US7vi@e?#CSf8xz0SaMMnK023MzGvC|;aT739ocJ{8TMz3yZdc!|_nQYFb$l5gLeGKC2 z-N*xKdP}i*;bSQFeuWjnpd}iZf8C|TGV`B(e4NbpC%oWN5yU$$U1O4BJ)cn1U{0F3 z@fo_qVIxZCGv(>W0w>?WA?m@5gACnDb zKdCTR3n!=FzQ38$ixkj-nO)!W@wEc0U-e~pTVY3>EQ6^(=TSSg7&W)un%)^m76&iT z4>FfsT$u4jnS6Qu>ip*Qx&bD`1_7@T1(=HEEdKnZHi2eZ(OCU@kpMX)koGYl+f&yp zCEH)0QIz4uDV*z{$UU6uS?JG$2=gY|6?#O5!&g|tE2kETsXg3C!`t<6#n@+dmmeLl zi`Oz8;D`vsRR1j__L9sFH*kXTaK2JzmnX-|7Inhhr4gkP+pm zDHrDt-5%_5UC{et@=MOW63_5jH7KqItpH^)Csj}YSCNOY(*&qM(q5UY)uR-^(UmoN z6fyK5AP9xhXdg4TusDaFqg{HM%9fz$xX54E%3M4jfK&O`goDiWd|g!#eZO`0%+WNpP=RQ1utfgw z=X9<3)@2)4mIqLR zf$m*4J1XlVXNb&EdDMoz$0x*#g#*XQ^}*Iqgd%yT5vJlz_~v}{c^JWOdxAu30dE6XT5Bbwx=vC-l-g>VF@c5Vw<;18|^B(tk2T9x= z9iw=QrkO_q{0R0PdDhYb75AIlf!YeDQ^|)vS;xbc?;(Hcv&@leQr|s)z>NY~B6K&o zX-Ut%dKUIyVf+z#luYP=FnBR^Q4Ul0#daY;=2}=6&1|9+dPbg(MCZHMt%x66LYKJ6 zx+sTMa6IWneA+w_@61dM>J^l@_jaOK2ueR3XqHmCDP|?gW<+ZVnx(SIx9+O&kmj;m zt($+ZO~Z?}=gjJBhzJn1hFc!iI=|mv;3IcPbp&tLA?q-30Ag?POxv`R5iIBbAmxA> zFQ1eiI8Zg2?yLcPbfEZHZR}a72p}C}Gn>gdGEf)bv_CwOBMJgCBQcADX#?Cfj}Ul6 z5IPu>}(fCLMcy8aWehXgZHtvFB zhnMr&$Pl!rI(rpMeCT<`ULb3K+_SA9Tv>v|2N0?v2>D^raa>`rEafNX&gIQWvuS}< zIKzcs&nSh9nGz`)ol#@r2L2I1_o0-b=}pfqBVQ1P%8~MYjbw4K)S223IH-8;A-XI+ zHP~KXU@mZK#rl1o>@|h3f@jC80}B;Rkm}X}mouc<;g92&-c38H;i_7%d@IEvi?blO zr+1FBbZPk4!Ngsbcj&&>fpk`-L(5^VcEf~Hv8pSlvp`p^yb|LZ^EQ~RUSWsR{3@=e z=O`w}oU>g0nhPeUzq#l5wnoQ3!zRqKn&n4fp^ys1Q)WHFLf#T=IB)P~p}UAjtkn-a zi2vqV5NqA9ss^FWO=5X4>sj^G*3rHM4Gan^Y2OS?4x532d6mXDgJ|oG;9sDxn@)37 z4xlLwp_=(1dM0BUH1P+8c^@kTOZXjUcB!|ImWE~^o%?Ra9fdaB$)9mJA6n>dJu8p>NdS{IWAjv{%Q0x^I zJlHhDln4inmKmCL+Y_saqr3w6-fFac2ZD@yj~N3N?%>W*%tEtY9WhWINcspo4X?Qs zWH^ibt0jd(NyIxeZ`@g9$!^DMf%jYe!o3p9#f8L`>zl3C_ zUdf?awCG_f(pM8CC7_~&-}A0z?c#dy-yTNT6GM>4V#jch)UYrOH?dF8m-=N+iA@z$ zXl*3FzA65p)IU#3a?WEKucvP+-PE@$1}Tnsk}qYO8ySpe$#AnWiT&AbK(Yn+2h6{+ z-pQc+U^A13v&@_CH0=cBPXBbyv%QL9VHIYASuL+6>+*Q1ne7TLXU1f$aItB#aeZ$} zJX2WYYf~sWQaRy+8R6pM7pB?c*rPXcEIx?8dXk+`QO^$;P{IIC z7VvhZ#kF5gEFGd`yDsFBlVz`R|Tz65GBV zJzMP`J-Y5&0szI}#J(eA`_EkxsnMW+h)ly5SWpM!u0(hG(gGZoq+yMA|M`kZvCWjW zh)HG27~?ZXvI;tLjpC2V9m{xXAa7j9)!4cLy^nIS#k|Y1sW0=`n;iPn;;@H}JR9!d z1JI;%z8XA-oZjk}8*tU_Vi)GN5nB{D+#h^EikNqbD7AnVC9Q z)d?Ui9sin`puv|3#Bjpl>9ZQ|c?6w_O9rZ2)y!z68U$R(sk1p+I-%}F{}@PmGtS4s@md>;8pj>yoRO8!DOV9q0LMb)&p?= zqW`ovB)YV@<%3|2TDr$2DDzoYDB5`Fb8J1sEo4n}KD!+^VY!!jwE0V7b(2QU@-^~C z+a8PAY*O#Xv23HF0=5=XZD~-tx6IhEpSZdbOr&dkrECpv6_-Qt{X})8eGAsq)#`i_4$Sjr4Ro z;cX&!#Cp#*_9q%hUisFmBhT_8`X{-S?Ygr-&R-Zui6kZQC_{!3>0|PW`$b)WPmthp zE&PR$m0!(?t2%at?G%+Cgdw5Y0RL4XrlCoQK?@9u}<+TpH^Og$nm1>{lQ9-nG@IiEkno{<$`N*;aLi-?eQ3Q}b^*cWX}H5V?6F99 z<&g-i{FZ9{?DAcc!&8DTrvvbjixitp0Q!Q&G13fbsD6pxl=7V-8!}Q}mkqcubZ&8} z;Eid}2*#9-wOCJspd>KX&ZR61VaBC5B1e1*zW|HzueBz@qs;pP1(-O4Qx^^Np`&wH zI4Z$Dc0?!Ly|iLJ1?KYEg?z9TQ@uF?>meg56&dG)hmWE&)D2t|coz%vjoy*B>pFh+ zF6Mc75}_Sm&;U4eM2%AC<$d=wowVb0FVmKT__UuYCZplb=T}&o?;+B5Z)Za5GPpLU z5^+*(CBfBA#{p)V+iBh1se>*<>oyv$=e$ogf zP^FPQPD4l9a`?fPq#!_v!@{OxmWm!olJqjqE)__= zPv413M$>W#tPRx@)-tys^I5eRfdTOPtW6f*QZi3bohuCYhr2HM@%0DK=vioYp8Qsr zFik#==vTKsn>J|T-_*3d9u&Tra72r>Cl!4&N*s98UL-;bccUB8Qll9Y9k*p>Ss)vZ zD^C5UP-XTZ+OGvKbk6D*mGEm}63S%t5|BM=I3X4zljE*4q{nj{iihSwm)j4c?eB{u z4J+>VNLmc1HuJYgGW6im>+zvcLo}5@f`;}n5bF6 zICA|`Mzx4(JVFNgbd|u*gw$g5s@^|lQur==PA$WhzP!uW&p61k>i#}BipLw4(cP1d z0e|39ChkrQJ<>!Ya`<%?`ZJTwqmKS$%s+NAqs59LIzYUz?`uzgfVMaiJ=W(qg$?%e^?p?#|8||) zHRkpf=w^Dy(uIT&WmbhR>rxd2A&dQXF;ta8s$)b5Av}gS#BYd%)-y?E=yoDaxL-5& z{3r&XO#O1ew4X_UPWmXp%#YSOEAF=0><8P5VwuW`p%Ru%Sc73I!mm~$nU#7T_EHMz z6+I#jER}`@RUI<9OY3o0PMs4Y#m(+O>trYiqt&J@Rw}T`b*#Zd^lnx-^eL-hZ4I^3 z4ZSVIQ8T5qOokT!hEqz968b$*g?H7K(j-wE!5Cmu;GYxfWy5{Wv`7>~YUKM*K=~(J z9)-NPx4A4L4yC=+D7pE{($QgvkB8@Ex?pCn#9v~Rm+zXyFO%0?A>bj%D`T}|M7yta zN`l30Ux zZ={AX3D?`zEKHg@8gAP7{1%yMwJ4B%atHqD75+DglJRRY@tiW4vO+7mV6%96DhBDC zBQCbn3H+jveA;X7MRGx1ajqK@#2`oj-&{*FX-izOuApFfdkk|3R*S`Cs7L1+0bvR| zu}T4^c*%iRQaOp)DG3!eOPT~gjr3Q!t~R-hqmpJpv=^fj2+f;E&hxjtIx58!Foo^^ z^%`)D0dAJIK<$U5Z}<)ZK4yAq?}mcnYjS3+Hn8*>DhE=YzzE;I7`kUks`Q`tqn2%b z2(=y(DgmKPe^})IK)*`h4d1O${uPt(kw)~LxYwgm-)8G;XatJ*Ltq1t?0Y_>KPH+ch| z%u5`R!BOpH`h87Ewf)tp#SrS~RikcceE^0)dA}CWSuE+b*B7#-A!epAAv4Kqp7WJD zADl$(TQl>-V<|l}5bsj9@lTk2_Z#m|bg(kR1}Oq@ z04dp@mw*u(&Wr$})C50GL&!P+Se2pLLVVV0+(im1$ryS1M{ zi<){{WAAg!nNOvIy1G%AAi*fz7nN8u*{BOZEkS}|HAJhAeA$pMs%sjo?tugglCBX5 zPK|_!nSatl**`ot@MN&;tI7}<38%o#;Z1W94X>&GmkT>`6?8>~qFaZg8K*vANi6Lu zb!5Ed#WlvpF@ob>6PGG7FAgVL42;)Sul!RN3jPNgF6}!}L?<9k*l`y{G1o#faOF7N zOfKues@2$>rr9UTIwj&XE4$x`D8MYa+V4ucIa?+gYC-LEqO!(SQCTwj)5O)got@d? z6ZTG+)uHcRvZa-*lX7RIB*|9bjnr{I?OZ-I*^DU0S|y^B(;iV@iNLGUO#tx&w`d&- zbtJF5py`WGm591rb;3!?Ak#VBdfsXeUE+6SjQii&|%4+(K7a(mVU$SN^57#|ND$E*hSS{NJ zV$`;4f!tkdLv9$aXRn$ts%wW=YJF(M0YVOT z2o{!H49X9$qsa(N?U!*%BOYiU)5dV_0N=Y}Mcxo@a49Pg1XaPfKal+p&4j)9&|>pT zW*)FU843G_E$`z#>^RJU#6zNG=Gd06I@z%+Vw%Yi*UbsZ@030R!YGZ$@q((oyQ&Ds z$@>6k#7wmIyjJ#FUExg6~|8`YTo$_Y(Jmr zb9_3o&#Ne?6Z6f-9wnY!fuJQV$P*uKYt-akcZ#SLd~k*BORtF1lyLp&&mSqSef6^NhGqAR5;EZeG64X9 zQ?nm306H6D7PKhzMX?>@juk-Fv#KAa_poKA?fqe^dx%YSw9l zhvfOH0gro9ELKe6X>L;d3)%0en0x!Z#^iRe%@h1rK939X7kg89 zNY*#-w>o$$cJ@N(K_7jCviMKIw@L*HpqUHGMwxO%yl3=rzr-sb@u_vqxDV4YAA$~c zCj4~`VcD1y40ubrk-Erydp7q2+xVn`X9lkZUx+|HYT}y6V?LwBofNpDob7jz@6}TJ+m)>}P z>-9U{cR-xvv?*98B1ub?VE5w1^ys&69@?o6>OKlijs8$>TXk@^7s-XE80ccY6Clrnvcb}$ITxt4*`5fW94DW8 zvPrk4ytd^L zIjn-Q$ZY6>3}kHQ5hakg9nT(|z?)+w2Z4JCVt+Q-q*c4k_2~M2-)a;7Z90%pJUyxs z02%?v5&$x=1}OvpL=}~vmw**AjEcMywn%|1;OGEZyWw1ZPVR~QNtWq9$5{vD#d^!_QzVDq-CJo4b6V!UTYb1 zw*rxDG@DV0jOq_@WU@%5NP|p$((?#5m6zDyliyGlsQ5v- zJ^@?-{s(8{F#D(1g?1}_78qud@3p+fxAH>vEzE2mwa4Nz1CGc^EqQZT|M>VlK~34` z?e0M2kJjN?PhoRgad7T1jQ*kI`0{QA7T(g%GQl0JiKNd9ccJ7-^GFylfgWQO`Q9w< z`8XZyHbsn6zphM*wL9Yq(`U>SBZhoWYssVSI_N%Y8yxUUVyJV;EF!hget&q3m|_iS zWI@uV`IcnQU}2&G&r}`ky7J8My}9=P2n;k&b=~k+QX5NF6&0U6eZfJC=b8bOGxDYh zthC^X&QU`vSPBD&C%-7o7&jPTHnkSLDZXp5Ey2H+nh@&N#f?Knhi05Y6t%+{F zPE0)9oQez&QhV<-P6h&dunV*je|A8DQyK*Ok-Q1kp>r@e0gQn-Rgz>jF61T3e`28g z^4gjE)kt<6_hdnrr9;AD)GR0y5L`7@^>hJW4~adUS_thpPLS$#CuRpyNd!Vu=#64Y zuyqtHoPK?fN}pd~F|%6>mRT;0C!?N?=W>>~OnZ<^T5P!$T+#GueZ8i0#L-?svjxp< zn+>3huUg(Ul!V4gC#(GcBKOE*X&HwmCw3Sp)NI`U$?^a2sEMqR@Wh-!_$MRkp|r1G z6fj)QVSoTPRALGXGt&OIg6L_tHH_d;GFn=V>AH6OTUjCM-4?YWEBMAC*~oPG&?r4! z7bcEU-%l%#Lc;QbsXOjUi7$$(tO+zgM?P4F#plBA04nOTXJvtCw7W*qYcjG0DFYxR z6_uZtfGaa*HI(4R<=EwFg+VfrnLz=8v02l>@ukQm39M~v_WXUtN zuiHY98H=B)lvmoZPBU;`Qy6r(N*l*bRT}EmoMH0c+n-TJ%OrvCCb#d%l9XXtd2k4x zOKM2muQDAF9wdEUzf5sProD{$86R_SIi5A6HGy^1(<%*z7W3YJw|*8GCbXl}^{W=Z zEHp5u>krBhHY2qoSAFXcDDs)X6Q3AaA?0xw##4v}z(n6qimtHFcVansW^r&k!bue$ ztgWm|=HFB0rM0p!zW2`BF1X@fSnLSY_?B6|;VYewF!7IF?+?JM8j<}mWApsDRXLpL ze_%1B_Ov$1ZLQ53{+xQs`;|y$~?K`__xp7U8^Au_uJO~>YaF&qaUgor$XH>iHf&}9# zd`UW0NtPut0c$$TIm76Yr%XWk^t6iX5*@b6FEFU2}G6nA;uTXGA*2YyW)_CN^ zoTi%S4hO~jetohG>KwT6U5oNoQNUUF)RCat zd8Oc|f9+Nk8~{O!j$Kctxk~%@9F#KNLl9 zX~==a_OK}-YL4YRhPOwRGaZc!n2Jkbo!UY`BUM5jp(Tr^9e7yermQ zJd7%MS*>AzU>T_QId()txb)O@5Us~csHvi*7cC~q=E+)UlG#I!l-1j})q-SIcFbYq z2hav?OhzL`MB*1(d{USAXHdB+EAdyoEgMKR7Vtb-Sx4F^W~4m^gohs55&#+jm=XXo z{Q@!vA_HR!1I&O9au-FVg}$I@0Q};^)q8gG^`5UVCeHfK^)e}o8!_nY(9zylW_c7? zpS8A_MM7-lRcaw9wz@IfN*HT!e)G9Ls__O&$&9I>#2|}Q0|go8!k#2AK=3PM;yt`w-qr!x7Y1cZmPCMsE-E|x{jmgPzzyx30)yIxE|fg&QtcXrqN?|rmIF!vbiZ?>h>E3w3Gn<*v@kDaG9gz+mXtPK7{&l>* zgo#VUHe((4uKS5Z8Q+81qfTFPZFGAN(?yTiOv+i)@7UgsvfOd4H`^naUQO?9#L*~q#~h6(P~>+!C5Thtst*B z?Gv9rie)rPoYu2I2atVi=~I3gzzRMh+ubS}q1@ahuQh8f4Xu;rt$~3$w zbwaFNp?rRLKnCSTVNZSt$#?w)9ZI%jQ-R2kVf<<*Y%Ji#F}V2~FbM{A*B;Zvz?t=X zY~;&U|5J!{cbql)V&nbg@RPw(ygzzz;NGz_p5@o6Up}1QsY_p-q)EnQoN(3+(X~7T zGM|v5)(|r3AyUSMuA2x_5hO{wMw5R2JBgC3Q3Ry9lL|%A_asU8nhmgc77jyhlSg8P z80=8F>Mw<;B+%vUefq$#=jl>NV`uBJtASHBkiwDc%0*Q4O@%_Zi%?vZec)C-o>*Sd zRPu1P_^8=z5U7H0jtsmtSnI@D5eXVr0k>u^?yl8|SE9q6u_J7Z^orrw-?2VhrOG9J zi>M=Psc0s`WsDQ*$q%3k_x*D^3+z6vmkxXPfAm1CVIyyk=y;WP{bxpXcl9XyBvGUO zZOns#k$4R)DhlJcz;r3Gu|Nf>eseMNq8zc_bs+DZ92d z?rR%aO(bg>?`s=I-A)NQ1?kT73=;y^?$Wji%&@8^EjqD#Ko()s7?py_N{FJbGzEnA zMa|ofetszYDBF6t^>{Lv12PF9MGF@nG5|`7C>D?t#R1>|{Nk7(F*52NUE_2lL+Dmv z&9Q8FZkru0pHtjN>|=I~AeEpH!NOW1htV9}NTCgqRa0ZU%g8&scf^%S46sfH-9yVsVpILcZX=zoqNaDz<6 z(gyRZwB;{{}3W8 zg>)-GEce<)fi3+GFy5NUg7pJ+kvuFodAy@ozQL>-8VwfLis$FMyU`Io~n4rxC z{=Nb7!c)OB0e6C*tTe4UpkB-g=GPv(@ZuDjk^*8Oxy)>WPs;q}AuVIG%?AQWZn2_% zBg=GP6F~m^27?vaFFbQ+$wsdx?(e}r47}z1z{ACqsqFJ>Y#1DoQEM)K$N56bSEYyf4 zI98F-S-<}YyK=`KNkW%YS^ez*%J{Qm*!nrvJQLOb;7byPA4~A0(s2+u#S}PM zkP&DV1?+-mLEK<-8)kN)mCbj-x?nU`eAtc6Acbe%@t|Mtdp=V8^I0$ux-N-^BBQfW zii0v6Yg7m^S7|Q;DfGN%U?A5X^Od3I^N$sm>O*Am*dEPrd-~@3tVJ;8Iw7ZUIL~$U4xr^k1FVL3;%?5m({inv)m$73%G`BXMDe4^u~~KViFa9 zIQ~AeFto=?q5OQuTco*Npx+@C5)i>6?Wwjxy|r@DJuokQZ-fA zU~q{h-%{?G$D^NI*L9%@l=@O_XgFUA*aAAF1R7N-nQU?{{uqtUg8>ywvqA+>`|DP zM*u>Yed7*e-0KN>3p5&eMNYlF?V$4237&L1vC8IUy@gOb3ErXPD!54n3$4c`{ir;k zTLB8Yl2PfEmr4p&6qLi~-C8|3LSbI$I++XSQNR)a8Ue@>05ahNG7TUogEt>C08S?+7NA1> z0pI}oo7Vut=!S5PZ%wMfUp=9N=d8D+$E43>ourca+mg!H{r9L55ogYM@j7N2=uEQQ z5g}%+X08qFDoGCEkE0iKMdxAO$LqrykL||FB6Qdy3i0e+bd=XGzM#=1SVLItg}3`p zMam48wqk6wFUxCnK4+yA_OlnnCCiup@KC7w$n}owxtT>p*D*hp zE53$k$G%w3sj@OPJK_C#q`b@k#QEmF4%C(4+6L`G;geUVeK|zg>pdSn;dZ2J%EBi* z&f+xTHx{Wkt)hx7yZ>mjw}1_ulpsseMkiR!DQtw)Zt3|RGE8!OJ_k6`%KSOD?kYsV zv&?i9`{h`>H09r{7u?ndc~Yj*`dQc^v9$_2Y|LbEA|>)c;+b*cXUS(Ypl5u#Fi!)* z#;o}W6+Zgv;a^0=h4>{EPYbUzD&H|#lUc)@^bM|GqJ;eb#p77K@R3$DVBOgq$W6Ee z_GLw0)+!msJ~3NOC1wV|Hqk}JN=D1PM^CQmLXjAg5ma+`S{C~fU6RRnT&@V{{Xx#K z;A+d;9SU0Osn+^L!!i!uN&3#%?*o0dR;IOBhUPE_k+~rJhK6qmH1~{|I4YqZfD!;Q z7%~p%0TVX^%%A`$0!9E}002M$*_)p;KoS5ll|CU2Fo261-;H1lp(KgK;E+KOBLDz% z1Y|%Xv;aQxEC?x|Rml82sA^Z+fKP&hP;NxMN|MaF)_q~eBnKgg^ED^OJo6xvnj!R( z?8gPJPy!+0Il?Xfyt}%k9E=*7=GYZ;qm&oFpe=eL-m_Fw?8>3F=AC*5>wbCc81C7_O;*2s10xmbeM-1r?d2q;JAmnV94t(LcD z_!6xls^WN7h$_b$3JWGY-%N&P)mDJd9PjT0% zd@H5s$Tp|$q`}_qr16`N(QjGfEa{r&#u`I%x6s+L0TX4lMyhq!qkmc}#_3IwGdG{C z8tGW9p}J^@EoT1Z|Lhpi6uRLU>iFgyNF803@R(iFTUyeNMQW6 z1R7MIk{9Wl+|_1{3bW;Q*ar-B!lj7EHQPas2@V$>yU5QWEFmGw{hvXp4R>;uumqU> zemtKfof1yrHYr6}5{;r+YL$(yqfzl3@5{qBK-IIT5Y5Z|8%cJ$sW1}3_xlzCnfWa~ zzCIxizaC<<#evD5dIU(s=u$;y0eaN<@7By!3XK|uO#*^3CN7KUghDxz)UUkOz)i$@ zQ2|%B%G_19ymsH*39Gmo!N3@3purbS!zM~j4Hd5;e3IQ@B-rY?A1cf`8gZPO;VgY= z`(Bb7Hj*+LP6dI(K)dWo>yPWdt4e?1=y!>$A~=mG(__ip5WssTMj^{MR&LfcN;p7y zzT#m%7@6&vn)jG+0#tUoGgjz_KOx#H`-DRPb_NVO?bv?jx=O^OT1=&4JkS(E#Xmz| z^WLL%Roh)posKktasOkn1n~uR7C5mfL2<1_&^EhJ}(gd`(HnTNNIs?`tA4K$Wp6e;8q6J?OWe{{@8-rW*$vUMmBm5}cu~eU3us<> zq!UdwOL?V_SHUP`D-W`=Nr5Z-Dm@J2L$yGSdFf7FWHcC_3aqe??xl%~Fqr-{o|FU7 zDgyzYlOH8p*tsH;`n;=hGF`8x9mlt{=l|_C;;)f7g*nCkcQy%G~@PKzPeTUtB2Y_q`{^%9#g?L0NlNLWQJZCXL0+o`;N>Rna zHe>^-?~EQH#wDL25b9i{&i|&bIlGd@kVH@m#mG(h;PGo6?pj}TNLfABp4(*>17WB9 zeQ+8Ln5O$RjH7gswmAkFBg{~LM}53aFn`;^U>kIX?yd1PkZ(jL!mqe zk^u-R0KF7JGuO^JD+9-Bc<>1+o1k+u^CakKI#~LE7+cWQxg89$#!2dtGNfGyc5I;F zF~+s-rU2x$H}v$IU}++d)Gv!z$Yl4}fv$Utdiq8Ev(3?IN)Ywx)svd*ZWfP0r#eGL z&L|+~ROSaou}Q!bGZdj~;UOlsw`GMP?g%-t?lTp661=nAW~kRwg5HGHP96%SV$v3 zO@F_^AO?YSdO1u5u5Us6@HD%$i4=F7S>t0yYgbW_y|G6hJ@?c^PbQuL28{2>I_ffT zf`9&x$J}ISRCibY(JT6uB>ZXD!9}Rpvd00^RCd$_=QCKu?{JaAj zO^3x=y?|P284U=!sjI5=+oZM@8n-tLVr~{D2$2`=s#KsYFk{>F=D>DmJM@YRd6mrA6O50eHy zN#V*$qbPQBn1kBND&2(mN#7W4mnL&fZjo0sks(;Y!)bL5V97KvdOYaVoncb*-8l`5 z0W;A)dlNHaP$W66!&7h^#K652kAd5tj5WG;5df`U>W4Sr;%Cc$aq#wyKr{YSD$u9bGi=M|CBzm(O0#7cwE7=E>J+F_3~&AbC-s zF0DStu=jlRzRnQ|)*$gH@J9TaIX$`9V*!;0xQ~+(by*gCKp(Y_n=E)+;i!rX$HZaZBOMo9du4}0Fmic~) zP{#}v*d5hLNai;YfJv-t6aYv8BNfWGcovn-$%u}WSr zzZ`C%lW%!Wsk1E}^ZF+I395*$Jpx_u8_VuaGu;=M07jQ_=) zLrXer&ZubVmpB>J!E}<}HWM_~;-<<{h&sAu%TAFI$#wrD1S$A%knVp-DFAEHM zN-Bcl`-h_XAFrSq6L*9w(LZ!*!W=l18m;r0PwMd0dU$kSjF3EuZxlGY` zTe@i7O`jjzZt6r`ze2mTDNdr9=-kFMXa{v%U5b|x^tY-SPeSwCPOcqgJ7GqFRpjv@WI z^D4Ntvwcx4<8NQnBZo1m%12SixJ%mNbG}a`BXTcB?ah!h`3D%cMCw5H+vrS}Z&Gol`E{apgLTKOC|_U2q+#2hV*4G$~VWLLdL1yxSz6QcrIq}3! z8_0H(m&T)l`lb~wg8`9$zQU zHM&WRNGl2&X-a?@ytaVQ@LsS-LA5K}dNnV^^a6t@fCVGX+F# zSH5h+@J?J6hx;_;Kq%TRDP>SM8MWR=hn5z&o|l9pU|QXz!izWJR+Ri>I_Bs^TDQH4 zr8!3lZCK?z!uL4p;J!LO$tB$ahnirdQ;#;Y{sKEp`k-{(X79eRT?6NB^~Q4n;yYdO zb`1$mNICTb)@<)4(_zjZ37=>zjAw6EJ-vnIq=CZRz5~QpqBYIDsii;K!O=h|%MV{BxRvjK#1?U4=mmE@}rKkIE<%zlH8(qYjyCwzsMj zBgg0Tf`2v_HW$7umnqVop5-nVL5D519%5JbY~;J?lZBS(nP6~?vg$MLA>utU87LMc zl4=rn%SYS|8>srxuugF{gnLqwWC7C%-hjPf~a(xR!j_oPnDXfWV=&zSGn) z9YZ%oJEs_){jJfIYs#vTtPvz~M(nWbHHNK@@Lq+jG;rHP-|4&5)88zVQU96bYAC@D zAmFdNR^+%@QBF9Uyhhd2ml`YR(GfYvkD0nx7? zMc9+OjHD?{i8T>3g}I4gY8VEE>S!wz%bD=(TNc#TH_chTx9V{DZ+ll2a;K&&8|xn( z7Rng1toq6wlCb75AXYMPB`57SBA>LyLqFyt*R4X2YsI0UU2+!12(HkdHVGvYq8G{Q z{l9~@sb*$LhW-i(gfsD_YH8Hy6N5eqS#SOZ@~nz`z|Tg1=+jSsJ3&#G$?x>_k@tK~ zd*v4~k!P$qTNU6PfGRH;o3vots#d3R)%QYA=$6!b#OC%8Q3vv#!WfrSx-;9Y_ci<# z)Xoq-8gyYcP`J$IE0?H9PR^}bF0s!)BMqebjqjPi$^%-0nAFm0q;OhQqA;uA$0Qp$ z{Sv?@@My;S=3AK~TtV*B%&Gi3lTqEt`pOO|gWi7?l`ZC&afW!OpZ;XXSJV zn)~4Lfuf`P_z-T_^q$F`75!G$NP(hkdi3mgxs5hS(n~4TC(a4~z6ZPM%;~ECyZOcy zgDtzA zROv9{hn0A^F6}ihcuuna#cQjW9w_%tIy2j6;6IiX0enx>9@k2=nRYGX!7Lv8_HL~&FxQqL7^o^bM{0pIigPn9rSqg0R7%y*6$gnG! zF0TPLYXN^7ssxt8o^ZazMKe+RXn_sncoh?E%?Z72T{Vj~$PCLlf%{JhO4W9Pl`&j@5cjIzAA@fy5oZkp^iq&D{%r@A7oKPb;3O!>eRNhGCjFyzhA6< z{uS4)lNHFR;()kj5xZv`f>7TC7A5!xF6Uw~q(&md#V+}&2{Ztw?5dZ#uV6{%XQ{J{ z=7rbH!7=9{#cZEdz@?c`?ukAcv#hGnubRXN;W1NTtnKsFIXR z)J8mh=lEcE0`q7>E27<~`BXXL_mBm!SVjv3lU6Tcv0{l}eQ=!gIvQ+s?*|HfiPd|G zN|q%T+lh|P@%oT!`PCmE1zI-GP_jVmh3S-ZE4F@6mWpp?*3*&GRFS=v1LL&>Y0Srg zNM8nndoD+(&hGC-)CZu-6yC@HZ{40}4>(|JE%=O>Ru6fjBG1%sb^@^o*Ut39 zc3T!9+cBkjlEaao=DBaaI^(o!h7rW?obW$n7&-0-_I|vWEsgfW`X^h)shkp zG%rS)*q=HfvultA_jWvc(gte0A{erx$GVTFF?%?2$tDtVPMGI%>OHe2N;5)YP+s94 z8aC#u>zXXZiIOb~%MA1*=nMRMx_}*CSLI_UI?uA(OTktf0K>a^_b}0YSTcWlD}wc1 z_4`V)z|kk=X^KD1SDf~aME6oaM73pUE-oB8lP=K}G|UoCzLGH;iybe6N;eV;+qg}b$u;DED%!0*;< z&}a40E8g_a3g)|xaEovN2_V51!Hb&02hF38R}4sfUtp1N_TCXVRa%d%b>Zux*mwO%a6K3`N~d)RmHq$U0bCv%2x z=uEkmM51UccICmkGgr{sq7i(FcUi_jp^&(}2T0x9GKg?ILAyV=!^?9=iad!#f59}~Io~Y=~=5@+6;MZBwB~w;GaU!B))fd{v9t~$`M#XLG ztw6vERt+A2Y@!m=&MfRd76Ye9A`?99qrvWCd3BwaBW#}V6YDQGNDT*iO&_Q4Jz&B# za9C9sC}$qB@8#_RhsS=n5I~ay-yEAfo>ZFi%-s*9NHipgMckee^o`Vp*aI_ciV7~) z#LI8uiks|`)ks(|y%gb3<+7~b-Jl49xc-d2L5CIY5h;dB$fRDTPON0D1@eNI*zJng zES(*Cb}7_ML6s2B9od5JV_MZq;2{D4c$1BuM;)Peq)EZ+9ETngdwDeOA_9o(g%G*P zj3JA~-1Sl&nJI6J&R*D_W%A*8lTglolY9(1o9gcJHxD&ZU)b9Ty_EpNZI{6Q}jp$kR@UFw-omnJv3qUDBtzg6XMJu?sNIE`YNKI#w z8&lxHvy8#;6TI(jo$-b<2L*!BLe(2p*$4vh-`UT+1|z!iAD6)tO(NzQ(NMzK#_-XyZalSLWNUOd2Wg9}FGej`eO-j4c2F3$l;r-CViEcx>ol zAwzPs3^mdx9qzERJLCH#^=acYZvOC7w79uvx|1hki8Ys)G88u>VD10U3(+|oH++g4 zoPhfluWG=+PLaE;O0*QkeF=OlTwUS(9iMGb$F6Xv*{q%@7}(EGQpwQ0siIN9j@b*8 zew{T2=wcfX36Zr5a-l;}k!L2vn!q!E^TjE_==IgJtpQg1sF+B z1=Nc57RRFvYtSb3IIm8BNrGuz4?OKRB{*MygX2$p5*ipa6x+|||? z(8b#A_gV`yg0NVmH7u~R2%L7j6l7DuPS6B`%gJ&oI8MA>PCzU_=UQVH*3QgR6n&m5 zOpXgEwQ%e_kp;db7yt70m`50?9DV;M{|ZJ5_+sSBh>1FRax>m?-xNx;%dDKy>V3YQ zjc$V-jZEBsyWFnMF?Ijp7+qTE@57huYt`~{c0Fwd*gkw5m6qo`!bw`Oco@zTuX(mBl)FQ*kCgJ5g%>mW8o%`vS%2zMn@ z_Y%I|778`{K|>Uv#7wxvui$OEzg}*p%cCohg&R?hbX0+Z;lZl<{yO_nmU&@unPiHn z)}v(W_znY0{r}7-tDmQcTy4S#zjPviu@hYr>Ym~}vSD66t1x*Lwftarrb~WXC#{9PUs8L=~&NE&l1d zbd)WO57|5TPt=tYW5b8;U8c{jZc}$_FJ#XqodBOUo#wGn3zlYhW=SqCn-8Lo^tVSW z_{c{*ECX~uCiE9ss$P#QvAGuq&j-xAU#v6skSmW$+ayCwegQIcQ*1be0qx?1nJI;F9ChA5)+gD=W2e`GR#l!ezF`xy4%D- zY5A9eURv#=Yo+N4RQEANDshDhzp{$Hgs#b@RF)jZg@vMf9fz(&-L?BJ#n9=g z$wQ{bjVQx0h-Ge#Puk}ShX>MM3bR&du>!uU98^hXJKb*Tee$YGYzuGVEy43!yL!LN z8|y*wt@7>@8e~~l&#U#?H?l47QYX8f?4Cyy;K6^9c*HIgk!vw85tcyK)N7o~MrpJU zOyknnD{BT^v7nvDK}1w*=E=v~M^d41xz<~SU@sx^Z& zeHLS}hjHA2Fn}1W#6{1W4ipSw`UX3CILvFo4juIo);|s+jc_uGgB%c1pwR3m7Nu0r zP<8l+*B|<}9Fagp9iA$jyR+@Fkl>=f2r%}M5*D_6Yu`W4Utf`}EMv&p*VH8QnKX?){3tgCnA~djT-gkiFIdK7OeJW(r)yDFKL+aJt{uJ0=v$k zd=?8abQaFmR4KUT<2dJ{Os^yJdEHS_8*#s8I zEz-@8t(8?whZpylX$|dUE0(sY#daSCQSt=IZ~Io*_q;S%L_b9wDoKI0FX(g&=K2B8 z;ZI>nw`3VC1A!Oc??!6@{z29ALGs#WA8(!+E4c?bYU$x0LN$6^-8paDs;@gYrO>2y z?JzFbKCUn~#~hq#n1u!BsCW*`aRCz*pBiE?^r*T&xvGTW-kCVXT7Okx08P4Egl5a# zI*4r%(_dEmbb30$3|3F#?FKO{es7lLh4YvAg66XcLL5bZopIzTnAZU7=Ot%g<9D zgokeD%g2Wc8?fz2+o6`UgvM1w~Z{Ja_ zBh=rT8_GDuetZbD2O$P)s60>Q*%Wdy`zfW46Vv-$Hu+z^q`NCgO3E-slR%BMSUrik zD4YY!Ge*Ztwo()r0DeG$zkS;UGi;^|&($pN4+D$34R~7?)k@N2Cg~4efr>>+t6h(A zD6%+?SLna*g%c_p3N`(|zf~sXJVQZV~o*D}O zXI~)J*@V+Tt!~z_pSw$_d|b12eYgLwl{tdZC;L+%hd(1~G@QXMz;G66uo^yY>*-c& z2wLNVH6#v!A$&8qNez=_W^4aiM@#&kF8cgAfy@~?G=H|Ghj7CD7={pY=3iG^Yob7J zbh>t_KTxTC&VN5!kSgn{K$3;KtoMRhiLhPd134w?Qhe!+_ES_cLzJ(0>UoJ z@C4sv(r-hqbE!@d3C^6#DZjNOPD@bgbdHoPxUPD2$^)O{BH{x&%y$3v8Hwna?yTX~I^9o`AzbqO;hO{$0A%-8!jJWsrl%IYssiZqo zaT^4tX}Pj zrG#^|?0xQxsVOzSTm~8cevFkG0a%rJ;dj@XZ{Sbk@YDsfg{Q*?Z_SY-wQIxen~<*b z#tw3j++PpZ?tv9CBPS~`Sce|*`L^xt3wth@+#flh%0uo3&Y$l=Lmr>W%0lK zPuESAnIYA&SayMYIK3{5a3R~XF(VJ;HiRab0Y$>jE^4qi8yFHbK(I;5K}3b|;zpb3Pr0u9^5s3Jg+OyZ4hcvf!{c5hRHx3)g3^S?sgvhSWKJ zOb-Of7=q^^4a-6rYfrlNaKI>Rg!3Z)C1trxshskI0;W2#e<5t*i)vHL=)A|IA zxZ^4=TfiS#AQ1?dmQR*R_^%L*>pI1=>5U-bSy6rjt;zRrj@xqOax6{3KKkXtzC8Gu zn?T&$)!cN*_ekff<7+AHGWVMAHkEap<8UdIR05X>YG7%sm zhLxW(fL5l+7M4aFVekNfHOn1h+4ZBY&yU?kO*=IzGw7C4y~h}(2r*?ti^7ncSH#3Z ztKMHWhX8sr6Q`cbo4<14M%3+p9pqwv%y+|w!PUOx`ag7o0sB}2|ASS*C6<~Ge=1s@ z&a8aYjpZO(hqNN&6Z+6Tt3;$DF(j)Sw(aFw zxazkcqsN@Bv)^)m)}!sjMvV)Nrz;ORg%|C5C@1r^{_8qUQg>1mkBr5H+3FqLEh3yH3&wwjc>dbT{TYhr!!v9ow&4bV1J=(} zM4FvdoP-b1|HfW~OBdOdBD3#JU7k^#(Oa$blactw!3t)U%KvLJ0AktTUF?113&uj> zDVHN^SJOte?Cd3uAi|i<6O4KS#h!~s%|>AzV4) z&2%E2%_I(#AbVR+@xuB&g%a}l#bnLWvbVDDC0BhEI4SF2!p=Yv02%?<5&$yq3MmT! zMF&LRm%u3@W{R936F(A8Aou`SORR)Ucw#cG!M7`6Psj5BC+nvL^e|QCK2y*Hf)~EA zxzY_(KGKg?i^lRT3i|9iEp!+!Q_A|7$_#1B_uvGsAyWgDi=|Y(2nCEHIWKGr_!Sxb zK`h(n!0fLZ-#r)0sX^U{PqU9TrmqY1{6(&_N>}*~tJjkiBJi8M2u>aCM>hWkdZ0*w zOVDcv+xDAU zKvSixi+9BGoRV4Ee7LL!b((*H#uF0dcQ~Ch%lTNO0eC0HEOmPI>p?uGp|=Y1EejIQ z^^s=?LSeV?bLm|jc-m3t>i<}Qw5Hobi{>=NKa?AFix0qjXg}z zN=LEcTWStNdi^H##BU8D^FZwH_XcVK3|a!RG}3;*sU#37TZQ-@5Wa#-51ZAG{Tca; z%s*)jtsD8%zIG@*TOjruoCTQgz@}E^YavM}_Ld2{!su(G3bx8b^g}7zBQVTNC%JZr zJ`Ix4{WAl)q~%JnuFv_^)LWjU(` zD$YU-P{u>RS4OrIWrT7HgqMi|tE0K{tKf|R0*sOW`rR|9Y@oU~0;ts}56lM!Q_O53 zd3j!qy@64&YIDV~L?Cr>L@9hfjL`MW)3*VE#9%P8`6K3{VZOd3RJUO|9K`sU_bU|K z--Q@Q=J4X7_a}aTqk2~MQ-L_XtPex?2aRr4B`qD*y;uYNrFiDroWHvG39?`^q<9R) znhX5WL!FvD{?Yy6@jhz%$nTgK6~Hi9e~-WX+H}>{*LMW0L6%ChQFeluNQ;Z-`D;&> z%0;bZf6Lu6jS3V6|99b3{*7wUw>H1r+6p-Jab%uFRgHz($Ysjlz_3?(F~cpqwLCLz z!-X6+mx`mkBmh&kwD9W*WlRB7+ zic@;wQ!hmQ27ByV5_V|f{WV1y+U#2iY!Wivke7A);&57KS=bxQ|IouSMb7!k8c5q) zui9`Ia>2u%P-PQ_w$bs@SGKxX=naLTGp28*%=kM7U79!3D#2=buY2_{qlQ7^Q& zq5F!+r59l1RHNKYZll(u%prgnpQO_Kno)A(K+v9$+pIV)u<2cUk-TmPYf zpW8>8c`leeOA3+e3_%|nT|vsu^KHuwA8DvC2&6?+4RG^G6x1XzqSB*b@9?9+KL`2x ziUL~ydr#RWD1|gAVpjbMtrJ{AX;Uf=VB83CPiTR4be@r_-Q@AIBa-)Z(Mr0RA4Vmy zdTq}QYmgP4tx>ujtZY{SEicJga#G~mz4{W?PUW%n?6QN`@Ct?Y!>k0fT!^&ek!jW5}7C5j+15ml$SgN&Fhl4W>kHd>NRsQrdi>zt_$2SW=GOGqD3LymtMBkUd9@1rs zK@xNz=m6j+{q*Om_v~T)c`-;A`Gixm5rLl!|7?AwCBZMv@r#Y{QP!!IGi{tMq0@L- z{0Ll115>Ip9dVb)cV$vgWazXIwjDE3XiGO$+2l(YexW>!p|64Al=CM0cWN^r8Gz$M z08A5T(x-eXa)8yT>t<096!qejW1E=00<}7P1n>_P$>IA1V~*^Vrlj<}c3M13ZLe%! zpcZ!NFK;28!lh1HEnc%o&Y6>VTi%3LyrFiizNRNQSH)5Ez}#h&d|5UYf&q-Wn$R9m zdPZCI%e-i0LTMp1@57?in(9sfVDGTs!xMi%gR_dLvRHIgJ--zXPT=^m_h9=JnQD!s zgm5D(ST;AR%}>$0c1HFO6w*cg0=IBJew=CXcR2Kjoy>TW!le5*LDT|9Qd}*7wpzj( zNU=+#KeaZQUw^dV&AgJCGgeHFC#;*D=gkLL38F{JHl`B%B2`mifj`Qg$vsik`QUZiWC3w~vU&RCIL? zy+t4h*+`g%6eWCZ4kJ=4DE888GtwLA^w~@l{7LpfA&NM}vjmF4SS_rr?&R-7dN2SQ>4N~n?3 zeTiZ{QU~m)ueZq!C^Us{Ji=qo@)}e%7ENiQ6ja!Ugu;QVw*lD40d0@E{L!CFgD2YBVV6c ztM|JS=wl5_v9m`Ch5mzxOL8T*=K=D89qf=_3F1Gf4GwDRJJD?jON zsfS8{!T$Vp;g$1Ke~PT@2J^ZAEDK@r6U-mv1IBbTWmS1BMle|7_DFPFQ|36I1C*P? zw|VuF+p4Uu&CFlw?*{YD^$xLb+9HoTiS0a^m4cheM9+^WBs*wV6!w5DU_A`bK_3=9 z6pa(hsubksf*nN!5Ut~grdEYI^_(0QcUdu%kqMBvXa)~t=rjFq-I1r>DMs3=cNTh)jbD0GcGsq=? z5&#+j$PxfDhyyYeBnWN^pE7_(V~rMo5d0zV0G!F@0JKylzFSV}8iKM>w=s#yR!Ii@ zARDikyJt?E*ZaUDT)IeZqpusq<^hgx@AiDEirsM|ks7P9xXOa8CubA1!<%X{E_PTu zc9cuAR~u?dUp?tQ)O1D?iTiR$+kvnscw&oUM6uCq|4|zG`(dp{r0u= zvL+B49>^IziR^@06~s9aJu>0H6VlM)AX2h9iokdvZV(a-HG#6`^EkTm4Fzig+6~~O z$X;=TLL5NpA&$Gz8Y>bE=hP@z31G00-$)d=ew#!W@+jZaf=*hl;a{k#!pjoPd#vnS zUdZGTr(XfGIhfc$YllqUEMTNSS3$)f8G16jm9JLOO}?JdnA8#g8Ud&h05XOMDGVSI zvjX3jzz;EIih}{>2*bcY@BsP8?-3CHfRln#tt$0jB*YljB>{pItzIp zaSBy^J4<1;L^jC|fCgc{wmFb_0u#rj>05R+GPI{0LP!lU1!tV`QcqT?7%eTXf!p2n zt5)Q@qRs?6w*+g#4^5k9kki~En&%u&H!}YNltpqW7F8kJfv%DY7~0T(Dl8U6SR;_n zVzILtM`BXT{boCxhGvKO&rCZq|C0uDA9fL&uNcnom1`j22;`X?TKmv{9=!zh5~kTS zJZNNaph%JgbJl^+<6VpO>TV(b;JfYgz1#K4zut#0V_=b$cONa@?lU&#wJecLHpP2y zCMS033Iuj<4+cw?Q7e4{7!IPdHcin82cKD*+bsvkq=H85GmrYe6sRu5v=`Qw!AGF@ z_h(mHR42w(Qk1!gN+2=P9{^jDF8;xoo~(ihH-9oQ1jL?#CHOisY2Z5NnI;eW7eueK zn?S(1IVruqXhX=YEo@!m7A|H1dV?TpK%<}KVHXYmH|U0I*lHHXj6|_xw0$&0IKCKi zHbxD%OR#Mp80IY2SVc8gL|GUhYv)*xWPF0@U_(;ecwP_#f;Fg6ihH{!VMF<3FP_lU zV0D>j-eZ6-IvtZx0uuhRaV{g+UAP5{KF6BEQ)9z@`PErpde2(k7B9;Ms$cJ%|`FoTB?9bS1iN?c$VZYR#xMwN56k-@NrW}WVqae{ z`38{+iI*`c{5w_}j#3FGmd33w|4ilV8j}L2bvi*HKdAO~Fxrrkp88NYZ*yKqHo^vl zJ)-=g7)11}#F(>h%=v}4;|&5GP&Tt+4FqWglG3_=OQ3?p_;37|GO8+cAf{sQ`j!9% zQl=mLGDs0Sv>`6FXVnet4Q&1YU}q8yzv)1%%Rj2qnWzGeU18cS z+_hktgvFVsek?@6cd@jH-;&VP;|vLhUv;xn!h^&AS&ChcvWY_LO%sNxB$Nj`;RE$| z?Kt~_s{shkiP0oZVmK(0iC1RR1%{}rk=jE-8i=FxQ~-Qrg`K&9A>9o{<<C+RdU0qA2#Fhzu*mDZY`N`{Vcd|2!Rj%6U zu+d+4;av^ZK3+wPIMH(d3eRA|?k0@lFtv=vAj)yTl{)IoJnH14vAiKBo?ieF%YM&J zZkgOv$6g{8Ut!VsA26VMWwNd-HfG#0=B`C+YAYV%%9&TNfFH1k=JIAm>DYX#k;MW@ zBKdM%4S^X{$q-k}E3|h8T}AHO+O2u1_`#Xp*K>hcEDnI9J9Lz=W=CZP{FW7P$%gNUxwd?~-N`%;)9&zUQvQjBU! z@qg|{c7_$)ps9^|;OiLN!QVgW2yka3Rd-gcgch_~ePyO_mT87bif?mQ@3eeedN(HO zbhBq^T~}=f>Px)|q@sT?VZ}3Vy-H-Muv!(~N*d z%|`Y6K=tck)TbnPPBW~r4I*lS-^moqeuw6hIYOQQ+n|D77Wq$nA7x8CEpNTSv8lyzwJ9d_BU(+H^s4^Rq`=5=G zFCl@v@Ing@i&GM?7s>9R^n^~eEf@Z&>5}nj^#5XUgecbI8`$*O5_ua-dkA%SHH`{Z z=1m@|Y!tAjcCiTO;D|24oj9fD6Gh^)SzioHYq{Huwq{7YCKinD^u)_TCMDC!2_#wk zQf5M&gB8J2V;F|4womSB+`RMaH7mRKKR3osJVVH7d^JP(1A4_A>;ROiaTa z8Ob3ZHOTQ-Tt z+Eid|fLu^Fp}PA0cLTLsvwAdf zkFGCXrAP|Va}P$FvA7R7>N7$^UzlA`FocFxA{c^{RfMXQ zynQRbBFCOY^p%CRm4}^}?1h|w=!4u<uR(ULF`yOn6MBrb)?kVpC5T1od2vb46c)krsS2h9oP+39 zgQ|tF4uKoqmLrZ|KMj1*0f|a+VYt;*8yY3x$xm`lIqdU+jB^}qCYJR;GxV*!o@RKl z0$pB#3$mUM-e(EDd~dBqG9t$rE6rx!{M%#Quk>-ev?v$fV31He50^h@JlB$=_z869 z6Xf(*?JKsWmr=kDnVfTV;1SjDH%$Pj{uZ~~TPSUsXsn_%-!m@UaZFBV7fc#{Fkz|R zDCXftn54KiVlhGq0{D0uGetI=T8)EN{gQZ%O^OLF0H-rctE=OF* z^6unzkZ)MkdH6Ed>|>De=P{Vc?&DdoFL7VF#FihZpYiMmBUY~LB5NE`rL#fF)aJ3{ zszn^)2m1({&91K?gip)P^=d^p03MIgsN;=mNI@d`aP)f#%t8sM`)T|N6`?qDa$K&H zFT_n2x<#AY?t`k$dm9idjZr&>Kk~`f1CAZuw4T5CX_l=bDKO66LGRy{ zTNuTD1XrWjT5a_$b%WHu5duK9djI#J8}OqO4o*T6z&C7EmA0?1TY`_6WG<*0_jSHA z>{Z#CkQvX>6k>x?H;Ds%sQX-BuMd-puif?BB|P zJi_4;PcGukP6!@L*Xo@F|6;7QwWwADiaPyAgV_leq?6VHo@|l`2>4wKWK%I3jrSop z1$W?aOtcjov!-IBRFU?PozPR*rjA@eh?Dt!d)wq!5{;*45xm$JD8FD|TgfEUxsR5aR^qr|QcmsrPn zOzEWbB0Hr28Uv&b=kG!K@}3(9@=SYoJB}NDe+Dt!l;_-SSnV(0avi}siRp%^=+y@Hl@>vt#WmjK;!L6BvOw=rS@U{YK`v~gwyH8eA)ri^sNpA ze>}EhUSMX^tt}oT`Xe3J6*6zjVUib5;J4b}1s3Z!cXJT`5XSSAoy`)nvwj<`K*D-E zcn@W=%LE7fJ}b4C(7DwA0vo7;Rg4{04N)BANSU35+|y&EJK)Wh$~(%?u>Rir0(H5I z3&c3QS&zQ0=xk9VkZw}QIip70>?MCe5dem4dvex|NMWDplbb#)+lMf$a*_xZ*UG26 zG*pCA%0Y;cQ1-2mJuDf$^eQ*WMS&bC9GjnZa`X5hFX~4=jx}&DW1x9dZyp&} zVo0!N+{b`gJ{FE_uOpgIFPf;lw5-Kh-lrU3>@f^*c<2QWq`gdDJ5515mQ^u&=oQVr zd7e@AM!ime57&k-`LzvS=KP{SR0q7bYorod7Xc@Vf`7!=6}-wU1CMg^wNvhutuSQa z{30B~aI3YoA;GDJX3+c|GkEqC{nB@* zS>)@#(dFdO`n3HSqpehdX-%ZZ71U6pa?d#;OHz>^I}P{Fy7&^#c`+wy_r8;k+J&9z zU5l_im_7CtEZa>lyfL}U-3kX4z)+05`5BD)T}q(xZco)n}5={A%m$`IdKkgEwlXi=dScZPtViYcb9IE~b}v zrMFMp?v|_lZffqEIA zVlt630wLV#nW^z=nMwzx63~5w-p64UN-gO?MU2isR)M1}eUsao*vSw1lXp#p0@310 z2Gyd9HbWIR!Bpj_s~GEjAhPH{4q4TbJjFVO<^^jM%8LtHC)%pjJiZg5viEk<{MWT5 zkZ*#AT`i3bnp@s1aMQ%jq5UK{q$>W%80Tm8Gkhw~tmizjfPf37Sw^_mco!e z{DzG5zTMhbNt6wH+GrZ+UE;d5mM=T!6mu#PMCMbc}B$6+Yzfj z2X_{aR)`W|SDgivY)4Uam)Pp}pv#E2vt`-vg8yJz9xDiaw?2R56g*|# zw#qU$^8A?#);}z(_s2a}e@YZ+K&~UJOl_)z40&VoSMh!hFbOcj+Rg3wNQs9V2;&54 z6`y`Iy%c9}LY?%e?j&{z5H=BcJ&Yy*7n>kFn*80p*( zkA-%xUl&m;TP=idGSusc4<3+6GXcPy>r-m?HR)j6~O}I8px{L#XQoS#$;48TRshH~PCRv6umIM(YH-)AEFcB$OY9=jPR>*J*SwYF%Pt zVbe!DibG7{Yc2%FO~8=t9=+w->wre9i1kGYeL=+Gy55)L4Uz1dW=`& zq5qmeopsO*HHP>2GyxoHXN*WNkYEH(>lHL4`xHn{KA=e{d?Q-xNf%#@aR9XM{}w;C z+bImJYkh=x4z|gM=I7Mqz`v2##y{X!ag0`xQgA8ihN6wb)nK>uN-bX0M;zb+_kecW z;bFto2`J#=m4Nw0cz%7k4&vZM+NLwB%r8=ax%kd^@Smu@~B*^c06X0PHhwFEM`QI=y$!p6q zrIvHlzFv61n7Uc*byR78`MYp%Lk6W0wE(kN7Vkt3sapGsTuuk>ch$<_I0X#diAa2Z z%eJ|j&NUUrh)c^#WT&oZs^i|8He;hrXD2Bms_p~6nvesDZAx$M(xFBS(-i&cv z?iJf7L#_Rn#Ts6m<3@Wr`;z; zH-ED*jnI}Y%v~7O+%WMEi7yArHT)LM$*cajgSvO?2~T>lfNaQgkBhk6awvE(gVBgjABr?zCe!lHGM5dxbkUFAG z#tVTG$EUcJl2>_ME>yWt1wy5mWS#(l+4^FzicRI^;d~0;e8fO3iGGZ>KgWjDk%kT~ zhV{FJ^4bz_fWIveSECBDqoLYe^=>^W8Y;K{msw_V#&0OluXUG2eh@dm=wvcfj0si! z%W(0vKbe7uBJPS6%ZRO8IKd)Q?ld5Ps#y)!>Rmd~%%tcy>+5FJ9+%Uderq1l|L(FY ze(ZDIn!6MLry+ddZ^ybiDiRXTj88c8he4gt^}pJdB<7=^^ZU13w$xCTFHVzg25+ ztPfmiK<*~O-;|!lMIUW|$~A>>DPB1>(L(3_)Vd71?rQYyMOeD$W(ZC&Np>Ty?wm#h zDB=oXft?Z?tHf!x9G=iD6sO` z!ZtZ{@kNF+&GZ1usDqsK7Lr7IVjOAO_fJmrc(+fExVoIA3_!~#5+OMWonroR^SE>Q z>_w6QL#3%E89@UI^27S0?)TuI=0Xj&fK#k7eTY-9Pd~)GTI6g|{%irZQ1}231D*_t zQ+pWK@8Y~ZM>N|M$&mvt@$vUG&d`nGacddtTkntSF!=VMQ9!83c}aG*&@MlL9hwu3 zo~k!U&e7bHdUad*i!F|$T=O*!QDs%@Eply{FH`&2kIJf7C!7y@Ev#a7lW{*twh?^h z^>Au&b~u^p{~Gq$#vhhm&g(p@SBQ#!_auCjg@|&DLvlqAQR&3*3k{iIw^8cU*c+wJ z_(7a1xbC0le7cr5A5${u?FTiNz2$~AK6q8$D9aq_G(J(=^H99B%?XO^Q)|6umU?(!~Lp6eIF1x*~Y2WLcT5R6^)-2a}z z{g)e>U=8*h<9PsX1V;)zx2021jIlH;+c4B51_M3Z7!(=V@`};ZCjfpr1%Ibp|2^-R z7DYU*Ry>dy)q}E7YBBi1|M?-0N|%|@lpq<#?=8`2%$H(^5b_E9Rnw^)d(23HQ(n)k zJwuwcKFzanp!sdjVs+s9MpTI(-bu!@Q!LtxV?fIpcFLIJJhEAD!=~8f5_R4W#4*Bf zIrre^BI7%iI5bnKDx-19*1A<5B!_1a=<;%k!cz5xG_zFRBv7z9_bg1t1Q3-0*e>kk z@GeIK`)OL=!iepcxvoN zpwB*PJu1i-SS>Z}w+++}pcI`kbLR^1M%&Xg+N!%6%^&kbax9e+GO(p<2lY!5N;Ki+fkpHe zt!wQDclt#6Lp$rL1ob|leHXwTz~Z(jkgMh7w0 zBnm@z4pf2WoV);&+a~nb_@Ul`*NlQNJILmk6$F|P7EqTF(8e9BCDb$XHfuuueXQF7 zduHv6nkr`2-7`Wb%U`O^P!mdN^}2OvECBk%=A}{|X!`af(BkzlsUr@6fr! zNNu&&?m|fE{#V%x`9+9f&`4_HWxxk?XEfYazj4lOazrN!PzzUM@OsJgK;Lnp@Cm6m zBX%8WXO;)PzV5SE(6~Euv@uLv6`Cm!hw3ZQp$?nCj1NThsBU~G|O`z+9J0EJ~9)nX+^oK=_ zU&N-XhRdt7oy|gih+Uet;c=8ZOQ$NG<3q}Wi61WRdFyk@Le%|u##^h1os3S!U7ThL zomtM#-F7JdDZM*Z*g%r)n%q$iYbn4gxMMQ(j)`1cm{$aRy3g4%@dJk(9xS-Tw8}I@-R$1cqtlO zP=X!g@JeLYzvy;ii=wVCD{N}FW3B)Cl7jH zu%3JQdZf>ZVN_vq)oHgGv?88x(W6y-k{V!6$g+E-5n%y_7Yoieu^j8`qPt+O$qDxp ze5^<+9<@Dd?6RnE*h^Z(qn8JdXuU&%t?~%PaRx2KvQD$^0nZcv^ktP$My=q|dlsEq zG(hyxdNYV^YMzL^fS{aAk{65WDKV*vOziSH;I9*PNbGx>yd`M#g(#Q@385BbzD77U z_oh4<^eBh=KoiDdc;zm&I^#K{<30Mm=&=^#wBRDiY-z7gz)eR7RszWh8h*+OozjLCJKWR! zQjm=iDxF8_o>dfA*gHPBNQU%i)?re7X~*zgJjNtTTuf(CKMC2B%<@aGA}hp-F<1o$ zo4kiU5F5^*X9EXp=kGS|vyGw2Q<++9HS*|;1mA#0+hoF1cbmT@05}xWiVT1;A<%LZ zfU6TnMHO{|WVJCJ^0s(Ln(Pp$#Rj2&`nNt(0kd?U$XuOFK z?Y}lp38O;ymFRR*xFOjP(V%`N4TAN)H+4{5o>!DpCzgMw!LkOOjXye^{<0%#5|~*- zfK6*5~AtN-bj52BQZ8}$1IQa&jA}svnF7|I$>v`5bk_$xd zPgLG7qL|(2gT14Y)NFLF|95xB`5@y@<{&U~SkOj#$j|qwhJC{;ok5~bRT^K`)J>l7 zD9|3esZj*`!0z=ndowF0QJ&k@HSy04v`PKlhX*|8L>K5yZ<%$_kQo)Y69Cpm!8(J? zZ?Mrs6`s-Bv*DveRz7KSWaQ+gY(!@gzS+yhK1%U!cx6W)*0>acOin86b>AsoiN;## z#Br3MsHR7;D#r#1AQHyAvt9CP_MfBKgbr~K2Wm!Dbi3sN1cozwkm&bWBEJrtUPNhr z%V~X4wYx;Cqwa5Om})VF&nezCDdx{*?ICBxvw6YDE}0&rJU&-lT>BhWi9YK*Am3WO zt!`Xmh#QnCpp^Sc{H&onF|g;KABvF}hQ`VbbLQk4wKdirc~Rc!bxsr@ zU{{0R5aFeuA^=H!op&WELEFQ^y(ngY-ze!sY1EBI!c&Zj1zx4dbSLB?x2tNHa?9Xj zv7~xsH$(3wbTk>nLPu_bGF|ZW#e}ROl)?S?E%J+KKcMJ~B$oA-nhXq;CFk-!ln+Mz zf}4wgWK3xD3rPNtd3Av@QRNNkZv;whtE>dglbqkYQ5zT~Qb?s1-*KFw?C&3v7d`Q( zoUU?3NO`VK6Rn|*kSA|`-@q4Ei5hU(ZyD5UNcDv;UG@;L{<^8M*B; zK3tbj=3_8mxMhL1IXq-Dl`%qWVcB!>GmK-AW6DDJ1pF$ddA7n0xMi_3?h(-aLM<6X zLpRQ42VOW0V1_NnVw9RmWDs6b?LVMSapu#+2@CrF&Q`4R)-r4A$CsJ8q$G-XU42&- zMoo-gH0T2;D|%fZRg54K>z=Xz^l8}*x0B{5+d9IdwoFEIHVu^FSB`GxnY0FiBbS72 zYtE>hMy{bK+9NhFh%(!Q&TL9y9qWzW?Qp;Wyozh3hql#Ox3?d@RVpf_?ymsDw znW+!)!eVf=iF|AEFKK~L#uo|xB@N+<3ymKHg-vDXTM>&iQ^LC}_{FyvXp4(5_E;OaTYS5>F9$mh6Z(R(Pt4!y< z)I-QBLK8|Vs>*-M`bSW_`3QAC2rlWUH@mpnWZF%+!hM)j^--uo zp9;>q+@`(g8df(v^w82ZRVn{%TU$RWrwP>OOUo42&?R5LyQ1(RuZzBIbkRf!dSt{& z%NK<}_gSysbY3lH$WB+dlD=-gAxv{w#8*sJKt#7bp^@LQ%`V`NXVyJ19HU)1aQiFa zLB|;DsDquy!oYq;8EK#E@J)%vu6lf;ylDS$c2gH{;G}LaPYpgfmN=Fc{w&m=4cPq- zjXP=A0Ww$vk99!QQ|+Z-qI3$_m_iCtQZ-!zDQ?@-vK%3UhLHNPj9E&AP}f>oYIW7k zdfqex#_IHtQA>O9)nevkb{m_=#AwW-CfArl?TdO|e;-bfT^g+#Bx+JX?u;hzRTmmb zxQ)|crG$jxz1vLZoYKP3)~(ScacEiEnSKBq2MUUK7cXHyJA06hYWx`EMr~=^B)l4g zb5EY+d)^~)jo-^e;mK>=yy>Gb^vspl%kNFj1VYPV_ndqRHr)BU4D&|FG`IeqiK~-1 zL#E^?a3WqR=oT~d^&HZz9KYX+=^xsZ~ee4q%s#jLId1=)1dEg~SGV+C{ zo4UT7(O^wUc+U1!5>V5jJkpij3Y7R8%Zni&+(%~;I+3X_%Vd94ky~0wZEs_f^DE>D!-wNv;ftlz zIjg$BGQ3F;U`~vCC^g9L1MwaH`Huvy0y z-=tc_7s>4GmH5>tzXnIMf$S#|Zav4ie2a**E^U{F3CbNM!=EKQi)}7=%dS;+_=q6n z7@IDmtflRI=#@rf3o-{?0A#ozH8U+@DgG60 z%;c0GHRb_57|*0SC!nQAUh-tiKSI0YRfJn= zL!OyX1^e8_FTJtLcD=+2F9NfB8t6KP>*gx1;;|d>}~CDy1h+8keG#6Gd|; z4q_CZHt;%gfEFR3gl>)%rw?CY>VbNI(Sy20k$8q5Ax8IAh2~nbe{>kDZf`n@17_Vz zJn4RL_CVS)gDk&%YC%oUI+0usrie#%u1z{mrf?K!QlD-ySzCPJv`P%z2q4|XKh-w> zd==Q6p^oSF^I_qswn&XYs*ynvLOGKENvc9vtJ^n2%|ErH$+`G4qWuzimO z1VEJ6xZakJLI>E)y*zRe7mNSiS73NY= zo(|GVcu?ePj*9O@Nwjt!Y1(AzN-#*uMid#T`oOPsV+Z}SXpvseJZb!MH}lr{x_aS=#mRLi&{+3IT^ZrG;o6XZ*=RV?wN` z7DS5yX|6+uCS|L9n7~?R27SPW)sR8!8@hUyL$K@AvW1P^{26fo7y=>ia<^Jn=-b!q zTgN&&ez+A^e$JGAoBa+U`w3W7C$(~nVV%`8WicXJY>CKzEFurS2sf)Bk4iVkGGHh{ z(aQ7VtUHOG=71LvLhf?yVW11mDETKar^u)pK1o=UJ10YRuITp&*cZRNmhBqON{ymy z%xgr;eYVdHJ#@y!ylR-!XrMr2TL7Ye8QZc_&+L7fkQe!HE+FfGQ0?y#q-&7bUdO8; zn}pC+&mdA0KCfxpgRA(_0|jNp)Ox(f9C+_m#tuI$3aUOc-M4Bh8hn-@U7c_`OGTe+ z3VVsW3l~|T(|tE~-T_~jklrqT_P1F@3rJIW+cdtMSF(Nt%9ukTJ`K8FnZ*0*|gKVA9Kul)2Qkg-U%M|jdbbw5qnnvPS{0NJdE~s z6t3hccli3#$gu(9+Zo7E@8PR*`BGEoUObI$7SHd~@iM^<(-Dk6ot;sR*}r5}DUY96 zBCB^0qrcZ}i*{F%xh{XrA$I?f)Al_q?&Qc)$`+2@|s^mLWQVkHo&ZDk1&k>ftbhWoqg0Th(77R4YoZj zm@+o&kLoYxq`u#r^}8#)7hXU*Q%Ic`4%#yR?B_pPEKMYoV9CcDmD$p` zad~ROp{fi*4ABSIUEqd5)6yX~WEiK?nUni<+Si zP5U8%uU5N=*G+V12PGYux%!Z^JIF zW=Sqoi`kn#8S6~b)qX}FJn!xpuw4FWHcnUlV5~+4rRd|voyt|9^S<64#9{IeyUg`c zYI_+79+rP%AKC-d%b=d_gsI+B$ z5rEJD`IJYUHDTM~O7k>ze``1Poe)-PTG>V>F?)X4@=lMD$Vf~dS@2|DqlVNbin=Ls zrRyA7EnUVG=pL)+&3rU5$9$)x%Ei2znb5R3+pk9pDduEM$bSXJANR`vo~3Le z%t0oFHi5-Gp1iqVPt+ozgQ1{}_?A0}~HxA%^C9Y(?vl$jlrcCHdHByWny)9w2CP(I-%USIx*M^PE~8yQ~3X; zZtWN1F|qGm+Hu$6Oek#v8(R&^nj@GS=C52e5P4!gVKnHHa@VD;&Xi@+h=W05w_OR~ zkQfXzFG0P8ZydbidQZEn1knZIV6+uIL65{vMobHVaK!+gK(SLRx%BTL5wd3m7V>oj{yBT z#4Ykt zO-Re$iOeVVA#tesS+2o0Ry=CGA^CQL8vhAml!)j!lY-{*t+(l-6`f@>AE#gbjbLy9 zi|@h8ra*T1*D}#L7&F5#Bm%3I%@$(Fq~&Z)W|Yaq*~K{Yx(`IVa>-@_mSHrTo?mt7 z@2dYjA@eAeo<;a@gecaRYPl>rtB72^GV(g$)f5hdW8CH!SQIZB*HhHqA1^}$CbFf~ zb;x4Sk!7-7o|I^)IjI~&L&7!xhiYw`{GnHgt_@@!ElV`Wu6{1x6yK&E_XKc)u0iuA zoSJG&Wfbu9nv;XopGP^=hQkZEKVm;g_cCVTXQ9^f?8gmwGpdYkN5r6w5|7{mPHDFkq44Qk@PRi zZtIkV@f^XPGN}YI8v+C)ryn&yav{bRFaRC^`INjzo(;(Ty(?}5C+St#_ZBN+qatV( z_Z1A*G7B^5bS-#gjOTVjA`h#7`5{Q~{BbLgJMYmFrsU^Ta(uMs(j!cgWcw_ssI-(l z`urQ=<8vs2hs5)wDZvOxR-OOtiRuC(8C78od>%30#Vml2#S@6$^7%hT61B4+ZsdQj zKaGr3XK%SM(FoORkhi+g-gCAdPb4=Bx>Q)hQFA5^1U?SLe{_E(IoJ@m4#3n?i6wEI zLQt_p09-lcQ)xXlI)juh-nzAJfxdCsc~kN&PK2rtJwfA|Lw?lSREe>*B2M@PqnxId zp-`0E>9>V1)DSvCiC^LUleq!xNk(vw^>MOTYCzt5tF%3gq~r<*8#6!LP1o>mEnxRA zXQOf~V9I`h{xjSVgr%#&1s1IE&fr-hQ5 zT_hkqskGj^_xNuiR6(`A8kH$N)K>hVDzd`dZ4WZ3N)|BTbyGfC`TFjzL3OrnK!iiJyVJWWTXHNeqegwnBqLg+L%$~sUJ|R9*>B_X z-~2PJl^IKyO8eIRxYoO5$SR7+Qe&-g&;fv9M0l?N&#liRlw3jy=O>h@5&#+j*b)FT znFKN&Ad3?RA2mQOA;uQApa22j0Qr>0-M+^(Lbm`zSNY-+v;BN>-5cI-@CS zuYOrR>3Kk(MM2>j;x)2=L3imb7GmT*oEc{n(#O2a%cv)r9R3esd3IfKFUdDR@Lf8k z71}jYStKk{4y{$y@Y75HNzaM!d*1D-H7@4rF(S*%u>guS@17++N>X;wr|NNxZd>4sJH z!NU+L8S=knPj`(eMqho?#V*$0G@#vYw z4-J~K;gBHe`>2KectmNd`qmbw@p|ldB^yOncgnhagKs4+MtgLV22Q*GWX2?n^Q4+4 zFeO}*o^_r0)iK>l01Pc_E1ZB502%?95&$yk3@H%-0ab~QnIKsk6^ob;sjQPgK0O{4Uyd)gryw zsE*-_k1i&9GOd6UH?+{IGXJZosX*8`vNEzgVno=~z0nO;fqFr6EJgz6*3k8v-VI+* z>}}yD*FM7-B_$qcGU15Z44LQd-ccVr)I18&ev@=E_}%WGt-;!yiaY#sbA=>awjlVo z+wSIS`g&)qAK2rh0-SGmP0_UZv#1il*~C7Fw3-D?;L3d9PoUCHdoU}Gi3A)Y=gE%U zfprRWdt<+lZhZ{k?(+!Eb_`#46}qNW*XTa~@&&~%m42FpubK11mCNIthNH}&W)UWjqJyi7cy7`=gYP+5Dw>-6Iv^X;*2! z&=T-Ew3KIURnJxl2I@U(bNx_Oh>WNUKh7X!sGLXtRoN&5WeQH=jjKB-M_F@@()oWu zm|4?~G6X9q!`sEW=o_$2$iVC~`%;`hEm>++Z5Fy(X#HK8{EfF@IB)|mL|twNqVS#v zdrK|pAv`ts8Dklt0Xtg{udRwt!2U=?7{kv(tJU*@6gqfcw|4| z&8-`$sQY~HbDoZz2Qza{ANpbQLu0J&_~>I?SUxSAt=&mEen!ajIGbQ+(UC1!!SJ^) zsN%Qhjm-5SyphngPc{+0jO?1;OQD}ZXp46nVm^p8VQ#cyyyO_v3CcPeA1rT2UN#n1 zM8cDr5M}?6{^43>Poa1ZN7de3>tqKZ6N<|Pu#ASjT9jIskIm(DtJVPZ?aQojTh(brnt0!EFh> z%_r@VNC$t!F<3bgwV85TD*p8@vRk4#Ms`j_&Z0U8kPE~=w*HlW7ee8)RJU~Hs#)+D znh^cPIS|VU+X#fVAE2?Kn4BwG zHXh?&;2O26LTn-s6vO&Xu?2I3czc)&!QK6~vqA2Yo$=Ukx*BhoAA9!p93oz6Wwy1f zXdBp%k>20wVz?SFlC}a_O@U;LiJmn2lf{L@OX@wpW(8IN5w<|sgt|7UR#&RfGa;8_ zK1}X%49+St7mzBZZAp`L7F{ii^$-3`i^D`3gFy1-846iB2Fywx`Rut{(Po>}X-Nnp zj4pIK^-voJ{tLd>Wgp@kjJ~?m;~-{OCr{_G_W| z1O*dG3`gcN%%7H0Y)L$+>3}pTaz})TXNM&e~FbMVH{gHAZ?UfVtQ*s*GLylH_| z5GmNjsii~9=D0zgR8yX$_v|1OY@{6C-^>@dU+1 z4EUt_A(zU8%opITz2<#$gNNvSE$utogQTO$rpy&dZ*rxR77FP!uw;Zc z_%d41y*LT59&KbKW~T2N=q$t&_&}~(&U!*8tLaz4txcPwDC13)gujrzfOSz>5umV( zfJF)&g%VMlWva4;75V7xExbiVFDf zVOPfe*fNZIkCU*vA}x4&Rb<4|q%g!V<09mcuu%9(zmt3++)7f^l28kB->63)Kio_l z22JQ_ni2I7DBjr!X_({1uJcD9v_0V#LCj}vJEnqVEDWld{*%5D?{RcnW)T{B{y zZ3M^LN2rO-T|>_`)BQxTZkTEmWSR#BT%mHMUMiXfCzIg3@_)8n*x={*iRu@I{3T`< z0K$**qnE7V-1HZ7k{q4h8I$q5u7qHxWDB%L*%rS+E6bS!U}pkZ?cn5l^2z(gwEV1~N^IBQWVJ0;a*>%Oho&6XRlbM;;vgc{^x%E=3$41NMFhLsQ0f=&5_@+xjUPbS%%~xRnsC z!ploS@od5C_RBpijZO`QchF5)=vWZTrYYh&O2fxt!SNReU-W2q158o%)G%oU}$%c9Q5Q<$6Ui7;QG ztN04Fn*Y?ZZCjBQ7?kqbYZo@)H$v|i^^TI>xosB`;K&^@bY|``uWVit0{UE0N}+0T zv2jL-O_Wq4N$3~=QH_gN<>7;XpsFFUhTxX)#NU-2e> zOASqCtEX~dbo@P8o}VC-N6;RoX7&M}P9W0ToMUwj1=CM}=mw#RvL zELbq_*=8B zd&~8;Ca$3ot1sCgr#91+pir~X}8_aNh`m^qyd9%YL9A*L2HKu`r4#zN(rw{{6nRf##d+L$6jo_cG8+58)&6pF_Pjcghu0( zht-wNEquMXWj^UB!%RWz0MJ<_y=d>5_(E@$Wtq`iW5tXfS~|Jg)xfuwNu*T0*gzQ? zGT=C~u0;#?;^Ii$FJqKZ{Ue4an2{OB z|Kqzo!Mt`H4a(`jT~A*Eplu&Q?eYeOH!&^ylNEKrHOmce-eZjz7Xnyrtj3ZIDD25f z63Ta*`dRh@3Ul6@RIqrWEEdeAfhoKH`%Xb#h#c4Yei_xpHH4|6id8(Nlum%MlUXcx zaB1A-E&90NgLwZ_I2tYXz`)oy+Rz1IS-VAFtqzfH5VsZ_;?3VLq}G3Hi#fCrS-g?> zW3JjbM;|*FBiSC!+W5XD_2xJ&4Ir@iqueUk1J>*#+UK4H0nmf5UQ_a-$2jK0vmE9P z&Z7hS8zJ`F1DX0RIaaanZ<5owq#~Lz6Oik;=`m{e|FgiWnG%2dfZS&+lSnNo6%$BuOv*tew;(|vT>Gb5HKA2SS9$rQFWDDn?^@sq)x-WBc*?PMeGs|5Mt9U$((3j~h35y$& z*b#Fju`}2;F;;&OMGBBk6UrjFGKu(RBzt!U=2@+XgjQ$hW3Jjb;Q1i300w>w0^Vk6$ZEX z5cH}G_*K!&R!|ZE8Ue@>05ZJ^DUu*WA*-JPTJYF^bCX z(gvpL(IN7+Jw}yBU_L^aqRJsQk=b2+a=jqm!lVj7V7Y#Q=^gpAAyi5Z*@I{`$GeLw z-oH%;P0n#t`>#m$?I&=L#W$(jIQ8uAPRu>VBHWGaPKjw^s+qxzGf5t8#}=>`8;1o&>T&~7Gf z;NJWah#9oT)gKsLnv({@G2hi+S2Dh>`EAMfv~};i41uU;cv?-rZPt@4j&-}MkG>TKFw@GFp#WIvpQ6c+kC+;V3sifyeg%fcE8a~8~8g=gZ zoHwww!hVUke0T|Y;?VPlOUG#*zjO^kA5a4OWQL2^_6D`lXgsr4L<0dDQNtL`06zcu zm}?NVt(R;gRg7^PM|(Z~0BK&e&PLA8tkz8XlT}yPM2Vp9el$S}ylE@zAG$0LgLwC$ zM_QD_&~2EQ87fA_H9S2{uG~|}_l?}Q+7$w;u}WEqRf&e#f_hMi0kF7jzm-Z`LQBX> zHZ$+tsYGuCN@F|AC3-@VaPV#2C_S*|)U%vz`Q^@RM9Tp_UB~kj?V-QHDoe&Eizsdg z<8S^@!B^c0vVF}}3STTY-b0S*3yWCr&vw7$i_yF zO_GD9jR@?_{hTYipDR2vHKuoN?!nqp2DMD1_lJf?sQ!e@t^X}H?Fzs=6~Lf z{fbKJHfwE1H|-MyM{hU38H5?41*Mg34zsTROpkY*@>{Q9r?zOJZ zjZOkYlrV)vTTL{s!^B@;$#Y6hF%9|$ZUV=zEYxY}h&qPyFi|}5Z`4X+I=w|ZzLn2l z+F7rZZwD@)x1k~|0yia`nl(w_aI=A=t>5$t&8P1W!_)$*5-&5D3iO7=Y%H|(9JJBq zv|oV<%jubMEx}$)<$*S2UN-07^wr|TqW9o^!ZkV9TlYwD;m^Q43xmuFBvcw7 zYn?xWRPX%ja2*4A&*qcld!f#n|CLgN1v6Uz2$yLi<&b&-7S;U8U5R%m#F=&ecsa?( z#>8NI4eR3E8MU~!o-ugw@D^zLa*wtOCB00)^gu>|dI!;s3Bn4-*0k$Y4g7_Ra_ zzRk}NBI3Zy)D@~iIAFu$*RI@C1q7X9u6D6ZJ!XrYITs!`d`iBw0%rPZq4M>;lVDBgdEg?ES?d!DZ75e?T1ufC91CkZV9q*rjo1U5E-Vg|9P>xQl1Gt!|- zr%e?-iYn@w_l9m``Q3tGdKixF%d_-g{y&kM4kLaJ&3nte5&YN@5P|Dj~i$AREf zPcUN_>qpB*u0D0py|X)1?<;_9!5EZ7m5Fq5_E$71?!V?qadUD>iMK?qEz^?fVj2KG4vv2D^?XDSV&ZNr)F{}6iAG-ENj z4OD!Z>k8`VFui?0w*9j#2(9)y0R$<&<1z_^#0$Pcy!0ZSUESi~_Lp=sq%uOIM&;&~ z+<|aX9q~zkph#Q@CKhgka@)!vTAJ@i;+QaR8AMH&aL9oD$6^=cWw?nUfF1{W~(rp;r}3C4lPx(Sb9 zuSk&`G|jC~2z)01mGhH>$&2-Kv+pIOPoBx&gx@gmL_io(#sUGg1uqL3Lk8obt{bwC zQ`JuFS!1Jvysgl_Dur_A&&GUHF^7T})5GWxJd9(ugm8L9&6gF~A4Xac)~r6HrD2J< z@_Z2c=^*b4w4utv0*=voqUC2j!-xQ9NX;k{ppo_1(YchlcF&SWGS+o$qpSA2+<=zg zQ0k+v?Z$*H#1qIvX8vFYG`WmFMqm4^U!sM49Y1UUS>!bkrU>=v23o?yB+LbX!T1Tt zoIoBbHflF(gTwfmxiI^`Zg8c7c6IAR4?+J`jvW8K4-b5%2>9wMCqu1lDB*y5{rCau zju&>`?x&}!-?%|Aj1f;cw9VELCIH8yy1X4FUp)w`KvR7PUvVhT5)D4%gQatZ)E~4T zyJ}rVTMw2OqbH6APz-|))LDU$PurmRVFj)mLnewL)fGW8V6-=p?Df0A&!0~Fw_7DA za$v^y)7V|$J5RxKW_#~^h|Vn#Z7K4O&2o=O1lloke3Yqj%J4jmv53Z{ zLlEv4+n1z;GWt&FWtM$GR=Gc1Q8}_B>sJ!83T!xthTI+aADZD9p{;a-ZvLt&Gut*I zPYPLUisJeOpUp`;EM}af@C!XZkruy|i)MvD!6!k~_l2>=P2z13CD-Zyx|nn6mGU*l z7tq_mV+eQC>mHip_UDJU7+0gMm#N?ZwR-A`b5h(ITd z=ITw(qJ|#BfKY2)n<$9W0;4)5T9QFc9Ozc+zsJ(cj2`doqhJ2U^fO<~lClnr)Jr?* zA1~P8);SEngs0s6%b#lhAKyqSoSoYoBlr3tAF^pkY#$M`8pe z{ZCp*)po}qfD-4cF{-vZ-ioa%XKv95;$6dX5 z=%+DWLF?uXGA_xR1L(-n0#ONxz>USmz1pAKUaBTbb)sZboCpwsd{7)omw8SHVa20{ zebT=s6tV46@rDsn6DlZ#X+|?chQhsT{-zXqp>#f?P1_{=*qua~Q}3yx8+Lz6LMGll z2_H_8mBM~LXgvODfB^)V`pRG(7q$&&fJLa^qHybBUsKH-pK7?pSW`bvhZUYGF^{3D zSg}k#STzJ091b%-24_!F9WF^rJG#5#41~I7HUldnIT!Ch`bFzFq4CJK24nsflz!CZ zcuDV<$$a(rOfaZl@T3Z&(fb3d$!uR3uQEBm#!0+RoySdxROtX(%pQ{N;i9i0~{~P`h63eX?i#PHg+@FX{d9Z&83nR@_ve?UR0jOjBM* zPfGp#zW<0LqO2_teA_<6VGsEDnpbt>x^wu#pNih2{dBrE{1lEDQLeJa0dv&&jLpgtlIACN_Kv5~{S@y?<$wIP7jgdof&FXASZt?oZ5pa(?& zHxB{1+aH)jWFN_4dr^h5AGB<}#!4!_2BgF2zTy&8w-nVLr10m)^+Lfx87N z;A@d4l9QJPZ@tJ!DWpq474+E@$#;MDsMaWdUr&pUUDF%Y0Iu7x9@-`s%?4p4JMzXo zC-kXMBIH%;pkUay>VY&X`*L4Zen-0dzaLW-#zp%|7Vi{U#p^z>e}{X&$Yd{&|KJoG zsH#$w4MhIIHwV8cTbhy4-b_=C!~X{IEP0}lab3b|NApCgv?HBKiL#bgZ{xOfR>qqF z*R%>hDxvf%X=tFswo1WLk#>Kz^xIbmU zo3%7jCoJsMk0{lI(-we`{#Ks|hjaaskZs5?HBxx4io;>qIJZ$?--bqUm5yG61@Jz5 z(5ml`^VozgIOGJeE`d6%Q|J*^{R6;Hv5JH2d(QY7J8S0rLH#)%%Gya2Q|ZNfs?VAZ z4(V|kBGl_TnT^*@ zdjAO@r{)*Jum{}DCM~Akinkd2FXq16I>f;2PJ`6*8i`mf6j#X2Txt%?kn5^8Gcq}$ zaYi0V7ovHxrvNAUcf?%DF7a+rz##BzzjTcMmrapw0r~UI09sWl} zAlwu^JJ0U?t53f?OZKBVP_x8}o0l8*i5Y~rHN7k-k~ubSn;qPVfY*%HtdOwv> zd$6#=LJTxcUwM(tq+hMK=0_z`FiIeDN%*=Y*z7)D2ID!*FWt){VzV)-0o>59ijs9E{^zQIQ_-Ru+x)1hux?3z~kTQ!~V6En>^ zNQKC>oKYzf9R2(bDyr=?lnh4oZO*8qGVgG&nFVo~x~Y8sHscclyvTRYgJND(`-)4D zSu5o(;0c<*^jryJLndi{9-kU1WYW;}aBZxAT&InR|7|`kp@r}uoSh)0yLLcinHv!_ zqxm@`ANR0SSbG$OGwY7tz3Q5n{_TRt%sq^yz}0k}T}&vGzn;kE4CnEGeY@YXNnLmT z+iVzX{wG6`kDk)-hgqu8<^#uRWqIfXB{D4`!zZvjMv&w7#U!&>91UVorVl#IM2a@* zy-0CTd7q2u^UIA#4-}ZW;`DxX*4xYF!XBA`eT33{mEZ~$fQ#woPdzOO<6xakFT;&+ zyRU1?Q4{QSh@a%@RjuBJru;|m8TzKHtf^uRs8tFQjkJj?>A6G)-P483dig)UI*(X# zd_84^2q#kLZaw{wm)7LWuK?j>}m_R}}?xY$}VfbHONp>2xvQ z-u)zyZgZ0FhX52tJcKYspn?llbM#J!;pR3V6j2azW)A&qCYuICo)iy zG6q)$r7En14f7nN$>aq2;+enq>|~a?_1Lp4k_a4*ETu|HJXn|ys;J&3l2|OF{)7i^ zyehm$wQ14x=veRkCKmy+0qMk2ZlE!IY%>x!tJCnxYA;dLPRH6!68nG_-vBCZ<@~+a zfDn8sdS9P?H@W-jCxJMTrJ@2t^PFh@Mr`QnBY@gF z&ZwHaQ#_dY;8j!_PH)tn*agB3P!r(Ww%+BUHIL2l)Wn(SC?DFRc5`{FPZvuu_s+Lh zb3#-=0Ayb^><5FRr99wfC!Q!qy@Pq+o`g3-TS>IM_kzu^4)fzQI7Z8hCyYVTv%_fL zG3%DL?E(q3E%{4QB8dwBuntBmgrgS;kd2IkPIUc>Pcqx`$X2E`@g(o8ynoKmm=Qlv zkBcDQppfHQXg?4N*R>9X4lpQj#q_2`O~F*6&eE#L_Ri~R{6ZYve& zb5Y|<4T}e2NVW>GI{$?e^U#T&dloeTrYI?;@*1GD!;46&h=O{?oLTJ0u!bIwHp^iY zn*?qWX9l3CJe2-7HtAih5>%+mk6@nzgt~rqQejcBLxsH=#7t7L<`;sNf}pEsISU&% z+i_)i7AODY@Uyb^FgCtZvw)(y#tefz7{*q^(WAH$`4}!Y%G6ZQGp*`R_8JCQ0{_fQ z%RFPISL~?|($rN(rR$mPK!)y^%2(3b?(~U8owu&1fu;qqf?P}ajaGP6fAwV#qymlx z==Pt-b$X#WKfKu5g}EJa5DX9Hn?GV7UaLVvExy@Sc;jQIJXCaPAOZ@U|XEn80c+r|>i9tCRPP+!L{`$n%=EcaMM=l7>pR}8(? zy?u2C`an5O)Ue>wHmCw>1|;3nPP7`N^&DdvRTaE)TDq)0C5(!1qE}zwakpmCD+@qf zR~O{cq<{^So}OL zCdBs1iWj`mwS7~f!kQM+MVi`n>?OjJGLRC9Q&vcp*7*P|CQLv&A6Gj}NxbryVxv5g zcZ>Qt)UQ0(ws6`o4Qc?wYW%@v!Af8okx>5K^*UOPFglQJ{@v4>U;#k?#CnRRJ5R!} z7(NBKJb^SO)2IVdSf~koxHh5e6wftBVn7h)Ix|4B1U>Z08P8fIAt@3Lt6&0JHB{ga z<@pJ<%5t3$*%AjCT%krV^%+ZuT~`R0i-0J&u4=SqD7S0|;TeF@FmLIyt^Wo+tM&h% zO2VK3qOys>g8dzi0svY~T(D&_x|QYB`+q>yh1%`gO1W}{Xk}Nvd`Vegz(T|HZ)0`f zpy-_`Alz1^{3NrzVhR4#oj9z;+a%MD%vdN0I22?_~#KBh>hK$cxX>@TOyuTl+ERqYh`%miP;!L9Wo8*@b>)4ut$`y20}vdDWk zUyf?LhDTQ>a0wjavpXatuZ0&Zo~~!1zCCKy>I~Wjpa7YnN2zWIA}i4=Hr#QJrP}dD za6hCgYd^0-N8#!#aq4mtod95z_pp00=&Zt9+6TQJsh4fD)QWrjmPOVL!wlfza===w z4o%EY$Or|yEF1njW|Y1bzC(y*N80Yq<9X_|3oBJ95MBRRYm4@zkjch=TsAfF^&z#8 z_>{S%-V~1i15|T-9QOq>$1bGx05%iW*4m)`!gZqIbkJmMqwtUx3Wp5rrp4glJMfXS zlxJvVC~>~W855@g)>pBE+rcSJy-K1j;c+uqh?NjWG@gkAm*g7pVl)UUhT=nnN&=a5 zch{{#B{=gd(8{JV@&p4T<9)I+OCuQ{0S8pQ;FHJzpo)8!f&&ogg=~}$6(cm{qS?%^ z2PHs+Z+>I>7G19s4jz8xM0J$ula7J!*ioXDt5p3?<>)af3#P_@-9?XF0!gl(cxU`% zcp&M2yUQr(aGXX>Npm$&62$k#!?wPKg()(gBpr^T-EVZ+)CHB4r={)!E)}7|OxDAK zyl%|E+0yq<7G{aR^0_PrrTM6oa2qF+Vn9d%fvEucu*JaN;U>Cn6tv3g5cgHh2<{ZkO{>P5zH?ehn^FG}&TI9be5FjNMfrNYtmxu%V$bv;6iG zgejhtP?n)Ee(>Xfb&#Z0byu+KlbrF_W78q45ayLq6ZoxgCKHhUJseR#eXr6!@;0-W zUca+4;_4XcS|{?spz?!GrY?>0jD~O+v!E{_NJ4R?ya{{9p4}SCe(Dw>{I+Y$Ia5q6 zFQDpJiq%j<`}jjye-}xN(SNH$I+%G;Dxm0CzVJu88on{j`LA;-y&$mAX>1v+E--B`y9RuqB_G*BF()1g$l?n4KWu~4KsI&6o3w#q#!$Xff6~Rd$4Q~C{j`{A^$hU zhTA@b^2|F?C1mGhsq7DS^aX8vA|8tP+Ah^e!iFHsw#bC`3gDkwD3yag( zY^&4g1`TfE?eNI>Ga((#p92D5G3G{rSJq$_kY^ri3Pl4mNTRj80HAw0IM&fZha zGVL3x*v{H}iE3w`yDc!=>h;(Nb&!RCQx%YFL;XK*mb9amcCG_2jks?%*3ceevxetK zY*N|u;HM=d*E`g}aAt-R)Y+%u;l!gO-*0)FrKU6%G*+zx|hKF)DOc z1}y#pD4XM47Str$QU7QlE^w`f6#hFlOURN33HBHHpx{4nfOMuf&nHmEYF^#GR-^Yo zZ@xTyB2a+8wM+a>S*J?v@fT(WRQ}IGTpByZv+< zH5Xi8c8{F_WCSaTH6&uO?pY9n#W325GUjIaj5rW+M#k{?&LcX{k!RYOr-$}pghDr< z1=^~lrkk>*COh09FFAHbM1V)PUf(Kuo4CrH{--ly5aYHG}sRM;uY;T~NIxd;lVp=@i@YWhTCLcpjC z7-0`pu2~?J!snM}Ei$)s(IEJt)Xj+T5lpo)y+cp!ZdWO>P6bzgiuH+es4$u6E;97{ zR>~22a_ZMlj1F4I@LuKX=XLJC3BEaIC{%gIklszqSWz0;j}W7+xCU;bt|ZC#X2r1B z;HR4oeVoq_h=Lr`lEhMwEe!(KZCAW|va#1IgBRbxiUwpn|Hp*bejW{ETC9!{)0vvGO z^6!Ro({=88j9acCOTGChtH_tER~6+ZS!7)dL^dP4mu+&sjBm$Vt7)M*L7G{j5# z${BSw(G2E>twyjrO%R-y>!nm19ux*SPn zUc3@_DuH6yn{@dM=3HTm%k$%K{y8jMPZmdeu7b4AjM;qLxw$EqI*op;B;^+F5L1a4 zAqmyop&``7o~as|Ey2AVdRbNkHXwYoWUCy~+9af;*g+(1fQ5+llfVN-0=KI;3lz<( zh+>8XvgG?aPT>mgn*q|rX~;OWFm~#jiQ_O&KBwvb8j*ObAj0<^SO3m}cv=Rvo+dIg zDNX$gJCqR(M#Uc3`Bn1R%H0=uVw%{qrLp}KR4EF$@;j}a2|7vkwm>9_E$M$4hp$xj z&Ip=!!o{Wdfn`mSCxc;o&%=3db^Nhs?4NA0GdE$zcd@gwRA#PJS=7mFY5)z zI=U+$^FMdk8tedj=6UmXd@3@D%C8wMJ>TsQSkHs0`TLANapa z^0ODvcBj=X@sM$bY2Yfe3gbVN+!f6GwlKFtKtvQIPY_y;y-)Ox45RHu#kJ4mcjR2B zaRX#nInr}#v2r73_POES%U9t$U@hZ`0BSm3A)@AVt{i}L*7ZkEqQIHbSgLRGqUhDH z5UTAjM=e7<9hG&x?6YUq^UuRY^pybBEy^L-nN%%gQ=62W&F61Su2{Btsg{nSetz$cvo-133iz zK+pjB$LM>zkOwsV$|FJ@3nqGeBDeo zVw{)Dc_yyI?>ZyrvQ6oAe2$_ukPoFkxAFym2 z(YZ8LP0D*YWRGZzyt06m>8O-I$J!FiI{qydVnD_W~>l$f7Y;2b9u;K z)>8a@#NbCa`RST|iNG*ei73*PgdqwVgXdeoWz*}zUf`9`#MffgX!TO5RO@>j80XtB zhbeQX5By8znFlqbVqMpX2bu+0c4Ig!!rW=m-H{F{WvC!TVPogRNuf z8Abf<8$>*$D;mTU>GUGcNqERhTq~QsyRF(0b0}xU|G0All69f*l}$QZ!*&NDa2qwG z-%|clF5ZL>wuI@w(oqyp#D@xydsajH)POQCGA95ihBbrCun8=IMS|J`fB^Z(3z(S` z=wZtId%EN4D2MvuJ2zY={yI!;QL8`_02%?95&$xd1u`ij0wz_THUMtK*%pW+S;TC+UJPF*Sg)GosIoVW zT(s)C%=#H)bvonX=0LL<$0>oEN9><~lXs&IQ;&yzN&2a6j!%nvRDKS-G=V$EE5p!+ zly59Tt}_>7)DWHjzK5$zl~_~Go%-Z)etb?@M?WD3CzLVXL|@V9G*$tStOpw$_VF&v z$NZFR^JG{=+g?$q7c_p_hOk+Mk@BB@NIMSu2EU#i8~j-YDaL@lIHUazfCHrP;8KuJTAmMLxU z2M|PfgwB2+@i+A~?}94*7J&_6?BN_f_f9UM3T(o>FAcfWCkNA917gxB1&bJ~!?EIcI3lmbTnw&aZ zsLNCyysNJYp)xHGiJ@H8^_$o8Od<+1%b@H;q1#l@=_6nD4aXw93CxW@h09I3sw1Gvh1;Y33rd z7b3xIx+m={ia-(o8Uff605b9lDHi}0hak_HfIX703zQfS4+9fm=m1z9pRZz9q2kNK z?sCuTen;mph)Md(luXA44bhK{T_Y|&Od|#lL~!aQ^mas5`C-Vu(q`x)x0=jU`k^yk zB7E1LoRQ|1Cde;9du&095E-^SY+DW5nV8#2knFx5=ebp9HP^$a2uYEuE2Ww^erY{_3yoXyw;H@d;<))&Wx{W;^1vPO^1Myue@G!HRm6Hhm2o%SPuhDt85%56yz~|UdXUh5qr?q$Y%xHXFBeNcVz~MmIYWxc-fL-ANZHviiC#Nndi)2;w-N<1IxM+c= zz9J1GbFaG$=U%2s_~*P_4AQs_zROXiVC!kwT%MkOC{2X#Wc<5+Pb{FX#+I^hG$QDh zqPs&2O!njRiL;y7YQ#%Ty$7aWnn64n^+-&zmAjkTJADK+ZGf#>q~t=lS6BfkJNKio%iH{g1X51(7K;mgeOPhdmaEx z1c&jbr4-CJhYAksI{%UorQo%;LEKl{Q@!;m!HqGvr)vRk8im#NSNOQi1&a= z@2&CZX0%(~I#UDNHF@Ro8H%n!4m~LQu}L#NA9bh?fjJomLs#zY6}WA}hz-dpLVCkQ z;AM=mj{yfCAybZjig5S>g{&jT;vHFZ0HT=I8IJqwHEe_qe2}f5!slh;VlKRou?z1CoS5T++mTnO zMz+hA2WbYtb=ryzK0<;X@7M(;-F@N<RU) zw!F>TL?GyF*pMwghdF~sl8H#s?KS7~%ZK^fcvNS|9NNFIk(Uh0J1=emLi<68j~Fz@V8i%VAxzNPzamyruT5uu2?PTA z<-4Ss^_cRP?>bf9BF!$Lqb6^w8m#bE==ug5KJQu~d?$lFiunJrkQP6@2cnZ%x&9bgoW%d(K z!#&ciwlWaCmL4Rpy+kn#?`=-;s^hg=+$)z3Lt>hwV=XfQ$)v8-#1ydo3Ft$ExVdtx&s4O`nx8k;0GOd8gE2^N2) z_P0q!B72T+u2=bzU%KJ&NNG(Xsr035Rk@~UCOLgL5#T)8GvsH#g3T59RnEkOkGz~z zHmLV|{ec)6M?b02H_HM3vi4Z64J$AOClTm!5`U@2%3OW1^R=AWn|N zp<`(D+JPPGcCmw5B=ri5X?QX6{-NbWmuH87$a3}hs3 zL&bg1swgE~nfl3HyrF8gntR0vcJ=-ng9eCl4s;V$b0nlg@%6Weyby2*S_FSlr!9nE zU*iwh3lTpyUv4tu2q_jIMTa2InSe<$ZHtTm0yrUp!0-V1nh6B&XgIUPoBJ&CzT;j= zqE7`vzYkmTDCv6Dc7hfP5MZT(k0VbMt0k?_;m5pd_Ubud$AL!P3jOZvWyzQc`aBk( z=67Z@D{t;hdXJ#nXGOPr(*P0Ev+_*r$v@|di7dtVF8eC%eggahZC&LX_-5~ioWOq4 zl0bK@p?n+))Wob$U~YRa8keCfkTD5BjO}_$sDxWDZ4p*0ZeILEj=UUJsjrvAL;jR# zU3Lb}s4QpD>g2Eb}#U7%C@PpykLqN!JF9#vX!&X8^IfAfoW5Sbh9I-3kgj1~i6l_Q`MS&O}W zzQTVUbfTxE!Mn82fR>6aLCHx1^(lo889?KXqnmF}uC!Y3GO(caeEjTtfzhcOpfm4$KbjHucM_R*)8T5W*I6)ng=A2&*^)9?0#5TWDDe-CpP`1_E^%7sTsRF1*;+wz)*| zcw_BmEi+5i9xHZoB!xq1tylAAdu!!zimb7^^%zQ687Fd8z1D8Q;9-&&9NA%h0!-5A zHrNHjZ4@5}_*}F+zwGPD?pX1srwhO(BINV+Z^5e)qTJWO@amWbo3@@H8R{x3fDa>y z!ID$sQx`x0@J7xrK-s|o5kn7d(m$TS+M!`L8+9UsOzjv*6BODG##76g7(4&Pf0puX zmxp3a3?4sF6HdEjxh28&KYNdf_Wd9p@kph6kS`V9la8_(ErV=95$}mKqSB9OMY6z& zm_C^;2Lxn2biO3s80W6B40EwshgY?P^U|mPEJcsGl*$2F7f9iE7WuCBuRzUG z^>qf7pg0Ptteh45oYzhvbAsto&;uI=1b~eL6XH{;@1<&^8F8chW;wY?gVH_jz8r>4 z$C`Ww0@P;N4?UQT+ltm1T??MF_Eyp+E$es9Ei0AhkArf`G=pRD-rVh9PyIeVuQcrw z!1<5aKNl8iC}|8hQE|axKvt;i#x6vqZCwPcWhZa2Ts&N$5nb)d!ZCDXT^q&VC&{zb zxdMI7RtXSLVWOR(W2e6;ZyW3l;IL@RTY_)=$r3c_tq=R_WXZglfFV$PhI_ka)wcy+ zl>13vri^A;Me9V!oN1JO!#6DeUH~P}g+as2eCdPZOi9TI;LgPM;;b`%lD|DeD@(`D3AS;X^9FfX+9`Y+|(-n#{Z$rqHNA=>3-rv@{m7vZA3aZ4wK z@?;v<=4v)zqBD3m$rMRocWX3?&(5jFVreZP0~<|J+yjC?8Inld6Fb1C@^W8==p}48 zmND9B(&rPOif2gh|HSNq>NG+9A|x=g+!px&;u%E2DA#Cy`m%ocu=02*Bv-$$V}F;# zF%VEbEdw7a!5Yp2`NTpU)PR``2z7&4FCVNP_20 zW9Z-gj&OF+P8L=~uf!j;dMFp)uS4dSNbMKR@?bQ>lgp92&cF&z^f2armL63gZTaJk zk&oS?Ms~g$-qu504a+z#Sr4PUWOa3hf5`ud17Oi9$Yv2{jDbCvjb8AWuvn3|Mhsv) z`M;6p()k2dfa_;nZ6pQtPqICag{Jt&$t`zo(e{&0qI8d9t)8`}LK#B1^X)MX6Nu1I zj%hvjmI$*ea9s{T5KeP!DEs5!=l*DKYpgyDL07W}K^v}?tp2}nTf*!}^+gFLK#4vl zJ;1!vM~#18h_6kw#1V=A&)GtHrmYGX14l>75ajg7q7Jk*{cPe&jl)^&d=f7KdMArQ zH^5nqU%CD}t^|4-z#nf<{7PMMoXJ8S!8SaT5FaAvu&b7{aj1ti4E-m+C=#G+P9?;gz9I`v-<4VlfABmi-h!*RH@ zg2sVh=nYW0=$B4SuZX2xOf07kD3&8;I4$?!GWJ@xq7f|pM@ChcAL?)uQ!Xr#epRro zo6s=k!8jscc0&?K%9-CS3P?Mpt3}#Bu9LI*TvZ{X6DxkAv3h|~v=*knuWP)>DIWN5 znX^!Y#=(AX{WUo`SF&#L(yVfHUXlRij^`u3`q_ypAU4x<nZP5yOdH{@P%elwnp6Y zx_7~`@IsnT)mCglEIoB9mSpYyo2Tj++aO8p)y@(<5(iClYbAAoMf@?l3LnOsf1}35 zP*J=VdqLOI_DeMoht=QfFOcd+3pXw4clui0ole=PVBMV zI>**;M&~>-fGLjDw*vCCc9rk1P>5)WN%Rc4?T5cdDIdCotYTP&D6D6g)a<4)VcnF* zc-?a9OD=mobe_M@r>l46$fYB>z1PT);ZtHOT&wKjcruAL{bYo6L>EJ!{ezW_Yk||S zk#+D{8fLsb{k~UC%_8GY3c!$`#&>3Y;8*Wu+lCZdh`jmUAm_H){UA`7CPK$Tx(#ej z_&tysr4N@CgjxgNk0M5Mx4%)$Kv(QPPCvQCS9yx*utkqs-y|?SfA{rDT`cVVu>W3b z@-qm<6zyUB0L$x4L`kB91@3~sD)l_>r4d1&Xwq;V?^Gc!eHXf7K~0b9I&tScX>*F_pV^QqUD zDh5;dUa6wN!dE`#pB}t-Yo~l2M~m3`l{-RCxluBsSVnNUT+PF#+Eu<>7@Xp6g%7%& ziQMJWdP*zt=b=E@JORV3h`uMa@6L_*q198qMvuUCSR^{MkF8Uxy{;jwEGI2U=>iSA2Fa(_ARY_9|4MwK@si1f-*Qn3Ln!E5C z-DBrCg7pKjd+bpqP&v?@_?)c!KY%{T`CN<~#PWZ`O$Q*!ZP^<{s$cS55i8^)W!yo{ z6jcH;+s*J*1+)w*fOMzifVes4)fxyrYUtc`hz4*12e&-04Cig?@?C#=hd>t{<^hE% z4D-Rk2rnS$O%%;P7;%mlCGs8{C@x*umM}VWAZtD78yZk%gf0xQo83rs7~s@xdH&T= zSJ{%g%7(LGL3T@7yO@h8_KPrz`w2NMbxq-KAsLaiB%#F~a-qp^&={2@4WX693-5&P zt(LS_+w_Y2MPLTuy76|eT%cB^mH3ozNap2O`kJO0D0<=+tX~}8PsSo`o}l5?;WCF0 zw#aYGjn64ch;HMGS!N)QLkYb`*Bi*z=Mg9qLuhE^r@TQyV?U;lUjb6n^~nMv$v(5E zm%z@Xb#SLW`H+1nMsw5iXU`PsNm{itnA%%3*C4zz?nx&>rFGk|6tmCz+4|eDNSNZJSal484rQ?@r5cTv_FTQYEdJ z=~?4AgAULaf*=?-M9s-82$6yb{D4*J z=u+5HV%d5Vng0C4eFcFZ?{_AoQ>3HIY;Wo^APs@7_tKmbrSqxc$$JJ?LpyzT4bT=P zuwQa>Cy&`jJ>2q=ai)#Y!@V(xa3mj4EnY#3TAx5J5I;FBVWSWr6}~%%84#|z%)T|- zyj^i)-}+Z~mAII2!_9x!q^j3G$jZhusjuC=ec ze^I`{YVrt{zy56zk#aUuHp4Rd9%&!jo)TYxxHb=nY&&D@>Ae10GBr2}tdEir+znUG z&I-(p$>-fB3Y5wKh5J1g1qrcyJ!bkrEQw9_}SG z057@rBY>9vliQl~!&WuvX9Old>VgjUgYRLBPazzw)X6U{%Co!^@!qDV>>RCo9`bZG zhTYVov7DOwB>dX&7}hRDeY*m@+@vARq|*<*7oZ#4q}5=4tTC?)^QaYMS3imYx;}ol zmn0C<}pB9I0lBnVsiSZ)F-~_I#Ny zqGhu!(HK@h<^tPc)Os!zTs5kq@Ae`|``HNeug=D)`$C zY((t&EtK-o=ps%(coKcfl3!*u=$GhMe5ijJfX-L}Vo;^gYlW(euz+XRRpg-Yf_=wQ zW0$G5VE2yx#|M@CIr*T2lI?^5SSHgs!pV$62)`%8 zY?_D*1At>%?jDtCdo^%&lzXJB&L>;Yhn`w(cdcFpm5D;wtk089k51pswm6t6HM1|b zj#tRiQ1#$uVGO6ayc)D8UXz{$S1gM-f{*1PM{aYZ$I-EaJDTQDQ7ev|< zv?Z_0oL#8z4Ce$eD_lE32*qw{6!{S`Spbxu3!TqpMZ4g$XET)+_WAd!#Qj4!dh;ki z_l|1mgk>i{JBU7z?XWAsaF@TS7oxg@scBkiG7wicoIrRpQ(0mjA{=KI6Hc?b5H11S z`ylHRj~$daH%(943eJE)0pLIxYzJiXC(h6y-IkRZ|4WWhgliYKyb=LMhtLbyk-j=J zO!=0H#t_<_$qPi!-{d+^E;|Tmryez*g;@=PTI4s;B<_HE>F7FN(a;hAG8Hm1VF4DU zgUx^d09XJ35CGYmpBlgt05aDzAs;jViPN8vz!vFc3c(=)00~z^AkZTuz~8h0!{so6 zR5(r9$jg%U?>-qZfIF98KLMJxWluVP??n4g}yBjS^mu5=i+|2O9T5iO1*hb*$T>)whLY7 zV&r{4*hgz-L7AqKh++UgQ^4 ziL-s}Zz077^f2a2mwq)5U5RA4*>FWY1>5y*CH(nYjr{~yy^JIZvA{7eLhVwx-8Xcr zJZ{-50N4PvBW@mX`OpWiy>$i*3JKl+sTR?#yn7mz?6 zGs+-Zm0R*HO#>QFY2;krEwA-=G=sZq=c`K%ON<7!LOR#u;(Z{T#+iQCM(CL^r15Co z$@X#zSr-mp7XcsI1~v%nq5V$&jSPuSQNwzUwi=?{awPZ_{KlCNLIH0gZu+}%(FjX! zG3{f9ZxwapfgGb#))tdJ;|*PsY$bPb4{PlwJZIS-s}a)Cc{8M>5|-X=J6o1Z5Bq}O ziXxN$yywajr$ZmmzfFiE7U8nc1zv`V(~h)b`zUpaNI|)S5g0-5mD>O^k<-Pvtb6`s zd>|0&R;?kVU@JK(Wz7?MkA5<)+itWI!&#tRz^An7HdOVr`er}O=Rz-oyJz5KQURG$ z+&jMex|xgSDXmo`#$`%z#j~n41)1 z#`Mtn%>R8#XaT-E>1#lPXM|Pnd!NNHip+1SvpG9@yF?Gi>-ns`B&CcRN- zVgj%g#+9;3o)dte^#*vqJixv({;ijgS0U=7aAV_2Jbmh%TR7yP%o9tqROt3NPT%AU-VPxf=z%?Hf z|3ef#9!ZsX0+XaPK{JWAR=itg{AV=3M zE&Jp-$8M_>@+8*Wl$fX1LgDlA~K@>E3U9x_&vWZW3Ms03@p#3oFmH@t(&G-3t z_5MN;!<^3xCm8cxqe6kXuO^T)%i|kfY_~K4Pgenrk7jjcPGA^;OEuhXkmH3GnM9Jq z3Qc$?ZaHwPp%WMw$*}ajH^u6>L6cVa!2gpR&%KV2yPHkg4y;R>&klAGI_!=GB6GV> zFKBGWNlChYQ+#km*jxqSrIX#j6{KeEVo$jQ5hMTh&a{f4H1v8Rq{~B{kL}=CcBMj~ ztUSxKqkq=KbX2xFBDx;#f0C%s@4Mrs?N}jOUH$^%%)4cT*AKE2)LC^kA_kt`nyc8J z<%yfuOU@%a@I9d7k1S?Q4Yvb(h?9}U-6?;#o9b_rHsH?S27(4KJ zk15r5g|Hh#P~s8zcH(L99i`dyp0uwjci(!SzL==cfVDY20v zPax95sm+y3syH_=8By#)BnWXTv7(X|$T@~9+Q&$J(RrZSx=<_RhIMrpp6z+f?6`XZ ztI#t~Q#L@@lhx{Srs!S#M=JEjaW;Ka4iQgd`rrVp$WswSx`DI~pXy2y)R{soRl>~4 zMfjkTr85*EI>@jNOfMOHg~1`-8h_gV!O%vnl$; zFj;m<<1-*Vwi@|VZ7;k>RloCumP5y>n)kf0dP`U{<^tQQFEiPW>jUF8Fwkc;ym#@u zt`H-VjA~2b?inBqNw-qum@r>k$P-PIR1&*uyV=trpE6r8+Qm_)KZ*lE7?BH1Q@wz6};I5#g)F~egbIQHP`oS(O0dQ zbS7Q-ZGK|Ilb=Kq6?X}F#C9~Dx$n(d9n1;|pdjq=!dF0rb^t zOBz33BXPuv?dCEp9w(~@$#3H=tw9-#77s(l|wRUUF7eG$(_)lnQJBe(m`&RVwYr`9CR{-%_!^$$bchCcA5^7 z6@WefGW!U{JsR%@w0VF+W2xWgPS8h9j5l~44zD^u=$*b5e6i|&as40%LGol*w@a#N zSp$N#(fj7QI2s#}5_=)gQjS<}F=_PNM-zAuLmDpnY`MIdeS^++?4k(QSBBYX^*2O% zy~tvsc>9;10T(1;c@{jI$gHjA*T2C~YnowYq;KPlJX4WrSOSs1>ef83jzn%vEDFQq zOKM>S>o0)Qr|fV1Y2-_!8cke7DP%%Y44F*#V8M#a8Ry85sr%*QN)9_YGC6{=f2YH- z54sDA9N)?-3dBwmd##))aGW*W?Hcl!uL4>s(aQqP?;i%dEY(`Qjh2!`_A-2>sXt;( zRW%Cc`HqpMZz6&yF|l6XDOCJrdAF2QWb{8jZyUvVDUxAOy#F7bJXZeh=u96JkOlVR zj-WJ^6=5|*)XX)9gJ5>>#@iwK#lwBkJMdbEv!0IeD?TulgH7A!K6^NI;ZM!$< zx>WH`D5ci7^2+gapAM3WyJOe_!1jZb3jxb$=8};AuWB*_9+y5k8B-*%PiOe=<=}hR za-BViO_bTqElv(P2O7(GC!(&EWGp6jouNNn8e+G*G1Sq%k62{DOgIx8l!Air1tq+6W zAWm80aBy@(nv-0*vkk@!kPTO3OYshNKqo1Ea%EMM&_C=^_U&h42fla|gRjfb!pX6B zL>SNtB7hXT>ZnhPW0X&JdL2?L`!KC?Z(vkkgryG9=~B+uUbx-R4H;QdS0u(ko+6Fd zczUvT1G-;T*V%vS{FF4sUlRHm=di-{tDUBph6>n;GJfP9dp(}8kjg~3}@>nu7A$=6wN|gQ*tMK0n=jV;iaQBLY2JMObwaYz75GK4 zzMVakZ~#0iSrf9CY)ox;P^|VVq)%@zz)EZsFrIgq1;E4ZPXvmIrps{H~UT8 zX@|l07@%qYZ%$-+(h)VqGcgwMAi> zzG4ET`_(jq_>o`&cWdUU1~=-&A3+4oAdj*OJO(|pLK_c@KOFvVdW8^ zU389H6bwq`_i)vjv0w^Z6PD!Oq^kDATX6{`j;wkW7|HOHbM*B)YqvH&N5qPTVpx<;qUp zQI7Q;KG7oTdOGaqmp!p_Qc+phzsL?UUub zJ{;b_P~$)1mIjE_O|zoQKisOK=3F^(Yn@T2C3Pq>4gbdxWu;48<%#*`JaOyjUKbly zZ@~&Yg;n2P6nH|&jmA<+Un6+C1H#{UgmXLk13o#GFhfyQ zin_a>-f1; zn1&O)sw;X}=$0um{-wjh5FrS<>@~+&WoC<@f{GTVNWKB6{ve*-w80AtU|3u`7sL!T z_es8w4BZ1^Z)l9ABb;6b=n#+Sm7CUA;e00N0^gVLl6lV4A2BlbVFDSzSnv?-Jk;Yi zym{+NUcF3I3Bl9~Xj?+FM28jNquL9K=pHk9J6u62iV=knaR!2VQ_5O_o#~@V?$Ao3 zPGrZDh&^7ww*QgljCR+%u)O?xacGAbPw$CPy&RF|w~~~io)@33l|j9b^mE^m_8E}% zLC-r9J1KeMN81Fm^<(UuHfK2A)Nl6%?D*`-EvqA9S4F#fAopzsIbtqV9m8|thdK|M zMC#M@hc)QQ&N;fW^Y$>~S~ADbe2R8wd@>E!>|Z{D5ap7lzoS=n%4&L1pE#ZWz9LIy zp>gWy6^3spSb#IjgoVtCi2pd?Dn%tS^ykft8uE&&A}&mx+G zTzZ+txKRb$-5uQKrJ>vC07cS4b`d55gr?759L@dbq9tfi;qh%7T{ zCk&>jh6^DJoC`3J@uFY7KT=ttA;|ux#6XHKs6H+Q{$-6Qd+E2%l$9m~!se(8XhEmg zVEwPsE0evh{^Zz@?z$$&@WnTdK5m8dGd|oWE8Gl%Ke_&h@^=F?Plp}SpUJE`)D({~ zsAVpy@Byf5lYX333}&eu(;OSP4nL2MEvT9Pn*OAQ%X_-4n4AU?B-?0^i0Ol}tbz;; zS#qX2-oZg;5pI%7K?e_rQq2@sHGR!!p|vMkrw-VNR?)MJGI$$%4`<`E4|j#T$r;^A)5>U?_JiBDZ>N6 zA8Zj*KW+2x@ZWptFA2Q2nIl025lInP6}Q;fk|QV@2*h?C3S*GE8>d2#cNnZ+^bad{#s_sp22_E zbiO@W>+*lKHYi85N8|Z8qY2xi&_hMcXRE0iQ3qY&XSnS!uJ215A6?)d6nGu0=fSg# zwgD%98G;@D^Shgaex^-_9MRp9Z>X>6M+gtK!U0SN9p7Phul4an8_E-?T=*vPC#!6n zk0?wNj8}=06Y)cD9d;QwN+Hh5BANx;hO}5ku{MKaiaZ_j z5B&rDRN7~3CbBW`(-w?q49B55D|G1C-1toA8)5dJp5Sfb4434{j?%(RZGt65J) zytH|`?>V;8Krs4FQCLbCwd7(Bs0hT&w zlxON!?Kmqqm!S5!uX1GSCsvlY|DaSs0ateO_UB^r1nBRk5unpo2-Be%^A)?oh9Kqj zMm$EIEIR?xT1=Moj=BLKZ39k%O{z>qwFI~VL`M4U|6x4#74x!?q^Tzlc@KY=C%I^X zM6r^HT?K!0A|s2|1N&oFS*FAIf5RSw zFiJbeIFV;uFL`kGYV%B{fwtCpOIPYxyp1MA-|42SE<0b_W-|y8)~svD9pjj+FZ}u{F>K-H z98$@}UluvYj6h_f8heJISOP}UAH74_2ER}i4@B`a3-LByk*r{}x5$9hRZVs>vE~zZ zr9|U`iXT43OWvkOf4ala$S=jrgT3n<5GyNUx$bO6?CMima+Z=*Ucn@jatj&GLzF8g zXxWnFi~pLz2^C%NHo`fcB0?q85))VVH9!%gWkWsm3)sHKX6jL;P}YFo->h47i1gw^ zV(bMl9F$!#bV<{jK``RGJX}uEzy1g7i%zdt}EHhsx2 z;fDhMdyeAsbdGlXmi{iRt<Ce{~9Bv8Z@f$Dx2R5=qrC;wZ8*U%^GCa zRHI8{=f8?n)X^u9b(S$sB0{`(nvU;eGHg&D$tZM~C>}9jvu*9^Ud$Y~Ff`!~nnxK~ zX+yddsax!cD8xG#@7evHn}y1-6SJY1E#OBB_^oikBKjduDdYW-#;sUwtN#JHrrHC; zS`34@a6iS;hw|ryQL6CR!n-1y2n#qwA2Y8Tfa&p&XBNYRkh)5)d-Q%c zOE!+vkikKkZ(YKkLi#s=kK;FQwX{M~@bs-mE!~IdJ$zfF^MW8UTx zm?ntfRw9>kMBwvj4;R1|h%Zb~XIJQwV(of})P0#mdLrgahZa|xtWb;KIIx`!mJ9PV zV#_Vb^WfinA_?({*^_wA117o85NGlZSiPbcyyyL2>y?9Y;35g)aIb})FLZk##+7_) zTMrL2P(MM4jgXcFPgP%LzJ9IyoQflZs!U(A&7g@oL^#uM$JtfJyWIb_TeE@71`Ol< z)Henn&aBz_9tt@L8d7(`SV{Ez(~UDD%M@3x-tEgxd=AjiZ&a_3A8ybzT`Eol(%I4I z?t1t4Rz)`&#_|0`l1Qr(qXU5t5ixex#RQf;HxVc@=ZDpcgVOGNmz!y?JY?mg}=GDdm0%7(7!DML1AdQU`PR6t8bz)OeYdm7$`3)0%y50 zchfBb^i?l;ryG zP=!O%2r;<5P;6li*MQxg{7yQJ;0n6K*1ex7ba73&E1S(pg`(M6a$mIUCN_QvugJd6k5t$O))GV6b`s3Zp zAw*_c^Yjh{o1-mN*RRdphGXV7A#c5YUKwXBjoij`M zFnI3=j|St)A{(nDU#rX)Y1h&(^OmX`nq0_aW98sLll$;5WsbA>UL2y;Tu1)javCX9 zgbHzzOjC0HK|_@+t7|$aqI3b7cY>RcviTO7yD(45zD<~W;(t5iabjhe8nq$vMG(h@ za&0CsJDOV@#%ICGD&bIDl@lad5je!=7%~6{xHV3rF3w&0u|$?V-CK+IiqJ-RgEsOt zOm4zh;Yr1rVYZ3q?_AU`@Yq2mOISa$<1GG#=y456>1}NVQvkGv%&P*dE{DJD#sbYB z#F7=0eYdd}iUYpha{_$Xosa`7zW)4m*r?*sw#O`h$Fhc1Cy%w4cwa(|L8*zFtr=^P za#f5|6EpqCGVqlIP0Xa%S|kod$qT;Y46P8{-B|bP?fsn#4PTJMwt>up;w#~f&czS; z0R)O(vdwr<>*lBm#PCM*Fc0I39`zo#F~D5Phd30)2LRL&s5Tk+HC4Ts{?jdQ^8dqF zmKrotw7NhS-LZACBW}a^>mOC#;h+n3ZHi|Ty9tGQyiI=9q*DxUokfS6*Ta*eki{%J z(5N@brPT4MeG8Owxp2g;xYcy%UaWH#!mF%}2+HKZKfJ5|)I$!3j)uyIGj)TzU`+#I zXQKBublw=ZqbsE=sn7hNkv_2y!LqyuNGpKT!};nXT`D6h1Fgq2;l zOyAZ&Gm~`Wv}2%*Y-l-6_L{*VoqR+l8nMz*DlgalA%x}=jo|20ZD;|{KDWp~vDpKp z;$fMQ6p-;`QLs%WsK*zR24!*Dkb;OgG>_BRGm^Ne(~fx@;_{s$b{Tfn-#wa?baMoo zb;?ZI7P+VVzgp@Ve}H0^iBP#M?XO2S2_SB@8XN98EgR`3&@k75IB%nuVwo?C?!N8` zYP9xO#C-iA_Yr5^gOY0d$2ejhr~d6g>pTKIL41%x`h~swvq?c4SvaCQ`uQwqQLJ2Pq(#Yp*ofj-|&CP7q z4oR$_T80!;Q#IB~T!{cvz0(AVcw{K}ouO#22g#ij(7o049&0`r>t_5pssCs`aW79U zB*{JJf=#4h17sK@1W;UJW-QNfv~rn9f2?nYYH}50j4N6&)oklZeYEFkNt_errtd1T zIM6(bOsWmRzJZAEIx^GBO92hVx_LHU)4a4IB!Ggy? z^6{wtQUj0vXt-7V1;qFN`9XXa3iEA{`T(KfW>2wZYMCgVZCr;wEcSmmm zt;ynx+CJ(~4HjjN?nU{$gLSxK3nO%s_)4e5n1kA~wE9w4?#>0_!9loiaRcoGh7rtY zszHDgb}snSzp3nnwyL9KSjvKIYsNtgb^0YVQK;-4DdfK{Ao1if99mM;$2D)-rJ+DA zJfTKhX~uyIIhlTuoLN-+N?EHQJ1%4Zdq9N0^ITLcafSDe z-yA*jZr5xj*-yFCY>102xh&)mv=JvOUXJ!H_PB*>jIqD4v<+MpHF7Lig4 zUaGhxkQ#~t5VW;m#qP1WH$+9*fI+}&hw3D`CJJq60QtYluSlz2TQc!<5cN)smAJJ6 zmGy(NOnnej$y<2khxmP|=#G{m`$u|X1WX-#S3YY(g(umfCezg&B#V-0cu)=qsRQ2E zlERE3eg*{5Rwg0N;GY}>KGO`q@2uX0M&sQ4y0M+o&*}qqHaS1CFRr~_P@LW>O<=ba zGnRk*o<|m{E?dj%lQ)e&7{}i_hfEn1*Tkfj8wE5orqkR-`4EM)e5;{yC z#Ek$+o-~>L2{8QgoXmwQ$i0pp(cb?5CPFLVTh`eAq3iM&dESRxMpUy9$N|-vTo&uV z`QOZs;J)7ha6llBgsXS#flw>PqF)ph(Yn4K+ zX*|KA3>1_ot;GBVa@pFYjmxKl(kZEJ=>((W3PK5<+NJ>r0%P7doH>bJR7V1yeM6&> zD-7LIE1v9yq_@aL_^sFT;?aTBm?{uP%x{;WrzkRM3XLFi5BUjRlRAp~?cZ#mp0HBG zisL3W`IwG+bs3#aX~cJNDLlDDx4HqMBHT>{pp*BKe$8siH^;=wqOQ?<%r8{%sX&Y2 z^1MZm|6wR^T+lvs4Fe`|N(wWOVPQT8z|6-fq}xN_h_HUD)fy%Y+~}b$^*7xlds#iGJDz%WN^Xw997vp z6)$~o?M(U4;8!YNWXC2)9B;aoxF%PAk*+8nB;Rr_xxBz!Tui9;gac%zNY>(Cm0+D| zJ!#qoe(uaB6IwsMFf!8wGBW^y6Q>_G0D4BC78oEN0Qv}_(7DQjK%OgE`8xim-TT>;KB2;-#>81XMQeXMj&h#X0-Q>XzPsT;y^>@JH&qZy(c>s4OTxFV97&w6qP5 zDn(a0mnm=#`Et0JaU?`(D7R!OOUfiRrc4qst-1otORL{hg3@kD)azMn?4@QhHA2#x zTK!yffmVCJzu(PrGos06a9LR``biF|gd>TkTX}E%5U}u^CDrdh|L^d1cK<+d;9$n~ z=a`C&4L2@#&NXZ{8XSUyW1{b~5p@Z{?tK$HaUXySA&0bSfJiwp)y0F{&H;H_8Du2l z!Fhe&mHz+awLWd@$Ed%#ZMUx7VwimGxWH;mq8^(aVL(|+KwTgV4!p&$Q(3(%(sZY( z$|g-&A*c`FdF&$uP1_M+Bb64xd&@Kk?hLS?vbiL(#J!wHC#k;fOIjBGJ0F+N2YCA_ z=iayFE!MWT6*NTh@dYEA6&PjnU6V$jSa7ffbj}Sz0JM1KW9;L%Lf>YOnk)z^Y9>|? zMq0gDl2)IM;T^E{zo;zK4m(^bo?fZoLbw^GoW+WHGfvF-dN>U4zuPCb$-avUB#+BO z>39AhPwLzLp`FBSX>PFx)gX&DV?9|dg8v@fL_*8;7{goC;c;Vix8*E!i4Tx&TtU=Vtq-$8gHSRv3wTB7*Xgh6^6WcZcc?$~%z5KjdMRC&89pOB6g3tF$ zW^s`fLR43;fJN+A-YFgNHGCfnR)lB|#!_E#5)lQyc>JzZ$TG$x?@sk#euKK_ zL|CXt$l|43ap=PbzmsKX(S|=or;JEC@0$G{nppOHJ}A0&&Aa{+#U`E-AwVV5koJUm z)PXLBpGf5f=wIMfAL5Q}vKs_X1gfvL16zb9u+j%? zdT8b+mhT#K!g`eZovZX^0m|{b&pj0OCP}et8_BT&Iu=a(Dm8s^9ZWt|&6v0EC*FQU zI)OnEzMJG!&%w@Ym$$X;xhKG8E*As|-{L+MumQ8M~o;Le+cKrj!Psjs+5&#+jm=XXo z*bFHh06`VSkC}i`iwKL11ByTZ@lf~x)&miofQ0nz{-B|nHEwv}=O(<%(wV54^^YSI zV{b(MYz04w+6BdGJwd$p5S?9=CdaOHQg|yQ0skiwzU4a%Xe?)gPew~XauPLVru=3l ztyZ{J3OhEFH9b;kp<9dvsP2Nh}~f@CCf{BdUPzP}*s zoCXD4Up1)OohxDzQ!gU5_+xVz#j4sS4Tg(}B-KokoyLJ^KL*u6Faf>0i%5K3X>9g2!Z2Fo%xO8$bCBg2+ z?zEqb0DnEc^AVgVD&BPC(GF?P^7H)zJ{UjofZ)!SaXUW6Z2yU%cFeBeWmWJa3TY0J zMC;v!sOwD-n!vq@)#XN=a^hIfzC)(P&(IC7OF4&ZuxOE-Sd-=Sv0tbmWi(eo65 zK$={}r%DkXAn$NF^>-xfW0KpSf(u6gMo>4elb{>{S?f&rZF%^@>M_wB`Wa?IxhX zSbVTUw_?HrWnOW&JQ!-PrC&N{ST&yaXy*yfdVH*gDD1XilY^T;kt^=REF@a#qOL0Z z!4KhhPvQzs9ex;`&0af*a@538qgLY&2X+%Yhm_v5D}j?PU;q(Gx3CmS1#6(m6`NTl zfEom@o@i#-HFz2~3uSb9xQMP@?bY1Q>*Bl!jM&T@7aMXE0V*i(jHRs73|&M-Tw3W8 zo~>uPtYWiWjGz_Z(ZAK=P~mkWoZu*>GQnwx=H$D0KhQT8D$@@fiJ??+3zGh1vJ38pKx+-{|^yZ z(os;wu-9ORklcM1V&kZ5G*4Fv;a0Qtx;oKH!7*DjQNyo<%pwu4xOt4dB)`>LWs3~f zyC+l#mFE_#v3mfI_4M)ESQ?LHl-B#A6B z3}beti)bfBGz{!>h$Y80HF^ZLSY7qy;F*YAtLI-zSv1`2yeS<+Q)$;2CvzVw?)xJ& zU_=sC-nkpI|5(94>Ai#%YANItn-B2-#k3jWLj{3X1hv$mNDXy2QRxgI2zN2eA-)_S z5X!=VXK$pjjJvs+Wmo%(x=+bur zU!rr_pEsfuaksO%9H(*%SJvV|+WqZ7DP_*Lt%|2aDK~;S4+SOdS~iwBm}7g)5Dbge z7Qh}qYl*X0E&5Kj)--2X&r)wA78*a?o5}L}r`d*Z1r!gp3<(iow?Y?jBc?AG;6GL@ zM;83#ee!N8N`;Wx)iYq(c=*3o&oTp2SSp6agPD`@n8$o2sK_}|ExqWZzfTu=@Vli1 ziw<%NvRpCMif?_6!u7pQl4^A&0KfSug`>X+pMFZZIDNd-j* zQOts%f2Hq8QC3BJ-tSf}g*A7$;y&Pg=lp4}a`Ah%*6r7+be9FKC$F4IcABxu^PLro zTf%@=T91>kG%~fZb~c%dI0_e#4E#21zq{ z;zrd$ z$-z+yR6AS@$xLqdVvkf#>QN$hk@r5UW%#YurVGP8s3$4x$E~{E6H1Pw(6m?*1=joE zp;RkS%y$jV6bQY;J`@nn6+J3R&T1SZmvNc_7?nMF*>jPMlhvjN!nXSo`bfua^{v1k z#(>JZtzKxXJT(3H=-z`d>+DL(0z{OO?gaeoN?wwu)#&SD>T+RmyYtT%I5|))$0U6z z;?w++finK`bryGAWm1Nc8M@uanfM2Lqs-&ki2xVKSN^^r&53Q*)Q z7#=1HdVzK@nDgA#eVVP%aoW*ki+#ObyN*vd-=1A+HLQUaxry&cef}wwf^owQU=O2lAvD6k)uw!o?(Neg}NmI-4 zmF6EouzfI%sLbZ!?aP5Erizntn={J(?cwd&-9QVO99;FU__0e5<&a-hz$63j8utk| zRl;_wdKDS3?d<{Kw+TSqYe13TQJw$G2{g`Fz)W}-**Zw{5rgM~_JVu5Vb;UAA%V3# zD|6JT4Ll(KoI*@5Exkl(^AwzJ@Fs>qQ~O;NGPbham>cxO;f;-mH3Wl2XfIP^TD{5^UDvIO8yJ=PYFdLlaK`ma-by&cyi zeUlDY4zc#A>p;dGAFM6MJn9?(FkC0b)6*gPcb+^0%%X!MS_A&fzri?5ZgWCkunNx& zqppOz`U?=nouU*dFm5QVF0#R;2{&sPe$5QyTH^*_nfGPhLb(~d0Kl=BS~ftJaG{|c z?&`^OB0!OOvikHU33JR?NA(j!R)tZWl(`^b#6kD+{CxTS{q0b?;Ko zCW&4x&7r`EA#c&GyJhNF25N*eJb7W6OV~a#B~>aj%=QJ+l&L0DZJHB2mFJ7RhQ#bx zV^?MEmsFRW#MQaLV8B@=_MlsyWO)mOArbv&jt1EpJP9@!G*9j;#cNa1Gb#HRM3LLI z=i$2s)?sL1qz>T2q5pT3j=#~3fLnp>f9NZ|bqW!;t`;~Uyi5k5FF86Tqe`NX@&k0S z*$aVP5DTFECmcz(`NRN}D<-X)-uFC`{kfm~-u&6+99H)-6CqspmqU*^P~IN!j4{d# zI^_6;hqvou39XpKwy)yAk1X7Jbn=MIcLaZvHI*>Gy42WX$`&}v=^VPj`w!hH z+@K*5oJt?g9R6cXXD~F2dZcom!bRAw0;-Ycz;AS4W;qjYEjy@didZKpKeG-CCazRO zwt7Ylqp$01`L!1_g{MJU3$Mg#uNT}KH(A=P<_l(`=8P@$fHKMjGB+dwD~lgC0D{Am z7MLe`fuI2TC`6-Gm74VwNuY-fVgs|mrjGp=`rj6y6?CSCSe0HE`6ET?a8x)lxyJQZ zL9ihlr|T}e=jP>uzkoY(5QGlgB9~p&#pz~Vy#$>~5&DO1Md$6#nJfTlwVa&j|8S8g z@bLj@YM+|)+greygNFAg^+OA>{cuACMFi~^mfgxK3*)atWi}WkCO_R2E7{rzHeC?W z)i|$T@C9r=T_ysT9s>dp&|l)efBor0FsNKo#dr8WA0^{12Ks#~@2r2{*L%gicUx#Y z7H;J{0TTBoT5mwpstwyEw5)P^e5Mc*G1W8ynM5U;bidx!ihQAI_&SMJC(6^^GVym> zI^wLz60dO~5!seqDUZ0vrMV$@znFy|6M+;9woPx>lAWN2*&xxsZx32{D@x;>s_j+-5Lcqp)=iRBd`^a(+188al20+zw4J54{TU>IC8|#Ft2^ zH}?h2kvpXsu^`}irEq0SfBe>nOtq*9*3EEf;cr9kw7-NTTjb}r#Qj=dc_p7fG}RUc zZG_GWB82_3uY3GX)S~^D(V^TqLmwHC*!U6v8Ue@>05bmsGC3egOA8-10Cskn7Kj4o zfuI2SlxFoKV0QCDs~3>ixVlGrGKvQ5M}F@SY5p8J{iQdQ8n@dMz>NSxf^$|Z5mf^S z?NXD;1Ia?vw_hU6r&=X9pNt^q92-LSSVe^Vcj!`ysEWus)r!y7F(u=kn?>e+8Ua&b zwTaKR=CPxKqmuWGY6POldK?4@NFL;x(|^fI>4#-loD1OtCjKb*#%*{VEAF4A8WW;R zP$Z{A5j!w6-`Vr#Oun24fyaC!<4VLY%w>Kwa}j@4LJje%kAlj7&w9>BqCoE1h5KB> znnkNTwHl@)P*Q#<8`I2T$Q#LjTMeIO@8eMcUJi4k;x2Jn8^wZS#GNrYA>IBS&=oG# z)>5ED1p-A7k}E|#5T#B*q5+J~b~&6@jgO#Uq zWzi5rBEbeb5uOkHrjl*l;dpUaI{>-QV{rS+gj`Q0vKf`qrS4{BD{Vf}?~vqIyeZ{` z``LuiQmng_dDNhKnevHU#P&;oUoRU|EtgZkl;BaKt<02D)GhdUy9n$!3a)GKE$QmI zm&vp$)D7yJcS&bqMC*#F8G(&_P2y|$xuBj@i0sr2@cMtv5c!KdqGrt*v>3}5#blJV z;~91+ga~eHx`!?t_pYoaQupY*@VWX2TynCgj}#UaeC+y z02%?P5&$y)2q_*Q5;2R9nSeV&^$S1(0s!y;`JWGt$EWulZ%?wzG_L{+A=wIv7fE5`#=SUuApds0DiuuUT`Q?=Jir~ z^-=&8gJay_DheCS55$xFEZed2qCq+BS0e};wd@9Gpqg^~##xp&xi6H9oTu6s0bK23 zIs&vw5794zh{$M@~2YZtQ) zYrLt5*+i;?e|@-u>66aM)6#DzE-7t!beG*5sJa>(hG46cUb%37*}uEW4M}!ER8yW< zg(KV{5kPH9B{k+?s2&*@+5$9E6W#GB=(5h2opXDHL=1M8hw9(F)bOp*Sz+KVZq@0q zAO8LEx`v~P!F|D?HiDVT~U?*Qh2K?LO{g*3BSC5rb>(HfVYj{NB0nR*3`t zT(x`u(KE>$1+xX8zSC>uM1QjQ0TBbKD>wAn;+BHELUqN2mTbS%HG3c=@4!)JLRLX@ zgYBbaDx)8Jg`HG-q{Wsodov{PR~&t(nN}*KlypEES5{K(B_4XPh_Yn$xdxVk|5MDQ zBI{*1ZT4S22j!BBQyLp^fjIq3;lmdy-^9C64*S^x8yt2|m_&2x-IM=s3!?#rTue7& zD}15${1QW7Bjz$VT?x8JTVVItv02l`XE>-p@Y5;UakQNU;ukwImCTY{s(&ot;zFa@{t_qr_1U$G2t5O{SxJ_*2SMS(~Dj znh{4BEP;6+Lai*wrvb;P;yC>bbC&kd$5ry~-_7-ww z>k;{dyf-A|x=VfT%Trgz_FCfbAeWtv0-(Pc)r8y$qgWhD3UqehfczW+8*vJ>o+CoJ zSMknSnFF&5?~xw|tA>Yp+LzPPBGB29`}MGiZ$q*K%AS%vn9c+EdUYAmymKTJY^Or%~-r3CHJnax=u694r8ih8tbZVFL)h^(eelsG@r@nm(uF|njW2? zTdVXn-23#7&Th*mQC~;K@lb?57bWh#`!vT{49q2#qaB_%yHQD&%LNI-L>bZAwVe+C zWNBOhJ++*e?Y%`aQ=+K!1S?dfI?EPTS`Y(9yz=X9PysP}1zzY~#2du^0G+)9d5`yQ zX4O`dIWe;de&8R!eODYSvLJu43o2)|)^x{FcjkJ!zM!FEu^p*)90+hTt)i;KC zO6p(6uiGJ5?umC&p~8x%!XJu67D^N0PJ__;CSBzTaqdo10X`h>l9&WxV4^T$%rEOF z&p>A4Av;EG>9O@1F*uR#n`DEW$j~u?zw->okBcs?QGZKo0QPKZXbw9J#a#Mj0-K#o z2=LyxM)tsvczgaqslogNE@S(rW+n@nSSynG9I(58{tjitBk@^QSrlm)7TU2eq|@zH zr&RM0mpu~Y0M6v{ZGkAAah5){_nyo_2te*>Win9#t?Jyk23Oq9yD`T2`pe@5CK2)em1`rA~Qr0=*N zy%U$&z~zgsRD&i7O@pE4!*}&l8{W)h%Ia58|L$vsv zVgYfA&$$(Z8f}xyUHhYPFx`4BmG(ue6x@tTK#<>2?B{iC+sI)Tm!E%SEC+TcI6Avc zv@7tS75^a#1&VFTEfW>A`fipao5H!YCIb)Wb%ZkAfOa$HsPJs0f4YeC>I@<60?ikj zv(ffhc;(9;JbP}<

    t5tuhF?!ItJpp8bye8YB#|fG3q&Jr1BV^Tl5CM7%AZL?2C! ztNK2{2C6Z%S~&VsAk0lR(XL!wabqf|vg^%W!WI1QEXTxw0d$1Z>C#mapG|*(_T=tO zn|7I3z4KK$D?*gmXW~dV6dE|8Ko@lw~M7 zO9HkMiV`s94fW8EVkOXxE4K}su-8l0J)q0KGWDHk$tI15yPYN#TJ>WVq}5!gkv42v z+7G3T32v$sR7XK8=eOe7Wu*-KnDO8EuR@0a6$)B8erIuwN(24GY_6gklhV62_cj{C z`VXN4d^_WrcV`-Djmr&fkmK>IN&;zc_PufPtP#q_n8(oQ5zA`ZKOA(to~GkG-j~Po z85m#^05TOaK4=6ZHv`R3003A301yD#o1I}m5&$y)Ga)4~f)g6wlAs*{q>9iHF#rHD zxNt%Z8@3;`0O0}&;y2w4W0>Zj-BmB1FXZlG!#W_{@o=Y|zwD!(qpv%cj!47yNoq)+ zDIhTjuqsQ*hr?8*1D~Xiq}hiGxg7oZECo`(KFA`_I#ntYfaqG31+Gg$>U3*k=ser) zF_zg-cR={oY%be3NUc!0t`mVsjre&;s5tfKv&Ojs7Y^@!{nWIu4i?0BiaVmgMikUm3FD(6voV)Z>DykG)R)bqPjUpHK$2f^^a^a9! zcn5g_^X=C;5>4(#^r9IHP)3^fHPO%8!LA-C7f9z;D(hYDoY;J?3_K|73n#z+s2Y-v z?U6=BxRS^g$NJATDqs*Em{88|J0LTO`v${O>E3V*>Uw9|TI})13eP6UQc#s0e2mfy zZ;~xELzNrJ?_ipPWcLe8hBv{*UL6vxBv_EIFL35GsXsL~F4ytOzRuME^62EG;$RDt zrG8Jog0!q~;rXQ5^HsQGSwr~@`CJ3tCxW$HHI`OIM=PT|Rf$SQ4OGHPFnBGbpW4N2mcNl!P z;h(RD4+qm~@t5@e6*&sGD<^YMc>VhKFo~pyP;g3pz8m%KCm_eMU-9dycAiCl=qjEG zV5A+3nKEdh%%OSw)^^oToS|~>g8l0G&$s$*!*PUOK&#jS6>s%bgWn-{TLjtPQW5eE zumP5_nmnY{nE?IXYlD<^u~~GFc-yYjp7ERrl1P!DJ*Z|SDfjkYbQfZ5Ki(wXcis@L zcFr>McpN$O&hqM@Al9=wVrvoz2 zgouam*l%$sN&TODST>m$zT;KUFR(}c-5Lco|Ly54V-(!^Ut}>P?yCVn`tj7+07=l< z3(D;-Nn}RHISMTvR(;Hz>QtrD=;TXfG1NZ6D-aY*3Y31wV}d`9tT{baGw{4@!kJp31WF9FfN>!5ka;Y?r_$3xbm9J9950pY)WsUtEpx_oG`-7LO?ESNEp^?SLPo8RyX_GAe=#LO=jO=u717#A{s=i znDESdpHMNQi3RkzT7#Q?P=oyiw2FJd7Ag#aQo*9Zc^4=^@7^qnOOeD2XO^HUKYZ>#vGUWDd9TQTQmdi0PK z0a6`$6z*iIXZS$aVt@1>OzQOMC%>&}h)uPChZcm#kxi`w3z0HeE#zaweI>D$7p>=E z!4aArhn;1X6=ePUKFU}pQD<|{3s)WuI_kH-sKUKX&FJQiEJ1a_k{S78Hlhyn%WH`q zdUsX;E?<*cp>Xuf55;nGG5|VC}?XC2d99Le2g%M_n47em~LXA|9wtYQ<(jo60@~-kH z>i7&@|LLHSldu1w>R0T-ovu07q_2y=Sn(Rt9#O2RA;bXhw5#S@w7;<Lb23|lUHKcosw(ZR!vzf@bCp^B67$4=>i4EZ_5wX@R#bNG5 z+iO6GXW0)jJY9>W!wG9UhPXlS_#-ea(et1-kcV0Jkm=XgHZuQi6P5)HNHV+Ed@Hfv zVvGq@AES!G{%Nra+0@BrsMyU<1w-zGnCTrn`5ILHN-w-jDobfNWF6(UE$;U?NVLF5{A5tV9F%vqcZ z6ifI2Djk{Gxy2lh3(%{6U6}l8#IF#Ea>aZZ-G|4a^RqBMZ?i>X|i{7@t z8`dN_m3l4aRigc>B3N9aHS=3Xl`T>3pCMZ)m~!!L7=I7s|F7R$BHgOtOmax{u%G%# z6k;3o1AvCUP@1f(DZ9<^m!K5<2*}btW+G}}2Bxk9e?uL2Yn;>K0GOPmF##7$o9(5( zcqrX{+y|QuE>rePePZUFDFapduFYM4u-BCJpX698D1EX&?Y^jIBtJIlQ@ot>+%W}7Y=45TljZH3eFrNLW`=a=NPl6`!M`sVrQ3r|Lru&uf`*j}z2_yZ-mlt9dyx(3I zSXu&XBE1g7xmbTm+QF81*plHl%s@yn41b_!X5@ZfOQr(_mP?mb7uPMzJZ02_d^fG0 zH|;o+h!(e$0l+sn=n!)lC}QuB9;I*k^`h#{e`mme^#tyJJi zIyoCxt$uQwA_ZSJ$4rGQg?JP*q~8BjXp|URXZ&O((hId(P7?@Tf(SprrB2FbeB%uD z*ncfE=Wj$cunUG_V>fCxREC=pF|R(XZSr+JKf>FXAR zWnjvcj}_Oe z>joa`MW$8ZkE*?$dP+9(Obv$y_zk^>5q>NrkC3P(p!)uukbT;81r^^;^*{NI6&F*! z9!NQAq*ez3>Qw+uhs@@CfBJ^ian&mw9w@u;>}{@yLv@>>07{EQFxII1U4%~VZ!|ej zFxu829-^V_?9*0(74nfqYGXtpIDk2r^-xK0zQdmnOZ=1NpfLyt8jP}7~1F)s94DHk!=50{{kcM+D(763eJ0-h_5DLCk zZJ|BVn=`(Gv<32Zv!=^duaM%Wl`uhPx<4L(v2Y!cYJ94insxvNaz`r|F<)}^kFGHe zO6y+RI$oXP45Zz+JOyl+!}=g=GNU`iIoS05$QIIV^(QYjwdwppcvYiva9m6x6n9pw z>UdUL-}D~I=n2V|l_LAE3*QT`k!NHA1F{1y-V9{||~L@q}_6~khXcI41F&4qN@ zMo^}8U3fz(M1MDvek6d6;J?G<-2aHufy+$1Xk0LiWDdt)Xwkdn4sgLVDumCfdZVL! z51b}Ds(P`$q$O@>ekN@4y~A5K-9psgr2{OGX>m~&es-^Ob4Z}u^S@PM8~1qeWuuFc zfK3Z)_d>s|d%deVMrEqhuaKkIQTW|Q&5J0a#@zn9#w}FS!IRx$rNg5tQU*Y70#gNR zEM0d5*a>{PoovH&b@@&&U{;Dl^a+D2(v2TRi8zFPWZb^}VUKI2?^2bejyB4Tvd=g$ z^=Qxs>`2|9K7+OrFm`#|%f@5~lhH25%duzmFyH{KQ={QrDGDN(3`FP2ovAi1QPxezhOf z_r&^s5jY4!2kee@t~4+dh_5_BTw@-6qEvhW{S?c_%NHl-m)b&J=Hk=vi4TwcpXv57 zu^O`QuyyV0H2E5+LTFTdtTmW@YpOaqe2R3Quw8M>G^crssO&jmyS_2$<$PJ`m!&6H z`SL{qaAFNCe6^3e%LccafdG28+34w?mqQ8GG`}7wEHaXEG1(AbC*^OdVi+Ns^lTjN z2}L|IC+go_ImnM2Do@YoetkN2@<-ph8=gcu#o7lEf!a$gC!hkoyL^ zu9AEkpb(dJys^lj)k?9d&Hj`S0HBoPJwokxE$z#4b;rJN-Zn|U*->g1>LmerBu7PkA6EJ z0(=AqY1a8YPHUtL&|!;GKBfK7r-O zw@fE_mKF?N7EuFh;36j9Gl!ldxcEBa5=UU?i3Xc`$_-axskbVERh1ZsD!UsLZ@o+EdQ7&;^HcMnva~X;bnp({y6@!IyM(I~H{24)F ziNO<`lvq%s9|MmK8*{bvbF)~h9Tr5~N0k}*{0OUweBQK{><;*f)xvJ^4}RPz$f7Yd z;>IvA!)Ie#r>z}iRf@7QJVJMrYaz~$X`mj$N?dU>yH1pLs6T;&cL4PU8T)1|~9DfL&l>h$~Nn2F`- zXZIKUgGW5LxE)gw8y@83Ek9ujPw?E@VuCOB1!cUeU2>zO`$B#oKT-_0Da$SdrmP5{ z5zf$zJe=U(f0TBU$(s1_EzB-=QklP*NJuR+uRFCYUHTB!LB7tlt&nX=iAh+rJ=Aq& z!YgwDJw~r8AXTDtn{-8D+Yl_#MJIP@3M;1>w`M+KEM^T=AZ8!N)J0vgI-a;nV!;Vz zZ!)f2eico7!Ki5~NUcF@N+`8?l*fu!dC^x6m+BN}Ir7h-V=unc;l^mvyB~7DYuEsq z5Zt$cG^wsE*HSldima9f6pTmT5!=~dptk&nz9Hn@ps|=BQO4WT_(rtKR^E~ti}~Aa zBS&@;P3$fjExs~>m8lPS5 z>oUY+7p20raRzXM|7HRa&eC`kd)tK33#2JP7gopCglLRm$5qkYT{Tj78qmP z6N%qIv6izCX@KHx1j-(Vkn%@#yA>QPAx zmcu?GKjr_2a;{MyM0<{PM5vi=Zkjitq5QKcMYjHE8=G3y@}8fB=JKH$hJ1R<-Ju&J z;slM#$&2mYg~j*|hR)jkj!@E2LYy+JYrX*nn0eRD7UA*wHm3>!G?aaiBSI4PEBwOC zIHzE~%AI?;vl3KF6K)eBGp9zWadRqw9kFmXCG%CJ{8o?fRI{k_a<46)oxfb;j{Es? zm;iSRWXk7n54z9ZEIs~Lhx}pEb<}iGg}(&r0%`&kSg5*V=Mf8z0Y8a}PPDd2Tn5L`D=ZGK#ca$i$LSsm#}UC9^Jxki zbSgm=dIm)GB@;eff2PZ6icyy>jpQou(1x%q*LZ& zGEi-F+h4e;B9g}=BS~=aR1j4W2$4xK4(=p0+_dCEY zG6p%*uA_N8Z@kG4TKy%hmo&oE`C69%#uWbv}YfQEJ0Z-JH5A~-9%f6e}@eciXZF@Wpy)iJU2_|IaAwquT0{|wGM2ATz z0bf{=YnRI3+I;LoZDV4~+NO;Ch3W@@L2e;c{%B1cKWu)%^a2 zgeOx%(biFP*o6xi@de=?VBkh;w3j|fGrAjV(D$P6Y4W8i{ZAAP^B0t0C)6ePwJEQq z6H`|uUgJWDEP-X9@_tx!)_oSryt1B>8mCTmB+37}CGf?Rxy*W_sS;qr(>xxCVjd&QK=6x~W4RWYN%Y6ecZR)2H z7Yn2n?XXdY`6PNuatQ5{)65Yefx_N42e-wm>Os8yv8<%FIh71}LWk^Ic(p^g>sEYi zsg{1cry-UnTbcYXi#P1xg%KC4YkOy#eJT!2Y1pKNTK-|V&`jhhGaI4y=bMDWu#MDl6b@h; z!IJrxxnJQ4rqNe`SQNIDzWB3rrlCKGBRqjw32Ohm-Y4AGhyApg0lI;CNO-iNcdP~j)x*v? zA{l<*^0~RWMJ;O}YY@u+mMCp9muwzj;iUZpV#YZs=D~~<`p$%3_U+*=HrHhcwlyk% z4sLaAlCy9j)>Zhl$YQQXIqYv*iNlwL2#@L@Mc0>Jue`cz=Q+Z!zSsE@cHUC!MiXH* znzyej$tr@{krbI!qxFlr13) z*JJ_E#RggWf#O%7qCCG~XdL1pQS|vRexY5WR;Q)aBt+CSW?J)6Y(L?X$~KZZY43ll zg{0-uR_ciKG2gWVpmUE>XZggh1(TDFw*z5a=;^B@VNF4#uF9F&6T(&B;-+8G!G)@h+BvDr%9w?k~`R zL=`KZN&(0kkKGav4oK*Au1sWk?2K7|cr+{JG6TVupRumOqIvg3q4L$mbfOxYlYE@b zKN}LSl&ZY5ZP(TEkSc;_kgK!xfy0VZZFP$M(wvO!A?q2y#smV;?zXt+xV%@k0j;ci zD>qYZ=wf18_nCw_&@Yb>{RX>7GMrUSa=4cUV=mAfGPDdSAp!yu8qb*^Nt7!KL<8e6 z=m4>^1vP}RR-~Uxl~m2G%-3?}@Kj~aZyo?y-rK!j$+!AlFB@6Ns?9wF>2a+G=JMkRuyUAF z*VmB@d0dl7Z4D2srhDh@cvqk*RK-Fd5p@0UhSC)HUt;D#>QHU);Nb0W^|r_2$Jw-p zV3^R_6G@w1s#M<9vL^E!YJ|YI>U9WRD_0JG@A0v*Xx3x;P|fbF#eL1entFRUwV}kJ zR;cdq$UVKCe;zcFGOQTisX6fj3Ronmg)`E zX7HdcVtx7syHK=hgV59!%wSOy@tY?2A5g>^qtOcXFBbB}1*8 zDFeM^R^GcPaYJ<@Pq4=-$>Oa8Z>OAh-p*5BcJI&fpc!&tit*lDtKJ7uY6f^s*#9b_W?{!HC8$k>(rq8_1Qn&<<5eY-{J=HcEp#5v^ zs1AzkKzI5b!FWJ>IA2^_*2`F$Nn4@3Q;`e4O)T|uQE<3Q0_gnL>zI=ZXe+6}Xc8TM zMK5eIK~VADLKaGi44!A>|&IeNhyc~0vK-U6pt zjU=_xXoCLllg7uJ_9At$`w0r1+5upL$FFYi_=rk0s8Z zIwyANwukutL2lGREiIk;)l3|&Vx0@_dcBpXZC`<_HX!Y0nm-pIbMljdHIMdG zE4WHq-WA`Ix45S>xobH9oY^u9B)ZjL;VN!L)OO#W@Za|mp{vR+Z{4V*xWU6DIgOr`qm@#bZk#K?BUb50p;F(5gw z1yhqOSjgB--9PJdO+1n2>QE4E$eovV!RhHYR@$|%i4=4$<1U)Y;kav(8e_4eSLxtr zHwm+%!L}z$-CylCO~kF%5@bZX8bDlWmR@@&NcSnlPoILEizkBLB}Y?LeUi3e!$ZVe zgw{Usew{HrdBbr$n>>0`#ajOK9qX4<>K})dCGX%5sY|T)U|a%rrPa-^ z{<7uTQMwOO{p!iY1Y>aCXv(A^sSGLw4CG%%tfkWBKtC6+WA-Os7p+`J+@$6Pw7%t8 z2BCkbZ;WCQdR1~Jzt=_=CbP#z1YBIt-qNr~PF`jbO*|2M-V7J_(1U+ZBxl3EK$bf{ z;D0t&z-ej1vtl7D4UCGJby^J6R$C6vvNPocUP3}Xa7TZr#ia8 z_(6Zs_M=nr5&U^K<2cL=aREn2fI=GOMQq)y78L{zgS~82favLx!0W&7#|?XjYtZ&b zxL8Y^D8luoLu3Y*&5({pbwp8X$z(2sr%nWtvIv3trnluKs9dwOj>2r}gr()J17>{3 z8I2iAHayMhWGU<3eyg;bB%~m9Ac{ccdq;=t@W|+0?1Vi2X&auVkuj{5ANUniXd8<^ zm+L*m#dje6oP?%rm!Vp^=+QY1uMdTYA+)odP*3I{-U*DfBni$bi9o(J2pi9(=Z-Ya zxlsv`l|n|)7HsBTwiQhP6vm!QA_>;#`-2GtynMOWu8RJSCG=|sRvOu1KTCeTQ_{-g z1uZXy3#bKVSKk6F#1R2&IV9Bu@Q{PPB8!iPX<0GOaTavzj@9l~S*N8t>)!|*q)eg= zj1&BDNZ4wTE83?>cq*#A7U__JpP}qP>O|?@B&c99OzCdPYFAcrL`D$ zQfAi@i!;8=i$CWEm@voP_6aw;c}2=4hiqlwopU%WEC(sRs?A(N8+h5w@%CX zSEW5eaOY^`wms>Df`7@GnCq+-MrfybP0E-_%oU>68$K7E-MuO?Q;My#*tM!z_BCbz>b4YO+DNNOUa^jyQ{~h zDZ4Kh318AVF*rL~q4L4bDAy9~4mX%ygrdlCFeTyV@S_b@iFVjbj-e~nn|kDGv(G$~ z`Ny{B4A@a+-6EYMMpVlFI?!A@D?zS|eC)26#FI`^XyzyS^A9_-%sLuf^r)YC~BbP#Rc#vg}~~Nt=o%X-l%TZ#1G47787TB-fvNM?vOqF|kOG>N(J)7i^#bhyLou{Yp5qnfMb{*jt8)wj5? zP(S&XaSJ!JuZc(1his!aG5yc_lJBI|8t`2jJBAz(S0Q~?n=Z8EFVTH4{9y7$zu@SJ zNJM>c4zlC)H>RGi3%%RlP_P?^&f3(1XP8F#5V#4o1ZCs*`Ot+5Kf^=ojiHcA0WP8L z;s_D|8UdIR05bjqGC?4N78Rd0K#~KD78n2?0Qtx2=jM~r-M7&q6KY(x+XZ~6x3 zW@p^ztjK`De;JCuhWoE6gyW!EdF8=Y1vAT>^9U;noOd~!g@TbVuPW3=#V5k`jFWJ$ z_Q8K5)&kd#1`{(MVNuLD^LY)FYVIU+1-vA9jyf2pdpcs*$NKC>v;079Ps^Dx3A(9% zEm97WtednemLowvebW=b=hJa?3xp%=66^D?3+5ToP6(Z1^B@pC3V%`^OLyiy%|S|L zI;VrRwyFrPb-@t5MD)BsKm);WAO-my2WX&Kc;pSNA1|x%+uNHWy&OSsWN0YfjQ^X8 zc`n-w`=B=ru9etr-*l)6$iN{D7Yi0DZxD zqKe_i>zbY88YF5rjAxUT*4m@*N1XmQ@e&a|S<-AV<=zsHc<99IAiV7F2kc!QH#^NgkBn}l1VUgY4ov&K7Z8GT6_;D7_k z3V;#-8Uff605b0iDI);|M+ncEAV*>g3q%34VE6!7k=uDeg5XUjmdFI$`E=dv8jL^d z|2N`BSqe(2S6_pTe`}T%@_*;qg~VDD!&rJBGVFJEgYWNp{_ zxPd>Vv*iQzAHDU8FS>89w5%d&;9eQX(0zGM*7iI$r30T?*(>#XyeR0#S@`5tY(INl zw%r@9Y)>fV<8|#moWOC)_Fo_`d%|oh3I{GY)q>9v>|k3sCEenHP?t%#M|YpIs9o~U@XJN?COh*H@1KI)TkA^2 zMp`03*lsn?oFRGJf99Q>a^pd{*j!ISFj045#}j)__44-6wl3$hu~LcIaZC8A;n)9$>d|2$aqg)XxV-EMx+e)J zQ4n`02B!h7e0l7sX)IoIU35FTBhqNS5GZA)9f*TfO;u~pt6$UhEhhC5Hp1{iZJlK< zJT0WKmU{gdtj_|covKK2gjN7e1GTZGK-hz3U?XKYq1b`Wxl9NSWq=)pyO^ex=fBwR zoeBFqQuzPx$BQ5o%rT}&%Z`hnidYg@FY?#?6{?tVV4?7>HLhWn7%cAcXOu16Ug`?{ zLj|$9;E@97yV;}|7>jsrwRj0hfokKFBW6lG9xFlyNAklwu>T#{4za`!ez?&D53!#g z#<2#@HO40xu}*uE(eo%tY+vHomZKi$R+9?X|35+tJrb&u@F#<>o?2f5Y5Fz zY=Y=}$ZU$~e?*LIW6SVXU2o_}^|vVzu+;M8-uGE~!yxFF=dkvYdXg$I7CvRe7t1{N z*A!g+`*B%>-GO(YpOG%tq6*HiQ#c1%rYqVxh;>h2N@3HEJcw!6#Oq(Q)WT|uek4v+(m2WLs{7gT zdEg15Q{dXO;rg;quYVVAw?I-1rg>@x3H(&H*S{S(X`fVLaBb4~n@a;uv3T~kzS6`n z%H?C9?*46Rmb=8j@?WLK7FP(rq%V^$7tq&(vEnf?(n^|o$b&fNv0R`LOJ>$xOdPKJ~&YPy4zK){4isFRN6!I;Z2l{80ds#J~NWY;=x$dsRuBD83chC86zEu z2}5Mq&jsK^=gx~Fra5QcC7JnEO)dJn98Xxx?Q3dcw$7SY#{9jf3ZP7`AsX&$8{*7gJN=o1PlkA&-hSUx&c^+WwsFmf(Og zi3cenAQeXl&zT@M32X~Q0k9D00Nm1g2(n_>7L~2)c6&eq_mtYQ1ny!R_TN~A`go=6 z8E4$Ta5};C!Wv|b#rCT#jb`=>*NPU0j77<-9?=f@B}Z>Me(P&m56t%T)DyseJVEY| zmROB{JDvaoMqo3yWd3_9ek)oDzlH`HxJ8#rV}8O|CDwGQ^Xo$A2MdH9CyS#xgSw{d zMic4s?4CIwq8%eXBuIxe??v#47C(TzCb82pIKTuoTq2$V5K4lXccECz2+<5mP{SLc zWsn`wjaJoYP|@<0l_DRJnPu30zoI>ZiyY>e{2O`#ct>oObPwTM?YH}U-GlIJb0ID0 zXp?5@*zjiS!1g64ds+Nz^+8Fq5|y6HGv137waex_Z9dKeXj16So^JhSSrhTL`qji& zw`9!WYpH|sOE$(op%yq@$tT70A1_56EP!MoCx)fz!0k|wY-GKxrXJe-R*|L>w)YaV z;B{tjd508K3xJ#(Nq+(xXi6}6y7&PM$0K#|R5T~#+ zuhP=^^>&^D#{D9~EMk_}N7NB_P~Sy9iP6Pw$g_AU1(j#a!CKcrPUz1}O?>e?;Pq4Po=>V18$A*`Vj{plR zXYl#t3I1sAZ%ja+f6u5nztJ0e9RPrtO6WN+#55vdz;l;XL)E$8XRAMgxU&+rc(N6@`%ji8DyfaDZXJp~^u~@7UI0Jv~ zS(e+McMSotpTf&(C&Rx2`~P;R`i4!c|pyuU4e|D8Uo-knual3FI`lrwcs~G zyNa-@>56wUss9pw5fgDKE09Q^DBLXp#Pe-5Ymi7Uu zmuyirUYgx>QrcqK-fkr+>nq@t;yt(-Uz1(uAV zBooI~cUqZVi8Aqn{R{%mcrbE~iG2#aX8|d2_JHIrUM52d3Y<&wLhRbuV2q%}*PHxw zne~L|^r?~mEVK5?t@?U|3WaQ;%!hL`vWpVAWDuApEW>YQArjECLI8PgxhM{W;_#+ zs#x=klQ(=fk-?~(j*EA%elNJ6TOV9gpHRjG1OI>3YZ;etqPfLdv>X}8PmOGjue|Gk zfmT&kw8t;%qt}Ews2F*l!uUBKKd4J_727WF-aVsjqm%hMJM*TiamD{~CbE^C z-!)lqnW6s{$>wnN z``v-3SC|u3C)b-Eu*#L?FQtrSqS5iw`G?0ZNyB$vKO5q*rUaVSm(QpwZzt?)lf?{F zaOCFK~u|DV;QEIxEf&fc)= zE-uQ--FU?NNDAQ(ReE?%*-eYy`l5_H{u8DjzCGc)JL_w}OsKaCy2vNZ~z@3A5(}$jvKQnr$(el;Ia684OlQ(4LV$(^mUK;djyFXsTq0k+~epKFQ|2O<577kP|Dy- z>U-g-9`*H7256pE`|>_@$j?Oi)hUS8Pb6TTCZteDyWdf`hwrPEnvc?TphTDPnz;N} z@Z8d~rZ}nl{~asUZL6qS)246rbNE>(;1K=-Ekc4-k^+o|n>qf^Wx0qAV2)fIMq=Kx z*UqWonBDOhwxNE3|HG!MPx0t1KZu?+`;sd0izJ^lHy&JijbeEw;^-OxB0PGdKm`2)g z;0`)CxR1m^F5LU^a9=B%GFz-rCz{$=7XCSZxB}#So9N@lS01C)HKDCDl_PlOt%m&+ z3W7Jz_ipuXW>{MPey8e<*0U1eXQNavM4l5n{rVaNp=Fokq23cdlmc;|m(MV_xM!NY zhSO<3$T5%d!$+ArZ+ZSf;Z>bwPSqwXPN@n$)BEnc?`-x_TRyd^(WvG~{nGRy}lBp^hiBF~v1MM+2tzafAC0M0Pz0J*8ub2ffDK%aGM zNHMb8-}~ba*x!3NUCl}=?+R$j@5Ewm8L|~ZEQcYfq(q)JunTjF8M=>Dj75^C)C`K)WO@&Hsl+b9KPS z-cP+RLtp8(++dDo+OI~pKcHdKvhe(l!)0&MQf&ZNWscX!XxcV}>Ny;YAnHEyfz{x#tOl)-N;z3E zIwjfMF`*z&6eT_FuC}c~vyp6U*M*8unRkyuk&_Yn03(A{8FL*(vrwkVI)toD)4IDz z3$E#jH5<*rgD2>J2{}o#Z(twag!zaATqUr?KlI7uH+7?I-qW*kB;H6j?!g1IoI#2p zO$Xav4kFE7;M^x}Be% z&PluA_t})K=OUy_PG++rfcSDEEg3Im>73US%j*`JLA?BjFC$K6h9-q5{BpiOg|QG_ zQU2HL;uL8mZG0GrWHKh(qkNWf9$SCS;~oh>m~|96kokngOsoziEa-(1-^?g;lo8zD zW}M%@-x(XqT5GWb-q61}obBrXC^2%5laaVEZ|o<+IXVZ6m6yX#tj3XL`TOoFe#BaJ1<0Jo%2jyWo&=G>V{ z$nY$1PZ(2H>mOhgfsg~ zkBopaodhyRASp&gpEW>{^Ar{k03HDPWvfEi!pB@oC&R_HNg7ruEP`8f*%g>K)+jh; zwZN%%oGW)XJ3Dao2Qic)x%lw(Ip95aP948XQA=*51#=s~dom3Uf&~Mf%cy&0*BAzi zj;d<1)xS3DKog<~dSPypdHd8k<*Inj!+$%-cas=r|L8?DC2udDU$(%3T=@`^9WcQ^ z+>QgZgL;M)|8ZM;RdBV^s_`3zEYi={_sRE+^ANj;k54f<_893@^N~c`A@vY#+{8Rs zkwpMMP!mp4FX+Uo9ikv_9IJP%SeiBz{%%yf&38EeKd#a3DHv%Gt*U(4b^O`TH93^sT4K`PnchsmOmjFC;k5ZdM>G2&#L|D_uPxqF(h1SX}QH9%&=hZYzB z9su>PJ@g#n{`^|pW(KV{@?|g1wo~!=%Lyx#3H*0~VF!7sQYPZt7|)ug1)0^E&L=8u zcj{wJF*k_s;^gEKgz;Ve;`b&5Zl3a-d+Rd)jJlh=VG(xPVh`7q+EsdH?i1*3tKwhg z#}wv)bpfA&0aPEWXR59w4HT4;IaR(l`JO`o4KTb_Ko<`y_Q9uubDqC7Km3sg(n_~2 zB?g_d%bVPy(UZDQKfw<76E^7sbfOM-+jmynv)c>?y_F&r;9e3vIH;E^2ULmc(&DIr zk5pbmghRazvk%p^wv4p+GeWy_EN@M%nN%9Zf)E{-eN8O%i#eb?&$VI6LbnTFJD{4& zbz^$m12PX}j?0nJ6DRP`Cr7Aq?P=~tMg%BjL?6hcV-S_sU41p1skayc)UA?OcnV{G zR5C1*Q{96?-lWk;hn*qIpmJOJO2y&egHq;e3r;SfeW(9Ig~UJE)<8a*pPRn58jBRp z;gShLM)nCC_6eN;4vf_yoUAfer67K${ZFu0(%J9cSy~Vd34PGLw=>E9%5B^rz|^VM z#Qu)p$hJ=J8jC+Q6zuaIn;x}HhW}Eam z)y2<=&mW0rTBP~XEP=`h0&42jZjOxJO5MB1S|7jvVu{%@f$E`^1FwjRzhsVS6j@j{ zy{u}1*k}B9S;iiq^h9)&>$fwwQ1rPmKO!r2rH2qh*IC;r%A0a zo6%?sJH0hS;m|%{x9PtWv3Q$~Uyc81ZGDNrtSUBRZMnQvPR_Wu@GX#Q_Ma zI0V~sl24Ua)94pX3|*tbuwwQFwM7oNNw1V4OpC0DE997RU&(XUS%vuCW>>3rDY-{k zN@uK4q?`=5_&N&}q1}-NRWN;6^$O#36P1j%=Nb7!!;ne}u9u>#igK?lYE#W5z2Mcp zW@}e;#JjKSv!4a)or6<|DPbk{BxRgCnV+@XkO$~5Go9L!HE!;B3=L;8ELL|X-J~W*qWRV)&h>X*Qv0B_ zIfTcNm~qIHy$uiIJ&WdwNAEP}(TK__sIttt6L5Lg(TEu$9kr%U%qpp#0XAUAL2jS) zW)7sm$_)E{YkaAB;%sw#WWP~s^3E_UDqMJx3E1-3E_G315LFz@$F zoQzgWUFPIgRwOvqyJplu=@|ShH+MBZf6`e^L)|%7@}PMwowH2_E6C&BjE6TH7YG-C_13ojstg$D-5?w_RSY$ak0+ogJ+Hxu!5uJVJ%iWQYsLP z-eQ0yfsU`@CMJ$RGi=owmK?whgkMR86evM7{}6GDVI#x-p6>_XynKsTM*HBphJ7at5o) zl^SIi&>{%rb<_ju>b#_^AuCx$srK`Cpu*(P4i~^JDdH~=c0J2u2UxatP4gHxsu5pe z1q#nmrAY)O54yGPMowO4v?nnjsynkSxKf(Ur| zJ1!&(K`5NUP3`YU8*`4WxoVM8=_uG=;r0^CAhKP8z(9APZfvHR852O>5jQrQVI)jq zP8ICful*w5@d%`4Yv;vb=+4a77%Go>S|2rRWN6N2!CQ7*uHCnKb=K|KZO{hf-p2s! z!S(uOSh};pz`7eIqaxEby{Vid`Ciq#I3=4ajvESq*Y4K*DOCJl0au#gkPXjSfjA2` zO$b(HB(oGee-s8d1;P;qCj7-$m#*xUv?xrI2^PJhIMEHAhMC8+;S!`Fl1mmQqSB?| zQePg(@JoeQc3XLE{a&@zSHOmDZU?c*BDI>jYbM{1%qXcllqL{x+kR_@Ni(;G3cI|& zB;|U_aSoi{=`#&wq(hyp4E({i*v@RVVJolKC1fWTU$j^Xl4Ec_Hd?m7BUg=0qF|yC zBYV2=C=N~ESay+-Aa7{Q^hvjqm7xJxG7E=fDm6M4Osp7PizMxHUf_Gv37oCslsSr~jwgYu>TcyCt?y^wG%{Ufzbdc-Bg&($ zH1<881OyguH};D_Qn~8prMEQf%H_Yvpi{`bbh4^rz#J~blpnTm!Fhrlun`e*p7d*M zu%0)B8UyD_p|uYyQ5Qua-+H|IpEn_Cb4-0m{O$@A37PKelR_r6i=t(Q;wbPjvF2*L zjm+wy@T-l+TJ8^kpkv=u%ADOZTn$82E=hNN)P>HYcdwdQeA6@Ec%JpKFZDj?efJ-? zvPI_YJ4(-AT`kv8njEAsx9F$UN2_oAmmYv6I_10@(e&&eyu43Op~-j|mUd7H>l$?w zG=I@F9)yDX;zf2! zCA)PYB{BX>d1?(H(TBl<&GtuugH~8k6Gr#fWGMVVE47SFB!0bQmP*+D@*ya-4aLo~ zcl81+hx_%U74X``6PuK3s=)xp0doSF$#@i4}b@am_26AUFCf#x`x0)S@f4hf`E8 zNw#V-==jC^nx10bF1m^FIaLB+Ed9qlxeJ89`5=lJz|V-ecOeCN(f;_bQ&*X)b981^x`HF(*o`l$Qt= z5=bV@)hix#iz*vQSUN>!4X5xO2{S}C%GDFIT(WD{co|vJ=!;lAa?eYMv zoQK?_c@!;vd~(%ohGrcdFK|baLM#UTe8WjHCOvZqiDu$gd5aiRdQy6N$gJwdy_#F3 z-xDUkgxdjoJxl3tVUR;5D3!1x{bwsQrsP(Ax=I@tuL)4J51P6F$2zjb$jB(DimCBH zsAhrXF-KSI*U5to_Ouy=u>CHj%OkdbNQI0|FDXq(OkA47Cnh5RLi&)HNZ3sm?&se5 zZ<=N?iVxx5gDb~5MgRkXdb{}ZqN36uQB6KTG_inyUMM+S6 zYoj)eHSp+m7~zbnp~*^ovDF;wAaDOv>s4rZ5~&F+QgF8sZcwQa2Wl5@TV`wp_^6y{ zJb#j&;$pA!Qq~3GOyM{NXL}Lb!0aJ;_Or#@9k%j>+7FvS0+yW}+-zqEIJ|eY^x{xJ z$drvOTfmg_BNSmvRm~299gNM#PAv{~JymsXI~sgsGIEz!u!$;iHs=l1iIoA*R@AnX zvbR|7wOsnJpz7i2?xS0i_cT(J+q-GDcC2_Ypz{@Omzdd?vpr`TeAH%QH0=7NYiC>~ zj)@FC#K*qJ!%K!s#bke7t_X27kB^1rXi8aFu|y@WvsSH$Wo+7`m7VJyC^tHIh!6m; zsR!CDBt7F$nJn&Amz}s!$a3lQXT9qx#`RV`r8$3GaZKP=gtKa9TELpPwcx0RpxF;! zMO}6!TrlW>Ud%vGd`ft@w6X#nU&TZ~vhrMqKe3{;O& z;s#45UHLz5bP-*KZ6~E9RO1Zjor`FmI>KuEnP}?Yz~3=;2VTJ8MuItW6AG`4huSH? z-U>OGggvFEF}_ME=CkyM@ydXwk%+=+Fkhjc2N2(0pGSxk}Y|;{A>V={E_laWwx`^ zQIa&V^*(}A5mjBI#TW}NwYG92l%RC_=Dp=tqQNjn2g~i@a!}0NYIx!hqm{p2cOnd$ zZU^`L!m$2pXBX})Y4!>Usq1xHoN-h3sgJ)+r0^$N_0y#$w{@>1m|euJJQ$LQ20LgE-@Na&}j z&+N?fmNcF))<^V6FO~Q@%uO#6y^d<4aV`ktxpHfq=7h?w9a&xQ7Y9rxMd=ceawO}a z2H;`iE2PQ7k&KnB9oBSYdMQyJypDQFy6Gdu1}F@-{+fahk3Qk5imNFT;|A!90v?@& z262b8N!~4u$EN)^6RjaE6<~x!%P$RAZw*=&G(H7XeF7~9V*w$QYOgOa)-bC$pDrt> zZ8FEkPw?4L7h95%3tkKwlK>35Wb@-|IFF}dFn*_Ot-H8H-^#l29jSm+vz^Yew3Xm{ zKy`5ju&fBxj2C>6q4tYt%XpA5^zDLr&#nQc75lEEbg@vLEu{b!o zchxt+@6big=#Kx_o&DV^+x%8?v~VH1`6ISWC6A7wzdJ5_U zYrKdSKk1T$_mX#qK(jUY%T6aPm=o<1ZUF9qJXrW-_Qv7TnJa-9UH@V4&HZ%ah#A5& zegF#i{2j@%tR&D7{D{(JF@3CMbh&d#MhxI_F{W)v&V00PmAeRo8^mAX54*ndOgf{p zYGQU59VX@Gm1H~2`%JY@2+CvF@4%9K&#-Sx&D-eDibfp_IER48ehI^%ZI4c-)~@f{ z4?v2(G`U$=?-SJ(i=HfV88RAinnJVEO+ZJvw!t~xGk36k#s}Of1vAeuFPFYkO z&YEH~>5w?n)wx4)D5C}GG(my7ac*s2=@V=vKEO;jSYbbkki5idmh$>SGH-`Yy~C-8 z`8tl}&|3kxr> zPq2*jM&p}t!C?9XrF6utL>`*(yx`+!Ju77C|?VRWO|x>)v>VV5xvs+zZg=N<*Gh)ueYCmof>OGN$xDs^=w zom%~mTGG=?{qv_x)m{?i5eU}?>&up+~8~(ozeaP-fv^$ zGYpo8ec$naYZC2*aU6o)JQZUY0R9*r{dz1i!KGax3bmdqw&^+5B*NKEM7njrRPK^$*VmaA~6B@s9qGki2*Ke~LbG;i%KYAMPc* zd;#Vb)#&xkKD0SEuH+M_=)C)y!&J%IkCnhI7owa|dLoT0VWUBGIReBY2bbe^psKqx zm*r)KBQF?wk*~cOM!sshnd+%~sbj9ZN1d%RgB-bwsMW0edOlsb7~_mME%*V>RyI#5 zgCABKiXKV6Nke-!i++U23Z#iG>kw?l82#ghy7h@$QV{HRv`Az~XWx-v=MgL_8W z^+_u*eHiQFKiH=kPCNGH$lU*&O>6bVj}PEwY;AC^pX~uIA@S!5o_YaAXyJ;9;85rw zuHE2gS=>=UP()B%MQYOz@r}n}Y47V7Ui09pf9&L-z3l^^Y8aT8%kKRX7)bE`qvyYTv9jCJT&xEk(sM)oe8kas;&=_SeWH6H()R zb5^>KJ-$+7*sqR|^<O5MR-aaE`lvh zlq-H8%EC56_4_!ty^g(L>T<7jG*k;;mLs{0{tbwmx! zUqr?!eR6SLY7_THrpg2)iQmY3`6X9lw|G#a5#q zJdkAX0i0;xxn|?_kbfJrg%qdQLz~KC1l(s?t@8ilW7NNZ_Q4h~h4N0J^NUnTI*A_6 zteg$;mS^`!GqCe^48ZQ8O5PWGauB`5Fxz~jA}c#VkcI!(RBRRMKgN|v|D~EolD^w`<@%0?GiDDgs8H0g zv3nlIrE3mr_Qt2*Ai8qN{*;Sb_4}7e^~$d4@SB$jJ|?L8Bwn1whD@Cp;W1nRQl0uV zcw6BgDuFhCM4{~FDL8L!*Pk0%?}Zg z`Zom^W1rAy#8RA>bxxw=BN+5}xV{(p@7Fz?c1z^%(jYnVfpXuq71&I^&=qDJ!FF7ue%e=UVl%SQ{ zP=f144pju{m>8mkX*Tu(5GVdHR{3kIpEkmS6GFM;81pUsrBEb}@S1@qOKJEywiW@j z|4DU;C;ji^5ZnBVZLKDbn`}PlpMyt~`AETzFzq{^V+zdlY#ccn5Hp`OPsA5`JX#1L z?8@-pp*3;oR8??~{4(ihUYreh>1Oc49+-YW<-l zJWv$5jER3B_EO#NiTy&{W{c8e*C7Jh);7x%li7zg@bA(2c^tdQGij+4N1Z*lx`L8L z;&}qNBtMWfjmaV)MR*TA0E$Wd5jJW-Srj#WJmTaz=w7A_F?k2kA<2aU}b}_qQ9YxkPigd;AC9LII_>zl{v_U?u}M~nP5v;iiF*Btl(|x;tdy#X8Q%v z3#u~cZxE+0!{)4@QLb8?c30Mo!4aAsR{i(T!%8&`Qnw%Y1Pf;K%b+|n-gjq?xH_z) z$rKz#uO;jK1_3*?RXgF}you_Gv_snuwoffd^EM3>2N(u@K`zqRv-TKl`uO5v=O8Qu zl)2IiEf-LO#Bv7Ap0|XvwuGW zf0kl~rEvcsN)CK*(^~rN*}Z1D5$+utLW0y!sfgxa>M#Yy1RY5#fb-O`jz9@1kCDL*hXIP1M796NFL!X6`?QCH&(tW4A+H^F*ZB zgCe)|u^caV0&)=5xD~@qWQ}sp7Lr|(^+jDUTALq50J9lf;P5p{Kn=7%V*ky`PR}pW zXh;LpjOR&j`+}gHpm{}aL_Kl2%aFCO%MTjVr!m%^SB2%SZ;NHLlLq;DN5J945c6Rr zH44r}oSeEd2KA2%1_`I*)3h=9>>r3O!6rz3zY~Q=1<;IqD}aJ6@hjAj3CEPy!;O)r zDD%-ag(avxh#J|tGl&`QU$0)|DAOg#Nt~6)J|PX$olfu0|GOn@QvBGtX1$Lwmn3&9 zu!1l7brf+UNK$^4#&>Is0m2G!@nyzD$BQ>5t0a^ardwl6k*IDhVp}Evd5DNZd-V9o zFMLPR%DgsTAF!BRVPxo|LyAB%$~4r{<-EG*@#XnV`u9HQMReq}^W~|*`{;|}9_5g% zx{e+*wm}&f+U4hdx~`=5iXUKaMmUE&2z-0q>_cU4fci*A3z%!p8||gwp2La<%zjY~ zGEH@PyAjsIJMb<=(!zz*I2Bwz)p+_{9!8OjO~h8#cVO1E9Cp#_biki)x*ub*#|P8~1vXSla$iN`eno2p-ky=>yQf6> z%hv(jilC#P?lCohGVKg0CjcZt(~p^8SCTM`k6$$b6T&d)0KqYFeBKUWVir&-jjylu zdMFfE{Q<7%&R$~lf=DuPM^(26$=)(}Xm@SC1I!^jzT55@N7s{@X=iz`2HtapjS6yb zGa#EakG1?B0$;VqI3uvyeoyIM%D?{E%4Bl6>L9BQY6KTuiwK68iMnp$WKqNvB427J z)-aQrzuBJGEWcpa4)~m4`~dW#4`I+WnDMAWo)ZJ0(*Ixsxb332-*A%gRodMBbF&+G z^{lpE-8tWiaf9v?<)jKmCUE|KwH#v|EJ#Sl2ArNvQwodXuKF>(%AWn2ydA>63!|V_ z84U~dv2N!#zVFD}fh*uK=5&b_?5P@8rOM!W7es8h&hZ#laamMN$-d0gmZa}Pv@af4 z7q|lOc0s%!#EQJuTP8mYRVK(UhqSqQfY@GBXKi$b%UaoBcDZWKnXfD~82;Oi4G52z zB{}&|uCn;m=j)ndG523sp{g<+>0^-&#u}Xvf#>1$9?nJu{bD*3n6dZEp{6;{)0IFN zYA&L&^P((DrBha7`?(Zg^HLNE-c{IhGISdWI7v*L)kFf+3Z1`v>ReNot2^b0T<75{6jcj}@nT8;&X z>4gHR|9Bpan(t^%K@{}))=Q)oEAkyN35Ep31ZH?U6P_F_QZqQe+vu$p9{Glo!;Ptf z^%+LD3aCIDgV{m!xjgT&cbTNt05i^gz%S) zdi?>PAkh}key;Z3-rm*1!?l@TfArvb>-RApDpMfR$hbPaMLd5RLDNaP#0zkIs9)p3 z&v|1L_#Nq=E5hZc4=YEJUPpgtKpzqCR57Z2yq4y`wf0;S!D3<`#aI|IH_#X8*l0oZ!lSS+Mxlq16sEjYkB|&+-o7JTvN%}ePJFQbR zen&ms4`|?ki}Rdf=*y045LcMN@DHmCd_uWyhW85nlsDaOVfN1`?(OC!xw@Y3oPRW* zuZwra0kbJe^vpg+`cm~!AtZWu<}?#`lr4|lFbzr!R!^t;n2Wj59ueben>!d@kV8rUXd)r4^?yV4V850R15|Mbr zC5}SbU9Aa+e_;Q>ZbE=^9+$vK;0dq$D0y_+l0-Ui5n7)GwBs>5__9{LXUI zh$Afov4~#&T*V`u(>9HCXrCEA+G}$B8Q=l^3Ig@DO<{Y=Pa)jX`3i--abPrG#a{Fl zH7EW_H3JA0oM*%*$VQBD{+Tpd#4V?gBjd+@Zw8{;F9@r;scj4K-o+mKoFy;{@XvWAx+rm9Si2V50Rw+$@m zP1;vYDMHmafbgak^4%sPfR~Uu&NL(P zfHK?&DJBvGLDP?!U`;aC3q%9MAou{^)f(Awq=KToBc(wJhAkuM5bpqb&cf6}HA-Tt zOf>9w_q2ai{~tjC?71Wzpi}7Fz4b(b3L07biwdGSE8b~tvnHi0hTrw#x#3x4Zl}>Wk{LGo0M}SIM{C<7G*4)e&XE~96 z!YDK$cYUHgH6#m{gxSshxlyMq?MM^}8gk8e*yQuMXPql3CGJL9+`s%Iwe?G^#Z;eT zj4QQ~dTHI0M4)NZ0GuZbf7BrE!^V3nL23%17T;_WiOf~=%>`s*drbOD)KCXFphIZZ z^h%lqkTk*oarp!gAdwfMe*v%EZD~;L1B(22nqq0?h3Kn#D1-%q@|qhlB14+vzxxn# z3oH0E=L(^Lpoh!#v;{=Ltbyec;89nLfc}=ZL?t8M(ecQLQ*24jOVn5Ho5(Ya#>orY;9Qwwq_CzSC^dK>kiCb%t@g^FOIMN-SWX>0j(3tKr+mAZs^Zp+}U| zitk?7b*Fv-kkj1>tMIJg*#k{!N0YON^3C?`_y&>}5dKnv_-V@gwsN8BCLyCbnm@|r!Vm*#vMz{m z#gf0o4SYGSiMW`Yjk=bo05mNA2}5?OwBBj&aI-kkPA7a3hm^*|X4^^JN+dDi5L|Cu zY6FP74xsF~vy08@KAaGH%__fmq zmJeMujjq7?o{G4w5PN`Jg2oB&1hec1e&_I50C;ua4Hd@hRyaBm{Um7s?-X`llVgJV z>MmOpjctLx6$=X}Q2DSP*jEXSJ7j$FOF?kIJ) z+i!8+GoJJEHe6!#^1Xt5eb*5$Fw6o$A(vlm(pcG{`QdDtQ728H4Uy?IuUGJnc=I}g z)jzfR*rL+^%bJJL^s%8GxAjoN3uAulP==(vjCE}3P}(nhp@M8`pgxn=kn_mR zvRlR_vqVFdei+)?>&>ydir(f0^98+NO4CXE{2ZX~7&B5b?M4uEUD`lm7I=v1NPBg( zwlqswxK4vz226>pEbfnWce-f-O873iXOI9t7~6@hkqZri#XyEB7V6xrlC9hfxsd2T z^4%_!p~;)ZByq8>#IGUxFg5U>wo@m*%rPAOKbJx@#~y60A^Y|mEF74#svbv(VitzF zu?AkY6hWK}j+*oH#-Ed*wK%%aamn!*V68#T=qCDL3|w|%uwHDljATo>$1(loY4D$J zWQnU4OKlTp+{{SA)Hac(ktcO#|8M?6p{m0xQ#;a8#7GScszI)hG7o&33h=8Mz%jjz z)k0!S>CJ|`C&IYws;S{J{LIrp;)|lG{v7?cX5v4&Q^ecVU#b==LqDae<~^xxIBik5 zOs&0sJI4pjd*ReMJ(}cvG~E~P!GJmk&aNDq6|~2<($N!M%2ItK#-}oo_a}9sHR++! zIOKvRgc(y11BWWx)nS;NS2)F|>@8WXt_*$=b!%bCOHbRb=m^ETC$tp9?k(Q;3PkuW zbah_P!vBy!K;&iZcqSC}ut(ye74?dOV^BvY>eoe59D|g~oP(Pr`i0LwIgVW|KGG8R z53DE5fSE`CXm$jk0hsXbDmp-+exM@~02%?<5&$x~1Tsw`3L{4!HNdW9))t8GegWVB z`kUv#gu|6G$@LC)>%_5<(Au=we`<)=2tx~_ zBS!^-$MAO9YEa{3zs2~`NA%9!2=8EdVmS#E|45li#*K~oZ5TZ(B z8%NQp2VkVTVnv*O@wgX*o*RhF@FUzFo=hPLUt-}W(4?f~w}Q5O1XNXXJP$H&r`I&O zVW4^KsuqT)?@2DNf_h_!RZu2B4y^=;w$M02%?9rCyOz literal 0 HcmV?d00001 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info b/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info new file mode 100644 index 0000000000..fca7833962 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info @@ -0,0 +1,167 @@ +12571 1 0 +9881 0 33000 +5 0 66000 +544 0 100000 +5 0 133000 +2642 0 166000 +5 0 200000 +531 0 233000 +5 0 266000 +1359 0 300000 +5 0 333000 +551 0 366000 +28 0 400000 +10791 0 433000 +5 0 466000 +655 0 500000 +5 0 533000 +3769 0 566000 +5 0 600000 +711 0 633000 +5 0 666000 +2004 0 700000 +5 0 733000 +657 0 766000 +26 0 800000 +8969 0 833000 +5 0 866000 +630 0 900000 +5 0 933000 +2787 0 966000 +5 0 1000000 +404 0 1033000 +5 0 1066000 +1518 0 1100000 +5 0 1133000 +493 0 1166000 +26 0 1200000 +9900 0 1233000 +5 0 1266000 +620 0 1300000 +5 0 1333000 +3072 0 1366000 +5 0 1400000 +668 0 1433000 +5 0 1466000 +1821 0 1500000 +5 0 1533000 +682 0 1566000 +26 0 1600000 +9560 0 1633000 +5 0 1666000 +667 0 1700000 +5 0 1733000 +3249 0 1766000 +5 0 1800000 +589 0 1833000 +5 0 1866000 +1816 0 1900000 +5 0 1933000 +548 0 1966000 +26 0 2000000 +9916 0 2033000 +5 0 2066000 +628 0 2100000 +5 0 2133000 +3034 0 2166000 +5 0 2200000 +590 0 2233000 +5 0 2266000 +1581 0 2300000 +5 0 2333000 +524 0 2366000 +26 0 2400000 +8182 0 2433000 +5 0 2466000 +552 0 2500000 +5 0 2533000 +2412 0 2566000 +5 0 2600000 +489 0 2633000 +5 0 2666000 +1227 0 2700000 +5 0 2733000 +432 0 2766000 +26 0 2800000 +2017 0 2833000 +516 0 2866000 +26 0 2900000 +16619 0 2933000 +6710 0 2966000 +5 0 3000000 +425 0 3033000 +5 0 3066000 +1964 0 3100000 +5 0 3133000 +386 0 3166000 +5 0 3200000 +1129 0 3233000 +5 0 3266000 +1156 0 3300000 +5 0 3333000 +486 0 3366000 +28 0 3400000 +10304 0 3433000 +5 0 3466000 +412 0 3500000 +5 0 3533000 +2608 0 3566000 +5 0 3600000 +397 0 3633000 +5 0 3666000 +1514 0 3700000 +5 0 3733000 +533 0 3766000 +26 0 3800000 +11698 0 3833000 +5 0 3866000 +542 0 3900000 +5 0 3933000 +3334 0 3966000 +5 0 4000000 +547 0 4033000 +5 0 4066000 +1809 0 4100000 +5 0 4133000 +55 0 4166000 +26 0 4200000 +9496 0 4233000 +5 0 4266000 +658 0 4300000 +5 0 4333000 +3232 0 4366000 +5 0 4400000 +600 0 4433000 +5 0 4466000 +1766 0 4500000 +5 0 4533000 +550 0 4566000 +25 0 4600000 +9951 0 4633000 +5 0 4666000 +624 0 4700000 +5 0 4733000 +3617 0 4766000 +5 0 4800000 +644 0 4833000 +5 0 4866000 +1841 0 4900000 +5 0 4933000 +649 0 4966000 +25 0 5000000 +9901 0 5033000 +5 0 5066000 +515 0 5100000 +5 0 5133000 +2814 0 5166000 +5 0 5200000 +511 0 5233000 +5 0 5266000 +1521 0 5300000 +5 0 5333000 +509 0 5366000 +26 0 5400000 +10579 0 5433000 +5 0 5466000 +575 0 5500000 +5 0 5533000 \ No newline at end of file diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp index 8420cab0a1..9a42d72955 100644 --- a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp @@ -101,7 +101,7 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { const StringToName kStringToName[] = { {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4}, - {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, + {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1}, }; const size_t kNumStringToName = @@ -142,79 +142,48 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { // callback function to process onWorkDone received by Listener void handleWorkDone(std::list>& workItems) { for (std::unique_ptr& work : workItems) { - // handle configuration changes in work done - if (!work->worklets.empty() && - (work->worklets.front()->output.configUpdate.size() != 0)) { - ALOGV("Config Update"); - std::vector> updates = - std::move(work->worklets.front()->output.configUpdate); - std::vector configParam; - std::vector> failures; - for (size_t i = 0; i < updates.size(); ++i) { - C2Param* param = updates[i].get(); - if (param->index() == - C2VideoSizeStreamInfo::output::PARAM_TYPE) { - ALOGV("Received C2VideoSizeStreamInfo"); - configParam.push_back(param); - } - } - mComponent->config(configParam, C2_DONT_BLOCK, &failures); - ASSERT_EQ(failures.size(), 0u); - } - - mFramesReceived++; - mEos = (work->worklets.front()->output.flags & - C2FrameData::FLAG_END_OF_STREAM) != 0; - auto frameIndexIt = - std::find(mFlushedIndices.begin(), mFlushedIndices.end(), - work->input.ordinal.frameIndex.peeku()); - - // For decoder components current timestamp always exceeds - // previous timestamp - typedef std::unique_lock ULock; - bool codecConfig = ((work->worklets.front()->output.flags & - C2FrameData::FLAG_CODEC_CONFIG) != 0); - if (!codecConfig && - !work->worklets.front()->output.buffers.empty()) { - EXPECT_GE( - (work->worklets.front()->output.ordinal.timestamp.peeku()), - mTimestampUs); - mTimestampUs = - work->worklets.front()->output.ordinal.timestamp.peeku(); - - ULock l(mQueueLock); - if (mTimestampDevTest) { - bool tsHit = false; - std::list::iterator it = mTimestampUslist.begin(); - while (it != mTimestampUslist.end()) { - if (*it == mTimestampUs) { - mTimestampUslist.erase(it); - tsHit = true; - break; + if (!work->worklets.empty()) { + // For decoder components current timestamp always exceeds + // previous timestamp + typedef std::unique_lock ULock; + bool codecConfig = ((work->worklets.front()->output.flags & + C2FrameData::FLAG_CODEC_CONFIG) != 0); + if (!codecConfig && + !work->worklets.front()->output.buffers.empty()) { + EXPECT_GE( + (work->worklets.front()->output.ordinal.timestamp.peeku()), + mTimestampUs); + mTimestampUs = + work->worklets.front()->output.ordinal.timestamp.peeku(); + + ULock l(mQueueLock); + if (mTimestampDevTest) { + bool tsHit = false; + std::list::iterator it = mTimestampUslist.begin(); + while (it != mTimestampUslist.end()) { + if (*it == mTimestampUs) { + mTimestampUslist.erase(it); + tsHit = true; + break; + } + it++; } - it++; - } - if (tsHit == false) { - if (mTimestampUslist.empty() == false) { - EXPECT_EQ(tsHit, true) - << "TimeStamp not recognized"; - } else { - std::cout << "[ INFO ] Received non-zero " - "output / TimeStamp not recognized \n"; + if (tsHit == false) { + if (mTimestampUslist.empty() == false) { + EXPECT_EQ(tsHit, true) + << "TimeStamp not recognized"; + } else { + std::cout << "[ INFO ] Received non-zero " + "output / TimeStamp not recognized \n"; + } } } } - } - - work->input.buffers.clear(); - work->worklets.clear(); - { - ULock l(mQueueLock); - mWorkQueue.push_back(std::move(work)); - if (!mFlushedIndices.empty()) { - mFlushedIndices.erase(frameIndexIt); - } - mQueueCondition.notify_all(); + bool mCsd; + workDone(mComponent, work, mFlushedIndices, mQueueLock, + mQueueCondition, mWorkQueue, mEos, mCsd, + mFramesReceived); + (void)mCsd; } } } @@ -227,6 +196,7 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { hevc, vp8, vp9, + av1, unknown_comp, }; @@ -341,6 +311,11 @@ void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, "bbb_vp9_640x360_1600kbps_30fps.vp9"}, {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info"}}, + {Codec2VideoDecHidlTest::standardComp::av1, + {"bbb_av1_640_360.av1", + "bbb_av1_176_144.av1"}, + {"bbb_av1_640_360.info", + "bbb_av1_176_144.info"}}, }; for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) { @@ -352,11 +327,11 @@ void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, } } -void decodeNFrames(const std::shared_ptr &component, - std::mutex &queueLock, std::condition_variable &queueCondition, - std::list> &workQueue, - std::list &flushedIndices, - std::shared_ptr &linearPool, +void decodeNFrames(const std::shared_ptr& component, + std::mutex &queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, + std::list& flushedIndices, + std::shared_ptr& linearPool, std::ifstream& eleStream, android::Vector* Info, int offset, int range, bool signalEOS = true) { @@ -397,35 +372,37 @@ void decodeNFrames(const std::shared_ptr &comp int size = (*Info)[frameID].bytesCount; char* data = (char*)malloc(size); + ASSERT_NE(data, nullptr); eleStream.read(data, size); ASSERT_EQ(eleStream.gcount(), size); - std::shared_ptr block; - ASSERT_EQ(C2_OK, - linearPool->fetchLinearBlock( - size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, - &block)); - ASSERT_TRUE(block); - - // Write View - C2WriteView view = block->map().get(); - if (view.error() != C2_OK) { - fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error()); - break; - } - ASSERT_EQ((size_t)size, view.capacity()); - ASSERT_EQ(0u, view.offset()); - ASSERT_EQ((size_t)size, view.size()); + work->input.buffers.clear(); + if (size) { + std::shared_ptr block; + ASSERT_EQ(C2_OK, + linearPool->fetchLinearBlock( + size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, + &block)); + ASSERT_TRUE(block); + + // Write View + C2WriteView view = block->map().get(); + if (view.error() != C2_OK) { + fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error()); + break; + } + ASSERT_EQ((size_t)size, view.capacity()); + ASSERT_EQ(0u, view.offset()); + ASSERT_EQ((size_t)size, view.size()); - memcpy(view.base(), data, size); + memcpy(view.base(), data, size); - work->input.buffers.clear(); - work->input.buffers.emplace_back(new LinearBuffer(block)); + work->input.buffers.emplace_back(new LinearBuffer(block)); + free(data); + } work->worklets.clear(); work->worklets.emplace_back(new C2Worklet); - free(data); - std::list> items; items.push_back(std::move(work)); @@ -437,29 +414,6 @@ void decodeNFrames(const std::shared_ptr &comp } } -void waitOnInputConsumption(std::mutex& queueLock, - std::condition_variable& queueCondition, - std::list>& workQueue, - size_t bufferCount = MAX_INPUT_BUFFERS) { - typedef std::unique_lock ULock; - uint32_t queueSize; - uint32_t maxRetry = 0; - { - ULock l(queueLock); - queueSize = workQueue.size(); - } - while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) { - ULock l(queueLock); - if (queueSize != workQueue.size()) { - queueSize = workQueue.size(); - maxRetry = 0; - } else { - queueCondition.wait_for(l, TIME_OUT); - maxRetry++; - } - } -} - TEST_F(Codec2VideoDecHidlTest, validateCompName) { if (mDisableTest) return; ALOGV("Checks if the given component is a valid video component"); @@ -678,8 +632,8 @@ TEST_F(Codec2VideoDecHidlTest, ThumbnailTest) { EXPECT_GE(mFramesReceived, 1U); ASSERT_EQ(mEos, true); ASSERT_EQ(mComponent->stop(), C2_OK); - ASSERT_EQ(mComponent->release(), C2_OK); } + ASSERT_EQ(mComponent->release(), C2_OK); } TEST_F(Codec2VideoDecHidlTest, EOSTest) { @@ -712,7 +666,6 @@ TEST_F(Codec2VideoDecHidlTest, EOSTest) { ASSERT_EQ(mComponent->queue(&items), C2_OK); { - typedef std::unique_lock ULock; ULock l(mQueueLock); if (mWorkQueue.size() != MAX_INPUT_BUFFERS) { mQueueCondition.wait_for(l, TIME_OUT); @@ -723,46 +676,6 @@ TEST_F(Codec2VideoDecHidlTest, EOSTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } -TEST_F(Codec2VideoDecHidlTest, EmptyBufferTest) { - description("Tests empty input buffer"); - if (mDisableTest) return; - typedef std::unique_lock ULock; - ASSERT_EQ(mComponent->start(), C2_OK); - std::unique_ptr work; - // Prepare C2Work - { - ULock l(mQueueLock); - if (!mWorkQueue.empty()) { - work.swap(mWorkQueue.front()); - mWorkQueue.pop_front(); - } else { - ASSERT_TRUE(false) << "mWorkQueue Empty at the start of test"; - } - } - ASSERT_NE(work, nullptr); - - work->input.flags = (C2FrameData::flags_t)0; - work->input.ordinal.timestamp = 0; - work->input.ordinal.frameIndex = 0; - work->input.buffers.clear(); - work->worklets.clear(); - work->worklets.emplace_back(new C2Worklet); - - std::list> items; - items.push_back(std::move(work)); - ASSERT_EQ(mComponent->queue(&items), C2_OK); - - { - typedef std::unique_lock ULock; - ULock l(mQueueLock); - if (mWorkQueue.size() != MAX_INPUT_BUFFERS) { - mQueueCondition.wait_for(l, TIME_OUT); - } - } - ASSERT_EQ(mWorkQueue.size(), (size_t)MAX_INPUT_BUFFERS); - ASSERT_EQ(mComponent->stop(), C2_OK); -} - TEST_F(Codec2VideoDecHidlTest, FlushTest) { description("Tests Flush calls"); if (mDisableTest) return; @@ -874,6 +787,69 @@ TEST_F(Codec2VideoDecHidlTest, FlushTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } +TEST_F(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) { + description("Decode with multiple empty input frames"); + if (mDisableTest) return; + + char mURL[512], info[512]; + std::ifstream eleStream, eleInfo; + + strcpy(mURL, gEnv->getRes().c_str()); + strcpy(info, gEnv->getRes().c_str()); + GetURLForComponent(mCompName, mURL, info); + + eleInfo.open(info); + ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found"; + android::Vector Info; + int bytesCount = 0; + uint32_t frameId = 0; + uint32_t flags = 0; + uint32_t timestamp = 0; + bool codecConfig = false; + // This test introduces empty CSD after every 20th frame + // and empty input frames at an interval of 5 frames. + while (1) { + if (!(frameId % 5)) { + if (!(frameId % 20)) flags = 32; + else flags = 0; + bytesCount = 0; + } else { + if (!(eleInfo >> bytesCount)) break; + eleInfo >> flags; + eleInfo >> timestamp; + codecConfig = flags ? + ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0; + } + Info.push_back({bytesCount, flags, timestamp}); + frameId++; + } + eleInfo.close(); + + ASSERT_EQ(mComponent->start(), C2_OK); + ALOGV("mURL : %s", mURL); + eleStream.open(mURL, std::ifstream::binary); + ASSERT_EQ(eleStream.is_open(), true); + ASSERT_NO_FATAL_FAILURE(decodeNFrames( + mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, + mLinearPool, eleStream, &Info, 0, (int)Info.size())); + + // blocking call to ensures application to Wait till all the inputs are + // consumed + if (!mEos) { + ALOGV("Waiting for input consumption"); + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue)); + } + + eleStream.close(); + if (mFramesReceived != Info.size()) { + ALOGE("Input buffer count and Output buffer count mismatch"); + ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, + Info.size()); + ASSERT_TRUE(false); + } +} + } // anonymous namespace // TODO : Video specific configuration Test diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 87b7902fd8..8585c87edf 100644 --- a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -139,40 +139,11 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { // callback function to process onWorkDone received by Listener void handleWorkDone(std::list>& workItems) { for (std::unique_ptr& work : workItems) { - // handle configuration changes in work done - if (!work->worklets.empty() && - (work->worklets.front()->output.configUpdate.size() != 0)) { - ALOGV("Config Update"); - std::vector> updates = - std::move(work->worklets.front()->output.configUpdate); - std::vector configParam; - std::vector> failures; - for (size_t i = 0; i < updates.size(); ++i) { - C2Param* param = updates[i].get(); - if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) { - mCsd = true; - } - } - } - mFramesReceived++; - if (work->result != C2_OK) mFailedWorkReceived++; - mEos = (work->worklets.front()->output.flags & - C2FrameData::FLAG_END_OF_STREAM) != 0; - auto frameIndexIt = - std::find(mFlushedIndices.begin(), mFlushedIndices.end(), - work->input.ordinal.frameIndex.peeku()); - ALOGV("WorkDone: frameID received %d", - (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); - work->input.buffers.clear(); - work->worklets.clear(); - { - typedef std::unique_lock ULock; - ULock l(mQueueLock); - mWorkQueue.push_back(std::move(work)); - if (!mFlushedIndices.empty()) { - mFlushedIndices.erase(frameIndexIt); - } - mQueueCondition.notify_all(); + if (!work->worklets.empty()) { + if (work->result != C2_OK) mFailedWorkReceived++; + workDone(mComponent, work, mFlushedIndices, mQueueLock, + mQueueCondition, mWorkQueue, mEos, mCsd, + mFramesReceived); } } } @@ -272,11 +243,11 @@ void GetURLForComponent(char* URL) { strcat(URL, "bbb_352x288_420p_30fps_32frames.yuv"); } -void encodeNFrames(const std::shared_ptr &component, - std::mutex &queueLock, std::condition_variable &queueCondition, - std::list> &workQueue, - std::list &flushedIndices, - std::shared_ptr &graphicPool, +void encodeNFrames(const std::shared_ptr& component, + std::mutex &queueLock, std::condition_variable& queueCondition, + std::list>& workQueue, + std::list& flushedIndices, + std::shared_ptr& graphicPool, std::ifstream& eleStream, uint32_t frameID, uint32_t nFrames, uint32_t nWidth, int32_t nHeight, bool flushed = false,bool signalEOS = true) { @@ -319,6 +290,7 @@ void encodeNFrames(const std::shared_ptr &comp flushedIndices.emplace_back(frameID); } char* data = (char*)malloc(bytesCount); + ASSERT_NE(data, nullptr); memset(data, 0, bytesCount); if (eleStream.is_open()) { eleStream.read(data, bytesCount); @@ -365,30 +337,6 @@ void encodeNFrames(const std::shared_ptr &comp } } -void waitOnInputConsumption(std::mutex &queueLock, - std::condition_variable &queueCondition, - std::list> &workQueue, - size_t bufferCount = MAX_INPUT_BUFFERS) { - typedef std::unique_lock ULock; - uint32_t queueSize; - int maxRetry = 0; - { - ULock l(queueLock); - queueSize = workQueue.size(); - } - while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) { - ULock l(queueLock); - if (queueSize != workQueue.size()) { - queueSize = workQueue.size(); - maxRetry = 0; - } else { - queueCondition.wait_for(l, TIME_OUT); - maxRetry++; - } - } -} - - TEST_F(Codec2VideoEncHidlTest, validateCompName) { if (mDisableTest) return; ALOGV("Checks if the given component is a valid video component"); @@ -488,46 +436,6 @@ TEST_F(Codec2VideoEncHidlTest, EOSTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } -TEST_F(Codec2VideoEncHidlTest, EmptyBufferTest) { - description("Tests empty input buffer"); - if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); - - typedef std::unique_lock ULock; - std::unique_ptr work; - { - ULock l(mQueueLock); - if (!mWorkQueue.empty()) { - work.swap(mWorkQueue.front()); - mWorkQueue.pop_front(); - } else { - ALOGE("mWorkQueue Empty is not expected at the start of the test"); - ASSERT_TRUE(false); - } - } - ASSERT_NE(work, nullptr); - work->input.flags = (C2FrameData::flags_t)0; - work->input.ordinal.timestamp = 0; - work->input.ordinal.frameIndex = 0; - work->input.buffers.clear(); - work->worklets.clear(); - work->worklets.emplace_back(new C2Worklet); - - std::list> items; - items.push_back(std::move(work)); - ASSERT_EQ(mComponent->queue(&items), C2_OK); - uint32_t queueSize; - { - ULock l(mQueueLock); - queueSize = mWorkQueue.size(); - if (queueSize < MAX_INPUT_BUFFERS) { - mQueueCondition.wait_for(l, TIME_OUT); - } - } - ASSERT_EQ(mWorkQueue.size(), (uint32_t)MAX_INPUT_BUFFERS); - ASSERT_EQ(mComponent->stop(), C2_OK); -} - TEST_F(Codec2VideoEncHidlTest, FlushTest) { description("Test Request for flush"); if (mDisableTest) return; diff --git a/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h index 1215b131f0..dd455576d2 100644 --- a/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h @@ -17,9 +17,6 @@ #ifndef MEDIA_C2_VIDEO_HIDL_TEST_COMMON_H #define MEDIA_C2_VIDEO_HIDL_TEST_COMMON_H -#define MAX_RETRY 20 -#define TIME_OUT 400ms -#define MAX_INPUT_BUFFERS 8 #define ENCODER_TIMESTAMP_INCREMENT 40000 #define ENC_NUM_FRAMES 32 #define ENC_DEFAULT_FRAME_WIDTH 352 -- GitLab From c656c8df182f2b7a1ad0d692ad7730e9c3c4c293 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 13 Dec 2018 09:56:33 -0800 Subject: [PATCH 0608/1530] Move MKV/MP4 extractors to APEX Test: make systemimage on marlin Bug: 111407253 Change-Id: Icd90893f7498090ea9d8cbe29f3845e98d794ebc --- apex/Android.bp | 2 ++ services/mediaextractor/Android.mk | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index eee26aec7d..449d29cebf 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -21,7 +21,9 @@ apex { "libamrextractor", "libflacextractor", "libmidiextractor", + "libmkvextractor", "libmp3extractor", + "libmp4extractor", "libwavextractor", ], key: "com.android.media.key", diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 7c9c727589..dddd960fbd 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -40,8 +40,6 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy # extractor libraries LOCAL_REQUIRED_MODULES += \ - libmkvextractor \ - libmp4extractor \ libmpeg2extractor \ liboggextractor \ -- GitLab From b2dec569274e175e5b81ec2d9baa432e0da5e563 Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Thu, 6 Dec 2018 08:07:28 -0800 Subject: [PATCH 0609/1530] Enable Scudo for mediaswcodec Scudo is a hardened usermode allocator that aims at mitigating heap-based vulnerabilities (heap overflow, double free, etc). See go/scudo-allocator for more information. This change enables the allocator for the mediaswcodec service, effectively linking in the Scudo dynamic library to the binary. Allocation functions will be serviced by the allocator. The observed impact on the memory footprint of the service is about a 500k increase to the RSS (due to the Quarantine and chunk headers overhead), and the MediaCts tests pass without any issue I could see. In the event of a heap bug detected, the allocator will abort the process with a 'Scudo ERROR' message in the log followed by the cause (corrupted header, invalid state & so on) and the stack trace. Test: CtsMediaTestCases on a marlin Bug: 63907455 Change-Id: I751af455c002a59ccc4d89cd1e29743036f27708 --- services/mediacodec/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 789548d5b2..3b6dc80f76 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -108,6 +108,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE := mediaswcodec LOCAL_INIT_RC := mediaswcodec.rc LOCAL_32_BIT_ONLY := true +LOCAL_SANITIZE := scudo sanitizer_runtime_libraries := llndk_libraries := -- GitLab From 595caa3bb29626a8da08c318dc009213bd94adca Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 13 Dec 2018 11:08:28 -0800 Subject: [PATCH 0610/1530] Audio HAL: Simplify types updates for 'common' and 'effect' packages Instead of importing every single type individually ("using "), import entire namespaces of ::android::hardware::audio::common::CPP_VERSION and ::android::hardware::audio::effect::CPP_VERSION inside the "implementation" namespace in the default implementation. This simplifies modifying types, as it is not needed anymore to surround "using " directives with #if MAJOR_VERSION ... Note that the contents of the namespaces are imported inside the "implementation" namespace, which reduces risks of name collision Enclosed EffectHalHidl and supporting classes into 'effect' namespace because importing namespaces of ::a::h::audio::effect::CPP_VERSION and ::a::h::audio::CPP_VERSION into the same namespace, as it happens for StreamHalHidl.cpp module, causes name conflicts. Test: make Change-Id: Ia37d672605c7541b19ee40fc1b58eabaa9ed8e17 --- media/libaudiohal/EffectsFactoryHalInterface.cpp | 6 +++--- media/libaudiohal/impl/EffectBufferHalHidl.cpp | 2 ++ media/libaudiohal/impl/EffectBufferHalHidl.h | 5 ++++- media/libaudiohal/impl/EffectHalHidl.cpp | 8 +++----- media/libaudiohal/impl/EffectHalHidl.h | 13 ++++++------- media/libaudiohal/impl/EffectsFactoryHalHidl.cpp | 6 +++--- media/libaudiohal/impl/EffectsFactoryHalHidl.h | 6 ++++-- media/libaudiohal/impl/StreamHalHidl.cpp | 1 + .../impl/include/libaudiohal/FactoryHalHidl.h | 15 +++++++++++++-- 9 files changed, 39 insertions(+), 23 deletions(-) diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index e21c23525f..bd3ef6158d 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -25,13 +25,13 @@ namespace android { // static sp EffectsFactoryHalInterface::create() { if (hardware::audio::effect::V5_0::IEffectsFactory::getService() != nullptr) { - return V5_0::createEffectsFactoryHal(); + return effect::V5_0::createEffectsFactoryHal(); } if (hardware::audio::effect::V4_0::IEffectsFactory::getService() != nullptr) { - return V4_0::createEffectsFactoryHal(); + return effect::V4_0::createEffectsFactoryHal(); } if (hardware::audio::effect::V2_0::IEffectsFactory::getService() != nullptr) { - return V2_0::createEffectsFactoryHal(); + return effect::V2_0::createEffectsFactoryHal(); } return nullptr; } diff --git a/media/libaudiohal/impl/EffectBufferHalHidl.cpp b/media/libaudiohal/impl/EffectBufferHalHidl.cpp index 6ef4e8a0e0..5367972cff 100644 --- a/media/libaudiohal/impl/EffectBufferHalHidl.cpp +++ b/media/libaudiohal/impl/EffectBufferHalHidl.cpp @@ -30,6 +30,7 @@ using ::android::hardware::Return; using ::android::hidl::allocator::V1_0::IAllocator; namespace android { +namespace effect { namespace CPP_VERSION { // static @@ -142,5 +143,6 @@ void EffectBufferHalHidl::commit(size_t size) { memcpy(mExternalData, mAudioBuffer.raw, size); } +} // namespace effect } // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/impl/EffectBufferHalHidl.h b/media/libaudiohal/impl/EffectBufferHalHidl.h index 0c99a02f2c..482681324c 100644 --- a/media/libaudiohal/impl/EffectBufferHalHidl.h +++ b/media/libaudiohal/impl/EffectBufferHalHidl.h @@ -23,13 +23,15 @@ #include #include -using android::hardware::audio::effect::CPP_VERSION::AudioBuffer; using android::hardware::hidl_memory; using android::hidl::memory::V1_0::IMemory; namespace android { +namespace effect { namespace CPP_VERSION { +using namespace ::android::hardware::audio::effect::CPP_VERSION; + class EffectBufferHalHidl : public EffectBufferHalInterface { public: @@ -73,6 +75,7 @@ class EffectBufferHalHidl : public EffectBufferHalInterface }; } // namespace CPP_VERSION +} // namespace effect } // namespace android #endif // ANDROID_HARDWARE_EFFECT_BUFFER_HAL_HIDL_H diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp index df79b95dab..b0597b3607 100644 --- a/media/libaudiohal/impl/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -26,11 +26,6 @@ #include "EffectHalHidl.h" #include "HidlUtils.h" -using ::android::hardware::audio::effect::CPP_VERSION::AudioBuffer; -using ::android::hardware::audio::effect::CPP_VERSION::EffectBufferAccess; -using ::android::hardware::audio::effect::CPP_VERSION::EffectConfigParameters; -using ::android::hardware::audio::effect::CPP_VERSION::MessageQueueFlagBits; -using ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils; using ::android::hardware::audio::common::utils::EnumBitfield; using ::android::hardware::hidl_vec; @@ -38,9 +33,11 @@ using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; namespace android { +namespace effect { namespace CPP_VERSION { using namespace ::android::hardware::audio::common::CPP_VERSION; +using namespace ::android::hardware::audio::effect::CPP_VERSION; EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { @@ -338,4 +335,5 @@ status_t EffectHalHidl::setConfigImpl( } } // namespace CPP_VERSION +} // namespace effect } // namespace android diff --git a/media/libaudiohal/impl/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h index cd447ff3e2..9d9f707c85 100644 --- a/media/libaudiohal/impl/EffectHalHidl.h +++ b/media/libaudiohal/impl/EffectHalHidl.h @@ -23,17 +23,15 @@ #include #include -using ::android::hardware::audio::effect::CPP_VERSION::EffectBufferConfig; -using ::android::hardware::audio::effect::CPP_VERSION::EffectConfig; -using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor; -using ::android::hardware::audio::effect::CPP_VERSION::IEffect; -using EffectResult = ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; namespace android { +namespace effect { namespace CPP_VERSION { +using namespace ::android::hardware::audio::effect::CPP_VERSION; + class EffectHalHidl : public EffectHalInterface { public: @@ -70,7 +68,7 @@ class EffectHalHidl : public EffectHalInterface private: friend class EffectsFactoryHalHidl; - typedef MessageQueue StatusMQ; + typedef MessageQueue StatusMQ; sp mEffect; const uint64_t mEffectId; @@ -80,7 +78,7 @@ class EffectHalHidl : public EffectHalInterface std::unique_ptr mStatusMQ; EventFlag* mEfGroup; - static status_t analyzeResult(const EffectResult& result); + static status_t analyzeResult(const Result& result); static void effectBufferConfigFromHal( const buffer_config_t& halConfig, EffectBufferConfig* config); static void effectBufferConfigToHal( @@ -105,6 +103,7 @@ class EffectHalHidl : public EffectHalInterface }; } // namespace CPP_VERSION +} // namespace effect } // namespace android #endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp index 7fea46699f..7fd6bdebf3 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp @@ -26,14 +26,14 @@ #include "HidlUtils.h" using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils; -using ::android::hardware::audio::effect::CPP_VERSION::IEffect; -using ::android::hardware::audio::effect::CPP_VERSION::Result; using ::android::hardware::Return; namespace android { +namespace effect { namespace CPP_VERSION { using namespace ::android::hardware::audio::common::CPP_VERSION; +using namespace ::android::hardware::audio::effect::CPP_VERSION; EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") { mEffectsFactory = IEffectsFactory::getService(); @@ -145,6 +145,6 @@ status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, return EffectBufferHalHidl::mirror(external, size, buffer); } - } // namespace CPP_VERSION +} // namespace effect } // namespace android diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h index 702715367b..01178ffd8b 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h @@ -24,11 +24,12 @@ #include "ConversionHelperHidl.h" namespace android { +namespace effect { namespace CPP_VERSION { -using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor; -using ::android::hardware::audio::effect::CPP_VERSION::IEffectsFactory; using ::android::hardware::hidl_vec; +using ::android::CPP_VERSION::ConversionHelperHidl; +using namespace ::android::hardware::audio::effect::CPP_VERSION; class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl { @@ -70,6 +71,7 @@ sp createEffectsFactoryHal() { } } // namespace CPP_VERSION +} // namespace effect } // namespace android #endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 9765f1ea69..c12b3629c0 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -35,6 +35,7 @@ using ::android::hardware::Void; namespace android { namespace CPP_VERSION { +using EffectHalHidl = ::android::effect::CPP_VERSION::EffectHalHidl; using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand; using namespace ::android::hardware::audio::common::CPP_VERSION; diff --git a/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h index 1d912a0413..c7319d06a5 100644 --- a/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h +++ b/media/libaudiohal/impl/include/libaudiohal/FactoryHalHidl.h @@ -25,18 +25,29 @@ namespace android { +namespace effect { namespace V2_0 { sp createEffectsFactoryHal(); -sp createDevicesFactoryHal(); } // namespace V2_0 namespace V4_0 { sp createEffectsFactoryHal(); -sp createDevicesFactoryHal(); } // namespace V4_0 namespace V5_0 { sp createEffectsFactoryHal(); +} // namespace V5_0 +} // namespace effect + +namespace V2_0 { +sp createDevicesFactoryHal(); +} // namespace V2_0 + +namespace V4_0 { +sp createDevicesFactoryHal(); +} // namespace V4_0 + +namespace V5_0 { sp createDevicesFactoryHal(); } // namespace V5_0 -- GitLab From e92a5c7d3b0face0059566118e68d19c95284f39 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 13 Dec 2018 09:52:38 -0800 Subject: [PATCH 0611/1530] Convert OggExtractor to V3 format and move it to the media APEX. Bug: 111407253 Test: manual, CTS Change-Id: Ia6b6edc42f8bb374d4cc07888fb0f45030ca462f --- apex/Android.bp | 1 + include/media/MediaExtractorPluginHelper.h | 16 +- media/extractors/ogg/Android.bp | 1 - media/extractors/ogg/OggExtractor.cpp | 173 +++++++++++++++------ media/extractors/ogg/OggExtractor.h | 4 +- media/libstagefright/MediaTrack.cpp | 3 + media/libstagefright/Utils.cpp | 3 +- media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + media/ndk/libmediandk.map.txt | 1 + services/mediaextractor/Android.mk | 1 - 11 files changed, 142 insertions(+), 63 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 449d29cebf..991696c121 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -24,6 +24,7 @@ apex { "libmkvextractor", "libmp3extractor", "libmp4extractor", + "liboggextractor", "libwavextractor", ], key: "com.android.media.key", diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h index 292ec93ee8..705aa811c5 100644 --- a/include/media/MediaExtractorPluginHelper.h +++ b/include/media/MediaExtractorPluginHelper.h @@ -183,32 +183,32 @@ public: mBuffer = buf; } - ~MediaBufferHelperV3() {} + virtual ~MediaBufferHelperV3() {} - void release() { + virtual void release() { mBuffer->release(mBuffer->handle); } - void* data() { + virtual void* data() { return mBuffer->data(mBuffer->handle); } - size_t size() { + virtual size_t size() { return mBuffer->size(mBuffer->handle); } - size_t range_offset() { + virtual size_t range_offset() { return mBuffer->range_offset(mBuffer->handle); } - size_t range_length() { + virtual size_t range_length() { return mBuffer->range_length(mBuffer->handle); } - void set_range(size_t offset, size_t length) { + virtual void set_range(size_t offset, size_t length) { mBuffer->set_range(mBuffer->handle, offset, length); } - AMediaFormat *meta_data() { + virtual AMediaFormat *meta_data() { return mBuffer->meta_data(mBuffer->handle); } }; diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp index b28877d5d1..604ec5945e 100644 --- a/media/extractors/ogg/Android.bp +++ b/media/extractors/ogg/Android.bp @@ -13,7 +13,6 @@ cc_library_shared { shared_libs: [ "liblog", - "libmediaextractor", "libmediandk", ], diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index cc2c792191..29fe2b13f9 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -28,11 +28,8 @@ #include #include #include -#include -#include #include #include -#include #include #include #include @@ -48,7 +45,7 @@ extern "C" { namespace android { -struct OggSource : public MediaTrackHelperV2 { +struct OggSource : public MediaTrackHelperV3 { explicit OggSource(OggExtractor *extractor); virtual media_status_t getFormat(AMediaFormat *); @@ -57,7 +54,7 @@ struct OggSource : public MediaTrackHelperV2 { virtual media_status_t stop(); virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); protected: virtual ~OggSource(); @@ -85,7 +82,7 @@ struct MyOggExtractor { status_t seekToTime(int64_t timeUs); status_t seekToOffset(off64_t offset); - virtual media_status_t readNextPacket(MediaBufferBase **buffer) = 0; + virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer) = 0; status_t init(); @@ -93,6 +90,9 @@ struct MyOggExtractor { return AMediaFormat_copy(meta, mFileMeta); } + void setBufferGroup(MediaBufferGroupHelperV3 *group) { + mBufferGroup = group; + } protected: struct Page { uint64_t mGranulePosition; @@ -110,6 +110,7 @@ protected: int64_t mTimeUs; }; + MediaBufferGroupHelperV3 *mBufferGroup; DataSourceHelper *mSource; off64_t mOffset; Page mCurrentPage; @@ -148,7 +149,7 @@ protected: // 1 - bitstream identification header // 3 - comment header // 5 - codec setup header (Vorbis only) - virtual media_status_t verifyHeader(MediaBufferBase *buffer, uint8_t type) = 0; + virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type) = 0; // Read the next ogg packet from the underlying data source; optionally // calculate the timestamp for the output packet whilst pretending @@ -156,9 +157,9 @@ protected: // // *buffer is NULL'ed out immediately upon entry, and if successful a new buffer is allocated; // clients are responsible for releasing the original buffer. - media_status_t _readNextPacket(MediaBufferBase **buffer, bool calcVorbisTimestamp); + media_status_t _readNextPacket(MediaBufferHelperV3 **buffer, bool calcVorbisTimestamp); - int32_t getPacketBlockSize(MediaBufferBase *buffer); + int32_t getPacketBlockSize(MediaBufferHelperV3 *buffer); void parseFileMetaData(); @@ -182,7 +183,7 @@ struct MyVorbisExtractor : public MyOggExtractor { virtual uint64_t approxBitrate() const; - virtual media_status_t readNextPacket(MediaBufferBase **buffer) { + virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer) { return _readNextPacket(buffer, /* calcVorbisTimestamp = */ true); } @@ -194,7 +195,7 @@ protected: return granulePos * 1000000ll / mVi.rate; } - virtual media_status_t verifyHeader(MediaBufferBase *buffer, uint8_t type); + virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type); }; struct MyOpusExtractor : public MyOggExtractor { @@ -212,16 +213,16 @@ struct MyOpusExtractor : public MyOggExtractor { return 0; } - virtual media_status_t readNextPacket(MediaBufferBase **buffer); + virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer); protected: virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const; - virtual media_status_t verifyHeader(MediaBufferBase *buffer, uint8_t type); + virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type); private: - media_status_t verifyOpusHeader(MediaBufferBase *buffer); - media_status_t verifyOpusComments(MediaBufferBase *buffer); - uint32_t getNumSamplesInPacket(MediaBufferBase *buffer) const; + media_status_t verifyOpusHeader(MediaBufferHelperV3 *buffer); + media_status_t verifyOpusComments(MediaBufferHelperV3 *buffer); + uint32_t getNumSamplesInPacket(MediaBufferHelperV3 *buffer) const; uint8_t mChannelCount; uint16_t mCodecDelay; @@ -249,7 +250,9 @@ media_status_t OggSource::start() { if (mStarted) { return AMEDIA_ERROR_INVALID_OPERATION; } - + // initialize buffer group with a single small buffer, but a generous upper limit + mBufferGroup->init(1 /* number of buffers */, 128 /* size */, 64 /* max number of buffers */); + mExtractor->mImpl->setBufferGroup(mBufferGroup); mStarted = true; return AMEDIA_OK; @@ -262,7 +265,7 @@ media_status_t OggSource::stop() { } media_status_t OggSource::read( - MediaBufferBase **out, const ReadOptions *options) { + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -274,26 +277,27 @@ media_status_t OggSource::read( } } - MediaBufferBase *packet; + MediaBufferHelperV3 *packet; media_status_t err = mExtractor->mImpl->readNextPacket(&packet); if (err != AMEDIA_OK) { return err; } + AMediaFormat *meta = packet->meta_data(); #if 0 int64_t timeUs; - if (packet->meta_data().findInt64(kKeyTime, &timeUs)) { + if (AMediaFormat_findInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeStampUs)) { ALOGI("found time = %lld us", timeUs); } else { ALOGI("NO time"); } #endif - packet->meta_data().setInt32(kKeyIsSyncFrame, 1); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); *out = packet; - + ALOGV("returning buffer %p", packet); return AMEDIA_OK; } @@ -304,7 +308,8 @@ MyOggExtractor::MyOggExtractor( const char *mimeType, size_t numHeaders, int64_t seekPreRollUs) - : mSource(source), + : mBufferGroup(NULL), + mSource(source), mOffset(0), mCurGranulePosition(0), mPrevGranulePosition(0), @@ -573,13 +578,13 @@ ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) { return sizeof(header) + page->mNumSegments + totalSize; } -media_status_t MyOpusExtractor::readNextPacket(MediaBufferBase **out) { +media_status_t MyOpusExtractor::readNextPacket(MediaBufferHelperV3 **out) { if (mOffset <= mFirstDataOffset && mStartGranulePosition < 0) { // The first sample might not start at time 0; find out where by subtracting // the number of samples on the first page from the granule position // (position of last complete sample) of the first page. This happens // the first time before we attempt to read a packet from the first page. - MediaBufferBase *mBuf; + MediaBufferHelperV3 *mBuf; uint32_t numSamples = 0; uint64_t curGranulePosition = 0; while (true) { @@ -617,24 +622,25 @@ media_status_t MyOpusExtractor::readNextPacket(MediaBufferBase **out) { int32_t currentPageSamples; // Calculate timestamps by accumulating durations starting from the first sample of a page; // We assume that we only seek to page boundaries. - if ((*out)->meta_data().findInt32(kKeyValidSamples, ¤tPageSamples)) { + AMediaFormat *meta = (*out)->meta_data(); + if (AMediaFormat_getInt32(meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, ¤tPageSamples)) { // first packet in page if (mOffset == mFirstDataOffset) { currentPageSamples -= mStartGranulePosition; - (*out)->meta_data().setInt32(kKeyValidSamples, currentPageSamples); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, currentPageSamples); } mCurGranulePosition = mCurrentPage.mGranulePosition - currentPageSamples; } int64_t timeUs = getTimeUsOfGranule(mCurGranulePosition); - (*out)->meta_data().setInt64(kKeyTime, timeUs); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); uint32_t frames = getNumSamplesInPacket(*out); mCurGranulePosition += frames; return AMEDIA_OK; } -uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferBase *buffer) const { +uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferHelperV3 *buffer) const { if (buffer == NULL || buffer->range_length() < 1) { return 0; } @@ -680,10 +686,66 @@ uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferBase *buffer) const { return numSamples; } -media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcVorbisTimestamp) { +/* + * basic mediabuffer implementation used during initial parsing of the + * header packets, which happens before we have a buffer group + */ +class StandAloneMediaBuffer : public MediaBufferHelperV3 { +private: + void *mData; + size_t mSize; + size_t mOffset; + size_t mLength; + AMediaFormat *mFormat; +public: + StandAloneMediaBuffer(size_t size) : MediaBufferHelperV3(NULL) { + mSize = size; + mData = malloc(mSize); + mOffset = 0; + mLength = mSize; + mFormat = AMediaFormat_new(); + ALOGV("created standalone media buffer %p of size %zu", this, mSize); + } + + ~StandAloneMediaBuffer() override { + free(mData); + AMediaFormat_delete(mFormat); + ALOGV("deleted standalone media buffer %p of size %zu", this, mSize); + } + + void release() override { + delete this; + } + + void* data() override { + return mData; + } + + size_t size() override { + return mSize; + } + + size_t range_offset() override { + return mOffset; + } + + size_t range_length() override { + return mLength; + } + + void set_range(size_t offset, size_t length) override { + mOffset = offset; + mLength = length; + } + AMediaFormat *meta_data() override { + return mFormat; + } +}; + +media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelperV3 **out, bool calcVorbisTimestamp) { *out = NULL; - MediaBufferBase *buffer = NULL; + MediaBufferHelperV3 *buffer = NULL; int64_t timeUs = -1; for (;;) { @@ -719,7 +781,13 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcV ALOGE("b/36592202"); return AMEDIA_ERROR_MALFORMED; } - MediaBufferBase *tmp = MediaBufferBase::Create(fullSize); + MediaBufferHelperV3 *tmp; + if (mBufferGroup) { + mBufferGroup->acquire_buffer(&tmp, false, fullSize); + ALOGV("acquired buffer %p from group", tmp); + } else { + tmp = new StandAloneMediaBuffer(fullSize); + } if (tmp == NULL) { if (buffer != NULL) { buffer->release(); @@ -727,6 +795,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcV ALOGE("b/36592202"); return AMEDIA_ERROR_MALFORMED; } + AMediaFormat_clear(tmp->meta_data()); if (buffer != NULL) { memcpy(tmp->data(), buffer->data(), buffer->range_length()); tmp->set_range(0, buffer->range_length()); @@ -756,8 +825,9 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcV // We've just read the entire packet. if (mFirstPacketInPage) { - buffer->meta_data().setInt32( - kKeyValidSamples, mCurrentPageSamples); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt32( + meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, mCurrentPageSamples); mFirstPacketInPage = false; } @@ -778,7 +848,8 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcV mCurrentPage.mPrevPacketPos += actualBlockSize / 2; mCurrentPage.mPrevPacketSize = curBlockSize; } - buffer->meta_data().setInt64(kKeyTime, timeUs); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); } *out = buffer; @@ -824,11 +895,13 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcV // is already complete. if (timeUs >= 0) { - buffer->meta_data().setInt64(kKeyTime, timeUs); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); } - buffer->meta_data().setInt32( - kKeyValidSamples, mCurrentPageSamples); + AMediaFormat *meta = buffer->meta_data(); + AMediaFormat_setInt32( + meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, mCurrentPageSamples); mFirstPacketInPage = false; *out = buffer; @@ -843,7 +916,7 @@ status_t MyOggExtractor::init() { AMediaFormat_setString(mMeta, AMEDIAFORMAT_KEY_MIME, mMimeType); media_status_t err; - MediaBufferBase *packet; + MediaBufferHelperV3 *packet; for (size_t i = 0; i < mNumHeaders; ++i) { // ignore timestamp for configuration packets if ((err = _readNextPacket(&packet, /* calcVorbisTimestamp = */ false)) != AMEDIA_OK) { @@ -920,7 +993,7 @@ void MyOggExtractor::buildTableOfContents() { } } -int32_t MyOggExtractor::getPacketBlockSize(MediaBufferBase *buffer) { +int32_t MyOggExtractor::getPacketBlockSize(MediaBufferHelperV3 *buffer) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -960,7 +1033,7 @@ int64_t MyOpusExtractor::getTimeUsOfGranule(uint64_t granulePos) const { return pcmSamplePosition * 1000000ll / kOpusSampleRate; } -media_status_t MyOpusExtractor::verifyHeader(MediaBufferBase *buffer, uint8_t type) { +media_status_t MyOpusExtractor::verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type) { switch (type) { // there are actually no header types defined in the Opus spec; we choose 1 and 3 to mean // header and comments such that we can share code with MyVorbisExtractor. @@ -973,7 +1046,7 @@ media_status_t MyOpusExtractor::verifyHeader(MediaBufferBase *buffer, uint8_t ty } } -media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferBase *buffer) { +media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferHelperV3 *buffer) { const size_t kOpusHeaderSize = 19; const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -1001,7 +1074,7 @@ media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferBase *buffer) { return AMEDIA_OK; } -media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) { +media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferHelperV3 *buffer) { // add artificial framing bit so we can reuse _vorbis_unpack_comment int32_t commentSize = buffer->range_length() + 1; auto tmp = heapbuffer(commentSize); @@ -1094,7 +1167,7 @@ media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) { } media_status_t MyVorbisExtractor::verifyHeader( - MediaBufferBase *buffer, uint8_t type) { + MediaBufferHelperV3 *buffer, uint8_t type) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -1262,7 +1335,7 @@ size_t OggExtractor::countTracks() { return mInitCheck != OK ? 0 : 1; } -MediaTrackHelperV2 *OggExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *OggExtractor::getTrack(size_t index) { if (index >= 1) { return NULL; } @@ -1284,13 +1357,13 @@ media_status_t OggExtractor::getMetaData(AMediaFormat *meta) { return mImpl->getFileMetaData(meta); } -static CMediaExtractorV2* CreateExtractor( +static CMediaExtractorV3* CreateExtractor( CDataSource *source, void *) { - return wrapV2(new OggExtractor(new DataSourceHelper(source))); + return wrapV3(new OggExtractor(new DataSourceHelper(source))); } -static CreatorFuncV2 Sniff( +static CreatorFuncV3 Sniff( CDataSource *source, float *confidence, void **, @@ -1311,11 +1384,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("8cc5cd06-f772-495e-8a62-cba9649374e9"), 1, // version "Ogg Extractor", - { .v2 = Sniff } + { .v3 = Sniff } }; } diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h index cd674f3e5b..97506ad168 100644 --- a/media/extractors/ogg/OggExtractor.h +++ b/media/extractors/ogg/OggExtractor.h @@ -31,11 +31,11 @@ class String8; struct MyOggExtractor; struct OggSource; -struct OggExtractor : public MediaExtractorPluginHelperV2 { +struct OggExtractor : public MediaExtractorPluginHelperV3 { explicit OggExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV2 *getTrack(size_t index); + virtual MediaTrackHelperV3 *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 5f2e60123e..ef252f44e6 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -245,6 +245,9 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption if (format->mFormat->findInt32("crypto-skip-byte-block", &val32)) { meta.setInt32(kKeySkipByteBlock, val32); } + if (format->mFormat->findInt32("valid-samples", &val32)) { + meta.setInt32(kKeyValidSamples, val32); + } sp valbuf; if (format->mFormat->findBuffer("crypto-plain-sizes", &valbuf)) { meta.setData(kKeyPlainSizes, diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index cfa9fd9041..5e8d173825 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -613,13 +613,14 @@ static std::vector> int32Mappings { { "crypto-default-iv-size", kKeyCryptoDefaultIVSize }, { "crypto-encrypted-byte-block", kKeyEncryptedByteBlock }, { "crypto-skip-byte-block", kKeySkipByteBlock }, + { "frame-count", kKeyFrameCount }, { "max-bitrate", kKeyMaxBitRate }, { "pcm-big-endian", kKeyPcmBigEndian }, { "temporal-layer-count", kKeyTemporalLayerCount }, { "temporal-layer-id", kKeyTemporalLayerId }, { "thumbnail-width", kKeyThumbnailWidth }, { "thumbnail-height", kKeyThumbnailHeight }, - { "frame-count", kKeyFrameCount }, + { "valid-samples", kKeyValidSamples }, } }; diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 8f3a9f347b..92d3aefa99 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -377,6 +377,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_TIME_US = "timeUs"; EXPORT const char* AMEDIAFORMAT_KEY_TITLE = "title"; EXPORT const char* AMEDIAFORMAT_KEY_TRACK_ID = "track-id"; EXPORT const char* AMEDIAFORMAT_KEY_TRACK_INDEX = "track-index"; +EXPORT const char* AMEDIAFORMAT_KEY_VALID_SAMPLES = "valid-samples"; EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width"; EXPORT const char* AMEDIAFORMAT_KEY_YEAR = "year"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 6e7e0f99a6..2551228c04 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -226,6 +226,7 @@ extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TITLE __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_VALID_SAMPLES __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_YEAR __INTRODUCED_IN(29); #endif /* __ANDROID_API__ >= 29 */ diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index d24cc9b029..c50084eb7d 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -140,6 +140,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_TIME_US; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_INDEX; # var introduced=28 AMEDIAFORMAT_KEY_TRACK_ID; # var introduced=28 + AMEDIAFORMAT_KEY_VALID_SAMPLES; # var introduced=29 AMEDIAFORMAT_KEY_WIDTH; # var introduced=21 AMEDIAFORMAT_KEY_YEAR; # var introduced=29 AMediaCodecActionCode_isRecoverable; # introduced=28 diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index dddd960fbd..336bbe85d7 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -41,7 +41,6 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy # extractor libraries LOCAL_REQUIRED_MODULES += \ libmpeg2extractor \ - liboggextractor \ LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ -- GitLab From 61cf6c171c70c771c8de83549d99a979d37b5e4e Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 10 Dec 2018 14:00:29 -0800 Subject: [PATCH 0612/1530] audio: add test_return_stop.cpp Test returning a STOP code from the callback. Bug: 120845500 Test: this is a test Change-Id: I88aa46b356443ab055c194cb430d93618980894d --- media/libaaudio/tests/Android.bp | 12 + media/libaaudio/tests/test_return_stop.cpp | 284 +++++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 media/libaaudio/tests/test_return_stop.cpp diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index 319467ef1b..cb243a0016 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -184,3 +184,15 @@ cc_test { "libutils", ], } + +cc_test { + name: "test_return_stop", + defaults: ["libaaudio_tests_defaults"], + srcs: ["test_return_stop.cpp"], + shared_libs: [ + "libaaudio", + "libbinder", + "libcutils", + "libutils", + ], +} diff --git a/media/libaaudio/tests/test_return_stop.cpp b/media/libaaudio/tests/test_return_stop.cpp new file mode 100644 index 0000000000..f34c3c8240 --- /dev/null +++ b/media/libaaudio/tests/test_return_stop.cpp @@ -0,0 +1,284 @@ +/* + * 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. + */ + +/** + * Return stop from the callback. + * Expect the callback to cease. + * Check the logcat for bad behavior. + */ + +#include +#include +#include + +#include + +#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000) +#define STOP_AT_MSEC 1000 +#define LOOP_DURATION_MSEC 4000 +#define SLEEP_DURATION_MSEC 200 + +static void s_myErrorCallbackProc( + AAudioStream *stream, + void *userData, + aaudio_result_t error); + +struct AudioEngine { + AAudioStreamBuilder *builder = nullptr; + AAudioStream *stream = nullptr; + std::thread *thread = nullptr; + int32_t stopAtFrame = 0; + bool stopped = false; + // These counters are read and written by the callback and the main thread. + std::atomic framesRead{}; + std::atomic startingFramesRead{}; + std::atomic framesCalled{}; + std::atomic callbackCount{}; + std::atomic callbackCountAfterStop{}; + + void reset() { + framesRead.store(0); + startingFramesRead.store(0); + framesCalled.store(0); + callbackCount.store(0); + callbackCountAfterStop.store(0); + stopped = false; + } +}; + +// Callback function that fills the audio output buffer. +static aaudio_data_callback_result_t s_myDataCallbackProc( + AAudioStream *stream, + void *userData, + void *audioData, + int32_t numFrames +) { + (void) audioData; + (void) numFrames; + AudioEngine *engine = (struct AudioEngine *)userData; + engine->callbackCount++; + if (engine->stopped) { + engine->callbackCountAfterStop++; + } + + engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream); + if (engine->startingFramesRead == 0) { + engine->startingFramesRead.store(engine->framesRead.load()); + } + engine->framesCalled += numFrames; + if (engine->framesCalled >= engine->stopAtFrame) { + engine->stopped = true; + return AAUDIO_CALLBACK_RESULT_STOP; + } else { + return AAUDIO_CALLBACK_RESULT_CONTINUE; + } +} + +static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine, + aaudio_direction_t direction, + aaudio_sharing_mode_t sharingMode, + aaudio_performance_mode_t perfMode) { + // Use an AAudioStreamBuilder to contain requested parameters. + aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder); + if (result != AAUDIO_OK) { + printf("AAudio_createStreamBuilder returned %s", + AAudio_convertResultToText(result)); + return result; + } + + // Request stream properties. + AAudioStreamBuilder_setFormat(engine->builder, AAUDIO_FORMAT_PCM_FLOAT); + AAudioStreamBuilder_setPerformanceMode(engine->builder, perfMode); + AAudioStreamBuilder_setSharingMode(engine->builder, sharingMode); + AAudioStreamBuilder_setDirection(engine->builder, direction); + AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine); + AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine); + + // Create an AAudioStream using the Builder. + result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream); + if (result != AAUDIO_OK) { + printf("AAudioStreamBuilder_openStream returned %s", + AAudio_convertResultToText(result)); + return result; + } + + return result; +} + +static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) { + aaudio_result_t result = AAUDIO_OK; + if (engine->stream != nullptr) { + result = AAudioStream_close(engine->stream); + if (result != AAUDIO_OK) { + printf("AAudioStream_close returned %s\n", + AAudio_convertResultToText(result)); + } + engine->stream = nullptr; + } + AAudioStreamBuilder_delete(engine->builder); + engine->builder = nullptr; + return result; +} + +static void s_myErrorCallbackProc( + AAudioStream *stream __unused, + void *userData __unused, + aaudio_result_t error) { + printf("%s() - error = %d\n", __func__, error); +} + +void usage() { + printf("test_return_stop [-i] [-x] [-n] [-c]\n"); + printf(" -i direction INPUT, otherwise OUTPUT\n"); + printf(" -x sharing mode EXCLUSIVE, otherwise SHARED\n"); + printf(" -n performance mode NONE, otherwise LOW_LATENCY\n"); + printf(" -c always return CONTINUE from callback, not STOP\n"); +} + +int main(int argc, char **argv) { + (void) argc; + (void) argv; + struct AudioEngine engine; + aaudio_sharing_mode_t sharingMode = AAUDIO_SHARING_MODE_SHARED; + aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; + aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT; + aaudio_result_t result = AAUDIO_OK; + bool alwaysContinue = false; + int errorCount = 0; + int callbackResult = EXIT_SUCCESS; + + // Make printf print immediately so that debug info is not stuck + // in a buffer if we hang or crash. + setvbuf(stdout, nullptr, _IONBF, (size_t) 0); + + printf("Test Return Stop V1.0\n"); + printf("Wait for a few seconds.\n"); + printf("You should see callbackCount and framesRead stop advancing\n"); + printf("when callbackCount reaches %d msec\n", STOP_AT_MSEC); + printf("\n"); + + for (int i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] == '-') { + char option = arg[1]; + switch (option) { + case 'c': + alwaysContinue = true; + break; + case 'i': + direction = AAUDIO_DIRECTION_INPUT; + break; + case 'n': + perfMode = AAUDIO_PERFORMANCE_MODE_NONE; + break; + case 'x': + sharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; + break; + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } else { + usage(); + exit(EXIT_FAILURE); + break; + } + } + + result = s_OpenAudioStream(&engine, direction, sharingMode, perfMode); + if (result != AAUDIO_OK) { + printf("s_OpenAudioStream returned %s", + AAudio_convertResultToText(result)); + errorCount++; + } + + int32_t framesPerBurst = AAudioStream_getFramesPerBurst(engine.stream); + // Check to see what kind of stream we actually got. + int32_t deviceId = AAudioStream_getDeviceId(engine.stream); + aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream); + printf("-------- opened: deviceId = %3d, framesPerBurst = %3d, perfMode = %d\n", + deviceId, framesPerBurst, actualPerfMode); + + // Calculate how many callbacks needed. + if (alwaysContinue) { + engine.stopAtFrame = INT32_MAX; + } else { + int32_t sampleRate = AAudioStream_getSampleRate(engine.stream); + engine.stopAtFrame = STOP_AT_MSEC * sampleRate / 1000; + } + + for (int loops = 0; loops < 2 && result == AAUDIO_OK; loops++) { + engine.reset(); + + // Start stream. + result = AAudioStream_requestStart(engine.stream); + printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result); + if (result != AAUDIO_OK) { + printf("ERROR - AAudioStream_requestStart returned %s", + AAudio_convertResultToText(result)); + errorCount++; + break; + } + + if (result == AAUDIO_OK) { + const int watchLoops = LOOP_DURATION_MSEC / SLEEP_DURATION_MSEC; + for (int i = watchLoops; i > 0; i--) { + printf("playing silence #%02d, framesRead = %7d, framesWritten = %7d," + " framesCalled = %6d, callbackCount = %4d\n", + i, + (int32_t) AAudioStream_getFramesRead(engine.stream), + (int32_t) AAudioStream_getFramesWritten(engine.stream), + engine.framesCalled.load(), + engine.callbackCount.load() + ); + usleep(SLEEP_DURATION_MSEC * 1000); + } + } + + if (engine.stopAtFrame != INT32_MAX) { + callbackResult = (engine.callbackCountAfterStop == 0) ? EXIT_SUCCESS + : EXIT_FAILURE; + if (callbackResult) { + printf("ERROR - Callback count after STOP = %d\n", + engine.callbackCountAfterStop.load()); + errorCount++; + } + } + + if (engine.startingFramesRead.load() == engine.framesRead.load()) { + printf("ERROR - framesRead did not advance across callbacks\n"); + errorCount++; + } + + result = AAudioStream_requestStop(engine.stream); + printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result); + if (result != AAUDIO_OK) { + errorCount++; + } + usleep(SLEEP_DURATION_MSEC * 1000); + printf("getFramesRead() = %d, getFramesWritten() = %d\n", + (int32_t) AAudioStream_getFramesRead(engine.stream), + (int32_t) AAudioStream_getFramesWritten(engine.stream)); + } + + s_CloseAudioStream(&engine); + + printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result)); + printf("test %s\n", errorCount ? "FAILED" : "PASSED"); + + return errorCount ? EXIT_FAILURE : EXIT_SUCCESS; +} -- GitLab From 99896dab5f368916c86acb97e82bf72c86a14922 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Mon, 9 Apr 2018 11:05:33 +0200 Subject: [PATCH 0613/1530] audiopolicy: Fix sofware bridge creation for AudioHAL supporting routing APIs AudioPolicy roughly checks if HAL supports routing APIs 3.0 to enable or not software bridge in case of device to device patch requests. However, Audio HAL declares in XML file routes supported. AudioPolicy shall refine its decision upon information available in the policy configuration file. Test: Audio smoke tests. Test: CTS tests for AudioRecord, AudioTrack and AudioEffect Change-Id: Idc2e1ccd7d9fb6385f9a05ee002bec045e608232 Signed-off-by: Francois Gaffie --- .../managerdefinitions/include/AudioRoute.h | 13 +++++++++++++ .../common/managerdefinitions/include/HwModule.h | 11 +++++++++++ .../common/managerdefinitions/src/AudioRoute.cpp | 15 +++++++++++++++ .../common/managerdefinitions/src/HwModule.cpp | 9 +++++++++ .../managerdefault/AudioPolicyManager.cpp | 4 +++- 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioRoute.h b/services/audiopolicy/common/managerdefinitions/include/AudioRoute.h index 330f1d4d09..0357ff4110 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioRoute.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioRoute.h @@ -46,6 +46,19 @@ public: audio_route_type_t getType() const { return mType; } + /** + * @brief supportsPatch checks if an audio patch is supported by a Route declared in + * the audio_policy_configuration.xml file. + * If the patch is supported natively by an AudioHAL (which supports of course Routing API 3.0), + * audiopolicy will not request AudioFlinger to use a software bridge to realize a patch + * between 2 ports. + * @param srcPort (aka the source) to be considered + * @param dstPort (aka the sink) to be considered + * @return true if the audio route supports the connection between the sink and the source, + * false otherwise + */ + bool supportsPatch(const sp &srcPort, const sp &dstPort) const; + void dump(String8 *dst, int spaces) const; private: diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h index 65604317b7..2b57fa9dac 100644 --- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -81,6 +81,17 @@ public: return mPorts.findByTagName(tagName); } + /** + * @brief supportsPatch checks if an audio patch between 2 ports beloging to this HwModule + * is supported by a HwModule. The ports and the route shall be declared in the + * audio_policy_configuration.xml file. + * @param srcPort (aka the source) to be considered + * @param dstPort (aka the sink) to be considered + * @return true if the HwModule supports the connection between the sink and the source, + * false otherwise + */ + bool supportsPatch(const sp &srcPort, const sp &dstPort) const; + // TODO remove from here (split serialization) void dump(String8 *dst) const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp index c1fe5b04be..79f0919a84 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioRoute.cpp @@ -37,4 +37,19 @@ void AudioRoute::dump(String8 *dst, int spaces) const dst->append("\n"); } +bool AudioRoute::supportsPatch(const sp &srcPort, const sp &dstPort) const +{ + if (mSink == 0 || dstPort == 0 || dstPort != mSink) { + return false; + } + ALOGV("%s: sinks %s matching", __FUNCTION__, mSink->getTagName().string()); + for (const auto &sourcePort : mSources) { + if (sourcePort == srcPort) { + ALOGV("%s: sources %s matching", __FUNCTION__, sourcePort->getTagName().string()); + return true; + } + } + return false; +} + } diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 92bc595494..59ba479b38 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -218,6 +218,15 @@ void HwModule::setHandle(audio_module_handle_t handle) { mHandle = handle; } +bool HwModule::supportsPatch(const sp &srcPort, const sp &dstPort) const { + for (const auto &route : mRoutes) { + if (route->supportsPatch(srcPort, dstPort)) { + return true; + } + } + return false; +} + void HwModule::dump(String8 *dst) const { dst->appendFormat(" - name: %s\n", getName()); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 02f6f5a2b4..d13253dc12 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3083,8 +3083,10 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, // create a software bridge in PatchPanel if: // - source and sink devices are on different HW modules OR // - audio HAL version is < 3.0 + // - audio HAL version is >= 3.0 but no route has been declared between devices if (!srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) || - (srcDeviceDesc->mModule->getHalVersionMajor() < 3)) { + (srcDeviceDesc->mModule->getHalVersionMajor() < 3) || + !srcDeviceDesc->mModule->supportsPatch(srcDeviceDesc, sinkDeviceDesc)) { // support only one sink device for now to simplify output selection logic if (patch->num_sinks > 1) { return INVALID_OPERATION; -- GitLab From 83d789d6234f5cd5eb90f02a47ab04d4f6ea8299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 29 May 2018 13:29:19 +0200 Subject: [PATCH 0614/1530] audiopolicy: apm: connectAudioSource: use Sw brigding according to hal support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if the implementation is not ready, this patch updates the conditions of audio patch creation delegation to audio hal not only if HAL is at least supporting 3.0 routing API but also has declared within policy configuration file that a route between Device port is supported. If the route is not declared in configuration file, Audio HAL is not able to do the connexion, let AudioFlinger do the the Sw Bridging. Test: Audio smoke tests. Test: CTS tests for AudioRecord, AudioTrack and AudioEffect Change-Id: I6ed11be04473be2edfcf16e922d1c6864184b6cd Signed-off-by: François Gaffie --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d13253dc12..ec1b9ea8a6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3430,14 +3430,14 @@ status_t AudioPolicyManager::connectAudioSource(const sp audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - if (srcDeviceDesc->getAudioPort()->mModule->getHandle() == - sinkDeviceDesc->getAudioPort()->mModule->getHandle() && + if (srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) && srcDeviceDesc->getAudioPort()->mModule->getHalVersionMajor() >= 3 && + sinkDeviceDesc->mModule->supportsPatch(srcDeviceDesc, sinkDeviceDesc) && srcDeviceDesc->getAudioPort()->mGains.size() > 0) { - ALOGV("%s AUDIO_DEVICE_API_VERSION_3_0", __FUNCTION__); + ALOGV("%s Device to Device route supported by >=3.0 HAL", __FUNCTION__); // TODO: may explicitly specify whether we should use HW or SW patch - // create patch between src device and output device - // create Hwoutput and add to mHwOutputs + // create patch between src device and output device + // create Hwoutput and add to mHwOutputs } else { audio_attributes_t resultAttr; audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; -- GitLab From 2b167173e145d8846c9a248039f0f4dc423ade73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 4 Jun 2018 16:05:14 +0200 Subject: [PATCH 0615/1530] audiopolicy: update type convertion library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: Audio smoke tests. Test: CTS tests for AudioRecord, AudioTrack and AudioEffect Change-Id: I3ed8c7d7ee8a97f0bd41a5ae9f28d673994d7f07 Signed-off-by: François Gaffie --- media/libmedia/TypeConverter.cpp | 18 ++ media/libmedia/include/media/TypeConverter.h | 168 +++++++++++------- .../include/TypeConverter.h | 24 +-- .../managerdefinitions/src/Serializer.cpp | 2 +- 4 files changed, 129 insertions(+), 83 deletions(-) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index fb861d766c..b5a7172e64 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -285,6 +285,7 @@ const GainModeConverter::Table GainModeConverter::mTable[] = { template <> const StreamTypeConverter::Table StreamTypeConverter::mTable[] = { + MAKE_STRING_FROM_ENUM(AUDIO_STREAM_DEFAULT), MAKE_STRING_FROM_ENUM(AUDIO_STREAM_VOICE_CALL), MAKE_STRING_FROM_ENUM(AUDIO_STREAM_SYSTEM), MAKE_STRING_FROM_ENUM(AUDIO_STREAM_RING), @@ -361,6 +362,22 @@ const SourceTypeConverter::Table SourceTypeConverter::mTable[] = { TERMINATOR }; +template <> +const AudioFlagConverter::Table AudioFlagConverter::mTable[] = { + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NONE), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_AUDIBILITY_ENFORCED), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_SECURE), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_SCO), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_BEACON), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_HW_AV_SYNC), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_HW_HOTWORD), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_BYPASS_MUTE), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_LOW_LATENCY), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER), + TERMINATOR +}; + template class TypeConverter; template class TypeConverter; template class TypeConverter; @@ -374,6 +391,7 @@ template class TypeConverter; template class TypeConverter; template class TypeConverter; template class TypeConverter; +template class TypeConverter; bool deviceFromString(const std::string& literalDevice, audio_devices_t& device) { return InputDeviceConverter::fromString(literalDevice, device) || diff --git a/media/libmedia/include/media/TypeConverter.h b/media/libmedia/include/media/TypeConverter.h index 86f0d4c275..418e09c291 100644 --- a/media/libmedia/include/media/TypeConverter.h +++ b/media/libmedia/include/media/TypeConverter.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -30,77 +31,55 @@ namespace android { -struct SampleRateTraits -{ - typedef uint32_t Type; - typedef SortedVector Collection; -}; -struct DeviceTraits -{ - typedef audio_devices_t Type; - typedef Vector Collection; -}; -struct OutputDeviceTraits : public DeviceTraits {}; -struct InputDeviceTraits : public DeviceTraits {}; -struct OutputFlagTraits -{ - typedef audio_output_flags_t Type; - typedef Vector Collection; -}; -struct InputFlagTraits +template +struct DefaultTraits { - typedef audio_input_flags_t Type; - typedef Vector Collection; + typedef T Type; + typedef std::vector Collection; + static void add(Collection &collection, Type value) + { + collection.push_back(value); + } }; -struct FormatTraits +template +struct VectorTraits { - typedef audio_format_t Type; + typedef T Type; typedef Vector Collection; + static void add(Collection &collection, Type value) + { + collection.add(value); + } }; -struct ChannelTraits +template +struct SortedVectorTraits { - typedef audio_channel_mask_t Type; + typedef T Type; typedef SortedVector Collection; + static void add(Collection &collection, Type value) + { + collection.add(value); + } }; + +using SampleRateTraits = SortedVectorTraits; +using DeviceTraits = DefaultTraits; +struct OutputDeviceTraits : public DeviceTraits {}; +struct InputDeviceTraits : public DeviceTraits {}; +using ChannelTraits = SortedVectorTraits; struct OutputChannelTraits : public ChannelTraits {}; struct InputChannelTraits : public ChannelTraits {}; struct ChannelIndexTraits : public ChannelTraits {}; -struct GainModeTraits -{ - typedef audio_gain_mode_t Type; - typedef Vector Collection; -}; -struct StreamTraits -{ - typedef audio_stream_type_t Type; - typedef Vector Collection; -}; -struct AudioModeTraits -{ - typedef audio_mode_t Type; - typedef Vector Collection; -}; -struct AudioContentTraits -{ - typedef audio_content_type_t Type; - typedef Vector Collection; -}; -struct UsageTraits -{ - typedef audio_usage_t Type; - typedef Vector Collection; -}; -struct SourceTraits -{ - typedef audio_source_t Type; - typedef Vector Collection; -}; -template -struct DefaultTraits -{ - typedef T Type; - typedef Vector Collection; -}; +using InputFlagTraits = DefaultTraits; +using OutputFlagTraits = DefaultTraits; +using FormatTraits = VectorTraits; +using GainModeTraits = DefaultTraits; +using StreamTraits = DefaultTraits; +using AudioModeTraits = DefaultTraits; +using AudioContentTraits = DefaultTraits; +using UsageTraits = DefaultTraits; +using SourceTraits = DefaultTraits; +struct AudioFlagTraits : public DefaultTraits {}; template static void collectionFromString(const std::string &str, typename Traits::Collection &collection, @@ -110,7 +89,7 @@ static void collectionFromString(const std::string &str, typename Traits::Collec for (const char *cstr = strtok(literal, del); cstr != NULL; cstr = strtok(NULL, del)) { typename Traits::Type value; if (utilities::convertTo(cstr, value)) { - collection.add(value); + Traits::add(collection, value); } } free(literal); @@ -181,7 +160,7 @@ inline void TypeConverter::collectionFromString(const std::string &str, for (const char *cstr = strtok(literal, del); cstr != NULL; cstr = strtok(NULL, del)) { typename Traits::Type value; if (fromString(cstr, value)) { - collection.add(value); + Traits::add(collection, value); } } free(literal); @@ -234,6 +213,7 @@ typedef TypeConverter AudioModeConverter; typedef TypeConverter AudioContentTypeConverter; typedef TypeConverter UsageTypeConverter; typedef TypeConverter SourceTypeConverter; +typedef TypeConverter AudioFlagConverter; template<> const OutputDeviceConverter::Table OutputDeviceConverter::mTable[]; template<> const InputDeviceConverter::Table InputDeviceConverter::mTable[]; @@ -249,6 +229,7 @@ template<> const AudioModeConverter::Table AudioModeConverter::mTable[]; template<> const AudioContentTypeConverter::Table AudioContentTypeConverter::mTable[]; template<> const UsageTypeConverter::Table UsageTypeConverter::mTable[]; template<> const SourceTypeConverter::Table SourceTypeConverter::mTable[]; +template<> const AudioFlagConverter::Table AudioFlagConverter::mTable[]; bool deviceFromString(const std::string& literalDevice, audio_devices_t& device); @@ -274,6 +255,69 @@ InputChannelTraits::Collection inputChannelMasksFromString( OutputChannelTraits::Collection outputChannelMasksFromString( const std::string &outChannels, const char *del = AudioParameter::valueListSeparator); +static inline std::string toString(audio_usage_t usage) +{ + std::string usageLiteral; + if (!android::UsageTypeConverter::toString(usage, usageLiteral)) { + ALOGV("failed to convert usage: %d", usage); + return "AUDIO_USAGE_UNKNOWN"; + } + return usageLiteral; +} + +static inline std::string toString(audio_content_type_t content) +{ + std::string contentLiteral; + if (!android::AudioContentTypeConverter::toString(content, contentLiteral)) { + ALOGV("failed to convert content type: %d", content); + return "AUDIO_CONTENT_TYPE_UNKNOWN"; + } + return contentLiteral; +} + +static inline std::string toString(audio_stream_type_t stream) +{ + std::string streamLiteral; + if (!android::StreamTypeConverter::toString(stream, streamLiteral)) { + ALOGV("failed to convert stream: %d", stream); + return "AUDIO_STREAM_DEFAULT"; + } + return streamLiteral; +} + +static inline std::string toString(audio_source_t source) +{ + std::string sourceLiteral; + if (!android::SourceTypeConverter::toString(source, sourceLiteral)) { + ALOGV("failed to convert source: %d", source); + return "AUDIO_SOURCE_DEFAULT"; + } + return sourceLiteral; +} + +static inline std::string toString(const audio_attributes_t &attributes) +{ + std::ostringstream result; + result << "{ Content type: " << toString(attributes.content_type) + << " Usage: " << toString(attributes.usage) + << " Source: " << toString(attributes.source) + << " Flags: " << attributes.flags + << " Tags: " << attributes.tags + << " }"; + + return result.str(); +} + +static inline std::string toString(audio_mode_t mode) +{ + std::string modeLiteral; + if (!android::AudioModeConverter::toString(mode, modeLiteral)) { + ALOGV("failed to convert mode: %d", mode); + return "AUDIO_MODE_INVALID"; + } + return modeLiteral; +} + }; // namespace android #endif /*ANDROID_TYPE_CONVERTER_H_*/ diff --git a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h index 63c19d117b..6b0476c3ec 100644 --- a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h +++ b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h @@ -23,26 +23,10 @@ namespace android { -struct DeviceCategoryTraits -{ - typedef device_category Type; - typedef Vector Collection; -}; -struct MixTypeTraits -{ - typedef int32_t Type; - typedef Vector Collection; -}; -struct RouteFlagTraits -{ - typedef uint32_t Type; - typedef Vector Collection; -}; -struct RuleTraits -{ - typedef uint32_t Type; - typedef Vector Collection; -}; +struct RuleTraits : public DefaultTraits {}; +using DeviceCategoryTraits = DefaultTraits; +struct MixTypeTraits : public DefaultTraits {}; +struct RouteFlagTraits : public DefaultTraits {}; typedef TypeConverter DeviceCategoryConverter; typedef TypeConverter MixTypeConverter; diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 179a678255..930171fb3c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -742,7 +742,7 @@ Return VolumeTraits::deserialize(const xmlNode *cur, } ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast(pointDefinition.get())); - Vector point; + std::vector point; collectionFromString>( reinterpret_cast(pointDefinition.get()), point, ","); if (point.size() != 2) { -- GitLab From aca677cf5fbd2a2d5aaccae3af4da6c1d700eb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 3 May 2018 10:47:50 +0200 Subject: [PATCH 0616/1530] audiopolicy: common: add several helper function on DeviceDesc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Add another DeviceVector constructor to simplify usage -report tagname as port name -migrate API from apm -search/filter helpers Test: Audio smoke tests. Test: CTS tests for AudioRecord, AudioTrack and AudioEffect Change-Id: Ie37a2d9db2e8a15a37743e7129159fa77a4c6b51 Signed-off-by: François Gaffie --- .../managerdefinitions/include/AudioPort.h | 3 +- .../include/DeviceDescriptor.h | 88 +++++++++++++++++-- .../managerdefinitions/include/IOProfile.h | 2 +- .../src/AudioOutputDescriptor.cpp | 4 +- .../src/DeviceDescriptor.cpp | 73 ++++++++++++--- .../managerdefinitions/src/HwModule.cpp | 6 +- .../managerdefinitions/src/Serializer.cpp | 4 +- .../managerdefault/AudioPolicyManager.cpp | 69 +++++++-------- .../managerdefault/AudioPolicyManager.h | 13 +++ 9 files changed, 198 insertions(+), 64 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h index ebb9352603..bb9cad8c6b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -116,6 +116,7 @@ public: audio_module_handle_t getModuleHandle() const; uint32_t getModuleVersionMajor() const; const char *getModuleName() const; + sp getModule() const { return mModule; } bool useInputChannelMask() const { @@ -137,12 +138,12 @@ public: void log(const char* indent) const; AudioGainCollection mGains; // gain controllers - sp mModule; // audio HW module exposing this I/O stream private: void pickChannelMask(audio_channel_mask_t &channelMask, const ChannelsVector &channelMasks) const; void pickSamplingRate(uint32_t &rate,const SampleRateVector &samplingRates) const; + sp mModule; // audio HW module exposing this I/O stream String8 mName; audio_port_type_t mType; audio_port_role_t mRole; diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index 6f99bf3016..d02123c760 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -39,6 +39,8 @@ public: virtual const String8 getTagName() const { return mTagName; } audio_devices_t type() const { return mDeviceType; } + String8 address() const { return mAddress; } + void setAddress(const String8 &address) { mAddress = address; } const FormatVector& encodedFormats() const { return mEncodedFormats; } @@ -57,39 +59,113 @@ public: audio_port_handle_t getId() const; void dump(String8 *dst, int spaces, int index, bool verbose = true) const; void log() const; - - String8 mAddress; + std::string toString() const; private: + String8 mAddress{""}; String8 mTagName; // Unique human readable identifier for a device port found in conf file. audio_devices_t mDeviceType; FormatVector mEncodedFormats; - audio_port_handle_t mId; - -friend class DeviceVector; + audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE; }; class DeviceVector : public SortedVector > { public: DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {} + explicit DeviceVector(const sp& item) : DeviceVector() + { + add(item); + } ssize_t add(const sp& item); void add(const DeviceVector &devices); ssize_t remove(const sp& item); + void remove(const DeviceVector &devices); ssize_t indexOf(const sp& item) const; audio_devices_t types() const { return mDeviceTypes; } // If 'address' is empty, a device with a non-empty address may be returned // if there is no device with the specified 'type' and empty address. - sp getDevice(audio_devices_t type, const String8 &address) const; + sp getDevice(audio_devices_t type, const String8 &address = {}) const; DeviceVector getDevicesFromTypeMask(audio_devices_t types) const; + + /** + * @brief getDeviceFromId + * @param id of the DeviceDescriptor to seach (aka Port handle). + * @return DeviceDescriptor associated to port id if found, nullptr otherwise. If the id is + * equal to AUDIO_PORT_HANDLE_NONE, it also returns a nullptr. + */ sp getDeviceFromId(audio_port_handle_t id) const; sp getDeviceFromTagName(const String8 &tagName) const; DeviceVector getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; audio_devices_t getDeviceTypesFromHwModule(audio_module_handle_t moduleHandle) const; + bool contains(const sp& item) const { return indexOf(item) >= 0; } + + /** + * @brief containsAtLeastOne + * @param devices vector of devices to check against. + * @return true if the DeviceVector contains at list one of the devices from the given vector. + */ + bool containsAtLeastOne(const DeviceVector &devices) const; + + /** + * @brief containsAllDevices + * @param devices vector of devices to check against. + * @return true if the DeviceVector contains all the devices from the given vector + */ + bool containsAllDevices(const DeviceVector &devices) const; + + /** + * @brief filter the devices supported by this collection against another collection + * @param devices to filter against + * @return + */ + DeviceVector filter(const DeviceVector &devices) const; + + /** + * @brief merge two vectors. As SortedVector Implementation is buggy (it does not check the size + * of the destination vector, only of the source, it provides a safe implementation + * @param devices source device vector to merge with + * @return size of the merged vector. + */ + ssize_t merge(const DeviceVector &devices) + { + if (isEmpty()) { + add(devices); + return size(); + } + return SortedVector::merge(devices); + } + + /** + * @brief operator == DeviceVector are equals if all the DeviceDescriptor can be found (aka + * DeviceDescriptor with same type and address) and the vector has same size. + * @param right DeviceVector to compare to. + * @return true if right contains the same device and has the same size. + */ + bool operator==(const DeviceVector &right) const + { + if (size() != right.size()) { + return false; + } + for (const auto &device : *this) { + if (right.indexOf(device) < 0) { + return false; + } + } + return true; + } + + bool operator!=(const DeviceVector &right) const + { + return !operator==(right); + } + + std::string toString() const; + void dump(String8 *dst, const String8 &tag, int spaces = 0, bool verbose = true) const; private: diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index 8ff823860f..ca6ca5671e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -88,7 +88,7 @@ public: bool supportDeviceAddress(const String8 &address) const { - return mSupportedDevices[0]->mAddress == address; + return mSupportedDevices[0]->address() == address; } // chose first device present in mSupportedDevices also part of deviceType diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 4ce6b082c8..97504abce6 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -698,8 +698,8 @@ bool SwAudioOutputCollection::isA2dpOffloadedOnPrimary() const sp primaryOutput = getPrimaryOutput(); if ((primaryOutput != NULL) && (primaryOutput->mProfile != NULL) - && (primaryOutput->mProfile->mModule != NULL)) { - sp primaryHwModule = primaryOutput->mProfile->mModule; + && (primaryOutput->mProfile->getModule() != NULL)) { + sp primaryHwModule = primaryOutput->mProfile->getModule(); Vector > primaryHwModuleOutputProfiles = primaryHwModule->getOutputProfiles(); for (size_t i = 0; i < primaryHwModuleOutputProfiles.size(); i++) { diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 9e5f9440d8..04cbcd141e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -35,7 +35,7 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc AudioPort(String8(""), AUDIO_PORT_TYPE_DEVICE, audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE), - mAddress(""), mTagName(tagName), mDeviceType(type), mEncodedFormats(encodedFormats), mId(0) + mTagName(tagName), mDeviceType(type), mEncodedFormats(encodedFormats) { if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { mAddress = String8("0"); @@ -132,6 +132,13 @@ ssize_t DeviceVector::remove(const sp& item) return ret; } +void DeviceVector::remove(const DeviceVector &devices) +{ + for (const auto& device : devices) { + remove(device); + } +} + DeviceVector DeviceVector::getDevicesFromHwModule(audio_module_handle_t moduleHandle) const { DeviceVector devices; @@ -159,9 +166,9 @@ sp DeviceVector::getDevice(audio_devices_t type, const String8 sp device; for (size_t i = 0; i < size(); i++) { if (itemAt(i)->type() == type) { - if (address == "" || itemAt(i)->mAddress == address) { + if (address == "" || itemAt(i)->address() == address) { device = itemAt(i); - if (itemAt(i)->mAddress == address) { + if (itemAt(i)->address() == address) { break; } } @@ -174,9 +181,11 @@ sp DeviceVector::getDevice(audio_devices_t type, const String8 sp DeviceVector::getDeviceFromId(audio_port_handle_t id) const { - for (const auto& device : *this) { - if (device->getId() == id) { - return device; + if (id != AUDIO_PORT_HANDLE_NONE) { + for (const auto& device : *this) { + if (device->getId() == id) { + return device; + } } } return nullptr; @@ -188,8 +197,8 @@ DeviceVector DeviceVector::getDevicesFromTypeMask(audio_devices_t type) const bool isOutput = audio_is_output_devices(type); type &= ~AUDIO_DEVICE_BIT_IN; for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { - bool curIsOutput = audio_is_output_devices(itemAt(i)->mDeviceType); - audio_devices_t curType = itemAt(i)->mDeviceType & ~AUDIO_DEVICE_BIT_IN; + bool curIsOutput = audio_is_output_devices(itemAt(i)->type()); + audio_devices_t curType = itemAt(i)->type() & ~AUDIO_DEVICE_BIT_IN; if ((isOutput == curIsOutput) && ((type & curType) != 0)) { devices.add(itemAt(i)); type &= ~curType; @@ -251,8 +260,7 @@ void DeviceDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, // without the test? // This has been demonstrated to NOT be true (at start up) // ALOG_ASSERT(mModule != NULL); - dstConfig->ext.device.hw_module = - mModule != 0 ? mModule->getHandle() : AUDIO_MODULE_HANDLE_NONE; + dstConfig->ext.device.hw_module = getModuleHandle(); (void)audio_utils_strlcpy_zerofill(dstConfig->ext.device.address, mAddress.string()); } @@ -263,7 +271,7 @@ void DeviceDescriptor::toAudioPort(struct audio_port *port) const port->id = mId; toAudioPortConfig(&port->active_config); port->ext.device.type = mDeviceType; - port->ext.device.hw_module = mModule->getHandle(); + port->ext.device.hw_module = getModuleHandle(); (void)audio_utils_strlcpy_zerofill(port->ext.device.address, mAddress.string()); } @@ -294,6 +302,49 @@ void DeviceDescriptor::dump(String8 *dst, int spaces, int index, bool verbose) c AudioPort::dump(dst, spaces, verbose); } +std::string DeviceDescriptor::toString() const +{ + std::stringstream sstream; + sstream << "type:0x" << std::hex << type() << ",@:" << mAddress; + return sstream.str(); +} + +std::string DeviceVector::toString() const +{ + if (isEmpty()) { + return {"AUDIO_DEVICE_NONE"}; + } + std::string result = {"{"}; + for (const auto &device : *this) { + if (device != *begin()) { + result += ";"; + } + result += device->toString(); + } + return result + "}"; +} + +DeviceVector DeviceVector::filter(const DeviceVector &devices) const +{ + DeviceVector filteredDevices; + for (const auto &device : *this) { + if (devices.contains(device)) { + filteredDevices.add(device); + } + } + return filteredDevices; +} + +bool DeviceVector::containsAtLeastOne(const DeviceVector &devices) const +{ + return !filter(devices).isEmpty(); +} + +bool DeviceVector::containsAllDevices(const DeviceVector &devices) const +{ + return filter(devices).size() == devices.size(); +} + void DeviceDescriptor::log() const { std::string device; diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 59ba479b38..80af88d53e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -51,7 +51,7 @@ status_t HwModule::addOutputProfile(const String8& name, const audio_config_t *c config->sample_rate)); sp devDesc = new DeviceDescriptor(device); - devDesc->mAddress = address; + devDesc->setAddress(address); profile->addSupportedDevice(devDesc); return addOutputProfile(profile); @@ -113,7 +113,7 @@ status_t HwModule::addInputProfile(const String8& name, const audio_config_t *co config->sample_rate)); sp devDesc = new DeviceDescriptor(device); - devDesc->mAddress = address; + devDesc->setAddress(address); profile->addSupportedDevice(devDesc); ALOGV("addInputProfile() name %s rate %d mask 0x%08x", @@ -296,7 +296,7 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices sp devDesc = new DeviceDescriptor(device); devDesc->setName(String8(device_name)); - devDesc->mAddress = address; + devDesc->setAddress(address); return devDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 930171fb3c..1154654f85 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -516,7 +516,7 @@ Return DevicePortTraits::deserialize(const xmlNode *c std::string address = getXmlAttribute(cur, Attributes::address); if (!address.empty()) { ALOGV("%s: address=%s for %s", __func__, address.c_str(), name.c_str()); - deviceDesc->mAddress = String8(address.c_str()); + deviceDesc->setAddress(String8(address.c_str())); } AudioProfileTraits::Collection profiles; @@ -535,7 +535,7 @@ Return DevicePortTraits::deserialize(const xmlNode *c return Status::fromStatusT(status); } ALOGV("%s: adding device tag %s type %08x address %s", __func__, - deviceDesc->getName().string(), type, deviceDesc->mAddress.string()); + deviceDesc->getName().string(), type, deviceDesc->address().string()); return deviceDesc; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ec1b9ea8a6..64a2b8a506 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -149,13 +149,13 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // Before checking outputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the outputs...) - broadcastDeviceConnectionState(device, state, devDesc->mAddress); + broadcastDeviceConnectionState(device, state, devDesc->address()); - if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) { + if (checkOutputsForDevice(devDesc, state, outputs, devDesc->address()) != NO_ERROR) { mAvailableOutputDevices.remove(devDesc); broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - devDesc->mAddress); + devDesc->address()); return INVALID_OPERATION; } // Propagate device availability to Engine @@ -178,12 +178,12 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, ALOGV("setDeviceConnectionState() disconnecting output device %x", device); // Send Disconnect to HALs - broadcastDeviceConnectionState(device, state, devDesc->mAddress); + broadcastDeviceConnectionState(device, state, devDesc->address()); // remove device from available output devices mAvailableOutputDevices.remove(devDesc); - checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress); + checkOutputsForDevice(devDesc, state, outputs, devDesc->address()); // Propagate device availability to Engine mEngine->setDeviceConnectionState(devDesc, state); @@ -265,11 +265,11 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) - broadcastDeviceConnectionState(device, state, devDesc->mAddress); + broadcastDeviceConnectionState(device, state, devDesc->address()); - if (checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress) != NO_ERROR) { + if (checkInputsForDevice(devDesc, state, inputs, devDesc->address()) != NO_ERROR) { broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - devDesc->mAddress); + devDesc->address()); return INVALID_OPERATION; } @@ -294,9 +294,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, ALOGV("setDeviceConnectionState() disconnecting input device %x", device); // Set Disconnect to HALs - broadcastDeviceConnectionState(device, state, devDesc->mAddress); + broadcastDeviceConnectionState(device, state, devDesc->address()); - checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress); + checkInputsForDevice(devDesc, state, inputs, devDesc->address()); mAvailableInputDevices.remove(devDesc); // Propagate device availability to Engine @@ -910,8 +910,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, } outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); - *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId() - : AUDIO_PORT_HANDLE_NONE; + *selectedDeviceId = getFirstDeviceId(outputDevices); ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId); @@ -1053,8 +1052,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( new SwAudioOutputDescriptor(profile, mpClientInterface); DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); - String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress - : String8(""); + String8 address = getFirstDeviceAddress(outputDevices); // MSD patch may be using the only output stream that can service this request. Release // MSD patch to prioritize this request over any active output on MSD. @@ -1755,10 +1753,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } // Explicit routing? - sp deviceDesc; - if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) { - deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId); - } + sp deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId); // special case for mmap capture: if an input IO handle is specified, we reuse this input if // possible @@ -1864,8 +1859,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, exit: inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); - *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId() - : AUDIO_PORT_HANDLE_NONE; + *selectedDeviceId = getFirstDeviceId(inputDevices); isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD && mSoundTriggerSessions.indexOfKey(session) > 0; @@ -1996,7 +1990,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, if (address == "") { DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); // the inputs vector must be of size >= 1, but we don't want to crash here - address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8(""); + address = getFirstDeviceAddress(inputDevices); } status_t status = inputDesc->open(&lConfig, device, address, @@ -2963,7 +2957,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } if (!outputDesc->mProfile->isCompatibleProfile(devDesc->type(), - devDesc->mAddress, + devDesc->address(), patch->sources[0].sample_rate, NULL, // updatedSamplingRate patch->sources[0].format, @@ -3020,7 +3014,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } if (!inputDesc->mProfile->isCompatibleProfile(devDesc->type(), - devDesc->mAddress, + devDesc->address(), patch->sinks[0].sample_rate, NULL, /*updatedSampleRate*/ patch->sinks[0].format, @@ -3085,8 +3079,8 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, // - audio HAL version is < 3.0 // - audio HAL version is >= 3.0 but no route has been declared between devices if (!srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) || - (srcDeviceDesc->mModule->getHalVersionMajor() < 3) || - !srcDeviceDesc->mModule->supportsPatch(srcDeviceDesc, sinkDeviceDesc)) { + (srcDeviceDesc->getModuleVersionMajor() < 3) || + !srcDeviceDesc->getModule()->supportsPatch(srcDeviceDesc, sinkDeviceDesc)) { // support only one sink device for now to simplify output selection logic if (patch->num_sinks > 1) { return INVALID_OPERATION; @@ -3431,8 +3425,8 @@ status_t AudioPolicyManager::connectAudioSource(const sp audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; if (srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) && - srcDeviceDesc->getAudioPort()->mModule->getHalVersionMajor() >= 3 && - sinkDeviceDesc->mModule->supportsPatch(srcDeviceDesc, sinkDeviceDesc) && + srcDeviceDesc->getModuleVersionMajor() >= 3 && + sinkDeviceDesc->getModule()->supportsPatch(srcDeviceDesc, sinkDeviceDesc) && srcDeviceDesc->getAudioPort()->mGains.size() > 0) { ALOGV("%s Device to Device route supported by >=3.0 HAL", __FUNCTION__); // TODO: may explicitly specify whether we should use HW or SW patch @@ -3666,7 +3660,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat AUDIO_DEVICE_OUT_HDMI); for (size_t i = 0; i < hdmiOutputDevices.size(); i++) { // Simulate reconnection to update enabled surround sound formats. - String8 address = hdmiOutputDevices[i]->mAddress; + String8 address = hdmiOutputDevices[i]->address(); String8 name = hdmiOutputDevices[i]->getName(); status_t status = setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_HDMI, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, @@ -3686,7 +3680,7 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat AUDIO_DEVICE_IN_HDMI); for (size_t i = 0; i < hdmiInputDevices.size(); i++) { // Simulate reconnection to update enabled surround sound formats. - String8 address = hdmiInputDevices[i]->mAddress; + String8 address = hdmiInputDevices[i]->address(); String8 name = hdmiInputDevices[i]->getName(); status_t status = setDeviceConnectionStateInt(AUDIO_DEVICE_IN_HDMI, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, @@ -3944,8 +3938,7 @@ status_t AudioPolicyManager::initialize() { const DeviceVector &supportedDevices = outProfile->getSupportedDevices(); const DeviceVector &devicesForType = supportedDevices.getDevicesFromTypeMask( profileType); - String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress - : String8(""); + String8 address = getFirstDeviceAddress(devicesForType); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; status_t status = outputDesc->open(nullptr, profileType, address, AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output); @@ -3999,8 +3992,7 @@ status_t AudioPolicyManager::initialize() { DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(profileType); // the inputs vector must be of size >= 1, but we don't want to crash here - String8 address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress - : String8(""); + String8 address = getFirstDeviceAddress(inputDevices); ALOGV(" for input device 0x%x using address %s", profileType, address.string()); ALOGE_IF(inputDevices.size() == 0, "Input device list is empty!"); @@ -4062,11 +4054,11 @@ status_t AudioPolicyManager::initialize() { } // If microphones address is empty, set it according to device type for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { - if (mAvailableInputDevices[i]->mAddress.isEmpty()) { + if (mAvailableInputDevices[i]->address().isEmpty()) { if (mAvailableInputDevices[i]->type() == AUDIO_DEVICE_IN_BUILTIN_MIC) { - mAvailableInputDevices[i]->mAddress = String8(AUDIO_BOTTOM_MICROPHONE_ADDRESS); + mAvailableInputDevices[i]->address() = String8(AUDIO_BOTTOM_MICROPHONE_ADDRESS); } else if (mAvailableInputDevices[i]->type() == AUDIO_DEVICE_IN_BACK_MIC) { - mAvailableInputDevices[i]->mAddress = String8(AUDIO_BACK_MICROPHONE_ADDRESS); + mAvailableInputDevices[i]->address() = String8(AUDIO_BACK_MICROPHONE_ADDRESS); } } } @@ -5235,8 +5227,9 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou if (!deviceList.isEmpty()) { PatchBuilder patchBuilder; patchBuilder.addSource(outputDesc); - for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) { - patchBuilder.addSink(deviceList.itemAt(i)); + ALOG_ASSERT(deviceList.size() <= AUDIO_PATCH_PORTS_MAX, "Too many sink ports"); + for (const auto &device : deviceList) { + patchBuilder.addSink(device); } installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index aa7ffc8e29..86993d4195 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -519,6 +519,19 @@ protected: return mAvailableInputDevices.getDeviceTypesFromHwModule( mPrimaryOutput->getModuleHandle()); } + /** + * @brief getFirstDeviceId of the Device Vector + * @return if the collection is not empty, it returns the first device Id, + * otherwise AUDIO_PORT_HANDLE_NONE + */ + audio_port_handle_t getFirstDeviceId(const DeviceVector &devices) const + { + return (devices.size() > 0) ? devices.itemAt(0)->getId() : AUDIO_PORT_HANDLE_NONE; + } + String8 getFirstDeviceAddress(const DeviceVector &devices) const + { + return (devices.size() > 0) ? devices.itemAt(0)->address() : String8(""); + } uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0); sp createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs); -- GitLab From 762365c3ee5f212d4995e002e1c53b221b587c92 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 10 Dec 2018 16:02:16 -0800 Subject: [PATCH 0617/1530] aaudio: suspend a stream when its queue is full This will prevent log spam when AAUDIO_CALLBACK_RESULT_STOP is returned from an audio callback. Bug: 120845500 Test: test_return_stop.cpp Change-Id: Icfe1541d6fa7b045285ac3dfbb75dfed5424d49b --- .../src/client/AudioStreamInternalCapture.cpp | 2 +- .../src/client/AudioStreamInternalPlay.cpp | 2 +- .../AAudioServiceEndpointCapture.cpp | 3 ++- .../oboeservice/AAudioServiceEndpointPlay.cpp | 4 ++++ .../oboeservice/AAudioServiceStreamBase.cpp | 5 ++++- .../oboeservice/AAudioServiceStreamBase.h | 19 +++++++++++++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp index 4a0e6da444..58ef7b15f8 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp @@ -258,7 +258,7 @@ void *AudioStreamInternalCapture::callbackLoop() { callbackResult = maybeCallDataCallback(mCallbackBuffer, mCallbackFrames); if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { - ALOGD("callback returned AAUDIO_CALLBACK_RESULT_STOP"); + ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); break; } } diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 2ae37a55d9..9af47b2ea2 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -293,7 +293,7 @@ void *AudioStreamInternalPlay::callbackLoop() { break; } } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { - ALOGV("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); + ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); break; } } diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp index 98288e16b1..37d105bf5e 100644 --- a/services/oboeservice/AAudioServiceEndpointCapture.cpp +++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp @@ -82,8 +82,9 @@ void *AAudioServiceEndpointCapture::callbackLoop() { std::lock_guard lock(mLockStreams); for (const auto& clientStream : mRegisteredStreams) { - if (clientStream->isRunning()) { + if (clientStream->isRunning() && !clientStream->isSuspended()) { int64_t clientFramesWritten = 0; + sp streamShared = static_cast(clientStream.get()); diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp index ac0d61d2b6..1e1c552a0c 100644 --- a/services/oboeservice/AAudioServiceEndpointPlay.cpp +++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp @@ -84,6 +84,10 @@ void *AAudioServiceEndpointPlay::callbackLoop() { int64_t clientFramesRead = 0; bool allowUnderflow = true; + if (clientStream->isSuspended()) { + continue; // dead stream + } + aaudio_stream_state_t state = clientStream->getState(); if (state == AAUDIO_STREAM_STATE_STOPPING) { allowUnderflow = false; // just read what is already in the FIFO diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp index 354b36a933..defbb7b2ef 100644 --- a/services/oboeservice/AAudioServiceStreamBase.cpp +++ b/services/oboeservice/AAudioServiceStreamBase.cpp @@ -179,6 +179,7 @@ aaudio_result_t AAudioServiceStreamBase::start() { } setFlowing(false); + setSuspended(false); // Start with fresh presentation timestamps. mAtomicTimestamp.clear(); @@ -345,7 +346,9 @@ aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessag } int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1); if (count != 1) { - ALOGE("%s(): Queue full. Did client die? %s", __func__, getTypeText()); + ALOGW("%s(): Queue full. Did client stop? Suspending stream. what = %u, %s", + __func__, command->what, getTypeText()); + setSuspended(true); return AAUDIO_ERROR_WOULD_BLOCK; } else { return AAUDIO_OK; diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h index a1815d036f..7904b25182 100644 --- a/services/oboeservice/AAudioServiceStreamBase.h +++ b/services/oboeservice/AAudioServiceStreamBase.h @@ -203,6 +203,20 @@ public: return mFlowing; } + /** + * Set false when the stream should not longer be processed. + * This may be caused by a message queue overflow. + * Set true when stream is started. + * @param suspended + */ + void setSuspended(bool suspended) { + mSuspended = suspended; + } + + bool isSuspended() const { + return mSuspended; + } + /** * Atomically increment the number of active references to the stream by AAudioService. * @@ -304,7 +318,12 @@ private: // This is modified under a global lock in AAudioStreamTracker. int32_t mCallingCount = 0; + // This indicates that a stream that is being referenced by a binder call needs to closed. std::atomic mCloseNeeded{false}; + + // This indicate that a running stream should not be processed because of an error, + // for example a full message queue. Note that this atomic is unrelated to mCloseNeeded. + std::atomic mSuspended{false}; }; } /* namespace aaudio */ -- GitLab From 4f4689606732d276587ecf3eb686caef4630c60c Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 5 Dec 2018 15:48:41 -0800 Subject: [PATCH 0618/1530] hdr10+: parameter passing for OMX Add hdr10+ parameter passing to OMX. Metadata is sent to OMX component via setConfig(). Available output metadata is notified via onEvent(), and then retrieved via getConfig(). Hdr10+ metadata pass through is added to soft VP9 for testing. bug: 118507186 test: Locally built DecoderTest in cts that sends setParameters with input frames when decoding VP9. Verify the metadata is received on expected output. Change-Id: I9bb87581a3036f9ae3cc881cbfe1a3c99c8f78b3 --- .../include/media/stagefright/MetaDataBase.h | 1 + media/libstagefright/ACodec.cpp | 127 +++++++++++++++++- media/libstagefright/MediaCodec.cpp | 7 + media/libstagefright/Utils.cpp | 19 ++- .../codecs/avcenc/SoftAVCEnc.cpp | 6 +- .../libstagefright/codecs/avcenc/SoftAVCEnc.h | 4 +- .../libstagefright/codecs/on2/dec/SoftVPX.cpp | 22 ++- media/libstagefright/codecs/on2/dec/SoftVPX.h | 9 +- .../codecs/on2/enc/SoftVPXEncoder.cpp | 6 +- .../codecs/on2/enc/SoftVPXEncoder.h | 4 +- .../include/media/stagefright/ACodec.h | 18 +++ .../omx/SimpleSoftOMXComponent.cpp | 27 +++- .../omx/SoftVideoDecoderOMXComponent.cpp | 83 +++++++++++- .../stagefright/omx/SimpleSoftOMXComponent.h | 10 ++ .../omx/SoftVideoDecoderOMXComponent.h | 15 ++- 15 files changed, 337 insertions(+), 21 deletions(-) diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h index 9f2deda471..b99c14ca19 100644 --- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h +++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h @@ -199,6 +199,7 @@ enum { // HDR related kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo + kKeyHdr10PlusInfo = 'hdrD', // raw data // color aspects kKeyColorRange = 'cRng', // int32_t, color range, value defined by ColorAspects.Range diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 114f49223d..6ad0417fc3 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -576,6 +576,7 @@ ACodec::ACodec() mTunneled(false), mDescribeColorAspectsIndex((OMX_INDEXTYPE)0), mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0), + mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0), mStateGeneration(0), mVendorExtensionsStatus(kExtensionsUnchecked) { memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo)); @@ -3765,8 +3766,17 @@ status_t ACodec::initDescribeHDRStaticInfoIndex() { "OMX.google.android.index.describeHDRStaticInfo", &mDescribeHDRStaticInfoIndex); if (err != OK) { mDescribeHDRStaticInfoIndex = (OMX_INDEXTYPE)0; + return err; } - return err; + + err = mOMXNode->getExtensionIndex( + "OMX.google.android.index.describeHDR10PlusInfo", &mDescribeHDR10PlusInfoIndex); + if (err != OK) { + mDescribeHDR10PlusInfoIndex = (OMX_INDEXTYPE)0; + return err; + } + + return OK; } status_t ACodec::setHDRStaticInfo(const DescribeHDRStaticInfoParams ¶ms) { @@ -5397,6 +5407,70 @@ status_t ACodec::getPortFormat(OMX_U32 portIndex, sp ¬ify) { return getVendorParameters(portIndex, notify); } +DescribeHDR10PlusInfoParams* ACodec::getHDR10PlusInfo(size_t paramSizeUsed) { + if (mDescribeHDR10PlusInfoIndex == 0) { + ALOGE("getHDR10PlusInfo: does not support DescribeHDR10PlusInfoParams"); + return nullptr; + } + + size_t newSize = sizeof(DescribeHDR10PlusInfoParams) - 1 + + ((paramSizeUsed > 0) ? paramSizeUsed : 512); + if (mHdr10PlusScratchBuffer == nullptr + || newSize > mHdr10PlusScratchBuffer->size()) { + mHdr10PlusScratchBuffer = new ABuffer(newSize); + } + DescribeHDR10PlusInfoParams *config = + (DescribeHDR10PlusInfoParams *)mHdr10PlusScratchBuffer->data(); + InitOMXParams(config); + config->nSize = mHdr10PlusScratchBuffer->size(); + config->nPortIndex = 1; + size_t paramSize = config->nSize - sizeof(DescribeHDR10PlusInfoParams) + 1; + config->nParamSize = paramSize; + config->nParamSizeUsed = 0; + status_t err = mOMXNode->getConfig( + (OMX_INDEXTYPE)mDescribeHDR10PlusInfoIndex, + config, config->nSize); + if (err != OK) { + ALOGE("failed to get DescribeHDR10PlusInfoParams (err %d)", err); + return nullptr; + } + if (config->nParamSize != paramSize) { + ALOGE("DescribeHDR10PlusInfoParams alters nParamSize: %u vs %zu", + config->nParamSize, paramSize); + return nullptr; + } + if (paramSizeUsed > 0 && config->nParamSizeUsed != paramSizeUsed) { + ALOGE("DescribeHDR10PlusInfoParams returns wrong nParamSizeUsed: %u vs %zu", + config->nParamSizeUsed, paramSizeUsed); + return nullptr; + } + return config; +} + +void ACodec::onConfigUpdate(OMX_INDEXTYPE configIndex) { + if (mDescribeHDR10PlusInfoIndex == 0 + || configIndex != mDescribeHDR10PlusInfoIndex) { + // mDescribeHDR10PlusInfoIndex is the only update we recognize now + return; + } + + DescribeHDR10PlusInfoParams *config = getHDR10PlusInfo(); + if (config == nullptr) { + return; + } + if (config->nParamSizeUsed > config->nParamSize) { + // try again with the size specified + config = getHDR10PlusInfo(config->nParamSizeUsed); + if (config == nullptr) { + return; + } + } + + mOutputFormat = mOutputFormat->dup(); // trigger an output format changed event + mOutputFormat->setBuffer("hdr10-plus-info", + ABuffer::CreateAsCopy(config->nValue, config->nParamSizeUsed)); +} + void ACodec::onDataSpaceChanged(android_dataspace dataSpace, const ColorAspects &aspects) { // aspects are normally communicated in ColorAspects int32_t range, standard, transfer; @@ -6337,6 +6411,15 @@ void ACodec::BaseState::onOutputBufferDrained(const sp &msg) { } } + sp hdr10PlusInfo; + if (buffer->format()->findBuffer("hdr10-plus-info", &hdr10PlusInfo) + && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0 + && hdr10PlusInfo != mCodec->mLastHdr10PlusBuffer) { + native_window_set_buffers_hdr10_plus_metadata(mCodec->mNativeWindow.get(), + hdr10PlusInfo->size(), hdr10PlusInfo->data()); + mCodec->mLastHdr10PlusBuffer = hdr10PlusInfo; + } + // save buffers sent to the surface so we can get render time when they return int64_t mediaTimeUs = -1; buffer->meta()->findInt64("timeUs", &mediaTimeUs); @@ -7475,12 +7558,45 @@ status_t ACodec::setParameters(const sp ¶ms) { } } + sp hdr10PlusInfo; + if (params->findBuffer("hdr10-plus-info", &hdr10PlusInfo) + && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0) { + (void)setHdr10PlusInfo(hdr10PlusInfo); + } + // Ignore errors as failure is expected for codecs that aren't video encoders. (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat); return setVendorParameters(params); } +status_t ACodec::setHdr10PlusInfo(const sp &hdr10PlusInfo) { + if (mDescribeHDR10PlusInfoIndex == 0) { + ALOGE("setHdr10PlusInfo: does not support DescribeHDR10PlusInfoParams"); + return ERROR_UNSUPPORTED; + } + size_t newSize = sizeof(DescribeHDR10PlusInfoParams) + hdr10PlusInfo->size() - 1; + if (mHdr10PlusScratchBuffer == nullptr || + newSize > mHdr10PlusScratchBuffer->size()) { + mHdr10PlusScratchBuffer = new ABuffer(newSize); + } + DescribeHDR10PlusInfoParams *config = + (DescribeHDR10PlusInfoParams *)mHdr10PlusScratchBuffer->data(); + InitOMXParams(config); + config->nPortIndex = 0; + config->nSize = newSize; + config->nParamSize = hdr10PlusInfo->size(); + config->nParamSizeUsed = hdr10PlusInfo->size(); + memcpy(config->nValue, hdr10PlusInfo->data(), hdr10PlusInfo->size()); + status_t err = mOMXNode->setConfig( + (OMX_INDEXTYPE)mDescribeHDR10PlusInfoIndex, + config, config->nSize); + if (err != OK) { + ALOGE("failed to set DescribeHDR10PlusInfoParams (err %d)", err); + } + return OK; +} + // Removes trailing tags matching |tag| from |key| (e.g. a settings name). |minLength| specifies // the minimum number of characters to keep in |key| (even if it has trailing tags). // (Used to remove trailing 'value' tags in settings names, e.g. to normalize @@ -7902,6 +8018,15 @@ bool ACodec::ExecutingState::onOMXEvent( return true; } + case OMX_EventConfigUpdate: + { + CHECK_EQ(data1, (OMX_U32)kPortIndexOutput); + + mCodec->onConfigUpdate((OMX_INDEXTYPE)data2); + + return true; + } + case OMX_EventBufferFlag: { return true; diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 70064ea087..bd9e2bb519 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -2197,6 +2197,13 @@ void MediaCodec::onMessageReceived(const sp &msg) { } } + sp hdr10PlusInfo; + if (mOutputFormat->findBuffer("hdr10-plus-info", &hdr10PlusInfo) + && hdr10PlusInfo != nullptr && hdr10PlusInfo->size() > 0) { + native_window_set_buffers_hdr10_plus_metadata(mSurface.get(), + hdr10PlusInfo->size(), hdr10PlusInfo->data()); + } + if (mime.startsWithIgnoreCase("video/")) { mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees); } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index cfa9fd9041..3af494484c 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -119,7 +119,8 @@ static bool isHdr(const sp &format) { } // if user/container supplied HDR static info without transfer set, assume true - if (format->contains("hdr-static-info") && !format->contains("color-transfer")) { + if ((format->contains("hdr-static-info") || format->contains("hdr10-plus-info")) + && !format->contains("color-transfer")) { return true; } // otherwise, verify that an HDR transfer function is set @@ -876,6 +877,16 @@ status_t convertMetaDataToMessage( ColorUtils::setHDRStaticInfoIntoFormat(*(HDRStaticInfo*)data, msg); } + if (meta->findData(kKeyHdr10PlusInfo, &type, &data, &size) + && size > 0) { + sp buffer = new (std::nothrow) ABuffer(size); + if (buffer.get() == NULL || buffer->base() == NULL) { + return NO_MEMORY; + } + memcpy(buffer->data(), data, size); + msg->setBuffer("hdr10-plus-info", buffer); + } + convertMetaDataToMessageColorAspects(meta, msg); } else if (!strncasecmp("audio/", mime, 6)) { int32_t numChannels, sampleRate; @@ -1624,6 +1635,12 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { } } + sp hdr10PlusInfo; + if (msg->findBuffer("hdr10-plus-info", &hdr10PlusInfo)) { + meta->setData(kKeyHdr10PlusInfo, 0, + hdr10PlusInfo->data(), hdr10PlusInfo->size()); + } + convertMessageToMetaDataColorAspects(msg, meta); AString tsSchema; diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp index 379d41e073..e0f26831bf 100644 --- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp @@ -1058,8 +1058,8 @@ OMX_ERRORTYPE SoftAVC::getConfig( } } -OMX_ERRORTYPE SoftAVC::setConfig( - OMX_INDEXTYPE index, const OMX_PTR _params) { +OMX_ERRORTYPE SoftAVC::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig) { switch ((int)index) { case OMX_IndexConfigVideoIntraVOPRefresh: { @@ -1125,7 +1125,7 @@ OMX_ERRORTYPE SoftAVC::setConfig( } default: - return SimpleSoftOMXComponent::setConfig(index, _params); + return SimpleSoftOMXComponent::internalSetConfig(index, _params, frameConfig); } } diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h index a43cdf17fd..8253b7db0e 100644 --- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h @@ -219,8 +219,8 @@ private: OMX_ERRORTYPE internalSetBitrateParams( const OMX_VIDEO_PARAM_BITRATETYPE *bitrate); - OMX_ERRORTYPE setConfig( - OMX_INDEXTYPE index, const OMX_PTR _params); + OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig); OMX_ERRORTYPE getConfig( OMX_INDEXTYPE index, const OMX_PTR _params); diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp index 8d5f3e7d72..0f2ff1730f 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -33,6 +33,7 @@ static const CodecProfileLevel kVP9ProfileLevels[] = { { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level5 }, { OMX_VIDEO_VP9Profile2, OMX_VIDEO_VP9Level5 }, { OMX_VIDEO_VP9Profile2HDR, OMX_VIDEO_VP9Level5 }, + { OMX_VIDEO_VP9Profile2HDR10Plus, OMX_VIDEO_VP9Level5 }, }; SoftVPX::SoftVPX( @@ -84,6 +85,10 @@ bool SoftVPX::supportDescribeHdrStaticInfo() { return true; } +bool SoftVPX::supportDescribeHdr10PlusInfo() { + return true; +} + status_t SoftVPX::initDecoder() { mCtx = new vpx_codec_ctx_t; vpx_codec_err_t vpx_err; @@ -167,7 +172,12 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por outHeader->nOffset = 0; outHeader->nFlags = 0; outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * bpp * 3) / 2; - outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv; + PrivInfo *privInfo = (PrivInfo *)mImg->user_priv; + outHeader->nTimeStamp = privInfo->mTimeStamp; + if (privInfo->mHdr10PlusInfo != nullptr) { + queueOutputFrameConfig(privInfo->mHdr10PlusInfo); + } + if (outputBufferSafe(outHeader)) { uint8_t *dst = outHeader->pBuffer; const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y]; @@ -275,7 +285,13 @@ void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { } } - mTimeStamps[mTimeStampIdx] = inHeader->nTimeStamp; + mPrivInfo[mTimeStampIdx].mTimeStamp = inHeader->nTimeStamp; + + if (inInfo->mFrameConfig) { + mPrivInfo[mTimeStampIdx].mHdr10PlusInfo = dequeueInputFrameConfig(); + } else { + mPrivInfo[mTimeStampIdx].mHdr10PlusInfo.clear(); + } if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { mEOSStatus = INPUT_EOS_SEEN; @@ -285,7 +301,7 @@ void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { if (inHeader->nFilledLen > 0) { vpx_codec_err_t err = vpx_codec_decode( (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset, - inHeader->nFilledLen, &mTimeStamps[mTimeStampIdx], 0); + inHeader->nFilledLen, &mPrivInfo[mTimeStampIdx], 0); if (err == VPX_CODEC_OK) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h index b62b526361..0aa8e9cb12 100644 --- a/media/libstagefright/codecs/on2/dec/SoftVPX.h +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -26,6 +26,8 @@ namespace android { +struct ABuffer; + struct SoftVPX : public SoftVideoDecoderOMXComponent { SoftVPX(const char *name, const char *componentRole, @@ -41,6 +43,7 @@ protected: virtual void onPortFlushCompleted(OMX_U32 portIndex); virtual void onReset(); virtual bool supportDescribeHdrStaticInfo(); + virtual bool supportDescribeHdr10PlusInfo(); private: enum { @@ -60,7 +63,11 @@ private: void *mCtx; bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder. - OMX_TICKS mTimeStamps[kNumBuffers]; + struct PrivInfo { + OMX_TICKS mTimeStamp; + sp mHdr10PlusInfo; + }; + PrivInfo mPrivInfo[kNumBuffers]; uint8_t mTimeStampIdx; vpx_image_t *mImg; diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index 2dfba13a71..d0cb0718c2 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -401,8 +401,8 @@ OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index, } } -OMX_ERRORTYPE SoftVPXEncoder::setConfig( - OMX_INDEXTYPE index, const OMX_PTR _params) { +OMX_ERRORTYPE SoftVPXEncoder::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig) { switch (index) { case OMX_IndexConfigVideoIntraVOPRefresh: { @@ -442,7 +442,7 @@ OMX_ERRORTYPE SoftVPXEncoder::setConfig( } default: - return SimpleSoftOMXComponent::setConfig(index, _params); + return SimpleSoftOMXComponent::internalSetConfig(index, _params, frameConfig); } } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index dd86d36949..263d13450f 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -84,8 +84,8 @@ protected: virtual OMX_ERRORTYPE internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR param); - virtual OMX_ERRORTYPE setConfig( - OMX_INDEXTYPE index, const OMX_PTR params); + virtual OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig); // OMX callback when buffers available // Note that both an input and output buffer diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 80125d433c..9b2853e238 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -237,6 +237,8 @@ private: android_native_rect_t mLastNativeWindowCrop; int32_t mLastNativeWindowDataSpace; HDRStaticInfo mLastHDRStaticInfo; + sp mHdr10PlusScratchBuffer; + sp mLastHdr10PlusBuffer; sp mConfigFormat; sp mInputFormat; sp mOutputFormat; @@ -290,6 +292,7 @@ private: OMX_INDEXTYPE mDescribeColorAspectsIndex; OMX_INDEXTYPE mDescribeHDRStaticInfoIndex; + OMX_INDEXTYPE mDescribeHDR10PlusInfoIndex; std::shared_ptr mBufferChannel; @@ -424,6 +427,11 @@ private: // unspecified values. void onDataSpaceChanged(android_dataspace dataSpace, const ColorAspects &aspects); + // notifies the codec that the config with |configIndex| has changed, the value + // can be queried by OMX getConfig, and the config should be applied to the next + // output buffer notified after this callback. + void onConfigUpdate(OMX_INDEXTYPE configIndex); + // gets index or sets it to 0 on error. Returns error from codec. status_t initDescribeHDRStaticInfoIndex(); @@ -435,12 +443,22 @@ private: // sets |params|. Returns the codec error. status_t setHDRStaticInfo(const DescribeHDRStaticInfoParams ¶ms); + // sets |hdr10PlusInfo|. Returns the codec error. + status_t setHdr10PlusInfo(const sp &hdr10PlusInfo); + // gets |params|. Returns the codec error. status_t getHDRStaticInfo(DescribeHDRStaticInfoParams ¶ms); // gets HDR static information for the video encoder/decoder port and sets them into |format|. status_t getHDRStaticInfoForVideoCodec(OMX_U32 portIndex, sp &format); + // gets DescribeHDR10PlusInfoParams params. If |paramSizeUsed| is zero, it's + // possible that the returned DescribeHDR10PlusInfoParams only has the + // nParamSizeUsed field updated, because the size of the storage is insufficient. + // In this case, getHDR10PlusInfo() should be called again with |paramSizeUsed| + // specified to the previous returned value. + DescribeHDR10PlusInfoParams* getHDR10PlusInfo(size_t paramSizeUsed = 0); + typedef struct drcParams { int32_t drcCut; int32_t drcBoost; diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp index 55afe04a79..ddb459f54c 100644 --- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp +++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp @@ -34,7 +34,8 @@ SimpleSoftOMXComponent::SimpleSoftOMXComponent( mLooper(new ALooper), mHandler(new AHandlerReflector(this)), mState(OMX_StateLoaded), - mTargetState(OMX_StateLoaded) { + mTargetState(OMX_StateLoaded), + mFrameConfig(false) { mLooper->setName(name); mLooper->registerHandler(mHandler); @@ -204,6 +205,21 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( } } +OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig) { + return OMX_ErrorUndefined; +} + +OMX_ERRORTYPE SimpleSoftOMXComponent::setConfig( + OMX_INDEXTYPE index, const OMX_PTR params) { + bool frameConfig = mFrameConfig; + OMX_ERRORTYPE err = internalSetConfig(index, params, &frameConfig); + if (err == OMX_ErrorNone) { + mFrameConfig = frameConfig; + } + return err; +} + OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( OMX_BUFFERHEADERTYPE **header, OMX_U32 portIndex, @@ -336,6 +352,10 @@ OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { sp msg = new AMessage(kWhatEmptyThisBuffer, mHandler); msg->setPointer("header", buffer); + if (mFrameConfig) { + msg->setInt32("frame-config", mFrameConfig); + mFrameConfig = false; + } msg->post(); return OMX_ErrorNone; @@ -378,6 +398,10 @@ void SimpleSoftOMXComponent::onMessageReceived(const sp &msg) { { OMX_BUFFERHEADERTYPE *header; CHECK(msg->findPointer("header", (void **)&header)); + int32_t frameConfig; + if (!msg->findInt32("frame-config", &frameConfig)) { + frameConfig = 0; + } CHECK(mState == OMX_StateExecuting && mTargetState == mState); @@ -393,6 +417,7 @@ void SimpleSoftOMXComponent::onMessageReceived(const sp &msg) { CHECK(!buffer->mOwnedByUs); buffer->mOwnedByUs = true; + buffer->mFrameConfig = (bool)frameConfig; CHECK((msgType == kWhatEmptyThisBuffer && port->mDef.eDir == OMX_DirInput) diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp index f9f7ec2d36..e853da9763 100644 --- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp @@ -602,13 +602,40 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig( return OMX_ErrorNone; } + case kDescribeHdr10PlusInfoIndex: + { + if (!supportDescribeHdr10PlusInfo()) { + return OMX_ErrorUnsupportedIndex; + } + + if (mHdr10PlusOutputs.size() > 0) { + auto it = mHdr10PlusOutputs.begin(); + + auto info = (*it).get(); + + DescribeHDR10PlusInfoParams* outParams = + (DescribeHDR10PlusInfoParams *)params; + + outParams->nParamSizeUsed = info->size(); + + // If the buffer provided by the client does not have enough + // storage, return the size only and do not remove the param yet. + if (outParams->nParamSize >= info->size()) { + memcpy(outParams->nValue, info->data(), info->size()); + mHdr10PlusOutputs.erase(it); + } + return OMX_ErrorNone; + } + return OMX_ErrorUnderflow; + } + default: return OMX_ErrorUnsupportedIndex; } } -OMX_ERRORTYPE SoftVideoDecoderOMXComponent::setConfig( - OMX_INDEXTYPE index, const OMX_PTR params){ +OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig){ switch ((int)index) { case kDescribeColorAspectsIndex: { @@ -658,11 +685,55 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::setConfig( return OMX_ErrorNone; } + case kDescribeHdr10PlusInfoIndex: + { + if (!supportDescribeHdr10PlusInfo()) { + return OMX_ErrorUnsupportedIndex; + } + + const DescribeHDR10PlusInfoParams* inParams = + (DescribeHDR10PlusInfoParams *)params; + + if (*frameConfig) { + // This is a request to append to the current frame config set. + // For now, we only support kDescribeHdr10PlusInfoIndex, which + // we simply replace with the last set value. + if (mHdr10PlusInputs.size() > 0) { + *(--mHdr10PlusInputs.end()) = ABuffer::CreateAsCopy( + inParams->nValue, inParams->nParamSizeUsed); + } else { + ALOGW("Ignoring kDescribeHdr10PlusInfoIndex: append to " + "frame config while no frame config is present"); + } + } else { + // This is a frame config, setting *frameConfig to true so that + // the client marks the next queued input frame to apply it. + *frameConfig = true; + mHdr10PlusInputs.push_back(ABuffer::CreateAsCopy( + inParams->nValue, inParams->nParamSizeUsed)); + } + return OMX_ErrorNone; + } + default: return OMX_ErrorUnsupportedIndex; } } +sp SoftVideoDecoderOMXComponent::dequeueInputFrameConfig() { + auto it = mHdr10PlusInputs.begin(); + sp info = *it; + mHdr10PlusInputs.erase(it); + return info; +} + +void SoftVideoDecoderOMXComponent::queueOutputFrameConfig(const sp &info) { + mHdr10PlusOutputs.push_back(info); + notify(OMX_EventConfigUpdate, + kOutputPortIndex, + kDescribeHdr10PlusInfoIndex, + NULL); +} OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( const char *name, OMX_INDEXTYPE *index) { @@ -677,6 +748,10 @@ OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex( && supportDescribeHdrStaticInfo()) { *(int32_t*)index = kDescribeHdrStaticInfoIndex; return OMX_ErrorNone; + } else if (!strcmp(name, "OMX.google.android.index.describeHDR10PlusInfo") + && supportDescribeHdr10PlusInfo()) { + *(int32_t*)index = kDescribeHdr10PlusInfoIndex; + return OMX_ErrorNone; } return SimpleSoftOMXComponent::getExtensionIndex(name, index); @@ -694,6 +769,10 @@ bool SoftVideoDecoderOMXComponent::supportDescribeHdrStaticInfo() { return false; } +bool SoftVideoDecoderOMXComponent::supportDescribeHdr10PlusInfo() { + return false; +} + void SoftVideoDecoderOMXComponent::onReset() { mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SimpleSoftOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SimpleSoftOMXComponent.h index 1d1f2bdf78..6bbedda99f 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SimpleSoftOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SimpleSoftOMXComponent.h @@ -20,6 +20,7 @@ #include "SoftOMXComponent.h" +#include #include #include #include @@ -28,6 +29,7 @@ namespace android { struct ALooper; +struct ABuffer; struct CodecProfileLevel { OMX_U32 mProfile; @@ -49,6 +51,7 @@ protected: struct BufferInfo { OMX_BUFFERHEADERTYPE *mHeader; bool mOwnedByUs; + bool mFrameConfig; }; struct PortInfo { @@ -76,6 +79,9 @@ protected: virtual OMX_ERRORTYPE internalSetParameter( OMX_INDEXTYPE index, const OMX_PTR params); + virtual OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig); + virtual void onQueueFilled(OMX_U32 portIndex); List &getPortQueue(OMX_U32 portIndex); @@ -101,6 +107,7 @@ private: OMX_STATETYPE mTargetState; Vector mPorts; + std::atomic_bool mFrameConfig; bool isSetParameterAllowed( OMX_INDEXTYPE index, const OMX_PTR params) const; @@ -114,6 +121,9 @@ private: virtual OMX_ERRORTYPE setParameter( OMX_INDEXTYPE index, const OMX_PTR params); + virtual OMX_ERRORTYPE setConfig( + OMX_INDEXTYPE index, const OMX_PTR params); + virtual OMX_ERRORTYPE useBuffer( OMX_BUFFERHEADERTYPE **buffer, OMX_U32 portIndex, diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h index 56fc691dea..3b381cea02 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h @@ -20,6 +20,7 @@ #include "SimpleSoftOMXComponent.h" +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include namespace android { @@ -48,6 +50,7 @@ protected: enum { kDescribeColorAspectsIndex = kPrepareForAdaptivePlaybackIndex + 1, kDescribeHdrStaticInfoIndex = kPrepareForAdaptivePlaybackIndex + 2, + kDescribeHdr10PlusInfoIndex = kPrepareForAdaptivePlaybackIndex + 3, }; enum { @@ -68,8 +71,8 @@ protected: virtual OMX_ERRORTYPE getConfig( OMX_INDEXTYPE index, OMX_PTR params); - virtual OMX_ERRORTYPE setConfig( - OMX_INDEXTYPE index, const OMX_PTR params); + virtual OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig); virtual OMX_ERRORTYPE getExtensionIndex( const char *name, OMX_INDEXTYPE *index); @@ -80,6 +83,8 @@ protected: virtual bool supportDescribeHdrStaticInfo(); + virtual bool supportDescribeHdr10PlusInfo(); + // This function sets both minimum buffer count and actual buffer count of // input port to be |numInputBuffers|. It will also set both minimum buffer // count and actual buffer count of output port to be |numOutputBuffers|. @@ -166,6 +171,9 @@ protected: // Helper function to dump the ColorAspects. void dumpColorAspects(const ColorAspects &colorAspects); + sp dequeueInputFrameConfig(); + void queueOutputFrameConfig(const sp &info); + private: uint32_t mMinInputBufferSize; uint32_t mMinCompressionRatio; @@ -174,6 +182,9 @@ private: OMX_VIDEO_CODINGTYPE mCodingType; const CodecProfileLevel *mProfileLevels; size_t mNumProfileLevels; + typedef List > Hdr10PlusInfoList; + Hdr10PlusInfoList mHdr10PlusInputs; + Hdr10PlusInfoList mHdr10PlusOutputs; DISALLOW_EVIL_CONSTRUCTORS(SoftVideoDecoderOMXComponent); }; -- GitLab From 17944af5b111ade413e8e71a34898ccf8a51c15a Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Thu, 13 Dec 2018 18:13:10 -0800 Subject: [PATCH 0619/1530] mediaplayer2: add srcId for source dependent API's Test: cts Bug: 112549021 Change-Id: I619c85af8f59cb5956ca0b399e918ea38e3b32b5 --- .../include/mediaplayer2/mediaplayer2.h | 2 +- media/libmediaplayer2/mediaplayer2.cpp | 9 ++++++++- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 20 ++++++++++++++++--- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 6 +++--- .../nuplayer2/NuPlayer2Driver.cpp | 13 ++++++++---- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index c7cd7d2d61..a945ffd600 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -86,7 +86,7 @@ public: MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC); status_t notifyAt(int64_t mediaTimeUs); status_t getCurrentPosition(int64_t *msec); - status_t getDuration(int64_t *msec); + status_t getDuration(int64_t srcId, int64_t *msec); status_t reset(); status_t setAudioStreamType(audio_stream_type_t type); status_t getAudioStreamType(audio_stream_type_t *type); diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 2ae5a8ca30..f43205920f 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -718,8 +718,15 @@ status_t MediaPlayer2::getCurrentPosition(int64_t *msec) { return ret; } -status_t MediaPlayer2::getDuration(int64_t *msec) { +status_t MediaPlayer2::getDuration(int64_t srcId, int64_t *msec) { Mutex::Autolock _l(mLock); + // TODO: cache duration for currentSrcId and nextSrcId, and return correct + // value for nextSrcId. + if (srcId != mSrcId) { + *msec = -1; + return OK; + } + ALOGV("getDuration_l"); bool isValidState = (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE)); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 81ffbc7599..080d923b94 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -791,9 +791,13 @@ void NuPlayer2::onMessageReceived(const sp &msg) { sp replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + int64_t srcId; + CHECK(msg->findInt64("srcId", (int64_t*)&srcId)); + PlayerMessage* reply; CHECK(msg->findPointer("reply", (void**)&reply)); + // TODO: use correct source info based on srcId. size_t inbandTracks = 0; if (mCurrentSourceInfo.mSource != NULL) { inbandTracks = mCurrentSourceInfo.mSource->getTrackCount(); @@ -824,10 +828,14 @@ void NuPlayer2::onMessageReceived(const sp &msg) { case kWhatGetSelectedTrack: { + int64_t srcId; + CHECK(msg->findInt64("srcId", (int64_t*)&srcId)); + int32_t type32; CHECK(msg->findInt32("type", (int32_t*)&type32)); media_track_type type = (media_track_type)type32; + // TODO: use correct source info based on srcId. size_t inbandTracks = 0; status_t err = INVALID_OPERATION; ssize_t selectedTrack = -1; @@ -863,15 +871,18 @@ void NuPlayer2::onMessageReceived(const sp &msg) { sp replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + int64_t srcId; size_t trackIndex; int32_t select; int64_t timeUs; + CHECK(msg->findInt64("srcId", (int64_t*)&srcId)); CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK(msg->findInt32("select", &select)); CHECK(msg->findInt64("timeUs", &timeUs)); status_t err = INVALID_OPERATION; + // TODO: use correct source info based on srcId. size_t inbandTracks = 0; if (mCurrentSourceInfo.mSource != NULL) { inbandTracks = mCurrentSourceInfo.mSource->getTrackCount(); @@ -2324,8 +2335,9 @@ status_t NuPlayer2::setVideoScalingMode(int32_t mode) { return OK; } -status_t NuPlayer2::getTrackInfo(PlayerMessage* reply) const { +status_t NuPlayer2::getTrackInfo(int64_t srcId, PlayerMessage* reply) const { sp msg = new AMessage(kWhatGetTrackInfo, this); + msg->setInt64("srcId", srcId); msg->setPointer("reply", reply); sp response; @@ -2333,9 +2345,10 @@ status_t NuPlayer2::getTrackInfo(PlayerMessage* reply) const { return err; } -status_t NuPlayer2::getSelectedTrack(int32_t type, PlayerMessage* reply) const { +status_t NuPlayer2::getSelectedTrack(int64_t srcId, int32_t type, PlayerMessage* reply) const { sp msg = new AMessage(kWhatGetSelectedTrack, this); msg->setPointer("reply", reply); + msg->setInt64("srcId", srcId); msg->setInt32("type", type); sp response; @@ -2346,8 +2359,9 @@ status_t NuPlayer2::getSelectedTrack(int32_t type, PlayerMessage* reply) const { return err; } -status_t NuPlayer2::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { +status_t NuPlayer2::selectTrack(int64_t srcId, size_t trackIndex, bool select, int64_t timeUs) { sp msg = new AMessage(kWhatSelectTrack, this); + msg->setInt64("srcId", srcId); msg->setSize("trackIndex", trackIndex); msg->setInt32("select", select); msg->setInt64("timeUs", timeUs); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index e9b5f11635..fdc128f9c9 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -82,9 +82,9 @@ struct NuPlayer2 : public AHandler { void rewind(); status_t setVideoScalingMode(int32_t mode); - status_t getTrackInfo(PlayerMessage* reply) const; - status_t getSelectedTrack(int32_t type, PlayerMessage* reply) const; - status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs); + status_t getTrackInfo(int64_t srcId, PlayerMessage* reply) const; + status_t getSelectedTrack(int64_t srcId, int32_t type, PlayerMessage* reply) const; + status_t selectTrack(int64_t srcId, size_t trackIndex, bool select, int64_t timeUs); status_t getCurrentPosition(int64_t *mediaUs); void getStats(Vector > *mTrackStats); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 56d708a7fe..2dab2dd0f3 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -603,28 +603,33 @@ status_t NuPlayer2Driver::invoke(const PlayerMessage &request, PlayerMessage *re case MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO: { - return mPlayer->getTrackInfo(response); + int64_t srcId = (it++)->int64_value(); + return mPlayer->getTrackInfo(srcId, response); } case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK: { + int64_t srcId = (it++)->int64_value(); int trackIndex = (it++)->int32_value(); int64_t msec = 0; // getCurrentPosition should always return OK getCurrentPosition(&msec); - return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000LL); + return mPlayer->selectTrack(srcId, trackIndex, true /* select */, msec * 1000LL); } case MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK: { + int64_t srcId = (it++)->int64_value(); int trackIndex = (it++)->int32_value(); - return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */); + return mPlayer->selectTrack( + srcId, trackIndex, false /* select */, 0xdeadbeef /* not used */); } case MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK: { + int64_t srcId = (it++)->int64_value(); int32_t type = (it++)->int32_value(); - return mPlayer->getSelectedTrack(type, response); + return mPlayer->getSelectedTrack(srcId, type, response); } default: -- GitLab From 5beb4810060cda2afea1c89f98caf5c337763c0a Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 14 Dec 2018 10:48:17 -0800 Subject: [PATCH 0620/1530] audioserver: Use '_exit' instead of 'exit' in HalDeathHandler Using 'exit' from an RPC threadpool thread is not safe, as 'exit' runs atexit handlers that destroy global objects. This can interfere with code still running on other threads. '_exit' does not run atexit handlers, just terminates the process. Bug: 116665972 Test: kill android.hardware.audio@2.0-service, check logcat Change-Id: I5391a659e359e0ca5bba580f1c51dea5df3ea562 --- media/libaudiohal/HalDeathHandlerHidl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudiohal/HalDeathHandlerHidl.cpp b/media/libaudiohal/HalDeathHandlerHidl.cpp index 1e3ab58466..6e33523097 100644 --- a/media/libaudiohal/HalDeathHandlerHidl.cpp +++ b/media/libaudiohal/HalDeathHandlerHidl.cpp @@ -54,7 +54,7 @@ void HalDeathHandler::serviceDied(uint64_t /*cookie*/, const wp& /*who*/) handler.second(); } ALOGE("HAL server crashed, audio server is restarting"); - exit(1); + _exit(1); // Avoid calling atexit handlers, as this code runs on a thread from RPC threadpool. } } // namespace android -- GitLab From 1b74640c7bc024fe10088d67d1e9f81892fa1659 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 14 Dec 2018 14:39:35 -0800 Subject: [PATCH 0621/1530] Use local buffer for AdjustChannelNonDestructiveBufferProvider. It will make the track data disordered when adjusting channels non destructive if the track is a static track. Bug: 121047798 Test: Manually Change-Id: Ibd9bc012ac9d36e0f19f6f6796753f9355cd3c97 --- media/libaudioprocessing/BufferProviders.cpp | 23 ++++++++------------ 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp index fe92d43b70..b764ccbc8c 100644 --- a/media/libaudioprocessing/BufferProviders.cpp +++ b/media/libaudioprocessing/BufferProviders.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "BufferProvider" //#define LOG_NDEBUG 0 +#include + #include #include #include @@ -36,13 +38,6 @@ namespace android { // ---------------------------------------------------------------------------- - -template -static inline T min(const T& a, const T& b) -{ - return a < b ? a : b; -} - CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize, size_t bufferFrameCount) : mInputFrameSize(inputFrameSize), @@ -100,8 +95,8 @@ status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer) mConsumed = 0; } ALOG_ASSERT(mConsumed < mBuffer.frameCount); - size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed); - count = min(count, pBuffer->frameCount); + size_t count = std::min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed); + count = std::min(count, pBuffer->frameCount); pBuffer->raw = mLocalBufferData; pBuffer->frameCount = count; copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, @@ -491,7 +486,7 @@ status_t TimestretchBufferProvider::getNextBuffer( } // time-stretch the data - dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired); + dstAvailable = std::min(mLocalBufferFrameCount - mRemaining, outputDesired); size_t srcAvailable = mBuffer.frameCount; processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable, mBuffer.raw, &srcAvailable); @@ -589,7 +584,7 @@ void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames } else { // cyclically repeat the source. for (size_t count = 0; count < *dstFrames; count += *srcFrames) { - size_t remaining = min(*srcFrames, *dstFrames - count); + size_t remaining = std::min(*srcFrames, *dstFrames - count); memcpy((uint8_t*)dstBuffer + mFrameSize * count, srcBuffer, mFrameSize * remaining); } @@ -657,9 +652,9 @@ AdjustChannelsNonDestructiveBufferProvider::AdjustChannelsNonDestructiveBufferPr audio_format_t format, size_t inChannelCount, size_t outChannelCount, audio_format_t contractedFormat, size_t contractedFrameCount, void* contractedBuffer) : CopyBufferProvider( - audio_bytes_per_frame(inChannelCount, format), - audio_bytes_per_frame(outChannelCount, format), - 0 /*bufferFrameCount*/), + audio_bytes_per_frame(std::max(inChannelCount, outChannelCount), format), + audio_bytes_per_frame(std::max(inChannelCount, outChannelCount), format), + contractedFrameCount), mFormat(format), mInChannelCount(inChannelCount), mOutChannelCount(outChannelCount), -- GitLab From 0c5015fca72232b0087530eec1d75fa6e5aa6bfe Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Wed, 28 Nov 2018 18:37:06 +0530 Subject: [PATCH 0622/1530] libeffects: Added support for 88.2kHz and 176.4kHz Test: local native test (lvmtest) Bug: 120806181 Change-Id: Idb06f6917a86634dfabcb51f3a80a5ab579fa91c --- media/libeffects/lvm/lib/Bass/lib/LVDBE.h | 12 +- .../lvm/lib/Bass/src/LVDBE_Coeffs.h | 108 +++++++++- .../lvm/lib/Bass/src/LVDBE_Tables.c | 72 +++++++ .../lvm/lib/Bundle/src/LVM_Coeffs.h | 185 +++++++++++++++++- .../lvm/lib/Bundle/src/LVM_Control.c | 3 +- .../libeffects/lvm/lib/Bundle/src/LVM_Init.c | 32 ++- .../lvm/lib/Bundle/src/LVM_Tables.c | 94 +++++++++ .../libeffects/lvm/lib/Common/lib/LVM_Types.h | 6 +- media/libeffects/lvm/lib/Eq/lib/LVEQNB.h | 12 +- .../libeffects/lvm/lib/Eq/src/LVEQNB_Coeffs.h | 2 + .../libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c | 30 +-- .../lvm/lib/Reverb/src/LVREV_Private.h | 2 +- .../Reverb/src/LVREV_SetControlParameters.c | 3 +- .../lvm/lib/Reverb/src/LVREV_Tables.c | 2 + .../lib/SpectrumAnalyzer/src/LVPSA_Private.h | 2 +- .../lib/SpectrumAnalyzer/src/LVPSA_Tables.c | 18 ++ .../src/LVCS_Headphone_Coeffs.h | 93 +++++++++ .../lvm/lib/StereoWidening/src/LVCS_Tables.c | 73 ++++++- media/libeffects/lvm/tests/lvmtest.cpp | 85 ++++++-- .../lvm/wrapper/Bundle/EffectBundle.cpp | 8 + .../lvm/wrapper/Reverb/EffectReverb.cpp | 6 + 21 files changed, 779 insertions(+), 69 deletions(-) diff --git a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h index a1fa79ae7c..cc066b0ebf 100644 --- a/media/libeffects/lvm/lib/Bass/lib/LVDBE.h +++ b/media/libeffects/lvm/lib/Bass/lib/LVDBE.h @@ -199,8 +199,10 @@ typedef enum #define LVDBE_CAP_FS_44100 128 #define LVDBE_CAP_FS_48000 256 #if defined(BUILD_FLOAT) && defined(HIGHER_FS) -#define LVDBE_CAP_FS_96000 512 -#define LVDBE_CAP_FS_192000 1024 +#define LVDBE_CAP_FS_88200 512 +#define LVDBE_CAP_FS_96000 1024 +#define LVDBE_CAP_FS_176400 2048 +#define LVDBE_CAP_FS_192000 4096 #endif typedef enum @@ -215,8 +217,10 @@ typedef enum LVDBE_FS_44100 = 7, LVDBE_FS_48000 = 8, #if defined(BUILD_FLOAT) && defined(HIGHER_FS) - LVDBE_FS_96000 = 9, - LVDBE_FS_192000 = 10, + LVDBE_FS_88200 = 9, + LVDBE_FS_96000 = 10, + LVDBE_FS_176400 = 11, + LVDBE_FS_192000 = 12, #endif LVDBE_FS_MAX = LVM_MAXINT_32 } LVDBE_Fs_en; diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Coeffs.h b/media/libeffects/lvm/lib/Bass/src/LVDBE_Coeffs.h index 4ecaf1489e..8f058e8529 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Coeffs.h +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Coeffs.h @@ -580,12 +580,24 @@ #define HPF_Fs48000_Fc55_B2 0.989882f #ifdef HIGHER_FS +#define HPF_Fs88200_Fc55_A0 0.985818f +#define HPF_Fs88200_Fc55_A1 (-1.971636f) +#define HPF_Fs88200_Fc55_A2 0.985818f +#define HPF_Fs88200_Fc55_B1 (-1.994466f) +#define HPF_Fs88200_Fc55_B2 0.994481f + #define HPF_Fs96000_Fc55_A0 0.986040f #define HPF_Fs96000_Fc55_A1 (-1.972080f) #define HPF_Fs96000_Fc55_A2 0.986040f #define HPF_Fs96000_Fc55_B1 (-1.994915f) #define HPF_Fs96000_Fc55_B2 0.994928f +#define HPF_Fs176400_Fc55_A0 0.987183f +#define HPF_Fs176400_Fc55_A1 (-1.974366f) +#define HPF_Fs176400_Fc55_A2 0.987183f +#define HPF_Fs176400_Fc55_B1 (-1.997233f) +#define HPF_Fs176400_Fc55_B2 0.997237f + #define HPF_Fs192000_Fc55_A0 0.987294f #define HPF_Fs192000_Fc55_A1 (-1.974588f) #define HPF_Fs192000_Fc55_A2 0.987294f @@ -642,12 +654,24 @@ #define HPF_Fs48000_Fc66_B2 0.987871f #ifdef HIGHER_FS +#define HPF_Fs88200_Fc66_A0 0.985273f +#define HPF_Fs88200_Fc66_A1 (-1.970546f) +#define HPF_Fs88200_Fc66_A2 0.985273f +#define HPF_Fs88200_Fc66_B1 (-1.993359f) +#define HPF_Fs88200_Fc66_B2 0.993381f + #define HPF_Fs96000_Fc66_A0 0.985539f #define HPF_Fs96000_Fc66_A1 (-1.971077f) #define HPF_Fs96000_Fc66_A2 0.985539f #define HPF_Fs96000_Fc66_B1 (-1.993898f) #define HPF_Fs96000_Fc66_B2 0.993917f +#define HPF_Fs176400_Fc66_A0 0.986910f +#define HPF_Fs176400_Fc66_A1 (-1.973820f) +#define HPF_Fs176400_Fc66_A2 0.986910f +#define HPF_Fs176400_Fc66_B1 (-1.996679f) +#define HPF_Fs176400_Fc66_B2 0.996685f + #define HPF_Fs192000_Fc66_A0 0.987043f #define HPF_Fs192000_Fc66_A1 (-1.974086f) #define HPF_Fs192000_Fc66_A2 0.987043f @@ -703,12 +727,24 @@ #define HPF_Fs48000_Fc78_B2 0.985681f #ifdef HIGHER_FS +#define HPF_Fs88200_Fc78_A0 0.984678f +#define HPF_Fs88200_Fc78_A1 (-1.969356f) +#define HPF_Fs88200_Fc78_A2 0.984678f +#define HPF_Fs88200_Fc78_B1 (-1.992151f) +#define HPF_Fs88200_Fc78_B2 0.992182f + #define HPF_Fs96000_Fc78_A0 0.984992f #define HPF_Fs96000_Fc78_A1 (-1.969984f) #define HPF_Fs96000_Fc78_A2 0.984992f #define HPF_Fs96000_Fc78_B1 (-1.992789f) #define HPF_Fs96000_Fc78_B2 0.992815f +#define HPF_Fs176400_Fc78_A0 0.986612f +#define HPF_Fs176400_Fc78_A1 (-1.973224f) +#define HPF_Fs176400_Fc78_A2 0.986612f +#define HPF_Fs176400_Fc78_B1 (-1.996076f) +#define HPF_Fs176400_Fc78_B2 0.996083f + #define HPF_Fs192000_Fc78_A0 0.986769f #define HPF_Fs192000_Fc78_A1 (-1.973539f) #define HPF_Fs192000_Fc78_A2 0.986769f @@ -764,12 +800,24 @@ #define HPF_Fs48000_Fc90_B2 0.983497f #ifdef HIGHER_FS +#define HPF_Fs88200_Fc90_A0 0.984084f +#define HPF_Fs88200_Fc90_A1 (-1.968168f) +#define HPF_Fs88200_Fc90_A2 0.984084f +#define HPF_Fs88200_Fc90_B1 (-1.990944f) +#define HPF_Fs88200_Fc90_B2 0.990985f + #define HPF_Fs96000_Fc90_A0 0.984446f #define HPF_Fs96000_Fc90_A1 (-1.968892f) #define HPF_Fs96000_Fc90_A2 0.984446f #define HPF_Fs96000_Fc90_B1 (-1.991680f) #define HPF_Fs96000_Fc90_B2 0.991714f +#define HPF_Fs176400_Fc90_A0 0.986314f +#define HPF_Fs176400_Fc90_A1 (-1.972629f) +#define HPF_Fs176400_Fc90_A2 0.986314f +#define HPF_Fs176400_Fc90_B1 (-1.995472f) +#define HPF_Fs176400_Fc90_B2 0.995482f + #define HPF_Fs192000_Fc90_A0 0.986496f #define HPF_Fs192000_Fc90_A1 (-1.972992f) #define HPF_Fs192000_Fc90_A2 0.986496f @@ -831,12 +879,24 @@ #define BPF_Fs48000_Fc55_B2 0.996875f #ifdef HIGHER_FS +#define BPF_Fs88200_Fc55_A0 0.000831f +#define BPF_Fs88200_Fc55_A1 0.000000f +#define BPF_Fs88200_Fc55_A2 (-0.000831f) +#define BPF_Fs88200_Fc55_B1 (-1.998321f) +#define BPF_Fs88200_Fc55_B2 0.998338f + #define BPF_Fs96000_Fc55_A0 0.000762f #define BPF_Fs96000_Fc55_A1 0.000000f #define BPF_Fs96000_Fc55_A2 (-0.000762f) #define BPF_Fs96000_Fc55_B1 (-1.998461f) #define BPF_Fs96000_Fc55_B2 0.998477f +#define BPF_Fs176400_Fc55_A0 0.000416f +#define BPF_Fs176400_Fc55_A1 0.000000f +#define BPF_Fs176400_Fc55_A2 (-0.000416f) +#define BPF_Fs176400_Fc55_B1 (-1.999164f) +#define BPF_Fs176400_Fc55_B2 0.999169f + #define BPF_Fs192000_Fc55_A0 0.000381f #define BPF_Fs192000_Fc55_A1 0.000000f #define BPF_Fs192000_Fc55_A2 (-0.000381f) @@ -892,12 +952,24 @@ #define BPF_Fs48000_Fc66_B2 0.995690f #ifdef HIGHER_FS +#define BPF_Fs88200_Fc66_A0 0.001146f +#define BPF_Fs88200_Fc66_A1 0.000000f +#define BPF_Fs88200_Fc66_A2 (-0.001146f) +#define BPF_Fs88200_Fc66_B1 (-1.997684f) +#define BPF_Fs88200_Fc66_B2 0.997708f + #define BPF_Fs96000_Fc66_A0 0.001055f #define BPF_Fs96000_Fc66_A1 0.000000f #define BPF_Fs96000_Fc66_A2 (-0.001055f) #define BPF_Fs96000_Fc66_B1 (-1.997868f) #define BPF_Fs96000_Fc66_B2 0.997891f +#define BPF_Fs176400_Fc66_A0 0.000573f +#define BPF_Fs176400_Fc66_A1 0.000000f +#define BPF_Fs176400_Fc66_A2 (-0.000573f) +#define BPF_Fs176400_Fc66_B1 (-1.998847f) +#define BPF_Fs176400_Fc66_B2 0.998853f + #define BPF_Fs192000_Fc66_A0 0.000528f #define BPF_Fs192000_Fc66_A1 0.000000f #define BPF_Fs192000_Fc66_A2 (-0.000528f) @@ -953,12 +1025,24 @@ #define BPF_Fs48000_Fc78_B2 0.993639f #ifdef HIGHER_FS +#define BPF_Fs88200_Fc78_A0 0.001693f +#define BPF_Fs88200_Fc78_A1 0.000000f +#define BPF_Fs88200_Fc78_A2 (-0.001693f) +#define BPF_Fs88200_Fc78_B1 (-1.996582f) +#define BPF_Fs88200_Fc78_B2 0.996615f + #define BPF_Fs96000_Fc78_A0 0.001555f #define BPF_Fs96000_Fc78_A1 0.000000f #define BPF_Fs96000_Fc78_A2 (-0.0015555f) #define BPF_Fs96000_Fc78_B1 (-1.996860f) #define BPF_Fs96000_Fc78_B2 0.996891f +#define BPF_Fs176400_Fc78_A0 0.000847f +#define BPF_Fs176400_Fc78_A1 0.000000f +#define BPF_Fs176400_Fc78_A2 (-0.000847f) +#define BPF_Fs176400_Fc78_B1 (-1.998298f) +#define BPF_Fs176400_Fc78_B2 0.998306f + #define BPF_Fs192000_Fc78_A0 0.000778f #define BPF_Fs192000_Fc78_A1 0.000000f #define BPF_Fs192000_Fc78_A2 (-0.000778f) @@ -1014,12 +1098,24 @@ #define BPF_Fs48000_Fc90_B2 0.992177f #ifdef HIGHER_FS +#define BPF_Fs88200_Fc90_A0 0.002083f +#define BPF_Fs88200_Fc90_A1 0.000000f +#define BPF_Fs88200_Fc90_A2 (-0.002083f) +#define BPF_Fs88200_Fc90_B1 (-1.995791f) +#define BPF_Fs88200_Fc90_B2 0.995835f + #define BPF_Fs96000_Fc90_A0 0.001913f #define BPF_Fs96000_Fc90_A1 0.000000f #define BPF_Fs96000_Fc90_A2 (-0.001913f) #define BPF_Fs96000_Fc90_B1 (-1.996134f) #define BPF_Fs96000_Fc90_B2 0.996174f +#define BPF_Fs176400_Fc90_A0 0.001042f +#define BPF_Fs176400_Fc90_A1 0.000000f +#define BPF_Fs176400_Fc90_A2 (-0.001042f) +#define BPF_Fs176400_Fc90_B1 (-1.997904f) +#define BPF_Fs176400_Fc90_B2 0.997915f + #define BPF_Fs192000_Fc90_A0 0.000958f #define BPF_Fs192000_Fc90_A1 0.000000f #define BPF_Fs192000_Fc90_A2 (-0.000958f) @@ -1045,7 +1141,9 @@ #define AGC_ATTACK_Fs48000 0.971628f #ifdef HIGHER_FS +#define AGC_ATTACK_Fs88200 0.984458f #define AGC_ATTACK_Fs96000 0.985712f +#define AGC_ATTACK_Fs176400 0.992199f #define AGC_ATTACK_Fs192000 0.992830f #endif @@ -1062,7 +1160,9 @@ #define AGC_DECAY_Fs48000 0.000007f #ifdef HIGHER_FS +#define AGC_DECAY_Fs88200 0.0000038f #define AGC_DECAY_FS96000 0.0000035f +#define AGC_DECAY_Fs176400 0.00000188f #define AGC_DECAY_FS192000 0.00000175f #endif @@ -1125,7 +1225,9 @@ #define VOL_TC_Fs44100 0.004525f #define VOL_TC_Fs48000 0.004158f #ifdef HIGHER_FS +#define VOL_TC_Fs88200 0.002263f #define VOL_TC_Fs96000 0.002079f +#define VOL_TC_Fs176400 0.001131f #define VOL_TC_Fs192000 0.001039f #endif #define MIX_TC_Fs8000 29365 /* Floating point value 0.896151 */ @@ -1138,9 +1240,13 @@ #define MIX_TC_Fs44100 32097 /* Floating point value 0.979515 */ #define MIX_TC_Fs48000 32150 /* Floating point value 0.981150 */ #ifdef HIGHER_FS +/* Floating point value 0.989704 */ +#define MIX_TC_Fs88200 32430 #define MIX_TC_Fs96000 32456 /* Floating point value 0.990530 */ +/* Floating point value 0.994838 */ +#define MIX_TC_Fs176400 32598 #define MIX_TC_Fs192000 32611 /* Floating point value 0.992524 */ #endif #endif /*BUILD_FLOAT*/ -#endif \ No newline at end of file +#endif diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c index c4a9b1423d..a2ce40458f 100644 --- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c +++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Tables.c @@ -88,11 +88,21 @@ const BQ_FLOAT_Coefs_t LVDBE_HPF_Table[] = { -HPF_Fs48000_Fc55_B2, -HPF_Fs48000_Fc55_B1}, #ifdef HIGHER_FS + {HPF_Fs88200_Fc55_A2, /* 88kS/s coefficients */ + HPF_Fs88200_Fc55_A1, + HPF_Fs88200_Fc55_A0, + -HPF_Fs88200_Fc55_B2, + -HPF_Fs88200_Fc55_B1}, {HPF_Fs96000_Fc55_A2, /* 96kS/s coefficients */ HPF_Fs96000_Fc55_A1, HPF_Fs96000_Fc55_A0, -HPF_Fs96000_Fc55_B2, -HPF_Fs96000_Fc55_B1}, + {HPF_Fs176400_Fc55_A2, /* 176kS/s coefficients */ + HPF_Fs176400_Fc55_A1, + HPF_Fs176400_Fc55_A0, + -HPF_Fs176400_Fc55_B2, + -HPF_Fs176400_Fc55_B1}, {HPF_Fs192000_Fc55_A2, /* 192kS/s coefficients */ HPF_Fs192000_Fc55_A1, HPF_Fs192000_Fc55_A0, @@ -147,11 +157,21 @@ const BQ_FLOAT_Coefs_t LVDBE_HPF_Table[] = { -HPF_Fs48000_Fc66_B2, -HPF_Fs48000_Fc66_B1}, #ifdef HIGHER_FS + {HPF_Fs88200_Fc66_A2, /* 88kS/s coefficients */ + HPF_Fs88200_Fc66_A1, + HPF_Fs88200_Fc66_A0, + -HPF_Fs88200_Fc66_B2, + -HPF_Fs88200_Fc66_B1}, {HPF_Fs96000_Fc66_A2, /* 96kS/s coefficients */ HPF_Fs96000_Fc66_A1, HPF_Fs96000_Fc66_A0, -HPF_Fs96000_Fc66_B2, -HPF_Fs96000_Fc66_B1}, + {HPF_Fs176400_Fc66_A2, /* 176kS/s coefficients */ + HPF_Fs176400_Fc66_A1, + HPF_Fs176400_Fc66_A0, + -HPF_Fs176400_Fc66_B2, + -HPF_Fs176400_Fc66_B1}, {HPF_Fs192000_Fc66_A2, /* 192kS/s coefficients */ HPF_Fs192000_Fc66_A1, HPF_Fs192000_Fc66_A0, @@ -207,11 +227,21 @@ const BQ_FLOAT_Coefs_t LVDBE_HPF_Table[] = { -HPF_Fs48000_Fc78_B2, -HPF_Fs48000_Fc78_B1}, #ifdef HIGHER_FS + {HPF_Fs88200_Fc78_A2, /* 88kS/s coefficients */ + HPF_Fs88200_Fc78_A1, + HPF_Fs88200_Fc78_A0, + -HPF_Fs88200_Fc78_B2, + -HPF_Fs88200_Fc78_B1}, {HPF_Fs96000_Fc78_A2, /* 96kS/s coefficients */ HPF_Fs96000_Fc78_A1, HPF_Fs96000_Fc78_A0, -HPF_Fs96000_Fc78_B2, -HPF_Fs96000_Fc78_B1}, + {HPF_Fs176400_Fc78_A2, /* 176kS/s coefficients */ + HPF_Fs176400_Fc78_A1, + HPF_Fs176400_Fc78_A0, + -HPF_Fs176400_Fc78_B2, + -HPF_Fs176400_Fc78_B1}, {HPF_Fs192000_Fc78_A2, /* 192kS/s coefficients */ HPF_Fs192000_Fc78_A1, HPF_Fs192000_Fc78_A0, @@ -269,11 +299,21 @@ const BQ_FLOAT_Coefs_t LVDBE_HPF_Table[] = { #ifdef HIGHER_FS , + {HPF_Fs88200_Fc90_A2, /* 88kS/s coefficients */ + HPF_Fs88200_Fc90_A1, + HPF_Fs88200_Fc90_A0, + -HPF_Fs88200_Fc90_B2, + -HPF_Fs88200_Fc90_B1}, {HPF_Fs96000_Fc90_A2, /* 96kS/s coefficients */ HPF_Fs96000_Fc90_A1, HPF_Fs96000_Fc90_A0, -HPF_Fs96000_Fc90_B2, -HPF_Fs96000_Fc90_B1}, + {HPF_Fs176400_Fc90_A2, /* 176kS/s coefficients */ + HPF_Fs176400_Fc90_A1, + HPF_Fs176400_Fc90_A0, + -HPF_Fs176400_Fc90_B2, + -HPF_Fs176400_Fc90_B1}, {HPF_Fs192000_Fc90_A2, /* 192kS/s coefficients */ HPF_Fs192000_Fc90_A1, HPF_Fs192000_Fc90_A0, @@ -320,9 +360,15 @@ const BP_FLOAT_Coefs_t LVDBE_BPF_Table[] = { -BPF_Fs48000_Fc55_B2, -BPF_Fs48000_Fc55_B1}, #ifdef HIGHER_FS + {BPF_Fs88200_Fc55_A0, /* 88kS/s coefficients */ + -BPF_Fs88200_Fc55_B2, + -BPF_Fs88200_Fc55_B1}, {BPF_Fs96000_Fc55_A0, /* 96kS/s coefficients */ -BPF_Fs96000_Fc55_B2, -BPF_Fs96000_Fc55_B1}, + {BPF_Fs176400_Fc55_A0, /* 176kS/s coefficients */ + -BPF_Fs176400_Fc55_B2, + -BPF_Fs176400_Fc55_B1}, {BPF_Fs192000_Fc55_A0, /* 192kS/s coefficients */ -BPF_Fs192000_Fc55_B2, -BPF_Fs192000_Fc55_B1}, @@ -357,9 +403,15 @@ const BP_FLOAT_Coefs_t LVDBE_BPF_Table[] = { -BPF_Fs48000_Fc66_B2, -BPF_Fs48000_Fc66_B1}, #ifdef HIGHER_FS + {BPF_Fs88200_Fc66_A0, /* 88kS/s coefficients */ + -BPF_Fs88200_Fc66_B2, + -BPF_Fs88200_Fc66_B1}, {BPF_Fs96000_Fc66_A0, /* 96kS/s coefficients */ -BPF_Fs96000_Fc66_B2, -BPF_Fs96000_Fc66_B1}, + {BPF_Fs176400_Fc66_A0, /* 176kS/s coefficients */ + -BPF_Fs176400_Fc66_B2, + -BPF_Fs176400_Fc66_B1}, {BPF_Fs192000_Fc66_A0, /* 192kS/s coefficients */ -BPF_Fs192000_Fc66_B2, -BPF_Fs192000_Fc66_B1}, @@ -394,9 +446,15 @@ const BP_FLOAT_Coefs_t LVDBE_BPF_Table[] = { -BPF_Fs48000_Fc78_B2, -BPF_Fs48000_Fc78_B1}, #ifdef HIGHER_FS + {BPF_Fs88200_Fc66_A0, /* 88kS/s coefficients */ + -BPF_Fs88200_Fc66_B2, + -BPF_Fs88200_Fc66_B1}, {BPF_Fs96000_Fc78_A0, /* 96kS/s coefficients */ -BPF_Fs96000_Fc78_B2, -BPF_Fs96000_Fc78_B1}, + {BPF_Fs176400_Fc66_A0, /* 176kS/s coefficients */ + -BPF_Fs176400_Fc66_B2, + -BPF_Fs176400_Fc66_B1}, {BPF_Fs192000_Fc78_A0, /* 192kS/s coefficients */ -BPF_Fs192000_Fc78_B2, -BPF_Fs192000_Fc78_B1}, @@ -432,9 +490,15 @@ const BP_FLOAT_Coefs_t LVDBE_BPF_Table[] = { -BPF_Fs48000_Fc90_B1} #ifdef HIGHER_FS , + {BPF_Fs88200_Fc90_A0, /* 88kS/s coefficients */ + -BPF_Fs88200_Fc90_B2, + -BPF_Fs88200_Fc90_B1}, {BPF_Fs96000_Fc90_A0, /* 96kS/s coefficients */ -BPF_Fs96000_Fc90_B2, -BPF_Fs96000_Fc90_B1}, + {BPF_Fs176400_Fc90_A0, /* 176kS/s coefficients */ + -BPF_Fs176400_Fc90_B2, + -BPF_Fs176400_Fc90_B1}, {BPF_Fs192000_Fc90_A0, /* 192kS/s coefficients */ -BPF_Fs192000_Fc90_B2, -BPF_Fs192000_Fc90_B1} @@ -466,7 +530,9 @@ const LVM_FLOAT LVDBE_AGC_ATTACK_Table[] = { AGC_ATTACK_Fs44100, AGC_ATTACK_Fs48000 #ifdef HIGHER_FS + ,AGC_ATTACK_Fs88200 ,AGC_ATTACK_Fs96000 + ,AGC_ATTACK_Fs176400 ,AGC_ATTACK_Fs192000 #endif @@ -488,7 +554,9 @@ const LVM_FLOAT LVDBE_AGC_DECAY_Table[] = { AGC_DECAY_Fs44100, AGC_DECAY_Fs48000 #ifdef HIGHER_FS + ,AGC_DECAY_Fs88200 ,AGC_DECAY_FS96000 + ,AGC_DECAY_Fs176400 ,AGC_DECAY_FS192000 #endif @@ -583,7 +651,9 @@ const LVM_FLOAT LVDBE_VolumeTCTable[] = { VOL_TC_Fs44100, VOL_TC_Fs48000 #ifdef HIGHER_FS + ,VOL_TC_Fs88200 ,VOL_TC_Fs96000 + ,VOL_TC_Fs176400 ,VOL_TC_Fs192000 #endif }; @@ -602,7 +672,9 @@ const LVM_INT16 LVDBE_MixerTCTable[] = { MIX_TC_Fs44100, MIX_TC_Fs48000 #ifdef HIGHER_FS + ,MIX_TC_Fs88200 ,MIX_TC_Fs96000 + ,MIX_TC_Fs176400 ,MIX_TC_Fs192000 #endif diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Coeffs.h b/media/libeffects/lvm/lib/Bundle/src/LVM_Coeffs.h index 8c048477d4..bab4049914 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Coeffs.h +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Coeffs.h @@ -487,6 +487,97 @@ #define HPF_Fs48000_Gain15_B2 0.000000 #ifdef HIGHER_FS +/* Coefficients for sample rate 88200 */ +/* Gain = 1.000000 dB */ +#define HPF_Fs88200_Gain1_A0 1.094374f +#define HPF_Fs88200_Gain1_A1 (-0.641256f) +#define HPF_Fs88200_Gain1_A2 0.000000f +#define HPF_Fs88200_Gain1_B1 (-0.546882f) +#define HPF_Fs88200_Gain1_B2 0.000000f +/* Gain = 2.000000 dB */ +#define HPF_Fs88200_Gain2_A0 1.200264f +#define HPF_Fs88200_Gain2_A1 (-0.747146f) +#define HPF_Fs88200_Gain2_A2 0.000000f +#define HPF_Fs88200_Gain2_B1 (-0.546882f) +#define HPF_Fs88200_Gain2_B2 0.000000f +/* Gain = 3.000000 dB */ +#define HPF_Fs88200_Gain3_A0 1.319074f +#define HPF_Fs88200_Gain3_A1 (-0.865956f) +#define HPF_Fs88200_Gain3_A2 0.000000f +#define HPF_Fs88200_Gain3_B1 (-0.546882f) +#define HPF_Fs88200_Gain3_B2 0.000000f +/* Gain = 4.000000 dB */ +#define HPF_Fs88200_Gain4_A0 1.452380f +#define HPF_Fs88200_Gain4_A1 (-0.999263f) +#define HPF_Fs88200_Gain4_A2 0.000000f +#define HPF_Fs88200_Gain4_B1 (-0.546882f) +#define HPF_Fs88200_Gain4_B2 0.000000f +/* Gain = 5.000000 dB */ +#define HPF_Fs88200_Gain5_A0 1.601953f +#define HPF_Fs88200_Gain5_A1 (-1.148836f) +#define HPF_Fs88200_Gain5_A2 0.000000f +#define HPF_Fs88200_Gain5_B1 (-0.546882f) +#define HPF_Fs88200_Gain5_B2 0.000000f +/* Gain = 6.000000 dB */ +#define HPF_Fs88200_Gain6_A0 1.769777f +#define HPF_Fs88200_Gain6_A1 (-1.316659f) +#define HPF_Fs88200_Gain6_A2 0.000000f +#define HPF_Fs88200_Gain6_B1 (-0.546882f) +#define HPF_Fs88200_Gain6_B2 0.000000f +/* Gain = 7.000000 dB */ +#define HPF_Fs88200_Gain7_A0 1.958078f +#define HPF_Fs88200_Gain7_A1 (-1.504960f) +#define HPF_Fs88200_Gain7_A2 0.000000f +#define HPF_Fs88200_Gain7_B1 (-0.546882f) +#define HPF_Fs88200_Gain7_B2 0.000000f +/* Gain = 8.000000 dB */ +#define HPF_Fs88200_Gain8_A0 2.169355f +#define HPF_Fs88200_Gain8_A1 (-1.716238f) +#define HPF_Fs88200_Gain8_A2 0.000000f +#define HPF_Fs88200_Gain8_B1 (-0.546882f) +#define HPF_Fs88200_Gain8_B2 0.000000f +/* Gain = 9.000000 dB */ +#define HPF_Fs88200_Gain9_A0 2.406412f +#define HPF_Fs88200_Gain9_A1 (-1.953295f) +#define HPF_Fs88200_Gain9_A2 0.000000f +#define HPF_Fs88200_Gain9_B1 (-0.546882f) +#define HPF_Fs88200_Gain9_B2 0.000000f +/* Gain = 10.000000 dB */ +#define HPF_Fs88200_Gain10_A0 2.672395f +#define HPF_Fs88200_Gain10_A1 (-2.219277f) +#define HPF_Fs88200_Gain10_A2 0.000000f +#define HPF_Fs88200_Gain10_B1 (-0.546882f) +#define HPF_Fs88200_Gain10_B2 0.000000f +/* Gain = 11.000000 dB */ +#define HPF_Fs88200_Gain11_A0 2.970832f +#define HPF_Fs88200_Gain11_A1 (-2.517714f) +#define HPF_Fs88200_Gain11_A2 0.000000f +#define HPF_Fs88200_Gain11_B1 (-0.546882f) +#define HPF_Fs88200_Gain11_B2 0.000000f +/* Gain = 12.000000 dB */ +#define HPF_Fs88200_Gain12_A0 3.305684f +#define HPF_Fs88200_Gain12_A1 (-2.852566f) +#define HPF_Fs88200_Gain12_A2 0.000000f +#define HPF_Fs88200_Gain12_B1 (-0.546882f) +#define HPF_Fs88200_Gain12_B2 0.000000f +/* Gain = 13.000000 dB */ +#define HPF_Fs88200_Gain13_A0 3.681394f +#define HPF_Fs88200_Gain13_A1 (-3.228276f) +#define HPF_Fs88200_Gain13_A2 0.000000f +#define HPF_Fs88200_Gain13_B1 (-0.546882f) +#define HPF_Fs88200_Gain13_B2 0.000000f +/* Gain = 14.000000 dB */ +#define HPF_Fs88200_Gain14_A0 4.102947f +#define HPF_Fs88200_Gain14_A1 (-3.649830f) +#define HPF_Fs88200_Gain14_A2 0.000000f +#define HPF_Fs88200_Gain14_B1 (-0.546882f) +#define HPF_Fs88200_Gain14_B2 0.000000f +/* Gain = 15.000000 dB */ +#define HPF_Fs88200_Gain15_A0 4.575938f +#define HPF_Fs88200_Gain15_A1 (-4.122820f) +#define HPF_Fs88200_Gain15_A2 0.000000f +#define HPF_Fs88200_Gain15_B1 (-0.546882f) +#define HPF_Fs88200_Gain15_B2 0.000000f /* Coefficients for sample rate 96000Hz */ /* Gain = 1.000000 dB */ @@ -580,6 +671,98 @@ #define HPF_Fs96000_Gain15_B1 (-0.577350) #define HPF_Fs96000_Gain15_B2 0.000000 +/* Coefficients for sample rate 176400 */ +/* Gain = 1.000000 dB */ +#define HPF_Fs176400_Gain1_A0 1.106711f +#define HPF_Fs176400_Gain1_A1 (-0.855807f) +#define HPF_Fs176400_Gain1_A2 0.000000f +#define HPF_Fs176400_Gain1_B1 (-0.749096f) +#define HPF_Fs176400_Gain1_B2 0.000000f +/* Gain = 2.000000 dB */ +#define HPF_Fs176400_Gain2_A0 1.226443f +#define HPF_Fs176400_Gain2_A1 (-0.975539f) +#define HPF_Fs176400_Gain2_A2 0.000000f +#define HPF_Fs176400_Gain2_B1 (-0.749096f) +#define HPF_Fs176400_Gain2_B2 0.000000f +/* Gain = 3.000000 dB */ +#define HPF_Fs176400_Gain3_A0 1.360784f +#define HPF_Fs176400_Gain3_A1 (-1.109880f) +#define HPF_Fs176400_Gain3_A2 0.000000f +#define HPF_Fs176400_Gain3_B1 (-0.749096f) +#define HPF_Fs176400_Gain3_B2 0.000000f +/* Gain = 4.000000 dB */ +#define HPF_Fs176400_Gain4_A0 1.511517f +#define HPF_Fs176400_Gain4_A1 (-1.260613f) +#define HPF_Fs176400_Gain4_A2 0.000000f +#define HPF_Fs176400_Gain4_B1 (-0.749096f) +#define HPF_Fs176400_Gain4_B2 0.000000f +/* Gain = 5.000000 dB */ +#define HPF_Fs176400_Gain5_A0 1.680643f +#define HPF_Fs176400_Gain5_A1 (-1.429739f) +#define HPF_Fs176400_Gain5_A2 0.000000f +#define HPF_Fs176400_Gain5_B1 (-0.749096f) +#define HPF_Fs176400_Gain5_B2 0.000000f +/* Gain = 6.000000 dB */ +#define HPF_Fs176400_Gain6_A0 1.870405f +#define HPF_Fs176400_Gain6_A1 (-1.619501f) +#define HPF_Fs176400_Gain6_A2 0.000000f +#define HPF_Fs176400_Gain6_B1 (-0.749096f) +#define HPF_Fs176400_Gain6_B2 0.000000f +/* Gain = 7.000000 dB */ +#define HPF_Fs176400_Gain7_A0 2.083321f +#define HPF_Fs176400_Gain7_A1 (-1.832417f) +#define HPF_Fs176400_Gain7_A2 0.000000f +#define HPF_Fs176400_Gain7_B1 (-0.749096f) +#define HPF_Fs176400_Gain7_B2 0.000000f +/* Gain = 8.000000 dB */ +#define HPF_Fs176400_Gain8_A0 2.322217f +#define HPF_Fs176400_Gain8_A1 (-2.071313f) +#define HPF_Fs176400_Gain8_A2 0.000000f +#define HPF_Fs176400_Gain8_B1 (-0.749096f) +#define HPF_Fs176400_Gain8_B2 0.000000f +/* Gain = 9.000000 dB */ +#define HPF_Fs176400_Gain9_A0 2.590263f +#define HPF_Fs176400_Gain9_A1 (-2.339359f) +#define HPF_Fs176400_Gain9_A2 0.000000f +#define HPF_Fs176400_Gain9_B1 (-0.749096f) +#define HPF_Fs176400_Gain9_B2 0.000000f +/* Gain = 10.000000 dB */ +#define HPF_Fs176400_Gain10_A0 2.891016f +#define HPF_Fs176400_Gain10_A1 (-2.640112f) +#define HPF_Fs176400_Gain10_A2 0.000000f +#define HPF_Fs176400_Gain10_B1 (-0.749096f) +#define HPF_Fs176400_Gain10_B2 0.000000f +/* Gain = 11.000000 dB */ +#define HPF_Fs176400_Gain11_A0 3.228465f +#define HPF_Fs176400_Gain11_A1 (-2.977561f) +#define HPF_Fs176400_Gain11_A2 0.000000f +#define HPF_Fs176400_Gain11_B1 (-0.749096f) +#define HPF_Fs176400_Gain11_B2 0.000000f +/* Gain = 12.000000 dB */ +#define HPF_Fs176400_Gain12_A0 3.607090f +#define HPF_Fs176400_Gain12_A1 (-3.356186f) +#define HPF_Fs176400_Gain12_A2 0.000000f +#define HPF_Fs176400_Gain12_B1 (-0.749096f) +#define HPF_Fs176400_Gain12_B2 0.000000f +/* Gain = 13.000000 dB */ +#define HPF_Fs176400_Gain13_A0 4.031914f +#define HPF_Fs176400_Gain13_A1 (-3.781010f) +#define HPF_Fs176400_Gain13_A2 0.000000f +#define HPF_Fs176400_Gain13_B1 (-0.749096f) +#define HPF_Fs176400_Gain13_B2 0.000000f +/* Gain = 14.000000 dB */ +#define HPF_Fs176400_Gain14_A0 4.508575f +#define HPF_Fs176400_Gain14_A1 (-4.257671f) +#define HPF_Fs176400_Gain14_A2 0.000000f +#define HPF_Fs176400_Gain14_B1 (-0.749096f) +#define HPF_Fs176400_Gain14_B2 0.000000f +/* Gain = 15.000000 dB */ +#define HPF_Fs176400_Gain15_A0 5.043397f +#define HPF_Fs176400_Gain15_A1 (-4.792493f) +#define HPF_Fs176400_Gain15_A2 0.000000f +#define HPF_Fs176400_Gain15_B1 (-0.749096f) +#define HPF_Fs176400_Gain15_B2 0.000000f + /* Coefficients for sample rate 192000Hz */ /* Gain = 1.000000 dB */ #define HPF_Fs192000_Gain1_A0 1.107823 @@ -1216,4 +1399,4 @@ #endif -#endif \ No newline at end of file +#endif diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c index 7b85f230f2..62b4c7360b 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c @@ -71,7 +71,8 @@ LVM_ReturnStatus_en LVM_SetControlParameters(LVM_Handle_t hInstance, ((pParams->SampleRate != LVM_FS_8000) && (pParams->SampleRate != LVM_FS_11025) && (pParams->SampleRate != LVM_FS_12000) && (pParams->SampleRate != LVM_FS_16000) && (pParams->SampleRate != LVM_FS_22050) && (pParams->SampleRate != LVM_FS_24000) && (pParams->SampleRate != LVM_FS_32000) && (pParams->SampleRate != LVM_FS_44100) && (pParams->SampleRate != LVM_FS_48000) && - (pParams->SampleRate != LVM_FS_96000) && (pParams->SampleRate != LVM_FS_192000)) || + (pParams->SampleRate != LVM_FS_88200) && (pParams->SampleRate != LVM_FS_96000) && + (pParams->SampleRate != LVM_FS_176400) && (pParams->SampleRate != LVM_FS_192000)) || #else ((pParams->SampleRate != LVM_FS_8000) && (pParams->SampleRate != LVM_FS_11025) && (pParams->SampleRate != LVM_FS_12000) && (pParams->SampleRate != LVM_FS_16000) && (pParams->SampleRate != LVM_FS_22050) && (pParams->SampleRate != LVM_FS_24000) && diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c index ade329b675..0669a817a9 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c @@ -233,7 +233,13 @@ LVM_ReturnStatus_en LVM_GetMemoryTable(LVM_Handle_t hInstance, * Set the capabilities */ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) - DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | LVDBE_CAP_FS_48000 | LVDBE_CAP_FS_96000 | LVDBE_CAP_FS_192000; + DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | + LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | + LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | + LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | + LVDBE_CAP_FS_48000 | LVDBE_CAP_FS_88200 | + LVDBE_CAP_FS_96000 | LVDBE_CAP_FS_176400 | + LVDBE_CAP_FS_192000; #else DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | LVDBE_CAP_FS_48000; #endif @@ -270,7 +276,13 @@ LVM_ReturnStatus_en LVM_GetMemoryTable(LVM_Handle_t hInstance, * Set the capabilities */ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) - EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | LVEQNB_CAP_FS_48000 | LVEQNB_CAP_FS_96000 | LVEQNB_CAP_FS_192000; + EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | + LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | + LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | + LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | + LVEQNB_CAP_FS_48000 | LVEQNB_CAP_FS_88200 | + LVEQNB_CAP_FS_96000 | LVEQNB_CAP_FS_176400 | + LVEQNB_CAP_FS_192000; #else EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | LVEQNB_CAP_FS_48000; #endif @@ -747,7 +759,13 @@ LVM_ReturnStatus_en LVM_GetInstanceHandle(LVM_Handle_t *phInstance, * Set the initialisation capabilities */ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) - DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | LVDBE_CAP_FS_48000 | LVDBE_CAP_FS_96000 | LVDBE_CAP_FS_192000; + DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | + LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | + LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | + LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | + LVDBE_CAP_FS_48000 | LVDBE_CAP_FS_88200 | + LVDBE_CAP_FS_96000 | LVDBE_CAP_FS_176400 | + LVDBE_CAP_FS_192000; #else DBE_Capabilities.SampleRate = LVDBE_CAP_FS_8000 | LVDBE_CAP_FS_11025 | LVDBE_CAP_FS_12000 | LVDBE_CAP_FS_16000 | LVDBE_CAP_FS_22050 | LVDBE_CAP_FS_24000 | LVDBE_CAP_FS_32000 | LVDBE_CAP_FS_44100 | LVDBE_CAP_FS_48000; #endif @@ -805,7 +823,13 @@ LVM_ReturnStatus_en LVM_GetInstanceHandle(LVM_Handle_t *phInstance, * Set the initialisation capabilities */ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) - EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | LVEQNB_CAP_FS_48000 | LVEQNB_CAP_FS_96000 | LVEQNB_CAP_FS_192000; + EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | + LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | + LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | + LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | + LVEQNB_CAP_FS_48000 | LVEQNB_CAP_FS_88200 | + LVEQNB_CAP_FS_96000 | LVEQNB_CAP_FS_176400 | + LVEQNB_CAP_FS_192000; #else EQNB_Capabilities.SampleRate = LVEQNB_CAP_FS_8000 | LVEQNB_CAP_FS_11025 | LVEQNB_CAP_FS_12000 | LVEQNB_CAP_FS_16000 | LVEQNB_CAP_FS_22050 | LVEQNB_CAP_FS_24000 | LVEQNB_CAP_FS_32000 | LVEQNB_CAP_FS_44100 | LVEQNB_CAP_FS_48000; #endif diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c index 199ddde4d2..a5356d2616 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Tables.c @@ -269,6 +269,53 @@ FO_FLOAT_LShx_Coefs_t LVM_TrebleBoostCoefs[] = { -HPF_Fs48000_Gain15_B1} #ifdef HIGHER_FS , + /* 88kHz Sampling rate */ + {HPF_Fs88200_Gain1_A1, /* Gain Setting 1 */ + HPF_Fs88200_Gain1_A0, + -HPF_Fs88200_Gain1_B1}, + {HPF_Fs88200_Gain2_A1, /* Gain Setting 2 */ + HPF_Fs88200_Gain2_A0, + -HPF_Fs88200_Gain2_B1}, + {HPF_Fs88200_Gain3_A1, /* Gain Setting 3 */ + HPF_Fs88200_Gain3_A0, + -HPF_Fs88200_Gain3_B1}, + {HPF_Fs88200_Gain4_A1, /* Gain Setting 4 */ + HPF_Fs88200_Gain4_A0, + -HPF_Fs88200_Gain4_B1}, + {HPF_Fs88200_Gain5_A1, /* Gain Setting 5 */ + HPF_Fs88200_Gain5_A0, + -HPF_Fs88200_Gain5_B1}, + {HPF_Fs88200_Gain6_A1, /* Gain Setting 6 */ + HPF_Fs88200_Gain6_A0, + -HPF_Fs88200_Gain6_B1}, + {HPF_Fs88200_Gain7_A1, /* Gain Setting 7 */ + HPF_Fs88200_Gain7_A0, + -HPF_Fs88200_Gain7_B1}, + {HPF_Fs88200_Gain8_A1, /* Gain Setting 8 */ + HPF_Fs88200_Gain8_A0, + -HPF_Fs88200_Gain8_B1}, + {HPF_Fs88200_Gain9_A1, /* Gain Setting 9 */ + HPF_Fs88200_Gain9_A0, + -HPF_Fs88200_Gain9_B1}, + {HPF_Fs88200_Gain10_A1, /* Gain Setting 10 */ + HPF_Fs88200_Gain10_A0, + -HPF_Fs88200_Gain10_B1}, + {HPF_Fs88200_Gain11_A1, /* Gain Setting 11 */ + HPF_Fs88200_Gain11_A0, + -HPF_Fs88200_Gain11_B1}, + {HPF_Fs88200_Gain12_A1, /* Gain Setting 12 */ + HPF_Fs88200_Gain12_A0, + -HPF_Fs88200_Gain12_B1}, + {HPF_Fs88200_Gain13_A1, /* Gain Setting 13 */ + HPF_Fs88200_Gain13_A0, + -HPF_Fs88200_Gain13_B1}, + {HPF_Fs88200_Gain14_A1, /* Gain Setting 14 */ + HPF_Fs88200_Gain14_A0, + -HPF_Fs88200_Gain14_B1}, + {HPF_Fs88200_Gain15_A1, /* Gain Setting 15 */ + HPF_Fs88200_Gain15_A0, + -HPF_Fs88200_Gain15_B1}, + /* 96kHz sampling rate */ {HPF_Fs96000_Gain1_A1, /* Gain setting 1 */ HPF_Fs96000_Gain1_A0, @@ -316,6 +363,53 @@ FO_FLOAT_LShx_Coefs_t LVM_TrebleBoostCoefs[] = { HPF_Fs96000_Gain15_A0, -HPF_Fs96000_Gain15_B1}, + /* 176kHz Sampling rate */ + {HPF_Fs176400_Gain1_A1, /* Gain Setting 1 */ + HPF_Fs176400_Gain1_A0, + -HPF_Fs176400_Gain1_B1}, + {HPF_Fs176400_Gain2_A1, /* Gain Setting 2 */ + HPF_Fs176400_Gain2_A0, + -HPF_Fs176400_Gain2_B1}, + {HPF_Fs176400_Gain3_A1, /* Gain Setting 3 */ + HPF_Fs176400_Gain3_A0, + -HPF_Fs176400_Gain3_B1}, + {HPF_Fs176400_Gain4_A1, /* Gain Setting 4 */ + HPF_Fs176400_Gain4_A0, + -HPF_Fs176400_Gain4_B1}, + {HPF_Fs176400_Gain5_A1, /* Gain Setting 5 */ + HPF_Fs176400_Gain5_A0, + -HPF_Fs176400_Gain5_B1}, + {HPF_Fs176400_Gain6_A1, /* Gain Setting 6 */ + HPF_Fs176400_Gain6_A0, + -HPF_Fs176400_Gain6_B1}, + {HPF_Fs176400_Gain7_A1, /* Gain Setting 7 */ + HPF_Fs176400_Gain7_A0, + -HPF_Fs176400_Gain7_B1}, + {HPF_Fs176400_Gain8_A1, /* Gain Setting 8 */ + HPF_Fs176400_Gain8_A0, + -HPF_Fs176400_Gain8_B1}, + {HPF_Fs176400_Gain9_A1, /* Gain Setting 9 */ + HPF_Fs176400_Gain9_A0, + -HPF_Fs176400_Gain9_B1}, + {HPF_Fs176400_Gain10_A1, /* Gain Setting 10 */ + HPF_Fs176400_Gain10_A0, + -HPF_Fs176400_Gain10_B1}, + {HPF_Fs176400_Gain11_A1, /* Gain Setting 11 */ + HPF_Fs176400_Gain11_A0, + -HPF_Fs176400_Gain11_B1}, + {HPF_Fs176400_Gain12_A1, /* Gain Setting 12 */ + HPF_Fs176400_Gain12_A0, + -HPF_Fs176400_Gain12_B1}, + {HPF_Fs176400_Gain13_A1, /* Gain Setting 13 */ + HPF_Fs176400_Gain13_A0, + -HPF_Fs176400_Gain13_B1}, + {HPF_Fs176400_Gain14_A1, /* Gain Setting 14 */ + HPF_Fs176400_Gain14_A0, + -HPF_Fs176400_Gain14_B1}, + {HPF_Fs176400_Gain15_A1, /* Gain Setting 15 */ + HPF_Fs176400_Gain15_A0, + -HPF_Fs176400_Gain15_B1}, + /* 192kHz sampling rate */ {HPF_Fs192000_Gain1_A1, /* Gain setting 1 */ HPF_Fs192000_Gain1_A0, diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h index 303b62d92e..59586e012f 100644 --- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h +++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h @@ -169,8 +169,10 @@ typedef enum LVM_FS_44100 = 7, LVM_FS_48000 = 8, #ifdef HIGHER_FS - LVM_FS_96000 = 9, - LVM_FS_192000 = 10, + LVM_FS_88200 = 9, + LVM_FS_96000 = 10, + LVM_FS_176400 = 11, + LVM_FS_192000 = 12, #endif LVM_FS_INVALID = LVM_MAXENUM-1, LVM_FS_DUMMY = LVM_MAXENUM diff --git a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h index e7fdbf60da..385dbcf870 100644 --- a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h +++ b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h @@ -201,8 +201,10 @@ typedef enum #define LVEQNB_CAP_FS_44100 128 #define LVEQNB_CAP_FS_48000 256 #if defined(BUILD_FLOAT) && defined(HIGHER_FS) -#define LVEQNB_CAP_FS_96000 512 -#define LVEQNB_CAP_FS_192000 1024 +#define LVEQNB_CAP_FS_88200 512 +#define LVEQNB_CAP_FS_96000 1024 +#define LVEQNB_CAP_FS_176400 2048 +#define LVEQNB_CAP_FS_192000 4096 #endif typedef enum @@ -217,8 +219,10 @@ typedef enum LVEQNB_FS_44100 = 7, LVEQNB_FS_48000 = 8, #ifdef HIGHER_FS - LVEQNB_FS_96000 = 9, - LVEQNB_FS_192000 = 10, + LVEQNB_FS_88200 = 9, + LVEQNB_FS_96000 = 10, + LVEQNB_FS_176400 = 11, + LVEQNB_FS_192000 = 12, #endif LVEQNB_FS_MAX = LVM_MAXINT_32 } LVEQNB_Fs_en; diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Coeffs.h b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Coeffs.h index 42ea46f98e..755141ed8e 100644 --- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Coeffs.h +++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Coeffs.h @@ -109,7 +109,9 @@ #define LVEQNB_2PiOn_48000 0.000131f #ifdef HIGHER_FS +#define LVEQNB_2PiOn_88200 0.000071f #define LVEQNB_2PiOn_96000 0.000065f +#define LVEQNB_2PiOn_176400 0.000036f #define LVEQNB_2PiOn_192000 0.000033f #endif diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c index 563181cb6c..453c42dc54 100644 --- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c +++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Tables.c @@ -46,7 +46,9 @@ const LVM_UINT32 LVEQNB_SampleRateTab[] = {8000, /* 8kS/s 32000, 44100, 48000, + 88200, 96000, + 176400, 192000 }; #else @@ -82,7 +84,9 @@ const LVM_FLOAT LVEQNB_TwoPiOnFsTable[] = {LVEQNB_2PiOn_8000, /* 8kS/s LVEQNB_2PiOn_44100, LVEQNB_2PiOn_48000 #ifdef HIGHER_FS + ,LVEQNB_2PiOn_88200 ,LVEQNB_2PiOn_96000 + ,LVEQNB_2PiOn_176400 ,LVEQNB_2PiOn_192000 #endif }; @@ -249,30 +253,4 @@ const LVM_INT16 LVEQNB_DPCosCoef[] = {1, /* Shifts 16586, /* a2 */ -44}; /* a3 */ -/************************************************************************************/ -/* */ -/* Bypass mixer time constants (100ms) */ -/* */ -/************************************************************************************/ - -#define LVEQNB_MIX_TC_Fs8000 32580 /* Floating point value 0.994262695 */ -#define LVEQNB_MIX_TC_Fs11025 32632 /* Floating point value 0.995849609 */ -#define LVEQNB_MIX_TC_Fs12000 32643 /* Floating point value 0.996185303 */ -#define LVEQNB_MIX_TC_Fs16000 32674 /* Floating point value 0.997131348 */ -#define LVEQNB_MIX_TC_Fs22050 32700 /* Floating point value 0.997924805 */ -#define LVEQNB_MIX_TC_Fs24000 32705 /* Floating point value 0.998077393 */ -#define LVEQNB_MIX_TC_Fs32000 32721 /* Floating point value 0.998565674 */ -#define LVEQNB_MIX_TC_Fs44100 32734 /* Floating point value 0.998962402 */ -#define LVEQNB_MIX_TC_Fs48000 32737 /* Floating point value 0.999053955 */ - -const LVM_INT16 LVEQNB_MixerTCTable[] = { - LVEQNB_MIX_TC_Fs8000, - LVEQNB_MIX_TC_Fs11025, - LVEQNB_MIX_TC_Fs12000, - LVEQNB_MIX_TC_Fs16000, - LVEQNB_MIX_TC_Fs22050, - LVEQNB_MIX_TC_Fs24000, - LVEQNB_MIX_TC_Fs32000, - LVEQNB_MIX_TC_Fs44100, - LVEQNB_MIX_TC_Fs48000}; diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h index ff7475e05b..c915ac0225 100644 --- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h +++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Private.h @@ -123,7 +123,7 @@ extern "C" { #ifndef HIGHER_FS #define LVREV_NUM_FS 9 /* Number of supported sample rates */ #else -#define LVREV_NUM_FS 11 /* Number of supported sample rates */ +#define LVREV_NUM_FS 13 /* Number of supported sample rates */ #endif #define LVREV_MAXBLKSIZE_LIMIT 64 /* Maximum block size low limit */ diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c index 8c7807f6db..dfed28e891 100644 --- a/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c +++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_SetControlParameters.c @@ -68,7 +68,8 @@ LVREV_ReturnStatus_en LVREV_SetControlParameters(LVREV_Handle_t hInsta (pNewParams->SampleRate != LVM_FS_44100) && (pNewParams->SampleRate != LVM_FS_48000) #ifdef HIGHER_FS - && (pNewParams->SampleRate != LVM_FS_96000) && (pNewParams->SampleRate != LVM_FS_192000) + && (pNewParams->SampleRate != LVM_FS_88200) && (pNewParams->SampleRate != LVM_FS_96000) + && (pNewParams->SampleRate != LVM_FS_176400) && (pNewParams->SampleRate != LVM_FS_192000) #endif ) #ifdef SUPPORT_MC diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c index b3edc60920..1058740900 100644 --- a/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c +++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_Tables.c @@ -52,7 +52,9 @@ const LVM_UINT32 LVM_FsTable[] = { 32000, 44100, 48000, + 88200, 96000, + 176400, 192000 }; #endif diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h index a750bb02a8..ee07e2e8a5 100644 --- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h +++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Private.h @@ -46,7 +46,7 @@ extern "C" { #ifndef HIGHER_FS #define LVPSA_NR_SUPPORTED_RATE 9 /* From 8000Hz to 48000Hz*/ #else -#define LVPSA_NR_SUPPORTED_RATE 11 /* From 8000Hz to 192000Hz*/ +#define LVPSA_NR_SUPPORTED_RATE 13 /* From 8000Hz to 192000Hz*/ #endif #define LVPSA_NR_SUPPORTED_SPEED 3 /* LOW, MEDIUM, HIGH */ diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c index 1287503aa5..f8af496fc3 100644 --- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c +++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Tables.c @@ -54,7 +54,9 @@ const LVM_UINT32 LVPSA_SampleRateTab[] = { 8000, /* 8kS/ 32000, 44100, 48000, + 88200, 96000, + 176400, 192000}; /* 192kS/s */ #endif @@ -78,7 +80,9 @@ const LVM_UINT32 LVPSA_SampleRateInvTab[] = { 268435, / 48696, 44739 #ifdef HIGHER_FS + ,24348 ,22369 + ,12174 ,11185 /* 192kS/s */ #endif }; @@ -105,7 +109,9 @@ const LVM_UINT16 LVPSA_nSamplesBufferUpdate[] = { 160, / 882, 960 #ifdef HIGHER_FS + ,1764 ,1920 + ,3528 ,3840 /* 192kS/s */ #endif }; @@ -128,7 +134,9 @@ const LVM_UINT16 LVPSA_DownSamplingFactor[] = { 5, /* 30, /* 44100 S/s */ 32 /* 48000 S/s */ #ifdef HIGHER_FS + ,60 /* 88200 S/s */ ,64 /* 96000 S/s */ + ,120 /* 176400 S/s */ ,128 /*192000 S/s */ #endif }; @@ -153,7 +161,9 @@ const LVM_INT16 LVPSA_TwoPiOnFsTable[] = { 26354, /* 8kS/s */ 4781, 4392 #ifdef HIGHER_FS + ,2390 ,2196 + ,1195 ,1098 /* 192kS/s */ #endif }; @@ -169,7 +179,9 @@ const LVM_FLOAT LVPSA_Float_TwoPiOnFsTable[] = { 0.8042847f, /* 8kS/s 0.1459089f, 0.1340372f #ifdef HIGHER_FS + ,0.0729476f ,0.0670186f + ,0.0364738f ,0.0335093f /* 192kS/s */ #endif }; @@ -352,7 +364,9 @@ const QPD_FLOAT_Coefs LVPSA_QPD_Float_Coefs[] = { /* 48kS/s */ {-0.9932638457976282f,0.0066249934025109f}, #ifdef HIGHER_FS + {-0.9931269618682563f,0.0067592649720609f}, {-0.9932638457976282f,0.0066249934025109f}, + {-0.9931269618682563f,0.0067592649720609f}, {-0.9932638457976282f,0.0066249934025109f}, #endif /* 8kS/s */ /* LVPSA_SPEED_MEDIUM */ @@ -368,7 +382,9 @@ const QPD_FLOAT_Coefs LVPSA_QPD_Float_Coefs[] = { /* 48kS/s */ {-0.9540119562298059f,0.0445343819446862f}, #ifdef HIGHER_FS + {-0.9531011912040412f,0.0453995238058269f}, {-0.9540119562298059f,0.0445343819446862f}, + {-0.9531011912040412f,0.0453995238058269f}, {-0.9540119562298059f,0.0445343819446862f}, #endif /* 8kS/s */ /* LVPSA_SPEED_HIGH */ @@ -383,7 +399,9 @@ const QPD_FLOAT_Coefs LVPSA_QPD_Float_Coefs[] = { /* 48kS/s */ {-0.7274807319045067f,0.2356666540727019f} #ifdef HIGHER_FS + ,{-0.7229706319049001f,0.2388987224549055f} ,{-0.7274807319045067f,0.2356666540727019f} + ,{-0.7229706319049001f,0.2388987224549055f} ,{-0.7274807319045067f,0.2356666540727019f} #endif }; diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h index 0c2fe53035..277d95c6da 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h @@ -152,6 +152,24 @@ #define CS_SIDE_48000_SCALE 14 #ifdef HIGHER_FS +/* Coefficients for 88200Hz sample rate. + * The filter coefficients are obtained by carrying out + * state-space analysis using the coefficients available + * for 44100Hz. + */ +#define CS_MIDDLE_88200_A0 0.233846f +#define CS_MIDDLE_88200_A1 (-0.232657f) +#define CS_MIDDLE_88200_A2 0.000000f +#define CS_MIDDLE_88200_B1 (-0.992747f) +#define CS_MIDDLE_88200_B2 0.000000f +#define CS_MIDDLE_88200_SCALE 15 +#define CS_SIDE_88200_A0 0.231541f +#define CS_SIDE_88200_A1 (-0.289586f) +#define CS_SIDE_88200_A2 0.058045f +#define CS_SIDE_88200_B1 (-1.765300f) +#define CS_SIDE_88200_B2 0.769816f +#define CS_SIDE_88200_SCALE 14 + /* Stereo Enhancer coefficients for 96000Hz sample rate, scaled with 0.165*/ /* high pass filter with cutoff frequency 102.18 Hz*/ #define CS_MIDDLE_96000_A0 0.235532 @@ -168,6 +186,24 @@ #define CS_SIDE_96000_B2 0.797236 #define CS_SIDE_96000_SCALE 14 +/* Stereo Enhancer coefficients for 176400Hz sample rate. + * The filter coefficients are obtained by carrying out + * state-space analysis using the coefficients available + * for 44100Hz. + */ +#define CS_MIDDLE_176400_A0 0.233973f +#define CS_MIDDLE_176400_A1 (-0.233378f) +#define CS_MIDDLE_176400_A2 0.000000f +#define CS_MIDDLE_176400_B1 (-0.996367f) +#define CS_MIDDLE_176400_B2 0.000000f +#define CS_MIDDLE_176400_SCALE 15 +#define CS_SIDE_176400_A0 0.199836f +#define CS_SIDE_176400_A1 (-0.307544f) +#define CS_SIDE_176400_A2 0.107708f +#define CS_SIDE_176400_B1 (-1.876572f) +#define CS_SIDE_176400_B2 0.877771f +#define CS_SIDE_176400_SCALE 14 + /* Stereo Enhancer coefficients for 192000Hz sample rate, scaled with 0.1689*/ #define CS_MIDDLE_192000_A0 0.241219 #define CS_MIDDLE_192000_A1 (-0.240656) @@ -199,7 +235,13 @@ #define LVCS_STEREODELAY_CS_24KHZ 279 /* Sample rate 24kS/s */ #define LVCS_STEREODELAY_CS_32KHZ 372 /* Sample rate 32kS/s */ #define LVCS_STEREODELAY_CS_44KHZ 512 /* Sample rate 44kS/s */ +// TODO: this should linearly scale by frequency but is limited to 512 frames until +// we ensure enough buffer size has been allocated. #define LVCS_STEREODELAY_CS_48KHZ 512 /* Sample rate 48kS/s */ +#define LVCS_STEREODELAY_CS_88KHZ 512 /* Sample rate 88.2kS/s */ +#define LVCS_STEREODELAY_CS_96KHZ 512 /* Sample rate 96kS/s */ +#define LVCS_STEREODELAY_CS_176KHZ 512 /* Sample rate 176.4kS/s */ +#define LVCS_STEREODELAY_CS_192KHZ 512 /* Sample rate 196kS/s */ /* Reverb coefficients for 8000 Hz sample rate, scaled with 1.038030 */ #define CS_REVERB_8000_A0 0.667271 @@ -275,6 +317,14 @@ #define CS_REVERB_48000_SCALE 14 #ifdef HIGHER_FS +/* Reverb coefficients for 88200Hz sample rate, scaled with 0.8 */ +/* Band pass filter with fc1=500 and fc2=8000 */ +#define CS_REVERB_88200_A0 0.171901f +#define CS_REVERB_88200_A1 0.000000f +#define CS_REVERB_88200_A2 (-0.171901f) +#define CS_REVERB_88200_B1 (-1.553948f) +#define CS_REVERB_88200_B2 (0.570248f) +#define CS_REVERB_88200_SCALE 14 /* Reverb coefficients for 96000Hz sample rate, scaled with 0.8 */ /* Band pass filter with fc1=500 and fc2=8000*/ #define CS_REVERB_96000_A0 0.1602488 @@ -284,6 +334,14 @@ #define CS_REVERB_96000_B2 0.599377 #define CS_REVERB_96000_SCALE 14 +/* Reverb coefficients for 176400Hz sample rate, scaled with 0.8 */ +/* Band pass filter with fc1=500 and fc2=8000 */ +#define CS_REVERB_176400_A0 0.094763f +#define CS_REVERB_176400_A1 0.000000f +#define CS_REVERB_176400_A2 (-0.094763f) +#define CS_REVERB_176400_B1 (-1.758593f) +#define CS_REVERB_176400_B2 (0.763091f) +#define CS_REVERB_176400_SCALE 14 /* Reverb coefficients for 192000Hz sample rate, scaled with 0.8 */ /* Band pass filter with fc1=500 and fc2=8000*/ #define CS_REVERB_192000_A0 0.0878369 @@ -446,6 +504,24 @@ #ifdef HIGHER_FS +/* Equaliser coefficients for 88200Hz sample rate. + * The filter coefficients are obtained by carrying out + * state-space analysis using the coefficients available + * for 44100Hz. + */ +#define CS_EQUALISER_88200_A0 1.771899f +#define CS_EQUALISER_88200_A1 (-2.930762f) +#define CS_EQUALISER_88200_A2 1.172175f +#define CS_EQUALISER_88200_B1 (-1.438349f) +#define CS_EQUALISER_88200_B2 0.442520f +#define CS_EQUALISER_88200_SCALE 13 +#define CSEX_EQUALISER_88200_A0 2.675241f +#define CSEX_EQUALISER_88200_A1 (-4.466154f) +#define CSEX_EQUALISER_88200_A2 1.810305f +#define CSEX_EQUALISER_88200_B1 (-0.925350f) +#define CSEX_EQUALISER_88200_B2 (-0.066616f) +#define CSEX_EQUALISER_88200_SCALE 13 + #define CS_EQUALISER_96000_A0 1.784497 #define CS_EQUALISER_96000_A1 (-3.001435) #define CS_EQUALISER_96000_A2 1.228422 @@ -458,6 +534,23 @@ #define CSEX_EQUALISER_96000_B1 (-0.971718) #define CSEX_EQUALISER_96000_B2 (-0.021216) #define CSEX_EQUALISER_96000_SCALE 13 +/* Equaliser coefficients for 176400Hz sample rate. + * The filter coefficients are obtained by carrying out + * state-space analysis using the coefficients available + * for 44100Hz. + */ +#define CS_EQUALISER_176400_A0 1.883440f +#define CS_EQUALISER_176400_A1 (-3.414272f) +#define CS_EQUALISER_176400_A2 1.534702f +#define CS_EQUALISER_176400_B1 (-1.674614f) +#define CS_EQUALISER_176400_B2 0.675827f +#define CS_EQUALISER_176400_SCALE 13 +#define CSEX_EQUALISER_176400_A0 3.355068f +#define CSEX_EQUALISER_176400_A1 (-6.112578f) +#define CSEX_EQUALISER_176400_A2 2.764135f +#define CSEX_EQUALISER_176400_B1 (-1.268533f) +#define CSEX_EQUALISER_176400_B2 0.271277f +#define CSEX_EQUALISER_176400_SCALE 13 #define CS_EQUALISER_192000_A0 1.889582 #define CS_EQUALISER_192000_A1 (-3.456140) diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c index 07657647ce..a1fb48febc 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Tables.c @@ -74,10 +74,18 @@ const BiquadA01B1CoefsSP_t LVCS_SEMidCoefTable[] = { (LVM_UINT16 )CS_MIDDLE_48000_SCALE} #ifdef HIGHER_FS , + {CS_MIDDLE_88200_A0, /* 88kS/s coefficients */ + CS_MIDDLE_88200_A1, + CS_MIDDLE_88200_B1, + (LVM_UINT16)CS_MIDDLE_88200_SCALE}, {CS_MIDDLE_96000_A0, /* 96kS/s coefficients */ CS_MIDDLE_96000_A1, CS_MIDDLE_96000_B1, (LVM_UINT16 )CS_MIDDLE_96000_SCALE}, + {CS_MIDDLE_176400_A0, /* 176kS/s coefficients */ + CS_MIDDLE_176400_A1, + CS_MIDDLE_176400_B1, + (LVM_UINT16)CS_MIDDLE_176400_SCALE}, {CS_MIDDLE_192000_A0, /* 192kS/s coefficients */ CS_MIDDLE_192000_A1, CS_MIDDLE_192000_B1, @@ -144,12 +152,24 @@ const BiquadA012B12CoefsSP_t LVCS_SESideCoefTable[] = { (LVM_UINT16 )CS_SIDE_48000_SCALE} #ifdef HIGHER_FS , + {CS_SIDE_88200_A0, /* 88kS/s coefficients */ + CS_SIDE_88200_A1, + CS_SIDE_88200_A2, + CS_SIDE_88200_B1, + CS_SIDE_88200_B2, + (LVM_UINT16)CS_SIDE_88200_SCALE}, {CS_SIDE_96000_A0, /* 96kS/s coefficients */ CS_SIDE_96000_A1, CS_SIDE_96000_A2, CS_SIDE_96000_B1, CS_SIDE_96000_B2, (LVM_UINT16 )CS_SIDE_96000_SCALE}, + {CS_SIDE_176400_A0, /*176kS/s coefficients */ + CS_SIDE_176400_A1, + CS_SIDE_176400_A2, + CS_SIDE_176400_B1, + CS_SIDE_176400_B2, + (LVM_UINT16)CS_SIDE_176400_SCALE}, {CS_SIDE_192000_A0, /* 192kS/s coefficients */ CS_SIDE_192000_A1, CS_SIDE_192000_A2, @@ -223,12 +243,24 @@ const BiquadA012B12CoefsSP_t LVCS_EqualiserCoefTable[] = { CS_EQUALISER_48000_B2, (LVM_UINT16 )CS_EQUALISER_48000_SCALE}, #ifdef HIGHER_FS + {CS_EQUALISER_88200_A0, /* 88kS/s coeffieients */ + CS_EQUALISER_88200_A1, + CS_EQUALISER_88200_A2, + CS_EQUALISER_88200_B1, + CS_EQUALISER_88200_B2, + (LVM_UINT16)CS_EQUALISER_88200_SCALE}, {CS_EQUALISER_96000_A0, /* 96kS/s coefficients */ CS_EQUALISER_96000_A1, CS_EQUALISER_96000_A2, CS_EQUALISER_96000_B1, CS_EQUALISER_96000_B2, (LVM_UINT16 )CS_EQUALISER_96000_SCALE}, + {CS_EQUALISER_176400_A0, /* 176kS/s coefficients */ + CS_EQUALISER_176400_A1, + CS_EQUALISER_176400_A2, + CS_EQUALISER_176400_B1, + CS_EQUALISER_176400_B2, + (LVM_UINT16)CS_EQUALISER_176400_SCALE}, {CS_EQUALISER_192000_A0, /* 192kS/s coefficients */ CS_EQUALISER_192000_A1, CS_EQUALISER_192000_A2, @@ -294,12 +326,24 @@ const BiquadA012B12CoefsSP_t LVCS_EqualiserCoefTable[] = { (LVM_UINT16 )CSEX_EQUALISER_48000_SCALE} #ifdef HIGHER_FS , + {CSEX_EQUALISER_88200_A0, /* 88kS/s coefficients */ + CSEX_EQUALISER_88200_A1, + CSEX_EQUALISER_88200_A2, + CSEX_EQUALISER_88200_B1, + CSEX_EQUALISER_88200_B2, + (LVM_UINT16)CSEX_EQUALISER_88200_SCALE}, {CSEX_EQUALISER_96000_A0, /* 96kS/s coefficients */ CSEX_EQUALISER_96000_A1, CSEX_EQUALISER_96000_A2, CSEX_EQUALISER_96000_B1, CSEX_EQUALISER_96000_B2, (LVM_UINT16 )CSEX_EQUALISER_96000_SCALE}, + {CSEX_EQUALISER_176400_A0, /* 176kS/s coefficients */ + CSEX_EQUALISER_176400_A1, + CSEX_EQUALISER_176400_A2, + CSEX_EQUALISER_176400_B1, + CSEX_EQUALISER_176400_B2, + (LVM_UINT16)CSEX_EQUALISER_176400_SCALE}, {CSEX_EQUALISER_192000_A0, /* 192kS/s coefficients */ CSEX_EQUALISER_192000_A1, CSEX_EQUALISER_192000_A2, @@ -326,7 +370,12 @@ const LVM_UINT16 LVCS_StereoDelayCS[] = { LVCS_STEREODELAY_CS_24KHZ, LVCS_STEREODELAY_CS_32KHZ, LVCS_STEREODELAY_CS_44KHZ, - LVCS_STEREODELAY_CS_48KHZ}; + LVCS_STEREODELAY_CS_48KHZ, + LVCS_STEREODELAY_CS_88KHZ, + LVCS_STEREODELAY_CS_96KHZ, + LVCS_STEREODELAY_CS_176KHZ, + LVCS_STEREODELAY_CS_192KHZ, +}; /************************************************************************************/ /* */ @@ -392,12 +441,24 @@ const BiquadA012B12CoefsSP_t LVCS_ReverbCoefTable[] = { (LVM_UINT16 )CS_REVERB_48000_SCALE} #ifdef HIGHER_FS , + {CS_REVERB_88200_A0, /* 88kS/s coefficients */ + CS_REVERB_88200_A1, + CS_REVERB_88200_A2, + CS_REVERB_88200_B1, + CS_REVERB_88200_B2, + (LVM_UINT16)CS_REVERB_88200_SCALE}, {CS_REVERB_96000_A0, /* 96kS/s coefficients */ CS_REVERB_96000_A1, CS_REVERB_96000_A2, CS_REVERB_96000_B1, CS_REVERB_96000_B2, (LVM_UINT16 )CS_REVERB_96000_SCALE}, + {CS_REVERB_176400_A0, /* 176kS/s coefficients */ + CS_REVERB_176400_A1, + CS_REVERB_176400_A2, + CS_REVERB_176400_B1, + CS_REVERB_176400_B2, + (LVM_UINT16)CS_REVERB_176400_SCALE}, {CS_REVERB_192000_A0, /* 192kS/s coefficients */ CS_REVERB_192000_A1, CS_REVERB_192000_A2, @@ -509,12 +570,14 @@ const LVCS_VolCorrect_t LVCS_VolCorrectTable[] = { #define LVCS_VOL_TC_Fs44100 32734 /* Floating point value 0.998962402 */ #define LVCS_VOL_TC_Fs48000 32737 /* Floating point value 0.999053955 */ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) +#define LVCS_VOL_TC_Fs88200 32751 /* Floating point value 0.999481066 */ #define LVCS_VOL_TC_Fs96000 32751 /* Floating point value 0.999511703 */ /* Todo @ need to re check this value*/ +#define LVCS_VOL_TC_Fs176400 32759 /* Floating point value 0.999740499 */ #define LVCS_VOL_TC_Fs192000 32763 /* Floating point value 0.999877925 */ /* Todo @ need to re check this value*/ #endif #if defined(BUILD_FLOAT) && defined(HIGHER_FS) -const LVM_INT16 LVCS_VolumeTCTable[11] = {LVCS_VOL_TC_Fs8000, +const LVM_INT16 LVCS_VolumeTCTable[13] = {LVCS_VOL_TC_Fs8000, LVCS_VOL_TC_Fs11025, LVCS_VOL_TC_Fs12000, LVCS_VOL_TC_Fs16000, @@ -523,7 +586,9 @@ const LVM_INT16 LVCS_VolumeTCTable[11] = {LVCS_VOL_TC_Fs8000, LVCS_VOL_TC_Fs32000, LVCS_VOL_TC_Fs44100, LVCS_VOL_TC_Fs48000, + LVCS_VOL_TC_Fs88200, LVCS_VOL_TC_Fs96000, + LVCS_VOL_TC_Fs176400, LVCS_VOL_TC_Fs192000 }; #else @@ -545,7 +610,7 @@ const LVM_INT16 LVCS_VolumeTCTable[9] = {LVCS_VOL_TC_Fs8000, /* */ /************************************************************************************/ #if defined(BUILD_FLOAT) && defined(HIGHER_FS) -const LVM_INT32 LVCS_SampleRateTable[11] = {8000, +const LVM_INT32 LVCS_SampleRateTable[13] = {8000, 11025, 12000, 16000, @@ -554,7 +619,9 @@ const LVM_INT32 LVCS_SampleRateTable[11] = {8000, 32000, 44100, 48000, + 88200, 96000, + 176400, 192000 }; #else diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp index 01c5955234..99551ccfe9 100644 --- a/media/libeffects/lvm/tests/lvmtest.cpp +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -447,19 +447,69 @@ int lvmControl(struct EffectContext *pContext, lvmConfigParams_t *plvmConfigParams, LVM_ControlParams_t *params) { LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */ - LVM_EQNB_BandDef_t BandDefs[MAX_NUM_BANDS]; /* Equaliser band definitions */ - int eqPresetLevel = plvmConfigParams->eqPresetLevel; - int nrChannels = plvmConfigParams->nrChannels; - params->NrChannels = nrChannels; /* Set the initial process parameters */ /* General parameters */ params->OperatingMode = LVM_MODE_ON; - params->SampleRate = LVM_FS_44100; - params->SourceFormat = LVM_STEREO; params->SpeakerType = LVM_HEADPHONES; - pContext->pBundledContext->SampleRate = LVM_FS_44100; + const int nrChannels = plvmConfigParams->nrChannels; + params->NrChannels = nrChannels; + if (nrChannels == 1) { + params->SourceFormat = LVM_MONO; + } else if (nrChannels == 2) { + params->SourceFormat = LVM_STEREO; + } else if (nrChannels > 2 && nrChannels <= 8) { // FCC_2 FCC_8 + params->SourceFormat = LVM_MULTICHANNEL; + } else { + return -EINVAL; + } + + LVM_Fs_en sampleRate; + switch (plvmConfigParams->samplingFreq) { + case 8000: + sampleRate = LVM_FS_8000; + break; + case 11025: + sampleRate = LVM_FS_11025; + break; + case 12000: + sampleRate = LVM_FS_12000; + break; + case 16000: + sampleRate = LVM_FS_16000; + break; + case 22050: + sampleRate = LVM_FS_22050; + break; + case 24000: + sampleRate = LVM_FS_24000; + break; + case 32000: + sampleRate = LVM_FS_32000; + break; + case 44100: + sampleRate = LVM_FS_44100; + break; + case 48000: + sampleRate = LVM_FS_48000; + break; + case 88200: + sampleRate = LVM_FS_88200; + break; + case 96000: + sampleRate = LVM_FS_96000; + break; + case 176400: + sampleRate = LVM_FS_176400; + break; + case 192000: + sampleRate = LVM_FS_192000; + break; + default: + return -EINVAL; + } + params->SampleRate = sampleRate; /* Concert Sound parameters */ params->VirtualizerOperatingMode = plvmConfigParams->csEnable; @@ -468,14 +518,17 @@ int lvmControl(struct EffectContext *pContext, params->CS_EffectLevel = LVM_CS_EFFECT_NONE; /* N-Band Equaliser parameters */ - params->EQNB_OperatingMode = plvmConfigParams->eqEnable; - params->pEQNB_BandDefinition = &BandDefs[0]; + const int eqPresetLevel = plvmConfigParams->eqPresetLevel; + LVM_EQNB_BandDef_t BandDefs[MAX_NUM_BANDS]; /* Equaliser band definitions */ for (int i = 0; i < FIVEBAND_NUMBANDS; i++) { BandDefs[i].Frequency = EQNB_5BandPresetsFrequencies[i]; BandDefs[i].QFactor = EQNB_5BandPresetsQFactors[i]; BandDefs[i].Gain = EQNB_5BandSoftPresets[(FIVEBAND_NUMBANDS * eqPresetLevel) + i]; } + params->EQNB_OperatingMode = plvmConfigParams->eqEnable; + // Caution: raw pointer to stack data, stored in instance by LVM_SetControlParameters. + params->pEQNB_BandDefinition = &BandDefs[0]; /* Volume Control parameters */ params->VC_EffectLevel = 0; @@ -490,16 +543,6 @@ int lvmControl(struct EffectContext *pContext, /* Bass Enhancement parameters */ params->BE_OperatingMode = plvmConfigParams->bassEnable; - if (nrChannels == 1) { - params->SourceFormat = LVM_MONO; - } - if (nrChannels == 2) { - params->SourceFormat = LVM_STEREO; - } - if ((nrChannels > 2) && (nrChannels <= 8)) { - params->SourceFormat = LVM_MULTICHANNEL; - } - /* Activate the initial settings */ LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, params); @@ -613,7 +656,9 @@ int main(int argc, const char *argv[]) { samplingFreq != 12000 && samplingFreq != 16000 && samplingFreq != 22050 && samplingFreq != 24000 && samplingFreq != 32000 && samplingFreq != 44100 && - samplingFreq != 48000 && samplingFreq != 96000) { + samplingFreq != 48000 && samplingFreq != 88200 && + samplingFreq != 96000 && samplingFreq != 176400 && + samplingFreq != 192000) { ALOGE("\nError: Unsupported Sampling Frequency : %d\n", samplingFreq); return -1; } diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 09e99645a9..b5860de718 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -1275,10 +1275,18 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ pContext->pBundledContext->SamplesPerSecond = 48000 * NrChannels; break; #if defined(BUILD_FLOAT) && defined(HIGHER_FS) + case 88200: + SampleRate = LVM_FS_88200; + pContext->pBundledContext->SamplesPerSecond = 88200 * NrChannels; + break; case 96000: SampleRate = LVM_FS_96000; pContext->pBundledContext->SamplesPerSecond = 96000 * NrChannels; break; + case 176400: + SampleRate = LVM_FS_176400; + pContext->pBundledContext->SamplesPerSecond = 176400 * NrChannels; + break; case 192000: SampleRate = LVM_FS_192000; pContext->pBundledContext->SamplesPerSecond = 192000 * NrChannels; diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index d55816913c..602f607c81 100644 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -675,9 +675,15 @@ int Reverb_setConfig(ReverbContext *pContext, effect_config_t *pConfig){ SampleRate = LVM_FS_48000; break; #if defined(BUILD_FLOAT) && defined(HIGHER_FS) + case 88200: + SampleRate = LVM_FS_88200; + break; case 96000: SampleRate = LVM_FS_96000; break; + case 176400: + SampleRate = LVM_FS_176400; + break; case 192000: SampleRate = LVM_FS_192000; break; -- GitLab From 04395fafea962f68193a3394ae7d460f6c95ade5 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 14 Dec 2018 15:07:26 -0800 Subject: [PATCH 0623/1530] Audio HAL: More generalization for types imports Use namespace import in ConversionHelperHidl.cpp. Test: try moving types between audio/types.hal and common/types.hal. Change-Id: I7ffab98d0e3b8bbc82ab94b8d99b7d05e224e1e4 --- media/libaudiohal/impl/ConversionHelperHidl.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp index 97478592f8..9f8a5203c3 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.cpp +++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp @@ -22,19 +22,12 @@ #include "ConversionHelperHidl.h" -using ::android::hardware::audio::CPP_VERSION::Result; - -#if MAJOR_VERSION >= 4 -using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneChannelMapping; -using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneDirectionality; -using ::android::hardware::audio::CPP_VERSION::AudioMicrophoneLocation; -using ::android::hardware::audio::CPP_VERSION::DeviceAddress; -using ::android::hardware::audio::CPP_VERSION::MicrophoneInfo; -#endif - namespace android { namespace CPP_VERSION { +using namespace ::android::hardware::audio::common::CPP_VERSION; +using namespace ::android::hardware::audio::CPP_VERSION; + // static status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { AudioParameter halKeys(keys); -- GitLab From b82512bc30ed5fcaf216295b32ecfcf3dad214bd Mon Sep 17 00:00:00 2001 From: Jin Seok Park Date: Fri, 7 Dec 2018 15:23:03 +0900 Subject: [PATCH 0624/1530] AML: Remove use of Bundle#setDefusable Bundle#setDefusable no longer needs to be called since we are removing the instance of unbundling MediaMetadata in MediaSessionRecord. Bug: 119789387 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: If3cc95c06feaf61c187b4254a2cd7a601319210f --- .../apex/java/android/media/MediaMetadata.java | 4 +--- .../apex/java/android/media/session/ISession.aidl | 2 +- .../java/android/media/session/MediaSession.java | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java index 33e6916173..adfd20b95f 100644 --- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java +++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java @@ -422,9 +422,7 @@ public final class MediaMetadata implements Parcelable { } private MediaMetadata(Parcel in) { - //TODO(b/119789387): Resolve hidden API usage: Bundle#setDefusable - //mBundle = Bundle.setDefusable(in.readBundle(), true); - mBundle = new Bundle(); //TODO:remove this. + mBundle = in.readBundle(); } /** diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl index cbd93cb817..6363ed0f62 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl @@ -39,7 +39,7 @@ interface ISession { void destroy(); // These commands are for the TransportPerformer - void setMetadata(in MediaMetadata metadata); + void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); void setPlaybackState(in PlaybackState state); //TODO(b/119750807): Resolve hidden API usage ParceledListSlice. //void setQueue(in ParceledListSlice queue); diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index 1ae1d2c782..4ebfb8e052 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -30,6 +30,7 @@ import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; +import android.media.session.MediaSessionManager.RemoteUserInfo; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -40,7 +41,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; -import android.media.session.MediaSessionManager.RemoteUserInfo; import android.service.media.MediaBrowserService; import android.text.TextUtils; import android.util.Log; @@ -439,11 +439,21 @@ public final class MediaSession { * @see android.media.MediaMetadata.Builder#putBitmap */ public void setMetadata(@Nullable MediaMetadata metadata) { + long duration = -1; + int fields = 0; + MediaDescription description = null; if (metadata != null) { metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build(); + if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { + duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION); + } + fields = metadata.size(); + description = metadata.getDescription(); } + String metadataDescription = "size=" + fields + ", description=" + description; + try { - mBinder.setMetadata(metadata); + mBinder.setMetadata(metadata, duration, metadataDescription); } catch (RemoteException e) { Log.wtf(TAG, "Dead object in setPlaybackState.", e); } -- GitLab From 384a196f7cfbe4e1a481be0b281e1b7ae1bef42a Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 17 Dec 2018 14:14:27 -0800 Subject: [PATCH 0625/1530] aaudio: remove redundant message from test-return_stop.cpp It was unnecessary and was missing a newline. Test: this is a test Change-Id: I6463579c759b620ac3d34dceacee8e4e9c806808 --- media/libaaudio/tests/test_return_stop.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/media/libaaudio/tests/test_return_stop.cpp b/media/libaaudio/tests/test_return_stop.cpp index f34c3c8240..9a9e00cd18 100644 --- a/media/libaaudio/tests/test_return_stop.cpp +++ b/media/libaaudio/tests/test_return_stop.cpp @@ -228,8 +228,6 @@ int main(int argc, char **argv) { result = AAudioStream_requestStart(engine.stream); printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result); if (result != AAUDIO_OK) { - printf("ERROR - AAudioStream_requestStart returned %s", - AAudio_convertResultToText(result)); errorCount++; break; } -- GitLab From 00c5c05d2041978b67394a27bbd64f2ea623c1df Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 17 Dec 2018 14:15:58 -0800 Subject: [PATCH 0626/1530] Use hidl memory from allocator in CAS Mpeg2ts extractor has a depedency on libbinder for IMemory, but it doesn't actually use IMemory other than using it to construct the hidl memory. Make mediaextractor use hidl memory allocated by hidl allocator, instead of constructing hidl memory from IMemory. bug: 112766913 bug: 111407253 test: CTS MediaDrmClearkeyTest#testClearKeyPlaybackMpeg2ts Change-Id: I39e131718ea41f9cb23177c28213ddf7b7741338 --- media/extractors/mpeg2/Android.bp | 4 +- media/libstagefright/httplive/Android.bp | 4 +- media/libstagefright/mpeg2ts/ATSParser.cpp | 84 +++++++++++++--------- media/libstagefright/mpeg2ts/Android.bp | 3 +- 4 files changed, 57 insertions(+), 38 deletions(-) diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index 38c86eb4a3..e5cdfe486e 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -15,10 +15,10 @@ cc_library_shared { "android.hardware.cas@1.0", "android.hardware.cas.native@1.0", "android.hidl.token@1.0-utils", - "libbinder", + "android.hidl.allocator@1.0", + "libhidlmemory", "libcrypto", "libcutils", - "libhidlallocatorutils", "libhidlbase", "liblog", "libmediaextractor", diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp index 78d410ad8c..c4a072b989 100644 --- a/media/libstagefright/httplive/Android.bp +++ b/media/libstagefright/httplive/Android.bp @@ -29,7 +29,6 @@ cc_library { shared_libs: [ "liblog", - "libbinder", "libcrypto", "libcutils", "libmedia", @@ -38,10 +37,11 @@ cc_library { "libstagefright", "libstagefright_foundation", "libutils", - "libhidlallocatorutils", "libhidlbase", + "libhidlmemory", "android.hardware.cas@1.0", "android.hardware.cas.native@1.0", + "android.hidl.allocator@1.0", ], header_libs: [ diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 590131e308..e9baa1a35c 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -23,10 +23,10 @@ #include "ESQueue.h" #include -#include -#include +#include +#include #include -#include +#include #include #include #include @@ -46,12 +46,13 @@ #include namespace android { -using hardware::fromHeap; using hardware::hidl_string; using hardware::hidl_vec; -using hardware::HidlMemory; +using hardware::hidl_memory; using namespace hardware::cas::V1_0; using namespace hardware::cas::native::V1_0; +typedef hidl::allocator::V1_0::IAllocator TAllocator; +typedef hidl::memory::V1_0::IMemory TMemory; // I want the expression "y" evaluated even if verbose logging is off. #define MY_LOGV(x, y) \ @@ -208,9 +209,8 @@ private: bool mScrambled; bool mSampleEncrypted; sp mSampleAesKeyItem; - sp mMem; - sp mDealer; - sp mHidlMemory; + sp mHidlMemory; + sp mHidlAllocator; hardware::cas::native::V1_0::SharedBuffer mDescramblerSrcBuffer; sp mDescrambledBuffer; List mSubSamples; @@ -975,16 +975,43 @@ bool ATSParser::Stream::ensureBufferCapacity(size_t neededSize) { mBuffer == NULL ? 0 : mBuffer->capacity(), neededSize, mScrambled); sp newBuffer, newScrambledBuffer; - sp newMem; - sp newDealer; + sp newMem; if (mScrambled) { - size_t alignment = MemoryDealer::getAllocationAlignment(); - neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1); - // Align to multiples of 64K. - neededSize = (neededSize + 65535) & ~65535; - newDealer = new MemoryDealer(neededSize, "ATSParser"); - newMem = newDealer->allocate(neededSize); - newScrambledBuffer = new ABuffer(newMem->pointer(), newMem->size()); + if (mHidlAllocator == nullptr) { + mHidlAllocator = TAllocator::getService("ashmem"); + if (mHidlAllocator == nullptr) { + ALOGE("[stream %d] can't get hidl allocator", mElementaryPID); + return false; + } + } + + hidl_memory hidlMemToken; + bool success; + auto transStatus = mHidlAllocator->allocate( + neededSize, + [&success, &hidlMemToken]( + bool s, + hidl_memory const& m) { + success = s; + hidlMemToken = m; + }); + + if (!transStatus.isOk()) { + ALOGE("[stream %d] hidl allocator failed at the transport: %s", + mElementaryPID, transStatus.description().c_str()); + return false; + } + if (!success) { + ALOGE("[stream %d] hidl allocator failed", mElementaryPID); + return false; + } + newMem = mapMemory(hidlMemToken); + if (newMem == nullptr || newMem->getPointer() == nullptr) { + ALOGE("[stream %d] hidl failed to map memory", mElementaryPID); + return false; + } + + newScrambledBuffer = new ABuffer(newMem->getPointer(), newMem->getSize()); if (mDescrambledBuffer != NULL) { memcpy(newScrambledBuffer->data(), @@ -993,24 +1020,15 @@ bool ATSParser::Stream::ensureBufferCapacity(size_t neededSize) { } else { newScrambledBuffer->setRange(0, 0); } - mMem = newMem; - mDealer = newDealer; + mHidlMemory = newMem; mDescrambledBuffer = newScrambledBuffer; - ssize_t offset; - size_t size; - sp heap = newMem->getMemory(&offset, &size); - if (heap == NULL) { - return false; - } - - mHidlMemory = fromHeap(heap); - mDescramblerSrcBuffer.heapBase = *mHidlMemory; - mDescramblerSrcBuffer.offset = (uint64_t) offset; - mDescramblerSrcBuffer.size = (uint64_t) size; + mDescramblerSrcBuffer.heapBase = hidlMemToken; + mDescramblerSrcBuffer.offset = 0ULL; + mDescramblerSrcBuffer.size = (uint64_t)neededSize; - ALOGD("[stream %d] created shared buffer for descrambling, offset %zd, size %zu", - mElementaryPID, offset, size); + ALOGD("[stream %d] created shared buffer for descrambling, size %zu", + mElementaryPID, neededSize); } else { // Align to multiples of 64K. neededSize = (neededSize + 65535) & ~65535; @@ -1498,7 +1516,7 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { return UNKNOWN_ERROR; } - if (mDescrambledBuffer == NULL || mMem == NULL) { + if (mDescrambledBuffer == NULL || mHidlMemory == NULL) { ALOGE("received scrambled packets without shared memory!"); return UNKNOWN_ERROR; diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp index e516cf1e0f..a507b91a24 100644 --- a/media/libstagefright/mpeg2ts/Android.bp +++ b/media/libstagefright/mpeg2ts/Android.bp @@ -30,9 +30,10 @@ cc_library_static { shared_libs: [ "libcrypto", "libmedia", - "libhidlallocatorutils", + "libhidlmemory", "android.hardware.cas.native@1.0", "android.hidl.memory@1.0", + "android.hidl.allocator@1.0", ], header_libs: [ -- GitLab From 8725ae03a5f9c097837054abc7bd1d2df2c504e7 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 31 Aug 2018 10:33:56 -0700 Subject: [PATCH 0627/1530] C2SoftAVCEnc: Enable CABAC for main profile encoding Bug: 113677531 Test: Modified screenrecord application to force main profile and verified the generated bitstream manually Change-Id: Icab0c7fa1c60bb7ae8fbdef45a170cf2d3c6d541 --- media/codec2/components/avc/C2SoftAvcEnc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index ee5cf27db5..cf06623795 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -768,7 +768,11 @@ c2_status_t C2SoftAvcEnc::setProfileParams() { s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS; s_profile_params_ip.e_profile = mIntf->getProfile_l(); - s_profile_params_ip.u4_entropy_coding_mode = mEntropyMode; + if (s_profile_params_ip.e_profile == IV_PROFILE_BASE) { + s_profile_params_ip.u4_entropy_coding_mode = 0; + } else { + s_profile_params_ip.u4_entropy_coding_mode = 1; + } s_profile_params_ip.u4_timestamp_high = -1; s_profile_params_ip.u4_timestamp_low = -1; -- GitLab From 848b3c720e5c2d490cc7607221396ea0f9618ca0 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 27 Nov 2018 14:24:33 -0800 Subject: [PATCH 0628/1530] C2SoftVorbisDec: Ignore error from vorbis_dsp_synthesis Error from vorbis_dsp_synthesis() is to be ignored. After seek in some cases, vorbis_dsp_synthesis() returns OV_ENOTAUDIO when input is not an audio packet. This results in seek failing for vorbis files. OMX plugin for Vorbis decoder ignores the errors returned from this function. C2 plugin is updated to do the same. Bug: 110759274 Test: cts-tradefed run commandAndExit cts-dev -m CtsMediaTestCases \ -t android.media.cts.DecoderTest Change-Id: Ic98d0555121c7f741c468c69f1acdb75036d8c3d --- media/codec2/components/vorbis/C2SoftVorbisDec.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp index 280ae36165..48825e4421 100644 --- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp +++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp @@ -405,10 +405,7 @@ void C2SoftVorbisDec::process( int numFrames = 0; int ret = vorbis_dsp_synthesis(mState, &pack, 1); if (0 != ret) { - ALOGE("vorbis_dsp_synthesis returned %d", ret); - mSignalledError = true; - work->result = C2_CORRUPTED; - return; + ALOGD("vorbis_dsp_synthesis returned %d; ignored", ret); } else { numFrames = vorbis_dsp_pcmout( mState, reinterpret_cast (wView.data()), -- GitLab From ed4e0cddefd31574b18de72a9063a86420141964 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 11 Oct 2018 18:11:49 -0700 Subject: [PATCH 0629/1530] C2SoftVpxDec: Add support for Profile 2 Add support to decode Profile 2 streams (YUV420 10bit) Test: stagefright -s -S video_1280x720_vp9_hdr_static_3mbps.mkv Bug: 117511385 Change-Id: Ica0dee0ee8b3e216824a86df41a961a1f0f38e23 --- media/codec2/components/vpx/C2SoftVpxDec.cpp | 189 +++++++++++++++++-- 1 file changed, 174 insertions(+), 15 deletions(-) diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp index 8ecbf5d8e3..9ba23624e1 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.cpp +++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp @@ -188,6 +188,24 @@ public: .withConstValue(defaultColorInfo) .build()); + addParameter( + DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS) + .withDefault(new C2StreamColorAspectsTuning::output( + 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED, + C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED)) + .withFields({ + C2F(mDefaultColorAspects, range).inRange( + C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER), + C2F(mDefaultColorAspects, primaries).inRange( + C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER), + C2F(mDefaultColorAspects, transfer).inRange( + C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER), + C2F(mDefaultColorAspects, matrix).inRange( + C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER) + }) + .withSetter(DefaultColorAspectsSetter) + .build()); + // TODO: support more formats? addParameter( DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT) @@ -228,6 +246,22 @@ public: return C2R::Ok(); } + static C2R DefaultColorAspectsSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + if (me.v.range > C2Color::RANGE_OTHER) { + me.set().range = C2Color::RANGE_OTHER; + } + if (me.v.primaries > C2Color::PRIMARIES_OTHER) { + me.set().primaries = C2Color::PRIMARIES_OTHER; + } + if (me.v.transfer > C2Color::TRANSFER_OTHER) { + me.set().transfer = C2Color::TRANSFER_OTHER; + } + if (me.v.matrix > C2Color::MATRIX_OTHER) { + me.set().matrix = C2Color::MATRIX_OTHER; + } + return C2R::Ok(); + } static C2R ProfileLevelSetter(bool mayBlock, C2P &me, const C2P &size) { @@ -236,6 +270,9 @@ public: (void)me; // TODO: validate return C2R::Ok(); } + std::shared_ptr getDefaultColorAspects_l() { + return mDefaultColorAspects; + } static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P &me) { (void)mayBlock; @@ -256,6 +293,7 @@ private: std::shared_ptr mMaxInputSize; std::shared_ptr mColorInfo; std::shared_ptr mPixelFormat; + std::shared_ptr mDefaultColorAspects; #ifdef VP9 #if 0 std::shared_ptr mHdrStaticInfo; @@ -524,32 +562,129 @@ void C2SoftVpxDec::process( static void copyOutputBufferToYV12Frame(uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride, size_t srcUStride, size_t srcVStride, - uint32_t width, uint32_t height, int32_t bpp) { - size_t dstYStride = align(width, 16) * bpp ; + uint32_t width, uint32_t height) { + size_t dstYStride = align(width, 16); size_t dstUVStride = align(dstYStride / 2, 16); uint8_t *dstStart = dst; for (size_t i = 0; i < height; ++i) { - memcpy(dst, srcY, width * bpp); + memcpy(dst, srcY, width); srcY += srcYStride; dst += dstYStride; } dst = dstStart + dstYStride * height; for (size_t i = 0; i < height / 2; ++i) { - memcpy(dst, srcV, width / 2 * bpp); + memcpy(dst, srcV, width / 2); srcV += srcVStride; dst += dstUVStride; } dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2); for (size_t i = 0; i < height / 2; ++i) { - memcpy(dst, srcU, width / 2 * bpp); + memcpy(dst, srcU, width / 2); srcU += srcUStride; dst += dstUVStride; } } +static void convertYUV420Planar16ToY410(uint32_t *dst, + const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstStride, size_t width, size_t height) { + + // Converting two lines at a time, slightly faster + for (size_t y = 0; y < height; y += 2) { + uint32_t *dstTop = (uint32_t *) dst; + uint32_t *dstBot = (uint32_t *) (dst + dstStride); + uint16_t *ySrcTop = (uint16_t*) srcY; + uint16_t *ySrcBot = (uint16_t*) (srcY + srcYStride); + uint16_t *uSrc = (uint16_t*) srcU; + uint16_t *vSrc = (uint16_t*) srcV; + + uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1; + size_t x = 0; + for (; x < width - 3; x += 4) { + + u01 = *((uint32_t*)uSrc); uSrc += 2; + v01 = *((uint32_t*)vSrc); vSrc += 2; + + y01 = *((uint32_t*)ySrcTop); ySrcTop += 2; + y23 = *((uint32_t*)ySrcTop); ySrcTop += 2; + y45 = *((uint32_t*)ySrcBot); ySrcBot += 2; + y67 = *((uint32_t*)ySrcBot); ySrcBot += 2; + + uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20); + uv1 = (u01 >> 16) | ((v01 >> 16) << 20); + + *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0; + *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0; + *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1; + *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1; + + *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0; + *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0; + *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1; + *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1; + } + + // There should be at most 2 more pixels to process. Note that we don't + // need to consider odd case as the buffer is always aligned to even. + if (x < width) { + u01 = *uSrc; + v01 = *vSrc; + y01 = *((uint32_t*)ySrcTop); + y45 = *((uint32_t*)ySrcBot); + uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20); + *dstTop++ = ((y01 & 0x3FF) << 10) | uv0; + *dstTop++ = ((y01 >> 16) << 10) | uv0; + *dstBot++ = ((y45 & 0x3FF) << 10) | uv0; + *dstBot++ = ((y45 >> 16) << 10) | uv0; + } + + srcY += srcYStride * 2; + srcU += srcUStride; + srcV += srcVStride; + dst += dstStride * 2; + } + + return; +} + +static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, + const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, + size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstStride, size_t width, size_t height) { + + uint8_t *dstY = (uint8_t *)dst; + size_t dstYSize = dstStride * height; + size_t dstUVStride = align(dstStride / 2, 16); + size_t dstUVSize = dstUVStride * height / 2; + uint8_t *dstV = dstY + dstYSize; + uint8_t *dstU = dstV + dstUVSize; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + dstY[x] = (uint8_t)(srcY[x] >> 2); + } + + srcY += srcYStride; + dstY += dstStride; + } + + for (size_t y = 0; y < (height + 1) / 2; ++y) { + for (size_t x = 0; x < (width + 1) / 2; ++x) { + dstU[x] = (uint8_t)(srcU[x] >> 2); + dstV[x] = (uint8_t)(srcV[x] >> 2); + } + + srcU += srcUStride; + srcV += srcVStride; + dstU += dstUVStride; + dstV += dstUVStride; + } + return; +} bool C2SoftVpxDec::outputBuffer( const std::shared_ptr &pool, const std::unique_ptr &work) @@ -581,15 +716,21 @@ bool C2SoftVpxDec::outputBuffer( } CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016); - int32_t bpp = 1; - if (img->fmt == VPX_IMG_FMT_I42016) { - bpp = 2; - } std::shared_ptr block; uint32_t format = HAL_PIXEL_FORMAT_YV12; + if (img->fmt == VPX_IMG_FMT_I42016) { + IntfImpl::Lock lock = mIntf->lock(); + std::shared_ptr defaultColorAspects = mIntf->getDefaultColorAspects_l(); + + if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 && + defaultColorAspects->matrix == C2Color::MATRIX_BT2020 && + defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) { + format = HAL_PIXEL_FORMAT_RGBA_1010102; + } + } C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16) * bpp, mHeight, format, usage, &block); + c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &block); if (err != C2_OK) { ALOGE("fetchGraphicBlock for Output failed with status %d", err); work->result = err; @@ -610,12 +751,30 @@ bool C2SoftVpxDec::outputBuffer( size_t srcYStride = img->stride[VPX_PLANE_Y]; size_t srcUStride = img->stride[VPX_PLANE_U]; size_t srcVStride = img->stride[VPX_PLANE_V]; - const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y]; - const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U]; - const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V]; - copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, - srcYStride, srcUStride, srcVStride, mWidth, mHeight, bpp); + if (img->fmt == VPX_IMG_FMT_I42016) { + const uint16_t *srcY = (const uint16_t *)img->planes[VPX_PLANE_Y]; + const uint16_t *srcU = (const uint16_t *)img->planes[VPX_PLANE_U]; + const uint16_t *srcV = (const uint16_t *)img->planes[VPX_PLANE_V]; + + if (format == HAL_PIXEL_FORMAT_RGBA_1010102) { + convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, + srcUStride / 2, srcVStride / 2, + align(mWidth, 16), + mWidth, mHeight); + } else { + convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2, + srcUStride / 2, srcVStride / 2, + align(mWidth, 16), + mWidth, mHeight); + } + } else { + const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y]; + const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U]; + const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V]; + copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, + srcYStride, srcUStride, srcVStride, mWidth, mHeight); + } finishWork(*(int64_t *)img->user_priv, work, std::move(block)); return true; } -- GitLab From 35d36cba0feb9cd63f3e8e27cd6e7efdb1236909 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 12 Oct 2018 15:10:56 -0700 Subject: [PATCH 0630/1530] C2AllocatorGralloc: support locking RGBA_1010102 as YUVA_1010102 Bug: 117672953 Test: stagefright -s -S video_1280x720_vp9_hdr_static_3mbps.mkv Change-Id: I738820251ea9ddd6b9fe9d2bafcdc6d1c6fb3744 --- media/codec2/vndk/C2AllocatorGralloc.cpp | 202 ++++++++++++++++------- 1 file changed, 139 insertions(+), 63 deletions(-) diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index 22e8d8476c..4878974e6c 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -366,88 +366,88 @@ c2_status_t C2AllocationGralloc::map( generation, igbp_id, igbp_slot); } - // UGLY HACK: assume YCbCr 4:2:0 8-bit format (and lockable via lockYCbCr) if we don't - // recognize the format - PixelFormat format = mInfo.mapperInfo.format; - if (format != PixelFormat::RGBA_8888 && format != PixelFormat::RGBX_8888) { - format = PixelFormat::YCBCR_420_888; - } - - switch (format) { - case PixelFormat::YCBCR_420_888: - case PixelFormat::YV12: { - YCbCrLayout ycbcrLayout; - mMapper->lockYCbCr( - const_cast(mBuffer), grallocUsage, + switch (mInfo.mapperInfo.format) { + case PixelFormat::RGBA_1010102: { + // TRICKY: this is used for media as YUV444 in the case when it is queued directly to a + // Surface. In all other cases it is RGBA. We don't know which case it is here, so + // default to YUV for now. + void *pointer = nullptr; + mMapper->lock( + const_cast(mBuffer), + grallocUsage, { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, // TODO: fence hidl_handle(), - [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { + [&err, &pointer](const auto &maperr, const auto &mapPointer) { err = maperr2error(maperr); if (err == C2_OK) { - ycbcrLayout = mapLayout; + pointer = mapPointer; } }); if (err != C2_OK) { - ALOGD("lockYCbCr failed: %d", err); + ALOGD("lock failed: %d", err); return err; } - addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y; - addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb; - addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr; - layout->type = C2PlanarLayout::TYPE_YUV; - layout->numPlanes = 3; - layout->rootPlanes = 3; + // treat as 32-bit values + addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)pointer; + addr[C2PlanarLayout::PLANE_U] = (uint8_t *)pointer; + addr[C2PlanarLayout::PLANE_V] = (uint8_t *)pointer; + addr[C2PlanarLayout::PLANE_A] = (uint8_t *)pointer; + layout->type = C2PlanarLayout::TYPE_YUVA; + layout->numPlanes = 4; + layout->rootPlanes = 1; layout->planes[C2PlanarLayout::PLANE_Y] = { C2PlaneInfo::CHANNEL_Y, // channel - 1, // colInc - (int32_t)ycbcrLayout.yStride, // rowInc + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc 1, // mColSampling 1, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness + 32, // allocatedDepth + 10, // bitDepth + 10, // rightShift + C2PlaneInfo::LITTLE_END, // endianness C2PlanarLayout::PLANE_Y, // rootIx 0, // offset }; layout->planes[C2PlanarLayout::PLANE_U] = { - C2PlaneInfo::CHANNEL_CB, // channel - (int32_t)ycbcrLayout.chromaStep, // colInc - (int32_t)ycbcrLayout.cStride, // rowInc - 2, // mColSampling - 2, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness - C2PlanarLayout::PLANE_U, // rootIx - 0, // offset + C2PlaneInfo::CHANNEL_CB, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 32, // allocatedDepth + 10, // bitDepth + 0, // rightShift + C2PlaneInfo::LITTLE_END, // endianness + C2PlanarLayout::PLANE_Y, // rootIx + 0, // offset }; layout->planes[C2PlanarLayout::PLANE_V] = { - C2PlaneInfo::CHANNEL_CR, // channel - (int32_t)ycbcrLayout.chromaStep, // colInc - (int32_t)ycbcrLayout.cStride, // rowInc - 2, // mColSampling - 2, // mRowSampling - 8, // allocatedDepth - 8, // bitDepth - 0, // rightShift - C2PlaneInfo::NATIVE, // endianness - C2PlanarLayout::PLANE_V, // rootIx - 0, // offset + C2PlaneInfo::CHANNEL_CR, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 32, // allocatedDepth + 10, // bitDepth + 20, // rightShift + C2PlaneInfo::LITTLE_END, // endianness + C2PlanarLayout::PLANE_Y, // rootIx + 0, // offset + }; + layout->planes[C2PlanarLayout::PLANE_A] = { + C2PlaneInfo::CHANNEL_A, // channel + 4, // colInc + 4 * (int32_t)mInfo.stride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 32, // allocatedDepth + 2, // bitDepth + 30, // rightShift + C2PlaneInfo::LITTLE_END, // endianness + C2PlanarLayout::PLANE_Y, // rootIx + 0, // offset }; - // handle interleaved formats - intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U]; - if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) { - layout->rootPlanes = 2; - layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U; - layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset; - } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) { - layout->rootPlanes = 2; - layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V; - layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset; - } break; } @@ -519,9 +519,85 @@ c2_status_t C2AllocationGralloc::map( }; break; } + + case PixelFormat::YCBCR_420_888: + // fall-through + case PixelFormat::YV12: + // fall-through default: { - ALOGD("unsupported pixel format: %d", mInfo.mapperInfo.format); - return C2_OMITTED; + YCbCrLayout ycbcrLayout; + mMapper->lockYCbCr( + const_cast(mBuffer), grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { + err = maperr2error(maperr); + if (err == C2_OK) { + ycbcrLayout = mapLayout; + } + }); + if (err != C2_OK) { + ALOGD("lockYCbCr failed: %d", err); + return err; + } + addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y; + addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb; + addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr; + layout->type = C2PlanarLayout::TYPE_YUV; + layout->numPlanes = 3; + layout->rootPlanes = 3; + layout->planes[C2PlanarLayout::PLANE_Y] = { + C2PlaneInfo::CHANNEL_Y, // channel + 1, // colInc + (int32_t)ycbcrLayout.yStride, // rowInc + 1, // mColSampling + 1, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + C2PlanarLayout::PLANE_Y, // rootIx + 0, // offset + }; + layout->planes[C2PlanarLayout::PLANE_U] = { + C2PlaneInfo::CHANNEL_CB, // channel + (int32_t)ycbcrLayout.chromaStep, // colInc + (int32_t)ycbcrLayout.cStride, // rowInc + 2, // mColSampling + 2, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + C2PlanarLayout::PLANE_U, // rootIx + 0, // offset + }; + layout->planes[C2PlanarLayout::PLANE_V] = { + C2PlaneInfo::CHANNEL_CR, // channel + (int32_t)ycbcrLayout.chromaStep, // colInc + (int32_t)ycbcrLayout.cStride, // rowInc + 2, // mColSampling + 2, // mRowSampling + 8, // allocatedDepth + 8, // bitDepth + 0, // rightShift + C2PlaneInfo::NATIVE, // endianness + C2PlanarLayout::PLANE_V, // rootIx + 0, // offset + }; + // handle interleaved formats + intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U]; + if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) { + layout->rootPlanes = 2; + layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U; + layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset; + } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) { + layout->rootPlanes = 2; + layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V; + layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset; + } + break; } } mLocked = true; -- GitLab From 21f0854d4e799f221b5da8105bd9c8bdffaaf15c Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Tue, 18 Dec 2018 07:39:15 -0800 Subject: [PATCH 0631/1530] Disable Scudo's mismatch check for media_swcodec Some third party code is triggering the mismatch allocation check resulting in the service being terminated. Given that those mismatches have fairly low security implications and that said code cannot be immediately fixed, disable the check for now. Test: media post submit test suite Bug: 121178348 Change-Id: If9601d719969e590b80ab50c016903fb459c0cdf --- services/mediacodec/main_swcodecservice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index 386abb236b..79fea25e15 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -37,6 +37,12 @@ static const char kSystemSeccompPolicyPath[] = static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediacodec.policy"; +// Disable Scudo's mismatch allocation check, as it is being triggered +// by some third party code. +extern "C" const char *__scudo_default_options() { + return "DeallocationTypeMismatch=false"; +} + int main(int argc __unused, char** /*argv*/) { LOG(INFO) << "media swcodec service starting"; -- GitLab From be6a118e435fbddc0c14f04e5ce805f0c51756ac Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Mon, 17 Dec 2018 19:00:40 -0800 Subject: [PATCH 0632/1530] bufferpool2.0: Avoid lock during hidl oneway call Hidl oneway call works as synchronous call when it is called from same process. Avoid lock while calling hidl oneway interfaces. Bug: 121047202 Change-Id: I20c29640414edd70e414af749c0b3f96efda8ca3 --- media/bufferpool/2.0/AccessorImpl.cpp | 32 +++++++++++++++++++-------- media/bufferpool/2.0/AccessorImpl.h | 4 +++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp index 2c734ac565..526090943c 100644 --- a/media/bufferpool/2.0/AccessorImpl.cpp +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -253,9 +253,21 @@ void Accessor::Impl::flush() { } void Accessor::Impl::handleInvalidateAck() { - std::lock_guard lock(mBufferPool.mMutex); - mBufferPool.processStatusMessages(); - mBufferPool.mInvalidation.onHandleAck(); + std::map> observers; + uint32_t invalidationId; + { + std::lock_guard lock(mBufferPool.mMutex); + mBufferPool.processStatusMessages(); + mBufferPool.mInvalidation.onHandleAck(&observers, &invalidationId); + } + // Do not hold lock for send invalidations + for (auto it = observers.begin(); it != observers.end(); ++it) { + const sp observer = it->second; + if (observer) { + Return transResult = observer->onMessage(it->first, invalidationId); + (void) transResult; + } + } } bool Accessor::Impl::isValid() { @@ -365,19 +377,21 @@ void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest( sInvalidator->addAccessor(mId, impl); } -void Accessor::Impl::BufferPool::Invalidation::onHandleAck() { +void Accessor::Impl::BufferPool::Invalidation::onHandleAck( + std::map> *observers, + uint32_t *invalidationId) { if (mInvalidationId != 0) { + *invalidationId = mInvalidationId; std::set deads; for (auto it = mAcks.begin(); it != mAcks.end(); ++it) { if (it->second != mInvalidationId) { const sp observer = mObservers[it->first]; if (observer) { - ALOGV("connection %lld call observer (%u: %u)", + observers->emplace(it->first, observer); + ALOGV("connection %lld will call observer (%u: %u)", (long long)it->first, it->second, mInvalidationId); - Return transResult = observer->onMessage(it->first, mInvalidationId); - (void) transResult; - // N.B: ignore possibility of onMessage oneway call being - // lost. + // N.B: onMessage will be called later. ignore possibility of + // onMessage# oneway call being lost. it->second = mInvalidationId; } else { ALOGV("bufferpool2 observer died %lld", (long long)it->first); diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h index b3faa962a9..eea72b98fb 100644 --- a/media/bufferpool/2.0/AccessorImpl.h +++ b/media/bufferpool/2.0/AccessorImpl.h @@ -158,7 +158,9 @@ private: BufferInvalidationChannel &channel, const std::shared_ptr &impl); - void onHandleAck(); + void onHandleAck( + std::map> *observers, + uint32_t *invalidationId); } mInvalidation; /// Buffer pool statistics which tracks allocation and transfer statistics. struct Stats { -- GitLab From 0415a846bff7f6dac340c75a79e2ab89327c1c23 Mon Sep 17 00:00:00 2001 From: mike dooley Date: Tue, 18 Dec 2018 19:46:56 +0100 Subject: [PATCH 0633/1530] Adding sound trigger V2_2 Use sound trigger V2_2 if available, otherwise fall back to earlier versions. Note: V2_1 will be removed in a subsequent cl. Test: built android with V2_2 enabled and tested using SoundTriggerTestApp Bug: 70206501 Change-Id: I85b817b396b5765fea6507b8362d2f8509785557 --- services/soundtrigger/SoundTriggerHalHidl.cpp | 87 ++++++++++++++----- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp index 1d37a8ef44..68d54c7142 100644 --- a/services/soundtrigger/SoundTriggerHalHidl.cpp +++ b/services/soundtrigger/SoundTriggerHalHidl.cpp @@ -168,18 +168,23 @@ int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_ int ret; SoundModelHandle halHandle; sp soundtrigger_2_1 = toService2_1(soundtrigger); + sp soundtrigger_2_2 = toService2_2(soundtrigger); if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) { - if (!soundtrigger_2_1) { - ISoundTriggerHw::PhraseSoundModel halSoundModel; - convertPhraseSoundModelToHal(&halSoundModel, sound_model); - AutoMutex lock(mHalLock); - hidlReturn = soundtrigger->loadPhraseSoundModel( - halSoundModel, - this, modelId, [&](int32_t retval, auto res) { - ret = retval; - halHandle = res; - }); - } else { + if (soundtrigger_2_2) { + V2_2_ISoundTriggerHw::PhraseSoundModel halSoundModel; + auto result = convertPhraseSoundModelToHal(&halSoundModel, sound_model); + if (result.first) { + AutoMutex lock(mHalLock); + hidlReturn = soundtrigger_2_2->loadPhraseSoundModel_2_1( + halSoundModel, + this, modelId, [&](int32_t retval, auto res) { + ret = retval; + halHandle = res; + }); + } else { + return NO_MEMORY; + } + } else if (soundtrigger_2_1) { V2_1_ISoundTriggerHw::PhraseSoundModel halSoundModel; auto result = convertPhraseSoundModelToHal(&halSoundModel, sound_model); if (result.first) { @@ -193,18 +198,32 @@ int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_ } else { return NO_MEMORY; } - } - } else { - if (!soundtrigger_2_1) { - ISoundTriggerHw::SoundModel halSoundModel; - convertSoundModelToHal(&halSoundModel, sound_model); + } else { + ISoundTriggerHw::PhraseSoundModel halSoundModel; + convertPhraseSoundModelToHal(&halSoundModel, sound_model); AutoMutex lock(mHalLock); - hidlReturn = soundtrigger->loadSoundModel(halSoundModel, + hidlReturn = soundtrigger->loadPhraseSoundModel( + halSoundModel, this, modelId, [&](int32_t retval, auto res) { ret = retval; halHandle = res; }); - } else { + } + } else { + if (soundtrigger_2_2) { + V2_2_ISoundTriggerHw::SoundModel halSoundModel; + auto result = convertSoundModelToHal(&halSoundModel, sound_model); + if (result.first) { + AutoMutex lock(mHalLock); + hidlReturn = soundtrigger_2_2->loadSoundModel_2_1(halSoundModel, + this, modelId, [&](int32_t retval, auto res) { + ret = retval; + halHandle = res; + }); + } else { + return NO_MEMORY; + } + } else if (soundtrigger_2_1) { V2_1_ISoundTriggerHw::SoundModel halSoundModel; auto result = convertSoundModelToHal(&halSoundModel, sound_model); if (result.first) { @@ -217,6 +236,15 @@ int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_ } else { return NO_MEMORY; } + } else { + ISoundTriggerHw::SoundModel halSoundModel; + convertSoundModelToHal(&halSoundModel, sound_model); + AutoMutex lock(mHalLock); + hidlReturn = soundtrigger->loadSoundModel(halSoundModel, + this, modelId, [&](int32_t retval, auto res) { + ret = retval; + halHandle = res; + }); } } @@ -282,16 +310,20 @@ int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle, model->mRecognitionCookie = cookie; sp soundtrigger_2_1 = toService2_1(soundtrigger); + sp soundtrigger_2_2 = toService2_2(soundtrigger); Return hidlReturn(0); - if (!soundtrigger_2_1) { - ISoundTriggerHw::RecognitionConfig halConfig; - convertRecognitionConfigToHal(&halConfig, config); - { + if (soundtrigger_2_2) { + V2_2_ISoundTriggerHw::RecognitionConfig halConfig; + auto result = convertRecognitionConfigToHal(&halConfig, config); + if (result.first) { AutoMutex lock(mHalLock); - hidlReturn = soundtrigger->startRecognition(model->mHalHandle, halConfig, this, handle); + hidlReturn = soundtrigger_2_2->startRecognition_2_1( + model->mHalHandle, halConfig, this, handle); + } else { + return NO_MEMORY; } - } else { + } else if (soundtrigger_2_1) { V2_1_ISoundTriggerHw::RecognitionConfig halConfig; auto result = convertRecognitionConfigToHal(&halConfig, config); if (result.first) { @@ -301,6 +333,13 @@ int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle, } else { return NO_MEMORY; } + } else { + ISoundTriggerHw::RecognitionConfig halConfig; + convertRecognitionConfigToHal(&halConfig, config); + { + AutoMutex lock(mHalLock); + hidlReturn = soundtrigger->startRecognition(model->mHalHandle, halConfig, this, handle); + } } if (!hidlReturn.isOk()) { -- GitLab From cfd2830436e4bd400c4bf9c223642ef567c5aa6e Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Tue, 11 Dec 2018 19:16:46 -0800 Subject: [PATCH 0634/1530] Prevent recording when sensor privacy is enabled Test: Manually verified audio was not recorded when sensor privacy \ was enabled. Bug: 110842805 Change-Id: I3bb966d9322e258a8aba43fb4e9f65badd7c3506 --- services/audiopolicy/Android.mk | 6 ++- .../service/AudioPolicyService.cpp | 45 +++++++++++++++++++ .../audiopolicy/service/AudioPolicyService.h | 26 +++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 02ab8ad615..bfa1b5e92f 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -25,7 +25,11 @@ LOCAL_SHARED_LIBRARIES := \ libmedia_helper \ libmediametrics \ libmediautils \ - libeffectsconfig + libeffectsconfig \ + libsensorprivacy + +LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \ + libsensorprivacy LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index ee5d6ffee9..f233971cf0 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,9 @@ void AudioPolicyService::onFirstRef() mUidPolicy = new UidPolicy(this); mUidPolicy->registerSelf(); + + mSensorPrivacyPolicy = new SensorPrivacyPolicy(this); + mSensorPrivacyPolicy->registerSelf(); } AudioPolicyService::~AudioPolicyService() @@ -99,6 +103,9 @@ AudioPolicyService::~AudioPolicyService() mUidPolicy->unregisterSelf(); mUidPolicy.clear(); + + mSensorPrivacyPolicy->unregisterSelf(); + mSensorPrivacyPolicy.clear(); } // A notification client is always registered by AudioSystem when the client process @@ -375,6 +382,12 @@ void AudioPolicyService::updateUidStates_l() bool isAssistantOnTop = false; bool isSensitiveActive = false; + // if Sensor Privacy is enabled then all recordings should be silenced. + if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { + silenceAllRecordings_l(); + return; + } + for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; @@ -445,6 +458,13 @@ void AudioPolicyService::updateUidStates_l() } } +void AudioPolicyService::silenceAllRecordings_l() { + for (size_t i = 0; i < mAudioRecordClients.size(); i++) { + sp current = mAudioRecordClients[i]; + setAppState_l(current->uid, APP_STATE_IDLE); + } +} + /* static */ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { switch (amState) { @@ -858,6 +878,31 @@ bool AudioPolicyService::UidPolicy::isA11yUid(uid_t uid) return it != mA11yUids.end(); } +// ----------- AudioPolicyService::SensorPrivacyService implementation ---------- +void AudioPolicyService::SensorPrivacyPolicy::registerSelf() { + SensorPrivacyManager spm; + mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled(); + spm.addSensorPrivacyListener(this); +} + +void AudioPolicyService::SensorPrivacyPolicy::unregisterSelf() { + SensorPrivacyManager spm; + spm.removeSensorPrivacyListener(this); +} + +bool AudioPolicyService::SensorPrivacyPolicy::isSensorPrivacyEnabled() { + return mSensorPrivacyEnabled; +} + +binder::Status AudioPolicyService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) { + mSensorPrivacyEnabled = enabled; + sp service = mService.promote(); + if (service != nullptr) { + service->updateUidStates(); + } + return binder::Status::ok(); +} + // ----------- AudioPolicyService::AudioCommandThread implementation ---------- AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 23c3daace5..45d37dc022 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -33,6 +33,7 @@ #include #include "AudioPolicyEffects.h" #include "managerdefault/AudioPolicyManager.h" +#include #include @@ -279,6 +280,8 @@ private: void updateUidStates(); void updateUidStates_l(); + void silenceAllRecordings_l(); + static bool isPrivacySensitive(audio_source_t source); // If recording we need to make sure the UID is allowed to do that. If the UID is idle @@ -334,6 +337,27 @@ private: std::vector mA11yUids; }; + // If sensor privacy is enabled then all apps, including those that are active, should be + // prevented from recording. This is handled similar to idle UIDs, any app that attempts + // to record while sensor privacy is enabled will receive buffers with zeros. As soon as + // sensor privacy is disabled active apps will receive the expected data when recording. + class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener { + public: + explicit SensorPrivacyPolicy(wp service) + : mService(service) {} + + void registerSelf(); + void unregisterSelf(); + + bool isSensorPrivacyEnabled(); + + binder::Status onSensorPrivacyChanged(bool enabled); + + private: + wp mService; + std::atomic_bool mSensorPrivacyEnabled; + }; + // Thread used to send audio config commands to audio flinger // For audio config commands, it is necessary because audio flinger requires that the calling // process (user) has permission to modify audio settings. @@ -718,6 +742,8 @@ private: audio_mode_t mPhoneState; sp mUidPolicy; + sp mSensorPrivacyPolicy; + DefaultKeyedVector< audio_port_handle_t, sp > mAudioRecordClients; DefaultKeyedVector< audio_port_handle_t, sp > mAudioPlaybackClients; }; -- GitLab From ff0508b3d64e54829b69170adace3bbea4f6a5b6 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 14 Dec 2018 15:17:58 -0800 Subject: [PATCH 0635/1530] Convert MPEG2 extractor to V3 format and move it to the media APEX. Bug: 111407253 Test: manual, CTS Change-Id: Ibcbdd0e901e9d79b5a857b5d0a65bd6b1ec675a6 --- apex/Android.bp | 1 + media/extractors/mpeg2/Android.bp | 10 +- media/extractors/mpeg2/ExtractorBundle.cpp | 14 +- media/extractors/mpeg2/MPEG2PSExtractor.cpp | 142 +++++++++----- media/extractors/mpeg2/MPEG2PSExtractor.h | 10 +- media/extractors/mpeg2/MPEG2TSExtractor.cpp | 180 ++++++++++++++---- media/extractors/mpeg2/MPEG2TSExtractor.h | 12 +- media/libmediaextractor/Android.bp | 9 + media/libmediaextractor/MediaBuffer.cpp | 4 + media/libmediaextractor/MediaBufferGroup.cpp | 4 + .../include/media/stagefright/MediaBuffer.h | 21 +- media/libstagefright/Android.bp | 2 +- media/libstagefright/MediaTrack.cpp | 4 + media/libstagefright/Utils.cpp | 1 + services/mediaextractor/Android.mk | 4 - 15 files changed, 310 insertions(+), 108 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 991696c121..05cc2c5aa6 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -24,6 +24,7 @@ apex { "libmkvextractor", "libmp3extractor", "libmp4extractor", + "libmpeg2extractor", "liboggextractor", "libwavextractor", ], diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index e5cdfe486e..b8160930f3 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -17,12 +17,9 @@ cc_library_shared { "android.hidl.token@1.0-utils", "android.hidl.allocator@1.0", "libhidlmemory", - "libcrypto", - "libcutils", "libhidlbase", "liblog", - "libmediaextractor", - "libstagefright_foundation", + "libmediandk", ], header_libs: [ @@ -30,8 +27,13 @@ cc_library_shared { ], static_libs: [ + "libcrypto", + "libstagefright_foundation", "libstagefright_mpeg2support", + "libmediaextractor", "libutils", + "libstagefright", + "libstagefright_esds", ], name: "libmpeg2extractor", diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index 366aa59d9c..c10a79784b 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -31,27 +31,27 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION, + EXTRACTORDEF_VERSION_CURRENT + 1, UUID("3d1dcfeb-e40a-436d-a574-c2438a555e5f"), 1, "MPEG2-PS/TS Extractor", { - []( + .v3 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFunc { + FreeMetaFunc *) -> CreatorFuncV3 { DataSourceHelper helper(source); if (SniffMPEG2TS(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MPEG2TSExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new MPEG2TSExtractor(new DataSourceHelper(source)));}; } else if (SniffMPEG2PS(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MPEG2PSExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractorV3* { + return wrapV3(new MPEG2PSExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index fc13d2cc0c..b60fc4e864 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -33,22 +33,23 @@ #include #include #include +#include #include #include namespace android { -struct MPEG2PSExtractor::Track : public MediaTrackHelper, public RefBase { +struct MPEG2PSExtractor::Track : public MediaTrackHelperV3 { Track(MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type); - virtual status_t start(); - virtual status_t stop(); - virtual status_t getFormat(MetaDataBase &); + virtual media_status_t start(); + virtual media_status_t stop(); + virtual media_status_t getFormat(AMediaFormat *); - virtual status_t read( - MediaBufferBase **buffer, const ReadOptions *options); + virtual media_status_t read( + MediaBufferHelperV3 **buffer, const ReadOptions *options); protected: virtual ~Track(); @@ -71,22 +72,22 @@ private: DISALLOW_EVIL_CONSTRUCTORS(Track); }; -struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelper { - WrappedTrack(MPEG2PSExtractor *extractor, const sp &track); +struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelperV3 { + WrappedTrack(MPEG2PSExtractor *extractor, Track *track); - virtual status_t start(); - virtual status_t stop(); - virtual status_t getFormat(MetaDataBase &); + virtual media_status_t start(); + virtual media_status_t stop(); + virtual media_status_t getFormat(AMediaFormat *); - virtual status_t read( - MediaBufferBase **buffer, const ReadOptions *options); + virtual media_status_t read( + MediaBufferHelperV3 **buffer, const ReadOptions *options); protected: virtual ~WrappedTrack(); private: MPEG2PSExtractor *mExtractor; - sp mTrack; + MPEG2PSExtractor::Track *mTrack; DISALLOW_EVIL_CONSTRUCTORS(WrappedTrack); }; @@ -107,13 +108,14 @@ MPEG2PSExtractor::MPEG2PSExtractor(DataSourceHelper *source) } // Remove all tracks that were unable to determine their format. - MetaDataBase meta; + AMediaFormat *meta = AMediaFormat_new(); for (size_t i = mTracks.size(); i > 0;) { i--; - if (mTracks.valueAt(i)->getFormat(meta) != OK) { + if (mTracks.valueAt(i)->getFormat(meta) != AMEDIA_OK) { mTracks.removeItemsAt(i); } } + AMediaFormat_delete(meta); mScanning = false; } @@ -126,7 +128,7 @@ size_t MPEG2PSExtractor::countTracks() { return mTracks.size(); } -MediaTrackHelper *MPEG2PSExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *MPEG2PSExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } @@ -134,20 +136,20 @@ MediaTrackHelper *MPEG2PSExtractor::getTrack(size_t index) { return new WrappedTrack(this, mTracks.valueAt(index)); } -status_t MPEG2PSExtractor::getTrackMetaData( - MetaDataBase &meta, +media_status_t MPEG2PSExtractor::getTrackMetaData( + AMediaFormat *meta, size_t index, uint32_t /* flags */) { if (index >= mTracks.size()) { - return UNKNOWN_ERROR; + return AMEDIA_ERROR_UNKNOWN; } return mTracks.valueAt(index)->getFormat(meta); } -status_t MPEG2PSExtractor::getMetaData(MetaDataBase &meta) { - meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2PS); +media_status_t MPEG2PSExtractor::getMetaData(AMediaFormat *meta) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_MPEG2PS); - return OK; + return AMEDIA_OK; } uint32_t MPEG2PSExtractor::flags() const { @@ -635,42 +637,55 @@ MPEG2PSExtractor::Track::~Track() { mQueue = NULL; } -status_t MPEG2PSExtractor::Track::start() { +media_status_t MPEG2PSExtractor::Track::start() { if (mSource == NULL) { - return NO_INIT; + return AMEDIA_ERROR_UNKNOWN; } - return mSource->start(NULL); // AnotherPacketSource::start doesn't use its argument + // initialize with one small buffer, but allow growth + mBufferGroup->init(1 /* one buffer */, 256 /* buffer size */, 64 /* max number of buffers */); + + if (mSource->start(NULL) == OK) { // AnotherPacketSource::start doesn't use its argument + return AMEDIA_OK; + } + return AMEDIA_ERROR_UNKNOWN; } -status_t MPEG2PSExtractor::Track::stop() { +media_status_t MPEG2PSExtractor::Track::stop() { if (mSource == NULL) { - return NO_INIT; + return AMEDIA_ERROR_UNKNOWN; } - return mSource->stop(); + if (mSource->stop() == OK) { + return AMEDIA_OK; + } + return AMEDIA_ERROR_UNKNOWN; } -status_t MPEG2PSExtractor::Track::getFormat(MetaDataBase &meta) { +void copyAMessageToAMediaFormat(AMediaFormat *format, sp msg); + +media_status_t MPEG2PSExtractor::Track::getFormat(AMediaFormat *meta) { if (mSource == NULL) { - return NO_INIT; + return AMEDIA_ERROR_UNKNOWN; } sp sourceMeta = mSource->getFormat(); - meta = *sourceMeta; - return OK; + sp msg; + convertMetaDataToMessage(sourceMeta, &msg); + copyAMessageToAMediaFormat(meta, msg); + return AMEDIA_OK; } -status_t MPEG2PSExtractor::Track::read( - MediaBufferBase **buffer, const ReadOptions *options) { +media_status_t MPEG2PSExtractor::Track::read( + MediaBufferHelperV3 **buffer, const ReadOptions *options) { if (mSource == NULL) { - return NO_INIT; + return AMEDIA_ERROR_UNKNOWN; } status_t finalResult; while (!mSource->hasBufferAvailable(&finalResult)) { if (finalResult != OK) { - return ERROR_END_OF_STREAM; + return AMEDIA_ERROR_END_OF_STREAM; } status_t err = mExtractor->feedMore(); @@ -680,7 +695,46 @@ status_t MPEG2PSExtractor::Track::read( } } - return mSource->read(buffer, (MediaSource::ReadOptions*)options); + MediaBufferBase *mbuf; + mSource->read(&mbuf, (MediaTrack::ReadOptions*) options); + size_t length = mbuf->range_length(); + MediaBufferHelperV3 *outbuf; + mBufferGroup->acquire_buffer(&outbuf, false, length); + memcpy(outbuf->data(), mbuf->data(), length); + outbuf->set_range(0, length); + *buffer = outbuf; + MetaDataBase &inMeta = mbuf->meta_data(); + AMediaFormat *outMeta = outbuf->meta_data(); + int64_t val64; + if (inMeta.findInt64(kKeyTime, &val64)) { + AMediaFormat_setInt64(outMeta, AMEDIAFORMAT_KEY_TIME_US, val64); + } + int32_t val32; + if (inMeta.findInt32(kKeyIsSyncFrame, &val32)) { + AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, val32); + } + if (inMeta.findInt32(kKeyCryptoMode, &val32)) { + AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_CRYPTO_MODE, val32); + } + uint32_t bufType; + const void *bufData; + size_t bufSize; + if (inMeta.findData(kKeyCryptoIV, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_IV, bufData, bufSize); + } + if (inMeta.findData(kKeyCryptoKey, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_KEY, bufData, bufSize); + } + if (inMeta.findData(kKeyPlainSizes, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, bufData, bufSize); + } + if (inMeta.findData(kKeyEncryptedSizes, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, bufData, bufSize); + } + if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); + } + return AMEDIA_OK; } status_t MPEG2PSExtractor::Track::appendPESData( @@ -726,7 +780,7 @@ status_t MPEG2PSExtractor::Track::appendPESData( //////////////////////////////////////////////////////////////////////////////// MPEG2PSExtractor::WrappedTrack::WrappedTrack( - MPEG2PSExtractor *extractor, const sp &track) + MPEG2PSExtractor *extractor, Track *track) : mExtractor(extractor), mTrack(track) { } @@ -734,20 +788,20 @@ MPEG2PSExtractor::WrappedTrack::WrappedTrack( MPEG2PSExtractor::WrappedTrack::~WrappedTrack() { } -status_t MPEG2PSExtractor::WrappedTrack::start() { +media_status_t MPEG2PSExtractor::WrappedTrack::start() { return mTrack->start(); } -status_t MPEG2PSExtractor::WrappedTrack::stop() { +media_status_t MPEG2PSExtractor::WrappedTrack::stop() { return mTrack->stop(); } -status_t MPEG2PSExtractor::WrappedTrack::getFormat(MetaDataBase &meta) { +media_status_t MPEG2PSExtractor::WrappedTrack::getFormat(AMediaFormat *meta) { return mTrack->getFormat(meta); } -status_t MPEG2PSExtractor::WrappedTrack::read( - MediaBufferBase **buffer, const ReadOptions *options) { +media_status_t MPEG2PSExtractor::WrappedTrack::read( + MediaBufferHelperV3 **buffer, const ReadOptions *options) { return mTrack->read(buffer, options); } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h index c4082ef6cb..d251688470 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.h +++ b/media/extractors/mpeg2/MPEG2PSExtractor.h @@ -32,14 +32,14 @@ struct AMessage; struct Track; class String8; -struct MPEG2PSExtractor : public MediaExtractorPluginHelper { +struct MPEG2PSExtractor : public MediaExtractorPluginHelperV3 { explicit MPEG2PSExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelper *getTrack(size_t index); - virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags); + virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); - virtual status_t getMetaData(MetaDataBase& meta); + virtual media_status_t getMetaData(AMediaFormat *meta); virtual uint32_t flags() const; virtual const char * name() { return "MPEG2PSExtractor"; } @@ -57,7 +57,7 @@ private: off64_t mOffset; status_t mFinalResult; sp mBuffer; - KeyedVector > mTracks; + KeyedVector mTracks; bool mScanning; bool mProgramStreamMapValid; diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 605b13aa52..fc066ee882 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "mpeg2ts/AnotherPacketSource.h" @@ -50,19 +51,19 @@ static const size_t kTSPacketSize = 188; static const int kMaxDurationReadSize = 250000LL; static const int kMaxDurationRetry = 6; -struct MPEG2TSSource : public MediaTrackHelper { +struct MPEG2TSSource : public MediaTrackHelperV3 { MPEG2TSSource( MPEG2TSExtractor *extractor, const sp &impl, bool doesSeek); virtual ~MPEG2TSSource(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t getFormat(MetaDataBase &); + virtual media_status_t start(); + virtual media_status_t stop(); + virtual media_status_t getFormat(AMediaFormat *); - virtual status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL); + virtual media_status_t read( + MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); private: MPEG2TSExtractor *mExtractor; @@ -87,22 +88,84 @@ MPEG2TSSource::MPEG2TSSource( MPEG2TSSource::~MPEG2TSSource() { } -status_t MPEG2TSSource::start() { - return mImpl->start(NULL); // AnotherPacketSource::start() doesn't use its argument +media_status_t MPEG2TSSource::start() { + // initialize with one small buffer, but allow growth + mBufferGroup->init(1 /* one buffer */, 256 /* buffer size */, 64 /* max number of buffers */); + + if (!mImpl->start(NULL)) { // AnotherPacketSource::start() doesn't use its argument + return AMEDIA_OK; + } + return AMEDIA_ERROR_UNKNOWN; +} + +media_status_t MPEG2TSSource::stop() { + if (!mImpl->stop()) { + return AMEDIA_OK; + } + return AMEDIA_ERROR_UNKNOWN; } -status_t MPEG2TSSource::stop() { - return mImpl->stop(); +void copyAMessageToAMediaFormat(AMediaFormat *format, sp msg) { + size_t numEntries = msg->countEntries(); + for (size_t i = 0; i < numEntries; i++) { + AMessage::Type type; + const char *name = msg->getEntryNameAt(i, &type); + AMessage::ItemData id = msg->getEntryAt(i); + + switch (type) { + case AMessage::kTypeInt32: + int32_t val32; + if (id.find(&val32)) { + AMediaFormat_setInt32(format, name, val32); + } + break; + case AMessage::kTypeInt64: + int64_t val64; + if (id.find(&val64)) { + AMediaFormat_setInt64(format, name, val64); + } + break; + case AMessage::kTypeFloat: + float valfloat; + if (id.find(&valfloat)) { + AMediaFormat_setFloat(format, name, valfloat); + } + break; + case AMessage::kTypeDouble: + double valdouble; + if (id.find(&valdouble)) { + AMediaFormat_setDouble(format, name, valdouble); + } + break; + case AMessage::kTypeString: + if (AString s; id.find(&s)) { + AMediaFormat_setString(format, name, s.c_str()); + } + break; + case AMessage::kTypeBuffer: + { + sp buffer; + if (id.find(&buffer)) { + AMediaFormat_setBuffer(format, name, buffer->data(), buffer->size()); + } + break; + } + default: + ALOGW("ignoring unsupported type %d '%s'", type, name); + } + } } -status_t MPEG2TSSource::getFormat(MetaDataBase &meta) { +media_status_t MPEG2TSSource::getFormat(AMediaFormat *meta) { sp implMeta = mImpl->getFormat(); - meta = *implMeta; - return OK; + sp msg; + convertMetaDataToMessage(implMeta, &msg); + copyAMessageToAMediaFormat(meta, msg); + return AMEDIA_OK; } -status_t MPEG2TSSource::read( - MediaBufferBase **out, const ReadOptions *options) { +media_status_t MPEG2TSSource::read( + MediaBufferHelperV3 **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -110,16 +173,59 @@ status_t MPEG2TSSource::read( if (mDoesSeek && options && options->getSeekTo(&seekTimeUs, &seekMode)) { // seek is needed status_t err = mExtractor->seek(seekTimeUs, (ReadOptions::SeekMode)seekMode); - if (err != OK) { - return err; + if (err == ERROR_END_OF_STREAM) { + return AMEDIA_ERROR_END_OF_STREAM; + } else if (err != OK) { + return AMEDIA_ERROR_UNKNOWN; } } if (mExtractor->feedUntilBufferAvailable(mImpl) != OK) { - return ERROR_END_OF_STREAM; + return AMEDIA_ERROR_END_OF_STREAM; + } + + MediaBufferBase *mbuf; + mImpl->read(&mbuf, (MediaTrack::ReadOptions*) options); + size_t length = mbuf->range_length(); + MediaBufferHelperV3 *outbuf; + mBufferGroup->acquire_buffer(&outbuf, false, length); + memcpy(outbuf->data(), mbuf->data(), length); + outbuf->set_range(0, length); + *out = outbuf; + MetaDataBase &inMeta = mbuf->meta_data(); + AMediaFormat *outMeta = outbuf->meta_data(); + AMediaFormat_clear(outMeta); + int64_t val64; + if (inMeta.findInt64(kKeyTime, &val64)) { + AMediaFormat_setInt64(outMeta, AMEDIAFORMAT_KEY_TIME_US, val64); + } + int32_t val32; + if (inMeta.findInt32(kKeyIsSyncFrame, &val32)) { + AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, val32); + } + if (inMeta.findInt32(kKeyCryptoMode, &val32)) { + AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_CRYPTO_MODE, val32); + } + uint32_t bufType; + const void *bufData; + size_t bufSize; + if (inMeta.findData(kKeyCryptoIV, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_IV, bufData, bufSize); + } + if (inMeta.findData(kKeyCryptoKey, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_KEY, bufData, bufSize); + } + if (inMeta.findData(kKeyPlainSizes, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, bufData, bufSize); + } + if (inMeta.findData(kKeyEncryptedSizes, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, bufData, bufSize); + } + if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } - return mImpl->read(out, (MediaSource::ReadOptions*) options); + return AMEDIA_OK; } //////////////////////////////////////////////////////////////////////////////// @@ -140,7 +246,7 @@ size_t MPEG2TSExtractor::countTracks() { return mSourceImpls.size(); } -MediaTrackHelper *MPEG2TSExtractor::getTrack(size_t index) { +MediaTrackHelperV3 *MPEG2TSExtractor::getTrack(size_t index) { if (index >= mSourceImpls.size()) { return NULL; } @@ -151,22 +257,23 @@ MediaTrackHelper *MPEG2TSExtractor::getTrack(size_t index) { (mSeekSyncPoints == &mSyncPoints.editItemAt(index))); } -status_t MPEG2TSExtractor::getTrackMetaData( - MetaDataBase &meta, +media_status_t MPEG2TSExtractor::getTrackMetaData( + AMediaFormat *meta, size_t index, uint32_t /* flags */) { sp implMeta = index < mSourceImpls.size() ? mSourceImpls.editItemAt(index)->getFormat() : NULL; if (implMeta == NULL) { - return UNKNOWN_ERROR; + return AMEDIA_ERROR_UNKNOWN; } - meta = *implMeta; - return OK; + sp msg = new AMessage; + convertMetaDataToMessage(implMeta, &msg); + copyAMessageToAMediaFormat(meta, msg); + return AMEDIA_OK; } -status_t MPEG2TSExtractor::getMetaData(MetaDataBase &meta) { - meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); - - return OK; +media_status_t MPEG2TSExtractor::getMetaData(AMediaFormat *meta) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); + return AMEDIA_OK; } //static @@ -177,7 +284,7 @@ bool MPEG2TSExtractor::isScrambledFormat(MetaDataBase &format) { || !strcasecmp(MEDIA_MIMETYPE_AUDIO_SCRAMBLED, mime)); } -status_t MPEG2TSExtractor::setMediaCas(const uint8_t* casToken, size_t size) { +media_status_t MPEG2TSExtractor::setMediaCas(const uint8_t* casToken, size_t size) { HalToken halToken; halToken.setToExternal((uint8_t*)casToken, size); sp cas = ICas::castFrom(retrieveHalInterface(halToken)); @@ -187,8 +294,9 @@ status_t MPEG2TSExtractor::setMediaCas(const uint8_t* casToken, size_t size) { if (err == OK) { ALOGI("All tracks now have descramblers"); init(); + return AMEDIA_OK; } - return err; + return AMEDIA_ERROR_UNKNOWN; } void MPEG2TSExtractor::addSource(const sp &impl) { @@ -494,7 +602,7 @@ uint32_t MPEG2TSExtractor::flags() const { } status_t MPEG2TSExtractor::seek(int64_t seekTimeUs, - const MediaTrackHelper::ReadOptions::SeekMode &seekMode) { + const MediaTrackHelperV3::ReadOptions::SeekMode &seekMode) { if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) { ALOGW("No sync point to seek to."); // ... and therefore we have nothing useful to do here. @@ -515,18 +623,18 @@ status_t MPEG2TSExtractor::seek(int64_t seekTimeUs, } switch (seekMode) { - case MediaTrackHelper::ReadOptions::SEEK_NEXT_SYNC: + case MediaTrackHelperV3::ReadOptions::SEEK_NEXT_SYNC: if (index == mSeekSyncPoints->size()) { ALOGW("Next sync not found; starting from the latest sync."); --index; } break; - case MediaTrackHelper::ReadOptions::SEEK_CLOSEST_SYNC: - case MediaTrackHelper::ReadOptions::SEEK_CLOSEST: + case MediaTrackHelperV3::ReadOptions::SEEK_CLOSEST_SYNC: + case MediaTrackHelperV3::ReadOptions::SEEK_CLOSEST: ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC", seekMode); FALLTHROUGH_INTENDED; - case MediaTrackHelper::ReadOptions::SEEK_PREVIOUS_SYNC: + case MediaTrackHelperV3::ReadOptions::SEEK_PREVIOUS_SYNC: if (index == 0) { ALOGW("Previous sync not found; starting from the earliest " "sync."); diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index 40134424c6..aed17bbb0c 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -38,16 +38,16 @@ struct CDataSource; struct MPEG2TSSource; class String8; -struct MPEG2TSExtractor : public MediaExtractorPluginHelper { +struct MPEG2TSExtractor : public MediaExtractorPluginHelperV3 { explicit MPEG2TSExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelper *getTrack(size_t index); - virtual status_t getTrackMetaData(MetaDataBase &meta, size_t index, uint32_t flags); + virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); - virtual status_t getMetaData(MetaDataBase& meta); + virtual media_status_t getMetaData(AMediaFormat *meta); - virtual status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) override; + virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) override; virtual uint32_t flags() const; virtual const char * name() { return "MPEG2TSExtractor"; } @@ -90,7 +90,7 @@ private: // the data has syntax error during parsing, etc. status_t feedMore(bool isInit = false); status_t seek(int64_t seekTimeUs, - const MediaTrackHelper::ReadOptions::SeekMode& seekMode); + const MediaTrackHelperV3::ReadOptions::SeekMode& seekMode); status_t queueDiscontinuityForSeek(int64_t actualSeekTimeUs); status_t seekBeyond(int64_t seekTimeUs); diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp index 6f2b35f027..1aa1e13369 100644 --- a/media/libmediaextractor/Android.bp +++ b/media/libmediaextractor/Android.bp @@ -14,6 +14,15 @@ cc_library { "-Wall", ], + static: { + cflags: [ + "-Wno-multichar", + "-Werror", + "-Wall", + "-DNO_IMEMORY", + ], + }, + shared_libs: [ "libbinder", "libstagefright_foundation", diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp index 26d0bd40fb..bab3a03c4f 100644 --- a/media/libmediaextractor/MediaBuffer.cpp +++ b/media/libmediaextractor/MediaBuffer.cpp @@ -51,9 +51,12 @@ MediaBuffer::MediaBuffer(size_t size) mRangeLength(size), mOwnsData(true), mMetaData(new MetaDataBase) { +#ifndef NO_IMEMORY if (size < kSharedMemThreshold || std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) { +#endif mData = malloc(size); +#ifndef NO_IMEMORY } else { ALOGV("creating memoryDealer"); sp memoryDealer = @@ -71,6 +74,7 @@ MediaBuffer::MediaBuffer(size_t size) ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData); } } +#endif } MediaBuffer::MediaBuffer(const sp &buffer) diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp index 4e6beca19d..e88ed5adca 100644 --- a/media/libmediaextractor/MediaBufferGroup.cpp +++ b/media/libmediaextractor/MediaBufferGroup.cpp @@ -62,6 +62,7 @@ void MediaBufferGroup::init(size_t buffers, size_t buffer_size, size_t growthLim mInternal->mGrowthLimit = buffers; } +#ifndef NO_IMEMORY if (buffer_size >= kSharedMemoryThreshold) { ALOGD("creating MemoryDealer"); // Using a single MemoryDealer is efficient for a group of shared memory objects. @@ -84,6 +85,9 @@ void MediaBufferGroup::init(size_t buffers, size_t buffer_size, size_t growthLim } return; } +#else + (void)kSharedMemoryThreshold; +#endif // Non-shared memory allocation. for (size_t i = 0; i < buffers; ++i) { diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h index 5b362a4ad8..ace63ae9f8 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h @@ -46,12 +46,13 @@ public: explicit MediaBuffer(size_t size); explicit MediaBuffer(const sp &buffer); - +#ifndef NO_IMEMORY MediaBuffer(const sp &mem) : MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) { // delegate and override mMemory mMemory = mem; } +#endif // If MediaBufferGroup is set, decrement the local reference count; // if the local reference count drops to 0, return the buffer to the @@ -92,17 +93,26 @@ public: } virtual int remoteRefcount() const { +#ifndef NO_IMEMORY if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; int32_t remoteRefcount = reinterpret_cast(mMemory->pointer())->getRemoteRefcount(); // Sanity check so that remoteRefCount() is non-negative. return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data. +#else + return 0; +#endif } // returns old value int addRemoteRefcount(int32_t value) { +#ifndef NO_IMEMORY if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; return reinterpret_cast(mMemory->pointer())->addRemoteRefcount(value); +#else + (void) value; + return 0; +#endif } bool isDeadObject() const { @@ -110,8 +120,13 @@ public: } static bool isDeadObject(const sp &memory) { +#ifndef NO_IMEMORY if (memory.get() == nullptr || memory->pointer() == nullptr) return false; return reinterpret_cast(memory->pointer())->isDeadObject(); +#else + (void) memory; + return false; +#endif } // Sticky on enabling of shared memory MediaBuffers. By default we don't use @@ -204,7 +219,11 @@ private: }; inline SharedControl *getSharedControl() const { +#ifndef NO_IMEMORY return reinterpret_cast(mMemory->pointer()); +#else + return nullptr; +#endif } }; diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 9aea88a599..dec5d3b7fc 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -77,7 +77,7 @@ cc_library_shared { }, } -cc_library_shared { +cc_library { name: "libstagefright", srcs: [ diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index ef252f44e6..1e49f6ae31 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -265,6 +265,10 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption meta.setData(kKeyCryptoIV, MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); } + if (format->mFormat->findBuffer("sei", &valbuf)) { + meta.setData(kKeySEI, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } } else { *buffer = nullptr; } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 5e8d173825..8f2427e3ec 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -634,6 +634,7 @@ static std::vector> bufferMappings { { "crypto-encrypted-sizes", kKeyEncryptedSizes }, { "crypto-plain-sizes", kKeyPlainSizes }, { "icc-profile", kKeyIccProfile }, + { "sei", kKeySEI }, { "text-format-data", kKeyTextFormatData }, } }; diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 336bbe85d7..e31eadc8d8 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -38,10 +38,6 @@ LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaextractor.policy LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy -# extractor libraries -LOCAL_REQUIRED_MODULES += \ - libmpeg2extractor \ - LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ liblog libbase libicuuc libavservices_minijail -- GitLab From d9499eb6d127dc53c8aa3deda05a70b03fc464f3 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 17 Dec 2018 16:23:22 -0800 Subject: [PATCH 0636/1530] Audio HAL: Update for the new definition of SinkMetadata In Audio HAL V5.0 SinkMetadata receives a new member: optional DeviceAddress. Use per field initialization to leave the safe_union in an uninitialized state. Bug: 120859615 Test: make Change-Id: I358b9abbcb0690aebb2c658a72adb287ceec22b5 --- media/libaudiohal/impl/DeviceHalHidl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index 8537608323..7a9e843b48 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -277,18 +277,18 @@ status_t DeviceHalHidl::openInputStream( HidlUtils::audioConfigFromHal(*config, &hidlConfig); Result retval = Result::NOT_INITIALIZED; #if MAJOR_VERSION == 2 - auto sourceMetadata = AudioSource(source); + auto sinkMetadata = AudioSource(source); #elif MAJOR_VERSION >= 4 // TODO: correctly propagate the tracks sources and volume // for now, only send the main source at 1dbfs - SinkMetadata sourceMetadata = {{{AudioSource(source), 1}}}; + SinkMetadata sinkMetadata = {{{ .source = AudioSource(source), .gain = 1 }}}; #endif Return ret = mDevice->openInputStream( handle, hidlDevice, hidlConfig, EnumBitfield(flags), - sourceMetadata, + sinkMetadata, [&](Result r, const sp& result, const AudioConfig& suggestedConfig) { retval = r; if (retval == Result::OK) { -- GitLab From 79b85a879771f30cfb2de4954badd5738d5b1dd7 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Thu, 6 Dec 2018 16:09:45 +0530 Subject: [PATCH 0637/1530] libeffects: Correct ConcertSurround 96kHz and 192kHz filter coefs Corrected the headphone side filter coefficients for 96kHz and 192kHz sampling rates for the Concert Surround / Stereo Widening (CS) module. Test: local native test lvmtest Bug: 120942559 Change-Id: I213ff07aa54f4e0e4271c7ae619ede3881cee409 --- .../src/LVCS_Headphone_Coeffs.h | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h index 277d95c6da..e45d81f439 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h @@ -178,13 +178,15 @@ #define CS_MIDDLE_96000_B1 (-0.993334) #define CS_MIDDLE_96000_B2 0.000000 #define CS_MIDDLE_96000_SCALE 15 -/* bandpass filter with fc1 270 and fc2 3703, designed using 2nd order butterworth */ -#define CS_SIDE_96000_A0 0.016727 -#define CS_SIDE_96000_A1 0.000000 -#define CS_SIDE_96000_A2 (-0.016727) -#define CS_SIDE_96000_B1 (-1.793372) -#define CS_SIDE_96000_B2 0.797236 -#define CS_SIDE_96000_SCALE 14 +/* Coefficients calculated using tf2ss and ss2tf functions based on + * coefficients available for 48000Hz sampling frequency + */ +#define CS_SIDE_96000_A0 0.224326f +#define CS_SIDE_96000_A1 (-0.294937f) +#define CS_SIDE_96000_A2 0.070611f +#define CS_SIDE_96000_B1 (-1.792166f) +#define CS_SIDE_96000_B2 0.795830f +#define CS_SIDE_96000_SCALE 14 /* Stereo Enhancer coefficients for 176400Hz sample rate. * The filter coefficients are obtained by carrying out @@ -211,13 +213,15 @@ #define CS_MIDDLE_192000_B1 (-0.996661) #define CS_MIDDLE_192000_B2 0.000000 #define CS_MIDDLE_192000_SCALE 15 -/* bandpass filter with fc1 270 and fc2 3703, designed using 2nd order butterworth */ -#define CS_SIDE_192000_A0 0.008991 -#define CS_SIDE_192000_A1 (-0.000000) -#define CS_SIDE_192000_A2 (-0.008991) -#define CS_SIDE_192000_B1 (-1.892509) -#define CS_SIDE_192000_B2 0.893524 -#define CS_SIDE_192000_SCALE 14 +/* Coefficients calculated using tf2ss and ss2tf functions based on + * coefficients available for 48000Hz sampling frequency + */ +#define CS_SIDE_192000_A0 0.196039f +#define CS_SIDE_192000_A1 (-0.311027f) +#define CS_SIDE_192000_A2 0.114988f +#define CS_SIDE_192000_B1 (-1.891380f) +#define CS_SIDE_192000_B2 0.8923460f +#define CS_SIDE_192000_SCALE 14 #endif /************************************************************************************/ -- GitLab From a003298b48a186bf2de45a074e6c532be92907e4 Mon Sep 17 00:00:00 2001 From: Jin Seok Park Date: Tue, 18 Dec 2018 15:43:55 +0900 Subject: [PATCH 0638/1530] AML: Replace ParceledListSlice This CL creates a new class named MediaParceledListSlice that replaces ParceledListSlice. MediaParceledListSlice is mostly copied from ParcelImplListSlice.java with a few adjustments: 1) not specifying type of parcelable (to work for both MediaSession.QueueItem & MediaBrowser.Mediaitem) 2) Setting parameter to null for calling Parcel#readParcelable. Parcel retrieves the class loader inside readParcelableCreator. Bug: 119750807 Test: mmm . (under frameworks/av/packages/MediaComponents) Change-Id: If234308724ba132140089835e66b3948aacc4e57 --- packages/MediaComponents/apex/Android.bp | 2 + .../android/media/MediaParceledListSlice.aidl | 19 ++ .../android/media/MediaParceledListSlice.java | 202 ++++++++++++++++++ .../android/media/browse/MediaBrowser.java | 14 +- .../java/android/media/session/ISession.aidl | 5 +- .../media/session/ISessionController.aidl | 5 +- .../session/ISessionControllerCallback.aidl | 5 +- .../media/session/MediaController.java | 12 +- .../android/media/session/MediaSession.java | 7 +- .../media/IMediaBrowserServiceCallbacks.aidl | 8 +- .../service/media/MediaBrowserService.java | 9 +- 11 files changed, 245 insertions(+), 43 deletions(-) create mode 100644 packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl create mode 100644 packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp index e797e149fe..d89eb773c1 100644 --- a/packages/MediaComponents/apex/Android.bp +++ b/packages/MediaComponents/apex/Android.bp @@ -9,6 +9,8 @@ filegroup { // "Refusing to generate code with unstructured parcelables." "java/android/media/MediaDescription.aidl", "java/android/media/MediaMetadata.aidl", + // TODO(insun): check why MediaParceledListSlice.aidl should be added here + "java/android/media/MediaParceledListSlice.aidl", "java/android/media/Rating.aidl", "java/android/media/browse/MediaBrowser.aidl", "java/android/media/session/MediaSession.aidl", diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl new file mode 100644 index 0000000000..228ea9ca31 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl @@ -0,0 +1,19 @@ +/* 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. + */ + +package android.media; + +/** @hide */ +parcelable MediaParceledListSlice; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java new file mode 100644 index 0000000000..ec3fdb71e2 --- /dev/null +++ b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java @@ -0,0 +1,202 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.UnsupportedAppUsage; +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Transfer a large list of objects across an IPC. Splits into multiple transactions if needed. + * Note: Only use classes declared final in order to avoid subclasses overriding reading/writing + * parcel logic. + * + * TODO: Add test for sending large data + * @hide + */ +public class MediaParceledListSlice implements Parcelable { + private static final String TAG = "MediaParceledListSlice"; + private static final boolean DEBUG = false; + + private static final int MAX_IPC_SIZE = 64 * 1024; // IBinder.MAX_IPC_SIZE + + final List mList; + + public MediaParceledListSlice(List list) { + if (list == null) { + throw new IllegalArgumentException("list shouldn't be null"); + } + mList = list; + } + + MediaParceledListSlice(Parcel p) { + final int itemCount = p.readInt(); + mList = new ArrayList<>(itemCount); + if (DEBUG) { + Log.d(TAG, "Retrieving " + itemCount + " items"); + } + if (itemCount <= 0) { + return; + } + + int i = 0; + while (i < itemCount) { + if (p.readInt() == 0) { + break; + } + + final T parcelable = p.readParcelable(null); + mList.add(parcelable); + + if (DEBUG) { + Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1)); + } + i++; + } + if (i >= itemCount) { + return; + } + final IBinder retriever = p.readStrongBinder(); + while (i < itemCount) { + if (DEBUG) { + Log.d(TAG, "Reading more @" + i + " of " + itemCount + ": retriever=" + retriever); + } + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInt(i); + try { + retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failure retrieving array; only received " + i + " of " + itemCount, e); + return; + } + while (i < itemCount && reply.readInt() != 0) { + final T parcelable = reply.readParcelable(null); + mList.add(parcelable); + + if (DEBUG) { + Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1)); + } + i++; + } + reply.recycle(); + data.recycle(); + } + } + + public List getList() { + return mList; + } + + /** + * Write this to another Parcel. Note that this discards the internal Parcel + * and should not be used anymore. This is so we can pass this to a Binder + * where we won't have a chance to call recycle on this. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + final int itemCount = mList.size(); + dest.writeInt(itemCount); + if (DEBUG) { + Log.d(TAG, "Writing " + itemCount + " items"); + } + if (itemCount > 0) { + int i = 0; + while (i < itemCount && dest.dataSize() < MAX_IPC_SIZE) { + dest.writeInt(1); + + final T parcelable = mList.get(i); + dest.writeParcelable(parcelable, flags); + + if (DEBUG) { + Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); + } + i++; + } + if (i < itemCount) { + dest.writeInt(0); + Binder retriever = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + int i = data.readInt(); + if (DEBUG) { + Log.d(TAG, "Writing more @" + i + " of " + itemCount); + } + while (i < itemCount && reply.dataSize() < MAX_IPC_SIZE) { + reply.writeInt(1); + + final T parcelable = mList.get(i); + reply.writeParcelable(parcelable, flags); + + if (DEBUG) { + Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); + } + i++; + } + if (i < itemCount) { + if (DEBUG) { + Log.d(TAG, "Breaking @" + i + " of " + itemCount); + } + reply.writeInt(0); + } + return true; + } + }; + if (DEBUG) { + Log.d(TAG, "Breaking @" + i + " of " + itemCount + ": retriever=" + retriever); + } + dest.writeStrongBinder(retriever); + } + } + } + + @Override + public int describeContents() { + int contents = 0; + final List list = getList(); + for (int i = 0; i < list.size(); i++) { + contents |= list.get(i).describeContents(); + } + return contents; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public MediaParceledListSlice createFromParcel(Parcel in) { + return new MediaParceledListSlice(in); + } + + @Override + public MediaParceledListSlice[] newArray(int size) { + return new MediaParceledListSlice[size]; + } + }; +} diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java index 4e091ad48d..b1b14c6e4b 100644 --- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java +++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java @@ -23,8 +23,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -//import android.content.pm.ParceledListSlice; import android.media.MediaDescription; +import android.media.MediaParceledListSlice; import android.media.session.MediaController; import android.media.session.MediaSession; import android.os.Binder; @@ -652,10 +652,8 @@ public final class MediaBrowser { }); } - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, - final String parentId, final ParceledListSlice list, final Bundle options) { + final String parentId, final MediaParceledListSlice list, final Bundle options) { mHandler.post(new Runnable() { @Override public void run() { @@ -699,7 +697,6 @@ public final class MediaBrowser { } }); } - */ /** * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. @@ -1109,22 +1106,19 @@ public final class MediaBrowser { } } - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* @Override - public void onLoadChildren(String parentId, ParceledListSlice list) { + public void onLoadChildren(String parentId, MediaParceledListSlice list) { onLoadChildrenWithOptions(parentId, list, null); } @Override - public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list, + public void onLoadChildrenWithOptions(String parentId, MediaParceledListSlice list, final Bundle options) { MediaBrowser mediaBrowser = mMediaBrowser.get(); if (mediaBrowser != null) { mediaBrowser.onLoadChildren(this, parentId, list, options); } } - */ } private static class Subscription { diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl index cbd93cb817..c5b376c80a 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl @@ -16,9 +16,9 @@ package android.media.session; import android.app.PendingIntent; -import android.content.pm.ParceledListSlice; //import android.media.AudioAttributes; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.session.ISessionController; import android.media.session.PlaybackState; import android.media.session.MediaSession; @@ -41,8 +41,7 @@ interface ISession { // These commands are for the TransportPerformer void setMetadata(in MediaMetadata metadata); void setPlaybackState(in PlaybackState state); - //TODO(b/119750807): Resolve hidden API usage ParceledListSlice. - //void setQueue(in ParceledListSlice queue); + void setQueue(in MediaParceledListSlice queue); void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl index 031a388722..74897f73e1 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl @@ -17,8 +17,8 @@ package android.media.session; import android.app.PendingIntent; import android.content.Intent; -//import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.session.ISessionControllerCallback; import android.media.session.MediaSession; @@ -81,8 +81,7 @@ interface ISessionController { String action, in Bundle args); MediaMetadata getMetadata(); PlaybackState getPlaybackState(); - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - //ParceledListSlice getQueue(); + MediaParceledListSlice getQueue(); CharSequence getQueueTitle(); Bundle getExtras(); int getRatingType(); diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl index 173504b946..f5cc4f6efa 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl @@ -15,8 +15,8 @@ package android.media.session; -//import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; import android.media.session.MediaSession; @@ -32,8 +32,7 @@ oneway interface ISessionControllerCallback { // These callbacks are for the TransportController void onPlaybackStateChanged(in PlaybackState state); void onMetadataChanged(in MediaMetadata metadata); - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - //void onQueueChanged(in ParceledListSlice queue); + void onQueueChanged(in MediaParceledListSlice queue); void onQueueTitleChanged(CharSequence title); void onExtrasChanged(in Bundle extras); void onVolumeInfoChanged(in ParcelableVolumeInfo info); diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 60f74ab071..8c3a013a2d 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -21,10 +21,10 @@ import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; -//import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.VolumeProvider; import android.net.Uri; @@ -243,17 +243,14 @@ public final class MediaController { * @return The current play queue or null. */ public @Nullable List getQueue() { - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* try { - ParceledListSlice queue = mSessionBinder.getQueue(); + MediaParceledListSlice queue = mSessionBinder.getQueue(); if (queue != null) { return queue.getList(); } } catch (RemoteException e) { Log.wtf(TAG, "Error calling getQueue.", e); } - */ return null; } @@ -1102,10 +1099,8 @@ public final class MediaController { } } - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* @Override - public void onQueueChanged(ParceledListSlice parceledQueue) { + public void onQueueChanged(MediaParceledListSlice parceledQueue) { List queue = parceledQueue == null ? null : parceledQueue .getList(); MediaController controller = mController.get(); @@ -1113,7 +1108,6 @@ public final class MediaController { controller.postMessage(MSG_UPDATE_QUEUE, queue, null); } } - */ @Override public void onQueueTitleChanged(CharSequence title) { diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index 1ae1d2c782..e5eff95687 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -24,10 +24,10 @@ import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -//import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; +import android.media.MediaParceledListSlice; import android.media.Rating; import android.media.VolumeProvider; import android.net.Uri; @@ -461,14 +461,11 @@ public final class MediaSession { * @param queue A list of items in the play queue. */ public void setQueue(@Nullable List queue) { - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* try { - mBinder.setQueue(queue == null ? null : new ParceledListSlice(queue)); + mBinder.setQueue(queue == null ? null : new MediaParceledListSlice(queue)); } catch (RemoteException e) { Log.wtf("Dead object in setQueue.", e); } - */ } /** diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl index bcc282620d..8dc480d6bf 100644 --- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl +++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl @@ -2,8 +2,8 @@ package android.service.media; -//import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; +import android.media.MediaParceledListSlice; import android.media.session.MediaSession; import android.os.Bundle; @@ -22,7 +22,7 @@ oneway interface IMediaBrowserServiceCallbacks { */ void onConnect(String root, in MediaSession.Token session, in Bundle extras); void onConnectFailed(); - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - //void onLoadChildren(String mediaId, in ParceledListSlice list); - //void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options); + void onLoadChildren(String mediaId, in MediaParceledListSlice list); + void onLoadChildrenWithOptions(String mediaId, in MediaParceledListSlice list, + in Bundle options); } diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java index fa7696e827..a66ec352c2 100644 --- a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java +++ b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java @@ -25,7 +25,7 @@ import android.annotation.UnsupportedAppUsage; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; -//import android.content.pm.ParceledListSlice; +import android.media.MediaParceledListSlice; import android.media.browse.MediaBrowser; import android.media.browse.MediaBrowserUtils; import android.media.session.MediaSession; @@ -687,10 +687,8 @@ public abstract class MediaBrowserService extends Service { List filteredList = (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 ? applyOptions(list, options) : list; - //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice. - /* - final ParceledListSlice pls = - filteredList == null ? null : new ParceledListSlice<>(filteredList); + final MediaParceledListSlice pls = + filteredList == null ? null : new MediaParceledListSlice<>(filteredList); try { connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options); } catch (RemoteException ex) { @@ -698,7 +696,6 @@ public abstract class MediaBrowserService extends Service { Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId + " package=" + connection.pkg); } - */ } }; -- GitLab From f4de6114c20c3b3376806b359d527330f1891826 Mon Sep 17 00:00:00 2001 From: jiabin Date: Wed, 19 Dec 2018 12:40:08 -0800 Subject: [PATCH 0639/1530] Do not allow set sample rate when haptic channels exists. As haptic playback via audio require sample rate is the same as the HAL one, sample rate should not be changed after AudioTrack is created. Bug: 121271890 Test: Manually Change-Id: I1549017b3ca8523735190f62f8c9a4fb8b95e000 --- media/libaudioclient/AudioTrack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 1f6dd60c89..b444d2d154 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -952,7 +952,8 @@ status_t AudioTrack::setSampleRate(uint32_t rate) if (rate == mSampleRate) { return NO_ERROR; } - if (isOffloadedOrDirect_l() || (mFlags & AUDIO_OUTPUT_FLAG_FAST)) { + if (isOffloadedOrDirect_l() || (mFlags & AUDIO_OUTPUT_FLAG_FAST) + || (mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL)) { return INVALID_OPERATION; } if (mOutput == AUDIO_IO_HANDLE_NONE) { -- GitLab From c241b0dfc49014087123dae92f7341db8d30d443 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 28 Nov 2018 09:08:49 -0800 Subject: [PATCH 0640/1530] audo policy: add effects to record clients Add list of active effects on input streams and record clients for further reporting in AudioRecordingConfiguration. Bug: 111438757 Test: make and dumpsys Change-Id: I1ae00d2431c80b053f67e2b780f368d5a4822b01 --- .../include/AudioInputDescriptor.h | 10 ++- .../include/ClientDescriptor.h | 7 +- .../include/EffectDescriptor.h | 10 +-- .../src/AudioInputDescriptor.cpp | 49 ++++++++++++++ .../src/ClientDescriptor.cpp | 10 +++ .../src/EffectDescriptor.cpp | 65 +++++++++++++------ .../managerdefault/AudioPolicyManager.cpp | 27 ++++++++ .../managerdefault/AudioPolicyManager.h | 10 +-- 8 files changed, 153 insertions(+), 35 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 9f8b8c0580..afd599fd4e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -23,6 +23,7 @@ #include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "ClientDescriptor.h" +#include "EffectDescriptor.h" namespace android { @@ -62,7 +63,8 @@ public: bool isSoundTrigger() const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } - + void trackEffectEnabled(const sp &effect, bool enabled); + EffectDescriptorCollection getEnabledEffects() const; // implementation of AudioIODescriptorInterface audio_config_base_t getConfig() const override; audio_patch_handle_t getPatchHandle() const override; @@ -86,6 +88,9 @@ public: RecordClientVector clientsList(bool activeOnly = false, audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const; + // implementation of ClientMapHandler + void addClient(const sp &client) override; + private: void updateClientRecordingConfiguration(int event, const sp& client); @@ -101,6 +106,7 @@ public: SortedVector mPreemptedSessions; AudioPolicyClientInterface * const mClientInterface; int32_t mGlobalActiveCount = 0; // non-client-specific activity ref count + EffectDescriptorCollection mEnabledEffects; }; class AudioInputCollection : @@ -126,6 +132,8 @@ public: sp getInputForClient(audio_port_handle_t portId); + void trackEffectEnabled(const sp &effect, bool enabled); + void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 986d109122..a1870291f2 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -28,6 +28,7 @@ #include #include #include "AudioPatch.h" +#include "EffectDescriptor.h" #include "RoutingStrategy.h" namespace android { @@ -119,13 +120,15 @@ public: void setAppState(app_state_t appState) { mAppState = appState; } app_state_t appState() { return mAppState; } bool isSilenced() const { return mAppState == APP_STATE_IDLE; } + void trackEffectEnabled(const sp &effect, bool enabled); + EffectDescriptorCollection getEnabledEffects() const { return mEnabledEffects; } private: const audio_source_t mSource; const audio_input_flags_t mFlags; const bool mIsSoundTrigger; app_state_t mAppState; - + EffectDescriptorCollection mEnabledEffects; }; class SourceClientDescriptor: public TrackClientDescriptor @@ -172,7 +175,7 @@ public: virtual ~ClientMapHandler() = default; // Track client management - void addClient(const sp &client) { + virtual void addClient(const sp &client) { const audio_port_handle_t portId = client->portId(); LOG_ALWAYS_FATAL_IF(!mClients.emplace(portId, client).second, "%s(%d): attempting to add client that already exists", __func__, portId); diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h index 9fa74861b2..2dc33ab936 100644 --- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h @@ -25,12 +25,12 @@ namespace android { - class EffectDescriptor : public RefBase { public: - void dump(String8 *dst) const; + void dump(String8 *dst, int spaces = 0) const; + int mId; // effect unique ID int mIo; // io the effect is attached to routing_strategy mStrategy; // routing strategy the effect is associated to int mSession; // audio session the effect is on @@ -46,12 +46,14 @@ public: status_t registerEffect(const effect_descriptor_t *desc, audio_io_handle_t io, uint32_t strategy, int session, int id); status_t unregisterEffect(int id); + sp getEffect(int id) const; status_t setEffectEnabled(int id, bool enabled); + bool isEffectEnabled(int id) const; uint32_t getMaxEffectsCpuLoad() const; uint32_t getMaxEffectsMemory() const; - bool isNonOffloadableEffectEnabled(); + bool isNonOffloadableEffectEnabled() const; - void dump(String8 *dst) const; + void dump(String8 *dst, int spaces = 0, bool verbose = true) const; private: status_t setEffectEnabled(const sp &effectDesc, bool enabled); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 559274f8c3..409e7d8c9e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -269,6 +269,16 @@ void AudioInputDescriptor::close() } } +void AudioInputDescriptor::addClient(const sp &client) { + ClientMapHandler::addClient(client); + + for (size_t i = 0; i < mEnabledEffects.size(); i++) { + if (mEnabledEffects.valueAt(i)->mSession == client->session()) { + client->trackEffectEnabled(mEnabledEffects.valueAt(i), true); + } + } +} + void AudioInputDescriptor::setClientActive(const sp& client, bool active) { LOG_ALWAYS_FATAL_IF(getClient(client->portId()) == nullptr, @@ -345,6 +355,33 @@ RecordClientVector AudioInputDescriptor::clientsList(bool activeOnly, audio_sour return clients; } +void AudioInputDescriptor::trackEffectEnabled(const sp &effect, + bool enabled) +{ + RecordClientVector clients = getClientsForSession((audio_session_t)effect->mSession); + for (const auto& client : clients) { + client->trackEffectEnabled(effect, enabled); + } + + if (enabled) { + mEnabledEffects.replaceValueFor(effect->mId, effect); + } else { + mEnabledEffects.removeItem(effect->mId); + } +} + +EffectDescriptorCollection AudioInputDescriptor::getEnabledEffects() const +{ + EffectDescriptorCollection enabledEffects; + // report effects for highest priority active source as applied to all clients + RecordClientVector clients = + clientsList(true /*activeOnly*/, source(), false /*preferredDeviceOnly*/); + if (clients.size() > 0) { + enabledEffects = clients[0]->getEnabledEffects(); + } + return enabledEffects; +} + void AudioInputDescriptor::dump(String8 *dst) const { dst->appendFormat(" ID: %d\n", getId()); @@ -352,6 +389,7 @@ void AudioInputDescriptor::dump(String8 *dst) const dst->appendFormat(" Format: %d\n", mFormat); dst->appendFormat(" Channels: %08x\n", mChannelMask); dst->appendFormat(" Devices %08x\n", mDevice); + getEnabledEffects().dump(dst, 1 /*spaces*/, false /*verbose*/); dst->append(" AudioRecord Clients:\n"); ClientMapHandler::dump(dst); dst->append("\n"); @@ -424,6 +462,17 @@ sp AudioInputCollection::getInputForClient(audio_port_hand return 0; } +void AudioInputCollection::trackEffectEnabled(const sp &effect, + bool enabled) +{ + for (size_t i = 0; i < size(); i++) { + sp inputDesc = valueAt(i); + if (inputDesc->mIoHandle == effect->mIo) { + return inputDesc->trackEffectEnabled(effect, enabled); + } + } +} + void AudioInputCollection::dump(String8 *dst) const { dst->append("\nInputs dump:\n"); diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 815612d000..82d64c9e4d 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -63,10 +63,20 @@ std::string TrackClientDescriptor::toShortString() const return ss.str(); } +void RecordClientDescriptor::trackEffectEnabled(const sp &effect, bool enabled) +{ + if (enabled) { + mEnabledEffects.replaceValueFor(effect->mId, effect); + } else { + mEnabledEffects.removeItem(effect->mId); + } +} + void RecordClientDescriptor::dump(String8 *dst, int spaces, int index) const { ClientDescriptor::dump(dst, spaces, index); dst->appendFormat("%*s- Source: %d flags: %08x\n", spaces, "", mSource, mFlags); + mEnabledEffects.dump(dst, spaces + 2 /*spaces*/, false /*verbose*/); } SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp index 8bbb79852f..40c49e7e9c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp @@ -22,13 +22,13 @@ namespace android { -void EffectDescriptor::dump(String8 *dst) const +void EffectDescriptor::dump(String8 *dst, int spaces) const { - dst->appendFormat(" I/O: %d\n", mIo); - dst->appendFormat(" Strategy: %d\n", mStrategy); - dst->appendFormat(" Session: %d\n", mSession); - dst->appendFormat(" Name: %s\n", mDesc.name); - dst->appendFormat(" %s\n", mEnabled ? "Enabled" : "Disabled"); + dst->appendFormat("%*sI/O: %d\n", spaces, "", mIo); + dst->appendFormat("%*sStrategy: %d\n", spaces, "", mStrategy); + dst->appendFormat("%*sSession: %d\n", spaces, "", mSession); + dst->appendFormat("%*sName: %s\n", spaces, "", mDesc.name); + dst->appendFormat("%*s%s\n", spaces, "", mEnabled ? "Enabled" : "Disabled"); } EffectDescriptorCollection::EffectDescriptorCollection() : @@ -45,6 +45,11 @@ status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *d int session, int id) { + if (getEffect(id) != nullptr) { + ALOGW("%s effect %s already registered", __FUNCTION__, desc->name); + return INVALID_OPERATION; + } + if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", desc->name, desc->memoryUsage); @@ -60,6 +65,7 @@ status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *d sp effectDesc = new EffectDescriptor(); memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); + effectDesc->mId = id; effectDesc->mIo = io; effectDesc->mStrategy = static_cast(strategy); effectDesc->mSession = session; @@ -70,17 +76,22 @@ status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *d return NO_ERROR; } -status_t EffectDescriptorCollection::unregisterEffect(int id) +sp EffectDescriptorCollection::getEffect(int id) const { ssize_t index = indexOfKey(id); if (index < 0) { - ALOGW("unregisterEffect() unknown effect ID %d", id); - return INVALID_OPERATION; + return nullptr; } + return valueAt(index); +} - sp effectDesc = valueAt(index); - - setEffectEnabled(effectDesc, false); +status_t EffectDescriptorCollection::unregisterEffect(int id) +{ + sp effectDesc = getEffect(id); + if (effectDesc == nullptr) { + ALOGW("%s unknown effect ID %d", __FUNCTION__, id); + return INVALID_OPERATION; + } if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) { ALOGW("unregisterEffect() memory %d too big for total %d", @@ -107,6 +118,14 @@ status_t EffectDescriptorCollection::setEffectEnabled(int id, bool enabled) return setEffectEnabled(valueAt(index), enabled); } +bool EffectDescriptorCollection::isEffectEnabled(int id) const +{ + ssize_t index = indexOfKey(id); + if (index < 0) { + return false; + } + return valueAt(index)->mEnabled; +} status_t EffectDescriptorCollection::setEffectEnabled(const sp &effectDesc, bool enabled) @@ -138,7 +157,7 @@ status_t EffectDescriptorCollection::setEffectEnabled(const sp return NO_ERROR; } -bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() +bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() const { for (size_t i = 0; i < size(); i++) { sp effectDesc = valueAt(i); @@ -162,15 +181,21 @@ uint32_t EffectDescriptorCollection::getMaxEffectsMemory() const return MAX_EFFECTS_MEMORY; } -void EffectDescriptorCollection::dump(String8 *dst) const +void EffectDescriptorCollection::dump(String8 *dst, int spaces, bool verbose) const { - dst->appendFormat( - "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB, Max memory used: %d KB\n", - (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory, mTotalEffectsMemoryMaxUsed); - dst->append("Registered effects:\n"); + if (verbose) { + dst->appendFormat( + "\n%*sTotal Effects CPU: %f MIPS, " + "Total Effects memory: %d KB, Max memory used: %d KB\n", + spaces, "", + (float) mTotalEffectsCpuLoad / 10, + mTotalEffectsMemory, + mTotalEffectsMemoryMaxUsed); + } + dst->appendFormat("%*sEffects:\n", spaces, ""); for (size_t i = 0; i < size(); i++) { - dst->appendFormat("- Effect %d dump:\n", keyAt(i)); - valueAt(i)->dump(dst); + dst->appendFormat("%*s- Effect %d:\n", spaces, "", keyAt(i)); + valueAt(i)->dump(dst, spaces + 2); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 64a2b8a506..3686f2d484 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2423,6 +2423,33 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, return mEffects.registerEffect(desc, io, strategy, session, id); } +status_t AudioPolicyManager::unregisterEffect(int id) +{ + if (mEffects.getEffect(id) == nullptr) { + return INVALID_OPERATION; + } + + if (mEffects.isEffectEnabled(id)) { + ALOGW("%s effect %d enabled", __FUNCTION__, id); + setEffectEnabled(id, false); + } + return mEffects.unregisterEffect(id); +} + +status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) +{ + sp effect = mEffects.getEffect(id); + if (effect == nullptr) { + return INVALID_OPERATION; + } + + status_t status = mEffects.setEffectEnabled(id, enabled); + if (status == NO_ERROR) { + mInputs.trackEffectEnabled(effect, enabled); + } + return status; +} + bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { bool active = false; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 86993d4195..35dd87ca47 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -164,14 +164,8 @@ public: uint32_t strategy, int session, int id); - virtual status_t unregisterEffect(int id) - { - return mEffects.unregisterEffect(id); - } - virtual status_t setEffectEnabled(int id, bool enabled) - { - return mEffects.setEffectEnabled(id, enabled); - } + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; // return whether a stream is playing remotely, override to change the definition of -- GitLab From be9768e8eff47385c32bcc7f9c3b82c27058b3cc Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 19 Dec 2018 13:10:35 -0800 Subject: [PATCH 0641/1530] Fix memory leaks Bug: 111407253 Test: manual, libmemunreachable Change-Id: I69ad7b48498346925dd02c10b206035c0ae49305 --- media/extractors/midi/MidiExtractor.cpp | 1 + media/extractors/mpeg2/MPEG2PSExtractor.cpp | 1 + media/extractors/mpeg2/MPEG2TSExtractor.cpp | 2 +- media/libmedia/MidiIoWrapper.cpp | 17 +++++------------ media/libmedia/include/media/MidiIoWrapper.h | 4 ++-- media/libmediaextractor/MediaBufferGroup.cpp | 1 + .../include/media/stagefright/MediaBufferBase.h | 5 ++++- media/libstagefright/MediaTrack.cpp | 1 + 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index 43f394c63a..1ddcfbbfe8 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -279,6 +279,7 @@ MidiExtractor::~MidiExtractor() ALOGV("MidiExtractor dtor"); AMediaFormat_delete(mFileMetadata); AMediaFormat_delete(mTrackMetadata); + delete mEngine; } size_t MidiExtractor::countTracks() diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index b60fc4e864..d058637c33 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -734,6 +734,7 @@ media_status_t MPEG2PSExtractor::Track::read( if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } + mbuf->release(); return AMEDIA_OK; } diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index fc066ee882..11c3787dab 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -224,7 +224,7 @@ media_status_t MPEG2TSSource::read( if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } - + mbuf->release(); return AMEDIA_OK; } diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp index 1150d61c77..d8ef9cfd56 100644 --- a/media/libmedia/MidiIoWrapper.cpp +++ b/media/libmedia/MidiIoWrapper.cpp @@ -50,24 +50,15 @@ MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) { mDataSource = nullptr; } -MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) { - ALOGV("MidiIoWrapper(DataSource)"); - mFd = -1; - mDataSource = source; - off64_t l; - if (mDataSource->getSize(&l) == OK) { - mLength = l; - } else { - mLength = 0; - } -} - class DataSourceUnwrapper : public DataSourceBase { public: explicit DataSourceUnwrapper(CDataSource *csource) { mSource = csource; } + + virtual ~DataSourceUnwrapper() {} + virtual status_t initCheck() const { return OK; } // Returns the number of bytes read, or -1 on failure. It's not an error if @@ -98,6 +89,7 @@ private: MidiIoWrapper::MidiIoWrapper(CDataSource *csource) { ALOGV("MidiIoWrapper(CDataSource)"); mFd = -1; + mBase = 0; mDataSource = new DataSourceUnwrapper(csource); off64_t l; if (mDataSource->getSize(&l) == OK) { @@ -112,6 +104,7 @@ MidiIoWrapper::~MidiIoWrapper() { if (mFd >= 0) { close(mFd); } + delete mDataSource; } int MidiIoWrapper::readAt(void *buffer, int offset, int size) { diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h index 6309dda53b..b19d49e751 100644 --- a/media/libmedia/include/media/MidiIoWrapper.h +++ b/media/libmedia/include/media/MidiIoWrapper.h @@ -24,12 +24,12 @@ namespace android { struct CDataSource; +class DataSourceUnwrapper; class MidiIoWrapper { public: explicit MidiIoWrapper(const char *path); explicit MidiIoWrapper(int fd, off64_t offset, int64_t size); - explicit MidiIoWrapper(DataSourceBase *source); explicit MidiIoWrapper(CDataSource *csource); ~MidiIoWrapper(); @@ -43,7 +43,7 @@ private: int mFd; off64_t mBase; int64_t mLength; - DataSourceBase *mDataSource; + DataSourceUnwrapper *mDataSource; EAS_FILE mEasFile; }; diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp index e88ed5adca..84ff9a602d 100644 --- a/media/libmediaextractor/MediaBufferGroup.cpp +++ b/media/libmediaextractor/MediaBufferGroup.cpp @@ -125,6 +125,7 @@ MediaBufferGroup::~MediaBufferGroup() { buffer->release(); } delete mInternal; + delete mWrapper; } void MediaBufferGroup::add_buffer(MediaBufferBase *buffer) { diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h index 3682368b25..e357d3e932 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h @@ -75,7 +75,10 @@ public: virtual int localRefcount() const = 0; virtual int remoteRefcount() const = 0; - virtual ~MediaBufferBase() {}; + virtual ~MediaBufferBase() { + delete mWrapper; + delete mFormat; + }; CMediaBufferV3 *wrap() { if (mWrapper) { diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 1e49f6ae31..b15f3bb31a 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -166,6 +166,7 @@ MediaTrackCUnwrapperV3::MediaTrackCUnwrapperV3(CMediaTrackV3 *cmediatrack3) { MediaTrackCUnwrapperV3::~MediaTrackCUnwrapperV3() { wrapper->free(wrapper->data); free(wrapper); + delete bufferGroup; } status_t MediaTrackCUnwrapperV3::start() { -- GitLab From a9f86651eaaf2f702b3759ec38dbb64ca8858356 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 28 Nov 2018 17:23:11 -0800 Subject: [PATCH 0642/1530] audo policy: more info in onRecordingConfigurationUpdate Report more information in onRecordingConfigurationUpdate() callback: - For client: - Port ID - Enabled effects - Silenced by policy - For stream: - Active effects - Active audio source Bug: 111438757 Test: make Change-Id: I34c3d0ee6251576de7d793bfaa2bc2c34efa2319 --- media/libaudioclient/AudioSystem.cpp | 14 ++-- .../IAudioPolicyServiceClient.cpp | 52 +++++++++++++-- .../include/media/AudioSystem.h | 21 ++++-- .../include/media/IAudioPolicyServiceClient.h | 9 ++- services/audiopolicy/AudioPolicyInterface.h | 11 ++-- .../include/AudioInputDescriptor.h | 2 + .../src/AudioInputDescriptor.cpp | 53 ++++++++++++--- .../managerdefault/AudioPolicyManager.cpp | 8 +-- .../service/AudioPolicyClientImpl.cpp | 13 ++-- .../service/AudioPolicyService.cpp | 64 ++++++++++++------ .../audiopolicy/service/AudioPolicyService.h | 66 +++++++++++++------ .../audiopolicy/tests/AudioPolicyTestClient.h | 13 ++-- 12 files changed, 242 insertions(+), 84 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 3e91717d23..dc7531ceb0 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1388,9 +1388,14 @@ void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate( } void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate( - int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle) { + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { record_config_callback cb = NULL; { Mutex::Autolock _l(AudioSystem::gLock); @@ -1398,7 +1403,8 @@ void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate( } if (cb != NULL) { - cb(event, clientInfo, clientConfig, deviceConfig, patchHandle); + cb(event, clientInfo, clientConfig, clientEffects, + deviceConfig, effects, patchHandle, source); } } diff --git a/media/libaudioclient/IAudioPolicyServiceClient.cpp b/media/libaudioclient/IAudioPolicyServiceClient.cpp index ad7f1dea55..1f9eab7fa8 100644 --- a/media/libaudioclient/IAudioPolicyServiceClient.cpp +++ b/media/libaudioclient/IAudioPolicyServiceClient.cpp @@ -52,12 +52,37 @@ inline void readRecordClientInfoFromParcel(const Parcel& data, record_client_inf clientInfo->uid = (uid_t) data.readUint32(); clientInfo->session = (audio_session_t) data.readInt32(); clientInfo->source = (audio_source_t) data.readInt32(); + data.read(&clientInfo->port_id, sizeof(audio_port_handle_t)); + clientInfo->silenced = data.readBool(); } -inline void writeRecordClientInfoFromParcel(Parcel& data, const record_client_info_t *clientInfo) { +inline void writeRecordClientInfoToParcel(Parcel& data, const record_client_info_t *clientInfo) { data.writeUint32((uint32_t) clientInfo->uid); data.writeInt32((int32_t) clientInfo->session); data.writeInt32((int32_t) clientInfo->source); + data.write(&clientInfo->port_id, sizeof(audio_port_handle_t)); + data.writeBool(clientInfo->silenced); +} + +inline void readEffectVectorFromParcel(const Parcel& data, + std::vector *effects) { + int32_t numEffects = data.readInt32(); + for (int32_t i = 0; i < numEffects; i++) { + effect_descriptor_t effect; + if (data.read(&effect, sizeof(effect_descriptor_t)) != NO_ERROR) { + break; + } + (*effects).push_back(effect); + } +} + +inline void writeEffectVectorToParcel(Parcel& data, std::vector effects) { + data.writeUint32((uint32_t) effects.size()); + for (const auto& effect : effects) { + if (data.write(&effect, sizeof(effect_descriptor_t)) != NO_ERROR) { + break; + } + } } // ---------------------------------------------------------------------- @@ -92,16 +117,24 @@ public: remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); } - void onRecordingConfigurationUpdate(int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle) { + void onRecordingConfigurationUpdate(int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor()); data.writeInt32(event); - writeRecordClientInfoFromParcel(data, clientInfo); + writeRecordClientInfoToParcel(data, clientInfo); writeAudioConfigBaseToParcel(data, clientConfig); + writeEffectVectorToParcel(data, clientEffects); writeAudioConfigBaseToParcel(data, deviceConfig); + writeEffectVectorToParcel(data, effects); data.writeInt32(patchHandle); + data.writeInt32((int32_t) source); remote()->transact(RECORDING_CONFIGURATION_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); } }; @@ -139,10 +172,15 @@ status_t BnAudioPolicyServiceClient::onTransact( audio_config_base_t deviceConfig; readRecordClientInfoFromParcel(data, &clientInfo); readAudioConfigBaseFromParcel(data, &clientConfig); + std::vector clientEffects; + readEffectVectorFromParcel(data, &clientEffects); readAudioConfigBaseFromParcel(data, &deviceConfig); + std::vector effects; + readEffectVectorFromParcel(data, &effects); audio_patch_handle_t patchHandle = (audio_patch_handle_t) data.readInt32(); - onRecordingConfigurationUpdate(event, &clientInfo, &clientConfig, &deviceConfig, - patchHandle); + audio_source_t source = (audio_source_t) data.readInt32(); + onRecordingConfigurationUpdate(event, &clientInfo, &clientConfig, clientEffects, + &deviceConfig, effects, patchHandle, source); return NO_ERROR; } break; default: diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 74156caf9a..b0da5b8e03 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -35,9 +35,14 @@ namespace android { typedef void (*audio_error_callback)(status_t err); typedef void (*dynamic_policy_callback)(int event, String8 regId, int val); -typedef void (*record_config_callback)(int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle); +typedef void (*record_config_callback)(int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); class IAudioFlinger; class IAudioPolicyService; @@ -448,9 +453,13 @@ private: virtual void onAudioPatchListUpdate(); virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); virtual void onRecordingConfigurationUpdate(int event, - const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle); + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); private: Mutex mLock; diff --git a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h index e0d2495fad..b3c0381db1 100644 --- a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h +++ b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h @@ -17,10 +17,12 @@ #ifndef ANDROID_IAUDIOPOLICYSERVICECLIENT_H #define ANDROID_IAUDIOPOLICYSERVICECLIENT_H +#include #include #include #include +#include namespace android { @@ -30,6 +32,8 @@ struct record_client_info { uid_t uid; audio_session_t session; audio_source_t source; + audio_port_handle_t port_id; + bool silenced; }; typedef struct record_client_info record_client_info_t; @@ -51,8 +55,11 @@ public: virtual void onRecordingConfigurationUpdate(int event, const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig, + std::vector clientEffects, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle) = 0; + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) = 0; }; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index ea6389ca6b..ad12a904a9 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -332,10 +332,13 @@ public: virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0; virtual void onRecordingConfigurationUpdate(int event, - const record_client_info_t *clientInfo, - const struct audio_config_base *clientConfig, - const struct audio_config_base *deviceConfig, - audio_patch_handle_t patchHandle) = 0; + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) = 0; }; extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index afd599fd4e..fa9ba0bf1b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -88,6 +88,8 @@ public: RecordClientVector clientsList(bool activeOnly = false, audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const; + void setAppState(uid_t uid, app_state_t state); + // implementation of ClientMapHandler void addClient(const sp &client) override; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 409e7d8c9e..0bc88a5dcb 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -322,11 +322,26 @@ void AudioInputDescriptor::updateClientRecordingConfiguration( int event, const sp& client) { const audio_config_base_t sessionConfig = client->config(); - const record_client_info_t recordClientInfo{client->uid(), client->session(), client->source()}; + const record_client_info_t recordClientInfo{client->uid(), client->session(), + client->source(), client->portId(), + client->isSilenced()}; const audio_config_base_t config = getConfig(); - mClientInterface->onRecordingConfigurationUpdate(event, - &recordClientInfo, &sessionConfig, - &config, mPatchHandle); + + std::vector clientEffects; + EffectDescriptorCollection effectsList = client->getEnabledEffects(); + for (size_t i = 0; i < effectsList.size(); i++) { + clientEffects.push_back(effectsList.valueAt(i)->mDesc); + } + + std::vector effects; + effectsList = getEnabledEffects(); + for (size_t i = 0; i < effectsList.size(); i++) { + effects.push_back(effectsList.valueAt(i)->mDesc); + } + + mClientInterface->onRecordingConfigurationUpdate(event, &recordClientInfo, &sessionConfig, + clientEffects, &config, effects, + mPatchHandle, source()); } RecordClientVector AudioInputDescriptor::getClientsForSession( @@ -358,16 +373,22 @@ RecordClientVector AudioInputDescriptor::clientsList(bool activeOnly, audio_sour void AudioInputDescriptor::trackEffectEnabled(const sp &effect, bool enabled) { - RecordClientVector clients = getClientsForSession((audio_session_t)effect->mSession); - for (const auto& client : clients) { - client->trackEffectEnabled(effect, enabled); - } - if (enabled) { mEnabledEffects.replaceValueFor(effect->mId, effect); } else { mEnabledEffects.removeItem(effect->mId); } + + RecordClientVector clients = getClientsForSession((audio_session_t)effect->mSession); + for (const auto& client : clients) { + sp clientEffect = client->getEnabledEffects().getEffect(effect->mId); + bool changed = (enabled && clientEffect == nullptr) + || (!enabled && clientEffect != nullptr); + client->trackEffectEnabled(effect, enabled); + if (changed && client->active()) { + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + } + } } EffectDescriptorCollection AudioInputDescriptor::getEnabledEffects() const @@ -382,6 +403,20 @@ EffectDescriptorCollection AudioInputDescriptor::getEnabledEffects() const return enabledEffects; } +void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state) { + RecordClientVector clients = clientsList(false /*activeOnly*/); + + for (const auto& client : clients) { + if (uid == client->uid()) { + bool wasSilenced = client->isSilenced(); + client->setAppState(state); + if (client->active() && wasSilenced != client->isSilenced()) { + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + } + } + } +} + void AudioInputDescriptor::dump(String8 *dst) const { dst->appendFormat(" ID: %d\n", getId()); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 3686f2d484..7b71752230 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3733,13 +3733,11 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { - Vector > activeInputs = mInputs.getActiveInputs(); - ALOGV("%s(uid:%d, state:%d)", __func__, uid, state); - for (size_t i = 0; i < activeInputs.size(); i++) { - sp activeDesc = activeInputs[i]; - RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/); + for (size_t i = 0; i < mInputs.size(); i++) { + sp inputDesc = mInputs.valueAt(i); + RecordClientVector clients = inputDesc->clientsList(false /*activeOnly*/); for (const auto& client : clients) { if (uid == client->uid()) { client->setAppState(state); diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index 21fffec518..d826192d9f 100644 --- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -209,12 +209,17 @@ void AudioPolicyService::AudioPolicyClient::onDynamicPolicyMixStateUpdate( } void AudioPolicyService::AudioPolicyClient::onRecordingConfigurationUpdate( - int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle) + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { mAudioPolicyService->onRecordingConfigurationUpdate(event, clientInfo, - clientConfig, deviceConfig, patchHandle); + clientConfig, clientEffects, deviceConfig, effects, patchHandle, source); } audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId(audio_unique_id_use_t use) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index f233971cf0..416817ff1e 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -215,22 +215,34 @@ void AudioPolicyService::doOnDynamicPolicyMixStateUpdate(const String8& regId, i } } -void AudioPolicyService::onRecordingConfigurationUpdate(int event, - const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle) +void AudioPolicyService::onRecordingConfigurationUpdate( + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { mOutputCommandThread->recordingConfigurationUpdateCommand(event, clientInfo, - clientConfig, deviceConfig, patchHandle); -} - -void AudioPolicyService::doOnRecordingConfigurationUpdate(int event, - const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle) + clientConfig, clientEffects, deviceConfig, effects, patchHandle, source); +} + +void AudioPolicyService::doOnRecordingConfigurationUpdate( + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { Mutex::Autolock _l(mNotificationClientsLock); for (size_t i = 0; i < mNotificationClients.size(); i++) { mNotificationClients.valueAt(i)->onRecordingConfigurationUpdate(event, clientInfo, - clientConfig, deviceConfig, patchHandle); + clientConfig, clientEffects, deviceConfig, effects, patchHandle, source); } } @@ -298,13 +310,18 @@ void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate( } void AudioPolicyService::NotificationClient::onRecordingConfigurationUpdate( - int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle) + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) { mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo, - clientConfig, deviceConfig, patchHandle); + clientConfig, clientEffects, deviceConfig, effects, patchHandle, source); } } @@ -1071,8 +1088,9 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() } mLock.unlock(); svc->doOnRecordingConfigurationUpdate(data->mEvent, &data->mClientInfo, - &data->mClientConfig, &data->mDeviceConfig, - data->mPatchHandle); + &data->mClientConfig, data->mClientEffects, + &data->mDeviceConfig, data->mEffects, + data->mPatchHandle, data->mSource); mLock.lock(); } break; default: @@ -1307,9 +1325,14 @@ void AudioPolicyService::AudioCommandThread::dynamicPolicyMixStateUpdateCommand( } void AudioPolicyService::AudioCommandThread::recordingConfigurationUpdateCommand( - int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle) + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source) { spcommand = new AudioCommand(); command->mCommand = RECORDING_CONFIGURATION_UPDATE; @@ -1317,8 +1340,11 @@ void AudioPolicyService::AudioCommandThread::recordingConfigurationUpdateCommand data->mEvent = event; data->mClientInfo = *clientInfo; data->mClientConfig = *clientConfig; + data->mClientEffects = clientEffects; data->mDeviceConfig = *deviceConfig; + data->mEffects = effects; data->mPatchHandle = patchHandle; + data->mSource = source; command->mParam = data; ALOGV("AudioCommandThread() adding recording configuration update event %d, source %d uid %u", event, clientInfo->source, clientInfo->uid); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 45d37dc022..c44d816cd3 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -240,12 +240,22 @@ public: void onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state); void doOnDynamicPolicyMixStateUpdate(const String8& regId, int32_t state); - void onRecordingConfigurationUpdate(int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle); - void doOnRecordingConfigurationUpdate(int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle); + void onRecordingConfigurationUpdate(int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); + void doOnRecordingConfigurationUpdate(int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); private: AudioPolicyService() ANDROID_API; @@ -409,13 +419,17 @@ private: void updateAudioPatchListCommand(); status_t setAudioPortConfigCommand(const struct audio_port_config *config, int delayMs); - void dynamicPolicyMixStateUpdateCommand(const String8& regId, int32_t state); + void dynamicPolicyMixStateUpdateCommand(const String8& regId, + int32_t state); void recordingConfigurationUpdateCommand( - int event, - const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle); + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); void insertCommand_l(AudioCommand *command, int delayMs = 0); private: class AudioCommandData; @@ -500,8 +514,11 @@ private: int mEvent; record_client_info_t mClientInfo; struct audio_config_base mClientConfig; + std::vector mClientEffects; struct audio_config_base mDeviceConfig; + std::vector mEffects; audio_patch_handle_t mPatchHandle; + audio_source_t mSource; }; Mutex mLock; @@ -605,9 +622,13 @@ private: virtual void onAudioPatchListUpdate(); virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); virtual void onRecordingConfigurationUpdate(int event, - const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle); + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use); @@ -625,12 +646,17 @@ private: void onAudioPortListUpdate(); void onAudioPatchListUpdate(); - void onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state); + void onDynamicPolicyMixStateUpdate(const String8& regId, + int32_t state); void onRecordingConfigurationUpdate( - int event, const record_client_info_t *clientInfo, - const audio_config_base_t *clientConfig, - const audio_config_base_t *deviceConfig, - audio_patch_handle_t patchHandle); + int event, + const record_client_info_t *clientInfo, + const audio_config_base_t *clientConfig, + std::vector clientEffects, + const audio_config_base_t *deviceConfig, + std::vector effects, + audio_patch_handle_t patchHandle, + audio_source_t source); void setAudioPortCallbacksEnabled(bool enabled); uid_t uid() { diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h index 2ff767570d..6ae354bdba 100644 --- a/services/audiopolicy/tests/AudioPolicyTestClient.h +++ b/services/audiopolicy/tests/AudioPolicyTestClient.h @@ -75,11 +75,14 @@ public: void onAudioPatchListUpdate() override { } audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t /*use*/) override { return 0; } void onDynamicPolicyMixStateUpdate(String8 /*regId*/, int32_t /*state*/) override { } - void onRecordingConfigurationUpdate(int /*event*/, - const record_client_info_t* /*clientInfo*/, - const struct audio_config_base* /*clientConfig*/, - const struct audio_config_base* /*deviceConfig*/, - audio_patch_handle_t /*patchHandle*/) override { } + void onRecordingConfigurationUpdate(int event __unused, + const record_client_info_t *clientInfo __unused, + const audio_config_base_t *clientConfig __unused, + std::vector clientEffects __unused, + const audio_config_base_t *deviceConfig __unused, + std::vector effects __unused, + audio_patch_handle_t patchHandle __unused, + audio_source_t source __unused) override { } }; } // namespace android -- GitLab From e78b676a4de85f228ae4d717f225c0cf00dbe767 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 19 Dec 2018 16:29:01 -0800 Subject: [PATCH 0643/1530] audio policy: fix flag matching in output selection Make sure we don't pick an output profile with specific functional requirements (e.g HW A/V sync) for a client not compatible with those requriements. Bug: 118704067 Test: Play Netflix and assistant simultaneously Change-Id: I6529deb6aa322be9f76433b1ead307a8ba367768 --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 64a2b8a506..5b1a990df9 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1313,6 +1313,10 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector outputDesc = mOutputs.valueFor(output); if (!outputDesc->isDuplicated()) { @@ -1336,6 +1340,10 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVectormProfile->getFlags())) { + continue; + } // if a valid format is specified, skip output if not compatible if (format != AUDIO_FORMAT_INVALID) { -- GitLab From c37b1908b6a054e4331465bda7d36497b8746001 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Tue, 18 Dec 2018 11:36:13 -0800 Subject: [PATCH 0644/1530] Video frame scheduler using public APIs Bug: 112555500 Test: MediaPlayer2Test Change-Id: Ie4b21964c761b4350b50b23271b28cf9dcd933ad --- .../nuplayer2/NuPlayer2Renderer.cpp | 6 +- .../nuplayer2/NuPlayer2Renderer.h | 4 +- .../nuplayer/NuPlayerRenderer.h | 4 +- media/libstagefright/Android.bp | 6 +- media/libstagefright/VideoFrameScheduler.cpp | 447 +---------------- media/libstagefright/VideoFrameScheduler2.cpp | 285 +++++++++++ .../VideoFrameSchedulerBase.cpp | 465 ++++++++++++++++++ .../media/stagefright/VideoFrameScheduler.h | 73 +-- .../media/stagefright/VideoFrameScheduler2.h | 46 ++ .../stagefright/VideoFrameSchedulerBase.h | 100 ++++ 10 files changed, 917 insertions(+), 519 deletions(-) create mode 100644 media/libstagefright/VideoFrameScheduler2.cpp create mode 100644 media/libstagefright/VideoFrameSchedulerBase.cpp create mode 100644 media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h create mode 100644 media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp index 9d9e179a03..e3c9b4b9e0 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -1436,7 +1436,7 @@ void NuPlayer2::Renderer::onQueueBuffer(const sp &msg) { if (mHasVideo) { if (mVideoScheduler == NULL) { - mVideoScheduler = new VideoFrameScheduler(); + mVideoScheduler = new VideoFrameScheduler2(); mVideoScheduler->init(); } } @@ -1779,7 +1779,7 @@ void NuPlayer2::Renderer::onResume() { void NuPlayer2::Renderer::onSetVideoFrameRate(float fps) { if (mVideoScheduler == NULL) { - mVideoScheduler = new VideoFrameScheduler(); + mVideoScheduler = new VideoFrameScheduler2(); } mVideoScheduler->init(fps); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h index 305af682de..484d9b73d8 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h @@ -28,7 +28,7 @@ namespace android { class JWakeLock; struct MediaClock; class MediaCodecBuffer; -struct VideoFrameScheduler; +struct VideoFrameSchedulerBase; struct NuPlayer2::Renderer : public AHandler { enum Flags { @@ -156,7 +156,7 @@ private: List mAudioQueue; List mVideoQueue; uint32_t mNumFramesWritten; - sp mVideoScheduler; + sp mVideoScheduler; bool mDrainAudioQueuePending; bool mDrainVideoQueuePending; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index a047975958..a521f62687 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -28,7 +28,7 @@ namespace android { class AWakeLock; struct MediaClock; class MediaCodecBuffer; -struct VideoFrameScheduler; +struct VideoFrameSchedulerBase; struct NuPlayer::Renderer : public AHandler { enum Flags { @@ -156,7 +156,7 @@ private: List mAudioQueue; List mVideoQueue; uint32_t mNumFramesWritten; - sp mVideoScheduler; + sp mVideoScheduler; bool mDrainAudioQueuePending; bool mDrainVideoQueuePending; diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 9aea88a599..ae9d0c0a57 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -133,6 +133,7 @@ cc_library_shared { "SurfaceUtils.cpp", "Utils.cpp", "ThrottledSource.cpp", + "VideoFrameSchedulerBase.cpp", "VideoFrameScheduler.cpp", ], @@ -237,7 +238,8 @@ cc_library_static { "MediaClock.cpp", "NdkUtils.cpp", "Utils.cpp", - "VideoFrameScheduler.cpp", + "VideoFrameSchedulerBase.cpp", + "VideoFrameScheduler2.cpp", "http/ClearMediaHTTP.cpp", ], @@ -247,10 +249,12 @@ cc_library_static { "libnetd_client", "libutils", "libstagefright_foundation", + "libandroid", ], static_libs: [ "libmedia_player2_util", + "libmedia2_jni_core", ], export_include_dirs: [ diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp index 9020fc1aaa..4e5b5e2851 100644 --- a/media/libstagefright/VideoFrameScheduler.cpp +++ b/media/libstagefright/VideoFrameScheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -19,8 +19,7 @@ #include #define ATRACE_TAG ATRACE_TAG_VIDEO #include - -#include +#include #include #include @@ -32,321 +31,14 @@ namespace android { -static const nsecs_t kNanosIn1s = 1000000000; - -template -static int compare(const T *lhs, const T *rhs) { - if (*lhs < *rhs) { - return -1; - } else if (*lhs > *rhs) { - return 1; - } else { - return 0; - } -} - -/* ======================================================================= */ -/* PLL */ -/* ======================================================================= */ - -static const size_t kMinSamplesToStartPrime = 3; -static const size_t kMinSamplesToStopPrime = VideoFrameScheduler::kHistorySize; -static const size_t kMinSamplesToEstimatePeriod = 3; -static const size_t kMaxSamplesToEstimatePeriod = VideoFrameScheduler::kHistorySize; - -static const size_t kPrecision = 12; -static const int64_t kErrorThreshold = (1 << (kPrecision * 2)) / 10; -static const int64_t kMultiplesThresholdDiv = 4; // 25% -static const int64_t kReFitThresholdDiv = 100; // 1% -static const nsecs_t kMaxAllowedFrameSkip = kNanosIn1s; // 1 sec -static const nsecs_t kMinPeriod = kNanosIn1s / 120; // 120Hz -static const nsecs_t kRefitRefreshPeriod = 10 * kNanosIn1s; // 10 sec - -VideoFrameScheduler::PLL::PLL() - : mPeriod(-1), - mPhase(0), - mPrimed(false), - mSamplesUsedForPriming(0), - mLastTime(-1), - mNumSamples(0) { -} - -void VideoFrameScheduler::PLL::reset(float fps) { - //test(); - - mSamplesUsedForPriming = 0; - mLastTime = -1; - - // set up or reset video PLL - if (fps <= 0.f) { - mPeriod = -1; - mPrimed = false; - } else { - ALOGV("reset at %.1f fps", fps); - mPeriod = (nsecs_t)(1e9 / fps + 0.5); - mPrimed = true; - } - - restart(); -} - -// reset PLL but keep previous period estimate -void VideoFrameScheduler::PLL::restart() { - mNumSamples = 0; - mPhase = -1; -} - -#if 0 - -void VideoFrameScheduler::PLL::test() { - nsecs_t period = kNanosIn1s / 60; - mTimes[0] = 0; - mTimes[1] = period; - mTimes[2] = period * 3; - mTimes[3] = period * 4; - mTimes[4] = period * 7; - mTimes[5] = period * 8; - mTimes[6] = period * 10; - mTimes[7] = period * 12; - mNumSamples = 8; - int64_t a, b, err; - fit(0, period * 12 / 7, 8, &a, &b, &err); - // a = 0.8(5)+ - // b = -0.14097(2)+ - // err = 0.2750578(703)+ - ALOGD("a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", - (long long)a, (a / (float)(1 << kPrecision)), - (long long)b, (b / (float)(1 << kPrecision)), - (long long)err, (err / (float)(1 << (kPrecision * 2)))); -} - -#endif - -bool VideoFrameScheduler::PLL::fit( - nsecs_t phase, nsecs_t period, size_t numSamplesToUse, - int64_t *a, int64_t *b, int64_t *err) { - if (numSamplesToUse > mNumSamples) { - numSamplesToUse = mNumSamples; - } - - if ((period >> kPrecision) == 0 ) { - ALOGW("Period is 0, or after including precision is 0 - would cause div0, returning"); - return false; - } - - int64_t sumX = 0; - int64_t sumXX = 0; - int64_t sumXY = 0; - int64_t sumYY = 0; - int64_t sumY = 0; - - int64_t x = 0; // x usually is in [0..numSamplesToUse) - nsecs_t lastTime; - for (size_t i = 0; i < numSamplesToUse; i++) { - size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize; - nsecs_t time = mTimes[ix]; - if (i > 0) { - x += divRound(time - lastTime, period); - } - // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision - // ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during - // priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod - // while we are not refitting. - int64_t y = divRound(time - phase, period >> kPrecision); - sumX += x; - sumY += y; - sumXX += x * x; - sumXY += x * y; - sumYY += y * y; - lastTime = time; - } - - int64_t div = (int64_t)numSamplesToUse * sumXX - sumX * sumX; - if (div == 0) { - return false; - } - - int64_t a_nom = (int64_t)numSamplesToUse * sumXY - sumX * sumY; - int64_t b_nom = sumXX * sumY - sumX * sumXY; - *a = divRound(a_nom, div); - *b = divRound(b_nom, div); - // don't use a and b directly as the rounding error is significant - *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div); - ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", - numSamplesToUse, - (long long)*a, (*a / (float)(1 << kPrecision)), - (long long)*b, (*b / (float)(1 << kPrecision)), - (long long)*err, (*err / (float)(1 << (kPrecision * 2)))); - return true; -} - -void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) { - if (numSamplesToUse > mNumSamples) { - numSamplesToUse = mNumSamples; - } - CHECK(numSamplesToUse >= 3); // must have at least 3 samples - - // estimate video framerate from deltas between timestamps, and - // 2nd order deltas - Vector deltas; - nsecs_t lastTime, firstTime; - for (size_t i = 0; i < numSamplesToUse; ++i) { - size_t index = (mNumSamples - numSamplesToUse + i) % kHistorySize; - nsecs_t time = mTimes[index]; - if (i > 0) { - if (time - lastTime > kMinPeriod) { - //ALOGV("delta: %lld", (long long)(time - lastTime)); - deltas.push(time - lastTime); - } - } else { - firstTime = time; - } - lastTime = time; - } - deltas.sort(compare); - size_t numDeltas = deltas.size(); - if (numDeltas > 1) { - nsecs_t deltaMinLimit = max(deltas[0] / kMultiplesThresholdDiv, kMinPeriod); - nsecs_t deltaMaxLimit = deltas[numDeltas / 2] * kMultiplesThresholdDiv; - for (size_t i = numDeltas / 2 + 1; i < numDeltas; ++i) { - if (deltas[i] > deltaMaxLimit) { - deltas.resize(i); - numDeltas = i; - break; - } - } - for (size_t i = 1; i < numDeltas; ++i) { - nsecs_t delta2nd = deltas[i] - deltas[i - 1]; - if (delta2nd >= deltaMinLimit) { - //ALOGV("delta2: %lld", (long long)(delta2nd)); - deltas.push(delta2nd); - } - } - } - - // use the one that yields the best match - int64_t bestScore; - for (size_t i = 0; i < deltas.size(); ++i) { - nsecs_t delta = deltas[i]; - int64_t score = 0; -#if 1 - // simplest score: number of deltas that are near multiples - size_t matches = 0; - for (size_t j = 0; j < deltas.size(); ++j) { - nsecs_t err = periodicError(deltas[j], delta); - if (err < delta / kMultiplesThresholdDiv) { - ++matches; - } - } - score = matches; -#if 0 - // could be weighed by the (1 - normalized error) - if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { - int64_t a, b, err; - fit(firstTime, delta, numSamplesToUse, &a, &b, &err); - err = (1 << (2 * kPrecision)) - err; - score *= max(0, err); - } -#endif -#else - // or use the error as a negative score - if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { - int64_t a, b, err; - fit(firstTime, delta, numSamplesToUse, &a, &b, &err); - score = -delta * err; - } -#endif - if (i == 0 || score > bestScore) { - bestScore = score; - mPeriod = delta; - mPhase = firstTime; - } - } - ALOGV("priming[%zu] phase:%lld period:%lld", - numSamplesToUse, (long long)mPhase, (long long)mPeriod); -} - -nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) { - if (mLastTime >= 0 - // if time goes backward, or we skipped rendering - && (time > mLastTime + kMaxAllowedFrameSkip || time < mLastTime)) { - restart(); - } - - mLastTime = time; - mTimes[mNumSamples % kHistorySize] = time; - ++mNumSamples; - - bool doFit = time > mRefitAt; - if ((mPeriod <= 0 || !mPrimed) && mNumSamples >= kMinSamplesToStartPrime) { - prime(kMinSamplesToStopPrime); - ++mSamplesUsedForPriming; - doFit = true; - } - if (mPeriod > 0 && mNumSamples >= kMinSamplesToEstimatePeriod) { - if (mPhase < 0) { - // initialize phase to the current render time - mPhase = time; - doFit = true; - } else if (!doFit) { - int64_t err = periodicError(time - mPhase, mPeriod); - doFit = err > mPeriod / kReFitThresholdDiv; - } - - if (doFit) { - int64_t a, b, err; - if (!fit(mPhase, mPeriod, kMaxSamplesToEstimatePeriod, &a, &b, &err)) { - // samples are not suitable for fitting. this means they are - // also not suitable for priming. - ALOGV("could not fit - keeping old period:%lld", (long long)mPeriod); - return mPeriod; - } - - mRefitAt = time + kRefitRefreshPeriod; - - mPhase += (mPeriod * b) >> kPrecision; - mPeriod = (mPeriod * a) >> kPrecision; - ALOGV("new phase:%lld period:%lld", (long long)mPhase, (long long)mPeriod); - - if (err < kErrorThreshold) { - if (!mPrimed && mSamplesUsedForPriming >= kMinSamplesToStopPrime) { - mPrimed = true; - } - } else { - mPrimed = false; - mSamplesUsedForPriming = 0; - } - } - } - return mPeriod; -} - -nsecs_t VideoFrameScheduler::PLL::getPeriod() const { - return mPrimed ? mPeriod : 0; -} - -/* ======================================================================= */ -/* Frame Scheduler */ -/* ======================================================================= */ - -static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60; // 60Hz -static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s; // 1 sec - -VideoFrameScheduler::VideoFrameScheduler() - : mVsyncTime(0), - mVsyncPeriod(0), - mVsyncRefreshAt(0), - mLastVsyncTime(-1), - mTimeCorrection(0) { +VideoFrameScheduler::VideoFrameScheduler() : VideoFrameSchedulerBase() { } void VideoFrameScheduler::updateVsync() { mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod; - mVsyncPeriod = 0; mVsyncTime = 0; + mVsyncPeriod = 0; - // TODO: schedule frames for the destination surface - // For now, surface flinger only schedules frames on the primary display if (mComposer == NULL) { String16 name("SurfaceFlinger"); sp sm = defaultServiceManager(); @@ -368,136 +60,6 @@ void VideoFrameScheduler::updateVsync() { } } -void VideoFrameScheduler::init(float videoFps) { - updateVsync(); - - mLastVsyncTime = -1; - mTimeCorrection = 0; - - mPll.reset(videoFps); -} - -void VideoFrameScheduler::restart() { - mLastVsyncTime = -1; - mTimeCorrection = 0; - - mPll.restart(); -} - -nsecs_t VideoFrameScheduler::getVsyncPeriod() { - if (mVsyncPeriod > 0) { - return mVsyncPeriod; - } - return kDefaultVsyncPeriod; -} - -float VideoFrameScheduler::getFrameRate() { - nsecs_t videoPeriod = mPll.getPeriod(); - if (videoPeriod > 0) { - return 1e9 / videoPeriod; - } - return 0.f; -} - -nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) { - nsecs_t origRenderTime = renderTime; - - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now >= mVsyncRefreshAt) { - updateVsync(); - } - - // without VSYNC info, there is nothing to do - if (mVsyncPeriod == 0) { - ALOGV("no vsync: render=%lld", (long long)renderTime); - return renderTime; - } - - // ensure vsync time is well before (corrected) render time - if (mVsyncTime > renderTime - 4 * mVsyncPeriod) { - mVsyncTime -= - ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod; - } - - // Video presentation takes place at the VSYNC _after_ renderTime. Adjust renderTime - // so this effectively becomes a rounding operation (to the _closest_ VSYNC.) - renderTime -= mVsyncPeriod / 2; - - const nsecs_t videoPeriod = mPll.addSample(origRenderTime); - if (videoPeriod > 0) { - // Smooth out rendering - size_t N = 12; - nsecs_t fiveSixthDev = - abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod) - / (mVsyncPeriod / 100); - // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz) - if (fiveSixthDev < 12) { /* 12% / 6 = 2% */ - N = 20; - } - - nsecs_t offset = 0; - nsecs_t edgeRemainder = 0; - for (size_t i = 1; i <= N; i++) { - offset += - (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod; - edgeRemainder += (videoPeriod * i) % mVsyncPeriod; - } - mTimeCorrection += mVsyncPeriod / 2 - offset / (nsecs_t)N; - renderTime += mTimeCorrection; - nsecs_t correctionLimit = mVsyncPeriod * 3 / 5; - edgeRemainder = abs(edgeRemainder / (nsecs_t)N - mVsyncPeriod / 2); - if (edgeRemainder <= mVsyncPeriod / 3) { - correctionLimit /= 2; - } - - // estimate how many VSYNCs a frame will spend on the display - nsecs_t nextVsyncTime = - renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod); - if (mLastVsyncTime >= 0) { - size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod; - size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod); - bool vsyncsPerFrameAreNearlyConstant = - periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0; - - if (mTimeCorrection > correctionLimit && - (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) { - // remove a VSYNC - mTimeCorrection -= mVsyncPeriod / 2; - renderTime -= mVsyncPeriod / 2; - nextVsyncTime -= mVsyncPeriod; - if (vsyncsForLastFrame > 0) - --vsyncsForLastFrame; - } else if (mTimeCorrection < -correctionLimit && - (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) { - // add a VSYNC - mTimeCorrection += mVsyncPeriod / 2; - renderTime += mVsyncPeriod / 2; - nextVsyncTime += mVsyncPeriod; - if (vsyncsForLastFrame < ULONG_MAX) - ++vsyncsForLastFrame; - } else if (mTimeCorrection < -correctionLimit * 2 - || mTimeCorrection > correctionLimit * 2) { - ALOGW("correction beyond limit: %lld vs %lld (vsyncs for last frame: %zu, min: %zu)" - " restarting. render=%lld", - (long long)mTimeCorrection, (long long)correctionLimit, - vsyncsForLastFrame, minVsyncsPerFrame, (long long)origRenderTime); - restart(); - return origRenderTime; - } - - ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame); - } - mLastVsyncTime = nextVsyncTime; - } - - // align rendertime to the center between VSYNC edges - renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod; - renderTime += mVsyncPeriod / 2; - ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime); - ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000); - return renderTime; -} - void VideoFrameScheduler::release() { mComposer.clear(); } @@ -507,4 +69,3 @@ VideoFrameScheduler::~VideoFrameScheduler() { } } // namespace android - diff --git a/media/libstagefright/VideoFrameScheduler2.cpp b/media/libstagefright/VideoFrameScheduler2.cpp new file mode 100644 index 0000000000..e02ae7d3a8 --- /dev/null +++ b/media/libstagefright/VideoFrameScheduler2.cpp @@ -0,0 +1,285 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "VideoFrameScheduler2" +#include +#define ATRACE_TAG ATRACE_TAG_VIDEO +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace android { + +static void getVsyncOffset(long* appVsyncOffsetPtr, long* sfVsyncOffsetPtr); + +/* ======================================================================= */ +/* VsyncTracker */ +/* ======================================================================= */ + +class VsyncTracker : public RefBase{ +public: + VsyncTracker(); + ~VsyncTracker() {} + long getVsyncPeriod(); + long getVsyncTime(long periodOffset); + void addSample(long timestamp); + +private: + static const int kMaxSamples = 32; + static const int kMinSamplesForUpdate = 6; + int mNumSamples; + int mFirstSample; + long mReferenceTime; + long mPhase; + long mPeriod; + long mTimestampSamples[kMaxSamples]; + Mutex mLock; + + void updateModelLocked(); +}; + +VsyncTracker::VsyncTracker() + : mNumSamples(0), + mFirstSample(0), + mReferenceTime(0), + mPhase(0), + mPeriod(0) { + for (int i = 0; i < kMaxSamples; i++) { + mTimestampSamples[i] = 0; + } +} + +long VsyncTracker::getVsyncPeriod() { + Mutex::Autolock dataLock(mLock); + return mPeriod; +} + +long VsyncTracker::getVsyncTime(long periodOffset) { + Mutex::Autolock dataLock(mLock); + const long now = systemTime(); + long phase = mReferenceTime + mPhase; + return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; +} + +void VsyncTracker::addSample(long timestamp) { + Mutex::Autolock dataLock(mLock); + if (mNumSamples == 0) { + mPhase = 0; + mReferenceTime = timestamp; + } + int idx = (mFirstSample + mNumSamples) % kMaxSamples; + mTimestampSamples[idx] = timestamp; + if (mNumSamples < kMaxSamples) { + mNumSamples++; + } else { + mFirstSample = (mFirstSample + 1) % kMaxSamples; + } + updateModelLocked(); +} + +void VsyncTracker::updateModelLocked() { + if (mNumSamples < kMinSamplesForUpdate) { + return; + } + long durationSum = 0; + long minDuration = LONG_MAX; + long maxDuration = 0; + + for (int i = 1; i < mNumSamples; i++) { + int idx = (mFirstSample + i) % kMaxSamples; + int prev = (idx + kMaxSamples - 1) % kMaxSamples; + long duration = mTimestampSamples[idx] - mTimestampSamples[prev]; + durationSum += duration; + minDuration = min(minDuration, duration); + maxDuration = max(maxDuration, duration); + } + + durationSum -= (minDuration + maxDuration); + mPeriod = durationSum / (mNumSamples - 3); + + double sampleAvgX = 0.0; + double sampleAvgY = 0.0; + double scale = 2.0 * M_PI / (double) mPeriod; + + for (int i = 1; i < mNumSamples; i++) { + int idx = (mFirstSample + i) % kMaxSamples; + long sample = mTimestampSamples[idx] - mReferenceTime; + double samplePhase = (double) (sample % mPeriod) * scale; + sampleAvgX += cos(samplePhase); + sampleAvgY += sin(samplePhase); + } + + sampleAvgX /= (double) mNumSamples - 1.0; + sampleAvgY /= (double) mNumSamples - 1.0; + mPhase = (long) (atan2(sampleAvgY, sampleAvgX) / scale); +} + +static void frameCallback(long frameTimeNanos, void* data) { + if (data == NULL) { + return; + } + sp vsyncTracker(static_cast(data)); + vsyncTracker->addSample(frameTimeNanos); + AChoreographer_postFrameCallback(AChoreographer_getInstance(), + frameCallback, static_cast(vsyncTracker.get())); +} + +/* ======================================================================= */ +/* JNI */ +/* ======================================================================= */ + +static void getVsyncOffset(long* appVsyncOffsetPtr, long* sfVsyncOffsetPtr) { + static const long kOneMillisecInNanosec = 1000000; + static const long kOneSecInNanosec = kOneMillisecInNanosec * 1000; + + JNIEnv *env = JavaVMHelper::getJNIEnv(); + jclass jDisplayManagerGlobalCls = env->FindClass( + "android/hardware/display/DisplayManagerGlobal"); + jclass jDisplayCls = env->FindClass("android/view/Display"); + + jmethodID jGetInstance = env->GetStaticMethodID(jDisplayManagerGlobalCls, + "getInstance", "()Landroid/hardware/display/DisplayManagerGlobal;"); + jobject javaDisplayManagerGlobalObj = env->CallStaticObjectMethod( + jDisplayManagerGlobalCls, jGetInstance); + + jfieldID jDEFAULT_DISPLAY = env->GetStaticFieldID(jDisplayCls, "DEFAULT_DISPLAY", "I"); + jint DEFAULT_DISPLAY = env->GetStaticIntField(jDisplayCls, jDEFAULT_DISPLAY); + + jmethodID jgetRealDisplay = env->GetMethodID(jDisplayManagerGlobalCls, + "getRealDisplay", "(I)Landroid/view/Display;"); + jobject javaDisplayObj = env->CallObjectMethod( + javaDisplayManagerGlobalObj, jgetRealDisplay, DEFAULT_DISPLAY); + + jmethodID jGetRefreshRate = env->GetMethodID(jDisplayCls, "getRefreshRate", "()F"); + jfloat javaRefreshRate = env->CallFloatMethod(javaDisplayObj, jGetRefreshRate); + long vsyncPeriod = (long) (kOneSecInNanosec / (float) javaRefreshRate); + + jmethodID jGetAppVsyncOffsetNanos = env->GetMethodID( + jDisplayCls, "getAppVsyncOffsetNanos", "()J"); + jlong javaAppVsyncOffset = env->CallLongMethod(javaDisplayObj, jGetAppVsyncOffsetNanos); + *appVsyncOffsetPtr = (long) javaAppVsyncOffset; + + jmethodID jGetPresentationDeadlineNanos = env->GetMethodID( + jDisplayCls, "getPresentationDeadlineNanos", "()J"); + jlong javaPresentationDeadline = env->CallLongMethod( + javaDisplayObj, jGetPresentationDeadlineNanos); + + *sfVsyncOffsetPtr = vsyncPeriod - ((long) javaPresentationDeadline - kOneMillisecInNanosec); +} + +/* ======================================================================= */ +/* Choreographer Thread */ +/* ======================================================================= */ + +struct ChoreographerThread : public Thread { + ChoreographerThread(bool canCallJava); + status_t init(void* data); + virtual status_t readyToRun() override; + virtual bool threadLoop() override; + +protected: + virtual ~ChoreographerThread() {} + +private: + DISALLOW_EVIL_CONSTRUCTORS(ChoreographerThread); + void* mData; +}; + +ChoreographerThread::ChoreographerThread(bool canCallJava) : Thread(canCallJava) { +} + +status_t ChoreographerThread::init(void* data) { + if (data == NULL) { + return NO_INIT; + } + mData = data; + return OK; +} + +status_t ChoreographerThread::readyToRun() { + ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + if (AChoreographer_getInstance() == NULL) { + return NO_INIT; + } + AChoreographer_postFrameCallback(AChoreographer_getInstance(), frameCallback, mData); + return OK; +} + +bool ChoreographerThread::threadLoop() { + ALooper_pollOnce(-1, nullptr, nullptr, nullptr); + return true; +} + +/* ======================================================================= */ +/* Frame Scheduler */ +/* ======================================================================= */ + +VideoFrameScheduler2::VideoFrameScheduler2() : VideoFrameSchedulerBase() { + + getVsyncOffset(&mAppVsyncOffset, &mSfVsyncOffset); + + Mutex::Autolock threadLock(mLock); + mChoreographerThread = new ChoreographerThread(true); + + mVsyncTracker = new VsyncTracker(); + if (mChoreographerThread->init(static_cast(mVsyncTracker.get())) != OK) { + mChoreographerThread.clear(); + } + if (mChoreographerThread != NULL && mChoreographerThread->run("Choreographer") != OK) { + mChoreographerThread.clear(); + } +} + +void VideoFrameScheduler2::updateVsync() { + mVsyncTime = 0; + mVsyncPeriod = 0; + + if (mVsyncTracker != NULL) { + mVsyncPeriod = mVsyncTracker->getVsyncPeriod(); + mVsyncTime = mVsyncTracker->getVsyncTime(mSfVsyncOffset - mAppVsyncOffset); + } + mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod; +} + +void VideoFrameScheduler2::release() { + // Do not change order + { + Mutex::Autolock threadLock(mLock); + mChoreographerThread->requestExitAndWait(); + mChoreographerThread.clear(); + } + + mVsyncTracker.clear(); +} + +VideoFrameScheduler2::~VideoFrameScheduler2() { + release(); +} + +} // namespace android diff --git a/media/libstagefright/VideoFrameSchedulerBase.cpp b/media/libstagefright/VideoFrameSchedulerBase.cpp new file mode 100644 index 0000000000..77107ff90f --- /dev/null +++ b/media/libstagefright/VideoFrameSchedulerBase.cpp @@ -0,0 +1,465 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "VideoFrameSchedulerBase" +#include +#define ATRACE_TAG ATRACE_TAG_VIDEO +#include +#include + +#include +#include +#include + +namespace android { + +template +static int compare(const T *lhs, const T *rhs) { + if (*lhs < *rhs) { + return -1; + } else if (*lhs > *rhs) { + return 1; + } else { + return 0; + } +} + +/* ======================================================================= */ +/* PLL */ +/* ======================================================================= */ + +static const size_t kMinSamplesToStartPrime = 3; +static const size_t kMinSamplesToStopPrime = VideoFrameSchedulerBase::kHistorySize; +static const size_t kMinSamplesToEstimatePeriod = 3; +static const size_t kMaxSamplesToEstimatePeriod = VideoFrameSchedulerBase::kHistorySize; + +static const size_t kPrecision = 12; +static const int64_t kErrorThreshold = (1 << (kPrecision * 2)) / 10; +static const int64_t kMultiplesThresholdDiv = 4; // 25% +static const int64_t kReFitThresholdDiv = 100; // 1% +static const nsecs_t kMaxAllowedFrameSkip = VideoFrameSchedulerBase::kNanosIn1s; // 1 sec +static const nsecs_t kMinPeriod = VideoFrameSchedulerBase::kNanosIn1s / 120; // 120Hz +static const nsecs_t kRefitRefreshPeriod = 10 * VideoFrameSchedulerBase::kNanosIn1s; // 10 sec + +VideoFrameSchedulerBase::PLL::PLL() + : mPeriod(-1), + mPhase(0), + mPrimed(false), + mSamplesUsedForPriming(0), + mLastTime(-1), + mNumSamples(0) { +} + +void VideoFrameSchedulerBase::PLL::reset(float fps) { + //test(); + + mSamplesUsedForPriming = 0; + mLastTime = -1; + + // set up or reset video PLL + if (fps <= 0.f) { + mPeriod = -1; + mPrimed = false; + } else { + ALOGV("reset at %.1f fps", fps); + mPeriod = (nsecs_t)(1e9 / fps + 0.5); + mPrimed = true; + } + + restart(); +} + +// reset PLL but keep previous period estimate +void VideoFrameSchedulerBase::PLL::restart() { + mNumSamples = 0; + mPhase = -1; +} + +#if 0 + +void VideoFrameSchedulerBase::PLL::test() { + nsecs_t period = VideoFrameSchedulerBase::kNanosIn1s / 60; + mTimes[0] = 0; + mTimes[1] = period; + mTimes[2] = period * 3; + mTimes[3] = period * 4; + mTimes[4] = period * 7; + mTimes[5] = period * 8; + mTimes[6] = period * 10; + mTimes[7] = period * 12; + mNumSamples = 8; + int64_t a, b, err; + fit(0, period * 12 / 7, 8, &a, &b, &err); + // a = 0.8(5)+ + // b = -0.14097(2)+ + // err = 0.2750578(703)+ + ALOGD("a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", + (long long)a, (a / (float)(1 << kPrecision)), + (long long)b, (b / (float)(1 << kPrecision)), + (long long)err, (err / (float)(1 << (kPrecision * 2)))); +} + +#endif + +bool VideoFrameSchedulerBase::PLL::fit( + nsecs_t phase, nsecs_t period, size_t numSamplesToUse, + int64_t *a, int64_t *b, int64_t *err) { + if (numSamplesToUse > mNumSamples) { + numSamplesToUse = mNumSamples; + } + + if ((period >> kPrecision) == 0 ) { + ALOGW("Period is 0, or after including precision is 0 - would cause div0, returning"); + return false; + } + + int64_t sumX = 0; + int64_t sumXX = 0; + int64_t sumXY = 0; + int64_t sumYY = 0; + int64_t sumY = 0; + + int64_t x = 0; // x usually is in [0..numSamplesToUse) + nsecs_t lastTime; + for (size_t i = 0; i < numSamplesToUse; i++) { + size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize; + nsecs_t time = mTimes[ix]; + if (i > 0) { + x += divRound(time - lastTime, period); + } + // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision + // ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during + // priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod + // while we are not refitting. + int64_t y = divRound(time - phase, period >> kPrecision); + sumX += x; + sumY += y; + sumXX += x * x; + sumXY += x * y; + sumYY += y * y; + lastTime = time; + } + + int64_t div = (int64_t)numSamplesToUse * sumXX - sumX * sumX; + if (div == 0) { + return false; + } + + int64_t a_nom = (int64_t)numSamplesToUse * sumXY - sumX * sumY; + int64_t b_nom = sumXX * sumY - sumX * sumXY; + *a = divRound(a_nom, div); + *b = divRound(b_nom, div); + // don't use a and b directly as the rounding error is significant + *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div); + ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", + numSamplesToUse, + (long long)*a, (*a / (float)(1 << kPrecision)), + (long long)*b, (*b / (float)(1 << kPrecision)), + (long long)*err, (*err / (float)(1 << (kPrecision * 2)))); + return true; +} + +void VideoFrameSchedulerBase::PLL::prime(size_t numSamplesToUse) { + if (numSamplesToUse > mNumSamples) { + numSamplesToUse = mNumSamples; + } + CHECK(numSamplesToUse >= 3); // must have at least 3 samples + + // estimate video framerate from deltas between timestamps, and + // 2nd order deltas + Vector deltas; + nsecs_t lastTime, firstTime; + for (size_t i = 0; i < numSamplesToUse; ++i) { + size_t index = (mNumSamples - numSamplesToUse + i) % kHistorySize; + nsecs_t time = mTimes[index]; + if (i > 0) { + if (time - lastTime > kMinPeriod) { + //ALOGV("delta: %lld", (long long)(time - lastTime)); + deltas.push(time - lastTime); + } + } else { + firstTime = time; + } + lastTime = time; + } + deltas.sort(compare); + size_t numDeltas = deltas.size(); + if (numDeltas > 1) { + nsecs_t deltaMinLimit = max(deltas[0] / kMultiplesThresholdDiv, kMinPeriod); + nsecs_t deltaMaxLimit = deltas[numDeltas / 2] * kMultiplesThresholdDiv; + for (size_t i = numDeltas / 2 + 1; i < numDeltas; ++i) { + if (deltas[i] > deltaMaxLimit) { + deltas.resize(i); + numDeltas = i; + break; + } + } + for (size_t i = 1; i < numDeltas; ++i) { + nsecs_t delta2nd = deltas[i] - deltas[i - 1]; + if (delta2nd >= deltaMinLimit) { + //ALOGV("delta2: %lld", (long long)(delta2nd)); + deltas.push(delta2nd); + } + } + } + + // use the one that yields the best match + int64_t bestScore; + for (size_t i = 0; i < deltas.size(); ++i) { + nsecs_t delta = deltas[i]; + int64_t score = 0; +#if 1 + // simplest score: number of deltas that are near multiples + size_t matches = 0; + for (size_t j = 0; j < deltas.size(); ++j) { + nsecs_t err = periodicError(deltas[j], delta); + if (err < delta / kMultiplesThresholdDiv) { + ++matches; + } + } + score = matches; +#if 0 + // could be weighed by the (1 - normalized error) + if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { + int64_t a, b, err; + fit(firstTime, delta, numSamplesToUse, &a, &b, &err); + err = (1 << (2 * kPrecision)) - err; + score *= max(0, err); + } +#endif +#else + // or use the error as a negative score + if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { + int64_t a, b, err; + fit(firstTime, delta, numSamplesToUse, &a, &b, &err); + score = -delta * err; + } +#endif + if (i == 0 || score > bestScore) { + bestScore = score; + mPeriod = delta; + mPhase = firstTime; + } + } + ALOGV("priming[%zu] phase:%lld period:%lld", + numSamplesToUse, (long long)mPhase, (long long)mPeriod); +} + +nsecs_t VideoFrameSchedulerBase::PLL::addSample(nsecs_t time) { + if (mLastTime >= 0 + // if time goes backward, or we skipped rendering + && (time > mLastTime + kMaxAllowedFrameSkip || time < mLastTime)) { + restart(); + } + + mLastTime = time; + mTimes[mNumSamples % kHistorySize] = time; + ++mNumSamples; + + bool doFit = time > mRefitAt; + if ((mPeriod <= 0 || !mPrimed) && mNumSamples >= kMinSamplesToStartPrime) { + prime(kMinSamplesToStopPrime); + ++mSamplesUsedForPriming; + doFit = true; + } + if (mPeriod > 0 && mNumSamples >= kMinSamplesToEstimatePeriod) { + if (mPhase < 0) { + // initialize phase to the current render time + mPhase = time; + doFit = true; + } else if (!doFit) { + int64_t err = periodicError(time - mPhase, mPeriod); + doFit = err > mPeriod / kReFitThresholdDiv; + } + + if (doFit) { + int64_t a, b, err; + if (!fit(mPhase, mPeriod, kMaxSamplesToEstimatePeriod, &a, &b, &err)) { + // samples are not suitable for fitting. this means they are + // also not suitable for priming. + ALOGV("could not fit - keeping old period:%lld", (long long)mPeriod); + return mPeriod; + } + + mRefitAt = time + kRefitRefreshPeriod; + + mPhase += (mPeriod * b) >> kPrecision; + mPeriod = (mPeriod * a) >> kPrecision; + ALOGV("new phase:%lld period:%lld", (long long)mPhase, (long long)mPeriod); + + if (err < kErrorThreshold) { + if (!mPrimed && mSamplesUsedForPriming >= kMinSamplesToStopPrime) { + mPrimed = true; + } + } else { + mPrimed = false; + mSamplesUsedForPriming = 0; + } + } + } + return mPeriod; +} + +nsecs_t VideoFrameSchedulerBase::PLL::getPeriod() const { + return mPrimed ? mPeriod : 0; +} + +/* ======================================================================= */ +/* Frame Scheduler */ +/* ======================================================================= */ + +VideoFrameSchedulerBase::VideoFrameSchedulerBase() + : mVsyncTime(0), + mVsyncPeriod(0), + mVsyncRefreshAt(0), + mLastVsyncTime(-1), + mTimeCorrection(0) { +} + +void VideoFrameSchedulerBase::init(float videoFps) { + updateVsync(); + + mLastVsyncTime = -1; + mTimeCorrection = 0; + + mPll.reset(videoFps); +} + +void VideoFrameSchedulerBase::restart() { + mLastVsyncTime = -1; + mTimeCorrection = 0; + + mPll.restart(); +} + +nsecs_t VideoFrameSchedulerBase::getVsyncPeriod() { + if (mVsyncPeriod > 0) { + return mVsyncPeriod; + } + return kDefaultVsyncPeriod; +} + +float VideoFrameSchedulerBase::getFrameRate() { + nsecs_t videoPeriod = mPll.getPeriod(); + if (videoPeriod > 0) { + return 1e9 / videoPeriod; + } + return 0.f; +} + +nsecs_t VideoFrameSchedulerBase::schedule(nsecs_t renderTime) { + nsecs_t origRenderTime = renderTime; + + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now >= mVsyncRefreshAt) { + updateVsync(); + } + + // without VSYNC info, there is nothing to do + if (mVsyncPeriod == 0) { + ALOGV("no vsync: render=%lld", (long long)renderTime); + return renderTime; + } + + // ensure vsync time is well before (corrected) render time + if (mVsyncTime > renderTime - 4 * mVsyncPeriod) { + mVsyncTime -= + ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod; + } + + // Video presentation takes place at the VSYNC _after_ renderTime. Adjust renderTime + // so this effectively becomes a rounding operation (to the _closest_ VSYNC.) + renderTime -= mVsyncPeriod / 2; + + const nsecs_t videoPeriod = mPll.addSample(origRenderTime); + if (videoPeriod > 0) { + // Smooth out rendering + size_t N = 12; + nsecs_t fiveSixthDev = + abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod) + / (mVsyncPeriod / 100); + // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz) + if (fiveSixthDev < 12) { /* 12% / 6 = 2% */ + N = 20; + } + + nsecs_t offset = 0; + nsecs_t edgeRemainder = 0; + for (size_t i = 1; i <= N; i++) { + offset += + (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod; + edgeRemainder += (videoPeriod * i) % mVsyncPeriod; + } + mTimeCorrection += mVsyncPeriod / 2 - offset / (nsecs_t)N; + renderTime += mTimeCorrection; + nsecs_t correctionLimit = mVsyncPeriod * 3 / 5; + edgeRemainder = abs(edgeRemainder / (nsecs_t)N - mVsyncPeriod / 2); + if (edgeRemainder <= mVsyncPeriod / 3) { + correctionLimit /= 2; + } + + // estimate how many VSYNCs a frame will spend on the display + nsecs_t nextVsyncTime = + renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod); + if (mLastVsyncTime >= 0) { + size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod; + size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod); + bool vsyncsPerFrameAreNearlyConstant = + periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0; + + if (mTimeCorrection > correctionLimit && + (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) { + // remove a VSYNC + mTimeCorrection -= mVsyncPeriod / 2; + renderTime -= mVsyncPeriod / 2; + nextVsyncTime -= mVsyncPeriod; + if (vsyncsForLastFrame > 0) + --vsyncsForLastFrame; + } else if (mTimeCorrection < -correctionLimit && + (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) { + // add a VSYNC + mTimeCorrection += mVsyncPeriod / 2; + renderTime += mVsyncPeriod / 2; + nextVsyncTime += mVsyncPeriod; + if (vsyncsForLastFrame < ULONG_MAX) + ++vsyncsForLastFrame; + } else if (mTimeCorrection < -correctionLimit * 2 + || mTimeCorrection > correctionLimit * 2) { + ALOGW("correction beyond limit: %lld vs %lld (vsyncs for last frame: %zu, min: %zu)" + " restarting. render=%lld", + (long long)mTimeCorrection, (long long)correctionLimit, + vsyncsForLastFrame, minVsyncsPerFrame, (long long)origRenderTime); + restart(); + return origRenderTime; + } + + ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame); + } + mLastVsyncTime = nextVsyncTime; + } + + // align rendertime to the center between VSYNC edges + renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod; + renderTime += mVsyncPeriod / 2; + ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime); + ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000); + return renderTime; +} + +VideoFrameSchedulerBase::~VideoFrameSchedulerBase() {} + +} // namespace android diff --git a/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h b/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h index 9d97dfde6e..fcfcbec006 100644 --- a/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h +++ b/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h @@ -1,5 +1,5 @@ /* - * Copyright 2014, The Android Open Source Project + * Copyright 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,87 +17,24 @@ #ifndef VIDEO_FRAME_SCHEDULER_H_ #define VIDEO_FRAME_SCHEDULER_H_ -#include -#include - -#include +#include namespace android { class ISurfaceComposer; -struct VideoFrameScheduler : public RefBase { +struct VideoFrameScheduler : public VideoFrameSchedulerBase { VideoFrameScheduler(); - - // (re)initialize scheduler - void init(float videoFps = -1); - // use in case of video render-time discontinuity, e.g. seek - void restart(); - // get adjusted nanotime for a video frame render at renderTime - nsecs_t schedule(nsecs_t renderTime); - - // returns the vsync period for the main display - nsecs_t getVsyncPeriod(); - - // returns the current frames-per-second, or 0.f if not primed - float getFrameRate(); - - void release(); - - static const size_t kHistorySize = 8; + void release() override; protected: virtual ~VideoFrameScheduler(); private: - struct PLL { - PLL(); - - // reset PLL to new PLL - void reset(float fps = -1); - // keep current estimate, but restart phase - void restart(); - // returns period or 0 if not yet primed - nsecs_t addSample(nsecs_t time); - nsecs_t getPeriod() const; - - private: - nsecs_t mPeriod; - nsecs_t mPhase; - - bool mPrimed; // have an estimate for the period - size_t mSamplesUsedForPriming; - - nsecs_t mLastTime; // last input time - nsecs_t mRefitAt; // next input time to fit at - - size_t mNumSamples; // can go past kHistorySize - nsecs_t mTimes[kHistorySize]; - - void test(); - // returns whether fit was successful - bool fit(nsecs_t phase, nsecs_t period, size_t numSamples, - int64_t *a, int64_t *b, int64_t *err); - void prime(size_t numSamples); - }; - - void updateVsync(); - - nsecs_t mVsyncTime; // vsync timing from display - nsecs_t mVsyncPeriod; - nsecs_t mVsyncRefreshAt; // next time to refresh timing info - - nsecs_t mLastVsyncTime; // estimated vsync time for last frame - nsecs_t mTimeCorrection; // running adjustment - - PLL mPll; // PLL for video frame rate based on render time - + void updateVsync() override; sp mComposer; - - DISALLOW_EVIL_CONSTRUCTORS(VideoFrameScheduler); }; } // namespace android #endif // VIDEO_FRAME_SCHEDULER_H_ - diff --git a/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h b/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h new file mode 100644 index 0000000000..be911cc576 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h @@ -0,0 +1,46 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIDEO_FRAME_SCHEDULER_2_H_ +#define VIDEO_FRAME_SCHEDULER_2_H_ + +#include + +namespace android { + +class VsyncTracker; +struct ChoreographerThread; + +struct VideoFrameScheduler2 : public VideoFrameSchedulerBase { + VideoFrameScheduler2(); + void release() override; + +protected: + virtual ~VideoFrameScheduler2(); + +private: + void updateVsync() override; + + long mAppVsyncOffset; + long mSfVsyncOffset; + sp mVsyncTracker; + sp mChoreographerThread; + Mutex mLock; +}; + +} // namespace android + +#endif // VIDEO_FRAME_SCHEDULER_2_H_ diff --git a/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h b/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h new file mode 100644 index 0000000000..ff5f716268 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h @@ -0,0 +1,100 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIDEO_FRAME_SCHEDULER_BASE_H_ +#define VIDEO_FRAME_SCHEDULER_BASE_H_ + +#include +#include + +#include + +namespace android { + +struct VideoFrameSchedulerBase : public RefBase { + VideoFrameSchedulerBase(); + + // (re)initialize scheduler + void init(float videoFps = -1); + // use in case of video render-time discontinuity, e.g. seek + void restart(); + // get adjusted nanotime for a video frame render at renderTime + nsecs_t schedule(nsecs_t renderTime); + + // returns the vsync period for the main display + nsecs_t getVsyncPeriod(); + + // returns the current frames-per-second, or 0.f if not primed + float getFrameRate(); + + virtual void release() = 0; + + static const size_t kHistorySize = 8; + static const nsecs_t kNanosIn1s = 1000000000; + static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60; // 60Hz + static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s; // 1 sec + +protected: + virtual ~VideoFrameSchedulerBase(); + + nsecs_t mVsyncTime; // vsync timing from display + nsecs_t mVsyncPeriod; + nsecs_t mVsyncRefreshAt; // next time to refresh timing info + +private: + struct PLL { + PLL(); + + // reset PLL to new PLL + void reset(float fps = -1); + // keep current estimate, but restart phase + void restart(); + // returns period or 0 if not yet primed + nsecs_t addSample(nsecs_t time); + nsecs_t getPeriod() const; + + private: + nsecs_t mPeriod; + nsecs_t mPhase; + + bool mPrimed; // have an estimate for the period + size_t mSamplesUsedForPriming; + + nsecs_t mLastTime; // last input time + nsecs_t mRefitAt; // next input time to fit at + + size_t mNumSamples; // can go past kHistorySize + nsecs_t mTimes[kHistorySize]; + + void test(); + // returns whether fit was successful + bool fit(nsecs_t phase, nsecs_t period, size_t numSamples, + int64_t *a, int64_t *b, int64_t *err); + void prime(size_t numSamples); + }; + + virtual void updateVsync() = 0; + + nsecs_t mLastVsyncTime; // estimated vsync time for last frame + nsecs_t mTimeCorrection; // running adjustment + PLL mPll; // PLL for video frame rate based on render time + + DISALLOW_EVIL_CONSTRUCTORS(VideoFrameSchedulerBase); +}; + +} // namespace android + +#endif // VIDEO_FRAME_SCHEDULER_BASE_H_ -- GitLab From 0ff9ae3ecbb421551d5de9701a349d8a3741f965 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 5 Dec 2018 18:06:12 -0800 Subject: [PATCH 0645/1530] Camera: Enable logical multi-camera API in NDK The support inclues: - Physical camera specific stream support, - Physical camera result metadata, and Test: Newly added NDK CTS test pass Test: Newly added VNDK test pass Bug: 120566141 Bug: 115532726 Change-Id: I939b81522ca6c518c0e54ded5d3615f9973a6a65 --- camera/camera2/OutputConfiguration.cpp | 8 +- .../camera/camera2/OutputConfiguration.h | 4 +- camera/ndk/NdkCameraCaptureSession.cpp | 50 ++- camera/ndk/NdkCameraDevice.cpp | 14 + camera/ndk/NdkCameraMetadata.cpp | 17 + camera/ndk/impl/ACameraCaptureSession.cpp | 41 --- camera/ndk/impl/ACameraCaptureSession.h | 13 +- camera/ndk/impl/ACameraCaptureSession.inc | 68 ++++ camera/ndk/impl/ACameraDevice.cpp | 251 +++++++------- camera/ndk/impl/ACameraDevice.h | 63 +++- camera/ndk/impl/ACameraDevice.inc | 130 +++++++ camera/ndk/impl/ACameraMetadata.cpp | 52 +++ camera/ndk/impl/ACameraMetadata.h | 5 + .../include/camera/NdkCameraCaptureSession.h | 97 ++++++ camera/ndk/include/camera/NdkCameraDevice.h | 30 ++ camera/ndk/include/camera/NdkCameraMetadata.h | 22 ++ .../include/camera/NdkCameraMetadataTags.h | 19 + camera/ndk/libcamera2ndk.map.txt | 4 + .../impl/ACameraCaptureSessionVendor.h | 7 +- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 325 +++++++++--------- camera/ndk/ndk_vendor/impl/ACameraDevice.h | 74 +++- .../ndk_vendor/impl/ACameraDeviceVendor.inc | 152 ++++++++ camera/tests/CameraBinderTests.cpp | 3 +- .../hidl/AidlCameraDeviceCallbacks.cpp | 8 +- .../camera/libcameraservice/hidl/Convert.cpp | 3 +- 25 files changed, 1094 insertions(+), 366 deletions(-) create mode 100644 camera/ndk/impl/ACameraCaptureSession.inc create mode 100644 camera/ndk/impl/ACameraDevice.inc create mode 100644 camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp index 321eb08bea..4e9b27d50d 100644 --- a/camera/camera2/OutputConfiguration.cpp +++ b/camera/camera2/OutputConfiguration.cpp @@ -167,19 +167,23 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { } OutputConfiguration::OutputConfiguration(sp& gbp, int rotation, + const String16& physicalId, int surfaceSetID, bool isShared) { mGbps.push_back(gbp); mRotation = rotation; mSurfaceSetID = surfaceSetID; mIsDeferred = false; mIsShared = isShared; + mPhysicalCameraId = physicalId; } OutputConfiguration::OutputConfiguration( const std::vector>& gbps, - int rotation, int surfaceSetID, int surfaceType, int width, int height, bool isShared) + int rotation, const String16& physicalCameraId, int surfaceSetID, int surfaceType, + int width, int height, bool isShared) : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType), - mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared) { } + mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared), + mPhysicalCameraId(physicalCameraId) { } status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const { diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h index 5b117fb14e..95c4f39d91 100644 --- a/camera/include/camera/camera2/OutputConfiguration.h +++ b/camera/include/camera/camera2/OutputConfiguration.h @@ -65,10 +65,12 @@ public: OutputConfiguration(const android::Parcel& parcel); OutputConfiguration(sp& gbp, int rotation, + const String16& physicalCameraId, int surfaceSetID = INVALID_SET_ID, bool isShared = false); OutputConfiguration(const std::vector>& gbps, - int rotation, int surfaceSetID = INVALID_SET_ID, + int rotation, const String16& physicalCameraId, + int surfaceSetID = INVALID_SET_ID, int surfaceType = OutputConfiguration::SURFACE_TYPE_UNKNOWN, int width = 0, int height = 0, bool isShared = false); diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp index fd95296161..540d84e43b 100644 --- a/camera/ndk/NdkCameraCaptureSession.cpp +++ b/camera/ndk/NdkCameraCaptureSession.cpp @@ -28,6 +28,8 @@ #include #include "impl/ACameraCaptureSession.h" +#include "impl/ACameraCaptureSession.inc" + using namespace android; EXPORT @@ -82,7 +84,31 @@ camera_status_t ACameraCaptureSession_capture( return ACAMERA_ERROR_SESSION_CLOSED; } - return session->capture(cbs, numRequests, requests, captureSequenceId); + return session->capture( + cbs, numRequests, requests, captureSequenceId); +} + +EXPORT +camera_status_t ACameraCaptureSession_logicalCamera_capture( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + if (session == nullptr || requests == nullptr || numRequests < 1) { + ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", + __FUNCTION__, session, numRequests, requests); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + if (session->isClosed()) { + ALOGE("%s: session %p is already closed", __FUNCTION__, session); + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + return ACAMERA_ERROR_SESSION_CLOSED; + } + + return session->capture( + lcbs, numRequests, requests, captureSequenceId); } EXPORT @@ -106,6 +132,28 @@ camera_status_t ACameraCaptureSession_setRepeatingRequest( return session->setRepeatingRequest(cbs, numRequests, requests, captureSequenceId); } +EXPORT +camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + if (session == nullptr || requests == nullptr || numRequests < 1) { + ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", + __FUNCTION__, session, numRequests, requests); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + if (session->isClosed()) { + ALOGE("%s: session %p is already closed", __FUNCTION__, session); + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + return ACAMERA_ERROR_SESSION_CLOSED; + } + + return session->setRepeatingRequest(lcbs, numRequests, requests, captureSequenceId); +} + EXPORT camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session) { ATRACE_CALL(); diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp index ef05e0b80f..98608da774 100644 --- a/camera/ndk/NdkCameraDevice.cpp +++ b/camera/ndk/NdkCameraDevice.cpp @@ -128,6 +128,20 @@ camera_status_t ACaptureSessionSharedOutput_create( return ACAMERA_OK; } +EXPORT +camera_status_t ACaptureSessionPhysicalOutput_create( + ACameraWindowType* window, const char* physicalId, + /*out*/ACaptureSessionOutput** out) { + ATRACE_CALL(); + if (window == nullptr || physicalId == nullptr || out == nullptr) { + ALOGE("%s: Error: bad argument. window %p, physicalId %p, out %p", + __FUNCTION__, window, physicalId, out); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + *out = new ACaptureSessionOutput(window, false, physicalId); + return ACAMERA_OK; +} + EXPORT camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out, ACameraWindowType* window) { diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp index 34ec2da100..9a39ed872f 100644 --- a/camera/ndk/NdkCameraMetadata.cpp +++ b/camera/ndk/NdkCameraMetadata.cpp @@ -69,3 +69,20 @@ void ACameraMetadata_free(ACameraMetadata* metadata) { metadata->decStrong((void*) ACameraMetadata_free); } } + +EXPORT +bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata, + /*out*/size_t* numPhysicalCameras, /*out*/const char*const** physicalCameraIds) { + ATRACE_CALL(); + if (numPhysicalCameras == nullptr || physicalCameraIds == nullptr) { + ALOGE("%s: Invalid input: numPhysicalCameras %p, physicalCameraIds %p", + __FUNCTION__, numPhysicalCameras, physicalCameraIds); + return false; + } + if (staticMetadata == nullptr) { + ALOGE("%s: Invalid input: staticMetadata is null.", __FUNCTION__); + return false; + } + + return staticMetadata->isLogicalMultiCamera(numPhysicalCameras, physicalCameraIds); +} diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp index fb72bdbb3c..d6f14122ea 100644 --- a/camera/ndk/impl/ACameraCaptureSession.cpp +++ b/camera/ndk/impl/ACameraCaptureSession.cpp @@ -107,47 +107,6 @@ ACameraCaptureSession::abortCaptures() { return ret; } -camera_status_t -ACameraCaptureSession::setRepeatingRequest( - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - sp dev = getDeviceSp(); - if (dev == nullptr) { - ALOGE("Error: Device associated with session %p has been closed!", this); - return ACAMERA_ERROR_SESSION_CLOSED; - } - - camera_status_t ret; - dev->lockDeviceForSessionOps(); - { - Mutex::Autolock _l(mSessionLock); - ret = dev->setRepeatingRequestsLocked( - this, cbs, numRequests, requests, captureSequenceId); - } - dev->unlockDevice(); - return ret; -} - -camera_status_t ACameraCaptureSession::capture( - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - sp dev = getDeviceSp(); - if (dev == nullptr) { - ALOGE("Error: Device associated with session %p has been closed!", this); - return ACAMERA_ERROR_SESSION_CLOSED; - } - camera_status_t ret; - dev->lockDeviceForSessionOps(); - { - Mutex::Autolock _l(mSessionLock); - ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId); - } - dev->unlockDevice(); - return ret; -} - camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) { sp dev = getDeviceSp(); if (dev == nullptr) { diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h index 133c2c8325..08a922634d 100644 --- a/camera/ndk/impl/ACameraCaptureSession.h +++ b/camera/ndk/impl/ACameraCaptureSession.h @@ -17,6 +17,7 @@ #define _ACAMERA_CAPTURE_SESSION_H #include +#include #include #include @@ -29,8 +30,9 @@ using namespace android; struct ACaptureSessionOutput { - explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false) : - mWindow(window), mIsShared(isShared) {}; + explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false, + const char* physicalCameraId = "") : + mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {}; bool operator == (const ACaptureSessionOutput& other) const { return mWindow == other.mWindow; @@ -49,6 +51,7 @@ struct ACaptureSessionOutput { std::set mSharedWindows; bool mIsShared; int mRotation = CAMERA3_STREAM_ROTATION_0; + std::string mPhysicalCameraId; }; #endif @@ -88,13 +91,15 @@ struct ACameraCaptureSession : public RefBase { camera_status_t abortCaptures(); + template camera_status_t setRepeatingRequest( - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); + template camera_status_t capture( - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); diff --git a/camera/ndk/impl/ACameraCaptureSession.inc b/camera/ndk/impl/ACameraCaptureSession.inc new file mode 100644 index 0000000000..86bf8a5d25 --- /dev/null +++ b/camera/ndk/impl/ACameraCaptureSession.inc @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include "ACameraCaptureSession.h" + +#ifdef __ANDROID_VNDK__ +#include "ndk_vendor/impl/ACameraDeviceVendor.inc" +#else +#include "ACameraDevice.inc" +#endif + +using namespace android; + +template +camera_status_t +ACameraCaptureSession::setRepeatingRequest( + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + sp dev = getDeviceSp(); + if (dev == nullptr) { + ALOGE("Error: Device associated with session %p has been closed!", this); + return ACAMERA_ERROR_SESSION_CLOSED; + } + + camera_status_t ret; + dev->lockDeviceForSessionOps(); + { + Mutex::Autolock _l(mSessionLock); + ret = dev->setRepeatingRequestsLocked( + this, cbs, numRequests, requests, captureSequenceId); + } + dev->unlockDevice(); + return ret; +} + +template +camera_status_t ACameraCaptureSession::capture( + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + sp dev = getDeviceSp(); + if (dev == nullptr) { + ALOGE("Error: Device associated with session %p has been closed!", this); + return ACAMERA_ERROR_SESSION_CLOSED; + } + camera_status_t ret; + dev->lockDeviceForSessionOps(); + { + Mutex::Autolock _l(mSessionLock); + ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId); + } + dev->unlockDevice(); + return ret; +} diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 657d41fa4e..d8a57650c8 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -20,13 +20,14 @@ #include #include #include -#include #include #include "ACameraDevice.h" #include "ACameraMetadata.h" #include "ACaptureRequest.h" #include "ACameraCaptureSession.h" +#include "ACameraCaptureSession.inc" + namespace android { namespace acam { @@ -39,6 +40,7 @@ const char* CameraDevice::kSessionSpKey = "SessionSp"; const char* CameraDevice::kCaptureRequestKey = "CaptureRequest"; const char* CameraDevice::kTimeStampKey = "TimeStamp"; const char* CameraDevice::kCaptureResultKey = "CaptureResult"; +const char* CameraDevice::kPhysicalCaptureResultKey = "PhysicalCaptureResult"; const char* CameraDevice::kCaptureFailureKey = "CaptureFailure"; const char* CameraDevice::kSequenceIdKey = "SequenceId"; const char* CameraDevice::kFrameNumberKey = "FrameNumber"; @@ -190,106 +192,6 @@ CameraDevice::createCaptureSession( return ACAMERA_OK; } -camera_status_t -CameraDevice::captureLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - return submitRequestsLocked( - session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false); -} - -camera_status_t -CameraDevice::setRepeatingRequestsLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - return submitRequestsLocked( - session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true); -} - -camera_status_t -CameraDevice::submitRequestsLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId, - bool isRepeating) { - camera_status_t ret = checkCameraClosedOrErrorLocked(); - if (ret != ACAMERA_OK) { - ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret); - return ret; - } - - // Form two vectors of capture request, one for internal tracking - std::vector requestList; - Vector > requestsV; - requestsV.setCapacity(numRequests); - for (int i = 0; i < numRequests; i++) { - sp req; - ret = allocateCaptureRequest(requests[i], req); - if (ret != ACAMERA_OK) { - ALOGE("Convert capture request to internal format failure! ret %d", ret); - return ret; - } - if (req->mSurfaceList.empty()) { - ALOGE("Capture request without output target cannot be submitted!"); - return ACAMERA_ERROR_INVALID_PARAMETER; - } - requestList.push_back(*(req.get())); - requestsV.push_back(req); - } - - if (isRepeating) { - ret = stopRepeatingLocked(); - if (ret != ACAMERA_OK) { - ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); - return ret; - } - } - - binder::Status remoteRet; - hardware::camera2::utils::SubmitInfo info; - remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info); - int sequenceId = info.mRequestId; - int64_t lastFrameNumber = info.mLastFrameNumber; - if (sequenceId < 0) { - ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId); - return ACAMERA_ERROR_UNKNOWN; - } - - CallbackHolder cbHolder(session, requestsV, isRepeating, cbs); - mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder)); - - if (isRepeating) { - // stopRepeating above should have cleanup repeating sequence id - if (mRepeatingSequenceId != REQUEST_ID_NONE) { - setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); - return ACAMERA_ERROR_CAMERA_DEVICE; - } - mRepeatingSequenceId = sequenceId; - } else { - mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); - } - - if (mIdle) { - sp msg = new AMessage(kWhatSessionStateCb, mHandler); - msg->setPointer(kContextKey, session->mUserSessionCallback.context); - msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); - postSessionMsgAndCleanup(msg); - } - mIdle = false; - mBusySession = session; - - if (captureSequenceId) { - *captureSequenceId = sequenceId; - } - return ACAMERA_OK; -} - camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { camera_status_t ret = checkCameraClosedOrErrorLocked(); if (ret != ACAMERA_OK) { @@ -325,8 +227,9 @@ camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOut return ret; } - OutputConfiguration outConfig(iGBP, output->mRotation, OutputConfiguration::INVALID_SET_ID, - true); + String16 physicalId16(output->mPhysicalCameraId.c_str()); + OutputConfiguration outConfig(iGBP, output->mRotation, physicalId16, + OutputConfiguration::INVALID_SET_ID, true); for (auto& anw : output->mSharedWindows) { ret = getIGBPfromAnw(anw, iGBP); @@ -640,8 +543,9 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu if (ret != ACAMERA_OK) { return ret; } + String16 physicalId16(outConfig.mPhysicalCameraId.c_str()); outputSet.insert(std::make_pair( - anw, OutputConfiguration(iGBP, outConfig.mRotation, + anw, OutputConfiguration(iGBP, outConfig.mRotation, physicalId16, OutputConfiguration::INVALID_SET_ID, outConfig.mIsShared))); } auto addSet = outputSet; @@ -829,7 +733,7 @@ CameraDevice::onCaptureErrorLocked( if (errorCode == hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER) { int32_t streamId = resultExtras.errorStreamId; ACameraCaptureSession_captureCallback_bufferLost onBufferLost = - cbh.mCallbacks.onCaptureBufferLost; + cbh.mOnCaptureBufferLost; auto outputPairIt = mConfiguredOutputs.find(streamId); if (outputPairIt == mConfiguredOutputs.end()) { ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId); @@ -846,7 +750,7 @@ CameraDevice::onCaptureErrorLocked( getId(), anw, frameNumber); sp msg = new AMessage(kWhatCaptureBufferLost, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onBufferLost); msg->setObject(kCaptureRequestKey, request); @@ -858,7 +762,7 @@ CameraDevice::onCaptureErrorLocked( } } else { // Handle other capture failures // Fire capture failure callback if there is one registered - ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed; + ACameraCaptureSession_captureCallback_failed onError = cbh.mOnCaptureFailed; sp failure(new CameraCaptureFailure()); failure->frameNumber = frameNumber; // TODO: refine this when implementing flush @@ -868,7 +772,7 @@ CameraDevice::onCaptureErrorLocked( hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT); sp msg = new AMessage(kWhatCaptureFail, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onError); msg->setObject(kCaptureRequestKey, request); @@ -890,6 +794,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatSessionStateCb: case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: @@ -960,6 +865,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatSessionStateCb: case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: @@ -977,6 +883,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( switch (msg->what()) { case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureBufferLost: found = msg->findObject(kCaptureRequestKey, &obj); @@ -1048,6 +955,64 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatLogicalCaptureResult: + { + ACameraCaptureSession_logicalCamera_captureCallback_result onResult; + found = msg->findPointer(kCallbackFpKey, (void**) &onResult); + if (!found) { + ALOGE("%s: Cannot find logicalCamera capture result callback!", + __FUNCTION__); + return; + } + if (onResult == nullptr) { + return; + } + + found = msg->findObject(kCaptureResultKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture result!", __FUNCTION__); + return; + } + sp result(static_cast(obj.get())); + + found = msg->findObject(kPhysicalCaptureResultKey, &obj); + if (!found) { + ALOGE("%s: Cannot find physical capture result!", __FUNCTION__); + return; + } + sp physicalResult( + static_cast(obj.get())); + std::vector& physicalResultInfo = + physicalResult->mPhysicalResultInfo; + + std::vector physicalCameraIds; + std::vector> physicalMetadataCopy; + for (size_t i = 0; i < physicalResultInfo.size(); i++) { + String8 physicalId8(physicalResultInfo[i].mPhysicalCameraId); + physicalCameraIds.push_back(physicalId8.c_str()); + + CameraMetadata clone = physicalResultInfo[i].mPhysicalCameraMetadata; + clone.update(ANDROID_SYNC_FRAME_NUMBER, + &physicalResult->mFrameNumber, /*data_count*/1); + sp metadata = + new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT); + physicalMetadataCopy.push_back(metadata); + } + + std::vector physicalCameraIdPtrs; + std::vector physicalMetadataCopyPtrs; + for (size_t i = 0; i < physicalResultInfo.size(); i++) { + physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str()); + physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get()); + } + + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onResult)(context, session.get(), request, result.get(), + physicalResultInfo.size(), physicalCameraIdPtrs.data(), + physicalMetadataCopyPtrs.data()); + freeACaptureRequest(request); + break; + } case kWhatCaptureFail: { ACameraCaptureSession_captureCallback_failed onFail; @@ -1158,12 +1123,34 @@ void CameraDevice::CallbackHandler::onMessageReceived( } CameraDevice::CallbackHolder::CallbackHolder( - sp session, - const Vector >& requests, - bool isRepeating, - ACameraCaptureSession_captureCallbacks* cbs) : - mSession(session), mRequests(requests), - mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {} + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacks* cbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(false) { + initCaptureCallbacks(cbs); + + if (cbs != nullptr) { + mOnCaptureCompleted = cbs->onCaptureCompleted; + } +} + +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(true) { + initCaptureCallbacks(lcbs); + + if (lcbs != nullptr) { + mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + } +} void CameraDevice::checkRepeatingSequenceCompleteLocked( @@ -1180,9 +1167,9 @@ CameraDevice::checkRepeatingSequenceCompleteLocked( mSequenceCallbackMap.erase(cbIt); // send seq aborted callback sp msg = new AMessage(kWhatCaptureSeqAbort, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, cbh.mSession); - msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted); + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted); msg->setInt32(kSequenceIdKey, sequenceId); postSessionMsgAndCleanup(msg); } else { @@ -1230,9 +1217,9 @@ CameraDevice::checkAndFireSequenceCompleteLocked() { mSequenceCallbackMap.erase(cbIt); // send seq complete callback sp msg = new AMessage(kWhatCaptureSeqEnd, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, cbh.mSession); - msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted); + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted); msg->setInt32(kSequenceIdKey, sequenceId); msg->setInt64(kFrameNumberKey, lastFrameNumber); @@ -1389,7 +1376,7 @@ CameraDevice::ServiceCallback::onCaptureStarted( auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; - ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted; + ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1398,7 +1385,7 @@ CameraDevice::ServiceCallback::onCaptureStarted( } sp request = cbh.mRequests[burstId]; sp msg = new AMessage(kWhatCaptureStart, dev->mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onStart); msg->setObject(kCaptureRequestKey, request); @@ -1413,7 +1400,6 @@ CameraDevice::ServiceCallback::onResultReceived( const CameraMetadata& metadata, const CaptureResultExtras& resultExtras, const std::vector& physicalResultInfos) { - (void) physicalResultInfos; binder::Status ret = binder::Status::ok(); sp dev = mDevice.promote(); @@ -1449,9 +1435,6 @@ CameraDevice::ServiceCallback::onResultReceived( auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; - ACameraCaptureSession_captureCallback_result onResult = isPartialResult ? - cbh.mCallbacks.onCaptureProgressed : - cbh.mCallbacks.onCaptureCompleted; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1461,13 +1444,27 @@ CameraDevice::ServiceCallback::onResultReceived( sp request = cbh.mRequests[burstId]; sp result(new ACameraMetadata( metadataCopy.release(), ACameraMetadata::ACM_RESULT)); + sp physicalResult( + new ACameraPhysicalCaptureResultInfo(physicalResultInfos, frameNumber)); - sp msg = new AMessage(kWhatCaptureResult, dev->mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + sp msg = new AMessage( + cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult, + dev->mHandler); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onResult); msg->setObject(kCaptureRequestKey, request); msg->setObject(kCaptureResultKey, result); + if (isPartialResult) { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnCaptureProgressed); + } else if (cbh.mIsLogicalCameraCallback) { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnLogicalCameraCaptureCompleted); + msg->setObject(kPhysicalCaptureResultKey, physicalResult); + } else { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnCaptureCompleted); + } dev->postSessionMsgAndCleanup(msg); } diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 8f56d3f2e2..d0f363bfbb 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,16 @@ namespace acam { // Wrap ACameraCaptureFailure so it can be ref-counted struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {}; +// Wrap PhysicalCaptureResultInfo so that it can be ref-counted +struct ACameraPhysicalCaptureResultInfo: public RefBase { + ACameraPhysicalCaptureResultInfo(const std::vector& info, + int64_t frameNumber) : + mPhysicalResultInfo(info), mFrameNumber(frameNumber) {} + + std::vector mPhysicalResultInfo; + int64_t mFrameNumber; +}; + class CameraDevice final : public RefBase { public: CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, @@ -109,19 +120,22 @@ class CameraDevice final : public RefBase { camera_status_t waitUntilIdleLocked(); + template camera_status_t captureLocked(sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); + template camera_status_t setRepeatingRequestsLocked(sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); + template camera_status_t submitRequestsLocked( sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*out*/int* captureSequenceId, bool isRepeating); @@ -192,6 +206,7 @@ class CameraDevice final : public RefBase { // Capture callbacks kWhatCaptureStart, // onCaptureStarted kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted + kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed kWhatCaptureSeqEnd, // onCaptureSequenceCompleted kWhatCaptureSeqAbort, // onCaptureSequenceAborted @@ -207,6 +222,7 @@ class CameraDevice final : public RefBase { static const char* kCaptureRequestKey; static const char* kTimeStampKey; static const char* kCaptureResultKey; + static const char* kPhysicalCaptureResultKey; static const char* kCaptureFailureKey; static const char* kSequenceIdKey; static const char* kFrameNumberKey; @@ -245,19 +261,46 @@ class CameraDevice final : public RefBase { const Vector >& requests, bool isRepeating, ACameraCaptureSession_captureCallbacks* cbs); - - static ACameraCaptureSession_captureCallbacks fillCb( - ACameraCaptureSession_captureCallbacks* cbs) { + CallbackHolder(sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs); + + template + void initCaptureCallbacks(T* cbs) { + mContext = nullptr; + mOnCaptureStarted = nullptr; + mOnCaptureProgressed = nullptr; + mOnCaptureCompleted = nullptr; + mOnLogicalCameraCaptureCompleted = nullptr; + mOnCaptureFailed = nullptr; + mOnCaptureSequenceCompleted = nullptr; + mOnCaptureSequenceAborted = nullptr; + mOnCaptureBufferLost = nullptr; if (cbs != nullptr) { - return *cbs; + mContext = cbs->context; + mOnCaptureStarted = cbs->onCaptureStarted; + mOnCaptureProgressed = cbs->onCaptureProgressed; + mOnCaptureFailed = cbs->onCaptureFailed; + mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; + mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; + mOnCaptureBufferLost = cbs->onCaptureBufferLost; } - return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; } - sp mSession; Vector > mRequests; const bool mIsRepeating; - ACameraCaptureSession_captureCallbacks mCallbacks; + const bool mIsLogicalCameraCallback; + + void* mContext; + ACameraCaptureSession_captureCallback_start mOnCaptureStarted; + ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; + ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; + ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; + ACameraCaptureSession_captureCallback_failed mOnCaptureFailed; + ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted; + ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted; + ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost; }; // sequence id -> callbacks map std::map mSequenceCallbackMap; diff --git a/camera/ndk/impl/ACameraDevice.inc b/camera/ndk/impl/ACameraDevice.inc new file mode 100644 index 0000000000..1fc5352e85 --- /dev/null +++ b/camera/ndk/impl/ACameraDevice.inc @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#include +#include +#include "ACameraDevice.h" +#include "ACameraMetadata.h" +#include "ACaptureRequest.h" +#include "ACameraCaptureSession.h" + +namespace android { +namespace acam { + +template +camera_status_t +CameraDevice::captureLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false); +} + +template +camera_status_t +CameraDevice::setRepeatingRequestsLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true); +} + +template +camera_status_t CameraDevice::submitRequestsLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId, + bool isRepeating) { + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret); + return ret; + } + + // Form two vectors of capture request, one for internal tracking + std::vector requestList; + Vector > requestsV; + requestsV.setCapacity(numRequests); + for (int i = 0; i < numRequests; i++) { + sp req; + ret = allocateCaptureRequest(requests[i], req); + if (ret != ACAMERA_OK) { + ALOGE("Convert capture request to internal format failure! ret %d", ret); + return ret; + } + if (req->mSurfaceList.empty()) { + ALOGE("Capture request without output target cannot be submitted!"); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + requestList.push_back(*(req.get())); + requestsV.push_back(req); + } + + if (isRepeating) { + ret = stopRepeatingLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); + return ret; + } + } + + binder::Status remoteRet; + hardware::camera2::utils::SubmitInfo info; + remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info); + int sequenceId = info.mRequestId; + int64_t lastFrameNumber = info.mLastFrameNumber; + if (sequenceId < 0) { + ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId); + return ACAMERA_ERROR_UNKNOWN; + } + + CallbackHolder cbHolder(session, requestsV, isRepeating, cbs); + mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder)); + + if (isRepeating) { + // stopRepeating above should have cleanup repeating sequence id + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return ACAMERA_ERROR_CAMERA_DEVICE; + } + mRepeatingSequenceId = sequenceId; + } else { + mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); + } + + if (mIdle) { + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, session->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); + postSessionMsgAndCleanup(msg); + } + mIdle = false; + mBusySession = session; + + if (captureSequenceId) { + *captureSequenceId = sequenceId; + } + return ACAMERA_OK; +} + +} // namespace acam +} // namespace android diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index 94b57132cb..c6612337ed 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -50,6 +50,7 @@ ACameraMetadata::isNdkSupportedCapability(int32_t capability) { case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS: case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE: case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT: + case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA: return true; case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING: case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING: @@ -79,11 +80,41 @@ ACameraMetadata::filterUnsupportedFeatures() { uint8_t capability = entry.data.u8[i]; if (isNdkSupportedCapability(capability)) { capabilities.push(capability); + + if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) { + derivePhysicalCameraIds(); + } } } mData.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities); } +void +ACameraMetadata::derivePhysicalCameraIds() { + ACameraMetadata_const_entry entry; + auto ret = getConstEntry(ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, &entry); + if (ret != ACAMERA_OK) { + ALOGE("%s: Get ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS key failed. ret %d", + __FUNCTION__, ret); + return; + } + + const uint8_t* ids = entry.data.u8; + size_t start = 0; + for (size_t i = 0; i < entry.count; ++i) { + if (ids[i] == '\0') { + if (start != i) { + mStaticPhysicalCameraIds.push_back((const char*)ids+start); + } + start = i+1; + } + } + + if (mStaticPhysicalCameraIds.size() < 2) { + ALOGW("%s: Logical multi-camera device only has %zu physical cameras", + __FUNCTION__, mStaticPhysicalCameraIds.size()); + } +} void ACameraMetadata::filterDurations(uint32_t tag) { @@ -309,6 +340,27 @@ ACameraMetadata::getInternalData() const { return mData; } +bool +ACameraMetadata::isLogicalMultiCamera(size_t* count, const char*const** physicalCameraIds) const { + if (mType != ACM_CHARACTERISTICS) { + ALOGE("%s must be called for a static metadata!", __FUNCTION__); + return false; + } + if (count == nullptr || physicalCameraIds == nullptr) { + ALOGE("%s: Invalid input count: %p, physicalCameraIds: %p", __FUNCTION__, + count, physicalCameraIds); + return false; + } + + if (mStaticPhysicalCameraIds.size() >= 2) { + *count = mStaticPhysicalCameraIds.size(); + *physicalCameraIds = mStaticPhysicalCameraIds.data(); + return true; + } + + return false; +} + // TODO: some of key below should be hidden from user // ex: ACAMERA_REQUEST_ID and ACAMERA_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h index f21dbaf1ba..7049c4bab5 100644 --- a/camera/ndk/impl/ACameraMetadata.h +++ b/camera/ndk/impl/ACameraMetadata.h @@ -17,6 +17,7 @@ #define _ACAMERA_METADATA_H #include +#include #include #include @@ -65,6 +66,7 @@ struct ACameraMetadata : public RefBase { /*out*/const uint32_t** tags) const; const CameraMetadata& getInternalData() const; + bool isLogicalMultiCamera(size_t* count, const char* const** physicalCameraIds) const; private: @@ -74,6 +76,7 @@ struct ACameraMetadata : public RefBase { void filterUnsupportedFeatures(); // Hide features not yet supported by NDK void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats void filterDurations(uint32_t tag); // translate hal format to NDK formats + void derivePhysicalCameraIds(); // Derive array of physical ids. template camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) { @@ -112,6 +115,8 @@ struct ACameraMetadata : public RefBase { const ACAMERA_METADATA_TYPE mType; static std::unordered_set sSystemTags; + + std::vector mStaticPhysicalCameraIds; }; #endif // _ACAMERA_METADATA_H diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 1244582364..d13a818648 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -643,6 +643,103 @@ camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* ACaptureSessionOutput* output) __INTRODUCED_IN(28); #endif /* __ANDROID_API__ >= 28 */ +#if __ANDROID_API__ >= 29 +/** + * The definition of final capture result callback with logical multi-camera support. + * + * This has the same functionality as final ACameraCaptureSession_captureCallback_result, with + * added ability to return physical camera result metadata within a logical multi-camera. + * + * For a logical multi-camera, this function will be called with the Id and result metadata + * of the underlying physical cameras, which the corresponding capture request contains targets for. + * If the capture request doesn't contain targets specific to any physical camera, or the current + * camera device isn't a logical multi-camera, physicalResultCount will be 0. + * + * @param context The optional application context provided by user in + * {@link ACameraCaptureSession_captureCallbacks}. + * @param session The camera capture session of interest. + * @param request The capture request of interest. Note that this pointer points to a copy of + * capture request sent by application, so the address is different to what + * application sent but the content will match. This request will be freed by + * framework immediately after this callback returns. + * @param result The capture result metadata reported by camera device. The memory is managed by + * camera framework. Do not access this pointer after this callback returns. + * @param physicalResultCount The number of physical camera result metadata + * @param physicalCameraIds The array of physical camera IDs on which the + * physical result metadata are reported. + * @param physicalResults The array of capture result metadata reported by the + * physical camera devices. + */ +typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_result)( + void* context, ACameraCaptureSession* session, + ACaptureRequest* request, const ACameraMetadata* result, + size_t physicalResultCount, const char** physicalCameraIds, + const ACameraMetadata** physicalResults); + +/** + * This has the same functionality as ACameraCaptureSession_captureCallbacks, + * with the exception that an onLogicalCameraCaptureCompleted callback is + * used, instead of onCaptureCompleted, to support logical multi-camera. + */ +typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks { + /** + * Same as ACameraCaptureSession_captureCallbacks + */ + void* context; + ACameraCaptureSession_captureCallback_start onCaptureStarted; + ACameraCaptureSession_captureCallback_result onCaptureProgressed; + + /** + * This callback is called when an image capture has fully completed and all the + * result metadata is available. For a logical multi-camera, this callback + * also returns the result metadata for all physical cameras being + * explicitly requested on. + * + *

    This callback will always fire after the last {@link onCaptureProgressed}; + * in other words, no more partial results will be delivered once the completed result + * is available.

    + * + *

    For performance-intensive use-cases where latency is a factor, consider + * using {@link onCaptureProgressed} instead.

    + * + *

    Note that the ACaptureRequest pointer in the callback will not match what application has + * submitted, but the contents the ACaptureRequest will match what application submitted.

    + */ + ACameraCaptureSession_logicalCamera_captureCallback_result onLogicalCameraCaptureCompleted; + + /** + * Same as ACameraCaptureSession_captureCallbacks + */ + ACameraCaptureSession_captureCallback_failed onCaptureFailed; + ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted; + ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted; + ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost; +} ACameraCaptureSession_logicalCamera_captureCallbacks; + +/** + * This has the same functionality as ACameraCaptureSession_capture, with added + * support for logical multi-camera where the capture callbacks supports result metadata for + * physical cameras. + */ +camera_status_t ACameraCaptureSession_logicalCamera_capture( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(29); + +/** + * This has the same functionality as ACameraCaptureSession_setRepeatingRequest, with added + * support for logical multi-camera where the capture callbacks supports result metadata for + * physical cameras. + */ +camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif /* _NDK_CAMERA_CAPTURE_SESSION_H */ diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index 4fe43d5278..26af4f8787 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -765,6 +765,36 @@ camera_status_t ACameraDevice_createCaptureSessionWithSessionParameters( #endif /* __ANDROID_API__ >= 28 */ +#if __ANDROID_API__ >= 29 + +/** + * Create a ACaptureSessionOutput object used for streaming from a physical + * camera as part of a logical camera device. + * + *

    The ACaptureSessionOutput is used in {@link ACaptureSessionOutputContainer_add} method to add + * an output {@link ANativeWindow} to ACaptureSessionOutputContainer. Use + * {@link ACaptureSessionOutput_free} to free the object and its memory after application no longer + * needs the {@link ACaptureSessionOutput}.

    + * + * @param anw the {@link ANativeWindow} to be associated with the {@link ACaptureSessionOutput} + * @param physicalId the Id of the physical camera this output is associated + * with. + * @param output the output {@link ACaptureSessionOutput} will be stored here if the + * method call succeeds. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds. The created container will be + * filled in the output argument.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if anw, physicalId or output is NULL.
    + * + * @see ACaptureSessionOutputContainer_add + */ +camera_status_t ACaptureSessionPhysicalOutput_create( + ACameraWindowType* anw, const char* physicalId, + /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif /* _NDK_CAMERA_DEVICE_H */ diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h index 611e270e56..9bbfb83fc8 100644 --- a/camera/ndk/include/camera/NdkCameraMetadata.h +++ b/camera/ndk/include/camera/NdkCameraMetadata.h @@ -233,6 +233,28 @@ void ACameraMetadata_free(ACameraMetadata* metadata) __INTRODUCED_IN(24); #endif /* __ANDROID_API__ >= 24 */ +#if __ANDROID_API__ >= 29 + +/** + * Helper function to check if a camera is logical multi-camera. + * + *

    Check whether a camera device is a logical multi-camera based on its + * static metadata. If it is, also returns its physical sub camera Ids.

    + * + * @param staticMetadata the static metadata of the camera being checked. + * @param numPhysicalCameras returns the number of physical cameras. + * @param physicalCameraIds returns the array of physical camera Ids backing this logical + * camera device. Note that this pointer is only valid + * during the lifetime of the staticMetadata object. + * + * @return true if this is a logical multi-camera, false otherwise. + */ +bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata, + /*out*/size_t* numPhysicalCameras, /*out*/const char* const** physicalCameraIds) + __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif /* _NDK_CAMERA_METADATA_H */ diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index cb474f442f..4bb74cbf6d 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -5551,6 +5551,25 @@ typedef enum acamera_metadata_tag { ACAMERA_DEPTH_START + 5, ACAMERA_DEPTH_END, + /** + *

    String containing the ids of the underlying physical cameras.

    + * + *

    Type: byte[n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    For a logical camera, this is concatenation of all underlying physical camera ids. + * The null terminator for physical camera id must be preserved so that the whole string + * can be tokenized using '\0' to generate list of physical camera ids.

    + *

    For example, if the physical camera ids of the logical camera are "2" and "3", the + * value of this tag will be ['2', '\0', '3', '\0'].

    + *

    The number of physical camera ids must be no less than 2.

    + */ + ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS = // byte[n] + ACAMERA_LOGICAL_MULTI_CAMERA_START, /** *

    The accuracy of frame timestamp synchronization between physical cameras

    * diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index a29e96df34..5a0002290a 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -2,9 +2,11 @@ LIBCAMERA2NDK { global: ACameraCaptureSession_abortCaptures; ACameraCaptureSession_capture; + ACameraCaptureSession_logicalCamera_capture; # introduced=29 ACameraCaptureSession_close; ACameraCaptureSession_getDevice; ACameraCaptureSession_setRepeatingRequest; + ACameraCaptureSession_logicalCamera_setRepeatingRequest; # introduced=29 ACameraCaptureSession_stopRepeating; ACameraCaptureSession_updateSharedOutput; # introduced=28 ACameraDevice_close; @@ -24,6 +26,7 @@ LIBCAMERA2NDK { ACameraMetadata_free; ACameraMetadata_getAllTags; ACameraMetadata_getConstEntry; + ACameraMetadata_isLogicalMultiCamera; # introduced=29 ACameraOutputTarget_create; ACameraOutputTarget_free; ACaptureRequest_addTarget; @@ -48,6 +51,7 @@ LIBCAMERA2NDK { ACaptureSessionSharedOutput_create; # introduced=28 ACaptureSessionSharedOutput_add; # introduced=28 ACaptureSessionSharedOutput_remove; # introduced=28 + ACaptureSessionPhysicalOutput_create; # introduced=29 ACaptureSessionOutput_free; local: *; diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h index 8d9e90cac5..e1af8c1421 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h +++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h @@ -14,11 +14,13 @@ * limitations under the License. */ +#include #include "utils.h" struct ACaptureSessionOutput { - explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false) : - mWindow(window), mIsShared(isShared) {}; + explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false, + const char* physicalCameraId = "") : + mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {}; bool operator == (const ACaptureSessionOutput& other) const { return (mWindow == other.mWindow); @@ -40,6 +42,7 @@ struct ACaptureSessionOutput { std::set mSharedWindows; bool mIsShared; int mRotation = CAMERA3_STREAM_ROTATION_0; + std::string mPhysicalCameraId; }; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 26e6b3c50f..f7863a5eb6 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -29,6 +29,8 @@ #include "ACaptureRequest.h" #include "utils.h" +#include "ACameraCaptureSession.inc" + using namespace android; namespace android { @@ -47,6 +49,7 @@ const char* CameraDevice::kSessionSpKey = "SessionSp"; const char* CameraDevice::kCaptureRequestKey = "CaptureRequest"; const char* CameraDevice::kTimeStampKey = "TimeStamp"; const char* CameraDevice::kCaptureResultKey = "CaptureResult"; +const char* CameraDevice::kPhysicalCaptureResultKey = "PhysicalCaptureResult"; const char* CameraDevice::kCaptureFailureKey = "CaptureFailure"; const char* CameraDevice::kSequenceIdKey = "SequenceId"; const char* CameraDevice::kFrameNumberKey = "FrameNumber"; @@ -206,28 +209,8 @@ CameraDevice::createCaptureSession( return ACAMERA_OK; } -camera_status_t -CameraDevice::captureLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - return submitRequestsLocked( - session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false); -} - -camera_status_t -CameraDevice::setRepeatingRequestsLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId) { - return submitRequestsLocked( - session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true); -} - -void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, - sp &req) { +void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, + sp &req) { CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData(); const camera_metadata_t *camera_metadata = metadataCopy.getAndLock(); HCameraMetadata hCameraMetadata; @@ -237,101 +220,6 @@ void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata)); } -camera_status_t -CameraDevice::submitRequestsLocked( - sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, - int numRequests, ACaptureRequest** requests, - /*optional*/int* captureSequenceId, - bool isRepeating) { - camera_status_t ret = checkCameraClosedOrErrorLocked(); - if (ret != ACAMERA_OK) { - ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret); - return ret; - } - - // Form two vectors of capture request, one for internal tracking - std::vector requestList; - Vector> requestsV; - requestsV.setCapacity(numRequests); - for (int i = 0; i < numRequests; i++) { - sp req; - ret = allocateCaptureRequest(requests[i], req); - // We need to call this method since after submitRequestList is called, - // the request metadata queue might have removed the capture request - // metadata. Therefore we simply add the metadata to its wrapper class, - // so that it can be retrived later. - addRequestSettingsMetadata(requests[i], req); - if (ret != ACAMERA_OK) { - ALOGE("Convert capture request to internal format failure! ret %d", ret); - return ret; - } - if (req->mCaptureRequest.streamAndWindowIds.size() == 0) { - ALOGE("Capture request without output target cannot be submitted!"); - return ACAMERA_ERROR_INVALID_PARAMETER; - } - requestList.push_back(utils::convertToHidl(req.get())); - requestsV.push_back(req); - } - if (isRepeating) { - ret = stopRepeatingLocked(); - if (ret != ACAMERA_OK) { - ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); - return ret; - } - } - - SubmitInfo info; - Status status; - auto remoteRet = mRemote->submitRequestList(requestList, isRepeating, - [&status, &info](auto s, auto &submitInfo) { - status = s; - info = submitInfo; - }); - if (!remoteRet.isOk()) { - ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__, - remoteRet.description().c_str()); - } - if (status != Status::NO_ERROR) { - return utils::convertFromHidl(status); - } - int32_t sequenceId = info.requestId; - int64_t lastFrameNumber = info.lastFrameNumber; - if (sequenceId < 0) { - ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId); - return ACAMERA_ERROR_UNKNOWN; - } - - CallbackHolder cbHolder(session, requestsV, isRepeating, cbs); - mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder)); - - if (isRepeating) { - // stopRepeating above should have cleanup repeating sequence id - if (mRepeatingSequenceId != REQUEST_ID_NONE) { - setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); - return ACAMERA_ERROR_CAMERA_DEVICE; - } - mRepeatingSequenceId = sequenceId; - } else { - mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); - } - - if (mIdle) { - sp msg = new AMessage(kWhatSessionStateCb, mHandler); - msg->setPointer(kContextKey, session->mUserSessionCallback.context); - msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); - postSessionMsgAndCleanup(msg); - } - mIdle = false; - mBusySession = session; - - if (captureSequenceId) { - *captureSequenceId = sequenceId; - } - return ACAMERA_OK; -} - camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { camera_status_t ret = checkCameraClosedOrErrorLocked(); if (ret != ACAMERA_OK) { @@ -365,6 +253,7 @@ camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOut outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing. outConfig.windowHandles.resize(output->mSharedWindows.size() + 1); outConfig.windowHandles[0] = output->mWindow; + outConfig.physicalCameraId = output->mPhysicalCameraId; int i = 1; for (auto& anw : output->mSharedWindows) { outConfig.windowHandles[i++] = anw; @@ -668,6 +557,7 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu outConfigInsert.windowGroupId = -1; outConfigInsert.windowHandles.resize(outConfig.mSharedWindows.size() + 1); outConfigInsert.windowHandles[0] = anw; + outConfigInsert.physicalCameraId = outConfig.mPhysicalCameraId; native_handle_ptr_wrapper wrap(anw); outputSet.insert(std::make_pair(anw, outConfigInsertW)); } @@ -894,7 +784,7 @@ CameraDevice::onCaptureErrorLocked( if (errorCode == ErrorCode::CAMERA_BUFFER) { int32_t streamId = resultExtras.errorStreamId; ACameraCaptureSession_captureCallback_bufferLost onBufferLost = - cbh.mCallbacks.onCaptureBufferLost; + cbh.mOnCaptureBufferLost; auto outputPairIt = mConfiguredOutputs.find(streamId); if (outputPairIt == mConfiguredOutputs.end()) { ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId); @@ -913,7 +803,7 @@ CameraDevice::onCaptureErrorLocked( getId(), anw, frameNumber); sp msg = new AMessage(kWhatCaptureBufferLost, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onBufferLost); msg->setObject(kCaptureRequestKey, request); @@ -925,7 +815,7 @@ CameraDevice::onCaptureErrorLocked( } } else { // Handle other capture failures // Fire capture failure callback if there is one registered - ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed; + ACameraCaptureSession_captureCallback_failed onError = cbh.mOnCaptureFailed; sp failure(new CameraCaptureFailure()); failure->frameNumber = frameNumber; // TODO: refine this when implementing flush @@ -934,7 +824,7 @@ CameraDevice::onCaptureErrorLocked( failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT); sp msg = new AMessage(kWhatCaptureFail, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onError); msg->setObject(kCaptureRequestKey, request); @@ -956,6 +846,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatSessionStateCb: case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: @@ -1026,6 +917,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatSessionStateCb: case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: @@ -1043,6 +935,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( switch (msg->what()) { case kWhatCaptureStart: case kWhatCaptureResult: + case kWhatLogicalCaptureResult: case kWhatCaptureFail: case kWhatCaptureBufferLost: found = msg->findObject(kCaptureRequestKey, &obj); @@ -1114,6 +1007,62 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatLogicalCaptureResult: + { + ACameraCaptureSession_logicalCamera_captureCallback_result onResult; + found = msg->findPointer(kCallbackFpKey, (void**) &onResult); + if (!found) { + ALOGE("%s: Cannot find capture result callback!", __FUNCTION__); + return; + } + if (onResult == nullptr) { + return; + } + + found = msg->findObject(kCaptureResultKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture result!", __FUNCTION__); + return; + } + sp result(static_cast(obj.get())); + + found = msg->findObject(kPhysicalCaptureResultKey, &obj); + if (!found) { + ALOGE("%s: Cannot find physical capture result!", __FUNCTION__); + return; + } + sp physicalResult( + static_cast(obj.get())); + std::vector& physicalResultInfo = + physicalResult->mPhysicalResultInfo; + + std::vector physicalCameraIds; + std::vector> physicalMetadataCopy; + for (size_t i = 0; i < physicalResultInfo.size(); i++) { + physicalCameraIds.push_back(physicalResultInfo[i].physicalCameraId); + + CameraMetadata clone = physicalResultInfo[i].physicalMetadata; + clone.update(ANDROID_SYNC_FRAME_NUMBER, + &physicalResult->mFrameNumber, /*data_count*/1); + sp metadata = + new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT); + physicalMetadataCopy.push_back(metadata); + } + std::vector physicalCameraIdPtrs; + std::vector physicalMetadataCopyPtrs; + for (size_t i = 0; i < physicalResultInfo.size(); i++) { + physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str()); + physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get()); + } + + ACaptureRequest* request = allocateACaptureRequest(requestSp); + (*onResult)(context, session.get(), request, result.get(), + physicalResultInfo.size(), physicalCameraIdPtrs.data(), + physicalMetadataCopyPtrs.data()); + freeACaptureRequest(request); + break; + } + case kWhatCaptureFail: { ACameraCaptureSession_captureCallback_failed onFail; @@ -1224,12 +1173,34 @@ void CameraDevice::CallbackHandler::onMessageReceived( } CameraDevice::CallbackHolder::CallbackHolder( - sp session, - const Vector >& requests, - bool isRepeating, - ACameraCaptureSession_captureCallbacks* cbs) : - mSession(session), mRequests(requests), - mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {} + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacks* cbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(false) { + initCaptureCallbacks(cbs); + + if (cbs != nullptr) { + mOnCaptureCompleted = cbs->onCaptureCompleted; + } +} + +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(true) { + initCaptureCallbacks(lcbs); + + if (lcbs != nullptr) { + mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + } +} void CameraDevice::checkRepeatingSequenceCompleteLocked( @@ -1246,9 +1217,9 @@ CameraDevice::checkRepeatingSequenceCompleteLocked( mSequenceCallbackMap.erase(cbIt); // send seq aborted callback sp msg = new AMessage(kWhatCaptureSeqAbort, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, cbh.mSession); - msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted); + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted); msg->setInt32(kSequenceIdKey, sequenceId); postSessionMsgAndCleanup(msg); } else { @@ -1295,9 +1266,9 @@ CameraDevice::checkAndFireSequenceCompleteLocked() { mSequenceCallbackMap.erase(cbIt); // send seq complete callback sp msg = new AMessage(kWhatCaptureSeqEnd, mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, cbh.mSession); - msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted); + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted); msg->setInt32(kSequenceIdKey, sequenceId); msg->setInt64(kFrameNumberKey, lastFrameNumber); @@ -1454,7 +1425,7 @@ CameraDevice::ServiceCallback::onCaptureStarted( auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; - ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted; + ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1463,7 +1434,7 @@ CameraDevice::ServiceCallback::onCaptureStarted( } sp request = cbh.mRequests[burstId]; sp msg = new AMessage(kWhatCaptureStart, dev->mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); msg->setPointer(kCallbackFpKey, (void*) onStart); msg->setObject(kCaptureRequestKey, request); @@ -1478,7 +1449,6 @@ CameraDevice::ServiceCallback::onResultReceived( const FmqSizeOrMetadata& resultMetadata, const CaptureResultExtras& resultExtras, const hidl_vec& physicalResultInfos) { - (void) physicalResultInfos; auto ret = Void(); sp dev = mDevice.promote(); @@ -1508,27 +1478,10 @@ CameraDevice::ServiceCallback::onResultReceived( } CameraMetadata metadataCopy; - HCameraMetadata hCameraMetadata; - bool converted = false; - if (resultMetadata.getDiscriminator() == - FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) { - hCameraMetadata.resize(resultMetadata.fmqMetadataSize()); - bool read = dev->mCaptureResultMetadataQueue->read(hCameraMetadata.data(), - resultMetadata.fmqMetadataSize()); - if (!read) { - ALOGE("%s capture request settings could't be read from fmq", - __FUNCTION__); - return ret; - } - // TODO: Do we actually need to clone here ? - converted = utils::convertFromHidlCloned(hCameraMetadata, &metadataCopy); - - } else { - converted = utils::convertFromHidlCloned(resultMetadata.metadata(), &metadataCopy); - } - - if (!converted) { - ALOGE("%s result metadata couldn't be converted", __FUNCTION__); + camera_status_t status = readOneResultMetadata(resultMetadata, + dev->mCaptureResultMetadataQueue.get(), &metadataCopy); + if (status != ACAMERA_OK) { + ALOGE("%s: result metadata couldn't be converted", __FUNCTION__); return ret; } @@ -1538,9 +1491,6 @@ CameraDevice::ServiceCallback::onResultReceived( auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; - ACameraCaptureSession_captureCallback_result onResult = isPartialResult ? - cbh.mCallbacks.onCaptureProgressed : - cbh.mCallbacks.onCaptureCompleted; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1551,12 +1501,39 @@ CameraDevice::ServiceCallback::onResultReceived( sp result(new ACameraMetadata( metadataCopy.release(), ACameraMetadata::ACM_RESULT)); - sp msg = new AMessage(kWhatCaptureResult, dev->mHandler); - msg->setPointer(kContextKey, cbh.mCallbacks.context); + std::vector localPhysicalResult; + localPhysicalResult.resize(physicalResultInfos.size()); + for (size_t i = 0; i < physicalResultInfos.size(); i++) { + localPhysicalResult[i].physicalCameraId = physicalResultInfos[i].physicalCameraId; + status = readOneResultMetadata(physicalResultInfos[i].physicalCameraMetadata, + dev->mCaptureResultMetadataQueue.get(), + &localPhysicalResult[i].physicalMetadata); + if (status != ACAMERA_OK) { + ALOGE("%s: physical camera result metadata couldn't be converted", __FUNCTION__); + return ret; + } + } + sp physicalResult( + new ACameraPhysicalCaptureResultInfo(localPhysicalResult, frameNumber)); + + sp msg = new AMessage( + cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult, + dev->mHandler); + msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onResult); msg->setObject(kCaptureRequestKey, request); msg->setObject(kCaptureResultKey, result); + if (isPartialResult) { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnCaptureProgressed); + } else if (cbh.mIsLogicalCameraCallback) { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnLogicalCameraCaptureCompleted); + msg->setObject(kPhysicalCaptureResultKey, physicalResult); + } else { + msg->setPointer(kCallbackFpKey, + (void *)cbh.mOnCaptureCompleted); + } dev->postSessionMsgAndCleanup(msg); } @@ -1590,5 +1567,31 @@ CameraDevice::ServiceCallback::onRepeatingRequestError( return ret; } +camera_status_t CameraDevice::ServiceCallback::readOneResultMetadata( + const FmqSizeOrMetadata& fmqSizeOrMetadata, ResultMetadataQueue* metadataQueue, + CameraMetadata* metadata) { + if (metadataQueue == nullptr || metadata == nullptr) { + return ACAMERA_ERROR_INVALID_PARAMETER; + } + bool converted; + HCameraMetadata hCameraMetadata; + if (fmqSizeOrMetadata.getDiscriminator() == + FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) { + hCameraMetadata.resize(fmqSizeOrMetadata.fmqMetadataSize()); + bool read = metadataQueue->read( + hCameraMetadata.data(), fmqSizeOrMetadata.fmqMetadataSize()); + if (!read) { + ALOGE("%s capture request settings could't be read from fmq", __FUNCTION__); + return ACAMERA_ERROR_UNKNOWN; + } + // TODO: Do we actually need to clone here ? + converted = utils::convertFromHidlCloned(hCameraMetadata, metadata); + } else { + converted = utils::convertFromHidlCloned(fmqSizeOrMetadata.metadata(), metadata); + } + + return converted ? ACAMERA_OK : ACAMERA_ERROR_UNKNOWN; +} + } // namespace acam } // namespace android diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 01a219f01a..c63b97fc0c 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,21 @@ using utils::OutputConfigurationWrapper; // Wrap ACameraCaptureFailure so it can be ref-counted struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure { }; +// Wrap PhysicalCaptureResultInfo so that it can be ref-counted +struct PhysicalCaptureResultInfoLocal { + std::string physicalCameraId; + CameraMetadata physicalMetadata; +}; + +struct ACameraPhysicalCaptureResultInfo: public RefBase { + ACameraPhysicalCaptureResultInfo(const std::vector& info, + int64_t frameNumber) : + mPhysicalResultInfo(info), mFrameNumber(frameNumber) {} + + std::vector mPhysicalResultInfo; + int64_t mFrameNumber; +}; + class CameraDevice final : public RefBase { public: CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb, @@ -99,6 +115,8 @@ class CameraDevice final : public RefBase { android::hardware::Return onRepeatingRequestError(uint64_t lastFrameNumber, int32_t stoppedSequenceId) override; private: + camera_status_t readOneResultMetadata(const FmqSizeOrMetadata& fmqSizeOrMetadata, + ResultMetadataQueue* metadataQueue, CameraMetadata* metadata); const wp mDevice; }; inline sp getServiceCallback() { @@ -127,24 +145,28 @@ class CameraDevice final : public RefBase { camera_status_t waitUntilIdleLocked(); - + template camera_status_t captureLocked(sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); + template camera_status_t setRepeatingRequestsLocked(sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId); + template camera_status_t submitRequestsLocked( sp session, - /*optional*/ACameraCaptureSession_captureCallbacks* cbs, + /*optional*/T* cbs, int numRequests, ACaptureRequest** requests, /*out*/int* captureSequenceId, bool isRepeating); + void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, sp &req); + camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output); camera_status_t allocateCaptureRequest( @@ -206,6 +228,7 @@ class CameraDevice final : public RefBase { // Capture callbacks kWhatCaptureStart, // onCaptureStarted kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted + kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed kWhatCaptureSeqEnd, // onCaptureSequenceCompleted kWhatCaptureSeqAbort, // onCaptureSequenceAborted @@ -221,6 +244,7 @@ class CameraDevice final : public RefBase { static const char* kCaptureRequestKey; static const char* kTimeStampKey; static const char* kCaptureResultKey; + static const char* kPhysicalCaptureResultKey; static const char* kCaptureFailureKey; static const char* kSequenceIdKey; static const char* kFrameNumberKey; @@ -259,19 +283,47 @@ class CameraDevice final : public RefBase { const Vector>& requests, bool isRepeating, ACameraCaptureSession_captureCallbacks* cbs); - - static ACameraCaptureSession_captureCallbacks fillCb( - ACameraCaptureSession_captureCallbacks* cbs) { + CallbackHolder(sp session, + const Vector>& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs); + + template + void initCaptureCallbacks(T* cbs) { + mContext = nullptr; + mOnCaptureStarted = nullptr; + mOnCaptureProgressed = nullptr; + mOnCaptureCompleted = nullptr; + mOnLogicalCameraCaptureCompleted = nullptr; + mOnCaptureFailed = nullptr; + mOnCaptureSequenceCompleted = nullptr; + mOnCaptureSequenceAborted = nullptr; + mOnCaptureBufferLost = nullptr; if (cbs != nullptr) { - return *cbs; + mContext = cbs->context; + mOnCaptureStarted = cbs->onCaptureStarted; + mOnCaptureProgressed = cbs->onCaptureProgressed; + mOnCaptureFailed = cbs->onCaptureFailed; + mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; + mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; + mOnCaptureBufferLost = cbs->onCaptureBufferLost; } - return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; } sp mSession; - Vector> mRequests; + Vector> mRequests; const bool mIsRepeating; - ACameraCaptureSession_captureCallbacks mCallbacks; + const bool mIsLogicalCameraCallback; + + void* mContext; + ACameraCaptureSession_captureCallback_start mOnCaptureStarted; + ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; + ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; + ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; + ACameraCaptureSession_captureCallback_failed mOnCaptureFailed; + ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted; + ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted; + ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost; }; // sequence id -> callbacks map std::map mSequenceCallbackMap; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc new file mode 100644 index 0000000000..7d2304e63e --- /dev/null +++ b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include + +#include "ndk_vendor/impl/ACameraDevice.h" +#include "ACameraCaptureSession.h" +#include "ACameraMetadata.h" +#include "ACaptureRequest.h" +#include "utils.h" + +using namespace android; + +namespace android { +namespace acam { + +template +camera_status_t +CameraDevice::captureLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false); +} + +template +camera_status_t +CameraDevice::setRepeatingRequestsLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + return submitRequestsLocked( + session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true); +} + +template +camera_status_t CameraDevice::submitRequestsLocked( + sp session, + /*optional*/T* cbs, + int numRequests, ACaptureRequest** requests, + /*out*/int* captureSequenceId, + bool isRepeating) +{ + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret); + return ret; + } + + // Form two vectors of capture request, one for internal tracking + std::vector requestList; + Vector> requestsV; + requestsV.setCapacity(numRequests); + for (int i = 0; i < numRequests; i++) { + sp req; + ret = allocateCaptureRequest(requests[i], req); + // We need to call this method since after submitRequestList is called, + // the request metadata queue might have removed the capture request + // metadata. Therefore we simply add the metadata to its wrapper class, + // so that it can be retrieved later. + addRequestSettingsMetadata(requests[i], req); + if (ret != ACAMERA_OK) { + ALOGE("Convert capture request to internal format failure! ret %d", ret); + return ret; + } + if (req->mCaptureRequest.streamAndWindowIds.size() == 0) { + ALOGE("Capture request without output target cannot be submitted!"); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + requestList.push_back(utils::convertToHidl(req.get())); + requestsV.push_back(req); + } + if (isRepeating) { + ret = stopRepeatingLocked(); + if (ret != ACAMERA_OK) { + ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret); + return ret; + } + } + + SubmitInfo info; + Status status; + auto remoteRet = mRemote->submitRequestList(requestList, isRepeating, + [&status, &info](auto s, auto &submitInfo) { + status = s; + info = submitInfo; + }); + if (!remoteRet.isOk()) { + ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__, + remoteRet.description().c_str()); + } + if (status != Status::NO_ERROR) { + return utils::convertFromHidl(status); + } + int32_t sequenceId = info.requestId; + int64_t lastFrameNumber = info.lastFrameNumber; + if (sequenceId < 0) { + ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId); + return ACAMERA_ERROR_UNKNOWN; + } + + CallbackHolder cbHolder(session, requestsV, isRepeating, cbs); + mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder)); + if (isRepeating) { + // stopRepeating above should have cleanup repeating sequence id + if (mRepeatingSequenceId != REQUEST_ID_NONE) { + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); + return ACAMERA_ERROR_CAMERA_DEVICE; + } + mRepeatingSequenceId = sequenceId; + } else { + mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber)); + } + + if (mIdle) { + sp msg = new AMessage(kWhatSessionStateCb, mHandler); + msg->setPointer(kContextKey, session->mUserSessionCallback.context); + msg->setObject(kSessionSpKey, session); + msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive); + postSessionMsgAndCleanup(msg); + } + mIdle = false; + mBusySession = session; + + if (captureSequenceId) { + *captureSequenceId = sequenceId; + } + return ACAMERA_OK; +} + +} // namespace acam +} // namespace android diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp index fa8a7a360a..8534b289ea 100644 --- a/camera/tests/CameraBinderTests.cpp +++ b/camera/tests/CameraBinderTests.cpp @@ -480,7 +480,8 @@ TEST_F(CameraClientBinderTest, CheckBinderCameraDeviceUser) { sp surface(new Surface(gbProducer, /*controlledByApp*/false)); - OutputConfiguration output(gbProducer, /*rotation*/0); + String16 noPhysicalId; + OutputConfiguration output(gbProducer, /*rotation*/0, noPhysicalId); // Can we configure? res = device->beginConfigure(); diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp index e5e5024204..f063506d4b 100644 --- a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp +++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp @@ -139,18 +139,18 @@ void H2BCameraDeviceCallbacks::CallbackHandler::processResultMessage( } CameraMetadataNative &result = resultWrapper->mResult; auto resultExtras = resultWrapper->mResultExtras; - auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos; HCaptureResultExtras hResultExtras = hardware::cameraservice::utils::conversion::convertToHidl(resultExtras); - hidl_vec hPhysicalCaptureResultInfos = - hardware::cameraservice::utils::conversion::convertToHidl( - physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue); // Convert Metadata into HCameraMetadata; FmqSizeOrMetadata hResult; const camera_metadata_t *rawMetadata = result.getAndLock(); converter->convertResultMetadataToHidl(rawMetadata, &hResult); result.unlock(rawMetadata); + auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos; + hidl_vec hPhysicalCaptureResultInfos = + hardware::cameraservice::utils::conversion::convertToHidl( + physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue); auto ret = converter->mBase->onResultReceived(hResult, hResultExtras, hPhysicalCaptureResultInfos); if (!ret.isOk()) { diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index 582ce347f9..a87812b6b4 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -89,8 +89,9 @@ hardware::camera2::params::OutputConfiguration convertFromHidl( for (auto &handle : windowHandles) { iGBPs.push_back(new H2BGraphicBufferProducer(AImageReader_getHGBPFromHandle(handle))); } + String16 physicalCameraId16(hOutputConfiguration.physicalCameraId.c_str()); hardware::camera2::params::OutputConfiguration outputConfiguration( - iGBPs, convertFromHidl(hOutputConfiguration.rotation), + iGBPs, convertFromHidl(hOutputConfiguration.rotation), physicalCameraId16, hOutputConfiguration.windowGroupId, OutputConfiguration::SURFACE_TYPE_UNKNOWN, 0, 0, (windowHandles.size() > 1)); return outputConfiguration; -- GitLab From a308287ea13cbab40ad6892be20b1253b83c0e36 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 19 Dec 2018 14:36:28 -0800 Subject: [PATCH 0646/1530] Remove support for non-NDK extractor formats Remove support for intermediate extractor format versions and leave only the 'V3' format, but remove 'V3' from the names of the classes and structs involved, and reset the version number back to 2 (version 1 is the C++ interface shipped in P, which is no longer supported). Bug: 111407253 Test: build Change-Id: I702080e6bbfbac265967a93945ee97191d463125 --- include/media/MediaExtractorPluginApi.h | 103 +---- include/media/MediaExtractorPluginHelper.h | 391 ++---------------- include/media/MediaTrack.h | 36 -- media/extractors/aac/AACExtractor.cpp | 20 +- media/extractors/aac/AACExtractor.h | 4 +- media/extractors/amr/AMRExtractor.cpp | 20 +- media/extractors/amr/AMRExtractor.h | 4 +- media/extractors/flac/FLACExtractor.cpp | 36 +- media/extractors/flac/FLACExtractor.h | 4 +- media/extractors/midi/MidiExtractor.cpp | 26 +- media/extractors/midi/MidiExtractor.h | 10 +- media/extractors/mkv/MatroskaExtractor.cpp | 32 +- media/extractors/mkv/MatroskaExtractor.h | 4 +- media/extractors/mp3/MP3Extractor.cpp | 20 +- media/extractors/mp3/MP3Extractor.h | 4 +- media/extractors/mp4/MPEG4Extractor.cpp | 24 +- media/extractors/mp4/MPEG4Extractor.h | 4 +- media/extractors/mpeg2/ExtractorBundle.cpp | 14 +- media/extractors/mpeg2/MPEG2PSExtractor.cpp | 16 +- media/extractors/mpeg2/MPEG2PSExtractor.h | 4 +- media/extractors/mpeg2/MPEG2TSExtractor.cpp | 20 +- media/extractors/mpeg2/MPEG2TSExtractor.h | 6 +- media/extractors/ogg/OggExtractor.cpp | 74 ++-- media/extractors/ogg/OggExtractor.h | 4 +- media/extractors/wav/WAVExtractor.cpp | 20 +- media/extractors/wav/WAVExtractor.h | 4 +- .../media/stagefright/MediaBufferBase.h | 6 +- .../media/stagefright/MediaBufferGroup.h | 8 +- media/libstagefright/MediaExtractor.cpp | 110 +---- .../libstagefright/MediaExtractorFactory.cpp | 29 +- media/libstagefright/MediaTrack.cpp | 114 +---- .../media/stagefright/MediaExtractor.h | 51 +-- 32 files changed, 280 insertions(+), 942 deletions(-) diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h index b480bbe54c..854bf83199 100644 --- a/include/media/MediaExtractorPluginApi.h +++ b/include/media/MediaExtractorPluginApi.h @@ -47,33 +47,11 @@ enum CMediaTrackReadOptions : uint32_t { NONBLOCKING = 16 }; -struct CMediaTrack { - void *data; - void (*free)(void *data); - - status_t (*start)(void *data); - status_t (*stop)(void *data); - status_t (*getFormat)(void *data, MetaDataBase &format); - status_t (*read)(void *data, MediaBufferBase **buffer, uint32_t options, int64_t seekPosUs); - bool (*supportsNonBlockingRead)(void *data); -}; - -struct CMediaTrackV2 { - void *data; - void (*free)(void *data); - - media_status_t (*start)(void *data); - media_status_t (*stop)(void *data); - media_status_t (*getFormat)(void *data, AMediaFormat *format); - media_status_t (*read)(void *data, MediaBufferBase **buffer, uint32_t options, int64_t seekPosUs); - bool (*supportsNonBlockingRead)(void *data); -}; - /** - * only use CMediaBufferV3 allocated from the CMediaBufferGroupV3 that is + * only use CMediaBuffer allocated from the CMediaBufferGroup that is * provided to CMediaTrack::start() */ -struct CMediaBufferV3 { +struct CMediaBuffer { void *handle; void (*release)(void *handle); void* (*data)(void *handle); @@ -84,49 +62,32 @@ struct CMediaBufferV3 { AMediaFormat* (*meta_data)(void *handle); }; -struct CMediaBufferGroupV3 { +struct CMediaBufferGroup { void *handle; bool (*init)(void *handle, size_t buffers, size_t buffer_size, size_t growthLimit); void (*add_buffer)(void *handle, size_t size); media_status_t (*acquire_buffer)(void *handle, - CMediaBufferV3 **buffer, bool nonBlocking, size_t requestedSize); + CMediaBuffer **buffer, bool nonBlocking, size_t requestedSize); bool (*has_buffers)(void *handle); }; -struct CMediaTrackV3 { +struct CMediaTrack { void *data; void (*free)(void *data); - media_status_t (*start)(void *data, CMediaBufferGroupV3 *bufferGroup); + media_status_t (*start)(void *data, CMediaBufferGroup *bufferGroup); media_status_t (*stop)(void *data); media_status_t (*getFormat)(void *data, AMediaFormat *format); - media_status_t (*read)(void *data, CMediaBufferV3 **buffer, uint32_t options, int64_t seekPosUs); + media_status_t (*read)(void *data, CMediaBuffer **buffer, uint32_t options, int64_t seekPosUs); bool (*supportsNonBlockingRead)(void *data); }; -struct CMediaExtractorV1 { +struct CMediaExtractor { void *data; void (*free)(void *data); size_t (*countTracks)(void *data); CMediaTrack* (*getTrack)(void *data, size_t index); - status_t (*getTrackMetaData)( - void *data, - MetaDataBase& meta, - size_t index, uint32_t flags); - - status_t (*getMetaData)(void *data, MetaDataBase& meta); - uint32_t (*flags)(void *data); - status_t (*setMediaCas)(void *data, const uint8_t* casToken, size_t size); - const char * (*name)(void *data); -}; - -struct CMediaExtractorV2 { - void *data; - - void (*free)(void *data); - size_t (*countTracks)(void *data); - CMediaTrackV2* (*getTrack)(void *data, size_t index); media_status_t (*getTrackMetaData)( void *data, AMediaFormat *meta, @@ -138,48 +99,19 @@ struct CMediaExtractorV2 { const char * (*name)(void *data); }; -struct CMediaExtractorV3 { - void *data; - - void (*free)(void *data); - size_t (*countTracks)(void *data); - CMediaTrackV3* (*getTrack)(void *data, size_t index); - media_status_t (*getTrackMetaData)( - void *data, - AMediaFormat *meta, - size_t index, uint32_t flags); - - media_status_t (*getMetaData)(void *data, AMediaFormat *meta); - uint32_t (*flags)(void *data); - media_status_t (*setMediaCas)(void *data, const uint8_t* casToken, size_t size); - const char * (*name)(void *data); -}; - -typedef CMediaExtractorV1* (*CreatorFuncV1)(CDataSource *source, void *meta); +typedef CMediaExtractor* (*CreatorFunc)(CDataSource *source, void *meta); typedef void (*FreeMetaFunc)(void *meta); // The sniffer can optionally fill in an opaque object, "meta", that helps // the corresponding extractor initialize its state without duplicating // effort already exerted by the sniffer. If "freeMeta" is given, it will be // called against the opaque object when it is no longer used. -typedef CreatorFuncV1 (*SnifferFuncV1)( - CDataSource *source, float *confidence, - void **meta, FreeMetaFunc *freeMeta); - -typedef CMediaExtractorV2* (*CreatorFuncV2)(CDataSource *source, void *meta); - -typedef CreatorFuncV2 (*SnifferFuncV2)( - CDataSource *source, float *confidence, - void **meta, FreeMetaFunc *freeMeta); - -typedef CMediaExtractorV3* (*CreatorFuncV3)(CDataSource *source, void *meta); - -typedef CreatorFuncV3 (*SnifferFuncV3)( +typedef CreatorFunc (*SnifferFunc)( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta); -typedef CMediaExtractorV1 CMediaExtractor; -typedef CreatorFuncV1 CreatorFunc; +typedef CMediaExtractor CMediaExtractor; +typedef CreatorFunc CreatorFunc; typedef struct { @@ -203,16 +135,17 @@ struct ExtractorDef { const char *extractor_name; union { - SnifferFuncV1 v1; - SnifferFuncV2 v2; - SnifferFuncV3 v3; + SnifferFunc v2; } sniff; }; +// the C++ based API which first shipped in P and is no longer supported const uint32_t EXTRACTORDEF_VERSION_LEGACY = 1; -const uint32_t EXTRACTORDEF_VERSION_CURRENT = 2; -const uint32_t EXTRACTORDEF_VERSION = EXTRACTORDEF_VERSION_LEGACY; +// the first C/NDK based API +const uint32_t EXTRACTORDEF_VERSION_NDK_V1 = 2; + +const uint32_t EXTRACTORDEF_VERSION = EXTRACTORDEF_VERSION_NDK_V1; // each plugin library exports one function of this type typedef ExtractorDef (*GetExtractorDef)(); diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h index 705aa811c5..f4d4da630f 100644 --- a/include/media/MediaExtractorPluginHelper.h +++ b/include/media/MediaExtractorPluginHelper.h @@ -35,155 +35,18 @@ class MetaDataBase; struct MediaTrack; -class MediaTrackHelper { -public: - virtual ~MediaTrackHelper() {}; - virtual status_t start() = 0; - virtual status_t stop() = 0; - virtual status_t getFormat(MetaDataBase& format) = 0; - - class ReadOptions { - public: - enum SeekMode : int32_t { - SEEK_PREVIOUS_SYNC, - SEEK_NEXT_SYNC, - SEEK_CLOSEST_SYNC, - SEEK_CLOSEST, - SEEK_FRAME_INDEX, - }; - - ReadOptions(uint32_t options, int64_t seekPosUs) { - mOptions = options; - mSeekPosUs = seekPosUs; - } - bool getSeekTo(int64_t *time_us, SeekMode *mode) const { - if ((mOptions & CMediaTrackReadOptions::SEEK) == 0) { - return false; - } - *time_us = mSeekPosUs; - *mode = (SeekMode) (mOptions & 7); - return true; - } - bool getNonBlocking() const { - return mOptions & CMediaTrackReadOptions::NONBLOCKING; - } - private: - uint32_t mOptions; - int64_t mSeekPosUs; - }; - - virtual status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL) = 0; - virtual bool supportsNonBlockingRead() { return false; } -}; +class MediaTrackHelper; -inline CMediaTrack *wrap(MediaTrackHelper *track) { - CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack)); - wrapper->data = track; - wrapper->free = [](void *data) -> void { - delete (MediaTrackHelper*)(data); - }; - wrapper->start = [](void *data) -> status_t { - return ((MediaTrackHelper*)data)->start(); - }; - wrapper->stop = [](void *data) -> status_t { - return ((MediaTrackHelper*)data)->stop(); - }; - wrapper->getFormat = [](void *data, MetaDataBase &meta) -> status_t { - return ((MediaTrackHelper*)data)->getFormat(meta); - }; - wrapper->read = [](void *data, MediaBufferBase **buffer, uint32_t options, int64_t seekPosUs) - -> status_t { - MediaTrackHelper::ReadOptions opts(options, seekPosUs); - return ((MediaTrackHelper*)data)->read(buffer, &opts); - }; - wrapper->supportsNonBlockingRead = [](void *data) -> bool { - return ((MediaTrackHelper*)data)->supportsNonBlockingRead(); - }; - return wrapper; -} - - -class MediaTrackHelperV2 { -public: - virtual ~MediaTrackHelperV2() {}; - virtual media_status_t start() = 0; - virtual media_status_t stop() = 0; - virtual media_status_t getFormat(AMediaFormat *format) = 0; - - class ReadOptions { - public: - enum SeekMode : int32_t { - SEEK_PREVIOUS_SYNC, - SEEK_NEXT_SYNC, - SEEK_CLOSEST_SYNC, - SEEK_CLOSEST, - SEEK_FRAME_INDEX, - }; - - ReadOptions(uint32_t options, int64_t seekPosUs) { - mOptions = options; - mSeekPosUs = seekPosUs; - } - bool getSeekTo(int64_t *time_us, SeekMode *mode) const { - if ((mOptions & CMediaTrackReadOptions::SEEK) == 0) { - return false; - } - *time_us = mSeekPosUs; - *mode = (SeekMode) (mOptions & 7); - return true; - } - bool getNonBlocking() const { - return mOptions & CMediaTrackReadOptions::NONBLOCKING; - } - private: - uint32_t mOptions; - int64_t mSeekPosUs; - }; - - virtual media_status_t read( - MediaBufferBase **buffer, const ReadOptions *options = NULL) = 0; - virtual bool supportsNonBlockingRead() { return false; } -}; - -inline CMediaTrackV2 *wrapV2(MediaTrackHelperV2 *track) { - CMediaTrackV2 *wrapper = (CMediaTrackV2*) malloc(sizeof(CMediaTrackV2)); - wrapper->data = track; - wrapper->free = [](void *data) -> void { - delete (MediaTrackHelperV2*)(data); - }; - wrapper->start = [](void *data) -> media_status_t { - return ((MediaTrackHelperV2*)data)->start(); - }; - wrapper->stop = [](void *data) -> media_status_t { - return ((MediaTrackHelperV2*)data)->stop(); - }; - wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t { - return ((MediaTrackHelperV2*)data)->getFormat(meta); - }; - wrapper->read = [](void *data, MediaBufferBase **buffer, uint32_t options, int64_t seekPosUs) - -> media_status_t { - MediaTrackHelperV2::ReadOptions opts(options, seekPosUs); - return ((MediaTrackHelperV2*)data)->read(buffer, &opts); - }; - wrapper->supportsNonBlockingRead = [](void *data) -> bool { - return ((MediaTrackHelperV2*)data)->supportsNonBlockingRead(); - }; - return wrapper; -} - -class MediaTrackHelperV3; - -class MediaBufferHelperV3 { +class MediaBufferHelper { private: - friend CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *); - CMediaBufferV3 *mBuffer; + friend CMediaTrack *wrap(MediaTrackHelper *); + CMediaBuffer *mBuffer; public: - MediaBufferHelperV3(CMediaBufferV3 *buf) { + MediaBufferHelper(CMediaBuffer *buf) { mBuffer = buf; } - virtual ~MediaBufferHelperV3() {} + virtual ~MediaBufferHelper() {} virtual void release() { mBuffer->release(mBuffer->handle); @@ -213,15 +76,15 @@ public: } }; -class MediaBufferGroupHelperV3 { +class MediaBufferGroupHelper { private: - CMediaBufferGroupV3 *mGroup; - std::map mBufferHelpers; + CMediaBufferGroup *mGroup; + std::map mBufferHelpers; public: - MediaBufferGroupHelperV3(CMediaBufferGroupV3 *group) { + MediaBufferGroupHelper(CMediaBufferGroup *group) { mGroup = group; } - ~MediaBufferGroupHelperV3() { + ~MediaBufferGroupHelper() { // delete all entries in map ALOGV("buffergroup %p map has %zu entries", this, mBufferHelpers.size()); for (auto it = mBufferHelpers.begin(); it != mBufferHelpers.end(); ++it) { @@ -235,14 +98,14 @@ public: mGroup->add_buffer(mGroup->handle, size); } media_status_t acquire_buffer( - MediaBufferHelperV3 **buffer, bool nonBlocking = false, size_t requestedSize = 0) { - CMediaBufferV3 *buf = nullptr; + MediaBufferHelper **buffer, bool nonBlocking = false, size_t requestedSize = 0) { + CMediaBuffer *buf = nullptr; media_status_t ret = mGroup->acquire_buffer(mGroup->handle, &buf, nonBlocking, requestedSize); if (ret == AMEDIA_OK && buf != nullptr) { auto helper = mBufferHelpers.find(buf); if (helper == mBufferHelpers.end()) { - MediaBufferHelperV3* newHelper = new MediaBufferHelperV3(buf); + MediaBufferHelper* newHelper = new MediaBufferHelper(buf); mBufferHelpers.insert(std::make_pair(buf, newHelper)); *buffer = newHelper; } else { @@ -258,11 +121,11 @@ public: } }; -class MediaTrackHelperV3 { +class MediaTrackHelper { public: - MediaTrackHelperV3() : mBufferGroup(nullptr) { + MediaTrackHelper() : mBufferGroup(nullptr) { } - virtual ~MediaTrackHelperV3() { + virtual ~MediaTrackHelper() { delete mBufferGroup; } virtual media_status_t start() = 0; @@ -300,45 +163,45 @@ public: }; virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL) = 0; + MediaBufferHelper **buffer, const ReadOptions *options = NULL) = 0; virtual bool supportsNonBlockingRead() { return false; } protected: - friend CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *track); - MediaBufferGroupHelperV3 *mBufferGroup; + friend CMediaTrack *wrap(MediaTrackHelper *track); + MediaBufferGroupHelper *mBufferGroup; }; -inline CMediaTrackV3 *wrapV3(MediaTrackHelperV3 *track) { - CMediaTrackV3 *wrapper = (CMediaTrackV3*) malloc(sizeof(CMediaTrackV3)); +inline CMediaTrack *wrap(MediaTrackHelper *track) { + CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack)); wrapper->data = track; wrapper->free = [](void *data) -> void { - delete (MediaTrackHelperV3*)(data); + delete (MediaTrackHelper*)(data); }; - wrapper->start = [](void *data, CMediaBufferGroupV3 *bufferGroup) -> media_status_t { - if (((MediaTrackHelperV3*)data)->mBufferGroup) { + wrapper->start = [](void *data, CMediaBufferGroup *bufferGroup) -> media_status_t { + if (((MediaTrackHelper*)data)->mBufferGroup) { // this shouldn't happen, but handle it anyway - delete ((MediaTrackHelperV3*)data)->mBufferGroup; + delete ((MediaTrackHelper*)data)->mBufferGroup; } - ((MediaTrackHelperV3*)data)->mBufferGroup = new MediaBufferGroupHelperV3(bufferGroup); - return ((MediaTrackHelperV3*)data)->start(); + ((MediaTrackHelper*)data)->mBufferGroup = new MediaBufferGroupHelper(bufferGroup); + return ((MediaTrackHelper*)data)->start(); }; wrapper->stop = [](void *data) -> media_status_t { - return ((MediaTrackHelperV3*)data)->stop(); + return ((MediaTrackHelper*)data)->stop(); }; wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t { - return ((MediaTrackHelperV3*)data)->getFormat(meta); + return ((MediaTrackHelper*)data)->getFormat(meta); }; - wrapper->read = [](void *data, CMediaBufferV3 **buffer, uint32_t options, int64_t seekPosUs) + wrapper->read = [](void *data, CMediaBuffer **buffer, uint32_t options, int64_t seekPosUs) -> media_status_t { - MediaTrackHelperV3::ReadOptions opts(options, seekPosUs); - MediaBufferHelperV3 *buf = NULL; - media_status_t ret = ((MediaTrackHelperV3*)data)->read(&buf, &opts); + MediaTrackHelper::ReadOptions opts(options, seekPosUs); + MediaBufferHelper *buf = NULL; + media_status_t ret = ((MediaTrackHelper*)data)->read(&buf, &opts); if (ret == AMEDIA_OK && buf != nullptr) { *buffer = buf->mBuffer; } return ret; }; wrapper->supportsNonBlockingRead = [](void *data) -> bool { - return ((MediaTrackHelperV3*)data)->supportsNonBlockingRead(); + return ((MediaTrackHelper*)data)->supportsNonBlockingRead(); }; return wrapper; } @@ -356,13 +219,13 @@ public: enum GetTrackMetaDataFlags { kIncludeExtensiveMetaData = 1 }; - virtual status_t getTrackMetaData( - MetaDataBase& meta, + virtual media_status_t getTrackMetaData( + AMediaFormat *meta, size_t index, uint32_t flags = 0) = 0; // Return container specific meta-data. The default implementation // returns an empty metadata object. - virtual status_t getMetaData(MetaDataBase& meta) = 0; + virtual media_status_t getMetaData(AMediaFormat *meta) = 0; enum Flags { CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" @@ -377,8 +240,8 @@ public: return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE; }; - virtual status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { - return INVALID_OPERATION; + virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { + return AMEDIA_ERROR_INVALID_OPERATION; } virtual const char * name() { return ""; } @@ -405,13 +268,13 @@ inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) { }; wrapper->getTrackMetaData = []( void *data, - MetaDataBase& meta, - size_t index, uint32_t flags) -> status_t { + AMediaFormat *meta, + size_t index, uint32_t flags) -> media_status_t { return ((MediaExtractorPluginHelper*)data)->getTrackMetaData(meta, index, flags); }; wrapper->getMetaData = []( void *data, - MetaDataBase& meta) -> status_t { + AMediaFormat *meta) -> media_status_t { return ((MediaExtractorPluginHelper*)data)->getMetaData(meta); }; wrapper->flags = []( @@ -419,7 +282,7 @@ inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) { return ((MediaExtractorPluginHelper*)data)->flags(); }; wrapper->setMediaCas = []( - void *data, const uint8_t *casToken, size_t size) -> status_t { + void *data, const uint8_t *casToken, size_t size) -> media_status_t { return ((MediaExtractorPluginHelper*)data)->setMediaCas(casToken, size); }; wrapper->name = []( @@ -429,172 +292,6 @@ inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) { return wrapper; } -class MediaExtractorPluginHelperV2 -{ -public: - virtual ~MediaExtractorPluginHelperV2() {} - virtual size_t countTracks() = 0; - virtual MediaTrackHelperV2 *getTrack(size_t index) = 0; - - enum GetTrackMetaDataFlags { - kIncludeExtensiveMetaData = 1 - }; - virtual media_status_t getTrackMetaData( - AMediaFormat *meta, - size_t index, uint32_t flags = 0) = 0; - - // Return container specific meta-data. The default implementation - // returns an empty metadata object. - virtual media_status_t getMetaData(AMediaFormat *meta) = 0; - - enum Flags { - CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" - CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button" - CAN_PAUSE = 4, - CAN_SEEK = 8, // the "seek bar" - }; - - // If subclasses do _not_ override this, the default is - // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE - virtual uint32_t flags() const { - return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE; - }; - - virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { - return AMEDIA_ERROR_INVALID_OPERATION; - } - - virtual const char * name() { return ""; } - -protected: - MediaExtractorPluginHelperV2() {} - -private: - MediaExtractorPluginHelperV2(const MediaExtractorPluginHelperV2 &); - MediaExtractorPluginHelperV2 &operator=(const MediaExtractorPluginHelperV2 &); -}; - -inline CMediaExtractorV2 *wrapV2(MediaExtractorPluginHelperV2 *extractor) { - CMediaExtractorV2 *wrapper = (CMediaExtractorV2*) malloc(sizeof(CMediaExtractorV2)); - wrapper->data = extractor; - wrapper->free = [](void *data) -> void { - delete (MediaExtractorPluginHelperV2*)(data); - }; - wrapper->countTracks = [](void *data) -> size_t { - return ((MediaExtractorPluginHelperV2*)data)->countTracks(); - }; - wrapper->getTrack = [](void *data, size_t index) -> CMediaTrackV2* { - return wrapV2(((MediaExtractorPluginHelperV2*)data)->getTrack(index)); - }; - wrapper->getTrackMetaData = []( - void *data, - AMediaFormat *meta, - size_t index, uint32_t flags) -> media_status_t { - return ((MediaExtractorPluginHelperV2*)data)->getTrackMetaData(meta, index, flags); - }; - wrapper->getMetaData = []( - void *data, - AMediaFormat *meta) -> media_status_t { - return ((MediaExtractorPluginHelperV2*)data)->getMetaData(meta); - }; - wrapper->flags = []( - void *data) -> uint32_t { - return ((MediaExtractorPluginHelperV2*)data)->flags(); - }; - wrapper->setMediaCas = []( - void *data, const uint8_t *casToken, size_t size) -> media_status_t { - return ((MediaExtractorPluginHelperV2*)data)->setMediaCas(casToken, size); - }; - wrapper->name = []( - void *data) -> const char * { - return ((MediaExtractorPluginHelperV2*)data)->name(); - }; - return wrapper; -} - -class MediaExtractorPluginHelperV3 -{ -public: - virtual ~MediaExtractorPluginHelperV3() {} - virtual size_t countTracks() = 0; - virtual MediaTrackHelperV3 *getTrack(size_t index) = 0; - - enum GetTrackMetaDataFlags { - kIncludeExtensiveMetaData = 1 - }; - virtual media_status_t getTrackMetaData( - AMediaFormat *meta, - size_t index, uint32_t flags = 0) = 0; - - // Return container specific meta-data. The default implementation - // returns an empty metadata object. - virtual media_status_t getMetaData(AMediaFormat *meta) = 0; - - enum Flags { - CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" - CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button" - CAN_PAUSE = 4, - CAN_SEEK = 8, // the "seek bar" - }; - - // If subclasses do _not_ override this, the default is - // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE - virtual uint32_t flags() const { - return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE; - }; - - virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) { - return AMEDIA_ERROR_INVALID_OPERATION; - } - - virtual const char * name() { return ""; } - -protected: - MediaExtractorPluginHelperV3() {} - -private: - MediaExtractorPluginHelperV3(const MediaExtractorPluginHelperV2 &); - MediaExtractorPluginHelperV3 &operator=(const MediaExtractorPluginHelperV2 &); -}; - -inline CMediaExtractorV3 *wrapV3(MediaExtractorPluginHelperV3 *extractor) { - CMediaExtractorV3 *wrapper = (CMediaExtractorV3*) malloc(sizeof(CMediaExtractorV3)); - wrapper->data = extractor; - wrapper->free = [](void *data) -> void { - delete (MediaExtractorPluginHelperV3*)(data); - }; - wrapper->countTracks = [](void *data) -> size_t { - return ((MediaExtractorPluginHelperV3*)data)->countTracks(); - }; - wrapper->getTrack = [](void *data, size_t index) -> CMediaTrackV3* { - return wrapV3(((MediaExtractorPluginHelperV3*)data)->getTrack(index)); - }; - wrapper->getTrackMetaData = []( - void *data, - AMediaFormat *meta, - size_t index, uint32_t flags) -> media_status_t { - return ((MediaExtractorPluginHelperV3*)data)->getTrackMetaData(meta, index, flags); - }; - wrapper->getMetaData = []( - void *data, - AMediaFormat *meta) -> media_status_t { - return ((MediaExtractorPluginHelperV3*)data)->getMetaData(meta); - }; - wrapper->flags = []( - void *data) -> uint32_t { - return ((MediaExtractorPluginHelperV3*)data)->flags(); - }; - wrapper->setMediaCas = []( - void *data, const uint8_t *casToken, size_t size) -> media_status_t { - return ((MediaExtractorPluginHelperV3*)data)->setMediaCas(casToken, size); - }; - wrapper->name = []( - void *data) -> const char * { - return ((MediaExtractorPluginHelperV3*)data)->name(); - }; - return wrapper; -} - /* adds some convience methods */ class DataSourceHelper { public: diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h index baa3410cb4..e828a7f81e 100644 --- a/include/media/MediaTrack.h +++ b/include/media/MediaTrack.h @@ -156,42 +156,6 @@ protected: private: CMediaTrack *wrapper; -}; - -class MediaTrackCUnwrapperV2 : public MediaTrack { -public: - explicit MediaTrackCUnwrapperV2(CMediaTrackV2 *wrapper); - - virtual status_t start(); - virtual status_t stop(); - virtual status_t getFormat(MetaDataBase& format); - virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL); - - virtual bool supportNonblockingRead(); - -protected: - virtual ~MediaTrackCUnwrapperV2(); - -private: - CMediaTrackV2 *wrapper; -}; - -class MediaTrackCUnwrapperV3 : public MediaTrack { -public: - explicit MediaTrackCUnwrapperV3(CMediaTrackV3 *wrapper); - - virtual status_t start(); - virtual status_t stop(); - virtual status_t getFormat(MetaDataBase& format); - virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL); - - virtual bool supportNonblockingRead(); - -protected: - virtual ~MediaTrackCUnwrapperV3(); - -private: - CMediaTrackV3 *wrapper; MediaBufferGroup *bufferGroup; }; diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index 9384ebf0ec..4e9ac6e978 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -31,7 +31,7 @@ namespace android { -class AACSource : public MediaTrackHelperV3 { +class AACSource : public MediaTrackHelper { public: AACSource( DataSourceHelper *source, @@ -45,7 +45,7 @@ public: virtual media_status_t getFormat(AMediaFormat*); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~AACSource(); @@ -195,7 +195,7 @@ size_t AACExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV3 *AACExtractor::getTrack(size_t index) { +MediaTrackHelper *AACExtractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -264,7 +264,7 @@ media_status_t AACSource::getFormat(AMediaFormat *meta) { } media_status_t AACSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -287,7 +287,7 @@ media_status_t AACSource::read( return AMEDIA_ERROR_END_OF_STREAM; } - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; @@ -316,14 +316,14 @@ media_status_t AACSource::read( //////////////////////////////////////////////////////////////////////////////// -static CMediaExtractorV3* CreateExtractor( +static CMediaExtractor* CreateExtractor( CDataSource *source, void *meta) { off64_t offset = *static_cast(meta); - return wrapV3(new AACExtractor(new DataSourceHelper(source), offset)); + return wrap(new AACExtractor(new DataSourceHelper(source), offset)); } -static CreatorFuncV3 Sniff( +static CreatorFunc Sniff( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; @@ -383,11 +383,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("4fd80eae-03d2-4d72-9eb9-48fa6bb54613"), 1, // version "AAC Extractor", - { .v3 = Sniff } + { .v2 = Sniff } }; } diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h index be33bf566e..643d3f41d4 100644 --- a/media/extractors/aac/AACExtractor.h +++ b/media/extractors/aac/AACExtractor.h @@ -29,12 +29,12 @@ namespace android { struct AMessage; class String8; -class AACExtractor : public MediaExtractorPluginHelperV3 { +class AACExtractor : public MediaExtractorPluginHelper { public: AACExtractor(DataSourceHelper *source, off64_t offset); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index 7fd2a417ec..00d2a9218c 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -29,7 +29,7 @@ namespace android { -class AMRSource : public MediaTrackHelperV3 { +class AMRSource : public MediaTrackHelper { public: AMRSource( DataSourceHelper *source, @@ -44,7 +44,7 @@ public: virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~AMRSource(); @@ -209,7 +209,7 @@ size_t AMRExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV3 *AMRExtractor::getTrack(size_t index) { +MediaTrackHelper *AMRExtractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -273,7 +273,7 @@ media_status_t AMRSource::getFormat(AMediaFormat *meta) { } media_status_t AMRSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -322,7 +322,7 @@ media_status_t AMRSource::read( return AMEDIA_ERROR_MALFORMED; } - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; @@ -363,22 +363,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("c86639c9-2f31-40ac-a715-fa01b4493aaf"), 1, "AMR Extractor", { - .v3 = []( + .v2 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV3 { + FreeMetaFunc *) -> CreatorFunc { DataSourceHelper helper(source); if (SniffAMR(&helper, nullptr, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new AMRExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractor* { + return wrap(new AMRExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h index b50ce8122a..b76ee9c613 100644 --- a/media/extractors/amr/AMRExtractor.h +++ b/media/extractors/amr/AMRExtractor.h @@ -29,12 +29,12 @@ struct AMessage; class String8; #define OFFSET_TABLE_LEN 300 -class AMRExtractor : public MediaExtractorPluginHelperV3 { +class AMRExtractor : public MediaExtractorPluginHelper { public: explicit AMRExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 4e04605947..b5eaf9bb59 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -52,7 +52,7 @@ static inline bool shouldExtractorOutputFloat(int bitsPerSample) class FLACParser; -class FLACSource : public MediaTrackHelperV3 { +class FLACSource : public MediaTrackHelper { public: FLACSource( @@ -65,7 +65,7 @@ public: virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~FLACSource(); @@ -124,12 +124,12 @@ public: } // media buffers - void allocateBuffers(MediaBufferGroupHelperV3 *group); + void allocateBuffers(MediaBufferGroupHelper *group); void releaseBuffers(); - MediaBufferHelperV3 *readBuffer() { + MediaBufferHelper *readBuffer() { return readBuffer(false, 0LL); } - MediaBufferHelperV3 *readBuffer(FLAC__uint64 sample) { + MediaBufferHelper *readBuffer(FLAC__uint64 sample) { return readBuffer(true, sample); } @@ -142,7 +142,7 @@ private: // media buffers size_t mMaxBufferSize; - MediaBufferGroupHelperV3 *mGroup; + MediaBufferGroupHelper *mGroup; void (*mCopy)(int16_t *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels); // handle to underlying libFLAC parser @@ -166,7 +166,7 @@ private: FLAC__StreamDecoderErrorStatus mErrorStatus; status_t init(); - MediaBufferHelperV3 *readBuffer(bool doSeek, FLAC__uint64 sample); + MediaBufferHelper *readBuffer(bool doSeek, FLAC__uint64 sample); // no copy constructor or assignment FLACParser(const FLACParser &); @@ -576,7 +576,7 @@ status_t FLACParser::init() return OK; } -void FLACParser::allocateBuffers(MediaBufferGroupHelperV3 *group) +void FLACParser::allocateBuffers(MediaBufferGroupHelper *group) { CHECK(mGroup == NULL); mGroup = group; @@ -588,7 +588,7 @@ void FLACParser::releaseBuffers() { } -MediaBufferHelperV3 *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) +MediaBufferHelper *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) { mWriteRequested = true; mWriteCompleted = false; @@ -625,7 +625,7 @@ MediaBufferHelperV3 *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) } // acquire a media buffer CHECK(mGroup != NULL); - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { return NULL; @@ -716,9 +716,9 @@ media_status_t FLACSource::getFormat(AMediaFormat *meta) } media_status_t FLACSource::read( - MediaBufferHelperV3 **outBuffer, const ReadOptions *options) + MediaBufferHelper **outBuffer, const ReadOptions *options) { - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; // process an optional seek request int64_t seekTimeUs; ReadOptions::SeekMode mode; @@ -772,7 +772,7 @@ size_t FLACExtractor::countTracks() return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV3 *FLACExtractor::getTrack(size_t index) +MediaTrackHelper *FLACExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; @@ -828,22 +828,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("1364b048-cc45-4fda-9934-327d0ebf9829"), 1, "FLAC Extractor", { - .v3 = []( + .v2 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV3 { + FreeMetaFunc *) -> CreatorFunc { DataSourceHelper helper(source); if (SniffFLAC(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new FLACExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractor* { + return wrap(new FLACExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h index 9604e4ac4e..5a73d20366 100644 --- a/media/extractors/flac/FLACExtractor.h +++ b/media/extractors/flac/FLACExtractor.h @@ -27,13 +27,13 @@ namespace android { class FLACParser; -class FLACExtractor : public MediaExtractorPluginHelperV3 { +class FLACExtractor : public MediaExtractorPluginHelper { public: explicit FLACExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index 1ddcfbbfe8..0c74376c6d 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -32,7 +32,7 @@ namespace android { // how many Sonivox output buffers to aggregate into one MediaBuffer static const int NUM_COMBINE_BUFFERS = 4; -class MidiSource : public MediaTrackHelperV3 { +class MidiSource : public MediaTrackHelper { public: MidiSource( @@ -44,7 +44,7 @@ public: virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~MidiSource(); @@ -113,10 +113,10 @@ media_status_t MidiSource::getFormat(AMediaFormat *meta) } media_status_t MidiSource::read( - MediaBufferHelperV3 **outBuffer, const ReadOptions *options) + MediaBufferHelper **outBuffer, const ReadOptions *options) { ALOGV("MidiSource::read"); - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; // process an optional seek request int64_t seekTimeUs; ReadOptions::SeekMode mode; @@ -199,7 +199,7 @@ status_t MidiEngine::initCheck() { return mIsInitialized ? OK : UNKNOWN_ERROR; } -status_t MidiEngine::allocateBuffers(MediaBufferGroupHelperV3 *group) { +status_t MidiEngine::allocateBuffers(MediaBufferGroupHelper *group) { // select reverb preset and enable EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, EAS_PARAM_REVERB_CHAMBER); EAS_SetParameter(mEasData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, EAS_FALSE); @@ -222,13 +222,13 @@ status_t MidiEngine::seekTo(int64_t positionUs) { return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR; } -MediaBufferHelperV3* MidiEngine::readBuffer() { +MediaBufferHelper* MidiEngine::readBuffer() { EAS_STATE state; EAS_State(mEasData, mEasHandle, &state); if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) { return NULL; } - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { ALOGE("readBuffer: no buffer"); @@ -287,7 +287,7 @@ size_t MidiExtractor::countTracks() return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV3 *MidiExtractor::getTrack(size_t index) +MediaTrackHelper *MidiExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; @@ -332,21 +332,21 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("ef6cca0a-f8a2-43e6-ba5f-dfcd7c9a7ef2"), 1, "MIDI Extractor", { - .v3 = []( + .v2 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV3 { + FreeMetaFunc *) -> CreatorFunc { if (SniffMidi(source, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new MidiExtractor(source));}; + void *) -> CMediaExtractor* { + return wrap(new MidiExtractor(source));}; } return NULL; } diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h index ad345b8b1a..2e78086065 100644 --- a/media/extractors/midi/MidiExtractor.h +++ b/media/extractors/midi/MidiExtractor.h @@ -38,26 +38,26 @@ public: status_t initCheck(); - status_t allocateBuffers(MediaBufferGroupHelperV3 *group); + status_t allocateBuffers(MediaBufferGroupHelper *group); status_t releaseBuffers(); status_t seekTo(int64_t positionUs); - MediaBufferHelperV3* readBuffer(); + MediaBufferHelper* readBuffer(); private: MidiIoWrapper *mIoWrapper; - MediaBufferGroupHelperV3 *mGroup; + MediaBufferGroupHelper *mGroup; EAS_DATA_HANDLE mEasData; EAS_HANDLE mEasHandle; const S_EAS_LIB_CONFIG* mEasConfig; bool mIsInitialized; }; -class MidiExtractor : public MediaExtractorPluginHelperV3 { +class MidiExtractor : public MediaExtractorPluginHelper { public: explicit MidiExtractor(CDataSource *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 4a307407e8..42a9c42107 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -125,7 +125,7 @@ private: BlockIterator &operator=(const BlockIterator &); }; -struct MatroskaSource : public MediaTrackHelperV3 { +struct MatroskaSource : public MediaTrackHelper { MatroskaSource(MatroskaExtractor *extractor, size_t index); virtual media_status_t start(); @@ -134,7 +134,7 @@ struct MatroskaSource : public MediaTrackHelperV3 { virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options); + MediaBufferHelper **buffer, const ReadOptions *options); protected: virtual ~MatroskaSource(); @@ -154,11 +154,11 @@ private: BlockIterator mBlockIter; ssize_t mNALSizeLen; // for type AVC or HEVC - List mPendingFrames; + List mPendingFrames; status_t advance(); - status_t setWebmBlockCryptoInfo(MediaBufferHelperV3 *mbuf); + status_t setWebmBlockCryptoInfo(MediaBufferHelper *mbuf); media_status_t readBlock(); void clearPendingFrames(); @@ -569,7 +569,7 @@ static AString uriDebugString(const char *uri) { void MatroskaSource::clearPendingFrames() { while (!mPendingFrames.empty()) { - MediaBufferHelperV3 *frame = *mPendingFrames.begin(); + MediaBufferHelper *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); frame->release(); @@ -577,7 +577,7 @@ void MatroskaSource::clearPendingFrames() { } } -status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferHelperV3 *mbuf) { +status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferHelper *mbuf) { if (mbuf->range_length() < 1 || mbuf->range_length() - 1 > INT32_MAX) { // 1-byte signal return ERROR_MALFORMED; @@ -727,7 +727,7 @@ media_status_t MatroskaSource::readBlock() { } len += trackInfo->mHeaderLen; - MediaBufferHelperV3 *mbuf; + MediaBufferHelper *mbuf; mBufferGroup->acquire_buffer(&mbuf, false /* nonblocking */, len /* requested size */); mbuf->set_range(0, len); uint8_t *data = static_cast(mbuf->data()); @@ -763,7 +763,7 @@ media_status_t MatroskaSource::readBlock() { } media_status_t MatroskaSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t targetSampleTimeUs = -1ll; @@ -799,7 +799,7 @@ media_status_t MatroskaSource::read( } } - MediaBufferHelperV3 *frame = *mPendingFrames.begin(); + MediaBufferHelper *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); if ((mType != AVC && mType != HEVC) || mNALSizeLen == 0) { @@ -828,7 +828,7 @@ media_status_t MatroskaSource::read( size_t srcSize = frame->range_length(); size_t dstSize = 0; - MediaBufferHelperV3 *buffer = NULL; + MediaBufferHelper *buffer = NULL; uint8_t *dstPtr = NULL; for (int32_t pass = 0; pass < 2; ++pass) { @@ -1005,7 +1005,7 @@ size_t MatroskaExtractor::countTracks() { return mTracks.size(); } -MediaTrackHelperV3 *MatroskaExtractor::getTrack(size_t index) { +MediaTrackHelper *MatroskaExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } @@ -1673,22 +1673,22 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("abbedd92-38c4-4904-a4c1-b3f45f899980"), 1, "Matroska Extractor", { - .v3 = []( + .v2 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV3 { + FreeMetaFunc *) -> CreatorFunc { DataSourceHelper helper(source); if (SniffMatroska(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new MatroskaExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractor* { + return wrap(new MatroskaExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index a09256ae40..3871bdfb9f 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -35,12 +35,12 @@ class MetaData; struct DataSourceBaseReader; struct MatroskaSource; -struct MatroskaExtractor : public MediaExtractorPluginHelperV3 { +struct MatroskaExtractor : public MediaExtractorPluginHelper { explicit MatroskaExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 7abec549cb..20bcda8761 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -207,7 +207,7 @@ static bool Resync( return valid; } -class MP3Source : public MediaTrackHelperV3 { +class MP3Source : public MediaTrackHelper { public: MP3Source( AMediaFormat *meta, DataSourceHelper *source, @@ -220,7 +220,7 @@ public: virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~MP3Source(); @@ -413,7 +413,7 @@ size_t MP3Extractor::countTracks() { return mInitCheck != OK ? 0 : 1; } -MediaTrackHelperV3 *MP3Extractor::getTrack(size_t index) { +MediaTrackHelper *MP3Extractor::getTrack(size_t index) { if (mInitCheck != OK || index != 0) { return NULL; } @@ -493,7 +493,7 @@ media_status_t MP3Source::getFormat(AMediaFormat *meta) { } media_status_t MP3Source::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -523,7 +523,7 @@ media_status_t MP3Source::read( mSamplesRead = 0; } - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return AMEDIA_ERROR_UNKNOWN; @@ -668,14 +668,14 @@ media_status_t MP3Extractor::getMetaData(AMediaFormat *meta) { return AMEDIA_OK; } -static CMediaExtractorV3* CreateExtractor( +static CMediaExtractor* CreateExtractor( CDataSource *source, void *meta) { Mp3Meta *metaData = static_cast(meta); - return wrapV3(new MP3Extractor(new DataSourceHelper(source), metaData)); + return wrap(new MP3Extractor(new DataSourceHelper(source), metaData)); } -static CreatorFuncV3 Sniff( +static CreatorFunc Sniff( CDataSource *source, float *confidence, void **meta, FreeMetaFunc *freeMeta) { off64_t pos = 0; @@ -712,11 +712,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("812a3f6c-c8cf-46de-b529-3774b14103d4"), 1, // version "MP3 Extractor", - { .v3 = Sniff } + { .v2 = Sniff } }; } diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h index fe72cff2c0..1e38ab7dc7 100644 --- a/media/extractors/mp3/MP3Extractor.h +++ b/media/extractors/mp3/MP3Extractor.h @@ -32,13 +32,13 @@ struct MP3Seeker; class String8; struct Mp3Meta; -class MP3Extractor : public MediaExtractorPluginHelperV3 { +class MP3Extractor : public MediaExtractorPluginHelper { public: MP3Extractor(DataSourceHelper *source, Mp3Meta *meta); ~MP3Extractor(); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 390ba43838..2909a50c0e 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -69,7 +69,7 @@ enum { kMaxAtomSize = 64 * 1024 * 1024, }; -class MPEG4Source : public MediaTrackHelperV3 { +class MPEG4Source : public MediaTrackHelper { static const size_t kMaxPcmFrameSize = 8192; public: // Caller retains ownership of both "dataSource" and "sampleTable". @@ -88,10 +88,10 @@ public: virtual media_status_t getFormat(AMediaFormat *); - virtual media_status_t read(MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + virtual media_status_t read(MediaBufferHelper **buffer, const ReadOptions *options = NULL); virtual bool supportNonblockingRead() { return true; } virtual media_status_t fragmentedRead( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); virtual ~MPEG4Source(); @@ -136,7 +136,7 @@ private: bool mStarted; - MediaBufferHelperV3 *mBuffer; + MediaBufferHelper *mBuffer; uint8_t *mSrcBuffer; @@ -3855,7 +3855,7 @@ void MPEG4Extractor::parseID3v2MetaData(off64_t offset) { } } -MediaTrackHelperV3 *MPEG4Extractor::getTrack(size_t index) { +MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { status_t err; if ((err = readMetaData()) != OK) { return NULL; @@ -5206,7 +5206,7 @@ int32_t MPEG4Source::parseHEVCLayerId(const uint8_t *data, size_t size) { } media_status_t MPEG4Source::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { Mutex::Autolock autoLock(mLock); CHECK(mStarted); @@ -5609,7 +5609,7 @@ media_status_t MPEG4Source::read( } media_status_t MPEG4Source::fragmentedRead( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { ALOGV("MPEG4Source::fragmentedRead"); @@ -6102,11 +6102,11 @@ static bool BetterSniffMPEG4(DataSourceHelper *source, float *confidence) { return true; } -static CMediaExtractorV3* CreateExtractor(CDataSource *source, void *) { - return wrapV3(new MPEG4Extractor(new DataSourceHelper(source))); +static CMediaExtractor* CreateExtractor(CDataSource *source, void *) { + return wrap(new MPEG4Extractor(new DataSourceHelper(source))); } -static CreatorFuncV3 Sniff( +static CreatorFunc Sniff( CDataSource *source, float *confidence, void **, FreeMetaFunc *) { DataSourceHelper helper(source); @@ -6127,11 +6127,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("27575c67-4417-4c54-8d3d-8e626985a164"), 2, // version "MP4 Extractor", - { .v3 = Sniff } + { .v2 = Sniff } }; } diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index a9a46357c1..79d5ff6dd0 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -53,12 +53,12 @@ struct Trex { uint32_t default_sample_flags; }; -class MPEG4Extractor : public MediaExtractorPluginHelperV3 { +class MPEG4Extractor : public MediaExtractorPluginHelper { public: explicit MPEG4Extractor(DataSourceHelper *source, const char *mime = NULL); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index c10a79784b..2f4196c120 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -31,27 +31,27 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("3d1dcfeb-e40a-436d-a574-c2438a555e5f"), 1, "MPEG2-PS/TS Extractor", { - .v3 = []( + .v2 = []( CDataSource *source, float *confidence, void **, - FreeMetaFunc *) -> CreatorFuncV3 { + FreeMetaFunc *) -> CreatorFunc { DataSourceHelper helper(source); if (SniffMPEG2TS(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new MPEG2TSExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractor* { + return wrap(new MPEG2TSExtractor(new DataSourceHelper(source)));}; } else if (SniffMPEG2PS(&helper, confidence)) { return []( CDataSource *source, - void *) -> CMediaExtractorV3* { - return wrapV3(new MPEG2PSExtractor(new DataSourceHelper(source)));}; + void *) -> CMediaExtractor* { + return wrap(new MPEG2PSExtractor(new DataSourceHelper(source)));}; } return NULL; } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index d058637c33..554d252e35 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -40,7 +40,7 @@ namespace android { -struct MPEG2PSExtractor::Track : public MediaTrackHelperV3 { +struct MPEG2PSExtractor::Track : public MediaTrackHelper { Track(MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type); @@ -49,7 +49,7 @@ struct MPEG2PSExtractor::Track : public MediaTrackHelperV3 { virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options); + MediaBufferHelper **buffer, const ReadOptions *options); protected: virtual ~Track(); @@ -72,7 +72,7 @@ private: DISALLOW_EVIL_CONSTRUCTORS(Track); }; -struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelperV3 { +struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelper { WrappedTrack(MPEG2PSExtractor *extractor, Track *track); virtual media_status_t start(); @@ -80,7 +80,7 @@ struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelperV3 { virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options); + MediaBufferHelper **buffer, const ReadOptions *options); protected: virtual ~WrappedTrack(); @@ -128,7 +128,7 @@ size_t MPEG2PSExtractor::countTracks() { return mTracks.size(); } -MediaTrackHelperV3 *MPEG2PSExtractor::getTrack(size_t index) { +MediaTrackHelper *MPEG2PSExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } @@ -677,7 +677,7 @@ media_status_t MPEG2PSExtractor::Track::getFormat(AMediaFormat *meta) { } media_status_t MPEG2PSExtractor::Track::read( - MediaBufferHelperV3 **buffer, const ReadOptions *options) { + MediaBufferHelper **buffer, const ReadOptions *options) { if (mSource == NULL) { return AMEDIA_ERROR_UNKNOWN; } @@ -698,7 +698,7 @@ media_status_t MPEG2PSExtractor::Track::read( MediaBufferBase *mbuf; mSource->read(&mbuf, (MediaTrack::ReadOptions*) options); size_t length = mbuf->range_length(); - MediaBufferHelperV3 *outbuf; + MediaBufferHelper *outbuf; mBufferGroup->acquire_buffer(&outbuf, false, length); memcpy(outbuf->data(), mbuf->data(), length); outbuf->set_range(0, length); @@ -802,7 +802,7 @@ media_status_t MPEG2PSExtractor::WrappedTrack::getFormat(AMediaFormat *meta) { } media_status_t MPEG2PSExtractor::WrappedTrack::read( - MediaBufferHelperV3 **buffer, const ReadOptions *options) { + MediaBufferHelper **buffer, const ReadOptions *options) { return mTrack->read(buffer, options); } diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h index d251688470..e5d591f1da 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.h +++ b/media/extractors/mpeg2/MPEG2PSExtractor.h @@ -32,11 +32,11 @@ struct AMessage; struct Track; class String8; -struct MPEG2PSExtractor : public MediaExtractorPluginHelperV3 { +struct MPEG2PSExtractor : public MediaExtractorPluginHelper { explicit MPEG2PSExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 11c3787dab..3bb2af7572 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -51,7 +51,7 @@ static const size_t kTSPacketSize = 188; static const int kMaxDurationReadSize = 250000LL; static const int kMaxDurationRetry = 6; -struct MPEG2TSSource : public MediaTrackHelperV3 { +struct MPEG2TSSource : public MediaTrackHelper { MPEG2TSSource( MPEG2TSExtractor *extractor, const sp &impl, @@ -63,7 +63,7 @@ struct MPEG2TSSource : public MediaTrackHelperV3 { virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); private: MPEG2TSExtractor *mExtractor; @@ -165,7 +165,7 @@ media_status_t MPEG2TSSource::getFormat(AMediaFormat *meta) { } media_status_t MPEG2TSSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -187,7 +187,7 @@ media_status_t MPEG2TSSource::read( MediaBufferBase *mbuf; mImpl->read(&mbuf, (MediaTrack::ReadOptions*) options); size_t length = mbuf->range_length(); - MediaBufferHelperV3 *outbuf; + MediaBufferHelper *outbuf; mBufferGroup->acquire_buffer(&outbuf, false, length); memcpy(outbuf->data(), mbuf->data(), length); outbuf->set_range(0, length); @@ -246,7 +246,7 @@ size_t MPEG2TSExtractor::countTracks() { return mSourceImpls.size(); } -MediaTrackHelperV3 *MPEG2TSExtractor::getTrack(size_t index) { +MediaTrackHelper *MPEG2TSExtractor::getTrack(size_t index) { if (index >= mSourceImpls.size()) { return NULL; } @@ -602,7 +602,7 @@ uint32_t MPEG2TSExtractor::flags() const { } status_t MPEG2TSExtractor::seek(int64_t seekTimeUs, - const MediaTrackHelperV3::ReadOptions::SeekMode &seekMode) { + const MediaTrackHelper::ReadOptions::SeekMode &seekMode) { if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) { ALOGW("No sync point to seek to."); // ... and therefore we have nothing useful to do here. @@ -623,18 +623,18 @@ status_t MPEG2TSExtractor::seek(int64_t seekTimeUs, } switch (seekMode) { - case MediaTrackHelperV3::ReadOptions::SEEK_NEXT_SYNC: + case MediaTrackHelper::ReadOptions::SEEK_NEXT_SYNC: if (index == mSeekSyncPoints->size()) { ALOGW("Next sync not found; starting from the latest sync."); --index; } break; - case MediaTrackHelperV3::ReadOptions::SEEK_CLOSEST_SYNC: - case MediaTrackHelperV3::ReadOptions::SEEK_CLOSEST: + case MediaTrackHelper::ReadOptions::SEEK_CLOSEST_SYNC: + case MediaTrackHelper::ReadOptions::SEEK_CLOSEST: ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC", seekMode); FALLTHROUGH_INTENDED; - case MediaTrackHelperV3::ReadOptions::SEEK_PREVIOUS_SYNC: + case MediaTrackHelper::ReadOptions::SEEK_PREVIOUS_SYNC: if (index == 0) { ALOGW("Previous sync not found; starting from the earliest " "sync."); diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index aed17bbb0c..e425d237c3 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -38,11 +38,11 @@ struct CDataSource; struct MPEG2TSSource; class String8; -struct MPEG2TSExtractor : public MediaExtractorPluginHelperV3 { +struct MPEG2TSExtractor : public MediaExtractorPluginHelper { explicit MPEG2TSExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); @@ -90,7 +90,7 @@ private: // the data has syntax error during parsing, etc. status_t feedMore(bool isInit = false); status_t seek(int64_t seekTimeUs, - const MediaTrackHelperV3::ReadOptions::SeekMode& seekMode); + const MediaTrackHelper::ReadOptions::SeekMode& seekMode); status_t queueDiscontinuityForSeek(int64_t actualSeekTimeUs); status_t seekBeyond(int64_t seekTimeUs); diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index 29fe2b13f9..c3914f183d 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -45,7 +45,7 @@ extern "C" { namespace android { -struct OggSource : public MediaTrackHelperV3 { +struct OggSource : public MediaTrackHelper { explicit OggSource(OggExtractor *extractor); virtual media_status_t getFormat(AMediaFormat *); @@ -54,7 +54,7 @@ struct OggSource : public MediaTrackHelperV3 { virtual media_status_t stop(); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); protected: virtual ~OggSource(); @@ -82,7 +82,7 @@ struct MyOggExtractor { status_t seekToTime(int64_t timeUs); status_t seekToOffset(off64_t offset); - virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer) = 0; + virtual media_status_t readNextPacket(MediaBufferHelper **buffer) = 0; status_t init(); @@ -90,7 +90,7 @@ struct MyOggExtractor { return AMediaFormat_copy(meta, mFileMeta); } - void setBufferGroup(MediaBufferGroupHelperV3 *group) { + void setBufferGroup(MediaBufferGroupHelper *group) { mBufferGroup = group; } protected: @@ -110,7 +110,7 @@ protected: int64_t mTimeUs; }; - MediaBufferGroupHelperV3 *mBufferGroup; + MediaBufferGroupHelper *mBufferGroup; DataSourceHelper *mSource; off64_t mOffset; Page mCurrentPage; @@ -149,7 +149,7 @@ protected: // 1 - bitstream identification header // 3 - comment header // 5 - codec setup header (Vorbis only) - virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type) = 0; + virtual media_status_t verifyHeader(MediaBufferHelper *buffer, uint8_t type) = 0; // Read the next ogg packet from the underlying data source; optionally // calculate the timestamp for the output packet whilst pretending @@ -157,9 +157,9 @@ protected: // // *buffer is NULL'ed out immediately upon entry, and if successful a new buffer is allocated; // clients are responsible for releasing the original buffer. - media_status_t _readNextPacket(MediaBufferHelperV3 **buffer, bool calcVorbisTimestamp); + media_status_t _readNextPacket(MediaBufferHelper **buffer, bool calcVorbisTimestamp); - int32_t getPacketBlockSize(MediaBufferHelperV3 *buffer); + int32_t getPacketBlockSize(MediaBufferHelper *buffer); void parseFileMetaData(); @@ -183,7 +183,7 @@ struct MyVorbisExtractor : public MyOggExtractor { virtual uint64_t approxBitrate() const; - virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer) { + virtual media_status_t readNextPacket(MediaBufferHelper **buffer) { return _readNextPacket(buffer, /* calcVorbisTimestamp = */ true); } @@ -195,7 +195,7 @@ protected: return granulePos * 1000000ll / mVi.rate; } - virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type); + virtual media_status_t verifyHeader(MediaBufferHelper *buffer, uint8_t type); }; struct MyOpusExtractor : public MyOggExtractor { @@ -213,16 +213,16 @@ struct MyOpusExtractor : public MyOggExtractor { return 0; } - virtual media_status_t readNextPacket(MediaBufferHelperV3 **buffer); + virtual media_status_t readNextPacket(MediaBufferHelper **buffer); protected: virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const; - virtual media_status_t verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type); + virtual media_status_t verifyHeader(MediaBufferHelper *buffer, uint8_t type); private: - media_status_t verifyOpusHeader(MediaBufferHelperV3 *buffer); - media_status_t verifyOpusComments(MediaBufferHelperV3 *buffer); - uint32_t getNumSamplesInPacket(MediaBufferHelperV3 *buffer) const; + media_status_t verifyOpusHeader(MediaBufferHelper *buffer); + media_status_t verifyOpusComments(MediaBufferHelper *buffer); + uint32_t getNumSamplesInPacket(MediaBufferHelper *buffer) const; uint8_t mChannelCount; uint16_t mCodecDelay; @@ -265,7 +265,7 @@ media_status_t OggSource::stop() { } media_status_t OggSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; @@ -277,7 +277,7 @@ media_status_t OggSource::read( } } - MediaBufferHelperV3 *packet; + MediaBufferHelper *packet; media_status_t err = mExtractor->mImpl->readNextPacket(&packet); if (err != AMEDIA_OK) { @@ -578,13 +578,13 @@ ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) { return sizeof(header) + page->mNumSegments + totalSize; } -media_status_t MyOpusExtractor::readNextPacket(MediaBufferHelperV3 **out) { +media_status_t MyOpusExtractor::readNextPacket(MediaBufferHelper **out) { if (mOffset <= mFirstDataOffset && mStartGranulePosition < 0) { // The first sample might not start at time 0; find out where by subtracting // the number of samples on the first page from the granule position // (position of last complete sample) of the first page. This happens // the first time before we attempt to read a packet from the first page. - MediaBufferHelperV3 *mBuf; + MediaBufferHelper *mBuf; uint32_t numSamples = 0; uint64_t curGranulePosition = 0; while (true) { @@ -640,7 +640,7 @@ media_status_t MyOpusExtractor::readNextPacket(MediaBufferHelperV3 **out) { return AMEDIA_OK; } -uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferHelperV3 *buffer) const { +uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferHelper *buffer) const { if (buffer == NULL || buffer->range_length() < 1) { return 0; } @@ -690,7 +690,7 @@ uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferHelperV3 *buffer) con * basic mediabuffer implementation used during initial parsing of the * header packets, which happens before we have a buffer group */ -class StandAloneMediaBuffer : public MediaBufferHelperV3 { +class StandAloneMediaBuffer : public MediaBufferHelper { private: void *mData; size_t mSize; @@ -698,7 +698,7 @@ private: size_t mLength; AMediaFormat *mFormat; public: - StandAloneMediaBuffer(size_t size) : MediaBufferHelperV3(NULL) { + StandAloneMediaBuffer(size_t size) : MediaBufferHelper(NULL) { mSize = size; mData = malloc(mSize); mOffset = 0; @@ -742,10 +742,10 @@ public: } }; -media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelperV3 **out, bool calcVorbisTimestamp) { +media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool calcVorbisTimestamp) { *out = NULL; - MediaBufferHelperV3 *buffer = NULL; + MediaBufferHelper *buffer = NULL; int64_t timeUs = -1; for (;;) { @@ -781,7 +781,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelperV3 **out, bool c ALOGE("b/36592202"); return AMEDIA_ERROR_MALFORMED; } - MediaBufferHelperV3 *tmp; + MediaBufferHelper *tmp; if (mBufferGroup) { mBufferGroup->acquire_buffer(&tmp, false, fullSize); ALOGV("acquired buffer %p from group", tmp); @@ -916,7 +916,7 @@ status_t MyOggExtractor::init() { AMediaFormat_setString(mMeta, AMEDIAFORMAT_KEY_MIME, mMimeType); media_status_t err; - MediaBufferHelperV3 *packet; + MediaBufferHelper *packet; for (size_t i = 0; i < mNumHeaders; ++i) { // ignore timestamp for configuration packets if ((err = _readNextPacket(&packet, /* calcVorbisTimestamp = */ false)) != AMEDIA_OK) { @@ -993,7 +993,7 @@ void MyOggExtractor::buildTableOfContents() { } } -int32_t MyOggExtractor::getPacketBlockSize(MediaBufferHelperV3 *buffer) { +int32_t MyOggExtractor::getPacketBlockSize(MediaBufferHelper *buffer) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -1033,7 +1033,7 @@ int64_t MyOpusExtractor::getTimeUsOfGranule(uint64_t granulePos) const { return pcmSamplePosition * 1000000ll / kOpusSampleRate; } -media_status_t MyOpusExtractor::verifyHeader(MediaBufferHelperV3 *buffer, uint8_t type) { +media_status_t MyOpusExtractor::verifyHeader(MediaBufferHelper *buffer, uint8_t type) { switch (type) { // there are actually no header types defined in the Opus spec; we choose 1 and 3 to mean // header and comments such that we can share code with MyVorbisExtractor. @@ -1046,7 +1046,7 @@ media_status_t MyOpusExtractor::verifyHeader(MediaBufferHelperV3 *buffer, uint8_ } } -media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferHelperV3 *buffer) { +media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferHelper *buffer) { const size_t kOpusHeaderSize = 19; const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -1074,7 +1074,7 @@ media_status_t MyOpusExtractor::verifyOpusHeader(MediaBufferHelperV3 *buffer) { return AMEDIA_OK; } -media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferHelperV3 *buffer) { +media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferHelper *buffer) { // add artificial framing bit so we can reuse _vorbis_unpack_comment int32_t commentSize = buffer->range_length() + 1; auto tmp = heapbuffer(commentSize); @@ -1167,7 +1167,7 @@ media_status_t MyOpusExtractor::verifyOpusComments(MediaBufferHelperV3 *buffer) } media_status_t MyVorbisExtractor::verifyHeader( - MediaBufferHelperV3 *buffer, uint8_t type) { + MediaBufferHelper *buffer, uint8_t type) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); @@ -1335,7 +1335,7 @@ size_t OggExtractor::countTracks() { return mInitCheck != OK ? 0 : 1; } -MediaTrackHelperV3 *OggExtractor::getTrack(size_t index) { +MediaTrackHelper *OggExtractor::getTrack(size_t index) { if (index >= 1) { return NULL; } @@ -1357,13 +1357,13 @@ media_status_t OggExtractor::getMetaData(AMediaFormat *meta) { return mImpl->getFileMetaData(meta); } -static CMediaExtractorV3* CreateExtractor( +static CMediaExtractor* CreateExtractor( CDataSource *source, void *) { - return wrapV3(new OggExtractor(new DataSourceHelper(source))); + return wrap(new OggExtractor(new DataSourceHelper(source))); } -static CreatorFuncV3 Sniff( +static CreatorFunc Sniff( CDataSource *source, float *confidence, void **, @@ -1384,11 +1384,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("8cc5cd06-f772-495e-8a62-cba9649374e9"), 1, // version "Ogg Extractor", - { .v3 = Sniff } + { .v2 = Sniff } }; } diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h index 97506ad168..c75dfa991b 100644 --- a/media/extractors/ogg/OggExtractor.h +++ b/media/extractors/ogg/OggExtractor.h @@ -31,11 +31,11 @@ class String8; struct MyOggExtractor; struct OggSource; -struct OggExtractor : public MediaExtractorPluginHelperV3 { +struct OggExtractor : public MediaExtractorPluginHelper { explicit OggExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 1f0aae5a9b..6f9f689062 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -66,7 +66,7 @@ static uint16_t U16_LE_AT(const uint8_t *ptr) { return ptr[1] << 8 | ptr[0]; } -struct WAVSource : public MediaTrackHelperV3 { +struct WAVSource : public MediaTrackHelper { WAVSource( DataSourceHelper *dataSource, AMediaFormat *meta, @@ -79,7 +79,7 @@ struct WAVSource : public MediaTrackHelperV3 { virtual media_status_t getFormat(AMediaFormat *meta); virtual media_status_t read( - MediaBufferHelperV3 **buffer, const ReadOptions *options = NULL); + MediaBufferHelper **buffer, const ReadOptions *options = NULL); virtual bool supportNonblockingRead() { return true; } @@ -131,7 +131,7 @@ size_t WAVExtractor::countTracks() { return mInitCheck == OK ? 1 : 0; } -MediaTrackHelperV3 *WAVExtractor::getTrack(size_t index) { +MediaTrackHelper *WAVExtractor::getTrack(size_t index) { if (mInitCheck != OK || index > 0) { return NULL; } @@ -428,7 +428,7 @@ media_status_t WAVSource::getFormat(AMediaFormat *meta) { } media_status_t WAVSource::read( - MediaBufferHelperV3 **out, const ReadOptions *options) { + MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; if (options != nullptr && options->getNonBlocking() && !mBufferGroup->has_buffers()) { @@ -454,7 +454,7 @@ media_status_t WAVSource::read( mCurrentPos = pos + mOffset; } - MediaBufferHelperV3 *buffer; + MediaBufferHelper *buffer; media_status_t err = mBufferGroup->acquire_buffer(&buffer); if (err != OK) { return err; @@ -581,13 +581,13 @@ media_status_t WAVSource::read( //////////////////////////////////////////////////////////////////////////////// -static CMediaExtractorV3* CreateExtractor( +static CMediaExtractor* CreateExtractor( CDataSource *source, void *) { - return wrapV3(new WAVExtractor(new DataSourceHelper(source))); + return wrap(new WAVExtractor(new DataSourceHelper(source))); } -static CreatorFuncV3 Sniff( +static CreatorFunc Sniff( CDataSource *source, float *confidence, void **, @@ -621,11 +621,11 @@ extern "C" { __attribute__ ((visibility ("default"))) ExtractorDef GETEXTRACTORDEF() { return { - EXTRACTORDEF_VERSION_CURRENT + 1, + EXTRACTORDEF_VERSION, UUID("7d613858-5837-4a38-84c5-332d1cddee27"), 1, // version "WAV Extractor", - { .v3 = Sniff } + { .v2 = Sniff } }; } diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h index 9b7dfde018..b514196b13 100644 --- a/media/extractors/wav/WAVExtractor.h +++ b/media/extractors/wav/WAVExtractor.h @@ -29,12 +29,12 @@ struct AMessage; struct CDataSource; class String8; -class WAVExtractor : public MediaExtractorPluginHelperV3 { +class WAVExtractor : public MediaExtractorPluginHelper { public: explicit WAVExtractor(DataSourceHelper *source); virtual size_t countTracks(); - virtual MediaTrackHelperV3 *getTrack(size_t index); + virtual MediaTrackHelper *getTrack(size_t index); virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags); virtual media_status_t getMetaData(AMediaFormat *meta); diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h index e357d3e932..e2cbfc8d6a 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h @@ -80,11 +80,11 @@ public: delete mFormat; }; - CMediaBufferV3 *wrap() { + CMediaBuffer *wrap() { if (mWrapper) { return mWrapper; } - mWrapper = new CMediaBufferV3; + mWrapper = new CMediaBuffer; mWrapper->handle = this; mWrapper->release = [](void *handle) -> void { @@ -127,7 +127,7 @@ protected: mFormat = nullptr; } private: - CMediaBufferV3 *mWrapper; + CMediaBuffer *mWrapper; AMediaFormat *mFormat; }; diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h index dc045569f5..a162116cc0 100644 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h +++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h @@ -59,12 +59,12 @@ public: // If buffer is nullptr, have acquire_buffer() check for remote release. virtual void signalBufferReturned(MediaBufferBase *buffer); - CMediaBufferGroupV3 *wrap() { + CMediaBufferGroup *wrap() { if (mWrapper) { return mWrapper; } - mWrapper = new CMediaBufferGroupV3; + mWrapper = new CMediaBufferGroup; mWrapper->handle = this; mWrapper->add_buffer = [](void *handle, size_t size) -> void { @@ -80,7 +80,7 @@ public: }; mWrapper->acquire_buffer = [](void *handle, - CMediaBufferV3 **buf, bool nonBlocking, size_t requestedSize) -> media_status_t { + CMediaBuffer **buf, bool nonBlocking, size_t requestedSize) -> media_status_t { MediaBufferBase *acquiredBuf = nullptr; status_t err = ((MediaBufferGroup*)handle)->acquire_buffer( &acquiredBuf, nonBlocking, requestedSize); @@ -100,7 +100,7 @@ public: } private: - CMediaBufferGroupV3 *mWrapper; + CMediaBufferGroup *mWrapper; struct InternalData; InternalData *mInternal; diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index ea818ff59d..95119310b4 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -43,116 +43,24 @@ uint32_t MediaExtractor::flags() const { } // -------------------------------------------------------------------------------- -MediaExtractorCUnwrapperV1::MediaExtractorCUnwrapperV1(CMediaExtractor *plugin) { +MediaExtractorCUnwrapper::MediaExtractorCUnwrapper(CMediaExtractor *plugin) { this->plugin = plugin; } -MediaExtractorCUnwrapperV1::~MediaExtractorCUnwrapperV1() { +MediaExtractorCUnwrapper::~MediaExtractorCUnwrapper() { plugin->free(plugin->data); free(plugin); } -size_t MediaExtractorCUnwrapperV1::countTracks() { +size_t MediaExtractorCUnwrapper::countTracks() { return plugin->countTracks(plugin->data); } -MediaTrack *MediaExtractorCUnwrapperV1::getTrack(size_t index) { +MediaTrack *MediaExtractorCUnwrapper::getTrack(size_t index) { return new MediaTrackCUnwrapper(plugin->getTrack(plugin->data, index)); } -status_t MediaExtractorCUnwrapperV1::getTrackMetaData( - MetaDataBase& meta, size_t index, uint32_t flags) { - return plugin->getTrackMetaData(plugin->data, meta, index, flags); -} - -status_t MediaExtractorCUnwrapperV1::getMetaData(MetaDataBase& meta) { - return plugin->getMetaData(plugin->data, meta); -} - -const char * MediaExtractorCUnwrapperV1::name() { - return plugin->name(plugin->data); -} - -uint32_t MediaExtractorCUnwrapperV1::flags() const { - return plugin->flags(plugin->data); -} - -status_t MediaExtractorCUnwrapperV1::setMediaCas(const uint8_t* casToken, size_t size) { - return plugin->setMediaCas(plugin->data, casToken, size); -} - -// -------------------------------------------------------------------------------- -MediaExtractorCUnwrapperV2::MediaExtractorCUnwrapperV2(CMediaExtractorV2 *plugin) { - this->plugin = plugin; -} - -MediaExtractorCUnwrapperV2::~MediaExtractorCUnwrapperV2() { - plugin->free(plugin->data); - free(plugin); -} - -size_t MediaExtractorCUnwrapperV2::countTracks() { - return plugin->countTracks(plugin->data); -} - -MediaTrack *MediaExtractorCUnwrapperV2::getTrack(size_t index) { - return new MediaTrackCUnwrapperV2(plugin->getTrack(plugin->data, index)); -} - -status_t MediaExtractorCUnwrapperV2::getTrackMetaData( - MetaDataBase& meta, size_t index, uint32_t flags) { - sp msg = new AMessage(); - AMediaFormat *format = AMediaFormat_fromMsg(&msg); - media_status_t ret = plugin->getTrackMetaData(plugin->data, format, index, flags); - sp newMeta = new MetaData(); - convertMessageToMetaData(msg, newMeta); - delete format; - meta = *newMeta; - return reverse_translate_error(ret); -} - -status_t MediaExtractorCUnwrapperV2::getMetaData(MetaDataBase& meta) { - sp msg = new AMessage(); - AMediaFormat *format = AMediaFormat_fromMsg(&msg); - media_status_t ret = plugin->getMetaData(plugin->data, format); - sp newMeta = new MetaData(); - convertMessageToMetaData(msg, newMeta); - delete format; - meta = *newMeta; - return reverse_translate_error(ret); -} - -const char * MediaExtractorCUnwrapperV2::name() { - return plugin->name(plugin->data); -} - -uint32_t MediaExtractorCUnwrapperV2::flags() const { - return plugin->flags(plugin->data); -} - -status_t MediaExtractorCUnwrapperV2::setMediaCas(const uint8_t* casToken, size_t size) { - return plugin->setMediaCas(plugin->data, casToken, size); -} - -// -------------------------------------------------------------------------------- -MediaExtractorCUnwrapperV3::MediaExtractorCUnwrapperV3(CMediaExtractorV3 *plugin) { - this->plugin = plugin; -} - -MediaExtractorCUnwrapperV3::~MediaExtractorCUnwrapperV3() { - plugin->free(plugin->data); - free(plugin); -} - -size_t MediaExtractorCUnwrapperV3::countTracks() { - return plugin->countTracks(plugin->data); -} - -MediaTrack *MediaExtractorCUnwrapperV3::getTrack(size_t index) { - return new MediaTrackCUnwrapperV3(plugin->getTrack(plugin->data, index)); -} - -status_t MediaExtractorCUnwrapperV3::getTrackMetaData( +status_t MediaExtractorCUnwrapper::getTrackMetaData( MetaDataBase& meta, size_t index, uint32_t flags) { sp msg = new AMessage(); AMediaFormat *format = AMediaFormat_fromMsg(&msg); @@ -164,7 +72,7 @@ status_t MediaExtractorCUnwrapperV3::getTrackMetaData( return reverse_translate_error(ret); } -status_t MediaExtractorCUnwrapperV3::getMetaData(MetaDataBase& meta) { +status_t MediaExtractorCUnwrapper::getMetaData(MetaDataBase& meta) { sp msg = new AMessage(); AMediaFormat *format = AMediaFormat_fromMsg(&msg); media_status_t ret = plugin->getMetaData(plugin->data, format); @@ -175,15 +83,15 @@ status_t MediaExtractorCUnwrapperV3::getMetaData(MetaDataBase& meta) { return reverse_translate_error(ret); } -const char * MediaExtractorCUnwrapperV3::name() { +const char * MediaExtractorCUnwrapper::name() { return plugin->name(plugin->data); } -uint32_t MediaExtractorCUnwrapperV3::flags() const { +uint32_t MediaExtractorCUnwrapper::flags() const { return plugin->flags(plugin->data); } -status_t MediaExtractorCUnwrapperV3::setMediaCas(const uint8_t* casToken, size_t size) { +status_t MediaExtractorCUnwrapper::setMediaCas(const uint8_t* casToken, size_t size) { return plugin->setMediaCas(plugin->data, casToken, size); } diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 81fc4ae56d..2c7a4e58f3 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -106,24 +106,12 @@ sp MediaExtractorFactory::CreateFromService( } MediaExtractor *ex = nullptr; - if (creatorVersion == 1) { - CMediaExtractor *ret = ((CreatorFuncV1)creator)(source->wrap(), meta); + if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1) { + CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta); if (meta != nullptr && freeMeta != nullptr) { freeMeta(meta); } - ex = ret != nullptr ? new MediaExtractorCUnwrapperV1(ret) : nullptr; - } else if (creatorVersion == 2) { - CMediaExtractorV2 *ret = ((CreatorFuncV2)creator)(source->wrap(), meta); - if (meta != nullptr && freeMeta != nullptr) { - freeMeta(meta); - } - ex = ret != nullptr ? new MediaExtractorCUnwrapperV2(ret) : nullptr; - } else if (creatorVersion == 3) { - CMediaExtractorV3 *ret = ((CreatorFuncV3)creator)(source->wrap(), meta); - if (meta != nullptr && freeMeta != nullptr) { - freeMeta(meta); - } - ex = ret != nullptr ? new MediaExtractorCUnwrapperV3(ret) : nullptr; + ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr; } ALOGV("Created an extractor '%s' with confidence %.2f", @@ -195,15 +183,9 @@ void *MediaExtractorFactory::sniff( FreeMetaFunc newFreeMeta = nullptr; void *curCreator = NULL; - if ((*it)->def.def_version == 1) { - curCreator = (void*) (*it)->def.sniff.v1( - source->wrap(), &newConfidence, &newMeta, &newFreeMeta); - } else if ((*it)->def.def_version == 2) { + if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) { curCreator = (void*) (*it)->def.sniff.v2( source->wrap(), &newConfidence, &newMeta, &newFreeMeta); - } else if ((*it)->def.def_version == 3) { - curCreator = (void*) (*it)->def.sniff.v3( - source->wrap(), &newConfidence, &newMeta, &newFreeMeta); } if (curCreator) { @@ -232,8 +214,7 @@ void *MediaExtractorFactory::sniff( void MediaExtractorFactory::RegisterExtractor(const sp &plugin, std::list> &pluginList) { // sanity check check struct version, uuid, name - if (plugin->def.def_version == 0 - || plugin->def.def_version > EXTRACTORDEF_VERSION_CURRENT + 1) { + if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1) { ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version); return; } diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index b15f3bb31a..1c1be30ab6 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -58,129 +58,31 @@ bool MediaTrack::ReadOptions::getSeekTo( return (mOptions & kSeekTo_Option) != 0; } -/* -------------- unwrapper v1 --------------- */ +/* -------------- unwrapper --------------- */ MediaTrackCUnwrapper::MediaTrackCUnwrapper(CMediaTrack *cmediatrack) { wrapper = cmediatrack; -} - -MediaTrackCUnwrapper::~MediaTrackCUnwrapper() { - wrapper->free(wrapper->data); - free(wrapper); -} - -status_t MediaTrackCUnwrapper::start() { - return wrapper->start(wrapper->data); -} - -status_t MediaTrackCUnwrapper::stop() { - return wrapper->stop(wrapper->data); -} - -status_t MediaTrackCUnwrapper::getFormat(MetaDataBase& format) { - return wrapper->getFormat(wrapper->data, format); -} - -status_t MediaTrackCUnwrapper::read(MediaBufferBase **buffer, const ReadOptions *options) { - - uint32_t opts = 0; - - if (options && options->getNonBlocking()) { - opts |= CMediaTrackReadOptions::NONBLOCKING; - } - - int64_t seekPosition = 0; - MediaTrack::ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekPosition, &seekMode)) { - opts |= SEEK; - opts |= (uint32_t) seekMode; - } - - - return wrapper->read(wrapper->data, buffer, opts, seekPosition); -} - -bool MediaTrackCUnwrapper::supportNonblockingRead() { - return wrapper->supportsNonBlockingRead(wrapper->data); -} - -/* -------------- unwrapper v2 --------------- */ - -MediaTrackCUnwrapperV2::MediaTrackCUnwrapperV2(CMediaTrackV2 *cmediatrack2) { - wrapper = cmediatrack2; -} - -MediaTrackCUnwrapperV2::~MediaTrackCUnwrapperV2() { - wrapper->free(wrapper->data); - free(wrapper); -} - -status_t MediaTrackCUnwrapperV2::start() { - return reverse_translate_error(wrapper->start(wrapper->data)); -} - -status_t MediaTrackCUnwrapperV2::stop() { - return reverse_translate_error(wrapper->stop(wrapper->data)); -} - -status_t MediaTrackCUnwrapperV2::getFormat(MetaDataBase& format) { - sp msg = new AMessage(); - AMediaFormat *tmpFormat = AMediaFormat_fromMsg(&msg); - media_status_t ret = wrapper->getFormat(wrapper->data, tmpFormat); - sp newMeta = new MetaData(); - convertMessageToMetaData(msg, newMeta); - delete tmpFormat; - format = *newMeta; - return reverse_translate_error(ret); -} - -status_t MediaTrackCUnwrapperV2::read(MediaBufferBase **buffer, const ReadOptions *options) { - - uint32_t opts = 0; - - if (options && options->getNonBlocking()) { - opts |= CMediaTrackReadOptions::NONBLOCKING; - } - - int64_t seekPosition = 0; - MediaTrack::ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekPosition, &seekMode)) { - opts |= SEEK; - opts |= (uint32_t) seekMode; - } - - return reverse_translate_error(wrapper->read(wrapper->data, buffer, opts, seekPosition)); -} - -bool MediaTrackCUnwrapperV2::supportNonblockingRead() { - return wrapper->supportsNonBlockingRead(wrapper->data); -} - -/* -------------- unwrapper v3 --------------- */ - -MediaTrackCUnwrapperV3::MediaTrackCUnwrapperV3(CMediaTrackV3 *cmediatrack3) { - wrapper = cmediatrack3; bufferGroup = nullptr; } -MediaTrackCUnwrapperV3::~MediaTrackCUnwrapperV3() { +MediaTrackCUnwrapper::~MediaTrackCUnwrapper() { wrapper->free(wrapper->data); free(wrapper); delete bufferGroup; } -status_t MediaTrackCUnwrapperV3::start() { +status_t MediaTrackCUnwrapper::start() { if (bufferGroup == nullptr) { bufferGroup = new MediaBufferGroup(); } return reverse_translate_error(wrapper->start(wrapper->data, bufferGroup->wrap())); } -status_t MediaTrackCUnwrapperV3::stop() { +status_t MediaTrackCUnwrapper::stop() { return reverse_translate_error(wrapper->stop(wrapper->data)); } -status_t MediaTrackCUnwrapperV3::getFormat(MetaDataBase& format) { +status_t MediaTrackCUnwrapper::getFormat(MetaDataBase& format) { sp msg = new AMessage(); AMediaFormat *tmpFormat = AMediaFormat_fromMsg(&msg); media_status_t ret = wrapper->getFormat(wrapper->data, tmpFormat); @@ -191,7 +93,7 @@ status_t MediaTrackCUnwrapperV3::getFormat(MetaDataBase& format) { return reverse_translate_error(ret); } -status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOptions *options) { +status_t MediaTrackCUnwrapper::read(MediaBufferBase **buffer, const ReadOptions *options) { uint32_t opts = 0; @@ -205,7 +107,7 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption opts |= SEEK; opts |= (uint32_t) seekMode; } - CMediaBufferV3 *buf = nullptr; + CMediaBuffer *buf = nullptr; media_status_t ret = wrapper->read(wrapper->data, &buf, opts, seekPosition); if (ret == AMEDIA_OK && buf != nullptr) { *buffer = (MediaBufferBase*)buf->handle; @@ -277,7 +179,7 @@ status_t MediaTrackCUnwrapperV3::read(MediaBufferBase **buffer, const ReadOption return reverse_translate_error(ret); } -bool MediaTrackCUnwrapperV3::supportNonblockingRead() { +bool MediaTrackCUnwrapper::supportNonblockingRead() { return wrapper->supportsNonBlockingRead(wrapper->data); } diff --git a/media/libstagefright/include/media/stagefright/MediaExtractor.h b/media/libstagefright/include/media/stagefright/MediaExtractor.h index 6f3e57eb0a..79f18d5d69 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractor.h @@ -90,22 +90,7 @@ private: class MediaExtractorCUnwrapper : public MediaExtractor { public: - MediaExtractorCUnwrapper() {}; - virtual size_t countTracks() = 0; - virtual MediaTrack *getTrack(size_t index) = 0; - virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0) = 0; - virtual status_t getMetaData(MetaDataBase& meta) = 0; - virtual const char * name() = 0; - virtual uint32_t flags() const = 0; - virtual status_t setMediaCas(const uint8_t* casToken, size_t size) = 0; -protected: - virtual ~MediaExtractorCUnwrapper() {}; -}; - - -class MediaExtractorCUnwrapperV1 : public MediaExtractorCUnwrapper { -public: - explicit MediaExtractorCUnwrapperV1(CMediaExtractor *plugin); + explicit MediaExtractorCUnwrapper(CMediaExtractor *plugin); virtual size_t countTracks(); virtual MediaTrack *getTrack(size_t index); virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0); @@ -114,43 +99,11 @@ public: virtual uint32_t flags() const; virtual status_t setMediaCas(const uint8_t* casToken, size_t size); protected: - virtual ~MediaExtractorCUnwrapperV1(); + virtual ~MediaExtractorCUnwrapper(); private: CMediaExtractor *plugin; }; -class MediaExtractorCUnwrapperV2 : public MediaExtractorCUnwrapper { -public: - explicit MediaExtractorCUnwrapperV2(CMediaExtractorV2 *plugin); - virtual size_t countTracks(); - virtual MediaTrack *getTrack(size_t index); - virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0); - virtual status_t getMetaData(MetaDataBase& meta); - virtual const char * name(); - virtual uint32_t flags() const; - virtual status_t setMediaCas(const uint8_t* casToken, size_t size); -protected: - virtual ~MediaExtractorCUnwrapperV2(); -private: - CMediaExtractorV2 *plugin; -}; - -class MediaExtractorCUnwrapperV3 : public MediaExtractorCUnwrapper { -public: - explicit MediaExtractorCUnwrapperV3(CMediaExtractorV3 *plugin); - virtual size_t countTracks(); - virtual MediaTrack *getTrack(size_t index); - virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags = 0); - virtual status_t getMetaData(MetaDataBase& meta); - virtual const char * name(); - virtual uint32_t flags() const; - virtual status_t setMediaCas(const uint8_t* casToken, size_t size); -protected: - virtual ~MediaExtractorCUnwrapperV3(); -private: - CMediaExtractorV3 *plugin; -}; - } // namespace android #endif // MEDIA_EXTRACTOR_H_ -- GitLab From 44f43604955f35a47184d64865bd089c9e673e60 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 18 Dec 2018 12:51:19 -0800 Subject: [PATCH 0647/1530] MediaRecorder: add getPortId() method Add method to retrieve unique port ID for the AudioRecord instance associated with a MediaRecorder. Bug: 111438757 Test: CTS tests for MediaRecorder Change-Id: I77295ccd163bfa2319a492ed64186a28b691ac38 --- media/libmedia/IMediaRecorder.cpp | 30 ++++++++++++++++++- media/libmedia/include/media/IMediaRecorder.h | 2 +- .../include/media/MediaRecorderBase.h | 1 + media/libmedia/include/media/mediarecorder.h | 1 + media/libmedia/mediarecorder.cpp | 11 +++++++ .../MediaRecorderClient.cpp | 9 ++++++ .../MediaRecorderClient.h | 1 + .../StagefrightRecorder.cpp | 6 ++++ .../StagefrightRecorder.h | 2 +- media/libstagefright/AudioSource.cpp | 7 +++++ .../include/media/stagefright/AudioSource.h | 1 + 11 files changed, 68 insertions(+), 3 deletions(-) diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index b2c91c41ca..56ee18ea48 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -65,7 +65,7 @@ enum { GET_ROUTED_DEVICE_ID, ENABLE_AUDIO_DEVICE_CALLBACK, GET_ACTIVE_MICROPHONES, - + GET_PORT_ID, }; class BpMediaRecorder: public BpInterface @@ -407,6 +407,23 @@ public: return status; } + status_t getPortId(audio_port_handle_t *portId) + { + ALOGV("getPortId"); + if (portId == nullptr) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_PORT_ID, data, &reply); + if (status != OK + || (status = (status_t)reply.readInt32()) != NO_ERROR) { + *portId = AUDIO_PORT_HANDLE_NONE; + return status; + } + *portId = (audio_port_handle_t)reply.readInt32(); + return NO_ERROR; + } }; IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder"); @@ -661,6 +678,17 @@ status_t BnMediaRecorder::onTransact( return NO_ERROR; } + case GET_PORT_ID: { + ALOGV("GET_PORT_ID"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + audio_port_handle_t portId; + status_t status = getPortId(&portId); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeInt32(portId); + } + return NO_ERROR; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h index 379000e679..e7c466d4b8 100644 --- a/media/libmedia/include/media/IMediaRecorder.h +++ b/media/libmedia/include/media/IMediaRecorder.h @@ -73,7 +73,7 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; - + virtual status_t getPortId(audio_port_handle_t *portId) = 0; }; // ---------------------------------------------------------------------------- diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h index 5340ddeb73..e1c5d47b77 100644 --- a/media/libmedia/include/media/MediaRecorderBase.h +++ b/media/libmedia/include/media/MediaRecorderBase.h @@ -72,6 +72,7 @@ struct MediaRecorderBase { virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; + virtual status_t getPortId(audio_port_handle_t *portId) const = 0; diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index bdf1aaed5f..caa0186cc3 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -264,6 +264,7 @@ public: status_t getRoutedDeviceId(audio_port_handle_t *deviceId); status_t enableAudioDeviceCallback(bool enabled); status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t getPortId(audio_port_handle_t *portId) const; private: void doCleanUp(); diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 92cfb1c73d..d07e703895 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -842,4 +842,15 @@ status_t MediaRecorder::getActiveMicrophones(std::vector* return mMediaRecorder->getActiveMicrophones(activeMicrophones); } +status_t MediaRecorder::getPortId(audio_port_handle_t *portId) const +{ + ALOGV("getPortId"); + + if (mMediaRecorder == NULL) { + ALOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + return mMediaRecorder->getPortId(portId); +} + } // namespace android diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 4206647c61..3fa8e3f816 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -537,4 +537,13 @@ status_t MediaRecorderClient::getActiveMicrophones( } return NO_INIT; } + +status_t MediaRecorderClient::getPortId(audio_port_handle_t *portId) { + ALOGV("getPortId"); + Mutex::Autolock lock(mLock); + if (mRecorder != NULL) { + return mRecorder->getPortId(portId); + } + return NO_INIT; +} }; // namespace android diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index d2e681f428..303cefc709 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -109,6 +109,7 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones( std::vector* activeMicrophones); + status_t getPortId(audio_port_handle_t *portId) override; private: friend class MediaPlayerService; // for accessing private constructor diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index eae52c2d72..f2a303812a 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -2255,6 +2255,12 @@ status_t StagefrightRecorder::getActiveMicrophones( return NO_INIT; } +status_t StagefrightRecorder::getPortId(audio_port_handle_t *portId) const { + if (mAudioSourceNode != 0) { + return mAudioSourceNode->getPortId(portId); + } + return NO_INIT; +} status_t StagefrightRecorder::dump( int fd, const Vector& args) const { diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 2ada30197c..a292e58d96 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -77,7 +77,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual void setAudioDeviceCallback(const sp& callback); virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones(std::vector* activeMicrophones); - + status_t getPortId(audio_port_handle_t *portId) const override; private: mutable Mutex mLock; diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 2ae3218dda..9de1e22a32 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -506,4 +506,11 @@ status_t AudioSource::getActiveMicrophones( return NO_INIT; } +status_t AudioSource::getPortId(audio_port_handle_t *portId) const { + if (mRecord != 0) { + *portId = mRecord->getPortId(); + return NO_ERROR; + } + return NO_INIT; +} } // namespace android diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h index 206d322661..b0e32d0822 100644 --- a/media/libstagefright/include/media/stagefright/AudioSource.h +++ b/media/libstagefright/include/media/stagefright/AudioSource.h @@ -69,6 +69,7 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t getPortId(audio_port_handle_t *portId) const; protected: virtual ~AudioSource(); -- GitLab From 46f854ad5dc3472c95a397353e873d2cfff9d614 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Mon, 17 Dec 2018 15:04:13 +0530 Subject: [PATCH 0648/1530] libeffects: Updated test script for higher frequencies and effects Added test cases for testing audio effects for higher frequencies Added test cases for different combination of effects Removed generated *.raw files at the end of the test Test: local native test (lvmtest) Bug: 120910607 Change-Id: I4df654314fee39a41bac05a8398d01e8f11e1173 --- .../lvm/tests/build_and_run_all_unit_tests.sh | 83 ++++++++++++++----- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 340469a2bb..861ee64070 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -20,30 +20,69 @@ adb root && adb wait-for-device remount # location of test files testdir="/data/local/tmp/lvmTest" -#flags="-bE -tE -eqE -csE" -flags="-csE -tE -eqE" - - echo "========================================" echo "testing lvm" -adb shell mkdir $testdir +adb shell mkdir -p $testdir adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir -# run multichannel effects at different channel counts, saving only the stereo channel pair. -adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_1.raw\ - -ch:1 -fs:44100 $flags -adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_2.raw\ - -ch:2 -fs:44100 $flags -adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_4.raw\ - -ch:4 -fs:44100 $flags -adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_6.raw\ - -ch:6 -fs:44100 $flags -adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw -o:$testdir/sinesweep_8.raw\ - -ch:8 -fs:44100 $flags - -# two channel files should be identical to higher channel computation (first 2 channels). -adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_2.raw -adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_4.raw -adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_6.raw -adb shell cmp $testdir/sinesweep_2.raw $testdir/sinesweep_8.raw +flags_arr=( + "-csE" + "-eqE" + "-tE" + "-csE -tE -eqE" + "-bE" + "-csE -tE" + "-csE -eqE" "-tE -eqE" + "-csE -tE -bE -eqE" +) + +fs_arr=( + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 88200 + 96000 + 176400 + 192000 +) + +ch_arr=( + 1 + 2 + 4 + 6 + 8 +) + +# run multichannel effects at different configs, saving only the stereo channel +# pair. +for flags in "${flags_arr[@]}" +do + for fs in ${fs_arr[*]} + do + for ch in ${ch_arr[*]} + do + adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ + -o:$testdir/sinesweep_$((ch))_$((fs)).raw -ch:$ch -fs:$fs $flags + + # two channel files should be identical to higher channel + # computation (first 2 channels). + # Do not compare cases where -bE is in flags (due to mono computation) + if [[ $flags != *"-bE"* ]] && [ "$ch" -gt 2 ] + then + adb shell cmp $testdir/sinesweep_2_$((fs)).raw \ + $testdir/sinesweep_$((ch))_$((fs)).raw + fi + + done + done +done + +adb shell rm -r $testdir -- GitLab From b144b4b601b0fe08f8d40d7a7e68e446e8adcea6 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 20 Dec 2018 14:15:49 -0800 Subject: [PATCH 0649/1530] lvmtest: Add mono mode for audio effect testing. This ensures all input audio channels are identical by replicating the first audio channel of each audio frame to the other channels. Test: Use lvmtest with -M Bug: 121331591 Change-Id: Ie85b6511397a97c9d0f3451328d128048c803ce5 --- media/libeffects/lvm/tests/lvmtest.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp index 99551ccfe9..43271d2212 100644 --- a/media/libeffects/lvm/tests/lvmtest.cpp +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -76,6 +76,7 @@ struct lvmConfigParams_t { int samplingFreq = 44100; int nrChannels = 2; int fChannels = 2; + bool monoMode = false; int bassEffectLevel = 0; int eqPresetLevel = 0; int frameLength = 256; @@ -98,6 +99,8 @@ void printUsage() { printf("\n"); printf("\n -ch: (1 through 8)\n\n"); printf("\n -fch: (1 through 8)\n\n"); + printf("\n -M"); + printf("\n Mono mode (force all input audio channels to be identical)"); printf("\n -basslvl:"); printf("\n A value that ranges between 0 - 15 default 0"); printf("\n"); @@ -612,6 +615,15 @@ int lvmMainProcess(lvmConfigParams_t *plvmConfigParams, FILE *finp, FILE *fout) } memcpy_to_float_from_i16(floatIn.data(), in.data(), frameLength * channelCount); + // Mono mode will replicate the first channel to all other channels. + // This ensures all audio channels are identical. This is useful for testing + // Bass Boost, which extracts a mono signal for processing. + if (plvmConfigParams->monoMode && channelCount > 1) { + for (int i = 0; i < frameLength; ++i) { + auto *fp = &floatIn[i * channelCount]; + std::fill(fp + 1, fp + channelCount, *fp); // replicate ch 0 + } + } #if 1 errCode = lvmExecute(floatIn.data(), floatOut.data(), &context, plvmConfigParams); if (errCode) { @@ -677,6 +689,8 @@ int main(int argc, const char *argv[]) { return -1; } lvmConfigParams.fChannels = fChannels; + } else if (!strcmp(argv[i],"-M")) { + lvmConfigParams.monoMode = true; } else if (!strncmp(argv[i], "-basslvl:", 9)) { const int bassEffectLevel = atoi(argv[i] + 9); if (bassEffectLevel > 15 || bassEffectLevel < 0) { -- GitLab From 37d73d1502db31e3542f1ce4de5c7cbed8d1e174 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 20 Dec 2018 12:49:09 -0800 Subject: [PATCH 0650/1530] Remove unused libraries from various media libraries Test: build Change-Id: I3bfb70a6ed3fe8f0e1683d456156517f8b992a33 --- media/libmediaextractor/Android.bp | 1 - media/libmediametrics/Android.bp | 2 -- media/libstagefright/Android.bp | 6 +----- media/libstagefright/HTTPBase.cpp | 2 -- media/libstagefright/bqhelper/Android.bp | 3 --- media/libstagefright/flac/dec/Android.bp | 2 -- media/libstagefright/omx/Android.bp | 1 - media/libstagefright/xmlparser/Android.bp | 6 ++++++ media/ndk/Android.bp | 5 ----- media/utils/Android.bp | 1 - services/medialog/Android.bp | 2 -- 11 files changed, 7 insertions(+), 24 deletions(-) diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp index 1aa1e13369..4758cd65e8 100644 --- a/media/libmediaextractor/Android.bp +++ b/media/libmediaextractor/Android.bp @@ -27,7 +27,6 @@ cc_library { "libbinder", "libstagefright_foundation", "libutils", - "libcutils", "liblog", ], diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp index 8f8c47891e..e188e54fb3 100644 --- a/media/libmediametrics/Android.bp +++ b/media/libmediametrics/Android.bp @@ -10,11 +10,9 @@ cc_library { ], shared_libs: [ - "libbase", "libbinder", "libcutils", "liblog", - "libstagefright_foundation", "libutils", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 3388ed9580..249f2a4a51 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -146,7 +146,6 @@ cc_library { "libdl", "libdrmframework", "libgui", - "libion", "liblog", "libmedia", "libmedia_omx", @@ -154,7 +153,6 @@ cc_library { "libmediaextractor", "libmediametrics", "libmediautils", - "libnetd_client", "libui", "libutils", "libmedia_helper", @@ -162,7 +160,6 @@ cc_library { "libstagefright_foundation", "libstagefright_omx_utils", "libstagefright_opus_common", - "libstagefright_xmlparser", "libRScpp", "libhidlallocatorutils", "libhidlbase", @@ -171,8 +168,6 @@ cc_library { "android.hidl.allocator@1.0", "android.hardware.cas.native@1.0", "android.hardware.media.omx@1.0", - "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.mapper@2.0", ], static_libs: [ @@ -190,6 +185,7 @@ cc_library { ], header_libs:[ + "libstagefright_xmlparser_headers", "media_ndk_headers", ], diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index 03e0d12e5b..d118e8c5e7 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -26,8 +26,6 @@ #include #include -#include - namespace android { HTTPBase::HTTPBase() diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp index 81777f14a1..218fe1569e 100644 --- a/media/libstagefright/bqhelper/Android.bp +++ b/media/libstagefright/bqhelper/Android.bp @@ -25,7 +25,6 @@ cc_library_shared { ], shared_libs: [ - "libbase", "libbinder", "libcutils", "libgui", @@ -38,8 +37,6 @@ cc_library_shared { "libutils", "android.hardware.graphics.bufferqueue@1.0", - - "libnativewindow", // TODO(b/62923479): use header library ], export_shared_lib_headers: [ diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp index 6bfab1685f..751b05358b 100644 --- a/media/libstagefright/flac/dec/Android.bp +++ b/media/libstagefright/flac/dec/Android.bp @@ -36,8 +36,6 @@ cc_library { shared_libs: [ "liblog", - "libstagefright_foundation", - "libutils", ], header_libs: ["libmedia_headers"], } diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 8a76de396c..362b7f5a18 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -53,7 +53,6 @@ cc_library_shared { "libhidlbase", "libhidlmemory", "libhidltransport", - "libnativewindow", // TODO(b/62923479): use header library "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp index b55dbb0448..bebfb3bd64 100644 --- a/media/libstagefright/xmlparser/Android.bp +++ b/media/libstagefright/xmlparser/Android.bp @@ -1,3 +1,9 @@ +cc_library_headers { + name: "libstagefright_xmlparser_headers", + export_include_dirs: ["include"], + vendor_available: true, +} + cc_library_shared { name: "libstagefright_xmlparser", vendor_available: true, diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 697695037c..73bd2cabe6 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -88,7 +88,6 @@ cc_library_shared { "libandroid", "libandroid_runtime", "libbinder", - "libhwbinder", "libhidlbase", "libgui", "libui", @@ -141,10 +140,6 @@ cc_library { ], shared_libs: [ - "libstagefright_foundation", - "liblog", - "libutils", - "libcutils", ], sanitize: { diff --git a/media/utils/Android.bp b/media/utils/Android.bp index b05e022862..a11602b97a 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -27,7 +27,6 @@ cc_library { ], shared_libs: [ "libbinder", - "libcutils", "liblog", "libutils", "libmemunreachable", diff --git a/services/medialog/Android.bp b/services/medialog/Android.bp index ca96f622fc..bee5d25a2d 100644 --- a/services/medialog/Android.bp +++ b/services/medialog/Android.bp @@ -9,10 +9,8 @@ cc_library_shared { shared_libs: [ "libaudioutils", "libbinder", - "libcutils", "liblog", "libmediautils", - "libnbaio", "libnblog", "libutils", ], -- GitLab From 03a6e6a08dbda1c94e0c9ce6fba4a4abd3df6d59 Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Tue, 4 Dec 2018 10:54:13 -0700 Subject: [PATCH 0651/1530] Adding Audio HAL V5: Direction API Bug: 119137468 Test: Testbed app. Change-Id: I1b3c81079000958da80d36616fe06930d876aac3 --- media/libaudioclient/AudioRecord.cpp | 11 +++++++ .../aidl/android/media/IAudioRecord.aidl | 8 +++++ .../include/media/AudioRecord.h | 8 +++++ media/libaudiohal/impl/StreamHalHidl.cpp | 24 ++++++++++++++ media/libaudiohal/impl/StreamHalHidl.h | 6 ++++ media/libaudiohal/impl/StreamHalLocal.cpp | 21 ++++++++++++ media/libaudiohal/impl/StreamHalLocal.h | 6 ++++ .../media/audiohal/StreamHalInterface.h | 6 ++++ services/audioflinger/AudioFlinger.h | 4 +++ services/audioflinger/RecordTracks.h | 3 ++ services/audioflinger/Threads.cpp | 14 ++++++++ services/audioflinger/Threads.h | 3 ++ services/audioflinger/Tracks.cpp | 33 +++++++++++++++++++ 13 files changed, 147 insertions(+) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 3223647f54..65b8a389bc 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -1398,6 +1398,17 @@ status_t AudioRecord::getActiveMicrophones(std::vector* a return mAudioRecord->getActiveMicrophones(activeMicrophones).transactionError(); } +status_t AudioRecord::setMicrophoneDirection(audio_microphone_direction_t direction) +{ + AutoMutex lock(mLock); + return mAudioRecord->setMicrophoneDirection(direction).transactionError(); +} + +status_t AudioRecord::setMicrophoneFieldDimension(float zoom) { + AutoMutex lock(mLock); + return mAudioRecord->setMicrophoneFieldDimension(zoom).transactionError(); +} + // ========================================================================= void AudioRecord::DeathNotifier::binderDied(const wp& who __unused) diff --git a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl index 01e0a7160f..cf9c7f44d2 100644 --- a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl +++ b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl @@ -36,4 +36,12 @@ interface IAudioRecord { /* Get a list of current active microphones. */ void getActiveMicrophones(out MicrophoneInfo[] activeMicrophones); + + /* Set the microphone direction (for processing). + */ + void setMicrophoneDirection(int /*audio_microphone_direction_t*/ direction); + + /* Set the microphone zoom (for processing). + */ + void setMicrophoneFieldDimension(float zoom); } diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index 35a7e0583f..ebee124e10 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -534,6 +534,14 @@ public: */ status_t getActiveMicrophones(std::vector* activeMicrophones); + /* Set the Microphone direction (for processing purposes). + */ + status_t setMicrophoneDirection(audio_microphone_direction_t direction); + + /* Set the Microphone zoom factor (for processing purposes). + */ + status_t setMicrophoneFieldDimension(float zoom); + /* Get the unique port ID assigned to this AudioRecord instance by audio policy manager. * The ID is unique across all audioserver clients and can change during the life cycle * of a given AudioRecord instance if the connection to audioserver is restored. diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index c12b3629c0..2e35be63fe 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -854,5 +854,29 @@ status_t StreamInHalHidl::updateSinkMetadata(const } #endif +#if MAJOR_VERSION < 5 +status_t StreamInHalHidl::setMicrophoneDirection(audio_microphone_direction_t direction __unused) { + if (mStream == 0) return NO_INIT; + return INVALID_OPERATION; +} + +status_t StreamInHalHidl::setMicrophoneFieldDimension(float zoom __unused) { + if (mStream == 0) return NO_INIT; + return INVALID_OPERATION; +} +#else +status_t StreamInHalHidl::setMicrophoneDirection(audio_microphone_direction_t direction) { + if (!mStream) return NO_INIT; + return processReturn("setMicrophoneDirection", + mStream->setMicrophoneDirection(static_cast(direction))); +} + +status_t StreamInHalHidl::setMicrophoneFieldDimension(float zoom) { + if (!mStream) return NO_INIT; + return processReturn("setMicrophoneFieldDimension", + mStream->setMicrophoneFieldDimension(zoom)); +} +#endif + } // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h index f7b507e73c..9ac10674d1 100644 --- a/media/libaudiohal/impl/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -220,6 +220,12 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { // Get active microphones virtual status_t getActiveMicrophones(std::vector *microphones); + // Set microphone direction (for processing) + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) override; + + // Set microphone zoom (for processing) + virtual status_t setMicrophoneFieldDimension(float zoom) override; + // Called when the metadata of the stream's sink has been changed. status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp index 26d30d4746..fcb809bf53 100644 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -368,5 +368,26 @@ status_t StreamInHalLocal::getActiveMicrophones(std::vectorset_microphone_direction == NULL) return INVALID_OPERATION; + return mStream->set_microphone_direction(mStream, direction); +} + +status_t StreamInHalLocal::setMicrophoneFieldDimension(float zoom) { + if (mStream->set_microphone_field_dimension == NULL) return INVALID_OPERATION; + return mStream->set_microphone_field_dimension(mStream, zoom); + +} +#endif + } // namespace CPP_VERSION } // namespace android diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h index 4fd1960887..3d6c50ef97 100644 --- a/media/libaudiohal/impl/StreamHalLocal.h +++ b/media/libaudiohal/impl/StreamHalLocal.h @@ -204,6 +204,12 @@ class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { // Get active microphones virtual status_t getActiveMicrophones(std::vector *microphones); + // Sets microphone direction (for processing) + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); + + // Sets microphone zoom (for processing) + virtual status_t setMicrophoneFieldDimension(float zoom); + // Called when the metadata of the stream's sink has been changed. status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h index bd71dc0986..ed8282fdfc 100644 --- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h @@ -179,6 +179,12 @@ class StreamInHalInterface : public virtual StreamHalInterface { // Get active microphones virtual status_t getActiveMicrophones(std::vector *microphones) = 0; + // Set direction for capture processing + virtual status_t setMicrophoneDirection(audio_microphone_direction_t) = 0; + + // Set zoom factor for capture stream + virtual status_t setMicrophoneFieldDimension(float zoom) = 0; + struct SinkMetadata { std::vector tracks; }; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 6c698f63fc..4c0a5bcc8d 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -579,6 +579,10 @@ using effect_buffer_t = int16_t; virtual binder::Status stop(); virtual binder::Status getActiveMicrophones( std::vector* activeMicrophones); + virtual binder::Status setMicrophoneDirection( + int /*audio_microphone_direction_t*/ direction); + virtual binder::Status setMicrophoneFieldDimension(float zoom); + private: const sp mRecordTrack; diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 85f5456b9e..32af7d5339 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -71,6 +71,9 @@ public: status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t setMicrophoneDirection(audio_microphone_direction_t direction); + status_t setMicrophoneFieldDimension(float zoom); + static bool checkServerLatencySupported( audio_format_t format, audio_input_flags_t flags) { return audio_is_linear_pcm(format) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index c6941c045d..a21bdfa482 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7578,6 +7578,20 @@ status_t AudioFlinger::RecordThread::getActiveMicrophones( return status; } +status_t AudioFlinger::RecordThread::setMicrophoneDirection(audio_microphone_direction_t direction) +{ + ALOGV("RecordThread::setMicrophoneDirection"); + AutoMutex _l(mLock); + return mInput->stream->setMicrophoneDirection(direction); +} + +status_t AudioFlinger::RecordThread::setMicrophoneFieldDimension(float zoom) +{ + ALOGV("RecordThread::setMicrophoneFieldDimension"); + AutoMutex _l(mLock); + return mInput->stream->setMicrophoneFieldDimension(zoom); +} + void AudioFlinger::RecordThread::updateMetadata_l() { if (mInput == nullptr || mInput->stream == nullptr || diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index e8b2158e71..0f1b7b28f7 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1541,6 +1541,9 @@ public: status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t setMicrophoneDirection(audio_microphone_direction_t direction); + status_t setMicrophoneFieldDimension(float zoom); + void updateMetadata_l() override; bool fastTrackAvailable() const { return mFastTrackAvail; } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 9a7f1f1c94..d23d19d813 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1710,6 +1710,18 @@ binder::Status AudioFlinger::RecordHandle::getActiveMicrophones( mRecordTrack->getActiveMicrophones(activeMicrophones)); } +binder::Status AudioFlinger::RecordHandle::setMicrophoneDirection( + int /*audio_microphone_direction_t*/ direction) { + ALOGV("%s()", __func__); + return binder::Status::fromStatusT(mRecordTrack->setMicrophoneDirection( + static_cast(direction))); +} + +binder::Status AudioFlinger::RecordHandle::setMicrophoneFieldDimension(float zoom) { + ALOGV("%s()", __func__); + return binder::Status::fromStatusT(mRecordTrack->setMicrophoneFieldDimension(zoom)); +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::RecordTrack" @@ -2004,6 +2016,27 @@ status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones( } } +status_t AudioFlinger::RecordThread::RecordTrack::setMicrophoneDirection( + audio_microphone_direction_t direction) { + sp thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + return recordThread->setMicrophoneDirection(direction); + } else { + return BAD_VALUE; + } +} + +status_t AudioFlinger::RecordThread::RecordTrack::setMicrophoneFieldDimension(float zoom) { + sp thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + return recordThread->setMicrophoneFieldDimension(zoom); + } else { + return BAD_VALUE; + } +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::PatchRecord" -- GitLab From 2ba48f84870b0d266d6dd244d9e0642617719176 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 21 Dec 2018 10:36:15 -0800 Subject: [PATCH 0652/1530] Remove some more unused libraries from media libraries Test: build Change-Id: I1d7af4398f383305a8dd3a61a19a587e7d8ad01c --- drm/libmediadrm/Android.bp | 3 --- media/libmediaplayer2/Android.bp | 13 +++++-------- media/libstagefright/codecs/flac/dec/Android.bp | 1 - 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index 41d18332ef..01efb223c0 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -27,7 +27,6 @@ cc_library { "libmediadrmmetrics_lite", "libmediametrics", "libmediautils", - "libprotobuf-cpp-lite", "libstagefright_foundation", "libutils", "android.hardware.drm@1.0", @@ -60,13 +59,11 @@ cc_library_shared { shared_libs: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", - "libbase", "libbinder", "libhidlbase", "liblog", "libmediametrics", "libprotobuf-cpp-lite", - "libstagefright_foundation", "libutils", ], cflags: [ diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 6b433752fb..54309ee933 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -91,18 +91,15 @@ cc_library { "JMedia2HTTPConnection.cpp", ], + header_libs: [ + "libbinder_headers", + "libnativehelper_header_only", + ], + shared_libs: [ - "android.hidl.token@1.0-utils", "liblog", - "libcutils", "libutils", - "libbinder", - "libstagefright_foundation", - "libmediaextractor", "libdl", - "libaudioutils", - "libaudioclient", - "libnativehelper", ], include_dirs: [ diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp index 1674cb2055..3d4a44f0c4 100644 --- a/media/libstagefright/codecs/flac/dec/Android.bp +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -29,7 +29,6 @@ cc_library_shared { }, shared_libs: [ - "libcutils", "liblog", "libstagefright_flacdec", "libstagefright_omx", -- GitLab From 4a84cbb33dd74bc033518d007e2d9315e600bdac Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 18 Dec 2018 16:34:47 -0800 Subject: [PATCH 0653/1530] NdkWrapper: add missing DRM AMediaFormat keys Bug: 121387877 Test: MediaPlayer2DrmTest Change-Id: I7e07bb80746990a4f2e35ff595cab8ed75eda062 --- media/libmedia/NdkWrapper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp index eed96e7649..156991eb13 100644 --- a/media/libmedia/NdkWrapper.cpp +++ b/media/libmedia/NdkWrapper.cpp @@ -57,6 +57,10 @@ static const char *AMediaFormatKeyGroupInt32[] = { AMEDIAFORMAT_KEY_COLOR_STANDARD, AMEDIAFORMAT_KEY_COLOR_TRANSFER, AMEDIAFORMAT_KEY_COMPLEXITY, + AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, + AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, + AMEDIAFORMAT_KEY_CRYPTO_MODE, + AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL, AMEDIAFORMAT_KEY_GRID_COLUMNS, AMEDIAFORMAT_KEY_GRID_ROWS, @@ -102,6 +106,8 @@ static const char *AMediaFormatKeyGroupString[] = { }; static const char *AMediaFormatKeyGroupBuffer[] = { + AMEDIAFORMAT_KEY_CRYPTO_IV, + AMEDIAFORMAT_KEY_CRYPTO_KEY, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, AMEDIAFORMAT_KEY_SEI, AMEDIAFORMAT_KEY_MPEG_USER_DATA, -- GitLab From f60cbe5665dea5b0b244f57815ab49db5a652eea Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Fri, 21 Dec 2018 13:32:36 -0800 Subject: [PATCH 0654/1530] nuplayer2: pass crypto info from extractor to decoder Bug: 121387877 Test: MediaPlayer2DrmTest Change-Id: Iabe59aaffe2b0fae2f5a8420566b41ff324d0758 --- media/libmediaplayer2/nuplayer2/GenericSource2.cpp | 5 +++++ media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp index f01361b8b0..9552580d73 100644 --- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp +++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp @@ -1286,6 +1286,11 @@ void NuPlayer2::GenericSource2::readBuffer( mVideoTimeUs = timeUs; } + sp cryptInfo = extractor->getSampleCryptoInfo(); + if (cryptInfo != NULL) { + meta->setObject("cryptInfo", cryptInfo); + } + queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track); if (numBuffers == 0 && actualTimeUs != nullptr) { diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp index 49e3e3b167..a5bd62d0fd 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp @@ -1108,6 +1108,11 @@ bool NuPlayer2::Decoder::onInputBufferFetched(const sp &msg) { } // buffer->data() } // needsCopy + sp cryptInfoObj; + if (buffer->meta()->findObject("cryptInfo", &cryptInfoObj)) { + cryptInfo = static_cast(cryptInfoObj.get()); + } + status_t err; if (cryptInfo != NULL) { err = mCodec->queueSecureInputBuffer( -- GitLab From 669425507df4a9ee4d062d994cf9549ed5f7723f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 21 Dec 2018 16:07:12 -0800 Subject: [PATCH 0655/1530] AudioMixer: Accept a float downmixer Test: Play Music with multichannel source using float and int16 downmixer Bug: 121222814 Change-Id: I1057b3a2f342e42ab56652f724111fa7cd68d1cf --- media/libaudioprocessing/AudioMixer.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 2567b3b238..86711de647 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -302,14 +302,19 @@ status_t AudioMixer::Track::prepareForDownmix() if (audio_channel_mask_get_representation(channelMask) == AUDIO_CHANNEL_REPRESENTATION_POSITION && DownmixerBufferProvider::isMultichannelCapable()) { - mDownmixerBufferProvider.reset(new DownmixerBufferProvider(channelMask, - mMixerChannelMask, - AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */, - sampleRate, sessionId, kCopyBufferFrameCount)); - if (static_cast(mDownmixerBufferProvider.get())->isValid()) { - mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix - reconfigureBufferProviders(); - return NO_ERROR; + + // Check if we have a float or int16 downmixer, in that order. + for (const audio_format_t format : { AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_16_BIT }) { + mDownmixerBufferProvider.reset(new DownmixerBufferProvider( + channelMask, mMixerChannelMask, + format, + sampleRate, sessionId, kCopyBufferFrameCount)); + if (static_cast(mDownmixerBufferProvider.get()) + ->isValid()) { + mDownmixRequiresFormat = format; + reconfigureBufferProviders(); + return NO_ERROR; + } } // mDownmixerBufferProvider reset below. } -- GitLab From ed1172f61f054fc2d42bf5e2d0b8833b6f553ace Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 21 Dec 2018 16:20:03 -0800 Subject: [PATCH 0656/1530] EffectDownmix: Fix float mode and enable Test: Play Music with multichannel source using float and int16 downmixer Bug: 121222814 Change-Id: If97770991edc3bec02edc82101aaa8ad86ac4bcf --- media/libeffects/downmix/Android.bp | 2 +- media/libeffects/downmix/EffectDownmix.c | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp index 227f2a15a2..9c82b1d1e9 100644 --- a/media/libeffects/downmix/Android.bp +++ b/media/libeffects/downmix/Android.bp @@ -13,7 +13,7 @@ cc_library_shared { relative_install_path: "soundfx", cflags: [ - //"-DBUILD_FLOAT", + "-DBUILD_FLOAT", "-fvisibility=hidden", "-Wall", "-Werror", diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c index b4a1d77f09..99ac4f5899 100644 --- a/media/libeffects/downmix/EffectDownmix.c +++ b/media/libeffects/downmix/EffectDownmix.c @@ -31,10 +31,12 @@ // Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing //#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0 -#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 - #ifdef BUILD_FLOAT #define MINUS_3_DB_IN_FLOAT 0.70710678f // -3dB = 0.70710678f +const audio_format_t gTargetFormat = AUDIO_FORMAT_PCM_FLOAT; +#else +#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 +const audio_format_t gTargetFormat = AUDIO_FORMAT_PCM_16_BIT; #endif // subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_* @@ -703,7 +705,7 @@ int Downmix_Init(downmix_module_t *pDwmModule) { memset(&pDwmModule->context, 0, sizeof(downmix_object_t)); pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; - pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pDwmModule->config.inputCfg.format = gTargetFormat; pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1; pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL; pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL; @@ -715,7 +717,7 @@ int Downmix_Init(downmix_module_t *pDwmModule) { // set a default value for the access mode, but should be overwritten by caller pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; - pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + pDwmModule->config.outputCfg.format = gTargetFormat; pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL; pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL; @@ -762,8 +764,8 @@ int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bo // Check configuration compatibility with build options, and effect capabilities if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS - || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT - || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { + || pConfig->inputCfg.format != gTargetFormat + || pConfig->outputCfg.format != gTargetFormat) { ALOGE("Downmix_Configure error: invalid config"); return -EINVAL; } @@ -1185,8 +1187,8 @@ void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, if (accumulate) { while (numFrames) { // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) - centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) - + (pSrc[3] * MINUS_3_DB_IN_Q19_12); + centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_FLOAT) + + (pSrc[3] * MINUS_3_DB_IN_FLOAT); // FL + centerPlusLfeContrib + SL + RL lt = pSrc[0] + centerPlusLfeContrib + pSrc[6] + pSrc[4]; // FR + centerPlusLfeContrib + SR + RR @@ -1427,4 +1429,4 @@ bool Downmix_foldGeneric( } return true; } -#endif \ No newline at end of file +#endif -- GitLab From b49627770b2f803b2ebc093ebdd927ee86ea7a46 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Thu, 8 Nov 2018 15:59:56 -0800 Subject: [PATCH 0657/1530] MPEG4Extractor+MPEG4Writer:Bframes+EditlistSupport Enabled BFrames encoding in ACodec. Added edit list box support for B frames recording use case in Mpeg4Writer. Added edit list support in MPEG4Extractor for video tracks with B frames and postive CTTS entries. Also, fixed issues in the existing edit list support for audio tracks. Bug: 113022200 Bug: 113114128 Test: Recorded video in crosshatch and that played in synch on VLC Player-Linux, QT Player-MAC and test device/crosshatch. Test: run cts-dev --module CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice A few test cases in MediaRecorderTest is failing, but those are failing without any of the code changes in this change list. A few of the test cases in MediaMetadataRetrieverTest are failing. Those cases didn't account for edit list entry in video file and hence those cases have to be modified. Test: Simulated frame drops while B Frames recording, by skipping a few frames while muxing a video file with Bframes using MediaMuxer and manually checked values in CTTS table and also the elst entry in edts box. Test: Checked playback of files in bug 113114128. Test: Recorded video on crosshatch. Checked playback on Nexus 4 and Nexus 6. Change-Id: I0cf7b26c0d6180b6bb7ad9924505823a629e55a0 --- media/extractors/mp4/MPEG4Extractor.cpp | 185 ++++++++++++------ media/extractors/mp4/MPEG4Extractor.h | 1 + media/libstagefright/ACodec.cpp | 4 +- media/libstagefright/MPEG4Writer.cpp | 144 ++++++++++---- .../include/media/stagefright/MPEG4Writer.h | 2 + 5 files changed, 229 insertions(+), 107 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 2909a50c0e..253f4006d6 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -80,7 +80,8 @@ public: Vector &sidx, const Trex *trex, off64_t firstMoofOffset, - const sp &itemTable); + const sp &itemTable, + int32_t elstShiftStartTicks); virtual status_t init(); virtual media_status_t start(); @@ -109,7 +110,7 @@ private: off64_t mFirstMoofOffset; off64_t mCurrentMoofOffset; off64_t mNextMoofOffset; - uint32_t mCurrentTime; + uint32_t mCurrentTime; // in media timescale ticks int32_t mLastParsedTrackId; int32_t mTrackId; @@ -143,6 +144,10 @@ private: bool mIsHeif; sp mItemTable; + // Start offset from composition time to presentation time. + // Support shift only for video tracks through mElstShiftStartTicks for now. + int32_t mElstShiftStartTicks; + size_t parseNALSize(const uint8_t *data) const; status_t parseChunk(off64_t *offset); status_t parseTrackFragmentHeader(off64_t offset, off64_t size); @@ -459,11 +464,12 @@ media_status_t MPEG4Extractor::getTrackMetaData( [=] { int64_t duration; int32_t samplerate; + // Only for audio track. if (track->has_elst && mHeaderTimescale != 0 && AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) && AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) { - // elst has to be processed only the first time this function is called + // Elst has to be processed only the first time this function is called. track->has_elst = false; if (track->elst_segment_duration > INT64_MAX) { @@ -479,67 +485,72 @@ media_status_t MPEG4Extractor::getTrackMetaData( halfscale, mHeaderTimescale, track->timescale); if ((uint32_t)samplerate != track->timescale){ - ALOGV("samplerate:%" PRId32 ", track->timescale and samplerate are different!", samplerate); - } - - int64_t delay; - // delay = ((media_time * samplerate) + halfscale) / track->timescale; - if (__builtin_mul_overflow(media_time, samplerate, &delay) || - __builtin_add_overflow(delay, halfscale, &delay) || - (delay /= track->timescale, false) || - delay > INT32_MAX || - delay < INT32_MIN) { - ALOGW("ignoring edit list with bogus values"); - return; + ALOGV("samplerate:%" PRId32 ", track->timescale and samplerate are different!", + samplerate); + } + // Both delay and paddingsamples have to be set inorder for either to be + // effective in the lower layers. + int64_t delay = 0; + if (media_time > 0) { // Gapless playback + // delay = ((media_time * samplerate) + halfscale) / track->timescale; + if (__builtin_mul_overflow(media_time, samplerate, &delay) || + __builtin_add_overflow(delay, halfscale, &delay) || + (delay /= track->timescale, false) || + delay > INT32_MAX || + delay < INT32_MIN) { + ALOGW("ignoring edit list with bogus values"); + return; + } } ALOGV("delay = %" PRId64, delay); AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay); - int64_t scaled_duration; - // scaled_duration = duration * mHeaderTimescale; - if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration)) { - return; - } - ALOGV("scaled_duration = %" PRId64, scaled_duration); - - int64_t segment_end; - int64_t padding; - int64_t segment_duration_e6; - int64_t media_time_scaled_e6; - int64_t media_time_scaled; - // padding = scaled_duration - ((segment_duration * 1000000) + - // ((media_time * mHeaderTimeScale * 1000000)/track->timescale) ) - // segment_duration is based on timescale in movie header box(mdhd) - // media_time is based on timescale track header/media timescale - if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) || - __builtin_mul_overflow(media_time, mHeaderTimescale, &media_time_scaled) || - __builtin_mul_overflow(media_time_scaled, 1000000, &media_time_scaled_e6)) { - return; - } - media_time_scaled_e6 /= track->timescale; - if(__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end) || - __builtin_sub_overflow(scaled_duration, segment_end, &padding)) { - return; - } - ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding); int64_t paddingsamples = 0; - if (padding < 0) { + if (segment_duration > 0) { + int64_t scaled_duration; + // scaled_duration = duration * mHeaderTimescale; + if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration)) { + return; + } + ALOGV("scaled_duration = %" PRId64, scaled_duration); + + int64_t segment_end; + int64_t padding; + int64_t segment_duration_e6; + int64_t media_time_scaled_e6; + int64_t media_time_scaled; + // padding = scaled_duration - ((segment_duration * 1000000) + + // ((media_time * mHeaderTimescale * 1000000)/track->timescale) ) + // segment_duration is based on timescale in movie header box(mdhd) + // media_time is based on timescale track header/media timescale + if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) || + __builtin_mul_overflow(media_time, mHeaderTimescale, &media_time_scaled) || + __builtin_mul_overflow(media_time_scaled, 1000000, &media_time_scaled_e6)) { + return; + } + media_time_scaled_e6 /= track->timescale; + if (__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end) + || __builtin_sub_overflow(scaled_duration, segment_end, &padding)) { + return; + } + ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding); // track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is) // might be slightly shorter than the segment duration, which would make the // padding negative. Clamp to zero. - padding = 0; - } else { - int64_t halfscale_e6; - int64_t timescale_e6; - // paddingsamples = ((padding * samplerate) + (halfscale * 1000000)) - // / (mHeaderTimescale * 1000000); - if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) || - __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) || - __builtin_mul_overflow(mHeaderTimescale, 1000000, ×cale_e6) || - __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) || - (paddingsamples /= timescale_e6, false) || - paddingsamples > INT32_MAX) { - return; + if (padding > 0) { + int64_t halfscale_mht = mHeaderTimescale / 2; + int64_t halfscale_e6; + int64_t timescale_e6; + // paddingsamples = ((padding * samplerate) + (halfscale_mht * 1000000)) + // / (mHeaderTimescale * 1000000); + if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) || + __builtin_mul_overflow(halfscale_mht, 1000000, &halfscale_e6) || + __builtin_mul_overflow(mHeaderTimescale, 1000000, ×cale_e6) || + __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) || + (paddingsamples /= timescale_e6, false) || + paddingsamples > INT32_MAX) { + return; + } } } ALOGV("paddingsamples = %" PRId64, paddingsamples); @@ -668,6 +679,7 @@ status_t MPEG4Extractor::readMetaData() { track->includes_expensive_metadata = false; track->skipTrack = false; track->timescale = 1000000; + track->elstShiftStartTicks = 0; } } @@ -965,6 +977,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMEDIAFORMAT_KEY_MIME, "application/octet-stream"); track->has_elst = false; track->subsample_encryption = false; + track->elstShiftStartTicks = 0; } off64_t stop_offset = *offset + chunk_size; @@ -1092,6 +1105,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (entry_count != 1) { // we only support a single entry at the moment, for gapless playback + // or start offset ALOGW("ignoring edit list with %d entries", entry_count); } else { off64_t entriesoffset = data_offset + 8; @@ -3929,9 +3943,15 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { } } + if (track->has_elst and !strncasecmp("video/", mime, 6) and track->elst_media_time > 0) { + track->elstShiftStartTicks = track->elst_media_time; + ALOGV("video track->elstShiftStartTicks :%" PRId64, track->elst_media_time); + } + MPEG4Source *source = new MPEG4Source( track->meta, mDataSource, track->timescale, track->sampleTable, - mSidxEntries, trex, mMoofOffset, itemTable); + mSidxEntries, trex, mMoofOffset, itemTable, + track->elstShiftStartTicks); if (source->init() != OK) { delete source; return NULL; @@ -4332,7 +4352,8 @@ MPEG4Source::MPEG4Source( Vector &sidx, const Trex *trex, off64_t firstMoofOffset, - const sp &itemTable) + const sp &itemTable, + int32_t elstShiftStartTicks) : mFormat(format), mDataSource(dataSource), mTimescale(timeScale), @@ -4360,7 +4381,8 @@ MPEG4Source::MPEG4Source( mBuffer(NULL), mSrcBuffer(NULL), mIsHeif(itemTable != NULL), - mItemTable(itemTable) { + mItemTable(itemTable), + mElstShiftStartTicks(elstShiftStartTicks) { memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo)); @@ -4445,11 +4467,31 @@ MPEG4Source::MPEG4Source( } status_t MPEG4Source::init() { + status_t err = OK; + const char *mime; + CHECK(AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime)); if (mFirstMoofOffset != 0) { off64_t offset = mFirstMoofOffset; - return parseChunk(&offset); + err = parseChunk(&offset); + if(err == OK && !strncasecmp("video/", mime, 6) + && !mCurrentSamples.isEmpty()) { + // Start offset should be less or equal to composition time of first sample. + // ISO : sample_composition_time_offset, version 0 (unsigned) for major brands. + mElstShiftStartTicks = std::min(mElstShiftStartTicks, + (*mCurrentSamples.begin()).compositionOffset); + } + return err; } - return OK; + + if (!strncasecmp("video/", mime, 6)) { + uint32_t firstSampleCTS = 0; + err = mSampleTable->getMetaDataForSample(0, NULL, NULL, &firstSampleCTS); + // Start offset should be less or equal to composition time of first sample. + // Composition time stamp of first sample cannot be negative. + mElstShiftStartTicks = std::min(mElstShiftStartTicks, (int32_t)firstSampleCTS); + } + + return err; } MPEG4Source::~MPEG4Source() { @@ -4990,7 +5032,7 @@ status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) { status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { - ALOGV("MPEG4Extractor::parseTrackFragmentRun"); + ALOGV("MPEG4Source::parseTrackFragmentRun"); if (size < 8) { return -EINVAL; } @@ -5132,10 +5174,10 @@ status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { } ALOGV("adding sample %d at offset 0x%08" PRIx64 ", size %u, duration %u, " - " flags 0x%08x", i + 1, + " flags 0x%08x ctsOffset %" PRIu32, i + 1, dataOffset, sampleSize, sampleDuration, (flags & kFirstSampleFlagsPresent) && i == 0 - ? firstSampleFlags : sampleFlags); + ? firstSampleFlags : sampleFlags, sampleCtsOffset); tmp.offset = dataOffset; tmp.size = sampleSize; tmp.duration = sampleDuration; @@ -5227,6 +5269,7 @@ media_status_t MPEG4Source::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { + if (mIsHeif) { CHECK(mSampleTable == NULL); CHECK(mItemTable != NULL); @@ -5264,6 +5307,9 @@ media_status_t MPEG4Source::read( CHECK(!"Should not be here."); break; } + if( mode != ReadOptions::SEEK_FRAME_INDEX) { + seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale; + } uint32_t sampleIndex; status_t err = mSampleTable->findSampleAtTime( @@ -5305,6 +5351,7 @@ media_status_t MPEG4Source::read( if (mode == ReadOptions::SEEK_CLOSEST || mode == ReadOptions::SEEK_FRAME_INDEX) { + sampleTime -= mElstShiftStartTicks; targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale; } @@ -5343,6 +5390,10 @@ media_status_t MPEG4Source::read( if (!mIsHeif) { err = mSampleTable->getMetaDataForSample( mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts); + if(err == OK) { + cts -= mElstShiftStartTicks; + } + } else { err = mItemTable->getImageOffsetAndSize( options && options->getSeekTo(&seekTimeUs, &mode) ? @@ -5623,6 +5674,10 @@ media_status_t MPEG4Source::fragmentedRead( ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { + seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale; + ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRId32, seekTimeUs, + mElstShiftStartTicks); + int numSidxEntries = mSegments.size(); if (numSidxEntries != 0) { int64_t totalTime = 0; @@ -5709,6 +5764,8 @@ media_status_t MPEG4Source::fragmentedRead( offset = smpl->offset; size = smpl->size; cts = mCurrentTime + smpl->compositionOffset; + cts -= mElstShiftStartTicks; + mCurrentTime += smpl->duration; isSyncSample = (mCurrentSampleIndex == 0); diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 79d5ff6dd0..fadfb5061f 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -85,6 +85,7 @@ private: bool has_elst; int64_t elst_media_time; uint64_t elst_segment_duration; + int32_t elstShiftStartTicks; bool subsample_encryption; }; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6ad0417fc3..60b236d7bb 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4421,8 +4421,8 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1); // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames - h264type.nRefFrames = 1; - h264type.nBFrames = 0; + // h264type.nRefFrames = 1; + // h264type.nBFrames = 0; h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames); h264type.nAllowedPictureTypes = diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a48466ade3..b45eb03450 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -137,6 +137,8 @@ public: private: enum { + // TODO: need to increase this considering the bug + // about camera app not sending video frames continuously? kMaxCttsOffsetTimeUs = 1000000LL, // 1 second kSampleArraySize = 1000, }; @@ -317,6 +319,7 @@ private: ListTableEntries *mStssTableEntries; ListTableEntries *mSttsTableEntries; ListTableEntries *mCttsTableEntries; + ListTableEntries *mElstTableEntries; // 3columns: segDuration, mediaTime, mediaRate int64_t mMinCttsOffsetTimeUs; int64_t mMinCttsOffsetTicks; @@ -416,6 +419,8 @@ private: // Duration is time scale based void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur); void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur); + void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime, + int16_t mediaRate, int16_t mediaRateFraction); bool isTrackMalFormed() const; void sendTrackSummary(bool hasMultipleTracks); @@ -448,6 +453,7 @@ private: void writeVideoFourCCBox(); void writeMetadataFourCCBox(); void writeStblBox(bool use32BitOffset); + void writeEdtsBox(); Track(const Track &); Track &operator=(const Track &); @@ -483,6 +489,7 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) { mStartTimestampUs = -1ll; mStartTimeOffsetMs = -1; + mStartTimeOffsetBFramesUs = 0; mPaused = false; mStarted = false; mWriterThreadStarted = false; @@ -1272,6 +1279,10 @@ void MPEG4Writer::writeMoovBox(int64_t durationUs) { // Adjust the global start time. mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs; + // Add mStartTimeOffsetBFramesUs(-ve or zero) to the duration of first entry in STTS. + mStartTimeOffsetBFramesUs = minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs; + ALOGV("mStartTimeOffsetBFramesUs :%" PRId32, mStartTimeOffsetBFramesUs); + for (List::iterator it = mTracks.begin(); it != mTracks.end(); ++it) { if (!(*it)->isHeic()) { @@ -1747,6 +1758,11 @@ int64_t MPEG4Writer::getStartTimestampUs() { return mStartTimestampUs; } +int32_t MPEG4Writer::getStartTimeOffsetBFramesUs() { + Mutex::Autolock autoLock(mLock); + return mStartTimeOffsetBFramesUs; +} + size_t MPEG4Writer::numTracks() { Mutex::Autolock autolock(mLock); return mTracks.size(); @@ -1776,6 +1792,7 @@ MPEG4Writer::Track::Track( mStssTableEntries(new ListTableEntries(1000)), mSttsTableEntries(new ListTableEntries(1000)), mCttsTableEntries(new ListTableEntries(1000)), + mElstTableEntries(new ListTableEntries(3)), // Reserve 3 rows, a row has 3 items mMinCttsOffsetTimeUs(0), mMinCttsOffsetTicks(0), mMaxCttsOffsetTicks(0), @@ -1842,46 +1859,48 @@ MPEG4Writer::Track::Track( // Clear all the internal states except the CSD data. void MPEG4Writer::Track::resetInternal() { - mDone = false; - mPaused = false; - mResumed = false; - mStarted = false; - mGotStartKeyFrame = false; - mIsMalformed = false; - mTrackDurationUs = 0; - mEstimatedTrackSizeBytes = 0; - mSamplesHaveSameSize = 0; - if (mStszTableEntries != NULL) { - delete mStszTableEntries; - mStszTableEntries = new ListTableEntries(1000); - } - - if (mStcoTableEntries != NULL) { - delete mStcoTableEntries; - mStcoTableEntries = new ListTableEntries(1000); - } - if (mCo64TableEntries != NULL) { - delete mCo64TableEntries; - mCo64TableEntries = new ListTableEntries(1000); - } - - if (mStscTableEntries != NULL) { - delete mStscTableEntries; - mStscTableEntries = new ListTableEntries(1000); - } - if (mStssTableEntries != NULL) { - delete mStssTableEntries; - mStssTableEntries = new ListTableEntries(1000); - } - if (mSttsTableEntries != NULL) { - delete mSttsTableEntries; - mSttsTableEntries = new ListTableEntries(1000); - } - if (mCttsTableEntries != NULL) { - delete mCttsTableEntries; - mCttsTableEntries = new ListTableEntries(1000); - } - mReachedEOS = false; + mDone = false; + mPaused = false; + mResumed = false; + mStarted = false; + mGotStartKeyFrame = false; + mIsMalformed = false; + mTrackDurationUs = 0; + mEstimatedTrackSizeBytes = 0; + mSamplesHaveSameSize = 0; + if (mStszTableEntries != NULL) { + delete mStszTableEntries; + mStszTableEntries = new ListTableEntries(1000); + } + if (mStcoTableEntries != NULL) { + delete mStcoTableEntries; + mStcoTableEntries = new ListTableEntries(1000); + } + if (mCo64TableEntries != NULL) { + delete mCo64TableEntries; + mCo64TableEntries = new ListTableEntries(1000); + } + if (mStscTableEntries != NULL) { + delete mStscTableEntries; + mStscTableEntries = new ListTableEntries(1000); + } + if (mStssTableEntries != NULL) { + delete mStssTableEntries; + mStssTableEntries = new ListTableEntries(1000); + } + if (mSttsTableEntries != NULL) { + delete mSttsTableEntries; + mSttsTableEntries = new ListTableEntries(1000); + } + if (mCttsTableEntries != NULL) { + delete mCttsTableEntries; + mCttsTableEntries = new ListTableEntries(1000); + } + if (mElstTableEntries != NULL) { + delete mElstTableEntries; + mElstTableEntries = new ListTableEntries(3); + } + mReachedEOS = false; } void MPEG4Writer::Track::updateTrackSizeEstimate() { @@ -1900,6 +1919,7 @@ void MPEG4Writer::Track::updateTrackSizeEstimate() { mStssTableEntries->count() * 4 + // stss box size mSttsTableEntries->count() * 8 + // stts box size mCttsTableEntries->count() * 8 + // ctts box size + mElstTableEntries->count() * 12 + // elst box size stcoBoxSizeBytes + // stco box size stszBoxSizeBytes; // stsz box size } @@ -1936,6 +1956,16 @@ void MPEG4Writer::Track::addOneCttsTableEntry( mCttsTableEntries->add(htonl(duration)); } +void MPEG4Writer::Track::addOneElstTableEntry( + uint32_t segmentDuration, int32_t mediaTime, int16_t mediaRate, int16_t mediaRateFraction) { + ALOGV("segmentDuration:%u, mediaTime:%d", segmentDuration, mediaTime); + ALOGV("mediaRate :%" PRId16 ", mediaRateFraction :%" PRId16 ", Ored %u", mediaRate, + mediaRateFraction, ((((uint32_t)mediaRate) << 16) | ((uint32_t)mediaRateFraction))); + mElstTableEntries->add(htonl(segmentDuration)); + mElstTableEntries->add(htonl(mediaTime)); + mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction)); +} + status_t MPEG4Writer::setNextFd(int fd) { ALOGV("addNextFd"); Mutex::Autolock l(mLock); @@ -2173,6 +2203,7 @@ MPEG4Writer::Track::~Track() { delete mSttsTableEntries; delete mStssTableEntries; delete mCttsTableEntries; + delete mElstTableEntries; mStszTableEntries = NULL; mStcoTableEntries = NULL; @@ -2181,6 +2212,7 @@ MPEG4Writer::Track::~Track() { mSttsTableEntries = NULL; mStssTableEntries = NULL; mCttsTableEntries = NULL; + mElstTableEntries = NULL; if (mCodecSpecificData != NULL) { free(mCodecSpecificData); @@ -3612,6 +3644,7 @@ void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) { uint32_t now = getMpeg4Time(); mOwner->beginBox("trak"); writeTkhdBox(now); + writeEdtsBox(); mOwner->beginBox("mdia"); writeMdhdBox(now); writeHdlrBox(); @@ -3982,6 +4015,33 @@ void MPEG4Writer::Track::writeHdlrBox() { mOwner->endBox(); } +void MPEG4Writer::Track::writeEdtsBox(){ + ALOGV("%s : getStartTimeOffsetTimeUs of track:%" PRId64 " us", getTrackType(), + getStartTimeOffsetTimeUs()); + + // Prepone video playback. + if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) { + int32_t mvhdTimeScale = mOwner->getTimeScale(); + uint32_t tkhdDuration = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6; + int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs()) + * mTimeScale + 5E5) / 1E6; + if (tkhdDuration > 0 && mediaTime > 0) { + addOneElstTableEntry(tkhdDuration, mediaTime, 1, 0); + } + } + + if (mElstTableEntries->count() == 0) { + return; + } + + mOwner->beginBox("edts"); + mOwner->beginBox("elst"); + mOwner->writeInt32(0); // version=0, flags=0 + mElstTableEntries->write(mOwner); + mOwner->endBox(); // elst; + mOwner->endBox(); // edts +} + void MPEG4Writer::Track::writeMdhdBox(uint32_t now) { int64_t trakDurationUs = getDurationUs(); int64_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6; @@ -4118,7 +4178,9 @@ void MPEG4Writer::Track::writeSttsBox() { uint32_t duration; CHECK(mSttsTableEntries->get(duration, 1)); duration = htonl(duration); // Back to host byte order - mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1); + int32_t startTimeOffsetScaled = (((getStartTimeOffsetTimeUs() + + mOwner->getStartTimeOffsetBFramesUs()) * mTimeScale) + 500000LL) / 1000000LL; + mSttsTableEntries->set(htonl((int32_t)duration + startTimeOffsetScaled), 1); } mSttsTableEntries->write(mOwner); mOwner->endBox(); // stts diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h index f18940dadb..1abef8cabe 100644 --- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h +++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h @@ -110,6 +110,7 @@ private: uint32_t mInterleaveDurationUs; int32_t mTimeScale; int64_t mStartTimestampUs; + int32_t mStartTimeOffsetBFramesUs; // Start time offset when B Frames are present int mLatitudex10000; int mLongitudex10000; bool mAreGeoTagsAvailable; @@ -129,6 +130,7 @@ private: void setStartTimestampUs(int64_t timeUs); int64_t getStartTimestampUs(); // Not const + int32_t getStartTimeOffsetBFramesUs(); status_t startTracks(MetaData *params); size_t numTracks(); int64_t estimateMoovBoxSize(int32_t bitRate); -- GitLab From 71886f20d865ce16a732fa769c54789e970ec3ce Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Wed, 26 Dec 2018 14:05:28 +0900 Subject: [PATCH 0658/1530] Re-apply Context#getOpPackageName - Context#getOpPackageName is now public. - Revive the code which uses Context#getOpPackageName Bug: 119748678 Test: build Change-Id: I8efc2891873061ba8caed3213f1be5ad2eebf64e --- .../media/session/MediaController.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index 8c3a013a2d..c060aafba5 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -153,9 +153,7 @@ public final class MediaController { return false; } try { - //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file. - // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling. - return mSessionBinder.sendMediaButton("mContext.getOpPackageName()", mCbStub, + return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub, asSystemService, keyEvent); } catch (RemoteException e) { // System is dead. =( @@ -188,7 +186,7 @@ public final class MediaController { break; } try { - mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, direction, AudioManager.FLAG_SHOW_UI); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); @@ -199,7 +197,7 @@ public final class MediaController { final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE | AudioManager.FLAG_FROM_KEY; try { - mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0, + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); @@ -369,7 +367,7 @@ public final class MediaController { */ public void setVolumeTo(int value, int flags) { try { - mSessionBinder.setVolumeTo("mContext.getOpPackageName()", mCbStub, value, flags); + mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling setVolumeTo.", e); } @@ -390,7 +388,7 @@ public final class MediaController { */ public void adjustVolume(int direction, int flags) { try { - mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, false, direction, + mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); @@ -457,7 +455,7 @@ public final class MediaController { throw new IllegalArgumentException("command cannot be null or empty"); } try { - mSessionBinder.sendCommand("mContext.getOpPackageName()", mCbStub, command, args, cb); + mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCommand.", e); } @@ -523,7 +521,7 @@ public final class MediaController { if (!mCbRegistered) { try { - mSessionBinder.registerCallbackListener("mContext.getOpPackageName()", mCbStub); + mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub); mCbRegistered = true; } catch (RemoteException e) { Log.e(TAG, "Dead object in registerCallback", e); @@ -670,7 +668,7 @@ public final class MediaController { */ public void prepare() { try { - mSessionBinder.prepare("mContext.getOpPackageName()", mCbStub); + mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare.", e); } @@ -694,7 +692,7 @@ public final class MediaController { "You must specify a non-empty String for prepareFromMediaId."); } try { - mSessionBinder.prepareFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId, + mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); @@ -721,7 +719,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.prepareFromSearch("mContext.getOpPackageName()", mCbStub, query, + mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + query + ").", e); @@ -746,7 +744,7 @@ public final class MediaController { "You must specify a non-empty Uri for prepareFromUri."); } try { - mSessionBinder.prepareFromUri("mContext.getOpPackageName()", mCbStub, uri, extras); + mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); } @@ -757,7 +755,7 @@ public final class MediaController { */ public void play() { try { - mSessionBinder.play("mContext.getOpPackageName()", mCbStub); + mSessionBinder.play(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play.", e); } @@ -776,7 +774,7 @@ public final class MediaController { "You must specify a non-empty String for playFromMediaId."); } try { - mSessionBinder.playFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId, + mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); @@ -799,7 +797,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.playFromSearch("mContext.getOpPackageName()", mCbStub, query, extras); + mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + query + ").", e); } @@ -818,7 +816,7 @@ public final class MediaController { "You must specify a non-empty Uri for playFromUri."); } try { - mSessionBinder.playFromUri("mContext.getOpPackageName()", mCbStub, uri, extras); + mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + uri + ").", e); } @@ -830,7 +828,7 @@ public final class MediaController { */ public void skipToQueueItem(long id) { try { - mSessionBinder.skipToQueueItem("mContext.getOpPackageName()", mCbStub, id); + mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id); } catch (RemoteException e) { Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); } @@ -842,7 +840,7 @@ public final class MediaController { */ public void pause() { try { - mSessionBinder.pause("mContext.getOpPackageName()", mCbStub); + mSessionBinder.pause(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling pause.", e); } @@ -854,7 +852,7 @@ public final class MediaController { */ public void stop() { try { - mSessionBinder.stop("mContext.getOpPackageName()", mCbStub); + mSessionBinder.stop(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling stop.", e); } @@ -867,7 +865,7 @@ public final class MediaController { */ public void seekTo(long pos) { try { - mSessionBinder.seekTo("mContext.getOpPackageName()", mCbStub, pos); + mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos); } catch (RemoteException e) { Log.wtf(TAG, "Error calling seekTo.", e); } @@ -879,7 +877,7 @@ public final class MediaController { */ public void fastForward() { try { - mSessionBinder.fastForward("mContext.getOpPackageName()", mCbStub); + mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling fastForward.", e); } @@ -890,7 +888,7 @@ public final class MediaController { */ public void skipToNext() { try { - mSessionBinder.next("mContext.getOpPackageName()", mCbStub); + mSessionBinder.next(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling next.", e); } @@ -902,7 +900,7 @@ public final class MediaController { */ public void rewind() { try { - mSessionBinder.rewind("mContext.getOpPackageName()", mCbStub); + mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rewind.", e); } @@ -913,7 +911,7 @@ public final class MediaController { */ public void skipToPrevious() { try { - mSessionBinder.previous("mContext.getOpPackageName()", mCbStub); + mSessionBinder.previous(mContext.getOpPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling previous.", e); } @@ -928,7 +926,7 @@ public final class MediaController { */ public void setRating(Rating rating) { try { - mSessionBinder.rate("mContext.getOpPackageName()", mCbStub, rating); + mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rate.", e); } @@ -963,7 +961,7 @@ public final class MediaController { throw new IllegalArgumentException("CustomAction cannot be null."); } try { - mSessionBinder.sendCustomAction("mContext.getOpPackageName()", mCbStub, action, args); + mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCustomAction.", e); } -- GitLab From a84a5a306f3addd205a8f3277c83bd22325f878a Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Thu, 27 Dec 2018 15:08:25 -0800 Subject: [PATCH 0659/1530] rate_control.cpp:removed redundant NULL checks First argument (VideoEncData *) of the functions RC_UpdateBuffer, targetBitCalculation and calculateQuantizer_Multipass in rate_control.cpp file of libstagefright_m4vh263enc SoftMPEG4Encoder was checked against NULL after it was already refered successfully. Hence, those redundant checks are removed now. Above mentioned functions' VideoEncData pointer argument can't be null as those functions are called only after successful initialisation in PVInitVideoEncoder by SoftMPEG4Encoder::initEncoder. rate_control.cpp is compiled as a part of static library : libstagefright_m4vh263enc. This library can only be used using SoftMPEG4Encoder wrapper which makes sure VideoEncData * was allocated required memory. Bug: 112154365 Test: Encoded a YUV file using test case libstagefright_m4vh263enc_test and generated a bit stream output file without any error. Change-Id: Id7f08cc57350bc70729a82afebbc79ba83dac72d --- .../codecs/m4v_h263/enc/src/rate_control.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/codecs/m4v_h263/enc/src/rate_control.cpp b/media/libstagefright/codecs/m4v_h263/enc/src/rate_control.cpp index 53149c1991..ecc3217f58 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/src/rate_control.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/src/rate_control.cpp @@ -377,15 +377,15 @@ void RC_ResetSkipNextFrame(VideoEncData *video, Int currLayer) /* In/out : Nr, B, Rr */ /* Return : Void */ /* Modified : */ +/* Input argument "video" is guaranteed non-null by caller */ /* ======================================================================== */ - PV_STATUS RC_UpdateBuffer(VideoEncData *video, Int currLayer, Int num_skip) { rateControl *rc = video->rc[currLayer]; MultiPass *pMP = video->pMP[currLayer]; - if (video == NULL || rc == NULL || pMP == NULL) + if (rc == NULL || pMP == NULL) return PV_FAIL; rc->VBV_fullness -= (Int)(rc->bitrate / rc->framerate * num_skip); //rc[currLayer]->Rp; @@ -524,6 +524,7 @@ PV_STATUS RC_UpdateBXRCParams(void *input) /* In/out : rc->T */ /* Return : Void */ /* Modified : */ +/* Input argument "input" is guaranteed non-null by caller */ /* ================================================================================ */ void targetBitCalculation(void *input) @@ -537,7 +538,7 @@ void targetBitCalculation(void *input) Int diff_counter_BTsrc, diff_counter_BTdst, prev_counter_diff, curr_counter_diff, bound; /* BT = Bit Transfer, for pMP->counter_BTsrc, pMP->counter_BTdst */ - if (video == NULL || currVol == NULL || pMP == NULL || rc == NULL) + if (currVol == NULL || pMP == NULL || rc == NULL) return; /* some stuff about frame dropping remained here to be done because pMP cannot be inserted into updateRateControl()*/ @@ -693,6 +694,7 @@ void targetBitCalculation(void *input) /* In/out : rc->T and rc->Qc */ /* Return : Void */ /* Modified : */ +/* Input argument "input" is guaranteed non-null by caller */ /* ================================================================================ */ /* Mad based variable bit allocation + QP calculation with a new quadratic method */ @@ -708,7 +710,7 @@ void calculateQuantizer_Multipass(void *input) float curr_mad, prev_mad, curr_RD, prev_RD, average_mad, aver_QP; - if (video == NULL || currVol == NULL || pMP == NULL || rc == NULL) + if (currVol == NULL || pMP == NULL || rc == NULL) return; /* Mad based variable bit allocation */ -- GitLab From 338857364319bacb70bcaa524c0837e693eb2814 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Thu, 20 Dec 2018 10:34:37 -0800 Subject: [PATCH 0660/1530] Camera: Add vndk test for logical camera Test: Newly added test pass Bug: 120566141 Change-Id: Ib726561bef172ecd3c24844c98938f15e03cc838 --- .../tests/AImageReaderVendorTest.cpp | 468 +++++++++++++----- 1 file changed, 348 insertions(+), 120 deletions(-) diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 579412ecf4..f9bb3acefe 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -55,38 +55,27 @@ static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; class CameraHelper { public: - CameraHelper(native_handle_t* imgReaderAnw) : mImgReaderAnw(imgReaderAnw) {} + CameraHelper(const char* id, ACameraManager *manager) : + mImgReaderAnw(nullptr), mCameraId(id), mCameraManager(manager) {} ~CameraHelper() { closeCamera(); } - int initCamera() { - if (mImgReaderAnw == nullptr) { + struct PhysicalImgReaderInfo { + const char* physicalCameraId; + native_handle_t* anw; + }; + int initCamera(native_handle_t* imgReaderAnw, + const std::vector& physicalImgReaders) { + if (imgReaderAnw == nullptr) { ALOGE("Cannot initialize camera before image reader get initialized."); return -1; } - int ret; - - mCameraManager = ACameraManager_create(); - if (mCameraManager == nullptr) { - ALOGE("Failed to create ACameraManager."); + if (mIsCameraReady) { + ALOGE("initCamera should only be called once."); return -1; } - ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList); - if (ret != AMEDIA_OK) { - ALOGE("Failed to get cameraIdList: ret=%d", ret); - return ret; - } - if (mCameraIdList->numCameras < 1) { - ALOGW("Device has no camera on board."); - return 0; - } - - // We always use the first camera. - mCameraId = mCameraIdList->cameraIds[0]; - if (mCameraId == nullptr) { - ALOGE("Failed to get cameraId."); - return -1; - } + int ret; + mImgReaderAnw = imgReaderAnw; ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice); if (ret != AMEDIA_OK || mDevice == nullptr) { @@ -94,18 +83,6 @@ class CameraHelper { return -1; } - ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId, &mCameraMetadata); - if (ret != ACAMERA_OK || mCameraMetadata == nullptr) { - ALOGE("Get camera %s characteristics failure. ret %d, metadata %p", mCameraId, ret, - mCameraMetadata); - return -1; - } - - if (!isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) { - ALOGW("Camera does not support BACKWARD_COMPATIBLE."); - return 0; - } - // Create capture session ret = ACaptureSessionOutputContainer_create(&mOutputs); if (ret != AMEDIA_OK) { @@ -122,6 +99,25 @@ class CameraHelper { ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret); return ret; } + + for (auto& physicalStream : physicalImgReaders) { + ACaptureSessionOutput* sessionOutput = nullptr; + ret = ACaptureSessionPhysicalOutput_create(physicalStream.anw, + physicalStream.physicalCameraId, &sessionOutput); + if (ret != ACAMERA_OK) { + ALOGE("ACaptureSessionPhysicalOutput_create failed, ret=%d", ret); + return ret; + } + ret = ACaptureSessionOutputContainer_add(mOutputs, sessionOutput); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret); + return ret; + } + mExtraOutputs.push_back(sessionOutput); + // Assume that at most one physical stream per physical camera. + mPhysicalCameraIds.push_back(physicalStream.physicalCameraId); + } + ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession); if (ret != AMEDIA_OK) { ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret); @@ -145,21 +141,25 @@ class CameraHelper { return ret; } + for (auto& physicalStream : physicalImgReaders) { + ACameraOutputTarget* outputTarget = nullptr; + ret = ACameraOutputTarget_create(physicalStream.anw, &outputTarget); + if (ret != AMEDIA_OK) { + ALOGE("ACameraOutputTarget_create failed, ret=%d", ret); + return ret; + } + ret = ACaptureRequest_addTarget(mStillRequest, outputTarget); + if (ret != AMEDIA_OK) { + ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret); + return ret; + } + mReqExtraOutputs.push_back(outputTarget); + } + mIsCameraReady = true; return 0; } - bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) { - ACameraMetadata_const_entry entry; - ACameraMetadata_getConstEntry( - mCameraMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry); - for (uint32_t i = 0; i < entry.count; i++) { - if (entry.data.u8[i] == cap) { - return true; - } - } - return false; - } bool isCameraReady() { return mIsCameraReady; } @@ -169,6 +169,10 @@ class CameraHelper { ACameraOutputTarget_free(mReqImgReaderOutput); mReqImgReaderOutput = nullptr; } + for (auto& outputTarget : mReqExtraOutputs) { + ACameraOutputTarget_free(outputTarget); + } + mReqExtraOutputs.clear(); if (mStillRequest) { ACaptureRequest_free(mStillRequest); mStillRequest = nullptr; @@ -182,6 +186,10 @@ class CameraHelper { ACaptureSessionOutput_free(mImgReaderOutput); mImgReaderOutput = nullptr; } + for (auto& extraOutput : mExtraOutputs) { + ACaptureSessionOutput_free(extraOutput); + } + mExtraOutputs.clear(); if (mOutputs) { ACaptureSessionOutputContainer_free(mOutputs); mOutputs = nullptr; @@ -191,19 +199,6 @@ class CameraHelper { ACameraDevice_close(mDevice); mDevice = nullptr; } - if (mCameraMetadata) { - ACameraMetadata_free(mCameraMetadata); - mCameraMetadata = nullptr; - } - // Destroy camera manager - if (mCameraIdList) { - ACameraManager_deleteCameraIdList(mCameraIdList); - mCameraIdList = nullptr; - } - if (mCameraManager) { - ACameraManager_delete(mCameraManager); - mCameraManager = nullptr; - } mIsCameraReady = false; } @@ -213,6 +208,12 @@ class CameraHelper { &seqId); } + int takeLogicalCameraPicture() { + int seqId; + return ACameraCaptureSession_logicalCamera_capture(mSession, &mLogicalCaptureCallbacks, + 1, &mStillRequest, &seqId); + } + bool checkCallbacks(int pictureCount) { std::lock_guard lock(mMutex); if (mCompletedCaptureCallbackCount != pictureCount) { @@ -241,22 +242,22 @@ class CameraHelper { native_handle_t* mImgReaderAnw = nullptr; // not owned by us. - // Camera manager - ACameraManager* mCameraManager = nullptr; - ACameraIdList* mCameraIdList = nullptr; // Camera device - ACameraMetadata* mCameraMetadata = nullptr; ACameraDevice* mDevice = nullptr; // Capture session ACaptureSessionOutputContainer* mOutputs = nullptr; ACaptureSessionOutput* mImgReaderOutput = nullptr; + std::vector mExtraOutputs; + ACameraCaptureSession* mSession = nullptr; // Capture request ACaptureRequest* mStillRequest = nullptr; ACameraOutputTarget* mReqImgReaderOutput = nullptr; + std::vector mReqExtraOutputs; bool mIsCameraReady = false; const char* mCameraId; + ACameraManager* mCameraManager; int mCompletedCaptureCallbackCount = 0; std::mutex mMutex; ACameraCaptureSession_captureCallbacks mCaptureCallbacks = { @@ -264,7 +265,6 @@ class CameraHelper { this, // context nullptr, // onCaptureStarted nullptr, // onCaptureProgressed - // onCaptureCompleted, called serially, so no lock needed. [](void* ctx , ACameraCaptureSession *, ACaptureRequest *, const ACameraMetadata *) { CameraHelper *ch = static_cast(ctx); @@ -275,8 +275,44 @@ class CameraHelper { nullptr, // onCaptureSequenceCompleted nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost - }; + }; + std::vector mPhysicalCameraIds; + ACameraCaptureSession_logicalCamera_captureCallbacks mLogicalCaptureCallbacks = { + // TODO: Add tests for other callbacks + this, // context + nullptr, // onCaptureStarted + nullptr, // onCaptureProgressed + [](void* ctx , ACameraCaptureSession *, ACaptureRequest *, + const ACameraMetadata *, size_t physicalResultCount, + const char** physicalCameraIds, const ACameraMetadata** physicalResults) { + CameraHelper *ch = static_cast(ctx); + std::lock_guard lock(ch->mMutex); + ASSERT_EQ(physicalResultCount, ch->mPhysicalCameraIds.size()); + for (size_t i = 0; i < physicalResultCount; i++) { + ASSERT_TRUE(physicalCameraIds[i] != nullptr); + ASSERT_TRUE(physicalResults[i] != nullptr); + ASSERT_NE(std::find(ch->mPhysicalCameraIds.begin(), + ch->mPhysicalCameraIds.end(), physicalCameraIds[i]), + ch->mPhysicalCameraIds.end()); + + // Verify frameNumber and sensorTimestamp exist in physical + // result metadata + ACameraMetadata_const_entry entry; + ACameraMetadata_getConstEntry( + physicalResults[i], ACAMERA_SYNC_FRAME_NUMBER, &entry); + ASSERT_EQ(entry.count, 1); + ACameraMetadata_getConstEntry( + physicalResults[i], ACAMERA_SENSOR_TIMESTAMP, &entry); + ASSERT_EQ(entry.count, 1); + } + ch->mCompletedCaptureCallbackCount++; + }, + nullptr, // onCaptureFailed + nullptr, // onCaptureSequenceCompleted + nullptr, // onCaptureSequenceAborted + nullptr, // onCaptureBufferLost + }; }; class ImageReaderTestCase { @@ -476,84 +512,276 @@ class ImageReaderTestCase { AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved}; }; -bool takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) { - int ret = 0; - ImageReaderTestCase testCase( - kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, - readerAsync); - ret = testCase.initImageReader(); - if (ret < 0) { - ALOGE("Unable to initialize ImageReader"); - return false; - } - CameraHelper cameraHelper(testCase.getNativeWindow()); - ret = cameraHelper.initCamera(); - if (ret < 0) { - ALOGE("Unable to initialize camera helper"); - return false; - } +class AImageReaderVendorTest : public ::testing::Test { + public: + void SetUp() override { + mCameraManager = ACameraManager_create(); + if (mCameraManager == nullptr) { + ALOGE("Failed to create ACameraManager."); + return; + } - if (!cameraHelper.isCameraReady()) { - ALOGW("Camera is not ready after successful initialization. It's either due to camera on " - "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on " - "board."); - return true; + camera_status_t ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList); + if (ret != ACAMERA_OK) { + ALOGE("Failed to get cameraIdList: ret=%d", ret); + return; + } + if (mCameraIdList->numCameras < 1) { + ALOGW("Device has no camera on board."); + return; + } + } + void TearDown() override { + // Destroy camera manager + if (mCameraIdList) { + ACameraManager_deleteCameraIdList(mCameraIdList); + mCameraIdList = nullptr; + } + if (mCameraManager) { + ACameraManager_delete(mCameraManager); + mCameraManager = nullptr; + } } - for (int i = 0; i < pictureCount; i++) { - ret = cameraHelper.takePicture(); + bool takePictures(const char* id, uint64_t readerUsage, int readerMaxImages, + bool readerAsync, int pictureCount) { + int ret = 0; + + ImageReaderTestCase testCase( + kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, + readerAsync); + ret = testCase.initImageReader(); + if (ret < 0) { + ALOGE("Unable to initialize ImageReader"); + return false; + } + + CameraHelper cameraHelper(id, mCameraManager); + ret = cameraHelper.initCamera(testCase.getNativeWindow(), {}); if (ret < 0) { - ALOGE("Unable to take picture"); + ALOGE("Unable to initialize camera helper"); return false; } + + if (!cameraHelper.isCameraReady()) { + ALOGW("Camera is not ready after successful initialization. It's either due to camera " + "on board lacks BACKWARDS_COMPATIBLE capability or the device does not have " + "camera on board."); + return true; + } + + for (int i = 0; i < pictureCount; i++) { + ret = cameraHelper.takePicture(); + if (ret < 0) { + ALOGE("Unable to take picture"); + return false; + } + } + + // Sleep until all capture finished + for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { + usleep(kCaptureWaitUs); + if (testCase.getAcquiredImageCount() == pictureCount) { + ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, + pictureCount); + break; + } + } + return testCase.getAcquiredImageCount() == pictureCount && + cameraHelper.checkCallbacks(pictureCount); } - // Sleep until all capture finished - for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { - usleep(kCaptureWaitUs); - if (testCase.getAcquiredImageCount() == pictureCount) { - ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, - pictureCount); - break; + bool testTakePicturesNative(const char* id) { + for (auto& readerUsage : + {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) { + for (auto& readerMaxImages : {1, 4, 8}) { + for (auto& readerAsync : {true, false}) { + for (auto& pictureCount : {1, 4, 8}) { + if (!takePictures(id, readerUsage, readerMaxImages, + readerAsync, pictureCount)) { + ALOGE("Test takePictures failed for test case usage=%" PRIu64 + ", maxImages=%d, async=%d, pictureCount=%d", + readerUsage, readerMaxImages, readerAsync, pictureCount); + return false; + } + } + } + } } + return true; } - return testCase.getAcquiredImageCount() == pictureCount && - cameraHelper.checkCallbacks(pictureCount); -} -class AImageReaderWindowHandleTest : public ::testing::Test { - public: - void SetUp() override { + // Camera manager + ACameraManager* mCameraManager = nullptr; + ACameraIdList* mCameraIdList = nullptr; + + bool isCapabilitySupported(ACameraMetadata* staticInfo, + acamera_metadata_enum_android_request_available_capabilities_t cap) { + ACameraMetadata_const_entry entry; + ACameraMetadata_getConstEntry( + staticInfo, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry); + for (uint32_t i = 0; i < entry.count; i++) { + if (entry.data.u8[i] == cap) { + return true; + } + } + return false; } - void TearDown() override { + bool isSizeSupportedForFormat(ACameraMetadata* staticInfo, + int32_t format, int32_t width, int32_t height) { + ACameraMetadata_const_entry entry; + ACameraMetadata_getConstEntry(staticInfo, + ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry); + for (uint32_t i = 0; i < entry.count; i += 4) { + if (entry.data.i32[i] == format && + entry.data.i32[i+3] == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + entry.data.i32[i+1] == width && + entry.data.i32[i+2] == height) { + return true; + } + } + return false; } -}; + void findCandidateLogicalCamera(const char **cameraId, + ACameraMetadata** staticMetadata, + std::vector* candidatePhysicalIds) { + // Find first available logical camera + for (int i = 0; i < mCameraIdList->numCameras; i++) { + camera_status_t ret; + ret = ACameraManager_getCameraCharacteristics( + mCameraManager, mCameraIdList->cameraIds[i], staticMetadata); + ASSERT_EQ(ret, ACAMERA_OK); + ASSERT_NE(*staticMetadata, nullptr); + + if (!isCapabilitySupported(*staticMetadata, + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) { + ACameraMetadata_free(*staticMetadata); + *staticMetadata = nullptr; + continue; + } -bool testTakePicturesNative() { - for (auto& readerUsage : - {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) { - for (auto& readerMaxImages : {1, 4, 8}) { - for (auto& readerAsync : {true, false}) { - for (auto& pictureCount : {1, 4, 8}) { - if (!takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) { - ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, " - "async=%d, pictureCount=%d", - readerUsage, readerMaxImages, readerAsync, pictureCount); - return false; - } + // Check returned physical camera Ids are valid + size_t physicalCameraIdCnt = 0; + const char*const* physicalCameraIds = nullptr; + bool isLogicalCamera = ACameraMetadata_isLogicalMultiCamera(*staticMetadata, + &physicalCameraIdCnt, &physicalCameraIds); + ASSERT_TRUE(isLogicalCamera); + ASSERT_GE(physicalCameraIdCnt, 2); + ACameraMetadata* physicalCameraMetadata = nullptr; + candidatePhysicalIds->clear(); + for (size_t j = 0; j < physicalCameraIdCnt && candidatePhysicalIds->size() < 2; j++) { + ASSERT_GT(strlen(physicalCameraIds[j]), 0); + ret = ACameraManager_getCameraCharacteristics( + mCameraManager, physicalCameraIds[j], &physicalCameraMetadata); + ASSERT_EQ(ret, ACAMERA_OK); + ASSERT_NE(physicalCameraMetadata, nullptr); + + if (isSizeSupportedForFormat(physicalCameraMetadata, kTestImageFormat, + kTestImageWidth, kTestImageHeight)) { + candidatePhysicalIds->push_back(physicalCameraIds[j]); } + ACameraMetadata_free(physicalCameraMetadata); + } + if (candidatePhysicalIds->size() == 2) { + *cameraId = mCameraIdList->cameraIds[i]; + return; + } else { + ACameraMetadata_free(*staticMetadata); + *staticMetadata = nullptr; } } + *cameraId = nullptr; + return; + } +}; + +TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) { + // We always use the first camera. + const char* cameraId = mCameraIdList->cameraIds[0]; + ASSERT_TRUE(cameraId != nullptr); + + ACameraMetadata* staticMetadata = nullptr; + camera_status_t ret = ACameraManager_getCameraCharacteristics( + mCameraManager, cameraId, &staticMetadata); + ASSERT_EQ(ret, ACAMERA_OK); + ASSERT_NE(staticMetadata, nullptr); + + bool isBC = isCapabilitySupported(staticMetadata, + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); + + ACameraMetadata_free(staticMetadata); + + if (!isBC) { + ALOGW("Camera does not support BACKWARD_COMPATIBLE."); + return; } - return true; + + EXPECT_TRUE(testTakePicturesNative(cameraId)); } +TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) { + const char* cameraId = nullptr; + ACameraMetadata* staticMetadata = nullptr; + std::vector physicalCameraIds; + + findCandidateLogicalCamera(&cameraId, &staticMetadata, &physicalCameraIds); + if (cameraId == nullptr) { + // Couldn't find logical camera to test + return; + } + + // Test streaming the logical multi-camera + uint64_t readerUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; + int32_t readerMaxImages = 8; + bool readerAsync = false; + const int pictureCount = 6; + std::vector testCases; + for (size_t i = 0; i < 3; i++) { + ImageReaderTestCase* testCase = new ImageReaderTestCase( + kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, + readerAsync); + ASSERT_EQ(testCase->initImageReader(), 0); + testCases.push_back(testCase); + } + + CameraHelper cameraHelper(cameraId, mCameraManager); + std::vector physicalImgReaderInfo; + physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()}); + physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()}); + + int ret = cameraHelper.initCamera(testCases[0]->getNativeWindow(), physicalImgReaderInfo); + ASSERT_EQ(ret, 0); + + if (!cameraHelper.isCameraReady()) { + ALOGW("Camera is not ready after successful initialization. It's either due to camera on " + "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on " + "board."); + return; + } + + for (int i = 0; i < pictureCount; i++) { + ret = cameraHelper.takeLogicalCameraPicture(); + ASSERT_EQ(ret, 0); + } + + // Sleep until all capture finished + for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { + usleep(kCaptureWaitUs); + if (testCases[0]->getAcquiredImageCount() == pictureCount) { + ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, + pictureCount); + break; + } + } + ASSERT_EQ(testCases[0]->getAcquiredImageCount(), pictureCount); + ASSERT_EQ(testCases[1]->getAcquiredImageCount(), pictureCount); + ASSERT_EQ(testCases[2]->getAcquiredImageCount(), pictureCount); + ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); -TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) { - EXPECT_TRUE(testTakePicturesNative()); + ACameraMetadata_free(staticMetadata); } } // namespace -- GitLab From de68228564d2686c702e93c0f13f7ff75b50e939 Mon Sep 17 00:00:00 2001 From: Jaewan Kim Date: Fri, 28 Dec 2018 21:22:10 +0900 Subject: [PATCH 0661/1530] MediaController: Use Context#getPackageName() for RemoteUserInfo Bug: 111817607, Bug: 111817367 Test: Manual test, incluing the scenario at b/110525559 Change-Id: I1e5f7ba4d04d7b1f414cbdcd723e208f0fea29c3 --- .../media/session/ISessionController.aidl | 4 +- .../media/session/ISessionManager.aidl | 16 ++--- .../media/session/MediaController.java | 58 ++++++++++--------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl index 74897f73e1..433b12fad9 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl @@ -48,9 +48,9 @@ interface ISessionController { PendingIntent getLaunchPendingIntent(); long getFlags(); ParcelableVolumeInfo getVolumeAttributes(); - void adjustVolume(String packageName, ISessionControllerCallback caller, + void adjustVolume(String packageName, String opPackageName, ISessionControllerCallback caller, boolean asSystemService, int direction, int flags); - void setVolumeTo(String packageName, ISessionControllerCallback caller, + void setVolumeTo(String packageName, String opPackageName, ISessionControllerCallback caller, int value, int flags); // These commands are for the TransportControls diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl index 3578c16036..d6c226f8b6 100644 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl +++ b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl @@ -17,7 +17,6 @@ package android.media.session; import android.content.ComponentName; import android.media.IRemoteVolumeController; -import android.media.ISessionTokensListener; import android.media.session.IActiveSessionsListener; import android.media.session.ICallback; import android.media.session.IOnMediaKeyListener; @@ -36,9 +35,10 @@ interface ISessionManager { List getSessions(in ComponentName compName, int userId); void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, boolean needWakeLock); - void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, - int stream, boolean musicOnly); - void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags); + void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService, + in KeyEvent keyEvent, int stream, boolean musicOnly); + void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream, + int delta, int flags); void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, int userId); void removeSessionsListener(in IActiveSessionsListener listener); @@ -55,12 +55,4 @@ interface ISessionManager { // MediaSession2 boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); - boolean createSession2(in Bundle sessionToken); - void destroySession2(in Bundle sessionToken); - List getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly, - String packageName); - - void addSessionTokensListener(in ISessionTokensListener listener, int userId, - String packageName); - void removeSessionTokensListener(in ISessionTokensListener listener, String packageName); } diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index c060aafba5..eaebcfb9f7 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -153,7 +153,7 @@ public final class MediaController { return false; } try { - return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub, + return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub, asSystemService, keyEvent); } catch (RemoteException e) { // System is dead. =( @@ -186,8 +186,9 @@ public final class MediaController { break; } try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, - direction, AudioManager.FLAG_SHOW_UI); + mSessionBinder.adjustVolume(mContext.getPackageName(), + mContext.getOpPackageName(), mCbStub, true, direction, + AudioManager.FLAG_SHOW_UI); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); } @@ -197,8 +198,8 @@ public final class MediaController { final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE | AudioManager.FLAG_FROM_KEY; try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0, - flags); + mSessionBinder.adjustVolume(mContext.getPackageName(), + mContext.getOpPackageName(), mCbStub, true, 0, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy", e); } @@ -367,7 +368,8 @@ public final class MediaController { */ public void setVolumeTo(int value, int flags) { try { - mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags); + mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(), + mCbStub, value, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling setVolumeTo.", e); } @@ -388,8 +390,8 @@ public final class MediaController { */ public void adjustVolume(int direction, int flags) { try { - mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction, - flags); + mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(), + mCbStub, false, direction, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); } @@ -455,7 +457,7 @@ public final class MediaController { throw new IllegalArgumentException("command cannot be null or empty"); } try { - mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb); + mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCommand.", e); } @@ -521,7 +523,7 @@ public final class MediaController { if (!mCbRegistered) { try { - mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub); + mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub); mCbRegistered = true; } catch (RemoteException e) { Log.e(TAG, "Dead object in registerCallback", e); @@ -668,7 +670,7 @@ public final class MediaController { */ public void prepare() { try { - mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub); + mSessionBinder.prepare(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare.", e); } @@ -692,7 +694,7 @@ public final class MediaController { "You must specify a non-empty String for prepareFromMediaId."); } try { - mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); @@ -719,7 +721,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query, + mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + query + ").", e); @@ -744,7 +746,7 @@ public final class MediaController { "You must specify a non-empty Uri for prepareFromUri."); } try { - mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); } @@ -755,7 +757,7 @@ public final class MediaController { */ public void play() { try { - mSessionBinder.play(mContext.getOpPackageName(), mCbStub); + mSessionBinder.play(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play.", e); } @@ -774,7 +776,7 @@ public final class MediaController { "You must specify a non-empty String for playFromMediaId."); } try { - mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId, + mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); @@ -797,7 +799,7 @@ public final class MediaController { query = ""; } try { - mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras); + mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + query + ").", e); } @@ -816,7 +818,7 @@ public final class MediaController { "You must specify a non-empty Uri for playFromUri."); } try { - mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras); + mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras); } catch (RemoteException e) { Log.wtf(TAG, "Error calling play(" + uri + ").", e); } @@ -828,7 +830,7 @@ public final class MediaController { */ public void skipToQueueItem(long id) { try { - mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id); + mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id); } catch (RemoteException e) { Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); } @@ -840,7 +842,7 @@ public final class MediaController { */ public void pause() { try { - mSessionBinder.pause(mContext.getOpPackageName(), mCbStub); + mSessionBinder.pause(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling pause.", e); } @@ -852,7 +854,7 @@ public final class MediaController { */ public void stop() { try { - mSessionBinder.stop(mContext.getOpPackageName(), mCbStub); + mSessionBinder.stop(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling stop.", e); } @@ -865,7 +867,7 @@ public final class MediaController { */ public void seekTo(long pos) { try { - mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos); + mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos); } catch (RemoteException e) { Log.wtf(TAG, "Error calling seekTo.", e); } @@ -877,7 +879,7 @@ public final class MediaController { */ public void fastForward() { try { - mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub); + mSessionBinder.fastForward(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling fastForward.", e); } @@ -888,7 +890,7 @@ public final class MediaController { */ public void skipToNext() { try { - mSessionBinder.next(mContext.getOpPackageName(), mCbStub); + mSessionBinder.next(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling next.", e); } @@ -900,7 +902,7 @@ public final class MediaController { */ public void rewind() { try { - mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub); + mSessionBinder.rewind(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rewind.", e); } @@ -911,7 +913,7 @@ public final class MediaController { */ public void skipToPrevious() { try { - mSessionBinder.previous(mContext.getOpPackageName(), mCbStub); + mSessionBinder.previous(mContext.getPackageName(), mCbStub); } catch (RemoteException e) { Log.wtf(TAG, "Error calling previous.", e); } @@ -926,7 +928,7 @@ public final class MediaController { */ public void setRating(Rating rating) { try { - mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating); + mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating); } catch (RemoteException e) { Log.wtf(TAG, "Error calling rate.", e); } @@ -961,7 +963,7 @@ public final class MediaController { throw new IllegalArgumentException("CustomAction cannot be null."); } try { - mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args); + mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args); } catch (RemoteException e) { Log.d(TAG, "Dead object in sendCustomAction.", e); } -- GitLab From b6826d9e88b521c5c970cf748db83b43c70d56d1 Mon Sep 17 00:00:00 2001 From: Victor Chang Date: Tue, 20 Nov 2018 19:11:07 +0000 Subject: [PATCH 0662/1530] Use ICU in libandroidicu libicuuc and libicui18n are moved into APEX, but they have no stable ABI due to the version suffix. Use libandroidicu which provides stable symbol. See http://go/apex-stable-icu4c-interface for the design. See http://aosp/801855 for libandroidicu implementation. Bug: 117094880 Test: m checkbuild Change-Id: Ida6aac85dfb79bf8e7a3a2540e567ee211279e09 --- .../plugins/forward-lock/FwdLockEngine/Android.bp | 3 +-- media/libmedia/Android.bp | 6 ++---- media/mediaserver/Android.mk | 2 +- services/audiopolicy/Android.mk | 2 +- services/mediaextractor/Android.mk | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp index 28a78aa21a..bb9d7ec791 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp @@ -29,8 +29,7 @@ cc_library_shared { srcs: ["src/FwdLockEngine.cpp"], shared_libs: [ - "libicui18n", - "libicuuc", + "libandroidicu", "libutils", "liblog", "libdl", diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 6002e95d0d..7d759e08ed 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -216,8 +216,7 @@ cc_library { "libutils", "libbinder", "libsonivox", - "libicuuc", - "libicui18n", + "libandroidicu", "libexpat", "libcamera_client", "libstagefright_foundation", @@ -232,8 +231,7 @@ cc_library { export_shared_lib_headers: [ "libaudioclient", "libbinder", - "libicuuc", - "libicui18n", + "libandroidicu", "libsonivox", "libmedia_omx", ], diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index f7597db816..1fbb85e6d0 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -20,7 +20,7 @@ LOCAL_SHARED_LIBRARIES := \ libmediaplayerservice \ libutils \ libbinder \ - libicuuc \ + libandroidicu \ android.hardware.media.omx@1.0 \ LOCAL_STATIC_LIBRARIES := \ diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index bfa1b5e92f..621e9a405d 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -85,7 +85,7 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_SHARED_LIBRARIES += libmedia_helper LOCAL_SHARED_LIBRARIES += libmediametrics -LOCAL_SHARED_LIBRARIES += libhidlbase libicuuc libxml2 +LOCAL_SHARED_LIBRARIES += libhidlbase libxml2 ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index e31eadc8d8..6101c8a201 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -40,7 +40,7 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ - liblog libbase libicuuc libavservices_minijail + liblog libbase libandroidicu libavservices_minijail LOCAL_STATIC_LIBRARIES := libicuandroid_utils LOCAL_MODULE:= mediaextractor LOCAL_INIT_RC := mediaextractor.rc -- GitLab From 7676a406a2fff64c8c465c879300a7a35e2e0739 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Thu, 27 Dec 2018 11:04:16 -0800 Subject: [PATCH 0663/1530] Make assignment of mPlugin members consistent Before, it was possible for mPlugin, mPluginV1_1, and mPluginV1_2 to be assigned to different plugins. Now, they are guaranteed to always point to the same object. Also to be safe, mPlugin is set to NULL if there is an initailization error. Test: Run gts on blueline Bug: 112386116 Bug: 121382196 Change-Id: Ie3ff7369e0c66d4502fab3f4a1d18b2882140143 --- drm/libmediadrm/DrmHal.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 14ff49356e..66c509f647 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -553,12 +553,14 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], const String8& appPackageName) { Mutex::Autolock autoLock(mLock); - for (size_t i = 0; i < mFactories.size(); i++) { + for (size_t i = mFactories.size() - 1; i >= 0; i--) { if (mFactories[i]->isCryptoSchemeSupported(uuid)) { - mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); - if (mPlugin != NULL) { + 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); + break; } } } @@ -567,6 +569,9 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], mInitCheck = ERROR_UNSUPPORTED; } else { if (!mPlugin->setListener(this).isOk()) { + mPlugin = NULL; + mPluginV1_1 = NULL; + mPluginV1_2 = NULL; mInitCheck = DEAD_OBJECT; } else { mInitCheck = OK; -- GitLab From bda70da1f0fb32d37abd704ff8618e55661be0a9 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 19 Dec 2018 07:30:15 -0800 Subject: [PATCH 0664/1530] Dynamic audio policies: multi-zone through uid/device affinity Setting audio device affinity for a given uid augments all audio mix criteria that do not route to the given devices to exclude the uid. AudioPolicyManager: after changing the device affinity, check the outputs addressing the devices to re-evaluate audio routing Bug: 111647296 Test: requires device with routing policy started by CarService Change-Id: I72de54ae067151fe6ac2ec43b78fe544a9fd9888 --- media/libaudioclient/AudioPolicy.cpp | 44 ++++++++-- media/libaudioclient/AudioSystem.cpp | 13 +++ media/libaudioclient/IAudioPolicyService.cpp | 74 ++++++++++++++++- .../include/media/AudioPolicy.h | 17 +++- .../include/media/AudioSystem.h | 4 + .../include/media/IAudioPolicyService.h | 5 ++ services/audiopolicy/Android.mk | 1 + services/audiopolicy/AudioPolicyInterface.h | 4 + .../include/AudioPolicyMix.h | 4 + .../managerdefinitions/src/AudioPolicyMix.cpp | 81 +++++++++++++++++++ .../managerdefault/AudioPolicyManager.cpp | 53 ++++++++++++ .../managerdefault/AudioPolicyManager.h | 3 + .../service/AudioPolicyInterfaceImpl.cpp | 25 ++++++ .../audiopolicy/service/AudioPolicyService.h | 4 + 14 files changed, 325 insertions(+), 7 deletions(-) diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp index d1f752587e..9601d6dd87 100644 --- a/media/libaudioclient/AudioPolicy.cpp +++ b/media/libaudioclient/AudioPolicy.cpp @@ -21,6 +21,22 @@ namespace android { +// +// AudioDeviceTypeAddr implementation +// +status_t AudioDeviceTypeAddr::readFromParcel(Parcel *parcel) { + mType = (audio_devices_t) parcel->readInt32(); + mAddress = parcel->readString8(); + return NO_ERROR; +} + +status_t AudioDeviceTypeAddr::writeToParcel(Parcel *parcel) const { + parcel->writeInt32((int32_t) mType); + parcel->writeString8(mAddress); + return NO_ERROR; +} + + // // AudioMixMatchCriterion implementation // @@ -40,11 +56,22 @@ AudioMixMatchCriterion::AudioMixMatchCriterion(audio_usage_t usage, status_t AudioMixMatchCriterion::readFromParcel(Parcel *parcel) { mRule = parcel->readInt32(); - if (mRule == RULE_MATCH_ATTRIBUTE_USAGE || - mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE) { - mValue.mUsage = (audio_usage_t)parcel->readInt32(); - } else { - mValue.mSource = (audio_source_t)parcel->readInt32(); + switch (mRule) { + case RULE_MATCH_ATTRIBUTE_USAGE: + case RULE_EXCLUDE_ATTRIBUTE_USAGE: + mValue.mUsage = (audio_usage_t) parcel->readInt32(); + break; + case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: + case RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET: + mValue.mSource = (audio_source_t) parcel->readInt32(); + break; + case RULE_MATCH_UID: + case RULE_EXCLUDE_UID: + mValue.mUid = (uid_t) parcel->readInt32(); + break; + default: + ALOGE("Trying to build AudioMixMatchCriterion from unknown rule %d", mRule); + return BAD_VALUE; } return NO_ERROR; } @@ -116,4 +143,11 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const return NO_ERROR; } +void AudioMix::excludeUid(uid_t uid) const { + AudioMixMatchCriterion crit; + crit.mRule = RULE_EXCLUDE_UID; + crit.mValue.mUid = uid; + mCriteria.add(crit); +} + } // namespace android diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index dc7531ceb0..baeae8b709 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1236,6 +1236,19 @@ status_t AudioSystem::registerPolicyMixes(const Vector& mixes, bool re return aps->registerPolicyMixes(mixes, registration); } +status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const Vector& devices) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->setUidDeviceAffinities(uid, devices); +} + +status_t AudioSystem::removeUidDeviceAffinities(uid_t uid) { + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->removeUidDeviceAffinities(uid); +} + status_t AudioSystem::startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 0ce8b16f47..272415c59f 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -90,6 +90,8 @@ enum { SET_ASSISTANT_UID, SET_A11Y_SERVICES_UIDS, IS_HAPTIC_PLAYBACK_SUPPORTED, + SET_UID_DEVICE_AFFINITY, + REMOVE_UID_DEVICE_AFFINITY, }; #define MAX_ITEMS_PER_LIST 1024 @@ -990,6 +992,50 @@ public: return reply.readBool(); } + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& devices) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + + data.writeInt32((int32_t) uid); + size_t size = devices.size(); + size_t sizePosition = data.dataPosition(); + data.writeInt32((int32_t) size); + size_t finalSize = size; + for (size_t i = 0; i < size; i++) { + size_t position = data.dataPosition(); + if (devices[i].writeToParcel(&data) != NO_ERROR) { + data.setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = data.dataPosition(); + data.setDataPosition(sizePosition); + data.writeInt32(finalSize); + data.setDataPosition(position); + } + + status_t status = remote()->transact(SET_UID_DEVICE_AFFINITY, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t removeUidDeviceAffinities(uid_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + + data.writeInt32((int32_t) uid); + + status_t status = remote()->transact(REMOVE_UID_DEVICE_AFFINITY, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1048,7 +1094,9 @@ status_t BnAudioPolicyService::onTransact( case GET_SURROUND_FORMATS: case SET_SURROUND_FORMAT_ENABLED: case SET_ASSISTANT_UID: - case SET_A11Y_SERVICES_UIDS: { + case SET_A11Y_SERVICES_UIDS: + case SET_UID_DEVICE_AFFINITY: + case REMOVE_UID_DEVICE_AFFINITY: { if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), @@ -1811,6 +1859,30 @@ status_t BnAudioPolicyService::onTransact( CHECK_INTERFACE(IAudioPolicyService, data, reply); bool isSupported = isHapticPlaybackSupported(); reply->writeBool(isSupported); + return NO_ERROR; + } + + case SET_UID_DEVICE_AFFINITY: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + const uid_t uid = (uid_t) data.readInt32(); + Vector devices; + size_t size = (size_t)data.readInt32(); + for (size_t i = 0; i < size; i++) { + AudioDeviceTypeAddr device; + if (device.readFromParcel((Parcel*)&data) == NO_ERROR) { + devices.add(device); + } + } + status_t status = setUidDeviceAffinities(uid, devices); + reply->writeInt32(status); + return NO_ERROR; + } + + case REMOVE_UID_DEVICE_AFFINITY: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + const uid_t uid = (uid_t) data.readInt32(); + status_t status = removeUidDeviceAffinities(uid); + reply->writeInt32(status); return NO_ERROR; } diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index 8da0069ccb..96e1235e9b 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -56,6 +56,19 @@ namespace android { #define MAX_MIXES_PER_POLICY 10 #define MAX_CRITERIA_PER_MIX 20 +class AudioDeviceTypeAddr { +public: + AudioDeviceTypeAddr() {} + AudioDeviceTypeAddr(audio_devices_t type, String8 address) : + mType(type), mAddress(address) {} + + status_t readFromParcel(Parcel *parcel); + status_t writeToParcel(Parcel *parcel) const; + + audio_devices_t mType; + String8 mAddress; +}; + class AudioMixMatchCriterion { public: AudioMixMatchCriterion() {} @@ -87,7 +100,9 @@ public: status_t readFromParcel(Parcel *parcel); status_t writeToParcel(Parcel *parcel) const; - Vector mCriteria; + void excludeUid(uid_t uid) const; + + mutable Vector mCriteria; uint32_t mMixType; audio_config_t mFormat; uint32_t mRouteFlags; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index b0da5b8e03..781e9dfc41 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -325,6 +325,10 @@ public: static status_t registerPolicyMixes(const Vector& mixes, bool registration); + static status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); + + static status_t removeUidDeviceAffinities(uid_t uid); + static status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 61f3b27d6f..fb4fe9302f 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -167,6 +167,11 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes, bool registration) = 0; + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& devices) + = 0; + + virtual status_t removeUidDeviceAffinities(uid_t uid) = 0; + virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId) = 0; diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index bfa1b5e92f..d71aa15832 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -51,6 +51,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ + libaudioclient \ libsoundtrigger ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index ad12a904a9..1c2b9d7c8b 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -210,6 +210,10 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes) = 0; virtual status_t unregisterPolicyMixes(Vector mixes) = 0; + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& devices) + = 0; + virtual status_t removeUidDeviceAffinities(uid_t uid) = 0; + virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 96c00ea7a7..955e87bd04 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -80,6 +80,10 @@ public: status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); + status_t removeUidDeviceAffinities(uid_t uid); + status_t getDevicesForUid(uid_t uid, Vector& devices) const; + void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 3cf80146e4..776d98f850 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -340,6 +340,87 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return NO_ERROR; } +status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, + const Vector& devices) { + // remove existing rules for this uid + removeUidDeviceAffinities(uid); + + // for each player mix: add a rule to match or exclude the uid based on the device + for (size_t i = 0; i < size(); i++) { + const AudioMix *mix = valueAt(i)->getMix(); + if (mix->mMixType != MIX_TYPE_PLAYERS) { + continue; + } + // check if this mix goes to a device in the list of devices + bool deviceMatch = false; + for (size_t j = 0; j < devices.size(); j++) { + if (devices[j].mType == mix->mDeviceType + && devices[j].mAddress == mix->mDeviceAddress) { + deviceMatch = true; + break; + } + } + if (!deviceMatch) { + // this mix doesn't go to one of the listed devices for the given uid, + // modify its rules to exclude the uid + mix->excludeUid(uid); + } + } + + return NO_ERROR; +} + +status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { + // for each player mix: remove existing rules that match or exclude this uid + for (size_t i = 0; i < size(); i++) { + bool foundUidRule = false; + AudioMix *mix = valueAt(i)->getMix(); + if (mix->mMixType != MIX_TYPE_PLAYERS) { + continue; + } + std::vector criteriaToRemove; + for (size_t j = 0; j < mix->mCriteria.size(); j++) { + const uint32_t rule = mix->mCriteria[j].mRule; + // is this rule affecting the uid? + if (rule == RULE_EXCLUDE_UID + && uid == mix->mCriteria[j].mValue.mUid) { + foundUidRule = true; + criteriaToRemove.push_back(j); + } + } + if (foundUidRule) { + for (size_t j = criteriaToRemove.size() - 1; j >= 0; j--) { + mix->mCriteria.removeAt(criteriaToRemove[j]); + } + } + } + return NO_ERROR; +} + +status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid, + Vector& devices) const { + // for each player mix: find rules that don't exclude this uid, and add the device to the list + for (size_t i = 0; i < size(); i++) { + bool ruleAllowsUid = true; + AudioMix *mix = valueAt(i)->getMix(); + if (mix->mMixType != MIX_TYPE_PLAYERS) { + continue; + } + for (size_t j = 0; j < mix->mCriteria.size(); j++) { + const uint32_t rule = mix->mCriteria[j].mRule; + if (rule == RULE_EXCLUDE_UID + && uid == mix->mCriteria[j].mValue.mUid) { + ruleAllowsUid = false; + break; + } + } + if (ruleAllowsUid) { + devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress)); + } + } + return NO_ERROR; +} + void AudioPolicyMixCollection::dump(String8 *dst) const { dst->append("\nAudio Policy Mix:\n"); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index aa205f0bae..baa5eb385b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2674,6 +2674,59 @@ void AudioPolicyManager::dumpManualSurroundFormats(String8 *dst) const } } +status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid, + const Vector& devices) { + ALOGV("%s() uid=%d num devices %zu", __FUNCTION__, uid, devices.size()); + // uid/device affinity is only for output devices + for (size_t i = 0; i < devices.size(); i++) { + if (!audio_is_output_device(devices[i].mType)) { + ALOGE("setUidDeviceAffinities() device=%08x is NOT an output device", + devices[i].mType); + return BAD_VALUE; + } + } + status_t res = mPolicyMixes.setUidDeviceAffinities(uid, devices); + if (res == NO_ERROR) { + // reevaluate outputs for all given devices + for (size_t i = 0; i < devices.size(); i++) { + sp devDesc = mHwModules.getDeviceDescriptor( + devices[i].mType, devices[i].mAddress, String8()); + SortedVector outputs; + if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + outputs, + devDesc->address()) != NO_ERROR) { + ALOGE("setUidDeviceAffinities() error in checkOutputsForDevice for device=%08x" + " addr=%s", devices[i].mType, devices[i].mAddress.string()); + return INVALID_OPERATION; + } + } + } + return res; +} + +status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) { + ALOGV("%s() uid=%d", __FUNCTION__, uid); + Vector devices; + status_t res = mPolicyMixes.getDevicesForUid(uid, devices); + if (res == NO_ERROR) { + // reevaluate outputs for all found devices + for (size_t i = 0; i < devices.size(); i++) { + sp devDesc = mHwModules.getDeviceDescriptor( + devices[i].mType, devices[i].mAddress, String8()); + SortedVector outputs; + if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + outputs, + devDesc->address()) != NO_ERROR) { + ALOGE("%s() error in checkOutputsForDevice for device=%08x addr=%s", + __FUNCTION__, devices[i].mType, devices[i].mAddress.string()); + return INVALID_OPERATION; + } + } + } + + return res; +} + void AudioPolicyManager::dump(String8 *dst) const { dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 35dd87ca47..9eb1dcfaa6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -218,6 +218,9 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes); virtual status_t unregisterPolicyMixes(Vector mixes); + virtual status_t setUidDeviceAffinities(uid_t uid, + const Vector& devices); + virtual status_t removeUidDeviceAffinities(uid_t uid); virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 439764be89..80503fdaa1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1037,6 +1037,31 @@ status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, } } +status_t AudioPolicyService::setUidDeviceAffinities(uid_t uid, + const Vector& devices) { + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + AutoCallerClear acc; + return mAudioPolicyManager->setUidDeviceAffinities(uid, devices); +} + +status_t AudioPolicyService::removeUidDeviceAffinities(uid_t uid) { + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + AutoCallerClear acc; + return mAudioPolicyManager->removeUidDeviceAffinities(uid); +} + status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId) diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index c44d816cd3..959e757229 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -199,6 +199,10 @@ public: virtual status_t registerPolicyMixes(const Vector& mixes, bool registration); + virtual status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); + + virtual status_t removeUidDeviceAffinities(uid_t uid); + virtual status_t startAudioSource(const struct audio_port_config *source, const audio_attributes_t *attributes, audio_port_handle_t *portId); -- GitLab From adadf1c18f5cf507ae13f0464fceeb717c8c7f94 Mon Sep 17 00:00:00 2001 From: "ji, zhenlong z" Date: Tue, 6 Nov 2018 16:04:51 +0800 Subject: [PATCH 0665/1530] Enable fstat to seccomp filter for x86_64 bug: 120238328 Test: run CTS case - android.security.cts.ProcessMustUseSeccompTest Change-Id: I08ac4d75d77061a48641973cff61d51fee3c5c28 --- .../mediaextractor/seccomp_policy/mediaextractor-x86_64.policy | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy index 6d9ed6f675..35ac458d71 100644 --- a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy +++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy @@ -21,6 +21,7 @@ clone: 1 getuid: 1 setpriority: 1 sigaltstack: 1 +fstat: 1 fstatfs: 1 newfstatat: 1 restart_syscall: 1 -- GitLab From a57cff4e6c41c2eaf7c63cc6c5fc8f06c11f8306 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Thu, 3 Jan 2019 10:06:15 -0800 Subject: [PATCH 0666/1530] MediaRecorder.h:correct MEDIA_RECORDER_ERROR enum MEDIA_RECORDER_ERROR state had an enum value 0 which would not work with some of the existing code using bitwise operators (mask). Hence, set a new value for this state. Bug: 120389907 Test: cts-tradefed run cts-dev --module CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice Change-Id: I06f35b7438ff12050dd34bc64fbdca3a5c07b47a --- media/libmedia/include/media/mediarecorder.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index caa0186cc3..33be5591d4 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -115,9 +115,6 @@ enum video_encoder { * The state machine of the media_recorder. */ enum media_recorder_states { - // Error state. - MEDIA_RECORDER_ERROR = 0, - // Recorder was just created. MEDIA_RECORDER_IDLE = 1 << 0, @@ -132,6 +129,9 @@ enum media_recorder_states { // Recording is in progress. MEDIA_RECORDER_RECORDING = 1 << 4, + + // Error state. + MEDIA_RECORDER_ERROR = 1 << 5, }; // The "msg" code passed to the listener in notify. -- GitLab From a1ac9f8b52693ec9f468712232fd08b2c3aad9c5 Mon Sep 17 00:00:00 2001 From: melvin xu Date: Tue, 18 Dec 2018 13:15:08 +0800 Subject: [PATCH 0667/1530] add color converter for NV12 to RGB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry-picked from https://android-review.googlesource.com/c/platform/frameworks/av/+/773126/ with fixes on non-zero crops) CTS-on-gsi test, CtsMediaTestCases -- android.media.cts.MediaMetadataRetrieverTest#testGetFrameAtIndex failed CtsMediaTestCases -- android.media.cts.MediaMetadataRetrieverTest#testGetFramesAtIndex failed CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_Grid_Handler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_Grid_NoHandler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_Handler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_NoHandler fail [Android Version]: VTS Version 9.0_r2 [CTS pachage version] Suite / Plan VTS / cts-on-gsi Suite / Build 9.0_R2 [device](Any device config may relate this failure) unisoc's device size:1080*1920 [bugzilla bugid] 117044023 [CTS Test Pre–Condition] 1.Language set to EN; 2.Keyguard set to none; 3.Enable GPS, Wifi network, USB debugging, Stay awake, Allow mock locations. 4.CTS version is VTS / cts-on-gsi 9.0_r2 [CTS Test Step]: 1 ./vts-tradefed 2 run cts-on-gsi [Expected Result ]: This case will pass. [Testing Result]: case failed: CtsMediaTestCases android.media.cts.MediaMetadataRetrieverTest#testGetFrameAtIndex failed android.media.cts.MediaMetadataRetrieverTest#testGetFramesAtIndex failed android.media.cts.HeifWriterTest#testInputBitmap_Grid_Handler fail android.media.cts.HeifWriterTest#testInputBitmap_Grid_NoHandler fail android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_Handler fail android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_NoHandler fail [Analysize]: log: 07-30 12:21:07.795 364 489 E FrameDecoder: Unable to convert from format 0x00000015 to 0x7f00a000 07-30 12:21:07.795 364 489 E FrameDecoder: failed to get video frame (err -1010) From the log, we find the testcase is related with colorformat. Bug #117044023 [root cause]: 1. we can get below information from source code: OMX_COLOR_FormatYUV420SemiPlanar = 0x00000015 ; OMX_COLOR_Format32BitRGBA8888 = 0x7f00a000; “ MediaMetadataRetrieverTest#testGetFrameAtIndex†cts case requires the color format of the frame data to be OMX_COLOR_Format32BitRGBA8888 color format. Frameworks\av\media\libstagefright\colorconversion\ColorConverter.cpp : bool ColorConverter::isValid() const { …… case OMX_COLOR_FormatYUV420Planar: return mDstFormat == OMX_COLOR_Format16bitRGB565 || mDstFormat == OMX_COLOR_Format32BitRGBA8888 || mDstFormat == OMX_COLOR_Format32bitBGRA8888; case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return mDstFormat == OMX_COLOR_Format16bitRGB565; ……} ColorConverter does not support color format conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. 2. The input data of this case should be OMX_COLOR_Format32BitRGBA8888 color format, and the ColorConverter in frameworks only support color format conversion from OMX_COLOR_FormatYUV420Planar to OMX_COLOR_Format32BitRGBA8888, does not support from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. But the video hardware decoder of Unisoc device can output YUV data with OMX_COLOR_FormatYUV420SemiPlanar color format, it can not output OMX_COLOR_FormatYUV420Planar color format. So this case failed. [changes]: Add a color conversion code to ColorConverter(Frameworks\av\media\libstagefright\colorconversion\ColorConverter.cpp, the patch is listed below). Enable ColorConverter to support color conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. Because the hardware decoder of Spreadtrum phone does not support OMX_COLOR_FormatYUV420Planar. we need the ColorConverter in frameworks support color format conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. We will request to waive for this. Could you help us or give us a waiver? Thanks a lot. [side effects]:No [self test]: pass [download normally]:Yes [power on/off normally]:Yes [do common repository/branch inspection]:Yes [is there dependence]:No [confirm dependent commit]:No [board]: unisoc device [change_type ] fix [tag_product ] common [test Case]:as testing steps [reviewers]: wenan.hu [Patch Link]: https://android-review.googlesource.com/c/platform/frameworks/av/+/773126 Change-Id: I882f3729a9620b4c5c456a3099b5e8809b4b5545 Signed-off-by: melvin xu DO NOT MERGE: add color converter for NV12 to RGB (cherry-picked from aosp/773126, with more fixes on offset for non-zero crop) CTS-on-gsi test, CtsMediaTestCases -- android.media.cts.MediaMetadataRetrieverTest#testGetFrameAtIndex failed CtsMediaTestCases -- android.media.cts.MediaMetadataRetrieverTest#testGetFramesAtIndex failed CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_Grid_Handler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_Grid_NoHandler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_Handler fail CtsMediaTestCases -- android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_NoHandler fail [Android Version]: VTS Version 9.0_r2 [CTS pachage version] Suite / Plan VTS / cts-on-gsi Suite / Build 9.0_R2 [device](Any device config may relate this failure) unisoc's device size:1080*1920 [bugzilla bugid] 117044023 [CTS Test Pre–Condition] 1.Language set to EN; 2.Keyguard set to none; 3.Enable GPS, Wifi network, USB debugging, Stay awake, Allow mock locations. 4.CTS version is VTS / cts-on-gsi 9.0_r2 [CTS Test Step]: 1 ./vts-tradefed 2 run cts-on-gsi [Expected Result ]: This case will pass. [Testing Result]: case failed: CtsMediaTestCases android.media.cts.MediaMetadataRetrieverTest#testGetFrameAtIndex failed android.media.cts.MediaMetadataRetrieverTest#testGetFramesAtIndex failed android.media.cts.HeifWriterTest#testInputBitmap_Grid_Handler fail android.media.cts.HeifWriterTest#testInputBitmap_Grid_NoHandler fail android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_Handler fail android.media.cts.HeifWriterTest#testInputBitmap_NoGrid_NoHandler fail [Analysize]: log: 07-30 12:21:07.795 364 489 E FrameDecoder: Unable to convert from format 0x00000015 to 0x7f00a000 07-30 12:21:07.795 364 489 E FrameDecoder: failed to get video frame (err -1010) From the log, we find the testcase is related with colorformat. Bug #117044023 [root cause]: 1. we can get below information from source code: OMX_COLOR_FormatYUV420SemiPlanar = 0x00000015 ; OMX_COLOR_Format32BitRGBA8888 = 0x7f00a000; “ MediaMetadataRetrieverTest#testGetFrameAtIndex†cts case requires the color format of the frame data to be OMX_COLOR_Format32BitRGBA8888 color format. Frameworks\av\media\libstagefright\colorconversion\ColorConverter.cpp : bool ColorConverter::isValid() const { …… case OMX_COLOR_FormatYUV420Planar: return mDstFormat == OMX_COLOR_Format16bitRGB565 || mDstFormat == OMX_COLOR_Format32BitRGBA8888 || mDstFormat == OMX_COLOR_Format32bitBGRA8888; case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return mDstFormat == OMX_COLOR_Format16bitRGB565; ……} ColorConverter does not support color format conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. 2. The input data of this case should be OMX_COLOR_Format32BitRGBA8888 color format, and the ColorConverter in frameworks only support color format conversion from OMX_COLOR_FormatYUV420Planar to OMX_COLOR_Format32BitRGBA8888, does not support from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. But the video hardware decoder of Unisoc device can output YUV data with OMX_COLOR_FormatYUV420SemiPlanar color format, it can not output OMX_COLOR_FormatYUV420Planar color format. So this case failed. [changes]: Add a color conversion code to ColorConverter(Frameworks\av\media\libstagefright\colorconversion\ColorConverter.cpp, the patch is listed below). Enable ColorConverter to support color conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. Because the hardware decoder of Spreadtrum phone does not support OMX_COLOR_FormatYUV420Planar. we need the ColorConverter in frameworks support color format conversion from OMX_COLOR_FormatYUV420SemiPlanar to OMX_COLOR_Format32BitRGBA8888. We will request to waive for this. Could you help us or give us a waiver? Thanks a lot. [side effects]:No [self test]: pass [download normally]:Yes [power on/off normally]:Yes [do common repository/branch inspection]:Yes [is there dependence]:No [confirm dependent commit]:No [board]: unisoc device [change_type ] fix [tag_product ] common [test Case]:as testing steps [reviewers]: wenan.hu [Patch Link]: https://android-review.googlesource.com/c/platform/frameworks/av/+/773126 Change-Id: I882f3729a9620b4c5c456a3099b5e8809b4b5545 Signed-off-by: melvin xu --- .../colorconversion/ColorConverter.cpp | 46 ++++++++++++++++++- .../media/stagefright/ColorConverter.h | 3 ++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index 86bd9d628a..d136d9e4b1 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -27,6 +27,7 @@ #include "libyuv/convert_from.h" #include "libyuv/convert_argb.h" +#include "libyuv/planar_functions.h" #include "libyuv/video_common.h" #include #include @@ -91,10 +92,17 @@ bool ColorConverter::isValid() const { case OMX_COLOR_FormatCbYCrY: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: - case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return mDstFormat == OMX_COLOR_Format16bitRGB565; + case OMX_COLOR_FormatYUV420SemiPlanar: +#ifdef USE_LIBYUV + return mDstFormat == OMX_COLOR_Format16bitRGB565 + || mDstFormat == OMX_COLOR_Format32BitRGBA8888; +#else + return mDstFormat == OMX_COLOR_Format16bitRGB565; +#endif + default: return false; } @@ -236,7 +244,11 @@ status_t ColorConverter::convert( break; case OMX_COLOR_FormatYUV420SemiPlanar: +#ifdef USE_LIBYUV + err = convertYUV420SemiPlanarUseLibYUV(src, dst); +#else err = convertYUV420SemiPlanar(src, dst); +#endif break; case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: @@ -365,6 +377,36 @@ status_t ColorConverter::convertYUV420PlanarUseLibYUV( return OK; } +status_t ColorConverter::convertYUV420SemiPlanarUseLibYUV( + const BitmapParams &src, const BitmapParams &dst) { + uint8_t *dst_ptr = (uint8_t *)dst.mBits + + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp; + + const uint8_t *src_y = + (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft; + + const uint8_t *src_u = + (const uint8_t *)src.mBits + src.mStride * src.mHeight + + (src.mCropTop / 2) * src.mStride + src.mCropLeft; + + switch (mDstFormat) { + case OMX_COLOR_Format16bitRGB565: + libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr, + dst.mStride, src.cropWidth(), src.cropHeight()); + break; + + case OMX_COLOR_Format32BitRGBA8888: + libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr, + dst.mStride, src.cropWidth(), src.cropHeight()); + break; + + default: + return ERROR_UNSUPPORTED; + } + + return OK; +} + std::function getReadFromSrc(OMX_COLOR_FORMATTYPE srcFormat) { @@ -852,7 +894,7 @@ status_t ColorConverter::convertYUV420SemiPlanar( const uint8_t *src_u = (const uint8_t *)src.mBits + src.mHeight * src.mStride + - src.mCropTop * src.mStride / 2 + src.mCropLeft; + (src.mCropTop / 2) * src.mStride + src.mCropLeft; for (size_t y = 0; y < src.cropHeight(); ++y) { for (size_t x = 0; x < src.cropWidth(); x += 2) { diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h index 6d4c1bfa1b..75b0d8e98f 100644 --- a/media/libstagefright/include/media/stagefright/ColorConverter.h +++ b/media/libstagefright/include/media/stagefright/ColorConverter.h @@ -90,6 +90,9 @@ private: status_t convertYUV420PlanarUseLibYUV( const BitmapParams &src, const BitmapParams &dst); + status_t convertYUV420SemiPlanarUseLibYUV( + const BitmapParams &src, const BitmapParams &dst); + status_t convertYUV420Planar16( const BitmapParams &src, const BitmapParams &dst); -- GitLab From 0fbfa6f0a89b56e4c7bb8b9343b4be5512ed805c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 3 Jan 2019 14:30:06 -0800 Subject: [PATCH 0668/1530] Fix various DRM related tests Bug: 122279759 Test: GTS Change-Id: Ibcb9d9edb0f59fff03182d44cea640d8177e34be --- media/libstagefright/MediaTrack.cpp | 2 +- media/ndk/NdkMediaFormat.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 1c1be30ab6..f1584918a8 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -120,7 +120,7 @@ status_t MediaTrackCUnwrapper::read(MediaBufferBase **buffer, const ReadOptions if (format->mFormat->findInt64("timeUs", &val64)) { meta.setInt64(kKeyTime, val64); } - if (format->mFormat->findInt64("duration", &val64)) { + if (format->mFormat->findInt64("durationUs", &val64)) { meta.setInt64(kKeyDuration, val64); } if (format->mFormat->findInt64("target-time", &val64)) { diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 92d3aefa99..fcb706d07f 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -298,7 +298,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES = "crypto-encrypted-s EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_IV = "crypto-iv"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_KEY = "crypto-key"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_MODE = "crypto-mode"; -EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES = "crypto-encrypted-sizes"; +EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES = "crypto-plain-sizes"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK = "crypto-skip-byte-block"; EXPORT const char* AMEDIAFORMAT_KEY_CSD = "csd"; EXPORT const char* AMEDIAFORMAT_KEY_CSD_0 = "csd-0"; -- GitLab From 1da72ee18a983bb98c95affee107957f9c0459ca Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 3 Jan 2019 15:27:52 -0800 Subject: [PATCH 0669/1530] ACodec: log when pcm encoding converter is created Test: MediaCodecTest#testPCMEncoding Bug: 122275566 Change-Id: I53160551e680d4e8fd4c2e93babccb79d95c9fe4 --- media/libstagefright/ACodec.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 60b236d7bb..ceb8a13e32 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2320,12 +2320,16 @@ status_t ACodec::configureCodec( (void)mInputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding); mConverter[kPortIndexInput] = AudioConverter::Create(pcmEncoding, codecPcmEncoding); if (mConverter[kPortIndexInput] != NULL) { + ALOGD("%s: encoder %s input format pcm encoding converter from %d to %d", + __func__, mComponentName.c_str(), pcmEncoding, codecPcmEncoding); mInputFormat->setInt32("pcm-encoding", pcmEncoding); } } else { (void)mOutputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding); mConverter[kPortIndexOutput] = AudioConverter::Create(codecPcmEncoding, pcmEncoding); if (mConverter[kPortIndexOutput] != NULL) { + ALOGD("%s: decoder %s output format pcm encoding converter from %d to %d", + __func__, mComponentName.c_str(), codecPcmEncoding, pcmEncoding); mOutputFormat->setInt32("pcm-encoding", pcmEncoding); } } -- GitLab From 0cb5a09f475cd8759da879216272c581b75e2073 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 3 Jan 2019 16:38:22 -0800 Subject: [PATCH 0670/1530] codec2: make allocation blocking for software codecs Bug: 120490517 Test: bug repro steps Change-Id: Ifb151ad4ba77b013d9e6689177b7b501ecdd54c5 --- .../components/base/SimpleC2Component.cpp | 58 ++++++++++++++++++- .../base/include/SimpleC2Component.h | 3 +- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index 50b4d20f39..b8baec80bf 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -132,6 +132,56 @@ void SimpleC2Component::WorkHandler::onMessageReceived(const sp &msg) } } +class SimpleC2Component::BlockingBlockPool : public C2BlockPool { +public: + BlockingBlockPool(const std::shared_ptr& base): mBase{base} {} + + virtual local_id_t getLocalId() const override { + return mBase->getLocalId(); + } + + virtual C2Allocator::id_t getAllocatorId() const override { + return mBase->getAllocatorId(); + } + + virtual c2_status_t fetchLinearBlock( + uint32_t capacity, + C2MemoryUsage usage, + std::shared_ptr* block) { + c2_status_t status; + do { + status = mBase->fetchLinearBlock(capacity, usage, block); + } while (status == C2_TIMED_OUT); + return status; + } + + virtual c2_status_t fetchCircularBlock( + uint32_t capacity, + C2MemoryUsage usage, + std::shared_ptr* block) { + c2_status_t status; + do { + status = mBase->fetchCircularBlock(capacity, usage, block); + } while (status == C2_TIMED_OUT); + return status; + } + + virtual c2_status_t fetchGraphicBlock( + uint32_t width, uint32_t height, uint32_t format, + C2MemoryUsage usage, + std::shared_ptr* block) { + c2_status_t status; + do { + status = mBase->fetchGraphicBlock(width, height, format, usage, + block); + } while (status == C2_TIMED_OUT); + return status; + } + +private: + std::shared_ptr mBase; +}; + //////////////////////////////////////////////////////////////////////////////// namespace { @@ -446,12 +496,16 @@ bool SimpleC2Component::processQueue() { } } - err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool); + std::shared_ptr blockPool; + err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool); ALOGD("Using output block pool with poolID %llu => got %llu - %d", (unsigned long long)poolId, (unsigned long long)( - mOutputBlockPool ? mOutputBlockPool->getLocalId() : 111000111), + blockPool ? blockPool->getLocalId() : 111000111), err); + if (err == C2_OK) { + mOutputBlockPool = std::make_shared(blockPool); + } return err; }(); if (err != C2_OK) { diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h index b3a98f4607..43029a9b48 100644 --- a/media/codec2/components/base/include/SimpleC2Component.h +++ b/media/codec2/components/base/include/SimpleC2Component.h @@ -234,7 +234,8 @@ private: typedef std::unordered_map> PendingWork; Mutexed mPendingWork; - std::shared_ptr mOutputBlockPool; + class BlockingBlockPool; + std::shared_ptr mOutputBlockPool; SimpleC2Component() = delete; }; -- GitLab From 84889cbfd31daadca6fd0ef8048bf99104373e30 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 3 Jan 2019 17:07:54 -0800 Subject: [PATCH 0671/1530] aacenc: fix timestamp calculation Test: log audio timestamps Change-Id: Ic09173b871a25366930e38b36e7c398c0dba24cc --- media/codec2/components/aac/C2SoftAacEnc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index aeefbdba41..87730ae328 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -468,7 +468,8 @@ void C2SoftAacEnc::process( if (outargs.numOutBytes > 0) { mInputSize = 0; - int consumed = ((capacity / sizeof(int16_t)) - inargs.numInSamples); + int consumed = (capacity / sizeof(int16_t)) - inargs.numInSamples + + outargs.numInSamples; mInputTimeUs = work->input.ordinal.timestamp + (consumed * 1000000ll / channelCount / sampleRate); buffer = createLinearBuffer(block, 0, outargs.numOutBytes); -- GitLab From d1d435a76bc728e3e0883d525c2cf34f32c6517d Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Tue, 18 Dec 2018 17:39:42 -0800 Subject: [PATCH 0672/1530] Prevent camera access when sensor privacy is enabled Test: Manually verified apps cannot use the camera when sensor privacy \ is enabled. Bug: 110842805 Change-Id: Ic3fed8272e90f3f64e0f6c342569c27e1a476014 --- services/camera/libcameraservice/Android.bp | 2 + .../camera/libcameraservice/CameraService.cpp | 80 ++++++++++++++++++- .../camera/libcameraservice/CameraService.h | 31 +++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 025f0b2b21..851dd690d9 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -83,6 +83,7 @@ cc_library_shared { "libhidltransport", "libjpeg", "libmemunreachable", + "libsensorprivacy", "libstagefright_foundation", "android.frameworks.cameraservice.common@2.0", "android.frameworks.cameraservice.service@2.0", @@ -100,6 +101,7 @@ cc_library_shared { "libbinder", "libcamera_client", "libfmq", + "libsensorprivacy", ], include_dirs: [ diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6003607600..c3113bf6ea 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,8 @@ void CameraService::onFirstRef() mUidPolicy = new UidPolicy(this); mUidPolicy->registerSelf(); + mSensorPrivacyPolicy = new SensorPrivacyPolicy(this); + mSensorPrivacyPolicy->registerSelf(); sp hcs = HidlCameraService::getInstance(this); if (hcs->registerAsService() != android::OK) { ALOGE("%s: Failed to register default android.frameworks.cameraservice.service@1.0", @@ -230,6 +233,7 @@ void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeS CameraService::~CameraService() { VendorTagDescriptor::clearGlobalVendorTagDescriptor(); mUidPolicy->unregisterSelf(); + mSensorPrivacyPolicy->unregisterSelf(); } void CameraService::onNewProviderRegistered() { @@ -951,6 +955,14 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId, clientName8.string(), clientUid, clientPid, cameraId.string()); } + // If sensor privacy is enabled then prevent access to the camera + if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { + ALOGE("Access Denial: cannot use the camera when sensor privacy is enabled"); + return STATUS_ERROR_FMT(ERROR_DISABLED, + "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" when sensor privacy " + "is enabled", clientName8.string(), clientUid, clientPid, cameraId.string()); + } + // Only use passed in clientPid to check permission. Use calling PID as the client PID that's // connected to camera service directly. originalClientPid = clientPid; @@ -1603,9 +1615,10 @@ Status CameraService::notifySystemEvent(int32_t eventId, switch(eventId) { case ICameraService::EVENT_USER_SWITCHED: { - // Try to register for UID policy updates, in case we're recovering + // Try to register for UID and sensor privacy policy updates, in case we're recovering // from a system server crash mUidPolicy->registerSelf(); + mSensorPrivacyPolicy->registerSelf(); doUserSwitch(/*newUserIds*/ args); break; } @@ -2576,6 +2589,59 @@ void CameraService::UidPolicy::updateOverrideUid(uid_t uid, String16 callingPack } } +// ---------------------------------------------------------------------------- +// SensorPrivacyPolicy +// ---------------------------------------------------------------------------- +void CameraService::SensorPrivacyPolicy::registerSelf() { + Mutex::Autolock _l(mSensorPrivacyLock); + if (mRegistered) { + return; + } + SensorPrivacyManager spm; + spm.addSensorPrivacyListener(this); + mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled(); + status_t res = spm.linkToDeath(this); + if (res == OK) { + mRegistered = true; + ALOGV("SensorPrivacyPolicy: Registered with SensorPrivacyManager"); + } +} + +void CameraService::SensorPrivacyPolicy::unregisterSelf() { + Mutex::Autolock _l(mSensorPrivacyLock); + SensorPrivacyManager spm; + spm.removeSensorPrivacyListener(this); + spm.unlinkToDeath(this); + mRegistered = false; + ALOGV("SensorPrivacyPolicy: Unregistered with SensorPrivacyManager"); +} + +bool CameraService::SensorPrivacyPolicy::isSensorPrivacyEnabled() { + Mutex::Autolock _l(mSensorPrivacyLock); + return mSensorPrivacyEnabled; +} + +binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) { + { + Mutex::Autolock _l(mSensorPrivacyLock); + mSensorPrivacyEnabled = enabled; + } + // if sensor privacy is enabled then block all clients from accessing the camera + if (enabled) { + sp service = mService.promote(); + if (service != nullptr) { + service->blockAllClients(); + } + } + return binder::Status::ok(); +} + +void CameraService::SensorPrivacyPolicy::binderDied(const wp& /*who*/) { + Mutex::Autolock _l(mSensorPrivacyLock); + ALOGV("SensorPrivacyPolicy: SensorPrivacyManager has died"); + mRegistered = false; +} + // ---------------------------------------------------------------------------- // CameraState // ---------------------------------------------------------------------------- @@ -3062,6 +3128,18 @@ void CameraService::blockClientsForUid(uid_t uid) { } } +void CameraService::blockAllClients() { + const auto clients = mActiveClientManager.getAll(); + for (auto& current : clients) { + if (current != nullptr) { + const auto basicClient = current->getValue(); + if (basicClient.get() != nullptr) { + basicClient->block(); + } + } + } +} + // NOTE: This is a remote API - make sure all args are validated status_t CameraService::shellCommand(int in, int out, int err, const Vector& args) { if (!checkCallingPermission(sManageCameraPermission, nullptr, nullptr)) { diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index d332f6e6d3..a2961986b2 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -18,6 +18,7 @@ #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H #include +#include #include #include @@ -554,8 +555,35 @@ private: std::unordered_map mOverrideUids; }; // class UidPolicy + // If sensor privacy is enabled then all apps, including those that are active, should be + // prevented from accessing the camera. + class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener, + public virtual IBinder::DeathRecipient { + public: + explicit SensorPrivacyPolicy(wp service) + : mService(service), mSensorPrivacyEnabled(false), mRegistered(false) {} + + void registerSelf(); + void unregisterSelf(); + + bool isSensorPrivacyEnabled(); + + binder::Status onSensorPrivacyChanged(bool enabled); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp &who); + + private: + wp mService; + Mutex mSensorPrivacyLock; + bool mSensorPrivacyEnabled; + bool mRegistered; + }; + sp mUidPolicy; + sp mSensorPrivacyPolicy; + // Delay-load the Camera HAL module virtual void onFirstRef(); @@ -825,6 +853,9 @@ private: // Blocks all clients from the UID void blockClientsForUid(uid_t uid); + // Blocks all active clients. + void blockAllClients(); + // Overrides the UID state as if it is idle status_t handleSetUidState(const Vector& args, int err); -- GitLab From a5ecb6497152b311fa9de454af9923ceee3708b2 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 18 Dec 2018 18:24:57 +0530 Subject: [PATCH 0673/1530] codec2: MTS add support for multiple-input in AudioDectest Test: MtsHidlC2V1_0TargetAudioDecTest -I software -P /sdcard/res/ -C c2.android.mp3.decoder Bug: 80272610 Change-Id: Ibe031ffa3815d18a071bc8f2f23c2ba6dce403aa --- .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 93 +- ...ac_stereo_128kbps_48000hz_multi_frame.info | 443 ++++++++++ ..._amrwb_1ch_14kbps_16000hz_multi_frame.info | 460 ++++++++++ ...p3_stereo_192kbps_48000hz_multi_frame.info | 385 +++++++++ ...e_amrnb_1ch_12kbps_8000hz_multi_frame.info | 807 ++++++++++++++++++ 5 files changed, 2157 insertions(+), 31 deletions(-) create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info create mode 100644 media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info create mode 100644 media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index 1e87f38646..d4b973fd5f 100644 --- a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -330,57 +330,72 @@ void getInputChannelInfo( } } +// number of elementary streams per component +#define STREAM_COUNT 2 + // LookUpTable of clips and metadata for component testing void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, - char* info) { + char* info, size_t streamIndex = 0) { struct CompToURL { Codec2AudioDecHidlTest::standardComp comp; - const char* mURL; - const char* info; + const char mURL[STREAM_COUNT][512]; + const char info[STREAM_COUNT][512]; }; + ASSERT_TRUE(streamIndex < STREAM_COUNT); + static const CompToURL kCompToURL[] = { {Codec2AudioDecHidlTest::standardComp::xaac, - "bbb_aac_stereo_128kbps_48000hz.aac", - "bbb_aac_stereo_128kbps_48000hz.info"}, + {"bbb_aac_stereo_128kbps_48000hz.aac", + "bbb_aac_stereo_128kbps_48000hz.aac"}, + {"bbb_aac_stereo_128kbps_48000hz.info", + "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}}, {Codec2AudioDecHidlTest::standardComp::mp3, - "bbb_mp3_stereo_192kbps_48000hz.mp3", - "bbb_mp3_stereo_192kbps_48000hz.info"}, + {"bbb_mp3_stereo_192kbps_48000hz.mp3", + "bbb_mp3_stereo_192kbps_48000hz.mp3"}, + {"bbb_mp3_stereo_192kbps_48000hz.info", + "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}}, {Codec2AudioDecHidlTest::standardComp::aac, - "bbb_aac_stereo_128kbps_48000hz.aac", - "bbb_aac_stereo_128kbps_48000hz.info"}, + {"bbb_aac_stereo_128kbps_48000hz.aac", + "bbb_aac_stereo_128kbps_48000hz.aac"}, + {"bbb_aac_stereo_128kbps_48000hz.info", + "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}}, {Codec2AudioDecHidlTest::standardComp::amrnb, - "sine_amrnb_1ch_12kbps_8000hz.amrnb", - "sine_amrnb_1ch_12kbps_8000hz.info"}, + {"sine_amrnb_1ch_12kbps_8000hz.amrnb", + "sine_amrnb_1ch_12kbps_8000hz.amrnb"}, + {"sine_amrnb_1ch_12kbps_8000hz.info", + "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}}, {Codec2AudioDecHidlTest::standardComp::amrwb, - "bbb_amrwb_1ch_14kbps_16000hz.amrwb", - "bbb_amrwb_1ch_14kbps_16000hz.info"}, + {"bbb_amrwb_1ch_14kbps_16000hz.amrwb", + "bbb_amrwb_1ch_14kbps_16000hz.amrwb"}, + {"bbb_amrwb_1ch_14kbps_16000hz.info", + "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}}, {Codec2AudioDecHidlTest::standardComp::vorbis, - "bbb_vorbis_stereo_128kbps_48000hz.vorbis", - "bbb_vorbis_stereo_128kbps_48000hz.info"}, + {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""}, + {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}}, {Codec2AudioDecHidlTest::standardComp::opus, - "bbb_opus_stereo_128kbps_48000hz.opus", - "bbb_opus_stereo_128kbps_48000hz.info"}, + {"bbb_opus_stereo_128kbps_48000hz.opus", ""}, + {"bbb_opus_stereo_128kbps_48000hz.info", ""}}, {Codec2AudioDecHidlTest::standardComp::g711alaw, - "bbb_g711alaw_1ch_8khz.raw", - "bbb_g711alaw_1ch_8khz.info"}, + {"bbb_g711alaw_1ch_8khz.raw", ""}, + {"bbb_g711alaw_1ch_8khz.info", ""}}, {Codec2AudioDecHidlTest::standardComp::g711mlaw, - "bbb_g711mulaw_1ch_8khz.raw", - "bbb_g711mulaw_1ch_8khz.info"}, + {"bbb_g711mulaw_1ch_8khz.raw", ""}, + {"bbb_g711mulaw_1ch_8khz.info", ""}}, {Codec2AudioDecHidlTest::standardComp::gsm, - "bbb_gsm_1ch_8khz_13kbps.raw", - "bbb_gsm_1ch_8khz_13kbps.info"}, + {"bbb_gsm_1ch_8khz_13kbps.raw", ""}, + {"bbb_gsm_1ch_8khz_13kbps.info", ""}}, {Codec2AudioDecHidlTest::standardComp::raw, - "bbb_raw_1ch_8khz_s32le.raw", - "bbb_raw_1ch_8khz_s32le.info"}, + {"bbb_raw_1ch_8khz_s32le.raw", ""}, + {"bbb_raw_1ch_8khz_s32le.info", ""}}, {Codec2AudioDecHidlTest::standardComp::flac, - "bbb_flac_stereo_680kbps_48000hz.flac", - "bbb_flac_stereo_680kbps_48000hz.info"}, + {"bbb_flac_stereo_680kbps_48000hz.flac", ""}, + {"bbb_flac_stereo_680kbps_48000hz.info", ""}}, }; for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) { if (kCompToURL[i].comp == comp) { - strcat(mURL, kCompToURL[i].mURL); - strcat(info, kCompToURL[i].info); + strcat(mURL, kCompToURL[i].mURL[streamIndex]); + strcat(info, kCompToURL[i].info[streamIndex]); return; } } @@ -491,10 +506,15 @@ TEST_F(Codec2AudioDecHidlTest, configComp) { ASSERT_EQ(mComponent->stop(), C2_OK); } -TEST_F(Codec2AudioDecHidlTest, DecodeTest) { +class Codec2AudioDecDecodeTest : public Codec2AudioDecHidlTest, + public ::testing::WithParamInterface { +}; + +TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { description("Decodes input file"); if (mDisableTest) return; + uint32_t streamIndex = GetParam(); ASSERT_EQ(mComponent->start(), C2_OK); mTimestampDevTest = true; char mURL[512], info[512]; @@ -502,7 +522,12 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) { strcpy(mURL, gEnv->getRes().c_str()); strcpy(info, gEnv->getRes().c_str()); - GetURLForComponent(mCompName, mURL, info); + GetURLForComponent(mCompName, mURL, info, streamIndex); + if (!strcmp(mURL, gEnv->getRes().c_str())) { + ALOGV("EMPTY INPUT gEnv->getRes().c_str() %s mURL %s ", + gEnv->getRes().c_str(), mURL); + return; + } eleInfo.open(info); ASSERT_EQ(eleInfo.is_open(), true); @@ -521,6 +546,9 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) { Info.push_back({bytesCount, flags, timestamp}); } eleInfo.close(); + // Reset total no of frames received + mFramesReceived = 0; + mTimestampUs = 0; int32_t bitStreamInfo[2] = {0}; if (mCompName == raw) { bitStreamInfo[0] = 8000; @@ -577,6 +605,9 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } +INSTANTIATE_TEST_CASE_P(StreamIndexes, Codec2AudioDecDecodeTest, + ::testing::Values(0, 1)); + // thumbnail test TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) { description("Test Request for thumbnail"); diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info new file mode 100644 index 0000000000..182af20b50 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info @@ -0,0 +1,443 @@ +5 32 0 +5 32 0 +337 1 0 +322 1 21333 +279 1 42666 +563 1 64000 +635 1 106666 +634 1 149333 +629 1 192000 +680 1 234666 +688 1 277333 +1036 1 320000 +1040 1 384000 +1009 1 448000 +1020 1 512000 +1357 1 576000 +1353 1 661333 +1351 1 746666 +1351 1 832000 +343 1 917333 +335 1 938666 +339 1 960000 +342 1 981333 +348 1 1002666 +350 1 1024000 +351 1 1045333 +342 1 1066666 +366 1 1088000 +340 1 1109333 +354 1 1130666 +340 1 1152000 +334 1 1173333 +338 1 1194666 +340 1 1216000 +351 1 1237333 +346 1 1258666 +331 1 1280000 +321 1 1301333 +343 1 1322666 +342 1 1344000 +345 1 1365333 +326 1 1386666 +342 1 1408000 +356 1 1429333 +351 1 1450666 +343 1 1472000 +347 1 1493333 +349 1 1514666 +350 1 1536000 +330 1 1557333 +341 1 1578666 +340 1 1600000 +330 1 1621333 +340 1 1642666 +335 1 1664000 +344 1 1685333 +359 1 1706666 +337 1 1728000 +346 1 1749333 +330 1 1770666 +351 1 1792000 +355 1 1813333 +352 1 1834666 +325 1 1856000 +342 1 1877333 +327 1 1898666 +349 1 1920000 +326 1 1941333 +337 1 1962666 +378 1 1984000 +321 1 2005333 +319 1 2026666 +346 1 2048000 +352 1 2069333 +349 1 2090666 +331 1 2112000 +330 1 2133333 +329 1 2154666 +333 1 2176000 +367 1 2197333 +362 1 2218666 +337 1 2240000 +337 1 2261333 +360 1 2282666 +333 1 2304000 +317 1 2325333 +344 1 2346666 +335 1 2368000 +337 1 2389333 +349 1 2410666 +336 1 2432000 +348 1 2453333 +349 1 2474666 +342 1 2496000 +359 1 2517333 +340 1 2538666 +340 1 2560000 +348 1 2581333 +334 1 2602666 +328 1 2624000 +341 1 2645333 +339 1 2666666 +337 1 2688000 +350 1 2709333 +326 1 2730666 +360 1 2752000 +344 1 2773333 +340 1 2794666 +343 1 2816000 +361 1 2837333 +329 1 2858666 +345 1 2880000 +345 1 2901333 +330 1 2922666 +342 1 2944000 +344 1 2965333 +330 1 2986666 +329 1 3008000 +335 1 3029333 +366 1 3050666 +328 1 3072000 +349 1 3093333 +339 1 3114666 +340 1 3136000 +335 1 3157333 +327 1 3178666 +348 1 3200000 +339 1 3221333 +334 1 3242666 +350 1 3264000 +325 1 3285333 +361 1 3306666 +338 1 3328000 +350 1 3349333 +353 1 3370666 +327 1 3392000 +346 1 3413333 +348 1 3434666 +339 1 3456000 +342 1 3477333 +334 1 3498666 +350 1 3520000 +354 1 3541333 +363 1 3562666 +322 1 3584000 +337 1 3605333 +355 1 3626666 +329 1 3648000 +324 1 3669333 +338 1 3690666 +356 1 3712000 +330 1 3733333 +321 1 3754666 +337 1 3776000 +345 1 3797333 +335 1 3818666 +348 1 3840000 +342 1 3861333 +348 1 3882666 +335 1 3904000 +344 1 3925333 +357 1 3946666 +368 1 3968000 +324 1 3989333 +343 1 4010666 +341 1 4032000 +329 1 4053333 +356 1 4074666 +317 1 4096000 +351 1 4117333 +340 1 4138666 +340 1 4160000 +332 1 4181333 +355 1 4202666 +357 1 4224000 +327 1 4245333 +338 1 4266666 +323 1 4288000 +346 1 4309333 +352 1 4330666 +347 1 4352000 +343 1 4373333 +311 1 4394666 +338 1 4416000 +365 1 4437333 +349 1 4458666 +327 1 4480000 +355 1 4501333 +319 1 4522666 +349 1 4544000 +351 1 4565333 +337 1 4586666 +340 1 4608000 +349 1 4629333 +316 1 4650666 +344 1 4672000 +334 1 4693333 +344 1 4714666 +347 1 4736000 +348 1 4757333 +334 1 4778666 +338 1 4800000 +331 1 4821333 +344 1 4842666 +342 1 4864000 +336 1 4885333 +326 1 4906666 +364 1 4928000 +350 1 4949333 +350 1 4970666 +363 1 4992000 +358 1 5013333 +305 1 5034666 +344 1 5056000 +346 1 5077333 +342 1 5098666 +330 1 5120000 +318 1 5141333 +361 1 5162666 +354 1 5184000 +313 1 5205333 +330 1 5226666 +350 1 5248000 +347 1 5269333 +346 1 5290666 +357 1 5312000 +325 1 5333333 +335 1 5354666 +331 1 5376000 +366 1 5397333 +329 1 5418666 +349 1 5440000 +371 1 5461333 +326 1 5482666 +333 1 5504000 +319 1 5525333 +327 1 5546666 +353 1 5568000 +356 1 5589333 +348 1 5610666 +338 1 5632000 +331 1 5653333 +341 1 5674666 +362 1 5696000 +326 1 5717333 +359 1 5738666 +315 1 5760000 +376 1 5781333 +343 1 5802666 +354 1 5824000 +353 1 5845333 +344 1 5866666 +334 1 5888000 +345 1 5909333 +355 1 5930666 +322 1 5952000 +334 1 5973333 +353 1 5994666 +338 1 6016000 +351 1 6037333 +334 1 6058666 +339 1 6080000 +345 1 6101333 +347 1 6122666 +355 1 6144000 +312 1 6165333 +352 1 6186666 +354 1 6208000 +318 1 6229333 +344 1 6250666 +363 1 6272000 +321 1 6293333 +339 1 6314666 +356 1 6336000 +334 1 6357333 +354 1 6378666 +325 1 6400000 +321 1 6421333 +341 1 6442666 +337 1 6464000 +351 1 6485333 +343 1 6506666 +341 1 6528000 +344 1 6549333 +341 1 6570666 +364 1 6592000 +319 1 6613333 +348 1 6634666 +332 1 6656000 +333 1 6677333 +343 1 6698666 +348 1 6720000 +347 1 6741333 +350 1 6762666 +342 1 6784000 +341 1 6805333 +326 1 6826666 +351 1 6848000 +329 1 6869333 +323 1 6890666 +350 1 6912000 +361 1 6933333 +326 1 6954666 +345 1 6976000 +345 1 6997333 +311 1 7018666 +349 1 7040000 +358 1 7061333 +352 1 7082666 +347 1 7104000 +364 1 7125333 +328 1 7146666 +318 1 7168000 +351 1 7189333 +340 1 7210666 +341 1 7232000 +355 1 7253333 +336 1 7274666 +352 1 7296000 +341 1 7317333 +334 1 7338666 +348 1 7360000 +342 1 7381333 +335 1 7402666 +342 1 7424000 +359 1 7445333 +349 1 7466666 +329 1 7488000 +356 1 7509333 +292 1 7530666 +316 1 7552000 +318 1 7573333 +320 1 7594666 +342 1 7616000 +285 1 7637333 +326 1 7658666 +352 1 7680000 +392 1 7701333 +364 1 7722666 +384 1 7744000 +334 1 7765333 +317 1 7786666 +326 1 7808000 +373 1 7829333 +354 1 7850666 +329 1 7872000 +347 1 7893333 +353 1 7914666 +338 1 7936000 +317 1 7957333 +354 1 7978666 +345 1 8000000 +350 1 8021333 +351 1 8042666 +332 1 8064000 +358 1 8085333 +315 1 8106666 +336 1 8128000 +358 1 8149333 +343 1 8170666 +319 1 8192000 +370 1 8213333 +344 1 8234666 +361 1 8256000 +343 1 8277333 +337 1 8298666 +354 1 8320000 +332 1 8341333 +348 1 8362666 +328 1 8384000 +345 1 8405333 +340 1 8426666 +346 1 8448000 +341 1 8469333 +344 1 8490666 +342 1 8512000 +341 1 8533333 +345 1 8554666 +337 1 8576000 +335 1 8597333 +335 1 8618666 +340 1 8640000 +345 1 8661333 +341 1 8682666 +342 1 8704000 +338 1 8725333 +343 1 8746666 +336 1 8768000 +338 1 8789333 +353 1 8810666 +339 1 8832000 +329 1 8853333 +349 1 8874666 +323 1 8896000 +351 1 8917333 +359 1 8938666 +357 1 8960000 +341 1 8981333 +333 1 9002666 +335 1 9024000 +328 1 9045333 +347 1 9066666 +343 1 9088000 +369 1 9109333 +331 1 9130666 +344 1 9152000 +330 1 9173333 +346 1 9194666 +337 1 9216000 +341 1 9237333 +338 1 9258666 +329 1 9280000 +360 1 9301333 +336 1 9322666 +341 1 9344000 +341 1 9365333 +345 1 9386666 +351 1 9408000 +349 1 9429333 +336 1 9450666 +326 1 9472000 +349 1 9493333 +343 1 9514666 +357 1 9536000 +342 1 9557333 +325 1 9578666 +346 1 9600000 +326 1 9621333 +402 1 9642666 +331 1 9664000 +339 1 9685333 +371 1 9706666 +314 1 9728000 +310 1 9749333 +364 1 9770666 +338 1 9792000 +339 1 9813333 +337 1 9834666 +355 1 9856000 +351 1 9877333 +332 1 9898666 +316 1 9920000 +474 1 9941333 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info new file mode 100644 index 0000000000..c420009c10 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info @@ -0,0 +1,460 @@ +41 1 0 +41 1 20000 +82 1 40000 +82 1 80000 +82 1 120000 +82 1 160000 +82 1 200000 +82 1 240000 +82 1 280000 +82 1 320000 +82 1 360000 +123 1 400000 +123 1 460000 +123 1 520000 +123 1 580000 +123 1 640000 +164 1 700000 +164 1 780000 +164 1 860000 +164 1 940000 +164 1 1020000 +41 1 1100000 +41 1 1120000 +41 1 1140000 +41 1 1160000 +41 1 1180000 +41 1 1200000 +41 1 1220000 +41 1 1240000 +41 1 1260000 +41 1 1280000 +41 1 1300000 +41 1 1320000 +41 1 1340000 +41 1 1360000 +41 1 1380000 +41 1 1400000 +41 1 1420000 +41 1 1440000 +41 1 1460000 +41 1 1480000 +41 1 1500000 +41 1 1520000 +41 1 1540000 +41 1 1560000 +41 1 1580000 +41 1 1600000 +41 1 1620000 +41 1 1640000 +41 1 1660000 +41 1 1680000 +41 1 1700000 +41 1 1720000 +41 1 1740000 +41 1 1760000 +41 1 1780000 +41 1 1800000 +41 1 1820000 +41 1 1840000 +41 1 1860000 +41 1 1880000 +41 1 1900000 +41 1 1920000 +41 1 1940000 +41 1 1960000 +41 1 1980000 +41 1 2000000 +41 1 2020000 +41 1 2040000 +41 1 2060000 +41 1 2080000 +41 1 2100000 +41 1 2120000 +41 1 2140000 +41 1 2160000 +41 1 2180000 +41 1 2200000 +41 1 2220000 +41 1 2240000 +41 1 2260000 +41 1 2280000 +41 1 2300000 +41 1 2320000 +41 1 2340000 +41 1 2360000 +41 1 2380000 +41 1 2400000 +41 1 2420000 +41 1 2440000 +41 1 2460000 +41 1 2480000 +41 1 2500000 +41 1 2520000 +41 1 2540000 +41 1 2560000 +41 1 2580000 +41 1 2600000 +41 1 2620000 +41 1 2640000 +41 1 2660000 +41 1 2680000 +41 1 2700000 +41 1 2720000 +41 1 2740000 +41 1 2760000 +41 1 2780000 +41 1 2800000 +41 1 2820000 +41 1 2840000 +41 1 2860000 +41 1 2880000 +41 1 2900000 +41 1 2920000 +41 1 2940000 +41 1 2960000 +41 1 2980000 +41 1 3000000 +41 1 3020000 +41 1 3040000 +41 1 3060000 +41 1 3080000 +41 1 3100000 +41 1 3120000 +41 1 3140000 +41 1 3160000 +41 1 3180000 +41 1 3200000 +41 1 3220000 +41 1 3240000 +41 1 3260000 +41 1 3280000 +41 1 3300000 +41 1 3320000 +41 1 3340000 +41 1 3360000 +41 1 3380000 +41 1 3400000 +41 1 3420000 +41 1 3440000 +41 1 3460000 +41 1 3480000 +41 1 3500000 +41 1 3520000 +41 1 3540000 +41 1 3560000 +41 1 3580000 +41 1 3600000 +41 1 3620000 +41 1 3640000 +41 1 3660000 +41 1 3680000 +41 1 3700000 +41 1 3720000 +41 1 3740000 +41 1 3760000 +41 1 3780000 +41 1 3800000 +41 1 3820000 +41 1 3840000 +41 1 3860000 +41 1 3880000 +41 1 3900000 +41 1 3920000 +41 1 3940000 +41 1 3960000 +41 1 3980000 +41 1 4000000 +41 1 4020000 +41 1 4040000 +41 1 4060000 +41 1 4080000 +41 1 4100000 +41 1 4120000 +41 1 4140000 +41 1 4160000 +41 1 4180000 +41 1 4200000 +41 1 4220000 +41 1 4240000 +41 1 4260000 +41 1 4280000 +41 1 4300000 +41 1 4320000 +41 1 4340000 +41 1 4360000 +41 1 4380000 +41 1 4400000 +41 1 4420000 +41 1 4440000 +41 1 4460000 +41 1 4480000 +41 1 4500000 +41 1 4520000 +41 1 4540000 +41 1 4560000 +41 1 4580000 +41 1 4600000 +41 1 4620000 +41 1 4640000 +41 1 4660000 +41 1 4680000 +41 1 4700000 +41 1 4720000 +41 1 4740000 +41 1 4760000 +41 1 4780000 +41 1 4800000 +41 1 4820000 +41 1 4840000 +41 1 4860000 +41 1 4880000 +41 1 4900000 +41 1 4920000 +41 1 4940000 +41 1 4960000 +41 1 4980000 +41 1 5000000 +41 1 5020000 +41 1 5040000 +41 1 5060000 +41 1 5080000 +41 1 5100000 +41 1 5120000 +41 1 5140000 +41 1 5160000 +41 1 5180000 +41 1 5200000 +41 1 5220000 +41 1 5240000 +41 1 5260000 +41 1 5280000 +41 1 5300000 +41 1 5320000 +41 1 5340000 +41 1 5360000 +41 1 5380000 +41 1 5400000 +41 1 5420000 +41 1 5440000 +41 1 5460000 +41 1 5480000 +41 1 5500000 +41 1 5520000 +41 1 5540000 +41 1 5560000 +41 1 5580000 +41 1 5600000 +41 1 5620000 +41 1 5640000 +41 1 5660000 +41 1 5680000 +41 1 5700000 +41 1 5720000 +41 1 5740000 +41 1 5760000 +41 1 5780000 +41 1 5800000 +41 1 5820000 +41 1 5840000 +41 1 5860000 +41 1 5880000 +41 1 5900000 +41 1 5920000 +41 1 5940000 +41 1 5960000 +41 1 5980000 +41 1 6000000 +41 1 6020000 +41 1 6040000 +41 1 6060000 +41 1 6080000 +41 1 6100000 +41 1 6120000 +41 1 6140000 +41 1 6160000 +41 1 6180000 +41 1 6200000 +41 1 6220000 +41 1 6240000 +41 1 6260000 +41 1 6280000 +41 1 6300000 +41 1 6320000 +41 1 6340000 +41 1 6360000 +41 1 6380000 +41 1 6400000 +41 1 6420000 +41 1 6440000 +41 1 6460000 +41 1 6480000 +41 1 6500000 +41 1 6520000 +41 1 6540000 +41 1 6560000 +41 1 6580000 +41 1 6600000 +41 1 6620000 +41 1 6640000 +41 1 6660000 +41 1 6680000 +41 1 6700000 +41 1 6720000 +41 1 6740000 +41 1 6760000 +41 1 6780000 +41 1 6800000 +41 1 6820000 +41 1 6840000 +41 1 6860000 +41 1 6880000 +41 1 6900000 +41 1 6920000 +41 1 6940000 +41 1 6960000 +41 1 6980000 +41 1 7000000 +41 1 7020000 +41 1 7040000 +41 1 7060000 +41 1 7080000 +41 1 7100000 +41 1 7120000 +41 1 7140000 +41 1 7160000 +41 1 7180000 +41 1 7200000 +41 1 7220000 +41 1 7240000 +41 1 7260000 +41 1 7280000 +41 1 7300000 +41 1 7320000 +41 1 7340000 +41 1 7360000 +41 1 7380000 +41 1 7400000 +41 1 7420000 +41 1 7440000 +41 1 7460000 +41 1 7480000 +41 1 7500000 +41 1 7520000 +41 1 7540000 +41 1 7560000 +41 1 7580000 +41 1 7600000 +41 1 7620000 +41 1 7640000 +41 1 7660000 +41 1 7680000 +41 1 7700000 +41 1 7720000 +41 1 7740000 +41 1 7760000 +41 1 7780000 +41 1 7800000 +41 1 7820000 +41 1 7840000 +41 1 7860000 +41 1 7880000 +41 1 7900000 +41 1 7920000 +41 1 7940000 +41 1 7960000 +41 1 7980000 +41 1 8000000 +41 1 8020000 +41 1 8040000 +41 1 8060000 +41 1 8080000 +41 1 8100000 +41 1 8120000 +41 1 8140000 +41 1 8160000 +41 1 8180000 +41 1 8200000 +41 1 8220000 +41 1 8240000 +41 1 8260000 +41 1 8280000 +41 1 8300000 +41 1 8320000 +41 1 8340000 +41 1 8360000 +41 1 8380000 +41 1 8400000 +41 1 8420000 +41 1 8440000 +41 1 8460000 +41 1 8480000 +41 1 8500000 +41 1 8520000 +41 1 8540000 +41 1 8560000 +41 1 8580000 +41 1 8600000 +41 1 8620000 +41 1 8640000 +41 1 8660000 +41 1 8680000 +41 1 8700000 +41 1 8720000 +41 1 8740000 +41 1 8760000 +41 1 8780000 +41 1 8800000 +41 1 8820000 +41 1 8840000 +41 1 8860000 +41 1 8880000 +41 1 8900000 +41 1 8920000 +41 1 8940000 +41 1 8960000 +41 1 8980000 +41 1 9000000 +41 1 9020000 +41 1 9040000 +41 1 9060000 +41 1 9080000 +41 1 9100000 +41 1 9120000 +41 1 9140000 +41 1 9160000 +41 1 9180000 +41 1 9200000 +41 1 9220000 +41 1 9240000 +41 1 9260000 +41 1 9280000 +41 1 9300000 +41 1 9320000 +41 1 9340000 +41 1 9360000 +41 1 9380000 +41 1 9400000 +41 1 9420000 +41 1 9440000 +41 1 9460000 +41 1 9480000 +41 1 9500000 +41 1 9520000 +41 1 9540000 +41 1 9560000 +41 1 9580000 +41 1 9600000 +41 1 9620000 +41 1 9640000 +41 1 9660000 +41 1 9680000 +41 1 9700000 +41 1 9720000 +41 1 9740000 +41 1 9760000 +41 1 9780000 +41 1 9800000 +41 1 9820000 +41 1 9840000 +41 1 9860000 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info new file mode 100644 index 0000000000..575c75f667 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info @@ -0,0 +1,385 @@ +576 1 0 +576 1 24000 +1152 1 48000 +1152 1 96000 +1152 1 144000 +1152 1 192000 +1728 1 240000 +1728 1 312000 +1728 1 384000 +1728 1 456000 +1728 1 528000 +2304 1 600000 +2304 1 696000 +2304 1 792000 +2304 1 888000 +2304 1 984000 +576 1 1080000 +576 1 1104000 +576 1 1128000 +576 1 1152000 +576 1 1176000 +576 1 1200000 +576 1 1224000 +576 1 1248000 +576 1 1272000 +576 1 1296000 +576 1 1320000 +576 1 1344000 +576 1 1368000 +576 1 1392000 +576 1 1416000 +576 1 1440000 +576 1 1464000 +576 1 1488000 +576 1 1512000 +576 1 1536000 +576 1 1560000 +576 1 1584000 +576 1 1608000 +576 1 1632000 +576 1 1656000 +576 1 1680000 +576 1 1704000 +576 1 1728000 +576 1 1752000 +576 1 1776000 +576 1 1800000 +576 1 1824000 +576 1 1848000 +576 1 1872000 +576 1 1896000 +576 1 1920000 +576 1 1944000 +576 1 1968000 +576 1 1992000 +576 1 2016000 +576 1 2040000 +576 1 2064000 +576 1 2088000 +576 1 2112000 +576 1 2136000 +576 1 2160000 +576 1 2184000 +576 1 2208000 +576 1 2232000 +576 1 2256000 +576 1 2280000 +576 1 2304000 +576 1 2328000 +576 1 2352000 +576 1 2376000 +576 1 2400000 +576 1 2424000 +576 1 2448000 +576 1 2472000 +576 1 2496000 +576 1 2520000 +576 1 2544000 +576 1 2568000 +576 1 2592000 +576 1 2616000 +576 1 2640000 +576 1 2664000 +576 1 2688000 +576 1 2712000 +576 1 2736000 +576 1 2760000 +576 1 2784000 +576 1 2808000 +576 1 2832000 +576 1 2856000 +576 1 2880000 +576 1 2904000 +576 1 2928000 +576 1 2952000 +576 1 2976000 +576 1 3000000 +576 1 3024000 +576 1 3048000 +576 1 3072000 +576 1 3096000 +576 1 3120000 +576 1 3144000 +576 1 3168000 +576 1 3192000 +576 1 3216000 +576 1 3240000 +576 1 3264000 +576 1 3288000 +576 1 3312000 +576 1 3336000 +576 1 3360000 +576 1 3384000 +576 1 3408000 +576 1 3432000 +576 1 3456000 +576 1 3480000 +576 1 3504000 +576 1 3528000 +576 1 3552000 +576 1 3576000 +576 1 3600000 +576 1 3624000 +576 1 3648000 +576 1 3672000 +576 1 3696000 +576 1 3720000 +576 1 3744000 +576 1 3768000 +576 1 3792000 +576 1 3816000 +576 1 3840000 +576 1 3864000 +576 1 3888000 +576 1 3912000 +576 1 3936000 +576 1 3960000 +576 1 3984000 +576 1 4008000 +576 1 4032000 +576 1 4056000 +576 1 4080000 +576 1 4104000 +576 1 4128000 +576 1 4152000 +576 1 4176000 +576 1 4200000 +576 1 4224000 +576 1 4248000 +576 1 4272000 +576 1 4296000 +576 1 4320000 +576 1 4344000 +576 1 4368000 +576 1 4392000 +576 1 4416000 +576 1 4440000 +576 1 4464000 +576 1 4488000 +576 1 4512000 +576 1 4536000 +576 1 4560000 +576 1 4584000 +576 1 4608000 +576 1 4632000 +576 1 4656000 +576 1 4680000 +576 1 4704000 +576 1 4728000 +576 1 4752000 +576 1 4776000 +576 1 4800000 +576 1 4824000 +576 1 4848000 +576 1 4872000 +576 1 4896000 +576 1 4920000 +576 1 4944000 +576 1 4968000 +576 1 4992000 +576 1 5016000 +576 1 5040000 +576 1 5064000 +576 1 5088000 +576 1 5112000 +576 1 5136000 +576 1 5160000 +576 1 5184000 +576 1 5208000 +576 1 5232000 +576 1 5256000 +576 1 5280000 +576 1 5304000 +576 1 5328000 +576 1 5352000 +576 1 5376000 +576 1 5400000 +576 1 5424000 +576 1 5448000 +576 1 5472000 +576 1 5496000 +576 1 5520000 +576 1 5544000 +576 1 5568000 +576 1 5592000 +576 1 5616000 +576 1 5640000 +576 1 5664000 +576 1 5688000 +576 1 5712000 +576 1 5736000 +576 1 5760000 +576 1 5784000 +576 1 5808000 +576 1 5832000 +576 1 5856000 +576 1 5880000 +576 1 5904000 +576 1 5928000 +576 1 5952000 +576 1 5976000 +576 1 6000000 +576 1 6024000 +576 1 6048000 +576 1 6072000 +576 1 6096000 +576 1 6120000 +576 1 6144000 +576 1 6168000 +576 1 6192000 +576 1 6216000 +576 1 6240000 +576 1 6264000 +576 1 6288000 +576 1 6312000 +576 1 6336000 +576 1 6360000 +576 1 6384000 +576 1 6408000 +576 1 6432000 +576 1 6456000 +576 1 6480000 +576 1 6504000 +576 1 6528000 +576 1 6552000 +576 1 6576000 +576 1 6600000 +576 1 6624000 +576 1 6648000 +576 1 6672000 +576 1 6696000 +576 1 6720000 +576 1 6744000 +576 1 6768000 +576 1 6792000 +576 1 6816000 +576 1 6840000 +576 1 6864000 +576 1 6888000 +576 1 6912000 +576 1 6936000 +576 1 6960000 +576 1 6984000 +576 1 7008000 +576 1 7032000 +576 1 7056000 +576 1 7080000 +576 1 7104000 +576 1 7128000 +576 1 7152000 +576 1 7176000 +576 1 7200000 +576 1 7224000 +576 1 7248000 +576 1 7272000 +576 1 7296000 +576 1 7320000 +576 1 7344000 +576 1 7368000 +576 1 7392000 +576 1 7416000 +576 1 7440000 +576 1 7464000 +576 1 7488000 +576 1 7512000 +576 1 7536000 +576 1 7560000 +576 1 7584000 +576 1 7608000 +576 1 7632000 +576 1 7656000 +576 1 7680000 +576 1 7704000 +576 1 7728000 +576 1 7752000 +576 1 7776000 +576 1 7800000 +576 1 7824000 +576 1 7848000 +576 1 7872000 +576 1 7896000 +576 1 7920000 +576 1 7944000 +576 1 7968000 +576 1 7992000 +576 1 8016000 +576 1 8040000 +576 1 8064000 +576 1 8088000 +576 1 8112000 +576 1 8136000 +576 1 8160000 +576 1 8184000 +576 1 8208000 +576 1 8232000 +576 1 8256000 +576 1 8280000 +576 1 8304000 +576 1 8328000 +576 1 8352000 +576 1 8376000 +576 1 8400000 +576 1 8424000 +576 1 8448000 +576 1 8472000 +576 1 8496000 +576 1 8520000 +576 1 8544000 +576 1 8568000 +576 1 8592000 +576 1 8616000 +576 1 8640000 +576 1 8664000 +576 1 8688000 +576 1 8712000 +576 1 8736000 +576 1 8760000 +576 1 8784000 +576 1 8808000 +576 1 8832000 +576 1 8856000 +576 1 8880000 +576 1 8904000 +576 1 8928000 +576 1 8952000 +576 1 8976000 +576 1 9000000 +576 1 9024000 +576 1 9048000 +576 1 9072000 +576 1 9096000 +576 1 9120000 +576 1 9144000 +576 1 9168000 +576 1 9192000 +576 1 9216000 +576 1 9240000 +576 1 9264000 +576 1 9288000 +576 1 9312000 +576 1 9336000 +576 1 9360000 +576 1 9384000 +576 1 9408000 +576 1 9432000 +576 1 9456000 +576 1 9480000 +576 1 9504000 +576 1 9528000 +576 1 9552000 +576 1 9576000 +576 1 9600000 +576 1 9624000 +576 1 9648000 +576 1 9672000 +576 1 9696000 +576 1 9720000 +576 1 9744000 +576 1 9768000 +576 1 9792000 +576 1 9816000 +576 1 9840000 +576 1 9864000 +576 1 9888000 +576 1 9912000 diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info new file mode 100644 index 0000000000..0176eaf456 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info @@ -0,0 +1,807 @@ +32 1 0 +32 1 20000 +64 1 40000 +64 1 80000 +64 1 120000 +96 1 160000 +96 1 220000 +96 1 280000 +96 1 340000 +128 1 400000 +128 1 480000 +128 1 560000 +128 1 640000 +128 1 720000 +32 1 800000 +32 1 820000 +32 1 840000 +32 1 860000 +32 1 880000 +32 1 900000 +32 1 920000 +32 1 940000 +32 1 960000 +32 1 980000 +32 1 1000000 +32 1 1020000 +32 1 1040000 +32 1 1060000 +32 1 1080000 +32 1 1100000 +32 1 1120000 +32 1 1140000 +32 1 1160000 +32 1 1180000 +32 1 1200000 +32 1 1220000 +32 1 1240000 +32 1 1260000 +32 1 1280000 +32 1 1300000 +32 1 1320000 +32 1 1340000 +32 1 1360000 +32 1 1380000 +32 1 1400000 +32 1 1420000 +32 1 1440000 +32 1 1460000 +32 1 1480000 +32 1 1500000 +32 1 1520000 +32 1 1540000 +32 1 1560000 +32 1 1580000 +32 1 1600000 +32 1 1620000 +32 1 1640000 +32 1 1660000 +32 1 1680000 +32 1 1700000 +32 1 1720000 +32 1 1740000 +32 1 1760000 +32 1 1780000 +32 1 1800000 +32 1 1820000 +32 1 1840000 +32 1 1860000 +32 1 1880000 +32 1 1900000 +32 1 1920000 +32 1 1940000 +32 1 1960000 +32 1 1980000 +32 1 2000000 +32 1 2020000 +32 1 2040000 +32 1 2060000 +32 1 2080000 +32 1 2100000 +32 1 2120000 +32 1 2140000 +32 1 2160000 +32 1 2180000 +32 1 2200000 +32 1 2220000 +32 1 2240000 +32 1 2260000 +32 1 2280000 +32 1 2300000 +32 1 2320000 +32 1 2340000 +32 1 2360000 +32 1 2380000 +32 1 2400000 +32 1 2420000 +32 1 2440000 +32 1 2460000 +32 1 2480000 +32 1 2500000 +32 1 2520000 +32 1 2540000 +32 1 2560000 +32 1 2580000 +32 1 2600000 +32 1 2620000 +32 1 2640000 +32 1 2660000 +32 1 2680000 +32 1 2700000 +32 1 2720000 +32 1 2740000 +32 1 2760000 +32 1 2780000 +32 1 2800000 +32 1 2820000 +32 1 2840000 +32 1 2860000 +32 1 2880000 +32 1 2900000 +32 1 2920000 +32 1 2940000 +32 1 2960000 +32 1 2980000 +32 1 3000000 +32 1 3020000 +32 1 3040000 +32 1 3060000 +32 1 3080000 +32 1 3100000 +32 1 3120000 +32 1 3140000 +32 1 3160000 +32 1 3180000 +32 1 3200000 +32 1 3220000 +32 1 3240000 +32 1 3260000 +32 1 3280000 +32 1 3300000 +32 1 3320000 +32 1 3340000 +32 1 3360000 +32 1 3380000 +32 1 3400000 +32 1 3420000 +32 1 3440000 +32 1 3460000 +32 1 3480000 +32 1 3500000 +32 1 3520000 +32 1 3540000 +32 1 3560000 +32 1 3580000 +32 1 3600000 +32 1 3620000 +32 1 3640000 +32 1 3660000 +32 1 3680000 +32 1 3700000 +32 1 3720000 +32 1 3740000 +32 1 3760000 +32 1 3780000 +32 1 3800000 +32 1 3820000 +32 1 3840000 +32 1 3860000 +32 1 3880000 +32 1 3900000 +32 1 3920000 +32 1 3940000 +32 1 3960000 +32 1 3980000 +32 1 4000000 +32 1 4020000 +32 1 4040000 +32 1 4060000 +32 1 4080000 +32 1 4100000 +32 1 4120000 +32 1 4140000 +32 1 4160000 +32 1 4180000 +32 1 4200000 +32 1 4220000 +32 1 4240000 +32 1 4260000 +32 1 4280000 +32 1 4300000 +32 1 4320000 +32 1 4340000 +32 1 4360000 +32 1 4380000 +32 1 4400000 +32 1 4420000 +32 1 4440000 +32 1 4460000 +32 1 4480000 +32 1 4500000 +32 1 4520000 +32 1 4540000 +32 1 4560000 +32 1 4580000 +32 1 4600000 +32 1 4620000 +32 1 4640000 +32 1 4660000 +32 1 4680000 +32 1 4700000 +32 1 4720000 +32 1 4740000 +32 1 4760000 +32 1 4780000 +32 1 4800000 +32 1 4820000 +32 1 4840000 +32 1 4860000 +32 1 4880000 +32 1 4900000 +32 1 4920000 +32 1 4940000 +32 1 4960000 +32 1 4980000 +32 1 5000000 +32 1 5020000 +32 1 5040000 +32 1 5060000 +32 1 5080000 +32 1 5100000 +32 1 5120000 +32 1 5140000 +32 1 5160000 +32 1 5180000 +32 1 5200000 +32 1 5220000 +32 1 5240000 +32 1 5260000 +32 1 5280000 +32 1 5300000 +32 1 5320000 +32 1 5340000 +32 1 5360000 +32 1 5380000 +32 1 5400000 +32 1 5420000 +32 1 5440000 +32 1 5460000 +32 1 5480000 +32 1 5500000 +32 1 5520000 +32 1 5540000 +32 1 5560000 +32 1 5580000 +32 1 5600000 +32 1 5620000 +32 1 5640000 +32 1 5660000 +32 1 5680000 +32 1 5700000 +32 1 5720000 +32 1 5740000 +32 1 5760000 +32 1 5780000 +32 1 5800000 +32 1 5820000 +32 1 5840000 +32 1 5860000 +32 1 5880000 +32 1 5900000 +32 1 5920000 +32 1 5940000 +32 1 5960000 +32 1 5980000 +32 1 6000000 +32 1 6020000 +32 1 6040000 +32 1 6060000 +32 1 6080000 +32 1 6100000 +32 1 6120000 +32 1 6140000 +32 1 6160000 +32 1 6180000 +32 1 6200000 +32 1 6220000 +32 1 6240000 +32 1 6260000 +32 1 6280000 +32 1 6300000 +32 1 6320000 +32 1 6340000 +32 1 6360000 +32 1 6380000 +32 1 6400000 +32 1 6420000 +32 1 6440000 +32 1 6460000 +32 1 6480000 +32 1 6500000 +32 1 6520000 +32 1 6540000 +32 1 6560000 +32 1 6580000 +32 1 6600000 +32 1 6620000 +32 1 6640000 +32 1 6660000 +32 1 6680000 +32 1 6700000 +32 1 6720000 +32 1 6740000 +32 1 6760000 +32 1 6780000 +32 1 6800000 +32 1 6820000 +32 1 6840000 +32 1 6860000 +32 1 6880000 +32 1 6900000 +32 1 6920000 +32 1 6940000 +32 1 6960000 +32 1 6980000 +32 1 7000000 +32 1 7020000 +32 1 7040000 +32 1 7060000 +32 1 7080000 +32 1 7100000 +32 1 7120000 +32 1 7140000 +32 1 7160000 +32 1 7180000 +32 1 7200000 +32 1 7220000 +32 1 7240000 +32 1 7260000 +32 1 7280000 +32 1 7300000 +32 1 7320000 +32 1 7340000 +32 1 7360000 +32 1 7380000 +32 1 7400000 +32 1 7420000 +32 1 7440000 +32 1 7460000 +32 1 7480000 +32 1 7500000 +32 1 7520000 +32 1 7540000 +32 1 7560000 +32 1 7580000 +32 1 7600000 +32 1 7620000 +32 1 7640000 +32 1 7660000 +32 1 7680000 +32 1 7700000 +32 1 7720000 +32 1 7740000 +32 1 7760000 +32 1 7780000 +32 1 7800000 +32 1 7820000 +32 1 7840000 +32 1 7860000 +32 1 7880000 +32 1 7900000 +32 1 7920000 +32 1 7940000 +32 1 7960000 +32 1 7980000 +32 1 8000000 +32 1 8020000 +32 1 8040000 +32 1 8060000 +32 1 8080000 +32 1 8100000 +32 1 8120000 +32 1 8140000 +32 1 8160000 +32 1 8180000 +32 1 8200000 +32 1 8220000 +32 1 8240000 +32 1 8260000 +32 1 8280000 +32 1 8300000 +32 1 8320000 +32 1 8340000 +32 1 8360000 +32 1 8380000 +32 1 8400000 +32 1 8420000 +32 1 8440000 +32 1 8460000 +32 1 8480000 +32 1 8500000 +32 1 8520000 +32 1 8540000 +32 1 8560000 +32 1 8580000 +32 1 8600000 +32 1 8620000 +32 1 8640000 +32 1 8660000 +32 1 8680000 +32 1 8700000 +32 1 8720000 +32 1 8740000 +32 1 8760000 +32 1 8780000 +32 1 8800000 +32 1 8820000 +32 1 8840000 +32 1 8860000 +32 1 8880000 +32 1 8900000 +32 1 8920000 +32 1 8940000 +32 1 8960000 +32 1 8980000 +32 1 9000000 +32 1 9020000 +32 1 9040000 +32 1 9060000 +32 1 9080000 +32 1 9100000 +32 1 9120000 +32 1 9140000 +32 1 9160000 +32 1 9180000 +32 1 9200000 +32 1 9220000 +32 1 9240000 +32 1 9260000 +32 1 9280000 +32 1 9300000 +32 1 9320000 +32 1 9340000 +32 1 9360000 +32 1 9380000 +32 1 9400000 +32 1 9420000 +32 1 9440000 +32 1 9460000 +32 1 9480000 +32 1 9500000 +32 1 9520000 +32 1 9540000 +32 1 9560000 +32 1 9580000 +32 1 9600000 +32 1 9620000 +32 1 9640000 +32 1 9660000 +32 1 9680000 +32 1 9700000 +32 1 9720000 +32 1 9740000 +32 1 9760000 +32 1 9780000 +32 1 9800000 +32 1 9820000 +32 1 9840000 +32 1 9860000 +32 1 9880000 +32 1 9900000 +32 1 9920000 +32 1 9940000 +32 1 9960000 +32 1 9980000 +32 1 10000000 +32 1 10020000 +32 1 10040000 +32 1 10060000 +32 1 10080000 +32 1 10100000 +32 1 10120000 +32 1 10140000 +32 1 10160000 +32 1 10180000 +32 1 10200000 +32 1 10220000 +32 1 10240000 +32 1 10260000 +32 1 10280000 +32 1 10300000 +32 1 10320000 +32 1 10340000 +32 1 10360000 +32 1 10380000 +32 1 10400000 +32 1 10420000 +32 1 10440000 +32 1 10460000 +32 1 10480000 +32 1 10500000 +32 1 10520000 +32 1 10540000 +32 1 10560000 +32 1 10580000 +32 1 10600000 +32 1 10620000 +32 1 10640000 +32 1 10660000 +32 1 10680000 +32 1 10700000 +32 1 10720000 +32 1 10740000 +32 1 10760000 +32 1 10780000 +32 1 10800000 +32 1 10820000 +32 1 10840000 +32 1 10860000 +32 1 10880000 +32 1 10900000 +32 1 10920000 +32 1 10940000 +32 1 10960000 +32 1 10980000 +32 1 11000000 +32 1 11020000 +32 1 11040000 +32 1 11060000 +32 1 11080000 +32 1 11100000 +32 1 11120000 +32 1 11140000 +32 1 11160000 +32 1 11180000 +32 1 11200000 +32 1 11220000 +32 1 11240000 +32 1 11260000 +32 1 11280000 +32 1 11300000 +32 1 11320000 +32 1 11340000 +32 1 11360000 +32 1 11380000 +32 1 11400000 +32 1 11420000 +32 1 11440000 +32 1 11460000 +32 1 11480000 +32 1 11500000 +32 1 11520000 +32 1 11540000 +32 1 11560000 +32 1 11580000 +32 1 11600000 +32 1 11620000 +32 1 11640000 +32 1 11660000 +32 1 11680000 +32 1 11700000 +32 1 11720000 +32 1 11740000 +32 1 11760000 +32 1 11780000 +32 1 11800000 +32 1 11820000 +32 1 11840000 +32 1 11860000 +32 1 11880000 +32 1 11900000 +32 1 11920000 +32 1 11940000 +32 1 11960000 +32 1 11980000 +32 1 12000000 +32 1 12020000 +32 1 12040000 +32 1 12060000 +32 1 12080000 +32 1 12100000 +32 1 12120000 +32 1 12140000 +32 1 12160000 +32 1 12180000 +32 1 12200000 +32 1 12220000 +32 1 12240000 +32 1 12260000 +32 1 12280000 +32 1 12300000 +32 1 12320000 +32 1 12340000 +32 1 12360000 +32 1 12380000 +32 1 12400000 +32 1 12420000 +32 1 12440000 +32 1 12460000 +32 1 12480000 +32 1 12500000 +32 1 12520000 +32 1 12540000 +32 1 12560000 +32 1 12580000 +32 1 12600000 +32 1 12620000 +32 1 12640000 +32 1 12660000 +32 1 12680000 +32 1 12700000 +32 1 12720000 +32 1 12740000 +32 1 12760000 +32 1 12780000 +32 1 12800000 +32 1 12820000 +32 1 12840000 +32 1 12860000 +32 1 12880000 +32 1 12900000 +32 1 12920000 +32 1 12940000 +32 1 12960000 +32 1 12980000 +32 1 13000000 +32 1 13020000 +32 1 13040000 +32 1 13060000 +32 1 13080000 +32 1 13100000 +32 1 13120000 +32 1 13140000 +32 1 13160000 +32 1 13180000 +32 1 13200000 +32 1 13220000 +32 1 13240000 +32 1 13260000 +32 1 13280000 +32 1 13300000 +32 1 13320000 +32 1 13340000 +32 1 13360000 +32 1 13380000 +32 1 13400000 +32 1 13420000 +32 1 13440000 +32 1 13460000 +32 1 13480000 +32 1 13500000 +32 1 13520000 +32 1 13540000 +32 1 13560000 +32 1 13580000 +32 1 13600000 +32 1 13620000 +32 1 13640000 +32 1 13660000 +32 1 13680000 +32 1 13700000 +32 1 13720000 +32 1 13740000 +32 1 13760000 +32 1 13780000 +32 1 13800000 +32 1 13820000 +32 1 13840000 +32 1 13860000 +32 1 13880000 +32 1 13900000 +32 1 13920000 +32 1 13940000 +32 1 13960000 +32 1 13980000 +32 1 14000000 +32 1 14020000 +32 1 14040000 +32 1 14060000 +32 1 14080000 +32 1 14100000 +32 1 14120000 +32 1 14140000 +32 1 14160000 +32 1 14180000 +32 1 14200000 +32 1 14220000 +32 1 14240000 +32 1 14260000 +32 1 14280000 +32 1 14300000 +32 1 14320000 +32 1 14340000 +32 1 14360000 +32 1 14380000 +32 1 14400000 +32 1 14420000 +32 1 14440000 +32 1 14460000 +32 1 14480000 +32 1 14500000 +32 1 14520000 +32 1 14540000 +32 1 14560000 +32 1 14580000 +32 1 14600000 +32 1 14620000 +32 1 14640000 +32 1 14660000 +32 1 14680000 +32 1 14700000 +32 1 14720000 +32 1 14740000 +32 1 14760000 +32 1 14780000 +32 1 14800000 +32 1 14820000 +32 1 14840000 +32 1 14860000 +32 1 14880000 +32 1 14900000 +32 1 14920000 +32 1 14940000 +32 1 14960000 +32 1 14980000 +32 1 15000000 +32 1 15020000 +32 1 15040000 +32 1 15060000 +32 1 15080000 +32 1 15100000 +32 1 15120000 +32 1 15140000 +32 1 15160000 +32 1 15180000 +32 1 15200000 +32 1 15220000 +32 1 15240000 +32 1 15260000 +32 1 15280000 +32 1 15300000 +32 1 15320000 +32 1 15340000 +32 1 15360000 +32 1 15380000 +32 1 15400000 +32 1 15420000 +32 1 15440000 +32 1 15460000 +32 1 15480000 +32 1 15500000 +32 1 15520000 +32 1 15540000 +32 1 15560000 +32 1 15580000 +32 1 15600000 +32 1 15620000 +32 1 15640000 +32 1 15660000 +32 1 15680000 +32 1 15700000 +32 1 15720000 +32 1 15740000 +32 1 15760000 +32 1 15780000 +32 1 15800000 +32 1 15820000 +32 1 15840000 +32 1 15860000 +32 1 15880000 +32 1 15900000 +32 1 15920000 +32 1 15940000 +32 1 15960000 +32 1 15980000 +32 1 16000000 +32 1 16020000 +32 1 16040000 +32 1 16060000 +32 1 16080000 +32 1 16100000 +32 1 16120000 +32 1 16140000 +32 1 16160000 +32 1 16180000 +32 1 16200000 +32 1 16220000 +32 1 16240000 +32 1 16260000 +32 1 16280000 +32 1 16300000 +32 1 16320000 +32 1 16340000 +32 1 16360000 +32 1 16380000 +32 1 16400000 +32 1 16420000 +32 1 16440000 +32 1 16460000 +32 1 16480000 +32 1 16500000 +32 1 16520000 +32 1 16540000 +32 1 16560000 +32 1 16580000 +32 1 16600000 +32 1 16620000 +32 1 16640000 -- GitLab From d3067439fa2c33d917f58fce7e036ea289ae9378 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Mon, 17 Dec 2018 17:05:36 +0530 Subject: [PATCH 0674/1530] codec2: MTS add support for multiple-input in VideoDectest Test: MtsHidlC2V1_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/ Bug: 80272610 Change-Id: I5061a6994701246f279641af8d2c4ad14c3c69b3 --- .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp index 9a42d72955..8cbb7a7a4a 100644 --- a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp @@ -421,17 +421,21 @@ TEST_F(Codec2VideoDecHidlTest, validateCompName) { ASSERT_EQ(mDisableTest, false); } +class Codec2VideoDecDecodeTest : public Codec2VideoDecHidlTest, + public ::testing::WithParamInterface { +}; + // Bitstream Test -TEST_F(Codec2VideoDecHidlTest, DecodeTest) { +TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { description("Decodes input file"); if (mDisableTest) return; + uint32_t streamIndex = GetParam(); char mURL[512], info[512]; std::ifstream eleStream, eleInfo; - strcpy(mURL, gEnv->getRes().c_str()); strcpy(info, gEnv->getRes().c_str()); - GetURLForComponent(mCompName, mURL, info); + GetURLForComponent(mCompName, mURL, info, streamIndex); eleInfo.open(info); ASSERT_EQ(eleInfo.is_open(), true) << mURL << " - file not found"; @@ -452,6 +456,9 @@ TEST_F(Codec2VideoDecHidlTest, DecodeTest) { eleInfo.close(); ASSERT_EQ(mComponent->start(), C2_OK); + // Reset total no of frames received + mFramesReceived = 0; + mTimestampUs = 0; ALOGV("mURL : %s", mURL); eleStream.open(mURL, std::ifstream::binary); ASSERT_EQ(eleStream.is_open(), true); @@ -476,8 +483,11 @@ TEST_F(Codec2VideoDecHidlTest, DecodeTest) { } if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true); + ASSERT_EQ(mComponent->stop(), C2_OK); } +INSTANTIATE_TEST_CASE_P(StreamIndexes, Codec2VideoDecDecodeTest, + ::testing::Values(0, 1)); // Adaptive Test TEST_F(Codec2VideoDecHidlTest, AdaptiveDecodeTest) { -- GitLab From f03d9359e9d37ed3cc58afe763848e65ffb560e1 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 4 Jan 2019 11:24:47 -0800 Subject: [PATCH 0675/1530] cas: check descrambler status in ATSParser bug: 120303321 Change-Id: Iffd24d75173ba2a4453945860f0b419035440581 --- media/libstagefright/mpeg2ts/ATSParser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index e9baa1a35c..345f85dd5c 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -1610,9 +1610,9 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) { detailedError = _detailedError; }); - if (!returnVoid.isOk()) { - ALOGE("[stream %d] descramble failed, trans=%s", - mElementaryPID, returnVoid.description().c_str()); + if (!returnVoid.isOk() || status != Status::OK) { + ALOGE("[stream %d] descramble failed, trans=%s, status=%d", + mElementaryPID, returnVoid.description().c_str(), status); return UNKNOWN_ERROR; } -- GitLab From 1225b9329367ab30587983c78f343734c33af4d4 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Fri, 4 Jan 2019 12:44:38 -0800 Subject: [PATCH 0676/1530] NuPlayerRenderer: handle fractional audio data before EOS When audio reaches EOS and MediaClock has not started, use video timestamp to start MediaClock. Test: MediaPlayer sends MEDIA_PLAYBACK_COMPLETE Bug: 122052445 Change-Id: Ia37643b8d5260cda0e7cde22dea883804511cff6 --- .../nuplayer2/NuPlayer2Renderer.cpp | 16 +++++++++++----- .../nuplayer/NuPlayerRenderer.cpp | 16 +++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp index e3c9b4b9e0..51e472d380 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp @@ -1269,10 +1269,10 @@ void NuPlayer2::Renderer::postDrainVideoQueue() { mAnchorTimeMediaUs = mediaTimeUs; } } - mNextVideoTimeMediaUs = mediaTimeUs + 100000; + mNextVideoTimeMediaUs = mediaTimeUs; if (!mHasAudio) { // smooth out videos >= 10fps - mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { @@ -1406,9 +1406,15 @@ void NuPlayer2::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t mHasAudio = false; if (mNextVideoTimeMediaUs >= 0) { int64_t mediaUs = 0; - mMediaClock->getMediaTime(ALooper::GetNowUs(), &mediaUs); - if (mNextVideoTimeMediaUs > mediaUs) { - mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + int64_t nowUs = ALooper::GetNowUs(); + status_t result = mMediaClock->getMediaTime(nowUs, &mediaUs); + if (result == OK) { + if (mNextVideoTimeMediaUs > mediaUs) { + mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + } + } else { + mMediaClock->updateAnchor( + mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000); } } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index c8f6738a83..c990b2a328 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -1299,10 +1299,10 @@ void NuPlayer::Renderer::postDrainVideoQueue() { mAnchorTimeMediaUs = mediaTimeUs; } } - mNextVideoTimeMediaUs = mediaTimeUs + 100000; + mNextVideoTimeMediaUs = mediaTimeUs; if (!mHasAudio) { // smooth out videos >= 10fps - mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { @@ -1436,9 +1436,15 @@ void NuPlayer::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t d mHasAudio = false; if (mNextVideoTimeMediaUs >= 0) { int64_t mediaUs = 0; - mMediaClock->getMediaTime(ALooper::GetNowUs(), &mediaUs); - if (mNextVideoTimeMediaUs > mediaUs) { - mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + int64_t nowUs = ALooper::GetNowUs(); + status_t result = mMediaClock->getMediaTime(nowUs, &mediaUs); + if (result == OK) { + if (mNextVideoTimeMediaUs > mediaUs) { + mMediaClock->updateMaxTimeMedia(mNextVideoTimeMediaUs); + } + } else { + mMediaClock->updateAnchor( + mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000); } } } -- GitLab From 76c72c34d4bb80573cf42fa3ed43227e57dd2030 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 4 Jan 2019 14:01:57 -0800 Subject: [PATCH 0677/1530] SoftOpus: fix out of bounds access Bug: 117661116 Test: CTS, manual Change-Id: I40991947c3bd37aba6bfa623416fb8fdbdfe7842 --- media/libstagefright/codecs/opus/dec/SoftOpus.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp index 942f8506a0..c6dc32614a 100644 --- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -430,6 +430,15 @@ void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) { return; } + if (size < sizeof(int64_t)) { + // The 2nd and 3rd input buffer are expected to contain + // an int64_t (see below), so make sure we get at least + // that much. The first input buffer must contain 19 bytes, + // but that is checked already. + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + if (mInputBufferCount == 0) { delete mHeader; mHeader = new OpusHeader(); -- GitLab From 896b4d6dd04b8624fdb46963f37e494461a4ec45 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Fri, 4 Jan 2019 15:26:48 -0800 Subject: [PATCH 0678/1530] NuPlayer2Renderer: support 8/16 bit and float PCM types Test: cts Bug: 120749869 Change-Id: I95f9865e7cea00e57f3faebeefd5cd095736201d --- .../nuplayer2/NuPlayer2Renderer.cpp | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp index e3c9b4b9e0..08e81e1959 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -86,6 +88,20 @@ const NuPlayer2::Renderer::PcmInfo NuPlayer2::Renderer::AUDIO_PCMINFO_INITIALIZE // static const int64_t NuPlayer2::Renderer::kMinPositionUpdateDelayUs = 100000LL; +static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) { + switch (pcmEncoding) { + case kAudioEncodingPcmFloat: + return AUDIO_FORMAT_PCM_FLOAT; + case kAudioEncodingPcm16bit: + return AUDIO_FORMAT_PCM_16_BIT; + case kAudioEncodingPcm8bit: + return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this? + default: + ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding); + return AUDIO_FORMAT_INVALID; + } +} + NuPlayer2::Renderer::Renderer( const sp &sink, const sp &mediaClock, @@ -1864,8 +1880,13 @@ status_t NuPlayer2::Renderer::onOpenAudioSink( int32_t sampleRate; CHECK(format->findInt32("sample-rate", &sampleRate)); + // read pcm encoding from MediaCodec output format, if available + int32_t pcmEncoding; + audio_format_t audioFormat = + format->findInt32(KEY_PCM_ENCODING, &pcmEncoding) ? + audioFormatFromEncoding(pcmEncoding) : AUDIO_FORMAT_PCM_16_BIT; + if (offloadingAudio()) { - audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; AString mime; CHECK(format->findString("mime", &mime)); status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str()); @@ -1966,7 +1987,7 @@ status_t NuPlayer2::Renderer::onOpenAudioSink( const PcmInfo info = { (audio_channel_mask_t)channelMask, (audio_output_flags_t)pcmFlags, - AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat + audioFormat, numChannels, sampleRate }; @@ -1998,7 +2019,7 @@ status_t NuPlayer2::Renderer::onOpenAudioSink( sampleRate, numChannels, (audio_channel_mask_t)channelMask, - AUDIO_FORMAT_PCM_16_BIT, + audioFormat, mUseAudioCallback ? &NuPlayer2::Renderer::AudioSinkCallback : NULL, mUseAudioCallback ? this : NULL, (audio_output_flags_t)pcmFlags, @@ -2054,4 +2075,3 @@ void NuPlayer2::Renderer::onChangeAudioFormat( } } // namespace android - -- GitLab From 20729a775fb11b0d20abfe13b156b1e11ff2bb46 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 4 Jan 2019 16:01:55 -0800 Subject: [PATCH 0679/1530] Use setAddress to set microphone address when it is empty. It is impossible to set address for input device via address(). Use setAddress instead. Bug: 122330994 Test: run cts Change-Id: Ia588ad0b5c0d71dc532422bc2434105716b924c9 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7b71752230..de5daa80ae 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4081,9 +4081,9 @@ status_t AudioPolicyManager::initialize() { for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { if (mAvailableInputDevices[i]->address().isEmpty()) { if (mAvailableInputDevices[i]->type() == AUDIO_DEVICE_IN_BUILTIN_MIC) { - mAvailableInputDevices[i]->address() = String8(AUDIO_BOTTOM_MICROPHONE_ADDRESS); + mAvailableInputDevices[i]->setAddress(String8(AUDIO_BOTTOM_MICROPHONE_ADDRESS)); } else if (mAvailableInputDevices[i]->type() == AUDIO_DEVICE_IN_BACK_MIC) { - mAvailableInputDevices[i]->address() = String8(AUDIO_BACK_MICROPHONE_ADDRESS); + mAvailableInputDevices[i]->setAddress(String8(AUDIO_BACK_MICROPHONE_ADDRESS)); } } } -- GitLab From 03de12c486f8db9db83f4ec2555b1d28ec01acf6 Mon Sep 17 00:00:00 2001 From: Insun Kang Date: Mon, 7 Jan 2019 12:07:20 +0900 Subject: [PATCH 0680/1530] Resolve hidden API usage of Handler constructor Previous code uses hidden constructor of Handler to set its messages to be asynchronous. This replaces it with Message#setAsynchronous(), which is public. Bug: 122047488 Test: build Change-Id: Icaeffcbcba5bc82a9162cdd17df7190d72a43bdd --- .../apex/java/android/media/session/MediaController.java | 4 ++-- .../apex/java/android/media/session/MediaSession.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java index eaebcfb9f7..65682a8d8a 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java @@ -1142,8 +1142,7 @@ public final class MediaController { private boolean mRegistered = false; public MessageHandler(Looper looper, MediaController.Callback cb) { - //TODO:(b/119539849) Uncomment below line and resolve the error. - // super(looper, null, true); + super(looper); mCallback = cb; } @@ -1182,6 +1181,7 @@ public final class MediaController { public void post(int what, Object obj, Bundle data) { Message msg = obtainMessage(what, obj); + msg.setAsynchronous(true); msg.setData(data); msg.sendToTarget(); } diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index 943843d2c4..73e16a65b6 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -1458,8 +1458,7 @@ public final class MediaSession { private RemoteUserInfo mCurrentControllerInfo; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { - //TODO:(b/119539849) Uncomment below line and resolve the error. - //super(looper, null, true); + super(looper); mCallback = callback; mCallback.mHandler = this; } @@ -1467,6 +1466,7 @@ public final class MediaSession { public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) { Pair objWithCaller = Pair.create(caller, obj); Message msg = obtainMessage(what, objWithCaller); + msg.setAsynchronous(true); msg.setData(data); if (delayMs > 0) { sendMessageDelayed(msg, delayMs); -- GitLab From 5986d79bb646ffbf38a1daaa9bb26e002d5bcc8d Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Mon, 7 Jan 2019 11:55:03 -0800 Subject: [PATCH 0681/1530] Enable Scudo for mediaextractor Scudo is a hardened usermode allocator that aims at mitigating heap-based vulnerabilities (heap overflow, double free, etc). See go/scudo-allocator for more information. This change enables the allocator for the mediaextractor service, effectively linking in the Scudo dynamic library to the binary. Allocation functions will be serviced by the allocator. The 'deallocation-type-mismatch' check had to be disabled as some third party libraries were triggering it. In the event of a heap bug detected, the allocator will abort the process with a 'Scudo ERROR' message in the log followed by the cause (corrupted header, invalid state & so on) and the stack trace. Test: CtsMediaTestCases on a marlin Bug: 63907455 Change-Id: I9e0ea03c0d39e723a880e7fcd761fe0718bbbacd --- services/mediaextractor/Android.mk | 1 + services/mediaextractor/main_extractorservice.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index e31eadc8d8..0238d8fe2c 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -46,6 +46,7 @@ LOCAL_MODULE:= mediaextractor LOCAL_INIT_RC := mediaextractor.rc LOCAL_C_INCLUDES := frameworks/av/media/libmedia LOCAL_CFLAGS := -Wall -Werror +LOCAL_SANITIZE := scudo include $(BUILD_EXECUTABLE) # service seccomp filter diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp index 8d3359a04d..5f427118cd 100644 --- a/services/mediaextractor/main_extractorservice.cpp +++ b/services/mediaextractor/main_extractorservice.cpp @@ -42,6 +42,12 @@ static const char kSystemSeccompPolicyPath[] = static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediaextractor.policy"; +// Disable Scudo's mismatch allocation check, as it is being triggered +// by some third party code. +extern "C" const char *__scudo_default_options() { + return "DeallocationTypeMismatch=false"; +} + int main(int argc __unused, char** argv) { limitProcessMemory( -- GitLab From 55baca288135ca376df973219f71525a3b72fc4c Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Mon, 7 Jan 2019 13:54:13 -0800 Subject: [PATCH 0682/1530] AImageReader: avoid edit during traversing Test: HWAsan build + CTS NativeImageReaderTest Bug: 119839845 Change-Id: Ic22dba944e233fc55b5fe35004148867348f951a --- media/ndk/NdkImageReader.cpp | 10 ++++++++-- media/ndk/NdkImageReaderPriv.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index b86ab424e0..1a0c3b1ff2 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -357,8 +357,10 @@ AImageReader::~AImageReader() { it != mAcquiredImages.end(); it++) { AImage* image = *it; Mutex::Autolock _l(image->mLock); - releaseImageLocked(image, /*releaseFenceFd*/-1); + // Do not alter mAcquiredImages while we are iterating on it + releaseImageLocked(image, /*releaseFenceFd*/-1, /*clearCache*/false); } + mAcquiredImages.clear(); // Delete Buffer Items for (auto it = mBuffers.begin(); @@ -497,7 +499,7 @@ AImageReader::returnBufferItemLocked(BufferItem* buffer) { } void -AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd) { +AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd, bool clearCache) { BufferItem* buffer = image->mBuffer; if (buffer == nullptr) { // This should not happen, but is not fatal @@ -521,6 +523,10 @@ AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd) { image->mLockedBuffer = nullptr; image->mIsClosed = true; + if (!clearCache) { + return; + } + bool found = false; // cleanup acquired image list for (auto it = mAcquiredImages.begin(); diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h index 78152d2946..e328cb1429 100644 --- a/media/ndk/NdkImageReaderPriv.h +++ b/media/ndk/NdkImageReaderPriv.h @@ -88,7 +88,7 @@ struct AImageReader : public RefBase { media_status_t acquireImageLocked(/*out*/AImage** image, /*out*/int* fenceFd); // Called by AImage/~AImageReader to close image. Caller is responsible to grab AImage::mLock - void releaseImageLocked(AImage* image, int releaseFenceFd); + void releaseImageLocked(AImage* image, int releaseFenceFd, bool clearCache = true); static int getBufferWidth(BufferItem* buffer); static int getBufferHeight(BufferItem* buffer); -- GitLab From 2c66f89ff17cc327ace5a6104676a6197a15585e Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 7 Jan 2019 14:39:17 -0800 Subject: [PATCH 0683/1530] Completely remove Metadata from MediaPlayer2. deprecated Metadata class has dependency on libbinder, so this change removes Metadata usage from MediaPlayer2 completely. Test: make libmedia2_jni Bug: 112766913 Change-Id: I32b0ae618c524a32afee07a21e950dbfad3e5750 --- .../mediaplayer2/MediaPlayer2Interface.h | 13 --------- .../include/mediaplayer2/mediaplayer2.h | 1 - media/libmediaplayer2/mediaplayer2.cpp | 1 - .../nuplayer2/NuPlayer2Driver.cpp | 27 ------------------- .../nuplayer2/NuPlayer2Driver.h | 3 --- 5 files changed, 45 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index 5e9858960d..0c8d016902 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -224,18 +223,6 @@ public: // @return OK if the call was successful. virtual status_t invoke(const PlayerMessage &request, PlayerMessage *reply) = 0; - // The Client in the MetadataPlayerService calls this method on - // the native player to retrieve all or a subset of metadata. - // - // @param ids SortedList of metadata ID to be fetch. If empty, all - // the known metadata should be returned. - // @param[inout] records Parcel where the player appends its metadata. - // @return OK if the call was successful. - virtual status_t getMetadata(const media::Metadata::Filter& /* ids */, - Parcel* /* records */) { - return INVALID_OPERATION; - }; - void setListener(const sp &listener) { Mutex::Autolock autoLock(mListenerLock); mListener = listener; diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index a945ffd600..b8d034b197 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index f43205920f..0923d88a4d 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 2dab2dd0f3..3c11b17fc6 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -662,33 +662,6 @@ status_t NuPlayer2Driver::getParameter(int key, Parcel *reply) { return INVALID_OPERATION; } -status_t NuPlayer2Driver::getMetadata( - const media::Metadata::Filter& /* ids */, Parcel *records) { - Mutex::Autolock autoLock(mLock); - - using media::Metadata; - - Metadata meta(records); - - meta.appendBool( - Metadata::kPauseAvailable, - mPlayerFlags & NuPlayer2::Source::FLAG_CAN_PAUSE); - - meta.appendBool( - Metadata::kSeekBackwardAvailable, - mPlayerFlags & NuPlayer2::Source::FLAG_CAN_SEEK_BACKWARD); - - meta.appendBool( - Metadata::kSeekForwardAvailable, - mPlayerFlags & NuPlayer2::Source::FLAG_CAN_SEEK_FORWARD); - - meta.appendBool( - Metadata::kSeekAvailable, - mPlayerFlags & NuPlayer2::Source::FLAG_CAN_SEEK); - - return OK; -} - void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) { ALOGD("notifyResetComplete(%p)", this); Mutex::Autolock autoLock(mLock); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index bb30c76839..fe17d03c25 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -61,9 +61,6 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { virtual status_t setParameter(int key, const Parcel &request) override; virtual status_t getParameter(int key, Parcel *reply) override; - virtual status_t getMetadata( - const media::Metadata::Filter& ids, Parcel *records) override; - virtual status_t dump(int fd, const Vector &args) const override; virtual void onMessageReceived(const sp &msg) override; -- GitLab From 919f85f20efe47e23a37b053a05ff071a770d1d0 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 7 Jan 2019 14:58:00 -0800 Subject: [PATCH 0684/1530] MediaPlayer2: replace IPCThreadState with libbinder_ndk. checkCallingPermission() testing in dumpPlayers is dropped since MediaPlayer2 is not running on system service so the checking in MediaPlayer2 is not meaningful. Test: make libmedia2_jni Bug: 112766913 Change-Id: I8af753f0d83689f92348ecf986c30c75d7ae12ff --- media/libmediaplayer2/Android.bp | 1 + media/libmediaplayer2/mediaplayer2.cpp | 191 ++++++++++++------------- 2 files changed, 93 insertions(+), 99 deletions(-) diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 54309ee933..38f42dc6db 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -16,6 +16,7 @@ cc_library_static { "libandroid_runtime", "libaudioclient", "libbinder", + "libbinder_ndk", "libcutils", "libgui", "liblog", diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 0923d88a4d..e088a01199 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -18,9 +18,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaPlayer2Native" -#include -#include - +#include #include #include #include @@ -102,114 +100,110 @@ status_t dumpPlayers(int fd, const Vector& args) { String8 result; SortedVector< sp > players; //to serialise the mutex unlock & client destruction. - if (checkCallingPermission(String16("android.permission.DUMP")) == false) { - snprintf(buffer, SIZE, "Permission Denial: can't dump MediaPlayer2\n"); - result.append(buffer); - } else { - { - Mutex::Autolock lock(sRecordLock); - ensureInit_l(); - for (int i = 0, n = sPlayers->size(); i < n; ++i) { - sp p = (*sPlayers)[i].promote(); - if (p != 0) { - p->dump(fd, args); - } - players.add(p); + { + Mutex::Autolock lock(sRecordLock); + ensureInit_l(); + for (int i = 0, n = sPlayers->size(); i < n; ++i) { + sp p = (*sPlayers)[i].promote(); + if (p != 0) { + p->dump(fd, args); } + players.add(p); } + } - result.append(" Files opened and/or mapped:\n"); - snprintf(buffer, SIZE, "/proc/%d/maps", getpid()); - FILE *f = fopen(buffer, "r"); - if (f) { - while (!feof(f)) { - fgets(buffer, SIZE, f); - if (strstr(buffer, " /storage/") || - strstr(buffer, " /system/sounds/") || - strstr(buffer, " /data/") || - strstr(buffer, " /system/media/")) { - result.append(" "); - result.append(buffer); - } + result.append(" Files opened and/or mapped:\n"); + snprintf(buffer, SIZE, "/proc/%d/maps", getpid()); + FILE *f = fopen(buffer, "r"); + if (f) { + while (!feof(f)) { + fgets(buffer, SIZE, f); + if (strstr(buffer, " /storage/") || + strstr(buffer, " /system/sounds/") || + strstr(buffer, " /data/") || + strstr(buffer, " /system/media/")) { + result.append(" "); + result.append(buffer); } - fclose(f); - } else { - result.append("couldn't open "); - result.append(buffer); - result.append("\n"); } - - snprintf(buffer, SIZE, "/proc/%d/fd", getpid()); - DIR *d = opendir(buffer); - if (d) { - struct dirent *ent; - while((ent = readdir(d)) != NULL) { - if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { - snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name); - struct stat s; - if (lstat(buffer, &s) == 0) { - if ((s.st_mode & S_IFMT) == S_IFLNK) { - char linkto[256]; - int len = readlink(buffer, linkto, sizeof(linkto)); - if(len > 0) { - if(len > 255) { - linkto[252] = '.'; - linkto[253] = '.'; - linkto[254] = '.'; - linkto[255] = 0; - } else { - linkto[len] = 0; - } - if (strstr(linkto, "/storage/") == linkto || - strstr(linkto, "/system/sounds/") == linkto || - strstr(linkto, "/data/") == linkto || - strstr(linkto, "/system/media/") == linkto) { - result.append(" "); - result.append(buffer); - result.append(" -> "); - result.append(linkto); - result.append("\n"); - } + fclose(f); + } else { + result.append("couldn't open "); + result.append(buffer); + result.append("\n"); + } + + snprintf(buffer, SIZE, "/proc/%d/fd", getpid()); + DIR *d = opendir(buffer); + if (d) { + struct dirent *ent; + while((ent = readdir(d)) != NULL) { + if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) { + snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name); + struct stat s; + if (lstat(buffer, &s) == 0) { + if ((s.st_mode & S_IFMT) == S_IFLNK) { + char linkto[256]; + int len = readlink(buffer, linkto, sizeof(linkto)); + if(len > 0) { + if(len > 255) { + linkto[252] = '.'; + linkto[253] = '.'; + linkto[254] = '.'; + linkto[255] = 0; + } else { + linkto[len] = 0; + } + if (strstr(linkto, "/storage/") == linkto || + strstr(linkto, "/system/sounds/") == linkto || + strstr(linkto, "/data/") == linkto || + strstr(linkto, "/system/media/") == linkto) { + result.append(" "); + result.append(buffer); + result.append(" -> "); + result.append(linkto); + result.append("\n"); } - } else { - result.append(" unexpected type for "); - result.append(buffer); - result.append("\n"); } + } else { + result.append(" unexpected type for "); + result.append(buffer); + result.append("\n"); } } } - closedir(d); - } else { - result.append("couldn't open "); - result.append(buffer); - result.append("\n"); } + closedir(d); + } else { + result.append("couldn't open "); + result.append(buffer); + result.append("\n"); + } - gLooperRoster.dump(fd, args); + gLooperRoster.dump(fd, args); - bool dumpMem = false; - bool unreachableMemory = false; - for (size_t i = 0; i < args.size(); i++) { - if (args[i] == String16("-m")) { - dumpMem = true; - } else if (args[i] == String16("--unreachable")) { - unreachableMemory = true; - } - } - if (dumpMem) { - result.append("\nDumping memory:\n"); - std::string s = dumpMemoryAddresses(100 /* limit */); - result.append(s.c_str(), s.size()); - } - if (unreachableMemory) { - result.append("\nDumping unreachable memory:\n"); - // TODO - should limit be an argument parameter? - // TODO: enable GetUnreachableMemoryString if it's part of stable API - //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */); - //result.append(s.c_str(), s.size()); + bool dumpMem = false; + bool unreachableMemory = false; + for (size_t i = 0; i < args.size(); i++) { + if (args[i] == String16("-m")) { + dumpMem = true; + } else if (args[i] == String16("--unreachable")) { + unreachableMemory = true; } } + if (dumpMem) { + result.append("\nDumping memory:\n"); + std::string s = dumpMemoryAddresses(100 /* limit */); + result.append(s.c_str(), s.size()); + } + if (unreachableMemory) { + result.append("\nDumping unreachable memory:\n"); + // TODO - should limit be an argument parameter? + // TODO: enable GetUnreachableMemoryString if it's part of stable API + //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */); + //result.append(s.c_str(), s.size()); + } + write(fd, result.string(), result.size()); return NO_ERROR; } @@ -252,9 +246,8 @@ MediaPlayer2::MediaPlayer2(int32_t sessionId) { mVideoWidth = mVideoHeight = 0; mSendLevel = 0; - // TODO: get pid and uid from JAVA - mPid = IPCThreadState::self()->getCallingPid(); - mUid = IPCThreadState::self()->getCallingUid(); + mPid = AIBinder_getCallingPid(); + mUid = AIBinder_getCallingUid(); mAudioOutput = new MediaPlayer2AudioOutput(sessionId, mUid, mPid, NULL /*attributes*/); } -- GitLab From fd018728e2e5cc8611724a2f60ba56a105e1eecf Mon Sep 17 00:00:00 2001 From: mike dooley Date: Fri, 4 Jan 2019 09:31:31 +0100 Subject: [PATCH 0685/1530] Make the framework treat getModelState recognition events the same as regular recognition events The application will differentiate between DSP and app triggered regcognition events. Test: manual test with STTA and NowPlaying app on p19 Bug: 119386757 Change-Id: Ia93aea9f56f8b9fcd31347169245f21724ac93c8 --- services/soundtrigger/SoundTriggerHwService.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index 944abcc31d..fe2ccf24ca 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -765,10 +765,7 @@ void SoundTriggerHwService::Module::onCallbackEvent(const sp& eve } recognitionEvent->capture_session = model->mCaptureSession; - // Don't reset the model state if this recognition event is a get-state response - if (recognitionEvent->status != RECOGNITION_STATUS_GET_STATE_RESPONSE) { - model->mState = Model::STATE_IDLE; - } + model->mState = Model::STATE_IDLE; clients.add(model->mModuleClient); } } break; -- GitLab From 9ed79de171bc734cc57e5835fe163cd216340f8e Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 28 Dec 2018 15:53:55 -0800 Subject: [PATCH 0686/1530] FlacDecoder: Enable float support Test: MediaCodecTest#testFlacIdentity Bug: 122117025 Change-Id: I9a80a61fbcfc615305b39befbbd9840748098a6a --- .../codec2/components/flac/C2SoftFlacDec.cpp | 27 ++- media/libstagefright/ACodec.cpp | 13 +- .../codecs/flac/dec/SoftFlacDecoder.cpp | 38 +++- .../codecs/flac/dec/SoftFlacDecoder.h | 4 + media/libstagefright/flac/dec/Android.bp | 10 +- media/libstagefright/flac/dec/FLACDecoder.cpp | 177 +++++------------- media/libstagefright/flac/dec/FLACDecoder.h | 4 +- .../include/media/stagefright/ACodec.h | 3 +- 8 files changed, 123 insertions(+), 153 deletions(-) diff --git a/media/codec2/components/flac/C2SoftFlacDec.cpp b/media/codec2/components/flac/C2SoftFlacDec.cpp index f1e2f51946..86b16e8a64 100644 --- a/media/codec2/components/flac/C2SoftFlacDec.cpp +++ b/media/codec2/components/flac/C2SoftFlacDec.cpp @@ -83,8 +83,21 @@ public: DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 32768)) .build()); + + addParameter( + DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING) + .withDefault(new C2StreamPcmEncodingInfo::output(0u, C2Config::PCM_16)) + .withFields({C2F(mPcmEncodingInfo, value).oneOf({ + C2Config::PCM_16, + // C2Config::PCM_8, + C2Config::PCM_FLOAT}) + }) + .withSetter((Setter::StrictValueWithNoDeps)) + .build()); } + int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } + private: std::shared_ptr mInputFormat; std::shared_ptr mOutputFormat; @@ -94,6 +107,7 @@ private: std::shared_ptr mChannelCount; std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; + std::shared_ptr mPcmEncodingInfo; }; C2SoftFlacDec::C2SoftFlacDec( @@ -263,11 +277,11 @@ void C2SoftFlacDec::process( return; } - size_t outSize; - if (mHasStreamInfo) - outSize = mStreamInfo.max_blocksize * mStreamInfo.channels * sizeof(short); - else - outSize = kMaxBlockSize * FLACDecoder::kMaxChannels * sizeof(short); + const bool outputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT; + const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(short); + size_t outSize = mHasStreamInfo ? + mStreamInfo.max_blocksize * mStreamInfo.channels * sampleSize + : kMaxBlockSize * FLACDecoder::kMaxChannels * sampleSize; std::shared_ptr block; C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; @@ -284,9 +298,8 @@ void C2SoftFlacDec::process( return; } - short *output = reinterpret_cast(wView.data()); status_t decoderErr = mFLACDecoder->decodeOneFrame( - input, inSize, output, &outSize); + input, inSize, wView.data(), &outSize, outputFloat); if (decoderErr != OK) { ALOGE("process: FLACDecoder decodeOneFrame returns error %d", decoderErr); mSignalledError = true; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ceb8a13e32..2ea5286bea 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2183,7 +2183,8 @@ status_t ACodec::configureCodec( err = setupG711Codec(encoder, sampleRate, numChannels); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { - int32_t numChannels = 0, sampleRate = 0, compressionLevel = -1; + // numChannels needs to be set to properly communicate PCM values. + int32_t numChannels = 2, sampleRate = 44100, compressionLevel = -1; if (encoder && (!msg->findInt32("channel-count", &numChannels) || !msg->findInt32("sample-rate", &sampleRate))) { @@ -2209,7 +2210,7 @@ status_t ACodec::configureCodec( } } err = setupFlacCodec( - encoder, numChannels, sampleRate, compressionLevel); + encoder, numChannels, sampleRate, compressionLevel, pcmEncoding); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { int32_t numChannels, sampleRate; @@ -3033,8 +3034,8 @@ status_t ACodec::setupG711Codec(bool encoder, int32_t sampleRate, int32_t numCha } status_t ACodec::setupFlacCodec( - bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel) { - + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel, + AudioEncoding encoding) { if (encoder) { OMX_AUDIO_PARAM_FLACTYPE def; InitOMXParams(&def); @@ -3057,7 +3058,8 @@ status_t ACodec::setupFlacCodec( return setupRawAudioFormat( encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, - numChannels); + numChannels, + encoding); } status_t ACodec::setupRawAudioFormat( @@ -3115,6 +3117,7 @@ status_t ACodec::setupRawAudioFormat( pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; if (getOMXChannelMapping(numChannels, pcmParams.eChannelMapping) != OK) { + ALOGE("%s: incorrect numChannels: %d", __func__, numChannels); return OMX_ErrorNone; } diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp index 4db0060deb..842a7ce169 100644 --- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp @@ -89,12 +89,12 @@ void SoftFlacDecoder::initPorts() { def.eDir = OMX_DirOutput; def.nBufferCountMin = kNumOutputBuffers; def.nBufferCountActual = def.nBufferCountMin; - def.nBufferSize = 4096 * FLACDecoder::kMaxChannels; + def.nBufferSize = kNumSamplesPerFrame * FLACDecoder::kMaxChannels * sizeof(float); def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; + def.nBufferAlignment = sizeof(float); def.format.audio.cMIMEType = const_cast("audio/raw"); def.format.audio.pNativeRender = NULL; @@ -173,7 +173,7 @@ OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter( flacParams->nChannels = mStreamInfo.channels; flacParams->nSampleRate = mStreamInfo.sample_rate; } else { - flacParams->nChannels = 1; + flacParams->nChannels = 2; flacParams->nSampleRate = 44100; } @@ -195,10 +195,10 @@ OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter( return OMX_ErrorBadPortIndex; } - pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eNumData = mNumericalData; pcmParams->eEndian = OMX_EndianBig; pcmParams->bInterleaved = OMX_TRUE; - pcmParams->nBitPerSample = 16; + pcmParams->nBitPerSample = mBitsPerSample; pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; @@ -211,7 +211,7 @@ OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter( pcmParams->nChannels = mStreamInfo.channels; pcmParams->nSamplingRate = mStreamInfo.sample_rate; } else { - pcmParams->nChannels = 1; + pcmParams->nChannels = 2; pcmParams->nSamplingRate = 44100; } @@ -281,6 +281,19 @@ OMX_ERRORTYPE SoftFlacDecoder::internalSetParameter( return OMX_ErrorBadPortIndex; } + if (pcmParams->eNumData == OMX_NumericalDataFloat && pcmParams->nBitPerSample == 32) { + mNumericalData = OMX_NumericalDataFloat; + mBitsPerSample = 32; + } else if (pcmParams->eNumData == OMX_NumericalDataSigned + && pcmParams->nBitPerSample == 16) { + mNumericalData = OMX_NumericalDataSigned; + mBitsPerSample = 16; + } else { + ALOGE("Invalid eNumData %d, nBitsPerSample %d", + pcmParams->eNumData, pcmParams->nBitPerSample); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; } @@ -301,11 +314,13 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) { List &inQueue = getPortQueue(0); List &outQueue = getPortQueue(1); + const bool outputFloat = mNumericalData == OMX_NumericalDataFloat; + ALOGV("onQueueFilled %d/%d:", inQueue.empty(), outQueue.empty()); while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty() && !mFinishedDecoder) { BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - int16_t *outBuffer = reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + void *outBuffer = reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset; int64_t timeStamp = 0; @@ -374,7 +389,7 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) { } status_t decoderErr = mFLACDecoder->decodeOneFrame( - inBuffer, inBufferLength, outBuffer, &outBufferSize); + inBuffer, inBufferLength, outBuffer, &outBufferSize, outputFloat); if (decoderErr != OK) { ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr); mSignalledError = true; @@ -393,7 +408,9 @@ void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) { continue; } } else if (mSawInputEOS) { - status_t decoderErr = mFLACDecoder->decodeOneFrame(NULL, 0, outBuffer, &outBufferSize); + status_t decoderErr = mFLACDecoder->decodeOneFrame( + nullptr /* inBuffer */, 0 /* inBufferLen */, + outBuffer, &outBufferSize, outputFloat); mFinishedDecoder = true; if (decoderErr != OK) { ALOGE("onQueueFilled: FLACDecoder finish returns error %d", decoderErr); @@ -456,7 +473,8 @@ void SoftFlacDecoder::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { mOutputPortSettingsChange = AWAITING_ENABLED; PortInfo *info = editPortInfo(1 /* portIndex */); if (!info->mDef.bEnabled) { - info->mDef.nBufferSize = mStreamInfo.max_blocksize * mStreamInfo.channels * 2; + info->mDef.nBufferSize = + mStreamInfo.max_blocksize * mStreamInfo.channels * sizeof(float); } break; } diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h index b63f7ad040..ba02074c6d 100644 --- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h @@ -45,10 +45,14 @@ protected: virtual void onReset() override; private: + static constexpr unsigned int kNumSamplesPerFrame = 2048; // adjusted based on stream. + enum { kNumInputBuffers = 4, kNumOutputBuffers = 4, }; + OMX_NUMERICALDATATYPE mNumericalData = OMX_NumericalDataSigned; + OMX_U32 mBitsPerSample = 16; FLACDecoder *mFLACDecoder; FLAC__StreamMetadata_StreamInfo mStreamInfo; diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp index 751b05358b..307c9b0e99 100644 --- a/media/libstagefright/flac/dec/Android.bp +++ b/media/libstagefright/flac/dec/Android.bp @@ -27,11 +27,17 @@ cc_library { }, static: { - whole_static_libs: ["libFLAC"], + whole_static_libs: [ + "libFLAC", + "libaudioutils", + ], }, shared: { - static_libs: ["libFLAC"], + static_libs: [ + "libFLAC", + "libaudioutils", + ], }, shared_libs: [ diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp index dfdc41c211..cef0bc6c24 100644 --- a/media/libstagefright/flac/dec/FLACDecoder.cpp +++ b/media/libstagefright/flac/dec/FLACDecoder.cpp @@ -20,6 +20,7 @@ #include "FLACDecoder.h" +#include // float_from_i32 #include #include #include @@ -117,104 +118,43 @@ void FLACDecoder::errorCallback(FLAC__StreamDecoderErrorStatus status) mErrorStatus = status; } -// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. +// Copy samples from FLAC native 32-bit non-interleaved to 16-bit signed +// or 32-bit float interleaved. +// TODO: Consider moving to audio_utils. See similar code at FLACExtractor.cpp // These are candidates for optimization if needed. -static void copyMono8( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], +static void copyTo16Signed( + short *dst, + const int *const *src, unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] << 8; - } -} - -static void copyStereo8( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] << 8; - *dst++ = src[1][i] << 8; - } -} - -static void copyMultiCh8( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned nChannels) { - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i] << 8; + unsigned nChannels, + unsigned bitsPerSample) { + const int leftShift = 16 - (int)bitsPerSample; // cast to int to prevent unsigned overflow. + if (leftShift >= 0) { + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i] << leftShift; + } } - } -} - -static void copyMono16( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i]; - } -} - -static void copyStereo16( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i]; - *dst++ = src[1][i]; - } -} - -static void copyMultiCh16( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned nChannels) { - for (unsigned i = 0; i < nSamples; ++i) { - for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i]; + } else { + const int rightShift = -leftShift; + for (unsigned i = 0; i < nSamples; ++i) { + for (unsigned c = 0; c < nChannels; ++c) { + *dst++ = src[c][i] >> rightShift; + } } } } -// TODO: 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger -static void copyMono24( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], +static void copyToFloat( + float *dst, + const int *const *src, unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] >> 8; - } -} - -static void copyStereo24( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned /* nChannels */) { - for (unsigned i = 0; i < nSamples; ++i) { - *dst++ = src[0][i] >> 8; - *dst++ = src[1][i] >> 8; - } -} - -static void copyMultiCh24( - int16_t *dst, - const int * src[FLACDecoder::kMaxChannels], - unsigned nSamples, - unsigned nChannels) { + unsigned nChannels, + unsigned bitsPerSample) { + const unsigned leftShift = 32 - bitsPerSample; for (unsigned i = 0; i < nSamples; ++i) { for (unsigned c = 0; c < nChannels; ++c) { - *dst++ = src[c][i] >> 8; + *dst++ = float_from_i32(src[c][i] << leftShift); } } } @@ -238,8 +178,7 @@ FLACDecoder::FLACDecoder() mStreamInfoValid(false), mWriteRequested(false), mWriteCompleted(false), - mErrorStatus((FLAC__StreamDecoderErrorStatus) -1), - mCopy(nullptr) { + mErrorStatus((FLAC__StreamDecoderErrorStatus) -1) { ALOGV("ctor:"); memset(&mStreamInfo, 0, sizeof(mStreamInfo)); memset(&mWriteHeader, 0, sizeof(mWriteHeader)); @@ -379,6 +318,7 @@ status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) case 8: case 16: case 24: + case 32: // generally rare, but is supported in the framework break; default: @@ -387,31 +327,6 @@ status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) return ERROR_MALFORMED; } - // configure the appropriate copy function, defaulting to trespass - static const struct { - unsigned mChannels; - unsigned mBitsPerSample; - void (*mCopy)(int16_t *dst, const int * src[kMaxChannels], - unsigned nSamples, unsigned nChannels); - } table[] = { - { 1, 8, copyMono8 }, - { 2, 8, copyStereo8 }, - { 8, 8, copyMultiCh8 }, - { 1, 16, copyMono16 }, - { 2, 16, copyStereo16 }, - { 8, 16, copyMultiCh16 }, - { 1, 24, copyMono24 }, - { 2, 24, copyStereo24 }, - { 8, 24, copyMultiCh24 }, - }; - for (const auto &entry : table) { - if (entry.mChannels >= getChannels() && - entry.mBitsPerSample == getBitsPerSample()) { - mCopy = entry.mCopy; - break; - } - } - // Now we have all metadata blocks. mBufferPos = 0; mBufferDataSize = 0; @@ -420,7 +335,7 @@ status_t FLACDecoder::parseMetadata(const uint8_t *inBuffer, size_t inBufferLen) } status_t FLACDecoder::decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen, - int16_t *outBuffer, size_t *outBufferLen) { + void *outBuffer, size_t *outBufferLen, bool outputFloat) { ALOGV("decodeOneFrame: input size(%zu)", inBufferLen); if (!mStreamInfoValid) { @@ -469,21 +384,33 @@ status_t FLACDecoder::decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen return ERROR_MALFORMED; } - size_t bufferSize = blocksize * getChannels() * sizeof(int16_t); + const unsigned channels = getChannels(); + const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(int16_t); + const size_t frameSize = channels * sampleSize; + size_t bufferSize = blocksize * frameSize; if (bufferSize > *outBufferLen) { ALOGW("decodeOneFrame: output buffer holds only partial frame %zu:%zu", *outBufferLen, bufferSize); - blocksize = *outBufferLen / (getChannels() * sizeof(int16_t)); - bufferSize = blocksize * getChannels() * sizeof(int16_t); + blocksize = *outBufferLen / frameSize; + bufferSize = blocksize * frameSize; } - if (mCopy == nullptr) { - ALOGE("decodeOneFrame: format is not supported: channels(%d), BitsPerSample(%d)", - getChannels(), getBitsPerSample()); - return ERROR_UNSUPPORTED; - } // copy PCM from FLAC write buffer to output buffer, with interleaving - (*mCopy)(outBuffer, mWriteBuffer, blocksize, getChannels()); + + const unsigned bitsPerSample = getBitsPerSample(); + if (outputFloat) { + copyToFloat(reinterpret_cast(outBuffer), + mWriteBuffer, + blocksize, + channels, + bitsPerSample); + } else { + copyTo16Signed(reinterpret_cast(outBuffer), + mWriteBuffer, + blocksize, + channels, + bitsPerSample); + } *outBufferLen = bufferSize; return OK; } diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h index af419a2110..694fccb25e 100644 --- a/media/libstagefright/flac/dec/FLACDecoder.h +++ b/media/libstagefright/flac/dec/FLACDecoder.h @@ -41,7 +41,7 @@ public: status_t parseMetadata(const uint8_t *inBuffer, size_t inBufferLen); status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen, - int16_t *outBuffer, size_t *outBufferLen); + void *outBuffer, size_t *outBufferLen, bool outputFloat = false); void flush(); virtual ~FLACDecoder(); @@ -89,8 +89,6 @@ private: // most recent error reported by libFLAC decoder FLAC__StreamDecoderErrorStatus mErrorStatus; - void (*mCopy)(int16_t *dst, const int *src[kMaxChannels], unsigned nSamples, unsigned nChannels); - status_t init(); // FLAC stream decoder callbacks as C++ instance methods diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 9b2853e238..9d46d2d6a9 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -488,7 +488,8 @@ private: status_t setupG711Codec(bool encoder, int32_t sampleRate, int32_t numChannels); status_t setupFlacCodec( - bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel); + bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel, + AudioEncoding encoding); status_t setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels, -- GitLab From 1188a0511ec6b02b83123782378862fadea96be6 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 2 Jan 2019 13:09:52 -0800 Subject: [PATCH 0687/1530] FlacEncoder: Enable float support Test: MediaCodecTest#testFlacIdentity Bug: 122264366 Change-Id: Ic692ce0d5dc8358cbd173013c9d4f68b7e45a8ad --- media/codec2/components/flac/Android.bp | 5 +- .../codec2/components/flac/C2SoftFlacEnc.cpp | 48 +++++++++++---- .../libstagefright/codecs/flac/enc/Android.bp | 5 +- .../codecs/flac/enc/SoftFlacEncoder.cpp | 60 ++++++++++++++----- .../codecs/flac/enc/SoftFlacEncoder.h | 8 ++- 5 files changed, 95 insertions(+), 31 deletions(-) diff --git a/media/codec2/components/flac/Android.bp b/media/codec2/components/flac/Android.bp index d48596440a..e5eb51d8d5 100644 --- a/media/codec2/components/flac/Android.bp +++ b/media/codec2/components/flac/Android.bp @@ -23,5 +23,8 @@ cc_library_shared { srcs: ["C2SoftFlacEnc.cpp"], - static_libs: ["libFLAC"], + static_libs: [ + "libaudioutils", + "libFLAC", + ], } diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp index e4192c72f0..4ea35c21f9 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.cpp +++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "C2SoftFlacEnc" #include +#include #include #include @@ -72,11 +73,23 @@ public: DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 4608)) .build()); + + addParameter( + DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING) + .withDefault(new C2StreamPcmEncodingInfo::input(0u, C2Config::PCM_16)) + .withFields({C2F(mPcmEncodingInfo, value).oneOf({ + C2Config::PCM_16, + // C2Config::PCM_8, + C2Config::PCM_FLOAT}) + }) + .withSetter((Setter::StrictValueWithNoDeps)) + .build()); } uint32_t getSampleRate() const { return mSampleRate->value; } uint32_t getChannelCount() const { return mChannelCount->value; } uint32_t getBitrate() const { return mBitrate->value; } + int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: std::shared_ptr mInputFormat; @@ -87,6 +100,7 @@ private: std::shared_ptr mChannelCount; std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; + std::shared_ptr mPcmEncodingInfo; }; constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder"; @@ -224,12 +238,15 @@ void C2SoftFlacEnc::process( mWroteHeader = true; } - uint32_t sampleRate = mIntf->getSampleRate(); - uint32_t channelCount = mIntf->getChannelCount(); - uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate; + const uint32_t sampleRate = mIntf->getSampleRate(); + const uint32_t channelCount = mIntf->getChannelCount(); + const bool inputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT; + const unsigned sampleSize = inputFloat ? sizeof(float) : sizeof(int16_t); + const unsigned frameSize = channelCount * sampleSize; + const uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate; size_t outCapacity = inSize; - outCapacity += mBlockSize * channelCount * sizeof(int16_t); + outCapacity += mBlockSize * frameSize; C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &mOutputBlock); @@ -250,14 +267,19 @@ void C2SoftFlacEnc::process( size_t inPos = 0; while (inPos < inSize) { const uint8_t *inPtr = rView.data() + inOffset; - size_t processSize = MIN(kInBlockSize * channelCount * sizeof(int16_t), (inSize - inPos)); - const unsigned nbInputFrames = processSize / (channelCount * sizeof(int16_t)); - const unsigned nbInputSamples = processSize / sizeof(int16_t); - const int16_t *pcm16 = reinterpret_cast(inPtr + inPos); - ALOGV("about to encode %zu bytes", processSize); + const size_t processSize = MIN(kInBlockSize * frameSize, (inSize - inPos)); + const unsigned nbInputFrames = processSize / frameSize; + const unsigned nbInputSamples = processSize / sampleSize; - for (unsigned i = 0; i < nbInputSamples; i++) { - mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + ALOGV("about to encode %zu bytes", processSize); + if (inputFloat) { + const float * const pcmFloat = reinterpret_cast(inPtr + inPos); + memcpy_to_q8_23_from_float_with_clamp(mInputBufferPcm32, pcmFloat, nbInputSamples); + } else { + const int16_t * const pcm16 = reinterpret_cast(inPtr + inPos); + for (unsigned i = 0; i < nbInputSamples; i++) { + mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + } } FLAC__bool ok = FLAC__stream_encoder_process_interleaved( @@ -342,10 +364,12 @@ status_t C2SoftFlacEnc::configureEncoder() { return UNKNOWN_ERROR; } + const bool inputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT; + const int bitsPerSample = inputFloat ? 24 : 16; FLAC__bool ok = true; ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mIntf->getChannelCount()); ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mIntf->getSampleRate()); - ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16); + ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, bitsPerSample); ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel); ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false); if (!ok) { diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp index 9b696da25d..b32ab08a6c 100644 --- a/media/libstagefright/codecs/flac/enc/Android.bp +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -28,7 +28,10 @@ cc_library_shared { ], header_libs: ["libbase_headers"], - static_libs: ["libFLAC"], + static_libs: [ + "libaudioutils", + "libFLAC", + ], name: "libstagefright_soft_flacenc", vendor_available: true, diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp index 955f211f4f..3add006c9d 100644 --- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp @@ -20,7 +20,7 @@ #include #include "SoftFlacEncoder.h" - +#include #include #include @@ -75,7 +75,9 @@ SoftFlacEncoder::SoftFlacEncoder( } if (!mSignalledError) { // no use allocating input buffer if we had an error above - mInputBufferPcm32 = (FLAC__int32*) malloc(sizeof(FLAC__int32) * 2 * kMaxNumSamplesPerFrame); + // 2x the pcm16 samples can exist with the same size as pcmFloat samples. + mInputBufferPcm32 = (FLAC__int32*) malloc( + sizeof(FLAC__int32) * kNumSamplesPerFrame * kMaxChannels * 2); if (mInputBufferPcm32 == NULL) { ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error allocating internal input buffer", name); mSignalledError = true; @@ -115,14 +117,14 @@ void SoftFlacEncoder::initPorts() { // configure input port of the encoder def.nPortIndex = 0; def.eDir = OMX_DirInput; - def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough + def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; def.nBufferSize = kMaxInputBufferSize; def.bEnabled = OMX_TRUE; def.bPopulated = OMX_FALSE; def.eDomain = OMX_PortDomainAudio; def.bBuffersContiguous = OMX_FALSE; - def.nBufferAlignment = 2; + def.nBufferAlignment = sizeof(float); def.format.audio.cMIMEType = const_cast("audio/raw"); def.format.audio.pNativeRender = NULL; @@ -134,7 +136,7 @@ void SoftFlacEncoder::initPorts() { // configure output port of the encoder def.nPortIndex = 1; def.eDir = OMX_DirOutput; - def.nBufferCountMin = kNumBuffers;// TODO verify that 1 is enough + def.nBufferCountMin = kNumBuffers; def.nBufferCountActual = def.nBufferCountMin; def.nBufferSize = kMaxOutputBufferSize; def.bEnabled = OMX_TRUE; @@ -193,10 +195,10 @@ OMX_ERRORTYPE SoftFlacEncoder::internalGetParameter( return OMX_ErrorUndefined; } - pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eNumData = mNumericalData; pcmParams->eEndian = OMX_EndianBig; pcmParams->bInterleaved = OMX_TRUE; - pcmParams->nBitPerSample = 16; + pcmParams->nBitPerSample = mBitsPerSample; pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; @@ -270,12 +272,26 @@ OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( return OMX_ErrorUndefined; } - if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) { + if (pcmParams->nChannels < 1 || pcmParams->nChannels > kMaxChannels) { return OMX_ErrorUndefined; } mNumChannels = pcmParams->nChannels; mSampleRate = pcmParams->nSamplingRate; + + if (pcmParams->eNumData == OMX_NumericalDataFloat && pcmParams->nBitPerSample == 32) { + mNumericalData = OMX_NumericalDataFloat; + mBitsPerSample = 32; + } else if (pcmParams->eNumData == OMX_NumericalDataSigned + && pcmParams->nBitPerSample == 16) { + mNumericalData = OMX_NumericalDataSigned; + mBitsPerSample = 16; + } else { + ALOGE("%s: invalid eNumData %d, nBitsPerSample %d", + __func__, pcmParams->eNumData, pcmParams->nBitPerSample); + return OMX_ErrorUndefined; + } + ALOGV("will encode %d channels at %dHz", mNumChannels, mSampleRate); return configureEncoder(); @@ -356,6 +372,10 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { List &inQueue = getPortQueue(0); List &outQueue = getPortQueue(1); + const bool inputFloat = mNumericalData == OMX_NumericalDataFloat; + const size_t sampleSize = inputFloat ? sizeof(float) : sizeof(int16_t); + const size_t frameSize = sampleSize * mNumChannels; + FLAC__bool ok = true; while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty() && !mSentOutputEOS) { @@ -381,13 +401,21 @@ void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { mEncoderReturnedNbBytes = 0; mCurrentInputTimeStamp = inHeader->nTimeStamp; - const unsigned nbInputFrames = inHeader->nFilledLen / (2 * mNumChannels); - const unsigned nbInputSamples = inHeader->nFilledLen / 2; - const OMX_S16 * const pcm16 = reinterpret_cast(inHeader->pBuffer); + const unsigned nbInputFrames = inHeader->nFilledLen / frameSize; + const unsigned nbInputSamples = inHeader->nFilledLen / sampleSize; - CHECK_LE(nbInputSamples, 2 * kMaxNumSamplesPerFrame); - for (unsigned i=0 ; i < nbInputSamples ; i++) { - mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + if (inputFloat) { + CHECK_LE(nbInputSamples, kNumSamplesPerFrame * kMaxChannels); + const float * const pcmFloat = reinterpret_cast(inHeader->pBuffer); + memcpy_to_q8_23_from_float_with_clamp( + mInputBufferPcm32, pcmFloat, nbInputSamples); + } else { + // note nbInputSamples may be 2x as large for pcm16 data. + CHECK_LE(nbInputSamples, kNumSamplesPerFrame * kMaxChannels * 2); + const int16_t * const pcm16 = reinterpret_cast(inHeader->pBuffer); + for (unsigned i = 0; i < nbInputSamples; ++i) { + mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + } } ALOGV(" about to encode %u samples per channel", nbInputFrames); ok = FLAC__stream_encoder_process_interleaved( @@ -526,10 +554,12 @@ OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() { return OMX_ErrorInvalidState; } + const bool inputFloat = mNumericalData == OMX_NumericalDataFloat; + const int codecBitsPerSample = inputFloat ? 24 : 16; FLAC__bool ok = true; ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels); ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate); - ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16); + ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, codecBitsPerSample); ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, (unsigned)mCompressionLevel); ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false); diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h index 64a6b1edcc..722fc13fa1 100644 --- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h @@ -45,8 +45,10 @@ protected: private: const unsigned int kNumBuffers = 2; - const unsigned int kMaxNumSamplesPerFrame = 1152; - const unsigned int kMaxInputBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t) * 2; + static constexpr unsigned int kMaxChannels = 2; + static constexpr unsigned int kNumSamplesPerFrame = 1152; + static constexpr unsigned int kMaxInputBufferSize = + kNumSamplesPerFrame * kMaxChannels * sizeof(float); const unsigned int kMaxOutputBufferSize = 65536; //TODO check if this can be reduced bool mSignalledError; @@ -54,6 +56,8 @@ private: OMX_U32 mNumChannels; OMX_U32 mSampleRate; OMX_U32 mCompressionLevel; + OMX_NUMERICALDATATYPE mNumericalData = OMX_NumericalDataSigned; + OMX_U32 mBitsPerSample = 16; // should the data received by the callback be written to the output port bool mEncoderWriteData; -- GitLab From ce727fe64f379a0fced2d3b0713e5323806c6ffa Mon Sep 17 00:00:00 2001 From: Victor Chang Date: Wed, 9 Jan 2019 12:59:02 +0000 Subject: [PATCH 0688/1530] Remove unused ICU4C dependency in audio policy Bug: 117094880 Test: m droid Change-Id: I8ba3db496f11630eebabb7184c532d59237279b0 --- services/audiopolicy/common/managerdefinitions/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp index d0b4973885..e5ebab7e16 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.bp +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -24,7 +24,6 @@ cc_library_static { shared_libs: [ "libcutils", "libhidlbase", - "libicuuc", "liblog", "libmedia", "libutils", -- GitLab From 18862186d39954af7f89b6018bb0d99b10f6fa15 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 8 Jan 2019 16:32:41 -0800 Subject: [PATCH 0689/1530] Virtualizer: Fix channel configuration capability Channel masks beyond stereo are not supported by Concert Surround virtualization at this time. Test: VirtualizerTest#test4_1SpeakerAnglesCapaMatchesFormatModeCapa Bug: 122358150 Change-Id: Ib5ea0f8655c1845d5974ec9cc18a821652fac9b4 --- media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index b5860de718..0c6f8de3c0 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -1544,7 +1544,7 @@ int VirtualizerIsDeviceSupported(audio_devices_t deviceType) { int VirtualizerIsConfigurationSupported(audio_channel_mask_t channelMask, audio_devices_t deviceType) { uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); - if (channelCount < 1 || channelCount > LVM_MAX_CHANNELS) { + if (channelCount < 1 || channelCount > FCC_2) { // TODO: update to 8 channels when supported. return -EINVAL; } return VirtualizerIsDeviceSupported(deviceType); -- GitLab From fc9f40f380004ac7f23aa9f4312fdf227181235f Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Fri, 4 Jan 2019 14:15:28 -0800 Subject: [PATCH 0690/1530] jWakeLock replace native binder with java PowerManager in JWakeLock Test: MediaPlayer2Test Bug: 122470692 Change-Id: I488758892d225ca31defbe3d96ebf68a57dc9e35 --- .../include/mediaplayer2/mediaplayer2.h | 5 +- media/libmediaplayer2/mediaplayer2.cpp | 11 +-- media/libmediaplayer2/nuplayer2/JWakeLock.cpp | 89 ++++++++----------- media/libmediaplayer2/nuplayer2/JWakeLock.h | 29 ++---- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 8 +- media/libmediaplayer2/nuplayer2/NuPlayer2.h | 7 +- .../nuplayer2/NuPlayer2Driver.cpp | 4 +- .../nuplayer2/NuPlayer2Driver.h | 3 +- .../nuplayer2/NuPlayer2Renderer.cpp | 3 +- .../nuplayer2/NuPlayer2Renderer.h | 2 + 10 files changed, 72 insertions(+), 89 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index b8d034b197..78865c448d 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -54,7 +54,7 @@ class MediaPlayer2 : public MediaPlayer2InterfaceListener public: ~MediaPlayer2(); - static sp Create(int32_t sessionId); + static sp Create(int32_t sessionId, jobject context); static status_t DumpAll(int fd, const Vector& args); void disconnect(); @@ -117,7 +117,7 @@ public: status_t dump(int fd, const Vector& args); private: - MediaPlayer2(int32_t sessionId); + MediaPlayer2(int32_t sessionId, jobject context); bool init(); // Disconnect from the currently connected ANativeWindow. @@ -153,6 +153,7 @@ private: int mVideoHeight; int32_t mAudioSessionId; sp mAudioAttributes; + sp mContext; float mSendLevel; sp mConnectedWindow; }; diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index e088a01199..f75380ced6 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -211,8 +211,8 @@ status_t dumpPlayers(int fd, const Vector& args) { } // anonymous namespace //static -sp MediaPlayer2::Create(int32_t sessionId) { - sp player = new MediaPlayer2(sessionId); +sp MediaPlayer2::Create(int32_t sessionId, jobject context) { + sp player = new MediaPlayer2(sessionId, context); if (!player->init()) { return NULL; @@ -229,13 +229,14 @@ status_t MediaPlayer2::DumpAll(int fd, const Vector& args) { return dumpPlayers(fd, args); } -MediaPlayer2::MediaPlayer2(int32_t sessionId) { +MediaPlayer2::MediaPlayer2(int32_t sessionId, jobject context) { ALOGV("constructor"); mSrcId = 0; mLockThreadId = 0; mListener = NULL; mStreamType = AUDIO_STREAM_MUSIC; mAudioAttributes = NULL; + mContext = new JObjectHolder(context); mCurrentPosition = -1; mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC; mSeekPosition = -1; @@ -326,15 +327,15 @@ status_t MediaPlayer2::setDataSource(const sp &dsd) { sp oldPlayer; - Mutex::Autolock _l(mLock); { + Mutex::Autolock _l(mLock); if (!((mCurrentState & MEDIA_PLAYER2_IDLE) || mCurrentState == MEDIA_PLAYER2_STATE_ERROR)) { ALOGE("setDataSource called in wrong state %d", mCurrentState); return INVALID_OPERATION; } - sp player = new NuPlayer2Driver(mPid, mUid); + sp player = new NuPlayer2Driver(mPid, mUid, mContext); status_t err = player->initCheck(); if (err != NO_ERROR) { ALOGE("Failed to create player object, initCheck failed(%d)", err); diff --git a/media/libmediaplayer2/nuplayer2/JWakeLock.cpp b/media/libmediaplayer2/nuplayer2/JWakeLock.cpp index c9a107170f..983d77e05a 100644 --- a/media/libmediaplayer2/nuplayer2/JWakeLock.cpp +++ b/media/libmediaplayer2/nuplayer2/JWakeLock.cpp @@ -20,55 +20,50 @@ #include "JWakeLock.h" -#include -#include #include -#include - namespace android { -//TODO: use JAVA PowerManager, instead of binder -JWakeLock::JWakeLock() : - mPowerManager(NULL), - mWakeLockToken(NULL), +JWakeLock::JWakeLock(const sp &context) : mWakeLockCount(0), - mDeathRecipient(new PMDeathRecipient(this)) {} + mWakeLock(NULL), + mContext(context) {} JWakeLock::~JWakeLock() { - if (mPowerManager != NULL) { - sp binder = IInterface::asBinder(mPowerManager); - binder->unlinkToDeath(mDeathRecipient); - } - clearPowerManager(); + clearJavaWakeLock(); } bool JWakeLock::acquire() { if (mWakeLockCount == 0) { - CHECK(mWakeLockToken == NULL); - if (mPowerManager == NULL) { - // use checkService() to avoid blocking if power service is not up yet - sp binder = - defaultServiceManager()->checkService(String16("power")); - if (binder == NULL) { - ALOGW("could not get the power manager service"); - } else { - mPowerManager = interface_cast(binder); - binder->linkToDeath(mDeathRecipient); - } + if (mWakeLock == NULL) { + JNIEnv *env = JavaVMHelper::getJNIEnv(); + jclass jContextCls = env->FindClass("android/content/Context"); + jclass jPowerManagerCls = env->FindClass("android/os/PowerManager"); + + jmethodID jGetSystemService = env->GetMethodID(jContextCls, + "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject javaPowerManagerObj = env->CallObjectMethod(mContext->getJObject(), + jGetSystemService, env->NewStringUTF("power")); + + jfieldID jPARTIAL_WAKE_LOCK = env->GetStaticFieldID(jPowerManagerCls, + "PARTIAL_WAKE_LOCK", "I"); + jint PARTIAL_WAKE_LOCK = env->GetStaticIntField(jPowerManagerCls, jPARTIAL_WAKE_LOCK); + + jmethodID jNewWakeLock = env->GetMethodID(jPowerManagerCls, + "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;"); + jobject javaWakeLock = env->CallObjectMethod(javaPowerManagerObj, + jNewWakeLock, PARTIAL_WAKE_LOCK, env->NewStringUTF("JWakeLock")); + mWakeLock = new JObjectHolder(javaWakeLock); + env->DeleteLocalRef(javaPowerManagerObj); + env->DeleteLocalRef(javaWakeLock); } - if (mPowerManager != NULL) { - sp binder = new BBinder(); - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - status_t status = mPowerManager->acquireWakeLock( - POWERMANAGER_PARTIAL_WAKE_LOCK, - binder, String16("JWakeLock"), String16("media")); - IPCThreadState::self()->restoreCallingIdentity(token); - if (status == NO_ERROR) { - mWakeLockToken = binder; - mWakeLockCount++; - return true; - } + if (mWakeLock != NULL) { + JNIEnv *env = JavaVMHelper::getJNIEnv(); + jclass wakeLockCls = env->FindClass("android/os/PowerManager$WakeLock"); + jmethodID jAcquire = env->GetMethodID(wakeLockCls, "acquire", "()V"); + env->CallVoidMethod(mWakeLock->getJObject(), jAcquire); + mWakeLockCount++; + return true; } } else { mWakeLockCount++; @@ -86,25 +81,17 @@ void JWakeLock::release(bool force) { mWakeLockCount = 1; } if (--mWakeLockCount == 0) { - CHECK(mWakeLockToken != NULL); - if (mPowerManager != NULL) { - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - mPowerManager->releaseWakeLock(mWakeLockToken, 0 /* flags */); - IPCThreadState::self()->restoreCallingIdentity(token); + if (mWakeLock != NULL) { + JNIEnv *env = JavaVMHelper::getJNIEnv(); + jclass wakeLockCls = env->FindClass("android/os/PowerManager$WakeLock"); + jmethodID jRelease = env->GetMethodID(wakeLockCls, "release", "()V"); + env->CallVoidMethod(mWakeLock->getJObject(), jRelease); } - mWakeLockToken.clear(); } } -void JWakeLock::clearPowerManager() { +void JWakeLock::clearJavaWakeLock() { release(true); - mPowerManager.clear(); -} - -void JWakeLock::PMDeathRecipient::binderDied(const wp& who __unused) { - if (mWakeLock != NULL) { - mWakeLock->clearPowerManager(); - } } } // namespace android diff --git a/media/libmediaplayer2/nuplayer2/JWakeLock.h b/media/libmediaplayer2/nuplayer2/JWakeLock.h index eace87e9dc..36c542e335 100644 --- a/media/libmediaplayer2/nuplayer2/JWakeLock.h +++ b/media/libmediaplayer2/nuplayer2/JWakeLock.h @@ -18,7 +18,7 @@ #define J_WAKELOCK_H_ #include -#include +#include #include namespace android { @@ -26,7 +26,7 @@ namespace android { class JWakeLock : public RefBase { public: - JWakeLock(); + JWakeLock(const sp &context); // NOTE: acquire and release are not thread safe @@ -37,28 +37,11 @@ public: virtual ~JWakeLock(); private: - sp mPowerManager; - sp mWakeLockToken; - uint32_t mWakeLockCount; + uint32_t mWakeLockCount; + sp mWakeLock; + const sp mContext; - class PMDeathRecipient : public IBinder::DeathRecipient { - public: - explicit PMDeathRecipient(JWakeLock *wakeLock) : mWakeLock(wakeLock) {} - virtual ~PMDeathRecipient() {} - - // IBinder::DeathRecipient - virtual void binderDied(const wp &who); - - private: - PMDeathRecipient(const PMDeathRecipient&); - PMDeathRecipient& operator= (const PMDeathRecipient&); - - JWakeLock *mWakeLock; - }; - - const sp mDeathRecipient; - - void clearPowerManager(); + void clearJavaWakeLock(); DISALLOW_EVIL_CONSTRUCTORS(JWakeLock); }; diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 080d923b94..5da6e2496a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -209,7 +209,8 @@ private: //////////////////////////////////////////////////////////////////////////////// -NuPlayer2::NuPlayer2(pid_t pid, uid_t uid, const sp &mediaClock) +NuPlayer2::NuPlayer2( + pid_t pid, uid_t uid, const sp &mediaClock, const sp &context) : mPID(pid), mUID(uid), mMediaClock(mediaClock), @@ -240,7 +241,8 @@ NuPlayer2::NuPlayer2(pid_t pid, uid_t uid, const sp &mediaClock) mVideoDecoderError(false), mPaused(false), mPausedByClient(true), - mPausedForBuffering(false) { + mPausedForBuffering(false), + mContext(context) { CHECK(mediaClock != NULL); clearFlushComplete(); } @@ -1738,7 +1740,7 @@ void NuPlayer2::onStart(bool play) { sp notify = new AMessage(kWhatRendererNotify, this); ++mRendererGeneration; notify->setInt32("generation", mRendererGeneration); - mRenderer = new Renderer(mAudioSink, mMediaClock, notify, flags); + mRenderer = new Renderer(mAudioSink, mMediaClock, notify, mContext, flags); mRendererLooper = new ALooper; mRendererLooper->setName("NuPlayer2Renderer"); mRendererLooper->start(false, true, ANDROID_PRIORITY_AUDIO); diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index fdc128f9c9..798c725a6e 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -22,6 +22,7 @@ #include #include +#include #include "mediaplayer2.pb.h" @@ -42,7 +43,8 @@ class MetaData; struct NuPlayer2Driver; struct NuPlayer2 : public AHandler { - explicit NuPlayer2(pid_t pid, uid_t uid, const sp &mediaClock); + explicit NuPlayer2(pid_t pid, uid_t uid, + const sp &mediaClock, const sp &context); void setDriver(const wp &driver); @@ -272,6 +274,9 @@ private: // Pause state as requested by source (internally) due to buffering bool mPausedForBuffering; + // Passed from JAVA + const sp mContext; + inline const sp &getDecoder(bool audio) { return audio ? mAudioDecoder : mVideoDecoder; } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 3c11b17fc6..56e9471f2a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -108,7 +108,7 @@ static const char *kPlayerRebufferingCount = "android.media.mediaplayer.rebuffer static const char *kPlayerRebufferingAtExit = "android.media.mediaplayer.rebufferExit"; -NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid) +NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp &context) : mState(STATE_IDLE), mAsyncResult(UNKNOWN_ERROR), mSrcId(0), @@ -123,7 +123,7 @@ NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid) mLooper(new ALooper), mNuPlayer2Looper(new ALooper), mMediaClock(new MediaClock), - mPlayer(new NuPlayer2(pid, uid, mMediaClock)), + mPlayer(new NuPlayer2(pid, uid, mMediaClock, context)), mPlayerFlags(0), mAnalyticsItem(NULL), mClientUid(uid), diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index fe17d03c25..0ec3a4b23b 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -18,6 +18,7 @@ #include #include +#include namespace android { @@ -26,7 +27,7 @@ struct MediaClock; struct NuPlayer2; struct NuPlayer2Driver : public MediaPlayer2Interface { - explicit NuPlayer2Driver(pid_t pid, uid_t uid); + explicit NuPlayer2Driver(pid_t pid, uid_t uid, const sp &context); virtual status_t initCheck() override; diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp index 2881fd4d39..3be7e36e2c 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp @@ -106,6 +106,7 @@ NuPlayer2::Renderer::Renderer( const sp &sink, const sp &mediaClock, const sp ¬ify, + const sp &context, uint32_t flags) : mAudioSink(sink), mUseVirtualAudioSink(false), @@ -147,7 +148,7 @@ NuPlayer2::Renderer::Renderer( mTotalBuffersQueued(0), mLastAudioBufferDrained(0), mUseAudioCallback(false), - mWakeLock(new JWakeLock()) { + mWakeLock(new JWakeLock(context)) { CHECK(mediaClock != NULL); mMediaClock->setPlaybackRate(mPlaybackSettings.mSpeed); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h index 484d9b73d8..d065dee11b 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h @@ -20,6 +20,7 @@ #include #include +#include #include "NuPlayer2.h" @@ -38,6 +39,7 @@ struct NuPlayer2::Renderer : public AHandler { Renderer(const sp &sink, const sp &mediaClock, const sp ¬ify, + const sp &context, uint32_t flags = 0); static size_t AudioSinkCallback( -- GitLab From 0b83feb0680af98e35fb2f54ad854889759b0a9d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 20 Dec 2018 17:44:13 -0800 Subject: [PATCH 0691/1530] adding apex for swcodecs bug: 111407413 Change-Id: I37fb496042cec061eb1244399a70e92ef14311ef --- apex/Android.bp | 17 ++++++++ apex/com.android.media.swcodec.avbpubkey | Bin 0 -> 1032 bytes apex/com.android.media.swcodec.pem | 51 ++++++++++++++++++++++ apex/manifest_codec.json | 4 ++ services/mediacodec/registrant/Android.bp | 1 + 5 files changed, 73 insertions(+) create mode 100644 apex/com.android.media.swcodec.avbpubkey create mode 100644 apex/com.android.media.swcodec.pem create mode 100644 apex/manifest_codec.json diff --git a/apex/Android.bp b/apex/Android.bp index 05cc2c5aa6..604b26850b 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -31,8 +31,25 @@ apex { key: "com.android.media.key", } +apex { + name: "com.android.media.swcodec", + compile_multilib: "32", + manifest: "manifest_codec.json", + native_shared_libs: [ + "libmedia_codecserviceregistrant", + ], + use_vendor: true, + key: "com.android.media.swcodec.key", +} + apex_key { name: "com.android.media.key", public_key: "com.android.media.avbpubkey", private_key: "com.android.media.pem", } + +apex_key { + name: "com.android.media.swcodec.key", + public_key: "com.android.media.swcodec.avbpubkey", + private_key: "com.android.media.swcodec.pem", +} diff --git a/apex/com.android.media.swcodec.avbpubkey b/apex/com.android.media.swcodec.avbpubkey new file mode 100644 index 0000000000000000000000000000000000000000..045972306d1ec3e68eecf09c41e577cf10ed9420 GIT binary patch literal 1032 zcmV+j1o!&@01yDjG#-V#9;G-z4o9B~F~MbPAsLRv?mh<}9xu*67cYL8hAb*haINJy z@S#~t!$raeFdo4>;H-ZeG#(2FzV=@?Dh(oUeObuGZMq^mwW|aWD#CiMa<3@ZZRg;Vx*^g4jOL_S^QMMLdwNa^7xM((15vWO}L z6{eS%6$4oh>OOiSNo$O#f&^uU%M;W5ODZ!F!~{Eir{dN>iw9%7QUYcmFuD}ig^JduUDHBrNEGGpLV3opued5hd{?F5No zwpg659)#e3#=-u@{T%A-#o-6elmXKFohE=N_B*iE*9_ZIz8ZNQ(ZoG*)YrtJxIUTM0Qtb`5$XoYleG;xZ`A z38F%67kME_>^q$(SAsG7J^EA(RqCWtlp28fpK82jvzHI@FP?vu%n5qVlZ}PuQ!Vqu zD`;HDc9hu&S|>m<(IZpUH^Z`nxhmg}v4L4iF|BaiIL!!Fy1N$sui33~&q$%#Ry{vx zJtQ+w>_=}=082=7Iks?^c(>L(CQ~lL$pb5Z>EtQM>6NT7g1DElAdoXo^ObHP$1RcTwow z2!{^VA7|j!*Upqbt3#9_5|xB{ata%e9}2WWJ9FJ(oj9L+(Ig~0F#wmKMLZD$aFVd~ Co%B%v literal 0 HcmV?d00001 diff --git a/apex/com.android.media.swcodec.pem b/apex/com.android.media.swcodec.pem new file mode 100644 index 0000000000..e379cd3a87 --- /dev/null +++ b/apex/com.android.media.swcodec.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKgIBAAKCAgEAvB6lOEEOR58KMcFlayEZjsXuPgcfHi/OPxcvfpiGLCpOcK3l +OPChWUvDRcIHMB7BO+Csfxs0HgsHvvZfNyoNIm99WcjFbboiO7WrBBArIszPr14X +cfY2NxzT9LBj+EqAnbdL+4OQW1/npLHNE0qtDcxL71ipbjSuKNb58E9qGL0KwvkS +fwwueWj++bg/iz7dq0mz7iKpnxYscNm4RhJjqcG2Usmg4Ejhr8h5UmOUmTJbObC/ +vzClXQqeeuzS8NTtGVgQ/CI9gC2WN9upq2p/2T7P4U3o3CWvBytUoLKR0UyC83ey +S8XJgOa42uWR3T/eJOI1ZS4H6Srg1o2XC8Yb8EprFI/NM6/+/5DX/FgDimsslAP6 +Qq4+pSte9v/FjWGqy7QBQaefFRGRuS63xHcSZhXC9J2CFdnxo8+65QT8r4yfQEei +Ax/0Q94yB7VIL1pIJxHEonKjtd3iKdFEKQzADJ3edsmtHybERdJKCxcm9QIrDCsC +4YVT9nX0OoC9RD5d5EVD7W5I6eEnRu6igrKIKgUVppeYFQNZD+o+eiNJa4yoggRl +h8sT0/xLKjMRxAQ7fafi1j+LB2O7UgJmIDDAnidq8Aoz7h3pNi139rNWrjNfwhus +nMcZvL9dTVv26JWeESHP/zAfAX7j1rfkDwR02ocRLquwEUs1+UHA27Wi1bMCAwEA +AQKCAgAO3PT83tb7/arWh4s1zaWxTB4otHNW9Tv8bB6KiA6Bys3rxTGJMCnvXjcN +eekLekKWMoguer3BaemwwtJ/D0l+YQSsZVqD8uLliNL8PTLLSxdVqb98d5GNBTAR +8yXS5kAHNgZA1wI+1fL9ZjbnwUyu/Gc7f+vTE0J3Y5TX0c52KemBwiAd/Z5mZU9P +96i9nbfx4p7ev8pbEWttdyZCEw3gybdYDyowzlFWjCZZxhhlij7+7eIYfwVxtncT +C0cXVBtvly+wXBwz1mY5/5cGPiHfzkCqcndlfWy4ykmjcLhoqvzls51Ys0Xac2BD +m0PNEVDB5UWGuv5RA9xD12gJvBtU3D7ggMw6C5RcXJT+jSYmSFtD2klWi+It8A/N +Hv42soKskt6JqYAWE1cvJ3PEqH9ASEJNq2R0Z/PmuM000UJyzU9KId3SNwjXA1Xc +Kn9hRga4uf9elHTjkTDt79/8+Xv7hjer9sF/S/np7g04rUjIWkuFkC/7NK2tQSh1 +mljV1sD9SF4DPfVK75LwJJaQUlI7TtGd5KJ7FzZwvb+w8ODrpW3hkt6FcI6KwE/a +QT1T2Z9DknXJSYNdWGrj6vMHsYMiyz8IdAHSCrOB6eXCQxpitbn6W71Raw7f9UaZ +VDK5AhTU493hkGj1no4cJwecXInMigg/c4ywk2Ibh6IV8O0nkQKCAQEA2+dQQ0IF +vvVgmQ2WxHBD7M2mbOyf43YBY6Ka6oPBNGPVpZE8X8LoTavQLV+SgCkH8T6gY7XS +5L4Ze0JFxfua0o1rm6+L2XrOx5F/A2Y40YcPclEik5h1woSwH/J1iHGiEhY8Nqeu +9GCvjQojkgXx/Rn3Nz+lpvZ329O3H85RWWGF0l60RwLOkig0ZwUb619t8affmGIl +sxdDv2nfy7OtJX8iGDua7Kf64dvVWQKKtACWkARrlkcWX3uoESxkpSDxue+z7ndH +o7uHLfM8Tx+Rn+QvYWuRW5TPLbEDMbIYrX65ISt2r/T7v/04XdAC8YpCQRytlqPI +fpDm15htyHBizQKCAQEA2v+5otRoY56JduISaVGlsJ/IbvKzDAIyWmM14EpRCR5E +lu+MpTcRAiFQAbHGXr1tMlTFdVFD090WAzIKaKtADFVLXobIHeGspnRCq5OpVp9W +RvLtVwLxflHAc2yN9/LNtnBqHUgt+S01LBPElybdGHQRTtqAKXhkp31sM21O34Go +Pri/IxgupWxykMaW44Kig1Cr5DKvc8cwUsGuyDdJm8oBQeNPTMWqSnXtqoTWSaYg +2kxiMTFokrkSXgufb8wng6OXt/QelywrhG3hAsldPO3GdKidDSxhWZSgpUXXFdAX +y4GO0IcRJBF/WJtYTYtR+l84nQA2/1Ye4ujFlT0afwKCAQEAmXrXpSm2cvI2CnzW +hqJIdkWOa6W3bn1VOOIrt5Rfy54GZnl4pumVU2igcpqq2HJKzdDFBvLHj8kyZbn6 +ktUp2NzFhzK9q/uvyNA+0vOMoojeeg4w0MzvG+WaO6Hw8FtHH9KPEiJ01LGKtSin +bOpjXCC8T75HcsHBJBefTz6jvnt3eD2LG6jU3mPbNy/0rZG8XZaqU2PlJhsNuNI/ +VaBBL9OMy1cGqTgQvYS+YlKI1ls2uqurH4bcEaZvxhSy5iGZNQodDkoIITnofmSu +6haBgBQ2EYuPN1kkRKKwNQY1fRneQk1gmCynbPdiWO+urkCuP12xtlr3u4aM51rG +/Meb3QKCAQEA12SxZm9XhLOHLIBJ74A4YLGm50iZxXPbpn7xnHo7naZBe9p8EHtK +pTeygxggrUnOPrSVyT92YMiQP/BVwIC+a+LwUDZsWMd/ke/DKxH+eY4Zw4pm2S+x +6bXqfRwFvhr3LTr/g3FcljlalNGUh73Xs5dk9pN9fkxFY16+rw4Rh0709Uur4o6E +QnuZar+H5Ji10kXj6nvXiR4ebybEC3QlV66k8fLqKe44AShf61jfkmxs34hFA3E/ +EyAn6ouv8rtvGdArBuh5teHho0yXBLCcnbKXgGHepfhCf2LpZeR9GZ0j6iqxFnPh +7gGvqKyReyNOK9y/x9tQPG6tzit3OcNxbQKCAQEAuDheDOSkXPopXMvLAKOHBIdZ +rbQ7oFTDHt59gbucM7U8/l2YtaxuKOQFBLnzQa/amIkkrtklYwOsz8E24d7Nm39w +ykLHwX0XrmjAm6M4XKmDv66a+kSnSV6LEbKZdjvXP02DV+tGeZ5VsnNvJDquxMsD +fvRTspB8j8CpU96szekxl/tCRhqbdw/4kVTSj5BF++OaRRcJrAyj1B2qf1ynAZE1 +gUvVPkEYa914zcrxg9XIXT4M7yqB7i8KJegOtOtcWjJ7uTiP+638AvygJLMJnSrV ++HjFZWG6P9btZmLHSEBRvwGOAilp0qejXo866l0fmnlGy7ehKz8u3PzvnlPYjQ== +-----END RSA PRIVATE KEY----- diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json new file mode 100644 index 0000000000..e2bceec65f --- /dev/null +++ b/apex/manifest_codec.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.media.swcodec", + "version": 1 +} diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 653317b4dc..0c6a9bc010 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -1,5 +1,6 @@ cc_library_shared { name: "libmedia_codecserviceregistrant", + vendor_available: true, srcs: [ "CodecServiceRegistrant.cpp", ], -- GitLab From 89cefe5cd557151261ac23a4e4d1c611350966d5 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 9 Jan 2019 18:49:31 -0800 Subject: [PATCH 0692/1530] NdkWrapper: return NULL when cryptoInfo is NULL Test: cts Bug: 122073361 Change-Id: I3192f1935760e4e1d1038d7b4cb328975a9a63e5 --- media/libmedia/NdkWrapper.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp index 156991eb13..6dbc9b8874 100644 --- a/media/libmedia/NdkWrapper.cpp +++ b/media/libmedia/NdkWrapper.cpp @@ -1249,7 +1249,11 @@ sp AMediaExtractorWrapper::getSampleCryptoInfo() { if (mAMediaExtractor == NULL) { return NULL; } - return new AMediaCodecCryptoInfoWrapper(AMediaExtractor_getSampleCryptoInfo(mAMediaExtractor)); + AMediaCodecCryptoInfo *cryptoInfo = AMediaExtractor_getSampleCryptoInfo(mAMediaExtractor); + if (cryptoInfo == NULL) { + return NULL; + } + return new AMediaCodecCryptoInfoWrapper(cryptoInfo); } AMediaDataSourceWrapper::AMediaDataSourceWrapper(const sp &dataSource) -- GitLab From b8684f3a673dff814d68b2f695991e554718fd8b Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Wed, 12 Dec 2018 08:41:31 -0800 Subject: [PATCH 0693/1530] Add additional error conditions to MediaDrm New codes are being added to handle resource contention, lost session state, frame size too large and insufficient security level for decryption. Also cleans up inconsistent use of tamper detected error where invalid state error should have been used. bug:111504510 bug:111505796 test:cts and gts media test cases Change-Id: I28ca04cdc8ce64047d189fcf4d59bab24208e1a7 --- drm/libmediadrm/CryptoHal.cpp | 71 ++++-- drm/libmediadrm/DrmHal.cpp | 230 ++++++++++++------ .../plugins/clearkey/hidl/CryptoPlugin.cpp | 65 +++-- .../plugins/clearkey/hidl/DrmPlugin.cpp | 90 +++++-- .../plugins/clearkey/hidl/Session.cpp | 11 +- .../hidl/include/ClearKeyDrmProperties.h | 5 + .../clearkey/hidl/include/ClearKeyTypes.h | 2 +- .../clearkey/hidl/include/CryptoPlugin.h | 33 ++- .../plugins/clearkey/hidl/include/DrmPlugin.h | 104 ++++++-- .../plugins/clearkey/hidl/include/Session.h | 17 +- .../clearkey/hidl/include/TypeConvert.h | 11 + media/libmedia/include/media/CryptoHal.h | 2 + media/libmedia/include/media/DrmHal.h | 8 +- .../include/media/stagefright/MediaErrors.h | 7 +- 14 files changed, 474 insertions(+), 182 deletions(-) diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp index 3035c5a250..4dda5d7a32 100644 --- a/drm/libmediadrm/CryptoHal.cpp +++ b/drm/libmediadrm/CryptoHal.cpp @@ -30,16 +30,16 @@ #include #include +using drm::V1_0::BufferType; +using drm::V1_0::DestinationBuffer; +using drm::V1_0::ICryptoFactory; +using drm::V1_0::ICryptoPlugin; +using drm::V1_0::Mode; +using drm::V1_0::Pattern; +using drm::V1_0::SharedBuffer; +using drm::V1_0::Status; +using drm::V1_0::SubSample; -using ::android::hardware::drm::V1_0::BufferType; -using ::android::hardware::drm::V1_0::DestinationBuffer; -using ::android::hardware::drm::V1_0::ICryptoFactory; -using ::android::hardware::drm::V1_0::ICryptoPlugin; -using ::android::hardware::drm::V1_0::Mode; -using ::android::hardware::drm::V1_0::Pattern; -using ::android::hardware::drm::V1_0::SharedBuffer; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; using ::android::hardware::hidl_array; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_memory; @@ -50,6 +50,7 @@ using ::android::hardware::Void; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::sp; +typedef drm::V1_2::Status Status_V1_2; namespace android { @@ -76,6 +77,18 @@ static status_t toStatusT(Status status) { } } +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; @@ -196,6 +209,9 @@ status_t CryptoHal::createPlugin(const uint8_t uuid[16], const void *data, for (size_t i = 0; i < mFactories.size(); i++) { if (mFactories[i]->isCryptoSchemeSupported(uuid)) { mPlugin = makeCryptoPlugin(mFactories[i], uuid, data, size); + if (mPlugin != NULL) { + mPluginV1_2 = drm::V1_2::ICryptoPlugin::castFrom(mPlugin); + } } } @@ -216,6 +232,7 @@ status_t CryptoHal::destroyPlugin() { } mPlugin.clear(); + mPluginV1_2.clear(); return OK; } @@ -389,21 +406,33 @@ ssize_t CryptoHal::decrypt(const uint8_t keyId[16], const uint8_t iv[16], status_t err = UNKNOWN_ERROR; uint32_t bytesWritten = 0; - Return hResult = mPlugin->decrypt(secure, toHidlArray16(keyId), toHidlArray16(iv), hMode, - hPattern, hSubSamples, hSource, offset, hDestination, - [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) { - if (status == Status::OK) { - bytesWritten = hBytesWritten; - *errorDetailMsg = toString8(hDetailedError); - } - err = toStatusT(status); - } - ); + Return hResult; - if (!hResult.isOk()) { - err = DEAD_OBJECT; + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv), + hMode, hPattern, hSubSamples, hSource, offset, hDestination, + [&](Status_V1_2 status, uint32_t hBytesWritten, hidl_string hDetailedError) { + if (status == Status_V1_2::OK) { + bytesWritten = hBytesWritten; + *errorDetailMsg = toString8(hDetailedError); + } + err = toStatusT_1_2(status); + } + ); + } else { + hResult = mPlugin->decrypt(secure, toHidlArray16(keyId), toHidlArray16(iv), + hMode, hPattern, hSubSamples, hSource, offset, hDestination, + [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) { + if (status == Status::OK) { + bytesWritten = hBytesWritten; + *errorDetailMsg = toString8(hDetailedError); + } + err = toStatusT(status); + } + ); } + err = hResult.isOk() ? err : DEAD_OBJECT; if (err == OK) { return bytesWritten; } diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 66c509f647..fc847ff72f 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -41,6 +41,7 @@ using drm::V1_0::KeyedVector; using drm::V1_0::KeyStatusType; +using drm::V1_0::KeyRequestType; using drm::V1_0::KeyType; using drm::V1_0::KeyValue; using drm::V1_0::SecureStop; @@ -60,6 +61,9 @@ using ::android::hidl::manager::V1_0::IServiceManager; using ::android::os::PersistableBundle; using ::android::sp; +typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; +typedef drm::V1_2::Status Status_V1_2; + namespace { // This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant @@ -239,7 +243,7 @@ static status_t toStatusT(Status status) { return ERROR_DRM_CANNOT_HANDLE; break; case Status::ERROR_DRM_INVALID_STATE: - return ERROR_DRM_TAMPER_DETECTED; + return ERROR_DRM_INVALID_STATE; break; case Status::BAD_VALUE: return BAD_VALUE; @@ -260,6 +264,19 @@ static status_t toStatusT(Status status) { } } +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; @@ -319,8 +336,11 @@ void DrmHal::cleanup() { setListener(NULL); mInitCheck = NO_INIT; - - if (mPlugin != NULL) { + if (mPluginV1_2 != NULL) { + if (!mPluginV1_2->setListener(NULL).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } else if (mPlugin != NULL) { if (!mPlugin->setListener(NULL).isOk()) { mInitCheck = DEAD_OBJECT; } @@ -532,6 +552,22 @@ Return DrmHal::sendKeysChange(const hidl_vec& sessionId, return Void(); } +Return DrmHal::sendSessionLostState( + const hidl_vec& sessionId) { + + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Parcel obj; + writeByteArray(obj, sessionId); + Mutex::Autolock lock(mNotifyLock); + listener->notify(DrmPlugin::kDrmPluginEventSessionLostState, 0, &obj); + } + return Void(); +} + bool DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { Mutex::Autolock autoLock(mLock); @@ -568,16 +604,22 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], if (mPlugin == NULL) { mInitCheck = ERROR_UNSUPPORTED; } else { - if (!mPlugin->setListener(this).isOk()) { - mPlugin = NULL; - mPluginV1_1 = NULL; - mPluginV1_2 = NULL; + mInitCheck = OK; + if (mPluginV1_2 != NULL) { + if (!mPluginV1_2->setListener(this).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } else if (!mPlugin->setListener(this).isOk()) { mInitCheck = DEAD_OBJECT; - } else { - mInitCheck = OK; + } + if (mInitCheck != OK) { + mPlugin.clear(); + mPluginV1_1.clear(); + mPluginV1_2.clear(); } } + return mInitCheck; } @@ -694,6 +736,39 @@ status_t DrmHal::closeSession(Vector const &sessionId) { return DEAD_OBJECT; } +static DrmPlugin::KeyRequestType toKeyRequestType( + KeyRequestType keyRequestType) { + switch (keyRequestType) { + case KeyRequestType::INITIAL: + return DrmPlugin::kKeyRequestType_Initial; + break; + case KeyRequestType::RENEWAL: + return DrmPlugin::kKeyRequestType_Renewal; + break; + case KeyRequestType::RELEASE: + return DrmPlugin::kKeyRequestType_Release; + break; + default: + return DrmPlugin::kKeyRequestType_Unknown; + break; + } +} + +static DrmPlugin::KeyRequestType toKeyRequestType_1_1( + KeyRequestType_V1_1 keyRequestType) { + switch (keyRequestType) { + case KeyRequestType_V1_1::NONE: + return DrmPlugin::kKeyRequestType_None; + break; + case KeyRequestType_V1_1::UPDATE: + return DrmPlugin::kKeyRequestType_Update; + break; + default: + return toKeyRequestType(static_cast(keyRequestType)); + break; + } +} + status_t DrmHal::getKeyRequest(Vector const &sessionId, Vector const &initData, String8 const &mimeType, DrmPlugin::KeyType keyType, KeyedVector const &sessionId, ::KeyedVector hOptionalParameters = toHidlKeyedVector(optionalParameters); status_t err = UNKNOWN_ERROR; + Return hResult; - if (mPluginV1_1 != NULL) { - Return hResult = - mPluginV1_1->getKeyRequest_1_1( + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->getKeyRequest_1_2( + toHidlVec(sessionId), toHidlVec(initData), + toHidlString(mimeType), hKeyType, hOptionalParameters, + [&](Status_V1_2 status, const hidl_vec& hRequest, + KeyRequestType_V1_1 hKeyRequestType, + const hidl_string& hDefaultUrl) { + if (status == Status_V1_2::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); + } + err = toStatusT_1_2(status); + }); + } else if (mPluginV1_1 != NULL) { + hResult = mPluginV1_1->getKeyRequest_1_1( toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType, hOptionalParameters, [&](Status status, const hidl_vec& hRequest, - drm::V1_1::KeyRequestType hKeyRequestType, - const hidl_string& hDefaultUrl) { - - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - - switch (hKeyRequestType) { - case drm::V1_1::KeyRequestType::INITIAL: - *keyRequestType = DrmPlugin::kKeyRequestType_Initial; - break; - case drm::V1_1::KeyRequestType::RENEWAL: - *keyRequestType = DrmPlugin::kKeyRequestType_Renewal; - break; - case drm::V1_1::KeyRequestType::RELEASE: - *keyRequestType = DrmPlugin::kKeyRequestType_Release; - break; - case drm::V1_1::KeyRequestType::NONE: - *keyRequestType = DrmPlugin::kKeyRequestType_None; - break; - case drm::V1_1::KeyRequestType::UPDATE: - *keyRequestType = DrmPlugin::kKeyRequestType_Update; - break; - default: - *keyRequestType = DrmPlugin::kKeyRequestType_Unknown; - break; - } - err = toStatusT(status); - } - }); - return hResult.isOk() ? err : DEAD_OBJECT; - } - - Return hResult = mPlugin->getKeyRequest(toHidlVec(sessionId), - toHidlVec(initData), toHidlString(mimeType), hKeyType, hOptionalParameters, - [&](Status status, const hidl_vec& hRequest, - drm::V1_0::KeyRequestType hKeyRequestType, - const hidl_string& hDefaultUrl) { - - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - - switch (hKeyRequestType) { - case drm::V1_0::KeyRequestType::INITIAL: - *keyRequestType = DrmPlugin::kKeyRequestType_Initial; - break; - case drm::V1_0::KeyRequestType::RENEWAL: - *keyRequestType = DrmPlugin::kKeyRequestType_Renewal; - break; - case drm::V1_0::KeyRequestType::RELEASE: - *keyRequestType = DrmPlugin::kKeyRequestType_Release; - break; - default: - *keyRequestType = DrmPlugin::kKeyRequestType_Unknown; - break; + KeyRequestType_V1_1 hKeyRequestType, + const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); } err = toStatusT(status); - } - }); + }); + } else { + hResult = mPlugin->getKeyRequest( + toHidlVec(sessionId), toHidlVec(initData), + toHidlString(mimeType), hKeyType, hOptionalParameters, + [&](Status status, const hidl_vec& hRequest, + KeyRequestType hKeyRequestType, + const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType(hKeyRequestType); + } + err = toStatusT(status); + }); + } err = hResult.isOk() ? err : DEAD_OBJECT; keyRequestTimer.SetAttribute(err); @@ -868,18 +921,33 @@ status_t DrmHal::getProvisionRequest(String8 const &certType, INIT_CHECK(); status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->getProvisionRequest( - toHidlString(certType), toHidlString(certAuthority), - [&](Status status, const hidl_vec& hRequest, - const hidl_string& hDefaultUrl) { - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); + Return hResult; + + if (mPluginV1_2 != NULL) { + Return hResult = mPluginV1_2->getProvisionRequest_1_2( + toHidlString(certType), toHidlString(certAuthority), + [&](Status_V1_2 status, const hidl_vec& hRequest, + const hidl_string& hDefaultUrl) { + if (status == Status_V1_2::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + } + err = toStatusT_1_2(status); } - err = toStatusT(status); - } - ); + ); + } else { + Return hResult = mPlugin->getProvisionRequest( + toHidlString(certType), toHidlString(certAuthority), + [&](Status status, const hidl_vec& hRequest, + const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + } + err = toStatusT(status); + } + ); + } err = hResult.isOk() ? err : DEAD_OBJECT; mMetrics.mGetProvisionRequestCounter.Increment(err); diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp index a488f86e1d..fc0cceb66e 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp @@ -42,10 +42,42 @@ Return CryptoPlugin::setSharedBufferBase( return Void(); } +Return CryptoPlugin::decrypt( + bool secure, + const hidl_array& keyId, + const hidl_array& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_cb _hidl_cb) { + + Status status = Status::ERROR_DRM_UNKNOWN; + hidl_string detailedError; + uint32_t bytesWritten = 0; + + Return hResult = decrypt_1_2( + secure, keyId, iv, mode, pattern, subSamples, source, offset, destination, + [&](Status_V1_2 hStatus, uint32_t hBytesWritten, hidl_string hDetailedError) { + status = toStatus_1_0(hStatus); + if (status == Status::OK) { + bytesWritten = hBytesWritten; + detailedError = hDetailedError; + } + } + ); + + status = hResult.isOk() ? status : Status::ERROR_DRM_CANNOT_HANDLE; + _hidl_cb(status, bytesWritten, detailedError); + return Void(); +} + // Returns negative values for error code and positive values for the size of // decrypted data. In theory, the output size can be larger than the input // size, but in practice this will never happen for AES-CTR. -Return CryptoPlugin::decrypt( +Return CryptoPlugin::decrypt_1_2( bool secure, const hidl_array& keyId, const hidl_array& iv, @@ -55,17 +87,17 @@ Return CryptoPlugin::decrypt( const SharedBuffer& source, uint64_t offset, const DestinationBuffer& destination, - decrypt_cb _hidl_cb) { + decrypt_1_2_cb _hidl_cb) { UNUSED(pattern); if (secure) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "Secure decryption is not supported with ClearKey."); return Void(); } if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "source decrypt buffer base not set"); return Void(); } @@ -73,7 +105,7 @@ Return CryptoPlugin::decrypt( if (destination.type == BufferType::SHARED_MEMORY) { const SharedBuffer& dest = destination.nonsecureMemory; if (mSharedBufferMap.find(dest.bufferId) == mSharedBufferMap.end()) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination decrypt buffer base not set"); return Void(); } @@ -81,12 +113,12 @@ Return CryptoPlugin::decrypt( sp sourceBase = mSharedBufferMap[source.bufferId]; if (sourceBase == nullptr) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr"); + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr"); return Void(); } if (source.offset + offset + source.size > sourceBase->getSize()) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); return Void(); } @@ -98,12 +130,12 @@ Return CryptoPlugin::decrypt( const SharedBuffer& destBuffer = destination.nonsecureMemory; sp destBase = mSharedBufferMap[destBuffer.bufferId]; if (destBase == nullptr) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr"); + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr"); return Void(); } if (destBuffer.offset + destBuffer.size > destBase->getSize()) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); return Void(); } destPtr = static_cast(base + destination.nonsecureMemory.offset); @@ -128,7 +160,7 @@ Return CryptoPlugin::decrypt( if (mode == Mode::UNENCRYPTED) { if (haveEncryptedSubsamples) { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "Encrypted subsamples found in allegedly unencrypted data."); return Void(); } @@ -144,22 +176,21 @@ Return CryptoPlugin::decrypt( } } - _hidl_cb(Status::OK, static_cast(offset), ""); + _hidl_cb(Status_V1_2::OK, static_cast(offset), ""); return Void(); } else if (mode == Mode::AES_CTR) { size_t bytesDecrypted; - Status res = mSession->decrypt(keyId.data(), iv.data(), srcPtr, + Status_V1_2 res = mSession->decrypt(keyId.data(), iv.data(), srcPtr, static_cast(destPtr), toVector(subSamples), &bytesDecrypted); - if (res == Status::OK) { - _hidl_cb(Status::OK, static_cast(bytesDecrypted), ""); + if (res == Status_V1_2::OK) { + _hidl_cb(Status_V1_2::OK, static_cast(bytesDecrypted), ""); return Void(); } else { - _hidl_cb(Status::ERROR_DRM_DECRYPT, static_cast(res), - "Decryption Error"); + _hidl_cb(res, 0, "Decryption Error"); return Void(); } } else { - _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "Selected encryption mode is not supported by the ClearKey DRM Plugin."); return Void(); } diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp index 7184b538d5..badb99e402 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -61,15 +61,23 @@ namespace drm { namespace V1_2 { namespace clearkey { -using ::android::hardware::drm::V1_2::KeySetId; -using ::android::hardware::drm::V1_2::OfflineLicenseState; +KeyRequestType toKeyRequestType_V1_0(KeyRequestType_V1_1 keyRequestType) { + switch (keyRequestType) { + case KeyRequestType_V1_1::NONE: + case KeyRequestType_V1_1::UPDATE: + return KeyRequestType::UNKNOWN; + default: + return static_cast(keyRequestType); + } +} DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) : mSessionLibrary(sessionLibrary), mOpenSessionOkCount(0), mCloseSessionOkCount(0), mCloseSessionNotOpenedCount(0), - mNextSecureStopId(kSecureStopIdStart) { + mNextSecureStopId(kSecureStopIdStart), + mMockError(Status_V1_2::OK) { mPlayPolicy.clear(); initProperties(); mSecureStops.clear(); @@ -84,6 +92,7 @@ void DrmPlugin::initProperties() { mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue; mStringProperties[kAlgorithmsKey] = kAlgorithmsValue; mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue; + mStringProperties[kDrmErrorTestKey] = kDrmErrorTestValue; std::vector valueVector; valueVector.clear(); @@ -112,6 +121,7 @@ void DrmPlugin::installSecureStop(const hidl_vec& sessionId) { Return DrmPlugin::openSession(openSession_cb _hidl_cb) { sp session = mSessionLibrary->createSession(); + processMockError(session); std::vector sessionId = session->sessionId(); Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO); @@ -123,6 +133,7 @@ Return DrmPlugin::openSession(openSession_cb _hidl_cb) { Return DrmPlugin::openSession_1_1(SecurityLevel securityLevel, openSession_1_1_cb _hidl_cb) { sp session = mSessionLibrary->createSession(); + processMockError(session); std::vector sessionId = session->sessionId(); Status status = setSecurityLevel(sessionId, securityLevel); @@ -138,6 +149,10 @@ Return DrmPlugin::closeSession(const hidl_vec& sessionId) { sp session = mSessionLibrary->findSession(toVector(sessionId)); if (session.get()) { + if (session->getMockError() != Status_V1_2::OK) { + sendSessionLostState(sessionId); + return Status::ERROR_DRM_INVALID_STATE; + } mCloseSessionOkCount++; mSessionLibrary->destroySession(session); return Status::OK; @@ -146,13 +161,13 @@ Return DrmPlugin::closeSession(const hidl_vec& sessionId) { return Status::ERROR_DRM_SESSION_NOT_OPENED; } -Status DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, +Status_V1_2 DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, const hidl_vec& initData, const hidl_string& mimeType, KeyType keyType, const hidl_vec& optionalParameters, std::vector *request, - KeyRequestType *keyRequestType, + KeyRequestType_V1_1 *keyRequestType, std::string *defaultUrl) { UNUSED(optionalParameters); @@ -161,18 +176,18 @@ Status DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, // Those tests pass in an empty initData, we use the empty initData to // signal such specific use case. if (keyType == KeyType::OFFLINE && 0 == initData.size()) { - return Status::ERROR_DRM_CANNOT_HANDLE; + return Status_V1_2::ERROR_DRM_CANNOT_HANDLE; } *defaultUrl = ""; - *keyRequestType = KeyRequestType::UNKNOWN; + *keyRequestType = KeyRequestType_V1_1::UNKNOWN; *request = std::vector(); if (scope.size() == 0 || (keyType != KeyType::STREAMING && keyType != KeyType::OFFLINE && keyType != KeyType::RELEASE)) { - return Status::BAD_VALUE; + return Status_V1_2::BAD_VALUE; } const std::vector scopeId = toVector(scope); @@ -181,12 +196,16 @@ Status DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, std::vector sessionId(scopeId.begin(), scopeId.end()); session = mSessionLibrary->findSession(sessionId); if (!session.get()) { - return Status::ERROR_DRM_SESSION_NOT_OPENED; + return Status_V1_2::ERROR_DRM_SESSION_NOT_OPENED; + } else if (session->getMockError() != Status_V1_2::OK) { + return session->getMockError(); } - *keyRequestType = KeyRequestType::INITIAL; + + *keyRequestType = KeyRequestType_V1_1::INITIAL; } - Status status = session->getKeyRequest(initData, mimeType, keyType, request); + Status_V1_2 status = static_cast( + session->getKeyRequest(initData, mimeType, keyType, request)); if (keyType == KeyType::RELEASE) { std::vector keySetId(scopeId.begin(), scopeId.end()); @@ -198,7 +217,7 @@ Status DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, DeviceFiles::kLicenseStateReleasing, emptyResponse)) { ALOGE("Problem releasing offline license"); - return Status::ERROR_DRM_UNKNOWN; + return Status_V1_2::ERROR_DRM_UNKNOWN; } if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) { sp session = mSessionLibrary->createSession(); @@ -209,7 +228,7 @@ Status DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, } else { ALOGE("Offline license not found, nothing to release"); } - *keyRequestType = KeyRequestType::RELEASE; + *keyRequestType = KeyRequestType_V1_1::RELEASE; } return status; } @@ -223,15 +242,15 @@ Return DrmPlugin::getKeyRequest( getKeyRequest_cb _hidl_cb) { UNUSED(optionalParameters); - KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; + KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN; std::string defaultUrl(""); std::vector request; - Status status = getKeyRequestCommon( + Status_V1_2 status = getKeyRequestCommon( scope, initData, mimeType, keyType, optionalParameters, &request, &keyRequestType, &defaultUrl); - _hidl_cb(status, toHidlVec(request), - static_cast(keyRequestType), + _hidl_cb(toStatus_1_0(status), toHidlVec(request), + toKeyRequestType_V1_0(keyRequestType), hidl_string(defaultUrl)); return Void(); } @@ -245,10 +264,31 @@ Return DrmPlugin::getKeyRequest_1_1( getKeyRequest_1_1_cb _hidl_cb) { UNUSED(optionalParameters); - KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; + KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN; std::string defaultUrl(""); std::vector request; - Status status = getKeyRequestCommon( + Status_V1_2 status = getKeyRequestCommon( + scope, initData, mimeType, keyType, optionalParameters, + &request, &keyRequestType, &defaultUrl); + + _hidl_cb(toStatus_1_0(status), toHidlVec(request), + keyRequestType, hidl_string(defaultUrl)); + return Void(); +} + +Return DrmPlugin::getKeyRequest_1_2( + const hidl_vec& scope, + const hidl_vec& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec& optionalParameters, + getKeyRequest_1_2_cb _hidl_cb) { + UNUSED(optionalParameters); + + KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN; + std::string defaultUrl(""); + std::vector request; + Status_V1_2 status = getKeyRequestCommon( scope, initData, mimeType, keyType, optionalParameters, &request, &keyRequestType, &defaultUrl); @@ -434,6 +474,8 @@ Return DrmPlugin::getPropertyString( value = mStringProperties[kAlgorithmsKey]; } else if (name == kListenerTestSupportKey) { value = mStringProperties[kListenerTestSupportKey]; + } else if (name == kDrmErrorTestKey) { + value = mStringProperties[kDrmErrorTestKey]; } else { ALOGE("App requested unknown string property %s", name.c_str()); _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, ""); @@ -478,6 +520,16 @@ Return DrmPlugin::setPropertyString( return Status::BAD_VALUE; } + if (name == kDrmErrorTestKey) { + if (value == kResourceContentionValue) { + mMockError = Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION; + } else if (value == kLostStateValue) { + mMockError = Status_V1_2::ERROR_DRM_SESSION_LOST_STATE; + } else if (value == kFrameTooLargeValue) { + mMockError = Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE; + } + } + mStringProperties[key] = std::string(value.c_str()); return Status::OK; } diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp index f4c49b90aa..a9d7016075 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp @@ -68,25 +68,30 @@ Status Session::provideKeyResponse(const std::vector& response) { } } -Status Session::decrypt( +Status_V1_2 Session::decrypt( const KeyId keyId, const Iv iv, const uint8_t* srcPtr, uint8_t* destPtr, const std::vector subSamples, size_t* bytesDecryptedOut) { Mutex::Autolock lock(mMapLock); + if (getMockError() != Status_V1_2::OK) { + return getMockError(); + } + std::vector keyIdVector; keyIdVector.clear(); keyIdVector.insert(keyIdVector.end(), keyId, keyId + kBlockSize); std::map, std::vector >::iterator itr; itr = mKeyMap.find(keyIdVector); if (itr == mKeyMap.end()) { - return Status::ERROR_DRM_NO_LICENSE; + return Status_V1_2::ERROR_DRM_NO_LICENSE; } AesCtrDecryptor decryptor; - return decryptor.decrypt( + Status status = decryptor.decrypt( itr->second /*key*/, iv, srcPtr, destPtr, subSamples, subSamples.size(), bytesDecryptedOut); + return static_cast(status); } } // namespace clearkey diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h index f83903c80f..1bbc822fbf 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h @@ -35,6 +35,11 @@ static const std::string kAlgorithmsKey("algorithms"); static const std::string kAlgorithmsValue(""); static const std::string kListenerTestSupportKey("listenerTestSupport"); static const std::string kListenerTestSupportValue("true"); +static const std::string kDrmErrorTestKey("drmErrorTest"); +static const std::string kDrmErrorTestValue(""); +static const std::string kResourceContentionValue("resourceContention"); +static const std::string kLostStateValue("lostState"); +static const std::string kFrameTooLargeValue("frameTooLarge"); static const std::string kDeviceIdKey("deviceId"); static const uint8_t kTestDeviceIdData[] = diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h index 7e9b6bd6dc..2dafa36936 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h @@ -17,7 +17,7 @@ #ifndef CLEARKEY_MACROS_H_ #define CLEARKEY_MACROS_H_ -#include +#include #include diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h index 480dc7ec29..8680f0ca54 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 "ClearKeyTypes.h" @@ -35,13 +35,14 @@ namespace drm { namespace V1_2 { namespace clearkey { -using ::android::hardware::drm::V1_0::DestinationBuffer; -using ::android::hardware::drm::V1_0::ICryptoPlugin; -using ::android::hardware::drm::V1_0::Mode; -using ::android::hardware::drm::V1_0::Pattern; -using ::android::hardware::drm::V1_0::SharedBuffer; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; +namespace drm = ::android::hardware::drm; +using drm::V1_0::DestinationBuffer; +using drm::V1_0::Mode; +using drm::V1_0::Pattern; +using drm::V1_0::SharedBuffer; +using drm::V1_0::Status; +using drm::V1_0::SubSample; + using ::android::hardware::hidl_array; using ::android::hardware::hidl_memory; using ::android::hardware::hidl_string; @@ -51,7 +52,9 @@ using ::android::hardware::Void; using ::android::hidl::memory::V1_0::IMemory; using ::android::sp; -struct CryptoPlugin : public ICryptoPlugin { +typedef drm::V1_2::Status Status_V1_2; + +struct CryptoPlugin : public drm::V1_2::ICryptoPlugin { explicit CryptoPlugin(const hidl_vec& sessionId) { mInitStatus = setMediaDrmSession(sessionId); } @@ -80,6 +83,18 @@ struct CryptoPlugin : public ICryptoPlugin { const DestinationBuffer& destination, decrypt_cb _hidl_cb); + Return decrypt_1_2( + bool secure, + const hidl_array& keyId, + const hidl_array& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_1_2_cb _hidl_cb); + Return setSharedBufferBase(const hidl_memory& base, uint32_t bufferId); diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h index 256c5d66f4..a9b897b02e 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -18,6 +18,7 @@ #define CLEARKEY_DRM_PLUGIN_H_ #include +#include #include #include @@ -34,22 +35,24 @@ namespace drm { namespace V1_2 { namespace clearkey { -using ::android::hardware::drm::V1_0::EventType; -using ::android::hardware::drm::V1_0::IDrmPluginListener; -using ::android::hardware::drm::V1_0::KeyStatus; -using ::android::hardware::drm::V1_0::KeyType; -using ::android::hardware::drm::V1_0::KeyValue; -using ::android::hardware::drm::V1_0::SecureStop; -using ::android::hardware::drm::V1_0::SecureStopId; -using ::android::hardware::drm::V1_0::SessionId; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_1::DrmMetricGroup; -using ::android::hardware::drm::V1_1::HdcpLevel; -using ::android::hardware::drm::V1_1::KeyRequestType; -using ::android::hardware::drm::V1_1::SecureStopRelease; -using ::android::hardware::drm::V1_1::SecurityLevel; -using ::android::hardware::drm::V1_2::IDrmPlugin; -using ::android::hardware::drm::V1_2::OfflineLicenseState; +namespace drm = ::android::hardware::drm; +using drm::V1_0::EventType; +using drm::V1_0::IDrmPluginListener; +using drm::V1_0::KeyRequestType; +using drm::V1_0::KeyStatus; +using drm::V1_0::KeyType; +using drm::V1_0::KeyValue; +using drm::V1_0::SecureStop; +using drm::V1_0::SecureStopId; +using drm::V1_0::SessionId; +using drm::V1_0::Status; +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 ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; @@ -57,6 +60,10 @@ using ::android::hardware::Return; using ::android::hardware::Void; using ::android::sp; +typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; +typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; +typedef drm::V1_2::Status Status_V1_2; + struct DrmPlugin : public IDrmPlugin { explicit DrmPlugin(SessionLibrary* sessionLibrary); @@ -84,6 +91,14 @@ struct DrmPlugin : public IDrmPlugin { const hidl_vec& optionalParameters, getKeyRequest_1_1_cb _hidl_cb) override; + Return getKeyRequest_1_2( + const hidl_vec& scope, + const hidl_vec& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec& optionalParameters, + getKeyRequest_1_2_cb _hidl_cb) override; + Return provideKeyResponse( const hidl_vec& scope, const hidl_vec& response, @@ -116,6 +131,18 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } + Return getProvisionRequest_1_2( + const hidl_string& certificateType, + const hidl_string& certificateAuthority, + getProvisionRequest_1_2_cb _hidl_cb) { + UNUSED(certificateType); + UNUSED(certificateAuthority); + + hidl_string defaultUrl; + _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, hidl_vec(), defaultUrl); + return Void(); + } + Return provideProvisionResponse( const hidl_vec& response, provideProvisionResponse_cb _hidl_cb) { @@ -256,12 +283,17 @@ struct DrmPlugin : public IDrmPlugin { Return setListener(const sp& listener) { mListener = listener; + mListenerV1_2 = IDrmPluginListener_V1_2::castFrom(listener); return Void(); }; - Return sendEvent(EventType eventType, const hidl_vec& sessionId, + Return sendEvent( + EventType eventType, + const hidl_vec& sessionId, const hidl_vec& data) { - if (mListener != NULL) { + if (mListenerV1_2 != NULL) { + mListenerV1_2->sendEvent(eventType, sessionId, data); + } else if (mListener != NULL) { mListener->sendEvent(eventType, sessionId, data); } else { ALOGE("Null event listener, event not sent"); @@ -269,8 +301,12 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } - Return sendExpirationUpdate(const hidl_vec& sessionId, int64_t expiryTimeInMS) { - if (mListener != NULL) { + Return sendExpirationUpdate( + const hidl_vec& sessionId, + int64_t expiryTimeInMS) { + if (mListenerV1_2 != NULL) { + mListenerV1_2->sendExpirationUpdate(sessionId, expiryTimeInMS); + } else if (mListener != NULL) { mListener->sendExpirationUpdate(sessionId, expiryTimeInMS); } else { ALOGE("Null event listener, event not sent"); @@ -278,9 +314,12 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } - Return sendKeysChange(const hidl_vec& sessionId, + Return sendKeysChange( + const hidl_vec& sessionId, const hidl_vec& keyStatusList, bool hasNewUsableKey) { - if (mListener != NULL) { + if (mListenerV1_2 != NULL) { + mListenerV1_2->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); + } else if (mListener != NULL) { mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); } else { ALOGE("Null event listener, event not sent"); @@ -288,6 +327,14 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } + Return sendSessionLostState( + const hidl_vec& sessionId) { + if (mListenerV1_2 != NULL) { + mListenerV1_2->sendSessionLostState(sessionId); + } + return Void(); + } + Return getSecureStops(getSecureStops_cb _hidl_cb); Return getSecureStop(const hidl_vec& secureStopId, @@ -314,13 +361,13 @@ private: Return setSecurityLevel(const hidl_vec& sessionId, SecurityLevel level); - Status getKeyRequestCommon(const hidl_vec& scope, + Status_V1_2 getKeyRequestCommon(const hidl_vec& scope, const hidl_vec& initData, const hidl_string& mimeType, KeyType keyType, const hidl_vec& optionalParameters, std::vector *request, - KeyRequestType *getKeyRequestType, + KeyRequestType_V1_1 *getKeyRequestType, std::string *defaultUrl); struct ClearkeySecureStop { @@ -335,12 +382,21 @@ private: std::map > mReleaseKeysMap; std::map, SecurityLevel> mSecurityLevel; sp mListener; + sp mListenerV1_2; SessionLibrary *mSessionLibrary; int64_t mOpenSessionOkCount; int64_t mCloseSessionOkCount; int64_t mCloseSessionNotOpenedCount; uint32_t mNextSecureStopId; + // set by property to mock error scenarios + Status_V1_2 mMockError; + + void processMockError(const sp &session) { + session->setMockError(mMockError); + mMockError = Status_V1_2::OK; + } + DeviceFiles mFileHandle; CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin); diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h index f35560d7da..a159e5ae76 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h @@ -30,13 +30,16 @@ namespace drm { namespace V1_2 { namespace clearkey { -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; +namespace drm = ::android::hardware::drm; +using drm::V1_0::Status; +using drm::V1_0::SubSample; + +typedef drm::V1_2::Status Status_V1_2; class Session : public RefBase { public: explicit Session(const std::vector& sessionId) - : mSessionId(sessionId) {} + : mSessionId(sessionId), mMockError(Status_V1_2::OK) {} virtual ~Session() {} const std::vector& sessionId() const { return mSessionId; } @@ -50,17 +53,23 @@ public: Status provideKeyResponse( const std::vector& response); - Status decrypt( + Status_V1_2 decrypt( const KeyId keyId, const Iv iv, const uint8_t* srcPtr, uint8_t* dstPtr, const std::vector subSamples, size_t* bytesDecryptedOut); + void setMockError(Status_V1_2 error) {mMockError = error;} + Status_V1_2 getMockError() const {return mMockError;} + private: CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session); const std::vector mSessionId; KeyMap mKeyMap; Mutex mMapLock; + + // For mocking error return scenarios + Status_V1_2 mMockError; }; } // namespace clearkey diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h index f6d30c9044..b0f8607729 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h @@ -68,6 +68,17 @@ template std::vector toVector( return vec; } +inline Status toStatus_1_0(Status_V1_2 status) { + switch (status) { + case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY: + case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE: + case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE: + return Status::ERROR_DRM_UNKNOWN; + default: + return static_cast(status); + } +} + } // namespace clearkey } // namespace V1_2 } // namespace drm diff --git a/media/libmedia/include/media/CryptoHal.h b/media/libmedia/include/media/CryptoHal.h index ff8789d1e6..73c029fae3 100644 --- a/media/libmedia/include/media/CryptoHal.h +++ b/media/libmedia/include/media/CryptoHal.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,7 @@ private: const Vector> mFactories; sp mPlugin; + sp mPluginV1_2; /** * mInitCheck is: diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h index 33029822d4..de0f3c7bbf 100644 --- a/media/libmedia/include/media/DrmHal.h +++ b/media/libmedia/include/media/DrmHal.h @@ -20,11 +20,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -43,6 +43,8 @@ using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; +typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; + namespace android { struct DrmSessionClientInterface; @@ -54,7 +56,7 @@ inline bool operator==(const Vector &l, const Vector &r) { struct DrmHal : public BnDrm, public IBinder::DeathRecipient, - public IDrmPluginListener { + public IDrmPluginListener_V1_2 { DrmHal(); virtual ~DrmHal(); @@ -176,6 +178,8 @@ struct DrmHal : public BnDrm, Return sendKeysChange(const hidl_vec& sessionId, const hidl_vec& keyStatusList, bool hasNewUsableKey); + Return sendSessionLostState(const hidl_vec& sessionId); + virtual void binderDied(const wp &the_late_who); private: diff --git a/media/libstagefright/include/media/stagefright/MediaErrors.h b/media/libstagefright/include/media/stagefright/MediaErrors.h index 6a5c6b6167..09639e2cef 100644 --- a/media/libstagefright/include/media/stagefright/MediaErrors.h +++ b/media/libstagefright/include/media/stagefright/MediaErrors.h @@ -70,7 +70,12 @@ enum { ERROR_DRM_DEVICE_REVOKED = DRM_ERROR_BASE - 9, ERROR_DRM_RESOURCE_BUSY = DRM_ERROR_BASE - 10, ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = DRM_ERROR_BASE - 11, - ERROR_DRM_LAST_USED_ERRORCODE = DRM_ERROR_BASE - 11, + ERROR_DRM_INSUFFICIENT_SECURITY = DRM_ERROR_BASE - 12, + ERROR_DRM_FRAME_TOO_LARGE = DRM_ERROR_BASE - 13, + ERROR_DRM_RESOURCE_CONTENTION = DRM_ERROR_BASE - 14, + ERROR_DRM_SESSION_LOST_STATE = DRM_ERROR_BASE - 15, + ERROR_DRM_INVALID_STATE = DRM_ERROR_BASE - 16, + ERROR_DRM_LAST_USED_ERRORCODE = DRM_ERROR_BASE - 16, ERROR_DRM_VENDOR_MAX = DRM_ERROR_BASE - 500, ERROR_DRM_VENDOR_MIN = DRM_ERROR_BASE - 999, -- GitLab From 57822099ae5f887353ff07cb15563a4aac54336c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 7 Jan 2019 14:51:32 -0800 Subject: [PATCH 0694/1530] Export support types from extractors Make each extractor plugin export a list of file types it supports, so we no longer need to hardcode such a list in the framework. Test: manual Change-Id: I1e41a5d477ea56960ad3e4bc35f5183c03c3fe3a --- include/media/MediaExtractorPluginApi.h | 17 +++++- media/extractors/aac/AACExtractor.cpp | 6 +- media/extractors/amr/AMRExtractor.cpp | 41 ++++++++------ media/extractors/flac/FLACExtractor.cpp | 28 ++++++---- media/extractors/midi/MidiExtractor.cpp | 33 ++++++++--- media/extractors/mkv/MatroskaExtractor.cpp | 27 ++++++--- media/extractors/mp3/MP3Extractor.cpp | 12 +++- media/extractors/mp4/MPEG4Extractor.cpp | 15 ++++- media/extractors/mpeg2/ExtractorBundle.cpp | 40 ++++++++----- media/extractors/ogg/OggExtractor.cpp | 8 ++- media/extractors/wav/WAVExtractor.cpp | 7 ++- media/libmedia/IMediaExtractorService.cpp | 28 ++++++++++ .../include/media/IMediaExtractorService.h | 4 ++ .../libstagefright/MediaExtractorFactory.cpp | 56 +++++++++++++++++-- .../StagefrightMediaScanner.cpp | 22 +++----- .../media/stagefright/MediaExtractorFactory.h | 2 + .../mediaextractor/MediaExtractorService.cpp | 4 ++ .../mediaextractor/MediaExtractorService.h | 2 + 18 files changed, 265 insertions(+), 87 deletions(-) diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h index 854bf83199..916472cef5 100644 --- a/include/media/MediaExtractorPluginApi.h +++ b/include/media/MediaExtractorPluginApi.h @@ -135,8 +135,16 @@ struct ExtractorDef { const char *extractor_name; union { - SnifferFunc v2; - } sniff; + struct { + SnifferFunc sniff; + } v2; + struct { + SnifferFunc sniff; + // a NULL terminated list of container mime types and/or file extensions + // that this extractor supports + const char **supported_types; + } v3; + } u; }; // the C++ based API which first shipped in P and is no longer supported @@ -145,7 +153,10 @@ const uint32_t EXTRACTORDEF_VERSION_LEGACY = 1; // the first C/NDK based API const uint32_t EXTRACTORDEF_VERSION_NDK_V1 = 2; -const uint32_t EXTRACTORDEF_VERSION = EXTRACTORDEF_VERSION_NDK_V1; +// the second C/NDK based API +const uint32_t EXTRACTORDEF_VERSION_NDK_V2 = 3; + +const uint32_t EXTRACTORDEF_VERSION = EXTRACTORDEF_VERSION_NDK_V2; // each plugin library exports one function of this type typedef ExtractorDef (*GetExtractorDef)(); diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index 4e9ac6e978..beddad031f 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -377,6 +377,10 @@ static CreatorFunc Sniff( return NULL; } +static const char *extensions[] = { + "aac", + NULL +}; extern "C" { // This is the only symbol that needs to be exported @@ -387,7 +391,7 @@ ExtractorDef GETEXTRACTORDEF() { UUID("4fd80eae-03d2-4d72-9eb9-48fa6bb54613"), 1, // version "AAC Extractor", - { .v2 = Sniff } + { .v3 = {Sniff, extensions} }, }; } diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp index 00d2a9218c..df8de9bde3 100644 --- a/media/extractors/amr/AMRExtractor.cpp +++ b/media/extractors/amr/AMRExtractor.cpp @@ -199,7 +199,7 @@ media_status_t AMRExtractor::getMetaData(AMediaFormat *meta) { if (mInitCheck == OK) { AMediaFormat_setString(meta, - AMEDIAFORMAT_KEY_MIME, mIsWide ? "audio/amr-wb" : "audio/amr"); + AMEDIAFORMAT_KEY_MIME, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB : "audio/amr"); } return AMEDIA_OK; @@ -358,6 +358,12 @@ media_status_t AMRSource::read( //////////////////////////////////////////////////////////////////////////////// +static const char *extensions[] = { + "amr", + "awb", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -368,21 +374,24 @@ ExtractorDef GETEXTRACTORDEF() { 1, "AMR Extractor", { - .v2 = []( - CDataSource *source, - float *confidence, - void **, - FreeMetaFunc *) -> CreatorFunc { - DataSourceHelper helper(source); - if (SniffAMR(&helper, nullptr, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new AMRExtractor(new DataSourceHelper(source)));}; - } - return NULL; - } - } + .v3 = { + []( + CDataSource *source, + float *confidence, + void **, + FreeMetaFunc *) -> CreatorFunc { + DataSourceHelper helper(source); + if (SniffAMR(&helper, nullptr, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new AMRExtractor(new DataSourceHelper(source)));}; + } + return NULL; + }, + extensions + }, + }, }; } diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index b5eaf9bb59..e4bbc07b91 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -822,6 +822,11 @@ bool SniffFLAC(DataSourceHelper *source, float *confidence) return true; } +static const char *extensions[] = { + "flac", + "fl", + NULL +}; extern "C" { // This is the only symbol that needs to be exported @@ -833,21 +838,24 @@ ExtractorDef GETEXTRACTORDEF() { 1, "FLAC Extractor", { - .v2 = []( + .v3 = { + []( CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - DataSourceHelper helper(source); - if (SniffFLAC(&helper, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new FLACExtractor(new DataSourceHelper(source)));}; - } - return NULL; + DataSourceHelper helper(source); + if (SniffFLAC(&helper, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new FLACExtractor(new DataSourceHelper(source)));}; + } + return NULL; + }, + extensions } - } + }, }; } diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp index 0c74376c6d..9f4f9e69b4 100644 --- a/media/extractors/midi/MidiExtractor.cpp +++ b/media/extractors/midi/MidiExtractor.cpp @@ -326,6 +326,18 @@ bool SniffMidi(CDataSource *source, float *confidence) } +static const char *extensions[] = { + "imy", + "mid", + "midi", + "mxmf", + "ota", + "rtttl", + "rtx", + "smf", + "xmf", + NULL +}; extern "C" { // This is the only symbol that needs to be exported @@ -337,20 +349,23 @@ ExtractorDef GETEXTRACTORDEF() { 1, "MIDI Extractor", { - .v2 = []( + .v3 = { + []( CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - if (SniffMidi(source, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MidiExtractor(source));}; - } - return NULL; + if (SniffMidi(source, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new MidiExtractor(source));}; + } + return NULL; + }, + extensions } - } + }, }; } diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 9f197b0bc5..4200a46b21 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1669,6 +1669,12 @@ bool SniffMatroska( return true; } +static const char *extensions[] = { + "mka", + "mkv", + "webm", + NULL +}; extern "C" { // This is the only symbol that needs to be exported @@ -1680,19 +1686,22 @@ ExtractorDef GETEXTRACTORDEF() { 1, "Matroska Extractor", { - .v2 = []( + .v3 = { + []( CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - DataSourceHelper helper(source); - if (SniffMatroska(&helper, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MatroskaExtractor(new DataSourceHelper(source)));}; - } - return NULL; + DataSourceHelper helper(source); + if (SniffMatroska(&helper, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new MatroskaExtractor(new DataSourceHelper(source)));}; + } + return NULL; + }, + extensions } } }; diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 20bcda8761..61838f69c6 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -609,7 +609,7 @@ media_status_t MP3Extractor::getMetaData(AMediaFormat *meta) { if (mInitCheck != OK) { return AMEDIA_ERROR_UNKNOWN; } - AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, "audio/mpeg"); + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG); DataSourceHelper helper(mDataSource); ID3 id3(&helper); @@ -707,6 +707,14 @@ static CreatorFunc Sniff( return CreateExtractor; } +static const char *extensions[] = { + "mp3", + "mpeg", + "mpg", + "mpga", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -716,7 +724,7 @@ ExtractorDef GETEXTRACTORDEF() { UUID("812a3f6c-c8cf-46de-b529-3774b14103d4"), 1, // version "MP3 Extractor", - { .v2 = Sniff } + { .v3 = {Sniff, extensions} } }; } diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 524db4e047..04413592ff 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -6183,6 +6183,19 @@ static CreatorFunc Sniff( return NULL; } +static const char *extensions[] = { + "3g2", + "3gp", + "3gpp", + "3gpp2", + "m4a", + "m4r", + "m4v", + "mov", + "mp4", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -6192,7 +6205,7 @@ ExtractorDef GETEXTRACTORDEF() { UUID("27575c67-4417-4c54-8d3d-8e626985a164"), 2, // version "MP4 Extractor", - { .v2 = Sniff } + { .v3 = {Sniff, extensions} }, }; } diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index 2f4196c120..946a2a9d99 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "MPEG2PSExtractor.h" #include "MPEG2TSExtractor.h" @@ -26,6 +27,12 @@ namespace android { struct CDataSource; +static const char *extensions[] = { + "m2p", + "ts", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -36,26 +43,29 @@ ExtractorDef GETEXTRACTORDEF() { 1, "MPEG2-PS/TS Extractor", { - .v2 = []( + .v3 = { + []( CDataSource *source, float *confidence, void **, FreeMetaFunc *) -> CreatorFunc { - DataSourceHelper helper(source); - if (SniffMPEG2TS(&helper, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MPEG2TSExtractor(new DataSourceHelper(source)));}; - } else if (SniffMPEG2PS(&helper, confidence)) { - return []( - CDataSource *source, - void *) -> CMediaExtractor* { - return wrap(new MPEG2PSExtractor(new DataSourceHelper(source)));}; - } - return NULL; + DataSourceHelper helper(source); + if (SniffMPEG2TS(&helper, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new MPEG2TSExtractor(new DataSourceHelper(source)));}; + } else if (SniffMPEG2PS(&helper, confidence)) { + return []( + CDataSource *source, + void *) -> CMediaExtractor* { + return wrap(new MPEG2PSExtractor(new DataSourceHelper(source)));}; + } + return NULL; + }, + extensions } - } + }, }; } diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index c3914f183d..590358c469 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -1379,6 +1379,12 @@ static CreatorFunc Sniff( return CreateExtractor; } +static const char *extensions[] = { + "oga", + "ogg", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -1388,7 +1394,7 @@ ExtractorDef GETEXTRACTORDEF() { UUID("8cc5cd06-f772-495e-8a62-cba9649374e9"), 1, // version "Ogg Extractor", - { .v2 = Sniff } + { .v3 = {Sniff, extensions} }, }; } diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 6f9f689062..e58bd1f3a5 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -616,6 +616,11 @@ static CreatorFunc Sniff( return CreateExtractor; } +static const char *extensions[] = { + "wav", + NULL +}; + extern "C" { // This is the only symbol that needs to be exported __attribute__ ((visibility ("default"))) @@ -625,7 +630,7 @@ ExtractorDef GETEXTRACTORDEF() { UUID("7d613858-5837-4a38-84c5-332d1cddee27"), 1, // version "WAV Extractor", - { .v2 = Sniff } + { .v3 = {Sniff, extensions} }, }; } diff --git a/media/libmedia/IMediaExtractorService.cpp b/media/libmedia/IMediaExtractorService.cpp index 0295abcb35..243b09dd34 100644 --- a/media/libmedia/IMediaExtractorService.cpp +++ b/media/libmedia/IMediaExtractorService.cpp @@ -29,6 +29,7 @@ namespace android { enum { MAKE_EXTRACTOR = IBinder::FIRST_CALL_TRANSACTION, MAKE_IDATA_SOURCE_FD, + GET_SUPPORTED_TYPES, }; class BpMediaExtractorService : public BpInterface @@ -68,6 +69,24 @@ public: } return nullptr; } + + virtual std::unordered_set getSupportedTypes() { + std::unordered_set supportedTypes; + Parcel data, reply; + data.writeInterfaceToken(IMediaExtractorService::getInterfaceDescriptor()); + status_t ret = remote()->transact(GET_SUPPORTED_TYPES, data, &reply); + if (ret == NO_ERROR) { + // process reply + while(true) { + const char *ext = reply.readCString(); + if (!ext) { + break; + } + supportedTypes.insert(std::string(ext)); + } + } + return supportedTypes; + } }; IMPLEMENT_META_INTERFACE(MediaExtractorService, "android.media.IMediaExtractorService"); @@ -113,6 +132,15 @@ status_t BnMediaExtractorService::onTransact( return NO_ERROR; } + case GET_SUPPORTED_TYPES: + { + CHECK_INTERFACE(IMediaExtractorService, data, reply); + std::unordered_set supportedTypes = getSupportedTypes(); + for (auto it = supportedTypes.begin(); it != supportedTypes.end(); ++it) { + reply->writeCString((*it).c_str()); + } + return NO_ERROR; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/include/media/IMediaExtractorService.h b/media/libmedia/include/media/IMediaExtractorService.h index 45e96201cb..5ce2cdb6bd 100644 --- a/media/libmedia/include/media/IMediaExtractorService.h +++ b/media/libmedia/include/media/IMediaExtractorService.h @@ -17,6 +17,8 @@ #ifndef ANDROID_IMEDIAEXTRACTORSERVICE_H #define ANDROID_IMEDIAEXTRACTORSERVICE_H +#include + #include #include #include @@ -33,6 +35,8 @@ public: virtual sp makeExtractor(const sp &source, const char *mime) = 0; virtual sp makeIDataSource(int fd, int64_t offset, int64_t length) = 0; + + virtual std::unordered_set getSupportedTypes() = 0; }; class BnMediaExtractorService: public BnInterface diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 2c7a4e58f3..86402cee52 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -106,7 +107,8 @@ sp MediaExtractorFactory::CreateFromService( } MediaExtractor *ex = nullptr; - if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1) { + if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 || + creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) { CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta); if (meta != nullptr && freeMeta != nullptr) { freeMeta(meta); @@ -184,7 +186,10 @@ void *MediaExtractorFactory::sniff( void *curCreator = NULL; if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) { - curCreator = (void*) (*it)->def.sniff.v2( + curCreator = (void*) (*it)->def.u.v2.sniff( + source->wrap(), &newConfidence, &newMeta, &newFreeMeta); + } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) { + curCreator = (void*) (*it)->def.u.v3.sniff( source->wrap(), &newConfidence, &newMeta, &newFreeMeta); } @@ -214,7 +219,8 @@ void *MediaExtractorFactory::sniff( void MediaExtractorFactory::RegisterExtractor(const sp &plugin, std::list> &pluginList) { // sanity check check struct version, uuid, name - if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1) { + if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 && + plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) { ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version); return; } @@ -394,6 +400,8 @@ static bool compareFunc(const sp& first, const spdef.extractor_name, second->def.extractor_name) < 0; } +static std::unordered_set gSupportedExtensions; + // static void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { Mutex::Autolock autoLock(gPluginMutex); @@ -426,9 +434,37 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { newList->sort(compareFunc); gPlugins = newList; + + for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) { + if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) { + for (size_t i = 0;; i++) { + const char* ext = (*it)->def.u.v3.supported_types[i]; + if (ext == nullptr) { + break; + } + gSupportedExtensions.insert(std::string(ext)); + } + } + } + gPluginsRegistered = true; } +// static +std::unordered_set MediaExtractorFactory::getSupportedTypes() { + if (getuid() == AID_MEDIA_EX) { + return gSupportedExtensions; + } + ALOGV("get service manager"); + sp binder = defaultServiceManager()->getService(String16("media.extractor")); + + if (binder != 0) { + sp mediaExService(interface_cast(binder)); + return mediaExService->getSupportedTypes(); + } + return std::unordered_set(); +} + status_t MediaExtractorFactory::dump(int fd, const Vector&) { Mutex::Autolock autoLock(gPluginMutex); String8 out; @@ -445,13 +481,25 @@ status_t MediaExtractorFactory::dump(int fd, const Vector&) { out.append("Available extractors:\n"); if (gPluginsRegistered) { for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) { - out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)\n", + out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)", (*it)->def.extractor_name, (*it)->def.def_version, (*it)->uuidString.c_str(), (*it)->def.extractor_version, (*it)->libPath.c_str()); + if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) { + out.append(", supports: "); + for (size_t i = 0;; i++) { + const char* mime = (*it)->def.u.v3.supported_types[i]; + if (mime == nullptr) { + break; + } + out.appendFormat("%s ", mime); + } + } + out.append("\n"); } + out.append("\n"); } else { out.append(" (no plugins registered)\n"); } diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 17187a853f..74b7340775 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -34,25 +35,16 @@ StagefrightMediaScanner::StagefrightMediaScanner() {} StagefrightMediaScanner::~StagefrightMediaScanner() {} +static std::unordered_set gSupportedExtensions; + static bool FileHasAcceptableExtension(const char *extension) { - static const char *kValidExtensions[] = { - ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", - ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf", - ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov", - ".m4v", ".oga", ".m4r" - }; - static const size_t kNumValidExtensions = - sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); - for (size_t i = 0; i < kNumValidExtensions; ++i) { - if (!strcasecmp(extension, kValidExtensions[i])) { - return true; - } + if (gSupportedExtensions.empty()) { + // get the list from the service + gSupportedExtensions = MediaExtractorFactory::getSupportedTypes(); } - return false; + return gSupportedExtensions.count(std::string(extension + 1)) != 0; } MediaScanResult StagefrightMediaScanner::processFile( diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index 84e01f30c5..ba6631c0e0 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -19,6 +19,7 @@ #define MEDIA_EXTRACTOR_FACTORY_H_ #include +#include #include @@ -35,6 +36,7 @@ public: const sp &source, const char *mime = NULL); static void LoadPlugins(const ::std::string& apkPath); static status_t dump(int fd, const Vector& args); + static std::unordered_set getSupportedTypes(); static void SetLinkedLibraries(const std::string& linkedLibraries); private: diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp index 8b26178640..0665930394 100644 --- a/services/mediaextractor/MediaExtractorService.cpp +++ b/services/mediaextractor/MediaExtractorService.cpp @@ -59,6 +59,10 @@ sp MediaExtractorService::makeIDataSource(int fd, int64_t offset, i return CreateIDataSourceFromDataSource(source); } +std::unordered_set MediaExtractorService::getSupportedTypes() { + return MediaExtractorFactory::getSupportedTypes(); +} + status_t MediaExtractorService::dump(int fd, const Vector& args) { return MediaExtractorFactory::dump(fd, args) || dumpExtractors(fd, args); } diff --git a/services/mediaextractor/MediaExtractorService.h b/services/mediaextractor/MediaExtractorService.h index 60070041cf..c9cebcf91d 100644 --- a/services/mediaextractor/MediaExtractorService.h +++ b/services/mediaextractor/MediaExtractorService.h @@ -37,6 +37,8 @@ public: virtual sp makeIDataSource(int fd, int64_t offset, int64_t length); + virtual std::unordered_set getSupportedTypes(); + virtual status_t dump(int fd, const Vector& args); virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, -- GitLab From db978bbb76431a78aa7d40241251f12acfc298e7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 10 Jan 2019 08:20:36 -0800 Subject: [PATCH 0695/1530] Fix end-of-stream handling in OggExtractor Bug: 122496376 Test: CTS Change-Id: Ied78ce1fbd26e8303cc1772858d31e04765904df --- media/extractors/ogg/OggExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index c3914f183d..5abe039326 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -872,7 +872,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool cal ALOGV("readPage returned %zd", n); - return n < 0 ? (media_status_t) n : AMEDIA_ERROR_END_OF_STREAM; + return n == ERROR_END_OF_STREAM ? AMEDIA_ERROR_END_OF_STREAM : AMEDIA_ERROR_UNKNOWN; } // Prevent a harmless unsigned integer overflow by clamping to 0 -- GitLab From f19cf79e38507cc5e32d1e23825921a5019cd833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 30 May 2018 17:22:17 +0200 Subject: [PATCH 0696/1530] audiopolicy: engineconfigurable: apply coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: Ibfa26a5893e4481619f407f9b1cb679f96e8a7e9 Signed-off-by: François Gaffie --- .../engineconfigurable/include/AudioPolicyEngineInstance.h | 6 ++---- services/audiopolicy/engineconfigurable/src/Collection.h | 6 ++---- services/audiopolicy/engineconfigurable/src/Element.h | 6 ++---- services/audiopolicy/engineconfigurable/src/Engine.cpp | 7 +++---- services/audiopolicy/engineconfigurable/src/Engine.h | 6 ++---- .../audiopolicy/engineconfigurable/src/EngineInstance.cpp | 6 ++---- .../audiopolicy/engineconfigurable/src/InputSource.cpp | 7 +++---- services/audiopolicy/engineconfigurable/src/InputSource.h | 6 ++---- services/audiopolicy/engineconfigurable/src/Strategy.cpp | 6 ++---- services/audiopolicy/engineconfigurable/src/Strategy.h | 6 ++---- services/audiopolicy/engineconfigurable/src/Stream.cpp | 6 ++---- services/audiopolicy/engineconfigurable/src/Stream.h | 7 +++---- services/audiopolicy/engineconfigurable/src/Usage.cpp | 6 ++---- services/audiopolicy/engineconfigurable/src/Usage.h | 6 ++---- .../engineconfigurable/wrapper/ParameterManagerWrapper.cpp | 7 +++---- .../wrapper/include/ParameterManagerWrapper.h | 6 ++---- 16 files changed, 36 insertions(+), 64 deletions(-) diff --git a/services/audiopolicy/engineconfigurable/include/AudioPolicyEngineInstance.h b/services/audiopolicy/engineconfigurable/include/AudioPolicyEngineInstance.h index a597e87466..efc69da15e 100644 --- a/services/audiopolicy/engineconfigurable/include/AudioPolicyEngineInstance.h +++ b/services/audiopolicy/engineconfigurable/include/AudioPolicyEngineInstance.h @@ -19,10 +19,8 @@ class AudioPolicyManagerInterface; class AudioPolicyPluginInterface; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { class Engine; diff --git a/services/audiopolicy/engineconfigurable/src/Collection.h b/services/audiopolicy/engineconfigurable/src/Collection.h index b72ded8d0d..1f8ed8de64 100644 --- a/services/audiopolicy/engineconfigurable/src/Collection.h +++ b/services/audiopolicy/engineconfigurable/src/Collection.h @@ -28,10 +28,8 @@ #include #include -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { /** * Collection of policy element as a map indexed with a their UID type. diff --git a/services/audiopolicy/engineconfigurable/src/Element.h b/services/audiopolicy/engineconfigurable/src/Element.h index 52e77e5fa4..1b55c8cf12 100644 --- a/services/audiopolicy/engineconfigurable/src/Element.h +++ b/services/audiopolicy/engineconfigurable/src/Element.h @@ -22,10 +22,8 @@ #include #include -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { template class Element diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 0d18ffa351..bcd7b2236b 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -35,10 +35,9 @@ using std::string; using std::map; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { + template <> StrategyCollection &Engine::getCollection() { diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h index 328d23d00f..ba4f8893b9 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.h +++ b/services/audiopolicy/engineconfigurable/src/Engine.h @@ -21,12 +21,10 @@ #include #include "Collection.h" -namespace android -{ +namespace android { class AudioPolicyManagerObserver; -namespace audio_policy -{ +namespace audio_policy { class ParameterManagerWrapper; class VolumeProfile; diff --git a/services/audiopolicy/engineconfigurable/src/EngineInstance.cpp b/services/audiopolicy/engineconfigurable/src/EngineInstance.cpp index 9aa89b2fa2..24425904bc 100644 --- a/services/audiopolicy/engineconfigurable/src/EngineInstance.cpp +++ b/services/audiopolicy/engineconfigurable/src/EngineInstance.cpp @@ -21,10 +21,8 @@ using std::string; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { EngineInstance::EngineInstance() { diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.cpp b/services/audiopolicy/engineconfigurable/src/InputSource.cpp index ae39fef2da..b9a38d44f4 100644 --- a/services/audiopolicy/engineconfigurable/src/InputSource.cpp +++ b/services/audiopolicy/engineconfigurable/src/InputSource.cpp @@ -20,10 +20,9 @@ using std::string; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { + status_t Element::setIdentifier(audio_source_t identifier) { if (identifier > AUDIO_SOURCE_MAX && identifier != AUDIO_SOURCE_HOTWORD) { diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.h b/services/audiopolicy/engineconfigurable/src/InputSource.h index 6c498dca1b..64b390e668 100644 --- a/services/audiopolicy/engineconfigurable/src/InputSource.h +++ b/services/audiopolicy/engineconfigurable/src/InputSource.h @@ -18,10 +18,8 @@ #include "Element.h" -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { /** * Specialization of policy base class element for audio_source_t diff --git a/services/audiopolicy/engineconfigurable/src/Strategy.cpp b/services/audiopolicy/engineconfigurable/src/Strategy.cpp index a5399143ba..310b35ed64 100644 --- a/services/audiopolicy/engineconfigurable/src/Strategy.cpp +++ b/services/audiopolicy/engineconfigurable/src/Strategy.cpp @@ -20,10 +20,8 @@ using std::string; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { status_t Element::setIdentifier(routing_strategy identifier) { diff --git a/services/audiopolicy/engineconfigurable/src/Strategy.h b/services/audiopolicy/engineconfigurable/src/Strategy.h index 1157d558b5..f2487fda67 100644 --- a/services/audiopolicy/engineconfigurable/src/Strategy.h +++ b/services/audiopolicy/engineconfigurable/src/Strategy.h @@ -19,10 +19,8 @@ #include "Element.h" #include -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { /** * @tparam audio_devices_t: Applicable output device(s) for this strategy. diff --git a/services/audiopolicy/engineconfigurable/src/Stream.cpp b/services/audiopolicy/engineconfigurable/src/Stream.cpp index 0ed364f789..73fb94d4dc 100644 --- a/services/audiopolicy/engineconfigurable/src/Stream.cpp +++ b/services/audiopolicy/engineconfigurable/src/Stream.cpp @@ -21,10 +21,8 @@ using std::string; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { status_t Element::setIdentifier(audio_stream_type_t identifier) { diff --git a/services/audiopolicy/engineconfigurable/src/Stream.h b/services/audiopolicy/engineconfigurable/src/Stream.h index 690200331e..2bf70b3973 100644 --- a/services/audiopolicy/engineconfigurable/src/Stream.h +++ b/services/audiopolicy/engineconfigurable/src/Stream.h @@ -21,10 +21,9 @@ #include #include -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { + /** * @tparam routing_strategy: Applicable strategy for this stream. */ diff --git a/services/audiopolicy/engineconfigurable/src/Usage.cpp b/services/audiopolicy/engineconfigurable/src/Usage.cpp index 5d20828107..8c0dfba247 100644 --- a/services/audiopolicy/engineconfigurable/src/Usage.cpp +++ b/services/audiopolicy/engineconfigurable/src/Usage.cpp @@ -18,10 +18,8 @@ #include "Usage.h" -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { status_t Element::setIdentifier(audio_usage_t identifier) { diff --git a/services/audiopolicy/engineconfigurable/src/Usage.h b/services/audiopolicy/engineconfigurable/src/Usage.h index d69e0e0488..72a452f348 100644 --- a/services/audiopolicy/engineconfigurable/src/Usage.h +++ b/services/audiopolicy/engineconfigurable/src/Usage.h @@ -19,10 +19,8 @@ #include "Element.h" #include -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { /** * @tparam routing_strategy: Applicable strategy for this usage. diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index 9b0442e7c1..f8174f8c28 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -56,13 +56,12 @@ public: } }; -namespace android -{ +namespace android { using utilities::convertTo; -namespace audio_policy -{ +namespace audio_policy { + const char *const ParameterManagerWrapper::mPolicyPfwDefaultConfFileName = "/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml"; diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h index 4c1acfe70c..107ef12bd4 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h +++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h @@ -32,10 +32,8 @@ struct cnode; class ParameterMgrPlatformConnectorLogger; -namespace android -{ -namespace audio_policy -{ +namespace android { +namespace audio_policy { class ParameterManagerWrapper { -- GitLab From a56b5c2686910731a070e2fd36ee2f64a0ff0266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 21 Feb 2018 18:04:39 +0100 Subject: [PATCH 0697/1530] audiopolicy: engineconfigurable: Migrate to XML criteria and criterion type defintion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: Ia7eb6b86dffb31bcb4191f5e265e85135934dc36 Signed-off-by: François Gaffie --- .../parameter-framework/examples/Android.mk | 42 ++- .../device_for_strategies.pfw | 18 +- .../Structure/PolicySubsystem-CommonTypes.xml | 7 + .../engineconfigurable/tools/Android.bp | 31 ++ .../tools/buildPolicyCriterionTypes.py | 243 +++++++++++++ ...on_criterion_types_from_android_headers.mk | 24 ++ .../engineconfigurable/wrapper/Android.mk | 47 ++- .../wrapper/ParameterManagerWrapper.cpp | 335 +++++++----------- .../wrapper/ParameterManagerWrapperConfig.cpp | 208 +++++++++++ .../wrapper/ParameterManagerWrapperConfig.h | 139 ++++++++ .../wrapper/audio_policy_criteria_conf.h | 73 ---- .../wrapper/config/audio_policy_criteria.conf | 146 -------- .../wrapper/config/policy_criteria.xml | 30 ++ .../config/policy_criterion_types.xml.in | 91 +++++ .../config/policy_wrapper_configuration.xml | 25 ++ .../wrapper/include/ParameterManagerWrapper.h | 107 +----- 16 files changed, 1009 insertions(+), 557 deletions(-) create mode 100644 services/audiopolicy/engineconfigurable/tools/Android.bp create mode 100755 services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py create mode 100644 services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk create mode 100644 services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp create mode 100644 services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/config/audio_policy_criteria.conf create mode 100644 services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml create mode 100644 services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in create mode 100644 services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index baaefd26dd..75067349ad 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -7,10 +7,10 @@ # ################################################################################################ -ifeq (1, 0) - LOCAL_PATH := $(call my-dir) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) + PFW_CORE := external/parameter-framework BUILD_PFW_SETTINGS := $(PFW_CORE)/support/android/build_pfw_settings.mk PFW_DEFAULT_SCHEMAS_DIR := $(PFW_CORE)/upstream/schemas @@ -25,7 +25,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/parameter-framework +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework LOCAL_SRC_FILES := $(LOCAL_MODULE).in AUDIO_PATTERN = @TUNING_ALLOWED@ @@ -46,7 +47,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := PolicyClass.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/parameter-framework/Structure/Policy +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) @@ -54,12 +56,12 @@ include $(CLEAR_VARS) LOCAL_MODULE := PolicySubsystem.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true LOCAL_REQUIRED_MODULES := \ PolicySubsystem-CommonTypes.xml \ - PolicySubsystem-Volume.xml \ - libpolicy-subsystem \ + libpolicy-subsystem -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/parameter-framework/Structure/Policy +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) @@ -67,7 +69,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := PolicySubsystem-CommonTypes.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/parameter-framework/Structure/Policy +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) @@ -76,14 +79,15 @@ include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy -LOCAL_ADDITIONAL_DEPENDENCIES := \ +LOCAL_REQUIRED_MODULES := \ PolicyClass.xml \ PolicySubsystem.xml \ ParameterFrameworkConfigurationPolicy.xml ifeq ($(pfw_rebuild_settings),true) -PFW_TOPLEVEL_FILE := $(TARGET_OUT_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ @@ -107,18 +111,23 @@ LOCAL_SRC_FILES := Settings/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) endif # pfw_rebuild_settings +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 0) + ######### Policy PFW Settings - No Output ######### +ifeq (0, 1) + include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.no-output LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoOutputDevice.xml LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy -LOCAL_ADDITIONAL_DEPENDENCIES := \ +LOCAL_REQUIRED_MODULES := \ PolicyClass.xml \ PolicySubsystem.xml \ ParameterFrameworkConfigurationPolicy.xml -PFW_TOPLEVEL_FILE := $(TARGET_OUT_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoOutput/device_for_strategies.pfw \ @@ -128,19 +137,22 @@ PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/volumes.pfw include $(BUILD_PFW_SETTINGS) - +endif # ifeq (0, 1) ######### Policy PFW Settings - No Input ######### +ifeq (0, 1) + include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.no-input LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoInputDevice.xml LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy -LOCAL_ADDITIONAL_DEPENDENCIES := \ +LOCAL_REQUIRED_MODULES := \ PolicyClass.xml \ PolicySubsystem.xml \ ParameterFrameworkConfigurationPolicy.xml -PFW_TOPLEVEL_FILE := $(TARGET_OUT_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw index 917d4a761a..f9236105b5 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/SettingsNoOutput/device_for_strategies.pfw @@ -9,7 +9,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -37,7 +37,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -65,7 +65,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -93,7 +93,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -121,7 +121,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -149,7 +149,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -177,7 +177,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -205,7 +205,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 @@ -233,7 +233,7 @@ domain: DeviceForStrategy bluetooth_sco = 0 bluetooth_sco_headset = 0 bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0> + bluetooth_a2dp = 0 bluetooth_a2dp_headphones = 0 bluetooth_a2dp_speaker = 0 hdmi = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml index 461e44a7cb..f410773d3f 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml @@ -37,6 +37,10 @@ + + + + @@ -67,6 +71,9 @@ + + + diff --git a/services/audiopolicy/engineconfigurable/tools/Android.bp b/services/audiopolicy/engineconfigurable/tools/Android.bp new file mode 100644 index 0000000000..d8f29dca3d --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/Android.bp @@ -0,0 +1,31 @@ +// 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. + +python_binary_host { + name: "buildPolicyCriterionTypes.py", + owner: "renault", + main: "buildPolicyCriterionTypes.py", + srcs: [ + "buildPolicyCriterionTypes.py", + ], + version: { + py2: { + enabled: true, + }, + py3: { + enabled: false, + }, + }, +} + diff --git a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py new file mode 100755 index 0000000000..0fb70a6c9d --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py @@ -0,0 +1,243 @@ +#!/usr/bin/python + +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import re +import sys +import tempfile +import os +import logging +import subprocess +import xml.etree.ElementTree as ET +import xml.etree.ElementInclude as EI +import xml.dom.minidom as MINIDOM +from collections import OrderedDict + +# +# Helper script that helps to feed at build time the XML criterion types file used by +# the engineconfigurable to start the parameter-framework. +# It prevents to fill them manually and avoid divergences with android. +# +# The Device Types criterion types are fed from audio-base.h file with the option +# --androidaudiobaseheader +# +# The Device Addresses criterion types are fed from the audio policy configuration file +# in order to discover all the devices for which the address matter. +# --audiopolicyconfigurationfile +# +# The reference file of criterion types must also be set as an input of the script: +# --criteriontypes +# +# At last, the output of the script shall be set also: +# --outputfile +# + +def parseArgs(): + argparser = argparse.ArgumentParser(description="Parameter-Framework XML \ + audio criterion type file generator.\n\ + Exit with the number of (recoverable or not) error that occured.") + argparser.add_argument('--androidaudiobaseheader', + help="Android Audio Base C header file, Mandatory.", + metavar="ANDROID_AUDIO_BASE_HEADER", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--audiopolicyconfigurationfile', + help="Android Audio Policy Configuration file, Mandatory.", + metavar="(AUDIO_POLICY_CONFIGURATION_FILE)", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--criteriontypes', + help="Criterion types XML base file, in \ + ' \ + values=/>' \ + format. Mandatory.", + metavar="CRITERION_TYPE_FILE", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--outputfile', + help="Criterion types outputfile file. Mandatory.", + metavar="CRITERION_TYPE_OUTPUT_FILE", + type=argparse.FileType('w'), + required=True) + argparser.add_argument('--verbose', + action='store_true') + + return argparser.parse_args() + + +def generateXmlCriterionTypesFile(criterionTypes, addressCriteria, criterionTypesFile, outputFile): + + logging.info("Importing criterionTypesFile {}".format(criterionTypesFile)) + criterion_types_in_tree = ET.parse(criterionTypesFile) + + criterion_types_root = criterion_types_in_tree.getroot() + + for criterion_name, values_dict in criterionTypes.items(): + for criterion_type in criterion_types_root.findall('criterion_type'): + if criterion_type.get('name') == criterion_name: + values_node = ET.SubElement(criterion_type, "values") + ordered_values = OrderedDict(sorted(values_dict.items(), key=lambda x: x[1])) + for key, value in ordered_values.items(): + value_node = ET.SubElement(values_node, "value") + value_node.set('numerical', str(value)) + value_node.set('literal', key) + + if addressCriteria: + for criterion_name, values_list in addressCriteria.items(): + for criterion_type in criterion_types_root.findall('criterion_type'): + if criterion_type.get('name') == criterion_name: + values_node = ET.SubElement(criterion_type, "values") + index = 0 + for value in values_list: + value_node = ET.SubElement(values_node, "value", literal=value) + value_node.set('numerical', str(1 << index)) + index += 1 + + xmlstr = ET.tostring(criterion_types_root, encoding='utf8', method='xml') + reparsed = MINIDOM.parseString(xmlstr) + prettyXmlStr = reparsed.toprettyxml(newl='\r\n') + prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()]) + outputFile.write(prettyXmlStr.encode('utf-8')) + +def capitalizeLine(line): + return ' '.join((w.capitalize() for w in line.split(' '))) + + +# +# Parse the audio policy configuration file and output a dictionary of device criteria addresses +# +def parseAndroidAudioPolicyConfigurationFile(audiopolicyconfigurationfile): + + logging.info("Checking Audio Policy Configuration file {}".format(audiopolicyconfigurationfile)) + # + # extract all devices addresses from audio policy configuration file + # + address_criteria_mapping_table = { + 'sink' : "OutputDevicesAddressesType", + 'source' : "InputDevicesAddressesType" } + + address_criteria = { + 'OutputDevicesAddressesType' : [], + 'InputDevicesAddressesType' : [] } + + oldWorkingDir = os.getcwd() + print "Current working directory %s" % oldWorkingDir + + newDir = os.path.join(oldWorkingDir , audiopolicyconfigurationfile.name) + + policy_in_tree = ET.parse(audiopolicyconfigurationfile) + os.chdir(os.path.dirname(os.path.normpath(newDir))) + + print "new working directory %s" % os.getcwd() + + policy_root = policy_in_tree.getroot() + EI.include(policy_root) + + os.chdir(oldWorkingDir) + + for device in policy_root.iter('devicePort'): + for key in address_criteria_mapping_table.keys(): + if device.get('role') == key and device.get('address') : + logging.info("{}: <{}>".format(key, device.get('address'))) + address_criteria[address_criteria_mapping_table[key]].append(device.get('address')) + + for criteria in address_criteria: + values = ','.join(address_criteria[criteria]) + logging.info("{}: <{}>".format(criteria, values)) + + return address_criteria + +# +# Parse the audio-base.h file and output a dictionary of android dependent criterion types: +# -Android Mode +# -Output devices type +# -Input devices type +# +def parseAndroidAudioFile(androidaudiobaseheaderFile): + # + # Adaptation table between Android Enumeration prefix and Audio PFW Criterion type names + # + criterion_mapping_table = { + 'AUDIO_MODE' : "AndroidModeType", + 'AUDIO_DEVICE_OUT' : "OutputDevicesMaskType", + 'AUDIO_DEVICE_IN' : "InputDevicesMaskType"} + + all_criteria = { + 'AndroidModeType' : {}, + 'OutputDevicesMaskType' : {}, + 'InputDevicesMaskType' : {} } + + # + # _CNT, _MAX, _ALL and _NONE are prohibited values as ther are just helpers for enum users. + # + ignored_values = [ 'CNT', 'MAX', 'ALL', 'NONE' ] + + criteria_pattern = re.compile( + r"\s*(?P(?:"+'|'.join(criterion_mapping_table.keys()) + "))\_" \ + r"(?P(?!" + '|'.join(ignored_values) + ")\w*)\s*=\s*" \ + r"(?P(?:0[xX])?[0-9a-fA-F]+)") + + logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile)) + + for line_number, line in enumerate(androidaudiobaseheaderFile): + match = criteria_pattern.match(line) + if match: + logging.debug("The following line is VALID: {}:{}\n{}".format( + androidaudiobaseheaderFile.name, line_number, line)) + + criterion_name = criterion_mapping_table[match.groupdict()['type']] + literal = ''.join((w.capitalize() for w in match.groupdict()['literal'].split('_'))) + numerical_value = match.groupdict()['values'] + + # for AUDIO_DEVICE_IN: need to remove sign bit + if criterion_name == "InputDevicesMaskType": + numerical_value = str(int(numerical_value, 0) & ~2147483648) + + # Remove duplicated numerical values + if int(numerical_value, 0) in all_criteria[criterion_name].values(): + logging.info("criterion {} duplicated values:".format(criterion_name)) + logging.info("{}:{}".format(numerical_value, literal)) + logging.info("KEEPING LATEST") + for key in all_criteria[criterion_name].keys(): + if all_criteria[criterion_name][key] == int(numerical_value, 0): + del all_criteria[criterion_name][key] + + all_criteria[criterion_name][literal] = int(numerical_value, 0) + + logging.debug("type:{},".format(criterion_name)) + logging.debug("iteral:{},".format(literal)) + logging.debug("values:{}.".format(numerical_value)) + + return all_criteria + + +def main(): + logging.root.setLevel(logging.INFO) + args = parseArgs() + + all_criteria = parseAndroidAudioFile(args.androidaudiobaseheader) + + address_criteria = parseAndroidAudioPolicyConfigurationFile(args.audiopolicyconfigurationfile) + + criterion_types = args.criteriontypes + + generateXmlCriterionTypesFile(all_criteria, address_criteria, criterion_types, args.outputfile) + +# If this file is directly executed +if __name__ == "__main__": + exit(main()) diff --git a/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk new file mode 100644 index 0000000000..4814376baf --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk @@ -0,0 +1,24 @@ +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(CRITERION_TYPES_FILE) +$(LOCAL_BUILT_MODULE): MY_ANDROID_AUDIO_BASE_HEADER_FILE := $(ANDROID_AUDIO_BASE_HEADER_FILE) +$(LOCAL_BUILT_MODULE): MY_AUDIO_POLICY_CONFIGURATION_FILE := $(AUDIO_POLICY_CONFIGURATION_FILE) +$(LOCAL_BUILT_MODULE): MY_CRITERION_TOOL := $(HOST_OUT)/bin/buildPolicyCriterionTypes.py +$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) \ + buildPolicyCriterionTypes.py \ + $(CRITERION_TYPES_FILE) \ + $(ANDROID_AUDIO_BASE_HEADER_FILE) + + "$(MY_CRITERION_TOOL)" \ + --androidaudiobaseheader "$(MY_ANDROID_AUDIO_BASE_HEADER_FILE)" \ + --audiopolicyconfigurationfile "$(MY_AUDIO_POLICY_CONFIGURATION_FILE)" \ + --criteriontypes "$(MY_CRITERION_TYPES_FILE)" \ + --outputfile "$(@)" + +# Clear variables for further use +CRITERION_TYPES_FILE := +ANDROID_AUDIO_BASE_HEADER_FILE := +AUDIO_POLICY_CONFIGURATION_FILE := diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk index b128a38098..d19a364ec9 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk @@ -1,5 +1,8 @@ LOCAL_PATH:= $(call my-dir) +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +PROVISION_CRITERION_TYPES := $(TOOLS)/provision_criterion_types_from_android_headers.mk + ################################################################## # WRAPPER LIBRARY ################################################################## @@ -10,12 +13,22 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ frameworks/av/services/audiopolicy/engineconfigurable/include \ frameworks/av/services/audiopolicy/engineconfigurable/interface \ + frameworks/av/services/audiopolicy/common/include \ + external/libxml2/include \ + external/icu/icu4c/source/common -LOCAL_SRC_FILES:= ParameterManagerWrapper.cpp +LOCAL_SRC_FILES:= \ + ParameterManagerWrapper.cpp \ + ParameterManagerWrapperConfig.cpp LOCAL_SHARED_LIBRARIES := \ libparameter \ - libmedia_helper + libmedia_helper \ + libicuuc \ + libxml2 + +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) @@ -31,11 +44,35 @@ include $(BUILD_STATIC_LIBRARY) # CONFIGURATION FILE ################################################################## -# specific management of audio_policy_criteria.conf +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) + include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_criteria.conf +LOCAL_MODULE := policy_wrapper_configuration.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := config/$(LOCAL_MODULE) include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := policy_criteria.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := config/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := policy_criterion_types.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml + +AUDIO_POLICY_CONFIGURATION_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml +ANDROID_AUDIO_BASE_HEADER_FILE := system/media/audio/include/system/audio-base.h +CRITERION_TYPES_FILE := $(LOCAL_PATH)/config/policy_criterion_types.xml.in + +include $(PROVISION_CRITERION_TYPES) + +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index f8174f8c28..fc6c1e4173 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -18,7 +18,7 @@ //#define LOG_NDEBUG 0 #include "ParameterManagerWrapper.h" -#include "audio_policy_criteria_conf.h" +#include "ParameterManagerWrapperConfig.h" #include #include #include @@ -38,6 +38,7 @@ using std::string; using std::map; using std::vector; +using CriterionTypes = std::map; /// PFW related definitions // Logger @@ -65,6 +66,27 @@ namespace audio_policy { const char *const ParameterManagerWrapper::mPolicyPfwDefaultConfFileName = "/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml"; +static const char *const gInputDeviceCriterionName = "AvailableInputDevices"; +static const char *const gOutputDeviceCriterionName = "AvailableOutputDevices"; +static const char *const gPhoneStateCriterionName = "TelephonyMode"; +static const char *const gOutputDeviceAddressCriterionName = "AvailableOutputDevicesAddresses"; +static const char *const gInputDeviceAddressCriterionName = "AvailableInputDevicesAddresses"; + +/** + * Order MUST be align with defintiion of audio_policy_force_use_t within audio_policy.h + */ +static const char *const gForceUseCriterionTag[AUDIO_POLICY_FORCE_USE_CNT] = +{ + [AUDIO_POLICY_FORCE_FOR_COMMUNICATION] = "ForceUseForCommunication", + [AUDIO_POLICY_FORCE_FOR_MEDIA] = "ForceUseForMedia", + [AUDIO_POLICY_FORCE_FOR_RECORD] = "ForceUseForRecord", + [AUDIO_POLICY_FORCE_FOR_DOCK] = "ForceUseForDock", + [AUDIO_POLICY_FORCE_FOR_SYSTEM] = "ForceUseForSystem", + [AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] = "ForceUseForHdmiSystemAudio", + [AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND] = "ForceUseForEncodedSurround", + [AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING] = "ForceUseForVibrateRinging" +}; + template <> struct ParameterManagerWrapper::parameterManagerElementSupported {}; template <> @@ -79,15 +101,64 @@ ParameterManagerWrapper::ParameterManagerWrapper() // Logger mPfwConnector->setLogger(mPfwConnectorLogger); - // Load criteria file - if ((loadAudioPolicyCriteriaConfig(gAudioPolicyCriteriaVendorConfFilePath) != NO_ERROR) && - (loadAudioPolicyCriteriaConfig(gAudioPolicyCriteriaConfFilePath) != NO_ERROR)) { - ALOGE("%s: Neither vendor conf file (%s) nor system conf file (%s) could be found", - __FUNCTION__, gAudioPolicyCriteriaVendorConfFilePath, - gAudioPolicyCriteriaConfFilePath); + status_t loadResult = loadConfig(); + if (loadResult < 0) { + ALOGE("Policy Wrapper configuration is partially invalid."); } } +status_t ParameterManagerWrapper::loadConfig() +{ + auto result = wrapper_config::parse(); + if (result.parsedConfig == nullptr) { + return -ENOENT; + } + ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); + + CriterionTypes criterionTypes; + for (auto criterionType : result.parsedConfig->criterionTypes) { + ALOG_ASSERT(criterionTypes.find(criterionType.name) == criterionTypes.end(), + "CriterionType %s already added", criterionType.name.c_str()); + ALOGV("%s: Adding new criterionType %s", __FUNCTION__, criterionType.name.c_str()); + + auto criterionTypePfw = + mPfwConnector->createSelectionCriterionType(criterionType.isInclusive); + + for (auto pair : criterionType.valuePairs) { + std::string error; + ALOGV("%s: Adding pair %d,%s for criterionType %s", __FUNCTION__, pair.first, + pair.second.c_str(), criterionType.name.c_str()); + criterionTypePfw->addValuePair(pair.first, pair.second, error); + } + criterionTypes[criterionType.name] = criterionTypePfw; + } + + for (auto criterion : result.parsedConfig->criteria) { + ALOG_ASSERT(mPolicyCriteria.find(criterion.name) == mPolicyCriteria.end(), + "%s: Criterion %s already added", __FUNCTION__, criterion.name.c_str()); + + auto criterionType = + getElement(criterion.typeName, criterionTypes); + ALOG_ASSERT(criterionType != nullptr, "No %s Criterion type found for criterion %s", + criterion.typeName.c_str(), criterion.name.c_str()); + + auto criterionPfw = mPfwConnector->createSelectionCriterion(criterion.name, criterionType); + mPolicyCriteria[criterion.name] = criterionPfw; + + if (not criterion.defaultLiteralValue.empty()) { + int numericalValue = 0; + if (not criterionType->getNumericalValue(criterion.defaultLiteralValue.c_str(), + numericalValue)) { + ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__, + criterion.defaultLiteralValue.c_str()); + continue; + } + criterionPfw->setCriterionState(numericalValue); + } + } + return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE; +} + ParameterManagerWrapper::~ParameterManagerWrapper() { // Unset logger @@ -111,112 +182,6 @@ status_t ParameterManagerWrapper::start() return NO_ERROR; } - -void ParameterManagerWrapper::addCriterionType(const string &typeName, bool isInclusive) -{ - ALOG_ASSERT(mPolicyCriterionTypes.find(typeName) == mPolicyCriterionTypes.end(), - "CriterionType %s already added", typeName.c_str()); - ALOGD("%s: Adding new criterionType %s", __FUNCTION__, typeName.c_str()); - - mPolicyCriterionTypes[typeName] = mPfwConnector->createSelectionCriterionType(isInclusive); -} - -void ParameterManagerWrapper::addCriterionTypeValuePair( - const string &typeName, - uint32_t numericValue, - const string &literalValue) -{ - ALOG_ASSERT(mPolicyCriterionTypes.find(typeName) != mPolicyCriterionTypes.end(), - "CriterionType %s not found", typeName.c_str()); - ALOGV("%s: Adding new value pair (%d,%s) for criterionType %s", __FUNCTION__, - numericValue, literalValue.c_str(), typeName.c_str()); - ISelectionCriterionTypeInterface *criterionType = mPolicyCriterionTypes[typeName]; - std::string error; - criterionType->addValuePair(numericValue, literalValue, error); -} - -void ParameterManagerWrapper::loadCriterionType(cnode *root, bool isInclusive) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node; - for (node = root->first_child; node != NULL; node = node->next) { - - ALOG_ASSERT(node != NULL, "error in parsing file"); - const char *typeName = node->name; - char *valueNames = strndup(node->value, strlen(node->value)); - - addCriterionType(typeName, isInclusive); - - uint32_t index = 0; - char *ctx; - char *valueName = strtok_r(valueNames, ",", &ctx); - while (valueName != NULL) { - if (strlen(valueName) != 0) { - - // Conf file may use or not pair, if no pair, use incremental index, else - // use provided index. - if (strchr(valueName, ':') != NULL) { - - char *first = strtok(valueName, ":"); - char *second = strtok(NULL, ":"); - ALOG_ASSERT((first != NULL) && (strlen(first) != 0) && - (second != NULL) && (strlen(second) != 0), - "invalid value pair"); - - if (!convertTo(first, index)) { - ALOGE("%s: Invalid index(%s) found", __FUNCTION__, first); - } - addCriterionTypeValuePair(typeName, index, second); - } else { - - uint32_t pfwIndex = isInclusive ? 1 << index : index; - addCriterionTypeValuePair(typeName, pfwIndex, valueName); - index += 1; - } - } - valueName = strtok_r(NULL, ",", &ctx); - } - free(valueNames); - } -} - -void ParameterManagerWrapper::loadInclusiveCriterionType(cnode *root) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node = config_find(root, gInclusiveCriterionTypeTag.c_str()); - if (node == NULL) { - return; - } - loadCriterionType(node, true); -} - -void ParameterManagerWrapper::loadExclusiveCriterionType(cnode *root) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node = config_find(root, gExclusiveCriterionTypeTag.c_str()); - if (node == NULL) { - return; - } - loadCriterionType(node, false); -} - -void ParameterManagerWrapper::parseChildren(cnode *root, string &defaultValue, string &type) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node; - for (node = root->first_child; node != NULL; node = node->next) { - ALOG_ASSERT(node != NULL, "error in parsing file"); - - if (string(node->name) == gDefaultTag) { - defaultValue = node->value; - } else if (string(node->name) == gTypeTag) { - type = node->value; - } else { - ALOGE("%s: Unrecognized %s %s node", __FUNCTION__, node->name, node->value); - } - } -} - template T *ParameterManagerWrapper::getElement(const string &name, std::map &elementsMap) { @@ -235,97 +200,6 @@ const T *ParameterManagerWrapper::getElement(const string &name, const std::map< return it != elementsMap.end() ? it->second : NULL; } -void ParameterManagerWrapper::loadCriteria(cnode *root) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node = config_find(root, gCriterionTag.c_str()); - - if (node == NULL) { - ALOGW("%s: no inclusive criteria found", __FUNCTION__); - return; - } - for (node = node->first_child; node != NULL; node = node->next) { - loadCriterion(node); - } -} - -void ParameterManagerWrapper::addCriterion(const string &name, const string &typeName, - const string &defaultLiteralValue) -{ - ALOG_ASSERT(mPolicyCriteria.find(name) == mPolicyCriteria.end(), - "Route Criterion %s already added", name.c_str()); - - ISelectionCriterionTypeInterface *criterionType = - getElement(typeName, mPolicyCriterionTypes); - - ISelectionCriterionInterface *criterion = - mPfwConnector->createSelectionCriterion(name, criterionType); - - mPolicyCriteria[name] = criterion; - int numericalValue = 0; - if (!criterionType->getNumericalValue(defaultLiteralValue.c_str(), numericalValue)) { - ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__, - defaultLiteralValue.c_str()); - } - criterion->setCriterionState(numericalValue); -} - -void ParameterManagerWrapper::loadCriterion(cnode *root) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - const char *criterionName = root->name; - - ALOG_ASSERT(mPolicyCriteria.find(criterionName) == mPolicyCriteria.end(), - "Criterion %s already added", criterionName); - - string paramKeyName = ""; - string path = ""; - string typeName = ""; - string defaultValue = ""; - - parseChildren(root, defaultValue, typeName); - - addCriterion(criterionName, typeName, defaultValue); -} - -void ParameterManagerWrapper::loadConfig(cnode *root) -{ - ALOG_ASSERT(root != NULL, "error in parsing file"); - cnode *node = config_find(root, gPolicyConfTag.c_str()); - if (node == NULL) { - ALOGW("%s: Could not find node for pfw", __FUNCTION__); - return; - } - ALOGD("%s: Loading conf for pfw", __FUNCTION__); - loadInclusiveCriterionType(node); - loadExclusiveCriterionType(node); - loadCriteria(node); -} - - -status_t ParameterManagerWrapper::loadAudioPolicyCriteriaConfig(const char *path) -{ - ALOG_ASSERT(path != NULL, "error in parsing file: empty path"); - cnode *root; - char *data; - ALOGD("%s", __FUNCTION__); - data = (char *)load_file(path, NULL); - if (data == NULL) { - return -ENODEV; - } - root = config_node("", ""); - ALOG_ASSERT(root != NULL, "Unable to allocate a configuration node"); - config_load(root, data); - - loadConfig(root); - - config_free(root); - free(root); - free(data); - ALOGD("%s: loaded", __FUNCTION__); - return NO_ERROR; -} - bool ParameterManagerWrapper::isStarted() { return mPfwConnector && mPfwConnector->isStarted(); @@ -334,9 +208,9 @@ bool ParameterManagerWrapper::isStarted() status_t ParameterManagerWrapper::setPhoneState(audio_mode_t mode) { ISelectionCriterionInterface *criterion = - getElement(gPhoneStateCriterionTag, mPolicyCriteria); + getElement(gPhoneStateCriterionName, mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionTag.c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionName); return BAD_VALUE; } if (!isValueValidForCriterion(criterion, static_cast(mode))) { @@ -350,9 +224,9 @@ status_t ParameterManagerWrapper::setPhoneState(audio_mode_t mode) audio_mode_t ParameterManagerWrapper::getPhoneState() const { const ISelectionCriterionInterface *criterion = - getElement(gPhoneStateCriterionTag, mPolicyCriteria); + getElement(gPhoneStateCriterionName, mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionTag.c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gPhoneStateCriterionName); return AUDIO_MODE_NORMAL; } return static_cast(criterion->getCriterionState()); @@ -369,7 +243,7 @@ status_t ParameterManagerWrapper::setForceUse(audio_policy_force_use_t usage, ISelectionCriterionInterface *criterion = getElement(gForceUseCriterionTag[usage], mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage].c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage]); return BAD_VALUE; } if (!isValueValidForCriterion(criterion, static_cast(config))) { @@ -389,7 +263,7 @@ audio_policy_forced_cfg_t ParameterManagerWrapper::getForceUse(audio_policy_forc const ISelectionCriterionInterface *criterion = getElement(gForceUseCriterionTag[usage], mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage].c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gForceUseCriterionTag[usage]); return AUDIO_POLICY_FORCE_NONE; } return static_cast(criterion->getCriterionState()); @@ -403,12 +277,45 @@ bool ParameterManagerWrapper::isValueValidForCriterion(ISelectionCriterionInterf return interface->getLiteralValue(valueToCheck, literalValue); } +status_t ParameterManagerWrapper::setDeviceConnectionState(const sp devDesc, + audio_policy_dev_state_t state) +{ + std::string criterionName = audio_is_output_device(devDesc->type()) ? + gOutputDeviceAddressCriterionName : gInputDeviceAddressCriterionName; + + ALOGV("%s: device with address %s %s", __FUNCTION__, devDesc->mAddress.string(), + state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE? "disconnected" : "connected"); + ISelectionCriterionInterface *criterion = + getElement(criterionName, mPolicyCriteria); + + if (criterion == NULL) { + ALOGE("%s: no criterion found for %s", __FUNCTION__, criterionName.c_str()); + return DEAD_OBJECT; + } + + auto criterionType = criterion->getCriterionType(); + int deviceAddressId; + if (not criterionType->getNumericalValue(devDesc->mAddress.string(), deviceAddressId)) { + ALOGE("%s: unknown device address reported (%s)", __FUNCTION__, devDesc->mAddress.c_str()); + return BAD_TYPE; + } + int currentValueMask = criterion->getCriterionState(); + if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + currentValueMask |= deviceAddressId; + } + else { + currentValueMask &= ~deviceAddressId; + } + criterion->setCriterionState(currentValueMask); + return NO_ERROR; +} + status_t ParameterManagerWrapper::setAvailableInputDevices(audio_devices_t inputDevices) { ISelectionCriterionInterface *criterion = - getElement(gInputDeviceCriterionTag, mPolicyCriteria); + getElement(gInputDeviceCriterionName, mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gInputDeviceCriterionTag.c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gInputDeviceCriterionName); return DEAD_OBJECT; } criterion->setCriterionState(inputDevices & ~AUDIO_DEVICE_BIT_IN); @@ -419,9 +326,9 @@ status_t ParameterManagerWrapper::setAvailableInputDevices(audio_devices_t input status_t ParameterManagerWrapper::setAvailableOutputDevices(audio_devices_t outputDevices) { ISelectionCriterionInterface *criterion = - getElement(gOutputDeviceCriterionTag, mPolicyCriteria); + getElement(gOutputDeviceCriterionName, mPolicyCriteria); if (criterion == NULL) { - ALOGE("%s: no criterion found for %s", __FUNCTION__, gOutputDeviceCriterionTag.c_str()); + ALOGE("%s: no criterion found for %s", __FUNCTION__, gOutputDeviceCriterionName); return DEAD_OBJECT; } criterion->setCriterionState(outputDevices); diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp new file mode 100644 index 0000000000..bc6d046f31 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp @@ -0,0 +1,208 @@ +/* + * 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 "APM::AudioPolicyEngine/PFWWrapperConfig" +#define LOG_NDEBUG 0 + +#include "ParameterManagerWrapperConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace android { + +using utilities::convertTo; + +namespace audio_policy { +namespace wrapper_config { +namespace detail { + +std::string getXmlAttribute(const xmlNode *cur, const char *attribute) +{ + xmlChar *xmlValue = xmlGetProp(cur, (const xmlChar *)attribute); + if (xmlValue == NULL) { + return ""; + } + std::string value((const char *)xmlValue); + xmlFree(xmlValue); + return value; +} + +template +static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur, + typename Trait::Collection &collection, + size_t &nbSkippedElement) +{ + const xmlNode *root = cur->xmlChildrenNode; + while (root != NULL) { + if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) && + xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { + root = root->next; + continue; + } + const xmlNode *child = root; + if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) { + child = child->xmlChildrenNode; + } + while (child != NULL) { + if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) { + status_t status = Trait::deserialize(doc, child, collection); + if (status == NO_ERROR) { + nbSkippedElement += 1; + } + } + child = child->next; + } + if (!xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { + return NO_ERROR; + } + root = root->next; + } + return NO_ERROR; +} + +const char *const ValueTraits::tag = "value"; +const char *const ValueTraits::collectionTag = "values"; + +const char ValueTraits::Attributes::literal[] = "literal"; +const char ValueTraits::Attributes::numerical[] = "numerical"; + +status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values) +{ + std::string literal = getXmlAttribute(child, Attributes::literal); + if (literal.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); + return BAD_VALUE; + } + uint32_t numerical = 0; + std::string numericalTag = getXmlAttribute(child, Attributes::numerical); + if (numericalTag.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); + return BAD_VALUE; + } + if (!convertTo(numericalTag, numerical)) { + ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str()); + return BAD_VALUE; + } + values.push_back({numerical, literal}); + return NO_ERROR; +} + +const char *const CriterionTypeTraits::tag = "criterion_type"; +const char *const CriterionTypeTraits::collectionTag = "criterion_types"; + +const char CriterionTypeTraits::Attributes::name[] = "name"; +const char CriterionTypeTraits::Attributes::type[] = "type"; + +status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, + Collection &criterionTypes) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str()); + + std::string type = getXmlAttribute(child, Attributes::type); + if (type.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type); + return BAD_VALUE; + } + ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str()); + bool isInclusive(type == "inclusive"); + + ValuePairs pairs; + size_t nbSkippedElements = 0; + detail::deserializeCollection(doc, child, pairs, nbSkippedElements); + + criterionTypes.push_back({name, isInclusive, pairs}); + return NO_ERROR; +} + +const char *const CriterionTraits::tag = "criterion"; +const char *const CriterionTraits::collectionTag = "criteria"; + +const char CriterionTraits::Attributes::name[] = "name"; +const char CriterionTraits::Attributes::type[] = "type"; +const char CriterionTraits::Attributes::defaultVal[] = "default"; + +status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &criteria) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); + + std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal); + if (defaultValue.empty()) { + // Not mandatory to provide a default value for a criterion, even it is recommanded... + ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::defaultVal); + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str()); + + std::string typeName = getXmlAttribute(child, Attributes::type); + if (typeName.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str()); + + criteria.push_back({name, typeName, defaultValue}); + return NO_ERROR; +} +} // namespace detail + +ParsingResult parse(const char* path) { + xmlDocPtr doc; + doc = xmlParseFile(path); + if (doc == NULL) { + ALOGE("%s: Could not parse document %s", __FUNCTION__, path); + return {nullptr, 0}; + } + xmlNodePtr cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); + xmlFreeDoc(doc); + return {nullptr, 0}; + } + if (xmlXIncludeProcess(doc) < 0) { + ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); + return {nullptr, 0}; + } + size_t nbSkippedElements = 0; + auto config = std::make_unique(); + + detail::deserializeCollection( + doc, cur, config->criteria, nbSkippedElements); + detail::deserializeCollection( + doc, cur, config->criterionTypes, nbSkippedElements); + + return {std::move(config), nbSkippedElements}; +} + +} // namespace wrapper_config +} // namespace audio_policy +} // namespace android diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h new file mode 100644 index 0000000000..467d0e11fb --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +struct _xmlNode; +struct _xmlDoc; + +namespace android { +namespace audio_policy { +namespace wrapper_config { + +/** Default path of audio policy usages configuration file. */ +constexpr char DEFAULT_PATH[] = "/vendor/etc/policy_wrapper_configuration.xml"; + +/** Directories where the effect libraries will be search for. */ +constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"}; + +using ValuePair = std::pair; +using ValuePairs = std::vector; + +struct CriterionType +{ + std::string name; + bool isInclusive; + ValuePairs valuePairs; +}; + +using CriterionTypes = std::vector; + +struct Criterion +{ + std::string name; + std::string typeName; + std::string defaultLiteralValue; +}; + +using Criteria = std::vector; + +struct Config { + float version; + Criteria criteria; + CriterionTypes criterionTypes; +}; + +namespace detail +{ +struct ValueTraits +{ + static const char *const tag; + static const char *const collectionTag; + + struct Attributes + { + static const char literal[]; + static const char numerical[]; + }; + + typedef ValuePair Element; + typedef ValuePair *PtrElement; + typedef ValuePairs Collection; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; + +struct CriterionTypeTraits +{ + static const char *const tag; + static const char *const collectionTag; + + struct Attributes + { + static const char name[]; + static const char type[]; + }; + + typedef CriterionType Element; + typedef CriterionType *PtrElement; + typedef CriterionTypes Collection; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; + +struct CriterionTraits +{ + static const char *const tag; + static const char *const collectionTag; + + struct Attributes + { + static const char name[]; + static const char type[]; + static const char defaultVal[]; + }; + + typedef Criterion Element; + typedef Criterion *PtrElement; + typedef Criteria Collection; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; +} // namespace detail + +/** Result of `parse(const char*)` */ +struct ParsingResult { + /** Parsed config, nullptr if the xml lib could not load the file */ + std::unique_ptr parsedConfig; + size_t nbSkippedElement; //< Number of skipped invalid product strategies +}; + +/** Parses the provided audio policy usage configuration. + * @return audio policy usage @see Config + */ +ParsingResult parse(const char* path = DEFAULT_PATH); + +} // namespace wrapper_config +} // namespace audio_policy +} // android diff --git a/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h b/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h deleted file mode 100644 index e4fd1768fc..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -////////////////////////////////////////////////////////////////////////////////////////////////// -// Definitions for audio policy criteria configuration file (audio_policy_criteria.conf) // -// // -// @TODO: scripted from audio.h & audio_policy,h // -////////////////////////////////////////////////////////////////////////////////////////////////// - -static const char *const gAudioPolicyCriteriaConfFilePath = - "/system/etc/audio_policy_criteria.conf"; -static const char *const gAudioPolicyCriteriaVendorConfFilePath = - "/vendor/etc/audio_policy_criteria.conf"; - -/** - * PFW instances tags - */ -static const std::string &gPolicyConfTag = "Policy"; -static const std::string &gDefaultTag = "Default"; -static const std::string &gTypeTag = "Type"; - -/** - * PFW elements tags - */ -static const std::string &gInclusiveCriterionTypeTag = "InclusiveCriterionType"; -static const std::string &gExclusiveCriterionTypeTag = "ExclusiveCriterionType"; -static const std::string &gCriterionTag = "Criterion"; - -/** - * PFW known criterion tags - */ -static const std::string &gInputDeviceCriterionTag = "AvailableInputDevices"; -static const std::string &gOutputDeviceCriterionTag = "AvailableOutputDevices"; -static const std::string &gPhoneStateCriterionTag = "TelephonyMode"; - -/** - * Order MUST be align with defintiion of audio_policy_force_use_t within audio_policy.h - */ -static const std::string gForceUseCriterionTag[AUDIO_POLICY_FORCE_USE_CNT] = -{ - [AUDIO_POLICY_FORCE_FOR_COMMUNICATION] = "ForceUseForCommunication", - [AUDIO_POLICY_FORCE_FOR_MEDIA] = "ForceUseForMedia", - [AUDIO_POLICY_FORCE_FOR_RECORD] = "ForceUseForRecord", - [AUDIO_POLICY_FORCE_FOR_DOCK] = "ForceUseForDock", - [AUDIO_POLICY_FORCE_FOR_SYSTEM] = "ForceUseForSystem", - [AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] = "ForceUseForHdmiSystemAudio", - [AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND] = "ForceUseForEncodedSurround", - [AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING] = "ForceUseForVibrateRinging" -}; - - - - - - diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/audio_policy_criteria.conf b/services/audiopolicy/engineconfigurable/wrapper/config/audio_policy_criteria.conf deleted file mode 100644 index 043d5a6902..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/config/audio_policy_criteria.conf +++ /dev/null @@ -1,146 +0,0 @@ -################################################################################################ -# -# @NOTE: -# Audio Policy Criteria file example for generic device build -# -# Any vendor shall have its own configuration within the corresponding device folder -# -################################################################################################ - -######################################################### -# Criterion type Example: -# For each criterion, a couple of numerical, literal values must be provided to the PFW. -# The numerical part is not mandatory. If not filled by the user, a default numerical value will be -# automatically provided by audio HAL using the following logic: -# - Exclusive criterion: -# * 0 -> first literal value, -# * 1 -> second literal value, -# ... -# * N -> (N+1)th literal value. -# - Inclusive criterion: -# * 1 << 0 -> first literal value, -# * 1 << 1 -> second literal value, -# ... -# * 1 << N -> (N+1)th literal value, -# -######################################################### -# Policy { -# InclusiveCriterionType|ExclusiveCriterionType { -# [numerical value 1:],[numerical value 2:],,... -# } -# } - -######################################################### -# Criterion: -######################################################### -# Policy { -# Criterion { -# { -# Type -# Default -# } -# } -# } - -Policy { - InclusiveCriterionType { - # - # DO NOT CHANGE ORDER. This definition must be aligned with the definition of - # AUDIO_DEVICE_OUT_* within file of android. - # - OutputDevicesMaskType Earpiece,Speaker,WiredHeadset,WiredHeadphone,BluetoothSco,BluetoothScoHeadset,BluetoothScoCarkit,BluetoothA2dp,BluetoothA2dpHeadphones,BluetoothA2dpSpeaker,Hdmi,AnlgDockHeadset,DgtlDockHeadset,UsbAccessory,UsbDevice,RemoteSubmix,TelephonyTx,Line,HdmiArc,Spdif,Fm,AuxLine,SpeakerSafe - # - # DO NOT CHANGE ORDER. This definition must be aligned with the definition of - # AUDIO_DEVICE_IN_* within file of android. - # Note also that direction bit will be decimated by AudioHAL in order to allow using a mask - # with the cardinality of 1 between a bit and an input device. - # - InputDevicesMaskType Communication,Ambient,BuiltinMic,BluetoothScoHeadset,WiredHeadset,Hdmi,TelephonyRx,BackMic,RemoteSubmix,AnlgDockHeadset,DgtlDockHeadset,UsbAccessory,UsbDevice,FmTuner,TvTune,Line,Spdif,BluetoothA2dp,Loopback - } - ExclusiveCriterionType { - # - # The values of the mode MUST be aligned with the definition of the audio_mode_t - # from system/audio.h - # - AndroidModeType 0:Normal,1:RingTone,2:InCall,3:InCommunication - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForCommunicationType 0:ForceNone,1:ForceSpeaker,3:ForceBtSco - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForMediaType 0:ForceNone,1:ForceSpeaker,2:ForceHeadphones,4:ForceBtA2dp,5:ForceWiredAccessory,8:ForceAnalogDock,9:ForceDigitalDock,10:ForceNoBtA2dp - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForRecordType 0:ForceNone,3:ForceBtSco,5:ForceWiredAccessory - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForDockType 0:ForceNone,5:ForceWiredAccessory,6:ForceBtCarDock,7:ForceBtDeskDock,8:ForceAnalogDock,9:ForceDigitalDock - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForSystemType 0:ForceNone,11:ForceSystemEnforced - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio.h - # - ForceUseForHdmiSystemAudioType 0:ForceNone,12:ForceHdmiSystemEnforced - # - # The values of the mode MUST be aligned with the definition of the - # audio_policy_forced_config_t from system/audio_policy.h - # - ForceUseForEncodedSurroundType 0:ForceNone,13:ForceEncodedSurroundNever,14:ForceEncodedSurroundAlways - } - - Criterion { - AvailableInputDevices { - Type InputDevicesMaskType - Default none - } - AvailableOutputDevices { - Type OutputDevicesMaskType - Default none - } - TelephonyMode { - Type AndroidModeType - Default Normal - } - ForceUseForCommunication { - Type ForceUseForCommunicationType - Default ForceNone - } - ForceUseForMedia { - Type ForceUseForMediaType - Default ForceNone - } - ForceUseForRecord { - Type ForceUseForRecordType - Default ForceNone - } - ForceUseForDock { - Type ForceUseForDockType - Default ForceNone - } - ForceUseForSystem { - Type ForceUseForSystemType - Default ForceNone - } - ForceUseForHdmiSystemAudio { - Type ForceUseForHdmiSystemAudioType - Default ForceNone - } - ForceUseForEncodedSurround { - Type ForceUseForEncodedSurroundType - Default ForceNone - } - } -} - diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml new file mode 100644 index 0000000000..ec82b2e8c7 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in new file mode 100644 index 0000000000..6cb799f3ef --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml b/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml new file mode 100644 index 0000000000..5d9193bb1f --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h index 107ef12bd4..1a634a1d54 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h +++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h @@ -16,10 +16,15 @@ #pragma once +#include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -27,7 +32,6 @@ class CParameterMgrPlatformConnector; class ISelectionCriterionInterface; -class ISelectionCriterionTypeInterface; struct cnode; class ParameterMgrPlatformConnectorLogger; @@ -38,14 +42,7 @@ namespace audio_policy { class ParameterManagerWrapper { private: - typedef std::pair CriterionTypeValuePair; - - typedef std::map CriterionCollection; - typedef std::map CriterionTypeCollection; - typedef CriterionCollection::iterator CriterionMapIterator; - typedef CriterionCollection::const_iterator CriterionMapConstIterator; - typedef CriterionTypeCollection::iterator CriterionTypeMapIterator; - typedef CriterionTypeCollection::const_iterator CriteriaTypeMapConstIterator; + using Criteria = std::map; public: ParameterManagerWrapper(); @@ -118,6 +115,9 @@ public: */ status_t setAvailableOutputDevices(audio_devices_t outputDevices); + status_t setDeviceConnectionState(const sp devDesc, + audio_policy_dev_state_t state); + private: /** * Apply the configuration of the platform on the policy parameter manager. @@ -134,93 +134,11 @@ private: /** * Load the criterion configuration file. * - * @param[in] path Criterion conf file path. - * * @return NO_ERROR is parsing successful, error code otherwise. */ - status_t loadAudioPolicyCriteriaConfig(const char *path); - - /** - * Add a criterion type to AudioPolicyPfw. - * - * @param[in] typeName of the PFW criterion type. - * @param[in] isInclusive attribute of the criterion type. - */ - void addCriterionType(const std::string &typeName, bool isInclusive); - - /** - * Add a criterion type value pair to AudioPolicyPfw. - * - * @param[in] typeName criterion type name to which this value pair is added to. - * @param[in] numeric part of the value pair. - * @param[in] literal part of the value pair. - */ - void addCriterionTypeValuePair(const std::string &typeName, uint32_t numeric, - const std::string &literal); - - /** - * Add a criterion to AudioPolicyPfw. - * - * @param[in] name of the PFW criterion. - * @param[in] typeName criterion type name to which this criterion is associated to. - * @param[in] defaultLiteralValue of the PFW criterion. - */ - void addCriterion(const std::string &name, - const std::string &typeName, - const std::string &defaultLiteralValue); - /** - * Parse and load the inclusive criterion type from configuration file. - * - * @param[in] root node of the configuration file. - */ - void loadInclusiveCriterionType(cnode *root); - - /** - * Parse and load the exclusive criterion type from configuration file. - * - * @param[in] root node of the configuration file. - */ - void loadExclusiveCriterionType(cnode *root); - - /** - * Parse and load the criteria from configuration file. - * - * @param[in] root node of the configuration file. - */ - void loadCriteria(cnode *root); + status_t loadConfig(); - /** - * Parse and load a criterion from configuration file. - * - * @param[in] root node of the configuration file. - */ - void loadCriterion(cnode *root); - - /** - * Parse and load the criterion types from configuration file. - * - * @param[in] root node of the configuration file - * @param[in] isInclusive true if inclusive, false is exclusive. - */ - void loadCriterionType(cnode *root, bool isInclusive); - - /** - * Load the configuration file. - * - * @param[in] root node of the configuration file. - */ - void loadConfig(cnode *root); - - /** - * Parse and load the chidren node from a given root node. - * - * @param[in] root node of the configuration file - * @param[out] defaultValue of the parameter manager element to retrieve. - * @param[out] type of the parameter manager element to retrieve. - */ - void parseChildren(cnode *root, std::string &defaultValue, std::string &type); - - /** + /** * Retrieve an element from a map by its name. * * @tparam T type of element to search. @@ -264,8 +182,7 @@ private: bool isValueValidForCriterion(ISelectionCriterionInterface *criterion, int valueToCheck); - CriterionTypeCollection mPolicyCriterionTypes; /**< Policy Criterion Type map. */ - CriterionCollection mPolicyCriteria; /**< Policy Criterion Map. */ + Criteria mPolicyCriteria; /**< Policy Criterion Map. */ CParameterMgrPlatformConnector *mPfwConnector; /**< Policy Parameter Manager connector. */ ParameterMgrPlatformConnectorLogger *mPfwConnectorLogger; /**< Policy PFW logger. */ -- GitLab From 177d3f83ae46fb5fe6b9d0841d395314b802259e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 22 Feb 2018 09:52:39 +0100 Subject: [PATCH 0698/1530] audiopolicy: engineconfigurable: Simplify policy PFW plugin: remove Id from structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: I9ae8dd640c692ec0895094f60424fe77752a01bd Signed-off-by: François Gaffie --- .../Structure/PolicySubsystem-CommonTypes.xml | 8 +- .../examples/Structure/PolicySubsystem.xml | 128 ++++++++---------- .../parameter-framework/plugin/Android.mk | 1 + .../plugin/InputSource.cpp | 9 +- .../plugin/PolicySubsystem.cpp | 8 +- .../parameter-framework/plugin/Strategy.cpp | 36 ++++- .../parameter-framework/plugin/Stream.cpp | 9 +- .../parameter-framework/plugin/Usage.cpp | 8 +- 8 files changed, 118 insertions(+), 89 deletions(-) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml index f410773d3f..daa7f689e2 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml @@ -151,7 +151,7 @@ - + @@ -177,7 +177,7 @@ - + @@ -188,7 +188,7 @@ - + @@ -197,7 +197,7 @@ + Mapping="InputSource" Description="Selected Input device"/> diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml index ad9c3563a0..45d1e8a7f2 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml @@ -13,54 +13,40 @@ - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + - - @@ -68,36 +54,34 @@ - - - + + + + Mapping="Name:AUDIO_USAGE_VOICE_COMMUNICATION"/> - - + Mapping="Name:AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING"/> + + + Mapping="Name:AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE"/> + Mapping="Name:AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST"/> + Mapping="Name:AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT"/> + Mapping="Name:AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED"/> + Mapping="Name:AUDIO_USAGE_NOTIFICATION_EVENT"/> + Mapping="Name:AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY"/> + Mapping="Name:AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/> - - - + Mapping="Name:AUDIO_USAGE_ASSISTANCE_SONIFICATION"/> + + + @@ -106,25 +90,25 @@ - - + + + Mapping="Name:AUDIO_SOURCE_VOICE_UPLINK"/> + Mapping="Name:AUDIO_SOURCE_VOICE_DOWNLINK"/> - + Mapping="Name:AUDIO_SOURCE_VOICE_CALL"/> + + Mapping="Name:AUDIO_SOURCE_VOICE_RECOGNITION"/> + Mapping="Name:AUDIO_SOURCE_VOICE_COMMUNICATION"/> + Mapping="Name:AUDIO_SOURCE_REMOTE_SUBMIX"/> - - + Mapping="Name:AUDIO_SOURCE_UNPROCESSED"/> + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index 3559cf1eee..db1f0381c2 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -28,6 +28,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libaudiopolicyengineconfigurable \ libparameter \ + libmedia_helper \ liblog \ LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp index eac4efe2c4..f91f8d7814 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp @@ -17,6 +17,7 @@ #include "InputSource.h" #include "PolicyMappingKeys.h" #include "PolicySubsystem.h" +#include using std::string; @@ -33,9 +34,13 @@ InputSource::InputSource(const string &mappingValue, instanceConfigurableElement->getBelongingSubsystem())), mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) { - mId = static_cast(context.getItemAsInteger(MappingKeyIdentifier)); + std::string name(context.getItem(MappingKeyName)); + + if(not android::SourceTypeConverter::fromString(name, mId)) { + LOG_ALWAYS_FATAL("Invalid Input Source name: %s, invalid XML structure file", name.c_str()); + } // Declares the strategy to audio policy engine - mPolicyPluginInterface->addInputSource(getFormattedMappingValue(), mId); + mPolicyPluginInterface->addInputSource(name, mId); } bool InputSource::sendToHW(string & /*error*/) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp index 98d10a9779..7374fc3fe5 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp @@ -65,22 +65,22 @@ PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &log addSubsystemObjectFactory( new TSubsystemObjectFactory( mStreamComponentName, - (1 << MappingKeyIdentifier)) + (1 << MappingKeyName)) ); addSubsystemObjectFactory( new TSubsystemObjectFactory( mStrategyComponentName, - (1 << MappingKeyAmend1) | (1 << MappingKeyIdentifier)) + 0) ); addSubsystemObjectFactory( new TSubsystemObjectFactory( mUsageComponentName, - (1 << MappingKeyAmend1) | (1 << MappingKeyIdentifier)) + (1 << MappingKeyName)) ); addSubsystemObjectFactory( new TSubsystemObjectFactory( mInputSourceComponentName, - (1 << MappingKeyAmend1) | (1 << MappingKeyIdentifier)) + (1 << MappingKeyName)) ); } diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp index 746c3a8d0c..876bcb0595 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp @@ -17,10 +17,38 @@ #include "Strategy.h" #include "PolicyMappingKeys.h" #include "PolicySubsystem.h" +#include using std::string; using android::routing_strategy; +namespace detail { + +constexpr std::pair routingStrategyMap[] = { + {android::STRATEGY_MEDIA, "STRATEGY_MEDIA"}, + {android::STRATEGY_PHONE, "STRATEGY_PHONE"}, + {android::STRATEGY_SONIFICATION, "STRATEGY_SONIFICATION"}, + {android::STRATEGY_SONIFICATION_RESPECTFUL, "STRATEGY_SONIFICATION_RESPECTFUL"}, + {android::STRATEGY_DTMF, "STRATEGY_DTMF"}, + {android::STRATEGY_ENFORCED_AUDIBLE, "STRATEGY_ENFORCED_AUDIBLE"}, + {android::STRATEGY_TRANSMITTED_THROUGH_SPEAKER, "STRATEGY_TRANSMITTED_THROUGH_SPEAKER"}, + {android::STRATEGY_ACCESSIBILITY, "STRATEGY_ACCESSIBILITY"}, + {android::STRATEGY_REROUTING, "STRATEGY_REROUTING"}, +}; + +bool fromString(const char *literalName, routing_strategy &type) +{ + for (auto& pair : routingStrategyMap) { + if (strcmp(pair.second, literalName) == 0) { + type = pair.first; + return true; + } + } + return false; +} + +} + Strategy::Strategy(const string &mappingValue, CInstanceConfigurableElement *instanceConfigurableElement, const CMappingContext &context, @@ -35,10 +63,12 @@ Strategy::Strategy(const string &mappingValue, instanceConfigurableElement->getBelongingSubsystem())), mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) { - mId = static_cast(context.getItemAsInteger(MappingKeyIdentifier)); - + std::string name(context.getItem(MappingKeyName)); + if (not detail::fromString(name.c_str(), mId)) { + LOG_ALWAYS_FATAL("Invalid Strategy %s, invalid XML structure file", name.c_str()); + } // Declares the strategy to audio policy engine - mPolicyPluginInterface->addStrategy(getFormattedMappingValue(), mId); + mPolicyPluginInterface->addStrategy(instanceConfigurableElement->getName(), mId); } bool Strategy::sendToHW(string & /*error*/) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp index c642a232d0..46c9e1cab9 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp @@ -17,6 +17,7 @@ #include "Stream.h" #include "PolicyMappingKeys.h" #include "PolicySubsystem.h" +#include using std::string; using android::routing_strategy; @@ -29,10 +30,14 @@ Stream::Stream(const string &/*mappingValue*/, instanceConfigurableElement->getBelongingSubsystem())), mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) { - mId = static_cast(context.getItemAsInteger(MappingKeyIdentifier)); + std::string name(context.getItem(MappingKeyName)); + + if (not android::StreamTypeConverter::fromString(name, mId)) { + LOG_ALWAYS_FATAL("Invalid Stream type name: %s, invalid XML structure file", name.c_str()); + } // Declares the strategy to audio policy engine - mPolicyPluginInterface->addStream(getFormattedMappingValue(), mId); + mPolicyPluginInterface->addStream(name, mId); } bool Stream::sendToHW(string & /*error*/) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp index 78199f8141..925d6319f2 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp @@ -17,6 +17,7 @@ #include "Usage.h" #include "PolicyMappingKeys.h" #include "PolicySubsystem.h" +#include using std::string; using android::routing_strategy; @@ -34,10 +35,13 @@ Usage::Usage(const string &mappingValue, instanceConfigurableElement->getBelongingSubsystem())), mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) { - mId = static_cast(context.getItemAsInteger(MappingKeyIdentifier)); + std::string name(context.getItem(MappingKeyName)); + if (not android::UsageTypeConverter::fromString(name, mId)) { + LOG_ALWAYS_FATAL("Invalid Usage name: %s, invalid XML structure file", name.c_str()); + } // Declares the strategy to audio policy engine - mPolicyPluginInterface->addUsage(getFormattedMappingValue(), mId); + mPolicyPluginInterface->addUsage(name, mId); } bool Usage::sendToHW(string & /*error*/) -- GitLab From d544f1c61fce0050aacfe5ef8a01213cf8040fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 25 Jun 2018 15:25:48 +0200 Subject: [PATCH 0699/1530] audiopolicy: engineconfigurable: switch to unix socket for PFW debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SEPolicy rule prevents the audioserver to use tcp / udp sockets. The Parameter-framework has been updated to use unix socket. This patch makes use of the unix socket and adds the corresponding sepolicy rules. Test: make Change-Id: I61fcf6fec06a78cf3189c93a86469070ef15bba4 Signed-off-by: François Gaffie --- media/audioserver/audioserver.rc | 3 +++ .../examples/ParameterFrameworkConfigurationPolicy.xml.in | 3 ++- .../audiopolicy/engineconfigurable/sepolicy/audioserver.te | 4 ++++ services/audiopolicy/engineconfigurable/sepolicy/file.te | 2 ++ .../audiopolicy/engineconfigurable/sepolicy/file_contexts | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 services/audiopolicy/engineconfigurable/sepolicy/audioserver.te create mode 100644 services/audiopolicy/engineconfigurable/sepolicy/file.te create mode 100644 services/audiopolicy/engineconfigurable/sepolicy/file_contexts diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc index f1e815bfaf..dfb1a3fb93 100644 --- a/media/audioserver/audioserver.rc +++ b/media/audioserver/audioserver.rc @@ -16,3 +16,6 @@ on property:vts.native_server.on=1 stop audioserver on property:vts.native_server.on=0 start audioserver + +on init + mkdir /dev/socket/audioserver 0775 audioserver audioserver diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in b/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in index f5615cd52b..f80a07fc1f 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in @@ -1,6 +1,7 @@ + SystemClassName="Policy" ServerPort="/dev/socket/audioserver/policy_debug" + TuningAllowed="@TUNING_ALLOWED@"> diff --git a/services/audiopolicy/engineconfigurable/sepolicy/audioserver.te b/services/audiopolicy/engineconfigurable/sepolicy/audioserver.te new file mode 100644 index 0000000000..4d41d42c06 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/sepolicy/audioserver.te @@ -0,0 +1,4 @@ +userdebug_or_eng(` + allow audioserver audioserver_socket:dir rw_dir_perms; + allow audioserver audioserver_socket:sock_file create_file_perms; +') diff --git a/services/audiopolicy/engineconfigurable/sepolicy/file.te b/services/audiopolicy/engineconfigurable/sepolicy/file.te new file mode 100644 index 0000000000..7db1937a44 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/sepolicy/file.te @@ -0,0 +1,2 @@ +# Policy Engine remote connection for runtime debug of parameter framework +type audioserver_socket, file_type, coredomain_socket; diff --git a/services/audiopolicy/engineconfigurable/sepolicy/file_contexts b/services/audiopolicy/engineconfigurable/sepolicy/file_contexts new file mode 100644 index 0000000000..950fcce8c7 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/sepolicy/file_contexts @@ -0,0 +1 @@ +/dev/socket/audioserver(/.*)? u:object_r:audioserver_socket:s0 -- GitLab From 3305c11ac31fc595a3bb313e765aa4b2f6dcfe71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 22 Feb 2018 10:56:49 +0100 Subject: [PATCH 0700/1530] audiopolicy: engineconfigurable: Forward device connection state to Policy PFW wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: I4a70abbae6c6bd0e683447d32d964728a9acc3fd Signed-off-by: François Gaffie --- services/audiopolicy/engineconfigurable/src/Engine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index bcd7b2236b..009cf9004b 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -219,8 +219,10 @@ audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) co } status_t Engine::setDeviceConnectionState(const sp devDesc, - audio_policy_dev_state_t /*state*/) + audio_policy_dev_state_t state) { + mPolicyParameterMgr->setDeviceConnectionState(devDesc, state); + if (audio_is_output_device(devDesc->type())) { return mPolicyParameterMgr->setAvailableOutputDevices( mApmObserver->getAvailableOutputDevices().types()); -- GitLab From 0c280aa1bdef93e318fb987e0cd239c1bcf0d438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 25 Jul 2018 10:02:15 +0200 Subject: [PATCH 0701/1530] AudioFlinger: update cache in/out configuration on device port id change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of creation of AudioPatch with the same device type but a different device id (typically: a device has been connected / disconnection, so removed and re-attached to its corresponding module, a new Id is assigned), the cache configuration of the clients is not updated. If the client calls getRoutedDevice, the id will not be up to date and even may refer to unknown port id. Test: as follows: 1 / Calls setDeviceConnectionState AUDIO_DEVICE_OUT_HDMI state=1 mame=y_dummy_hdmi @=my_dummy_hdmi 2 / Plays explicititely on this device (by getting the id AudioSystem::listAudioPorts 2 / Calls setDeviceConnectionState AUDIO_DEVICE_OUT_HDMI state=0 3 / Calls setDeviceConnectionState AUDIO_DEVICE_OUT_HDMI state=1 mame=y_dummy_hdmi @=my_dummy_hdmi Plays explicititely on this device (by getting the id AudioSystem::listAudioPorts 4 / Calls AudioTrack->getRoutedDeviceId() The id shall match the second one, NOT the first one that does not exist any more Change-Id: I608ed8d78907b5ea1c9b6ef13c6cbfe528d88f44 Signed-off-by: François Gaffie --- services/audioflinger/Threads.cpp | 14 +++++++++----- services/audioflinger/Threads.h | 5 ++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index c6941c045d..989e6eb8f1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3850,6 +3850,7 @@ status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_pat type |= patch->sinks[i].ext.device.type; } + audio_port_handle_t sinkPortId = patch->sinks[0].id; #ifdef ADD_BATTERY_DATA // when changing the audio output device, call addBatteryData to notify // the change @@ -3879,7 +3880,7 @@ status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_pat // mPrevOutDevice is the latest device set by createAudioPatch_l(). It is not set when // the thread is created so that the first patch creation triggers an ioConfigChanged callback - bool configChanged = mPrevOutDevice != type; + bool configChanged = (mPrevOutDevice != type) || (mDeviceId != sinkPortId); mOutDevice = type; mPatch = *patch; @@ -3908,6 +3909,7 @@ status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_pat } if (configChanged) { mPrevOutDevice = type; + mDeviceId = sinkPortId; sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED); } return status; @@ -8145,6 +8147,7 @@ status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch // store new device and send to effects mInDevice = patch->sources[0].ext.device.type; + audio_port_handle_t deviceId = patch->sources[0].id; mPatch = *patch; for (size_t i = 0; i < mEffectChains.size(); i++) { mEffectChains[i]->setDevice_l(mInDevice); @@ -8186,9 +8189,10 @@ status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *handle = AUDIO_PATCH_HANDLE_NONE; } - if (mInDevice != mPrevInDevice) { + if ((mInDevice != mPrevInDevice) || (mDeviceId != deviceId)) { sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED); mPrevInDevice = mInDevice; + mDeviceId = deviceId; } return status; @@ -8285,7 +8289,7 @@ AudioFlinger::MmapThread::MmapThread( audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady) : ThreadBase(audioFlinger, id, outDevice, inDevice, MMAP, systemReady), mSessionId(AUDIO_SESSION_NONE), - mDeviceId(AUDIO_PORT_HANDLE_NONE), mPortId(AUDIO_PORT_HANDLE_NONE), + mPortId(AUDIO_PORT_HANDLE_NONE), mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev), mActiveTracks(&this->mLocalLog), mHalVolFloat(-1.0f), // Initialize to illegal value so it always gets set properly later. @@ -8769,7 +8773,7 @@ status_t AudioFlinger::MmapThread::createAudioPatch_l(const struct audio_patch * *handle = AUDIO_PATCH_HANDLE_NONE; } - if (isOutput() && mPrevOutDevice != mOutDevice) { + if (isOutput() && (mPrevOutDevice != mOutDevice || mDeviceId != deviceId)) { mPrevOutDevice = type; sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED); sp callback = mCallback.promote(); @@ -8780,7 +8784,7 @@ status_t AudioFlinger::MmapThread::createAudioPatch_l(const struct audio_patch * } mDeviceId = deviceId; } - if (!isOutput() && mPrevInDevice != mInDevice) { + if (!isOutput() && (mPrevInDevice != mInDevice || mDeviceId != deviceId)) { mPrevInDevice = type; sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED); sp callback = mCallback.promote(); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index e8b2158e71..5d06773a0e 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -485,6 +485,10 @@ protected: audio_devices_t mPrevOutDevice; // previous output device audio_devices_t mPrevInDevice; // previous input device struct audio_patch mPatch; + /** + * @brief mDeviceId current device port unique identifier + */ + audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE; audio_source_t mAudioSource; const audio_io_handle_t mId; @@ -1704,7 +1708,6 @@ class MmapThread : public ThreadBase audio_attributes_t mAttr; audio_session_t mSessionId; - audio_port_handle_t mDeviceId; audio_port_handle_t mPortId; wp mCallback; -- GitLab From fc078cd2f085fdc24a2ab7d34f90a0d5e08ae756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 22 Feb 2018 09:20:24 +0100 Subject: [PATCH 0702/1530] audiopolicy: engineconfigurable: Get rid of criteria text file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: I8c3a4dbd99f6c1ee8bac60e28b69429f1356ba30 Signed-off-by: François Gaffie --- .../parameter-framework/examples/Android.mk | 55 +++- .../Settings/PolicyConfigurableDomains.xml | 2 +- .../examples/Settings/strategy_for_usage.pfw | 2 +- .../examples/policy_criteria.txt | 10 - .../tools/build_audio_pfw_settings.mk | 35 +++ .../tools/domainGeneratorPolicy.py | 268 ++++++++++++++++++ 6 files changed, 346 insertions(+), 26 deletions(-) delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/policy_criteria.txt create mode 100644 services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk create mode 100755 services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 75067349ad..7631976b2b 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -12,10 +12,14 @@ LOCAL_PATH := $(call my-dir) ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) PFW_CORE := external/parameter-framework -BUILD_PFW_SETTINGS := $(PFW_CORE)/support/android/build_pfw_settings.mk +#@TODO: upstream new domain generator +#BUILD_PFW_SETTINGS := $(PFW_CORE)/support/android/build_pfw_settings.mk PFW_DEFAULT_SCHEMAS_DIR := $(PFW_CORE)/upstream/schemas PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk + ################################################################## # CONFIGURATION FILES ################################################################## @@ -82,13 +86,13 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - PolicyClass.xml \ - PolicySubsystem.xml \ - ParameterFrameworkConfigurationPolicy.xml + policy_criteria.xml \ + policy_criterion_types.xml \ + PolicySubsystem.xml \ + PolicyClass.xml \ + ParameterFrameworkConfigurationPolicy.xml ifeq ($(pfw_rebuild_settings),true) -PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ $(LOCAL_PATH)/Settings/device_for_strategy_phone.pfw \ @@ -104,6 +108,17 @@ PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_input_source.pfw \ $(LOCAL_PATH)/Settings/volumes.pfw +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(PFW_EDD_FILES) + + +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml + +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml + +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + include $(BUILD_PFW_SETTINGS) else # Use the existing file @@ -123,12 +138,15 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - PolicyClass.xml \ - PolicySubsystem.xml \ - ParameterFrameworkConfigurationPolicy.xml + policy_criteria.xml \ + policy_criterion_types.xml \ + PolicySubsystem.xml \ + PolicyClass.xml \ + ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoOutput/device_for_strategies.pfw \ $(LOCAL_PATH)/Settings/strategy_for_stream.pfw \ @@ -148,12 +166,15 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - PolicyClass.xml \ - PolicySubsystem.xml \ - ParameterFrameworkConfigurationPolicy.xml + policy_criteria.xml \ + policy_criterion_types.xml \ + PolicySubsystem.xml \ + PolicyClass.xml \ + ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERIA_FILE := $(LOCAL_PATH)/policy_criteria.txt +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ $(LOCAL_PATH)/Settings/device_for_strategy_phone.pfw \ @@ -172,3 +193,9 @@ PFW_EDD_FILES := \ include $(BUILD_PFW_SETTINGS) endif # ifeq (1, 0) + +####################################################################### +# Recursive call sub-folder Android.mk +####################################################################### + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml index b43f83bc0d..0710441810 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/PolicyConfigurableDomains.xml @@ -8472,7 +8472,7 @@ - + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_usage.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_usage.pfw index b3115e7f0e..fbea9e2881 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_usage.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_usage.pfw @@ -24,7 +24,7 @@ supDomain: SelectedStrategyForUsages # In case of Ring or Alarm stream type active, switching to sonification # @todo: handle this dynamic case. As a WA, using Ringtone mode... # - TelephonyMode Is RingTone + TelephonyMode Is Ringtone /Policy/policy/usages/assistance_accessibility/applicable_strategy/strategy = sonification diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/policy_criteria.txt b/services/audiopolicy/engineconfigurable/parameter-framework/examples/policy_criteria.txt deleted file mode 100644 index 480cbe1cd8..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/policy_criteria.txt +++ /dev/null @@ -1,10 +0,0 @@ -ExclusiveCriterion TelephonyMode : Normal RingTone InCall InCommunication -InclusiveCriterion AvailableInputDevices : Communication Ambient BuiltinMic BluetoothScoHeadset WiredHeadset Hdmi TelephonyRx BackMic RemoteSubmix AnlgDockHeadset DgtlDockHeadset UsbAccessory UsbDevice FmTuner TvTuner Line Spdif BluetoothA2dp Loopback Ip Bus Stub -InclusiveCriterion AvailableOutputDevices : Earpiece Speaker WiredSpeaker WiredHeadset WiredHeadphone BluetoothSco BluetoothScoHeadset BluetoothScoCarkit BluetoothA2dp BluetoothA2dpHeadphones BluetoothA2dpSpeaker Hdmi AnlgDockHeadset DgtlDockHeadset UsbAccessory UsbDevice RemoteSubmix TelephonyTx Line HdmiArc Spdif Fm AuxLine SpeakerSafe Ip Bus Stub -ExclusiveCriterion ForceUseForCommunication : ForceNone ForceSpeaker ForceBtSco -ExclusiveCriterion ForceUseForMedia : ForceNone ForceSpeaker ForceHeadphones ForceBtA2dp ForceWiredAccessory ForceAnalogDock ForceDigitalDock ForceNoBtA2dp ForceSystemEnforced -ExclusiveCriterion ForceUseForRecord : ForceNone ForceBtSco ForceWiredAccessory -ExclusiveCriterion ForceUseForDock : ForceNone ForceWiredAccessory ForceBtCarDock ForceBtDeskDock ForceAnalogDock ForceDigitalDock -ExclusiveCriterion ForceUseForSystem : ForceNone ForceSystemEnforced -ExclusiveCriterion ForceUseForHdmiSystemAudio : ForceNone ForceHdmiSystemEnforced -ExclusiveCriterion ForceUseForEncodedSurround : ForceNone ForceEncodedSurroundNever ForceEncodedSurroundAlways diff --git a/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk b/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk new file mode 100644 index 0000000000..2b8646962b --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk @@ -0,0 +1,35 @@ +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(PFW_CRITERION_TYPES_FILE) +$(LOCAL_BUILT_MODULE): MY_TOOL := domainGeneratorPolicy.py +$(LOCAL_BUILT_MODULE): MY_TOPLEVEL_FILE := $(PFW_TOPLEVEL_FILE) +$(LOCAL_BUILT_MODULE): MY_CRITERIA_FILE := $(PFW_CRITERIA_FILE) +$(LOCAL_BUILT_MODULE): MY_TUNING_FILE := $(PFW_TUNING_FILE) +$(LOCAL_BUILT_MODULE): MY_EDD_FILES := $(PFW_EDD_FILES) +$(LOCAL_BUILT_MODULE): MY_DOMAIN_FILES := $(PFW_DOMAIN_FILES) +$(LOCAL_BUILT_MODULE): MY_SCHEMAS_DIR := $(PFW_SCHEMAS_DIR) +$(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(PFW_CRITERION_TYPES_FILE) +$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) domainGeneratorPolicy.py + + "$(MY_TOOL)" --validate \ + --toplevel-config "$(MY_TOPLEVEL_FILE)" \ + --criteria "$(MY_CRITERIA_FILE)" \ + --criteriontypes "$(MY_CRITERION_TYPES_FILE)" \ + --initial-settings $(MY_TUNING_FILE) \ + --add-edds $(MY_EDD_FILES) \ + --add-domains $(MY_DOMAIN_FILES) \ + --schemas-dir $(MY_SCHEMAS_DIR) > "$@" + + +# Clear variables for further use +PFW_TOPLEVEL_FILE := +PFW_STRUCTURE_FILES := +PFW_CRITERIA_FILE := +PFW_CRITERION_TYPES_FILE := +PFW_TUNING_FILE := +PFW_EDD_FILES := +PFW_DOMAIN_FILES := +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) diff --git a/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py b/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py new file mode 100755 index 0000000000..7929402030 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py @@ -0,0 +1,268 @@ +#!/usr/bin/python + +# +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import EddParser +from PFWScriptGenerator import PfwScriptTranslator +import hostConfig + +import argparse +import re +import sys +import tempfile +import os +import logging +import subprocess +import xml.etree.ElementTree as ET + +# +# In order to build the XML Settings file at build time, an instance of the parameter-framework +# shall be started and fed with all the criterion types/criteria that will be used by +# the engineconfigurable. +# This scripts allows generates the settings from the same audio_criterion_types.xml / +# audio_criteria.xml files used at run time by the engineconfigurable +# + +def parseArgs(): + argparser = argparse.ArgumentParser(description="Parameter-Framework XML \ + Settings file generator.\n\ + Exit with the number of (recoverable or not) error that occured.") + argparser.add_argument('--toplevel-config', + help="Top-level parameter-framework configuration file. Mandatory.", + metavar="TOPLEVEL_CONFIG_FILE", + required=True) + argparser.add_argument('--criteria', + help="Criteria file, in XML format: \ + in ' \ + \ + ' \ + format. Mandatory.", + metavar="CRITERIA_FILE", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--criteriontypes', + help="Criterion types XML file, in \ + ' \ + values=/> \ + ' \ + format. Mandatory.", + metavar="CRITERION_TYPE_FILE", + type=argparse.FileType('r'), + required=False) + argparser.add_argument('--initial-settings', + help="Initial XML settings file (containing a \ + tag", + nargs='?', + default=None, + metavar="XML_SETTINGS_FILE") + argparser.add_argument('--add-domains', + help="List of single domain files (each containing a single \ + tag", + metavar="XML_DOMAIN_FILE", + nargs='*', + dest='xml_domain_files', + default=[]) + argparser.add_argument('--add-edds', + help="List of files in EDD syntax (aka \".pfw\" files)", + metavar="EDD_FILE", + type=argparse.FileType('r'), + nargs='*', + default=[], + dest='edd_files') + argparser.add_argument('--schemas-dir', + help="Directory of parameter-framework XML Schemas for generation \ + validation", + default=None) + argparser.add_argument('--target-schemas-dir', + help="Ignored. Kept for retro-compatibility") + argparser.add_argument('--validate', + help="Validate the settings against XML schemas", + action='store_true') + argparser.add_argument('--verbose', + action='store_true') + + return argparser.parse_args() + +# +# Parses audio_criterion_types.xml / audio_criteria.xml files used at run time by the +# engineconfigurable and outputs a dictionnary of criteria. +# For each criteria, the name, type (aka inclusive (bitfield) or exclusive (enum), the values +# are provided. +# +def parseCriteriaAndCriterionTypes(criteriaFile, criterionTypesFile): + # Parse criteria and criterion types XML files + # + criteria_tree = ET.parse(criteriaFile) + logging.info("Importing criteriaFile {}".format(criteriaFile)) + criterion_types_tree = ET.parse(criterionTypesFile) + logging.info("Importing criterionTypesFile {}".format(criterionTypesFile)) + + criteria_root = criteria_tree.getroot() + criterion_types_root = criterion_types_tree.getroot() + + all_criteria = [] + for criterion in criteria_root.findall('criterion'): + criterion_name = criterion.get('name') + type_name = criterion.get('type') + logging.info("Importing criterion_name {}".format(criterion_name)) + logging.info("Importing type_name {}".format(type_name)) + + for criterion_types in criterion_types_tree.findall('criterion_type'): + criterion_type_name = criterion_types.get('name') + if criterion_type_name == type_name: + criterion_inclusiveness = criterion_types.get('type') + + criterion_values = [] + + values_node = criterion_types.find('values') + if values_node: + for value in values_node.findall('value'): + criterion_values.append(value.get('literal')) + + if len(criterion_values) == 0: + criterion_values.append('') + + logging.info("Importing criterion_type_name {}".format(criterion_type_name)) + logging.info("Importing criterion_inclusiveness {}".format(criterion_inclusiveness)) + logging.info("Importing criterion_values {}".format(criterion_values)) + + all_criteria.append({ + "name" : criterion_name, + "inclusive" : criterion_inclusiveness, + "values" : criterion_values}) + break + + return all_criteria + +# +# Parses the Edd files (aka .pfw extension file), which is a simplified language to write the +# parameter framework settings. +# +def parseEdd(EDDFiles): + parsed_edds = [] + + for edd_file in EDDFiles: + try: + root = EddParser.Parser().parse(edd_file) + except EddParser.MySyntaxError as ex: + logging.critical(str(ex)) + logging.info("EXIT ON FAILURE") + exit(2) + + try: + root.propagate() + except EddParser.MyPropagationError, ex : + logging.critical(str(ex)) + logging.info("EXIT ON FAILURE") + exit(1) + + parsed_edds.append((edd_file.name, root)) + return parsed_edds + +# +# Generates all the required commands to be sent to the instance of parameter-framework launched +# at runtime to generate the XML Settings file. +# It takes as input the collection of criteria, the domains and the simplified settings read from +# pfw. +# +def generateDomainCommands(logging, all_criteria, initial_settings, xml_domain_files, parsed_edds): + # create and inject all the criteria + logging.info("Creating all criteria") + for criterion in all_criteria: + yield ["createSelectionCriterion", criterion['inclusive'], + criterion['name']] + criterion['values'] + + yield ["start"] + + # Import initial settings file + if initial_settings: + logging.info("Importing initial settings file {}".format(initial_settings)) + yield ["importDomainsWithSettingsXML", initial_settings] + + # Import each standalone domain files + for domain_file in xml_domain_files: + logging.info("Importing single domain file {}".format(domain_file)) + yield ["importDomainWithSettingsXML", domain_file] + + # Generate the script for each EDD file + for filename, parsed_edd in parsed_edds: + logging.info("Translating and injecting EDD file {}".format(filename)) + translator = PfwScriptTranslator() + parsed_edd.translate(translator) + for command in translator.getScript(): + yield command + +# +# Entry point of the domain generator. +# -Parses Criterion types and criteria files +# -Parses settings written in simplified pfw language. +# -Launches a parameter-framework +# -Translates the settings into command that can be interpreted by parameter-framework. +# -Use the exports command and output them in XML Settings file. +# +def main(): + logging.root.setLevel(logging.INFO) + args = parseArgs() + + all_criteria = parseCriteriaAndCriterionTypes(args.criteria, args.criteriontypes) + + # + # EDD files (aka ".pfw" files) + # + parsed_edds = parseEdd(args.edd_files) + + # We need to modify the toplevel configuration file to account for differences + # between development setup and target (installation) setup, in particular, the + # TuningMwith ode must be enforced, regardless of what will be allowed on the target + fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml", + prefix="TMPdomainGeneratorPFConfig_") + + install_path = os.path.dirname(os.path.realpath(args.toplevel_config)) + hostConfig.configure( + infile=args.toplevel_config, + outfile=fake_toplevel_config, + structPath=install_path) + fake_toplevel_config.close() + + # Create the connector. Pipe its input to us in order to write commands; + # connect its output to stdout in order to have it dump the domains + # there; connect its error output to stderr. + connector = subprocess.Popen(["domainGeneratorConnector", + fake_toplevel_config.name, + 'verbose' if args.verbose else 'no-verbose', + 'validate' if args.validate else 'no-validate', + args.schemas_dir], + stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr) + + initial_settings = None + if args.initial_settings: + initial_settings = os.path.realpath(args.initial_settings) + + for command in generateDomainCommands(logging, all_criteria, initial_settings, + args.xml_domain_files, parsed_edds): + connector.stdin.write('\0'.join(command)) + connector.stdin.write("\n") + + # Closing the connector's input triggers the domain generation + connector.stdin.close() + connector.wait() + os.remove(fake_toplevel_config.name) + return connector.returncode + +# If this file is directly executed +if __name__ == "__main__": + exit(main()) -- GitLab From b5894373bc83714da58cdc8ee2b86900dfc87ca3 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 10 Jan 2019 14:44:13 -0800 Subject: [PATCH 0703/1530] Add timestamps to mediaextractor dumpsys Test: manual Change-Id: I0245de9c56d42a754cbac5fc09f5014e21c29b2a --- media/libmedia/IMediaExtractor.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp index e9a6230f6d..fb6d3a2902 100644 --- a/media/libmedia/IMediaExtractor.cpp +++ b/media/libmedia/IMediaExtractor.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -219,10 +220,16 @@ typedef struct { Vector> tracks; Vector trackDescriptions; String8 toString() const; + time_t when; } ExtractorInstance; String8 ExtractorInstance::toString() const { - String8 str = name; + String8 str; + char timeString[32]; + strftime(timeString, sizeof(timeString), "%m-%d %T", localtime(&when)); + str.append(timeString); + str.append(": "); + str.append(name); str.append(" for mime "); str.append(mime); str.append(", source "); @@ -287,6 +294,7 @@ void registerMediaExtractor( ex.sourceDescription = source->toString(); ex.owner = IPCThreadState::self()->getCallingPid(); ex.extractor = extractor; + ex.when = time(NULL); { Mutex::Autolock lock(sExtractorsLock); -- GitLab From 17dc17828f0aedb037cf974daeb44645f390cb49 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Thu, 10 Jan 2019 19:00:05 -0800 Subject: [PATCH 0704/1530] Audio policy: uid device affinity deterministic rule When setting a uid device affinity, make the rules deterministic by applying an "exclude" rule on all mixes that don't reach the devices (existing), but also a "match" on the mixes that do (new). Bug: 111647296 Test: requires device with routing policy started by CarService Change-Id: I3bb844156ade75e4b9576c1e18200ca90ce26cf9 --- media/libaudioclient/AudioPolicy.cpp | 9 ++++++++- media/libaudioclient/include/media/AudioPolicy.h | 3 ++- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 8 +++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp index 9601d6dd87..3ab38cd259 100644 --- a/media/libaudioclient/AudioPolicy.cpp +++ b/media/libaudioclient/AudioPolicy.cpp @@ -143,11 +143,18 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const return NO_ERROR; } -void AudioMix::excludeUid(uid_t uid) const { +void AudioMix::setExcludeUid(uid_t uid) const { AudioMixMatchCriterion crit; crit.mRule = RULE_EXCLUDE_UID; crit.mValue.mUid = uid; mCriteria.add(crit); } +void AudioMix::setMatchUid(uid_t uid) const { + AudioMixMatchCriterion crit; + crit.mRule = RULE_MATCH_UID; + crit.mValue.mUid = uid; + mCriteria.add(crit); +} + } // namespace android diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index 96e1235e9b..786fb9ab34 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -100,7 +100,8 @@ public: status_t readFromParcel(Parcel *parcel); status_t writeToParcel(Parcel *parcel) const; - void excludeUid(uid_t uid) const; + void setExcludeUid(uid_t uid) const; + void setMatchUid(uid_t uid) const; mutable Vector mCriteria; uint32_t mMixType; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 776d98f850..4d0916ebd3 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -360,10 +360,12 @@ status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, break; } } - if (!deviceMatch) { + if (deviceMatch) { + mix->setMatchUid(uid); + } else { // this mix doesn't go to one of the listed devices for the given uid, // modify its rules to exclude the uid - mix->excludeUid(uid); + mix->setExcludeUid(uid); } } @@ -382,7 +384,7 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { for (size_t j = 0; j < mix->mCriteria.size(); j++) { const uint32_t rule = mix->mCriteria[j].mRule; // is this rule affecting the uid? - if (rule == RULE_EXCLUDE_UID + if ((rule == RULE_EXCLUDE_UID || rule == RULE_MATCH_UID) && uid == mix->mCriteria[j].mValue.mUid) { foundUidRule = true; criteriaToRemove.push_back(j); -- GitLab From 323af8e1b843976c8ae64fac31c88613e6083322 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 10 Jan 2019 12:12:38 -0800 Subject: [PATCH 0705/1530] Unify data type: change "long" to "nsecs_t" in computing Using "long" may result in overflow sometimes. Test: MediaPlayer2Test Bug: 120995219 Change-Id: I580f7fcc98ad9a811fc4ea43a64d2c530dd1cc31 --- media/libstagefright/VideoFrameScheduler2.cpp | 70 ++++++++++++------- .../media/stagefright/VideoFrameScheduler2.h | 4 +- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/media/libstagefright/VideoFrameScheduler2.cpp b/media/libstagefright/VideoFrameScheduler2.cpp index e02ae7d3a8..fc76904571 100644 --- a/media/libstagefright/VideoFrameScheduler2.cpp +++ b/media/libstagefright/VideoFrameScheduler2.cpp @@ -36,7 +36,7 @@ namespace android { -static void getVsyncOffset(long* appVsyncOffsetPtr, long* sfVsyncOffsetPtr); +static void getVsyncOffset(nsecs_t* appVsyncOffsetPtr, nsecs_t* sfVsyncOffsetPtr); /* ======================================================================= */ /* VsyncTracker */ @@ -46,19 +46,19 @@ class VsyncTracker : public RefBase{ public: VsyncTracker(); ~VsyncTracker() {} - long getVsyncPeriod(); - long getVsyncTime(long periodOffset); - void addSample(long timestamp); + nsecs_t getVsyncPeriod(); + nsecs_t getVsyncTime(nsecs_t periodOffset); + void addSample(nsecs_t timestamp); private: static const int kMaxSamples = 32; static const int kMinSamplesForUpdate = 6; int mNumSamples; int mFirstSample; - long mReferenceTime; - long mPhase; - long mPeriod; - long mTimestampSamples[kMaxSamples]; + nsecs_t mReferenceTime; + nsecs_t mPhase; + nsecs_t mPeriod; + nsecs_t mTimestampSamples[kMaxSamples]; Mutex mLock; void updateModelLocked(); @@ -75,19 +75,39 @@ VsyncTracker::VsyncTracker() } } -long VsyncTracker::getVsyncPeriod() { +nsecs_t VsyncTracker::getVsyncPeriod() { Mutex::Autolock dataLock(mLock); return mPeriod; } -long VsyncTracker::getVsyncTime(long periodOffset) { +nsecs_t VsyncTracker::getVsyncTime(nsecs_t periodOffset) { Mutex::Autolock dataLock(mLock); - const long now = systemTime(); - long phase = mReferenceTime + mPhase; - return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; + const nsecs_t now = systemTime(); + nsecs_t phase = mReferenceTime + mPhase; + + // result = (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase + // prevent overflow + nsecs_t result = (now - phase) / mPeriod; + if (result > LONG_LONG_MAX - periodOffset - 1) { + return LONG_LONG_MAX; + } else { + result += periodOffset + 1; + } + if (result > LONG_LONG_MAX / mPeriod) { + return LONG_LONG_MAX; + } else { + result *= mPeriod; + } + if (result > LONG_LONG_MAX - phase) { + return LONG_LONG_MAX; + } else { + result += phase; + } + + return result; } -void VsyncTracker::addSample(long timestamp) { +void VsyncTracker::addSample(nsecs_t timestamp) { Mutex::Autolock dataLock(mLock); if (mNumSamples == 0) { mPhase = 0; @@ -107,17 +127,17 @@ void VsyncTracker::updateModelLocked() { if (mNumSamples < kMinSamplesForUpdate) { return; } - long durationSum = 0; - long minDuration = LONG_MAX; - long maxDuration = 0; + nsecs_t durationSum = 0; + nsecs_t minDuration = LONG_MAX; + nsecs_t maxDuration = 0; for (int i = 1; i < mNumSamples; i++) { int idx = (mFirstSample + i) % kMaxSamples; int prev = (idx + kMaxSamples - 1) % kMaxSamples; long duration = mTimestampSamples[idx] - mTimestampSamples[prev]; durationSum += duration; - minDuration = min(minDuration, duration); - maxDuration = max(maxDuration, duration); + if (minDuration > duration) { minDuration = duration; } + if (maxDuration < duration) { maxDuration = duration; } } durationSum -= (minDuration + maxDuration); @@ -154,9 +174,9 @@ static void frameCallback(long frameTimeNanos, void* data) { /* JNI */ /* ======================================================================= */ -static void getVsyncOffset(long* appVsyncOffsetPtr, long* sfVsyncOffsetPtr) { - static const long kOneMillisecInNanosec = 1000000; - static const long kOneSecInNanosec = kOneMillisecInNanosec * 1000; +static void getVsyncOffset(nsecs_t* appVsyncOffsetPtr, nsecs_t* sfVsyncOffsetPtr) { + static const nsecs_t kOneMillisecInNanosec = 1000000; + static const nsecs_t kOneSecInNanosec = kOneMillisecInNanosec * 1000; JNIEnv *env = JavaVMHelper::getJNIEnv(); jclass jDisplayManagerGlobalCls = env->FindClass( @@ -178,19 +198,19 @@ static void getVsyncOffset(long* appVsyncOffsetPtr, long* sfVsyncOffsetPtr) { jmethodID jGetRefreshRate = env->GetMethodID(jDisplayCls, "getRefreshRate", "()F"); jfloat javaRefreshRate = env->CallFloatMethod(javaDisplayObj, jGetRefreshRate); - long vsyncPeriod = (long) (kOneSecInNanosec / (float) javaRefreshRate); + nsecs_t vsyncPeriod = (nsecs_t) (kOneSecInNanosec / (float) javaRefreshRate); jmethodID jGetAppVsyncOffsetNanos = env->GetMethodID( jDisplayCls, "getAppVsyncOffsetNanos", "()J"); jlong javaAppVsyncOffset = env->CallLongMethod(javaDisplayObj, jGetAppVsyncOffsetNanos); - *appVsyncOffsetPtr = (long) javaAppVsyncOffset; + *appVsyncOffsetPtr = (nsecs_t) javaAppVsyncOffset; jmethodID jGetPresentationDeadlineNanos = env->GetMethodID( jDisplayCls, "getPresentationDeadlineNanos", "()J"); jlong javaPresentationDeadline = env->CallLongMethod( javaDisplayObj, jGetPresentationDeadlineNanos); - *sfVsyncOffsetPtr = vsyncPeriod - ((long) javaPresentationDeadline - kOneMillisecInNanosec); + *sfVsyncOffsetPtr = vsyncPeriod - ((nsecs_t) javaPresentationDeadline - kOneMillisecInNanosec); } /* ======================================================================= */ diff --git a/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h b/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h index be911cc576..891b9a05cb 100644 --- a/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h +++ b/media/libstagefright/include/media/stagefright/VideoFrameScheduler2.h @@ -34,8 +34,8 @@ protected: private: void updateVsync() override; - long mAppVsyncOffset; - long mSfVsyncOffset; + nsecs_t mAppVsyncOffset; + nsecs_t mSfVsyncOffset; sp mVsyncTracker; sp mChoreographerThread; Mutex mLock; -- GitLab From 103639a8d35be7f84ed7f2638c30d76dddcc28e9 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Fri, 11 Jan 2019 14:30:09 +0800 Subject: [PATCH 0706/1530] Suppress null-dereference warning temporarily Bug: 122154294 Test: m checkbuild Change-Id: I009c7b30aceadf4323170972d75a5824a3c721a5 --- media/codec2/core/include/C2Param.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h index 40be3b3f1b..bedd01bea1 100644 --- a/media/codec2/core/include/C2Param.h +++ b/media/codec2/core/include/C2Param.h @@ -988,7 +988,10 @@ struct C2FieldDescriptor { /** specialization for easy enums */ template inline static NamedValuesType namedValuesFor(const C2EasyEnum &) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" return namedValuesFor(*(E*)nullptr); +#pragma GCC diagnostic pop } private: @@ -1104,7 +1107,10 @@ struct C2FieldDescriptor::_NamedValuesGetter { template struct C2FieldDescriptor::_NamedValuesGetter { inline static C2FieldDescriptor::NamedValuesType getNamedValues() { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" return C2FieldDescriptor::namedValuesFor(*(B*)nullptr); +#pragma GCC diagnostic pop } }; -- GitLab From 09948322c9c54035039e65ce602e60125e23cb27 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 11 Jan 2019 08:17:26 -0800 Subject: [PATCH 0707/1530] Fix crash in MPEG2PSExtractor Bug: 122116060 Test: CTS Change-Id: I28175354ba25800adea0f10620411fdb9a3a093a --- media/extractors/mpeg2/MPEG2PSExtractor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index 554d252e35..5163f26918 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -790,6 +790,7 @@ MPEG2PSExtractor::WrappedTrack::~WrappedTrack() { } media_status_t MPEG2PSExtractor::WrappedTrack::start() { + mTrack->mBufferGroup = mBufferGroup; return mTrack->start(); } -- GitLab From c6148a6ad7c2636aa96fa20024953046e851ef93 Mon Sep 17 00:00:00 2001 From: Sampath Shetty Date: Tue, 18 Dec 2018 15:50:43 +1100 Subject: [PATCH 0708/1530] Send audio-presentation metadata in access units Audio presentation metadata is attached to the access units so that each media buffer read() retreives it's associated audio presentations information. Bug: 120846068 Test: atest CtsMediaTestCases:MediaExtractorTest#testGetAudioPresentations repeat 3..5 times Change-Id: I47f4d0dd959537fe98aa757bfa762ff02aa86a35 --- media/extractors/mpeg2/MPEG2PSExtractor.cpp | 3 ++ media/extractors/mpeg2/MPEG2TSExtractor.cpp | 3 ++ media/libstagefright/MediaTrack.cpp | 4 +++ media/libstagefright/NuMediaExtractor.cpp | 36 ++++++++++--------- .../media/stagefright/NuMediaExtractor.h | 3 +- media/libstagefright/mpeg2ts/ATSParser.cpp | 18 ++++++++-- .../mpeg2ts/AnotherPacketSource.cpp | 25 ++++--------- .../mpeg2ts/AnotherPacketSource.h | 2 -- media/ndk/NdkMediaExtractor.cpp | 10 ++++++ 9 files changed, 63 insertions(+), 41 deletions(-) diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index 554d252e35..b99fde9886 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -734,6 +734,9 @@ media_status_t MPEG2PSExtractor::Track::read( if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } + if (inMeta.findData(kKeyAudioPresentationInfo, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, bufData, bufSize); + } mbuf->release(); return AMEDIA_OK; } diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 3bb2af7572..4ed6098847 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -224,6 +224,9 @@ media_status_t MPEG2TSSource::read( if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } + if (inMeta.findData(kKeyAudioPresentationInfo, &bufType, &bufData, &bufSize)) { + AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, bufData, bufSize); + } mbuf->release(); return AMEDIA_OK; } diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 1c1be30ab6..4669cc9a9d 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -172,6 +172,10 @@ status_t MediaTrackCUnwrapper::read(MediaBufferBase **buffer, const ReadOptions meta.setData(kKeySEI, MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); } + if (format->mFormat->findBuffer("audio-presentation-info", &valbuf)) { + meta.setData(kKeyAudioPresentationInfo, + MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size()); + } } else { *buffer = nullptr; } diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index f5178ddb32..779372d749 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -795,30 +795,34 @@ bool NuMediaExtractor::getCachedDuration( } // Return OK if we have received an audio presentation info. +// Return ERROR_END_OF_STREAM if no tracks are available. // Return ERROR_UNSUPPORTED if the track has no audio presentation. // Return INVALID_OPERATION if audio presentation metadata version does not match. status_t NuMediaExtractor::getAudioPresentations( - size_t trackIndex, AudioPresentationCollection *presentations) const { + size_t trackIndex, AudioPresentationCollection *presentations) { Mutex::Autolock autoLock(mLock); - - if (mImpl == NULL) { - return -EINVAL; - } - - if (trackIndex >= mImpl->countTracks()) { - return -ERANGE; + ssize_t minIndex = fetchAllTrackSamples(); + if (minIndex < 0) { + return ERROR_END_OF_STREAM; } + for (size_t i = 0; i < mSelectedTracks.size(); ++i) { + TrackInfo *info = &mSelectedTracks.editItemAt(i); - sp meta = mImpl->getTrackMetaData(trackIndex); + if (info->mTrackIndex == trackIndex) { + sp meta = new MetaData(info->mSamples.begin()->mBuffer->meta_data()); - uint32_t type; - const void *data; - size_t size; - if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) { - std::istringstream inStream(std::string(static_cast(data), size)); - return deserializeAudioPresentations(&inStream, presentations); + uint32_t type; + const void *data; + size_t size; + if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) { + std::istringstream inStream(std::string(static_cast(data), size)); + return deserializeAudioPresentations(&inStream, presentations); + } + ALOGV("Track %zu does not contain any audio presentation", trackIndex); + return ERROR_UNSUPPORTED; + } } - ALOGE("Source does not contain any audio presentation"); + ALOGV("Source does not contain any audio presentation"); return ERROR_UNSUPPORTED; } diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h index 8dc8d388ee..f34f9b63c9 100644 --- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h @@ -96,8 +96,7 @@ struct NuMediaExtractor : public RefBase { bool getCachedDuration(int64_t *durationUs, bool *eos) const; - status_t getAudioPresentations(size_t trackIdx, - AudioPresentationCollection *presentations) const; + status_t getAudioPresentations(size_t trackIdx, AudioPresentationCollection *presentations); protected: virtual ~NuMediaExtractor(); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index e9baa1a35c..7ce9deac27 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -217,6 +217,9 @@ private: sp mDescrambler; AudioPresentationCollection mAudioPresentations; + // Send audio presentations along with access units. + void addAudioPresentations(const sp &buffer); + // Flush accumulated payload if necessary --- i.e. at EOS or at the start of // another payload. event is set if the flushed payload is PES with a sync // frame. @@ -1708,6 +1711,13 @@ status_t ATSParser::Stream::flush(SyncEvent *event) { return err; } +void ATSParser::Stream::addAudioPresentations(const sp &buffer) { + std::ostringstream outStream(std::ios::out); + serializeAudioPresentations(mAudioPresentations, &outStream); + sp ap = ABuffer::CreateAsCopy(outStream.str().data(), outStream.str().size()); + buffer->meta()->setBuffer("audio-presentation-info", ap); +} + void ATSParser::Stream::onPayloadData( unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */, unsigned PES_scrambling_control, @@ -1758,8 +1768,10 @@ void ATSParser::Stream::onPayloadData( } } mSource = new AnotherPacketSource(meta); + if (mAudioPresentations.size() > 0) { + addAudioPresentations(accessUnit); + } mSource->queueAccessUnit(accessUnit); - mSource->convertAudioPresentationInfoToMetadata(mAudioPresentations); ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x", mElementaryPID, mStreamType); } @@ -1771,8 +1783,10 @@ void ATSParser::Stream::onPayloadData( if (mSource->getFormat() == NULL) { mSource->setFormat(mQueue->getFormat()); } + if (mAudioPresentations.size() > 0) { + addAudioPresentations(accessUnit); + } mSource->queueAccessUnit(accessUnit); - mSource->convertAudioPresentationInfoToMetadata(mAudioPresentations); } // Every access unit has a pesStartOffset queued in |mPesStartOffsets|. diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index e2c50314fb..3cdd228e73 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -223,6 +223,12 @@ status_t AnotherPacketSource::read( kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size()); } + sp ap; + if (buffer->meta()->findBuffer("audio-presentation-info", &ap) && ap != NULL) { + bufmeta.setData( + kKeyAudioPresentationInfo, 0, ap->data(), ap->size()); + } + int32_t cryptoMode; if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) { int32_t cryptoKey; @@ -697,23 +703,4 @@ sp AnotherPacketSource::trimBuffersBeforeMeta( return firstMeta; } -void AnotherPacketSource::convertAudioPresentationInfoToMetadata( - const AudioPresentationCollection& presentations) { - sp meta = getFormat(); - if (meta == NULL) { - return; - } - if (presentations.empty()) { - // Clear audio presentation info in metadata. - Mutex::Autolock autoLock(mLock); - meta->remove(kKeyAudioPresentationInfo); - } else { - std::ostringstream outStream(std::ios::out); - serializeAudioPresentations(presentations, &outStream); - Mutex::Autolock autoLock(mLock); - meta->setData(kKeyAudioPresentationInfo, MetaData::TYPE_NONE, - outStream.str().data(), outStream.str().size()); - } -} - } // namespace android diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index 57a6c33981..f4a6acbe27 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -85,8 +85,6 @@ struct AnotherPacketSource : public MediaSource { void trimBuffersAfterMeta(const sp &meta); sp trimBuffersBeforeMeta(const sp &meta); - void convertAudioPresentationInfoToMetadata(const AudioPresentationCollection &presentations); - protected: virtual ~AnotherPacketSource(); diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index f697bd1af4..8296598d44 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -448,6 +448,16 @@ media_status_t AMediaExtractor_getSampleFormat(AMediaExtractor *ex, AMediaFormat meta->setBuffer(AMEDIAFORMAT_KEY_MPEG_USER_DATA, mpegUserData); } + const void *audioPresentationsPointer; + size_t audioPresentationsLength; + if (sampleMeta->findData( + kKeyAudioPresentationInfo, &dataType, + &audioPresentationsPointer, &audioPresentationsLength)) { + sp audioPresentationsData = ABuffer::CreateAsCopy( + audioPresentationsPointer, audioPresentationsLength); + meta->setBuffer(AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, audioPresentationsData); + } + return AMEDIA_OK; } -- GitLab From df2e359d5c19c142fe400980cd15b0b60bea507b Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 19 Dec 2018 14:27:42 -0800 Subject: [PATCH 0709/1530] AudioPolicy: Add unit tests for the MSD use cases Test the following scenarios: - inputs / outputs update due to setForceUse change; - MSD device selection and downstream patch creation when an output for a supported format is opened; - default device selection and downstream patch teardown when an output for an unsupported format is opened; - swiching outputs depending on the format. Bug: 121048134 Test: audiopolicy_tests Change-Id: I3a07c758ea7fd87250f425c27fbf0c38eb5bf8b5 --- .../include/AudioPolicyConfig.h | 8 +- .../tests/audiopolicymanager_tests.cpp | 238 +++++++++++++++++- 2 files changed, 229 insertions(+), 17 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index 5099ebb94f..d52eb3d35c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -133,18 +133,14 @@ public: mDefaultOutputDevice->attach(module); defaultInputDevice->attach(module); - sp outProfile; - outProfile = new OutputProfile(String8("primary")); - outProfile->attach(module); + sp outProfile = new OutputProfile(String8("primary")); outProfile->addAudioProfile( new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 44100)); outProfile->addSupportedDevice(mDefaultOutputDevice); outProfile->setFlags(AUDIO_OUTPUT_FLAG_PRIMARY); module->addOutputProfile(outProfile); - sp inProfile; - inProfile = new InputProfile(String8("primary")); - inProfile->attach(module); + sp inProfile = new InputProfile(String8("primary")); inProfile->addAudioProfile(micProfile); inProfile->addSupportedDevice(defaultInputDevice); module->addInputProfile(inProfile); diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp index 56af1528c0..24326bb75b 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -112,10 +112,34 @@ class AudioPolicyManagerTestClient : public AudioPolicyTestClient { std::set mActivePatches; }; +class PatchCountCheck { + public: + explicit PatchCountCheck(AudioPolicyManagerTestClient *client) + : mClient{client}, + mInitialCount{mClient->getActivePatchesCount()} {} + void assertDelta(int delta) const { + ASSERT_EQ(mInitialCount + delta, mClient->getActivePatchesCount()); } + void assertNoChange() const { assertDelta(0); } + private: + const AudioPolicyManagerTestClient *mClient; + const size_t mInitialCount; +}; + class AudioPolicyManagerTest : public testing::Test { protected: - virtual void SetUp(); - virtual void TearDown(); + void SetUp() override; + void TearDown() override; + virtual void SetUpConfig(AudioPolicyConfig *config) { (void)config; } + + void dumpToLog(); + void getOutputForAttr( + audio_port_handle_t *selectedDeviceId, + audio_format_t format, + int channelMask, + int sampleRate, + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + audio_port_handle_t *portId = nullptr); + PatchCountCheck snapPatchCount() { return PatchCountCheck(mClient.get()); } std::unique_ptr mClient; std::unique_ptr mManager; @@ -125,6 +149,7 @@ void AudioPolicyManagerTest::SetUp() { mClient.reset(new AudioPolicyManagerTestClient); mManager.reset(new AudioPolicyTestManager(mClient.get())); mManager->getConfig().setDefault(); + SetUpConfig(&mManager->getConfig()); // Subclasses may want to customize the config. ASSERT_EQ(NO_ERROR, mManager->initialize()); ASSERT_EQ(NO_ERROR, mManager->initCheck()); } @@ -134,11 +159,7 @@ void AudioPolicyManagerTest::TearDown() { mClient.reset(); } -TEST_F(AudioPolicyManagerTest, InitSuccess) { - // SetUp must finish with no assertions. -} - -TEST_F(AudioPolicyManagerTest, Dump) { +void AudioPolicyManagerTest::dumpToLog() { int pipefd[2]; ASSERT_NE(-1, pipe(pipefd)); pid_t cpid = fork(); @@ -168,10 +189,43 @@ TEST_F(AudioPolicyManagerTest, Dump) { } } +void AudioPolicyManagerTest::getOutputForAttr( + audio_port_handle_t *selectedDeviceId, + audio_format_t format, + int channelMask, + int sampleRate, + audio_output_flags_t flags, + audio_port_handle_t *portId) { + audio_attributes_t attr = {}; + audio_io_handle_t output = AUDIO_PORT_HANDLE_NONE; + audio_stream_type_t stream = AUDIO_STREAM_DEFAULT; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = sampleRate; + config.channel_mask = channelMask; + config.format = format; + *selectedDeviceId = AUDIO_PORT_HANDLE_NONE; + audio_port_handle_t localPortId; + if (!portId) portId = &localPortId; + *portId = AUDIO_PORT_HANDLE_NONE; + ASSERT_EQ(OK, mManager->getOutputForAttr( + &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags, + selectedDeviceId, portId)); + ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId); +} + + +TEST_F(AudioPolicyManagerTest, InitSuccess) { + // SetUp must finish with no assertions. +} + +TEST_F(AudioPolicyManagerTest, Dump) { + dumpToLog(); +} + TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { audio_patch patch{}; audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; - const size_t patchCountBefore = mClient->getActivePatchesCount(); + const PatchCountCheck patchCount = snapPatchCount(); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(nullptr, &handle, 0)); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, nullptr, 0)); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0)); @@ -198,20 +252,182 @@ TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0)); // Verify that the handle is left unchanged. ASSERT_EQ(AUDIO_PATCH_HANDLE_NONE, handle); - ASSERT_EQ(patchCountBefore, mClient->getActivePatchesCount()); + patchCount.assertNoChange(); } TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) { audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; uid_t uid = 42; - const size_t patchCountBefore = mClient->getActivePatchesCount(); + const PatchCountCheck patchCount = snapPatchCount(); ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty()); PatchBuilder patchBuilder; patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]). addSink(mManager->getConfig().getDefaultOutputDevice()); ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid)); ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle); - ASSERT_EQ(patchCountBefore + 1, mClient->getActivePatchesCount()); + patchCount.assertDelta(1); } // TODO: Add patch creation tests that involve already existing patch + +class AudioPolicyManagerTestMsd : public AudioPolicyManagerTest { + protected: + void SetUpConfig(AudioPolicyConfig *config) override; + void TearDown() override; + + sp mMsdOutputDevice; + sp mMsdInputDevice; +}; + +void AudioPolicyManagerTestMsd::SetUpConfig(AudioPolicyConfig *config) { + // TODO: Consider using Serializer to load part of the config from a string. + mMsdOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_BUS); + sp pcmOutputProfile = new AudioProfile( + AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000); + sp ac3OutputProfile = new AudioProfile( + AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000); + mMsdOutputDevice->addAudioProfile(pcmOutputProfile); + mMsdOutputDevice->addAudioProfile(ac3OutputProfile); + mMsdInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUS); + // Match output profile from AudioPolicyConfig::setDefault. + sp pcmInputProfile = new AudioProfile( + AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, 44100); + mMsdInputDevice->addAudioProfile(pcmInputProfile); + config->addAvailableDevice(mMsdOutputDevice); + config->addAvailableDevice(mMsdInputDevice); + + sp msdModule = new HwModule(AUDIO_HARDWARE_MODULE_ID_MSD, 2 /*halVersionMajor*/); + HwModuleCollection modules = config->getHwModules(); + modules.add(msdModule); + config->setHwModules(modules); + mMsdOutputDevice->attach(msdModule); + mMsdInputDevice->attach(msdModule); + + sp msdOutputProfile = new OutputProfile(String8("msd input")); + msdOutputProfile->addAudioProfile(pcmOutputProfile); + msdOutputProfile->addSupportedDevice(mMsdOutputDevice); + msdModule->addOutputProfile(msdOutputProfile); + sp msdCompressedOutputProfile = + new OutputProfile(String8("msd compressed input")); + msdCompressedOutputProfile->addAudioProfile(ac3OutputProfile); + msdCompressedOutputProfile->setFlags( + AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | + AUDIO_OUTPUT_FLAG_NON_BLOCKING); + msdCompressedOutputProfile->addSupportedDevice(mMsdOutputDevice); + msdModule->addOutputProfile(msdCompressedOutputProfile); + + sp msdInputProfile = new InputProfile(String8("msd output")); + msdInputProfile->addAudioProfile(pcmInputProfile); + msdInputProfile->addSupportedDevice(mMsdInputDevice); + msdModule->addInputProfile(msdInputProfile); + + // Add a profile with another encoding to the default device to test routing + // of streams that are not supported by MSD. + sp dtsOutputProfile = new AudioProfile( + AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000); + config->getDefaultOutputDevice()->addAudioProfile(dtsOutputProfile); + sp primaryEncodedOutputProfile = new OutputProfile(String8("encoded")); + primaryEncodedOutputProfile->addAudioProfile(dtsOutputProfile); + primaryEncodedOutputProfile->setFlags(AUDIO_OUTPUT_FLAG_DIRECT); + primaryEncodedOutputProfile->addSupportedDevice(config->getDefaultOutputDevice()); + config->getHwModules().getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)-> + addOutputProfile(primaryEncodedOutputProfile); +} + +void AudioPolicyManagerTestMsd::TearDown() { + mMsdOutputDevice.clear(); + mMsdInputDevice.clear(); + AudioPolicyManagerTest::TearDown(); +} + +TEST_F(AudioPolicyManagerTestMsd, InitSuccess) { + ASSERT_TRUE(mMsdOutputDevice); + ASSERT_TRUE(mMsdInputDevice); +} + +TEST_F(AudioPolicyManagerTestMsd, Dump) { + dumpToLog(); +} + +TEST_F(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) { + const PatchCountCheck patchCount = snapPatchCount(); + mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, + AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS); + patchCount.assertDelta(1); +} + +TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(1); +} + +TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(1); +} + +TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(1); + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(1); +} + +TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); + ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertNoChange(); +} + +TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) { + // Switch between formats that are supported and not supported by MSD. + { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId, portId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT, + &portId); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(1); + mManager->releaseOutput(portId); + patchCount.assertDelta(1); // compared to the state at the block entry + // TODO: make PatchCountCheck asserts more obvious. It's easy to + // miss the fact that it is immutable. + } + { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId, portId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT, + &portId); + ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertDelta(-1); + mManager->releaseOutput(portId); + patchCount.assertNoChange(); + } + { + const PatchCountCheck patchCount = snapPatchCount(); + audio_port_handle_t selectedDeviceId; + getOutputForAttr(&selectedDeviceId, + AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); + ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); + patchCount.assertNoChange(); + } +} -- GitLab From 937dba18c32af5030999daf8d4e9aa3d4cf3b705 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Sun, 13 Jan 2019 20:43:23 +0800 Subject: [PATCH 0710/1530] Suppress null-dereference warning temporarily There's one more occurance of this warning. In file included from hardware/google/av/codec2/tests/C2SampleComponent_test.cpp:23: In file included from frameworks/av/media/codec2/core/include/C2Component.h:29: In file included from frameworks/av/media/codec2/core/include/C2Enum.h:20: frameworks/av/media/codec2/core/include/C2Param.h:1612:92: error: binding dereferenced null pointer to reference has undefined behavior [-Werror,-Wnull-dereference] C2FieldDescriptor::NamedValuesType named = C2FieldDescriptor::namedValuesFor(*(T*)0); ^~~~~~ Also modernised the code to use nullptr instead of 0. Bug: 122154294 Test: m checkbuild Exempt-From-Owner-Approval: No functionality change, similar CL already reviewed by owner Change-Id: Ieec3335d2f52f0c6f07be1e7c99e4f36c882dabf --- media/codec2/core/include/C2Param.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h index bedd01bea1..efc5c8944c 100644 --- a/media/codec2/core/include/C2Param.h +++ b/media/codec2/core/include/C2Param.h @@ -1605,11 +1605,13 @@ struct C2FieldSupportedValues { /// \internal /// \todo: create separate values vs. flags initializer as for flags we want /// to list both allowed and required flags - template +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" + template C2FieldSupportedValues(bool flags, const T*) : type(flags ? FLAGS : VALUES), range{(T)0, (T)0, (T)0, (T)0, (T)0} { - C2FieldDescriptor::NamedValuesType named = C2FieldDescriptor::namedValuesFor(*(T*)0); + C2FieldDescriptor::NamedValuesType named = C2FieldDescriptor::namedValuesFor(*(T*)nullptr); if (flags) { values.emplace_back(0); // min-mask defaults to 0 } @@ -1618,6 +1620,7 @@ struct C2FieldSupportedValues { } } }; +#pragma GCC diagnostic pop /** * Supported values for a specific field. -- GitLab From 54c88096751d736a4b91d1f3eb0c6e0d5c3470ea Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 11 Jan 2019 14:06:15 -0800 Subject: [PATCH 0711/1530] Remove libmediaextractor Move DataSourceBase and MediaSource to libstagefright, and MediaBuffer, MediaBufferGroup and MetaData to libstagefright_foundation. Test: build Change-Id: I4f0ae01d40a32f4b773a2f74d5f2ae136e375322 --- cmds/stagefright/Android.mk | 12 +- include/media/DataSource.h | 2 +- include/media/DataSourceBase.h | 2 +- include/media/ExtractorUtils.h | 33 +- include/media/MediaSource.h | 2 +- media/codec2/components/cmds/Android.bp | 1 - media/extractors/mpeg2/Android.bp | 3 +- media/libmedia/Android.bp | 2 - media/libmediaextractor/Android.bp | 60 ---- .../include/media/ExtractorUtils.h | 32 -- .../include/media/stagefright/MediaBuffer.h | 232 ------------- .../media/stagefright/MediaBufferBase.h | 136 -------- .../media/stagefright/MediaBufferGroup.h | 114 ------- .../include/media/stagefright/MetaData.h | 49 --- .../include/media/stagefright/MetaDataBase.h | 318 ----------------- media/libmediaplayer2/Android.bp | 1 - media/libmediaplayerservice/Android.bp | 1 - media/libstagefright/Android.bp | 3 +- .../DataSourceBase.cpp | 0 .../MediaSource.cpp | 0 media/libstagefright/foundation/Android.bp | 91 +++++ .../foundation}/MediaBuffer.cpp | 0 .../foundation}/MediaBufferBase.cpp | 0 .../foundation}/MediaBufferGroup.cpp | 0 .../foundation}/MetaData.cpp | 0 .../foundation}/MetaDataBase.cpp | 0 media/libstagefright/httplive/Android.bp | 1 - .../include/media/stagefright}/DataSource.h | 0 .../media/stagefright}/DataSourceBase.h | 0 .../include/media/stagefright/MediaBuffer.h | 233 ++++++++++++- .../media/stagefright/MediaBufferBase.h | 137 +++++++- .../media/stagefright/MediaBufferGroup.h | 115 ++++++- .../include/media/stagefright}/MediaSource.h | 0 .../include/media/stagefright/MetaData.h | 50 ++- .../include/media/stagefright/MetaDataBase.h | 319 +++++++++++++++++- media/libstagefright/omx/tests/Android.bp | 1 - media/ndk/Android.bp | 1 - 37 files changed, 984 insertions(+), 967 deletions(-) mode change 120000 => 100644 include/media/ExtractorUtils.h delete mode 100644 media/libmediaextractor/Android.bp delete mode 100644 media/libmediaextractor/include/media/ExtractorUtils.h delete mode 100644 media/libmediaextractor/include/media/stagefright/MediaBuffer.h delete mode 100644 media/libmediaextractor/include/media/stagefright/MediaBufferBase.h delete mode 100644 media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h delete mode 100644 media/libmediaextractor/include/media/stagefright/MetaData.h delete mode 100644 media/libmediaextractor/include/media/stagefright/MetaDataBase.h rename media/{libmediaextractor => libstagefright}/DataSourceBase.cpp (100%) rename media/{libmediaextractor => libstagefright}/MediaSource.cpp (100%) rename media/{libmediaextractor => libstagefright/foundation}/MediaBuffer.cpp (100%) rename media/{libmediaextractor => libstagefright/foundation}/MediaBufferBase.cpp (100%) rename media/{libmediaextractor => libstagefright/foundation}/MediaBufferGroup.cpp (100%) rename media/{libmediaextractor => libstagefright/foundation}/MetaData.cpp (100%) rename media/{libmediaextractor => libstagefright/foundation}/MetaDataBase.cpp (100%) rename media/{libmediaextractor/include/media => libstagefright/include/media/stagefright}/DataSource.h (100%) rename media/{libmediaextractor/include/media => libstagefright/include/media/stagefright}/DataSourceBase.h (100%) mode change 120000 => 100644 media/libstagefright/include/media/stagefright/MediaBuffer.h mode change 120000 => 100644 media/libstagefright/include/media/stagefright/MediaBufferBase.h mode change 120000 => 100644 media/libstagefright/include/media/stagefright/MediaBufferGroup.h rename media/{libmediaextractor/include/media => libstagefright/include/media/stagefright}/MediaSource.h (100%) mode change 120000 => 100644 media/libstagefright/include/media/stagefright/MetaData.h mode change 120000 => 100644 media/libstagefright/include/media/stagefright/MetaDataBase.h diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 7a10302cfc..6eb2e9f07b 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -8,7 +8,7 @@ LOCAL_SRC_FILES:= \ SineSource.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libmedia_omx libmediaextractor libutils libbinder \ + libstagefright libmedia libmedia_omx libutils libbinder \ libstagefright_foundation libjpeg libui libgui libcutils liblog \ libhidlbase \ android.hardware.media.omx@1.0 \ @@ -36,7 +36,7 @@ LOCAL_SRC_FILES:= \ record.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libmediaextractor liblog libutils libbinder \ + libstagefright libmedia liblog libutils libbinder \ libstagefright_foundation LOCAL_C_INCLUDES:= \ @@ -61,7 +61,7 @@ LOCAL_SRC_FILES:= \ recordvideo.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libmediaextractor liblog libutils libbinder \ + libstagefright libmedia liblog libutils libbinder \ libstagefright_foundation LOCAL_C_INCLUDES:= \ @@ -87,7 +87,7 @@ LOCAL_SRC_FILES:= \ audioloop.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libmediaextractor liblog libutils libbinder \ + libstagefright libmedia liblog libutils libbinder \ libstagefright_foundation LOCAL_C_INCLUDES:= \ @@ -111,7 +111,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libui libgui \ - libstagefright_foundation libmedia libcutils libmediaextractor + libstagefright_foundation libmedia libcutils LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ @@ -203,7 +203,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation \ - libcutils libc libmediaextractor + libcutils libc LOCAL_C_INCLUDES:= \ frameworks/av/media/libstagefright \ diff --git a/include/media/DataSource.h b/include/media/DataSource.h index 905bec1118..198b27eaa4 120000 --- a/include/media/DataSource.h +++ b/include/media/DataSource.h @@ -1 +1 @@ -../../media/libmediaextractor/include/media/DataSource.h \ No newline at end of file +stagefright/DataSource.h \ No newline at end of file diff --git a/include/media/DataSourceBase.h b/include/media/DataSourceBase.h index 54c80476c5..d2ab2f17d1 120000 --- a/include/media/DataSourceBase.h +++ b/include/media/DataSourceBase.h @@ -1 +1 @@ -../../media/libmediaextractor/include/media/DataSourceBase.h \ No newline at end of file +stagefright/DataSourceBase.h \ No newline at end of file diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h deleted file mode 120000 index e2dd082066..0000000000 --- a/include/media/ExtractorUtils.h +++ /dev/null @@ -1 +0,0 @@ -../../media/libmediaextractor/include/media/ExtractorUtils.h \ No newline at end of file diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h new file mode 100644 index 0000000000..22f9349272 --- /dev/null +++ b/include/media/ExtractorUtils.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef EXTRACTOR_UTILS_H_ + +#define EXTRACTOR_UTILS_H_ + +#include + +namespace android { + +template +std::unique_ptr heapbuffer(size_t size) { + return std::unique_ptr(new (std::nothrow) T[size]); +} + +} // namespace android + +#endif // UTILS_H_ diff --git a/include/media/MediaSource.h b/include/media/MediaSource.h index 2e147c41a4..34bf65dc62 120000 --- a/include/media/MediaSource.h +++ b/include/media/MediaSource.h @@ -1 +1 @@ -../../media/libmediaextractor/include/media/MediaSource.h \ No newline at end of file +../../media/libstagefright/include/media/stagefright/MediaSource.h \ No newline at end of file diff --git a/media/codec2/components/cmds/Android.bp b/media/codec2/components/cmds/Android.bp index 6b0977b0a8..35f689e1bd 100644 --- a/media/codec2/components/cmds/Android.bp +++ b/media/codec2/components/cmds/Android.bp @@ -15,7 +15,6 @@ cc_binary { "libcutils", "libgui", "liblog", - "libmediaextractor", "libstagefright", "libstagefright_foundation", "libui", diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index b8160930f3..2a94671207 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -28,9 +28,8 @@ cc_library_shared { static_libs: [ "libcrypto", - "libstagefright_foundation", + "libstagefright_foundation_without_imemory", "libstagefright_mpeg2support", - "libmediaextractor", "libutils", "libstagefright", "libstagefright_esds", diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 7d759e08ed..3efb5de3fa 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -220,7 +220,6 @@ cc_library { "libexpat", "libcamera_client", "libstagefright_foundation", - "libmediaextractor", "libgui", "libdl", "libaudioutils", @@ -277,7 +276,6 @@ cc_library_static { "libbinder", "libcutils", "liblog", - "libmediaextractor", "libmediandk", "libnativewindow", "libmediandk_utils", diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp deleted file mode 100644 index 4758cd65e8..0000000000 --- a/media/libmediaextractor/Android.bp +++ /dev/null @@ -1,60 +0,0 @@ -cc_library { - name: "libmediaextractor", - - include_dirs: [ - "frameworks/av/include", - "frameworks/av/media/libmediaextractor/include", - ], - - export_include_dirs: ["include"], - - cflags: [ - "-Wno-multichar", - "-Werror", - "-Wall", - ], - - static: { - cflags: [ - "-Wno-multichar", - "-Werror", - "-Wall", - "-DNO_IMEMORY", - ], - }, - - shared_libs: [ - "libbinder", - "libstagefright_foundation", - "libutils", - "liblog", - ], - - header_libs: [ - "media_ndk_headers", - ], - - export_header_lib_headers: [ - "media_ndk_headers", - ], - - srcs: [ - "DataSourceBase.cpp", - "MediaBuffer.cpp", - "MediaBufferBase.cpp", - "MediaBufferGroup.cpp", - "MediaSource.cpp", - "MetaData.cpp", - "MetaDataBase.cpp", - ], - - clang: true, - - sanitize: { - misc_undefined: [ - "unsigned-integer-overflow", - "signed-integer-overflow", - ], - cfi: true, - }, -} diff --git a/media/libmediaextractor/include/media/ExtractorUtils.h b/media/libmediaextractor/include/media/ExtractorUtils.h deleted file mode 100644 index 22f9349272..0000000000 --- a/media/libmediaextractor/include/media/ExtractorUtils.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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. - */ - -#ifndef EXTRACTOR_UTILS_H_ - -#define EXTRACTOR_UTILS_H_ - -#include - -namespace android { - -template -std::unique_ptr heapbuffer(size_t size) { - return std::unique_ptr(new (std::nothrow) T[size]); -} - -} // namespace android - -#endif // UTILS_H_ diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h deleted file mode 100644 index ace63ae9f8..0000000000 --- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_BUFFER_H_ - -#define MEDIA_BUFFER_H_ - -#include -#include - -#include - -#include -#include -#include -#include - -namespace android { - -struct ABuffer; -class MediaBuffer; -class MediaBufferObserver; -class MetaDataBase; - -class MediaBuffer : public MediaBufferBase { -public: - // allocations larger than or equal to this will use shared memory. - static const size_t kSharedMemThreshold = 64 * 1024; - - // The underlying data remains the responsibility of the caller! - MediaBuffer(void *data, size_t size); - - explicit MediaBuffer(size_t size); - - explicit MediaBuffer(const sp &buffer); -#ifndef NO_IMEMORY - MediaBuffer(const sp &mem) : - MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) { - // delegate and override mMemory - mMemory = mem; - } -#endif - - // If MediaBufferGroup is set, decrement the local reference count; - // if the local reference count drops to 0, return the buffer to the - // associated MediaBufferGroup. - // - // If no MediaBufferGroup is set, the local reference count must be zero - // when called, whereupon the MediaBuffer is deleted. - virtual void release(); - - // Increments the local reference count. - // Use only when MediaBufferGroup is set. - virtual void add_ref(); - - virtual void *data() const; - virtual size_t size() const; - - virtual size_t range_offset() const; - virtual size_t range_length() const; - - virtual void set_range(size_t offset, size_t length); - - MetaDataBase& meta_data(); - - // Clears meta data and resets the range to the full extent. - virtual void reset(); - - virtual void setObserver(MediaBufferObserver *group); - - // sum of localRefcount() and remoteRefcount() - // Result should be treated as approximate unless the result precludes concurrent accesses. - virtual int refcount() const { - return localRefcount() + remoteRefcount(); - } - - // Result should be treated as approximate unless the result precludes concurrent accesses. - virtual int localRefcount() const { - return mRefCount.load(std::memory_order_relaxed); - } - - virtual int remoteRefcount() const { -#ifndef NO_IMEMORY - if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; - int32_t remoteRefcount = - reinterpret_cast(mMemory->pointer())->getRemoteRefcount(); - // Sanity check so that remoteRefCount() is non-negative. - return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data. -#else - return 0; -#endif - } - - // returns old value - int addRemoteRefcount(int32_t value) { -#ifndef NO_IMEMORY - if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; - return reinterpret_cast(mMemory->pointer())->addRemoteRefcount(value); -#else - (void) value; - return 0; -#endif - } - - bool isDeadObject() const { - return isDeadObject(mMemory); - } - - static bool isDeadObject(const sp &memory) { -#ifndef NO_IMEMORY - if (memory.get() == nullptr || memory->pointer() == nullptr) return false; - return reinterpret_cast(memory->pointer())->isDeadObject(); -#else - (void) memory; - return false; -#endif - } - - // Sticky on enabling of shared memory MediaBuffers. By default we don't use - // shared memory for MediaBuffers, but we enable this for those processes - // that export MediaBuffers. - static void useSharedMemory() { - std::atomic_store_explicit( - &mUseSharedMemory, (int_least32_t)1, std::memory_order_seq_cst); - } - -protected: - // true if MediaBuffer is observed (part of a MediaBufferGroup). - inline bool isObserved() const { - return mObserver != nullptr; - } - - virtual ~MediaBuffer(); - - sp mMemory; - -private: - friend class MediaBufferGroup; - friend class OMXDecoder; - friend class BnMediaSource; - friend class BpMediaSource; - - // For use by OMXDecoder, reference count must be 1, drop reference - // count to 0 without signalling the observer. - void claim(); - - MediaBufferObserver *mObserver; - std::atomic mRefCount; - - void *mData; - size_t mSize, mRangeOffset, mRangeLength; - sp mBuffer; - - bool mOwnsData; - - MetaDataBase* mMetaData; - - static std::atomic_int_least32_t mUseSharedMemory; - - MediaBuffer(const MediaBuffer &); - MediaBuffer &operator=(const MediaBuffer &); - - // SharedControl block at the start of IMemory. - struct SharedControl { - enum { - FLAG_DEAD_OBJECT = (1 << 0), - }; - - // returns old value - inline int32_t addRemoteRefcount(int32_t value) { - return std::atomic_fetch_add_explicit( - &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst); - } - - inline int32_t getRemoteRefcount() const { - return std::atomic_load_explicit(&mRemoteRefcount, std::memory_order_seq_cst); - } - - inline void setRemoteRefcount(int32_t value) { - std::atomic_store_explicit( - &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst); - } - - inline bool isDeadObject() const { - return (std::atomic_load_explicit( - &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0; - } - - inline void setDeadObject() { - (void)std::atomic_fetch_or_explicit( - &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst); - } - - inline void clear() { - std::atomic_store_explicit( - &mFlags, (int_least32_t)0, std::memory_order_seq_cst); - std::atomic_store_explicit( - &mRemoteRefcount, (int_least32_t)0, std::memory_order_seq_cst); - } - - private: - // Caution: atomic_int_fast32_t is 64 bits on LP64. - std::atomic_int_least32_t mFlags; - std::atomic_int_least32_t mRemoteRefcount; - int32_t unused[6] __attribute__((__unused__)); // additional buffer space - }; - - inline SharedControl *getSharedControl() const { -#ifndef NO_IMEMORY - return reinterpret_cast(mMemory->pointer()); -#else - return nullptr; -#endif - } -}; - -} // namespace android - -#endif // MEDIA_BUFFER_H_ diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h deleted file mode 100644 index e2cbfc8d6a..0000000000 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_BUFFER_BASE_H_ - -#define MEDIA_BUFFER_BASE_H_ - -#include -#include -#include - -namespace android { - -class MediaBufferBase; -class MetaDataBase; - -class MediaBufferObserver { -public: - MediaBufferObserver() {} - virtual ~MediaBufferObserver() {} - - virtual void signalBufferReturned(MediaBufferBase *buffer) = 0; - -private: - MediaBufferObserver(const MediaBufferObserver &); - MediaBufferObserver &operator=(const MediaBufferObserver &); -}; - -class MediaBufferBase { -public: - static MediaBufferBase *Create(size_t size); - - // If MediaBufferGroup is set, decrement the local reference count; - // if the local reference count drops to 0, return the buffer to the - // associated MediaBufferGroup. - // - // If no MediaBufferGroup is set, the local reference count must be zero - // when called, whereupon the MediaBuffer is deleted. - virtual void release() = 0; - - // Increments the local reference count. - // Use only when MediaBufferGroup is set. - virtual void add_ref() = 0; - - virtual void *data() const = 0; - virtual size_t size() const = 0; - - virtual size_t range_offset() const = 0; - virtual size_t range_length() const = 0; - - virtual void set_range(size_t offset, size_t length) = 0; - - virtual MetaDataBase& meta_data() = 0; - - // Clears meta data and resets the range to the full extent. - virtual void reset() = 0; - - virtual void setObserver(MediaBufferObserver *group) = 0; - - virtual int refcount() const = 0; - - virtual int localRefcount() const = 0; - virtual int remoteRefcount() const = 0; - - virtual ~MediaBufferBase() { - delete mWrapper; - delete mFormat; - }; - - CMediaBuffer *wrap() { - if (mWrapper) { - return mWrapper; - } - mWrapper = new CMediaBuffer; - mWrapper->handle = this; - - mWrapper->release = [](void *handle) -> void { - ((MediaBufferBase*)handle)->release(); - }; - - mWrapper->data = [](void *handle) -> void * { - return ((MediaBufferBase*)handle)->data(); - }; - - mWrapper->size = [](void *handle) -> size_t { - return ((MediaBufferBase*)handle)->size(); - }; - - mWrapper->range_offset = [](void *handle) -> size_t { - return ((MediaBufferBase*)handle)->range_offset(); - }; - - mWrapper->range_length = [](void *handle) -> size_t { - return ((MediaBufferBase*)handle)->range_length(); - }; - - mWrapper->set_range = [](void *handle, size_t offset, size_t length) -> void { - return ((MediaBufferBase*)handle)->set_range(offset, length); - }; - - mWrapper->meta_data = [](void *handle) -> AMediaFormat* { - if (((MediaBufferBase*)handle)->mFormat == nullptr) { - sp msg = new AMessage(); - ((MediaBufferBase*)handle)->mFormat = AMediaFormat_fromMsg(&msg); - } - return ((MediaBufferBase*)handle)->mFormat; - }; - - return mWrapper; - } -protected: - MediaBufferBase() { - mWrapper = nullptr; - mFormat = nullptr; - } -private: - CMediaBuffer *mWrapper; - AMediaFormat *mFormat; -}; - -} // namespace android - -#endif // MEDIA_BUFFER_BASE_H_ diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h deleted file mode 100644 index a162116cc0..0000000000 --- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_BUFFER_GROUP_H_ - -#define MEDIA_BUFFER_GROUP_H_ - -#include - -#include -#include -#include -#include -#include - -namespace android { - -class MediaBufferBase; - -class MediaBufferGroup : public MediaBufferObserver { -public: - MediaBufferGroup(size_t growthLimit = 0); - - // create a media buffer group with preallocated buffers - MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit = 0); - - ~MediaBufferGroup(); - - void add_buffer(MediaBufferBase *buffer); - - bool has_buffers(); - - // If nonBlocking is false, it blocks until a buffer is available and - // passes it to the caller in *buffer, while returning OK. - // The returned buffer will have a reference count of 1. - // If nonBlocking is true and a buffer is not immediately available, - // buffer is set to NULL and it returns WOULD_BLOCK. - // If requestedSize is 0, any free MediaBuffer will be returned. - // If requestedSize is > 0, the returned MediaBuffer should have buffer - // size of at least requstedSize. - status_t acquire_buffer( - MediaBufferBase **buffer, bool nonBlocking = false, size_t requestedSize = 0); - - size_t buffers() const; - - // If buffer is nullptr, have acquire_buffer() check for remote release. - virtual void signalBufferReturned(MediaBufferBase *buffer); - - CMediaBufferGroup *wrap() { - if (mWrapper) { - return mWrapper; - } - - mWrapper = new CMediaBufferGroup; - mWrapper->handle = this; - - mWrapper->add_buffer = [](void *handle, size_t size) -> void { - MediaBufferBase *buf = MediaBufferBase::Create(size); - ((MediaBufferGroup*)handle)->add_buffer(buf); - }; - - mWrapper->init = [](void *handle, - size_t buffers, size_t buffer_size, size_t growthLimit) -> bool { - ((MediaBufferGroup*)handle)->init(buffers, buffer_size, growthLimit); - // ((MediaBufferGroup*)handle)->mWrapper->init = nullptr; // enforce call-once - return true; - }; - - mWrapper->acquire_buffer = [](void *handle, - CMediaBuffer **buf, bool nonBlocking, size_t requestedSize) -> media_status_t { - MediaBufferBase *acquiredBuf = nullptr; - status_t err = ((MediaBufferGroup*)handle)->acquire_buffer( - &acquiredBuf, nonBlocking, requestedSize); - if (err == OK && acquiredBuf != nullptr) { - *buf = acquiredBuf->wrap(); - } else { - *buf = nullptr; - } - return translate_error(err); - }; - - mWrapper->has_buffers = [](void *handle) -> bool { - return ((MediaBufferGroup*)handle)->has_buffers(); - }; - - return mWrapper; - } - -private: - CMediaBufferGroup *mWrapper; - struct InternalData; - InternalData *mInternal; - - MediaBufferGroup(const MediaBufferGroup &); - MediaBufferGroup &operator=(const MediaBufferGroup &); - void init(size_t buffers, size_t buffer_size, size_t growthLimit); -}; - -} // namespace android - -#endif // MEDIA_BUFFER_GROUP_H_ diff --git a/media/libmediaextractor/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h deleted file mode 100644 index f625358f6c..0000000000 --- a/media/libmediaextractor/include/media/stagefright/MetaData.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2009 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 META_DATA_H_ - -#define META_DATA_H_ - -#include - -#include - -#include -#include -#include - -namespace android { - -class MetaData final : public MetaDataBase, public RefBase { -public: - MetaData(); - MetaData(const MetaData &from); - MetaData(const MetaDataBase &from); - -protected: - virtual ~MetaData(); - -private: - friend class BnMediaSource; - friend class BpMediaSource; - friend class BpMediaExtractor; - static sp createFromParcel(const Parcel &parcel); -}; - -} // namespace android - -#endif // META_DATA_H_ diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h deleted file mode 100644 index b99c14ca19..0000000000 --- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2009 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 META_DATA_BASE_H_ - -#define META_DATA_BASE_H_ - -#include - -#include - -#include -#include - -namespace android { - -// The following keys map to int32_t data unless indicated otherwise. -enum { - kKeyMIMEType = 'mime', // cstring - kKeyWidth = 'widt', // int32_t, image pixel - kKeyHeight = 'heig', // int32_t, image pixel - kKeyDisplayWidth = 'dWid', // int32_t, display/presentation - kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation - kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width - kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height - kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width - kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height - - // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1) - kKeyCropRect = 'crop', - - kKeyRotation = 'rotA', // int32_t (angle in degrees) - kKeyIFramesInterval = 'ifiv', // int32_t - kKeyStride = 'strd', // int32_t - kKeySliceHeight = 'slht', // int32_t - kKeyChannelCount = '#chn', // int32_t - kKeyChannelMask = 'chnm', // int32_t - kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz) - kKeyPcmEncoding = 'PCMe', // int32_t (audio encoding enum) - kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) - kKeyBitRate = 'brte', // int32_t (bps) - kKeyMaxBitRate = 'mxBr', // int32_t (bps) - kKeyBitsPerSample = 'bits', // int32_t (bits per sample) - kKeyStreamHeader = 'stHd', // raw data - kKeyESDS = 'esds', // raw data - kKeyAACProfile = 'aacp', // int32_t - kKeyAVCC = 'avcc', // raw data - kKeyHVCC = 'hvcc', // raw data - kKeyThumbnailHVCC = 'thvc', // raw data - kKeyD263 = 'd263', // raw data - kKeyVorbisInfo = 'vinf', // raw data - kKeyVorbisBooks = 'vboo', // raw data - kKeyOpusHeader = 'ohdr', // raw data - kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns) - kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns) - kKeyFlacMetadata = 'flMd', // raw data - kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information) - kKeyIsSyncFrame = 'sync', // int32_t (bool) - kKeyIsCodecConfig = 'conf', // int32_t (bool) - kKeyIsMuxerData = 'muxd', // int32_t (bool) - kKeyTime = 'time', // int64_t (usecs) - kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs) - kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp) - kKeyTargetTime = 'tarT', // int64_t (usecs) - kKeyDriftTime = 'dftT', // int64_t (usecs) - kKeyAnchorTime = 'ancT', // int64_t (usecs) - kKeyDuration = 'dura', // int64_t (usecs) - kKeyPixelFormat = 'pixf', // int32_t - kKeyColorFormat = 'colf', // int32_t - kKeyColorSpace = 'cols', // int32_t - kKeyPlatformPrivate = 'priv', // pointer - kKeyDecoderComponent = 'decC', // cstring - kKeyBufferID = 'bfID', - kKeyMaxInputSize = 'inpS', - kKeyMaxWidth = 'maxW', - kKeyMaxHeight = 'maxH', - kKeyThumbnailTime = 'thbT', // int64_t (usecs) - kKeyTrackID = 'trID', - kKeyEncoderDelay = 'encd', // int32_t (frames) - kKeyEncoderPadding = 'encp', // int32_t (frames) - - kKeyAlbum = 'albu', // cstring - kKeyArtist = 'arti', // cstring - kKeyAlbumArtist = 'aart', // cstring - kKeyComposer = 'comp', // cstring - kKeyGenre = 'genr', // cstring - kKeyTitle = 'titl', // cstring - kKeyYear = 'year', // cstring - kKeyAlbumArt = 'albA', // compressed image data - kKeyAuthor = 'auth', // cstring - kKeyCDTrackNumber = 'cdtr', // cstring - kKeyDiscNumber = 'dnum', // cstring - kKeyDate = 'date', // cstring - kKeyWriter = 'writ', // cstring - kKeyCompilation = 'cpil', // cstring - kKeyLocation = 'loc ', // cstring - kKeyTimeScale = 'tmsl', // int32_t - kKeyCaptureFramerate = 'capF', // float (capture fps) - - // video profile and level - kKeyVideoProfile = 'vprf', // int32_t - kKeyVideoLevel = 'vlev', // int32_t - - // Set this key to enable authoring files in 64-bit offset - kKey64BitFileOffset = 'fobt', // int32_t (bool) - kKey2ByteNalLength = '2NAL', // int32_t (bool) - - // Identify the file output format for authoring - // Please see for the supported - // file output formats. - kKeyFileType = 'ftyp', // int32_t - - // Track authoring progress status - // kKeyTrackTimeStatus is used to track progress in elapsed time - kKeyTrackTimeStatus = 'tktm', // int64_t - - kKeyRealTimeRecording = 'rtrc', // bool (int32_t) - kKeyNumBuffers = 'nbbf', // int32_t - - // Ogg files can be tagged to be automatically looping... - kKeyAutoLoop = 'autL', // bool (int32_t) - - kKeyValidSamples = 'valD', // int32_t - - kKeyIsUnreadable = 'unre', // bool (int32_t) - - // An indication that a video buffer has been rendered. - kKeyRendered = 'rend', // bool (int32_t) - - // The language code for this media - kKeyMediaLanguage = 'lang', // cstring - - // To store the timed text format data - kKeyTextFormatData = 'text', // raw data - - kKeyRequiresSecureBuffers = 'secu', // bool (int32_t) - - kKeyIsADTS = 'adts', // bool (int32_t) - kKeyAACAOT = 'aaot', // int32_t - - // If a MediaBuffer's data represents (at least partially) encrypted - // data, the following fields aid in decryption. - // The data can be thought of as pairs of plain and encrypted data - // fragments, i.e. plain and encrypted data alternate. - // The first fragment is by convention plain data (if that's not the - // case, simply specify plain fragment size of 0). - // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of - // size_t values. The sum total of all size_t values of both arrays - // must equal the amount of data (i.e. MediaBuffer's range_length()). - // If both arrays are present, they must be of the same size. - // If only encrypted sizes are present it is assumed that all - // plain sizes are 0, i.e. all fragments are encrypted. - // To programmatically set these array, use the MetaDataBase::setData API, i.e. - // const size_t encSizes[]; - // meta->setData( - // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes)); - // A plain sizes array by itself makes no sense. - kKeyEncryptedSizes = 'encr', // size_t[] - kKeyPlainSizes = 'plai', // size_t[] - kKeyCryptoKey = 'cryK', // uint8_t[16] - kKeyCryptoIV = 'cryI', // uint8_t[16] - kKeyCryptoMode = 'cryM', // int32_t - - kKeyCryptoDefaultIVSize = 'cryS', // int32_t - - kKeyPssh = 'pssh', // raw data - kKeyCASystemID = 'caid', // int32_t - kKeyCASessionID = 'seid', // raw data - kKeyCAPrivateData = 'cadc', // raw data - - kKeyEncryptedByteBlock = 'cblk', // uint8_t - kKeySkipByteBlock = 'sblk', // uint8_t - - // Please see MediaFormat.KEY_IS_AUTOSELECT. - kKeyTrackIsAutoselect = 'auto', // bool (int32_t) - // Please see MediaFormat.KEY_IS_DEFAULT. - kKeyTrackIsDefault = 'dflt', // bool (int32_t) - // Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well. - kKeyTrackIsForced = 'frcd', // bool (int32_t) - - // H264 supplemental enhancement information offsets/sizes - kKeySEI = 'sei ', // raw data - - // MPEG user data offsets - kKeyMpegUserData = 'mpud', // size_t[] - - // HDR related - kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo - kKeyHdr10PlusInfo = 'hdrD', // raw data - - // color aspects - kKeyColorRange = 'cRng', // int32_t, color range, value defined by ColorAspects.Range - kKeyColorPrimaries = 'cPrm', // int32_t, - // color Primaries, value defined by ColorAspects.Primaries - kKeyTransferFunction = 'tFun', // int32_t, - // transfer Function, value defined by ColorAspects.Transfer. - kKeyColorMatrix = 'cMtx', // int32_t, - // color Matrix, value defined by ColorAspects.MatrixCoeffs. - kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer) - kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded - - kKeyTileWidth = 'tilW', // int32_t, HEIF tile width - kKeyTileHeight = 'tilH', // int32_t, HEIF tile height - kKeyGridRows = 'grdR', // int32_t, HEIF grid rows - kKeyGridCols = 'grdC', // int32_t, HEIF grid columns - kKeyIccProfile = 'prof', // raw data, ICC profile data - kKeyIsPrimaryImage = 'prim', // bool (int32_t), image track is the primary image - kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track - kKeyExifOffset = 'exof', // int64_t, Exif data offset - kKeyExifSize = 'exsz', // int64_t, Exif data size - kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block - kKeyPcmBigEndian = 'pcmb', // bool (int32_t) - - // Key for ALAC Magic Cookie - kKeyAlacMagicCookie = 'almc', // raw data - - // AC-4 AudioPresentationInfo - kKeyAudioPresentationInfo = 'audP', // raw data -}; - -enum { - kTypeESDS = 'esds', - kTypeAVCC = 'avcc', - kTypeHVCC = 'hvcc', - kTypeD263 = 'd263', -}; - -enum { - kCryptoModeUnencrypted = 0, - kCryptoModeAesCtr = 1, - kCryptoModeAesCbc = 2, -}; - -class Parcel; - -class MetaDataBase { -public: - MetaDataBase(); - MetaDataBase(const MetaDataBase &from); - MetaDataBase& operator = (const MetaDataBase &); - - virtual ~MetaDataBase(); - - enum Type { - TYPE_NONE = 'none', - TYPE_C_STRING = 'cstr', - TYPE_INT32 = 'in32', - TYPE_INT64 = 'in64', - TYPE_FLOAT = 'floa', - TYPE_POINTER = 'ptr ', - TYPE_RECT = 'rect', - }; - - void clear(); - bool remove(uint32_t key); - - bool setCString(uint32_t key, const char *value); - bool setInt32(uint32_t key, int32_t value); - bool setInt64(uint32_t key, int64_t value); - bool setFloat(uint32_t key, float value); - bool setPointer(uint32_t key, void *value); - - bool setRect( - uint32_t key, - int32_t left, int32_t top, - int32_t right, int32_t bottom); - - bool findCString(uint32_t key, const char **value) const; - bool findInt32(uint32_t key, int32_t *value) const; - bool findInt64(uint32_t key, int64_t *value) const; - bool findFloat(uint32_t key, float *value) const; - bool findPointer(uint32_t key, void **value) const; - - bool findRect( - uint32_t key, - int32_t *left, int32_t *top, - int32_t *right, int32_t *bottom) const; - - bool setData(uint32_t key, uint32_t type, const void *data, size_t size); - - bool findData(uint32_t key, uint32_t *type, - const void **data, size_t *size) const; - - bool hasData(uint32_t key) const; - - String8 toString() const; - void dumpToLog() const; - -private: - friend class BpMediaSource; - friend class BnMediaSource; - friend class BnMediaExtractor; - friend class MetaData; - - struct typed_data; - struct Rect; - struct MetaDataInternal; - MetaDataInternal *mInternalData; - status_t writeToParcel(Parcel &parcel); - status_t updateFromParcel(const Parcel &parcel); -}; - -} // namespace android - -#endif // META_DATA_H_ diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 38f42dc6db..b3f74043ac 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -21,7 +21,6 @@ cc_library_static { "libgui", "liblog", "libmedia_omx", - "libmediaextractor", "libstagefright_foundation", "libui", "libutils", diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp index 51879fd9e1..55867a56a2 100644 --- a/media/libmediaplayerservice/Android.bp +++ b/media/libmediaplayerservice/Android.bp @@ -24,7 +24,6 @@ cc_library_shared { "liblog", "libmedia", "libmedia_omx", - "libmediaextractor", "libmediadrm", "libmediametrics", "libmediautils", diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 249f2a4a51..f45cc58e91 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -94,6 +94,7 @@ cc_library { "CameraSource.cpp", "CameraSourceTimeLapse.cpp", "DataConverter.cpp", + "DataSourceBase.cpp", "DataSourceFactory.cpp", "DataURISource.cpp", "ClearFileSource.cpp", @@ -113,6 +114,7 @@ cc_library { "MediaCodecSource.cpp", "MediaExtractor.cpp", "MediaExtractorFactory.cpp", + "MediaSource.cpp", "MediaSync.cpp", "MediaTrack.cpp", "http/ClearMediaHTTP.cpp", @@ -150,7 +152,6 @@ cc_library { "libmedia", "libmedia_omx", "libaudioclient", - "libmediaextractor", "libmediametrics", "libmediautils", "libui", diff --git a/media/libmediaextractor/DataSourceBase.cpp b/media/libstagefright/DataSourceBase.cpp similarity index 100% rename from media/libmediaextractor/DataSourceBase.cpp rename to media/libstagefright/DataSourceBase.cpp diff --git a/media/libmediaextractor/MediaSource.cpp b/media/libstagefright/MediaSource.cpp similarity index 100% rename from media/libmediaextractor/MediaSource.cpp rename to media/libstagefright/MediaSource.cpp diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index dcf1ab89dc..027e593e39 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -25,6 +25,7 @@ cc_library { header_libs: [ "libhardware_headers", "libstagefright_foundation_headers", + "media_ndk_headers", "media_plugin_headers", ], @@ -64,8 +65,98 @@ cc_library { "AudioPresentationInfo.cpp", "ByteUtils.cpp", "ColorUtils.cpp", + "MediaBuffer.cpp", + "MediaBufferBase.cpp", + "MediaBufferGroup.cpp", "MediaDefs.cpp", "MediaKeys.cpp", + "MetaData.cpp", + "MetaDataBase.cpp", + "avc_utils.cpp", + "base64.cpp", + "hexdump.cpp", + ], + + clang: true, + + sanitize: { + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + cfi: true, + }, +} + +cc_library_static { + name: "libstagefright_foundation_without_imemory", + vendor_available: true, + vndk: { + enabled: true, + }, + double_loadable: true, + include_dirs: [ + "frameworks/av/include", + "frameworks/native/include", + "frameworks/native/libs/arect/include", + "frameworks/native/libs/nativebase/include", + ], + + local_include_dirs: [ + "include/media/stagefright/foundation", + ], + + header_libs: [ + "libhardware_headers", + "libstagefright_foundation_headers", + "media_ndk_headers", + "media_plugin_headers", + ], + + export_header_lib_headers: [ + "libstagefright_foundation_headers", + "media_plugin_headers", + ], + + export_shared_lib_headers: [ + "libbinder", + ], + + cflags: [ + "-Wno-multichar", + "-Werror", + "-Wall", + "-DNO_IMEMORY", + ], + + shared_libs: [ + "libbinder", + "libutils", + "libcutils", + "liblog", + ], + + srcs: [ + "AAtomizer.cpp", + "ABitReader.cpp", + "ABuffer.cpp", + "ADebug.cpp", + "AHandler.cpp", + "ALooper.cpp", + "ALooperRoster.cpp", + "AMessage.cpp", + "AString.cpp", + "AStringUtils.cpp", + "AudioPresentationInfo.cpp", + "ByteUtils.cpp", + "ColorUtils.cpp", + "MediaBuffer.cpp", + "MediaBufferBase.cpp", + "MediaBufferGroup.cpp", + "MediaDefs.cpp", + "MediaKeys.cpp", + "MetaData.cpp", + "MetaDataBase.cpp", "avc_utils.cpp", "base64.cpp", "hexdump.cpp", diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp similarity index 100% rename from media/libmediaextractor/MediaBuffer.cpp rename to media/libstagefright/foundation/MediaBuffer.cpp diff --git a/media/libmediaextractor/MediaBufferBase.cpp b/media/libstagefright/foundation/MediaBufferBase.cpp similarity index 100% rename from media/libmediaextractor/MediaBufferBase.cpp rename to media/libstagefright/foundation/MediaBufferBase.cpp diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp similarity index 100% rename from media/libmediaextractor/MediaBufferGroup.cpp rename to media/libstagefright/foundation/MediaBufferGroup.cpp diff --git a/media/libmediaextractor/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp similarity index 100% rename from media/libmediaextractor/MetaData.cpp rename to media/libstagefright/foundation/MetaData.cpp diff --git a/media/libmediaextractor/MetaDataBase.cpp b/media/libstagefright/foundation/MetaDataBase.cpp similarity index 100% rename from media/libmediaextractor/MetaDataBase.cpp rename to media/libstagefright/foundation/MetaDataBase.cpp diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp index c4a072b989..c0ee14eaa3 100644 --- a/media/libstagefright/httplive/Android.bp +++ b/media/libstagefright/httplive/Android.bp @@ -32,7 +32,6 @@ cc_library { "libcrypto", "libcutils", "libmedia", - "libmediaextractor", "libmediandk", "libstagefright", "libstagefright_foundation", diff --git a/media/libmediaextractor/include/media/DataSource.h b/media/libstagefright/include/media/stagefright/DataSource.h similarity index 100% rename from media/libmediaextractor/include/media/DataSource.h rename to media/libstagefright/include/media/stagefright/DataSource.h diff --git a/media/libmediaextractor/include/media/DataSourceBase.h b/media/libstagefright/include/media/stagefright/DataSourceBase.h similarity index 100% rename from media/libmediaextractor/include/media/DataSourceBase.h rename to media/libstagefright/include/media/stagefright/DataSourceBase.h diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h deleted file mode 120000 index 1d49c1a0a1..0000000000 --- a/media/libstagefright/include/media/stagefright/MediaBuffer.h +++ /dev/null @@ -1 +0,0 @@ -../../../../libmediaextractor/include/media/stagefright/MediaBuffer.h \ No newline at end of file diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h new file mode 100644 index 0000000000..ace63ae9f8 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/MediaBuffer.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_BUFFER_H_ + +#define MEDIA_BUFFER_H_ + +#include +#include + +#include + +#include +#include +#include +#include + +namespace android { + +struct ABuffer; +class MediaBuffer; +class MediaBufferObserver; +class MetaDataBase; + +class MediaBuffer : public MediaBufferBase { +public: + // allocations larger than or equal to this will use shared memory. + static const size_t kSharedMemThreshold = 64 * 1024; + + // The underlying data remains the responsibility of the caller! + MediaBuffer(void *data, size_t size); + + explicit MediaBuffer(size_t size); + + explicit MediaBuffer(const sp &buffer); +#ifndef NO_IMEMORY + MediaBuffer(const sp &mem) : + MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) { + // delegate and override mMemory + mMemory = mem; + } +#endif + + // If MediaBufferGroup is set, decrement the local reference count; + // if the local reference count drops to 0, return the buffer to the + // associated MediaBufferGroup. + // + // If no MediaBufferGroup is set, the local reference count must be zero + // when called, whereupon the MediaBuffer is deleted. + virtual void release(); + + // Increments the local reference count. + // Use only when MediaBufferGroup is set. + virtual void add_ref(); + + virtual void *data() const; + virtual size_t size() const; + + virtual size_t range_offset() const; + virtual size_t range_length() const; + + virtual void set_range(size_t offset, size_t length); + + MetaDataBase& meta_data(); + + // Clears meta data and resets the range to the full extent. + virtual void reset(); + + virtual void setObserver(MediaBufferObserver *group); + + // sum of localRefcount() and remoteRefcount() + // Result should be treated as approximate unless the result precludes concurrent accesses. + virtual int refcount() const { + return localRefcount() + remoteRefcount(); + } + + // Result should be treated as approximate unless the result precludes concurrent accesses. + virtual int localRefcount() const { + return mRefCount.load(std::memory_order_relaxed); + } + + virtual int remoteRefcount() const { +#ifndef NO_IMEMORY + if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; + int32_t remoteRefcount = + reinterpret_cast(mMemory->pointer())->getRemoteRefcount(); + // Sanity check so that remoteRefCount() is non-negative. + return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data. +#else + return 0; +#endif + } + + // returns old value + int addRemoteRefcount(int32_t value) { +#ifndef NO_IMEMORY + if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0; + return reinterpret_cast(mMemory->pointer())->addRemoteRefcount(value); +#else + (void) value; + return 0; +#endif + } + + bool isDeadObject() const { + return isDeadObject(mMemory); + } + + static bool isDeadObject(const sp &memory) { +#ifndef NO_IMEMORY + if (memory.get() == nullptr || memory->pointer() == nullptr) return false; + return reinterpret_cast(memory->pointer())->isDeadObject(); +#else + (void) memory; + return false; +#endif + } + + // Sticky on enabling of shared memory MediaBuffers. By default we don't use + // shared memory for MediaBuffers, but we enable this for those processes + // that export MediaBuffers. + static void useSharedMemory() { + std::atomic_store_explicit( + &mUseSharedMemory, (int_least32_t)1, std::memory_order_seq_cst); + } + +protected: + // true if MediaBuffer is observed (part of a MediaBufferGroup). + inline bool isObserved() const { + return mObserver != nullptr; + } + + virtual ~MediaBuffer(); + + sp mMemory; + +private: + friend class MediaBufferGroup; + friend class OMXDecoder; + friend class BnMediaSource; + friend class BpMediaSource; + + // For use by OMXDecoder, reference count must be 1, drop reference + // count to 0 without signalling the observer. + void claim(); + + MediaBufferObserver *mObserver; + std::atomic mRefCount; + + void *mData; + size_t mSize, mRangeOffset, mRangeLength; + sp mBuffer; + + bool mOwnsData; + + MetaDataBase* mMetaData; + + static std::atomic_int_least32_t mUseSharedMemory; + + MediaBuffer(const MediaBuffer &); + MediaBuffer &operator=(const MediaBuffer &); + + // SharedControl block at the start of IMemory. + struct SharedControl { + enum { + FLAG_DEAD_OBJECT = (1 << 0), + }; + + // returns old value + inline int32_t addRemoteRefcount(int32_t value) { + return std::atomic_fetch_add_explicit( + &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst); + } + + inline int32_t getRemoteRefcount() const { + return std::atomic_load_explicit(&mRemoteRefcount, std::memory_order_seq_cst); + } + + inline void setRemoteRefcount(int32_t value) { + std::atomic_store_explicit( + &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst); + } + + inline bool isDeadObject() const { + return (std::atomic_load_explicit( + &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0; + } + + inline void setDeadObject() { + (void)std::atomic_fetch_or_explicit( + &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst); + } + + inline void clear() { + std::atomic_store_explicit( + &mFlags, (int_least32_t)0, std::memory_order_seq_cst); + std::atomic_store_explicit( + &mRemoteRefcount, (int_least32_t)0, std::memory_order_seq_cst); + } + + private: + // Caution: atomic_int_fast32_t is 64 bits on LP64. + std::atomic_int_least32_t mFlags; + std::atomic_int_least32_t mRemoteRefcount; + int32_t unused[6] __attribute__((__unused__)); // additional buffer space + }; + + inline SharedControl *getSharedControl() const { +#ifndef NO_IMEMORY + return reinterpret_cast(mMemory->pointer()); +#else + return nullptr; +#endif + } +}; + +} // namespace android + +#endif // MEDIA_BUFFER_H_ diff --git a/media/libstagefright/include/media/stagefright/MediaBufferBase.h b/media/libstagefright/include/media/stagefright/MediaBufferBase.h deleted file mode 120000 index 80e49b019a..0000000000 --- a/media/libstagefright/include/media/stagefright/MediaBufferBase.h +++ /dev/null @@ -1 +0,0 @@ -../../../../libmediaextractor/include/media/stagefright/MediaBufferBase.h \ No newline at end of file diff --git a/media/libstagefright/include/media/stagefright/MediaBufferBase.h b/media/libstagefright/include/media/stagefright/MediaBufferBase.h new file mode 100644 index 0000000000..e2cbfc8d6a --- /dev/null +++ b/media/libstagefright/include/media/stagefright/MediaBufferBase.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_BUFFER_BASE_H_ + +#define MEDIA_BUFFER_BASE_H_ + +#include +#include +#include + +namespace android { + +class MediaBufferBase; +class MetaDataBase; + +class MediaBufferObserver { +public: + MediaBufferObserver() {} + virtual ~MediaBufferObserver() {} + + virtual void signalBufferReturned(MediaBufferBase *buffer) = 0; + +private: + MediaBufferObserver(const MediaBufferObserver &); + MediaBufferObserver &operator=(const MediaBufferObserver &); +}; + +class MediaBufferBase { +public: + static MediaBufferBase *Create(size_t size); + + // If MediaBufferGroup is set, decrement the local reference count; + // if the local reference count drops to 0, return the buffer to the + // associated MediaBufferGroup. + // + // If no MediaBufferGroup is set, the local reference count must be zero + // when called, whereupon the MediaBuffer is deleted. + virtual void release() = 0; + + // Increments the local reference count. + // Use only when MediaBufferGroup is set. + virtual void add_ref() = 0; + + virtual void *data() const = 0; + virtual size_t size() const = 0; + + virtual size_t range_offset() const = 0; + virtual size_t range_length() const = 0; + + virtual void set_range(size_t offset, size_t length) = 0; + + virtual MetaDataBase& meta_data() = 0; + + // Clears meta data and resets the range to the full extent. + virtual void reset() = 0; + + virtual void setObserver(MediaBufferObserver *group) = 0; + + virtual int refcount() const = 0; + + virtual int localRefcount() const = 0; + virtual int remoteRefcount() const = 0; + + virtual ~MediaBufferBase() { + delete mWrapper; + delete mFormat; + }; + + CMediaBuffer *wrap() { + if (mWrapper) { + return mWrapper; + } + mWrapper = new CMediaBuffer; + mWrapper->handle = this; + + mWrapper->release = [](void *handle) -> void { + ((MediaBufferBase*)handle)->release(); + }; + + mWrapper->data = [](void *handle) -> void * { + return ((MediaBufferBase*)handle)->data(); + }; + + mWrapper->size = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->size(); + }; + + mWrapper->range_offset = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->range_offset(); + }; + + mWrapper->range_length = [](void *handle) -> size_t { + return ((MediaBufferBase*)handle)->range_length(); + }; + + mWrapper->set_range = [](void *handle, size_t offset, size_t length) -> void { + return ((MediaBufferBase*)handle)->set_range(offset, length); + }; + + mWrapper->meta_data = [](void *handle) -> AMediaFormat* { + if (((MediaBufferBase*)handle)->mFormat == nullptr) { + sp msg = new AMessage(); + ((MediaBufferBase*)handle)->mFormat = AMediaFormat_fromMsg(&msg); + } + return ((MediaBufferBase*)handle)->mFormat; + }; + + return mWrapper; + } +protected: + MediaBufferBase() { + mWrapper = nullptr; + mFormat = nullptr; + } +private: + CMediaBuffer *mWrapper; + AMediaFormat *mFormat; +}; + +} // namespace android + +#endif // MEDIA_BUFFER_BASE_H_ diff --git a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h b/media/libstagefright/include/media/stagefright/MediaBufferGroup.h deleted file mode 120000 index 009b3d9299..0000000000 --- a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h +++ /dev/null @@ -1 +0,0 @@ -../../../../libmediaextractor/include/media/stagefright/MediaBufferGroup.h \ No newline at end of file diff --git a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h b/media/libstagefright/include/media/stagefright/MediaBufferGroup.h new file mode 100644 index 0000000000..a162116cc0 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/MediaBufferGroup.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MEDIA_BUFFER_GROUP_H_ + +#define MEDIA_BUFFER_GROUP_H_ + +#include + +#include +#include +#include +#include +#include + +namespace android { + +class MediaBufferBase; + +class MediaBufferGroup : public MediaBufferObserver { +public: + MediaBufferGroup(size_t growthLimit = 0); + + // create a media buffer group with preallocated buffers + MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit = 0); + + ~MediaBufferGroup(); + + void add_buffer(MediaBufferBase *buffer); + + bool has_buffers(); + + // If nonBlocking is false, it blocks until a buffer is available and + // passes it to the caller in *buffer, while returning OK. + // The returned buffer will have a reference count of 1. + // If nonBlocking is true and a buffer is not immediately available, + // buffer is set to NULL and it returns WOULD_BLOCK. + // If requestedSize is 0, any free MediaBuffer will be returned. + // If requestedSize is > 0, the returned MediaBuffer should have buffer + // size of at least requstedSize. + status_t acquire_buffer( + MediaBufferBase **buffer, bool nonBlocking = false, size_t requestedSize = 0); + + size_t buffers() const; + + // If buffer is nullptr, have acquire_buffer() check for remote release. + virtual void signalBufferReturned(MediaBufferBase *buffer); + + CMediaBufferGroup *wrap() { + if (mWrapper) { + return mWrapper; + } + + mWrapper = new CMediaBufferGroup; + mWrapper->handle = this; + + mWrapper->add_buffer = [](void *handle, size_t size) -> void { + MediaBufferBase *buf = MediaBufferBase::Create(size); + ((MediaBufferGroup*)handle)->add_buffer(buf); + }; + + mWrapper->init = [](void *handle, + size_t buffers, size_t buffer_size, size_t growthLimit) -> bool { + ((MediaBufferGroup*)handle)->init(buffers, buffer_size, growthLimit); + // ((MediaBufferGroup*)handle)->mWrapper->init = nullptr; // enforce call-once + return true; + }; + + mWrapper->acquire_buffer = [](void *handle, + CMediaBuffer **buf, bool nonBlocking, size_t requestedSize) -> media_status_t { + MediaBufferBase *acquiredBuf = nullptr; + status_t err = ((MediaBufferGroup*)handle)->acquire_buffer( + &acquiredBuf, nonBlocking, requestedSize); + if (err == OK && acquiredBuf != nullptr) { + *buf = acquiredBuf->wrap(); + } else { + *buf = nullptr; + } + return translate_error(err); + }; + + mWrapper->has_buffers = [](void *handle) -> bool { + return ((MediaBufferGroup*)handle)->has_buffers(); + }; + + return mWrapper; + } + +private: + CMediaBufferGroup *mWrapper; + struct InternalData; + InternalData *mInternal; + + MediaBufferGroup(const MediaBufferGroup &); + MediaBufferGroup &operator=(const MediaBufferGroup &); + void init(size_t buffers, size_t buffer_size, size_t growthLimit); +}; + +} // namespace android + +#endif // MEDIA_BUFFER_GROUP_H_ diff --git a/media/libmediaextractor/include/media/MediaSource.h b/media/libstagefright/include/media/stagefright/MediaSource.h similarity index 100% rename from media/libmediaextractor/include/media/MediaSource.h rename to media/libstagefright/include/media/stagefright/MediaSource.h diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h deleted file mode 120000 index 160f8d310c..0000000000 --- a/media/libstagefright/include/media/stagefright/MetaData.h +++ /dev/null @@ -1 +0,0 @@ -../../../../libmediaextractor/include/media/stagefright/MetaData.h \ No newline at end of file diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h new file mode 100644 index 0000000000..f625358f6c --- /dev/null +++ b/media/libstagefright/include/media/stagefright/MetaData.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009 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 META_DATA_H_ + +#define META_DATA_H_ + +#include + +#include + +#include +#include +#include + +namespace android { + +class MetaData final : public MetaDataBase, public RefBase { +public: + MetaData(); + MetaData(const MetaData &from); + MetaData(const MetaDataBase &from); + +protected: + virtual ~MetaData(); + +private: + friend class BnMediaSource; + friend class BpMediaSource; + friend class BpMediaExtractor; + static sp createFromParcel(const Parcel &parcel); +}; + +} // namespace android + +#endif // META_DATA_H_ diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h deleted file mode 120000 index 1e12193578..0000000000 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ /dev/null @@ -1 +0,0 @@ -../../../../libmediaextractor/include/media/stagefright/MetaDataBase.h \ No newline at end of file diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h new file mode 100644 index 0000000000..b99c14ca19 --- /dev/null +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2009 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 META_DATA_BASE_H_ + +#define META_DATA_BASE_H_ + +#include + +#include + +#include +#include + +namespace android { + +// The following keys map to int32_t data unless indicated otherwise. +enum { + kKeyMIMEType = 'mime', // cstring + kKeyWidth = 'widt', // int32_t, image pixel + kKeyHeight = 'heig', // int32_t, image pixel + kKeyDisplayWidth = 'dWid', // int32_t, display/presentation + kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation + kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width + kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height + kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width + kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height + + // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1) + kKeyCropRect = 'crop', + + kKeyRotation = 'rotA', // int32_t (angle in degrees) + kKeyIFramesInterval = 'ifiv', // int32_t + kKeyStride = 'strd', // int32_t + kKeySliceHeight = 'slht', // int32_t + kKeyChannelCount = '#chn', // int32_t + kKeyChannelMask = 'chnm', // int32_t + kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz) + kKeyPcmEncoding = 'PCMe', // int32_t (audio encoding enum) + kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) + kKeyBitRate = 'brte', // int32_t (bps) + kKeyMaxBitRate = 'mxBr', // int32_t (bps) + kKeyBitsPerSample = 'bits', // int32_t (bits per sample) + kKeyStreamHeader = 'stHd', // raw data + kKeyESDS = 'esds', // raw data + kKeyAACProfile = 'aacp', // int32_t + kKeyAVCC = 'avcc', // raw data + kKeyHVCC = 'hvcc', // raw data + kKeyThumbnailHVCC = 'thvc', // raw data + kKeyD263 = 'd263', // raw data + kKeyVorbisInfo = 'vinf', // raw data + kKeyVorbisBooks = 'vboo', // raw data + kKeyOpusHeader = 'ohdr', // raw data + kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns) + kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns) + kKeyFlacMetadata = 'flMd', // raw data + kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information) + kKeyIsSyncFrame = 'sync', // int32_t (bool) + kKeyIsCodecConfig = 'conf', // int32_t (bool) + kKeyIsMuxerData = 'muxd', // int32_t (bool) + kKeyTime = 'time', // int64_t (usecs) + kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs) + kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp) + kKeyTargetTime = 'tarT', // int64_t (usecs) + kKeyDriftTime = 'dftT', // int64_t (usecs) + kKeyAnchorTime = 'ancT', // int64_t (usecs) + kKeyDuration = 'dura', // int64_t (usecs) + kKeyPixelFormat = 'pixf', // int32_t + kKeyColorFormat = 'colf', // int32_t + kKeyColorSpace = 'cols', // int32_t + kKeyPlatformPrivate = 'priv', // pointer + kKeyDecoderComponent = 'decC', // cstring + kKeyBufferID = 'bfID', + kKeyMaxInputSize = 'inpS', + kKeyMaxWidth = 'maxW', + kKeyMaxHeight = 'maxH', + kKeyThumbnailTime = 'thbT', // int64_t (usecs) + kKeyTrackID = 'trID', + kKeyEncoderDelay = 'encd', // int32_t (frames) + kKeyEncoderPadding = 'encp', // int32_t (frames) + + kKeyAlbum = 'albu', // cstring + kKeyArtist = 'arti', // cstring + kKeyAlbumArtist = 'aart', // cstring + kKeyComposer = 'comp', // cstring + kKeyGenre = 'genr', // cstring + kKeyTitle = 'titl', // cstring + kKeyYear = 'year', // cstring + kKeyAlbumArt = 'albA', // compressed image data + kKeyAuthor = 'auth', // cstring + kKeyCDTrackNumber = 'cdtr', // cstring + kKeyDiscNumber = 'dnum', // cstring + kKeyDate = 'date', // cstring + kKeyWriter = 'writ', // cstring + kKeyCompilation = 'cpil', // cstring + kKeyLocation = 'loc ', // cstring + kKeyTimeScale = 'tmsl', // int32_t + kKeyCaptureFramerate = 'capF', // float (capture fps) + + // video profile and level + kKeyVideoProfile = 'vprf', // int32_t + kKeyVideoLevel = 'vlev', // int32_t + + // Set this key to enable authoring files in 64-bit offset + kKey64BitFileOffset = 'fobt', // int32_t (bool) + kKey2ByteNalLength = '2NAL', // int32_t (bool) + + // Identify the file output format for authoring + // Please see for the supported + // file output formats. + kKeyFileType = 'ftyp', // int32_t + + // Track authoring progress status + // kKeyTrackTimeStatus is used to track progress in elapsed time + kKeyTrackTimeStatus = 'tktm', // int64_t + + kKeyRealTimeRecording = 'rtrc', // bool (int32_t) + kKeyNumBuffers = 'nbbf', // int32_t + + // Ogg files can be tagged to be automatically looping... + kKeyAutoLoop = 'autL', // bool (int32_t) + + kKeyValidSamples = 'valD', // int32_t + + kKeyIsUnreadable = 'unre', // bool (int32_t) + + // An indication that a video buffer has been rendered. + kKeyRendered = 'rend', // bool (int32_t) + + // The language code for this media + kKeyMediaLanguage = 'lang', // cstring + + // To store the timed text format data + kKeyTextFormatData = 'text', // raw data + + kKeyRequiresSecureBuffers = 'secu', // bool (int32_t) + + kKeyIsADTS = 'adts', // bool (int32_t) + kKeyAACAOT = 'aaot', // int32_t + + // If a MediaBuffer's data represents (at least partially) encrypted + // data, the following fields aid in decryption. + // The data can be thought of as pairs of plain and encrypted data + // fragments, i.e. plain and encrypted data alternate. + // The first fragment is by convention plain data (if that's not the + // case, simply specify plain fragment size of 0). + // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of + // size_t values. The sum total of all size_t values of both arrays + // must equal the amount of data (i.e. MediaBuffer's range_length()). + // If both arrays are present, they must be of the same size. + // If only encrypted sizes are present it is assumed that all + // plain sizes are 0, i.e. all fragments are encrypted. + // To programmatically set these array, use the MetaDataBase::setData API, i.e. + // const size_t encSizes[]; + // meta->setData( + // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes)); + // A plain sizes array by itself makes no sense. + kKeyEncryptedSizes = 'encr', // size_t[] + kKeyPlainSizes = 'plai', // size_t[] + kKeyCryptoKey = 'cryK', // uint8_t[16] + kKeyCryptoIV = 'cryI', // uint8_t[16] + kKeyCryptoMode = 'cryM', // int32_t + + kKeyCryptoDefaultIVSize = 'cryS', // int32_t + + kKeyPssh = 'pssh', // raw data + kKeyCASystemID = 'caid', // int32_t + kKeyCASessionID = 'seid', // raw data + kKeyCAPrivateData = 'cadc', // raw data + + kKeyEncryptedByteBlock = 'cblk', // uint8_t + kKeySkipByteBlock = 'sblk', // uint8_t + + // Please see MediaFormat.KEY_IS_AUTOSELECT. + kKeyTrackIsAutoselect = 'auto', // bool (int32_t) + // Please see MediaFormat.KEY_IS_DEFAULT. + kKeyTrackIsDefault = 'dflt', // bool (int32_t) + // Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well. + kKeyTrackIsForced = 'frcd', // bool (int32_t) + + // H264 supplemental enhancement information offsets/sizes + kKeySEI = 'sei ', // raw data + + // MPEG user data offsets + kKeyMpegUserData = 'mpud', // size_t[] + + // HDR related + kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo + kKeyHdr10PlusInfo = 'hdrD', // raw data + + // color aspects + kKeyColorRange = 'cRng', // int32_t, color range, value defined by ColorAspects.Range + kKeyColorPrimaries = 'cPrm', // int32_t, + // color Primaries, value defined by ColorAspects.Primaries + kKeyTransferFunction = 'tFun', // int32_t, + // transfer Function, value defined by ColorAspects.Transfer. + kKeyColorMatrix = 'cMtx', // int32_t, + // color Matrix, value defined by ColorAspects.MatrixCoeffs. + kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer) + kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded + + kKeyTileWidth = 'tilW', // int32_t, HEIF tile width + kKeyTileHeight = 'tilH', // int32_t, HEIF tile height + kKeyGridRows = 'grdR', // int32_t, HEIF grid rows + kKeyGridCols = 'grdC', // int32_t, HEIF grid columns + kKeyIccProfile = 'prof', // raw data, ICC profile data + kKeyIsPrimaryImage = 'prim', // bool (int32_t), image track is the primary image + kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track + kKeyExifOffset = 'exof', // int64_t, Exif data offset + kKeyExifSize = 'exsz', // int64_t, Exif data size + kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block + kKeyPcmBigEndian = 'pcmb', // bool (int32_t) + + // Key for ALAC Magic Cookie + kKeyAlacMagicCookie = 'almc', // raw data + + // AC-4 AudioPresentationInfo + kKeyAudioPresentationInfo = 'audP', // raw data +}; + +enum { + kTypeESDS = 'esds', + kTypeAVCC = 'avcc', + kTypeHVCC = 'hvcc', + kTypeD263 = 'd263', +}; + +enum { + kCryptoModeUnencrypted = 0, + kCryptoModeAesCtr = 1, + kCryptoModeAesCbc = 2, +}; + +class Parcel; + +class MetaDataBase { +public: + MetaDataBase(); + MetaDataBase(const MetaDataBase &from); + MetaDataBase& operator = (const MetaDataBase &); + + virtual ~MetaDataBase(); + + enum Type { + TYPE_NONE = 'none', + TYPE_C_STRING = 'cstr', + TYPE_INT32 = 'in32', + TYPE_INT64 = 'in64', + TYPE_FLOAT = 'floa', + TYPE_POINTER = 'ptr ', + TYPE_RECT = 'rect', + }; + + void clear(); + bool remove(uint32_t key); + + bool setCString(uint32_t key, const char *value); + bool setInt32(uint32_t key, int32_t value); + bool setInt64(uint32_t key, int64_t value); + bool setFloat(uint32_t key, float value); + bool setPointer(uint32_t key, void *value); + + bool setRect( + uint32_t key, + int32_t left, int32_t top, + int32_t right, int32_t bottom); + + bool findCString(uint32_t key, const char **value) const; + bool findInt32(uint32_t key, int32_t *value) const; + bool findInt64(uint32_t key, int64_t *value) const; + bool findFloat(uint32_t key, float *value) const; + bool findPointer(uint32_t key, void **value) const; + + bool findRect( + uint32_t key, + int32_t *left, int32_t *top, + int32_t *right, int32_t *bottom) const; + + bool setData(uint32_t key, uint32_t type, const void *data, size_t size); + + bool findData(uint32_t key, uint32_t *type, + const void **data, size_t *size) const; + + bool hasData(uint32_t key) const; + + String8 toString() const; + void dumpToLog() const; + +private: + friend class BpMediaSource; + friend class BnMediaSource; + friend class BnMediaExtractor; + friend class MetaData; + + struct typed_data; + struct Rect; + struct MetaDataInternal; + MetaDataInternal *mInternalData; + status_t writeToParcel(Parcel &parcel); + status_t updateFromParcel(const Parcel &parcel); +}; + +} // namespace android + +#endif // META_DATA_H_ diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp index 31bc8376d0..fb03229d99 100644 --- a/media/libstagefright/omx/tests/Android.bp +++ b/media/libstagefright/omx/tests/Android.bp @@ -9,7 +9,6 @@ cc_test { "libbinder", "libmedia", "libmedia_omx", - "libmediaextractor", "libutils", "liblog", "libstagefright_foundation", diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 73bd2cabe6..74754ea5be 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -78,7 +78,6 @@ cc_library_shared { "libmedia_omx", "libmedia_jni", "libmediadrm", - "libmediaextractor", "libstagefright", "libstagefright_foundation", "libstagefright_bufferqueue_helper", -- GitLab From d9cf3a2fa73ab1e4832c2167d08a4c35738639b1 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 9 Jan 2019 13:12:00 -0800 Subject: [PATCH 0712/1530] Load sw C2 codecs from apex bug: 111407413 test: crosshatch-userdebug builds, verified C2 sw codecs are used in image decoding and audio playback; CTS media heavy presubmit:https://atp.googleplex.com/test_runs/32791402 (failures are pre-existing) Change-Id: I56f1a98d906ecb1b5b56e642b44eb394d58c9318 --- .../mediacodec/MediaCodecUpdateService.cpp | 129 ++++++------------ services/mediacodec/MediaCodecUpdateService.h | 16 +-- services/mediacodec/main_swcodecservice.cpp | 30 +--- 3 files changed, 47 insertions(+), 128 deletions(-) diff --git a/services/mediacodec/MediaCodecUpdateService.cpp b/services/mediacodec/MediaCodecUpdateService.cpp index aee890dd6c..0e6892d645 100644 --- a/services/mediacodec/MediaCodecUpdateService.cpp +++ b/services/mediacodec/MediaCodecUpdateService.cpp @@ -18,14 +18,10 @@ //#define LOG_NDEBUG 0 #include -#include -#include -#include #include #include #include -#include -#include +#include #include "MediaCodecUpdateService.h" @@ -47,90 +43,53 @@ extern "C" { } namespace android { -namespace media { - -binder::Status MediaCodecUpdateService::loadPlugins(const ::std::string& apkPath) { - ALOGV("loadPlugins %s", apkPath.c_str()); - - ZipArchiveHandle zipHandle; - void *registrantLib = NULL; - int32_t ret = OpenArchive(apkPath.c_str(), &zipHandle); - - if (ret == 0) { - char abilist32[PROPERTY_VALUE_MAX]; - property_get("ro.product.cpu.abilist32", abilist32, "armeabi-v7a"); - - auto abis = base::Split(abilist32, ","); - if (abis.empty()) { - ALOGW("abilist is empty, trying armeabi-v7a ..."); - abis.push_back("armeabi-v7a"); - } - - // TODO: Only try the first entry in abilist32 for now. - // We probably should try the next if it fails. - String8 libPathInApk = String8("lib/") + String8(abis[0].c_str()); - String8 defaultLibPath = String8(apkPath.c_str()) + "!/" + libPathInApk; - String8 libPath = defaultLibPath + "/libmedia_codecserviceregistrant.so"; - String8 zipEntryPath = libPathInApk + "/libmedia_codecserviceregistrant.so"; - - ZipEntry entry; - ret = FindEntry(zipHandle, ZipString(zipEntryPath), &entry); - - if (ret == 0) { - android_namespace_t *codecNs = android_create_namespace("codecs", - nullptr, // ld_library_path - defaultLibPath.c_str(), - ANDROID_NAMESPACE_TYPE_ISOLATED, - nullptr, // permitted_when_isolated_path - nullptr); // parent - - if (codecNs != nullptr) { - String8 linked_libraries(LINKED_LIBRARIES); - if (android_link_namespaces( - codecNs, nullptr, linked_libraries.c_str())) { - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = codecNs, - }; - - registrantLib = android_dlopen_ext( - libPath.string(), - RTLD_NOW | RTLD_LOCAL, &dlextinfo); - - if (registrantLib == NULL) { - ALOGE("Failed to load lib from archive: %s", dlerror()); - } - } else { - ALOGE("Failed to link namespace"); - } - } else { - ALOGE("Failed to create codec namespace"); - } - } else { - ALOGE("Failed to find entry (ret=%d)", ret); - } - - CloseArchive(zipHandle); - } else { - ALOGE("Failed to open archive (ret=%d)", ret); + +void loadFromApex(const char *libDirPath) { + ALOGV("loadFromApex: path=%s", libDirPath); + + String8 libPath = String8(libDirPath) + "/libmedia_codecserviceregistrant.so"; + + android_namespace_t *codecNs = android_create_namespace("codecs", + nullptr, // ld_library_path + libDirPath, + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); // parent + + if (codecNs == nullptr) { + ALOGE("Failed to create codec namespace"); + return; + } + + String8 linked_libraries(LINKED_LIBRARIES); + if (!android_link_namespaces(codecNs, nullptr, linked_libraries.c_str())) { + ALOGE("Failed to link namespace"); + return; } - if (registrantLib) { - RegisterCodecServicesFunc registerCodecServices = - reinterpret_cast( - dlsym(registrantLib, "RegisterCodecServices")); - if (registerCodecServices) { - registerCodecServices(); - } else { - LOG(WARNING) << "Cannot register codec services " - "-- corrupted library."; - } - } else { - LOG(ERROR) << "Cannot find codec service registrant."; + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = codecNs, + }; + + void *registrantLib = android_dlopen_ext( + libPath.string(), + RTLD_NOW | RTLD_LOCAL, &dlextinfo); + + if (registrantLib == nullptr) { + ALOGE("Failed to load lib from archive: %s", dlerror()); + } + + RegisterCodecServicesFunc registerCodecServices = + reinterpret_cast( + dlsym(registrantLib, "RegisterCodecServices")); + + if (registerCodecServices == nullptr) { + ALOGE("Cannot register codec services -- corrupted library."); + return; } - return binder::Status::ok(); + registerCodecServices(); } -} // namespace media } // namespace android diff --git a/services/mediacodec/MediaCodecUpdateService.h b/services/mediacodec/MediaCodecUpdateService.h index 7b7cee90df..09d6dbe730 100644 --- a/services/mediacodec/MediaCodecUpdateService.h +++ b/services/mediacodec/MediaCodecUpdateService.h @@ -17,24 +17,10 @@ #ifndef ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H #define ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H -#include -#include - namespace android { -namespace media { -class MediaCodecUpdateService - : public BinderService, public BnMediaUpdateService -{ - friend class BinderService; -public: - MediaCodecUpdateService() : BnMediaUpdateService() { } - virtual ~MediaCodecUpdateService() { } - static const char* getServiceName() { return "media.codec.update"; } - binder::Status loadPlugins(const ::std::string& apkPath); -}; +void loadFromApex(const char *libDirPath); -} // namespace media } // namespace android #endif // ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index 79fea25e15..1168825d66 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -20,11 +20,7 @@ // from LOCAL_C_INCLUDES #include "minijail.h" -#include -#include -#include #include -#include #include "MediaCodecUpdateService.h" @@ -49,32 +45,10 @@ int main(int argc __unused, char** /*argv*/) signal(SIGPIPE, SIG_IGN); SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath); - std::string value = base::GetProperty("ro.build.type", "unknown"); - if (value == "userdebug" || value == "eng") { - media::MediaCodecUpdateService::instantiate(); - } - - android::ProcessState::self()->startThreadPool(); - ::android::hardware::configureRpcThreadpool(64, false); - // Registration of customized codec services - void *registrantLib = dlopen( - "libmedia_codecserviceregistrant.so", - RTLD_NOW | RTLD_LOCAL); - if (registrantLib) { - RegisterCodecServicesFunc registerCodecServices = - reinterpret_cast( - dlsym(registrantLib, "RegisterCodecServices")); - if (registerCodecServices) { - registerCodecServices(); - } else { - LOG(WARNING) << "Cannot register codec services " - "-- corrupted library."; - } - } else { - LOG(ERROR) << "Cannot find codec service registrant."; - } + // codec libs are currently 32-bit only + loadFromApex("/apex/com.android.media.swcodec/lib"); ::android::hardware::joinRpcThreadpool(); } -- GitLab From 9401b553496fa0a6be0513fd6b26758c672151e7 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 7 Jan 2019 16:20:28 -0800 Subject: [PATCH 0713/1530] camera2 vndk: Set up vendor tags in CameraManager Bug: 122477622 Test: AImageReaderVendorTest Change-Id: I3957d8ee5d1b03c263584e1cd79e937a2f8782ac Signed-off-by: Jayant Chowdhary --- camera/ndk/Android.bp | 4 + camera/ndk/ndk_vendor/impl/ACameraManager.cpp | 140 +++++++++++++++++- camera/ndk/ndk_vendor/impl/ACameraManager.h | 2 + .../tests/AImageReaderVendorTest.cpp | 11 +- 4 files changed, 148 insertions(+), 9 deletions(-) diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp index 60c9f851b8..d96f4039e9 100644 --- a/camera/ndk/Android.bp +++ b/camera/ndk/Android.bp @@ -137,6 +137,7 @@ cc_test { shared_libs: [ "libhwbinder", "libcamera2ndk_vendor", + "libcamera_metadata", "libmediandk", "libnativewindow", "libutils", @@ -144,6 +145,9 @@ cc_test { "libcutils", "liblog", ], + static_libs: [ + "android.hardware.camera.common@1.0-helper", + ], cflags: [ "-D__ANDROID_VNDK__", ], diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp index f395b443ee..575ee9dd6f 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -34,7 +34,10 @@ using namespace android::acam; namespace android { namespace acam { -using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId; +using frameworks::cameraservice::service::V2_0::CameraStatusAndId; +using frameworks::cameraservice::common::V2_0::ProviderIdAndVendorTagSections; +using android::hardware::camera::common::V1_0::helper::VendorTagDescriptor; +using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache; // Static member definitions const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; @@ -43,6 +46,104 @@ const char* CameraManagerGlobal::kContextKey = "CallbackContext"; Mutex CameraManagerGlobal::sLock; CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr; +/** + * The vendor tag descriptor class that takes HIDL vendor tag information as + * input. Not part of vendor available VendorTagDescriptor class because that class is used by + * default HAL implementation code as well. + */ +class HidlVendorTagDescriptor : public VendorTagDescriptor { +public: + /** + * Create a VendorTagDescriptor object from the HIDL VendorTagSection + * vector. + * + * Returns OK on success, or a negative error code. + */ + static status_t createDescriptorFromHidl(const hidl_vec& vts, + /*out*/ sp *descriptor); +}; + +status_t HidlVendorTagDescriptor::createDescriptorFromHidl(const hidl_vec &vts, + sp *descriptor) { + int tagCount = 0; + + for (size_t s = 0; s < vts.size(); s++) { + tagCount += vts[s].tags.size(); + } + + if (tagCount < 0 || tagCount > INT32_MAX) { + ALOGE("%s: tag count %d from vendor tag sections is invalid.", __FUNCTION__, tagCount); + return BAD_VALUE; + } + + Vector tagArray; + LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount, + "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount); + + sp desc = new HidlVendorTagDescriptor(); + desc->mTagCount = tagCount; + + KeyedVector tagToSectionMap; + + int idx = 0; + for (size_t s = 0; s < vts.size(); s++) { + const VendorTagSection& section = vts[s]; + const char *sectionName = section.sectionName.c_str(); + if (sectionName == NULL) { + ALOGE("%s: no section name defined for vendor tag section %zu.", __FUNCTION__, s); + return BAD_VALUE; + } + String8 sectionString(sectionName); + desc->mSections.add(sectionString); + + for (size_t j = 0; j < section.tags.size(); j++) { + uint32_t tag = section.tags[j].tagId; + if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { + ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); + return BAD_VALUE; + } + + tagArray.editItemAt(idx++) = section.tags[j].tagId; + + const char *tagName = section.tags[j].tagName.c_str(); + if (tagName == NULL) { + ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag); + return BAD_VALUE; + } + desc->mTagToNameMap.add(tag, String8(tagName)); + tagToSectionMap.add(tag, sectionString); + + int tagType = (int) section.tags[j].tagType; + if (tagType < 0 || tagType >= NUM_TYPES) { + ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); + return BAD_VALUE; + } + desc->mTagToTypeMap.emplace(tag, tagType); + } + } + + for (size_t i = 0; i < tagArray.size(); ++i) { + uint32_t tag = tagArray[i]; + String8 sectionString = tagToSectionMap.valueFor(tag); + + // Set up tag to section index map + ssize_t index = desc->mSections.indexOf(sectionString); + LOG_ALWAYS_FATAL_IF(index < 0, "index %zd must be non-negative", index); + desc->mTagToSectionMap.add(tag, static_cast(index)); + + // Set up reverse mapping + ssize_t reverseIndex = -1; + if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) { + KeyedVector* nameMapper = new KeyedVector(); + reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper); + } + desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag); + } + + *descriptor = std::move(desc); + return OK; +} + CameraManagerGlobal& CameraManagerGlobal::getInstance() { Mutex::Autolock _l(sLock); @@ -80,8 +181,34 @@ static bool isCameraServiceDisabled() { return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0); } -// TODO: Add back when vendor tags are supported for libcamera2ndk_vendor when -// the HIDL interface supports querying by vendor id. +bool CameraManagerGlobal::setupVendorTags() { + sp tagCache = new VendorTagDescriptorCache(); + Status status = Status::NO_ERROR; + std::vector providerIdsAndVts; + auto remoteRet = mCameraService->getCameraVendorTagSections([&status, &providerIdsAndVts] + (Status s, + auto &IdsAndVts) { + status = s; + providerIdsAndVts = IdsAndVts; }); + + if (!remoteRet.isOk() || status != Status::NO_ERROR) { + ALOGE("Failed to retrieve VendorTagSections %s", remoteRet.description().c_str()); + return false; + } + // Convert each providers VendorTagSections into a VendorTagDescriptor and + // add it to the cache + for (auto &providerIdAndVts : providerIdsAndVts) { + sp vendorTagDescriptor; + if (HidlVendorTagDescriptor::createDescriptorFromHidl(providerIdAndVts.vendorTagSections, + &vendorTagDescriptor) != OK) { + ALOGE("Failed to convert from Hidl: VendorTagDescriptor"); + return false; + } + tagCache->addVendorDescriptor(providerIdAndVts.providerId, vendorTagDescriptor); + } + VendorTagDescriptorCache::setAsGlobalVendorTagCache(tagCache); + return true; +} sp CameraManagerGlobal::getCameraService() { Mutex::Autolock _l(mLock); @@ -140,6 +267,13 @@ sp CameraManagerGlobal::getCameraService() { if (!remoteRet.isOk() || status != Status::NO_ERROR) { ALOGE("Failed to add listener to camera service %s", remoteRet.description().c_str()); } + + // Setup vendor tags + if (!setupVendorTags()) { + ALOGE("Unable to set up vendor tags"); + return nullptr; + } + for (auto& c : cameraStatuses) { onStatusChangedLocked(c); } diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h index c8d640ffe7..6b1365a8c1 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.h +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -142,6 +142,8 @@ class CameraManagerGlobal final : public RefBase { void onStatusChanged(const CameraStatusAndId &statusAndId); void onStatusChangedLocked(const CameraStatusAndId &statusAndId); + bool setupVendorTags(); + // Utils for status static bool validStatus(CameraDeviceStatus status); static bool isStatusAvailable(CameraDeviceStatus status); diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index f9bb3acefe..93108b0995 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -37,12 +37,7 @@ #include #include #include - -//#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) -//#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#include namespace { @@ -53,6 +48,8 @@ static constexpr int kTestImageWidth = 640; static constexpr int kTestImageHeight = 480; static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; +using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache; + class CameraHelper { public: CameraHelper(const char* id, ACameraManager *manager) : @@ -527,6 +524,8 @@ class AImageReaderVendorTest : public ::testing::Test { ALOGE("Failed to get cameraIdList: ret=%d", ret); return; } + // TODO: Add more rigorous tests for vendor tags + ASSERT_NE(VendorTagDescriptorCache::getGlobalVendorTagCache(), nullptr); if (mCameraIdList->numCameras < 1) { ALOGW("Device has no camera on board."); return; -- GitLab From ca5642cd9ccd95cae88dd9204958419e279ea532 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Mon, 12 Nov 2018 12:34:42 -0800 Subject: [PATCH 0714/1530] List drm HALs from manifest To support lazy drm HALs, libmediadrm needs to list all available HALs that are defined in the manifest. Otherwise, it will only list HALs that are currently running. This change is necessary because lazy HALs do not run until they are requested. Without this change, libmediadrm would not be aware that the lazy HALs are present, and it would not know to call getService() to start them. Test: Run gts Bug: 112386116 Change-Id: I9b41c60d574b9c8c857b8838a5bbdc64388c9ddb --- drm/libmediadrm/DrmHal.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 66c509f647..981bd5b03a 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -333,10 +333,10 @@ void DrmHal::cleanup() { Vector> DrmHal::makeDrmFactories() { Vector> factories; - auto manager = hardware::defaultServiceManager(); + auto manager = hardware::defaultServiceManager1_2(); if (manager != NULL) { - manager->listByInterface(drm::V1_0::IDrmFactory::descriptor, + manager->listManifestByInterface(drm::V1_0::IDrmFactory::descriptor, [&factories](const hidl_vec ®istered) { for (const auto &instance : registered) { auto factory = drm::V1_0::IDrmFactory::getService(instance); @@ -346,7 +346,7 @@ Vector> DrmHal::makeDrmFactories() { } } ); - manager->listByInterface(drm::V1_1::IDrmFactory::descriptor, + manager->listManifestByInterface(drm::V1_1::IDrmFactory::descriptor, [&factories](const hidl_vec ®istered) { for (const auto &instance : registered) { auto factory = drm::V1_1::IDrmFactory::getService(instance); -- GitLab From bb243cfc4c0a370fa3abe5e82fb9e725393e0b25 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 14 Jan 2019 23:36:54 +0000 Subject: [PATCH 0715/1530] Revert "Fix end-of-stream handling in OggExtractor" This reverts commit db978bbb76431a78aa7d40241251f12acfc298e7. Reason for revert: Broke some ringtones. See bug 122805825 Change-Id: Idcbc2f6ec320cce17502192977677741bbc1ddc1 --- media/extractors/ogg/OggExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index 5abe039326..c3914f183d 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -872,7 +872,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool cal ALOGV("readPage returned %zd", n); - return n == ERROR_END_OF_STREAM ? AMEDIA_ERROR_END_OF_STREAM : AMEDIA_ERROR_UNKNOWN; + return n < 0 ? (media_status_t) n : AMEDIA_ERROR_END_OF_STREAM; } // Prevent a harmless unsigned integer overflow by clamping to 0 -- GitLab From b4e037e01450cbc99191a89e507805974d347899 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 14 Jan 2019 15:56:33 -0800 Subject: [PATCH 0716/1530] AudioFlinger: provide downstream sink device to software patch source When opening an input stream for a software patch, pass in the downstream destination device info. This helps the intermediate module to set up any device-specific processing. Note that the intermediate module must implement Audio HAL V5.0 in order to be able to receive this information. It's not available on the legacy audio HAL API either. Bug: 120859615 Test: make Change-Id: I542a47d2c299fe19f576d5f5c2b237cc00ae5b93 --- media/libaudiohal/impl/DeviceHalHidl.cpp | 13 +++++++++++++ media/libaudiohal/impl/DeviceHalHidl.h | 2 ++ media/libaudiohal/impl/DeviceHalLocal.cpp | 2 ++ media/libaudiohal/impl/DeviceHalLocal.h | 2 ++ .../include/media/audiohal/DeviceHalInterface.h | 2 ++ services/audioflinger/AudioFlinger.cpp | 13 +++++++++---- services/audioflinger/AudioFlinger.h | 4 +++- services/audioflinger/PatchPanel.cpp | 8 +++++++- 8 files changed, 40 insertions(+), 6 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index 7a9e843b48..a1e869f538 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -268,6 +268,8 @@ status_t DeviceHalHidl::openInputStream( audio_input_flags_t flags, const char *address, audio_source_t source, + audio_devices_t outputDevice, + const char *outputDeviceAddress, sp *inStream) { if (mDevice == 0) return NO_INIT; DeviceAddress hidlDevice; @@ -282,6 +284,17 @@ status_t DeviceHalHidl::openInputStream( // TODO: correctly propagate the tracks sources and volume // for now, only send the main source at 1dbfs SinkMetadata sinkMetadata = {{{ .source = AudioSource(source), .gain = 1 }}}; +#endif +#if MAJOR_VERSION < 5 + (void)outputDevice; + (void)outputDeviceAddress; +#else + if (outputDevice != AUDIO_DEVICE_NONE) { + DeviceAddress hidlOutputDevice; + status = deviceAddressFromHal(outputDevice, outputDeviceAddress, &hidlOutputDevice); + if (status != OK) return status; + sinkMetadata.tracks[0].destination.device(std::move(hidlOutputDevice)); + } #endif Return ret = mDevice->openInputStream( handle, diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h index 291c88fbe8..f7d465ffb0 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -86,6 +86,8 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl audio_input_flags_t flags, const char *address, audio_source_t source, + audio_devices_t outputDevice, + const char *outputDeviceAddress, sp *inStream); // Returns whether createAudioPatch and releaseAudioPatch operations are supported. diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp index dffe9da95a..ee6825273a 100644 --- a/media/libaudiohal/impl/DeviceHalLocal.cpp +++ b/media/libaudiohal/impl/DeviceHalLocal.cpp @@ -131,6 +131,8 @@ status_t DeviceHalLocal::openInputStream( audio_input_flags_t flags, const char *address, audio_source_t source, + audio_devices_t /*outputDevice*/, + const char */*outputDeviceAddress*/, sp *inStream) { audio_stream_in_t *halStream; ALOGV("open_input_stream handle: %d devices: %x flags: %#x " diff --git a/media/libaudiohal/impl/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h index 18bd879384..36db72e132 100644 --- a/media/libaudiohal/impl/DeviceHalLocal.h +++ b/media/libaudiohal/impl/DeviceHalLocal.h @@ -79,6 +79,8 @@ class DeviceHalLocal : public DeviceHalInterface audio_input_flags_t flags, const char *address, audio_source_t source, + audio_devices_t outputDevice, + const char *outputDeviceAddress, sp *inStream); // Returns whether createAudioPatch and releaseAudioPatch operations are supported. diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h index 7de8eb3c72..e5652371e2 100644 --- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h @@ -84,6 +84,8 @@ class DeviceHalInterface : public RefBase audio_input_flags_t flags, const char *address, audio_source_t source, + audio_devices_t outputDevice, + const char *outputDeviceAddress, sp *inStream) = 0; // Returns whether createAudioPatch and releaseAudioPatch operations are supported. diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 26f76c03a6..0d6ef46b4c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2379,7 +2379,8 @@ status_t AudioFlinger::openInput(audio_module_handle_t module, return BAD_VALUE; } - sp thread = openInput_l(module, input, config, *devices, address, source, flags); + sp thread = openInput_l( + module, input, config, *devices, address, source, flags, AUDIO_DEVICE_NONE, String8{}); if (thread != 0) { // notify client processes of the new input creation @@ -2395,7 +2396,9 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod audio_devices_t devices, const String8& address, audio_source_t source, - audio_input_flags_t flags) + audio_input_flags_t flags, + audio_devices_t outputDevice, + const String8& outputDeviceAddress) { AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices); if (inHwDev == NULL) { @@ -2424,7 +2427,8 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod sp inHwHal = inHwDev->hwDevice(); sp inStream; status_t status = inHwHal->openInputStream( - *input, devices, &halconfig, flags, address.string(), source, &inStream); + *input, devices, &halconfig, flags, address.string(), source, + outputDevice, outputDeviceAddress, &inStream); ALOGV("openInput_l() openInputStream returned input %p, devices %#x, SamplingRate %d" ", Format %#x, Channels %#x, flags %#x, status %d addr %s", inStream.get(), @@ -2447,7 +2451,8 @@ sp AudioFlinger::openInput_l(audio_module_handle_t mod ALOGV("openInput_l() reopening with proposed sampling rate and channel mask"); inStream.clear(); status = inHwHal->openInputStream( - *input, devices, &halconfig, flags, address.string(), source, &inStream); + *input, devices, &halconfig, flags, address.string(), source, + outputDevice, outputDeviceAddress, &inStream); // FIXME log this new status; HAL should not propose any further changes } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 6c698f63fc..f7696a720e 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -620,7 +620,9 @@ using effect_buffer_t = int16_t; audio_devices_t device, const String8& address, audio_source_t source, - audio_input_flags_t flags); + audio_input_flags_t flags, + audio_devices_t outputDevice, + const String8& outputDeviceAddress); sp openOutput_l(audio_module_handle_t module, audio_io_handle_t *output, audio_config_t *config, diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 538a0eb79f..b2cce1c464 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -211,6 +211,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) && ((patch->sinks[0].ext.device.hw_module != srcModule) || !audioHwDevice->supportsAudioPatches()))) { + audio_devices_t outputDevice = AUDIO_DEVICE_NONE; + String8 outputDeviceAddress; if (patch->num_sources == 2) { if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module != @@ -261,6 +263,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa goto exit; } newPatch.mPlayback.setThread(reinterpret_cast(thread.get())); + outputDevice = device; + outputDeviceAddress = address; } audio_devices_t device = patch->sources[0].ext.device.type; String8 address = String8(patch->sources[0].ext.device.address); @@ -293,7 +297,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa device, address, AUDIO_SOURCE_MIC, - flags); + flags, + outputDevice, + outputDeviceAddress); ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x", thread.get(), config.channel_mask); if (thread == 0) { -- GitLab From 76ba73f7ed1b172efed2dd76bc3877f1a607dc0c Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 14 Jan 2019 17:11:05 -0800 Subject: [PATCH 0717/1530] camera2 ndk: Check for nullptr before assigning captureSequenceId. Bug: 122853890 Test: mm -j64 Test: AImageReaderVendorTest Change-Id: I9520abdaecf60401dad614481ca98195501c52e8 Signed-off-by: Jayant Chowdhary --- camera/ndk/NdkCameraCaptureSession.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp index 540d84e43b..ab796fb751 100644 --- a/camera/ndk/NdkCameraCaptureSession.cpp +++ b/camera/ndk/NdkCameraCaptureSession.cpp @@ -80,7 +80,9 @@ camera_status_t ACameraCaptureSession_capture( if (session->isClosed()) { ALOGE("%s: session %p is already closed", __FUNCTION__, session); - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + if (captureSequenceId != nullptr) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } return ACAMERA_ERROR_SESSION_CLOSED; } -- GitLab From 44c2cc4e1637d6f7c43f14f0dbd6f078497719a2 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Mon, 14 Jan 2019 10:24:18 -0800 Subject: [PATCH 0718/1530] Add support for HDCP 2.3 bug:120040804 Change-Id: I937fb776f93fb0041e6f152bb12b6d1e84c2188f --- drm/libmediadrm/DrmHal.cpp | 54 +++++++++++-------- .../plugins/clearkey/hidl/include/DrmPlugin.h | 8 +++ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index fc847ff72f..2d4b3d8e69 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -63,6 +63,7 @@ using ::android::sp; typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; typedef drm::V1_2::Status Status_V1_2; +typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2; namespace { @@ -156,26 +157,26 @@ static DrmPlugin::OfflineLicenseState toOfflineLicenseState( } } -static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) { +static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel_V1_2 level) { switch(level) { - case HdcpLevel::HDCP_NONE: + case HdcpLevel_V1_2::HDCP_NONE: return DrmPlugin::kHdcpNone; - case HdcpLevel::HDCP_V1: + case HdcpLevel_V1_2::HDCP_V1: return DrmPlugin::kHdcpV1; - case HdcpLevel::HDCP_V2: + case HdcpLevel_V1_2::HDCP_V2: return DrmPlugin::kHdcpV2; - case HdcpLevel::HDCP_V2_1: + case HdcpLevel_V1_2::HDCP_V2_1: return DrmPlugin::kHdcpV2_1; - case HdcpLevel::HDCP_V2_2: + case HdcpLevel_V1_2::HDCP_V2_2: return DrmPlugin::kHdcpV2_2; - case HdcpLevel::HDCP_NO_OUTPUT: + case HdcpLevel_V1_2::HDCP_V2_3: + return DrmPlugin::kHdcpV2_3; + case HdcpLevel_V1_2::HDCP_NO_OUTPUT: return DrmPlugin::kHdcpNoOutput; default: return DrmPlugin::kHdcpLevelUnknown; } } - - static ::KeyedVector toHidlKeyedVector(const KeyedVector& keyedVector) { std::vector stdKeyedVector; @@ -1093,22 +1094,31 @@ status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel *connected, } status_t err = UNKNOWN_ERROR; - if (mPluginV1_1 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; - } - *connected = DrmPlugin::kHdcpLevelUnknown; *max = DrmPlugin::kHdcpLevelUnknown; - Return hResult = mPluginV1_1->getHdcpLevels( - [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) { - if (status == Status::OK) { - *connected = toHdcpLevel(hConnected); - *max = toHdcpLevel(hMax); - } - err = toStatusT(status); - } - ); + Return hResult; + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->getHdcpLevels_1_2( + [&](Status_V1_2 status, const HdcpLevel_V1_2& hConnected, const HdcpLevel_V1_2& hMax) { + if (status == Status_V1_2::OK) { + *connected = toHdcpLevel(hConnected); + *max = toHdcpLevel(hMax); + } + err = toStatusT_1_2(status); + }); + } else if (mPluginV1_1 != NULL) { + hResult = mPluginV1_1->getHdcpLevels( + [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) { + if (status == Status::OK) { + *connected = toHdcpLevel(static_cast(hConnected)); + *max = toHdcpLevel(static_cast(hMax)); + } + err = toStatusT(status); + }); + } else { + return ERROR_DRM_CANNOT_HANDLE; + } return hResult.isOk() ? err : DEAD_OBJECT; } diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h index a9b897b02e..ba5fa65092 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -63,6 +63,7 @@ using ::android::sp; typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; typedef drm::V1_2::Status Status_V1_2; +typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2; struct DrmPlugin : public IDrmPlugin { explicit DrmPlugin(SessionLibrary* sessionLibrary); @@ -162,6 +163,13 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } + Return getHdcpLevels_1_2(getHdcpLevels_1_2_cb _hidl_cb) { + HdcpLevel_V1_2 connectedLevel = HdcpLevel_V1_2::HDCP_NONE; + HdcpLevel_V1_2 maxLevel = HdcpLevel_V1_2::HDCP_NO_OUTPUT; + _hidl_cb(Status_V1_2::OK, connectedLevel, maxLevel); + return Void(); + } + Return getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) override; Return getSecurityLevel(const hidl_vec& sessionId, -- GitLab From 526aa572bbc5dd1fa1b103a98d9f91a758d8d5c4 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 15 Jan 2019 10:54:58 -0800 Subject: [PATCH 0719/1530] audio flinger: clear possible stale audio patch on inputs and outputs In PatchPanel::createAudioPatch() verify that when creating a patch with an output or input mixer as source or sink, no patch with the same output or input mixer remains in the patch list. This will prevent a call to releaseAudioPatch(() received later with a stale patch handle due to delayed request to clear current output or input routing. Test: play music and insert USB headset. Change-Id: I9417bf061f5ef1d93cf3871a9ed51ee2b6ae62ef --- services/audioflinger/PatchPanel.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 538a0eb79f..63a9ec4f2f 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -322,6 +322,13 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } } status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + // remove stale audio patch with same input as sink if any + for (auto& iter : mPatches) { + if (iter.second.mAudioPatch.sinks[0].ext.mix.handle == thread->id()) { + mPatches.erase(iter.first); + break; + } + } } else { sp hwDevice = audioHwDevice->hwDevice(); status = hwDevice->createAudioPatch(patch->num_sources, @@ -376,6 +383,14 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa } status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + + // remove stale audio patch with same output as source if any + for (auto& iter : mPatches) { + if (iter.second.mAudioPatch.sources[0].ext.mix.handle == thread->id()) { + mPatches.erase(iter.first); + break; + } + } } break; default: status = BAD_VALUE; -- GitLab From 11d301045991dd66bf3f64ba510e069b7c09fedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Fri, 2 Nov 2018 16:09:09 +0100 Subject: [PATCH 0720/1530] audiopolicy: apm: Prepare the future: use DeviceDescriptor/DeviceVector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch updates the policy manager and common to widely use (vector of) device descriptor rather than the device type mask each time it is possible. It will allow taking into account the address of the device. Test: manual audio smoke tests Change-Id: I822097f1240d379b2f89aaad1e6609059942e61c Signed-off-by: François Gaffie --- .../include/AudioInputDescriptor.h | 22 +- .../include/AudioOutputDescriptor.h | 84 +- .../include/AudioPolicyMix.h | 7 +- .../include/DeviceDescriptor.h | 19 + .../managerdefinitions/include/HwModule.h | 7 +- .../managerdefinitions/include/IOProfile.h | 49 +- .../src/AudioInputDescriptor.cpp | 36 +- .../src/AudioOutputDescriptor.cpp | 96 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 16 +- .../src/DeviceDescriptor.cpp | 11 + .../managerdefinitions/src/HwModule.cpp | 28 +- .../managerdefinitions/src/IOProfile.cpp | 16 +- .../audiopolicy/enginedefault/src/Engine.cpp | 4 +- .../managerdefault/AudioPolicyManager.cpp | 1206 ++++++++--------- .../managerdefault/AudioPolicyManager.h | 137 +- 15 files changed, 890 insertions(+), 848 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index fa9ba0bf1b..842b7e7636 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -23,11 +23,12 @@ #include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "ClientDescriptor.h" +#include "DeviceDescriptor.h" #include "EffectDescriptor.h" +#include "IOProfile.h" namespace android { -class IOProfile; class AudioMix; class AudioPolicyClientInterface; @@ -42,10 +43,16 @@ public: audio_port_handle_t getId() const; audio_module_handle_t getModuleHandle() const; + audio_devices_t getDeviceType() const { return (mDevice != nullptr) ? + mDevice->type() : AUDIO_DEVICE_NONE; } + sp getDevice() const { return mDevice; } + void setDevice(const sp &device) { mDevice = device; } + DeviceVector supportedDevices() const { + return mProfile != nullptr ? mProfile->getSupportedDevices() : DeviceVector(); } + void dump(String8 *dst) const override; audio_io_handle_t mIoHandle = AUDIO_IO_HANDLE_NONE; // input handle - audio_devices_t mDevice = AUDIO_DEVICE_NONE; // current device this input is routed to AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from @@ -71,8 +78,7 @@ public: void setPatchHandle(audio_patch_handle_t handle) override; status_t open(const audio_config_t *config, - audio_devices_t device, - const String8& address, + const sp &device, audio_source_t source, audio_input_flags_t flags, audio_io_handle_t *input); @@ -99,6 +105,8 @@ public: audio_patch_handle_t mPatchHandle = AUDIO_PATCH_HANDLE_NONE; audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE; + sp mDevice = nullptr; /**< current device this input is routed to */ + // Because a preemptible capture session can preempt another one, we end up in an endless loop // situation were each session is allowed to restart after being preempted, // thus preempting the other one which restarts and so on. @@ -120,8 +128,8 @@ public: sp getInputFromId(audio_port_handle_t id) const; // count active capture sessions using one of the specified devices. - // ignore devices if AUDIO_DEVICE_IN_DEFAULT is passed - uint32_t activeInputsCountOnDevices(audio_devices_t devices = AUDIO_DEVICE_IN_DEFAULT) const; + // ignore devices if empty vector is passed + uint32_t activeInputsCountOnDevices(const DeviceVector &devices) const; /** * return io handle of active input or 0 if no input is active @@ -130,8 +138,6 @@ public: */ Vector > getActiveInputs(); - audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; - sp getInputForClient(audio_port_handle_t portId); void trackEffectEnabled(const sp &effect, bool enabled); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index ed995e0ce0..14b995b25a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -26,13 +26,14 @@ #include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "ClientDescriptor.h" +#include "DeviceDescriptor.h" +#include namespace android { class IOProfile; class AudioMix; class AudioPolicyClientInterface; -class DeviceDescriptor; // descriptor for audio outputs. Used to maintain current configuration of each opened audio output // and keep track of the usage of this output by each audio stream type. @@ -48,14 +49,12 @@ public: void log(const char* indent); audio_port_handle_t getId() const; - virtual audio_devices_t device() const; - virtual bool sharesHwModuleWith(const sp& outputDesc); - virtual audio_devices_t supportedDevices(); + virtual DeviceVector devices() const { return mDevices; } + bool sharesHwModuleWith(const sp& outputDesc); + virtual DeviceVector supportedDevices() const { return mDevices; } virtual bool isDuplicated() const { return false; } virtual uint32_t latency() { return 0; } virtual bool isFixedVolume(audio_devices_t device); - virtual sp subOutput1() { return 0; } - virtual sp subOutput2() { return 0; } virtual bool setVolume(float volume, audio_stream_type_t stream, audio_devices_t device, @@ -119,7 +118,7 @@ public: return mActiveClients; } - audio_devices_t mDevice = AUDIO_DEVICE_NONE; // current device this output is routed to + DeviceVector mDevices; /**< current devices this output is routed to */ nsecs_t mStopTime[AUDIO_STREAM_CNT]; int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible @@ -151,14 +150,15 @@ public: virtual ~SwAudioOutputDescriptor() {} void dump(String8 *dst) const override; - virtual audio_devices_t device() const; - virtual bool sharesHwModuleWith(const sp& outputDesc); - virtual audio_devices_t supportedDevices(); + virtual DeviceVector devices() const; + void setDevices(const DeviceVector &devices) { mDevices = devices; } + bool sharesHwModuleWith(const sp& outputDesc); + virtual DeviceVector supportedDevices() const; virtual uint32_t latency(); virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } virtual bool isFixedVolume(audio_devices_t device); - virtual sp subOutput1() { return mOutput1; } - virtual sp subOutput2() { return mOutput2; } + sp subOutput1() { return mOutput1; } + sp subOutput2() { return mOutput2; } void changeStreamActiveCount( const sp& client, int delta) override; virtual bool setVolume(float volume, @@ -171,22 +171,49 @@ public: const struct audio_port_config *srcConfig = NULL) const; virtual void toAudioPort(struct audio_port *port) const; - status_t open(const audio_config_t *config, - audio_devices_t device, - const String8& address, - audio_stream_type_t stream, - audio_output_flags_t flags, - audio_io_handle_t *output); - // Called when a stream is about to be started - // Note: called before setClientActive(true); - status_t start(); - // Called after a stream is stopped. - // Note: called after setClientActive(false); - void stop(); - void close(); - status_t openDuplicating(const sp& output1, - const sp& output2, - audio_io_handle_t *ioHandle); + status_t open(const audio_config_t *config, + const DeviceVector &devices, + audio_stream_type_t stream, + audio_output_flags_t flags, + audio_io_handle_t *output); + + // Called when a stream is about to be started + // Note: called before setClientActive(true); + status_t start(); + // Called after a stream is stopped. + // Note: called after setClientActive(false); + void stop(); + void close(); + status_t openDuplicating(const sp& output1, + const sp& output2, + audio_io_handle_t *ioHandle); + + /** + * @brief supportsDevice + * @param device to be checked against + * @return true if the device is supported by type (for non bus / remote submix devices), + * true if the device is supported (both type and address) for bus / remote submix + * false otherwise + */ + bool supportsDevice(const sp &device) const; + + /** + * @brief supportsAllDevices + * @param devices to be checked against + * @return true if the device is weakly supported by type (e.g. for non bus / rsubmix devices), + * true if the device is supported (both type and address) for bus / remote submix + * false otherwise + */ + bool supportsAllDevices(const DeviceVector &devices) const; + + /** + * @brief filterSupportedDevices takes a vector of devices and filters them according to the + * device supported by this output (the profile from which this output derives from) + * @param devices reference device vector to be filtered + * @return vector of devices filtered from the supported devices of this output (weakly or not + * depending on the device type) + */ + DeviceVector filterSupportedDevices(const DeviceVector &devices) const; const sp mProfile; // I/O profile this output derives from audio_io_handle_t mIoHandle; // output handle @@ -208,7 +235,6 @@ public: void dump(String8 *dst) const override; - virtual audio_devices_t supportedDevices(); virtual bool setVolume(float volume, audio_stream_type_t stream, audio_devices_t device, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 955e87bd04..e6a62d9a8e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -16,6 +16,7 @@ #pragma once +#include "DeviceDescriptor.h" #include #include #include @@ -74,9 +75,9 @@ public: status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid, sp &desc); - audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availableDeviceTypes, - AudioMix **policyMix); + sp getDeviceAndMixForInputSource(audio_source_t inputSource, + const DeviceVector &availableDeviceTypes, + AudioMix **policyMix); status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index d02123c760..9181c9a976 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -125,6 +125,8 @@ public: */ DeviceVector filter(const DeviceVector &devices) const; + DeviceVector filter(audio_devices_t deviceTypes) const; + /** * @brief merge two vectors. As SortedVector Implementation is buggy (it does not check the size * of the destination vector, only of the source, it provides a safe implementation @@ -164,6 +166,23 @@ public: return !operator==(right); } + /** + * @brief getFirstValidAddress + * @return the first valid address of a list of device, "" if no device with valid address + * found. + * This helper function helps maintaining compatibility with legacy where we used to have a + * devices mask and an address. + */ + String8 getFirstValidAddress() const + { + for (const auto &device : *this) { + if (device->address() != "") { + return device->address(); + } + } + return String8(""); + } + std::string toString() const; void dump(String8 *dst, const String8 &tag, int spaces = 0, bool verbose = true) const; diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h index 2b57fa9dac..69126ba0aa 100644 --- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -113,7 +113,12 @@ class HwModuleCollection : public Vector > public: sp getModuleFromName(const char *name) const; - sp getModuleForDevice(audio_devices_t device) const; + sp getModuleForDeviceTypes(audio_devices_t device) const; + + sp getModuleForDevice(const sp &device) const; + + DeviceVector getAvailableDevicesFromModuleName(const char *name, + const DeviceVector &availableDevices) const; sp getDeviceDescriptor(const audio_devices_t device, const char *device_address, diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index ca6ca5671e..7761625ede 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -57,12 +57,25 @@ public: } } - // This method is used for input and direct output, and is not used for other output. - // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. - // For input, flags is interpreted as audio_input_flags_t. - // TODO: merge audio_output_flags_t and audio_input_flags_t. - bool isCompatibleProfile(audio_devices_t device, - const String8& address, + /** + * @brief isCompatibleProfile: This method is used for input and direct output, + * and is not used for other output. + * Checks if the IO profile is compatible with specified parameters. + * For input, flags is interpreted as audio_input_flags_t. + * TODO: merge audio_output_flags_t and audio_input_flags_t. + * + * @param devices vector of devices to be checked for compatibility + * @param samplingRate to be checked for compatibility. Must be specified + * @param updatedSamplingRate if non-NULL, it is assigned the actual sample rate. + * @param format to be checked for compatibility. Must be specified + * @param updatedFormat if non-NULL, it is assigned the actual format + * @param channelMask to be checked for compatibility. Must be specified + * @param updatedChannelMask if non-NULL, it is assigned the actual channel mask + * @param flags to be checked for compatibility + * @param exactMatchRequiredForInputFlags true if exact match is required on flags + * @return true if the profile is compatible, false otherwise. + */ + bool isCompatibleProfile(const DeviceVector &devices, uint32_t samplingRate, uint32_t *updatedSamplingRate, audio_format_t format, @@ -78,7 +91,7 @@ public: bool hasSupportedDevices() const { return !mSupportedDevices.isEmpty(); } - bool supportDevice(audio_devices_t device) const + bool supportsDeviceTypes(audio_devices_t device) const { if (audio_is_output_devices(device)) { return mSupportedDevices.types() & device; @@ -91,6 +104,23 @@ public: return mSupportedDevices[0]->address() == address; } + /** + * @brief supportsDevice + * @param device to be checked against + * forceCheckOnAddress if true, check on type and address whatever the type, otherwise + * the address enforcement is limited to "offical devices" that distinguishe on address + * @return true if the device is supported by type (for non bus / remote submix devices), + * true if the device is supported (both type and address) for bus / remote submix + * false otherwise + */ + bool supportsDevice(const sp &device, bool forceCheckOnAddress = false) const + { + if (!device_distinguishes_on_address(device->type()) && !forceCheckOnAddress) { + return supportsDeviceTypes(device->type()); + } + return mSupportedDevices.contains(device); + } + // chose first device present in mSupportedDevices also part of deviceType audio_devices_t getSupportedDeviceForType(audio_devices_t deviceType) const { @@ -116,11 +146,6 @@ public: mSupportedDevices = devices; } - sp getSupportedDeviceByAddress(audio_devices_t type, String8 address) const - { - return mSupportedDevices.getDevice(type, address); - } - const DeviceVector &getSupportedDevices() const { return mSupportedDevices; } bool canOpenNewIo() { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 0bc88a5dcb..c483ffea32 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -21,7 +21,6 @@ #include #include #include "AudioInputDescriptor.h" -#include "IOProfile.h" #include "AudioGain.h" #include "HwModule.h" @@ -180,8 +179,7 @@ audio_config_base_t AudioInputDescriptor::getConfig() const } status_t AudioInputDescriptor::open(const audio_config_t *config, - audio_devices_t device, - const String8& address, + const sp &device, audio_source_t source, audio_input_flags_t flags, audio_io_handle_t *input) @@ -198,24 +196,26 @@ status_t AudioInputDescriptor::open(const audio_config_t *config, mDevice = device; - ALOGV("opening input for device %08x address %s profile %p name %s", - mDevice, address.string(), mProfile.get(), mProfile->getName().string()); + ALOGV("opening input for device %s profile %p name %s", + mDevice->toString().c_str(), mProfile.get(), mProfile->getName().string()); + + audio_devices_t deviceType = mDevice->type(); status_t status = mClientInterface->openInput(mProfile->getModuleHandle(), input, &lConfig, - &mDevice, - address, + &deviceType, + mDevice->address(), source, flags); - LOG_ALWAYS_FATAL_IF(mDevice != device, + LOG_ALWAYS_FATAL_IF(mDevice->type() != deviceType, "%s openInput returned device %08x when given device %08x", - __FUNCTION__, mDevice, device); + __FUNCTION__, mDevice->type(), deviceType); if (status == NO_ERROR) { LOG_ALWAYS_FATAL_IF(*input == AUDIO_IO_HANDLE_NONE, - "%s openInput returned input handle %d for device %08x", - __FUNCTION__, *input, device); + "%s openInput returned input handle %d for device %s", + __FUNCTION__, *input, mDevice->toString().c_str()); mSamplingRate = lConfig.sample_rate; mChannelMask = lConfig.channel_mask; mFormat = lConfig.format; @@ -423,7 +423,7 @@ void AudioInputDescriptor::dump(String8 *dst) const dst->appendFormat(" Sampling rate: %d\n", mSamplingRate); dst->appendFormat(" Format: %d\n", mFormat); dst->appendFormat(" Channels: %08x\n", mChannelMask); - dst->appendFormat(" Devices %08x\n", mDevice); + dst->appendFormat(" Devices %s\n", mDevice->toString().c_str()); getEnabledEffects().dump(dst, 1 /*spaces*/, false /*verbose*/); dst->append(" AudioRecord Clients:\n"); ClientMapHandler::dump(dst); @@ -452,14 +452,13 @@ sp AudioInputCollection::getInputFromId(audio_port_handle_ return NULL; } -uint32_t AudioInputCollection::activeInputsCountOnDevices(audio_devices_t devices) const +uint32_t AudioInputCollection::activeInputsCountOnDevices(const DeviceVector &devices) const { uint32_t count = 0; for (size_t i = 0; i < size(); i++) { const sp inputDescriptor = valueAt(i); if (inputDescriptor->isActive() && - ((devices == AUDIO_DEVICE_IN_DEFAULT) || - ((inputDescriptor->mDevice & devices & ~AUDIO_DEVICE_BIT_IN) != 0))) { + (devices.isEmpty() || devices.contains(inputDescriptor->getDevice()))) { count++; } } @@ -479,13 +478,6 @@ Vector > AudioInputCollection::getActiveInputs() return activeInputs; } -audio_devices_t AudioInputCollection::getSupportedDevices(audio_io_handle_t handle) const -{ - sp inputDesc = valueFor(handle); - audio_devices_t devices = inputDesc->mProfile->getSupportedDevicesType(); - return devices; -} - sp AudioInputCollection::getInputForClient(audio_port_handle_t portId) { for (size_t i = 0; i < size(); i++) { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 97504abce6..578d4937a0 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -82,25 +82,10 @@ audio_port_handle_t AudioOutputDescriptor::getId() const return mId; } -audio_devices_t AudioOutputDescriptor::device() const -{ - return mDevice; -} - -audio_devices_t AudioOutputDescriptor::supportedDevices() -{ - return mDevice; -} - bool AudioOutputDescriptor::sharesHwModuleWith( const sp& outputDesc) { - if (outputDesc->isDuplicated()) { - return sharesHwModuleWith(outputDesc->subOutput1()) || - sharesHwModuleWith(outputDesc->subOutput2()); - } else { - return hasSameHwModuleAs(outputDesc); - } + return hasSameHwModuleAs(outputDesc); } void AudioOutputDescriptor::changeStreamActiveCount(const sp& client, @@ -282,7 +267,7 @@ void AudioOutputDescriptor::dump(String8 *dst) const dst->appendFormat(" Sampling rate: %d\n", mSamplingRate); dst->appendFormat(" Format: %08x\n", mFormat); dst->appendFormat(" Channels: %08x\n", mChannelMask); - dst->appendFormat(" Devices: %08x\n", device()); + dst->appendFormat(" Devices: %s\n", devices().toString().c_str()); dst->appendFormat(" Global active count: %u\n", mGlobalActiveCount); dst->append(" Stream volume activeCount muteCount\n"); for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { @@ -330,17 +315,18 @@ void SwAudioOutputDescriptor::dump(String8 *dst) const AudioOutputDescriptor::dump(dst); } -audio_devices_t SwAudioOutputDescriptor::device() const +DeviceVector SwAudioOutputDescriptor::devices() const { if (isDuplicated()) { - return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); - } else { - return mDevice; + DeviceVector devices = mOutput1->devices(); + devices.merge(mOutput2->devices()); + return devices; } + return mDevices; } bool SwAudioOutputDescriptor::sharesHwModuleWith( - const sp& outputDesc) + const sp& outputDesc) { if (isDuplicated()) { return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); @@ -352,13 +338,34 @@ bool SwAudioOutputDescriptor::sharesHwModuleWith( } } -audio_devices_t SwAudioOutputDescriptor::supportedDevices() +DeviceVector SwAudioOutputDescriptor::supportedDevices() const { if (isDuplicated()) { - return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); - } else { - return mProfile->getSupportedDevicesType(); + DeviceVector supportedDevices = mOutput1->supportedDevices(); + supportedDevices.merge(mOutput2->supportedDevices()); + return supportedDevices; + } + return mProfile->getSupportedDevices(); +} + +bool SwAudioOutputDescriptor::supportsDevice(const sp &device) const +{ + // Performs weak check until dynamic support of supportedDevice on Modules/Profiles + if (!device_distinguishes_on_address(device->type())) { + return supportedDevices().types() & (device->type()); } + return supportedDevices().contains(device); +} + +bool SwAudioOutputDescriptor::supportsAllDevices(const DeviceVector &devices) const +{ + return supportedDevices().containsAllDevices(devices); +} + +DeviceVector SwAudioOutputDescriptor::filterSupportedDevices(const DeviceVector &devices) const +{ + DeviceVector filteredDevices = supportedDevices(); + return filteredDevices.filter(devices); } uint32_t SwAudioOutputDescriptor::latency() @@ -443,12 +450,15 @@ bool SwAudioOutputDescriptor::setVolume(float volume, } status_t SwAudioOutputDescriptor::open(const audio_config_t *config, - audio_devices_t device, - const String8& address, + const DeviceVector &devices, audio_stream_type_t stream, audio_output_flags_t flags, audio_io_handle_t *output) { + mDevices = devices; + const String8& address = devices.getFirstValidAddress(); + audio_devices_t device = devices.types(); + audio_config_t lConfig; if (config == nullptr) { lConfig = AUDIO_CONFIG_INITIALIZER; @@ -459,7 +469,6 @@ status_t SwAudioOutputDescriptor::open(const audio_config_t *config, lConfig = *config; } - mDevice = device; // if the selected profile is offloaded and no offload info was specified, // create a default one if ((mProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && @@ -477,19 +486,19 @@ status_t SwAudioOutputDescriptor::open(const audio_config_t *config, mFlags = (audio_output_flags_t)(mFlags | flags); - ALOGV("opening output for device %08x address %s profile %p name %s", - mDevice, address.string(), mProfile.get(), mProfile->getName().string()); + ALOGV("opening output for device %s profile %p name %s", + mDevices.toString().c_str(), mProfile.get(), mProfile->getName().string()); status_t status = mClientInterface->openOutput(mProfile->getModuleHandle(), output, &lConfig, - &mDevice, + &device, address, &mLatency, mFlags); - LOG_ALWAYS_FATAL_IF(mDevice != device, + LOG_ALWAYS_FATAL_IF(mDevices.types() != device, "%s openOutput returned device %08x when given device %08x", - __FUNCTION__, mDevice, device); + __FUNCTION__, mDevices.types(), device); if (status == NO_ERROR) { LOG_ALWAYS_FATAL_IF(*output == AUDIO_IO_HANDLE_NONE, @@ -605,11 +614,6 @@ void HwAudioOutputDescriptor::dump(String8 *dst) const mSource->dump(dst, 0, 0); } -audio_devices_t HwAudioOutputDescriptor::supportedDevices() -{ - return mDevice; -} - void HwAudioOutputDescriptor::toAudioPortConfig( struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const @@ -657,7 +661,7 @@ bool SwAudioOutputCollection::isStreamActiveLocally(audio_stream_type_t stream, for (size_t i = 0; i < this->size(); i++) { const sp outputDesc = this->valueAt(i); if (outputDesc->isStreamActive(stream, inPastMs, sysTime) - && ((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) == 0)) { + && ((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) == 0)) { return true; } } @@ -670,7 +674,7 @@ bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream, nsecs_t sysTime = systemTime(); for (size_t i = 0; i < size(); i++) { const sp outputDesc = valueAt(i); - if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && + if (((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && outputDesc->isStreamActive(stream, inPastMs, sysTime)) { // do not consider re routing (when the output is going to a dynamic policy) // as "remote playback" @@ -686,7 +690,8 @@ audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const { for (size_t i = 0; i < size(); i++) { sp outputDesc = valueAt(i); - if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { + if (!outputDesc->isDuplicated() && + outputDesc->devices().types() & AUDIO_DEVICE_OUT_ALL_A2DP) { return this->keyAt(i); } } @@ -700,10 +705,9 @@ bool SwAudioOutputCollection::isA2dpOffloadedOnPrimary() const if ((primaryOutput != NULL) && (primaryOutput->mProfile != NULL) && (primaryOutput->mProfile->getModule() != NULL)) { sp primaryHwModule = primaryOutput->mProfile->getModule(); - Vector > primaryHwModuleOutputProfiles = - primaryHwModule->getOutputProfiles(); - for (size_t i = 0; i < primaryHwModuleOutputProfiles.size(); i++) { - if (primaryHwModuleOutputProfiles[i]->supportDevice(AUDIO_DEVICE_OUT_ALL_A2DP)) { + + for (const auto &outputProfile : primaryHwModule->getOutputProfiles()) { + if (outputProfile->supportsDeviceTypes(AUDIO_DEVICE_OUT_ALL_A2DP)) { return true; } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 776d98f850..054a35ce70 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -280,13 +280,11 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } -audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availDevices, - AudioMix **policyMix) +sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( + audio_source_t inputSource, const DeviceVector &availDevices, AudioMix **policyMix) { for (size_t i = 0; i < size(); i++) { AudioMix *mix = valueAt(i)->getMix(); - if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; } @@ -295,17 +293,21 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so mix->mCriteria[j].mValue.mSource == inputSource) || (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule && mix->mCriteria[j].mValue.mSource != inputSource)) { - if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + // assuming PolicyMix only for remote submix for input + // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX + audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + auto mixDevice = availDevices.getDevice(device, mix->mDeviceAddress); + if (mixDevice != nullptr) { if (policyMix != NULL) { *policyMix = mix; } - return AUDIO_DEVICE_IN_REMOTE_SUBMIX; + return mixDevice; } break; } } } - return AUDIO_DEVICE_NONE; + return nullptr; } status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 04cbcd141e..7137786d0f 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -335,6 +335,17 @@ DeviceVector DeviceVector::filter(const DeviceVector &devices) const return filteredDevices; } +DeviceVector DeviceVector::filter(audio_devices_t deviceTypes) const +{ + DeviceVector filteredDevices; + for (const auto &device : *this) { + if ((device->type() & deviceTypes) == device->type()) { + filteredDevices.add(device); + } + } + return filteredDevices; +} + bool DeviceVector::containsAtLeastOne(const DeviceVector &devices) const { return !filter(devices).isEmpty(); diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 80af88d53e..3547a056f9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -260,13 +260,13 @@ sp HwModuleCollection::getModuleFromName(const char *name) const return nullptr; } -sp HwModuleCollection::getModuleForDevice(audio_devices_t device) const +sp HwModuleCollection::getModuleForDeviceTypes(audio_devices_t device) const { for (const auto& module : *this) { const auto& profiles = audio_is_output_device(device) ? module->getOutputProfiles() : module->getInputProfiles(); for (const auto& profile : profiles) { - if (profile->supportDevice(device)) { + if (profile->supportsDeviceTypes(device)) { return module; } } @@ -274,6 +274,30 @@ sp HwModuleCollection::getModuleForDevice(audio_devices_t device) con return nullptr; } +sp HwModuleCollection::getModuleForDevice(const sp &device) const +{ + for (const auto& module : *this) { + const auto& profiles = audio_is_output_device(device->type()) ? + module->getOutputProfiles() : module->getInputProfiles(); + for (const auto& profile : profiles) { + if (profile->supportsDevice(device)) { + return module; + } + } + } + return nullptr; +} + +DeviceVector HwModuleCollection::getAvailableDevicesFromModuleName( + const char *name, const DeviceVector &availableDevices) const +{ + sp module = getModuleFromName(name); + if (module == nullptr) { + return DeviceVector(); + } + return availableDevices.getDevicesFromHwModule(module->getHandle()); +} + sp HwModuleCollection::getDeviceDescriptor(const audio_devices_t device, const char *device_address, const char *device_name, diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp index 3788244783..fe2eaee244 100644 --- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp @@ -25,11 +25,7 @@ namespace android { -// checks if the IO profile is compatible with specified parameters. -// Sampling rate, format and channel mask must be specified in order to -// get a valid a match -bool IOProfile::isCompatibleProfile(audio_devices_t device, - const String8& address, +bool IOProfile::isCompatibleProfile(const DeviceVector &devices, uint32_t samplingRate, uint32_t *updatedSamplingRate, audio_format_t format, @@ -46,14 +42,8 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device, getType() == AUDIO_PORT_TYPE_MIX && getRole() == AUDIO_PORT_ROLE_SINK; ALOG_ASSERT(isPlaybackThread != isRecordThread); - - if (device != AUDIO_DEVICE_NONE) { - // just check types if multiple devices are selected - if (popcount(device & ~AUDIO_DEVICE_BIT_IN) > 1) { - if ((mSupportedDevices.types() & device) != device) { - return false; - } - } else if (mSupportedDevices.getDevice(device, address) == 0) { + if (!devices.isEmpty()) { + if (!mSupportedDevices.containsAllDevices(devices)) { return false; } } diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 69395f3e55..bb8db30cf7 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -322,7 +322,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // a primary device // FIXME: this is not the right way of solving this problem audio_devices_t availPrimaryOutputDevices = - (primaryOutput->supportedDevices() | AUDIO_DEVICE_OUT_HEARING_AID) & + (primaryOutput->supportedDevices().types() | AUDIO_DEVICE_OUT_HEARING_AID) & availableOutputDevices.types(); if (((availableInputDevices.types() & @@ -475,7 +475,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // compressed format as they would likely not be mixed and dropped. for (size_t i = 0; i < outputs.size(); i++) { sp desc = outputs.valueAt(i); - audio_devices_t devices = desc->device() & + audio_devices_t devices = desc->devices().types() & (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC); if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) && devices != AUDIO_DEVICE_NONE) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5544821afd..47e6016614 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -88,36 +88,35 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, return status; } -void AudioPolicyManager::broadcastDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const String8 &device_address) +void AudioPolicyManager::broadcastDeviceConnectionState(const sp &device, + audio_policy_dev_state_t state) { - AudioParameter param(device_address); + AudioParameter param(device->address()); const String8 key(state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE ? AudioParameter::keyStreamConnect : AudioParameter::keyStreamDisconnect); - param.addInt(key, device); + param.addInt(key, device->type()); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); } -status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, +status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceType, audio_policy_dev_state_t state, const char *device_address, const char *device_name) { ALOGV("setDeviceConnectionStateInt() device: 0x%X, state %d, address %s name %s", - device, state, device_address, device_name); + deviceType, state, device_address, device_name); // connect/disconnect only 1 device at a time - if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + if (!audio_is_output_device(deviceType) && !audio_is_input_device(deviceType)) return BAD_VALUE; - sp devDesc = - mHwModules.getDeviceDescriptor(device, device_address, device_name); + sp device = + mHwModules.getDeviceDescriptor(deviceType, device_address, device_name); // handle output devices - if (audio_is_output_device(device)) { + if (audio_is_output_device(deviceType)) { SortedVector outputs; - ssize_t index = mAvailableOutputDevices.indexOf(devDesc); + ssize_t index = mAvailableOutputDevices.indexOf(device); // save a copy of the opened output descriptors before any output is opened or closed // by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies() @@ -127,19 +126,19 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // handle output device connection case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { if (index >= 0) { - ALOGW("setDeviceConnectionState() device already connected: %x", device); + ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - ALOGV("setDeviceConnectionState() connecting device %x", device); + ALOGV("%s() connecting device %s", __func__, device->toString().c_str()); // register new device as available - index = mAvailableOutputDevices.add(devDesc); + index = mAvailableOutputDevices.add(device); if (index >= 0) { - sp module = mHwModules.getModuleForDevice(device); + sp module = mHwModules.getModuleForDeviceTypes(deviceType); if (module == 0) { - ALOGD("setDeviceConnectionState() could not find HW module for device %08x", - device); - mAvailableOutputDevices.remove(devDesc); + ALOGD("setDeviceConnectionState() could not find HW module for device %s", + device->toString().c_str()); + mAvailableOutputDevices.remove(device); return INVALID_OPERATION; } mAvailableOutputDevices[index]->attach(module); @@ -149,48 +148,46 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // Before checking outputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the outputs...) - broadcastDeviceConnectionState(device, state, devDesc->address()); + broadcastDeviceConnectionState(device, state); - if (checkOutputsForDevice(devDesc, state, outputs, devDesc->address()) != NO_ERROR) { - mAvailableOutputDevices.remove(devDesc); + if (checkOutputsForDevice(device, state, outputs) != NO_ERROR) { + mAvailableOutputDevices.remove(device); - broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - devDesc->address()); + broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); return INVALID_OPERATION; } // Propagate device availability to Engine - mEngine->setDeviceConnectionState(devDesc, state); + mEngine->setDeviceConnectionState(device, state); // outputs should never be empty here ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():" "checkOutputsForDevice() returned no outputs but status OK"); - ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs", - outputs.size()); + ALOGV("%s() checkOutputsForDevice() returned %zu outputs", __func__, outputs.size()); } break; // handle output device disconnection case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { if (index < 0) { - ALOGW("setDeviceConnectionState() device not connected: %x", device); + ALOGW("%s() device not connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - ALOGV("setDeviceConnectionState() disconnecting output device %x", device); + ALOGV("%s() disconnecting output device %s", __func__, device->toString().c_str()); // Send Disconnect to HALs - broadcastDeviceConnectionState(device, state, devDesc->address()); + broadcastDeviceConnectionState(device, state); // remove device from available output devices - mAvailableOutputDevices.remove(devDesc); + mAvailableOutputDevices.remove(device); - checkOutputsForDevice(devDesc, state, outputs, devDesc->address()); + checkOutputsForDevice(device, state, outputs); // Propagate device availability to Engine - mEngine->setDeviceConnectionState(devDesc, state); + mEngine->setDeviceConnectionState(device, state); } break; default: - ALOGE("setDeviceConnectionState() invalid state: %x", state); + ALOGE("%s() invalid state: %x", __func__, state); return BAD_VALUE; } @@ -199,8 +196,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, if (!outputs.isEmpty()) { for (audio_io_handle_t output : outputs) { sp desc = mOutputs.valueFor(output); - // close unused outputs after device disconnection or direct outputs that have been - // opened by checkOutputsForDevice() to query dynamic parameters + // close unused outputs after device disconnection or direct outputs that have + // been opened by checkOutputsForDevice() to query dynamic parameters if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && (desc->mDirectOpenCount == 0))) { @@ -214,29 +211,28 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, }); if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) { - audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); - updateCallRouting(newDevice); + DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/); + updateCallRouting(newDevices); } - const audio_devices_t msdOutDevice = getModuleDeviceTypes( - mAvailableOutputDevices, AUDIO_HARDWARE_MODULE_ID_MSD); + const DeviceVector msdOutDevices = getMsdAudioOutDevices(); for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) { - audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/); + DeviceVector newDevices = getNewOutputDevices(desc, true /*fromCache*/); // do not force device change on duplicated output because if device is 0, it will // also force a device 0 for the two outputs it is duplicated to which may override // a valid device selection on those outputs. - bool force = (msdOutDevice == AUDIO_DEVICE_NONE || msdOutDevice != desc->device()) + bool force = (msdOutDevices.isEmpty() || msdOutDevices != desc->devices()) && !desc->isDuplicated() - && (!device_distinguishes_on_address(device) + && (!device_distinguishes_on_address(deviceType) // always force when disconnecting (a non-duplicated device) || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)); - setOutputDevice(desc, newDevice, force, 0); + setOutputDevices(desc, newDevices, force, 0); } } if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { - cleanUpForDevice(devDesc); + cleanUpForDevice(device); } mpClientInterface->onAudioPortListUpdate(); @@ -244,36 +240,35 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, } // end if is output device // handle input devices - if (audio_is_input_device(device)) { + if (audio_is_input_device(deviceType)) { SortedVector inputs; - ssize_t index = mAvailableInputDevices.indexOf(devDesc); + ssize_t index = mAvailableInputDevices.indexOf(device); switch (state) { // handle input device connection case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { if (index >= 0) { - ALOGW("setDeviceConnectionState() device already connected: %d", device); + ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - sp module = mHwModules.getModuleForDevice(device); + sp module = mHwModules.getModuleForDeviceTypes(deviceType); if (module == NULL) { ALOGW("setDeviceConnectionState(): could not find HW module for device %08x", - device); + deviceType); return INVALID_OPERATION; } // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) - broadcastDeviceConnectionState(device, state, devDesc->address()); + broadcastDeviceConnectionState(device, state); - if (checkInputsForDevice(devDesc, state, inputs, devDesc->address()) != NO_ERROR) { - broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - devDesc->address()); + if (checkInputsForDevice(device, state, inputs) != NO_ERROR) { + broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); return INVALID_OPERATION; } - index = mAvailableInputDevices.add(devDesc); + index = mAvailableInputDevices.add(device); if (index >= 0) { mAvailableInputDevices[index]->attach(module); } else { @@ -281,30 +276,30 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, } // Propagate device availability to Engine - mEngine->setDeviceConnectionState(devDesc, state); + mEngine->setDeviceConnectionState(device, state); } break; // handle input device disconnection case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { if (index < 0) { - ALOGW("setDeviceConnectionState() device not connected: %d", device); + ALOGW("%s() device not connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - ALOGV("setDeviceConnectionState() disconnecting input device %x", device); + ALOGV("%s() disconnecting input device %s", __func__, device->toString().c_str()); // Set Disconnect to HALs - broadcastDeviceConnectionState(device, state, devDesc->address()); + broadcastDeviceConnectionState(device, state); - checkInputsForDevice(devDesc, state, inputs, devDesc->address()); - mAvailableInputDevices.remove(devDesc); + checkInputsForDevice(device, state, inputs); + mAvailableInputDevices.remove(device); // Propagate device availability to Engine - mEngine->setDeviceConnectionState(devDesc, state); + mEngine->setDeviceConnectionState(device, state); } break; default: - ALOGE("setDeviceConnectionState() invalid state: %x", state); + ALOGE("%s() invalid state: %x", __func__, state); return BAD_VALUE; } @@ -314,19 +309,19 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, updateDevicesAndOutputs(); if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) { - audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); - updateCallRouting(newDevice); + DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/); + updateCallRouting(newDevices); } if (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { - cleanUpForDevice(devDesc); + cleanUpForDevice(device); } mpClientInterface->onAudioPortListUpdate(); return NO_ERROR; } // end if is input device - ALOGW("setDeviceConnectionState() invalid device: %x", device); + ALOGW("%s() invalid device: %s", __func__, device->toString().c_str()); return BAD_VALUE; } @@ -350,7 +345,7 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi } else if (audio_is_input_device(device)) { deviceVector = &mAvailableInputDevices; } else { - ALOGW("getDeviceConnectionState() invalid device type %08x", device); + ALOGW("%s() invalid device type %08x", __func__, device); return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; } @@ -425,16 +420,19 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, return NO_ERROR; } -uint32_t AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs) +uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs) { bool createTxPatch = false; uint32_t muteWaitMs = 0; - if(!hasPrimaryOutput() || mPrimaryOutput->device() == AUDIO_DEVICE_OUT_STUB) { + if(!hasPrimaryOutput() || mPrimaryOutput->devices().types() == AUDIO_DEVICE_OUT_STUB) { return muteWaitMs; } - audio_devices_t txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); - ALOGV("updateCallRouting device rxDevice %08x txDevice %08x", rxDevice, txDevice); + ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device"); + + auto txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + ALOGV("updateCallRouting device rxDevice %s txDevice %s", + rxDevices.toString().c_str(), txDevice->toString().c_str()); // release existing RX patch if any if (mCallRxPatch != 0) { @@ -450,16 +448,15 @@ uint32_t AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, uint32_ // If the RX device is on the primary HW module, then use legacy routing method for voice calls // via setOutputDevice() on primary output. // Otherwise, create two audio patches for TX and RX path. - if (availablePrimaryOutputDevices() & rxDevice) { - muteWaitMs = setOutputDevice(mPrimaryOutput, rxDevice, true, delayMs); + if (availablePrimaryOutputDevices().contains(rxDevices.itemAt(0))) { + muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs); // If the TX device is also on the primary HW module, setOutputDevice() will take care // of it due to legacy implementation. If not, create a patch. - if ((availablePrimaryInputDevices() & txDevice & ~AUDIO_DEVICE_BIT_IN) - == AUDIO_DEVICE_NONE) { + if (!availablePrimaryModuleInputDevices().contains(txDevice)) { createTxPatch = true; } } else { // create RX path audio patch - mCallRxPatch = createTelephonyPatch(true /*isRx*/, rxDevice, delayMs); + mCallRxPatch = createTelephonyPatch(true /*isRx*/, rxDevices.itemAt(0), delayMs); createTxPatch = true; } if (createTxPatch) { // create TX path audio patch @@ -470,26 +467,31 @@ uint32_t AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, uint32_ } sp AudioPolicyManager::createTelephonyPatch( - bool isRx, audio_devices_t device, uint32_t delayMs) { + bool isRx, const sp &device, uint32_t delayMs) { PatchBuilder patchBuilder; - sp txSourceDeviceDesc; + if (device == nullptr) { + return nullptr; + } if (isRx) { - patchBuilder.addSink(findDevice(mAvailableOutputDevices, device)). - addSource(findDevice(mAvailableInputDevices, AUDIO_DEVICE_IN_TELEPHONY_RX)); + patchBuilder.addSink(device). + addSource(mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX)); } else { - patchBuilder.addSource(txSourceDeviceDesc = findDevice(mAvailableInputDevices, device)). - addSink(findDevice(mAvailableOutputDevices, AUDIO_DEVICE_OUT_TELEPHONY_TX)); + patchBuilder.addSource(device). + addSink(mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX)); } - audio_devices_t outputDevice = isRx ? device : AUDIO_DEVICE_OUT_TELEPHONY_TX; - SortedVector outputs = getOutputsForDevice(outputDevice, mOutputs); - audio_io_handle_t output = selectOutput(outputs); + // @TODO: still ignoring the address, or not dealing platform with mutliple telephonydevices + const sp outputDevice = isRx ? + device : mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX); + SortedVector outputs = + getOutputsForDevices(DeviceVector(outputDevice), mOutputs); + audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); // request to reuse existing output stream if one is already opened to reach the target device if (output != AUDIO_IO_HANDLE_NONE) { sp outputDesc = mOutputs.valueFor(output); - ALOG_ASSERT(!outputDesc->isDuplicated(), - "%s() %#x device output %d is duplicated", __func__, outputDevice, output); + ALOG_ASSERT(!outputDesc->isDuplicated(), "%s() %s device output %d is duplicated", __func__, + outputDevice->toString().c_str(), output); patchBuilder.addSource(outputDesc, { .stream = AUDIO_STREAM_PATCH }); } @@ -499,7 +501,7 @@ sp AudioPolicyManager::createTelephonyPatch( // call TX device but this information is not in the audio patch and logic here must be // symmetric to the one in startInput() for (const auto& activeDesc : mInputs.getActiveInputs()) { - if (activeDesc->hasSameHwModuleAs(txSourceDeviceDesc)) { + if (activeDesc->hasSameHwModuleAs(device)) { closeActiveClients(activeDesc); } } @@ -599,17 +601,17 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) } if (hasPrimaryOutput()) { - // Note that despite the fact that getNewOutputDevice() is called on the primary output, + // Note that despite the fact that getNewOutputDevices() is called on the primary output, // the device returned is not necessarily reachable via this output - audio_devices_t rxDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/); // force routing command to audio hardware when ending call // even if no device change is needed - if (isStateInCall(oldState) && rxDevice == AUDIO_DEVICE_NONE) { - rxDevice = mPrimaryOutput->device(); + if (isStateInCall(oldState) && rxDevices.isEmpty()) { + rxDevices = mPrimaryOutput->devices(); } if (state == AUDIO_MODE_IN_CALL) { - updateCallRouting(rxDevice, delayMs); + updateCallRouting(rxDevices, delayMs); } else if (oldState == AUDIO_MODE_IN_CALL) { if (mCallRxPatch != 0) { mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0); @@ -619,18 +621,18 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0); mCallTxPatch.clear(); } - setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + setOutputDevices(mPrimaryOutput, rxDevices, force, 0); } else { - setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + setOutputDevices(mPrimaryOutput, rxDevices, force, 0); } } // reevaluate routing on all outputs in case tracks have been started during the call for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); - audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/); + DeviceVector newDevices = getNewOutputDevices(desc, true /*fromCache*/); if (state != AUDIO_MODE_IN_CALL || desc != mPrimaryOutput) { - setOutputDevice(desc, newDevice, (newDevice != AUDIO_DEVICE_NONE), 0 /*delayMs*/); + setOutputDevices(desc, newDevices, !newDevices.isEmpty(), 0 /*delayMs*/); } } @@ -654,7 +656,7 @@ audio_mode_t AudioPolicyManager::getPhoneState() { } void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, - audio_policy_forced_cfg_t config) + audio_policy_forced_cfg_t config) { ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mEngine->getPhoneState()); if (config == mEngine->getForceUse(usage)) { @@ -680,26 +682,25 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, delayMs = TOUCH_SOUND_FIXED_DELAY_MS; } if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) { - audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/); - waitMs = updateCallRouting(newDevice, delayMs); + DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/); + waitMs = updateCallRouting(newDevices, delayMs); } for (size_t i = 0; i < mOutputs.size(); i++) { sp outputDesc = mOutputs.valueAt(i); - audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/); + DeviceVector newDevices = getNewOutputDevices(outputDesc, true /*fromCache*/); if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) { - waitMs = setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE), - delayMs); + waitMs = setOutputDevices(outputDesc, newDevices, !newDevices.isEmpty(), delayMs); } - if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { - applyStreamVolumes(outputDesc, newDevice, waitMs, true); + if (forceVolumeReeval && !newDevices.isEmpty()) { + applyStreamVolumes(outputDesc, newDevices.types(), waitMs, true); } } for (const auto& activeDesc : mInputs.getActiveInputs()) { - audio_devices_t newDevice = getNewInputDevice(activeDesc); + auto newDevice = getNewInputDevice(activeDesc); // Force new input selection if the new device can not be reached via current input if (activeDesc->mProfile->getSupportedDevices().types() & - (newDevice & ~AUDIO_DEVICE_BIT_IN)) { + (newDevice->type() & ~AUDIO_DEVICE_BIT_IN)) { setInputDevice(activeDesc->mIoHandle, newDevice); } else { closeInput(activeDesc->mIoHandle); @@ -715,7 +716,7 @@ void AudioPolicyManager::setSystemProperty(const char* property, const char* val // Find an output profile compatible with the parameters passed. When "directOnly" is set, restrict // search to profiles for direct outputs. sp AudioPolicyManager::getProfileForOutput( - audio_devices_t device, + const DeviceVector& devices, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -736,7 +737,7 @@ sp AudioPolicyManager::getProfileForOutput( for (const auto& hwModule : mHwModules) { for (const auto& curProfile : hwModule->getOutputProfiles()) { - if (!curProfile->isCompatibleProfile(device, String8(""), + if (!curProfile->isCompatibleProfile(devices, samplingRate, NULL /*updatedSamplingRate*/, format, NULL /*updatedFormat*/, channelMask, NULL /*updatedChannelMask*/, @@ -744,7 +745,7 @@ sp AudioPolicyManager::getProfileForOutput( continue; } // reject profiles not corresponding to a device currently available - if ((mAvailableOutputDevices.types() & curProfile->getSupportedDevicesType()) == 0) { + if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) { continue; } if (!directOnly) return curProfile; @@ -765,7 +766,7 @@ sp AudioPolicyManager::getProfileForOutput( audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) { routing_strategy strategy = getStrategy(stream); - audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); // Note that related method getOutputForAttr() uses getOutputForDevice() not selectOutput(). // We use selectOutput() here since we don't have the desired AudioTrack sample rate, @@ -773,10 +774,11 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) // getOutput() solely on audio_stream_type such as AudioSystem::getOutputFrameCount() // and AudioSystem::getOutputSamplingRate(). - SortedVector outputs = getOutputsForDevice(device, mOutputs); - audio_io_handle_t output = selectOutput(outputs); + SortedVector outputs = getOutputsForDevices(devices, mOutputs); + audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); - ALOGV("getOutput() stream %d selected device %08x, output %d", stream, device, output); + ALOGV("getOutput() stream %d selected devices %s, output %d", stream, + devices.toString().c_str(), output); return output; } @@ -813,12 +815,11 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId) { - DeviceVector outputDevices; + DeviceVector devices; routing_strategy strategy; - audio_devices_t device; + audio_devices_t deviceType = AUDIO_DEVICE_NONE; const audio_port_handle_t requestedDeviceId = *selectedDeviceId; - audio_devices_t msdDevice = - getModuleDeviceTypes(mAvailableOutputDevices, AUDIO_HARDWARE_MODULE_ID_MSD); + DeviceVector msdDevices = getMsdAudioOutDevices(); status_t status = getAudioAttributes(resultAttr, attr, *stream); if (status != NO_ERROR) { @@ -839,7 +840,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { sp deviceDesc = mAvailableOutputDevices.getDeviceFromId(requestedDeviceId); - device = deviceDesc->type(); + deviceType = deviceDesc->type(); } else { // If no explict route, is there a matching dynamic policy that applies? sp desc; @@ -863,7 +864,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, ALOGW("%s no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE", __func__); return BAD_VALUE; } - device = getDeviceForStrategy(strategy, false /*fromCache*/); + deviceType = getDeviceForStrategy(strategy, false /*fromCache*/); } if ((resultAttr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { @@ -875,7 +876,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, // FIXME: provide a more generic approach which is not device specific and move this back // to getOutputForDevice. // TODO: Remove check of AUDIO_STREAM_MUSIC once migration is completed on the app side. - if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX && + if (deviceType == AUDIO_DEVICE_OUT_TELEPHONY_TX && (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) && audio_is_linear_pcm(config->format) && isInCall()) { @@ -883,34 +884,36 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC; } else { // Get the devce type directly from the engine to bypass preferred route logic - device = mEngine->getDeviceForStrategy(strategy); + deviceType = mEngine->getDeviceForStrategy(strategy); } } ALOGV("%s device 0x%x, sampling rate %d, format %#x, channel mask %#x, " "flags %#x", - __func__, device, config->sample_rate, config->format, config->channel_mask, *flags); + __func__, + deviceType, config->sample_rate, config->format, config->channel_mask, *flags); *output = AUDIO_IO_HANDLE_NONE; - if (msdDevice != AUDIO_DEVICE_NONE) { - *output = getOutputForDevice(msdDevice, session, *stream, config, flags); - if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(device) == NO_ERROR) { - ALOGV("%s() Using MSD device 0x%x instead of device 0x%x", - __func__, msdDevice, device); - device = msdDevice; + if (!msdDevices.isEmpty()) { + *output = getOutputForDevices(msdDevices, session, *stream, config, flags); + sp deviceDesc = mAvailableOutputDevices.getDevice(deviceType); + if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(deviceDesc) == NO_ERROR) { + ALOGV("%s() Using MSD devices %s instead of device %s", + __func__, msdDevices.toString().c_str(), deviceDesc->toString().c_str()); + deviceType = msdDevices.types(); } else { *output = AUDIO_IO_HANDLE_NONE; } } + devices = mAvailableOutputDevices.getDevicesFromTypeMask(deviceType); if (*output == AUDIO_IO_HANDLE_NONE) { - *output = getOutputForDevice(device, session, *stream, config, flags); + *output = getOutputForDevices(devices, session, *stream, config, flags); } if (*output == AUDIO_IO_HANDLE_NONE) { return INVALID_OPERATION; } - outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); - *selectedDeviceId = getFirstDeviceId(outputDevices); + *selectedDeviceId = getFirstDeviceId(devices); ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId); @@ -958,8 +961,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, return NO_ERROR; } -audio_io_handle_t AudioPolicyManager::getOutputForDevice( - audio_devices_t device, +audio_io_handle_t AudioPolicyManager::getOutputForDevices( + const DeviceVector &devices, audio_session_t session, audio_stream_type_t stream, const audio_config_t *config, @@ -1017,7 +1020,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if (((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) { - profile = getProfileForOutput(device, + profile = getProfileForOutput(devices, config->sample_rate, config->format, config->channel_mask, @@ -1037,7 +1040,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( (config->channel_mask == desc->mChannelMask) && (session == desc->mDirectClientSession)) { desc->mDirectOpenCount++; - ALOGI("getOutputForDevice() reusing direct output %d for session %d", + ALOGI("%s reusing direct output %d for session %d", __func__, mOutputs.keyAt(i), session); return mOutputs.keyAt(i); } @@ -1051,8 +1054,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( sp outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface); - DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device); - String8 address = getFirstDeviceAddress(outputDevices); + String8 address = getFirstDeviceAddress(devices); // MSD patch may be using the only output stream that can service this request. Release // MSD patch to prioritize this request over any active output on MSD. @@ -1062,7 +1064,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) { const struct audio_port_config *sink = &patch->mPatch.sinks[j]; if (sink->type == AUDIO_PORT_TYPE_DEVICE && - (sink->ext.device.type & device) != AUDIO_DEVICE_NONE && + (sink->ext.device.type & devices.types()) != AUDIO_DEVICE_NONE && (address.isEmpty() || strncmp(sink->ext.device.address, address.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) { releaseAudioPatch(patch->mHandle, mUidCached); @@ -1071,15 +1073,15 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( } } - status = outputDesc->open(config, device, address, stream, *flags, &output); + status = outputDesc->open(config, devices, stream, *flags, &output); // only accept an output with the requested parameters if (status != NO_ERROR || (config->sample_rate != 0 && config->sample_rate != outputDesc->mSamplingRate) || (config->format != AUDIO_FORMAT_DEFAULT && config->format != outputDesc->mFormat) || (config->channel_mask != 0 && config->channel_mask != outputDesc->mChannelMask)) { - ALOGV("getOutputForDevice() failed opening direct output: output %d sample rate %d %d," - "format %d %d, channel mask %04x %04x", output, config->sample_rate, + ALOGV("%s failed opening direct output: output %d sample rate %d %d," + "format %d %d, channel mask %04x %04x", __func__, output, config->sample_rate, outputDesc->mSamplingRate, config->format, outputDesc->mFormat, config->channel_mask, outputDesc->mChannelMask); if (output != AUDIO_IO_HANDLE_NONE) { @@ -1097,7 +1099,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( addOutput(output, outputDesc); mPreviousOutputs = mOutputs; - ALOGV("getOutputForDevice() returns new direct output %d", output); + ALOGV("%s returns new direct output %d", __func__, output); mpClientInterface->onAudioPortListUpdate(); return output; } @@ -1118,14 +1120,14 @@ non_direct_output: if (audio_is_linear_pcm(config->format)) { // get which output is suitable for the specified stream. The actual // routing change will happen when startOutput() will be called - SortedVector outputs = getOutputsForDevice(device, mOutputs); + SortedVector outputs = getOutputsForDevices(devices, mOutputs); // at this stage we should ignore the DIRECT flag as no direct output could be found earlier *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT); output = selectOutput(outputs, *flags, config->format, config->channel_mask, config->sample_rate); } - ALOGW_IF((output == 0), "getOutputForDevice() could not find output for stream %d, " + ALOGW_IF((output == 0), "getOutputForDevices() could not find output for stream %d, " "sampling rate %d, format %#x, channels %#x, flags %#x", stream, config->sample_rate, config->format, config->channel_mask, *flags); @@ -1133,13 +1135,14 @@ non_direct_output: } sp AudioPolicyManager::getMsdAudioInDevice() const { - sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); - if (msdModule != 0) { - DeviceVector msdInputDevices = mAvailableInputDevices.getDevicesFromHwModule( - msdModule->getHandle()); - if (!msdInputDevices.isEmpty()) return msdInputDevices.itemAt(0); - } - return 0; + auto msdInDevices = mHwModules.getAvailableDevicesFromModuleName(AUDIO_HARDWARE_MODULE_ID_MSD, + mAvailableInputDevices); + return msdInDevices.isEmpty()? nullptr : msdInDevices.itemAt(0); +} + +DeviceVector AudioPolicyManager::getMsdAudioOutDevices() const { + return mHwModules.getAvailableDevicesFromModuleName(AUDIO_HARDWARE_MODULE_ID_MSD, + mAvailableOutputDevices); } const AudioPatchCollection AudioPolicyManager::getMsdPatches() const { @@ -1160,7 +1163,7 @@ const AudioPatchCollection AudioPolicyManager::getMsdPatches() const { return msdPatches; } -status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDevice, +status_t AudioPolicyManager::getBestMsdAudioProfileFor(const sp &outputDevice, bool hwAvSync, audio_port_config *sourceConfig, audio_port_config *sinkConfig) const { sp msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD); @@ -1170,7 +1173,7 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDev } sp deviceModule = mHwModules.getModuleForDevice(outputDevice); if (deviceModule == nullptr) { - ALOGE("%s() unable to get module for %#x", __func__, outputDevice); + ALOGE("%s() unable to get module for %s", __func__, outputDevice->toString().c_str()); return NO_INIT; } const InputProfileCollection &inputProfiles = msdModule->getInputProfiles(); @@ -1180,7 +1183,7 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDev } const OutputProfileCollection &outputProfiles = deviceModule->getOutputProfiles(); if (outputProfiles.isEmpty()) { - ALOGE("%s() no output profiles for device %#x", __func__, outputDevice); + ALOGE("%s() no output profiles for device %s", __func__, outputDevice->toString().c_str()); return NO_INIT; } AudioProfileVector msdProfiles; @@ -1201,8 +1204,8 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDev compressedFormatsOrder, surroundChannelMasksOrder, true /*preferHigherSamplingRates*/, &bestSinkConfig); if (result != NO_ERROR) { - ALOGD("%s() no matching profiles found for device: %#x, hwAvSync: %d", - __func__, outputDevice, hwAvSync); + ALOGD("%s() no matching profiles found for device: %s, hwAvSync: %d", + __func__, outputDevice->toString().c_str(), hwAvSync); return result; } sinkConfig->sample_rate = bestSinkConfig.sample_rate; @@ -1231,11 +1234,10 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(audio_devices_t outputDev return NO_ERROR; } -PatchBuilder AudioPolicyManager::buildMsdPatch(audio_devices_t outputDevice) const +PatchBuilder AudioPolicyManager::buildMsdPatch(const sp &outputDevice) const { PatchBuilder patchBuilder; - patchBuilder.addSource(getMsdAudioInDevice()). - addSink(findDevice(mAvailableOutputDevices, outputDevice)); + patchBuilder.addSource(getMsdAudioInDevice()).addSink(outputDevice); audio_port_config sourceConfig = patchBuilder.patch()->sources[0]; audio_port_config sinkConfig = patchBuilder.patch()->sinks[0]; // TODO: Figure out whether MSD module has HW_AV_SYNC flag set in the AP config file. @@ -1253,15 +1255,18 @@ PatchBuilder AudioPolicyManager::buildMsdPatch(audio_devices_t outputDevice) con return patchBuilder; } -status_t AudioPolicyManager::setMsdPatch(audio_devices_t outputDevice) { - ALOGV("%s() for outputDevice %#x", __func__, outputDevice); - if (outputDevice == AUDIO_DEVICE_NONE) { +status_t AudioPolicyManager::setMsdPatch(const sp &outputDevice) { + sp device = outputDevice; + if (device == nullptr) { // Use media strategy for unspecified output device. This should only // occur on checkForDeviceAndOutputChanges(). Device connection events may // therefore invalidate explicit routing requests. - outputDevice = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + DeviceVector devices = getDevicesForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + LOG_ALWAYS_FATAL_IF(devices.isEmpty(), "no outpudevice to set Msd Patch"); + device = devices.itemAt(0); } - PatchBuilder patchBuilder = buildMsdPatch(outputDevice); + ALOGV("%s() for device %s", __func__, device->toString().c_str()); + PatchBuilder patchBuilder = buildMsdPatch(device); const struct audio_patch* patch = patchBuilder.patch(); const AudioPatchCollection msdPatches = getMsdPatches(); if (!msdPatches.isEmpty()) { @@ -1277,8 +1282,9 @@ status_t AudioPolicyManager::setMsdPatch(audio_devices_t outputDevice) { patch, 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/); ALOGE_IF(status != NO_ERROR, "%s() error %d creating MSD audio patch", __func__, status); ALOGI_IF(status == NO_ERROR, "%s() Patch created from MSD_IN to " - "device:%#x (format:%#x channels:%#x samplerate:%d)", __func__, outputDevice, - patch->sources[0].format, patch->sources[0].channel_mask, patch->sources[0].sample_rate); + "device:%s (format:%#x channels:%#x samplerate:%d)", __func__, + device->toString().c_str(), patch->sources[0].format, + patch->sources[0].channel_mask, patch->sources[0].sample_rate); return status; } @@ -1289,7 +1295,7 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector& outp bool force = !outputDesc->isActive() && (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE); - audio_devices_t device = AUDIO_DEVICE_NONE; + DeviceVector devices; AudioMix *policyMix = NULL; const char *address = NULL; if (outputDesc->mPolicyMix != NULL) { policyMix = outputDesc->mPolicyMix; + audio_devices_t newDeviceType; address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { - device = policyMix->mDeviceType; + newDeviceType = policyMix->mDeviceType; } else { - device = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } + devices.add(mAvailableOutputDevices.getDevice(newDeviceType, String8(address))); } // requiresMuteCheck is false when we can bypass mute strategy. @@ -1476,8 +1484,8 @@ status_t AudioPolicyManager::startSource(const sp& outp outputDesc->setClientActive(client, true); if (client->hasPreferredDevice(true)) { - device = getNewOutputDevice(outputDesc, false /*fromCache*/); - if (device != outputDesc->device()) { + devices = getNewOutputDevices(outputDesc, false /*fromCache*/); + if (devices != outputDesc->devices()) { checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle); } } @@ -1486,10 +1494,10 @@ status_t AudioPolicyManager::startSource(const sp& outp selectOutputForMusicEffects(); } - if (outputDesc->streamActiveCount(stream) == 1 || device != AUDIO_DEVICE_NONE) { + if (outputDesc->streamActiveCount(stream) == 1 || !devices.isEmpty()) { // starting an output being rerouted? - if (device == AUDIO_DEVICE_NONE) { - device = getNewOutputDevice(outputDesc, false /*fromCache*/); + if (devices.isEmpty()) { + devices = getNewOutputDevices(outputDesc, false /*fromCache*/); } routing_strategy strategy = getStrategy(stream); @@ -1498,13 +1506,13 @@ status_t AudioPolicyManager::startSource(const sp& outp (beaconMuteLatency > 0); uint32_t waitMs = beaconMuteLatency; for (size_t i = 0; i < mOutputs.size(); i++) { - sp desc = mOutputs.valueAt(i); + sp desc = mOutputs.valueAt(i); if (desc != outputDesc) { // An output has a shared device if // - managed by the same hw module // - supports the currently selected device const bool sharedDevice = outputDesc->sharesHwModuleWith(desc) - && (desc->supportedDevices() & device) != AUDIO_DEVICE_NONE; + && (!desc->filterSupportedDevices(devices).isEmpty()); // force a device change if any other output is: // - managed by the same hw module @@ -1514,7 +1522,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // In this case, the audio HAL must receive the new device selection so that it can // change the device currently selected by the other output. if (sharedDevice && - desc->device() != device && + desc->devices() != devices && desc->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) { force = true; } @@ -1537,13 +1545,13 @@ status_t AudioPolicyManager::startSource(const sp& outp } const uint32_t muteWaitMs = - setOutputDevice(outputDesc, device, force, 0, NULL, address, requiresMuteCheck); + setOutputDevices(outputDesc, devices, force, 0, NULL, requiresMuteCheck); // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, - mVolumeCurves->getVolumeIndex(stream, outputDesc->device()), + mVolumeCurves->getVolumeIndex(stream, outputDesc->devices().types()), outputDesc, - outputDesc->device()); + outputDesc->devices().types()); // update the outputs if starting an output with a stream that can affect notification // routing @@ -1574,7 +1582,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // Automatically enable the remote submix input when output is started on a re routing mix // of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(device) && policyMix != NULL && + if (audio_is_remote_submix_device(devices.types()) && policyMix != NULL && policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, @@ -1619,7 +1627,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu if (outputDesc->streamActiveCount(stream) == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(outputDesc->mDevice) && + if (audio_is_remote_submix_device(outputDesc->devices().types()) && outputDesc->mPolicyMix != NULL && outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, @@ -1640,33 +1648,31 @@ status_t AudioPolicyManager::stopSource(const sp& outpu // store time at which the stream was stopped - see isStreamActive() if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) { outputDesc->mStopTime[stream] = systemTime(); - audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); + DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when // the track stop() command is received and at that time the audio track buffer can // still contain data that needs to be drained. The latency only covers the audio HAL // and kernel buffers. Also the latency does not always include additional delay in the // audio path (audio DSP, CODEC ...) - setOutputDevice(outputDesc, newDevice, false, outputDesc->latency()*2); + setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2); // force restoring the device selection on other active outputs if it differs from the // one being selected for this output uint32_t delayMs = outputDesc->latency()*2; for (size_t i = 0; i < mOutputs.size(); i++) { - sp desc = mOutputs.valueAt(i); + sp desc = mOutputs.valueAt(i); if (desc != outputDesc && desc->isActive() && outputDesc->sharesHwModuleWith(desc) && - (newDevice != desc->device())) { - audio_devices_t newDevice2 = getNewOutputDevice(desc, false /*fromCache*/); - bool force = desc->device() != newDevice2; - - setOutputDevice(desc, - newDevice2, - force, - delayMs); + (newDevices != desc->devices())) { + DeviceVector newDevices2 = getNewOutputDevices(desc, false /*fromCache*/); + bool force = desc->devices() != newDevices2; + + setOutputDevices(desc, newDevices2, force, delayMs); + // re-apply device specific volume if not done by setOutputDevice() if (!force) { - applyStreamVolumes(desc, newDevice2, delayMs); + applyStreamVolumes(desc, newDevices2.types(), delayMs); } } } @@ -1739,17 +1745,14 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, attr->source, config->sample_rate, config->format, config->channel_mask, session, flags); status_t status = NO_ERROR; - // handle legacy remote submix case where the address was not always specified - String8 address = String8(""); audio_source_t halInputSource; audio_source_t inputSource = attr->source; AudioMix *policyMix = NULL; - DeviceVector inputDevices; + sp device; sp inputDesc; sp clientDesc; audio_port_handle_t requestedDeviceId = *selectedDeviceId; bool isSoundTrigger; - audio_devices_t device; // The supplied portId must be AUDIO_PORT_HANDLE_NONE if (*portId != AUDIO_PORT_HANDLE_NONE) { @@ -1761,7 +1764,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } // Explicit routing? - sp deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId); + sp explicitRoutingDevice = + mAvailableInputDevices.getDeviceFromId(*selectedDeviceId); // special case for mmap capture: if an input IO handle is specified, we reuse this input if // possible @@ -1802,7 +1806,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } } *inputType = API_INPUT_LEGACY; - device = inputDesc->mDevice; + device = inputDesc->getDevice(); ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session); goto exit; @@ -1820,35 +1824,29 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, goto error; } *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; - device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; - address = String8(attr->tags + strlen("addr=")); + device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + String8(attr->tags + strlen("addr="))); } else { - if (deviceDesc != 0) { - device = deviceDesc->type(); + if (explicitRoutingDevice != nullptr) { + device = explicitRoutingDevice; } else { device = getDeviceAndMixForInputSource(inputSource, &policyMix); } - if (device == AUDIO_DEVICE_NONE) { + if (device == nullptr) { ALOGW("getInputForAttr() could not find device for source %d", inputSource); status = BAD_VALUE; goto error; } - if (policyMix != NULL) { - address = policyMix->mDeviceAddress; - if (policyMix->mMixType == MIX_TYPE_RECORDERS) { - // there is an external policy, but this input is attached to a mix of recorders, - // meaning it receives audio injected into the framework, so the recorder doesn't - // know about it and is therefore considered "legacy" - *inputType = API_INPUT_LEGACY; - } else { - // recording a mix of players defined by an external policy, we're rerouting for - // an external policy - *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; - } - } else if (audio_is_remote_submix_device(device)) { - address = String8("0"); + if (policyMix != nullptr) { + ALOG_ASSERT(policyMix->mMixType == MIX_TYPE_RECORDERS, "Invalid Mix Type"); + // there is an external policy, but this input is attached to a mix of recorders, + // meaning it receives audio injected into the framework, so the recorder doesn't + // know about it and is therefore considered "legacy" + *inputType = API_INPUT_LEGACY; + } else if (audio_is_remote_submix_device(device->type())) { + device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8("0")); *inputType = API_INPUT_MIX_CAPTURE; - } else if (device == AUDIO_DEVICE_IN_TELEPHONY_RX) { + } else if (device->type() == AUDIO_DEVICE_IN_TELEPHONY_RX) { *inputType = API_INPUT_TELEPHONY_RX; } else { *inputType = API_INPUT_LEGACY; @@ -1856,7 +1854,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } - *input = getInputForDevice(device, address, session, inputSource, + *input = getInputForDevice(device, session, inputSource, config, flags, policyMix); if (*input == AUDIO_IO_HANDLE_NONE) { @@ -1866,16 +1864,15 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, exit: - inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); - *selectedDeviceId = getFirstDeviceId(inputDevices); + *selectedDeviceId = mAvailableInputDevices.contains(device) ? + device->getId() : AUDIO_PORT_HANDLE_NONE; isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD && mSoundTriggerSessions.indexOfKey(session) > 0; *portId = AudioPort::getNextUniqueId(); - clientDesc = new RecordClientDescriptor(*portId, uid, session, - *attr, *config, requestedDeviceId, - inputSource,flags, isSoundTrigger); + clientDesc = new RecordClientDescriptor(*portId, uid, session, *attr, *config, + requestedDeviceId, inputSource,flags, isSoundTrigger); inputDesc = mInputs.valueFor(*input); inputDesc->addClient(clientDesc); @@ -1889,8 +1886,7 @@ error: } -audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, - String8 address, +audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp &device, audio_session_t session, audio_source_t inputSource, const audio_config_base_t *config, @@ -1926,8 +1922,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, audio_input_flags_t profileFlags = flags; for (;;) { profileFormat = config->format; // reset each time through loop, in case it is updated - profile = getInputProfile(device, address, - profileSamplingRate, profileFormat, profileChannelMask, + profile = getInputProfile(device, profileSamplingRate, profileFormat, profileChannelMask, profileFlags); if (profile != 0) { break; // success @@ -1936,9 +1931,9 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) { profileFlags = AUDIO_INPUT_FLAG_NONE; // retry } else { // fail - ALOGW("getInputForDevice() could not find profile for device 0x%X, " - "sampling rate %u, format %#x, channel mask 0x%X, flags %#x", - device, config->sample_rate, config->format, config->channel_mask, flags); + ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, " + "channel mask 0x%X, flags %#x", __func__, device->toString().c_str(), + config->sample_rate, config->format, config->channel_mask, flags); return input; } } @@ -1995,14 +1990,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, lConfig.channel_mask = profileChannelMask; lConfig.format = profileFormat; - if (address == "") { - DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device); - // the inputs vector must be of size >= 1, but we don't want to crash here - address = getFirstDeviceAddress(inputDevices); - } - - status_t status = inputDesc->open(&lConfig, device, address, - halInputSource, profileFlags, &input); + status_t status = inputDesc->open(&lConfig, device, halInputSource, profileFlags, &input); // only accept input with the exact requested set of parameters if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE || @@ -2059,7 +2047,7 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) // indicate active capture to sound trigger service if starting capture from a mic on // primary HW module - audio_devices_t device = getNewInputDevice(inputDesc); + sp device = getNewInputDevice(inputDesc); setInputDevice(input, device, true /* force */); if (inputDesc->activeCount() == 1) { @@ -2070,8 +2058,8 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) MIX_STATE_MIXING); } - audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); - if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices(); + if (primaryInputDevices.contains(device) && mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) { SoundTrigger::setCaptureState(true); } @@ -2079,7 +2067,7 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) // automatically enable the remote submix output when input is started if not // used by a policy mix of type MIX_TYPE_RECORDERS // For remote submix (a virtual device), we open only one input per capture request. - if (audio_is_remote_submix_device(inputDesc->mDevice)) { + if (audio_is_remote_submix_device(inputDesc->getDeviceType())) { String8 address = String8(""); if (inputDesc->mPolicyMix == NULL) { address = String8("0"); @@ -2130,7 +2118,7 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) // automatically disable the remote submix output when input is stopped if not // used by a policy mix of type MIX_TYPE_RECORDERS - if (audio_is_remote_submix_device(inputDesc->mDevice)) { + if (audio_is_remote_submix_device(inputDesc->getDeviceType())) { String8 address = String8(""); if (inputDesc->mPolicyMix == NULL) { address = String8("0"); @@ -2143,14 +2131,12 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) address, "remote-submix"); } } - - audio_devices_t device = inputDesc->mDevice; resetInputDevice(input); // indicate inactive capture to sound trigger service if stopping capture from a mic on // primary HW module - audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); - if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices(); + if (primaryInputDevices.contains(inputDesc->getDevice()) && mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { SoundTrigger::setCaptureState(false); } @@ -2280,7 +2266,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, status_t status = NO_ERROR; for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); - audio_devices_t curDevice = desc->device(); + audio_devices_t curDevice = desc->devices().types(); for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) { continue; @@ -2356,8 +2342,8 @@ audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() // 4: the first output in the list routing_strategy strategy = getStrategy(AUDIO_STREAM_MUSIC); - audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); - SortedVector outputs = getOutputsForDevice(device, mOutputs); + DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); + SortedVector outputs = getOutputsForDevices(devices, mOutputs); if (outputs.size() == 0) { return AUDIO_IO_HANDLE_NONE; @@ -2693,8 +2679,7 @@ status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid, devices[i].mType, devices[i].mAddress, String8()); SortedVector outputs; if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - outputs, - devDesc->address()) != NO_ERROR) { + outputs) != NO_ERROR) { ALOGE("setUidDeviceAffinities() error in checkOutputsForDevice for device=%08x" " addr=%s", devices[i].mType, devices[i].mAddress.string()); return INVALID_OPERATION; @@ -2715,8 +2700,7 @@ status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) { devices[i].mType, devices[i].mAddress, String8()); SortedVector outputs; if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputs, - devDesc->address()) != NO_ERROR) { + outputs) != NO_ERROR) { ALOGE("%s() error in checkOutputsForDevice for device=%08x addr=%s", __FUNCTION__, devices[i].mType, devices[i].mAddress.string()); return INVALID_OPERATION; @@ -2836,7 +2820,7 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI // See if there is a profile to support this. // AUDIO_DEVICE_NONE - sp profile = getProfileForOutput(AUDIO_DEVICE_NONE /*ignore device */, + sp profile = getProfileForOutput(DeviceVector() /*ignore device */, offloadInfo.sample_rate, offloadInfo.format, offloadInfo.channel_mask, @@ -2850,7 +2834,7 @@ bool AudioPolicyManager::isDirectOutputSupported(const audio_config_base_t& conf const audio_attributes_t& attributes) { audio_output_flags_t output_flags = AUDIO_OUTPUT_FLAG_NONE; audio_attributes_flags_to_audio_output_flags(attributes.flags, output_flags); - sp profile = getProfileForOutput(AUDIO_DEVICE_NONE /*ignore device */, + sp profile = getProfileForOutput(DeviceVector() /*ignore device */, config.sample_rate, config.format, config.channel_mask, @@ -3044,8 +3028,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } - if (!outputDesc->mProfile->isCompatibleProfile(devDesc->type(), - devDesc->address(), + if (!outputDesc->mProfile->isCompatibleProfile(DeviceVector(devDesc), patch->sources[0].sample_rate, NULL, // updatedSamplingRate patch->sources[0].format, @@ -3066,7 +3049,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, // TODO: reconfigure output format and channels here ALOGV("createAudioPatch() setting device %08x on output %d", devices.types(), outputDesc->mIoHandle); - setOutputDevice(outputDesc, devices.types(), true, 0, handle); + setOutputDevices(outputDesc, devices, true, 0, handle); index = mAudioPatches.indexOfKey(*handle); if (index >= 0) { if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) { @@ -3095,14 +3078,13 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } } - sp devDesc = + sp device = mAvailableInputDevices.getDeviceFromId(patch->sources[0].id); - if (devDesc == 0) { + if (device == 0) { return BAD_VALUE; } - if (!inputDesc->mProfile->isCompatibleProfile(devDesc->type(), - devDesc->address(), + if (!inputDesc->mProfile->isCompatibleProfile(DeviceVector(device), patch->sinks[0].sample_rate, NULL, /*updatedSampleRate*/ patch->sinks[0].format, @@ -3116,9 +3098,9 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return INVALID_OPERATION; } // TODO: reconfigure output format and channels here - ALOGV("createAudioPatch() setting device %08x on output %d", - devDesc->type(), inputDesc->mIoHandle); - setInputDevice(inputDesc->mIoHandle, devDesc->type(), true, handle); + ALOGV("%s() setting device %s on output %d", __func__, + device->toString().c_str(), inputDesc->mIoHandle); + setInputDevice(inputDesc->mIoHandle, device, true, handle); index = mAudioPatches.indexOfKey(*handle); if (index >= 0) { if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) { @@ -3138,16 +3120,16 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } } - sp srcDeviceDesc = + sp srcDevice = mAvailableInputDevices.getDeviceFromId(patch->sources[0].id); - if (srcDeviceDesc == 0) { + if (srcDevice == 0) { return BAD_VALUE; } //update source and sink with our own data as the data passed in the patch may // be incomplete. struct audio_patch newPatch = *patch; - srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]); + srcDevice->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]); for (size_t i = 0; i < patch->num_sinks; i++) { if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { @@ -3155,26 +3137,26 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return INVALID_OPERATION; } - sp sinkDeviceDesc = + sp sinkDevice = mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id); - if (sinkDeviceDesc == 0) { + if (sinkDevice == 0) { return BAD_VALUE; } - sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]); + sinkDevice->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]); // create a software bridge in PatchPanel if: // - source and sink devices are on different HW modules OR // - audio HAL version is < 3.0 // - audio HAL version is >= 3.0 but no route has been declared between devices - if (!srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) || - (srcDeviceDesc->getModuleVersionMajor() < 3) || - !srcDeviceDesc->getModule()->supportsPatch(srcDeviceDesc, sinkDeviceDesc)) { + if (!srcDevice->hasSameHwModuleAs(sinkDevice) || + (srcDevice->getModuleVersionMajor() < 3) || + !srcDevice->getModule()->supportsPatch(srcDevice, sinkDevice)) { // support only one sink device for now to simplify output selection logic if (patch->num_sinks > 1) { return INVALID_OPERATION; } SortedVector outputs = - getOutputsForDevice(sinkDeviceDesc->type(), mOutputs); + getOutputsForDevices(DeviceVector(sinkDevice), mOutputs); // if the sink device is reachable via an opened output stream, request to go via // this output stream by adding a second source to the patch description audio_io_handle_t output = selectOutput(outputs); @@ -3232,11 +3214,11 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle, return BAD_VALUE; } - setOutputDevice(outputDesc, - getNewOutputDevice(outputDesc, true /*fromCache*/), - true, - 0, - NULL); + setOutputDevices(outputDesc, + getNewOutputDevices(outputDesc, true /*fromCache*/), + true, + 0, + NULL); } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { sp inputDesc = mInputs.getInputFromId(patch->sinks[0].id); @@ -3359,8 +3341,8 @@ void AudioPolicyManager::clearAudioPatches(uid_t uid) void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy, audio_io_handle_t ouptutToSkip) { - audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); - SortedVector outputs = getOutputsForDevice(device, mOutputs); + DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); + SortedVector outputs = getOutputsForDevices(devices, mOutputs); for (size_t j = 0; j < mOutputs.size(); j++) { if (mOutputs.keyAt(j) == ouptutToSkip) { continue; @@ -3379,8 +3361,8 @@ void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy, } } } else { - audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); - setOutputDevice(outputDesc, newDevice, false); + setOutputDevices( + outputDesc, getNewOutputDevices(outputDesc, false /*fromCache*/), false); } } } @@ -3443,7 +3425,7 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session { *session = (audio_session_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION); *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_INPUT); - *device = getDeviceAndMixForInputSource(AUDIO_SOURCE_HOTWORD); + *device = getDeviceAndMixForInputSource(AUDIO_SOURCE_HOTWORD)->type(); return mSoundTriggerSessions.acquireSession(*session, *ioHandle); } @@ -3469,10 +3451,10 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so return INVALID_OPERATION; } - sp srcDeviceDesc = + sp srcDevice = mAvailableInputDevices.getDevice(source->ext.device.type, - String8(source->ext.device.address)); - if (srcDeviceDesc == 0) { + String8(source->ext.device.address)); + if (srcDevice == 0) { ALOGW("%s source->ext.device.type %08x not found", __FUNCTION__, source->ext.device.type); return BAD_VALUE; } @@ -3483,7 +3465,7 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so sp patchDesc = new AudioPatch(&dummyPatch, uid); sp sourceDesc = - new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc, + new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice, streamTypefromAttributesInt(attributes), getStrategyForAttr(attributes)); @@ -3504,18 +3486,20 @@ status_t AudioPolicyManager::connectAudioSource(const sp audio_attributes_t attributes = sourceDesc->attributes(); routing_strategy strategy = getStrategyForAttr(&attributes); audio_stream_type_t stream = sourceDesc->stream(); - sp srcDeviceDesc = sourceDesc->srcDevice(); + sp srcDevice = sourceDesc->srcDevice(); - audio_devices_t sinkDevice = getDeviceForStrategy(strategy, true); - sp sinkDeviceDesc = - mAvailableOutputDevices.getDevice(sinkDevice, String8("")); + DeviceVector sinkDevices = getDevicesForStrategy(strategy, true); + ALOG_ASSERT(!sinkDevices.isEmpty(), "connectAudioSource(): no device found for strategy"); + sp sinkDevice = sinkDevices.itemAt(0); + ALOG_ASSERT(mAvailableOutputDevices.contains(sinkDevice), "%s: Device %s not available", + __FUNCTION__, sinkDevice->toString().c_str()); audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE; - if (srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) && - srcDeviceDesc->getModuleVersionMajor() >= 3 && - sinkDeviceDesc->getModule()->supportsPatch(srcDeviceDesc, sinkDeviceDesc) && - srcDeviceDesc->getAudioPort()->mGains.size() > 0) { + if (srcDevice->hasSameHwModuleAs(sinkDevice) && + srcDevice->getModuleVersionMajor() >= 3 && + sinkDevice->getModule()->supportsPatch(srcDevice, sinkDevice) && + srcDevice->getAudioPort()->mGains.size() > 0) { ALOGV("%s Device to Device route supported by >=3.0 HAL", __FUNCTION__); // TODO: may explicitly specify whether we should use HW or SW patch // create patch between src device and output device @@ -3532,12 +3516,12 @@ status_t AudioPolicyManager::connectAudioSource(const sp getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, &attributes, &stream, sourceDesc->uid(), &config, &flags, &selectedDeviceId); if (output == AUDIO_IO_HANDLE_NONE) { - ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevice); + ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types()); return INVALID_OPERATION; } sp outputDesc = mOutputs.valueFor(output); if (outputDesc->isDuplicated()) { - ALOGV("%s output for device %08x is duplicated", __FUNCTION__, sinkDevice); + ALOGV("%s output for device %08x is duplicated", __FUNCTION__, sinkDevices.types()); return INVALID_OPERATION; } status_t status = outputDesc->start(); @@ -3551,7 +3535,7 @@ status_t AudioPolicyManager::connectAudioSource(const sp // - the sink is defined by whatever output device is currently selected for the output // though which this patch is routed. PatchBuilder patchBuilder; - patchBuilder.addSource(srcDeviceDesc).addSource(outputDesc, { .stream = stream }); + patchBuilder.addSource(srcDevice).addSource(outputDesc, { .stream = stream }); status = mpClientInterface->createAudioPatch(patchBuilder.patch(), &afPatchHandle, 0); @@ -3978,8 +3962,6 @@ status_t AudioPolicyManager::initialize() { // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices // open all output streams needed to access attached devices - audio_devices_t outputDeviceTypes = mAvailableOutputDevices.types(); - audio_devices_t inputDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; for (const auto& hwModule : mHwModulesAll) { hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName())); if (hwModule->getHandle() == AUDIO_MODULE_HANDLE_NONE) { @@ -4008,51 +3990,49 @@ status_t AudioPolicyManager::initialize() { if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) { continue; } - audio_devices_t profileType = outProfile->getSupportedDevicesType(); - if ((profileType & mDefaultOutputDevice->type()) != AUDIO_DEVICE_NONE) { - profileType = mDefaultOutputDevice->type(); + const DeviceVector &supportedDevices = outProfile->getSupportedDevices(); + DeviceVector availProfileDevices = supportedDevices.filter(mAvailableOutputDevices); + sp supportedDevice = 0; + if (supportedDevices.contains(mDefaultOutputDevice)) { + supportedDevice = mDefaultOutputDevice; } else { - // chose first device present in profile's SupportedDevices also part of - // outputDeviceTypes - profileType = outProfile->getSupportedDeviceForType(outputDeviceTypes); + // choose first device present in profile's SupportedDevices also part of + // mAvailableOutputDevices. + if (availProfileDevices.isEmpty()) { + continue; + } + supportedDevice = availProfileDevices.itemAt(0); } - if ((profileType & outputDeviceTypes) == 0) { + if (!mAvailableOutputDevices.contains(supportedDevice)) { continue; } sp outputDesc = new SwAudioOutputDescriptor(outProfile, mpClientInterface); - const DeviceVector &supportedDevices = outProfile->getSupportedDevices(); - const DeviceVector &devicesForType = supportedDevices.getDevicesFromTypeMask( - profileType); - String8 address = getFirstDeviceAddress(devicesForType); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; - status_t status = outputDesc->open(nullptr, profileType, address, - AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output); - + status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice), + AUDIO_STREAM_DEFAULT, + AUDIO_OUTPUT_FLAG_NONE, &output); if (status != NO_ERROR) { - ALOGW("Cannot open output stream for device %08x on hw module %s", - outputDesc->mDevice, - hwModule->getName()); - } else { - for (const auto& dev : supportedDevices) { - ssize_t index = mAvailableOutputDevices.indexOf(dev); - // give a valid ID to an attached device once confirmed it is reachable - if (index >= 0 && !mAvailableOutputDevices[index]->isAttached()) { - mAvailableOutputDevices[index]->attach(hwModule); - } - } - if (mPrimaryOutput == 0 && - outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) { - mPrimaryOutput = outputDesc; + ALOGW("Cannot open output stream for devices %s on hw module %s", + supportedDevice->toString().c_str(), hwModule->getName()); + continue; + } + for (const auto &device : availProfileDevices) { + // give a valid ID to an attached device once confirmed it is reachable + if (!device->isAttached()) { + device->attach(hwModule); } - addOutput(output, outputDesc); - setOutputDevice(outputDesc, - profileType, - true, - 0, - NULL, - address); } + if (mPrimaryOutput == 0 && + outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) { + mPrimaryOutput = outputDesc; + } + addOutput(output, outputDesc); + setOutputDevices(outputDesc, + DeviceVector(supportedDevice), + true, + 0, + NULL); } // open input streams needed to access attached devices to validate // mAvailableInputDevices list @@ -4067,75 +4047,57 @@ status_t AudioPolicyManager::initialize() { continue; } // chose first device present in profile's SupportedDevices also part of - // inputDeviceTypes - audio_devices_t profileType = inProfile->getSupportedDeviceForType(inputDeviceTypes); - - if ((profileType & inputDeviceTypes) == 0) { + // available input devices + const DeviceVector &supportedDevices = inProfile->getSupportedDevices(); + DeviceVector availProfileDevices = supportedDevices.filter(mAvailableInputDevices); + if (availProfileDevices.isEmpty()) { + ALOGE("%s: Input device list is empty!", __FUNCTION__); continue; } sp inputDesc = new AudioInputDescriptor(inProfile, mpClientInterface); - DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(profileType); - // the inputs vector must be of size >= 1, but we don't want to crash here - String8 address = getFirstDeviceAddress(inputDevices); - ALOGV(" for input device 0x%x using address %s", profileType, address.string()); - ALOGE_IF(inputDevices.size() == 0, "Input device list is empty!"); - audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; status_t status = inputDesc->open(nullptr, - profileType, - address, + availProfileDevices.itemAt(0), AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE, &input); - - if (status == NO_ERROR) { - for (const auto& dev : inProfile->getSupportedDevices()) { - ssize_t index = mAvailableInputDevices.indexOf(dev); - // give a valid ID to an attached device once confirmed it is reachable - if (index >= 0) { - sp devDesc = mAvailableInputDevices[index]; - if (!devDesc->isAttached()) { - devDesc->attach(hwModule); - devDesc->importAudioPort(inProfile, true); - } - } - } - inputDesc->close(); - } else { - ALOGW("Cannot open input stream for device %08x on hw module %s", - profileType, + if (status != NO_ERROR) { + ALOGW("Cannot open input stream for device %s on hw module %s", + availProfileDevices.toString().c_str(), hwModule->getName()); + continue; } + for (const auto &device : availProfileDevices) { + // give a valid ID to an attached device once confirmed it is reachable + if (!device->isAttached()) { + device->attach(hwModule); + device->importAudioPort(inProfile, true); + } + } + inputDesc->close(); } } // make sure all attached devices have been allocated a unique ID - for (size_t i = 0; i < mAvailableOutputDevices.size();) { - if (!mAvailableOutputDevices[i]->isAttached()) { - ALOGW("Output device %08x unreachable", mAvailableOutputDevices[i]->type()); - mAvailableOutputDevices.remove(mAvailableOutputDevices[i]); - continue; - } - // The device is now validated and can be appended to the available devices of the engine - mEngine->setDeviceConnectionState(mAvailableOutputDevices[i], - AUDIO_POLICY_DEVICE_STATE_AVAILABLE); - i++; - } - for (size_t i = 0; i < mAvailableInputDevices.size();) { - if (!mAvailableInputDevices[i]->isAttached()) { - ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->type()); - mAvailableInputDevices.remove(mAvailableInputDevices[i]); - continue; + auto checkAndSetAvailable = [this](auto& devices) { + for (const auto &device : devices) { + if (!device->isAttached()) { + ALOGW("device %s is unreachable", device->toString().c_str()); + devices.remove(device); + continue; + } + // Device is now validated and can be appended to the available devices of the engine + mEngine->setDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE); } - // The device is now validated and can be appended to the available devices of the engine - mEngine->setDeviceConnectionState(mAvailableInputDevices[i], - AUDIO_POLICY_DEVICE_STATE_AVAILABLE); - i++; - } + }; + checkAndSetAvailable(mAvailableOutputDevices); + checkAndSetAvailable(mAvailableInputDevices); + // make sure default device is reachable - if (mDefaultOutputDevice == 0 || mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) { - ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->type()); + if (mDefaultOutputDevice == 0 || !mAvailableOutputDevices.contains(mDefaultOutputDevice)) { + ALOGE_IF(mDefaultOutputDevice != 0, "Default device %s is unreachable", + mDefaultOutputDevice->toString().c_str()); status = NO_INIT; } // If microphones address is empty, set it according to device type @@ -4208,44 +4170,27 @@ void AudioPolicyManager::addInput(audio_io_handle_t input, nextAudioPortGeneration(); } -void AudioPolicyManager::findIoHandlesByAddress(const sp& desc /*in*/, - const audio_devices_t device /*in*/, - const String8& address /*in*/, - SortedVector& outputs /*out*/) { - sp devDesc = - desc->mProfile->getSupportedDeviceByAddress(device, address); - if (devDesc != 0) { - ALOGV("findIoHandlesByAddress(): adding opened output %d on same address %s", - desc->mIoHandle, address.string()); - outputs.add(desc->mIoHandle); - } -} - -status_t AudioPolicyManager::checkOutputsForDevice(const sp& devDesc, +status_t AudioPolicyManager::checkOutputsForDevice(const sp& device, audio_policy_dev_state_t state, - SortedVector& outputs, - const String8& address) + SortedVector& outputs) { - audio_devices_t device = devDesc->type(); + audio_devices_t deviceType = device->type(); + const String8 &address = device->address(); sp desc; - if (audio_device_is_digital(device)) { + if (audio_device_is_digital(deviceType)) { // erase all current sample rates, formats and channel masks - devDesc->clearAudioProfiles(); + device->clearAudioProfiles(); } if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { // first list already open outputs that can be routed to this device for (size_t i = 0; i < mOutputs.size(); i++) { desc = mOutputs.valueAt(i); - if (!desc->isDuplicated() && (desc->supportedDevices() & device)) { - if (!device_distinguishes_on_address(device)) { - ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i)); - outputs.add(mOutputs.keyAt(i)); - } else { - ALOGV(" checking address match due to device 0x%x", device); - findIoHandlesByAddress(desc, device, address, outputs); - } + if (!desc->isDuplicated() && desc->supportsDevice(device)) { + ALOGV("checkOutputsForDevice(): adding opened output %d on device %s", + mOutputs.keyAt(i), device->toString().c_str()); + outputs.add(mOutputs.keyAt(i)); } } // then look for output profiles that can be routed to this device @@ -4253,13 +4198,10 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d for (const auto& hwModule : mHwModules) { for (size_t j = 0; j < hwModule->getOutputProfiles().size(); j++) { sp profile = hwModule->getOutputProfiles()[j]; - if (profile->supportDevice(device)) { - if (!device_distinguishes_on_address(device) || - profile->supportDeviceAddress(address)) { - profiles.add(profile); - ALOGV("checkOutputsForDevice(): adding profile %zu from module %s", - j, hwModule->getName()); - } + if (profile->supportsDevice(device)) { + profiles.add(profile); + ALOGV("checkOutputsForDevice(): adding profile %zu from module %s", + j, hwModule->getName()); } } } @@ -4267,7 +4209,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d ALOGV(" found %zu profiles, %zu outputs", profiles.size(), outputs.size()); if (profiles.isEmpty() && outputs.isEmpty()) { - ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + ALOGW("checkOutputsForDevice(): No output available for device %04x", deviceType); return BAD_VALUE; } @@ -4283,8 +4225,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d if (!desc->isDuplicated() && desc->mProfile == profile) { // matching profile: save the sample rates, format and channel masks supported // by the profile in our device descriptor - if (audio_device_is_digital(device)) { - devDesc->importAudioPort(profile); + if (audio_device_is_digital(deviceType)) { + device->importAudioPort(profile); } break; } @@ -4300,20 +4242,20 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d } ALOGV("opening output for device %08x with params %s profile %p name %s", - device, address.string(), profile.get(), profile->getName().string()); + deviceType, address.string(), profile.get(), profile->getName().string()); desc = new SwAudioOutputDescriptor(profile, mpClientInterface); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; - status_t status = desc->open(nullptr, device, address, + status_t status = desc->open(nullptr, DeviceVector(device), AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output); if (status == NO_ERROR) { // Here is where the out_set_parameters() for card & device gets called if (!address.isEmpty()) { - char *param = audio_device_address_to_parameter(device, address); + char *param = audio_device_address_to_parameter(deviceType, address); mpClientInterface->setParameters(output, String8(param)); free(param); } - updateAudioProfiles(devDesc, output, profile->getAudioProfiles()); + updateAudioProfiles(device, output, profile->getAudioProfiles()); if (!profile->hasValidAudioProfile()) { ALOGW("checkOutputsForDevice() missing param"); desc->close(); @@ -4328,7 +4270,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d config.offload_info.channel_mask = config.channel_mask; config.offload_info.format = config.format; - status_t status = desc->open(&config, device, address, AUDIO_STREAM_DEFAULT, + status_t status = desc->open(&config, DeviceVector(device), + AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output); if (status != NO_ERROR) { output = AUDIO_IO_HANDLE_NONE; @@ -4337,7 +4280,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d if (output != AUDIO_IO_HANDLE_NONE) { addOutput(output, desc); - if (device_distinguishes_on_address(device) && address != "0") { + if (device_distinguishes_on_address(deviceType) && address != "0") { sp policyMix; if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) { ALOGE("checkOutputsForDevice() cannot find policy for address %s", @@ -4376,28 +4319,28 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d output = AUDIO_IO_HANDLE_NONE; } if (output == AUDIO_IO_HANDLE_NONE) { - ALOGW("checkOutputsForDevice() could not open output for device %x", device); + ALOGW("checkOutputsForDevice() could not open output for device %x", deviceType); profiles.removeAt(profile_index); profile_index--; } else { outputs.add(output); // Load digital format info only for digital devices - if (audio_device_is_digital(device)) { - devDesc->importAudioPort(profile); + if (audio_device_is_digital(deviceType)) { + device->importAudioPort(profile); } - if (device_distinguishes_on_address(device)) { - ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)", - device, address.string()); - setOutputDevice(desc, device, true/*force*/, 0/*delay*/, - NULL/*patch handle*/, address.string()); + if (device_distinguishes_on_address(deviceType)) { + ALOGV("checkOutputsForDevice(): setOutputDevices %s", + device->toString().c_str()); + setOutputDevices(desc, DeviceVector(device), true/*force*/, 0/*delay*/, + NULL/*patch handle*/); } ALOGV("checkOutputsForDevice(): adding output %d", output); } } if (profiles.isEmpty()) { - ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + ALOGW("checkOutputsForDevice(): No output available for device %04x", deviceType); return BAD_VALUE; } } else { // Disconnect @@ -4406,10 +4349,10 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d desc = mOutputs.valueAt(i); if (!desc->isDuplicated()) { // exact match on device - if (device_distinguishes_on_address(device) && - (desc->supportedDevices() == device)) { - findIoHandlesByAddress(desc, device, address, outputs); - } else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) { + if (device_distinguishes_on_address(deviceType) && + (desc->supportedDevices().types() == deviceType)) { + outputs.add(mOutputs.keyAt(i)); + } else if (!(desc->supportedDevices().types() & mAvailableOutputDevices.types())) { ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i)); outputs.add(mOutputs.keyAt(i)); @@ -4420,7 +4363,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d for (const auto& hwModule : mHwModules) { for (size_t j = 0; j < hwModule->getOutputProfiles().size(); j++) { sp profile = hwModule->getOutputProfiles()[j]; - if (profile->supportDevice(device)) { + if (profile->supportsDevice(device)) { ALOGV("checkOutputsForDevice(): " "clearing direct output profile %zu on module %s", j, hwModule->getName()); @@ -4432,24 +4375,22 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d return NO_ERROR; } -status_t AudioPolicyManager::checkInputsForDevice(const sp& devDesc, +status_t AudioPolicyManager::checkInputsForDevice(const sp& device, audio_policy_dev_state_t state, - SortedVector& inputs, - const String8& address) + SortedVector& inputs) { - audio_devices_t device = devDesc->type(); sp desc; - if (audio_device_is_digital(device)) { + if (audio_device_is_digital(device->type())) { // erase all current sample rates, formats and channel masks - devDesc->clearAudioProfiles(); + device->clearAudioProfiles(); } if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { // first list already open inputs that can be routed to this device for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { desc = mInputs.valueAt(input_index); - if (desc->mProfile->supportDevice(device)) { + if (desc->mProfile->supportsDeviceTypes(device->type())) { ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index)); inputs.add(mInputs.keyAt(input_index)); } @@ -4463,19 +4404,16 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de profile_index++) { sp profile = hwModule->getInputProfiles()[profile_index]; - if (profile->supportDevice(device)) { - if (!device_distinguishes_on_address(device) || - profile->supportDeviceAddress(address)) { - profiles.add(profile); - ALOGV("checkInputsForDevice(): adding profile %zu from module %s", - profile_index, hwModule->getName()); - } + if (profile->supportsDevice(device)) { + profiles.add(profile); + ALOGV("checkInputsForDevice(): adding profile %zu from module %s", + profile_index, hwModule->getName()); } } } if (profiles.isEmpty() && inputs.isEmpty()) { - ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + ALOGW("%s: No input available for device %s", __func__, device->toString().c_str()); return BAD_VALUE; } @@ -4490,8 +4428,8 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de for (input_index = 0; input_index < mInputs.size(); input_index++) { desc = mInputs.valueAt(input_index); if (desc->mProfile == profile) { - if (audio_device_is_digital(device)) { - devDesc->importAudioPort(profile); + if (audio_device_is_digital(device->type())) { + device->importAudioPort(profile); } break; } @@ -4510,18 +4448,18 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; status_t status = desc->open(nullptr, device, - address, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE, &input); if (status == NO_ERROR) { + const String8& address = device->address(); if (!address.isEmpty()) { - char *param = audio_device_address_to_parameter(device, address); + char *param = audio_device_address_to_parameter(device->type(), address); mpClientInterface->setParameters(input, String8(param)); free(param); } - updateAudioProfiles(devDesc, input, profile->getAudioProfiles()); + updateAudioProfiles(device, input, profile->getAudioProfiles()); if (!profile->hasValidAudioProfile()) { ALOGW("checkInputsForDevice() direct input missing param"); desc->close(); @@ -4534,20 +4472,21 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de } // endif input != 0 if (input == AUDIO_IO_HANDLE_NONE) { - ALOGW("checkInputsForDevice() could not open input for device 0x%X", device); + ALOGW("%s could not open input for device %s", __func__, + device->toString().c_str()); profiles.removeAt(profile_index); profile_index--; } else { inputs.add(input); - if (audio_device_is_digital(device)) { - devDesc->importAudioPort(profile); + if (audio_device_is_digital(device->type())) { + device->importAudioPort(profile); } ALOGV("checkInputsForDevice(): adding input %d", input); } } // end scan profiles if (profiles.isEmpty()) { - ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + ALOGW("%s: No input available for device %s", __func__, device->toString().c_str()); return BAD_VALUE; } } else { @@ -4555,7 +4494,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de // check if one opened input is not needed any more after disconnecting one device for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { desc = mInputs.valueAt(input_index); - if (!(desc->mProfile->supportDevice(mAvailableInputDevices.types()))) { + if (!(desc->mProfile->supportsDeviceTypes(mAvailableInputDevices.types()))) { ALOGV("checkInputsForDevice(): disconnecting adding input %d", mInputs.keyAt(input_index)); inputs.add(mInputs.keyAt(input_index)); @@ -4567,7 +4506,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de profile_index < hwModule->getInputProfiles().size(); profile_index++) { sp profile = hwModule->getInputProfiles()[profile_index]; - if (profile->supportDevice(device)) { + if (profile->supportsDeviceTypes(device->type())) { ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %s", profile_index, hwModule->getName()); profile->clearAudioProfiles(); @@ -4641,7 +4580,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) // MSD patches may have been released to support a non-MSD direct output. Reset MSD patch if // no direct outputs are open. - if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) { + if (!getMsdAudioOutDevices().isEmpty()) { bool directOutputOpen = false; for (size_t i = 0; i < mOutputs.size(); i++) { if (mOutputs[i]->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { @@ -4668,7 +4607,7 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) nextAudioPortGeneration(); - audio_devices_t device = inputDesc->mDevice; + sp device = inputDesc->getDevice(); ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); @@ -4680,26 +4619,27 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) inputDesc->close(); mInputs.removeItem(input); - audio_devices_t primaryInputDevices = availablePrimaryInputDevices(); - if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + DeviceVector primaryInputDevices = availablePrimaryModuleInputDevices(); + if (primaryInputDevices.contains(device) && mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { SoundTrigger::setCaptureState(false); } } -SortedVector AudioPolicyManager::getOutputsForDevice( - audio_devices_t device, - const SwAudioOutputCollection& openOutputs) +SortedVector AudioPolicyManager::getOutputsForDevices( + const DeviceVector &devices, + const SwAudioOutputCollection& openOutputs) { SortedVector outputs; - ALOGVV("getOutputsForDevice() device %04x", device); + ALOGVV("%s() devices %s", __func__, devices.toString().c_str()); for (size_t i = 0; i < openOutputs.size(); i++) { - ALOGVV("output %zu isDuplicated=%d device=%04x", + ALOGVV("output %zu isDuplicated=%d device=%s", i, openOutputs.valueAt(i)->isDuplicated(), - openOutputs.valueAt(i)->supportedDevices()); - if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) { - ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i)); + openOutputs.valueAt(i)->supportedDevices().toString().c_str()); + if ((devices.types() & openOutputs.valueAt(i)->supportedDevices().types()) == + devices.types()) { + ALOGVV("%s() found output %d", __func__, openOutputs.keyAt(i)); outputs.add(openOutputs.keyAt(i)); } } @@ -4721,10 +4661,10 @@ void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function on void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) { - audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); - audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/); - SortedVector srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); - SortedVector dstOutputs = getOutputsForDevice(newDevice, mOutputs); + DeviceVector oldDevices = getDevicesForStrategy(strategy, true /*fromCache*/); + DeviceVector newDevices = getDevicesForStrategy(strategy, false /*fromCache*/); + SortedVector srcOutputs = getOutputsForDevices(oldDevices, mPreviousOutputs); + SortedVector dstOutputs = getOutputsForDevices(newDevices, mOutputs); // also take into account external policy-related changes: add all outputs which are // associated with policies in the "before" and "after" output vectors @@ -4754,14 +4694,16 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) maxLatency = desc->latency(); } } - ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d", - strategy, srcOutputs[0], dstOutputs[0]); + ALOGV("%s: strategy %d, moving from output %s to output %s", __func__, strategy, + (srcOutputs.isEmpty()? "none" : std::to_string(srcOutputs[0]).c_str()), + (dstOutputs.isEmpty()? "none" : std::to_string(dstOutputs[0]).c_str())); // mute strategy while moving tracks from one output to another for (audio_io_handle_t srcOut : srcOutputs) { sp desc = mPreviousOutputs.valueFor(srcOut); if (desc != 0 && isStrategyActive(desc, strategy)) { setStrategyMute(strategy, true, desc); - setStrategyMute(strategy, false, desc, maxLatency * LATENCY_MUTE_FACTOR, newDevice); + setStrategyMute(strategy, false, desc, maxLatency * LATENCY_MUTE_FACTOR, + newDevices.types()); } sp source = getSourceForStrategyOnOutput(srcOut, strategy); @@ -4880,26 +4822,28 @@ sp AudioPolicyManager::findPreferredDevice( return device; } -audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp& outputDesc, - bool fromCache) +DeviceVector AudioPolicyManager::getNewOutputDevices(const sp& outputDesc, + bool fromCache) { + DeviceVector devices; + ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); if (patchDesc->mUid != mUidCached) { - ALOGV("getNewOutputDevice() device %08x forced by patch %d", - outputDesc->device(), outputDesc->getPatchHandle()); - return outputDesc->device(); + ALOGV("%s device %s forced by patch %d", __func__, + outputDesc->devices().toString().c_str(), outputDesc->getPatchHandle()); + return outputDesc->devices(); } } // Honor explicit routing requests only if no client using default routing is active on this // input: a specific app can not force routing for other apps by setting a preferred device. bool active; // unused - sp deviceDesc = + sp device = findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices); - if (deviceDesc != nullptr) { - return deviceDesc->type(); + if (device != nullptr) { + return DeviceVector(device); } // check the following by order of priority to request a routing change if necessary: @@ -4925,56 +4869,55 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(const spgetForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); + devices = getDevicesForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); } else if (isInCall() || isStrategyActiveOnSameModule(outputDesc, STRATEGY_PHONE)) { - device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); + devices = getDevicesForStrategy(STRATEGY_PHONE, fromCache); } else if (isStrategyActiveOnSameModule(outputDesc, STRATEGY_SONIFICATION)) { - device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); + devices = getDevicesForStrategy(STRATEGY_SONIFICATION, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) { - device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); + devices = getDevicesForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) { - device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache); + devices = getDevicesForStrategy(STRATEGY_ACCESSIBILITY, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) { - device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); + devices = getDevicesForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_MEDIA)) { - device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); + devices = getDevicesForStrategy(STRATEGY_MEDIA, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_DTMF)) { - device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + devices = getDevicesForStrategy(STRATEGY_DTMF, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { - device = getDeviceForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); + devices = getDevicesForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); } else if (isStrategyActive(outputDesc, STRATEGY_REROUTING)) { - device = getDeviceForStrategy(STRATEGY_REROUTING, fromCache); + devices = getDevicesForStrategy(STRATEGY_REROUTING, fromCache); } - ALOGV("getNewOutputDevice() selected device %x", device); - return device; + ALOGV("getNewOutputDevice() selected devices %s", devices.toString().c_str()); + return devices; } -audio_devices_t AudioPolicyManager::getNewInputDevice(const sp& inputDesc) +sp AudioPolicyManager::getNewInputDevice( + const sp& inputDesc) { - audio_devices_t device = AUDIO_DEVICE_NONE; + sp device; ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); if (patchDesc->mUid != mUidCached) { - ALOGV("getNewInputDevice() device %08x forced by patch %d", - inputDesc->mDevice, inputDesc->getPatchHandle()); - return inputDesc->mDevice; + ALOGV("getNewInputDevice() device %s forced by patch %d", + inputDesc->getDevice()->toString().c_str(), inputDesc->getPatchHandle()); + return inputDesc->getDevice(); } } // Honor explicit routing requests only if no client using default routing is active on this // input: a specific app can not force routing for other apps by setting a preferred device. bool active; - sp deviceDesc = - findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices); - if (deviceDesc != nullptr) { - return deviceDesc->type(); + device = findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices); + if (device != nullptr) { + return device; } // If we are not in call and no client is active on this input, this methods returns @@ -5006,36 +4949,37 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } - audio_devices_t activeDevices = AUDIO_DEVICE_NONE; - audio_devices_t devices = AUDIO_DEVICE_NONE; + DeviceVector activeDevices; + DeviceVector devices; for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); - audio_devices_t curDevices = - getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/); - devices |= curDevices; - for (audio_io_handle_t output : getOutputsForDevice(curDevices, mOutputs)) { + DeviceVector curDevices = + getDevicesForStrategy((routing_strategy)curStrategy, false /*fromCache*/); + devices.merge(curDevices); + for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) { sp outputDesc = mOutputs.valueFor(output); if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) { - activeDevices |= outputDesc->device(); + activeDevices.merge(outputDesc->devices()); } } } // Favor devices selected on active streams if any to report correct device in case of // explicit device selection - if (activeDevices != AUDIO_DEVICE_NONE) { + if (!activeDevices.isEmpty()) { devices = activeDevices; } /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it and doesn't really need to.*/ - if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { - devices |= AUDIO_DEVICE_OUT_SPEAKER; - devices &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE; + DeviceVector speakerSafeDevices = devices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER_SAFE); + if (!speakerSafeDevices.isEmpty()) { + devices.merge(mAvailableOutputDevices.getDevicesFromTypeMask(AUDIO_DEVICE_OUT_SPEAKER)); + devices.remove(speakerSafeDevices); } - return devices; + return devices.types(); } routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) const @@ -5126,34 +5070,33 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) { return 0; } -audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, - bool fromCache) +DeviceVector AudioPolicyManager::getDevicesForStrategy(routing_strategy strategy, bool fromCache) { // Honor explicit routing requests only if all active clients have a preferred route in which // case the last active client route is used - sp deviceDesc = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices); - if (deviceDesc != nullptr) { - return deviceDesc->type(); + sp device = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices); + if (device != nullptr) { + return DeviceVector(device); } if (fromCache) { - ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", - strategy, mDeviceForStrategy[strategy]); - return mDeviceForStrategy[strategy]; + ALOGVV("%s from cache strategy %d, device %s", __func__, strategy, + mDevicesForStrategy[strategy].toString().c_str()); + return mDevicesForStrategy[strategy]; } - return mEngine->getDeviceForStrategy(strategy); + return mAvailableOutputDevices.getDevicesFromTypeMask(mEngine->getDeviceForStrategy(strategy)); } void AudioPolicyManager::updateDevicesAndOutputs() { for (int i = 0; i < NUM_STRATEGIES; i++) { - mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + mDevicesForStrategy[i] = getDevicesForStrategy((routing_strategy)i, false /*fromCache*/); } mPreviousOutputs = mOutputs; } uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const sp& outputDesc, - audio_devices_t prevDevice, + audio_devices_t prevDeviceType, uint32_t delayMs) { // mute/unmute strategies using an incompatible device combination @@ -5164,13 +5107,14 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spdevice(); - bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2); + audio_devices_t deviceType = outputDesc->devices().types(); + bool shouldMute = outputDesc->isActive() && (popcount(deviceType) >= 2); for (size_t i = 0; i < NUM_STRATEGIES; i++) { - audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); - curDevice = curDevice & outputDesc->supportedDevices(); - bool mute = shouldMute && (curDevice & device) && (curDevice != device); + audio_devices_t curDeviceType = + getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + curDeviceType = curDeviceType & outputDesc->supportedDevices().types(); + bool mute = shouldMute && (curDeviceType & deviceType) && (curDeviceType != deviceType); bool doMute = false; if (mute && !outputDesc->mStrategyMutedByDevice[i]) { @@ -5184,12 +5128,11 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const sp desc = mOutputs.valueAt(j); // skip output if it does not share any device with current output - if ((desc->supportedDevices() & outputDesc->supportedDevices()) - == AUDIO_DEVICE_NONE) { + if (!desc->supportedDevices().containsAtLeastOne(outputDesc->supportedDevices())) { continue; } ALOGVV("checkDeviceMuteStrategies() %s strategy %zu (curDevice %04x)", - mute ? "muting" : "unmuting", i, curDevice); + mute ? "muting" : "unmuting", i, curDeviceType); setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs); if (isStrategyActive(desc, (routing_strategy)i)) { if (mute) { @@ -5209,7 +5152,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spisActive() && (device != prevDevice)) { + if (outputDesc->isActive() && (deviceType != prevDeviceType)) { uint32_t tempMuteWaitMs = outputDesc->latency() * 2; // temporary mute duration is conservatively set to 4 times the reported latency uint32_t tempMuteDurationMs = outputDesc->latency() * 4; @@ -5223,7 +5166,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const sp& outputDesc, - audio_devices_t device, - bool force, - int delayMs, - audio_patch_handle_t *patchHandle, - const char *address, - bool requiresMuteCheck) +uint32_t AudioPolicyManager::setOutputDevices(const sp& outputDesc, + const DeviceVector &devices, + bool force, + int delayMs, + audio_patch_handle_t *patchHandle, + bool requiresMuteCheck) { - ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs); - AudioParameter param; + ALOGV("%s device %s delayMs %d", __func__, devices.toString().c_str(), delayMs); uint32_t muteWaitMs; if (outputDesc->isDuplicated()) { - muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs, - nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck); - muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs, - nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck); + muteWaitMs = setOutputDevices(outputDesc->subOutput1(), devices, force, delayMs, + nullptr /* patchHandle */, requiresMuteCheck); + muteWaitMs += setOutputDevices(outputDesc->subOutput2(), devices, force, delayMs, + nullptr /* patchHandle */, requiresMuteCheck); return muteWaitMs; } + + // filter devices according to output selected + DeviceVector filteredDevices = devices.filter(outputDesc->supportedDevices().types()); + // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current // output profile - if ((device != AUDIO_DEVICE_NONE) && - ((device & outputDesc->supportedDevices()) == AUDIO_DEVICE_NONE)) { + if (!devices.isEmpty() && filteredDevices.isEmpty()) { + ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str()); return 0; } - // filter devices according to output selected - device = (audio_devices_t)(device & outputDesc->supportedDevices()); - - audio_devices_t prevDevice = outputDesc->mDevice; + DeviceVector prevDevices = outputDesc->devices(); - ALOGV("setOutputDevice() prevDevice 0x%04x", prevDevice); + ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str()); - if (device != AUDIO_DEVICE_NONE) { - outputDesc->mDevice = device; + if (!filteredDevices.isEmpty()) { + outputDesc->setDevices(filteredDevices); } // if the outputs are not materially active, there is no need to mute. if (requiresMuteCheck) { - muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs); + muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevices.types(), delayMs); } else { ALOGV("%s: suppressing checkDeviceMuteStrategies", __func__); muteWaitMs = 0; @@ -5287,42 +5229,32 @@ uint32_t AudioPolicyManager::setOutputDevice(const sp& ou // OR the requested device is the same as current device // AND force is not specified // AND the output is connected by a valid audio patch. - // Doing this check here allows the caller to call setOutputDevice() without conditions - if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && - !force && - outputDesc->getPatchHandle() != 0) { - ALOGV("setOutputDevice() setting same device 0x%04x or null device", device); + // Doing this check here allows the caller to call setOutputDevices() without conditions + if ((!filteredDevices.isEmpty() || filteredDevices == prevDevices) && + !force && outputDesc->getPatchHandle() != 0) { + ALOGV("%s setting same device %s or null device, force=%d, patch handle=%d", __func__, + filteredDevices.toString().c_str(), force, outputDesc->getPatchHandle()); return muteWaitMs; } - ALOGV("setOutputDevice() changing device"); + ALOGV("%s changing device to %s", __func__, filteredDevices.toString().c_str()); // do the routing - if (device == AUDIO_DEVICE_NONE) { + if (filteredDevices.isEmpty()) { resetOutputDevice(outputDesc, delayMs, NULL); } else { - DeviceVector deviceList; - if ((address == NULL) || (strlen(address) == 0)) { - deviceList = mAvailableOutputDevices.getDevicesFromTypeMask(device); - } else { - sp deviceDesc = mAvailableOutputDevices.getDevice( - device, String8(address)); - if (deviceDesc) deviceList.add(deviceDesc); + PatchBuilder patchBuilder; + patchBuilder.addSource(outputDesc); + ALOG_ASSERT(filteredDevices.size() <= AUDIO_PATCH_PORTS_MAX, "Too many sink ports"); + for (const auto &filteredDevice : filteredDevices) { + patchBuilder.addSink(filteredDevice); } - if (!deviceList.isEmpty()) { - PatchBuilder patchBuilder; - patchBuilder.addSource(outputDesc); - ALOG_ASSERT(deviceList.size() <= AUDIO_PATCH_PORTS_MAX, "Too many sink ports"); - for (const auto &device : deviceList) { - patchBuilder.addSink(device); - } - installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); - } + installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs); } // update stream volumes according to new device - applyStreamVolumes(outputDesc, device, delayMs); + applyStreamVolumes(outputDesc, filteredDevices.types(), delayMs); return muteWaitMs; } @@ -5351,18 +5283,17 @@ status_t AudioPolicyManager::resetOutputDevice(const sp& } status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, - audio_devices_t device, + const sp &device, bool force, audio_patch_handle_t *patchHandle) { status_t status = NO_ERROR; sp inputDesc = mInputs.valueFor(input); - if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) { - inputDesc->mDevice = device; + if ((device != nullptr) && ((device != inputDesc->getDevice()) || force)) { + inputDesc->setDevice(device); - DeviceVector deviceList = mAvailableInputDevices.getDevicesFromTypeMask(device); - if (!deviceList.isEmpty()) { + if (mAvailableInputDevices.contains(device)) { PatchBuilder patchBuilder; patchBuilder.addSink(inputDesc, // AUDIO_SOURCE_HOTWORD is for internal use only: @@ -5374,7 +5305,7 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, } return result; }). //only one input device for now - addSource(deviceList.itemAt(0)); + addSource(device); status = installPatch(__func__, patchHandle, inputDesc.get(), patchBuilder.patch(), 0); } } @@ -5404,8 +5335,7 @@ status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input, return status; } -sp AudioPolicyManager::getInputProfile(audio_devices_t device, - const String8& address, +sp AudioPolicyManager::getInputProfile(const sp &device, uint32_t& samplingRate, audio_format_t& format, audio_channel_mask_t& channelMask, @@ -5425,7 +5355,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, for (const auto& profile : hwModule->getInputProfiles()) { // profile->log(); //updatedFormat = format; - if (profile->isCompatibleProfile(device, address, samplingRate, + if (profile->isCompatibleProfile(DeviceVector(device), samplingRate, &samplingRate /*updatedSamplingRate*/, format, &format, /*updatedFormat*/ @@ -5436,7 +5366,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, true /*exactMatchRequiredForInputFlags*/)) { return profile; } - if (firstInexact == nullptr && profile->isCompatibleProfile(device, address, + if (firstInexact == nullptr && profile->isCompatibleProfile(DeviceVector(device), samplingRate, &updatedSamplingRate, format, @@ -5460,32 +5390,28 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, return NULL; } - -audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) +sp AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, + AudioMix **policyMix) { // Honor explicit routing requests only if all active clients have a preferred route in which // case the last active client route is used - sp deviceDesc = + sp device = findPreferredDevice(mInputs, inputSource, mAvailableInputDevices); - if (deviceDesc != nullptr) { - return deviceDesc->type(); + if (device != nullptr) { + return device; } - - audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; - audio_devices_t selectedDeviceFromMix = - mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix); - - if (selectedDeviceFromMix != AUDIO_DEVICE_NONE) { - return selectedDeviceFromMix; - } - return getDeviceForInputSource(inputSource); + sp selectedDeviceFromMix = + mPolicyMixes.getDeviceAndMixForInputSource(inputSource, mAvailableInputDevices, + policyMix); + return (selectedDeviceFromMix != nullptr) ? + selectedDeviceFromMix : getDeviceForInputSource(inputSource); } -audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) +sp AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) { - return mEngine->getDeviceForInputSource(inputSource); + audio_devices_t device = mEngine->getDeviceForInputSource(inputSource); + return mAvailableInputDevices.getDevice(device); } float AudioPolicyManager::computeVolume(audio_stream_type_t stream, @@ -5630,7 +5556,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, } if (device == AUDIO_DEVICE_NONE) { - device = outputDesc->device(); + device = outputDesc->devices().types(); } float volumeDb = computeVolume(stream, index, device); @@ -5701,7 +5627,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, audio_devices_t device) { if (device == AUDIO_DEVICE_NONE) { - device = outputDesc->device(); + device = outputDesc->devices().types(); } ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", @@ -5798,9 +5724,9 @@ bool AudioPolicyManager::isStrategyActive(const sp& outpu return false; } -bool AudioPolicyManager::isStrategyActiveOnSameModule(const sp& outputDesc, - routing_strategy strategy, uint32_t inPastMs, - nsecs_t sysTime) const +bool AudioPolicyManager::isStrategyActiveOnSameModule(const sp& outputDesc, + routing_strategy strategy, uint32_t inPastMs, + nsecs_t sysTime) const { for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 9eb1dcfaa6..b214f1fa0f 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -313,36 +313,40 @@ protected: // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND // before updateDevicesAndOutputs() is called. virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, - bool fromCache); + bool fromCache) + { + return getDevicesForStrategy(strategy, fromCache).types(); + } + + DeviceVector getDevicesForStrategy(routing_strategy strategy, bool fromCache); bool isStrategyActive(const sp& outputDesc, routing_strategy strategy, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; - bool isStrategyActiveOnSameModule(const sp& outputDesc, - routing_strategy strategy, uint32_t inPastMs = 0, - nsecs_t sysTime = 0) const; + bool isStrategyActiveOnSameModule(const sp& outputDesc, + routing_strategy strategy, uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; // change the route of the specified output. Returns the number of ms we have slept to // allow new routing to take effect in certain cases. - virtual uint32_t setOutputDevice(const sp& outputDesc, - audio_devices_t device, - bool force = false, - int delayMs = 0, - audio_patch_handle_t *patchHandle = NULL, - const char *address = nullptr, - bool requiresMuteCheck = true); + uint32_t setOutputDevices(const sp& outputDesc, + const DeviceVector &device, + bool force = false, + int delayMs = 0, + audio_patch_handle_t *patchHandle = NULL, + bool requiresMuteCheck = true); status_t resetOutputDevice(const sp& outputDesc, int delayMs = 0, audio_patch_handle_t *patchHandle = NULL); status_t setInputDevice(audio_io_handle_t input, - audio_devices_t device, + const sp &device, bool force = false, audio_patch_handle_t *patchHandle = NULL); status_t resetInputDevice(audio_io_handle_t input, audio_patch_handle_t *patchHandle = NULL); // select input device corresponding to requested audio source - virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource); + sp getDeviceForInputSource(audio_source_t inputSource); // compute the actual volume for a given stream according to the requested index and a particular // device @@ -391,15 +395,13 @@ protected: // when a device is disconnected, checks if an output is not used any more and // returns its handle if any. // transfers the audio tracks and effects from one output thread to another accordingly. - status_t checkOutputsForDevice(const sp& devDesc, + status_t checkOutputsForDevice(const sp& device, audio_policy_dev_state_t state, - SortedVector& outputs, - const String8& address); + SortedVector& outputs); - status_t checkInputsForDevice(const sp& devDesc, + status_t checkInputsForDevice(const sp& device, audio_policy_dev_state_t state, - SortedVector& inputs, - const String8& address); + SortedVector& inputs); // close an output and its companion duplicating output. void closeOutput(audio_io_handle_t output); @@ -437,8 +439,8 @@ protected: // must be called every time a condition that affects the device choice for a given output is // changed: connected device, phone state, force use, output start, output stop.. // see getDeviceForStrategy() for the use of fromCache parameter - audio_devices_t getNewOutputDevice(const sp& outputDesc, - bool fromCache); + DeviceVector getNewOutputDevices(const sp& outputDesc, + bool fromCache); // updates cache of device used by all strategies (mDeviceForStrategy[]) // must be called every time a condition that affects the device choice for a given strategy is @@ -448,7 +450,7 @@ protected: void updateDevicesAndOutputs(); // selects the most appropriate device on input for current state - audio_devices_t getNewInputDevice(const sp& inputDesc); + sp getNewInputDevice(const sp& inputDesc); virtual uint32_t getMaxEffectsCpuLoad() { @@ -460,16 +462,16 @@ protected: return mEffects.getMaxEffectsMemory(); } - SortedVector getOutputsForDevice(audio_devices_t device, - const SwAudioOutputCollection& openOutputs); + SortedVector getOutputsForDevices( + const DeviceVector &devices, const SwAudioOutputCollection& openOutputs); // mute/unmute strategies using an incompatible device combination // if muting, wait for the audio in pcm buffer to be drained before proceeding // if unmuting, unmute only after the specified delay // Returns the number of ms waited virtual uint32_t checkDeviceMuteStrategies(const sp& outputDesc, - audio_devices_t prevDevice, - uint32_t delayMs); + audio_devices_t prevDeviceType, + uint32_t delayMs); audio_io_handle_t selectOutput(const SortedVector& outputs, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, @@ -477,13 +479,22 @@ protected: audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE, uint32_t samplingRate = 0); // samplingRate, format, channelMask are in/out and so may be modified - sp getInputProfile(audio_devices_t device, - const String8& address, + sp getInputProfile(const sp & device, uint32_t& samplingRate, audio_format_t& format, audio_channel_mask_t& channelMask, audio_input_flags_t flags); - sp getProfileForOutput(audio_devices_t device, + /** + * @brief getProfileForOutput + * @param devices vector of descriptors, may be empty if ignoring the device is required + * @param samplingRate + * @param format + * @param channelMask + * @param flags + * @param directOnly + * @return IOProfile to be used if found, nullptr otherwise + */ + sp getProfileForOutput(const DeviceVector &devices, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -501,19 +512,19 @@ protected: return mAudioPatches.removeAudioPatch(handle); } - audio_devices_t availablePrimaryOutputDevices() const + DeviceVector availablePrimaryOutputDevices() const { if (!hasPrimaryOutput()) { - return AUDIO_DEVICE_NONE; + return DeviceVector(); } - return mPrimaryOutput->supportedDevices() & mAvailableOutputDevices.types(); + return mAvailableOutputDevices.filter(mPrimaryOutput->supportedDevices().types()); } - audio_devices_t availablePrimaryInputDevices() const + DeviceVector availablePrimaryModuleInputDevices() const { if (!hasPrimaryOutput()) { - return AUDIO_DEVICE_NONE; + return DeviceVector(); } - return mAvailableInputDevices.getDeviceTypesFromHwModule( + return mAvailableInputDevices.getDevicesFromHwModule( mPrimaryOutput->getModuleHandle()); } /** @@ -530,8 +541,9 @@ protected: return (devices.size() > 0) ? devices.itemAt(0)->address() : String8(""); } - uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0); - sp createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs); + uint32_t updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs = 0); + sp createTelephonyPatch(bool isRx, const sp &device, + uint32_t delayMs); sp findDevice( const DeviceVector& devices, audio_devices_t device) const; audio_devices_t getModuleDeviceTypes( @@ -581,7 +593,16 @@ protected: DeviceVector mAvailableInputDevices; // all available input devices bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected - audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; + + /** + * @brief mDevicesForStrategy vector of devices that are assigned for a given strategy. + * Note: in case of removal of device (@see setDeviceConnectionState), the device descriptor + * will be removed from the @see mAvailableOutputDevices or @see mAvailableInputDevices + * but the devices for strategies will be reevaluated within the + * @see setDeviceConnectionState function. + */ + DeviceVector mDevicesForStrategy[NUM_STRATEGIES]; + float mLastVoiceVolume; // last voice volume value sent to audio HAL bool mA2dpSuspended; // true if A2DP output is suspended @@ -637,13 +658,14 @@ private: // Support for Multi-Stream Decoder (MSD) module sp getMsdAudioInDevice() const; + DeviceVector getMsdAudioOutDevices() const; const AudioPatchCollection getMsdPatches() const; - status_t getBestMsdAudioProfileFor(audio_devices_t outputDevice, + status_t getBestMsdAudioProfileFor(const sp &outputDevice, bool hwAvSync, audio_port_config *sourceConfig, audio_port_config *sinkConfig) const; - PatchBuilder buildMsdPatch(audio_devices_t outputDevice) const; - status_t setMsdPatch(audio_devices_t outputDevice = AUDIO_DEVICE_NONE); + PatchBuilder buildMsdPatch(const sp &outputDevice) const; + status_t setMsdPatch(const sp &outputDevice = nullptr); // If any, resolve any "dynamic" fields of an Audio Profiles collection void updateAudioProfiles(const sp& devDesc, audio_io_handle_t ioHandle, @@ -654,22 +676,12 @@ private: // It can give a chance to HAL implementer to retrieve dynamic capabilities associated // to this device for example. // TODO avoid opening stream to retrieve capabilities of a profile. - void broadcastDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const String8 &device_address); + void broadcastDeviceConnectionState(const sp &device, + audio_policy_dev_state_t state); // updates device caching and output for streams that can influence the // routing of notifications void handleNotificationRoutingForStream(audio_stream_type_t stream); - // find the outputs on a given output descriptor that have the given address. - // to be called on an AudioOutputDescriptor whose supported devices (as defined - // in mProfile->mSupportedDevices) matches the device whose address is to be matched. - // see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one - // where addresses are used to distinguish between one connected device and another. - void findIoHandlesByAddress(const sp& desc /*in*/, - const audio_devices_t device /*in*/, - const String8& address /*in*/, - SortedVector& outputs /*out*/); uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; } // internal method, get audio_attributes_t from either a source audio_attributes_t // or audio_stream_type_t, respectively. @@ -687,15 +699,14 @@ private: audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId); // internal method to return the output handle for the given device and format - audio_io_handle_t getOutputForDevice( - audio_devices_t device, + audio_io_handle_t getOutputForDevices( + const DeviceVector &devices, audio_session_t session, audio_stream_type_t stream, const audio_config_t *config, audio_output_flags_t *flags); // internal method to return the input handle for the given device and format - audio_io_handle_t getInputForDevice(audio_devices_t device, - String8 address, + audio_io_handle_t getInputForDevice(const sp &device, audio_session_t session, audio_source_t inputSource, const audio_config_base_t *config, @@ -713,14 +724,14 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). - audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp getDeviceAndMixForInputSource(audio_source_t inputSource, + AudioMix **policyMix = NULL); // Called by setDeviceConnectionState(). - status_t setDeviceConnectionStateInt(audio_devices_t device, - audio_policy_dev_state_t state, - const char *device_address, - const char *device_name); + status_t setDeviceConnectionStateInt(audio_devices_t deviceType, + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name); void updateMono(audio_io_handle_t output) { AudioParameter param; param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono); -- GitLab From 716e1435b2b0af38c801f2bf3872b9edc2ba7c33 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Mon, 14 Jan 2019 16:58:59 +0100 Subject: [PATCH 0721/1530] audiopolicy: fix rsubmix / device with dynamic address regressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Orphan devices (aka devices not declared in configuration file) used to be managed by apm::mAvailableDevices and affinity with profile was done by type only. As the device affinity with profile is now enforced by checking with descriptor rather than with type, profile's supported devices list shall be dynamically updated. Test: manual audio smoke tests Change-Id: I5e8092968bbdb03ac7dc1aa1a3f15a0bc1b09251 Signed-off-by: François Gaffie --- .../include/AudioInputDescriptor.h | 1 + .../managerdefinitions/include/AudioPort.h | 3 +- .../include/DeviceDescriptor.h | 4 +- .../managerdefinitions/include/HwModule.h | 63 +++++++- .../managerdefinitions/include/IOProfile.h | 24 +-- .../src/AudioInputDescriptor.cpp | 53 ++++--- .../src/AudioOutputDescriptor.cpp | 11 -- .../managerdefinitions/src/AudioPort.cpp | 6 + .../src/DeviceDescriptor.cpp | 17 +-- .../managerdefinitions/src/HwModule.cpp | 116 +++++++++++++-- .../wrapper/ParameterManagerWrapper.cpp | 4 +- .../managerdefault/AudioPolicyManager.cpp | 140 +++++++++--------- .../managerdefault/AudioPolicyManager.h | 8 +- 13 files changed, 284 insertions(+), 166 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 842b7e7636..d4cfd1ebd0 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -68,6 +68,7 @@ public: bool isSourceActive(audio_source_t source) const; audio_source_t source() const; bool isSoundTrigger() const; + audio_attributes_t getHighestPriorityAttributes() const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } void trackEffectEnabled(const sp &effect, bool enabled); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h index bb9cad8c6b..1b5a2d6d8a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -65,6 +65,7 @@ public: uint32_t getFlags() const { return mFlags; } virtual void attach(const sp& module); + virtual void detach(); bool isAttached() { return mModule != 0; } // Audio port IDs are in a different namespace than AudioFlinger unique IDs @@ -161,7 +162,7 @@ public: const struct audio_port_config *srcConfig = NULL) const = 0; virtual sp getAudioPort() const = 0; virtual bool hasSameHwModuleAs(const sp& other) const { - return (other != 0) && + return (other != 0) && (other->getAudioPort() != 0) && (getAudioPort() != 0) && (other->getAudioPort()->getModuleHandle() == getAudioPort()->getModuleHandle()); } unsigned int mSamplingRate = 0u; diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index 9181c9a976..b581665caf 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -53,6 +53,8 @@ public: // AudioPort virtual void attach(const sp& module); + virtual void detach(); + virtual void toAudioPort(struct audio_port *port) const; virtual void importAudioPort(const sp& port, bool force = false); @@ -125,8 +127,6 @@ public: */ DeviceVector filter(const DeviceVector &devices) const; - DeviceVector filter(audio_devices_t deviceTypes) const; - /** * @brief merge two vectors. As SortedVector Implementation is buggy (it does not check the size * of the destination vector, only of the source, it provides a safe implementation diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h index 69126ba0aa..d7dc4b009e 100644 --- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -46,6 +46,22 @@ public: const DeviceVector &getDeclaredDevices() const { return mDeclaredDevices; } void setDeclaredDevices(const DeviceVector &devices); + DeviceVector getAllDevices() const + { + DeviceVector devices = mDeclaredDevices; + devices.merge(mDynamicDevices); + return devices; + } + void addDynamicDevice(const sp &device) + { + mDynamicDevices.add(device); + } + + bool removeDynamicDevice(const sp &device) + { + return mDynamicDevices.remove(device) >= 0; + } + DeviceVector getDynamicDevices() const { return mDynamicDevices; } const InputProfileCollection &getInputProfiles() const { return mInputProfiles; } const OutputProfileCollection &getOutputProfiles() const { return mOutputProfiles; } @@ -104,6 +120,7 @@ private: InputProfileCollection mInputProfiles; // input profiles exposed by this module uint32_t mHalVersion; // audio HAL API version DeviceVector mDeclaredDevices; // devices declared in audio_policy configuration file. + DeviceVector mDynamicDevices; /**< devices that can be added/removed at runtime (e.g. rsbumix)*/ AudioRouteVector mRoutes; AudioPortVector mPorts; }; @@ -120,11 +137,51 @@ public: DeviceVector getAvailableDevicesFromModuleName(const char *name, const DeviceVector &availableDevices) const; - sp getDeviceDescriptor(const audio_devices_t device, - const char *device_address, - const char *device_name, + /** + * @brief getDeviceDescriptor returns a device descriptor associated to the device type and + * device address (if matchAddress is true). + * It may loop twice on all modules to check if allowToCreate is true + * -first loop will check if the device is found on a module since declared in the list + * of device port in configuration file + * -(allowToCreate is true)second loop will check if the device is weakly supported by one + * or more profiles on a given module and will add as a supported device for this module. + * The device will also be added to the dynamic list of device of this module + * @param type of the device requested + * @param address of the device requested + * @param name of the device that requested + * @param matchAddress true if a strong match is required + * @param allowToCreate true if allowed to create dynamic device (e.g. hdmi, usb...) + * @return device descriptor associated to the type (and address if matchAddress is true) + */ + sp getDeviceDescriptor(const audio_devices_t type, + const char *address, + const char *name, + bool allowToCreate = false, bool matchAddress = true) const; + /** + * @brief createDevice creates a new device from the type and address given. It checks that + * according to the device type, a module is supporting this device (weak check). + * This concerns only dynamic device, aka device with a specific address and not + * already supported by module/underlying profiles. + * @param type of the device to be created + * @param address of the device to be created + * @param name of the device to be created + * @return device descriptor if a module is supporting this type, nullptr otherwise. + */ + sp createDevice(const audio_devices_t type, + const char *address, + const char *name) const; + + /** + * @brief cleanUpForDevice: loop on all profiles of all modules to remove device from + * the list of supported device. If this device is a dynamic device (aka a device not in the + * xml file with a runtime address), it is also removed from the module collection of dynamic + * devices. + * @param device that has been disconnected + */ + void cleanUpForDevice(const sp &device); + void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index 7761625ede..d0c05a5af3 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -99,11 +99,6 @@ public: return mSupportedDevices.types() & (device & ~AUDIO_DEVICE_BIT_IN); } - bool supportDeviceAddress(const String8 &address) const - { - return mSupportedDevices[0]->address() == address; - } - /** * @brief supportsDevice * @param device to be checked against @@ -121,26 +116,15 @@ public: return mSupportedDevices.contains(device); } - // chose first device present in mSupportedDevices also part of deviceType - audio_devices_t getSupportedDeviceForType(audio_devices_t deviceType) const - { - for (size_t k = 0; k < mSupportedDevices.size(); k++) { - audio_devices_t profileType = mSupportedDevices[k]->type(); - if (profileType & deviceType) { - return profileType; - } - } - return AUDIO_DEVICE_NONE; - } - - audio_devices_t getSupportedDevicesType() const { return mSupportedDevices.types(); } - void clearSupportedDevices() { mSupportedDevices.clear(); } void addSupportedDevice(const sp &device) { mSupportedDevices.add(device); } - + void removeSupportedDevice(const sp &device) + { + mSupportedDevices.remove(device); + } void setSupportedDevices(const DeviceVector &devices) { mSupportedDevices = devices; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index c483ffea32..55d4db4488 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -54,30 +54,7 @@ audio_port_handle_t AudioInputDescriptor::getId() const audio_source_t AudioInputDescriptor::source() const { - audio_source_t source = AUDIO_SOURCE_DEFAULT; - - for (bool activeOnly : { true, false }) { - int32_t topPriority = -1; - app_state_t topState = APP_STATE_IDLE; - for (const auto &client : getClientIterable()) { - if (activeOnly && !client->active()) { - continue; - } - app_state_t curState = client->appState(); - if (curState >= topState) { - int32_t curPriority = source_priority(client->source()); - if (curPriority > topPriority) { - source = client->source(); - topPriority = curPriority; - } - topState = curState; - } - } - if (source != AUDIO_SOURCE_DEFAULT) { - break; - } - } - return source; + return getHighestPriorityAttributes().source; } void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, @@ -147,6 +124,34 @@ bool AudioInputDescriptor::isSourceActive(audio_source_t source) const return false; } +audio_attributes_t AudioInputDescriptor::getHighestPriorityAttributes() const +{ + audio_attributes_t attributes = { .source = AUDIO_SOURCE_DEFAULT }; + + for (bool activeOnly : { true, false }) { + int32_t topPriority = -1; + app_state_t topState = APP_STATE_IDLE; + for (const auto &client : getClientIterable()) { + if (activeOnly && !client->active()) { + continue; + } + app_state_t curState = client->appState(); + if (curState >= topState) { + int32_t curPriority = source_priority(client->source()); + if (curPriority > topPriority) { + attributes = client->attributes(); + topPriority = curPriority; + } + topState = curState; + } + } + if (attributes.source != AUDIO_SOURCE_DEFAULT) { + break; + } + } + return attributes; +} + bool AudioInputDescriptor::isSoundTrigger() const { // sound trigger and non sound trigger clients are not mixed on a given input // so check only first client diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 578d4937a0..643cbd100d 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -350,10 +350,6 @@ DeviceVector SwAudioOutputDescriptor::supportedDevices() const bool SwAudioOutputDescriptor::supportsDevice(const sp &device) const { - // Performs weak check until dynamic support of supportedDevice on Modules/Profiles - if (!device_distinguishes_on_address(device->type())) { - return supportedDevices().types() & (device->type()); - } return supportedDevices().contains(device); } @@ -758,13 +754,6 @@ bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgno return false; } -audio_devices_t SwAudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const -{ - sp outputDesc = valueFor(handle); - audio_devices_t devices = outputDesc->mProfile->getSupportedDevicesType(); - return devices; -} - sp SwAudioOutputCollection::getOutputForClient(audio_port_handle_t portId) { for (size_t i = 0; i < size(); i++) { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp index 19dde6a609..9fcf5e7a9c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -31,9 +31,15 @@ namespace android { // --- AudioPort class implementation void AudioPort::attach(const sp& module) { + ALOGV("%s: attaching module %s to port %s", __FUNCTION__, getModuleName(), mName.string()); mModule = module; } +void AudioPort::detach() +{ + mModule = nullptr; +} + // Note that is a different namespace than AudioFlinger unique IDs audio_port_handle_t AudioPort::getNextUniqueId() { diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 7137786d0f..01111c5150 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -58,6 +58,12 @@ void DeviceDescriptor::attach(const sp& module) mId = getNextUniqueId(); } +void DeviceDescriptor::detach() +{ + mId = AUDIO_PORT_HANDLE_NONE; + AudioPort::detach(); +} + bool DeviceDescriptor::equals(const sp& other) const { // Devices are considered equal if they: @@ -335,17 +341,6 @@ DeviceVector DeviceVector::filter(const DeviceVector &devices) const return filteredDevices; } -DeviceVector DeviceVector::filter(audio_devices_t deviceTypes) const -{ - DeviceVector filteredDevices; - for (const auto &device : *this) { - if ((device->type() & deviceTypes) == device->type()) { - filteredDevices.add(device); - } - } - return filteredDevices; -} - bool DeviceVector::containsAtLeastOne(const DeviceVector &devices) const { return !filter(devices).isEmpty(); diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 3547a056f9..7d2d094f4e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -52,6 +52,9 @@ status_t HwModule::addOutputProfile(const String8& name, const audio_config_t *c sp devDesc = new DeviceDescriptor(device); devDesc->setAddress(address); + addDynamicDevice(devDesc); + // Reciprocally attach the device to the module + devDesc->attach(this); profile->addSupportedDevice(devDesc); return addOutputProfile(profile); @@ -97,6 +100,9 @@ status_t HwModule::removeOutputProfile(const String8& name) { for (size_t i = 0; i < mOutputProfiles.size(); i++) { if (mOutputProfiles[i]->getName() == name) { + for (const auto &device : mOutputProfiles[i]->getSupportedDevices()) { + removeDynamicDevice(device); + } mOutputProfiles.removeAt(i); break; } @@ -114,6 +120,9 @@ status_t HwModule::addInputProfile(const String8& name, const audio_config_t *co sp devDesc = new DeviceDescriptor(device); devDesc->setAddress(address); + addDynamicDevice(devDesc); + // Reciprocally attach the device to the module + devDesc->attach(this); profile->addSupportedDevice(devDesc); ALOGV("addInputProfile() name %s rate %d mask 0x%08x", @@ -126,6 +135,9 @@ status_t HwModule::removeInputProfile(const String8& name) { for (size_t i = 0; i < mInputProfiles.size(); i++) { if (mInputProfiles[i]->getName() == name) { + for (const auto &device : mInputProfiles[i]->getSupportedDevices()) { + removeDynamicDevice(device); + } mInputProfiles.removeAt(i); break; } @@ -247,6 +259,7 @@ void HwModule::dump(String8 *dst) const } } mDeclaredDevices.dump(dst, String8("Declared"), 2, true); + mDynamicDevices.dump(dst, String8("Dynamic"), 2, true); mRoutes.dump(dst, 2); } @@ -298,30 +311,103 @@ DeviceVector HwModuleCollection::getAvailableDevicesFromModuleName( return availableDevices.getDevicesFromHwModule(module->getHandle()); } -sp HwModuleCollection::getDeviceDescriptor(const audio_devices_t device, - const char *device_address, - const char *device_name, +sp HwModuleCollection::getDeviceDescriptor(const audio_devices_t deviceType, + const char *address, + const char *name, + bool allowToCreate, bool matchAddress) const { - String8 address = (device_address == nullptr || !matchAddress) ? - String8("") : String8(device_address); + String8 devAddress = (address == nullptr || !matchAddress) ? String8("") : String8(address); // handle legacy remote submix case where the address was not always specified - if (device_distinguishes_on_address(device) && (address.length() == 0)) { - address = String8("0"); + if (device_distinguishes_on_address(deviceType) && (devAddress.length() == 0)) { + devAddress = String8("0"); } for (const auto& hwModule : *this) { - DeviceVector declaredDevices = hwModule->getDeclaredDevices(); - sp deviceDesc = declaredDevices.getDevice(device, address); - if (deviceDesc) { - return deviceDesc; + DeviceVector moduleDevices = hwModule->getAllDevices(); + auto moduleDevice = moduleDevices.getDevice(deviceType, devAddress); + if (moduleDevice) { + if (allowToCreate) { + moduleDevice->attach(hwModule); + } + return moduleDevice; } } + if (!allowToCreate) { + ALOGE("%s: could not find HW module for device %s %04x address %s", __FUNCTION__, + name, deviceType, address); + return nullptr; + } + return createDevice(deviceType, address, name); +} - sp devDesc = new DeviceDescriptor(device); - devDesc->setName(String8(device_name)); - devDesc->setAddress(address); - return devDesc; +sp HwModuleCollection::createDevice(const audio_devices_t type, + const char *address, + const char *name) const +{ + sp hwModule = getModuleForDeviceTypes(type); + if (hwModule == 0) { + ALOGE("%s: could not find HW module for device %04x address %s", __FUNCTION__, type, + address); + return nullptr; + } + sp device = new DeviceDescriptor(type, String8(name)); + device->setName(String8(name)); + device->setAddress(String8(address)); + + // Add the device to the list of dynamic devices + hwModule->addDynamicDevice(device); + // Reciprocally attach the device to the module + device->attach(hwModule); + ALOGD("%s: adding dynamic device %s to module %s", __FUNCTION__, + device->toString().c_str(), hwModule->getName()); + + const auto &profiles = (audio_is_output_device(type) ? hwModule->getOutputProfiles() : + hwModule->getInputProfiles()); + for (const auto &profile : profiles) { + // Add the device as supported to all profile supporting "weakly" or not the device + // according to its type + if (profile->supportsDevice(device, false /*matchAdress*/)) { + + // @todo quid of audio profile? import the profile from device of the same type? + const auto &isoTypeDeviceForProfile = profile->getSupportedDevices().getDevice(type); + device->importAudioPort(isoTypeDeviceForProfile, true /* force */); + + ALOGV("%s: adding device %s to profile %s", __FUNCTION__, + device->toString().c_str(), profile->getTagName().c_str()); + profile->addSupportedDevice(device); + } + } + return device; +} + +void HwModuleCollection::cleanUpForDevice(const sp &device) +{ + for (const auto& hwModule : *this) { + DeviceVector moduleDevices = hwModule->getAllDevices(); + if (!moduleDevices.contains(device)) { + continue; + } + device->detach(); + // Only remove from dynamic list, not from declared list!!! + if (!hwModule->getDynamicDevices().contains(device)) { + return; + } + hwModule->removeDynamicDevice(device); + ALOGV("%s: removed dynamic device %s from module %s", __FUNCTION__, + device->toString().c_str(), hwModule->getName()); + + const IOProfileCollection &profiles = audio_is_output_device(device->type()) ? + hwModule->getOutputProfiles() : hwModule->getInputProfiles(); + for (const auto &profile : profiles) { + // For cleanup, strong match is required + if (profile->supportsDevice(device, true /*matchAdress*/)) { + ALOGV("%s: removing device %s from profile %s", __FUNCTION__, + device->toString().c_str(), profile->getTagName().c_str()); + profile->removeSupportedDevice(device); + } + } + } } void HwModuleCollection::dump(String8 *dst) const diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index fc6c1e4173..1934fa4c3b 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -295,8 +295,8 @@ status_t ParameterManagerWrapper::setDeviceConnectionState(const spgetCriterionType(); int deviceAddressId; - if (not criterionType->getNumericalValue(devDesc->mAddress.string(), deviceAddressId)) { - ALOGE("%s: unknown device address reported (%s)", __FUNCTION__, devDesc->mAddress.c_str()); + if (not criterionType->getNumericalValue(devDesc->address().string(), deviceAddressId)) { + ALOGW("%s: unknown device address reported (%s)", __FUNCTION__, devDesc->address().c_str()); return BAD_TYPE; } int currentValueMask = criterion->getCriterionState(); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 47e6016614..d7cf88ee72 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -110,7 +110,11 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT if (!audio_is_output_device(deviceType) && !audio_is_input_device(deviceType)) return BAD_VALUE; sp device = - mHwModules.getDeviceDescriptor(deviceType, device_address, device_name); + mHwModules.getDeviceDescriptor(deviceType, device_address, device_name, + state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE); + if (device == 0) { + return INVALID_OPERATION; + } // handle output devices if (audio_is_output_device(deviceType)) { @@ -132,17 +136,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGV("%s() connecting device %s", __func__, device->toString().c_str()); // register new device as available - index = mAvailableOutputDevices.add(device); - if (index >= 0) { - sp module = mHwModules.getModuleForDeviceTypes(deviceType); - if (module == 0) { - ALOGD("setDeviceConnectionState() could not find HW module for device %s", - device->toString().c_str()); - mAvailableOutputDevices.remove(device); - return INVALID_OPERATION; - } - mAvailableOutputDevices[index]->attach(module); - } else { + if (mAvailableOutputDevices.add(device) < 0) { return NO_MEMORY; } @@ -153,6 +147,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT if (checkOutputsForDevice(device, state, outputs) != NO_ERROR) { mAvailableOutputDevices.remove(device); + mHwModules.cleanUpForDevice(device); + broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); return INVALID_OPERATION; } @@ -252,26 +248,19 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - sp module = mHwModules.getModuleForDeviceTypes(deviceType); - if (module == NULL) { - ALOGW("setDeviceConnectionState(): could not find HW module for device %08x", - deviceType); - return INVALID_OPERATION; - } - // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) broadcastDeviceConnectionState(device, state); if (checkInputsForDevice(device, state, inputs) != NO_ERROR) { broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); + + mHwModules.cleanUpForDevice(device); + return INVALID_OPERATION; } - index = mAvailableInputDevices.add(device); - if (index >= 0) { - mAvailableInputDevices[index]->attach(module); - } else { + if (mAvailableInputDevices.add(device) < 0) { return NO_MEMORY; } @@ -329,7 +318,7 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi const char *device_address) { sp devDesc = - mHwModules.getDeviceDescriptor(device, device_address, "", + mHwModules.getDeviceDescriptor(device, device_address, "", false /* allowToCreate */, (strlen(device_address) != 0)/*matchAddress*/); if (devDesc == 0) { @@ -371,8 +360,7 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, // Check if the device is currently connected sp devDesc = mHwModules.getDeviceDescriptor(device, device_address, device_name); - ssize_t index = mAvailableOutputDevices.indexOf(devDesc); - if (index < 0) { + if (devDesc == 0 || mAvailableOutputDevices.indexOf(devDesc) < 0) { // Nothing to do: device is not connected return NO_ERROR; } @@ -430,7 +418,8 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui } ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device"); - auto txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION }; + auto txDevice = getDeviceAndMixForAttributes(attr); ALOGV("updateCallRouting device rxDevice %s txDevice %s", rxDevices.toString().c_str(), txDevice->toString().c_str()); @@ -699,8 +688,7 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, for (const auto& activeDesc : mInputs.getActiveInputs()) { auto newDevice = getNewInputDevice(activeDesc); // Force new input selection if the new device can not be reached via current input - if (activeDesc->mProfile->getSupportedDevices().types() & - (newDevice->type() & ~AUDIO_DEVICE_BIT_IN)) { + if (activeDesc->mProfile->getSupportedDevices().contains(newDevice)) { setInputDevice(activeDesc->mIoHandle, newDevice); } else { closeInput(activeDesc->mIoHandle); @@ -818,7 +806,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, DeviceVector devices; routing_strategy strategy; audio_devices_t deviceType = AUDIO_DEVICE_NONE; - const audio_port_handle_t requestedDeviceId = *selectedDeviceId; + const audio_port_handle_t requestedPortId = *selectedDeviceId; DeviceVector msdDevices = getMsdAudioOutDevices(); status_t status = getAudioAttributes(resultAttr, attr, *stream); @@ -830,17 +818,16 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, " session %d selectedDeviceId %d", __func__, resultAttr->usage, resultAttr->content_type, resultAttr->tags, resultAttr->flags, - session, requestedDeviceId); + session, requestedPortId); *stream = streamTypefromAttributesInt(resultAttr); strategy = getStrategyForAttr(resultAttr); // First check for explicit routing (eg. setPreferredDevice) - if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { - sp deviceDesc = - mAvailableOutputDevices.getDeviceFromId(requestedDeviceId); - deviceType = deviceDesc->type(); + sp requestedDevice = mAvailableOutputDevices.getDeviceFromId(requestedPortId); + if (requestedDevice != nullptr) { + deviceType = requestedDevice->type(); } else { // If no explict route, is there a matching dynamic policy that applies? sp desc; @@ -880,7 +867,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) && audio_is_linear_pcm(config->format) && isInCall()) { - if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) { + if (requestedPortId != AUDIO_PORT_HANDLE_NONE) { *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC; } else { // Get the devce type directly from the engine to bypass preferred route logic @@ -934,7 +921,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, if (*portId != AUDIO_PORT_HANDLE_NONE) { return INVALID_OPERATION; } - const audio_port_handle_t requestedDeviceId = *selectedDeviceId; + const audio_port_handle_t requestedPortId = *selectedDeviceId; audio_attributes_t resultAttr; status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid, config, flags, selectedDeviceId); @@ -949,14 +936,14 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, sp clientDesc = new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, - requestedDeviceId, *stream, + requestedPortId, *stream, getStrategyForAttr(&resultAttr), *flags); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); ALOGV("%s returns output %d selectedDeviceId %d for port ID %d", - __func__, *output, requestedDeviceId, *portId); + __func__, *output, requestedPortId, *portId); return NO_ERROR; } @@ -1746,7 +1733,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, status_t status = NO_ERROR; audio_source_t halInputSource; - audio_source_t inputSource = attr->source; + audio_attributes_t attributes = *attr; AudioMix *policyMix = NULL; sp device; sp inputDesc; @@ -1759,8 +1746,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, return INVALID_OPERATION; } - if (inputSource == AUDIO_SOURCE_DEFAULT) { - inputSource = AUDIO_SOURCE_MIC; + if (attr->source == AUDIO_SOURCE_DEFAULT) { + attributes.source = AUDIO_SOURCE_MIC; } // Explicit routing? @@ -1815,11 +1802,11 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, *input = AUDIO_IO_HANDLE_NONE; *inputType = API_INPUT_INVALID; - halInputSource = inputSource; + halInputSource = attributes.source; - if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && - strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { - status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix); + if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && + strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) { + status = mPolicyMixes.getInputMixForAttr(attributes, &policyMix); if (status != NO_ERROR) { goto error; } @@ -1830,10 +1817,10 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; } else { - device = getDeviceAndMixForInputSource(inputSource, &policyMix); + device = getDeviceAndMixForAttributes(attributes, &policyMix); } if (device == nullptr) { - ALOGW("getInputForAttr() could not find device for source %d", inputSource); + ALOGW("getInputForAttr() could not find device for source %d", attributes.source); status = BAD_VALUE; goto error; } @@ -1854,7 +1841,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } - *input = getInputForDevice(device, session, inputSource, + *input = getInputForDevice(device, session, attributes.source, config, flags, policyMix); if (*input == AUDIO_IO_HANDLE_NONE) { @@ -1867,12 +1854,13 @@ exit: *selectedDeviceId = mAvailableInputDevices.contains(device) ? device->getId() : AUDIO_PORT_HANDLE_NONE; - isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD && + isSoundTrigger = attributes.source == AUDIO_SOURCE_HOTWORD && mSoundTriggerSessions.indexOfKey(session) > 0; *portId = AudioPort::getNextUniqueId(); clientDesc = new RecordClientDescriptor(*portId, uid, session, *attr, *config, - requestedDeviceId, inputSource,flags, isSoundTrigger); + requestedDeviceId, attributes.source, flags, + isSoundTrigger); inputDesc = mInputs.valueFor(*input); inputDesc->addClient(clientDesc); @@ -3425,7 +3413,8 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session { *session = (audio_session_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION); *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_INPUT); - *device = getDeviceAndMixForInputSource(AUDIO_SOURCE_HOTWORD)->type(); + audio_attributes_t attr = { .source = AUDIO_SOURCE_HOTWORD }; + *device = getDeviceAndMixForAttributes(attr)->type(); return mSoundTriggerSessions.acquireSession(*session, *ioHandle); } @@ -4349,10 +4338,9 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d desc = mOutputs.valueAt(i); if (!desc->isDuplicated()) { // exact match on device - if (device_distinguishes_on_address(deviceType) && - (desc->supportedDevices().types() == deviceType)) { + if (device_distinguishes_on_address(deviceType) && desc->supportsDevice(device)) { outputs.add(mOutputs.keyAt(i)); - } else if (!(desc->supportedDevices().types() & mAvailableOutputDevices.types())) { + } else if (!mAvailableOutputDevices.containsAtLeastOne(desc->supportedDevices())) { ALOGV("checkOutputsForDevice(): disconnecting adding output %d", mOutputs.keyAt(i)); outputs.add(mOutputs.keyAt(i)); @@ -4494,7 +4482,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de // check if one opened input is not needed any more after disconnecting one device for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { desc = mInputs.valueAt(input_index); - if (!(desc->mProfile->supportsDeviceTypes(mAvailableInputDevices.types()))) { + if (!mAvailableInputDevices.containsAtLeastOne(desc->supportedDevices())) { ALOGV("checkInputsForDevice(): disconnecting adding input %d", mInputs.keyAt(input_index)); inputs.add(mInputs.keyAt(input_index)); @@ -4506,7 +4494,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de profile_index < hwModule->getInputProfiles().size(); profile_index++) { sp profile = hwModule->getInputProfiles()[profile_index]; - if (profile->supportsDeviceTypes(device->type())) { + if (profile->supportsDevice(device)) { ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %s", profile_index, hwModule->getName()); profile->clearAudioProfiles(); @@ -4637,8 +4625,7 @@ SortedVector AudioPolicyManager::getOutputsForDevices( ALOGVV("output %zu isDuplicated=%d device=%s", i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices().toString().c_str()); - if ((devices.types() & openOutputs.valueAt(i)->supportedDevices().types()) == - devices.types()) { + if (openOutputs.valueAt(i)->supportsAllDevices(devices)) { ALOGVV("%s() found output %d", __func__, openOutputs.keyAt(i)); outputs.add(openOutputs.keyAt(i)); } @@ -4684,7 +4671,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) } } - if (srcOutputs != dstOutputs) { + if (!dstOutputs.isEmpty() && srcOutputs != dstOutputs) { // get maximum latency of all source outputs to determine the minimum mute time guaranteeing // audio from invalidated tracks will be rendered when unmuting uint32_t maxLatency = 0; @@ -4922,12 +4909,12 @@ sp AudioPolicyManager::getNewInputDevice( // If we are not in call and no client is active on this input, this methods returns // AUDIO_DEVICE_NONE, causing the patch on the input stream to be released. - audio_source_t source = inputDesc->source(); - if (source == AUDIO_SOURCE_DEFAULT && isInCall()) { - source = AUDIO_SOURCE_VOICE_COMMUNICATION; + audio_attributes_t attributes = inputDesc->getHighestPriorityAttributes(); + if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) { + attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION; } - if (source != AUDIO_SOURCE_DEFAULT) { - device = getDeviceAndMixForInputSource(source); + if (attributes.source != AUDIO_SOURCE_DEFAULT) { + device = getDeviceAndMixForAttributes(attributes); } return device; @@ -5199,7 +5186,7 @@ uint32_t AudioPolicyManager::setOutputDevices(const sp& } // filter devices according to output selected - DeviceVector filteredDevices = devices.filter(outputDesc->supportedDevices().types()); + DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices); // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current // output profile @@ -5390,27 +5377,32 @@ sp AudioPolicyManager::getInputProfile(const sp &de return NULL; } -sp AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) +sp AudioPolicyManager::getDeviceAndMixForAttributes( + const audio_attributes_t &attributes, AudioMix **policyMix) { // Honor explicit routing requests only if all active clients have a preferred route in which // case the last active client route is used sp device = - findPreferredDevice(mInputs, inputSource, mAvailableInputDevices); + findPreferredDevice(mInputs, attributes.source, mAvailableInputDevices); if (device != nullptr) { return device; } sp selectedDeviceFromMix = - mPolicyMixes.getDeviceAndMixForInputSource(inputSource, mAvailableInputDevices, + mPolicyMixes.getDeviceAndMixForInputSource(attributes.source, mAvailableInputDevices, policyMix); return (selectedDeviceFromMix != nullptr) ? - selectedDeviceFromMix : getDeviceForInputSource(inputSource); + selectedDeviceFromMix : getDeviceForAttributes(attributes); } -sp AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) +sp AudioPolicyManager::getDeviceForAttributes(const audio_attributes_t &attributes) { - audio_devices_t device = mEngine->getDeviceForInputSource(inputSource); + audio_devices_t device = mEngine->getDeviceForInputSource(attributes.source); + if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && + strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) { + return mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + String8(attributes.tags + strlen("addr="))); + } return mAvailableInputDevices.getDevice(device); } @@ -5785,6 +5777,8 @@ void AudioPolicyManager::cleanUpForDevice(const sp& deviceDesc releaseAudioPatch(patchDesc->mHandle, patchDesc->mUid); } } + + mHwModules.cleanUpForDevice(deviceDesc); } void AudioPolicyManager::modifySurroundFormats( diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index b214f1fa0f..e99de16493 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -346,7 +346,7 @@ protected: audio_patch_handle_t *patchHandle = NULL); // select input device corresponding to requested audio source - sp getDeviceForInputSource(audio_source_t inputSource); + sp getDeviceForAttributes(const audio_attributes_t &attributes); // compute the actual volume for a given stream according to the requested index and a particular // device @@ -517,7 +517,7 @@ protected: if (!hasPrimaryOutput()) { return DeviceVector(); } - return mAvailableOutputDevices.filter(mPrimaryOutput->supportedDevices().types()); + return mAvailableOutputDevices.filter(mPrimaryOutput->supportedDevices()); } DeviceVector availablePrimaryModuleInputDevices() const { @@ -724,8 +724,8 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). - sp getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp getDeviceAndMixForAttributes(const audio_attributes_t &attributes, + AudioMix **policyMix = NULL); // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t deviceType, -- GitLab From b141c526eac14ad0f92c9ec6aae6a3b3ebc35708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 12 Mar 2018 11:47:40 +0100 Subject: [PATCH 0722/1530] audiopolicy: apm: Fix segfault when using bus device without Policy Mix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: manual audio smoke tests Change-Id: Ic9ecd5928931d63d42cecea7283c7282ec43ebc0 Signed-off-by: François Gaffie --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d7cf88ee72..30b438d17e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4271,12 +4271,13 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d addOutput(output, desc); if (device_distinguishes_on_address(deviceType) && address != "0") { sp policyMix; - if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) { - ALOGE("checkOutputsForDevice() cannot find policy for address %s", + if (mPolicyMixes.getAudioPolicyMix(address, policyMix) == NO_ERROR) { + policyMix->setOutput(desc); + desc->mPolicyMix = policyMix->getMix(); + } else { + ALOGW("checkOutputsForDevice() cannot find policy for address %s", address.string()); } - policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) { -- GitLab From 36e45afe2f43c64de1724ba9d8f662c1f09f7ee7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 14 Jan 2019 16:09:24 -0800 Subject: [PATCH 0723/1530] Really fix end-of-stream handling in OggExtractor Bug: 122496376 Bug: 122805825 Test: CTS, manual Change-Id: I486b8c81bb8e21b54d9eb835eb9b25737d8b0102 --- media/extractors/ogg/OggExtractor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index 590358c469..596c1c8ced 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -515,10 +515,10 @@ ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) { ALOGV("failed to read %zu bytes at offset %#016llx, got %zd bytes", sizeof(header), (long long)offset, n); - if (n < 0) { - return n; - } else if (n == 0) { + if (n == 0 || n == ERROR_END_OF_STREAM) { return AMEDIA_ERROR_END_OF_STREAM; + } else if (n < 0) { + return AMEDIA_ERROR_UNKNOWN; } else { return AMEDIA_ERROR_IO; } @@ -872,7 +872,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool cal ALOGV("readPage returned %zd", n); - return n < 0 ? (media_status_t) n : AMEDIA_ERROR_END_OF_STREAM; + return (media_status_t) n; } // Prevent a harmless unsigned integer overflow by clamping to 0 -- GitLab From 7aafa219855503a52acae4e0526a31b4177a835e Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 11 Jan 2019 11:08:24 -0800 Subject: [PATCH 0724/1530] Ensure that Thread(/*canCallJava*/ true) is attached to the VM. This is supposed to be done by runtime, but when libutils is dual-loaded with linker namespace, CreateThreadFunc should be initialized separately within the namespace since libutils keeps it in a global variable. Test: build & boot & atest MediaPlayer2Test Bug: 112766913 Change-Id: I23eac262c8b88178bf151d7c6ab49a48af932001 --- media/libmediaplayer2/JavaVMHelper.cpp | 114 ++++++++++++++++++ .../include/mediaplayer2/JavaVMHelper.h | 1 + 2 files changed, 115 insertions(+) diff --git a/media/libmediaplayer2/JavaVMHelper.cpp b/media/libmediaplayer2/JavaVMHelper.cpp index 90aaa7fb46..8d03ed0a10 100644 --- a/media/libmediaplayer2/JavaVMHelper.cpp +++ b/media/libmediaplayer2/JavaVMHelper.cpp @@ -19,6 +19,7 @@ #include "mediaplayer2/JavaVMHelper.h" #include +#include #include @@ -27,6 +28,109 @@ namespace android { // static std::atomic JavaVMHelper::sJavaVM(NULL); +/* + * Makes the current thread visible to the VM. + * + * The JNIEnv pointer returned is only valid for the current thread, and + * thus must be tucked into thread-local storage. + */ +static int javaAttachThread(const char* threadName, JNIEnv** pEnv) { + JavaVMAttachArgs args; + JavaVM* vm; + jint result; + + vm = JavaVMHelper::getJavaVM(); + if (vm == NULL) { + return JNI_ERR; + } + + args.version = JNI_VERSION_1_4; + args.name = (char*) threadName; + args.group = NULL; + + result = vm->AttachCurrentThread(pEnv, (void*) &args); + if (result != JNI_OK) { + ALOGI("NOTE: attach of thread '%s' failed\n", threadName); + } + + return result; +} + +/* + * Detach the current thread from the set visible to the VM. + */ +static int javaDetachThread(void) { + JavaVM* vm; + jint result; + + vm = JavaVMHelper::getJavaVM(); + if (vm == NULL) { + return JNI_ERR; + } + + result = vm->DetachCurrentThread(); + if (result != JNI_OK) { + ALOGE("ERROR: thread detach failed\n"); + } + return result; +} + +/* + * When starting a native thread that will be visible from the VM, we + * bounce through this to get the right attach/detach action. + * Note that this function calls free(args) + */ +static int javaThreadShell(void* args) { + void* start = ((void**)args)[0]; + void* userData = ((void **)args)[1]; + char* name = (char*) ((void **)args)[2]; // we own this storage + free(args); + JNIEnv* env; + int result; + + /* hook us into the VM */ + if (javaAttachThread(name, &env) != JNI_OK) { + return -1; + } + + /* start the thread running */ + result = (*(android_thread_func_t)start)(userData); + + /* unhook us */ + javaDetachThread(); + free(name); + + return result; +} + +/* + * This is invoked from androidCreateThreadEtc() via the callback + * set with androidSetCreateThreadFunc(). + * + * We need to create the new thread in such a way that it gets hooked + * into the VM before it really starts executing. + */ +static int javaCreateThreadEtc( + android_thread_func_t entryFunction, + void* userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t* threadId) { + void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free + int result; + + LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc"); + + args[0] = (void*) entryFunction; + args[1] = userData; + args[2] = (void*) strdup(threadName); // javaThreadShell must free + + result = androidCreateRawThreadEtc(javaThreadShell, args, + threadName, threadPriority, threadStackSize, threadId); + return result; +} + // static JNIEnv *JavaVMHelper::getJNIEnv() { JNIEnv *env; @@ -40,9 +144,19 @@ JNIEnv *JavaVMHelper::getJNIEnv() { return env; } +//static +JavaVM *JavaVMHelper::getJavaVM() { + return sJavaVM.load(); +} + // static void JavaVMHelper::setJavaVM(JavaVM *vm) { sJavaVM.store(vm); + + // Ensure that Thread(/*canCallJava*/ true) in libutils is attached to the VM. + // This is supposed to be done by runtime, but when libutils is used with linker + // namespace, CreateThreadFunc should be initialized separately within the namespace. + androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc); } } // namespace android diff --git a/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h index 35091b7917..4b56aca6f4 100644 --- a/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h +++ b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h @@ -26,6 +26,7 @@ namespace android { struct JavaVMHelper { static JNIEnv *getJNIEnv(); + static JavaVM *getJavaVM(); static void setJavaVM(JavaVM *vm); private: -- GitLab From 5253b344a9d3f7007958561dacddb8da732e63f0 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 10 Jan 2019 19:00:27 -0800 Subject: [PATCH 0725/1530] Include libmediaplayer2_jni to media apex Test: build Bug: 112766913 Change-Id: Ib3266ef36adbd956aad6ca274626ccc53780b12c --- apex/Android.bp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apex/Android.bp b/apex/Android.bp index 604b26850b..51e4c2322d 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -27,6 +27,8 @@ apex { "libmpeg2extractor", "liboggextractor", "libwavextractor", + // MediaPlayer2 + "libmediaplayer2_jni", ], key: "com.android.media.key", } -- GitLab From a577a6c56a9a7b75af377eb1672cf320d8cac7b0 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 15 Jan 2019 11:54:14 -0800 Subject: [PATCH 0726/1530] media/codec2: move HDR10PlusMetadata to image/video section Bug: 118507186 Change-Id: I426e919f981cef19d4f06c5e60c15ae40c5e0795 --- media/codec2/core/include/C2Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index cf1f6cf0de..e5bf6dffd5 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -180,6 +180,7 @@ enum C2ParamIndexKind : C2Param::type_index_t { kParamIndexPictureTypeMask, kParamIndexPictureType, + kParamIndexHdr10PlusMetadata, /* ------------------------------------ video components ------------------------------------ */ @@ -194,7 +195,6 @@ enum C2ParamIndexKind : C2Param::type_index_t { kParamIndexLayerIndex, kParamIndexLayerCount, kParamIndexIntraRefresh, - kParamIndexHdr10PlusMetadata, /* ------------------------------------ image components ------------------------------------ */ -- GitLab From 26280682dda17a67f34b0d861a090a9d49e28689 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 15 Nov 2018 15:12:43 -0800 Subject: [PATCH 0727/1530] stagefright: add asString for MediaCodecConstants enumerations Also add level 6+ for AVC Bug: 119631295 Change-Id: I57abab1b9b55dc67eb9cc2eab5f94ae83309d62d --- .../media/stagefright/MediaCodecConstants.h | 383 ++++++++++++++++++ 1 file changed, 383 insertions(+) diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index 984c23d61a..c06c2885d4 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -31,6 +31,21 @@ constexpr int32_t AVCProfileHigh444 = 0x40; constexpr int32_t AVCProfileConstrainedBaseline = 0x10000; constexpr int32_t AVCProfileConstrainedHigh = 0x80000; +inline static const char *asString_AVCProfile(int32_t i, const char *def = "??") { + switch (i) { + case AVCProfileBaseline: return "Baseline"; + case AVCProfileMain: return "Main"; + case AVCProfileExtended: return "Extended"; + case AVCProfileHigh: return "High"; + case AVCProfileHigh10: return "High10"; + case AVCProfileHigh422: return "High422"; + case AVCProfileHigh444: return "High444"; + case AVCProfileConstrainedBaseline: return "ConstrainedBaseline"; + case AVCProfileConstrainedHigh: return "ConstrainedHigh"; + default: return def; + } +} + constexpr int32_t AVCLevel1 = 0x01; constexpr int32_t AVCLevel1b = 0x02; constexpr int32_t AVCLevel11 = 0x04; @@ -48,6 +63,35 @@ constexpr int32_t AVCLevel42 = 0x2000; constexpr int32_t AVCLevel5 = 0x4000; constexpr int32_t AVCLevel51 = 0x8000; constexpr int32_t AVCLevel52 = 0x10000; +constexpr int32_t AVCLevel6 = 0x20000; +constexpr int32_t AVCLevel61 = 0x40000; +constexpr int32_t AVCLevel62 = 0x80000; + +inline static const char *asString_AVCLevel(int32_t i, const char *def = "??") { + switch (i) { + case AVCLevel1: return "1"; + case AVCLevel1b: return "1b"; + case AVCLevel11: return "1.1"; + case AVCLevel12: return "1.2"; + case AVCLevel13: return "1.3"; + case AVCLevel2: return "2"; + case AVCLevel21: return "2.1"; + case AVCLevel22: return "2.2"; + case AVCLevel3: return "3"; + case AVCLevel31: return "3.1"; + case AVCLevel32: return "3.2"; + case AVCLevel4: return "4"; + case AVCLevel41: return "4.1"; + case AVCLevel42: return "4.2"; + case AVCLevel5: return "5"; + case AVCLevel51: return "5.1"; + case AVCLevel52: return "5.2"; + case AVCLevel6: return "6"; + case AVCLevel61: return "6.1"; + case AVCLevel62: return "6.2"; + default: return def; + } +} constexpr int32_t H263ProfileBaseline = 0x01; constexpr int32_t H263ProfileH320Coding = 0x02; @@ -59,6 +103,21 @@ constexpr int32_t H263ProfileInternet = 0x40; constexpr int32_t H263ProfileInterlace = 0x80; constexpr int32_t H263ProfileHighLatency = 0x100; +inline static const char *asString_H263Profile(int32_t i, const char *def = "??") { + switch (i) { + case H263ProfileBaseline: return "Baseline"; + case H263ProfileH320Coding: return "H320Coding"; + case H263ProfileBackwardCompatible: return "BackwardCompatible"; + case H263ProfileISWV2: return "ISWV2"; + case H263ProfileISWV3: return "ISWV3"; + case H263ProfileHighCompression: return "HighCompression"; + case H263ProfileInternet: return "Internet"; + case H263ProfileInterlace: return "Interlace"; + case H263ProfileHighLatency: return "HighLatency"; + default: return def; + } +} + constexpr int32_t H263Level10 = 0x01; constexpr int32_t H263Level20 = 0x02; constexpr int32_t H263Level30 = 0x04; @@ -68,6 +127,20 @@ constexpr int32_t H263Level50 = 0x20; constexpr int32_t H263Level60 = 0x40; constexpr int32_t H263Level70 = 0x80; +inline static const char *asString_H263Level(int32_t i, const char *def = "??") { + switch (i) { + case H263Level10: return "10"; + case H263Level20: return "20"; + case H263Level30: return "30"; + case H263Level40: return "40"; + case H263Level45: return "45"; + case H263Level50: return "50"; + case H263Level60: return "60"; + case H263Level70: return "70"; + default: return def; + } +} + constexpr int32_t MPEG4ProfileSimple = 0x01; constexpr int32_t MPEG4ProfileSimpleScalable = 0x02; constexpr int32_t MPEG4ProfileCore = 0x04; @@ -85,6 +158,28 @@ constexpr int32_t MPEG4ProfileAdvancedCore = 0x2000; constexpr int32_t MPEG4ProfileAdvancedScalable = 0x4000; constexpr int32_t MPEG4ProfileAdvancedSimple = 0x8000; +inline static const char *asString_MPEG4Profile(int32_t i, const char *def = "??") { + switch (i) { + case MPEG4ProfileSimple: return "Simple"; + case MPEG4ProfileSimpleScalable: return "SimpleScalable"; + case MPEG4ProfileCore: return "Core"; + case MPEG4ProfileMain: return "Main"; + case MPEG4ProfileNbit: return "Nbit"; + case MPEG4ProfileScalableTexture: return "ScalableTexture"; + case MPEG4ProfileSimpleFace: return "SimpleFace"; + case MPEG4ProfileSimpleFBA: return "SimpleFBA"; + case MPEG4ProfileBasicAnimated: return "BasicAnimated"; + case MPEG4ProfileHybrid: return "Hybrid"; + case MPEG4ProfileAdvancedRealTime: return "AdvancedRealTime"; + case MPEG4ProfileCoreScalable: return "CoreScalable"; + case MPEG4ProfileAdvancedCoding: return "AdvancedCoding"; + case MPEG4ProfileAdvancedCore: return "AdvancedCore"; + case MPEG4ProfileAdvancedScalable: return "AdvancedScalable"; + case MPEG4ProfileAdvancedSimple: return "AdvancedSimple"; + default: return def; + } +} + constexpr int32_t MPEG4Level0 = 0x01; constexpr int32_t MPEG4Level0b = 0x02; constexpr int32_t MPEG4Level1 = 0x04; @@ -96,6 +191,22 @@ constexpr int32_t MPEG4Level4a = 0x40; constexpr int32_t MPEG4Level5 = 0x80; constexpr int32_t MPEG4Level6 = 0x100; +inline static const char *asString_MPEG4Level(int32_t i, const char *def = "??") { + switch (i) { + case MPEG4Level0: return "0"; + case MPEG4Level0b: return "0b"; + case MPEG4Level1: return "1"; + case MPEG4Level2: return "2"; + case MPEG4Level3: return "3"; + case MPEG4Level3b: return "3b"; + case MPEG4Level4: return "4"; + case MPEG4Level4a: return "4a"; + case MPEG4Level5: return "5"; + case MPEG4Level6: return "6"; + default: return def; + } +} + constexpr int32_t MPEG2ProfileSimple = 0x00; constexpr int32_t MPEG2ProfileMain = 0x01; constexpr int32_t MPEG2Profile422 = 0x02; @@ -103,12 +214,35 @@ constexpr int32_t MPEG2ProfileSNR = 0x03; constexpr int32_t MPEG2ProfileSpatial = 0x04; constexpr int32_t MPEG2ProfileHigh = 0x05; +inline static const char *asString_MPEG2Profile(int32_t i, const char *def = "??") { + switch (i) { + case MPEG2ProfileSimple: return "Simple"; + case MPEG2ProfileMain: return "Main"; + case MPEG2Profile422: return "422"; + case MPEG2ProfileSNR: return "SNR"; + case MPEG2ProfileSpatial: return "Spatial"; + case MPEG2ProfileHigh: return "High"; + default: return def; + } +} + constexpr int32_t MPEG2LevelLL = 0x00; constexpr int32_t MPEG2LevelML = 0x01; constexpr int32_t MPEG2LevelH14 = 0x02; constexpr int32_t MPEG2LevelHL = 0x03; constexpr int32_t MPEG2LevelHP = 0x04; +inline static const char *asString_MPEG2Level(int32_t i, const char *def = "??") { + switch (i) { + case MPEG2LevelLL: return "LL"; + case MPEG2LevelML: return "ML"; + case MPEG2LevelH14: return "H14"; + case MPEG2LevelHL: return "HL"; + case MPEG2LevelHP: return "HP"; + default: return def; + } +} + constexpr int32_t AACObjectMain = 1; constexpr int32_t AACObjectLC = 2; constexpr int32_t AACObjectSSR = 3; @@ -122,13 +256,48 @@ constexpr int32_t AACObjectHE_PS = 29; constexpr int32_t AACObjectELD = 39; constexpr int32_t AACObjectXHE = 42; +inline static const char *asString_AACObject(int32_t i, const char *def = "??") { + switch (i) { + case AACObjectMain: return "Main"; + case AACObjectLC: return "LC"; + case AACObjectSSR: return "SSR"; + case AACObjectLTP: return "LTP"; + case AACObjectHE: return "HE"; + case AACObjectScalable: return "Scalable"; + case AACObjectERLC: return "ERLC"; + case AACObjectERScalable: return "ERScalable"; + case AACObjectLD: return "LD"; + case AACObjectHE_PS: return "HE_PS"; + case AACObjectELD: return "ELD"; + case AACObjectXHE: return "XHE"; + default: return def; + } +} + constexpr int32_t VP8Level_Version0 = 0x01; constexpr int32_t VP8Level_Version1 = 0x02; constexpr int32_t VP8Level_Version2 = 0x04; constexpr int32_t VP8Level_Version3 = 0x08; +inline static const char *asString_VP8Level(int32_t i, const char *def = "??") { + switch (i) { + case VP8Level_Version0: return "V0"; + case VP8Level_Version1: return "V1"; + case VP8Level_Version2: return "V2"; + case VP8Level_Version3: return "V3"; + default: return def; + } +} + constexpr int32_t VP8ProfileMain = 0x01; +inline static const char *asString_VP8Profile(int32_t i, const char *def = "??") { + switch (i) { + case VP8ProfileMain: return "Main"; + default: return def; + } +} + constexpr int32_t VP9Profile0 = 0x01; constexpr int32_t VP9Profile1 = 0x02; constexpr int32_t VP9Profile2 = 0x04; @@ -138,6 +307,20 @@ constexpr int32_t VP9Profile3HDR = 0x2000; constexpr int32_t VP9Profile2HDR10Plus = 0x4000; constexpr int32_t VP9Profile3HDR10Plus = 0x8000; +inline static const char *asString_VP9Profile(int32_t i, const char *def = "??") { + switch (i) { + case VP9Profile0: return "0"; + case VP9Profile1: return "1"; + case VP9Profile2: return "2"; + case VP9Profile3: return "3"; + case VP9Profile2HDR: return "2HDR"; + case VP9Profile3HDR: return "3HDR"; + case VP9Profile2HDR10Plus: return "2HDRPlus"; + case VP9Profile3HDR10Plus: return "3HDRPlus"; + default: return def; + } +} + constexpr int32_t VP9Level1 = 0x1; constexpr int32_t VP9Level11 = 0x2; constexpr int32_t VP9Level2 = 0x4; @@ -153,10 +336,39 @@ constexpr int32_t VP9Level6 = 0x800; constexpr int32_t VP9Level61 = 0x1000; constexpr int32_t VP9Level62 = 0x2000; +inline static const char *asString_VP9Level(int32_t i, const char *def = "??") { + switch (i) { + case VP9Level1: return "1"; + case VP9Level11: return "1.1"; + case VP9Level2: return "2"; + case VP9Level21: return "2.1"; + case VP9Level3: return "3"; + case VP9Level31: return "3.1"; + case VP9Level4: return "4"; + case VP9Level41: return "4.1"; + case VP9Level5: return "5"; + case VP9Level51: return "5.1"; + case VP9Level52: return "5.2"; + case VP9Level6: return "6"; + case VP9Level61: return "6.1"; + case VP9Level62: return "6.2"; + default: return def; + } +} + constexpr int32_t AV1Profile0 = 0x01; constexpr int32_t AV1Profile1 = 0x02; constexpr int32_t AV1Profile2 = 0x04; +inline static const char *asString_AV1Profile(int32_t i, const char *def = "??") { + switch (i) { + case AV1Profile0: return "0"; + case AV1Profile1: return "1"; + case AV1Profile2: return "2"; + default: return def; + } +} + constexpr int32_t AV1Level2 = 0x1; constexpr int32_t AV1Level21 = 0x2; constexpr int32_t AV1Level22 = 0x4; @@ -182,12 +394,53 @@ constexpr int32_t AV1Level71 = 0x200000; constexpr int32_t AV1Level72 = 0x400000; constexpr int32_t AV1Level73 = 0x800000; +inline static const char *asString_AV1Level(int32_t i, const char *def = "??") { + switch (i) { + case AV1Level2: return "2"; + case AV1Level21: return "2.1"; + case AV1Level22: return "2.2"; + case AV1Level23: return "2.3"; + case AV1Level3: return "3"; + case AV1Level31: return "3.1"; + case AV1Level32: return "3.2"; + case AV1Level33: return "3.3"; + case AV1Level4: return "4"; + case AV1Level41: return "4.1"; + case AV1Level42: return "4.2"; + case AV1Level43: return "4.3"; + case AV1Level5: return "5"; + case AV1Level51: return "5.1"; + case AV1Level52: return "5.2"; + case AV1Level53: return "5.3"; + case AV1Level6: return "6"; + case AV1Level61: return "6.1"; + case AV1Level62: return "6.2"; + case AV1Level63: return "6.3"; + case AV1Level7: return "7"; + case AV1Level71: return "7.1"; + case AV1Level72: return "7.2"; + case AV1Level73: return "7.3"; + default: return def; + } +} + constexpr int32_t HEVCProfileMain = 0x01; constexpr int32_t HEVCProfileMain10 = 0x02; constexpr int32_t HEVCProfileMainStill = 0x04; constexpr int32_t HEVCProfileMain10HDR10 = 0x1000; constexpr int32_t HEVCProfileMain10HDR10Plus = 0x2000; +inline static const char *asString_HEVCProfile(int32_t i, const char *def = "??") { + switch (i) { + case HEVCProfileMain: return "Main"; + case HEVCProfileMain10: return "Main10"; + case HEVCProfileMainStill: return "MainStill"; + case HEVCProfileMain10HDR10: return "Main10HDR10"; + case HEVCProfileMain10HDR10Plus: return "Main10HDR10Plus"; + default: return def; + } +} + constexpr int32_t HEVCMainTierLevel1 = 0x1; constexpr int32_t HEVCHighTierLevel1 = 0x2; constexpr int32_t HEVCMainTierLevel2 = 0x4; @@ -215,6 +468,38 @@ constexpr int32_t HEVCHighTierLevel61 = 0x800000; constexpr int32_t HEVCMainTierLevel62 = 0x1000000; constexpr int32_t HEVCHighTierLevel62 = 0x2000000; +inline static const char *asString_HEVCTierLevel(int32_t i, const char *def = "??") { + switch (i) { + case HEVCMainTierLevel1: return "Main 1"; + case HEVCHighTierLevel1: return "High 1"; + case HEVCMainTierLevel2: return "Main 2"; + case HEVCHighTierLevel2: return "High 2"; + case HEVCMainTierLevel21: return "Main 2.1"; + case HEVCHighTierLevel21: return "High 2.1"; + case HEVCMainTierLevel3: return "Main 3"; + case HEVCHighTierLevel3: return "High 3"; + case HEVCMainTierLevel31: return "Main 3.1"; + case HEVCHighTierLevel31: return "High 3.1"; + case HEVCMainTierLevel4: return "Main 4"; + case HEVCHighTierLevel4: return "High 4"; + case HEVCMainTierLevel41: return "Main 4.1"; + case HEVCHighTierLevel41: return "High 4.1"; + case HEVCMainTierLevel5: return "Main 5"; + case HEVCHighTierLevel5: return "High 5"; + case HEVCMainTierLevel51: return "Main 5.1"; + case HEVCHighTierLevel51: return "High 5.1"; + case HEVCMainTierLevel52: return "Main 5.2"; + case HEVCHighTierLevel52: return "High 5.2"; + case HEVCMainTierLevel6: return "Main 6"; + case HEVCHighTierLevel6: return "High 6"; + case HEVCMainTierLevel61: return "Main 6.1"; + case HEVCHighTierLevel61: return "High 6.1"; + case HEVCMainTierLevel62: return "Main 6.2"; + case HEVCHighTierLevel62: return "High 6.2"; + default: return def; + } +} + constexpr int32_t DolbyVisionProfileDvavPer = 0x1; constexpr int32_t DolbyVisionProfileDvavPen = 0x2; constexpr int32_t DolbyVisionProfileDvheDer = 0x4; @@ -226,6 +511,22 @@ constexpr int32_t DolbyVisionProfileDvheDtb = 0x80; constexpr int32_t DolbyVisionProfileDvheSt = 0x100; constexpr int32_t DolbyVisionProfileDvavSe = 0x200; +inline static const char *asString_DolbyVisionProfile(int32_t i, const char *def = "??") { + switch (i) { + case DolbyVisionProfileDvavPer: return "DvavPer"; + case DolbyVisionProfileDvavPen: return "DvavPen"; + case DolbyVisionProfileDvheDer: return "DvheDer"; + case DolbyVisionProfileDvheDen: return "DvheDen"; + case DolbyVisionProfileDvheDtr: return "DvheDtr"; + case DolbyVisionProfileDvheStn: return "DvheStn"; + case DolbyVisionProfileDvheDth: return "DvheDth"; + case DolbyVisionProfileDvheDtb: return "DvheDtb"; + case DolbyVisionProfileDvheSt: return "DvheSt"; + case DolbyVisionProfileDvavSe: return "DvavSe"; + default: return def; + } +} + constexpr int32_t DolbyVisionLevelHd24 = 0x1; constexpr int32_t DolbyVisionLevelHd30 = 0x2; constexpr int32_t DolbyVisionLevelFhd24 = 0x4; @@ -236,10 +537,34 @@ constexpr int32_t DolbyVisionLevelUhd30 = 0x40; constexpr int32_t DolbyVisionLevelUhd48 = 0x80; constexpr int32_t DolbyVisionLevelUhd60 = 0x100; +inline static const char *asString_DolbyVisionLevel(int32_t i, const char *def = "??") { + switch (i) { + case DolbyVisionLevelHd24: return "Hd24"; + case DolbyVisionLevelHd30: return "Hd30"; + case DolbyVisionLevelFhd24: return "Fhd24"; + case DolbyVisionLevelFhd30: return "Fhd30"; + case DolbyVisionLevelFhd60: return "Fhd60"; + case DolbyVisionLevelUhd24: return "Uhd24"; + case DolbyVisionLevelUhd30: return "Uhd30"; + case DolbyVisionLevelUhd48: return "Uhd48"; + case DolbyVisionLevelUhd60: return "Uhd60"; + default: return def; + } +} + constexpr int32_t BITRATE_MODE_CBR = 2; constexpr int32_t BITRATE_MODE_CQ = 0; constexpr int32_t BITRATE_MODE_VBR = 1; +inline static const char *asString_BitrateMode(int32_t i, const char *def = "??") { + switch (i) { + case BITRATE_MODE_CBR: return "CBR"; + case BITRATE_MODE_CQ: return "CQ"; + case BITRATE_MODE_VBR: return "VBR"; + default: return def; + } +} + constexpr int32_t COLOR_Format12bitRGB444 = 3; constexpr int32_t COLOR_Format16bitARGB1555 = 5; constexpr int32_t COLOR_Format16bitARGB4444 = 4; @@ -293,6 +618,64 @@ constexpr int32_t COLOR_FormatYUV444Interleaved = 29; constexpr int32_t COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00; constexpr int32_t COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; +inline static const char *asString_ColorFormat(int32_t i, const char *def = "??") { + switch (i) { + case COLOR_Format12bitRGB444: return "12bitRGB444"; + case COLOR_Format16bitARGB1555: return "16bitARGB1555"; + case COLOR_Format16bitARGB4444: return "16bitARGB4444"; + case COLOR_Format16bitBGR565: return "16bitBGR565"; + case COLOR_Format16bitRGB565: return "16bitRGB565"; + case COLOR_Format18bitARGB1665: return "18bitARGB1665"; + case COLOR_Format18BitBGR666: return "18BitBGR666"; + case COLOR_Format18bitRGB666: return "18bitRGB666"; + case COLOR_Format19bitARGB1666: return "19bitARGB1666"; + case COLOR_Format24BitABGR6666: return "24BitABGR6666"; + case COLOR_Format24bitARGB1887: return "24bitARGB1887"; + case COLOR_Format24BitARGB6666: return "24BitARGB6666"; + case COLOR_Format24bitBGR888: return "24bitBGR888"; + case COLOR_Format24bitRGB888: return "24bitRGB888"; + case COLOR_Format25bitARGB1888: return "25bitARGB1888"; + case COLOR_Format32bitABGR8888: return "32bitABGR8888"; + case COLOR_Format32bitARGB8888: return "32bitARGB8888"; + case COLOR_Format32bitBGRA8888: return "32bitBGRA8888"; + case COLOR_Format8bitRGB332: return "8bitRGB332"; + case COLOR_FormatCbYCrY: return "CbYCrY"; + case COLOR_FormatCrYCbY: return "CrYCbY"; + case COLOR_FormatL16: return "L16"; + case COLOR_FormatL2: return "L2"; + case COLOR_FormatL24: return "L24"; + case COLOR_FormatL32: return "L32"; + case COLOR_FormatL4: return "L4"; + case COLOR_FormatL8: return "L8"; + case COLOR_FormatMonochrome: return "Monochrome"; + case COLOR_FormatRawBayer10bit: return "RawBayer10bit"; + case COLOR_FormatRawBayer8bit: return "RawBayer8bit"; + case COLOR_FormatRawBayer8bitcompressed: return "RawBayer8bitcompressed"; + case COLOR_FormatRGBAFlexible: return "RGBAFlexible"; + case COLOR_FormatRGBFlexible: return "RGBFlexible"; + case COLOR_FormatSurface: return "Surface"; + case COLOR_FormatYCbYCr: return "YCbYCr"; + case COLOR_FormatYCrYCb: return "YCrYCb"; + case COLOR_FormatYUV411PackedPlanar: return "YUV411PackedPlanar"; + case COLOR_FormatYUV411Planar: return "YUV411Planar"; + case COLOR_FormatYUV420Flexible: return "YUV420Flexible"; + case COLOR_FormatYUV420PackedPlanar: return "YUV420PackedPlanar"; + case COLOR_FormatYUV420PackedSemiPlanar: return "YUV420PackedSemiPlanar"; + case COLOR_FormatYUV420Planar: return "YUV420Planar"; + case COLOR_FormatYUV420SemiPlanar: return "YUV420SemiPlanar"; + case COLOR_FormatYUV422Flexible: return "YUV422Flexible"; + case COLOR_FormatYUV422PackedPlanar: return "YUV422PackedPlanar"; + case COLOR_FormatYUV422PackedSemiPlanar: return "YUV422PackedSemiPlanar"; + case COLOR_FormatYUV422Planar: return "YUV422Planar"; + case COLOR_FormatYUV422SemiPlanar: return "YUV422SemiPlanar"; + case COLOR_FormatYUV444Flexible: return "YUV444Flexible"; + case COLOR_FormatYUV444Interleaved: return "YUV444Interleaved"; + case COLOR_QCOM_FormatYUV420SemiPlanar: return "QCOM_YUV420SemiPlanar"; + case COLOR_TI_FormatYUV420PackedSemiPlanar: return "TI_YUV420PackedSemiPlanar"; + default: return def; + } +} + constexpr char FEATURE_AdaptivePlayback[] = "adaptive-playback"; constexpr char FEATURE_IntraRefresh[] = "intra-refresh"; constexpr char FEATURE_PartialFrame[] = "partial-frame"; -- GitLab From d6c948ca7ff3e69281e7126dd4b9868f128ab935 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 15 Jan 2019 12:25:17 -0800 Subject: [PATCH 0728/1530] FLACExtractor: Fix sniffer to allow STREAMINFO only metadata Test: MediaExtractorTest#testFlacIdentity (no regression) Bug: 122902567 Change-Id: I835af8698fc83fdf06f21afa4c0a42193c9b80ad --- media/extractors/flac/FLACExtractor.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index e4bbc07b91..dcda6bf310 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -806,14 +806,21 @@ media_status_t FLACExtractor::getMetaData(AMediaFormat *meta) bool SniffFLAC(DataSourceHelper *source, float *confidence) { - // first 4 is the signature word - // second 4 is the sizeof STREAMINFO - // 042 is the mandatory STREAMINFO - // no need to read rest of the header, as a premature EOF will be caught later - uint8_t header[4+4]; - if (source->readAt(0, header, sizeof(header)) != sizeof(header) - || memcmp("fLaC\0\0\0\042", header, 4+4)) - { + // FLAC header. + // https://xiph.org/flac/format.html#stream + // + // Note: content stored big endian. + // byte offset bit size content + // 0 32 fLaC + // 4 8 metadata type STREAMINFO (0) (note: OR with 0x80 if last metadata) + // 5 24 size of metadata, for STREAMINFO (0x22). + + // Android is LE, so express header as little endian int64 constant. + constexpr int64_t flacHeader = (0x22LL << 56) | 'CaLf'; + constexpr int64_t flacHeader2 = flacHeader | (0x80LL << 32); // alternate form (last metadata) + int64_t header; + if (source->readAt(0, &header, sizeof(header)) != sizeof(header) + || (header != flacHeader && header != flacHeader2)) { return false; } -- GitLab From 20f9bf837fa3102bbd9e15b8a8c6ed14b72df292 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 15 Jan 2019 14:46:30 -0800 Subject: [PATCH 0729/1530] audio: Add presubmit tests for APM Hook up audiopolicy_tests and systemaudio_tests as presubmit tests. These tests are native C++Unit tests, it takes a couple of millisec to run them. Bug: 122855449 Test: cd frameworks/av/services/audiopolicy && atest Change-Id: I7abfeb81472ff2a2610948aa514eb506bed7546b --- services/audiopolicy/TEST_MAPPING | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 services/audiopolicy/TEST_MAPPING diff --git a/services/audiopolicy/TEST_MAPPING b/services/audiopolicy/TEST_MAPPING new file mode 100644 index 0000000000..a94fd87768 --- /dev/null +++ b/services/audiopolicy/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "audiopolicy_tests" + }, + { + "name": "systemaudio_tests" + } + ] +} -- GitLab From 8d4bdfd7e52f92d0e3eda23fec5081191bbf9a7b Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 13 Nov 2018 14:23:49 -0800 Subject: [PATCH 0730/1530] media/stagefright: add support for codec attributes in MediaCodecInfo - Move away from int flags, and signal capabilities in info AMessage/Map instead. - Add more attributes (other than isEncoder). - Parse aliases, attributes and performance points from XML - Add plumbing for codec aliases and attributes - Use proper 'mediaType' phrase for supported types. Bug: 119631295 Bug: 112370870 Bug: 112374531 Change-Id: I41ea555ca26ab09be176fb96a3138043d960caa3 --- media/codec2/core/include/C2Component.h | 3 +- media/codec2/hidl/client/client.cpp | 1 + media/codec2/sfplugin/Codec2InfoBuilder.cpp | 104 +++++++++---- media/libmedia/MediaCodecInfo.cpp | 99 +++++++----- media/libmedia/include/media/MediaCodecInfo.h | 142 ++++++++++++------ media/libstagefright/ACodec.cpp | 34 +++-- media/libstagefright/MediaCodec.cpp | 8 +- media/libstagefright/MediaCodecList.cpp | 10 +- .../MediaCodecListOverrides.cpp | 12 +- media/libstagefright/OmxInfoBuilder.cpp | 57 +++++-- media/libstagefright/omx/1.0/Omx.cpp | 4 +- .../xmlparser/MediaCodecsXmlParser.cpp | 54 ++++++- .../xmlparser/MediaCodecsXmlParser.h | 5 +- 13 files changed, 373 insertions(+), 160 deletions(-) diff --git a/media/codec2/core/include/C2Component.h b/media/codec2/core/include/C2Component.h index 8810725790..ecf8d2e63e 100644 --- a/media/codec2/core/include/C2Component.h +++ b/media/codec2/core/include/C2Component.h @@ -409,12 +409,13 @@ public: kind_t kind; ///< component kind rank_t rank; ///< component rank C2String mediaType; ///< media type supported by the component + C2String owner; ///< name of the component store owning this component /** * name alias(es) for backward compatibility. * \note Multiple components can have the same alias as long as their media-type differs. */ - std::vector aliases; ///< name aliases for backward compatibility + std::vector aliases; ///< name aliases for backward compatibility }; // METHODS AVAILABLE WHEN RUNNING diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index ff3e5340f9..f5cc9ff65d 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -557,6 +557,7 @@ const std::vector& Codec2Client::listComponents() const { for (size_t i = 0; i < t.size(); ++i) { c2_status_t status = objcpy( &mTraitsList[i], &mAliasesBuffer[i], t[i]); + mTraitsList[i].owner = mInstanceName; if (status != C2_OK) { ALOGE("listComponents -- corrupted output."); return; diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 5d0ccd2039..70ffa83397 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -73,10 +73,10 @@ constexpr OMX_U32 kPortIndexOutput = 1; constexpr OMX_U32 kMaxIndicesToCheck = 32; status_t queryOmxCapabilities( - const char* name, const char* mime, bool isEncoder, + const char* name, const char* mediaType, bool isEncoder, MediaCodecInfo::CapabilitiesWriter* caps) { - const char *role = GetComponentRole(isEncoder, mime); + const char *role = GetComponentRole(isEncoder, mediaType); if (role == nullptr) { return BAD_VALUE; } @@ -128,8 +128,8 @@ status_t queryOmxCapabilities( return err; } - bool isVideo = hasPrefix(mime, "video/") == 0; - bool isImage = hasPrefix(mime, "image/") == 0; + bool isVideo = hasPrefix(mediaType, "video/") == 0; + bool isImage = hasPrefix(mediaType, "image/") == 0; if (isVideo || isImage) { OMX_VIDEO_PARAM_PROFILELEVELTYPE param; @@ -149,7 +149,7 @@ status_t queryOmxCapabilities( // AVC components may not list the constrained profiles explicitly, but // decoders that support a profile also support its constrained version. // Encoders must explicitly support constrained profiles. - if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) { + if (!isEncoder && strcasecmp(mediaType, MEDIA_MIMETYPE_VIDEO_AVC) == 0) { if (param.eProfile == OMX_VIDEO_AVCProfileHigh) { caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel); } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) { @@ -193,7 +193,7 @@ status_t queryOmxCapabilities( asString(portFormat.eColorFormat), portFormat.eColorFormat); } } - } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) { + } else if (strcasecmp(mediaType, MEDIA_MIMETYPE_AUDIO_AAC) == 0) { // More audio codecs if they have profiles. OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param; InitOMXParams(¶m); @@ -228,14 +228,17 @@ status_t queryOmxCapabilities( if (omxNode->configureVideoTunnelMode( kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) { // tunneled playback includes adaptive playback - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback - | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback); - } else if (omxNode->setPortMode( - kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK || - omxNode->prepareForAdaptivePlayback( - kPortIndexOutput, OMX_TRUE, - 1280 /* width */, 720 /* height */) == OK) { - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback); + } else { + // tunneled playback is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK); + if (omxNode->setPortMode( + kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK || + omxNode->prepareForAdaptivePlayback( + kPortIndexOutput, OMX_TRUE, + 1280 /* width */, 720 /* height */) != OK) { + // adaptive playback is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK); + } } } @@ -243,11 +246,20 @@ status_t queryOmxCapabilities( OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params; InitOMXParams(¶ms); params.nPortIndex = kPortIndexOutput; - // TODO: should we verify if fallback is supported? + + OMX_VIDEO_PARAM_INTRAREFRESHTYPE fallbackParams; + InitOMXParams(&fallbackParams); + fallbackParams.nPortIndex = kPortIndexOutput; + fallbackParams.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic; + if (omxNode->getConfig( (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh, - ¶ms, sizeof(params)) == OK) { - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh); + ¶ms, sizeof(params)) != OK && + omxNode->getParameter( + OMX_IndexParamVideoIntraRefresh, &fallbackParams, + sizeof(fallbackParams)) != OK) { + // intra refresh is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH); } } @@ -270,12 +282,26 @@ void buildOmxInfo(const MediaCodecsXmlParser& parser, writer->addMediaCodecInfo(); info->setName(name.c_str()); info->setOwner("default"); - info->setEncoder(encoder); + typename std::underlying_type::type attrs = 0; + if (encoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; + } + // NOTE: we don't support software-only codecs in OMX + if (!hasPrefix(name, "OMX.google.")) { + attrs |= MediaCodecInfo::kFlagIsVendor; + if (properties.quirkSet.find("attribute::software-codec") + == properties.quirkSet.end()) { + attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } + } + info->setAttributes(attrs); info->setRank(omxRank); - for (const MediaCodecsXmlParser::Type& type : properties.typeMap) { - const std::string &mime = type.first; + // OMX components don't have aliases + for (const MediaCodecsXmlParser::Type &type : properties.typeMap) { + const std::string &mediaType = type.first; + std::unique_ptr caps = - info->addMime(mime.c_str()); + info->addMediaType(mediaType.c_str()); const MediaCodecsXmlParser::AttributeMap &attrMap = type.second; for (const MediaCodecsXmlParser::Attribute& attr : attrMap) { const std::string &key = attr.first; @@ -289,13 +315,13 @@ void buildOmxInfo(const MediaCodecsXmlParser& parser, } status_t err = queryOmxCapabilities( name.c_str(), - mime.c_str(), + mediaType.c_str(), encoder, caps.get()); if (err != OK) { - ALOGE("Failed to query capabilities for %s (mime: %s). Error: %d", + ALOGI("Failed to query capabilities for %s (media type: %s). Error: %d", name.c_str(), - mime.c_str(), + mediaType.c_str(), static_cast(err)); } } @@ -407,20 +433,40 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { break; } + ALOGV("canonName = %s", canonName.c_str()); std::unique_ptr codecInfo = writer->addMediaCodecInfo(); codecInfo->setName(trait.name.c_str()); - codecInfo->setOwner("codec2"); + codecInfo->setOwner(("codec2::" + trait.owner).c_str()); + const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(canonName); + bool encoder = trait.kind == C2Component::KIND_ENCODER; - codecInfo->setEncoder(encoder); + typename std::underlying_type::type attrs = 0; + + if (encoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; + } + if (trait.owner == "software") { + attrs |= MediaCodecInfo::kFlagIsSoftwareOnly; + } else { + attrs |= MediaCodecInfo::kFlagIsVendor; + if (trait.owner == "vendor-software") { + attrs |= MediaCodecInfo::kFlagIsSoftwareOnly; + } else if (codec.quirkSet.find("attribute::software-codec") == codec.quirkSet.end()) { + attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } + } + codecInfo->setAttributes(attrs); codecInfo->setRank(rank); - const MediaCodecsXmlParser::CodecProperties &codec = - parser.getCodecMap().at(canonName); + + for (const std::string &alias : codec.aliases) { + codecInfo->addAlias(alias.c_str()); + } for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) { const std::string &mediaType = typeIt->first; const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second; std::unique_ptr caps = - codecInfo->addMime(mediaType.c_str()); + codecInfo->addMediaType(mediaType.c_str()); for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) { std::string key, value; std::tie(key, value) = *attrIt; diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp index 5308e1c331..86ad99778c 100644 --- a/media/libmedia/MediaCodecInfo.cpp +++ b/media/libmedia/MediaCodecInfo.cpp @@ -28,6 +28,15 @@ namespace android { +/** This redundant redeclaration is needed for C++ pre 14 */ +constexpr char MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_DYNAMIC_TIMESTAMP[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_FRAME_PARSING[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_MULTIPLE_FRAMES[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_SECURE_PLAYBACK[]; +constexpr char MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK[]; + void MediaCodecInfo::Capabilities::getSupportedProfileLevels( Vector *profileLevels) const { profileLevels->clear(); @@ -40,16 +49,11 @@ void MediaCodecInfo::Capabilities::getSupportedColorFormats( colorFormats->appendVector(mColorFormats); } -uint32_t MediaCodecInfo::Capabilities::getFlags() const { - return mFlags; -} - const sp MediaCodecInfo::Capabilities::getDetails() const { return mDetails; } -MediaCodecInfo::Capabilities::Capabilities() - : mFlags(0) { +MediaCodecInfo::Capabilities::Capabilities() { mDetails = new AMessage; } @@ -73,12 +77,10 @@ sp MediaCodecInfo::Capabilities::FromParcel( caps->mColorFormats.push_back(color); } } - uint32_t flags = static_cast(parcel.readInt32()); sp details = AMessage::FromParcel(parcel); if (details == NULL) return NULL; if (caps != NULL) { - caps->mFlags = flags; caps->mDetails = details; } return caps; @@ -96,7 +98,6 @@ status_t MediaCodecInfo::Capabilities::writeToParcel(Parcel *parcel) const { for (size_t i = 0; i < mColorFormats.size(); i++) { parcel->writeInt32(mColorFormats.itemAt(i)); } - parcel->writeInt32(mFlags); mDetails->writeToParcel(parcel); return OK; } @@ -111,6 +112,14 @@ void MediaCodecInfo::CapabilitiesWriter::addDetail( mCap->mDetails->setInt32(key, value); } +void MediaCodecInfo::CapabilitiesWriter::removeDetail(const char* key) { + if (mCap->mDetails->removeEntryAt(mCap->mDetails->findEntryByName(key)) == OK) { + ALOGD("successfully removed detail %s", key); + } else { + ALOGD("detail %s wasn't present to remove", key); + } +} + void MediaCodecInfo::CapabilitiesWriter::addProfileLevel( uint32_t profile, uint32_t level) { ProfileLevel profileLevel; @@ -129,32 +138,32 @@ void MediaCodecInfo::CapabilitiesWriter::addColorFormat(uint32_t format) { } } -void MediaCodecInfo::CapabilitiesWriter::addFlags(uint32_t flags) { - mCap->mFlags |= flags; -} - MediaCodecInfo::CapabilitiesWriter::CapabilitiesWriter( MediaCodecInfo::Capabilities* cap) : mCap(cap) { } -bool MediaCodecInfo::isEncoder() const { - return mIsEncoder; +MediaCodecInfo::Attributes MediaCodecInfo::getAttributes() const { + return mAttributes; } -uint32_t MediaCodecInfo::rank() const { +uint32_t MediaCodecInfo::getRank() const { return mRank; } -void MediaCodecInfo::getSupportedMimes(Vector *mimes) const { - mimes->clear(); +void MediaCodecInfo::getAliases(Vector *aliases) const { + *aliases = mAliases; +} + +void MediaCodecInfo::getSupportedMediaTypes(Vector *mediaTypes) const { + mediaTypes->clear(); for (size_t ix = 0; ix < mCaps.size(); ix++) { - mimes->push_back(mCaps.keyAt(ix)); + mediaTypes->push_back(mCaps.keyAt(ix)); } } const sp -MediaCodecInfo::getCapabilitiesFor(const char *mime) const { - ssize_t ix = getCapabilityIndex(mime); +MediaCodecInfo::getCapabilitiesFor(const char *mediaType) const { + ssize_t ix = getCapabilityIndex(mediaType); if (ix >= 0) { return mCaps.valueAt(ix); } @@ -173,21 +182,26 @@ const char *MediaCodecInfo::getOwnerName() const { sp MediaCodecInfo::FromParcel(const Parcel &parcel) { AString name = AString::FromParcel(parcel); AString owner = AString::FromParcel(parcel); - bool isEncoder = static_cast(parcel.readInt32()); + Attributes attributes = static_cast(parcel.readInt32()); uint32_t rank = parcel.readUint32(); sp info = new MediaCodecInfo; info->mName = name; info->mOwner = owner; - info->mIsEncoder = isEncoder; + info->mAttributes = attributes; info->mRank = rank; + size_t numAliases = static_cast(parcel.readInt32()); + for (size_t i = 0; i < numAliases; i++) { + AString alias = AString::FromParcel(parcel); + info->mAliases.add(alias); + } size_t size = static_cast(parcel.readInt32()); for (size_t i = 0; i < size; i++) { - AString mime = AString::FromParcel(parcel); + AString mediaType = AString::FromParcel(parcel); sp caps = Capabilities::FromParcel(parcel); if (caps == NULL) return NULL; if (info != NULL) { - info->mCaps.add(mime, caps); + info->mCaps.add(mediaType, caps); } } return info; @@ -196,8 +210,12 @@ sp MediaCodecInfo::FromParcel(const Parcel &parcel) { status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const { mName.writeToParcel(parcel); mOwner.writeToParcel(parcel); - parcel->writeInt32(mIsEncoder); + parcel->writeInt32(mAttributes); parcel->writeUint32(mRank); + parcel->writeInt32(mAliases.size()); + for (const AString &alias : mAliases) { + alias.writeToParcel(parcel); + } parcel->writeInt32(mCaps.size()); for (size_t i = 0; i < mCaps.size(); i++) { mCaps.keyAt(i).writeToParcel(parcel); @@ -206,10 +224,10 @@ status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const { return OK; } -ssize_t MediaCodecInfo::getCapabilityIndex(const char *mime) const { - if (mime) { +ssize_t MediaCodecInfo::getCapabilityIndex(const char *mediaType) const { + if (mediaType) { for (size_t ix = 0; ix < mCaps.size(); ix++) { - if (mCaps.keyAt(ix).equalsIgnoreCase(mime)) { + if (mCaps.keyAt(ix).equalsIgnoreCase(mediaType)) { return ix; } } @@ -217,19 +235,26 @@ ssize_t MediaCodecInfo::getCapabilityIndex(const char *mime) const { return -1; } -MediaCodecInfo::MediaCodecInfo() : mRank(0x100) { +MediaCodecInfo::MediaCodecInfo() + : mAttributes((MediaCodecInfo::Attributes)0), + mRank(0x100) { } void MediaCodecInfoWriter::setName(const char* name) { mInfo->mName = name; } +void MediaCodecInfoWriter::addAlias(const char* name) { + mInfo->mAliases.add(name); +} + void MediaCodecInfoWriter::setOwner(const char* owner) { mInfo->mOwner = owner; } -void MediaCodecInfoWriter::setEncoder(bool isEncoder) { - mInfo->mIsEncoder = isEncoder; +void MediaCodecInfoWriter::setAttributes( + typename std::underlying_type::type attributes) { + mInfo->mAttributes = (MediaCodecInfo::Attributes)attributes; } void MediaCodecInfoWriter::setRank(uint32_t rank) { @@ -237,21 +262,21 @@ void MediaCodecInfoWriter::setRank(uint32_t rank) { } std::unique_ptr - MediaCodecInfoWriter::addMime(const char *mime) { - ssize_t ix = mInfo->getCapabilityIndex(mime); + MediaCodecInfoWriter::addMediaType(const char *mediaType) { + ssize_t ix = mInfo->getCapabilityIndex(mediaType); if (ix >= 0) { return std::unique_ptr( new MediaCodecInfo::CapabilitiesWriter( mInfo->mCaps.valueAt(ix).get())); } sp caps = new MediaCodecInfo::Capabilities(); - mInfo->mCaps.add(AString(mime), caps); + mInfo->mCaps.add(AString(mediaType), caps); return std::unique_ptr( new MediaCodecInfo::CapabilitiesWriter(caps.get())); } -bool MediaCodecInfoWriter::removeMime(const char *mime) { - ssize_t ix = mInfo->getCapabilityIndex(mime); +bool MediaCodecInfoWriter::removeMediaType(const char *mediaType) { + ssize_t ix = mInfo->getCapabilityIndex(mediaType); if (ix >= 0) { mInfo->mCaps.removeItemsAt(ix); return true; diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h index b3777d37c5..54f565a3f1 100644 --- a/media/libmedia/include/media/MediaCodecInfo.h +++ b/media/libmedia/include/media/MediaCodecInfo.h @@ -30,6 +30,8 @@ #include #include +#include + namespace android { struct AMessage; @@ -51,21 +53,47 @@ struct MediaCodecInfo : public RefBase { struct CapabilitiesWriter; - struct Capabilities : public RefBase { - enum { - // decoder flags - kFlagSupportsAdaptivePlayback = 1 << 0, - kFlagSupportsSecurePlayback = 1 << 1, - kFlagSupportsTunneledPlayback = 1 << 2, - - // encoder flags - kFlagSupportsIntraRefresh = 1 << 0, + enum Attributes : int32_t { + // attribute flags + kFlagIsEncoder = 1 << 0, + kFlagIsVendor = 1 << 1, + kFlagIsSoftwareOnly = 1 << 2, + kFlagIsHardwareAccelerated = 1 << 3, + }; - }; + struct Capabilities : public RefBase { + constexpr static char FEATURE_ADAPTIVE_PLAYBACK[] = "feature-adaptive-playback"; + constexpr static char FEATURE_DYNAMIC_TIMESTAMP[] = "feature-dynamic-timestamp"; + constexpr static char FEATURE_FRAME_PARSING[] = "feature-frame-parsing"; + constexpr static char FEATURE_INTRA_REFRESH[] = "feature-frame-parsing"; + constexpr static char FEATURE_MULTIPLE_FRAMES[] = "feature-multiple-frames"; + constexpr static char FEATURE_SECURE_PLAYBACK[] = "feature-secure-playback"; + constexpr static char FEATURE_TUNNELED_PLAYBACK[] = "feature-tunneled-playback"; + /** + * Returns the supported levels for each supported profile in a target array. + * + * @param profileLevels target array for the profile levels. + */ void getSupportedProfileLevels(Vector *profileLevels) const; + + /** + * Returns the supported color formats in a target array. Only used for video/image + * components. + * + * @param colorFormats target array for the color formats. + */ void getSupportedColorFormats(Vector *colorFormats) const; - uint32_t getFlags() const; + + /** + * Returns metadata associated with this codec capability. + * + * This contains: + * - features, + * - performance data. + * + * TODO: expose this as separate API-s and wrap here. + */ const sp getDetails() const; protected: @@ -73,7 +101,6 @@ struct MediaCodecInfo : public RefBase { SortedVector mProfileLevelsSorted; Vector mColorFormats; SortedVector mColorFormatsSorted; - uint32_t mFlags; sp mDetails; Capabilities(); @@ -93,8 +120,7 @@ struct MediaCodecInfo : public RefBase { /** * This class is used for modifying information inside a `Capabilities` * object. An object of type `CapabilitiesWriter` can be obtained by calling - * `MediaCodecInfoWriter::addMime()` or - * `MediaCodecInfoWriter::updateMime()`. + * `MediaCodecInfoWriter::addMediaType()`. */ struct CapabilitiesWriter { /** @@ -121,6 +147,13 @@ struct MediaCodecInfo : public RefBase { * @param value The `int32_t` value. */ void addDetail(const char* key, int32_t value); + /** + * Removes a key-value pair from the list of details. If the key is not + * present, this call does nothing. + * + * @param key The key. + */ + void removeDetail(const char* key); /** * Add a profile-level pair. If this profile-level pair already exists, * it will be ignored. @@ -136,13 +169,7 @@ struct MediaCodecInfo : public RefBase { * @param format The color format. */ void addColorFormat(uint32_t format); - /** - * Add flags. The underlying operation is bitwise-or. In other words, - * bits that have already been set will be ignored. - * - * @param flags The additional flags. - */ - void addFlags(uint32_t flags); + private: /** * The associated `Capabilities` object. @@ -158,11 +185,27 @@ struct MediaCodecInfo : public RefBase { friend MediaCodecInfoWriter; }; - bool isEncoder() const; - void getSupportedMimes(Vector *mimes) const; - const sp getCapabilitiesFor(const char *mime) const; + inline bool isEncoder() const { + return getAttributes() & kFlagIsEncoder; + } + + Attributes getAttributes() const; + void getSupportedMediaTypes(Vector *mediaTypes) const; + const sp getCapabilitiesFor(const char *mediaType) const; const char *getCodecName() const; + /** + * Returns a vector containing alternate names for the codec. + * + * \param aliases the destination array for the aliases. This is cleared. + * + * Multiple codecs may share alternate names as long as their supported media types are + * distinct; however, these will result in different aliases for the MediaCodec user as + * the canonical codec has to be resolved without knowing the media type in + * MediaCodec::CreateByComponentName. + */ + void getAliases(Vector *aliases) const; + /** * Return the name of the service that hosts the codec. This value is not * visible at the Java level. @@ -170,7 +213,14 @@ struct MediaCodecInfo : public RefBase { * Currently, this is the "instance name" of the IOmx service. */ const char *getOwnerName() const; - uint32_t rank() const; + + /** + * Returns the rank of the component. + * + * Technically this is defined to be per media type, but that makes ordering the MediaCodecList + * impossible as MediaCodecList is ordered by codec name. + */ + uint32_t getRank() const; /** * Serialization over Binder @@ -181,11 +231,12 @@ struct MediaCodecInfo : public RefBase { private: AString mName; AString mOwner; - bool mIsEncoder; + Attributes mAttributes; KeyedVector > mCaps; + Vector mAliases; uint32_t mRank; - ssize_t getCapabilityIndex(const char *mime) const; + ssize_t getCapabilityIndex(const char *mediaType) const; /** * Construct an `MediaCodecInfo` object. After the construction, its @@ -218,6 +269,13 @@ struct MediaCodecInfoWriter { * @param name The new name. */ void setName(const char* name); + /** + * Adds an alias (alternate name) for the codec. Multiple codecs can share an alternate name + * as long as their supported media types are distinct. + * + * @param name an alternate name. + */ + void addAlias(const char* name); /** * Set the owner name of the codec. * @@ -228,32 +286,32 @@ struct MediaCodecInfoWriter { */ void setOwner(const char* owner); /** - * Set whether this codec is an encoder or a decoder. + * Sets codec attributes. * - * @param isEncoder Whether this codec is an encoder or a decoder. + * @param attributes Codec attributes. */ - void setEncoder(bool isEncoder = true); + void setAttributes(typename std::underlying_type::type attributes); /** - * Add a mime to an indexed list and return a `CapabilitiesWriter` object + * Add a media type to an indexed list and return a `CapabilitiesWriter` object * that can be used for modifying the associated `Capabilities`. * - * If the mime already exists, this function will return the - * `CapabilitiesWriter` associated with the mime. + * If the media type already exists, this function will return the + * `CapabilitiesWriter` associated with the media type. * - * @param[in] mime The name of a new mime to add. + * @param[in] mediaType The name of a new media type to add. * @return writer The `CapabilitiesWriter` object for modifying the - * `Capabilities` associated with the mime. `writer` will be valid - * regardless of whether `mime` already exists or not. + * `Capabilities` associated with the media type. `writer` will be valid + * regardless of whether `mediaType` already exists or not. */ - std::unique_ptr addMime( - const char* mime); + std::unique_ptr addMediaType( + const char* mediaType); /** - * Remove a mime. + * Remove a media type. * - * @param mime The name of the mime to remove. - * @return `true` if `mime` is removed; `false` if `mime` is not found. + * @param mediaType The name of the media type to remove. + * @return `true` if `mediaType` is removed; `false` if `mediaType` is not found. */ - bool removeMime(const char* mime); + bool removeMediaType(const char* mediaType); /** * Set rank of the codec. MediaCodecList will stable-sort the list according * to rank in non-descending order. diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index dadfe285bc..a1a2660fb6 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -8682,14 +8682,17 @@ status_t ACodec::queryCapabilities( if (omxNode->configureVideoTunnelMode( kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) { // tunneled playback includes adaptive playback - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback - | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback); - } else if (omxNode->setPortMode( - kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK || - omxNode->prepareForAdaptivePlayback( - kPortIndexOutput, OMX_TRUE, - 1280 /* width */, 720 /* height */) == OK) { - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback); + } else { + // tunneled playback is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK); + if (omxNode->setPortMode( + kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) != OK && + omxNode->prepareForAdaptivePlayback( + kPortIndexOutput, OMX_TRUE, + 1280 /* width */, 720 /* height */) != OK) { + // adaptive playback is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK); + } } } @@ -8697,11 +8700,20 @@ status_t ACodec::queryCapabilities( OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params; InitOMXParams(¶ms); params.nPortIndex = kPortIndexOutput; - // TODO: should we verify if fallback is supported? + + OMX_VIDEO_PARAM_INTRAREFRESHTYPE fallbackParams; + InitOMXParams(&fallbackParams); + fallbackParams.nPortIndex = kPortIndexOutput; + fallbackParams.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic; + if (omxNode->getConfig( (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh, - ¶ms, sizeof(params)) == OK) { - caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh); + ¶ms, sizeof(params)) != OK && + omxNode->getParameter( + OMX_IndexParamVideoIntraRefresh, &fallbackParams, + sizeof(fallbackParams)) != OK) { + // intra refresh is not supported + caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH); } } diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 7816fae36c..25478887ce 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -911,10 +911,10 @@ status_t MediaCodec::init(const AString &name) { continue; } mCodecInfo = mcl->getCodecInfo(codecIdx); - Vector mimes; - mCodecInfo->getSupportedMimes(&mimes); - for (size_t i = 0; i < mimes.size(); i++) { - if (mimes[i].startsWith("video/")) { + Vector mediaTypes; + mCodecInfo->getSupportedMediaTypes(&mediaTypes); + for (size_t i = 0; i < mediaTypes.size(); i++) { + if (mediaTypes[i].startsWith("video/")) { mIsVideo = true; break; } diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index eaff283cee..93478e9d60 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -215,13 +215,9 @@ MediaCodecList::MediaCodecList(std::vector builders) mCodecInfos.begin(), mCodecInfos.end(), [](const sp &info1, const sp &info2) { - if (info2 == nullptr) { - return false; - } else if (info1 == nullptr) { - return true; - } else { - return info1->rank() < info2->rank(); - } + // null is lowest + return info1 == nullptr + || (info2 != nullptr && info1->getRank() < info2->getRank()); }); } diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp index cac53f4c3e..dd7c3e67eb 100644 --- a/media/libstagefright/MediaCodecListOverrides.cpp +++ b/media/libstagefright/MediaCodecListOverrides.cpp @@ -228,18 +228,18 @@ void profileCodecs( continue; } - Vector mimes; - info->getSupportedMimes(&mimes); - for (size_t i = 0; i < mimes.size(); ++i) { + Vector mediaTypes; + info->getSupportedMediaTypes(&mediaTypes); + for (size_t i = 0; i < mediaTypes.size(); ++i) { const sp &caps = - info->getCapabilitiesFor(mimes[i].c_str()); + info->getCapabilitiesFor(mediaTypes[i].c_str()); if (!forceToMeasure && (caps->getDetails()->contains("max-supported-instances") || caps->getDetails()->contains("max-concurrent-instances"))) { continue; } - size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps); + size_t max = doProfileCodecs(info->isEncoder(), name, mediaTypes[i], caps); if (max > 0) { CodecSettings settings; char maxStr[32]; @@ -248,7 +248,7 @@ void profileCodecs( AString key = name; key.append(" "); - key.append(mimes[i]); + key.append(mediaTypes[i]); if (info->isEncoder()) { encoder_results->add(key, settings); diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp index 96b896ba9b..382c9476ea 100644 --- a/media/libstagefright/OmxInfoBuilder.cpp +++ b/media/libstagefright/OmxInfoBuilder.cpp @@ -57,14 +57,9 @@ bool hasPrefix(const hidl_string& s, const char* prefix) { } status_t queryCapabilities( - const IOmxStore::NodeInfo& node, const char* mime, bool isEncoder, + const IOmxStore::NodeInfo& node, const char* mediaType, bool isEncoder, MediaCodecInfo::CapabilitiesWriter* caps) { sp codec = new ACodec(); - status_t err = codec->queryCapabilities( - node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps); - if (err != OK) { - return err; - } for (const auto& attribute : node.attributes) { // All features have an int32 value except // "feature-bitrate-modes", which has a string value. @@ -81,6 +76,12 @@ status_t queryCapabilities( attribute.key.c_str(), attribute.value.c_str()); } } + // query capabilities may remove capabilities that are not actually supported by the codec + status_t err = codec->queryCapabilities( + node.owner.c_str(), node.name.c_str(), mediaType, isEncoder, caps); + if (err != OK) { + return err; + } return OK; } @@ -163,7 +164,10 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { info = c2i->second.get(); info->setName(nodeName.c_str()); info->setOwner(node.owner.c_str()); - info->setEncoder(isEncoder); + info->setAttributes( + // all OMX codecs are vendor codecs (in the vendor partition), but + // treat OMX.google codecs as non-hardware-accelerated and non-vendor + (isEncoder ? MediaCodecInfo::kFlagIsEncoder : 0)); info->setRank(defaultRank); } else { // The node has been seen before. Simply retrieve the @@ -180,7 +184,19 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { info = c2i->second.get(); info->setName(nodeName.c_str()); info->setOwner(node.owner.c_str()); - info->setEncoder(isEncoder); + typename std::underlying_type::type attrs = + MediaCodecInfo::kFlagIsVendor; + if (isEncoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; + } + if (std::count_if( + node.attributes.begin(), node.attributes.end(), + [](const IOmxStore::Attribute &i) -> bool { + return i.key == "attribute::software-codec"; + })) { + attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } + info->setAttributes(attrs); info->setRank(defaultRank); } else { // If preferPlatformNodes is true, this node must be @@ -195,12 +211,12 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { } } std::unique_ptr caps = - info->addMime(typeName.c_str()); + info->addMediaType(typeName.c_str()); if (queryCapabilities( node, typeName.c_str(), isEncoder, caps.get()) != OK) { - ALOGW("Fail to add mime %s to codec %s", + ALOGW("Fail to add media type %s to codec %s", typeName.c_str(), nodeName.c_str()); - info->removeMime(typeName.c_str()); + info->removeMediaType(typeName.c_str()); } } @@ -219,7 +235,18 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { info = c2i->second.get(); info->setName(nodeName.c_str()); info->setOwner(node->owner.c_str()); - info->setEncoder(isEncoder); + typename std::underlying_type::type attrs = + MediaCodecInfo::kFlagIsVendor; + if (isEncoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; + } + if (std::count_if( + node->attributes.begin(), node->attributes.end(), + [](const IOmxStore::Attribute &i) -> bool { + return i.key == "attribute::software-codec"; + })) { + attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } info->setRank(defaultRank); } else { // The node has been seen before. Simply retrieve the @@ -227,13 +254,13 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { info = c2i->second.get(); } std::unique_ptr caps = - info->addMime(typeName.c_str()); + info->addMediaType(typeName.c_str()); if (queryCapabilities( *node, typeName.c_str(), isEncoder, caps.get()) != OK) { - ALOGW("Fail to add mime %s to codec %s " + ALOGW("Fail to add media type %s to codec %s " "after software codecs", typeName.c_str(), nodeName.c_str()); - info->removeMime(typeName.c_str()); + info->removeMediaType(typeName.c_str()); } } } diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp index 4e2d398118..121bb1a7be 100644 --- a/media/libstagefright/omx/1.0/Omx.cpp +++ b/media/libstagefright/omx/1.0/Omx.cpp @@ -124,11 +124,11 @@ Return Omx::allocateNode( } else { uint32_t quirks = 0; for (const auto& quirk : codec->second.quirkSet) { - if (quirk == "requires-allocate-on-input-ports") { + if (quirk == "quirk::requires-allocate-on-input-ports") { quirks |= OMXNodeInstance:: kRequiresAllocateBufferOnInputPorts; } - if (quirk == "requires-allocate-on-output-ports") { + if (quirk == "quirk::requires-allocate-on-output-ports") { quirks |= OMXNodeInstance:: kRequiresAllocateBufferOnOutputPorts; } diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp index 2dec9faa08..6e541ba4f0 100644 --- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp +++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp @@ -26,8 +26,9 @@ #include #include -#include #include +#include +#include namespace android { @@ -326,8 +327,8 @@ void MediaCodecsXmlParser::startElementHandler( case SECTION_DECODER: case SECTION_ENCODER: { - if (strEq(name, "Quirk")) { - (void)addQuirk(attrs); + if (strEq(name, "Quirk") || strEq(name, "Attribute")) { + (void)addQuirk(attrs, name); } else if (strEq(name, "Type")) { (void)addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER)); @@ -348,6 +349,8 @@ void MediaCodecsXmlParser::startElementHandler( if (outside && (strEq(name, "Limit") || strEq(name, "Feature"))) { ALOGW("ignoring %s specified outside of a Type", name); + } else if (strEq(name, "Alias")) { + (void)addAlias(attrs); } else if (strEq(name, "Limit")) { (void)addLimit(attrs); } else if (strEq(name, "Feature")) { @@ -579,7 +582,7 @@ status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( return OK; } -status_t MediaCodecsXmlParser::addQuirk(const char **attrs) { +status_t MediaCodecsXmlParser::addQuirk(const char **attrs, const char *tag) { if (mCurrentCodec == mCodecMap.end()) { return BAD_VALUE; } @@ -606,7 +609,12 @@ status_t MediaCodecsXmlParser::addQuirk(const char **attrs) { return BAD_VALUE; } - mCurrentCodec->second.quirkSet.emplace(name); + std::string tagString = tag; + std::transform(tagString.begin(), tagString.end(), tagString.begin(), ::tolower); + tagString.append("::"); + tagString.append(name); + mCurrentCodec->second.quirkSet.emplace(tagString.c_str()); + ALOGI("adding %s to %s", tagString.c_str(), mCurrentCodec->first.c_str()); return OK; } @@ -760,6 +768,7 @@ status_t MediaCodecsXmlParser::addLimit(const char **attrs) { strEq(a_name, "quality") || strEq(a_name, "size") || strEq(a_name, "measured-blocks-per-second") || + strHasPrefix(a_name, "performance-point-") || strHasPrefix(a_name, "measured-frame-rate-")) { // "range" is specified in exactly one of the following forms: // 1) min-max @@ -964,6 +973,34 @@ status_t MediaCodecsXmlParser::addFeature(const char **attrs) { return OK; } +status_t MediaCodecsXmlParser::addAlias(const char **attrs) { + size_t i = 0; + const char *name = nullptr; + + while (attrs[i] != nullptr) { + if (strEq(attrs[i], "name")) { + if (attrs[++i] == nullptr) { + ALOGE("addAlias: name is null"); + return BAD_VALUE; + } + name = attrs[i]; + } else { + ALOGE("addAlias: unrecognized attribute: %s", attrs[i]); + return BAD_VALUE; + } + ++i; + } + + // Every feature must have a name. + if (name == nullptr) { + ALOGE("alias with no 'name' attribute"); + return BAD_VALUE; + } + + mCurrentCodec->second.aliases.emplace_back(name); + return OK; +} + const MediaCodecsXmlParser::AttributeMap& MediaCodecsXmlParser::getServiceAttributeMap() const { return mServiceAttributeMap; @@ -1041,11 +1078,18 @@ void MediaCodecsXmlParser::generateRoleMap() const { NodeInfo nodeInfo; nodeInfo.name = codecName; + // NOTE: no aliases are exposed in role info + // attribute quirks are exposed as node attributes nodeInfo.attributeList.reserve(typeAttributeMap.size()); for (const auto& attribute : typeAttributeMap) { nodeInfo.attributeList.push_back( Attribute{attribute.first, attribute.second}); } + for (const std::string &quirk : codec.second.quirkSet) { + if (strHasPrefix(quirk.c_str(), "attribute::")) { + nodeInfo.attributeList.push_back(Attribute{quirk, "present"}); + } + } nodeList->insert(std::make_pair( std::move(order), std::move(nodeInfo))); } diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h index cc69e5276b..fd949da3c0 100644 --- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h +++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h @@ -65,6 +65,7 @@ public: size_t order; ///< Order of appearance in the file (starting from 0) QuirkSet quirkSet; ///< Set of quirks requested by this codec TypeMap typeMap; ///< Map of types supported by this codec + std::vector aliases; ///< Name aliases for this codec }; typedef std::pair Codec; @@ -76,6 +77,7 @@ public: struct NodeInfo { std::string name; std::vector attributeList; + // note: aliases are not exposed here as they are not part of the role map }; /** @@ -171,8 +173,9 @@ private: void addMediaCodec(bool encoder, const char *name, const char *type = nullptr); - status_t addQuirk(const char **attrs); + status_t addQuirk(const char **attrs, const char *tag); status_t addTypeFromAttributes(const char **attrs, bool encoder); + status_t addAlias(const char **attrs); status_t addLimit(const char **attrs); status_t addFeature(const char **attrs); void addType(const char *name); -- GitLab From 60d429c4236cb2e8ae66253d592d97276bb16fbc Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 10 May 2018 14:08:19 -0700 Subject: [PATCH 0731/1530] cmd/stagefright: print more debug info for codecs Replace -p (profiles) with -i (info) Bug: 119631295 Change-Id: I37f208aad9c9d4698d77e30d9278148134e94372 --- cmds/stagefright/stagefright.cpp | 161 +++++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 42 deletions(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 34a9a40122..bf36be09e3 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -628,7 +629,7 @@ static void usage(const char *me) { fprintf(stderr, " -l(ist) components\n"); fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n"); fprintf(stderr, " -b bug to reproduce\n"); - fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n"); + fprintf(stderr, " -i(nfo) dump codec info (profiles and color formats supported, details)\n"); fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n"); fprintf(stderr, " -s(oftware) prefer software codec\n"); fprintf(stderr, " -r(hardware) force to use hardware codec\n"); @@ -646,55 +647,131 @@ static void usage(const char *me) { fprintf(stderr, " -v be more verbose\n"); } -static void dumpCodecProfiles(bool queryDecoders) { - const char *kMimeTypes[] = { - MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, - MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC, - MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB, - MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, - MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, - MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9, - MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_VIDEO_HEVC, - MEDIA_MIMETYPE_AUDIO_EAC3, MEDIA_MIMETYPE_AUDIO_AC4, - MEDIA_MIMETYPE_VIDEO_AV1 - }; - - const char *codecType = queryDecoders? "decoder" : "encoder"; - printf("%s profiles:\n", codecType); +static void dumpCodecDetails(bool queryDecoders) { + const char *codecType = queryDecoders? "Decoder" : "Encoder"; + printf("\n%s infos by media types:\n" + "=============================\n", codecType); sp list = MediaCodecList::getInstance(); size_t numCodecs = list->countCodecs(); - for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) { - printf("type '%s':\n", kMimeTypes[k]); - - for (size_t index = 0; index < numCodecs; ++index) { - sp info = list->getCodecInfo(index); - if (info == NULL || info->isEncoder() != !queryDecoders) { - continue; + // gather all media types supported by codec class, and link to codecs that support them + KeyedVector>> allMediaTypes; + for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) { + sp info = list->getCodecInfo(codec_ix); + if (info->isEncoder() == !queryDecoders) { + Vector supportedMediaTypes; + info->getSupportedMediaTypes(&supportedMediaTypes); + if (!supportedMediaTypes.size()) { + printf("warning: %s does not support any media types\n", + info->getCodecName()); + } else { + for (const AString &mediaType : supportedMediaTypes) { + if (allMediaTypes.indexOfKey(mediaType) < 0) { + allMediaTypes.add(mediaType, Vector>()); + } + allMediaTypes.editValueFor(mediaType).add(info); + } } - sp caps = info->getCapabilitiesFor(kMimeTypes[k]); + } + } + + KeyedVector visitedCodecs; + for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) { + const AString &mediaType = allMediaTypes.keyAt(type_ix); + printf("\nMedia type '%s':\n", mediaType.c_str()); + + for (const sp &info : allMediaTypes.valueAt(type_ix)) { + sp caps = info->getCapabilitiesFor(mediaType.c_str()); if (caps == NULL) { + printf("warning: %s does not have capabilities for type %s\n", + info->getCodecName(), mediaType.c_str()); continue; } - printf(" %s '%s' supports ", + printf(" %s \"%s\" supports\n", codecType, info->getCodecName()); - Vector profileLevels; - caps->getSupportedProfileLevels(&profileLevels); - if (profileLevels.size() == 0) { - printf("NOTHING.\n"); - continue; + auto printList = [](const char *type, const Vector &values){ + printf(" %s: [", type); + for (size_t j = 0; j < values.size(); ++j) { + printf("\n %s%s", values[j].c_str(), + j == values.size() - 1 ? " " : ","); + } + printf("]\n"); + }; + + if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) { + visitedCodecs.add(info->getCodecName(), true); + { + Vector aliases; + info->getAliases(&aliases); + // quote alias + for (AString &alias : aliases) { + alias.insert("\"", 1, 0); + alias.append('"'); + } + printList("aliases", aliases); + } + { + uint32_t attrs = info->getAttributes(); + Vector list; + list.add(AStringPrintf("encoder: %d", !!(attrs & MediaCodecInfo::kFlagIsEncoder))); + list.add(AStringPrintf("vendor: %d", !!(attrs & MediaCodecInfo::kFlagIsVendor))); + list.add(AStringPrintf("software-only: %d", !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly))); + list.add(AStringPrintf("hw-accelerated: %d", !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated))); + printList(AStringPrintf("attributes: %#x", attrs).c_str(), list); + } + + printf(" owner: \"%s\"\n", info->getOwnerName()); + printf(" rank: %u\n", info->getRank()); + } else { + printf(" aliases, attributes, owner, rank: see above\n"); } - for (size_t j = 0; j < profileLevels.size(); ++j) { - const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j]; + { + Vector list; + Vector profileLevels; + caps->getSupportedProfileLevels(&profileLevels); + for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) { + const char *niceProfile = + mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC) ? asString_AACObject(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Profile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Profile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Profile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCProfile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Profile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCProfile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Profile(pl.mProfile) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Profile(pl.mProfile) :"??"; + const char *niceLevel = + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Level(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Level(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Level(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCLevel(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Level(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCTierLevel(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Level(pl.mLevel) : + mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Level(pl.mLevel) : + "??"; + + list.add(AStringPrintf("% 5u/% 5u (%s/%s)", + pl.mProfile, pl.mLevel, niceProfile, niceLevel)); + } + printList("profile/levels", list); + } - printf("%s%u/%u", j > 0 ? ", " : "", - profileLevel.mProfile, profileLevel.mLevel); + { + Vector list; + Vector colors; + caps->getSupportedColorFormats(&colors); + for (uint32_t color : colors) { + list.add(AStringPrintf("%#x (%s)", color, + asString_ColorFormat((int32_t)color))); + } + printList("colors", list); } - printf("\n"); + printf(" details: %s\n", caps->getDetails()->debugString(6).c_str()); } } } @@ -704,7 +781,7 @@ int main(int argc, char **argv) { bool audioOnly = false; bool listComponents = false; - bool dumpProfiles = false; + bool dumpCodecInfo = false; bool extractThumbnail = false; bool seekTest = false; bool useSurfaceAlloc = false; @@ -724,7 +801,7 @@ int main(int argc, char **argv) { sp looper; int res; - while ((res = getopt(argc, argv, "vhaqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) { + while ((res = getopt(argc, argv, "vhaqn:lm:b:itsrow:kN:xSTd:D:")) >= 0) { switch (res) { case 'a': { @@ -794,9 +871,9 @@ int main(int argc, char **argv) { break; } - case 'p': + case 'i': { - dumpProfiles = true; + dumpCodecInfo = true; break; } @@ -937,9 +1014,9 @@ int main(int argc, char **argv) { return 0; } - if (dumpProfiles) { - dumpCodecProfiles(true /* queryDecoders */); - dumpCodecProfiles(false /* queryDecoders */); + if (dumpCodecInfo) { + dumpCodecDetails(true /* queryDecoders */); + dumpCodecDetails(false /* queryDecoders */); } if (listComponents) { -- GitLab From a8d5b4f6edc15f490db22135624828319e3d7a5d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 4 Dec 2018 17:05:29 -0800 Subject: [PATCH 0732/1530] hdr10+: profile level report bug: 118507186 test: test with locally modified cts to use hdr10+ profile VP9Profile2HDR10Plus, it should be supported Change-Id: Iabc90a09e7fb5ba44746b29739f94a55c9708a94 --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 37 +++++++++++ media/codec2/sfplugin/utils/Codec2Mapper.cpp | 66 ++++++++++++++++++-- media/codec2/sfplugin/utils/Codec2Mapper.h | 3 + 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 5d0ccd2039..184f215fca 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -450,6 +450,23 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { asString(err), asString(profileQuery[0].status)); if (err == C2_OK && profileQuery[0].status == C2_OK) { if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) { + std::vector> paramDescs; + c2_status_t err1 = intf->querySupportedParams(¶mDescs); + bool isHdr = false, isHdr10Plus = false; + if (err1 == C2_OK) { + for (const std::shared_ptr &desc : paramDescs) { + if ((uint32_t)desc->index() == + C2StreamHdr10PlusInfo::output::PARAM_TYPE) { + isHdr10Plus = true; + } else if ((uint32_t)desc->index() == + C2StreamHdrStaticInfo::output::PARAM_TYPE) { + isHdr = true; + } + } + } + // For VP9, the static info is always propagated by framework. + isHdr |= (mediaType == MIMETYPE_VIDEO_VP9); + for (C2Value::Primitive profile : profileQuery[0].values.values) { pl.profile = (C2Config::profile_t)profile.ref(); std::vector> failures; @@ -473,6 +490,26 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { caps->addProfileLevel( (uint32_t)sdkProfile, (uint32_t)sdkLevel); gotProfileLevels = true; + if (isHdr) { + auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper( + trait.mediaType); + if (hdrMapper && hdrMapper->mapProfile( + pl.profile, &sdkProfile)) { + caps->addProfileLevel( + (uint32_t)sdkProfile, + (uint32_t)sdkLevel); + } + if (isHdr10Plus) { + hdrMapper = C2Mapper::GetHdrProfileLevelMapper( + trait.mediaType, true /*isHdr10Plus*/); + if (hdrMapper && hdrMapper->mapProfile( + pl.profile, &sdkProfile)) { + caps->addProfileLevel( + (uint32_t)sdkProfile, + (uint32_t)sdkLevel); + } + } + } } else if (!mapper) { caps->addProfileLevel(pl.profile, pl.level); gotProfileLevels = true; diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index b1b33e1eb4..c369e16b5e 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -253,6 +253,14 @@ ALookup sHevcProfiles = { { C2Config::PROFILE_HEVC_MAIN_10_INTRA, HEVCProfileMain10 }, }; +ALookup sHevcHdrProfiles = { + { C2Config::PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10 }, +}; + +ALookup sHevcHdr10PlusProfiles = { + { C2Config::PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10Plus }, +}; + ALookup sMpeg2Levels = { { C2Config::LEVEL_MP2V_LOW, MPEG2LevelLL }, { C2Config::LEVEL_MP2V_MAIN, MPEG2LevelML }, @@ -324,6 +332,20 @@ ALookup sVp9Profiles = { { C2Config::PROFILE_VP9_1, VP9Profile1 }, { C2Config::PROFILE_VP9_2, VP9Profile2 }, { C2Config::PROFILE_VP9_3, VP9Profile3 }, + { C2Config::PROFILE_VP9_2, VP9Profile2HDR }, + { C2Config::PROFILE_VP9_3, VP9Profile3HDR }, + { C2Config::PROFILE_VP9_2, VP9Profile2HDR10Plus }, + { C2Config::PROFILE_VP9_3, VP9Profile3HDR10Plus }, +}; + +ALookup sVp9HdrProfiles = { + { C2Config::PROFILE_VP9_2, VP9Profile2HDR }, + { C2Config::PROFILE_VP9_3, VP9Profile3HDR }, +}; + +ALookup sVp9Hdr10PlusProfiles = { + { C2Config::PROFILE_VP9_2, VP9Profile2HDR10Plus }, + { C2Config::PROFILE_VP9_3, VP9Profile3HDR10Plus }, }; ALookup sAv1Levels = { @@ -461,6 +483,10 @@ struct H263ProfileLevelMapper : ProfileLevelMapperHelper { }; struct HevcProfileLevelMapper : ProfileLevelMapperHelper { + HevcProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false) : + ProfileLevelMapperHelper(), + mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus) {} + virtual bool simpleMap(C2Config::level_t from, int32_t *to) { return sHevcLevels.map(from, to); } @@ -468,11 +494,19 @@ struct HevcProfileLevelMapper : ProfileLevelMapperHelper { return sHevcLevels.map(from, to); } virtual bool simpleMap(C2Config::profile_t from, int32_t *to) { - return sHevcProfiles.map(from, to); + return mIsHdr10Plus ? sHevcHdr10PlusProfiles.map(from, to) : + mIsHdr ? sHevcHdrProfiles.map(from, to) : + sHevcProfiles.map(from, to); } virtual bool simpleMap(int32_t from, C2Config::profile_t *to) { - return sHevcProfiles.map(from, to); + return mIsHdr10Plus ? sHevcHdr10PlusProfiles.map(from, to) : + mIsHdr ? sHevcHdrProfiles.map(from, to) : + sHevcProfiles.map(from, to); } + +private: + bool mIsHdr; + bool mIsHdr10Plus; }; struct Mpeg2ProfileLevelMapper : ProfileLevelMapperHelper { @@ -527,6 +561,10 @@ struct Vp8ProfileLevelMapper : ProfileLevelMapperHelper { }; struct Vp9ProfileLevelMapper : ProfileLevelMapperHelper { + Vp9ProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false) : + ProfileLevelMapperHelper(), + mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus) {} + virtual bool simpleMap(C2Config::level_t from, int32_t *to) { return sVp9Levels.map(from, to); } @@ -534,11 +572,19 @@ struct Vp9ProfileLevelMapper : ProfileLevelMapperHelper { return sVp9Levels.map(from, to); } virtual bool simpleMap(C2Config::profile_t from, int32_t *to) { - return sVp9Profiles.map(from, to); + return mIsHdr10Plus ? sVp9Hdr10PlusProfiles.map(from, to) : + mIsHdr ? sVp9HdrProfiles.map(from, to) : + sVp9Profiles.map(from, to); } virtual bool simpleMap(int32_t from, C2Config::profile_t *to) { - return sVp9Profiles.map(from, to); + return mIsHdr10Plus ? sVp9Hdr10PlusProfiles.map(from, to) : + mIsHdr ? sVp9HdrProfiles.map(from, to) : + sVp9Profiles.map(from, to); } + +private: + bool mIsHdr; + bool mIsHdr10Plus; }; } // namespace @@ -569,6 +615,18 @@ C2Mapper::GetProfileLevelMapper(std::string mediaType) { return nullptr; } +// static +std::shared_ptr +C2Mapper::GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus) { + std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower); + if (mediaType == MIMETYPE_VIDEO_HEVC) { + return std::make_shared(true, isHdr10Plus); + } else if (mediaType == MIMETYPE_VIDEO_VP9) { + return std::make_shared(true, isHdr10Plus); + } + return nullptr; +} + // static bool C2Mapper::map(C2Config::bitrate_mode_t from, int32_t *to) { return sBitrateModes.map(from, to); diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.h b/media/codec2/sfplugin/utils/Codec2Mapper.h index 1eeb92e805..cec6f07723 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.h +++ b/media/codec2/sfplugin/utils/Codec2Mapper.h @@ -40,6 +40,9 @@ namespace android { static std::shared_ptr GetProfileLevelMapper(std::string mediaType); + static std::shared_ptr + GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus = false); + // convert between bitrates static bool map(C2Config::bitrate_mode_t, int32_t*); static bool map(int32_t, C2Config::bitrate_mode_t*); -- GitLab From ae4b6ecfbcbea56cca160715a2ef710d8d87a40f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 15 Jan 2019 18:34:38 -0800 Subject: [PATCH 0733/1530] add new audio sources for audio capture This commit adds the following audio capture sources: - AUDIO_SOURCE_VOICE_PERFORMANCE: for real time live performances like Karaoke. - AUDIO_SOURCE_ECHO_REFERENCE: for capturing the reference signal to suppress by an echo canceller. Protected by privileged permission CAPTURE_AUDIO_OUTPUT. Also added device AUDIO_DEVICE_IN_ECHO_REFERENCE selected when the requested capture source is AUDIO_SOURCE_ECHO_REFERENCE. Bug: 118203066 Test: CTS tests for audio capture Change-Id: Ieb159ea82a7b81acf762506a44e24ec80111609f --- media/libaaudio/include/aaudio/AAudio.h | 7 +++++++ .../src/core/AAudioStreamParameters.cpp | 1 + .../libaaudio/src/utility/AAudioUtilities.cpp | 1 + media/libaaudio/tests/test_attributes.cpp | 1 + media/libeffects/config/src/EffectsConfig.cpp | 1 + media/libmedia/TypeConverter.cpp | 4 ++++ services/audioflinger/Threads.cpp | 2 ++ services/audiopolicy/common/include/policy.h | 13 +++++++++++-- .../engineconfigurable/src/InputSource.cpp | 3 ++- .../audiopolicy/enginedefault/src/Engine.cpp | 19 ++++++++++++++++++- .../service/AudioPolicyEffects.cpp | 8 +++++--- .../service/AudioPolicyInterfaceImpl.cpp | 10 +++++++--- 12 files changed, 60 insertions(+), 10 deletions(-) diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 1493b261e2..8e36c77a3d 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -373,6 +373,13 @@ enum { * so the recorded volume may be very low. */ AAUDIO_INPUT_PRESET_UNPROCESSED = 9, + + /** + * Use this preset for capturing audio meant to be processed in real time + * and played back for live performance (e.g karaoke). + * The capture path will minimize latency and coupling with playback path. + */ + AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE = 10, }; typedef int32_t aaudio_input_preset_t; diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp index bd42697aca..88da53a3e8 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.cpp +++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp @@ -158,6 +158,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION: case AAUDIO_INPUT_PRESET_VOICE_RECOGNITION: case AAUDIO_INPUT_PRESET_UNPROCESSED: + case AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE: break; // valid default: ALOGE("input preset not valid = %d", mInputPreset); diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp index f5b3ad4fc1..723cbf11f7 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.cpp +++ b/media/libaaudio/src/utility/AAudioUtilities.cpp @@ -210,6 +210,7 @@ audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t pres STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_RECOGNITION == AUDIO_SOURCE_VOICE_RECOGNITION); STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION == AUDIO_SOURCE_VOICE_COMMUNICATION); STATIC_ASSERT(AAUDIO_INPUT_PRESET_UNPROCESSED == AUDIO_SOURCE_UNPROCESSED); + STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE == AUDIO_SOURCE_VOICE_PERFORMANCE); if (preset == AAUDIO_UNSPECIFIED) { preset = AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; } diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp index b01af25e3d..dbf87122a9 100644 --- a/media/libaaudio/tests/test_attributes.cpp +++ b/media/libaaudio/tests/test_attributes.cpp @@ -130,6 +130,7 @@ static const aaudio_input_preset_t sInputPresets[] = { AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION, AAUDIO_INPUT_PRESET_UNPROCESSED, + AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE, }; static void checkAttributesUsage(aaudio_performance_mode_t perfMode) { diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp index 76b4adc201..f39eb0c7ac 100644 --- a/media/libeffects/config/src/EffectsConfig.cpp +++ b/media/libeffects/config/src/EffectsConfig.cpp @@ -115,6 +115,7 @@ constexpr std::pair STREAM_NAME_MAP {AUDIO_SOURCE_VOICE_RECOGNITION, "voice_recognition"}, {AUDIO_SOURCE_VOICE_COMMUNICATION, "voice_communication"}, {AUDIO_SOURCE_UNPROCESSED, "unprocessed"}, + {AUDIO_SOURCE_VOICE_PERFORMANCE, "voice_performance"}, }; /** Find the stream type enum corresponding to the stream type name or return false */ diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index b5a7172e64..0ab0e9b7c0 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -93,6 +93,8 @@ const InputDeviceConverter::Table InputDeviceConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUS), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_PROXY), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_HEADSET), + MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_BLE), + MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ECHO_REFERENCE), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DEFAULT), // STUB must be after DEFAULT, so the latter is picked up by toString first. MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_STUB), @@ -357,6 +359,8 @@ const SourceTypeConverter::Table SourceTypeConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_COMMUNICATION), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_REMOTE_SUBMIX), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_UNPROCESSED), + MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_VOICE_PERFORMANCE), + MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_ECHO_REFERENCE), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_FM_TUNER), MAKE_STRING_FROM_ENUM(AUDIO_SOURCE_HOTWORD), TERMINATOR diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 989e6eb8f1..607d2d110c 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -494,6 +494,8 @@ const char *sourceToString(audio_source_t source) case AUDIO_SOURCE_VOICE_COMMUNICATION: return "voice communication"; case AUDIO_SOURCE_REMOTE_SUBMIX: return "remote submix"; case AUDIO_SOURCE_UNPROCESSED: return "unprocessed"; + case AUDIO_SOURCE_VOICE_PERFORMANCE: return "voice performance"; + case AUDIO_SOURCE_ECHO_REFERENCE: return "echo reference"; case AUDIO_SOURCE_FM_TUNER: return "FM tuner"; case AUDIO_SOURCE_HOTWORD: return "hotword"; default: return "unknown"; diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 30b0044440..46a2a40453 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -83,7 +83,10 @@ static inline bool device_distinguishes_on_address(audio_devices_t device) * @param[in] inputSource to consider. Valid sources are: * - AUDIO_SOURCE_VOICE_COMMUNICATION * - AUDIO_SOURCE_CAMCORDER + * - AUDIO_SOURCE_VOICE_PERFORMANCE + * - AUDIO_SOURCE_UNPROCESSED * - AUDIO_SOURCE_MIC + * - AUDIO_SOURCE_ECHO_REFERENCE * - AUDIO_SOURCE_FM_TUNER * - AUDIO_SOURCE_VOICE_RECOGNITION * - AUDIO_SOURCE_HOTWORD @@ -96,10 +99,16 @@ static inline int32_t source_priority(audio_source_t inputSource) { switch (inputSource) { case AUDIO_SOURCE_VOICE_COMMUNICATION: - return 6; + return 9; case AUDIO_SOURCE_CAMCORDER: - return 5; + return 8; + case AUDIO_SOURCE_VOICE_PERFORMANCE: + return 7; + case AUDIO_SOURCE_UNPROCESSED: + return 6; case AUDIO_SOURCE_MIC: + return 5; + case AUDIO_SOURCE_ECHO_REFERENCE: return 4; case AUDIO_SOURCE_FM_TUNER: return 3; diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.cpp b/services/audiopolicy/engineconfigurable/src/InputSource.cpp index b9a38d44f4..d252d3ff76 100644 --- a/services/audiopolicy/engineconfigurable/src/InputSource.cpp +++ b/services/audiopolicy/engineconfigurable/src/InputSource.cpp @@ -25,7 +25,8 @@ namespace audio_policy { status_t Element::setIdentifier(audio_source_t identifier) { - if (identifier > AUDIO_SOURCE_MAX && identifier != AUDIO_SOURCE_HOTWORD) { + if (identifier > AUDIO_SOURCE_MAX && identifier != AUDIO_SOURCE_HOTWORD + && identifier != AUDIO_SOURCE_FM_TUNER && identifier != AUDIO_SOURCE_ECHO_REFERENCE) { return BAD_VALUE; } mIdentifier = identifier; diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 69395f3e55..0ef6f527f2 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -631,6 +631,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons case AUDIO_SOURCE_UNPROCESSED: case AUDIO_SOURCE_HOTWORD: case AUDIO_SOURCE_CAMCORDER: + case AUDIO_SOURCE_VOICE_PERFORMANCE: inputSource = AUDIO_SOURCE_VOICE_COMMUNICATION; break; default: @@ -737,16 +738,32 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons device = AUDIO_DEVICE_IN_VOICE_CALL; } break; + case AUDIO_SOURCE_VOICE_PERFORMANCE: + if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) { + device = AUDIO_DEVICE_IN_USB_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; case AUDIO_SOURCE_REMOTE_SUBMIX: if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; } break; - case AUDIO_SOURCE_FM_TUNER: + case AUDIO_SOURCE_FM_TUNER: if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) { device = AUDIO_DEVICE_IN_FM_TUNER; } break; + case AUDIO_SOURCE_ECHO_REFERENCE: + if (availableDeviceTypes & AUDIO_DEVICE_IN_ECHO_REFERENCE) { + device = AUDIO_DEVICE_IN_ECHO_REFERENCE; + } + break; default: ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); break; diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index 29b0561511..919a90d744 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -330,11 +330,12 @@ status_t AudioPolicyEffects::addSourceDefaultEffect(const effect_uuid_t *type, return BAD_VALUE; } - // HOTWORD and FM_TUNER are two special case sources > MAX. + // HOTWORD, FM_TUNER and ECHO_REFERENCE are special case sources > MAX. if (source < AUDIO_SOURCE_DEFAULT || (source > AUDIO_SOURCE_MAX && source != AUDIO_SOURCE_HOTWORD && - source != AUDIO_SOURCE_FM_TUNER)) { + source != AUDIO_SOURCE_FM_TUNER && + source != AUDIO_SOURCE_ECHO_REFERENCE)) { ALOGE("addSourceDefaultEffect(): Unsupported source type %d", source); return BAD_VALUE; } @@ -534,7 +535,8 @@ void AudioPolicyEffects::EffectVector::setProcessorEnabled(bool enabled) CAMCORDER_SRC_TAG, VOICE_REC_SRC_TAG, VOICE_COMM_SRC_TAG, - UNPROCESSED_SRC_TAG + UNPROCESSED_SRC_TAG, + VOICE_PERFORMANCE_SRC_TAG }; // returns the audio_source_t enum corresponding to the input source name or diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 80503fdaa1..2c904d9ad7 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -336,8 +336,11 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, } // already checked by client, but double-check in case the client wrapper is bypassed - if (attr->source < AUDIO_SOURCE_DEFAULT && attr->source >= AUDIO_SOURCE_CNT && - attr->source != AUDIO_SOURCE_HOTWORD && attr->source != AUDIO_SOURCE_FM_TUNER) { + if ((attr->source < AUDIO_SOURCE_DEFAULT) + || (attr->source >= AUDIO_SOURCE_CNT + && attr->source != AUDIO_SOURCE_HOTWORD + && attr->source != AUDIO_SOURCE_FM_TUNER + && attr->source != AUDIO_SOURCE_ECHO_REFERENCE)) { return BAD_VALUE; } @@ -367,7 +370,8 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK || attr->source == AUDIO_SOURCE_VOICE_DOWNLINK || - attr->source == AUDIO_SOURCE_VOICE_CALL) && + attr->source == AUDIO_SOURCE_VOICE_CALL || + attr->source == AUDIO_SOURCE_ECHO_REFERENCE) && !captureAudioOutputAllowed(pid, uid)) { return PERMISSION_DENIED; } -- GitLab From 9b248b8791dbfbd9a4ee80ddad9edc94c32e072e Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 11 Jan 2019 14:52:26 -0800 Subject: [PATCH 0734/1530] Fix a potential integer overflow in MediaBuffer Test: build and boot. Video on SUW plays Bug: 111921829 Change-Id: I86669beccad15ebbef7ecb44f5c7819bb496afe8 --- media/libstagefright/foundation/MediaBuffer.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp index bab3a03c4f..9beac05f47 100644 --- a/media/libstagefright/foundation/MediaBuffer.cpp +++ b/media/libstagefright/foundation/MediaBuffer.cpp @@ -59,9 +59,11 @@ MediaBuffer::MediaBuffer(size_t size) #ifndef NO_IMEMORY } else { ALOGV("creating memoryDealer"); - sp memoryDealer = - new MemoryDealer(size + sizeof(SharedControl), "MediaBuffer"); - mMemory = memoryDealer->allocate(size + sizeof(SharedControl)); + size_t newSize = 0; + if (!__builtin_add_overflow(size, sizeof(SharedControl), &newSize)) { + sp memoryDealer = new MemoryDealer(newSize, "MediaBuffer"); + mMemory = memoryDealer->allocate(newSize); + } if (mMemory == NULL) { ALOGW("Failed to allocate shared memory, trying regular allocation!"); mData = malloc(size); -- GitLab From 7cf2914796c6a2c5419b3113508c1a8b7748ed61 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 11 Jan 2019 14:27:51 -0800 Subject: [PATCH 0735/1530] Fix potential integer overflow in MPEG4Extractor Test: build Bug: 111792351 Change-Id: Ib21ce33fb6c473c217f1f2a35e2ce1c81256f22f --- media/extractors/mp4/MPEG4Extractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 04413592ff..262852f6c8 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5142,7 +5142,7 @@ status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { sampleCtsOffset = 0; } - if (size < (off64_t)(sampleCount * bytesPerSample)) { + if (size < (off64_t)sampleCount * bytesPerSample) { return -EINVAL; } -- GitLab From 1a394d593e5d089a2f6c5bcf897525375013bb32 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Fri, 11 Jan 2019 15:49:24 -0800 Subject: [PATCH 0736/1530] drmserver: use getCallingSid Bug: 121035042 Test: `atest android.drm.cts` CtsDrmTestCases: Passed: 43, Failed: 0 Change-Id: Ia85f437da29821d08dd585f87ac096de5f85b980 --- drm/drmserver/DrmManagerService.cpp | 25 ++++++++++++++++--------- drm/drmserver/DrmManagerService.h | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp index 2532275119..2600a2c23e 100644 --- a/drm/drmserver/DrmManagerService.cpp +++ b/drm/drmserver/DrmManagerService.cpp @@ -58,22 +58,26 @@ const char *DrmManagerService::get_perm_label(drm_perm_t perm) { return drm_perm_labels[index]; } -bool DrmManagerService::selinuxIsProtectedCallAllowed(pid_t spid, drm_perm_t perm) { +bool DrmManagerService::selinuxIsProtectedCallAllowed(pid_t spid, const char* ssid, drm_perm_t perm) { if (selinux_enabled <= 0) { return true; } - char *sctx; + char *sctx = NULL; const char *selinux_class = "drmservice"; const char *str_perm = get_perm_label(perm); - if (getpidcon(spid, &sctx) != 0) { - ALOGE("SELinux: getpidcon(pid=%d) failed.\n", spid); - return false; + if (ssid == NULL) { + android_errorWriteLog(0x534e4554, "121035042"); + + if (getpidcon(spid, &sctx) != 0) { + ALOGE("SELinux: getpidcon(pid=%d) failed.\n", spid); + return false; + } } - bool allowed = (selinux_check_access(sctx, drmserver_context, selinux_class, - str_perm, NULL) == 0); + bool allowed = (selinux_check_access(ssid ? ssid : sctx, drmserver_context, + selinux_class, str_perm, NULL) == 0); freecon(sctx); return allowed; @@ -86,10 +90,11 @@ bool DrmManagerService::isProtectedCallAllowed(drm_perm_t perm) { IPCThreadState* ipcState = IPCThreadState::self(); uid_t uid = ipcState->getCallingUid(); pid_t spid = ipcState->getCallingPid(); + const char* ssid = ipcState->getCallingSid(); for (unsigned int i = 0; i < trustedUids.size(); ++i) { if (trustedUids[i] == uid) { - return selinuxIsProtectedCallAllowed(spid, perm); + return selinuxIsProtectedCallAllowed(spid, ssid, perm); } } return false; @@ -97,7 +102,9 @@ bool DrmManagerService::isProtectedCallAllowed(drm_perm_t perm) { void DrmManagerService::instantiate() { ALOGV("instantiate"); - defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService()); + sp service = new DrmManagerService(); + service->setRequestingSid(true); + defaultServiceManager()->addService(String16("drm.drmManager"), service); if (0 >= trustedUids.size()) { // TODO diff --git a/drm/drmserver/DrmManagerService.h b/drm/drmserver/DrmManagerService.h index 7aaeab5f64..2e27a3c393 100644 --- a/drm/drmserver/DrmManagerService.h +++ b/drm/drmserver/DrmManagerService.h @@ -60,7 +60,7 @@ private: static const char *get_perm_label(drm_perm_t perm); - static bool selinuxIsProtectedCallAllowed(pid_t spid, drm_perm_t perm); + static bool selinuxIsProtectedCallAllowed(pid_t spid, const char* ssid, drm_perm_t perm); static bool isProtectedCallAllowed(drm_perm_t perm); -- GitLab From 510512674313e53e166d33ae053ccbc2b71a61c8 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 28 Nov 2018 13:59:05 -0800 Subject: [PATCH 0737/1530] CCodec: put encrypted buffer in array mode right away Bug: 118412571 Test: atest GtsMediaTestCases:WidevineH264PlaybackTests Change-Id: Ic51339eb38e0bb877f3194dad21c151c7f49b2a3 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 55a97d8860..432b8c00ee 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -2127,6 +2127,7 @@ status_t CCodecBufferChannel::start( pools->inputPool = pool; } + bool forceArrayMode = false; Mutexed>::Locked buffers(mInputBuffers); if (graphic) { if (mInputSurface) { @@ -2158,6 +2159,7 @@ status_t CCodecBufferChannel::start( } buffers->reset(new EncryptedLinearInputBuffers( secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity, mName)); + forceArrayMode = true; } else { buffers->reset(new LinearInputBuffers(mName)); } @@ -2169,6 +2171,10 @@ status_t CCodecBufferChannel::start( } else { // TODO: error } + + if (forceArrayMode) { + *buffers = (*buffers)->toArrayMode(kMinInputBufferArraySize); + } } if (outputFormat != nullptr) { -- GitLab From 1e83beeadb4db01091f2ba62eda445304e699e3e Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 17 Dec 2018 14:15:20 -0800 Subject: [PATCH 0738/1530] aaudio: call stop() from the callback Stop the stream when the callback returns AAUDIO_CALLBACK_RESULT_STOP. Refactor start/pause/stop code to allow different paths for stop from the app or from the callback. Fix error handling in joinThread(). Simplify code path through PlayerBase. Bug: 120932593 Bug: 121158739 Test: test_return_stop Test: test_return_stop -i Test: test_return_stop -n Test: test_return_stop -i -n Test: atest CtsNativeMediaAAudioTestCases Change-Id: I85134211348b26077693d9a71bf1331dad60da38 --- .../src/client/AudioStreamInternal.cpp | 3 +- .../src/client/AudioStreamInternalCapture.cpp | 1 + .../src/client/AudioStreamInternalPlay.cpp | 1 + media/libaaudio/src/core/AAudioAudio.cpp | 2 +- media/libaaudio/src/core/AudioStream.cpp | 72 +++++++++++++++---- media/libaaudio/src/core/AudioStream.h | 71 +++++++----------- .../src/legacy/AudioStreamLegacy.cpp | 21 ++++-- .../libaaudio/src/legacy/AudioStreamTrack.cpp | 6 +- 8 files changed, 105 insertions(+), 72 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index fffcda016e..9c682b702e 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -349,8 +349,7 @@ aaudio_result_t AudioStreamInternal::stopCallback() } } -aaudio_result_t AudioStreamInternal::requestStop() -{ +aaudio_result_t AudioStreamInternal::requestStop() { aaudio_result_t result = stopCallback(); if (result != AAUDIO_OK) { return result; diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp index 58ef7b15f8..7dcb620dd2 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp @@ -259,6 +259,7 @@ void *AudioStreamInternalCapture::callbackLoop() { if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); + result = systemStopFromCallback(); break; } } diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 9af47b2ea2..0884fca378 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -294,6 +294,7 @@ void *AudioStreamInternalPlay::callbackLoop() { } } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); + result = systemStopFromCallback(); break; } } diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp index 2fb39868f7..0d71efc22e 100644 --- a/media/libaaudio/src/core/AAudioAudio.cpp +++ b/media/libaaudio/src/core/AAudioAudio.cpp @@ -316,7 +316,7 @@ AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); ALOGD("%s(%p) called", __func__, stream); - return audioStream->systemStop(); + return audioStream->systemStopFromApp(); } AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream, diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp index 391af29c3b..e39a075161 100644 --- a/media/libaaudio/src/core/AudioStream.cpp +++ b/media/libaaudio/src/core/AudioStream.cpp @@ -119,21 +119,29 @@ aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) return AAUDIO_OK; } -aaudio_result_t AudioStream::safeStart() { +aaudio_result_t AudioStream::systemStart() { std::lock_guard lock(mStreamLock); + if (collidesWithCallback()) { ALOGE("%s cannot be called from a callback!", __func__); return AAUDIO_ERROR_INVALID_STATE; } - return requestStart(); + + aaudio_result_t result = requestStart(); + if (result == AAUDIO_OK) { + // We only call this for logging in "dumpsys audio". So ignore return code. + (void) mPlayerBase->start(); + } + return result; } -aaudio_result_t AudioStream::safePause() { +aaudio_result_t AudioStream::systemPause() { + std::lock_guard lock(mStreamLock); + if (!isPauseSupported()) { return AAUDIO_ERROR_UNIMPLEMENTED; } - std::lock_guard lock(mStreamLock); if (collidesWithCallback()) { ALOGE("%s cannot be called from a callback!", __func__); return AAUDIO_ERROR_INVALID_STATE; @@ -169,7 +177,12 @@ aaudio_result_t AudioStream::safePause() { return AAUDIO_ERROR_INVALID_STATE; } - return requestPause(); + aaudio_result_t result = requestPause(); + if (result == AAUDIO_OK) { + // We only call this for logging in "dumpsys audio". So ignore return code. + (void) mPlayerBase->pause(); + } + return result; } aaudio_result_t AudioStream::safeFlush() { @@ -192,12 +205,31 @@ aaudio_result_t AudioStream::safeFlush() { return requestFlush(); } -aaudio_result_t AudioStream::safeStop() { +aaudio_result_t AudioStream::systemStopFromCallback() { + std::lock_guard lock(mStreamLock); + aaudio_result_t result = safeStop(); + if (result == AAUDIO_OK) { + // We only call this for logging in "dumpsys audio". So ignore return code. + (void) mPlayerBase->stop(); + } + return result; +} + +aaudio_result_t AudioStream::systemStopFromApp() { std::lock_guard lock(mStreamLock); if (collidesWithCallback()) { - ALOGE("stream cannot be stopped from a callback!"); + ALOGE("stream cannot be stopped by calling from a callback!"); return AAUDIO_ERROR_INVALID_STATE; } + aaudio_result_t result = safeStop(); + if (result == AAUDIO_OK) { + // We only call this for logging in "dumpsys audio". So ignore return code. + (void) mPlayerBase->stop(); + } + return result; +} + +aaudio_result_t AudioStream::safeStop() { switch (getState()) { // Proceed with stopping. @@ -224,7 +256,7 @@ aaudio_result_t AudioStream::safeStop() { case AAUDIO_STREAM_STATE_CLOSING: case AAUDIO_STREAM_STATE_CLOSED: default: - ALOGW("requestStop() stream not running, state = %s", + ALOGW("%s() stream not running, state = %s", __func__, AAudio_convertStreamStateToText(getState())); return AAUDIO_ERROR_INVALID_STATE; } @@ -349,21 +381,33 @@ aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds, } } -aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds) +aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds __unused) { if (!mHasThread) { ALOGE("joinThread() - but has no thread"); return AAUDIO_ERROR_INVALID_STATE; } + aaudio_result_t result = AAUDIO_OK; + // If the callback is stopping the stream because the app passed back STOP + // then we don't need to join(). The thread is already about to exit. + if (pthread_self() != mThread) { + // Called from an app thread. Not the callback. #if 0 - // TODO implement equivalent of pthread_timedjoin_np() - struct timespec abstime; - int err = pthread_timedjoin_np(mThread, returnArg, &abstime); + // TODO implement equivalent of pthread_timedjoin_np() + struct timespec abstime; + int err = pthread_timedjoin_np(mThread, returnArg, &abstime); #else - int err = pthread_join(mThread, returnArg); + int err = pthread_join(mThread, returnArg); #endif + if (err) { + ALOGE("%s() pthread_join() returns err = %d", __func__, err); + result = AAudioConvert_androidToAAudioResult(-err); + } + } + // This must be set false so that the callback thread can be created + // when the stream is restarted. mHasThread = false; - return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult; + return (result != AAUDIO_OK) ? result : mThreadRegistrationResult; } aaudio_data_callback_result_t AudioStream::maybeCallDataCallback(void *audioData, diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h index 60200b255b..46951f50fe 100644 --- a/media/libaaudio/src/core/AudioStream.h +++ b/media/libaaudio/src/core/AudioStream.h @@ -51,21 +51,6 @@ public: virtual ~AudioStream(); - /** - * Lock a mutex and make sure we are not calling from a callback function. - * @return result of requestStart(); - */ - aaudio_result_t safeStart(); - - aaudio_result_t safePause(); - - aaudio_result_t safeFlush(); - - aaudio_result_t safeStop(); - - aaudio_result_t safeClose(); - - // =========== Begin ABSTRACT methods =========================== protected: /* Asynchronous requests. @@ -74,7 +59,7 @@ protected: virtual aaudio_result_t requestStart() = 0; /** - * Check the state to see if Pause if currently legal. + * Check the state to see if Pause is currently legal. * * @param result pointer to return code * @return true if OK to continue, if false then return result @@ -356,33 +341,28 @@ public: mPlayerBase->unregisterWithAudioManager(); } - // Pass start request through PlayerBase for tracking. - aaudio_result_t systemStart() { - mPlayerBase->start(); - // Pass aaudio_result_t around the PlayerBase interface, which uses status__t. - return mPlayerBase->getResult(); - } + aaudio_result_t systemStart(); - // Pass pause request through PlayerBase for tracking. - aaudio_result_t systemPause() { - mPlayerBase->pause(); - return mPlayerBase->getResult(); - } + aaudio_result_t systemPause(); - // Pass stop request through PlayerBase for tracking. - aaudio_result_t systemStop() { - mPlayerBase->stop(); - return mPlayerBase->getResult(); - } + aaudio_result_t safeFlush(); + + /** + * This is called when an app calls AAudioStream_requestStop(); + * It prevents calls from a callback. + */ + aaudio_result_t systemStopFromApp(); + + /** + * This is called internally when an app callback returns AAUDIO_CALLBACK_RESULT_STOP. + */ + aaudio_result_t systemStopFromCallback(); + + aaudio_result_t safeClose(); protected: - // PlayerBase allows the system to control the stream. - // Calling through PlayerBase->start() notifies the AudioManager of the player state. - // The AudioManager also can start/stop a stream by calling mPlayerBase->playerStart(). - // systemStart() ==> mPlayerBase->start() mPlayerBase->playerStart() ==> requestStart() - // \ / - // ------ AudioManager ------- + // PlayerBase allows the system to control the stream volume. class MyPlayerBase : public android::PlayerBase { public: explicit MyPlayerBase(AudioStream *parent); @@ -406,20 +386,19 @@ protected: void clearParentReference() { mParent = nullptr; } + // Just a stub. The ability to start audio through PlayerBase is being deprecated. android::status_t playerStart() override { - // mParent should NOT be null. So go ahead and crash if it is. - mResult = mParent->safeStart(); - return AAudioConvert_aaudioToAndroidStatus(mResult); + return android::NO_ERROR; } + // Just a stub. The ability to pause audio through PlayerBase is being deprecated. android::status_t playerPause() override { - mResult = mParent->safePause(); - return AAudioConvert_aaudioToAndroidStatus(mResult); + return android::NO_ERROR; } + // Just a stub. The ability to stop audio through PlayerBase is being deprecated. android::status_t playerStop() override { - mResult = mParent->safeStop(); - return AAudioConvert_aaudioToAndroidStatus(mResult); + return android::NO_ERROR; } android::status_t playerSetVolume() override { @@ -548,6 +527,8 @@ protected: private: + aaudio_result_t safeStop(); + std::mutex mStreamLock; const android::sp mPlayerBase; diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp index a6b9f5d7b5..2edab58321 100644 --- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp +++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp @@ -78,8 +78,9 @@ int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) { aaudio_data_callback_result_t callbackResult; - // This illegal size can be used to tell AudioFlinger to stop calling us. - // This takes advantage of AudioFlinger killing the stream. + // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us. + // This takes advantage of them killing the stream when they see a size out of range. + // That is an undocumented behavior. // TODO add to API in AudioRecord and AudioTrack const size_t SIZE_STOP_CALLBACKS = SIZE_MAX; @@ -95,7 +96,7 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode ALOGW("processCallbackCommon() data, stream disconnected"); audioBuffer->size = SIZE_STOP_CALLBACKS; } else if (!mCallbackEnabled.load()) { - ALOGW("processCallbackCommon() stopping because callback disabled"); + ALOGW("processCallbackCommon() no data because callback disabled"); audioBuffer->size = SIZE_STOP_CALLBACKS; } else { if (audioBuffer->frameCount == 0) { @@ -115,10 +116,16 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode } if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) { audioBuffer->size = audioBuffer->frameCount * getBytesPerDeviceFrame(); - } else { // STOP or invalid result - ALOGW("%s() callback requested stop, fake an error", __func__); - audioBuffer->size = SIZE_STOP_CALLBACKS; - // Disable the callback just in case AudioFlinger keeps trying to call us. + } else { + if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { + ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); + } else { + ALOGW("%s() callback returned invalid result = %d", + __func__, callbackResult); + } + audioBuffer->size = 0; + systemStopFromCallback(); + // Disable the callback just in case the system keeps trying to call us. mCallbackEnabled.store(false); } diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index 1ac2558006..c995e99cb5 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -288,7 +288,7 @@ aaudio_result_t AudioStreamTrack::requestStart() { aaudio_result_t AudioStreamTrack::requestPause() { if (mAudioTrack.get() == nullptr) { - ALOGE("requestPause() no AudioTrack"); + ALOGE("%s() no AudioTrack", __func__); return AAUDIO_ERROR_INVALID_STATE; } @@ -304,7 +304,7 @@ aaudio_result_t AudioStreamTrack::requestPause() { aaudio_result_t AudioStreamTrack::requestFlush() { if (mAudioTrack.get() == nullptr) { - ALOGE("requestFlush() no AudioTrack"); + ALOGE("%s() no AudioTrack", __func__); return AAUDIO_ERROR_INVALID_STATE; } @@ -318,7 +318,7 @@ aaudio_result_t AudioStreamTrack::requestFlush() { aaudio_result_t AudioStreamTrack::requestStop() { if (mAudioTrack.get() == nullptr) { - ALOGE("requestStop() no AudioTrack"); + ALOGE("%s() no AudioTrack", __func__); return AAUDIO_ERROR_INVALID_STATE; } -- GitLab From 1ccd0abbcfc2c40b4974aca9b15c277e4b37658a Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 16 Jan 2019 14:45:30 -0800 Subject: [PATCH 0739/1530] Use library defaults for libstagefright_foundation Test: build Change-Id: I06bfb6e9da21e7eba373ab32188daadc7938df97 --- media/libstagefright/foundation/Android.bp | 84 +++------------------- 1 file changed, 8 insertions(+), 76 deletions(-) diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index 027e593e39..dd1d904c85 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -4,8 +4,8 @@ cc_library_headers { vendor_available: true, } -cc_library { - name: "libstagefright_foundation", +cc_defaults { + name: "libstagefright_foundation_defaults", vendor_available: true, vndk: { enabled: true, @@ -88,39 +88,14 @@ cc_library { }, } +cc_library { + name: "libstagefright_foundation", + defaults: ["libstagefright_foundation_defaults"], +} + cc_library_static { name: "libstagefright_foundation_without_imemory", - vendor_available: true, - vndk: { - enabled: true, - }, - double_loadable: true, - include_dirs: [ - "frameworks/av/include", - "frameworks/native/include", - "frameworks/native/libs/arect/include", - "frameworks/native/libs/nativebase/include", - ], - - local_include_dirs: [ - "include/media/stagefright/foundation", - ], - - header_libs: [ - "libhardware_headers", - "libstagefright_foundation_headers", - "media_ndk_headers", - "media_plugin_headers", - ], - - export_header_lib_headers: [ - "libstagefright_foundation_headers", - "media_plugin_headers", - ], - - export_shared_lib_headers: [ - "libbinder", - ], + defaults: ["libstagefright_foundation_defaults"], cflags: [ "-Wno-multichar", @@ -128,47 +103,4 @@ cc_library_static { "-Wall", "-DNO_IMEMORY", ], - - shared_libs: [ - "libbinder", - "libutils", - "libcutils", - "liblog", - ], - - srcs: [ - "AAtomizer.cpp", - "ABitReader.cpp", - "ABuffer.cpp", - "ADebug.cpp", - "AHandler.cpp", - "ALooper.cpp", - "ALooperRoster.cpp", - "AMessage.cpp", - "AString.cpp", - "AStringUtils.cpp", - "AudioPresentationInfo.cpp", - "ByteUtils.cpp", - "ColorUtils.cpp", - "MediaBuffer.cpp", - "MediaBufferBase.cpp", - "MediaBufferGroup.cpp", - "MediaDefs.cpp", - "MediaKeys.cpp", - "MetaData.cpp", - "MetaDataBase.cpp", - "avc_utils.cpp", - "base64.cpp", - "hexdump.cpp", - ], - - clang: true, - - sanitize: { - misc_undefined: [ - "unsigned-integer-overflow", - "signed-integer-overflow", - ], - cfi: true, - }, } -- GitLab From 88471f15c3026c6a139e43e8dc73ae397470dc2c Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 16 Jan 2019 15:17:02 -0800 Subject: [PATCH 0740/1530] MediaClock: relax condition for time discontinuity This will reduce MEDIA_TIME_DISCONTINUITY notification Test: cts Bug: 119524499 Change-Id: Ic64fe02e832ea4c1d9733a23f872e513972d075a --- media/libstagefright/MediaClock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp index 8bff9f7526..4f9bc6d32f 100644 --- a/media/libstagefright/MediaClock.cpp +++ b/media/libstagefright/MediaClock.cpp @@ -110,7 +110,7 @@ void MediaClock::updateAnchor( if (mAnchorTimeRealUs != -1) { int64_t oldNowMediaUs = mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate; - if (nowMediaUs < oldNowMediaUs + if (nowMediaUs < oldNowMediaUs + kAnchorFluctuationAllowedUs && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) { return; } -- GitLab From b2c9f01aa85f6509cfa430d081f7792b5d8449ff Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 26 Sep 2018 16:00:36 -0700 Subject: [PATCH 0741/1530] aaudio: loopback test should ignore some read errs If the input stream is not yet running then it will return INVALID_STATE. Test: aaudio_loopback -tm Change-Id: I752675bf7dd7325beee16f3de24183042632f807 --- .../libaaudio/examples/loopback/src/loopback.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index 2a02b2000e..3de1514485 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -105,9 +105,14 @@ static int32_t readFormattedData(LoopbackData *myData, int32_t numFrames) { assert(false); } if (framesRead < 0) { - myData->inputError = framesRead; - printf("ERROR in read = %d = %s\n", framesRead, - AAudio_convertResultToText(framesRead)); + // Expect INVALID_STATE if STATE_STARTING + if (myData->framesReadTotal > 0) { + myData->inputError = framesRead; + printf("ERROR in read = %d = %s\n", framesRead, + AAudio_convertResultToText(framesRead)); + } else { + framesRead = 0; + } } else { myData->framesReadTotal += framesRead; } @@ -149,8 +154,10 @@ static aaudio_data_callback_result_t MyDataCallbackProc( int32_t totalFramesRead = 0; do { actualFramesRead = readFormattedData(myData, numFrames); - if (actualFramesRead) { + if (actualFramesRead > 0) { totalFramesRead += actualFramesRead; + } else if (actualFramesRead < 0) { + result = AAUDIO_CALLBACK_RESULT_STOP; } // Ignore errors because input stream may not be started yet. } while (actualFramesRead > 0); -- GitLab From 182a03fb81ef40d63b0281ad29d7014f03fe16f0 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 16 Jan 2019 14:01:23 -0800 Subject: [PATCH 0742/1530] stagefright/codec2: add av1 level enum reflections Bug: 111936705 Change-Id: Id9f656459ed5aad99056b2893291d60501aed672 --- media/codec2/core/include/C2Config.h | 3 ++- media/codec2/vndk/C2Config.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index cf1f6cf0de..23939b5cb2 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -638,7 +638,7 @@ enum C2Config::level_t : uint32_t { LEVEL_VP9_6_1, ///< VP9 Level 6.1 LEVEL_VP9_6_2, ///< VP9 Level 6.2 - // Dolby Vision level + // Dolby Vision levels LEVEL_DV_MAIN_HD_24 = _C2_PL_DV_BASE, ///< Dolby Vision main tier hd24 LEVEL_DV_MAIN_HD_30, ///< Dolby Vision main tier hd30 LEVEL_DV_MAIN_FHD_24, ///< Dolby Vision main tier fhd24 @@ -659,6 +659,7 @@ enum C2Config::level_t : uint32_t { LEVEL_DV_HIGH_UHD_48, ///< Dolby Vision high tier uhd48 LEVEL_DV_HIGH_UHD_60, ///< Dolby Vision high tier uhd60 + // AV1 levels LEVEL_AV1_2 = _C2_PL_AV1_BASE , ///< AV1 Level 2 LEVEL_AV1_2_1, ///< AV1 Level 2.1 LEVEL_AV1_2_2, ///< AV1 Level 2.2 diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp index 782bec5fbc..8a27088fe5 100644 --- a/media/codec2/vndk/C2Config.cpp +++ b/media/codec2/vndk/C2Config.cpp @@ -221,6 +221,30 @@ DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::level_t, ({ { "vp9-6", C2Config::LEVEL_VP9_6 }, { "vp9-6.1", C2Config::LEVEL_VP9_6_1 }, { "vp9-6.2", C2Config::LEVEL_VP9_6_2 }, + { "av1-2", C2Config::LEVEL_AV1_2 }, + { "av1-2.1", C2Config::LEVEL_AV1_2_1 }, + { "av1-2.2", C2Config::LEVEL_AV1_2_2 }, + { "av1-2.3", C2Config::LEVEL_AV1_2_3 }, + { "av1-3", C2Config::LEVEL_AV1_3 }, + { "av1-3.1", C2Config::LEVEL_AV1_3_1 }, + { "av1-3.2", C2Config::LEVEL_AV1_3_2 }, + { "av1-3.3", C2Config::LEVEL_AV1_3_3 }, + { "av1-4", C2Config::LEVEL_AV1_4 }, + { "av1-4.1", C2Config::LEVEL_AV1_4_1 }, + { "av1-4.2", C2Config::LEVEL_AV1_4_2 }, + { "av1-4.3", C2Config::LEVEL_AV1_4_3 }, + { "av1-5", C2Config::LEVEL_AV1_5 }, + { "av1-5.1", C2Config::LEVEL_AV1_5_1 }, + { "av1-5.2", C2Config::LEVEL_AV1_5_2 }, + { "av1-5.3", C2Config::LEVEL_AV1_5_3 }, + { "av1-6", C2Config::LEVEL_AV1_6 }, + { "av1-6.1", C2Config::LEVEL_AV1_6_1 }, + { "av1-6.2", C2Config::LEVEL_AV1_6_2 }, + { "av1-6.3", C2Config::LEVEL_AV1_6_3 }, + { "av1-7", C2Config::LEVEL_AV1_7 }, + { "av1-7.1", C2Config::LEVEL_AV1_7_1 }, + { "av1-7.2", C2Config::LEVEL_AV1_7_2 }, + { "av1-7.3", C2Config::LEVEL_AV1_7_3 }, })) DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2BufferData::type_t, ({ -- GitLab From 568daf20239f9216081cb6146ad887e848c54589 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 16 Jan 2019 13:55:17 -0800 Subject: [PATCH 0743/1530] stagefright: Add support for levels 6+ for AVC/H.264 Bug: 122909666 Change-Id: I15f6fdcd936a2a3b8d46e46fb939923f7a3678b4 --- media/codec2/core/include/C2Config.h | 3 ++ media/codec2/sfplugin/utils/Codec2Mapper.cpp | 4 +- media/codec2/vndk/C2Config.cpp | 3 ++ media/libstagefright/ACodec.cpp | 39 +++++++++++--------- media/libstagefright/Utils.cpp | 3 ++ 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index cf1f6cf0de..fb141f226a 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -597,6 +597,9 @@ enum C2Config::level_t : uint32_t { LEVEL_AVC_5, ///< AVC (H.264) Level 5 LEVEL_AVC_5_1, ///< AVC (H.264) Level 5.1 LEVEL_AVC_5_2, ///< AVC (H.264) Level 5.2 + LEVEL_AVC_6, ///< AVC (H.264) Level 6 + LEVEL_AVC_6_1, ///< AVC (H.264) Level 6.1 + LEVEL_AVC_6_2, ///< AVC (H.264) Level 6.2 // HEVC (H.265) tiers and levels LEVEL_HEVC_MAIN_1 = _C2_PL_HEVC_BASE, ///< HEVC (H.265) Main Tier Level 1 diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index b1b33e1eb4..5ff7625ee9 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -65,7 +65,9 @@ ALookup sAvcLevels = { { C2Config::LEVEL_AVC_5, AVCLevel5 }, { C2Config::LEVEL_AVC_5_1, AVCLevel51 }, { C2Config::LEVEL_AVC_5_2, AVCLevel52 }, - + { C2Config::LEVEL_AVC_6, AVCLevel6 }, + { C2Config::LEVEL_AVC_6_1, AVCLevel61 }, + { C2Config::LEVEL_AVC_6_2, AVCLevel62 }, }; ALookup sAvcProfiles = { diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp index 782bec5fbc..b8a429a024 100644 --- a/media/codec2/vndk/C2Config.cpp +++ b/media/codec2/vndk/C2Config.cpp @@ -186,6 +186,9 @@ DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(C2Config::level_t, ({ { "avc-5", C2Config::LEVEL_AVC_5 }, { "avc-5.1", C2Config::LEVEL_AVC_5_1 }, { "avc-5.2", C2Config::LEVEL_AVC_5_2 }, + { "avc-6", C2Config::LEVEL_AVC_6 }, + { "avc-6.1", C2Config::LEVEL_AVC_6_1 }, + { "avc-6.2", C2Config::LEVEL_AVC_6_2 }, { "hevc-main-1", C2Config::LEVEL_HEVC_MAIN_1 }, { "hevc-main-2", C2Config::LEVEL_HEVC_MAIN_2 }, { "hevc-main-2.1", C2Config::LEVEL_HEVC_MAIN_2_1 }, diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index dadfe285bc..ffa28f36ec 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4297,24 +4297,27 @@ int /* OMX_VIDEO_AVCLEVELTYPE */ ACodec::getAVCLevelFor( int maxDimension = max(width, height); static const int limits[][5] = { - /* MBps MB dim bitrate level */ - { 1485, 99, 28, 64, OMX_VIDEO_AVCLevel1 }, - { 1485, 99, 28, 128, OMX_VIDEO_AVCLevel1b }, - { 3000, 396, 56, 192, OMX_VIDEO_AVCLevel11 }, - { 6000, 396, 56, 384, OMX_VIDEO_AVCLevel12 }, - { 11880, 396, 56, 768, OMX_VIDEO_AVCLevel13 }, - { 11880, 396, 56, 2000, OMX_VIDEO_AVCLevel2 }, - { 19800, 792, 79, 4000, OMX_VIDEO_AVCLevel21 }, - { 20250, 1620, 113, 4000, OMX_VIDEO_AVCLevel22 }, - { 40500, 1620, 113, 10000, OMX_VIDEO_AVCLevel3 }, - { 108000, 3600, 169, 14000, OMX_VIDEO_AVCLevel31 }, - { 216000, 5120, 202, 20000, OMX_VIDEO_AVCLevel32 }, - { 245760, 8192, 256, 20000, OMX_VIDEO_AVCLevel4 }, - { 245760, 8192, 256, 50000, OMX_VIDEO_AVCLevel41 }, - { 522240, 8704, 263, 50000, OMX_VIDEO_AVCLevel42 }, - { 589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5 }, - { 983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 }, - { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 }, + /* MBps MB dim bitrate level */ + { 1485, 99, 28, 64, OMX_VIDEO_AVCLevel1 }, + { 1485, 99, 28, 128, OMX_VIDEO_AVCLevel1b }, + { 3000, 396, 56, 192, OMX_VIDEO_AVCLevel11 }, + { 6000, 396, 56, 384, OMX_VIDEO_AVCLevel12 }, + { 11880, 396, 56, 768, OMX_VIDEO_AVCLevel13 }, + { 11880, 396, 56, 2000, OMX_VIDEO_AVCLevel2 }, + { 19800, 792, 79, 4000, OMX_VIDEO_AVCLevel21 }, + { 20250, 1620, 113, 4000, OMX_VIDEO_AVCLevel22 }, + { 40500, 1620, 113, 10000, OMX_VIDEO_AVCLevel3 }, + { 108000, 3600, 169, 14000, OMX_VIDEO_AVCLevel31 }, + { 216000, 5120, 202, 20000, OMX_VIDEO_AVCLevel32 }, + { 245760, 8192, 256, 20000, OMX_VIDEO_AVCLevel4 }, + { 245760, 8192, 256, 50000, OMX_VIDEO_AVCLevel41 }, + { 522240, 8704, 263, 50000, OMX_VIDEO_AVCLevel42 }, + { 589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5 }, + { 983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 }, + { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 }, + { 4177920, 139264, 1055, 240000, OMX_VIDEO_AVCLevel6 }, + { 8355840, 139264, 1055, 480000, OMX_VIDEO_AVCLevel61 }, + { 16711680, 139264, 1055, 800000, OMX_VIDEO_AVCLevel62 }, }; for (size_t i = 0; i < ARRAY_SIZE(limits); i++) { diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 49e485af99..6f974f0f6a 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -191,6 +191,9 @@ static void parseAvcProfileLevelFromAvcc(const uint8_t *ptr, size_t size, sp profiles { { 66, OMX_VIDEO_AVCProfileBaseline }, -- GitLab From be67658177b3ccc8fec702907a6ae56e2dc89ca1 Mon Sep 17 00:00:00 2001 From: Yuxiu Zhang Date: Fri, 16 Mar 2018 17:12:16 +0800 Subject: [PATCH 0744/1530] FLAC: support files which has ID3v2 tag or Last-metadata-block flag is 1 Problem: 1. Some flac audios cannot be played because they have ID3v2 tag 2. flac audio which Last-metadata-block flag is 1 cannot be played by default Solution: 1. Skip ID3v2 tag, and make these flac audios can be played 2. Last-metadata-block flag can be 1 according to flac spec. https://xiph.org/flac/format.html#metadata_block_streaminfo It is rational that flac files only have one metadata block (STREAMINFO) before audio frames. So, we should support files which Last-metadata-block is 1. Bug: 122939727 Test: 1. manually play flac audio with ID3v2 tag 2. manually play flac audio which Last-metadata-block is 1 Change-Id: Id4928bc3a597176be63130bce9ba25df1978b51d --- media/extractors/flac/FLACExtractor.cpp | 35 ++++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index dcda6bf310..84fbceebff 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -806,7 +806,32 @@ media_status_t FLACExtractor::getMetaData(AMediaFormat *meta) bool SniffFLAC(DataSourceHelper *source, float *confidence) { - // FLAC header. + // Skip ID3 tags + off64_t pos = 0; + uint8_t header[10]; + for (;;) { + if (source->readAt(pos, header, sizeof(header)) != sizeof(header)) { + return false; // no more file to read. + } + + // check for ID3 tag + if (memcmp("ID3", header, 3) != 0) { + break; // not an ID3 tag. + } + + // skip the ID3v2 data and check again + const unsigned id3Len = 10 + + (((header[6] & 0x7f) << 21) + | ((header[7] & 0x7f) << 14) + | ((header[8] & 0x7f) << 7) + | (header[9] & 0x7f)); + pos += id3Len; + + ALOGV("skipped ID3 tag of len %u new starting offset is %#016llx", + id3Len, (long long)pos); + } + + // Check FLAC header. // https://xiph.org/flac/format.html#stream // // Note: content stored big endian. @@ -815,12 +840,8 @@ bool SniffFLAC(DataSourceHelper *source, float *confidence) // 4 8 metadata type STREAMINFO (0) (note: OR with 0x80 if last metadata) // 5 24 size of metadata, for STREAMINFO (0x22). - // Android is LE, so express header as little endian int64 constant. - constexpr int64_t flacHeader = (0x22LL << 56) | 'CaLf'; - constexpr int64_t flacHeader2 = flacHeader | (0x80LL << 32); // alternate form (last metadata) - int64_t header; - if (source->readAt(0, &header, sizeof(header)) != sizeof(header) - || (header != flacHeader && header != flacHeader2)) { + if (memcmp("fLaC\x00\x00\x00\x22", header, 8) != 0 && + memcmp("fLaC\x80\x00\x00\x22", header, 8) != 0) { return false; } -- GitLab From 19785de5ed298b2c28f398d9c27c02e173aa1f52 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 3 Jan 2019 15:53:49 -0800 Subject: [PATCH 0745/1530] aaudio test: improve test_timestamps.cpp Test for timestamps returned when STOPPED. Test for no valid timestamps. Bug: 122043542 Test: adb shell test_timestamps Change-Id: I3f9ea636c7a469ffd6aa721776127dfeac94bc1e --- media/libaaudio/tests/test_timestamps.cpp | 125 ++++++++++++++++++---- 1 file changed, 102 insertions(+), 23 deletions(-) diff --git a/media/libaaudio/tests/test_timestamps.cpp b/media/libaaudio/tests/test_timestamps.cpp index dfa781521d..7b1dfd3a8a 100644 --- a/media/libaaudio/tests/test_timestamps.cpp +++ b/media/libaaudio/tests/test_timestamps.cpp @@ -35,6 +35,7 @@ #define NUM_SECONDS 1 #define NUM_LOOPS 4 +#define MAX_TESTS 20 typedef struct TimestampInfo { int64_t framesTotal; @@ -53,6 +54,49 @@ typedef struct TimestampCallbackData_s { bool forceUnderruns = false; } TimestampCallbackData_t; +struct TimeStampTestLog { + aaudio_policy_t isMmap; + aaudio_sharing_mode_t sharingMode; + aaudio_performance_mode_t performanceMode; + aaudio_direction_t direction; + aaudio_result_t result; +}; + +static int s_numTests = 0; +// Use a plain old array because we reference this from the callback and do not want any +// automatic memory allocation. +static TimeStampTestLog s_testLogs[MAX_TESTS]{}; + +static void logTestResult(bool isMmap, + aaudio_sharing_mode_t sharingMode, + aaudio_performance_mode_t performanceMode, + aaudio_direction_t direction, + aaudio_result_t result) { + if(s_numTests >= MAX_TESTS) { + printf("ERROR - MAX_TESTS too small = %d\n", MAX_TESTS); + return; + } + s_testLogs[s_numTests].isMmap = isMmap; + s_testLogs[s_numTests].sharingMode = sharingMode; + s_testLogs[s_numTests].performanceMode = performanceMode; + s_testLogs[s_numTests].direction = direction; + s_testLogs[s_numTests].result = result; + s_numTests++; +} + +static void printTestResults() { + for (int i = 0; i < s_numTests; i++) { + TimeStampTestLog *log = &s_testLogs[i]; + printf("%2d: mmap = %3s, sharing = %9s, perf = %11s, dir = %6s ---- %4s\n", + i, + log->isMmap ? "yes" : "no", + getSharingModeText(log->sharingMode), + getPerformanceModeText(log->performanceMode), + getDirectionText(log->direction), + log->result ? "FAIL" : "pass"); + } +} + // Callback function that fills the audio output buffer. aaudio_data_callback_result_t timestampDataCallbackProc( AAudioStream *stream, @@ -115,6 +159,7 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, int32_t originalBufferSize = 0; int32_t requestedBufferSize = 0; int32_t finalBufferSize = 0; + bool isMmap = false; aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT; aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED; aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE; @@ -124,7 +169,8 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, memset(&sTimestampData, 0, sizeof(sTimestampData)); - printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n", + printf("\n=================================================================================\n"); + printf("--------- testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) --------\n", mmapPolicy, getSharingModeText(sharingMode), getPerformanceModeText(performanceMode), @@ -177,8 +223,8 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, printf(" chans = %3d, rate = %6d format = %d\n", actualChannelCount, actualSampleRate, actualDataFormat); - printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream) - ? "yes" : "no"); + isMmap = AAudioStream_isMMapUsed(aaudioStream); + printf(" Is MMAP used? %s\n", isMmap ? "yes" : "no"); // This is the number of frames that are read in one chunk by a DMA controller // or a DSP or a mixer. @@ -218,7 +264,7 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, for (int second = 0; second < NUM_SECONDS; second++) { // Give AAudio callback time to run in the background. - sleep(1); + usleep(200 * 1000); // Periodically print the progress so we know it hasn't died. printf("framesWritten = %d, XRuns = %d\n", @@ -234,18 +280,25 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, } printf("timestampCount = %d\n", sTimestampData.timestampCount); - int printed = 0; - for (int i = 0; i < sTimestampData.timestampCount; i++) { + int printedGood = 0; + int printedBad = 0; + for (int i = 1; i < sTimestampData.timestampCount; i++) { TimestampInfo *timestamp = &sTimestampData.timestamps[i]; - bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition); - bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos); - if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) { - printf(" %3d : frames %8lld, xferd %8lld", i, - (long long) timestamp->framesTotal, - (long long) timestamp->appPosition); - if (timestamp->result != AAUDIO_OK) { - printf(", result = %s\n", AAudio_convertResultToText(timestamp->result)); - } else { + if (timestamp->result != AAUDIO_OK) { + if (printedBad < 5) { + printf(" %3d : frames %8lld, xferd %8lld, result = %s\n", + i, + (long long) timestamp->framesTotal, + (long long) timestamp->appPosition, + AAudio_convertResultToText(timestamp->result)); + printedBad++; + } + } else { + const bool posChanged = (timestamp->timestampPosition != + (timestamp - 1)->timestampPosition); + const bool timeChanged = (timestamp->timestampNanos + != (timestamp - 1)->timestampNanos); + if ((printedGood < 20) && (posChanged || timeChanged)) { bool negative = timestamp->timestampPosition < 0; bool retro = (i > 0 && (timestamp->timestampPosition < (timestamp - 1)->timestampPosition)); @@ -253,17 +306,39 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, : (retro ? " <= RETROGRADE!" : ""); double latency = calculateLatencyMillis(timestamp->timestampPosition, - timestamp->timestampNanos, - timestamp->appPosition, - timestamp->appNanoseconds, - actualSampleRate); - printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n", + timestamp->timestampNanos, + timestamp->appPosition, + timestamp->appNanoseconds, + actualSampleRate); + printf(" %3d : frames %8lld, xferd %8lld", + i, + (long long) timestamp->framesTotal, + (long long) timestamp->appPosition); + printf(" STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n", (long long) timestamp->timestampPosition, (long long) timestamp->timestampNanos, latency, message); + printedGood++; } - printed++; + } + } + + if (printedGood == 0) { + printf("ERROR - AAudioStream_getTimestamp() never gave us a valid timestamp\n"); + result = AAUDIO_ERROR_INTERNAL; + } else { + // Make sure we do not get timestamps when stopped. + int64_t position; + int64_t time; + aaudio_result_t tempResult = AAudioStream_getTimestamp(aaudioStream, + CLOCK_MONOTONIC, + &position, &time); + if (tempResult != AAUDIO_ERROR_INVALID_STATE) { + printf("ERROR - AAudioStream_getTimestamp() should return" + " INVALID_STATE when stopped! %s\n", + AAudio_convertResultToText(tempResult)); + result = AAUDIO_ERROR_INTERNAL; } } @@ -273,12 +348,14 @@ static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy, } finish: + + logTestResult(isMmap, sharingMode, performanceMode, direction, result); + if (aaudioStream != nullptr) { AAudioStream_close(aaudioStream); } AAudioStreamBuilder_delete(aaudioBuilder); printf("result = %d = %s\n", result, AAudio_convertResultToText(result)); - return result; } @@ -292,7 +369,7 @@ int main(int argc, char **argv) { // in a buffer if we hang or crash. setvbuf(stdout, nullptr, _IONBF, (size_t) 0); - printf("Test Timestamps V0.1.3\n"); + printf("Test Timestamps V0.1.4\n"); // Legacy aaudio_policy_t policy = AAUDIO_POLICY_NEVER; @@ -332,5 +409,7 @@ int main(int argc, char **argv) { AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_DIRECTION_OUTPUT); + printTestResults(); + return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE; } -- GitLab From 3788f65b8730791b07c983772e3dfcdf7e17da8e Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 16 Jan 2019 16:57:22 -0800 Subject: [PATCH 0746/1530] aaudio: only return a timestamp when STARTED Otherwise return INVALID_STATE Bug: 122043542 Test: adb shell test_timestamps Change-Id: Ie9f3c96dc051aade25ee2641799fbc024196d1e4 --- media/libaaudio/src/legacy/AudioStreamRecord.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp index 40e22ac1e7..f5500898ea 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp +++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp @@ -486,6 +486,9 @@ aaudio_result_t AudioStreamRecord::getTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds) { ExtendedTimestamp extendedTimestamp; + if (getState() != AAUDIO_STREAM_STATE_STARTED) { + return AAUDIO_ERROR_INVALID_STATE; + } status_t status = mAudioRecord->getTimestamp(&extendedTimestamp); if (status == WOULD_BLOCK) { return AAUDIO_ERROR_INVALID_STATE; -- GitLab From 7668c0274deb774365ba65e526fddfda67fcbbcb Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Tue, 15 Jan 2019 20:06:35 -0800 Subject: [PATCH 0747/1530] Include updatable-media.jar in media apex Test: build Bug: 112766913 Change-Id: I281648104d67019b3cf9c007c90da573dee9e379 --- apex/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/apex/Android.bp b/apex/Android.bp index 51e4c2322d..bf295babdd 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,6 +15,7 @@ apex { name: "com.android.media", manifest: "manifest.json", + java_libs: ["updatable-media"], native_shared_libs: [ // Extractor plugins "libaacextractor", -- GitLab From aa1523c14af06a2f213e570d12ce01b71422b26a Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 17 Jan 2019 16:38:55 -0800 Subject: [PATCH 0748/1530] stagefright: allow OMX aliases by Codec2 after alias change Fix misleading strncmp, and use strncmp only for checking beginning of string. Bug: 119631295 Change-Id: Ibdffb5b1844fc12e264148a05a8f8ab89e310c47 --- media/libstagefright/MediaCodec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 25478887ce..c7da7c7c0b 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -862,9 +862,9 @@ static CodecBase *CreateCCodec() { //static sp MediaCodec::GetCodecBase(const AString &name, const char *owner) { if (owner) { - if (strncmp(owner, "default", 8) == 0) { + if (strcmp(owner, "default") == 0) { return new ACodec; - } else if (strncmp(owner, "codec2", 7) == 0) { + } else if (strncmp(owner, "codec2", 6) == 0) { return CreateCCodec(); } } -- GitLab From ed19c9be7f752daabcca83ba98e7028e13b93541 Mon Sep 17 00:00:00 2001 From: Nick Chalko Date: Thu, 25 Oct 2018 13:53:22 -0700 Subject: [PATCH 0749/1530] Add audio device type AUDIO_DEVICE_IN_HDMI_ARC ag/5360113 Bug:117967786 Test: flashall on an atom Change-Id: Ib58d8c381c27d4ffe92504a6bb48d0ca794c1956 --- media/libmedia/TypeConverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index 0ab0e9b7c0..c24e046bbe 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -74,6 +74,7 @@ const InputDeviceConverter::Table InputDeviceConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI), + MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI_ARC), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BACK_MIC), -- GitLab From fffc228d75b047cb32f2fa467aa53cda97c1b235 Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Thu, 10 Jan 2019 15:18:12 -0800 Subject: [PATCH 0750/1530] Add mediaswcodec seccomp policy. Adds a seccomp policy for mediaswcodec that's more constrained than the mediacodec seccomp policy. Bug: 116668460 Test: stracing mediaswcodec while running CTS tests. Change-Id: I110ed42dd1cacd03caa577c5104a2ac9989f7c06 --- services/mediacodec/Android.mk | 26 +++++++- services/mediacodec/main_swcodecservice.cpp | 6 +- .../seccomp_policy/mediaswcodec-arm.policy | 60 +++++++++++++++++++ .../seccomp_policy/mediaswcodec-x86.policy | 1 + 4 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 services/mediacodec/seccomp_policy/mediaswcodec-arm.policy create mode 120000 services/mediacodec/seccomp_policy/mediaswcodec-x86.policy diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 3b6dc80f76..a104ee5ced 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -69,8 +69,8 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) # seccomp is not required for coverage build. ifneq ($(NATIVE_COVERAGE),true) -LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediacodec.policy -LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediacodec.policy +LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaswcodec.policy +LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaswcodec.policy endif LOCAL_SRC_FILES := \ main_swcodecservice.cpp \ @@ -137,4 +137,26 @@ endif include $(BUILD_PREBUILT) endif +#################################################################### + +# sw service seccomp policy +ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) +include $(CLEAR_VARS) +LOCAL_MODULE := mediaswcodec.policy +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy +# mediaswcodec runs in 32-bit combatibility mode. For 64 bit architectures, +# use the 32 bit policy +ifdef TARGET_2ND_ARCH + ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) + LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_2ND_ARCH).policy + else + LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy + endif +else + LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy +endif +include $(BUILD_PREBUILT) +endif + include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index 1168825d66..12397fb4c5 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -26,12 +26,10 @@ using namespace android; -// TODO: replace policy with software codec-only policies -// Must match location in Android.mk. static const char kSystemSeccompPolicyPath[] = - "/system/etc/seccomp_policy/mediacodec.policy"; + "/system/etc/seccomp_policy/mediaswcodec.policy"; static const char kVendorSeccompPolicyPath[] = - "/vendor/etc/seccomp_policy/mediacodec.policy"; + "/vendor/etc/seccomp_policy/mediaswcodec.policy"; // Disable Scudo's mismatch allocation check, as it is being triggered // by some third party code. diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy new file mode 100644 index 0000000000..588141a297 --- /dev/null +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy @@ -0,0 +1,60 @@ +# Copyright (C) 2019 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. + +futex: 1 +# ioctl calls are filtered via the selinux policy. +ioctl: 1 +sched_yield: 1 +close: 1 +dup: 1 +ppoll: 1 +mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mmap2: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE + +# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail +# parser support for '<' is in this needs to be modified to also prevent +# |old_address| and |new_address| from touching the exception vector page, which +# on ARM is statically loaded at 0xffff 0000. See +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html +# for more details. +mremap: arg3 == 3 +munmap: 1 +prctl: 1 +getuid32: 1 +writev: 1 +sigaltstack: 1 +clone: 1 +exit: 1 +lseek: 1 +rt_sigprocmask: 1 +openat: 1 +fstat64: 1 +write: 1 +nanosleep: 1 +setpriority: 1 +set_tid_address: 1 +getdents64: 1 +readlinkat: 1 +read: 1 +pread64: 1 +fstatfs64: 1 +gettimeofday: 1 +faccessat: 1 +_llseek: 1 +fstatat64: 1 +ugetrlimit: 1 +exit_group: 1 +restart_syscall: 1 +rt_sigreturn: 1 +getrandom: 1 diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-x86.policy b/services/mediacodec/seccomp_policy/mediaswcodec-x86.policy new file mode 120000 index 0000000000..ab2592a713 --- /dev/null +++ b/services/mediacodec/seccomp_policy/mediaswcodec-x86.policy @@ -0,0 +1 @@ +mediacodec-x86.policy \ No newline at end of file -- GitLab From 7435e7da371e60bed90909be6cc57a3f111fb6b4 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Wed, 19 Dec 2018 17:09:28 -0800 Subject: [PATCH 0751/1530] Add libprocessgroup dependency and fix sched_policy include libaudioclient and libmedia are using get_sched_policy and set_sched_policy which are now implemented inside libprocessgroup. Add required dependencies and include required headers. Exempt-From-Owner-Approval: janitorial Bug: 111307099 Test: builds, boots Change-Id: I83dd6878839122e3ea71b7be6f6f312376897d56 Signed-off-by: Suren Baghdasaryan --- media/libaudioclient/Android.bp | 1 + media/libaudioclient/AudioRecord.cpp | 1 + media/libaudioclient/AudioTrack.cpp | 1 + media/libmedia/Android.bp | 1 + media/libmedia/IMediaMetadataRetriever.cpp | 1 + 5 files changed, 5 insertions(+) diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index 827df6ab9f..1417aaf18c 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { "libmediametrics", "libmediautils", "libnblog", + "libprocessgroup", "libutils", ], export_shared_lib_headers: ["libbinder"], diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 65b8a389bc..72a23e3022 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index b444d2d154..e9a0e2273d 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 3efb5de3fa..68dae56d4e 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -213,6 +213,7 @@ cc_library { "android.hidl.token@1.0-utils", "liblog", "libcutils", + "libprocessgroup", "libutils", "libbinder", "libsonivox", diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index 590ba1a55a..f9fa86ec81 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include -- GitLab From b06fb9faae0530a5b15d721878a6e07d56cfb987 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 10 Jan 2019 15:37:18 +0800 Subject: [PATCH 0752/1530] Cannot play back mov file which contains udta atom When we play back mov file which contains udta atom and udta terminates with code x00000000, parsing chunk has error. Solution is to pass udta terminated code to play file. Bug: 122749421 Test: Play the video file whose udta terminates with x00000000 and check if it can play normally Change-Id: Ifd87ffb943d019d10bba1dfd9b0d822d3ba37727 --- media/extractors/mp4/MPEG4Extractor.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 04413592ff..1ab9c53ed2 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -985,6 +985,22 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { off64_t stop_offset = *offset + chunk_size; *offset = data_offset; while (*offset < stop_offset) { + + // pass udata terminate + if (mIsQT && stop_offset - *offset == 4 && chunk_type == FOURCC("udta")) { + // handle the case that udta terminates with terminate code x00000000 + // note that 0 terminator is optional and we just handle this case. + uint32_t terminate_code = 1; + mDataSource->readAt(*offset, &terminate_code, 4); + if (0 == terminate_code) { + *offset += 4; + ALOGD("Terminal code for udta"); + continue; + } else { + ALOGW("invalid udta Terminal code"); + } + } + status_t err = parseChunk(offset, depth + 1); if (err != OK) { if (isTrack) { -- GitLab From b2a7ed2e9d10d3aa277b0457a2601c99217e273f Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 18 Jan 2019 10:34:03 -0800 Subject: [PATCH 0753/1530] Fix test failures Bug: 123077756 Test: CTS Change-Id: I2b440fef382bd7a6b5d956defd7715ec614f8d31 --- include/media/MediaExtractorPluginHelper.h | 3 +++ include/media/MediaTrack.h | 3 ++- media/libstagefright/MediaExtractor.cpp | 2 +- media/libstagefright/MediaTrack.cpp | 7 +++++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h index f4d4da630f..b86f177e00 100644 --- a/include/media/MediaExtractorPluginHelper.h +++ b/include/media/MediaExtractorPluginHelper.h @@ -171,6 +171,9 @@ protected: }; inline CMediaTrack *wrap(MediaTrackHelper *track) { + if (track == nullptr) { + return nullptr; + } CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack)); wrapper->data = track; wrapper->free = [](void *data) -> void { diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h index e828a7f81e..493eba3bd3 100644 --- a/include/media/MediaTrack.h +++ b/include/media/MediaTrack.h @@ -142,7 +142,7 @@ private: class MediaTrackCUnwrapper : public MediaTrack { public: - explicit MediaTrackCUnwrapper(CMediaTrack *wrapper); + static MediaTrackCUnwrapper *create(CMediaTrack *wrapper); virtual status_t start(); virtual status_t stop(); @@ -155,6 +155,7 @@ protected: virtual ~MediaTrackCUnwrapper(); private: + explicit MediaTrackCUnwrapper(CMediaTrack *wrapper); CMediaTrack *wrapper; MediaBufferGroup *bufferGroup; }; diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 95119310b4..4ed3382135 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -57,7 +57,7 @@ size_t MediaExtractorCUnwrapper::countTracks() { } MediaTrack *MediaExtractorCUnwrapper::getTrack(size_t index) { - return new MediaTrackCUnwrapper(plugin->getTrack(plugin->data, index)); + return MediaTrackCUnwrapper::create(plugin->getTrack(plugin->data, index)); } status_t MediaExtractorCUnwrapper::getTrackMetaData( diff --git a/media/libstagefright/MediaTrack.cpp b/media/libstagefright/MediaTrack.cpp index 036e79d523..89c9b25668 100644 --- a/media/libstagefright/MediaTrack.cpp +++ b/media/libstagefright/MediaTrack.cpp @@ -65,6 +65,13 @@ MediaTrackCUnwrapper::MediaTrackCUnwrapper(CMediaTrack *cmediatrack) { bufferGroup = nullptr; } +MediaTrackCUnwrapper *MediaTrackCUnwrapper::create(CMediaTrack *cmediatrack) { + if (cmediatrack == nullptr) { + return nullptr; + } + return new MediaTrackCUnwrapper(cmediatrack); +} + MediaTrackCUnwrapper::~MediaTrackCUnwrapper() { wrapper->free(wrapper->data); free(wrapper); -- GitLab From 7f0f4312d70c0712b23c3af594e0f8ba02bab742 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 18 Jan 2019 10:47:14 -0800 Subject: [PATCH 0754/1530] audi policy manager: fix use after free in initialize() Fix regression introduced by commit 11d30104 in cleanup loop in AudioPolicyManager::initialize(). Bug: 123080575 Test: make Change-Id: I048eeb5f5c6e6bc83dd2b31af4dd600bc07ab1a2 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 30b438d17e..5c8a799d50 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4070,7 +4070,8 @@ status_t AudioPolicyManager::initialize() { } // make sure all attached devices have been allocated a unique ID auto checkAndSetAvailable = [this](auto& devices) { - for (const auto &device : devices) { + for (size_t i = 0; i < devices.size();) { + const auto &device = devices[i]; if (!device->isAttached()) { ALOGW("device %s is unreachable", device->toString().c_str()); devices.remove(device); @@ -4078,6 +4079,7 @@ status_t AudioPolicyManager::initialize() { } // Device is now validated and can be appended to the available devices of the engine mEngine->setDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE); + i++; } }; checkAndSetAvailable(mAvailableOutputDevices); -- GitLab From 078b58e99bf28450f225fea1357d216c0d76cb3f Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 9 Jan 2019 15:08:06 -0800 Subject: [PATCH 0755/1530] CCodec: read # of input/output slots from component Bug: 112362633 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I019f638e897c0131788a117a2259bf664fd33a10 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 79 +++++++++---------- media/codec2/sfplugin/CCodecBufferChannel.h | 5 ++ 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 432b8c00ee..b529cbc3d5 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -271,12 +271,8 @@ private: namespace { -// TODO: get this info from component -const static size_t kMinInputBufferArraySize = 4; -const static size_t kMaxPipelineCapacity = 18; -const static size_t kChannelOutputDelay = 0; -const static size_t kMinOutputBufferArraySize = kMaxPipelineCapacity + - kChannelOutputDelay; +const static size_t kSmoothnessFactor = 4; +const static size_t kRenderingDepth = 3; const static size_t kLinearBufferSize = 1048576; // This can fit 4K RGBA frame, and most likely client won't need more than this. const static size_t kMaxLinearBufferSize = 3840 * 2160 * 4; @@ -829,6 +825,7 @@ public: const sp &crypto, int32_t heapSeqNum, size_t capacity, + size_t numInputSlots, const char *componentName, const char *name = "EncryptedInput") : LinearInputBuffers(componentName, name), mUsage({0, 0}), @@ -840,7 +837,7 @@ public: } else { mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; } - for (size_t i = 0; i < kMinInputBufferArraySize; ++i) { + for (size_t i = 0; i < numInputSlots; ++i) { sp memory = mDealer->allocate(capacity); if (memory == nullptr) { ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", mName, i); @@ -951,11 +948,12 @@ private: class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers { public: - GraphicInputBuffers(const char *componentName, const char *name = "2D-BB-Input") + GraphicInputBuffers( + size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input") : InputBuffers(componentName, name), mImpl(mName), mLocalBufferPool(LocalBufferPool::Create( - kMaxLinearBufferSize * kMinInputBufferArraySize)) { } + kMaxLinearBufferSize * numInputSlots)) { } ~GraphicInputBuffers() override = default; bool requestNewBuffer(size_t *index, sp *buffer) override { @@ -1291,10 +1289,11 @@ public: class RawGraphicOutputBuffers : public FlexOutputBuffers { public: - RawGraphicOutputBuffers(const char *componentName, const char *name = "2D-BB-Output") + RawGraphicOutputBuffers( + size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output") : FlexOutputBuffers(componentName, name), mLocalBufferPool(LocalBufferPool::Create( - kMaxLinearBufferSize * kMinOutputBufferArraySize)) { } + kMaxLinearBufferSize * numOutputSlots)) { } ~RawGraphicOutputBuffers() override = default; sp wrap(const std::shared_ptr &buffer) override { @@ -1545,6 +1544,8 @@ CCodecBufferChannel::CCodecBufferChannel( const std::shared_ptr &callback) : mHeapSeqNum(-1), mCCodecCallback(callback), + mNumInputSlots(kSmoothnessFactor), + mNumOutputSlots(kSmoothnessFactor), mFrameIndex(0u), mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), @@ -2006,7 +2007,7 @@ void CCodecBufferChannel::getInputBufferArray(Vector> *arra Mutexed>::Locked buffers(mInputBuffers); if (!(*buffers)->isArrayMode()) { - *buffers = (*buffers)->toArrayMode(kMinInputBufferArraySize); + *buffers = (*buffers)->toArrayMode(mNumInputSlots); } (*buffers)->getArray(array); @@ -2017,7 +2018,7 @@ void CCodecBufferChannel::getOutputBufferArray(Vector> *arr Mutexed>::Locked buffers(mOutputBuffers); if (!(*buffers)->isArrayMode()) { - *buffers = (*buffers)->toArrayMode(kMinOutputBufferArraySize); + *buffers = (*buffers)->toArrayMode(mNumOutputSlots); } (*buffers)->getArray(array); @@ -2029,12 +2030,19 @@ status_t CCodecBufferChannel::start( C2StreamBufferTypeSetting::output oStreamFormat(0u); C2PortReorderBufferDepthTuning::output reorderDepth; C2PortReorderKeySetting::output reorderKey; + C2PortActualDelayTuning::input inputDelay(0); + C2PortActualDelayTuning::output outputDelay(0); + C2ActualPipelineDelayTuning pipelineDelay(0); + c2_status_t err = mComponent->query( { &iStreamFormat, &oStreamFormat, &reorderDepth, &reorderKey, + &inputDelay, + &pipelineDelay, + &outputDelay, }, {}, C2_DONT_BLOCK, @@ -2057,6 +2065,13 @@ status_t CCodecBufferChannel::start( reorder->setKey(reorderKey.value); } } + + mNumInputSlots = + (inputDelay ? inputDelay.value : 0) + + (pipelineDelay ? pipelineDelay.value : 0) + + kSmoothnessFactor; + mNumOutputSlots = (outputDelay ? outputDelay.value : 0) + kSmoothnessFactor; + // TODO: get this from input format bool secure = mComponent->getName().find(".secure") != std::string::npos; @@ -2135,7 +2150,7 @@ status_t CCodecBufferChannel::start( } else if (mMetaMode == MODE_ANW) { buffers->reset(new GraphicMetadataInputBuffers(mName)); } else { - buffers->reset(new GraphicInputBuffers(mName)); + buffers->reset(new GraphicInputBuffers(mNumInputSlots, mName)); } } else { if (hasCryptoOrDescrambler()) { @@ -2148,7 +2163,7 @@ status_t CCodecBufferChannel::start( if (mDealer == nullptr) { mDealer = new MemoryDealer( align(capacity, MemoryDealer::getAllocationAlignment()) - * (kMinInputBufferArraySize + 1), + * (mNumInputSlots + 1), "EncryptedLinearInputBuffers"); mDecryptDestination = mDealer->allocate((size_t)capacity); } @@ -2158,7 +2173,8 @@ status_t CCodecBufferChannel::start( mHeapSeqNum = -1; } buffers->reset(new EncryptedLinearInputBuffers( - secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity, mName)); + secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity, + mNumInputSlots, mName)); forceArrayMode = true; } else { buffers->reset(new LinearInputBuffers(mName)); @@ -2173,7 +2189,7 @@ status_t CCodecBufferChannel::start( } if (forceArrayMode) { - *buffers = (*buffers)->toArrayMode(kMinInputBufferArraySize); + *buffers = (*buffers)->toArrayMode(mNumInputSlots); } } @@ -2292,7 +2308,7 @@ status_t CCodecBufferChannel::start( if (outputSurface) { buffers->reset(new GraphicOutputBuffers(mName)); } else { - buffers->reset(new RawGraphicOutputBuffers(mName)); + buffers->reset(new RawGraphicOutputBuffers(mNumOutputSlots, mName)); } } else { buffers->reset(new LinearOutputBuffers(mName)); @@ -2313,7 +2329,7 @@ status_t CCodecBufferChannel::start( // WORKAROUND: if we're using early CSD workaround we convert to // array mode, to appease apps assuming the output // buffers to be of the same size. - (*buffers) = (*buffers)->toArrayMode(kMinOutputBufferArraySize); + (*buffers) = (*buffers)->toArrayMode(mNumOutputSlots); int32_t channelCount; int32_t sampleRate; @@ -2341,27 +2357,10 @@ status_t CCodecBufferChannel::start( // about buffers from the previous generation do not interfere with the // newly initialized pipeline capacity. - // Query delays - C2PortRequestedDelayTuning::input inputDelay; - C2PortRequestedDelayTuning::output outputDelay; - C2RequestedPipelineDelayTuning pipelineDelay; -#if 0 - err = mComponent->query( - { &inputDelay, &pipelineDelay, &outputDelay }, - {}, - C2_DONT_BLOCK, - nullptr); mAvailablePipelineCapacity.initialize( - inputDelay, - inputDelay + pipelineDelay, - inputDelay + pipelineDelay + outputDelay, + mNumInputSlots, + mNumInputSlots + mNumOutputSlots, mName); -#else - mAvailablePipelineCapacity.initialize( - kMinInputBufferArraySize, - kMaxPipelineCapacity, - mName); -#endif mInputMetEos = false; mSync.start(); @@ -2380,7 +2379,7 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { } std::vector> toBeQueued; // TODO: use proper buffer depth instead of this random value - for (size_t i = 0; i < kMinInputBufferArraySize; ++i) { + for (size_t i = 0; i < mNumInputSlots; ++i) { size_t index; sp buffer; { @@ -2737,7 +2736,7 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { sp producer; if (newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - newSurface->setMaxDequeuedBufferCount(kMinOutputBufferArraySize); + newSurface->setMaxDequeuedBufferCount(mNumOutputSlots + kRenderingDepth); producer = newSurface->getIGraphicBufferProducer(); producer->setGenerationNumber(generation); } else { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 431baaa990..fd806b7dc0 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -37,6 +37,8 @@ namespace android { +class MemoryDealer; + class CCodecCallback { public: virtual ~CCodecCallback() = default; @@ -233,6 +235,9 @@ private: QueueSync mQueueSync; std::vector> mParamsToBeSet; + size_t mNumInputSlots; + size_t mNumOutputSlots; + Mutexed> mInputBuffers; Mutexed>> mFlushedConfigs; Mutexed> mOutputBuffers; -- GitLab From a53ffa67b35a7e57efec57811e4f915d1caac1eb Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 10 Oct 2018 16:21:37 -0700 Subject: [PATCH 0756/1530] aaudio: rename timestamp variables There were several different timestamp variables with slightly different meaning but with the same name. That made it hard to debug the code. Test: atest CtsNativeMediaAAudioTestCases Change-Id: Iebf438676925c698377abe02c81f3d559829318a --- media/libaaudio/src/client/AudioStreamInternal.cpp | 10 +++++----- media/libaaudio/src/client/AudioStreamInternal.h | 2 +- .../libaaudio/src/client/AudioStreamInternalPlay.cpp | 2 +- services/oboeservice/AAudioServiceEndpoint.h | 2 +- services/oboeservice/AAudioServiceEndpointShared.cpp | 4 ++-- services/oboeservice/AAudioServiceStreamBase.cpp | 11 ++++++++--- services/oboeservice/AAudioServiceStreamBase.h | 2 +- services/oboeservice/AAudioServiceStreamMMAP.cpp | 6 +++--- services/oboeservice/AAudioServiceStreamShared.cpp | 6 +++--- 9 files changed, 25 insertions(+), 20 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index 9c682b702e..3b03601282 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -62,7 +62,7 @@ AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterfa , mServiceStreamHandle(AAUDIO_HANDLE_INVALID) , mInService(inService) , mServiceInterface(serviceInterface) - , mAtomicTimestamp() + , mAtomicInternalTimestamp() , mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND) , mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND) { @@ -363,7 +363,7 @@ aaudio_result_t AudioStreamInternal::requestStop() { mClockModel.stop(AudioClock::getNanoseconds()); setState(AAUDIO_STREAM_STATE_STOPPING); - mAtomicTimestamp.clear(); + mAtomicInternalTimestamp.clear(); return mServiceInterface.stopStream(mServiceStreamHandle); } @@ -412,8 +412,8 @@ aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds) { // Generated in server and passed to client. Return latest. - if (mAtomicTimestamp.isValid()) { - Timestamp timestamp = mAtomicTimestamp.read(); + if (mAtomicInternalTimestamp.isValid()) { + Timestamp timestamp = mAtomicInternalTimestamp.read(); int64_t position = timestamp.getPosition() + mFramesOffsetFromService; if (position >= 0) { *framePosition = position; @@ -460,7 +460,7 @@ aaudio_result_t AudioStreamInternal::onTimestampService(AAudioServiceMessage *me aaudio_result_t AudioStreamInternal::onTimestampHardware(AAudioServiceMessage *message) { Timestamp timestamp(message->timestamp.position, message->timestamp.timestamp); - mAtomicTimestamp.write(timestamp); + mAtomicInternalTimestamp.write(timestamp); return AAUDIO_OK; } diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h index 3bb9e1ea1b..1c88f529b9 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.h +++ b/media/libaaudio/src/client/AudioStreamInternal.h @@ -163,7 +163,7 @@ protected: AAudioServiceInterface &mServiceInterface; // abstract interface to the service - SimpleDoubleBuffer mAtomicTimestamp; + SimpleDoubleBuffer mAtomicInternalTimestamp; AtomicRequestor mNeedCatchUp; // Ask read() or write() to sync on first timestamp. diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 0884fca378..6af8e7d1e5 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -71,7 +71,7 @@ aaudio_result_t AudioStreamInternalPlay::requestPause() mClockModel.stop(AudioClock::getNanoseconds()); setState(AAUDIO_STREAM_STATE_PAUSING); - mAtomicTimestamp.clear(); + mAtomicInternalTimestamp.clear(); return mServiceInterface.pauseStream(mServiceStreamHandle); } diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h index 43b0a373da..3616fa2949 100644 --- a/services/oboeservice/AAudioServiceEndpoint.h +++ b/services/oboeservice/AAudioServiceEndpoint.h @@ -121,7 +121,7 @@ protected: mutable std::mutex mLockStreams; std::vector> mRegisteredStreams; - SimpleDoubleBuffer mAtomicTimestamp; + SimpleDoubleBuffer mAtomicEndpointTimestamp; android::AudioClient mMmapClient; // set in open, used in open and startStream diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp index 2f1ec7e00c..0a415fdae7 100644 --- a/services/oboeservice/AAudioServiceEndpointShared.cpp +++ b/services/oboeservice/AAudioServiceEndpointShared.cpp @@ -181,8 +181,8 @@ aaudio_result_t AAudioServiceEndpointShared::stopStream(sp>>>>>>>>>>>>> TIMESTAMPS", __func__, getTypeText()); TimestampScheduler timestampScheduler; timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate()); timestampScheduler.start(AudioClock::getNanoseconds()); int64_t nextTime = timestampScheduler.nextAbsoluteTime(); + int32_t loopCount = 0; while(mThreadEnabled.load()) { + loopCount++; if (AudioClock::getNanoseconds() >= nextTime) { aaudio_result_t result = sendCurrentTimestamp(); if (result != AAUDIO_OK) { + ALOGE("%s() timestamp thread got result = %d", __func__, result); break; } nextTime = timestampScheduler.nextAbsoluteTime(); @@ -310,7 +314,8 @@ void AAudioServiceStreamBase::run() { AudioClock::sleepUntilNanoTime(nextTime); } } - ALOGD("%s() %s exiting <<<<<<<<<<<<<< TIMESTAMPS", __func__, getTypeText()); + ALOGD("%s() %s exiting after %d loops <<<<<<<<<<<<<< TIMESTAMPS", + __func__, getTypeText(), loopCount); } void AAudioServiceStreamBase::disconnect() { diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h index 7904b25182..ffc768b79a 100644 --- a/services/oboeservice/AAudioServiceStreamBase.h +++ b/services/oboeservice/AAudioServiceStreamBase.h @@ -301,7 +301,7 @@ protected: // TODO rename mClientHandle to mPortHandle to be more consistent with AudioFlinger. audio_port_handle_t mClientHandle = AUDIO_PORT_HANDLE_NONE; - SimpleDoubleBuffer mAtomicTimestamp; + SimpleDoubleBuffer mAtomicStreamTimestamp; android::AAudioService &mAudioService; diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp index 9377945f08..837b080fae 100644 --- a/services/oboeservice/AAudioServiceStreamMMAP.cpp +++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp @@ -162,7 +162,7 @@ aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positio aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos); if (result == AAUDIO_OK) { Timestamp timestamp(*positionFrames, *timeNanos); - mAtomicTimestamp.write(timestamp); + mAtomicStreamTimestamp.write(timestamp); *positionFrames = timestamp.getPosition(); *timeNanos = timestamp.getNanoseconds(); } else if (result != AAUDIO_ERROR_UNAVAILABLE) { @@ -184,8 +184,8 @@ aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionF static_cast(endpoint.get()); // TODO Get presentation timestamp from the HAL - if (mAtomicTimestamp.isValid()) { - Timestamp timestamp = mAtomicTimestamp.read(); + if (mAtomicStreamTimestamp.isValid()) { + Timestamp timestamp = mAtomicStreamTimestamp.read(); *positionFrames = timestamp.getPosition(); *timeNanos = timestamp.getNanoseconds() + serviceEndpointMMAP->getHardwareTimeOffsetNanos(); return AAUDIO_OK; diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp index d5450fe987..14742dd112 100644 --- a/services/oboeservice/AAudioServiceStreamShared.cpp +++ b/services/oboeservice/AAudioServiceStreamShared.cpp @@ -238,15 +238,15 @@ aaudio_result_t AAudioServiceStreamShared::getAudioDataDescription( } void AAudioServiceStreamShared::markTransferTime(Timestamp ×tamp) { - mAtomicTimestamp.write(timestamp); + mAtomicStreamTimestamp.write(timestamp); } // Get timestamp that was written by mixer or distributor. aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) { // TODO Get presentation timestamp from the HAL - if (mAtomicTimestamp.isValid()) { - Timestamp timestamp = mAtomicTimestamp.read(); + if (mAtomicStreamTimestamp.isValid()) { + Timestamp timestamp = mAtomicStreamTimestamp.read(); *positionFrames = timestamp.getPosition(); *timeNanos = timestamp.getNanoseconds(); return AAUDIO_OK; -- GitLab From 4e4647077f00c47650d6ecd66cd812073371bd10 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Thu, 10 Jan 2019 23:38:46 -0800 Subject: [PATCH 0757/1530] audiopolicy: Add support for hybrid mode on A2DP Add support in AudioPolicyManager to select a module, device or output based on device type and codec. Implement hybrid mode with A2DP which enables runtime switching between HALs for A2DP based on offload support for specific A2DP codecs. Optimize A2DP active device change and device config change in AudioPolicyManager. Bug: 111812273 Test: make Change-Id: I246d71dd08bacbca6ed9b0012e7d7698bd8a0953 --- media/libaudioclient/AudioSystem.cpp | 10 +- media/libaudioclient/IAudioPolicyService.cpp | 17 +- .../include/media/AudioSystem.h | 6 +- .../include/media/IAudioPolicyService.h | 6 +- services/audiopolicy/AudioPolicyInterface.h | 6 +- services/audiopolicy/common/include/policy.h | 15 ++ .../include/AudioOutputDescriptor.h | 1 + .../include/DeviceDescriptor.h | 18 +- .../managerdefinitions/include/HwModule.h | 11 +- .../managerdefinitions/include/IOProfile.h | 15 +- .../src/AudioOutputDescriptor.cpp | 14 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 3 +- .../src/DeviceDescriptor.cpp | 55 ++++- .../managerdefinitions/src/HwModule.cpp | 54 +++-- .../managerdefinitions/src/Serializer.cpp | 10 +- .../audiopolicy/enginedefault/src/Engine.cpp | 2 +- .../managerdefault/AudioPolicyManager.cpp | 193 ++++++++++++------ .../managerdefault/AudioPolicyManager.h | 9 +- .../service/AudioPolicyInterfaceImpl.cpp | 10 +- .../audiopolicy/service/AudioPolicyService.h | 6 +- 20 files changed, 334 insertions(+), 127 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index baeae8b709..0beb65de91 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -784,7 +784,8 @@ const sp AudioSystem::get_audio_policy_service() status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { const sp& aps = AudioSystem::get_audio_policy_service(); const char *address = ""; @@ -798,7 +799,7 @@ status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, if (device_name != NULL) { name = device_name; } - return aps->setDeviceConnectionState(device, state, address, name); + return aps->setDeviceConnectionState(device, state, address, name, encodedFormat); } audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device, @@ -812,7 +813,8 @@ audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t d status_t AudioSystem::handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { const sp& aps = AudioSystem::get_audio_policy_service(); const char *address = ""; @@ -826,7 +828,7 @@ status_t AudioSystem::handleDeviceConfigChange(audio_devices_t device, if (device_name != NULL) { name = device_name; } - return aps->handleDeviceConfigChange(device, address, name); + return aps->handleDeviceConfigChange(device, address, name, encodedFormat); } status_t AudioSystem::setPhoneState(audio_mode_t state) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 272415c59f..0fae2a1f07 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -108,7 +108,8 @@ public: audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -116,6 +117,7 @@ public: data.writeInt32(static_cast (state)); data.writeCString(device_address); data.writeCString(device_name); + data.writeInt32(static_cast (encodedFormat)); remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply); return static_cast (reply.readInt32()); } @@ -134,13 +136,15 @@ public: virtual status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast (device)); data.writeCString(device_address); data.writeCString(device_name); + data.writeInt32(static_cast (encodedFormat)); remote()->transact(HANDLE_DEVICE_CONFIG_CHANGE, data, &reply); return static_cast (reply.readInt32()); } @@ -1121,6 +1125,7 @@ status_t BnAudioPolicyService::onTransact( static_cast (data.readInt32()); const char *device_address = data.readCString(); const char *device_name = data.readCString(); + audio_format_t codecFormat = static_cast (data.readInt32()); if (device_address == nullptr || device_name == nullptr) { ALOGE("Bad Binder transaction: SET_DEVICE_CONNECTION_STATE for device %u", device); reply->writeInt32(static_cast (BAD_VALUE)); @@ -1128,7 +1133,8 @@ status_t BnAudioPolicyService::onTransact( reply->writeInt32(static_cast (setDeviceConnectionState(device, state, device_address, - device_name))); + device_name, + codecFormat))); } return NO_ERROR; } break; @@ -1154,13 +1160,16 @@ status_t BnAudioPolicyService::onTransact( static_cast (data.readInt32()); const char *device_address = data.readCString(); const char *device_name = data.readCString(); + audio_format_t codecFormat = + static_cast (data.readInt32()); if (device_address == nullptr || device_name == nullptr) { ALOGE("Bad Binder transaction: HANDLE_DEVICE_CONFIG_CHANGE for device %u", device); reply->writeInt32(static_cast (BAD_VALUE)); } else { reply->writeInt32(static_cast (handleDeviceConfigChange(device, device_address, - device_name))); + device_name, + codecFormat))); } return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 781e9dfc41..2b4a373558 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -209,12 +209,14 @@ public: // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions) // static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address, const char *device_name); + const char *device_address, const char *device_name, + audio_format_t encodedFormat); static audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); static status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); static status_t setPhoneState(audio_mode_t state); static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index fb4fe9302f..75de8e9725 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -44,12 +44,14 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) = 0; + const char *device_name, + audio_format_t encodedFormat) = 0; virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address) = 0; virtual status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) = 0; + const char *device_name, + audio_format_t encodedFormat) = 0; virtual status_t setPhoneState(audio_mode_t state) = 0; virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) = 0; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 1c2b9d7c8b..8bfb7360c5 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -75,14 +75,16 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) = 0; + const char *device_name, + audio_format_t encodedFormat) = 0; // retrieve a device connection status virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address) = 0; // indicate a change in device configuration virtual status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) = 0; + const char *device_name, + audio_format_t encodedFormat) = 0; // indicate a change in phone state. Valid phones states are defined by audio_mode_t virtual void setPhoneState(audio_mode_t state) = 0; // force using a specific device category for the specified usage diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 46a2a40453..837ca47fa3 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -75,6 +75,21 @@ static inline bool device_distinguishes_on_address(audio_devices_t device) ((device & APM_AUDIO_DEVICE_OUT_MATCH_ADDRESS_ALL) != 0)); } +/** + * Check whether audio device has encoding capability. + * + * @param[in] device to consider + * + * @return true if device has encoding capability, false otherwise.. + */ +static inline bool device_has_encoding_capability(audio_devices_t device) +{ + if (device & AUDIO_DEVICE_OUT_ALL_A2DP) { + return true; + } + return false; +} + /** * Returns the priority of a given audio source for capture. The priority is used when more than one * capture session is active on a given input stream to determine which session drives routing and diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 14b995b25a..e1ecc61eda 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -154,6 +154,7 @@ public: void setDevices(const DeviceVector &devices) { mDevices = devices; } bool sharesHwModuleWith(const sp& outputDesc); virtual DeviceVector supportedDevices() const; + virtual bool deviceSupportsEncodedFormats(audio_devices_t device); virtual uint32_t latency(); virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } virtual bool isFixedVolume(audio_devices_t device); diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index b581665caf..cc43fe69e0 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -44,8 +44,18 @@ public: const FormatVector& encodedFormats() const { return mEncodedFormats; } + audio_format_t getEncodedFormat() { return mCurrentEncodedFormat; } + + void setEncodedFormat(audio_format_t format) { + mCurrentEncodedFormat = format; + } + bool equals(const sp& other) const; + bool hasCurrentEncodedFormat() const; + + bool supportsFormat(audio_format_t format); + // AudioPortConfig virtual sp getAudioPort() const { return (AudioPort*) this; } virtual void toAudioPortConfig(struct audio_port_config *dstConfig, @@ -69,6 +79,7 @@ private: audio_devices_t mDeviceType; FormatVector mEncodedFormats; audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE; + audio_format_t mCurrentEncodedFormat; }; class DeviceVector : public SortedVector > @@ -88,9 +99,10 @@ public: audio_devices_t types() const { return mDeviceTypes; } - // If 'address' is empty, a device with a non-empty address may be returned - // if there is no device with the specified 'type' and empty address. - sp getDevice(audio_devices_t type, const String8 &address = {}) const; + // If 'address' is empty and 'codec' is AUDIO_FORMAT_DEFAULT, a device with a non-empty + // address may be returned if there is no device with the specified 'type' and empty address. + sp getDevice(audio_devices_t type, const String8 &address, + audio_format_t codec) const; DeviceVector getDevicesFromTypeMask(audio_devices_t types) const; /** diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h index d7dc4b009e..eb34da448a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -130,9 +130,11 @@ class HwModuleCollection : public Vector > public: sp getModuleFromName(const char *name) const; - sp getModuleForDeviceTypes(audio_devices_t device) const; + sp getModuleForDeviceTypes(audio_devices_t device, + audio_format_t encodedFormat) const; - sp getModuleForDevice(const sp &device) const; + sp getModuleForDevice(const sp &device, + audio_format_t encodedFormat) const; DeviceVector getAvailableDevicesFromModuleName(const char *name, const DeviceVector &availableDevices) const; @@ -149,6 +151,7 @@ public: * @param type of the device requested * @param address of the device requested * @param name of the device that requested + * @param encodedFormat if not AUDIO_FORMAT_DEFAULT, must match one supported format * @param matchAddress true if a strong match is required * @param allowToCreate true if allowed to create dynamic device (e.g. hdmi, usb...) * @return device descriptor associated to the type (and address if matchAddress is true) @@ -156,6 +159,7 @@ public: sp getDeviceDescriptor(const audio_devices_t type, const char *address, const char *name, + audio_format_t encodedFormat, bool allowToCreate = false, bool matchAddress = true) const; @@ -171,7 +175,8 @@ public: */ sp createDevice(const audio_devices_t type, const char *address, - const char *name) const; + const char *name, + const audio_format_t encodedFormat) const; /** * @brief cleanUpForDevice: loop on all profiles of all modules to remove device from diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index d0c05a5af3..dc409a7439 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -94,7 +94,10 @@ public: bool supportsDeviceTypes(audio_devices_t device) const { if (audio_is_output_devices(device)) { - return mSupportedDevices.types() & device; + if (deviceSupportsEncodedFormats(device)) { + return mSupportedDevices.types() & device; + } + return false; } return mSupportedDevices.types() & (device & ~AUDIO_DEVICE_BIT_IN); } @@ -116,6 +119,16 @@ public: return mSupportedDevices.contains(device); } + bool deviceSupportsEncodedFormats(audio_devices_t device) const + { + DeviceVector deviceList = + mSupportedDevices.getDevicesFromTypeMask(device); + if (!deviceList.empty()) { + return deviceList.itemAt(0)->hasCurrentEncodedFormat(); + } + return false; + } + void clearSupportedDevices() { mSupportedDevices.clear(); } void addSupportedDevice(const sp &device) { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 643cbd100d..57328f0ed7 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -364,6 +364,16 @@ DeviceVector SwAudioOutputDescriptor::filterSupportedDevices(const DeviceVector return filteredDevices.filter(devices); } +bool SwAudioOutputDescriptor::deviceSupportsEncodedFormats(audio_devices_t device) +{ + if (isDuplicated()) { + return (mOutput1->deviceSupportsEncodedFormats(device) + || mOutput2->deviceSupportsEncodedFormats(device)); + } else { + return mProfile->deviceSupportsEncodedFormats(device); + } +} + uint32_t SwAudioOutputDescriptor::latency() { if (isDuplicated()) { @@ -687,7 +697,9 @@ audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const for (size_t i = 0; i < size(); i++) { sp outputDesc = valueAt(i); if (!outputDesc->isDuplicated() && - outputDesc->devices().types() & AUDIO_DEVICE_OUT_ALL_A2DP) { + outputDesc->devices().types() & AUDIO_DEVICE_OUT_ALL_A2DP && + outputDesc->deviceSupportsEncodedFormats( + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP)) { return this->keyAt(i); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index d18091ca25..799950c534 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -296,7 +296,8 @@ sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( // assuming PolicyMix only for remote submix for input // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; - auto mixDevice = availDevices.getDevice(device, mix->mDeviceAddress); + auto mixDevice = + availDevices.getDevice(device, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT); if (mixDevice != nullptr) { if (policyMix != NULL) { *policyMix = mix; diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 01111c5150..2ed8455458 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #include +#include #include "DeviceDescriptor.h" #include "TypeConverter.h" #include "AudioGain.h" @@ -37,6 +38,7 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc AUDIO_PORT_ROLE_SOURCE), mTagName(tagName), mDeviceType(type), mEncodedFormats(encodedFormats) { + mCurrentEncodedFormat = AUDIO_FORMAT_DEFAULT; if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { mAddress = String8("0"); } @@ -45,6 +47,12 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc mEncodedFormats.add(AUDIO_FORMAT_AC3); mEncodedFormats.add(AUDIO_FORMAT_IEC61937); } + // For backward compatibility always indicate support for SBC and AAC if no + // supported format is listed in the configuration file + if ((type & AUDIO_DEVICE_OUT_ALL_A2DP) != 0 && mEncodedFormats.isEmpty()) { + mEncodedFormats.add(AUDIO_FORMAT_SBC); + mEncodedFormats.add(AUDIO_FORMAT_AAC); + } } audio_port_handle_t DeviceDescriptor::getId() const @@ -58,21 +66,49 @@ void DeviceDescriptor::attach(const sp& module) mId = getNextUniqueId(); } -void DeviceDescriptor::detach() -{ +void DeviceDescriptor::detach() { mId = AUDIO_PORT_HANDLE_NONE; AudioPort::detach(); } +template +bool checkEqual(const T& f1, const T& f2) +{ + std::set s1(f1.begin(), f1.end()); + std::set s2(f2.begin(), f2.end()); + return s1 == s2; +} + bool DeviceDescriptor::equals(const sp& other) const { // Devices are considered equal if they: // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE) // - have the same address + // - have the same encodingFormats (if device supports encoding) if (other == 0) { return false; } - return (mDeviceType == other->mDeviceType) && (mAddress == other->mAddress); + + return (mDeviceType == other->mDeviceType) && (mAddress == other->mAddress) && + checkEqual(mEncodedFormats, other->mEncodedFormats); +} + +bool DeviceDescriptor::hasCurrentEncodedFormat() const +{ + if (!device_has_encoding_capability(type())) { + return true; + } + return (mCurrentEncodedFormat != AUDIO_FORMAT_DEFAULT); +} + +bool DeviceDescriptor::supportsFormat(audio_format_t format) +{ + for (const auto& devFormat : mEncodedFormats) { + if (devFormat == format) { + return true; + } + } + return false; } void DeviceVector::refreshTypes() @@ -167,12 +203,17 @@ audio_devices_t DeviceVector::getDeviceTypesFromHwModule(audio_module_handle_t m return deviceTypes; } -sp DeviceVector::getDevice(audio_devices_t type, const String8& address) const +sp DeviceVector::getDevice(audio_devices_t type, const String8& address, + audio_format_t format) const { sp device; for (size_t i = 0; i < size(); i++) { if (itemAt(i)->type() == type) { - if (address == "" || itemAt(i)->address() == address) { + // Assign device if address is empty or matches and + // format is default or matches + if (((address == "" || itemAt(i)->address() == address) && + format == AUDIO_FORMAT_DEFAULT) || + itemAt(i)->supportsFormat(format)) { device = itemAt(i); if (itemAt(i)->address() == address) { break; @@ -180,8 +221,8 @@ sp DeviceVector::getDevice(audio_devices_t type, const String8 } } } - ALOGV("DeviceVector::%s() for type %08x address \"%s\" found %p", - __func__, type, address.string(), device.get()); + ALOGV("DeviceVector::%s() for type %08x address \"%s\" found %p format %08x", + __func__, type, address.string(), device.get(), format); return device; } diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 7d2d094f4e..85d9bcec88 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -273,32 +273,34 @@ sp HwModuleCollection::getModuleFromName(const char *name) const return nullptr; } -sp HwModuleCollection::getModuleForDeviceTypes(audio_devices_t device) const +sp HwModuleCollection::getModuleForDeviceTypes(audio_devices_t type, + audio_format_t encodedFormat) const { for (const auto& module : *this) { - const auto& profiles = audio_is_output_device(device) ? + const auto& profiles = audio_is_output_device(type) ? module->getOutputProfiles() : module->getInputProfiles(); for (const auto& profile : profiles) { - if (profile->supportsDeviceTypes(device)) { - return module; + if (profile->supportsDeviceTypes(type)) { + if (encodedFormat != AUDIO_FORMAT_DEFAULT) { + DeviceVector declaredDevices = module->getDeclaredDevices(); + sp deviceDesc = + declaredDevices.getDevice(type, String8(), encodedFormat); + if (deviceDesc) { + return module; + } + } else { + return module; + } } } } return nullptr; } -sp HwModuleCollection::getModuleForDevice(const sp &device) const +sp HwModuleCollection::getModuleForDevice(const sp &device, + audio_format_t encodedFormat) const { - for (const auto& module : *this) { - const auto& profiles = audio_is_output_device(device->type()) ? - module->getOutputProfiles() : module->getInputProfiles(); - for (const auto& profile : profiles) { - if (profile->supportsDevice(device)) { - return module; - } - } - } - return nullptr; + return getModuleForDeviceTypes(device->type(), encodedFormat); } DeviceVector HwModuleCollection::getAvailableDevicesFromModuleName( @@ -314,6 +316,7 @@ DeviceVector HwModuleCollection::getAvailableDevicesFromModuleName( sp HwModuleCollection::getDeviceDescriptor(const audio_devices_t deviceType, const char *address, const char *name, + const audio_format_t encodedFormat, bool allowToCreate, bool matchAddress) const { @@ -325,8 +328,14 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices for (const auto& hwModule : *this) { DeviceVector moduleDevices = hwModule->getAllDevices(); - auto moduleDevice = moduleDevices.getDevice(deviceType, devAddress); + auto moduleDevice = moduleDevices.getDevice(deviceType, devAddress, encodedFormat); if (moduleDevice) { + if (encodedFormat != AUDIO_FORMAT_DEFAULT) { + moduleDevice->setEncodedFormat(encodedFormat); + if (moduleDevice->address() != devAddress) { + moduleDevice->setAddress(devAddress); + } + } if (allowToCreate) { moduleDevice->attach(hwModule); } @@ -338,14 +347,15 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices name, deviceType, address); return nullptr; } - return createDevice(deviceType, address, name); + return createDevice(deviceType, address, name, encodedFormat); } sp HwModuleCollection::createDevice(const audio_devices_t type, const char *address, - const char *name) const + const char *name, + const audio_format_t encodedFormat) const { - sp hwModule = getModuleForDeviceTypes(type); + sp hwModule = getModuleForDeviceTypes(type, encodedFormat); if (hwModule == 0) { ALOGE("%s: could not find HW module for device %04x address %s", __FUNCTION__, type, address); @@ -354,8 +364,9 @@ sp HwModuleCollection::createDevice(const audio_devices_t type sp device = new DeviceDescriptor(type, String8(name)); device->setName(String8(name)); device->setAddress(String8(address)); + device->setEncodedFormat(encodedFormat); - // Add the device to the list of dynamic devices + // Add the device to the list of dynamic devices hwModule->addDynamicDevice(device); // Reciprocally attach the device to the module device->attach(hwModule); @@ -370,7 +381,8 @@ sp HwModuleCollection::createDevice(const audio_devices_t type if (profile->supportsDevice(device, false /*matchAdress*/)) { // @todo quid of audio profile? import the profile from device of the same type? - const auto &isoTypeDeviceForProfile = profile->getSupportedDevices().getDevice(type); + const auto &isoTypeDeviceForProfile = + profile->getSupportedDevices().getDevice(type, String8(), AUDIO_FORMAT_DEFAULT); device->importAudioPort(isoTypeDeviceForProfile, true /* force */); ALOGV("%s: adding device %s to profile %s", __FUNCTION__, diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 1154654f85..98d375c115 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -140,6 +140,8 @@ struct DevicePortTraits : public AndroidCollectionTraits. */ /** optional: device address, char string less than 64. */ static constexpr const char *address = "address"; + /** optional: the list of encoded audio formats that are known to be supported. */ + static constexpr const char *encodedFormats = "encodedFormats"; }; static Return deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); @@ -511,7 +513,13 @@ Return DevicePortTraits::deserialize(const xmlNode *c ALOGW("%s: bad type %08x", __func__, type); return Status::fromStatusT(BAD_VALUE); } - Element deviceDesc = new DeviceDescriptor(type, String8(name.c_str())); + std::string encodedFormatsLiteral = getXmlAttribute(cur, Attributes::encodedFormats); + ALOGV("%s: %s %s=%s", __func__, tag, Attributes::encodedFormats, encodedFormatsLiteral.c_str()); + FormatVector encodedFormats; + if (!encodedFormatsLiteral.empty()) { + encodedFormats = formatsFromString(encodedFormatsLiteral, " "); + } + Element deviceDesc = new DeviceDescriptor(type, encodedFormats, String8(name.c_str())); std::string address = getXmlAttribute(cur, Attributes::address); if (!address.empty()) { diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 3d68cd8834..cc5a025d80 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -506,7 +506,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - String8("0")) != 0) { + String8("0"), AUDIO_FORMAT_DEFAULT) != 0) { device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5c8a799d50..a03e20d276 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -81,9 +81,11 @@ static const std::vector surroundChannelMasksOrder = {{ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { - status_t status = setDeviceConnectionStateInt(device, state, device_address, device_name); + status_t status = setDeviceConnectionStateInt(device, state, device_address, + device_name, encodedFormat); nextAudioPortGeneration(); return status; } @@ -101,16 +103,17 @@ void AudioPolicyManager::broadcastDeviceConnectionState(const sp device = - mHwModules.getDeviceDescriptor(deviceType, device_address, device_name, + mHwModules.getDeviceDescriptor(deviceType, device_address, device_name, encodedFormat, state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE); if (device == 0) { return INVALID_OPERATION; @@ -133,10 +136,22 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - ALOGV("%s() connecting device %s", __func__, device->toString().c_str()); + ALOGV("%s() connecting device %s format %x", + __func__, device->toString().c_str(), encodedFormat); // register new device as available - if (mAvailableOutputDevices.add(device) < 0) { + index = mAvailableOutputDevices.add(device); + if (index >= 0) { + sp module = mHwModules.getModuleForDevice(device, encodedFormat); + if (module == 0) { + ALOGD("setDeviceConnectionState() could not find HW module for device %s", + device->toString().c_str()); + mAvailableOutputDevices.remove(device); + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() module name=%s", module->getName()); + mAvailableOutputDevices[index]->attach(module); + } else { return NO_MEMORY; } @@ -178,6 +193,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT checkOutputsForDevice(device, state, outputs); + // Reset active device codec + device->setEncodedFormat(AUDIO_FORMAT_DEFAULT); + // Propagate device availability to Engine mEngine->setDeviceConnectionState(device, state); } break; @@ -248,6 +266,13 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } + sp module = mHwModules.getModuleForDevice(device, AUDIO_FORMAT_DEFAULT); + if (module == NULL) { + ALOGW("setDeviceConnectionState(): could not find HW module for device %s", + device->toString().c_str()); + return INVALID_OPERATION; + } + // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) broadcastDeviceConnectionState(device, state); @@ -318,7 +343,8 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi const char *device_address) { sp devDesc = - mHwModules.getDeviceDescriptor(device, device_address, "", false /* allowToCreate */, + mHwModules.getDeviceDescriptor(device, device_address, "", AUDIO_FORMAT_DEFAULT, + false /* allowToCreate */, (strlen(device_address) != 0)/*matchAddress*/); if (devDesc == 0) { @@ -338,50 +364,61 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; } - return (deviceVector->getDevice(device, String8(device_address)) != 0) ? + return (deviceVector->getDevice( + device, String8(device_address), AUDIO_FORMAT_DEFAULT) != 0) ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; } status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { status_t status; String8 reply; AudioParameter param; int isReconfigA2dpSupported = 0; - ALOGV("handleDeviceConfigChange(() device: 0x%X, address %s name %s", - device, device_address, device_name); + ALOGV("handleDeviceConfigChange(() device: 0x%X, address %s name %s encodedFormat: 0x%X", + device, device_address, device_name, encodedFormat); // connect/disconnect only 1 device at a time if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; // Check if the device is currently connected - sp devDesc = - mHwModules.getDeviceDescriptor(device, device_address, device_name); - if (devDesc == 0 || mAvailableOutputDevices.indexOf(devDesc) < 0) { + DeviceVector availableDevices = getAvailableOutputDevices(); + DeviceVector deviceList = availableDevices.getDevicesFromTypeMask(device); + if (deviceList.empty()) { // Nothing to do: device is not connected return NO_ERROR; } + sp devDesc = deviceList.itemAt(0); // For offloaded A2DP, Hw modules may have the capability to - // configure codecs. Check if any of the loaded hw modules - // supports this. - // If supported, send a set parameter to configure A2DP codecs - // and return. No need to toggle device state. + // configure codecs. + // Handle two specific cases by sending a set parameter to + // configure A2DP codecs. No need to toggle device state. + // Case 1: A2DP active device switches from primary to primary + // module + // Case 2: A2DP device config changes on primary module. if (device & AUDIO_DEVICE_OUT_ALL_A2DP) { - reply = mpClientInterface->getParameters( - AUDIO_IO_HANDLE_NONE, - String8(AudioParameter::keyReconfigA2dpSupported)); - AudioParameter repliedParameters(reply); - repliedParameters.getInt( - String8(AudioParameter::keyReconfigA2dpSupported), isReconfigA2dpSupported); - if (isReconfigA2dpSupported) { - const String8 key(AudioParameter::keyReconfigA2dp); - param.add(key, String8("true")); - mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); - return NO_ERROR; + sp module = mHwModules.getModuleForDeviceTypes(device, encodedFormat); + audio_module_handle_t primaryHandle = mPrimaryOutput->getModuleHandle(); + if (availablePrimaryOutputDevices().contains(devDesc) && + (module != 0 && module->getHandle() == primaryHandle)) { + reply = mpClientInterface->getParameters( + AUDIO_IO_HANDLE_NONE, + String8(AudioParameter::keyReconfigA2dpSupported)); + AudioParameter repliedParameters(reply); + repliedParameters.getInt( + String8(AudioParameter::keyReconfigA2dpSupported), isReconfigA2dpSupported); + if (isReconfigA2dpSupported) { + const String8 key(AudioParameter::keyReconfigA2dp); + param.add(key, String8("true")); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + devDesc->setEncodedFormat(encodedFormat); + return NO_ERROR; + } } } @@ -389,7 +426,8 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, // This will force reading again the device configuration status = setDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - device_address, device_name); + device_address, device_name, + devDesc->getEncodedFormat()); if (status != NO_ERROR) { ALOGW("handleDeviceConfigChange() error disabling connection state: %d", status); @@ -398,7 +436,7 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, status = setDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - device_address, device_name); + device_address, device_name, encodedFormat); if (status != NO_ERROR) { ALOGW("handleDeviceConfigChange() error enabling connection state: %d", status); @@ -464,15 +502,18 @@ sp AudioPolicyManager::createTelephonyPatch( } if (isRx) { patchBuilder.addSink(device). - addSource(mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX)); + addSource(mAvailableInputDevices.getDevice( + AUDIO_DEVICE_IN_TELEPHONY_RX, String8(), AUDIO_FORMAT_DEFAULT)); } else { patchBuilder.addSource(device). - addSink(mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX)); + addSink(mAvailableOutputDevices.getDevice( + AUDIO_DEVICE_OUT_TELEPHONY_TX, String8(), AUDIO_FORMAT_DEFAULT)); } // @TODO: still ignoring the address, or not dealing platform with mutliple telephonydevices const sp outputDevice = isRx ? - device : mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX); + device : mAvailableOutputDevices.getDevice( + AUDIO_DEVICE_OUT_TELEPHONY_TX, String8(), AUDIO_FORMAT_DEFAULT); SortedVector outputs = getOutputsForDevices(DeviceVector(outputDevice), mOutputs); audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); @@ -736,6 +777,10 @@ sp AudioPolicyManager::getProfileForOutput( if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) { continue; } + // reject profiles if connected device does not support codec + if (!curProfile->deviceSupportsEncodedFormats(devices.types())) { + continue; + } if (!directOnly) return curProfile; // when searching for direct outputs, if several profiles are compatible, give priority // to one with offload capability @@ -840,7 +885,8 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, *output = desc->mIoHandle; AudioMix *mix = desc->mPolicyMix; sp deviceDesc = - mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress); + mAvailableOutputDevices.getDevice( + mix->mDeviceType, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT); *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE; ALOGV("%s returns output %d", __func__, *output); return NO_ERROR; @@ -883,7 +929,8 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, *output = AUDIO_IO_HANDLE_NONE; if (!msdDevices.isEmpty()) { *output = getOutputForDevices(msdDevices, session, *stream, config, flags); - sp deviceDesc = mAvailableOutputDevices.getDevice(deviceType); + sp deviceDesc = + mAvailableOutputDevices.getDevice(deviceType, String8(), AUDIO_FORMAT_DEFAULT); if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(deviceDesc) == NO_ERROR) { ALOGV("%s() Using MSD devices %s instead of device %s", __func__, msdDevices.toString().c_str(), deviceDesc->toString().c_str()); @@ -1158,7 +1205,7 @@ status_t AudioPolicyManager::getBestMsdAudioProfileFor(const sp deviceModule = mHwModules.getModuleForDevice(outputDevice); + sp deviceModule = mHwModules.getModuleForDevice(outputDevice, AUDIO_FORMAT_DEFAULT); if (deviceModule == nullptr) { ALOGE("%s() unable to get module for %s", __func__, outputDevice->toString().c_str()); return NO_INIT; @@ -1456,7 +1503,8 @@ status_t AudioPolicyManager::startSource(const sp& outp } else { newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } - devices.add(mAvailableOutputDevices.getDevice(newDeviceType, String8(address))); + devices.add(mAvailableOutputDevices.getDevice(newDeviceType, + String8(address), AUDIO_FORMAT_DEFAULT)); } // requiresMuteCheck is false when we can bypass mute strategy. @@ -1574,7 +1622,8 @@ status_t AudioPolicyManager::startSource(const sp& outp setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address, - "remote-submix"); + "remote-submix", + AUDIO_FORMAT_DEFAULT); } return NO_ERROR; @@ -1620,7 +1669,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, outputDesc->mPolicyMix->mDeviceAddress, - "remote-submix"); + "remote-submix", AUDIO_FORMAT_DEFAULT); } } bool forceDeviceUpdate = false; @@ -1812,7 +1861,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - String8(attr->tags + strlen("addr="))); + String8(attr->tags + strlen("addr=")), + AUDIO_FORMAT_DEFAULT); } else { if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; @@ -1831,7 +1881,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, // know about it and is therefore considered "legacy" *inputType = API_INPUT_LEGACY; } else if (audio_is_remote_submix_device(device->type())) { - device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8("0")); + device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8("0"), + AUDIO_FORMAT_DEFAULT); *inputType = API_INPUT_MIX_CAPTURE; } else if (device->type() == AUDIO_DEVICE_IN_TELEPHONY_RX) { *inputType = API_INPUT_TELEPHONY_RX; @@ -2065,7 +2116,7 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address, "remote-submix"); + address, "remote-submix", AUDIO_FORMAT_DEFAULT); } } } @@ -2116,7 +2167,7 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address, "remote-submix"); + address, "remote-submix", AUDIO_FORMAT_DEFAULT); } } resetInputDevice(input); @@ -2536,11 +2587,11 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) if (mix.mMixType == MIX_TYPE_PLAYERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string(), "remote-submix"); + address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); } else { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string(), "remote-submix"); + address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); } } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { String8 address = mix.mDeviceAddress; @@ -2614,13 +2665,13 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string(), "remote-submix"); + address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); } if (getDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address.string()) == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string(), "remote-submix"); + address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); } rSubmixModule->removeOutputProfile(address); rSubmixModule->removeInputProfile(address); @@ -2664,7 +2715,8 @@ status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid, // reevaluate outputs for all given devices for (size_t i = 0; i < devices.size(); i++) { sp devDesc = mHwModules.getDeviceDescriptor( - devices[i].mType, devices[i].mAddress, String8()); + devices[i].mType, devices[i].mAddress, String8(), + AUDIO_FORMAT_DEFAULT); SortedVector outputs; if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, outputs) != NO_ERROR) { @@ -2685,7 +2737,8 @@ status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) { // reevaluate outputs for all found devices for (size_t i = 0; i < devices.size(); i++) { sp devDesc = mHwModules.getDeviceDescriptor( - devices[i].mType, devices[i].mAddress, String8()); + devices[i].mType, devices[i].mAddress, String8(), + AUDIO_FORMAT_DEFAULT); SortedVector outputs; if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, outputs) != NO_ERROR) { @@ -3442,7 +3495,8 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so sp srcDevice = mAvailableInputDevices.getDevice(source->ext.device.type, - String8(source->ext.device.address)); + String8(source->ext.device.address), + AUDIO_FORMAT_DEFAULT); if (srcDevice == 0) { ALOGW("%s source->ext.device.type %08x not found", __FUNCTION__, source->ext.device.type); return BAD_VALUE; @@ -3726,14 +3780,16 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat status_t status = setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_HDMI, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, address.c_str(), - name.c_str()); + name.c_str(), + AUDIO_FORMAT_DEFAULT); if (status != NO_ERROR) { continue; } status = setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_HDMI, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.c_str(), - name.c_str()); + name.c_str(), + AUDIO_FORMAT_DEFAULT); profileUpdated |= (status == NO_ERROR); } // FIXME: Why doing this for input HDMI devices if we don't augment their reported formats? @@ -3746,14 +3802,16 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat status_t status = setDeviceConnectionStateInt(AUDIO_DEVICE_IN_HDMI, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, address.c_str(), - name.c_str()); + name.c_str(), + AUDIO_FORMAT_DEFAULT); if (status != NO_ERROR) { continue; } status = setDeviceConnectionStateInt(AUDIO_DEVICE_IN_HDMI, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.c_str(), - name.c_str()); + name.c_str(), + AUDIO_FORMAT_DEFAULT); profileUpdated |= (status == NO_ERROR); } @@ -4178,7 +4236,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d // first list already open outputs that can be routed to this device for (size_t i = 0; i < mOutputs.size(); i++) { desc = mOutputs.valueAt(i); - if (!desc->isDuplicated() && desc->supportsDevice(device)) { + if (!desc->isDuplicated() && desc->supportsDevice(device) + && desc->deviceSupportsEncodedFormats(deviceType)) { ALOGV("checkOutputsForDevice(): adding opened output %d on device %s", mOutputs.keyAt(i), device->toString().c_str()); outputs.add(mOutputs.keyAt(i)); @@ -4341,7 +4400,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d desc = mOutputs.valueAt(i); if (!desc->isDuplicated()) { // exact match on device - if (device_distinguishes_on_address(deviceType) && desc->supportsDevice(device)) { + if (device_distinguishes_on_address(deviceType) && desc->supportsDevice(device) + && desc->deviceSupportsEncodedFormats(deviceType)) { outputs.add(mOutputs.keyAt(i)); } else if (!mAvailableOutputDevices.containsAtLeastOne(desc->supportedDevices())) { ALOGV("checkOutputsForDevice(): disconnecting adding output %d", @@ -4628,7 +4688,8 @@ SortedVector AudioPolicyManager::getOutputsForDevices( ALOGVV("output %zu isDuplicated=%d device=%s", i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices().toString().c_str()); - if (openOutputs.valueAt(i)->supportsAllDevices(devices)) { + if (openOutputs.valueAt(i)->supportsAllDevices(devices) + && openOutputs.valueAt(i)->deviceSupportsEncodedFormats(devices.types())) { ALOGVV("%s() found output %d", __func__, openOutputs.keyAt(i)); outputs.add(openOutputs.keyAt(i)); } @@ -4684,9 +4745,10 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) maxLatency = desc->latency(); } } - ALOGV("%s: strategy %d, moving from output %s to output %s", __func__, strategy, - (srcOutputs.isEmpty()? "none" : std::to_string(srcOutputs[0]).c_str()), - (dstOutputs.isEmpty()? "none" : std::to_string(dstOutputs[0]).c_str())); + ALOGV_IF(!(srcOutputs.isEmpty() || dstOutputs.isEmpty()), + "%s: strategy %d, moving from output %s to output %s", __func__, strategy, + std::to_string(srcOutputs[0]).c_str(), + std::to_string(dstOutputs[0]).c_str()); // mute strategy while moving tracks from one output to another for (audio_io_handle_t srcOut : srcOutputs) { sp desc = mPreviousOutputs.valueFor(srcOut); @@ -5404,9 +5466,10 @@ sp AudioPolicyManager::getDeviceForAttributes(const audio_attr if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) { return mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - String8(attributes.tags + strlen("addr="))); + String8(attributes.tags + strlen("addr=")), + AUDIO_FORMAT_DEFAULT); } - return mAvailableInputDevices.getDevice(device); + return mAvailableInputDevices.getDevice(device, String8(), AUDIO_FORMAT_DEFAULT); } float AudioPolicyManager::computeVolume(audio_stream_type_t stream, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index e99de16493..4b42a3427e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -97,12 +97,14 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); virtual status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); virtual void setPhoneState(audio_mode_t state); virtual void setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); @@ -731,7 +733,8 @@ private: status_t setDeviceConnectionStateInt(audio_devices_t deviceType, audio_policy_dev_state_t state, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); void updateMono(audio_io_handle_t output) { AudioParameter param; param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 2c904d9ad7..09168f4e35 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -32,7 +32,8 @@ namespace android { status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -49,7 +50,7 @@ status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, Mutex::Autolock _l(mLock); AutoCallerClear acc; return mAudioPolicyManager->setDeviceConnectionState(device, state, - device_address, device_name); + device_address, device_name, encodedFormat); } audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( @@ -66,7 +67,8 @@ audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( status_t AudioPolicyService::handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name) + const char *device_name, + audio_format_t encodedFormat) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -79,7 +81,7 @@ status_t AudioPolicyService::handleDeviceConfigChange(audio_devices_t device, Mutex::Autolock _l(mLock); AutoCallerClear acc; return mAudioPolicyManager->handleDeviceConfigChange(device, device_address, - device_name); + device_name, encodedFormat); } status_t AudioPolicyService::setPhoneState(audio_mode_t state) diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 959e757229..6ff973e1b7 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -61,13 +61,15 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); virtual audio_policy_dev_state_t getDeviceConnectionState( audio_devices_t device, const char *device_address); virtual status_t handleDeviceConfigChange(audio_devices_t device, const char *device_address, - const char *device_name); + const char *device_name, + audio_format_t encodedFormat); virtual status_t setPhoneState(audio_mode_t state); virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); -- GitLab From 11029ad02b64c74b8d49eb5e69c4db254e9f3e68 Mon Sep 17 00:00:00 2001 From: Arun Mirpuri Date: Wed, 19 Dec 2018 20:45:19 -0800 Subject: [PATCH 0758/1530] audiopolicy: Implement API for querying A2DP offload formats Add support in AudioPolicyManager to return supported encoding formats for A2DP on the primary HAL. Current implementation is to query prop defined and convert this to a list of audio formats Bug: 111812273 Test: make Change-Id: Ic3ea72e19aa4c0bd4c156f62e45e6225f9ce6b11 --- media/libaudioclient/AudioSystem.cpp | 7 +++ media/libaudioclient/IAudioPolicyService.cpp | 44 ++++++++++++++++++- .../include/media/AudioSystem.h | 3 ++ .../include/media/IAudioPolicyService.h | 2 + services/audiopolicy/AudioPolicyInterface.h | 3 ++ .../managerdefault/AudioPolicyManager.cpp | 37 ++++++++++++++++ .../managerdefault/AudioPolicyManager.h | 3 ++ .../service/AudioPolicyInterfaceImpl.cpp | 11 +++++ .../audiopolicy/service/AudioPolicyService.h | 2 + 9 files changed, 110 insertions(+), 2 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 0beb65de91..4c762eddcf 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1337,6 +1337,13 @@ bool AudioSystem::isHapticPlaybackSupported() return aps->isHapticPlaybackSupported(); } +status_t AudioSystem::getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getHwOffloadEncodingFormatsSupportedForA2DP(formats); +} // --------------------------------------------------------------------------- diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 0fae2a1f07..8c7fac508b 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -92,6 +92,7 @@ enum { IS_HAPTIC_PLAYBACK_SUPPORTED, SET_UID_DEVICE_AFFINITY, REMOVE_UID_DEVICE_AFFINITY, + GET_OFFLOAD_FORMATS_A2DP }; #define MAX_ITEMS_PER_LIST 1024 @@ -888,7 +889,30 @@ public: return reply.readInt32(); } - virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, + virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) + { + if (formats == NULL) { + return BAD_VALUE; + } + + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_OFFLOAD_FORMATS_A2DP, data, &reply); + if (status != NO_ERROR || (status = (status_t)reply.readInt32()) != NO_ERROR) { + return status; + } + + size_t list_size = reply.readUint32(); + + for (size_t i = 0; i < list_size; i++) { + formats->push_back(static_cast(reply.readInt32())); + } + return NO_ERROR; + } + + + virtual status_t addStreamDefaultEffect(const effect_uuid_t *type, const String16& opPackageName, const effect_uuid_t *uuid, int32_t priority, @@ -1100,7 +1124,8 @@ status_t BnAudioPolicyService::onTransact( case SET_ASSISTANT_UID: case SET_A11Y_SERVICES_UIDS: case SET_UID_DEVICE_AFFINITY: - case REMOVE_UID_DEVICE_AFFINITY: { + case REMOVE_UID_DEVICE_AFFINITY: + case GET_OFFLOAD_FORMATS_A2DP: { if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), @@ -1754,6 +1779,21 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case GET_OFFLOAD_FORMATS_A2DP: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + std::vector encodingFormats; + status_t status = getHwOffloadEncodingFormatsSupportedForA2DP(&encodingFormats); + reply->writeInt32(status); + if (status != NO_ERROR) { + return NO_ERROR; + } + reply->writeUint32(static_cast(encodingFormats.size())); + for (size_t i = 0; i < encodingFormats.size(); i++) + reply->writeInt32(static_cast(encodingFormats[i])); + return NO_ERROR; + } + + case ADD_STREAM_DEFAULT_EFFECT: { CHECK_INTERFACE(IAudioPolicyService, data, reply); effect_uuid_t type; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 2b4a373558..a208602a32 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -344,6 +344,9 @@ public: static status_t getMicrophones(std::vector *microphones); + static status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats); + // numSurroundFormats holds the maximum number of formats and bool value allowed in the array. // When numSurroundFormats is 0, surroundFormats and surroundFormatsEnabled will not be // populated. The actual number of surround formats should be returned at numSurroundFormats. diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 75de8e9725..177adc279e 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -188,6 +188,8 @@ public: audio_format_t *surroundFormats, bool *surroundFormatsEnabled, bool reported) = 0; + virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) = 0; virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) = 0; virtual status_t setAssistantUid(uid_t uid) = 0; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 8bfb7360c5..cf2ce99096 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -236,6 +236,9 @@ public: virtual bool isHapticPlaybackSupported() = 0; + virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) = 0; + virtual void setAppState(uid_t uid, app_state_t state); }; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index a03e20d276..cc151e7d8a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -446,6 +446,43 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, return NO_ERROR; } +status_t AudioPolicyManager::getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) +{ + ALOGV("getHwOffloadEncodingFormatsSupportedForA2DP()"); + char *tok = NULL, *saveptr; + status_t status = NO_ERROR; + char encoding_formats_list[PROPERTY_VALUE_MAX]; + audio_format_t format = AUDIO_FORMAT_DEFAULT; + // FIXME This list should not come from a property but the supported encoded + // formats of declared A2DP devices in primary module + property_get("persist.bluetooth.a2dp_offload.cap", encoding_formats_list, ""); + tok = strtok_r(encoding_formats_list, "-", &saveptr); + for (;tok != NULL; tok = strtok_r(NULL, "-", &saveptr)) { + if (strcmp(tok, "sbc") == 0) { + ALOGV("%s: SBC offload supported\n",__func__); + format = AUDIO_FORMAT_SBC; + } else if (strcmp(tok, "aptx") == 0) { + ALOGV("%s: APTX offload supported\n",__func__); + format = AUDIO_FORMAT_APTX; + } else if (strcmp(tok, "aptxhd") == 0) { + ALOGV("%s: APTX HD offload supported\n",__func__); + format = AUDIO_FORMAT_APTX_HD; + } else if (strcmp(tok, "ldac") == 0) { + ALOGV("%s: LDAC offload supported\n",__func__); + format = AUDIO_FORMAT_LDAC; + } else if (strcmp(tok, "aac") == 0) { + ALOGV("%s: AAC offload supported\n",__func__); + format = AUDIO_FORMAT_AAC; + } else { + ALOGE("%s: undefined token - %s\n",__func__, tok); + continue; + } + formats->push_back(format); + } + return status; +} + uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs) { bool createTxPatch = false; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 4b42a3427e..fb1f7cb4e2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -241,6 +241,9 @@ public: bool reported); virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); + virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats); + // return the strategy corresponding to a given stream type routing_strategy getStrategy(audio_stream_type_t stream) const; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 09168f4e35..49c541cf34 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1140,6 +1140,17 @@ status_t AudioPolicyService::getSurroundFormats(unsigned int *numSurroundFormats surroundFormatsEnabled, reported); } +status_t AudioPolicyService::getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->getHwOffloadEncodingFormatsSupportedForA2DP(formats); +} + status_t AudioPolicyService::setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled) { if (mAudioPolicyManager == NULL) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 6ff973e1b7..c073b7c0da 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -220,6 +220,8 @@ public: audio_format_t *surroundFormats, bool *surroundFormatsEnabled, bool reported); + virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( + std::vector *formats); virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); virtual status_t setAssistantUid(uid_t uid); -- GitLab From 929a3d005dd2d6d06dac0f24091643fca13bb1b3 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 21 Jan 2019 10:11:56 -0800 Subject: [PATCH 0759/1530] Add libmedia2_jni to media apex. Test: build & boot Bug: 112766913, Bug: 123185917 Change-Id: Idc402390074b73855e870fa03661f9d1939a088c --- apex/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apex/Android.bp b/apex/Android.bp index bf295babdd..422880a6ba 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -29,7 +29,7 @@ apex { "liboggextractor", "libwavextractor", // MediaPlayer2 - "libmediaplayer2_jni", + "libmedia2_jni", ], key: "com.android.media.key", } -- GitLab From 831b2167e413d7226ed09c528df977dd80cf4ee1 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 9 Jan 2019 17:30:24 -0800 Subject: [PATCH 0760/1530] Remove compile_multilib:32 from libmedia_codecserviceregistrant Test: cts-tradefed run commandAndExit cts-dev -m CtsMediaTestCases\ -t android.media.cts.DecoderTest Bug: 112004561 Change-Id: I500cc37fef611b042dbeee6f8a7ea63db1a6f205 --- services/mediacodec/registrant/Android.bp | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 8c40ad1ac8..8ae337630c 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -49,7 +49,5 @@ cc_library_shared { "libcodec2_soft_gsmdec", "libcodec2_soft_xaacdec", ], - - compile_multilib: "32", } -- GitLab From 72500a1dfe55141ee92eac5259aebf2667a0da92 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 11 Jan 2019 18:05:12 -0800 Subject: [PATCH 0761/1530] Allow muxer to write exif with APP1 Also contains misc fixes around exif reading/writing. bug: 123094535 test: verify locally that ExifInterface retrieves the exif data at correct offset; HeifWriter accepts exif data starting with 'Exif'+APP1 marker. Change-Id: I526641724caaea68d2b6f1075d9aa30d9a74b973 --- media/extractors/mp4/ItemTable.cpp | 27 ++++++- media/libstagefright/MPEG4Writer.cpp | 79 ++++++++++++++----- .../include/media/stagefright/MPEG4Writer.h | 4 +- .../include/media/stagefright/MetaDataBase.h | 3 +- 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index 55a0c47c68..15788d4c8d 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -1687,8 +1687,31 @@ status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) { } // skip the first 4-byte of the offset to TIFF header - *offset = mItemIdToExifMap[exifIndex].offset + 4; - *size = mItemIdToExifMap[exifIndex].size - 4; + uint32_t tiffOffset; + if (!mDataSource->readAt( + mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) { + return ERROR_IO; + } + + // We need 'Exif\0\0' before the tiff header + tiffOffset = ntohl(tiffOffset); + if (tiffOffset < 6) { + return ERROR_MALFORMED; + } + // The first 4-byte of the item is the offset of the tiff header within the + // 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) { + 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; return OK; } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 7df1a2ddbe..c4015fbcbc 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -85,7 +85,7 @@ static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_la static const int kTimestampDebugCount = 10; static const int kItemIdBase = 10000; static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'}; -static const int32_t kTiffHeaderOffset = htonl(sizeof(kExifHeader)); +static const uint8_t kExifApp1Marker[] = {'E', 'x', 'i', 'f', 0xff, 0xe1}; static const uint8_t kMandatoryHevcNalUnitTypes[3] = { kHevcNalUnitTypeVps, @@ -125,7 +125,7 @@ public: bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; } - bool isExifData(const MediaBufferBase *buffer) const; + bool isExifData(MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const; void addChunkOffset(off64_t offset); void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif); void flushItemRefs(); @@ -364,7 +364,7 @@ private: Vector mProperties; ItemRefs mDimgRefs; - ItemRefs mCdscRefs; + Vector mExifList; uint16_t mImageItemId; int32_t mIsPrimary; int32_t mWidth, mHeight; @@ -1368,14 +1368,16 @@ void MPEG4Writer::unlock() { } off64_t MPEG4Writer::addSample_l( - MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten) { + MediaBuffer *buffer, bool usePrefix, + uint32_t tiffHdrOffset, size_t *bytesWritten) { off64_t old_offset = mOffset; if (usePrefix) { addMultipleLengthPrefixedSamples_l(buffer); } else { - if (isExif) { - ::write(mFd, &kTiffHeaderOffset, 4); // exif_tiff_header_offset field + if (tiffHdrOffset > 0) { + tiffHdrOffset = htonl(tiffHdrOffset); + ::write(mFd, &tiffHdrOffset, 4); // exif_tiff_header_offset field mOffset += 4; } @@ -1803,7 +1805,6 @@ MPEG4Writer::Track::Track( mStartTimestampUs(-1), mRotation(0), mDimgRefs("dimg"), - mCdscRefs("cdsc"), mImageItemId(0), mIsPrimary(0), mWidth(0), @@ -1984,11 +1985,34 @@ status_t MPEG4Writer::setNextFd(int fd) { return OK; } -bool MPEG4Writer::Track::isExifData(const MediaBufferBase *buffer) const { - return mIsHeic - && (buffer->range_length() > sizeof(kExifHeader)) - && !memcmp((uint8_t *)buffer->data() + buffer->range_offset(), - kExifHeader, sizeof(kExifHeader)); +bool MPEG4Writer::Track::isExifData( + MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const { + if (!mIsHeic) { + return false; + } + + // Exif block starting with 'Exif\0\0' + size_t length = buffer->range_length(); + uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset(); + if ((length > sizeof(kExifHeader)) + && !memcmp(data, kExifHeader, sizeof(kExifHeader))) { + *tiffHdrOffset = sizeof(kExifHeader); + return true; + } + + // Exif block starting with fourcc 'Exif' followed by APP1 marker + if ((length > sizeof(kExifApp1Marker) + 2 + sizeof(kExifHeader)) + && !memcmp(data, kExifApp1Marker, sizeof(kExifApp1Marker)) + && !memcmp(data + sizeof(kExifApp1Marker) + 2, kExifHeader, sizeof(kExifHeader))) { + // skip 'Exif' fourcc + buffer->set_range(4, buffer->range_length() - 4); + + // 2-byte APP1 + 2-byte size followed by kExifHeader + *tiffHdrOffset = 2 + 2 + sizeof(kExifHeader); + return true; + } + + return false; } void MPEG4Writer::Track::addChunkOffset(off64_t offset) { @@ -2014,7 +2038,7 @@ void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool } if (isExif) { - mCdscRefs.value.push_back(mOwner->addItem_l({ + mExifList.push_back(mOwner->addItem_l({ .itemType = "Exif", .isPrimary = false, .isHidden = false, @@ -2117,7 +2141,16 @@ void MPEG4Writer::Track::flushItemRefs() { if (mImageItemId > 0) { mOwner->addRefs_l(mImageItemId, mDimgRefs); - mOwner->addRefs_l(mImageItemId, mCdscRefs); + + if (!mExifList.empty()) { + // The "cdsc" ref is from the metadata/exif item to the image item. + // So the refs all contain the image item. + ItemRefs cdscRefs("cdsc"); + cdscRefs.value.push_back(mImageItemId); + for (uint16_t exifItem : mExifList) { + mOwner->addRefs_l(exifItem, cdscRefs); + } + } } } @@ -2269,14 +2302,16 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { while (!chunk->mSamples.empty()) { List::iterator it = chunk->mSamples.begin(); - int32_t isExif; - if (!(*it)->meta_data().findInt32(kKeyIsExif, &isExif)) { - isExif = 0; + uint32_t tiffHdrOffset; + if (!(*it)->meta_data().findInt32( + kKeyExifTiffOffset, (int32_t*)&tiffHdrOffset)) { + tiffHdrOffset = 0; } + bool isExif = (tiffHdrOffset > 0); bool usePrefix = chunk->mTrack->usePrefix() && !isExif; size_t bytesWritten; - off64_t offset = addSample_l(*it, usePrefix, isExif, &bytesWritten); + off64_t offset = addSample_l(*it, usePrefix, tiffHdrOffset, &bytesWritten); if (chunk->mTrack->isHeic()) { chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif); @@ -3002,10 +3037,11 @@ status_t MPEG4Writer::Track::threadEntry() { } bool isExif = false; + uint32_t tiffHdrOffset = 0; int32_t isMuxerData; if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) { // We only support one type of muxer data, which is Exif data block. - isExif = isExifData(buffer); + isExif = isExifData(buffer, &tiffHdrOffset); if (!isExif) { ALOGW("Ignoring bad Exif data block"); buffer->release(); @@ -3027,7 +3063,7 @@ status_t MPEG4Writer::Track::threadEntry() { buffer = NULL; if (isExif) { - copy->meta_data().setInt32(kKeyIsExif, 1); + copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset); } bool usePrefix = this->usePrefix() && !isExif; @@ -3300,7 +3336,8 @@ status_t MPEG4Writer::Track::threadEntry() { } if (!hasMultipleTracks) { size_t bytesWritten; - off64_t offset = mOwner->addSample_l(copy, usePrefix, isExif, &bytesWritten); + off64_t offset = mOwner->addSample_l( + copy, usePrefix, tiffHdrOffset, &bytesWritten); if (mIsHeic) { addItemOffsetAndSize(offset, bytesWritten, isExif); diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h index 1abef8cabe..803155df12 100644 --- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h +++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h @@ -257,7 +257,9 @@ private: void initInternal(int fd, bool isFirstSession); // Acquire lock before calling these methods - off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten); + off64_t addSample_l( + MediaBuffer *buffer, bool usePrefix, + uint32_t tiffHdrOffset, size_t *bytesWritten); void addLengthPrefixedSample_l(MediaBuffer *buffer); void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer); uint16_t addProperty_l(const ItemProperty &); diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index b99c14ca19..2910bd38ac 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -221,7 +221,8 @@ enum { kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track kKeyExifOffset = 'exof', // int64_t, Exif data offset kKeyExifSize = 'exsz', // int64_t, Exif data size - kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block + kKeyExifTiffOffset = 'thdr', // int32_t, if > 0, buffer contains exif data block with + // tiff hdr at specified offset kKeyPcmBigEndian = 'pcmb', // bool (int32_t) // Key for ALAC Magic Cookie -- GitLab From b3fc6efa771223c0d9bd8bbc83054452afc6cfbc Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 22 Jan 2019 11:41:55 -0800 Subject: [PATCH 0762/1530] Fail gracefully instead of CHECKing Bug: 122894106 Test: manual Change-Id: Ia3c40815bfb8dd64eeb8cbeb082c601a2015877b --- media/extractors/mp4/MPEG4Extractor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index cc1534a323..2013575aea 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -4868,7 +4868,9 @@ status_t MPEG4Source::parseClearEncryptedSizes( off64_t offset, bool isSubsampleEncryption, uint32_t flags) { int32_t ivlength; - CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &ivlength)); + if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &ivlength)) { + return ERROR_MALFORMED; + } // only 0, 8 and 16 byte initialization vectors are supported if (ivlength != 0 && ivlength != 8 && ivlength != 16) { -- GitLab From 2ad4439c968eaaa704c658202607669ffc43c2ef Mon Sep 17 00:00:00 2001 From: Rob McConnell Date: Tue, 22 Jan 2019 12:22:57 -0800 Subject: [PATCH 0763/1530] cas: fix crash with partially scrambled audio content This patch addresses a situation when MPEG-2 TS level scrambled audio/video content has some of the audio content scrambled. This can cause the MPEG-2 TS extractor to crash due to it trying to perform an "out-of-bound" index operation in to the sync points keyed vector. The sequence of events to cause this crash are as follows: 1) The MPEG-2 TS extractor is first initialised and begins to search through the content until it finds both audio and video scrambled ES. In the test stream, the video ES is found before the audio so the "mSourceImpls" Vector contains video as the first entry and then audio. 2) The client application makes a call to set mediacas. This makes another call to the "init()" method which in turn causes the extractor to continue to parses the scrambled A/V content. With the particular test stream the audio has predominantly unscrambled PES packets and the audio format is readily detected as "unscrambled" along with a sync event. This causes the "mSyncPoints" keyed vector to have its first entry set for the audio track. 3) When the call to "addSyncPoint_l()" is made this method iterates over the "mSourceImpls" vector until it finds a match with the event. In this case it finds the audio sync event at the second index position. 4) The code makes the assumption that the indexing in to "mSourceImpls" and "mSyncPoints" are both tied to the same source (i.e. the same track). This is not the case as "mSourceImpls[1]" contains the audio source but "mSyncPoints[0]" contains the audio sync event. 5) When "addSyncPoint_l()" tries to index in to "mSyncPoints[1]", we receive a crash due to an "out-of-bound" index operation. The patch ensures that the "mSourceImpls" and "mSyncPoints" indexing are identical for the different sources (tracks). This prevents an "out-of-bound" index access to occur. bug: 123013446 test: atest android.media.cts.MediaDrmClearkeyTest#testClearKeyPlaybackMpeg2ts atest android.media.cts.MediaDrmClearkeyTest#testPlaybackMpeg2ts Change-Id: I75f5b24eb6a958796add737677f28178243095a4 Signed-off-by: Rob McConnell --- media/extractors/mpeg2/MPEG2TSExtractor.cpp | 28 +++++++++++++-------- media/extractors/mpeg2/MPEG2TSExtractor.h | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index e1509eef44..49dd0b4e11 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -302,16 +302,21 @@ media_status_t MPEG2TSExtractor::setMediaCas(const uint8_t* casToken, size_t siz return AMEDIA_ERROR_UNKNOWN; } -void MPEG2TSExtractor::addSource(const sp &impl) { - bool found = false; +status_t MPEG2TSExtractor::findIndexOfSource(const sp &impl, size_t *index) { for (size_t i = 0; i < mSourceImpls.size(); i++) { if (mSourceImpls[i] == impl) { - found = true; - break; + *index = i; + return OK; } } - if (!found) { + return NAME_NOT_FOUND; +} + +void MPEG2TSExtractor::addSource(const sp &impl) { + size_t index; + if (findIndexOfSource(impl, &index) != OK) { mSourceImpls.push(impl); + mSyncPoints.push(); } } @@ -319,6 +324,7 @@ void MPEG2TSExtractor::init() { bool haveAudio = false; bool haveVideo = false; int64_t startTime = ALooper::GetNowUs(); + size_t index; status_t err; while ((err = feedMore(true /* isInit */)) == OK @@ -337,8 +343,9 @@ void MPEG2TSExtractor::init() { haveVideo = true; addSource(impl); if (!isScrambledFormat(*(format.get()))) { - mSyncPoints.push(); - mSeekSyncPoints = &mSyncPoints.editTop(); + if (findIndexOfSource(impl, &index) == OK) { + mSeekSyncPoints = &mSyncPoints.editItemAt(index); + } } } } @@ -352,10 +359,9 @@ void MPEG2TSExtractor::init() { if (format != NULL) { haveAudio = true; addSource(impl); - if (!isScrambledFormat(*(format.get()))) { - mSyncPoints.push(); - if (!haveVideo) { - mSeekSyncPoints = &mSyncPoints.editTop(); + if (!isScrambledFormat(*(format.get())) && !haveVideo) { + if (findIndexOfSource(impl, &index) == OK) { + mSeekSyncPoints = &mSyncPoints.editItemAt(index); } } } diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index e425d237c3..2537d3bd7f 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -95,6 +95,7 @@ private: status_t seekBeyond(int64_t seekTimeUs); status_t feedUntilBufferAvailable(const sp &impl); + status_t findIndexOfSource(const sp &impl, size_t *index); // Add a SynPoint derived from |event|. void addSyncPoint_l(const ATSParser::SyncEvent &event); -- GitLab From 245bd8a37c7faa3bada97e00e031a645403a22e1 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 19 Nov 2018 15:59:35 -0800 Subject: [PATCH 0764/1530] Camera: Update NDK for activePhysicalId metadata Test: Camera CTS on Pixel Bug: 77915333 Change-Id: I93338d4e7f14be31bbae78f1f58a5164eae66c80 --- .../include/camera/NdkCameraMetadataTags.h | 113 +++++++++++++++++- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 4bb74cbf6d..641816f1d4 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -5561,12 +5561,12 @@ typedef enum acamera_metadata_tag { *
  • ACameraMetadata from ACameraManager_getCameraCharacteristics
  • *

    * - *

    For a logical camera, this is concatenation of all underlying physical camera ids. - * The null terminator for physical camera id must be preserved so that the whole string - * can be tokenized using '\0' to generate list of physical camera ids.

    - *

    For example, if the physical camera ids of the logical camera are "2" and "3", the + *

    For a logical camera, this is concatenation of all underlying physical camera IDs. + * The null terminator for physical camera ID must be preserved so that the whole string + * can be tokenized using '\0' to generate list of physical camera IDs.

    + *

    For example, if the physical camera IDs of the logical camera are "2" and "3", the * value of this tag will be ['2', '\0', '3', '\0'].

    - *

    The number of physical camera ids must be no less than 2.

    + *

    The number of physical camera IDs must be no less than 2.

    */ ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS = // byte[n] ACAMERA_LOGICAL_MULTI_CAMERA_START, @@ -5591,6 +5591,28 @@ typedef enum acamera_metadata_tag { */ ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE = // byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t) ACAMERA_LOGICAL_MULTI_CAMERA_START + 1, + /** + *

    String containing the ID of the underlying active physical camera.

    + * + *

    Type: byte

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks
    • + *

    + * + *

    The ID of the active physical camera that's backing the logical camera. All camera + * streams and metadata that are not physical camera specific will be originating from this + * physical camera. This must be one of valid physical IDs advertised in the physicalIds + * static tag.

    + *

    For a logical camera made up of physical cameras where each camera's lenses have + * different characteristics, the camera device may choose to switch between the physical + * cameras when application changes FOCAL_LENGTH or SCALER_CROP_REGION. + * At the time of lens switch, this result metadata reflects the new active physical camera + * ID.

    + */ + ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID = // byte + ACAMERA_LOGICAL_MULTI_CAMERA_START + 2, ACAMERA_LOGICAL_MULTI_CAMERA_END, /** @@ -7162,6 +7184,10 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { *

    If this is supported, android.scaler.streamConfigurationMap will * additionally return a min frame duration that is greater than * zero for each supported size-format combination.

    + *

    For camera devices with LOGICAL_MULTI_CAMERA capability, when the underlying active + * physical camera switches, exposureTime, sensitivity, and lens properties may change + * even if AE/AF is locked. However, the overall auto exposure and auto focus experience + * for users will be consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.

    * * @see ACAMERA_BLACK_LEVEL_LOCK * @see ACAMERA_CONTROL_AE_LOCK @@ -7217,6 +7243,10 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * will accurately report the values applied by AWB in the result.

    *

    A given camera device may also support additional post-processing * controls, but this capability only covers the above list of controls.

    + *

    For camera devices with LOGICAL_MULTI_CAMERA capability, when underlying active + * physical camera switches, tonemap, white balance, and shading map may change even if + * awb is locked. However, the overall post-processing experience for users will be + * consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.

    * * @see ACAMERA_COLOR_CORRECTION_ABERRATION_MODE * @see ACAMERA_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES @@ -7396,7 +7426,7 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * *
  • The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be * the same.
  • - *
  • The logical camera device must be LIMITED or higher device.
  • + *
  • The logical camera must be LIMITED or higher device.
  • * *

    Both the logical camera device and its underlying physical devices support the * mandatory stream combinations required for their device levels.

    @@ -7416,13 +7446,84 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { *

    Using physical streams in place of a logical stream of the same size and format will * not slow down the frame rate of the capture, as long as the minimum frame duration * of the physical and logical streams are the same.

    + *

    A logical camera device's dynamic metadata may contain + * ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID to notify the application of the current + * active physical camera Id. An active physical camera is the physical camera from which + * the logical camera's main image data outputs (YUV or RAW) and metadata come from. + * In addition, this serves as an indication which physical camera is used to output to + * a RAW stream, or in case only physical cameras support RAW, which physical RAW stream + * the application should request.

    + *

    Logical camera's static metadata tags below describe the default active physical + * camera. An active physical camera is default if it's used when application directly + * uses requests built from a template. All templates will default to the same active + * physical camera.

    + *
      + *
    • ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE
    • + *
    • ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
    • + *
    • ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE
    • + *
    • ACAMERA_SENSOR_INFO_MAX_FRAME_DURATION
    • + *
    • ACAMERA_SENSOR_INFO_PHYSICAL_SIZE
    • + *
    • ACAMERA_SENSOR_INFO_WHITE_LEVEL
    • + *
    • ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED
    • + *
    • ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
    • + *
    • ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
    • + *
    • ACAMERA_SENSOR_CALIBRATION_TRANSFORM1
    • + *
    • ACAMERA_SENSOR_CALIBRATION_TRANSFORM2
    • + *
    • ACAMERA_SENSOR_COLOR_TRANSFORM1
    • + *
    • ACAMERA_SENSOR_COLOR_TRANSFORM2
    • + *
    • ACAMERA_SENSOR_FORWARD_MATRIX1
    • + *
    • ACAMERA_SENSOR_FORWARD_MATRIX2
    • + *
    • ACAMERA_SENSOR_BLACK_LEVEL_PATTERN
    • + *
    • ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY
    • + *
    • ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS
    • + *
    • ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES
    • + *
    • ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE
    • + *
    • ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE
    • + *
    • ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION
    • + *
    • ACAMERA_LENS_POSE_ROTATION
    • + *
    • ACAMERA_LENS_POSE_TRANSLATION
    • + *
    • ACAMERA_LENS_INTRINSIC_CALIBRATION
    • + *
    • ACAMERA_LENS_POSE_REFERENCE
    • + *
    • ACAMERA_LENS_DISTORTION
    • + *
    + *

    To maintain backward compatibility, the capture request and result metadata tags + * required for basic camera functionalities will be solely based on the + * logical camera capabiltity. Other request and result metadata tags, on the other + * hand, will be based on current active physical camera. For example, the physical + * cameras' sensor sensitivity and lens capability could be different from each other. + * So when the application manually controls sensor exposure time/gain, or does manual + * focus control, it must checks the current active physical camera's exposure, gain, + * and focus distance range.

    * * @see ACAMERA_LENS_DISTORTION + * @see ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION + * @see ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE + * @see ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE * @see ACAMERA_LENS_INTRINSIC_CALIBRATION * @see ACAMERA_LENS_POSE_REFERENCE * @see ACAMERA_LENS_POSE_ROTATION * @see ACAMERA_LENS_POSE_TRANSLATION + * @see ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID * @see ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE + * @see ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES + * @see ACAMERA_SENSOR_BLACK_LEVEL_PATTERN + * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM1 + * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM2 + * @see ACAMERA_SENSOR_COLOR_TRANSFORM1 + * @see ACAMERA_SENSOR_COLOR_TRANSFORM2 + * @see ACAMERA_SENSOR_FORWARD_MATRIX1 + * @see ACAMERA_SENSOR_FORWARD_MATRIX2 + * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT + * @see ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE + * @see ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED + * @see ACAMERA_SENSOR_INFO_MAX_FRAME_DURATION + * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE + * @see ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE + * @see ACAMERA_SENSOR_INFO_WHITE_LEVEL + * @see ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY + * @see ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS + * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 + * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2 */ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11, -- GitLab From 2853c307830e76ff19935808e498846be683243d Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Tue, 22 Jan 2019 13:19:12 +0530 Subject: [PATCH 0765/1530] libeffects: Added missing table entries corresponding to 88.2kHz and 176.4kHz Table entries corresponding to 88.2kHz and 176.4kHz were missing for tables corresponding to time constants and omega(2*pi/Fs). Added these missing entries. Test: local native test (lvmtest) Bug: 123209908 Change-Id: I6c17bb8492a3138acdc1e1fce3bc0a3e5a7eabf2 --- .../libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c | 4 +++- .../lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c | 4 +++- media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c index 48f5d544c6..9d3ee88043 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_SetTimeConstant.c @@ -51,7 +51,7 @@ void LVC_Mixer_SetTimeConstant(LVMixer3_FLOAT_st *pStream, LVM_INT16 NumChannels) { #ifdef HIGHER_FS - LVM_FLOAT DeltaTable[11] = {0.500000f,/*8000*/ + LVM_FLOAT DeltaTable[13] = {0.500000f,/*8000*/ 0.362812f,/*11025*/ 0.333333f,/*12000*/ 0.250000f,/*16000*/ @@ -60,7 +60,9 @@ void LVC_Mixer_SetTimeConstant(LVMixer3_FLOAT_st *pStream, 0.125000f,/*32000*/ 0.090703f,/*44100*/ 0.083333f,/*48000*/ + 0.045352f,/*88200*/ 0.041667f,/*96000*/ + 0.022676f,/*176400*/ 0.020833f};/*192000*/ #else LVM_FLOAT DeltaTable[9] = {0.500000f,/*8000*/ diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c index 9dc7d21f1e..0e0acf1bd8 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c @@ -52,7 +52,7 @@ void LVC_Mixer_VarSlope_SetTimeConstant( LVMixer3_FLOAT_st *pStream, LVM_INT16 NumChannels) { #ifdef HIGHER_FS - LVM_FLOAT DeltaTable[11] = {0.500000f,/*8000*/ + LVM_FLOAT DeltaTable[13] = {0.500000f,/*8000*/ 0.362812f,/*11025*/ 0.333333f,/*12000*/ 0.250000f,/*16000*/ @@ -61,7 +61,9 @@ void LVC_Mixer_VarSlope_SetTimeConstant( LVMixer3_FLOAT_st *pStream, 0.125000f,/*32000*/ 0.090703f,/*44100*/ 0.083333f,/*48000*/ + 0.045352f,/*88200*/ 0.041666f,/*96000*/ + 0.022676f,/*176400*/ 0.020833f};/*192000*/ #else LVM_FLOAT DeltaTable[9] = {0.500000f,/*8000*/ diff --git a/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c b/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c index 7846ca0159..6307e68989 100644 --- a/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c +++ b/media/libeffects/lvm/lib/Common/src/LVM_GetOmega.c @@ -53,7 +53,9 @@ const LVM_INT32 LVVDL_2PiOnFsShiftTable[]={LVVDL_2PiByFs_SHIFT1 , /* #define LVVDL_2PiBy_48000_f 0.000130900f #ifdef HIGHER_FS +#define LVVDL_2PiBy_88200_f 0.000071238f #define LVVDL_2PiBy_96000_f 0.000065450f +#define LVVDL_2PiBy_176400_f 0.000035619f #define LVVDL_2PiBy_192000_f 0.000032725f #endif const LVM_FLOAT LVVDL_2PiOnFsTable[] = {LVVDL_2PiBy_8000_f, @@ -66,7 +68,9 @@ const LVM_FLOAT LVVDL_2PiOnFsTable[] = {LVVDL_2PiBy_8000_f, LVVDL_2PiBy_44100_f, LVVDL_2PiBy_48000_f #ifdef HIGHER_FS + ,LVVDL_2PiBy_88200_f ,LVVDL_2PiBy_96000_f + ,LVVDL_2PiBy_176400_f ,LVVDL_2PiBy_192000_f #endif }; -- GitLab From 99dbfa822e277956799e987c106d4ec1b7984f1b Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Thu, 17 Jan 2019 17:27:06 -0800 Subject: [PATCH 0766/1530] Add securityLevel to MediaDrm isCryptoSchemeSupported API bug:110701831 test: cts media test cases, widevine integration tests Change-Id: Ia4308a27a11a279db1fb127c51d752ecaa86dfcb --- drm/libmediadrm/DrmHal.cpp | 81 ++++++++++++------- drm/libmediadrm/IDrm.cpp | 13 ++- .../plugins/clearkey/hidl/DrmFactory.cpp | 8 ++ .../clearkey/hidl/include/ClearKeyTypes.h | 1 + .../clearkey/hidl/include/DrmFactory.h | 4 + media/libmedia/include/media/DrmHal.h | 9 ++- media/libmedia/include/media/IDrm.h | 4 +- .../nuplayer/NuPlayerDrm.cpp | 3 +- media/ndk/NdkMediaDrm.cpp | 2 +- 9 files changed, 89 insertions(+), 36 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index b72348f456..480c7cda0a 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -145,6 +145,23 @@ static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { } } +static SecurityLevel toHidlSecurityLevel(DrmPlugin::SecurityLevel level) { + switch(level) { + case DrmPlugin::kSecurityLevelSwSecureCrypto: + return SecurityLevel::SW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelSwSecureDecode: + return SecurityLevel::SW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureCrypto: + return SecurityLevel::HW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelHwSecureDecode: + return SecurityLevel::HW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureAll: + return SecurityLevel::HW_SECURE_ALL; + default: + return SecurityLevel::UNKNOWN; + } +} + static DrmPlugin::OfflineLicenseState toOfflineLicenseState( OfflineLicenseState licenseState) { switch(licenseState) { @@ -569,16 +586,39 @@ Return DrmHal::sendSessionLostState( return Void(); } -bool DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { +bool DrmHal::matchMimeTypeAndSecurityLevel(sp &factory, + const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level) { + if (mimeType == "") { + return true; + } else if (!factory->isContentTypeSupported(mimeType.string())) { + return false; + } + + if (level == DrmPlugin::kSecurityLevelUnknown) { + return true; + } else { + sp factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory); + if (factoryV1_2 == NULL) { + return true; + } else if (factoryV1_2->isCryptoSchemeSupported_1_2(uuid, + mimeType.string(), toHidlSecurityLevel(level))) { + return true; + } + } + return false; +} + +bool DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level) { Mutex::Autolock autoLock(mLock); for (size_t i = 0; i < mFactories.size(); i++) { - if (mFactories[i]->isCryptoSchemeSupported(uuid)) { - if (mimeType != "") { - if (mFactories[i]->isContentTypeSupported(mimeType.string())) { - return true; - } - } else { + sp factory = mFactories[i]; + if (factory->isCryptoSchemeSupported(uuid)) { + if (matchMimeTypeAndSecurityLevel(factory, uuid, mimeType, level)) { return true; } } @@ -634,30 +674,15 @@ status_t DrmHal::openSession(DrmPlugin::SecurityLevel level, Mutex::Autolock autoLock(mLock); INIT_CHECK(); - SecurityLevel hSecurityLevel; + SecurityLevel hSecurityLevel = toHidlSecurityLevel(level); bool setSecurityLevel = true; - switch(level) { - case DrmPlugin::kSecurityLevelSwSecureCrypto: - hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO; - break; - case DrmPlugin::kSecurityLevelSwSecureDecode: - hSecurityLevel = SecurityLevel::SW_SECURE_DECODE; - break; - case DrmPlugin::kSecurityLevelHwSecureCrypto: - hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO; - break; - case DrmPlugin::kSecurityLevelHwSecureDecode: - hSecurityLevel = SecurityLevel::HW_SECURE_DECODE; - break; - case DrmPlugin::kSecurityLevelHwSecureAll: - hSecurityLevel = SecurityLevel::HW_SECURE_ALL; - break; - case DrmPlugin::kSecurityLevelMax: + if (level == DrmPlugin::kSecurityLevelMax) { setSecurityLevel = false; - break; - default: - return ERROR_DRM_CANNOT_HANDLE; + } else { + if (hSecurityLevel == SecurityLevel::UNKNOWN) { + return ERROR_DRM_CANNOT_HANDLE; + } } status_t err = UNKNOWN_ERROR; diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp index 8c26317128..0f343151c8 100644 --- a/drm/libmediadrm/IDrm.cpp +++ b/drm/libmediadrm/IDrm.cpp @@ -83,11 +83,14 @@ struct BpDrm : public BpInterface { return reply.readInt32(); } - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType, + DrmPlugin::SecurityLevel level) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.write(uuid, 16); data.writeString8(mimeType); + data.writeInt32(level); + status_t status = remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); if (status != OK) { ALOGE("isCryptoSchemeSupported: binder call failed: %d", status); @@ -123,11 +126,11 @@ struct BpDrm : public BpInterface { return reply.readInt32(); } - virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel, + virtual status_t openSession(DrmPlugin::SecurityLevel level, Vector &sessionId) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - data.writeInt32(securityLevel); + data.writeInt32(level); status_t status = remote()->transact(OPEN_SESSION, data, &reply); if (status != OK) { @@ -768,7 +771,9 @@ status_t BnDrm::onTransact( uint8_t uuid[16]; data.read(uuid, sizeof(uuid)); String8 mimeType = data.readString8(); - reply->writeInt32(isCryptoSchemeSupported(uuid, mimeType)); + DrmPlugin::SecurityLevel level = + static_cast(data.readInt32()); + reply->writeInt32(isCryptoSchemeSupported(uuid, mimeType, level)); return OK; } diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp index 9d040a8fc1..9fb5bbefa5 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp @@ -34,6 +34,7 @@ namespace V1_2 { namespace clearkey { using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_1::SecurityLevel; using ::android::hardware::Void; Return DrmFactory::isCryptoSchemeSupported( @@ -41,6 +42,13 @@ Return DrmFactory::isCryptoSchemeSupported( return clearkeydrm::isClearKeyUUID(uuid.data()); } +Return DrmFactory::isCryptoSchemeSupported_1_2(const hidl_array& uuid, + const hidl_string &mimeType, + SecurityLevel level) { + return isCryptoSchemeSupported(uuid) && isContentTypeSupported(mimeType) && + level == SecurityLevel::SW_SECURE_CRYPTO; +} + Return DrmFactory::isContentTypeSupported(const hidl_string &mimeType) { // This should match the mimeTypes handed by InitDataParser. return mimeType == kIsoBmffVideoMimeType || diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h index 2dafa36936..03c434eaa3 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h @@ -28,6 +28,7 @@ namespace V1_2 { namespace clearkey { using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::drm::V1_1::SecurityLevel; using ::android::hardware::hidl_vec; const uint8_t kBlockSize = 16; //AES_BLOCK_SIZE; diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h index ff715ea67f..4ca856d63e 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h @@ -39,6 +39,10 @@ struct DrmFactory : public IDrmFactory { Return isCryptoSchemeSupported(const hidl_array& uuid) override; + Return isCryptoSchemeSupported_1_2(const hidl_array& uuid, + const hidl_string& mimeType, + SecurityLevel level) override; + Return isContentTypeSupported(const hidl_string &mimeType) override; diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h index de0f3c7bbf..7be5cf2515 100644 --- a/media/libmedia/include/media/DrmHal.h +++ b/media/libmedia/include/media/DrmHal.h @@ -38,6 +38,7 @@ using drm::V1_0::IDrmFactory; using drm::V1_0::IDrmPlugin; using drm::V1_0::IDrmPluginListener; using drm::V1_0::KeyStatus; +using drm::V1_1::SecurityLevel; using drm::V1_2::OfflineLicenseState; using ::android::hardware::hidl_vec; using ::android::hardware::Return; @@ -62,7 +63,9 @@ struct DrmHal : public BnDrm, virtual status_t initCheck() const; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType); + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], + const String8& mimeType, + DrmPlugin::SecurityLevel level); virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName); @@ -223,6 +226,10 @@ private: status_t getPropertyStringInternal(String8 const &name, String8 &value) const; status_t getPropertyByteArrayInternal(String8 const &name, Vector &value) const; + bool matchMimeTypeAndSecurityLevel(sp &factory, + const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level); DISALLOW_EVIL_CONSTRUCTORS(DrmHal); }; diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h index 49166c6857..a32756fd27 100644 --- a/media/libmedia/include/media/IDrm.h +++ b/media/libmedia/include/media/IDrm.h @@ -34,7 +34,9 @@ struct IDrm : public IInterface { virtual status_t initCheck() const = 0; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) = 0; + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel securityLevel) = 0; virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName) = 0; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp index 8d876dabe8..67a0f1e611 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp @@ -159,7 +159,8 @@ Vector NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t pss if (drm != NULL) { for (size_t i = 0; i < psshDRMs.size(); i++) { DrmUUID uuid = psshDRMs[i]; - if (drm->isCryptoSchemeSupported(uuid.ptr(), String8())) + if (drm->isCryptoSchemeSupported(uuid.ptr(), String8(), + DrmPlugin::kSecurityLevelUnknown)) supportedDRMs.add(uuid); } diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp index 55afb33803..9082f620f3 100644 --- a/media/ndk/NdkMediaDrm.cpp +++ b/media/ndk/NdkMediaDrm.cpp @@ -274,7 +274,7 @@ bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeTy } String8 mimeStr = mimeType ? String8(mimeType) : String8(""); - return drm->isCryptoSchemeSupported(uuid, mimeStr); + return drm->isCryptoSchemeSupported(uuid, mimeStr, DrmPlugin::kSecurityLevelUnknown); } EXPORT -- GitLab From a52bdd8908f53c784a993dd70b0f92751031b026 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Tue, 15 Jan 2019 12:43:41 +0530 Subject: [PATCH 0767/1530] libeffects: Corrected Concert Surround delay values for higher sampling frequencies. Adjusted delay values in LVCS modwqule for higher sampling frequencies starting from 48000 Hz. The values for higher frequencies are obtained by linearly scaling the delay value for 44100 Hz frequency. Test: Local Native Test (lvmtest) Bug: 120991000 Change-Id: I261fd1ef338474f266aa19347e44aee1412db953 --- .../lib/StereoWidening/src/LVCS_Headphone_Coeffs.h | 13 ++++++------- .../lib/StereoWidening/src/LVCS_ReverbGenerator.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h index e45d81f439..ba05577c6d 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Headphone_Coeffs.h @@ -239,13 +239,12 @@ #define LVCS_STEREODELAY_CS_24KHZ 279 /* Sample rate 24kS/s */ #define LVCS_STEREODELAY_CS_32KHZ 372 /* Sample rate 32kS/s */ #define LVCS_STEREODELAY_CS_44KHZ 512 /* Sample rate 44kS/s */ -// TODO: this should linearly scale by frequency but is limited to 512 frames until -// we ensure enough buffer size has been allocated. -#define LVCS_STEREODELAY_CS_48KHZ 512 /* Sample rate 48kS/s */ -#define LVCS_STEREODELAY_CS_88KHZ 512 /* Sample rate 88.2kS/s */ -#define LVCS_STEREODELAY_CS_96KHZ 512 /* Sample rate 96kS/s */ -#define LVCS_STEREODELAY_CS_176KHZ 512 /* Sample rate 176.4kS/s */ -#define LVCS_STEREODELAY_CS_192KHZ 512 /* Sample rate 196kS/s */ +#define LVCS_STEREODELAY_CS_48KHZ 557 /* Sample rate 48kS/s */ +#define LVCS_STEREODELAY_CS_88KHZ 1024 /* Sample rate 88.2kS/s */ +#define LVCS_STEREODELAY_CS_96KHZ 1115 /* Sample rate 96kS/s */ +#define LVCS_STEREODELAY_CS_176KHZ 2048 /* Sample rate 176.4kS/s */ +#define LVCS_STEREODELAY_CS_192KHZ 2229 /* Sample rate 196kS/s */ +#define LVCS_STEREODELAY_CS_MAX_VAL LVCS_STEREODELAY_CS_192KHZ /* Reverb coefficients for 8000 Hz sample rate, scaled with 1.038030 */ #define CS_REVERB_8000_A0 0.667271 diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h index 69892b6594..f94d4e412f 100644 --- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h +++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_ReverbGenerator.h @@ -65,7 +65,7 @@ typedef struct /* Filter */ void (*pBiquadCallBack) (Biquad_Instance_t*, LVM_INT16*, LVM_INT16*, LVM_INT16); #else - LVM_FLOAT StereoSamples[2 * LVCS_STEREODELAY_CS_48KHZ]; + LVM_FLOAT StereoSamples[2 * LVCS_STEREODELAY_CS_MAX_VAL]; /* Reverb Level */ LVM_FLOAT ReverbLevel; /* Filter */ -- GitLab From 45d37a11ac42575ec8ddc1f736acfc0e1292e3b4 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Sun, 20 Jan 2019 14:19:59 -0800 Subject: [PATCH 0768/1530] Set up codec publication to include AV1 codec May need tuning based on performance information Bug: 111936705 Test: sample playback Change-Id: Ic7a63e4094ce405117aea5bb04b2fd8fb6016591 --- .../libstagefright/data/media_codecs_google_c2_video.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml index adb45b37ee..c49789e378 100644 --- a/media/libstagefright/data/media_codecs_google_c2_video.xml +++ b/media/libstagefright/data/media_codecs_google_c2_video.xml @@ -71,6 +71,15 @@
    + + + + + + + + + -- GitLab From 9eb18550939321e37affd2260e3bb72716cb33d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 5 Nov 2018 10:33:26 +0100 Subject: [PATCH 0769/1530] audiopolicy: apm: updateCallRouting shall use SW Bridging if no external route support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While using Routing3.0 API, and XML configuration file, the route section shall declare the topology supported by a given platform. A Device sink to device source route declaration implies that an external route is supported and AudioHAL can configure it. In case of call, even if the Rx and Tx device belongs to respectively primary output and input of the primary module, it does not mean AudioHAL can connect the telephony Rx and Tx by itself unless a Route it declared. This is the purpose of this patch to take benefit of XML file. Test: make Change-Id: Id62ab3889f20b4daacf7adcce4d7688caf509443 Signed-off-by: François Gaffie --- .../managerdefault/AudioPolicyManager.cpp | 58 +++++++++++++++---- .../managerdefault/AudioPolicyManager.h | 7 +++ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cc151e7d8a..262ee8b8cf 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -486,6 +486,7 @@ status_t AudioPolicyManager::getHwOffloadEncodingFormatsSupportedForA2DP( uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs) { bool createTxPatch = false; + bool createRxPatch = false; uint32_t muteWaitMs = 0; if(!hasPrimaryOutput() || mPrimaryOutput->devices().types() == AUDIO_DEVICE_OUT_STUB) { @@ -494,9 +495,10 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device"); audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION }; - auto txDevice = getDeviceAndMixForAttributes(attr); + auto txSourceDevice = getDeviceAndMixForAttributes(attr); + ALOG_ASSERT(txSourceDevice != 0, "updateCallRouting() input selected device not available"); ALOGV("updateCallRouting device rxDevice %s txDevice %s", - rxDevices.toString().c_str(), txDevice->toString().c_str()); + rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str()); // release existing RX patch if any if (mCallRxPatch != 0) { @@ -509,22 +511,54 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui mCallTxPatch.clear(); } - // If the RX device is on the primary HW module, then use legacy routing method for voice calls - // via setOutputDevice() on primary output. - // Otherwise, create two audio patches for TX and RX path. - if (availablePrimaryOutputDevices().contains(rxDevices.itemAt(0))) { - muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs); + auto telephonyRxModule = + mHwModules.getModuleForDeviceTypes(AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT); + auto telephonyTxModule = + mHwModules.getModuleForDeviceTypes(AUDIO_DEVICE_OUT_TELEPHONY_TX, AUDIO_FORMAT_DEFAULT); + // retrieve Rx Source and Tx Sink device descriptors + sp rxSourceDevice = + mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_TELEPHONY_RX, + String8(), + AUDIO_FORMAT_DEFAULT); + sp txSinkDevice = + mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX, + String8(), + AUDIO_FORMAT_DEFAULT); + + // RX and TX Telephony device are declared by Primary Audio HAL + if (isPrimaryModule(telephonyRxModule) && isPrimaryModule(telephonyTxModule) && + (telephonyRxModule->getHalVersionMajor() >= 3)) { + if (rxSourceDevice == 0 || txSinkDevice == 0) { + // RX / TX Telephony device(s) is(are) not currently available + ALOGE("updateCallRouting() no telephony Tx and/or RX device"); + return muteWaitMs; + } + // do not create a patch (aka Sw Bridging) if Primary HW module has declared supporting a + // route between telephony RX to Sink device and Source device to telephony TX + const auto &primaryModule = telephonyRxModule; + createRxPatch = !primaryModule->supportsPatch(rxSourceDevice, rxDevices.itemAt(0)); + createTxPatch = !primaryModule->supportsPatch(txSourceDevice, txSinkDevice); + } else { + // If the RX device is on the primary HW module, then use legacy routing method for + // voice calls via setOutputDevice() on primary output. + // Otherwise, create two audio patches for TX and RX path. + createRxPatch = !(availablePrimaryOutputDevices().contains(rxDevices.itemAt(0))) && + (rxSourceDevice != 0); // If the TX device is also on the primary HW module, setOutputDevice() will take care // of it due to legacy implementation. If not, create a patch. - if (!availablePrimaryModuleInputDevices().contains(txDevice)) { - createTxPatch = true; - } + createTxPatch = !(availablePrimaryModuleInputDevices().contains(txSourceDevice)) && + (txSinkDevice != 0); + } + // Use legacy routing method for voice calls via setOutputDevice() on primary output. + // Otherwise, create two audio patches for TX and RX path. + if (!createRxPatch) { + muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs); } else { // create RX path audio patch mCallRxPatch = createTelephonyPatch(true /*isRx*/, rxDevices.itemAt(0), delayMs); - createTxPatch = true; + ALOG_ASSERT(createTxPatch, "No Tx Patch will be created, nor legacy routing done"); } if (createTxPatch) { // create TX path audio patch - mCallTxPatch = createTelephonyPatch(false /*isRx*/, txDevice, delayMs); + mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs); } return muteWaitMs; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index fb1f7cb4e2..de6d489893 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -517,6 +517,13 @@ protected: return mAudioPatches.removeAudioPatch(handle); } + bool isPrimaryModule(const sp &module) const + { + if (module == 0 || !hasPrimaryOutput()) { + return false; + } + return module->getHandle() == mPrimaryOutput->getModuleHandle(); + } DeviceVector availablePrimaryOutputDevices() const { if (!hasPrimaryOutput()) { -- GitLab From a807ef99610c73ace4627ca65b4c3eb4ed84b604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 5 Nov 2018 10:44:33 +0100 Subject: [PATCH 0770/1530] audiopolicy: apm: Fix default bus device issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a bus device is used as default, when calling setForceUse, a given SwOutputDescriptor might be asked to switch to another bus device (the default), violating the Policy Mix rule. Test: see below -start an automotive device with PolicyMix registered and bus device as the default. -Consider having a system bus for system sound and media for music. -Play touch sound (check played in system bus). -launch music player. Result: -Music shall be played on media bus device -Touch sound still played on system bus device Change-Id: I6668cffa525af51af6ed0838745658f72ae00a62 Signed-off-by: François Gaffie --- .../include/AudioPolicyMix.h | 17 +++++++++++++-- .../managerdefinitions/src/AudioPolicyMix.cpp | 21 +++++++++++++++++++ .../managerdefault/AudioPolicyManager.cpp | 10 +++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index e6a62d9a8e..2932296cb6 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -23,9 +23,10 @@ #include #include -namespace android { +#include +#include -class SwAudioOutputDescriptor; +namespace android { /** * custom mix entry in mPolicyMixes @@ -79,6 +80,18 @@ public: const DeviceVector &availableDeviceTypes, AudioMix **policyMix); + /** + * @brief try to find a matching mix for a given output descriptor and returns the associated + * output device. + * @param output to be considered + * @param availableOutputDevices list of output devices currently reachable + * @param policyMix to be returned if any mix matching ouput descriptor + * @return device selected from the mix attached to the output, null pointer otherwise + */ + sp getDeviceAndMixForOutput(const sp &output, + const DeviceVector &availableOutputDevices, + AudioMix **policyMix = nullptr); + status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 799950c534..3b9411a068 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -280,6 +280,27 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } +sp AudioPolicyMixCollection::getDeviceAndMixForOutput( + const sp &output, + const DeviceVector &availableOutputDevices, + AudioMix **policyMix) +{ + for (size_t i = 0; i < size(); i++) { + if (valueAt(i)->getOutput() == output) { + AudioMix *mix = valueAt(i)->getMix(); + if (policyMix != nullptr) + *policyMix = mix; + // This Desc is involved in a Mix, which has the highest prio + audio_devices_t deviceType = mix->mDeviceType; + String8 address = mix->mDeviceAddress; + ALOGV("%s: device (0x%x, addr=%s) forced by mix", + __FUNCTION__, deviceType, address.c_str()); + return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT); + } + } + return nullptr; +} + sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( audio_source_t inputSource, const DeviceVector &availDevices, AudioMix **policyMix) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 262ee8b8cf..0d9663aa1c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -790,6 +790,9 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, sp outputDesc = mOutputs.valueAt(i); DeviceVector newDevices = getNewOutputDevices(outputDesc, true /*fromCache*/); if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) { + // As done in setDeviceConnectionState, we could also fix default device issue by + // preventing the force re-routing in case of default dev that distinguishes on address. + // Let's give back to engine full device choice decision however. waitMs = setOutputDevices(outputDesc, newDevices, !newDevices.isEmpty(), delayMs); } if (forceVolumeReeval && !newDevices.isEmpty()) { @@ -4969,6 +4972,13 @@ DeviceVector AudioPolicyManager::getNewOutputDevices(const sp Date: Fri, 4 Jan 2019 16:07:50 +0100 Subject: [PATCH 0771/1530] audiopolicy: engineconfigurable: fix legacy remote submix Test: make Change-Id: I57d364f7b1db5833f51ed75c2dba1f198414a54f Signed-off-by: Francois Gaffie --- .../Settings/device_for_strategy_accessibility.pfw | 1 + .../examples/Settings/device_for_strategy_dtmf.pfw | 1 + .../Settings/device_for_strategy_enforced_audible.pfw | 1 + .../examples/Settings/device_for_strategy_media.pfw | 1 + .../examples/Settings/device_for_strategy_rerouting.pfw | 1 + .../wrapper/config/policy_criterion_types.xml.in | 7 ++++++- 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw index eb119805e7..7c87c80fc7 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw @@ -28,6 +28,7 @@ supDomain: DeviceForStrategy TelephonyMode IsNot InCall TelephonyMode IsNot InCommunication AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 component: /Policy/policy/strategies/accessibility/selected_output_devices/mask remote_submix = 1 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw index 883c741afb..c830c42190 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw @@ -20,6 +20,7 @@ supDomain: DeviceForStrategy TelephonyMode IsNot InCall TelephonyMode IsNot InCommunication AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 component: /Policy/policy/strategies/dtmf/selected_output_devices/mask remote_submix = 1 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw index f5046319b0..c6411381fa 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw @@ -61,6 +61,7 @@ supDomain: DeviceForStrategy domain: Device2 conf: RemoteSubmix AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask remote_submix = 1 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw index bdb6ae0f7c..f8bab3dbbe 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw @@ -19,6 +19,7 @@ domainGroup: DeviceForStrategy domain: Device2 conf: RemoteSubmix AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 component: /Policy/policy/strategies/media/selected_output_devices/mask speaker = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw index 04e62f76d3..28a3629748 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw @@ -24,6 +24,7 @@ domainGroup: DeviceForStrategy domain: Device2 conf: RemoteSubmix AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 component: /Policy/policy/strategies/rerouting/selected_output_devices/mask remote_submix = 1 diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in index 6cb799f3ef..fe17369d28 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in +++ b/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in @@ -16,7 +16,12 @@ - + + + + + + -- GitLab From 9c17d8d36c0bcc5577dc80ec536c2db379d622e7 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 22 Jan 2019 20:49:01 -0800 Subject: [PATCH 0772/1530] stagefright: add c2 gsm and mpeg2 decoders to codec xmls Bug: 112362730 Change-Id: I8508b3658f099e084ca9d2fa4bcde2bfab9acf7f --- .../data/media_codecs_google_c2_telephony.xml | 25 ++++++++++++++++ .../data/media_codecs_google_c2_tv.xml | 29 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 media/libstagefright/data/media_codecs_google_c2_telephony.xml create mode 100644 media/libstagefright/data/media_codecs_google_c2_tv.xml diff --git a/media/libstagefright/data/media_codecs_google_c2_telephony.xml b/media/libstagefright/data/media_codecs_google_c2_telephony.xml new file mode 100644 index 0000000000..d1055b318a --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_c2_telephony.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/media/libstagefright/data/media_codecs_google_c2_tv.xml b/media/libstagefright/data/media_codecs_google_c2_tv.xml new file mode 100644 index 0000000000..fa082c7276 --- /dev/null +++ b/media/libstagefright/data/media_codecs_google_c2_tv.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + -- GitLab From bf5b9565bc82ad6a246204668e3229d9b40406c3 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Wed, 23 Jan 2019 14:17:02 +0900 Subject: [PATCH 0773/1530] Remove Constructor of RemoteUserInfo which takes IBinder Bug: 119752205, Bug: 122550211 Test: make -j; Change-Id: I6c9664d67ec9f00aec563996d2e45cec2e9397bc --- .../apex/java/android/media/session/MediaSession.java | 3 +-- .../apex/java/android/service/media/MediaBrowserService.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java index 73e16a65b6..3cbeff9e25 100644 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java @@ -1077,8 +1077,7 @@ public final class MediaSession { private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, ISessionControllerCallback caller) { - return new RemoteUserInfo(packageName, pid, uid, - caller != null ? caller.asBinder() : null); + return new RemoteUserInfo(packageName, pid, uid); } @Override diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java index a66ec352c2..76c99b9945 100644 --- a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java +++ b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java @@ -544,8 +544,7 @@ public abstract class MediaBrowserService extends Service { throw new IllegalStateException("This should be called inside of onGetRoot or" + " onLoadChildren or onLoadItem methods"); } - return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid, - mCurConnection.callbacks.asBinder()); + return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); } /** -- GitLab From 12de2b95de10c905c0982869b0243f9916560438 Mon Sep 17 00:00:00 2001 From: wuhuang Date: Fri, 18 Jan 2019 17:14:03 +0800 Subject: [PATCH 0774/1530] mp4 extractor: Too big sample timestamp can lead to overflow Exception. seekTo in the video track will query the timestamp, PTS of the sample according to the seekTo time. The PTS value greater than UNIT_32_Max can cause the uint_32 overflow during the calculation. Test: To test the videoes that the PTS value is greater than UNIT_32_Max, find out the relative overflows in the logs. Change-Id: I796e3abcd7d0aad1614c07119a3012fc641266d8 --- media/extractors/mp4/MPEG4Extractor.cpp | 8 +++---- media/extractors/mp4/SampleIterator.cpp | 10 ++++----- media/extractors/mp4/SampleIterator.h | 14 ++++++------ media/extractors/mp4/SampleTable.cpp | 30 ++++++++++++------------- media/extractors/mp4/SampleTable.h | 6 ++--- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index cc1534a323..3a58421612 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -594,7 +594,7 @@ media_status_t MPEG4Extractor::getTrackMetaData( } } else { uint32_t sampleIndex; - uint32_t sampleTime; + uint64_t sampleTime; if (track->timescale != 0 && track->sampleTable->findThumbnailSample(&sampleIndex) == OK && track->sampleTable->getMetaDataForSample( @@ -4503,7 +4503,7 @@ status_t MPEG4Source::init() { } if (!strncasecmp("video/", mime, 6)) { - uint32_t firstSampleCTS = 0; + uint64_t firstSampleCTS = 0; err = mSampleTable->getMetaDataForSample(0, NULL, NULL, &firstSampleCTS); // Start offset should be less or equal to composition time of first sample. // Composition time stamp of first sample cannot be negative. @@ -5349,7 +5349,7 @@ media_status_t MPEG4Source::read( sampleIndex, &syncSampleIndex, findFlags); } - uint32_t sampleTime; + uint64_t sampleTime; if (err == OK) { err = mSampleTable->getMetaDataForSample( sampleIndex, NULL, NULL, &sampleTime); @@ -5399,7 +5399,7 @@ media_status_t MPEG4Source::read( off64_t offset = 0; size_t size = 0; - uint32_t cts, stts; + uint64_t cts, stts; bool isSyncSample; bool newBuffer = false; if (mBuffer == NULL) { diff --git a/media/extractors/mp4/SampleIterator.cpp b/media/extractors/mp4/SampleIterator.cpp index 1a6d306dbd..d6287ec40e 100644 --- a/media/extractors/mp4/SampleIterator.cpp +++ b/media/extractors/mp4/SampleIterator.cpp @@ -301,7 +301,7 @@ status_t SampleIterator::getSampleSizeDirect( } status_t SampleIterator::findSampleTimeAndDuration( - uint32_t sampleIndex, uint32_t *time, uint32_t *duration) { + uint32_t sampleIndex, uint64_t *time, uint64_t *duration) { if (sampleIndex >= mTable->mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } @@ -314,8 +314,8 @@ status_t SampleIterator::findSampleTimeAndDuration( break; } if (mTimeToSampleIndex == mTable->mTimeToSampleCount || - (mTTSDuration != 0 && mTTSCount > UINT32_MAX / mTTSDuration) || - mTTSSampleTime > UINT32_MAX - (mTTSCount * mTTSDuration)) { + (mTTSDuration != 0 && mTTSCount > UINT64_MAX / mTTSDuration) || + mTTSSampleTime > UINT64_MAX - (mTTSCount * mTTSDuration)) { return ERROR_OUT_OF_RANGE; } @@ -341,8 +341,8 @@ status_t SampleIterator::findSampleTimeAndDuration( int32_t offset = mTable->getCompositionTimeOffset(sampleIndex); if ((offset < 0 && *time < (offset == INT32_MIN ? INT32_MAX : uint32_t(-offset))) || - (offset > 0 && *time > UINT32_MAX - offset)) { - ALOGE("%u + %d would overflow", *time, offset); + (offset > 0 && *time > UINT64_MAX - offset)) { + ALOGE("%llu + %d would overflow", (unsigned long long) *time, offset); return ERROR_OUT_OF_RANGE; } if (offset > 0) { diff --git a/media/extractors/mp4/SampleIterator.h b/media/extractors/mp4/SampleIterator.h index 6e4f60eccc..5a0ea76c3c 100644 --- a/media/extractors/mp4/SampleIterator.h +++ b/media/extractors/mp4/SampleIterator.h @@ -33,8 +33,8 @@ struct SampleIterator { uint32_t getDescIndex() const { return mChunkDesc; } off64_t getSampleOffset() const { return mCurrentSampleOffset; } size_t getSampleSize() const { return mCurrentSampleSize; } - uint32_t getSampleTime() const { return mCurrentSampleTime; } - uint32_t getSampleDuration() const { return mCurrentSampleDuration; } + uint64_t getSampleTime() const { return mCurrentSampleTime; } + uint64_t getSampleDuration() const { return mCurrentSampleDuration; } uint32_t getLastSampleIndexInChunk() const { return mCurrentSampleIndex + mSamplesPerChunk - @@ -63,20 +63,20 @@ private: uint32_t mTimeToSampleIndex; uint32_t mTTSSampleIndex; - uint32_t mTTSSampleTime; + uint64_t mTTSSampleTime; uint32_t mTTSCount; - uint32_t mTTSDuration; + uint64_t mTTSDuration; uint32_t mCurrentSampleIndex; off64_t mCurrentSampleOffset; size_t mCurrentSampleSize; - uint32_t mCurrentSampleTime; - uint32_t mCurrentSampleDuration; + uint64_t mCurrentSampleTime; + uint64_t mCurrentSampleDuration; void reset(); status_t findChunkRange(uint32_t sampleIndex); status_t getChunkOffset(uint32_t chunk, off64_t *offset); - status_t findSampleTimeAndDuration(uint32_t sampleIndex, uint32_t *time, uint32_t *duration); + status_t findSampleTimeAndDuration(uint32_t sampleIndex, uint64_t *time, uint64_t *duration); SampleIterator(const SampleIterator &); SampleIterator &operator=(const SampleIterator &); diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp index d242798c73..571b441a52 100644 --- a/media/extractors/mp4/SampleTable.cpp +++ b/media/extractors/mp4/SampleTable.cpp @@ -614,7 +614,7 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { return OK; } -uint32_t abs_difference(uint32_t time1, uint32_t time2) { +uint32_t abs_difference(uint64_t time1, uint64_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } @@ -662,7 +662,7 @@ void SampleTable::buildSampleEntriesTable() { } uint32_t sampleIndex = 0; - uint32_t sampleTime = 0; + uint64_t sampleTime = 0; for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; @@ -684,13 +684,13 @@ void SampleTable::buildSampleEntriesTable() { (compTimeDelta == INT32_MIN ? INT32_MAX : uint32_t(-compTimeDelta))) || (compTimeDelta > 0 && - sampleTime > UINT32_MAX - compTimeDelta)) { - ALOGE("%u + %d would overflow, clamping", - sampleTime, compTimeDelta); + sampleTime > UINT64_MAX - compTimeDelta)) { + ALOGE("%llu + %d would overflow, clamping", + (unsigned long long) sampleTime, compTimeDelta); if (compTimeDelta < 0) { sampleTime = 0; } else { - sampleTime = UINT32_MAX; + sampleTime = UINT64_MAX; } compTimeDelta = 0; } @@ -701,10 +701,10 @@ void SampleTable::buildSampleEntriesTable() { } ++sampleIndex; - if (sampleTime > UINT32_MAX - delta) { - ALOGE("%u + %u would overflow, clamping", - sampleTime, delta); - sampleTime = UINT32_MAX; + if (sampleTime > UINT64_MAX - delta) { + ALOGE("%llu + %u would overflow, clamping", + (unsigned long long) sampleTime, delta); + sampleTime = UINT64_MAX; } else { sampleTime += delta; } @@ -870,19 +870,19 @@ status_t SampleTable::findSyncSampleNear( if (err != OK) { return err; } - uint32_t sample_time = mSampleIterator->getSampleTime(); + uint64_t sample_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(mSyncSamples[left]); if (err != OK) { return err; } - uint32_t upper_time = mSampleIterator->getSampleTime(); + uint64_t upper_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(mSyncSamples[left - 1]); if (err != OK) { return err; } - uint32_t lower_time = mSampleIterator->getSampleTime(); + uint64_t lower_time = mSampleIterator->getSampleTime(); // use abs_difference for safety if (abs_difference(upper_time, sample_time) > @@ -955,9 +955,9 @@ status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *compositionTime, + uint64_t *compositionTime, bool *isSyncSample, - uint32_t *sampleDuration) { + uint64_t *sampleDuration) { Mutex::Autolock autoLock(mLock); status_t err; diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/SampleTable.h index d4b5dc84c1..57f6e624e7 100644 --- a/media/extractors/mp4/SampleTable.h +++ b/media/extractors/mp4/SampleTable.h @@ -66,9 +66,9 @@ public: uint32_t sampleIndex, off64_t *offset, size_t *size, - uint32_t *compositionTime, + uint64_t *compositionTime, bool *isSyncSample = NULL, - uint32_t *sampleDuration = NULL); + uint64_t *sampleDuration = NULL); // call only after getMetaDataForSample has been called successfully. uint32_t getLastSampleIndexInChunk(); @@ -124,7 +124,7 @@ private: struct SampleTimeEntry { uint32_t mSampleIndex; - uint32_t mCompositionTime; + uint64_t mCompositionTime; }; SampleTimeEntry *mSampleTimeEntries; -- GitLab From 350b3590701f58584a9fdae663f81fc0d3ea98d6 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 7 Dec 2018 17:03:55 -0800 Subject: [PATCH 0775/1530] Switch mediaswcodec to 64 bit Bug: 112004561 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I90edbe5ce637fb77b4a2a1dad4830bc4c8ef9b68 --- apex/Android.bp | 1 - services/mediacodec/Android.mk | 21 +++---- services/mediacodec/main_swcodecservice.cpp | 5 +- .../seccomp_policy/mediaswcodec-arm64.policy | 61 +++++++++++++++++++ .../seccomp_policy/mediaswcodec-x86_64.policy | 1 + 5 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy create mode 120000 services/mediacodec/seccomp_policy/mediaswcodec-x86_64.policy diff --git a/apex/Android.bp b/apex/Android.bp index 422880a6ba..39997d2ea6 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -36,7 +36,6 @@ apex { apex { name: "com.android.media.swcodec", - compile_multilib: "32", manifest: "manifest_codec.json", native_shared_libs: [ "libmedia_codecserviceregistrant", diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index a104ee5ced..6a71d7d34b 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -70,8 +70,11 @@ include $(CLEAR_VARS) # seccomp is not required for coverage build. ifneq ($(NATIVE_COVERAGE),true) LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaswcodec.policy +LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaswcodec.policy LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaswcodec.policy +LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaswcodec.policy endif + LOCAL_SRC_FILES := \ main_swcodecservice.cpp \ MediaCodecUpdateService.cpp \ @@ -107,8 +110,12 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE := mediaswcodec LOCAL_INIT_RC := mediaswcodec.rc -LOCAL_32_BIT_ONLY := true LOCAL_SANITIZE := scudo +ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86_64 arm64)) + LOCAL_MULTILIB := both + LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 + LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE) +endif sanitizer_runtime_libraries := llndk_libraries := @@ -145,17 +152,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := mediaswcodec.policy LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy -# mediaswcodec runs in 32-bit combatibility mode. For 64 bit architectures, -# use the 32 bit policy -ifdef TARGET_2ND_ARCH - ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true) - LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_2ND_ARCH).policy - else - LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy - endif -else - LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy -endif +LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy include $(BUILD_PREBUILT) endif diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index 12397fb4c5..05b5695414 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -45,8 +45,11 @@ int main(int argc __unused, char** /*argv*/) ::android::hardware::configureRpcThreadpool(64, false); - // codec libs are currently 32-bit only +#ifdef __LP64__ + loadFromApex("/apex/com.android.media.swcodec/lib64"); +#else loadFromApex("/apex/com.android.media.swcodec/lib"); +#endif ::android::hardware::joinRpcThreadpool(); } diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy new file mode 100644 index 0000000000..1bee1b5478 --- /dev/null +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy @@ -0,0 +1,61 @@ +# Copyright (C) 2019 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. + +futex: 1 +# ioctl calls are filtered via the selinux policy. +ioctl: 1 +sched_yield: 1 +close: 1 +dup: 1 +ppoll: 1 +mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +getuid: 1 +getrlimit: 1 +fstat: 1 +newfstatat: 1 +fstatfs: 1 + +# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail +# parser support for '<' is in this needs to be modified to also prevent +# |old_address| and |new_address| from touching the exception vector page, which +# on ARM is statically loaded at 0xffff 0000. See +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html +# for more details. +mremap: arg3 == 3 +munmap: 1 +prctl: 1 +writev: 1 +sigaltstack: 1 +clone: 1 +exit: 1 +lseek: 1 +rt_sigprocmask: 1 +openat: 1 +write: 1 +nanosleep: 1 +setpriority: 1 +set_tid_address: 1 +getdents64: 1 +readlinkat: 1 +read: 1 +pread64: 1 +gettimeofday: 1 +faccessat: 1 +exit_group: 1 +restart_syscall: 1 +rt_sigreturn: 1 +getrandom: 1 +madvise: 1 + diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-x86_64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-x86_64.policy new file mode 120000 index 0000000000..ab2592a713 --- /dev/null +++ b/services/mediacodec/seccomp_policy/mediaswcodec-x86_64.policy @@ -0,0 +1 @@ +mediacodec-x86.policy \ No newline at end of file -- GitLab From 0c1c7a2351f73a76bb8cabe2eb134cca98b7965d Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 17 Jan 2019 14:58:28 -0800 Subject: [PATCH 0776/1530] audiopolicy: Read supported encoded formats from config file Adds reading of 'encodedFormats' attribute in 'devicePort'. Keep fallback for an empty value for HDMI in case the framework runs against pre 5.0 audio HAL. Add generic (example) APM config files for TV devices. Bug: 120860184 Test: on a TV device tested that Pixel 1 a_p_c.xml file is not updated Change-Id: I85013a9e6172decd6ad72079d9c77e5f80e29d19 --- .../src/DeviceDescriptor.cpp | 8 ++- .../audio_policy_configuration_generic.xml | 6 +++ .../audio_policy_configuration_generic_tv.xml | 49 +++++++++++++++++++ .../audio_policy_configuration_stub.xml | 4 ++ .../primary_audio_policy_configuration.xml | 2 +- .../primary_audio_policy_configuration_tv.xml | 26 ++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 services/audiopolicy/config/audio_policy_configuration_generic_tv.xml create mode 100644 services/audiopolicy/config/primary_audio_policy_configuration_tv.xml diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 2ed8455458..1bc4ec848a 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -42,8 +42,12 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { mAddress = String8("0"); } - /* FIXME: read from APM config file */ - if (type == AUDIO_DEVICE_OUT_HDMI) { + /* If framework runs against a pre 5.0 Audio HAL, encoded formats are absent from the config. + * FIXME: APM should know the version of the HAL and don't add the formats for V5.0. + * For now, the workaround to remove AC3 and IEC61937 support on HDMI is to declare + * something like 'encodedFormats="AUDIO_FORMAT_PCM_16_BIT"' on the HDMI devicePort. + */ + if (type == AUDIO_DEVICE_OUT_HDMI && mEncodedFormats.isEmpty()) { mEncodedFormats.add(AUDIO_FORMAT_AC3); mEncodedFormats.add(AUDIO_FORMAT_IEC61937); } diff --git a/services/audiopolicy/config/audio_policy_configuration_generic.xml b/services/audiopolicy/config/audio_policy_configuration_generic.xml index 58768c3d01..40dcc22b58 100644 --- a/services/audiopolicy/config/audio_policy_configuration_generic.xml +++ b/services/audiopolicy/config/audio_policy_configuration_generic.xml @@ -37,4 +37,10 @@ + + + + + + diff --git a/services/audiopolicy/config/audio_policy_configuration_generic_tv.xml b/services/audiopolicy/config/audio_policy_configuration_generic_tv.xml new file mode 100644 index 0000000000..5f1ca31ff8 --- /dev/null +++ b/services/audiopolicy/config/audio_policy_configuration_generic_tv.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/config/audio_policy_configuration_stub.xml b/services/audiopolicy/config/audio_policy_configuration_stub.xml index 26c381f561..8350eb8e6c 100644 --- a/services/audiopolicy/config/audio_policy_configuration_stub.xml +++ b/services/audiopolicy/config/audio_policy_configuration_stub.xml @@ -15,6 +15,9 @@ --> + + + @@ -26,5 +29,6 @@ + diff --git a/services/audiopolicy/config/primary_audio_policy_configuration.xml b/services/audiopolicy/config/primary_audio_policy_configuration.xml index 5b7ae7f756..eedc96bf5b 100644 --- a/services/audiopolicy/config/primary_audio_policy_configuration.xml +++ b/services/audiopolicy/config/primary_audio_policy_configuration.xml @@ -1,5 +1,5 @@ - + Speaker diff --git a/services/audiopolicy/config/primary_audio_policy_configuration_tv.xml b/services/audiopolicy/config/primary_audio_policy_configuration_tv.xml new file mode 100644 index 0000000000..826015a42d --- /dev/null +++ b/services/audiopolicy/config/primary_audio_policy_configuration_tv.xml @@ -0,0 +1,26 @@ + + + + + Speaker + + Speaker + + + + + + + + + + + + + + + + -- GitLab From d55083d1449834d523cd3d9b5976b7e5011dce03 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 22 Jan 2019 14:11:02 -0800 Subject: [PATCH 0777/1530] audio: add new audio formats Add type converter for LHDC Bluetooth codec audio format. Bug: 120395342 Test: make Change-Id: I8e18db9c86ad8f71f99cf72c18372895557153bc --- media/libmedia/TypeConverter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index b4ceeb0408..aa77cd3a5b 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -215,6 +215,8 @@ const FormatConverter::Table FormatConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_HE_V2), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_CELT), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_ADAPTIVE), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC_LL), TERMINATOR }; -- GitLab From dc3ad9abfe646cbbac5e2d14647cf52c74a72641 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Tue, 22 Jan 2019 11:40:32 -0800 Subject: [PATCH 0778/1530] audio: Add type conversion entries for audio formats Add type conversion entries for AAC_LATM*, CELT and APTX_ADAPTIVE formats. Bug: 123082414 Test: make Change-Id: I5ff9af7126cab00f04322e060d2e5c0fd06d6811 --- media/libmedia/TypeConverter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index c24e046bbe..b4ceeb0408 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -209,6 +209,12 @@ const FormatConverter::Table FormatConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_1_0), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_0), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_1), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_LC), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_HE_V1), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_LATM_HE_V2), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_CELT), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_ADAPTIVE), TERMINATOR }; -- GitLab From fe0fb8b050e0acc8f749bcee482b8c157ed8233b Mon Sep 17 00:00:00 2001 From: Revathi Uddaraju Date: Thu, 27 Jul 2017 17:05:37 +0800 Subject: [PATCH 0779/1530] audiopolicy: fix wrong volume db value for voice call stream When apply mute for voice stream, vol index 0 is set, while voice stream minimum vol index is 1. This will cause negative vol index is calculated and maximum db value is set. Ensure vol index is valide to calculate. Also add range check in rescaleVolumeIndex(). Bug: 123063166 Test: make CRs-Fixed: 2056642 Change-Id: I89ee64d0914a971194a72b4de483d88b5029d80e --- .../common/managerdefinitions/src/VolumeCurve.cpp | 12 +++++++++--- .../managerdefault/AudioPolicyManager.cpp | 13 +++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp index 620f361c54..2625733f20 100644 --- a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp @@ -26,16 +26,22 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) { ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve"); - size_t nbCurvePoints = mCurvePoints.size(); - // the volume index in the UI is relative to the min and max volume indices for this stream - int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex; if (indexInUi < volIndexMin) { + // an index of 0 means mute request when volIndexMin > 0 + if (indexInUi == 0) { + ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin); + return VOLUME_MIN_DB; + } ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin); indexInUi = volIndexMin; } else if (indexInUi > volIndexMax) { ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax); indexInUi = volIndexMax; } + + size_t nbCurvePoints = mCurvePoints.size(); + // the volume index in the UI is relative to the min and max volume indices for this stream + int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex; int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin); // Where would this volume index been inserted in the curve point diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cc151e7d8a..0ea3d728a8 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2290,6 +2290,10 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, int indexMax) { ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMax < 0) { + ALOGE("%s for stream %d: invalid min %d or max %d", __func__, stream , indexMin, indexMax); + return; + } mVolumeCurves->initStreamVolume(stream, indexMin, indexMax); // initialize other private stream volumes which follow this one @@ -5624,6 +5628,15 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream); float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream); + // preserve mute request or correct range + if (srcIndex < minSrc) { + if (srcIndex == 0) { + return 0; + } + srcIndex = minSrc; + } else if (srcIndex > maxSrc) { + srcIndex = maxSrc; + } return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc)); } -- GitLab From 0b4530ddb0871c544901191c8746e35a25d270c9 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Fri, 18 Jan 2019 12:12:03 -0800 Subject: [PATCH 0780/1530] AMediaExtractor: expand subsample sizes from 32-bit to size_t Bug: 121387843 Test: MediaPlayer2DrmTest Change-Id: I6ca467cb62daca3722016939a68a2ffd49dabd56 --- media/ndk/NdkMediaExtractor.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index 8296598d44..28e4f12880 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -46,6 +46,18 @@ struct AMediaExtractor { sp mPsshBuf; }; +sp U32ArrayToSizeBuf(size_t numSubSamples, uint32_t *data) { + if (numSubSamples > SIZE_MAX / sizeof(size_t)) { + return NULL; + } + sp sizebuf = new ABuffer(numSubSamples * sizeof(size_t)); + size_t *sizes = (size_t *)sizebuf->data(); + for (size_t i = 0; sizes != NULL && i < numSubSamples; i++) { + sizes[i] = data[i]; + } + return sizebuf; +} + extern "C" { EXPORT @@ -339,7 +351,7 @@ AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) { return NULL; } - size_t numSubSamples = cryptedsize / sizeof(size_t); + size_t numSubSamples = cryptedsize / sizeof(uint32_t); const void *cleardata; size_t clearsize; @@ -373,6 +385,16 @@ AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) mode = CryptoPlugin::kMode_AES_CTR; } + if (sizeof(uint32_t) != sizeof(size_t)) { + sp clearbuf = U32ArrayToSizeBuf(numSubSamples, (uint32_t *)cleardata); + sp cryptedbuf = U32ArrayToSizeBuf(numSubSamples, (uint32_t *)crypteddata); + cleardata = clearbuf == NULL ? NULL : clearbuf->data(); + crypteddata = crypteddata == NULL ? NULL : cryptedbuf->data(); + if(crypteddata == NULL || cleardata == NULL) { + return NULL; + } + } + return AMediaCodecCryptoInfo_new( numSubSamples, (uint8_t*) key, -- GitLab From bcbb3006925bc2aee73b555b2d9e85c17597a115 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Wed, 23 Jan 2019 13:53:33 -0800 Subject: [PATCH 0781/1530] Deal with new PROCESS_STATE Introduced a new PROCESS_STATE_FOREGROUND_SERVICE_LOCATION. Deal with it. Bug: 111453223 Test: atest CtsAppTestCases:ActivityManagerProcessStateTest Change-Id: I918bd58bfc7df0849a1d8548c785be756f7103e1 --- services/audiopolicy/service/AudioPolicyService.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 416817ff1e..a39477dd30 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -880,9 +880,8 @@ bool AudioPolicyService::UidPolicy::isA11yOnTop() { if (it == mA11yUids.end()) { continue; } - if (uid.second.second == ActivityManager::PROCESS_STATE_TOP || - uid.second.second == ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE || - uid.second.second == ActivityManager::PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + if (uid.second.second >= ActivityManager::PROCESS_STATE_TOP + && uid.second.second <= ActivityManager::PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { return true; } } -- GitLab From b89991f51c918f0678be6dabd3c262863a94404c Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Thu, 10 Jan 2019 19:26:43 +0530 Subject: [PATCH 0782/1530] libeffects: Added standalone testbench that calculates SNR value Added a standalone test bench that calculates SNR values using 2 files as input. This tool is used to test the multichannel changes. Test: build_and_run_all_unit_tests.sh Bug: 121353611 Change-Id: I5e67d8113bace7872133f2e02d9ae7b9d90e61ff --- media/libeffects/lvm/tests/Android.bp | 13 +++ .../lvm/tests/build_and_run_all_unit_tests.sh | 9 +- media/libeffects/lvm/tests/snr.cpp | 103 ++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 media/libeffects/lvm/tests/snr.cpp diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp index 8ee807c317..003ce9ed3d 100644 --- a/media/libeffects/lvm/tests/Android.bp +++ b/media/libeffects/lvm/tests/Android.bp @@ -44,3 +44,16 @@ cc_test { "-Wextra", ], } + +cc_test { + name: "snr", + host_supported: false, + + srcs: ["snr.cpp"], + + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], +} diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 861ee64070..41a4f04448 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -25,16 +25,17 @@ echo "testing lvm" adb shell mkdir -p $testdir adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir +adb push $OUT/testcases/snr/arm64/snr $testdir flags_arr=( "-csE" "-eqE" "-tE" "-csE -tE -eqE" - "-bE" + "-bE -M" "-csE -tE" "-csE -eqE" "-tE -eqE" - "-csE -tE -bE -eqE" + "-csE -tE -bE -M -eqE" ) fs_arr=( @@ -79,6 +80,10 @@ do then adb shell cmp $testdir/sinesweep_2_$((fs)).raw \ $testdir/sinesweep_$((ch))_$((fs)).raw + elif [[ $flags == *"-bE"* ]] && [ "$ch" -gt 2 ] + then + adb shell $testdir/snr $testdir/sinesweep_2_$((fs)).raw \ + $testdir/sinesweep_$((ch))_$((fs)).raw -thr:90.308998 fi done diff --git a/media/libeffects/lvm/tests/snr.cpp b/media/libeffects/lvm/tests/snr.cpp new file mode 100644 index 0000000000..88110c0f84 --- /dev/null +++ b/media/libeffects/lvm/tests/snr.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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 +#include +#include +#include +#include +#include +#include + +template +std::pair getSignalNoise(FILE *finp, FILE *fref) { + constexpr size_t framesize = 256; + std::vector in(framesize); + std::vector ref(framesize); + A signal{}; + A noise{}; + + for (;;) { + size_t read_samples_in = fread(&in[0], sizeof(T), framesize, finp); + const size_t read_samples_ref = fread(&ref[0], sizeof(T), framesize, fref); + if (read_samples_in != read_samples_ref) { + printf("file sizes do not match (last %zu %zu)", read_samples_in, read_samples_ref); + read_samples_in = std::min(read_samples_in, read_samples_ref); + } + if (read_samples_in == 0) { + return { signal, noise }; + } + for (size_t i = 0; i < read_samples_in; ++i) { + const A value(ref[i]); + const A diff(A(in[i]) - value); + signal += value * value; + noise += diff * diff; + } + } +} + +void printUsage() { + printf("\nUsage: "); + printf("\n snr [options]\n"); + printf("\nwhere, \n is the reference file name"); + printf("\n on which will be taken as pure signal"); + printf("\n is test file for snr calculation"); + printf("\n and options are mentioned below"); + printf("\n"); + printf("\n -pcm_format:"); + printf("\n 0 - 16 bit pcm"); + printf("\n 1 - 32 bit float"); + printf("\n default 0"); + printf("\n -thr:"); + printf("\n default - negative infinity\n\n"); +} + +int main(int argc, const char *argv[]) { + if (argc < 3) { + printUsage(); + return -1; + } + int pcm_format = 0; + float thr = - std::numeric_limits::infinity(); + FILE *fref = fopen(argv[1], "rb"); + FILE *finp = fopen(argv[2], "rb"); + for (int i = 3; i < argc; i++) { + if (!strncmp(argv[i], "-pcm_format:", 12)) { + pcm_format = atoi(argv[i] + 12); + } else if (!strncmp(argv[i], "-thr:", 5)) { + thr = atof(argv[i] + 5); + } + } + if (finp == nullptr || fref == nullptr) { + printf("\nError: missing input/reference files\n"); + return -1; + } + auto sn = pcm_format == 0 + ? getSignalNoise(finp, fref) + : getSignalNoise(finp, fref); + if (sn.first > 0.f && sn.second > 0.f) { + float snr = 10.f * log(sn.first / sn.second); + // compare the measured snr value with threshold + if (snr < thr) { + printf("%.6f less than threshold %.6f\n", snr, thr); + } else { + printf("%.6f\n", snr); + } + } + fclose(finp); + fclose(fref); + + return 0; +} -- GitLab From db085f3fb86fefebdcbf47809ab36a0288984eb8 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Sat, 29 Dec 2018 15:14:27 +0530 Subject: [PATCH 0783/1530] libeffects: Corrected scratch memory size calculations Corrected the scratch memory size calculations of bundle scratch for floating point and multichannel builds. Added comments detailing the usage and size calculation of memories of various memories LVM. Test: local native test (lvmtest) Bug: 120874654 Change-Id: I69bb7faa106be5b82b4e579718afe0d1d3bb798d --- .../libeffects/lvm/lib/Bundle/src/LVM_Init.c | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c index 0669a817a9..c57498e5a3 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.c @@ -61,6 +61,72 @@ /* */ /****************************************************************************************/ +/* + * 4 Types of Memory Regions of LVM + * TODO: Allocate on the fly. + * i) LVM_MEMREGION_PERSISTENT_SLOW_DATA - For Instance Handles + * ii) LVM_MEMREGION_PERSISTENT_FAST_DATA - Persistent Buffers + * iii) LVM_MEMREGION_PERSISTENT_FAST_COEF - For Holding Structure values + * iv) LVM_MEMREGION_TEMPORARY_FAST - For Holding Structure values + * + * LVM_MEMREGION_PERSISTENT_SLOW_DATA: + * Total Memory size: + * sizeof(LVM_Instance_t) + \ + * sizeof(LVM_Buffer_t) + \ + * sizeof(LVPSA_InstancePr_t) + \ + * sizeof(LVM_Buffer_t) - needed if buffer mode is LVM_MANAGED_BUFFER + * + * LVM_MEMREGION_PERSISTENT_FAST_DATA: + * Total Memory size: + * sizeof(LVM_TE_Data_t) + \ + * 2 * pInstParams->EQNB_NumBands * sizeof(LVM_EQNB_BandDef_t) + \ + * sizeof(LVCS_Data_t) + \ + * sizeof(LVDBE_Data_FLOAT_t) + \ + * sizeof(Biquad_2I_Order2_FLOAT_Taps_t) + \ + * sizeof(Biquad_2I_Order2_FLOAT_Taps_t) + \ + * pInstParams->EQNB_NumBands * sizeof(Biquad_2I_Order2_FLOAT_Taps_t) + \ + * pInstParams->EQNB_NumBands * sizeof(LVEQNB_BandDef_t) + \ + * pInstParams->EQNB_NumBands * sizeof(LVEQNB_BiquadType_en) + \ + * 2 * LVM_HEADROOM_MAX_NBANDS * sizeof(LVM_HeadroomBandDef_t) + \ + * PSA_InitParams.nBands * sizeof(Biquad_1I_Order2_Taps_t) + \ + * PSA_InitParams.nBands * sizeof(QPD_Taps_t) + * + * LVM_MEMREGION_PERSISTENT_FAST_COEF: + * Total Memory size: + * sizeof(LVM_TE_Coefs_t) + \ + * sizeof(LVCS_Coefficient_t) + \ + * sizeof(LVDBE_Coef_FLOAT_t) + \ + * sizeof(Biquad_FLOAT_Instance_t) + \ + * sizeof(Biquad_FLOAT_Instance_t) + \ + * pInstParams->EQNB_NumBands * sizeof(Biquad_FLOAT_Instance_t) + \ + * PSA_InitParams.nBands * sizeof(Biquad_Instance_t) + \ + * PSA_InitParams.nBands * sizeof(QPD_State_t) + * + * LVM_MEMREGION_TEMPORARY_FAST (Scratch): + * Total Memory Size: + * BundleScratchSize + \ + * MAX_INTERNAL_BLOCKSIZE * sizeof(LVM_FLOAT) + \ + * MaxScratchOf (CS, EQNB, DBE, PSA) + * + * a)BundleScratchSize: + * 3 * LVM_MAX_CHANNELS \ + * * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) * sizeof(LVM_FLOAT) + * This Memory is allocated only when Buffer mode is LVM_MANAGED_BUFFER. + * b)MaxScratchOf (CS, EQNB, DBE, PSA) + * This Memory is needed for scratch usage for CS, EQNB, DBE, PSA. + * CS = (LVCS_SCRATCHBUFFERS * sizeof(LVM_FLOAT) + * * pCapabilities->MaxBlockSize) + * EQNB = (LVEQNB_SCRATCHBUFFERS * sizeof(LVM_FLOAT) + * * pCapabilities->MaxBlockSize) + * DBE = (LVDBE_SCRATCHBUFFERS_INPLACE*sizeof(LVM_FLOAT) + * * pCapabilities->MaxBlockSize) + * PSA = (2 * pInitParams->MaxInputBlockSize * sizeof(LVM_FLOAT)) + * one MaxInputBlockSize for input and another for filter output + * c)MAX_INTERNAL_BLOCKSIZE + * This Memory is needed for PSAInput - Temp memory to store output + * from McToMono block and given as input to PSA block + */ + LVM_ReturnStatus_en LVM_GetMemoryTable(LVM_Handle_t hInstance, LVM_MemTab_t *pMemoryTable, LVM_InstParams_t *pInstParams) @@ -168,7 +234,13 @@ LVM_ReturnStatus_en LVM_GetMemoryTable(LVM_Handle_t hInstance, AlgScratchSize = 0; if (pInstParams->BufferMode == LVM_MANAGED_BUFFERS) { +#ifdef BUILD_FLOAT + BundleScratchSize = 3 * LVM_MAX_CHANNELS \ + * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) \ + * sizeof(LVM_FLOAT); +#else BundleScratchSize = 6 * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) * sizeof(LVM_INT16); +#endif InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_TEMPORARY_FAST], /* Scratch buffer */ BundleScratchSize); InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_SLOW_DATA], @@ -369,8 +441,13 @@ LVM_ReturnStatus_en LVM_GetMemoryTable(LVM_Handle_t hInstance, PSA_MemTab.Region[LVM_PERSISTENT_FAST_COEF].Size); /* Fast Temporary */ +#ifdef BUILD_FLOAT + InstAlloc_AddMember(&AllocMem[LVM_TEMPORARY_FAST], + MAX_INTERNAL_BLOCKSIZE * sizeof(LVM_FLOAT)); +#else InstAlloc_AddMember(&AllocMem[LVM_TEMPORARY_FAST], MAX_INTERNAL_BLOCKSIZE * sizeof(LVM_INT16)); +#endif if (PSA_MemTab.Region[LVM_TEMPORARY_FAST].Size > AlgScratchSize) { @@ -559,13 +636,20 @@ LVM_ReturnStatus_en LVM_GetInstanceHandle(LVM_Handle_t *phInstance, */ pInstance->pBufferManagement = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_PERSISTENT_SLOW_DATA], sizeof(LVM_Buffer_t)); +#ifdef BUILD_FLOAT + BundleScratchSize = (LVM_INT32) + (3 * LVM_MAX_CHANNELS \ + * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) \ + * sizeof(LVM_FLOAT)); +#else BundleScratchSize = (LVM_INT32)(6 * (MIN_INTERNAL_BLOCKSIZE + InternalBlockSize) * sizeof(LVM_INT16)); +#endif pInstance->pBufferManagement->pScratch = InstAlloc_AddMember(&AllocMem[LVM_MEMREGION_TEMPORARY_FAST], /* Scratch 1 buffer */ (LVM_UINT32)BundleScratchSize); #ifdef BUILD_FLOAT LoadConst_Float(0, /* Clear the input delay buffer */ (LVM_FLOAT *)&pInstance->pBufferManagement->InDelayBuffer, - (LVM_INT16)(2 * MIN_INTERNAL_BLOCKSIZE)); + (LVM_INT16)(LVM_MAX_CHANNELS * MIN_INTERNAL_BLOCKSIZE)); #else LoadConst_16(0, /* Clear the input delay buffer */ (LVM_INT16 *)&pInstance->pBufferManagement->InDelayBuffer, -- GitLab From 2c80be0e59e65e424862af426962939ad96e6b92 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 23 Jan 2019 18:06:37 -0800 Subject: [PATCH 0784/1530] audio policy: fix dynamic policy registration Rely on supported devices rather than active patches to match a render mix to an output. Bug: 123294474 Test: run the car emulator and check installed dynamic policies. Change-Id: If8a68678ba541f343894c563592d82447b37fe13 --- .../managerdefault/AudioPolicyManager.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cc151e7d8a..e74e692b48 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2632,19 +2632,23 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) } } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { String8 address = mix.mDeviceAddress; - audio_devices_t device = mix.mDeviceType; + audio_devices_t type = mix.mDeviceType; ALOGV(" registerPolicyMixes() mix %zu of %zu is RENDER, dev=0x%X addr=%s", - i, mixes.size(), device, address.string()); + i, mixes.size(), type, address.string()); + + sp device = mHwModules.getDeviceDescriptor( + mix.mDeviceType, mix.mDeviceAddress, + String8(), AUDIO_FORMAT_DEFAULT); + if (device == nullptr) { + res = INVALID_OPERATION; + break; + } bool foundOutput = false; for (size_t j = 0 ; j < mOutputs.size() ; j++) { sp desc = mOutputs.valueAt(j); - sp patch = mAudioPatches.valueFor(desc->getPatchHandle()); - if ((patch != 0) && (patch->mPatch.num_sinks != 0) - && (patch->mPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE) - && (patch->mPatch.sinks[0].ext.device.type == device) - && (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(), - AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) { + + if (desc->supportedDevices().contains(device)) { if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) { res = INVALID_OPERATION; } else { @@ -2656,12 +2660,12 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) if (res != NO_ERROR) { ALOGE(" Error registering mix %zu for device 0x%X addr %s", - i, device, address.string()); + i, type, address.string()); res = INVALID_OPERATION; break; } else if (!foundOutput) { ALOGE(" Output not found for mix %zu for device 0x%X addr %s", - i, device, address.string()); + i, type, address.string()); res = INVALID_OPERATION; break; } -- GitLab From fa94a3ba799c3aadb405ef12deee9531e05dbcee Mon Sep 17 00:00:00 2001 From: Yichi Chen Date: Sat, 8 Dec 2018 00:06:25 +0800 Subject: [PATCH 0785/1530] C2: Delete mHandle properly to avoid memory leakage The return in the destructor of C2AllocationGralloc make its mHandle cannot be deleted properly. Remove the return and add the check for each resource properly to ensure mHandle can be freed. Bug: 119925123 Test: Play Youtube without increase inside memory heap Change-Id: Ifd4f1e405a30b9993082237d6a62b8a60c0cda20 --- media/codec2/vndk/C2AllocatorGralloc.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index 4878974e6c..fdea2d3c3f 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -304,17 +304,18 @@ C2AllocationGralloc::C2AllocationGralloc( } C2AllocationGralloc::~C2AllocationGralloc() { - if (!mBuffer) { - return; - } - if (mLocked) { + if (mBuffer && mLocked) { // implementation ignores addresss and rect uint8_t* addr[C2PlanarLayout::MAX_NUM_PLANES] = {}; unmap(addr, C2Rect(), nullptr); } - mMapper->freeBuffer(const_cast(mBuffer)); - native_handle_delete(const_cast( - reinterpret_cast(mHandle))); + if (mBuffer) { + mMapper->freeBuffer(const_cast(mBuffer)); + } + if (mHandle) { + native_handle_delete( + const_cast(reinterpret_cast(mHandle))); + } } c2_status_t C2AllocationGralloc::map( -- GitLab From 2729dcf5981d1ee966f98c6b7c03cd4ae9674954 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Fri, 18 Jan 2019 13:15:07 -0800 Subject: [PATCH 0786/1530] Handle gralloc handle leaks Delete locked handle from C2AllocatorGralloc. Delete temporary cloned handle from C2OMXNode. Bug: 122978854 Change-Id: I6cff2775380e85e4ca83315df06fe59a42e6a4ca --- media/codec2/sfplugin/C2OMXNode.cpp | 6 +++++- media/codec2/vndk/C2AllocatorGralloc.cpp | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 749fd7a72c..9500aedb18 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -225,14 +225,18 @@ status_t C2OMXNode::emptyBuffer( if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer && omxBuf.mGraphicBuffer != nullptr) { std::shared_ptr alloc; + native_handle_t *clonedHandle = native_handle_clone(omxBuf.mGraphicBuffer->handle); handle = WrapNativeCodec2GrallocHandle( - native_handle_clone(omxBuf.mGraphicBuffer->handle), + clonedHandle, omxBuf.mGraphicBuffer->width, omxBuf.mGraphicBuffer->height, omxBuf.mGraphicBuffer->format, omxBuf.mGraphicBuffer->usage, omxBuf.mGraphicBuffer->stride); c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc); + if (clonedHandle) { + native_handle_delete(clonedHandle); + } if (err != OK) { return UNKNOWN_ERROR; } diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index fdea2d3c3f..18f2430980 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -316,6 +316,11 @@ C2AllocationGralloc::~C2AllocationGralloc() { native_handle_delete( const_cast(reinterpret_cast(mHandle))); } + if (mLockedHandle) { + native_handle_delete( + const_cast( + reinterpret_cast(mLockedHandle))); + } } c2_status_t C2AllocationGralloc::map( -- GitLab From fd8efe25a5022e313978fbd7c57a3bc1d0ea9915 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 23 Jan 2019 19:10:33 -0800 Subject: [PATCH 0787/1530] Delay start of mediaswcodec until after APEX is mounted The swcodecs are all loaded from apex. bug: 123166478 Change-Id: Ib7c4ac54aedb596fb7c6fff9016ae989955dd374 test: builds; boots and c2 swcodec runs normally --- services/mediacodec/mediaswcodec.rc | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediacodec/mediaswcodec.rc b/services/mediacodec/mediaswcodec.rc index dfe3381985..354966655c 100644 --- a/services/mediacodec/mediaswcodec.rc +++ b/services/mediacodec/mediaswcodec.rc @@ -2,5 +2,6 @@ service media.swcodec /system/bin/mediaswcodec class main user mediacodec group camera drmrpc mediadrm + updatable ioprio rt 4 writepid /dev/cpuset/foreground/tasks -- GitLab From 51087de7b6058d09fc2ed72ba935b93e544cc684 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 22 Jan 2019 15:39:07 -0800 Subject: [PATCH 0788/1530] Replace FOURC('a', 'b', 'c', 'd') with FOURCC("abcd") Test: compare disassembly before/after Change-Id: I8f9c75d19f2dcd0f0f31a082c0e98cd7eb6655a4 --- media/extractors/mp4/ItemTable.cpp | 82 ++--- media/extractors/mp4/MPEG4Extractor.cpp | 416 ++++++++++++------------ media/extractors/mp4/SampleTable.cpp | 8 +- 3 files changed, 253 insertions(+), 253 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index 55a0c47c68..7816473368 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -48,7 +48,7 @@ struct ImageItem { offset(0), size(0), nextTileIndex(0) {} bool isGrid() const { - return type == FOURCC('g', 'r', 'i', 'd'); + return type == FOURCC("grid"); } status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) { @@ -223,7 +223,7 @@ status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) { struct PitmBox : public FullBox { PitmBox(DataSourceHelper *source) : - FullBox(source, FOURCC('p', 'i', 't', 'm')) {} + FullBox(source, FOURCC("pitm")) {} status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId); }; @@ -303,7 +303,7 @@ struct ItemLoc { struct IlocBox : public FullBox { IlocBox(DataSourceHelper *source, KeyedVector *itemLocs) : - FullBox(source, FOURCC('i', 'l', 'o', 'c')), + FullBox(source, FOURCC("iloc")), mItemLocs(itemLocs), mHasConstructMethod1(false) {} status_t parse(off64_t offset, size_t size); @@ -497,7 +497,7 @@ void ItemReference::apply( ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId); switch(type()) { - case FOURCC('d', 'i', 'm', 'g'): { + case FOURCC("dimg"): { ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId); // ignore non-image items @@ -525,7 +525,7 @@ void ItemReference::apply( } break; } - case FOURCC('t', 'h', 'm', 'b'): { + case FOURCC("thmb"): { ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId); // ignore non-image items @@ -554,7 +554,7 @@ void ItemReference::apply( } break; } - case FOURCC('c', 'd', 's', 'c'): { + case FOURCC("cdsc"): { ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId); // ignore non-exif block items @@ -575,7 +575,7 @@ void ItemReference::apply( } break; } - case FOURCC('a', 'u', 'x', 'l'): { + case FOURCC("auxl"): { ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId); // ignore non-image items @@ -628,7 +628,7 @@ status_t ItemReference::parse(off64_t offset, size_t size) { struct IrefBox : public FullBox { IrefBox(DataSourceHelper *source, Vector > *itemRefs) : - FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {} + FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {} status_t parse(off64_t offset, size_t size); @@ -690,7 +690,7 @@ private: struct IspeBox : public FullBox, public ItemProperty { IspeBox(DataSourceHelper *source) : - FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {} + FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {} status_t parse(off64_t offset, size_t size) override; @@ -726,7 +726,7 @@ status_t IspeBox::parse(off64_t offset, size_t size) { struct HvccBox : public Box, public ItemProperty { HvccBox(DataSourceHelper *source) : - Box(source, FOURCC('h', 'v', 'c', 'C')) {} + Box(source, FOURCC("hvcC")) {} status_t parse(off64_t offset, size_t size) override; @@ -759,7 +759,7 @@ status_t HvccBox::parse(off64_t offset, size_t size) { struct IrotBox : public Box, public ItemProperty { IrotBox(DataSourceHelper *source) : - Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {} + Box(source, FOURCC("irot")), mAngle(0) {} status_t parse(off64_t offset, size_t size) override; @@ -788,7 +788,7 @@ status_t IrotBox::parse(off64_t offset, size_t size) { struct ColrBox : public Box, public ItemProperty { ColrBox(DataSourceHelper *source) : - Box(source, FOURCC('c', 'o', 'l', 'r')) {} + Box(source, FOURCC("colr")) {} status_t parse(off64_t offset, size_t size) override; @@ -812,11 +812,11 @@ status_t ColrBox::parse(off64_t offset, size_t size) { } offset += 4; size -= 4; - if (colour_type == FOURCC('n', 'c', 'l', 'x')) { + if (colour_type == FOURCC("nclx")) { return OK; } - if ((colour_type != FOURCC('r', 'I', 'C', 'C')) && - (colour_type != FOURCC('p', 'r', 'o', 'f'))) { + if ((colour_type != FOURCC("rICC")) && + (colour_type != FOURCC("prof"))) { return ERROR_MALFORMED; } @@ -836,7 +836,7 @@ status_t ColrBox::parse(off64_t offset, size_t size) { struct IpmaBox : public FullBox { IpmaBox(DataSourceHelper *source, Vector *associations) : - FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {} + FullBox(source, FOURCC("ipma")), mAssociations(associations) {} status_t parse(off64_t offset, size_t size); private: @@ -910,7 +910,7 @@ status_t IpmaBox::parse(off64_t offset, size_t size) { struct IpcoBox : public Box { IpcoBox(DataSourceHelper *source, Vector > *properties) : - Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {} + Box(source, FOURCC("ipco")), mItemProperties(properties) {} status_t parse(off64_t offset, size_t size); protected: @@ -930,22 +930,22 @@ status_t IpcoBox::parse(off64_t offset, size_t size) { status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) { sp itemProperty; switch(type) { - case FOURCC('h', 'v', 'c', 'C'): + case FOURCC("hvcC"): { itemProperty = new HvccBox(source()); break; } - case FOURCC('i', 's', 'p', 'e'): + case FOURCC("ispe"): { itemProperty = new IspeBox(source()); break; } - case FOURCC('i', 'r', 'o', 't'): + case FOURCC("irot"): { itemProperty = new IrotBox(source()); break; } - case FOURCC('c', 'o', 'l', 'r'): + case FOURCC("colr"): { itemProperty = new ColrBox(source()); break; @@ -969,7 +969,7 @@ struct IprpBox : public Box { IprpBox(DataSourceHelper *source, Vector > *properties, Vector *associations) : - Box(source, FOURCC('i', 'p', 'r', 'p')), + Box(source, FOURCC("iprp")), mProperties(properties), mAssociations(associations) {} status_t parse(off64_t offset, size_t size); @@ -993,12 +993,12 @@ status_t IprpBox::parse(off64_t offset, size_t size) { status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) { switch(type) { - case FOURCC('i', 'p', 'c', 'o'): + case FOURCC("ipco"): { IpcoBox ipcoBox(source(), mProperties); return ipcoBox.parse(offset, size); } - case FOURCC('i', 'p', 'm', 'a'): + case FOURCC("ipma"): { IpmaBox ipmaBox(source(), mAssociations); return ipmaBox.parse(offset, size); @@ -1024,7 +1024,7 @@ struct ItemInfo { struct InfeBox : public FullBox { InfeBox(DataSourceHelper *source) : - FullBox(source, FOURCC('i', 'n', 'f', 'e')) {} + FullBox(source, FOURCC("infe")) {} status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo); @@ -1104,7 +1104,7 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { } ALOGV("item_name %s", item_name.c_str()); - if (item_type == FOURCC('m', 'i', 'm', 'e')) { + if (item_type == FOURCC("mime")) { String8 content_type; if (!parseNullTerminatedString(&offset, &size, &content_type)) { return ERROR_MALFORMED; @@ -1117,7 +1117,7 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { return ERROR_MALFORMED; } } - } else if (item_type == FOURCC('u', 'r', 'i', ' ')) { + } else if (item_type == FOURCC("uri ")) { String8 item_uri_type; if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) { return ERROR_MALFORMED; @@ -1129,7 +1129,7 @@ status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) { struct IinfBox : public FullBox { IinfBox(DataSourceHelper *source, Vector *itemInfos) : - FullBox(source, FOURCC('i', 'i', 'n', 'f')), + FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos), mHasGrids(false) {} status_t parse(off64_t offset, size_t size); @@ -1179,7 +1179,7 @@ status_t IinfBox::parse(off64_t offset, size_t size) { } status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) { - if (type != FOURCC('i', 'n', 'f', 'e')) { + if (type != FOURCC("infe")) { return OK; } @@ -1188,7 +1188,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); - mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd')); + mHasGrids |= (itemInfo.itemType == FOURCC("grid")); } // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported // version. Ignore this error as it's not fatal. @@ -1214,31 +1214,31 @@ ItemTable::~ItemTable() {} status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) { switch(type) { - case FOURCC('i', 'l', 'o', 'c'): + case FOURCC("iloc"): { return parseIlocBox(data_offset, chunk_data_size); } - case FOURCC('i', 'i', 'n', 'f'): + case FOURCC("iinf"): { return parseIinfBox(data_offset, chunk_data_size); } - case FOURCC('i', 'p', 'r', 'p'): + case FOURCC("iprp"): { return parseIprpBox(data_offset, chunk_data_size); } - case FOURCC('p', 'i', 't', 'm'): + case FOURCC("pitm"): { return parsePitmBox(data_offset, chunk_data_size); } - case FOURCC('i', 'd', 'a', 't'): + case FOURCC("idat"): { return parseIdatBox(data_offset, chunk_data_size); } - case FOURCC('i', 'r', 'e', 'f'): + case FOURCC("iref"): { return parseIrefBox(data_offset, chunk_data_size); } - case FOURCC('i', 'p', 'r', 'o'): + case FOURCC("ipro"): { ALOGW("ipro box not supported!"); break; @@ -1355,9 +1355,9 @@ status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { // 'grid': derived image from tiles // 'hvc1': coded image (or tile) // 'Exif': EXIF metadata - if (info.itemType != FOURCC('g', 'r', 'i', 'd') && - info.itemType != FOURCC('h', 'v', 'c', '1') && - info.itemType != FOURCC('E', 'x', 'i', 'f')) { + if (info.itemType != FOURCC("grid") && + info.itemType != FOURCC("hvc1") && + info.itemType != FOURCC("Exif")) { continue; } @@ -1380,7 +1380,7 @@ status_t ItemTable::buildImageItemsIfPossible(uint32_t type) { return ERROR_MALFORMED; } - if (info.itemType == FOURCC('E', 'x', 'i', 'f')) { + if (info.itemType == FOURCC("Exif")) { // Only add if the Exif data is non-empty. The first 4 bytes contain // the offset to TIFF header, which the Exif parser doesn't use. if (size > 4) { diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index cc1534a323..216aa78220 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -308,42 +308,42 @@ static const bool kUseHexDump = false; static const char *FourCC2MIME(uint32_t fourcc) { switch (fourcc) { - case FOURCC('m', 'p', '4', 'a'): + case FOURCC("mp4a"): return MEDIA_MIMETYPE_AUDIO_AAC; - case FOURCC('s', 'a', 'm', 'r'): + case FOURCC("samr"): return MEDIA_MIMETYPE_AUDIO_AMR_NB; - case FOURCC('s', 'a', 'w', 'b'): + case FOURCC("sawb"): return MEDIA_MIMETYPE_AUDIO_AMR_WB; - case FOURCC('e', 'c', '-', '3'): + case FOURCC("ec-3"): return MEDIA_MIMETYPE_AUDIO_EAC3; - case FOURCC('m', 'p', '4', 'v'): + case FOURCC("mp4v"): return MEDIA_MIMETYPE_VIDEO_MPEG4; - case FOURCC('s', '2', '6', '3'): - case FOURCC('h', '2', '6', '3'): - case FOURCC('H', '2', '6', '3'): + case FOURCC("s263"): + case FOURCC("h263"): + case FOURCC("H263"): return MEDIA_MIMETYPE_VIDEO_H263; - case FOURCC('a', 'v', 'c', '1'): + case FOURCC("avc1"): return MEDIA_MIMETYPE_VIDEO_AVC; - case FOURCC('h', 'v', 'c', '1'): - case FOURCC('h', 'e', 'v', '1'): + case FOURCC("hvc1"): + case FOURCC("hev1"): return MEDIA_MIMETYPE_VIDEO_HEVC; - case FOURCC('a', 'c', '-', '4'): + case FOURCC("ac-4"): return MEDIA_MIMETYPE_AUDIO_AC4; - case FOURCC('t', 'w', 'o', 's'): - case FOURCC('s', 'o', 'w', 't'): + case FOURCC("twos"): + case FOURCC("sowt"): return MEDIA_MIMETYPE_AUDIO_RAW; - case FOURCC('a', 'l', 'a', 'c'): + case FOURCC("alac"): return MEDIA_MIMETYPE_AUDIO_ALAC; - case FOURCC('a', 'v', '0', '1'): + case FOURCC("av01"): return MEDIA_MIMETYPE_VIDEO_AV1; default: ALOGW("Unknown fourcc: %c%c%c%c", @@ -749,21 +749,21 @@ private: static bool underMetaDataPath(const Vector &path) { return path.size() >= 5 - && path[0] == FOURCC('m', 'o', 'o', 'v') - && path[1] == FOURCC('u', 'd', 't', 'a') - && path[2] == FOURCC('m', 'e', 't', 'a') - && path[3] == FOURCC('i', 'l', 's', 't'); + && path[0] == FOURCC("moov") + && path[1] == FOURCC("udta") + && path[2] == FOURCC("meta") + && path[3] == FOURCC("ilst"); } static bool underQTMetaPath(const Vector &path, int32_t depth) { return path.size() >= 2 - && path[0] == FOURCC('m', 'o', 'o', 'v') - && path[1] == FOURCC('m', 'e', 't', 'a') + && path[0] == FOURCC("moov") + && path[1] == FOURCC("meta") && (depth == 2 || (depth == 3 - && (path[2] == FOURCC('h', 'd', 'l', 'r') - || path[2] == FOURCC('i', 'l', 's', 't') - || path[2] == FOURCC('k', 'e', 'y', 's')))); + && (path[2] == FOURCC("hdlr") + || path[2] == FOURCC("ilst") + || path[2] == FOURCC("keys")))); } // Given a time in seconds since Jan 1 1904, produce a human-readable string. @@ -867,7 +867,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { ALOGE("b/23540914"); return ERROR_MALFORMED; } - if (chunk_type != FOURCC('m', 'd', 'a', 't') && chunk_data_size > kMaxAtomSize) { + if (chunk_type != FOURCC("mdat") && chunk_data_size > kMaxAtomSize) { char errMsg[100]; sprintf(errMsg, "%s atom has size %" PRId64, chunk, chunk_data_size); ALOGE("%s (b/28615448)", errMsg); @@ -875,8 +875,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; } - if (chunk_type != FOURCC('c', 'p', 'r', 't') - && chunk_type != FOURCC('c', 'o', 'v', 'r') + if (chunk_type != FOURCC("cprt") + && chunk_type != FOURCC("covr") && mPath.size() == 5 && underMetaDataPath(mPath)) { off64_t stop_offset = *offset + chunk_size; *offset = data_offset; @@ -895,40 +895,40 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } switch(chunk_type) { - case FOURCC('m', 'o', 'o', 'v'): - case FOURCC('t', 'r', 'a', 'k'): - case FOURCC('m', 'd', 'i', 'a'): - case FOURCC('m', 'i', 'n', 'f'): - case FOURCC('d', 'i', 'n', 'f'): - case FOURCC('s', 't', 'b', 'l'): - case FOURCC('m', 'v', 'e', 'x'): - case FOURCC('m', 'o', 'o', 'f'): - case FOURCC('t', 'r', 'a', 'f'): - case FOURCC('m', 'f', 'r', 'a'): - case FOURCC('u', 'd', 't', 'a'): - case FOURCC('i', 'l', 's', 't'): - case FOURCC('s', 'i', 'n', 'f'): - case FOURCC('s', 'c', 'h', 'i'): - case FOURCC('e', 'd', 't', 's'): - case FOURCC('w', 'a', 'v', 'e'): + case FOURCC("moov"): + case FOURCC("trak"): + case FOURCC("mdia"): + case FOURCC("minf"): + case FOURCC("dinf"): + case FOURCC("stbl"): + case FOURCC("mvex"): + case FOURCC("moof"): + case FOURCC("traf"): + case FOURCC("mfra"): + case FOURCC("udta"): + case FOURCC("ilst"): + case FOURCC("sinf"): + case FOURCC("schi"): + case FOURCC("edts"): + case FOURCC("wave"): { - if (chunk_type == FOURCC('m', 'o', 'o', 'v') && depth != 0) { + if (chunk_type == FOURCC("moov") && depth != 0) { ALOGE("moov: depth %d", depth); return ERROR_MALFORMED; } - if (chunk_type == FOURCC('m', 'o', 'o', 'v') && mInitCheck == OK) { + if (chunk_type == FOURCC("moov") && mInitCheck == OK) { ALOGE("duplicate moov"); return ERROR_MALFORMED; } - if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) { + if (chunk_type == FOURCC("moof") && !mMoofFound) { // store the offset of the first segment mMoofFound = true; mMoofOffset = *offset; } - if (chunk_type == FOURCC('s', 't', 'b', 'l')) { + if (chunk_type == FOURCC("stbl")) { ALOGV("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size); if (mDataSource->flags() @@ -954,7 +954,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } bool isTrack = false; - if (chunk_type == FOURCC('t', 'r', 'a', 'k')) { + if (chunk_type == FOURCC("trak")) { if (depth != 1) { ALOGE("trak: depth %d", depth); return ERROR_MALFORMED; @@ -1049,7 +1049,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return OK; } - } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) { + } else if (chunk_type == FOURCC("moov")) { mInitCheck = OK; return UNKNOWN_ERROR; // Return a dummy error. @@ -1057,7 +1057,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 'c', 'h', 'm'): + case FOURCC("schm"): { *offset += chunk_size; @@ -1072,23 +1072,23 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { scheme_type = ntohl(scheme_type); int32_t mode = kCryptoModeUnencrypted; switch(scheme_type) { - case FOURCC('c', 'b', 'c', '1'): + case FOURCC("cbc1"): { mode = kCryptoModeAesCbc; break; } - case FOURCC('c', 'b', 'c', 's'): + case FOURCC("cbcs"): { mode = kCryptoModeAesCbc; mLastTrack->subsample_encryption = true; break; } - case FOURCC('c', 'e', 'n', 'c'): + case FOURCC("cenc"): { mode = kCryptoModeAesCtr; break; } - case FOURCC('c', 'e', 'n', 's'): + case FOURCC("cens"): { mode = kCryptoModeAesCtr; mLastTrack->subsample_encryption = true; @@ -1102,7 +1102,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } - case FOURCC('e', 'l', 's', 't'): + case FOURCC("elst"): { *offset += chunk_size; @@ -1158,7 +1158,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('f', 'r', 'm', 'a'): + case FOURCC("frma"): { *offset += chunk_size; @@ -1187,7 +1187,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // If format type is 'alac', it is necessary to get the parameters // from a alac atom spreading behind the frma atom. // See 'external/alac/ALACMagicCookieDescription.txt'. - if (original_fourcc == FOURCC('a', 'l', 'a', 'c')) { + if (original_fourcc == FOURCC("alac")) { // Store ALAC magic cookie (decoder needs it). uint8_t alacInfo[12]; data_offset = *offset; @@ -1197,7 +1197,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } uint32_t size = U32_AT(&alacInfo[0]); if ((size != ALAC_SPECIFIC_INFO_SIZE) || - (U32_AT(&alacInfo[4]) != FOURCC('a', 'l', 'a', 'c')) || + (U32_AT(&alacInfo[4]) != FOURCC("alac")) || (U32_AT(&alacInfo[8]) != 0)) { return ERROR_MALFORMED; } @@ -1226,7 +1226,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'e', 'n', 'c'): + case FOURCC("tenc"): { *offset += chunk_size; @@ -1339,7 +1339,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'k', 'h', 'd'): + case FOURCC("tkhd"): { *offset += chunk_size; @@ -1351,7 +1351,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'r', 'e', 'f'): + case FOURCC("tref"): { off64_t stop_offset = *offset + chunk_size; *offset = data_offset; @@ -1367,7 +1367,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'h', 'm', 'b'): + case FOURCC("thmb"): { *offset += chunk_size; @@ -1384,7 +1384,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('p', 's', 's', 'h'): + case FOURCC("pssh"): { *offset += chunk_size; @@ -1420,7 +1420,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'd', 'h', 'd'): + case FOURCC("mdhd"): { *offset += chunk_size; @@ -1516,7 +1516,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 's', 'd'): + case FOURCC("stsd"): { uint8_t buffer[8]; if (chunk_data_size < (off64_t)sizeof(buffer)) { @@ -1568,7 +1568,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } break; } - case FOURCC('m', 'e', 't', 't'): + case FOURCC("mett"): { *offset += chunk_size; @@ -1622,16 +1622,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'p', '4', 'a'): - case FOURCC('e', 'n', 'c', 'a'): - case FOURCC('s', 'a', 'm', 'r'): - case FOURCC('s', 'a', 'w', 'b'): - case FOURCC('t', 'w', 'o', 's'): - case FOURCC('s', 'o', 'w', 't'): - case FOURCC('a', 'l', 'a', 'c'): + case FOURCC("mp4a"): + case FOURCC("enca"): + case FOURCC("samr"): + case FOURCC("sawb"): + case FOURCC("twos"): + case FOURCC("sowt"): + case FOURCC("alac"): { - if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a') - && depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) { + if (mIsQT && chunk_type == FOURCC("mp4a") + && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) { // Ignore mp4a embedded in QT wave atom *offset += chunk_size; break; @@ -1661,7 +1661,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { off64_t stop_offset = *offset + chunk_size; *offset = data_offset + sizeof(buffer); - if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')) { + if (mIsQT && chunk_type == FOURCC("mp4a")) { if (version == 1) { if (mDataSource->readAt(*offset, buffer, 16) < 16) { return ERROR_IO; @@ -1694,7 +1694,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } - if (chunk_type != FOURCC('e', 'n', 'c', 'a')) { + if (chunk_type != FOURCC("enca")) { // if the chunk type is enca, we'll get the type from the frma box later AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type)); @@ -1703,7 +1703,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, FourCC2MIME(chunk_type))) { AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, sample_size); - if (chunk_type == FOURCC('t', 'w', 'o', 's')) { + if (chunk_type == FOURCC("twos")) { AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, 1); } @@ -1714,7 +1714,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels); AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate); - if (chunk_type == FOURCC('a', 'l', 'a', 'c')) { + if (chunk_type == FOURCC("alac")) { // See 'external/alac/ALACMagicCookieDescription.txt for the detail'. // Store ALAC magic cookie (decoder needs it). @@ -1726,7 +1726,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } uint32_t size = U32_AT(&alacInfo[0]); if ((size != ALAC_SPECIFIC_INFO_SIZE) || - (U32_AT(&alacInfo[4]) != FOURCC('a', 'l', 'a', 'c')) || + (U32_AT(&alacInfo[4]) != FOURCC("alac")) || (U32_AT(&alacInfo[8]) != 0)) { return ERROR_MALFORMED; } @@ -1764,15 +1764,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'p', '4', 'v'): - case FOURCC('e', 'n', 'c', 'v'): - case FOURCC('s', '2', '6', '3'): - case FOURCC('H', '2', '6', '3'): - case FOURCC('h', '2', '6', '3'): - case FOURCC('a', 'v', 'c', '1'): - case FOURCC('h', 'v', 'c', '1'): - case FOURCC('h', 'e', 'v', '1'): - case FOURCC('a', 'v', '0', '1'): + case FOURCC("mp4v"): + case FOURCC("encv"): + case FOURCC("s263"): + case FOURCC("H263"): + case FOURCC("h263"): + case FOURCC("avc1"): + case FOURCC("hvc1"): + case FOURCC("hev1"): + case FOURCC("av01"): { uint8_t buffer[78]; if (chunk_data_size < (ssize_t)sizeof(buffer)) { @@ -1802,7 +1802,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; - if (chunk_type != FOURCC('e', 'n', 'c', 'v')) { + if (chunk_type != FOURCC("encv")) { // if the chunk type is encv, we'll get the type from the frma box later AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type)); @@ -1825,8 +1825,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 'c', 'o'): - case FOURCC('c', 'o', '6', '4'): + case FOURCC("stco"): + case FOURCC("co64"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) { return ERROR_MALFORMED; @@ -1845,7 +1845,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 's', 'c'): + case FOURCC("stsc"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) return ERROR_MALFORMED; @@ -1863,8 +1863,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 's', 'z'): - case FOURCC('s', 't', 'z', '2'): + case FOURCC("stsz"): + case FOURCC("stz2"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) { return ERROR_MALFORMED; @@ -1983,7 +1983,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 't', 's'): + case FOURCC("stts"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) return ERROR_MALFORMED; @@ -2001,7 +2001,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('c', 't', 't', 's'): + case FOURCC("ctts"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) return ERROR_MALFORMED; @@ -2019,7 +2019,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 't', 's', 's'): + case FOURCC("stss"): { if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) return ERROR_MALFORMED; @@ -2038,7 +2038,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } // \xA9xyz - case FOURCC(0xA9, 'x', 'y', 'z'): + case FOURCC("\251xyz"): { *offset += chunk_size; @@ -2088,7 +2088,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('e', 's', 'd', 's'): + case FOURCC("esds"): { *offset += chunk_size; @@ -2118,7 +2118,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4); if (mPath.size() >= 2 - && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) { + && mPath[mPath.size() - 2] == FOURCC("mp4a")) { // Information from the ESDS must be relied on for proper // setup of sample rate and channel count for MPEG4 Audio. // The generic header appears to only contain generic @@ -2132,7 +2132,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } if (mPath.size() >= 2 - && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'v')) { + && mPath[mPath.size() - 2] == FOURCC("mp4v")) { // Check if the video is MPEG2 ESDS esds(&buffer[4], chunk_data_size - 4); @@ -2147,7 +2147,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('b', 't', 'r', 't'): + case FOURCC("btrt"): { *offset += chunk_size; if (mLastTrack == NULL) { @@ -2177,7 +2177,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('a', 'v', 'c', 'C'): + case FOURCC("avcC"): { *offset += chunk_size; @@ -2201,7 +2201,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('h', 'v', 'c', 'C'): + case FOURCC("hvcC"): { auto buffer = heapbuffer(chunk_data_size); @@ -2225,7 +2225,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('d', '2', '6', '3'): + case FOURCC("d263"): { *offset += chunk_size; /* @@ -2260,7 +2260,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'e', 't', 'a'): + case FOURCC("meta"): { off64_t stop_offset = *offset + chunk_size; *offset = data_offset; @@ -2304,13 +2304,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('i', 'l', 'o', 'c'): - case FOURCC('i', 'i', 'n', 'f'): - case FOURCC('i', 'p', 'r', 'p'): - case FOURCC('p', 'i', 't', 'm'): - case FOURCC('i', 'd', 'a', 't'): - case FOURCC('i', 'r', 'e', 'f'): - case FOURCC('i', 'p', 'r', 'o'): + case FOURCC("iloc"): + case FOURCC("iinf"): + case FOURCC("iprp"): + case FOURCC("pitm"): + case FOURCC("idat"): + case FOURCC("iref"): + case FOURCC("ipro"): { if (mIsHeif) { if (mItemTable == NULL) { @@ -2326,9 +2326,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'e', 'a', 'n'): - case FOURCC('n', 'a', 'm', 'e'): - case FOURCC('d', 'a', 't', 'a'): + case FOURCC("mean"): + case FOURCC("name"): + case FOURCC("data"): { *offset += chunk_size; @@ -2343,7 +2343,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'v', 'h', 'd'): + case FOURCC("mvhd"): { *offset += chunk_size; @@ -2395,7 +2395,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'e', 'h', 'd'): + case FOURCC("mehd"): { *offset += chunk_size; @@ -2440,7 +2440,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('m', 'd', 'a', 't'): + case FOURCC("mdat"): { mMdatFound = true; @@ -2448,7 +2448,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('h', 'd', 'l', 'r'): + case FOURCC("hdlr"): { *offset += chunk_size; @@ -2466,7 +2466,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // For the 3GPP file format, the handler-type within the 'hdlr' box // shall be 'text'. We also want to support 'sbtl' handler type // for a practical reason as various MPEG4 containers use it. - if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) { + if (type == FOURCC("text") || type == FOURCC("sbtl")) { if (mLastTrack != NULL) { AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_TEXT_3GPP); @@ -2476,7 +2476,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('k', 'e', 'y', 's'): + case FOURCC("keys"): { *offset += chunk_size; @@ -2489,7 +2489,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'r', 'e', 'x'): + case FOURCC("trex"): { *offset += chunk_size; @@ -2508,7 +2508,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'x', '3', 'g'): + case FOURCC("tx3g"): { if (mLastTrack == NULL) return ERROR_MALFORMED; @@ -2552,7 +2552,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('c', 'o', 'v', 'r'): + case FOURCC("covr"): { *offset += chunk_size; @@ -2583,12 +2583,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('c', 'o', 'l', 'r'): + case FOURCC("colr"): { *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('s', 't', 's', 'd')) { + if (depth >= 2 && mPath[depth - 2] == FOURCC("stsd")) { status_t err = parseColorInfo(data_offset, chunk_data_size); if (err != OK) { return err; @@ -2598,12 +2598,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('t', 'i', 't', 'l'): - case FOURCC('p', 'e', 'r', 'f'): - case FOURCC('a', 'u', 't', 'h'): - case FOURCC('g', 'n', 'r', 'e'): - case FOURCC('a', 'l', 'b', 'm'): - case FOURCC('y', 'r', 'r', 'c'): + case FOURCC("titl"): + case FOURCC("perf"): + case FOURCC("auth"): + case FOURCC("gnre"): + case FOURCC("albm"): + case FOURCC("yrrc"): { *offset += chunk_size; @@ -2616,7 +2616,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('I', 'D', '3', '2'): + case FOURCC("ID32"): { *offset += chunk_size; @@ -2629,7 +2629,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('-', '-', '-', '-'): + case FOURCC("----"): { mLastCommentMean.clear(); mLastCommentName.clear(); @@ -2638,7 +2638,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } - case FOURCC('s', 'i', 'd', 'x'): + case FOURCC("sidx"): { status_t err = parseSegmentIndex(data_offset, chunk_data_size); if (err != OK) { @@ -2648,25 +2648,25 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return UNKNOWN_ERROR; // stop parsing after sidx } - case FOURCC('a', 'c', '-', '3'): + case FOURCC("ac-3"): { *offset += chunk_size; return parseAC3SpecificBox(data_offset); } - case FOURCC('e', 'c', '-', '3'): + case FOURCC("ec-3"): { *offset += chunk_size; return parseEAC3SpecificBox(data_offset); } - case FOURCC('a', 'c', '-', '4'): + case FOURCC("ac-4"): { *offset += chunk_size; return parseAC4SpecificBox(data_offset); } - case FOURCC('f', 't', 'y', 'p'): + case FOURCC("ftyp"): { if (chunk_data_size < 8 || depth != 0) { return ERROR_MALFORMED; @@ -2691,16 +2691,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { brandSet.insert(brand); } - if (brandSet.count(FOURCC('q', 't', ' ', ' ')) > 0) { + if (brandSet.count(FOURCC("qt ")) > 0) { mIsQT = true; } else { - if (brandSet.count(FOURCC('m', 'i', 'f', '1')) > 0 - && brandSet.count(FOURCC('h', 'e', 'i', 'c')) > 0) { + if (brandSet.count(FOURCC("mif1")) > 0 + && brandSet.count(FOURCC("heic")) > 0) { ALOGV("identified HEIF image"); mIsHeif = true; - brandSet.erase(FOURCC('m', 'i', 'f', '1')); - brandSet.erase(FOURCC('h', 'e', 'i', 'c')); + brandSet.erase(FOURCC("mif1")); + brandSet.erase(FOURCC("heic")); } if (!brandSet.empty()) { @@ -2787,7 +2787,7 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) { // + 4-byte size offset += 4; uint32_t type; - if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '4')) { + if (!mDataSource->getUInt32(offset, &type) || type != FOURCC("dac4")) { ALOGE("MPEG4Extractor: error while reading ac-4 specific block: header not dac4"); return ERROR_MALFORMED; } @@ -2914,7 +2914,7 @@ status_t MPEG4Extractor::parseEAC3SpecificBox(off64_t offset) { offset += 4; uint32_t type; - if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'e', 'c', '3')) { + if (!mDataSource->getUInt32(offset, &type) || type != FOURCC("dec3")) { ALOGE("MPEG4Extractor: error while reading eac-3 specific block: header not dec3"); return ERROR_MALFORMED; } @@ -3071,7 +3071,7 @@ status_t MPEG4Extractor::parseAC3SpecificBox(off64_t offset) { offset += 4; uint32_t type; - if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '3')) { + if (!mDataSource->getUInt32(offset, &type) || type != FOURCC("dac3")) { ALOGE("MPEG4Extractor: error while reading ac-3 specific block: header not dac3"); return ERROR_MALFORMED; } @@ -3273,7 +3273,7 @@ status_t MPEG4Extractor::parseQTMetaKey(off64_t offset, size_t size) { uint32_t type; if (!mDataSource->getUInt32(keyOffset + 4, &type) - || type != FOURCC('m', 'd', 't', 'a')) { + || type != FOURCC("mdta")) { return ERROR_MALFORMED; } @@ -3315,7 +3315,7 @@ status_t MPEG4Extractor::parseQTMetaVal( } uint32_t atomFourCC; if (!mDataSource->getUInt32(offset + 4, &atomFourCC) - || atomFourCC != FOURCC('d', 'a', 't', 'a')) { + || atomFourCC != FOURCC("data")) { return ERROR_MALFORMED; } uint32_t dataType; @@ -3476,48 +3476,48 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { MakeFourCCString(mPath[4], chunk); ALOGV("meta: %s @ %lld", chunk, (long long)offset); switch ((int32_t)mPath[4]) { - case FOURCC(0xa9, 'a', 'l', 'b'): + case FOURCC("\251alb"): { metadataKey = "album"; break; } - case FOURCC(0xa9, 'A', 'R', 'T'): + case FOURCC("\251ART"): { metadataKey = "artist"; break; } - case FOURCC('a', 'A', 'R', 'T'): + case FOURCC("aART"): { metadataKey = "albumartist"; break; } - case FOURCC(0xa9, 'd', 'a', 'y'): + case FOURCC("\251day"): { metadataKey = "year"; break; } - case FOURCC(0xa9, 'n', 'a', 'm'): + case FOURCC("\251nam"): { metadataKey = "title"; break; } - case FOURCC(0xa9, 'w', 'r', 't'): + case FOURCC("\251wrt"): { metadataKey = "writer"; break; } - case FOURCC('c', 'o', 'v', 'r'): + case FOURCC("covr"): { metadataKey = "albumart"; break; } - case FOURCC('g', 'n', 'r', 'e'): - case FOURCC(0xa9, 'g', 'e', 'n'): + case FOURCC("gnre"): + case FOURCC("\251gen"): { metadataKey = "genre"; break; } - case FOURCC('c', 'p', 'i', 'l'): + case FOURCC("cpil"): { if (size == 9 && flags == 21) { char tmp[16]; @@ -3528,7 +3528,7 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { } break; } - case FOURCC('t', 'r', 'k', 'n'): + case FOURCC("trkn"): { if (size == 16 && flags == 0) { char tmp[16]; @@ -3540,7 +3540,7 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { } break; } - case FOURCC('d', 'i', 's', 'k'): + case FOURCC("disk"): { if ((size == 14 || size == 16) && flags == 0) { char tmp[16]; @@ -3552,17 +3552,17 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { } break; } - case FOURCC('-', '-', '-', '-'): + case FOURCC("----"): { buffer[size] = '\0'; switch (mPath[5]) { - case FOURCC('m', 'e', 'a', 'n'): + case FOURCC("mean"): mLastCommentMean.setTo((const char *)buffer + 4); break; - case FOURCC('n', 'a', 'm', 'e'): + case FOURCC("name"): mLastCommentName.setTo((const char *)buffer + 4); break; - case FOURCC('d', 'a', 't', 'a'): + case FOURCC("data"): if (size < 8) { delete[] buffer; buffer = NULL; @@ -3670,8 +3670,8 @@ status_t MPEG4Extractor::parseColorInfo(off64_t offset, size_t size) { } int32_t type = U32_AT(&buffer[0]); - if ((type == FOURCC('n', 'c', 'l', 'x') && size >= 11) - || (type == FOURCC('n', 'c', 'l', 'c') && size >= 10)) { + if ((type == FOURCC("nclx") && size >= 11) + || (type == FOURCC("nclc") && size >= 10)) { // only store the first color specification int32_t existingColor; if (!AMediaFormat_getInt32(mLastTrack->meta, @@ -3679,7 +3679,7 @@ status_t MPEG4Extractor::parseColorInfo(off64_t offset, size_t size) { int32_t primaries = U16_AT(&buffer[4]); int32_t isotransfer = U16_AT(&buffer[6]); int32_t coeffs = U16_AT(&buffer[8]); - bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128); + bool fullRange = (type == FOURCC("nclx")) && (buffer[10] & 128); int32_t range = 0; int32_t standard = 0; @@ -3725,27 +3725,27 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept const char *metadataKey = nullptr; switch (mPath[depth]) { - case FOURCC('t', 'i', 't', 'l'): + case FOURCC("titl"): { metadataKey = "title"; break; } - case FOURCC('p', 'e', 'r', 'f'): + case FOURCC("perf"): { metadataKey = "artist"; break; } - case FOURCC('a', 'u', 't', 'h'): + case FOURCC("auth"): { metadataKey = "writer"; break; } - case FOURCC('g', 'n', 'r', 'e'): + case FOURCC("gnre"): { metadataKey = "genre"; break; } - case FOURCC('a', 'l', 'b', 'm'): + case FOURCC("albm"): { if (buffer[size - 1] != '\0') { char tmp[4]; @@ -3757,7 +3757,7 @@ status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int dept metadataKey = "album"; break; } - case FOURCC('y', 'r', 'r', 'c'): + case FOURCC("yrrc"): { if (size < 6) { delete[] buffer; @@ -4610,8 +4610,8 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { switch(chunk_type) { - case FOURCC('t', 'r', 'a', 'f'): - case FOURCC('m', 'o', 'o', 'f'): { + case FOURCC("traf"): + case FOURCC("moof"): { off64_t stop_offset = *offset + chunk_size; *offset = data_offset; while (*offset < stop_offset) { @@ -4620,7 +4620,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { return err; } } - if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { + if (chunk_type == FOURCC("moof")) { // *offset points to the box following this moof. Find the next moof from there. while (true) { @@ -4649,7 +4649,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { return ERROR_MALFORMED; } - if (chunk_type == FOURCC('m', 'o', 'o', 'f')) { + if (chunk_type == FOURCC("moof")) { mNextMoofOffset = *offset; break; } else if (chunk_size == 0) { @@ -4661,7 +4661,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } - case FOURCC('t', 'f', 'h', 'd'): { + case FOURCC("tfhd"): { status_t err; if ((err = parseTrackFragmentHeader(data_offset, chunk_data_size)) != OK) { return err; @@ -4670,7 +4670,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } - case FOURCC('t', 'r', 'u', 'n'): { + case FOURCC("trun"): { status_t err; if (mLastParsedTrackId == mTrackId) { if ((err = parseTrackFragmentRun(data_offset, chunk_data_size)) != OK) { @@ -4682,7 +4682,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } - case FOURCC('s', 'a', 'i', 'z'): { + case FOURCC("saiz"): { status_t err; if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) { return err; @@ -4690,7 +4690,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { *offset += chunk_size; break; } - case FOURCC('s', 'a', 'i', 'o'): { + case FOURCC("saio"): { status_t err; if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) { @@ -4700,7 +4700,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } - case FOURCC('s', 'e', 'n', 'c'): { + case FOURCC("senc"): { status_t err; if ((err = parseSampleEncryption(data_offset)) != OK) { return err; @@ -4709,7 +4709,7 @@ status_t MPEG4Source::parseChunk(off64_t *offset) { break; } - case FOURCC('m', 'd', 'a', 't'): { + case FOURCC("mdat"): { // parse DRM info if present ALOGV("MPEG4Source::parseChunk mdat"); // if saiz/saoi was previously observed, do something with the sampleinfos @@ -6031,28 +6031,28 @@ static bool LegacySniffMPEG4(DataSourceHelper *source, float *confidence) { static bool isCompatibleBrand(uint32_t fourcc) { static const uint32_t kCompatibleBrands[] = { - FOURCC('i', 's', 'o', 'm'), - FOURCC('i', 's', 'o', '2'), - FOURCC('a', 'v', 'c', '1'), - FOURCC('h', 'v', 'c', '1'), - FOURCC('h', 'e', 'v', '1'), - FOURCC('a', 'v', '0', '1'), - FOURCC('3', 'g', 'p', '4'), - FOURCC('m', 'p', '4', '1'), - FOURCC('m', 'p', '4', '2'), - FOURCC('d', 'a', 's', 'h'), + FOURCC("isom"), + FOURCC("iso2"), + FOURCC("avc1"), + FOURCC("hvc1"), + FOURCC("hev1"), + FOURCC("av01"), + FOURCC("3gp4"), + FOURCC("mp41"), + FOURCC("mp42"), + FOURCC("dash"), // Won't promise that the following file types can be played. // Just give these file types a chance. - FOURCC('q', 't', ' ', ' '), // Apple's QuickTime - FOURCC('M', 'S', 'N', 'V'), // Sony's PSP - - FOURCC('3', 'g', '2', 'a'), // 3GPP2 - FOURCC('3', 'g', '2', 'b'), - FOURCC('m', 'i', 'f', '1'), // HEIF image - FOURCC('h', 'e', 'i', 'c'), // HEIF image - FOURCC('m', 's', 'f', '1'), // HEIF image sequence - FOURCC('h', 'e', 'v', 'c'), // HEIF image sequence + FOURCC("qt "), // Apple's QuickTime + FOURCC("MSNV"), // Sony's PSP + + FOURCC("3g2a"), // 3GPP2 + FOURCC("3g2b"), + FOURCC("mif1"), // HEIF image + FOURCC("heic"), // HEIF image + FOURCC("msf1"), // HEIF image sequence + FOURCC("hevc"), // HEIF image sequence }; for (size_t i = 0; @@ -6120,7 +6120,7 @@ static bool BetterSniffMPEG4(DataSourceHelper *source, float *confidence) { ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)offset); switch (chunkType) { - case FOURCC('f', 't', 'y', 'p'): + case FOURCC("ftyp"): { if (chunkDataSize < 8) { return false; @@ -6155,7 +6155,7 @@ static bool BetterSniffMPEG4(DataSourceHelper *source, float *confidence) { break; } - case FOURCC('m', 'o', 'o', 'v'): + case FOURCC("moov"): { moovAtomEndOffset = offset + chunkSize; diff --git a/media/extractors/mp4/SampleTable.cpp b/media/extractors/mp4/SampleTable.cpp index d242798c73..7db984ad03 100644 --- a/media/extractors/mp4/SampleTable.cpp +++ b/media/extractors/mp4/SampleTable.cpp @@ -37,13 +37,13 @@ namespace android { // static -const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); +const uint32_t SampleTable::kChunkOffsetType32 = FOURCC("stco"); // static -const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); +const uint32_t SampleTable::kChunkOffsetType64 = FOURCC("co64"); // static -const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); +const uint32_t SampleTable::kSampleSizeType32 = FOURCC("stsz"); // static -const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); +const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC("stz2"); //////////////////////////////////////////////////////////////////////////////// -- GitLab From 2d4e170f3ee7ec77cbc267ca7cff78861382d112 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 24 Jan 2019 12:59:44 -0800 Subject: [PATCH 0789/1530] audiopolicy: Fix after commit 11d301045991dd66bf3f64ba510e069b7c09fedb The change ag/5874343 switched to DeviceVector usage instead of audio_devices_t. In AudioPolicyManager::setOutputDevices, one of the conditions comparing a device to AUDIO_DEVICE_NONE had been inverted. This was resulting in resetting of default patches during a call to 'setForceUse' function. Test: audiopolicy_tests Change-Id: Idc43bc152c3d749cd1dab9b696e7e1e700ef3926 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cc151e7d8a..e6dc9a7a64 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5319,7 +5319,7 @@ uint32_t AudioPolicyManager::setOutputDevices(const sp& // AND force is not specified // AND the output is connected by a valid audio patch. // Doing this check here allows the caller to call setOutputDevices() without conditions - if ((!filteredDevices.isEmpty() || filteredDevices == prevDevices) && + if ((filteredDevices.isEmpty() || filteredDevices == prevDevices) && !force && outputDesc->getPatchHandle() != 0) { ALOGV("%s setting same device %s or null device, force=%d, patch handle=%d", __func__, filteredDevices.toString().c_str(), force, outputDesc->getPatchHandle()); -- GitLab From 69f6f29fc30d43ce614b653e66941ffb4d334fea Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 24 Jan 2019 09:47:07 -0800 Subject: [PATCH 0790/1530] Fix issue with previous CL to use 64-bit sample times Bug: 122746532 Test: manual Change-Id: I52070ac126baa1fbea33cf7d3566a91306ed6514 --- media/extractors/mp4/SampleIterator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/extractors/mp4/SampleIterator.cpp b/media/extractors/mp4/SampleIterator.cpp index d6287ec40e..ec12130904 100644 --- a/media/extractors/mp4/SampleIterator.cpp +++ b/media/extractors/mp4/SampleIterator.cpp @@ -330,7 +330,7 @@ status_t SampleIterator::findSampleTimeAndDuration( // below is equivalent to: // *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); - uint32_t tmp; + uint64_t tmp; if (__builtin_sub_overflow(sampleIndex, mTTSSampleIndex, &tmp) || __builtin_mul_overflow(mTTSDuration, tmp, &tmp) || __builtin_add_overflow(mTTSSampleTime, tmp, &tmp)) { @@ -340,7 +340,7 @@ status_t SampleIterator::findSampleTimeAndDuration( int32_t offset = mTable->getCompositionTimeOffset(sampleIndex); if ((offset < 0 && *time < (offset == INT32_MIN ? - INT32_MAX : uint32_t(-offset))) || + INT64_MAX : uint64_t(-offset))) || (offset > 0 && *time > UINT64_MAX - offset)) { ALOGE("%llu + %d would overflow", (unsigned long long) *time, offset); return ERROR_OUT_OF_RANGE; @@ -348,7 +348,7 @@ status_t SampleIterator::findSampleTimeAndDuration( if (offset > 0) { *time += offset; } else { - *time -= (offset == INT32_MIN ? INT32_MAX : (-offset)); + *time -= (offset == INT64_MIN ? INT64_MAX : (-offset)); } *duration = mTTSDuration; -- GitLab From 8fc45862aa5fab7c7161a086a9f0c445fa91cf4d Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 24 Jan 2019 13:28:57 -0800 Subject: [PATCH 0791/1530] Fix MediaPlayer2Test on 32bit mode Test: MediaPlayer2Test with armeabi-v7a Bug: 123361167 Change-Id: Ida8b6ebc0616b35251d47c8c7246734d47b2c7ff --- apex/Android.bp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 39997d2ea6..c077a77e25 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -16,21 +16,31 @@ apex { name: "com.android.media", manifest: "manifest.json", java_libs: ["updatable-media"], - native_shared_libs: [ - // Extractor plugins - "libaacextractor", - "libamrextractor", - "libflacextractor", - "libmidiextractor", - "libmkvextractor", - "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", - "libwavextractor", - // MediaPlayer2 - "libmedia2_jni", - ], + compile_multilib: "both", + multilib: { + first: { + // Extractor process runs only with the primary ABI. + native_shared_libs: [ + // Extractor plugins + "libaacextractor", + "libamrextractor", + "libflacextractor", + "libmidiextractor", + "libmkvextractor", + "libmp3extractor", + "libmp4extractor", + "libmpeg2extractor", + "liboggextractor", + "libwavextractor", + ], + }, + both: { + native_shared_libs: [ + // MediaPlayer2 + "libmedia2_jni", + ], + }, + }, key: "com.android.media.key", } -- GitLab From ba8c48429d82d22737a84d2713fa1880a4654cbd Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 18 Jan 2019 11:35:33 -0800 Subject: [PATCH 0792/1530] Further work on libmediametrics stable API implement a missing method (for inflight metrics), enumerate exported symbols. Bug: 119675363 Test: build, CTS media/codec tests Change-Id: Id92f1b331babacc0de6c0005d6ba0c97c91c0291 --- media/libmediametrics/Android.bp | 21 +- .../IMediaAnalyticsService.cpp | 2 +- media/libmediametrics/MediaAnalyticsItem.cpp | 207 +++++++++++++++++- media/libmediametrics/MediaMetrics.cpp | 19 +- .../include/MediaAnalyticsItem.h | 15 +- media/libmediametrics/include/MediaMetrics.h | 10 +- media/libmediametrics/libmediametrics.map.txt | 29 +++ 7 files changed, 266 insertions(+), 37 deletions(-) create mode 100644 media/libmediametrics/libmediametrics.map.txt diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp index e188e54fb3..15ea578fdb 100644 --- a/media/libmediametrics/Android.bp +++ b/media/libmediametrics/Android.bp @@ -1,6 +1,4 @@ -// TODO: change it back to cc_library_shared when there is a way to -// expose media metrics as stable API. -cc_library { +cc_library_shared { name: "libmediametrics", srcs: [ @@ -32,12 +30,13 @@ cc_library { cfi: true, }, - // enumerate the stable interface -// this would mean nobody can use the C++ interface. have to rework some things. -// stubs: { -// symbol_file: "libmediametrics.map.txt", -// versions: [ -// "1" , -// ] -// }, + // enumerate stable entry points, for apex use + stubs: { + symbol_file: "libmediametrics.map.txt", + versions: [ + "1" , + ] + }, } + + diff --git a/media/libmediametrics/IMediaAnalyticsService.cpp b/media/libmediametrics/IMediaAnalyticsService.cpp index 28a77462b2..9114927dac 100644 --- a/media/libmediametrics/IMediaAnalyticsService.cpp +++ b/media/libmediametrics/IMediaAnalyticsService.cpp @@ -142,7 +142,7 @@ status_t BnMediaAnalyticsService::onTransact( CHECK_INTERFACE(IMediaAnalyticsService, data, reply); bool forcenew; - MediaAnalyticsItem *item = new MediaAnalyticsItem; + MediaAnalyticsItem *item = MediaAnalyticsItem::create(); data.readBool(&forcenew); item->readFromParcel(data); diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp index 448e2d9959..02c23b1231 100644 --- a/media/libmediametrics/MediaAnalyticsItem.cpp +++ b/media/libmediametrics/MediaAnalyticsItem.cpp @@ -52,6 +52,17 @@ const char * const MediaAnalyticsItem::EnabledProperty = "media.metrics.enabled const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.metrics.enabled"; const int MediaAnalyticsItem::EnabledProperty_default = 1; +// So caller doesn't need to know size of allocated space +MediaAnalyticsItem *MediaAnalyticsItem::create() +{ + return MediaAnalyticsItem::create(kKeyNone); +} + +MediaAnalyticsItem *MediaAnalyticsItem::create(MediaAnalyticsItem::Key key) +{ + MediaAnalyticsItem *item = new MediaAnalyticsItem(key); + return item; +} // access functions for the class MediaAnalyticsItem::MediaAnalyticsItem() @@ -642,6 +653,19 @@ bool MediaAnalyticsItem::growProps(int increment) // int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) { + int32_t version = data.readInt32(); + + switch(version) { + case 0: + return readFromParcel0(data); + break; + default: + ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version); + return -1; + } +} + +int32_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) { // into 'this' object // .. we make a copy of the string to put away. mKey = data.readCString(); @@ -691,8 +715,23 @@ int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) { } int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) { + if (data == NULL) return -1; + int32_t version = 0; + data->writeInt32(version); + + switch(version) { + case 0: + return writeToParcel0(data); + break; + default: + ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version); + return -1; + } +} + +int32_t MediaAnalyticsItem::writeToParcel0(Parcel *data) { data->writeCString(mKey.c_str()); data->writeInt32(mPid); @@ -737,7 +776,6 @@ int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) { return 0; } - const char *MediaAnalyticsItem::toCString() { return toCString(PROTO_LAST); } @@ -876,8 +914,6 @@ bool MediaAnalyticsItem::selfrecord(bool forcenew) { } return true; } else { - std::string p = this->toString(); - ALOGW("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew); return false; } } @@ -1035,5 +1071,170 @@ bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) { return true; } +// a byte array; contents are +// overall length (uint32) including the length field itself +// encoding version (uint32) +// count of properties (uint32) +// N copies of: +// property name as length(int16), bytes +// the bytes WILL include the null terminator of the name +// type (uint8 -- 1 byte) +// size of value field (int16 -- 2 bytes) +// value (size based on type) +// int32, int64, double -- little endian 4/8/8 bytes respectively +// cstring -- N bytes of value [WITH terminator] + +enum { kInt32 = 0, kInt64, kDouble, kRate, kCString}; + +bool MediaAnalyticsItem::dumpAttributes(char **pbuffer, size_t *plength) { + + char *build = NULL; + + if (pbuffer == NULL || plength == NULL) + return false; + + // consistency for the caller, who owns whatever comes back in this pointer. + *pbuffer = NULL; + + // first, let's calculate sizes + int32_t goal = 0; + int32_t version = 0; + + goal += sizeof(uint32_t); // overall length, including the length field + goal += sizeof(uint32_t); // encoding version + goal += sizeof(uint32_t); // # properties + + int32_t count = mPropCount; + for (int i = 0 ; i < count; i++ ) { + Prop *prop = &mProps[i]; + goal += sizeof(uint16_t); // name length + goal += strlen(prop->mName) + 1; // string + null + goal += sizeof(uint8_t); // type + goal += sizeof(uint16_t); // size of value + switch (prop->mType) { + case MediaAnalyticsItem::kTypeInt32: + goal += sizeof(uint32_t); + break; + case MediaAnalyticsItem::kTypeInt64: + goal += sizeof(uint64_t); + break; + case MediaAnalyticsItem::kTypeDouble: + goal += sizeof(double); + break; + case MediaAnalyticsItem::kTypeRate: + goal += 2 * sizeof(uint64_t); + break; + case MediaAnalyticsItem::kTypeCString: + // length + actual string + null + goal += strlen(prop->u.CStringValue) + 1; + break; + default: + ALOGE("found bad Prop type: %d, idx %d, name %s", + prop->mType, i, prop->mName); + return false; + } + } + + // now that we have a size... let's allocate and fill + build = (char *)malloc(goal); + if (build == NULL) + return false; + + memset(build, 0, goal); + + char *filling = build; + +#define _INSERT(val, size) \ + { memcpy(filling, &(val), (size)); filling += (size);} +#define _INSERTSTRING(val, size) \ + { memcpy(filling, (val), (size)); filling += (size);} + + _INSERT(goal, sizeof(int32_t)); + _INSERT(version, sizeof(int32_t)); + _INSERT(count, sizeof(int32_t)); + + for (int i = 0 ; i < count; i++ ) { + Prop *prop = &mProps[i]; + int16_t attrNameLen = strlen(prop->mName) + 1; + _INSERT(attrNameLen, sizeof(int16_t)); + _INSERTSTRING(prop->mName, attrNameLen); // termination included + int8_t elemtype; + int16_t elemsize; + switch (prop->mType) { + case MediaAnalyticsItem::kTypeInt32: + { + elemtype = kInt32; + _INSERT(elemtype, sizeof(int8_t)); + elemsize = sizeof(int32_t); + _INSERT(elemsize, sizeof(int16_t)); + + _INSERT(prop->u.int32Value, sizeof(int32_t)); + break; + } + case MediaAnalyticsItem::kTypeInt64: + { + elemtype = kInt64; + _INSERT(elemtype, sizeof(int8_t)); + elemsize = sizeof(int64_t); + _INSERT(elemsize, sizeof(int16_t)); + + _INSERT(prop->u.int64Value, sizeof(int64_t)); + break; + } + case MediaAnalyticsItem::kTypeDouble: + { + elemtype = kDouble; + _INSERT(elemtype, sizeof(int8_t)); + elemsize = sizeof(double); + _INSERT(elemsize, sizeof(int16_t)); + + _INSERT(prop->u.doubleValue, sizeof(double)); + break; + } + case MediaAnalyticsItem::kTypeRate: + { + elemtype = kRate; + _INSERT(elemtype, sizeof(int8_t)); + elemsize = 2 * sizeof(uint64_t); + _INSERT(elemsize, sizeof(int16_t)); + + _INSERT(prop->u.rate.count, sizeof(uint64_t)); + _INSERT(prop->u.rate.duration, sizeof(uint64_t)); + break; + } + case MediaAnalyticsItem::kTypeCString: + { + elemtype = kCString; + _INSERT(elemtype, sizeof(int8_t)); + elemsize = strlen(prop->u.CStringValue) + 1; + _INSERT(elemsize, sizeof(int16_t)); + + _INSERTSTRING(prop->u.CStringValue, elemsize); + break; + } + default: + // error if can't encode; warning if can't decode + ALOGE("found bad Prop type: %d, idx %d, name %s", + prop->mType, i, prop->mName); + goto badness; + } + } + + if (build + goal != filling) { + ALOGE("problems populating; wrote=%d planned=%d", + (int)(filling-build), goal); + goto badness; + } + + *pbuffer = build; + *plength = goal; + + return true; + + badness: + free(build); + return false; +} + } // namespace android diff --git a/media/libmediametrics/MediaMetrics.cpp b/media/libmediametrics/MediaMetrics.cpp index 9b08aa74fd..61091900c0 100644 --- a/media/libmediametrics/MediaMetrics.cpp +++ b/media/libmediametrics/MediaMetrics.cpp @@ -34,7 +34,7 @@ // manage the overall record mediametrics_handle_t mediametrics_create(mediametricskey_t key) { - android::MediaAnalyticsItem *item = new android::MediaAnalyticsItem(key); + android::MediaAnalyticsItem *item = android::MediaAnalyticsItem::create(key); return (mediametrics_handle_t) item; } @@ -187,18 +187,9 @@ bool mediametrics_isEnabled() { return android::MediaAnalyticsItem::isEnabled(); } -#if 0 -// do not expose this as is. -// need to revisit (or redefine) how the android::Parcel parameter is handled -// so that it meets the stable-API criteria for updateable components. -// -int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel) { +bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, size_t *length) { android::MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle; - if (item == NULL) { - return -1; - } - return item->writeToParcel(parcel); -} -#endif - + if (item == NULL) return false; + return item->dumpAttributes(buffer, length); +} diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h index b99cd91c1c..2f9e7c204f 100644 --- a/media/libmediametrics/include/MediaAnalyticsItem.h +++ b/media/libmediametrics/include/MediaAnalyticsItem.h @@ -17,9 +17,10 @@ #ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H #define ANDROID_MEDIA_MEDIAANALYTICSITEM_H -#include #include #include + +#include #include #include #include @@ -84,6 +85,10 @@ class MediaAnalyticsItem { public: + // so clients do not need to know size details + static MediaAnalyticsItem* create(Key key); + static MediaAnalyticsItem* create(); + // access functions for the class MediaAnalyticsItem(); MediaAnalyticsItem(Key); @@ -175,6 +180,9 @@ class MediaAnalyticsItem { int32_t writeToParcel(Parcel *); int32_t readFromParcel(const Parcel&); + // supports the stable interface + bool dumpAttributes(char **pbuffer, size_t *plength); + std::string toString(); std::string toString(int version); const char *toCString(); @@ -183,6 +191,11 @@ class MediaAnalyticsItem { // are we collecting analytics data static bool isEnabled(); + private: + // handle Parcel version 0 + int32_t writeToParcel0(Parcel *); + int32_t readFromParcel0(const Parcel&); + protected: // merge fields from arg into this diff --git a/media/libmediametrics/include/MediaMetrics.h b/media/libmediametrics/include/MediaMetrics.h index 4d2f352984..a4e1ed2c13 100644 --- a/media/libmediametrics/include/MediaMetrics.h +++ b/media/libmediametrics/include/MediaMetrics.h @@ -85,13 +85,9 @@ const char *mediametrics_readable(mediametrics_handle_t handle); void mediametrics_setUid(mediametrics_handle_t handle, uid_t uid); bool mediametrics_isEnabled(); -#if 0 -// do not expose this as is. -// need to revisit (or redefine) how the android::Parcel parameter is handled -// so that it meets the stable-API criteria for updateable components. -// -int32_t mediametrics_writeToParcel(mediametrics_handle_t handle, android::Parcel *parcel); -#endif +// serialized copy of the attributes/values, mostly for upstream getMetrics() calls +// caller owns the buffer allocated as part of this call. +bool mediametrics_getAttributes(mediametrics_handle_t handle, char **buffer, size_t *length); __END_DECLS diff --git a/media/libmediametrics/libmediametrics.map.txt b/media/libmediametrics/libmediametrics.map.txt new file mode 100644 index 0000000000..c46281aa63 --- /dev/null +++ b/media/libmediametrics/libmediametrics.map.txt @@ -0,0 +1,29 @@ +LIBMEDIAMETRICS_1 { + global: + mediametrics_addDouble; # apex + mediametrics_addInt32; # apex + mediametrics_addInt64; # apex + mediametrics_addRate; # apex + mediametrics_count; # apex + mediametrics_create; # apex + mediametrics_delete; # apex + mediametrics_freeCString; # apex + mediametrics_getAttributes; # apex + mediametrics_getCString; # apex + mediametrics_getDouble; # apex + mediametrics_getInt32; # apex + mediametrics_getInt64; # apex + mediametrics_getKey; # apex + mediametrics_getRate; # apex + mediametrics_isEnabled; # apex + mediametrics_readable; # apex + mediametrics_selfRecord; # apex + mediametrics_setCString; # apex + mediametrics_setDouble; # apex + mediametrics_setInt32; # apex + mediametrics_setInt64; # apex + mediametrics_setRate; # apex + mediametrics_setUid; # apex + local: + *; +}; -- GitLab From 89269d69b4d93deed90aa50881d0a5d88b3fd8d9 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Sun, 20 Jan 2019 14:46:19 -0800 Subject: [PATCH 0793/1530] MediaPlayer2 using the new mediametrics stable interface converts mediaplayer2 to use the new interface. Bug: 112555455 Test: boot / relevant ctsMediaTests Change-Id: I186c1931408414e9fd42bbc501e636a9624b6a6e --- .../mediaplayer2/MediaPlayer2Interface.h | 2 + .../include/mediaplayer2/mediaplayer2.h | 1 + media/libmediaplayer2/mediaplayer2.cpp | 17 +++- media/libmediaplayer2/nuplayer2/Android.bp | 1 + .../nuplayer2/NuPlayer2Decoder.cpp | 2 + .../nuplayer2/NuPlayer2Driver.cpp | 85 +++++++++---------- .../nuplayer2/NuPlayer2Driver.h | 5 +- 7 files changed, 66 insertions(+), 47 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h index 0c8d016902..7804a62df0 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h @@ -214,6 +214,8 @@ public: virtual status_t setParameter(int key, const Parcel &request) = 0; virtual status_t getParameter(int key, Parcel *reply) = 0; + virtual status_t getMetrics(char **buffer, size_t *length) = 0; + // Invoke a generic method on the player by using opaque parcels // for the request and reply. // diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 78865c448d..2993ab1063 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -102,6 +102,7 @@ public: status_t setAudioAttributes(const jobject attributes); jobject getAudioAttributes(); status_t getParameter(int key, Parcel* reply); + status_t getMetrics(char **buffer, size_t *length); // Modular DRM status_t prepareDrm(int64_t srcId, diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index f75380ced6..53f2fb12c8 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -979,6 +978,22 @@ status_t MediaPlayer2::getParameter(int key, Parcel *reply) { return status; } +// for mediametrics +status_t MediaPlayer2::getMetrics(char **buffer, size_t *length) { + ALOGD("MediaPlayer2::getMetrics()"); + Mutex::Autolock _l(mLock); + if (mPlayer == NULL) { + ALOGV("getMetrics: no active player"); + return INVALID_OPERATION; + } + + status_t status = mPlayer->getMetrics(buffer, length); + if (status != OK) { + ALOGD("getMetrics returns %d", status); + } + return status; +} + void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) { ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d", (long long)srcId, msg, ext1, ext2); diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp index 71cd50f628..0f69b2eabe 100644 --- a/media/libmediaplayer2/nuplayer2/Android.bp +++ b/media/libmediaplayer2/nuplayer2/Android.bp @@ -51,6 +51,7 @@ cc_library_static { "libui", "libgui", "libmedia", + "libmediametrics", "libmediandk", "libmediandk_utils", "libpowermanager", diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp index a5bd62d0fd..9729d86964 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp @@ -107,6 +107,8 @@ sp NuPlayer2::Decoder::getStats() const { mStats->setInt64("frames-total", mNumFramesTotal); mStats->setInt64("frames-dropped-input", mNumInputFramesDropped); mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); + mStats->setFloat("frame-rate-total", mFrameRateTotal); + return mStats; } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 56e9471f2a..1b661f291e 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -92,6 +92,7 @@ static const char *kPlayerWidth = "android.media.mediaplayer.width"; static const char *kPlayerHeight = "android.media.mediaplayer.height"; static const char *kPlayerFrames = "android.media.mediaplayer.frames"; static const char *kPlayerFramesDropped = "android.media.mediaplayer.dropped"; +static const char *kPlayerFrameRate = "android.media.mediaplayer.fps"; static const char *kPlayerAMime = "android.media.mediaplayer.audio.mime"; static const char *kPlayerACodec = "android.media.mediaplayer.audio.codec"; static const char *kPlayerDuration = "android.media.mediaplayer.durationMs"; @@ -125,7 +126,7 @@ NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp & mMediaClock(new MediaClock), mPlayer(new NuPlayer2(pid, uid, mMediaClock, context)), mPlayerFlags(0), - mAnalyticsItem(NULL), + mMetricsHandle(0), mClientUid(uid), mAtEOS(false), mLooping(false), @@ -136,9 +137,9 @@ NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp & mMediaClock->init(); - // set up an analytics record - mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer); - mAnalyticsItem->setUid(mClientUid); + // set up media metrics record + mMetricsHandle = mediametrics_create(kKeyPlayer); + mediametrics_setUid(mMetricsHandle, mClientUid); mNuPlayer2Looper->start( false, /* runOnCallingThread */ @@ -159,10 +160,7 @@ NuPlayer2Driver::~NuPlayer2Driver() { updateMetrics("destructor"); logMetrics("destructor"); - if (mAnalyticsItem != NULL) { - delete mAnalyticsItem; - mAnalyticsItem = NULL; - } + mediametrics_delete(mMetricsHandle); } status_t NuPlayer2Driver::initCheck() { @@ -453,15 +451,15 @@ void NuPlayer2Driver::updateMetrics(const char *where) { if (mime.startsWith("video/")) { int32_t width, height; - mAnalyticsItem->setCString(kPlayerVMime, mime.c_str()); + mediametrics_setCString(mMetricsHandle, kPlayerVMime, mime.c_str()); if (!name.empty()) { - mAnalyticsItem->setCString(kPlayerVCodec, name.c_str()); + mediametrics_setCString(mMetricsHandle, kPlayerVCodec, name.c_str()); } if (stats->findInt32("width", &width) && stats->findInt32("height", &height)) { - mAnalyticsItem->setInt32(kPlayerWidth, width); - mAnalyticsItem->setInt32(kPlayerHeight, height); + mediametrics_setInt32(mMetricsHandle, kPlayerWidth, width); + mediametrics_setInt32(mMetricsHandle, kPlayerHeight, height); } int64_t numFramesTotal = 0; @@ -469,14 +467,18 @@ void NuPlayer2Driver::updateMetrics(const char *where) { stats->findInt64("frames-total", &numFramesTotal); stats->findInt64("frames-dropped-output", &numFramesDropped); - mAnalyticsItem->setInt64(kPlayerFrames, numFramesTotal); - mAnalyticsItem->setInt64(kPlayerFramesDropped, numFramesDropped); + mediametrics_setInt64(mMetricsHandle, kPlayerFrames, numFramesTotal); + mediametrics_setInt64(mMetricsHandle, kPlayerFramesDropped, numFramesDropped); + float frameRate = 0; + if (stats->findFloat("frame-rate-output", &frameRate)) { + mediametrics_setInt64(mMetricsHandle, kPlayerFrameRate, frameRate); + } } else if (mime.startsWith("audio/")) { - mAnalyticsItem->setCString(kPlayerAMime, mime.c_str()); + mediametrics_setCString(mMetricsHandle, kPlayerAMime, mime.c_str()); if (!name.empty()) { - mAnalyticsItem->setCString(kPlayerACodec, name.c_str()); + mediametrics_setCString(mMetricsHandle, kPlayerACodec, name.c_str()); } } } @@ -487,17 +489,17 @@ void NuPlayer2Driver::updateMetrics(const char *where) { // getDuration() uses mLock for mutex -- careful where we use it. int64_t duration_ms = -1; getDuration(&duration_ms); - mAnalyticsItem->setInt64(kPlayerDuration, duration_ms); + mediametrics_setInt64(mMetricsHandle, kPlayerDuration, duration_ms); - mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 ); + mediametrics_setInt64(mMetricsHandle, kPlayerPlaying, (mPlayingTimeUs+500)/1000 ); if (mRebufferingEvents != 0) { - mAnalyticsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 ); - mAnalyticsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents); - mAnalyticsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit); + mediametrics_setInt64(mMetricsHandle, kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 ); + mediametrics_setInt32(mMetricsHandle, kPlayerRebufferingCount, mRebufferingEvents); + mediametrics_setInt32(mMetricsHandle, kPlayerRebufferingAtExit, mRebufferingAtExit); } - mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType()); + mediametrics_setCString(mMetricsHandle, kPlayerDataSourceType, mPlayer->getDataSourceType()); } @@ -507,7 +509,7 @@ void NuPlayer2Driver::logMetrics(const char *where) { } ALOGV("logMetrics(%p) from %s at state %d", this, where, mState); - if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) { + if (mMetricsHandle == 0 || mediametrics_isEnabled() == false) { return; } @@ -516,16 +518,12 @@ void NuPlayer2Driver::logMetrics(const char *where) { // and that always injects 3 fields (duration, playing time, and // datasource) into the record. // So the canonical "empty" record has 3 elements in it. - if (mAnalyticsItem->count() > 3) { - - mAnalyticsItem->selfrecord(); - + if (mediametrics_count(mMetricsHandle) > 3) { + mediametrics_selfRecord(mMetricsHandle); // re-init in case we prepare() and start() again. - delete mAnalyticsItem ; - mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer); - if (mAnalyticsItem) { - mAnalyticsItem->setUid(mClientUid); - } + mediametrics_delete(mMetricsHandle); + mMetricsHandle = mediametrics_create(kKeyPlayer); + mediametrics_setUid(mMetricsHandle, mClientUid); } else { ALOGV("did not have anything to record"); } @@ -649,17 +647,16 @@ status_t NuPlayer2Driver::setParameter( return INVALID_OPERATION; } -status_t NuPlayer2Driver::getParameter(int key, Parcel *reply) { +status_t NuPlayer2Driver::getParameter(int key __unused, Parcel *reply __unused) { + return INVALID_OPERATION; +} - if (key == FOURCC('m','t','r','X')) { - // mtrX -- a play on 'metrics' (not matrix) - // gather current info all together, parcel it, and send it back - updateMetrics("api"); - mAnalyticsItem->writeToParcel(reply); +status_t NuPlayer2Driver::getMetrics(char **buffer, size_t *length) { + updateMetrics("api"); + if (mediametrics_getAttributes(mMetricsHandle, buffer, length)) return OK; - } - - return INVALID_OPERATION; + else + return FAILED_TRANSACTION; } void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) { @@ -867,11 +864,11 @@ void NuPlayer2Driver::notifyListener_l( // ext1 is our primary 'error type' value. Only add ext2 when non-zero. // [test against msg is due to fall through from previous switch value] if (msg == MEDIA2_ERROR) { - mAnalyticsItem->setInt32(kPlayerError, ext1); + mediametrics_setInt32(mMetricsHandle, kPlayerError, ext1); if (ext2 != 0) { - mAnalyticsItem->setInt32(kPlayerErrorCode, ext2); + mediametrics_setInt32(mMetricsHandle, kPlayerErrorCode, ext2); } - mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str()); + mediametrics_setCString(mMetricsHandle, kPlayerErrorState, stateString(mState).c_str()); } mAtEOS = true; break; diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 0ec3a4b23b..3d299f3582 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -16,7 +16,7 @@ #include -#include +#include #include #include @@ -61,6 +61,7 @@ struct NuPlayer2Driver : public MediaPlayer2Interface { virtual void setAudioSink(const sp &audioSink) override; virtual status_t setParameter(int key, const Parcel &request) override; virtual status_t getParameter(int key, Parcel *reply) override; + virtual status_t getMetrics(char **buf, size_t *length) override; virtual status_t dump(int fd, const Vector &args) const override; @@ -132,7 +133,7 @@ private: sp mAudioSink; uint32_t mPlayerFlags; - MediaAnalyticsItem *mAnalyticsItem; + mediametrics_handle_t mMetricsHandle; uid_t mClientUid; bool mAtEOS; -- GitLab From 187dcc50b6e6ff2a15b1c56782e6353bf3020712 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 16 Jan 2019 12:58:54 -0800 Subject: [PATCH 0794/1530] libstagefright: Move OpusHeader files to libstagefright_foundation Now OpusHeader.cpp is part of libstagefright_foundation. This avoids a need to include libstagefright_opus_common as an additional shared library Bug: 115576456 Test: Tested with a Ogg writer using a local AMediaMuxer based application Change-Id: I102492676447c9f3677e2d418bd39d274ad4f392 --- media/libstagefright/Android.bp | 1 - media/libstagefright/OggWriter.cpp | 2 +- media/libstagefright/foundation/Android.bp | 1 + .../{opus => foundation}/OpusHeader.cpp | 6 ------ .../stagefright/foundation}/OpusHeader.h | 0 media/libstagefright/opus/Android.bp | 21 ------------------- media/libstagefright/webm/Android.bp | 1 - media/libstagefright/webm/WebmWriter.cpp | 2 +- 8 files changed, 3 insertions(+), 31 deletions(-) rename media/libstagefright/{opus => foundation}/OpusHeader.cpp (95%) rename media/libstagefright/{opus/include => foundation/include/media/stagefright/foundation}/OpusHeader.h (100%) delete mode 100644 media/libstagefright/opus/Android.bp diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index f45cc58e91..03eef48ab3 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -160,7 +160,6 @@ cc_library { "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx_utils", - "libstagefright_opus_common", "libRScpp", "libhidlallocatorutils", "libhidlbase", diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp index ad55c56ca6..7a60c6501c 100644 --- a/media/libstagefright/OggWriter.cpp +++ b/media/libstagefright/OggWriter.cpp @@ -30,7 +30,7 @@ #include #include #include -#include "OpusHeader.h" +#include extern "C" { #include diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp index dd1d904c85..533cd721df 100644 --- a/media/libstagefright/foundation/Android.bp +++ b/media/libstagefright/foundation/Android.bp @@ -72,6 +72,7 @@ cc_defaults { "MediaKeys.cpp", "MetaData.cpp", "MetaDataBase.cpp", + "OpusHeader.cpp", "avc_utils.cpp", "base64.cpp", "hexdump.cpp", diff --git a/media/libstagefright/opus/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp similarity index 95% rename from media/libstagefright/opus/OpusHeader.cpp rename to media/libstagefright/foundation/OpusHeader.cpp index e4a460c683..d2c8e2901d 100644 --- a/media/libstagefright/opus/OpusHeader.cpp +++ b/media/libstagefright/foundation/OpusHeader.cpp @@ -43,9 +43,6 @@ constexpr uint8_t kOpusChannelMap[kMaxChannels][kMaxChannels] = { {0, 6, 1, 2, 3, 4, 5, 7}, }; -// Opus always has a 48kHz output rate. This is true for all Opus, not just this -// implementation. -constexpr int kRate = 48000; // Size of the Opus header excluding optional mapping information. constexpr size_t kOpusHeaderSize = 19; // Offset to magic string that starts Opus header. @@ -76,15 +73,12 @@ constexpr size_t kOpusHeaderNumStreamsOffset = 19; constexpr size_t kOpusHeaderNumCoupledStreamsOffset = 20; // Offset to the stream to channel mapping in the Opus header. constexpr size_t kOpusHeaderStreamMapOffset = 21; -// Maximum packet size used in Xiph's opusdec. -constexpr int kMaxOpusOutputPacketSizeSamples = 960 * 6; // Default audio output channel layout. Used to initialize |stream_map| in // OpusHeader, and passed to opus_multistream_decoder_create() when the header // does not contain mapping information. The values are valid only for mono and // stereo output: Opus streams with more than 2 channels require a stream map. constexpr int kMaxChannelsWithDefaultLayout = 2; -constexpr uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = {0, 1}; static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_offset) { // check whether the 2nd byte is within the buffer diff --git a/media/libstagefright/opus/include/OpusHeader.h b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h similarity index 100% rename from media/libstagefright/opus/include/OpusHeader.h rename to media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h diff --git a/media/libstagefright/opus/Android.bp b/media/libstagefright/opus/Android.bp deleted file mode 100644 index c5086ec3c0..0000000000 --- a/media/libstagefright/opus/Android.bp +++ /dev/null @@ -1,21 +0,0 @@ -cc_library_shared { - name: "libstagefright_opus_common", - vendor_available: true, - - export_include_dirs: ["include"], - - srcs: ["OpusHeader.cpp"], - - shared_libs: ["liblog"], - - cflags: ["-Werror"], - - sanitize: { - integer_overflow: true, - cfi: true, - diag: { - integer_overflow: true, - cfi: true, - }, - }, -} \ No newline at end of file diff --git a/media/libstagefright/webm/Android.bp b/media/libstagefright/webm/Android.bp index 1f840b78ca..64ecc2db19 100644 --- a/media/libstagefright/webm/Android.bp +++ b/media/libstagefright/webm/Android.bp @@ -28,7 +28,6 @@ cc_library_static { shared_libs: [ "libstagefright_foundation", - "libstagefright_opus_common", "libutils", "liblog", ], diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp index 7b4b23a799..b0a303e80b 100644 --- a/media/libstagefright/webm/WebmWriter.cpp +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include -- GitLab From ad69acb241c5ea05b9df34c75e6cf41d5d0dcf34 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 15 Jan 2019 11:58:29 -0800 Subject: [PATCH 0795/1530] OpusHeader: Add support for unified CSD Added few macros and functions to handle unified CSD for opus These functions are used to write/read codec delay and seek pre-roll data to/from CSD buffer Bug: 115576456 Test: vendor Change-Id: Iace44d7dea860c9fcf27492fcd9666b99a4c1e73 --- .../libstagefright/foundation/OpusHeader.cpp | 86 ++++++++++++++++++- .../media/stagefright/foundation/OpusHeader.h | 25 ++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp index d2c8e2901d..9faede1e58 100644 --- a/media/libstagefright/foundation/OpusHeader.cpp +++ b/media/libstagefright/foundation/OpusHeader.cpp @@ -16,7 +16,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "SoftOpus" - +#include #include #include @@ -176,4 +176,88 @@ int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, } } +int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate, + uint8_t* output, size_t outputSize, uint64_t codecDelay, + uint64_t seekPreRoll) { + if (outputSize < AOPUS_UNIFIED_CSD_MINSIZE) { + ALOGD("Buffer not large enough to hold unified OPUS CSD"); + return -1; + } + + int headerLen = WriteOpusHeader(header, inputSampleRate, output, + outputSize); + if (headerLen < 0) { + ALOGD("WriteOpusHeader failed"); + return -1; + } + if (headerLen >= (outputSize - 2 * AOPUS_TOTAL_CSD_SIZE)) { + ALOGD("Buffer not large enough to hold codec delay and seek pre roll"); + return -1; + } + + uint64_t length = AOPUS_LENGTH; + + /* + Following is the CSD syntax for signalling codec delay and + seek pre-roll which is to be appended after OpusHeader + + Marker (8 bytes) | Length (8 bytes) | Samples (8 bytes) + + Markers supported: + AOPUSDLY - Signals Codec Delay + AOPUSPRL - Signals seek pre roll + + Length should be 8. + */ + + // Add codec delay + memcpy(output + headerLen, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE); + headerLen += AOPUS_MARKER_SIZE; + memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE); + headerLen += AOPUS_LENGTH_SIZE; + memcpy(output + headerLen, &codecDelay, AOPUS_CSD_SIZE); + headerLen += AOPUS_CSD_SIZE; + + // Add skip pre roll + memcpy(output + headerLen, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE); + headerLen += AOPUS_MARKER_SIZE; + memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE); + headerLen += AOPUS_LENGTH_SIZE; + memcpy(output + headerLen, &seekPreRoll, AOPUS_CSD_SIZE); + headerLen += AOPUS_CSD_SIZE; + + return headerLen; +} + +void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, + void **opusHeadBuf, size_t *opusHeadSize, + void **codecDelayBuf, size_t *codecDelaySize, + void **seekPreRollBuf, size_t *seekPreRollSize) { + *codecDelayBuf = NULL; + *codecDelaySize = 0; + *seekPreRollBuf = NULL; + *seekPreRollSize = 0; + *opusHeadBuf = (void *)data; + *opusHeadSize = data_size; + if (data_size >= AOPUS_UNIFIED_CSD_MINSIZE) { + size_t i = 0; + while (i < data_size - AOPUS_TOTAL_CSD_SIZE) { + uint8_t *csdBuf = (uint8_t *)data + i; + if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) { + *opusHeadSize = std::min(*opusHeadSize, i); + *codecDelayBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE; + *codecDelaySize = AOPUS_CSD_SIZE; + i += AOPUS_TOTAL_CSD_SIZE; + } else if (!memcmp(csdBuf, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE)) { + *opusHeadSize = std::min(*opusHeadSize, i); + *seekPreRollBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE; + *seekPreRollSize = AOPUS_CSD_SIZE; + i += AOPUS_TOTAL_CSD_SIZE; + } else { + i++; + } + } + } +} + } // namespace android diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h index f9f79cd905..9bffccbf44 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h @@ -24,6 +24,24 @@ namespace android { +/* Constants used for delimiting Opus CSD */ +#define AOPUS_CSD_CODEC_DELAY_MARKER "AOPUSDLY" +#define AOPUS_CSD_SEEK_PREROLL_MARKER "AOPUSPRL" +#define AOPUS_CSD_SIZE 8 +#define AOPUS_LENGTH 8 +#define AOPUS_MARKER_SIZE 8 +#define AOPUS_LENGTH_SIZE 8 +#define AOPUS_TOTAL_CSD_SIZE \ + ((AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_CSD_SIZE)) +#define AOPUS_CSD0_MINSIZE 19 +#define AOPUS_UNIFIED_CSD_MINSIZE \ + ((AOPUS_CSD0_MINSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE)) + +/* CSD0 at max can be 22 bytes + max number of channels (255) */ +#define AOPUS_CSD0_MAXSIZE 277 +#define AOPUS_UNIFIED_CSD_MAXSIZE \ + ((AOPUS_CSD0_MAXSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE)) + struct OpusHeader { int channels; int channel_mapping; @@ -36,6 +54,13 @@ struct OpusHeader { bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header); int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size); +void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, + void **opusHeadBuf, size_t *opusHeadSize, + void **codecDelayBuf, size_t *codecDelaySize, + void **seekPreRollBuf, size_t *seekPreRollSize); +int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate, + uint8_t* output, size_t outputSize, uint64_t codecDelay, + uint64_t seekPreRoll); } // namespace android #endif // OPUS_HEADER_H_ -- GitLab From 94d6bb5941a61d8f86e9256e257722a29c9ea7de Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 16 Nov 2018 17:40:29 -0800 Subject: [PATCH 0796/1530] C2SoftOpusDec: Add support for decoding single CSD If first CSD contains additional bytes, those are decoded based on markers present. C2 encoder plugin sends CSD in this format, so decoder is updated to support such input. C2 decoder also continues to support three different CSDs in sent in the legacy format (OpusHead in first CSD, followed by two CSDs of 8 bytes to signal CodecDelay and seek pre-roll) Bug: 115576456 Test: cts-tradefed run commandAndExit cts-dev -m CtsMediaTestCases \ -t android.media.cts.DecoderTest Change-Id: I089acbd2dddd36175c1e92455e488719a0e3a143 --- .../codec2/components/opus/C2SoftOpusDec.cpp | 155 ++++++------------ media/codec2/components/opus/C2SoftOpusDec.h | 10 -- 2 files changed, 48 insertions(+), 117 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp index 2439c3c4a5..3ce1fd67d3 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.cpp +++ b/media/codec2/components/opus/C2SoftOpusDec.cpp @@ -19,10 +19,9 @@ #include #include - +#include #include #include - #include "C2SoftOpusDec.h" extern "C" { @@ -188,16 +187,6 @@ static void fillEmptyWork(const std::unique_ptr &work) { work->workletsProcessed = 1u; } -static uint16_t ReadLE16(const uint8_t *data, size_t data_size, - uint32_t read_offset) { - if (read_offset + 1 > data_size) - return 0; - uint16_t val; - val = data[read_offset]; - val |= data[read_offset + 1] << 8; - return val; -} - static const int kRate = 48000; // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies @@ -216,81 +205,6 @@ static const int kMaxOpusOutputPacketSizeSamples = 960 * 6; static const int kMaxChannelsWithDefaultLayout = 2; static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 }; -// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header -static bool ParseOpusHeader(const uint8_t *data, size_t data_size, - OpusHeader* header) { - // Size of the Opus header excluding optional mapping information. - const size_t kOpusHeaderSize = 19; - - // Offset to the channel count byte in the Opus header. - const size_t kOpusHeaderChannelsOffset = 9; - - // Offset to the pre-skip value in the Opus header. - const size_t kOpusHeaderSkipSamplesOffset = 10; - - // Offset to the gain value in the Opus header. - const size_t kOpusHeaderGainOffset = 16; - - // Offset to the channel mapping byte in the Opus header. - const size_t kOpusHeaderChannelMappingOffset = 18; - - // Opus Header contains a stream map. The mapping values are in the header - // beyond the always present |kOpusHeaderSize| bytes of data. The mapping - // data contains stream count, coupling information, and per channel mapping - // values: - // - Byte 0: Number of streams. - // - Byte 1: Number coupled. - // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping - // values. - const size_t kOpusHeaderNumStreamsOffset = kOpusHeaderSize; - const size_t kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1; - const size_t kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2; - - if (data_size < kOpusHeaderSize) { - ALOGE("Header size is too small."); - return false; - } - header->channels = *(data + kOpusHeaderChannelsOffset); - if (header->channels <= 0 || header->channels > kMaxChannels) { - ALOGE("Invalid Header, wrong channel count: %d", header->channels); - return false; - } - - header->skip_samples = ReadLE16(data, - data_size, - kOpusHeaderSkipSamplesOffset); - - header->gain_db = static_cast(ReadLE16(data, - data_size, - kOpusHeaderGainOffset)); - - header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset); - if (!header->channel_mapping) { - if (header->channels > kMaxChannelsWithDefaultLayout) { - ALOGE("Invalid Header, missing stream map."); - return false; - } - header->num_streams = 1; - header->num_coupled = header->channels > 1; - header->stream_map[0] = 0; - header->stream_map[1] = 1; - return true; - } - if (data_size < kOpusHeaderStreamMapOffset + header->channels) { - ALOGE("Invalid stream map; insufficient data for current channel " - "count: %d", header->channels); - return false; - } - header->num_streams = *(data + kOpusHeaderNumStreamsOffset); - header->num_coupled = *(data + kOpusHeaderNumCoupledOffset); - if (header->num_streams + header->num_coupled != header->channels) { - ALOGE("Inconsistent channel mapping."); - return false; - } - for (int i = 0; i < header->channels; ++i) - header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i); - return true; -} // Convert nanoseconds to number of samples. static uint64_t ns_to_samples(uint64_t ns, int rate) { @@ -338,7 +252,19 @@ void C2SoftOpusDec::process( const uint8_t *data = rView.data() + inOffset; if (mInputBufferCount < 3) { if (mInputBufferCount == 0) { - if (!ParseOpusHeader(data, inSize, &mHeader)) { + size_t opusHeadSize = inSize; + size_t codecDelayBufSize = 0; + size_t seekPreRollBufSize = 0; + void *opusHeadBuf = (void *)data; + void *codecDelayBuf = NULL; + void *seekPreRollBuf = NULL; + + GetOpusHeaderBuffers(data, inSize, &opusHeadBuf, + &opusHeadSize, &codecDelayBuf, + &codecDelayBufSize, &seekPreRollBuf, + &seekPreRollBufSize); + + if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) { ALOGE("Encountered error while Parsing Opus Header."); mSignalledError = true; work->result = C2_CORRUPTED; @@ -377,6 +303,20 @@ void C2SoftOpusDec::process( work->result = C2_CORRUPTED; return; } + + if (codecDelayBuf && codecDelayBufSize == 8) { + uint64_t value; + memcpy(&value, codecDelayBuf, sizeof(uint64_t)); + mCodecDelay = ns_to_samples(value, kRate); + mSamplesToDiscard = mCodecDelay; + ++mInputBufferCount; + } + if (seekPreRollBuf && seekPreRollBufSize == 8) { + uint64_t value; + memcpy(&value, codecDelayBuf, sizeof(uint64_t)); + mSeekPreRoll = ns_to_samples(value, kRate); + ++mInputBufferCount; + } } else { if (inSize < 8) { ALOGE("Input sample size is too small."); @@ -392,29 +332,30 @@ void C2SoftOpusDec::process( } else { mSeekPreRoll = samples; - - ALOGI("Configuring decoder: %d Hz, %d channels", - kRate, mHeader.channels); - C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate); - C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels); - std::vector> failures; - c2_status_t err = mIntf->config( - { &sampleRateInfo, &channelCountInfo }, - C2_MAY_BLOCK, - &failures); - if (err == OK) { - work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo)); - work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo)); - } else { - ALOGE("Config Update failed"); - mSignalledError = true; - work->result = C2_CORRUPTED; - return; - } } } ++mInputBufferCount; + if (mInputBufferCount == 3) { + ALOGI("Configuring decoder: %d Hz, %d channels", + kRate, mHeader.channels); + C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate); + C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels); + std::vector> failures; + c2_status_t err = mIntf->config( + { &sampleRateInfo, &channelCountInfo }, + C2_MAY_BLOCK, + &failures); + if (err == OK) { + work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo)); + work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo)); + } else { + ALOGE("Config Update failed"); + mSignalledError = true; + work->result = C2_CORRUPTED; + return; + } + } fillEmptyWork(work); if (eos) { mSignalledOutputEos = true; diff --git a/media/codec2/components/opus/C2SoftOpusDec.h b/media/codec2/components/opus/C2SoftOpusDec.h index 92b7426d11..b0715ac95b 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.h +++ b/media/codec2/components/opus/C2SoftOpusDec.h @@ -24,16 +24,6 @@ struct OpusMSDecoder; namespace android { -struct OpusHeader { - int channels; - int skip_samples; - int channel_mapping; - int num_streams; - int num_coupled; - int16_t gain_db; - uint8_t stream_map[8]; -}; - struct C2SoftOpusDec : public SimpleC2Component { class IntfImpl; -- GitLab From c237cbc63980210d4b1c083cd0450cc7bf0b2606 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 16 Nov 2018 18:56:20 +0530 Subject: [PATCH 0797/1530] codec2: add C2SoftOpusEnc Test: Tested with local AMediaCodec application to encode to opus Bug: 115576456 Change-Id: I56f19f79fa2c9058651713594171ffed57bb9247 --- media/codec2/components/opus/Android.bp | 11 + .../codec2/components/opus/C2SoftOpusEnc.cpp | 638 ++++++++++++++++++ media/codec2/components/opus/C2SoftOpusEnc.h | 90 +++ media/codec2/vndk/C2Store.cpp | 1 + .../data/media_codecs_google_c2_audio.xml | 7 + services/mediacodec/registrant/Android.bp | 1 + 6 files changed, 748 insertions(+) create mode 100644 media/codec2/components/opus/C2SoftOpusEnc.cpp create mode 100644 media/codec2/components/opus/C2SoftOpusEnc.h diff --git a/media/codec2/components/opus/Android.bp b/media/codec2/components/opus/Android.bp index 240cdb960a..0ed141b5dc 100644 --- a/media/codec2/components/opus/Android.bp +++ b/media/codec2/components/opus/Android.bp @@ -9,3 +9,14 @@ cc_library_shared { shared_libs: ["libopus"], } +cc_library_shared { + name: "libcodec2_soft_opusenc", + defaults: [ + "libcodec2_soft-defaults", + "libcodec2_soft_sanitize_all-defaults", + ], + + srcs: ["C2SoftOpusEnc.cpp"], + + shared_libs: ["libopus"], +} diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp new file mode 100644 index 0000000000..d6ed5ffc3e --- /dev/null +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2019 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 "C2SoftOpusEnc" +#include + +#include +#include +#include +#include +#include "C2SoftOpusEnc.h" + +extern "C" { + #include + #include +} + +#define DEFAULT_FRAME_DURATION_MS 20 +namespace android { + +constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder"; + +class C2SoftOpusEnc::IntfImpl : public C2InterfaceHelper { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : C2InterfaceHelper(helper) { + + setDerivedInstance(this); + + addParameter( + DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio)) + .build()); + + addParameter( + DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + .build()); + + addParameter( + DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) + .withConstValue(AllocSharedString( + MEDIA_MIMETYPE_AUDIO_RAW)) + .build()); + + addParameter( + DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) + .withConstValue(AllocSharedString( + MEDIA_MIMETYPE_AUDIO_OPUS)) + .build()); + + addParameter( + DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + .withDefault(new C2StreamSampleRateInfo::input(0u, 48000)) + .withFields({C2F(mSampleRate, value).oneOf({ + 8000, 12000, 16000, 24000, 48000})}) + .withSetter((Setter::StrictValueWithNoDeps)) + .build()); + + addParameter( + DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) + .withFields({C2F(mChannelCount, value).inRange(1, 8)}) + .withSetter((Setter::StrictValueWithNoDeps)) + .build()); + + addParameter( + DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) + .withDefault(new C2BitrateTuning::output(0u, 128000)) + .withFields({C2F(mBitrate, value).inRange(500, 512000)}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); + + addParameter( + DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY) + .withDefault(new C2StreamComplexityTuning::output(0u, 10)) + .withFields({C2F(mComplexity, value).inRange(1, 10)}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); + + addParameter( + DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) + .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 3840)) + .build()); + } + + uint32_t getSampleRate() const { return mSampleRate->value; } + uint32_t getChannelCount() const { return mChannelCount->value; } + uint32_t getBitrate() const { return mBitrate->value; } + uint32_t getComplexity() const { return mComplexity->value; } + +private: + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; + std::shared_ptr mSampleRate; + std::shared_ptr mChannelCount; + std::shared_ptr mBitrate; + std::shared_ptr mComplexity; + std::shared_ptr mInputMaxBufSize; +}; + +C2SoftOpusEnc::C2SoftOpusEnc(const char* name, c2_node_id_t id, + const std::shared_ptr& intfImpl) + : SimpleC2Component( + std::make_shared>(name, id, intfImpl)), + mIntf(intfImpl), + mOutputBlock(nullptr), + mEncoder(nullptr), + mInputBufferPcm16(nullptr), + mOutIndex(0u) { +} + +C2SoftOpusEnc::~C2SoftOpusEnc() { + onRelease(); +} + +c2_status_t C2SoftOpusEnc::onInit() { + return initEncoder(); +} + +c2_status_t C2SoftOpusEnc::configureEncoder() { + unsigned char mono_mapping[256] = {0}; + unsigned char stereo_mapping[256] = {0, 1}; + unsigned char surround_mapping[256] = {0, 1, 255}; + mSampleRate = mIntf->getSampleRate(); + mChannelCount = mIntf->getChannelCount(); + uint32_t bitrate = mIntf->getBitrate(); + int complexity = mIntf->getComplexity(); + mNumSamplesPerFrame = mSampleRate / (1000 / mFrameDurationMs); + mNumPcmBytesPerInputFrame = + mChannelCount * mNumSamplesPerFrame * sizeof(int16_t); + int err = C2_OK; + + unsigned char* mapping; + if (mChannelCount < 2) { + mapping = mono_mapping; + } else if (mChannelCount == 2) { + mapping = stereo_mapping; + } else { + mapping = surround_mapping; + } + + if (mEncoder != nullptr) { + opus_multistream_encoder_destroy(mEncoder); + } + + mEncoder = opus_multistream_encoder_create(mSampleRate, mChannelCount, + 1, 1, mapping, OPUS_APPLICATION_AUDIO, &err); + if (err) { + ALOGE("Could not create libopus encoder. Error code: %i", err); + return C2_CORRUPTED; + } + + // Complexity + if (opus_multistream_encoder_ctl( + mEncoder, OPUS_SET_COMPLEXITY(complexity)) != OPUS_OK) { + ALOGE("failed to set complexity"); + return C2_BAD_VALUE; + } + + // DTX + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_DTX(0) != OPUS_OK)) { + ALOGE("failed to set dtx"); + return C2_BAD_VALUE; + } + + // Application + if (opus_multistream_encoder_ctl(mEncoder, + OPUS_SET_APPLICATION(OPUS_APPLICATION_AUDIO)) != OPUS_OK) { + ALOGE("failed to set application"); + return C2_BAD_VALUE; + } + + // Signal type + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_SIGNAL(OPUS_AUTO)) != + OPUS_OK) { + ALOGE("failed to set signal"); + return C2_BAD_VALUE; + } + + // Unconstrained VBR + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR(0) != OPUS_OK)) { + ALOGE("failed to set vbr type"); + return C2_BAD_VALUE; + } + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR_CONSTRAINT(0) != + OPUS_OK)) { + ALOGE("failed to set vbr constraint"); + return C2_BAD_VALUE; + } + + // Bitrate + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_BITRATE(bitrate)) != + OPUS_OK) { + ALOGE("failed to set bitrate"); + return C2_BAD_VALUE; + } + + // Get codecDelay + int32_t lookahead; + if (opus_multistream_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&lookahead)) != + OPUS_OK) { + ALOGE("failed to get lookahead"); + return C2_BAD_VALUE; + } + mCodecDelay = lookahead * 1000000000ll / mSampleRate; + + // Set seek preroll to 80 ms + mSeekPreRoll = 80000000; + return C2_OK; +} + +c2_status_t C2SoftOpusEnc::initEncoder() { + mSignalledEos = false; + mSignalledError = false; + mHeaderGenerated = false; + mIsFirstFrame = true; + mEncoderFlushed = false; + mBufferAvailable = false; + mAnchorTimeStamp = 0ull; + mProcessedSamples = 0; + mFilledLen = 0; + mFrameDurationMs = DEFAULT_FRAME_DURATION_MS; + if (!mInputBufferPcm16) { + mInputBufferPcm16 = + (int16_t*)malloc(kFrameSize * kMaxNumChannels * sizeof(int16_t)); + } + if (!mInputBufferPcm16) return C2_NO_MEMORY; + + /* Default Configurations */ + c2_status_t status = configureEncoder(); + return status; +} + +c2_status_t C2SoftOpusEnc::onStop() { + mSignalledEos = false; + mSignalledError = false; + mIsFirstFrame = true; + mEncoderFlushed = false; + mBufferAvailable = false; + mAnchorTimeStamp = 0ull; + mProcessedSamples = 0u; + mFilledLen = 0; + if (mEncoder) { + int status = opus_multistream_encoder_ctl(mEncoder, OPUS_RESET_STATE); + if (status != OPUS_OK) { + ALOGE("OPUS_RESET_STATE failed status = %s", opus_strerror(status)); + mSignalledError = true; + return C2_CORRUPTED; + } + } + if (mOutputBlock) mOutputBlock.reset(); + mOutputBlock = nullptr; + + return C2_OK; +} + +void C2SoftOpusEnc::onReset() { + (void)onStop(); +} + +void C2SoftOpusEnc::onRelease() { + (void)onStop(); + if (mInputBufferPcm16) { + free(mInputBufferPcm16); + mInputBufferPcm16 = nullptr; + } + if (mEncoder) { + opus_multistream_encoder_destroy(mEncoder); + mEncoder = nullptr; + } +} + +c2_status_t C2SoftOpusEnc::onFlush_sm() { + return onStop(); +} + +// Drain the encoder to get last frames (if any) +int C2SoftOpusEnc::drainEncoder(uint8_t* outPtr) { + memset((uint8_t *)mInputBufferPcm16 + mFilledLen, 0, + (mNumPcmBytesPerInputFrame - mFilledLen)); + int encodedBytes = opus_multistream_encode( + mEncoder, mInputBufferPcm16, mNumSamplesPerFrame, outPtr, kMaxPayload); + if (encodedBytes > mOutputBlock->capacity()) { + ALOGE("not enough space left to write encoded data, dropping %d bytes", + mBytesEncoded); + // a fatal error would stop the encoding + return -1; + } + ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes, + mNumPcmBytesPerInputFrame); + mEncoderFlushed = true; + mFilledLen = 0; + return encodedBytes; +} + +void C2SoftOpusEnc::process(const std::unique_ptr& work, + const std::shared_ptr& pool) { + // Initialize output work + work->result = C2_OK; + work->workletsProcessed = 1u; + work->worklets.front()->output.flags = work->input.flags; + + if (mSignalledError || mSignalledEos) { + work->result = C2_BAD_VALUE; + return; + } + + bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0; + C2ReadView rView = mDummyReadView; + size_t inOffset = 0u; + size_t inSize = 0u; + c2_status_t err = C2_OK; + if (!work->input.buffers.empty()) { + rView = + work->input.buffers[0]->data().linearBlocks().front().map().get(); + inSize = rView.capacity(); + if (inSize && rView.error()) { + ALOGE("read view map failed %d", rView.error()); + work->result = C2_CORRUPTED; + return; + } + } + + ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", + inSize, (int)work->input.ordinal.timestamp.peeku(), + (int)work->input.ordinal.frameIndex.peeku(), work->input.flags); + + if (!mEncoder) { + if (initEncoder() != C2_OK) { + ALOGE("initEncoder failed with status %d", err); + work->result = err; + mSignalledError = true; + return; + } + } + if (mIsFirstFrame) { + mAnchorTimeStamp = work->input.ordinal.timestamp.peekull(); + mIsFirstFrame = false; + } + + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock); + if (err != C2_OK) { + ALOGE("fetchLinearBlock for Output failed with status %d", err); + work->result = C2_NO_MEMORY; + return; + } + + C2WriteView wView = mOutputBlock->map().get(); + if (wView.error()) { + ALOGE("write view map failed %d", wView.error()); + work->result = C2_CORRUPTED; + mOutputBlock.reset(); + return; + } + + size_t inPos = 0; + size_t processSize = 0; + mBytesEncoded = 0; + uint64_t outTimeStamp = 0u; + std::shared_ptr buffer; + uint64_t inputIndex = work->input.ordinal.frameIndex.peeku(); + const uint8_t* inPtr = rView.data() + inOffset; + + class FillWork { + public: + FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal, + const std::shared_ptr &buffer) + : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) { + } + ~FillWork() = default; + + void operator()(const std::unique_ptr& work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = mOrdinal; + work->workletsProcessed = 1u; + work->result = C2_OK; + if (mBuffer) { + work->worklets.front()->output.buffers.push_back(mBuffer); + } + ALOGV("timestamp = %lld, index = %lld, w/%s buffer", + mOrdinal.timestamp.peekll(), + mOrdinal.frameIndex.peekll(), + mBuffer ? "" : "o"); + } + + private: + const uint32_t mFlags; + const C2WorkOrdinalStruct mOrdinal; + const std::shared_ptr mBuffer; + }; + + C2WorkOrdinalStruct outOrdinal = work->input.ordinal; + + if (!mHeaderGenerated) { + uint8_t header[AOPUS_UNIFIED_CSD_MAXSIZE]; + memset(header, 0, sizeof(header)); + OpusHeader opusHeader; + opusHeader.channels = mChannelCount; + opusHeader.num_streams = mChannelCount; + opusHeader.num_coupled = 0; + opusHeader.channel_mapping = ((mChannelCount > 8) ? 255 : (mChannelCount > 2)); + opusHeader.gain_db = 0; + opusHeader.skip_samples = 0; + int headerLen = WriteOpusHeaders(opusHeader, mSampleRate, header, + sizeof(header), mCodecDelay, mSeekPreRoll); + + std::unique_ptr csd = + C2StreamCsdInfo::output::AllocUnique(headerLen, 0u); + if (!csd) { + ALOGE("CSD allocation failed"); + mSignalledError = true; + work->result = C2_NO_MEMORY; + return; + } + ALOGV("put csd, %d bytes", headerLen); + memcpy(csd->m.value, header, headerLen); + work->worklets.front()->output.configUpdate.push_back(std::move(csd)); + mHeaderGenerated = true; + } + + /* + * For buffer size which is not a multiple of mNumPcmBytesPerInputFrame, we will + * accumulate the input and keep it. Once the input is filled with expected number + * of bytes, we will send it to encoder. mFilledLen manages the bytes of input yet + * to be processed. The next call will fill mNumPcmBytesPerInputFrame - mFilledLen + * bytes to input and send it to the encoder. + */ + while (inPos < inSize) { + const uint8_t* pcmBytes = inPtr + inPos; + int filledSamples = mFilledLen / sizeof(int16_t); + if ((inPos + (mNumPcmBytesPerInputFrame - mFilledLen)) <= inSize) { + processSize = mNumPcmBytesPerInputFrame - mFilledLen; + mBufferAvailable = true; + } else { + processSize = inSize - inPos; + mBufferAvailable = false; + if (eos) { + memset(mInputBufferPcm16 + filledSamples, 0, + (mNumPcmBytesPerInputFrame - mFilledLen)); + mBufferAvailable = true; + } + } + const unsigned nInputSamples = processSize / sizeof(int16_t); + + for (unsigned i = 0; i < nInputSamples; i++) { + int32_t data = pcmBytes[2 * i + 1] << 8 | pcmBytes[2 * i]; + data = ((data & 0xFFFF) ^ 0x8000) - 0x8000; + mInputBufferPcm16[i + filledSamples] = data; + } + inPos += processSize; + mFilledLen += processSize; + if (!mBufferAvailable) break; + uint8_t* outPtr = wView.data() + mBytesEncoded; + int encodedBytes = + opus_multistream_encode(mEncoder, mInputBufferPcm16, + mNumSamplesPerFrame, outPtr, kMaxPayload); + ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes, + processSize); + + if (encodedBytes < 0 || encodedBytes > kMaxPayload) { + ALOGE("opus_encode failed, encodedBytes : %d", encodedBytes); + mSignalledError = true; + work->result = C2_CORRUPTED; + return; + } + if (buffer) { + outOrdinal.frameIndex = mOutIndex++; + outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp; + cloneAndSend( + inputIndex, work, + FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer)); + buffer.reset(); + } + if (encodedBytes > 0) { + buffer = + createLinearBuffer(mOutputBlock, mBytesEncoded, encodedBytes); + } + mBytesEncoded += encodedBytes; + mProcessedSamples += (filledSamples + nInputSamples); + outTimeStamp = + mProcessedSamples * 1000000ll / mChannelCount / mSampleRate; + if ((processSize + mFilledLen) < mNumPcmBytesPerInputFrame) + mEncoderFlushed = true; + mFilledLen = 0; + } + + uint32_t flags = 0; + if (eos) { + ALOGV("signalled eos"); + mSignalledEos = true; + if (!mEncoderFlushed) { + if (buffer) { + outOrdinal.frameIndex = mOutIndex++; + outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp; + cloneAndSend( + inputIndex, work, + FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer)); + buffer.reset(); + } + // drain the encoder for last buffer + drainInternal(pool, work); + } + flags = C2FrameData::FLAG_END_OF_STREAM; + } + if (buffer) { + outOrdinal.frameIndex = mOutIndex++; + outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp; + FillWork((C2FrameData::flags_t)(flags), outOrdinal, buffer)(work); + buffer.reset(); + } + mOutputBlock = nullptr; +} + +c2_status_t C2SoftOpusEnc::drainInternal( + const std::shared_ptr& pool, + const std::unique_ptr& work) { + mBytesEncoded = 0; + std::shared_ptr buffer = nullptr; + C2WorkOrdinalStruct outOrdinal = work->input.ordinal; + bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0; + + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + c2_status_t err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock); + if (err != C2_OK) { + ALOGE("fetchLinearBlock for Output failed with status %d", err); + return C2_NO_MEMORY; + } + + C2WriteView wView = mOutputBlock->map().get(); + if (wView.error()) { + ALOGE("write view map failed %d", wView.error()); + mOutputBlock.reset(); + return C2_CORRUPTED; + } + + int encBytes = drainEncoder(wView.data()); + if (encBytes > 0) mBytesEncoded += encBytes; + if (mBytesEncoded > 0) { + buffer = createLinearBuffer(mOutputBlock, 0, mBytesEncoded); + mOutputBlock.reset(); + } + mProcessedSamples += (mNumPcmBytesPerInputFrame / sizeof(int16_t)); + uint64_t outTimeStamp = + mProcessedSamples * 1000000ll / mChannelCount / mSampleRate; + outOrdinal.frameIndex = mOutIndex++; + outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp; + work->worklets.front()->output.flags = + (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0); + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = outOrdinal; + work->workletsProcessed = 1u; + work->result = C2_OK; + if (buffer) { + work->worklets.front()->output.buffers.push_back(buffer); + } + mOutputBlock = nullptr; + return C2_OK; +} + +c2_status_t C2SoftOpusEnc::drain(uint32_t drainMode, + const std::shared_ptr& pool) { + if (drainMode == NO_DRAIN) { + ALOGW("drain with NO_DRAIN: no-op"); + return C2_OK; + } + if (drainMode == DRAIN_CHAIN) { + ALOGW("DRAIN_CHAIN not supported"); + return C2_OMITTED; + } + mIsFirstFrame = true; + mAnchorTimeStamp = 0ull; + mProcessedSamples = 0u; + return drainInternal(pool, nullptr); +} + +class C2SoftOpusEncFactory : public C2ComponentFactory { +public: + C2SoftOpusEncFactory() + : mHelper(std::static_pointer_cast( + GetCodec2PlatformComponentStore()->getParamReflector())) {} + + virtual c2_status_t createComponent( + c2_node_id_t id, std::shared_ptr* const component, + std::function deleter) override { + *component = std::shared_ptr( + new C2SoftOpusEnc( + COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual c2_status_t createInterface( + c2_node_id_t id, std::shared_ptr* const interface, + std::function deleter) override { + *interface = std::shared_ptr( + new SimpleInterface( + COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual ~C2SoftOpusEncFactory() override = default; +private: + std::shared_ptr mHelper; +}; + +} // namespace android + +extern "C" ::C2ComponentFactory* CreateCodec2Factory() { + ALOGV("in %s", __func__); + return new ::android::C2SoftOpusEncFactory(); +} + +extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; +} diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h new file mode 100644 index 0000000000..69e5240062 --- /dev/null +++ b/media/codec2/components/opus/C2SoftOpusEnc.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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_C2_SOFT_OPUS_ENC_H_ +#define ANDROID_C2_SOFT_OPUS_ENC_H_ + +#include +#include +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +struct OpusMSEncoder; + +namespace android { + +struct C2SoftOpusEnc : public SimpleC2Component { + class IntfImpl; + + C2SoftOpusEnc(const char *name, c2_node_id_t id, + const std::shared_ptr &intfImpl); + virtual ~C2SoftOpusEnc(); + + // From SimpleC2Component + c2_status_t onInit() override; + c2_status_t onStop() override; + void onReset() override; + void onRelease() override; + c2_status_t onFlush_sm() override; + void process( + const std::unique_ptr &work, + const std::shared_ptr &pool) override; + c2_status_t drain( + uint32_t drainMode, + const std::shared_ptr &pool) override; +private: + /* OPUS_FRAMESIZE_20_MS */ + const int kFrameSize = 960; + const int kMaxPayload = 4000; + const int kMaxNumChannels = 8; + + std::shared_ptr mIntf; + std::shared_ptr mOutputBlock; + + OpusMSEncoder* mEncoder; + int16_t* mInputBufferPcm16; + + bool mHeaderGenerated; + bool mIsFirstFrame; + bool mEncoderFlushed; + bool mBufferAvailable; + bool mSignalledEos; + bool mSignalledError; + uint32_t mSampleRate; + uint32_t mChannelCount; + uint32_t mFrameDurationMs; + uint64_t mAnchorTimeStamp; + uint64_t mProcessedSamples; + // Codec delay in ns + uint64_t mCodecDelay; + // Seek pre-roll in ns + uint64_t mSeekPreRoll; + int mNumSamplesPerFrame; + int mBytesEncoded; + int32_t mFilledLen; + size_t mNumPcmBytesPerInputFrame; + std::atomic_uint64_t mOutIndex; + c2_status_t initEncoder(); + c2_status_t configureEncoder(); + int drainEncoder(uint8_t* outPtr); + c2_status_t drainInternal(const std::shared_ptr& pool, + const std::unique_ptr& work); + + C2_DO_NOT_COPY(C2SoftOpusEnc); +}; + +} // namespace android + +#endif // ANDROID_C2_SOFT_OPUS_ENC_H_ diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index a5dd203db0..dc7e89c754 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -817,6 +817,7 @@ C2PlatformComponentStore::C2PlatformComponentStore() emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so"); emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so"); emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so"); + emplace("c2.android.opus.encoder", "libcodec2_soft_opusenc.so"); emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so"); emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so"); emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so"); diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml index 0b554a2b43..88cd08d2c9 100644 --- a/media/libstagefright/data/media_codecs_google_c2_audio.xml +++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml @@ -93,5 +93,12 @@
    + + + + + + + diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 8c40ad1ac8..0d100449a1 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -38,6 +38,7 @@ cc_library_shared { "libcodec2_soft_mp3dec", "libcodec2_soft_vorbisdec", "libcodec2_soft_opusdec", + "libcodec2_soft_opusenc", "libcodec2_soft_vp8dec", "libcodec2_soft_vp9dec", "libcodec2_soft_av1dec", -- GitLab From 36d18cc0bf20550a27e593a3c2734566695296bc Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 20 Dec 2018 19:06:01 -0800 Subject: [PATCH 0798/1530] libstagefright: Add support for muxing Opus files to Ogg format with unified CSD Now Ogg writer supports muxing opus files sent with either single unified CSD or 3 CSDs that are returned by Ogg extractor. Test: Tested using a local native application using AMediaMuxer API Bug: 115576456 Change-Id: I335b078a4edf059aca6b3fd0f17b6bab3e006615 --- media/libstagefright/OggWriter.cpp | 29 ++++++++--------------------- media/libstagefright/Utils.cpp | 29 ++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp index 7a60c6501c..5c139831a2 100644 --- a/media/libstagefright/OggWriter.cpp +++ b/media/libstagefright/OggWriter.cpp @@ -114,30 +114,17 @@ status_t OggWriter::addSource(const sp& source) { } mSampleRate = sampleRate; + uint32_t type; + const void *header_data; + size_t packet_size; + if (!source->getFormat()->findData(kKeyOpusHeader, &type, &header_data, &packet_size)) { + ALOGE("opus header not found"); + return UNKNOWN_ERROR; + } - OpusHeader header; - header.channels = nChannels; - header.num_streams = nChannels; - header.num_coupled = 0; - header.channel_mapping = ((nChannels > 8) ? 255 : (nChannels > 2)); - header.gain_db = 0; - header.skip_samples = 0; - - // headers are 21-bytes + something driven by channel count - // expect numbers in the low 30's here. WriteOpusHeader() will tell us - // if things are bad. - unsigned char header_data[100]; ogg_packet op; ogg_page og; - - const int packet_size = WriteOpusHeader(header, mSampleRate, (uint8_t*)header_data, - sizeof(header_data)); - - if (packet_size < 0) { - ALOGE("opus header writing failed"); - return UNKNOWN_ERROR; - } - op.packet = header_data; + op.packet = (unsigned char *)header_data; op.bytes = packet_size; op.b_o_s = 1; op.e_o_s = 0; diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 49e485af99..2e7da014a4 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1745,12 +1746,34 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) { - meta->setData(kKeyOpusHeader, 0, csd0->data(), csd0->size()); + size_t opusHeadSize = csd0->size(); + size_t codecDelayBufSize = 0; + size_t seekPreRollBufSize = 0; + void *opusHeadBuf = csd0->data(); + void *codecDelayBuf = NULL; + void *seekPreRollBuf = NULL; if (msg->findBuffer("csd-1", &csd1)) { - meta->setData(kKeyOpusCodecDelay, 0, csd1->data(), csd1->size()); + codecDelayBufSize = csd1->size(); + codecDelayBuf = csd1->data(); } if (msg->findBuffer("csd-2", &csd2)) { - meta->setData(kKeyOpusSeekPreRoll, 0, csd2->data(), csd2->size()); + seekPreRollBufSize = csd2->size(); + seekPreRollBuf = csd2->data(); + } + /* Extract codec delay and seek pre roll from csd-0, + * if csd-1 and csd-2 are not present */ + if (!codecDelayBuf && !seekPreRollBuf) { + GetOpusHeaderBuffers(csd0->data(), csd0->size(), &opusHeadBuf, + &opusHeadSize, &codecDelayBuf, + &codecDelayBufSize, &seekPreRollBuf, + &seekPreRollBufSize); + } + meta->setData(kKeyOpusHeader, 0, opusHeadBuf, opusHeadSize); + if (codecDelayBuf) { + meta->setData(kKeyOpusCodecDelay, 0, codecDelayBuf, codecDelayBufSize); + } + if (seekPreRollBuf) { + meta->setData(kKeyOpusSeekPreRoll, 0, seekPreRollBuf, seekPreRollBufSize); } } else if (mime == MEDIA_MIMETYPE_AUDIO_VORBIS) { meta->setData(kKeyVorbisInfo, 0, csd0->data(), csd0->size()); -- GitLab From ba2bb3a2a807e8925475ec674916da3a8b9a1989 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Thu, 24 Jan 2019 16:29:16 -0800 Subject: [PATCH 0799/1530] camera2 vndk: Remove superflous log from utility function. Test: builds Change-Id: I7070a78d77c6b525939fd4e44c9ca7ec5f16de99 Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/utils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp index 7193006799..5d2d47c175 100644 --- a/camera/ndk/ndk_vendor/impl/utils.cpp +++ b/camera/ndk/ndk_vendor/impl/utils.cpp @@ -70,7 +70,6 @@ void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) { return; } size_t size = get_camera_metadata_size(src); - ALOGE("Converting metadata size: %d", (int)size); dst->setToExternal((uint8_t *) src, size); return; } -- GitLab From d53525f4fd79a7ecbadd621630c23c0e925c731e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 24 Jan 2019 13:15:46 -0800 Subject: [PATCH 0800/1530] audiopolicy: Improve patch count checks Move ASSERT_ into the body of the test function, because assert failure only shows the code line where it has happened. Update names to emphasize that the delta is from the initial snapshot, not from the previous check. Test: audiopolicy_tests Change-Id: I4328d9d74c0632097150732727c123a446c8ee89 --- .../tests/audiopolicymanager_tests.cpp | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp index 24326bb75b..e9f465795f 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -117,9 +117,14 @@ class PatchCountCheck { explicit PatchCountCheck(AudioPolicyManagerTestClient *client) : mClient{client}, mInitialCount{mClient->getActivePatchesCount()} {} - void assertDelta(int delta) const { - ASSERT_EQ(mInitialCount + delta, mClient->getActivePatchesCount()); } - void assertNoChange() const { assertDelta(0); } + int deltaFromSnapshot() const { + size_t currentCount = mClient->getActivePatchesCount(); + if (mInitialCount <= currentCount) { + return currentCount - mInitialCount; + } else { + return -(static_cast(mInitialCount - currentCount)); + } + } private: const AudioPolicyManagerTestClient *mClient; const size_t mInitialCount; @@ -139,7 +144,7 @@ class AudioPolicyManagerTest : public testing::Test { int sampleRate, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, audio_port_handle_t *portId = nullptr); - PatchCountCheck snapPatchCount() { return PatchCountCheck(mClient.get()); } + PatchCountCheck snapshotPatchCount() { return PatchCountCheck(mClient.get()); } std::unique_ptr mClient; std::unique_ptr mManager; @@ -225,7 +230,7 @@ TEST_F(AudioPolicyManagerTest, Dump) { TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { audio_patch patch{}; audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(nullptr, &handle, 0)); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, nullptr, 0)); ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0)); @@ -252,20 +257,20 @@ TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) { ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0)); // Verify that the handle is left unchanged. ASSERT_EQ(AUDIO_PATCH_HANDLE_NONE, handle); - patchCount.assertNoChange(); + ASSERT_EQ(0, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) { audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE; uid_t uid = 42; - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty()); PatchBuilder patchBuilder; patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]). addSink(mManager->getConfig().getDefaultOutputDevice()); ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid)); ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } // TODO: Add patch creation tests that involve already existing patch @@ -350,84 +355,82 @@ TEST_F(AudioPolicyManagerTestMsd, Dump) { } TEST_F(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND, AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertNoChange(); + ASSERT_EQ(0, patchCount.deltaFromSnapshot()); } TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) { // Switch between formats that are supported and not supported by MSD. { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId, portId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT, &portId); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(1); + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); mManager->releaseOutput(portId); - patchCount.assertDelta(1); // compared to the state at the block entry - // TODO: make PatchCountCheck asserts more obvious. It's easy to - // miss the fact that it is immutable. + ASSERT_EQ(1, patchCount.deltaFromSnapshot()); } { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId, portId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT, &portId); ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertDelta(-1); + ASSERT_EQ(-1, patchCount.deltaFromSnapshot()); mManager->releaseOutput(portId); - patchCount.assertNoChange(); + ASSERT_EQ(0, patchCount.deltaFromSnapshot()); } { - const PatchCountCheck patchCount = snapPatchCount(); + const PatchCountCheck patchCount = snapshotPatchCount(); audio_port_handle_t selectedDeviceId; getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT); ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId()); - patchCount.assertNoChange(); + ASSERT_EQ(0, patchCount.deltaFromSnapshot()); } } -- GitLab From 86f1dd852787f2b240eed2d2bdfa65b9fe114d56 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 24 Jan 2019 13:17:59 -0800 Subject: [PATCH 0801/1530] audiopolicy: Specify the suite for presubmit tests This is required to actually run them on presubmit. Bug: 122855449 Test: run a presubmit check on a CL while the tests are broken Change-Id: Id95234d6a4198a246051c869d56ab5c62f6972f4 --- services/audiopolicy/tests/Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index 2ccb542eb9..e4fba0f56a 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -29,6 +29,8 @@ LOCAL_CFLAGS := -Werror -Wall LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) +LOCAL_COMPATIBILITY_SUITE := device-tests + include $(BUILD_NATIVE_TEST) # system/audio.h utilities test @@ -55,4 +57,6 @@ LOCAL_CFLAGS := -Werror -Wall LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) +LOCAL_COMPATIBILITY_SUITE := device-tests + include $(BUILD_NATIVE_TEST) -- GitLab From dcbda2a2efc838cf5b9597e6d346b3603e48bc40 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 24 Jan 2019 18:36:37 -0800 Subject: [PATCH 0802/1530] audio policy: fix recording activity reporting Add missing active clients management when an output or input is force closed by the audio policy manager. This resulted in stale activity monitoring entries in AuduiService. Bug: 123312504 Test: connect and disconnect a headset while recording a video Change-Id: I66fe96daed2475ae6ada700ef1210d38ad099da2 --- .../src/AudioInputDescriptor.cpp | 18 ++++++++++++------ .../src/AudioOutputDescriptor.cpp | 16 +++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 55d4db4488..c880e675be 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -257,15 +257,21 @@ void AudioInputDescriptor::stop() void AudioInputDescriptor::close() { if (mIoHandle != AUDIO_IO_HANDLE_NONE) { + // clean up active clients if any (can happen if close() is called to force + // clients to reconnect + for (const auto &client : getClientIterable()) { + if (client->active()) { + ALOGW("%s client with port ID %d still active on input %d", + __func__, client->portId(), mId); + setClientActive(client, false); + stop(); + } + } + mClientInterface->closeInput(mIoHandle); LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u", __FUNCTION__, mProfile->curOpenCount); - // do not call stop() here as stop() is supposed to be called after - // setClientActive(client, false) and we don't know how many clients - // are still active at this time - if (isActive()) { - mProfile->curActiveCount--; - } + mProfile->curOpenCount--; LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < mProfile->curActiveCount, "%s(%d): mProfile->curOpenCount %d < mProfile->curActiveCount %d.", diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 57328f0ed7..78b3f45bd9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -563,6 +563,17 @@ void SwAudioOutputDescriptor::stop() void SwAudioOutputDescriptor::close() { if (mIoHandle != AUDIO_IO_HANDLE_NONE) { + // clean up active clients if any (can happen if close() is called to force + // clients to reconnect + for (const auto &client : getClientIterable()) { + if (client->active()) { + ALOGW("%s client with port ID %d still active on output %d", + __func__, client->portId(), mId); + setClientActive(client, false); + stop(); + } + } + AudioParameter param; param.add(String8("closing"), String8("true")); mClientInterface->setParameters(mIoHandle, param.toString()); @@ -571,11 +582,6 @@ void SwAudioOutputDescriptor::close() LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u", __FUNCTION__, mProfile->curOpenCount); - // do not call stop() here as stop() is supposed to be called after setClientActive(false) - // and we don't know how many streams are still active at this time - if (isActive()) { - mProfile->curActiveCount--; - } mProfile->curOpenCount--; mIoHandle = AUDIO_IO_HANDLE_NONE; } -- GitLab From 191019ff4ec1129e6f0643b847268b5553a3383f Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 25 Jan 2019 03:18:15 +0000 Subject: [PATCH 0803/1530] Revert "Fix MediaPlayer2Test on 32bit mode" This reverts commit 8fc45862aa5fab7c7161a086a9f0c445fa91cf4d. Reason for revert: Build failure on sailfish due to system partition space. Change-Id: Ief6199e3b1a1fc842a34a225c4f6e567a99bcfe7 --- apex/Android.bp | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index c077a77e25..39997d2ea6 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -16,31 +16,21 @@ apex { name: "com.android.media", manifest: "manifest.json", java_libs: ["updatable-media"], - compile_multilib: "both", - multilib: { - first: { - // Extractor process runs only with the primary ABI. - native_shared_libs: [ - // Extractor plugins - "libaacextractor", - "libamrextractor", - "libflacextractor", - "libmidiextractor", - "libmkvextractor", - "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", - "libwavextractor", - ], - }, - both: { - native_shared_libs: [ - // MediaPlayer2 - "libmedia2_jni", - ], - }, - }, + native_shared_libs: [ + // Extractor plugins + "libaacextractor", + "libamrextractor", + "libflacextractor", + "libmidiextractor", + "libmkvextractor", + "libmp3extractor", + "libmp4extractor", + "libmpeg2extractor", + "liboggextractor", + "libwavextractor", + // MediaPlayer2 + "libmedia2_jni", + ], key: "com.android.media.key", } -- GitLab From f37a9661c9ef30e23f06c3fa980849bfe335846f Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 18 Jan 2019 14:26:47 +0800 Subject: [PATCH 0804/1530] Cannot play mp4 file with major brand 'wmf' Problem: Major brand 'wmf' is not in compatible array, so mp4 file with major brand 'wmf' is not recognized as mp4. Solution: add 'wmf' to compatible array Bug: 123051965 Test: play mp4 file whose ftyp major brand is 'wmf' and check if it can be played normally Change-Id: I9ff1411c01aa89d5bc35d85f6bc2ee07d37545e3 --- media/extractors/mp4/MPEG4Extractor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 50f172e888..d0efdddb2d 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -6048,6 +6048,7 @@ static bool isCompatibleBrand(uint32_t fourcc) { // Just give these file types a chance. FOURCC("qt "), // Apple's QuickTime FOURCC("MSNV"), // Sony's PSP + FOURCC("wmf "), FOURCC("3g2a"), // 3GPP2 FOURCC("3g2b"), -- GitLab From 4c6d2b5c41bc6f233e281d227e26f16f5f49ae26 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 4 Jan 2019 17:13:56 +0000 Subject: [PATCH 0805/1530] Camera: Add support for dynamic depth if possible Composite dynamic depth streams will be supported in case camera devices include: - Dense depth streams which are not exclusive. - Jpeg/Blob output with sizes that either match depth or are with similar aspect ratio. Make a guesstimate regarding the dynamic depth minimum frame and stall durations. Bug: 109735087 Test: Manual using application, Camera CTS Change-Id: I8a89c7895cf57ce4408e41b1afae9c85d48c4e07 --- .../include/camera/NdkCameraMetadataTags.h | 77 ++++++ .../common/CameraProviderManager.cpp | 248 ++++++++++++++++++ .../common/CameraProviderManager.h | 16 ++ 3 files changed, 341 insertions(+) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 641816f1d4..c1efa5fef2 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -5549,6 +5549,73 @@ typedef enum acamera_metadata_tag { ACAMERA_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS = // int32[n*5] ACAMERA_DEPTH_START + 5, + /** + *

    The available dynamic depth dataspace stream + * configurations that this camera device supports + * (i.e. format, width, height, output/input stream).

    + * + *

    Type: int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    These are output stream configurations for use with + * dataSpace DYNAMIC_DEPTH. The configurations are + * listed as (format, width, height, input?) tuples.

    + *

    Only devices that support depth output for at least + * the HAL_PIXEL_FORMAT_Y16 dense depth map along with + * HAL_PIXEL_FORMAT_BLOB with the same size or size with + * the same aspect ratio can have dynamic depth dataspace + * stream configuration. ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE also + * needs to be set to FALSE.

    + * + * @see ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS = + // int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t) + ACAMERA_DEPTH_START + 6, + /** + *

    This lists the minimum frame duration for each + * format/size combination for dynamic depth output streams.

    + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This should correspond to the frame duration when only that + * stream is active, with all processing (typically in android.*.mode) + * 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.

    + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS = // int64[4*n] + ACAMERA_DEPTH_START + 7, + /** + *

    This lists the maximum stall duration for each + * output format/size combination for dynamic depth streams.

    + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    A stall duration is how much extra time would get added + * to the normal minimum frame duration for a repeating request + * that has streams with non-zero stall.

    + *

    All dynamic depth output streams may have a nonzero stall + * duration.

    + */ + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS = // int64[4*n] + ACAMERA_DEPTH_START + 8, ACAMERA_DEPTH_END, /** @@ -8246,6 +8313,16 @@ typedef enum acamera_metadata_enum_acamera_depth_depth_is_exclusive { } acamera_metadata_enum_android_depth_depth_is_exclusive_t; +// ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS +typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_configurations { + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_OUTPUT + = 0, + + ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_INPUT + = 1, + +} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t; + // ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type { diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index a9cbe72926..cee4a84433 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -500,6 +500,248 @@ void CameraProviderManager::ProviderInfo::DeviceInfo3::queryPhysicalCameraIds() } } +void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedSizes( + const CameraMetadata& ch, uint32_t tag, android_pixel_format_t format, + std::vector> *sizes/*out*/) { + if (sizes == nullptr) { + return; + } + + auto scalerDims = ch.find(tag); + if (scalerDims.count > 0) { + // Scaler entry contains 4 elements (format, width, height, type) + for (size_t i = 0; i < scalerDims.count; i += 4) { + if ((scalerDims.data.i32[i] == format) && + (scalerDims.data.i32[i+3] == + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT)) { + sizes->push_back(std::make_tuple(scalerDims.data.i32[i+1], + scalerDims.data.i32[i+2])); + } + } + } +} + +void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedDurations( + const CameraMetadata& ch, uint32_t tag, android_pixel_format_t format, + const std::vector>& sizes, + std::vector *durations/*out*/) { + if (durations == nullptr) { + return; + } + + auto availableDurations = ch.find(tag); + if (availableDurations.count > 0) { + // Duration entry contains 4 elements (format, width, height, duration) + for (size_t i = 0; i < availableDurations.count; i += 4) { + for (const auto& size : sizes) { + int64_t width = std::get<0>(size); + int64_t height = std::get<1>(size); + if ((availableDurations.data.i64[i] == format) && + (availableDurations.data.i64[i+1] == width) && + (availableDurations.data.i64[i+2] == height)) { + durations->push_back(availableDurations.data.i64[i+3]); + } + } + } + } +} +void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedDynamicDepthDurations( + const std::vector& depthDurations, const std::vector& blobDurations, + std::vector *dynamicDepthDurations /*out*/) { + if ((dynamicDepthDurations == nullptr) || (depthDurations.size() != blobDurations.size())) { + return; + } + + // Unfortunately there is no direct way to calculate the dynamic depth stream duration. + // Processing time on camera service side can vary greatly depending on multiple + // variables which are not under our control. Make a guesstimate by taking the maximum + // corresponding duration value from depth and blob. + auto depthDuration = depthDurations.begin(); + auto blobDuration = blobDurations.begin(); + dynamicDepthDurations->reserve(depthDurations.size()); + while ((depthDuration != depthDurations.end()) && (blobDuration != blobDurations.end())) { + dynamicDepthDurations->push_back(std::max(*depthDuration, *blobDuration)); + depthDuration++; blobDuration++; + } +} + +void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedDynamicDepthSizes( + const std::vector>& blobSizes, + const std::vector>& depthSizes, + std::vector> *dynamicDepthSizes /*out*/, + std::vector> *internalDepthSizes /*out*/) { + if (dynamicDepthSizes == nullptr || internalDepthSizes == nullptr) { + return; + } + + // The dynamic depth spec. does not mention how close the AR ratio should be. + // Try using something appropriate. + float ARTolerance = .01f; + + //TODO: Remove this before merging! This is for testing purposes only + ARTolerance = 10.f; + + for (const auto& blobSize : blobSizes) { + float jpegAR = static_cast (std::get<0>(blobSize)) / + static_cast(std::get<1>(blobSize)); + bool found = false; + for (const auto& depthSize : depthSizes) { + if (depthSize == blobSize) { + internalDepthSizes->push_back(depthSize); + found = true; + break; + } else { + float depthAR = static_cast (std::get<0>(depthSize)) / + static_cast(std::get<1>(depthSize)); + if (std::fabs(jpegAR - depthAR) <= ARTolerance) { + internalDepthSizes->push_back(depthSize); + found = true; + break; + } + } + } + + if (found) { + dynamicDepthSizes->push_back(blobSize); + } + } +} + +status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() { + uint32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE; + uint32_t depthSizesTag = ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS; + auto& c = mCameraCharacteristics; + std::vector> supportedBlobSizes, supportedDepthSizes, + supportedDynamicDepthSizes, internalDepthSizes; + auto chTags = c.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS); + if (chTags.count == 0) { + ALOGE("%s: Supported camera characteristics is empty!", __FUNCTION__); + return BAD_VALUE; + } + + bool isDepthExclusivePresent = std::find(chTags.data.i32, chTags.data.i32 + chTags.count, + depthExclTag) != (chTags.data.i32 + chTags.count); + bool isDepthSizePresent = std::find(chTags.data.i32, chTags.data.i32 + chTags.count, + depthExclTag) != (chTags.data.i32 + chTags.count); + if (!(isDepthExclusivePresent && isDepthSizePresent)) { + // No depth support, nothing more to do. + return OK; + } + + auto depthExclusiveEntry = c.find(depthExclTag); + if (depthExclusiveEntry.count > 0) { + if (depthExclusiveEntry.data.u8[0] != ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE_FALSE) { + // Depth support is exclusive, nothing more to do. + return OK; + } + } else { + ALOGE("%s: Advertised depth exclusive tag but value is not present!", __FUNCTION__); + return BAD_VALUE; + } + + getSupportedSizes(c, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, HAL_PIXEL_FORMAT_BLOB, + &supportedBlobSizes); + getSupportedSizes(c, depthSizesTag, HAL_PIXEL_FORMAT_Y16, &supportedDepthSizes); + if (supportedBlobSizes.empty() || supportedDepthSizes.empty()) { + // Nothing to do in this case. + return OK; + } + + getSupportedDynamicDepthSizes(supportedBlobSizes, supportedDepthSizes, + &supportedDynamicDepthSizes, &internalDepthSizes); + if (supportedDynamicDepthSizes.empty()) { + ALOGE("%s: No dynamic depth size matched!", __func__); + // Nothing more to do. + return OK; + } + + std::vector dynamicDepthEntries; + for (const auto& it : supportedDynamicDepthSizes) { + int32_t entry[4] = {HAL_PIXEL_FORMAT_BLOB, static_cast (std::get<0>(it)), + static_cast (std::get<1>(it)), + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT }; + dynamicDepthEntries.insert(dynamicDepthEntries.end(), entry, entry + 4); + } + + std::vector depthMinDurations, depthStallDurations; + std::vector blobMinDurations, blobStallDurations; + std::vector dynamicDepthMinDurations, dynamicDepthStallDurations; + + getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS, + HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthMinDurations); + getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, + HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobMinDurations); + if (blobMinDurations.empty() || depthMinDurations.empty() || + (depthMinDurations.size() != blobMinDurations.size())) { + ALOGE("%s: Unexpected number of available depth min durations! %zu vs. %zu", + __FUNCTION__, depthMinDurations.size(), blobMinDurations.size()); + return BAD_VALUE; + } + + getSupportedDurations(c, ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS, + HAL_PIXEL_FORMAT_Y16, internalDepthSizes, &depthStallDurations); + getSupportedDurations(c, ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, + HAL_PIXEL_FORMAT_BLOB, supportedDynamicDepthSizes, &blobStallDurations); + if (blobStallDurations.empty() || depthStallDurations.empty() || + (depthStallDurations.size() != blobStallDurations.size())) { + ALOGE("%s: Unexpected number of available depth stall durations! %zu vs. %zu", + __FUNCTION__, depthStallDurations.size(), blobStallDurations.size()); + return BAD_VALUE; + } + + getSupportedDynamicDepthDurations(depthMinDurations, blobMinDurations, + &dynamicDepthMinDurations); + getSupportedDynamicDepthDurations(depthStallDurations, blobStallDurations, + &dynamicDepthStallDurations); + if (dynamicDepthMinDurations.empty() || dynamicDepthStallDurations.empty() || + (dynamicDepthMinDurations.size() != dynamicDepthStallDurations.size())) { + ALOGE("%s: Unexpected number of dynamic depth stall/min durations! %zu vs. %zu", + __FUNCTION__, dynamicDepthMinDurations.size(), dynamicDepthStallDurations.size()); + return BAD_VALUE; + } + + std::vector dynamicDepthMinDurationEntries; + auto itDuration = dynamicDepthMinDurations.begin(); + auto itSize = supportedDynamicDepthSizes.begin(); + while (itDuration != dynamicDepthMinDurations.end()) { + int64_t entry[4] = {HAL_PIXEL_FORMAT_BLOB, static_cast (std::get<0>(*itSize)), + static_cast (std::get<1>(*itSize)), *itDuration}; + dynamicDepthMinDurationEntries.insert(dynamicDepthMinDurationEntries.end(), entry, + entry + 4); + itDuration++; itSize++; + } + + std::vector dynamicDepthStallDurationEntries; + itDuration = dynamicDepthStallDurations.begin(); + itSize = supportedDynamicDepthSizes.begin(); + while (itDuration != dynamicDepthStallDurations.end()) { + int64_t entry[4] = {HAL_PIXEL_FORMAT_BLOB, static_cast (std::get<0>(*itSize)), + static_cast (std::get<1>(*itSize)), *itDuration}; + dynamicDepthStallDurationEntries.insert(dynamicDepthStallDurationEntries.end(), entry, + entry + 4); + itDuration++; itSize++; + } + + c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, + dynamicDepthEntries.data(), dynamicDepthEntries.size()); + c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, + dynamicDepthMinDurationEntries.data(), dynamicDepthMinDurationEntries.size()); + c.update(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, + dynamicDepthStallDurationEntries.data(), dynamicDepthStallDurationEntries.size()); + + std::vector supportedChTags; + supportedChTags.reserve(chTags.count + 3); + supportedChTags.insert(supportedChTags.end(), chTags.data.i32, + chTags.data.i32 + chTags.count); + supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS); + supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS); + supportedChTags.push_back(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS); + c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedChTags.data(), + supportedChTags.size()); + + return OK; +} + status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fixupMonochromeTags() { status_t res = OK; auto& c = mCameraCharacteristics; @@ -1442,6 +1684,12 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& __FUNCTION__, strerror(-res), res); return; } + res = addDynamicDepthTags(); + if (OK != res) { + ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-res), + res); + return; + } camera_metadata_entry flashAvailable = mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE); if (flashAvailable.count == 1 && diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 096674314b..1f34f2fc46 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -470,6 +470,22 @@ private: std::unordered_map mPhysicalCameraCharacteristics; void queryPhysicalCameraIds(); status_t fixupMonochromeTags(); + status_t addDynamicDepthTags(); + static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag, + android_pixel_format_t format, + std::vector> *sizes /*out*/); + void getSupportedDurations( const CameraMetadata& ch, uint32_t tag, + android_pixel_format_t format, + const std::vector>& sizes, + std::vector *durations/*out*/); + void getSupportedDynamicDepthDurations(const std::vector& depthDurations, + const std::vector& blobDurations, + std::vector *dynamicDepthDurations /*out*/); + static void getSupportedDynamicDepthSizes( + const std::vector>& blobSizes, + const std::vector>& depthSizes, + std::vector> *dynamicDepthSizes /*out*/, + std::vector> *internalDepthSizes /*out*/); status_t removeAvailableKeys(CameraMetadata& c, const std::vector& keys, uint32_t keyTag); }; -- GitLab From 538c90e79d06f99e8a2dda4b1ccf3a5e46062934 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Mon, 17 Dec 2018 18:03:19 +0000 Subject: [PATCH 0806/1530] Camera: Add DepthCompositeStream Add the necessary logic to support dynamic depth metadata. Bug: 109735087 Test: Manual using application, Camera CTS Change-Id: Ic4710872dc596bc718270e1c79d4da53fb850875 --- services/camera/libcameraservice/Android.bp | 8 + .../api1/client2/JpegProcessor.cpp | 4 + .../api1/client2/JpegProcessor.h | 6 +- .../api2/CameraDeviceClient.cpp | 108 +- .../api2/CameraDeviceClient.h | 4 + .../libcameraservice/api2/CompositeStream.cpp | 203 ++++ .../libcameraservice/api2/CompositeStream.h | 120 ++ .../api2/DepthCompositeStream.cpp | 1026 +++++++++++++++++ .../api2/DepthCompositeStream.h | 148 +++ .../common/CameraProviderManager.cpp | 7 +- .../common/CameraProviderManager.h | 2 + .../device3/Camera3Device.cpp | 18 +- .../device3/Camera3Stream.cpp | 21 +- .../libcameraservice/device3/Camera3Stream.h | 10 +- .../device3/Camera3StreamBufferListener.h | 2 + .../device3/Camera3StreamInterface.h | 8 +- 16 files changed, 1671 insertions(+), 24 deletions(-) create mode 100644 services/camera/libcameraservice/api2/CompositeStream.cpp create mode 100644 services/camera/libcameraservice/api2/CompositeStream.h create mode 100644 services/camera/libcameraservice/api2/DepthCompositeStream.cpp create mode 100644 services/camera/libcameraservice/api2/DepthCompositeStream.h diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 851dd690d9..3b362d6707 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -39,6 +39,8 @@ cc_library_shared { "api1/client2/CaptureSequencer.cpp", "api1/client2/ZslProcessor.cpp", "api2/CameraDeviceClient.cpp", + "api2/CompositeStream.cpp", + "api2/DepthCompositeStream.cpp", "device1/CameraHardwareInterface.cpp", "device3/Camera3Device.cpp", "device3/Camera3Stream.cpp", @@ -65,6 +67,9 @@ cc_library_shared { ], shared_libs: [ + "libimage_io", + "libdynamic_depth", + "libxml2", "libui", "liblog", "libutilscallstack", @@ -108,6 +113,8 @@ cc_library_shared { "system/media/private/camera/include", "frameworks/native/include/media/openmax", "frameworks/av/media/ndk", + "external/dynamic_depth/includes", + "external/dynamic_depth/internal", ], export_include_dirs: ["."], @@ -116,6 +123,7 @@ cc_library_shared { "-Wall", "-Wextra", "-Werror", + "-Wno-ignored-qualifiers", ], } diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index b7020fe312..e6f75f4d96 100755 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -62,6 +62,10 @@ void JpegProcessor::onFrameAvailable(const BufferItem& /*item*/) { } } +void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/, int /*streamId*/) { + // Intentionally left empty +} + void JpegProcessor::onBufferAcquired(const BufferInfo& /*bufferInfo*/) { // Intentionally left empty } diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h index 7187ad94a4..2ee930e02b 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.h +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h @@ -25,6 +25,7 @@ #include #include "camera/CameraMetadata.h" +#include "device3/Camera3StreamBufferListener.h" namespace android { @@ -53,12 +54,16 @@ class JpegProcessor: // Camera3StreamBufferListener implementation void onBufferAcquired(const BufferInfo& bufferInfo) override; void onBufferReleased(const BufferInfo& bufferInfo) override; + void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override; status_t updateStream(const Parameters ¶ms); status_t deleteStream(); int getStreamId() const; void dump(int fd, const Vector& args) const; + + static size_t findJpegSize(uint8_t* jpegBuffer, size_t maxSize); + private: static const nsecs_t kWaitDuration = 10000000; // 10 ms wp mDevice; @@ -82,7 +87,6 @@ class JpegProcessor: virtual bool threadLoop(); status_t processNewCapture(bool captureSuccess); - size_t findJpegSize(uint8_t* jpegBuffer, size_t maxSize); }; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 46fbc3e3c7..9e203da4d4 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -33,6 +33,8 @@ #include +#include "DepthCompositeStream.h" + // Convenience methods for constructing binder::Status objects for error returns #define STATUS_ERROR(errorCode, errorString) \ @@ -143,6 +145,7 @@ binder::Status CameraDeviceClient::submitRequest( binder::Status CameraDeviceClient::insertGbpLocked(const sp& gbp, SurfaceMap* outSurfaceMap, Vector* outputStreamIds, int32_t *currentStreamId) { + int compositeIdx; int idx = mStreamMap.indexOfKey(IInterface::asBinder(gbp)); // Trying to submit request with surface that wasn't created @@ -152,6 +155,11 @@ binder::Status CameraDeviceClient::insertGbpLocked(const spinsertGbp(outSurfaceMap, outputStreamIds, + currentStreamId); + return binder::Status::ok(); } const StreamSurfaceId& streamSurfaceId = mStreamMap.valueAt(idx); @@ -489,6 +497,17 @@ binder::Status CameraDeviceClient::endConfigure(int operatingMode, mCameraIdStr.string(), strerror(-err), err); ALOGE("%s: %s", __FUNCTION__, msg.string()); res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string()); + } else { + for (size_t i = 0; i < mCompositeStreamMap.size(); ++i) { + err = mCompositeStreamMap.valueAt(i)->configureStream(); + if (err != OK ) { + String8 msg = String8::format("Camera %s: Error configuring composite " + "streams: %s (%d)", mCameraIdStr.string(), strerror(-err), err); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string()); + break; + } + } } return res; @@ -692,8 +711,35 @@ binder::Status CameraDeviceClient::isSessionConfigurationSupported( return res; if (!isStreamInfoValid) { - mapStreamInfo(streamInfo, static_cast (it.getRotation()), - physicalCameraId, &streamConfiguration.streams[streamIdx++]); + if (camera3::DepthCompositeStream::isDepthCompositeStream(surface)) { + // We need to take in to account that composite streams can have + // additional internal camera streams. + std::vector compositeStreams; + ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo, + mDevice->info(), &compositeStreams); + if (ret != OK) { + String8 msg = String8::format( + "Camera %s: Failed adding depth composite streams: %s (%d)", + mCameraIdStr.string(), strerror(-ret), ret); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string()); + } + + if (compositeStreams.size() > 1) { + streamCount += compositeStreams.size() - 1; + streamConfiguration.streams.resize(streamCount); + } + + for (const auto& compositeStream : compositeStreams) { + mapStreamInfo(compositeStream, + static_cast (it.getRotation()), + physicalCameraId, &streamConfiguration.streams[streamIdx++]); + } + } else { + mapStreamInfo(streamInfo, + static_cast (it.getRotation()), + physicalCameraId, &streamConfiguration.streams[streamIdx++]); + } isStreamInfoValid = true; } } @@ -743,6 +789,7 @@ binder::Status CameraDeviceClient::deleteStream(int streamId) { bool isInput = false; std::vector> surfaces; ssize_t dIndex = NAME_NOT_FOUND; + ssize_t compositeIndex = NAME_NOT_FOUND; if (mInputStream.configured && mInputStream.id == streamId) { isInput = true; @@ -762,6 +809,13 @@ binder::Status CameraDeviceClient::deleteStream(int streamId) { } } + for (size_t i = 0; i < mCompositeStreamMap.size(); ++i) { + if (streamId == mCompositeStreamMap.valueAt(i)->getStreamId()) { + compositeIndex = i; + break; + } + } + if (surfaces.empty() && dIndex == NAME_NOT_FOUND) { String8 msg = String8::format("Camera %s: Invalid stream ID (%d) specified, no such" " stream created yet", mCameraIdStr.string(), streamId); @@ -791,6 +845,19 @@ binder::Status CameraDeviceClient::deleteStream(int streamId) { if (dIndex != NAME_NOT_FOUND) { mDeferredStreams.removeItemsAt(dIndex); } + + if (compositeIndex != NAME_NOT_FOUND) { + status_t ret; + if ((ret = mCompositeStreamMap.valueAt(compositeIndex)->deleteStream()) + != OK) { + String8 msg = String8::format("Camera %s: Unexpected error %s (%d) when " + "deleting composite stream %d", mCameraIdStr.string(), strerror(-err), err, + streamId); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string()); + } + mCompositeStreamMap.removeItemsAt(compositeIndex); + } } } @@ -870,11 +937,25 @@ binder::Status CameraDeviceClient::createStream( int streamId = camera3::CAMERA3_STREAM_ID_INVALID; std::vector surfaceIds; - err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width, - streamInfo.height, streamInfo.format, streamInfo.dataSpace, - static_cast(outputConfiguration.getRotation()), - &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(), - isShared); + if (!camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0])) { + err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width, + streamInfo.height, streamInfo.format, streamInfo.dataSpace, + static_cast(outputConfiguration.getRotation()), + &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(), + isShared); + } else { + sp compositeStream = new camera3::DepthCompositeStream(mDevice, + getRemoteCallback()); + err = compositeStream->createStream(surfaces, deferredConsumer, streamInfo.width, + streamInfo.height, streamInfo.format, + static_cast(outputConfiguration.getRotation()), + &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(), + isShared); + if (err == OK) { + mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()), + compositeStream); + } + } if (err != OK) { res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION, @@ -1808,7 +1889,14 @@ void CameraDeviceClient::notifyError(int32_t errorCode, // Thread safe. Don't bother locking. sp remoteCb = getRemoteCallback(); - if (remoteCb != 0) { + // Composites can have multiple internal streams. Error notifications coming from such internal + // streams may need to remain within camera service. + bool skipClientNotification = false; + for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { + skipClientNotification |= mCompositeStreamMap.valueAt(i)->onError(errorCode, resultExtras); + } + + if ((remoteCb != 0) && (!skipClientNotification)) { remoteCb->onDeviceError(errorCode, resultExtras); } } @@ -1901,6 +1989,10 @@ void CameraDeviceClient::onResultAvailable(const CaptureResult& result) { remoteCb->onResultReceived(result.mMetadata, result.mResultExtras, result.mPhysicalMetadatas); } + + for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { + mCompositeStreamMap.valueAt(i)->onResultAvailable(result); + } } binder::Status CameraDeviceClient::checkPidStatus(const char* checkLocation) { diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 17a0983498..1c5abb034d 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -26,8 +26,10 @@ #include "CameraService.h" #include "common/FrameProcessorBase.h" #include "common/Camera2ClientBase.h" +#include "CompositeStream.h" using android::camera3::OutputStreamInfo; +using android::camera3::CompositeStream; namespace android { @@ -314,6 +316,8 @@ private: // stream ID -> outputStreamInfo mapping std::unordered_map mStreamInfoMap; + KeyedVector, sp> mCompositeStreamMap; + static const int32_t MAX_SURFACES_PER_STREAM = 4; sp mProviderManager; }; diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp new file mode 100644 index 0000000000..796bf42ae0 --- /dev/null +++ b/services/camera/libcameraservice/api2/CompositeStream.cpp @@ -0,0 +1,203 @@ +/* + * 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 "Camera3-CompositeStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include +#include + +#include "common/CameraDeviceBase.h" +#include "CameraDeviceClient.h" +#include "CompositeStream.h" + +namespace android { +namespace camera3 { + +CompositeStream::CompositeStream(wp device, + wp cb) : + mDevice(device), + mRemoteCallback(cb), + mNumPartialResults(1), + mErrorState(false) { + sp cameraDevice = device.promote(); + if (cameraDevice.get() != nullptr) { + CameraMetadata staticInfo = cameraDevice->info(); + camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (entry.count > 0) { + mNumPartialResults = entry.data.i32[0]; + } + } +} + +status_t CompositeStream::createStream(const std::vector>& consumers, + bool hasDeferredConsumer, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int * id, const String8& physicalCameraId, + std::vector * surfaceIds, int streamSetId, bool isShared) { + if (hasDeferredConsumer) { + ALOGE("%s: Deferred consumers not supported in case of composite streams!", + __FUNCTION__); + return BAD_VALUE; + } + + if (streamSetId != camera3::CAMERA3_STREAM_ID_INVALID) { + ALOGE("%s: Surface groups not supported in case of composite streams!", + __FUNCTION__); + return BAD_VALUE; + } + + if (isShared) { + ALOGE("%s: Shared surfaces not supported in case of composite streams!", + __FUNCTION__); + return BAD_VALUE; + } + + return createInternalStreams(consumers, hasDeferredConsumer, width, height, format, rotation, id, + physicalCameraId, surfaceIds, streamSetId, isShared); +} + +status_t CompositeStream::deleteStream() { + { + Mutex::Autolock l(mMutex); + mPendingCaptureResults.clear(); + mCaptureResults.clear(); + mFrameNumberMap.clear(); + mErrorFrameNumbers.clear(); + } + + return deleteInternalStreams(); +} + +void CompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) { + Mutex::Autolock l(mMutex); + if (!mErrorState && (streamId == getStreamId())) { + mPendingCaptureResults.emplace(frameNumber, CameraMetadata()); + } +} + +void CompositeStream::onBufferReleased(const BufferInfo& bufferInfo) { + Mutex::Autolock l(mMutex); + if (!mErrorState && !bufferInfo.mError) { + mFrameNumberMap.emplace(bufferInfo.mFrameNumber, bufferInfo.mTimestamp); + mInputReadyCondition.signal(); + } +} + +void CompositeStream::eraseResult(int64_t frameNumber) { + Mutex::Autolock l(mMutex); + + auto it = mPendingCaptureResults.find(frameNumber); + if (it == mPendingCaptureResults.end()) { + return; + } + + it = mPendingCaptureResults.erase(it); +} + +void CompositeStream::onResultAvailable(const CaptureResult& result) { + bool resultError = false; + { + Mutex::Autolock l(mMutex); + + uint64_t frameNumber = result.mResultExtras.frameNumber; + bool resultReady = false; + auto it = mPendingCaptureResults.find(frameNumber); + if (it != mPendingCaptureResults.end()) { + it->second.append(result.mMetadata); + if (result.mResultExtras.partialResultCount >= mNumPartialResults) { + auto entry = it->second.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count == 1) { + auto ts = entry.data.i64[0]; + mCaptureResults.emplace(ts, std::make_tuple(frameNumber, it->second)); + resultReady = true; + } else { + ALOGE("%s: Timestamp metadata entry missing for frameNumber: %" PRIu64, + __FUNCTION__, frameNumber); + resultError = true; + } + mPendingCaptureResults.erase(it); + } + } + + if (resultReady) { + mInputReadyCondition.signal(); + } + } + + if (resultError) { + onResultError(result.mResultExtras); + } +} + +void CompositeStream::flagAnErrorFrameNumber(int64_t frameNumber) { + Mutex::Autolock l(mMutex); + mErrorFrameNumbers.emplace(frameNumber); + mInputReadyCondition.signal(); +} + +status_t CompositeStream::registerCompositeStreamListener(int32_t streamId) { + sp device = mDevice.promote(); + if (device.get() == nullptr) { + return NO_INIT; + } + + auto ret = device->addBufferListenerForStream(streamId, this); + if (ret != OK) { + ALOGE("%s: Failed to register composite stream listener!", __FUNCTION__); + } + + return ret; +} + +bool CompositeStream::onError(int32_t errorCode, const CaptureResultExtras& resultExtras) { + auto ret = false; + switch (errorCode) { + case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT: + onResultError(resultExtras); + break; + case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER: + ret = onStreamBufferError(resultExtras); + break; + case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST: + // Invalid request, this shouldn't affect composite streams. + break; + default: + ALOGE("%s: Unrecoverable error: %d detected!", __FUNCTION__, errorCode); + Mutex::Autolock l(mMutex); + mErrorState = true; + break; + } + + return ret; +} + +void CompositeStream::notifyError(int64_t frameNumber) { + sp remoteCb = + mRemoteCallback.promote(); + + if ((frameNumber >= 0) && (remoteCb.get() != nullptr)) { + CaptureResultExtras extras; + extras.errorStreamId = getStreamId(); + extras.frameNumber = frameNumber; + remoteCb->onDeviceError( + hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER, + extras); + } +} + +}; // namespace camera3 +}; // namespace android diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h new file mode 100644 index 0000000000..583774550b --- /dev/null +++ b/services/camera/libcameraservice/api2/CompositeStream.h @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_COMPOSITE_STREAM_H +#define ANDROID_SERVERS_CAMERA_CAMERA3_COMPOSITE_STREAM_H + +#include +#include + +#include +#include +#include +#include "common/CameraDeviceBase.h" +#include "device3/Camera3StreamInterface.h" + +namespace android { + +class CameraDeviceClient; +class CameraMetadata; +class Surface; + +namespace camera3 { + +class CompositeStream : public camera3::Camera3StreamBufferListener { + +public: + CompositeStream(wp device, wp cb); + virtual ~CompositeStream() {} + + status_t createStream(const std::vector>& consumers, + bool hasDeferredConsumer, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int streamSetId, bool isShared); + + status_t deleteStream(); + + // Create and register all internal camera streams. + virtual status_t createInternalStreams(const std::vector>& consumers, + bool hasDeferredConsumer, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int streamSetId, bool isShared) = 0; + + // Release all internal streams and corresponding resources. + virtual status_t deleteInternalStreams() = 0; + + // Stream configuration completed. + virtual status_t configureStream() = 0; + + // Insert the internal composite stream id in the user capture request. + virtual status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, + Vector* /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) = 0; + + // Return composite stream id. + virtual int getStreamId() = 0; + + void onResultAvailable(const CaptureResult& result); + bool onError(int32_t errorCode, const CaptureResultExtras& resultExtras); + + // Camera3StreamBufferListener implementation + void onBufferAcquired(const BufferInfo& /*bufferInfo*/) override { /*Empty for now */ } + void onBufferReleased(const BufferInfo& bufferInfo) override; + void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override; + +protected: + status_t registerCompositeStreamListener(int32_t streamId); + void eraseResult(int64_t frameNumber); + void flagAnErrorFrameNumber(int64_t frameNumber); + void notifyError(int64_t frameNumber); + + // Subclasses should check for buffer errors from internal streams and return 'true' in + // case the error notification should remain within camera service. + virtual bool onStreamBufferError(const CaptureResultExtras& resultExtras) = 0; + + // Subclasses can decide how to handle result errors depending on whether or not the + // internal processing needs result data. + virtual void onResultError(const CaptureResultExtras& resultExtras) = 0; + + // Device and/or service is in unrecoverable error state. + // Composite streams should behave accordingly. + void enableErrorState(); + + wp mDevice; + wp mRemoteCallback; + + mutable Mutex mMutex; + Condition mInputReadyCondition; + int32_t mNumPartialResults; + bool mErrorState; + + // Frame number to capture result map of partial pending request results. + std::unordered_map mPendingCaptureResults; + + // Timestamp to capture (frame number, result) map of completed pending request results. + std::unordered_map> mCaptureResults; + + // Frame number to timestamp map + std::unordered_map mFrameNumberMap; + + // Keeps a set buffer/result frame numbers for any errors detected during processing. + std::set mErrorFrameNumbers; + +}; + +}; //namespace camera3 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp new file mode 100644 index 0000000000..b12bb50f09 --- /dev/null +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -0,0 +1,1026 @@ +/* + * 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 "Camera3-DepthCompositeStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include "api1/client2/JpegProcessor.h" +#include "common/CameraProviderManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "DepthCompositeStream.h" + +using dynamic_depth::Camera; +using dynamic_depth::Cameras; +using dynamic_depth::CameraParams; +using dynamic_depth::Container; +using dynamic_depth::DepthFormat; +using dynamic_depth::DepthMapParams; +using dynamic_depth::DepthUnits; +using dynamic_depth::Device; +using dynamic_depth::DeviceParams; +using dynamic_depth::Dimension; +using dynamic_depth::Image; +using dynamic_depth::ImagingModelParams; +using dynamic_depth::Pose; +using dynamic_depth::Profile; +using dynamic_depth::Profiles; + +namespace android { +namespace camera3 { + +DepthCompositeStream::DepthCompositeStream(wp device, + wp cb) : + CompositeStream(device, cb), + mBlobStreamId(-1), + mBlobSurfaceId(-1), + mDepthStreamId(-1), + mDepthSurfaceId(-1), + mBlobWidth(0), + mBlobHeight(0), + mDepthBufferAcquired(false), + mBlobBufferAcquired(false), + mProducerListener(new ProducerListener()), + mMaxJpegSize(-1), + mIsLogicalCamera(false) { + sp cameraDevice = device.promote(); + if (cameraDevice.get() != nullptr) { + CameraMetadata staticInfo = cameraDevice->info(); + auto entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE); + if (entry.count > 0) { + mMaxJpegSize = entry.data.i32[0]; + } else { + ALOGW("%s: Maximum jpeg size absent from camera characteristics", __FUNCTION__); + } + + entry = staticInfo.find(ANDROID_LENS_INTRINSIC_CALIBRATION); + if (entry.count == 5) { + mInstrinsicCalibration.reserve(5); + mInstrinsicCalibration.insert(mInstrinsicCalibration.end(), entry.data.f, + entry.data.f + 5); + } else { + ALOGW("%s: Intrinsic calibration absent from camera characteristics!", __FUNCTION__); + } + + entry = staticInfo.find(ANDROID_LENS_DISTORTION); + if (entry.count == 5) { + mLensDistortion.reserve(5); + mLensDistortion.insert(mLensDistortion.end(), entry.data.f, entry.data.f + 5); + } else { + ALOGW("%s: Lens distortion absent from camera characteristics!", __FUNCTION__); + } + + entry = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES); + for (size_t i = 0; i < entry.count; ++i) { + uint8_t capability = entry.data.u8[i]; + if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) { + mIsLogicalCamera = true; + break; + } + } + + getSupportedDepthSizes(staticInfo, &mSupportedDepthSizes); + } +} + +DepthCompositeStream::~DepthCompositeStream() { + mBlobConsumer.clear(), + mBlobSurface.clear(), + mBlobStreamId = -1; + mBlobSurfaceId = -1; + mDepthConsumer.clear(); + mDepthSurface.clear(); + mDepthConsumer = nullptr; + mDepthSurface = nullptr; +} + +void DepthCompositeStream::compilePendingInputLocked() { + CpuConsumer::LockedBuffer imgBuffer; + + while (!mInputJpegBuffers.empty() && !mBlobBufferAcquired) { + auto it = mInputJpegBuffers.begin(); + auto res = mBlobConsumer->lockNextBuffer(&imgBuffer); + if (res == NOT_ENOUGH_DATA) { + // Can not lock any more buffers. + break; + } else if (res != OK) { + ALOGE("%s: Error locking blob image buffer: %s (%d)", __FUNCTION__, + strerror(-res), res); + mPendingInputFrames[*it].error = true; + mInputDepthBuffers.erase(it); + continue; + } + + if (*it != imgBuffer.timestamp) { + ALOGW("%s: Expecting jpeg buffer with time stamp: %" PRId64 " received buffer with " + "time stamp: %" PRId64, __FUNCTION__, *it, imgBuffer.timestamp); + } + + if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) && + (mPendingInputFrames[imgBuffer.timestamp].error)) { + mBlobConsumer->unlockBuffer(imgBuffer); + } else { + mPendingInputFrames[imgBuffer.timestamp].jpegBuffer = imgBuffer; + mBlobBufferAcquired = true; + } + mInputJpegBuffers.erase(it); + } + + while (!mInputDepthBuffers.empty() && !mDepthBufferAcquired) { + auto it = mInputDepthBuffers.begin(); + auto res = mDepthConsumer->lockNextBuffer(&imgBuffer); + if (res == NOT_ENOUGH_DATA) { + // Can not lock any more buffers. + break; + } else if (res != OK) { + ALOGE("%s: Error receiving depth image buffer: %s (%d)", __FUNCTION__, + strerror(-res), res); + mPendingInputFrames[*it].error = true; + mInputDepthBuffers.erase(it); + continue; + } + + if (*it != imgBuffer.timestamp) { + ALOGW("%s: Expecting depth buffer with time stamp: %" PRId64 " received buffer with " + "time stamp: %" PRId64, __FUNCTION__, *it, imgBuffer.timestamp); + } + + if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) && + (mPendingInputFrames[imgBuffer.timestamp].error)) { + mDepthConsumer->unlockBuffer(imgBuffer); + } else { + mPendingInputFrames[imgBuffer.timestamp].depthBuffer = imgBuffer; + mDepthBufferAcquired = true; + } + mInputDepthBuffers.erase(it); + } + + while (!mCaptureResults.empty()) { + auto it = mCaptureResults.begin(); + // Negative timestamp indicates that something went wrong during the capture result + // collection process. + if (it->first >= 0) { + mPendingInputFrames[it->first].frameNumber = std::get<0>(it->second); + mPendingInputFrames[it->first].result = std::get<1>(it->second); + } + mCaptureResults.erase(it); + } + + while (!mFrameNumberMap.empty()) { + auto it = mFrameNumberMap.begin(); + mPendingInputFrames[it->second].frameNumber = it->first; + mFrameNumberMap.erase(it); + } + + auto it = mErrorFrameNumbers.begin(); + while (it != mErrorFrameNumbers.end()) { + bool frameFound = false; + for (auto &inputFrame : mPendingInputFrames) { + if (inputFrame.second.frameNumber == *it) { + inputFrame.second.error = true; + frameFound = true; + break; + } + } + + if (frameFound) { + it = mErrorFrameNumbers.erase(it); + } else { + ALOGW("%s: Not able to find failing input with frame number: %" PRId64, __FUNCTION__, + *it); + it++; + } + } +} + +bool DepthCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*inout*/) { + if (currentTs == nullptr) { + return false; + } + + bool newInputAvailable = false; + for (const auto& it : mPendingInputFrames) { + if ((!it.second.error) && (it.second.depthBuffer.data != nullptr) && + (it.second.jpegBuffer.data != nullptr) && (it.first < *currentTs)) { + *currentTs = it.first; + newInputAvailable = true; + } + } + + return newInputAvailable; +} + +int64_t DepthCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*inout*/) { + int64_t ret = -1; + if (currentTs == nullptr) { + return ret; + } + + for (const auto& it : mPendingInputFrames) { + if (it.second.error && !it.second.errorNotified && (it.first < *currentTs)) { + *currentTs = it.first; + ret = it.second.frameNumber; + } + } + + return ret; +} + +status_t DepthCompositeStream::encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, + void *out, const size_t maxOutSize, uint8_t jpegQuality, size_t &actualSize) { + status_t ret; + // libjpeg is a C library so we use C-style "inheritance" by + // putting libjpeg's jpeg_destination_mgr first in our custom + // struct. This allows us to cast jpeg_destination_mgr* to + // CustomJpegDestMgr* when we get it passed to us in a callback. + struct CustomJpegDestMgr : public jpeg_destination_mgr { + JOCTET *mBuffer; + size_t mBufferSize; + size_t mEncodedSize; + bool mSuccess; + } dmgr; + + jpeg_compress_struct cinfo = {}; + jpeg_error_mgr jerr; + + // Initialize error handling with standard callbacks, but + // then override output_message (to print to ALOG) and + // error_exit to set a flag and print a message instead + // of killing the whole process. + cinfo.err = jpeg_std_error(&jerr); + + cinfo.err->output_message = [](j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message)(cinfo, buffer); + ALOGE("libjpeg error: %s", buffer); + }; + + cinfo.err->error_exit = [](j_common_ptr cinfo) { + (*cinfo->err->output_message)(cinfo); + if(cinfo->client_data) { + auto & dmgr = *static_cast(cinfo->client_data); + dmgr.mSuccess = false; + } + }; + + // Now that we initialized some callbacks, let's create our compressor + jpeg_create_compress(&cinfo); + dmgr.mBuffer = static_cast(out); + dmgr.mBufferSize = maxOutSize; + dmgr.mEncodedSize = 0; + dmgr.mSuccess = true; + cinfo.client_data = static_cast(&dmgr); + + // These lambdas become C-style function pointers and as per C++11 spec + // may not capture anything. + dmgr.init_destination = [](j_compress_ptr cinfo) { + auto & dmgr = static_cast(*cinfo->dest); + dmgr.next_output_byte = dmgr.mBuffer; + dmgr.free_in_buffer = dmgr.mBufferSize; + ALOGV("%s:%d jpeg start: %p [%zu]", + __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize); + }; + + dmgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) { + ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__); + return 0; + }; + + dmgr.term_destination = [](j_compress_ptr cinfo) { + auto & dmgr = static_cast(*cinfo->dest); + dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer; + ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); + }; + cinfo.dest = reinterpret_cast(&dmgr); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + + // Initialize defaults and then override what we want + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, jpegQuality, 1); + jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); + cinfo.raw_data_in = 0; + cinfo.dct_method = JDCT_IFAST; + + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, TRUE); + + for (size_t i = 0; i < cinfo.image_height; i++) { + auto currentRow = static_cast(in + i*width); + jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1); + } + + jpeg_finish_compress(&cinfo); + + actualSize = dmgr.mEncodedSize; + if (dmgr.mSuccess) { + ret = NO_ERROR; + } else { + ret = UNKNOWN_ERROR; + } + + return ret; +} + +std::unique_ptr DepthCompositeStream::processDepthMapFrame( + const CpuConsumer::LockedBuffer &depthMapBuffer, size_t maxJpegSize, uint8_t jpegQuality, + std::vector> *items /*out*/) { + std::vector points, confidence; + + size_t pointCount = depthMapBuffer.width * depthMapBuffer.height; + points.reserve(pointCount); + confidence.reserve(pointCount); + float near = UINT16_MAX; + float far = .0f; + uint16_t *data = reinterpret_cast (depthMapBuffer.data); + for (size_t i = 0; i < depthMapBuffer.height; i++) { + for (size_t j = 0; j < depthMapBuffer.width; j++) { + // Android densely packed depth map. The units for the range are in + // millimeters and need to be scaled to meters. + // The confidence value is encoded in the 3 most significant bits. + // The confidence data needs to be additionally normalized with + // values 1.0f, 0.0f representing maximum and minimum confidence + // respectively. + auto value = data[i*depthMapBuffer.stride + j]; + auto point = static_cast(value & 0x1FFF) / 1000.f; + points.push_back(point); + + auto conf = (value >> 13) & 0x7; + float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; + confidence.push_back(normConfidence); + + if (near > point) { + near = point; + } + if (far < point) { + far = point; + } + } + } + + if (near == far) { + ALOGE("%s: Near and far range values must not match!", __FUNCTION__); + return nullptr; + } + + std::vector pointsQuantized, confidenceQuantized; + pointsQuantized.reserve(pointCount); confidenceQuantized.reserve(pointCount); + auto pointIt = points.begin(); + auto confidenceIt = confidence.begin(); + while ((pointIt != points.end()) && (confidenceIt != confidence.end())) { + pointsQuantized.push_back(floorf(((far * (*pointIt - near)) / + (*pointIt * (far - near))) * 255.0f)); + confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f)); + confidenceIt++; pointIt++; + } + + DepthMapParams depthParams(DepthFormat::kRangeInverse, near, far, DepthUnits::kMeters, + "android/depthmap"); + depthParams.confidence_uri = "android/confidencemap"; + depthParams.mime = "image/jpeg"; + depthParams.depth_image_data.resize(maxJpegSize); + depthParams.confidence_data.resize(maxJpegSize); + size_t actualJpegSize; + auto ret = encodeGrayscaleJpeg(depthMapBuffer.width, depthMapBuffer.height, + pointsQuantized.data(), depthParams.depth_image_data.data(), maxJpegSize, jpegQuality, + actualJpegSize); + if (ret != NO_ERROR) { + ALOGE("%s: Depth map compression failed!", __FUNCTION__); + return nullptr; + } + depthParams.depth_image_data.resize(actualJpegSize); + + ret = encodeGrayscaleJpeg(depthMapBuffer.width, depthMapBuffer.height, + confidenceQuantized.data(), depthParams.confidence_data.data(), maxJpegSize, + jpegQuality, actualJpegSize); + if (ret != NO_ERROR) { + ALOGE("%s: Confidence map compression failed!", __FUNCTION__); + return nullptr; + } + depthParams.confidence_data.resize(actualJpegSize); + + return DepthMap::FromData(depthParams, items); +} + +status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { + status_t res; + sp outputANW = mOutputSurface; + ANativeWindowBuffer *anb; + int fenceFd; + void *dstBuffer; + auto imgBuffer = inputFrame.jpegBuffer; + + auto jpegSize = android::camera2::JpegProcessor::findJpegSize(inputFrame.jpegBuffer.data, + inputFrame.jpegBuffer.width); + if (jpegSize == 0) { + ALOGW("%s: Failed to find input jpeg size, default to using entire buffer!", __FUNCTION__); + jpegSize = inputFrame.jpegBuffer.width; + } + + std::vector> items; + std::vector> cameraList; + auto image = Image::FromDataForPrimaryImage("android/mainimage", &items); + std::unique_ptr cameraParams(new CameraParams(std::move(image))); + if (cameraParams == nullptr) { + ALOGE("%s: Failed to initialize camera parameters", __FUNCTION__); + return BAD_VALUE; + } + + size_t maxDepthJpegSize; + if (mMaxJpegSize > 0) { + maxDepthJpegSize = mMaxJpegSize; + } else { + maxDepthJpegSize = std::max (jpegSize, + inputFrame.depthBuffer.width * inputFrame.depthBuffer.height * 3 / 2); + } + uint8_t jpegQuality = 100; + auto entry = inputFrame.result.find(ANDROID_JPEG_QUALITY); + if (entry.count > 0) { + jpegQuality = entry.data.u8[0]; + } + cameraParams->depth_map = processDepthMapFrame(inputFrame.depthBuffer, maxDepthJpegSize, + jpegQuality, &items); + if (cameraParams->depth_map == nullptr) { + ALOGE("%s: Depth map processing failed!", __FUNCTION__); + return BAD_VALUE; + } + cameraParams->imaging_model = getImagingModel(); + + if (mIsLogicalCamera) { + cameraParams->trait = dynamic_depth::CameraTrait::LOGICAL; + } else { + cameraParams->trait = dynamic_depth::CameraTrait::PHYSICAL; + } + + cameraList.emplace_back(Camera::FromData(std::move(cameraParams))); + + auto deviceParams = std::make_unique (Cameras::FromCameraArray(&cameraList)); + deviceParams->container = Container::FromItems(&items); + std::vector> profileList; + profileList.emplace_back(Profile::FromData("DepthPhoto", {0})); + deviceParams->profiles = Profiles::FromProfileArray(&profileList); + std::unique_ptr device = Device::FromData(std::move(deviceParams)); + if (device == nullptr) { + ALOGE("%s: Failed to initialize camera device", __FUNCTION__); + return BAD_VALUE; + } + + std::istringstream inputJpegStream(std::string(reinterpret_cast (imgBuffer.data), + jpegSize)); + std::ostringstream outputJpegStream; + if (!WriteImageAndMetadataAndContainer(&inputJpegStream, device.get(), &outputJpegStream)) { + ALOGE("%s: Failed writing depth output", __FUNCTION__); + return BAD_VALUE; + } + + size_t finalJpegSize = static_cast (outputJpegStream.tellp()) + + sizeof(struct camera3_jpeg_blob); + + ALOGV("%s: Final jpeg size: %zu", __func__, finalJpegSize); + if ((res = native_window_set_buffers_dimensions(mOutputSurface.get(), finalJpegSize, 1)) + != OK) { + ALOGE("%s: Unable to configure stream buffer dimensions" + " %zux%u for stream %d", __FUNCTION__, finalJpegSize, 1U, mBlobStreamId); + return res; + } + + res = outputANW->dequeueBuffer(mOutputSurface.get(), &anb, &fenceFd); + if (res != OK) { + ALOGE("%s: Error retrieving output buffer: %s (%d)", __FUNCTION__, strerror(-res), + res); + return res; + } + + sp gb = GraphicBuffer::from(anb); + res = gb->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &dstBuffer, fenceFd); + if (res != OK) { + ALOGE("%s: Error trying to lock output buffer fence: %s (%d)", __FUNCTION__, + strerror(-res), res); + outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1); + return res; + } + + if ((gb->getWidth() < finalJpegSize) || (gb->getHeight() != 1)) { + ALOGE("%s: Blob buffer size mismatch, expected %dx%d received %zux%u", __FUNCTION__, + gb->getWidth(), gb->getHeight(), finalJpegSize, 1U); + outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1); + return BAD_VALUE; + } + + // Copy final jpeg with embedded depth data in the composite stream output buffer + uint8_t* header = static_cast (dstBuffer) + + (gb->getWidth() - sizeof(struct camera3_jpeg_blob)); + struct camera3_jpeg_blob *blob = reinterpret_cast (header); + blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID; + blob->jpeg_size = static_cast (outputJpegStream.tellp()); + memcpy(dstBuffer, outputJpegStream.str().c_str(), blob->jpeg_size); + outputANW->queueBuffer(mOutputSurface.get(), anb, /*fence*/ -1); + + return res; +} + +void DepthCompositeStream::releaseInputFrameLocked(InputFrame *inputFrame /*out*/) { + if (inputFrame == nullptr) { + return; + } + + if (inputFrame->depthBuffer.data != nullptr) { + mDepthConsumer->unlockBuffer(inputFrame->depthBuffer); + inputFrame->depthBuffer.data = nullptr; + mDepthBufferAcquired = false; + } + + if (inputFrame->jpegBuffer.data != nullptr) { + mBlobConsumer->unlockBuffer(inputFrame->jpegBuffer); + inputFrame->jpegBuffer.data = nullptr; + mBlobBufferAcquired = false; + } + + if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) { + notifyError(inputFrame->frameNumber); + inputFrame->errorNotified = true; + } +} + +void DepthCompositeStream::releaseInputFramesLocked(int64_t currentTs) { + auto it = mPendingInputFrames.begin(); + while (it != mPendingInputFrames.end()) { + if (it->first <= currentTs) { + releaseInputFrameLocked(&it->second); + it = mPendingInputFrames.erase(it); + } else { + it++; + } + } +} + +bool DepthCompositeStream::threadLoop() { + int64_t currentTs = INT64_MAX; + bool newInputAvailable = false; + + { + Mutex::Autolock l(mMutex); + + if (mErrorState) { + // In case we landed in error state, return any pending buffers and + // halt all further processing. + compilePendingInputLocked(); + releaseInputFramesLocked(currentTs); + return false; + } + + while (!newInputAvailable) { + compilePendingInputLocked(); + newInputAvailable = getNextReadyInputLocked(¤tTs); + if (!newInputAvailable) { + auto failingFrameNumber = getNextFailingInputLocked(¤tTs); + if (failingFrameNumber >= 0) { + // We cannot erase 'mPendingInputFrames[currentTs]' at this point because it is + // possible for two internal stream buffers to fail. In such scenario the + // composite stream should notify the client about a stream buffer error only + // once and this information is kept within 'errorNotified'. + // Any present failed input frames will be removed on a subsequent call to + // 'releaseInputFramesLocked()'. + releaseInputFrameLocked(&mPendingInputFrames[currentTs]); + currentTs = INT64_MAX; + } + + auto ret = mInputReadyCondition.waitRelative(mMutex, kWaitDuration); + if (ret == TIMED_OUT) { + return true; + } else if (ret != OK) { + ALOGE("%s: Timed wait on condition failed: %s (%d)", __FUNCTION__, + strerror(-ret), ret); + return false; + } + } + } + } + + auto res = processInputFrame(mPendingInputFrames[currentTs]); + Mutex::Autolock l(mMutex); + if (res != OK) { + ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)", __FUNCTION__, + currentTs, strerror(-res), res); + mPendingInputFrames[currentTs].error = true; + } + + releaseInputFramesLocked(currentTs); + + return true; +} + +bool DepthCompositeStream::isDepthCompositeStream(const sp &surface) { + ANativeWindow *anw = surface.get(); + status_t err; + int format; + if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) { + String8 msg = String8::format("Failed to query Surface format: %s (%d)", strerror(-err), + err); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return false; + } + + int dataspace; + if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE, &dataspace)) != OK) { + String8 msg = String8::format("Failed to query Surface dataspace: %s (%d)", strerror(-err), + err); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return false; + } + + if ((format == HAL_PIXEL_FORMAT_BLOB) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH)) { + return true; + } + + return false; +} + +status_t DepthCompositeStream::createInternalStreams(const std::vector>& consumers, + bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int /*streamSetId*/, bool /*isShared*/) { + if (mSupportedDepthSizes.empty()) { + ALOGE("%s: This camera device doesn't support any depth map streams!", __FUNCTION__); + return INVALID_OPERATION; + } + + size_t depthWidth, depthHeight; + auto ret = getMatchingDepthSize(width, height, mSupportedDepthSizes, &depthWidth, &depthHeight); + if (ret != OK) { + ALOGE("%s: Failed to find an appropriate depth stream size!", __FUNCTION__); + return ret; + } + + sp device = mDevice.promote(); + if (!device.get()) { + ALOGE("%s: Invalid camera device!", __FUNCTION__); + return NO_INIT; + } + + sp producer; + sp consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mBlobConsumer = new CpuConsumer(consumer, /*maxLockedBuffers*/1, /*controlledByApp*/ true); + mBlobConsumer->setFrameAvailableListener(this); + mBlobConsumer->setName(String8("Camera3-JpegCompositeStream")); + mBlobSurface = new Surface(producer); + + ret = device->createStream(mBlobSurface, width, height, format, kJpegDataSpace, rotation, + id, physicalCameraId, surfaceIds); + if (ret == OK) { + mBlobStreamId = *id; + mBlobSurfaceId = (*surfaceIds)[0]; + mOutputSurface = consumers[0]; + } else { + return ret; + } + + BufferQueue::createBufferQueue(&producer, &consumer); + mDepthConsumer = new CpuConsumer(consumer, /*maxLockedBuffers*/ 1, /*controlledByApp*/ true); + mDepthConsumer->setFrameAvailableListener(this); + mDepthConsumer->setName(String8("Camera3-DepthCompositeStream")); + mDepthSurface = new Surface(producer); + std::vector depthSurfaceId; + ret = device->createStream(mDepthSurface, depthWidth, depthHeight, kDepthMapPixelFormat, + kDepthMapDataSpace, rotation, &mDepthStreamId, physicalCameraId, &depthSurfaceId); + if (ret == OK) { + mDepthSurfaceId = depthSurfaceId[0]; + } else { + return ret; + } + + ret = registerCompositeStreamListener(getStreamId()); + if (ret != OK) { + ALOGE("%s: Failed to register blob stream listener!", __FUNCTION__); + return ret; + } + + ret = registerCompositeStreamListener(mDepthStreamId); + if (ret != OK) { + ALOGE("%s: Failed to register depth stream listener!", __FUNCTION__); + return ret; + } + + mBlobWidth = width; + mBlobHeight = height; + + return ret; +} + +status_t DepthCompositeStream::configureStream() { + if (isRunning()) { + // Processing thread is already running, nothing more to do. + return NO_ERROR; + } + + if (mOutputSurface.get() == nullptr) { + ALOGE("%s: No valid output surface set!", __FUNCTION__); + return NO_INIT; + } + + auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener); + if (res != OK) { + ALOGE("%s: Unable to connect to native window for stream %d", + __FUNCTION__, mBlobStreamId); + return res; + } + + if ((res = native_window_set_buffers_format(mOutputSurface.get(), HAL_PIXEL_FORMAT_BLOB)) + != OK) { + ALOGE("%s: Unable to configure stream buffer format for stream %d", __FUNCTION__, + mBlobStreamId); + return res; + } + + int maxProducerBuffers; + ANativeWindow *anw = mBlobSurface.get(); + if ((res = anw->query(anw, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxProducerBuffers)) != OK) { + ALOGE("%s: Unable to query consumer undequeued" + " buffer count for stream %d", __FUNCTION__, mBlobStreamId); + return res; + } + + ANativeWindow *anwConsumer = mOutputSurface.get(); + int maxConsumerBuffers; + if ((res = anwConsumer->query(anwConsumer, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &maxConsumerBuffers)) != OK) { + ALOGE("%s: Unable to query consumer undequeued" + " buffer count for stream %d", __FUNCTION__, mBlobStreamId); + return res; + } + + if ((res = native_window_set_buffer_count( + anwConsumer, maxProducerBuffers + maxConsumerBuffers)) != OK) { + ALOGE("%s: Unable to set buffer count for stream %d", __FUNCTION__, mBlobStreamId); + return res; + } + + run("DepthCompositeStreamProc"); + + return NO_ERROR; +} + +status_t DepthCompositeStream::deleteInternalStreams() { + // The 'CameraDeviceClient' parent will delete the blob stream + requestExit(); + + auto ret = join(); + if (ret != OK) { + ALOGE("%s: Failed to join with the main processing thread: %s (%d)", __FUNCTION__, + strerror(-ret), ret); + } + + sp device = mDevice.promote(); + if (!device.get()) { + ALOGE("%s: Invalid camera device!", __FUNCTION__); + return NO_INIT; + } + + if (mDepthStreamId >= 0) { + ret = device->deleteStream(mDepthStreamId); + mDepthStreamId = -1; + } + + return ret; +} + +void DepthCompositeStream::onFrameAvailable(const BufferItem& item) { + if (item.mDataSpace == kJpegDataSpace) { + ALOGV("%s: Jpeg buffer with ts: %" PRIu64 " ms. arrived!", + __func__, ns2ms(item.mTimestamp)); + + Mutex::Autolock l(mMutex); + if (!mErrorState) { + mInputJpegBuffers.push_back(item.mTimestamp); + mInputReadyCondition.signal(); + } + } else if (item.mDataSpace == kDepthMapDataSpace) { + ALOGV("%s: Depth buffer with ts: %" PRIu64 " ms. arrived!", __func__, + ns2ms(item.mTimestamp)); + + Mutex::Autolock l(mMutex); + if (!mErrorState) { + mInputDepthBuffers.push_back(item.mTimestamp); + mInputReadyCondition.signal(); + } + } else { + ALOGE("%s: Unexpected data space: 0x%x", __FUNCTION__, item.mDataSpace); + } +} + +status_t DepthCompositeStream::insertGbp(SurfaceMap* /*out*/outSurfaceMap, + Vector * /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) { + if (outSurfaceMap->find(mDepthStreamId) == outSurfaceMap->end()) { + (*outSurfaceMap)[mDepthStreamId] = std::vector(); + outputStreamIds->push_back(mDepthStreamId); + } + (*outSurfaceMap)[mDepthStreamId].push_back(mDepthSurfaceId); + + if (outSurfaceMap->find(mBlobStreamId) == outSurfaceMap->end()) { + (*outSurfaceMap)[mBlobStreamId] = std::vector(); + outputStreamIds->push_back(mBlobStreamId); + } + (*outSurfaceMap)[mBlobStreamId].push_back(mBlobSurfaceId); + + if (currentStreamId != nullptr) { + *currentStreamId = mBlobStreamId; + } + + return NO_ERROR; +} + +void DepthCompositeStream::onResultError(const CaptureResultExtras& resultExtras) { + // Processing can continue even in case of result errors. + // At the moment depth composite stream processing relies mainly on static camera + // characteristics data. The actual result data can be used for the jpeg quality but + // in case it is absent we can default to maximum. + eraseResult(resultExtras.frameNumber); +} + +bool DepthCompositeStream::onStreamBufferError(const CaptureResultExtras& resultExtras) { + bool ret = false; + // Buffer errors concerning internal composite streams should not be directly visible to + // camera clients. They must only receive a single buffer error with the public composite + // stream id. + if ((resultExtras.errorStreamId == mDepthStreamId) || + (resultExtras.errorStreamId == mBlobStreamId)) { + flagAnErrorFrameNumber(resultExtras.frameNumber); + ret = true; + } + + return ret; +} + +status_t DepthCompositeStream::getMatchingDepthSize(size_t width, size_t height, + const std::vector>& supporedDepthSizes, + size_t *depthWidth /*out*/, size_t *depthHeight /*out*/) { + if ((depthWidth == nullptr) || (depthHeight == nullptr)) { + return BAD_VALUE; + } + + float arTol = CameraProviderManager::kDepthARTolerance; + *depthWidth = *depthHeight = 0; + + float aspectRatio = static_cast (width) / static_cast (height); + for (const auto& it : supporedDepthSizes) { + auto currentWidth = std::get<0>(it); + auto currentHeight = std::get<1>(it); + if ((currentWidth == width) && (currentHeight == height)) { + *depthWidth = width; + *depthHeight = height; + break; + } else { + float currentRatio = static_cast (currentWidth) / + static_cast (currentHeight); + auto currentSize = currentWidth * currentHeight; + auto oldSize = (*depthWidth) * (*depthHeight); + if ((fabs(aspectRatio - currentRatio) <= arTol) && (currentSize > oldSize)) { + *depthWidth = currentWidth; + *depthHeight = currentHeight; + } + } + } + + return ((*depthWidth > 0) && (*depthHeight > 0)) ? OK : BAD_VALUE; +} + +void DepthCompositeStream::getSupportedDepthSizes(const CameraMetadata& ch, + std::vector>* depthSizes /*out*/) { + if (depthSizes == nullptr) { + return; + } + + auto entry = ch.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS); + if (entry.count > 0) { + // Depth stream dimensions have four int32_t components + // (pixelformat, width, height, type) + size_t entryCount = entry.count / 4; + depthSizes->reserve(entryCount); + for (size_t i = 0; i < entry.count; i += 4) { + if ((entry.data.i32[i] == kDepthMapPixelFormat) && + (entry.data.i32[i+3] == + ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT)) { + depthSizes->push_back(std::make_tuple(entry.data.i32[i+1], + entry.data.i32[i+2])); + } + } + } +} + +status_t DepthCompositeStream::getCompositeStreamInfo(const OutputStreamInfo &streamInfo, + const CameraMetadata& ch, std::vector* compositeOutput /*out*/) { + if (compositeOutput == nullptr) { + return BAD_VALUE; + } + + std::vector> depthSizes; + getSupportedDepthSizes(ch, &depthSizes); + if (depthSizes.empty()) { + ALOGE("%s: No depth stream configurations present", __FUNCTION__); + return BAD_VALUE; + } + + size_t depthWidth, depthHeight; + auto ret = getMatchingDepthSize(streamInfo.width, streamInfo.height, depthSizes, &depthWidth, + &depthHeight); + if (ret != OK) { + ALOGE("%s: No matching depth stream size found", __FUNCTION__); + return ret; + } + + compositeOutput->clear(); + compositeOutput->insert(compositeOutput->end(), 2, streamInfo); + + // Jpeg/Blob stream info + (*compositeOutput)[0].dataSpace = kJpegDataSpace; + (*compositeOutput)[0].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN; + + // Depth stream info + (*compositeOutput)[1].width = depthWidth; + (*compositeOutput)[1].height = depthHeight; + (*compositeOutput)[1].format = kDepthMapPixelFormat; + (*compositeOutput)[1].dataSpace = kDepthMapDataSpace; + (*compositeOutput)[1].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN; + + return NO_ERROR; +} + +std::unique_ptr DepthCompositeStream::getImagingModel() { + // It is not possible to generate an imaging model without instrinsic calibration. + if (mInstrinsicCalibration.empty() || mInstrinsicCalibration.size() != 5) { + return nullptr; + } + + // The camera intrinsic calibration layout is as follows: + // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] + const dynamic_depth::Point focalLength(mInstrinsicCalibration[0], + mInstrinsicCalibration[1]); + const Dimension imageSize(mBlobWidth, mBlobHeight); + ImagingModelParams params(focalLength, imageSize); + params.principal_point.x = mInstrinsicCalibration[2]; + params.principal_point.y = mInstrinsicCalibration[3]; + params.skew = mInstrinsicCalibration[4]; + + // The camera lens distortion contains the following lens correction coefficients. + // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] + if (mLensDistortion.size() == 5) { + // According to specification the lens distortion coefficients should be ordered + // as [1, kappa_4, kappa_1, kappa_5, kappa_2, 0, kappa_3, 0] + float distortionData[] = {1.f, mLensDistortion[3], mLensDistortion[0], mLensDistortion[4], + mLensDistortion[1], 0.f, mLensDistortion[2], 0.f}; + auto distortionDataLength = sizeof(distortionData) / sizeof(distortionData[0]); + params.distortion.reserve(distortionDataLength); + params.distortion.insert(params.distortion.end(), distortionData, + distortionData + distortionDataLength); + } + + return ImagingModel::FromData(params); +} + +}; // namespace camera3 +}; // namespace android diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h new file mode 100644 index 0000000000..129d5385e6 --- /dev/null +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h @@ -0,0 +1,148 @@ +/* + * 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_DEPTH_COMPOSITE_STREAM_H +#define ANDROID_SERVERS_CAMERA_CAMERA3_DEPTH_COMPOSITE_STREAM_H + +#include +#include + +#include +#include + +#include "CompositeStream.h" + +using dynamic_depth::DepthMap; +using dynamic_depth::Item; +using dynamic_depth::ImagingModel; + +namespace android { + +class CameraDeviceClient; +class CameraMetadata; +class Surface; + +namespace camera3 { + +class DepthCompositeStream : public CompositeStream, public Thread, + public CpuConsumer::FrameAvailableListener { + +public: + DepthCompositeStream(wp device, + wp cb); + ~DepthCompositeStream() override; + + static bool isDepthCompositeStream(const sp &surface); + + // CompositeStream overrides + status_t createInternalStreams(const std::vector>& consumers, + bool hasDeferredConsumer, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int streamSetId, bool isShared) override; + status_t deleteInternalStreams() override; + status_t configureStream() override; + status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector* /*out*/outputStreamIds, + int32_t* /*out*/currentStreamId) override; + int getStreamId() override { return mBlobStreamId; } + + // CpuConsumer listener implementation + void onFrameAvailable(const BufferItem& item) override; + + // Return stream information about the internal camera streams + static status_t getCompositeStreamInfo(const OutputStreamInfo &streamInfo, + const CameraMetadata& ch, std::vector* compositeOutput /*out*/); + +protected: + + bool threadLoop() override; + bool onStreamBufferError(const CaptureResultExtras& resultExtras) override; + void onResultError(const CaptureResultExtras& resultExtras) override; + +private: + struct InputFrame { + CpuConsumer::LockedBuffer depthBuffer; + CpuConsumer::LockedBuffer jpegBuffer; + CameraMetadata result; + bool error; + bool errorNotified; + int64_t frameNumber; + + InputFrame() : error(false), errorNotified(false), frameNumber(-1) { } + }; + + // Helper methods + static void getSupportedDepthSizes(const CameraMetadata& ch, + std::vector>* depthSizes /*out*/); + static status_t getMatchingDepthSize(size_t width, size_t height, + const std::vector>& supporedDepthSizes, + size_t *depthWidth /*out*/, size_t *depthHeight /*out*/); + + // Dynamic depth processing + status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out, + const size_t maxOutSize, uint8_t jpegQuality, size_t &actualSize); + std::unique_ptr processDepthMapFrame(const CpuConsumer::LockedBuffer &depthMapBuffer, + size_t maxJpegSize, uint8_t jpegQuality, + std::vector>* items /*out*/); + std::unique_ptr getImagingModel(); + status_t processInputFrame(const InputFrame &inputFrame); + + // Buffer/Results handling + void compilePendingInputLocked(); + void releaseInputFrameLocked(InputFrame *inputFrame /*out*/); + void releaseInputFramesLocked(int64_t currentTs); + + // Find first complete and valid frame with smallest timestamp + bool getNextReadyInputLocked(int64_t *currentTs /*inout*/); + + // Find next failing frame number with smallest timestamp and return respective frame number + int64_t getNextFailingInputLocked(int64_t *currentTs /*inout*/); + + static const nsecs_t kWaitDuration = 10000000; // 10 ms + static const auto kDepthMapPixelFormat = HAL_PIXEL_FORMAT_Y16; + static const auto kDepthMapDataSpace = HAL_DATASPACE_DEPTH; + static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF; + + struct ProducerListener : public BnProducerListener { + // ProducerListener implementation + void onBufferReleased() override { /*No impl. for now*/ }; + }; + + int mBlobStreamId, mBlobSurfaceId, mDepthStreamId, mDepthSurfaceId; + size_t mBlobWidth, mBlobHeight; + sp mBlobConsumer, mDepthConsumer; + bool mDepthBufferAcquired, mBlobBufferAcquired; + sp mDepthSurface, mBlobSurface, mOutputSurface; + sp mProducerListener; + + ssize_t mMaxJpegSize; + std::vector> mSupportedDepthSizes; + std::vector mInstrinsicCalibration, mLensDistortion; + bool mIsLogicalCamera; + + // Keep all incoming Depth buffer timestamps pending further processing. + std::vector mInputDepthBuffers; + + // Keep all incoming Jpeg/Blob buffer timestamps pending further processing. + std::vector mInputJpegBuffers; + + // Map of all input frames pending further processing. + std::unordered_map mPendingInputFrames; +}; + +}; //namespace camera3 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index cee4a84433..d6d34e95f2 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -58,6 +58,8 @@ const std::chrono::system_clock::duration kCameraKeepAliveDelay = 3s; } // anonymous namespace +const float CameraProviderManager::kDepthARTolerance = .1f; + CameraProviderManager::HardwareServiceInteractionProxy CameraProviderManager::sHardwareServiceInteractionProxy{}; @@ -576,10 +578,7 @@ void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedDynamicDepthS // The dynamic depth spec. does not mention how close the AR ratio should be. // Try using something appropriate. - float ARTolerance = .01f; - - //TODO: Remove this before merging! This is for testing purposes only - ARTolerance = 10.f; + float ARTolerance = kDepthARTolerance; for (const auto& blobSize : blobSizes) { float jpegAR = static_cast (std::get<0>(blobSize)) / diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 1f34f2fc46..80ec130e98 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -265,6 +265,8 @@ public: bool isLogicalCamera(const std::string& id, std::vector* physicalCameraIds); bool isHiddenPhysicalCamera(const std::string& cameraId); + + static const float kDepthARTolerance; private: // All private members, unless otherwise noted, expect mInterfaceMutex to be locked before use mutable std::mutex mInterfaceMutex; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 12fbf82f7c..99b804394a 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -3119,10 +3119,12 @@ void Camera3Device::returnOutputBuffers( status_t res = OK; if (it != outputSurfaces.end()) { res = stream->returnBuffer( - outputBuffers[i], timestamp, timestampIncreasing, it->second); + outputBuffers[i], timestamp, timestampIncreasing, it->second, + inResultExtras.frameNumber); } else { res = stream->returnBuffer( - outputBuffers[i], timestamp, timestampIncreasing); + outputBuffers[i], timestamp, timestampIncreasing, std::vector (), + inResultExtras.frameNumber); } // Note: stream may be deallocated at this point, if this buffer was @@ -3139,7 +3141,8 @@ void Camera3Device::returnOutputBuffers( // cancel the buffer camera3_stream_buffer_t sb = outputBuffers[i]; sb.status = CAMERA3_BUFFER_STATUS_ERROR; - stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing); + stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing, std::vector (), + inResultExtras.frameNumber); // notify client buffer error sp listener; @@ -3279,7 +3282,8 @@ void Camera3Device::flushInflightRequests() { streamBuffer.stream = halStream; switch (halStream->stream_type) { case CAMERA3_STREAM_OUTPUT: - res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0); + res = stream->returnBuffer(streamBuffer, /*timestamp*/ 0, + /*timestampIncreasing*/true, std::vector (), frameNumber); if (res != OK) { ALOGE("%s: Can't return output buffer for frame %d to" " stream %d: %s (%d)", __FUNCTION__, @@ -5469,6 +5473,8 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { return TIMED_OUT; } } + outputStream->fireBufferRequestForFrameNumber( + captureRequest->mResultExtras.frameNumber); String8 physicalCameraId = outputStream->getPhysicalCameraId(); @@ -5692,7 +5698,9 @@ void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) outputBuffers->editItemAt(i).acquire_fence = -1; } outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; - captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0); + captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0, + /*timestampIncreasing*/true, std::vector (), + captureRequest->mResultExtras.frameNumber); } if (sendRequestError) { diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 24d1c1bd24..b296513245 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -656,7 +656,7 @@ void Camera3Stream::removeOutstandingBuffer(const camera3_stream_buffer &buffer) status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, nsecs_t timestamp, bool timestampIncreasing, - const std::vector& surface_ids) { + const std::vector& surface_ids, uint64_t frameNumber) { ATRACE_CALL(); Mutex::Autolock l(mLock); @@ -687,7 +687,7 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, */ status_t res = returnBufferLocked(b, timestamp, surface_ids); if (res == OK) { - fireBufferListenersLocked(b, /*acquired*/false, /*output*/true); + fireBufferListenersLocked(b, /*acquired*/false, /*output*/true, timestamp, frameNumber); } // Even if returning the buffer failed, we still want to signal whoever is waiting for the @@ -763,8 +763,21 @@ status_t Camera3Stream::getInputBufferProducer(sp *produ return getInputBufferProducerLocked(producer); } +void Camera3Stream::fireBufferRequestForFrameNumber(uint64_t frameNumber) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + for (auto &it : mBufferListenerList) { + sp listener = it.promote(); + if (listener.get() != nullptr) { + listener->onBufferRequestForFrameNumber(frameNumber, getId()); + } + } +} + void Camera3Stream::fireBufferListenersLocked( - const camera3_stream_buffer& buffer, bool acquired, bool output) { + const camera3_stream_buffer& buffer, bool acquired, bool output, nsecs_t timestamp, + uint64_t frameNumber) { List >::iterator it, end; // TODO: finish implementing @@ -773,6 +786,8 @@ void Camera3Stream::fireBufferListenersLocked( Camera3StreamBufferListener::BufferInfo(); info.mOutput = output; info.mError = (buffer.status == CAMERA3_BUFFER_STATUS_ERROR); + info.mFrameNumber = frameNumber; + info.mTimestamp = timestamp; // TODO: rest of fields for (it = mBufferListenerList.begin(), end = mBufferListenerList.end(); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index ddba9f63e5..06deba9fa9 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -332,7 +332,8 @@ class Camera3Stream : */ status_t returnBuffer(const camera3_stream_buffer &buffer, nsecs_t timestamp, bool timestampIncreasing, - const std::vector& surface_ids = std::vector()); + const std::vector& surface_ids = std::vector(), + uint64_t frameNumber = 0); /** * Fill in the camera3_stream_buffer with the next valid buffer for this @@ -430,6 +431,11 @@ class Camera3Stream : */ status_t restoreConfiguredState(); + /** + * Notify buffer stream listeners about incoming request with particular frame number. + */ + void fireBufferRequestForFrameNumber(uint64_t frameNumber) override; + protected: const int mId; /** @@ -538,7 +544,7 @@ class Camera3Stream : static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms void fireBufferListenersLocked(const camera3_stream_buffer& buffer, - bool acquired, bool output); + bool acquired, bool output, nsecs_t timestamp = 0, uint64_t frameNumber = 0); List > mBufferListenerList; status_t cancelPrepareLocked(); diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h index 2db333db07..0e6104e7ff 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h +++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h @@ -41,6 +41,8 @@ public: virtual void onBufferAcquired(const BufferInfo& bufferInfo) = 0; // Buffer was released by the HAL virtual void onBufferReleased(const BufferInfo& bufferInfo) = 0; + // Notify about incoming buffer request frame number + virtual void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) = 0; }; }; //namespace camera3 diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index a84720bfe2..7b80cbd687 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -259,7 +259,8 @@ class Camera3StreamInterface : public virtual RefBase { */ virtual status_t returnBuffer(const camera3_stream_buffer &buffer, nsecs_t timestamp, bool timestampIncreasing = true, - const std::vector& surface_ids = std::vector()) = 0; + const std::vector& surface_ids = std::vector(), + uint64_t frameNumber = 0) = 0; /** * Fill in the camera3_stream_buffer with the next valid buffer for this @@ -341,6 +342,11 @@ class Camera3StreamInterface : public virtual RefBase { * Camera3Stream. */ virtual void setBufferFreedListener(wp listener) = 0; + + /** + * Notify buffer stream listeners about incoming request with particular frame number. + */ + virtual void fireBufferRequestForFrameNumber(uint64_t frameNumber) = 0; }; } // namespace camera3 -- GitLab From ff20fd45c7b77182846631c20cae1fcfca5a58a8 Mon Sep 17 00:00:00 2001 From: Khushal Date: Tue, 22 Jan 2019 15:31:00 -0800 Subject: [PATCH 0807/1530] Fix hardware protected path for AImageReader. Ensure IGraphicBufferConsumer is marked protected for AImageReader if the client requests AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT usage. BUG: crbug.com/889328 Test: Manual testing. Change-Id: Ic5a866cd294f44fc3669bbd1fdf0ab4a25b3239f --- media/ndk/NdkImageReader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index 1a0c3b1ff2..010c1aa520 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -308,6 +308,9 @@ AImageReader::init() { ALOGE("Failed to set BufferItemConsumer buffer dataSpace"); return AMEDIA_ERROR_UNKNOWN; } + if (mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { + gbConsumer->setConsumerIsProtected(true); + } mSurface = new Surface(mProducer, /*controlledByApp*/true); if (mSurface == nullptr) { -- GitLab From 30c15dc218ffddf97eab437aea3f12dee8d3d99e Mon Sep 17 00:00:00 2001 From: bohu Date: Fri, 25 Jan 2019 13:06:05 -0800 Subject: [PATCH 0808/1530] seccomp: allows more syscalls for swcodec BUG: 123415047 Test: lunch sdk_gphone_x86_64-user make -j emulator media.swcodec should not be killed by minijail anymore Change-Id: Id483daf395b977c538cc4b2b8f11e20e0a9954ca --- services/mediacodec/seccomp_policy/mediacodec-x86.policy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index 966e2140b7..6d88c845df 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -18,15 +18,19 @@ prctl: 1 openat: 1 open: 1 getuid32: 1 +getuid: 1 +getrlimit: 1 writev: 1 ioctl: 1 close: 1 mmap2: 1 +mmap: 1 fstat64: 1 stat64: 1 statfs64: 1 madvise: 1 fstatat64: 1 +newfstatat: 1 futex: 1 munmap: 1 faccessat: 1 -- GitLab From a4d13be8d6a571da06b97e25f97a664c904220d5 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 23 Jan 2019 15:24:46 -0800 Subject: [PATCH 0809/1530] Revist C2HandleGralloc::WrapNativeHandle Do not expose Move semantic of wrapping gralloc handle. And use cloned handle inside C2HandleGralloc::WrapNativeHandle. Bug: 123107921 Change-Id: If0335c3788cae2d13da215a998ed266ad3e3704c --- .../hidl/1.0/utils/InputSurfaceConnection.cpp | 2 +- media/codec2/sfplugin/C2OMXNode.cpp | 6 +-- media/codec2/sfplugin/Codec2Buffer.cpp | 2 +- media/codec2/vndk/C2AllocatorGralloc.cpp | 28 +++++++++-- media/codec2/vndk/platform/C2BqBuffer.cpp | 50 ++++++++----------- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp index ba7c2d64a1..8b1ece3096 100644 --- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp @@ -168,7 +168,7 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { std::shared_ptr alloc; C2Handle* handle = WrapNativeCodec2GrallocHandle( - native_handle_clone(buffer->handle), + buffer->handle, buffer->width, buffer->height, buffer->format, buffer->usage, buffer->stride); mAllocatorMutex.lock(); diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 9500aedb18..03d859aaee 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -225,18 +225,14 @@ status_t C2OMXNode::emptyBuffer( if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer && omxBuf.mGraphicBuffer != nullptr) { std::shared_ptr alloc; - native_handle_t *clonedHandle = native_handle_clone(omxBuf.mGraphicBuffer->handle); handle = WrapNativeCodec2GrallocHandle( - clonedHandle, + omxBuf.mGraphicBuffer->handle, omxBuf.mGraphicBuffer->width, omxBuf.mGraphicBuffer->height, omxBuf.mGraphicBuffer->format, omxBuf.mGraphicBuffer->usage, omxBuf.mGraphicBuffer->stride); c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc); - if (clonedHandle) { - native_handle_delete(clonedHandle); - } if (err != OK) { return UNKNOWN_ERROR; } diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 1113ae838c..597e8f3c50 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -578,7 +578,7 @@ std::shared_ptr GraphicMetadataBuffer::asC2Buffer() { ALOGV("VideoNativeMetadata: %dx%d", buffer->width, buffer->height); C2Handle *handle = WrapNativeCodec2GrallocHandle( - native_handle_clone(buffer->handle), + buffer->handle, buffer->width, buffer->height, buffer->format, diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index 18f2430980..e698bf4b9a 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -159,7 +159,7 @@ public: return xd != nullptr && xd->magic == MAGIC; } - static C2HandleGralloc* WrapNativeHandle( + static C2HandleGralloc* WrapAndMoveNativeHandle( const native_handle_t *const handle, uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) { @@ -181,6 +181,26 @@ public: return reinterpret_cast(res); } + static C2HandleGralloc* WrapNativeHandle( + const native_handle_t *const handle, + uint32_t width, uint32_t height, uint32_t format, uint64_t usage, + uint32_t stride, uint32_t generation, uint64_t igbp_id = 0, uint32_t igbp_slot = 0) { + if (handle == nullptr) { + return nullptr; + } + native_handle_t *clone = native_handle_clone(handle); + if (clone == nullptr) { + return nullptr; + } + C2HandleGralloc *res = WrapAndMoveNativeHandle( + clone, width, height, format, usage, stride, generation, igbp_id, igbp_slot); + if (res == nullptr) { + native_handle_close(clone); + } + native_handle_delete(clone); + return res; + } + static native_handle_t* UnwrapNativeHandle( const C2Handle *const handle) { const ExtraData *xd = getExtraData(handle); @@ -366,7 +386,7 @@ c2_status_t C2AllocationGralloc::map( if (mHandle) { mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot); } - mLockedHandle = C2HandleGralloc::WrapNativeHandle( + mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle( mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height, (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride, generation, igbp_id, igbp_slot); @@ -743,7 +763,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( return; } info.stride = stride; - buffer = std::move(buffers[0]); + buffer = buffers[0]; }); if (err != C2_OK) { return err; @@ -752,7 +772,7 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( allocation->reset(new C2AllocationGralloc( info, mMapper, buffer, - C2HandleGralloc::WrapNativeHandle( + C2HandleGralloc::WrapAndMoveNativeHandle( buffer.getNativeHandle(), info.mapperInfo.width, info.mapperInfo.height, (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride, diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 7a26035e78..6e71b98f1d 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -269,36 +269,28 @@ private: } } if (slotBuffer) { - native_handle_t *grallocHandle = native_handle_clone(slotBuffer->handle); - - if (grallocHandle) { - ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot); - C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle( - grallocHandle, - slotBuffer->width, - slotBuffer->height, - slotBuffer->format, - slotBuffer->usage, - slotBuffer->stride, - slotBuffer->getGenerationNumber(), - mProducerId, slot); - if (c2Handle) { - // Moved everything to c2Handle. - native_handle_delete(grallocHandle); - std::shared_ptr alloc; - c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc); - if (err != C2_OK) { - return err; - } - std::shared_ptr poolData = - std::make_shared( - slotBuffer->getGenerationNumber(), - mProducerId, slot, shared_from_this()); - *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); - return C2_OK; + ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot); + C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle( + slotBuffer->handle, + slotBuffer->width, + slotBuffer->height, + slotBuffer->format, + slotBuffer->usage, + slotBuffer->stride, + slotBuffer->getGenerationNumber(), + mProducerId, slot); + if (c2Handle) { + std::shared_ptr alloc; + c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc); + if (err != C2_OK) { + return err; } - native_handle_close(grallocHandle); - native_handle_delete(grallocHandle); + std::shared_ptr poolData = + std::make_shared( + slotBuffer->getGenerationNumber(), + mProducerId, slot, shared_from_this()); + *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); + return C2_OK; } // Block was not created. call requestBuffer# again next time. slotBuffer.clear(); -- GitLab From a6fc4d3f7a7f32695e019688fea9d176e982d778 Mon Sep 17 00:00:00 2001 From: Henry Fang Date: Thu, 17 Jan 2019 17:50:36 -0800 Subject: [PATCH 0810/1530] Update plugin to use cas@1.1 hal Implement sendSessionEvent and onSessionEvent in ClearKeyCasPlugin and MockCasPlugin Test: manual Bug: 122472761 Change-Id: Iceb3e14f35cf3178b70e43b59fd492255ab205bc --- .../plugins/clearkey/ClearKeyCasPlugin.cpp | 42 ++++++++++++++++++- .../plugins/clearkey/ClearKeyCasPlugin.h | 11 +++++ drm/mediacas/plugins/mock/MockCasPlugin.cpp | 23 ++++++++++ drm/mediacas/plugins/mock/MockCasPlugin.h | 11 ++++- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp index 1558e8b8da..27bd631afe 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp @@ -65,7 +65,20 @@ status_t ClearKeyCasFactory::createPlugin( *plugin = new ClearKeyCasPlugin(appData, callback); return OK; } -/////////////////////////////////////////////////////////////////////////////// + +status_t ClearKeyCasFactory::createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) { + if (!isSystemIdSupported(CA_system_id)) { + return BAD_VALUE; + } + + *plugin = new ClearKeyCasPlugin(appData, callback); + return OK; +} +//////////////////////////////////////////////////////////////////////////////// bool ClearKeyDescramblerFactory::isSystemIdSupported( int32_t CA_system_id) const { return CA_system_id == sClearKeySystemId; @@ -88,6 +101,12 @@ ClearKeyCasPlugin::ClearKeyCasPlugin( ALOGV("CTOR"); } +ClearKeyCasPlugin::ClearKeyCasPlugin( + void *appData, CasPluginCallbackExt callback) + : mCallbackExt(callback), mAppData(appData) { + ALOGV("CTOR"); +} + ClearKeyCasPlugin::~ClearKeyCasPlugin() { ALOGV("DTOR"); ClearKeySessionLibrary::get()->destroyPlugin(this); @@ -167,8 +186,27 @@ status_t ClearKeyCasPlugin::sendEvent( // Echo the received event to the callback. // Clear key plugin doesn't use any event, echo'ing for testing only. if (mCallback != NULL) { - mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), eventData.size()); + mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size()); + } else if (mCallbackExt != NULL) { + mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size(), NULL); + } + return OK; +} + +status_t ClearKeyCasPlugin::sendSessionEvent( + const CasSessionId &sessionId, int32_t event, + int arg, const CasData &eventData) { + ALOGV("sendSessionEvent: sessionId=%s, event=%d, arg=%d", + sessionIdToString(sessionId).string(), event, arg); + // Echo the received event to the callback. + // Clear key plugin doesn't use any event, echo'ing for testing only. + if (mCallbackExt != NULL) { + mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size(), &sessionId); } + return OK; } diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h index 389e1728d7..f48d5b1d13 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h @@ -47,6 +47,11 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; + virtual status_t createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) override; }; class ClearKeyDescramblerFactory : public DescramblerFactory { @@ -63,6 +68,7 @@ public: class ClearKeyCasPlugin : public CasPlugin { public: ClearKeyCasPlugin(void *appData, CasPluginCallback callback); + ClearKeyCasPlugin(void *appData, CasPluginCallbackExt callback); virtual ~ClearKeyCasPlugin(); virtual status_t setPrivateData( @@ -85,6 +91,10 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; + virtual status_t sendSessionEvent( + const CasSessionId &sessionId, + int32_t event, int32_t arg, const CasData &eventData) override; + virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( @@ -94,6 +104,7 @@ private: Mutex mKeyFetcherLock; std::unique_ptr mKeyFetcher; CasPluginCallback mCallback; + CasPluginCallbackExt mCallbackExt; void* mAppData; }; diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp index 8404a83935..2964791413 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp +++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp @@ -60,6 +60,19 @@ status_t MockCasFactory::createPlugin( return OK; } +status_t MockCasFactory::createPlugin( + int32_t CA_system_id, + void* /*appData*/, + CasPluginCallbackExt /*callback*/, + CasPlugin **plugin) { + if (!isSystemIdSupported(CA_system_id)) { + return BAD_VALUE; + } + + *plugin = new MockCasPlugin(); + return OK; +} + /////////////////////////////////////////////////////////////////////////////// bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const { @@ -170,6 +183,16 @@ status_t MockCasPlugin::sendEvent( return OK; } +status_t MockCasPlugin::sendSessionEvent( + const CasSessionId &sessionId, int32_t event, + int /*arg*/, const CasData& /*eventData*/) { + ALOGV("sendSessionEvent: sessionId=%s, event=%d", + arrayToString(sessionId).string(), event); + Mutex::Autolock lock(mLock); + + return OK; +} + status_t MockCasPlugin::provision(const String8 &str) { ALOGV("provision: provisionString=%s", str.string()); Mutex::Autolock lock(mLock); diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h index 81069906ed..74b540cc82 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.h +++ b/drm/mediacas/plugins/mock/MockCasPlugin.h @@ -42,6 +42,11 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; + virtual status_t createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) override; }; class MockDescramblerFactory : public DescramblerFactory { @@ -80,7 +85,11 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; - virtual status_t provision(const String8 &str) override; + virtual status_t sendSessionEvent( + const CasSessionId &sessionId, + int32_t event, int32_t arg, const CasData &eventData) override; + + virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( int32_t refreshType, const CasData &refreshData) override; -- GitLab From 5cc71c5fedf82cccf701ce24db79818cbce20b64 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 25 Jan 2019 14:10:42 -0800 Subject: [PATCH 0811/1530] AudioPolicyManager: fix offload playback for speaker and BT Without this, deep buffer playback is used. Fixes regression from commit 4e4647077f00c47650d6ecd66cd812073371bd10 Test: Play music with Speaker and BT Bug: 123371431 Change-Id: Ic5d23c6863223f5456414c887c14fda90aa06f5f --- .../audiopolicy/common/managerdefinitions/include/IOProfile.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h index dc409a7439..e0b56d4793 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -121,6 +121,9 @@ public: bool deviceSupportsEncodedFormats(audio_devices_t device) const { + if (device == AUDIO_DEVICE_NONE) { + return true; // required for isOffloadSupported() check + } DeviceVector deviceList = mSupportedDevices.getDevicesFromTypeMask(device); if (!deviceList.empty()) { -- GitLab From d84e8f7fdf1a61956953660d3af226c3b147e3bb Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 25 Jan 2019 18:15:35 -0800 Subject: [PATCH 0812/1530] audio policy: fix legacy Ad2P with advanced codecs The changes for hybrid A2DP broke advanced codec support for devices running a HAL V4.0 and no A2DP offload. Fix: consider that if no encoded format is explicitely listed in the device descriptor in audio policy configuration file, then no rule on encoded format should be enforced. Bug: 123399421 Test: connect Sony MDR-1 headphones and play music. Change-Id: Idd13cf0e0cdeb71554b23d178e61cc1d82abea60 --- .../managerdefinitions/src/DeviceDescriptor.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 1bc4ec848a..dc5b238cbb 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -51,12 +51,6 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc mEncodedFormats.add(AUDIO_FORMAT_AC3); mEncodedFormats.add(AUDIO_FORMAT_IEC61937); } - // For backward compatibility always indicate support for SBC and AAC if no - // supported format is listed in the configuration file - if ((type & AUDIO_DEVICE_OUT_ALL_A2DP) != 0 && mEncodedFormats.isEmpty()) { - mEncodedFormats.add(AUDIO_FORMAT_SBC); - mEncodedFormats.add(AUDIO_FORMAT_AAC); - } } audio_port_handle_t DeviceDescriptor::getId() const @@ -102,11 +96,19 @@ bool DeviceDescriptor::hasCurrentEncodedFormat() const if (!device_has_encoding_capability(type())) { return true; } + if (mEncodedFormats.isEmpty()) { + return true; + } + return (mCurrentEncodedFormat != AUDIO_FORMAT_DEFAULT); } bool DeviceDescriptor::supportsFormat(audio_format_t format) { + if (mEncodedFormats.isEmpty()) { + return true; + } + for (const auto& devFormat : mEncodedFormats) { if (devFormat == format) { return true; -- GitLab From 6b0b52643d064ad36a47a4bb5ad76966fc25cc0a Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 25 Jan 2019 19:48:31 -0800 Subject: [PATCH 0813/1530] Reduce logspam in nudriver Currently, there is a lot of logspam in nudriver. Reduce the spam. Test: flash the device and let it idle. Open 'adb logcat'. Observe. Before the change: logspam @ about 10 messages / second After the change: no logspam Bug: 65494379 Change-Id: Ie952fdcab61aa89bb72d9c6dc63487643febd974 --- media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp | 6 +++--- media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index df1ffde89c..6d69d5001a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -678,7 +678,7 @@ bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) { msg->setSize("buffer-ix", index); sp buffer = mCSDsToSubmit.itemAt(0); - ALOGI("[%s] resubmitting CSD", mComponentName.c_str()); + ALOGV("[%s] resubmitting CSD", mComponentName.c_str()); msg->setBuffer("buffer", buffer); mCSDsToSubmit.removeAt(0); if (!onInputBufferFetched(msg)) { @@ -749,7 +749,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer( reply->setSize("size", size); if (eos) { - ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video"); + ALOGV("[%s] saw output EOS", mIsAudio ? "audio" : "video"); buffer->meta()->setInt32("eos", true); reply->setInt32("eos", true); @@ -1029,7 +1029,7 @@ bool NuPlayer::Decoder::onInputBufferFetched(const sp &msg) { int64_t resumeAtMediaTimeUs; if (extra->findInt64( "resume-at-mediaTimeUs", &resumeAtMediaTimeUs)) { - ALOGI("[%s] suppressing rendering until %lld us", + ALOGV("[%s] suppressing rendering until %lld us", mComponentName.c_str(), (long long)resumeAtMediaTimeUs); mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index ba3ebaa9f8..a820445651 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -329,7 +329,7 @@ status_t NuPlayerDriver::prepareAsync() { } status_t NuPlayerDriver::start() { - ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS); + ALOGV("start(%p), state is %d, eos is %d", this, mState, mAtEOS); Mutex::Autolock autoLock(mLock); return start_l(); } @@ -471,7 +471,7 @@ status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) } status_t NuPlayerDriver::seekTo(int msec, MediaPlayerSeekMode mode) { - ALOGD("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState); + ALOGV("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState); Mutex::Autolock autoLock(mLock); int64_t seekTimeUs = msec * 1000LL; @@ -965,7 +965,7 @@ void NuPlayerDriver::notifyListener( void NuPlayerDriver::notifyListener_l( int msg, int ext1, int ext2, const Parcel *in) { - ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)", + ALOGV("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)", this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping); switch (msg) { case MEDIA_PLAYBACK_COMPLETE: -- GitLab From 342d98554de86ed62580bd142dd4ab70c9cb764e Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Sun, 27 Jan 2019 17:14:15 +0000 Subject: [PATCH 0814/1530] Use the correct headers for header_only_library dependencies on libnativehelper. Bug: 119840313 Test: m -j100 Change-Id: I9d9afeb8713cc8daca486c6f96fff09f6f8a473e --- media/libmediaplayer2/JMedia2HTTPConnection.cpp | 3 +-- media/libmediaplayer2/JMedia2HTTPService.cpp | 3 +-- media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/media/libmediaplayer2/JMedia2HTTPConnection.cpp b/media/libmediaplayer2/JMedia2HTTPConnection.cpp index d264a7fe72..e1baa104b4 100644 --- a/media/libmediaplayer2/JMedia2HTTPConnection.cpp +++ b/media/libmediaplayer2/JMedia2HTTPConnection.cpp @@ -21,11 +21,10 @@ #include #include #include -#include +#include #include "log/log.h" #include "jni.h" -#include namespace android { diff --git a/media/libmediaplayer2/JMedia2HTTPService.cpp b/media/libmediaplayer2/JMedia2HTTPService.cpp index 264c15dc03..20e3573de2 100644 --- a/media/libmediaplayer2/JMedia2HTTPService.cpp +++ b/media/libmediaplayer2/JMedia2HTTPService.cpp @@ -25,8 +25,7 @@ #include #include -#include -#include +#include namespace android { diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp index bbd22bcbe2..89703de7f8 100644 --- a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp +++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include "log/log.h" -- GitLab From 8a1f50678d5556ffcf05be818f364de8792e9c47 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Fri, 25 Jan 2019 03:12:01 -0800 Subject: [PATCH 0815/1530] cmds: Use getInternalDisplayToken API Bug: 116025192 Test: screenrecord still works Change-Id: Ic7ad1d78ac8aa810bad8901fe1e71582168b8c4c --- cmds/screenrecord/screenrecord.cpp | 9 +++++++-- cmds/stagefright/codec.cpp | 8 +++++--- cmds/stagefright/mediafilter.cpp | 8 +++++--- cmds/stagefright/stream.cpp | 8 +++++--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index 7803ccc752..c361690eb0 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -86,6 +86,7 @@ using android::DISPLAY_ORIENTATION_90; using android::INFO_FORMAT_CHANGED; using android::INFO_OUTPUT_BUFFERS_CHANGED; using android::INVALID_OPERATION; +using android::NAME_NOT_FOUND; using android::NO_ERROR; using android::UNKNOWN_ERROR; @@ -585,8 +586,12 @@ static status_t recordScreen(const char* fileName) { self->startThreadPool(); // Get main display parameters. - sp mainDpy = SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain); + const sp mainDpy = SurfaceComposerClient::getInternalDisplayToken(); + if (mainDpy == nullptr) { + fprintf(stderr, "ERROR: no display\n"); + return NAME_NOT_FOUND; + } + DisplayInfo mainDpyInfo; err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo); if (err != NO_ERROR) { diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp index a463ec514d..e5a43373c5 100644 --- a/cmds/stagefright/codec.cpp +++ b/cmds/stagefright/codec.cpp @@ -411,10 +411,12 @@ int main(int argc, char **argv) { composerClient = new SurfaceComposerClient; CHECK_EQ(composerClient->initCheck(), (status_t)OK); - sp display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + const sp display = SurfaceComposerClient::getInternalDisplayToken(); + CHECK(display != nullptr); + DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); + CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR); + ssize_t displayWidth = info.w; ssize_t displayHeight = info.h; diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp index f0ee0e170f..2cf6955e63 100644 --- a/cmds/stagefright/mediafilter.cpp +++ b/cmds/stagefright/mediafilter.cpp @@ -748,10 +748,12 @@ int main(int argc, char **argv) { composerClient = new SurfaceComposerClient; CHECK_EQ((status_t)OK, composerClient->initCheck()); - android::sp display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + const android::sp display = SurfaceComposerClient::getInternalDisplayToken(); + CHECK(display != nullptr); + DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); + CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR); + ssize_t displayWidth = info.w; ssize_t displayHeight = info.h; diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index b2f39dc7a8..35bdbc0978 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -318,10 +318,12 @@ int main(int argc, char **argv) { sp composerClient = new SurfaceComposerClient; CHECK_EQ(composerClient->initCheck(), (status_t)OK); - sp display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + const sp display = SurfaceComposerClient::getInternalDisplayToken(); + CHECK(display != nullptr); + DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); + CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR); + ssize_t displayWidth = info.w; ssize_t displayHeight = info.h; -- GitLab From 934ecfbdd877f4f1fdfaa23710eb3164ab6e7715 Mon Sep 17 00:00:00 2001 From: Jasmine Cha Date: Wed, 23 Jan 2019 18:19:14 +0800 Subject: [PATCH 0816/1530] Effects: add new effect volume flag Add new effect flag to monitor requested volume from audio framework. Pass requested volume directly if effect is volume monitor, and others still follow an original rule by volume controller. Bug: 123251705 Test: On/off equalizer on spotify/google music. Switch songs with equalizer Attach an effect with EFFECT_FLAG_INSERT_ANY, EFFECT_FLAG_INSERT_FIRST, EFFECT_FLAG_INSERT_LAST and check received volume. Change-Id: I01632bebb32aa6f921c964536039d43859ae3632 Signed-off-by: Jasmine Cha --- services/audioflinger/Effects.cpp | 13 ++++++++++--- services/audioflinger/Effects.h | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 6ab636970e..8455e54bb5 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1136,7 +1136,8 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set) if (isProcessEnabled() && ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL || - (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) { + (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND || + (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_MONITOR)) { uint32_t volume[2]; uint32_t *pVolume = NULL; uint32_t size = sizeof(volume); @@ -1331,6 +1332,7 @@ String8 effectFlagsToString(uint32_t flags) { case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break; case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break; case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break; + case EFFECT_FLAG_VOLUME_MONITOR: s.append("monitors volume"); break; default: s.append("unknown/reserved"); break; } s.append(", "); @@ -2277,7 +2279,7 @@ bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right, boo } // then indicate volume to all other effects in chain. // Pass altered volume to effects before volume controller - // and requested volume to effects after controller + // and requested volume to effects after controller or with volume monitor flag uint32_t lVol = newLeft; uint32_t rVol = newRight; @@ -2290,7 +2292,12 @@ bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right, boo lVol = *left; rVol = *right; } - mEffects[i]->setVolume(&lVol, &rVol, false); + // Pass requested volume directly if this is volume monitor module + if (mEffects[i]->isVolumeMonitor()) { + mEffects[i]->setVolume(left, right, false); + } else { + mEffects[i]->setVolume(&lVol, &rVol, false); + } } *left = newLeft; *right = newRight; diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index 15a26ea75e..58ce351997 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -134,6 +134,9 @@ public: bool isVolumeControl() const { return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL; } + bool isVolumeMonitor() const + { return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) + == EFFECT_FLAG_VOLUME_MONITOR; } status_t setOffloaded(bool offloaded, audio_io_handle_t io); bool isOffloaded() const; void addEffectToHal_l(); -- GitLab From cbf174b59307290e4c80947232feedb4cc9c0c26 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 25 Jan 2019 14:38:59 -0800 Subject: [PATCH 0817/1530] Camera: Link dynamically to Depth photo library Move all depth photo specific processing in a separate library and link to it dynamically. Bug: 109735087 Test: Camera CTS Change-Id: I00a20b26fc9a1d127ad962a36b5b554dd36f0d41 --- services/camera/libcameraservice/Android.bp | 39 +- .../api2/DepthCompositeStream.cpp | 390 ++++-------------- .../api2/DepthCompositeStream.h | 3 + .../common/CameraProviderManager.cpp | 32 ++ .../common/CameraProviderManager.h | 1 + .../common/DepthPhotoProcessor.cpp | 340 +++++++++++++++ .../common/DepthPhotoProcessor.h | 67 +++ 7 files changed, 562 insertions(+), 310 deletions(-) create mode 100644 services/camera/libcameraservice/common/DepthPhotoProcessor.cpp create mode 100644 services/camera/libcameraservice/common/DepthPhotoProcessor.h diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 3b362d6707..a090479183 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -67,9 +67,7 @@ cc_library_shared { ], shared_libs: [ - "libimage_io", - "libdynamic_depth", - "libxml2", + "libdl", "libui", "liblog", "libutilscallstack", @@ -127,3 +125,38 @@ cc_library_shared { ], } + +cc_library_shared { + name: "libdepthphoto", + + srcs: [ + "common/DepthPhotoProcessor.cpp", + ], + + shared_libs: [ + "libimage_io", + "libdynamic_depth", + "libxml2", + "liblog", + "libutilscallstack", + "libutils", + "libcutils", + "libjpeg", + "libmemunreachable", + ], + + include_dirs: [ + "external/dynamic_depth/includes", + "external/dynamic_depth/internal", + ], + + export_include_dirs: ["."], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wno-ignored-qualifiers", + ], + +} diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index b12bb50f09..1cf9529476 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -20,45 +20,13 @@ #include "api1/client2/JpegProcessor.h" #include "common/CameraProviderManager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - +#include "dlfcn.h" #include #include #include #include "DepthCompositeStream.h" -using dynamic_depth::Camera; -using dynamic_depth::Cameras; -using dynamic_depth::CameraParams; -using dynamic_depth::Container; -using dynamic_depth::DepthFormat; -using dynamic_depth::DepthMapParams; -using dynamic_depth::DepthUnits; -using dynamic_depth::Device; -using dynamic_depth::DeviceParams; -using dynamic_depth::Dimension; -using dynamic_depth::Image; -using dynamic_depth::ImagingModelParams; -using dynamic_depth::Pose; -using dynamic_depth::Profile; -using dynamic_depth::Profiles; - namespace android { namespace camera3 { @@ -75,7 +43,9 @@ DepthCompositeStream::DepthCompositeStream(wp device, mBlobBufferAcquired(false), mProducerListener(new ProducerListener()), mMaxJpegSize(-1), - mIsLogicalCamera(false) { + mIsLogicalCamera(false), + mDepthPhotoLibHandle(nullptr), + mDepthPhotoProcess(nullptr) { sp cameraDevice = device.promote(); if (cameraDevice.get() != nullptr) { CameraMetadata staticInfo = cameraDevice->info(); @@ -113,6 +83,19 @@ DepthCompositeStream::DepthCompositeStream(wp device, } getSupportedDepthSizes(staticInfo, &mSupportedDepthSizes); + + mDepthPhotoLibHandle = dlopen(camera3::kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL); + if (mDepthPhotoLibHandle != nullptr) { + mDepthPhotoProcess = reinterpret_cast ( + dlsym(mDepthPhotoLibHandle, camera3::kDepthPhotoProcessFunction)); + if (mDepthPhotoProcess == nullptr) { + ALOGE("%s: Failed to link to depth photo process function: %s", __FUNCTION__, + dlerror()); + } + } else { + ALOGE("%s: Failed to link to depth photo library: %s", __FUNCTION__, dlerror()); + } + } } @@ -125,6 +108,11 @@ DepthCompositeStream::~DepthCompositeStream() { mDepthSurface.clear(); mDepthConsumer = nullptr; mDepthSurface = nullptr; + if (mDepthPhotoLibHandle != nullptr) { + dlclose(mDepthPhotoLibHandle); + mDepthPhotoLibHandle = nullptr; + } + mDepthPhotoProcess = nullptr; } void DepthCompositeStream::compilePendingInputLocked() { @@ -259,200 +247,12 @@ int64_t DepthCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*ino return ret; } -status_t DepthCompositeStream::encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, - void *out, const size_t maxOutSize, uint8_t jpegQuality, size_t &actualSize) { - status_t ret; - // libjpeg is a C library so we use C-style "inheritance" by - // putting libjpeg's jpeg_destination_mgr first in our custom - // struct. This allows us to cast jpeg_destination_mgr* to - // CustomJpegDestMgr* when we get it passed to us in a callback. - struct CustomJpegDestMgr : public jpeg_destination_mgr { - JOCTET *mBuffer; - size_t mBufferSize; - size_t mEncodedSize; - bool mSuccess; - } dmgr; - - jpeg_compress_struct cinfo = {}; - jpeg_error_mgr jerr; - - // Initialize error handling with standard callbacks, but - // then override output_message (to print to ALOG) and - // error_exit to set a flag and print a message instead - // of killing the whole process. - cinfo.err = jpeg_std_error(&jerr); - - cinfo.err->output_message = [](j_common_ptr cinfo) { - char buffer[JMSG_LENGTH_MAX]; - - /* Create the message */ - (*cinfo->err->format_message)(cinfo, buffer); - ALOGE("libjpeg error: %s", buffer); - }; - - cinfo.err->error_exit = [](j_common_ptr cinfo) { - (*cinfo->err->output_message)(cinfo); - if(cinfo->client_data) { - auto & dmgr = *static_cast(cinfo->client_data); - dmgr.mSuccess = false; - } - }; - - // Now that we initialized some callbacks, let's create our compressor - jpeg_create_compress(&cinfo); - dmgr.mBuffer = static_cast(out); - dmgr.mBufferSize = maxOutSize; - dmgr.mEncodedSize = 0; - dmgr.mSuccess = true; - cinfo.client_data = static_cast(&dmgr); - - // These lambdas become C-style function pointers and as per C++11 spec - // may not capture anything. - dmgr.init_destination = [](j_compress_ptr cinfo) { - auto & dmgr = static_cast(*cinfo->dest); - dmgr.next_output_byte = dmgr.mBuffer; - dmgr.free_in_buffer = dmgr.mBufferSize; - ALOGV("%s:%d jpeg start: %p [%zu]", - __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize); - }; - - dmgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) { - ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__); - return 0; - }; - - dmgr.term_destination = [](j_compress_ptr cinfo) { - auto & dmgr = static_cast(*cinfo->dest); - dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer; - ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); - }; - cinfo.dest = reinterpret_cast(&dmgr); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 1; - cinfo.in_color_space = JCS_GRAYSCALE; - - // Initialize defaults and then override what we want - jpeg_set_defaults(&cinfo); - - jpeg_set_quality(&cinfo, jpegQuality, 1); - jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); - cinfo.raw_data_in = 0; - cinfo.dct_method = JDCT_IFAST; - - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; - - jpeg_start_compress(&cinfo, TRUE); - - for (size_t i = 0; i < cinfo.image_height; i++) { - auto currentRow = static_cast(in + i*width); - jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1); - } - - jpeg_finish_compress(&cinfo); - - actualSize = dmgr.mEncodedSize; - if (dmgr.mSuccess) { - ret = NO_ERROR; - } else { - ret = UNKNOWN_ERROR; - } - - return ret; -} - -std::unique_ptr DepthCompositeStream::processDepthMapFrame( - const CpuConsumer::LockedBuffer &depthMapBuffer, size_t maxJpegSize, uint8_t jpegQuality, - std::vector> *items /*out*/) { - std::vector points, confidence; - - size_t pointCount = depthMapBuffer.width * depthMapBuffer.height; - points.reserve(pointCount); - confidence.reserve(pointCount); - float near = UINT16_MAX; - float far = .0f; - uint16_t *data = reinterpret_cast (depthMapBuffer.data); - for (size_t i = 0; i < depthMapBuffer.height; i++) { - for (size_t j = 0; j < depthMapBuffer.width; j++) { - // Android densely packed depth map. The units for the range are in - // millimeters and need to be scaled to meters. - // The confidence value is encoded in the 3 most significant bits. - // The confidence data needs to be additionally normalized with - // values 1.0f, 0.0f representing maximum and minimum confidence - // respectively. - auto value = data[i*depthMapBuffer.stride + j]; - auto point = static_cast(value & 0x1FFF) / 1000.f; - points.push_back(point); - - auto conf = (value >> 13) & 0x7; - float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; - confidence.push_back(normConfidence); - - if (near > point) { - near = point; - } - if (far < point) { - far = point; - } - } - } - - if (near == far) { - ALOGE("%s: Near and far range values must not match!", __FUNCTION__); - return nullptr; - } - - std::vector pointsQuantized, confidenceQuantized; - pointsQuantized.reserve(pointCount); confidenceQuantized.reserve(pointCount); - auto pointIt = points.begin(); - auto confidenceIt = confidence.begin(); - while ((pointIt != points.end()) && (confidenceIt != confidence.end())) { - pointsQuantized.push_back(floorf(((far * (*pointIt - near)) / - (*pointIt * (far - near))) * 255.0f)); - confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f)); - confidenceIt++; pointIt++; - } - - DepthMapParams depthParams(DepthFormat::kRangeInverse, near, far, DepthUnits::kMeters, - "android/depthmap"); - depthParams.confidence_uri = "android/confidencemap"; - depthParams.mime = "image/jpeg"; - depthParams.depth_image_data.resize(maxJpegSize); - depthParams.confidence_data.resize(maxJpegSize); - size_t actualJpegSize; - auto ret = encodeGrayscaleJpeg(depthMapBuffer.width, depthMapBuffer.height, - pointsQuantized.data(), depthParams.depth_image_data.data(), maxJpegSize, jpegQuality, - actualJpegSize); - if (ret != NO_ERROR) { - ALOGE("%s: Depth map compression failed!", __FUNCTION__); - return nullptr; - } - depthParams.depth_image_data.resize(actualJpegSize); - - ret = encodeGrayscaleJpeg(depthMapBuffer.width, depthMapBuffer.height, - confidenceQuantized.data(), depthParams.confidence_data.data(), maxJpegSize, - jpegQuality, actualJpegSize); - if (ret != NO_ERROR) { - ALOGE("%s: Confidence map compression failed!", __FUNCTION__); - return nullptr; - } - depthParams.confidence_data.resize(actualJpegSize); - - return DepthMap::FromData(depthParams, items); -} - status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { status_t res; sp outputANW = mOutputSurface; ANativeWindowBuffer *anb; int fenceFd; void *dstBuffer; - auto imgBuffer = inputFrame.jpegBuffer; auto jpegSize = android::camera2::JpegProcessor::findJpegSize(inputFrame.jpegBuffer.data, inputFrame.jpegBuffer.width); @@ -461,15 +261,6 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { jpegSize = inputFrame.jpegBuffer.width; } - std::vector> items; - std::vector> cameraList; - auto image = Image::FromDataForPrimaryImage("android/mainimage", &items); - std::unique_ptr cameraParams(new CameraParams(std::move(image))); - if (cameraParams == nullptr) { - ALOGE("%s: Failed to initialize camera parameters", __FUNCTION__); - return BAD_VALUE; - } - size_t maxDepthJpegSize; if (mMaxJpegSize > 0) { maxDepthJpegSize = mMaxJpegSize; @@ -482,49 +273,16 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { if (entry.count > 0) { jpegQuality = entry.data.u8[0]; } - cameraParams->depth_map = processDepthMapFrame(inputFrame.depthBuffer, maxDepthJpegSize, - jpegQuality, &items); - if (cameraParams->depth_map == nullptr) { - ALOGE("%s: Depth map processing failed!", __FUNCTION__); - return BAD_VALUE; - } - cameraParams->imaging_model = getImagingModel(); - - if (mIsLogicalCamera) { - cameraParams->trait = dynamic_depth::CameraTrait::LOGICAL; - } else { - cameraParams->trait = dynamic_depth::CameraTrait::PHYSICAL; - } - cameraList.emplace_back(Camera::FromData(std::move(cameraParams))); + // The final depth photo will consist of the main jpeg buffer, the depth map buffer (also in + // jpeg format) and confidence map (jpeg as well). Assume worst case that all 3 jpeg need + // max jpeg size. + size_t finalJpegBufferSize = maxDepthJpegSize * 3; - auto deviceParams = std::make_unique (Cameras::FromCameraArray(&cameraList)); - deviceParams->container = Container::FromItems(&items); - std::vector> profileList; - profileList.emplace_back(Profile::FromData("DepthPhoto", {0})); - deviceParams->profiles = Profiles::FromProfileArray(&profileList); - std::unique_ptr device = Device::FromData(std::move(deviceParams)); - if (device == nullptr) { - ALOGE("%s: Failed to initialize camera device", __FUNCTION__); - return BAD_VALUE; - } - - std::istringstream inputJpegStream(std::string(reinterpret_cast (imgBuffer.data), - jpegSize)); - std::ostringstream outputJpegStream; - if (!WriteImageAndMetadataAndContainer(&inputJpegStream, device.get(), &outputJpegStream)) { - ALOGE("%s: Failed writing depth output", __FUNCTION__); - return BAD_VALUE; - } - - size_t finalJpegSize = static_cast (outputJpegStream.tellp()) + - sizeof(struct camera3_jpeg_blob); - - ALOGV("%s: Final jpeg size: %zu", __func__, finalJpegSize); - if ((res = native_window_set_buffers_dimensions(mOutputSurface.get(), finalJpegSize, 1)) + if ((res = native_window_set_buffers_dimensions(mOutputSurface.get(), finalJpegBufferSize, 1)) != OK) { ALOGE("%s: Unable to configure stream buffer dimensions" - " %zux%u for stream %d", __FUNCTION__, finalJpegSize, 1U, mBlobStreamId); + " %zux%u for stream %d", __FUNCTION__, finalJpegBufferSize, 1U, mBlobStreamId); return res; } @@ -544,20 +302,65 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { return res; } - if ((gb->getWidth() < finalJpegSize) || (gb->getHeight() != 1)) { + if ((gb->getWidth() < finalJpegBufferSize) || (gb->getHeight() != 1)) { ALOGE("%s: Blob buffer size mismatch, expected %dx%d received %zux%u", __FUNCTION__, - gb->getWidth(), gb->getHeight(), finalJpegSize, 1U); + gb->getWidth(), gb->getHeight(), finalJpegBufferSize, 1U); outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1); return BAD_VALUE; } - // Copy final jpeg with embedded depth data in the composite stream output buffer + DepthPhotoInputFrame depthPhoto; + depthPhoto.mMainJpegBuffer = reinterpret_cast (inputFrame.jpegBuffer.data); + depthPhoto.mMainJpegWidth = mBlobWidth; + depthPhoto.mMainJpegHeight = mBlobHeight; + depthPhoto.mMainJpegSize = jpegSize; + depthPhoto.mDepthMapBuffer = reinterpret_cast (inputFrame.depthBuffer.data); + depthPhoto.mDepthMapWidth = inputFrame.depthBuffer.width; + depthPhoto.mDepthMapHeight = inputFrame.depthBuffer.height; + depthPhoto.mDepthMapStride = inputFrame.depthBuffer.stride; + depthPhoto.mJpegQuality = jpegQuality; + depthPhoto.mIsLogical = mIsLogicalCamera; + depthPhoto.mMaxJpegSize = maxDepthJpegSize; + // The camera intrinsic calibration layout is as follows: + // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] + if (mInstrinsicCalibration.size() == 5) { + memcpy(depthPhoto.mInstrinsicCalibration, mInstrinsicCalibration.data(), + sizeof(depthPhoto.mInstrinsicCalibration)); + depthPhoto.mIsInstrinsicCalibrationValid = 1; + } else { + depthPhoto.mIsInstrinsicCalibrationValid = 0; + } + // The camera lens distortion contains the following lens correction coefficients. + // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] + if (mLensDistortion.size() == 5) { + memcpy(depthPhoto.mLensDistortion, mLensDistortion.data(), + sizeof(depthPhoto.mLensDistortion)); + depthPhoto.mIsLensDistortionValid = 1; + } else { + depthPhoto.mIsLensDistortionValid = 0; + } + + size_t actualJpegSize = 0; + res = mDepthPhotoProcess(depthPhoto, finalJpegBufferSize, dstBuffer, &actualJpegSize); + if (res != 0) { + ALOGE("%s: Depth photo processing failed: %s (%d)", __FUNCTION__, strerror(-res), res); + outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1); + return res; + } + + size_t finalJpegSize = actualJpegSize + sizeof(struct camera3_jpeg_blob); + if (finalJpegSize > finalJpegBufferSize) { + ALOGE("%s: Final jpeg buffer not large enough for the jpeg blob header", __FUNCTION__); + outputANW->cancelBuffer(mOutputSurface.get(), anb, /*fence*/ -1); + return NO_MEMORY; + } + + ALOGV("%s: Final jpeg size: %zu", __func__, finalJpegSize); uint8_t* header = static_cast (dstBuffer) + (gb->getWidth() - sizeof(struct camera3_jpeg_blob)); struct camera3_jpeg_blob *blob = reinterpret_cast (header); blob->jpeg_blob_id = CAMERA3_JPEG_BLOB_ID; - blob->jpeg_size = static_cast (outputJpegStream.tellp()); - memcpy(dstBuffer, outputJpegStream.str().c_str(), blob->jpeg_size); + blob->jpeg_size = actualJpegSize; outputANW->queueBuffer(mOutputSurface.get(), anb, /*fence*/ -1); return res; @@ -758,6 +561,11 @@ status_t DepthCompositeStream::configureStream() { return NO_ERROR; } + if ((mDepthPhotoLibHandle == nullptr) || (mDepthPhotoProcess == nullptr)) { + ALOGE("%s: Depth photo library is not present!", __FUNCTION__); + return NO_INIT; + } + if (mOutputSurface.get() == nullptr) { ALOGE("%s: No valid output surface set!", __FUNCTION__); return NO_INIT; @@ -990,37 +798,5 @@ status_t DepthCompositeStream::getCompositeStreamInfo(const OutputStreamInfo &st return NO_ERROR; } -std::unique_ptr DepthCompositeStream::getImagingModel() { - // It is not possible to generate an imaging model without instrinsic calibration. - if (mInstrinsicCalibration.empty() || mInstrinsicCalibration.size() != 5) { - return nullptr; - } - - // The camera intrinsic calibration layout is as follows: - // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] - const dynamic_depth::Point focalLength(mInstrinsicCalibration[0], - mInstrinsicCalibration[1]); - const Dimension imageSize(mBlobWidth, mBlobHeight); - ImagingModelParams params(focalLength, imageSize); - params.principal_point.x = mInstrinsicCalibration[2]; - params.principal_point.y = mInstrinsicCalibration[3]; - params.skew = mInstrinsicCalibration[4]; - - // The camera lens distortion contains the following lens correction coefficients. - // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] - if (mLensDistortion.size() == 5) { - // According to specification the lens distortion coefficients should be ordered - // as [1, kappa_4, kappa_1, kappa_5, kappa_2, 0, kappa_3, 0] - float distortionData[] = {1.f, mLensDistortion[3], mLensDistortion[0], mLensDistortion[4], - mLensDistortion[1], 0.f, mLensDistortion[2], 0.f}; - auto distortionDataLength = sizeof(distortionData) / sizeof(distortionData[0]); - params.distortion.reserve(distortionDataLength); - params.distortion.insert(params.distortion.end(), distortionData, - distortionData + distortionDataLength); - } - - return ImagingModel::FromData(params); -} - }; // namespace camera3 }; // namespace android diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h index 129d5385e6..e8fe517e13 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.h +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SERVERS_CAMERA_CAMERA3_DEPTH_COMPOSITE_STREAM_H #define ANDROID_SERVERS_CAMERA_CAMERA3_DEPTH_COMPOSITE_STREAM_H +#include "common/DepthPhotoProcessor.h" #include #include @@ -131,6 +132,8 @@ private: std::vector> mSupportedDepthSizes; std::vector mInstrinsicCalibration, mLensDistortion; bool mIsLogicalCamera; + void* mDepthPhotoLibHandle; + process_depth_photo_frame mDepthPhotoProcess; // Keep all incoming Depth buffer timestamps pending further processing. std::vector mInputDepthBuffers; diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index d6d34e95f2..3059b0737d 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -24,6 +24,8 @@ #include #include +#include "common/DepthPhotoProcessor.h" +#include #include #include #include @@ -606,6 +608,31 @@ void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedDynamicDepthS } } +bool CameraProviderManager::ProviderInfo::DeviceInfo3::isDepthPhotoLibraryPresent() { + static bool libraryPresent = false; + static bool initialized = false; + if (initialized) { + return libraryPresent; + } else { + initialized = true; + } + + void* depthLibHandle = dlopen(camera3::kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL); + if (depthLibHandle == nullptr) { + return false; + } + + auto processFunc = dlsym(depthLibHandle, camera3::kDepthPhotoProcessFunction); + if (processFunc != nullptr) { + libraryPresent = true; + } else { + libraryPresent = false; + } + dlclose(depthLibHandle); + + return libraryPresent; +} + status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() { uint32_t depthExclTag = ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE; uint32_t depthSizesTag = ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS; @@ -654,6 +681,11 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() return OK; } + if(!isDepthPhotoLibraryPresent()) { + // Depth photo processing library is not present, nothing more to do. + return OK; + } + std::vector dynamicDepthEntries; for (const auto& it : supportedDynamicDepthSizes) { int32_t entry[4] = {HAL_PIXEL_FORMAT_BLOB, static_cast (std::get<0>(it)), diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 80ec130e98..fbd7d2e8f8 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -483,6 +483,7 @@ private: void getSupportedDynamicDepthDurations(const std::vector& depthDurations, const std::vector& blobDurations, std::vector *dynamicDepthDurations /*out*/); + static bool isDepthPhotoLibraryPresent(); static void getSupportedDynamicDepthSizes( const std::vector>& blobSizes, const std::vector>& depthSizes, diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp new file mode 100644 index 0000000000..a945aca13b --- /dev/null +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2019 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 "Camera3-DepthPhotoProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 +// + +#include "DepthPhotoProcessor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using dynamic_depth::Camera; +using dynamic_depth::Cameras; +using dynamic_depth::CameraParams; +using dynamic_depth::Container; +using dynamic_depth::DepthFormat; +using dynamic_depth::DepthMap; +using dynamic_depth::DepthMapParams; +using dynamic_depth::DepthUnits; +using dynamic_depth::Device; +using dynamic_depth::DeviceParams; +using dynamic_depth::Dimension; +using dynamic_depth::Image; +using dynamic_depth::ImagingModel; +using dynamic_depth::ImagingModelParams; +using dynamic_depth::Item; +using dynamic_depth::Pose; +using dynamic_depth::Profile; +using dynamic_depth::Profiles; + +namespace android { +namespace camera3 { + +status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out, + const size_t maxOutSize, uint8_t jpegQuality, size_t &actualSize) { + status_t ret; + // libjpeg is a C library so we use C-style "inheritance" by + // putting libjpeg's jpeg_destination_mgr first in our custom + // struct. This allows us to cast jpeg_destination_mgr* to + // CustomJpegDestMgr* when we get it passed to us in a callback. + struct CustomJpegDestMgr : public jpeg_destination_mgr { + JOCTET *mBuffer; + size_t mBufferSize; + size_t mEncodedSize; + bool mSuccess; + } dmgr; + + jpeg_compress_struct cinfo = {}; + jpeg_error_mgr jerr; + + // Initialize error handling with standard callbacks, but + // then override output_message (to print to ALOG) and + // error_exit to set a flag and print a message instead + // of killing the whole process. + cinfo.err = jpeg_std_error(&jerr); + + cinfo.err->output_message = [](j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message)(cinfo, buffer); + ALOGE("libjpeg error: %s", buffer); + }; + + cinfo.err->error_exit = [](j_common_ptr cinfo) { + (*cinfo->err->output_message)(cinfo); + if(cinfo->client_data) { + auto & dmgr = *static_cast(cinfo->client_data); + dmgr.mSuccess = false; + } + }; + + // Now that we initialized some callbacks, let's create our compressor + jpeg_create_compress(&cinfo); + dmgr.mBuffer = static_cast(out); + dmgr.mBufferSize = maxOutSize; + dmgr.mEncodedSize = 0; + dmgr.mSuccess = true; + cinfo.client_data = static_cast(&dmgr); + + // These lambdas become C-style function pointers and as per C++11 spec + // may not capture anything. + dmgr.init_destination = [](j_compress_ptr cinfo) { + auto & dmgr = static_cast(*cinfo->dest); + dmgr.next_output_byte = dmgr.mBuffer; + dmgr.free_in_buffer = dmgr.mBufferSize; + ALOGV("%s:%d jpeg start: %p [%zu]", + __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize); + }; + + dmgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) { + ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__); + return 0; + }; + + dmgr.term_destination = [](j_compress_ptr cinfo) { + auto & dmgr = static_cast(*cinfo->dest); + dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer; + ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); + }; + cinfo.dest = reinterpret_cast(&dmgr); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + + // Initialize defaults and then override what we want + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, jpegQuality, 1); + jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); + cinfo.raw_data_in = 0; + cinfo.dct_method = JDCT_IFAST; + + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, TRUE); + + for (size_t i = 0; i < cinfo.image_height; i++) { + auto currentRow = static_cast(in + i*width); + jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1); + } + + jpeg_finish_compress(&cinfo); + + actualSize = dmgr.mEncodedSize; + if (dmgr.mSuccess) { + ret = NO_ERROR; + } else { + ret = UNKNOWN_ERROR; + } + + return ret; +} + +std::unique_ptr processDepthMapFrame(DepthPhotoInputFrame inputFrame, + std::vector> *items /*out*/) { + std::vector points, confidence; + + size_t pointCount = inputFrame.mDepthMapWidth * inputFrame.mDepthMapHeight; + points.reserve(pointCount); + confidence.reserve(pointCount); + float near = UINT16_MAX; + float far = .0f; + for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) { + for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) { + // Android densely packed depth map. The units for the range are in + // millimeters and need to be scaled to meters. + // The confidence value is encoded in the 3 most significant bits. + // The confidence data needs to be additionally normalized with + // values 1.0f, 0.0f representing maximum and minimum confidence + // respectively. + auto value = inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j]; + auto point = static_cast(value & 0x1FFF) / 1000.f; + points.push_back(point); + + auto conf = (value >> 13) & 0x7; + float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; + confidence.push_back(normConfidence); + + if (near > point) { + near = point; + } + if (far < point) { + far = point; + } + } + } + + if (near == far) { + ALOGE("%s: Near and far range values must not match!", __FUNCTION__); + return nullptr; + } + + std::vector pointsQuantized, confidenceQuantized; + pointsQuantized.reserve(pointCount); confidenceQuantized.reserve(pointCount); + auto pointIt = points.begin(); + auto confidenceIt = confidence.begin(); + while ((pointIt != points.end()) && (confidenceIt != confidence.end())) { + pointsQuantized.push_back(floorf(((far * (*pointIt - near)) / + (*pointIt * (far - near))) * 255.0f)); + confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f)); + confidenceIt++; pointIt++; + } + + DepthMapParams depthParams(DepthFormat::kRangeInverse, near, far, DepthUnits::kMeters, + "android/depthmap"); + depthParams.confidence_uri = "android/confidencemap"; + depthParams.mime = "image/jpeg"; + depthParams.depth_image_data.resize(inputFrame.mMaxJpegSize); + depthParams.confidence_data.resize(inputFrame.mMaxJpegSize); + size_t actualJpegSize; + auto ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, + pointsQuantized.data(), depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, + inputFrame.mJpegQuality, actualJpegSize); + if (ret != NO_ERROR) { + ALOGE("%s: Depth map compression failed!", __FUNCTION__); + return nullptr; + } + depthParams.depth_image_data.resize(actualJpegSize); + + ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, + confidenceQuantized.data(), depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, + inputFrame.mJpegQuality, actualJpegSize); + if (ret != NO_ERROR) { + ALOGE("%s: Confidence map compression failed!", __FUNCTION__); + return nullptr; + } + depthParams.confidence_data.resize(actualJpegSize); + + return DepthMap::FromData(depthParams, items); +} + +extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t depthPhotoBufferSize, + void* depthPhotoBuffer /*out*/, size_t* depthPhotoActualSize /*out*/) { + if ((inputFrame.mMainJpegBuffer == nullptr) || (inputFrame.mDepthMapBuffer == nullptr) || + (depthPhotoBuffer == nullptr) || (depthPhotoActualSize == nullptr)) { + return BAD_VALUE; + } + + std::vector> items; + std::vector> cameraList; + auto image = Image::FromDataForPrimaryImage("android/mainimage", &items); + std::unique_ptr cameraParams(new CameraParams(std::move(image))); + if (cameraParams == nullptr) { + ALOGE("%s: Failed to initialize camera parameters", __FUNCTION__); + return BAD_VALUE; + } + + cameraParams->depth_map = processDepthMapFrame(inputFrame, &items); + if (cameraParams->depth_map == nullptr) { + ALOGE("%s: Depth map processing failed!", __FUNCTION__); + return BAD_VALUE; + } + + // It is not possible to generate an imaging model without instrinsic calibration. + if (inputFrame.mIsInstrinsicCalibrationValid) { + // The camera intrinsic calibration layout is as follows: + // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] + const dynamic_depth::Point focalLength(inputFrame.mInstrinsicCalibration[0], + inputFrame.mInstrinsicCalibration[1]); + const Dimension imageSize(inputFrame.mMainJpegWidth, inputFrame.mMainJpegHeight); + ImagingModelParams imagingParams(focalLength, imageSize); + imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2]; + imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3]; + imagingParams.skew = inputFrame.mInstrinsicCalibration[4]; + + // The camera lens distortion contains the following lens correction coefficients. + // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] + if (inputFrame.mIsLensDistortionValid) { + // According to specification the lens distortion coefficients should be ordered + // as [1, kappa_4, kappa_1, kappa_5, kappa_2, 0, kappa_3, 0] + float distortionData[] = {1.f, inputFrame.mLensDistortion[3], + inputFrame.mLensDistortion[0], inputFrame.mLensDistortion[4], + inputFrame.mLensDistortion[1], 0.f, inputFrame.mLensDistortion[2], 0.f}; + auto distortionDataLength = sizeof(distortionData) / sizeof(distortionData[0]); + imagingParams.distortion.reserve(distortionDataLength); + imagingParams.distortion.insert(imagingParams.distortion.end(), distortionData, + distortionData + distortionDataLength); + } + + cameraParams->imaging_model = ImagingModel::FromData(imagingParams); + } + + if (inputFrame.mIsLogical) { + cameraParams->trait = dynamic_depth::CameraTrait::LOGICAL; + } else { + cameraParams->trait = dynamic_depth::CameraTrait::PHYSICAL; + } + + cameraList.emplace_back(Camera::FromData(std::move(cameraParams))); + + auto deviceParams = std::make_unique (Cameras::FromCameraArray(&cameraList)); + deviceParams->container = Container::FromItems(&items); + std::vector> profileList; + profileList.emplace_back(Profile::FromData("DepthPhoto", {0})); + deviceParams->profiles = Profiles::FromProfileArray(&profileList); + std::unique_ptr device = Device::FromData(std::move(deviceParams)); + if (device == nullptr) { + ALOGE("%s: Failed to initialize camera device", __FUNCTION__); + return BAD_VALUE; + } + + std::istringstream inputJpegStream( + std::string(inputFrame.mMainJpegBuffer, inputFrame.mMainJpegSize)); + std::ostringstream outputJpegStream; + if (!WriteImageAndMetadataAndContainer(&inputJpegStream, device.get(), &outputJpegStream)) { + ALOGE("%s: Failed writing depth output", __FUNCTION__); + return BAD_VALUE; + } + + *depthPhotoActualSize = static_cast (outputJpegStream.tellp()); + if (*depthPhotoActualSize > depthPhotoBufferSize) { + ALOGE("%s: Depth photo output buffer not sufficient, needed %zu actual %zu", __FUNCTION__, + *depthPhotoActualSize, depthPhotoBufferSize); + return NO_MEMORY; + } + + memcpy(depthPhotoBuffer, outputJpegStream.str().c_str(), *depthPhotoActualSize); + + return 0; +} + +}; // namespace camera3 +}; // namespace android diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h new file mode 100644 index 0000000000..19889a1f3d --- /dev/null +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 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_SERVERS_CAMERA_CAMERA3_DEPTH_PROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA3_DEPTH_PROCESSOR_H + +#include +#include + +namespace android { +namespace camera3 { + +struct DepthPhotoInputFrame { + const char* mMainJpegBuffer; + size_t mMainJpegSize; + size_t mMainJpegWidth, mMainJpegHeight; + uint16_t* mDepthMapBuffer; + size_t mDepthMapWidth, mDepthMapHeight, mDepthMapStride; + size_t mMaxJpegSize; + uint8_t mJpegQuality; + uint8_t mIsLogical; + float mInstrinsicCalibration[5]; + uint8_t mIsInstrinsicCalibrationValid; + float mLensDistortion[5]; + uint8_t mIsLensDistortionValid; + + DepthPhotoInputFrame() : + mMainJpegBuffer(nullptr), + mMainJpegSize(0), + mMainJpegWidth(0), + mMainJpegHeight(0), + mDepthMapBuffer(nullptr), + mDepthMapWidth(0), + mDepthMapHeight(0), + mDepthMapStride(0), + mMaxJpegSize(0), + mJpegQuality(100), + mIsLogical(0), + mInstrinsicCalibration{0.f}, + mIsInstrinsicCalibrationValid(0), + mLensDistortion{0.f}, + mIsLensDistortionValid(0) {} +}; + +static const char *kDepthPhotoLibrary = "libdepthphoto.so"; +static const char *kDepthPhotoProcessFunction = "processDepthPhotoFrame"; +typedef int (*process_depth_photo_frame) (DepthPhotoInputFrame /*inputFrame*/, + size_t /*depthPhotoBufferSize*/, void* /*depthPhotoBuffer out*/, + size_t* /*depthPhotoActualSize out*/); + +}; // namespace camera3 +}; // namespace android + +#endif -- GitLab From 79e337367c2c7bda80a89fa188966e5934b854ea Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 25 Jan 2019 16:35:26 -0800 Subject: [PATCH 0818/1530] Remove libbinder dependency from libmedia2_jni. -BufferingSettings doesn't need to be a parcelable. -Use libstagefright_foundation_without_imemory. Test: build and MediaPlayer2Test Bug: 123361167 Change-Id: I1c02bc4ed38a697950a9fd31f9fa395a219927e4 --- media/libmedia/BufferingSettings.cpp | 20 ------------------- media/libmedia/IMediaPlayer.cpp | 12 +++++++---- .../include/media/BufferingSettings.h | 7 ++----- media/libmediaplayer2/Android.bp | 2 +- 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/media/libmedia/BufferingSettings.cpp b/media/libmedia/BufferingSettings.cpp index 271a2382d9..1cc30c232f 100644 --- a/media/libmedia/BufferingSettings.cpp +++ b/media/libmedia/BufferingSettings.cpp @@ -27,26 +27,6 @@ BufferingSettings::BufferingSettings() : mInitialMarkMs(kNoMark), mResumePlaybackMarkMs(kNoMark) { } -status_t BufferingSettings::readFromParcel(const Parcel* parcel) { - if (parcel == nullptr) { - return BAD_VALUE; - } - mInitialMarkMs = parcel->readInt32(); - mResumePlaybackMarkMs = parcel->readInt32(); - - return OK; -} - -status_t BufferingSettings::writeToParcel(Parcel* parcel) const { - if (parcel == nullptr) { - return BAD_VALUE; - } - parcel->writeInt32(mInitialMarkMs); - parcel->writeInt32(mResumePlaybackMarkMs); - - return OK; -} - String8 BufferingSettings::toString() const { String8 s; s.appendFormat( diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index e2eccddd90..ea066650ae 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -179,7 +179,8 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); - buffering.writeToParcel(&data); + data.writeInt32(buffering.mInitialMarkMs); + data.writeInt32(buffering.mResumePlaybackMarkMs); remote()->transact(SET_BUFFERING_SETTINGS, data, &reply); return reply.readInt32(); } @@ -194,7 +195,8 @@ public: remote()->transact(GET_BUFFERING_SETTINGS, data, &reply); status_t err = reply.readInt32(); if (err == OK) { - err = buffering->readFromParcel(&reply); + buffering->mInitialMarkMs = reply.readInt32(); + buffering->mResumePlaybackMarkMs = reply.readInt32(); } return err; } @@ -696,7 +698,8 @@ status_t BnMediaPlayer::onTransact( case SET_BUFFERING_SETTINGS: { CHECK_INTERFACE(IMediaPlayer, data, reply); BufferingSettings buffering; - buffering.readFromParcel(&data); + buffering.mInitialMarkMs = data.readInt32(); + buffering.mResumePlaybackMarkMs = data.readInt32(); reply->writeInt32(setBufferingSettings(buffering)); return NO_ERROR; } break; @@ -706,7 +709,8 @@ status_t BnMediaPlayer::onTransact( status_t err = getBufferingSettings(&buffering); reply->writeInt32(err); if (err == OK) { - buffering.writeToParcel(reply); + reply->writeInt32(buffering.mInitialMarkMs); + reply->writeInt32(buffering.mResumePlaybackMarkMs); } return NO_ERROR; } break; diff --git a/media/libmedia/include/media/BufferingSettings.h b/media/libmedia/include/media/BufferingSettings.h index d2a3e408a9..d97cc00431 100644 --- a/media/libmedia/include/media/BufferingSettings.h +++ b/media/libmedia/include/media/BufferingSettings.h @@ -17,11 +17,11 @@ #ifndef ANDROID_BUFFERING_SETTINGS_H #define ANDROID_BUFFERING_SETTINGS_H -#include +#include namespace android { -struct BufferingSettings : public Parcelable { +struct BufferingSettings { static const int kNoMark = -1; int mInitialMarkMs; @@ -32,9 +32,6 @@ struct BufferingSettings : public Parcelable { BufferingSettings(); - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - String8 toString() const; }; diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index b3f74043ac..00f537d5a8 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -21,7 +21,6 @@ cc_library_static { "libgui", "liblog", "libmedia_omx", - "libstagefright_foundation", "libui", "libutils", @@ -55,6 +54,7 @@ cc_library_static { "libmediaplayer2-protos", "libmedia_player2_util", "libprotobuf-cpp-lite", + "libstagefright_foundation_without_imemory", "libstagefright_nuplayer2", "libstagefright_player2", "libstagefright_rtsp", -- GitLab From f707b40645e852fc0ebaafd2685bccda64f439e5 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 25 Jan 2019 09:08:42 -0800 Subject: [PATCH 0819/1530] Revert "Revert "Fix MediaPlayer2Test on 32bit mode"" This reverts commit 191019ff4ec1129e6f0643b847268b5553a3383f. Test: make systemimage on sailfish Bug: 123361167 Change-Id: Ia65af173ebcc94a07463f4b269e648b87be21dec --- apex/Android.bp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 39997d2ea6..c077a77e25 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -16,21 +16,31 @@ apex { name: "com.android.media", manifest: "manifest.json", java_libs: ["updatable-media"], - native_shared_libs: [ - // Extractor plugins - "libaacextractor", - "libamrextractor", - "libflacextractor", - "libmidiextractor", - "libmkvextractor", - "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", - "libwavextractor", - // MediaPlayer2 - "libmedia2_jni", - ], + compile_multilib: "both", + multilib: { + first: { + // Extractor process runs only with the primary ABI. + native_shared_libs: [ + // Extractor plugins + "libaacextractor", + "libamrextractor", + "libflacextractor", + "libmidiextractor", + "libmkvextractor", + "libmp3extractor", + "libmp4extractor", + "libmpeg2extractor", + "liboggextractor", + "libwavextractor", + ], + }, + both: { + native_shared_libs: [ + // MediaPlayer2 + "libmedia2_jni", + ], + }, + }, key: "com.android.media.key", } -- GitLab From e7d8a7101b6ad97b84f7286d4433f3d34c00538f Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 11 Jan 2019 23:10:32 +0800 Subject: [PATCH 0820/1530] Make mp4 file support mp3 audio There are many mp4 files with mp3 audio in real world. We need to be able to play these mp4 files. Bug: 122710842 Test: Play a mp4 file with mp3 audio and check if there is sound Change-Id: Ide6ed2fbe65039bb96eea5de3d650bb6981b578b --- media/extractors/mp4/MPEG4Extractor.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) mode change 100644 => 100755 media/extractors/mp4/MPEG4Extractor.cpp diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp old mode 100644 new mode 100755 index d0efdddb2d..7d155fefed --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -345,6 +345,9 @@ static const char *FourCC2MIME(uint32_t fourcc) { case FOURCC("av01"): return MEDIA_MIMETYPE_VIDEO_AV1; + case FOURCC(".mp3"): + case 0x6D730055: // "ms U" mp3 audio + return MEDIA_MIMETYPE_AUDIO_MPEG; default: ALOGW("Unknown fourcc: %c%c%c%c", (fourcc >> 24) & 0xff, @@ -1629,6 +1632,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("twos"): case FOURCC("sowt"): case FOURCC("alac"): + case FOURCC(".mp3"): + case 0x6D730055: // "ms U" mp3 audio { if (mIsQT && chunk_type == FOURCC("mp4a") && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) { @@ -4085,12 +4090,10 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return OK; } - if (objectTypeIndication == 0x6b) { - // The media subtype is MP3 audio - // Our software MP3 audio decoder may not be able to handle - // packetized MP3 audio; for now, lets just return ERROR_UNSUPPORTED - ALOGE("MP3 track in MP4/3GPP file is not supported"); - return ERROR_UNSUPPORTED; + if (objectTypeIndication == 0x6B || objectTypeIndication == 0x69) { + // mp3 audio + AMediaFormat_setString(mLastTrack->meta,AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG); + return OK; } if (mLastTrack != NULL) { -- GitLab From 2d951098fde1760d70e4763e6b157d9000892bb8 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 10 Jan 2019 14:47:10 +0800 Subject: [PATCH 0821/1530] Sample index in stsc is bigger than samplecount in stsz When sample index in stsc is bigger than samplecount in stsz, it returns error and video playing stops. Solution is to align samplecount in stsz. Bug: 122746529 Test: Play the file whose sample index in stsc box is bigger than samplecount in stsz box and check if it can play normally Change-Id: I2f09d6d17ff6bd04df076d744a25094c12524f19 --- media/extractors/mp4/SampleIterator.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/media/extractors/mp4/SampleIterator.cpp b/media/extractors/mp4/SampleIterator.cpp index ec12130904..2890b26301 100644 --- a/media/extractors/mp4/SampleIterator.cpp +++ b/media/extractors/mp4/SampleIterator.cpp @@ -111,8 +111,15 @@ status_t SampleIterator::seekTo(uint32_t sampleIndex) { if ((err = getSampleSizeDirect( firstChunkSampleIndex + i, &sampleSize)) != OK) { ALOGE("getSampleSizeDirect return error"); - mCurrentChunkSampleSizes.clear(); - return err; + // stsc sample count is not sync with stsz sample count + if (err == ERROR_OUT_OF_RANGE) { + ALOGW("stsc samples(%d) not sync with stsz samples(%d)", mSamplesPerChunk, i); + mSamplesPerChunk = i; + break; + } else{ + mCurrentChunkSampleSizes.clear(); + return err; + } } mCurrentChunkSampleSizes.push(sampleSize); -- GitLab From c41dc110a1fe7418a4118f3541d15d4ac703ad72 Mon Sep 17 00:00:00 2001 From: Mingwei Shi Date: Wed, 26 Jul 2017 10:02:40 +0800 Subject: [PATCH 0822/1530] PFW: support to load the config policy from vendor partition For treble compliance, loading the config policy from vendor partition should be supported. Bug: 80626872 Test: audio works normally with USE_CONFIGURABLE_AUDIO_POLICY set Change-Id: I7fec31346b80874c323556e28debb4a0888b27e6 Signed-off-by: Mingwei Shi Signed-off-by: Qin, Xiaokang (cherry picked from commit 955e35cb670ff210535be268aa3589d68d92b148) --- .../wrapper/ParameterManagerWrapper.cpp | 8 +++++++- .../wrapper/include/ParameterManagerWrapper.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index 1934fa4c3b..09faa4c448 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -65,6 +65,8 @@ namespace audio_policy { const char *const ParameterManagerWrapper::mPolicyPfwDefaultConfFileName = "/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml"; +const char *const ParameterManagerWrapper::mPolicyPfwVendorConfFileName = + "/vendor/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml"; static const char *const gInputDeviceCriterionName = "AvailableInputDevices"; static const char *const gOutputDeviceCriterionName = "AvailableOutputDevices"; @@ -96,7 +98,11 @@ ParameterManagerWrapper::ParameterManagerWrapper() : mPfwConnectorLogger(new ParameterMgrPlatformConnectorLogger) { // Connector - mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwDefaultConfFileName); + if (access(mPolicyPfwVendorConfFileName, R_OK) == 0) { + mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwVendorConfFileName); + } else { + mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwDefaultConfFileName); + } // Logger mPfwConnector->setLogger(mPfwConnectorLogger); diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h index 1a634a1d54..cd39b6f431 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h +++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h @@ -199,6 +199,7 @@ private: struct parameterManagerElementSupported; static const char *const mPolicyPfwDefaultConfFileName; /**< Default Policy PFW top file name.*/ + static const char *const mPolicyPfwVendorConfFileName; /**< Vendor Policy PFW top file name.*/ }; } // namespace audio_policy -- GitLab From 7c182f9f00f0a84562a38467340b362a612068bc Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 10 Jan 2019 16:11:51 +0800 Subject: [PATCH 0823/1530] Cannot play 3gp file which esds box is over 256 bytes The size of chunk box "esds" is over 256 bytes, which is Android default maximum size. There is much rubbish data in esds box except valid data. MPEG4Extractor uses uint8_t buffer[256] to handle esds box buffer, but sometimes it is not enough, so it returns error and cannot play. Solution: remove size check code and let malloc to prevent too large size. Bug: 122746531 Test: Play a video file whose esds size is over 256 bytes and check if it can play normally Change-Id: I2384bf8fc54e3f0c3a7d1351896bb408560b6490 --- media/extractors/mp4/MPEG4Extractor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 7d155fefed..237f9c8de4 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -2101,9 +2101,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; } - uint8_t buffer[256]; - if (chunk_data_size > (off64_t)sizeof(buffer)) { - return ERROR_BUFFER_TOO_SMALL; + auto tmp = heapbuffer(chunk_data_size); + uint8_t *buffer = tmp.get(); + if (buffer == NULL) { + return -ENOMEM; } if (mDataSource->readAt( -- GitLab From daca30ef426e30f921a6a2d39e07a31d789b837e Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Fri, 25 Jan 2019 14:59:54 -0800 Subject: [PATCH 0824/1530] mediaplayer2: allow prepare DRM in non-prepare states Required because mediaplayer2 needs to prepare DRM for pending playlist items while mediaplayer2 is in `started` state. Bug: 123425309 Test: MediaPlayer2DrmTest Change-Id: I20bd2afdc415e716a20cfca7c6b97d62e599aaad --- media/libmediaplayer2/mediaplayer2.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index 53f2fb12c8..ae7ac59993 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -1124,8 +1124,10 @@ status_t MediaPlayer2::prepareDrm( // completed) so the state change to "prepared" might not have happened yet (e.g., buffering). // Still, we can allow prepareDrm for the use case of being called in OnDrmInfoListener. if (!(mCurrentState & (MEDIA_PLAYER2_PREPARING | MEDIA_PLAYER2_PREPARED))) { - ALOGE("prepareDrm is called in the wrong state (%d).", mCurrentState); - return INVALID_OPERATION; + ALOGW("prepareDrm(%lld) called in non-prepare state(%d)", (long long)srcId, mCurrentState); + if (srcId == mSrcId) { + return INVALID_OPERATION; + } } if (drmSessionId.isEmpty()) { -- GitLab From 353e167d7f8a32970f5cd130ad99aea1c52e2499 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 7 Jan 2019 16:31:29 -0800 Subject: [PATCH 0825/1530] audio encoders: fix initial timestamp Use timestamp from the first non-empty work, not the very first one which may be empty with an invalid timestamp. Bug: 123428627 Test: atest CtsMediaTestCases:MediaRecorderTest Change-Id: I50f68765355b1fd3af4241adad0c6199fd7b4de8 --- media/codec2/components/aac/C2SoftAacEnc.cpp | 9 ++++++++- media/codec2/components/aac/C2SoftAacEnc.h | 1 + media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp | 2 +- media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp | 2 +- media/codec2/components/opus/C2SoftOpusEnc.cpp | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index 87730ae328..d1bdf0d513 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -151,6 +151,7 @@ C2SoftAacEnc::C2SoftAacEnc( mNumBytesPerInputFrame(0u), mOutBufferSize(0u), mSentCodecSpecificData(false), + mInputTimeSet(false), mInputSize(0), mInputTimeUs(-1ll), mSignalledError(false), @@ -176,6 +177,7 @@ status_t C2SoftAacEnc::initEncoder() { c2_status_t C2SoftAacEnc::onStop() { mSentCodecSpecificData = false; + mInputTimeSet = false; mInputSize = 0u; mInputTimeUs = -1ll; mSignalledError = false; @@ -193,6 +195,7 @@ void C2SoftAacEnc::onRelease() { c2_status_t C2SoftAacEnc::onFlush_sm() { mSentCodecSpecificData = false; + mInputTimeSet = false; mInputSize = 0u; return C2_OK; } @@ -337,7 +340,6 @@ void C2SoftAacEnc::process( mOutBufferSize = encInfo.maxOutBufBytes; mNumBytesPerInputFrame = encInfo.frameLength * channelCount * sizeof(int16_t); - mInputTimeUs = work->input.ordinal.timestamp; mSentCodecSpecificData = true; } @@ -351,6 +353,10 @@ void C2SoftAacEnc::process( data = view.data(); capacity = view.capacity(); } + if (!mInputTimeSet && capacity > 0) { + mInputTimeUs = work->input.ordinal.timestamp; + mInputTimeSet = true; + } size_t numFrames = (capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0)) / mNumBytesPerInputFrame; @@ -550,6 +556,7 @@ c2_status_t C2SoftAacEnc::drain( (void)pool; mSentCodecSpecificData = false; + mInputTimeSet = false; mInputSize = 0u; // TODO: we don't have any pending work at this time to drain. diff --git a/media/codec2/components/aac/C2SoftAacEnc.h b/media/codec2/components/aac/C2SoftAacEnc.h index 82fb4384a9..779365bf61 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.h +++ b/media/codec2/components/aac/C2SoftAacEnc.h @@ -57,6 +57,7 @@ private: UINT mOutBufferSize; bool mSentCodecSpecificData; + bool mInputTimeSet; size_t mInputSize; c2_cntr64_t mInputTimeUs; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp index ca21480cba..8c03257a74 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp @@ -271,7 +271,7 @@ void C2SoftAmrNbEnc::process( mFilledLen = 0; } ALOGV("causal sample size %d", mFilledLen); - if (mIsFirst) { + if (mIsFirst && outPos != 0) { mIsFirst = false; mAnchorTimeStamp = work->input.ordinal.timestamp.peekull(); } diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp index be3892fcd7..074493c42a 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp @@ -347,7 +347,7 @@ void C2SoftAmrWbEnc::process( mFilledLen = 0; } ALOGV("causal sample size %d", mFilledLen); - if (mIsFirst) { + if (mIsFirst && outPos != 0) { mIsFirst = false; mAnchorTimeStamp = work->input.ordinal.timestamp.peekull(); } diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index d6ed5ffc3e..68fcea14e1 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -350,7 +350,7 @@ void C2SoftOpusEnc::process(const std::unique_ptr& work, return; } } - if (mIsFirstFrame) { + if (mIsFirstFrame && inSize > 0) { mAnchorTimeStamp = work->input.ordinal.timestamp.peekull(); mIsFirstFrame = false; } -- GitLab From 48f59ed61b902f0e6cb7811bd8ee5fa60ab63abe Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 28 Jan 2019 15:06:59 -0800 Subject: [PATCH 0826/1530] AudioFlinger: Fix VolumeShaper initialization for DirectOutputThread Avoids temporary frequent polling after creation. Offload not affected. Test: compile Change-Id: I868a80ee25553c16083510ef6b79782c414a39a1 --- services/audioflinger/Threads.cpp | 9 +-------- services/audioflinger/Threads.h | 8 +++++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 31a8c7d130..9f838a3db8 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5329,16 +5329,9 @@ void AudioFlinger::MixerThread::cacheParameters_l() // ---------------------------------------------------------------------------- AudioFlinger::DirectOutputThread::DirectOutputThread(const sp& audioFlinger, - AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady) - : PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady) -{ -} - -AudioFlinger::DirectOutputThread::DirectOutputThread(const sp& audioFlinger, - AudioStreamOut* output, audio_io_handle_t id, uint32_t device, + AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, ThreadBase::type_t type, bool systemReady) : PlaybackThread(audioFlinger, output, id, device, type, systemReady) - , mVolumeShaperActive(false) { } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index aab760168a..8b8222c0cd 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1205,7 +1205,9 @@ class DirectOutputThread : public PlaybackThread { public: DirectOutputThread(const sp& audioFlinger, AudioStreamOut* output, - audio_io_handle_t id, audio_devices_t device, bool systemReady); + audio_io_handle_t id, audio_devices_t device, bool systemReady) + : DirectOutputThread(audioFlinger, output, id, device, DIRECT, systemReady) { } + virtual ~DirectOutputThread(); status_t selectPresentation(int presentationId, int programId); @@ -1231,10 +1233,10 @@ protected: virtual void onAddNewTrack_l(); - bool mVolumeShaperActive; + bool mVolumeShaperActive = false; DirectOutputThread(const sp& audioFlinger, AudioStreamOut* output, - audio_io_handle_t id, uint32_t device, ThreadBase::type_t type, + audio_io_handle_t id, audio_devices_t device, ThreadBase::type_t type, bool systemReady); void processVolume_l(Track *track, bool lastTrack); -- GitLab From 3fae037db39ff89707c3a58ef1419229a00ad71d Mon Sep 17 00:00:00 2001 From: Richard Folke Tullberg Date: Fri, 13 Jan 2017 09:04:25 +0100 Subject: [PATCH 0827/1530] Add master audio balance Test: Change Balance through Settings, play audio Bug: 28390736 Co-author: Ed Savage-Jones Change-Id: I0169b436ccbaa5628584d9f4954dd7c76d021aae --- media/libaudioclient/AudioSystem.cpp | 14 +++++ media/libaudioclient/IAudioFlinger.cpp | 45 ++++++++++++++++ .../include/media/AudioSystem.h | 3 ++ .../include/media/IAudioFlinger.h | 3 ++ services/audioflinger/AudioFlinger.cpp | 46 +++++++++++++++++ services/audioflinger/AudioFlinger.h | 6 +++ services/audioflinger/FastMixer.cpp | 12 +++-- services/audioflinger/FastMixer.h | 8 ++- services/audioflinger/Threads.cpp | 51 +++++++++++++++++-- services/audioflinger/Threads.h | 19 +++++++ 10 files changed, 198 insertions(+), 9 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 4c762eddcf..896198bfb2 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1281,6 +1281,20 @@ status_t AudioSystem::getMasterMono(bool *mono) return aps->getMasterMono(mono); } +status_t AudioSystem::setMasterBalance(float balance) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMasterBalance(balance); +} + +status_t AudioSystem::getMasterBalance(float *balance) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->getMasterBalance(balance); +} + float AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device) { const sp& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 00678c2371..825cd4ec3d 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -87,6 +87,8 @@ enum { SYSTEM_READY, FRAME_COUNT_HAL, GET_MICROPHONES, + SET_MASTER_BALANCE, + GET_MASTER_BALANCE, }; #define MAX_ITEMS_PER_LIST 1024 @@ -242,6 +244,34 @@ public: return reply.readInt32(); } + status_t setMasterBalance(float balance) override + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeFloat(balance); + status_t status = remote()->transact(SET_MASTER_BALANCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + return reply.readInt32(); + } + + status_t getMasterBalance(float *balance) const override + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_BALANCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = (status_t)reply.readInt32(); + if (status != NO_ERROR) { + return status; + } + *balance = reply.readFloat(); + return NO_ERROR; + } + virtual status_t setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { @@ -1050,6 +1080,21 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( masterMute() ); return NO_ERROR; } break; + case SET_MASTER_BALANCE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterBalance(data.readFloat()) ); + return NO_ERROR; + } break; + case GET_MASTER_BALANCE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + float f; + const status_t status = getMasterBalance(&f); + reply->writeInt32((int32_t)status); + if (status == NO_ERROR) { + (void)reply->writeFloat(f); + } + return NO_ERROR; + } break; case SET_STREAM_VOLUME: { CHECK_INTERFACE(IAudioFlinger, data, reply); int stream = data.readInt32(); diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index a208602a32..1fb7add602 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -339,6 +339,9 @@ public: static status_t setMasterMono(bool mono); static status_t getMasterMono(bool *mono); + static status_t setMasterBalance(float balance); + static status_t getMasterBalance(float *balance); + static float getStreamVolumeDB( audio_stream_type_t stream, int index, audio_devices_t device); diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index a34b207cc6..ef0ed0c560 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -359,6 +359,9 @@ public: virtual float masterVolume() const = 0; virtual bool masterMute() const = 0; + virtual status_t setMasterBalance(float balance) = 0; + virtual status_t getMasterBalance(float *balance) const = 0; + /* set/get stream type state. This will probably be used by * the preference panel, mostly. */ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0d6ef46b4c..b8307ce1b3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -897,6 +897,40 @@ status_t AudioFlinger::setMasterVolume(float value) return NO_ERROR; } +status_t AudioFlinger::setMasterBalance(float balance) +{ + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // check range + if (isnan(balance) || fabs(balance) > 1.f) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // short cut. + if (mMasterBalance == balance) return NO_ERROR; + + mMasterBalance = balance; + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isDuplicating()) { + continue; + } + mPlaybackThreads.valueAt(i)->setMasterBalance(balance); + } + + return NO_ERROR; +} + status_t AudioFlinger::setMode(audio_mode_t mode) { status_t ret = initCheck(); @@ -1036,6 +1070,13 @@ float AudioFlinger::masterVolume() const return masterVolume_l(); } +status_t AudioFlinger::getMasterBalance(float *balance) const +{ + Mutex::Autolock _l(mLock); + *balance = getMasterBalance_l(); + return NO_ERROR; // if called through binder, may return a transactional error +} + bool AudioFlinger::masterMute() const { Mutex::Autolock _l(mLock); @@ -1047,6 +1088,11 @@ float AudioFlinger::masterVolume_l() const return mMasterVolume; } +float AudioFlinger::getMasterBalance_l() const +{ + return mMasterBalance; +} + bool AudioFlinger::masterMute_l() const { return mMasterMute; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index c1169d2806..7e3a77547e 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -137,6 +137,10 @@ public: virtual float masterVolume() const; virtual bool masterMute() const; + // Balance value must be within -1.f (left only) to 1.f (right only) inclusive. + status_t setMasterBalance(float balance) override; + status_t getMasterBalance(float *balance) const override; + virtual status_t setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output); virtual status_t setStreamMute(audio_stream_type_t stream, bool muted); @@ -776,6 +780,7 @@ using effect_buffer_t = int16_t; // member variables below are protected by mLock float mMasterVolume; bool mMasterMute; + float mMasterBalance = 0.f; // end of variables protected by mLock DefaultKeyedVector< audio_io_handle_t, sp > mRecordThreads; @@ -793,6 +798,7 @@ using effect_buffer_t = int16_t; Vector mAudioSessionRefs; float masterVolume_l() const; + float getMasterBalance_l() const; bool masterMute_l() const; audio_module_handle_t loadHwModule_l(const char *name); diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index f328577456..7c9e15a0eb 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -60,7 +60,6 @@ FastMixer::FastMixer(audio_io_handle_t parentIoHandle) mSinkChannelCount(FCC_2), mMixerBuffer(NULL), mMixerBufferSize(0), - mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), mMixerBufferState(UNDEFINED), mFormat(Format_Invalid), mSampleRate(0), @@ -161,6 +160,7 @@ void FastMixer::onStateChange() mOutputSink = current->mOutputSink; mOutputSinkGen = current->mOutputSinkGen; mSinkChannelMask = current->mSinkChannelMask; + mBalance.setChannelMask(mSinkChannelMask); if (mOutputSink == NULL) { mFormat = Format_Invalid; mSampleRate = 0; @@ -191,10 +191,6 @@ void FastMixer::onStateChange() free(mSinkBuffer); mSinkBuffer = NULL; if (frameCount > 0 && mSampleRate > 0) { - // The mixer produces either 16 bit PCM or float output, select - // float output if the HAL supports higher than 16 bit precision. - mMixerBufferFormat = mFormat.mFormat == AUDIO_FORMAT_PCM_16_BIT ? - AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT; // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion @@ -471,6 +467,12 @@ void FastMixer::onWork() mono_blend(mMixerBuffer, mMixerBufferFormat, Format_channelCount(mFormat), frameCount, true /*limit*/); } + + // Balance must take effect after mono conversion. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mMixerBuffer, frameCount); + // prepare the buffer used to write to sink void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 1d332e01af..c31d476c48 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_FAST_MIXER_H #include +#include #include "FastThread.h" #include "StateQueue.h" #include "FastMixerState.h" @@ -41,6 +42,8 @@ public: FastMixerStateQueue* sq(); virtual void setMasterMono(bool mono) { mMasterMono.store(mono); /* memory_order_seq_cst */ } + virtual void setMasterBalance(float balance) { mMasterBalance.store(balance); } + virtual float getMasterBalance() const { return mMasterBalance.load(); } virtual void setBoottimeOffset(int64_t boottimeOffset) { mBoottimeOffset.store(boottimeOffset); /* memory_order_seq_cst */ } @@ -74,7 +77,7 @@ private: audio_channel_mask_t mSinkChannelMask; void* mMixerBuffer; // mixer output buffer. size_t mMixerBufferSize; - audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + static constexpr audio_format_t mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; uint32_t mAudioChannelCount; // audio channel count, excludes haptic channels. @@ -89,8 +92,11 @@ private: ExtendedTimestamp mTimestamp; int64_t mNativeFramesWrittenButNotPresented; + audio_utils::Balance mBalance; + // accessed without lock between multiple threads. std::atomic_bool mMasterMono; + std::atomic mMasterBalance{}; std::atomic_int_fast64_t mBoottimeOffset; const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9f838a3db8..1790f112cf 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -2271,6 +2272,11 @@ void AudioFlinger::PlaybackThread::setMasterVolume(float value) } } +void AudioFlinger::PlaybackThread::setMasterBalance(float balance) +{ + mMasterBalance.store(balance); +} + void AudioFlinger::PlaybackThread::setMasterMute(bool muted) { if (isDuplicating()) { @@ -2523,6 +2529,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() mChannelMask); } mChannelCount = audio_channel_count_from_out_mask(mChannelMask); + mBalance.setChannelMask(mChannelMask); // Get actual HAL format. status_t result = mOutput->stream->getFormat(&mHALFormat); @@ -2642,7 +2649,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() free(mMixerBuffer); mMixerBuffer = NULL; if (mMixerBufferEnabled) { - mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT. + mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // no longer valid: AUDIO_FORMAT_PCM_16_BIT. mMixerBufferSize = mNormalFrameCount * mChannelCount * audio_bytes_per_sample(mMixerBufferFormat); (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); @@ -3531,6 +3538,14 @@ bool AudioFlinger::PlaybackThread::threadLoop() true /*limit*/); } + if (!hasFastMixer()) { + // Balance must take effect after mono conversion. + // We do it here if there is no FastMixer. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mMixerBuffer, mNormalFrameCount); + } + memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, mNormalFrameCount * (mChannelCount + mHapticChannelCount)); @@ -3585,6 +3600,14 @@ bool AudioFlinger::PlaybackThread::threadLoop() true /*limit*/); } + if (!hasFastMixer()) { + // Balance must take effect after mono conversion. + // We do it here if there is no FastMixer. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mEffectBuffer, mNormalFrameCount); + } + memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, mNormalFrameCount * (mChannelCount + mHapticChannelCount)); // The sample data is partially interleaved when haptic channels exist, @@ -3985,6 +4008,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // mPipeSink below // mNormalSink below { + setMasterBalance(audioFlinger->getMasterBalance_l()); ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type); ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, " "mFrameCount=%zu, mNormalFrameCount=%zu", @@ -5266,6 +5290,9 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs); dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); + dprintf(fd, " Master balance: %f (%s)\n", mMasterBalance.load(), + (hasFastMixer() ? std::to_string(mFastMixer->getMasterBalance()) + : mBalance.toString()).c_str()); const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); if (latencyMs != 0.) { dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); @@ -5333,12 +5360,30 @@ AudioFlinger::DirectOutputThread::DirectOutputThread(const sp& aud ThreadBase::type_t type, bool systemReady) : PlaybackThread(audioFlinger, output, id, device, type, systemReady) { + setMasterBalance(audioFlinger->getMasterBalance_l()); } AudioFlinger::DirectOutputThread::~DirectOutputThread() { } +void AudioFlinger::DirectOutputThread::dumpInternals(int fd, const Vector& args) +{ + PlaybackThread::dumpInternals(fd, args); + dprintf(fd, " Master balance: %f Left: %f Right: %f\n", + mMasterBalance.load(), mMasterBalanceLeft, mMasterBalanceRight); +} + +void AudioFlinger::DirectOutputThread::setMasterBalance(float balance) +{ + Mutex::Autolock _l(mLock); + if (mMasterBalance != balance) { + mMasterBalance.store(balance); + mBalance.computeStereoBalance(balance, &mMasterBalanceLeft, &mMasterBalanceRight); + broadcast_l(); + } +} + void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack) { float left, right; @@ -5362,12 +5407,12 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr if (left > GAIN_FLOAT_UNITY) { left = GAIN_FLOAT_UNITY; } - left *= v; + left *= v * mMasterBalanceLeft; // DirectOutputThread balance applied as track volume right = float_from_gain(gain_minifloat_unpack_right(vlr)); if (right > GAIN_FLOAT_UNITY) { right = GAIN_FLOAT_UNITY; } - right *= v; + right *= v * mMasterBalanceRight; } if (lastTrack) { diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 8b8222c0cd..1131b266bc 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -733,6 +733,7 @@ public: // VolumeInterface virtual void setMasterVolume(float value); + virtual void setMasterBalance(float balance); virtual void setMasterMute(bool muted); virtual void setStreamVolume(audio_stream_type_t stream, float value); virtual void setStreamMute(audio_stream_type_t stream, bool muted); @@ -1027,6 +1028,8 @@ private: AudioStreamOut *mOutput; float mMasterVolume; + std::atomic mMasterBalance{}; + audio_utils::Balance mBalance; nsecs_t mLastWriteTime; int mNumWrites; int mNumDelayedWrites; @@ -1199,6 +1202,13 @@ protected: // Blending with limiter is not idempotent, // and blending without limiter is idempotent but inefficient to do twice. virtual bool requireMonoBlend() { return mMasterMono.load() && !hasFastMixer(); } + + void setMasterBalance(float balance) override { + mMasterBalance.store(balance); + if (hasFastMixer()) { + mFastMixer->setMasterBalance(balance); + } + } }; class DirectOutputThread : public PlaybackThread { @@ -1216,8 +1226,13 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); + + void dumpInternals(int fd, const Vector& args) override; + virtual void flushHw_l(); + void setMasterBalance(float balance) override; + protected: virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; @@ -1245,6 +1260,10 @@ protected: wp mPreviousTrack; // used to detect track switch + // This must be initialized for initial condition of mMasterBalance = 0 (disabled). + float mMasterBalanceLeft = 1.f; + float mMasterBalanceRight = 1.f; + public: virtual bool hasFastMixer() const { return false; } -- GitLab From 07095df02a11678cf4637ead28bc5c8c5d27db2a Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Tue, 29 Jan 2019 06:28:58 -0800 Subject: [PATCH 0828/1530] DepthCompositeStream: Fix erase() in error case We had a typo for which vector we were erase()ing from in an error case. We fix that typo here. Test: TreeHugger Change-Id: Id1d38a5c1955e19415ca2e1c84a3f58763bf83c8 --- services/camera/libcameraservice/api2/DepthCompositeStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index 1cf9529476..f627b25100 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -128,7 +128,7 @@ void DepthCompositeStream::compilePendingInputLocked() { ALOGE("%s: Error locking blob image buffer: %s (%d)", __FUNCTION__, strerror(-res), res); mPendingInputFrames[*it].error = true; - mInputDepthBuffers.erase(it); + mInputJpegBuffers.erase(it); continue; } -- GitLab From 1439ac2a601c469f142d0a01ecae4b9223c3fd73 Mon Sep 17 00:00:00 2001 From: mtk08122 Date: Mon, 14 Jan 2019 11:00:10 +0800 Subject: [PATCH 0829/1530] Return first data packet when seek to begin Problem: When play an ogg file and seek to begin, the packet which send to vorbis decoder might be not integral and it would cause noise issue. Solution: Return first data packet when seek to begin. Bug: 122921499 Test: Play ogg file and then seek to begin Change-Id: Idb89913262ff21add86b1eae75f72a1e390632e9 --- media/extractors/ogg/OggExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index 596c1c8ced..ba40690327 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -983,7 +983,7 @@ void MyOggExtractor::buildTableOfContents() { size_t denom = numerator - kMaxNumTOCEntries; size_t accum = 0; - for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) { + for (ssize_t i = mTableOfContents.size(); i > 0; --i) { accum += denom; if (accum >= numerator) { mTableOfContents.removeAt(i); -- GitLab From 9b1488173ac2a31a24ab7005828b9fa61a5163eb Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Sun, 27 Jan 2019 16:30:13 -0800 Subject: [PATCH 0830/1530] Codec2: Make surface change work Enable attaching output buffer from old surface to new surface. Bug: 121453508 Change-Id: Ie9a08b77f43ad787b0b6df347bd43c12493dd0de --- media/codec2/hidl/1.0/utils/types.cpp | 6 +++--- media/codec2/hidl/client/client.cpp | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index c053bc1257..a128a9d9d9 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -1693,11 +1693,11 @@ status_t attachToBufferQueue(const C2ConstGraphicBlock& block, if (result != OK) { ALOGW("attachToBufferQueue -- attachBuffer failed. Error code = %d", static_cast(result)); - return false; + return result; } ALOGV("attachToBufferQueue -- attachBuffer returned slot %d", static_cast(*bqSlot)); - return true; + return OK; } bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, @@ -1779,7 +1779,7 @@ bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, static_cast(bqSlot), static_cast(generation)); _C2BlockFactory::AssignBlockToBufferQueue( - data, getHgbp(igbp), bqId, bqSlot, true); + data, getHgbp(igbp), generation, bqId, bqSlot, true); return true; } diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index f5cc9ff65d..cd374b05d4 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -1186,7 +1186,7 @@ status_t Codec2Client::Component::queueToOutputSurface( uint32_t outputGeneration = mOutputGeneration; mOutputBufferQueueMutex.unlock(); - status_t status = !attachToBufferQueue(block, + status_t status = attachToBufferQueue(block, outputIgbp, outputGeneration, &bqSlot); @@ -1218,14 +1218,15 @@ status_t Codec2Client::Component::queueToOutputSurface( return NO_INIT; } - if (bqId != outputBqId) { - ALOGV("queueToOutputSurface -- bufferqueue ids mismatch."); - return DEAD_OBJECT; - } - - if (generation != outputGeneration) { - ALOGV("queueToOutputSurface -- generation numbers mismatch."); - return DEAD_OBJECT; + if (bqId != outputBqId || generation != outputGeneration) { + if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) { + ALOGE("queueToOutputSurface -- migration fialed"); + return DEAD_OBJECT; + } + if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) { + ALOGE("queueToOutputSurface -- corrupted bq assignment"); + return UNKNOWN_ERROR; + } } status_t status = outputIgbp->queueBuffer(static_cast(bqSlot), -- GitLab From 9c3e0dac4f3916e09204fdfbd092eccdbaf2ca65 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 18 Jan 2019 14:38:58 +0800 Subject: [PATCH 0831/1530] Cannot play mp4 file whose sawb box has sub box stts Problem: Android cannot play some special mp4 files which have sawb box and sawb box has stts subbox. Solution: Bypass the sub stts box to make file play Bug: 123057213 Test: test with the file whose sawb box has subbox stts and check if it can be played normally Change-Id: I7d27c7377914ac659e086bd3ad6286e3146aa6e9 --- media/extractors/mp4/MPEG4Extractor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 237f9c8de4..c647079c92 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1995,6 +1995,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { *offset += chunk_size; + if (depth >= 1 && mPath[depth - 1] != FOURCC("stbl")) { + char chunk[5]; + MakeFourCCString(mPath[depth - 1], chunk); + ALOGW("stts's parent box (%s) is not stbl, skip it.", chunk); + break; + } + status_t err = mLastTrack->sampleTable->setTimeToSampleParams( data_offset, chunk_data_size); -- GitLab From 186fdbfec4d678745636fa3d1029ea0d5340e823 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 29 Jan 2019 13:30:01 -0800 Subject: [PATCH 0832/1530] CCodec: harden against nullptr/out-of-memory Bug: 123571310 Test: bug repro steps Change-Id: I7c12fd375c1e320ea12473835d6d90804936a45a --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 14 ++++++++++++++ media/codec2/sfplugin/Codec2Buffer.cpp | 3 +++ 2 files changed, 17 insertions(+) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index b529cbc3d5..53ae585893 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1171,6 +1171,9 @@ public: size_t *index, sp *clientBuffer) override { sp newBuffer = wrap(buffer); + if (newBuffer == nullptr) { + return NO_MEMORY; + } newBuffer->setFormat(mFormat); *index = mImpl.assignSlot(newBuffer); *clientBuffer = newBuffer; @@ -1263,6 +1266,10 @@ public: return nullptr; } sp clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer); + if (clientBuffer == nullptr) { + ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName); + return nullptr; + } submit(clientBuffer); return clientBuffer; } @@ -1303,6 +1310,10 @@ public: [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; } else { @@ -2708,6 +2719,9 @@ void CCodecBufferChannel::sendOutputBuffers() { status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer); if (err != OK) { if (err != WOULD_BLOCK) { + if (!(*buffers)->isArrayMode()) { + *buffers = (*buffers)->toArrayMode(mNumOutputSlots); + } OutputBuffersArray *array = (OutputBuffersArray *)buffers->get(); array->realloc(entry.buffer); mCCodecCallback->onOutputBuffersChanged(); diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 597e8f3c50..2dec42ed15 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -451,6 +451,9 @@ public: } bool setBackBuffer(const sp &backBuffer) { + if (backBuffer == nullptr) { + return false; + } if (backBuffer->capacity() < mBackBufferSize) { return false; } -- GitLab From c87073d685eff98c0ed2d30139165eaecbf8ff13 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 18 Jan 2019 15:02:32 +0800 Subject: [PATCH 0833/1530] Cannot play mov file with samr box Problem: Cannot play mov file which has stsd->wave->samr. Solution: delete the judge condition "chunk_type == FOURCC('m','p','4','a')" to make other audio type can play, not only mp4a can be played. Bug: 123166627 Test: test with the file which has stsd->wave->samr and check if it can be played normally. Change-Id: I7816230af39d99b3f73085db04b8bd7acccd3a19 --- media/extractors/mp4/MPEG4Extractor.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index c647079c92..ac54116833 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1635,9 +1635,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC(".mp3"): case 0x6D730055: // "ms U" mp3 audio { - if (mIsQT && chunk_type == FOURCC("mp4a") - && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) { - // Ignore mp4a embedded in QT wave atom + if (mIsQT && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) { + // Ignore all atoms embedded in QT wave atom *offset += chunk_size; break; } @@ -1666,7 +1665,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { off64_t stop_offset = *offset + chunk_size; *offset = data_offset + sizeof(buffer); - if (mIsQT && chunk_type == FOURCC("mp4a")) { + if (mIsQT) { if (version == 1) { if (mDataSource->readAt(*offset, buffer, 16) < 16) { return ERROR_IO; -- GitLab From 43fac5f31f2692ea690a6dd50946d054e64d2b29 Mon Sep 17 00:00:00 2001 From: Zhiming Jiang Date: Tue, 22 Jan 2019 15:57:14 +0800 Subject: [PATCH 0834/1530] Compatible with npt syntax in SDP some servers send Some servers provide npt syntax in SDP as "a=range:npt:0-11" but AOSP only supports npt syntax "a=range:npt=0-11". If AOSP doesn't support syntax ':', it will result in problem for some live streaming. Bug: 123256414 Test: Play special file and check if duration is normal. Change-Id: Ie1120adf13c0e92b2e954bb4cb8cc46866d90e4b --- media/libstagefright/rtsp/ASessionDescription.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 325084ca77..c581e9d644 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -261,7 +261,7 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { return false; } - if (strncmp(value.c_str(), "npt=", 4)) { + if (strncmp(value.c_str(), "npt=", 4) && strncmp(value.c_str(), "npt:", 4)) { return false; } -- GitLab From 1c75a23d28a72f39e0d01b22c1ffc6e8034c0adc Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 9 Jan 2019 04:41:52 -0800 Subject: [PATCH 0835/1530] Initial commit for the Codec2.0 HAL Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 112362730 Change-Id: I26b99ba4b83c527c1caed7492d6d57b7056361e3 --- media/codec2/hidl/1.0/utils/Android.bp | 2 + media/codec2/hidl/1.0/utils/Component.cpp | 844 ++----------- .../hidl/1.0/utils/ComponentInterface.cpp | 110 ++ .../codec2/hidl/1.0/utils/ComponentStore.cpp | 196 +-- media/codec2/hidl/1.0/utils/Configurable.cpp | 49 +- .../hidl/1.0/utils/InputBufferManager.cpp | 461 +++++++ media/codec2/hidl/1.0/utils/InputSurface.cpp | 252 +--- .../hidl/1.0/utils/InputSurfaceConnection.cpp | 310 +++-- .../utils/include/codec2/hidl/1.0/Component.h | 86 +- .../codec2/hidl/1.0/ComponentInterface.h | 66 + .../include/codec2/hidl/1.0/ComponentStore.h | 80 +- .../include/codec2/hidl/1.0/Configurable.h | 117 +- .../codec2/hidl/1.0/ConfigurableC2Intf.h | 80 -- .../codec2/hidl/1.0/InputBufferManager.h | 294 +++++ .../include/codec2/hidl/1.0/InputSurface.h | 134 +- .../codec2/hidl/1.0/InputSurfaceConnection.h | 21 +- .../1.0/utils/include/codec2/hidl/1.0/types.h | 88 +- media/codec2/hidl/1.0/utils/types.cpp | 1085 ++++++++++------- .../vts/common/media_c2_hidl_test_common.h | 10 +- media/codec2/hidl/client/client.cpp | 820 +++++++------ .../hidl/client/include/codec2/hidl/client.h | 92 +- media/codec2/sfplugin/CCodec.cpp | 28 +- media/codec2/sfplugin/CCodecBufferChannel.cpp | 1 + 23 files changed, 2803 insertions(+), 2423 deletions(-) create mode 100644 media/codec2/hidl/1.0/utils/ComponentInterface.cpp create mode 100644 media/codec2/hidl/1.0/utils/InputBufferManager.cpp create mode 100644 media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h delete mode 100644 media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ConfigurableC2Intf.h create mode 100644 media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index c5ad6a01c8..d0296a5fcd 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -8,8 +8,10 @@ cc_library { srcs: [ "Component.cpp", + "ComponentInterface.cpp", "ComponentStore.cpp", "Configurable.cpp", + "InputBufferManager.cpp", "InputSurface.cpp", "InputSurfaceConnection.cpp", "types.cpp", diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp index 5ae1972a28..0473b57b19 100644 --- a/media/codec2/hidl/1.0/utils/Component.cpp +++ b/media/codec2/hidl/1.0/utils/Component.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ #define LOG_TAG "Codec2-Component" #include -#include #include #include -#include +#include +#include #include #include @@ -42,281 +42,6 @@ namespace utils { using namespace ::android; -namespace /* unnamed */ { - -// Implementation of ConfigurableC2Intf based on C2ComponentInterface -struct CompIntf : public ConfigurableC2Intf { - CompIntf(const std::shared_ptr& intf) : - ConfigurableC2Intf(intf->getName()), - mIntf(intf) { - } - - virtual c2_status_t config( - const std::vector& params, - c2_blocking_t mayBlock, - std::vector>* const failures - ) override { - ALOGV("config"); - return mIntf->config_vb(params, mayBlock, failures); - } - - virtual c2_status_t query( - const std::vector& indices, - c2_blocking_t mayBlock, - std::vector>* const params - ) const override { - ALOGV("query"); - return mIntf->query_vb({}, indices, mayBlock, params); - } - - virtual c2_status_t querySupportedParams( - std::vector>* const params - ) const override { - ALOGV("querySupportedParams"); - return mIntf->querySupportedParams_nb(params); - } - - virtual c2_status_t querySupportedValues( - std::vector& fields, - c2_blocking_t mayBlock) const override { - ALOGV("querySupportedValues"); - return mIntf->querySupportedValues_vb(fields, mayBlock); - } - -protected: - std::shared_ptr mIntf; -}; - -} // unnamed namespace - -// InputBufferManager -// ================== -// -// InputBufferManager presents a way to track and untrack input buffers in this -// (codec) process and send a notification to a listener, possibly in a -// different process, when a tracked buffer no longer has any references in this -// process. (In fact, this class would work for listeners in the same process -// too, but the optimization discussed below will not be beneficial.) -// -// InputBufferManager holds a collection of records representing tracked buffers -// and their callback listeners. Conceptually, one record is a triple (listener, -// frameIndex, bufferIndex) where -// -// - (frameIndex, bufferIndex) is a pair of indices used to identify the buffer. -// - listener is of type IComponentListener. Its onFramesRendered() function -// will be called after the associated buffer dies. The argument of -// onFramesRendered() is a list of RenderedFrame objects, each of which has -// the following members: -// -// uint64_t bufferQueueId -// int32_t slotId -// int64_t timestampNs -// -// When a tracked buffer associated to the triple (listener, frameIndex, -// bufferIndex) goes out of scope, listener->onFramesRendered() will be called -// with a RenderedFrame object whose members are set as follows: -// -// bufferQueueId = frameIndex -// slotId = ~bufferIndex -// timestampNs = systemTime() at the time of notification -// -// The reason for the bitwise negation of bufferIndex is that onFramesRendered() -// may be used for a different purpose when slotId is non-negative (which is a -// more general use case). -// -// IPC Optimization -// ---------------- -// -// Since onFramesRendered() generally is an IPC call, InputBufferManager tries -// not to call it too often. There is a mechanism to guarantee that any two -// calls to the same listener are at least kNotificationPeriodNs nanoseconds -// apart. -// -struct InputBufferManager { - // The minimum time period between IPC calls to notify the client about the - // destruction of input buffers. - static constexpr nsecs_t kNotificationPeriodNs = 1000000; - - // Track all buffers in a C2FrameData object. - // - // input (C2FrameData) has the following two members that are of interest: - // - // C2WorkOrdinal ordinal - // vector> buffers - // - // Calling registerFrameData(listener, input) will register multiple - // triples (, frameIndex, bufferIndex) where frameIndex is equal to - // input.ordinal.frameIndex and bufferIndex runs through the indices of - // input.buffers such that input.buffers[bufferIndex] is not null. - // - // This should be called from queue(). - static void registerFrameData( - const sp& listener, - const C2FrameData& input); - - // Untrack all buffers in a C2FrameData object. - // - // Calling unregisterFrameData(listener, input) will unregister and remove - // pending notifications for all triples (l, fi, bufferIndex) such that - // l = listener and fi = input.ordinal.frameIndex. - // - // This should be called from onWorkDone() and flush(). - static void unregisterFrameData( - const wp& listener, - const C2FrameData& input); - - // Untrack all buffers associated to a given listener. - // - // Calling unregisterFrameData(listener) will unregister and remove - // pending notifications for all triples (l, frameIndex, bufferIndex) such - // that l = listener. - // - // This should be called when the component cleans up all input buffers, - // i.e., when reset(), release(), stop() or ~Component() is called. - static void unregisterFrameData( - const wp& listener); - -private: - void _registerFrameData( - const sp& listener, - const C2FrameData& input); - void _unregisterFrameData( - const wp& listener, - const C2FrameData& input); - void _unregisterFrameData( - const wp& listener); - - // The callback function tied to C2Buffer objects. - // - // Note: This function assumes that sInstance is the only instance of this - // class. - static void onBufferDestroyed(const C2Buffer* buf, void* arg); - void _onBufferDestroyed(const C2Buffer* buf, void* arg); - - // Comparison operator for weak pointers. - struct CompareWeakComponentListener { - constexpr bool operator()( - const wp& x, - const wp& y) const { - return x.get_refs() < y.get_refs(); - } - }; - - // Persistent data to be passed as "arg" in onBufferDestroyed(). - // This is essentially the triple (listener, frameIndex, bufferIndex) plus a - // weak pointer to the C2Buffer object. - // - // Note that the "key" is bufferIndex according to operator<(). This is - // designed to work with TrackedBuffersMap defined below. - struct TrackedBuffer { - wp listener; - uint64_t frameIndex; - size_t bufferIndex; - std::weak_ptr buffer; - TrackedBuffer(const wp& listener, - uint64_t frameIndex, - size_t bufferIndex, - const std::shared_ptr& buffer) - : listener(listener), - frameIndex(frameIndex), - bufferIndex(bufferIndex), - buffer(buffer) {} - TrackedBuffer(const TrackedBuffer&) = default; - bool operator<(const TrackedBuffer& other) const { - return bufferIndex < other.bufferIndex; - } - }; - - // Map: listener -> frameIndex -> set. - // Essentially, this is used to store triples (listener, frameIndex, - // bufferIndex) that's searchable by listener and (listener, frameIndex). - // However, the value of the innermost map is TrackedBuffer, which also - // contains an extra copy of listener and frameIndex. This is needed - // because onBufferDestroyed() needs to know listener and frameIndex too. - typedef std::map, - std::map>, - CompareWeakComponentListener> TrackedBuffersMap; - - // Storage for pending (unsent) death notifications for one listener. - // Each pair in member named "indices" are (frameIndex, bufferIndex) from - // the (listener, frameIndex, bufferIndex) triple. - struct DeathNotifications { - - // The number of pending notifications for this listener. - // count may be 0, in which case the DeathNotifications object will - // remain valid for only a small period (kNotificationPeriodNs - // nanoseconds). - size_t count; - - // The timestamp of the most recent callback on this listener. This is - // used to guarantee that callbacks do not occur too frequently, and - // also to trigger expiration of a DeathNotifications object that has - // count = 0. - nsecs_t lastSentNs; - - // Map: frameIndex -> vector of bufferIndices - // This is essentially a collection of (framdeIndex, bufferIndex). - std::map> indices; - - DeathNotifications() - : count(0), - lastSentNs(systemTime() - kNotificationPeriodNs), - indices() {} - }; - - // Mutex for the management of all input buffers. - std::mutex mMutex; - - // Tracked input buffers. - TrackedBuffersMap mTrackedBuffersMap; - - // Death notifications to be sent. - // - // A DeathNotifications object is associated to each listener. An entry in - // this map will be removed if its associated DeathNotifications has count = - // 0 and lastSentNs < systemTime() - kNotificationPeriodNs. - std::map, DeathNotifications> mDeathNotifications; - - // Condition variable signaled when an entry is added to mDeathNotifications. - std::condition_variable mOnBufferDestroyed; - - // Notify the clients about buffer destructions. - // Return false if all destructions have been notified. - // Return true and set timeToRetry to the duration to wait for before - // retrying if some destructions have not been notified. - bool processNotifications(nsecs_t* timeToRetryNs); - - // Main function for the input buffer manager thread. - void main(); - - // The thread that manages notifications. - // - // Note: This variable is declared last so its initialization will happen - // after all other member variables have been initialized. - std::thread mMainThread; - - // Private constructor. - InputBufferManager(); - - // The only instance of this class. - static InputBufferManager& getInstance(); - -}; - -// ComponentInterface -ComponentInterface::ComponentInterface( - const std::shared_ptr& intf, - const sp& store) : - Configurable(new CachedConfigurable(std::make_unique(intf))), - mInterface(intf) { - mInit = init(store.get()); -} - -c2_status_t ComponentInterface::status() const { - return mInit; -} - // ComponentListener wrapper struct Component::Listener : public C2Component::Listener { @@ -328,12 +53,12 @@ struct Component::Listener : public C2Component::Listener { virtual void onError_nb( std::weak_ptr /* c2component */, uint32_t errorCode) override { - ALOGV("onError"); sp listener = mListener.promote(); if (listener) { Return transStatus = listener->onError(Status::OK, errorCode); if (!transStatus.isOk()) { - ALOGE("onError -- transaction failed."); + LOG(ERROR) << "Component::Listener::onError_nb -- " + << "transaction failed."; } } } @@ -342,7 +67,6 @@ struct Component::Listener : public C2Component::Listener { std::weak_ptr /* c2component */, std::vector> c2settingResult ) override { - ALOGV("onTripped"); sp listener = mListener.promote(); if (listener) { hidl_vec settingResults(c2settingResult.size()); @@ -350,8 +74,7 @@ struct Component::Listener : public C2Component::Listener { for (const std::shared_ptr &c2result : c2settingResult) { if (c2result) { - if (objcpy(&settingResults[ix++], *c2result) != - Status::OK) { + if (!objcpy(&settingResults[ix++], *c2result)) { break; } } @@ -359,7 +82,8 @@ struct Component::Listener : public C2Component::Listener { settingResults.resize(ix); Return transStatus = listener->onTripped(settingResults); if (!transStatus.isOk()) { - ALOGE("onTripped -- transaction failed."); + LOG(ERROR) << "Component::Listener::onTripped_nb -- " + << "transaction failed."; } } } @@ -367,7 +91,6 @@ struct Component::Listener : public C2Component::Listener { virtual void onWorkDone_nb( std::weak_ptr /* c2component */, std::list> c2workItems) override { - ALOGV("onWorkDone"); for (const std::unique_ptr& work : c2workItems) { if (work) { if (work->worklets.empty() @@ -385,15 +108,16 @@ struct Component::Listener : public C2Component::Listener { WorkBundle workBundle; sp strongComponent = mComponent.promote(); - if (objcpy(&workBundle, c2workItems, strongComponent ? - &strongComponent->mBufferPoolSender : nullptr) - != Status::OK) { - ALOGE("onWorkDone() received corrupted work items."); + if (!objcpy(&workBundle, c2workItems, strongComponent ? + &strongComponent->mBufferPoolSender : nullptr)) { + LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " + << "received corrupted work items."; return; } Return transStatus = listener->onWorkDone(workBundle); if (!transStatus.isOk()) { - ALOGE("onWorkDone -- transaction failed."); + LOG(ERROR) << "Component::Listener::onWorkDone_nb -- " + << "transaction failed."; return; } yieldBufferQueueBlocks(c2workItems, true); @@ -405,23 +129,86 @@ protected: 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) : - Configurable(new CachedConfigurable( - std::make_unique(component->intf()))), - mComponent(component), - mInterface(component->intf()), - mListener(listener), - mStore(store), - mBufferPoolSender(clientPoolManager) { + IClientManager>& clientPoolManager) + : mComponent{component}, + mInterface{new ComponentInterface(component->intf(), store.get())}, + mListener{listener}, + mStore{store}, + mBufferPoolSender{clientPoolManager} { // Retrieve supported parameters from store // TODO: We could cache this per component/interface type - mInit = init(store.get()); + mInit = mInterface->status(); } c2_status_t Component::status() const { @@ -430,11 +217,9 @@ c2_status_t Component::status() const { // Methods from ::android::hardware::media::c2::V1_0::IComponent Return Component::queue(const WorkBundle& workBundle) { - ALOGV("queue -- converting input"); std::list> c2works; - if (objcpy(&c2works, workBundle) != C2_OK) { - ALOGV("queue -- corrupted"); + if (!objcpy(&c2works, workBundle)) { return Status::CORRUPTED; } @@ -446,13 +231,11 @@ Return Component::queue(const WorkBundle& workBundle) { } } - ALOGV("queue -- calling"); return static_cast(mComponent->queue_nb(&c2works)); } Return Component::flush(flush_cb _hidl_cb) { std::list> c2flushedWorks; - ALOGV("flush -- calling"); c2_status_t c2res = mComponent->flush_sm( C2Component::FLUSH_COMPONENT, &c2flushedWorks); @@ -473,8 +256,9 @@ Return Component::flush(flush_cb _hidl_cb) { WorkBundle flushedWorkBundle; Status res = static_cast(c2res); if (c2res == C2_OK) { - ALOGV("flush -- converting output"); - res = objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender); + if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) { + res = Status::CORRUPTED; + } } _hidl_cb(res, flushedWorkBundle); yieldBufferQueueBlocks(c2flushedWorks, true); @@ -482,7 +266,6 @@ Return Component::flush(flush_cb _hidl_cb) { } Return Component::drain(bool withEos) { - ALOGV("drain"); return static_cast(mComponent->drain_nb(withEos ? C2Component::DRAIN_COMPONENT_WITH_EOS : C2Component::DRAIN_COMPONENT_NO_EOS)); @@ -512,14 +295,39 @@ Return Component::setOutputSurface( return Status::OK; } -Return Component::connectToOmxInputSurface( +Return Component::connectToInputSurface( + const sp& inputSurface, + connectToInputSurface_cb _hidl_cb) { + sp sink; + { + std::lock_guard lock(mSinkMutex); + if (!mSink) { + mSink = new Sink(shared_from_this()); + } + sink = mSink; + } + Status status; + sp connection; + auto transStatus = inputSurface->connect(sink, + [&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) { - // TODO implement + IGraphicBufferSource>& source, + connectToOmxInputSurface_cb _hidl_cb) { (void)producer; (void)source; - return Status::OMITTED; + (void)_hidl_cb; + return Void(); } Return Component::disconnectFromInputSurface() { @@ -530,11 +338,12 @@ Return Component::disconnectFromInputSurface() { namespace /* unnamed */ { struct BlockPoolIntf : public ConfigurableC2Intf { - BlockPoolIntf(const std::shared_ptr& pool) : - ConfigurableC2Intf("C2BlockPool:" + - (pool ? std::to_string(pool->getLocalId()) : - "null")), - mPool(pool) { + BlockPoolIntf(const std::shared_ptr& pool) + : ConfigurableC2Intf{ + "C2BlockPool:" + + (pool ? std::to_string(pool->getLocalId()) : "null"), + 0}, + mPool{pool} { } virtual c2_status_t config( @@ -613,18 +422,15 @@ Return Component::destroyBlockPool(uint64_t blockPoolId) { } Return Component::start() { - ALOGV("start"); return static_cast(mComponent->start()); } Return Component::stop() { - ALOGV("stop"); InputBufferManager::unregisterFrameData(mListener); return static_cast(mComponent->stop()); } Return Component::reset() { - ALOGV("reset"); Status status = static_cast(mComponent->reset()); { std::lock_guard lock(mBlockPoolsMutex); @@ -635,7 +441,6 @@ Return Component::reset() { } Return Component::release() { - ALOGV("release"); Status status = static_cast(mComponent->release()); { std::lock_guard lock(mBlockPoolsMutex); @@ -645,8 +450,13 @@ Return Component::release() { return status; } -void Component::setLocalId(const Component::LocalId& localId) { - mLocalId = localId; +Return> Component::getInterface() { + return sp(mInterface); +} + +std::shared_ptr Component::findLocalComponent( + const sp& sink) { + return Component::Sink::findLocalComponent(sink); } void Component::initListener(const sp& self) { @@ -660,395 +470,7 @@ void Component::initListener(const sp& self) { Component::~Component() { InputBufferManager::unregisterFrameData(mListener); - mStore->reportComponentDeath(mLocalId); -} - -Component::InterfaceKey::InterfaceKey(const sp& component) { - isRemote = component->isRemote(); - if (isRemote) { - remote = ::android::hardware::toBinder(component); - } else { - local = component; - } -} - -// InputBufferManager implementation - -constexpr nsecs_t InputBufferManager::kNotificationPeriodNs; - -void InputBufferManager::registerFrameData( - const sp& listener, - const C2FrameData& input) { - getInstance()._registerFrameData(listener, input); -} - -void InputBufferManager::unregisterFrameData( - const wp& listener, - const C2FrameData& input) { - getInstance()._unregisterFrameData(listener, input); -} - -void InputBufferManager::unregisterFrameData( - const wp& listener) { - getInstance()._unregisterFrameData(listener); -} - -void InputBufferManager::_registerFrameData( - const sp& listener, - const C2FrameData& input) { - uint64_t frameIndex = input.ordinal.frameIndex.peeku(); - ALOGV("InputBufferManager::_registerFrameData called " - "(listener @ %p, frameIndex = %llu)", - listener.get(), - static_cast(frameIndex)); - std::lock_guard lock(mMutex); - - std::set &bufferIds = - mTrackedBuffersMap[listener][frameIndex]; - - for (size_t i = 0; i < input.buffers.size(); ++i) { - if (!input.buffers[i]) { - ALOGV("InputBufferManager::_registerFrameData: " - "Input buffer at index %zu is null", i); - continue; - } - const TrackedBuffer &bufferId = - *bufferIds.emplace(listener, frameIndex, i, input.buffers[i]). - first; - - c2_status_t status = input.buffers[i]->registerOnDestroyNotify( - onBufferDestroyed, - const_cast(reinterpret_cast(&bufferId))); - if (status != C2_OK) { - ALOGD("InputBufferManager: registerOnDestroyNotify failed " - "(listener @ %p, frameIndex = %llu, bufferIndex = %zu) " - "=> %s (%d)", - listener.get(), - static_cast(frameIndex), - i, - asString(status), static_cast(status)); - } - } - - mDeathNotifications.emplace(listener, DeathNotifications()); -} - -// Remove a pair (listener, frameIndex) from mTrackedBuffersMap and -// mDeathNotifications. This implies all bufferIndices are removed. -// -// This is called from onWorkDone() and flush(). -void InputBufferManager::_unregisterFrameData( - const wp& listener, - const C2FrameData& input) { - uint64_t frameIndex = input.ordinal.frameIndex.peeku(); - ALOGV("InputBufferManager::_unregisterFrameData called " - "(listener @ %p, frameIndex = %llu)", - listener.unsafe_get(), - static_cast(frameIndex)); - std::lock_guard lock(mMutex); - - auto findListener = mTrackedBuffersMap.find(listener); - if (findListener != mTrackedBuffersMap.end()) { - std::map> &frameIndex2BufferIds - = findListener->second; - auto findFrameIndex = frameIndex2BufferIds.find(frameIndex); - if (findFrameIndex != frameIndex2BufferIds.end()) { - std::set &bufferIds = findFrameIndex->second; - for (const TrackedBuffer& bufferId : bufferIds) { - std::shared_ptr buffer = bufferId.buffer.lock(); - if (buffer) { - c2_status_t status = buffer->unregisterOnDestroyNotify( - onBufferDestroyed, - const_cast( - reinterpret_cast(&bufferId))); - if (status != C2_OK) { - ALOGD("InputBufferManager: " - "unregisterOnDestroyNotify failed " - "(listener @ %p, " - "frameIndex = %llu, " - "bufferIndex = %zu) " - "=> %s (%d)", - bufferId.listener.unsafe_get(), - static_cast( - bufferId.frameIndex), - bufferId.bufferIndex, - asString(status), static_cast(status)); - } - } - } - - frameIndex2BufferIds.erase(findFrameIndex); - if (frameIndex2BufferIds.empty()) { - mTrackedBuffersMap.erase(findListener); - } - } - } - - auto findListenerD = mDeathNotifications.find(listener); - if (findListenerD != mDeathNotifications.end()) { - DeathNotifications &deathNotifications = findListenerD->second; - auto findFrameIndex = deathNotifications.indices.find(frameIndex); - if (findFrameIndex != deathNotifications.indices.end()) { - std::vector &bufferIndices = findFrameIndex->second; - deathNotifications.count -= bufferIndices.size(); - deathNotifications.indices.erase(findFrameIndex); - } - } -} - -// Remove listener from mTrackedBuffersMap and mDeathNotifications. This implies -// all frameIndices and bufferIndices are removed. -// -// This is called when the component cleans up all input buffers, i.e., when -// reset(), release(), stop() or ~Component() is called. -void InputBufferManager::_unregisterFrameData( - const wp& listener) { - ALOGV("InputBufferManager::_unregisterFrameData called (listener @ %p)", - listener.unsafe_get()); - std::lock_guard lock(mMutex); - - auto findListener = mTrackedBuffersMap.find(listener); - if (findListener != mTrackedBuffersMap.end()) { - std::map> &frameIndex2BufferIds = - findListener->second; - for (auto findFrameIndex = frameIndex2BufferIds.begin(); - findFrameIndex != frameIndex2BufferIds.end(); - ++findFrameIndex) { - std::set &bufferIds = findFrameIndex->second; - for (const TrackedBuffer& bufferId : bufferIds) { - std::shared_ptr buffer = bufferId.buffer.lock(); - if (buffer) { - c2_status_t status = buffer->unregisterOnDestroyNotify( - onBufferDestroyed, - const_cast( - reinterpret_cast(&bufferId))); - if (status != C2_OK) { - ALOGD("InputBufferManager: " - "unregisterOnDestroyNotify failed " - "(listener @ %p, " - "frameIndex = %llu, " - "bufferIndex = %zu) " - "=> %s (%d)", - bufferId.listener.unsafe_get(), - static_cast(bufferId.frameIndex), - bufferId.bufferIndex, - asString(status), static_cast(status)); - } - } - } - } - mTrackedBuffersMap.erase(findListener); - } - - mDeathNotifications.erase(listener); -} - -// Move a buffer from mTrackedBuffersMap to mDeathNotifications. -// This is called when a registered C2Buffer object is destroyed. -void InputBufferManager::onBufferDestroyed(const C2Buffer* buf, void* arg) { - getInstance()._onBufferDestroyed(buf, arg); -} - -void InputBufferManager::_onBufferDestroyed(const C2Buffer* buf, void* arg) { - if (!buf || !arg) { - ALOGW("InputBufferManager::_onBufferDestroyed called " - "with null argument(s) (buf @ %p, arg @ %p)", - buf, arg); - return; - } - TrackedBuffer id(*reinterpret_cast(arg)); - ALOGV("InputBufferManager::_onBufferDestroyed called " - "(listener @ %p, frameIndex = %llu, bufferIndex = %zu)", - id.listener.unsafe_get(), - static_cast(id.frameIndex), - id.bufferIndex); - - std::lock_guard lock(mMutex); - - auto findListener = mTrackedBuffersMap.find(id.listener); - if (findListener == mTrackedBuffersMap.end()) { - ALOGD("InputBufferManager::_onBufferDestroyed received " - "invalid listener " - "(listener @ %p, frameIndex = %llu, bufferIndex = %zu)", - id.listener.unsafe_get(), - static_cast(id.frameIndex), - id.bufferIndex); - return; - } - - std::map> &frameIndex2BufferIds - = findListener->second; - auto findFrameIndex = frameIndex2BufferIds.find(id.frameIndex); - if (findFrameIndex == frameIndex2BufferIds.end()) { - ALOGD("InputBufferManager::_onBufferDestroyed received " - "invalid frame index " - "(listener @ %p, frameIndex = %llu, bufferIndex = %zu)", - id.listener.unsafe_get(), - static_cast(id.frameIndex), - id.bufferIndex); - return; - } - - std::set &bufferIds = findFrameIndex->second; - auto findBufferId = bufferIds.find(id); - if (findBufferId == bufferIds.end()) { - ALOGD("InputBufferManager::_onBufferDestroyed received " - "invalid buffer index: " - "(listener @ %p, frameIndex = %llu, bufferIndex = %zu)", - id.listener.unsafe_get(), - static_cast(id.frameIndex), - id.bufferIndex); - } - - bufferIds.erase(findBufferId); - if (bufferIds.empty()) { - frameIndex2BufferIds.erase(findFrameIndex); - if (frameIndex2BufferIds.empty()) { - mTrackedBuffersMap.erase(findListener); - } - } - - DeathNotifications &deathNotifications = mDeathNotifications[id.listener]; - deathNotifications.indices[id.frameIndex].emplace_back(id.bufferIndex); - ++deathNotifications.count; - mOnBufferDestroyed.notify_one(); -} - -// Notify the clients about buffer destructions. -// Return false if all destructions have been notified. -// Return true and set timeToRetry to the time point to wait for before -// retrying if some destructions have not been notified. -bool InputBufferManager::processNotifications(nsecs_t* timeToRetryNs) { - - struct Notification { - sp listener; - hidl_vec renderedFrames; - Notification(const sp& l, size_t s) - : listener(l), renderedFrames(s) {} - }; - std::list notifications; - - bool retry = false; - { - std::lock_guard lock(mMutex); - *timeToRetryNs = kNotificationPeriodNs; - nsecs_t timeNowNs = systemTime(); - for (auto it = mDeathNotifications.begin(); - it != mDeathNotifications.end(); ) { - sp listener = it->first.promote(); - if (!listener) { - ++it; - continue; - } - DeathNotifications &deathNotifications = it->second; - - nsecs_t timeSinceLastNotifiedNs = - timeNowNs - deathNotifications.lastSentNs; - // If not enough time has passed since the last callback, leave the - // notifications for this listener untouched for now and retry - // later. - if (timeSinceLastNotifiedNs < kNotificationPeriodNs) { - retry = true; - *timeToRetryNs = std::min(*timeToRetryNs, - kNotificationPeriodNs - timeSinceLastNotifiedNs); - ALOGV("InputBufferManager: Notifications for " - "listener @ %p will be postponed.", - listener.get()); - ++it; - continue; - } - - // If enough time has passed since the last notification to this - // listener but there are currently no pending notifications, the - // listener can be removed from mDeathNotifications---there is no - // need to keep track of the last notification time anymore. - if (deathNotifications.count == 0) { - it = mDeathNotifications.erase(it); - continue; - } - - // Create the argument for the callback. - notifications.emplace_back(listener, deathNotifications.count); - hidl_vec& renderedFrames = - notifications.back().renderedFrames; - size_t i = 0; - for (std::pair>& p : - deathNotifications.indices) { - uint64_t frameIndex = p.first; - const std::vector &bufferIndices = p.second; - for (const size_t& bufferIndex : bufferIndices) { - IComponentListener::RenderedFrame &renderedFrame - = renderedFrames[i++]; - renderedFrame.slotId = ~bufferIndex; - renderedFrame.bufferQueueId = frameIndex; - renderedFrame.timestampNs = timeNowNs; - ALOGV("InputBufferManager: " - "Sending death notification (listener @ %p, " - "frameIndex = %llu, bufferIndex = %zu)", - listener.get(), - static_cast(frameIndex), - bufferIndex); - } - } - - // Clear deathNotifications for this listener and set retry to true - // so processNotifications will be called again. This will - // guarantee that a listener with no pending notifications will - // eventually be removed from mDeathNotifications after - // kNotificationPeriodNs nanoseconds has passed. - retry = true; - deathNotifications.indices.clear(); - deathNotifications.count = 0; - deathNotifications.lastSentNs = timeNowNs; - ++it; - } - } - - // Call onFramesRendered outside the lock to avoid deadlock. - for (const Notification& notification : notifications) { - if (!notification.listener->onFramesRendered( - notification.renderedFrames).isOk()) { - // This may trigger if the client has died. - ALOGD("InputBufferManager: onFramesRendered transaction failed " - "(listener @ %p)", - notification.listener.get()); - } - } - if (retry) { - ALOGV("InputBufferManager: Pending death notifications" - "will be sent in %lldns.", - static_cast(*timeToRetryNs)); - } - return retry; -} - -void InputBufferManager::main() { - ALOGV("InputBufferManager: Starting main thread"); - nsecs_t timeToRetryNs; - while (true) { - std::unique_lock lock(mMutex); - while (mDeathNotifications.empty()) { - ALOGV("InputBufferManager: Waiting for buffer deaths"); - mOnBufferDestroyed.wait(lock); - } - lock.unlock(); - ALOGV("InputBufferManager: Sending buffer death notifications"); - while (processNotifications(&timeToRetryNs)) { - std::this_thread::sleep_for( - std::chrono::nanoseconds(timeToRetryNs)); - ALOGV("InputBufferManager: Sending pending death notifications"); - } - ALOGV("InputBufferManager: No pending death notifications"); - } -} - -InputBufferManager::InputBufferManager() - : mMainThread(&InputBufferManager::main, this) { -} - -InputBufferManager& InputBufferManager::getInstance() { - static InputBufferManager instance{}; - return instance; + mStore->reportComponentDeath(this); } } // namespace utils diff --git a/media/codec2/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp new file mode 100644 index 0000000000..39e5357439 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-ComponentInterface" +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + +using namespace ::android; + +namespace /* unnamed */ { + +// Implementation of ConfigurableC2Intf based on C2ComponentInterface +struct CompIntf : public ConfigurableC2Intf { + CompIntf(const std::shared_ptr& intf) : + ConfigurableC2Intf{intf->getName(), intf->getId()}, + mIntf{intf} { + } + + virtual c2_status_t config( + const std::vector& params, + c2_blocking_t mayBlock, + std::vector>* const failures + ) override { + return mIntf->config_vb(params, mayBlock, failures); + } + + virtual c2_status_t query( + const std::vector& indices, + c2_blocking_t mayBlock, + std::vector>* const params + ) const override { + return mIntf->query_vb({}, indices, mayBlock, params); + } + + virtual c2_status_t querySupportedParams( + std::vector>* const params + ) const override { + return mIntf->querySupportedParams_nb(params); + } + + virtual c2_status_t querySupportedValues( + std::vector& fields, + c2_blocking_t mayBlock) const override { + return mIntf->querySupportedValues_vb(fields, mayBlock); + } + +protected: + std::shared_ptr mIntf; +}; + +} // unnamed namespace + +// ComponentInterface +ComponentInterface::ComponentInterface( + const std::shared_ptr& intf, + ComponentStore* store) + : mInterface{intf}, + mConfigurable{new CachedConfigurable(std::make_unique(intf))} { + mInit = mConfigurable->init(store); +} + +c2_status_t ComponentInterface::status() const { + return mInit; +} + +Return> ComponentInterface::getConfigurable() { + return mConfigurable; +} + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp index 9c05014f00..bb5faa5fc3 100644 --- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,37 +16,25 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-ComponentStore" -#include +#include #include #include -#include -#include #include +#include #include #include +#include #include #include -#include - -#include - -#ifdef LOG -#undef LOG -#endif - -#ifdef PLOG -#undef PLOG -#endif - -#include - +#include +#include +#include #include #include -#include namespace android { namespace hardware { @@ -62,12 +50,12 @@ 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() : ""), - mStore(store) { + StoreIntf(const std::shared_ptr& store) + : ConfigurableC2Intf{store ? store->getName() : "", 0}, + mStore{store} { } - c2_status_t config( + virtual c2_status_t config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector> *const failures @@ -80,7 +68,7 @@ struct StoreIntf : public ConfigurableC2Intf { return mStore->config_sm(params, failures); } - c2_status_t query( + virtual c2_status_t query( const std::vector &indices, c2_blocking_t mayBlock, std::vector> *const params) const override { @@ -92,13 +80,13 @@ struct StoreIntf : public ConfigurableC2Intf { return mStore->query_sm({}, indices, params); } - c2_status_t querySupportedParams( + virtual c2_status_t querySupportedParams( std::vector> *const params ) const override { return mStore->querySupportedParams_nb(params); } - c2_status_t querySupportedValues( + virtual c2_status_t querySupportedValues( std::vector &fields, c2_blocking_t mayBlock) const override { // Assume all params are blocking @@ -115,9 +103,9 @@ protected: } // unnamed namespace -ComponentStore::ComponentStore(const std::shared_ptr& store) : - Configurable(new CachedConfigurable(std::make_unique(store))), - mStore(store) { +ComponentStore::ComponentStore(const std::shared_ptr& store) + : mConfigurable{new CachedConfigurable(std::make_unique(store))}, + mStore{store} { std::shared_ptr platformStore = android::GetCodec2PlatformComponentStore(); SetPreferredCodec2ComponentStore(store); @@ -126,7 +114,11 @@ ComponentStore::ComponentStore(const std::shared_ptr& store) : mParamReflector = mStore->getParamReflector(); // Retrieve supported parameters from store - mInit = init(this); + mInit = mConfigurable->init(this); +} + +c2_status_t ComponentStore::status() const { + return mInit; } c2_status_t ComponentStore::validateSupportedParams( @@ -172,19 +164,15 @@ Return ComponentStore::createComponent( component = new Component(c2component, listener, this, pool); if (!component) { status = Status::CORRUPTED; - } else if (component->status() != C2_OK) { - status = static_cast(component->status()); } else { - component->initListener(component); + reportComponentBirth(component.get()); if (component->status() != C2_OK) { status = static_cast(component->status()); } else { - std::lock_guard lock(mComponentRosterMutex); - component->setLocalId( - mComponentRoster.emplace( - Component::InterfaceKey(component), - c2component) - .first); + component->initListener(component); + if (component->status() != C2_OK) { + status = static_cast(component->status()); + } } } } @@ -202,7 +190,7 @@ Return ComponentStore::createInterface( onInterfaceLoaded(c2interface); interface = new ComponentInterface(c2interface, this); } - _hidl_cb((Status)res, interface); + _hidl_cb(static_cast(res), interface); return Void(); } @@ -213,27 +201,35 @@ Return ComponentStore::listComponents(listComponents_cb _hidl_cb) { size_t ix = 0; for (const std::shared_ptr &c2trait : c2traits) { if (c2trait) { - objcpy(&traits[ix++], *c2trait); + if (objcpy(&traits[ix], *c2trait)) { + ++ix; + } else { + break; + } } } traits.resize(ix); - _hidl_cb(traits); + _hidl_cb(Status::OK, traits); return Void(); } -Return> ComponentStore::createInputSurface() { +Return ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) { sp source = new GraphicBufferSource(); if (source->initCheck() != OK) { - return nullptr; + _hidl_cb(Status::CORRUPTED, nullptr); + return Void(); } typedef ::android::hardware::graphics::bufferqueue::V1_0:: IGraphicBufferProducer HGbp; typedef ::android::TWGraphicBufferProducer B2HGbp; - return new InputSurface( + sp inputSurface = new InputSurface( this, std::make_shared(), new B2HGbp(source->getIGraphicBufferProducer()), source); + _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY, + inputSurface); + return Void(); } void ComponentStore::onInterfaceLoaded(const std::shared_ptr &intf) { @@ -265,15 +261,25 @@ Return ComponentStore::getStructDescriptors( mUnsupportedStructDescriptors.emplace(coreIndex); } else { mStructDescriptors.insert({ coreIndex, structDesc }); - objcpy(&descriptors[dstIx++], *structDesc); - continue; + if (objcpy(&descriptors[dstIx], *structDesc)) { + ++dstIx; + continue; + } + res = Status::CORRUPTED; + break; } } res = Status::NOT_FOUND; } else if (item->second) { - objcpy(&descriptors[dstIx++], *item->second); + if (objcpy(&descriptors[dstIx], *item->second)) { + ++dstIx; + continue; + } + res = Status::CORRUPTED; + break; } else { res = Status::NO_MEMORY; + break; } } descriptors.resize(dstIx); @@ -292,29 +298,29 @@ Return ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) return Status::OMITTED; } -void ComponentStore::reportComponentDeath( - const Component::LocalId& componentLocalId) { - std::lock_guard lock(mComponentRosterMutex); - mComponentRoster.erase(componentLocalId); +Return> ComponentStore::getConfigurable() { + return mConfigurable; } -std::shared_ptr ComponentStore::findC2Component( - const sp& component) const { +// 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); - Component::LocalId it = mComponentRoster.find( - Component::InterfaceKey(component)); - if (it == mComponentRoster.end()) { - return std::shared_ptr(); - } - return it->second.lock(); + mComponentRoster.emplace(component, componentStatus); } -// Debug dump - -namespace /* unnamed */ { +// 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); +} -// Dump component traits -std::ostream& dump( +// Dumps component traits. +std::ostream& ComponentStore::dump( std::ostream& out, const std::shared_ptr& comp) { @@ -334,25 +340,38 @@ std::ostream& dump( return out; } -// Dump component -std::ostream& dump( +// Dumps component status. +std::ostream& ComponentStore::dump( std::ostream& out, - const std::shared_ptr& comp) { + ComponentStatus& compStatus) { constexpr const char indent[] = " "; - std::shared_ptr intf = comp->intf(); + // 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 -- null interface" << std::endl; + out << indent << "Unknown component -- null interface" << std::endl; return out; } - out << indent << "name: " << intf->getName() << std::endl; - out << indent << "id: " << intf->getId() << std::endl; + out << indent << "Name: " << intf->getName() << std::endl; + out << indent << "Id: " << intf->getId() << std::endl; + return out; } -} // unnamed namespace - +// Dumps information when lshal is called. Return ComponentStore::debug( const hidl_handle& handle, const hidl_vec& /* args */) { @@ -387,34 +406,19 @@ Return ComponentStore::debug( } } - // Retrieve the list of active components. - std::list> activeComps; + // Dump active components. { + out << indent << "Active components:" << std::endl << std::endl; std::lock_guard lock(mComponentRosterMutex); - auto i = mComponentRoster.begin(); - while (i != mComponentRoster.end()) { - std::shared_ptr c2comp = i->second.lock(); - if (!c2comp) { - auto j = i; - ++i; - mComponentRoster.erase(j); - } else { - ++i; - activeComps.emplace_back(c2comp); + if (mComponentRoster.size() == 0) { + out << indent << indent << "NONE" << std::endl << std::endl; + } else { + for (auto& pair : mComponentRoster) { + dump(out, pair.second) << std::endl; } } } - // Dump active components. - out << indent << "Active components:" << std::endl << std::endl; - if (activeComps.size() == 0) { - out << indent << indent << "NONE" << std::endl << std::endl; - } else { - for (const std::shared_ptr& c2comp : activeComps) { - dump(out, c2comp) << std::endl; - } - } - out << "End of dump -- C2ComponentStore: " << mStore->getName() << std::endl; } diff --git a/media/codec2/hidl/1.0/utils/Configurable.cpp b/media/codec2/hidl/1.0/utils/Configurable.cpp index d023ba8b56..a35b74c1e5 100644 --- a/media/codec2/hidl/1.0/utils/Configurable.cpp +++ b/media/codec2/hidl/1.0/utils/Configurable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,12 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-Configurable" -#include +#include #include #include #include + #include namespace android { @@ -33,8 +34,8 @@ namespace utils { using namespace ::android; CachedConfigurable::CachedConfigurable( - std::unique_ptr&& intf) : - mIntf(std::move(intf)) { + std::unique_ptr&& intf) + : mIntf{std::move(intf)} { } c2_status_t CachedConfigurable::init(ComponentStore* store) { @@ -45,6 +46,10 @@ c2_status_t CachedConfigurable::init(ComponentStore* store) { } // Methods from ::android::hardware::media::c2::V1_0::IConfigurable follow. +Return CachedConfigurable::getId() { + return mIntf->getId(); +} + Return CachedConfigurable::getName(getName_cb _hidl_cb) { _hidl_cb(mIntf->getName()); return Void(); @@ -65,9 +70,10 @@ Return CachedConfigurable::query( &c2heapParams); hidl_vec params; - createParamsBlob(¶ms, c2heapParams); + if (!createParamsBlob(¶ms, c2heapParams)) { + LOG(WARNING) << "query -- invalid output params."; + } _hidl_cb(static_cast(c2res), params); - return Void(); } @@ -78,7 +84,8 @@ Return CachedConfigurable::config( // inParams is not writable, so create a copy as config modifies the parameters hidl_vec inParamsCopy = inParams; std::vector c2params; - if (parseParamsBlob(&c2params, inParamsCopy) != C2_OK) { + if (!parseParamsBlob(&c2params, inParamsCopy)) { + LOG(WARNING) << "config -- invalid input params."; _hidl_cb(Status::CORRUPTED, hidl_vec(), hidl_vec()); @@ -95,13 +102,20 @@ Return CachedConfigurable::config( size_t ix = 0; for (const std::unique_ptr& c2result : c2failures) { if (c2result) { - objcpy(&failures[ix++], *c2result); + if (objcpy(&failures[ix], *c2result)) { + ++ix; + } else { + LOG(DEBUG) << "config -- invalid setting results."; + break; + } } } failures.resize(ix); } hidl_vec outParams; - createParamsBlob(&outParams, c2params); + if (!createParamsBlob(&outParams, c2params)) { + LOG(DEBUG) << "config -- invalid output params."; + } _hidl_cb((Status)c2res, failures, outParams); return Void(); } @@ -117,7 +131,13 @@ Return CachedConfigurable::querySupportedParams( size_t dstIx = 0; for (size_t srcIx = request.offset(); srcIx < request.endOffset(); ++srcIx) { if (mSupportedParams[srcIx]) { - objcpy(¶ms[dstIx++], *mSupportedParams[srcIx]); + if (objcpy(¶ms[dstIx], *mSupportedParams[srcIx])) { + ++dstIx; + } else { + res = Status::CORRUPTED; + LOG(WARNING) << "querySupportedParams -- invalid output params."; + break; + } } else { res = Status::BAD_INDEX; } @@ -154,7 +174,14 @@ Return CachedConfigurable::querySupportedValues( { size_t ix = 0; for (const C2FieldSupportedValuesQuery &result : c2fields) { - objcpy(&outFields[ix++], result); + if (!objcpy(&outFields[ix], result)) { + ++ix; + } else { + outFields.resize(ix); + c2res = C2_CORRUPTED; + LOG(WARNING) << "querySupportedValues -- invalid output params."; + break; + } } } _hidl_cb((Status)c2res, outFields); diff --git a/media/codec2/hidl/1.0/utils/InputBufferManager.cpp b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp new file mode 100644 index 0000000000..a023a05dc8 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp @@ -0,0 +1,461 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-InputBufferManager" +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + +using namespace ::android; + +void InputBufferManager::registerFrameData( + const sp& listener, + const C2FrameData& input) { + getInstance()._registerFrameData(listener, input); +} + +void InputBufferManager::unregisterFrameData( + const wp& listener, + const C2FrameData& input) { + getInstance()._unregisterFrameData(listener, input); +} + +void InputBufferManager::unregisterFrameData( + const wp& listener) { + getInstance()._unregisterFrameData(listener); +} + +void InputBufferManager::setNotificationInterval( + nsecs_t notificationIntervalNs) { + getInstance()._setNotificationInterval(notificationIntervalNs); +} + +void InputBufferManager::_registerFrameData( + const sp& listener, + const C2FrameData& input) { + uint64_t frameIndex = input.ordinal.frameIndex.peeku(); + LOG(VERBOSE) << "InputBufferManager::_registerFrameData -- called with " + << "listener @ 0x" << std::hex << listener.get() + << ", frameIndex = " << std::dec << frameIndex + << "."; + std::lock_guard lock(mMutex); + + std::set &bufferIds = + mTrackedBuffersMap[listener][frameIndex]; + + for (size_t i = 0; i < input.buffers.size(); ++i) { + if (!input.buffers[i]) { + LOG(VERBOSE) << "InputBufferManager::_registerFrameData -- " + << "Input buffer at index " << i << " is null."; + continue; + } + const TrackedBuffer &bufferId = + *bufferIds.emplace(listener, frameIndex, i, input.buffers[i]). + first; + + c2_status_t status = input.buffers[i]->registerOnDestroyNotify( + onBufferDestroyed, + const_cast(reinterpret_cast(&bufferId))); + if (status != C2_OK) { + LOG(DEBUG) << "InputBufferManager::_registerFrameData -- " + << "registerOnDestroyNotify() failed " + << "(listener @ 0x" << std::hex << listener.get() + << ", frameIndex = " << std::dec << frameIndex + << ", bufferIndex = " << i + << ") => status = " << status + << "."; + } + } + + mDeathNotifications.emplace( + listener, + DeathNotifications( + mNotificationIntervalNs.load(std::memory_order_relaxed))); +} + +// Remove a pair (listener, frameIndex) from mTrackedBuffersMap and +// mDeathNotifications. This implies all bufferIndices are removed. +// +// This is called from onWorkDone() and flush(). +void InputBufferManager::_unregisterFrameData( + const wp& listener, + const C2FrameData& input) { + uint64_t frameIndex = input.ordinal.frameIndex.peeku(); + LOG(VERBOSE) << "InputBufferManager::_unregisterFrameData -- called with " + << "listener @ 0x" << std::hex << listener.unsafe_get() + << ", frameIndex = " << std::dec << frameIndex + << "."; + std::lock_guard lock(mMutex); + + auto findListener = mTrackedBuffersMap.find(listener); + if (findListener != mTrackedBuffersMap.end()) { + std::map> &frameIndex2BufferIds + = findListener->second; + auto findFrameIndex = frameIndex2BufferIds.find(frameIndex); + if (findFrameIndex != frameIndex2BufferIds.end()) { + std::set &bufferIds = findFrameIndex->second; + for (const TrackedBuffer& bufferId : bufferIds) { + std::shared_ptr buffer = bufferId.buffer.lock(); + if (buffer) { + c2_status_t status = buffer->unregisterOnDestroyNotify( + onBufferDestroyed, + const_cast( + reinterpret_cast(&bufferId))); + if (status != C2_OK) { + LOG(DEBUG) << "InputBufferManager::_unregisterFrameData " + << "-- unregisterOnDestroyNotify() failed " + << "(listener @ 0x" + << std::hex + << bufferId.listener.unsafe_get() + << ", frameIndex = " + << std::dec << bufferId.frameIndex + << ", bufferIndex = " << bufferId.bufferIndex + << ") => status = " << status + << "."; + } + } + } + + frameIndex2BufferIds.erase(findFrameIndex); + if (frameIndex2BufferIds.empty()) { + mTrackedBuffersMap.erase(findListener); + } + } + } + + auto findListenerD = mDeathNotifications.find(listener); + if (findListenerD != mDeathNotifications.end()) { + DeathNotifications &deathNotifications = findListenerD->second; + auto findFrameIndex = deathNotifications.indices.find(frameIndex); + if (findFrameIndex != deathNotifications.indices.end()) { + std::vector &bufferIndices = findFrameIndex->second; + deathNotifications.count -= bufferIndices.size(); + deathNotifications.indices.erase(findFrameIndex); + } + } +} + +// Remove listener from mTrackedBuffersMap and mDeathNotifications. This implies +// all frameIndices and bufferIndices are removed. +// +// This is called when the component cleans up all input buffers, i.e., when +// reset(), release(), stop() or ~Component() is called. +void InputBufferManager::_unregisterFrameData( + const wp& listener) { + LOG(VERBOSE) << "InputBufferManager::_unregisterFrameData -- called with " + << "listener @ 0x" << std::hex << listener.unsafe_get() + << std::dec << "."; + std::lock_guard lock(mMutex); + + auto findListener = mTrackedBuffersMap.find(listener); + if (findListener != mTrackedBuffersMap.end()) { + std::map> &frameIndex2BufferIds = + findListener->second; + for (auto findFrameIndex = frameIndex2BufferIds.begin(); + findFrameIndex != frameIndex2BufferIds.end(); + ++findFrameIndex) { + std::set &bufferIds = findFrameIndex->second; + for (const TrackedBuffer& bufferId : bufferIds) { + std::shared_ptr buffer = bufferId.buffer.lock(); + if (buffer) { + c2_status_t status = buffer->unregisterOnDestroyNotify( + onBufferDestroyed, + const_cast( + reinterpret_cast(&bufferId))); + if (status != C2_OK) { + LOG(DEBUG) << "InputBufferManager::_unregisterFrameData " + << "-- unregisterOnDestroyNotify() failed " + << "(listener @ 0x" + << std::hex + << bufferId.listener.unsafe_get() + << ", frameIndex = " + << std::dec << bufferId.frameIndex + << ", bufferIndex = " << bufferId.bufferIndex + << ") => status = " << status + << "."; + } + } + } + } + mTrackedBuffersMap.erase(findListener); + } + + mDeathNotifications.erase(listener); +} + +// Set mNotificationIntervalNs. +void InputBufferManager::_setNotificationInterval( + nsecs_t notificationIntervalNs) { + mNotificationIntervalNs.store( + notificationIntervalNs, + std::memory_order_relaxed); +} + +// Move a buffer from mTrackedBuffersMap to mDeathNotifications. +// This is called when a registered C2Buffer object is destroyed. +void InputBufferManager::onBufferDestroyed(const C2Buffer* buf, void* arg) { + getInstance()._onBufferDestroyed(buf, arg); +} + +void InputBufferManager::_onBufferDestroyed(const C2Buffer* buf, void* arg) { + if (!buf || !arg) { + LOG(WARNING) << "InputBufferManager::_onBufferDestroyed -- called with " + << "null argument (s): " + << "buf @ 0x" << std::hex << buf + << ", arg @ 0x" << std::hex << arg + << std::dec << "."; + return; + } + TrackedBuffer id(*reinterpret_cast(arg)); + LOG(VERBOSE) << "InputBufferManager::_onBufferDestroyed -- called with " + << "buf @ 0x" << std::hex << buf + << ", arg @ 0x" << std::hex << arg + << std::dec << " -- " + << "listener @ 0x" << std::hex << id.listener.unsafe_get() + << ", frameIndex = " << std::dec << id.frameIndex + << ", bufferIndex = " << id.bufferIndex + << "."; + + std::lock_guard lock(mMutex); + + auto findListener = mTrackedBuffersMap.find(id.listener); + if (findListener == mTrackedBuffersMap.end()) { + LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- " + << "received invalid listener: " + << "listener @ 0x" << std::hex << id.listener.unsafe_get() + << " (frameIndex = " << std::dec << id.frameIndex + << ", bufferIndex = " << id.bufferIndex + << ")."; + return; + } + + std::map> &frameIndex2BufferIds + = findListener->second; + auto findFrameIndex = frameIndex2BufferIds.find(id.frameIndex); + if (findFrameIndex == frameIndex2BufferIds.end()) { + LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- " + << "received invalid frame index: " + << "frameIndex = " << id.frameIndex + << " (listener @ 0x" << std::hex << id.listener.unsafe_get() + << ", bufferIndex = " << std::dec << id.bufferIndex + << ")."; + return; + } + + std::set &bufferIds = findFrameIndex->second; + auto findBufferId = bufferIds.find(id); + if (findBufferId == bufferIds.end()) { + LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- " + << "received invalid buffer index: " + << "bufferIndex = " << id.bufferIndex + << " (frameIndex = " << id.frameIndex + << ", listener @ 0x" << std::hex << id.listener.unsafe_get() + << std::dec << ")."; + return; + } + + bufferIds.erase(findBufferId); + if (bufferIds.empty()) { + frameIndex2BufferIds.erase(findFrameIndex); + if (frameIndex2BufferIds.empty()) { + mTrackedBuffersMap.erase(findListener); + } + } + + DeathNotifications &deathNotifications = mDeathNotifications[id.listener]; + deathNotifications.indices[id.frameIndex].emplace_back(id.bufferIndex); + ++deathNotifications.count; + mOnBufferDestroyed.notify_one(); +} + +// Notify the clients about buffer destructions. +// Return false if all destructions have been notified. +// Return true and set timeToRetry to the time point to wait for before +// retrying if some destructions have not been notified. +bool InputBufferManager::processNotifications(nsecs_t* timeToRetryNs) { + + struct Notification { + sp listener; + hidl_vec inputBuffers; + Notification(const sp& l, size_t s) + : listener(l), inputBuffers(s) {} + }; + std::list notifications; + nsecs_t notificationIntervalNs = + mNotificationIntervalNs.load(std::memory_order_relaxed); + + bool retry = false; + { + std::lock_guard lock(mMutex); + *timeToRetryNs = notificationIntervalNs; + nsecs_t timeNowNs = systemTime(); + for (auto it = mDeathNotifications.begin(); + it != mDeathNotifications.end(); ) { + sp listener = it->first.promote(); + if (!listener) { + ++it; + continue; + } + DeathNotifications &deathNotifications = it->second; + + nsecs_t timeSinceLastNotifiedNs = + timeNowNs - deathNotifications.lastSentNs; + // If not enough time has passed since the last callback, leave the + // notifications for this listener untouched for now and retry + // later. + if (timeSinceLastNotifiedNs < notificationIntervalNs) { + retry = true; + *timeToRetryNs = std::min(*timeToRetryNs, + notificationIntervalNs - timeSinceLastNotifiedNs); + LOG(VERBOSE) << "InputBufferManager::processNotifications -- " + << "Notifications for listener @ " + << std::hex << listener.get() + << " will be postponed."; + ++it; + continue; + } + + // If enough time has passed since the last notification to this + // listener but there are currently no pending notifications, the + // listener can be removed from mDeathNotifications---there is no + // need to keep track of the last notification time anymore. + if (deathNotifications.count == 0) { + it = mDeathNotifications.erase(it); + continue; + } + + // Create the argument for the callback. + notifications.emplace_back(listener, deathNotifications.count); + hidl_vec &inputBuffers = + notifications.back().inputBuffers; + size_t i = 0; + for (std::pair>& p : + deathNotifications.indices) { + uint64_t frameIndex = p.first; + const std::vector &bufferIndices = p.second; + for (const size_t& bufferIndex : bufferIndices) { + IComponentListener::InputBuffer &inputBuffer + = inputBuffers[i++]; + inputBuffer.arrayIndex = bufferIndex; + inputBuffer.frameIndex = frameIndex; + } + } + + // Clear deathNotifications for this listener and set retry to true + // so processNotifications will be called again. This will + // guarantee that a listener with no pending notifications will + // eventually be removed from mDeathNotifications after + // mNotificationIntervalNs nanoseconds has passed. + retry = true; + deathNotifications.indices.clear(); + deathNotifications.count = 0; + deathNotifications.lastSentNs = timeNowNs; + ++it; + } + } + + // Call onInputBuffersReleased() outside the lock to avoid deadlock. + for (const Notification& notification : notifications) { + if (!notification.listener->onInputBuffersReleased( + notification.inputBuffers).isOk()) { + // This may trigger if the client has died. + LOG(DEBUG) << "InputBufferManager::processNotifications -- " + << "failed to send death notifications to " + << "listener @ 0x" << std::hex + << notification.listener.get() + << std::dec << "."; + } else { +#if LOG_NDEBUG == 0 + std::stringstream inputBufferLog; + for (const IComponentListener::InputBuffer& inputBuffer : + notification.inputBuffers) { + inputBufferLog << " (" << inputBuffer.frameIndex + << ", " << inputBuffer.arrayIndex + << ")"; + } + LOG(VERBOSE) << "InputBufferManager::processNotifications -- " + << "death notifications sent to " + << "listener @ 0x" << std::hex + << notification.listener.get() + << std::dec + << " with these (frameIndex, bufferIndex) pairs:" + << inputBufferLog.str(); +#endif + } + } +#if LOG_NDEBUG == 0 + if (retry) { + LOG(VERBOSE) << "InputBufferManager::processNotifications -- " + << "will retry again in " << *timeToRetryNs << "ns."; + } else { + LOG(VERBOSE) << "InputBufferManager::processNotifications -- " + << "no pending death notifications."; + } +#endif + return retry; +} + +void InputBufferManager::main() { + LOG(VERBOSE) << "InputBufferManager main -- started."; + nsecs_t timeToRetryNs; + while (true) { + std::unique_lock lock(mMutex); + while (mDeathNotifications.empty()) { + mOnBufferDestroyed.wait(lock); + } + lock.unlock(); + while (processNotifications(&timeToRetryNs)) { + std::this_thread::sleep_for( + std::chrono::nanoseconds(timeToRetryNs)); + } + } +} + +InputBufferManager::InputBufferManager() + : mMainThread{&InputBufferManager::main, this} { +} + +InputBufferManager& InputBufferManager::getInstance() { + static InputBufferManager instance{}; + return instance; +} + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + + + diff --git a/media/codec2/hidl/1.0/utils/InputSurface.cpp b/media/codec2/hidl/1.0/utils/InputSurface.cpp index b669460941..2cbe64b967 100644 --- a/media/codec2/hidl/1.0/utils/InputSurface.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurface.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-InputSurface" -#include +#include #include #include -#include #include #include @@ -36,9 +35,10 @@ namespace utils { using namespace ::android; -class InputSurface::ConfigurableImpl : public C2InterfaceHelper { +// Derived class of C2InterfaceHelper +class InputSurface::Interface : public C2InterfaceHelper { public: - explicit ConfigurableImpl( + explicit Interface( const std::shared_ptr &helper) : C2InterfaceHelper(helper) { @@ -63,33 +63,34 @@ private: std::shared_ptr mEos; }; -namespace { - -class ConfigurableWrapper : public ConfigurableC2Intf { +// Derived class of ConfigurableC2Intf +class InputSurface::ConfigurableIntf : public ConfigurableC2Intf { public: - ConfigurableWrapper( - const std::shared_ptr &impl, + ConfigurableIntf( + const std::shared_ptr &intf, const sp &source) - : ConfigurableC2Intf("input-surface"), - mImpl(impl), + : ConfigurableC2Intf("input-surface", 0), + mIntf(intf), mSource(source) { } - ~ConfigurableWrapper() override = default; + virtual ~ConfigurableIntf() override = default; - c2_status_t query( + virtual c2_status_t query( const std::vector &indices, c2_blocking_t mayBlock, - std::vector>* const params) const override { - return mImpl->query({}, indices, mayBlock, params); + std::vector>* const params + ) const override { + return mIntf->query({}, indices, mayBlock, params); } - c2_status_t config( + virtual c2_status_t config( const std::vector ¶ms, c2_blocking_t mayBlock, - std::vector>* const failures) override { - c2_status_t err = mImpl->config(params, mayBlock, failures); - if (mImpl->eos()) { + std::vector>* const failures + ) override { + c2_status_t err = mIntf->config(params, mayBlock, failures); + if (mIntf->eos()) { sp source = mSource.promote(); if (source == nullptr || source->signalEndOfInputStream() != OK) { // TODO: put something in |failures| @@ -100,202 +101,71 @@ public: return err; } - c2_status_t querySupportedParams( - std::vector>* const params) const override { - return mImpl->querySupportedParams(params); + virtual c2_status_t querySupportedParams( + std::vector>* const params + ) const override { + return mIntf->querySupportedParams(params); } - c2_status_t querySupportedValues( + virtual c2_status_t querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const override { - return mImpl->querySupportedValues(fields, mayBlock); + return mIntf->querySupportedValues(fields, mayBlock); } private: - const std::shared_ptr mImpl; + const std::shared_ptr mIntf; wp mSource; }; -} // namespace - - -Return InputSurface::connectToComponent( - const sp& component, - connectToComponent_cb _hidl_cb) { - Status status; - sp conn; - if (!component) { - status = Status::BAD_VALUE; - } else { - std::shared_ptr comp = mStore->findC2Component(component); - if (!comp) { - conn = new InputSurfaceConnection(mSource, component); - } else { - conn = new InputSurfaceConnection(mSource, comp); - } - if (!conn->init()) { - conn = nullptr; - status = Status::BAD_VALUE; - } else { - status = Status::OK; - } - } - _hidl_cb(status, conn); - return Void(); +Return> InputSurface::getGraphicBufferProducer() { + return mProducer; } Return> InputSurface::getConfigurable() { return mConfigurable; } -// Derived methods from IGraphicBufferProducer - -Return InputSurface::requestBuffer( - int32_t slot, - requestBuffer_cb _hidl_cb) { - return mBase->requestBuffer(slot, _hidl_cb); -} - -Return InputSurface::setMaxDequeuedBufferCount( - int32_t maxDequeuedBuffers) { - return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers); -} - -Return InputSurface::setAsyncMode( - bool async) { - return mBase->setAsyncMode(async); -} - -Return InputSurface::dequeueBuffer( - uint32_t width, - uint32_t height, - PixelFormat format, - uint32_t usage, - bool getFrameTimestamps, - dequeueBuffer_cb _hidl_cb) { - return mBase->dequeueBuffer( - width, height, format, usage, getFrameTimestamps, _hidl_cb); -} - -Return InputSurface::detachBuffer( - int32_t slot) { - return mBase->detachBuffer(slot); -} - -Return InputSurface::detachNextBuffer( - detachNextBuffer_cb _hidl_cb) { - return mBase->detachNextBuffer(_hidl_cb); -} - -Return InputSurface::attachBuffer( - const AnwBuffer& buffer, - attachBuffer_cb _hidl_cb) { - return mBase->attachBuffer(buffer, _hidl_cb); -} - -Return InputSurface::queueBuffer( - int32_t slot, - const QueueBufferInput& input, - queueBuffer_cb _hidl_cb) { - return mBase->queueBuffer(slot, input, _hidl_cb); -} - -Return InputSurface::cancelBuffer( - int32_t slot, - const hidl_handle& fence) { - return mBase->cancelBuffer(slot, fence); -} - -Return InputSurface::query( - int32_t what, - query_cb _hidl_cb) { - return mBase->query(what, _hidl_cb); -} - Return InputSurface::connect( - const sp& listener, - int32_t api, - bool producerControlledByApp, + const sp& sink, connect_cb _hidl_cb) { - return mBase->connect(listener, api, producerControlledByApp, _hidl_cb); -} - -Return InputSurface::disconnect( - int32_t api, - DisconnectMode mode) { - return mBase->disconnect(api, mode); -} - -Return InputSurface::setSidebandStream( - const hidl_handle& stream) { - return mBase->setSidebandStream(stream); -} - -Return InputSurface::allocateBuffers( - uint32_t width, - uint32_t height, - PixelFormat format, - uint32_t usage) { - return mBase->allocateBuffers(width, height, format, usage); -} - -Return InputSurface::allowAllocation( - bool allow) { - return mBase->allowAllocation(allow); -} - -Return InputSurface::setGenerationNumber( - uint32_t generationNumber) { - return mBase->setGenerationNumber(generationNumber); -} - -Return InputSurface::getConsumerName( - getConsumerName_cb _hidl_cb) { - return mBase->getConsumerName(_hidl_cb); -} - -Return InputSurface::setSharedBufferMode( - bool sharedBufferMode) { - return mBase->setSharedBufferMode(sharedBufferMode); -} - -Return InputSurface::setAutoRefresh( - bool autoRefresh) { - return mBase->setAutoRefresh(autoRefresh); -} - -Return InputSurface::setDequeueTimeout( - int64_t timeoutNs) { - return mBase->setDequeueTimeout(timeoutNs); -} - -Return InputSurface::getLastQueuedBuffer( - getLastQueuedBuffer_cb _hidl_cb) { - return mBase->getLastQueuedBuffer(_hidl_cb); -} - -Return InputSurface::getFrameTimestamps( - getFrameTimestamps_cb _hidl_cb) { - return mBase->getFrameTimestamps(_hidl_cb); + Status status; + sp connection; + if (!sink) { + _hidl_cb(Status::BAD_VALUE, nullptr); + return Void(); + } + std::shared_ptr comp = Component::findLocalComponent(sink); + if (comp) { + connection = new InputSurfaceConnection(mSource, comp, mStore); + } else { + connection = new InputSurfaceConnection(mSource, sink, mStore); + } + if (!connection->init()) { + connection = nullptr; + status = Status::BAD_VALUE; + } else { + status = Status::OK; + } + _hidl_cb(status, connection); + return Void(); } -Return InputSurface::getUniqueId( - getUniqueId_cb _hidl_cb) { - return mBase->getUniqueId(_hidl_cb); -} +// Derived methods from IGraphicBufferProducer // Constructor is exclusive to ComponentStore. InputSurface::InputSurface( const sp& store, const std::shared_ptr& reflector, - const sp& base, - const sp& source) : - mStore(store), - mBase(base), - mSource(source), - mHelper(std::make_shared(reflector)), - mConfigurable(new CachedConfigurable( - std::make_unique(mHelper, source))) { + const sp& producer, + const sp& source) + : mStore{store}, + mProducer{producer}, + mSource{source}, + mIntf{std::make_shared(reflector)}, + mConfigurable{new CachedConfigurable( + std::make_unique( + mIntf, source))} { mConfigurable->init(store.get()); } diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp index 8b1ece3096..1024f50be0 100644 --- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-InputSurfaceConnection" -#include +#include +#include #include #include @@ -65,51 +66,74 @@ public: } // unnamed namespace +// Derived class of ComponentWrapper for use with +// GraphicBufferSource::configure(). +// struct InputSurfaceConnection::Impl : public ComponentWrapper { + Impl(const sp& source, - const std::shared_ptr& comp) : - mSource(source), mComp(comp), mRemoteComp(), - mFrameIndex(0) { - std::shared_ptr intf = comp->intf(); - mCompName = intf ? intf->getName() : ""; + const std::shared_ptr& localComp) + : mSource{source}, mLocalComp{localComp}, mSink{}, mFrameIndex{0} { + std::shared_ptr intf = localComp->intf(); + mSinkName = intf ? intf->getName() : ""; } Impl(const sp& source, - const sp& comp) : - mSource(source), mComp(), mRemoteComp(comp), - mFrameIndex(0) { - Return transStatus = comp->getName( - [this](const hidl_string& name) { - mCompName = name.c_str(); + const sp& sink) + : mSource{source}, mLocalComp{}, mSink{sink}, mFrameIndex{0} { + Return> transResult = sink->getConfigurable(); + if (!transResult.isOk()) { + LOG(ERROR) << "Remote sink is dead."; + return; + } + mSinkConfigurable = + static_cast>(transResult); + if (!mSinkConfigurable) { + LOG(ERROR) << "Remote sink is not configurable."; + mSinkName = ""; + return; + } + + hidl_string name; + Return transStatus = mSinkConfigurable->getName( + [&name](const hidl_string& n) { + name = n; }); if (!transStatus.isOk()) { - ALOGD("getName -- Cannot obtain remote component name."); + LOG(ERROR) << "Remote sink's configurable is dead."; + mSinkName = ""; + return; } + mSinkName = name.c_str(); } - virtual ~Impl() = default; + virtual ~Impl() { + mSource->stop(); + mSource->release(); + } bool init() { - sp source = mSource.promote(); - if (source == nullptr) { + if (mSource == nullptr) { return false; } - status_t err = source->initCheck(); + status_t err = mSource->initCheck(); if (err != OK) { - ALOGD("Impl::init -- GBS init failed: %d", err); + LOG(WARNING) << "Impl::init -- GraphicBufferSource init failed: " + << "status = " << err << "."; return false; } // TODO: read settings properly from the interface C2VideoSizeStreamTuning::input inputSize; C2StreamUsageTuning::input usage; - c2_status_t c2Status = compQuery({ &inputSize, &usage }, + c2_status_t c2Status = queryFromSink({ &inputSize, &usage }, {}, C2_MAY_BLOCK, nullptr); if (c2Status != C2_OK) { - ALOGD("Impl::init -- cannot query information from " - "the component interface: %s.", asString(c2Status)); + LOG(WARNING) << "Impl::init -- cannot query information from " + "the component interface: " + << "status = " << asString(c2Status) << "."; return false; } @@ -122,26 +146,27 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { // asGrallocUsage(); uint32_t grallocUsage = - mCompName.compare(0, 11, "c2.android.") == 0 ? + mSinkName.compare(0, 11, "c2.android.") == 0 ? GRALLOC_USAGE_SW_READ_OFTEN : GRALLOC_USAGE_HW_VIDEO_ENCODER; - err = source->configure( + err = mSource->configure( this, dataSpace, kBufferCount, inputSize.width, inputSize.height, grallocUsage); if (err != OK) { - ALOGD("Impl::init -- GBS configure failed: %d", err); + LOG(WARNING) << "Impl::init -- GBS configure failed: " + << "status = " << err << "."; return false; } for (int32_t i = 0; i < kBufferCount; ++i) { - if (!source->onInputBufferAdded(i).isOk()) { - ALOGD("Impl::init: populating GBS slots failed"); + if (!mSource->onInputBufferAdded(i).isOk()) { + LOG(WARNING) << "Impl::init: failed to populate GBS slots."; return false; } } - if (!source->start().isOk()) { - ALOGD("Impl::init -- GBS start failed"); + if (!mSource->start().isOk()) { + LOG(WARNING) << "Impl::init -- GBS failed to start."; return false; } mAllocatorMutex.lock(); @@ -150,7 +175,8 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { &mAllocator); mAllocatorMutex.unlock(); if (c2err != OK) { - ALOGD("Impl::init -- failed to fetch gralloc allocator: %d", c2err); + LOG(WARNING) << "Impl::init -- failed to fetch gralloc allocator: " + << "status = " << asString(c2err) << "."; return false; } return true; @@ -162,7 +188,7 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { const sp& buffer, int64_t timestamp, int fenceFd) override { - ALOGV("Impl::submitBuffer -- bufferId = %d", bufferId); + LOG(VERBOSE) << "Impl::submitBuffer -- bufferId = " << bufferId << "."; // TODO: Use fd to construct fence (void)fenceFd; @@ -190,9 +216,8 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { // TODO: fence new Buffer2D(block->share( C2Rect(block->width(), block->height()), ::C2Fence())), - [bufferId, src = mSource](C2Buffer* ptr) { + [bufferId, source = mSource](C2Buffer* ptr) { delete ptr; - sp source = src.promote(); if (source != nullptr) { // TODO: fence (void)source->onInputBufferEmptied(bufferId, -1); @@ -204,12 +229,13 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { std::list> items; items.push_back(std::move(work)); - err = compQueue(&items); + err = queueToSink(&items); return (err == C2_OK) ? OK : UNKNOWN_ERROR; } - virtual status_t submitEos(int32_t /* bufferId */) override { - ALOGV("Impl::submitEos"); + virtual status_t submitEos(int32_t bufferId) override { + LOG(VERBOSE) << "Impl::submitEos -- bufferId = " << bufferId << "."; + (void)bufferId; std::unique_ptr work(new C2Work); work->input.flags = (C2FrameData::flags_t)0; @@ -221,11 +247,11 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { std::list> items; items.push_back(std::move(work)); - c2_status_t err = compQueue(&items); + c2_status_t err = queueToSink(&items); return (err == C2_OK) ? OK : UNKNOWN_ERROR; } - void dispatchDataSpaceChanged( + virtual void dispatchDataSpaceChanged( int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { // TODO (void)dataSpace; @@ -233,36 +259,63 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { (void)pixelFormat; } + // Configurable interface for InputSurfaceConnection::Impl. + // + // This class is declared as an inner class so that it will have access to + // all Impl's members. + struct ConfigurableIntf : public ConfigurableC2Intf { + sp mConnection; + ConfigurableIntf(const sp& connection) + : ConfigurableC2Intf{"input-surface-connection", 0}, + mConnection{connection} {} + virtual c2_status_t config( + const std::vector ¶ms, + c2_blocking_t mayBlock, + std::vector> *const failures + ) override; + virtual c2_status_t query( + const std::vector &indices, + c2_blocking_t mayBlock, + std::vector> *const params) const override; + virtual c2_status_t querySupportedParams( + std::vector> *const params + ) const override; + virtual c2_status_t querySupportedValues( + std::vector &fields, + c2_blocking_t mayBlock) const override; + }; + private: - c2_status_t compQuery( + c2_status_t queryFromSink( const std::vector &stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock, std::vector>* const heapParams) { - std::shared_ptr comp = mComp.lock(); - if (comp) { - std::shared_ptr intf = comp->intf(); + if (mLocalComp) { + std::shared_ptr intf = mLocalComp->intf(); if (intf) { return intf->query_vb(stackParams, heapParamIndices, mayBlock, heapParams); } else { - ALOGD("compQuery -- component does not have an interface."); + LOG(ERROR) << "queryFromSink -- " + << "component does not have an interface."; return C2_BAD_STATE; } } - if (!mRemoteComp) { - ALOGD("compQuery -- component no longer exists."); - return C2_BAD_STATE; - } + + CHECK(mSink) << "-- queryFromSink " + << "-- connection has no sink."; + CHECK(mSinkConfigurable) << "-- queryFromSink " + << "-- sink has no configurable."; hidl_vec indices( stackParams.size() + heapParamIndices.size()); size_t numIndices = 0; for (C2Param* const& stackParam : stackParams) { if (!stackParam) { - ALOGD("compQuery -- null stack param encountered."); + LOG(DEBUG) << "queryFromSink -- null stack param encountered."; continue; } indices[numIndices++] = static_cast(stackParam->index()); @@ -277,22 +330,22 @@ private: heapParams->reserve(heapParams->size() + numIndices); } c2_status_t status; - Return transStatus = mRemoteComp->query( + Return transStatus = mSinkConfigurable->query( indices, mayBlock == C2_MAY_BLOCK, [&status, &numStackIndices, &stackParams, heapParams]( Status s, const Params& p) { status = static_cast(s); if (status != C2_OK && status != C2_BAD_INDEX) { - ALOGD("compQuery -- call failed: %s.", asString(status)); + LOG(DEBUG) << "queryFromSink -- call failed: " + << "status = " << asString(status) << "."; return; } std::vector paramPointers; - c2_status_t parseStatus = parseParamsBlob(¶mPointers, p); - if (parseStatus != C2_OK) { - ALOGD("compQuery -- error while parsing params: %s.", - asString(parseStatus)); - status = parseStatus; + if (!parseParamsBlob(¶mPointers, p)) { + LOG(DEBUG) << "queryFromSink -- error while " + << "parsing params."; + status = C2_CORRUPTED; return; } size_t i = 0; @@ -302,7 +355,8 @@ private: if (numStackIndices > 0) { --numStackIndices; if (!paramPointer) { - ALOGD("compQuery -- null stack param."); + LOG(DEBUG) << "queryFromSink -- " + "null stack param."; ++it; continue; } @@ -313,25 +367,27 @@ private: CHECK(i < stackParams.size()); if (stackParams[i]->index() != paramPointer->index()) { - ALOGD("compQuery -- param skipped. index = %d", - static_cast( - stackParams[i]->index())); + LOG(DEBUG) << "queryFromSink -- " + "param skipped (index = " + << stackParams[i]->index() << ")."; stackParams[i++]->invalidate(); continue; } if (!stackParams[i++]->updateFrom(*paramPointer)) { - ALOGD("compQuery -- param update failed: " - "index = %d.", - static_cast(paramPointer->index())); + LOG(DEBUG) << "queryFromSink -- " + "param update failed (index = " + << paramPointer->index() << ")."; } } else { if (!paramPointer) { - ALOGD("compQuery -- null heap param."); + LOG(DEBUG) << "queryFromSink -- " + "null heap param."; ++it; continue; } if (!heapParams) { - ALOGD("compQuery -- too many stack params."); + LOG(WARNING) << "queryFromSink -- " + "too many stack params."; break; } heapParams->emplace_back(C2Param::Copy(*paramPointer)); @@ -340,94 +396,128 @@ private: } }); if (!transStatus.isOk()) { - ALOGD("compQuery -- transaction failed."); + LOG(ERROR) << "queryFromSink -- transaction failed."; return C2_CORRUPTED; } return status; } - c2_status_t compQueue(std::list>* const items) { - std::shared_ptr comp = mComp.lock(); - if (comp) { - return comp->queue_nb(items); + c2_status_t queueToSink(std::list>* const items) { + if (mLocalComp) { + return mLocalComp->queue_nb(items); } + CHECK(mSink) << "-- queueToSink " + << "-- connection has no sink."; + WorkBundle workBundle; - Status hidlStatus = objcpy(&workBundle, *items, nullptr); - if (hidlStatus != Status::OK) { - ALOGD("compQueue -- bad input."); + if (!objcpy(&workBundle, *items, nullptr)) { + LOG(ERROR) << "queueToSink -- bad input."; return C2_CORRUPTED; } - Return transStatus = mRemoteComp->queue(workBundle); + Return transStatus = mSink->queue(workBundle); if (!transStatus.isOk()) { - ALOGD("compQueue -- transaction failed."); + LOG(ERROR) << "queueToSink -- transaction failed."; return C2_CORRUPTED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGV("compQueue -- call failed: %s.", asString(status)); + LOG(DEBUG) << "queueToSink -- call failed: " + << asString(status); } return status; } - wp mSource; - std::weak_ptr mComp; - sp mRemoteComp; - std::string mCompName; + sp mSource; + std::shared_ptr mLocalComp; + sp mSink; + sp mSinkConfigurable; + std::string mSinkName; // Needed for ComponentWrapper implementation std::mutex mAllocatorMutex; std::shared_ptr mAllocator; std::atomic_uint64_t mFrameIndex; + }; InputSurfaceConnection::InputSurfaceConnection( const sp& source, - const std::shared_ptr& comp) : - mSource(source), - mImpl(new Impl(source, comp)) { + const std::shared_ptr& comp, + const sp& store) + : mImpl{new Impl(source, comp)}, + mConfigurable{new CachedConfigurable( + std::make_unique(mImpl))} { + mConfigurable->init(store.get()); } InputSurfaceConnection::InputSurfaceConnection( const sp& source, - const sp& comp) : - mSource(source), - mImpl(new Impl(source, comp)) { + const sp& sink, + const sp& store) + : mImpl{new Impl(source, sink)}, + mConfigurable{new CachedConfigurable( + std::make_unique(mImpl))} { + mConfigurable->init(store.get()); +} + +Return InputSurfaceConnection::disconnect() { + std::lock_guard lock(mImplMutex); + mImpl = nullptr; + return Status::OK; } InputSurfaceConnection::~InputSurfaceConnection() { - if (mSource) { - (void)mSource->stop(); - (void)mSource->release(); - mSource.clear(); - } - mImpl.clear(); + mImpl = nullptr; } bool InputSurfaceConnection::init() { - mMutex.lock(); - sp impl = mImpl; - mMutex.unlock(); + std::lock_guard lock(mImplMutex); + return mImpl->init(); +} - if (!impl) { - return false; - } - return impl->init(); +Return> InputSurfaceConnection::getConfigurable() { + return mConfigurable; } -Return InputSurfaceConnection::disconnect() { - ALOGV("disconnect"); - mMutex.lock(); - if (mSource) { - (void)mSource->stop(); - (void)mSource->release(); - mSource.clear(); - } - mImpl.clear(); - mMutex.unlock(); - ALOGV("disconnected"); - return Status::OK; +// Configurable interface for InputSurfaceConnection::Impl +c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::config( + const std::vector ¶ms, + c2_blocking_t mayBlock, + std::vector> *const failures) { + // TODO: implement + (void)params; + (void)mayBlock; + (void)failures; + return C2_OK; +} + +c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::query( + const std::vector &indices, + c2_blocking_t mayBlock, + std::vector> *const params) const { + // TODO: implement + (void)indices; + (void)mayBlock; + (void)params; + return C2_OK; +} + +c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedParams( + std::vector> *const params) const { + // TODO: implement + (void)params; + return C2_OK; +} + +c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedValues( + std::vector &fields, + c2_blocking_t mayBlock) const { + // TODO: implement + (void)fields; + (void)mayBlock; + return C2_OK; } } // namespace utils diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h index 0908226b87..4ac95c5d04 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +17,15 @@ #ifndef CODEC2_HIDL_V1_0_UTILS_COMPONENT_H #define CODEC2_HIDL_V1_0_UTILS_COMPONENT_H +#include #include #include #include +#include +#include #include #include -#include #include #include @@ -31,9 +33,9 @@ #include #include -#include #include #include +#include namespace android { namespace hardware { @@ -54,19 +56,8 @@ using ::android::wp; struct ComponentStore; -struct ComponentInterface : public Configurable { - ComponentInterface( - const std::shared_ptr& interface, - const sp& store); - c2_status_t status() const; - -protected: - c2_status_t mInit; - std::shared_ptr mInterface; - sp mStore; -}; - -struct Component : public Configurable { +struct Component : public IComponent, + public std::enable_shared_from_this { Component( const std::shared_ptr&, const sp& listener, @@ -85,10 +76,14 @@ struct Component : public Configurable { virtual Return setOutputSurface( uint64_t blockPoolId, const sp& surface) override; - virtual Return connectToOmxInputSurface( + 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) override; + IGraphicBufferSource>& source, + connectToOmxInputSurface_cb _hidl_cb) override; virtual Return disconnectFromInputSurface() override; virtual Return createBlockPool( uint32_t allocatorId, @@ -98,63 +93,34 @@ struct Component : public Configurable { virtual Return stop() override; virtual Return reset() override; virtual Return release() override; + virtual Return> getInterface() 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; - std::shared_ptr mInterface; + sp mInterface; sp mListener; sp mStore; ::android::hardware::media::c2::V1_0::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; - // This struct is a comparable wrapper for IComponent. - // - // An IComponent object is either local or remote. If it is local, we can - // use the underlying pointer as a key. If it is remote, we have to use the - // underlying pointer of the associated binder object as a key. - // - // See interfacesEqual() for more detail. - struct InterfaceKey { - // An InterfaceKey is constructed from IComponent. - InterfaceKey(const sp& component); - // operator< is defined here to control the default definition of - // std::less, which will be used in type Roster defined - // below. - bool operator<(const InterfaceKey& other) const { - return isRemote ? - (other.isRemote ? - // remote & remote - std::less()( - remote.unsafe_get(), - other.remote.unsafe_get()) : - // remote & local - false) : - (other.isRemote ? - // local & remote - true : - // local & local - std::less()( - local.unsafe_get(), - other.local.unsafe_get())); - } - private: - bool isRemote; - wp remote; - wp local; - }; - - typedef std::map> Roster; - typedef Roster::const_iterator LocalId; - LocalId mLocalId; - void setLocalId(const LocalId& localId); - void initListener(const sp& self); virtual ~Component() override; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h new file mode 100644 index 0000000000..a5d235eaf7 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h @@ -0,0 +1,66 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H +#define CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct ComponentStore; + +struct ComponentInterface : public IComponentInterface { + ComponentInterface( + const std::shared_ptr& interface, + ComponentStore* store); + c2_status_t status() const; + virtual Return> getConfigurable() override; + +protected: + std::shared_ptr mInterface; + sp mConfigurable; + c2_status_t mInit; +}; + + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h index 41e14160f1..be80c621c3 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,18 @@ #define CODEC2_HIDL_V1_0_UTILS_COMPONENTSTORE_H #include +#include #include -#include + #include +#include #include #include #include #include +#include #include #include #include @@ -42,53 +45,61 @@ namespace utils { using ::android::hardware::media::bufferpool::V2_0::IClientManager; -using ::android::hardware::hidl_array; using ::android::hardware::hidl_handle; -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::sp; -using ::android::wp; -struct ComponentStore : public Configurable { +struct ComponentStore : public IComponentStore { ComponentStore(const std::shared_ptr& store); virtual ~ComponentStore() = default; - c2_status_t status() const { - return mInit; - } + /** + * 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); - // Methods from ::android::hardware::media::c2::V1_0::IComponentStore - Return createComponent( + // 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; - Return createInterface( + virtual Return createInterface( const hidl_string& name, createInterface_cb _hidl_cb) override; - Return listComponents(listComponents_cb _hidl_cb) override; - Return> createInputSurface() override; - Return getStructDescriptors( + 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; - Return> getPoolClientManager() override; - Return copyBuffer( + virtual Return> getPoolClientManager() override; + virtual Return copyBuffer( const Buffer& src, const Buffer& dst) override; + virtual Return> getConfigurable() override; - // Debug dump - Return debug( + /** + * Dumps information when lshal is called. + */ + virtual Return debug( const hidl_handle& handle, const hidl_vec& args) override; protected: - // does bookkeeping for an interface that has been loaded + sp mConfigurable; + + // Does bookkeeping for an interface that has been loaded. void onInterfaceLoaded(const std::shared_ptr &intf); c2_status_t mInit; @@ -100,18 +111,33 @@ protected: std::set mLoadedInterfaces; mutable std::mutex mStructDescriptorsMutex; - // Component lifetime management - Component::Roster mComponentRoster; + // ComponentStore keeps track of live Components. + + struct ComponentStatus { + std::shared_ptr c2Component; + std::chrono::system_clock::time_point birthTime; + }; + mutable std::mutex mComponentRosterMutex; - void reportComponentDeath(const Component::LocalId& componentLocalId); + 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; - // C2Component lookup - std::shared_ptr findC2Component( - const sp& component) const; + // Helper functions for dumping. + + std::ostream& dump( + std::ostream& out, + const std::shared_ptr& comp); + + std::ostream& dump( + std::ostream& out, + ComponentStatus& compStatus); - friend struct InputSurface; }; } // namespace utils diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h index 2e33a6f804..8095185fa6 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,13 @@ #ifndef CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H #define CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H -#include +#include +#include #include #include #include -#include -#include - #include namespace android { @@ -35,9 +33,6 @@ namespace c2 { namespace V1_0 { 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; @@ -45,13 +40,53 @@ using ::android::sp; struct ComponentStore; +/** + * Codec2 objects of different types may have different querying and configuring + * functions, but across the Treble boundary, they share the same HIDL + * interface, IConfigurable. + * + * ConfigurableC2Intf is an abstract class that a Codec2 object can implement to + * easily expose an IConfigurable instance. See CachedConfigurable below. + */ +struct ConfigurableC2Intf { + C2String getName() const { return mName; } + uint32_t getId() const { return mId; } + /** C2ComponentInterface::query_vb sans stack params */ + virtual c2_status_t query( + const std::vector &indices, + c2_blocking_t mayBlock, + std::vector>* const params) const = 0; + /** C2ComponentInterface::config_vb */ + virtual c2_status_t config( + const std::vector ¶ms, + c2_blocking_t mayBlock, + std::vector>* const failures) = 0; + /** C2ComponentInterface::querySupportedParams_nb */ + virtual c2_status_t querySupportedParams( + std::vector>* const params) const = 0; + /** C2ComponentInterface::querySupportedParams_nb */ + virtual c2_status_t querySupportedValues( + std::vector& fields, c2_blocking_t mayBlock) const = 0; + + virtual ~ConfigurableC2Intf() = default; + + ConfigurableC2Intf(const C2String& name, uint32_t id) + : mName{name}, mId{id} {} + +protected: + C2String mName; /* cached component name */ + uint32_t mId; +}; + /** * Implementation of the IConfigurable interface that supports caching of * supported parameters from a supplied ComponentStore. * - * This is mainly the same for all of the configurable C2 interfaces though - * there are slight differences in the blocking behavior. This is handled in the - * ConfigurableC2Intf implementations. + * CachedConfigurable essentially converts a ConfigurableC2Intf into HIDL's + * IConfigurable. A Codec2 object generally implements ConfigurableC2Intf and + * passes the implementation to the constructor of CachedConfigurable. + * + * Note that caching happens */ struct CachedConfigurable : public IConfigurable { CachedConfigurable(std::unique_ptr&& intf); @@ -60,6 +95,8 @@ struct CachedConfigurable : public IConfigurable { // Methods from ::android::hardware::media::c2::V1_0::IConfigurable + virtual Return getId() override; + virtual Return getName(getName_cb _hidl_cb) override; virtual Return query( @@ -90,63 +127,6 @@ protected: std::vector> mSupportedParams; }; -/** - * Template that implements the `IConfigurable` interface for an inherited - * interface. Classes that implement a child interface `I` of `IConfigurable` - * can derive from `Configurable`. - */ -template -struct Configurable : public I { - Configurable(const sp& intf): mIntf(intf) { - } - - c2_status_t init(ComponentStore* store) { - return mIntf->init(store); - } - - // Methods from ::android::hardware::media::c2::V1_0::IConfigurable - - using getName_cb = typename I::getName_cb; - virtual Return getName(getName_cb _hidl_cb) override { - return mIntf->getName(_hidl_cb); - } - - using query_cb = typename I::query_cb; - virtual Return query( - const hidl_vec& indices, - bool mayBlock, - query_cb _hidl_cb) override { - return mIntf->query(indices, mayBlock, _hidl_cb); - } - - using config_cb = typename I::config_cb; - virtual Return config( - const hidl_vec& inParams, - bool mayBlock, - config_cb _hidl_cb) override { - return mIntf->config(inParams, mayBlock, _hidl_cb); - } - - using querySupportedParams_cb = typename I::querySupportedParams_cb; - virtual Return querySupportedParams( - uint32_t start, - uint32_t count, - querySupportedParams_cb _hidl_cb) override { - return mIntf->querySupportedParams(start, count, _hidl_cb); - } - - using querySupportedValues_cb = typename I::querySupportedValues_cb; - virtual Return querySupportedValues( - const hidl_vec& inFields, - bool mayBlock, - querySupportedValues_cb _hidl_cb) override { - return mIntf->querySupportedValues(inFields, mayBlock, _hidl_cb); - } - -protected: - sp mIntf; -}; - } // namespace utils } // namespace V1_0 } // namespace c2 @@ -155,3 +135,4 @@ protected: } // namespace android #endif // CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H + diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ConfigurableC2Intf.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ConfigurableC2Intf.h deleted file mode 100644 index b8801bb696..0000000000 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ConfigurableC2Intf.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -#ifndef CODEC2_HIDL_V1_0_UTILS_CONFIGURABLEC2INTF_H -#define CODEC2_HIDL_V1_0_UTILS_CONFIGURABLEC2INTF_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { -namespace hardware { -namespace media { -namespace c2 { -namespace V1_0 { -namespace utils { - -using ::android::sp; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; - -/** - * Common Codec 2.0 interface wrapper. - */ -struct ConfigurableC2Intf { - C2String getName() const { return mName; } - /** C2ComponentInterface::query_vb sans stack params */ - virtual c2_status_t query( - const std::vector &indices, - c2_blocking_t mayBlock, - std::vector>* const params) const = 0; - /** C2ComponentInterface::config_vb */ - virtual c2_status_t config( - const std::vector ¶ms, - c2_blocking_t mayBlock, - std::vector>* const failures) = 0; - /** C2ComponentInterface::querySupportedParams_nb */ - virtual c2_status_t querySupportedParams( - std::vector>* const params) const = 0; - /** C2ComponentInterface::querySupportedParams_nb */ - virtual c2_status_t querySupportedValues( - std::vector& fields, c2_blocking_t mayBlock) const = 0; - - virtual ~ConfigurableC2Intf() = default; - - ConfigurableC2Intf(const C2String& name) : mName(name) {} - -protected: - C2String mName; /* cache component name */ -}; - -} // namespace utils -} // namespace V1_0 -} // namespace c2 -} // namespace media -} // namespace hardware -} // namespace android - -#endif // CODEC2_HIDL_V1_0_UTILS_CONFIGURABLEC2INTF_H diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h new file mode 100644 index 0000000000..b6857d5f71 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h @@ -0,0 +1,294 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H +#define CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H + +#include +#include + +#include +#include + +#include +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + +using namespace ::android; + +/** + * InputBufferManager + * ================== + * + * InputBufferManager presents a way to track and untrack input buffers in this + * (codec) process and send a notification to a listener, possibly in a + * different process, when a tracked buffer no longer has any references in this + * process. + * + * InputBufferManager holds a collection of records representing tracked buffers + * and their callback listeners. Conceptually, one record is a triple (listener, + * frameIndex, bufferIndex) where + * + * - (frameIndex, bufferIndex) is a pair of indices used to identify the buffer. + * - listener is of type IComponentListener. Its onInputBuffersReleased() + * function will be called after the associated buffer dies. The argument of + * onInputBuffersReleased() is a list of InputBuffer objects, each of which + * has the following members: + * + * uint64_t frameIndex + * uint32_t arrayIndex + * + * When a tracked buffer associated to the triple (listener, frameIndex, + * bufferIndex) goes out of scope, listener->onInputBuffersReleased() will be + * called with an InputBuffer object whose members are set as follows: + * + * inputBuffer.frameIndex = frameIndex + * inputBuffer.arrayIndex = bufferIndex + * + * IPC Optimization + * ---------------- + * + * Since onInputBuffersReleased() is an IPC call, InputBufferManager tries not + * to call it too often. Any two calls to the same listener are at least + * mNotificationIntervalNs nanoseconds apart, where mNotificationIntervalNs is + * configurable via calling setNotificationInterval(). The default value of + * mNotificationIntervalNs is kDefaultNotificationInternalNs. + * + * Public Member Functions + * ----------------------- + * + * InputBufferManager is a singleton class. Its only instance is accessible via + * the following public functions: + * + * - registerFrameData(const sp& listener, + * const C2FrameData& input) + * + * - unregisterFrameData(const sp& listener, + * const C2FrameData& input) + * + * - unregisterFrameData(const sp& listener) + * + * - setNotificationInterval(nsecs_t notificationIntervalNs) + * + */ + +struct InputBufferManager { + + /** + * The default value for the time interval between 2 subsequent IPCs. + */ + static constexpr nsecs_t kDefaultNotificationIntervalNs = 1000000; /* 1ms */ + + /** + * Track all buffers in a C2FrameData object. + * + * input (C2FrameData) has the following two members that are of interest: + * + * C2WorkOrdinal ordinal + * vector> buffers + * + * Calling registerFrameData(listener, input) will register multiple + * triples (listener, frameIndex, bufferIndex) where frameIndex is equal to + * input.ordinal.frameIndex and bufferIndex runs through the indices of + * input.buffers such that input.buffers[bufferIndex] is not null. + * + * This should be called from queue(). + * + * \param listener Listener of death notifications. + * \param input Input frame data whose input buffers are to be tracked. + */ + static void registerFrameData( + const sp& listener, + const C2FrameData& input); + + /** + * Untrack all buffers in a C2FrameData object. + * + * Calling unregisterFrameData(listener, input) will unregister and remove + * pending notifications for all triples (l, fi, bufferIndex) such that + * l = listener and fi = input.ordinal.frameIndex. + * + * This should be called from onWorkDone() and flush(). + * + * \param listener Previously registered listener. + * \param input Previously registered frame data. + */ + static void unregisterFrameData( + const wp& listener, + const C2FrameData& input); + + /** + * Untrack all buffers associated to a given listener. + * + * Calling unregisterFrameData(listener) will unregister and remove + * pending notifications for all triples (l, frameIndex, bufferIndex) such + * that l = listener. + * + * This should be called when the component cleans up all input buffers, + * i.e., when reset(), release(), stop() or ~Component() is called. + * + * \param listener Previously registered listener. + */ + static void unregisterFrameData( + const wp& listener); + + /** + * Set the notification interval. + * + * \param notificationIntervalNs New notification interval, in nanoseconds. + */ + static void setNotificationInterval(nsecs_t notificationIntervalNs); + +private: + void _registerFrameData( + const sp& listener, + const C2FrameData& input); + void _unregisterFrameData( + const wp& listener, + const C2FrameData& input); + void _unregisterFrameData( + const wp& listener); + void _setNotificationInterval(nsecs_t notificationIntervalNs); + + // The callback function tied to C2Buffer objects. + // + // Note: This function assumes that sInstance is the only instance of this + // class. + static void onBufferDestroyed(const C2Buffer* buf, void* arg); + void _onBufferDestroyed(const C2Buffer* buf, void* arg); + + // Persistent data to be passed as "arg" in onBufferDestroyed(). + // This is essentially the triple (listener, frameIndex, bufferIndex) plus a + // weak pointer to the C2Buffer object. + // + // Note that the "key" is bufferIndex according to operator<(). This is + // designed to work with TrackedBuffersMap defined below. + struct TrackedBuffer { + wp listener; + uint64_t frameIndex; + size_t bufferIndex; + std::weak_ptr buffer; + TrackedBuffer(const wp& listener, + uint64_t frameIndex, + size_t bufferIndex, + const std::shared_ptr& buffer) + : listener(listener), + frameIndex(frameIndex), + bufferIndex(bufferIndex), + buffer(buffer) {} + TrackedBuffer(const TrackedBuffer&) = default; + bool operator<(const TrackedBuffer& other) const { + return bufferIndex < other.bufferIndex; + } + }; + + // Map: listener -> frameIndex -> set. + // Essentially, this is used to store triples (listener, frameIndex, + // bufferIndex) that's searchable by listener and (listener, frameIndex). + // However, the value of the innermost map is TrackedBuffer, which also + // contains an extra copy of listener and frameIndex. This is needed + // because onBufferDestroyed() needs to know listener and frameIndex too. + typedef std::map, + std::map>> TrackedBuffersMap; + + // Storage for pending (unsent) death notifications for one listener. + // Each pair in member named "indices" are (frameIndex, bufferIndex) from + // the (listener, frameIndex, bufferIndex) triple. + struct DeathNotifications { + + // The number of pending notifications for this listener. + // count may be 0, in which case the DeathNotifications object will + // remain valid for only a small period (specified + // nanoseconds). + size_t count; + + // The timestamp of the most recent callback on this listener. This is + // used to guarantee that callbacks do not occur too frequently, and + // also to trigger expiration of a DeathNotifications object that has + // count = 0. + nsecs_t lastSentNs; + + // Map: frameIndex -> vector of bufferIndices + // This is essentially a collection of (framdeIndex, bufferIndex). + std::map> indices; + + DeathNotifications( + nsecs_t notificationIntervalNs = kDefaultNotificationIntervalNs) + : count(0), + lastSentNs(systemTime() - notificationIntervalNs), + indices() {} + }; + + // The minimum time period between IPC calls to notify the client about the + // destruction of input buffers. + std::atomic mNotificationIntervalNs{kDefaultNotificationIntervalNs}; + + // Mutex for the management of all input buffers. + std::mutex mMutex; + + // Tracked input buffers. + TrackedBuffersMap mTrackedBuffersMap; + + // Death notifications to be sent. + // + // A DeathNotifications object is associated to each listener. An entry in + // this map will be removed if its associated DeathNotifications has count = + // 0 and lastSentNs < systemTime() - mNotificationIntervalNs. + std::map, DeathNotifications> mDeathNotifications; + + // Condition variable signaled when an entry is added to mDeathNotifications. + std::condition_variable mOnBufferDestroyed; + + // Notify the clients about buffer destructions. + // Return false if all destructions have been notified. + // Return true and set timeToRetry to the duration to wait for before + // retrying if some destructions have not been notified. + bool processNotifications(nsecs_t* timeToRetryNs); + + // Main function for the input buffer manager thread. + void main(); + + // The thread that manages notifications. + // + // Note: This variable is declared last so its initialization will happen + // after all other member variables have been initialized. + std::thread mMainThread; + + // Private constructor. + InputBufferManager(); + + // The only instance of this class. + static InputBufferManager& getInstance(); + +}; + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H + diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h index cef258ec5e..2682c13bfc 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,21 +19,14 @@ #include -#include -#include - #include -#include -#include -#include - +#include +#include #include -#include - -#include #include +#include -class C2ReflectorHelper; +#include namespace android { namespace hardware { @@ -49,133 +42,31 @@ using ::android::hardware::Return; using ::android::hardware::Void; using ::android::sp; -using ::android::hardware::graphics::common::V1_0::PixelFormat; -using ::android::hardware::media::V1_0::AnwBuffer; - struct InputSurface : public IInputSurface { - typedef ::android::hidl::base::V1_0::IBase IBase; - - typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IProducerListener HProducerListener; - - typedef ::android:: - IGraphicBufferProducer BGraphicBufferProducer; - typedef ::android::hardware::graphics::bufferqueue::V1_0:: IGraphicBufferProducer HGraphicBufferProducer; typedef ::android:: GraphicBufferSource GraphicBufferSource; -// Type disambiguation - - typedef ::android::hardware::media::c2::V1_0::Status Status; - -// New methods from IInputSurface - - virtual Return connectToComponent( - const sp& component, - connectToComponent_cb _hidl_cb) override; + virtual Return> getGraphicBufferProducer() override; virtual Return> getConfigurable() override; -// Methods derived from IGraphicBufferProducer - - virtual Return requestBuffer( - int32_t slot, - requestBuffer_cb _hidl_cb) override; - - virtual Return setMaxDequeuedBufferCount( - int32_t maxDequeuedBuffers) override; - - virtual Return setAsyncMode( - bool async) override; - - virtual Return dequeueBuffer( - uint32_t width, - uint32_t height, - PixelFormat format, - uint32_t usage, - bool getFrameTimestamps, - dequeueBuffer_cb _hidl_cb) override; - - virtual Return detachBuffer( - int32_t slot) override; - - virtual Return detachNextBuffer( - detachNextBuffer_cb _hidl_cb) override; - - virtual Return attachBuffer( - const AnwBuffer& buffer, - attachBuffer_cb _hidl_cb) override; - - virtual Return queueBuffer( - int32_t slot, - const QueueBufferInput& input, - queueBuffer_cb _hidl_cb) override; - - virtual Return cancelBuffer( - int32_t slot, - const hidl_handle& fence) override; - - virtual Return query( - int32_t what, - query_cb _hidl_cb) override; - virtual Return connect( - const sp& listener, - int32_t api, - bool producerControlledByApp, + const sp& sink, connect_cb _hidl_cb) override; - virtual Return disconnect( - int32_t api, - DisconnectMode mode) override; - - virtual Return setSidebandStream( - const hidl_handle& stream) override; - - virtual Return allocateBuffers( - uint32_t width, - uint32_t height, - PixelFormat format, - uint32_t usage) override; - - virtual Return allowAllocation( - bool allow) override; - - virtual Return setGenerationNumber( - uint32_t generationNumber) override; - - virtual Return getConsumerName( - getConsumerName_cb _hidl_cb) override; - - virtual Return setSharedBufferMode( - bool sharedBufferMode) override; - - virtual Return setAutoRefresh( - bool autoRefresh) override; - - virtual Return setDequeueTimeout( - int64_t timeoutNs) override; - - virtual Return getLastQueuedBuffer( - getLastQueuedBuffer_cb _hidl_cb) override; - - virtual Return getFrameTimestamps( - getFrameTimestamps_cb _hidl_cb) override; - - virtual Return getUniqueId( - getUniqueId_cb _hidl_cb) override; +protected: - class ConfigurableImpl; + class Interface; + class ConfigurableIntf; -protected: sp mStore; - sp mBase; + sp mProducer; sp mSource; - std::shared_ptr mHelper; + std::shared_ptr mIntf; sp mConfigurable; InputSurface( @@ -187,6 +78,7 @@ protected: virtual ~InputSurface() override = default; friend struct ComponentStore; + }; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h index 904fa9e403..758b6b2e2b 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,10 @@ #define CODEC2_HIDL_V1_0_UTILS_INPUTSURFACECONNECTION_H #include +#include #include +#include #include #include @@ -44,19 +46,28 @@ using ::android::hardware::Void; using ::android::sp; using ::android::GraphicBufferSource; +// An InputSurfaceConnection connects an InputSurface to a sink, which may be an +// IInputSink or a local C2Component. This can be specified by choosing the +// corresponding constructor. The reason for distinguishing these two cases is +// that when an InputSurfaceConnection lives in the same process as the +// component that processes the buffers, data parceling is not needed. struct InputSurfaceConnection : public IInputSurfaceConnection { virtual Return disconnect() override; + virtual Return> getConfigurable() override; + protected: InputSurfaceConnection( const sp& source, - const std::shared_ptr& component); + const std::shared_ptr& comp, + const sp& store); InputSurfaceConnection( const sp& source, - const sp& component); + const sp& sink, + const sp& store); bool init(); @@ -68,9 +79,9 @@ protected: struct Impl; - std::mutex mMutex; - sp mSource; + std::mutex mImplMutex; sp mImpl; + sp mConfigurable; virtual ~InputSurfaceConnection() override; }; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index d8a50b609a..c38e674389 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ #ifndef CODEC2_HIDL_V1_0_UTILS_TYPES_H #define CODEC2_HIDL_V1_0_UTILS_TYPES_H -#include - #include #include #include @@ -30,6 +28,9 @@ #include #include #include +#include + +#include using namespace std::chrono_literals; @@ -65,66 +66,74 @@ struct C2Hidl_Rect { }; typedef C2GlobalParam C2Hidl_RectInfo; +// Make asString() and operator<< work with Status as well as c2_status_t. +C2_DECLARE_AS_STRING_AND_DEFINE_STREAM_OUT(Status); + +/** + * All objcpy() functions will return a boolean value indicating whether the + * conversion succeeds or not. + */ + // C2SettingResult -> SettingResult -Status objcpy( +bool objcpy( SettingResult* d, const C2SettingResult& s); // SettingResult -> std::unique_ptr -c2_status_t objcpy( +bool objcpy( std::unique_ptr* d, const SettingResult& s); // C2ParamDescriptor -> ParamDescriptor -Status objcpy( +bool objcpy( ParamDescriptor* d, const C2ParamDescriptor& s); // ParamDescriptor -> std::shared_ptr -c2_status_t objcpy( +bool objcpy( std::shared_ptr* d, const ParamDescriptor& s); // C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery -Status objcpy( +bool objcpy( FieldSupportedValuesQuery* d, const C2FieldSupportedValuesQuery& s); // FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery -c2_status_t objcpy( +bool objcpy( C2FieldSupportedValuesQuery* d, const FieldSupportedValuesQuery& s); // C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult -Status objcpy( +bool objcpy( FieldSupportedValuesQueryResult* d, const C2FieldSupportedValuesQuery& s); // FieldSupportedValuesQuery, FieldSupportedValuesQueryResult -> C2FieldSupportedValuesQuery -c2_status_t objcpy( +bool objcpy( C2FieldSupportedValuesQuery* d, const FieldSupportedValuesQuery& sq, const FieldSupportedValuesQueryResult& sr); // C2Component::Traits -> ComponentTraits -Status objcpy( +bool objcpy( IComponentStore::ComponentTraits* d, const C2Component::Traits& s); // ComponentTraits -> C2Component::Traits, std::unique_ptr> // Note: The output d is only valid as long as aliasesBuffer remains alive. -c2_status_t objcpy( +bool objcpy( C2Component::Traits* d, std::unique_ptr>* aliasesBuffer, const IComponentStore::ComponentTraits& s); // C2StructDescriptor -> StructDescriptor -Status objcpy( +bool objcpy( StructDescriptor* d, const C2StructDescriptor& s); // StructDescriptor -> C2StructDescriptor -c2_status_t objcpy( +bool objcpy( std::unique_ptr* d, const StructDescriptor& s); @@ -208,68 +217,77 @@ private: // std::list> -> WorkBundle // Note: If bufferpool will be used, bpSender must not be null. -Status objcpy( +bool objcpy( WorkBundle* d, const std::list>& s, BufferPoolSender* bpSender = nullptr); // WorkBundle -> std::list> -c2_status_t objcpy( +bool objcpy( std::list>* d, const WorkBundle& s); /** - * Parses a params blob and returns C2Param pointers to its params. + * Parses a params blob and returns C2Param pointers to its params. The pointers + * point to locations inside the underlying buffer of \p blob. If \p blob is + * destroyed, the pointers become invalid. + * * \param[out] params target vector of C2Param pointers * \param[in] blob parameter blob to parse - * \retval C2_OK if the full blob was parsed - * \retval C2_BAD_VALUE otherwise + * \retval true if the full blob was parsed + * \retval false otherwise */ -c2_status_t parseParamsBlob( +bool parseParamsBlob( std::vector *params, const hidl_vec &blob); /** * Concatenates a list of C2Params into a params blob. + * * \param[out] blob target blob * \param[in] params parameters to concatenate - * \retval C2_OK if the blob was successfully created - * \retval C2_BAD_VALUE if the blob was not successful (this only happens if the parameters were - * not const) + * \retval true if the blob was successfully created + * \retval false if the blob was not successful (this only happens if the + * parameters were not const) */ -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector ¶ms); -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms); -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms); -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms); /** * Parses a params blob and create a vector of C2Params whose members are copies * of the params in the blob. + * * \param[out] params the resulting vector * \param[in] blob parameter blob to parse - * \retval C2_OK if the full blob was parsed and params was constructed - * \retval C2_BAD_VALUE otherwise + * \retval true if the full blob was parsed and params was constructed + * \retval false otherwise */ -c2_status_t copyParamsFromBlob( +bool copyParamsFromBlob( std::vector>* params, Params blob); +bool copyParamsFromBlob( + std::vector>* params, + Params blob); /** - * Parses a params blob and applies updates to params + * Parses a params blob and applies updates to params. + * * \param[in,out] params params to be updated * \param[in] blob parameter blob containing updates - * \retval C2_OK if the full blob was parsed and params was updated - * \retval C2_BAD_VALUE otherwise + * \retval true if the full blob was parsed and params was updated + * \retval false otherwise */ -c2_status_t updateParamsFromBlob( +bool updateParamsFromBlob( const std::vector& params, const Params& blob); diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index a128a9d9d9..caed839eaf 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,12 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-types" -#include +#include #include #include +#include #include #include @@ -35,10 +36,9 @@ #include #include +#include #include -#include - namespace android { namespace hardware { namespace media { @@ -57,8 +57,18 @@ using ::android::hardware::media::bufferpool::V2_0::implementation:: TransactionId; using ::android::TWGraphicBufferProducer; +const char* asString(Status status, const char* def) { + return asString(static_cast(status), def); +} + namespace /* unnamed */ { +template +typename std::underlying_type::type underlying_value( + EnumClass x) { + return static_cast::type>(x); +} + template void copyVector(DstVector* d, const SrcVector& s) { static_assert(sizeof(Common) == sizeof(decltype((*d)[0])), @@ -73,10 +83,11 @@ void copyVector(DstVector* d, const SrcVector& s) { } // C2ParamField -> ParamField -void objcpy(ParamField *d, const C2ParamField &s) { +bool objcpy(ParamField *d, const C2ParamField &s) { d->index = static_cast(_C2ParamInspector::GetIndex(s)); d->fieldId.offset = static_cast(_C2ParamInspector::GetOffset(s)); d->fieldId.size = static_cast(_C2ParamInspector::GetSize(s)); + return true; } struct C2ParamFieldBuilder : public C2ParamField { @@ -92,21 +103,23 @@ struct C2ParamFieldBuilder : public C2ParamField { }; // C2WorkOrdinalStruct -> WorkOrdinal -void objcpy(WorkOrdinal *d, const C2WorkOrdinalStruct &s) { +bool objcpy(WorkOrdinal *d, const C2WorkOrdinalStruct &s) { d->frameIndex = static_cast(s.frameIndex.peeku()); d->timestampUs = static_cast(s.timestamp.peeku()); d->customOrdinal = static_cast(s.customOrdinal.peeku()); + return true; } // WorkOrdinal -> C2WorkOrdinalStruct -void objcpy(C2WorkOrdinalStruct *d, const WorkOrdinal &s) { +bool objcpy(C2WorkOrdinalStruct *d, const WorkOrdinal &s) { d->frameIndex = c2_cntr64_t(s.frameIndex); d->timestamp = c2_cntr64_t(s.timestampUs); d->customOrdinal = c2_cntr64_t(s.customOrdinal); + return true; } // C2FieldSupportedValues::range's type -> FieldSupportedValues::Range -void objcpy( +bool objcpy( FieldSupportedValues::Range* d, const decltype(C2FieldSupportedValues::range)& s) { d->min = static_cast(s.min.u64); @@ -114,21 +127,24 @@ void objcpy( d->step = static_cast(s.step.u64); d->num = static_cast(s.num.u64); d->denom = static_cast(s.denom.u64); + return true; } // C2FieldSupportedValues -> FieldSupportedValues -Status objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) { - d->typeOther = static_cast(s.type); +bool objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) { switch (s.type) { case C2FieldSupportedValues::EMPTY: d->type = FieldSupportedValues::Type::EMPTY; d->values.resize(0); - return Status::OK; + break; case C2FieldSupportedValues::RANGE: d->type = FieldSupportedValues::Type::RANGE; - objcpy(&d->range, s.range); + if (!objcpy(&d->range, s.range)) { + LOG(ERROR) << "Invalid C2FieldSupportedValues::range."; + return false; + } d->values.resize(0); - return Status::OK; + break; default: switch (s.type) { case C2FieldSupportedValues::VALUES: @@ -138,18 +154,22 @@ Status objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) { d->type = FieldSupportedValues::Type::FLAGS; break; default: - d->type = FieldSupportedValues::Type::OTHER; - // Copy all fields in this case - objcpy(&d->range, s.range); + LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t " + << "with underlying value " << underlying_value(s.type) + << "."; + d->type = static_cast(s.type); + if (!objcpy(&d->range, s.range)) { + LOG(ERROR) << "Invalid C2FieldSupportedValues::range."; + return false; + } } - d->values.resize(s.values.size()); copyVector(&d->values, s.values); - return Status::OK; } + return true; } // FieldSupportedValues::Range -> C2FieldSupportedValues::range's type -void objcpy( +bool objcpy( decltype(C2FieldSupportedValues::range)* d, const FieldSupportedValues::Range& s) { d->min.u64 = static_cast(s.min); @@ -157,19 +177,23 @@ void objcpy( d->step.u64 = static_cast(s.step); d->num.u64 = static_cast(s.num); d->denom.u64 = static_cast(s.denom); + return true; } // FieldSupportedValues -> C2FieldSupportedValues -c2_status_t objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) { +bool objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) { switch (s.type) { case FieldSupportedValues::Type::EMPTY: d->type = C2FieldSupportedValues::EMPTY; - return C2_OK; + break; case FieldSupportedValues::Type::RANGE: d->type = C2FieldSupportedValues::RANGE; - objcpy(&d->range, s.range); + if (!objcpy(&d->range, s.range)) { + LOG(ERROR) << "Invalid FieldSupportedValues::range."; + return false; + } d->values.resize(0); - return C2_OK; + break; default: switch (s.type) { case FieldSupportedValues::Type::VALUES: @@ -179,22 +203,30 @@ c2_status_t objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) { d->type = C2FieldSupportedValues::FLAGS; break; default: - d->type = static_cast(s.typeOther); - // Copy all fields in this case - objcpy(&d->range, s.range); + LOG(DEBUG) << "Unrecognized FieldSupportedValues::Type " + << "with underlying value " << underlying_value(s.type) + << "."; + d->type = static_cast(s.type); + if (!objcpy(&d->range, s.range)) { + LOG(ERROR) << "Invalid FieldSupportedValues::range."; + return false; + } } copyVector(&d->values, s.values); - return C2_OK; } + return true; } } // unnamed namespace // C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery -Status objcpy( +bool objcpy( FieldSupportedValuesQuery* d, const C2FieldSupportedValuesQuery& s) { - objcpy(&d->field, s.field()); + if (!objcpy(&d->field, s.field())) { + LOG(ERROR) << "Invalid C2FieldSupportedValuesQuery::field."; + return false; + } switch (s.type()) { case C2FieldSupportedValuesQuery::POSSIBLE: d->type = FieldSupportedValuesQuery::Type::POSSIBLE; @@ -203,15 +235,16 @@ Status objcpy( d->type = FieldSupportedValuesQuery::Type::CURRENT; break; default: - ALOGE("Unknown type of C2FieldSupportedValuesQuery: %u", - static_cast(s.type())); - return Status::BAD_VALUE; + LOG(DEBUG) << "Unrecognized C2FieldSupportedValuesQuery::type_t " + << "with underlying value " << underlying_value(s.type()) + << "."; + d->type = static_cast(s.type()); } - return Status::OK; + return true; } // FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery -c2_status_t objcpy( +bool objcpy( C2FieldSupportedValuesQuery* d, const FieldSupportedValuesQuery& s) { C2FieldSupportedValuesQuery::type_t dType; @@ -223,16 +256,17 @@ c2_status_t objcpy( dType = C2FieldSupportedValuesQuery::CURRENT; break; default: - ALOGE("Unknown type of FieldSupportedValuesQuery: %u", - static_cast(s.type)); - return C2_BAD_VALUE; + LOG(DEBUG) << "Unrecognized FieldSupportedValuesQuery::Type " + << "with underlying value " << underlying_value(s.type) + << "."; + dType = static_cast(s.type); } *d = C2FieldSupportedValuesQuery(C2ParamFieldBuilder(s.field), dType); - return C2_OK; + return true; } // C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult -Status objcpy( +bool objcpy( FieldSupportedValuesQueryResult* d, const C2FieldSupportedValuesQuery& s) { d->status = static_cast(s.status); @@ -241,20 +275,24 @@ Status objcpy( // FieldSupportedValuesQuery, FieldSupportedValuesQueryResult -> // C2FieldSupportedValuesQuery -c2_status_t objcpy( +bool objcpy( C2FieldSupportedValuesQuery* d, const FieldSupportedValuesQuery& sq, const FieldSupportedValuesQueryResult& sr) { - c2_status_t status = objcpy(d, sq); - if (status != C2_OK) { - return status; + if (!objcpy(d, sq)) { + LOG(ERROR) << "Invalid FieldSupportedValuesQuery."; + return false; } d->status = static_cast(sr.status); - return objcpy(&d->values, sr.values); + if (!objcpy(&d->values, sr.values)) { + LOG(ERROR) << "Invalid FieldSupportedValuesQueryResult::values."; + return false; + } + return true; } // C2Component::Traits -> IComponentStore::ComponentTraits -Status objcpy( +bool objcpy( IComponentStore::ComponentTraits *d, const C2Component::Traits &s) { d->name = s.name; @@ -266,10 +304,19 @@ Status objcpy( case C2Component::DOMAIN_AUDIO: d->domain = IComponentStore::ComponentTraits::Domain::AUDIO; break; - default: + case C2Component::DOMAIN_IMAGE: + d->domain = IComponentStore::ComponentTraits::Domain::IMAGE; + break; + case C2Component::DOMAIN_OTHER: d->domain = IComponentStore::ComponentTraits::Domain::OTHER; + break; + default: + LOG(DEBUG) << "Unrecognized C2Component::domain_t " + << "with underlying value " << underlying_value(s.domain) + << "."; + d->domain = static_cast( + s.domain); } - d->domainOther = static_cast(s.domain); switch (s.kind) { case C2Component::KIND_DECODER: @@ -278,10 +325,16 @@ Status objcpy( case C2Component::KIND_ENCODER: d->kind = IComponentStore::ComponentTraits::Kind::ENCODER; break; - default: + case C2Component::KIND_OTHER: d->kind = IComponentStore::ComponentTraits::Kind::OTHER; + break; + default: + LOG(DEBUG) << "Unrecognized C2Component::kind_t " + << "with underlying value " << underlying_value(s.kind) + << "."; + d->kind = static_cast( + s.kind); } - d->kindOther = static_cast(s.kind); d->rank = static_cast(s.rank); @@ -292,11 +345,11 @@ Status objcpy( --ix; d->aliases[ix] = s.aliases[ix]; } - return Status::OK; + return true; } // ComponentTraits -> C2Component::Traits, std::unique_ptr> -c2_status_t objcpy( +bool objcpy( C2Component::Traits* d, std::unique_ptr>* aliasesBuffer, const IComponentStore::ComponentTraits& s) { @@ -309,8 +362,17 @@ c2_status_t objcpy( case IComponentStore::ComponentTraits::Domain::AUDIO: d->domain = C2Component::DOMAIN_AUDIO; break; + case IComponentStore::ComponentTraits::Domain::IMAGE: + d->domain = C2Component::DOMAIN_IMAGE; + break; + case IComponentStore::ComponentTraits::Domain::OTHER: + d->domain = C2Component::DOMAIN_OTHER; + break; default: - d->domain = static_cast(s.domainOther); + LOG(DEBUG) << "Unrecognized ComponentTraits::Domain " + << "with underlying value " << underlying_value(s.domain) + << "."; + d->domain = static_cast(s.domain); } switch (s.kind) { @@ -320,8 +382,14 @@ c2_status_t objcpy( case IComponentStore::ComponentTraits::Kind::ENCODER: d->kind = C2Component::KIND_ENCODER; break; + case IComponentStore::ComponentTraits::Kind::OTHER: + d->kind = C2Component::KIND_OTHER; + break; default: - d->kind = static_cast(s.kindOther); + LOG(DEBUG) << "Unrecognized ComponentTraits::Kind " + << "with underlying value " << underlying_value(s.kind) + << "."; + d->kind = static_cast(s.kind); } d->rank = static_cast(s.rank); @@ -336,52 +404,55 @@ c2_status_t objcpy( (**aliasesBuffer)[i] = s.aliases[i].c_str(); d->aliases[i] = (**aliasesBuffer)[i].c_str(); } - return C2_OK; + return true; } namespace /* unnamed */ { // C2ParamFieldValues -> ParamFieldValues -Status objcpy(ParamFieldValues *d, const C2ParamFieldValues &s) { - objcpy(&d->paramOrField, s.paramOrField); +bool objcpy(ParamFieldValues *d, const C2ParamFieldValues &s) { + if (!objcpy(&d->paramOrField, s.paramOrField)) { + LOG(ERROR) << "Invalid C2ParamFieldValues::paramOrField."; + return false; + } if (s.values) { d->values.resize(1); - return objcpy(&d->values[0], *s.values); + if (!objcpy(&d->values[0], *s.values)) { + LOG(ERROR) << "Invalid C2ParamFieldValues::values."; + return false; + } + return true; } d->values.resize(0); - return Status::OK; + return true; } // ParamFieldValues -> C2ParamFieldValues -c2_status_t objcpy(C2ParamFieldValues *d, const ParamFieldValues &s) { +bool objcpy(C2ParamFieldValues *d, const ParamFieldValues &s) { d->paramOrField = C2ParamFieldBuilder(s.paramOrField); if (s.values.size() == 1) { d->values = std::make_unique(); - return objcpy(d->values.get(), s.values[0]); + if (!objcpy(d->values.get(), s.values[0])) { + LOG(ERROR) << "Invalid ParamFieldValues::values."; + return false; + } + return true; } else if (s.values.size() == 0) { d->values.reset(); - return C2_OK; + return true; } - ALOGE("Multiple FieldSupportedValues objects. " - "(Only one is allowed.)"); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid ParamFieldValues: " + "Two or more FieldSupportedValues objects exist in " + "ParamFieldValues. " + "Only zero or one is allowed."; + return false; } } // unnamed namespace // C2SettingResult -> SettingResult -Status objcpy(SettingResult *d, const C2SettingResult &s) { - d->failureOther = static_cast(s.failure); +bool objcpy(SettingResult *d, const C2SettingResult &s) { switch (s.failure) { - case C2SettingResult::READ_ONLY: - d->failure = SettingResult::Failure::READ_ONLY; - break; - case C2SettingResult::MISMATCH: - d->failure = SettingResult::Failure::MISMATCH; - break; - case C2SettingResult::BAD_VALUE: - d->failure = SettingResult::Failure::BAD_VALUE; - break; case C2SettingResult::BAD_TYPE: d->failure = SettingResult::Failure::BAD_TYPE; break; @@ -391,53 +462,61 @@ Status objcpy(SettingResult *d, const C2SettingResult &s) { case C2SettingResult::BAD_INDEX: d->failure = SettingResult::Failure::BAD_INDEX; break; + case C2SettingResult::READ_ONLY: + d->failure = SettingResult::Failure::READ_ONLY; + break; + case C2SettingResult::MISMATCH: + d->failure = SettingResult::Failure::MISMATCH; + break; + case C2SettingResult::BAD_VALUE: + d->failure = SettingResult::Failure::BAD_VALUE; + break; case C2SettingResult::CONFLICT: d->failure = SettingResult::Failure::CONFLICT; break; case C2SettingResult::UNSUPPORTED: d->failure = SettingResult::Failure::UNSUPPORTED; break; + case C2SettingResult::INFO_BAD_VALUE: + d->failure = SettingResult::Failure::INFO_BAD_VALUE; + break; case C2SettingResult::INFO_CONFLICT: d->failure = SettingResult::Failure::INFO_CONFLICT; break; default: - d->failure = SettingResult::Failure::OTHER; + LOG(DEBUG) << "Unrecognized C2SettingResult::Failure " + << "with underlying value " << underlying_value(s.failure) + << "."; + d->failure = static_cast(s.failure); } - Status status = objcpy(&d->field, s.field); - if (status != Status::OK) { - return status; + if (!objcpy(&d->field, s.field)) { + LOG(ERROR) << "Invalid C2SettingResult::field."; + return false; } d->conflicts.resize(s.conflicts.size()); size_t i = 0; for (const C2ParamFieldValues& sConflict : s.conflicts) { ParamFieldValues &dConflict = d->conflicts[i++]; - status = objcpy(&dConflict, sConflict); - if (status != Status::OK) { - return status; + if (!objcpy(&dConflict, sConflict)) { + LOG(ERROR) << "Invalid C2SettingResult::conflicts[" + << i - 1 << "]."; + return false; } } - return Status::OK; + return true; } // SettingResult -> std::unique_ptr -c2_status_t objcpy(std::unique_ptr *d, const SettingResult &s) { +bool objcpy(std::unique_ptr *d, const SettingResult &s) { *d = std::unique_ptr(new C2SettingResult { .field = C2ParamFieldValues(C2ParamFieldBuilder()) }); if (!*d) { - return C2_NO_MEMORY; + LOG(ERROR) << "No memory for C2SettingResult."; + return false; } // failure switch (s.failure) { - case SettingResult::Failure::READ_ONLY: - (*d)->failure = C2SettingResult::READ_ONLY; - break; - case SettingResult::Failure::MISMATCH: - (*d)->failure = C2SettingResult::MISMATCH; - break; - case SettingResult::Failure::BAD_VALUE: - (*d)->failure = C2SettingResult::BAD_VALUE; - break; case SettingResult::Failure::BAD_TYPE: (*d)->failure = C2SettingResult::BAD_TYPE; break; @@ -447,23 +526,38 @@ c2_status_t objcpy(std::unique_ptr *d, const SettingResult &s) case SettingResult::Failure::BAD_INDEX: (*d)->failure = C2SettingResult::BAD_INDEX; break; + case SettingResult::Failure::READ_ONLY: + (*d)->failure = C2SettingResult::READ_ONLY; + break; + case SettingResult::Failure::MISMATCH: + (*d)->failure = C2SettingResult::MISMATCH; + break; + case SettingResult::Failure::BAD_VALUE: + (*d)->failure = C2SettingResult::BAD_VALUE; + break; case SettingResult::Failure::CONFLICT: (*d)->failure = C2SettingResult::CONFLICT; break; case SettingResult::Failure::UNSUPPORTED: (*d)->failure = C2SettingResult::UNSUPPORTED; break; + case SettingResult::Failure::INFO_BAD_VALUE: + (*d)->failure = C2SettingResult::INFO_BAD_VALUE; + break; case SettingResult::Failure::INFO_CONFLICT: (*d)->failure = C2SettingResult::INFO_CONFLICT; break; default: - (*d)->failure = static_cast(s.failureOther); + LOG(DEBUG) << "Unrecognized SettingResult::Failure " + << "with underlying value " << underlying_value(s.failure) + << "."; + (*d)->failure = static_cast(s.failure); } // field - c2_status_t status = objcpy(&(*d)->field, s.field); - if (status != C2_OK) { - return status; + if (!objcpy(&(*d)->field, s.field)) { + LOG(ERROR) << "Invalid SettingResult::field."; + return false; } // conflicts @@ -472,26 +566,26 @@ c2_status_t objcpy(std::unique_ptr *d, const SettingResult &s) for (const ParamFieldValues& sConflict : s.conflicts) { (*d)->conflicts.emplace_back( C2ParamFieldValues{ C2ParamFieldBuilder(), nullptr }); - status = objcpy(&(*d)->conflicts.back(), sConflict); - if (status != C2_OK) { - return status; + if (!objcpy(&(*d)->conflicts.back(), sConflict)) { + LOG(ERROR) << "Invalid SettingResult::conflicts."; + return false; } } - return C2_OK; + return true; } // C2ParamDescriptor -> ParamDescriptor -Status objcpy(ParamDescriptor *d, const C2ParamDescriptor &s) { +bool objcpy(ParamDescriptor *d, const C2ParamDescriptor &s) { d->index = static_cast(s.index()); d->attrib = static_cast>( _C2ParamInspector::GetAttrib(s)); d->name = s.name(); copyVector(&d->dependencies, s.dependencies()); - return Status::OK; + return true; } // ParamDescriptor -> C2ParamDescriptor -c2_status_t objcpy(std::shared_ptr *d, const ParamDescriptor &s) { +bool objcpy(std::shared_ptr *d, const ParamDescriptor &s) { std::vector dDependencies; dDependencies.reserve(s.dependencies.size()); for (const ParamIndex& sDependency : s.dependencies) { @@ -502,11 +596,11 @@ c2_status_t objcpy(std::shared_ptr *d, const ParamDescriptor static_cast(s.attrib), C2String(s.name.c_str()), std::move(dDependencies)); - return C2_OK; + return true; } // C2StructDescriptor -> StructDescriptor -Status objcpy(StructDescriptor *d, const C2StructDescriptor &s) { +bool objcpy(StructDescriptor *d, const C2StructDescriptor &s) { d->type = static_cast(s.coreIndex().coreIndex()); d->fields.resize(s.numFields()); size_t i = 0; @@ -518,7 +612,7 @@ Status objcpy(StructDescriptor *d, const C2StructDescriptor &s) { _C2ParamInspector::GetSize(sField)); dField.type = static_cast>( sField.type()); - dField.length = static_cast(sField.extent()); + dField.extent = static_cast(sField.extent()); dField.name = static_cast(sField.name()); const auto& sNamedValues = sField.namedValues(); dField.namedValues.resize(sNamedValues.size()); @@ -530,18 +624,18 @@ Status objcpy(StructDescriptor *d, const C2StructDescriptor &s) { sNamedValue.second.u64); } } - return Status::OK; + return true; } // StructDescriptor -> C2StructDescriptor -c2_status_t objcpy(std::unique_ptr *d, const StructDescriptor &s) { +bool objcpy(std::unique_ptr *d, const StructDescriptor &s) { C2Param::CoreIndex dIndex = C2Param::CoreIndex(static_cast(s.type)); std::vector dFields; dFields.reserve(s.fields.size()); for (const auto &sField : s.fields) { C2FieldDescriptor dField = { static_cast(sField.type), - sField.length, + sField.extent, sField.name, sField.fieldId.offset, sField.fieldId.size }; @@ -557,7 +651,7 @@ c2_status_t objcpy(std::unique_ptr *d, const StructDescripto } *d = std::make_unique( _C2ParamInspector::CreateStructDescriptor(dIndex, std::move(dFields))); - return C2_OK; + return true; } namespace /* unnamed */ { @@ -565,14 +659,14 @@ namespace /* unnamed */ { // Find or add a hidl BaseBlock object from a given C2Handle* to a list and an // associated map. // Note: The handle is not cloned. -Status _addBaseBlock( +bool _addBaseBlock( uint32_t* index, const C2Handle* handle, std::list* baseBlocks, std::map* baseBlockIndices) { if (!handle) { - ALOGE("addBaseBlock called on a null C2Handle."); - return Status::BAD_VALUE; + LOG(ERROR) << "addBaseBlock called on a null C2Handle."; + return false; } auto it = baseBlockIndices->find(handle); if (it != baseBlockIndices->end()) { @@ -583,26 +677,25 @@ Status _addBaseBlock( baseBlocks->emplace_back(); BaseBlock &dBaseBlock = baseBlocks->back(); - dBaseBlock.type = BaseBlock::Type::NATIVE; // This does not clone the handle. - dBaseBlock.nativeBlock = - reinterpret_cast(handle); + dBaseBlock.nativeBlock( + reinterpret_cast(handle)); } - return Status::OK; + return true; } // Find or add a hidl BaseBlock object from a given BufferPoolData to a list and // an associated map. -Status _addBaseBlock( +bool _addBaseBlock( uint32_t* index, const std::shared_ptr bpData, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { if (!bpData) { - ALOGE("addBaseBlock called on a null BufferPoolData."); - return Status::BAD_VALUE; + LOG(ERROR) << "addBaseBlock called on a null BufferPoolData."; + return false; } auto it = baseBlockIndices->find(bpData.get()); if (it != baseBlockIndices->end()) { @@ -613,24 +706,26 @@ Status _addBaseBlock( baseBlocks->emplace_back(); BaseBlock &dBaseBlock = baseBlocks->back(); - dBaseBlock.type = BaseBlock::Type::POOLED; if (bufferPoolSender) { + BufferStatusMessage pooledBlock; ResultStatus bpStatus = bufferPoolSender->send( bpData, - &dBaseBlock.pooledBlock); + &pooledBlock); if (bpStatus != ResultStatus::OK) { - ALOGE("Failed to send buffer with BufferPool. Error: %d.", - static_cast(bpStatus)); - return Status::BAD_VALUE; + LOG(ERROR) << "Failed to send buffer with BufferPool. Error: " + << static_cast(bpStatus) + << "."; + return false; } + dBaseBlock.pooledBlock(pooledBlock); } } - return Status::OK; + return true; } -Status addBaseBlock( +bool addBaseBlock( uint32_t* index, const C2Handle* handle, const std::shared_ptr& blockPoolData, @@ -649,8 +744,8 @@ Status addBaseBlock( std::shared_ptr bpData; if (!_C2BlockFactory::GetBufferPoolData(blockPoolData, &bpData) || !bpData) { - ALOGE("BufferPoolData unavailable in a block."); - return Status::BAD_VALUE; + LOG(ERROR) << "BufferPoolData unavailable in a block."; + return false; } return _addBaseBlock( index, bpData, @@ -662,69 +757,76 @@ Status addBaseBlock( index, handle, baseBlocks, baseBlockIndices); default: - ALOGE("Unknown C2BlockPoolData type."); - return Status::BAD_VALUE; + LOG(ERROR) << "Unknown C2BlockPoolData type."; + return false; } } // C2Fence -> hidl_handle // Note: File descriptors are not duplicated. The original file descriptor must // not be closed before the transaction is complete. -Status objcpy(hidl_handle* d, const C2Fence& s) { +bool objcpy(hidl_handle* d, const C2Fence& s) { (void)s; // TODO: implement s.fd() int fenceFd = -1; d->setTo(nullptr); if (fenceFd >= 0) { native_handle_t *handle = native_handle_create(1, 0); if (!handle) { - return Status::NO_MEMORY; + LOG(ERROR) << "Failed to create a native handle."; + return false; } handle->data[0] = fenceFd; d->setTo(handle, true /* owns */); } - return Status::OK; + return true; } // C2ConstLinearBlock -> Block // Note: Native handles are not duplicated. The original handles must not be // closed before the transaction is complete. -Status objcpy(Block* d, const C2ConstLinearBlock& s, +bool objcpy(Block* d, const C2ConstLinearBlock& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { std::shared_ptr bpData = _C2BlockFactory::GetLinearBlockPoolData(s); - Status status = addBaseBlock(&d->index, s.handle(), bpData, - bufferPoolSender, baseBlocks, baseBlockIndices); - if (status != Status::OK) { - return status; + if (!addBaseBlock(&d->index, s.handle(), bpData, + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid block data in C2ConstLinearBlock."; + return false; } // Create the metadata. C2Hidl_RangeInfo dRangeInfo; dRangeInfo.offset = static_cast(s.offset()); dRangeInfo.length = static_cast(s.size()); - status = createParamsBlob(&d->meta, - std::vector{ &dRangeInfo }); - if (status != Status::OK) { - return Status::BAD_VALUE; + if (!createParamsBlob(&d->meta, std::vector{ &dRangeInfo })) { + LOG(ERROR) << "Invalid range info in C2ConstLinearBlock."; + return false; } // Copy the fence - return objcpy(&d->fence, s.fence()); + if (!objcpy(&d->fence, s.fence())) { + LOG(ERROR) << "Invalid C2ConstLinearBlock::fence."; + return false; + } + return true; } // C2ConstGraphicBlock -> Block // Note: Native handles are not duplicated. The original handles must not be // closed before the transaction is complete. -Status objcpy(Block* d, const C2ConstGraphicBlock& s, +bool objcpy(Block* d, const C2ConstGraphicBlock& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { std::shared_ptr bpData = _C2BlockFactory::GetGraphicBlockPoolData(s); - Status status = addBaseBlock(&d->index, s.handle(), bpData, - bufferPoolSender, baseBlocks, baseBlockIndices); + if (!addBaseBlock(&d->index, s.handle(), bpData, + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid block data in C2ConstGraphicBlock."; + return false; + } // Create the metadata. C2Hidl_RectInfo dRectInfo; @@ -733,62 +835,70 @@ Status objcpy(Block* d, const C2ConstGraphicBlock& s, dRectInfo.top = static_cast(sRect.top); dRectInfo.width = static_cast(sRect.width); dRectInfo.height = static_cast(sRect.height); - status = createParamsBlob(&d->meta, - std::vector{ &dRectInfo }); - if (status != Status::OK) { - return Status::BAD_VALUE; + if (!createParamsBlob(&d->meta, std::vector{ &dRectInfo })) { + LOG(ERROR) << "Invalid rect info in C2ConstGraphicBlock."; + return false; } // Copy the fence - return objcpy(&d->fence, s.fence()); + if (!objcpy(&d->fence, s.fence())) { + LOG(ERROR) << "Invalid C2ConstGraphicBlock::fence."; + return false; + } + return true; } // C2BufferData -> Buffer // This function only fills in d->blocks. -Status objcpy(Buffer* d, const C2BufferData& s, +bool objcpy(Buffer* d, const C2BufferData& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { - Status status; d->blocks.resize( s.linearBlocks().size() + s.graphicBlocks().size()); size_t i = 0; for (const C2ConstLinearBlock& linearBlock : s.linearBlocks()) { Block& dBlock = d->blocks[i++]; - status = objcpy( + if (!objcpy( &dBlock, linearBlock, - bufferPoolSender, baseBlocks, baseBlockIndices); - if (status != Status::OK) { - return status; + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid C2BufferData::linearBlocks. " + << "(Destination index = " << i - 1 << ".)"; + return false; } } for (const C2ConstGraphicBlock& graphicBlock : s.graphicBlocks()) { Block& dBlock = d->blocks[i++]; - status = objcpy( + if (!objcpy( &dBlock, graphicBlock, - bufferPoolSender, baseBlocks, baseBlockIndices); - if (status != Status::OK) { - return status; + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid C2BufferData::graphicBlocks. " + << "(Destination index = " << i - 1 << ".)"; + return false; } } - return Status::OK; + return true; } // C2Buffer -> Buffer -Status objcpy(Buffer* d, const C2Buffer& s, +bool objcpy(Buffer* d, const C2Buffer& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { - Status status = createParamsBlob(&d->info, s.info()); - if (status != Status::OK) { - return status; + if (!createParamsBlob(&d->info, s.info())) { + LOG(ERROR) << "Invalid C2Buffer::info."; + return false; + } + if (!objcpy(d, s.data(), bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid C2Buffer::data."; + return false; } - return objcpy(d, s.data(), bufferPoolSender, baseBlocks, baseBlockIndices); + return true; } // C2InfoBuffer -> InfoBuffer -Status objcpy(InfoBuffer* d, const C2InfoBuffer& s, +bool objcpy(InfoBuffer* d, const C2InfoBuffer& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { @@ -798,24 +908,21 @@ Status objcpy(InfoBuffer* d, const C2InfoBuffer& s, (void)bufferPoolSender; (void)baseBlocks; (void)baseBlockIndices; - return Status::OK; - /* - // Stub implementation that may work in the future. - d->index = static_cast(s.index()); - d->buffer.info.resize(0); - return objcpy(&d->buffer, s.data(), baseBlocks, baseBlockIndices); - */ + LOG(INFO) << "InfoBuffer not implemented."; + return true; } // C2FrameData -> FrameData -Status objcpy(FrameData* d, const C2FrameData& s, +bool objcpy(FrameData* d, const C2FrameData& s, BufferPoolSender* bufferPoolSender, std::list* baseBlocks, std::map* baseBlockIndices) { d->flags = static_cast>(s.flags); - objcpy(&d->ordinal, s.ordinal); + if (!objcpy(&d->ordinal, s.ordinal)) { + LOG(ERROR) << "Invalid C2FrameData::ordinal."; + return false; + } - Status status; d->buffers.resize(s.buffers.size()); size_t i = 0; for (const std::shared_ptr& sBuffer : s.buffers) { @@ -827,17 +934,18 @@ Status objcpy(FrameData* d, const C2FrameData& s, dBuffer.blocks.resize(0); continue; } - status = objcpy( + if (!objcpy( &dBuffer, *sBuffer, - bufferPoolSender, baseBlocks, baseBlockIndices); - if (status != Status::OK) { - return status; + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid C2FrameData::buffers[" + << i - 1 << "]."; + return false; } } - status = createParamsBlob(&d->configUpdate, s.configUpdate); - if (status != Status::OK) { - return status; + if (!createParamsBlob(&d->configUpdate, s.configUpdate)) { + LOG(ERROR) << "Invalid C2FrameData::configUpdate."; + return false; } d->infoBuffers.resize(s.infoBuffers.size()); @@ -845,17 +953,19 @@ Status objcpy(FrameData* d, const C2FrameData& s, for (const std::shared_ptr& sInfoBuffer : s.infoBuffers) { InfoBuffer& dInfoBuffer = d->infoBuffers[i++]; if (!sInfoBuffer) { - ALOGE("Null C2InfoBuffer"); - return Status::BAD_VALUE; + LOG(ERROR) << "Null C2FrameData::infoBuffers[" + << i - 1 << "]."; + return false; } - status = objcpy(&dInfoBuffer, *sInfoBuffer, - bufferPoolSender, baseBlocks, baseBlockIndices); - if (status != Status::OK) { - return status; + if (!objcpy(&dInfoBuffer, *sInfoBuffer, + bufferPoolSender, baseBlocks, baseBlockIndices)) { + LOG(ERROR) << "Invalid C2FrameData::infoBuffers[" + << i - 1 << "]."; + return false; } } - return status; + return true; } } // unnamed namespace @@ -885,7 +995,7 @@ ResultStatus DefaultBufferPoolSender::send( const std::shared_ptr& bpData, BufferStatusMessage* bpMessage) { if (!mReceiverManager) { - ALOGE("No access to receiver's BufferPool."); + LOG(ERROR) << "No access to receiver's BufferPool."; return ResultStatus::NOT_FOUND; } ResultStatus rs; @@ -893,7 +1003,7 @@ ResultStatus DefaultBufferPoolSender::send( if (!mSenderManager) { mSenderManager = ClientManager::getInstance(); if (!mSenderManager) { - ALOGE("Failed to retrieve local BufferPool ClientManager."); + LOG(ERROR) << "Failed to retrieve local BufferPool ClientManager."; return ResultStatus::CRITICAL_ERROR; } } @@ -915,11 +1025,11 @@ ResultStatus DefaultBufferPoolSender::send( connectionId, &receiverConnectionId); if ((rs != ResultStatus::OK) && (rs != ResultStatus::ALREADY_EXISTS)) { - ALOGW("registerSender -- returned error: %d.", - static_cast(rs)); + LOG(WARNING) << "registerSender -- returned error: " + << static_cast(rs) + << "."; return rs; } else { - ALOGV("registerSender -- succeeded."); mReceiverConnectionId = receiverConnectionId; } } @@ -929,12 +1039,13 @@ ResultStatus DefaultBufferPoolSender::send( rs = mSenderManager->postSend( mReceiverConnectionId, bpData, &transactionId, ×tampUs); if (rs != ResultStatus::OK) { - ALOGE("ClientManager::postSend -- returned error: %d.", - static_cast(rs)); + LOG(ERROR) << "ClientManager::postSend -- returned error: " + << static_cast(rs) + << "."; return rs; } if (!bpMessage) { - ALOGE("Null output parameter for BufferStatusMessage."); + LOG(ERROR) << "Null output parameter for BufferStatusMessage."; return ResultStatus::CRITICAL_ERROR; } bpMessage->connectionId = mReceiverConnectionId; @@ -946,12 +1057,10 @@ ResultStatus DefaultBufferPoolSender::send( } // std::list> -> WorkBundle -Status objcpy( +bool objcpy( WorkBundle* d, const std::list>& s, BufferPoolSender* bufferPoolSender) { - Status status = Status::OK; - // baseBlocks holds a list of BaseBlock objects that Blocks can refer to. std::list baseBlocks; @@ -971,63 +1080,80 @@ Status objcpy( for (const std::unique_ptr& sWork : s) { Work &dWork = d->works[i++]; if (!sWork) { - ALOGW("Null C2Work encountered."); + LOG(WARNING) << "Null C2Work encountered."; continue; } - status = objcpy(&dWork.input, sWork->input, - bufferPoolSender, &baseBlocks, &baseBlockIndices); - if (status != Status::OK) { - return status; + + // chain info is not in use currently. + + // input + if (!objcpy(&dWork.input, sWork->input, + bufferPoolSender, &baseBlocks, &baseBlockIndices)) { + LOG(ERROR) << "Invalid C2Work::input."; + return false; } + + // worklets if (sWork->worklets.size() == 0) { - ALOGW("Work with no worklets."); + LOG(DEBUG) << "Work with no worklets."; } else { - if (sWork->worklets.size() > 1) { - ALOGW("Work with multiple worklets. " - "Only the first worklet will be marshalled."); - } - if (!sWork->worklets.front()) { - ALOGE("Null worklet encountered."); - return Status::BAD_VALUE; - } - - // Parcel the first worklet. - const C2Worklet &sWorklet = *sWork->worklets.front(); - Worklet &dWorklet = dWork.worklet; - - dWorklet.tunings.resize(sWorklet.tunings.size()); + // Parcel the worklets. + hidl_vec &dWorklets = dWork.worklets; + dWorklets.resize(sWork->worklets.size()); size_t j = 0; - for (const std::unique_ptr& sTuning : sWorklet.tunings) { - status = createParamsBlob( - &dWorklet.tunings[j++], - std::vector - { reinterpret_cast(sTuning.get()) }); - if (status != Status::OK) { - return status; + for (const std::unique_ptr& sWorklet : sWork->worklets) + { + if (!sWorklet) { + LOG(WARNING) << "Null C2Work::worklets[" + << j << "]."; + continue; } - } + Worklet &dWorklet = dWorklets[j++]; + + // component id + dWorklet.componentId = static_cast( + sWorklet->component); - dWorklet.failures.resize(sWorklet.failures.size()); - j = 0; - for (const std::unique_ptr& sFailure : - sWorklet.failures) { - if (!sFailure) { - ALOGE("Null C2SettingResult"); - return Status::BAD_VALUE; + // tunings + if (!createParamsBlob(&dWorklet.tunings, sWorklet->tunings)) { + LOG(ERROR) << "Invalid C2Work::worklets[" + << j - 1 << "]->tunings."; + return false; } - status = objcpy(&dWorklet.failures[j++], *sFailure); - if (status != Status::OK) { - return status; + + // failures + dWorklet.failures.resize(sWorklet->failures.size()); + size_t k = 0; + for (const std::unique_ptr& sFailure : + sWorklet->failures) { + if (!sFailure) { + LOG(WARNING) << "Null C2Work::worklets[" + << j - 1 << "]->failures[" + << k << "]."; + continue; + } + if (!objcpy(&dWorklet.failures[k++], *sFailure)) { + LOG(ERROR) << "Invalid C2Work::worklets[" + << j - 1 << "]->failures[" + << k - 1 << "]."; + return false; + } } - } - status = objcpy(&dWorklet.output, sWorklet.output, - bufferPoolSender, &baseBlocks, &baseBlockIndices); - if (status != Status::OK) { - return status; + // output + if (!objcpy(&dWorklet.output, sWorklet->output, + bufferPoolSender, &baseBlocks, &baseBlockIndices)) { + LOG(ERROR) << "Invalid C2Work::worklets[" + << j - 1 << "]->output."; + return false; + } } } - dWork.workletProcessed = sWork->workletsProcessed > 0; + + // worklets processed + dWork.workletsProcessed = sWork->workletsProcessed; + + // result dWork.result = static_cast(sWork->result); } @@ -1040,7 +1166,7 @@ Status objcpy( } } - return Status::OK; + return true; } namespace /* unnamed */ { @@ -1058,15 +1184,15 @@ struct C2BaseBlock { // hidl_handle -> C2Fence // Note: File descriptors are not duplicated. The original file descriptor must // not be closed before the transaction is complete. -c2_status_t objcpy(C2Fence* d, const hidl_handle& s) { +bool objcpy(C2Fence* d, const hidl_handle& s) { // TODO: Implement. (void)s; *d = C2Fence(); - return C2_OK; + return true; } // C2LinearBlock, vector, C2Fence -> C2Buffer -c2_status_t createLinearBuffer( +bool createLinearBuffer( std::shared_ptr* buffer, const std::shared_ptr& block, const std::vector& meta, @@ -1074,12 +1200,12 @@ c2_status_t createLinearBuffer( // Check the block meta. It should have exactly 1 C2Info: // C2Hidl_RangeInfo. if ((meta.size() != 1) || !meta[0]) { - ALOGE("Invalid block metadata for ion block."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid C2LinearBlock::meta."; + return false; } if (meta[0]->size() != sizeof(C2Hidl_RangeInfo)) { - ALOGE("Invalid block metadata for ion block: range."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid range info in C2LinearBlock."; + return false; } C2Hidl_RangeInfo *rangeInfo = reinterpret_cast(meta[0]); @@ -1089,14 +1215,14 @@ c2_status_t createLinearBuffer( rangeInfo->offset, rangeInfo->length, fence)); if (!(*buffer)) { - ALOGE("Cannot create a linear buffer."); - return C2_BAD_VALUE; + LOG(ERROR) << "CreateLinearBuffer failed."; + return false; } - return C2_OK; + return true; } // C2GraphicBlock, vector, C2Fence -> C2Buffer -c2_status_t createGraphicBuffer( +bool createGraphicBuffer( std::shared_ptr* buffer, const std::shared_ptr& block, const std::vector& meta, @@ -1104,12 +1230,12 @@ c2_status_t createGraphicBuffer( // Check the block meta. It should have exactly 1 C2Info: // C2Hidl_RectInfo. if ((meta.size() != 1) || !meta[0]) { - ALOGE("Invalid block metadata for graphic block."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid C2GraphicBlock::meta."; + return false; } if (meta[0]->size() != sizeof(C2Hidl_RectInfo)) { - ALOGE("Invalid block metadata for graphic block: crop rect."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid rect info in C2GraphicBlock."; + return false; } C2Hidl_RectInfo *rectInfo = reinterpret_cast(meta[0]); @@ -1120,136 +1246,144 @@ c2_status_t createGraphicBuffer( at(rectInfo->left, rectInfo->top), fence)); if (!(*buffer)) { - ALOGE("Cannot create a graphic buffer."); - return C2_BAD_VALUE; + LOG(ERROR) << "CreateGraphicBuffer failed."; + return false; } - return C2_OK; + return true; } // Buffer -> C2Buffer // Note: The native handles will be cloned. -c2_status_t objcpy(std::shared_ptr* d, const Buffer& s, +bool objcpy(std::shared_ptr* d, const Buffer& s, const std::vector& baseBlocks) { - c2_status_t status; *d = nullptr; // Currently, a non-null C2Buffer must contain exactly 1 block. if (s.blocks.size() == 0) { - return C2_OK; + return true; } else if (s.blocks.size() != 1) { - ALOGE("Currently, a C2Buffer must contain exactly 1 block."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid Buffer: " + "Currently, a C2Buffer must contain exactly 1 block."; + return false; } const Block &sBlock = s.blocks[0]; if (sBlock.index >= baseBlocks.size()) { - ALOGE("Index into baseBlocks is out of range."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid Buffer::blocks[0].index: " + "Array index out of range."; + return false; } const C2BaseBlock &baseBlock = baseBlocks[sBlock.index]; // Parse meta. std::vector sBlockMeta; - status = parseParamsBlob(&sBlockMeta, sBlock.meta); - if (status != C2_OK) { - ALOGE("Invalid block params blob."); - return C2_BAD_VALUE; + if (!parseParamsBlob(&sBlockMeta, sBlock.meta)) { + LOG(ERROR) << "Invalid Buffer::blocks[0].meta."; + return false; } // Copy fence. C2Fence dFence; - status = objcpy(&dFence, sBlock.fence); + if (!objcpy(&dFence, sBlock.fence)) { + LOG(ERROR) << "Invalid Buffer::blocks[0].fence."; + return false; + } // Construct a block. switch (baseBlock.type) { case C2BaseBlock::LINEAR: - status = createLinearBuffer(d, baseBlock.linear, sBlockMeta, dFence); + if (!createLinearBuffer(d, baseBlock.linear, sBlockMeta, dFence)) { + LOG(ERROR) << "Invalid C2BaseBlock::linear."; + return false; + } break; case C2BaseBlock::GRAPHIC: - status = createGraphicBuffer(d, baseBlock.graphic, sBlockMeta, dFence); + if (!createGraphicBuffer(d, baseBlock.graphic, sBlockMeta, dFence)) { + LOG(ERROR) << "Invalid C2BaseBlock::graphic."; + return false; + } break; default: - ALOGE("Invalid BaseBlock type."); - return C2_BAD_VALUE; - } - if (status != C2_OK) { - return status; + LOG(ERROR) << "Invalid C2BaseBlock::type."; + return false; } // Parse info std::vector params; - status = parseParamsBlob(¶ms, s.info); - if (status != C2_OK) { - ALOGE("Invalid buffer params blob."); - return status; + if (!parseParamsBlob(¶ms, s.info)) { + LOG(ERROR) << "Invalid Buffer::info."; + return false; } for (C2Param* param : params) { if (param == nullptr) { - ALOGE("Null buffer param encountered."); - return C2_BAD_VALUE; + LOG(ERROR) << "Null param in Buffer::info."; + return false; } - std::shared_ptr c2param( - C2Param::Copy(*param).release()); + std::shared_ptr c2param{ + C2Param::Copy(*param).release()}; if (!c2param) { - ALOGE("Invalid buffer param inside a blob."); - return C2_BAD_VALUE; + LOG(ERROR) << "Invalid param in Buffer::info."; + return false; } - status = (*d)->setInfo(std::static_pointer_cast(c2param)); + c2_status_t status = + (*d)->setInfo(std::static_pointer_cast(c2param)); if (status != C2_OK) { - ALOGE("C2Buffer::setInfo failed()."); - return C2_BAD_VALUE; + LOG(ERROR) << "C2Buffer::setInfo failed."; + return false; } } - return C2_OK; + return true; } // FrameData -> C2FrameData -c2_status_t objcpy(C2FrameData* d, const FrameData& s, +bool objcpy(C2FrameData* d, const FrameData& s, const std::vector& baseBlocks) { - c2_status_t status; d->flags = static_cast(s.flags); - objcpy(&d->ordinal, s.ordinal); + if (!objcpy(&d->ordinal, s.ordinal)) { + LOG(ERROR) << "Invalid FrameData::ordinal."; + return false; + } d->buffers.clear(); d->buffers.reserve(s.buffers.size()); for (const Buffer& sBuffer : s.buffers) { std::shared_ptr dBuffer; - status = objcpy(&dBuffer, sBuffer, baseBlocks); - if (status != C2_OK) { - return status; + if (!objcpy(&dBuffer, sBuffer, baseBlocks)) { + LOG(ERROR) << "Invalid FrameData::buffers."; + return false; } d->buffers.emplace_back(dBuffer); } std::vector params; - status = parseParamsBlob(¶ms, s.configUpdate); - if (status != C2_OK) { - ALOGE("Failed to parse frame data params."); - return status; + if (!parseParamsBlob(¶ms, s.configUpdate)) { + LOG(ERROR) << "Invalid FrameData::configUpdate."; + return false; } d->configUpdate.clear(); for (C2Param* param : params) { d->configUpdate.emplace_back(C2Param::Copy(*param)); if (!d->configUpdate.back()) { - ALOGE("Unexpected error while parsing frame data params."); - return C2_BAD_VALUE; + LOG(ERROR) << "Unexpected error while parsing " + "FrameData::configUpdate."; + return false; } } // TODO: Implement this once C2InfoBuffer has constructors. d->infoBuffers.clear(); - return C2_OK; + return true; } // BaseBlock -> C2BaseBlock -c2_status_t objcpy(C2BaseBlock* d, const BaseBlock& s) { - switch (s.type) { - case BaseBlock::Type::NATIVE: { +bool objcpy(C2BaseBlock* d, const BaseBlock& s) { + switch (s.getDiscriminator()) { + case BaseBlock::hidl_discriminator::nativeBlock: { native_handle_t* sHandle = - native_handle_clone(s.nativeBlock); + native_handle_clone(s.nativeBlock()); if (sHandle == nullptr) { - ALOGE("Null native handle in a block."); - return C2_BAD_VALUE; + LOG(ERROR) << "Null BaseBlock::nativeBlock."; + return false; } const C2Handle *sC2Handle = reinterpret_cast(sHandle); @@ -1257,25 +1391,25 @@ c2_status_t objcpy(C2BaseBlock* d, const BaseBlock& s) { d->linear = _C2BlockFactory::CreateLinearBlock(sC2Handle); if (d->linear) { d->type = C2BaseBlock::LINEAR; - return C2_OK; + return true; } d->graphic = _C2BlockFactory::CreateGraphicBlock(sC2Handle); if (d->graphic) { d->type = C2BaseBlock::GRAPHIC; - return C2_OK; + return true; } - ALOGE("Unknown handle type in native BaseBlock."); + LOG(ERROR) << "Unknown handle type in BaseBlock::nativeBlock."; if (sHandle) { native_handle_close(sHandle); native_handle_delete(sHandle); } - return C2_BAD_VALUE; + return false; } - case BaseBlock::Type::POOLED: { + case BaseBlock::hidl_discriminator::pooledBlock: { const BufferStatusMessage &bpMessage = - s.pooledBlock; + s.pooledBlock(); sp bp = ClientManager::getInstance(); std::shared_ptr bpData; native_handle_t *cHandle; @@ -1287,48 +1421,49 @@ c2_status_t objcpy(C2BaseBlock* d, const BaseBlock& s) { &cHandle, &bpData); if (bpStatus != ResultStatus::OK) { - ALOGE("Failed to receive buffer from bufferpool -- " - "resultStatus = %d", - static_cast(bpStatus)); - return toC2Status(bpStatus); + LOG(ERROR) << "Failed to receive buffer from bufferpool -- " + << "resultStatus = " << underlying_value(bpStatus) + << "."; + return false; } else if (!bpData) { - ALOGE("No data in bufferpool transaction."); - return C2_BAD_VALUE; + LOG(ERROR) << "No data in bufferpool transaction."; + return false; } d->linear = _C2BlockFactory::CreateLinearBlock(cHandle, bpData); if (d->linear) { d->type = C2BaseBlock::LINEAR; - return C2_OK; + return true; } d->graphic = _C2BlockFactory::CreateGraphicBlock(cHandle, bpData); if (d->graphic) { d->type = C2BaseBlock::GRAPHIC; - return C2_OK; + return true; } - ALOGE("Unknown handle type in pooled BaseBlock."); - return C2_BAD_VALUE; + LOG(ERROR) << "Unknown handle type in BaseBlock::pooledBlock."; + return false; } default: - ALOGE("Corrupted BaseBlock type: %d", static_cast(s.type)); - return C2_BAD_VALUE; + LOG(ERROR) << "Unrecognized BaseBlock's discriminator with " + << "underlying value " + << underlying_value(s.getDiscriminator()) << "."; + return false; } } } // unnamed namespace // WorkBundle -> std::list> -c2_status_t objcpy(std::list>* d, const WorkBundle& s) { - c2_status_t status; - +bool objcpy(std::list>* d, const WorkBundle& s) { // Convert BaseBlocks to C2BaseBlocks. std::vector dBaseBlocks(s.baseBlocks.size()); for (size_t i = 0; i < s.baseBlocks.size(); ++i) { - status = objcpy(&dBaseBlocks[i], s.baseBlocks[i]); - if (status != C2_OK) { - return status; + if (!objcpy(&dBaseBlocks[i], s.baseBlocks[i])) { + LOG(ERROR) << "Invalid WorkBundle::baseBlocks[" + << i << "]."; + return false; } } @@ -1337,74 +1472,58 @@ c2_status_t objcpy(std::list>* d, const WorkBundle& s) { d->emplace_back(std::make_unique()); C2Work& dWork = *d->back(); + // chain info is not in use currently. + // input - status = objcpy(&dWork.input, sWork.input, dBaseBlocks); - if (status != C2_OK) { - ALOGE("Error constructing C2Work's input."); - return C2_BAD_VALUE; + if (!objcpy(&dWork.input, sWork.input, dBaseBlocks)) { + LOG(ERROR) << "Invalid Work::input."; + return false; } // worklet(s) dWork.worklets.clear(); - // TODO: Currently, tunneling is not supported. - if (sWork.workletProcessed) { - dWork.workletsProcessed = 1; - - const Worklet &sWorklet = sWork.worklet; + for (const Worklet& sWorklet : sWork.worklets) { std::unique_ptr dWorklet = std::make_unique(); + // component id + dWorklet->component = static_cast( + sWorklet.componentId); + // tunings - dWorklet->tunings.clear(); - dWorklet->tunings.reserve(sWorklet.tunings.size()); - for (const Params& sTuning : sWorklet.tunings) { - std::vector dParams; - status = parseParamsBlob(&dParams, sTuning); - if (status != C2_OK) { - ALOGE("Failed to parse C2Tuning in C2Worklet."); - return C2_BAD_VALUE; - } - for (C2Param* param : dParams) { - std::unique_ptr dParam = C2Param::Copy(*param); - if (!dParam) { - ALOGE("Null C2Tuning encountered while " - "parsing C2Worklet."); - return C2_BAD_VALUE; - } - dWorklet->tunings.emplace_back( - std::unique_ptr( - reinterpret_cast( - dParam.release()))); - } + if (!copyParamsFromBlob(&dWorklet->tunings, sWorklet.tunings)) { + LOG(ERROR) << "Invalid Worklet::tunings"; + return false; } + // failures dWorklet->failures.clear(); dWorklet->failures.reserve(sWorklet.failures.size()); for (const SettingResult& sFailure : sWorklet.failures) { std::unique_ptr dFailure; - status = objcpy(&dFailure, sFailure); - if (status != C2_OK) { - ALOGE("Failed to create C2SettingResult in C2Worklet."); - return C2_BAD_VALUE; + if (!objcpy(&dFailure, sFailure)) { + LOG(ERROR) << "Invalid Worklet::failures."; + return false; } dWorklet->failures.emplace_back(std::move(dFailure)); } + // output - status = objcpy(&dWorklet->output, sWorklet.output, dBaseBlocks); - if (status != C2_OK) { - ALOGE("Failed to create output C2FrameData."); - return C2_BAD_VALUE; + if (!objcpy(&dWorklet->output, sWorklet.output, dBaseBlocks)) { + LOG(ERROR) << "Invalid Worklet::output."; + return false; } + dWork.worklets.emplace_back(std::move(dWorklet)); - } else { - dWork.worklets.emplace_back(std::make_unique()); - dWork.workletsProcessed = 0; } + // workletsProcessed + dWork.workletsProcessed = sWork.workletsProcessed; + // result dWork.result = static_cast(sWork.result); } - return C2_OK; + return true; } constexpr size_t PARAMS_ALIGNMENT = 8; // 64-bit alignment @@ -1413,7 +1532,7 @@ static_assert(PARAMS_ALIGNMENT % alignof(C2Info) == 0, "C2Param alignment mismat static_assert(PARAMS_ALIGNMENT % alignof(C2Tuning) == 0, "C2Param alignment mismatch"); // Params -> std::vector -c2_status_t parseParamsBlob(std::vector *params, const hidl_vec &blob) { +bool parseParamsBlob(std::vector *params, const hidl_vec &blob) { // assuming blob is const here size_t size = blob.size(); size_t ix = 0; @@ -1429,21 +1548,27 @@ c2_status_t parseParamsBlob(std::vector *params, const hidl_vec -Status _createParamsBlob(hidl_vec *blob, const T ¶ms) { +template +bool _createParamsBlob(hidl_vec *blob, const T ¶ms) { // assuming the parameter values are const size_t size = 0; for (const auto &p : params) { @@ -1469,77 +1594,106 @@ Status _createParamsBlob(hidl_vec *blob, const T ¶ms) { ix = align(ix, PARAMS_ALIGNMENT); } blob->resize(ix); - return ix == size ? Status::OK : Status::CORRUPTED; + if (ix != size) { + LOG(ERROR) << "createParamsBlob -- inconsistent sizes."; + return false; + } + return true; +} + +/** + * Parses a params blob and create a vector of new T objects that contain copies + * of the params in the blob. T is C2Param or its compatible derived class. + * + * \param[out] params the resulting vector + * \param[in] blob parameter blob to parse + * \retval C2_OK if the full blob was parsed and params was constructed + * \retval C2_BAD_VALUE otherwise + */ +template +bool _copyParamsFromBlob( + std::vector>* params, + Params blob) { + + std::vector paramPointers; + if (!parseParamsBlob(¶mPointers, blob)) { + LOG(ERROR) << "copyParamsFromBlob -- failed to parse."; + return false; + } + + params->resize(paramPointers.size()); + size_t i = 0; + for (C2Param* const& paramPointer : paramPointers) { + if (!paramPointer) { + LOG(ERROR) << "copyParamsFromBlob -- null paramPointer."; + return false; + } + (*params)[i++].reset(reinterpret_cast( + C2Param::Copy(*paramPointer).release())); + } + return true; } } // unnamed namespace // std::vector -> Params -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector ¶ms) { return _createParamsBlob(blob, params); } // std::vector -> Params -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector ¶ms) { return _createParamsBlob(blob, params); } // std::vector> -> Params -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms) { return _createParamsBlob(blob, params); } // std::vector> -> Params -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms) { return _createParamsBlob(blob, params); } // std::vector> -> Params -Status createParamsBlob( +bool createParamsBlob( hidl_vec *blob, const std::vector> ¶ms) { return _createParamsBlob(blob, params); } // Params -> std::vector> -c2_status_t copyParamsFromBlob( +bool copyParamsFromBlob( std::vector>* params, Params blob) { - std::vector paramPointers; - c2_status_t status = parseParamsBlob(¶mPointers, blob); - if (status != C2_OK) { - ALOGE("copyParamsFromBlob -- blob parsing failed."); - return status; - } - params->resize(paramPointers.size()); - size_t i = 0; - for (C2Param* const& paramPointer : paramPointers) { - if (!paramPointer) { - ALOGE("copyParamsFromBlob -- corrupted params blob."); - return C2_BAD_VALUE; - } - (*params)[i++] = C2Param::Copy(*paramPointer); - } - return C2_OK; + return _copyParamsFromBlob(params, blob); +} + +// Params -> std::vector> +bool copyParamsFromBlob( + std::vector>* params, + Params blob) { + return _copyParamsFromBlob(params, blob); } // Params -> update std::vector> -c2_status_t updateParamsFromBlob( +bool updateParamsFromBlob( const std::vector& params, const Params& blob) { std::unordered_map index2param; for (C2Param* const& param : params) { if (!param) { - ALOGE("updateParamsFromBlob -- corrupted input params."); - return C2_BAD_VALUE; + LOG(ERROR) << "updateParamsFromBlob -- null output param."; + return false; } if (index2param.find(param->index()) == index2param.end()) { index2param.emplace(param->index(), param); @@ -1547,33 +1701,31 @@ c2_status_t updateParamsFromBlob( } std::vector paramPointers; - c2_status_t status = parseParamsBlob(¶mPointers, blob); - if (status != C2_OK) { - ALOGE("updateParamsFromBlob -- blob parsing failed."); - return status; + if (!parseParamsBlob(¶mPointers, blob)) { + LOG(ERROR) << "updateParamsFromBlob -- failed to parse."; + return false; } for (C2Param* const& paramPointer : paramPointers) { if (!paramPointer) { - ALOGE("updateParamsFromBlob -- corrupted param in blob."); - return C2_BAD_VALUE; + LOG(ERROR) << "updateParamsFromBlob -- null input param."; + return false; } decltype(index2param)::iterator i = index2param.find( paramPointer->index()); if (i == index2param.end()) { - ALOGW("updateParamsFromBlob -- unseen param index."); + LOG(DEBUG) << "updateParamsFromBlob -- index " + << paramPointer->index() << " not found. Skipping..."; continue; } if (!i->second->updateFrom(*paramPointer)) { - ALOGE("updateParamsFromBlob -- mismatching sizes: " - "%u vs %u (index = %u).", - static_cast(params.size()), - static_cast(paramPointer->size()), - static_cast(i->first)); - return C2_BAD_VALUE; + LOG(ERROR) << "updateParamsFromBlob -- size mismatch: " + << params.size() << " vs " << paramPointer->size() + << " (index = " << i->first << ")."; + return false; } } - return C2_OK; + return true; } // Convert BufferPool ResultStatus to c2_status_t. @@ -1590,7 +1742,8 @@ c2_status_t toC2Status(ResultStatus rs) { case ResultStatus::CRITICAL_ERROR: return C2_CORRUPTED; default: - ALOGW("Unrecognized BufferPool ResultStatus: %d", static_cast(rs)); + LOG(WARNING) << "Unrecognized BufferPool ResultStatus: " + << static_cast(rs) << "."; return C2_CORRUPTED; } } @@ -1669,34 +1822,33 @@ status_t attachToBufferQueue(const C2ConstGraphicBlock& block, uint32_t generation, int32_t* bqSlot) { if (!igbp) { - ALOGW("attachToBufferQueue -- null producer."); + LOG(WARNING) << "attachToBufferQueue -- null producer."; return NO_INIT; } sp graphicBuffer = createGraphicBuffer(block); graphicBuffer->setGenerationNumber(generation); - ALOGV("attachToBufferQueue -- attaching buffer: " - "block dimension %ux%u, " - "graphicBuffer dimension %ux%u, " - "format %#x, usage %#llx, stride %u, generation %u.", - static_cast(block.width()), - static_cast(block.height()), - static_cast(graphicBuffer->getWidth()), - static_cast(graphicBuffer->getHeight()), - static_cast(graphicBuffer->getPixelFormat()), - static_cast(graphicBuffer->getUsage()), - static_cast(graphicBuffer->getStride()), - static_cast(graphicBuffer->getGenerationNumber())); + LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:" + << " block dimension " << block.width() << "x" + << block.height() + << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x" + << graphicBuffer->getHeight() + << std::hex << std::setfill('0') + << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat() + << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage() + << std::dec << std::setfill(' ') + << ", stride " << graphicBuffer->getStride() + << ", generation " << graphicBuffer->getGenerationNumber(); status_t result = igbp->attachBuffer(bqSlot, graphicBuffer); if (result != OK) { - ALOGW("attachToBufferQueue -- attachBuffer failed. Error code = %d", - static_cast(result)); + LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: " + "status = " << result << "."; return result; } - ALOGV("attachToBufferQueue -- attachBuffer returned slot %d", - static_cast(*bqSlot)); + LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #" + << *bqSlot << "."; return OK; } @@ -1747,11 +1899,11 @@ bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, // If the block's bqId is the same as the desired bqId, just hold. if ((oldId == bqId) && (oldGeneration == generation)) { - ALOGV("holdBufferQueueBlock -- import without attaching: " - "bqId %llu, bqSlot %d, generation %u.", - static_cast(oldId), - static_cast(oldSlot), - static_cast(generation)); + LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" + << " bqId " << oldId + << ", bqSlot " << oldSlot + << ", generation " << generation + << "."; _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); return true; } @@ -1765,19 +1917,18 @@ bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot); if (result != OK) { - ALOGE("holdBufferQueueBlock -- fail to attach: " - "target bqId %llu, generation %u.", - static_cast(bqId), - static_cast(generation)); - + LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" + << " target bqId " << bqId + << ", generation " << generation + << "."; return false; } - ALOGV("holdBufferQueueBlock -- attached: " - "bqId %llu, bqSlot %d, generation %u.", - static_cast(bqId), - static_cast(bqSlot), - static_cast(generation)); + LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" + << " bqId " << bqId + << ", bqSlot " << bqSlot + << ", generation " << generation + << "."; _C2BlockFactory::AssignBlockToBufferQueue( data, getHgbp(igbp), generation, bqId, bqSlot, true); return true; diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h index a68853018d..d1557cbcf5 100644 --- a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h @@ -94,10 +94,14 @@ struct CodecListener : public android::Codec2Client::Listener { (void)buffer; } - virtual void onFramesRendered( - const std::vector& renderedFrames) override { + virtual void onFrameRendered( + uint64_t bufferQueueId, + int32_t slotId, + int64_t timestampNs) override { /* TODO */ - (void)renderedFrames; + (void)bufferQueueId; + (void)slotId; + (void)timestampNs; } // std::mutex mQueueLock; // std::condition_variable mQueueCondition; diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index cd374b05d4..5b52fcd5e8 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2Client" -#include +#include #include @@ -32,7 +32,6 @@ #include #include #include -#undef LOG #include #include @@ -99,19 +98,17 @@ const C2String& Codec2ConfigurableClient::getName() const { return mName; } -Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const { - return static_cast(mBase.get()); -} - Codec2ConfigurableClient::Codec2ConfigurableClient( - const sp& base) : mBase(base) { - Return transStatus = base->getName( - [this](const hidl_string& name) { - mName = name.c_str(); - }); - if (!transStatus.isOk()) { - ALOGE("Cannot obtain name from IConfigurable."); - } + const sp& base) + : mBase{base}, + mName{[base]() -> C2String { + C2String outName; + Return transStatus = base->getName( + [&outName](const hidl_string& name) { + outName = name.c_str(); + }); + return transStatus.isOk() ? outName : ""; + }()} { } c2_status_t Codec2ConfigurableClient::query( @@ -124,7 +121,7 @@ c2_status_t Codec2ConfigurableClient::query( size_t numIndices = 0; for (C2Param* const& stackParam : stackParams) { if (!stackParam) { - ALOGW("query -- null stack param encountered."); + LOG(WARNING) << "query -- null stack param encountered."; continue; } indices[numIndices++] = static_cast(stackParam->index()); @@ -139,32 +136,31 @@ c2_status_t Codec2ConfigurableClient::query( heapParams->reserve(heapParams->size() + numIndices); } c2_status_t status; - Return transStatus = base()->query( + Return transStatus = mBase->query( indices, mayBlock == C2_MAY_BLOCK, [&status, &numStackIndices, &stackParams, heapParams]( Status s, const Params& p) { status = static_cast(s); if (status != C2_OK && status != C2_BAD_INDEX) { - ALOGE("query -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "query -- call failed: " + << status << "."; return; } std::vector paramPointers; - c2_status_t parseStatus = parseParamsBlob(¶mPointers, p); - if (parseStatus != C2_OK) { - ALOGE("query -- error while parsing params. " - "Error code = %d", static_cast(status)); - status = parseStatus; + if (!parseParamsBlob(¶mPointers, p)) { + LOG(ERROR) << "query -- error while parsing params."; + status = C2_CORRUPTED; return; } size_t i = 0; - for (auto it = paramPointers.begin(); it != paramPointers.end(); ) { + for (auto it = paramPointers.begin(); + it != paramPointers.end(); ) { C2Param* paramPointer = *it; if (numStackIndices > 0) { --numStackIndices; if (!paramPointer) { - ALOGW("query -- null stack param."); + LOG(WARNING) << "query -- null stack param."; ++it; continue; } @@ -172,37 +168,41 @@ c2_status_t Codec2ConfigurableClient::query( ++i; } if (i >= stackParams.size()) { - ALOGE("query -- unexpected error."); + LOG(ERROR) << "query -- unexpected error."; status = C2_CORRUPTED; return; } if (stackParams[i]->index() != paramPointer->index()) { - ALOGW("query -- param skipped. index = %d", - static_cast(stackParams[i]->index())); + LOG(WARNING) << "query -- param skipped: " + "index = " + << stackParams[i]->index() << "."; stackParams[i++]->invalidate(); continue; } if (!stackParams[i++]->updateFrom(*paramPointer)) { - ALOGW("query -- param update failed. index = %d", - static_cast(paramPointer->index())); + LOG(WARNING) << "query -- param update failed: " + "index = " + << paramPointer->index() << "."; } } else { if (!paramPointer) { - ALOGW("query -- null heap param."); + LOG(WARNING) << "query -- null heap param."; ++it; continue; } if (!heapParams) { - ALOGW("query -- unexpected extra stack param."); + LOG(WARNING) << "query -- " + "unexpected extra stack param."; } else { - heapParams->emplace_back(C2Param::Copy(*paramPointer)); + heapParams->emplace_back( + C2Param::Copy(*paramPointer)); } } ++it; } }); if (!transStatus.isOk()) { - ALOGE("query -- transaction failed."); + LOG(ERROR) << "query -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; @@ -213,13 +213,12 @@ c2_status_t Codec2ConfigurableClient::config( c2_blocking_t mayBlock, std::vector>* const failures) { Params hidlParams; - Status hidlStatus = createParamsBlob(&hidlParams, params); - if (hidlStatus != Status::OK) { - ALOGE("config -- bad input."); + if (!createParamsBlob(&hidlParams, params)) { + LOG(ERROR) << "config -- bad input."; return C2_TRANSACTION_FAILED; } c2_status_t status; - Return transStatus = base()->config( + Return transStatus = mBase->config( hidlParams, mayBlock == C2_MAY_BLOCK, [&status, ¶ms, failures]( @@ -227,24 +226,27 @@ c2_status_t Codec2ConfigurableClient::config( const hidl_vec f, const Params& o) { status = static_cast(s); - if (status != C2_OK) { - ALOGD("config -- call failed. " - "Error code = %d", static_cast(status)); + if (status != C2_OK && status != C2_BAD_INDEX) { + LOG(DEBUG) << "config -- call failed: " + << status << "."; } size_t i = failures->size(); failures->resize(i + f.size()); for (const SettingResult& sf : f) { - status = objcpy(&(*failures)[i++], sf); - if (status != C2_OK) { - ALOGE("config -- invalid returned SettingResult. " - "Error code = %d", static_cast(status)); + if (!objcpy(&(*failures)[i++], sf)) { + LOG(ERROR) << "config -- " + << "invalid SettingResult returned."; return; } } - status = updateParamsFromBlob(params, o); + if (!updateParamsFromBlob(params, o)) { + LOG(ERROR) << "config -- " + << "failed to parse returned params."; + status = C2_CORRUPTED; + } }); if (!transStatus.isOk()) { - ALOGE("config -- transaction failed."); + LOG(ERROR) << "config -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; @@ -254,7 +256,7 @@ c2_status_t Codec2ConfigurableClient::querySupportedParams( std::vector>* const params) const { // TODO: Cache and query properly! c2_status_t status; - Return transStatus = base()->querySupportedParams( + Return transStatus = mBase->querySupportedParams( std::numeric_limits::min(), std::numeric_limits::max(), [&status, params]( @@ -262,24 +264,22 @@ c2_status_t Codec2ConfigurableClient::querySupportedParams( const hidl_vec& p) { status = static_cast(s); if (status != C2_OK) { - ALOGE("querySupportedParams -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "querySupportedParams -- call failed: " + << status << "."; return; } size_t i = params->size(); params->resize(i + p.size()); for (const ParamDescriptor& sp : p) { - status = objcpy(&(*params)[i++], sp); - if (status != C2_OK) { - ALOGE("querySupportedParams -- " - "invalid returned ParamDescriptor. " - "Error code = %d", static_cast(status)); + if (!objcpy(&(*params)[i++], sp)) { + LOG(ERROR) << "querySupportedParams -- " + << "invalid returned ParamDescriptor."; return; } } }); if (!transStatus.isOk()) { - ALOGE("querySupportedParams -- transaction failed."); + LOG(ERROR) << "querySupportedParams -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; @@ -290,15 +290,14 @@ c2_status_t Codec2ConfigurableClient::querySupportedValues( c2_blocking_t mayBlock) const { hidl_vec inFields(fields.size()); for (size_t i = 0; i < fields.size(); ++i) { - Status hidlStatus = objcpy(&inFields[i], fields[i]); - if (hidlStatus != Status::OK) { - ALOGE("querySupportedValues -- bad input"); + if (!objcpy(&inFields[i], fields[i])) { + LOG(ERROR) << "querySupportedValues -- bad input"; return C2_TRANSACTION_FAILED; } } c2_status_t status; - Return transStatus = base()->querySupportedValues( + Return transStatus = mBase->querySupportedValues( inFields, mayBlock == C2_MAY_BLOCK, [&status, &inFields, &fields]( @@ -306,27 +305,28 @@ c2_status_t Codec2ConfigurableClient::querySupportedValues( const hidl_vec& r) { status = static_cast(s); if (status != C2_OK) { - ALOGE("querySupportedValues -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "querySupportedValues -- call failed: " + << status << "."; return; } if (r.size() != fields.size()) { - ALOGE("querySupportedValues -- input and output lists " - "have different sizes."); + LOG(ERROR) << "querySupportedValues -- " + "input and output lists " + "have different sizes."; status = C2_CORRUPTED; return; } for (size_t i = 0; i < fields.size(); ++i) { - status = objcpy(&fields[i], inFields[i], r[i]); - if (status != C2_OK) { - ALOGE("querySupportedValues -- invalid returned value. " - "Error code = %d", static_cast(status)); + if (!objcpy(&fields[i], inFields[i], r[i])) { + LOG(ERROR) << "querySupportedValues -- " + "invalid returned value."; + status = C2_CORRUPTED; return; } } }); if (!transStatus.isOk()) { - ALOGE("querySupportedValues -- transaction failed."); + LOG(ERROR) << "querySupportedValues -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; @@ -339,22 +339,24 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { virtual Return onWorkDone(const WorkBundle& workBundle) override { std::list> workItems; - c2_status_t status = objcpy(&workItems, workBundle); - if (status != C2_OK) { - ALOGI("onWorkDone -- received corrupted WorkBundle. " - "status = %d.", static_cast(status)); + if (!objcpy(&workItems, workBundle)) { + LOG(DEBUG) << "onWorkDone -- received corrupted WorkBundle."; return Void(); } // release input buffers potentially held by the component from queue size_t numDiscardedInputBuffers = 0; - std::shared_ptr strongComponent = component.lock(); + std::shared_ptr strongComponent = + component.lock(); if (strongComponent) { - numDiscardedInputBuffers = strongComponent->handleOnWorkDone(workItems); + numDiscardedInputBuffers = + strongComponent->handleOnWorkDone(workItems); } if (std::shared_ptr listener = base.lock()) { - listener->onWorkDone(component, workItems, numDiscardedInputBuffers); + listener->onWorkDone(component, + workItems, + numDiscardedInputBuffers); } else { - ALOGD("onWorkDone -- listener died."); + LOG(DEBUG) << "onWorkDone -- listener died."; } return Void(); } @@ -363,13 +365,10 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { const hidl_vec& settingResults) override { std::vector> c2SettingResults( settingResults.size()); - c2_status_t status; for (size_t i = 0; i < settingResults.size(); ++i) { std::unique_ptr c2SettingResult; - status = objcpy(&c2SettingResult, settingResults[i]); - if (status != C2_OK) { - ALOGI("onTripped -- received corrupted SettingResult. " - "status = %d.", static_cast(status)); + if (!objcpy(&c2SettingResult, settingResults[i])) { + LOG(DEBUG) << "onTripped -- received corrupted SettingResult."; return Void(); } c2SettingResults[i] = std::move(c2SettingResult); @@ -377,20 +376,21 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { if (std::shared_ptr listener = base.lock()) { listener->onTripped(component, c2SettingResults); } else { - ALOGD("onTripped -- listener died."); + LOG(DEBUG) << "onTripped -- listener died."; } return Void(); } virtual Return onError(Status s, uint32_t errorCode) override { - ALOGD("onError -- status = %d, errorCode = %u.", - static_cast(s), - static_cast(errorCode)); + LOG(DEBUG) << "onError --" + << " status = " << s + << ", errorCode = " << errorCode + << "."; if (std::shared_ptr listener = base.lock()) { listener->onError(component, s == Status::OK ? errorCode : static_cast(s)); } else { - ALOGD("onError -- listener died."); + LOG(DEBUG) << "onError -- listener died."; } return Void(); } @@ -398,55 +398,70 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { virtual Return onFramesRendered( const hidl_vec& renderedFrames) override { std::shared_ptr listener = base.lock(); - std::vector rfs; - rfs.reserve(renderedFrames.size()); - for (const RenderedFrame& rf : renderedFrames) { - if (rf.slotId >= 0) { - if (listener) { - rfs.emplace_back(rf.bufferQueueId, - rf.slotId, - rf.timestampNs); - } - } else { - std::shared_ptr strongComponent = - component.lock(); - if (strongComponent) { - uint64_t frameIndex = rf.bufferQueueId; - size_t bufferIndex = static_cast(~rf.slotId); - ALOGV("Received death notification of input buffer: " - "frameIndex = %llu, bufferIndex = %zu.", - static_cast(frameIndex), - bufferIndex); - std::shared_ptr buffer = - strongComponent->freeInputBuffer( - frameIndex, bufferIndex); - if (buffer) { - listener->onInputBufferDone(buffer); - } - } - } + if (!listener) { + LOG(DEBUG) << "onFramesRendered -- listener died."; + return Void(); } - if (!rfs.empty()) { - if (listener) { - listener->onFramesRendered(rfs); - } else { - ALOGD("onFramesRendered -- listener died."); + for (const RenderedFrame& renderedFrame : renderedFrames) { + listener->onFrameRendered( + renderedFrame.bufferQueueId, + renderedFrame.slotId, + renderedFrame.timestampNs); + } + return Void(); + } + + virtual Return onInputBuffersReleased( + const hidl_vec& inputBuffers) override { + std::shared_ptr listener = base.lock(); + if (!listener) { + LOG(DEBUG) << "onInputBuffersReleased -- listener died."; + return Void(); + } + std::shared_ptr strongComponent = + component.lock(); + if (!strongComponent) { + LOG(DEBUG) << "onInputBuffersReleased -- component died."; + return Void(); + } + for (const InputBuffer& inputBuffer : inputBuffers) { + std::shared_ptr buffer = + strongComponent->freeInputBuffer( + inputBuffer.frameIndex, + inputBuffer.arrayIndex); + LOG(VERBOSE) << "onInputBuffersReleased --" + " received death notification of" + " input buffer:" + " frameIndex = " << inputBuffer.frameIndex + << ", bufferIndex = " << inputBuffer.arrayIndex + << "."; + if (buffer) { + listener->onInputBufferDone(buffer); } } return Void(); } + }; // Codec2Client -Codec2Client::Base* Codec2Client::base() const { - return static_cast(mBase.get()); -} - -Codec2Client::Codec2Client(const sp& base, std::string instanceName) : - Codec2ConfigurableClient(base), mListed(false), mInstanceName(instanceName) { +Codec2Client::Codec2Client(const sp& base, + std::string serviceName) + : Configurable{ + [base]() -> sp { + Return> transResult = + base->getConfigurable(); + return transResult.isOk() ? + static_cast>(transResult) : + nullptr; + }() + }, + mBase{base}, + mListed{false}, + mServiceName{serviceName} { Return> transResult = base->getPoolClientManager(); if (!transResult.isOk()) { - ALOGE("getPoolClientManager -- failed transaction."); + LOG(ERROR) << "getPoolClientManager -- transaction failed."; } else { mHostPoolManager = static_cast>(transResult); } @@ -457,13 +472,10 @@ c2_status_t Codec2Client::createComponent( const std::shared_ptr& listener, std::shared_ptr* const component) { - // TODO: Add support for Bufferpool - - c2_status_t status; - sp hidlListener = new Component::HidlListener(); + sp hidlListener = new Component::HidlListener{}; hidlListener->base = listener; - Return transStatus = base()->createComponent( + Return transStatus = mBase->createComponent( name, hidlListener, ClientManager::getInstance(), @@ -478,23 +490,24 @@ c2_status_t Codec2Client::createComponent( hidlListener->component = *component; }); if (!transStatus.isOk()) { - ALOGE("createComponent -- failed transaction."); + LOG(ERROR) << "createComponent(" << name.c_str() + << ") -- transaction failed."; return C2_TRANSACTION_FAILED; - } - - if (status != C2_OK) { + } else if (status != C2_OK) { + LOG(ERROR) << "createComponent(" << name.c_str() + << ") -- call failed: " << status << "."; return status; - } - - if (!*component) { - ALOGE("createComponent -- null component."); + } else if (!*component) { + LOG(ERROR) << "createComponent(" << name.c_str() + << ") -- null component."; return C2_CORRUPTED; } status = (*component)->setDeathListener(*component, listener); if (status != C2_OK) { - ALOGE("createComponent -- setDeathListener returned error: %d.", - static_cast(status)); + LOG(ERROR) << "createComponent(" << name.c_str() + << ") -- failed to set up death listener: " + << status << "."; } (*component)->mBufferPoolSender.setReceiver(mHostPoolManager); @@ -505,44 +518,51 @@ c2_status_t Codec2Client::createInterface( const C2String& name, std::shared_ptr* const interface) { c2_status_t status; - Return transStatus = base()->createInterface( + Return transStatus = mBase->createInterface( name, [&status, interface]( Status s, const sp& i) { status = static_cast(s); if (status != C2_OK) { - ALOGE("createInterface -- call failed. " - "Error code = %d", static_cast(status)); return; } - *interface = std::make_shared(i); + *interface = std::make_shared(i); }); if (!transStatus.isOk()) { - ALOGE("createInterface -- failed transaction."); + LOG(ERROR) << "createInterface(" << name.c_str() + << ") -- transaction failed."; return C2_TRANSACTION_FAILED; + } else if (status != C2_OK) { + LOG(ERROR) << "createComponent(" << name.c_str() + << ") -- call failed: " << status << "."; + return status; } + return status; } c2_status_t Codec2Client::createInputSurface( - std::shared_ptr* const inputSurface) { - Return> transResult = base()->createInputSurface(); - if (!transResult.isOk()) { - ALOGE("createInputSurface -- failed transaction."); + std::shared_ptr* const inputSurface) { + c2_status_t status; + Return transStatus = mBase->createInputSurface( + [&status, inputSurface]( + Status s, + const sp& i) { + status = static_cast(s); + if (status != C2_OK) { + return; + } + *inputSurface = std::make_shared(i); + }); + if (!transStatus.isOk()) { + LOG(ERROR) << "createInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; + } else if (status != C2_OK) { + LOG(DEBUG) << "createInputSurface -- call failed: " + << status << "."; } - sp result = static_cast>(transResult); - if (!result) { - *inputSurface = nullptr; - return C2_OK; - } - *inputSurface = std::make_shared(result); - if (!*inputSurface) { - ALOGE("createInputSurface -- unknown error."); - return C2_CORRUPTED; - } - return C2_OK; + return status; } const std::vector& Codec2Client::listComponents() const { @@ -550,22 +570,26 @@ const std::vector& Codec2Client::listComponents() const { if (mListed) { return mTraitsList; } - Return transStatus = base()->listComponents( - [this](const hidl_vec& t) { + Return transStatus = mBase->listComponents( + [this](Status s, + const hidl_vec& t) { + if (s != Status::OK) { + LOG(DEBUG) << "listComponents -- call failed: " + << static_cast(s) << "."; + return; + } mTraitsList.resize(t.size()); mAliasesBuffer.resize(t.size()); for (size_t i = 0; i < t.size(); ++i) { - c2_status_t status = objcpy( - &mTraitsList[i], &mAliasesBuffer[i], t[i]); - mTraitsList[i].owner = mInstanceName; - if (status != C2_OK) { - ALOGE("listComponents -- corrupted output."); + if (!objcpy(&mTraitsList[i], &mAliasesBuffer[i], t[i])) { + LOG(ERROR) << "listComponents -- corrupted output."; return; } + mTraitsList[i].owner = mServiceName; } }); if (!transStatus.isOk()) { - ALOGE("listComponents -- failed transaction."); + LOG(ERROR) << "listComponents -- transaction failed."; } mListed = true; return mTraitsList; @@ -577,7 +601,7 @@ c2_status_t Codec2Client::copyBuffer( // TODO: Implement? (void)src; (void)dst; - ALOGE("copyBuffer not implemented"); + LOG(ERROR) << "copyBuffer not implemented"; return C2_OMITTED; } @@ -597,21 +621,25 @@ std::shared_ptr const hidl_vec& sd) { c2_status_t status = static_cast(s); if (status != C2_OK) { - ALOGE("getStructDescriptors -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "SimpleParamReflector -- " + "getStructDescriptors() failed: " + << status << "."; descriptor.reset(); return; } if (sd.size() != 1) { - ALOGD("getStructDescriptors -- returned vector of size %zu.", - sd.size()); + LOG(DEBUG) << "SimpleParamReflector -- " + "getStructDescriptors() " + "returned vector of size " + << sd.size() << ". " + "It should be 1."; descriptor.reset(); return; } - status = objcpy(&descriptor, sd[0]); - if (status != C2_OK) { - ALOGD("getStructDescriptors -- failed to convert. " - "Error code = %d", static_cast(status)); + if (!objcpy(&descriptor, sd[0])) { + LOG(DEBUG) << "SimpleParamReflector -- " + "getStructDescriptors() returned " + "corrupted data."; descriptor.reset(); return; } @@ -625,44 +653,44 @@ std::shared_ptr sp mBase; }; - return std::make_shared(base()); + return std::make_shared(mBase); }; std::shared_ptr Codec2Client::CreateFromService( - const char* instanceName, bool waitForService) { - if (!instanceName) { + const char* serviceName, bool waitForService) { + if (!serviceName) { return nullptr; } sp baseStore = waitForService ? - Base::getService(instanceName) : - Base::tryGetService(instanceName); + Base::getService(serviceName) : + Base::tryGetService(serviceName); if (!baseStore) { if (waitForService) { - ALOGW("Codec2.0 service \"%s\" inaccessible. " - "Check the device manifest.", - instanceName); + LOG(WARNING) << "Codec2.0 service \"" << serviceName << "\"" + " inaccessible. Check the device manifest."; } else { - ALOGD("Codec2.0 service \"%s\" unavailable right now. " - "Try again later.", - instanceName); + LOG(DEBUG) << "Codec2.0 service \"" << serviceName << "\"" + " unavailable at the moment. " + " Wait or check the device manifest."; } return nullptr; } - return std::make_shared(baseStore, instanceName); + return std::make_shared(baseStore, serviceName); } c2_status_t Codec2Client::ForAllStores( const std::string &key, - std::function&)> predicate) { + std::function&)> + predicate) { c2_status_t status = C2_NO_INIT; // no IComponentStores present // Cache the mapping key -> index of Codec2Client in getClient(). static std::mutex key2IndexMutex; static std::map key2Index; - // By default try all stores. However, try the last known client first. If the last known - // client fails, retry once. We do this by pushing the last known client in front of the - // list of all clients. + // By default try all stores. However, try the last known client first. If + // the last known client fails, retry once. We do this by pushing the last + // known client in front of the list of all clients. std::deque indices; for (size_t index = kNumClients; index > 0; ) { indices.push_front(--index); @@ -688,7 +716,8 @@ c2_status_t Codec2Client::ForAllStores( } } if (wasMapped) { - ALOGI("Could not find '%s' in last instance. Retrying...", key.c_str()); + LOG(INFO) << "Could not find \"" << key << "\"" + " in the last instance. Retrying..."; wasMapped = false; } } @@ -704,20 +733,27 @@ std::shared_ptr c2_status_t status = ForAllStores( componentName, [owner, &component, componentName, &listener]( - const std::shared_ptr &client) -> c2_status_t { - c2_status_t status = client->createComponent(componentName, listener, &component); + const std::shared_ptr &client) + -> c2_status_t { + c2_status_t status = client->createComponent(componentName, + listener, + &component); if (status == C2_OK) { if (owner) { *owner = client; } } else if (status != C2_NOT_FOUND) { - ALOGD("IComponentStore(%s)::createComponent('%s') returned %s", - client->getInstanceName().c_str(), componentName, asString(status)); + LOG(DEBUG) << "IComponentStore(" + << client->getServiceName() + << ")::createComponent(\"" << componentName + << "\") returned status = " + << status << "."; } return status; }); if (status != C2_OK) { - ALOGI("Could not create component '%s' (%s)", componentName, asString(status)); + LOG(DEBUG) << "Could not create component \"" << componentName << "\". " + "Status = " << status << "."; } return component; } @@ -730,20 +766,26 @@ std::shared_ptr c2_status_t status = ForAllStores( interfaceName, [owner, &interface, interfaceName]( - const std::shared_ptr &client) -> c2_status_t { - c2_status_t status = client->createInterface(interfaceName, &interface); + const std::shared_ptr &client) + -> c2_status_t { + c2_status_t status = client->createInterface(interfaceName, + &interface); if (status == C2_OK) { if (owner) { *owner = client; } } else if (status != C2_NOT_FOUND) { - ALOGD("IComponentStore(%s)::createInterface('%s') returned %s", - client->getInstanceName().c_str(), interfaceName, asString(status)); + LOG(DEBUG) << "IComponentStore(" + << client->getServiceName() + << ")::createInterface(\"" << interfaceName + << "\") returned status = " + << status << "."; } return status; }); if (status != C2_OK) { - ALOGI("Could not create interface '%s' (%s)", interfaceName, asString(status)); + LOG(DEBUG) << "Could not create interface \"" << interfaceName << "\". " + "Status = " << status << "."; } return interface; } @@ -762,7 +804,8 @@ std::shared_ptr Codec2Client::CreateInputSurface() { } } } - ALOGW("Could not create an input surface from any Codec2.0 services."); + LOG(INFO) << "Could not create an input surface " + "from any Codec2.0 services."; return nullptr; } @@ -798,15 +841,39 @@ const std::vector& Codec2Client::ListComponents() { Codec2Client::Listener::~Listener() { } -// Codec2Client::Component - -Codec2Client::Component::Base* Codec2Client::Component::base() const { - return static_cast(mBase.get()); +// Codec2Client::Interface +Codec2Client::Interface::Interface(const sp& base) + : Configurable{ + [base]() -> sp { + Return> transResult = + base->getConfigurable(); + return transResult.isOk() ? + static_cast>(transResult) : + nullptr; + }() + }, + mBase{base} { } -Codec2Client::Component::Component(const sp& base) : - Codec2Client::Configurable(base), - mBufferPoolSender(nullptr) { +// Codec2Client::Component +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; + }() + }, + mBase{base}, + mBufferPoolSender{nullptr} { } Codec2Client::Component::~Component() { @@ -817,7 +884,7 @@ c2_status_t Codec2Client::Component::createBlockPool( C2BlockPool::local_id_t* blockPoolId, std::shared_ptr* configurable) { c2_status_t status; - Return transStatus = base()->createBlockPool( + Return transStatus = mBase->createBlockPool( static_cast(id), [&status, blockPoolId, configurable]( Status s, @@ -826,15 +893,15 @@ c2_status_t Codec2Client::Component::createBlockPool( status = static_cast(s); configurable->reset(); if (status != C2_OK) { - ALOGE("createBlockPool -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "createBlockPool -- call failed: " + << status << "."; return; } *blockPoolId = static_cast(pId); - *configurable = std::make_shared(c); + *configurable = std::make_shared(c); }); if (!transStatus.isOk()) { - ALOGE("createBlockPool -- transaction failed."); + LOG(ERROR) << "createBlockPool -- transaction failed."; return C2_TRANSACTION_FAILED; } return status; @@ -842,10 +909,10 @@ c2_status_t Codec2Client::Component::createBlockPool( c2_status_t Codec2Client::Component::destroyBlockPool( C2BlockPool::local_id_t localId) { - Return transResult = base()->destroyBlockPool( + Return transResult = mBase->destroyBlockPool( static_cast(localId)); if (!transResult.isOk()) { - ALOGE("destroyBlockPool -- transaction failed."); + LOG(ERROR) << "destroyBlockPool -- transaction failed."; return C2_TRANSACTION_FAILED; } return static_cast(static_cast(transResult)); @@ -859,7 +926,8 @@ size_t Codec2Client::Component::handleOnWorkDone( if (work) { if (work->worklets.empty() || !work->worklets.back() - || (work->worklets.back()->output.flags & C2FrameData::FLAG_INCOMPLETE) == 0) { + || (work->worklets.back()->output.flags & + C2FrameData::FLAG_INCOMPLETE) == 0) { // input is complete inputDone.emplace_back(work->input.ordinal.frameIndex.peeku()); } @@ -872,13 +940,14 @@ size_t Codec2Client::Component::handleOnWorkDone( for (uint64_t inputIndex : inputDone) { auto it = mInputBuffers.find(inputIndex); if (it == mInputBuffers.end()) { - ALOGV("onWorkDone -- returned consumed/unknown " - "input frame: index %llu", - (long long)inputIndex); + LOG(VERBOSE) << "onWorkDone -- returned consumed/unknown " + "input frame: index = " + << inputIndex << "."; } else { - ALOGV("onWorkDone -- processed input frame: " - "index %llu (containing %zu buffers)", - (long long)inputIndex, it->second.size()); + LOG(VERBOSE) << "onWorkDone -- processed input frame: " + << inputIndex + << " (containing " << it->second.size() + << " buffers)."; mInputBuffers.erase(it); mInputBufferCount.erase(inputIndex); ++numDiscardedInputBuffers; @@ -906,21 +975,21 @@ std::shared_ptr Codec2Client::Component::freeInputBuffer( std::lock_guard lock(mInputBuffersMutex); auto it = mInputBuffers.find(frameIndex); if (it == mInputBuffers.end()) { - ALOGI("freeInputBuffer -- Unrecognized input frame index %llu.", - static_cast(frameIndex)); + LOG(INFO) << "freeInputBuffer -- Unrecognized input frame index " + << frameIndex << "."; return nullptr; } if (bufferIndex >= it->second.size()) { - ALOGI("freeInputBuffer -- Input buffer no. %zu is invalid in " - "input frame index %llu.", - bufferIndex, static_cast(frameIndex)); + LOG(INFO) << "freeInputBuffer -- Input buffer number " << bufferIndex + << " is not valid in input with frame index " << frameIndex + << "."; return nullptr; } buffer = it->second[bufferIndex]; if (!buffer) { - ALOGI("freeInputBuffer -- Input buffer no. %zu in " - "input frame index %llu has already been freed.", - bufferIndex, static_cast(frameIndex)); + LOG(INFO) << "freeInputBuffer -- Input buffer number " << bufferIndex + << " in input with frame index " << frameIndex + << " has already been freed."; return nullptr; } it->second[bufferIndex] = nullptr; @@ -949,33 +1018,33 @@ c2_status_t Codec2Client::Component::queue( if (!res.second) { // TODO: append? - for now we are replacing res.first->second = work->input.buffers; - ALOGI("queue -- duplicate input frame: index %llu. " - "Discarding the old input frame...", - (long long)inputIndex); + LOG(INFO) << "queue -- duplicate input frame index: " + << inputIndex + << ". Discarding the old input frame..."; } mInputBufferCount[inputIndex] = work->input.buffers.size(); - ALOGV("queue -- queueing input frame: " - "index %llu (containing %zu buffers)", - (long long)inputIndex, work->input.buffers.size()); + LOG(VERBOSE) << "queue -- queuing input frame: " + << "index = " << inputIndex + << ", number of buffers = " + << work->input.buffers.size() + << "."; } } WorkBundle workBundle; - Status hidlStatus = objcpy(&workBundle, *items, &mBufferPoolSender); - if (hidlStatus != Status::OK) { - ALOGE("queue -- bad input."); + if (!objcpy(&workBundle, *items, &mBufferPoolSender)) { + LOG(ERROR) << "queue -- bad input."; return C2_TRANSACTION_FAILED; } - Return transStatus = base()->queue(workBundle); + Return transStatus = mBase->queue(workBundle); if (!transStatus.isOk()) { - ALOGE("queue -- transaction failed."); + LOG(ERROR) << "queue -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("queue -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "queue -- call failed: " << status << "."; } return status; } @@ -985,19 +1054,22 @@ c2_status_t Codec2Client::Component::flush( std::list>* const flushedWork) { (void)mode; // Flush mode isn't supported in HIDL yet. c2_status_t status; - Return transStatus = base()->flush( + Return transStatus = mBase->flush( [&status, flushedWork]( Status s, const WorkBundle& wb) { status = static_cast(s); if (status != C2_OK) { - ALOGE("flush -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "flush -- call failed: " << status << "."; return; } - status = objcpy(flushedWork, wb); + if (!objcpy(flushedWork, wb)) { + status = C2_CORRUPTED; + } else { + status = C2_OK; + } }); if (!transStatus.isOk()) { - ALOGE("flush -- transaction failed."); + LOG(ERROR) << "flush -- transaction failed."; return C2_TRANSACTION_FAILED; } @@ -1021,13 +1093,14 @@ c2_status_t Codec2Client::Component::flush( std::lock_guard lock(mInputBuffersMutex); auto it = mInputBuffers.find(flushedIndex); if (it == mInputBuffers.end()) { - ALOGV("flush -- returned consumed/unknown input frame: " - "index %llu", - (long long)flushedIndex); + LOG(VERBOSE) << "flush -- returned consumed/unknown input frame: " + "index = " << flushedIndex << "."; } else { - ALOGV("flush -- returned unprocessed input frame: " - "index %llu (containing %zu buffers)", - (long long)flushedIndex, mInputBufferCount[flushedIndex]); + LOG(VERBOSE) << "flush -- returned unprocessed input frame: " + "index = " << flushedIndex + << ", number of buffers = " + << mInputBufferCount[flushedIndex] + << "."; mInputBuffers.erase(it); mInputBufferCount.erase(flushedIndex); } @@ -1048,47 +1121,44 @@ c2_status_t Codec2Client::Component::flush( } c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) { - Return transStatus = base()->drain( + Return transStatus = mBase->drain( mode == C2Component::DRAIN_COMPONENT_WITH_EOS); if (!transStatus.isOk()) { - ALOGE("drain -- transaction failed."); + LOG(ERROR) << "drain -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("drain -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "drain -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::start() { - Return transStatus = base()->start(); + Return transStatus = mBase->start(); if (!transStatus.isOk()) { - ALOGE("start -- transaction failed."); + LOG(ERROR) << "start -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("start -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "start -- call failed: " << status << "."; } return status; } c2_status_t Codec2Client::Component::stop() { - Return transStatus = base()->stop(); + Return transStatus = mBase->stop(); if (!transStatus.isOk()) { - ALOGE("stop -- transaction failed."); + LOG(ERROR) << "stop -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("stop -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "stop -- call failed: " << status << "."; } mInputBuffersMutex.lock(); mInputBuffers.clear(); @@ -1098,16 +1168,15 @@ c2_status_t Codec2Client::Component::stop() { } c2_status_t Codec2Client::Component::reset() { - Return transStatus = base()->reset(); + Return transStatus = mBase->reset(); if (!transStatus.isOk()) { - ALOGE("reset -- transaction failed."); + LOG(ERROR) << "reset -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("reset -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "reset -- call failed: " << status << "."; } mInputBuffersMutex.lock(); mInputBuffers.clear(); @@ -1117,16 +1186,15 @@ c2_status_t Codec2Client::Component::reset() { } c2_status_t Codec2Client::Component::release() { - Return transStatus = base()->release(); + Return transStatus = mBase->release(); if (!transStatus.isOk()) { - ALOGE("release -- transaction failed."); + LOG(ERROR) << "release -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("release -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "release -- call failed: " << status << "."; } mInputBuffersMutex.lock(); mInputBuffers.clear(); @@ -1144,17 +1212,16 @@ c2_status_t Codec2Client::Component::setOutputSurface( igbp = new TWGraphicBufferProducer(surface); } - Return transStatus = base()->setOutputSurface( + Return transStatus = mBase->setOutputSurface( static_cast(blockPoolId), igbp); if (!transStatus.isOk()) { - ALOGE("setOutputSurface -- transaction failed."); + LOG(ERROR) << "setOutputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("setOutputSurface -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "setOutputSurface -- call failed: " << status << "."; } else { std::lock_guard lock(mOutputBufferQueueMutex); if (mOutputIgbp != surface) { @@ -1162,7 +1229,8 @@ c2_status_t Codec2Client::Component::setOutputSurface( if (!surface) { mOutputBqId = 0; } else if (surface->getUniqueId(&mOutputBqId) != OK) { - ALOGE("setOutputSurface -- cannot obtain bufferqueue id."); + LOG(ERROR) << "setOutputSurface -- " + "cannot obtain bufferqueue id."; } } mOutputGeneration = generation; @@ -1191,17 +1259,16 @@ status_t Codec2Client::Component::queueToOutputSurface( outputGeneration, &bqSlot); if (status != OK) { - ALOGW("queueToOutputSurface -- attaching failed."); + LOG(WARNING) << "queueToOutputSurface -- attaching failed."; return INVALID_OPERATION; } status = outputIgbp->queueBuffer(static_cast(bqSlot), input, output); if (status != OK) { - ALOGE("queueToOutputSurface -- queueBuffer() failed " - "on non-bufferqueue-based block. " - "Error code = %d.", - static_cast(status)); + LOG(ERROR) << "queueToOutputSurface -- queueBuffer() failed " + "on non-bufferqueue-based block. " + "Error = " << status << "."; return status; } return OK; @@ -1214,17 +1281,17 @@ status_t Codec2Client::Component::queueToOutputSurface( mOutputBufferQueueMutex.unlock(); if (!outputIgbp) { - ALOGV("queueToOutputSurface -- output surface is null."); + LOG(VERBOSE) << "queueToOutputSurface -- output surface is null."; return NO_INIT; } if (bqId != outputBqId || generation != outputGeneration) { if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) { - ALOGE("queueToOutputSurface -- migration fialed"); + LOG(ERROR) << "queueToOutputSurface -- migration failed."; return DEAD_OBJECT; } if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) { - ALOGE("queueToOutputSurface -- corrupted bq assignment"); + LOG(ERROR) << "queueToOutputSurface -- corrupted bufferqueue assignment."; return UNKNOWN_ERROR; } } @@ -1232,49 +1299,77 @@ status_t Codec2Client::Component::queueToOutputSurface( status_t status = outputIgbp->queueBuffer(static_cast(bqSlot), input, output); if (status != OK) { - ALOGD("queueToOutputSurface -- queueBuffer() failed " - "on bufferqueue-based block. " - "Error code = %d.", - static_cast(status)); + LOG(DEBUG) << "queueToOutputSurface -- queueBuffer() failed " + "on bufferqueue-based block. " + "Error = " << status << "."; return status; } if (!yieldBufferQueueBlock(block)) { - ALOGD("queueToOutputSurface -- cannot yield bufferqueue-based block " - "to the bufferqueue."); + LOG(DEBUG) << "queueToOutputSurface -- cannot yield " + "bufferqueue-based block to the bufferqueue."; return UNKNOWN_ERROR; } return OK; } +c2_status_t Codec2Client::Component::connectToInputSurface( + const std::shared_ptr& inputSurface, + std::shared_ptr* connection) { + c2_status_t status; + Return transStatus = mBase->connectToInputSurface( + inputSurface->mBase, + [&status, connection]( + Status s, const sp& c) { + status = static_cast(s); + if (status != C2_OK) { + LOG(DEBUG) << "connectToInputSurface -- call failed: " + << status << "."; + return; + } + *connection = std::make_shared(c); + }); + if (!transStatus.isOk()) { + LOG(ERROR) << "connectToInputSurface -- transaction failed"; + return C2_TRANSACTION_FAILED; + } + return status; +} + c2_status_t Codec2Client::Component::connectToOmxInputSurface( const sp& producer, - const sp& source) { - Return transStatus = base()->connectToOmxInputSurface( - producer, source); + const sp& source, + std::shared_ptr* connection) { + c2_status_t status; + Return transStatus = mBase->connectToOmxInputSurface( + producer, source, + [&status, connection]( + Status s, const sp& c) { + status = static_cast(s); + if (status != C2_OK) { + LOG(DEBUG) << "connectToOmxInputSurface -- call failed: " + << status << "."; + return; + } + *connection = std::make_shared(c); + }); if (!transStatus.isOk()) { - ALOGE("connectToOmxInputSurface -- transaction failed."); + LOG(ERROR) << "connectToOmxInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } - c2_status_t status = - static_cast(static_cast(transStatus)); - if (status != C2_OK) { - ALOGE("connectToOmxInputSurface -- call failed. " - "Error code = %d", static_cast(status)); - } return status; } c2_status_t Codec2Client::Component::disconnectFromInputSurface() { - Return transStatus = base()->disconnectFromInputSurface(); + Return transStatus = mBase->disconnectFromInputSurface(); if (!transStatus.isOk()) { - ALOGE("disconnectToInputSurface -- transaction failed."); + LOG(ERROR) << "disconnectToInputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; } c2_status_t status = static_cast(static_cast(transStatus)); if (status != C2_OK) { - ALOGE("disconnectFromInputSurface -- call failed. " - "Error code = %d", static_cast(status)); + LOG(DEBUG) << "disconnectFromInputSurface -- call failed: " + << status << "."; } return status; } @@ -1294,7 +1389,7 @@ c2_status_t Codec2Client::Component::setDeathListener( if (std::shared_ptr listener = base.lock()) { listener->onDeath(component); } else { - ALOGW("onDeath -- listener died."); + LOG(DEBUG) << "onDeath -- listener died."; } } }; @@ -1304,93 +1399,68 @@ c2_status_t Codec2Client::Component::setDeathListener( deathRecipient->component = component; component->mDeathRecipient = deathRecipient; - Return transResult = component->base()->linkToDeath( + Return transResult = component->mBase->linkToDeath( component->mDeathRecipient, 0); if (!transResult.isOk()) { - ALOGE("setDeathListener -- failed transaction: linkToDeath."); + LOG(ERROR) << "setDeathListener -- linkToDeath() transaction failed."; return C2_TRANSACTION_FAILED; } if (!static_cast(transResult)) { - ALOGE("setDeathListener -- linkToDeath call failed."); + LOG(DEBUG) << "setDeathListener -- linkToDeath() call failed."; return C2_CORRUPTED; } return C2_OK; } // Codec2Client::InputSurface - -Codec2Client::InputSurface::Base* Codec2Client::InputSurface::base() const { - return static_cast(mBase.get()); -} - -Codec2Client::InputSurface::InputSurface(const sp& base) : - mBase(base), - mGraphicBufferProducer(new +Codec2Client::InputSurface::InputSurface(const sp& base) + : Configurable{ + [base]() -> sp { + Return> transResult = + base->getConfigurable(); + return transResult.isOk() ? + static_cast>(transResult) : + nullptr; + }() + }, + mBase{base}, + mGraphicBufferProducer{new ::android::hardware::graphics::bufferqueue::V1_0::utils:: - H2BGraphicBufferProducer(base)) { -} - -c2_status_t Codec2Client::InputSurface::connectToComponent( - const std::shared_ptr& component, - std::shared_ptr* connection) { - c2_status_t status; - Return transStatus = base()->connectToComponent( - component->base(), - [&status, connection]( - Status s, - const sp& c) { - status = static_cast(s); - if (status != C2_OK) { - ALOGE("connectToComponent -- call failed. " - "Error code = %d", static_cast(status)); - return; - } - *connection = std::make_shared(c); - }); - if (!transStatus.isOk()) { - ALOGE("connect -- transaction failed."); - return C2_TRANSACTION_FAILED; - } - return status; + H2BGraphicBufferProducer([base]() -> sp { + Return> transResult = + base->getGraphicBufferProducer(); + return transResult.isOk() ? + static_cast>(transResult) : + nullptr; + }())} { } -std::shared_ptr - Codec2Client::InputSurface::getConfigurable() const { - Return> transResult = base()->getConfigurable(); - if (!transResult.isOk()) { - ALOGW("getConfigurable -- transaction failed."); - return nullptr; - } - if (!static_cast>(transResult)) { - ALOGW("getConfigurable -- null pointer."); - return nullptr; - } - return std::make_shared(transResult); -} - -const sp& +sp Codec2Client::InputSurface::getGraphicBufferProducer() const { return mGraphicBufferProducer; } -const sp& Codec2Client::InputSurface::getHalInterface() const { +sp Codec2Client::InputSurface::getHalInterface() const { return mBase; } // Codec2Client::InputSurfaceConnection - -Codec2Client::InputSurfaceConnection::Base* - Codec2Client::InputSurfaceConnection::base() const { - return static_cast(mBase.get()); -} - Codec2Client::InputSurfaceConnection::InputSurfaceConnection( - const sp& base) : - mBase(base) { + const sp& base) + : Configurable{ + [base]() -> sp { + Return> transResult = + base->getConfigurable(); + return transResult.isOk() ? + static_cast>(transResult) : + nullptr; + }() + }, + mBase{base} { } c2_status_t Codec2Client::InputSurfaceConnection::disconnect() { - Return transResult = base()->disconnect(); + Return transResult = mBase->disconnect(); return static_cast(static_cast(transResult)); } diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index c48bf0cd51..f320ef3edc 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef CODEC2_HIDL_CLIENT_H_ -#define CODEC2_HIDL_CLIENT_H_ +#ifndef CODEC2_HIDL_CLIENT_H +#define CODEC2_HIDL_CLIENT_H #include #include @@ -70,8 +70,8 @@ namespace media { namespace c2 { namespace V1_0 { struct IConfigurable; -struct IComponentInterface; struct IComponent; +struct IComponentInterface; struct IComponentStore; struct IInputSurface; struct IInputSurfaceConnection; @@ -146,10 +146,8 @@ struct Codec2ConfigurableClient { Codec2ConfigurableClient(const sp& base); protected: - C2String mName; sp mBase; - - Base* base() const; + C2String mName; friend struct Codec2Client; }; @@ -162,17 +160,17 @@ struct Codec2Client : public Codec2ConfigurableClient { typedef Codec2ConfigurableClient Configurable; - typedef Configurable Interface; // These two types may diverge in the future. - struct Component; + struct Interface; + struct InputSurface; struct InputSurfaceConnection; typedef Codec2Client Store; - std::string getInstanceName() const { return mInstanceName; } + std::string getServiceName() const { return mServiceName; } c2_status_t createComponent( const C2String& name, @@ -195,7 +193,7 @@ struct Codec2Client : public Codec2ConfigurableClient { std::shared_ptr getParamReflector(); static std::shared_ptr CreateFromService( - const char* instanceName, + const char* serviceName, bool waitForService = true); // Try to create a component with a given name from all known @@ -218,10 +216,10 @@ struct Codec2Client : public Codec2ConfigurableClient { static std::shared_ptr CreateInputSurface(); // base cannot be null. - Codec2Client(const sp& base, std::string instanceName); + Codec2Client(const sp& base, std::string serviceName); protected: - Base* base() const; + sp mBase; // Finds the first store where the predicate returns OK, and returns the last // predicate result. Uses key to remember the last store found, and if cached, @@ -232,7 +230,7 @@ protected: mutable std::mutex mMutex; mutable bool mListed; - std::string mInstanceName; + std::string mServiceName; mutable std::vector mTraitsList; mutable std::vector>> mAliasesBuffer; @@ -241,6 +239,16 @@ protected: mHostPoolManager; }; +struct Codec2Client::Interface : public Codec2Client::Configurable { + + typedef ::android::hardware::media::c2::V1_0::IComponentInterface Base; + + Interface(const sp& base); + +protected: + sp mBase; +}; + struct Codec2Client::Listener { // This is called when the component produces some output. @@ -277,28 +285,12 @@ struct Codec2Client::Listener { virtual void onInputBufferDone( const std::shared_ptr& buffer) = 0; - // This structure is used for transporting onFramesRendered() event to the - // client in the case where the output buffers are obtained from a - // bufferqueue. - struct RenderedFrame { - // The id of the bufferqueue. - uint64_t bufferQueueId; - // The slot of the buffer inside the bufferqueue. - int32_t slotId; - // The timestamp. - int64_t timestampNs; - - RenderedFrame(uint64_t bufferQueueId, int32_t slotId, - int64_t timestampNs) - : bufferQueueId(bufferQueueId), - slotId(slotId), - timestampNs(timestampNs) {} - RenderedFrame(const RenderedFrame&) = default; - }; - - // This is called when the component becomes aware of frames being rendered. - virtual void onFramesRendered( - const std::vector& renderedFrames) = 0; + // This is called when the component becomes aware of a frame being + // rendered. + virtual void onFrameRendered( + uint64_t bufferQueueId, + int32_t slotId, + int64_t timestampNs) = 0; virtual ~Listener(); @@ -373,9 +365,15 @@ struct Codec2Client::Component : public Codec2Client::Configurable { const QueueBufferInput& input, QueueBufferOutput* output); + // Connect to a given InputSurface. + c2_status_t connectToInputSurface( + const std::shared_ptr& inputSurface, + std::shared_ptr* connection); + c2_status_t connectToOmxInputSurface( const sp& producer, - const sp& source); + const sp& source, + std::shared_ptr* connection); c2_status_t disconnectFromInputSurface(); @@ -385,7 +383,7 @@ struct Codec2Client::Component : public Codec2Client::Configurable { ~Component(); protected: - Base* base() const; + sp mBase; // Mutex for mInputBuffers and mInputBufferCount. mutable std::mutex mInputBuffersMutex; @@ -428,7 +426,7 @@ protected: }; -struct Codec2Client::InputSurface { +struct Codec2Client::InputSurface : public Codec2Client::Configurable { public: typedef ::android::hardware::media::c2::V1_0::IInputSurface Base; @@ -439,22 +437,15 @@ public: typedef ::android::IGraphicBufferProducer IGraphicBufferProducer; - c2_status_t connectToComponent( - const std::shared_ptr& component, - std::shared_ptr* connection); - - std::shared_ptr getConfigurable() const; - - const sp& getGraphicBufferProducer() const; + sp getGraphicBufferProducer() const; // Return the underlying IInputSurface. - const sp& getHalInterface() const; + sp getHalInterface() const; // base cannot be null. InputSurface(const sp& base); protected: - Base* base() const; sp mBase; sp mGraphicBufferProducer; @@ -463,7 +454,7 @@ protected: friend struct Component; }; -struct Codec2Client::InputSurfaceConnection { +struct Codec2Client::InputSurfaceConnection : public Codec2Client::Configurable { typedef ::android::hardware::media::c2::V1_0::IInputSurfaceConnection Base; @@ -473,7 +464,6 @@ struct Codec2Client::InputSurfaceConnection { InputSurfaceConnection(const sp& base); protected: - Base* base() const; sp mBase; friend struct Codec2Client::InputSurface; @@ -481,5 +471,5 @@ protected: } // namespace android -#endif // CODEC2_HIDL_CLIENT_H_ +#endif // CODEC2_HIDL_CLIENT_H diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 852d6d6b12..2527b00771 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -143,8 +143,7 @@ public: if (mConnection != nullptr) { return ALREADY_EXISTS; } - return toStatusT(mSurface->connectToComponent(comp, &mConnection), - C2_OPERATION_InputSurface_connectToComponent); + return toStatusT(comp->connectToInputSurface(mSurface, &mConnection)); } void disconnect() override { @@ -162,7 +161,7 @@ public: status_t signalEndOfInputStream() override { C2InputSurfaceEosTuning eos(true); std::vector> failures; - c2_status_t err = mSurface->getConfigurable()->config({&eos}, C2_MAY_BLOCK, &failures); + c2_status_t err = mSurface->config({&eos}, C2_MAY_BLOCK, &failures); if (err != C2_OK) { return UNKNOWN_ERROR; } @@ -495,10 +494,13 @@ struct CCodec::ClientListener : public Codec2Client::Listener { codec->mCallback->onError(DEAD_OBJECT, ACTION_CODE_FATAL); } - virtual void onFramesRendered( - const std::vector& renderedFrames) override { - // TODO - (void)renderedFrames; + virtual void onFrameRendered(uint64_t bufferQueueId, + int32_t slotId, + int64_t timestampNs) override { + // TODO: implement + (void)bufferQueueId; + (void)slotId; + (void)timestampNs; } virtual void onInputBufferDone( @@ -599,7 +601,7 @@ void CCodec::allocate(const sp &codecInfo) { // set up preferred component store to access vendor store parameters client = Codec2Client::CreateFromService("default", false); if (client) { - ALOGI("setting up '%s' as default (vendor) store", client->getInstanceName().c_str()); + ALOGI("setting up '%s' as default (vendor) store", client->getServiceName().c_str()); SetPreferredCodec2ComponentStore( std::make_shared(client)); } @@ -956,16 +958,18 @@ void CCodec::createInputSurface() { std::shared_ptr persistentSurface(CreateInputSurface()); if (persistentSurface->getHidlTarget()) { - sp inputSurface = IInputSurface::castFrom( + sp hidlInputSurface = IInputSurface::castFrom( persistentSurface->getHidlTarget()); - if (!inputSurface) { + if (!hidlInputSurface) { ALOGE("Corrupted input surface"); mCallback->onInputSurfaceCreationFailed(UNKNOWN_ERROR); return; } + std::shared_ptr inputSurface = + std::make_shared(hidlInputSurface); err = setupInputSurface(std::make_shared( - std::make_shared(inputSurface))); - bufferProducer = new H2BGraphicBufferProducer(inputSurface); + inputSurface)); + bufferProducer = inputSurface->getGraphicBufferProducer(); } else { int32_t width = 0; (void)outputFormat->findInt32("width", &width); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index b529cbc3d5..577aff3623 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1645,6 +1645,7 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spinput.ordinal.customOrdinal = timeUs; work->input.buffers.clear(); work->input.flags = C2FrameData::FLAG_END_OF_STREAM; + work->worklets.emplace_back(new C2Worklet); items.clear(); items.push_back(std::move(work)); -- GitLab From 1f213365b2b03d508f690cc870db8725ca0a4536 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 24 Jan 2019 06:59:16 -0800 Subject: [PATCH 0836/1530] Upstream changes from hardware/google/av This includes changes up to commit c3f6c282c22de48e89fe1f364c37b13be7498404 Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 112362730 Change-Id: Idf91ba6d3b71a724292cc3ae533307194678ea83 --- media/codec2/components/avc/C2SoftAvcEnc.cpp | 2 +- media/codec2/sfplugin/CCodec.cpp | 34 ++++- media/codec2/sfplugin/CCodecBufferChannel.cpp | 97 ++++++++----- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 4 +- media/codec2/sfplugin/utils/Android.bp | 4 + .../sfplugin/utils/Codec2BufferUtils.cpp | 131 +++++++++++++++++- .../codec2/sfplugin/utils/Codec2BufferUtils.h | 25 ++++ media/codec2/vndk/platform/C2BqBuffer.cpp | 2 +- 8 files changed, 255 insertions(+), 44 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index cf06623795..6ddb9ff735 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -216,7 +216,7 @@ public: }; uint64_t mbs = uint64_t((size.v.width + 15) / 16) * ((size.v.height + 15) / 16); - float mbsPerSec = float(mbs) / frameRate.v.value; + float mbsPerSec = float(mbs) * frameRate.v.value; // Check if the supplied level meets the MB / bitrate requirements. If // not, update the level with the lowest level meeting the requirements. diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 2527b00771..10263dedfb 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -1024,7 +1024,7 @@ status_t CCodec::setupInputSurface(const std::shared_ptr &s ALOGD("ISConfig: no configuration"); } - return surface->start(); + return OK; } void CCodec::initiateSetInputSurface(const sp &surface) { @@ -1111,12 +1111,20 @@ void CCodec::start() { } sp inputFormat; sp outputFormat; + status_t err2 = OK; { Mutexed::Locked config(mConfig); inputFormat = config->mInputFormat; outputFormat = config->mOutputFormat; + if (config->mInputSurface) { + err2 = config->mInputSurface->start(); + } } - status_t err2 = mChannel->start(inputFormat, outputFormat); + if (err2 != OK) { + mCallback->onError(err2, ACTION_CODE_FATAL); + return; + } + err2 = mChannel->start(inputFormat, outputFormat); if (err2 != OK) { mCallback->onError(err2, ACTION_CODE_FATAL); return; @@ -1190,6 +1198,13 @@ void CCodec::stop() { mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); } + { + Mutexed::Locked config(mConfig); + if (config->mInputSurface) { + config->mInputSurface->disconnect(); + config->mInputSurface = nullptr; + } + } { Mutexed::Locked state(mState); if (state->get() == STOPPING) { @@ -1200,6 +1215,7 @@ void CCodec::stop() { } void CCodec::initiateRelease(bool sendCallback /* = true */) { + bool clearInputSurfaceIfNeeded = false; { Mutexed::Locked state(mState); if (state->get() == RELEASED || state->get() == RELEASING) { @@ -1221,9 +1237,23 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) { } return; } + if (state->get() == STARTING + || state->get() == RUNNING + || state->get() == STOPPING) { + // Input surface may have been started, so clean up is needed. + clearInputSurfaceIfNeeded = true; + } state->set(RELEASING); } + if (clearInputSurfaceIfNeeded) { + Mutexed::Locked config(mConfig); + if (config->mInputSurface) { + config->mInputSurface->disconnect(); + config->mInputSurface = nullptr; + } + } + mChannel->stop(); // thiz holds strong ref to this while the thread is running. sp thiz(this); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 577aff3623..1ae7bd704d 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -128,7 +128,9 @@ public: * and released successfully. */ virtual bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) = 0; + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) = 0; /** * Release the buffer that is no longer used by the codec process. Return @@ -455,13 +457,18 @@ public: * \return true if the buffer is successfully released from a slot * false otherwise */ - bool releaseSlot(const sp &buffer, std::shared_ptr *c2buffer) { + bool releaseSlot( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { sp clientBuffer; size_t index = mBuffers.size(); for (size_t i = 0; i < mBuffers.size(); ++i) { if (mBuffers[i].clientBuffer == buffer) { clientBuffer = mBuffers[i].clientBuffer; - mBuffers[i].clientBuffer.clear(); + if (release) { + mBuffers[i].clientBuffer.clear(); + } index = i; break; } @@ -470,8 +477,11 @@ public: ALOGV("[%s] %s: No matching buffer found", mName, __func__); return false; } - std::shared_ptr result = clientBuffer->asC2Buffer(); - mBuffers[index].compBuffer = result; + std::shared_ptr result = mBuffers[index].compBuffer.lock(); + if (!result) { + result = clientBuffer->asC2Buffer(); + mBuffers[index].compBuffer = result; + } if (c2buffer) { *c2buffer = result; } @@ -485,8 +495,8 @@ public: if (!compBuffer || compBuffer != c2buffer) { continue; } - mBuffers[i].clientBuffer = nullptr; mBuffers[i].compBuffer.reset(); + ALOGV("[%s] codec released buffer #%zu", mName, i); return true; } ALOGV("[%s] codec released an unknown buffer", mName); @@ -597,7 +607,10 @@ public: * \return true if the buffer is successfully returned * false otherwise */ - bool returnBuffer(const sp &buffer, std::shared_ptr *c2buffer) { + bool returnBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { sp clientBuffer; size_t index = mBuffers.size(); for (size_t i = 0; i < mBuffers.size(); ++i) { @@ -606,7 +619,9 @@ public: ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", mName, i); } clientBuffer = mBuffers[i].clientBuffer; - mBuffers[i].ownedByClient = false; + if (release) { + mBuffers[i].ownedByClient = false; + } index = i; break; } @@ -616,8 +631,11 @@ public: return false; } ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index); - std::shared_ptr result = clientBuffer->asC2Buffer(); - mBuffers[index].compBuffer = result; + std::shared_ptr result = mBuffers[index].compBuffer.lock(); + if (!result) { + result = clientBuffer->asC2Buffer(); + mBuffers[index].compBuffer = result; + } if (c2buffer) { *c2buffer = result; } @@ -636,9 +654,9 @@ public: // This should not happen. ALOGD("[%s] codec released a buffer owned by client " "(index %zu)", mName, i); - mBuffers[i].ownedByClient = false; } mBuffers[i].compBuffer.reset(); + ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i); return true; } } @@ -723,8 +741,10 @@ public: } bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.returnBuffer(buffer, c2buffer); + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override { + return mImpl.returnBuffer(buffer, c2buffer, release); } bool expireComponentBuffer( @@ -765,8 +785,10 @@ public: } bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.releaseSlot(buffer, c2buffer); + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override { + return mImpl.releaseSlot(buffer, c2buffer, release); } bool expireComponentBuffer( @@ -801,7 +823,7 @@ public: return std::move(array); } - virtual sp alloc(size_t size) const { + virtual sp alloc(size_t size) { C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; std::shared_ptr block; @@ -850,11 +872,12 @@ public: ~EncryptedLinearInputBuffers() override { } - sp alloc(size_t size) const override { + sp alloc(size_t size) override { sp memory; - for (const Entry &entry : mMemoryVector) { - if (entry.block.expired()) { - memory = entry.memory; + size_t slot = 0; + for (; slot < mMemoryVector.size(); ++slot) { + if (mMemoryVector[slot].block.expired()) { + memory = mMemoryVector[slot].memory; break; } } @@ -864,10 +887,11 @@ public: std::shared_ptr block; c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block); - if (err != C2_OK) { + if (err != C2_OK || block == nullptr) { return nullptr; } + mMemoryVector[slot].block = block; return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum); } @@ -907,8 +931,10 @@ public: } bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.releaseSlot(buffer, c2buffer); + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override { + return mImpl.releaseSlot(buffer, c2buffer, release); } bool expireComponentBuffer( @@ -971,14 +997,17 @@ public: } bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.releaseSlot(buffer, c2buffer); + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override { + return mImpl.releaseSlot(buffer, c2buffer, release); } bool expireComponentBuffer( const std::shared_ptr &c2buffer) override { return mImpl.expireComponentBuffer(c2buffer); } + void flush() override { // This is no-op by default unless we're in array mode where we need to keep // track of the flushed work. @@ -1016,7 +1045,7 @@ public: } bool releaseBuffer( - const sp &, std::shared_ptr *) override { + const sp &, std::shared_ptr *, bool) override { return false; } @@ -1111,7 +1140,7 @@ public: bool releaseBuffer( const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.returnBuffer(buffer, c2buffer); + return mImpl.returnBuffer(buffer, c2buffer, true); } void flush(const std::list> &flushedWork) override { @@ -1190,8 +1219,9 @@ public: } bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.releaseSlot(buffer, c2buffer); + const sp &buffer, + std::shared_ptr *c2buffer) override { + return mImpl.releaseSlot(buffer, c2buffer, true); } void flush( @@ -1615,7 +1645,7 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spsize() > 0u) { Mutexed>::Locked buffers(mInputBuffers); std::shared_ptr c2buffer; - if (!(*buffers)->releaseBuffer(buffer, &c2buffer)) { + if (!(*buffers)->releaseBuffer(buffer, &c2buffer, false)) { return -ENOENT; } work->input.buffers.push_back(c2buffer); @@ -1653,6 +1683,10 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const sponWorkQueued(eos); + + Mutexed>::Locked buffers(mInputBuffers); + bool released = (*buffers)->releaseBuffer(buffer, nullptr, true); + ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not "); } feedInputBufferIfAvailableInternal(); @@ -1981,7 +2015,7 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) bool released = false; { Mutexed>::Locked buffers(mInputBuffers); - if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) { + if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) { buffers.unlock(); released = true; mAvailablePipelineCapacity.freeInputSlots(1, "discardBuffer"); @@ -2446,7 +2480,6 @@ void CCodecBufferChannel::stop() { mSync.stop(); mFirstValidFrameIndex = mFrameIndex.load(std::memory_order_relaxed); if (mInputSurface != nullptr) { - mInputSurface->disconnect(); mInputSurface.reset(); } } diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index a8cc62dab7..5f0dd0b637 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -299,7 +300,6 @@ void buildOmxInfo(const MediaCodecsXmlParser& parser, // OMX components don't have aliases for (const MediaCodecsXmlParser::Type &type : properties.typeMap) { const std::string &mediaType = type.first; - std::unique_ptr caps = info->addMediaType(mediaType.c_str()); const MediaCodecsXmlParser::AttributeMap &attrMap = type.second; @@ -376,7 +376,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { } bool surfaceTest(Codec2Client::CreateInputSurface()); - if (option == 0 || !surfaceTest) { + if (option == 0 || (option != 4 && !surfaceTest)) { buildOmxInfo(parser, writer); } diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp index eb6c3e917e..8c8f025aba 100644 --- a/media/codec2/sfplugin/utils/Android.bp +++ b/media/codec2/sfplugin/utils/Android.bp @@ -26,6 +26,10 @@ cc_library_shared { "libutils", ], + static_libs: [ + "libyuv_static", + ], + sanitize: { cfi: true, misc_undefined: [ diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp index 84d22a377a..6b8663fedc 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "Codec2BufferUtils" #include +#include + #include #include @@ -62,14 +64,10 @@ struct MemCopier { */ template static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBase) { - // TODO: more efficient copying --- e.g. one row at a time, copying - // interleaved planes together, etc. + // TODO: more efficient copying --- e.g. copy interleaved planes together, etc. const C2PlanarLayout &layout = view.layout(); const size_t bpp = divUp(img->mBitDepthAllocated, 8u); - if (view.width() != img->mWidth - || view.height() != img->mHeight) { - return BAD_VALUE; - } + for (uint32_t i = 0; i < layout.numPlanes; ++i) { typename std::conditional::type *imgRow = imgBase + img->mPlane[i].mOffset; @@ -120,10 +118,72 @@ static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBa } // namespace status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) { + if (view.width() != img->mWidth || view.height() != img->mHeight) { + return BAD_VALUE; + } + if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) { + // Take shortcuts to use libyuv functions between NV12 and I420 conversion. + const uint8_t* src_y = view.data()[0]; + const uint8_t* src_u = view.data()[1]; + const uint8_t* src_v = view.data()[2]; + int32_t src_stride_y = view.layout().planes[0].rowInc; + int32_t src_stride_u = view.layout().planes[1].rowInc; + int32_t src_stride_v = view.layout().planes[2].rowInc; + uint8_t* dst_y = imgBase + img->mPlane[0].mOffset; + uint8_t* dst_u = imgBase + img->mPlane[1].mOffset; + uint8_t* dst_v = imgBase + img->mPlane[2].mOffset; + int32_t dst_stride_y = img->mPlane[0].mRowInc; + int32_t dst_stride_u = img->mPlane[1].mRowInc; + int32_t dst_stride_v = img->mPlane[2].mRowInc; + if (IsNV12(view) && IsI420(img)) { + if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y, + dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(), + view.height())) { + return OK; + } + } else { + if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, + dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(), + view.height())) { + return OK; + } + } + } return _ImageCopy(view, img, imgBase); } status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) { + if (view.width() != img->mWidth || view.height() != img->mHeight) { + return BAD_VALUE; + } + if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) { + // Take shortcuts to use libyuv functions between NV12 and I420 conversion. + const uint8_t* src_y = imgBase + img->mPlane[0].mOffset; + const uint8_t* src_u = imgBase + img->mPlane[1].mOffset; + const uint8_t* src_v = imgBase + img->mPlane[2].mOffset; + int32_t src_stride_y = img->mPlane[0].mRowInc; + int32_t src_stride_u = img->mPlane[1].mRowInc; + int32_t src_stride_v = img->mPlane[2].mRowInc; + uint8_t* dst_y = view.data()[0]; + uint8_t* dst_u = view.data()[1]; + uint8_t* dst_v = view.data()[2]; + int32_t dst_stride_y = view.layout().planes[0].rowInc; + int32_t dst_stride_u = view.layout().planes[1].rowInc; + int32_t dst_stride_v = view.layout().planes[2].rowInc; + if (IsNV12(img) && IsI420(view)) { + if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y, + dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(), + view.height())) { + return OK; + } + } else { + if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, + dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(), + view.height())) { + return OK; + } + } + } return _ImageCopy(view, img, imgBase); } @@ -151,6 +211,65 @@ bool IsYUV420(const C2GraphicView &view) { && layout.planes[layout.PLANE_V].rowSampling == 2); } +bool IsNV12(const C2GraphicView &view) { + if (!IsYUV420(view)) { + return false; + } + const C2PlanarLayout &layout = view.layout(); + return (layout.rootPlanes == 2 + && layout.planes[layout.PLANE_U].colInc == 2 + && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_U + && layout.planes[layout.PLANE_U].offset == 0 + && layout.planes[layout.PLANE_V].colInc == 2 + && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_U + && layout.planes[layout.PLANE_V].offset == 1); +} + +bool IsI420(const C2GraphicView &view) { + if (!IsYUV420(view)) { + return false; + } + const C2PlanarLayout &layout = view.layout(); + return (layout.rootPlanes == 3 + && layout.planes[layout.PLANE_U].colInc == 1 + && layout.planes[layout.PLANE_U].rootIx == layout.PLANE_U + && layout.planes[layout.PLANE_U].offset == 0 + && layout.planes[layout.PLANE_V].colInc == 1 + && layout.planes[layout.PLANE_V].rootIx == layout.PLANE_V + && layout.planes[layout.PLANE_V].offset == 0); +} + +bool IsYUV420(const MediaImage2 *img) { + return (img->mType == MediaImage2::MEDIA_IMAGE_TYPE_YUV + && img->mNumPlanes == 3 + && img->mBitDepth == 8 + && img->mBitDepthAllocated == 8 + && img->mPlane[0].mHorizSubsampling == 1 + && img->mPlane[0].mVertSubsampling == 1 + && img->mPlane[1].mHorizSubsampling == 2 + && img->mPlane[1].mVertSubsampling == 2 + && img->mPlane[2].mHorizSubsampling == 2 + && img->mPlane[2].mVertSubsampling == 2); +} + +bool IsNV12(const MediaImage2 *img) { + if (!IsYUV420(img)) { + return false; + } + return (img->mPlane[1].mColInc == 2 + && img->mPlane[2].mColInc == 2 + && (img->mPlane[2].mOffset - img->mPlane[1].mOffset == 1)); +} + +bool IsI420(const MediaImage2 *img) { + if (!IsYUV420(img)) { + return false; + } + return (img->mPlane[1].mColInc == 1 + && img->mPlane[2].mColInc == 1 + && img->mPlane[2].mOffset > img->mPlane[1].mOffset); +} + MediaImage2 CreateYUV420PlanarMediaImage2( uint32_t width, uint32_t height, uint32_t stride, uint32_t vstride) { return MediaImage2 { diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.h b/media/codec2/sfplugin/utils/Codec2BufferUtils.h index eaf6776a2a..afadf006c8 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.h +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.h @@ -90,6 +90,31 @@ status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage */ bool IsYUV420(const C2GraphicView &view); +/** + * Returns true iff a view has a NV12 layout. + */ +bool IsNV12(const C2GraphicView &view); + +/** + * Returns true iff a view has a I420 layout. + */ +bool IsI420(const C2GraphicView &view); + +/** + * Returns true iff a MediaImage2 has a YUV 420 888 layout. + */ +bool IsYUV420(const MediaImage2 *img); + +/** + * Returns true iff a MediaImage2 has a NV12 layout. + */ +bool IsNV12(const MediaImage2 *img); + +/** + * Returns true iff a MediaImage2 has a I420 layout. + */ +bool IsI420(const MediaImage2 *img); + /** * A raw memory block to use for internal buffers. * diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 6e71b98f1d..7bf3d64c3c 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -192,7 +192,7 @@ private: int slot; ALOGV("tries to dequeue buffer"); Return transStatus = mProducer->dequeueBuffer( - width, height, pixelFormat, androidUsage.asGrallocUsage(), true, + width, height, pixelFormat, androidUsage.asGrallocUsage(), false, [&status, &slot, &fence]( int32_t tStatus, int32_t tSlot, hidl_handle const& tFence, HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) { -- GitLab From ee1f655728b31b4c74c4f211b96bfd117daaeacb Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 29 Jan 2019 18:22:02 -0800 Subject: [PATCH 0837/1530] audio policy: fix call audio hearing aid Remove assert added by mistake on a valid configuration. Bug: 123551930 Test: place a call with an hearing aid device. Change-Id: Ib279d1bea460026da45ffeb5a69910ac0c3d3c4f --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index cf9c298298..46f51e5e4a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -555,7 +555,6 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs); } else { // create RX path audio patch mCallRxPatch = createTelephonyPatch(true /*isRx*/, rxDevices.itemAt(0), delayMs); - ALOG_ASSERT(createTxPatch, "No Tx Patch will be created, nor legacy routing done"); } if (createTxPatch) { // create TX path audio patch mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs); -- GitLab From db3fa5f9ff069697b70f2a702177f1db83a6d5b7 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 25 Jan 2019 22:56:56 -0800 Subject: [PATCH 0838/1530] Throw exception on mismatched system vs vendor When the system partition is a later version than vendor, new MediaDrm APIs will not have HAL implementations. In this case throw java.lang.UnsupportedOperationException. bug:110701831 bug:123375769 test: cts media test cases, gts media tests Change-Id: Ib631bf4d4d245d857e61bd3fe0e5808e430a034d --- drm/libmediadrm/DrmHal.cpp | 72 ++++++++++--------- drm/libmediadrm/IDrm.cpp | 14 ++-- media/libmedia/include/media/DrmHal.h | 16 +++-- media/libmedia/include/media/IDrm.h | 7 +- .../nuplayer/NuPlayerDrm.cpp | 7 +- media/ndk/NdkMediaDrm.cpp | 5 +- 6 files changed, 70 insertions(+), 51 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 480c7cda0a..84713c1509 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -586,51 +586,57 @@ Return DrmHal::sendSessionLostState( return Void(); } -bool DrmHal::matchMimeTypeAndSecurityLevel(sp &factory, - const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level) { - if (mimeType == "") { - return true; - } else if (!factory->isContentTypeSupported(mimeType.string())) { - return false; +status_t DrmHal::matchMimeTypeAndSecurityLevel(const sp &factory, + const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported) { + *isSupported = false; + + // handle default value cases + if (level == DrmPlugin::kSecurityLevelUnknown) { + if (mimeType == "") { + // isCryptoSchemeSupported(uuid) + *isSupported = true; + } else { + // isCryptoSchemeSupported(uuid, mimeType) + *isSupported = factory->isContentTypeSupported(mimeType.string()); + } + return OK; + } else if (mimeType == "") { + return BAD_VALUE; } - if (level == DrmPlugin::kSecurityLevelUnknown) { - return true; + sp factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory); + if (factoryV1_2 == NULL) { + return ERROR_UNSUPPORTED; } else { - sp factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory); - if (factoryV1_2 == NULL) { - return true; - } else if (factoryV1_2->isCryptoSchemeSupported_1_2(uuid, - mimeType.string(), toHidlSecurityLevel(level))) { - return true; - } + *isSupported = factoryV1_2->isCryptoSchemeSupported_1_2(uuid, + mimeType.string(), toHidlSecurityLevel(level)); + return OK; } - return false; } -bool DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level) { +status_t DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported) { Mutex::Autolock autoLock(mLock); - - for (size_t i = 0; i < mFactories.size(); i++) { - sp factory = mFactories[i]; - if (factory->isCryptoSchemeSupported(uuid)) { - if (matchMimeTypeAndSecurityLevel(factory, uuid, mimeType, level)) { - return true; - } + *isSupported = false; + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + if (mFactories[i]->isCryptoSchemeSupported(uuid)) { + return matchMimeTypeAndSecurityLevel(mFactories[i], + uuid, mimeType, level, isSupported); } } - return false; + return OK; } status_t DrmHal::createPlugin(const uint8_t uuid[16], const String8& appPackageName) { Mutex::Autolock autoLock(mLock); - for (size_t i = mFactories.size() - 1; i >= 0; i--) { + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { if (mFactories[i]->isCryptoSchemeSupported(uuid)) { auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); if (plugin != NULL) { @@ -1213,7 +1219,7 @@ status_t DrmHal::getOfflineLicenseKeySetIds(List> &keySetIds) co } if (mPluginV1_2 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; + return ERROR_UNSUPPORTED; } status_t err = UNKNOWN_ERROR; @@ -1238,7 +1244,7 @@ status_t DrmHal::removeOfflineLicense(Vector const &keySetId) { } if (mPluginV1_2 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; + return ERROR_UNSUPPORTED; } Return status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId)); @@ -1254,7 +1260,7 @@ status_t DrmHal::getOfflineLicenseState(Vector const &keySetId, } if (mPluginV1_2 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; + return ERROR_UNSUPPORTED; } *licenseState = DrmPlugin::kOfflineLicenseStateUnknown; diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp index 0f343151c8..51274d16c1 100644 --- a/drm/libmediadrm/IDrm.cpp +++ b/drm/libmediadrm/IDrm.cpp @@ -83,8 +83,8 @@ struct BpDrm : public BpInterface { return reply.readInt32(); } - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType, - DrmPlugin::SecurityLevel level) { + virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType, + DrmPlugin::SecurityLevel level, bool *isSupported) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); data.write(uuid, 16); @@ -94,10 +94,11 @@ struct BpDrm : public BpInterface { status_t status = remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); if (status != OK) { ALOGE("isCryptoSchemeSupported: binder call failed: %d", status); - return false; + return status; } + *isSupported = static_cast(reply.readInt32()); - return reply.readInt32() != 0; + return reply.readInt32(); } virtual status_t createPlugin(const uint8_t uuid[16], @@ -773,7 +774,10 @@ status_t BnDrm::onTransact( String8 mimeType = data.readString8(); DrmPlugin::SecurityLevel level = static_cast(data.readInt32()); - reply->writeInt32(isCryptoSchemeSupported(uuid, mimeType, level)); + bool isSupported = false; + status_t result = isCryptoSchemeSupported(uuid, mimeType, level, &isSupported); + reply->writeInt32(isSupported); + reply->writeInt32(result); return OK; } diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h index 7be5cf2515..a630bfd431 100644 --- a/media/libmedia/include/media/DrmHal.h +++ b/media/libmedia/include/media/DrmHal.h @@ -63,9 +63,10 @@ struct DrmHal : public BnDrm, virtual status_t initCheck() const; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], - const String8& mimeType, - DrmPlugin::SecurityLevel level); + virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], + const String8& mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported); virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName); @@ -226,10 +227,11 @@ private: status_t getPropertyStringInternal(String8 const &name, String8 &value) const; status_t getPropertyByteArrayInternal(String8 const &name, Vector &value) const; - bool matchMimeTypeAndSecurityLevel(sp &factory, - const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level); + status_t matchMimeTypeAndSecurityLevel(const sp &factory, + const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported); DISALLOW_EVIL_CONSTRUCTORS(DrmHal); }; diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h index a32756fd27..fbe80c6635 100644 --- a/media/libmedia/include/media/IDrm.h +++ b/media/libmedia/include/media/IDrm.h @@ -34,9 +34,10 @@ struct IDrm : public IInterface { virtual status_t initCheck() const = 0; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel securityLevel) = 0; + virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel securityLevel, + bool *result) = 0; virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName) = 0; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp index 67a0f1e611..2d0c9e0e14 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp @@ -159,9 +159,12 @@ Vector NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t pss if (drm != NULL) { for (size_t i = 0; i < psshDRMs.size(); i++) { DrmUUID uuid = psshDRMs[i]; - if (drm->isCryptoSchemeSupported(uuid.ptr(), String8(), - DrmPlugin::kSecurityLevelUnknown)) + bool isSupported = false; + status = drm->isCryptoSchemeSupported(uuid.ptr(), String8(), + DrmPlugin::kSecurityLevelUnknown, &isSupported); + if (status == OK && isSupported) { supportedDRMs.add(uuid); + } } drm.clear(); diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp index 9082f620f3..2deb1a49bb 100644 --- a/media/ndk/NdkMediaDrm.cpp +++ b/media/ndk/NdkMediaDrm.cpp @@ -274,7 +274,10 @@ bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeTy } String8 mimeStr = mimeType ? String8(mimeType) : String8(""); - return drm->isCryptoSchemeSupported(uuid, mimeStr, DrmPlugin::kSecurityLevelUnknown); + bool isSupported = false; + status_t status = drm->isCryptoSchemeSupported(uuid, mimeStr, + DrmPlugin::kSecurityLevelUnknown, &isSupported); + return (status == OK) && isSupported; } EXPORT -- GitLab From 696a6a144e36b1fcc94142ee48d186cc0cc139ee Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Mon, 15 Oct 2018 15:41:20 -0700 Subject: [PATCH 0839/1530] Add new lazy service target to clearkey Test: Reboot device and make sure clearkey HAL is not running. Run gts. Bug: 112386116 Change-Id: I8ef1c88b64cfb3a292abfd31b3dda82c6cd73ef9 --- drm/mediadrm/plugins/clearkey/hidl/Android.bp | 19 +++++-- ....hardware.drm@1.2-service-lazy.clearkey.rc | 14 ++++++ ...droid.hardware.drm@1.2-service.clearkey.rc | 6 +++ .../plugins/clearkey/hidl/service.cpp | 10 +--- .../plugins/clearkey/hidl/serviceLazy.cpp | 50 +++++++++++++++++++ 5 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc create mode 100644 drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp index b44a6c7099..e91e918b1d 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp +++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp @@ -14,8 +14,8 @@ // limitations under the License. // -cc_binary { - name: "android.hardware.drm@1.2-service.clearkey", +cc_defaults { + name: "clearkey_service_defaults", vendor: true, srcs: [ @@ -33,13 +33,11 @@ cc_binary { "MemoryFileSystem.cpp", "Session.cpp", "SessionLibrary.cpp", - "service.cpp", ], relative_install_path: "hw", cflags: ["-Wall", "-Werror"], - init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"], shared_libs: [ "android.hardware.drm@1.0", @@ -80,3 +78,16 @@ cc_library_static { }, srcs: ["protos/DeviceFiles.proto"], } +cc_binary { + name: "android.hardware.drm@1.2-service.clearkey", + defaults: ["clearkey_service_defaults"], + srcs: ["service.cpp"], + init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"], +} +cc_binary { + name: "android.hardware.drm@1.2-service-lazy.clearkey", + overrides: ["android.hardware.drm@1.2-service.clearkey"], + defaults: ["clearkey_service_defaults"], + srcs: ["serviceLazy.cpp"], + init_rc: ["android.hardware.drm@1.2-service-lazy.clearkey.rc"], +} diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc new file mode 100644 index 0000000000..9afd3d7979 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc @@ -0,0 +1,14 @@ +service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-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 + 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.2-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc index ac184f7105..5ba669dc50 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc @@ -1,4 +1,10 @@ service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-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 class hal user media group media mediadrm diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp index 4ca31f35c1..b39ea018e4 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "android.hardware.drm@1.2-service.clearkey" - #include #include #include #include +#include #include using ::android::hardware::configureRpcThreadpool; @@ -31,14 +30,7 @@ using android::hardware::drm::V1_2::IDrmFactory; using android::hardware::drm::V1_2::clearkey::CryptoFactory; using android::hardware::drm::V1_2::clearkey::DrmFactory; - int main(int /* argc */, char** /* argv */) { - ALOGD("android.hardware.drm@1.2-service.clearkey starting..."); - - // The DRM HAL may communicate to other vendor components via - // /dev/vndbinder - android::ProcessState::initWithDriver("/dev/vndbinder"); - sp drmFactory = new DrmFactory; sp cryptoFactory = new CryptoFactory; diff --git a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp new file mode 100644 index 0000000000..99fd883b16 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2019 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 +#include + +#include +#include +#include +#include + +using ::android::hardware::configureRpcThreadpool; +using ::android::hardware::joinRpcThreadpool; +using ::android::sp; + +using android::hardware::drm::V1_2::ICryptoFactory; +using android::hardware::drm::V1_2::IDrmFactory; +using android::hardware::drm::V1_2::clearkey::CryptoFactory; +using android::hardware::drm::V1_2::clearkey::DrmFactory; +using android::hardware::LazyServiceRegistrar; + +int main(int /* argc */, char** /* argv */) { + sp drmFactory = new DrmFactory; + sp cryptoFactory = new CryptoFactory; + + configureRpcThreadpool(8, true /* callerWillJoin */); + + // Setup hwbinder service + LazyServiceRegistrar serviceRegistrar; + + // Setup hwbinder service + CHECK_EQ(serviceRegistrar.registerService(drmFactory, "clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Factory HAL"; + CHECK_EQ(serviceRegistrar.registerService(cryptoFactory, "clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Crypto HAL"; + + joinRpcThreadpool(); +} -- GitLab From d648399ddf41c4017bb04ecf45d160a4fcfa6611 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Sat, 22 Dec 2018 18:11:34 -0800 Subject: [PATCH 0840/1530] Add memfd related syscalls to allowed list Required for migration of ashmem to memfd. Bug: 113362644 Change-Id: I5c63ff130f67481deed5c8d975830463716c397c Signed-off-by: Joel Fernandes --- services/mediacodec/seccomp_policy/mediacodec-arm.policy | 2 ++ services/mediacodec/seccomp_policy/mediaswcodec-arm.policy | 2 ++ services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy | 2 ++ 3 files changed, 6 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy index edf4dabc4d..0aa5acc2a7 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy @@ -13,6 +13,8 @@ dup: 1 ppoll: 1 mmap2: 1 getrandom: 1 +memfd_create: 1 +ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail # parser support for '<' is in this needs to be modified to also prevent diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy index 588141a297..b9adbd9613 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy @@ -21,6 +21,8 @@ dup: 1 ppoll: 1 mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE mmap2: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +memfd_create: 1 +ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail # parser support for '<' is in this needs to be modified to also prevent diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy index 1bee1b5478..7abb432b23 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy @@ -26,6 +26,8 @@ getrlimit: 1 fstat: 1 newfstatat: 1 fstatfs: 1 +memfd_create: 1 +ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail # parser support for '<' is in this needs to be modified to also prevent -- GitLab From 837c360d19ab1a8c3fe79fd6db433e16d85af419 Mon Sep 17 00:00:00 2001 From: "wonhee48.seo" Date: Fri, 25 Jan 2019 17:55:12 +0900 Subject: [PATCH 0841/1530] media: max channel capability is extended to 6 bug:123388612 Change-Id: If934f9653b716741b177a6a2945ba95871193fa0 Signed-off-by: wonhee48.seo Signed-off-by: seonghwan.kim --- media/libstagefright/codecs/g711/dec/SoftG711.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp index c14983a894..877cb5ad73 100644 --- a/media/libstagefright/codecs/g711/dec/SoftG711.cpp +++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp @@ -23,6 +23,8 @@ #include #include +#define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */ + namespace android { template @@ -184,7 +186,7 @@ OMX_ERRORTYPE SoftG711::internalSetParameter( return OMX_ErrorUndefined; } - if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) { + if (pcmParams->nChannels < 1 || pcmParams->nChannels > MAX_CHANNEL_COUNT) { return OMX_ErrorUndefined; } -- GitLab From 308d50bf3b08f8197d7fed76a75f43d7398fc5a1 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 30 Jan 2019 11:57:16 -0800 Subject: [PATCH 0842/1530] audio policy: fix regression in USB audio Fix regression in audio device selection introduced by commit 4e4647077f Bug: 123500997 Test: play music over USB and BT A2DP Change-Id: I9a86e1a8f34f3b02f0dbb53b719884ab6c567265 --- .../audiopolicy/common/managerdefinitions/src/HwModule.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index 85d9bcec88..e0b233d18c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -332,10 +332,8 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices if (moduleDevice) { if (encodedFormat != AUDIO_FORMAT_DEFAULT) { moduleDevice->setEncodedFormat(encodedFormat); - if (moduleDevice->address() != devAddress) { - moduleDevice->setAddress(devAddress); - } } + moduleDevice->setAddress(devAddress); if (allowToCreate) { moduleDevice->attach(hwModule); } -- GitLab From 6897f229193507e3bb4deb1080ec58f39760768d Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 30 Jan 2019 13:29:24 -0800 Subject: [PATCH 0843/1530] CCodec: flush reorder stash at flush Bug: 123349335 Test: bug repro steps Change-Id: I6be571e2ca640b86eb2d227d92492bc8f0466ff5 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 6 ++++++ media/codec2/sfplugin/CCodecBufferChannel.h | 1 + 2 files changed, 7 insertions(+) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 8b21bd5e41..9616e47047 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1519,6 +1519,11 @@ void CCodecBufferChannel::ReorderStash::clear() { mKey = C2Config::ORDINAL; } +void CCodecBufferChannel::ReorderStash::flush() { + mPending.clear(); + mStash.clear(); +} + void CCodecBufferChannel::ReorderStash::setDepth(uint32_t depth) { mPending.splice(mPending.end(), mStash); mDepth = depth; @@ -2526,6 +2531,7 @@ void CCodecBufferChannel::flush(const std::list> &flushe Mutexed>::Locked buffers(mOutputBuffers); (*buffers)->flush(flushedWork); } + mReorderStash.lock()->flush(); } void CCodecBufferChannel::onWorkDone( diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index fd806b7dc0..ebc149144d 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -359,6 +359,7 @@ private: ReorderStash(); void clear(); + void flush(); void setDepth(uint32_t depth); void setKey(C2Config::ordinal_key_t key); bool pop(Entry *entry); -- GitLab From 08dd071c4a5f63ffe8ff0511a0068423d5bac090 Mon Sep 17 00:00:00 2001 From: Yuxiu Zhang Date: Fri, 18 Jan 2019 19:13:40 +0800 Subject: [PATCH 0844/1530] FLAC: resize raw decoder's buffer according to flac max buffer size Problem: Some flac can not be played because insufficient raw decoder's buffer size. Raw component's input and output buffer size are both 64K. But FLACExtractor's output(decoded pcm) buffer size may be greater than 64K. Solution: FLACExtractor pass the max buffer size to ACodec and resize raw component's input and output buffer size if possible. Bug: 122969286 Test: play flac audio which buffer size is greater than 64K normally Change-Id: Ic046d27926bdec35e3944aec2bd6718f197d42c1 --- media/extractors/flac/FLACExtractor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 84fbceebff..8854631332 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -581,6 +581,7 @@ 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); } @@ -667,7 +668,7 @@ FLACSource::FLACSource( : mDataSource(dataSource), mTrackMetadata(trackMetadata), mOutputFloat(outputFloat), - mParser(new FLACParser(mDataSource, outputFloat)), + mParser(new FLACParser(mDataSource, outputFloat, 0, mTrackMetadata)), mInitCheck(mParser->initCheck()), mStarted(false) { -- GitLab From 77270b880570e07a5d80b23371f53638ec30fb5c Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 18 Dec 2018 15:41:29 -0800 Subject: [PATCH 0845/1530] Apply intensity control for haptic data. Use the same logic in VibrationEffect.scale to control the intensity of haptic playback in audio framework. Note that as the maximum amplitude of vibrator is 255, convert the haptic data to pcm_8_bit before doing scaling. Test: Manually Change-Id: I6136d27c9255a215834b6e3092aa8ad696fbae04 --- .../libaudioclient/include/media/AudioMixer.h | 61 +++++++++++++++++++ media/libaudioprocessing/AudioMixer.cpp | 41 +++++++++++++ services/audioflinger/FastMixer.cpp | 4 ++ services/audioflinger/FastMixerState.h | 3 + services/audioflinger/PlaybackTracks.h | 10 +++ services/audioflinger/Threads.cpp | 5 ++ 6 files changed, 124 insertions(+) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 3ae7104459..fbbbd11135 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -80,6 +80,7 @@ public: MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output // for haptic HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not. + HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data. // for target RESAMPLE SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; // parameter 'value' is the new sample rate in Hz. @@ -102,6 +103,31 @@ public: // parameter 'value' is a pointer to the new playback rate. }; + enum { // Haptic intensity, should keep consistent with VibratorService + HAPTIC_SCALE_VERY_LOW = -2, + HAPTIC_SCALE_LOW = -1, + HAPTIC_SCALE_NONE = 0, + HAPTIC_SCALE_HIGH = 1, + HAPTIC_SCALE_VERY_HIGH = 2, + }; + typedef int32_t haptic_intensity_t; + static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2 / 3; + static constexpr float HAPTIC_SCALE_LOW_RATIO = 3 / 4; + static const CONSTEXPR float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; + + static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) { + switch (hapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + case HAPTIC_SCALE_LOW: + case HAPTIC_SCALE_NONE: + case HAPTIC_SCALE_HIGH: + case HAPTIC_SCALE_VERY_HIGH: + return true; + default: + return false; + } + } + AudioMixer(size_t frameCount, uint32_t sampleRate) : mSampleRate(sampleRate) , mFrameCount(frameCount) { @@ -147,6 +173,7 @@ public: } } (this->*mHook)(); + processHapticData(); } size_t getUnreleasedFrames(int name) const; @@ -364,6 +391,7 @@ private: // Haptic bool mHapticPlaybackEnabled; + haptic_intensity_t mHapticIntensity; audio_channel_mask_t mHapticChannelMask; uint32_t mHapticChannelCount; audio_channel_mask_t mMixerHapticChannelMask; @@ -374,6 +402,37 @@ private: uint32_t mAdjustNonDestructiveOutChannelCount; bool mKeepContractedChannels; + float getHapticScaleGamma() const { + // Need to keep consistent with the value in VibratorService. + switch (mHapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + return 2.0f; + case HAPTIC_SCALE_LOW: + return 1.5f; + case HAPTIC_SCALE_HIGH: + return 0.5f; + case HAPTIC_SCALE_VERY_HIGH: + return 0.25f; + default: + return 1.0f; + } + } + + float getHapticMaxAmplitudeRatio() const { + // Need to keep consistent with the value in VibratorService. + switch (mHapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + return HAPTIC_SCALE_VERY_LOW_RATIO; + case HAPTIC_SCALE_LOW: + return HAPTIC_SCALE_LOW_RATIO; + case HAPTIC_SCALE_NONE: + case HAPTIC_SCALE_HIGH: + case HAPTIC_SCALE_VERY_HIGH: + default: + return 1.0f; + } + } + private: // hooks void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); @@ -410,6 +469,8 @@ private: template void process__noResampleOneTrack(); + void processHapticData(); + static process_hook_t getProcessHook(int processType, uint32_t channelCount, audio_format_t mixerInFormat, audio_format_t mixerOutFormat); diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 86711de647..86777d6ca7 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -167,6 +167,7 @@ status_t AudioMixer::create( t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // haptic t->mHapticPlaybackEnabled = false; + t->mHapticIntensity = HAPTIC_SCALE_NONE; t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE; t->mMixerHapticChannelCount = 0; t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount; @@ -717,6 +718,12 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) track->prepareForAdjustChannels(); } } break; + case HAPTIC_INTENSITY: { + const haptic_intensity_t hapticIntensity = static_cast(valueInt); + if (track->mHapticIntensity != hapticIntensity) { + track->mHapticIntensity = hapticIntensity; + } + } break; default: LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } @@ -1846,6 +1853,40 @@ void AudioMixer::process__noResampleOneTrack() } } +void AudioMixer::processHapticData() +{ + // Need to keep consistent with VibrationEffect.scale(int, float, int) + for (const auto &pair : mGroups) { + // process by group of tracks with same output main buffer. + const auto &group = pair.second; + for (const int name : group) { + const std::shared_ptr &t = mTracks[name]; + if (t->mHapticPlaybackEnabled) { + size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount; + float gamma = t->getHapticScaleGamma(); + float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio(); + uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame( + t->mMixerChannelCount, t->mMixerFormat); + switch (t->mMixerFormat) { + // Mixer format should be AUDIO_FORMAT_PCM_FLOAT. + case AUDIO_FORMAT_PCM_FLOAT: { + float* fout = (float*) buffer; + for (size_t i = 0; i < sampleCount; i++) { + float mul = fout[i] >= 0 ? 1.0 : -1.0; + fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul; + } + } break; + default: + LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat); + break; + } + break; + } + } + } +} + /* This track hook is called to do resampling then mixing, * pulling from the track's upstream AudioBufferProvider. * diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index f328577456..ca0d74980d 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -295,6 +295,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)mSinkChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, + (void *)(uintptr_t)fastTrack->mHapticIntensity); mMixer->enable(name); } mGenerations[i] = fastTrack->mGeneration; @@ -333,6 +335,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)mSinkChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, + (void *)(uintptr_t)fastTrack->mHapticIntensity); // already enabled } mGenerations[i] = fastTrack->mGeneration; diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 9d2a733670..c27f2b7a9c 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,8 @@ struct FastTrack { audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; // intensity of + // haptic data }; // Represents a single state of the fast mixer diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index d9f570dfe3..8aeae7d186 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -119,6 +119,14 @@ public: void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) { mHapticPlaybackEnabled = hapticPlaybackEnabled; } + /** Return at what intensity to play haptics, used in mixer. */ + AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; } + /** Set intensity of haptic playback, should be set after querying vibrator service. */ + void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) { + if (AudioMixer::isValidHapticIntensity(hapticIntensity)) { + mHapticIntensity = hapticIntensity; + } + } protected: // for numerous @@ -197,6 +205,8 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not + // intensity to play haptic data + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; private: // The following fields are only for fast tracks, and should be in a subclass diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9f838a3db8..c8480c3541 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4615,6 +4615,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mChannelMask = track->mChannelMask; fastTrack->mFormat = track->mFormat; fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); + fastTrack->mHapticIntensity = track->getHapticIntensity(); fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -4937,6 +4938,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac trackId, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled()); + mAudioMixer->setParameter( + trackId, + AudioMixer::TRACK, + AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity()); // reset retry count track->mRetryCount = kMaxTrackRetries; -- GitLab From f949ddde2b4d1c986b847676f8bd9ad8364ebd4b Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 29 Jan 2019 14:34:11 -0800 Subject: [PATCH 0846/1530] cameraserver: Add support for hiding secure cameras. Bug: 123046494 Test: Sanity -> GCA Test: Hardcode all cameras hidden, AImageReaderVendorTest passes, GCA cannot find cameras. Change-Id: I7780751101c660e47d84627ec191f43d268f1aa9 Signed-off-by: Jayant Chowdhary --- .../include/camera/NdkCameraMetadataTags.h | 7 +++ .../camera/libcameraservice/CameraService.cpp | 52 +++++++++++++++++-- .../camera/libcameraservice/CameraService.h | 11 +++- .../common/CameraProviderManager.cpp | 25 +++++++++ .../common/CameraProviderManager.h | 3 ++ .../hidl/HidlCameraService.cpp | 3 +- 6 files changed, 94 insertions(+), 7 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index c1efa5fef2..69b9e7e5a2 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7608,6 +7608,13 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { */ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12, + /** + *

    The camera device is capable of writing image data into a region of memory + * inaccessible to Android userspace or the Android kernel, and only accessible to + * trusted execution environments (TEE).

    + */ + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13, + } acamera_metadata_enum_android_request_available_capabilities_t; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index c3113bf6ea..ee8d7e128b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -49,6 +49,7 @@ #include #include "hidl/HidlCameraService.h" #include +#include #include #include #include @@ -226,7 +227,7 @@ void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeS Mutex::Autolock lock(mStatusListenerLock); for (auto& i : mListenerList) { - i->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); + i.second->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); } } @@ -1287,6 +1288,18 @@ Status CameraService::connectLegacy( return ret; } +bool CameraService::shouldRejectHiddenCameraConnection(const String8 & cameraId) { + // If the thread serving this call is not a hwbinder thread and the caller + // isn't the cameraserver itself, and the camera id being requested is to be + // publically hidden, we should reject the connection. + if (!hardware::IPCThreadState::self()->isServingCall() && + CameraThreadState::getCallingPid() != getpid() && + mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) { + return true; + } + return false; +} + Status CameraService::connectDevice( const sp& cameraCb, const String16& cameraId, @@ -1299,6 +1312,7 @@ Status CameraService::connectDevice( Status ret = Status::ok(); String8 id = String8(cameraId); sp client = nullptr; + ret = connectHelper(cameraCb, id, /*api1CameraId*/-1, CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, @@ -1330,6 +1344,14 @@ Status CameraService::connectHelper(const sp& cameraCb, const String8& (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(), static_cast(effectiveApiLevel)); + if (shouldRejectHiddenCameraConnection(cameraId)) { + ALOGW("Attempting to connect to system-only camera id %s, connection rejected", + cameraId.c_str()); + return STATUS_ERROR_FMT(ERROR_DISCONNECTED, + "No camera device with ID \"%s\" currently available", + cameraId.string()); + + } sp client = nullptr; { // Acquire mServiceLock and prevent other clients from connecting @@ -1635,6 +1657,14 @@ Status CameraService::notifySystemEvent(int32_t eventId, Status CameraService::addListener(const sp& listener, /*out*/ std::vector *cameraStatuses) { + return addListenerHelper(listener, cameraStatuses); +} + +Status CameraService::addListenerHelper(const sp& listener, + /*out*/ + std::vector *cameraStatuses, + bool isVendorListener) { + ATRACE_CALL(); ALOGV("%s: Add listener %p", __FUNCTION__, listener.get()); @@ -1649,20 +1679,26 @@ Status CameraService::addListener(const sp& listener, { Mutex::Autolock lock(mStatusListenerLock); for (auto& it : mListenerList) { - if (IInterface::asBinder(it) == IInterface::asBinder(listener)) { + if (IInterface::asBinder(it.second) == IInterface::asBinder(listener)) { ALOGW("%s: Tried to add listener %p which was already subscribed", __FUNCTION__, listener.get()); return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered"); } } - mListenerList.push_back(listener); + mListenerList.emplace_back(isVendorListener, listener); } /* Collect current devices and status */ { Mutex::Autolock lock(mCameraStatesLock); for (auto& i : mCameraStates) { + if (!isVendorListener && + mCameraProviderManager->isPublicallyHiddenSecureCamera(i.first.c_str())) { + ALOGV("Cannot add public listener for hidden system-only %s for pid %d", + i.first.c_str(), CameraThreadState::getCallingPid()); + continue; + } cameraStatuses->emplace_back(i.first, mapToInterface(i.second->getStatus())); } } @@ -1697,7 +1733,7 @@ Status CameraService::removeListener(const sp& listener) { Mutex::Autolock lock(mStatusListenerLock); for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { - if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { + if (IInterface::asBinder(it->second) == IInterface::asBinder(listener)) { mListenerList.erase(it); return Status::ok(); } @@ -3033,7 +3069,13 @@ void CameraService::updateStatus(StatusInternal status, const String8& cameraId, Mutex::Autolock lock(mStatusListenerLock); for (auto& listener : mListenerList) { - listener->onStatusChanged(mapToInterface(status), String16(cameraId)); + if (!listener.first && + mCameraProviderManager->isPublicallyHiddenSecureCamera(cameraId.c_str())) { + ALOGV("Skipping camera discovery callback for system-only camera %s", + cameraId.c_str()); + continue; + } + listener.second->onStatusChanged(mapToInterface(status), String16(cameraId)); } }); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index a2961986b2..3af52fa54c 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -173,6 +173,10 @@ public: virtual status_t shellCommand(int in, int out, int err, const Vector& args); + binder::Status addListenerHelper(const sp& listener, + /*out*/ + std::vector* cameraStatuses, bool isVendor = false); + ///////////////////////////////////////////////////////////////////// // Client functionality @@ -615,6 +619,10 @@ private: sp* client, std::shared_ptr>>* partial); + // Should an operation attempt on a cameraId be rejected, if the camera id is + // advertised as a publically hidden secure camera, by the camera HAL ? + bool shouldRejectHiddenCameraConnection(const String8 & cameraId); + // Single implementation shared between the various connect calls template binder::Status connectHelper(const sp& cameraCb, const String8& cameraId, @@ -781,7 +789,8 @@ private: sp mCameraProviderManager; // Guarded by mStatusListenerMutex - std::vector> mListenerList; + std::vector>> mListenerList; + Mutex mStatusListenerLock; /** diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 3059b0737d..ad4fc5f834 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -504,6 +504,17 @@ void CameraProviderManager::ProviderInfo::DeviceInfo3::queryPhysicalCameraIds() } } +bool CameraProviderManager::ProviderInfo::DeviceInfo3::isPublicallyHiddenSecureCamera() { + camera_metadata_entry_t entryCap; + entryCap = mCameraCharacteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES); + if (entryCap.count != 1) { + // Do NOT hide this camera device if the capabilities specify anything more + // than ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA. + return false; + } + return entryCap.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA; +} + void CameraProviderManager::ProviderInfo::DeviceInfo3::getSupportedSizes( const CameraMetadata& ch, uint32_t tag, android_pixel_format_t format, std::vector> *sizes/*out*/) { @@ -882,6 +893,16 @@ bool CameraProviderManager::isLogicalCamera(const std::string& id, return deviceInfo->mIsLogicalCamera; } +bool CameraProviderManager::isPublicallyHiddenSecureCamera(const std::string& id) { + std::lock_guard lock(mInterfaceMutex); + + auto deviceInfo = findDeviceInfoLocked(id); + if (deviceInfo == nullptr) { + return false; + } + return deviceInfo->mIsPublicallyHiddenSecureCamera; +} + bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) { for (auto& provider : mProviders) { for (auto& deviceInfo : provider->mDevices) { @@ -1709,6 +1730,9 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& __FUNCTION__, id.c_str(), CameraProviderManager::statusToString(status), status); return; } + + mIsPublicallyHiddenSecureCamera = isPublicallyHiddenSecureCamera(); + status_t res = fixupMonochromeTags(); if (OK != res) { ALOGE("%s: Unable to fix up monochrome tags based for older HAL version: %s (%d)", @@ -1731,6 +1755,7 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& } queryPhysicalCameraIds(); + // Get physical camera characteristics if applicable auto castResult = device::V3_5::ICameraDevice::castFrom(interface); if (!castResult.isOk()) { diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index fbd7d2e8f8..18869f53f6 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -264,6 +264,7 @@ public: */ bool isLogicalCamera(const std::string& id, std::vector* physicalCameraIds); + bool isPublicallyHiddenSecureCamera(const std::string& id); bool isHiddenPhysicalCamera(const std::string& cameraId); static const float kDepthARTolerance; @@ -354,6 +355,7 @@ private: std::vector mPhysicalIds; hardware::CameraInfo mInfo; sp mSavedInterface; + bool mIsPublicallyHiddenSecureCamera = false; const hardware::camera::common::V1_0::CameraResourceCost mResourceCost; @@ -471,6 +473,7 @@ private: CameraMetadata mCameraCharacteristics; std::unordered_map mPhysicalCameraCharacteristics; void queryPhysicalCameraIds(); + bool isPublicallyHiddenSecureCamera(); status_t fixupMonochromeTags(); status_t addDynamicDepthTags(); static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag, diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp index 48f1d374bf..74cfe4267a 100644 --- a/services/camera/libcameraservice/hidl/HidlCameraService.cpp +++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp @@ -182,7 +182,8 @@ Return HidlCameraService::addListener(const sp& hC } } std::vector cameraStatusAndIds{}; - binder::Status serviceRet = mAidlICameraService->addListener(csListener, &cameraStatusAndIds); + binder::Status serviceRet = + mAidlICameraService->addListenerHelper(csListener, &cameraStatusAndIds, true); HStatus status = HStatus::NO_ERROR; if (!serviceRet.isOk()) { ALOGE("%s: Unable to add camera device status listener", __FUNCTION__); -- GitLab From 0f4e46ddf711df06a3b8b9990888b29b326101bf Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 30 Jan 2019 14:18:47 -0800 Subject: [PATCH 0847/1530] Codec2.0 HAL VTS: Add owners and create functional directory Add owners and move all current tests under functional directory. Bug: 118365011 Change-Id: I973b538211b3ddea3085ce4427ff5e72c1f5c584 --- media/codec2/hidl/1.0/vts/OWNERS | 9 +++++++++ .../hidl/1.0/vts/{ => functional}/audio/Android.bp | 0 .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 0 .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 0 .../audio/media_c2_audio_hidl_test_common.h | 0 .../hidl/1.0/vts/{ => functional}/common/Android.bp | 0 .../hidl/1.0/vts/{ => functional}/common/README.md | 0 .../common/media_c2_hidl_test_common.cpp | 0 .../common/media_c2_hidl_test_common.h | 0 .../1.0/vts/{ => functional}/component/Android.bp | 0 .../component/VtsHidlC2V1_0TargetComponentTest.cpp | 0 .../hidl/1.0/vts/{ => functional}/master/Android.bp | 0 .../master/VtsHidlC2V1_0TargetMasterTest.cpp | 0 .../res/bbb_352x288_420p_30fps_32frames.yuv | 0 .../res/bbb_aac_stereo_128kbps_48000hz.aac | Bin .../res/bbb_aac_stereo_128kbps_48000hz.info | 0 .../bbb_aac_stereo_128kbps_48000hz_multi_frame.info | 0 .../res/bbb_amrwb_1ch_14kbps_16000hz.amrwb | Bin .../res/bbb_amrwb_1ch_14kbps_16000hz.info | 0 .../bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info | 0 .../vts/{ => functional}/res/bbb_av1_176_144.av1 | Bin .../vts/{ => functional}/res/bbb_av1_176_144.info | 0 .../vts/{ => functional}/res/bbb_av1_640_360.av1 | Bin .../vts/{ => functional}/res/bbb_av1_640_360.info | 0 .../res/bbb_avc_176x144_300kbps_60fps.h264 | Bin .../res/bbb_avc_176x144_300kbps_60fps.info | 0 .../res/bbb_avc_640x360_768kbps_30fps.h264 | Bin .../res/bbb_avc_640x360_768kbps_30fps.info | 0 .../res/bbb_flac_stereo_680kbps_48000hz.flac | Bin .../res/bbb_flac_stereo_680kbps_48000hz.info | 0 .../{ => functional}/res/bbb_g711alaw_1ch_8khz.info | 0 .../{ => functional}/res/bbb_g711alaw_1ch_8khz.raw | 0 .../res/bbb_g711mulaw_1ch_8khz.info | 0 .../{ => functional}/res/bbb_g711mulaw_1ch_8khz.raw | 0 .../res/bbb_gsm_1ch_8khz_13kbps.info | 0 .../res/bbb_gsm_1ch_8khz_13kbps.raw | Bin .../res/bbb_h263_352x288_300kbps_12fps.h263 | Bin .../res/bbb_h263_352x288_300kbps_12fps.info | 0 .../res/bbb_hevc_176x144_176kbps_60fps.hevc | Bin .../res/bbb_hevc_176x144_176kbps_60fps.info | 0 .../res/bbb_hevc_640x360_1600kbps_30fps.hevc | Bin .../res/bbb_hevc_640x360_1600kbps_30fps.info | 0 .../res/bbb_mp3_stereo_192kbps_48000hz.info | 0 .../res/bbb_mp3_stereo_192kbps_48000hz.mp3 | Bin .../bbb_mp3_stereo_192kbps_48000hz_multi_frame.info | 0 .../res/bbb_mpeg2_176x144_105kbps_25fps.info | 0 .../res/bbb_mpeg2_176x144_105kbps_25fps.m2v | Bin .../res/bbb_mpeg2_352x288_1mbps_60fps.info | 0 .../res/bbb_mpeg2_352x288_1mbps_60fps.m2v | Bin .../res/bbb_mpeg4_352x288_512kbps_30fps.info | 0 .../res/bbb_mpeg4_352x288_512kbps_30fps.m4v | Bin .../res/bbb_opus_stereo_128kbps_48000hz.info | 0 .../res/bbb_opus_stereo_128kbps_48000hz.opus | Bin .../res/bbb_raw_1ch_16khz_s16le.raw | Bin .../{ => functional}/res/bbb_raw_1ch_8khz_s16le.raw | Bin .../res/bbb_raw_1ch_8khz_s32le.info | 0 .../{ => functional}/res/bbb_raw_1ch_8khz_s32le.raw | Bin .../res/bbb_raw_2ch_48khz_s16le.raw | Bin .../res/bbb_vorbis_stereo_128kbps_48000hz.info | 0 .../res/bbb_vorbis_stereo_128kbps_48000hz.vorbis | Bin .../res/bbb_vp8_176x144_240kbps_60fps.info | 0 .../res/bbb_vp8_176x144_240kbps_60fps.vp8 | Bin .../res/bbb_vp8_640x360_2mbps_30fps.info | 0 .../res/bbb_vp8_640x360_2mbps_30fps.vp8 | Bin .../res/bbb_vp9_176x144_285kbps_60fps.info | 0 .../res/bbb_vp9_176x144_285kbps_60fps.vp9 | Bin .../res/bbb_vp9_640x360_1600kbps_30fps.info | 0 .../res/bbb_vp9_640x360_1600kbps_30fps.vp9 | Bin .../res/sine_amrnb_1ch_12kbps_8000hz.amrnb | Bin .../res/sine_amrnb_1ch_12kbps_8000hz.info | 0 .../sine_amrnb_1ch_12kbps_8000hz_multi_frame.info | 0 .../hidl/1.0/vts/{ => functional}/video/Android.bp | 0 .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 0 .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 0 .../video/media_c2_video_hidl_test_common.h | 0 75 files changed, 9 insertions(+) create mode 100644 media/codec2/hidl/1.0/vts/OWNERS rename media/codec2/hidl/1.0/vts/{ => functional}/audio/Android.bp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/audio/media_c2_audio_hidl_test_common.h (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/common/Android.bp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/common/README.md (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/common/media_c2_hidl_test_common.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/common/media_c2_hidl_test_common.h (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/component/Android.bp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/component/VtsHidlC2V1_0TargetComponentTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/master/Android.bp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/master/VtsHidlC2V1_0TargetMasterTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_352x288_420p_30fps_32frames.yuv (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_aac_stereo_128kbps_48000hz.aac (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_aac_stereo_128kbps_48000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_amrwb_1ch_14kbps_16000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_av1_176_144.av1 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_av1_176_144.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_av1_640_360.av1 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_av1_640_360.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_avc_176x144_300kbps_60fps.h264 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_avc_176x144_300kbps_60fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_avc_640x360_768kbps_30fps.h264 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_avc_640x360_768kbps_30fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_flac_stereo_680kbps_48000hz.flac (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_flac_stereo_680kbps_48000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_g711alaw_1ch_8khz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_g711alaw_1ch_8khz.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_g711mulaw_1ch_8khz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_g711mulaw_1ch_8khz.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_gsm_1ch_8khz_13kbps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_gsm_1ch_8khz_13kbps.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_h263_352x288_300kbps_12fps.h263 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_h263_352x288_300kbps_12fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_hevc_176x144_176kbps_60fps.hevc (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_hevc_176x144_176kbps_60fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_hevc_640x360_1600kbps_30fps.hevc (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_hevc_640x360_1600kbps_30fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mp3_stereo_192kbps_48000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mp3_stereo_192kbps_48000hz.mp3 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg2_176x144_105kbps_25fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg2_176x144_105kbps_25fps.m2v (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg2_352x288_1mbps_60fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg2_352x288_1mbps_60fps.m2v (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg4_352x288_512kbps_30fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_mpeg4_352x288_512kbps_30fps.m4v (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_opus_stereo_128kbps_48000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_opus_stereo_128kbps_48000hz.opus (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_raw_1ch_16khz_s16le.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_raw_1ch_8khz_s16le.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_raw_1ch_8khz_s32le.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_raw_1ch_8khz_s32le.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_raw_2ch_48khz_s16le.raw (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vorbis_stereo_128kbps_48000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp8_176x144_240kbps_60fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp8_176x144_240kbps_60fps.vp8 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp8_640x360_2mbps_30fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp8_640x360_2mbps_30fps.vp8 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp9_176x144_285kbps_60fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp9_176x144_285kbps_60fps.vp9 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp9_640x360_1600kbps_30fps.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/bbb_vp9_640x360_1600kbps_30fps.vp9 (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/sine_amrnb_1ch_12kbps_8000hz.amrnb (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/sine_amrnb_1ch_12kbps_8000hz.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/video/Android.bp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/video/VtsHidlC2V1_0TargetVideoDecTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/video/VtsHidlC2V1_0TargetVideoEncTest.cpp (100%) rename media/codec2/hidl/1.0/vts/{ => functional}/video/media_c2_video_hidl_test_common.h (100%) diff --git a/media/codec2/hidl/1.0/vts/OWNERS b/media/codec2/hidl/1.0/vts/OWNERS new file mode 100644 index 0000000000..6733e0cbd1 --- /dev/null +++ b/media/codec2/hidl/1.0/vts/OWNERS @@ -0,0 +1,9 @@ +# Media team +lajos@google.com +pawin@google.com +taklee@google.com +wonsik@google.com + +# VTS team +yim@google.com +zhuoyao@google.com diff --git a/media/codec2/hidl/1.0/vts/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp similarity index 100% rename from media/codec2/hidl/1.0/vts/audio/Android.bp rename to media/codec2/hidl/1.0/vts/functional/audio/Android.bp diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp diff --git a/media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp diff --git a/media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h similarity index 100% rename from media/codec2/hidl/1.0/vts/audio/media_c2_audio_hidl_test_common.h rename to media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h diff --git a/media/codec2/hidl/1.0/vts/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp similarity index 100% rename from media/codec2/hidl/1.0/vts/common/Android.bp rename to media/codec2/hidl/1.0/vts/functional/common/Android.bp diff --git a/media/codec2/hidl/1.0/vts/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md similarity index 100% rename from media/codec2/hidl/1.0/vts/common/README.md rename to media/codec2/hidl/1.0/vts/functional/common/README.md diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.cpp rename to media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp diff --git a/media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h similarity index 100% rename from media/codec2/hidl/1.0/vts/common/media_c2_hidl_test_common.h rename to media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h diff --git a/media/codec2/hidl/1.0/vts/component/Android.bp b/media/codec2/hidl/1.0/vts/functional/component/Android.bp similarity index 100% rename from media/codec2/hidl/1.0/vts/component/Android.bp rename to media/codec2/hidl/1.0/vts/functional/component/Android.bp diff --git a/media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/component/VtsHidlC2V1_0TargetComponentTest.cpp rename to media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp diff --git a/media/codec2/hidl/1.0/vts/master/Android.bp b/media/codec2/hidl/1.0/vts/functional/master/Android.bp similarity index 100% rename from media/codec2/hidl/1.0/vts/master/Android.bp rename to media/codec2/hidl/1.0/vts/functional/master/Android.bp diff --git a/media/codec2/hidl/1.0/vts/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/master/VtsHidlC2V1_0TargetMasterTest.cpp rename to media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp diff --git a/media/codec2/hidl/1.0/vts/res/bbb_352x288_420p_30fps_32frames.yuv b/media/codec2/hidl/1.0/vts/functional/res/bbb_352x288_420p_30fps_32frames.yuv similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_352x288_420p_30fps_32frames.yuv rename to media/codec2/hidl/1.0/vts/functional/res/bbb_352x288_420p_30fps_32frames.yuv diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.aac b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.aac similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.aac rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.aac diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.amrwb diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.av1 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.av1 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.av1 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_av1_176_144.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_176_144.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.av1 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.av1 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.av1 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_av1_640_360.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_av1_640_360.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.h264 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.h264 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.h264 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.h264 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_avc_176x144_300kbps_60fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_176x144_300kbps_60fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.h264 b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.h264 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.h264 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.h264 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_avc_640x360_768kbps_30fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_avc_640x360_768kbps_30fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.flac b/media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.flac similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.flac rename to media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.flac diff --git a/media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_flac_stereo_680kbps_48000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_flac_stereo_680kbps_48000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_g711alaw_1ch_8khz.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711alaw_1ch_8khz.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_g711mulaw_1ch_8khz.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_g711mulaw_1ch_8khz.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_gsm_1ch_8khz_13kbps.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_gsm_1ch_8khz_13kbps.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.h263 b/media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.h263 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.h263 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.h263 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_h263_352x288_300kbps_12fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_h263_352x288_300kbps_12fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.hevc b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.hevc similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.hevc rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.hevc diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_176x144_176kbps_60fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_176x144_176kbps_60fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.hevc b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.hevc similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.hevc rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.hevc diff --git a/media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_hevc_640x360_1600kbps_30fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_hevc_640x360_1600kbps_30fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.mp3 b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.mp3 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz.mp3 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz.mp3 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.m2v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.m2v similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_176x144_105kbps_25fps.m2v rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_176x144_105kbps_25fps.m2v diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.m2v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.m2v similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg2_352x288_1mbps_60fps.m2v rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg2_352x288_1mbps_60fps.m2v diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.m4v b/media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.m4v similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_mpeg4_352x288_512kbps_30fps.m4v rename to media/codec2/hidl/1.0/vts/functional/res/bbb_mpeg4_352x288_512kbps_30fps.m4v diff --git a/media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.opus b/media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.opus similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_opus_stereo_128kbps_48000hz.opus rename to media/codec2/hidl/1.0/vts/functional/res/bbb_opus_stereo_128kbps_48000hz.opus diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_16khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_16khz_s16le.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_16khz_s16le.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_16khz_s16le.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s16le.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s16le.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s16le.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_raw_1ch_8khz_s32le.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_1ch_8khz_s32le.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_raw_2ch_48khz_s16le.raw b/media/codec2/hidl/1.0/vts/functional/res/bbb_raw_2ch_48khz_s16le.raw similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_raw_2ch_48khz_s16le.raw rename to media/codec2/hidl/1.0/vts/functional/res/bbb_raw_2ch_48khz_s16le.raw diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis b/media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vorbis_stereo_128kbps_48000hz.vorbis diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.vp8 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.vp8 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_176x144_240kbps_60fps.vp8 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_176x144_240kbps_60fps.vp8 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.vp8 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.vp8 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp8_640x360_2mbps_30fps.vp8 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp8_640x360_2mbps_30fps.vp8 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.vp9 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.vp9 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_176x144_285kbps_60fps.vp9 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_176x144_285kbps_60fps.vp9 diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.info b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.info rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.info diff --git a/media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.vp9 b/media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.vp9 similarity index 100% rename from media/codec2/hidl/1.0/vts/res/bbb_vp9_640x360_1600kbps_30fps.vp9 rename to media/codec2/hidl/1.0/vts/functional/res/bbb_vp9_640x360_1600kbps_30fps.vp9 diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.amrnb b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.amrnb similarity index 100% rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.amrnb rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.amrnb diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.info b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz.info rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz.info diff --git a/media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info b/media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info similarity index 100% rename from media/codec2/hidl/1.0/vts/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info rename to media/codec2/hidl/1.0/vts/functional/res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info diff --git a/media/codec2/hidl/1.0/vts/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp similarity index 100% rename from media/codec2/hidl/1.0/vts/video/Android.bp rename to media/codec2/hidl/1.0/vts/functional/video/Android.bp diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoDecTest.cpp rename to media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp diff --git a/media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/video/VtsHidlC2V1_0TargetVideoEncTest.cpp rename to media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp diff --git a/media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h similarity index 100% rename from media/codec2/hidl/1.0/vts/video/media_c2_video_hidl_test_common.h rename to media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h -- GitLab From f024a9eb29122a42179608372cb25129d1158e3b Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 30 Jan 2019 16:01:02 -0800 Subject: [PATCH 0848/1530] AudioPolicyManager: Fix nullptr device check Regression from commit 11d301045991dd66bf3f64ba510e069b7c09fedb Test: CTS AudioRecordTest passes Bug: 123597808 Change-Id: I3b1de479ebd8a279309d33f58db3e40b851f98e4 --- .../common/managerdefinitions/src/DeviceDescriptor.cpp | 2 +- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index dc5b238cbb..4cb1e17020 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -129,7 +129,7 @@ void DeviceVector::refreshTypes() ssize_t DeviceVector::indexOf(const sp& item) const { for (size_t i = 0; i < size(); i++) { - if (item->equals(itemAt(i))) { + if (itemAt(i)->equals(item)) { // item may be null sp<>, i.e. AUDIO_DEVICE_NONE return i; } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 46f51e5e4a..d7c7b4d029 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5062,7 +5062,7 @@ sp AudioPolicyManager::getNewInputDevice( } // If we are not in call and no client is active on this input, this methods returns - // AUDIO_DEVICE_NONE, causing the patch on the input stream to be released. + // a null sp<>, causing the patch on the input stream to be released. audio_attributes_t attributes = inputDesc->getHighestPriorityAttributes(); if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) { attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION; -- GitLab From a4f9d509c0283c1ba0c65b7839948356e30e8f88 Mon Sep 17 00:00:00 2001 From: Xusong Wang Date: Thu, 31 Jan 2019 01:07:00 +0000 Subject: [PATCH 0849/1530] Revert "Update plugin to use cas@1.1 hal" This reverts commit a6fc4d3f7a7f32695e019688fea9d176e982d778. Reason for revert: Change-Id: I51fad3126ac1823954029922a44be84a3fa0ca2d --- .../plugins/clearkey/ClearKeyCasPlugin.cpp | 42 +------------------ .../plugins/clearkey/ClearKeyCasPlugin.h | 11 ----- drm/mediacas/plugins/mock/MockCasPlugin.cpp | 23 ---------- drm/mediacas/plugins/mock/MockCasPlugin.h | 11 +---- 4 files changed, 3 insertions(+), 84 deletions(-) diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp index 27bd631afe..1558e8b8da 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp @@ -65,20 +65,7 @@ status_t ClearKeyCasFactory::createPlugin( *plugin = new ClearKeyCasPlugin(appData, callback); return OK; } - -status_t ClearKeyCasFactory::createPlugin( - int32_t CA_system_id, - void *appData, - CasPluginCallbackExt callback, - CasPlugin **plugin) { - if (!isSystemIdSupported(CA_system_id)) { - return BAD_VALUE; - } - - *plugin = new ClearKeyCasPlugin(appData, callback); - return OK; -} -//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// bool ClearKeyDescramblerFactory::isSystemIdSupported( int32_t CA_system_id) const { return CA_system_id == sClearKeySystemId; @@ -101,12 +88,6 @@ ClearKeyCasPlugin::ClearKeyCasPlugin( ALOGV("CTOR"); } -ClearKeyCasPlugin::ClearKeyCasPlugin( - void *appData, CasPluginCallbackExt callback) - : mCallbackExt(callback), mAppData(appData) { - ALOGV("CTOR"); -} - ClearKeyCasPlugin::~ClearKeyCasPlugin() { ALOGV("DTOR"); ClearKeySessionLibrary::get()->destroyPlugin(this); @@ -186,27 +167,8 @@ status_t ClearKeyCasPlugin::sendEvent( // Echo the received event to the callback. // Clear key plugin doesn't use any event, echo'ing for testing only. if (mCallback != NULL) { - mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), - eventData.size()); - } else if (mCallbackExt != NULL) { - mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), - eventData.size(), NULL); - } - return OK; -} - -status_t ClearKeyCasPlugin::sendSessionEvent( - const CasSessionId &sessionId, int32_t event, - int arg, const CasData &eventData) { - ALOGV("sendSessionEvent: sessionId=%s, event=%d, arg=%d", - sessionIdToString(sessionId).string(), event, arg); - // Echo the received event to the callback. - // Clear key plugin doesn't use any event, echo'ing for testing only. - if (mCallbackExt != NULL) { - mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), - eventData.size(), &sessionId); + mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), eventData.size()); } - return OK; } diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h index f48d5b1d13..389e1728d7 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h @@ -47,11 +47,6 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; - virtual status_t createPlugin( - int32_t CA_system_id, - void *appData, - CasPluginCallbackExt callback, - CasPlugin **plugin) override; }; class ClearKeyDescramblerFactory : public DescramblerFactory { @@ -68,7 +63,6 @@ public: class ClearKeyCasPlugin : public CasPlugin { public: ClearKeyCasPlugin(void *appData, CasPluginCallback callback); - ClearKeyCasPlugin(void *appData, CasPluginCallbackExt callback); virtual ~ClearKeyCasPlugin(); virtual status_t setPrivateData( @@ -91,10 +85,6 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; - virtual status_t sendSessionEvent( - const CasSessionId &sessionId, - int32_t event, int32_t arg, const CasData &eventData) override; - virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( @@ -104,7 +94,6 @@ private: Mutex mKeyFetcherLock; std::unique_ptr mKeyFetcher; CasPluginCallback mCallback; - CasPluginCallbackExt mCallbackExt; void* mAppData; }; diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp index 2964791413..8404a83935 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp +++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp @@ -60,19 +60,6 @@ status_t MockCasFactory::createPlugin( return OK; } -status_t MockCasFactory::createPlugin( - int32_t CA_system_id, - void* /*appData*/, - CasPluginCallbackExt /*callback*/, - CasPlugin **plugin) { - if (!isSystemIdSupported(CA_system_id)) { - return BAD_VALUE; - } - - *plugin = new MockCasPlugin(); - return OK; -} - /////////////////////////////////////////////////////////////////////////////// bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const { @@ -183,16 +170,6 @@ status_t MockCasPlugin::sendEvent( return OK; } -status_t MockCasPlugin::sendSessionEvent( - const CasSessionId &sessionId, int32_t event, - int /*arg*/, const CasData& /*eventData*/) { - ALOGV("sendSessionEvent: sessionId=%s, event=%d", - arrayToString(sessionId).string(), event); - Mutex::Autolock lock(mLock); - - return OK; -} - status_t MockCasPlugin::provision(const String8 &str) { ALOGV("provision: provisionString=%s", str.string()); Mutex::Autolock lock(mLock); diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h index 74b540cc82..81069906ed 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.h +++ b/drm/mediacas/plugins/mock/MockCasPlugin.h @@ -42,11 +42,6 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; - virtual status_t createPlugin( - int32_t CA_system_id, - void *appData, - CasPluginCallbackExt callback, - CasPlugin **plugin) override; }; class MockDescramblerFactory : public DescramblerFactory { @@ -85,11 +80,7 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; - virtual status_t sendSessionEvent( - const CasSessionId &sessionId, - int32_t event, int32_t arg, const CasData &eventData) override; - - virtual status_t provision(const String8 &str) override; + virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( int32_t refreshType, const CasData &refreshData) override; -- GitLab From 57303cc4312686e02e754ba99a81f0cf71144757 Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 18 Dec 2018 15:45:57 -0800 Subject: [PATCH 0850/1530] Interface between audio server and vibrator service The haptic playback should be controlled by vibrator service. Via the interface, audio server could notify vibrator service about starting or stopping haptic playback and get vibrator intensity from vibrator service. Vibrator service could call mute/unmute to control the haptic playback. Test: Manually Change-Id: Iad24813977e4dea0d67a91f8f8b390a016ce4ca2 --- media/audioserver/Android.mk | 3 +- services/audioflinger/Android.mk | 3 +- services/audioflinger/AudioFlinger.cpp | 37 ++++++++++++++++++++++++ services/audioflinger/AudioFlinger.h | 6 ++++ services/audioflinger/PlaybackTracks.h | 11 +++++++ services/audioflinger/Threads.cpp | 39 +++++++++++++------------ services/audioflinger/Tracks.cpp | 40 ++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 20 deletions(-) diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk index f05c84b95c..10d8b13347 100644 --- a/media/audioserver/Android.mk +++ b/media/audioserver/Android.mk @@ -19,7 +19,8 @@ LOCAL_SHARED_LIBRARIES := \ libnbaio \ libnblog \ libsoundtriggerservice \ - libutils + libutils \ + libvibrator # TODO oboeservice is the old folder name for aaudioservice. It will be changed. LOCAL_C_INCLUDES := \ diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index c0aa4777b7..91b75870ab 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -38,7 +38,8 @@ LOCAL_SHARED_LIBRARIES := \ libpowermanager \ libmediautils \ libmemunreachable \ - libmedia_helper + libmedia_helper \ + libvibrator LOCAL_STATIC_LIBRARIES := \ libcpustats \ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0d6ef46b4c..7660f03761 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -122,6 +123,21 @@ static void sMediaLogInit() } } +// Keep a strong reference to external vibrator service +static sp sExternalVibratorService; + +static sp getExternalVibratorService() { + if (sExternalVibratorService == 0) { + sp binder = defaultServiceManager()->getService( + String16("external_vibrator_service")); + if (binder != 0) { + sExternalVibratorService = + interface_cast(binder); + } + } + return sExternalVibratorService; +} + // ---------------------------------------------------------------------------- std::string formatToString(audio_format_t format) { @@ -318,6 +334,27 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di return ret; } +/* static */ +int AudioFlinger::onExternalVibrationStart(const sp& externalVibration) { + sp evs = getExternalVibratorService(); + if (evs != 0) { + int32_t ret; + binder::Status status = evs->onExternalVibrationStart(*externalVibration, &ret); + if (status.isOk()) { + return ret; + } + } + return AudioMixer::HAPTIC_SCALE_NONE; +} + +/* static */ +void AudioFlinger::onExternalVibrationStop(const sp& externalVibration) { + sp evs = getExternalVibratorService(); + if (evs != 0) { + evs->onExternalVibrationStop(*externalVibration); + } +} + static const char * const audio_interfaces[] = { AUDIO_HARDWARE_MODULE_ID_PRIMARY, AUDIO_HARDWARE_MODULE_ID_A2DP, diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index c1169d2806..b8a563bd6a 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -84,6 +85,8 @@ #include #include +#include + #include "android/media/BnAudioRecord.h" namespace android { @@ -284,6 +287,9 @@ public: const sp& callback, sp& interface, audio_port_handle_t *handle); + + static int onExternalVibrationStart(const sp& externalVibration); + static void onExternalVibrationStop(const sp& externalVibration); private: // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed. static const size_t kLogMemorySize = 400 * 1024; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 8aeae7d186..bad3ca89df 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -127,6 +127,7 @@ public: mHapticIntensity = hapticIntensity; } } + sp getExternalVibration() const { return mExternalVibration; } protected: // for numerous @@ -207,6 +208,16 @@ protected: bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not // intensity to play haptic data AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; + class AudioVibrationController : public os::BnExternalVibrationController { + public: + explicit AudioVibrationController(Track* track) : mTrack(track) {} + binder::Status mute(/*out*/ bool *ret) override; + binder::Status unmute(/*out*/ bool *ret) override; + private: + Track* const mTrack; + }; + sp mAudioVibrationController; + sp mExternalVibration; private: // The following fields are only for fast tracks, and should be in a subclass diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index c8480c3541..d70efb7c00 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2358,15 +2358,23 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING; } - // Disable all haptic playback for all other active tracks when haptic playback is supported - // and the track contains haptic channels. Enable haptic playback for current track. - // TODO: Request actual haptic playback status from vibrator service if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE && mHapticChannelMask != AUDIO_CHANNEL_NONE) { - for (auto &t : mActiveTracks) { - t->setHapticPlaybackEnabled(false); + // Unlock due to VibratorService will lock for this call and will + // call Tracks.mute/unmute which also require thread's lock. + mLock.unlock(); + const int intensity = AudioFlinger::onExternalVibrationStart( + track->getExternalVibration()); + mLock.lock(); + // Haptic playback should be enabled by vibrator service. + if (track->getHapticPlaybackEnabled()) { + // Disable haptic playback of all active track to ensure only + // one track playing haptic if current track should play haptic. + for (const auto &t : mActiveTracks) { + t->setHapticPlaybackEnabled(false); + } } - track->setHapticPlaybackEnabled(true); + track->setHapticIntensity(intensity); } track->mResetDone = false; @@ -3760,7 +3768,6 @@ bool AudioFlinger::PlaybackThread::threadLoop() // removeTracks_l() must be called with ThreadBase::mLock held void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tracksToRemove) { - bool enabledHapticTracksRemoved = false; for (const auto& track : tracksToRemove) { mActiveTracks.remove(track); ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId()); @@ -3782,17 +3789,13 @@ void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tra // remove from our tracks vector removeTrack_l(track); } - enabledHapticTracksRemoved |= track->getHapticPlaybackEnabled(); - } - // If the thread supports haptic playback and the track playing haptic data was removed, - // enable haptic playback on the first active track that contains haptic channels. - // TODO: Query vibrator service to know which track should enable haptic playback. - if (enabledHapticTracksRemoved && mHapticChannelMask != AUDIO_CHANNEL_NONE) { - for (auto &t : mActiveTracks) { - if (t->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) { - t->setHapticPlaybackEnabled(true); - break; - } + if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE + && mHapticChannelCount > 0) { + mLock.unlock(); + // Unlock due to VibratorService will lock for this call and will + // call Tracks.mute/unmute which also require thread's lock. + AudioFlinger::onExternalVibrationStop(track->getExternalVibration()); + mLock.lock(); } } } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index d23d19d813..22d34b26d4 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -451,6 +451,12 @@ AudioFlinger::PlaybackThread::Track::Track( mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_" + std::to_string(mId)); #endif + + if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) { + mAudioVibrationController = new AudioVibrationController(this); + mExternalVibration = new os::ExternalVibration( + mUid, "" /* pkg */, mAttr, mAudioVibrationController); + } } AudioFlinger::PlaybackThread::Track::~Track() @@ -1336,6 +1342,40 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo( mServerLatencyMs.store(latencyMs); } +binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::mute( + /*out*/ bool *ret) { + *ret = false; + sp thread = mTrack->mThread.promote(); + if (thread != 0) { + // Lock for updating mHapticPlaybackEnabled. + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE + && playbackThread->mHapticChannelCount > 0) { + mTrack->setHapticPlaybackEnabled(false); + *ret = true; + } + } + return binder::Status::ok(); +} + +binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::unmute( + /*out*/ bool *ret) { + *ret = false; + sp thread = mTrack->mThread.promote(); + if (thread != 0) { + // Lock for updating mHapticPlaybackEnabled. + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE + && playbackThread->mHapticChannelCount > 0) { + mTrack->setHapticPlaybackEnabled(true); + *ret = true; + } + } + return binder::Status::ok(); +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::OutputTrack" -- GitLab From a1185162c486651b856f3cb5ced219b67be735bf Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Wed, 30 Jan 2019 16:41:43 -0800 Subject: [PATCH 0851/1530] Camera: Only report dynamic depth tag related errors Failing to add dynamic depth camera characteristics must not have any additional side effects for the device info constructor. In case we encounter any issues report the failure and try to continue with the intitialization. Additionally fix a minor typo and remove a debug log. Bug: 123660999 Test: Manual using application, Camera CTS Change-Id: I7b4e723ce3bcaf0e5d4c313ba17bca965e8964d8 --- .../common/CameraProviderManager.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 3059b0737d..6f1beb13c1 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -648,7 +648,7 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() bool isDepthExclusivePresent = std::find(chTags.data.i32, chTags.data.i32 + chTags.count, depthExclTag) != (chTags.data.i32 + chTags.count); bool isDepthSizePresent = std::find(chTags.data.i32, chTags.data.i32 + chTags.count, - depthExclTag) != (chTags.data.i32 + chTags.count); + depthSizesTag) != (chTags.data.i32 + chTags.count); if (!(isDepthExclusivePresent && isDepthSizePresent)) { // No depth support, nothing more to do. return OK; @@ -676,7 +676,6 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addDynamicDepthTags() getSupportedDynamicDepthSizes(supportedBlobSizes, supportedDepthSizes, &supportedDynamicDepthSizes, &internalDepthSizes); if (supportedDynamicDepthSizes.empty()) { - ALOGE("%s: No dynamic depth size matched!", __func__); // Nothing more to do. return OK; } @@ -1715,11 +1714,10 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& __FUNCTION__, strerror(-res), res); return; } - res = addDynamicDepthTags(); - if (OK != res) { - ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-res), - res); - return; + auto stat = addDynamicDepthTags(); + if (OK != stat) { + ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-stat), + stat); } camera_metadata_entry flashAvailable = mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE); -- GitLab From f86268a0aa3c98cc5acdd18f43de82e780772b5b Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 30 Jan 2019 16:52:10 -0800 Subject: [PATCH 0852/1530] Prepend instead of append for criterias to remove Bug: 123661699 Test: manually call removeUidDeviceAffinity Change-Id: I729b02bbc45125638baefdccdedfaf046e146dd2 --- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 3b9411a068..cd10c8240d 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -411,11 +411,11 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { if ((rule == RULE_EXCLUDE_UID || rule == RULE_MATCH_UID) && uid == mix->mCriteria[j].mValue.mUid) { foundUidRule = true; - criteriaToRemove.push_back(j); + criteriaToRemove.insert(criteriaToRemove.begin(), j); } } if (foundUidRule) { - for (size_t j = criteriaToRemove.size() - 1; j >= 0; j--) { + for (size_t j = 0; j < criteriaToRemove.size(); j++) { mix->mCriteria.removeAt(criteriaToRemove[j]); } } -- GitLab From c85b8156ef3036914414002d55d302564f613c61 Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Thu, 31 Jan 2019 13:08:23 +0900 Subject: [PATCH 0853/1530] Remove MediaComponents Test: build Change-Id: I50da7f34858a4739777d84abdde8a502537bf57e --- packages/MediaComponents/apex/Android.bp | 41 - .../media/IRemoteVolumeController.aidl | 32 - .../android/media/ISessionTokensListener.aidl | 27 - .../java/android/media/MediaDescription.aidl | 18 - .../java/android/media/MediaDescription.java | 383 ---- .../java/android/media/MediaMetadata.aidl | 18 - .../java/android/media/MediaMetadata.java | 941 ---------- .../android/media/MediaParceledListSlice.aidl | 19 - .../android/media/MediaParceledListSlice.java | 202 --- .../apex/java/android/media/Rating.aidl | 19 - .../apex/java/android/media/Rating.java | 308 ---- .../java/android/media/VolumeProvider.java | 161 -- .../android/media/browse/MediaBrowser.aidl | 18 - .../android/media/browse/MediaBrowser.java | 1171 ------------ .../media/browse/MediaBrowserUtils.java | 72 - .../session/IActiveSessionsListener.aidl | 26 - .../java/android/media/session/ICallback.aidl | 35 - .../media/session/IOnMediaKeyListener.aidl | 28 - .../IOnVolumeKeyLongPressListener.aidl | 27 - .../java/android/media/session/ISession.aidl | 54 - .../media/session/ISessionCallback.aidl | 71 - .../media/session/ISessionController.aidl | 88 - .../session/ISessionControllerCallback.aidl | 39 - .../media/session/ISessionManager.aidl | 58 - .../media/session/MediaController.java | 1190 ------------- .../android/media/session/MediaSession.aidl | 19 - .../android/media/session/MediaSession.java | 1569 ----------------- .../media/session/ParcelableVolumeInfo.aidl | 18 - .../media/session/ParcelableVolumeInfo.java | 80 - .../android/media/session/PlaybackState.aidl | 18 - .../android/media/session/PlaybackState.java | 1081 ------------ .../service/media/IMediaBrowserService.aidl | 27 - .../media/IMediaBrowserServiceCallbacks.aidl | 28 - .../service/media/MediaBrowserService.java | 856 --------- packages/OWNERS | 9 - 35 files changed, 8751 deletions(-) delete mode 100644 packages/MediaComponents/apex/Android.bp delete mode 100644 packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaDescription.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaDescription.java delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaMetadata.java delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java delete mode 100644 packages/MediaComponents/apex/java/android/media/Rating.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/Rating.java delete mode 100644 packages/MediaComponents/apex/java/android/media/VolumeProvider.java delete mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java delete mode 100644 packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java delete mode 100644 packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ICallback.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ISession.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaController.java delete mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/MediaSession.java delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java delete mode 100644 packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl delete mode 100644 packages/MediaComponents/apex/java/android/media/session/PlaybackState.java delete mode 100644 packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl delete mode 100644 packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl delete mode 100644 packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java delete mode 100644 packages/OWNERS diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp deleted file mode 100644 index d89eb773c1..0000000000 --- a/packages/MediaComponents/apex/Android.bp +++ /dev/null @@ -1,41 +0,0 @@ -filegroup { - name: "media_aidl", - srcs: [ - "java/android/media/**/*.aidl", - "java/android/service/**/*.aidl", - ], - exclude_srcs: [ - // Exclude these aidls to avoid errors such as - // "Refusing to generate code with unstructured parcelables." - "java/android/media/MediaDescription.aidl", - "java/android/media/MediaMetadata.aidl", - // TODO(insun): check why MediaParceledListSlice.aidl should be added here - "java/android/media/MediaParceledListSlice.aidl", - "java/android/media/Rating.aidl", - "java/android/media/browse/MediaBrowser.aidl", - "java/android/media/session/MediaSession.aidl", - "java/android/media/session/ParcelableVolumeInfo.aidl", - "java/android/media/session/PlaybackState.aidl", - ], -} - -java_library { - name: "media", - installable: true, - sdk_version: "system_current", - srcs: [ - "java/android/media/**/*.java", - "java/android/service/**/*.java", - ":media_aidl", - ":framework-media-annotation-srcs", - ], - aidl: { - local_include_dirs: ["java"], - include_dirs: [ - "frameworks/base/core/java", - // for android.graphics.Bitmap - // from IMediaBrowserServiceCallback - "frameworks/base/graphics/java", - ], - }, -} diff --git a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl deleted file mode 100644 index e4a4a42380..0000000000 --- a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2014 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.session.ISessionController; - -/** - * AIDL for the MediaSessionService to report interesting events on remote playback - * to a volume control dialog. See also IVolumeController for the AudioService half. - * TODO add in better support for multiple remote sessions. - * @hide - */ -oneway interface IRemoteVolumeController { - void remoteVolumeChanged(ISessionController session, int flags); - // sets the default session to use with the slider, replaces remoteSliderVisibility - // on IVolumeController - void updateRemoteController(ISessionController session); -} diff --git a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl deleted file mode 100644 index c83a19e64d..0000000000 --- a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.os.Bundle; - -/** - * Listens for changes to the list of session tokens. - * @hide - */ -oneway interface ISessionTokensListener { - void onSessionTokensChanged(in List tokens); -} diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl deleted file mode 100644 index 6f934f75aa..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2014, 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; - -parcelable MediaDescription; diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java deleted file mode 100644 index 31079e5c54..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaDescription.java +++ /dev/null @@ -1,383 +0,0 @@ -package android.media; - -import android.annotation.Nullable; -import android.graphics.Bitmap; -import android.media.browse.MediaBrowser; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; - -/** - * A simple set of metadata for a media item suitable for display. This can be - * created using the Builder or retrieved from existing metadata using - * {@link MediaMetadata#getDescription()}. - */ -public class MediaDescription implements Parcelable { - /** - * A unique persistent id for the content or null. - */ - private final String mMediaId; - /** - * A primary title suitable for display or null. - */ - private final CharSequence mTitle; - /** - * A subtitle suitable for display or null. - */ - private final CharSequence mSubtitle; - /** - * A description suitable for display or null. - */ - private final CharSequence mDescription; - /** - * A bitmap icon suitable for display or null. - */ - private final Bitmap mIcon; - /** - * A Uri for an icon suitable for display or null. - */ - private final Uri mIconUri; - /** - * Extras for opaque use by apps/system. - */ - private final Bundle mExtras; - /** - * A Uri to identify this content. - */ - private final Uri mMediaUri; - - /** - * Used as a long extra field to indicate the bluetooth folder type of the media item as - * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for - * {@link MediaBrowser.MediaItem} with {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}. The value - * should be one of the following: - *
      - *
    • {@link #BT_FOLDER_TYPE_MIXED}
    • - *
    • {@link #BT_FOLDER_TYPE_TITLES}
    • - *
    • {@link #BT_FOLDER_TYPE_ALBUMS}
    • - *
    • {@link #BT_FOLDER_TYPE_ARTISTS}
    • - *
    • {@link #BT_FOLDER_TYPE_GENRES}
    • - *
    • {@link #BT_FOLDER_TYPE_PLAYLISTS}
    • - *
    • {@link #BT_FOLDER_TYPE_YEARS}
    • - *
    - * - * @see #getExtras() - */ - public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE"; - - /** - * The type of folder that is unknown or contains media elements of mixed types as specified in - * the section 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_MIXED = 0; - - /** - * The type of folder that contains media elements only as specified in the section 6.10.2.2 of - * the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_TITLES = 1; - - /** - * The type of folder that contains folders categorized by album as specified in the section - * 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_ALBUMS = 2; - - /** - * The type of folder that contains folders categorized by artist as specified in the section - * 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_ARTISTS = 3; - - /** - * The type of folder that contains folders categorized by genre as specified in the section - * 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_GENRES = 4; - - /** - * The type of folder that contains folders categorized by playlist as specified in the section - * 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_PLAYLISTS = 5; - - /** - * The type of folder that contains folders categorized by year as specified in the section - * 6.10.2.2 of the Bluetooth AVRCP 1.5. - */ - public static final long BT_FOLDER_TYPE_YEARS = 6; - - private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle, - CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) { - mMediaId = mediaId; - mTitle = title; - mSubtitle = subtitle; - mDescription = description; - mIcon = icon; - mIconUri = iconUri; - mExtras = extras; - mMediaUri = mediaUri; - } - - private MediaDescription(Parcel in) { - mMediaId = in.readString(); - mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mIcon = in.readParcelable(null); - mIconUri = in.readParcelable(null); - mExtras = in.readBundle(); - mMediaUri = in.readParcelable(null); - } - - /** - * Returns the media id or null. See - * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}. - */ - public @Nullable String getMediaId() { - return mMediaId; - } - - /** - * Returns a title suitable for display or null. - * - * @return A title or null. - */ - public @Nullable CharSequence getTitle() { - return mTitle; - } - - /** - * Returns a subtitle suitable for display or null. - * - * @return A subtitle or null. - */ - public @Nullable CharSequence getSubtitle() { - return mSubtitle; - } - - /** - * Returns a description suitable for display or null. - * - * @return A description or null. - */ - public @Nullable CharSequence getDescription() { - return mDescription; - } - - /** - * Returns a bitmap icon suitable for display or null. - * - * @return An icon or null. - */ - public @Nullable Bitmap getIconBitmap() { - return mIcon; - } - - /** - * Returns a Uri for an icon suitable for display or null. - * - * @return An icon uri or null. - */ - public @Nullable Uri getIconUri() { - return mIconUri; - } - - /** - * Returns any extras that were added to the description. - * - * @return A bundle of extras or null. - */ - public @Nullable Bundle getExtras() { - return mExtras; - } - - /** - * Returns a Uri representing this content or null. - * - * @return A media Uri or null. - */ - public @Nullable Uri getMediaUri() { - return mMediaUri; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mMediaId); - TextUtils.writeToParcel(mTitle, dest, 0); - TextUtils.writeToParcel(mSubtitle, dest, 0); - TextUtils.writeToParcel(mDescription, dest, 0); - dest.writeParcelable(mIcon, flags); - dest.writeParcelable(mIconUri, flags); - dest.writeBundle(mExtras); - dest.writeParcelable(mMediaUri, flags); - } - - @Override - public boolean equals(Object o) { - if (o == null) { - return false; - } - - if (!(o instanceof MediaDescription)){ - return false; - } - - final MediaDescription d = (MediaDescription) o; - - if (!String.valueOf(mTitle).equals(String.valueOf(d.mTitle))) { - return false; - } - - if (!String.valueOf(mSubtitle).equals(String.valueOf(d.mSubtitle))) { - return false; - } - - if (!String.valueOf(mDescription).equals(String.valueOf(d.mDescription))) { - return false; - } - - return true; - } - - @Override - public String toString() { - return mTitle + ", " + mSubtitle + ", " + mDescription; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public MediaDescription createFromParcel(Parcel in) { - return new MediaDescription(in); - } - - @Override - public MediaDescription[] newArray(int size) { - return new MediaDescription[size]; - } - }; - - /** - * Builder for {@link MediaDescription} objects. - */ - public static class Builder { - private String mMediaId; - private CharSequence mTitle; - private CharSequence mSubtitle; - private CharSequence mDescription; - private Bitmap mIcon; - private Uri mIconUri; - private Bundle mExtras; - private Uri mMediaUri; - - /** - * Creates an initially empty builder. - */ - public Builder() { - } - - /** - * Sets the media id. - * - * @param mediaId The unique id for the item or null. - * @return this - */ - public Builder setMediaId(@Nullable String mediaId) { - mMediaId = mediaId; - return this; - } - - /** - * Sets the title. - * - * @param title A title suitable for display to the user or null. - * @return this - */ - public Builder setTitle(@Nullable CharSequence title) { - mTitle = title; - return this; - } - - /** - * Sets the subtitle. - * - * @param subtitle A subtitle suitable for display to the user or null. - * @return this - */ - public Builder setSubtitle(@Nullable CharSequence subtitle) { - mSubtitle = subtitle; - return this; - } - - /** - * Sets the description. - * - * @param description A description suitable for display to the user or - * null. - * @return this - */ - public Builder setDescription(@Nullable CharSequence description) { - mDescription = description; - return this; - } - - /** - * Sets the icon. - * - * @param icon A {@link Bitmap} icon suitable for display to the user or - * null. - * @return this - */ - public Builder setIconBitmap(@Nullable Bitmap icon) { - mIcon = icon; - return this; - } - - /** - * Sets the icon uri. - * - * @param iconUri A {@link Uri} for an icon suitable for display to the - * user or null. - * @return this - */ - public Builder setIconUri(@Nullable Uri iconUri) { - mIconUri = iconUri; - return this; - } - - /** - * Sets a bundle of extras. - * - * @param extras The extras to include with this description or null. - * @return this - */ - public Builder setExtras(@Nullable Bundle extras) { - mExtras = extras; - return this; - } - - /** - * Sets the media uri. - * - * @param mediaUri The content's {@link Uri} for the item or null. - * @return this - */ - public Builder setMediaUri(@Nullable Uri mediaUri) { - mMediaUri = mediaUri; - return this; - } - - public MediaDescription build() { - return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri, - mExtras, mMediaUri); - } - } -} diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl deleted file mode 100644 index 66ee483041..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2014, 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; - -parcelable MediaMetadata; diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java deleted file mode 100644 index adfd20b95f..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java +++ /dev/null @@ -1,941 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.NonNull; -import android.annotation.StringDef; -import android.annotation.UnsupportedAppUsage; -import android.content.ContentResolver; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.browse.MediaBrowser; -import android.media.session.MediaController; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Set; -import java.util.Objects; - -/** - * Contains metadata about an item, such as the title, artist, etc. - */ -public final class MediaMetadata implements Parcelable { - private static final String TAG = "MediaMetadata"; - - /** - * @hide - */ - @StringDef(prefix = { "METADATA_KEY_" }, value = { - METADATA_KEY_TITLE, - METADATA_KEY_ARTIST, - METADATA_KEY_ALBUM, - METADATA_KEY_AUTHOR, - METADATA_KEY_WRITER, - METADATA_KEY_COMPOSER, - METADATA_KEY_COMPILATION, - METADATA_KEY_DATE, - METADATA_KEY_GENRE, - METADATA_KEY_ALBUM_ARTIST, - METADATA_KEY_ART_URI, - METADATA_KEY_ALBUM_ART_URI, - METADATA_KEY_DISPLAY_TITLE, - METADATA_KEY_DISPLAY_SUBTITLE, - METADATA_KEY_DISPLAY_DESCRIPTION, - METADATA_KEY_DISPLAY_ICON_URI, - METADATA_KEY_MEDIA_ID, - METADATA_KEY_MEDIA_URI, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TextKey {} - - /** - * @hide - */ - @StringDef(prefix = { "METADATA_KEY_" }, value = { - METADATA_KEY_DURATION, - METADATA_KEY_YEAR, - METADATA_KEY_TRACK_NUMBER, - METADATA_KEY_NUM_TRACKS, - METADATA_KEY_DISC_NUMBER, - METADATA_KEY_BT_FOLDER_TYPE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LongKey {} - - /** - * @hide - */ - @StringDef(prefix = { "METADATA_KEY_" }, value = { - METADATA_KEY_ART, - METADATA_KEY_ALBUM_ART, - METADATA_KEY_DISPLAY_ICON, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BitmapKey {} - - /** - * @hide - */ - @StringDef(prefix = { "METADATA_KEY_" }, value = { - METADATA_KEY_USER_RATING, - METADATA_KEY_RATING, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RatingKey {} - - /** - * The title of the media. - */ - public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; - - /** - * The artist of the media. - */ - public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; - - /** - * The duration of the media in ms. A negative duration indicates that the - * duration is unknown (or infinite). - */ - public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; - - /** - * The album title for the media. - */ - public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; - - /** - * The author of the media. - */ - public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; - - /** - * The writer of the media. - */ - public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; - - /** - * The composer of the media. - */ - public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; - - /** - * The compilation status of the media. - */ - public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; - - /** - * The date the media was created or published. The format is unspecified - * but RFC 3339 is recommended. - */ - public static final String METADATA_KEY_DATE = "android.media.metadata.DATE"; - - /** - * The year the media was created or published as a long. - */ - public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; - - /** - * The genre of the media. - */ - public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; - - /** - * The track number for the media. - */ - public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; - - /** - * The number of tracks in the media's original source. - */ - public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; - - /** - * The disc number for the media's original source. - */ - public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; - - /** - * The artist for the album of the media's original source. - */ - public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; - - /** - * The artwork for the media as a {@link Bitmap}. - *

    - * The artwork should be relatively small and may be scaled down by the - * system if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_ART_URI} should be used instead. - */ - public static final String METADATA_KEY_ART = "android.media.metadata.ART"; - - /** - * The artwork for the media as a Uri formatted String. The artwork can be - * loaded using a combination of {@link ContentResolver#openInputStream} and - * {@link BitmapFactory#decodeStream}. - *

    - * For the best results, Uris should use the content:// style and support - * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through - * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. - */ - public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; - - /** - * The artwork for the album of the media's original source as a - * {@link Bitmap}. - *

    - * The artwork should be relatively small and may be scaled down by the - * system if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead. - */ - public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; - - /** - * The artwork for the album of the media's original source as a Uri - * formatted String. The artwork can be loaded using a combination of - * {@link ContentResolver#openInputStream} and - * {@link BitmapFactory#decodeStream}. - *

    - * For the best results, Uris should use the content:// style and support - * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through - * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. - */ - public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; - - /** - * The user's rating for the media. - * - * @see Rating - */ - public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; - - /** - * The overall rating for the media. - * - * @see Rating - */ - public static final String METADATA_KEY_RATING = "android.media.metadata.RATING"; - - /** - * A title that is suitable for display to the user. This will generally be - * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats. - * When displaying media described by this metadata this should be preferred - * if present. - */ - public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"; - - /** - * A subtitle that is suitable for display to the user. When displaying a - * second line for media described by this metadata this should be preferred - * to other fields if present. - */ - public static final String METADATA_KEY_DISPLAY_SUBTITLE - = "android.media.metadata.DISPLAY_SUBTITLE"; - - /** - * A description that is suitable for display to the user. When displaying - * more information for media described by this metadata this should be - * preferred to other fields if present. - */ - public static final String METADATA_KEY_DISPLAY_DESCRIPTION - = "android.media.metadata.DISPLAY_DESCRIPTION"; - - /** - * An icon or thumbnail that is suitable for display to the user. When - * displaying an icon for media described by this metadata this should be - * preferred to other fields if present. This must be a {@link Bitmap}. - *

    - * The icon should be relatively small and may be scaled down by the system - * if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead. - */ - public static final String METADATA_KEY_DISPLAY_ICON - = "android.media.metadata.DISPLAY_ICON"; - - /** - * A Uri formatted String for an icon or thumbnail that is suitable for - * display to the user. When displaying more information for media described - * by this metadata the display description should be preferred to other - * fields when present. The icon can be loaded using a combination of - * {@link ContentResolver#openInputStream} and - * {@link BitmapFactory#decodeStream}. - *

    - * For the best results, Uris should use the content:// style and support - * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through - * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}. - */ - public static final String METADATA_KEY_DISPLAY_ICON_URI - = "android.media.metadata.DISPLAY_ICON_URI"; - - /** - * A String key for identifying the content. This value is specific to the - * service providing the content. If used, this should be a persistent - * unique key for the underlying content. It may be used with - * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)} - * to initiate playback when provided by a {@link MediaBrowser} connected to - * the same app. - */ - public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"; - - /** - * A Uri formatted String representing the content. This value is specific to the - * service providing the content. It may be used with - * {@link MediaController.TransportControls#playFromUri(Uri, Bundle)} - * to initiate playback when provided by a {@link MediaBrowser} connected to - * the same app. - */ - public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI"; - - /** - * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth - * AVRCP 1.5. It should be one of the following: - *

      - *
    • {@link MediaDescription#BT_FOLDER_TYPE_MIXED}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_TITLES}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_ALBUMS}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_ARTISTS}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_GENRES}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_PLAYLISTS}
    • - *
    • {@link MediaDescription#BT_FOLDER_TYPE_YEARS}
    • - *
    - */ - public static final String METADATA_KEY_BT_FOLDER_TYPE - = "android.media.metadata.BT_FOLDER_TYPE"; - - private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = { - METADATA_KEY_TITLE, - METADATA_KEY_ARTIST, - METADATA_KEY_ALBUM, - METADATA_KEY_ALBUM_ARTIST, - METADATA_KEY_WRITER, - METADATA_KEY_AUTHOR, - METADATA_KEY_COMPOSER - }; - - private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = { - METADATA_KEY_DISPLAY_ICON, - METADATA_KEY_ART, - METADATA_KEY_ALBUM_ART - }; - - private static final @TextKey String[] PREFERRED_URI_ORDER = { - METADATA_KEY_DISPLAY_ICON_URI, - METADATA_KEY_ART_URI, - METADATA_KEY_ALBUM_ART_URI - }; - - private static final int METADATA_TYPE_INVALID = -1; - private static final int METADATA_TYPE_LONG = 0; - private static final int METADATA_TYPE_TEXT = 1; - private static final int METADATA_TYPE_BITMAP = 2; - private static final int METADATA_TYPE_RATING = 3; - private static final ArrayMap METADATA_KEYS_TYPE; - - static { - METADATA_KEYS_TYPE = new ArrayMap(); - METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING); - METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP); - METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG); - METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT); - METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT); - } - - private static final SparseArray EDITOR_KEY_MAPPING; - - static { - EDITOR_KEY_MAPPING = new SparseArray(); - EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART); - EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING); - EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, - METADATA_KEY_ALBUM_ARTIST); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, - METADATA_KEY_TRACK_NUMBER); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION, - METADATA_KEY_COMPILATION); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, - METADATA_KEY_DISC_NUMBER); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, - METADATA_KEY_NUM_TRACKS); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER); - EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR); - } - - private final Bundle mBundle; - private MediaDescription mDescription; - - private MediaMetadata(Bundle bundle) { - mBundle = new Bundle(bundle); - } - - private MediaMetadata(Parcel in) { - mBundle = in.readBundle(); - } - - /** - * Returns true if the given key is contained in the metadata - * - * @param key a String key - * @return true if the key exists in this metadata, false otherwise - */ - public boolean containsKey(String key) { - return mBundle.containsKey(key); - } - - /** - * Returns the value associated with the given key, or null if no mapping of - * the desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key The key the value is stored under - * @return a CharSequence value, or null - */ - public CharSequence getText(@TextKey String key) { - return mBundle.getCharSequence(key); - } - - /** - * Returns the text value associated with the given key as a String, or null - * if no mapping of the desired type exists for the given key or a null - * value is explicitly associated with the key. This is equivalent to - * calling {@link #getText getText().toString()} if the value is not null. - * - * @param key The key the value is stored under - * @return a String value, or null - */ - public String getString(@TextKey String key) { - CharSequence text = getText(key); - if (text != null) { - return text.toString(); - } - return null; - } - - /** - * Returns the value associated with the given key, or 0L if no long exists - * for the given key. - * - * @param key The key the value is stored under - * @return a long value - */ - public long getLong(@LongKey String key) { - return mBundle.getLong(key, 0); - } - - /** - * Returns a {@link Rating} for the given key or null if no rating exists - * for the given key. - * - * @param key The key the value is stored under - * @return A {@link Rating} or null - */ - public Rating getRating(@RatingKey String key) { - Rating rating = null; - try { - rating = mBundle.getParcelable(key); - } catch (Exception e) { - // ignore, value was not a bitmap - Log.w(TAG, "Failed to retrieve a key as Rating.", e); - } - return rating; - } - - /** - * Returns a {@link Bitmap} for the given key or null if no bitmap exists - * for the given key. - * - * @param key The key the value is stored under - * @return A {@link Bitmap} or null - */ - public Bitmap getBitmap(@BitmapKey String key) { - Bitmap bmp = null; - try { - bmp = mBundle.getParcelable(key); - } catch (Exception e) { - // ignore, value was not a bitmap - Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); - } - return bmp; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeBundle(mBundle); - } - - /** - * Returns the number of fields in this metadata. - * - * @return The number of fields in the metadata. - */ - public int size() { - return mBundle.size(); - } - - /** - * Returns a Set containing the Strings used as keys in this metadata. - * - * @return a Set of String keys - */ - public Set keySet() { - return mBundle.keySet(); - } - - /** - * Returns a simple description of this metadata for display purposes. - * - * @return A simple description of this metadata. - */ - public @NonNull MediaDescription getDescription() { - if (mDescription != null) { - return mDescription; - } - - String mediaId = getString(METADATA_KEY_MEDIA_ID); - - CharSequence[] text = new CharSequence[3]; - Bitmap icon = null; - Uri iconUri = null; - - // First handle the case where display data is set already - CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE); - if (!TextUtils.isEmpty(displayText)) { - // If they have a display title use only display data, otherwise use - // our best bets - text[0] = displayText; - text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE); - text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION); - } else { - // Use whatever fields we can - int textIndex = 0; - int keyIndex = 0; - while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) { - CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]); - if (!TextUtils.isEmpty(next)) { - // Fill in the next empty bit of text - text[textIndex++] = next; - } - } - } - - // Get the best art bitmap we can find - for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) { - Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]); - if (next != null) { - icon = next; - break; - } - } - - // Get the best Uri we can find - for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) { - String next = getString(PREFERRED_URI_ORDER[i]); - if (!TextUtils.isEmpty(next)) { - iconUri = Uri.parse(next); - break; - } - } - - Uri mediaUri = null; - String mediaUriStr = getString(METADATA_KEY_MEDIA_URI); - if (!TextUtils.isEmpty(mediaUriStr)) { - mediaUri = Uri.parse(mediaUriStr); - } - - MediaDescription.Builder bob = new MediaDescription.Builder(); - bob.setMediaId(mediaId); - bob.setTitle(text[0]); - bob.setSubtitle(text[1]); - bob.setDescription(text[2]); - bob.setIconBitmap(icon); - bob.setIconUri(iconUri); - bob.setMediaUri(mediaUri); - if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) { - Bundle bundle = new Bundle(); - bundle.putLong(MediaDescription.EXTRA_BT_FOLDER_TYPE, - getLong(METADATA_KEY_BT_FOLDER_TYPE)); - bob.setExtras(bundle); - } - mDescription = bob.build(); - - return mDescription; - } - - /** - * Helper for getting the String key used by {@link MediaMetadata} from the - * integer key that {@link MediaMetadataEditor} uses. - * - * @param editorKey The key used by the editor - * @return The key used by this class or null if no mapping exists - * @hide - */ - @UnsupportedAppUsage - public static String getKeyFromMetadataEditorKey(int editorKey) { - return EDITOR_KEY_MAPPING.get(editorKey, null); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public MediaMetadata createFromParcel(Parcel in) { - return new MediaMetadata(in); - } - - @Override - public MediaMetadata[] newArray(int size) { - return new MediaMetadata[size]; - } - }; - - /** - * Compares the contents of this object to another MediaMetadata object. It - * does not compare Bitmaps and Ratings as the media player can choose to - * forgo these fields depending on how you retrieve the MediaMetadata. - * - * @param o The Metadata object to compare this object against - * @return Whether or not the two objects have matching fields (excluding - * Bitmaps and Ratings) - */ - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (!(o instanceof MediaMetadata)) { - return false; - } - - final MediaMetadata m = (MediaMetadata) o; - - for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { - String key = METADATA_KEYS_TYPE.keyAt(i); - switch (METADATA_KEYS_TYPE.valueAt(i)) { - case METADATA_TYPE_TEXT: - if (!Objects.equals(getString(key), m.getString(key))) { - return false; - } - break; - case METADATA_TYPE_LONG: - if (getLong(key) != m.getLong(key)) { - return false; - } - break; - default: - // Ignore ratings and bitmaps when comparing - break; - } - } - - return true; - } - - @Override - public int hashCode() { - int hashCode = 17; - - for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { - String key = METADATA_KEYS_TYPE.keyAt(i); - switch (METADATA_KEYS_TYPE.valueAt(i)) { - case METADATA_TYPE_TEXT: - hashCode = 31 * hashCode + Objects.hash(getString(key)); - break; - case METADATA_TYPE_LONG: - hashCode = 31 * hashCode + Long.hashCode(getLong(key)); - break; - default: - // Ignore ratings and bitmaps when comparing - break; - } - } - - return hashCode; - } - - /** - * Use to build MediaMetadata objects. The system defined metadata keys must - * use the appropriate data type. - */ - public static final class Builder { - private final Bundle mBundle; - - /** - * Create an empty Builder. Any field that should be included in the - * {@link MediaMetadata} must be added. - */ - public Builder() { - mBundle = new Bundle(); - } - - /** - * Create a Builder using a {@link MediaMetadata} instance to set the - * initial values. All fields in the source metadata will be included in - * the new metadata. Fields can be overwritten by adding the same key. - * - * @param source - */ - public Builder(MediaMetadata source) { - mBundle = new Bundle(source.mBundle); - } - - /** - * Create a Builder using a {@link MediaMetadata} instance to set - * initial values, but replace bitmaps with a scaled down copy if they - * are larger than maxBitmapSize. - * - * @param source The original metadata to copy. - * @param maxBitmapSize The maximum height/width for bitmaps contained - * in the metadata. - * @hide - */ - public Builder(MediaMetadata source, int maxBitmapSize) { - this(source); - for (String key : mBundle.keySet()) { - Object value = mBundle.get(key); - if (value != null && value instanceof Bitmap) { - Bitmap bmp = (Bitmap) value; - if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { - putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); - } - } - } - } - - /** - * Put a CharSequence value into the metadata. Custom keys may be used, - * but if the METADATA_KEYs defined in this class are used they may only - * be one of the following: - *
      - *
    • {@link #METADATA_KEY_TITLE}
    • - *
    • {@link #METADATA_KEY_ARTIST}
    • - *
    • {@link #METADATA_KEY_ALBUM}
    • - *
    • {@link #METADATA_KEY_AUTHOR}
    • - *
    • {@link #METADATA_KEY_WRITER}
    • - *
    • {@link #METADATA_KEY_COMPOSER}
    • - *
    • {@link #METADATA_KEY_DATE}
    • - *
    • {@link #METADATA_KEY_GENRE}
    • - *
    • {@link #METADATA_KEY_ALBUM_ARTIST}
    • - *
    • {@link #METADATA_KEY_ART_URI}
    • - *
    • {@link #METADATA_KEY_ALBUM_ART_URI}
    • - *
    • {@link #METADATA_KEY_DISPLAY_TITLE}
    • - *
    • {@link #METADATA_KEY_DISPLAY_SUBTITLE}
    • - *
    • {@link #METADATA_KEY_DISPLAY_DESCRIPTION}
    • - *
    • {@link #METADATA_KEY_DISPLAY_ICON_URI}
    • - *
    - * - * @param key The key for referencing this value - * @param value The CharSequence value to store - * @return The Builder to allow chaining - */ - public Builder putText(@TextKey String key, CharSequence value) { - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a CharSequence"); - } - } - mBundle.putCharSequence(key, value); - return this; - } - - /** - * Put a String value into the metadata. Custom keys may be used, but if - * the METADATA_KEYs defined in this class are used they may only be one - * of the following: - *
      - *
    • {@link #METADATA_KEY_TITLE}
    • - *
    • {@link #METADATA_KEY_ARTIST}
    • - *
    • {@link #METADATA_KEY_ALBUM}
    • - *
    • {@link #METADATA_KEY_AUTHOR}
    • - *
    • {@link #METADATA_KEY_WRITER}
    • - *
    • {@link #METADATA_KEY_COMPOSER}
    • - *
    • {@link #METADATA_KEY_DATE}
    • - *
    • {@link #METADATA_KEY_GENRE}
    • - *
    • {@link #METADATA_KEY_ALBUM_ARTIST}
    • - *
    • {@link #METADATA_KEY_ART_URI}
    • - *
    • {@link #METADATA_KEY_ALBUM_ART_URI}
    • - *
    • {@link #METADATA_KEY_DISPLAY_TITLE}
    • - *
    • {@link #METADATA_KEY_DISPLAY_SUBTITLE}
    • - *
    • {@link #METADATA_KEY_DISPLAY_DESCRIPTION}
    • - *
    • {@link #METADATA_KEY_DISPLAY_ICON_URI}
    • - *
    - *

    - * Uris for artwork should use the content:// style and support - * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork - * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri, - * String, Bundle)}. - * - * @param key The key for referencing this value - * @param value The String value to store - * @return The Builder to allow chaining - */ - public Builder putString(@TextKey String key, String value) { - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a String"); - } - } - mBundle.putCharSequence(key, value); - return this; - } - - /** - * Put a long value into the metadata. Custom keys may be used, but if - * the METADATA_KEYs defined in this class are used they may only be one - * of the following: - *

      - *
    • {@link #METADATA_KEY_DURATION}
    • - *
    • {@link #METADATA_KEY_TRACK_NUMBER}
    • - *
    • {@link #METADATA_KEY_NUM_TRACKS}
    • - *
    • {@link #METADATA_KEY_DISC_NUMBER}
    • - *
    • {@link #METADATA_KEY_YEAR}
    • - *
    - * - * @param key The key for referencing this value - * @param value The long value to store - * @return The Builder to allow chaining - */ - public Builder putLong(@LongKey String key, long value) { - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a long"); - } - } - mBundle.putLong(key, value); - return this; - } - - /** - * Put a {@link Rating} into the metadata. Custom keys may be used, but - * if the METADATA_KEYs defined in this class are used they may only be - * one of the following: - *
      - *
    • {@link #METADATA_KEY_RATING}
    • - *
    • {@link #METADATA_KEY_USER_RATING}
    • - *
    - * - * @param key The key for referencing this value - * @param value The Rating value to store - * @return The Builder to allow chaining - */ - public Builder putRating(@RatingKey String key, Rating value) { - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a Rating"); - } - } - mBundle.putParcelable(key, value); - return this; - } - - /** - * Put a {@link Bitmap} into the metadata. Custom keys may be used, but - * if the METADATA_KEYs defined in this class are used they may only be - * one of the following: - *
      - *
    • {@link #METADATA_KEY_ART}
    • - *
    • {@link #METADATA_KEY_ALBUM_ART}
    • - *
    • {@link #METADATA_KEY_DISPLAY_ICON}
    • - *
    - *

    - * Large bitmaps may be scaled down by the system when - * {@link android.media.session.MediaSession#setMetadata} is called. - * To pass full resolution images {@link Uri Uris} should be used with - * {@link #putString}. - * - * @param key The key for referencing this value - * @param value The Bitmap to store - * @return The Builder to allow chaining - */ - public Builder putBitmap(@BitmapKey String key, Bitmap value) { - if (METADATA_KEYS_TYPE.containsKey(key)) { - if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { - throw new IllegalArgumentException("The " + key - + " key cannot be used to put a Bitmap"); - } - } - mBundle.putParcelable(key, value); - return this; - } - - /** - * Creates a {@link MediaMetadata} instance with the specified fields. - * - * @return The new MediaMetadata instance - */ - public MediaMetadata build() { - return new MediaMetadata(mBundle); - } - - private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { - float maxSizeF = maxSize; - float widthScale = maxSizeF / bmp.getWidth(); - float heightScale = maxSizeF / bmp.getHeight(); - float scale = Math.min(widthScale, heightScale); - int height = (int) (bmp.getHeight() * scale); - int width = (int) (bmp.getWidth() * scale); - return Bitmap.createScaledBitmap(bmp, width, height, true); - } - } -} diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl deleted file mode 100644 index 228ea9ca31..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* 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. - */ - -package android.media; - -/** @hide */ -parcelable MediaParceledListSlice; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java b/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java deleted file mode 100644 index ec3fdb71e2..0000000000 --- a/packages/MediaComponents/apex/java/android/media/MediaParceledListSlice.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.annotation.UnsupportedAppUsage; -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Transfer a large list of objects across an IPC. Splits into multiple transactions if needed. - * Note: Only use classes declared final in order to avoid subclasses overriding reading/writing - * parcel logic. - * - * TODO: Add test for sending large data - * @hide - */ -public class MediaParceledListSlice implements Parcelable { - private static final String TAG = "MediaParceledListSlice"; - private static final boolean DEBUG = false; - - private static final int MAX_IPC_SIZE = 64 * 1024; // IBinder.MAX_IPC_SIZE - - final List mList; - - public MediaParceledListSlice(List list) { - if (list == null) { - throw new IllegalArgumentException("list shouldn't be null"); - } - mList = list; - } - - MediaParceledListSlice(Parcel p) { - final int itemCount = p.readInt(); - mList = new ArrayList<>(itemCount); - if (DEBUG) { - Log.d(TAG, "Retrieving " + itemCount + " items"); - } - if (itemCount <= 0) { - return; - } - - int i = 0; - while (i < itemCount) { - if (p.readInt() == 0) { - break; - } - - final T parcelable = p.readParcelable(null); - mList.add(parcelable); - - if (DEBUG) { - Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1)); - } - i++; - } - if (i >= itemCount) { - return; - } - final IBinder retriever = p.readStrongBinder(); - while (i < itemCount) { - if (DEBUG) { - Log.d(TAG, "Reading more @" + i + " of " + itemCount + ": retriever=" + retriever); - } - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInt(i); - try { - retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failure retrieving array; only received " + i + " of " + itemCount, e); - return; - } - while (i < itemCount && reply.readInt() != 0) { - final T parcelable = reply.readParcelable(null); - mList.add(parcelable); - - if (DEBUG) { - Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1)); - } - i++; - } - reply.recycle(); - data.recycle(); - } - } - - public List getList() { - return mList; - } - - /** - * Write this to another Parcel. Note that this discards the internal Parcel - * and should not be used anymore. This is so we can pass this to a Binder - * where we won't have a chance to call recycle on this. - */ - @Override - public void writeToParcel(Parcel dest, int flags) { - final int itemCount = mList.size(); - dest.writeInt(itemCount); - if (DEBUG) { - Log.d(TAG, "Writing " + itemCount + " items"); - } - if (itemCount > 0) { - int i = 0; - while (i < itemCount && dest.dataSize() < MAX_IPC_SIZE) { - dest.writeInt(1); - - final T parcelable = mList.get(i); - dest.writeParcelable(parcelable, flags); - - if (DEBUG) { - Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); - } - i++; - } - if (i < itemCount) { - dest.writeInt(0); - Binder retriever = new Binder() { - @Override - protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - if (code != FIRST_CALL_TRANSACTION) { - return super.onTransact(code, data, reply, flags); - } - int i = data.readInt(); - if (DEBUG) { - Log.d(TAG, "Writing more @" + i + " of " + itemCount); - } - while (i < itemCount && reply.dataSize() < MAX_IPC_SIZE) { - reply.writeInt(1); - - final T parcelable = mList.get(i); - reply.writeParcelable(parcelable, flags); - - if (DEBUG) { - Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); - } - i++; - } - if (i < itemCount) { - if (DEBUG) { - Log.d(TAG, "Breaking @" + i + " of " + itemCount); - } - reply.writeInt(0); - } - return true; - } - }; - if (DEBUG) { - Log.d(TAG, "Breaking @" + i + " of " + itemCount + ": retriever=" + retriever); - } - dest.writeStrongBinder(retriever); - } - } - } - - @Override - public int describeContents() { - int contents = 0; - final List list = getList(); - for (int i = 0; i < list.size(); i++) { - contents |= list.get(i).describeContents(); - } - return contents; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public MediaParceledListSlice createFromParcel(Parcel in) { - return new MediaParceledListSlice(in); - } - - @Override - public MediaParceledListSlice[] newArray(int size) { - return new MediaParceledListSlice[size]; - } - }; -} diff --git a/packages/MediaComponents/apex/java/android/media/Rating.aidl b/packages/MediaComponents/apex/java/android/media/Rating.aidl deleted file mode 100644 index 1dc336aeb7..0000000000 --- a/packages/MediaComponents/apex/java/android/media/Rating.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -parcelable Rating; diff --git a/packages/MediaComponents/apex/java/android/media/Rating.java b/packages/MediaComponents/apex/java/android/media/Rating.java deleted file mode 100644 index 04d5364f7c..0000000000 --- a/packages/MediaComponents/apex/java/android/media/Rating.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.annotation.IntDef; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * A class to encapsulate rating information used as content metadata. - * A rating is defined by its rating style (see {@link #RATING_HEART}, - * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, - * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may - * be defined as "unrated"), both of which are defined when the rating instance is constructed - * through one of the factory methods. - */ -public final class Rating implements Parcelable { - private final static String TAG = "Rating"; - - /** - * @hide - */ - @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS, - RATING_5_STARS, RATING_PERCENTAGE}) - @Retention(RetentionPolicy.SOURCE) - public @interface Style {} - - /** - * @hide - */ - @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS}) - @Retention(RetentionPolicy.SOURCE) - public @interface StarStyle {} - - /** - * Indicates a rating style is not supported. A Rating will never have this - * type, but can be used by other classes to indicate they do not support - * Rating. - */ - public final static int RATING_NONE = 0; - - /** - * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to - * indicate the content referred to is a favorite (or not). - */ - public final static int RATING_HEART = 1; - - /** - * A rating style for "thumb up" vs "thumb down". - */ - public final static int RATING_THUMB_UP_DOWN = 2; - - /** - * A rating style with 0 to 3 stars. - */ - public final static int RATING_3_STARS = 3; - - /** - * A rating style with 0 to 4 stars. - */ - public final static int RATING_4_STARS = 4; - - /** - * A rating style with 0 to 5 stars. - */ - public final static int RATING_5_STARS = 5; - - /** - * A rating style expressed as a percentage. - */ - public final static int RATING_PERCENTAGE = 6; - - private final static float RATING_NOT_RATED = -1.0f; - - private final int mRatingStyle; - - private final float mRatingValue; - - private Rating(@Style int ratingStyle, float rating) { - mRatingStyle = ratingStyle; - mRatingValue = rating; - } - - @Override - public String toString() { - return "Rating:style=" + mRatingStyle + " rating=" - + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue)); - } - - @Override - public int describeContents() { - return mRatingStyle; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRatingStyle); - dest.writeFloat(mRatingValue); - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - /** - * Rebuilds a Rating previously stored with writeToParcel(). - * @param p Parcel object to read the Rating from - * @return a new Rating created from the data in the parcel - */ - @Override - public Rating createFromParcel(Parcel p) { - return new Rating(p.readInt(), p.readFloat()); - } - - @Override - public Rating[] newArray(int size) { - return new Rating[size]; - } - }; - - /** - * Return a Rating instance with no rating. - * Create and return a new Rating instance with no rating known for the given - * rating style. - * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, - * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, - * or {@link #RATING_PERCENTAGE}. - * @return null if an invalid rating style is passed, a new Rating instance otherwise. - */ - public static Rating newUnratedRating(@Style int ratingStyle) { - switch(ratingStyle) { - case RATING_HEART: - case RATING_THUMB_UP_DOWN: - case RATING_3_STARS: - case RATING_4_STARS: - case RATING_5_STARS: - case RATING_PERCENTAGE: - return new Rating(ratingStyle, RATING_NOT_RATED); - default: - return null; - } - } - - /** - * Return a Rating instance with a heart-based rating. - * Create and return a new Rating instance with a rating style of {@link #RATING_HEART}, - * and a heart-based rating. - * @param hasHeart true for a "heart selected" rating, false for "heart unselected". - * @return a new Rating instance. - */ - public static Rating newHeartRating(boolean hasHeart) { - return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f); - } - - /** - * Return a Rating instance with a thumb-based rating. - * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN} - * rating style, and a "thumb up" or "thumb down" rating. - * @param thumbIsUp true for a "thumb up" rating, false for "thumb down". - * @return a new Rating instance. - */ - public static Rating newThumbRating(boolean thumbIsUp) { - return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f); - } - - /** - * Return a Rating instance with a star-based rating. - * Create and return a new Rating instance with one of the star-base rating styles - * and the given integer or fractional number of stars. Non integer values can for instance - * be used to represent an average rating value, which might not be an integer number of stars. - * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, - * {@link #RATING_5_STARS}. - * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to - * the rating style. - * @return null if the rating style is invalid, or the rating is out of range, - * a new Rating instance otherwise. - */ - public static Rating newStarRating(@StarStyle int starRatingStyle, float starRating) { - float maxRating = -1.0f; - switch(starRatingStyle) { - case RATING_3_STARS: - maxRating = 3.0f; - break; - case RATING_4_STARS: - maxRating = 4.0f; - break; - case RATING_5_STARS: - maxRating = 5.0f; - break; - default: - Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); - return null; - } - if ((starRating < 0.0f) || (starRating > maxRating)) { - Log.e(TAG, "Trying to set out of range star-based rating"); - return null; - } - return new Rating(starRatingStyle, starRating); - } - - /** - * Return a Rating instance with a percentage-based rating. - * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE} - * rating style, and a rating of the given percentage. - * @param percent the value of the rating - * @return null if the rating is out of range, a new Rating instance otherwise. - */ - public static Rating newPercentageRating(float percent) { - if ((percent < 0.0f) || (percent > 100.0f)) { - Log.e(TAG, "Invalid percentage-based rating value"); - return null; - } else { - return new Rating(RATING_PERCENTAGE, percent); - } - } - - /** - * Return whether there is a rating value available. - * @return true if the instance was not created with {@link #newUnratedRating(int)}. - */ - public boolean isRated() { - return mRatingValue >= 0.0f; - } - - /** - * Return the rating style. - * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, - * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, - * or {@link #RATING_PERCENTAGE}. - */ - @Style - public int getRatingStyle() { - return mRatingStyle; - } - - /** - * Return whether the rating is "heart selected". - * @return true if the rating is "heart selected", false if the rating is "heart unselected", - * if the rating style is not {@link #RATING_HEART} or if it is unrated. - */ - public boolean hasHeart() { - if (mRatingStyle != RATING_HEART) { - return false; - } else { - return (mRatingValue == 1.0f); - } - } - - /** - * Return whether the rating is "thumb up". - * @return true if the rating is "thumb up", false if the rating is "thumb down", - * if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated. - */ - public boolean isThumbUp() { - if (mRatingStyle != RATING_THUMB_UP_DOWN) { - return false; - } else { - return (mRatingValue == 1.0f); - } - } - - /** - * Return the star-based rating value. - * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is - * not star-based, or if it is unrated. - */ - public float getStarRating() { - switch (mRatingStyle) { - case RATING_3_STARS: - case RATING_4_STARS: - case RATING_5_STARS: - if (isRated()) { - return mRatingValue; - } - default: - return -1.0f; - } - } - - /** - * Return the percentage-based rating value. - * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is - * not percentage-based, or if it is unrated. - */ - public float getPercentRating() { - if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) { - return -1.0f; - } else { - return mRatingValue; - } - } -} diff --git a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java deleted file mode 100644 index 1c017c564b..0000000000 --- a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.IntDef; -import android.media.session.MediaSession; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Handles requests to adjust or set the volume on a session. This is also used - * to push volume updates back to the session. The provider must call - * {@link #setCurrentVolume(int)} each time the volume being provided changes. - *

    - * You can set a volume provider on a session by calling - * {@link MediaSession#setPlaybackToRemote}. - */ -public abstract class VolumeProvider { - - /** - * @hide - */ - @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE}) - @Retention(RetentionPolicy.SOURCE) - public @interface ControlType {} - - /** - * The volume is fixed and can not be modified. Requests to change volume - * should be ignored. - */ - public static final int VOLUME_CONTROL_FIXED = 0; - - /** - * The volume control uses relative adjustment via - * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific - * value should be ignored. - */ - public static final int VOLUME_CONTROL_RELATIVE = 1; - - /** - * The volume control uses an absolute value. It may be adjusted using - * {@link #onAdjustVolume(int)} or set directly using - * {@link #onSetVolumeTo(int)}. - */ - public static final int VOLUME_CONTROL_ABSOLUTE = 2; - - private final int mControlType; - private final int mMaxVolume; - private int mCurrentVolume; - private Callback mCallback; - - /** - * Create a new volume provider for handling volume events. You must specify - * the type of volume control, the maximum volume that can be used, and the - * current volume on the output. - * - * @param volumeControl The method for controlling volume that is used by - * this provider. - * @param maxVolume The maximum allowed volume. - * @param currentVolume The current volume on the output. - */ - public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) { - mControlType = volumeControl; - mMaxVolume = maxVolume; - mCurrentVolume = currentVolume; - } - - /** - * Get the volume control type that this volume provider uses. - * - * @return The volume control type for this volume provider - */ - @ControlType - public final int getVolumeControl() { - return mControlType; - } - - /** - * Get the maximum volume this provider allows. - * - * @return The max allowed volume. - */ - public final int getMaxVolume() { - return mMaxVolume; - } - - /** - * Gets the current volume. This will be the last value set by - * {@link #setCurrentVolume(int)}. - * - * @return The current volume. - */ - public final int getCurrentVolume() { - return mCurrentVolume; - } - - /** - * Notify the system that the current volume has been changed. This must be - * called every time the volume changes to ensure it is displayed properly. - * - * @param currentVolume The current volume on the output. - */ - public final void setCurrentVolume(int currentVolume) { - mCurrentVolume = currentVolume; - if (mCallback != null) { - mCallback.onVolumeChanged(this); - } - } - - /** - * Override to handle requests to set the volume of the current output. - * After the volume has been modified {@link #setCurrentVolume} must be - * called to notify the system. - * - * @param volume The volume to set the output to. - */ - public void onSetVolumeTo(int volume) { - } - - /** - * Override to handle requests to adjust the volume of the current output. - * Direction will be one of {@link AudioManager#ADJUST_LOWER}, - * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}. - * After the volume has been modified {@link #setCurrentVolume} must be - * called to notify the system. - * - * @param direction The direction to change the volume in. - */ - public void onAdjustVolume(int direction) { - } - - /** - * Sets a callback to receive volume changes. - * @hide - */ - public void setCallback(Callback callback) { - mCallback = callback; - } - - /** - * Listens for changes to the volume. - * @hide - */ - public static abstract class Callback { - public abstract void onVolumeChanged(VolumeProvider volumeProvider); - } -} diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl deleted file mode 100644 index 782e09471a..0000000000 --- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2014, 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.browse; - -parcelable MediaBrowser.MediaItem; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java deleted file mode 100644 index b1b14c6e4b..0000000000 --- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * Copyright (C) 2014 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.browse; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.media.MediaDescription; -import android.media.MediaParceledListSlice; -import android.media.session.MediaController; -import android.media.session.MediaSession; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.service.media.IMediaBrowserService; -import android.service.media.IMediaBrowserServiceCallbacks; -import android.service.media.MediaBrowserService; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; - -/** - * Browses media content offered by a link MediaBrowserService. - *

    - * This object is not thread-safe. All calls should happen on the thread on which the browser - * was constructed. - *

    - *

    Standard Extra Data

    - * - *

    These are the current standard fields that can be used as extra data via - * {@link #subscribe(String, Bundle, SubscriptionCallback)}, - * {@link #unsubscribe(String, SubscriptionCallback)}, and - * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}. - * - *

      - *
    • {@link #EXTRA_PAGE} - *
    • {@link #EXTRA_PAGE_SIZE} - *
    - */ -public final class MediaBrowser { - private static final String TAG = "MediaBrowser"; - private static final boolean DBG = false; - - /** - * Used as an int extra field to denote the page number to subscribe. - * The value of {@code EXTRA_PAGE} should be greater than or equal to 0. - * - * @see #EXTRA_PAGE_SIZE - */ - public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE"; - - /** - * Used as an int extra field to denote the number of media items in a page. - * The value of {@code EXTRA_PAGE_SIZE} should be greater than or equal to 1. - * - * @see #EXTRA_PAGE - */ - public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE"; - - private static final int CONNECT_STATE_DISCONNECTING = 0; - private static final int CONNECT_STATE_DISCONNECTED = 1; - private static final int CONNECT_STATE_CONNECTING = 2; - private static final int CONNECT_STATE_CONNECTED = 3; - private static final int CONNECT_STATE_SUSPENDED = 4; - - private final Context mContext; - private final ComponentName mServiceComponent; - private final ConnectionCallback mCallback; - private final Bundle mRootHints; - private final Handler mHandler = new Handler(); - private final ArrayMap mSubscriptions = new ArrayMap<>(); - - private volatile int mState = CONNECT_STATE_DISCONNECTED; - private volatile String mRootId; - private volatile MediaSession.Token mMediaSessionToken; - private volatile Bundle mExtras; - - private MediaServiceConnection mServiceConnection; - private IMediaBrowserService mServiceBinder; - private IMediaBrowserServiceCallbacks mServiceCallbacks; - - /** - * Creates a media browser for the specified media browser service. - * - * @param context The context. - * @param serviceComponent The component name of the media browser service. - * @param callback The connection callback. - * @param rootHints An optional bundle of service-specific arguments to send - * to the media browser service when connecting and retrieving the root id - * for browsing, or null if none. The contents of this bundle may affect - * the information returned when browsing. - * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT - * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE - * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED - */ - public MediaBrowser(Context context, ComponentName serviceComponent, - ConnectionCallback callback, Bundle rootHints) { - if (context == null) { - throw new IllegalArgumentException("context must not be null"); - } - if (serviceComponent == null) { - throw new IllegalArgumentException("service component must not be null"); - } - if (callback == null) { - throw new IllegalArgumentException("connection callback must not be null"); - } - mContext = context; - mServiceComponent = serviceComponent; - mCallback = callback; - mRootHints = rootHints == null ? null : new Bundle(rootHints); - } - - /** - * Connects to the media browser service. - *

    - * The connection callback specified in the constructor will be invoked - * when the connection completes or fails. - *

    - */ - public void connect() { - if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { - throw new IllegalStateException("connect() called while neither disconnecting nor " - + "disconnected (state=" + getStateLabel(mState) + ")"); - } - - mState = CONNECT_STATE_CONNECTING; - mHandler.post(new Runnable() { - @Override - public void run() { - if (mState == CONNECT_STATE_DISCONNECTING) { - return; - } - mState = CONNECT_STATE_CONNECTING; - // TODO: remove this extra check. - if (DBG) { - if (mServiceConnection != null) { - throw new RuntimeException("mServiceConnection should be null. Instead it" - + " is " + mServiceConnection); - } - } - if (mServiceBinder != null) { - throw new RuntimeException("mServiceBinder should be null. Instead it is " - + mServiceBinder); - } - if (mServiceCallbacks != null) { - throw new RuntimeException("mServiceCallbacks should be null. Instead it is " - + mServiceCallbacks); - } - - final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE); - intent.setComponent(mServiceComponent); - - mServiceConnection = new MediaServiceConnection(); - - boolean bound = false; - try { - bound = mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE); - } catch (Exception ex) { - Log.e(TAG, "Failed binding to service " + mServiceComponent); - } - - if (!bound) { - // Tell them that it didn't work. - forceCloseConnection(); - mCallback.onConnectionFailed(); - } - - if (DBG) { - Log.d(TAG, "connect..."); - dump(); - } - } - }); - } - - /** - * Disconnects from the media browser service. - * After this, no more callbacks will be received. - */ - public void disconnect() { - // It's ok to call this any state, because allowing this lets apps not have - // to check isConnected() unnecessarily. They won't appreciate the extra - // assertions for this. We do everything we can here to go back to a sane state. - mState = CONNECT_STATE_DISCONNECTING; - mHandler.post(new Runnable() { - @Override - public void run() { - // connect() could be called before this. Then we will disconnect and reconnect. - if (mServiceCallbacks != null) { - try { - mServiceBinder.disconnect(mServiceCallbacks); - } catch (RemoteException ex) { - // We are disconnecting anyway. Log, just for posterity but it's not - // a big problem. - Log.w(TAG, "RemoteException during connect for " + mServiceComponent); - } - } - int state = mState; - forceCloseConnection(); - // If the state was not CONNECT_STATE_DISCONNECTING, keep the state so that - // the operation came after disconnect() can be handled properly. - if (state != CONNECT_STATE_DISCONNECTING) { - mState = state; - } - if (DBG) { - Log.d(TAG, "disconnect..."); - dump(); - } - } - }); - } - - /** - * Null out the variables and unbind from the service. This doesn't include - * calling disconnect on the service, because we only try to do that in the - * clean shutdown cases. - *

    - * Everywhere that calls this EXCEPT for disconnect() should follow it with - * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback - * for a clean shutdown, but everywhere else is a dirty shutdown and should - * notify the app. - *

    - * Also, mState should be updated properly. Mostly it should be CONNECT_STATE_DIACONNECTED - * except for disconnect(). - */ - private void forceCloseConnection() { - if (mServiceConnection != null) { - try { - mContext.unbindService(mServiceConnection); - } catch (IllegalArgumentException e) { - if (DBG) { - Log.d(TAG, "unbindService failed", e); - } - } - } - mState = CONNECT_STATE_DISCONNECTED; - mServiceConnection = null; - mServiceBinder = null; - mServiceCallbacks = null; - mRootId = null; - mMediaSessionToken = null; - } - - /** - * Returns whether the browser is connected to the service. - */ - public boolean isConnected() { - return mState == CONNECT_STATE_CONNECTED; - } - - /** - * Gets the service component that the media browser is connected to. - */ - public @NonNull ComponentName getServiceComponent() { - if (!isConnected()) { - throw new IllegalStateException("getServiceComponent() called while not connected" + - " (state=" + mState + ")"); - } - return mServiceComponent; - } - - /** - * Gets the root id. - *

    - * Note that the root id may become invalid or change when the - * browser is disconnected. - *

    - * - * @throws IllegalStateException if not connected. - */ - public @NonNull String getRoot() { - if (!isConnected()) { - throw new IllegalStateException("getRoot() called while not connected (state=" - + getStateLabel(mState) + ")"); - } - return mRootId; - } - - /** - * Gets any extras for the media service. - * - * @throws IllegalStateException if not connected. - */ - public @Nullable Bundle getExtras() { - if (!isConnected()) { - throw new IllegalStateException("getExtras() called while not connected (state=" - + getStateLabel(mState) + ")"); - } - return mExtras; - } - - /** - * Gets the media session token associated with the media browser. - *

    - * Note that the session token may become invalid or change when the - * browser is disconnected. - *

    - * - * @return The session token for the browser, never null. - * - * @throws IllegalStateException if not connected. - */ - public @NonNull MediaSession.Token getSessionToken() { - if (!isConnected()) { - throw new IllegalStateException("getSessionToken() called while not connected (state=" - + mState + ")"); - } - return mMediaSessionToken; - } - - /** - * Queries for information about the media items that are contained within - * the specified id and subscribes to receive updates when they change. - *

    - * The list of subscriptions is maintained even when not connected and is - * restored after the reconnection. It is ok to subscribe while not connected - * but the results will not be returned until the connection completes. - *

    - *

    - * If the id is already subscribed with a different callback then the new - * callback will replace the previous one and the child data will be - * reloaded. - *

    - * - * @param parentId The id of the parent media item whose list of children - * will be subscribed. - * @param callback The callback to receive the list of children. - */ - public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { - subscribeInternal(parentId, null, callback); - } - - /** - * Queries with service-specific arguments for information about the media items - * that are contained within the specified id and subscribes to receive updates - * when they change. - *

    - * The list of subscriptions is maintained even when not connected and is - * restored after the reconnection. It is ok to subscribe while not connected - * but the results will not be returned until the connection completes. - *

    - *

    - * If the id is already subscribed with a different callback then the new - * callback will replace the previous one and the child data will be - * reloaded. - *

    - * - * @param parentId The id of the parent media item whose list of children - * will be subscribed. - * @param options The bundle of service-specific arguments to send to the media - * browser service. The contents of this bundle may affect the - * information returned when browsing. - * @param callback The callback to receive the list of children. - */ - public void subscribe(@NonNull String parentId, @NonNull Bundle options, - @NonNull SubscriptionCallback callback) { - if (options == null) { - throw new IllegalArgumentException("options cannot be null"); - } - subscribeInternal(parentId, new Bundle(options), callback); - } - - /** - * Unsubscribes for changes to the children of the specified media id. - *

    - * The query callback will no longer be invoked for results associated with - * this id once this method returns. - *

    - * - * @param parentId The id of the parent media item whose list of children - * will be unsubscribed. - */ - public void unsubscribe(@NonNull String parentId) { - unsubscribeInternal(parentId, null); - } - - /** - * Unsubscribes for changes to the children of the specified media id through a callback. - *

    - * The query callback will no longer be invoked for results associated with - * this id once this method returns. - *

    - * - * @param parentId The id of the parent media item whose list of children - * will be unsubscribed. - * @param callback A callback sent to the media browser service to subscribe. - */ - public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - unsubscribeInternal(parentId, callback); - } - - /** - * Retrieves a specific {@link MediaItem} from the connected service. Not - * all services may support this, so falling back to subscribing to the - * parent's id should be used when unavailable. - * - * @param mediaId The id of the item to retrieve. - * @param cb The callback to receive the result on. - */ - public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) { - if (TextUtils.isEmpty(mediaId)) { - throw new IllegalArgumentException("mediaId cannot be empty."); - } - if (cb == null) { - throw new IllegalArgumentException("cb cannot be null."); - } - if (mState != CONNECT_STATE_CONNECTED) { - Log.i(TAG, "Not connected, unable to retrieve the MediaItem."); - mHandler.post(new Runnable() { - @Override - public void run() { - cb.onError(mediaId); - } - }); - return; - } - ResultReceiver receiver = new ResultReceiver(mHandler) { - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (!isConnected()) { - return; - } - if (resultCode != 0 || resultData == null - || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) { - cb.onError(mediaId); - return; - } - Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM); - if (item != null && !(item instanceof MediaItem)) { - cb.onError(mediaId); - return; - } - cb.onItemLoaded((MediaItem)item); - } - }; - try { - mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks); - } catch (RemoteException e) { - Log.i(TAG, "Remote error getting media item."); - mHandler.post(new Runnable() { - @Override - public void run() { - cb.onError(mediaId); - } - }); - } - } - - private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) { - // Check arguments. - if (TextUtils.isEmpty(parentId)) { - throw new IllegalArgumentException("parentId cannot be empty."); - } - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - // Update or create the subscription. - Subscription sub = mSubscriptions.get(parentId); - if (sub == null) { - sub = new Subscription(); - mSubscriptions.put(parentId, sub); - } - sub.putCallback(mContext, options, callback); - - // If we are connected, tell the service that we are watching. If we aren't connected, - // the service will be told when we connect. - if (isConnected()) { - try { - if (options == null) { - mServiceBinder.addSubscriptionDeprecated(parentId, mServiceCallbacks); - } - mServiceBinder.addSubscription(parentId, callback.mToken, options, - mServiceCallbacks); - } catch (RemoteException ex) { - // Process is crashing. We will disconnect, and upon reconnect we will - // automatically reregister. So nothing to do here. - Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId); - } - } - } - - private void unsubscribeInternal(String parentId, SubscriptionCallback callback) { - // Check arguments. - if (TextUtils.isEmpty(parentId)) { - throw new IllegalArgumentException("parentId cannot be empty."); - } - - Subscription sub = mSubscriptions.get(parentId); - if (sub == null) { - return; - } - // Tell the service if necessary. - try { - if (callback == null) { - if (isConnected()) { - mServiceBinder.removeSubscriptionDeprecated(parentId, mServiceCallbacks); - mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks); - } - } else { - final List callbacks = sub.getCallbacks(); - final List optionsList = sub.getOptionsList(); - for (int i = callbacks.size() - 1; i >= 0; --i) { - if (callbacks.get(i) == callback) { - if (isConnected()) { - mServiceBinder.removeSubscription( - parentId, callback.mToken, mServiceCallbacks); - } - callbacks.remove(i); - optionsList.remove(i); - } - } - } - } catch (RemoteException ex) { - // Process is crashing. We will disconnect, and upon reconnect we will - // automatically reregister. So nothing to do here. - Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId); - } - - if (sub.isEmpty() || callback == null) { - mSubscriptions.remove(parentId); - } - } - - /** - * For debugging. - */ - private static String getStateLabel(int state) { - switch (state) { - case CONNECT_STATE_DISCONNECTING: - return "CONNECT_STATE_DISCONNECTING"; - case CONNECT_STATE_DISCONNECTED: - return "CONNECT_STATE_DISCONNECTED"; - case CONNECT_STATE_CONNECTING: - return "CONNECT_STATE_CONNECTING"; - case CONNECT_STATE_CONNECTED: - return "CONNECT_STATE_CONNECTED"; - case CONNECT_STATE_SUSPENDED: - return "CONNECT_STATE_SUSPENDED"; - default: - return "UNKNOWN/" + state; - } - } - - private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback, - final String root, final MediaSession.Token session, final Bundle extra) { - mHandler.post(new Runnable() { - @Override - public void run() { - // Check to make sure there hasn't been a disconnect or a different - // ServiceConnection. - if (!isCurrent(callback, "onConnect")) { - return; - } - // Don't allow them to call us twice. - if (mState != CONNECT_STATE_CONNECTING) { - Log.w(TAG, "onConnect from service while mState=" - + getStateLabel(mState) + "... ignoring"); - return; - } - mRootId = root; - mMediaSessionToken = session; - mExtras = extra; - mState = CONNECT_STATE_CONNECTED; - - if (DBG) { - Log.d(TAG, "ServiceCallbacks.onConnect..."); - dump(); - } - mCallback.onConnected(); - - // we may receive some subscriptions before we are connected, so re-subscribe - // everything now - for (Entry subscriptionEntry : mSubscriptions.entrySet()) { - String id = subscriptionEntry.getKey(); - Subscription sub = subscriptionEntry.getValue(); - List callbackList = sub.getCallbacks(); - List optionsList = sub.getOptionsList(); - for (int i = 0; i < callbackList.size(); ++i) { - try { - mServiceBinder.addSubscription(id, callbackList.get(i).mToken, - optionsList.get(i), mServiceCallbacks); - } catch (RemoteException ex) { - // Process is crashing. We will disconnect, and upon reconnect we will - // automatically reregister. So nothing to do here. - Log.d(TAG, "addSubscription failed with RemoteException parentId=" - + id); - } - } - } - } - }); - } - - private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) { - mHandler.post(new Runnable() { - @Override - public void run() { - Log.e(TAG, "onConnectFailed for " + mServiceComponent); - - // Check to make sure there hasn't been a disconnect or a different - // ServiceConnection. - if (!isCurrent(callback, "onConnectFailed")) { - return; - } - // Don't allow them to call us twice. - if (mState != CONNECT_STATE_CONNECTING) { - Log.w(TAG, "onConnect from service while mState=" - + getStateLabel(mState) + "... ignoring"); - return; - } - - // Clean up - forceCloseConnection(); - - // Tell the app. - mCallback.onConnectionFailed(); - } - }); - } - - private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, - final String parentId, final MediaParceledListSlice list, final Bundle options) { - mHandler.post(new Runnable() { - @Override - public void run() { - // Check that there hasn't been a disconnect or a different - // ServiceConnection. - if (!isCurrent(callback, "onLoadChildren")) { - return; - } - - if (DBG) { - Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId); - } - - // Check that the subscription is still subscribed. - final Subscription subscription = mSubscriptions.get(parentId); - if (subscription != null) { - // Tell the app. - SubscriptionCallback subscriptionCallback = - subscription.getCallback(mContext, options); - if (subscriptionCallback != null) { - List data = list == null ? null : list.getList(); - if (options == null) { - if (data == null) { - subscriptionCallback.onError(parentId); - } else { - subscriptionCallback.onChildrenLoaded(parentId, data); - } - } else { - if (data == null) { - subscriptionCallback.onError(parentId, options); - } else { - subscriptionCallback.onChildrenLoaded(parentId, data, options); - } - } - return; - } - } - if (DBG) { - Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId); - } - } - }); - } - - /** - * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not. - */ - private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) { - if (mServiceCallbacks != callback || mState == CONNECT_STATE_DISCONNECTING - || mState == CONNECT_STATE_DISCONNECTED) { - if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { - Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" - + mServiceCallbacks + " this=" + this); - } - return false; - } - return true; - } - - private ServiceCallbacks getNewServiceCallbacks() { - return new ServiceCallbacks(this); - } - - /** - * Log internal state. - * @hide - */ - void dump() { - Log.d(TAG, "MediaBrowser..."); - Log.d(TAG, " mServiceComponent=" + mServiceComponent); - Log.d(TAG, " mCallback=" + mCallback); - Log.d(TAG, " mRootHints=" + mRootHints); - Log.d(TAG, " mState=" + getStateLabel(mState)); - Log.d(TAG, " mServiceConnection=" + mServiceConnection); - Log.d(TAG, " mServiceBinder=" + mServiceBinder); - Log.d(TAG, " mServiceCallbacks=" + mServiceCallbacks); - Log.d(TAG, " mRootId=" + mRootId); - Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken); - } - - /** - * A class with information on a single media item for use in browsing/searching media. - * MediaItems are application dependent so we cannot guarantee that they contain the - * right values. - */ - public static class MediaItem implements Parcelable { - private final int mFlags; - private final MediaDescription mDescription; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE }) - public @interface Flags { } - - /** - * Flag: Indicates that the item has children of its own. - */ - public static final int FLAG_BROWSABLE = 1 << 0; - - /** - * Flag: Indicates that the item is playable. - *

    - * The id of this item may be passed to - * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)} - * to start playing it. - *

    - */ - public static final int FLAG_PLAYABLE = 1 << 1; - - /** - * Create a new MediaItem for use in browsing media. - * @param description The description of the media, which must include a - * media id. - * @param flags The flags for this item. - */ - public MediaItem(@NonNull MediaDescription description, @Flags int flags) { - if (description == null) { - throw new IllegalArgumentException("description cannot be null"); - } - if (TextUtils.isEmpty(description.getMediaId())) { - throw new IllegalArgumentException("description must have a non-empty media id"); - } - mFlags = flags; - mDescription = description; - } - - /** - * Private constructor. - */ - private MediaItem(Parcel in) { - mFlags = in.readInt(); - mDescription = MediaDescription.CREATOR.createFromParcel(in); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mFlags); - mDescription.writeToParcel(out, flags); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("MediaItem{"); - sb.append("mFlags=").append(mFlags); - sb.append(", mDescription=").append(mDescription); - sb.append('}'); - return sb.toString(); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public MediaItem createFromParcel(Parcel in) { - return new MediaItem(in); - } - - @Override - public MediaItem[] newArray(int size) { - return new MediaItem[size]; - } - }; - - /** - * Gets the flags of the item. - */ - public @Flags int getFlags() { - return mFlags; - } - - /** - * Returns whether this item is browsable. - * @see #FLAG_BROWSABLE - */ - public boolean isBrowsable() { - return (mFlags & FLAG_BROWSABLE) != 0; - } - - /** - * Returns whether this item is playable. - * @see #FLAG_PLAYABLE - */ - public boolean isPlayable() { - return (mFlags & FLAG_PLAYABLE) != 0; - } - - /** - * Returns the description of the media. - */ - public @NonNull MediaDescription getDescription() { - return mDescription; - } - - /** - * Returns the media id in the {@link MediaDescription} for this item. - * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID - */ - public @Nullable String getMediaId() { - return mDescription.getMediaId(); - } - } - - /** - * Callbacks for connection related events. - */ - public static class ConnectionCallback { - /** - * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed. - */ - public void onConnected() { - } - - /** - * Invoked when the client is disconnected from the media browser. - */ - public void onConnectionSuspended() { - } - - /** - * Invoked when the connection to the media browser failed. - */ - public void onConnectionFailed() { - } - } - - /** - * Callbacks for subscription related events. - */ - public static abstract class SubscriptionCallback { - Binder mToken; - - public SubscriptionCallback() { - mToken = new Binder(); - } - - /** - * Called when the list of children is loaded or updated. - * - * @param parentId The media id of the parent media item. - * @param children The children which were loaded. - */ - public void onChildrenLoaded(@NonNull String parentId, @NonNull List children) { - } - - /** - * Called when the list of children is loaded or updated. - * - * @param parentId The media id of the parent media item. - * @param children The children which were loaded. - * @param options The bundle of service-specific arguments sent to the media - * browser service. The contents of this bundle may affect the - * information returned when browsing. - */ - public void onChildrenLoaded(@NonNull String parentId, @NonNull List children, - @NonNull Bundle options) { - } - - /** - * Called when the id doesn't exist or other errors in subscribing. - *

    - * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} - * called, because some errors may heal themselves. - *

    - * - * @param parentId The media id of the parent media item whose children could - * not be loaded. - */ - public void onError(@NonNull String parentId) { - } - - /** - * Called when the id doesn't exist or other errors in subscribing. - *

    - * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} - * called, because some errors may heal themselves. - *

    - * - * @param parentId The media id of the parent media item whose children could - * not be loaded. - * @param options The bundle of service-specific arguments sent to the media - * browser service. - */ - public void onError(@NonNull String parentId, @NonNull Bundle options) { - } - } - - /** - * Callback for receiving the result of {@link #getItem}. - */ - public static abstract class ItemCallback { - /** - * Called when the item has been returned by the connected service. - * - * @param item The item that was returned or null if it doesn't exist. - */ - public void onItemLoaded(MediaItem item) { - } - - /** - * Called there was an error retrieving it or the connected service doesn't support - * {@link #getItem}. - * - * @param mediaId The media id of the media item which could not be loaded. - */ - public void onError(@NonNull String mediaId) { - } - } - - /** - * ServiceConnection to the other app. - */ - private class MediaServiceConnection implements ServiceConnection { - @Override - public void onServiceConnected(final ComponentName name, final IBinder binder) { - postOrRun(new Runnable() { - @Override - public void run() { - if (DBG) { - Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name - + " binder=" + binder); - dump(); - } - - // Make sure we are still the current connection, and that they haven't called - // disconnect(). - if (!isCurrent("onServiceConnected")) { - return; - } - - // Save their binder - mServiceBinder = IMediaBrowserService.Stub.asInterface(binder); - - // We make a new mServiceCallbacks each time we connect so that we can drop - // responses from previous connections. - mServiceCallbacks = getNewServiceCallbacks(); - mState = CONNECT_STATE_CONNECTING; - - // Call connect, which is async. When we get a response from that we will - // say that we're connected. - try { - if (DBG) { - Log.d(TAG, "ServiceCallbacks.onConnect..."); - dump(); - } - mServiceBinder.connect(mContext.getPackageName(), mRootHints, - mServiceCallbacks); - } catch (RemoteException ex) { - // Connect failed, which isn't good. But the auto-reconnect on the service - // will take over and we will come back. We will also get the - // onServiceDisconnected, which has all the cleanup code. So let that do - // it. - Log.w(TAG, "RemoteException during connect for " + mServiceComponent); - if (DBG) { - Log.d(TAG, "ServiceCallbacks.onConnect..."); - dump(); - } - } - } - }); - } - - @Override - public void onServiceDisconnected(final ComponentName name) { - postOrRun(new Runnable() { - @Override - public void run() { - if (DBG) { - Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name - + " this=" + this + " mServiceConnection=" + mServiceConnection); - dump(); - } - - // Make sure we are still the current connection, and that they haven't called - // disconnect(). - if (!isCurrent("onServiceDisconnected")) { - return; - } - - // Clear out what we set in onServiceConnected - mServiceBinder = null; - mServiceCallbacks = null; - - // And tell the app that it's suspended. - mState = CONNECT_STATE_SUSPENDED; - mCallback.onConnectionSuspended(); - } - }); - } - - private void postOrRun(Runnable r) { - if (Thread.currentThread() == mHandler.getLooper().getThread()) { - r.run(); - } else { - mHandler.post(r); - } - } - - /** - * Return true if this is the current ServiceConnection. Also logs if it's not. - */ - private boolean isCurrent(String funcName) { - if (mServiceConnection != this || mState == CONNECT_STATE_DISCONNECTING - || mState == CONNECT_STATE_DISCONNECTED) { - if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) { - // Check mState, because otherwise this log is noisy. - Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection=" - + mServiceConnection + " this=" + this); - } - return false; - } - return true; - } - } - - /** - * Callbacks from the service. - */ - private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub { - private WeakReference mMediaBrowser; - - public ServiceCallbacks(MediaBrowser mediaBrowser) { - mMediaBrowser = new WeakReference(mediaBrowser); - } - - /** - * The other side has acknowledged our connection. The parameters to this function - * are the initial data as requested. - */ - @Override - public void onConnect(String root, MediaSession.Token session, - final Bundle extras) { - MediaBrowser mediaBrowser = mMediaBrowser.get(); - if (mediaBrowser != null) { - mediaBrowser.onServiceConnected(this, root, session, extras); - } - } - - /** - * The other side does not like us. Tell the app via onConnectionFailed. - */ - @Override - public void onConnectFailed() { - MediaBrowser mediaBrowser = mMediaBrowser.get(); - if (mediaBrowser != null) { - mediaBrowser.onConnectionFailed(this); - } - } - - @Override - public void onLoadChildren(String parentId, MediaParceledListSlice list) { - onLoadChildrenWithOptions(parentId, list, null); - } - - @Override - public void onLoadChildrenWithOptions(String parentId, MediaParceledListSlice list, - final Bundle options) { - MediaBrowser mediaBrowser = mMediaBrowser.get(); - if (mediaBrowser != null) { - mediaBrowser.onLoadChildren(this, parentId, list, options); - } - } - } - - private static class Subscription { - private final List mCallbacks; - private final List mOptionsList; - - public Subscription() { - mCallbacks = new ArrayList<>(); - mOptionsList = new ArrayList<>(); - } - - public boolean isEmpty() { - return mCallbacks.isEmpty(); - } - - public List getOptionsList() { - return mOptionsList; - } - - public List getCallbacks() { - return mCallbacks; - } - - public SubscriptionCallback getCallback(Context context, Bundle options) { - if (options != null) { - options.setClassLoader(context.getClassLoader()); - } - for (int i = 0; i < mOptionsList.size(); ++i) { - if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) { - return mCallbacks.get(i); - } - } - return null; - } - - public void putCallback(Context context, Bundle options, SubscriptionCallback callback) { - if (options != null) { - options.setClassLoader(context.getClassLoader()); - } - for (int i = 0; i < mOptionsList.size(); ++i) { - if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) { - mCallbacks.set(i, callback); - return; - } - } - mCallbacks.add(callback); - mOptionsList.add(options); - } - } -} diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java deleted file mode 100644 index 2943e60dbb..0000000000 --- a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media.browse; - -import android.os.Bundle; - -/** - * @hide - */ -public class MediaBrowserUtils { - public static boolean areSameOptions(Bundle options1, Bundle options2) { - if (options1 == options2) { - return true; - } else if (options1 == null) { - return options2.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1 - && options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1; - } else if (options2 == null) { - return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1 - && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1; - } else { - return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) - == options2.getInt(MediaBrowser.EXTRA_PAGE, -1) - && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) - == options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); - } - } - - public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) { - int page1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE, -1); - int page2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE, -1); - int pageSize1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); - int pageSize2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); - - int startIndex1, startIndex2, endIndex1, endIndex2; - if (page1 == -1 || pageSize1 == -1) { - startIndex1 = 0; - endIndex1 = Integer.MAX_VALUE; - } else { - startIndex1 = pageSize1 * page1; - endIndex1 = startIndex1 + pageSize1 - 1; - } - - if (page2 == -1 || pageSize2 == -1) { - startIndex2 = 0; - endIndex2 = Integer.MAX_VALUE; - } else { - startIndex2 = pageSize2 * page2; - endIndex2 = startIndex2 + pageSize2 - 1; - } - - if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) { - return true; - } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) { - return true; - } - return false; - } -} diff --git a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl deleted file mode 100644 index 4b9e4bd0fb..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.media.session.MediaSession; - -/** - * Listens for changes to the list of active sessions. - * @hide - */ -oneway interface IActiveSessionsListener { - void onActiveSessionsChanged(in List sessions); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl deleted file mode 100644 index 322bffa96c..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media.session; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.media.session.MediaSession; -import android.view.KeyEvent; - -/** - * @hide - */ -oneway interface ICallback { - void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event, - in MediaSession.Token sessionToken); - void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event, - in ComponentName mediaButtonReceiver); - - void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken); - void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver); -} - diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl deleted file mode 100644 index aa98ea312a..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media.session; - -import android.os.ResultReceiver; -import android.view.KeyEvent; - -/** - * Listener to handle media key. - * @hide - */ -oneway interface IOnMediaKeyListener { - void onMediaKey(in KeyEvent event, in ResultReceiver result); -} - diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl deleted file mode 100644 index 07b83471d1..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media.session; - -import android.view.KeyEvent; - -/** - * Listener to handle volume key long-press. - * @hide - */ -oneway interface IOnVolumeKeyLongPressListener { - void onVolumeKeyLongPress(in KeyEvent event); -} - diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl deleted file mode 100644 index 14b1c64360..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.app.PendingIntent; -//import android.media.AudioAttributes; -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.session.ISessionController; -import android.media.session.PlaybackState; -import android.media.session.MediaSession; -import android.os.Bundle; -import android.os.ResultReceiver; - -/** - * Interface to a MediaSession in the system. - * @hide - */ -interface ISession { - void sendEvent(String event, in Bundle data); - ISessionController getController(); - void setFlags(int flags); - void setActive(boolean active); - void setMediaButtonReceiver(in PendingIntent mbr); - void setLaunchPendingIntent(in PendingIntent pi); - void destroy(); - - // These commands are for the TransportPerformer - void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); - void setPlaybackState(in PlaybackState state); - void setQueue(in MediaParceledListSlice queue); - void setQueueTitle(CharSequence title); - void setExtras(in Bundle extras); - void setRatingType(int type); - - // These commands relate to volume handling - //TODO(b/119751592): Decide if AudioAttributes should be updated. - //void setPlaybackToLocal(in AudioAttributes attributes); - void setPlaybackToRemote(int control, int max); - void setCurrentVolume(int currentVolume); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl deleted file mode 100644 index 626338d97c..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.content.Intent; -import android.media.Rating; -import android.media.session.ISessionControllerCallback; -import android.net.Uri; -import android.os.Bundle; -import android.os.ResultReceiver; - -/** - * @hide - */ -oneway interface ISessionCallback { - void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller, - String command, in Bundle args, in ResultReceiver cb); - void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent, - int sequenceNumber, in ResultReceiver cb); - void onMediaButtonFromController(String packageName, int pid, int uid, - ISessionControllerCallback caller, in Intent mediaButtonIntent); - - // These callbacks are for the TransportPerformer - void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onPrepareFromMediaId(String packageName, int pid, int uid, - ISessionControllerCallback caller, String mediaId, in Bundle extras); - void onPrepareFromSearch(String packageName, int pid, int uid, - ISessionControllerCallback caller, String query, in Bundle extras); - void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, - in Uri uri, in Bundle extras); - void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller, - String mediaId, in Bundle extras); - void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller, - String query, in Bundle extras); - void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller, - in Uri uri, in Bundle extras); - void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller, - long id); - void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller); - void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller, - long pos); - void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, - in Rating rating); - void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller, - String action, in Bundle args); - - // These callbacks are for volume handling - void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller, - int direction); - void onSetVolumeTo(String packageName, int pid, int uid, - ISessionControllerCallback caller, int value); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl deleted file mode 100644 index 433b12fad9..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.app.PendingIntent; -import android.content.Intent; -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.Rating; -import android.media.session.ISessionControllerCallback; -import android.media.session.MediaSession; -import android.media.session.ParcelableVolumeInfo; -import android.media.session.PlaybackState; -import android.net.Uri; -import android.os.Bundle; -import android.os.ResultReceiver; -import android.view.KeyEvent; - -import java.util.List; - -/** - * Interface to MediaSessionRecord in the system. - * @hide - */ -interface ISessionController { - void sendCommand(String packageName, ISessionControllerCallback caller, - String command, in Bundle args, in ResultReceiver cb); - boolean sendMediaButton(String packageName, ISessionControllerCallback caller, - boolean asSystemService, in KeyEvent mediaButton); - void registerCallbackListener(String packageName, ISessionControllerCallback cb); - void unregisterCallbackListener(ISessionControllerCallback cb); - boolean isTransportControlEnabled(); - String getPackageName(); - String getTag(); - PendingIntent getLaunchPendingIntent(); - long getFlags(); - ParcelableVolumeInfo getVolumeAttributes(); - void adjustVolume(String packageName, String opPackageName, ISessionControllerCallback caller, - boolean asSystemService, int direction, int flags); - void setVolumeTo(String packageName, String opPackageName, ISessionControllerCallback caller, - int value, int flags); - - // These commands are for the TransportControls - void prepare(String packageName, ISessionControllerCallback caller); - void prepareFromMediaId(String packageName, ISessionControllerCallback caller, - String mediaId, in Bundle extras); - void prepareFromSearch(String packageName, ISessionControllerCallback caller, - String string, in Bundle extras); - void prepareFromUri(String packageName, ISessionControllerCallback caller, - in Uri uri, in Bundle extras); - void play(String packageName, ISessionControllerCallback caller); - void playFromMediaId(String packageName, ISessionControllerCallback caller, - String mediaId, in Bundle extras); - void playFromSearch(String packageName, ISessionControllerCallback caller, - String string, in Bundle extras); - void playFromUri(String packageName, ISessionControllerCallback caller, - in Uri uri, in Bundle extras); - void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id); - void pause(String packageName, ISessionControllerCallback caller); - void stop(String packageName, ISessionControllerCallback caller); - void next(String packageName, ISessionControllerCallback caller); - void previous(String packageName, ISessionControllerCallback caller); - void fastForward(String packageName, ISessionControllerCallback caller); - void rewind(String packageName, ISessionControllerCallback caller); - void seekTo(String packageName, ISessionControllerCallback caller, long pos); - void rate(String packageName, ISessionControllerCallback caller, in Rating rating); - void sendCustomAction(String packageName, ISessionControllerCallback caller, - String action, in Bundle args); - MediaMetadata getMetadata(); - PlaybackState getPlaybackState(); - MediaParceledListSlice getQueue(); - CharSequence getQueueTitle(); - Bundle getExtras(); - int getRatingType(); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl deleted file mode 100644 index f5cc4f6efa..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.session.ParcelableVolumeInfo; -import android.media.session.PlaybackState; -import android.media.session.MediaSession; -import android.os.Bundle; - -/** - * @hide - */ -oneway interface ISessionControllerCallback { - void onEvent(String event, in Bundle extras); - void onSessionDestroyed(); - - // These callbacks are for the TransportController - void onPlaybackStateChanged(in PlaybackState state); - void onMetadataChanged(in MediaMetadata metadata); - void onQueueChanged(in MediaParceledListSlice queue); - void onQueueTitleChanged(CharSequence title); - void onExtrasChanged(in Bundle extras); - void onVolumeInfoChanged(in ParcelableVolumeInfo info); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl deleted file mode 100644 index d6c226f8b6..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2014 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.session; - -import android.content.ComponentName; -import android.media.IRemoteVolumeController; -import android.media.session.IActiveSessionsListener; -import android.media.session.ICallback; -import android.media.session.IOnMediaKeyListener; -import android.media.session.IOnVolumeKeyLongPressListener; -import android.media.session.ISession; -import android.media.session.ISessionCallback; -import android.os.Bundle; -import android.view.KeyEvent; - -/** - * Interface to the MediaSessionManagerService - * @hide - */ -interface ISessionManager { - ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId); - List getSessions(in ComponentName compName, int userId); - void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, - boolean needWakeLock); - void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService, - in KeyEvent keyEvent, int stream, boolean musicOnly); - void dispatchAdjustVolume(String packageName, String opPackageName, int suggestedStream, - int delta, int flags); - void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, - int userId); - void removeSessionsListener(in IActiveSessionsListener listener); - - // This is for the system volume UI only - void setRemoteVolumeController(in IRemoteVolumeController rvc); - - // For PhoneWindowManager to precheck media keys - boolean isGlobalPriorityActive(); - - void setCallback(in ICallback callback); - void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener); - void setOnMediaKeyListener(in IOnMediaKeyListener listener); - - // MediaSession2 - boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); -} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java deleted file mode 100644 index 65682a8d8a..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/MediaController.java +++ /dev/null @@ -1,1190 +0,0 @@ -/* - * Copyright (C) 2014 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.session; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; -import android.app.PendingIntent; -import android.content.Context; -import android.media.AudioAttributes; -import android.media.AudioManager; -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.Rating; -import android.media.VolumeProvider; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.text.TextUtils; -import android.util.Log; -import android.view.KeyEvent; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * Allows an app to interact with an ongoing media session. Media buttons and - * other commands can be sent to the session. A callback may be registered to - * receive updates from the session, such as metadata and play state changes. - *

    - * A MediaController can be created through {@link MediaSessionManager} if you - * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an - * enabled notification listener or by getting a {@link MediaSession.Token} - * directly from the session owner. - *

    - * MediaController objects are thread-safe. - */ -public final class MediaController { - private static final String TAG = "MediaController"; - - private static final int MSG_EVENT = 1; - private static final int MSG_UPDATE_PLAYBACK_STATE = 2; - private static final int MSG_UPDATE_METADATA = 3; - private static final int MSG_UPDATE_VOLUME = 4; - private static final int MSG_UPDATE_QUEUE = 5; - private static final int MSG_UPDATE_QUEUE_TITLE = 6; - private static final int MSG_UPDATE_EXTRAS = 7; - private static final int MSG_DESTROYED = 8; - - private final ISessionController mSessionBinder; - - private final MediaSession.Token mToken; - private final Context mContext; - private final CallbackStub mCbStub = new CallbackStub(this); - private final ArrayList mCallbacks = new ArrayList(); - private final Object mLock = new Object(); - - private boolean mCbRegistered = false; - private String mPackageName; - private String mTag; - - private final TransportControls mTransportControls; - - /** - * Call for creating a MediaController directly from a binder. Should only - * be used by framework code. - * - * @hide - */ - public MediaController(Context context, ISessionController sessionBinder) { - if (sessionBinder == null) { - throw new IllegalArgumentException("Session token cannot be null"); - } - if (context == null) { - throw new IllegalArgumentException("Context cannot be null"); - } - mSessionBinder = sessionBinder; - mTransportControls = new TransportControls(); - mToken = new MediaSession.Token(sessionBinder); - mContext = context; - } - - /** - * Create a new MediaController from a session's token. - * - * @param context The caller's context. - * @param token The token for the session. - */ - public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) { - this(context, token.getBinder()); - } - - /** - * Get a {@link TransportControls} instance to send transport actions to - * the associated session. - * - * @return A transport controls instance. - */ - public @NonNull TransportControls getTransportControls() { - return mTransportControls; - } - - /** - * Send the specified media button event to the session. Only media keys can - * be sent by this method, other keys will be ignored. - * - * @param keyEvent The media button event to dispatch. - * @return true if the event was sent to the session, false otherwise. - */ - public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { - return dispatchMediaButtonEventInternal(false, keyEvent); - } - - /** - * Dispatches the media button event as system service to the session. - *

    - * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the - * foreground activity didn't consume the key from the hardware devices. - * - * @param keyEvent media key event - * @return {@code true} if the event was sent to the session, {@code false} otherwise - * @hide - */ - public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { - return dispatchMediaButtonEventInternal(true, keyEvent); - } - - private boolean dispatchMediaButtonEventInternal(boolean asSystemService, - @NonNull KeyEvent keyEvent) { - if (keyEvent == null) { - throw new IllegalArgumentException("KeyEvent may not be null"); - } - if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { - return false; - } - try { - return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub, - asSystemService, keyEvent); - } catch (RemoteException e) { - // System is dead. =( - } - return false; - } - - /** - * Dispatches the volume button event as system service to the session. - *

    - * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the - * foreground activity didn't consume the key from the hardware devices. - * - * @param keyEvent volume key event - * @hide - */ - public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { - switch (keyEvent.getAction()) { - case KeyEvent.ACTION_DOWN: { - int direction = 0; - switch (keyEvent.getKeyCode()) { - case KeyEvent.KEYCODE_VOLUME_UP: - direction = AudioManager.ADJUST_RAISE; - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - direction = AudioManager.ADJUST_LOWER; - break; - case KeyEvent.KEYCODE_VOLUME_MUTE: - direction = AudioManager.ADJUST_TOGGLE_MUTE; - break; - } - try { - mSessionBinder.adjustVolume(mContext.getPackageName(), - mContext.getOpPackageName(), mCbStub, true, direction, - AudioManager.FLAG_SHOW_UI); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy", e); - } - } - - case KeyEvent.ACTION_UP: { - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE - | AudioManager.FLAG_FROM_KEY; - try { - mSessionBinder.adjustVolume(mContext.getPackageName(), - mContext.getOpPackageName(), mCbStub, true, 0, flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy", e); - } - } - } - } - - /** - * Get the current playback state for this session. - * - * @return The current PlaybackState or null - */ - public @Nullable PlaybackState getPlaybackState() { - try { - return mSessionBinder.getPlaybackState(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPlaybackState.", e); - return null; - } - } - - /** - * Get the current metadata for this session. - * - * @return The current MediaMetadata or null. - */ - public @Nullable MediaMetadata getMetadata() { - try { - return mSessionBinder.getMetadata(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getMetadata.", e); - return null; - } - } - - /** - * Get the current play queue for this session if one is set. If you only - * care about the current item {@link #getMetadata()} should be used. - * - * @return The current play queue or null. - */ - public @Nullable List getQueue() { - try { - MediaParceledListSlice queue = mSessionBinder.getQueue(); - if (queue != null) { - return queue.getList(); - } - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueue.", e); - } - return null; - } - - /** - * Get the queue title for this session. - */ - public @Nullable CharSequence getQueueTitle() { - try { - return mSessionBinder.getQueueTitle(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueueTitle", e); - } - return null; - } - - /** - * Get the extras for this session. - */ - public @Nullable Bundle getExtras() { - try { - return mSessionBinder.getExtras(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getExtras", e); - } - return null; - } - - /** - * Get the rating type supported by the session. One of: - *

      - *
    • {@link Rating#RATING_NONE}
    • - *
    • {@link Rating#RATING_HEART}
    • - *
    • {@link Rating#RATING_THUMB_UP_DOWN}
    • - *
    • {@link Rating#RATING_3_STARS}
    • - *
    • {@link Rating#RATING_4_STARS}
    • - *
    • {@link Rating#RATING_5_STARS}
    • - *
    • {@link Rating#RATING_PERCENTAGE}
    • - *
    - * - * @return The supported rating type - */ - public int getRatingType() { - try { - return mSessionBinder.getRatingType(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRatingType.", e); - return Rating.RATING_NONE; - } - } - - /** - * Get the flags for this session. Flags are defined in {@link MediaSession}. - * - * @return The current set of flags for the session. - */ - public @MediaSession.SessionFlags long getFlags() { - try { - return mSessionBinder.getFlags(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getFlags.", e); - } - return 0; - } - - /** - * Get the current playback info for this session. - * - * @return The current playback info or null. - */ - public @Nullable PlaybackInfo getPlaybackInfo() { - try { - ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes(); - return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType, - result.maxVolume, result.currentVolume); - - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getAudioInfo.", e); - } - return null; - } - - /** - * Get an intent for launching UI associated with this session if one - * exists. - * - * @return A {@link PendingIntent} to launch UI or null. - */ - public @Nullable PendingIntent getSessionActivity() { - try { - return mSessionBinder.getLaunchPendingIntent(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPendingIntent.", e); - } - return null; - } - - /** - * Get the token for the session this is connected to. - * - * @return The token for the connected session. - */ - public @NonNull MediaSession.Token getSessionToken() { - return mToken; - } - - /** - * Set the volume of the output this session is playing on. The command will - * be ignored if it does not support - * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in - * {@link AudioManager} may be used to affect the handling. - * - * @see #getPlaybackInfo() - * @param value The value to set it to, between 0 and the reported max. - * @param flags Flags from {@link AudioManager} to include with the volume - * request. - */ - public void setVolumeTo(int value, int flags) { - try { - mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(), - mCbStub, value, flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setVolumeTo.", e); - } - } - - /** - * Adjust the volume of the output this session is playing on. The direction - * must be one of {@link AudioManager#ADJUST_LOWER}, - * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}. - * The command will be ignored if the session does not support - * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or - * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in - * {@link AudioManager} may be used to affect the handling. - * - * @see #getPlaybackInfo() - * @param direction The direction to adjust the volume in. - * @param flags Any flags to pass with the command. - */ - public void adjustVolume(int direction, int flags) { - try { - mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(), - mCbStub, false, direction, flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy.", e); - } - } - - /** - * Registers a callback to receive updates from the Session. Updates will be - * posted on the caller's thread. - * - * @param callback The callback object, must not be null. - */ - public void registerCallback(@NonNull Callback callback) { - registerCallback(callback, null); - } - - /** - * Registers a callback to receive updates from the session. Updates will be - * posted on the specified handler's thread. - * - * @param callback The callback object, must not be null. - * @param handler The handler to post updates on. If null the callers thread - * will be used. - */ - public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { - if (callback == null) { - throw new IllegalArgumentException("callback must not be null"); - } - if (handler == null) { - handler = new Handler(); - } - synchronized (mLock) { - addCallbackLocked(callback, handler); - } - } - - /** - * Unregisters the specified callback. If an update has already been posted - * you may still receive it after calling this method. - * - * @param callback The callback to remove. - */ - public void unregisterCallback(@NonNull Callback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback must not be null"); - } - synchronized (mLock) { - removeCallbackLocked(callback); - } - } - - /** - * Sends a generic command to the session. It is up to the session creator - * to decide what commands and parameters they will support. As such, - * commands should only be sent to sessions that the controller owns. - * - * @param command The command to send - * @param args Any parameters to include with the command - * @param cb The callback to receive the result on - */ - public void sendCommand(@NonNull String command, @Nullable Bundle args, - @Nullable ResultReceiver cb) { - if (TextUtils.isEmpty(command)) { - throw new IllegalArgumentException("command cannot be null or empty"); - } - try { - mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCommand.", e); - } - } - - /** - * Get the session owner's package name. - * - * @return The package name of of the session owner. - */ - public String getPackageName() { - if (mPackageName == null) { - try { - mPackageName = mSessionBinder.getPackageName(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getPackageName.", e); - } - } - return mPackageName; - } - - /** - * Get the session's tag for debugging purposes. - * - * @return The session's tag. - * @hide - */ - public String getTag() { - if (mTag == null) { - try { - mTag = mSessionBinder.getTag(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getTag.", e); - } - } - return mTag; - } - - /* - * @hide - */ - ISessionController getSessionBinder() { - return mSessionBinder; - } - - /** - * @hide - */ - @UnsupportedAppUsage - public boolean controlsSameSession(MediaController other) { - if (other == null) return false; - return mSessionBinder.asBinder() == other.getSessionBinder().asBinder(); - } - - private void addCallbackLocked(Callback cb, Handler handler) { - if (getHandlerForCallbackLocked(cb) != null) { - Log.w(TAG, "Callback is already added, ignoring"); - return; - } - MessageHandler holder = new MessageHandler(handler.getLooper(), cb); - mCallbacks.add(holder); - holder.mRegistered = true; - - if (!mCbRegistered) { - try { - mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub); - mCbRegistered = true; - } catch (RemoteException e) { - Log.e(TAG, "Dead object in registerCallback", e); - } - } - } - - private boolean removeCallbackLocked(Callback cb) { - boolean success = false; - for (int i = mCallbacks.size() - 1; i >= 0; i--) { - MessageHandler handler = mCallbacks.get(i); - if (cb == handler.mCallback) { - mCallbacks.remove(i); - success = true; - handler.mRegistered = false; - } - } - if (mCbRegistered && mCallbacks.size() == 0) { - try { - mSessionBinder.unregisterCallbackListener(mCbStub); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in removeCallbackLocked"); - } - mCbRegistered = false; - } - return success; - } - - private MessageHandler getHandlerForCallbackLocked(Callback cb) { - if (cb == null) { - throw new IllegalArgumentException("Callback cannot be null"); - } - for (int i = mCallbacks.size() - 1; i >= 0; i--) { - MessageHandler handler = mCallbacks.get(i); - if (cb == handler.mCallback) { - return handler; - } - } - return null; - } - - private final void postMessage(int what, Object obj, Bundle data) { - synchronized (mLock) { - for (int i = mCallbacks.size() - 1; i >= 0; i--) { - mCallbacks.get(i).post(what, obj, data); - } - } - } - - /** - * Callback for receiving updates from the session. A Callback can be - * registered using {@link #registerCallback}. - */ - public static abstract class Callback { - /** - * Override to handle the session being destroyed. The session is no - * longer valid after this call and calls to it will be ignored. - */ - public void onSessionDestroyed() { - } - - /** - * Override to handle custom events sent by the session owner without a - * specified interface. Controllers should only handle these for - * sessions they own. - * - * @param event The event from the session. - * @param extras Optional parameters for the event, may be null. - */ - public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) { - } - - /** - * Override to handle changes in playback state. - * - * @param state The new playback state of the session - */ - public void onPlaybackStateChanged(@Nullable PlaybackState state) { - } - - /** - * Override to handle changes to the current metadata. - * - * @param metadata The current metadata for the session or null if none. - * @see MediaMetadata - */ - public void onMetadataChanged(@Nullable MediaMetadata metadata) { - } - - /** - * Override to handle changes to items in the queue. - * - * @param queue A list of items in the current play queue. It should - * include the currently playing item as well as previous and - * upcoming items if applicable. - * @see MediaSession.QueueItem - */ - public void onQueueChanged(@Nullable List queue) { - } - - /** - * Override to handle changes to the queue title. - * - * @param title The title that should be displayed along with the play queue such as - * "Now Playing". May be null if there is no such title. - */ - public void onQueueTitleChanged(@Nullable CharSequence title) { - } - - /** - * Override to handle changes to the {@link MediaSession} extras. - * - * @param extras The extras that can include other information associated with the - * {@link MediaSession}. - */ - public void onExtrasChanged(@Nullable Bundle extras) { - } - - /** - * Override to handle changes to the audio info. - * - * @param info The current audio info for this session. - */ - public void onAudioInfoChanged(PlaybackInfo info) { - } - } - - /** - * Interface for controlling media playback on a session. This allows an app - * to send media transport commands to the session. - */ - public final class TransportControls { - private static final String TAG = "TransportController"; - - private TransportControls() { - } - - /** - * Request that the player prepare its playback. In other words, other sessions can continue - * to play during the preparation of this session. This method can be used to speed up the - * start of the playback. Once the preparation is done, the session will change its playback - * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to - * start playback. - */ - public void prepare() { - try { - mSessionBinder.prepare(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare.", e); - } - } - - /** - * Request that the player prepare playback for a specific media id. In other words, other - * sessions can continue to play during the preparation of this session. This method can be - * used to speed up the start of the playback. Once the preparation is done, the session - * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, - * {@link #play} can be called to start playback. If the preparation is not needed, - * {@link #playFromMediaId} can be directly called without this method. - * - * @param mediaId The id of the requested media. - * @param extras Optional extras that can include extra information about the media item - * to be prepared. - */ - public void prepareFromMediaId(String mediaId, Bundle extras) { - if (TextUtils.isEmpty(mediaId)) { - throw new IllegalArgumentException( - "You must specify a non-empty String for prepareFromMediaId."); - } - try { - mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId, - extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); - } - } - - /** - * Request that the player prepare playback for a specific search query. An empty or null - * query should be treated as a request to prepare any music. In other words, other sessions - * can continue to play during the preparation of this session. This method can be used to - * speed up the start of the playback. Once the preparation is done, the session will - * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, - * {@link #play} can be called to start playback. If the preparation is not needed, - * {@link #playFromSearch} can be directly called without this method. - * - * @param query The search query. - * @param extras Optional extras that can include extra information - * about the query. - */ - public void prepareFromSearch(String query, Bundle extras) { - if (query == null) { - // This is to remain compatible with - // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH - query = ""; - } - try { - mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query, - extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + query + ").", e); - } - } - - /** - * Request that the player prepare playback for a specific {@link Uri}. In other words, - * other sessions can continue to play during the preparation of this session. This method - * can be used to speed up the start of the playback. Once the preparation is done, the - * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, - * {@link #play} can be called to start playback. If the preparation is not needed, - * {@link #playFromUri} can be directly called without this method. - * - * @param uri The URI of the requested media. - * @param extras Optional extras that can include extra information about the media item - * to be prepared. - */ - public void prepareFromUri(Uri uri, Bundle extras) { - if (uri == null || Uri.EMPTY.equals(uri)) { - throw new IllegalArgumentException( - "You must specify a non-empty Uri for prepareFromUri."); - } - try { - mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); - } - } - - /** - * Request that the player start its playback at its current position. - */ - public void play() { - try { - mSessionBinder.play(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play.", e); - } - } - - /** - * Request that the player start playback for a specific media id. - * - * @param mediaId The id of the requested media. - * @param extras Optional extras that can include extra information about the media item - * to be played. - */ - public void playFromMediaId(String mediaId, Bundle extras) { - if (TextUtils.isEmpty(mediaId)) { - throw new IllegalArgumentException( - "You must specify a non-empty String for playFromMediaId."); - } - try { - mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId, - extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); - } - } - - /** - * Request that the player start playback for a specific search query. - * An empty or null query should be treated as a request to play any - * music. - * - * @param query The search query. - * @param extras Optional extras that can include extra information - * about the query. - */ - public void playFromSearch(String query, Bundle extras) { - if (query == null) { - // This is to remain compatible with - // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH - query = ""; - } - try { - mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + query + ").", e); - } - } - - /** - * Request that the player start playback for a specific {@link Uri}. - * - * @param uri The URI of the requested media. - * @param extras Optional extras that can include extra information about the media item - * to be played. - */ - public void playFromUri(Uri uri, Bundle extras) { - if (uri == null || Uri.EMPTY.equals(uri)) { - throw new IllegalArgumentException( - "You must specify a non-empty Uri for playFromUri."); - } - try { - mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + uri + ").", e); - } - } - - /** - * Play an item with a specific id in the play queue. If you specify an - * id that is not in the play queue, the behavior is undefined. - */ - public void skipToQueueItem(long id) { - try { - mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); - } - } - - /** - * Request that the player pause its playback and stay at its current - * position. - */ - public void pause() { - try { - mSessionBinder.pause(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling pause.", e); - } - } - - /** - * Request that the player stop its playback; it may clear its state in - * whatever way is appropriate. - */ - public void stop() { - try { - mSessionBinder.stop(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling stop.", e); - } - } - - /** - * Move to a new location in the media stream. - * - * @param pos Position to move to, in milliseconds. - */ - public void seekTo(long pos) { - try { - mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling seekTo.", e); - } - } - - /** - * Start fast forwarding. If playback is already fast forwarding this - * may increase the rate. - */ - public void fastForward() { - try { - mSessionBinder.fastForward(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling fastForward.", e); - } - } - - /** - * Skip to the next item. - */ - public void skipToNext() { - try { - mSessionBinder.next(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling next.", e); - } - } - - /** - * Start rewinding. If playback is already rewinding this may increase - * the rate. - */ - public void rewind() { - try { - mSessionBinder.rewind(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rewind.", e); - } - } - - /** - * Skip to the previous item. - */ - public void skipToPrevious() { - try { - mSessionBinder.previous(mContext.getPackageName(), mCbStub); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling previous.", e); - } - } - - /** - * Rate the current content. This will cause the rating to be set for - * the current user. The Rating type must match the type returned by - * {@link #getRatingType()}. - * - * @param rating The rating to set for the current content - */ - public void setRating(Rating rating) { - try { - mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rate.", e); - } - } - - /** - * Send a custom action back for the {@link MediaSession} to perform. - * - * @param customAction The action to perform. - * @param args Optional arguments to supply to the {@link MediaSession} for this - * custom action. - */ - public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, - @Nullable Bundle args) { - if (customAction == null) { - throw new IllegalArgumentException("CustomAction cannot be null."); - } - sendCustomAction(customAction.getAction(), args); - } - - /** - * Send the id and args from a custom action back for the {@link MediaSession} to perform. - * - * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args) - * @param action The action identifier of the {@link PlaybackState.CustomAction} as - * specified by the {@link MediaSession}. - * @param args Optional arguments to supply to the {@link MediaSession} for this - * custom action. - */ - public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { - if (TextUtils.isEmpty(action)) { - throw new IllegalArgumentException("CustomAction cannot be null."); - } - try { - mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCustomAction.", e); - } - } - } - - /** - * Holds information about the current playback and how audio is handled for - * this session. - */ - public static final class PlaybackInfo { - /** - * The session uses remote playback. - */ - public static final int PLAYBACK_TYPE_REMOTE = 2; - /** - * The session uses local playback. - */ - public static final int PLAYBACK_TYPE_LOCAL = 1; - - private final int mVolumeType; - private final int mVolumeControl; - private final int mMaxVolume; - private final int mCurrentVolume; - private final AudioAttributes mAudioAttrs; - - /** - * @hide - */ - public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) { - mVolumeType = type; - mAudioAttrs = attrs; - mVolumeControl = control; - mMaxVolume = max; - mCurrentVolume = current; - } - - /** - * Get the type of playback which affects volume handling. One of: - *
      - *
    • {@link #PLAYBACK_TYPE_LOCAL}
    • - *
    • {@link #PLAYBACK_TYPE_REMOTE}
    • - *
    - * - * @return The type of playback this session is using. - */ - public int getPlaybackType() { - return mVolumeType; - } - - /** - * Get the audio attributes for this session. The attributes will affect - * volume handling for the session. When the volume type is - * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the - * remote volume handler. - * - * @return The attributes for this session. - */ - public AudioAttributes getAudioAttributes() { - return mAudioAttrs; - } - - /** - * Get the type of volume control that can be used. One of: - *
      - *
    • {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}
    • - *
    • {@link VolumeProvider#VOLUME_CONTROL_RELATIVE}
    • - *
    • {@link VolumeProvider#VOLUME_CONTROL_FIXED}
    • - *
    - * - * @return The type of volume control that may be used with this - * session. - */ - public int getVolumeControl() { - return mVolumeControl; - } - - /** - * Get the maximum volume that may be set for this session. - * - * @return The maximum allowed volume where this session is playing. - */ - public int getMaxVolume() { - return mMaxVolume; - } - - /** - * Get the current volume for this session. - * - * @return The current volume where this session is playing. - */ - public int getCurrentVolume() { - return mCurrentVolume; - } - } - - private final static class CallbackStub extends ISessionControllerCallback.Stub { - private final WeakReference mController; - - public CallbackStub(MediaController controller) { - mController = new WeakReference(controller); - } - - @Override - public void onSessionDestroyed() { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_DESTROYED, null, null); - } - } - - @Override - public void onEvent(String event, Bundle extras) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_EVENT, event, extras); - } - } - - @Override - public void onPlaybackStateChanged(PlaybackState state) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null); - } - } - - @Override - public void onMetadataChanged(MediaMetadata metadata) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_METADATA, metadata, null); - } - } - - @Override - public void onQueueChanged(MediaParceledListSlice parceledQueue) { - List queue = parceledQueue == null ? null : parceledQueue - .getList(); - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_QUEUE, queue, null); - } - } - - @Override - public void onQueueTitleChanged(CharSequence title) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null); - } - } - - @Override - public void onExtrasChanged(Bundle extras) { - MediaController controller = mController.get(); - if (controller != null) { - controller.postMessage(MSG_UPDATE_EXTRAS, extras, null); - } - } - - @Override - public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) { - MediaController controller = mController.get(); - if (controller != null) { - PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, - pvi.controlType, pvi.maxVolume, pvi.currentVolume); - controller.postMessage(MSG_UPDATE_VOLUME, info, null); - } - } - - } - - private final static class MessageHandler extends Handler { - private final MediaController.Callback mCallback; - private boolean mRegistered = false; - - public MessageHandler(Looper looper, MediaController.Callback cb) { - super(looper); - mCallback = cb; - } - - @Override - public void handleMessage(Message msg) { - if (!mRegistered) { - return; - } - switch (msg.what) { - case MSG_EVENT: - mCallback.onSessionEvent((String) msg.obj, msg.getData()); - break; - case MSG_UPDATE_PLAYBACK_STATE: - mCallback.onPlaybackStateChanged((PlaybackState) msg.obj); - break; - case MSG_UPDATE_METADATA: - mCallback.onMetadataChanged((MediaMetadata) msg.obj); - break; - case MSG_UPDATE_QUEUE: - mCallback.onQueueChanged((List) msg.obj); - break; - case MSG_UPDATE_QUEUE_TITLE: - mCallback.onQueueTitleChanged((CharSequence) msg.obj); - break; - case MSG_UPDATE_EXTRAS: - mCallback.onExtrasChanged((Bundle) msg.obj); - break; - case MSG_UPDATE_VOLUME: - mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj); - break; - case MSG_DESTROYED: - mCallback.onSessionDestroyed(); - break; - } - } - - public void post(int what, Object obj, Bundle data) { - Message msg = obtainMessage(what, obj); - msg.setAsynchronous(true); - msg.setData(data); - msg.sendToTarget(); - } - } - -} diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl deleted file mode 100644 index f657cef76b..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright 2014, 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.session; - -parcelable MediaSession.Token; -parcelable MediaSession.QueueItem; \ No newline at end of file diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java deleted file mode 100644 index 3cbeff9e25..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java +++ /dev/null @@ -1,1569 +0,0 @@ -/* - * Copyright (C) 2014 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.session; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.media.AudioAttributes; -import android.media.MediaDescription; -import android.media.MediaMetadata; -import android.media.MediaParceledListSlice; -import android.media.Rating; -import android.media.VolumeProvider; -import android.media.session.MediaSessionManager.RemoteUserInfo; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.UserHandle; -import android.service.media.MediaBrowserService; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; -import android.view.KeyEvent; -import android.view.ViewConfiguration; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.Objects; - -/** - * Allows interaction with media controllers, volume keys, media buttons, and - * transport controls. - *

    - * A MediaSession should be created when an app wants to publish media playback - * information or handle media keys. In general an app only needs one session - * for all playback, though multiple sessions can be created to provide finer - * grain controls of media. - *

    - * Once a session is created the owner of the session may pass its - * {@link #getSessionToken() session token} to other processes to allow them to - * create a {@link MediaController} to interact with the session. - *

    - * To receive commands, media keys, and other events a {@link Callback} must be - * set with {@link #setCallback(Callback)} and {@link #setActive(boolean) - * setActive(true)} must be called. - *

    - * When an app is finished performing playback it must call {@link #release()} - * to clean up the session and notify any controllers. - *

    - * MediaSession objects are thread safe. - */ -public final class MediaSession { - private static final String TAG = "MediaSession"; - - /** - * Set this flag on the session to indicate that it can handle media button - * events. - * @deprecated This flag is no longer used. All media sessions are expected to handle media - * button events now. - */ - @Deprecated - public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0; - - /** - * Set this flag on the session to indicate that it handles transport - * control commands through its {@link Callback}. - * @deprecated This flag is no longer used. All media sessions are expected to handle transport - * controls now. - */ - @Deprecated - public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; - - /** - * System only flag for a session that needs to have priority over all other - * sessions. This flag ensures this session will receive media button events - * regardless of the current ordering in the system. - * - * @hide - */ - public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16; - - /** - * @hide - */ - public static final int INVALID_UID = -1; - - /** - * @hide - */ - public static final int INVALID_PID = -1; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, value = { - FLAG_HANDLES_MEDIA_BUTTONS, - FLAG_HANDLES_TRANSPORT_CONTROLS, - FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) - public @interface SessionFlags { } - - private final Object mLock = new Object(); - private final int mMaxBitmapSize; - - private final MediaSession.Token mSessionToken; - private final MediaController mController; - private final ISession mBinder; - private final CallbackStub mCbStub; - - // Do not change the name of mCallback. Support lib accesses this by using reflection. - @UnsupportedAppUsage - private CallbackMessageHandler mCallback; - private VolumeProvider mVolumeProvider; - private PlaybackState mPlaybackState; - - private boolean mActive = false; - - /** - * Creates a new session. The session will automatically be registered with - * the system but will not be published until {@link #setActive(boolean) - * setActive(true)} is called. You must call {@link #release()} when - * finished with the session. - * - * @param context The context to use to create the session. - * @param tag A short name for debugging purposes. - */ - public MediaSession(@NonNull Context context, @NonNull String tag) { - this(context, tag, UserHandle.myUserId()); - } - - /** - * Creates a new session as the specified user. To create a session as a - * user other than your own you must hold the - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} - * permission. - * - * @param context The context to use to create the session. - * @param tag A short name for debugging purposes. - * @param userId The user id to create the session as. - * @hide - */ - public MediaSession(@NonNull Context context, @NonNull String tag, int userId) { - if (context == null) { - throw new IllegalArgumentException("context cannot be null."); - } - if (TextUtils.isEmpty(tag)) { - throw new IllegalArgumentException("tag cannot be null or empty"); - } - mMaxBitmapSize = context.getResources().getDimensionPixelSize( - android.R.dimen.config_mediaMetadataBitmapMaxSize); - mCbStub = new CallbackStub(this); - MediaSessionManager manager = (MediaSessionManager) context - .getSystemService(Context.MEDIA_SESSION_SERVICE); - try { - //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession - //mBinder = manager.createSession(mCbStub, tag, userId); - mBinder = null; //TODO: remove this. - mSessionToken = new Token(mBinder.getController()); - mController = new MediaController(context, mSessionToken); - } catch (RemoteException e) { - throw new RuntimeException("Remote error creating session.", e); - } - } - - /** - * Set the callback to receive updates for the MediaSession. This includes - * media button events and transport controls. The caller's thread will be - * used to post updates. - *

    - * Set the callback to null to stop receiving updates. - * - * @param callback The callback object - */ - public void setCallback(@Nullable Callback callback) { - setCallback(callback, null); - } - - /** - * Set the callback to receive updates for the MediaSession. This includes - * media button events and transport controls. - *

    - * Set the callback to null to stop receiving updates. - * - * @param callback The callback to receive updates on. - * @param handler The handler that events should be posted on. - */ - public void setCallback(@Nullable Callback callback, @Nullable Handler handler) { - synchronized (mLock) { - if (mCallback != null) { - // We're updating the callback, clear the session from the old one. - mCallback.mCallback.mSession = null; - mCallback.removeCallbacksAndMessages(null); - } - if (callback == null) { - mCallback = null; - return; - } - if (handler == null) { - handler = new Handler(); - } - callback.mSession = this; - CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(), - callback); - mCallback = msgHandler; - } - } - - /** - * Set an intent for launching UI for this Session. This can be used as a - * quick link to an ongoing media screen. The intent should be for an - * activity that may be started using {@link Activity#startActivity(Intent)}. - * - * @param pi The intent to launch to show UI for this Session. - */ - public void setSessionActivity(@Nullable PendingIntent pi) { - try { - mBinder.setLaunchPendingIntent(pi); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e); - } - } - - /** - * Set a pending intent for your media button receiver to allow restarting - * playback after the session has been stopped. If your app is started in - * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via - * the pending intent. - * - * @param mbr The {@link PendingIntent} to send the media button event to. - */ - public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { - try { - mBinder.setMediaButtonReceiver(mbr); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e); - } - } - - /** - * Set any flags for the session. - * - * @param flags The flags to set for this session. - */ - public void setFlags(@SessionFlags int flags) { - try { - mBinder.setFlags(flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setFlags.", e); - } - } - - /** - * Set the attributes for this session's audio. This will affect the - * system's volume handling for this session. If - * {@link #setPlaybackToRemote} was previously called it will stop receiving - * volume commands and the system will begin sending volume changes to the - * appropriate stream. - *

    - * By default sessions use attributes for media. - * - * @param attributes The {@link AudioAttributes} for this session's audio. - */ - public void setPlaybackToLocal(AudioAttributes attributes) { - if (attributes == null) { - throw new IllegalArgumentException("Attributes cannot be null for local playback."); - } - //TODO(b/119751592): Decide if AudioAttributes should be updated. - /* - try { - mBinder.setPlaybackToLocal(attributes); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); - } - */ - } - - /** - * Configure this session to use remote volume handling. This must be called - * to receive volume button events, otherwise the system will adjust the - * appropriate stream volume for this session. If - * {@link #setPlaybackToLocal} was previously called the system will stop - * handling volume changes for this session and pass them to the volume - * provider instead. - * - * @param volumeProvider The provider that will handle volume changes. May - * not be null. - */ - public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) { - if (volumeProvider == null) { - throw new IllegalArgumentException("volumeProvider may not be null!"); - } - synchronized (mLock) { - mVolumeProvider = volumeProvider; - } - volumeProvider.setCallback(new VolumeProvider.Callback() { - @Override - public void onVolumeChanged(VolumeProvider volumeProvider) { - notifyRemoteVolumeChanged(volumeProvider); - } - }); - - try { - mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(), - volumeProvider.getMaxVolume()); - mBinder.setCurrentVolume(volumeProvider.getCurrentVolume()); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); - } - } - - /** - * Set if this session is currently active and ready to receive commands. If - * set to false your session's controller may not be discoverable. You must - * set the session to active before it can start receiving media button - * events or transport commands. - * - * @param active Whether this session is active or not. - */ - public void setActive(boolean active) { - if (mActive == active) { - return; - } - try { - mBinder.setActive(active); - mActive = active; - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setActive.", e); - } - } - - /** - * Get the current active state of this session. - * - * @return True if the session is active, false otherwise. - */ - public boolean isActive() { - return mActive; - } - - /** - * Send a proprietary event to all MediaControllers listening to this - * Session. It's up to the Controller/Session owner to determine the meaning - * of any events. - * - * @param event The name of the event to send - * @param extras Any extras included with the event - */ - public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) { - if (TextUtils.isEmpty(event)) { - throw new IllegalArgumentException("event cannot be null or empty"); - } - try { - mBinder.sendEvent(event, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error sending event", e); - } - } - - /** - * This must be called when an app has finished performing playback. If - * playback is expected to start again shortly the session can be left open, - * but it must be released if your activity or service is being destroyed. - */ - public void release() { - try { - mBinder.destroy(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error releasing session: ", e); - } - } - - /** - * Retrieve a token object that can be used by apps to create a - * {@link MediaController} for interacting with this session. The owner of - * the session is responsible for deciding how to distribute these tokens. - * - * @return A token that can be used to create a MediaController for this - * session - */ - public @NonNull Token getSessionToken() { - return mSessionToken; - } - - /** - * Get a controller for this session. This is a convenience method to avoid - * having to cache your own controller in process. - * - * @return A controller for this session. - */ - public @NonNull MediaController getController() { - return mController; - } - - /** - * Update the current playback state. - * - * @param state The current state of playback - */ - public void setPlaybackState(@Nullable PlaybackState state) { - mPlaybackState = state; - try { - mBinder.setPlaybackState(state); - } catch (RemoteException e) { - Log.wtf(TAG, "Dead object in setPlaybackState.", e); - } - } - - /** - * Update the current metadata. New metadata can be created using - * {@link android.media.MediaMetadata.Builder}. This operation may take time proportional to - * the size of the bitmap to replace large bitmaps with a scaled down copy. - * - * @param metadata The new metadata - * @see android.media.MediaMetadata.Builder#putBitmap - */ - public void setMetadata(@Nullable MediaMetadata metadata) { - long duration = -1; - int fields = 0; - MediaDescription description = null; - if (metadata != null) { - metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build(); - if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { - duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION); - } - fields = metadata.size(); - description = metadata.getDescription(); - } - String metadataDescription = "size=" + fields + ", description=" + description; - - try { - mBinder.setMetadata(metadata, duration, metadataDescription); - } catch (RemoteException e) { - Log.wtf(TAG, "Dead object in setPlaybackState.", e); - } - } - - /** - * Update the list of items in the play queue. It is an ordered list and - * should contain the current item, and previous or upcoming items if they - * exist. Specify null if there is no current play queue. - *

    - * The queue should be of reasonable size. If the play queue is unbounded - * within your app, it is better to send a reasonable amount in a sliding - * window instead. - * - * @param queue A list of items in the play queue. - */ - public void setQueue(@Nullable List queue) { - try { - mBinder.setQueue(queue == null ? null : new MediaParceledListSlice(queue)); - } catch (RemoteException e) { - Log.wtf("Dead object in setQueue.", e); - } - } - - /** - * Set the title of the play queue. The UI should display this title along - * with the play queue itself. - * e.g. "Play Queue", "Now Playing", or an album name. - * - * @param title The title of the play queue. - */ - public void setQueueTitle(@Nullable CharSequence title) { - try { - mBinder.setQueueTitle(title); - } catch (RemoteException e) { - Log.wtf("Dead object in setQueueTitle.", e); - } - } - - /** - * Set the style of rating used by this session. Apps trying to set the - * rating should use this style. Must be one of the following: - *

      - *
    • {@link Rating#RATING_NONE}
    • - *
    • {@link Rating#RATING_3_STARS}
    • - *
    • {@link Rating#RATING_4_STARS}
    • - *
    • {@link Rating#RATING_5_STARS}
    • - *
    • {@link Rating#RATING_HEART}
    • - *
    • {@link Rating#RATING_PERCENTAGE}
    • - *
    • {@link Rating#RATING_THUMB_UP_DOWN}
    • - *
    - */ - public void setRatingType(@Rating.Style int type) { - try { - mBinder.setRatingType(type); - } catch (RemoteException e) { - Log.e(TAG, "Error in setRatingType.", e); - } - } - - /** - * Set some extras that can be associated with the {@link MediaSession}. No assumptions should - * be made as to how a {@link MediaController} will handle these extras. - * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts. - * - * @param extras The extras associated with the {@link MediaSession}. - */ - public void setExtras(@Nullable Bundle extras) { - try { - mBinder.setExtras(extras); - } catch (RemoteException e) { - Log.wtf("Dead object in setExtras.", e); - } - } - - /** - * Gets the controller information who sent the current request. - *

    - * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}. - * - * @throws IllegalStateException If this method is called outside of {@link Callback} methods. - * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) - */ - public final @NonNull RemoteUserInfo getCurrentControllerInfo() { - if (mCallback == null || mCallback.mCurrentControllerInfo == null) { - throw new IllegalStateException( - "This should be called inside of MediaSession.Callback methods"); - } - return mCallback.mCurrentControllerInfo; - } - - /** - * Notify the system that the remote volume changed. - * - * @param provider The provider that is handling volume changes. - * @hide - */ - public void notifyRemoteVolumeChanged(VolumeProvider provider) { - synchronized (mLock) { - if (provider == null || provider != mVolumeProvider) { - Log.w(TAG, "Received update from stale volume provider"); - return; - } - } - try { - mBinder.setCurrentVolume(provider.getCurrentVolume()); - } catch (RemoteException e) { - Log.e(TAG, "Error in notifyVolumeChanged", e); - } - } - - /** - * Returns the name of the package that sent the last media button, transport control, or - * command from controllers and the system. This is only valid while in a request callback, such - * as {@link Callback#onPlay}. - * - * @hide - */ - @UnsupportedAppUsage - public String getCallingPackage() { - if (mCallback != null && mCallback.mCurrentControllerInfo != null) { - return mCallback.mCurrentControllerInfo.getPackageName(); - } - return null; - } - - private void dispatchPrepare(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null); - } - - private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); - } - - private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras); - } - - private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras); - } - - private void dispatchPlay(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null); - } - - private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); - } - - private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras); - } - - private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) { - postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras); - } - - private void dispatchSkipToItem(RemoteUserInfo caller, long id) { - postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null); - } - - private void dispatchPause(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null); - } - - private void dispatchStop(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null); - } - - private void dispatchNext(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null); - } - - private void dispatchPrevious(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null); - } - - private void dispatchFastForward(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null); - } - - private void dispatchRewind(RemoteUserInfo caller) { - postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null); - } - - private void dispatchSeekTo(RemoteUserInfo caller, long pos) { - postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null); - } - - private void dispatchRate(RemoteUserInfo caller, Rating rating) { - postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null); - } - - private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) { - postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); - } - - private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) { - postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null); - } - - private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent, - long delay) { - postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, - mediaButtonIntent, null, delay); - } - - private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) { - postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null); - } - - private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) { - postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null); - } - - private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args, - ResultReceiver resultCb) { - Command cmd = new Command(command, args, resultCb); - postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null); - } - - private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) { - postToCallbackDelayed(caller, what, obj, data, 0); - } - - private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data, - long delay) { - synchronized (mLock) { - if (mCallback != null) { - mCallback.post(caller, what, obj, data, delay); - } - } - } - - /** - * Return true if this is considered an active playback state. - * - * @hide - */ - public static boolean isActiveState(int state) { - switch (state) { - case PlaybackState.STATE_FAST_FORWARDING: - case PlaybackState.STATE_REWINDING: - case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: - case PlaybackState.STATE_SKIPPING_TO_NEXT: - case PlaybackState.STATE_BUFFERING: - case PlaybackState.STATE_CONNECTING: - case PlaybackState.STATE_PLAYING: - return true; - } - return false; - } - - /** - * Represents an ongoing session. This may be passed to apps by the session - * owner to allow them to create a {@link MediaController} to communicate with - * the session. - */ - public static final class Token implements Parcelable { - - private ISessionController mBinder; - - /** - * @hide - */ - public Token(ISessionController binder) { - mBinder = binder; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mBinder.asBinder()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mBinder == null) ? 0 : mBinder.asBinder().hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Token other = (Token) obj; - if (mBinder == null) { - if (other.mBinder != null) - return false; - } else if (!mBinder.asBinder().equals(other.mBinder.asBinder())) - return false; - return true; - } - - ISessionController getBinder() { - return mBinder; - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public Token createFromParcel(Parcel in) { - return new Token(ISessionController.Stub.asInterface(in.readStrongBinder())); - } - - @Override - public Token[] newArray(int size) { - return new Token[size]; - } - }; - } - - /** - * Receives media buttons, transport controls, and commands from controllers - * and the system. A callback may be set using {@link #setCallback}. - */ - public abstract static class Callback { - - private MediaSession mSession; - private CallbackMessageHandler mHandler; - private boolean mMediaPlayPauseKeyPending; - - public Callback() { - } - - /** - * Called when a controller has sent a command to this session. - * The owner of the session may handle custom commands but is not - * required to. - * - * @param command The command name. - * @param args Optional parameters for the command, may be null. - * @param cb A result receiver to which a result may be sent by the command, may be null. - */ - public void onCommand(@NonNull String command, @Nullable Bundle args, - @Nullable ResultReceiver cb) { - } - - /** - * Called when a media button is pressed and this session has the - * highest priority or a controller sends a media button event to the - * session. The default behavior will call the relevant method if the - * action for it was set. - *

    - * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a - * KeyEvent in {@link Intent#EXTRA_KEY_EVENT} - * - * @param mediaButtonIntent an intent containing the KeyEvent as an - * extra - * @return True if the event was handled, false otherwise. - */ - public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) { - if (mSession != null && mHandler != null - && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) { - KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); - if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) { - PlaybackState state = mSession.mPlaybackState; - long validActions = state == null ? 0 : state.getActions(); - switch (ke.getKeyCode()) { - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_HEADSETHOOK: - if (ke.getRepeatCount() > 0) { - // Consider long-press as a single tap. - handleMediaPlayPauseKeySingleTapIfPending(); - } else if (mMediaPlayPauseKeyPending) { - // Consider double tap as the next. - mHandler.removeMessages(CallbackMessageHandler - .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); - mMediaPlayPauseKeyPending = false; - if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { - onSkipToNext(); - } - } else { - mMediaPlayPauseKeyPending = true; - mSession.dispatchMediaButtonDelayed( - mSession.getCurrentControllerInfo(), - mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout()); - } - return true; - default: - // If another key is pressed within double tap timeout, consider the - // pending play/pause as a single tap to handle media keys in order. - handleMediaPlayPauseKeySingleTapIfPending(); - break; - } - - switch (ke.getKeyCode()) { - case KeyEvent.KEYCODE_MEDIA_PLAY: - if ((validActions & PlaybackState.ACTION_PLAY) != 0) { - onPlay(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_PAUSE: - if ((validActions & PlaybackState.ACTION_PAUSE) != 0) { - onPause(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_NEXT: - if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) { - onSkipToNext(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) { - onSkipToPrevious(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_STOP: - if ((validActions & PlaybackState.ACTION_STOP) != 0) { - onStop(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) { - onFastForward(); - return true; - } - break; - case KeyEvent.KEYCODE_MEDIA_REWIND: - if ((validActions & PlaybackState.ACTION_REWIND) != 0) { - onRewind(); - return true; - } - break; - } - } - } - return false; - } - - private void handleMediaPlayPauseKeySingleTapIfPending() { - if (!mMediaPlayPauseKeyPending) { - return; - } - mMediaPlayPauseKeyPending = false; - mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT); - PlaybackState state = mSession.mPlaybackState; - long validActions = state == null ? 0 : state.getActions(); - boolean isPlaying = state != null - && state.getState() == PlaybackState.STATE_PLAYING; - boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE - | PlaybackState.ACTION_PLAY)) != 0; - boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE - | PlaybackState.ACTION_PAUSE)) != 0; - if (isPlaying && canPause) { - onPause(); - } else if (!isPlaying && canPlay) { - onPlay(); - } - } - - /** - * Override to handle requests to prepare playback. During the preparation, a session should - * not hold audio focus in order to allow other sessions play seamlessly. The state of - * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is - * done. - */ - public void onPrepare() { - } - - /** - * Override to handle requests to prepare for playing a specific mediaId that was provided - * by your app's {@link MediaBrowserService}. During the preparation, a session should not - * hold audio focus in order to allow other sessions play seamlessly. The state of playback - * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. - * The playback of the prepared content should start in the implementation of - * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting - * playback without preparation. - */ - public void onPrepareFromMediaId(String mediaId, Bundle extras) { - } - - /** - * Override to handle requests to prepare playback from a search query. An empty query - * indicates that the app may prepare any music. The implementation should attempt to make a - * smart choice about what to play. During the preparation, a session should not hold audio - * focus in order to allow other sessions play seamlessly. The state of playback should be - * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback - * of the prepared content should start in the implementation of {@link #onPlay}. Override - * {@link #onPlayFromSearch} to handle requests for starting playback without preparation. - */ - public void onPrepareFromSearch(String query, Bundle extras) { - } - - /** - * Override to handle requests to prepare a specific media item represented by a URI. - * During the preparation, a session should not hold audio focus in order to allow - * other sessions play seamlessly. The state of playback should be updated to - * {@link PlaybackState#STATE_PAUSED} after the preparation is done. - * The playback of the prepared content should start in the implementation of - * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests - * for starting playback without preparation. - */ - public void onPrepareFromUri(Uri uri, Bundle extras) { - } - - /** - * Override to handle requests to begin playback. - */ - public void onPlay() { - } - - /** - * Override to handle requests to begin playback from a search query. An - * empty query indicates that the app may play any music. The - * implementation should attempt to make a smart choice about what to - * play. - */ - public void onPlayFromSearch(String query, Bundle extras) { - } - - /** - * Override to handle requests to play a specific mediaId that was - * provided by your app's {@link MediaBrowserService}. - */ - public void onPlayFromMediaId(String mediaId, Bundle extras) { - } - - /** - * Override to handle requests to play a specific media item represented by a URI. - */ - public void onPlayFromUri(Uri uri, Bundle extras) { - } - - /** - * Override to handle requests to play an item with a given id from the - * play queue. - */ - public void onSkipToQueueItem(long id) { - } - - /** - * Override to handle requests to pause playback. - */ - public void onPause() { - } - - /** - * Override to handle requests to skip to the next media item. - */ - public void onSkipToNext() { - } - - /** - * Override to handle requests to skip to the previous media item. - */ - public void onSkipToPrevious() { - } - - /** - * Override to handle requests to fast forward. - */ - public void onFastForward() { - } - - /** - * Override to handle requests to rewind. - */ - public void onRewind() { - } - - /** - * Override to handle requests to stop playback. - */ - public void onStop() { - } - - /** - * Override to handle requests to seek to a specific position in ms. - * - * @param pos New position to move to, in milliseconds. - */ - public void onSeekTo(long pos) { - } - - /** - * Override to handle the item being rated. - * - * @param rating - */ - public void onSetRating(@NonNull Rating rating) { - } - - /** - * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be - * performed. - * - * @param action The action that was originally sent in the - * {@link PlaybackState.CustomAction}. - * @param extras Optional extras specified by the {@link MediaController}. - */ - public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { - } - } - - /** - * @hide - */ - public static class CallbackStub extends ISessionCallback.Stub { - private WeakReference mMediaSession; - - public CallbackStub(MediaSession session) { - mMediaSession = new WeakReference<>(session); - } - - private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - return new RemoteUserInfo(packageName, pid, uid); - } - - @Override - public void onCommand(String packageName, int pid, int uid, - ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller), - command, args, cb); - } - } - - @Override - public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent, - int sequenceNumber, ResultReceiver cb) { - MediaSession session = mMediaSession.get(); - try { - if (session != null) { - session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null), - mediaButtonIntent); - } - } finally { - if (cb != null) { - cb.send(sequenceNumber, null); - } - } - } - - @Override - public void onMediaButtonFromController(String packageName, int pid, int uid, - ISessionControllerCallback caller, Intent mediaButtonIntent) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller), - mediaButtonIntent); - } - } - - @Override - public void onPrepare(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onPrepareFromMediaId(String packageName, int pid, int uid, - ISessionControllerCallback caller, String mediaId, - Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPrepareFromMediaId( - createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras); - } - } - - @Override - public void onPrepareFromSearch(String packageName, int pid, int uid, - ISessionControllerCallback caller, String query, - Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPrepareFromSearch( - createRemoteUserInfo(packageName, pid, uid, caller), query, extras); - } - } - - @Override - public void onPrepareFromUri(String packageName, int pid, int uid, - ISessionControllerCallback caller, Uri uri, Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller), - uri, extras); - } - } - - @Override - public void onPlay(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onPlayFromMediaId(String packageName, int pid, int uid, - ISessionControllerCallback caller, String mediaId, - Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller), - mediaId, extras); - } - } - - @Override - public void onPlayFromSearch(String packageName, int pid, int uid, - ISessionControllerCallback caller, String query, - Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller), - query, extras); - } - } - - @Override - public void onPlayFromUri(String packageName, int pid, int uid, - ISessionControllerCallback caller, Uri uri, Bundle extras) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller), - uri, extras); - } - } - - @Override - public void onSkipToTrack(String packageName, int pid, int uid, - ISessionControllerCallback caller, long id) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id); - } - } - - @Override - public void onPause(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onStop(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onNext(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onPrevious(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onFastForward(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onRewind(String packageName, int pid, int uid, - ISessionControllerCallback caller) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller)); - } - } - - @Override - public void onSeekTo(String packageName, int pid, int uid, - ISessionControllerCallback caller, long pos) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos); - } - } - - @Override - public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller, - Rating rating) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating); - } - } - - @Override - public void onCustomAction(String packageName, int pid, int uid, - ISessionControllerCallback caller, String action, Bundle args) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller), - action, args); - } - } - - @Override - public void onAdjustVolume(String packageName, int pid, int uid, - ISessionControllerCallback caller, int direction) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller), - direction); - } - } - - @Override - public void onSetVolumeTo(String packageName, int pid, int uid, - ISessionControllerCallback caller, int value) { - MediaSession session = mMediaSession.get(); - if (session != null) { - session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller), - value); - } - } - } - - /** - * A single item that is part of the play queue. It contains a description - * of the item and its id in the queue. - */ - public static final class QueueItem implements Parcelable { - /** - * This id is reserved. No items can be explicitly assigned this id. - */ - public static final int UNKNOWN_ID = -1; - - private final MediaDescription mDescription; - @UnsupportedAppUsage - private final long mId; - - /** - * Create a new {@link MediaSession.QueueItem}. - * - * @param description The {@link MediaDescription} for this item. - * @param id An identifier for this item. It must be unique within the - * play queue and cannot be {@link #UNKNOWN_ID}. - */ - public QueueItem(MediaDescription description, long id) { - if (description == null) { - throw new IllegalArgumentException("Description cannot be null."); - } - if (id == UNKNOWN_ID) { - throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID"); - } - mDescription = description; - mId = id; - } - - private QueueItem(Parcel in) { - mDescription = MediaDescription.CREATOR.createFromParcel(in); - mId = in.readLong(); - } - - /** - * Get the description for this item. - */ - public MediaDescription getDescription() { - return mDescription; - } - - /** - * Get the queue id for this item. - */ - public long getQueueId() { - return mId; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - mDescription.writeToParcel(dest, flags); - dest.writeLong(mId); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator CREATOR = - new Creator() { - - @Override - public MediaSession.QueueItem createFromParcel(Parcel p) { - return new MediaSession.QueueItem(p); - } - - @Override - public MediaSession.QueueItem[] newArray(int size) { - return new MediaSession.QueueItem[size]; - } - }; - - @Override - public String toString() { - return "MediaSession.QueueItem {" + - "Description=" + mDescription + - ", Id=" + mId + " }"; - } - - @Override - public boolean equals(Object o) { - if (o == null) { - return false; - } - - if (!(o instanceof QueueItem)) { - return false; - } - - final QueueItem item = (QueueItem) o; - if (mId != item.mId) { - return false; - } - - if (!Objects.equals(mDescription, item.mDescription)) { - return false; - } - - return true; - } - } - - private static final class Command { - public final String command; - public final Bundle extras; - public final ResultReceiver stub; - - public Command(String command, Bundle extras, ResultReceiver stub) { - this.command = command; - this.extras = extras; - this.stub = stub; - } - } - - private class CallbackMessageHandler extends Handler { - private static final int MSG_COMMAND = 1; - private static final int MSG_MEDIA_BUTTON = 2; - private static final int MSG_PREPARE = 3; - private static final int MSG_PREPARE_MEDIA_ID = 4; - private static final int MSG_PREPARE_SEARCH = 5; - private static final int MSG_PREPARE_URI = 6; - private static final int MSG_PLAY = 7; - private static final int MSG_PLAY_MEDIA_ID = 8; - private static final int MSG_PLAY_SEARCH = 9; - private static final int MSG_PLAY_URI = 10; - private static final int MSG_SKIP_TO_ITEM = 11; - private static final int MSG_PAUSE = 12; - private static final int MSG_STOP = 13; - private static final int MSG_NEXT = 14; - private static final int MSG_PREVIOUS = 15; - private static final int MSG_FAST_FORWARD = 16; - private static final int MSG_REWIND = 17; - private static final int MSG_SEEK_TO = 18; - private static final int MSG_RATE = 19; - private static final int MSG_CUSTOM_ACTION = 20; - private static final int MSG_ADJUST_VOLUME = 21; - private static final int MSG_SET_VOLUME = 22; - private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; - - private MediaSession.Callback mCallback; - private RemoteUserInfo mCurrentControllerInfo; - - public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { - super(looper); - mCallback = callback; - mCallback.mHandler = this; - } - - public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) { - Pair objWithCaller = Pair.create(caller, obj); - Message msg = obtainMessage(what, objWithCaller); - msg.setAsynchronous(true); - msg.setData(data); - if (delayMs > 0) { - sendMessageDelayed(msg, delayMs); - } else { - sendMessage(msg); - } - } - - @Override - public void handleMessage(Message msg) { - mCurrentControllerInfo = ((Pair) msg.obj).first; - - VolumeProvider vp; - Object obj = ((Pair) msg.obj).second; - - switch (msg.what) { - case MSG_COMMAND: - Command cmd = (Command) obj; - mCallback.onCommand(cmd.command, cmd.extras, cmd.stub); - break; - case MSG_MEDIA_BUTTON: - mCallback.onMediaButtonEvent((Intent) obj); - break; - case MSG_PREPARE: - mCallback.onPrepare(); - break; - case MSG_PREPARE_MEDIA_ID: - mCallback.onPrepareFromMediaId((String) obj, msg.getData()); - break; - case MSG_PREPARE_SEARCH: - mCallback.onPrepareFromSearch((String) obj, msg.getData()); - break; - case MSG_PREPARE_URI: - mCallback.onPrepareFromUri((Uri) obj, msg.getData()); - break; - case MSG_PLAY: - mCallback.onPlay(); - break; - case MSG_PLAY_MEDIA_ID: - mCallback.onPlayFromMediaId((String) obj, msg.getData()); - break; - case MSG_PLAY_SEARCH: - mCallback.onPlayFromSearch((String) obj, msg.getData()); - break; - case MSG_PLAY_URI: - mCallback.onPlayFromUri((Uri) obj, msg.getData()); - break; - case MSG_SKIP_TO_ITEM: - mCallback.onSkipToQueueItem((Long) obj); - break; - case MSG_PAUSE: - mCallback.onPause(); - break; - case MSG_STOP: - mCallback.onStop(); - break; - case MSG_NEXT: - mCallback.onSkipToNext(); - break; - case MSG_PREVIOUS: - mCallback.onSkipToPrevious(); - break; - case MSG_FAST_FORWARD: - mCallback.onFastForward(); - break; - case MSG_REWIND: - mCallback.onRewind(); - break; - case MSG_SEEK_TO: - mCallback.onSeekTo((Long) obj); - break; - case MSG_RATE: - mCallback.onSetRating((Rating) obj); - break; - case MSG_CUSTOM_ACTION: - mCallback.onCustomAction((String) obj, msg.getData()); - break; - case MSG_ADJUST_VOLUME: - synchronized (mLock) { - vp = mVolumeProvider; - } - if (vp != null) { - vp.onAdjustVolume((int) obj); - } - break; - case MSG_SET_VOLUME: - synchronized (mLock) { - vp = mVolumeProvider; - } - if (vp != null) { - vp.onSetVolumeTo((int) obj); - } - break; - case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT: - mCallback.handleMediaPlayPauseKeySingleTapIfPending(); - break; - } - mCurrentControllerInfo = null; - } - } -} diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl deleted file mode 100644 index c4250f097f..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2014, 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.session; - -parcelable ParcelableVolumeInfo; diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java deleted file mode 100644 index f59c9756d4..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2014, 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.session; - -import android.media.AudioAttributes; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Convenience class for passing information about the audio configuration of a - * session. The public implementation is {@link MediaController.PlaybackInfo}. - * - * @hide - */ -public class ParcelableVolumeInfo implements Parcelable { - public int volumeType; - public AudioAttributes audioAttrs; - public int controlType; - public int maxVolume; - public int currentVolume; - - public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType, - int maxVolume, - int currentVolume) { - this.volumeType = volumeType; - this.audioAttrs = audioAttrs; - this.controlType = controlType; - this.maxVolume = maxVolume; - this.currentVolume = currentVolume; - } - - public ParcelableVolumeInfo(Parcel from) { - volumeType = from.readInt(); - controlType = from.readInt(); - maxVolume = from.readInt(); - currentVolume = from.readInt(); - audioAttrs = AudioAttributes.CREATOR.createFromParcel(from); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(volumeType); - dest.writeInt(controlType); - dest.writeInt(maxVolume); - dest.writeInt(currentVolume); - audioAttrs.writeToParcel(dest, flags); - } - - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public ParcelableVolumeInfo createFromParcel(Parcel in) { - return new ParcelableVolumeInfo(in); - } - - @Override - public ParcelableVolumeInfo[] newArray(int size) { - return new ParcelableVolumeInfo[size]; - } - }; -} diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl deleted file mode 100644 index 0876ebd2d4..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2014, 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.session; - -parcelable PlaybackState; diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java deleted file mode 100644 index ed4f9af72f..0000000000 --- a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java +++ /dev/null @@ -1,1081 +0,0 @@ -/* - * Copyright (C) 2014 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.session; - -import android.annotation.DrawableRes; -import android.annotation.IntDef; -import android.annotation.LongDef; -import android.annotation.Nullable; -import android.media.RemoteControlClient; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.SystemClock; -import android.text.TextUtils; -import java.util.ArrayList; -import java.util.List; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Playback state for a {@link MediaSession}. This includes a state like - * {@link PlaybackState#STATE_PLAYING}, the current playback position, - * and the current control capabilities. - */ -public final class PlaybackState implements Parcelable { - private static final String TAG = "PlaybackState"; - - /** - * @hide - */ - @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, - ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, - ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, - ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, - ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI}) - @Retention(RetentionPolicy.SOURCE) - public @interface Actions {} - - /** - * Indicates this session supports the stop command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_STOP = 1 << 0; - - /** - * Indicates this session supports the pause command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PAUSE = 1 << 1; - - /** - * Indicates this session supports the play command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PLAY = 1 << 2; - - /** - * Indicates this session supports the rewind command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_REWIND = 1 << 3; - - /** - * Indicates this session supports the previous command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; - - /** - * Indicates this session supports the next command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SKIP_TO_NEXT = 1 << 5; - - /** - * Indicates this session supports the fast forward command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_FAST_FORWARD = 1 << 6; - - /** - * Indicates this session supports the set rating command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SET_RATING = 1 << 7; - - /** - * Indicates this session supports the seek to command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SEEK_TO = 1 << 8; - - /** - * Indicates this session supports the play/pause toggle command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PLAY_PAUSE = 1 << 9; - - /** - * Indicates this session supports the play from media id command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; - - /** - * Indicates this session supports the play from search command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; - - /** - * Indicates this session supports the skip to queue item command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; - - /** - * Indicates this session supports the play from URI command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PLAY_FROM_URI = 1 << 13; - - /** - * Indicates this session supports the prepare command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PREPARE = 1 << 14; - - /** - * Indicates this session supports the prepare from media id command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15; - - /** - * Indicates this session supports the prepare from search command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16; - - /** - * Indicates this session supports the prepare from URI command. - * - * @see Builder#setActions(long) - */ - public static final long ACTION_PREPARE_FROM_URI = 1 << 17; - - /** - * @hide - */ - @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, - STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING, - STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM}) - @Retention(RetentionPolicy.SOURCE) - public @interface State {} - - /** - * This is the default playback state and indicates that no media has been - * added yet, or the performer has been reset and has no content to play. - * - * @see Builder#setState(int, long, float) - * @see Builder#setState(int, long, float, long) - */ - public final static int STATE_NONE = 0; - - /** - * State indicating this item is currently stopped. - * - * @see Builder#setState - */ - public final static int STATE_STOPPED = 1; - - /** - * State indicating this item is currently paused. - * - * @see Builder#setState - */ - public final static int STATE_PAUSED = 2; - - /** - * State indicating this item is currently playing. - * - * @see Builder#setState - */ - public final static int STATE_PLAYING = 3; - - /** - * State indicating this item is currently fast forwarding. - * - * @see Builder#setState - */ - public final static int STATE_FAST_FORWARDING = 4; - - /** - * State indicating this item is currently rewinding. - * - * @see Builder#setState - */ - public final static int STATE_REWINDING = 5; - - /** - * State indicating this item is currently buffering and will begin playing - * when enough data has buffered. - * - * @see Builder#setState - */ - public final static int STATE_BUFFERING = 6; - - /** - * State indicating this item is currently in an error state. The error - * message should also be set when entering this state. - * - * @see Builder#setState - */ - public final static int STATE_ERROR = 7; - - /** - * State indicating the class doing playback is currently connecting to a - * new destination. Depending on the implementation you may return to the previous - * state when the connection finishes or enter {@link #STATE_NONE}. - * If the connection failed {@link #STATE_ERROR} should be used. - * - * @see Builder#setState - */ - public final static int STATE_CONNECTING = 8; - - /** - * State indicating the player is currently skipping to the previous item. - * - * @see Builder#setState - */ - public final static int STATE_SKIPPING_TO_PREVIOUS = 9; - - /** - * State indicating the player is currently skipping to the next item. - * - * @see Builder#setState - */ - public final static int STATE_SKIPPING_TO_NEXT = 10; - - /** - * State indicating the player is currently skipping to a specific item in - * the queue. - * - * @see Builder#setState - */ - public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11; - - /** - * Use this value for the position to indicate the position is not known. - */ - public final static long PLAYBACK_POSITION_UNKNOWN = -1; - - private final int mState; - private final long mPosition; - private final long mBufferedPosition; - private final float mSpeed; - private final long mActions; - private List mCustomActions; - private final CharSequence mErrorMessage; - private final long mUpdateTime; - private final long mActiveItemId; - private final Bundle mExtras; - - private PlaybackState(int state, long position, long updateTime, float speed, - long bufferedPosition, long transportControls, - List customActions, long activeItemId, - CharSequence error, Bundle extras) { - mState = state; - mPosition = position; - mSpeed = speed; - mUpdateTime = updateTime; - mBufferedPosition = bufferedPosition; - mActions = transportControls; - mCustomActions = new ArrayList<>(customActions); - mActiveItemId = activeItemId; - mErrorMessage = error; - mExtras = extras; - } - - private PlaybackState(Parcel in) { - mState = in.readInt(); - mPosition = in.readLong(); - mSpeed = in.readFloat(); - mUpdateTime = in.readLong(); - mBufferedPosition = in.readLong(); - mActions = in.readLong(); - mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); - mActiveItemId = in.readLong(); - mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mExtras = in.readBundle(); - } - - @Override - public String toString() { - StringBuilder bob = new StringBuilder("PlaybackState {"); - bob.append("state=").append(mState); - bob.append(", position=").append(mPosition); - bob.append(", buffered position=").append(mBufferedPosition); - bob.append(", speed=").append(mSpeed); - bob.append(", updated=").append(mUpdateTime); - bob.append(", actions=").append(mActions); - bob.append(", custom actions=").append(mCustomActions); - bob.append(", active item id=").append(mActiveItemId); - bob.append(", error=").append(mErrorMessage); - bob.append("}"); - return bob.toString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mState); - dest.writeLong(mPosition); - dest.writeFloat(mSpeed); - dest.writeLong(mUpdateTime); - dest.writeLong(mBufferedPosition); - dest.writeLong(mActions); - dest.writeTypedList(mCustomActions); - dest.writeLong(mActiveItemId); - TextUtils.writeToParcel(mErrorMessage, dest, 0); - dest.writeBundle(mExtras); - } - - /** - * Get the current state of playback. One of the following: - *

      - *
    • {@link PlaybackState#STATE_NONE}
    • - *
    • {@link PlaybackState#STATE_STOPPED}
    • - *
    • {@link PlaybackState#STATE_PLAYING}
    • - *
    • {@link PlaybackState#STATE_PAUSED}
    • - *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • - *
    • {@link PlaybackState#STATE_REWINDING}
    • - *
    • {@link PlaybackState#STATE_BUFFERING}
    • - *
    • {@link PlaybackState#STATE_ERROR}
    • - *
    • {@link PlaybackState#STATE_CONNECTING}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • - *
    - */ - @State - public int getState() { - return mState; - } - - /** - * Get the current playback position in ms. - */ - public long getPosition() { - return mPosition; - } - - /** - * Get the current buffered position in ms. This is the farthest playback - * point that can be reached from the current position using only buffered - * content. - */ - public long getBufferedPosition() { - return mBufferedPosition; - } - - /** - * Get the current playback speed as a multiple of normal playback. This - * should be negative when rewinding. A value of 1 means normal playback and - * 0 means paused. - * - * @return The current speed of playback. - */ - public float getPlaybackSpeed() { - return mSpeed; - } - - /** - * Get the current actions available on this session. This should use a - * bitmask of the available actions. - *
      - *
    • {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}
    • - *
    • {@link PlaybackState#ACTION_REWIND}
    • - *
    • {@link PlaybackState#ACTION_PLAY}
    • - *
    • {@link PlaybackState#ACTION_PAUSE}
    • - *
    • {@link PlaybackState#ACTION_STOP}
    • - *
    • {@link PlaybackState#ACTION_FAST_FORWARD}
    • - *
    • {@link PlaybackState#ACTION_SKIP_TO_NEXT}
    • - *
    • {@link PlaybackState#ACTION_SEEK_TO}
    • - *
    • {@link PlaybackState#ACTION_SET_RATING}
    • - *
    • {@link PlaybackState#ACTION_PLAY_PAUSE}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}
    • - *
    • {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_URI}
    • - *
    • {@link PlaybackState#ACTION_PREPARE}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_URI}
    • - *
    - */ - @Actions - public long getActions() { - return mActions; - } - - /** - * Get the list of custom actions. - */ - public List getCustomActions() { - return mCustomActions; - } - - /** - * Get a user readable error message. This should be set when the state is - * {@link PlaybackState#STATE_ERROR}. - */ - public CharSequence getErrorMessage() { - return mErrorMessage; - } - - /** - * Get the elapsed real time at which position was last updated. If the - * position has never been set this will return 0; - * - * @return The last time the position was updated. - */ - public long getLastPositionUpdateTime() { - return mUpdateTime; - } - - /** - * Get the id of the currently active item in the queue. If there is no - * queue or a queue is not supported by the session this will be - * {@link MediaSession.QueueItem#UNKNOWN_ID}. - * - * @return The id of the currently active item in the queue or - * {@link MediaSession.QueueItem#UNKNOWN_ID}. - */ - public long getActiveQueueItemId() { - return mActiveItemId; - } - - /** - * Get any custom extras that were set on this playback state. - * - * @return The extras for this state or null. - */ - public @Nullable Bundle getExtras() { - return mExtras; - } - - /** - * Get the {@link PlaybackState} state for the given - * {@link RemoteControlClient} state. - * - * @param rccState The state used by {@link RemoteControlClient}. - * @return The equivalent state used by {@link PlaybackState}. - * @hide - */ - public static int getStateFromRccState(int rccState) { - switch (rccState) { - case RemoteControlClient.PLAYSTATE_BUFFERING: - return STATE_BUFFERING; - case RemoteControlClient.PLAYSTATE_ERROR: - return STATE_ERROR; - case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: - return STATE_FAST_FORWARDING; - //RemoteControlClient.PLAYSTATE_NONE is hidden - case 0: //RemoteControlClient.PLAYSTATE_NONE: - return STATE_NONE; - case RemoteControlClient.PLAYSTATE_PAUSED: - return STATE_PAUSED; - case RemoteControlClient.PLAYSTATE_PLAYING: - return STATE_PLAYING; - case RemoteControlClient.PLAYSTATE_REWINDING: - return STATE_REWINDING; - case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: - return STATE_SKIPPING_TO_PREVIOUS; - case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: - return STATE_SKIPPING_TO_NEXT; - case RemoteControlClient.PLAYSTATE_STOPPED: - return STATE_STOPPED; - default: - return -1; - } - } - - /** - * Get the {@link RemoteControlClient} state for the given - * {@link PlaybackState} state. - * - * @param state The state used by {@link PlaybackState}. - * @return The equivalent state used by {@link RemoteControlClient}. - * @hide - */ - public static int getRccStateFromState(int state) { - switch (state) { - case STATE_BUFFERING: - return RemoteControlClient.PLAYSTATE_BUFFERING; - case STATE_ERROR: - return RemoteControlClient.PLAYSTATE_ERROR; - case STATE_FAST_FORWARDING: - return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; - case STATE_NONE: - //RemoteControlClient.PLAYSTATE_NONE is hidden - return 0; //RemoteControlClient.PLAYSTATE_NONE; - case STATE_PAUSED: - return RemoteControlClient.PLAYSTATE_PAUSED; - case STATE_PLAYING: - return RemoteControlClient.PLAYSTATE_PLAYING; - case STATE_REWINDING: - return RemoteControlClient.PLAYSTATE_REWINDING; - case STATE_SKIPPING_TO_PREVIOUS: - return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS; - case STATE_SKIPPING_TO_NEXT: - return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS; - case STATE_STOPPED: - return RemoteControlClient.PLAYSTATE_STOPPED; - default: - return -1; - } - } - - /** - * @hide - */ - public static long getActionsFromRccControlFlags(int rccFlags) { - long actions = 0; - long flag = 1; - while (flag <= rccFlags) { - if ((flag & rccFlags) != 0) { - actions |= getActionForRccFlag((int) flag); - } - flag = flag << 1; - } - return actions; - } - - /** - * @hide - */ - public static int getRccControlFlagsFromActions(long actions) { - int rccFlags = 0; - long action = 1; - while (action <= actions && action < Integer.MAX_VALUE) { - if ((action & actions) != 0) { - rccFlags |= getRccFlagForAction(action); - } - action = action << 1; - } - return rccFlags; - } - - private static long getActionForRccFlag(int flag) { - switch (flag) { - case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS: - return ACTION_SKIP_TO_PREVIOUS; - case RemoteControlClient.FLAG_KEY_MEDIA_REWIND: - return ACTION_REWIND; - case RemoteControlClient.FLAG_KEY_MEDIA_PLAY: - return ACTION_PLAY; - case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE: - return ACTION_PLAY_PAUSE; - case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE: - return ACTION_PAUSE; - case RemoteControlClient.FLAG_KEY_MEDIA_STOP: - return ACTION_STOP; - case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD: - return ACTION_FAST_FORWARD; - case RemoteControlClient.FLAG_KEY_MEDIA_NEXT: - return ACTION_SKIP_TO_NEXT; - case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE: - return ACTION_SEEK_TO; - case RemoteControlClient.FLAG_KEY_MEDIA_RATING: - return ACTION_SET_RATING; - } - return 0; - } - - private static int getRccFlagForAction(long action) { - // We only care about the lower set of actions that can map to rcc - // flags. - int testAction = action < Integer.MAX_VALUE ? (int) action : 0; - switch (testAction) { - case (int) ACTION_SKIP_TO_PREVIOUS: - return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS; - case (int) ACTION_REWIND: - return RemoteControlClient.FLAG_KEY_MEDIA_REWIND; - case (int) ACTION_PLAY: - return RemoteControlClient.FLAG_KEY_MEDIA_PLAY; - case (int) ACTION_PLAY_PAUSE: - return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; - case (int) ACTION_PAUSE: - return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE; - case (int) ACTION_STOP: - return RemoteControlClient.FLAG_KEY_MEDIA_STOP; - case (int) ACTION_FAST_FORWARD: - return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD; - case (int) ACTION_SKIP_TO_NEXT: - return RemoteControlClient.FLAG_KEY_MEDIA_NEXT; - case (int) ACTION_SEEK_TO: - return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE; - case (int) ACTION_SET_RATING: - return RemoteControlClient.FLAG_KEY_MEDIA_RATING; - } - return 0; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public PlaybackState createFromParcel(Parcel in) { - return new PlaybackState(in); - } - - @Override - public PlaybackState[] newArray(int size) { - return new PlaybackState[size]; - } - }; - - /** - * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of - * the standard transport controls by exposing app specific actions to - * {@link MediaController MediaControllers}. - */ - public static final class CustomAction implements Parcelable { - private final String mAction; - private final CharSequence mName; - private final int mIcon; - private final Bundle mExtras; - - /** - * Use {@link PlaybackState.CustomAction.Builder#build()}. - */ - private CustomAction(String action, CharSequence name, int icon, Bundle extras) { - mAction = action; - mName = name; - mIcon = icon; - mExtras = extras; - } - - private CustomAction(Parcel in) { - mAction = in.readString(); - mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mIcon = in.readInt(); - mExtras = in.readBundle(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mAction); - TextUtils.writeToParcel(mName, dest, flags); - dest.writeInt(mIcon); - dest.writeBundle(mExtras); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - - @Override - public PlaybackState.CustomAction createFromParcel(Parcel p) { - return new PlaybackState.CustomAction(p); - } - - @Override - public PlaybackState.CustomAction[] newArray(int size) { - return new PlaybackState.CustomAction[size]; - } - }; - - /** - * Returns the action of the {@link CustomAction}. - * - * @return The action of the {@link CustomAction}. - */ - public String getAction() { - return mAction; - } - - /** - * Returns the display name of this action. e.g. "Favorite" - * - * @return The display name of this {@link CustomAction}. - */ - public CharSequence getName() { - return mName; - } - - /** - * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package. - * - * @return The resource id of the icon in the {@link MediaSession MediaSession's} package. - */ - public int getIcon() { - return mIcon; - } - - /** - * Returns extras which provide additional application-specific information about the - * action, or null if none. These arguments are meant to be consumed by a - * {@link MediaController} if it knows how to handle them. - * - * @return Optional arguments for the {@link CustomAction}. - */ - public Bundle getExtras() { - return mExtras; - } - - @Override - public String toString() { - return "Action:" + - "mName='" + mName + - ", mIcon=" + mIcon + - ", mExtras=" + mExtras; - } - - /** - * Builder for {@link CustomAction} objects. - */ - public static final class Builder { - private final String mAction; - private final CharSequence mName; - private final int mIcon; - private Bundle mExtras; - - /** - * Creates a {@link CustomAction} builder with the id, name, and icon set. - * - * @param action The action of the {@link CustomAction}. - * @param name The display name of the {@link CustomAction}. This name will be displayed - * along side the action if the UI supports it. - * @param icon The icon resource id of the {@link CustomAction}. This resource id - * must be in the same package as the {@link MediaSession}. It will be - * displayed with the custom action if the UI supports it. - */ - public Builder(String action, CharSequence name, @DrawableRes int icon) { - if (TextUtils.isEmpty(action)) { - throw new IllegalArgumentException( - "You must specify an action to build a CustomAction."); - } - if (TextUtils.isEmpty(name)) { - throw new IllegalArgumentException( - "You must specify a name to build a CustomAction."); - } - if (icon == 0) { - throw new IllegalArgumentException( - "You must specify an icon resource id to build a CustomAction."); - } - mAction = action; - mName = name; - mIcon = icon; - } - - /** - * Set optional extras for the {@link CustomAction}. These extras are meant to be - * consumed by a {@link MediaController} if it knows how to handle them. - * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions. - * - * @param extras Optional extras for the {@link CustomAction}. - * @return this. - */ - public Builder setExtras(Bundle extras) { - mExtras = extras; - return this; - } - - /** - * Build and return the {@link CustomAction} instance with the specified values. - * - * @return A new {@link CustomAction} instance. - */ - public CustomAction build() { - return new CustomAction(mAction, mName, mIcon, mExtras); - } - } - } - - /** - * Builder for {@link PlaybackState} objects. - */ - public static final class Builder { - private final List mCustomActions = new ArrayList<>(); - - private int mState; - private long mPosition; - private long mBufferedPosition; - private float mSpeed; - private long mActions; - private CharSequence mErrorMessage; - private long mUpdateTime; - private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID; - private Bundle mExtras; - - /** - * Creates an initially empty state builder. - */ - public Builder() { - } - - /** - * Creates a builder with the same initial values as those in the from - * state. - * - * @param from The state to use for initializing the builder. - */ - public Builder(PlaybackState from) { - if (from == null) { - return; - } - mState = from.mState; - mPosition = from.mPosition; - mBufferedPosition = from.mBufferedPosition; - mSpeed = from.mSpeed; - mActions = from.mActions; - if (from.mCustomActions != null) { - mCustomActions.addAll(from.mCustomActions); - } - mErrorMessage = from.mErrorMessage; - mUpdateTime = from.mUpdateTime; - mActiveItemId = from.mActiveItemId; - mExtras = from.mExtras; - } - - /** - * Set the current state of playback. - *

    - * The position must be in ms and indicates the current playback - * position within the item. If the position is unknown use - * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown - * position the time at which the position was updated must be provided. - * It is okay to use {@link SystemClock#elapsedRealtime()} if the - * current position was just retrieved. - *

    - * The speed is a multiple of normal playback and should be 0 when - * paused and negative when rewinding. Normal playback speed is 1.0. - *

    - * The state must be one of the following: - *

      - *
    • {@link PlaybackState#STATE_NONE}
    • - *
    • {@link PlaybackState#STATE_STOPPED}
    • - *
    • {@link PlaybackState#STATE_PLAYING}
    • - *
    • {@link PlaybackState#STATE_PAUSED}
    • - *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • - *
    • {@link PlaybackState#STATE_REWINDING}
    • - *
    • {@link PlaybackState#STATE_BUFFERING}
    • - *
    • {@link PlaybackState#STATE_ERROR}
    • - *
    • {@link PlaybackState#STATE_CONNECTING}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • - *
    - * - * @param state The current state of playback. - * @param position The position in the current item in ms. - * @param playbackSpeed The current speed of playback as a multiple of - * normal playback. - * @param updateTime The time in the {@link SystemClock#elapsedRealtime} - * timebase that the position was updated at. - * @return this - */ - public Builder setState(@State int state, long position, float playbackSpeed, - long updateTime) { - mState = state; - mPosition = position; - mUpdateTime = updateTime; - mSpeed = playbackSpeed; - return this; - } - - /** - * Set the current state of playback. - *

    - * The position must be in ms and indicates the current playback - * position within the item. If the position is unknown use - * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to - * the current {@link SystemClock#elapsedRealtime()}. - *

    - * The speed is a multiple of normal playback and should be 0 when - * paused and negative when rewinding. Normal playback speed is 1.0. - *

    - * The state must be one of the following: - *

      - *
    • {@link PlaybackState#STATE_NONE}
    • - *
    • {@link PlaybackState#STATE_STOPPED}
    • - *
    • {@link PlaybackState#STATE_PLAYING}
    • - *
    • {@link PlaybackState#STATE_PAUSED}
    • - *
    • {@link PlaybackState#STATE_FAST_FORWARDING}
    • - *
    • {@link PlaybackState#STATE_REWINDING}
    • - *
    • {@link PlaybackState#STATE_BUFFERING}
    • - *
    • {@link PlaybackState#STATE_ERROR}
    • - *
    • {@link PlaybackState#STATE_CONNECTING}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_NEXT}
    • - *
    • {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}
    • - *
    - * - * @param state The current state of playback. - * @param position The position in the current item in ms. - * @param playbackSpeed The current speed of playback as a multiple of - * normal playback. - * @return this - */ - public Builder setState(@State int state, long position, float playbackSpeed) { - return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); - } - - /** - * Set the current actions available on this session. This should use a - * bitmask of possible actions. - *
      - *
    • {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}
    • - *
    • {@link PlaybackState#ACTION_REWIND}
    • - *
    • {@link PlaybackState#ACTION_PLAY}
    • - *
    • {@link PlaybackState#ACTION_PAUSE}
    • - *
    • {@link PlaybackState#ACTION_STOP}
    • - *
    • {@link PlaybackState#ACTION_FAST_FORWARD}
    • - *
    • {@link PlaybackState#ACTION_SKIP_TO_NEXT}
    • - *
    • {@link PlaybackState#ACTION_SEEK_TO}
    • - *
    • {@link PlaybackState#ACTION_SET_RATING}
    • - *
    • {@link PlaybackState#ACTION_PLAY_PAUSE}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}
    • - *
    • {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}
    • - *
    • {@link PlaybackState#ACTION_PLAY_FROM_URI}
    • - *
    • {@link PlaybackState#ACTION_PREPARE}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}
    • - *
    • {@link PlaybackState#ACTION_PREPARE_FROM_URI}
    • - *
    - * - * @param actions The set of actions allowed. - * @return this - */ - public Builder setActions(@Actions long actions) { - mActions = actions; - return this; - } - - /** - * Add a custom action to the playback state. Actions can be used to - * expose additional functionality to {@link MediaController - * MediaControllers} beyond what is offered by the standard transport - * controls. - *

    - * e.g. start a radio station based on the current item or skip ahead by - * 30 seconds. - * - * @param action An identifier for this action. It can be sent back to - * the {@link MediaSession} through - * {@link MediaController.TransportControls#sendCustomAction(String, Bundle)}. - * @param name The display name for the action. If text is shown with - * the action or used for accessibility, this is what should - * be used. - * @param icon The resource action of the icon that should be displayed - * for the action. The resource should be in the package of - * the {@link MediaSession}. - * @return this - */ - public Builder addCustomAction(String action, String name, int icon) { - return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null)); - } - - /** - * Add a custom action to the playback state. Actions can be used to expose additional - * functionality to {@link MediaController MediaControllers} beyond what is offered by the - * standard transport controls. - *

    - * An example of an action would be to start a radio station based on the current item - * or to skip ahead by 30 seconds. - * - * @param customAction The custom action to add to the {@link PlaybackState}. - * @return this - */ - public Builder addCustomAction(PlaybackState.CustomAction customAction) { - if (customAction == null) { - throw new IllegalArgumentException( - "You may not add a null CustomAction to PlaybackState."); - } - mCustomActions.add(customAction); - return this; - } - - /** - * Set the current buffered position in ms. This is the farthest - * playback point that can be reached from the current position using - * only buffered content. - * - * @param bufferedPosition The position in ms that playback is buffered - * to. - * @return this - */ - public Builder setBufferedPosition(long bufferedPosition) { - mBufferedPosition = bufferedPosition; - return this; - } - - /** - * Set the active item in the play queue by specifying its id. The - * default value is {@link MediaSession.QueueItem#UNKNOWN_ID} - * - * @param id The id of the active item. - * @return this - */ - public Builder setActiveQueueItemId(long id) { - mActiveItemId = id; - return this; - } - - /** - * Set a user readable error message. This should be set when the state - * is {@link PlaybackState#STATE_ERROR}. - * - * @param error The error message for display to the user. - * @return this - */ - public Builder setErrorMessage(CharSequence error) { - mErrorMessage = error; - return this; - } - - /** - * Set any custom extras to be included with the playback state. - * - * @param extras The extras to include. - * @return this - */ - public Builder setExtras(Bundle extras) { - mExtras = extras; - return this; - } - - /** - * Build and return the {@link PlaybackState} instance with these - * values. - * - * @return A new state instance. - */ - public PlaybackState build() { - return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition, - mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras); - } - } -} diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl deleted file mode 100644 index 84f41f6c3a..0000000000 --- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. - -package android.service.media; - -import android.content.res.Configuration; -import android.service.media.IMediaBrowserServiceCallbacks; -import android.net.Uri; -import android.os.Bundle; -import android.os.ResultReceiver; - -/** - * Media API allows clients to browse through hierarchy of a user’s media collection, - * playback a specific media entry and interact with the now playing queue. - * @hide - */ -oneway interface IMediaBrowserService { - void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks); - void disconnect(IMediaBrowserServiceCallbacks callbacks); - - void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); - void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks); - - void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks); - void addSubscription(String uri, in IBinder token, in Bundle options, - IMediaBrowserServiceCallbacks callbacks); - void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks); -} diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl deleted file mode 100644 index 8dc480d6bf..0000000000 --- a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. - -package android.service.media; - -import android.graphics.Bitmap; -import android.media.MediaParceledListSlice; -import android.media.session.MediaSession; -import android.os.Bundle; - -/** - * Media API allows clients to browse through hierarchy of a user’s media collection, - * playback a specific media entry and interact with the now playing queue. - * @hide - */ -oneway interface IMediaBrowserServiceCallbacks { - /** - * Invoked when the connected has been established. - * @param root The root media id for browsing. - * @param session The {@link MediaSession.Token media session token} that can be used to control - * the playback of the media app. - * @param extra Extras returned by the media service. - */ - void onConnect(String root, in MediaSession.Token session, in Bundle extras); - void onConnectFailed(); - void onLoadChildren(String mediaId, in MediaParceledListSlice list); - void onLoadChildrenWithOptions(String mediaId, in MediaParceledListSlice list, - in Bundle options); -} diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java deleted file mode 100644 index 76c99b9945..0000000000 --- a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright (C) 2014 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.service.media; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; -import android.app.Service; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.media.MediaParceledListSlice; -import android.media.browse.MediaBrowser; -import android.media.browse.MediaBrowserUtils; -import android.media.session.MediaSession; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.media.session.MediaSessionManager; -import android.media.session.MediaSessionManager.RemoteUserInfo; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.service.media.IMediaBrowserService; -import android.service.media.IMediaBrowserServiceCallbacks; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.Pair; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -/** - * Base class for media browser services. - *

    - * Media browser services enable applications to browse media content provided by an application - * and ask the application to start playing it. They may also be used to control content that - * is already playing by way of a {@link MediaSession}. - *

    - * - * To extend this class, you must declare the service in your manifest file with - * an intent filter with the {@link #SERVICE_INTERFACE} action. - * - * For example: - *

    - * <service android:name=".MyMediaBrowserService"
    - *          android:label="@string/service_name" >
    - *     <intent-filter>
    - *         <action android:name="android.media.browse.MediaBrowserService" />
    - *     </intent-filter>
    - * </service>
    - * 
    - * - */ -public abstract class MediaBrowserService extends Service { - private static final String TAG = "MediaBrowserService"; - private static final boolean DBG = false; - - /** - * The {@link Intent} that must be declared as handled by the service. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; - - /** - * A key for passing the MediaItem to the ResultReceiver in getItem. - * @hide - */ - @UnsupportedAppUsage - public static final String KEY_MEDIA_ITEM = "media_item"; - - private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0; - private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1; - - private static final int RESULT_ERROR = -1; - private static final int RESULT_OK = 0; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED, - RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED }) - private @interface ResultFlags { } - - private final ArrayMap mConnections = new ArrayMap<>(); - private ConnectionRecord mCurConnection; - private final Handler mHandler = new Handler(); - private ServiceBinder mBinder; - MediaSession.Token mSession; - - /** - * All the info about a connection. - */ - private class ConnectionRecord implements IBinder.DeathRecipient { - String pkg; - int uid; - int pid; - Bundle rootHints; - IMediaBrowserServiceCallbacks callbacks; - BrowserRoot root; - HashMap>> subscriptions = new HashMap<>(); - - @Override - public void binderDied() { - mHandler.post(new Runnable() { - @Override - public void run() { - mConnections.remove(callbacks.asBinder()); - } - }); - } - } - - /** - * Completion handler for asynchronous callback methods in {@link MediaBrowserService}. - *

    - * Each of the methods that takes one of these to send the result must call - * {@link #sendResult} to respond to the caller with the given results. If those - * functions return without calling {@link #sendResult}, they must instead call - * {@link #detach} before returning, and then may call {@link #sendResult} when - * they are done. If more than one of those methods is called, an exception will - * be thrown. - * - * @see #onLoadChildren - * @see #onLoadItem - */ - public class Result { - private Object mDebug; - private boolean mDetachCalled; - private boolean mSendResultCalled; - @UnsupportedAppUsage - private int mFlags; - - Result(Object debug) { - mDebug = debug; - } - - /** - * Send the result back to the caller. - */ - public void sendResult(T result) { - if (mSendResultCalled) { - throw new IllegalStateException("sendResult() called twice for: " + mDebug); - } - mSendResultCalled = true; - onResultSent(result, mFlags); - } - - /** - * Detach this message from the current thread and allow the {@link #sendResult} - * call to happen later. - */ - public void detach() { - if (mDetachCalled) { - throw new IllegalStateException("detach() called when detach() had already" - + " been called for: " + mDebug); - } - if (mSendResultCalled) { - throw new IllegalStateException("detach() called when sendResult() had already" - + " been called for: " + mDebug); - } - mDetachCalled = true; - } - - boolean isDone() { - return mDetachCalled || mSendResultCalled; - } - - void setFlags(@ResultFlags int flags) { - mFlags = flags; - } - - /** - * Called when the result is sent, after assertions about not being called twice - * have happened. - */ - void onResultSent(T result, @ResultFlags int flags) { - } - } - - private class ServiceBinder extends IMediaBrowserService.Stub { - @Override - public void connect(final String pkg, final Bundle rootHints, - final IMediaBrowserServiceCallbacks callbacks) { - - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - if (!isValidPackage(pkg, uid)) { - throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid - + " package=" + pkg); - } - - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - // Clear out the old subscriptions. We are getting new ones. - mConnections.remove(b); - - final ConnectionRecord connection = new ConnectionRecord(); - connection.pkg = pkg; - connection.pid = pid; - connection.uid = uid; - connection.rootHints = rootHints; - connection.callbacks = callbacks; - - mCurConnection = connection; - connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints); - mCurConnection = null; - - // If they didn't return something, don't allow this client. - if (connection.root == null) { - Log.i(TAG, "No root for client " + pkg + " from service " - + getClass().getName()); - try { - callbacks.onConnectFailed(); - } catch (RemoteException ex) { - Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " - + "pkg=" + pkg); - } - } else { - try { - mConnections.put(b, connection); - b.linkToDeath(connection, 0); - if (mSession != null) { - callbacks.onConnect(connection.root.getRootId(), - mSession, connection.root.getExtras()); - } - } catch (RemoteException ex) { - Log.w(TAG, "Calling onConnect() failed. Dropping client. " - + "pkg=" + pkg); - mConnections.remove(b); - } - } - } - }); - } - - @Override - public void disconnect(final IMediaBrowserServiceCallbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - // Clear out the old subscriptions. We are getting new ones. - final ConnectionRecord old = mConnections.remove(b); - if (old != null) { - // TODO - old.callbacks.asBinder().unlinkToDeath(old, 0); - } - } - }); - } - - @Override - public void addSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { - // do-nothing - } - - @Override - public void addSubscription(final String id, final IBinder token, final Bundle options, - final IMediaBrowserServiceCallbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - // Get the record for the connection - final ConnectionRecord connection = mConnections.get(b); - if (connection == null) { - Log.w(TAG, "addSubscription for callback that isn't registered id=" - + id); - return; - } - - MediaBrowserService.this.addSubscription(id, connection, token, options); - } - }); - } - - @Override - public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { - // do-nothing - } - - @Override - public void removeSubscription(final String id, final IBinder token, - final IMediaBrowserServiceCallbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - - ConnectionRecord connection = mConnections.get(b); - if (connection == null) { - Log.w(TAG, "removeSubscription for callback that isn't registered id=" - + id); - return; - } - if (!MediaBrowserService.this.removeSubscription(id, connection, token)) { - Log.w(TAG, "removeSubscription called for " + id - + " which is not subscribed"); - } - } - }); - } - - @Override - public void getMediaItem(final String mediaId, final ResultReceiver receiver, - final IMediaBrowserServiceCallbacks callbacks) { - mHandler.post(new Runnable() { - @Override - public void run() { - final IBinder b = callbacks.asBinder(); - ConnectionRecord connection = mConnections.get(b); - if (connection == null) { - Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); - return; - } - performLoadItem(mediaId, connection, receiver); - } - }); - } - } - - @Override - public void onCreate() { - super.onCreate(); - mBinder = new ServiceBinder(); - } - - @Override - public IBinder onBind(Intent intent) { - if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mBinder; - } - return null; - } - - @Override - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - } - - /** - * Called to get the root information for browsing by a particular client. - *

    - * The implementation should verify that the client package has permission - * to access browse media information before returning the root id; it - * should return null if the client is not allowed to access this - * information. - *

    - * - * @param clientPackageName The package name of the application which is - * requesting access to browse media. - * @param clientUid The uid of the application which is requesting access to - * browse media. - * @param rootHints An optional bundle of service-specific arguments to send - * to the media browser service when connecting and retrieving the - * root id for browsing, or null if none. The contents of this - * bundle may affect the information returned when browsing. - * @return The {@link BrowserRoot} for accessing this app's content or null. - * @see BrowserRoot#EXTRA_RECENT - * @see BrowserRoot#EXTRA_OFFLINE - * @see BrowserRoot#EXTRA_SUGGESTED - */ - public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, - int clientUid, @Nullable Bundle rootHints); - - /** - * Called to get information about the children of a media item. - *

    - * Implementations must call {@link Result#sendResult result.sendResult} - * with the list of children. If loading the children will be an expensive - * operation that should be performed on another thread, - * {@link Result#detach result.detach} may be called before returning from - * this function, and then {@link Result#sendResult result.sendResult} - * called when the loading is complete. - *

    - * In case the media item does not have any children, call {@link Result#sendResult} - * with an empty list. When the given {@code parentId} is invalid, implementations must - * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke - * {@link MediaBrowser.SubscriptionCallback#onError}. - *

    - * - * @param parentId The id of the parent media item whose children are to be - * queried. - * @param result The Result to send the list of children to. - */ - public abstract void onLoadChildren(@NonNull String parentId, - @NonNull Result> result); - - /** - * Called to get information about the children of a media item. - *

    - * Implementations must call {@link Result#sendResult result.sendResult} - * with the list of children. If loading the children will be an expensive - * operation that should be performed on another thread, - * {@link Result#detach result.detach} may be called before returning from - * this function, and then {@link Result#sendResult result.sendResult} - * called when the loading is complete. - *

    - * In case the media item does not have any children, call {@link Result#sendResult} - * with an empty list. When the given {@code parentId} is invalid, implementations must - * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke - * {@link MediaBrowser.SubscriptionCallback#onError}. - *

    - * - * @param parentId The id of the parent media item whose children are to be - * queried. - * @param result The Result to send the list of children to. - * @param options The bundle of service-specific arguments sent from the media - * browser. The information returned through the result should be - * affected by the contents of this bundle. - */ - public void onLoadChildren(@NonNull String parentId, - @NonNull Result> result, @NonNull Bundle options) { - // To support backward compatibility, when the implementation of MediaBrowserService doesn't - // override onLoadChildren() with options, onLoadChildren() without options will be used - // instead, and the options will be applied in the implementation of result.onResultSent(). - result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED); - onLoadChildren(parentId, result); - } - - /** - * Called to get information about a specific media item. - *

    - * Implementations must call {@link Result#sendResult result.sendResult}. If - * loading the item will be an expensive operation {@link Result#detach - * result.detach} may be called before returning from this function, and - * then {@link Result#sendResult result.sendResult} called when the item has - * been loaded. - *

    - * When the given {@code itemId} is invalid, implementations must call - * {@link Result#sendResult result.sendResult} with {@code null}. - *

    - * The default implementation will invoke {@link MediaBrowser.ItemCallback#onError}. - *

    - * - * @param itemId The id for the specific - * {@link android.media.browse.MediaBrowser.MediaItem}. - * @param result The Result to send the item to. - */ - public void onLoadItem(String itemId, Result result) { - result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED); - result.sendResult(null); - } - - /** - * Call to set the media session. - *

    - * This should be called as soon as possible during the service's startup. - * It may only be called once. - * - * @param token The token for the service's {@link MediaSession}. - */ - public void setSessionToken(final MediaSession.Token token) { - if (token == null) { - throw new IllegalArgumentException("Session token may not be null."); - } - if (mSession != null) { - throw new IllegalStateException("The session token has already been set."); - } - mSession = token; - mHandler.post(new Runnable() { - @Override - public void run() { - Iterator iter = mConnections.values().iterator(); - while (iter.hasNext()){ - ConnectionRecord connection = iter.next(); - try { - connection.callbacks.onConnect(connection.root.getRootId(), token, - connection.root.getExtras()); - } catch (RemoteException e) { - Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); - iter.remove(); - } - } - } - }); - } - - /** - * Gets the session token, or null if it has not yet been created - * or if it has been destroyed. - */ - public @Nullable MediaSession.Token getSessionToken() { - return mSession; - } - - /** - * Gets the root hints sent from the currently connected {@link MediaBrowser}. - * The root hints are service-specific arguments included in an optional bundle sent to the - * media browser service when connecting and retrieving the root id for browsing, or null if - * none. The contents of this bundle may affect the information returned when browsing. - * - * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or - * {@link #onLoadChildren} or {@link #onLoadItem}. - * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT - * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE - * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED - */ - public final Bundle getBrowserRootHints() { - if (mCurConnection == null) { - throw new IllegalStateException("This should be called inside of onGetRoot or" - + " onLoadChildren or onLoadItem methods"); - } - return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); - } - - /** - * Gets the browser information who sent the current request. - * - * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or - * {@link #onLoadChildren} or {@link #onLoadItem}. - * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) - */ - public final RemoteUserInfo getCurrentBrowserInfo() { - if (mCurConnection == null) { - throw new IllegalStateException("This should be called inside of onGetRoot or" - + " onLoadChildren or onLoadItem methods"); - } - return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid); - } - - /** - * Notifies all connected media browsers that the children of - * the specified parent id have changed in some way. - * This will cause browsers to fetch subscribed content again. - * - * @param parentId The id of the parent media item whose - * children changed. - */ - public void notifyChildrenChanged(@NonNull String parentId) { - notifyChildrenChangedInternal(parentId, null); - } - - /** - * Notifies all connected media browsers that the children of - * the specified parent id have changed in some way. - * This will cause browsers to fetch subscribed content again. - * - * @param parentId The id of the parent media item whose - * children changed. - * @param options The bundle of service-specific arguments to send - * to the media browser. The contents of this bundle may - * contain the information about the change. - */ - public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) { - if (options == null) { - throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged"); - } - notifyChildrenChangedInternal(parentId, options); - } - - private void notifyChildrenChangedInternal(final String parentId, final Bundle options) { - if (parentId == null) { - throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); - } - mHandler.post(new Runnable() { - @Override - public void run() { - for (IBinder binder : mConnections.keySet()) { - ConnectionRecord connection = mConnections.get(binder); - List> callbackList = - connection.subscriptions.get(parentId); - if (callbackList != null) { - for (Pair callback : callbackList) { - if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) { - performLoadChildren(parentId, connection, callback.second); - } - } - } - } - } - }); - } - - /** - * Return whether the given package is one of the ones that is owned by the uid. - */ - private boolean isValidPackage(String pkg, int uid) { - if (pkg == null) { - return false; - } - final PackageManager pm = getPackageManager(); - final String[] packages = pm.getPackagesForUid(uid); - final int N = packages.length; - for (int i=0; i> callbackList = connection.subscriptions.get(id); - if (callbackList == null) { - callbackList = new ArrayList<>(); - } - for (Pair callback : callbackList) { - if (token == callback.first - && MediaBrowserUtils.areSameOptions(options, callback.second)) { - return; - } - } - callbackList.add(new Pair<>(token, options)); - connection.subscriptions.put(id, callbackList); - // send the results - performLoadChildren(id, connection, options); - } - - /** - * Remove the subscription. - */ - private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) { - if (token == null) { - return connection.subscriptions.remove(id) != null; - } - boolean removed = false; - List> callbackList = connection.subscriptions.get(id); - if (callbackList != null) { - Iterator> iter = callbackList.iterator(); - while (iter.hasNext()){ - if (token == iter.next().first) { - removed = true; - iter.remove(); - } - } - if (callbackList.size() == 0) { - connection.subscriptions.remove(id); - } - } - return removed; - } - - /** - * Call onLoadChildren and then send the results back to the connection. - *

    - * Callers must make sure that this connection is still connected. - */ - private void performLoadChildren(final String parentId, final ConnectionRecord connection, - final Bundle options) { - final Result> result - = new Result>(parentId) { - @Override - void onResultSent(List list, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { - if (DBG) { - Log.d(TAG, "Not sending onLoadChildren result for connection that has" - + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); - } - return; - } - - List filteredList = - (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 - ? applyOptions(list, options) : list; - final MediaParceledListSlice pls = - filteredList == null ? null : new MediaParceledListSlice<>(filteredList); - try { - connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options); - } catch (RemoteException ex) { - // The other side is in the process of crashing. - Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId - + " package=" + connection.pkg); - } - } - }; - - mCurConnection = connection; - if (options == null) { - onLoadChildren(parentId, result); - } else { - onLoadChildren(parentId, result, options); - } - mCurConnection = null; - - if (!result.isDone()) { - throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" - + " before returning for package=" + connection.pkg + " id=" + parentId); - } - } - - private List applyOptions(List list, - final Bundle options) { - if (list == null) { - return null; - } - int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1); - int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); - if (page == -1 && pageSize == -1) { - return list; - } - int fromIndex = pageSize * page; - int toIndex = fromIndex + pageSize; - if (page < 0 || pageSize < 1 || fromIndex >= list.size()) { - return Collections.EMPTY_LIST; - } - if (toIndex > list.size()) { - toIndex = list.size(); - } - return list.subList(fromIndex, toIndex); - } - - private void performLoadItem(String itemId, final ConnectionRecord connection, - final ResultReceiver receiver) { - final Result result = - new Result(itemId) { - @Override - void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { - if (mConnections.get(connection.callbacks.asBinder()) != connection) { - if (DBG) { - Log.d(TAG, "Not sending onLoadItem result for connection that has" - + " been disconnected. pkg=" + connection.pkg + " id=" + itemId); - } - return; - } - if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) { - receiver.send(RESULT_ERROR, null); - return; - } - Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_MEDIA_ITEM, item); - receiver.send(RESULT_OK, bundle); - } - }; - - mCurConnection = connection; - onLoadItem(itemId, result); - mCurConnection = null; - - if (!result.isDone()) { - throw new IllegalStateException("onLoadItem must call detach() or sendResult()" - + " before returning for id=" + itemId); - } - } - - /** - * Contains information that the browser service needs to send to the client - * when first connected. - */ - public static final class BrowserRoot { - /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for recently played media items. - * - *

    When creating a media browser for a given media browser service, this key can be - * supplied as a root hint for retrieving media items that are recently played. - * If the media browser service can provide such media items, the implementation must return - * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. - * - *

    The root hint may contain multiple keys. - * - * @see #EXTRA_OFFLINE - * @see #EXTRA_SUGGESTED - */ - public static final String EXTRA_RECENT = "android.service.media.extra.RECENT"; - - /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for offline media items. - * - *

    When creating a media browser for a given media browser service, this key can be - * supplied as a root hint for retrieving media items that are can be played without an - * internet connection. - * If the media browser service can provide such media items, the implementation must return - * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. - * - *

    The root hint may contain multiple keys. - * - * @see #EXTRA_RECENT - * @see #EXTRA_SUGGESTED - */ - public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; - - /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for suggested media items. - * - *

    When creating a media browser for a given media browser service, this key can be - * supplied as a root hint for retrieving the media items suggested by the media browser - * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)} - * is considered ordered by relevance, first being the top suggestion. - * If the media browser service can provide such media items, the implementation must return - * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. - * - *

    The root hint may contain multiple keys. - * - * @see #EXTRA_RECENT - * @see #EXTRA_OFFLINE - */ - public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; - - final private String mRootId; - final private Bundle mExtras; - - /** - * Constructs a browser root. - * @param rootId The root id for browsing. - * @param extras Any extras about the browser service. - */ - public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { - if (rootId == null) { - throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + - "Use null for BrowserRoot instead."); - } - mRootId = rootId; - mExtras = extras; - } - - /** - * Gets the root id for browsing. - */ - public String getRootId() { - return mRootId; - } - - /** - * Gets any extras about the browser service. - */ - public Bundle getExtras() { - return mExtras; - } - } -} diff --git a/packages/OWNERS b/packages/OWNERS deleted file mode 100644 index 3b9fd2beea..0000000000 --- a/packages/OWNERS +++ /dev/null @@ -1,9 +0,0 @@ -akersten@google.com -dwkang@google.com -hdmoon@google.com -insun@google.com -jaewan@google.com -jinpark@google.com -marcone@google.com -sungsoo@google.com -wjia@google.com -- GitLab From 9a95b0f26e5d30947e8f17888c093ceac6593087 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Wed, 30 Jan 2019 17:39:20 -0800 Subject: [PATCH 0854/1530] Resolve feedback from API review Fix inconsistent naming of offline license states bug:120489407 bug:120488811 test:cts and gts media tests Change-Id: I8473211d96383977ad33e4bd770fc4c71d9bd15f --- drm/libmediadrm/DrmHal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 84713c1509..ba46ffde42 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -168,7 +168,7 @@ static DrmPlugin::OfflineLicenseState toOfflineLicenseState( case OfflineLicenseState::USABLE: return DrmPlugin::kOfflineLicenseStateUsable; case OfflineLicenseState::INACTIVE: - return DrmPlugin::kOfflineLicenseStateInactive; + return DrmPlugin::kOfflineLicenseStateReleased; default: return DrmPlugin::kOfflineLicenseStateUnknown; } -- GitLab From 9e6b038eadbfcbf30daea66c6fb2ab688ef99535 Mon Sep 17 00:00:00 2001 From: Reavathi Uddaraju Date: Mon, 27 Feb 2017 17:33:37 +0800 Subject: [PATCH 0855/1530] stagefright: stop audio encoding before releasing writer thread Writer thread in encoder usecase gets stuck in destructor if recorder is stopped right after being paused. This is because encoder source is paused when recoder pauses, then omx encoder data flow stops. This in turn causes MediaCodecSource::read() to be blocked infinitely. The proper sequence to exit the main thread of encoding writer is to stop the source firstly before waiting for thread termination. Bug:2012015 Test: Video Recording. Change-Id: Ic70bdc2ad06d3e03f6a2966c7c7cc2557a214349 CRs-Fixed: 2012015 --- media/libstagefright/AACWriter.cpp | 2 +- media/libstagefright/AMRWriter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp index 2ea5fcdd19..18d3af79c0 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -154,11 +154,11 @@ status_t AACWriter::reset() { mDone = true; void *dummy; + status_t status = mSource->stop(); pthread_join(mThread, &dummy); status_t err = static_cast(reinterpret_cast(dummy)); { - status_t status = mSource->stop(); if (err == OK && (status != OK && status != ERROR_END_OF_STREAM)) { err = status; diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index 41106a1186..24f6f0bed9 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -149,11 +149,11 @@ status_t AMRWriter::reset() { mDone = true; void *dummy; + status_t status = mSource->stop(); pthread_join(mThread, &dummy); status_t err = static_cast(reinterpret_cast(dummy)); { - status_t status = mSource->stop(); if (err == OK && (status != OK && status != ERROR_END_OF_STREAM)) { err = status; -- GitLab From b5bfa0e2dfc53c22eb7b16d64395aca0f4065d76 Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Wed, 16 Jan 2019 15:49:42 -0800 Subject: [PATCH 0856/1530] CamcorderProfiles: Add new camcorder profiles add VGA. QHD, 4K-DCI profiles Bug:122944520 Test: run cts -m CtsMediaTestCases -t android.media.cts.CamcorderProfileTest --skip-preconditions run cts -m CtsMediaTestCases -t android.media.cts.CameraProfileTest --skip-preconditions Change-Id: Iacd8537f62112171ee611a7534f9d432f45d5d3f --- media/libmedia/MediaProfiles.cpp | 13 +++++++++++++ media/libmedia/include/media/MediaProfiles.h | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index 08c6a50006..98c54971ad 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -92,6 +92,19 @@ const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { {"highspeed720p", CAMCORDER_QUALITY_HIGH_SPEED_720P}, {"highspeed1080p", CAMCORDER_QUALITY_HIGH_SPEED_1080P}, {"highspeed2160p", CAMCORDER_QUALITY_HIGH_SPEED_2160P}, + + // Vendor-specific profiles + {"vga", CAMCORDER_QUALITY_VGA}, + {"4kdci", CAMCORDER_QUALITY_4KDCI}, + {"timelapsevga", CAMCORDER_QUALITY_TIME_LAPSE_VGA}, + {"timelapse4kdci", CAMCORDER_QUALITY_TIME_LAPSE_4KDCI}, + {"highspeedcif", CAMCORDER_QUALITY_HIGH_SPEED_CIF}, + {"highspeedvga", CAMCORDER_QUALITY_HIGH_SPEED_VGA}, + {"highspeed4kdci", CAMCORDER_QUALITY_HIGH_SPEED_4KDCI}, + {"qhd", CAMCORDER_QUALITY_QHD}, + {"2k", CAMCORDER_QUALITY_2k}, + {"timelapseqhd", CAMCORDER_QUALITY_TIME_LAPSE_QHD}, + {"timelapse2k", CAMCORDER_QUALITY_TIME_LAPSE_2k}, }; #if LOG_NDEBUG diff --git a/media/libmedia/include/media/MediaProfiles.h b/media/libmedia/include/media/MediaProfiles.h index 0feb4f3981..3e8e7c854e 100644 --- a/media/libmedia/include/media/MediaProfiles.h +++ b/media/libmedia/include/media/MediaProfiles.h @@ -34,7 +34,11 @@ enum camcorder_quality { CAMCORDER_QUALITY_1080P = 6, CAMCORDER_QUALITY_QVGA = 7, CAMCORDER_QUALITY_2160P = 8, - CAMCORDER_QUALITY_LIST_END = 8, + CAMCORDER_QUALITY_VGA = 9, + CAMCORDER_QUALITY_4KDCI = 10, + CAMCORDER_QUALITY_QHD = 11, + CAMCORDER_QUALITY_2k = 12, + CAMCORDER_QUALITY_LIST_END = 12, CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000, CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000, @@ -46,7 +50,11 @@ enum camcorder_quality { CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006, CAMCORDER_QUALITY_TIME_LAPSE_QVGA = 1007, CAMCORDER_QUALITY_TIME_LAPSE_2160P = 1008, - CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1008, + CAMCORDER_QUALITY_TIME_LAPSE_VGA = 1009, + CAMCORDER_QUALITY_TIME_LAPSE_4KDCI = 1010, + CAMCORDER_QUALITY_TIME_LAPSE_QHD = 1011, + CAMCORDER_QUALITY_TIME_LAPSE_2k = 1012, + CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1012, CAMCORDER_QUALITY_HIGH_SPEED_LIST_START = 2000, CAMCORDER_QUALITY_HIGH_SPEED_LOW = 2000, @@ -55,7 +63,10 @@ enum camcorder_quality { CAMCORDER_QUALITY_HIGH_SPEED_720P = 2003, CAMCORDER_QUALITY_HIGH_SPEED_1080P = 2004, CAMCORDER_QUALITY_HIGH_SPEED_2160P = 2005, - CAMCORDER_QUALITY_HIGH_SPEED_LIST_END = 2005, + CAMCORDER_QUALITY_HIGH_SPEED_CIF = 2006, + CAMCORDER_QUALITY_HIGH_SPEED_VGA = 2007, + CAMCORDER_QUALITY_HIGH_SPEED_4KDCI = 2008, + CAMCORDER_QUALITY_HIGH_SPEED_LIST_END = 2008, }; enum video_decoder { -- GitLab From 6a3052232c606bc2f74a849c6758fd44b6c01158 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 28 Jan 2019 20:33:18 -0800 Subject: [PATCH 0857/1530] use MediaAnalyticsItem::create() instead of new Bug: 123543489 Test: boot/logcat, select CtsMediaTests Change-Id: I92f8b9987b78466fae74ce4c0b177f8bb1e7dea8 --- drm/libmediadrm/DrmHal.cpp | 14 +++++++------- drm/libmediadrm/PluginMetricsReporting.cpp | 12 ++++++------ media/libaudioclient/include/media/AudioRecord.h | 2 +- media/libaudioclient/include/media/AudioTrack.h | 2 +- media/libmediametrics/include/MediaAnalyticsItem.h | 9 ++++++--- .../libmediaplayerservice/StagefrightRecorder.cpp | 2 +- .../nuplayer/NuPlayerDriver.cpp | 4 ++-- media/libnblog/ReportPerformance.cpp | 2 +- media/libstagefright/MediaCodec.cpp | 2 +- media/libstagefright/RemoteMediaExtractor.cpp | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 2 +- 11 files changed, 28 insertions(+), 25 deletions(-) diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 84713c1509..3affb2544a 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -1556,22 +1556,22 @@ void DrmHal::writeByteArray(Parcel &obj, hidl_vec const &vec) void DrmHal::reportFrameworkMetrics() const { - MediaAnalyticsItem item("mediadrm"); - item.generateSessionID(); - item.setPkgName(mMetrics.GetAppPackageName().c_str()); + std::unique_ptr item(MediaAnalyticsItem::create("mediadrm")); + item->generateSessionID(); + item->setPkgName(mMetrics.GetAppPackageName().c_str()); String8 vendor; String8 description; status_t result = getPropertyStringInternal(String8("vendor"), vendor); if (result != OK) { ALOGE("Failed to get vendor from drm plugin: %d", result); } else { - item.setCString("vendor", vendor.c_str()); + item->setCString("vendor", vendor.c_str()); } result = getPropertyStringInternal(String8("description"), description); if (result != OK) { ALOGE("Failed to get description from drm plugin: %d", result); } else { - item.setCString("description", description.c_str()); + item->setCString("description", description.c_str()); } std::string serializedMetrics; @@ -1582,9 +1582,9 @@ void DrmHal::reportFrameworkMetrics() const std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(), serializedMetrics.size()); if (!b64EncodedMetrics.empty()) { - item.setCString("serialized_metrics", b64EncodedMetrics.c_str()); + item->setCString("serialized_metrics", b64EncodedMetrics.c_str()); } - if (!item.selfrecord()) { + if (!item->selfrecord()) { ALOGE("Failed to self record framework metrics"); } } diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp index 5cb48bf678..8cd6f969cc 100644 --- a/drm/libmediadrm/PluginMetricsReporting.cpp +++ b/drm/libmediadrm/PluginMetricsReporting.cpp @@ -34,17 +34,17 @@ constexpr char kSerializedMetricsField[] = "serialized_metrics"; status_t reportVendorMetrics(const std::string& metrics, const String8& name, const String8& appPackageName) { - MediaAnalyticsItem analyticsItem(name.c_str()); - analyticsItem.generateSessionID(); + std::unique_ptr analyticsItem(MediaAnalyticsItem::create(name.c_str())); + analyticsItem->generateSessionID(); std::string app_package_name(appPackageName.c_str(), appPackageName.size()); - analyticsItem.setPkgName(app_package_name); + analyticsItem->setPkgName(app_package_name); if (metrics.size() > 0) { - analyticsItem.setCString(kSerializedMetricsField, metrics.c_str()); + analyticsItem->setCString(kSerializedMetricsField, metrics.c_str()); } - if (!analyticsItem.selfrecord()) { - ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID()); + if (!analyticsItem->selfrecord()) { + ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID()); } return OK; diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index ebee124e10..1f718448af 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -719,7 +719,7 @@ private: private: class MediaMetrics { public: - MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiorecord")), + MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiorecord")), mCreatedNs(systemTime(SYSTEM_TIME_REALTIME)), mStartedNs(0), mDurationNs(0), mCount(0), mLastError(NO_ERROR) { diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 7fdf7cc7bb..cbb750f90d 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -1227,7 +1227,7 @@ private: private: class MediaMetrics { public: - MediaMetrics() : mAnalyticsItem(new MediaAnalyticsItem("audiotrack")) { + MediaMetrics() : mAnalyticsItem(MediaAnalyticsItem::create("audiotrack")) { } ~MediaMetrics() { // mAnalyticsItem alloc failure will be flagged in the constructor diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h index 2f9e7c204f..4a36f6a0f1 100644 --- a/media/libmediametrics/include/MediaAnalyticsItem.h +++ b/media/libmediametrics/include/MediaAnalyticsItem.h @@ -82,16 +82,19 @@ class MediaAnalyticsItem { PROTO_LAST = PROTO_V1, }; + private: + // use the ::create() method instead + MediaAnalyticsItem(); + MediaAnalyticsItem(Key); + MediaAnalyticsItem(const MediaAnalyticsItem&); + MediaAnalyticsItem &operator=(const MediaAnalyticsItem&); public: - // so clients do not need to know size details static MediaAnalyticsItem* create(Key key); static MediaAnalyticsItem* create(); // access functions for the class - MediaAnalyticsItem(); - MediaAnalyticsItem(Key); ~MediaAnalyticsItem(); // SessionID ties multiple submissions for same key together diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f2a303812a..37b13f0be6 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -202,7 +202,7 @@ void StagefrightRecorder::flushAndResetMetrics(bool reinitialize) { } mAnalyticsDirty = false; if (reinitialize) { - mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder); + mAnalyticsItem = MediaAnalyticsItem::create(kKeyRecorder); } } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index a820445651..1b396c08bf 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -97,7 +97,7 @@ NuPlayerDriver::NuPlayerDriver(pid_t pid) mMediaClock->init(); // set up an analytics record - mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer); + mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer); mLooper->start( false, /* runOnCallingThread */ @@ -635,7 +635,7 @@ void NuPlayerDriver::logMetrics(const char *where) { // re-init in case we prepare() and start() again. delete mAnalyticsItem ; - mAnalyticsItem = new MediaAnalyticsItem("nuplayer"); + mAnalyticsItem = MediaAnalyticsItem::create("nuplayer"); if (mAnalyticsItem) { mAnalyticsItem->setUid(mClientUid); } diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp index f632e40d25..b050b8336e 100644 --- a/media/libnblog/ReportPerformance.cpp +++ b/media/libnblog/ReportPerformance.cpp @@ -168,7 +168,7 @@ bool sendToMediaMetrics(const PerformanceData& data) return false; } - std::unique_ptr item(new MediaAnalyticsItem("audiothread")); + std::unique_ptr item(MediaAnalyticsItem::create("audiothread")); const Histogram &workHist = data.workHist; if (workHist.totalCount() > 0) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index c7da7c7c0b..9c58e05ace 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -550,7 +550,7 @@ MediaCodec::~MediaCodec() { void MediaCodec::initAnalyticsItem() { if (mAnalyticsItem == NULL) { - mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName); + mAnalyticsItem = MediaAnalyticsItem::create(kCodecKeyName); } mLatencyHist.setup(kLatencyHistBuckets, kLatencyHistWidth, kLatencyHistFloor); diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp index 9d2c42b6cd..b0ce688e74 100644 --- a/media/libstagefright/RemoteMediaExtractor.cpp +++ b/media/libstagefright/RemoteMediaExtractor.cpp @@ -49,7 +49,7 @@ RemoteMediaExtractor::RemoteMediaExtractor( mAnalyticsItem = nullptr; if (MEDIA_LOG) { - mAnalyticsItem = new MediaAnalyticsItem(kKeyExtractor); + mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor); // track the container format (mpeg, aac, wvm, etc) size_t ntracks = extractor->countTracks(); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 49c541cf34..45fb174c81 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -525,7 +525,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) static constexpr char kAudioPolicyActiveDevice[] = "android.media.audiopolicy.active.device"; - MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy); + MediaAnalyticsItem *item = MediaAnalyticsItem::create(kAudioPolicy); if (item != NULL) { item->setInt32(kAudioPolicyStatus, status); -- GitLab From 1aa5592bafaeec5a3d04fc99c57a55384a0864e7 Mon Sep 17 00:00:00 2001 From: Shan An Date: Mon, 14 Jan 2019 10:21:40 +0800 Subject: [PATCH 0858/1530] Handle Http Live Streaming native exception Problem: Exception happens when user plays an http live streaming source whose playlist is empty. Take baidu baike link as an example, when user accesses https://baikebcs.bdimg.com/others/mda-anemptyfile20170518.m3u8 and taps "play", NE happens. Solution: Return malformed error when play an empty m3u8 file source Bug: 123009871 Test: Launch browser to access https://baikebcs.bdimg.com/others/mda-anemptyfile20170518.m3u8 and then tap "play" Change-Id: Ieaeb60f08fb24f2ca4ca554a70c5eb89b5cf6a16 --- media/libstagefright/httplive/M3UParser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 16179d3226..4392799867 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -706,6 +706,12 @@ status_t M3UParser::parse(const void *_data, size_t size) { ++lineNo; } + // playlist has no item, would cause exception + if (mItems.size() == 0) { + ALOGE("playlist has no item"); + return ERROR_MALFORMED; + } + // error checking of all fields that's required to appear once // (currently only checking "target-duration"), and // initialization of playlist properties (eg. mTargetDurationUs) -- GitLab From c0682e25540ce22c2428151086ed816c2df13db4 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 22 Jan 2019 15:52:02 -0800 Subject: [PATCH 0859/1530] aaudio test: test drift in clock model Simulate a sample rate clock that is slightly off from the expected rate. The clock model should adjust to the drift and stay locked. Test: this is a test Change-Id: I402d8a90c920ff9eca611ee56071441826fc9876 --- media/libaaudio/tests/test_clock_model.cpp | 55 +++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/media/libaaudio/tests/test_clock_model.cpp b/media/libaaudio/tests/test_clock_model.cpp index 3c09025a44..7f7abbd925 100644 --- a/media/libaaudio/tests/test_clock_model.cpp +++ b/media/libaaudio/tests/test_clock_model.cpp @@ -43,13 +43,47 @@ public: } void TearDown() { - } ~ClockModelTestFixture() { // cleanup any pending stuff, but no exceptions allowed } + // Test processing of timestamps when the hardware may be slightly off from + // the expected sample rate. + void checkDriftingClock(double hardwareFramesPerSecond, int numLoops) { + const int64_t startTimeNanos = 500000000; // arbitrary + model.start(startTimeNanos); + + const int64_t startPositionFrames = HW_FRAMES_PER_BURST; // hardware + // arbitrary time for first burst + const int64_t markerTime = startTimeNanos + NANOS_PER_MILLISECOND + + (200 * NANOS_PER_MICROSECOND); + + // Should set initial marker. + model.processTimestamp(startPositionFrames, markerTime); + ASSERT_EQ(startPositionFrames, model.convertTimeToPosition(markerTime)); + + double elapsedTimeSeconds = startTimeNanos / (double) NANOS_PER_SECOND; + for (int i = 0; i < numLoops; i++) { + // Calculate random delay over several bursts. + const double timeDelaySeconds = 10.0 * drand48() * NANOS_PER_BURST / NANOS_PER_SECOND; + elapsedTimeSeconds += timeDelaySeconds; + const int64_t elapsedTimeNanos = (int64_t)(elapsedTimeSeconds * NANOS_PER_SECOND); + const int64_t currentTimeNanos = startTimeNanos + elapsedTimeNanos; + // Simulate DSP running at the specified rate. + const int64_t currentTimeFrames = startPositionFrames + + (int64_t)(hardwareFramesPerSecond * elapsedTimeSeconds); + const int64_t numBursts = currentTimeFrames / HW_FRAMES_PER_BURST; + const int64_t alignedPosition = startPositionFrames + (numBursts * HW_FRAMES_PER_BURST); + + // Apply drifting timestamp. + model.processTimestamp(alignedPosition, currentTimeNanos); + + ASSERT_EQ(alignedPosition, model.convertTimeToPosition(currentTimeNanos)); + } + } + IsochronousClockModel model; }; @@ -95,7 +129,6 @@ TEST_F(ClockModelTestFixture, clock_start) { } // timestamps moves the window if outside the bounds -// TODO test nudging the window TEST_F(ClockModelTestFixture, clock_timestamp) { const int64_t startTime = 100000000; model.start(startTime); @@ -113,3 +146,21 @@ TEST_F(ClockModelTestFixture, clock_timestamp) { // convertPositionToTime rounds up EXPECT_EQ(markerTime + NANOS_PER_BURST, model.convertPositionToTime(position + 17)); } + +#define NUM_LOOPS_DRIFT 10000 + +// test nudging the window by using a drifting HW clock +TEST_F(ClockModelTestFixture, clock_no_drift) { + checkDriftingClock(SAMPLE_RATE, NUM_LOOPS_DRIFT); +} + +// These slow drift rates caused errors when I disabled the code that handles +// drifting in the clock model. So I think the test is valid. +// It is unlikely that real hardware would be off by more than this amount. +TEST_F(ClockModelTestFixture, clock_slow_drift) { + checkDriftingClock(0.998 * SAMPLE_RATE, NUM_LOOPS_DRIFT); +} + +TEST_F(ClockModelTestFixture, clock_fast_drift) { + checkDriftingClock(1.002 * SAMPLE_RATE, NUM_LOOPS_DRIFT); +} \ No newline at end of file -- GitLab From f6fff6b6ce376f1e44cd588c7b44c117e461183a Mon Sep 17 00:00:00 2001 From: Henry Fang Date: Thu, 31 Jan 2019 22:36:48 +0000 Subject: [PATCH 0860/1530] Revert "Revert "Update plugin to use cas@1.1 hal"" This reverts commit a4f9d509c0283c1ba0c65b7839948356e30e8f88. Reason for revert: Change-Id: I31fda92c2915dc04bc100cb97d36cf4bead234b7 --- .../plugins/clearkey/ClearKeyCasPlugin.cpp | 42 ++++++++++++++++++- .../plugins/clearkey/ClearKeyCasPlugin.h | 11 +++++ drm/mediacas/plugins/mock/MockCasPlugin.cpp | 23 ++++++++++ drm/mediacas/plugins/mock/MockCasPlugin.h | 11 ++++- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp index 1558e8b8da..27bd631afe 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp @@ -65,7 +65,20 @@ status_t ClearKeyCasFactory::createPlugin( *plugin = new ClearKeyCasPlugin(appData, callback); return OK; } -/////////////////////////////////////////////////////////////////////////////// + +status_t ClearKeyCasFactory::createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) { + if (!isSystemIdSupported(CA_system_id)) { + return BAD_VALUE; + } + + *plugin = new ClearKeyCasPlugin(appData, callback); + return OK; +} +//////////////////////////////////////////////////////////////////////////////// bool ClearKeyDescramblerFactory::isSystemIdSupported( int32_t CA_system_id) const { return CA_system_id == sClearKeySystemId; @@ -88,6 +101,12 @@ ClearKeyCasPlugin::ClearKeyCasPlugin( ALOGV("CTOR"); } +ClearKeyCasPlugin::ClearKeyCasPlugin( + void *appData, CasPluginCallbackExt callback) + : mCallbackExt(callback), mAppData(appData) { + ALOGV("CTOR"); +} + ClearKeyCasPlugin::~ClearKeyCasPlugin() { ALOGV("DTOR"); ClearKeySessionLibrary::get()->destroyPlugin(this); @@ -167,8 +186,27 @@ status_t ClearKeyCasPlugin::sendEvent( // Echo the received event to the callback. // Clear key plugin doesn't use any event, echo'ing for testing only. if (mCallback != NULL) { - mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), eventData.size()); + mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size()); + } else if (mCallbackExt != NULL) { + mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size(), NULL); + } + return OK; +} + +status_t ClearKeyCasPlugin::sendSessionEvent( + const CasSessionId &sessionId, int32_t event, + int arg, const CasData &eventData) { + ALOGV("sendSessionEvent: sessionId=%s, event=%d, arg=%d", + sessionIdToString(sessionId).string(), event, arg); + // Echo the received event to the callback. + // Clear key plugin doesn't use any event, echo'ing for testing only. + if (mCallbackExt != NULL) { + mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(), + eventData.size(), &sessionId); } + return OK; } diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h index 389e1728d7..f48d5b1d13 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h @@ -47,6 +47,11 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; + virtual status_t createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) override; }; class ClearKeyDescramblerFactory : public DescramblerFactory { @@ -63,6 +68,7 @@ public: class ClearKeyCasPlugin : public CasPlugin { public: ClearKeyCasPlugin(void *appData, CasPluginCallback callback); + ClearKeyCasPlugin(void *appData, CasPluginCallbackExt callback); virtual ~ClearKeyCasPlugin(); virtual status_t setPrivateData( @@ -85,6 +91,10 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; + virtual status_t sendSessionEvent( + const CasSessionId &sessionId, + int32_t event, int32_t arg, const CasData &eventData) override; + virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( @@ -94,6 +104,7 @@ private: Mutex mKeyFetcherLock; std::unique_ptr mKeyFetcher; CasPluginCallback mCallback; + CasPluginCallbackExt mCallbackExt; void* mAppData; }; diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp index 8404a83935..2964791413 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp +++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp @@ -60,6 +60,19 @@ status_t MockCasFactory::createPlugin( return OK; } +status_t MockCasFactory::createPlugin( + int32_t CA_system_id, + void* /*appData*/, + CasPluginCallbackExt /*callback*/, + CasPlugin **plugin) { + if (!isSystemIdSupported(CA_system_id)) { + return BAD_VALUE; + } + + *plugin = new MockCasPlugin(); + return OK; +} + /////////////////////////////////////////////////////////////////////////////// bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const { @@ -170,6 +183,16 @@ status_t MockCasPlugin::sendEvent( return OK; } +status_t MockCasPlugin::sendSessionEvent( + const CasSessionId &sessionId, int32_t event, + int /*arg*/, const CasData& /*eventData*/) { + ALOGV("sendSessionEvent: sessionId=%s, event=%d", + arrayToString(sessionId).string(), event); + Mutex::Autolock lock(mLock); + + return OK; +} + status_t MockCasPlugin::provision(const String8 &str) { ALOGV("provision: provisionString=%s", str.string()); Mutex::Autolock lock(mLock); diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h index 81069906ed..74b540cc82 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.h +++ b/drm/mediacas/plugins/mock/MockCasPlugin.h @@ -42,6 +42,11 @@ public: void *appData, CasPluginCallback callback, CasPlugin **plugin) override; + virtual status_t createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) override; }; class MockDescramblerFactory : public DescramblerFactory { @@ -80,7 +85,11 @@ public: virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) override; - virtual status_t provision(const String8 &str) override; + virtual status_t sendSessionEvent( + const CasSessionId &sessionId, + int32_t event, int32_t arg, const CasData &eventData) override; + + virtual status_t provision(const String8 &str) override; virtual status_t refreshEntitlements( int32_t refreshType, const CasData &refreshData) override; -- GitLab From bd63c6ffe67b3555262e85f23a4f26b4ff057404 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 18 Dec 2018 12:13:34 -0800 Subject: [PATCH 0861/1530] aaudio test_return_stop prints frames in buffer Error if more than capacity of the buffer. Bug: 121196899 Test: this is a test Change-Id: I805905ffd1718f3e4fec2212e674f449bd55c4a8 --- media/libaaudio/tests/test_return_stop.cpp | 53 +++++++++++++++------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/media/libaaudio/tests/test_return_stop.cpp b/media/libaaudio/tests/test_return_stop.cpp index 9a9e00cd18..1252dd3374 100644 --- a/media/libaaudio/tests/test_return_stop.cpp +++ b/media/libaaudio/tests/test_return_stop.cpp @@ -140,7 +140,7 @@ static void s_myErrorCallbackProc( printf("%s() - error = %d\n", __func__, error); } -void usage() { +static void s_usage() { printf("test_return_stop [-i] [-x] [-n] [-c]\n"); printf(" -i direction INPUT, otherwise OUTPUT\n"); printf(" -x sharing mode EXCLUSIVE, otherwise SHARED\n"); @@ -148,6 +148,28 @@ void usage() { printf(" -c always return CONTINUE from callback, not STOP\n"); } +/** + * @return 0 is OK, -1 for error + */ +static int s_checkEnginePositions(AudioEngine *engine) { + const int64_t framesRead = AAudioStream_getFramesRead(engine->stream); + const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream); + const int32_t delta = (int32_t)(framesWritten - framesRead); + printf("playing framesRead = %7d, framesWritten = %7d" + ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n", + (int32_t) framesRead, + (int32_t) framesWritten, + delta, + engine->framesCalled.load(), + engine->callbackCount.load() + ); + if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) { + printf("ERROR - delta > capacity\n"); + return -1; + } + return 0; +} + int main(int argc, char **argv) { (void) argc; (void) argv; @@ -188,12 +210,12 @@ int main(int argc, char **argv) { sharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; break; default: - usage(); + s_usage(); exit(EXIT_FAILURE); break; } } else { - usage(); + s_usage(); exit(EXIT_FAILURE); break; } @@ -201,12 +223,20 @@ int main(int argc, char **argv) { result = s_OpenAudioStream(&engine, direction, sharingMode, perfMode); if (result != AAUDIO_OK) { - printf("s_OpenAudioStream returned %s", + printf("s_OpenAudioStream returned %s\n", AAudio_convertResultToText(result)); errorCount++; } int32_t framesPerBurst = AAudioStream_getFramesPerBurst(engine.stream); + // Use double buffered stream. + const int32_t bufferSize = AAudioStream_setBufferSizeInFrames(engine.stream, 2 * framesPerBurst); + if (bufferSize < 0) { + printf("AAudioStream_setBufferSizeInFrames returned %s\n", + AAudio_convertResultToText(bufferSize)); + errorCount++; + } + // Check to see what kind of stream we actually got. int32_t deviceId = AAudioStream_getDeviceId(engine.stream); aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream); @@ -235,21 +265,14 @@ int main(int argc, char **argv) { if (result == AAUDIO_OK) { const int watchLoops = LOOP_DURATION_MSEC / SLEEP_DURATION_MSEC; for (int i = watchLoops; i > 0; i--) { - printf("playing silence #%02d, framesRead = %7d, framesWritten = %7d," - " framesCalled = %6d, callbackCount = %4d\n", - i, - (int32_t) AAudioStream_getFramesRead(engine.stream), - (int32_t) AAudioStream_getFramesWritten(engine.stream), - engine.framesCalled.load(), - engine.callbackCount.load() - ); + errorCount += s_checkEnginePositions(&engine) ? 1 : 0; usleep(SLEEP_DURATION_MSEC * 1000); } } if (engine.stopAtFrame != INT32_MAX) { callbackResult = (engine.callbackCountAfterStop == 0) ? EXIT_SUCCESS - : EXIT_FAILURE; + : EXIT_FAILURE; if (callbackResult) { printf("ERROR - Callback count after STOP = %d\n", engine.callbackCountAfterStop.load()); @@ -268,9 +291,7 @@ int main(int argc, char **argv) { errorCount++; } usleep(SLEEP_DURATION_MSEC * 1000); - printf("getFramesRead() = %d, getFramesWritten() = %d\n", - (int32_t) AAudioStream_getFramesRead(engine.stream), - (int32_t) AAudioStream_getFramesWritten(engine.stream)); + errorCount += s_checkEnginePositions(&engine) ? 1 : 0; } s_CloseAudioStream(&engine); -- GitLab From 18c8476f1f86c81e55812ce0d34a977d435b6c5d Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 18 Dec 2018 12:15:35 -0800 Subject: [PATCH 0862/1530] aaudio: fix framesWritten for Legacy INPUT Was set to 2X framesRead on stop. It turns out that AudioRecord::stop() does NOT reset getPosition() to zero. But AudioRecord::start() does. So the logic handling the reset had to move to the start code. Bug: 121196899 Test: test_return_stop -i -n Change-Id: I93635f9ba501b71b99b1df34c7920f7bfea5ca88 --- .../src/legacy/AudioStreamRecord.cpp | 23 ++++++++----------- .../libaaudio/src/legacy/AudioStreamTrack.cpp | 4 ++-- .../libaaudio/src/utility/MonotonicCounter.h | 8 ++++--- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp index f5500898ea..4a65fc9d18 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp +++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp @@ -326,16 +326,13 @@ aaudio_result_t AudioStreamRecord::requestStart() if (mAudioRecord.get() == nullptr) { return AAUDIO_ERROR_INVALID_STATE; } - // Get current position so we can detect when the track is recording. - status_t err = mAudioRecord->getPosition(&mPositionWhenStarting); - if (err != OK) { - return AAudioConvert_androidToAAudioResult(err); - } - // Enable callback before starting AudioTrack to avoid shutting + // Enable callback before starting AudioRecord to avoid shutting // down because of a race condition. mCallbackEnabled.store(true); - err = mAudioRecord->start(); + mFramesWritten.reset32(); // service writes frames + mTimestampPosition.reset32(); + status_t err = mAudioRecord->start(); // resets position to zero if (err != OK) { return AAudioConvert_androidToAAudioResult(err); } else { @@ -349,12 +346,10 @@ aaudio_result_t AudioStreamRecord::requestStop() { return AAUDIO_ERROR_INVALID_STATE; } setState(AAUDIO_STREAM_STATE_STOPPING); - incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review - mTimestampPosition.set(getFramesRead()); + mFramesWritten.catchUpTo(getFramesRead()); + mTimestampPosition.catchUpTo(getFramesRead()); mAudioRecord->stop(); mCallbackEnabled.store(false); - mFramesWritten.reset32(); // service writes frames, service position reset on flush - mTimestampPosition.reset32(); // Pass false to prevent errorCallback from being called after disconnect // when app has already requested a stop(). return checkForDisconnectRequest(false); @@ -368,10 +363,12 @@ aaudio_result_t AudioStreamRecord::updateStateMachine() switch (getState()) { // TODO add better state visibility to AudioRecord case AAUDIO_STREAM_STATE_STARTING: + // When starting, the position will begin at zero and then go positive. + // The position can wrap but by that time the state will not be STARTING. err = mAudioRecord->getPosition(&position); if (err != OK) { result = AAudioConvert_androidToAAudioResult(err); - } else if (position != mPositionWhenStarting) { + } else if (position > 0) { setState(AAUDIO_STREAM_STATE_STARTED); } break; @@ -504,12 +501,12 @@ int64_t AudioStreamRecord::getFramesWritten() { switch (getState()) { case AAUDIO_STREAM_STATE_STARTING: case AAUDIO_STREAM_STATE_STARTED: - case AAUDIO_STREAM_STATE_STOPPING: result = mAudioRecord->getPosition(&position); if (result == OK) { mFramesWritten.update32(position); } break; + case AAUDIO_STREAM_STATE_STOPPING: default: break; } diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index c995e99cb5..ff95aedcb6 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -323,8 +323,8 @@ aaudio_result_t AudioStreamTrack::requestStop() { } setState(AAUDIO_STREAM_STATE_STOPPING); - incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review - mTimestampPosition.set(getFramesWritten()); + mFramesRead.catchUpTo(getFramesWritten()); + mTimestampPosition.catchUpTo(getFramesWritten()); mFramesRead.reset32(); // service reads frames, service position reset on stop mTimestampPosition.reset32(); mAudioTrack->stop(); diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h index 5833eab3cb..63add4e8dd 100644 --- a/media/libaaudio/src/utility/MonotonicCounter.h +++ b/media/libaaudio/src/utility/MonotonicCounter.h @@ -41,10 +41,12 @@ public: } /** - * set the current value of the counter + * advance the current value to match the counter */ - void set(int64_t counter) { - mCounter64 = counter; + void catchUpTo(int64_t counter) { + if ((counter - mCounter64) > 0) { + mCounter64 = counter; + } } /** -- GitLab From 377c1c2d47299c63fe837ec7df02993461c7d639 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 12 Dec 2018 16:06:54 -0800 Subject: [PATCH 0863/1530] aaudio: cleanup code that reads timing model Only use the clock model for the position when it is running and when the service is not updating the FIFO counter. Bug: 120932593 Bug: 122374244 Test: test_return_stop Test: test_return_stop -i Change-Id: I6d6a23b35dca5529b6221e1b3b4a4e6672093bf2 --- .../src/client/AudioStreamInternal.cpp | 4 +++ .../src/client/AudioStreamInternal.h | 8 ++++++ .../src/client/AudioStreamInternalCapture.cpp | 13 +++------ .../src/client/AudioStreamInternalPlay.cpp | 28 ++++++------------- .../src/client/IsochronousClockModel.cpp | 6 +++- .../src/client/IsochronousClockModel.h | 10 ++++++- 6 files changed, 39 insertions(+), 30 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index 3b03601282..ec270f3745 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -713,3 +713,7 @@ int32_t AudioStreamInternal::getFramesPerBurst() const { aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) { return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst())); } + +bool AudioStreamInternal::isClockModelInControl() const { + return isActive() && mAudioEndpoint.isFreeRunning() && mClockModel.isRunning(); +} diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h index 1c88f529b9..86c4698d86 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.h +++ b/media/libaaudio/src/client/AudioStreamInternal.h @@ -144,6 +144,14 @@ protected: */ bool isInService() const { return mInService; } + /** + * Is the service FIFO position currently controlled by the AAudio service or HAL, + * or set based on the Clock Model. + * + * @return true if the ClockModel is currently determining the FIFO position + */ + bool isClockModelInControl() const; + IsochronousClockModel mClockModel; // timing model for chasing the HAL AudioEndpoint mAudioEndpoint; // source for reads or sink for writes diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp index 7dcb620dd2..a6cc45b464 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp @@ -210,17 +210,12 @@ aaudio_result_t AudioStreamInternalCapture::readNowWithConversion(void *buffer, } int64_t AudioStreamInternalCapture::getFramesWritten() { - int64_t framesWrittenHardware; - if (isActive()) { - framesWrittenHardware = mClockModel.convertTimeToPosition(AudioClock::getNanoseconds()); - } else { - framesWrittenHardware = mAudioEndpoint.getDataWriteCounter(); - } - // Prevent retrograde motion. + const int64_t framesWrittenHardware = isClockModelInControl() + ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds()) + : mAudioEndpoint.getDataWriteCounter(); + // Add service offset and prevent retrograde motion. mLastFramesWritten = std::max(mLastFramesWritten, framesWrittenHardware + mFramesOffsetFromService); - //ALOGD("getFramesWritten() returns %lld", - // (long long)mLastFramesWritten); return mLastFramesWritten; } diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 6af8e7d1e5..e1443d913f 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -242,27 +242,17 @@ aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buff return framesWritten; } -int64_t AudioStreamInternalPlay::getFramesRead() -{ - int64_t framesReadHardware; - if (isActive()) { - framesReadHardware = mClockModel.convertTimeToPosition(AudioClock::getNanoseconds()); - } else { - framesReadHardware = mAudioEndpoint.getDataReadCounter(); - } - int64_t framesRead = framesReadHardware + mFramesOffsetFromService; - // Prevent retrograde motion. - if (framesRead < mLastFramesRead) { - framesRead = mLastFramesRead; - } else { - mLastFramesRead = framesRead; - } - return framesRead; +int64_t AudioStreamInternalPlay::getFramesRead() { + const int64_t framesReadHardware = isClockModelInControl() + ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds()) + : mAudioEndpoint.getDataReadCounter(); + // Add service offset and prevent retrograde motion. + mLastFramesRead = std::max(mLastFramesRead, framesReadHardware + mFramesOffsetFromService); + return mLastFramesRead; } -int64_t AudioStreamInternalPlay::getFramesWritten() -{ - int64_t framesWritten = mAudioEndpoint.getDataWriteCounter() +int64_t AudioStreamInternalPlay::getFramesWritten() { + const int64_t framesWritten = mAudioEndpoint.getDataWriteCounter() + mFramesOffsetFromService; return framesWritten; } diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp index 95b52bea03..747d0e14bc 100644 --- a/media/libaaudio/src/client/IsochronousClockModel.cpp +++ b/media/libaaudio/src/client/IsochronousClockModel.cpp @@ -60,10 +60,14 @@ void IsochronousClockModel::stop(int64_t nanoTime) { mState = STATE_STOPPED; } -bool IsochronousClockModel::isStarting() { +bool IsochronousClockModel::isStarting() const { return mState == STATE_STARTING; } +bool IsochronousClockModel::isRunning() const { + return mState == STATE_RUNNING; +} + void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) { // ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu", // (long long)framePosition, diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h index 7182376163..46ca48e7af 100644 --- a/media/libaaudio/src/client/IsochronousClockModel.h +++ b/media/libaaudio/src/client/IsochronousClockModel.h @@ -36,7 +36,15 @@ public: void start(int64_t nanoTime); void stop(int64_t nanoTime); - bool isStarting(); + /** + * @return true if the model is starting up + */ + bool isStarting() const; + + /** + * @return true if the model is running and producing valid results + */ + bool isRunning() const; void processTimestamp(int64_t framePosition, int64_t nanoTime); -- GitLab From 7b417a29df10d12d4cd019e5b3fcaaf2c1208420 Mon Sep 17 00:00:00 2001 From: Dean Wheatley Date: Thu, 31 Jan 2019 20:39:42 +1100 Subject: [PATCH 0864/1530] Fix audio HAL 4.0 StreamHalHidl setParameters Test: make Change-Id: I944c417d9b9e51fb39950dd53f2c1ce7b7a818bb --- media/libaudiohal/impl/StreamHalHidl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 2e35be63fe..e396cf3878 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -106,7 +106,7 @@ status_t StreamHalHidl::setParameters(const String8& kvPairs) { status_t status = parametersFromHal(kvPairs, &hidlParams); if (status != OK) return status; return processReturn("setParameters", - utils::setParameters(mStream, hidlParams, {} /* options */)); + utils::setParameters(mStream, {} /* context */, hidlParams)); } status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { -- GitLab From 3d8a76288333239222e9f748a6557cb881c55c6a Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 1 Feb 2019 08:12:54 -0800 Subject: [PATCH 0865/1530] Load extractors from libs/extractors/ if present Bug: 123721777 Test: manual Change-Id: I1179cf661d5d93bfc681bca92c9388546ad2ac47 --- media/libstagefright/MediaExtractorFactory.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 86402cee52..19b174f65b 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -346,11 +346,13 @@ void MediaExtractorFactory::RegisterExtractorsInApex( ALOGV("search for plugins at %s", libDirPath); ALOGV("linked libs %s", gLinkedLibraries.c_str()); + std::string libDirPathEx = libDirPath; + libDirPathEx += "/extractors"; android_namespace_t *extractorNs = android_create_namespace("extractor", nullptr, // ld_library_path - libDirPath, + libDirPath, // default_library_path ANDROID_NAMESPACE_TYPE_ISOLATED, - nullptr, // permitted_when_isolated_path + libDirPathEx.c_str(), // permitted_when_isolated_path nullptr); // parent if (!android_link_namespaces(extractorNs, nullptr, gLinkedLibraries.c_str())) { ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex."); @@ -361,7 +363,14 @@ void MediaExtractorFactory::RegisterExtractorsInApex( .library_namespace = extractorNs, }; - DIR *libDir = opendir(libDirPath); + // try extractors subfolder first + DIR *libDir = opendir(libDirPathEx.c_str()); + + if (libDir) { + libDirPath = libDirPathEx.c_str(); + } else { + libDir = opendir(libDirPath); + } if (libDir) { struct dirent* libEntry; while ((libEntry = readdir(libDir))) { -- GitLab From 175e9917274a457225f64abd3d46f8dc89b9dc08 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Thu, 31 Jan 2019 18:33:24 -0800 Subject: [PATCH 0866/1530] Add libstagefright_mpeg2extractor Test: build Change-Id: I17b08ed8fe30c592e40ac848c587bbb1c140d35f --- media/extractors/mpeg2/Android.bp | 4 +++- media/libstagefright/Android.bp | 35 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index 2a94671207..0f0c72cb82 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -24,6 +24,8 @@ cc_library_shared { header_libs: [ "libbase_headers", + "libstagefright_headers", + "libmedia_headers", ], static_libs: [ @@ -31,7 +33,7 @@ cc_library_shared { "libstagefright_foundation_without_imemory", "libstagefright_mpeg2support", "libutils", - "libstagefright", + "libstagefright_mpeg2extractor", "libstagefright_esds", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 03eef48ab3..26464b80a0 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -77,6 +77,41 @@ cc_library_shared { }, } +cc_library_static { + name: "libstagefright_mpeg2extractor", + + srcs: [ + "Utils.cpp", + "MediaSource.cpp", + "HevcUtils.cpp", + ], + + shared_libs: [ + "liblog", + "libmedia", + "libmedia_omx", + ], + + export_include_dirs: [ + "include", + ], + + cflags: [ + "-Wno-multichar", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wall", + ], + + sanitize: { + cfi: true, + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + }, +} + cc_library { name: "libstagefright", -- GitLab From 3c49347c2ae3ae44d8a651c35e723e04b27d651d Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Sat, 22 Dec 2018 18:11:34 -0800 Subject: [PATCH 0867/1530] Add memfd related missing syscalls to allowed list Bug: 113362644 Change-Id: I7ae3305ad12600a38faebf0b66cb5b954862df5e Signed-off-by: Joel Fernandes --- services/mediacodec/seccomp_policy/mediacodec-arm.policy | 1 + services/mediacodec/seccomp_policy/mediacodec-x86.policy | 3 +++ services/mediacodec/seccomp_policy/mediaswcodec-arm.policy | 1 + services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy | 1 + 4 files changed, 6 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy index 0aa5acc2a7..9bdd4c805e 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy @@ -14,6 +14,7 @@ ppoll: 1 mmap2: 1 getrandom: 1 memfd_create: 1 +ftruncate: 1 ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index 6d88c845df..a48cedbfdc 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -55,6 +55,9 @@ write: 1 nanosleep: 1 sched_setscheduler: 1 uname: 1 +memfd_create: 1 +ftruncate: 1 +ftruncate64: 1 # Required by AddressSanitizer gettid: 1 diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy index b9adbd9613..02cedba8f0 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy @@ -22,6 +22,7 @@ ppoll: 1 mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE mmap2: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE memfd_create: 1 +ftruncate: 1 ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy index 7abb432b23..78ecaf58a9 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy @@ -27,6 +27,7 @@ fstat: 1 newfstatat: 1 fstatfs: 1 memfd_create: 1 +ftruncate: 1 ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail -- GitLab From 866f0f05d15f62bdf3621eab41433f3ff9f63d7b Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 1 Feb 2019 14:19:37 -0800 Subject: [PATCH 0868/1530] Wait for iref box if the heic file has Exif data When exif items are present, iref box must be present to associate exif with image items. bug: 123094535 test: use ExifInterface on files without grid, should be able to extract exif if available. Change-Id: Ia0728cdeefe28318a4ca03193ad93c3b64d78f7a --- media/extractors/mp4/ItemTable.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index eb6602c20c..a72e589b59 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -17,6 +17,8 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ItemTable" +#include + #include #include #include @@ -1129,19 +1131,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), mHasGrids(false) {} + FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {} status_t parse(off64_t offset, size_t size); - bool hasGrids() { return mHasGrids; } + bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; } protected: status_t onChunkData(uint32_t type, off64_t offset, size_t size) override; private: Vector *mItemInfos; - bool mHasGrids; + std::unordered_set mFourCCSeen; }; status_t IinfBox::parse(off64_t offset, size_t size) { @@ -1188,7 +1189,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); - mHasGrids |= (itemInfo.itemType == FOURCC("grid")); + mFourCCSeen.insert(itemInfo.itemType); } // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported // version. Ignore this error as it's not fatal. @@ -1277,7 +1278,7 @@ status_t ItemTable::parseIinfBox(off64_t offset, size_t size) { return err; } - if (iinfBox.hasGrids()) { + if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) { mRequiredBoxes.insert('iref'); } -- GitLab From 38ad341fffdf002335c6257764ba48161802e8be Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 1 Feb 2019 15:13:23 -0800 Subject: [PATCH 0869/1530] CCodec: fix reorder stash handling Bug: 118397206 Test: manual tests Change-Id: I2896e4d2ea8727285697ed029ee9063edbf43742 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 9616e47047..8e6a3f8fe9 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1550,13 +1550,13 @@ void CCodecBufferChannel::ReorderStash::emplace( int64_t timestamp, int32_t flags, const C2WorkOrdinalStruct &ordinal) { - for (auto it = mStash.begin(); it != mStash.end(); ++it) { + auto it = mStash.begin(); + for (; it != mStash.end(); ++it) { if (less(ordinal, it->ordinal)) { - mStash.emplace(it, buffer, timestamp, flags, ordinal); - return; + break; } } - mStash.emplace_back(buffer, timestamp, flags, ordinal); + mStash.emplace(it, buffer, timestamp, flags, ordinal); while (!mStash.empty() && mStash.size() > mDepth) { mPending.push_back(mStash.front()); mStash.pop_front(); @@ -2746,32 +2746,39 @@ void CCodecBufferChannel::sendOutputBuffers() { size_t index; while (true) { - { - Mutexed::Locked reorder(mReorderStash); - if (!reorder->hasPending()) { - break; - } - if (!reorder->pop(&entry)) { - break; - } + Mutexed::Locked reorder(mReorderStash); + if (!reorder->hasPending()) { + break; } + if (!reorder->pop(&entry)) { + break; + } + Mutexed>::Locked buffers(mOutputBuffers); status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer); if (err != OK) { + bool outputBuffersChanged = false; if (err != WOULD_BLOCK) { if (!(*buffers)->isArrayMode()) { *buffers = (*buffers)->toArrayMode(mNumOutputSlots); } OutputBuffersArray *array = (OutputBuffersArray *)buffers->get(); array->realloc(entry.buffer); - mCCodecCallback->onOutputBuffersChanged(); + outputBuffersChanged = true; } - buffers.unlock(); ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName); - mReorderStash.lock()->defer(entry); + reorder->defer(entry); + + buffers.unlock(); + reorder.unlock(); + + if (outputBuffersChanged) { + mCCodecCallback->onOutputBuffersChanged(); + } return; } buffers.unlock(); + reorder.unlock(); outBuffer->meta()->setInt64("timeUs", entry.timestamp); outBuffer->meta()->setInt32("flags", entry.flags); -- GitLab From ab3c6a5b751258159e5b1e3f7dcb47b131dbfdc6 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 1 Feb 2019 09:52:15 -0800 Subject: [PATCH 0870/1530] audioflinger: allow unsigned overflow in getRenderPosition Overflow is expected when 32-bit counter wraps after several hours. So prevent sanitization from causing a crash. Bug: 122986677 Test: run a stream for more than 12 hours so the position overflows Change-Id: I0f397f193288c2493a2036f67849bb12a9c9f221 --- services/audioflinger/AudioStreamOut.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp index 1d4b3fef99..a60a5f2272 100644 --- a/services/audioflinger/AudioStreamOut.cpp +++ b/services/audioflinger/AudioStreamOut.cpp @@ -66,8 +66,9 @@ status_t AudioStreamOut::getRenderPosition(uint64_t *frames) // Maintain a 64-bit render position using the 32-bit result from the HAL. // This delta calculation relies on the arithmetic overflow behavior // of integers. For example (100 - 0xFFFFFFF0) = 116. - uint32_t truncatedPosition = (uint32_t)mRenderPosition; - int32_t deltaHalPosition = (int32_t)(halPosition - truncatedPosition); + const uint32_t truncatedPosition = (uint32_t)mRenderPosition; + int32_t deltaHalPosition; // initialization not needed, overwitten by __builtin_sub_overflow() + (void) __builtin_sub_overflow(halPosition, truncatedPosition, &deltaHalPosition); if (deltaHalPosition > 0) { mRenderPosition += deltaHalPosition; } -- GitLab From 63f3611018857754d60a176783133371bae5f009 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Thu, 6 Dec 2018 14:57:03 -0800 Subject: [PATCH 0871/1530] Camera service: Support for device state change notifications Add AIDL method for device-wide physical state updates (such as folding/unfolding), and wire it up to the new camera provider HAL call. Also clean up camera provider startup sequence - devices were being enumerated before the new lazy HAL interface pointers were set up, resulting in many repeated calls to getService even for a non-lazy HAL. Also add unit test for CameraProviderManager to verify its section of the callpath, as well as tests to check that the provider HAL and hardware service manager aren't queried more than expected during initialization. Test: atest cameraservice_test Change-Id: I5ec60fd9d93b7a2fe4d1a5854fad720a972fe8ea --- .../aidl/android/hardware/ICameraService.aidl | 24 +++- camera/cameraserver/Android.bp | 1 + services/camera/libcameraservice/Android.bp | 1 + .../camera/libcameraservice/CameraService.cpp | 43 +++++++ .../camera/libcameraservice/CameraService.h | 2 + .../common/CameraProviderManager.cpp | 119 +++++++++++++++--- .../common/CameraProviderManager.h | 27 +++- .../camera/libcameraservice/tests/Android.mk | 1 + .../tests/CameraProviderManagerTest.cpp | 73 ++++++++++- 9 files changed, 266 insertions(+), 25 deletions(-) diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl index c03831457f..0e969c7662 100644 --- a/camera/aidl/android/hardware/ICameraService.aidl +++ b/camera/aidl/android/hardware/ICameraService.aidl @@ -162,6 +162,28 @@ interface ICameraService * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission. */ const int EVENT_NONE = 0; - const int EVENT_USER_SWITCHED = 1; + 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 device physical status change. May only be called from + * a privileged process. + * + * newState is a bitfield consisting of DEVICE_STATE_* values combined together. Valid state + * combinations are device-specific. At device startup, the camera service will assume the device + * state is NORMAL until otherwise notified. + * + * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission. + */ + oneway void notifyDeviceStateChange(long newState); + + // Bitfield constants for notifyDeviceStateChange + // All bits >= 32 are for custom vendor states + // Written as ints since AIDL does not support long constants. + const int DEVICE_STATE_NORMAL = 0; + const int DEVICE_STATE_BACK_COVERED = 1; + const int DEVICE_STATE_FRONT_COVERED = 2; + const int DEVICE_STATE_FOLDED = 4; + const int DEVICE_STATE_LAST_FRAMEWORK_BIT = 0x80000000; // 1 << 31; + } diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp index b88a2c5a70..92b06c2d10 100644 --- a/camera/cameraserver/Android.bp +++ b/camera/cameraserver/Android.bp @@ -27,6 +27,7 @@ cc_binary { "libhidltransport", "android.hardware.camera.common@1.0", "android.hardware.camera.provider@2.4", + "android.hardware.camera.provider@2.5", "android.hardware.camera.device@1.0", "android.hardware.camera.device@3.2", "android.hardware.camera.device@3.4", diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index a090479183..38c87c721d 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -93,6 +93,7 @@ cc_library_shared { "android.frameworks.cameraservice.device@2.0", "android.hardware.camera.common@1.0", "android.hardware.camera.provider@2.4", + "android.hardware.camera.provider@2.5", "android.hardware.camera.device@1.0", "android.hardware.camera.device@3.2", "android.hardware.camera.device@3.3", diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index c3113bf6ea..0b34b3b9f2 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1632,6 +1632,49 @@ Status CameraService::notifySystemEvent(int32_t eventId, return Status::ok(); } +Status CameraService::notifyDeviceStateChange(int64_t newState) { + const int pid = CameraThreadState::getCallingPid(); + const int selfPid = getpid(); + + // Permission checks + if (pid != selfPid) { + // Ensure we're being called by system_server, or similar process with + // permissions to notify the camera service about system events + if (!checkCallingPermission( + String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) { + const int uid = CameraThreadState::getCallingUid(); + ALOGE("Permission Denial: cannot send updates to camera service about device" + " state changes from pid=%d, uid=%d", pid, uid); + return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED, + "No permission to send updates to camera service about device state" + " changes from pid=%d, uid=%d", pid, uid); + } + } + + ATRACE_CALL(); + + using hardware::camera::provider::V2_5::DeviceState; + hardware::hidl_bitfield newDeviceState{}; + if (newState & ICameraService::DEVICE_STATE_BACK_COVERED) { + newDeviceState |= DeviceState::BACK_COVERED; + } + if (newState & ICameraService::DEVICE_STATE_FRONT_COVERED) { + newDeviceState |= DeviceState::FRONT_COVERED; + } + if (newState & ICameraService::DEVICE_STATE_FOLDED) { + newDeviceState |= DeviceState::FOLDED; + } + // Only map vendor bits directly + uint64_t vendorBits = static_cast(newState) & 0xFFFFFFFF00000000l; + newDeviceState |= vendorBits; + + ALOGV("%s: New device state 0x%" PRIx64, __FUNCTION__, newDeviceState); + Mutex::Autolock l(mServiceLock); + mCameraProviderManager->notifyDeviceStateChange(newDeviceState); + + return Status::ok(); +} + Status CameraService::addListener(const sp& listener, /*out*/ std::vector *cameraStatuses) { diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index a2961986b2..f351e7c8b7 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -154,6 +154,8 @@ public: virtual binder::Status notifySystemEvent(int32_t eventId, const std::vector& args); + virtual binder::Status notifyDeviceStateChange(int64_t newState); + // OK = supports api of that version, -EOPNOTSUPP = does not support virtual binder::Status supportsCameraApi( const String16& cameraId, int32_t apiVersion, diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 3059b0737d..137ffea66f 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -77,6 +77,8 @@ status_t CameraProviderManager::initialize(wp>( + provider::V2_5::DeviceState::NORMAL); // Registering will trigger notifications for all already-known providers bool success = mServiceProxy->registerForNotifications( @@ -280,6 +282,26 @@ status_t CameraProviderManager::setUpVendorTags() { return OK; } +status_t CameraProviderManager::notifyDeviceStateChange( + hardware::hidl_bitfield newState) { + std::lock_guard lock(mInterfaceMutex); + mDeviceState = newState; + status_t res = OK; + for (auto& provider : mProviders) { + ALOGV("%s: Notifying %s for new state 0x%" PRIx64, + __FUNCTION__, provider->mProviderName.c_str(), newState); + status_t singleRes = provider->notifyDeviceStateChange(mDeviceState); + if (singleRes != OK) { + ALOGE("%s: Unable to notify provider %s about device state change", + __FUNCTION__, + provider->mProviderName.c_str()); + res = singleRes; + // continue to do the rest of the providers instead of returning now + } + } + return res; +} + status_t CameraProviderManager::openSession(const std::string &id, const sp& callback, /*out*/ @@ -365,7 +387,7 @@ void CameraProviderManager::saveRef(DeviceMode usageType, const std::string &cam if (!kEnableLazyHal) { return; } - ALOGI("Saving camera provider %s for camera device %s", provider->descriptor, cameraId.c_str()); + ALOGV("Saving camera provider %s for camera device %s", provider->descriptor, cameraId.c_str()); std::lock_guard lock(mProviderInterfaceMapLock); std::unordered_map> *primaryMap, *alternateMap; if (usageType == DeviceMode::TORCH) { @@ -389,7 +411,7 @@ void CameraProviderManager::removeRef(DeviceMode usageType, const std::string &c if (!kEnableLazyHal) { return; } - ALOGI("Removing camera device %s", cameraId.c_str()); + ALOGV("Removing camera device %s", cameraId.c_str()); std::unordered_map> *providerMap; if (usageType == DeviceMode::TORCH) { providerMap = &mTorchProviderByCameraId; @@ -947,7 +969,7 @@ status_t CameraProviderManager::addProviderLocked(const std::string& newProvider } sp providerInfo = new ProviderInfo(newProvider, this); - status_t res = providerInfo->initialize(interface); + status_t res = providerInfo->initialize(interface, mDeviceState); if (res != OK) { return res; } @@ -1008,7 +1030,8 @@ CameraProviderManager::ProviderInfo::ProviderInfo( } status_t CameraProviderManager::ProviderInfo::initialize( - sp& interface) { + sp& interface, + hardware::hidl_bitfield currentDeviceState) { status_t res = parseProviderName(mProviderName, &mType, &mId); if (res != OK) { ALOGE("%s: Invalid provider name, ignoring", __FUNCTION__); @@ -1016,6 +1039,15 @@ status_t CameraProviderManager::ProviderInfo::initialize( } ALOGI("Connecting to new camera provider: %s, isRemote? %d", mProviderName.c_str(), interface->isRemote()); + + // Determine minor version + auto castResult = provider::V2_5::ICameraProvider::castFrom(interface); + if (castResult.isOk()) { + mMinorVersion = 5; + } else { + mMinorVersion = 4; + } + // cameraDeviceStatusChange callbacks may be called (and causing new devices added) // before setCallback returns hardware::Return status = interface->setCallback(this); @@ -1040,6 +1072,24 @@ status_t CameraProviderManager::ProviderInfo::initialize( __FUNCTION__, mProviderName.c_str()); } + if (!kEnableLazyHal) { + // Save HAL reference indefinitely + mSavedInterface = interface; + } else { + mActiveInterface = interface; + } + + ALOGV("%s: Setting device state for %s: 0x%" PRIx64, + __FUNCTION__, mProviderName.c_str(), mDeviceState); + notifyDeviceStateChange(currentDeviceState); + + res = setUpVendorTags(); + if (res != OK) { + ALOGE("%s: Unable to set up vendor tags from provider '%s'", + __FUNCTION__, mProviderName.c_str()); + return res; + } + // Get initial list of camera devices, if any std::vector devices; hardware::Return ret = interface->getCameraIdList([&status, this, &devices]( @@ -1096,34 +1146,28 @@ status_t CameraProviderManager::ProviderInfo::initialize( } } - res = setUpVendorTags(); - if (res != OK) { - ALOGE("%s: Unable to set up vendor tags from provider '%s'", - __FUNCTION__, mProviderName.c_str()); - return res; - } - ALOGI("Camera provider %s ready with %zu camera devices", mProviderName.c_str(), mDevices.size()); mInitialized = true; - if (!kEnableLazyHal) { - // Save HAL reference indefinitely - mSavedInterface = interface; - } return OK; } const sp CameraProviderManager::ProviderInfo::startProviderInterface() { ATRACE_CALL(); - ALOGI("Request to start camera provider: %s", mProviderName.c_str()); + ALOGV("Request to start camera provider: %s", mProviderName.c_str()); if (mSavedInterface != nullptr) { return mSavedInterface; } + if (!kEnableLazyHal) { + ALOGE("Bad provider state! Should not be here on a non-lazy HAL!"); + return nullptr; + } + auto interface = mActiveInterface.promote(); if (interface == nullptr) { - ALOGI("Could not promote, calling getService(%s)", mProviderName.c_str()); + ALOGI("Camera HAL provider needs restart, calling getService(%s)", mProviderName.c_str()); interface = mManager->mServiceProxy->getService(mProviderName); interface->setCallback(this); hardware::Return linked = interface->linkToDeath(this, /*cookie*/ mId); @@ -1136,9 +1180,22 @@ CameraProviderManager::ProviderInfo::startProviderInterface() { ALOGW("%s: Unable to link to provider '%s' death notifications", __FUNCTION__, mProviderName.c_str()); } + // Send current device state + if (mMinorVersion >= 5) { + auto castResult = provider::V2_5::ICameraProvider::castFrom(interface); + if (castResult.isOk()) { + sp interface_2_5 = castResult; + if (interface_2_5 != nullptr) { + ALOGV("%s: Initial device state for %s: 0x %" PRIx64, + __FUNCTION__, mProviderName.c_str(), mDeviceState); + interface_2_5->notifyDeviceStateChange(mDeviceState); + } + } + } + mActiveInterface = interface; } else { - ALOGI("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str()); + ALOGV("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str()); } return interface; } @@ -1223,8 +1280,10 @@ void CameraProviderManager::ProviderInfo::removeDevice(std::string id) { } status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector&) const { - dprintf(fd, "== Camera Provider HAL %s (v2.4, %s) static info: %zu devices: ==\n", - mProviderName.c_str(), mIsRemote ? "remote" : "passthrough", + dprintf(fd, "== Camera Provider HAL %s (v2.%d, %s) static info: %zu devices: ==\n", + mProviderName.c_str(), + mMinorVersion, + mIsRemote ? "remote" : "passthrough", mDevices.size()); for (auto& device : mDevices) { @@ -1423,6 +1482,26 @@ status_t CameraProviderManager::ProviderInfo::setUpVendorTags() { return OK; } +status_t CameraProviderManager::ProviderInfo::notifyDeviceStateChange( + hardware::hidl_bitfield newDeviceState) { + mDeviceState = newDeviceState; + if (mMinorVersion >= 5) { + // Check if the provider is currently active - not going to start it up for this notification + auto interface = mSavedInterface != nullptr ? mSavedInterface : mActiveInterface.promote(); + if (interface != nullptr) { + // Send current device state + auto castResult = provider::V2_5::ICameraProvider::castFrom(interface); + if (castResult.isOk()) { + sp interface_2_5 = castResult; + if (interface_2_5 != nullptr) { + interface_2_5->notifyDeviceStateChange(mDeviceState); + } + } + } + } + return OK; +} + template std::unique_ptr CameraProviderManager::ProviderInfo::initializeDeviceInfo( diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index fbd7d2e8f8..78007ff74f 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -28,9 +28,8 @@ #include #include #include -#include +#include #include -//#include #include #include @@ -205,6 +204,12 @@ public: */ status_t setUpVendorTags(); + /** + * Inform registered providers about a device state change, such as folding or unfolding + */ + status_t notifyDeviceStateChange( + android::hardware::hidl_bitfield newState); + /** * Open an active session to a camera device. * @@ -276,6 +281,9 @@ private: wp mListener; ServiceInteractionProxy* mServiceProxy; + // Current overall Android device physical status + android::hardware::hidl_bitfield mDeviceState; + // mProviderLifecycleLock is locked during onRegistration and removeProvider mutable std::mutex mProviderLifecycleLock; @@ -302,10 +310,14 @@ private: { const std::string mProviderName; const metadata_vendor_id_t mProviderTagid; + int mMinorVersion; sp mVendorTagDescriptor; bool mSetTorchModeSupported; bool mIsRemote; + // Current overall Android device physical status + hardware::hidl_bitfield mDeviceState; + // This pointer is used to keep a reference to the ICameraProvider that was last accessed. wp mActiveInterface; @@ -315,7 +327,9 @@ private: CameraProviderManager *manager); ~ProviderInfo(); - status_t initialize(sp& interface); + status_t initialize(sp& interface, + hardware::hidl_bitfield + currentDeviceState); const sp startProviderInterface(); @@ -344,6 +358,13 @@ private: */ status_t setUpVendorTags(); + /** + * Notify provider about top-level device physical state changes + */ + status_t notifyDeviceStateChange( + hardware::hidl_bitfield + newDeviceState); + // Basic device information, common to all camera devices struct DeviceInfo { const std::string mName; // Full instance name diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk index ad9963afc1..d777ca1cb1 100644 --- a/services/camera/libcameraservice/tests/Android.mk +++ b/services/camera/libcameraservice/tests/Android.mk @@ -29,6 +29,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ android.hardware.camera.common@1.0 \ android.hardware.camera.provider@2.4 \ + android.hardware.camera.provider@2.5 \ android.hardware.camera.device@1.0 \ android.hardware.camera.device@3.2 \ android.hardware.camera.device@3.4 diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp index 0086c6c5de..f47e5a581e 100644 --- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp +++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp @@ -33,6 +33,7 @@ using android::hardware::camera::common::V1_0::VendorTagSection; using android::hardware::camera::common::V1_0::CameraMetadataType; using android::hardware::camera::device::V3_2::ICameraDeviceCallback; using android::hardware::camera::device::V3_2::ICameraDeviceSession; +using android::hardware::camera::provider::V2_5::DeviceState; /** * Basic test implementation of a camera ver. 3.2 device interface @@ -87,7 +88,7 @@ struct TestDeviceInterface : public device::V3_2::ICameraDevice { /** * Basic test implementation of a camera provider */ -struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { +struct TestICameraProvider : virtual public provider::V2_5::ICameraProvider { sp mCallbacks; std::vector mDeviceNames; sp mDeviceInterface; @@ -101,6 +102,7 @@ struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { virtual hardware::Return setCallback( const sp& callbacks) override { + mCalledCounter[SET_CALLBACK]++; mCallbacks = callbacks; return hardware::Return(Status::OK); } @@ -108,6 +110,7 @@ struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { using getVendorTags_cb = std::function& sections)>; hardware::Return getVendorTags(getVendorTags_cb _hidl_cb) override { + mCalledCounter[GET_VENDOR_TAGS]++; _hidl_cb(Status::OK, mVendorTagSections); return hardware::Void(); } @@ -117,6 +120,7 @@ struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { bool support)>; virtual ::hardware::Return isSetTorchModeSupported( isSetTorchModeSupported_cb _hidl_cb) override { + mCalledCounter[IS_SET_TORCH_MODE_SUPPORTED]++; _hidl_cb(Status::OK, false); return hardware::Void(); } @@ -124,6 +128,7 @@ struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { using getCameraIdList_cb = std::function& cameraDeviceNames)>; virtual hardware::Return getCameraIdList(getCameraIdList_cb _hidl_cb) override { + mCalledCounter[GET_CAMERA_ID_LIST]++; _hidl_cb(Status::OK, mDeviceNames); return hardware::Void(); } @@ -148,6 +153,25 @@ struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider { return hardware::Void(); } + virtual hardware::Return notifyDeviceStateChange( + hardware::hidl_bitfield newState) override { + mCalledCounter[NOTIFY_DEVICE_STATE]++; + mCurrentState = newState; + return hardware::Void(); + } + + enum MethodNames { + SET_CALLBACK, + GET_VENDOR_TAGS, + IS_SET_TORCH_MODE_SUPPORTED, + NOTIFY_DEVICE_STATE, + GET_CAMERA_ID_LIST, + + METHOD_NAME_COUNT + }; + int mCalledCounter[METHOD_NAME_COUNT] {0}; + + hardware::hidl_bitfield mCurrentState = 0xFFFFFFFF; // Unlikely to be a real state }; /** @@ -209,11 +233,26 @@ TEST(CameraProviderManagerTest, InitializeTest) { res = providerManager->initialize(statusListener, &serviceProxy); ASSERT_EQ(res, OK) << "Unable to initialize provider manager"; + // Check that both "legacy" and "external" providers (really the same object) are called + // once for all the init methods + EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], 2) << + "Only one call to setCallback per provider expected during init"; + EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], 2) << + "Only one call to getVendorTags per provider expected during init"; + EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED], 2) << + "Only one call to isSetTorchModeSupported per provider expected during init"; + EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], 2) << + "Only one call to getCameraIdList per provider expected during init"; + EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], 2) << + "Only one call to notifyDeviceState per provider expected during init"; std::string legacyInstanceName = "legacy/0"; std::string externalInstanceName = "external/0"; bool gotLegacy = false; bool gotExternal = false; + EXPECT_EQ(2u, serviceProxy.mLastRequestedServiceNames.size()) << + "Only two service queries expected to be seen by hardware service manager"; + for (auto& serviceName : serviceProxy.mLastRequestedServiceNames) { if (serviceName == legacyInstanceName) gotLegacy = true; if (serviceName == externalInstanceName) gotExternal = true; @@ -375,3 +414,35 @@ TEST(CameraProviderManagerTest, MultipleVendorTagTest) { metadataCopy.dump(1, 2); secondMetadata.dump(1, 2); } + +TEST(CameraProviderManagerTest, NotifyStateChangeTest) { + std::vector deviceNames { + "device@3.2/test/0", + "device@1.0/test/0", + "device@3.2/test/1"}; + + hardware::hidl_vec vendorSection; + status_t res; + sp providerManager = new CameraProviderManager(); + sp statusListener = new TestStatusListener(); + TestInteractionProxy serviceProxy; + sp provider = new TestICameraProvider(deviceNames, + vendorSection); + serviceProxy.setProvider(provider); + + res = providerManager->initialize(statusListener, &serviceProxy); + ASSERT_EQ(res, OK) << "Unable to initialize provider manager"; + + ASSERT_EQ(provider->mCurrentState, + static_cast>(DeviceState::NORMAL)) + << "Initial device state not set"; + + res = providerManager->notifyDeviceStateChange( + static_cast>(DeviceState::FOLDED)); + + ASSERT_EQ(res, OK) << "Unable to call notifyDeviceStateChange"; + ASSERT_EQ(provider->mCurrentState, + static_cast>(DeviceState::FOLDED)) + << "Unable to change device state"; + +} -- GitLab From dc7553ff1bf7ec88e17a8577b94bb9dd9a1cbb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Fri, 2 Nov 2018 10:39:57 +0100 Subject: [PATCH 0872/1530] audiopolicy: engine: Add ProductStrategy common code -Adds new Engine APIs for Product Strategy management -Adds a common engine code to handle product strategies -Adds a parsing configuration library to feed the strategies -Prepare both engine for the switch Test: make Change-Id: I00f57ece798893bc6f9aa9ed54a3e5237e8d5cf1 --- include/media/AudioCommonTypes.h | 1 + .../include/media/AudioCommonTypes.h | 34 ++ services/audiopolicy/Android.mk | 12 +- .../common/include/RoutingStrategy.h | 4 - services/audiopolicy/common/include/policy.h | 18 + services/audiopolicy/engine/Android.mk | 9 + services/audiopolicy/engine/common/Android.bp | 19 ++ .../engine/common/include/EngineBase.h | 99 ++++++ .../engine/common/include/ProductStrategy.h | 156 +++++++++ .../engine/common/src/EngineBase.cpp | 171 ++++++++++ .../engine/common/src/EngineDefaultConfig.h | 138 ++++++++ .../engine/common/src/ProductStrategy.cpp | 257 +++++++++++++++ services/audiopolicy/engine/config/Android.mk | 41 +++ .../engine/config/include/EngineConfig.h | 73 +++++ .../engine/config/src/EngineConfig.cpp | 307 ++++++++++++++++++ .../audiopolicy/engine/interface/Android.bp | 19 ++ .../interface/AudioPolicyManagerInterface.h | 139 ++++++++ .../audiopolicy/engineconfigurable/Android.mk | 15 +- .../parameter-framework/plugin/Android.mk | 4 +- .../engineconfigurable/src/Engine.cpp | 188 +++++++++-- .../engineconfigurable/src/Engine.h | 204 +++++------- ...on_criterion_types_from_android_headers.mk | 1 - .../engineconfigurable/wrapper/Android.mk | 4 + .../wrapper/ParameterManagerWrapper.cpp | 2 +- services/audiopolicy/enginedefault/Android.mk | 20 +- .../audiopolicy/enginedefault/src/Engine.cpp | 227 ++++++++----- .../audiopolicy/enginedefault/src/Engine.h | 148 ++++----- services/audiopolicy/tests/Android.mk | 4 +- 28 files changed, 1967 insertions(+), 347 deletions(-) create mode 120000 include/media/AudioCommonTypes.h create mode 100644 media/libaudioclient/include/media/AudioCommonTypes.h create mode 100644 services/audiopolicy/engine/Android.mk create mode 100644 services/audiopolicy/engine/common/Android.bp create mode 100644 services/audiopolicy/engine/common/include/EngineBase.h create mode 100644 services/audiopolicy/engine/common/include/ProductStrategy.h create mode 100644 services/audiopolicy/engine/common/src/EngineBase.cpp create mode 100644 services/audiopolicy/engine/common/src/EngineDefaultConfig.h create mode 100644 services/audiopolicy/engine/common/src/ProductStrategy.cpp create mode 100644 services/audiopolicy/engine/config/Android.mk create mode 100644 services/audiopolicy/engine/config/include/EngineConfig.h create mode 100644 services/audiopolicy/engine/config/src/EngineConfig.cpp create mode 100644 services/audiopolicy/engine/interface/Android.bp diff --git a/include/media/AudioCommonTypes.h b/include/media/AudioCommonTypes.h new file mode 120000 index 0000000000..ae7c99ac70 --- /dev/null +++ b/include/media/AudioCommonTypes.h @@ -0,0 +1 @@ +../../media/libaudioclient/include/media/AudioCommonTypes.h \ No newline at end of file diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h new file mode 100644 index 0000000000..4c936d4812 --- /dev/null +++ b/media/libaudioclient/include/media/AudioCommonTypes.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { + +enum product_strategy_t : uint32_t; +const product_strategy_t PRODUCT_STRATEGY_NONE = static_cast(-1); + +using AttributesVector = std::vector; + +} // namespace android + diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index ebb4f3b6c9..243020dd9d 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -11,9 +11,11 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES := \ frameworks/av/services/audioflinger \ $(call include-path-for, audio-utils) \ - frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ @@ -76,10 +78,12 @@ LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) LOCAL_C_INCLUDES += \ - frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents @@ -114,10 +118,12 @@ LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents LOCAL_C_INCLUDES += \ - frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_CFLAGS := -Wall -Werror LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h index f8a1cd6c37..bddb34052d 100644 --- a/services/audiopolicy/common/include/RoutingStrategy.h +++ b/services/audiopolicy/common/include/RoutingStrategy.h @@ -18,10 +18,6 @@ namespace android { -// Time in milliseconds after media stopped playing during which we consider that the -// sonification should be as unobtrusive as during the time media was playing. -#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 - enum routing_strategy { STRATEGY_NONE = -1, STRATEGY_MEDIA, diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 837ca47fa3..04f1eb2b46 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -17,9 +17,21 @@ #pragma once #include +#include + +namespace android { + +using StreamTypeVector = std::vector; + + +static const audio_attributes_t defaultAttr = AUDIO_ATTRIBUTES_INITIALIZER; + +} // namespace android static const audio_format_t gDynamicFormat = AUDIO_FORMAT_DEFAULT; +static const uint32_t SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY = 5000; + // For mixed output and inputs, the policy will use max mixer sampling rates. // Do not limit sampling rate otherwise #define SAMPLE_RATE_HZ_MAX 192000 @@ -151,3 +163,9 @@ static inline bool audio_formats_match(audio_format_t format1, } return format1 == format2; } + +constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_t &rhs) +{ + return lhs.usage == rhs.usage && lhs.content_type == rhs.content_type && + lhs.flags == rhs.flags && (std::strcmp(lhs.tags, rhs.tags) == 0); +} diff --git a/services/audiopolicy/engine/Android.mk b/services/audiopolicy/engine/Android.mk new file mode 100644 index 0000000000..dcce8e3d28 --- /dev/null +++ b/services/audiopolicy/engine/Android.mk @@ -0,0 +1,9 @@ + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +####################################################################### +# Recursive call sub-folder Android.mk +# +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/services/audiopolicy/engine/common/Android.bp b/services/audiopolicy/engine/common/Android.bp new file mode 100644 index 0000000000..e6ede07383 --- /dev/null +++ b/services/audiopolicy/engine/common/Android.bp @@ -0,0 +1,19 @@ +// 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. + +cc_library_headers { + name: "libaudiopolicyengine_common_headers", + host_supported: true, + export_include_dirs: ["include"], +} diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h new file mode 100644 index 0000000000..32898b142b --- /dev/null +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace android { +namespace audio_policy { + +class EngineBase : public AudioPolicyManagerInterface +{ +public: + /// + /// from AudioPolicyManagerInterface + /// + android::status_t initCheck() override; + + void setObserver(AudioPolicyManagerObserver *observer) override; + + status_t setPhoneState(audio_mode_t mode) override; + + audio_mode_t getPhoneState() const override { return mPhoneState; } + + status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override + { + mForceUse[usage] = config; + return NO_ERROR; + } + + audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const override + { + return mForceUse[usage]; + } + android::status_t setDeviceConnectionState(const sp /*devDesc*/, + audio_policy_dev_state_t /*state*/) override + { + return NO_ERROR; + } + product_strategy_t getProductStrategyForAttributes( + const audio_attributes_t &attr) const override; + + audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const override; + + audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const override; + + StreamTypeVector getStreamTypesForProductStrategy(product_strategy_t ps) const override; + + AttributesVector getAllAttributesForProductStrategy(product_strategy_t ps) const override; + + StrategyVector getOrderedProductStrategies() const override; + + void dump(String8 *dst) const override; + + + engineConfig::ParsingResult loadAudioPolicyEngineConfig(); + + const ProductStrategyMap &getProductStrategies() const { return mProductStrategies; } + + ProductStrategyMap &getProductStrategies() { return mProductStrategies; } + + product_strategy_t getProductStrategyForStream(audio_stream_type_t stream) const; + + product_strategy_t getProductStrategyByName(const std::string &name) const; + + AudioPolicyManagerObserver *getApmObserver() const { return mApmObserver; } + + inline bool isInCall() const + { + return is_state_in_call(getPhoneState()); + } + +private: + AudioPolicyManagerObserver *mApmObserver = nullptr; + + ProductStrategyMap mProductStrategies; + audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */ + + /** current forced use configuration. */ + audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {}; +}; + +} // namespace audio_policy +} // namespace android diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h new file mode 100644 index 0000000000..66ae86eb41 --- /dev/null +++ b/services/audiopolicy/engine/common/include/ProductStrategy.h @@ -0,0 +1,156 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/** + * @brief The ProductStrategy class describes for each product_strategy_t identifier the + * associated audio attributes, the device types to use, the device address to use. + * The identifier is voluntarily not strongly typed in order to be extensible by OEM. + */ +class ProductStrategy : public virtual RefBase, private HandleGenerator +{ +private: + struct AudioAttributes { + audio_stream_type_t mStream = AUDIO_STREAM_DEFAULT; + uint32_t mGroupId = 0; + audio_attributes_t mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + }; + + using AudioAttributesVector = std::vector; + +public: + ProductStrategy(const std::string &name); + + void addAttributes(const AudioAttributes &audioAttributes); + + std::string getName() const { return mName; } + AttributesVector getAudioAttributes() const; + product_strategy_t getId() const { return mId; } + StreamTypeVector getSupportedStreams() const; + + /** + * @brief matches checks if the given audio attributes shall follow the strategy. + * Order of the attributes within a strategy matters. + * If only the usage is available, the check is performed on the usages of the given + * attributes, otherwise all fields must match. + * @param attributes to consider + * @return true if attributes matches with the strategy, false otherwise. + */ + bool matches(const audio_attributes_t attributes) const; + + bool supportStreamType(const audio_stream_type_t &streamType) const; + + void setDeviceAddress(const std::string &address) + { + mDeviceAddress = address; + } + + std::string getDeviceAddress() const { return mDeviceAddress; } + + void setDeviceTypes(audio_devices_t devices) + { + mApplicableDevices = devices; + } + + audio_devices_t getDeviceTypes() const { return mApplicableDevices; } + + audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const; + audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const; + + bool isDefault() const; + + void dump(String8 *dst, int spaces = 0) const; + + /** + * @brief attributesMatches: checks if client attributes matches with a reference attributes + * "matching" means the usage shall match if reference attributes has a defined usage, AND + * content type shall match if reference attributes has a defined content type AND + * flags shall match if reference attributes has defined flags AND + * tags shall match if reference attributes has defined tags. + * Reference attributes "default" shall not be considered as a "true" case. This convention + * is used to identify the default strategy. + * @param refAttributes to be considered + * @param clientAttritubes to be considered + * @return true if matching, false otherwise + */ + static bool attributesMatches(const audio_attributes_t refAttributes, + const audio_attributes_t clientAttritubes); +private: + std::string mName; + + AudioAttributesVector mAttributesVector; + + product_strategy_t mId; + + std::string mDeviceAddress; /**< Device address applicable for this strategy, maybe empty */ + + /** + * Applicable device(s) type mask for this strategy. + */ + audio_devices_t mApplicableDevices = AUDIO_DEVICE_NONE; +}; + +class ProductStrategyMap : public std::map > +{ +public: + /** + * @brief getProductStrategyForAttribute. The order of the vector is dimensionning. + * @param attr + * @return applicable product strategy for the given attribute, default if none applicable. + */ + product_strategy_t getProductStrategyForAttributes(const audio_attributes_t &attr) const; + + product_strategy_t getProductStrategyForStream(audio_stream_type_t stream) const; + + audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const; + + audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const; + + /** + * @brief getAttributesForProductStrategy can be called from + * AudioManager: in this case, the product strategy IS the former routing strategy + * CarAudioManager: in this case, the product strategy IS the car usage + * [getAudioAttributesForCarUsage] + * OemExtension: in this case, the product strategy IS the Oem usage + * + * @param strategy + * @return audio attributes (or at least one of the attributes) following the given strategy. + */ + audio_attributes_t getAttributesForProductStrategy(product_strategy_t strategy) const; + + audio_devices_t getDeviceTypesForProductStrategy(product_strategy_t strategy) const; + + std::string getDeviceAddressForProductStrategy(product_strategy_t strategy) const; + + product_strategy_t getDefault() const; + + void dump(String8 *dst, int spaces = 0) const; +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp new file mode 100644 index 0000000000..0f4d5a5b45 --- /dev/null +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -0,0 +1,171 @@ +/* + * 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 "APM::AudioPolicyEngine/Base" +#define LOG_NDEBUG 0 + +#include "EngineBase.h" +#include "EngineDefaultConfig.h" +#include + +namespace android { +namespace audio_policy { + +void EngineBase::setObserver(AudioPolicyManagerObserver *observer) +{ + ALOG_ASSERT(observer != NULL, "Invalid Audio Policy Manager observer"); + mApmObserver = observer; +} + +status_t EngineBase::initCheck() +{ + return (mApmObserver != nullptr)? NO_ERROR : NO_INIT; +} + +status_t EngineBase::setPhoneState(audio_mode_t state) +{ + ALOGV("setPhoneState() state %d", state); + + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return BAD_VALUE; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return BAD_VALUE; + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + + if (!is_state_in_call(oldState) && is_state_in_call(state)) { + ALOGV(" Entering call in setPhoneState()"); + } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { + ALOGV(" Exiting call in setPhoneState()"); + } + return NO_ERROR; +} + +product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attributes_t &attr) const +{ + return mProductStrategies.getProductStrategyForAttributes(attr); +} + +audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const +{ + return mProductStrategies.getStreamTypeForAttributes(attr); +} + +audio_attributes_t EngineBase::getAttributesForStreamType(audio_stream_type_t stream) const +{ + return mProductStrategies.getAttributesForStreamType(stream); +} + +product_strategy_t EngineBase::getProductStrategyForStream(audio_stream_type_t stream) const +{ + return mProductStrategies.getProductStrategyForStream(stream); +} + +product_strategy_t EngineBase::getProductStrategyByName(const std::string &name) const +{ + for (const auto &iter : mProductStrategies) { + if (iter.second->getName() == name) { + return iter.second->getId(); + } + } + return PRODUCT_STRATEGY_NONE; +} + +engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() +{ + auto loadProductStrategies = + [](auto& strategyConfigs, auto& productStrategies) { + uint32_t groupid = 0; + for (auto& strategyConfig : strategyConfigs) { + sp strategy = new ProductStrategy(strategyConfig.name); + for (const auto &group : strategyConfig.attributesGroups) { + for (const auto &attr : group.attributesVect) { + strategy->addAttributes({group.stream, groupid, attr}); + } + groupid += 1; + } + product_strategy_t strategyId = strategy->getId(); + productStrategies[strategyId] = strategy; + } + }; + + auto result = engineConfig::parse(); + if (result.parsedConfig == nullptr) { + ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__); + result = {std::make_unique(gDefaultEngineConfig), 0}; + } + ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); + loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); + return result; +} + +StrategyVector EngineBase::getOrderedProductStrategies() const +{ + auto findByFlag = [](const auto &productStrategies, auto flag) { + return std::find_if(begin(productStrategies), end(productStrategies), + [&](const auto &strategy) { + for (const auto &attributes : strategy.second->getAudioAttributes()) { + if ((attributes.flags & flag) == flag) { + return true; + } + } + return false; + }); + }; + auto strategies = mProductStrategies; + auto enforcedAudibleStrategyIter = findByFlag(strategies, AUDIO_FLAG_AUDIBILITY_ENFORCED); + + if (getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED && + enforcedAudibleStrategyIter != strategies.end()) { + auto enforcedAudibleStrategy = *enforcedAudibleStrategyIter; + strategies.erase(enforcedAudibleStrategyIter); + strategies.insert(begin(strategies), enforcedAudibleStrategy); + } + StrategyVector orderedStrategies; + for (const auto &iter : strategies) { + orderedStrategies.push_back(iter.second->getId()); + } + return orderedStrategies; +} + +StreamTypeVector EngineBase::getStreamTypesForProductStrategy(product_strategy_t ps) const +{ + // @TODO default music stream to control volume if no group? + return (mProductStrategies.find(ps) != end(mProductStrategies)) ? + mProductStrategies.at(ps)->getSupportedStreams() : + StreamTypeVector(AUDIO_STREAM_MUSIC); +} + +AttributesVector EngineBase::getAllAttributesForProductStrategy(product_strategy_t ps) const +{ + return (mProductStrategies.find(ps) != end(mProductStrategies)) ? + mProductStrategies.at(ps)->getAudioAttributes() : AttributesVector(); +} + +void EngineBase::dump(String8 *dst) const +{ + mProductStrategies.dump(dst, 2); +} + +} // namespace audio_policy +} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h new file mode 100644 index 0000000000..5f546b3433 --- /dev/null +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace android { +/** + * @brief AudioProductStrategies hard coded array of strategies to fill new engine API contract. + */ +const engineConfig::ProductStrategies gOrderedStrategies = { + {"STRATEGY_PHONE", + { + {"phone", AUDIO_STREAM_VOICE_CALL, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT, 0, + ""}}, + }, + {"sco", AUDIO_STREAM_BLUETOOTH_SCO, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO, + ""}}, + } + }, + }, + {"STRATEGY_SONIFICATION", + { + {"ring", AUDIO_STREAM_RING, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE, + AUDIO_SOURCE_DEFAULT, 0, ""}} + }, + {"alarm", AUDIO_STREAM_ALARM, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, 0, ""}}, + } + }, + }, + {"STRATEGY_ENFORCED_AUDIBLE", + { + {"", AUDIO_STREAM_ENFORCED_AUDIBLE, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, + AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}} + } + }, + }, + {"STRATEGY_ACCESSIBILITY", + { + {"", AUDIO_STREAM_ACCESSIBILITY, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, + AUDIO_SOURCE_DEFAULT, 0, ""}} + } + }, + }, + {"STRATEGY_SONIFICATION_RESPECTFUL", + { + {"", AUDIO_STREAM_NOTIFICATION, + { + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST, + AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT, + AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED, + AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_EVENT, + AUDIO_SOURCE_DEFAULT, 0, ""} + } + } + }, + }, + {"STRATEGY_MEDIA", + { + {"music", AUDIO_STREAM_MUSIC, + { + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_GAME, AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANT, AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, + AUDIO_SOURCE_DEFAULT, 0, ""}, + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""} + }, + }, + {"system", AUDIO_STREAM_SYSTEM, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION, + AUDIO_SOURCE_DEFAULT, 0, ""}} + } + }, + }, + {"STRATEGY_DTMF", + { + {"", AUDIO_STREAM_DTMF, + { + {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, + AUDIO_SOURCE_DEFAULT, 0, ""} + } + } + }, + }, + {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER", + { + {"", AUDIO_STREAM_TTS, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, + AUDIO_FLAG_BEACON, ""}} + } + }, + }, + {"STRATEGY_REROUTING", + { + {"", AUDIO_STREAM_REROUTING, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}} + } + }, + }, + {"STRATEGY_PATCH", + { + {"", AUDIO_STREAM_PATCH, + {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}} + } + }, + } +}; + +const engineConfig::Config gDefaultEngineConfig = { + 1.0, + gOrderedStrategies +}; +} // namespace android diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp new file mode 100644 index 0000000000..a3edb3954f --- /dev/null +++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp @@ -0,0 +1,257 @@ +/* + * 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 "APM::AudioPolicyEngine/ProductStrategy" +//#define LOG_NDEBUG 0 + +#include "ProductStrategy.h" + +#include +#include +#include +#include + +#include + + +namespace android { + +ProductStrategy::ProductStrategy(const std::string &name) : + mName(name), + mId(static_cast(HandleGenerator::getNextHandle())) +{ +} + +void ProductStrategy::addAttributes(const AudioAttributes &audioAttributes) +{ + mAttributesVector.push_back(audioAttributes); +} + +AttributesVector ProductStrategy::getAudioAttributes() const +{ + AttributesVector attrVector; + for (const auto &attrGroup : mAttributesVector) { + attrVector.push_back(attrGroup.mAttributes); + } + if (not attrVector.empty()) { + return attrVector; + } + return { AUDIO_ATTRIBUTES_INITIALIZER }; +} + +// @todo: all flags required to match? +// all tags required to match? +/* static */ +bool ProductStrategy::attributesMatches(const audio_attributes_t refAttributes, + const audio_attributes_t clientAttritubes) +{ + if (refAttributes == defaultAttr) { + // The default product strategy is the strategy that holds default attributes by convention. + // All attributes that fail to match will follow the default strategy for routing. + // Choosing the default must be done as a fallback, the attributes match shall not + // selects the default. + return false; + } + return ((refAttributes.usage == AUDIO_USAGE_UNKNOWN) || + (clientAttritubes.usage == refAttributes.usage)) && + ((refAttributes.content_type == AUDIO_CONTENT_TYPE_UNKNOWN) || + (clientAttritubes.content_type == refAttributes.content_type)) && + ((refAttributes.flags == AUDIO_OUTPUT_FLAG_NONE) || + (clientAttritubes.flags != AUDIO_OUTPUT_FLAG_NONE && + (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) && + ((strlen(refAttributes.tags) == 0) || + (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0)); +} + +bool ProductStrategy::matches(const audio_attributes_t attr) const +{ + return std::find_if(begin(mAttributesVector), end(mAttributesVector), + [&attr](const auto &supportedAttr) { + return attributesMatches(supportedAttr.mAttributes, attr); }) != end(mAttributesVector); +} + +audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(const audio_attributes_t &attr) const +{ + const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector), + [&attr](const auto &supportedAttr) { + return attributesMatches(supportedAttr.mAttributes, attr); }); + return iter != end(mAttributesVector) ? iter->mStream : AUDIO_STREAM_DEFAULT; +} + +audio_attributes_t ProductStrategy::getAttributesForStreamType(audio_stream_type_t streamType) const +{ + const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector), + [&streamType](const auto &supportedAttr) { + return supportedAttr.mStream == streamType; }); + return iter != end(mAttributesVector) ? iter->mAttributes : AUDIO_ATTRIBUTES_INITIALIZER; +} + +bool ProductStrategy::isDefault() const +{ + return std::find_if(begin(mAttributesVector), end(mAttributesVector), [](const auto &attr) { + return attr.mAttributes == defaultAttr; }) != end(mAttributesVector); +} + +StreamTypeVector ProductStrategy::getSupportedStreams() const +{ + StreamTypeVector streams; + for (const auto &supportedAttr : mAttributesVector) { + if (std::find(begin(streams), end(streams), supportedAttr.mStream) == end(streams) && + supportedAttr.mStream != AUDIO_STREAM_DEFAULT) { + streams.push_back(supportedAttr.mStream); + } + } + return streams; +} + +bool ProductStrategy::supportStreamType(const audio_stream_type_t &streamType) const +{ + return std::find_if(begin(mAttributesVector), end(mAttributesVector), + [&streamType](const auto &supportedAttr) { + return supportedAttr.mStream == streamType; }) != end(mAttributesVector); +} + +void ProductStrategy::dump(String8 *dst, int spaces) const +{ + dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId); + std::string deviceLiteral; + if (!OutputDeviceConverter::toString(mApplicableDevices, deviceLiteral)) { + ALOGE("%s: failed to convert device %d", __FUNCTION__, mApplicableDevices); + } + dst->appendFormat("%*sSelected Device: {type:%s, @:%s}\n", spaces + 2, "", + deviceLiteral.c_str(), mDeviceAddress.c_str()); + + for (const auto &attr : mAttributesVector) { + dst->appendFormat("%*sGroup: %d stream: %s\n", spaces + 3, "", attr.mGroupId, + android::toString(attr.mStream).c_str()); + dst->appendFormat("%*s Attributes: ", spaces + 3, ""); + std::string attStr = + attr.mAttributes == defaultAttr ? "{ Any }" : android::toString(attr.mAttributes); + dst->appendFormat("%s\n", attStr.c_str()); + } +} + +product_strategy_t ProductStrategyMap::getProductStrategyForAttributes( + const audio_attributes_t &attr) const +{ + for (const auto &iter : *this) { + if (iter.second->matches(attr)) { + return iter.second->getId(); + } + } + ALOGV("%s: No matching product strategy for attributes %s, return default", __FUNCTION__, + toString(attr).c_str()); + return getDefault(); +} + +audio_attributes_t ProductStrategyMap::getAttributesForStreamType(audio_stream_type_t stream) const +{ + for (const auto &iter : *this) { + const auto strategy = iter.second; + if (strategy->supportStreamType(stream)) { + return strategy->getAttributesForStreamType(stream); + } + } + ALOGV("%s: No product strategy for stream %s, using default", __FUNCTION__, + toString(stream).c_str()); + return {}; +} + +audio_stream_type_t ProductStrategyMap::getStreamTypeForAttributes( + const audio_attributes_t &attr) const +{ + for (const auto &iter : *this) { + audio_stream_type_t stream = iter.second->getStreamTypeForAttributes(attr); + if (stream != AUDIO_STREAM_DEFAULT) { + return stream; + } + } + ALOGV("%s: No product strategy for attributes %s, using default (aka MUSIC)", __FUNCTION__, + toString(attr).c_str()); + return AUDIO_STREAM_MUSIC; +} + +product_strategy_t ProductStrategyMap::getDefault() const +{ + for (const auto &iter : *this) { + if (iter.second->isDefault()) { + ALOGV("%s: using default %s", __FUNCTION__, iter.second->getName().c_str()); + return iter.second->getId(); + } + } + ALOGE("%s: No default product strategy defined", __FUNCTION__); + return PRODUCT_STRATEGY_NONE; +} + +audio_attributes_t ProductStrategyMap::getAttributesForProductStrategy( + product_strategy_t strategy) const +{ + if (find(strategy) == end()) { + ALOGE("Invalid %d strategy requested", strategy); + return AUDIO_ATTRIBUTES_INITIALIZER; + } + return at(strategy)->getAudioAttributes()[0]; +} + +product_strategy_t ProductStrategyMap::getProductStrategyForStream(audio_stream_type_t stream) const +{ + for (const auto &iter : *this) { + if (iter.second->supportStreamType(stream)) { + return iter.second->getId(); + } + } + ALOGV("%s: No product strategy for stream %d, using default", __FUNCTION__, stream); + return getDefault(); +} + + +audio_devices_t ProductStrategyMap::getDeviceTypesForProductStrategy( + product_strategy_t strategy) const +{ + if (find(strategy) == end()) { + ALOGE("Invalid %d strategy requested, returning device for default strategy", strategy); + product_strategy_t defaultStrategy = getDefault(); + if (defaultStrategy == PRODUCT_STRATEGY_NONE) { + return AUDIO_DEVICE_NONE; + } + return at(getDefault())->getDeviceTypes(); + } + return at(strategy)->getDeviceTypes(); +} + +std::string ProductStrategyMap::getDeviceAddressForProductStrategy(product_strategy_t psId) const +{ + if (find(psId) == end()) { + ALOGE("Invalid %d strategy requested, returning device for default strategy", psId); + product_strategy_t defaultStrategy = getDefault(); + if (defaultStrategy == PRODUCT_STRATEGY_NONE) { + return {}; + } + return at(getDefault())->getDeviceAddress(); + } + return at(psId)->getDeviceAddress(); +} + +void ProductStrategyMap::dump(String8 *dst, int spaces) const +{ + dst->appendFormat("%*sProduct Strategies dump:", spaces, ""); + for (const auto &iter : *this) { + iter.second->dump(dst, spaces + 2); + } +} + +} + diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk new file mode 100644 index 0000000000..dbcd1bf3fb --- /dev/null +++ b/services/audiopolicy/engine/config/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH := $(call my-dir) + +################################################################## +# Component build +################################################################## + +include $(CLEAR_VARS) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +LOCAL_C_INCLUDES := \ + $(LOCAL_EXPORT_C_INCLUDE_DIRS) \ + external/libxml2/include \ + external/icu/icu4c/source/common + +LOCAL_SRC_FILES := \ + src/EngineConfig.cpp + +LOCAL_CFLAGS += -Wall -Werror -Wextra + +LOCAL_SHARED_LIBRARIES := \ + libparameter \ + libmedia_helper \ + libandroidicu \ + libxml2 \ + libutils \ + liblog + +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents + +LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) + +LOCAL_MODULE := libaudiopolicyengineconfig +LOCAL_MODULE_TAGS := optional + +LOCAL_HEADER_LIBRARIES := \ + libaudio_system_headers \ + libaudiopolicycommon + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h new file mode 100644 index 0000000000..64b95261e8 --- /dev/null +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include +#include +#include + +struct _xmlNode; +struct _xmlDoc; + +namespace android { +namespace engineConfig { + +/** Default path of audio policy usages configuration file. */ +constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml"; + +/** Directories where the effect libraries will be search for. */ +constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"}; + +using AttributesVector = std::vector; +using StreamVector = std::vector; + +struct AttributesGroup { + std::string name; + audio_stream_type_t stream; + AttributesVector attributesVect; +}; + +using AttributesGroups = std::vector; + +struct ProductStrategy { + std::string name; + AttributesGroups attributesGroups; +}; + +using ProductStrategies = std::vector; + +struct Config { + float version; + ProductStrategies productStrategies; +}; + +/** Result of `parse(const char*)` */ +struct ParsingResult { + /** Parsed config, nullptr if the xml lib could not load the file */ + std::unique_ptr parsedConfig; + size_t nbSkippedElement; //< Number of skipped invalid product strategies +}; + +/** Parses the provided audio policy usage configuration. + * @return audio policy usage @see Config + */ +ParsingResult parse(const char* path = DEFAULT_PATH); + +} // namespace engineConfig +} // namespace android diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp new file mode 100644 index 0000000000..26dde663ad --- /dev/null +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -0,0 +1,307 @@ +/* + * 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 "APM::AudioPolicyEngine/Config" +//#define LOG_NDEBUG 0 + +#include "EngineConfig.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace android { + +using utilities::convertTo; + +namespace engineConfig { + +static constexpr const char *gVersionAttribute = "version"; +static const char *const gReferenceElementName = "reference"; +static const char *const gReferenceAttributeName = "name"; + +template +struct BaseSerializerTraits { + typedef E Element; + typedef C Collection; + typedef void* PtrSerializingCtx; +}; + +struct AttributesGroupTraits : public BaseSerializerTraits { + static constexpr const char *tag = "AttributesGroup"; + static constexpr const char *collectionTag = "AttributesGroups"; + + struct Attributes { + static constexpr const char *name = "name"; + static constexpr const char *streamType = "streamType"; + }; + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps); +}; + +struct ProductStrategyTraits : public BaseSerializerTraits { + static constexpr const char *tag = "ProductStrategy"; + static constexpr const char *collectionTag = "ProductStrategies"; + + struct Attributes { + static constexpr const char *name = "name"; + }; + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps); +}; + +using xmlCharUnique = std::unique_ptr; + +std::string getXmlAttribute(const xmlNode *cur, const char *attribute) +{ + xmlCharUnique charPtr(xmlGetProp(cur, reinterpret_cast(attribute)), xmlFree); + if (charPtr == NULL) { + return ""; + } + std::string value(reinterpret_cast(charPtr.get())); + return value; +} + +static void getReference(const _xmlNode *root, const _xmlNode *&refNode, const std::string &refName, + const char *collectionTag) +{ + for (root = root->xmlChildrenNode; root != NULL; root = root->next) { + if (!xmlStrcmp(root->name, (const xmlChar *)collectionTag)) { + for (xmlNode *cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)gReferenceElementName))) { + std::string name = getXmlAttribute(cur, gReferenceAttributeName); + if (refName == name) { + refNode = cur; + return; + } + } + } + } + } + return; +} + +template +static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur, + typename Trait::Collection &collection, + size_t &nbSkippedElement) +{ + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (xmlStrcmp(cur->name, (const xmlChar *)Trait::collectionTag) && + xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) { + continue; + } + const xmlNode *child = cur; + if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) { + child = child->xmlChildrenNode; + } + for (; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) { + status_t status = Trait::deserialize(doc, child, collection); + if (status != NO_ERROR) { + nbSkippedElement += 1; + } + } + } + if (!xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) { + return NO_ERROR; + } + } + return NO_ERROR; +} + +static constexpr const char *attributesAttributeRef = "attributesRef"; /**< for factorization. */ + +static status_t parseAttributes(const _xmlNode *cur, audio_attributes_t &attributes) +{ + for (; cur != NULL; cur = cur->next) { + if (!xmlStrcmp(cur->name, (const xmlChar *)("ContentType"))) { + std::string contentTypeXml = getXmlAttribute(cur, "value"); + audio_content_type_t contentType; + if (not AudioContentTypeConverter::fromString(contentTypeXml.c_str(), contentType)) { + ALOGE("Invalid content type %s", contentTypeXml.c_str()); + return BAD_VALUE; + } + attributes.content_type = contentType; + ALOGV("%s content type %s", __FUNCTION__, contentTypeXml.c_str()); + } + if (!xmlStrcmp(cur->name, (const xmlChar *)("Usage"))) { + std::string usageXml = getXmlAttribute(cur, "value"); + audio_usage_t usage; + if (not UsageTypeConverter::fromString(usageXml.c_str(), usage)) { + ALOGE("Invalid usage %s", usageXml.c_str()); + return BAD_VALUE; + } + attributes.usage = usage; + ALOGV("%s usage %s", __FUNCTION__, usageXml.c_str()); + } + if (!xmlStrcmp(cur->name, (const xmlChar *)("Flags"))) { + std::string flags = getXmlAttribute(cur, "value"); + + ALOGV("%s flags %s", __FUNCTION__, flags.c_str()); + attributes.flags = AudioFlagConverter::maskFromString(flags, " "); + } + if (!xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) { + std::string bundleKey = getXmlAttribute(cur, "key"); + std::string bundleValue = getXmlAttribute(cur, "value"); + + ALOGV("%s Bundle %s %s", __FUNCTION__, bundleKey.c_str(), bundleValue.c_str()); + + std::string tags(bundleKey + "=" + bundleValue); + std::strncpy(attributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); + } + } + return NO_ERROR; +} + +static status_t deserializeAttributes(_xmlDoc *doc, const _xmlNode *cur, + audio_attributes_t &attributes) { + // Retrieve content type, usage, flags, and bundle from xml + for (; cur != NULL; cur = cur->next) { + if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) { + const xmlNode *attrNode = cur; + std::string attrRef = getXmlAttribute(cur, attributesAttributeRef); + if (!attrRef.empty()) { + getReference(xmlDocGetRootElement(doc), attrNode, attrRef, attributesAttributeRef); + if (attrNode == NULL) { + ALOGE("%s: No reference found for %s", __FUNCTION__, attrRef.c_str()); + return BAD_VALUE; + } + return deserializeAttributes(doc, attrNode->xmlChildrenNode, attributes); + } + return parseAttributes(attrNode->xmlChildrenNode, attributes); + } + if (not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) { + return parseAttributes(cur, attributes); + } + } + return BAD_VALUE; +} + +static status_t deserializeAttributesCollection(_xmlDoc *doc, const _xmlNode *cur, + AttributesVector &collection) +{ + status_t ret = BAD_VALUE; + // Either we do provide only one attributes or a collection of supported attributes + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes")) || + not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) || + not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) { + audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER; + ret = deserializeAttributes(doc, cur, attributes); + if (ret == NO_ERROR) { + collection.push_back(attributes); + // We are done if the "Attributes" balise is omitted, only one Attributes is allowed + if (xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) { + return ret; + } + } + } + } + return ret; +} + +status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, + Collection &attributesGroup) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name); + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); + + audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; + std::string streamTypeXml = getXmlAttribute(child, Attributes::streamType); + if (streamTypeXml.empty()) { + ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::streamType); + } else { + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::streamType, streamTypeXml.c_str()); + if (not StreamTypeConverter::fromString(streamTypeXml.c_str(), streamType)) { + ALOGE("Invalid stream type %s", streamTypeXml.c_str()); + return BAD_VALUE; + } + } + AttributesVector attributesVect; + deserializeAttributesCollection(doc, child, attributesVect); + + attributesGroup.push_back({name, streamType, attributesVect}); + return NO_ERROR; +} + +status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, + Collection &strategies) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGE("ProductStrategyTraits No attribute %s found", Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); + + size_t skipped = 0; + AttributesGroups attrGroups; + deserializeCollection(doc, child, attrGroups, skipped); + + strategies.push_back({name, attrGroups}); + return NO_ERROR; +} + +ParsingResult parse(const char* path) { + xmlDocPtr doc; + doc = xmlParseFile(path); + if (doc == NULL) { + ALOGE("%s: Could not parse document %s", __FUNCTION__, path); + return {nullptr, 0}; + } + xmlNodePtr cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); + xmlFreeDoc(doc); + return {nullptr, 0}; + } + if (xmlXIncludeProcess(doc) < 0) { + ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); + return {nullptr, 0}; + } + std::string version = getXmlAttribute(cur, gVersionAttribute); + if (version.empty()) { + ALOGE("%s: No version found", __func__); + return {nullptr, 0}; + } + size_t nbSkippedElements = 0; + auto config = std::make_unique(); + config->version = std::stof(version); + deserializeCollection( + doc, cur, config->productStrategies, nbSkippedElements); + + return {std::move(config), nbSkippedElements}; +} + +} // namespace engineConfig +} // namespace android diff --git a/services/audiopolicy/engine/interface/Android.bp b/services/audiopolicy/engine/interface/Android.bp new file mode 100644 index 0000000000..2ea42b698f --- /dev/null +++ b/services/audiopolicy/engine/interface/Android.bp @@ -0,0 +1,19 @@ +// 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. + +cc_library_headers { + name: "libaudiopolicyengine_interface_headers", + host_supported: true, + export_include_dirs: ["."], +} diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 04594f5dfc..9f5fb0c2e3 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -28,6 +30,10 @@ namespace android { +using DeviceStrategyMap = std::map; +using StrategyVector = std::vector; + + /** * This interface is dedicated to the policy manager that a Policy Engine shall implement. */ @@ -55,6 +61,8 @@ public: * @param[in] inputSource to get the selected input device associated to * * @return selected input device for the given input source, may be none if error. + * + * @deprecated use getInputDeviceForAttributes */ virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const = 0; @@ -64,6 +72,8 @@ public: * @param[in] stream type for which the selected ouput device is requested. * * @return selected ouput device for the given strategy, may be none if error. + * + * @deprecated use getOutputDeviceForAttributes */ virtual audio_devices_t getDeviceForStrategy(routing_strategy stategy) const = 0; @@ -73,6 +83,9 @@ public: * @param[in] stream: for which the selected strategy followed by is requested. * * @return strategy to be followed. + * + * @deprecated use getOrderedStreams() / getLinkedStreams() to apply operation on stream + * following same former routing_strategy */ virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) = 0; @@ -82,6 +95,8 @@ public: * @param[in] usage to get the selected strategy followed by. * * @return strategy to be followed. + * + * @deprecated use getProductStrategyForAttributes */ virtual routing_strategy getStrategyForUsage(audio_usage_t usage) = 0; @@ -133,6 +148,130 @@ public: virtual status_t setDeviceConnectionState(const android::sp devDesc, audio_policy_dev_state_t state) = 0; + /** + * Get the strategy selected for a given audio attributes. + * + * @param[in] audio attributes to get the selected @product_strategy_t followed by. + * + * @return @product_strategy_t to be followed. + */ + virtual product_strategy_t getProductStrategyForAttributes( + const audio_attributes_t &attr) const = 0; + + /** + * @brief getOutputDevicesForAttributes retrieves the devices to be used for given + * audio attributes. + * @param attributes of the output requesting Device(s) selection + * @param preferedDevice valid reference if a prefered device is requested, nullptr otherwise. + * @param fromCache if true, the device is returned from internal cache, + * otherwise it is determined by current state (device connected,phone state, + * force use, a2dp output...) + * @return vector of selected device descriptors. + * Appropriate device for streams handled by the specified audio attributes according + * to current phone state, forced states, connected devices... + * if fromCache is true, the device is returned from internal cache, + * otherwise it is determined by current state (device connected,phone state, force use, + * a2dp output...) + * This allows to: + * 1 speed up process when the state is stable (when starting or stopping an output) + * 2 access to either current device selection (fromCache == true) or + * "future" device selection (fromCache == false) when called from a context + * where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + * before manager updates its outputs. + */ + virtual DeviceVector getOutputDevicesForAttributes( + const audio_attributes_t &attributes, + const sp &preferedDevice = nullptr, + bool fromCache = false) const = 0; + + /** + * @brief getOutputDevicesForStream Legacy function retrieving devices from a stream type. + * @param stream type of the output requesting Device(s) selection + * @param fromCache if true, the device is returned from internal cache, + * otherwise it is determined by current state (device connected,phone state, + * force use, a2dp output...) + * @return appropriate device for streams handled by the specified audio attributes according + * to current phone state, forced states, connected devices... + * if fromCache is true, the device is returned from internal cache, + * otherwise it is determined by current state (device connected,phone state, force use, + * a2dp output...) + * This allows to: + * 1 speed up process when the state is stable (when starting or stopping an output) + * 2 access to either current device selection (fromCache == true) or + * "future" device selection (fromCache == false) when called from a context + * where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + * before manager updates its outputs. + */ + virtual DeviceVector getOutputDevicesForStream(audio_stream_type_t stream, + bool fromCache = false) const = 0; + + /** + * Get the input device selected for given audio attributes. + * + * @param[in] attr audio attributes to consider + * @param[out] mix to be used if a mix has been installed for the given audio attributes. + * @return selected input device for the audio attributes, may be null if error. + */ + virtual sp getInputDeviceForAttributes( + const audio_attributes_t &attr, AudioMix **mix = nullptr) const = 0; + + /** + * Get the legacy stream type for a given audio attributes. + * + * @param[in] audio attributes to get the associated audio_stream_type_t. + * + * @return audio_stream_type_t associated to the attributes. + */ + virtual audio_stream_type_t getStreamTypeForAttributes( + const audio_attributes_t &attr) const = 0; + + /** + * @brief getAttributesForStream get the audio attributes from legacy stream type + * @param stream to consider + * @return audio attributes matching the legacy stream type + */ + virtual audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const = 0; + + /** + * @brief getStreamTypesForProductStrategy retrieves the list of legacy stream type following + * the given product strategy + * @param ps product strategy to consider + * @return associated legacy Stream Types vector of the given product strategy + */ + virtual StreamTypeVector getStreamTypesForProductStrategy(product_strategy_t ps) const = 0; + + /** + * @brief getAllAttributesForProductStrategy retrieves all the attributes following the given + * product strategy. Any attributes that "matches" with this one will follow the product + * strategy. + * "matching" means the usage shall match if reference attributes has a defined usage, AND + * content type shall match if reference attributes has a defined content type AND + * flags shall match if reference attributes has defined flags AND + * tags shall match if reference attributes has defined tags. + * @param ps product strategy to consider + * @return vector of product strategy ids, empty if unknown strategy. + */ + virtual AttributesVector getAllAttributesForProductStrategy(product_strategy_t ps) const = 0; + + /** + * @brief getOrderedAudioProductStrategies + * @return priority ordered product strategies to help the AudioPolicyManager evaluating the + * device selection per output according to the prioritized strategies. + */ + virtual StrategyVector getOrderedProductStrategies() const = 0; + + /** + * @brief updateDeviceSelectionCache. Device selection for AudioAttribute / Streams is cached + * in the engine in order to speed up process when the audio system is stable. + * When a device is connected, the android mode is changed, engine is notified and can update + * the cache. + * When starting / stopping an output with a stream that can affect notification, the engine + * needs to update the cache upon this function call. + */ + virtual void updateDeviceSelectionCache() = 0; + + virtual void dump(String8 *dst) const = 0; + protected: virtual ~AudioPolicyManagerInterface() {} }; diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index c2105e936b..df813cb068 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -14,11 +14,12 @@ LOCAL_SRC_FILES := \ src/Strategy.cpp \ src/Usage.cpp \ src/InputSource.cpp \ + ../engine/common/src/ProductStrategy.cpp \ + ../engine/common/src/EngineBase.cpp audio_policy_engine_includes_common := \ frameworks/av/services/audiopolicy/engineconfigurable/include \ - frameworks/av/services/audiopolicy/engineconfigurable/interface \ - frameworks/av/services/audiopolicy/engine/interface + frameworks/av/services/audiopolicy/engineconfigurable/interface LOCAL_CFLAGS += \ -Wall \ @@ -32,8 +33,12 @@ LOCAL_C_INCLUDES := \ $(audio_policy_engine_includes_common) \ $(TARGET_OUT_HEADERS)/hw \ $(call include-path-for, frameworks-av) \ - $(call include-path-for, audio-utils) \ - frameworks/av/services/audiopolicy/common/include + $(call include-path-for, audio-utils) + +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon \ + libaudiopolicyengine_common_headers \ + libaudiopolicyengine_interface_headers LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) @@ -45,8 +50,8 @@ LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents LOCAL_SHARED_LIBRARIES := \ + libaudiopolicyengineconfig \ liblog \ - libcutils \ libutils \ liblog \ libaudioutils \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index db1f0381c2..a37db1ee74 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -21,7 +21,6 @@ LOCAL_CFLAGS += \ -fvisibility=hidden LOCAL_C_INCLUDES := \ - frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engineconfigurable/include \ frameworks/av/services/audiopolicy/engineconfigurable/interface \ @@ -31,6 +30,9 @@ LOCAL_SHARED_LIBRARIES := \ libmedia_helper \ liblog \ +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) LOCAL_STATIC_LIBRARIES := libpfw_utility diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 009cf9004b..815de25330 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -29,6 +29,8 @@ #include "Stream.h" #include "InputSource.h" #include "Usage.h" + +#include #include #include @@ -80,12 +82,12 @@ const InputSourceCollection &Engine::getCollection() const return mInputSourceCollection; } -Engine::Engine() - : mManagerInterface(this), - mPluginInterface(this), - mPolicyParameterMgr(new ParameterManagerWrapper()), - mApmObserver(NULL) +Engine::Engine() : mPolicyParameterMgr(new ParameterManagerWrapper()) { + status_t loadResult = loadAudioPolicyEngineConfig(); + if (loadResult < 0) { + ALOGE("Policy Engine configuration is invalid."); + } } Engine::~Engine() @@ -96,20 +98,13 @@ Engine::~Engine() mUsageCollection.clear(); } - -void Engine::setObserver(AudioPolicyManagerObserver *observer) -{ - ALOG_ASSERT(observer != NULL, "Invalid Audio Policy Manager observer"); - mApmObserver = observer; -} - status_t Engine::initCheck() { - if (mPolicyParameterMgr == NULL || mPolicyParameterMgr->start() != NO_ERROR) { + if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start() != NO_ERROR) { ALOGE("%s: could not start Policy PFW", __FUNCTION__); return NO_INIT; } - return (mApmObserver != NULL)? NO_ERROR : NO_INIT; + return EngineBase::initCheck(); } template @@ -137,14 +132,14 @@ Property Engine::getPropertyForKey(Key key) const return element->template get(); } -routing_strategy Engine::ManagerInterfaceImpl::getStrategyForUsage(audio_usage_t usage) +routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) { - return mPolicyEngine->getPropertyForKey(usage); + return getPropertyForKey(usage); } -audio_devices_t Engine::ManagerInterfaceImpl::getDeviceForStrategy(routing_strategy strategy) const +audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const { - const SwAudioOutputCollection &outputs = mPolicyEngine->mApmObserver->getOutputs(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); /** This is the only case handled programmatically because the PFW is unable to know the * activity of streams. @@ -161,31 +156,28 @@ audio_devices_t Engine::ManagerInterfaceImpl::getDeviceForStrategy(routing_strat !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && outputs.isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { - return mPolicyEngine->getPropertyForKey(STRATEGY_MEDIA); + return getPropertyForKey(STRATEGY_MEDIA); } if (strategy == STRATEGY_ACCESSIBILITY && (outputs.isStreamActive(AUDIO_STREAM_RING) || outputs.isStreamActive(AUDIO_STREAM_ALARM))) { // do not route accessibility prompts to a digital output currently configured with a // compressed format as they would likely not be mixed and dropped. // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable. - return mPolicyEngine->getPropertyForKey( - STRATEGY_SONIFICATION); + return getPropertyForKey(STRATEGY_SONIFICATION); } - return mPolicyEngine->getPropertyForKey(strategy); + return getPropertyForKey(strategy); } -bool Engine::PluginInterfaceImpl::setVolumeProfileForStream(const audio_stream_type_t &stream, - const audio_stream_type_t &profile) +bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream, + const audio_stream_type_t &profile) { - if (mPolicyEngine->setPropertyForKey(stream, - profile)) { - mPolicyEngine->mApmObserver->getVolumeCurves().switchVolumeCurve(profile, stream); + if (setPropertyForKey(stream, profile)) { + getApmObserver()->getVolumeCurves().switchVolumeCurve(profile, stream); return true; } return false; } - template bool Engine::setPropertyForKey(const Property &property, const Key &key) { @@ -199,7 +191,11 @@ bool Engine::setPropertyForKey(const Property &property, const Key &key) status_t Engine::setPhoneState(audio_mode_t mode) { - return mPolicyParameterMgr->setPhoneState(mode); + status_t status = mPolicyParameterMgr->setPhoneState(mode); + if (status != NO_ERROR) { + return status; + } + return EngineBase::setPhoneState(mode); } audio_mode_t Engine::getPhoneState() const @@ -210,7 +206,11 @@ audio_mode_t Engine::getPhoneState() const status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { - return mPolicyParameterMgr->setForceUse(usage, config); + status_t status = mPolicyParameterMgr->setForceUse(usage, config); + if (status != NO_ERROR) { + return status; + } + return EngineBase::setForceUse(usage, config); } audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) const @@ -225,24 +225,144 @@ status_t Engine::setDeviceConnectionState(const sp devDesc, if (audio_is_output_device(devDesc->type())) { return mPolicyParameterMgr->setAvailableOutputDevices( - mApmObserver->getAvailableOutputDevices().types()); + getApmObserver()->getAvailableOutputDevices().types()); } else if (audio_is_input_device(devDesc->type())) { return mPolicyParameterMgr->setAvailableInputDevices( - mApmObserver->getAvailableInputDevices().types()); + getApmObserver()->getAvailableInputDevices().types()); } return BAD_TYPE; } +status_t Engine::loadAudioPolicyEngineConfig() +{ + auto result = EngineBase::loadAudioPolicyEngineConfig(); + + return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE; +} + +DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const +{ + const auto productStrategies = getProductStrategies(); + if (productStrategies.find(ps) == productStrategies.end()) { + ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps); + return {}; + } + const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); + uint32_t availableOutputDevicesType = availableOutputDevices.types(); + + /** This is the only case handled programmatically because the PFW is unable to know the + * activity of streams. + * + * -While media is playing on a remote device, use the the sonification behavior. + * Note that we test this usecase before testing if media is playing because + * the isStreamActive() method only informs about the activity of a stream, not + * if it's for local playback. Note also that we use the same delay between both tests + * + * -When media is not playing anymore, fall back on the sonification behavior + */ + audio_devices_t devices = AUDIO_DEVICE_NONE; + if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) && + !is_state_in_call(getPhoneState()) && + !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && + outputs.isStreamActive(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + product_strategy_t strategyForMedia = + getProductStrategyForStream(AUDIO_STREAM_MUSIC); + devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia); + } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) && + (outputs.isStreamActive(AUDIO_STREAM_RING) || + outputs.isStreamActive(AUDIO_STREAM_ALARM))) { + // do not route accessibility prompts to a digital output currently configured with a + // compressed format as they would likely not be mixed and dropped. + // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable. + product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING); + devices = productStrategies.getDeviceTypesForProductStrategy(strategyNotification); + } else { + devices = productStrategies.getDeviceTypesForProductStrategy(ps); + } + if (devices == AUDIO_DEVICE_NONE || + (devices & availableOutputDevicesType) == AUDIO_DEVICE_NONE) { + devices = getApmObserver()->getDefaultOutputDevice()->type(); + ALOGE_IF(devices == AUDIO_DEVICE_NONE, "%s: no valid default device defined", __FUNCTION__); + return DeviceVector(getApmObserver()->getDefaultOutputDevice()); + } + if (/*device_distinguishes_on_address(devices)*/ devices == AUDIO_DEVICE_OUT_BUS) { + // We do expect only one device for these types of devices + // Criterion device address garantee this one is available + // If this criterion is not wished, need to ensure this device is available + const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str()); + ALOGV("%s:device 0x%x %s %d", __FUNCTION__, devices, address.c_str(), ps); + return DeviceVector(availableOutputDevices.getDevice(devices, + address, + AUDIO_FORMAT_DEFAULT)); + } + ALOGV("%s:device 0x%x %d", __FUNCTION__, devices, ps); + return availableOutputDevices.getDevicesFromTypeMask(devices); +} + +DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes, + const sp &preferredDevice, + bool fromCache) const +{ + // First check for explict routing device + if (preferredDevice != nullptr) { + ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str()); + return DeviceVector(preferredDevice); + } + product_strategy_t strategy = EngineBase::getProductStrategyForAttributes(attributes); + // + // @TODO: manage dynamic mix + // + return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy); +} + +DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const +{ + auto attributes = EngineBase::getAttributesForStreamType(stream); + return getOutputDevicesForAttributes(attributes, nullptr, fromCache); +} + +sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, + AudioMix **/*mix*/) const +{ + const auto &availInputDevices = getApmObserver()->getAvailableInputDevices(); + std::string address; + // + // @TODO: manage explicit routing and dynamic mix + // + audio_devices_t deviceType = getPropertyForKey(attr.source); + + if (audio_is_remote_submix_device(deviceType)) { + address = "0"; + std::size_t pos; + std::string tags { attr.tags }; + if ((pos = tags.find("addr=")) != std::string::npos) { + address = tags.substr(pos + std::strlen("addr=")); + } + } + return availInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT); +} + +void Engine::updateDeviceSelectionCache() +{ + for (const auto &iter : getProductStrategies()) { + const auto &strategy = iter.second; + mDevicesForStrategies[strategy->getId()] = getDevicesForProductStrategy(strategy->getId()); + } +} + template <> AudioPolicyManagerInterface *Engine::queryInterface() { - return &mManagerInterface; + return this; } template <> AudioPolicyPluginInterface *Engine::queryInterface() { - return &mPluginInterface; + return this; } } // namespace audio_policy diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h index ba4f8893b9..aebf27d798 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.h +++ b/services/audiopolicy/engineconfigurable/src/Engine.h @@ -16,7 +16,7 @@ #pragma once - +#include "EngineBase.h" #include #include #include "Collection.h" @@ -29,7 +29,7 @@ namespace audio_policy { class ParameterManagerWrapper; class VolumeProfile; -class Engine +class Engine : public EngineBase, AudioPolicyPluginInterface { public: Engine(); @@ -38,129 +38,93 @@ public: template RequestedInterface *queryInterface(); -private: - /// Interface members - class ManagerInterfaceImpl : public AudioPolicyManagerInterface + /// + /// from EngineBase + /// + android::status_t initCheck() override; + + audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const override + { + return getPropertyForKey(inputSource); + } + + audio_devices_t getDeviceForStrategy(routing_strategy stategy) const override; + + routing_strategy getStrategyForStream(audio_stream_type_t stream) override + { + return getPropertyForKey(stream); + } + + routing_strategy getStrategyForUsage(audio_usage_t usage) override; + + status_t setPhoneState(audio_mode_t mode) override; + + audio_mode_t getPhoneState() const override; + + status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override; + + audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const override; + + android::status_t setDeviceConnectionState(const sp devDesc, + audio_policy_dev_state_t state) override; + + DeviceVector getOutputDevicesForAttributes(const audio_attributes_t &attr, + const sp &preferedDevice = nullptr, + bool fromCache = false) const override; + + DeviceVector getOutputDevicesForStream(audio_stream_type_t stream, + bool fromCache = false) const override; + + sp getInputDeviceForAttributes( + const audio_attributes_t &attr, AudioMix **mix = nullptr) const override; + + void updateDeviceSelectionCache() override; + + /// + /// from AudioPolicyPluginInterface + /// + status_t addStrategy(const std::string &name, routing_strategy strategy) override + { + return add(name, strategy); + } + status_t addStream(const std::string &name, audio_stream_type_t stream) override { - public: - ManagerInterfaceImpl(Engine *policyEngine) - : mPolicyEngine(policyEngine) {} - - virtual android::status_t initCheck() - { - return mPolicyEngine->initCheck(); - } - virtual void setObserver(AudioPolicyManagerObserver *observer) - { - mPolicyEngine->setObserver(observer); - } - virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const - { - return mPolicyEngine->getPropertyForKey(inputSource); - } - virtual audio_devices_t getDeviceForStrategy(routing_strategy stategy) const; - virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) - { - return mPolicyEngine->getPropertyForKey(stream); - } - virtual routing_strategy getStrategyForUsage(audio_usage_t usage); - virtual status_t setPhoneState(audio_mode_t mode) - { - return mPolicyEngine->setPhoneState(mode); - } - virtual audio_mode_t getPhoneState() const - { - return mPolicyEngine->getPhoneState(); - } - virtual status_t setForceUse(audio_policy_force_use_t usage, - audio_policy_forced_cfg_t config) - { - return mPolicyEngine->setForceUse(usage, config); - } - virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const - { - return mPolicyEngine->getForceUse(usage); - } - virtual android::status_t setDeviceConnectionState(const sp devDesc, - audio_policy_dev_state_t state) - { - return mPolicyEngine->setDeviceConnectionState(devDesc, state); - } - - private: - Engine *mPolicyEngine; - } mManagerInterface; - - class PluginInterfaceImpl : public AudioPolicyPluginInterface + return add(name, stream); + } + status_t addUsage(const std::string &name, audio_usage_t usage) override { - public: - PluginInterfaceImpl(Engine *policyEngine) - : mPolicyEngine(policyEngine) {} - - virtual status_t addStrategy(const std::string &name, routing_strategy strategy) - { - return mPolicyEngine->add(name, strategy); - } - virtual status_t addStream(const std::string &name, audio_stream_type_t stream) - { - return mPolicyEngine->add(name, stream); - } - virtual status_t addUsage(const std::string &name, audio_usage_t usage) - { - return mPolicyEngine->add(name, usage); - } - virtual status_t addInputSource(const std::string &name, audio_source_t source) - { - return mPolicyEngine->add(name, source); - } - virtual bool setDeviceForStrategy(const routing_strategy &strategy, audio_devices_t devices) - { - return mPolicyEngine->setPropertyForKey(devices, - strategy); - } - virtual bool setStrategyForStream(const audio_stream_type_t &stream, - routing_strategy strategy) - { - return mPolicyEngine->setPropertyForKey(strategy, - stream); - } - virtual bool setVolumeProfileForStream(const audio_stream_type_t &stream, - const audio_stream_type_t &volumeProfile); - - virtual bool setStrategyForUsage(const audio_usage_t &usage, routing_strategy strategy) - { - return mPolicyEngine->setPropertyForKey(strategy, - usage); - } - virtual bool setDeviceForInputSource(const audio_source_t &inputSource, - audio_devices_t device) - { - return mPolicyEngine->setPropertyForKey(device, - inputSource); - } - - private: - Engine *mPolicyEngine; - } mPluginInterface; + return add(name, usage); + } + status_t addInputSource(const std::string &name, audio_source_t source) override + { + return add(name, source); + } + bool setDeviceForStrategy(const routing_strategy &strategy, audio_devices_t devices) override + { + return setPropertyForKey(devices, strategy); + } + bool setStrategyForStream(const audio_stream_type_t &stream, + routing_strategy strategy) override + { + return setPropertyForKey(strategy, stream); + } + bool setVolumeProfileForStream(const audio_stream_type_t &stream, + const audio_stream_type_t &volumeProfile) override; + + bool setStrategyForUsage(const audio_usage_t &usage, routing_strategy strategy) override + { + return setPropertyForKey(strategy, usage); + } + bool setDeviceForInputSource(const audio_source_t &inputSource, audio_devices_t device) override + { + return setPropertyForKey(device, inputSource); + } private: /* Copy facilities are put private to disable copy. */ Engine(const Engine &object); Engine &operator=(const Engine &object); - void setObserver(AudioPolicyManagerObserver *observer); - - bool setVolumeProfileForStream(const audio_stream_type_t &stream, - device_category deviceCategory, - const VolumeCurvePoints &points); - - status_t initCheck(); - status_t setPhoneState(audio_mode_t mode); - audio_mode_t getPhoneState() const; - status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); - audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const; - status_t setDeviceConnectionState(const sp devDesc, - audio_policy_dev_state_t state); StrategyCollection mStrategyCollection; /**< Strategies indexed by their enum id. */ StreamCollection mStreamCollection; /**< Streams indexed by their enum id. */ UsageCollection mUsageCollection; /**< Usages indexed by their enum id. */ @@ -184,12 +148,16 @@ private: template bool setPropertyForKey(const Property &property, const Key &key); + status_t loadAudioPolicyEngineConfig(); + + DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const; + /** * Policy Parameter Manager hidden through a wrapper. */ ParameterManagerWrapper *mPolicyParameterMgr; - AudioPolicyManagerObserver *mApmObserver; + DeviceStrategyMap mDevicesForStrategies; }; } // namespace audio_policy diff --git a/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk index 4814376baf..eebdfd63f0 100644 --- a/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk +++ b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk @@ -8,7 +8,6 @@ $(LOCAL_BUILT_MODULE): MY_ANDROID_AUDIO_BASE_HEADER_FILE := $(ANDROID_AUDIO_BASE $(LOCAL_BUILT_MODULE): MY_AUDIO_POLICY_CONFIGURATION_FILE := $(AUDIO_POLICY_CONFIGURATION_FILE) $(LOCAL_BUILT_MODULE): MY_CRITERION_TOOL := $(HOST_OUT)/bin/buildPolicyCriterionTypes.py $(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) \ - buildPolicyCriterionTypes.py \ $(CRITERION_TYPES_FILE) \ $(ANDROID_AUDIO_BASE_HEADER_FILE) diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk index d19a364ec9..b71a53c724 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk @@ -14,6 +14,7 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy/engineconfigurable/include \ frameworks/av/services/audiopolicy/engineconfigurable/interface \ frameworks/av/services/audiopolicy/common/include \ + frameworks/av/services/audiopolicy/utilities/convert \ external/libxml2/include \ external/icu/icu4c/source/common @@ -27,6 +28,9 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ libxml2 +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index 09faa4c448..a3f341f93c 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -289,7 +289,7 @@ status_t ParameterManagerWrapper::setDeviceConnectionState(const sptype()) ? gOutputDeviceAddressCriterionName : gInputDeviceAddressCriterionName; - ALOGV("%s: device with address %s %s", __FUNCTION__, devDesc->mAddress.string(), + ALOGV("%s: device with address %s %s", __FUNCTION__, devDesc->address().string(), state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE? "disconnected" : "connected"); ISelectionCriterionInterface *criterion = getElement(criterionName, mPolicyCriteria); diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index 837d5bb0ef..f9212f96be 100644 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -8,10 +8,11 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/Engine.cpp \ src/EngineInstance.cpp \ + ../engine/common/src/ProductStrategy.cpp \ + ../engine/common/src/EngineBase.cpp audio_policy_engine_includes_common := \ - $(LOCAL_PATH)/include \ - frameworks/av/services/audiopolicy/engine/interface + $(LOCAL_PATH)/include LOCAL_CFLAGS += \ -Wall \ @@ -26,8 +27,7 @@ LOCAL_C_INCLUDES := \ $(TARGET_OUT_HEADERS)/hw \ $(call include-path-for, frameworks-av) \ $(call include-path-for, audio-utils) \ - $(call include-path-for, bionic) \ - frameworks/av/services/audiopolicy/common/include + $(call include-path-for, bionic) LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) @@ -37,12 +37,18 @@ LOCAL_MODULE_TAGS := optional LOCAL_HEADER_LIBRARIES := libbase_headers LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents \ + libaudiopolicycomponents -LOCAL_SHARED_LIBRARIES += \ +LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils \ libutils \ - libmedia_helper + libmedia_helper \ + libaudiopolicyengineconfig + +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon \ + libaudiopolicyengine_common_headers \ + libaudiopolicyengine_interface_headers include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index cc5a025d80..69f06982e5 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -38,29 +38,31 @@ namespace android namespace audio_policy { -Engine::Engine() - : mManagerInterface(this), - mPhoneState(AUDIO_MODE_NORMAL), - mApmObserver(NULL) -{ - for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) { - mForceUse[i] = AUDIO_POLICY_FORCE_NONE; - } -} +struct legacy_strategy_map { const char *name; legacy_strategy id; }; +static const std::vector gLegacyStrategy = { + { "STRATEGY_NONE", STRATEGY_NONE }, + { "STRATEGY_MEDIA", STRATEGY_MEDIA }, + { "STRATEGY_PHONE", STRATEGY_PHONE }, + { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION }, + { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL }, + { "STRATEGY_DTMF", STRATEGY_DTMF }, + { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE }, + { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER }, + { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY }, + { "STRATEGY_REROUTING", STRATEGY_REROUTING }, + { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume +}; -Engine::~Engine() -{ -} - -void Engine::setObserver(AudioPolicyManagerObserver *observer) +Engine::Engine() { - ALOG_ASSERT(observer != NULL, "Invalid Audio Policy Manager observer"); - mApmObserver = observer; -} + auto result = EngineBase::loadAudioPolicyEngineConfig(); + ALOGE_IF(result.nbSkippedElement != 0, + "Policy Engine configuration is partially invalid, skipped %zu elements", + result.nbSkippedElement); -status_t Engine::initCheck() -{ - return (mApmObserver != NULL) ? NO_ERROR : NO_INIT; + for (const auto &strategy : gLegacyStrategy) { + mLegacyStrategyMap[getProductStrategyByName(strategy.name)] = strategy.id; + } } status_t Engine::setPhoneState(audio_mode_t state) @@ -72,22 +74,22 @@ status_t Engine::setPhoneState(audio_mode_t state) return BAD_VALUE; } - if (state == mPhoneState ) { + if (state == getPhoneState()) { ALOGW("setPhoneState() setting same state %d", state); return BAD_VALUE; } // store previous phone state for management of sonification strategy below - int oldState = mPhoneState; - mPhoneState = state; + int oldState = getPhoneState(); + EngineBase::setPhoneState(state); if (!is_state_in_call(oldState) && is_state_in_call(state)) { ALOGV(" Entering call in setPhoneState()"); - mApmObserver->getVolumeCurves().switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, + getApmObserver()->getVolumeCurves().switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF); } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { ALOGV(" Exiting call in setPhoneState()"); - mApmObserver->getVolumeCurves().restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); + getApmObserver()->getVolumeCurves().restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); } return NO_ERROR; } @@ -101,7 +103,6 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); return BAD_VALUE; } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_MEDIA: if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && @@ -112,7 +113,6 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); return BAD_VALUE; } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_RECORD: if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && @@ -120,7 +120,6 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); return BAD_VALUE; } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_DOCK: if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && @@ -130,21 +129,18 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_SYSTEM: if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO: if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) { ALOGW("setForceUse() invalid config %d for HDMI_SYSTEM_AUDIO", config); } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND: if (config != AUDIO_POLICY_FORCE_NONE && @@ -154,20 +150,18 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced ALOGW("setForceUse() invalid config %d for ENCODED_SURROUND", config); return BAD_VALUE; } - mForceUse[usage] = config; break; case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING: if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) { ALOGW("setForceUse() invalid config %d for FOR_VIBRATE_RINGING", config); return BAD_VALUE; } - mForceUse[usage] = config; break; default: ALOGW("setForceUse() invalid usage %d", usage); break; // TODO return BAD_VALUE? } - return NO_ERROR; + return EngineBase::setForceUse(usage, config); } routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) @@ -176,14 +170,14 @@ routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) switch (stream) { case AUDIO_STREAM_VOICE_CALL: case AUDIO_STREAM_BLUETOOTH_SCO: - return STRATEGY_PHONE; + return android::STRATEGY_PHONE; case AUDIO_STREAM_RING: case AUDIO_STREAM_ALARM: - return STRATEGY_SONIFICATION; + return android::STRATEGY_SONIFICATION; case AUDIO_STREAM_NOTIFICATION: - return STRATEGY_SONIFICATION_RESPECTFUL; + return android::STRATEGY_SONIFICATION_RESPECTFUL; case AUDIO_STREAM_DTMF: - return STRATEGY_DTMF; + return android::STRATEGY_DTMF; default: ALOGE("unknown stream type %d", stream); FALLTHROUGH_INTENDED; @@ -191,15 +185,15 @@ routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs // while key clicks are played produces a poor result case AUDIO_STREAM_MUSIC: - return STRATEGY_MEDIA; + return android::STRATEGY_MEDIA; case AUDIO_STREAM_ENFORCED_AUDIBLE: - return STRATEGY_ENFORCED_AUDIBLE; + return android::STRATEGY_ENFORCED_AUDIBLE; case AUDIO_STREAM_TTS: - return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; + return android::STRATEGY_TRANSMITTED_THROUGH_SPEAKER; case AUDIO_STREAM_ACCESSIBILITY: - return STRATEGY_ACCESSIBILITY; + return android::STRATEGY_ACCESSIBILITY; case AUDIO_STREAM_REROUTING: - return STRATEGY_REROUTING; + return android::STRATEGY_REROUTING; } } @@ -208,55 +202,54 @@ routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) // usage to strategy mapping switch (usage) { case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return STRATEGY_ACCESSIBILITY; + return android::STRATEGY_ACCESSIBILITY; case AUDIO_USAGE_MEDIA: case AUDIO_USAGE_GAME: case AUDIO_USAGE_ASSISTANT: case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return STRATEGY_MEDIA; + return android::STRATEGY_MEDIA; case AUDIO_USAGE_VOICE_COMMUNICATION: - return STRATEGY_PHONE; + return android::STRATEGY_PHONE; case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return STRATEGY_DTMF; + return android::STRATEGY_DTMF; case AUDIO_USAGE_ALARM: case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return STRATEGY_SONIFICATION; + return android::STRATEGY_SONIFICATION; case AUDIO_USAGE_NOTIFICATION: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: case AUDIO_USAGE_NOTIFICATION_EVENT: - return STRATEGY_SONIFICATION_RESPECTFUL; + return android::STRATEGY_SONIFICATION_RESPECTFUL; case AUDIO_USAGE_UNKNOWN: default: - return STRATEGY_MEDIA; + return android::STRATEGY_MEDIA; } } audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const { - DeviceVector availableOutputDevices = mApmObserver->getAvailableOutputDevices(); - DeviceVector availableInputDevices = mApmObserver->getAvailableInputDevices(); + DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices(); - const SwAudioOutputCollection &outputs = mApmObserver->getOutputs(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); - return getDeviceForStrategyInt(strategy, availableOutputDevices, + return getDeviceForStrategyInt(static_cast(strategy), availableOutputDevices, availableInputDevices, outputs, (uint32_t)AUDIO_DEVICE_NONE); } - -audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, - DeviceVector availableOutputDevices, - DeviceVector availableInputDevices, - const SwAudioOutputCollection &outputs, - uint32_t outputDeviceTypesToIgnore) const +audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, + DeviceVector availableOutputDevices, + DeviceVector availableInputDevices, + const SwAudioOutputCollection &outputs, + uint32_t outputDeviceTypesToIgnore) const { uint32_t device = AUDIO_DEVICE_NONE; uint32_t availableOutputDevicesType = @@ -334,7 +327,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, } // for phone strategy, we first consider the forced use and then the available devices by // order of priority - switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) { case AUDIO_POLICY_FORCE_BT_SCO: if (!isInCall() || strategy != STRATEGY_DTMF) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; @@ -352,7 +345,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, if (device) break; // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (!isInCall() && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) && outputs.isA2dpSupported()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; @@ -386,7 +379,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output if (!isInCall() && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) && outputs.isA2dpSupported()) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; @@ -426,7 +419,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // - in countries where not enforced in which case it follows STRATEGY_MEDIA if ((strategy == STRATEGY_SONIFICATION) || - (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { + (getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } @@ -442,9 +435,9 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; } // Use ONLY Bluetooth SCO output when ringing in vibration mode - if (!((mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) + if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) && (strategy == STRATEGY_ENFORCED_AUDIBLE))) { - if (mForceUse[AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING] + if (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING) == AUDIO_POLICY_FORCE_BT_SCO) { if (device2 != AUDIO_DEVICE_NONE) { device = device2; @@ -453,7 +446,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, } } // Use both Bluetooth SCO and phone default output when ringing in normal mode - if (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) { + if (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION) == AUDIO_POLICY_FORCE_BT_SCO) { if ((strategy == STRATEGY_SONIFICATION) && (device & AUDIO_DEVICE_OUT_SPEAKER) && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { @@ -520,7 +513,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HEARING_AID; } if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) && outputs.isA2dpSupported()) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device2 == AUDIO_DEVICE_NONE) { @@ -531,7 +524,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, } } if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) { + (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; } if (device2 == AUDIO_DEVICE_NONE) { @@ -560,7 +553,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; } if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { + (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { @@ -581,7 +574,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, // If hdmi system audio mode is on, remove speaker out of output list. if ((strategy == STRATEGY_MEDIA) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == + (getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) == AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { device &= ~AUDIO_DEVICE_OUT_SPEAKER; } @@ -603,7 +596,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, if (device == AUDIO_DEVICE_NONE) { ALOGV("getDeviceForStrategy() no device found for strategy %d", strategy); - device = mApmObserver->getDefaultOutputDevice()->type(); + device = getApmObserver()->getDefaultOutputDevice()->type(); ALOGE_IF(device == AUDIO_DEVICE_NONE, "getDeviceForStrategy() no default device defined"); } @@ -614,9 +607,9 @@ audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy, audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const { - const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices(); - const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices(); - const SwAudioOutputCollection &outputs = mApmObserver->getOutputs(); + const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const DeviceVector &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; uint32_t device = AUDIO_DEVICE_NONE; @@ -651,7 +644,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons case AUDIO_SOURCE_MIC: if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; - } else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) && + } else if ((getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) && (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { @@ -675,7 +668,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons primaryOutput->getModuleHandle()) & ~AUDIO_DEVICE_BIT_IN; } - switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) { case AUDIO_POLICY_FORCE_BT_SCO: // if SCO device is requested but no SCO device is available, fall back to default case if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { @@ -709,7 +702,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons case AUDIO_SOURCE_VOICE_RECOGNITION: case AUDIO_SOURCE_UNPROCESSED: case AUDIO_SOURCE_HOTWORD: - if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && + if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO && availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { @@ -780,10 +773,86 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons return device; } +void Engine::updateDeviceSelectionCache() +{ + for (const auto &iter : getProductStrategies()) { + const auto &strategy = iter.second; + auto devices = getDevicesForProductStrategy(strategy->getId()); + mDevicesForStrategies[strategy->getId()] = devices; + strategy->setDeviceTypes(devices.types()); + strategy->setDeviceAddress(devices.getFirstValidAddress().c_str()); + } +} + +DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t strategy) const +{ + DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); + + auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ? + mLegacyStrategyMap.at(strategy) : STRATEGY_NONE; + audio_devices_t devices = getDeviceForStrategyInt(legacyStrategy, + availableOutputDevices, + availableInputDevices, outputs, + (uint32_t)AUDIO_DEVICE_NONE); + return availableOutputDevices.getDevicesFromTypeMask(devices); +} + +DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes, + const sp &preferredDevice, + bool fromCache) const +{ + // First check for explict routing device + if (preferredDevice != nullptr) { + ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str()); + return DeviceVector(preferredDevice); + } + // + // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to + // be by APM? + // + product_strategy_t strategy = getProductStrategyForAttributes(attributes); + // + // @TODO: manage dynamic mix + // + + return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy); +} + +DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const +{ + auto attributes = getAttributesForStreamType(stream); + return getOutputDevicesForAttributes(attributes, nullptr, fromCache); +} + +sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, + AudioMix **/*mix*/) const +{ + const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + std::string address; + // + // @TODO: manage explicit routing and dynamic mix + // + audio_devices_t deviceType = getDeviceForInputSource(attr.source); + + if (audio_is_remote_submix_device(deviceType)) { + address = "0"; + std::size_t pos; + std::string tags { attr.tags }; + if ((pos = tags.find("addr=")) != std::string::npos) { + address = tags.substr(pos + std::strlen("addr=")); + } + } + return availableInputDevices.getDevice(deviceType, + String8(address.c_str()), + AUDIO_FORMAT_DEFAULT); +} + template <> AudioPolicyManagerInterface *Engine::queryInterface() { - return &mManagerInterface; + return this; } } // namespace audio_policy diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index 06186c1c3a..80e92de0b3 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -16,7 +16,7 @@ #pragma once - +#include "EngineBase.h" #include "AudioPolicyManagerInterface.h" #include #include @@ -29,114 +29,76 @@ class AudioPolicyManagerObserver; namespace audio_policy { -class Engine +enum legacy_strategy { + STRATEGY_NONE = -1, + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + STRATEGY_TRANSMITTED_THROUGH_SPEAKER, + STRATEGY_ACCESSIBILITY, + STRATEGY_REROUTING, +}; + +class Engine : public EngineBase { public: Engine(); - virtual ~Engine(); + virtual ~Engine() = default; template RequestedInterface *queryInterface(); private: - /// Interface members - class ManagerInterfaceImpl : public AudioPolicyManagerInterface - { - public: - explicit ManagerInterfaceImpl(Engine *policyEngine) - : mPolicyEngine(policyEngine) {} - - virtual void setObserver(AudioPolicyManagerObserver *observer) - { - mPolicyEngine->setObserver(observer); - } - virtual status_t initCheck() - { - return mPolicyEngine->initCheck(); - } - virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const - { - return mPolicyEngine->getDeviceForInputSource(inputSource); - } - virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy) const - { - return mPolicyEngine->getDeviceForStrategy(strategy); - } - virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) - { - return mPolicyEngine->getStrategyForStream(stream); - } - virtual routing_strategy getStrategyForUsage(audio_usage_t usage) - { - return mPolicyEngine->getStrategyForUsage(usage); - } - virtual status_t setPhoneState(audio_mode_t mode) - { - return mPolicyEngine->setPhoneState(mode); - } - virtual audio_mode_t getPhoneState() const - { - return mPolicyEngine->getPhoneState(); - } - virtual status_t setForceUse(audio_policy_force_use_t usage, - audio_policy_forced_cfg_t config) - { - return mPolicyEngine->setForceUse(usage, config); - } - virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const - { - return mPolicyEngine->getForceUse(usage); - } - virtual status_t setDeviceConnectionState(const sp /*devDesc*/, - audio_policy_dev_state_t /*state*/) - { - return NO_ERROR; - } - private: - Engine *mPolicyEngine; - } mManagerInterface; + /// + /// from EngineBase, so from AudioPolicyManagerInterface + /// + audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const override; + + audio_devices_t getDeviceForStrategy(routing_strategy strategy) const override; + + routing_strategy getStrategyForStream(audio_stream_type_t stream) override; + + routing_strategy getStrategyForUsage(audio_usage_t usage) override; + + status_t setPhoneState(audio_mode_t mode) override; + + status_t setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) override; + + DeviceVector getOutputDevicesForAttributes(const audio_attributes_t &attr, + const sp &preferedDevice = nullptr, + bool fromCache = false) const override; + + DeviceVector getOutputDevicesForStream(audio_stream_type_t stream, + bool fromCache = false) const override; + + sp getInputDeviceForAttributes( + const audio_attributes_t &attr, AudioMix **mix = nullptr) const override; + + void updateDeviceSelectionCache() override; + private: /* Copy facilities are put private to disable copy. */ Engine(const Engine &object); Engine &operator=(const Engine &object); - void setObserver(AudioPolicyManagerObserver *observer); - - status_t initCheck(); - - inline bool isInCall() const - { - return is_state_in_call(mPhoneState); - } - - status_t setPhoneState(audio_mode_t mode); - audio_mode_t getPhoneState() const - { - return mPhoneState; - } - status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); - audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const - { - return mForceUse[usage]; - } status_t setDefaultDevice(audio_devices_t device); - routing_strategy getStrategyForStream(audio_stream_type_t stream); - routing_strategy getStrategyForUsage(audio_usage_t usage); - audio_devices_t getDeviceForStrategy(routing_strategy strategy) const; - audio_devices_t getDeviceForStrategyInt(routing_strategy strategy, - DeviceVector availableOutputDevices, - DeviceVector availableInputDevices, - const SwAudioOutputCollection &outputs, - uint32_t outputDeviceTypesToIgnore) const; - audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const; - audio_mode_t mPhoneState; /**< current phone state. */ - - /** current forced use configuration. */ - audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; - - AudioPolicyManagerObserver *mApmObserver; + audio_devices_t getDeviceForStrategyInt(legacy_strategy strategy, + DeviceVector availableOutputDevices, + DeviceVector availableInputDevices, + const SwAudioOutputCollection &outputs, + uint32_t outputDeviceTypesToIgnore) const; + + DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const; + + DeviceStrategyMap mDevicesForStrategies; + + std::map mLegacyStrategyMap; }; } // namespace audio_policy } // namespace android diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index e4fba0f56a..5f0974ea04 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -4,7 +4,6 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ - frameworks/av/services/audiopolicy/common/include \ frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ @@ -18,6 +17,9 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents \ +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon + LOCAL_SRC_FILES := \ audiopolicymanager_tests.cpp \ -- GitLab From f1e9508a70f5eff6d9e9d2cfdf5b19ac6a6974b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Fri, 2 Nov 2018 13:53:31 +0100 Subject: [PATCH 0873/1530] audiopolicy: engineconfigurable: add configuration of ProductStrategies -Update Policy PFW plugin to support product strategies -Provides example structure/Settings to configure Product Strategies Test: make Change-Id: I2493e261f302b78f3b96e17d87fb7028d9cde23e --- services/audiopolicy/engine/config/Android.mk | 1 + .../engineconfigurable/config/Android.mk | 9 + .../config/example/Android.mk | 67 ++ .../audio_policy_engine_configuration.xml | 22 + ...audio_policy_engine_product_strategies.xml | 161 ++++ .../audio_policy_engine_configuration.xml | 22 + ...audio_policy_engine_product_strategies.xml | 110 +++ .../interface/AudioPolicyPluginInterface.h | 18 + .../parameter-framework/examples/Android.mk | 99 +-- .../examples/Car/Android.mk | 139 ++++ .../device_for_product_strategies.pfw | 717 ++++++++++++++++++ .../Car/Structure/PolicySubsystem.xml | 141 ++++ .../Car/Structure/ProductStrategies.xml | 39 + .../examples/Phone/Android.mk | 105 +++ ...ice_for_product_strategy_accessibility.pfw | 596 +++++++++++++++ .../device_for_product_strategy_dtmf.pfw | 625 +++++++++++++++ ..._for_product_strategy_enforced_audible.pfw | 361 +++++++++ .../device_for_product_strategy_media.pfw | 340 +++++++++ .../device_for_product_strategy_phone.pfw | 483 ++++++++++++ .../device_for_product_strategy_rerouting.pfw | 43 ++ ...vice_for_product_strategy_sonification.pfw | 490 ++++++++++++ ...oduct_strategy_sonification_respectful.pfw | 536 +++++++++++++ ...t_strategy_transmitted_through_speaker.pfw | 43 ++ .../device_for_product_strategy_unknown.pfw | 36 + .../Phone/Structure/PolicySubsystem.xml | 141 ++++ .../Phone/Structure/ProductStrategies.xml | 35 + .../Structure/PolicySubsystem-CommonTypes.xml | 10 + .../parameter-framework/plugin/Android.mk | 9 +- .../plugin/PolicySubsystem.cpp | 6 + .../plugin/PolicySubsystem.h | 1 + .../plugin/ProductStrategy.cpp | 59 ++ .../plugin/ProductStrategy.h | 57 ++ .../engineconfigurable/src/Engine.cpp | 21 + .../engineconfigurable/src/Engine.h | 10 + .../engineconfigurable/wrapper/Android.mk | 1 - .../enginedefault/config/Android.mk | 9 + .../enginedefault/config/example/Android.mk | 32 + .../audio_policy_engine_configuration.xml | 22 + ...audio_policy_engine_product_strategies.xml | 110 +++ 39 files changed, 5652 insertions(+), 74 deletions(-) create mode 100644 services/audiopolicy/engineconfigurable/config/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/config/example/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h create mode 100644 services/audiopolicy/enginedefault/config/Android.mk create mode 100644 services/audiopolicy/enginedefault/config/example/Android.mk create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk index dbcd1bf3fb..cb35c37ca4 100644 --- a/services/audiopolicy/engine/config/Android.mk +++ b/services/audiopolicy/engine/config/Android.mk @@ -39,3 +39,4 @@ LOCAL_HEADER_LIBRARIES := \ libaudiopolicycommon include $(BUILD_SHARED_LIBRARY) + diff --git a/services/audiopolicy/engineconfigurable/config/Android.mk b/services/audiopolicy/engineconfigurable/config/Android.mk new file mode 100644 index 0000000000..dcce8e3d28 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/Android.mk @@ -0,0 +1,9 @@ + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +####################################################################### +# Recursive call sub-folder Android.mk +# +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk new file mode 100644 index 0000000000..caf8afa108 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -0,0 +1,67 @@ +LOCAL_PATH := $(call my-dir) + +################################################################## +# CONFIGURATION TOP FILE +################################################################## + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_configuration_phone.xml +LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) + +LOCAL_REQUIRED_MODULES := \ + audio_policy_engine_product_strategies_phone.xml + +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_product_strategies_phone.xml +LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) + + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) + +################################################################## +# AUTOMOTIVE CONFIGURATION TOP FILE +################################################################## +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_configuration_automotive.xml +LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) + +LOCAL_REQUIRED_MODULES := \ + audio_policy_engine_product_strategies_automotive.xml \ + +include $(BUILD_PREBUILT) + +################################################################## +# CONFIGURATION FILES +################################################################## + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_product_strategies_automotive.xml +LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml new file mode 100644 index 0000000000..ab61d8a937 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml new file mode 100644 index 0000000000..543a2f0971 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml new file mode 100644 index 0000000000..ab61d8a937 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml new file mode 100644 index 0000000000..f72e37916d --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h index 2e29a9b2a5..53be275c54 100644 --- a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h +++ b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h @@ -16,10 +16,12 @@ #pragma once +#include #include #include #include #include +#include #include #include #include @@ -139,6 +141,22 @@ public: virtual bool setDeviceForInputSource(const audio_source_t &inputSource, audio_devices_t device) = 0; + virtual void setDeviceAddressForProductStrategy(product_strategy_t strategy, + const std::string &address) = 0; + + /** + * Set the device to be used by a product strategy. + * + * @param[in] strategy: name of the product strategy for which the device to use has to be set + * @param[in] devices; mask of devices to be used for the given strategy. + * + * @return true if the devices were set correclty for this strategy, false otherwise. + */ + virtual bool setDeviceTypesForProductStrategy(product_strategy_t strategy, + audio_devices_t devices) = 0; + + virtual product_strategy_t getProductStrategyByName(const std::string &address) = 0; + protected: virtual ~AudioPolicyPluginInterface() {} }; diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 7631976b2b..a1ebd35bce 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -30,103 +30,61 @@ LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework +LOCAL_MODULE_RELATIVE_PATH := parameter-framework LOCAL_SRC_FILES := $(LOCAL_MODULE).in +# external/parameter-framework prevents from using debug interface AUDIO_PATTERN = @TUNING_ALLOWED@ -ifeq ($(TARGET_BUILD_VARIANT),user) +#ifeq ($(TARGET_BUILD_VARIANT),user) AUDIO_VALUE = false -else -AUDIO_VALUE = true -endif +#else +#AUDIO_VALUE = true +#endif -LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(LOCAL_MODULE_PATH)/$(LOCAL_MODULE) +LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE) include $(BUILD_PREBUILT) - -########## Policy PFW Structures ######### +########## Policy PFW Common Structures ######### include $(CLEAR_VARS) -LOCAL_MODULE := PolicyClass.xml +LOCAL_MODULE := PolicySubsystem-CommonTypes.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem.xml +LOCAL_MODULE := PolicyClass.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem-CommonTypes.xml \ - libpolicy-subsystem - -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) -include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem-CommonTypes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),1) -######### Policy PFW Settings ######### +########## Policy PFW Example Structures ######### +ifeq (0, 1) include $(CLEAR_VARS) -LOCAL_MODULE := parameter-framework.policy -LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml +LOCAL_MODULE := PolicySubsystem.xml.common +LOCAL_MODULE_STEM := PolicySubsystem.xml +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - policy_criteria.xml \ - policy_criterion_types.xml \ - PolicySubsystem.xml \ - PolicyClass.xml \ - ParameterFrameworkConfigurationPolicy.xml - -ifeq ($(pfw_rebuild_settings),true) -PFW_EDD_FILES := \ - $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_phone.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_sonification.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_sonification_respectful.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_dtmf.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_enforced_audible.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_transmitted_through_speaker.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_accessibility.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_rerouting.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_stream.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_usage.pfw \ - $(LOCAL_PATH)/Settings/device_for_input_source.pfw \ - $(LOCAL_PATH)/Settings/volumes.pfw - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(PFW_EDD_FILES) - - -PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml -PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml - -PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml - -PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + PolicySubsystem-CommonTypes.xml \ + PolicySubsystem-Volume.xml \ + libpolicy-subsystem \ -include $(BUILD_PFW_SETTINGS) -else -# Use the existing file -LOCAL_SRC_FILES := Settings/$(LOCAL_MODULE_STEM) +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) -endif # pfw_rebuild_settings -endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 0) +endif # ifeq (0, 1) ######### Policy PFW Settings - No Output ######### ifeq (0, 1) @@ -140,7 +98,7 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ policy_criteria.xml \ policy_criterion_types.xml \ - PolicySubsystem.xml \ + PolicySubsystem.xml.common \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml @@ -168,7 +126,7 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ policy_criteria.xml \ policy_criterion_types.xml \ - PolicySubsystem.xml \ + PolicySubsystem.xml.common \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml @@ -192,10 +150,11 @@ PFW_EDD_FILES := \ include $(BUILD_PFW_SETTINGS) -endif # ifeq (1, 0) - +endif #ifeq (0, 1) ####################################################################### # Recursive call sub-folder Android.mk ####################################################################### include $(call all-makefiles-under,$(LOCAL_PATH)) + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk new file mode 100644 index 0000000000..01839dd3ff --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -0,0 +1,139 @@ +################################################################################################ +# +# @NOTE: +# Audio Policy Engine configurable example for generic device build +# +# Any vendor shall have its own configuration within the corresponding device folder +# +################################################################################################ + +ifeq ($(BUILD_AUDIO_POLICY_AUTOMOTIVE_CONFIGURATION), 1) +LOCAL_PATH := $(call my-dir) + +PFW_CORE := external/parameter-framework +PFW_DEFAULT_SCHEMAS_DIR := $(PFW_CORE)/upstream/schemas +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk + + +################################################################## +# CONFIGURATION FILES +################################################################## + +######### Policy PFW top level file ######### + +include $(CLEAR_VARS) +LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml.car +LOCAL_MODULE_STEM := ParameterFrameworkConfigurationPolicy.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework +LOCAL_SRC_FILES := ../$(LOCAL_MODULE_STEM).in + +AUDIO_PATTERN = @TUNING_ALLOWED@ +ifeq ($(TARGET_BUILD_VARIANT),user) +AUDIO_VALUE = false +else +AUDIO_VALUE = true +endif + +LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE_STEM) + +include $(BUILD_PREBUILT) + +########## Policy PFW Common Structures ######### + +include $(CLEAR_VARS) +LOCAL_MODULE := PolicySubsystem-CommonTypes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := ../Structure/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := PolicyClass.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := ../Structure/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +########## Policy PFW Structures ######### + +include $(CLEAR_VARS) +LOCAL_MODULE := PolicySubsystem.xml.car +LOCAL_MODULE_STEM := PolicySubsystem.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_REQUIRED_MODULES := \ + ProductStrategies.xml.car \ + PolicySubsystem-Volume.xml \ + PolicySubsystem-CommonTypes.xml \ + libpolicy-subsystem + +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + + +include $(CLEAR_VARS) +LOCAL_MODULE := ProductStrategies.xml.car +LOCAL_MODULE_STEM := ProductStrategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + +######### Policy PFW Settings ######### +include $(CLEAR_VARS) +LOCAL_MODULE := parameter-framework.policy.car +LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy + +PFW_EDD_FILES := \ + $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ + $(LOCAL_PATH)/../Settings/strategy_for_stream.pfw \ + $(LOCAL_PATH)/../Settings/strategy_for_usage.pfw \ + $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ + $(LOCAL_PATH)/../Settings/volumes.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_media.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_phone.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_sonification.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_sonification_respectful.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_dtmf.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_enforced_audible.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_transmitted_through_speaker.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_accessibility.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_rerouting.pfw + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(PFW_EDD_FILES) + +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem.xml.car \ + PolicyClass.xml \ + policy_criteria.xml \ + policy_criterion_types.xml \ + ParameterFrameworkConfigurationPolicy.xml.car + +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml + +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml + +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +include $(BUILD_PFW_SETTINGS) + +endif #ifeq ($(BUILD_AUDIO_POLICY_AUTOMOTIVE_CONFIGURATION), 1) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw new file mode 100644 index 0000000000..196d82c625 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw @@ -0,0 +1,717 @@ +supDomain: DeviceForProductStrategies + supDomain: OemTrafficAnouncement + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_traffic_anouncement/device_address = BUS08_OEM1 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS08_OEM1 + + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + bus = 0 + + supDomain: OemStrategy1 + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_strategy_1/device_address = BUS08_OEM1 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS08_OEM1 + + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + bus = 0 + + + + supDomain: OemStrategy2 + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_strategy_2/device_address = BUS08_OEM1 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS08_OEM1 + + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + bus = 0 + + + + supDomain: Radio + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/radio/device_address = BUS09_OEM2 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS09_OEM2 + + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + speaker = 0 + bus = 0 + + supDomain: ExtAudioSource + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/ext_audio_source/device_address = BUS09_OEM2 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS09_OEM2 + + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + bus = 0 + + + + supDomain: VoiceCommand + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/voice_command/device_address = BUS02_VOICE_COMMAND + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS02_VOICE_COMMAND + + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: SafetyAlert + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/safety_alert/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: Music + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/music/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + speaker = 0 + bus = 0 + + + + supDomain: NavGuidance + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/nav_guidance/device_address = BUS01_NAV_GUIDANCE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS01_NAV_GUIDANCE + + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: VoiceCall + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/voice_call/device_address = BUS04_CALL + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS04_CALL + + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: Alarm + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/alarm/device_address = BUS05_ALARM + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS05_ALARM + + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: Ring + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/ring/device_address = BUS03_CALL_RING + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS03_CALL_RING + + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: Notification + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/notification/device_address = BUS06_NOTIFICATION + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS06_NOTIFICATION + + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + speaker = 0 + bus = 0 + + + supDomain: System + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/system/device_address = BUS07_SYSTEM_SOUND + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS07_SYSTEM_SOUND + + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + speaker = 0 + bus = 1 + + conf: Speaker + AvailableOutputDevices Includes Speaker + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + speaker = 1 + bus = 0 + + conf: Default + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + speaker = 0 + bus = 0 + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml new file mode 100644 index 0000000000..9f999b38d1 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml new file mode 100644 index 0000000000..53bba0361a --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk new file mode 100644 index 0000000000..bc91c6fcd8 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -0,0 +1,105 @@ +################################################################################################ +# +# @NOTE: +# Audio Policy Engine configurable example for generic device build +# +# Any vendor shall have its own configuration within the corresponding device folder +# +################################################################################################ + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) + +LOCAL_PATH := $(call my-dir) + +PFW_CORE := external/parameter-framework +PFW_DEFAULT_SCHEMAS_DIR := $(PFW_CORE)/upstream/schemas +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk + +################################################################## +# CONFIGURATION FILES +################################################################## +########## Policy PFW Structures ######### + +include $(CLEAR_VARS) +LOCAL_MODULE := PolicySubsystem.xml.phone +LOCAL_MODULE_STEM := PolicySubsystem.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem-CommonTypes.xml \ + ProductStrategies.xml.phone \ + PolicySubsystem-Volume.xml \ + libpolicy-subsystem \ + +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + + +include $(CLEAR_VARS) +LOCAL_MODULE := ProductStrategies.xml.phone +LOCAL_MODULE_STEM := ProductStrategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + +######### Policy PFW Settings ######### +include $(CLEAR_VARS) +LOCAL_MODULE := parameter-framework.policy.phone +LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy + +PFW_EDD_FILES := \ + $(LOCAL_PATH)/../Settings/strategy_for_stream.pfw \ + $(LOCAL_PATH)/../Settings/strategy_for_usage.pfw \ + $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ + $(LOCAL_PATH)/../Settings/volumes.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_media.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_phone.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_sonification.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_sonification_respectful.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_dtmf.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_enforced_audible.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_transmitted_through_speaker.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_accessibility.pfw \ + $(LOCAL_PATH)/../Settings/device_for_strategy_rerouting.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_media.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_accessibility.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_dtmf.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_enforced_audible.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_phone.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_sonification.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_sonification_respectful.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_rerouting.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_transmitted_through_speaker.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_unknown.pfw + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(PFW_EDD_FILES) + +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem.xml.phone \ + PolicyClass.xml \ + policy_criteria.xml \ + policy_criterion_types.xml \ + ParameterFrameworkConfigurationPolicy.xml + +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml + +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml + +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +include $(BUILD_PFW_SETTINGS) + +endif diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw new file mode 100644 index 0000000000..53e93dea21 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_accessibility.pfw @@ -0,0 +1,596 @@ +supDomain: DeviceForProductStrategy + + supDomain: Accessibility + # + # STRATEGY_ACCESSIBILITY follows STRATEGY_PHONE if in call widely speaking + # STRATEGY_ACCESSIBILITY follows STRATEGY_MEDIA otherwise + # + # Other case are handled programmatically has involving activity of streams. + # + domain: UnreachableDevices + conf: Calibration + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + hdmi_arc = 0 + spdif = 0 + aux_line = 0 + fm = 0 + speaker_safe = 0 + telephony_tx = 0 + ip = 0 + bus = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + + domain: Device + conf: RemoteSubmix + # + # Accessibility follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 1 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothA2dp + # + # Accessibility falls through media strategy if not in call (widely speaking) + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dp + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothA2dpHeadphone + # + # Accessibility falls through media strategy if not in call (widely speaking) + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dpHeadphones + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothA2dpSpeaker + # + # Accessibility falls through media strategy if not in call (widely speaking) + # + ForceUseForMedia IsNot ForceNoBtA2dp + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes BluetoothA2dpSpeaker + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: ForceSpeakerWhenNotInCall + # + # Accessibility follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia Is ForceSpeaker + AvailableOutputDevices Includes Speaker + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 1 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothScoCarkit + # + # accessibility falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes BluetoothScoCarkit + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 1 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothScoHeadset + # + # accessibility falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes BluetoothScoHeadset + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 1 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothSco + # + # accessibility falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes BluetoothSco + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 1 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: WiredHeadphone + ANY + # + # accessibility falls through Phone strategy if in call + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # accessibility follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes WiredHeadphone + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 1 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Line + ANY + # + # accessibility falls through Phone strategy if in call + # but Line has a lower priority than WiredHeadset in this case. + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Excludes WiredHeadset + # + # accessibility follows Media strategy if not in call + # + AvailableOutputDevices Includes Line + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: WiredHeadset + ANY + # + # accessibility falls through Phone strategy if in call + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # accessibility follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes WiredHeadset + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 1 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: UsbDevice + ANY + # + # accessibility falls through Phone strategy if in call (widely speaking) + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # accessibility follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Excludes UsbAccessory + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes UsbDevice + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + hdmi = 0 + + conf: UsbAccessory + # + # accessibility falls through Phone strategy if in call (widely speaking) + # but USB accessory not reachable in call + # + # accessibility follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes UsbAccessory + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 1 + usb_device = 0 + hdmi = 0 + + conf: DgtlDockHeadset + # + # accessibility falls through Phone strategy if in call (widely speaking) + # but DgtlDockHeadset not reachable in call + # + # accessibility follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes DgtlDockHeadset + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: AuxDigital + # + # accessibility falls through Phone strategy if in call (widely speaking) + # but Hdmi not reachable in call + # + # accessibility follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes Hdmi + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 1 + + conf: AnlgDockHeadset + # + # accessibility falls through Phone strategy if in call (widely speaking) + # but AnlgDockHeadset not reachable in call + # + # accessibility follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes AnlgDockHeadset + ForceUseForDock Is ForceAnalogDock + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Earpiece + # + # accessibility falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes Earpiece + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 1 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Speaker + AvailableOutputDevices Includes Speaker + + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 1 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Default + component: /Policy/policy/product_strategies/accessibility/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_sco = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw new file mode 100644 index 0000000000..b8426c68e8 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_dtmf.pfw @@ -0,0 +1,625 @@ +supDomain: DeviceForProductStrategies + supDomain: Dtmf + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + fm = 0 + speaker_safe = 0 + bluetooth_sco_carkit = 0 + ip = 0 + bus = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/dtmf/device_address = + + domain: Device2 + conf: RemoteSubmix + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 1 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dp + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dp + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dpHeadphones + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dpHeadphones + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dpSpeaker + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dpSpeaker + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: ForceSpeakerWhenNotInCall + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia Is ForceSpeaker + ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced + AvailableOutputDevices Includes Speaker + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 1 + + conf: BluetoothScoHeadset + # + # DTMF falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes BluetoothScoHeadset + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 1 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothSco + # + # DTMF falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes BluetoothSco + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 1 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: WiredHeadphone + ANY + # + # DTMF falls through Phone strategy if in call + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # DTMF follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes WiredHeadphone + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 1 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Line + ANY + # + # DTMF falls through Phone strategy if in call + # but Line has a lower priority than WiredHeadset in this case. + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Excludes WiredHeadset + # + # DTMF follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes Line + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 1 + speaker = 0 + + conf: WiredHeadset + ANY + # + # DTMF falls through Phone strategy if in call + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # DTMF follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes WiredHeadset + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 1 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: UsbDevice + ANY + # + # DTMF falls through Phone strategy if in call (widely speaking) + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + # + # DTMF follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Excludes UsbAccessory + ForceUseForCommunication Is ForceSpeaker + AvailableOutputDevices Includes UsbDevice + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: UsbAccessory + # + # DTMF falls through Phone strategy if in call (widely speaking) + # but USB accessory not reachable in call + # + # DTMF follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes UsbAccessory + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 1 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: DgtlDockHeadset + # + # DTMF falls through Phone strategy if in call (widely speaking) + # but DgtlDockHeadset not reachable in call + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes DgtlDockHeadset + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Hdmi + # + # DTMF falls through Phone strategy if in call (widely speaking) + # but Hdmi not reachable in call + # + # DTMF follows Media strategy if not in call + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes Hdmi + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: AnlgDockHeadset + # + # DTMF falls through Phone strategy if in call (widely speaking) + # but AnlgDockHeadset not reachable in call + # + # DTMF follows Media strategy if not in call + # Media strategy inverts the priority of USB device vs accessory + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForDock Is ForceAnalogDock + AvailableOutputDevices Includes AnlgDockHeadset + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Earpiece + # + # DTMF falls through Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Includes Earpiece + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 1 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Speaker + ANY + # + # DTMF falls through Phone strategy if in call + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceSpeaker + # + # DTMF follows Media strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced + AvailableOutputDevices Includes Speaker + + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 1 + + conf: Default + component: /Policy/policy/product_strategies/dtmf/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + domain: Arc + # + # DTMF strategy follows media strategy if not in call + # these following domains consists in device(s) that can co-exist with others + # e.g. ARC, SPDIF, AUX_LINE + # + conf: Selected + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes HdmiArc + + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/hdmi_arc = 1 + + conf: NotSelected + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/hdmi_arc = 0 + + domain: Spdif + # + # DTMF strategy follows media strategy if not in call + # these following domains consists in device(s) that can co-exist with others + # e.g. ARC, SPDIF, AUX_LINE + # + conf: Selected + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes Spdif + + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/spdif = 1 + + conf: NotSelected + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/spdif = 0 + + domain: AuxLine + # + # DTMF strategy follows media strategy if not in call + # these following domains consists in device(s) that can co-exist with others + # e.g. ARC, SPDIF, AUX_LINE + # + conf: Selected + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes AuxLine + + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/aux_line = 1 + + conf: NotSelected + /Policy/policy/product_strategies/dtmf/selected_output_devices/mask/aux_line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw new file mode 100644 index 0000000000..2daa9ac2f8 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_enforced_audible.pfw @@ -0,0 +1,361 @@ +supDomain: DeviceForProductStrategy + + supDomain: EnforcedAudible + + domain: UnreachableDevices + conf: Calibration + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + # no enforced_audible on remote submix (e.g. WFD) + hdmi_arc = 0 + spdif = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + bus = 0 + fm = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + + domain: Speaker + conf: Selected + # + # strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + # except: + # - when in call where it doesn't default to STRATEGY_PHONE behavior + # - in countries where not enforced in which case it follows STRATEGY_MEDIA + # + AvailableOutputDevices Includes Speaker + ANY + ForceUseForSystem Is ForceSystemEnforced + ALL + ForceUseForMedia Is ForceSpeaker + AvailableOutputDevices Excludes RemoteSubmix + ANY + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Excludes BluetoothA2dp + AvailableOutputDevices Excludes BluetoothA2dpHeadphones + AvailableOutputDevices Excludes BluetoothA2dpSpeaker + # + # Speaker is also the fallback device if any of the device from Device2 domain + # is selected. + # + ALL + AvailableOutputDevices Excludes RemoteSubmix + AvailableOutputDevices Excludes WiredHeadphone + AvailableOutputDevices Excludes Line + AvailableOutputDevices Excludes WiredHeadset + AvailableOutputDevices Excludes UsbAccessory + AvailableOutputDevices Excludes UsbDevice + AvailableOutputDevices Excludes DgtlDockHeadset + AvailableOutputDevices Excludes Hdmi + ANY + AvailableOutputDevices Excludes AnlgDockHeadset + ForceUseForDock IsNot ForceAnalogDock + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + speaker = 1 + + conf: NotSelected + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + speaker = 0 + + domain: Device2 + conf: RemoteSubmix + AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 1 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: BluetoothA2dp + AvailableOutputDevices Includes BluetoothA2dp + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: BluetoothA2dpHeadphones + AvailableOutputDevices Includes BluetoothA2dpHeadphones + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: BluetoothA2dpSpeaker + AvailableOutputDevices Includes BluetoothA2dpSpeaker + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: WiredHeadphone + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes WiredHeadphone + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 1 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: Line + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes Line + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 1 + + conf: WiredHeadset + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes WiredHeadset + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 1 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: UsbAccessory + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes UsbAccessory + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 1 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: UsbDevice + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes UsbDevice + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + telephony_tx = 0 + line = 0 + + conf: DgtlDockHeadset + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes DgtlDockHeadset + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: Hdmi + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes Hdmi + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: AnlgDockHeadset + ForceUseForMedia IsNot ForceSpeaker + ForceUseForDock Is ForceAnalogDock + AvailableOutputDevices Includes AnlgDockHeadset + + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: NoDevice + component: /Policy/policy/product_strategies/enforced_audible/selected_output_devices/mask + remote_submix = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw new file mode 100644 index 0000000000..d6d355c299 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_media.pfw @@ -0,0 +1,340 @@ +supDomain: DeviceForProductStrategy + supDomain: Media + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + fm = 0 + speaker_safe = 0 + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + telephony_tx = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + bus = 0 + stub = 0 + /Policy/policy/product_strategies/media/device_address = + + domain: Device2 + conf: RemoteSubmix + AvailableOutputDevices Includes RemoteSubmix + AvailableOutputDevicesAddresses Includes 0 + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 1 + line = 0 + + conf: BluetoothA2dp + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication IsNot ForceBtSco + AvailableOutputDevices Includes BluetoothA2dp + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 1 + remote_submix = 0 + line = 0 + + conf: BluetoothA2dpHeadphone + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication IsNot ForceBtSco + AvailableOutputDevices Includes BluetoothA2dpHeadphones + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: BluetoothA2dpSpeaker + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication IsNot ForceBtSco + AvailableOutputDevices Includes BluetoothA2dpSpeaker + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: ForceSpeaker + ForceUseForMedia Is ForceSpeaker + AvailableOutputDevices Includes Speaker + # + # If hdmi system audio mode is on, remove speaker out of output list. + # + ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 1 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: WiredHeadphone + AvailableOutputDevices Includes WiredHeadphone + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 1 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: Line + AvailableOutputDevices Includes Line + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 1 + + conf: WiredHeadset + AvailableOutputDevices Includes WiredHeadset + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 1 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: UsbAccessory + AvailableOutputDevices Includes UsbAccessory + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 1 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: UsbDevice + AvailableOutputDevices Includes UsbDevice + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 1 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: DgtlDockHeadset + AvailableOutputDevices Includes DgtlDockHeadset + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 1 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: AuxDigital + AvailableOutputDevices Includes Hdmi + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 1 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: AnlgDockHeadset + AvailableOutputDevices Includes AnlgDockHeadset + ForceUseForDock Is ForceAnalogDock + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 1 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: Speaker + AvailableOutputDevices Includes Speaker + # + # If hdmi system audio mode is on, remove speaker out of output list. + # + ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced + ForceUseForCommunication IsNot ForceBtSco + + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 1 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + conf: Default + component: /Policy/policy/product_strategies/media/selected_output_devices/mask + speaker = 0 + hdmi = 0 + dgtl_dock_headset = 0 + angl_dock_headset = 0 + usb_device = 0 + usb_accessory = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp = 0 + remote_submix = 0 + line = 0 + + domain: Arc + # + # these following domains consists in device(s) that can co-exist with others + # e.g. ARC, SPDIF, AUX_LINE + # + conf: Selected + AvailableOutputDevices Includes HdmiArc + + /Policy/policy/product_strategies/media/selected_output_devices/mask/hdmi_arc = 1 + + conf: NotSelected + /Policy/policy/product_strategies/media/selected_output_devices/mask/hdmi_arc = 0 + + domain: Spdif + # + # these following domains consists in device(s) that can co-exist with others + # e.g. ARC, SPDIF, AUX_LINE + # + conf: Selected + AvailableOutputDevices Includes Spdif + + /Policy/policy/product_strategies/media/selected_output_devices/mask/spdif = 1 + + conf: NotSelected + /Policy/policy/product_strategies/media/selected_output_devices/mask/spdif = 0 + + domain: AuxLine + conf: Selected + AvailableOutputDevices Includes AuxLine + + /Policy/policy/product_strategies/media/selected_output_devices/mask/aux_line = 1 + + conf: NotSelected + /Policy/policy/product_strategies/media/selected_output_devices/mask/aux_line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw new file mode 100644 index 0000000000..5693d4e4d1 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_phone.pfw @@ -0,0 +1,483 @@ +supDomain: DeviceForProductStrategy + supDomain: Phone + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + # no sonification on remote submix (e.g. WFD) + remote_submix = 0 + hdmi_arc = 0 + aux_line = 0 + spdif = 0 + fm = 0 + speaker_safe = 0 + bus = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/phone/device_address = + + domain: Device + conf: ScoCarkit + AvailableOutputDevices Includes BluetoothScoCarkit + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 1 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothScoHeadset + AvailableOutputDevices Includes BluetoothScoHeadset + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 1 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothSco + AvailableOutputDevices Includes BluetoothSco + ForceUseForCommunication Is ForceBtSco + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 1 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dp + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes BluetoothA2dp + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication Is ForceNone + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dpHeadphones + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes BluetoothA2dpHeadphones + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication Is ForceNone + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: BluetoothA2dpSpeaker + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes BluetoothA2dpSpeaker + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + ForceUseForCommunication Is ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: WiredHeadphone + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes WiredHeadphone + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 1 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: WiredHeadset + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes WiredHeadset + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 1 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Line + AvailableOutputDevices Includes Line + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 1 + speaker = 0 + + conf: UsbDevice + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes UsbDevice + ANY + ForceUseForCommunication Is ForceBtSco + ForceUseForCommunication Is ForceNone + ALL + ForceUseForCommunication Is ForceSpeaker + # + # In case of Force Speaker, priority between device and accessory are + # inverted compared to Force None or Bt Sco + # + AvailableOutputDevices Excludes UsbAccessory + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: UsbAccessory + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes UsbAccessory + TelephonyMode IsNot InCommunication + TelephonyMode IsNot InCall + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: DgtlDockHeadset + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes DgtlDockHeadset + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Hdmi + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes Hdmi + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: AnlgDockHeadset + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes AnlgDockHeadset + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Earpiece + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes Earpiece + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 1 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 0 + + conf: Speaker + # + # Fallback BT Sco devices in case of FORCE_BT_SCO + # or FORCE_NONE + # + AvailableOutputDevices Includes Speaker + ForceUseForCommunication Is ForceSpeaker + + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 1 + + conf: Default + # + # Fallback on default output device which can be speaker for example + # + component: /Policy/policy/product_strategies/phone/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + speaker = 1 + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw new file mode 100644 index 0000000000..c064c18bcc --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw @@ -0,0 +1,43 @@ +supDomain: DeviceForProductStrategy + supDomain: Rerouting + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/rerouting/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/rerouting/device_address = + + domain: SelectedDevice + conf: Bus + component: /Policy/policy/product_strategies/rerouting/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/rerouting/selected_output_devices/mask + bus = 0 + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw new file mode 100644 index 0000000000..c4edeeb9e2 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification.pfw @@ -0,0 +1,490 @@ +supDomain: DeviceForProductStrategy + supDomain: Sonification + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + remote_submix = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + speaker_safe = 0 + aux_line = 0 + # + # Sonification follows phone strategy if in call but HDMI is not reachable + # + hdmi = 0 + bus = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/sonification/device_address = + + domain: Speaker + + conf: Selected + AvailableOutputDevices Includes Speaker + ANY + # + # Sonification falls through ENFORCED_AUDIBLE if not in call (widely speaking) + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ALL + # + # Sonification follows phone strategy if in call (widely speaking) + # + ForceUseForCommunication Is ForceSpeaker + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Excludes Line + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + speaker = 1 + + conf: NotSelected + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + speaker = 0 + + domain: Device2 + + conf: BluetoothA2dp + # + # Sonification falls through media strategy if not in call (widely speaking) + # + AvailableOutputDevices Includes BluetoothA2dp + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: BluetoothA2dpHeadphones + # + # Sonification falls through media strategy if not in call (widely speaking) + # + AvailableOutputDevices Includes BluetoothA2dpHeadphones + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: BluetoothA2dpSpeaker + # + # Sonification falls through media strategy if not in call (widely speaking) + # + AvailableOutputDevices Includes BluetoothA2dpSpeaker + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: ScoCarkit + # + # Sonification follows phone strategy if in call (widely speaking) + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothScoCarkit + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 1 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: ScoHeadset + # + # Sonification follows phone strategy if in call (widely speaking) + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothScoHeadset + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 1 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: Sco + # + # Sonification follows phone strategy if in call (widely speaking) + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothSco + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 1 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: WiredHeadphone + AvailableOutputDevices Includes WiredHeadphone + ANY + # + # Sonification falls through media strategy if not in call (widely speaking) + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + # + # Sonification follows Phone strategy if in call (widely speaking) + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 1 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: Line + AvailableOutputDevices Includes Line + ANY + # + # Sonification follows Phone strategy if in call (widely speaking) + # but Line has a lower priority than WiredHeadset in this case. + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Excludes WiredHeadset + # + # Sonification falls through media strategy if not in call (widely speaking) + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 1 + + conf: WiredHeadset + AvailableOutputDevices Includes WiredHeadset + ANY + # + # Sonification falls through media strategy if not in call (widely speaking) + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + ALL + # + # Sonification Follows Phone Strategy if in call (widely speaking) + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 1 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: UsbDevice + AvailableOutputDevices Includes UsbDevice + ANY + # + # Sonification falls through media strategy if not in call (widely speaking) + # + ALL + AvailableOutputDevices Excludes UsbAccessory + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + ALL + # + # Sonification Follows Phone Strategy if in call (widely speaking) + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + telephony_tx = 0 + line = 0 + + conf: UsbAccessory + AvailableOutputDevices Includes UsbAccessory + # + # Sonification falls through media strategy if not in call (widely speaking) + # + # Sonification Follows Phone Strategy if in call (widely speaking) + # but USB Accessory not reachable in call. + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 1 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: DgtlDockHeadset + AvailableOutputDevices Includes DgtlDockHeadset + # + # Sonification falls through media strategy if not in call + # + # Sonification Follows Phone Strategy if in call (widely speaking) + # but DgtlDockHeadset not reachable in call. + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: AnlgDockHeadset + AvailableOutputDevices Includes AnlgDockHeadset + # + # Sonification falls through media strategy if not in call + # + # Sonification Follows Phone Strategy if in call (widely speaking) + # but AnlgDockHeadset not reachable in call. + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + ForceUseForDock Is ForceAnalogDock + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: Earpiece + # + # Sonification Follows Phone Strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Includes Earpiece + + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 1 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + + conf: None + component: /Policy/policy/product_strategies/sonification/selected_output_devices/mask + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw new file mode 100644 index 0000000000..0a3dd5f7e4 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_sonification_respectful.pfw @@ -0,0 +1,536 @@ +supDomain: DeviceForProductStrategy + supDomain: SonificationRespectful + # + # Sonificiation Respectful follows: + # - If in call: Strategy sonification (that follows phone strategy in call also...) + # - If not in call AND a music stream is active remotely: Strategy sonification (that + # follows enforced audible, which follows media) + # - if not in call and no music stream active remotely and music stream active): strategy + # media + # - Otherwise follows sonification by replacing speaker with speaker safe if speaker is + # selected. + # + # Case of stream active handled programmatically + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + remote_submix = 0 + hdmi_arc = 0 + aux_line = 0 + spdif = 0 + fm = 0 + telephony_tx = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/sonification_respectful/device_address = + + domain: Speakers + + conf: SpeakerSafe + AvailableOutputDevices Includes Speaker + AvailableOutputDevices Includes SpeakerSafe + # + # Follows sonification strategy if not in call and replace speaker by speaker safe + # if and only if speaker only selected + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + speaker_safe = 1 + speaker = 0 + + conf: Speaker + AvailableOutputDevices Includes Speaker + ANY + # + # Follows sonification strategy if not in call + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ALL + # + # Follows Phone Strategy if call + # + ForceUseForCommunication Is ForceSpeaker + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + AvailableOutputDevices Excludes Line + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + speaker_safe = 0 + speaker = 1 + + conf: None + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + speaker_safe = 0 + speaker = 0 + + domain: Device2 + conf: BluetoothA2dp + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dp + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 1 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothA2dpHeadphones + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dpHeadphones + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 1 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothA2dpSpeaker + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceNoBtA2dp + AvailableOutputDevices Includes BluetoothA2dpSpeaker + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 1 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothScoCarkit + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothScoCarkit + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 1 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothScoHeadset + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothScoHeadset + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 1 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: BluetoothSco + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication Is ForceBtSco + AvailableOutputDevices Includes BluetoothSco + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 1 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: WiredHeadphone + ANY + ALL + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + ALL + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes WiredHeadphone + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 1 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Line + ANY + # + # SonificationRespectful Follows Phone strategy if in call + # but Line has a lower priority than WiredHeadset in this case. + # + # + ALL + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Excludes WiredHeadset + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + ALL + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + AvailableOutputDevices Includes WiredHeadphone + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes Line + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 1 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: WiredHeadset + ANY + ALL + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + ALL + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes WiredHeadset + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 1 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: UsbDevice + ANY + ALL + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + ALL + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Excludes UsbAccessory + AvailableOutputDevices Includes UsbDevice + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 1 + hdmi = 0 + + conf: UsbAccessory + # + # SonificationRespectful Follows Phone strategy if in call (widely speaking) + # but UsbAccessory not reachable in call. + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes UsbAccessory + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 1 + usb_device = 0 + hdmi = 0 + + conf: DgtlDockHeadset + # + # SonificationRespectful Follows Phone strategy if in call (widely speaking) + # but DgtlDockHeadset not reachable in call. + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes DgtlDockHeadset + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 1 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: AuxDigital + # + # SonificationRespectful Follows Phone strategy if in call (widely speaking) + # but HDMI not reachable in call. + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + AvailableOutputDevices Includes Hdmi + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 1 + + conf: AnlgDockHeadset + # + # SonificationRespectful Follows Phone strategy if in call (widely speaking) + # but AnlgDockHeadset not reachable in call. + # + # SonificationRespectful Follows Sonification that falls through Media strategy if not in call + # SonificationRespectful follows media if music stream is active + # + TelephonyMode IsNot InCall + TelephonyMode IsNot InCommunication + ForceUseForMedia IsNot ForceSpeaker + ForceUseForDock Is ForceAnalogDock + AvailableOutputDevices Includes AnlgDockHeadset + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 1 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + + conf: Earpiece + # + # SonificationRespectful Follows Phone strategy if in call + # + ANY + TelephonyMode Is InCall + TelephonyMode Is InCommunication + ForceUseForCommunication IsNot ForceSpeaker + AvailableOutputDevices Includes Earpiece + + component: /Policy/policy/product_strategies/sonification_respectful/selected_output_devices/mask + earpiece = 1 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + bluetooth_a2dp = 0 + wired_headset = 0 + wired_headphone = 0 + line = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + hdmi = 0 + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw new file mode 100644 index 0000000000..3fc7670415 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_transmitted_through_speaker.pfw @@ -0,0 +1,43 @@ +supDomain: DeviceForProductStrategy + supDomain: TransmittedThroughSpeaker + domain: UnreacheableDevices + conf: Calibration + component: /Policy/policy/product_strategies/transmitted_through_speaker/selected_output_devices/mask + remote_submix = 0 + hdmi_arc = 0 + spdif = 0 + aux_line = 0 + fm = 0 + speaker_safe = 0 + earpiece = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + telephony_tx = 0 + line = 0 + ip = 0 + bus = 0 + stub = 0 + /Policy/policy/product_strategies/transmitted_through_speaker/device_address = + + domain: Speaker + conf: Selected + AvailableOutputDevices Includes Speaker + + component: /Policy/policy/product_strategies/transmitted_through_speaker/selected_output_devices/mask + speaker = 1 + + conf: NotSelected + component: /Policy/policy/product_strategies/transmitted_through_speaker/selected_output_devices/mask + speaker = 0 + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw new file mode 100644 index 0000000000..c46cf568ac --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw @@ -0,0 +1,36 @@ +supDomain: DeviceForProductStrategy + supDomain: Unknown + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/unknown/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + bus = 0 + stub = 0 + /Policy/policy/product_strategies/unknown/device_address = + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml new file mode 100644 index 0000000000..9f999b38d1 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml new file mode 100644 index 0000000000..4cbb3da197 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml index daa7f689e2..31973875f1 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml @@ -202,4 +202,14 @@ + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index a37db1ee74..e7a56e1c18 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -11,7 +11,8 @@ LOCAL_SRC_FILES := \ Strategy.cpp \ InputSource.cpp \ Stream.cpp \ - Usage.cpp + Usage.cpp \ + ProductStrategy.cpp LOCAL_CFLAGS += \ -Wall \ @@ -22,7 +23,7 @@ LOCAL_CFLAGS += \ LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy/engineconfigurable/include \ - frameworks/av/services/audiopolicy/engineconfigurable/interface \ + frameworks/av/services/audiopolicy/engineconfigurable/interface LOCAL_SHARED_LIBRARIES := \ libaudiopolicyengineconfigurable \ @@ -31,7 +32,9 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon + libaudiopolicycommon \ + libaudioclient_headers \ + libbase_headers LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp index 7374fc3fe5..ba6063a616 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp @@ -21,6 +21,7 @@ #include "Stream.h" #include "InputSource.h" #include "Usage.h" +#include "ProductStrategy.h" #include #include #include @@ -39,6 +40,7 @@ const char *const PolicySubsystem::mStreamComponentName = "Stream"; const char *const PolicySubsystem::mStrategyComponentName = "Strategy"; const char *const PolicySubsystem::mInputSourceComponentName = "InputSource"; const char *const PolicySubsystem::mUsageComponentName = "Usage"; +const char *const PolicySubsystem::mProductStrategyComponentName = "ProductStrategy"; PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &logger) : CSubsystem(name, logger), @@ -82,6 +84,10 @@ PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &log mInputSourceComponentName, (1 << MappingKeyName)) ); + addSubsystemObjectFactory( + new TSubsystemObjectFactory( + mProductStrategyComponentName, 0) + ); } // Retrieve Route interface diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h index 822eeb9c22..72f3252263 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h @@ -56,4 +56,5 @@ private: static const char *const mStrategyComponentName; static const char *const mInputSourceComponentName; static const char *const mUsageComponentName; + static const char *const mProductStrategyComponentName; }; diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp new file mode 100644 index 0000000000..bb29ef108c --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#include "ProductStrategy.h" +#include "PolicyMappingKeys.h" +#include "PolicySubsystem.h" + +using std::string; +using android::product_strategy_t; + +ProductStrategy::ProductStrategy(const string &mappingValue, + CInstanceConfigurableElement *instanceConfigurableElement, + const CMappingContext &context, + core::log::Logger& logger) + : CFormattedSubsystemObject(instanceConfigurableElement, + logger, + mappingValue, + MappingKeyAmend1, + (MappingKeyAmendEnd - MappingKeyAmend1 + 1), + context) +{ + ALOG_ASSERT(instanceConfigurableElement != nullptr, "Invalid Configurable Element"); + mPolicySubsystem = static_cast( + instanceConfigurableElement->getBelongingSubsystem()); + ALOG_ASSERT(mPolicySubsystem != nullptr, "Invalid Policy Subsystem"); + + mPolicyPluginInterface = mPolicySubsystem->getPolicyPluginInterface(); + ALOG_ASSERT(mPolicyPluginInterface != nullptr, "Invalid Policy Plugin Interface"); + + std::string name(instanceConfigurableElement->getName()); + mId = mPolicyPluginInterface->getProductStrategyByName(name); + + ALOG_ASSERT(mId != PRODUCT_STRATEGY_INVALID, "Product Strategy %s not found", name.c_str()); + + ALOGE("Product Strategy %s added", name.c_str()); +} + +bool ProductStrategy::sendToHW(string & /*error*/) +{ + Device deviceParams; + blackboardRead(&deviceParams, sizeof(deviceParams)); + + mPolicyPluginInterface->setDeviceTypesForProductStrategy(mId, deviceParams.applicableDevice); + mPolicyPluginInterface->setDeviceAddressForProductStrategy(mId, deviceParams.deviceAddress); + return true; +} diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h new file mode 100644 index 0000000000..244f0823e0 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#pragma once + +#include "FormattedSubsystemObject.h" +#include "InstanceConfigurableElement.h" +#include "MappingContext.h" +#include +#include +#include + +class PolicySubsystem; + +class ProductStrategy : public CFormattedSubsystemObject +{ +private: + static const uint32_t mMaxStringSize = 257; /**< max string size (plus zero terminal). */ + + struct Device + { + uint32_t applicableDevice; /**< applicable device for this strategy. */ + char deviceAddress[mMaxStringSize]; /**< device address associated with this strategy. */ + } __attribute__((packed)); + +public: + ProductStrategy(const std::string &mappingValue, + CInstanceConfigurableElement *instanceConfigurableElement, + const CMappingContext &context, + core::log::Logger& logger); + +protected: + virtual bool sendToHW(std::string &error); + +private: + const PolicySubsystem *mPolicySubsystem; /**< Route subsytem plugin. */ + + android::product_strategy_t mId; + + /** + * Interface to communicate with Audio Policy Engine. + */ + android::AudioPolicyPluginInterface *mPolicyPluginInterface; +}; diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 815de25330..caa1ec9252 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -353,6 +353,27 @@ void Engine::updateDeviceSelectionCache() } } +void Engine::setDeviceAddressForProductStrategy(product_strategy_t strategy, + const std::string &address) +{ + if (getProductStrategies().find(strategy) == getProductStrategies().end()) { + ALOGE("%s: Trying to set address %s on invalid strategy %d", __FUNCTION__, address.c_str(), + strategy); + return; + } + getProductStrategies().at(strategy)->setDeviceAddress(address); +} + +bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio_devices_t devices) +{ + if (getProductStrategies().find(strategy) == getProductStrategies().end()) { + ALOGE("%s: set device %d on invalid strategy %d", __FUNCTION__, devices, strategy); + return false; + } + getProductStrategies().at(strategy)->setDeviceTypes(devices); + return true; +} + template <> AudioPolicyManagerInterface *Engine::queryInterface() { diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h index aebf27d798..05fecf0ac0 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.h +++ b/services/audiopolicy/engineconfigurable/src/Engine.h @@ -119,6 +119,16 @@ public: { return setPropertyForKey(device, inputSource); } + void setDeviceAddressForProductStrategy(product_strategy_t strategy, + const std::string &address) override; + + bool setDeviceTypesForProductStrategy(product_strategy_t strategy, + audio_devices_t devices) override; + + product_strategy_t getProductStrategyByName(const std::string &name) override + { + return EngineBase::getProductStrategyByName(name); + } private: /* Copy facilities are put private to disable copy. */ diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk index b71a53c724..7e93a9970d 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk @@ -25,7 +25,6 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libparameter \ libmedia_helper \ - libicuuc \ libxml2 LOCAL_HEADER_LIBRARIES := \ diff --git a/services/audiopolicy/enginedefault/config/Android.mk b/services/audiopolicy/enginedefault/config/Android.mk new file mode 100644 index 0000000000..dcce8e3d28 --- /dev/null +++ b/services/audiopolicy/enginedefault/config/Android.mk @@ -0,0 +1,9 @@ + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +####################################################################### +# Recursive call sub-folder Android.mk +# +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk new file mode 100644 index 0000000000..04561b8ab9 --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH := $(call my-dir) + +################################################################## +# CONFIGURATION TOP FILE +################################################################## + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_configuration_phone.xml +LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) + +LOCAL_REQUIRED_MODULES := \ + audio_policy_engine_product_strategies_phone.xml + +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_product_strategies_phone.xml +LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +include $(BUILD_PREBUILT) + +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml new file mode 100644 index 0000000000..ab61d8a937 --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml new file mode 100644 index 0000000000..f72e37916d --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From 7188f1ad365cd9d475723b639d521b491eaca389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Fri, 2 Nov 2018 14:35:42 +0100 Subject: [PATCH 0874/1530] audiopolicy: engineconfigurable: Merge Policy Engine and Wrapper configuration files Test: make Change-Id: I0a905752218438378d9ca87457cd55f6cd0f2586 --- .../engine/common/src/EngineDefaultConfig.h | 4 +- .../engine/config/include/EngineConfig.h | 23 ++ .../engine/config/src/EngineConfig.cpp | 116 +++++++++- .../config/example/Android.mk | 34 ++- .../audio_policy_engine_configuration.xml | 2 + .../common/audio_policy_engine_criteria.xml} | 0 ...udio_policy_engine_criterion_types.xml.in} | 0 .../parameter-framework/examples/Android.mk | 37 ++-- .../examples/Car/Android.mk | 56 +---- .../examples/Phone/Android.mk | 12 +- .../engineconfigurable/src/Engine.cpp | 19 ++ .../engineconfigurable/wrapper/Android.mk | 44 +--- .../wrapper/ParameterManagerWrapper.cpp | 74 ++----- .../wrapper/ParameterManagerWrapperConfig.cpp | 208 ------------------ .../wrapper/ParameterManagerWrapperConfig.h | 139 ------------ .../config/policy_wrapper_configuration.xml | 25 --- .../wrapper/include/ParameterManagerWrapper.h | 21 +- .../enginedefault/config/example/Android.mk | 4 +- 18 files changed, 268 insertions(+), 550 deletions(-) rename services/audiopolicy/engineconfigurable/{wrapper/config/policy_criteria.xml => config/example/common/audio_policy_engine_criteria.xml} (100%) rename services/audiopolicy/engineconfigurable/{wrapper/config/policy_criterion_types.xml.in => config/example/common/audio_policy_engine_criterion_types.xml.in} (100%) delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 5f546b3433..3940c0c929 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -133,6 +133,8 @@ const engineConfig::ProductStrategies gOrderedStrategies = { const engineConfig::Config gDefaultEngineConfig = { 1.0, - gOrderedStrategies + gOrderedStrategies, + {}, + {} }; } // namespace android diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index 64b95261e8..e18f68759d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -52,9 +52,32 @@ struct ProductStrategy { using ProductStrategies = std::vector; +using ValuePair = std::pair; +using ValuePairs = std::vector; + +struct CriterionType +{ + std::string name; + bool isInclusive; + ValuePairs valuePairs; +}; + +using CriterionTypes = std::vector; + +struct Criterion +{ + std::string name; + std::string typeName; + std::string defaultLiteralValue; +}; + +using Criteria = std::vector; + struct Config { float version; ProductStrategies productStrategies; + Criteria criteria; + CriterionTypes criterionTypes; }; /** Result of `parse(const char*)` */ diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 26dde663ad..3aa38cf965 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -70,6 +70,43 @@ struct ProductStrategyTraits : public BaseSerializerTraits { + static constexpr const char *tag = "value"; + static constexpr const char *collectionTag = "values"; + + struct Attributes { + static constexpr const char *literal = "literal"; + static constexpr const char *numerical = "numerical"; + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; +struct CriterionTypeTraits : public BaseSerializerTraits { + static constexpr const char *tag = "criterion_type"; + static constexpr const char *collectionTag = "criterion_types"; + + struct Attributes { + static constexpr const char *name = "name"; + static constexpr const char *type = "type"; + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; +struct CriterionTraits : public BaseSerializerTraits { + static constexpr const char *tag = "criterion"; + static constexpr const char *collectionTag = "criteria"; + + struct Attributes { + static constexpr const char *name = "name"; + static constexpr const char *type = "type"; + static constexpr const char *defaultVal = "default"; + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; using xmlCharUnique = std::unique_ptr; @@ -254,6 +291,80 @@ status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, return NO_ERROR; } +status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values) +{ + std::string literal = getXmlAttribute(child, Attributes::literal); + if (literal.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); + return BAD_VALUE; + } + uint32_t numerical = 0; + std::string numericalTag = getXmlAttribute(child, Attributes::numerical); + if (numericalTag.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); + return BAD_VALUE; + } + if (!convertTo(numericalTag, numerical)) { + ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str()); + return BAD_VALUE; + } + values.push_back({numerical, literal}); + return NO_ERROR; +} + +status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, + Collection &criterionTypes) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str()); + + std::string type = getXmlAttribute(child, Attributes::type); + if (type.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type); + return BAD_VALUE; + } + ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str()); + bool isInclusive(type == "inclusive"); + + ValuePairs pairs; + size_t nbSkippedElements = 0; + deserializeCollection(doc, child, pairs, nbSkippedElements); + criterionTypes.push_back({name, isInclusive, pairs}); + return NO_ERROR; +} + +status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, + Collection &criteria) +{ + std::string name = getXmlAttribute(child, Attributes::name); + if (name.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); + + std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal); + if (defaultValue.empty()) { + // Not mandatory to provide a default value for a criterion, even it is recommanded... + ALOGV("%s: No attribute %s found (but recommanded)", __FUNCTION__, Attributes::defaultVal); + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str()); + + std::string typeName = getXmlAttribute(child, Attributes::type); + if (typeName.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); + return BAD_VALUE; + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str()); + + criteria.push_back({name, typeName, defaultValue}); + return NO_ERROR; +} + status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, Collection &strategies) { @@ -299,7 +410,10 @@ ParsingResult parse(const char* path) { config->version = std::stof(version); deserializeCollection( doc, cur, config->productStrategies, nbSkippedElements); - + deserializeCollection( + doc, cur, config->criteria, nbSkippedElements); + deserializeCollection( + doc, cur, config->criterionTypes, nbSkippedElements); return {std::move(config), nbSkippedElements}; } diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index caf8afa108..95a2ecc08d 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -1,5 +1,8 @@ LOCAL_PATH := $(call my-dir) +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +PROVISION_CRITERION_TYPES := $(TOOLS)/provision_criterion_types_from_android_headers.mk + ################################################################## # CONFIGURATION TOP FILE ################################################################## @@ -16,7 +19,9 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml + audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml include $(BUILD_PREBUILT) @@ -48,6 +53,8 @@ LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ audio_policy_engine_product_strategies_automotive.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml include $(BUILD_PREBUILT) @@ -65,3 +72,28 @@ LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_criteria.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := common/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_criterion_types.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml + +ANDROID_AUDIO_BASE_HEADER_FILE := system/media/audio/include/system/audio-base.h +AUDIO_POLICY_CONFIGURATION_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml +CRITERION_TYPES_FILE := $(LOCAL_PATH)/common/$(LOCAL_MODULE).in + +include $(PROVISION_CRITERION_TYPES) + +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml index ab61d8a937..e2fb02b140 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criteria.xml similarity index 100% rename from services/audiopolicy/engineconfigurable/wrapper/config/policy_criteria.xml rename to services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criteria.xml diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in b/services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in similarity index 100% rename from services/audiopolicy/engineconfigurable/wrapper/config/policy_criterion_types.xml.in rename to services/audiopolicy/engineconfigurable/config/example/common/audio_policy_engine_criterion_types.xml.in diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index a1ebd35bce..bf7ebb69bc 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -9,7 +9,7 @@ LOCAL_PATH := $(call my-dir) -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable no-output_configurable no-input_configurable)) PFW_CORE := external/parameter-framework #@TODO: upstream new domain generator @@ -20,11 +20,15 @@ PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk +endif + ################################################################## # CONFIGURATION FILES ################################################################## ######### Policy PFW top level file ######### +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) + include $(CLEAR_VARS) LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml LOCAL_MODULE_TAGS := optional @@ -65,10 +69,11 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) -endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),1) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) ########## Policy PFW Example Structures ######### -ifeq (0, 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) + include $(CLEAR_VARS) LOCAL_MODULE := PolicySubsystem.xml.common LOCAL_MODULE_STEM := PolicySubsystem.xml @@ -84,10 +89,10 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) -endif # ifeq (0, 1) +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) ######### Policy PFW Settings - No Output ######### -ifeq (0, 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable) include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.no-output @@ -96,15 +101,15 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - policy_criteria.xml \ - policy_criterion_types.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml \ PolicySubsystem.xml.common \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml -PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoOutput/device_for_strategies.pfw \ $(LOCAL_PATH)/Settings/strategy_for_stream.pfw \ @@ -113,9 +118,9 @@ PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/volumes.pfw include $(BUILD_PFW_SETTINGS) -endif # ifeq (0, 1) +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable) ######### Policy PFW Settings - No Input ######### -ifeq (0, 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.no-input @@ -124,15 +129,15 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ - policy_criteria.xml \ - policy_criterion_types.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml \ PolicySubsystem.xml.common \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml -PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ $(LOCAL_PATH)/Settings/device_for_strategy_phone.pfw \ @@ -150,7 +155,7 @@ PFW_EDD_FILES := \ include $(BUILD_PFW_SETTINGS) -endif #ifeq (0, 1) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) ####################################################################### # Recursive call sub-folder Android.mk ####################################################################### diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk index 01839dd3ff..b341520d71 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -7,7 +7,7 @@ # ################################################################################################ -ifeq ($(BUILD_AUDIO_POLICY_AUTOMOTIVE_CONFIGURATION), 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) LOCAL_PATH := $(call my-dir) PFW_CORE := external/parameter-framework @@ -22,48 +22,6 @@ BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk # CONFIGURATION FILES ################################################################## -######### Policy PFW top level file ######### - -include $(CLEAR_VARS) -LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml.car -LOCAL_MODULE_STEM := ParameterFrameworkConfigurationPolicy.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework -LOCAL_SRC_FILES := ../$(LOCAL_MODULE_STEM).in - -AUDIO_PATTERN = @TUNING_ALLOWED@ -ifeq ($(TARGET_BUILD_VARIANT),user) -AUDIO_VALUE = false -else -AUDIO_VALUE = true -endif - -LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE_STEM) - -include $(BUILD_PREBUILT) - -########## Policy PFW Common Structures ######### - -include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem-CommonTypes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := ../Structure/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := PolicyClass.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := ../Structure/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - ########## Policy PFW Structures ######### include $(CLEAR_VARS) @@ -123,12 +81,12 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \ LOCAL_REQUIRED_MODULES := \ PolicySubsystem.xml.car \ PolicyClass.xml \ - policy_criteria.xml \ - policy_criterion_types.xml \ - ParameterFrameworkConfigurationPolicy.xml.car + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml \ + ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml -PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml @@ -136,4 +94,4 @@ PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) include $(BUILD_PFW_SETTINGS) -endif #ifeq ($(BUILD_AUDIO_POLICY_AUTOMOTIVE_CONFIGURATION), 1) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk index bc91c6fcd8..29296e7566 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -7,7 +7,7 @@ # ################################################################################################ -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) LOCAL_PATH := $(call my-dir) @@ -89,12 +89,12 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \ LOCAL_REQUIRED_MODULES := \ PolicySubsystem.xml.phone \ PolicyClass.xml \ - policy_criteria.xml \ - policy_criterion_types.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml \ ParameterFrameworkConfigurationPolicy.xml -PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criterion_types.xml -PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/policy_criteria.xml +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml @@ -102,4 +102,4 @@ PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) include $(BUILD_PFW_SETTINGS) -endif +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index caa1ec9252..a04254c450 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -237,6 +237,25 @@ status_t Engine::loadAudioPolicyEngineConfig() { auto result = EngineBase::loadAudioPolicyEngineConfig(); + // Custom XML Parsing + auto loadCriteria= [this](const auto& configCriteria, const auto& configCriterionTypes) { + for (auto& criterion : configCriteria) { + engineConfig::CriterionType criterionType; + for (auto &configCriterionType : configCriterionTypes) { + if (configCriterionType.name == criterion.typeName) { + criterionType = configCriterionType; + break; + } + } + ALOG_ASSERT(not criterionType.name.empty(), "Invalid criterion type for %s", + criterion.name.c_str()); + mPolicyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive, + criterionType.valuePairs, + criterion.defaultLiteralValue); + } + }; + + loadCriteria(result.parsedConfig->criteria, result.parsedConfig->criterionTypes); return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE; } diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk index 7e93a9970d..c7d8d34f10 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk @@ -1,8 +1,5 @@ LOCAL_PATH:= $(call my-dir) -TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools -PROVISION_CRITERION_TYPES := $(TOOLS)/provision_criterion_types_from_android_headers.mk - ################################################################## # WRAPPER LIBRARY ################################################################## @@ -13,14 +10,11 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ frameworks/av/services/audiopolicy/engineconfigurable/include \ frameworks/av/services/audiopolicy/engineconfigurable/interface \ - frameworks/av/services/audiopolicy/common/include \ - frameworks/av/services/audiopolicy/utilities/convert \ external/libxml2/include \ external/icu/icu4c/source/common LOCAL_SRC_FILES:= \ - ParameterManagerWrapper.cpp \ - ParameterManagerWrapperConfig.cpp + ParameterManagerWrapper.cpp LOCAL_SHARED_LIBRARIES := \ libparameter \ @@ -43,39 +37,3 @@ LOCAL_CFLAGS := -Wall -Werror -Wextra include $(BUILD_STATIC_LIBRARY) -################################################################## -# CONFIGURATION FILE -################################################################## - -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) - -include $(CLEAR_VARS) -LOCAL_MODULE := policy_wrapper_configuration.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := config/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := policy_criteria.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := config/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := policy_criterion_types.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml - -AUDIO_POLICY_CONFIGURATION_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml -ANDROID_AUDIO_BASE_HEADER_FILE := system/media/audio/include/system/audio-base.h -CRITERION_TYPES_FILE := $(LOCAL_PATH)/config/policy_criterion_types.xml.in - -include $(PROVISION_CRITERION_TYPES) - -endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), 1) diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp index a3f341f93c..4b574443ea 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp +++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp @@ -18,7 +18,6 @@ //#define LOG_NDEBUG 0 #include "ParameterManagerWrapper.h" -#include "ParameterManagerWrapperConfig.h" #include #include #include @@ -38,7 +37,6 @@ using std::string; using std::map; using std::vector; -using CriterionTypes = std::map; /// PFW related definitions // Logger @@ -106,63 +104,35 @@ ParameterManagerWrapper::ParameterManagerWrapper() // Logger mPfwConnector->setLogger(mPfwConnectorLogger); - - status_t loadResult = loadConfig(); - if (loadResult < 0) { - ALOGE("Policy Wrapper configuration is partially invalid."); - } } -status_t ParameterManagerWrapper::loadConfig() +status_t ParameterManagerWrapper::addCriterion(const std::string &name, bool isInclusive, + ValuePairs pairs, const std::string &defaultValue) { - auto result = wrapper_config::parse(); - if (result.parsedConfig == nullptr) { - return -ENOENT; - } - ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); - - CriterionTypes criterionTypes; - for (auto criterionType : result.parsedConfig->criterionTypes) { - ALOG_ASSERT(criterionTypes.find(criterionType.name) == criterionTypes.end(), - "CriterionType %s already added", criterionType.name.c_str()); - ALOGV("%s: Adding new criterionType %s", __FUNCTION__, criterionType.name.c_str()); - - auto criterionTypePfw = - mPfwConnector->createSelectionCriterionType(criterionType.isInclusive); - - for (auto pair : criterionType.valuePairs) { - std::string error; - ALOGV("%s: Adding pair %d,%s for criterionType %s", __FUNCTION__, pair.first, - pair.second.c_str(), criterionType.name.c_str()); - criterionTypePfw->addValuePair(pair.first, pair.second, error); - } - criterionTypes[criterionType.name] = criterionTypePfw; + ALOG_ASSERT(not isStarted(), "Cannot add a criterion if PFW is already started"); + auto criterionType = mPfwConnector->createSelectionCriterionType(isInclusive); + + for (auto pair : pairs) { + std::string error; + ALOGV("%s: Adding pair %d,%s for criterionType %s", __FUNCTION__, pair.first, + pair.second.c_str(), name.c_str()); + criterionType->addValuePair(pair.first, pair.second, error); } + ALOG_ASSERT(mPolicyCriteria.find(name) == mPolicyCriteria.end(), + "%s: Criterion %s already added", __FUNCTION__, name.c_str()); + + auto criterion = mPfwConnector->createSelectionCriterion(name, criterionType); + mPolicyCriteria[name] = criterion; - for (auto criterion : result.parsedConfig->criteria) { - ALOG_ASSERT(mPolicyCriteria.find(criterion.name) == mPolicyCriteria.end(), - "%s: Criterion %s already added", __FUNCTION__, criterion.name.c_str()); - - auto criterionType = - getElement(criterion.typeName, criterionTypes); - ALOG_ASSERT(criterionType != nullptr, "No %s Criterion type found for criterion %s", - criterion.typeName.c_str(), criterion.name.c_str()); - - auto criterionPfw = mPfwConnector->createSelectionCriterion(criterion.name, criterionType); - mPolicyCriteria[criterion.name] = criterionPfw; - - if (not criterion.defaultLiteralValue.empty()) { - int numericalValue = 0; - if (not criterionType->getNumericalValue(criterion.defaultLiteralValue.c_str(), - numericalValue)) { - ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__, - criterion.defaultLiteralValue.c_str()); - continue; - } - criterionPfw->setCriterionState(numericalValue); + if (not defaultValue.empty()) { + int numericalValue = 0; + if (not criterionType->getNumericalValue(defaultValue.c_str(), numericalValue)) { + ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__, + defaultValue.c_str()); } + criterion->setCriterionState(numericalValue); } - return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE; + return NO_ERROR; } ParameterManagerWrapper::~ParameterManagerWrapper() diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp deleted file mode 100644 index bc6d046f31..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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 "APM::AudioPolicyEngine/PFWWrapperConfig" -#define LOG_NDEBUG 0 - -#include "ParameterManagerWrapperConfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace android { - -using utilities::convertTo; - -namespace audio_policy { -namespace wrapper_config { -namespace detail { - -std::string getXmlAttribute(const xmlNode *cur, const char *attribute) -{ - xmlChar *xmlValue = xmlGetProp(cur, (const xmlChar *)attribute); - if (xmlValue == NULL) { - return ""; - } - std::string value((const char *)xmlValue); - xmlFree(xmlValue); - return value; -} - -template -static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur, - typename Trait::Collection &collection, - size_t &nbSkippedElement) -{ - const xmlNode *root = cur->xmlChildrenNode; - while (root != NULL) { - if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) && - xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { - root = root->next; - continue; - } - const xmlNode *child = root; - if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) { - child = child->xmlChildrenNode; - } - while (child != NULL) { - if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) { - status_t status = Trait::deserialize(doc, child, collection); - if (status == NO_ERROR) { - nbSkippedElement += 1; - } - } - child = child->next; - } - if (!xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) { - return NO_ERROR; - } - root = root->next; - } - return NO_ERROR; -} - -const char *const ValueTraits::tag = "value"; -const char *const ValueTraits::collectionTag = "values"; - -const char ValueTraits::Attributes::literal[] = "literal"; -const char ValueTraits::Attributes::numerical[] = "numerical"; - -status_t ValueTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &values) -{ - std::string literal = getXmlAttribute(child, Attributes::literal); - if (literal.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); - return BAD_VALUE; - } - uint32_t numerical = 0; - std::string numericalTag = getXmlAttribute(child, Attributes::numerical); - if (numericalTag.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal); - return BAD_VALUE; - } - if (!convertTo(numericalTag, numerical)) { - ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str()); - return BAD_VALUE; - } - values.push_back({numerical, literal}); - return NO_ERROR; -} - -const char *const CriterionTypeTraits::tag = "criterion_type"; -const char *const CriterionTypeTraits::collectionTag = "criterion_types"; - -const char CriterionTypeTraits::Attributes::name[] = "name"; -const char CriterionTypeTraits::Attributes::type[] = "type"; - -status_t CriterionTypeTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, - Collection &criterionTypes) -{ - std::string name = getXmlAttribute(child, Attributes::name); - if (name.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); - return BAD_VALUE; - } - ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::name, name.c_str()); - - std::string type = getXmlAttribute(child, Attributes::type); - if (type.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::type); - return BAD_VALUE; - } - ALOGV("%s: %s %s = %s", __FUNCTION__, tag, Attributes::type, type.c_str()); - bool isInclusive(type == "inclusive"); - - ValuePairs pairs; - size_t nbSkippedElements = 0; - detail::deserializeCollection(doc, child, pairs, nbSkippedElements); - - criterionTypes.push_back({name, isInclusive, pairs}); - return NO_ERROR; -} - -const char *const CriterionTraits::tag = "criterion"; -const char *const CriterionTraits::collectionTag = "criteria"; - -const char CriterionTraits::Attributes::name[] = "name"; -const char CriterionTraits::Attributes::type[] = "type"; -const char CriterionTraits::Attributes::defaultVal[] = "default"; - -status_t CriterionTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *child, Collection &criteria) -{ - std::string name = getXmlAttribute(child, Attributes::name); - if (name.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); - return BAD_VALUE; - } - ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); - - std::string defaultValue = getXmlAttribute(child, Attributes::defaultVal); - if (defaultValue.empty()) { - // Not mandatory to provide a default value for a criterion, even it is recommanded... - ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::defaultVal); - } - ALOGV("%s: %s = %s", __FUNCTION__, Attributes::defaultVal, defaultValue.c_str()); - - std::string typeName = getXmlAttribute(child, Attributes::type); - if (typeName.empty()) { - ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::name); - return BAD_VALUE; - } - ALOGV("%s: %s = %s", __FUNCTION__, Attributes::type, typeName.c_str()); - - criteria.push_back({name, typeName, defaultValue}); - return NO_ERROR; -} -} // namespace detail - -ParsingResult parse(const char* path) { - xmlDocPtr doc; - doc = xmlParseFile(path); - if (doc == NULL) { - ALOGE("%s: Could not parse document %s", __FUNCTION__, path); - return {nullptr, 0}; - } - xmlNodePtr cur = xmlDocGetRootElement(doc); - if (cur == NULL) { - ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); - xmlFreeDoc(doc); - return {nullptr, 0}; - } - if (xmlXIncludeProcess(doc) < 0) { - ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); - return {nullptr, 0}; - } - size_t nbSkippedElements = 0; - auto config = std::make_unique(); - - detail::deserializeCollection( - doc, cur, config->criteria, nbSkippedElements); - detail::deserializeCollection( - doc, cur, config->criterionTypes, nbSkippedElements); - - return {std::move(config), nbSkippedElements}; -} - -} // namespace wrapper_config -} // namespace audio_policy -} // namespace android diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h deleted file mode 100644 index 467d0e11fb..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapperConfig.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include -#include -#include -#include - -struct _xmlNode; -struct _xmlDoc; - -namespace android { -namespace audio_policy { -namespace wrapper_config { - -/** Default path of audio policy usages configuration file. */ -constexpr char DEFAULT_PATH[] = "/vendor/etc/policy_wrapper_configuration.xml"; - -/** Directories where the effect libraries will be search for. */ -constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"}; - -using ValuePair = std::pair; -using ValuePairs = std::vector; - -struct CriterionType -{ - std::string name; - bool isInclusive; - ValuePairs valuePairs; -}; - -using CriterionTypes = std::vector; - -struct Criterion -{ - std::string name; - std::string typeName; - std::string defaultLiteralValue; -}; - -using Criteria = std::vector; - -struct Config { - float version; - Criteria criteria; - CriterionTypes criterionTypes; -}; - -namespace detail -{ -struct ValueTraits -{ - static const char *const tag; - static const char *const collectionTag; - - struct Attributes - { - static const char literal[]; - static const char numerical[]; - }; - - typedef ValuePair Element; - typedef ValuePair *PtrElement; - typedef ValuePairs Collection; - - static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, - Collection &collection); -}; - -struct CriterionTypeTraits -{ - static const char *const tag; - static const char *const collectionTag; - - struct Attributes - { - static const char name[]; - static const char type[]; - }; - - typedef CriterionType Element; - typedef CriterionType *PtrElement; - typedef CriterionTypes Collection; - - static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, - Collection &collection); -}; - -struct CriterionTraits -{ - static const char *const tag; - static const char *const collectionTag; - - struct Attributes - { - static const char name[]; - static const char type[]; - static const char defaultVal[]; - }; - - typedef Criterion Element; - typedef Criterion *PtrElement; - typedef Criteria Collection; - - static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, - Collection &collection); -}; -} // namespace detail - -/** Result of `parse(const char*)` */ -struct ParsingResult { - /** Parsed config, nullptr if the xml lib could not load the file */ - std::unique_ptr parsedConfig; - size_t nbSkippedElement; //< Number of skipped invalid product strategies -}; - -/** Parses the provided audio policy usage configuration. - * @return audio policy usage @see Config - */ -ParsingResult parse(const char* path = DEFAULT_PATH); - -} // namespace wrapper_config -} // namespace audio_policy -} // android diff --git a/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml b/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml deleted file mode 100644 index 5d9193bb1f..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/config/policy_wrapper_configuration.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h index cd39b6f431..5bfad29346 100644 --- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h +++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h @@ -39,6 +39,9 @@ class ParameterMgrPlatformConnectorLogger; namespace android { namespace audio_policy { +using ValuePair = std::pair; +using ValuePairs = std::vector; + class ParameterManagerWrapper { private: @@ -118,6 +121,17 @@ public: status_t setDeviceConnectionState(const sp devDesc, audio_policy_dev_state_t state); + /** + * @brief addCriterion to the policy pfw + * @param name of the criterion + * @param isInclusive if true, inclusive, if false exclusive criterion type + * @param pairs of numerical/literal values of the criterion + * @param defaultValue provided as literal. + * @return + */ + status_t addCriterion(const std::string &name, bool isInclusive, ValuePairs pairs, + const std::string &defaultValue); + private: /** * Apply the configuration of the platform on the policy parameter manager. @@ -131,13 +145,6 @@ private: */ void applyPlatformConfiguration(); - /** - * Load the criterion configuration file. - * - * @return NO_ERROR is parsing successful, error code otherwise. - */ - status_t loadConfig(); - /** * Retrieve an element from a map by its name. * diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk index 04561b8ab9..866466fc14 100644 --- a/services/audiopolicy/enginedefault/config/example/Android.mk +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -4,7 +4,7 @@ LOCAL_PATH := $(call my-dir) # CONFIGURATION TOP FILE ################################################################## -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) include $(CLEAR_VARS) LOCAL_MODULE := audio_policy_engine_configuration_phone.xml @@ -29,4 +29,4 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) -endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) +endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) -- GitLab From d0ba9ed06c61ab3e66627e027fbc34ba998140ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Mon, 5 Nov 2018 11:50:42 +0100 Subject: [PATCH 0875/1530] audio policy service: Add introspection API to retrieve AudioProductStrategies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL adds required introspection APIs to deal with product strategies: -getter of the collection of product strategies -helper function to return the strategy associated to a given attributes. This API is mandatory to avoid duplicating the logic that compiles the strategy for a given Audio Attributes structure. Test: make Change-Id: I0e107570a44227bb52a4f359954c93215d4f8bae Signed-off-by: François Gaffie --- include/media/AudioAttributes.h | 1 + include/media/AudioProductStrategy.h | 1 + media/libaudioclient/Android.bp | 24 ++++- media/libaudioclient/AudioAttributes.cpp | 67 +++++++++++++ media/libaudioclient/AudioProductStrategy.cpp | 94 +++++++++++++++++++ media/libaudioclient/AudioSystem.cpp | 59 +++++++++++- media/libaudioclient/IAudioPolicyService.cpp | 94 ++++++++++++++++++- .../include/media/AudioAttributes.h | 59 ++++++++++++ .../include/media/AudioCommonTypes.h | 11 ++- .../include/media/AudioProductStrategy.h | 66 +++++++++++++ .../include/media/AudioSystem.h | 7 ++ .../include/media/IAudioPolicyService.h | 2 + services/audiopolicy/Android.mk | 2 +- services/audiopolicy/AudioPolicyInterface.h | 4 + services/audiopolicy/common/include/policy.h | 6 -- .../engine/common/include/EngineBase.h | 2 + .../engine/common/include/ProductStrategy.h | 16 +--- .../engine/common/src/EngineBase.cpp | 11 +++ .../engine/common/src/ProductStrategy.cpp | 38 +++----- .../interface/AudioPolicyManagerInterface.h | 12 ++- .../audiopolicy/engineconfigurable/Android.mk | 1 + services/audiopolicy/enginedefault/Android.mk | 3 +- .../audiopolicy/enginedefault/src/Engine.h | 1 - .../managerdefault/AudioPolicyManager.h | 10 ++ .../service/AudioPolicyInterfaceImpl.cpp | 18 ++++ .../audiopolicy/service/AudioPolicyService.h | 3 + 26 files changed, 549 insertions(+), 63 deletions(-) create mode 120000 include/media/AudioAttributes.h create mode 120000 include/media/AudioProductStrategy.h create mode 100644 media/libaudioclient/AudioAttributes.cpp create mode 100644 media/libaudioclient/AudioProductStrategy.cpp create mode 100644 media/libaudioclient/include/media/AudioAttributes.h create mode 100644 media/libaudioclient/include/media/AudioProductStrategy.h diff --git a/include/media/AudioAttributes.h b/include/media/AudioAttributes.h new file mode 120000 index 0000000000..27ba4711aa --- /dev/null +++ b/include/media/AudioAttributes.h @@ -0,0 +1 @@ +../../media/libaudioclient/include/media/AudioAttributes.h \ No newline at end of file diff --git a/include/media/AudioProductStrategy.h b/include/media/AudioProductStrategy.h new file mode 120000 index 0000000000..6bfaf11296 --- /dev/null +++ b/include/media/AudioProductStrategy.h @@ -0,0 +1 @@ +../../media/libaudioclient/include/media/AudioProductStrategy.h \ No newline at end of file diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index 1417aaf18c..bd10d67491 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -4,6 +4,28 @@ cc_library_headers { export_include_dirs: ["include"], } +cc_library_shared { + name: "libaudiopolicy", + srcs: [ + "AudioAttributes.cpp", + "AudioPolicy.cpp", + "AudioProductStrategy.cpp", + ], + shared_libs: [ + "libaudioutils", + "libbinder", + "libcutils", + "liblog", + "libutils", + ], + cflags: [ + "-Werror", + "-Wall", + ], + include_dirs: ["system/media/audio_utils/include"], + export_include_dirs: ["include"], +} + cc_library_shared { name: "libaudioclient", @@ -23,7 +45,6 @@ cc_library_shared { ":libaudioclient_aidl", "AudioEffect.cpp", - "AudioPolicy.cpp", "AudioRecord.cpp", "AudioSystem.cpp", "AudioTrack.cpp", @@ -41,6 +62,7 @@ cc_library_shared { ], shared_libs: [ "libaudioutils", + "libaudiopolicy", "libaudiomanager", "libbinder", "libcutils", diff --git a/media/libaudioclient/AudioAttributes.cpp b/media/libaudioclient/AudioAttributes.cpp new file mode 100644 index 0000000000..0f327cf3b6 --- /dev/null +++ b/media/libaudioclient/AudioAttributes.cpp @@ -0,0 +1,67 @@ +/* + * 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 "AudioAttributes" +//#define LOG_NDEBUG 0 +#include + +#include + +#include + +namespace android { + +status_t AudioAttributes::readFromParcel(const Parcel *parcel) +{ + status_t ret = NO_ERROR; + mAttributes.content_type = static_cast(parcel->readInt32()); + mAttributes.usage = static_cast(parcel->readInt32()); + mAttributes.source = static_cast(parcel->readInt32()); + mAttributes.flags = static_cast(parcel->readInt32()); + const bool hasFlattenedTag = (parcel->readInt32() == 1); + if (hasFlattenedTag) { + std::string tags; + ret = parcel->readUtf8FromUtf16(&tags); + if (ret != NO_ERROR) { + return ret; + } + std::strncpy(mAttributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); + } else { + strcpy(mAttributes.tags, ""); + } + mStreamType = static_cast(parcel->readInt32()); + mGroupId = parcel->readUint32(); + return NO_ERROR; +} + +status_t AudioAttributes::writeToParcel(Parcel *parcel) const +{ + parcel->writeInt32(static_cast(mAttributes.content_type)); + parcel->writeInt32(static_cast(mAttributes.usage)); + parcel->writeInt32(static_cast(mAttributes.source)); + parcel->writeInt32(static_cast(mAttributes.flags)); + if (strlen(mAttributes.tags) == 0) { + parcel->writeInt32(0); + } else { + parcel->writeInt32(1); + parcel->writeUtf8AsUtf16(mAttributes.tags); + } + parcel->writeInt32(static_cast(mStreamType)); + parcel->writeUint32(mGroupId); + return NO_ERROR; +} + +} // namespace android diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp new file mode 100644 index 0000000000..1da1114709 --- /dev/null +++ b/media/libaudioclient/AudioProductStrategy.cpp @@ -0,0 +1,94 @@ +/* + * 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 "AudioProductStrategy" +//#define LOG_NDEBUG 0 +#include +#include +#include +#include + +namespace android { + +status_t AudioProductStrategy::readFromParcel(const Parcel *parcel) +{ + mId = static_cast(parcel->readInt32()); + status_t ret = parcel->readUtf8FromUtf16(&mName); + if (ret != NO_ERROR) { + return ret; + } + size_t size = static_cast(parcel->readInt32()); + for (size_t i = 0; i < size; i++) { + AudioAttributes attribute; + ret = attribute.readFromParcel(parcel); + if (ret != NO_ERROR) { + mAudioAttributes.clear(); + return ret; + } + mAudioAttributes.push_back(attribute); + } + return NO_ERROR; +} + +status_t AudioProductStrategy::writeToParcel(Parcel *parcel) const +{ + parcel->writeInt32(static_cast(mId)); + parcel->writeUtf8AsUtf16(mName); + size_t size = mAudioAttributes.size(); + size_t sizePosition = parcel->dataPosition(); + parcel->writeInt32(size); + size_t finalSize = size; + + for (size_t i = 0; i < size; i++) { + size_t position = parcel->dataPosition(); + AudioAttributes attribute(mAudioAttributes[i]); + status_t ret = attribute.writeToParcel(parcel); + if (ret != NO_ERROR) { + parcel->setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = parcel->dataPosition(); + parcel->setDataPosition(sizePosition); + parcel->writeInt32(finalSize); + parcel->setDataPosition(position); + } + return NO_ERROR; +} + +bool AudioProductStrategy::attributesMatches(const audio_attributes_t refAttributes, + const audio_attributes_t clientAttritubes) +{ + if (refAttributes == AUDIO_ATTRIBUTES_INITIALIZER) { + // The default product strategy is the strategy that holds default attributes by convention. + // All attributes that fail to match will follow the default strategy for routing. + // Choosing the default must be done as a fallback, the attributes match shall not + // select the default. + return false; + } + return ((refAttributes.usage == AUDIO_USAGE_UNKNOWN) || + (clientAttritubes.usage == refAttributes.usage)) && + ((refAttributes.content_type == AUDIO_CONTENT_TYPE_UNKNOWN) || + (clientAttritubes.content_type == refAttributes.content_type)) && + ((refAttributes.flags == AUDIO_FLAG_NONE) || + (clientAttritubes.flags != AUDIO_FLAG_NONE && + (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) && + ((strlen(refAttributes.tags) == 0) || + (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0)); +} + +} // namespace android diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 896198bfb2..b83a441e32 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -970,7 +971,7 @@ status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream, uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) { const sp& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return 0; + if (aps == 0) return PRODUCT_STRATEGY_NONE; return aps->getStrategyForStream(stream); } @@ -1327,7 +1328,6 @@ status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool return aps->setSurroundFormatEnabled(audioFormat, enabled); } - status_t AudioSystem::setAssistantUid(uid_t uid) { const sp & aps = AudioSystem::get_audio_policy_service(); @@ -1352,11 +1352,62 @@ bool AudioSystem::isHapticPlaybackSupported() } status_t AudioSystem::getHwOffloadEncodingFormatsSupportedForA2DP( - std::vector *formats) + std::vector *formats) { + const sp + & aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getHwOffloadEncodingFormatsSupportedForA2DP(formats); +} + +status_t AudioSystem::listAudioProductStrategies(AudioProductStrategyVector &strategies) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - return aps->getHwOffloadEncodingFormatsSupportedForA2DP(formats); + return aps->listAudioProductStrategies(strategies); +} + +audio_attributes_t AudioSystem::streamTypeToAttributes(audio_stream_type_t stream) +{ + AudioProductStrategyVector strategies; + listAudioProductStrategies(strategies); + for (const auto &strategy : strategies) { + auto attrVect = strategy.getAudioAttributes(); + auto iter = std::find_if(begin(attrVect), end(attrVect), [&stream](const auto &attributes) { + return attributes.getStreamType() == stream; }); + if (iter != end(attrVect)) { + return iter->getAttributes(); + } + } + ALOGE("invalid stream type %s when converting to attributes", toString(stream).c_str()); + return AUDIO_ATTRIBUTES_INITIALIZER; +} + +audio_stream_type_t AudioSystem::attributesToStreamType(const audio_attributes_t &attr) +{ + product_strategy_t strategyId = + AudioSystem::getProductStrategyFromAudioAttributes(AudioAttributes(attr)); + AudioProductStrategyVector strategies; + listAudioProductStrategies(strategies); + for (const auto &strategy : strategies) { + if (strategy.getId() == strategyId) { + auto attrVect = strategy.getAudioAttributes(); + auto iter = std::find_if(begin(attrVect), end(attrVect), [&attr](const auto &refAttr) { + return AudioProductStrategy::attributesMatches( + refAttr.getAttributes(), attr); }); + if (iter != end(attrVect)) { + return iter->getStreamType(); + } + } + } + ALOGE("invalid attributes %s when converting to stream", toString(attr).c_str()); + return AUDIO_STREAM_MUSIC; +} + +product_strategy_t AudioSystem::getProductStrategyFromAudioAttributes(const AudioAttributes &aa) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PRODUCT_STRATEGY_NONE; + return aps->getProductStrategyFromAudioAttributes(aa); } // --------------------------------------------------------------------------- diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 8c7fac508b..edd8e8011d 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -92,7 +92,9 @@ enum { IS_HAPTIC_PLAYBACK_SUPPORTED, SET_UID_DEVICE_AFFINITY, REMOVE_UID_DEVICE_AFFINITY, - GET_OFFLOAD_FORMATS_A2DP + GET_OFFLOAD_FORMATS_A2DP, + LIST_AUDIO_PRODUCT_STRATEGIES, + GET_STRATEGY_FOR_ATTRIBUTES, }; #define MAX_ITEMS_PER_LIST 1024 @@ -1051,19 +1053,61 @@ public: return status; } - virtual status_t removeUidDeviceAffinities(uid_t uid) - { + virtual status_t removeUidDeviceAffinities(uid_t uid) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32((int32_t) uid); - status_t status = remote()->transact(REMOVE_UID_DEVICE_AFFINITY, data, &reply); + status_t status = + remote()->transact(REMOVE_UID_DEVICE_AFFINITY, data, &reply); if (status == NO_ERROR) { - status = (status_t)reply.readInt32(); + status = (status_t) reply.readInt32(); + } + return status; + } + + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + + status_t status = remote()->transact(LIST_AUDIO_PRODUCT_STRATEGIES, data, &reply); + if (status != NO_ERROR) { + ALOGE("%s: permission denied", __func__); + return status; + } + status = static_cast(reply.readInt32()); + if (status == NO_ERROR) { + uint32_t numStrategies = static_cast(reply.readInt32()); + for (size_t i = 0; i < numStrategies; i++) { + AudioProductStrategy strategy; + status = strategy.readFromParcel(&reply); + if (status != NO_ERROR) { + ALOGE("%s: failed to read strategies", __FUNCTION__); + strategies.clear(); + return status; + } + strategies.push_back(strategy); + } } return status; } + + virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + status_t status = aa.writeToParcel(&data); + if (status != NO_ERROR) { + return PRODUCT_STRATEGY_NONE; + } + status = remote()->transact(GET_STRATEGY_FOR_ATTRIBUTES, data, &reply); + if (status == NO_ERROR) { + return static_cast(reply.readInt32()); + } + return PRODUCT_STRATEGY_NONE; + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1935,6 +1979,46 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case LIST_AUDIO_PRODUCT_STRATEGIES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + AudioProductStrategyVector strategies; + status_t status = listAudioProductStrategies(strategies); + reply->writeInt32(status); + if (status != NO_ERROR) { + return status; + } + size_t size = strategies.size(); + size_t sizePosition = reply->dataPosition(); + reply->writeInt32(size); + size_t finalSize = size; + for (size_t i = 0; i < size; i++) { + size_t position = reply->dataPosition(); + if (strategies[i].writeToParcel(reply) != NO_ERROR) { + reply->setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = reply->dataPosition(); + reply->setDataPosition(sizePosition); + reply->writeInt32(finalSize); + reply->setDataPosition(position); + } + return NO_ERROR; + } + + case GET_STRATEGY_FOR_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + AudioAttributes attributes; + status_t status = attributes.readFromParcel(&data); + if (status != NO_ERROR) { + return status; + } + product_strategy_t strategy = getProductStrategyFromAudioAttributes(attributes); + reply->writeUint32(static_cast(strategy)); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioAttributes.h b/media/libaudioclient/include/media/AudioAttributes.h new file mode 100644 index 0000000000..edf26ebe7c --- /dev/null +++ b/media/libaudioclient/include/media/AudioAttributes.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + + +#pragma once + +#include +#include +#include + +namespace android { + +class AudioAttributes : public Parcelable +{ +public: + AudioAttributes() = default; + AudioAttributes(const audio_attributes_t &attributes) : mAttributes(attributes) {} + AudioAttributes(uint32_t groupId, + audio_stream_type_t stream, + const audio_attributes_t &attributes) : + mAttributes(attributes), mStreamType(stream), mGroupId(groupId) {} + + audio_attributes_t getAttributes() const { return mAttributes; } + + status_t readFromParcel(const Parcel *parcel) override; + status_t writeToParcel(Parcel *parcel) const override; + + audio_stream_type_t getStreamType() const { return mStreamType; } + uint32_t getGroupId() const { return mGroupId; } + +private: + audio_attributes_t mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + /** + * @brief mStreamType: for legacy volume management, we need to be able to convert an attribute + * to a given stream type. + */ + audio_stream_type_t mStreamType = AUDIO_STREAM_DEFAULT; + + /** + * @brief mGroupId: for future volume management, define groups within a strategy that follows + * the same curves of volume (extension of stream types to manage volume) + */ + uint32_t mGroupId = 0; +}; + +} // namespace android diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h index 4c936d4812..48d15403d0 100644 --- a/media/libaudioclient/include/media/AudioCommonTypes.h +++ b/media/libaudioclient/include/media/AudioCommonTypes.h @@ -19,9 +19,7 @@ #include #include -#include -#include -#include +#include namespace android { @@ -29,6 +27,13 @@ enum product_strategy_t : uint32_t; const product_strategy_t PRODUCT_STRATEGY_NONE = static_cast(-1); using AttributesVector = std::vector; +using StreamTypes = std::vector; + +constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_t &rhs) +{ + return lhs.usage == rhs.usage && lhs.content_type == rhs.content_type && + lhs.flags == rhs.flags && (std::strcmp(lhs.tags, rhs.tags) == 0); +} } // namespace android diff --git a/media/libaudioclient/include/media/AudioProductStrategy.h b/media/libaudioclient/include/media/AudioProductStrategy.h new file mode 100644 index 0000000000..7441095e40 --- /dev/null +++ b/media/libaudioclient/include/media/AudioProductStrategy.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { + +class AudioProductStrategy : public Parcelable +{ +public: + AudioProductStrategy() {} + AudioProductStrategy(const std::string &name, const std::vector &attributes, + product_strategy_t id) : + mName(name), mAudioAttributes(attributes), mId(id) {} + + const std::string &getName() const { return mName; } + std::vector getAudioAttributes() const { return mAudioAttributes; } + product_strategy_t getId() const { return mId; } + + status_t readFromParcel(const Parcel *parcel) override; + status_t writeToParcel(Parcel *parcel) const override; + + /** + * @brief attributesMatches: checks if client attributes matches with a reference attributes + * "matching" means the usage shall match if reference attributes has a defined usage, AND + * content type shall match if reference attributes has a defined content type AND + * flags shall match if reference attributes has defined flags AND + * tags shall match if reference attributes has defined tags. + * Reference attributes "default" shall not be considered as a "true" case. This convention + * is used to identify the default strategy. + * @param refAttributes to be considered + * @param clientAttritubes to be considered + * @return true if matching, false otherwise + */ + static bool attributesMatches(const audio_attributes_t refAttributes, + const audio_attributes_t clientAttritubes); +private: + std::string mName; + std::vector mAudioAttributes; + product_strategy_t mId; +}; + +using AudioProductStrategyVector = std::vector; + +} // namespace android + diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 1fb7add602..87a9919126 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -364,6 +365,12 @@ public: static bool isHapticPlaybackSupported(); + static status_t listAudioProductStrategies(AudioProductStrategyVector &strategies); + static product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa); + + static audio_attributes_t streamTypeToAttributes(audio_stream_type_t stream); + static audio_stream_type_t attributesToStreamType(const audio_attributes_t &attr); + // ---------------------------------------------------------------------------- class AudioPortCallback : public RefBase diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 177adc279e..b2cda32561 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -196,6 +196,8 @@ public: virtual status_t setA11yServicesUids(const std::vector& uids) = 0; virtual bool isHapticPlaybackSupported() = 0; + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0; + virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) = 0; }; diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 243020dd9d..5476497c7b 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -53,7 +53,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ liblog \ - libaudioclient \ + libaudiopolicy \ libsoundtrigger ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index cf2ce99096..d7030f9b11 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -240,6 +240,10 @@ public: std::vector *formats) = 0; virtual void setAppState(uid_t uid, app_state_t state); + + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0; + + virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) = 0; }; diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index 04f1eb2b46..b90a08d36c 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -163,9 +163,3 @@ static inline bool audio_formats_match(audio_format_t format1, } return format1 == format2; } - -constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_t &rhs) -{ - return lhs.usage == rhs.usage && lhs.content_type == rhs.content_type && - lhs.flags == rhs.flags && (std::strcmp(lhs.tags, rhs.tags) == 0); -} diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index 32898b142b..5c33fb39b8 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -65,6 +65,8 @@ public: StrategyVector getOrderedProductStrategies() const override; + status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override; + void dump(String8 *dst) const override; diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h index 66ae86eb41..72505b29aa 100644 --- a/services/audiopolicy/engine/common/include/ProductStrategy.h +++ b/services/audiopolicy/engine/common/include/ProductStrategy.h @@ -49,6 +49,8 @@ public: void addAttributes(const AudioAttributes &audioAttributes); + std::vector listAudioAttributes() const; + std::string getName() const { return mName; } AttributesVector getAudioAttributes() const; product_strategy_t getId() const { return mId; } @@ -87,20 +89,6 @@ public: void dump(String8 *dst, int spaces = 0) const; - /** - * @brief attributesMatches: checks if client attributes matches with a reference attributes - * "matching" means the usage shall match if reference attributes has a defined usage, AND - * content type shall match if reference attributes has a defined content type AND - * flags shall match if reference attributes has defined flags AND - * tags shall match if reference attributes has defined tags. - * Reference attributes "default" shall not be considered as a "true" case. This convention - * is used to identify the default strategy. - * @param refAttributes to be considered - * @param clientAttritubes to be considered - * @return true if matching, false otherwise - */ - static bool attributesMatches(const audio_attributes_t refAttributes, - const audio_attributes_t clientAttritubes); private: std::string mName; diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 0f4d5a5b45..755f2a88e3 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -162,6 +162,17 @@ AttributesVector EngineBase::getAllAttributesForProductStrategy(product_strategy mProductStrategies.at(ps)->getAudioAttributes() : AttributesVector(); } +status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &strategies) const +{ + for (const auto &iter : mProductStrategies) { + const auto &productStrategy = iter.second; + strategies.push_back( + {productStrategy->getName(), productStrategy->listAudioAttributes(), + productStrategy->getId()}); + } + return NO_ERROR; +} + void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp index a3edb3954f..71607d1be4 100644 --- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp +++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp @@ -40,6 +40,15 @@ void ProductStrategy::addAttributes(const AudioAttributes &audioAttributes) mAttributesVector.push_back(audioAttributes); } +std::vector ProductStrategy::listAudioAttributes() const +{ + std::vector androidAa; + for (const auto &attr : mAttributesVector) { + androidAa.push_back({attr.mGroupId, attr.mStream, attr.mAttributes}); + } + return androidAa; +} + AttributesVector ProductStrategy::getAudioAttributes() const { AttributesVector attrVector; @@ -52,42 +61,19 @@ AttributesVector ProductStrategy::getAudioAttributes() const return { AUDIO_ATTRIBUTES_INITIALIZER }; } -// @todo: all flags required to match? -// all tags required to match? -/* static */ -bool ProductStrategy::attributesMatches(const audio_attributes_t refAttributes, - const audio_attributes_t clientAttritubes) -{ - if (refAttributes == defaultAttr) { - // The default product strategy is the strategy that holds default attributes by convention. - // All attributes that fail to match will follow the default strategy for routing. - // Choosing the default must be done as a fallback, the attributes match shall not - // selects the default. - return false; - } - return ((refAttributes.usage == AUDIO_USAGE_UNKNOWN) || - (clientAttritubes.usage == refAttributes.usage)) && - ((refAttributes.content_type == AUDIO_CONTENT_TYPE_UNKNOWN) || - (clientAttritubes.content_type == refAttributes.content_type)) && - ((refAttributes.flags == AUDIO_OUTPUT_FLAG_NONE) || - (clientAttritubes.flags != AUDIO_OUTPUT_FLAG_NONE && - (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) && - ((strlen(refAttributes.tags) == 0) || - (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0)); -} - bool ProductStrategy::matches(const audio_attributes_t attr) const { return std::find_if(begin(mAttributesVector), end(mAttributesVector), [&attr](const auto &supportedAttr) { - return attributesMatches(supportedAttr.mAttributes, attr); }) != end(mAttributesVector); + return AudioProductStrategy::attributesMatches(supportedAttr.mAttributes, attr); + }) != end(mAttributesVector); } audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(const audio_attributes_t &attr) const { const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector), [&attr](const auto &supportedAttr) { - return attributesMatches(supportedAttr.mAttributes, attr); }); + return AudioProductStrategy::attributesMatches(supportedAttr.mAttributes, attr); }); return iter != end(mAttributesVector) ? iter->mStream : AUDIO_STREAM_DEFAULT; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 9f5fb0c2e3..18ba2c8892 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -270,6 +270,16 @@ public: */ virtual void updateDeviceSelectionCache() = 0; + /** + * @brief listAudioProductStrategies. Introspection API to retrieve a collection of + * AudioProductStrategyVector that allows to build AudioAttributes according to a + * product_strategy which is just an index. It has also a human readable name to help the + * Car/Oem/AudioManager identiying the use case. + * @param strategies collection. + * @return OK if the list has been retrieved, error code otherwise + */ + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0; + virtual void dump(String8 *dst) const = 0; protected: diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index df813cb068..13d874aa8d 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -57,6 +57,7 @@ LOCAL_SHARED_LIBRARIES := \ libaudioutils \ libparameter \ libmedia_helper \ + libaudiopolicy \ libxml2 include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index f9212f96be..95eac1c17e 100644 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -44,7 +44,8 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libmedia_helper \ - libaudiopolicyengineconfig + libaudiopolicyengineconfig \ + libaudiopolicy LOCAL_HEADER_LIBRARIES := \ libaudiopolicycommon \ diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index 80e92de0b3..5672c07c20 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -80,7 +80,6 @@ private: void updateDeviceSelectionCache() override; - private: /* Copy facilities are put private to disable copy. */ Engine(const Engine &object); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index de6d489893..e715fd4ac2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -251,6 +251,16 @@ public: virtual bool isHapticPlaybackSupported(); + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) + { + return mEngine->listAudioProductStrategies(strategies); + } + + virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) + { + return mEngine->getProductStrategyForAttributes(aa.getAttributes()); + } + protected: // A constructor that allows more fine-grained control over initialization process, // used in automatic tests. diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 45fb174c81..d9fbdaafb1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1186,4 +1186,22 @@ bool AudioPolicyService::isHapticPlaybackSupported() return mAudioPolicyManager->isHapticPlaybackSupported(); } +status_t AudioPolicyService::listAudioProductStrategies(AudioProductStrategyVector &strategies) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->listAudioProductStrategies(strategies); +} + +product_strategy_t AudioPolicyService::getProductStrategyFromAudioAttributes( + const AudioAttributes &aa) +{ + if (mAudioPolicyManager == NULL) { + return PRODUCT_STRATEGY_NONE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getProductStrategyFromAudioAttributes(aa); +} } // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index c073b7c0da..ee293a7496 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -229,6 +229,9 @@ public: virtual bool isHapticPlaybackSupported(); + virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies); + virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa); + status_t doStopOutput(audio_port_handle_t portId); void doReleaseOutput(audio_port_handle_t portId); -- GitLab From d239aa68d1896b9526b0e2571c1feff61c452702 Mon Sep 17 00:00:00 2001 From: bohu Date: Sat, 2 Feb 2019 07:47:42 -0800 Subject: [PATCH 0876/1530] seccomp: allows more fstat calls for swcodec Test: lunch sdk_gphone_x86_64-userdebug make -j emulator media.swcodec should not be killed by minijail anymore Change-Id: I8bb9a4a36e207e2bb9c42226cac34bb46281b78f --- services/mediacodec/seccomp_policy/mediacodec-x86.policy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index a48cedbfdc..a1ef16fcbc 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -26,6 +26,7 @@ close: 1 mmap2: 1 mmap: 1 fstat64: 1 +fstat: 1 stat64: 1 statfs64: 1 madvise: 1 @@ -47,6 +48,7 @@ ugetrlimit: 1 readlinkat: 1 _llseek: 1 fstatfs64: 1 +fstatfs: 1 pread64: 1 mremap: 1 dup: 1 -- GitLab From c005e569c7bae3f8c49660c9388c3076046069f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 6 Nov 2018 15:04:49 +0100 Subject: [PATCH 0877/1530] audiopolicy: apm: switch to new Engine APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: Iedc2268852ee0bce32b67cfd324395c48cb33424 Signed-off-by: François Gaffie --- media/libaudioclient/IAudioPolicyService.cpp | 5 +- .../include/media/AudioCommonTypes.h | 5 +- services/audiopolicy/Android.mk | 12 +- services/audiopolicy/common/include/policy.h | 23 +- .../include/AudioIODescriptorInterface.h | 34 + .../include/AudioOutputDescriptor.h | 115 ++- .../include/AudioPolicyMix.h | 2 +- .../include/ClientDescriptor.h | 12 +- .../include/EffectDescriptor.h | 18 +- .../src/AudioOutputDescriptor.cpp | 28 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 2 +- .../src/ClientDescriptor.cpp | 2 +- .../src/EffectDescriptor.cpp | 21 +- .../engineconfigurable/src/Engine.cpp | 39 +- .../audiopolicy/enginedefault/src/Engine.cpp | 37 +- .../managerdefault/AudioPolicyManager.cpp | 665 +++++++----------- .../managerdefault/AudioPolicyManager.h | 164 +++-- .../service/AudioPolicyInterfaceImpl.cpp | 5 +- services/audiopolicy/tests/Android.mk | 4 +- 19 files changed, 619 insertions(+), 574 deletions(-) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index edd8e8011d..0db56e8609 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -414,7 +414,7 @@ public: data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast (stream)); remote()->transact(GET_STRATEGY_FOR_STREAM, data, &reply); - return reply.readInt32(); + return reply.readUint32(); } virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) @@ -1126,7 +1126,6 @@ status_t BnAudioPolicyService::onTransact( case START_INPUT: case STOP_INPUT: case RELEASE_INPUT: - case GET_STRATEGY_FOR_STREAM: case GET_OUTPUT_FOR_EFFECT: case REGISTER_EFFECT: case UNREGISTER_EFFECT: @@ -1422,7 +1421,7 @@ status_t BnAudioPolicyService::onTransact( CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_stream_type_t stream = static_cast (data.readInt32()); - reply->writeInt32(getStrategyForStream(stream)); + reply->writeUint32(getStrategyForStream(stream)); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h index 48d15403d0..5188da1fd3 100644 --- a/media/libaudioclient/include/media/AudioCommonTypes.h +++ b/media/libaudioclient/include/media/AudioCommonTypes.h @@ -34,6 +34,9 @@ constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_ return lhs.usage == rhs.usage && lhs.content_type == rhs.content_type && lhs.flags == rhs.flags && (std::strcmp(lhs.tags, rhs.tags) == 0); } - +constexpr bool operator!=(const audio_attributes_t &lhs, const audio_attributes_t &rhs) +{ + return !(lhs==rhs); +} } // namespace android diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 5476497c7b..f72f44a652 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -11,10 +11,10 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES := \ frameworks/av/services/audioflinger \ $(call include-path-for, audio-utils) \ - frameworks/av/services/audiopolicy/engine/interface \ LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers \ LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -78,11 +78,11 @@ LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) LOCAL_C_INCLUDES += \ - frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents @@ -118,11 +118,11 @@ LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents LOCAL_C_INCLUDES += \ - frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers LOCAL_CFLAGS := -Wall -Werror diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h index b90a08d36c..605fc1cda1 100644 --- a/services/audiopolicy/common/include/policy.h +++ b/services/audiopolicy/common/include/policy.h @@ -23,7 +23,6 @@ namespace android { using StreamTypeVector = std::vector; - static const audio_attributes_t defaultAttr = AUDIO_ATTRIBUTES_INITIALIZER; } // namespace android @@ -163,3 +162,25 @@ static inline bool audio_formats_match(audio_format_t format1, } return format1 == format2; } + +/** + * @brief hasStream checks if a given stream type is found in the list of streams + * @param streams collection of stream types to consider. + * @param streamType to consider + * @return true if voice stream is found in the given streams, false otherwise + */ +static inline bool hasStream(const android::StreamTypeVector &streams, + audio_stream_type_t streamType) +{ + return std::find(begin(streams), end(streams), streamType) != end(streams); +} + +/** + * @brief hasVoiceStream checks if a voice stream is found in the list of streams + * @param streams collection to consider. + * @return true if voice stream is found in the given streams, false otherwise + */ +static inline bool hasVoiceStream(const android::StreamTypeVector &streams) +{ + return hasStream(streams, AUDIO_STREAM_VOICE_CALL); +} diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h index 555412e0cd..6e2963245c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h @@ -16,6 +16,8 @@ #pragma once +#include "DeviceDescriptor.h" + namespace android { /** @@ -34,4 +36,36 @@ public: virtual void setPatchHandle(audio_patch_handle_t handle) = 0; }; +template +sp findPreferredDevice( + IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices) +{ + auto activeClients = desc->clientsList(true /*activeOnly*/); + auto activeClientsWithRoute = + desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/); + active = activeClients.size() > 0; + if (active && activeClients.size() == activeClientsWithRoute.size()) { + return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId()); + } + return nullptr; +} + +template +sp findPreferredDevice( + IoCollection& ioCollection, Filter filter, const DeviceVector& devices) +{ + sp device; + for (size_t i = 0; i < ioCollection.size(); i++) { + auto desc = ioCollection.valueAt(i); + bool active; + sp curDevice = findPreferredDevice(desc, filter, active, devices); + if (active && curDevice == nullptr) { + return nullptr; + } else if (curDevice != nullptr) { + device = curDevice; + } + } + return device; +} + } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index e1ecc61eda..6132bb4782 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -16,18 +16,20 @@ #pragma once +#define __STDC_LIMIT_MACROS +#include + #include #include #include #include #include -#include #include "AudioIODescriptorInterface.h" #include "AudioPort.h" #include "ClientDescriptor.h" #include "DeviceDescriptor.h" -#include +#include namespace android { @@ -35,6 +37,70 @@ class IOProfile; class AudioMix; class AudioPolicyClientInterface; +class ActivityTracking +{ +public: + virtual ~ActivityTracking() = default; + bool isActive(uint32_t inPastMs = 0, nsecs_t sysTime = 0) const + { + if (mActivityCount > 0) { + return true; + } + if (inPastMs == 0) { + return false; + } + if (sysTime == 0) { + sysTime = systemTime(); + } + if (ns2ms(sysTime - mStopTime) < inPastMs) { + return true; + } + return false; + } + void changeActivityCount(int delta) + { + if ((delta + (int)mActivityCount) < 0) { + LOG_ALWAYS_FATAL("%s: invalid delta %d, refCount %d", __func__, delta, mActivityCount); + } + mActivityCount += delta; + if (!mActivityCount) { + setStopTime(systemTime()); + } + } + uint32_t getActivityCount() const { return mActivityCount; } + nsecs_t getStopTime() const { return mStopTime; } + void setStopTime(nsecs_t stopTime) { mStopTime = stopTime; } + + virtual void dump(String8 *dst, int spaces) const + { + dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 " \n", spaces, "", + getActivityCount(), getStopTime()); + } +private: + uint32_t mActivityCount = 0; + nsecs_t mStopTime = 0; +}; + +/** + * @brief The Activity class: it tracks the activity for volume policy (volume index, mute, + * memorize previous stop, and store mute if incompatible device with another strategy. + * Having this class prevents from looping on all attributes (legacy streams) of the strategy + */ +class RoutingActivity : public ActivityTracking +{ +public: + void setMutedByDevice( bool isMuted) { mIsMutedByDevice = isMuted; } + bool isMutedByDevice() const { return mIsMutedByDevice; } + +private: + /** + * strategies muted because of incompatible device selection. + * See AudioPolicyManager::checkDeviceMuteStrategies() + */ + bool mIsMutedByDevice = false; +}; +using RoutingActivities = std::map; + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output // and keep track of the usage of this output by each audio stream type. class AudioOutputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface @@ -70,6 +136,14 @@ public: uint32_t streamActiveCount(audio_stream_type_t stream) const { return mActiveCount[stream]; } + /** + * @brief setStopTime set the stop time due to the client stoppage or a re routing of this + * client + * @param client to be considered + * @param sysTime when the client stopped/was rerouted + */ + void setStopTime(const sp& client, nsecs_t sysTime); + /** * Changes the client->active() state and the output descriptor's global active count, * along with the stream active count and mActiveClients. @@ -82,6 +156,21 @@ public: uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; + bool isStrategyActive(product_strategy_t ps, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const + { + return mRoutingActivities.find(ps) != std::end(mRoutingActivities)? + mRoutingActivities.at(ps).isActive(inPastMs, sysTime) : false; + } + bool isStrategyMutedByDevice(product_strategy_t ps) const + { + return mRoutingActivities.find(ps) != std::end(mRoutingActivities)? + mRoutingActivities.at(ps).isMutedByDevice() : false; + } + void setStrategyMutedByDevice(product_strategy_t ps, bool isMuted) + { + mRoutingActivities[ps].setMutedByDevice(isMuted); + } + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig = NULL) const; virtual sp getAudioPort() const { return mPort; } @@ -95,7 +184,8 @@ public: void setPatchHandle(audio_patch_handle_t handle) override; TrackClientVector clientsList(bool activeOnly = false, - routing_strategy strategy = STRATEGY_NONE, bool preferredDeviceOnly = false) const; + product_strategy_t strategy = PRODUCT_STRATEGY_NONE, + bool preferredDeviceOnly = false) const; // override ClientMapHandler to abort when removing a client when active. void removeClient(audio_port_handle_t portId) override { @@ -121,8 +211,6 @@ public: DeviceVector mDevices; /**< current devices this output is routed to */ nsecs_t mStopTime[AUDIO_STREAM_CNT]; int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter - bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible - // device selection. See checkDeviceMuteStrategies() AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy protected: @@ -139,6 +227,8 @@ protected: // Compare with the ClientMap (mClients) which are external AudioTrack clients of the // output descriptor (and do not count internal PatchTracks). ActiveClientMap mActiveClients; + + RoutingActivities mRoutingActivities; /**< track routing activity on this ouput.*/ }; // Audio output driven by a software mixer in audio flinger. @@ -274,6 +364,21 @@ public: */ bool isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + /** + * @brief isStrategyActiveOnSameModule checks if the given strategy is active (or was active + * in the past) on the given output and all the outputs belonging to the same HW Module + * the same module than the given output + * @param outputDesc to be considered + * @param ps product strategy to be checked upon activity status + * @param inPastMs if 0, check currently, otherwise, check in the past + * @param sysTime shall be set if request is done for the past activity. + * @return true if an output following the strategy is active on the same module than desc, + * false otherwise + */ + bool isStrategyActiveOnSameModule(product_strategy_t ps, + const sp& desc, + uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; + /** * returns the A2DP output handle if it is open or 0 otherwise */ diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 2932296cb6..7296c9532c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -78,7 +78,7 @@ public: sp getDeviceAndMixForInputSource(audio_source_t inputSource, const DeviceVector &availableDeviceTypes, - AudioMix **policyMix); + AudioMix **policyMix) const; /** * @brief try to find a matching mix for a given output descriptor and returns the associated diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index a1870291f2..2fb1277b96 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -22,14 +22,14 @@ #include #include -#include +#include #include #include #include #include +#include #include "AudioPatch.h" #include "EffectDescriptor.h" -#include "RoutingStrategy.h" namespace android { @@ -81,7 +81,7 @@ public: TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, audio_attributes_t attributes, audio_config_base_t config, audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, - routing_strategy strategy, audio_output_flags_t flags) : + product_strategy_t strategy, audio_output_flags_t flags) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), mStream(stream), mStrategy(strategy), mFlags(flags) {} ~TrackClientDescriptor() override = default; @@ -92,11 +92,11 @@ public: audio_output_flags_t flags() const { return mFlags; } audio_stream_type_t stream() const { return mStream; } - routing_strategy strategy() const { return mStrategy; } + product_strategy_t strategy() const { return mStrategy; } private: const audio_stream_type_t mStream; - const routing_strategy mStrategy; + const product_strategy_t mStrategy; const audio_output_flags_t mFlags; }; @@ -136,7 +136,7 @@ class SourceClientDescriptor: public TrackClientDescriptor public: SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, const sp& srcDevice, - audio_stream_type_t stream, routing_strategy strategy); + audio_stream_type_t stream, product_strategy_t strategy); ~SourceClientDescriptor() override = default; sp patchDesc() const { return mPatchDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h index 2dc33ab936..7f01dc522d 100644 --- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h @@ -16,7 +16,7 @@ #pragma once -#include +#include #include #include #include @@ -28,14 +28,26 @@ namespace android { class EffectDescriptor : public RefBase { public: + EffectDescriptor(const effect_descriptor_t *desc, bool isMusicEffect, + int id, int io, int session) : + mId(id), mIo(io), mSession(session), mEnabled(false), + mIsMusicEffect(isMusicEffect) + { + memcpy (&mDesc, desc, sizeof(effect_descriptor_t)); + } + void dump(String8 *dst, int spaces = 0) const; int mId; // effect unique ID int mIo; // io the effect is attached to - routing_strategy mStrategy; // routing strategy the effect is associated to int mSession; // audio session the effect is on effect_descriptor_t mDesc; // effect descriptor bool mEnabled; // enabled state: CPU load being used or not + + bool isMusicEffect() const { return mIsMusicEffect; } + +private: + bool mIsMusicEffect; }; class EffectDescriptorCollection : public KeyedVector > @@ -44,7 +56,7 @@ public: EffectDescriptorCollection(); status_t registerEffect(const effect_descriptor_t *desc, audio_io_handle_t io, - uint32_t strategy, int session, int id); + int session, int id, bool isMusicEffect); status_t unregisterEffect(int id); sp getEffect(int id) const; status_t setEffectEnabled(int id, bool enabled); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 78b3f45bd9..d997bf74af 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -44,9 +44,6 @@ AudioOutputDescriptor::AudioOutputDescriptor(const sp& port, mMuteCount[i] = 0; mStopTime[i] = 0; } - for (int i = 0; i < NUM_STRATEGIES; i++) { - mStrategyMutedByDevice[i] = false; - } if (mPort.get() != nullptr) { mPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat); if (mPort->mGains.size() > 0) { @@ -101,6 +98,7 @@ void AudioOutputDescriptor::changeStreamActiveCount(const spstrategy()].changeActivityCount(delta); if (delta > 0) { mActiveClients[client] += delta; @@ -122,6 +120,12 @@ void AudioOutputDescriptor::changeStreamActiveCount(const sp& client, nsecs_t sysTime) +{ + mStopTime[client->stream()] = sysTime; + mRoutingActivities[client->strategy()].setStopTime(sysTime); +} + void AudioOutputDescriptor::setClientActive(const sp& client, bool active) { LOG_ALWAYS_FATAL_IF(getClient(client->portId()) == nullptr, @@ -247,13 +251,13 @@ void AudioOutputDescriptor::toAudioPort(struct audio_port *port) const port->ext.mix.hw_module = getModuleHandle(); } -TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, routing_strategy strategy, +TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, product_strategy_t strategy, bool preferredDeviceOnly) const { TrackClientVector clients; for (const auto &client : getClientIterable()) { if ((!activeOnly || client->active()) - && (strategy == STRATEGY_NONE || strategy == client->strategy()) + && (strategy == PRODUCT_STRATEGY_NONE || strategy == client->strategy()) && (!preferredDeviceOnly || client->hasPreferredDevice())) { clients.push_back(client); } @@ -698,6 +702,20 @@ bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream, return false; } +bool SwAudioOutputCollection::isStrategyActiveOnSameModule(product_strategy_t ps, + const sp& desc, + uint32_t inPastMs, nsecs_t sysTime) const +{ + for (size_t i = 0; i < size(); i++) { + const sp otherDesc = valueAt(i); + if (desc->sharesHwModuleWith(otherDesc) && + otherDesc->isStrategyActive(ps, inPastMs, sysTime)) { + return true; + } + } + return false; +} + audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const { for (size_t i = 0; i < size(); i++) { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index cd10c8240d..2489e76e7a 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -302,7 +302,7 @@ sp AudioPolicyMixCollection::getDeviceAndMixForOutput( } sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( - audio_source_t inputSource, const DeviceVector &availDevices, AudioMix **policyMix) + audio_source_t inputSource, const DeviceVector &availDevices, AudioMix **policyMix) const { for (size_t i = 0; i < size(); i++) { AudioMix *mix = valueAt(i)->getMix(); diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 82d64c9e4d..1525285f55 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -82,7 +82,7 @@ void RecordClientDescriptor::dump(String8 *dst, int spaces, int index) const SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, const sp& srcDevice, audio_stream_type_t stream, - routing_strategy strategy) : + product_strategy_t strategy) : TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, stream, strategy, AUDIO_OUTPUT_FLAG_NONE), diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp index 40c49e7e9c..89f989943d 100644 --- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp @@ -24,8 +24,9 @@ namespace android { void EffectDescriptor::dump(String8 *dst, int spaces) const { + dst->appendFormat("%*sID: %d\n", spaces, "", mId); dst->appendFormat("%*sI/O: %d\n", spaces, "", mIo); - dst->appendFormat("%*sStrategy: %d\n", spaces, "", mStrategy); + dst->appendFormat("%*sMusic Effect: %s\n", spaces, "", isMusicEffect()? "yes" : "no"); dst->appendFormat("%*sSession: %d\n", spaces, "", mSession); dst->appendFormat("%*sName: %s\n", spaces, "", mDesc.name); dst->appendFormat("%*s%s\n", spaces, "", mEnabled ? "Enabled" : "Disabled"); @@ -41,9 +42,8 @@ EffectDescriptorCollection::EffectDescriptorCollection() : status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *desc, audio_io_handle_t io, - uint32_t strategy, int session, - int id) + int id, bool isMusicEffect) { if (getEffect(id) != nullptr) { ALOGW("%s effect %s already registered", __FUNCTION__, desc->name); @@ -59,18 +59,11 @@ status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *d if (mTotalEffectsMemory > mTotalEffectsMemoryMaxUsed) { mTotalEffectsMemoryMaxUsed = mTotalEffectsMemory; } - ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", - desc->name, io, strategy, session, id); + ALOGV("registerEffect() effect %s, io %d, session %d id %d", + desc->name, io, session, id); ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); - sp effectDesc = new EffectDescriptor(); - memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); - effectDesc->mId = id; - effectDesc->mIo = io; - effectDesc->mStrategy = static_cast(strategy); - effectDesc->mSession = session; - effectDesc->mEnabled = false; - + sp effectDesc = new EffectDescriptor(desc, isMusicEffect, id, io, session); add(id, effectDesc); return NO_ERROR; @@ -161,7 +154,7 @@ bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() const { for (size_t i = 0; i < size(); i++) { sp effectDesc = valueAt(i); - if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) && + if (effectDesc->mEnabled && (effectDesc->isMusicEffect()) && ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", effectDesc->mDesc.name, effectDesc->mSession); diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index a04254c450..1b09e3dcc6 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -32,6 +32,7 @@ #include #include +#include #include using std::string; @@ -330,10 +331,20 @@ DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &att ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str()); return DeviceVector(preferredDevice); } - product_strategy_t strategy = EngineBase::getProductStrategyForAttributes(attributes); + product_strategy_t strategy = getProductStrategyForAttributes(attributes); + const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); // - // @TODO: manage dynamic mix + // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to + // be by APM? // + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp device = findPreferredDevice(outputs, strategy, availableOutputDevices); + if (device != nullptr) { + return DeviceVector(device); + } + return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy); } @@ -344,13 +355,29 @@ DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool } sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, - AudioMix **/*mix*/) const + AudioMix **mix) const { - const auto &availInputDevices = getApmObserver()->getAvailableInputDevices(); + const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); + const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const auto &inputs = getApmObserver()->getInputs(); std::string address; // - // @TODO: manage explicit routing and dynamic mix + // Explicit Routing ??? what is the priority of explicit routing? Shall it be considered + // first as it used to be by APM? // + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp device = + findPreferredDevice(inputs, attr.source, availableInputDevices); + if (device != nullptr) { + return device; + } + + device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix); + if (device != nullptr) { + return device; + } + audio_devices_t deviceType = getPropertyForKey(attr.source); if (audio_is_remote_submix_device(deviceType)) { @@ -361,7 +388,7 @@ sp Engine::getInputDeviceForAttributes(const audio_attributes_ address = tags.substr(pos + std::strlen("addr=")); } } - return availInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT); + return availableInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT); } void Engine::updateDeviceSelectionCache() diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 69f06982e5..a449df6d6c 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -241,7 +242,8 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); - return getDeviceForStrategyInt(static_cast(strategy), availableOutputDevices, + return getDeviceForStrategyInt(static_cast(strategy), + availableOutputDevices, availableInputDevices, outputs, (uint32_t)AUDIO_DEVICE_NONE); } @@ -808,14 +810,19 @@ DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &att ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str()); return DeviceVector(preferredDevice); } + product_strategy_t strategy = getProductStrategyForAttributes(attributes); + const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); // // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to // be by APM? // - product_strategy_t strategy = getProductStrategyForAttributes(attributes); - // - // @TODO: manage dynamic mix - // + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp device = findPreferredDevice(outputs, strategy, availableOutputDevices); + if (device != nullptr) { + return DeviceVector(device); + } return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy); } @@ -827,13 +834,29 @@ DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool } sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, - AudioMix **/*mix*/) const + AudioMix **mix) const { + const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const auto &inputs = getApmObserver()->getInputs(); std::string address; + // - // @TODO: manage explicit routing and dynamic mix + // Explicit Routing ??? what is the priority of explicit routing? Shall it be considered + // first as it used to be by APM? // + // Honor explicit routing requests only if all active clients have a preferred route in which + // case the last active client route is used + sp device = + findPreferredDevice(inputs, attr.source, availableInputDevices); + if (device != nullptr) { + return device; + } + + device = policyMixes.getDeviceAndMixForInputSource(attr.source, availableInputDevices, mix); + if (device != nullptr) { + return device; + } audio_devices_t deviceType = getDeviceForInputSource(attr.source); if (audio_is_remote_submix_device(deviceType)) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d7c7b4d029..771f62aade 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -495,9 +495,10 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device"); audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION }; - auto txSourceDevice = getDeviceAndMixForAttributes(attr); + auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr); ALOG_ASSERT(txSourceDevice != 0, "updateCallRouting() input selected device not available"); - ALOGV("updateCallRouting device rxDevice %s txDevice %s", + + ALOGV("updateCallRouting device rxDevice %s txDevice %s", rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str()); // release existing RX patch if any @@ -677,26 +678,27 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) int delayMs = 0; if (isStateInCall(state)) { nsecs_t sysTime = systemTime(); + auto musicStrategy = streamToStrategy(AUDIO_STREAM_MUSIC); + auto sonificationStrategy = streamToStrategy(AUDIO_STREAM_ALARM); for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); // mute media and sonification strategies and delay device switch by the largest // latency of any output where either strategy is active. // This avoid sending the ring tone or music tail into the earpiece or headset. - if ((isStrategyActive(desc, STRATEGY_MEDIA, - SONIFICATION_HEADSET_MUSIC_DELAY, - sysTime) || - isStrategyActive(desc, STRATEGY_SONIFICATION, - SONIFICATION_HEADSET_MUSIC_DELAY, - sysTime)) && + if ((desc->isStrategyActive(musicStrategy, SONIFICATION_HEADSET_MUSIC_DELAY, sysTime) || + desc->isStrategyActive(sonificationStrategy, SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && (delayMs < (int)desc->latency()*2)) { delayMs = desc->latency()*2; } - setStrategyMute(STRATEGY_MEDIA, true, desc); - setStrategyMute(STRATEGY_MEDIA, false, desc, MUTE_TIME_MS, - getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); - setStrategyMute(STRATEGY_SONIFICATION, true, desc); - setStrategyMute(STRATEGY_SONIFICATION, false, desc, MUTE_TIME_MS, - getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/)); + setStrategyMute(musicStrategy, true, desc); + setStrategyMute(musicStrategy, false, desc, MUTE_TIME_MS, + mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), + nullptr, true /*fromCache*/).types()); + setStrategyMute(sonificationStrategy, true, desc); + setStrategyMute(sonificationStrategy, false, desc, MUTE_TIME_MS, + mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_ALARM), + nullptr, true /*fromCache*/).types()); } } @@ -743,12 +745,8 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) } // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE - if (state == AUDIO_MODE_RINGTONE && - isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { - mLimitRingtoneVolume = true; - } else { - mLimitRingtoneVolume = false; - } + mLimitRingtoneVolume = (state == AUDIO_MODE_RINGTONE && + isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)); } audio_mode_t AudioPolicyManager::getPhoneState() { @@ -871,8 +869,7 @@ sp AudioPolicyManager::getProfileForOutput( audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) { - routing_strategy strategy = getStrategy(stream); - DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); + DeviceVector devices = mEngine->getOutputDevicesForStream(stream, false /*fromCache*/); // Note that related method getOutputForAttr() uses getOutputForDevice() not selectOutput(). // We use selectOutput() here since we don't have the desired AudioTrack sample rate, @@ -906,7 +903,7 @@ status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr, ALOGE("%s: invalid stream type", __func__); return BAD_VALUE; } - stream_type_to_audio_attributes(srcStream, dstAttr); + *dstAttr = mEngine->getAttributesForStreamType(srcStream); } return NO_ERROR; } @@ -921,57 +918,52 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId) { - DeviceVector devices; - routing_strategy strategy; - audio_devices_t deviceType = AUDIO_DEVICE_NONE; + DeviceVector outputDevices; const audio_port_handle_t requestedPortId = *selectedDeviceId; DeviceVector msdDevices = getMsdAudioOutDevices(); + const sp requestedDevice = + mAvailableOutputDevices.getDeviceFromId(requestedPortId); + + status_t status = getAudioAttributes(resultAttr, attr, *stream); if (status != NO_ERROR) { return status; } + *stream = mEngine->getStreamTypeForAttributes(*resultAttr); - ALOGV("%s usage=%d, content=%d, tag=%s flags=%08x" - " session %d selectedDeviceId %d", - __func__, - resultAttr->usage, resultAttr->content_type, resultAttr->tags, resultAttr->flags, - session, requestedPortId); - - *stream = streamTypefromAttributesInt(resultAttr); - - strategy = getStrategyForAttr(resultAttr); - - // First check for explicit routing (eg. setPreferredDevice) - sp requestedDevice = mAvailableOutputDevices.getDeviceFromId(requestedPortId); - if (requestedDevice != nullptr) { - deviceType = requestedDevice->type(); - } else { - // If no explict route, is there a matching dynamic policy that applies? - sp desc; - if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) { - ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); - if (!audio_has_proportional_frames(config->format)) { - return BAD_VALUE; - } - *stream = streamTypefromAttributesInt(resultAttr); - *output = desc->mIoHandle; - AudioMix *mix = desc->mPolicyMix; - sp deviceDesc = - mAvailableOutputDevices.getDevice( - mix->mDeviceType, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT); - *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE; - ALOGV("%s returns output %d", __func__, *output); - return NO_ERROR; - } + ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__, + toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId); - // Virtual sources must always be dynamicaly or explicitly routed - if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) { - ALOGW("%s no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE", __func__); + // 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine + // 2/ If no explict route, is there a matching dynamic policy that applies? + // NOTE: new engine product strategy does not make use of dynamic routing, keep it for + // remote-submix and legacy + sp desc; + if (requestedDevice == nullptr && + mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) { + ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); + if (!audio_has_proportional_frames(config->format)) { return BAD_VALUE; } - deviceType = getDeviceForStrategy(strategy, false /*fromCache*/); + *output = desc->mIoHandle; + AudioMix *mix = desc->mPolicyMix; + sp deviceDesc = + mAvailableOutputDevices.getDevice(mix->mDeviceType, + mix->mDeviceAddress, + AUDIO_FORMAT_DEFAULT); + *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE; + ALOGV("getOutputForAttr() returns output %d", *output); + return NO_ERROR; } + // Virtual sources must always be dynamicaly or explicitly routed + if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) { + ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); + return BAD_VALUE; + } + // explicit routing managed by getDeviceForStrategy in APM is now handled by engine + // in order to let the choice of the order to future vendor engine + outputDevices = mEngine->getOutputDevicesForAttributes(*resultAttr, requestedDevice, false); if ((resultAttr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); @@ -982,45 +974,40 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, // FIXME: provide a more generic approach which is not device specific and move this back // to getOutputForDevice. // TODO: Remove check of AUDIO_STREAM_MUSIC once migration is completed on the app side. - if (deviceType == AUDIO_DEVICE_OUT_TELEPHONY_TX && - (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) && + if (outputDevices.types() == AUDIO_DEVICE_OUT_TELEPHONY_TX && + (*stream == AUDIO_STREAM_MUSIC || resultAttr->usage == AUDIO_USAGE_VOICE_COMMUNICATION) && audio_is_linear_pcm(config->format) && isInCall()) { if (requestedPortId != AUDIO_PORT_HANDLE_NONE) { *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC; - } else { - // Get the devce type directly from the engine to bypass preferred route logic - deviceType = mEngine->getDeviceForStrategy(strategy); + // @todo: provide another solution (separated CL) } } - ALOGV("%s device 0x%x, sampling rate %d, format %#x, channel mask %#x, " - "flags %#x", - __func__, - deviceType, config->sample_rate, config->format, config->channel_mask, *flags); + ALOGV("%s() device %s, sampling rate %d, format %#x, channel mask %#x, flags %#x stream %s", + __func__, outputDevices.toString().c_str(), config->sample_rate, config->format, + config->channel_mask, *flags, toString(*stream).c_str()); *output = AUDIO_IO_HANDLE_NONE; if (!msdDevices.isEmpty()) { *output = getOutputForDevices(msdDevices, session, *stream, config, flags); - sp deviceDesc = - mAvailableOutputDevices.getDevice(deviceType, String8(), AUDIO_FORMAT_DEFAULT); - if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(deviceDesc) == NO_ERROR) { - ALOGV("%s() Using MSD devices %s instead of device %s", - __func__, msdDevices.toString().c_str(), deviceDesc->toString().c_str()); - deviceType = msdDevices.types(); + sp device = outputDevices.isEmpty() ? nullptr : outputDevices.itemAt(0); + if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(device) == NO_ERROR) { + ALOGV("%s() Using MSD devices %s instead of devices %s", + __func__, msdDevices.toString().c_str(), outputDevices.toString().c_str()); + outputDevices = msdDevices; } else { *output = AUDIO_IO_HANDLE_NONE; } } - devices = mAvailableOutputDevices.getDevicesFromTypeMask(deviceType); if (*output == AUDIO_IO_HANDLE_NONE) { - *output = getOutputForDevices(devices, session, *stream, config, flags); + *output = getOutputForDevices(outputDevices, session, *stream, config, flags); } if (*output == AUDIO_IO_HANDLE_NONE) { return INVALID_OPERATION; } - *selectedDeviceId = getFirstDeviceId(devices); + *selectedDeviceId = getFirstDeviceId(outputDevices); ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId); @@ -1057,13 +1044,13 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, sp clientDesc = new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, requestedPortId, *stream, - getStrategyForAttr(&resultAttr), + mEngine->getProductStrategyForAttributes(resultAttr), *flags); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); - ALOGV("%s returns output %d selectedDeviceId %d for port ID %d", - __func__, *output, requestedPortId, *portId); + ALOGV("%s() returns output %d selectedDeviceId %d for port ID %d", __func__, + *output, *selectedDeviceId, *portId); return NO_ERROR; } @@ -1368,7 +1355,8 @@ status_t AudioPolicyManager::setMsdPatch(const sp &outputDevic // Use media strategy for unspecified output device. This should only // occur on checkForDeviceAndOutputChanges(). Device connection events may // therefore invalidate explicit routing requests. - DeviceVector devices = getDevicesForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + DeviceVector devices = mEngine->getOutputDevicesForAttributes( + attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, false /*fromCache*/); LOG_ALWAYS_FATAL_IF(devices.isEmpty(), "no outpudevice to set Msd Patch"); device = devices.itemAt(0); } @@ -1547,6 +1535,8 @@ status_t AudioPolicyManager::startSource(const sp& outp *delayMs = 0; audio_stream_type_t stream = client->stream(); + auto clientStrategy = client->strategy(); + auto clientAttr = client->attributes(); if (stream == AUDIO_STREAM_TTS) { ALOGV("\t found BEACON stream"); if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { @@ -1594,11 +1584,11 @@ status_t AudioPolicyManager::startSource(const sp& outp if (client->hasPreferredDevice(true)) { devices = getNewOutputDevices(outputDesc, false /*fromCache*/); if (devices != outputDesc->devices()) { - checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle); + checkStrategyRoute(clientStrategy, outputDesc->mIoHandle); } } - if (stream == AUDIO_STREAM_MUSIC) { + if (followsSameRouting(clientAttr, attributes_initializer(AUDIO_USAGE_MEDIA))) { selectOutputForMusicEffects(); } @@ -1607,11 +1597,10 @@ status_t AudioPolicyManager::startSource(const sp& outp if (devices.isEmpty()) { devices = getNewOutputDevices(outputDesc, false /*fromCache*/); } - - routing_strategy strategy = getStrategy(stream); - bool shouldWait = (strategy == STRATEGY_SONIFICATION) || - (strategy == STRATEGY_SONIFICATION_RESPECTFUL) || - (beaconMuteLatency > 0); + bool shouldWait = + (followsSameRouting(clientAttr, attributes_initializer(AUDIO_USAGE_ALARM)) || + followsSameRouting(clientAttr, attributes_initializer(AUDIO_USAGE_NOTIFICATION)) || + (beaconMuteLatency > 0)); uint32_t waitMs = beaconMuteLatency; for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); @@ -1666,7 +1655,7 @@ status_t AudioPolicyManager::startSource(const sp& outp handleNotificationRoutingForStream(stream); // force reevaluating accessibility routing when ringtone or alarm starts - if (strategy == STRATEGY_SONIFICATION) { + if (followsSameRouting(clientAttr, attributes_initializer(AUDIO_USAGE_ALARM))) { mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); } @@ -1685,7 +1674,7 @@ status_t AudioPolicyManager::startSource(const sp& outp if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE && mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc); + setStrategyMute(streamToStrategy(AUDIO_STREAM_ALARM), true, outputDesc); } // Automatically enable the remote submix input when output is started on a re routing mix @@ -1747,7 +1736,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu } bool forceDeviceUpdate = false; if (client->hasPreferredDevice(true)) { - checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE); + checkStrategyRoute(client->strategy(), AUDIO_IO_HANDLE_NONE); forceDeviceUpdate = true; } @@ -1756,7 +1745,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu // store time at which the stream was stopped - see isStreamActive() if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) { - outputDesc->mStopTime[stream] = systemTime(); + outputDesc->setStopTime(client, systemTime()); DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when // the track stop() command is received and at that time the audio track buffer can @@ -1791,10 +1780,10 @@ status_t AudioPolicyManager::stopSource(const sp& outpu if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE && mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - setStrategyMute(STRATEGY_SONIFICATION, false, outputDesc); + setStrategyMute(streamToStrategy(AUDIO_STREAM_RING), false, outputDesc); } - if (stream == AUDIO_STREAM_MUSIC) { + if (followsSameRouting(client->attributes(), attributes_initializer(AUDIO_USAGE_MEDIA))) { selectOutputForMusicEffects(); } return NO_ERROR; @@ -1849,9 +1838,9 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, input_type_t *inputType, audio_port_handle_t *portId) { - ALOGV("getInputForAttr() source %d, sampling rate %d, format %#x, channel mask %#x," - "session %d, flags %#x", - attr->source, config->sample_rate, config->format, config->channel_mask, session, flags); + ALOGV("%s() source %d, sampling rate %d, format %#x, channel mask %#x, session %d, " + "flags %#x attributes=%s", __func__, attr->source, config->sample_rate, + config->format, config->channel_mask, session, flags, toString(*attr).c_str()); status_t status = NO_ERROR; audio_source_t halInputSource; @@ -1940,7 +1929,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; } else { - device = getDeviceAndMixForAttributes(attributes, &policyMix); + device = mEngine->getInputDeviceForAttributes(attributes, &policyMix); } if (device == nullptr) { ALOGW("getInputForAttr() could not find device for source %d", attributes.source); @@ -1954,8 +1943,6 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, // know about it and is therefore considered "legacy" *inputType = API_INPUT_LEGACY; } else if (audio_is_remote_submix_device(device->type())) { - device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8("0"), - AUDIO_FORMAT_DEFAULT); *inputType = API_INPUT_MIX_CAPTURE; } else if (device->type() == AUDIO_DEVICE_IN_TELEPHONY_RX) { *inputType = API_INPUT_TELEPHONY_RX; @@ -1965,9 +1952,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, } - *input = getInputForDevice(device, session, attributes.source, - config, flags, - policyMix); + *input = getInputForDevice(device, session, attributes, config, flags, policyMix); if (*input == AUDIO_IO_HANDLE_NONE) { status = INVALID_OPERATION; goto error; @@ -1975,8 +1960,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, exit: - *selectedDeviceId = mAvailableInputDevices.contains(device) ? - device->getId() : AUDIO_PORT_HANDLE_NONE; + *selectedDeviceId = mAvailableInputDevices.contains(device) ? + device->getId() : AUDIO_PORT_HANDLE_NONE; isSoundTrigger = attributes.source == AUDIO_SOURCE_HOTWORD && mSoundTriggerSessions.indexOfKey(session) > 0; @@ -2000,16 +1985,16 @@ error: audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp &device, audio_session_t session, - audio_source_t inputSource, + const audio_attributes_t &attributes, const audio_config_base_t *config, audio_input_flags_t flags, AudioMix *policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; - audio_source_t halInputSource = inputSource; + audio_source_t halInputSource = attributes.source; bool isSoundTrigger = false; - if (inputSource == AUDIO_SOURCE_HOTWORD) { + if (attributes.source == AUDIO_SOURCE_HOTWORD) { ssize_t index = mSoundTriggerSessions.indexOfKey(session); if (index >= 0) { input = mSoundTriggerSessions.valueFor(session); @@ -2019,7 +2004,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(const spformat)) { flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_VOIP_TX); } @@ -2374,10 +2359,10 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, // update volume on all outputs and streams matching the following: // - The requested stream (or a stream matching for volume control) is active on the output - // - The device (or devices) selected by the strategy corresponding to this stream includes + // - The device (or devices) selected by the engine for this stream includes // the requested device // - For non default requested device, currently selected device on the output is either the - // requested device or one of the devices selected by the strategy + // requested device or one of the devices selected by the engine for this stream // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if // no specific device volume value exists for currently selected device. status_t status = NO_ERROR; @@ -2391,9 +2376,9 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) { continue; } - routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); - audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy( - curStrategy, false /*fromCache*/)); + audio_devices_t curStreamDevice = Volume::getDeviceForVolume( + mEngine->getOutputDevicesForStream((audio_stream_type_t)curStream, + false /*fromCache*/).types()); if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curStreamDevice & device) == 0)) { continue; @@ -2435,10 +2420,10 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, if (!audio_is_output_device(device)) { return BAD_VALUE; } - // if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device corresponding to - // the strategy the stream belongs to. + // if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device selected for this + // stream by the engine. if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { - device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); + device = mEngine->getOutputDevicesForStream(stream, true /*fromCache*/).types(); } device = Volume::getDeviceForVolume(device); @@ -2458,8 +2443,8 @@ audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() // 3: The primary output // 4: the first output in the list - routing_strategy strategy = getStrategy(AUDIO_STREAM_MUSIC); - DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); + DeviceVector devices = mEngine->getOutputDevicesForAttributes( + attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, false /*fromCache*/); SortedVector outputs = getOutputsForDevices(devices, mOutputs); if (outputs.size() == 0) { @@ -2531,7 +2516,9 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, return INVALID_OPERATION; } } - return mEffects.registerEffect(desc, io, strategy, session, id); + return mEffects.registerEffect(desc, io, session, id, + (strategy == streamToStrategy(AUDIO_STREAM_MUSIC) || + strategy == PRODUCT_STRATEGY_NONE)); } status_t AudioPolicyManager::unregisterEffect(int id) @@ -2870,6 +2857,9 @@ void AudioPolicyManager::dump(String8 *dst) const mAudioPatches.dump(dst); mPolicyMixes.dump(dst); mAudioSources.dump(dst); + + dst->appendFormat("\nPolicy Engine dump:\n"); + mEngine->dump(dst); } status_t AudioPolicyManager::dump(int fd) @@ -3461,27 +3451,27 @@ void AudioPolicyManager::clearAudioPatches(uid_t uid) } } -void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy, - audio_io_handle_t ouptutToSkip) +void AudioPolicyManager::checkStrategyRoute(product_strategy_t ps, audio_io_handle_t ouptutToSkip) { - DeviceVector devices = getDevicesForStrategy(strategy, false /*fromCache*/); + // Take the first attributes following the product strategy as it is used to retrieve the routed + // device. All attributes wihin a strategy follows the same "routing strategy" + auto attributes = mEngine->getAllAttributesForProductStrategy(ps).front(); + DeviceVector devices = mEngine->getOutputDevicesForAttributes(attributes, nullptr, false); SortedVector outputs = getOutputsForDevices(devices, mOutputs); for (size_t j = 0; j < mOutputs.size(); j++) { if (mOutputs.keyAt(j) == ouptutToSkip) { continue; } sp outputDesc = mOutputs.valueAt(j); - if (!isStrategyActive(outputDesc, (routing_strategy)strategy)) { + if (!outputDesc->isStrategyActive(ps)) { continue; } // If the default device for this strategy is on another output mix, // invalidate all tracks in this strategy to force re connection. // Otherwise select new device on the output mix. if (outputs.indexOf(mOutputs.keyAt(j)) < 0) { - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - if (getStrategy((audio_stream_type_t)stream) == strategy) { - mpClientInterface->invalidateStream((audio_stream_type_t)stream); - } + for (auto stream : mEngine->getStreamTypesForProductStrategy(ps)) { + mpClientInterface->invalidateStream(stream); } } else { setOutputDevices( @@ -3493,13 +3483,18 @@ void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy, void AudioPolicyManager::clearSessionRoutes(uid_t uid) { // remove output routes associated with this uid - SortedVector affectedStrategies; + std::vector affectedStrategies; for (size_t i = 0; i < mOutputs.size(); i++) { sp outputDesc = mOutputs.valueAt(i); for (const auto& client : outputDesc->getClientIterable()) { if (client->hasPreferredDevice() && client->uid() == uid) { client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE); - affectedStrategies.add(getStrategy(client->stream())); + auto clientStrategy = client->strategy(); + if (std::find(begin(affectedStrategies), end(affectedStrategies), clientStrategy) != + end(affectedStrategies)) { + continue; + } + affectedStrategies.push_back(client->strategy()); } } } @@ -3549,7 +3544,7 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session *session = (audio_session_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION); *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(AUDIO_UNIQUE_ID_USE_INPUT); audio_attributes_t attr = { .source = AUDIO_SOURCE_HOTWORD }; - *device = getDeviceAndMixForAttributes(attr)->type(); + *device = mEngine->getInputDeviceForAttributes(attr)->type(); return mSoundTriggerSessions.acquireSession(*session, *ioHandle); } @@ -3591,8 +3586,8 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so sp sourceDesc = new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice, - streamTypefromAttributesInt(attributes), - getStrategyForAttr(attributes)); + mEngine->getStreamTypeForAttributes(*attributes), + mEngine->getProductStrategyForAttributes(*attributes)); status_t status = connectAudioSource(sourceDesc); if (status == NO_ERROR) { @@ -3609,12 +3604,12 @@ status_t AudioPolicyManager::connectAudioSource(const sp disconnectAudioSource(sourceDesc); audio_attributes_t attributes = sourceDesc->attributes(); - routing_strategy strategy = getStrategyForAttr(&attributes); audio_stream_type_t stream = sourceDesc->stream(); sp srcDevice = sourceDesc->srcDevice(); - DeviceVector sinkDevices = getDevicesForStrategy(strategy, true); - ALOG_ASSERT(!sinkDevices.isEmpty(), "connectAudioSource(): no device found for strategy"); + DeviceVector sinkDevices = + mEngine->getOutputDevicesForAttributes(attributes, nullptr, true); + ALOG_ASSERT(!sinkDevices.isEmpty(), "connectAudioSource(): no device found for attributes"); sp sinkDevice = sinkDevices.itemAt(0); ALOG_ASSERT(mAvailableOutputDevices.contains(sinkDevice), "%s: Device %s not available", __FUNCTION__, sinkDevice->toString().c_str()); @@ -3968,16 +3963,15 @@ status_t AudioPolicyManager::disconnectAudioSource(const sp AudioPolicyManager::getSourceForStrategyOnOutput( - audio_io_handle_t output, routing_strategy strategy) +sp AudioPolicyManager::getSourceForAttributesOnOutput( + audio_io_handle_t output, const audio_attributes_t &attr) { sp source; for (size_t i = 0; i < mAudioSources.size(); i++) { sp sourceDesc = mAudioSources.valueAt(i); - audio_attributes_t attributes = sourceDesc->attributes(); - routing_strategy sourceStrategy = getStrategyForAttr(&attributes); sp outputDesc = sourceDesc->swOutput().promote(); - if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) { + if (followsSameRouting(attr, sourceDesc->attributes()) && + outputDesc != 0 && outputDesc->mIoHandle == output) { source = sourceDesc; break; } @@ -4792,16 +4786,25 @@ void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function on } } -void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) +bool AudioPolicyManager::followsSameRouting(const audio_attributes_t &lAttr, + const audio_attributes_t &rAttr) const { - DeviceVector oldDevices = getDevicesForStrategy(strategy, true /*fromCache*/); - DeviceVector newDevices = getDevicesForStrategy(strategy, false /*fromCache*/); + return mEngine->getProductStrategyForAttributes(lAttr) == + mEngine->getProductStrategyForAttributes(rAttr); +} + +void AudioPolicyManager::checkOutputForAttributes(const audio_attributes_t &attr) +{ + auto psId = mEngine->getProductStrategyForAttributes(attr); + + DeviceVector oldDevices = mEngine->getOutputDevicesForAttributes(attr, 0, true /*fromCache*/); + DeviceVector newDevices = mEngine->getOutputDevicesForAttributes(attr, 0, false /*fromCache*/); SortedVector srcOutputs = getOutputsForDevices(oldDevices, mPreviousOutputs); SortedVector dstOutputs = getOutputsForDevices(newDevices, mOutputs); // also take into account external policy-related changes: add all outputs which are // associated with policies in the "before" and "after" output vectors - ALOGVV("checkOutputForStrategy(): policy related outputs"); + ALOGVV("%s(): policy related outputs", __func__); for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) { const sp desc = mPreviousOutputs.valueAt(i); if (desc != 0 && desc->mPolicyMix != NULL) { @@ -4817,7 +4820,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) } } - if (!dstOutputs.isEmpty() && srcOutputs != dstOutputs) { + if (srcOutputs != dstOutputs) { // get maximum latency of all source outputs to determine the minimum mute time guaranteeing // audio from invalidated tracks will be rendered when unmuting uint32_t maxLatency = 0; @@ -4828,50 +4831,40 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) } } ALOGV_IF(!(srcOutputs.isEmpty() || dstOutputs.isEmpty()), - "%s: strategy %d, moving from output %s to output %s", __func__, strategy, + "%s: strategy %d, moving from output %s to output %s", __func__, psId, std::to_string(srcOutputs[0]).c_str(), std::to_string(dstOutputs[0]).c_str()); // mute strategy while moving tracks from one output to another for (audio_io_handle_t srcOut : srcOutputs) { sp desc = mPreviousOutputs.valueFor(srcOut); - if (desc != 0 && isStrategyActive(desc, strategy)) { - setStrategyMute(strategy, true, desc); - setStrategyMute(strategy, false, desc, maxLatency * LATENCY_MUTE_FACTOR, + if (desc != 0 && desc->isStrategyActive(psId)) { + setStrategyMute(psId, true, desc); + setStrategyMute(psId, false, desc, maxLatency * LATENCY_MUTE_FACTOR, newDevices.types()); } - sp source = - getSourceForStrategyOnOutput(srcOut, strategy); + sp source = getSourceForAttributesOnOutput(srcOut, attr); if (source != 0){ connectAudioSource(source); } } - // Move effects associated to this strategy from previous output to new output - if (strategy == STRATEGY_MEDIA) { + // Move effects associated to this stream from previous output to new output + if (followsSameRouting(attr, attributes_initializer(AUDIO_USAGE_MEDIA))) { selectOutputForMusicEffects(); } - // Move tracks associated to this strategy from previous output to new output - for (int i = 0; i < AUDIO_STREAM_FOR_POLICY_CNT; i++) { - if (getStrategy((audio_stream_type_t)i) == strategy) { - mpClientInterface->invalidateStream((audio_stream_type_t)i); - } + // Move tracks associated to this stream (and linked) from previous output to new output + for (auto stream : mEngine->getStreamTypesForProductStrategy(psId)) { + mpClientInterface->invalidateStream(stream); } } } void AudioPolicyManager::checkOutputForAllStrategies() { - if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) - checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); - checkOutputForStrategy(STRATEGY_PHONE); - if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) - checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); - checkOutputForStrategy(STRATEGY_SONIFICATION); - checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); - checkOutputForStrategy(STRATEGY_ACCESSIBILITY); - checkOutputForStrategy(STRATEGY_MEDIA); - checkOutputForStrategy(STRATEGY_DTMF); - checkOutputForStrategy(STRATEGY_REROUTING); + for (const auto &strategy : mEngine->getOrderedProductStrategies()) { + auto attributes = mEngine->getAllAttributesForProductStrategy(strategy).front(); + checkOutputForAttributes(attributes); + } } void AudioPolicyManager::checkA2dpSuspend() @@ -4924,38 +4917,6 @@ void AudioPolicyManager::checkA2dpSuspend() } } -template -sp AudioPolicyManager::findPreferredDevice( - IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices) -{ - auto activeClients = desc->clientsList(true /*activeOnly*/); - auto activeClientsWithRoute = - desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/); - active = activeClients.size() > 0; - if (active && activeClients.size() == activeClientsWithRoute.size()) { - return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId()); - } - return nullptr; -} - -template -sp AudioPolicyManager::findPreferredDevice( - IoCollection& ioCollection, Filter filter, const DeviceVector& devices) -{ - sp device; - for (size_t i = 0; i < ioCollection.size(); i++) { - auto desc = ioCollection.valueAt(i); - bool active; - sp curDevice = findPreferredDevice(desc, filter, active, devices); - if (active && curDevice == nullptr) { - return nullptr; - } else if (curDevice != nullptr) { - device = curDevice; - } - } - return device; -} - DeviceVector AudioPolicyManager::getNewOutputDevices(const sp& outputDesc, bool fromCache) { @@ -4975,7 +4936,7 @@ DeviceVector AudioPolicyManager::getNewOutputDevices(const sp device = - findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices); + findPreferredDevice(outputDesc, PRODUCT_STRATEGY_NONE, active, mAvailableOutputDevices); if (device != nullptr) { return DeviceVector(device); } @@ -4987,54 +4948,22 @@ DeviceVector AudioPolicyManager::getNewOutputDevices(const spgetForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - devices = getDevicesForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); - } else if (isInCall() || - isStrategyActiveOnSameModule(outputDesc, STRATEGY_PHONE)) { - devices = getDevicesForStrategy(STRATEGY_PHONE, fromCache); - } else if (isStrategyActiveOnSameModule(outputDesc, STRATEGY_SONIFICATION)) { - devices = getDevicesForStrategy(STRATEGY_SONIFICATION, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) { - devices = getDevicesForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) { - devices = getDevicesForStrategy(STRATEGY_ACCESSIBILITY, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) { - devices = getDevicesForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_MEDIA)) { - devices = getDevicesForStrategy(STRATEGY_MEDIA, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_DTMF)) { - devices = getDevicesForStrategy(STRATEGY_DTMF, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { - devices = getDevicesForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); - } else if (isStrategyActive(outputDesc, STRATEGY_REROUTING)) { - devices = getDevicesForStrategy(STRATEGY_REROUTING, fromCache); - } - - ALOGV("getNewOutputDevice() selected devices %s", devices.toString().c_str()); + for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) { + StreamTypeVector streams = mEngine->getStreamTypesForProductStrategy(productStrategy); + auto attr = mEngine->getAllAttributesForProductStrategy(productStrategy).front(); + + if ((hasVoiceStream(streams) && + (isInCall() || mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc))) || + (hasStream(streams, AUDIO_STREAM_ALARM) && + mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) || + outputDesc->isStrategyActive(productStrategy)) { + // Retrieval of devices for voice DL is done on primary output profile, cannot + // check the route (would force modifying configuration file for this profile) + devices = mEngine->getOutputDevicesForAttributes(attr, nullptr, fromCache); + break; + } + } + ALOGV("%s selected devices %s", __func__, devices.toString().c_str()); return devices; } @@ -5068,7 +4997,7 @@ sp AudioPolicyManager::getNewInputDevice( attributes.source = AUDIO_SOURCE_VOICE_COMMUNICATION; } if (attributes.source != AUDIO_SOURCE_DEFAULT) { - device = getDeviceAndMixForAttributes(attributes); + device = mEngine->getInputDeviceForAttributes(attributes); } return device; @@ -5079,26 +5008,22 @@ bool AudioPolicyManager::streamsMatchForvolume(audio_stream_type_t stream1, return (stream1 == stream2); } -uint32_t AudioPolicyManager::getStrategyForStream(audio_stream_type_t stream) { - return (uint32_t)getStrategy(stream); -} - audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) { // By checking the range of stream before calling getStrategy, we avoid - // getStrategy's behavior for invalid streams. getStrategy would do a ALOGE - // and then return STRATEGY_MEDIA, but we want to return the empty set. - if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) { + // getOutputDevicesForStream's behavior for invalid streams. + // engine's getOutputDevicesForStream would fallback on its default behavior (most probably + // device for music stream), but we want to return the empty set. + if (stream < AUDIO_STREAM_MIN || stream >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } DeviceVector activeDevices; DeviceVector devices; - for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { - if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { + for (audio_stream_type_t curStream = AUDIO_STREAM_MIN; curStream < AUDIO_STREAM_PUBLIC_CNT; + curStream = (audio_stream_type_t) (curStream + 1)) { + if (!streamsMatchForvolume(stream, curStream)) { continue; } - routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); - DeviceVector curDevices = - getDevicesForStrategy((routing_strategy)curStrategy, false /*fromCache*/); + DeviceVector curDevices = mEngine->getOutputDevicesForStream(curStream, false/*fromCache*/); devices.merge(curDevices); for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) { sp outputDesc = mOutputs.valueFor(output); @@ -5123,28 +5048,10 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre return devices.types(); } -routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) const -{ - ALOG_ASSERT(stream != AUDIO_STREAM_PATCH,"getStrategy() called for AUDIO_STREAM_PATCH"); - return mEngine->getStrategyForStream(stream); -} - -routing_strategy AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { - // flags to strategy mapping - if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { - return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; - } - if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { - return STRATEGY_ENFORCED_AUDIBLE; - } - // usage to strategy mapping - return mEngine->getStrategyForUsage(attr->usage); -} - void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) { switch(stream) { case AUDIO_STREAM_MUSIC: - checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + checkOutputForAttributes(attributes_initializer(AUDIO_USAGE_NOTIFICATION)); updateDevicesAndOutputs(); break; default: @@ -5211,33 +5118,14 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) { return 0; } -DeviceVector AudioPolicyManager::getDevicesForStrategy(routing_strategy strategy, bool fromCache) -{ - // Honor explicit routing requests only if all active clients have a preferred route in which - // case the last active client route is used - sp device = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices); - if (device != nullptr) { - return DeviceVector(device); - } - - if (fromCache) { - ALOGVV("%s from cache strategy %d, device %s", __func__, strategy, - mDevicesForStrategy[strategy].toString().c_str()); - return mDevicesForStrategy[strategy]; - } - return mAvailableOutputDevices.getDevicesFromTypeMask(mEngine->getDeviceForStrategy(strategy)); -} - void AudioPolicyManager::updateDevicesAndOutputs() { - for (int i = 0; i < NUM_STRATEGIES; i++) { - mDevicesForStrategy[i] = getDevicesForStrategy((routing_strategy)i, false /*fromCache*/); - } + mEngine->updateDeviceSelectionCache(); mPreviousOutputs = mOutputs; } uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const sp& outputDesc, - audio_devices_t prevDeviceType, + const DeviceVector &prevDevices, uint32_t delayMs) { // mute/unmute strategies using an incompatible device combination @@ -5248,22 +5136,24 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spdevices().types(); - bool shouldMute = outputDesc->isActive() && (popcount(deviceType) >= 2); - - for (size_t i = 0; i < NUM_STRATEGIES; i++) { - audio_devices_t curDeviceType = - getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); - curDeviceType = curDeviceType & outputDesc->supportedDevices().types(); - bool mute = shouldMute && (curDeviceType & deviceType) && (curDeviceType != deviceType); + DeviceVector devices = outputDesc->devices(); + bool shouldMute = outputDesc->isActive() && (devices.size() >= 2); + + auto productStrategies = mEngine->getOrderedProductStrategies(); + for (const auto &productStrategy : productStrategies) { + auto attributes = mEngine->getAllAttributesForProductStrategy(productStrategy).front(); + DeviceVector curDevices = + mEngine->getOutputDevicesForAttributes(attributes, nullptr, false/*fromCache*/); + curDevices = curDevices.filter(outputDesc->supportedDevices()); + bool mute = shouldMute && curDevices.containsAtLeastOne(devices) && curDevices != devices; bool doMute = false; - if (mute && !outputDesc->mStrategyMutedByDevice[i]) { + if (mute && !outputDesc->isStrategyMutedByDevice(productStrategy)) { doMute = true; - outputDesc->mStrategyMutedByDevice[i] = true; - } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){ + outputDesc->setStrategyMutedByDevice(productStrategy, true); + } else if (!mute && outputDesc->isStrategyMutedByDevice(productStrategy)) { doMute = true; - outputDesc->mStrategyMutedByDevice[i] = false; + outputDesc->setStrategyMutedByDevice(productStrategy, false); } if (doMute) { for (size_t j = 0; j < mOutputs.size(); j++) { @@ -5272,10 +5162,10 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spsupportedDevices().containsAtLeastOne(outputDesc->supportedDevices())) { continue; } - ALOGVV("checkDeviceMuteStrategies() %s strategy %zu (curDevice %04x)", - mute ? "muting" : "unmuting", i, curDeviceType); - setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs); - if (isStrategyActive(desc, (routing_strategy)i)) { + ALOGVV("%s() %s (curDevice %s)", __func__, + mute ? "muting" : "unmuting", curDevices.toString().c_str()); + setStrategyMute(productStrategy, mute, desc, mute ? 0 : delayMs); + if (desc->isStrategyActive(productStrategy)) { if (mute) { // FIXME: should not need to double latency if volume could be applied // immediately by the audioflinger mixer. We must account for the delay @@ -5293,7 +5183,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spisActive() && (deviceType != prevDeviceType)) { + if (outputDesc->isActive() && (devices != prevDevices)) { uint32_t tempMuteWaitMs = outputDesc->latency() * 2; // temporary mute duration is conservatively set to 4 times the reported latency uint32_t tempMuteDurationMs = outputDesc->latency() * 4; @@ -5301,13 +5191,13 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spisStrategyActive(productStrategy)) { // make sure that we do not start the temporary mute period too early in case of // delayed device change - setStrategyMute((routing_strategy)i, true, outputDesc, delayMs); - setStrategyMute((routing_strategy)i, false, outputDesc, - delayMs + tempMuteDurationMs, deviceType); + setStrategyMute(productStrategy, true, outputDesc, delayMs); + setStrategyMute(productStrategy, false, outputDesc, delayMs + tempMuteDurationMs, + devices.types()); } } } @@ -5359,7 +5249,7 @@ uint32_t AudioPolicyManager::setOutputDevices(const sp& // if the outputs are not materially active, there is no need to mute. if (requiresMuteCheck) { - muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevices.types(), delayMs); + muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevices, delayMs); } else { ALOGV("%s: suppressing checkDeviceMuteStrategies", __func__); muteWaitMs = 0; @@ -5531,36 +5421,6 @@ sp AudioPolicyManager::getInputProfile(const sp &de return NULL; } -sp AudioPolicyManager::getDeviceAndMixForAttributes( - const audio_attributes_t &attributes, AudioMix **policyMix) -{ - // Honor explicit routing requests only if all active clients have a preferred route in which - // case the last active client route is used - sp device = - findPreferredDevice(mInputs, attributes.source, mAvailableInputDevices); - if (device != nullptr) { - return device; - } - - sp selectedDeviceFromMix = - mPolicyMixes.getDeviceAndMixForInputSource(attributes.source, mAvailableInputDevices, - policyMix); - return (selectedDeviceFromMix != nullptr) ? - selectedDeviceFromMix : getDeviceForAttributes(attributes); -} - -sp AudioPolicyManager::getDeviceForAttributes(const audio_attributes_t &attributes) -{ - audio_devices_t device = mEngine->getDeviceForInputSource(attributes.source); - if (attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX && - strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) { - return mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - String8(attributes.tags + strlen("addr=")), - AUDIO_FORMAT_DEFAULT); - } - return mAvailableInputDevices.getDevice(device, String8(), AUDIO_FORMAT_DEFAULT); -} - float AudioPolicyManager::computeVolume(audio_stream_type_t stream, int index, audio_devices_t device) @@ -5613,18 +5473,18 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, // speaker is part of the select devices // - if music is playing, always limit the volume to current music volume, // with a minimum threshold at -36dB so that notification is always perceived. - const routing_strategy stream_strategy = getStrategy(stream); if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | AUDIO_DEVICE_OUT_USB_HEADSET | AUDIO_DEVICE_OUT_HEARING_AID)) && - ((stream_strategy == STRATEGY_SONIFICATION) - || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) + ((stream == AUDIO_STREAM_ALARM || stream == AUDIO_STREAM_RING) + || (stream == AUDIO_STREAM_NOTIFICATION) || (stream == AUDIO_STREAM_SYSTEM) - || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && - (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && + || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == + AUDIO_POLICY_FORCE_NONE))) && mVolumeCurves->canBeMuted(stream)) { // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was @@ -5632,7 +5492,9 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || mLimitRingtoneVolume) { volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; - audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); + audio_devices_t musicDevice = + mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), + nullptr, true /*fromCache*/).types(); float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC, musicDevice), @@ -5656,7 +5518,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, } } } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) || - stream_strategy != STRATEGY_SONIFICATION) { + (stream != AUDIO_STREAM_ALARM && stream != AUDIO_STREAM_RING)) { volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; } } @@ -5761,18 +5623,16 @@ void AudioPolicyManager::applyStreamVolumes(const sp& out } } -void AudioPolicyManager::setStrategyMute(routing_strategy strategy, - bool on, - const sp& outputDesc, - int delayMs, - audio_devices_t device) +void AudioPolicyManager::setStrategyMute(product_strategy_t strategy, + bool on, + const sp& outputDesc, + int delayMs, + audio_devices_t device) { - ALOGVV("setStrategyMute() strategy %d, mute %d, output ID %d", - strategy, on, outputDesc->getId()); - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - if (getStrategy((audio_stream_type_t)stream) == strategy) { - setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device); - } + for (auto stream: mEngine->getStreamTypesForProductStrategy(strategy)) { + ALOGVV("%s() stream %d, mute %d, output ID %d", __FUNCTION__, stream, on, + outputDesc->getId()); + setStreamMute(stream, on, outputDesc, delayMs, device); } } @@ -5814,25 +5674,9 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } } -audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_attributes_t *attr) -{ - // flags to stream type mapping - if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { - return AUDIO_STREAM_ENFORCED_AUDIBLE; - } - if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) { - return AUDIO_STREAM_BLUETOOTH_SCO; - } - if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { - return AUDIO_STREAM_TTS; - } - - return audio_usage_to_stream_type(attr->usage); -} - bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) { - // has flags that map to a strategy? + // has flags that map to a stream type? if ((paa->flags & (AUDIO_FLAG_AUDIBILITY_ENFORCED | AUDIO_FLAG_SCO | AUDIO_FLAG_BEACON)) != 0) { return true; } @@ -5863,37 +5707,6 @@ bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) return true; } -bool AudioPolicyManager::isStrategyActive(const sp& outputDesc, - routing_strategy strategy, uint32_t inPastMs, - nsecs_t sysTime) const -{ - if ((sysTime == 0) && (inPastMs != 0)) { - sysTime = systemTime(); - } - for (int i = 0; i < (int)AUDIO_STREAM_FOR_POLICY_CNT; i++) { - if (((getStrategy((audio_stream_type_t)i) == strategy) || - (STRATEGY_NONE == strategy)) && - outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { - return true; - } - } - return false; -} - -bool AudioPolicyManager::isStrategyActiveOnSameModule(const sp& outputDesc, - routing_strategy strategy, uint32_t inPastMs, - nsecs_t sysTime) const -{ - for (size_t i = 0; i < mOutputs.size(); i++) { - sp desc = mOutputs.valueAt(i); - if (outputDesc->sharesHwModuleWith(desc) - && isStrategyActive(desc, strategy, inPastMs, sysTime)) { - return true; - } - } - return false; -} - audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage) { return mEngine->getForceUse(usage); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index e715fd4ac2..0bf40f4ab4 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -153,9 +153,15 @@ public: audio_devices_t device); // return the strategy corresponding to a given stream type - virtual uint32_t getStrategyForStream(audio_stream_type_t stream); - // return the strategy corresponding to the given audio attributes - virtual routing_strategy getStrategyForAttr(const audio_attributes_t *attr); + virtual uint32_t getStrategyForStream(audio_stream_type_t stream) + { + return streamToStrategy(stream); + } + product_strategy_t streamToStrategy(audio_stream_type_t stream) const + { + auto attributes = mEngine->getAttributesForStreamType(stream); + return mEngine->getProductStrategyForAttributes(attributes); + } // return the enabled output devices for the given stream type virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); @@ -244,9 +250,6 @@ public: virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( std::vector *formats); - // return the strategy corresponding to a given stream type - routing_strategy getStrategy(audio_stream_type_t stream) const; - virtual void setAppState(uid_t uid, app_state_t state); virtual bool isHapticPlaybackSupported(); @@ -316,32 +319,6 @@ protected: void removeOutput(audio_io_handle_t output); void addInput(audio_io_handle_t input, const sp& inputDesc); - // return appropriate device for streams handled by the specified strategy according to current - // phone state, connected devices... - // if fromCache is true, the device is returned from mDeviceForStrategy[], - // otherwise it is determine by current state - // (device connected,phone state, force use, a2dp output...) - // This allows to: - // 1 speed up process when the state is stable (when starting or stopping an output) - // 2 access to either current device selection (fromCache == true) or - // "future" device selection (fromCache == false) when called from a context - // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND - // before updateDevicesAndOutputs() is called. - virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, - bool fromCache) - { - return getDevicesForStrategy(strategy, fromCache).types(); - } - - DeviceVector getDevicesForStrategy(routing_strategy strategy, bool fromCache); - - bool isStrategyActive(const sp& outputDesc, routing_strategy strategy, - uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; - - bool isStrategyActiveOnSameModule(const sp& outputDesc, - routing_strategy strategy, uint32_t inPastMs = 0, - nsecs_t sysTime = 0) const; - // change the route of the specified output. Returns the number of ms we have slept to // allow new routing to take effect in certain cases. uint32_t setOutputDevices(const sp& outputDesc, @@ -360,9 +337,6 @@ protected: status_t resetInputDevice(audio_io_handle_t input, audio_patch_handle_t *patchHandle = NULL); - // select input device corresponding to requested audio source - sp getDeviceForAttributes(const audio_attributes_t &attributes); - // compute the actual volume for a given stream according to the requested index and a particular // device virtual float computeVolume(audio_stream_type_t stream, @@ -383,8 +357,16 @@ protected: void applyStreamVolumes(const sp& outputDesc, audio_devices_t device, int delayMs = 0, bool force = false); - // Mute or unmute all streams handled by the specified strategy on the specified output - void setStrategyMute(routing_strategy strategy, + /** + * @brief setStrategyMute Mute or unmute all active clients on the considered output + * following the given strategy. + * @param strategy to be considered + * @param on true for mute, false for unmute + * @param outputDesc to be considered + * @param delayMs + * @param device + */ + void setStrategyMute(product_strategy_t strategy, bool on, const sp& outputDesc, int delayMs = 0, @@ -430,26 +412,28 @@ protected: // A2DP suspend status is rechecked. void checkForDeviceAndOutputChanges(std::function onOutputsChecked = nullptr); - // checks and if necessary changes outputs used for all strategies. - // must be called every time a condition that affects the output choice for a given strategy - // changes: connected device, phone state, force use... - // Must be called before updateDevicesAndOutputs() - void checkOutputForStrategy(routing_strategy strategy); + /** + * @brief checkOutputForAttributes checks and if necessary changes outputs used for the + * given audio attributes. + * must be called every time a condition that affects the output choice for a given + * attributes changes: connected device, phone state, force use... + * Must be called before updateDevicesAndOutputs() + * @param attr to be considered + */ + void checkOutputForAttributes(const audio_attributes_t &attr); + + bool followsSameRouting(const audio_attributes_t &lAttr, + const audio_attributes_t &rAttr) const; - // Same as checkOutputForStrategy() but for a all strategies in order of priority + /** + * @brief checkOutputForAllStrategies Same as @see checkOutputForAttributes() + * but for a all product strategies in order of priority + */ void checkOutputForAllStrategies(); // manages A2DP output suspend/restore according to phone state and BT SCO usage void checkA2dpSuspend(); - template - sp findPreferredDevice(IoDescriptor& desc, Filter filter, - bool& active, const DeviceVector& devices); - - template - sp findPreferredDevice(IoCollection& ioCollection, Filter filter, - const DeviceVector& devices); - // selects the most appropriate device on output for current state // must be called every time a condition that affects the device choice for a given output is // changed: connected device, phone state, force use, output start, output stop.. @@ -457,11 +441,14 @@ protected: DeviceVector getNewOutputDevices(const sp& outputDesc, bool fromCache); - // updates cache of device used by all strategies (mDeviceForStrategy[]) - // must be called every time a condition that affects the device choice for a given strategy is - // changed: connected device, phone state, force use... - // cached values are used by getDeviceForStrategy() if parameter fromCache is true. - // Must be called after checkOutputForAllStrategies() + /** + * @brief updateDevicesAndOutputs: updates cache of devices of the engine + * must be called every time a condition that affects the device choice is changed: + * connected device, phone state, force use... + * cached values are used by getOutputDevicesForStream()/getDevicesForAttributes if + * parameter fromCache is true. + * Must be called after checkOutputForAllStrategies() + */ void updateDevicesAndOutputs(); // selects the most appropriate device on input for current state @@ -480,13 +467,19 @@ protected: SortedVector getOutputsForDevices( const DeviceVector &devices, const SwAudioOutputCollection& openOutputs); - // mute/unmute strategies using an incompatible device combination - // if muting, wait for the audio in pcm buffer to be drained before proceeding - // if unmuting, unmute only after the specified delay - // Returns the number of ms waited - virtual uint32_t checkDeviceMuteStrategies(const sp& outputDesc, - audio_devices_t prevDeviceType, - uint32_t delayMs); + /** + * @brief checkDeviceMuteStrategies mute/unmute strategies + * using an incompatible device combination. + * if muting, wait for the audio in pcm buffer to be drained before proceeding + * if unmuting, unmute only after the specified delay + * @param outputDesc + * @param prevDevice + * @param delayMs + * @return the number of ms waited + */ + virtual uint32_t checkDeviceMuteStrategies(const sp& outputDesc, + const DeviceVector &prevDevices, + uint32_t delayMs); audio_io_handle_t selectOutput(const SortedVector& outputs, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, @@ -580,15 +573,22 @@ protected: void clearAudioPatches(uid_t uid); void clearSessionRoutes(uid_t uid); - void checkStrategyRoute(routing_strategy strategy, audio_io_handle_t ouptutToSkip); + + /** + * @brief checkStrategyRoute: when an output is beeing rerouted, reconsider each output + * that may host a strategy playing on the considered output. + * @param ps product strategy that initiated the rerouting + * @param ouptutToSkip output that initiated the rerouting + */ + void checkStrategyRoute(product_strategy_t ps, audio_io_handle_t ouptutToSkip); status_t hasPrimaryOutput() const { return mPrimaryOutput != 0; } status_t connectAudioSource(const sp& sourceDesc); status_t disconnectAudioSource(const sp& sourceDesc); - sp getSourceForStrategyOnOutput(audio_io_handle_t output, - routing_strategy strategy); + sp getSourceForAttributesOnOutput(audio_io_handle_t output, + const audio_attributes_t &attr); void cleanUpForDevice(const sp& deviceDesc); @@ -616,15 +616,6 @@ protected: bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected - /** - * @brief mDevicesForStrategy vector of devices that are assigned for a given strategy. - * Note: in case of removal of device (@see setDeviceConnectionState), the device descriptor - * will be removed from the @see mAvailableOutputDevices or @see mAvailableInputDevices - * but the devices for strategies will be reevaluated within the - * @see setDeviceConnectionState function. - */ - DeviceVector mDevicesForStrategy[NUM_STRATEGIES]; - float mLastVoiceVolume; // last voice volume value sent to audio HAL bool mA2dpSuspended; // true if A2DP output is suspended @@ -727,16 +718,26 @@ private: audio_stream_type_t stream, const audio_config_t *config, audio_output_flags_t *flags); - // internal method to return the input handle for the given device and format + + /** + * @brief getInputForDevice selects an input handle for a given input device and + * requester context + * @param device to be used by requester, selected by policy mix rules or engine + * @param session requester session id + * @param uid requester uid + * @param attributes requester audio attributes (e.g. input source and tags matter) + * @param config requester audio configuration (e.g. sample rate, format, channel mask). + * @param flags requester input flags + * @param policyMix may be null, policy rules to be followed by the requester + * @return input io handle aka unique input identifier selected for this device. + */ audio_io_handle_t getInputForDevice(const sp &device, audio_session_t session, - audio_source_t inputSource, + const audio_attributes_t &attributes, const audio_config_base_t *config, audio_input_flags_t flags, AudioMix *policyMix); - // internal function to derive a stream type value from audio attributes - audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); // event is one of STARTING_OUTPUT, STARTING_BEACON, STOPPING_OUTPUT, STOPPING_BEACON // returns 0 if no mute/unmute event happened, the largest latency of the device where // the mute/unmute happened @@ -744,11 +745,6 @@ private: uint32_t setBeaconMute(bool mute); bool isValidAttributes(const audio_attributes_t *paa); - // select input device corresponding to requested audio source and return associated policy - // mix if any. Calls getDeviceForInputSource(). - sp getDeviceAndMixForAttributes(const audio_attributes_t &attributes, - AudioMix **policyMix = NULL); - // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t deviceType, audio_policy_dev_state_t state, diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index d9fbdaafb1..63cb18757e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -703,11 +703,12 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) { if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { - return 0; + return PRODUCT_STRATEGY_NONE; } if (mAudioPolicyManager == NULL) { - return 0; + return PRODUCT_STRATEGY_NONE; } + // DO NOT LOCK, may be called from AudioFlinger with lock held, reaching deadlock AutoCallerClear acc; return mAudioPolicyManager->getStrategyForStream(stream); } diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index 5f0974ea04..97be44cea2 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -4,7 +4,6 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := \ frameworks/av/services/audiopolicy \ - frameworks/av/services/audiopolicy/engine/interface \ $(call include-path-for, audio-utils) \ LOCAL_SHARED_LIBRARIES := \ @@ -18,7 +17,8 @@ LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents \ LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers LOCAL_SRC_FILES := \ audiopolicymanager_tests.cpp \ -- GitLab From f579db5aa9e1cbbe5a8b7689e319913e154cda3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 13 Nov 2018 11:25:16 +0100 Subject: [PATCH 0878/1530] audiopolicy: exclusive Preferred Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It allows to force the routing of music during call without affecting clients that does not explicitely requested to use another device than the one decided by the policy during call (which is TELEPHONY TX). It fixes differently Bug 111467967. Test: make Change-Id: I6034f0d2568e1b2a1e600d9ae1453fd0c60ed02e Signed-off-by: François Gaffie --- .../include/ClientDescriptor.h | 22 ++++++++++++------- .../src/AudioOutputDescriptor.cpp | 3 ++- .../src/ClientDescriptor.cpp | 2 +- .../managerdefault/AudioPolicyManager.cpp | 14 +++++++----- .../managerdefault/AudioPolicyManager.h | 3 ++- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 2fb1277b96..4c069e40dc 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -41,10 +41,12 @@ class ClientDescriptor: public RefBase { public: ClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, - audio_attributes_t attributes, audio_config_base_t config, - audio_port_handle_t preferredDeviceId) : + audio_attributes_t attributes, audio_config_base_t config, + audio_port_handle_t preferredDeviceId, + bool isPreferredDeviceForExclusiveUse = false) : mPortId(portId), mUid(uid), mSessionId(sessionId), mAttributes(attributes), - mConfig(config), mPreferredDeviceId(preferredDeviceId), mActive(false) {} + mConfig(config), mPreferredDeviceId(preferredDeviceId), mActive(false), + mPreferredDeviceForExclusiveUse(isPreferredDeviceForExclusiveUse){} ~ClientDescriptor() override = default; virtual void dump(String8 *dst, int spaces, int index) const; @@ -58,7 +60,8 @@ public: audio_port_handle_t preferredDeviceId() const { return mPreferredDeviceId; }; void setPreferredDeviceId(audio_port_handle_t preferredDeviceId) { mPreferredDeviceId = preferredDeviceId; - }; + } + bool isPreferredDeviceForExclusiveUse() const { return mPreferredDeviceForExclusiveUse; } void setActive(bool active) { mActive = active; } bool active() const { return mActive; } bool hasPreferredDevice(bool activeOnly = false) const { @@ -73,16 +76,19 @@ private: const audio_config_base_t mConfig; audio_port_handle_t mPreferredDeviceId; // selected input device port ID bool mActive; + bool mPreferredDeviceForExclusiveUse = false; }; class TrackClientDescriptor: public ClientDescriptor { public: TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, - audio_attributes_t attributes, audio_config_base_t config, - audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, - product_strategy_t strategy, audio_output_flags_t flags) : - ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), + audio_attributes_t attributes, audio_config_base_t config, + audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, + product_strategy_t strategy, audio_output_flags_t flags, + bool isPreferredDeviceForExclusiveUse) : + ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId, + isPreferredDeviceForExclusiveUse), mStream(stream), mStrategy(strategy), mFlags(flags) {} ~TrackClientDescriptor() override = default; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index d997bf74af..1dfd88a20f 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -258,7 +258,8 @@ TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, product_st for (const auto &client : getClientIterable()) { if ((!activeOnly || client->active()) && (strategy == PRODUCT_STRATEGY_NONE || strategy == client->strategy()) - && (!preferredDeviceOnly || client->hasPreferredDevice())) { + && (!preferredDeviceOnly || + (client->hasPreferredDevice() && !client->isPreferredDeviceForExclusiveUse()))) { clients.push_back(client); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 1525285f55..a6f6c3b5c8 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -85,7 +85,7 @@ SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t product_strategy_t strategy) : TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, - stream, strategy, AUDIO_OUTPUT_FLAG_NONE), + stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false), mPatchDesc(patchDesc), mSrcDevice(srcDevice) { } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 771f62aade..126a5f7e69 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -916,7 +916,8 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, uid_t uid, const audio_config_t *config, audio_output_flags_t *flags, - audio_port_handle_t *selectedDeviceId) + audio_port_handle_t *selectedDeviceId, + bool *isRequestedDeviceForExclusiveUse) { DeviceVector outputDevices; const audio_port_handle_t requestedPortId = *selectedDeviceId; @@ -980,7 +981,7 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, isInCall()) { if (requestedPortId != AUDIO_PORT_HANDLE_NONE) { *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC; - // @todo: provide another solution (separated CL) + *isRequestedDeviceForExclusiveUse = true; } } @@ -1030,8 +1031,9 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } const audio_port_handle_t requestedPortId = *selectedDeviceId; audio_attributes_t resultAttr; + bool isRequestedDeviceForExclusiveUse = false; status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid, - config, flags, selectedDeviceId); + config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse); if (status != NO_ERROR) { return status; } @@ -1045,7 +1047,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, requestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), - *flags); + *flags, isRequestedDeviceForExclusiveUse); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); @@ -3633,8 +3635,10 @@ status_t AudioPolicyManager::connectAudioSource(const sp config.format = sourceDesc->config().format; audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE; + bool isRequestedDeviceForExclusiveUse = false; getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, - &attributes, &stream, sourceDesc->uid(), &config, &flags, &selectedDeviceId); + &attributes, &stream, sourceDesc->uid(), &config, &flags, + &selectedDeviceId, &isRequestedDeviceForExclusiveUse); if (output == AUDIO_IO_HANDLE_NONE) { ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types()); return INVALID_OPERATION; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 0bf40f4ab4..73c3b5683e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -710,7 +710,8 @@ private: uid_t uid, const audio_config_t *config, audio_output_flags_t *flags, - audio_port_handle_t *selectedDeviceId); + audio_port_handle_t *selectedDeviceId, + bool *isRequestedDeviceForExclusiveUse); // internal method to return the output handle for the given device and format audio_io_handle_t getOutputForDevices( const DeviceVector &devices, -- GitLab From 6054a7765cfd65c34d49a5a435bee1d0cdfb2d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 6 Nov 2018 13:10:58 +0100 Subject: [PATCH 0879/1530] audiopolicy: engine: remove deprecated APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: make Change-Id: Idfcb060095da92adb286706d4eaf9b9a78a8c61a Signed-off-by: François Gaffie --- .../common/include/RoutingStrategy.h | 35 - .../interface/AudioPolicyManagerInterface.h | 46 -- .../audiopolicy/engineconfigurable/Android.mk | 2 - .../interface/AudioPolicyPluginInterface.h | 57 -- .../parameter-framework/examples/Android.mk | 13 - .../examples/Car/Android.mk | 13 +- .../Car/Structure/PolicySubsystem.xml | 53 -- .../examples/Phone/Android.mk | 11 - .../Phone/Structure/PolicySubsystem.xml | 53 -- .../device_for_strategy_accessibility.pfw | 594 ----------------- .../Settings/device_for_strategy_dtmf.pfw | 624 ------------------ .../device_for_strategy_enforced_audible.pfw | 359 ---------- .../Settings/device_for_strategy_media.pfw | 339 ---------- .../Settings/device_for_strategy_phone.pfw | 483 -------------- .../device_for_strategy_rerouting.pfw | 301 --------- .../device_for_strategy_sonification.pfw | 490 -------------- ...e_for_strategy_sonification_respectful.pfw | 553 ---------------- ...r_strategy_transmitted_through_speaker.pfw | 43 -- .../examples/Settings/strategy_for_stream.pfw | 20 - .../Structure/PolicySubsystem-CommonTypes.xml | 33 - .../examples/Structure/PolicySubsystem.xml | 52 -- .../parameter-framework/plugin/Android.mk | 2 - .../plugin/PolicySubsystem.cpp | 14 - .../plugin/PolicySubsystem.h | 2 - .../parameter-framework/plugin/Strategy.cpp | 79 --- .../parameter-framework/plugin/Strategy.h | 47 -- .../parameter-framework/plugin/Stream.cpp | 8 +- .../parameter-framework/plugin/Stream.h | 1 - .../parameter-framework/plugin/Usage.cpp | 53 -- .../parameter-framework/plugin/Usage.h | 47 -- .../engineconfigurable/src/Collection.h | 8 - .../engineconfigurable/src/Element.h | 4 +- .../engineconfigurable/src/Engine.cpp | 60 -- .../engineconfigurable/src/Engine.h | 37 -- .../engineconfigurable/src/InputSource.h | 2 +- .../engineconfigurable/src/Strategy.cpp | 66 -- .../engineconfigurable/src/Strategy.h | 87 --- .../engineconfigurable/src/Stream.cpp | 26 - .../engineconfigurable/src/Stream.h | 10 +- .../engineconfigurable/src/Usage.cpp | 55 -- .../engineconfigurable/src/Usage.h | 86 --- .../audiopolicy/enginedefault/src/Engine.cpp | 82 --- .../audiopolicy/enginedefault/src/Engine.h | 10 +- 43 files changed, 11 insertions(+), 4949 deletions(-) delete mode 100644 services/audiopolicy/common/include/RoutingStrategy.h delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_phone.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification_respectful.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_transmitted_through_speaker.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_stream.pfw delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.h delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.h delete mode 100644 services/audiopolicy/engineconfigurable/src/Strategy.cpp delete mode 100644 services/audiopolicy/engineconfigurable/src/Strategy.h delete mode 100644 services/audiopolicy/engineconfigurable/src/Usage.cpp delete mode 100644 services/audiopolicy/engineconfigurable/src/Usage.h diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h deleted file mode 100644 index bddb34052d..0000000000 --- a/services/audiopolicy/common/include/RoutingStrategy.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android { - -enum routing_strategy { - STRATEGY_NONE = -1, - STRATEGY_MEDIA, - STRATEGY_PHONE, - STRATEGY_SONIFICATION, - STRATEGY_SONIFICATION_RESPECTFUL, - STRATEGY_DTMF, - STRATEGY_ENFORCED_AUDIBLE, - STRATEGY_TRANSMITTED_THROUGH_SPEAKER, - STRATEGY_ACCESSIBILITY, - STRATEGY_REROUTING, - NUM_STRATEGIES -}; - -}; //namespace android diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 18ba2c8892..498cc3bf82 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include #include #include @@ -55,51 +54,6 @@ public: */ virtual void setObserver(AudioPolicyManagerObserver *observer) = 0; - /** - * Get the input device selected for a given input source. - * - * @param[in] inputSource to get the selected input device associated to - * - * @return selected input device for the given input source, may be none if error. - * - * @deprecated use getInputDeviceForAttributes - */ - virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const = 0; - - /** - * Get the output device associated to a given strategy. - * - * @param[in] stream type for which the selected ouput device is requested. - * - * @return selected ouput device for the given strategy, may be none if error. - * - * @deprecated use getOutputDeviceForAttributes - */ - virtual audio_devices_t getDeviceForStrategy(routing_strategy stategy) const = 0; - - /** - * Get the strategy selected for a given stream type. - * - * @param[in] stream: for which the selected strategy followed by is requested. - * - * @return strategy to be followed. - * - * @deprecated use getOrderedStreams() / getLinkedStreams() to apply operation on stream - * following same former routing_strategy - */ - virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) = 0; - - /** - * Get the strategy selected for a given usage. - * - * @param[in] usage to get the selected strategy followed by. - * - * @return strategy to be followed. - * - * @deprecated use getProductStrategyForAttributes - */ - virtual routing_strategy getStrategyForUsage(audio_usage_t usage) = 0; - /** * Set the Telephony Mode. * diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index 13d874aa8d..bbd9688529 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -11,8 +11,6 @@ LOCAL_SRC_FILES := \ src/Engine.cpp \ src/EngineInstance.cpp \ src/Stream.cpp \ - src/Strategy.cpp \ - src/Usage.cpp \ src/InputSource.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h index 53be275c54..1fc22647c6 100644 --- a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h +++ b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include #include #include @@ -37,19 +36,6 @@ namespace android { class AudioPolicyPluginInterface { public: - /** - * Add a strategy to the engine - * - * @param[in] name of the strategy to add - * @param[in] identifier: the numerical value associated to this member. It MUST match either - * system/audio.h or system/audio_policy.h enumration value in order to link the - * parameter controled by the PFW and the policy manager component. - * - * @return NO_ERROR if the strategy has been added successfully, error code otherwise. - * - */ - virtual android::status_t addStrategy(const std::string &name, routing_strategy id) = 0; - /** * Add a streams to the engine. * @@ -63,19 +49,6 @@ public: */ virtual android::status_t addStream(const std::string &name, audio_stream_type_t id) = 0; - /** - * Add a usage to the engine - * - * @param[in] name of the usage to add - * @param[in] identifier: the numerical value associated to this member. It MUST match either - * system/audio.h or system/audio_policy.h enumration value in order to link the - * parameter controled by the PFW and the policy manager component. - * - * @return NO_ERROR if the usage has been added successfully, error code otherwise. - * - */ - virtual android::status_t addUsage(const std::string &name, audio_usage_t id) = 0; - /** * Add an input source to the engine * @@ -89,26 +62,6 @@ public: */ virtual android::status_t addInputSource(const std::string &name, audio_source_t id) = 0; - /** - * Set the device to be used by a strategy. - * - * @param[in] strategy: name of the strategy for which the device to use has to be set - * @param[in] devices; mask of devices to be used for the given strategy. - * - * @return true if the devices were set correclty for this strategy, false otherwise. - */ - virtual bool setDeviceForStrategy(const routing_strategy &strategy, audio_devices_t devices) = 0; - - /** - * Set the strategy to be followed by a stream. - * - * @param[in] stream: name of the stream for which the strategy to use has to be set - * @param[in] strategy to follow for the given stream. - * - * @return true if the strategy were set correclty for this stream, false otherwise. - */ - virtual bool setStrategyForStream(const audio_stream_type_t &stream, routing_strategy strategy) = 0; - /** * Set the strategy to be followed by a stream. * @@ -120,16 +73,6 @@ public: virtual bool setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &volumeProfile) = 0; - /** - * Set the strategy to be followed by a usage. - * - * @param[in] usage: name of the usage for which the strategy to use has to be set - * @param[in] strategy to follow for the given usage. - * - * @return true if the strategy were set correclty for this usage, false otherwise. - */ - virtual bool setStrategyForUsage(const audio_usage_t &usage, routing_strategy strategy) = 0; - /** * Set the input device to be used by an input source. * diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index bf7ebb69bc..060830b78b 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -112,8 +112,6 @@ PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteri PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoOutput/device_for_strategies.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_stream.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_usage.pfw \ $(LOCAL_PATH)/Settings/device_for_input_source.pfw \ $(LOCAL_PATH)/Settings/volumes.pfw @@ -139,17 +137,6 @@ PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrame PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ - $(LOCAL_PATH)/Settings/device_for_strategy_media.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_phone.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_sonification.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_sonification_respectful.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_dtmf.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_enforced_audible.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_transmitted_through_speaker.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_accessibility.pfw \ - $(LOCAL_PATH)/Settings/device_for_strategy_rerouting.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_stream.pfw \ - $(LOCAL_PATH)/Settings/strategy_for_usage.pfw \ $(LOCAL_PATH)/SettingsNoInput/device_for_input_source.pfw \ $(LOCAL_PATH)/Settings/volumes.pfw diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk index b341520d71..ea4a58f24c 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -61,19 +61,8 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ - $(LOCAL_PATH)/../Settings/strategy_for_stream.pfw \ - $(LOCAL_PATH)/../Settings/strategy_for_usage.pfw \ $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ - $(LOCAL_PATH)/../Settings/volumes.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_media.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_phone.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_sonification.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_sonification_respectful.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_dtmf.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_enforced_audible.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_transmitted_through_speaker.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_accessibility.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_rerouting.pfw + $(LOCAL_PATH)/../Settings/volumes.pfw LOCAL_ADDITIONAL_DEPENDENCIES := \ $(PFW_EDD_FILES) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml index 9f999b38d1..b55ce2c58d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml @@ -27,22 +27,6 @@ - - - - - - - - - - - - - - - - @@ -68,40 +52,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk index 29296e7566..e9d67e9119 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -59,19 +59,8 @@ LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy PFW_EDD_FILES := \ - $(LOCAL_PATH)/../Settings/strategy_for_stream.pfw \ - $(LOCAL_PATH)/../Settings/strategy_for_usage.pfw \ $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ $(LOCAL_PATH)/../Settings/volumes.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_media.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_phone.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_sonification.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_sonification_respectful.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_dtmf.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_enforced_audible.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_transmitted_through_speaker.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_accessibility.pfw \ - $(LOCAL_PATH)/../Settings/device_for_strategy_rerouting.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_media.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_accessibility.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_dtmf.pfw \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml index 9f999b38d1..b55ce2c58d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml @@ -27,22 +27,6 @@ - - - - - - - - - - - - - - - - @@ -68,40 +52,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw deleted file mode 100644 index 7c87c80fc7..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_accessibility.pfw +++ /dev/null @@ -1,594 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: Accessibility - # - # STRATEGY_ACCESSIBILITY follows STRATEGY_PHONE if in call widely speaking - # STRATEGY_ACCESSIBILITY follows STRATEGY_MEDIA otherwise - # - # Other case are handled programmatically has involving activity of streams. - # - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - hdmi_arc = 0 - spdif = 0 - aux_line = 0 - fm = 0 - speaker_safe = 0 - telephony_tx = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Device - conf: RemoteSubmix - # - # Accessibility follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes RemoteSubmix - AvailableOutputDevicesAddresses Includes 0 - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 1 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dp - # - # Accessibility falls through media strategy if not in call (widely speaking) - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dp - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpHeadphone - # - # Accessibility falls through media strategy if not in call (widely speaking) - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpHeadphones - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpSpeaker - # - # Accessibility falls through media strategy if not in call (widely speaking) - # - ForceUseForMedia IsNot ForceNoBtA2dp - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes BluetoothA2dpSpeaker - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: ForceSpeakerWhenNotInCall - # - # Accessibility follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia Is ForceSpeaker - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 1 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothScoCarkit - # - # accessibility falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes BluetoothScoCarkit - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 1 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothScoHeadset - # - # accessibility falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes BluetoothScoHeadset - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 1 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothSco - # - # accessibility falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes BluetoothSco - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 1 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadphone - ANY - # - # accessibility falls through Phone strategy if in call - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # accessibility follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 1 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Line - ANY - # - # accessibility falls through Phone strategy if in call - # but Line has a lower priority than WiredHeadset in this case. - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Excludes WiredHeadset - # - # accessibility follows Media strategy if not in call - # - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadset - ANY - # - # accessibility falls through Phone strategy if in call - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # accessibility follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 1 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: UsbDevice - ANY - # - # accessibility falls through Phone strategy if in call (widely speaking) - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # accessibility follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Excludes UsbAccessory - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - hdmi = 0 - - conf: UsbAccessory - # - # accessibility falls through Phone strategy if in call (widely speaking) - # but USB accessory not reachable in call - # - # accessibility follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - hdmi = 0 - - conf: DgtlDockHeadset - # - # accessibility falls through Phone strategy if in call (widely speaking) - # but DgtlDockHeadset not reachable in call - # - # accessibility follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: AuxDigital - # - # accessibility falls through Phone strategy if in call (widely speaking) - # but Hdmi not reachable in call - # - # accessibility follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 1 - - conf: AnlgDockHeadset - # - # accessibility falls through Phone strategy if in call (widely speaking) - # but AnlgDockHeadset not reachable in call - # - # accessibility follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes AnlgDockHeadset - ForceUseForDock Is ForceAnalogDock - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Earpiece - # - # accessibility falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes Earpiece - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 1 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Speaker - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 1 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Default - component: /Policy/policy/strategies/accessibility/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_sco = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw deleted file mode 100644 index c830c42190..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_dtmf.pfw +++ /dev/null @@ -1,624 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: Dtmf - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - fm = 0 - speaker_safe = 0 - bluetooth_sco_carkit = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Device2 - conf: RemoteSubmix - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes RemoteSubmix - AvailableOutputDevicesAddresses Includes 0 - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 1 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dp - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dp - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dpHeadphones - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpHeadphones - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dpSpeaker - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpSpeaker - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: ForceSpeakerWhenNotInCall - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia Is ForceSpeaker - ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 1 - - conf: BluetoothScoHeadset - # - # DTMF falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes BluetoothScoHeadset - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 1 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothSco - # - # DTMF falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes BluetoothSco - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 1 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: WiredHeadphone - ANY - # - # DTMF falls through Phone strategy if in call - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # DTMF follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 1 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Line - ANY - # - # DTMF falls through Phone strategy if in call - # but Line has a lower priority than WiredHeadset in this case. - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Excludes WiredHeadset - # - # DTMF follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 1 - speaker = 0 - - conf: WiredHeadset - ANY - # - # DTMF falls through Phone strategy if in call - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # DTMF follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: UsbDevice - ANY - # - # DTMF falls through Phone strategy if in call (widely speaking) - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - # - # DTMF follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Excludes UsbAccessory - ForceUseForCommunication Is ForceSpeaker - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: UsbAccessory - # - # DTMF falls through Phone strategy if in call (widely speaking) - # but USB accessory not reachable in call - # - # DTMF follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: DgtlDockHeadset - # - # DTMF falls through Phone strategy if in call (widely speaking) - # but DgtlDockHeadset not reachable in call - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Hdmi - # - # DTMF falls through Phone strategy if in call (widely speaking) - # but Hdmi not reachable in call - # - # DTMF follows Media strategy if not in call - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: AnlgDockHeadset - # - # DTMF falls through Phone strategy if in call (widely speaking) - # but AnlgDockHeadset not reachable in call - # - # DTMF follows Media strategy if not in call - # Media strategy inverts the priority of USB device vs accessory - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForDock Is ForceAnalogDock - AvailableOutputDevices Includes AnlgDockHeadset - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Earpiece - # - # DTMF falls through Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Includes Earpiece - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 1 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Speaker - ANY - # - # DTMF falls through Phone strategy if in call - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceSpeaker - # - # DTMF follows Media strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 1 - - conf: Default - component: /Policy/policy/strategies/dtmf/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - domain: Arc - # - # DTMF strategy follows media strategy if not in call - # these following domains consists in device(s) that can co-exist with others - # e.g. ARC, SPDIF, AUX_LINE - # - conf: Selected - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes HdmiArc - - /Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi_arc = 1 - - conf: NotSelected - /Policy/policy/strategies/dtmf/selected_output_devices/mask/hdmi_arc = 0 - - domain: Spdif - # - # DTMF strategy follows media strategy if not in call - # these following domains consists in device(s) that can co-exist with others - # e.g. ARC, SPDIF, AUX_LINE - # - conf: Selected - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes Spdif - - /Policy/policy/strategies/dtmf/selected_output_devices/mask/spdif = 1 - - conf: NotSelected - /Policy/policy/strategies/dtmf/selected_output_devices/mask/spdif = 0 - - domain: AuxLine - # - # DTMF strategy follows media strategy if not in call - # these following domains consists in device(s) that can co-exist with others - # e.g. ARC, SPDIF, AUX_LINE - # - conf: Selected - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes AuxLine - - /Policy/policy/strategies/dtmf/selected_output_devices/mask/aux_line = 1 - - conf: NotSelected - /Policy/policy/strategies/dtmf/selected_output_devices/mask/aux_line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw deleted file mode 100644 index c6411381fa..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_enforced_audible.pfw +++ /dev/null @@ -1,359 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: EnforcedAudible - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - # no enforced_audible on remote submix (e.g. WFD) - hdmi_arc = 0 - spdif = 0 - aux_line = 0 - speaker_safe = 0 - ip = 0 - bus = 0 - fm = 0 - stub = 0 - - domain: Speaker - conf: Selected - # - # strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION - # except: - # - when in call where it doesn't default to STRATEGY_PHONE behavior - # - in countries where not enforced in which case it follows STRATEGY_MEDIA - # - AvailableOutputDevices Includes Speaker - ANY - ForceUseForSystem Is ForceSystemEnforced - ALL - ForceUseForMedia Is ForceSpeaker - AvailableOutputDevices Excludes RemoteSubmix - ANY - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Excludes BluetoothA2dp - AvailableOutputDevices Excludes BluetoothA2dpHeadphones - AvailableOutputDevices Excludes BluetoothA2dpSpeaker - # - # Speaker is also the fallback device if any of the device from Device2 domain - # is selected. - # - ALL - AvailableOutputDevices Excludes RemoteSubmix - AvailableOutputDevices Excludes WiredHeadphone - AvailableOutputDevices Excludes Line - AvailableOutputDevices Excludes WiredHeadset - AvailableOutputDevices Excludes UsbAccessory - AvailableOutputDevices Excludes UsbDevice - AvailableOutputDevices Excludes DgtlDockHeadset - AvailableOutputDevices Excludes Hdmi - ANY - AvailableOutputDevices Excludes AnlgDockHeadset - ForceUseForDock IsNot ForceAnalogDock - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - speaker = 1 - - conf: NotSelected - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - speaker = 0 - - domain: Device2 - conf: RemoteSubmix - AvailableOutputDevices Includes RemoteSubmix - AvailableOutputDevicesAddresses Includes 0 - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 1 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: BluetoothA2dp - AvailableOutputDevices Includes BluetoothA2dp - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: BluetoothA2dpHeadphones - AvailableOutputDevices Includes BluetoothA2dpHeadphones - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: BluetoothA2dpSpeaker - AvailableOutputDevices Includes BluetoothA2dpSpeaker - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: WiredHeadphone - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 1 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: Line - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 1 - - conf: WiredHeadset - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: UsbAccessory - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: UsbDevice - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - telephony_tx = 0 - line = 0 - - conf: DgtlDockHeadset - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: Hdmi - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: AnlgDockHeadset - ForceUseForMedia IsNot ForceSpeaker - ForceUseForDock Is ForceAnalogDock - AvailableOutputDevices Includes AnlgDockHeadset - - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: NoDevice - component: /Policy/policy/strategies/enforced_audible/selected_output_devices/mask - remote_submix = 0 - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw deleted file mode 100644 index f8bab3dbbe..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_media.pfw +++ /dev/null @@ -1,339 +0,0 @@ -domainGroup: DeviceForStrategy - - domainGroup: Media - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/media/selected_output_devices/mask - fm = 0 - speaker_safe = 0 - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - telephony_tx = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Device2 - conf: RemoteSubmix - AvailableOutputDevices Includes RemoteSubmix - AvailableOutputDevicesAddresses Includes 0 - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 1 - line = 0 - - conf: BluetoothA2dp - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication IsNot ForceBtSco - AvailableOutputDevices Includes BluetoothA2dp - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 1 - remote_submix = 0 - line = 0 - - conf: BluetoothA2dpHeadphone - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication IsNot ForceBtSco - AvailableOutputDevices Includes BluetoothA2dpHeadphones - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: BluetoothA2dpSpeaker - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication IsNot ForceBtSco - AvailableOutputDevices Includes BluetoothA2dpSpeaker - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: ForceSpeaker - ForceUseForMedia Is ForceSpeaker - AvailableOutputDevices Includes Speaker - # - # If hdmi system audio mode is on, remove speaker out of output list. - # - ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 1 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: WiredHeadphone - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 1 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: Line - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 1 - - conf: WiredHeadset - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: UsbAccessory - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 1 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: UsbDevice - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 1 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: DgtlDockHeadset - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 1 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: AuxDigital - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 1 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: AnlgDockHeadset - AvailableOutputDevices Includes AnlgDockHeadset - ForceUseForDock Is ForceAnalogDock - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 1 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: Speaker - AvailableOutputDevices Includes Speaker - # - # If hdmi system audio mode is on, remove speaker out of output list. - # - ForceUseForHdmiSystemAudio IsNot ForceHdmiSystemEnforced - ForceUseForCommunication IsNot ForceBtSco - - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 1 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - conf: Default - component: /Policy/policy/strategies/media/selected_output_devices/mask - speaker = 0 - hdmi = 0 - dgtl_dock_headset = 0 - angl_dock_headset = 0 - usb_device = 0 - usb_accessory = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp = 0 - remote_submix = 0 - line = 0 - - domain: Arc - # - # these following domains consists in device(s) that can co-exist with others - # e.g. ARC, SPDIF, AUX_LINE - # - conf: Selected - AvailableOutputDevices Includes HdmiArc - - /Policy/policy/strategies/media/selected_output_devices/mask/hdmi_arc = 1 - - conf: NotSelected - /Policy/policy/strategies/media/selected_output_devices/mask/hdmi_arc = 0 - - domain: Spdif - # - # these following domains consists in device(s) that can co-exist with others - # e.g. ARC, SPDIF, AUX_LINE - # - conf: Selected - AvailableOutputDevices Includes Spdif - - /Policy/policy/strategies/media/selected_output_devices/mask/spdif = 1 - - conf: NotSelected - /Policy/policy/strategies/media/selected_output_devices/mask/spdif = 0 - - domain: AuxLine - conf: Selected - AvailableOutputDevices Includes AuxLine - - /Policy/policy/strategies/media/selected_output_devices/mask/aux_line = 1 - - conf: NotSelected - /Policy/policy/strategies/media/selected_output_devices/mask/aux_line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_phone.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_phone.pfw deleted file mode 100644 index d371ad9ec2..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_phone.pfw +++ /dev/null @@ -1,483 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: Phone - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/phone/selected_output_devices/mask - # no sonification on remote submix (e.g. WFD) - remote_submix = 0 - hdmi_arc = 0 - aux_line = 0 - spdif = 0 - fm = 0 - speaker_safe = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Device - conf: ScoCarkit - AvailableOutputDevices Includes BluetoothScoCarkit - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 1 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothScoHeadset - AvailableOutputDevices Includes BluetoothScoHeadset - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 1 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothSco - AvailableOutputDevices Includes BluetoothSco - ForceUseForCommunication Is ForceBtSco - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 1 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dp - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes BluetoothA2dp - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication Is ForceNone - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dpHeadphones - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes BluetoothA2dpHeadphones - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication Is ForceNone - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: BluetoothA2dpSpeaker - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes BluetoothA2dpSpeaker - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - ForceUseForCommunication Is ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: WiredHeadphone - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes WiredHeadphone - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 1 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: WiredHeadset - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes WiredHeadset - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Line - AvailableOutputDevices Includes Line - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 1 - speaker = 0 - - conf: UsbDevice - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes UsbDevice - ANY - ForceUseForCommunication Is ForceBtSco - ForceUseForCommunication Is ForceNone - ALL - ForceUseForCommunication Is ForceSpeaker - # - # In case of Force Speaker, priority between device and accessory are - # inverted compared to Force None or Bt Sco - # - AvailableOutputDevices Excludes UsbAccessory - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: UsbAccessory - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes UsbAccessory - TelephonyMode IsNot InCommunication - TelephonyMode IsNot InCall - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: DgtlDockHeadset - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes DgtlDockHeadset - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Hdmi - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes Hdmi - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: AnlgDockHeadset - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes AnlgDockHeadset - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Earpiece - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes Earpiece - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 1 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 0 - - conf: Speaker - # - # Fallback BT Sco devices in case of FORCE_BT_SCO - # or FORCE_NONE - # - AvailableOutputDevices Includes Speaker - ForceUseForCommunication Is ForceSpeaker - - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 1 - - conf: Default - # - # Fallback on default output device which can be speaker for example - # - component: /Policy/policy/strategies/phone/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - speaker = 1 - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw deleted file mode 100644 index 28a3629748..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_rerouting.pfw +++ /dev/null @@ -1,301 +0,0 @@ -domainGroup: DeviceForStrategy - - domainGroup: Rerouting - # - # Falls through media strategy - # - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - hdmi_arc = 0 - spdif = 0 - aux_line = 0 - fm = 0 - speaker_safe = 0 - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - telephony_tx = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Device2 - conf: RemoteSubmix - AvailableOutputDevices Includes RemoteSubmix - AvailableOutputDevicesAddresses Includes 0 - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 1 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dp - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dp - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpHeadphone - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpHeadphones - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpSpeaker - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpSpeaker - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: ForceSpeaker - ForceUseForMedia Is ForceSpeaker - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 1 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadphone - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 1 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Line - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadset - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 1 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: UsbAccessory - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - hdmi = 0 - - conf: UsbDevice - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - hdmi = 0 - - conf: DgtlDockHeadset - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: AuxDigital - # - # Rerouting is similar to media and sonification (exept here: sonification is not allowed on HDMI) - # - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 1 - - conf: AnlgDockHeadset - AvailableOutputDevices Includes AnlgDockHeadset - ForceUseForDock Is ForceAnalogDock - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Speaker - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 1 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Default - component: /Policy/policy/strategies/rerouting/selected_output_devices/mask - remote_submix = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - speaker = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification.pfw deleted file mode 100644 index 70740d149a..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification.pfw +++ /dev/null @@ -1,490 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: Sonification - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - # no sonification on remote submix (e.g. WFD) - remote_submix = 0 - hdmi_arc = 0 - spdif = 0 - fm = 0 - speaker_safe = 0 - aux_line = 0 - # - # Sonification follows phone strategy if in call but HDMI is not reachable - # - hdmi = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Speaker - - conf: Selected - AvailableOutputDevices Includes Speaker - ANY - # - # Sonification falls through ENFORCED_AUDIBLE if not in call (widely speaking) - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ALL - # - # Sonification follows phone strategy if in call (widely speaking) - # - ForceUseForCommunication Is ForceSpeaker - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Excludes Line - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - speaker = 1 - - conf: NotSelected - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - speaker = 0 - - domain: Device2 - - conf: BluetoothA2dp - # - # Sonification falls through media strategy if not in call (widely speaking) - # - AvailableOutputDevices Includes BluetoothA2dp - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: BluetoothA2dpHeadphones - # - # Sonification falls through media strategy if not in call (widely speaking) - # - AvailableOutputDevices Includes BluetoothA2dpHeadphones - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: BluetoothA2dpSpeaker - # - # Sonification falls through media strategy if not in call (widely speaking) - # - AvailableOutputDevices Includes BluetoothA2dpSpeaker - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: ScoCarkit - # - # Sonification follows phone strategy if in call (widely speaking) - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothScoCarkit - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 1 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: ScoHeadset - # - # Sonification follows phone strategy if in call (widely speaking) - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothScoHeadset - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 1 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: Sco - # - # Sonification follows phone strategy if in call (widely speaking) - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothSco - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 1 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: WiredHeadphone - AvailableOutputDevices Includes WiredHeadphone - ANY - # - # Sonification falls through media strategy if not in call (widely speaking) - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - # - # Sonification follows Phone strategy if in call (widely speaking) - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 1 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: Line - AvailableOutputDevices Includes Line - ANY - # - # Sonification follows Phone strategy if in call (widely speaking) - # but Line has a lower priority than WiredHeadset in this case. - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Excludes WiredHeadset - # - # Sonification falls through media strategy if not in call (widely speaking) - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 1 - - conf: WiredHeadset - AvailableOutputDevices Includes WiredHeadset - ANY - # - # Sonification falls through media strategy if not in call (widely speaking) - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - ALL - # - # Sonification Follows Phone Strategy if in call (widely speaking) - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: UsbDevice - AvailableOutputDevices Includes UsbDevice - ANY - # - # Sonification falls through media strategy if not in call (widely speaking) - # - ALL - AvailableOutputDevices Excludes UsbAccessory - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - ALL - # - # Sonification Follows Phone Strategy if in call (widely speaking) - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - telephony_tx = 0 - line = 0 - - conf: UsbAccessory - AvailableOutputDevices Includes UsbAccessory - # - # Sonification falls through media strategy if not in call (widely speaking) - # - # Sonification Follows Phone Strategy if in call (widely speaking) - # but USB Accessory not reachable in call. - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: DgtlDockHeadset - AvailableOutputDevices Includes DgtlDockHeadset - # - # Sonification falls through media strategy if not in call - # - # Sonification Follows Phone Strategy if in call (widely speaking) - # but DgtlDockHeadset not reachable in call. - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: AnlgDockHeadset - AvailableOutputDevices Includes AnlgDockHeadset - # - # Sonification falls through media strategy if not in call - # - # Sonification Follows Phone Strategy if in call (widely speaking) - # but AnlgDockHeadset not reachable in call. - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - ForceUseForDock Is ForceAnalogDock - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: Earpiece - # - # Sonification Follows Phone Strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Includes Earpiece - - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 1 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - - conf: None - component: /Policy/policy/strategies/sonification/selected_output_devices/mask - earpiece = 0 - wired_headset = 0 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification_respectful.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification_respectful.pfw deleted file mode 100644 index b673c4f99e..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_sonification_respectful.pfw +++ /dev/null @@ -1,553 +0,0 @@ -domainGroup: DeviceForStrategy - - domainGroup: SonificationRespectful - # - # Sonificiation Respectful follows: - # - If in call: Strategy sonification (that follows phone strategy in call also...) - # - If not in call AND a music stream is active remotely: Strategy sonification (that - # follows enforced audible, which follows media) - # - if not in call and no music stream active remotely and music stream active): strategy - # media - # - Otherwise follows sonification by replacing speaker with speaker safe if speaker is - # selected. - # - # Case of stream active handled programmatically - - domain: UnreachableDevices - conf: Calibration - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - remote_submix = 0 - hdmi_arc = 0 - aux_line = 0 - spdif = 0 - fm = 0 - telephony_tx = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Speakers - - conf: SpeakerSafe - AvailableOutputDevices Includes Speaker - AvailableOutputDevices Includes SpeakerSafe - # - # Follows sonification strategy if not in call and replace speaker by speaker safe - # if and only if speaker only selected - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - speaker_safe = 1 - speaker = 0 - - conf: Speaker - AvailableOutputDevices Includes Speaker - ANY - # - # Follows sonification strategy if not in call - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ALL - # - # Follows Phone Strategy if call - # - ForceUseForCommunication Is ForceSpeaker - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - AvailableOutputDevices Excludes Line - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - speaker_safe = 0 - speaker = 1 - - conf: None - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - speaker_safe = 0 - speaker = 0 - - domain: Device2 - conf: BluetoothA2dp - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dp - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 1 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpHeadphones - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpHeadphones - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 1 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothA2dpSpeaker - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceNoBtA2dp - AvailableOutputDevices Includes BluetoothA2dpSpeaker - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 1 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothScoCarkit - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothScoCarkit - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 1 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothScoHeadset - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothScoHeadset - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 1 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: BluetoothSco - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication Is ForceBtSco - AvailableOutputDevices Includes BluetoothSco - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 1 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadphone - ANY - ALL - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - ALL - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes WiredHeadphone - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 1 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Line - ANY - # - # SonificationRespectful Follows Phone strategy if in call - # but Line has a lower priority than WiredHeadset in this case. - # - # - ALL - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Excludes WiredHeadset - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - ALL - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - AvailableOutputDevices Includes WiredHeadphone - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes Line - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 1 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: WiredHeadset - ANY - ALL - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - ALL - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes WiredHeadset - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 1 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: UsbDevice - ANY - ALL - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - ALL - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Excludes UsbAccessory - AvailableOutputDevices Includes UsbDevice - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 1 - hdmi = 0 - - conf: UsbAccessory - # - # SonificationRespectful Follows Phone strategy if in call (widely speaking) - # but UsbAccessory not reachable in call. - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes UsbAccessory - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 1 - usb_device = 0 - hdmi = 0 - - conf: DgtlDockHeadset - # - # SonificationRespectful Follows Phone strategy if in call (widely speaking) - # but DgtlDockHeadset not reachable in call. - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes DgtlDockHeadset - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 1 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: AuxDigital - # - # SonificationRespectful Follows Phone strategy if in call (widely speaking) - # but HDMI not reachable in call. - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - AvailableOutputDevices Includes Hdmi - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 1 - - conf: AnlgDockHeadset - # - # SonificationRespectful Follows Phone strategy if in call (widely speaking) - # but AnlgDockHeadset not reachable in call. - # - # SonificationRespectful Follows Sonification that falls through Media strategy if not in call - # SonificationRespectful follows media if music stream is active - # - TelephonyMode IsNot InCall - TelephonyMode IsNot InCommunication - ForceUseForMedia IsNot ForceSpeaker - ForceUseForDock Is ForceAnalogDock - AvailableOutputDevices Includes AnlgDockHeadset - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 1 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: Earpiece - # - # SonificationRespectful Follows Phone strategy if in call - # - ANY - TelephonyMode Is InCall - TelephonyMode Is InCommunication - ForceUseForCommunication IsNot ForceSpeaker - AvailableOutputDevices Includes Earpiece - - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 1 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 - - conf: None - component: /Policy/policy/strategies/sonification_respectful/selected_output_devices/mask - earpiece = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - bluetooth_a2dp = 0 - wired_headset = 0 - wired_headphone = 0 - line = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - hdmi = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_transmitted_through_speaker.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_transmitted_through_speaker.pfw deleted file mode 100644 index 9f9c211964..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/device_for_strategy_transmitted_through_speaker.pfw +++ /dev/null @@ -1,43 +0,0 @@ -supDomain: DeviceForStrategy - - supDomain: TransmittedThroughSpeaker - domain: UnreacheableDevices - conf: Calibration - component: /Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask - remote_submix = 0 - hdmi_arc = 0 - spdif = 0 - aux_line = 0 - fm = 0 - speaker_safe = 0 - earpiece = 0 - wired_headset = 1 - wired_headphone = 0 - bluetooth_sco = 0 - bluetooth_sco_headset = 0 - bluetooth_sco_carkit = 0 - bluetooth_a2dp = 0 - bluetooth_a2dp_headphones = 0 - bluetooth_a2dp_speaker = 0 - hdmi = 0 - angl_dock_headset = 0 - dgtl_dock_headset = 0 - usb_accessory = 0 - usb_device = 0 - telephony_tx = 0 - line = 0 - ip = 0 - bus = 0 - stub = 0 - - domain: Speaker - conf: Selected - AvailableOutputDevices Includes Speaker - - component: /Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask - speaker = 1 - - conf: NotSelected - component: /Policy/policy/strategies/transmitted_through_speaker/selected_output_devices/mask - speaker = 0 - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_stream.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_stream.pfw deleted file mode 100644 index 3940b9dafa..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Settings/strategy_for_stream.pfw +++ /dev/null @@ -1,20 +0,0 @@ -domain: StrategyForStream - - conf: Calibration - /Policy/policy/streams/voice_call/applicable_strategy/strategy = phone - # - # NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs - # while key clicks are played produces a poor result - # - /Policy/policy/streams/system/applicable_strategy/strategy = media - /Policy/policy/streams/ring/applicable_strategy/strategy = sonification - /Policy/policy/streams/music/applicable_strategy/strategy = media - /Policy/policy/streams/alarm/applicable_strategy/strategy = sonification - /Policy/policy/streams/notification/applicable_strategy/strategy = sonification_respectful - /Policy/policy/streams/bluetooth_sco/applicable_strategy/strategy = phone - /Policy/policy/streams/enforced_audible/applicable_strategy/strategy = enforced_audible - /Policy/policy/streams/dtmf/applicable_strategy/strategy = dtmf - /Policy/policy/streams/tts/applicable_strategy/strategy = transmitted_through_speaker - /Policy/policy/streams/accessibility/applicable_strategy/strategy = accessibility - /Policy/policy/streams/rerouting/applicable_strategy/strategy = rerouting - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml index 31973875f1..56c5ed3684 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml @@ -133,30 +133,6 @@ - - - - - - - - - - - - - - - - - - - - - - - @@ -178,21 +154,12 @@ - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml index 45d1e8a7f2..a4e7537318 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml @@ -11,22 +11,6 @@ - - - - - - - - - - - - - - - - @@ -52,40 +36,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index e7a56e1c18..65dc9afff0 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -8,10 +8,8 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := \ PolicySubsystemBuilder.cpp \ PolicySubsystem.cpp \ - Strategy.cpp \ InputSource.cpp \ Stream.cpp \ - Usage.cpp \ ProductStrategy.cpp LOCAL_CFLAGS += \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp index ba6063a616..bfc1bca582 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp @@ -17,10 +17,8 @@ #include "PolicySubsystem.h" #include "SubsystemObjectFactory.h" #include "PolicyMappingKeys.h" -#include "Strategy.h" #include "Stream.h" #include "InputSource.h" -#include "Usage.h" #include "ProductStrategy.h" #include #include @@ -37,9 +35,7 @@ const char *const PolicySubsystem::mKeyAmend3 = "Amend3"; const char *const PolicySubsystem::mStreamComponentName = "Stream"; -const char *const PolicySubsystem::mStrategyComponentName = "Strategy"; const char *const PolicySubsystem::mInputSourceComponentName = "InputSource"; -const char *const PolicySubsystem::mUsageComponentName = "Usage"; const char *const PolicySubsystem::mProductStrategyComponentName = "ProductStrategy"; PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &logger) @@ -69,16 +65,6 @@ PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &log mStreamComponentName, (1 << MappingKeyName)) ); - addSubsystemObjectFactory( - new TSubsystemObjectFactory( - mStrategyComponentName, - 0) - ); - addSubsystemObjectFactory( - new TSubsystemObjectFactory( - mUsageComponentName, - (1 << MappingKeyName)) - ); addSubsystemObjectFactory( new TSubsystemObjectFactory( mInputSourceComponentName, diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h index 72f3252263..9bf1c23405 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.h @@ -53,8 +53,6 @@ private: static const char *const mKeyAmend3; /**< amend3 key mapping string. */ static const char *const mStreamComponentName; - static const char *const mStrategyComponentName; static const char *const mInputSourceComponentName; - static const char *const mUsageComponentName; static const char *const mProductStrategyComponentName; }; diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp deleted file mode 100644 index 876bcb0595..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Strategy.h" -#include "PolicyMappingKeys.h" -#include "PolicySubsystem.h" -#include - -using std::string; -using android::routing_strategy; - -namespace detail { - -constexpr std::pair routingStrategyMap[] = { - {android::STRATEGY_MEDIA, "STRATEGY_MEDIA"}, - {android::STRATEGY_PHONE, "STRATEGY_PHONE"}, - {android::STRATEGY_SONIFICATION, "STRATEGY_SONIFICATION"}, - {android::STRATEGY_SONIFICATION_RESPECTFUL, "STRATEGY_SONIFICATION_RESPECTFUL"}, - {android::STRATEGY_DTMF, "STRATEGY_DTMF"}, - {android::STRATEGY_ENFORCED_AUDIBLE, "STRATEGY_ENFORCED_AUDIBLE"}, - {android::STRATEGY_TRANSMITTED_THROUGH_SPEAKER, "STRATEGY_TRANSMITTED_THROUGH_SPEAKER"}, - {android::STRATEGY_ACCESSIBILITY, "STRATEGY_ACCESSIBILITY"}, - {android::STRATEGY_REROUTING, "STRATEGY_REROUTING"}, -}; - -bool fromString(const char *literalName, routing_strategy &type) -{ - for (auto& pair : routingStrategyMap) { - if (strcmp(pair.second, literalName) == 0) { - type = pair.first; - return true; - } - } - return false; -} - -} - -Strategy::Strategy(const string &mappingValue, - CInstanceConfigurableElement *instanceConfigurableElement, - const CMappingContext &context, - core::log::Logger& logger) - : CFormattedSubsystemObject(instanceConfigurableElement, - logger, - mappingValue, - MappingKeyAmend1, - (MappingKeyAmendEnd - MappingKeyAmend1 + 1), - context), - mPolicySubsystem(static_cast( - instanceConfigurableElement->getBelongingSubsystem())), - mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) -{ - std::string name(context.getItem(MappingKeyName)); - if (not detail::fromString(name.c_str(), mId)) { - LOG_ALWAYS_FATAL("Invalid Strategy %s, invalid XML structure file", name.c_str()); - } - // Declares the strategy to audio policy engine - mPolicyPluginInterface->addStrategy(instanceConfigurableElement->getName(), mId); -} - -bool Strategy::sendToHW(string & /*error*/) -{ - uint32_t applicableOutputDevice; - blackboardRead(&applicableOutputDevice, sizeof(applicableOutputDevice)); - return mPolicyPluginInterface->setDeviceForStrategy(mId, applicableOutputDevice); -} diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.h deleted file mode 100644 index c02b82cfa2..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Strategy.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "FormattedSubsystemObject.h" -#include "InstanceConfigurableElement.h" -#include "MappingContext.h" -#include -#include - -class PolicySubsystem; - -class Strategy : public CFormattedSubsystemObject -{ -public: - Strategy(const std::string &mappingValue, - CInstanceConfigurableElement *instanceConfigurableElement, - const CMappingContext &context, - core::log::Logger& logger); - -protected: - virtual bool sendToHW(std::string &error); - -private: - const PolicySubsystem *mPolicySubsystem; /**< Route subsytem plugin. */ - - /** - * Interface to communicate with Audio Policy Engine. - */ - android::AudioPolicyPluginInterface *mPolicyPluginInterface; - - android::routing_strategy mId; /**< strategy identifier to link with audio.h.*/ -}; diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp index 46c9e1cab9..5230e0e9dc 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.cpp @@ -20,7 +20,6 @@ #include using std::string; -using android::routing_strategy; Stream::Stream(const string &/*mappingValue*/, CInstanceConfigurableElement *instanceConfigurableElement, @@ -45,11 +44,8 @@ bool Stream::sendToHW(string & /*error*/) Applicable params; blackboardRead(¶ms, sizeof(params)); - mPolicyPluginInterface->setStrategyForStream(mId, - static_cast(params.strategy)); - - mPolicyPluginInterface->setVolumeProfileForStream(mId, - static_cast(params.volumeProfile)); + mPolicyPluginInterface->setVolumeProfileForStream( + mId, static_cast(params.volumeProfile)); return true; diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.h index 4a875dbe85..e0ce2fa251 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.h +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Stream.h @@ -29,7 +29,6 @@ class Stream : public CSubsystemObject private: struct Applicable { - uint32_t strategy; /**< applicable strategy for this stream. */ uint32_t volumeProfile; /**< applicable strategy for this stream. */ } __attribute__((packed)); diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp deleted file mode 100644 index 925d6319f2..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Usage.h" -#include "PolicyMappingKeys.h" -#include "PolicySubsystem.h" -#include - -using std::string; -using android::routing_strategy; - -Usage::Usage(const string &mappingValue, - CInstanceConfigurableElement *instanceConfigurableElement, - const CMappingContext &context, core::log::Logger &logger) - : CFormattedSubsystemObject(instanceConfigurableElement, - logger, - mappingValue, - MappingKeyAmend1, - (MappingKeyAmendEnd - MappingKeyAmend1 + 1), - context), - mPolicySubsystem(static_cast( - instanceConfigurableElement->getBelongingSubsystem())), - mPolicyPluginInterface(mPolicySubsystem->getPolicyPluginInterface()) -{ - std::string name(context.getItem(MappingKeyName)); - - if (not android::UsageTypeConverter::fromString(name, mId)) { - LOG_ALWAYS_FATAL("Invalid Usage name: %s, invalid XML structure file", name.c_str()); - } - // Declares the strategy to audio policy engine - mPolicyPluginInterface->addUsage(name, mId); -} - -bool Usage::sendToHW(string & /*error*/) -{ - uint32_t applicableStrategy; - blackboardRead(&applicableStrategy, sizeof(applicableStrategy)); - return mPolicyPluginInterface->setStrategyForUsage(mId, - static_cast(applicableStrategy)); -} diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.h deleted file mode 100644 index 860204f980..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Usage.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "FormattedSubsystemObject.h" -#include "InstanceConfigurableElement.h" -#include "MappingContext.h" -#include -#include - -class PolicySubsystem; - -class Usage : public CFormattedSubsystemObject -{ -public: - Usage(const std::string &mappingValue, - CInstanceConfigurableElement *instanceConfigurableElement, - const CMappingContext &context, - core::log::Logger& logger); - -protected: - virtual bool sendToHW(std::string &error); - -private: - const PolicySubsystem *mPolicySubsystem; /**< Route subsytem plugin. */ - - /** - * Interface to communicate with Audio Policy Engine. - */ - android::AudioPolicyPluginInterface *mPolicyPluginInterface; - - audio_usage_t mId; /**< usage identifier to link with audio.h. */ -}; diff --git a/services/audiopolicy/engineconfigurable/src/Collection.h b/services/audiopolicy/engineconfigurable/src/Collection.h index 1f8ed8de64..02b41cb9bc 100644 --- a/services/audiopolicy/engineconfigurable/src/Collection.h +++ b/services/audiopolicy/engineconfigurable/src/Collection.h @@ -18,8 +18,6 @@ #include "Element.h" #include "Stream.h" -#include "Strategy.h" -#include "Usage.h" #include "InputSource.h" #include #include @@ -147,15 +145,9 @@ struct Collection::collectionSupported {}; template <> struct Collection::collectionSupported {}; template <> -struct Collection::collectionSupported {}; -template <> struct Collection::collectionSupported {}; -template <> -struct Collection::collectionSupported {}; -typedef Collection StrategyCollection; typedef Collection StreamCollection; -typedef Collection UsageCollection; typedef Collection InputSourceCollection; } // namespace audio_policy diff --git a/services/audiopolicy/engineconfigurable/src/Element.h b/services/audiopolicy/engineconfigurable/src/Element.h index 1b55c8cf12..97950d849b 100644 --- a/services/audiopolicy/engineconfigurable/src/Element.h +++ b/services/audiopolicy/engineconfigurable/src/Element.h @@ -62,7 +62,7 @@ public: /** * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t + * Property may be audio_stream_type_t, audio_usage_t, audio_source_t * or a string. * * @tparam Property for which this policy element has setter / getter. @@ -73,7 +73,7 @@ public: /** * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t + * Property may be audio_stream_type_t, audio_usage_t, audio_source_t * or a string. * * @tparam Property for which this policy element has setter / getter. diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 1b09e3dcc6..f486dcac50 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -25,10 +25,8 @@ #endif #include "Engine.h" -#include "Strategy.h" #include "Stream.h" #include "InputSource.h" -#include "Usage.h" #include #include @@ -41,43 +39,23 @@ using std::map; namespace android { namespace audio_policy { -template <> -StrategyCollection &Engine::getCollection() -{ - return mStrategyCollection; -} template <> StreamCollection &Engine::getCollection() { return mStreamCollection; } template <> -UsageCollection &Engine::getCollection() -{ - return mUsageCollection; -} -template <> InputSourceCollection &Engine::getCollection() { return mInputSourceCollection; } -template <> -const StrategyCollection &Engine::getCollection() const -{ - return mStrategyCollection; -} template <> const StreamCollection &Engine::getCollection() const { return mStreamCollection; } template <> -const UsageCollection &Engine::getCollection() const -{ - return mUsageCollection; -} -template <> const InputSourceCollection &Engine::getCollection() const { return mInputSourceCollection; @@ -93,10 +71,8 @@ Engine::Engine() : mPolicyParameterMgr(new ParameterManagerWrapper()) Engine::~Engine() { - mStrategyCollection.clear(); mStreamCollection.clear(); mInputSourceCollection.clear(); - mUsageCollection.clear(); } status_t Engine::initCheck() @@ -133,42 +109,6 @@ Property Engine::getPropertyForKey(Key key) const return element->template get(); } -routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) -{ - return getPropertyForKey(usage); -} - -audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const -{ - const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); - - /** This is the only case handled programmatically because the PFW is unable to know the - * activity of streams. - * - * -While media is playing on a remote device, use the the sonification behavior. - * Note that we test this usecase before testing if media is playing because - * the isStreamActive() method only informs about the activity of a stream, not - * if it's for local playback. Note also that we use the same delay between both tests - * - * -When media is not playing anymore, fall back on the sonification behavior - */ - if (strategy == STRATEGY_SONIFICATION_RESPECTFUL && - !is_state_in_call(getPhoneState()) && - !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, - SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && - outputs.isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { - return getPropertyForKey(STRATEGY_MEDIA); - } - if (strategy == STRATEGY_ACCESSIBILITY && - (outputs.isStreamActive(AUDIO_STREAM_RING) || outputs.isStreamActive(AUDIO_STREAM_ALARM))) { - // do not route accessibility prompts to a digital output currently configured with a - // compressed format as they would likely not be mixed and dropped. - // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable. - return getPropertyForKey(STRATEGY_SONIFICATION); - } - return getPropertyForKey(strategy); -} - bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &profile) { diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h index 05fecf0ac0..555399447e 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.h +++ b/services/audiopolicy/engineconfigurable/src/Engine.h @@ -43,20 +43,6 @@ public: /// android::status_t initCheck() override; - audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const override - { - return getPropertyForKey(inputSource); - } - - audio_devices_t getDeviceForStrategy(routing_strategy stategy) const override; - - routing_strategy getStrategyForStream(audio_stream_type_t stream) override - { - return getPropertyForKey(stream); - } - - routing_strategy getStrategyForUsage(audio_usage_t usage) override; - status_t setPhoneState(audio_mode_t mode) override; audio_mode_t getPhoneState() const override; @@ -83,38 +69,17 @@ public: /// /// from AudioPolicyPluginInterface /// - status_t addStrategy(const std::string &name, routing_strategy strategy) override - { - return add(name, strategy); - } status_t addStream(const std::string &name, audio_stream_type_t stream) override { return add(name, stream); } - status_t addUsage(const std::string &name, audio_usage_t usage) override - { - return add(name, usage); - } status_t addInputSource(const std::string &name, audio_source_t source) override { return add(name, source); } - bool setDeviceForStrategy(const routing_strategy &strategy, audio_devices_t devices) override - { - return setPropertyForKey(devices, strategy); - } - bool setStrategyForStream(const audio_stream_type_t &stream, - routing_strategy strategy) override - { - return setPropertyForKey(strategy, stream); - } bool setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &volumeProfile) override; - bool setStrategyForUsage(const audio_usage_t &usage, routing_strategy strategy) override - { - return setPropertyForKey(strategy, usage); - } bool setDeviceForInputSource(const audio_source_t &inputSource, audio_devices_t device) override { return setPropertyForKey(device, inputSource); @@ -135,9 +100,7 @@ private: Engine(const Engine &object); Engine &operator=(const Engine &object); - StrategyCollection mStrategyCollection; /**< Strategies indexed by their enum id. */ StreamCollection mStreamCollection; /**< Streams indexed by their enum id. */ - UsageCollection mUsageCollection; /**< Usages indexed by their enum id. */ InputSourceCollection mInputSourceCollection; /**< Input sources indexed by their enum id. */ template diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.h b/services/audiopolicy/engineconfigurable/src/InputSource.h index 64b390e668..e1865cc8dd 100644 --- a/services/audiopolicy/engineconfigurable/src/InputSource.h +++ b/services/audiopolicy/engineconfigurable/src/InputSource.h @@ -59,7 +59,7 @@ public: /** * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t + * Property may be audio_stream_type_t, audio_usage_t, audio_source_t * or a string. */ template diff --git a/services/audiopolicy/engineconfigurable/src/Strategy.cpp b/services/audiopolicy/engineconfigurable/src/Strategy.cpp deleted file mode 100644 index 310b35ed64..0000000000 --- a/services/audiopolicy/engineconfigurable/src/Strategy.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::AudioPolicyEngine/Strategy" - -#include "Strategy.h" - -using std::string; - -namespace android { -namespace audio_policy { - -status_t Element::setIdentifier(routing_strategy identifier) -{ - if (identifier >= NUM_STRATEGIES) { - return BAD_VALUE; - } - mIdentifier = identifier; - ALOGD("%s: Strategy %s identifier 0x%X", __FUNCTION__, getName().c_str(), identifier); - return NO_ERROR; -} - -/** - * Set the device associated to this strategy. - * It checks if the output device is valid. - * - * @param[in] devices selected for the given strategy. - * - * @return NO_ERROR if the device is either valid or none, error code otherwise. - */ -template <> -status_t Element::set(audio_devices_t devices) -{ - if (!audio_is_output_devices(devices) || devices == AUDIO_DEVICE_NONE) { - ALOGE("%s: trying to set an invalid device 0x%X for strategy %s", - __FUNCTION__, devices, getName().c_str()); - return BAD_VALUE; - } - ALOGD("%s: 0x%X for strategy %s", __FUNCTION__, devices, getName().c_str()); - mApplicableDevices = devices; - return NO_ERROR; -} - -template <> -audio_devices_t Element::get() const -{ - ALOGV("%s: 0x%X for strategy %s", __FUNCTION__, mApplicableDevices, getName().c_str()); - return mApplicableDevices; -} - -} // namespace audio_policy -} // namespace android - diff --git a/services/audiopolicy/engineconfigurable/src/Strategy.h b/services/audiopolicy/engineconfigurable/src/Strategy.h deleted file mode 100644 index f2487fda67..0000000000 --- a/services/audiopolicy/engineconfigurable/src/Strategy.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "Element.h" -#include - -namespace android { -namespace audio_policy { - -/** - * @tparam audio_devices_t: Applicable output device(s) for this strategy. - */ -template <> -class Element -{ -public: - Element(const std::string &name) - : mName(name), - mApplicableDevices(AUDIO_DEVICE_NONE) - {} - ~Element() {} - - /** - * Returns identifier of this policy element - * - * @returns string representing the name of this policy element - */ - const std::string &getName() const { return mName; } - - /** - * Set the unique identifier for this policy element. - * - * @tparam Key type of the unique identifier. - * @param[in] identifier to be set. - * - * @return NO_ERROR if the identifier is valid and set correctly, error code otherwise. - */ - status_t setIdentifier(routing_strategy identifier); - - /** - * @return the unique identifier of this policy element. - */ - routing_strategy getIdentifier() const { return mIdentifier; } - - /** - * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t - * or a string. - */ - template - Property get() const; - - template - status_t set(Property property); - -private: - /* Copy facilities are put private to disable copy. */ - Element(const Element &object); - Element &operator=(const Element &object); - - std::string mName; /**< Unique literal Identifier of a policy base element*/ - routing_strategy mIdentifier; /**< Unique numerical Identifier of a policy base element*/ - - audio_devices_t mApplicableDevices; /**< Applicable output device(s) for this strategy. */ -}; - -typedef Element Strategy; - -} // namespace audio_policy -} // namespace android - - diff --git a/services/audiopolicy/engineconfigurable/src/Stream.cpp b/services/audiopolicy/engineconfigurable/src/Stream.cpp index 73fb94d4dc..297eb02375 100644 --- a/services/audiopolicy/engineconfigurable/src/Stream.cpp +++ b/services/audiopolicy/engineconfigurable/src/Stream.cpp @@ -34,32 +34,6 @@ status_t Element::setIdentifier(audio_stream_type_t identif return NO_ERROR; } -/** -* Set the strategy to follow for this stream. -* It checks if the strategy is valid. -* -* @param[in] strategy to be followed. -* -* @return NO_ERROR if the strategy is set correctly, error code otherwise. -*/ -template <> -status_t Element::set(routing_strategy strategy) -{ - if (strategy >= NUM_STRATEGIES) { - return BAD_VALUE; - } - mApplicableStrategy = strategy; - ALOGD("%s: 0x%X for Stream %s", __FUNCTION__, strategy, getName().c_str()); - return NO_ERROR; -} - -template <> -routing_strategy Element::get() const -{ - ALOGV("%s: 0x%X for Stream %s", __FUNCTION__, mApplicableStrategy, getName().c_str()); - return mApplicableStrategy; -} - template <> status_t Element::set(audio_stream_type_t volumeProfile) { diff --git a/services/audiopolicy/engineconfigurable/src/Stream.h b/services/audiopolicy/engineconfigurable/src/Stream.h index 2bf70b3973..a4fdd39b31 100644 --- a/services/audiopolicy/engineconfigurable/src/Stream.h +++ b/services/audiopolicy/engineconfigurable/src/Stream.h @@ -18,22 +18,20 @@ #include "Element.h" #include "EngineDefinition.h" -#include #include namespace android { namespace audio_policy { /** - * @tparam routing_strategy: Applicable strategy for this stream. + * @tparam product_strategy_t: Applicable strategy for this stream. */ template <> class Element { public: Element(const std::string &name) - : mName(name), - mApplicableStrategy(STRATEGY_MEDIA) + : mName(name) {} ~Element() {} @@ -61,7 +59,7 @@ public: /** * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t + * Property may be audio_stream_type_t, audio_usage_t, audio_source_t * or a string. */ template @@ -78,8 +76,6 @@ private: std::string mName; /**< Unique literal Identifier of a policy base element*/ audio_stream_type_t mIdentifier; /**< Unique numerical Identifier of a policy base element*/ - routing_strategy mApplicableStrategy; /**< Applicable strategy for this stream. */ - audio_stream_type_t mVolumeProfile; /**< Volume Profile followed by this stream. */ }; diff --git a/services/audiopolicy/engineconfigurable/src/Usage.cpp b/services/audiopolicy/engineconfigurable/src/Usage.cpp deleted file mode 100644 index 8c0dfba247..0000000000 --- a/services/audiopolicy/engineconfigurable/src/Usage.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "APM::AudioPolicyEngine/Usage" - -#include "Usage.h" - -namespace android { -namespace audio_policy { - -status_t Element::setIdentifier(audio_usage_t identifier) -{ - if (identifier > AUDIO_USAGE_MAX) { - return BAD_VALUE; - } - mIdentifier = identifier; - ALOGD("%s: Usage %s has identifier 0x%X", __FUNCTION__, getName().c_str(), identifier); - return NO_ERROR; -} - -template <> -status_t Element::set(routing_strategy strategy) -{ - if (strategy >= NUM_STRATEGIES) { - return BAD_VALUE; - } - ALOGD("%s: %d for Usage %s", __FUNCTION__, strategy, getName().c_str()); - mApplicableStrategy = strategy; - return NO_ERROR; -} - -template <> -routing_strategy Element::get() const -{ - ALOGD("%s: %d for Usage %s", __FUNCTION__, mApplicableStrategy, getName().c_str()); - return mApplicableStrategy; -} - -} // namespace audio_policy -} // namespace android - - diff --git a/services/audiopolicy/engineconfigurable/src/Usage.h b/services/audiopolicy/engineconfigurable/src/Usage.h deleted file mode 100644 index 72a452f348..0000000000 --- a/services/audiopolicy/engineconfigurable/src/Usage.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "Element.h" -#include - -namespace android { -namespace audio_policy { - -/** - * @tparam routing_strategy: Applicable strategy for this usage. - */ -template <> -class Element -{ -public: - Element(const std::string &name) - : mName(name), - mApplicableStrategy(STRATEGY_MEDIA) - {} - ~Element() {} - - /** - * Returns identifier of this policy element - * - * @returns string representing the name of this policy element - */ - const std::string &getName() const { return mName; } - - /** - * Set the unique identifier for this policy element. - * - * @tparam Key type of the unique identifier. - * @param[in] identifier to be set. - * - * @return NO_ERROR if the identifier is valid and set correctly, error code otherwise. - */ - status_t setIdentifier(audio_usage_t identifier); - - /** - * @return the unique identifier of this policy element. - */ - audio_usage_t getIdentifier() const { return mIdentifier; } - - /** - * A Policy element may implement getter/setter function for a given property. - * Property may be routing_strategy, audio_stream_type_t, audio_usage_t, audio_source_t - * or a string. - */ - template - Property get() const; - - template - status_t set(Property property); - -private: - /* Copy facilities are put private to disable copy. */ - Element(const Element &object); - Element &operator=(const Element &object); - - std::string mName; /**< Unique literal Identifier of a policy base element*/ - audio_usage_t mIdentifier; /**< Unique numerical Identifier of a policy base element*/ - routing_strategy mApplicableStrategy; /**< Applicable strategy for this usage. */ -}; - -typedef Element Usage; - -} // namespace audio_policy -} // namespace android - - diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index a449df6d6c..93af8a6725 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -165,88 +165,6 @@ status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced return EngineBase::setForceUse(usage, config); } -routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) -{ - // stream to strategy mapping - switch (stream) { - case AUDIO_STREAM_VOICE_CALL: - case AUDIO_STREAM_BLUETOOTH_SCO: - return android::STRATEGY_PHONE; - case AUDIO_STREAM_RING: - case AUDIO_STREAM_ALARM: - return android::STRATEGY_SONIFICATION; - case AUDIO_STREAM_NOTIFICATION: - return android::STRATEGY_SONIFICATION_RESPECTFUL; - case AUDIO_STREAM_DTMF: - return android::STRATEGY_DTMF; - default: - ALOGE("unknown stream type %d", stream); - FALLTHROUGH_INTENDED; - case AUDIO_STREAM_SYSTEM: - // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs - // while key clicks are played produces a poor result - case AUDIO_STREAM_MUSIC: - return android::STRATEGY_MEDIA; - case AUDIO_STREAM_ENFORCED_AUDIBLE: - return android::STRATEGY_ENFORCED_AUDIBLE; - case AUDIO_STREAM_TTS: - return android::STRATEGY_TRANSMITTED_THROUGH_SPEAKER; - case AUDIO_STREAM_ACCESSIBILITY: - return android::STRATEGY_ACCESSIBILITY; - case AUDIO_STREAM_REROUTING: - return android::STRATEGY_REROUTING; - } -} - -routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) -{ - // usage to strategy mapping - switch (usage) { - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return android::STRATEGY_ACCESSIBILITY; - - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANT: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return android::STRATEGY_MEDIA; - - case AUDIO_USAGE_VOICE_COMMUNICATION: - return android::STRATEGY_PHONE; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return android::STRATEGY_DTMF; - - case AUDIO_USAGE_ALARM: - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return android::STRATEGY_SONIFICATION; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - return android::STRATEGY_SONIFICATION_RESPECTFUL; - - case AUDIO_USAGE_UNKNOWN: - default: - return android::STRATEGY_MEDIA; - } -} - -audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const -{ - DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); - DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices(); - - const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); - - return getDeviceForStrategyInt(static_cast(strategy), - availableOutputDevices, - availableInputDevices, outputs, (uint32_t)AUDIO_DEVICE_NONE); -} - audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, DeviceVector availableOutputDevices, DeviceVector availableInputDevices, diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index 5672c07c20..15fc358687 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -55,14 +55,6 @@ private: /// /// from EngineBase, so from AudioPolicyManagerInterface /// - audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const override; - - audio_devices_t getDeviceForStrategy(routing_strategy strategy) const override; - - routing_strategy getStrategyForStream(audio_stream_type_t stream) override; - - routing_strategy getStrategyForUsage(audio_usage_t usage) override; - status_t setPhoneState(audio_mode_t mode) override; status_t setForceUse(audio_policy_force_use_t usage, @@ -95,6 +87,8 @@ private: DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const; + audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const; + DeviceStrategyMap mDevicesForStrategies; std::map mLegacyStrategyMap; -- GitLab From 51f0e98712a7378073ec97ab59ea406a1fbaf169 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 1 Feb 2019 19:19:11 -0800 Subject: [PATCH 0880/1530] AudioFlinger: Fix Tee track filename Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I5cd1ecd9f85bb2278e6d1f9434aa453833afa173 Signed-off-by: Kevin Rocard --- services/audioflinger/Tracks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 22d34b26d4..e4af6569c8 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -449,7 +449,7 @@ AudioFlinger::PlaybackThread::Track::Track( || thread->type() == ThreadBase::DUPLICATING; #ifdef TEE_SINK mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) - + "_" + std::to_string(mId)); + + "_" + std::to_string(mId) + "_T"); #endif if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) { -- GitLab From eb8c7bf64aa39ca688631802a18b45bd8a2631d4 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 1 Feb 2019 13:06:00 -0800 Subject: [PATCH 0881/1530] Remove support for loading extractors from apk Bug: 123250010 Test: boot, play video Change-Id: I19c66d76f98a593ac9082054a2faccedcd73a55d --- media/libmedia/Android.bp | 8 --- .../android/media/IMediaUpdateService.aidl | 25 ------- .../libstagefright/MediaExtractorFactory.cpp | 67 +------------------ .../media/stagefright/MediaExtractorFactory.h | 5 +- services/mediaextractor/Android.mk | 3 +- .../MediaExtractorUpdateService.cpp | 35 ---------- .../MediaExtractorUpdateService.h | 40 ----------- .../mediaextractor/main_extractorservice.cpp | 6 -- 8 files changed, 5 insertions(+), 184 deletions(-) delete mode 100644 media/libmedia/aidl/android/media/IMediaUpdateService.aidl delete mode 100644 services/mediaextractor/MediaExtractorUpdateService.cpp delete mode 100644 services/mediaextractor/MediaExtractorUpdateService.h diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 68dae56d4e..9a1ac53ab2 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -144,18 +144,10 @@ cc_library_static { }, } -filegroup { - name: "mediaupdateservice_aidl", - srcs: [ - "aidl/android/media/IMediaUpdateService.aidl", - ], -} - cc_library { name: "libmedia", srcs: [ - ":mediaupdateservice_aidl", "IDataSource.cpp", "BufferingSettings.cpp", "mediaplayer.cpp", diff --git a/media/libmedia/aidl/android/media/IMediaUpdateService.aidl b/media/libmedia/aidl/android/media/IMediaUpdateService.aidl deleted file mode 100644 index 477796995a..0000000000 --- a/media/libmedia/aidl/android/media/IMediaUpdateService.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -/** - * Service to reload media component plugins when update package is installed/uninstalled. - * @hide - */ -interface IMediaUpdateService { - void loadPlugins(@utf8InCpp String apkPath); -} diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 19b174f65b..40b542115c 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -89,7 +89,7 @@ sp MediaExtractorFactory::CreateFromService( ALOGV("MediaExtractorFactory::CreateFromService %s", mime); - UpdateExtractors(nullptr); + UpdateExtractors(); // initialize source decryption if needed source->DrmInitialization(nullptr /* mime */); @@ -122,13 +122,6 @@ sp MediaExtractorFactory::CreateFromService( return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin); } -//static -void MediaExtractorFactory::LoadPlugins(const ::std::string& apkPath) { - // TODO: Verify apk path with package manager in extractor process. - ALOGV("Load plugins from: %s", apkPath.c_str()); - UpdateExtractors(apkPath.empty() ? nullptr : apkPath.c_str()); -} - struct ExtractorPlugin : public RefBase { ExtractorDef def; void *libHandle; @@ -257,54 +250,6 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, pluginList.push_back(plugin); } -//static -void MediaExtractorFactory::RegisterExtractorsInApk( - const char *apkPath, std::list> &pluginList) { - ALOGV("search for plugins at %s", apkPath); - ZipArchiveHandle zipHandle; - int32_t ret = OpenArchive(apkPath, &zipHandle); - if (ret == 0) { - char abi[PROPERTY_VALUE_MAX]; - property_get("ro.product.cpu.abi", abi, "arm64-v8a"); - String8 prefix8 = String8::format("lib/%s/", abi); - ZipString prefix(prefix8.c_str()); - ZipString suffix("extractor.so"); - void* cookie; - ret = StartIteration(zipHandle, &cookie, &prefix, &suffix); - if (ret == 0) { - ZipEntry entry; - ZipString name; - while (Next(cookie, &entry, &name) == 0) { - String8 libPath = String8(apkPath) + "!/" + - String8(reinterpret_cast(name.name), name.name_length); - // TODO: Open with a linker namespace so that it can be linked with sub-libraries - // within the apk instead of system libraries already loaded. - void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL); - if (libHandle) { - GetExtractorDef getDef = - (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); - if (getDef) { - ALOGV("registering sniffer for %s", libPath.string()); - RegisterExtractor( - new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); - } else { - ALOGW("%s does not contain sniffer", libPath.string()); - dlclose(libHandle); - } - } else { - ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); - } - } - EndIteration(cookie); - } else { - ALOGW("couldn't find plugins from %s, %d", apkPath, ret); - } - CloseArchive(zipHandle); - } else { - ALOGW("couldn't open(%s) %d", apkPath, ret); - } -} - //static void MediaExtractorFactory::RegisterExtractorsInSystem( const char *libDirPath, std::list> &pluginList) { @@ -412,11 +357,9 @@ static bool compareFunc(const sp& first, const sp gSupportedExtensions; // static -void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { +void MediaExtractorFactory::UpdateExtractors() { Mutex::Autolock autoLock(gPluginMutex); - if (newUpdateApkPath != nullptr) { - gPluginsRegistered = false; - } + if (gPluginsRegistered) { return; } @@ -437,10 +380,6 @@ void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) { #endif "/extractors", *newList); - if (newUpdateApkPath != nullptr) { - RegisterExtractorsInApk(newUpdateApkPath, *newList); - } - newList->sort(compareFunc); gPlugins = newList; diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index ba6631c0e0..4358aac9a7 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -34,7 +34,6 @@ public: const sp &source, const char *mime = NULL); static sp CreateFromService( const sp &source, const char *mime = NULL); - static void LoadPlugins(const ::std::string& apkPath); static status_t dump(int fd, const Vector& args); static std::unordered_set getSupportedTypes(); static void SetLinkedLibraries(const std::string& linkedLibraries); @@ -46,8 +45,6 @@ private: static bool gIgnoreVersion; static std::string gLinkedLibraries; - static void RegisterExtractorsInApk( - const char *apkPath, std::list> &pluginList); static void RegisterExtractorsInSystem( const char *libDirPath, std::list> &pluginList); static void RegisterExtractorsInApex( @@ -59,7 +56,7 @@ private: float *confidence, void **meta, FreeMetaFunc *freeMeta, sp &plugin, uint32_t *creatorVersion); - static void UpdateExtractors(const char *newUpdateApkPath); + static void UpdateExtractors(); }; } // namespace android diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index dd6488120c..7654982f49 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -4,8 +4,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS := -Wall -Werror LOCAL_SRC_FILES := \ - MediaExtractorService.cpp \ - MediaExtractorUpdateService.cpp \ + MediaExtractorService.cpp LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog LOCAL_MODULE:= libmediaextractorservice diff --git a/services/mediaextractor/MediaExtractorUpdateService.cpp b/services/mediaextractor/MediaExtractorUpdateService.cpp deleted file mode 100644 index 473a698d2b..0000000000 --- a/services/mediaextractor/MediaExtractorUpdateService.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MediaExtractorUpdateService" -#define LOG_NDEBUG 0 -#include - -#include - -#include "MediaExtractorUpdateService.h" - -namespace android { -namespace media { - -binder::Status MediaExtractorUpdateService::loadPlugins(const ::std::string& apkPath) { - ALOGV("loadPlugins %s", apkPath.c_str()); - MediaExtractorFactory::LoadPlugins(apkPath); - return binder::Status::ok(); -} - -} // namespace media -} // namespace android diff --git a/services/mediaextractor/MediaExtractorUpdateService.h b/services/mediaextractor/MediaExtractorUpdateService.h deleted file mode 100644 index ea34c9d21a..0000000000 --- a/services/mediaextractor/MediaExtractorUpdateService.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H -#define ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H - -#include -#include - -namespace android { -namespace media { - -class MediaExtractorUpdateService - : public BinderService, public BnMediaUpdateService -{ - friend class BinderService; -public: - MediaExtractorUpdateService() : BnMediaUpdateService() { } - virtual ~MediaExtractorUpdateService() { } - static const char* getServiceName() { return "media.extractor.update"; } - binder::Status loadPlugins(const ::std::string& apkPath); -}; - -} // namespace media -} // namespace android - -#endif // ANDROID_MEDIA_EXTRACTOR_UPDATE_SERVICE_H diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp index 5f427118cd..3c15bfdffc 100644 --- a/services/mediaextractor/main_extractorservice.cpp +++ b/services/mediaextractor/main_extractorservice.cpp @@ -31,7 +31,6 @@ // from LOCAL_C_INCLUDES #include "IcuUtils.h" #include "MediaExtractorService.h" -#include "MediaExtractorUpdateService.h" #include "MediaUtils.h" #include "minijail.h" @@ -72,11 +71,6 @@ int main(int argc __unused, char** argv) sp sm = defaultServiceManager(); MediaExtractorService::instantiate(); - std::string value = base::GetProperty("ro.build.type", "unknown"); - if (value == "userdebug" || value == "eng") { - media::MediaExtractorUpdateService::instantiate(); - } - ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } -- GitLab From 4a224f5902f88c8dda4c9f6930148ee367a103da Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 5 Jun 2018 15:40:19 -0700 Subject: [PATCH 0882/1530] Expose a few keys that are already used by apps bug: 80314065 Change-Id: I63773e96577a273d890438526cc575f3ed225cea --- media/libmedia/NdkWrapper.cpp | 1 + .../include/media/stagefright/MediaCodecConstants.h | 5 +++++ media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + media/ndk/libmediandk.map.txt | 1 + 5 files changed, 9 insertions(+) diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp index 6dbc9b8874..cbd64bbc28 100644 --- a/media/libmedia/NdkWrapper.cpp +++ b/media/libmedia/NdkWrapper.cpp @@ -57,6 +57,7 @@ static const char *AMediaFormatKeyGroupInt32[] = { AMEDIAFORMAT_KEY_COLOR_STANDARD, AMEDIAFORMAT_KEY_COLOR_TRANSFER, AMEDIAFORMAT_KEY_COMPLEXITY, + AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED, AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, AMEDIAFORMAT_KEY_CRYPTO_MODE, diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index c06c2885d4..2dca5c3cd4 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -752,6 +752,7 @@ constexpr char KEY_COLOR_RANGE[] = "color-range"; constexpr char KEY_COLOR_STANDARD[] = "color-standard"; constexpr char KEY_COLOR_TRANSFER[] = "color-transfer"; constexpr char KEY_COMPLEXITY[] = "complexity"; +constexpr char KEY_CREATE_INPUT_SURFACE_SUSPENDED[] = "create-input-buffers-suspended"; constexpr char KEY_DURATION[] = "durationUs"; constexpr char KEY_FEATURE_[] = "feature-"; constexpr char KEY_FLAC_COMPRESSION_LEVEL[] = "flac-compression-level"; @@ -772,8 +773,10 @@ constexpr char KEY_LANGUAGE[] = "language"; constexpr char KEY_LATENCY[] = "latency"; constexpr char KEY_LEVEL[] = "level"; constexpr char KEY_MAX_BIT_RATE[] = "max-bitrate"; +constexpr char KEY_MAX_FPS_TO_ENCODER[] = "max-fps-to-encoder"; constexpr char KEY_MAX_HEIGHT[] = "max-height"; constexpr char KEY_MAX_INPUT_SIZE[] = "max-input-size"; +constexpr char KEY_MAX_PTS_GAP_TO_ENCODER[] = "max-pts-gap-to-encoder"; constexpr char KEY_MAX_WIDTH[] = "max-width"; constexpr char KEY_MIME[] = "mime"; constexpr char KEY_OPERATING_RATE[] = "operating-rate"; @@ -828,8 +831,10 @@ constexpr int32_t INFO_OUTPUT_FORMAT_CHANGED = -2; constexpr int32_t INFO_TRY_AGAIN_LATER = -1; constexpr int32_t VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; constexpr int32_t VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; +constexpr char PARAMETER_KEY_OFFSET_TIME[] = "time-offset-us"; constexpr char PARAMETER_KEY_REQUEST_SYNC_FRAME[] = "request-sync"; constexpr char PARAMETER_KEY_SUSPEND[] = "drop-input-frames"; +constexpr char PARAMETER_KEY_SUSPEND_TIME[] = "drop-start-time-us"; constexpr char PARAMETER_KEY_VIDEO_BITRATE[] = "video-bitrate"; } diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index fcb706d07f..cd8ecb5d1e 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -292,6 +292,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER = "color-transfer"; EXPORT const char* AMEDIAFORMAT_KEY_COMPILATION = "compilation"; EXPORT const char* AMEDIAFORMAT_KEY_COMPLEXITY = "complexity"; EXPORT const char* AMEDIAFORMAT_KEY_COMPOSER = "composer"; +EXPORT const char* AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE = "crypto-default-iv-size"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK = "crypto-encrypted-byte-block"; EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES = "crypto-encrypted-sizes"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 2551228c04..cc1d9efc31 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -188,6 +188,7 @@ extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_COMPILATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_COMPOSER __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index c50084eb7d..171167d62f 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -56,6 +56,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_COMPILATION; # var introduced=29 AMEDIAFORMAT_KEY_COMPLEXITY; # var introduced=28 AMEDIAFORMAT_KEY_COMPOSER; # var introduced=29 + AMEDIAFORMAT_KEY_CREATE_INPUT_SURFACE_SUSPENDED; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK; # var introduced=29 AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES; # var introduced=29 -- GitLab From 26af1a51efb42d3b6a5ed76bd307bba9cd636bfe Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Fri, 25 Jan 2019 06:57:01 -0800 Subject: [PATCH 0883/1530] Camera: Remove thread that keeps HAL service alive for extra delay hwservicemanager now guarantees clients will be held for ~5s before the callback onClients(false) is called and they are allowed to exit. Test: On walleye_svelte, manually observe camera behavior and the lifetime of the lazy camera service process. Change-Id: Iafbdd73f439da3a4f0fecb069e356d30aa2732b5 --- .../common/CameraProviderManager.cpp | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 3059b0737d..7c32e84220 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -50,14 +50,6 @@ namespace { const std::string kLegacyProviderName("legacy/0"); const std::string kExternalProviderName("external/0"); const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false)); - -// The extra amount of time to hold a reference to an ICameraProvider after it is no longer needed. -// Hold the reference for this extra time so that if the camera is unreferenced and then referenced -// again quickly, we do not let the HAL exit and then need to immediately restart it. An example -// when this could happen is switching from a front-facing to a rear-facing camera. If the HAL were -// to exit during the camera switch, the camera could appear janky to the user. -const std::chrono::system_clock::duration kCameraKeepAliveDelay = 3s; - } // anonymous namespace const float CameraProviderManager::kDepthARTolerance = .1f; @@ -399,12 +391,15 @@ void CameraProviderManager::removeRef(DeviceMode usageType, const std::string &c std::lock_guard lock(mProviderInterfaceMapLock); auto search = providerMap->find(cameraId.c_str()); if (search != providerMap->end()) { - auto ptr = search->second; - auto future = std::async(std::launch::async, [ptr] { - std::this_thread::sleep_for(kCameraKeepAliveDelay); - IPCThreadState::self()->flushCommands(); - }); + // Drop the reference to this ICameraProvider. This is safe to do immediately (without an + // added delay) because hwservicemanager guarantees to hold the reference for at least five + // more seconds. We depend on this behavior so that if the provider is unreferenced and + // then referenced again quickly, we do not let the HAL exit and then need to immediately + // restart it. An example when this could happen is switching from a front-facing to a + // rear-facing camera. If the HAL were to exit during the camera switch, the camera could + // appear janky to the user. providerMap->erase(cameraId.c_str()); + IPCThreadState::self()->flushCommands(); } else { ALOGE("%s: Asked to remove reference for camera %s, but no reference to it was found. This " "could mean removeRef was called twice for the same camera ID.", __FUNCTION__, -- GitLab From 8f646d16e90978990bac3b6e263ed2d0fab5f0b3 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Tue, 8 Jan 2019 10:04:52 +0530 Subject: [PATCH 0884/1530] Updating xaac plugin code after DRC memory cleanup Bug:115509210 Test: poc+vendor Change-Id: I13b897402a203c7865f296b7a304db32d8431c45 --- .../codecs/xaacdec/SoftXAAC.cpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp index 8a86a0d096..da8675874e 100644 --- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -1178,6 +1178,8 @@ IA_ERRORCODE SoftXAAC::configMPEGDDrc() { int i_target_loudness; unsigned int i_sbr_mode; int i; + int ui_proc_mem_tabs_size = 0; + pVOID pv_alloc_ptr = NULL; #ifdef ENABLE_MPEG_D_DRC { @@ -1228,6 +1230,29 @@ IA_ERRORCODE SoftXAAC::configMPEGDDrc() { IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &i_sbr_mode); RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); + /* Get memory info tables size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEMTABS_SIZE, 0, + &ui_proc_mem_tabs_size); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); + + pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size); + + if (pv_alloc_ptr == NULL) { + ALOGE("Cannot create requested memory %d", ui_proc_mem_tabs_size); + return IA_FATAL_ERROR; + } + + memset(pv_alloc_ptr, 0, ui_proc_mem_tabs_size); + + mMemoryVec.push(pv_alloc_ptr); + + /* Set pointer for process memory tables */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, + pv_alloc_ptr); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR"); + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); -- GitLab From ad3420b4287751c14f94137c1ab042bea246a8d9 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Wed, 31 Oct 2018 18:20:10 +0530 Subject: [PATCH 0885/1530] C2SoftXaac: Updating xaac plugin code after DRC memory cleanup Bug: 115509210 Test: poc+vendor Change-Id: Ia2ef81f4101485ea7df515d719f9df802874fd4e --- .../codec2/components/xaac/C2SoftXaacDec.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp index 1c0e70b2a2..c0ad9920a8 100644 --- a/media/codec2/components/xaac/C2SoftXaacDec.cpp +++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp @@ -1067,6 +1067,8 @@ int C2SoftXaacDec::configMPEGDDrc() { int i_loud_norm; int i_target_loudness; unsigned int i_sbr_mode; + uint32_t ui_proc_mem_tabs_size = 0; + pVOID pv_alloc_ptr = NULL; /* Sampling Frequency */ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, @@ -1115,6 +1117,24 @@ int C2SoftXaacDec::configMPEGDDrc() { IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &i_sbr_mode); RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); + /* Get memory info tables size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEMTABS_SIZE, 0, + &ui_proc_mem_tabs_size); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); + + pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size); + if (pv_alloc_ptr == NULL) { + ALOGE(" Cannot create requested memory %d", ui_proc_mem_tabs_size); + return IA_FATAL_ERROR; + } + memset(pv_alloc_ptr, 0, ui_proc_mem_tabs_size); + mMemoryVec.push(pv_alloc_ptr); + + /* Set pointer for process memory tables */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, + pv_alloc_ptr); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR"); + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, nullptr); -- GitLab From 21cb47be5a6b0170c3a6cea57d13a9c6fcbf03da Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Fri, 18 Jan 2019 15:08:17 -0800 Subject: [PATCH 0886/1530] Camera: fix cleanupFailedRequests + HAL buffer manager path Test: crash fixed Bug: 120986771 Change-Id: I3f7031b0a2db30dfbdcf0522dec1c0d6dc9ab26b --- .../device3/Camera3Device.cpp | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 99b804394a..82dfc0fba9 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -5689,18 +5689,21 @@ void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) captureRequest->mInputStream->returnInputBuffer(captureRequest->mInputBuffer); } - for (size_t i = 0; i < halRequest->num_output_buffers; i++) { - //Buffers that failed processing could still have - //valid acquire fence. - int acquireFence = (*outputBuffers)[i].acquire_fence; - if (0 <= acquireFence) { - close(acquireFence); - outputBuffers->editItemAt(i).acquire_fence = -1; + // No output buffer can be returned when using HAL buffer manager + if (!mUseHalBufManager) { + for (size_t i = 0; i < halRequest->num_output_buffers; i++) { + //Buffers that failed processing could still have + //valid acquire fence. + int acquireFence = (*outputBuffers)[i].acquire_fence; + if (0 <= acquireFence) { + close(acquireFence); + outputBuffers->editItemAt(i).acquire_fence = -1; + } + outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0, + /*timestampIncreasing*/true, std::vector (), + captureRequest->mResultExtras.frameNumber); } - outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; - captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0, - /*timestampIncreasing*/true, std::vector (), - captureRequest->mResultExtras.frameNumber); } if (sendRequestError) { -- GitLab From 58d4be5a5a5d24f18b7a207148c7caa317fef366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 6 Nov 2018 15:30:12 +0100 Subject: [PATCH 0887/1530] audio: get rid of hardcoded translation stream <--> attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IMPORTANT NOTE: CL depends on another CL in frameworks/base https://partner-android-review.googlesource.com/c/platform/frameworks/base/+/1206275 AudioProductStrategies offers the possibility to dynamically translate attributes to stream types (and vice versa) within audio policy engine. Legacy engine has hard coded rules to maintain the translation service. This patch removes the hardcoded translation within the helper and replaces them by AudioProductStrategy APIs. Test: AudioPolicyTests: AudioProductStrategiesAllStreamsTest It loops on all stream types supported by strategy and ensures device selection matches. Hard coded stuff would prevent right device selection. Test: CTS: AudioTrackTest AudioRecordTest Test: audio smoke test on sailfish, walleye blueline Change-Id: I76589df5555136ed49dbacc7aac9b0b5e828bef2 Signed-off-by: François Gaffie --- include/media/AudioPolicyHelper.h | 1 - media/libaudioclient/AudioTrack.cpp | 10 +- media/libaudioclient/ToneGenerator.cpp | 3 +- .../include/media/AudioPolicyHelper.h | 143 ------------------ .../MediaPlayer2AudioOutput.cpp | 1 - .../MediaPlayerService.cpp | 5 +- .../managerdefault/AudioPolicyManager.cpp | 3 +- .../service/AudioPolicyEffects.cpp | 4 +- .../service/AudioPolicyInterfaceImpl.cpp | 1 - .../mediaanalytics/MediaAnalyticsService.cpp | 1 - 10 files changed, 10 insertions(+), 162 deletions(-) delete mode 120000 include/media/AudioPolicyHelper.h delete mode 100644 media/libaudioclient/include/media/AudioPolicyHelper.h diff --git a/include/media/AudioPolicyHelper.h b/include/media/AudioPolicyHelper.h deleted file mode 120000 index 558657e08d..0000000000 --- a/include/media/AudioPolicyHelper.h +++ /dev/null @@ -1 +0,0 @@ -../../media/libaudioclient/include/media/AudioPolicyHelper.h \ No newline at end of file diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index c2ee2ee34e..79abea07e3 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -487,7 +486,7 @@ status_t AudioTrack::set( __func__, mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags); mStreamType = AUDIO_STREAM_DEFAULT; - audio_attributes_flags_to_audio_output_flags(mAttributes.flags, flags); + audio_flags_to_audio_output_flags(mAttributes.flags, &flags); } // these below should probably come from the audioFlinger too... @@ -1390,7 +1389,7 @@ status_t AudioTrack::attachAuxEffect(int effectId) audio_stream_type_t AudioTrack::streamType() const { if (mStreamType == AUDIO_STREAM_DEFAULT) { - return audio_attributes_to_stream_type(&mAttributes); + return AudioSystem::attributesToStreamType(mAttributes); } return mStreamType; } @@ -1473,7 +1472,7 @@ status_t AudioTrack::createTrack_l() IAudioFlinger::CreateTrackInput input; if (mStreamType != AUDIO_STREAM_DEFAULT) { - stream_type_to_audio_attributes(mStreamType, &input.attr); + input.attr = AudioSystem::streamTypeToAttributes(mStreamType); } else { input.attr = mAttributes; } @@ -2891,7 +2890,8 @@ status_t AudioTrack::dump(int fd, const Vector& args __unused) const mPortId, mStatus, mState, mSessionId, mFlags); result.appendFormat(" stream type(%d), left - right volume(%f, %f)\n", (mStreamType == AUDIO_STREAM_DEFAULT) ? - audio_attributes_to_stream_type(&mAttributes) : mStreamType, + AudioSystem::attributesToStreamType(mAttributes) : + mStreamType, mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]); result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n", mFormat, mChannelMask, mChannelCount); diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp index 5c5dbd68a7..536b00dddc 100644 --- a/media/libaudioclient/ToneGenerator.cpp +++ b/media/libaudioclient/ToneGenerator.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "media/ToneGenerator.h" @@ -1242,7 +1241,7 @@ bool ToneGenerator::initAudioTrack() { if (mStreamType == AUDIO_STREAM_VOICE_CALL) { streamType = AUDIO_STREAM_DTMF; } - stream_type_to_audio_attributes(streamType, &attr); + attr = AudioSystem::streamTypeToAttributes(streamType); const size_t frameCount = mProcessSize; status_t status = mpAudioTrack->set( diff --git a/media/libaudioclient/include/media/AudioPolicyHelper.h b/media/libaudioclient/include/media/AudioPolicyHelper.h deleted file mode 100644 index 46de6b397d..0000000000 --- a/media/libaudioclient/include/media/AudioPolicyHelper.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2014 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 AUDIO_POLICY_HELPER_H_ -#define AUDIO_POLICY_HELPER_H_ - -#include -#include - -static inline -audio_stream_type_t audio_usage_to_stream_type(const audio_usage_t usage) -{ - switch(usage) { - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - case AUDIO_USAGE_ASSISTANT: - return AUDIO_STREAM_MUSIC; - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return AUDIO_STREAM_ACCESSIBILITY; - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return AUDIO_STREAM_SYSTEM; - case AUDIO_USAGE_VOICE_COMMUNICATION: - return AUDIO_STREAM_VOICE_CALL; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return AUDIO_STREAM_DTMF; - - case AUDIO_USAGE_ALARM: - return AUDIO_STREAM_ALARM; - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return AUDIO_STREAM_RING; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - return AUDIO_STREAM_NOTIFICATION; - - case AUDIO_USAGE_UNKNOWN: - default: - return AUDIO_STREAM_MUSIC; - } -} - -static inline -audio_stream_type_t audio_attributes_to_stream_type(const audio_attributes_t *attr) -{ - // flags to stream type mapping - if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { - return AUDIO_STREAM_ENFORCED_AUDIBLE; - } - if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) { - return AUDIO_STREAM_BLUETOOTH_SCO; - } - - // usage to stream type mapping - return audio_usage_to_stream_type(attr->usage); -} - -static inline -void stream_type_to_audio_attributes(audio_stream_type_t streamType, - audio_attributes_t *attr) { - memset(attr, 0, sizeof(audio_attributes_t)); - - switch (streamType) { - case AUDIO_STREAM_DEFAULT: - case AUDIO_STREAM_MUSIC: - attr->content_type = AUDIO_CONTENT_TYPE_MUSIC; - attr->usage = AUDIO_USAGE_MEDIA; - break; - case AUDIO_STREAM_VOICE_CALL: - attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; - attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION; - break; - case AUDIO_STREAM_ENFORCED_AUDIBLE: - attr->flags |= AUDIO_FLAG_AUDIBILITY_ENFORCED; - FALLTHROUGH_INTENDED; // attributes in common with STREAM_SYSTEM - case AUDIO_STREAM_SYSTEM: - attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - attr->usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION; - break; - case AUDIO_STREAM_RING: - attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - attr->usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE; - break; - case AUDIO_STREAM_ALARM: - attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - attr->usage = AUDIO_USAGE_ALARM; - break; - case AUDIO_STREAM_NOTIFICATION: - attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - attr->usage = AUDIO_USAGE_NOTIFICATION; - break; - case AUDIO_STREAM_BLUETOOTH_SCO: - attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; - attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION; - attr->flags |= AUDIO_FLAG_SCO; - break; - case AUDIO_STREAM_DTMF: - attr->content_type = AUDIO_CONTENT_TYPE_SONIFICATION; - attr->usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - break; - case AUDIO_STREAM_TTS: - attr->content_type = AUDIO_CONTENT_TYPE_SPEECH; - attr->usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - break; - default: - ALOGE("invalid stream type %d when converting to attributes", streamType); - } -} - -// Convert flags sent from Java AudioAttributes.getFlags() method to audio_output_flags_t -static inline -void audio_attributes_flags_to_audio_output_flags(const audio_flags_mask_t audioAttributeFlags, - audio_output_flags_t &flags) { - if ((audioAttributeFlags & AUDIO_FLAG_HW_AV_SYNC) != 0) { - flags = static_cast(flags | - AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_DIRECT); - } - if ((audioAttributeFlags & AUDIO_FLAG_LOW_LATENCY) != 0) { - flags = static_cast(flags | AUDIO_OUTPUT_FLAG_FAST); - } - // check deep buffer after flags have been modified above - if (flags == AUDIO_OUTPUT_FLAG_NONE && (audioAttributeFlags & AUDIO_FLAG_DEEP_BUFFER) != 0) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } -} - -#endif //AUDIO_POLICY_HELPER_H_ diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp index 98a3e75064..4de92ad13f 100644 --- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp +++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp @@ -22,7 +22,6 @@ #include // for property_get #include -#include #include namespace { diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 96f79e0654..da95817512 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -1627,7 +1626,7 @@ MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t ui mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); if (mAttributes != NULL) { memcpy(mAttributes, attr, sizeof(audio_attributes_t)); - mStreamType = audio_attributes_to_stream_type(attr); + mStreamType = AudioSystem::attributesToStreamType(*attr); } } else { mAttributes = NULL; @@ -1816,7 +1815,7 @@ void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); } memcpy(mAttributes, attributes, sizeof(audio_attributes_t)); - mStreamType = audio_attributes_to_stream_type(attributes); + mStreamType = AudioSystem::attributesToStreamType(*attributes); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 126a5f7e69..0eee3f2c31 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -2948,7 +2947,7 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI bool AudioPolicyManager::isDirectOutputSupported(const audio_config_base_t& config, const audio_attributes_t& attributes) { audio_output_flags_t output_flags = AUDIO_OUTPUT_FLAG_NONE; - audio_attributes_flags_to_audio_output_flags(attributes.flags, output_flags); + audio_flags_to_audio_output_flags(attributes.flags, &output_flags); sp profile = getProfileForOutput(DeviceVector() /*ignore device */, config.sample_rate, config.format, diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index 919a90d744..4947714841 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -398,8 +397,7 @@ status_t AudioPolicyEffects::addStreamDefaultEffect(const effect_uuid_t *type, ALOGE("addStreamDefaultEffect(): Null uuid or type uuid pointer"); return BAD_VALUE; } - - audio_stream_type_t stream = audio_usage_to_stream_type(usage); + audio_stream_type_t stream = AudioSystem::attributesToStreamType(attributes_initializer(usage)); if (stream < AUDIO_STREAM_MIN || stream >= AUDIO_STREAM_PUBLIC_CNT) { ALOGE("addStreamDefaultEffect(): Unsupported stream type %d", stream); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 63cb18757e..7768ea347c 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -19,7 +19,6 @@ #include "AudioPolicyService.h" #include "TypeConverter.h" -#include #include #include #include diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp index ae832bad15..4f3ac1ba8d 100644 --- a/services/mediaanalytics/MediaAnalyticsService.cpp +++ b/services/mediaanalytics/MediaAnalyticsService.cpp @@ -51,7 +51,6 @@ #include #include -#include #include #include #include -- GitLab From 0435a4dcb66366da53cf4544c941a40ed2b9c2bd Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 1 Feb 2019 18:17:25 -0800 Subject: [PATCH 0888/1530] Audiopolicy: Fix DeviceVector::getDevice logic It was currently ignoring address even if the default format was passed. Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I44a17fa32c4dd32c426d20bf0902bd406682743c Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/src/DeviceDescriptor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 4cb1e17020..a3121d1456 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -215,11 +215,12 @@ sp DeviceVector::getDevice(audio_devices_t type, const String8 sp device; for (size_t i = 0; i < size(); i++) { if (itemAt(i)->type() == type) { - // Assign device if address is empty or matches and - // format is default or matches + // If format is specified, match it and ignore address + // Otherwise if address is specified match it + // Otherwise always match if (((address == "" || itemAt(i)->address() == address) && format == AUDIO_FORMAT_DEFAULT) || - itemAt(i)->supportsFormat(format)) { + (itemAt(i)->supportsFormat(format) && format != AUDIO_FORMAT_DEFAULT)) { device = itemAt(i); if (itemAt(i)->address() == address) { break; -- GitLab From e5cd43c99ea50692e8ccfa2459afab1039208b36 Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Tue, 5 Feb 2019 10:10:15 -0800 Subject: [PATCH 0889/1530] Remove CFI diagnostics. Remove diagnostics mode from CFI across the board. This should reduce performance overhead and also allows the minimal runtime to work when other ubsan sanitizers are enabled. CFI stack dumps should include a CFI related function, so it should be apparent when a crash is CFI-related. Bug: 117417735 Test: make -j Change-Id: Ia16ac02e844a7cd17e647ddb2208c3583a9e852b --- media/codec2/components/base/Android.bp | 9 --------- media/libmediaplayer2/Android.bp | 3 --- media/libstagefright/timedtext/Android.bp | 3 --- 3 files changed, 15 deletions(-) diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp index d02f541555..78a444bdbc 100644 --- a/media/codec2/components/base/Android.bp +++ b/media/codec2/components/base/Android.bp @@ -74,9 +74,6 @@ cc_defaults { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, } @@ -90,9 +87,6 @@ cc_defaults { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, } @@ -128,9 +122,6 @@ cc_library_shared { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index b3f74043ac..b4e769cbf9 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -123,9 +123,6 @@ cc_library { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, } diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp index 97e1ec6b26..6935655187 100644 --- a/media/libstagefright/timedtext/Android.bp +++ b/media/libstagefright/timedtext/Android.bp @@ -44,9 +44,6 @@ cc_library_static { "signed-integer-overflow", ], cfi: true, - diag: { - cfi: true, - }, }, include_dirs: [ -- GitLab From 5479d73c13ca68fc2a386b62b7f6298cca25ff56 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 5 Feb 2019 10:20:41 -0800 Subject: [PATCH 0890/1530] Use header file for namespace functions Test: build, boot Change-Id: I6eda845869f0240892afe0dc1f0b08aab0920f90 --- media/libstagefright/Android.bp | 1 + media/libstagefright/MediaExtractorFactory.cpp | 18 +----------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 26464b80a0..488890da74 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -220,6 +220,7 @@ cc_library { ], header_libs:[ + "libnativeloader-dummy-headers", "libstagefright_xmlparser_headers", "media_ndk_headers", ], diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 40b542115c..a938d514b5 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -38,23 +39,6 @@ #include #include -// Copied from GraphicsEnv.cpp -// TODO(b/37049319) Get this from a header once one exists -extern "C" { - android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); - bool android_link_namespaces(android_namespace_t* from, - android_namespace_t* to, - const char* shared_libs_sonames); - enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - }; -} - namespace android { // static -- GitLab From b9b7a56a58c71bbc069bcb23e1a5c679480b8f7d Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 5 Feb 2019 12:45:13 -0800 Subject: [PATCH 0891/1530] audio policy: fix build remove unused dependency from parameter lib in engine config. Test: make. Change-Id: I95ca407ab213fd7a9a976873c7e711b60d078156 --- services/audiopolicy/engine/config/Android.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk index cb35c37ca4..fe7d961799 100644 --- a/services/audiopolicy/engine/config/Android.mk +++ b/services/audiopolicy/engine/config/Android.mk @@ -19,7 +19,6 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS += -Wall -Werror -Wextra LOCAL_SHARED_LIBRARIES := \ - libparameter \ libmedia_helper \ libandroidicu \ libxml2 \ -- GitLab From b921a79c53e96c9808da0600685745eb97b61540 Mon Sep 17 00:00:00 2001 From: Henry Fang Date: Mon, 4 Feb 2019 17:12:42 -0800 Subject: [PATCH 0892/1530] enable cas@1.1 interface cas@1.1 hal interface and implementation are available. Fix a bug in default plugin for CTS test. Test: Manual bug: 123903559 Change-Id: Icd41736c45bb70e4f9e275bd989eca4f64ba3dac --- drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp index 27bd631afe..bf352243ca 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp @@ -97,13 +97,13 @@ status_t ClearKeyDescramblerFactory::createPlugin( /////////////////////////////////////////////////////////////////////////////// ClearKeyCasPlugin::ClearKeyCasPlugin( void *appData, CasPluginCallback callback) - : mCallback(callback), mAppData(appData) { + : mCallback(callback), mCallbackExt(NULL), mAppData(appData) { ALOGV("CTOR"); } ClearKeyCasPlugin::ClearKeyCasPlugin( void *appData, CasPluginCallbackExt callback) - : mCallbackExt(callback), mAppData(appData) { + : mCallback(NULL), mCallbackExt(callback), mAppData(appData) { ALOGV("CTOR"); } -- GitLab From 93f18799932651e863b359a8eafa469a4379a455 Mon Sep 17 00:00:00 2001 From: Roma Kaul Date: Wed, 5 Dec 2018 21:22:04 +0530 Subject: [PATCH 0893/1530] Adding CSD support for AV1 in MP4 container Test: stagefright -S -N c2.android.av1.decoder /sdcard/av1.mp4 Bug: 123404784 Change-Id: I69f8faa55853dac6d295e39508dbc1cee9865660 --- media/extractors/mp4/MPEG4Extractor.cpp | 38 +++++++++++++++++++ media/libstagefright/Utils.cpp | 12 ++++++ .../include/media/stagefright/MetaDataBase.h | 2 + 3 files changed, 52 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index ac54116833..1c698f14c5 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -2236,7 +2236,29 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { *offset += chunk_size; break; } + case FOURCC("av1C"): + { + auto buffer = heapbuffer(chunk_data_size); + + if (buffer.get() == NULL) { + ALOGE("b/28471206"); + return NO_MEMORY; + } + + if (mDataSource->readAt( + data_offset, buffer.get(), chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_0, buffer.get(), chunk_data_size); + + *offset += chunk_size; + break; + } case FOURCC("d263"): { *offset += chunk_size; @@ -3972,6 +3994,18 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) { itemTable = mItemTable; } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { + void *data; + size_t size; + if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) { + return NULL; + } + + const uint8_t *ptr = (const uint8_t *)data; + + if (size < 5 || ptr[0] != 0x81) { // configurationVersion == 1 + return NULL; + } } if (track->has_elst and !strncasecmp("video/", mime, 6) and track->elst_media_time > 0) { @@ -4005,6 +4039,10 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) { return ERROR_MALFORMED; } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) { + if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) { + return ERROR_MALFORMED; + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 2e7da014a4..9ee53802d0 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1184,6 +1184,16 @@ status_t convertMetaDataToMessage( } parseHevcProfileLevelFromHvcc((const uint8_t *)data, dataSize, msg); + } else if (meta->findData(kKeyAV1C, &type, &data, &size)) { + sp buffer = new (std::nothrow) ABuffer(size); + if (buffer.get() == NULL || buffer->base() == NULL) { + return NO_MEMORY; + } + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); } else if (meta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds((const char *)data, size); if (esds.InitCheck() != (status_t)OK) { @@ -1743,6 +1753,8 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { std::vector hvcc(csd0size + 1024); size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4); meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize); + } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) { + meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) { meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size()); } else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) { diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index 2910bd38ac..437bdb79b7 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -59,6 +59,7 @@ enum { kKeyAACProfile = 'aacp', // int32_t kKeyAVCC = 'avcc', // raw data kKeyHVCC = 'hvcc', // raw data + kKeyAV1C = 'av1c', // raw data kKeyThumbnailHVCC = 'thvc', // raw data kKeyD263 = 'd263', // raw data kKeyVorbisInfo = 'vinf', // raw data @@ -236,6 +237,7 @@ enum { kTypeESDS = 'esds', kTypeAVCC = 'avcc', kTypeHVCC = 'hvcc', + kTypeAV1C = 'av1c', kTypeD263 = 'd263', }; -- GitLab From fa97d9b1736ee5cdbfc9f454811343c7cd876d2e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Feb 2019 14:08:21 -0800 Subject: [PATCH 0894/1530] audioflinger: Fix move semantics in PatchPanel The old code was not implemented correctly. Test: make and run TV with MSD device Change-Id: I64eec7c0c78705fd0497022ff86fa1aef05745b2 --- services/audioflinger/PatchPanel.h | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 2d9bd8e2e9..612855f57c 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -81,13 +81,16 @@ private: class Endpoint { public: Endpoint() = default; - Endpoint(Endpoint&& other) { *this = std::move(other); } - Endpoint& operator=(Endpoint&& other) { + Endpoint(const Endpoint&) = delete; + Endpoint& operator=(const Endpoint&) = delete; + Endpoint(Endpoint&& other) noexcept { swap(other); } + Endpoint& operator=(Endpoint&& other) noexcept { + swap(other); + return *this; + } + ~Endpoint() { ALOGE_IF(mHandle != AUDIO_PATCH_HANDLE_NONE, "A non empty Patch Endpoint leaked, handle %d", mHandle); - *this = other; - other.mHandle = AUDIO_PATCH_HANDLE_NONE; - return *this; } status_t checkTrack(TrackType *trackOrNull) const { @@ -127,10 +130,19 @@ private: } void stopTrack() { if (mTrack) mTrack->stop(); } - private: - Endpoint(const Endpoint&) = default; - Endpoint& operator=(const Endpoint&) = default; + void swap(Endpoint &other) noexcept { + using std::swap; + swap(mThread, other.mThread); + swap(mCloseThread, other.mCloseThread); + swap(mHandle, other.mHandle); + swap(mTrack, other.mTrack); + } + + friend void swap(Endpoint &a, Endpoint &b) noexcept { + a.swap(b); + } + private: sp mThread; bool mCloseThread = true; audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE; -- GitLab From 6a69e1149f8e1eee75d392dbe5314c13a429b8d5 Mon Sep 17 00:00:00 2001 From: Shengbao Xu Date: Thu, 31 Jan 2019 17:45:35 +0800 Subject: [PATCH 0895/1530] Add mp2 container and codec support AOSP doesn't support audio mp2 file, so mp2 audio file with file extension .mp2 cannot be identified as an audio. 1. Add mp2 extension to mp3extractor.cpp and cannot use mp2extractor as demuxer for mp2 file 2. Add MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II to Acodec.cpp when configure omx component params; then audio channel and samplerate can be configured correctly when init omx component Bug: 123730913 Test: play mp2 file Change-Id: I42d7cbc578f801df13f249ff41442f1ebf0e5180 --- media/extractors/mp3/MP3Extractor.cpp | 1 + media/libstagefright/ACodec.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp index 61838f69c6..a838ae67a3 100644 --- a/media/extractors/mp3/MP3Extractor.cpp +++ b/media/extractors/mp3/MP3Extractor.cpp @@ -708,6 +708,7 @@ static CreatorFunc Sniff( } static const char *extensions[] = { + "mp2", "mp3", "mpeg", "mpg", diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index a1a2660fb6..6d2329f730 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2092,7 +2092,8 @@ status_t ACodec::configureCodec( if (usingSwRenderer) { outputFormat->setInt32("using-sw-renderer", 1); } - } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) || + !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II)) { int32_t numChannels, sampleRate; if (!msg->findInt32("channel-count", &numChannels) || !msg->findInt32("sample-rate", &sampleRate)) { -- GitLab From 1da379a140fda8bd1a76ecab5347622641b44528 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Tue, 5 Feb 2019 15:36:31 -0800 Subject: [PATCH 0896/1530] Camera: Cache physical camera id values A pointer to data within CameraMetadata can become invalid after each metadata modification. Cache any values that could potentially point to the raw internal metadata buffer. Bug: 123954417 Test: Camera CTS Change-Id: Ie5ac9600ff454af551c4cf8e0f25ba5a6a6e897d --- camera/ndk/impl/ACameraMetadata.cpp | 3 ++- camera/ndk/impl/ACameraMetadata.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index c6612337ed..50ad7b28c8 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -104,7 +104,8 @@ ACameraMetadata::derivePhysicalCameraIds() { for (size_t i = 0; i < entry.count; ++i) { if (ids[i] == '\0') { if (start != i) { - mStaticPhysicalCameraIds.push_back((const char*)ids+start); + mStaticPhysicalCameraIdValues.push_back(String8((const char *)ids+start)); + mStaticPhysicalCameraIds.push_back(mStaticPhysicalCameraIdValues.back().string()); } start = i+1; } diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h index 7049c4bab5..3d895cb21f 100644 --- a/camera/ndk/impl/ACameraMetadata.h +++ b/camera/ndk/impl/ACameraMetadata.h @@ -117,6 +117,7 @@ struct ACameraMetadata : public RefBase { static std::unordered_set sSystemTags; std::vector mStaticPhysicalCameraIds; + std::vector mStaticPhysicalCameraIdValues; }; #endif // _ACAMERA_METADATA_H -- GitLab From f158ff01fc14328bae6b703daa839f3db4c33ef6 Mon Sep 17 00:00:00 2001 From: Revathi Uddaraju Date: Thu, 4 May 2017 13:07:12 +0800 Subject: [PATCH 0897/1530] libmedia: synchronize access to meta. Assignment of IMediaSource::mMetaData of type sp in IMediaSource::getFormat() is not thread-safe and is found to be causing stability issues. Synchronize access to meta smart pointer to prevent potential race condition. Test: build Bug: 123082419 Change-Id: I8a94d82d69f35307e5fab8174d752a847f47a2f2 --- media/libmedia/IMediaSource.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp index e7da4884ae..4dece966ab 100644 --- a/media/libmedia/IMediaSource.cpp +++ b/media/libmedia/IMediaSource.cpp @@ -107,6 +107,7 @@ public: data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor()); status_t ret = remote()->transact(GETFORMAT, data, &reply); if (ret == NO_ERROR) { + AutoMutex _l(mLock); mMetaData = MetaData::createFromParcel(reply); return mMetaData; } @@ -222,6 +223,8 @@ private: // NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive // XXX: could we use this for caching, or does metadata change on the fly? sp mMetaData; + // ensure synchronize access to mMetaData + Mutex mLock; // Cache all IMemory objects received from MediaExtractor. // We gc IMemory objects that are no longer active (referenced by a MediaBuffer). -- GitLab From 1086b05f7d3cb296d55facd88621f16c2737cf33 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 18 Dec 2018 17:52:55 -0800 Subject: [PATCH 0898/1530] Add MIX_ROUTE_FLAG_LOOP_BACK_RENDER Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I276a10921aac9f5e709c0a44c661cbbdc94e49db Signed-off-by: Kevin Rocard --- media/libaudioclient/include/media/AudioPolicy.h | 9 +++++++++ .../common/managerdefinitions/src/TypeConverter.cpp | 1 + 2 files changed, 10 insertions(+) diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index 786fb9ab34..bf8d62795c 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -49,8 +49,12 @@ namespace android { #define MIX_STATE_IDLE 0 #define MIX_STATE_MIXING 1 +/** Control to which device some audio is rendered */ #define MIX_ROUTE_FLAG_RENDER 0x1 +/** Loop back some audio instead of rendering it */ #define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1) +/** Loop back some audio while it is rendered */ +#define MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK) #define MIX_ROUTE_FLAG_ALL (MIX_ROUTE_FLAG_RENDER | MIX_ROUTE_FLAG_LOOP_BACK) #define MAX_MIXES_PER_POLICY 10 @@ -119,6 +123,11 @@ public: #define RECORD_CONFIG_EVENT_START 1 #define RECORD_CONFIG_EVENT_STOP 0 +static inline bool is_mix_loopback_render(uint32_t routeFlags) { + return (routeFlags & MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER) + == MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER; +} + }; // namespace android #endif // ANDROID_AUDIO_POLICY_H diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp index 6f48eae9c1..7c76d8a35b 100644 --- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp @@ -45,6 +45,7 @@ template <> const RouteFlagTypeConverter::Table RouteFlagTypeConverter::mTable[] = { MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_RENDER), MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK), + MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER), MAKE_STRING_FROM_ENUM(MIX_ROUTE_FLAG_ALL), TERMINATOR }; -- GitLab From 8b5c20c58e20691f3c0cf6c5fd87f2b599d89132 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:15:08 -0800 Subject: [PATCH 0899/1530] codec2: make C2Component::Traits.aliases a vector of C2Strings - also set aliases array size in HAL adaptor Bug: 119631295 Change-Id: I1202a85f5988cfbad0576a49a2522757bb2bb713 --- .../hidl/1.0/utils/include/codec2/hidl/1.0/types.h | 4 +--- media/codec2/hidl/1.0/utils/types.cpp | 11 ++--------- media/codec2/hidl/client/client.cpp | 3 +-- media/codec2/hidl/client/include/codec2/hidl/client.h | 2 -- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index c38e674389..b9f3aa88ea 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -120,11 +120,9 @@ bool objcpy( IComponentStore::ComponentTraits* d, const C2Component::Traits& s); -// ComponentTraits -> C2Component::Traits, std::unique_ptr> -// Note: The output d is only valid as long as aliasesBuffer remains alive. +// ComponentTraits -> C2Component::Traits bool objcpy( C2Component::Traits* d, - std::unique_ptr>* aliasesBuffer, const IComponentStore::ComponentTraits& s); // C2StructDescriptor -> StructDescriptor diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index caed839eaf..02cdc23c50 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -351,7 +351,6 @@ bool objcpy( // ComponentTraits -> C2Component::Traits, std::unique_ptr> bool objcpy( C2Component::Traits* d, - std::unique_ptr>* aliasesBuffer, const IComponentStore::ComponentTraits& s) { d->name = s.name.c_str(); @@ -394,15 +393,9 @@ bool objcpy( d->rank = static_cast(s.rank); d->mediaType = s.mediaType.c_str(); - - // aliasesBuffer must not be resized after this. - *aliasesBuffer = std::make_unique>( - s.aliases.size()); - (*aliasesBuffer)->resize(s.aliases.size()); - std::vector dAliases(s.aliases.size()); + d->aliases.resize(s.aliases.size()); for (size_t i = 0; i < s.aliases.size(); ++i) { - (**aliasesBuffer)[i] = s.aliases[i].c_str(); - d->aliases[i] = (**aliasesBuffer)[i].c_str(); + d->aliases[i] = s.aliases[i]; } return true; } diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 5b52fcd5e8..458f675dc9 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -579,9 +579,8 @@ const std::vector& Codec2Client::listComponents() const { return; } mTraitsList.resize(t.size()); - mAliasesBuffer.resize(t.size()); for (size_t i = 0; i < t.size(); ++i) { - if (!objcpy(&mTraitsList[i], &mAliasesBuffer[i], t[i])) { + if (!objcpy(&mTraitsList[i], t[i])) { LOG(ERROR) << "listComponents -- corrupted output."; return; } diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index f320ef3edc..8f065126a6 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -232,8 +232,6 @@ protected: mutable bool mListed; std::string mServiceName; mutable std::vector mTraitsList; - mutable std::vector>> - mAliasesBuffer; sp<::android::hardware::media::bufferpool::V2_0::IClientManager> mHostPoolManager; -- GitLab From ab34ed6f5484aa7597855c9394149f05a6af7fd1 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 31 Jan 2019 15:28:46 -0800 Subject: [PATCH 0900/1530] CCodec: refactor pipeline logic Bug: 123632127 Test: bug repro steps Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:include-annotation:android.media.cts.MediaHeavyPresubmitTests Change-Id: I289f51709dbd675991cd8949cd343c5bf5c6ef5c --- media/codec2/components/aac/C2SoftAacDec.cpp | 41 ++-- media/codec2/components/avc/C2SoftAvcDec.cpp | 8 + .../codec2/components/hevc/C2SoftHevcDec.cpp | 6 +- .../components/mpeg4_h263/C2SoftMpeg4Dec.cpp | 6 +- .../common/media_c2_hidl_test_common.h | 9 +- media/codec2/hidl/client/client.cpp | 152 +------------ .../hidl/client/include/codec2/hidl/client.h | 34 +-- media/codec2/sfplugin/Android.bp | 1 + media/codec2/sfplugin/CCodec.cpp | 104 ++------- media/codec2/sfplugin/CCodec.h | 14 +- media/codec2/sfplugin/CCodecBufferChannel.cpp | 211 ++++++++---------- media/codec2/sfplugin/CCodecBufferChannel.h | 89 +------- media/codec2/sfplugin/PipelineWatcher.cpp | 145 ++++++++++++ media/codec2/sfplugin/PipelineWatcher.h | 78 +++++++ 14 files changed, 395 insertions(+), 503 deletions(-) create mode 100644 media/codec2/sfplugin/PipelineWatcher.cpp create mode 100644 media/codec2/sfplugin/PipelineWatcher.h diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp index c7c84421ab..04dda8f52c 100644 --- a/media/codec2/components/aac/C2SoftAacDec.cpp +++ b/media/codec2/components/aac/C2SoftAacDec.cpp @@ -52,33 +52,26 @@ namespace android { -class C2SoftAacDec::IntfImpl : public C2InterfaceHelper { +constexpr char COMPONENT_NAME[] = "c2.android.aac.decoder"; + +class C2SoftAacDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); - - addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) - .build()); + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_AAC) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_AAC)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::output(2u)) .build()); addParameter( @@ -231,8 +224,6 @@ private: // TODO Add : C2StreamAacSbrModeTuning }; -constexpr char COMPONENT_NAME[] = "c2.android.aac.decoder"; - C2SoftAacDec::C2SoftAacDec( const char *name, c2_node_id_t id, diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp index 3e62744bd1..86cd3d8fb6 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.cpp +++ b/media/codec2/components/avc/C2SoftAvcDec.cpp @@ -51,6 +51,12 @@ public: noInputLatency(); noTimeStretch(); + // TODO: Proper support for reorder depth. + addParameter( + DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::output(8u)) + .build()); + // TODO: output latency and reordering addParameter( @@ -877,6 +883,8 @@ void C2SoftAvcDec::process( } else if (!hasPicture) { fillEmptyWork(work); } + + work->input.buffers.clear(); } c2_status_t C2SoftAvcDec::drainInternal( diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp index 99892cef85..f0d7d887f6 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.cpp +++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp @@ -51,7 +51,11 @@ public: noInputLatency(); noTimeStretch(); - // TODO: output latency and reordering + // TODO: Proper support for reorder depth. + addParameter( + DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::output(8u)) + .build()); addParameter( DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp index 901f5ed287..0b89cff68c 100644 --- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp +++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp @@ -60,7 +60,11 @@ public: noInputLatency(); noTimeStretch(); - // TODO: output latency and reordering + // TODO: Proper support for reorder depth. + addParameter( + DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY) + .withConstValue(new C2PortActualDelayTuning::output(1u)) + .build()); addParameter( DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h index d1557cbcf5..fca2902277 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h @@ -55,12 +55,10 @@ struct CodecListener : public android::Codec2Client::Listener { : callBack(fn) {} virtual void onWorkDone( const std::weak_ptr& comp, - std::list>& workItems, - size_t numDiscardedInputBuffers) override { + std::list>& workItems) override { /* TODO */ ALOGD("onWorkDone called"); (void)comp; - (void)numDiscardedInputBuffers; if (callBack) callBack(workItems); } @@ -89,9 +87,10 @@ struct CodecListener : public android::Codec2Client::Listener { } virtual void onInputBufferDone( - const std::shared_ptr& buffer) override { + uint64_t frameIndex, size_t arrayIndex) override { /* TODO */ - (void)buffer; + (void)frameIndex; + (void)arrayIndex; } virtual void onFrameRendered( diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 5b52fcd5e8..e02c2ca68b 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -344,17 +344,13 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { return Void(); } // release input buffers potentially held by the component from queue - size_t numDiscardedInputBuffers = 0; std::shared_ptr strongComponent = component.lock(); if (strongComponent) { - numDiscardedInputBuffers = - strongComponent->handleOnWorkDone(workItems); + strongComponent->handleOnWorkDone(workItems); } if (std::shared_ptr listener = base.lock()) { - listener->onWorkDone(component, - workItems, - numDiscardedInputBuffers); + listener->onWorkDone(component, workItems); } else { LOG(DEBUG) << "onWorkDone -- listener died."; } @@ -418,26 +414,15 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { LOG(DEBUG) << "onInputBuffersReleased -- listener died."; return Void(); } - std::shared_ptr strongComponent = - component.lock(); - if (!strongComponent) { - LOG(DEBUG) << "onInputBuffersReleased -- component died."; - return Void(); - } for (const InputBuffer& inputBuffer : inputBuffers) { - std::shared_ptr buffer = - strongComponent->freeInputBuffer( - inputBuffer.frameIndex, - inputBuffer.arrayIndex); LOG(VERBOSE) << "onInputBuffersReleased --" " received death notification of" " input buffer:" " frameIndex = " << inputBuffer.frameIndex << ", bufferIndex = " << inputBuffer.arrayIndex << "."; - if (buffer) { - listener->onInputBufferDone(buffer); - } + listener->onInputBufferDone( + inputBuffer.frameIndex, inputBuffer.arrayIndex); } return Void(); } @@ -918,43 +903,8 @@ c2_status_t Codec2Client::Component::destroyBlockPool( return static_cast(static_cast(transResult)); } -size_t Codec2Client::Component::handleOnWorkDone( +void Codec2Client::Component::handleOnWorkDone( const std::list> &workItems) { - // Input buffers' lifetime management - std::vector inputDone; - for (const std::unique_ptr &work : workItems) { - if (work) { - if (work->worklets.empty() - || !work->worklets.back() - || (work->worklets.back()->output.flags & - C2FrameData::FLAG_INCOMPLETE) == 0) { - // input is complete - inputDone.emplace_back(work->input.ordinal.frameIndex.peeku()); - } - } - } - - size_t numDiscardedInputBuffers = 0; - { - std::lock_guard lock(mInputBuffersMutex); - for (uint64_t inputIndex : inputDone) { - auto it = mInputBuffers.find(inputIndex); - if (it == mInputBuffers.end()) { - LOG(VERBOSE) << "onWorkDone -- returned consumed/unknown " - "input frame: index = " - << inputIndex << "."; - } else { - LOG(VERBOSE) << "onWorkDone -- processed input frame: " - << inputIndex - << " (containing " << it->second.size() - << " buffers)."; - mInputBuffers.erase(it); - mInputBufferCount.erase(inputIndex); - ++numDiscardedInputBuffers; - } - } - } - // Output bufferqueue-based blocks' lifetime management mOutputBufferQueueMutex.lock(); sp igbp = mOutputIgbp; @@ -965,72 +915,10 @@ size_t Codec2Client::Component::handleOnWorkDone( if (igbp) { holdBufferQueueBlocks(workItems, igbp, bqId, generation); } - return numDiscardedInputBuffers; -} - -std::shared_ptr Codec2Client::Component::freeInputBuffer( - uint64_t frameIndex, - size_t bufferIndex) { - std::shared_ptr buffer; - std::lock_guard lock(mInputBuffersMutex); - auto it = mInputBuffers.find(frameIndex); - if (it == mInputBuffers.end()) { - LOG(INFO) << "freeInputBuffer -- Unrecognized input frame index " - << frameIndex << "."; - return nullptr; - } - if (bufferIndex >= it->second.size()) { - LOG(INFO) << "freeInputBuffer -- Input buffer number " << bufferIndex - << " is not valid in input with frame index " << frameIndex - << "."; - return nullptr; - } - buffer = it->second[bufferIndex]; - if (!buffer) { - LOG(INFO) << "freeInputBuffer -- Input buffer number " << bufferIndex - << " in input with frame index " << frameIndex - << " has already been freed."; - return nullptr; - } - it->second[bufferIndex] = nullptr; - if (--mInputBufferCount[frameIndex] == 0) { - mInputBuffers.erase(it); - mInputBufferCount.erase(frameIndex); - } - return buffer; } c2_status_t Codec2Client::Component::queue( std::list>* const items) { - // remember input buffers queued to hold reference to them - { - std::lock_guard lock(mInputBuffersMutex); - for (const std::unique_ptr &work : *items) { - if (!work) { - continue; - } - if (work->input.buffers.size() == 0) { - continue; - } - - uint64_t inputIndex = work->input.ordinal.frameIndex.peeku(); - auto res = mInputBuffers.emplace(inputIndex, work->input.buffers); - if (!res.second) { - // TODO: append? - for now we are replacing - res.first->second = work->input.buffers; - LOG(INFO) << "queue -- duplicate input frame index: " - << inputIndex - << ". Discarding the old input frame..."; - } - mInputBufferCount[inputIndex] = work->input.buffers.size(); - LOG(VERBOSE) << "queue -- queuing input frame: " - << "index = " << inputIndex - << ", number of buffers = " - << work->input.buffers.size() - << "."; - } - } - WorkBundle workBundle; if (!objcpy(&workBundle, *items, &mBufferPoolSender)) { LOG(ERROR) << "queue -- bad input."; @@ -1088,24 +976,6 @@ c2_status_t Codec2Client::Component::flush( } } - // Input buffers' lifetime management - for (uint64_t flushedIndex : flushedIndices) { - std::lock_guard lock(mInputBuffersMutex); - auto it = mInputBuffers.find(flushedIndex); - if (it == mInputBuffers.end()) { - LOG(VERBOSE) << "flush -- returned consumed/unknown input frame: " - "index = " << flushedIndex << "."; - } else { - LOG(VERBOSE) << "flush -- returned unprocessed input frame: " - "index = " << flushedIndex - << ", number of buffers = " - << mInputBufferCount[flushedIndex] - << "."; - mInputBuffers.erase(it); - mInputBufferCount.erase(flushedIndex); - } - } - // Output bufferqueue-based blocks' lifetime management mOutputBufferQueueMutex.lock(); sp igbp = mOutputIgbp; @@ -1160,10 +1030,6 @@ c2_status_t Codec2Client::Component::stop() { if (status != C2_OK) { LOG(DEBUG) << "stop -- call failed: " << status << "."; } - mInputBuffersMutex.lock(); - mInputBuffers.clear(); - mInputBufferCount.clear(); - mInputBuffersMutex.unlock(); return status; } @@ -1178,10 +1044,6 @@ c2_status_t Codec2Client::Component::reset() { if (status != C2_OK) { LOG(DEBUG) << "reset -- call failed: " << status << "."; } - mInputBuffersMutex.lock(); - mInputBuffers.clear(); - mInputBufferCount.clear(); - mInputBuffersMutex.unlock(); return status; } @@ -1196,10 +1058,6 @@ c2_status_t Codec2Client::Component::release() { if (status != C2_OK) { LOG(DEBUG) << "release -- call failed: " << status << "."; } - mInputBuffersMutex.lock(); - mInputBuffers.clear(); - mInputBufferCount.clear(); - mInputBuffersMutex.unlock(); return status; } diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index f320ef3edc..5b3afcaa68 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -252,16 +252,9 @@ protected: struct Codec2Client::Listener { // This is called when the component produces some output. - // - // numDiscardedInputBuffers is the number of input buffers contained in - // workItems that have just become unused. Note that workItems may contain - // more input buffers than numDiscardedInputBuffers because buffers that - // have been previously reported by onInputBufferDone() are not counted - // towards numDiscardedInputBuffers, but may still show up in workItems. virtual void onWorkDone( const std::weak_ptr& comp, - std::list>& workItems, - size_t numDiscardedInputBuffers) = 0; + std::list>& workItems) = 0; // This is called when the component goes into a tripped state. virtual void onTripped( @@ -283,7 +276,7 @@ struct Codec2Client::Listener { // Input buffers that have been returned by onWorkDone() or flush() will not // trigger a call to this function. virtual void onInputBufferDone( - const std::shared_ptr& buffer) = 0; + uint64_t frameIndex, size_t arrayIndex) = 0; // This is called when the component becomes aware of a frame being // rendered. @@ -385,24 +378,6 @@ struct Codec2Client::Component : public Codec2Client::Configurable { protected: sp mBase; - // Mutex for mInputBuffers and mInputBufferCount. - mutable std::mutex mInputBuffersMutex; - - // Map: frameIndex -> vector of bufferIndices - // - // mInputBuffers[frameIndex][bufferIndex] may be null if the buffer in that - // slot has been freed. - mutable std::map>> - mInputBuffers; - - // Map: frameIndex -> number of bufferIndices that have not been freed - // - // mInputBufferCount[frameIndex] keeps track of the number of non-null - // elements in mInputBuffers[frameIndex]. When mInputBufferCount[frameIndex] - // decreases to 0, frameIndex can be removed from both mInputBuffers and - // mInputBufferCount. - mutable std::map mInputBufferCount; - ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender mBufferPoolSender; @@ -419,10 +394,7 @@ protected: friend struct Codec2Client; struct HidlListener; - // Return the number of input buffers that should be discarded. - size_t handleOnWorkDone(const std::list> &workItems); - // Remove an input buffer from mInputBuffers and return it. - std::shared_ptr freeInputBuffer(uint64_t frameIndex, size_t bufferIndex); + void handleOnWorkDone(const std::list> &workItems); }; diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index 2870d391eb..a212651570 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -8,6 +8,7 @@ cc_library_shared { "CCodecConfig.cpp", "Codec2Buffer.cpp", "Codec2InfoBuilder.cpp", + "PipelineWatcher.cpp", "ReflectedParamUpdater.cpp", "SkipCutBuffer.cpp", ], diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 10263dedfb..ed1f85b844 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -448,14 +448,13 @@ struct CCodec::ClientListener : public Codec2Client::Listener { virtual void onWorkDone( const std::weak_ptr& component, - std::list>& workItems, - size_t numDiscardedInputBuffers) override { + std::list>& workItems) override { (void)component; sp codec(mCodec.promote()); if (!codec) { return; } - codec->onWorkDone(workItems, numDiscardedInputBuffers); + codec->onWorkDone(workItems); } virtual void onTripped( @@ -504,10 +503,10 @@ struct CCodec::ClientListener : public Codec2Client::Listener { } virtual void onInputBufferDone( - const std::shared_ptr& buffer) override { + uint64_t frameIndex, size_t arrayIndex) override { sp codec(mCodec.promote()); if (codec) { - codec->onInputBufferDone(buffer); + codec->onInputBufferDone(frameIndex, arrayIndex); } } @@ -531,10 +530,6 @@ public: {RenderedFrameInfo(mediaTimeUs, renderTimeNs)}); } - void onWorkQueued(bool eos) override { - mCodec->onWorkQueued(eos); - } - void onOutputBuffersChanged() override { mCodec->mCallback->onOutputBuffersChanged(); } @@ -546,8 +541,7 @@ private: // CCodec CCodec::CCodec() - : mChannel(new CCodecBufferChannel(std::make_shared(this))), - mQueuedWorkCount(0) { + : mChannel(new CCodecBufferChannel(std::make_shared(this))) { } CCodec::~CCodec() { @@ -1343,7 +1337,6 @@ void CCodec::flush() { } mChannel->flush(flushedWork); - subQueuedWorkCount(flushedWork.size()); { Mutexed::Locked state(mState); @@ -1465,28 +1458,16 @@ void CCodec::signalRequestIDRFrame() { config->setParameters(comp, params, C2_MAY_BLOCK); } -void CCodec::onWorkDone(std::list> &workItems, - size_t numDiscardedInputBuffers) { +void CCodec::onWorkDone(std::list> &workItems) { if (!workItems.empty()) { - { - Mutexed>::Locked numDiscardedInputBuffersQueue( - mNumDiscardedInputBuffersQueue); - numDiscardedInputBuffersQueue->insert( - numDiscardedInputBuffersQueue->end(), - workItems.size() - 1, 0); - numDiscardedInputBuffersQueue->emplace_back( - numDiscardedInputBuffers); - } - { - Mutexed>>::Locked queue(mWorkDoneQueue); - queue->splice(queue->end(), workItems); - } + Mutexed>>::Locked queue(mWorkDoneQueue); + queue->splice(queue->end(), workItems); } (new AMessage(kWhatWorkDone, this))->post(); } -void CCodec::onInputBufferDone(const std::shared_ptr& buffer) { - mChannel->onInputBufferDone(buffer); +void CCodec::onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) { + mChannel->onInputBufferDone(frameIndex, arrayIndex); } void CCodec::onMessageReceived(const sp &msg) { @@ -1512,7 +1493,6 @@ void CCodec::onMessageReceived(const sp &msg) { case kWhatStart: { // C2Component::start() should return within 500ms. setDeadline(now, 550ms, "start"); - mQueuedWorkCount = 0; start(); break; } @@ -1520,10 +1500,6 @@ void CCodec::onMessageReceived(const sp &msg) { // C2Component::stop() should return within 500ms. setDeadline(now, 550ms, "stop"); stop(); - - mQueuedWorkCount = 0; - Mutexed::Locked deadline(mQueueDeadline); - deadline->set(TimePoint::max(), "none"); break; } case kWhatFlush: { @@ -1549,7 +1525,6 @@ void CCodec::onMessageReceived(const sp &msg) { } case kWhatWorkDone: { std::unique_ptr work; - size_t numDiscardedInputBuffers; bool shouldPost = false; { Mutexed>>::Locked queue(mWorkDoneQueue); @@ -1560,24 +1535,10 @@ void CCodec::onMessageReceived(const sp &msg) { queue->pop_front(); shouldPost = !queue->empty(); } - { - Mutexed>::Locked numDiscardedInputBuffersQueue( - mNumDiscardedInputBuffersQueue); - if (numDiscardedInputBuffersQueue->empty()) { - numDiscardedInputBuffers = 0; - } else { - numDiscardedInputBuffers = numDiscardedInputBuffersQueue->front(); - numDiscardedInputBuffersQueue->pop_front(); - } - } if (shouldPost) { (new AMessage(kWhatWorkDone, this))->post(); } - if (work->worklets.empty() - || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE)) { - subQueuedWorkCount(1); - } // handle configuration changes in work done Mutexed::Locked config(mConfig); bool changed = false; @@ -1641,8 +1602,7 @@ void CCodec::onMessageReceived(const sp &msg) { } mChannel->onWorkDone( std::move(work), changed ? config->mOutputFormat : nullptr, - initData.hasChanged() ? initData.update().get() : nullptr, - numDiscardedInputBuffers); + initData.hasChanged() ? initData.update().get() : nullptr); break; } case kWhatWatch: { @@ -1669,16 +1629,25 @@ void CCodec::setDeadline( void CCodec::initiateReleaseIfStuck() { std::string name; bool pendingDeadline = false; - for (Mutexed *deadlinePtr : { &mDeadline, &mQueueDeadline, &mEosDeadline }) { - Mutexed::Locked deadline(*deadlinePtr); + { + Mutexed::Locked deadline(mDeadline); if (deadline->get() < std::chrono::steady_clock::now()) { name = deadline->getName(); - break; } if (deadline->get() != TimePoint::max()) { pendingDeadline = true; } } + if (name.empty()) { + constexpr std::chrono::steady_clock::duration kWorkDurationThreshold = 3s; + std::chrono::steady_clock::duration elapsed = mChannel->elapsed(); + if (elapsed >= kWorkDurationThreshold) { + name = "queue"; + } + if (elapsed > 0s) { + pendingDeadline = true; + } + } if (name.empty()) { // We're not stuck. if (pendingDeadline) { @@ -1694,33 +1663,6 @@ void CCodec::initiateReleaseIfStuck() { mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); } -void CCodec::onWorkQueued(bool eos) { - ALOGV("queued work count +1 from %d", mQueuedWorkCount.load()); - int32_t count = ++mQueuedWorkCount; - if (eos) { - CCodecWatchdog::getInstance()->watch(this); - Mutexed::Locked deadline(mEosDeadline); - deadline->set(std::chrono::steady_clock::now() + 3s, "eos"); - } - // TODO: query and use input/pipeline/output delay combined - if (count >= 4) { - CCodecWatchdog::getInstance()->watch(this); - Mutexed::Locked deadline(mQueueDeadline); - deadline->set(std::chrono::steady_clock::now() + 3s, "queue"); - } -} - -void CCodec::subQueuedWorkCount(uint32_t count) { - ALOGV("queued work count -%u from %d", count, mQueuedWorkCount.load()); - int32_t currentCount = (mQueuedWorkCount -= count); - if (currentCount == 0) { - Mutexed::Locked deadline(mEosDeadline); - deadline->set(TimePoint::max(), "none"); - } - Mutexed::Locked deadline(mQueueDeadline); - deadline->set(TimePoint::max(), "none"); -} - } // namespace android extern "C" android::CodecBase *CreateCodec() { diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/CCodec.h index 78b009ed80..bb8bd194f4 100644 --- a/media/codec2/sfplugin/CCodec.h +++ b/media/codec2/sfplugin/CCodec.h @@ -66,9 +66,8 @@ public: virtual void signalRequestIDRFrame() override; void initiateReleaseIfStuck(); - void onWorkDone(std::list> &workItems, - size_t numDiscardedInputBuffers); - void onInputBufferDone(const std::shared_ptr& buffer); + void onWorkDone(std::list> &workItems); + void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex); protected: virtual ~CCodec(); @@ -76,7 +75,7 @@ protected: virtual void onMessageReceived(const sp &msg) override; private: - typedef std::chrono::time_point TimePoint; + typedef std::chrono::steady_clock::time_point TimePoint; status_t tryAndReportOnError(std::function job); @@ -100,9 +99,6 @@ private: const std::chrono::milliseconds &timeout, const char *name); - void onWorkQueued(bool eos); - void subQueuedWorkCount(uint32_t count); - enum { kWhatAllocate, kWhatConfigure, @@ -167,13 +163,9 @@ private: struct ClientListener; Mutexed mDeadline; - std::atomic_int32_t mQueuedWorkCount; - Mutexed mQueueDeadline; - Mutexed mEosDeadline; typedef CCodecConfig Config; Mutexed mConfig; Mutexed>> mWorkDoneQueue; - Mutexed> mNumDiscardedInputBuffersQueue; friend class CCodecCallbackImpl; diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 8e6a3f8fe9..587f83c6e7 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -152,6 +152,11 @@ public: */ virtual std::unique_ptr toArrayMode(size_t size) = 0; + /** + * Return number of buffers the client owns. + */ + virtual size_t numClientBuffers() const = 0; + protected: // Pool to obtain blocks for input buffers. std::shared_ptr mPool; @@ -508,6 +513,14 @@ public: mBuffers.clear(); } + size_t numClientBuffers() const { + return std::count_if( + mBuffers.begin(), mBuffers.end(), + [](const Entry &entry) { + return (entry.clientBuffer != nullptr); + }); + } + private: friend class BuffersArrayImpl; @@ -693,6 +706,14 @@ public: } } + size_t numClientBuffers() const { + return std::count_if( + mBuffers.begin(), mBuffers.end(), + [](const Entry &entry) { + return entry.ownedByClient; + }); + } + private: std::string mImplName; ///< name for debugging const char *mName; ///< C-string version of name @@ -756,6 +777,10 @@ public: mImpl.flush(); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + private: BuffersArrayImpl mImpl; }; @@ -823,6 +848,10 @@ public: return std::move(array); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + virtual sp alloc(size_t size) { C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; std::shared_ptr block; @@ -967,6 +996,10 @@ public: return std::move(array); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + private: FlexBuffersImpl mImpl; std::shared_ptr mStore; @@ -1030,6 +1063,10 @@ public: return std::move(array); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + private: FlexBuffersImpl mImpl; std::shared_ptr mLocalBufferPool; @@ -1065,6 +1102,10 @@ public: void getArray(Vector> *array) const final { array->clear(); } + + size_t numClientBuffers() const final { + return 0u; + } }; class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers { @@ -1422,90 +1463,6 @@ void CCodecBufferChannel::QueueSync::stop() { count->value = -1; } -// CCodecBufferChannel::PipelineCapacity - -CCodecBufferChannel::PipelineCapacity::PipelineCapacity() - : input(0), component(0), - mName("") { -} - -void CCodecBufferChannel::PipelineCapacity::initialize( - int newInput, - int newComponent, - const char* newName, - const char* callerTag) { - input.store(newInput, std::memory_order_relaxed); - component.store(newComponent, std::memory_order_relaxed); - mName = newName; - ALOGV("[%s] %s -- PipelineCapacity::initialize(): " - "pipeline availability initialized ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - newInput, newComponent); -} - -bool CCodecBufferChannel::PipelineCapacity::allocate(const char* callerTag) { - int prevInput = input.fetch_sub(1, std::memory_order_relaxed); - int prevComponent = component.fetch_sub(1, std::memory_order_relaxed); - if (prevInput > 0 && prevComponent > 0) { - ALOGV("[%s] %s -- PipelineCapacity::allocate() returns true: " - "pipeline availability -1 all ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - prevInput - 1, - prevComponent - 1); - return true; - } - input.fetch_add(1, std::memory_order_relaxed); - component.fetch_add(1, std::memory_order_relaxed); - ALOGV("[%s] %s -- PipelineCapacity::allocate() returns false: " - "pipeline availability unchanged ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - prevInput, - prevComponent); - return false; -} - -void CCodecBufferChannel::PipelineCapacity::free(const char* callerTag) { - int prevInput = input.fetch_add(1, std::memory_order_relaxed); - int prevComponent = component.fetch_add(1, std::memory_order_relaxed); - ALOGV("[%s] %s -- PipelineCapacity::free(): " - "pipeline availability +1 all ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - prevInput + 1, - prevComponent + 1); -} - -int CCodecBufferChannel::PipelineCapacity::freeInputSlots( - size_t numDiscardedInputBuffers, - const char* callerTag) { - int prevInput = input.fetch_add(numDiscardedInputBuffers, - std::memory_order_relaxed); - ALOGV("[%s] %s -- PipelineCapacity::freeInputSlots(%zu): " - "pipeline availability +%zu input ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - numDiscardedInputBuffers, - numDiscardedInputBuffers, - prevInput + static_cast(numDiscardedInputBuffers), - component.load(std::memory_order_relaxed)); - return prevInput + static_cast(numDiscardedInputBuffers); -} - -int CCodecBufferChannel::PipelineCapacity::freeComponentSlot( - const char* callerTag) { - int prevComponent = component.fetch_add(1, std::memory_order_relaxed); - ALOGV("[%s] %s -- PipelineCapacity::freeComponentSlot(): " - "pipeline availability +1 component ==> " - "input = %d, component = %d", - mName, callerTag ? callerTag : "*", - input.load(std::memory_order_relaxed), - prevComponent + 1); - return prevComponent + 1; -} - // CCodecBufferChannel::ReorderStash CCodecBufferChannel::ReorderStash::ReorderStash() { @@ -1595,7 +1552,6 @@ CCodecBufferChannel::CCodecBufferChannel( mFrameIndex(0u), mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), - mAvailablePipelineCapacity(), mInputMetEos(false) { Mutexed>::Locked buffers(mInputBuffers); buffers->reset(new DummyInputBuffers("")); @@ -1658,6 +1614,9 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spinput.ordinal.customOrdinal = timeUs; work->input.buffers.clear(); + uint64_t queuedFrameIndex = work->input.ordinal.frameIndex.peeku(); + std::vector> queuedBuffers; + if (buffer->size() > 0u) { Mutexed>::Locked buffers(mInputBuffers); std::shared_ptr c2buffer; @@ -1665,11 +1624,9 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spinput.buffers.push_back(c2buffer); - } else { - mAvailablePipelineCapacity.freeInputSlots(1, "queueInputBufferInternal"); - if (eos) { - flags |= C2FrameData::FLAG_END_OF_STREAM; - } + queuedBuffers.push_back(c2buffer); + } else if (eos) { + flags |= C2FrameData::FLAG_END_OF_STREAM; } work->input.flags = (C2FrameData::flags_t)flags; // TODO: fill info's @@ -1680,10 +1637,16 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const sp> items; items.push_back(std::move(work)); + mPipelineWatcher.lock()->onWorkQueued( + queuedFrameIndex, + std::move(queuedBuffers), + PipelineWatcher::Clock::now()); c2_status_t err = mComponent->queue(&items); + if (err != C2_OK) { + mPipelineWatcher.lock()->onWorkDone(queuedFrameIndex); + } if (err == C2_OK && eos && buffer->size() > 0u) { - mCCodecCallback->onWorkQueued(false); work.reset(new C2Work); work->input.ordinal.timestamp = timeUs; work->input.ordinal.frameIndex = mFrameIndex++; @@ -1693,13 +1656,22 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spinput.flags = C2FrameData::FLAG_END_OF_STREAM; work->worklets.emplace_back(new C2Worklet); + queuedFrameIndex = work->input.ordinal.frameIndex.peeku(); + queuedBuffers.clear(); + items.clear(); items.push_back(std::move(work)); + + mPipelineWatcher.lock()->onWorkQueued( + queuedFrameIndex, + std::move(queuedBuffers), + PipelineWatcher::Clock::now()); err = mComponent->queue(&items); + if (err != C2_OK) { + mPipelineWatcher.lock()->onWorkDone(queuedFrameIndex); + } } if (err == C2_OK) { - mCCodecCallback->onWorkQueued(eos); - Mutexed>::Locked buffers(mInputBuffers); bool released = (*buffers)->releaseBuffer(buffer, nullptr, true); ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not "); @@ -1846,14 +1818,16 @@ void CCodecBufferChannel::feedInputBufferIfAvailable() { void CCodecBufferChannel::feedInputBufferIfAvailableInternal() { while (!mInputMetEos && !mReorderStash.lock()->hasPending() && - mAvailablePipelineCapacity.allocate("feedInputBufferIfAvailable")) { + !mPipelineWatcher.lock()->pipelineFull()) { sp inBuffer; size_t index; { Mutexed>::Locked buffers(mInputBuffers); + if ((*buffers)->numClientBuffers() >= mNumInputSlots) { + return; + } if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) { ALOGV("[%s] no new buffer available", mName); - mAvailablePipelineCapacity.free("feedInputBufferIfAvailable"); break; } } @@ -2032,15 +2006,12 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) { Mutexed>::Locked buffers(mInputBuffers); if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) { - buffers.unlock(); released = true; - mAvailablePipelineCapacity.freeInputSlots(1, "discardBuffer"); } } { Mutexed>::Locked buffers(mOutputBuffers); if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) { - buffers.unlock(); released = true; } } @@ -2408,10 +2379,14 @@ status_t CCodecBufferChannel::start( // about buffers from the previous generation do not interfere with the // newly initialized pipeline capacity. - mAvailablePipelineCapacity.initialize( - mNumInputSlots, - mNumInputSlots + mNumOutputSlots, - mName); + { + Mutexed::Locked watcher(mPipelineWatcher); + watcher->inputDelay(inputDelay ? inputDelay.value : 0) + .pipelineDelay(pipelineDelay ? pipelineDelay.value : 0) + .outputDelay(outputDelay ? outputDelay.value : 0) + .smoothnessFactor(kSmoothnessFactor); + watcher->flush(); + } mInputMetEos = false; mSync.start(); @@ -2472,21 +2447,16 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { buffer->meta()->setInt64("timeUs", 0); post = false; } - if (mAvailablePipelineCapacity.allocate("requestInitialInputBuffers")) { - if (post) { - mCallback->onInputBufferAvailable(index, buffer); - } else { - toBeQueued.emplace_back(buffer); - } + if (post) { + mCallback->onInputBufferAvailable(index, buffer); } else { - ALOGD("[%s] pipeline is full while requesting %zu-th input buffer", - mName, i); + toBeQueued.emplace_back(buffer); } } } for (const sp &buffer : toBeQueued) { if (queueInputBufferInternal(buffer) != OK) { - mAvailablePipelineCapacity.freeComponentSlot("requestInitialInputBuffers"); + ALOGV("[%s] Error while queueing initial buffers", mName); } } return OK; @@ -2532,28 +2502,25 @@ void CCodecBufferChannel::flush(const std::list> &flushe (*buffers)->flush(flushedWork); } mReorderStash.lock()->flush(); + mPipelineWatcher.lock()->flush(); } void CCodecBufferChannel::onWorkDone( std::unique_ptr work, const sp &outputFormat, - const C2StreamInitDataInfo::output *initData, - size_t numDiscardedInputBuffers) { + const C2StreamInitDataInfo::output *initData) { if (handleWork(std::move(work), outputFormat, initData)) { - mAvailablePipelineCapacity.freeInputSlots(numDiscardedInputBuffers, - "onWorkDone"); feedInputBufferIfAvailable(); } } void CCodecBufferChannel::onInputBufferDone( - const std::shared_ptr& buffer) { + uint64_t frameIndex, size_t arrayIndex) { + std::shared_ptr buffer = + mPipelineWatcher.lock()->onInputBufferReleased(frameIndex, arrayIndex); bool newInputSlotAvailable; { Mutexed>::Locked buffers(mInputBuffers); newInputSlotAvailable = (*buffers)->expireComponentBuffer(buffer); - if (newInputSlotAvailable) { - mAvailablePipelineCapacity.freeInputSlots(1, "onInputBufferDone"); - } } if (newInputSlotAvailable) { feedInputBufferIfAvailable(); @@ -2573,7 +2540,7 @@ bool CCodecBufferChannel::handleWork( if (work->worklets.size() != 1u || !work->worklets.front() || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE)) { - mAvailablePipelineCapacity.freeComponentSlot("handleWork"); + mPipelineWatcher.lock()->onWorkDone(work->input.ordinal.frameIndex.peeku()); } if (work->result == C2_NOT_FOUND) { @@ -2832,6 +2799,10 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { return OK; } +PipelineWatcher::Clock::duration CCodecBufferChannel::elapsed() { + return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now()); +} + void CCodecBufferChannel::setMetaMode(MetaMode mode) { mMetaMode = mode; } diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index ebc149144d..9dccab8918 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -34,6 +34,7 @@ #include #include "InputSurfaceWrapper.h" +#include "PipelineWatcher.h" namespace android { @@ -44,7 +45,6 @@ public: virtual ~CCodecCallback() = default; virtual void onError(status_t err, enum ActionCode actionCode) = 0; virtual void onOutputFramesRendered(int64_t mediaTimeUs, nsecs_t renderTimeNs) = 0; - virtual void onWorkQueued(bool eos) = 0; virtual void onOutputBuffersChanged() = 0; }; @@ -128,22 +128,21 @@ public: * @param workItems finished work item. * @param outputFormat new output format if it has changed, otherwise nullptr * @param initData new init data (CSD) if it has changed, otherwise nullptr - * @param numDiscardedInputBuffers the number of input buffers that are - * returned for the first time (not previously returned by - * onInputBufferDone()). */ void onWorkDone( std::unique_ptr work, const sp &outputFormat, - const C2StreamInitDataInfo::output *initData, - size_t numDiscardedInputBuffers); + const C2StreamInitDataInfo::output *initData); /** * Make an input buffer available for the client as it is no longer needed * by the codec. * - * @param buffer The buffer that becomes unused. + * @param frameIndex The index of input work + * @param arrayIndex The index of buffer in the input work buffers. */ - void onInputBufferDone(const std::shared_ptr& buffer); + void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex); + + PipelineWatcher::Clock::duration elapsed(); enum MetaMode { MODE_NONE, @@ -266,79 +265,7 @@ private: MetaMode mMetaMode; - // PipelineCapacity is used in the input buffer gating logic. - // - // There are three criteria that need to be met before - // onInputBufferAvailable() is called: - // 1. The number of input buffers that have been received by - // CCodecBufferChannel but not returned via onWorkDone() or - // onInputBufferDone() does not exceed a certain limit. (Let us call this - // number the "input" capacity.) - // 2. The number of work items that have been received by - // CCodecBufferChannel whose outputs have not been returned from the - // component (by calling onWorkDone()) does not exceed a certain limit. - // (Let us call this the "component" capacity.) - // - // These three criteria guarantee that a new input buffer that arrives from - // the invocation of onInputBufferAvailable() will not - // 1. overload CCodecBufferChannel's input buffers; - // 2. overload the component; or - // - struct PipelineCapacity { - // The number of available input capacity. - std::atomic_int input; - // The number of available component capacity. - std::atomic_int component; - - PipelineCapacity(); - // Set the values of #input and #component. - void initialize(int newInput, int newComponent, - const char* newName = "", - const char* callerTag = nullptr); - - // Return true and decrease #input and #component by one if - // they are all greater than zero; return false otherwise. - // - // callerTag is used for logging only. - // - // allocate() is called by CCodecBufferChannel to check whether it can - // receive another input buffer. If the return value is true, - // onInputBufferAvailable() and onOutputBufferAvailable() can be called - // afterwards. - bool allocate(const char* callerTag = nullptr); - - // Increase #input and #component by one. - // - // callerTag is used for logging only. - // - // free() is called by CCodecBufferChannel after allocate() returns true - // but onInputBufferAvailable() cannot be called for any reasons. It - // essentially undoes an allocate() call. - void free(const char* callerTag = nullptr); - - // Increase #input by @p numDiscardedInputBuffers. - // - // callerTag is used for logging only. - // - // freeInputSlots() is called by CCodecBufferChannel when onWorkDone() - // or onInputBufferDone() is called. @p numDiscardedInputBuffers is - // provided in onWorkDone(), and is 1 in onInputBufferDone(). - int freeInputSlots(size_t numDiscardedInputBuffers, - const char* callerTag = nullptr); - - // Increase #component by one and return the updated value. - // - // callerTag is used for logging only. - // - // freeComponentSlot() is called by CCodecBufferChannel when - // onWorkDone() is called. - int freeComponentSlot(const char* callerTag = nullptr); - - private: - // Component name. Used for logging. - const char* mName; - }; - PipelineCapacity mAvailablePipelineCapacity; + Mutexed mPipelineWatcher; class ReorderStash { public: diff --git a/media/codec2/sfplugin/PipelineWatcher.cpp b/media/codec2/sfplugin/PipelineWatcher.cpp new file mode 100644 index 0000000000..fe0a2c89d3 --- /dev/null +++ b/media/codec2/sfplugin/PipelineWatcher.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2019 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 "PipelineWatcher" + +#include + +#include + +#include "PipelineWatcher.h" + +namespace android { + +PipelineWatcher &PipelineWatcher::inputDelay(uint32_t value) { + mInputDelay = value; + return *this; +} + +PipelineWatcher &PipelineWatcher::pipelineDelay(uint32_t value) { + mPipelineDelay = value; + return *this; +} + +PipelineWatcher &PipelineWatcher::outputDelay(uint32_t value) { + mOutputDelay = value; + return *this; +} + +PipelineWatcher &PipelineWatcher::smoothnessFactor(uint32_t value) { + mSmoothnessFactor = value; + return *this; +} + +void PipelineWatcher::onWorkQueued( + uint64_t frameIndex, + std::vector> &&buffers, + const Clock::time_point &queuedAt) { + ALOGV("onWorkQueued(frameIndex=%llu, buffers(size=%zu), queuedAt=%lld)", + (unsigned long long)frameIndex, + buffers.size(), + (long long)queuedAt.time_since_epoch().count()); + auto it = mFramesInPipeline.find(frameIndex); + if (it != mFramesInPipeline.end()) { + ALOGD("onWorkQueued: Duplicate frame index (%llu); previous entry removed", + (unsigned long long)frameIndex); + (void)mFramesInPipeline.erase(it); + } + (void)mFramesInPipeline.try_emplace(frameIndex, std::move(buffers), queuedAt); +} + +std::shared_ptr PipelineWatcher::onInputBufferReleased( + uint64_t frameIndex, size_t arrayIndex) { + ALOGV("onInputBufferReleased(frameIndex=%llu, arrayIndex=%zu)", + (unsigned long long)frameIndex, arrayIndex); + auto it = mFramesInPipeline.find(frameIndex); + if (it == mFramesInPipeline.end()) { + ALOGD("onInputBufferReleased: frameIndex not found (%llu); ignored", + (unsigned long long)frameIndex); + return nullptr; + } + if (it->second.buffers.size() <= arrayIndex) { + ALOGD("onInputBufferReleased: buffers at %llu: size %zu, requested index: %zu", + (unsigned long long)frameIndex, it->second.buffers.size(), arrayIndex); + return nullptr; + } + std::shared_ptr buffer(std::move(it->second.buffers[arrayIndex])); + ALOGD_IF(!buffer, "onInputBufferReleased: buffer already released (%llu:%zu)", + (unsigned long long)frameIndex, arrayIndex); + return buffer; +} + +void PipelineWatcher::onWorkDone(uint64_t frameIndex) { + ALOGV("onWorkDone(frameIndex=%llu)", (unsigned long long)frameIndex); + auto it = mFramesInPipeline.find(frameIndex); + if (it == mFramesInPipeline.end()) { + ALOGD("onWorkDone: frameIndex not found (%llu); ignored", + (unsigned long long)frameIndex); + return; + } + (void)mFramesInPipeline.erase(it); +} + +void PipelineWatcher::flush() { + mFramesInPipeline.clear(); +} + +bool PipelineWatcher::pipelineFull() const { + if (mFramesInPipeline.size() >= + mInputDelay + mPipelineDelay + mOutputDelay + mSmoothnessFactor) { + ALOGV("pipelineFull: too many frames in pipeline (%zu)", mFramesInPipeline.size()); + return true; + } + size_t sizeWithInputReleased = std::count_if( + mFramesInPipeline.begin(), + mFramesInPipeline.end(), + [](const decltype(mFramesInPipeline)::value_type &value) { + for (const std::shared_ptr &buffer : value.second.buffers) { + if (buffer) { + return false; + } + } + return true; + }); + if (sizeWithInputReleased >= + mPipelineDelay + mOutputDelay + mSmoothnessFactor) { + ALOGV("pipelineFull: too many frames in pipeline, with input released (%zu)", + sizeWithInputReleased); + return true; + } + ALOGV("pipeline has room (total: %zu, input released: %zu)", + mFramesInPipeline.size(), sizeWithInputReleased); + return false; +} + +PipelineWatcher::Clock::duration PipelineWatcher::elapsed( + const PipelineWatcher::Clock::time_point &now) const { + return std::accumulate( + mFramesInPipeline.begin(), + mFramesInPipeline.end(), + Clock::duration::zero(), + [&now](const Clock::duration ¤t, + const decltype(mFramesInPipeline)::value_type &value) { + Clock::duration elapsed = now - value.second.queuedAt; + ALOGV("elapsed: frameIndex = %llu elapsed = %lldms", + (unsigned long long)value.first, + std::chrono::duration_cast(elapsed).count()); + return current > elapsed ? current : elapsed; + }); +} + +} // namespace android diff --git a/media/codec2/sfplugin/PipelineWatcher.h b/media/codec2/sfplugin/PipelineWatcher.h new file mode 100644 index 0000000000..ce82298730 --- /dev/null +++ b/media/codec2/sfplugin/PipelineWatcher.h @@ -0,0 +1,78 @@ +/* + * Copyright 2019 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 PIPELINE_WATCHER_H_ +#define PIPELINE_WATCHER_H_ + +#include +#include +#include + +#include + +namespace android { + +/** + * PipelineWatcher watches the status of the work. + */ +class PipelineWatcher { +public: + typedef std::chrono::steady_clock Clock; + + PipelineWatcher() + : mInputDelay(0), + mPipelineDelay(0), + mOutputDelay(0), + mSmoothnessFactor(0) {} + ~PipelineWatcher() = default; + + PipelineWatcher &inputDelay(uint32_t value); + PipelineWatcher &pipelineDelay(uint32_t value); + PipelineWatcher &outputDelay(uint32_t value); + PipelineWatcher &smoothnessFactor(uint32_t value); + + void onWorkQueued( + uint64_t frameIndex, + std::vector> &&buffers, + const Clock::time_point &queuedAt); + std::shared_ptr onInputBufferReleased( + uint64_t frameIndex, size_t arrayIndex); + void onWorkDone(uint64_t frameIndex); + void flush(); + + bool pipelineFull() const; + Clock::duration elapsed(const Clock::time_point &now) const; + +private: + uint32_t mInputDelay; + uint32_t mPipelineDelay; + uint32_t mOutputDelay; + uint32_t mSmoothnessFactor; + + struct Frame { + Frame(std::vector> &&b, + const Clock::time_point &q) + : buffers(b), + queuedAt(q) {} + std::vector> buffers; + const Clock::time_point queuedAt; + }; + std::map mFramesInPipeline; +}; + +} // namespace android + +#endif // PIPELINE_WATCHER_H_ -- GitLab From cb5702252856cb7cc5a3319c6ae314ffcc2cbf7d Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 6 Feb 2019 09:12:03 -0800 Subject: [PATCH 0901/1530] Use header file for namespace functions Test: build, boot Change-Id: I6e37a6a98de5f200672a78b3f2de1edb9b365a71 --- services/mediacodec/Android.mk | 3 +++ .../mediacodec/MediaCodecUpdateService.cpp | 18 +----------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 6a71d7d34b..f78c671720 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -108,6 +108,9 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libziparchive \ +LOCAL_HEADER_LIBRARIES := \ + libnativeloader-dummy-headers \ + LOCAL_MODULE := mediaswcodec LOCAL_INIT_RC := mediaswcodec.rc LOCAL_SANITIZE := scudo diff --git a/services/mediacodec/MediaCodecUpdateService.cpp b/services/mediacodec/MediaCodecUpdateService.cpp index 0e6892d645..50ccbcef39 100644 --- a/services/mediacodec/MediaCodecUpdateService.cpp +++ b/services/mediacodec/MediaCodecUpdateService.cpp @@ -20,28 +20,12 @@ #include #include #include +#include #include #include #include "MediaCodecUpdateService.h" -// Copied from GraphicsEnv.cpp -// TODO(b/37049319) Get this from a header once one exists -extern "C" { - android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); - bool android_link_namespaces(android_namespace_t* from, - android_namespace_t* to, - const char* shared_libs_sonames); - enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - }; -} - namespace android { void loadFromApex(const char *libDirPath) { -- GitLab From ba98acb34e60b2bf2dac5a55c4acbb80b5408568 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 18 Dec 2018 16:51:44 -0800 Subject: [PATCH 0902/1530] Audiopolicy: Move AudioPolicyMixCollection in helper function No functional changes Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I300450742a3d6f0b22f69a25eeda7800e0a27943 Signed-off-by: Kevin Rocard --- .../include/AudioPolicyMix.h | 5 + .../managerdefinitions/src/AudioPolicyMix.cpp | 220 ++++++++++-------- 2 files changed, 123 insertions(+), 102 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 7296c9532c..e5c7d48bab 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -99,6 +99,11 @@ public: status_t getDevicesForUid(uid_t uid, Vector& devices) const; void dump(String8 *dst) const; + +private: + enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX }; + MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex, + audio_attributes_t attributes, uid_t uid); }; } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 2489e76e7a..32c90b1114 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -156,128 +156,144 @@ void AudioPolicyMixCollection::closeOutput(sp &desc) } } -status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid, - sp &desc) +status_t AudioPolicyMixCollection::getOutputForAttr( + audio_attributes_t attributes, uid_t uid, sp &desc) { ALOGV("getOutputForAttr() querying %zu mixes:", size()); desc = 0; for (size_t i = 0; i < size(); i++) { sp policyMix = valueAt(i); + sp policyDesc = policyMix->getOutput(); + if (!policyDesc) { + ALOGV("Skiping %zu: Mix has no output", i); + continue; + } + AudioMix *mix = policyMix->getMix(); + switch (mixMatch(mix, i, attributes, uid)) { + case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort ? + case MixMatchStatus::NO_MATCH: continue; // skip the mix + case MixMatchStatus::MATCH:; + } - if (mix->mMixType == MIX_TYPE_PLAYERS) { - // TODO if adding more player rules (currently only 2), make rule handling "generic" - // as there is no difference in the treatment of usage- or uid-based rules - bool hasUsageMatchRules = false; - bool hasUsageExcludeRules = false; - bool usageMatchFound = false; - bool usageExclusionFound = false; - - bool hasUidMatchRules = false; - bool hasUidExcludeRules = false; - bool uidMatchFound = false; - bool uidExclusionFound = false; - - bool hasAddrMatch = false; - - // iterate over all mix criteria to list what rules this mix contains - for (size_t j = 0; j < mix->mCriteria.size(); j++) { - ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu", - i, j, mix->mCriteria.size()); - - // if there is an address match, prioritize that match - if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && - strncmp(attributes.tags + strlen("addr="), - mix->mDeviceAddress.string(), - AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - hasAddrMatch = true; - break; - } + desc = policyMix->getOutput(); + desc->mPolicyMix = mix; + return NO_ERROR; + } + return BAD_VALUE; +} - switch (mix->mCriteria[j].mRule) { - case RULE_MATCH_ATTRIBUTE_USAGE: - ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d", - mix->mCriteria[j].mValue.mUsage); - hasUsageMatchRules = true; - if (mix->mCriteria[j].mValue.mUsage == attributes.usage) { - // found one match against all allowed usages - usageMatchFound = true; - } - break; - case RULE_EXCLUDE_ATTRIBUTE_USAGE: - ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d", - mix->mCriteria[j].mValue.mUsage); - hasUsageExcludeRules = true; - if (mix->mCriteria[j].mValue.mUsage == attributes.usage) { - // found this usage is to be excluded - usageExclusionFound = true; - } - break; - case RULE_MATCH_UID: - ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid); - hasUidMatchRules = true; - if (mix->mCriteria[j].mValue.mUid == uid) { - // found one UID match against all allowed UIDs - uidMatchFound = true; - } - break; - case RULE_EXCLUDE_UID: - ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid); - hasUidExcludeRules = true; - if (mix->mCriteria[j].mValue.mUid == uid) { - // found this UID is to be excluded - uidExclusionFound = true; - } - break; - default: - break; - } +AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( + const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) { - // consistency checks: for each "dimension" of rules (usage, uid...), we can - // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination - if (hasUsageMatchRules && hasUsageExcludeRules) { - ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE" - " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i); - return BAD_VALUE; - } - if (hasUidMatchRules && hasUidExcludeRules) { - ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID" - " and RULE_EXCLUDE_UID in mix %zu", i); - return BAD_VALUE; - } + if (mix->mMixType == MIX_TYPE_PLAYERS) { + // TODO if adding more player rules (currently only 2), make rule handling "generic" + // as there is no difference in the treatment of usage- or uid-based rules + bool hasUsageMatchRules = false; + bool hasUsageExcludeRules = false; + bool usageMatchFound = false; + bool usageExclusionFound = false; - if ((hasUsageExcludeRules && usageExclusionFound) - || (hasUidExcludeRules && uidExclusionFound)) { - break; // stop iterating on criteria because an exclusion was found (will fail) - } + bool hasUidMatchRules = false; + bool hasUidExcludeRules = false; + bool uidMatchFound = false; + bool uidExclusionFound = false; - }//iterate on mix criteria + bool hasAddrMatch = false; - // determine if exiting on success (or implicit failure as desc is 0) - if (hasAddrMatch || - !((hasUsageExcludeRules && usageExclusionFound) || - (hasUsageMatchRules && !usageMatchFound) || - (hasUidExcludeRules && uidExclusionFound) || - (hasUidMatchRules && !uidMatchFound))) { - ALOGV("\tgetOutputForAttr will use mix %zu", i); - desc = policyMix->getOutput(); - } + // iterate over all mix criteria to list what rules this mix contains + for (size_t j = 0; j < mix->mCriteria.size(); j++) { + ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu", + mixIndex, j, mix->mCriteria.size()); - } else if (mix->mMixType == MIX_TYPE_RECORDERS) { - if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE && - strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + // if there is an address match, prioritize that match + if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && strncmp(attributes.tags + strlen("addr="), mix->mDeviceAddress.string(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = policyMix->getOutput(); + hasAddrMatch = true; + break; } + + switch (mix->mCriteria[j].mRule) { + case RULE_MATCH_ATTRIBUTE_USAGE: + ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d", + mix->mCriteria[j].mValue.mUsage); + hasUsageMatchRules = true; + if (mix->mCriteria[j].mValue.mUsage == attributes.usage) { + // found one match against all allowed usages + usageMatchFound = true; + } + break; + case RULE_EXCLUDE_ATTRIBUTE_USAGE: + ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d", + mix->mCriteria[j].mValue.mUsage); + hasUsageExcludeRules = true; + if (mix->mCriteria[j].mValue.mUsage == attributes.usage) { + // found this usage is to be excluded + usageExclusionFound = true; + } + break; + case RULE_MATCH_UID: + ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid); + hasUidMatchRules = true; + if (mix->mCriteria[j].mValue.mUid == uid) { + // found one UID match against all allowed UIDs + uidMatchFound = true; + } + break; + case RULE_EXCLUDE_UID: + ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid); + hasUidExcludeRules = true; + if (mix->mCriteria[j].mValue.mUid == uid) { + // found this UID is to be excluded + uidExclusionFound = true; + } + break; + default: + break; + } + + // consistency checks: for each "dimension" of rules (usage, uid...), we can + // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination + if (hasUsageMatchRules && hasUsageExcludeRules) { + ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE" + " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex); + return MixMatchStatus::INVALID_MIX; + } + if (hasUidMatchRules && hasUidExcludeRules) { + ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID" + " and RULE_EXCLUDE_UID in mix %zu", mixIndex); + return MixMatchStatus::INVALID_MIX; + } + + if ((hasUsageExcludeRules && usageExclusionFound) + || (hasUidExcludeRules && uidExclusionFound)) { + break; // stop iterating on criteria because an exclusion was found (will fail) + } + + }//iterate on mix criteria + + // determine if exiting on success (or implicit failure as desc is 0) + if (hasAddrMatch || + !((hasUsageExcludeRules && usageExclusionFound) || + (hasUsageMatchRules && !usageMatchFound) || + (hasUidExcludeRules && uidExclusionFound) || + (hasUidMatchRules && !uidMatchFound))) { + ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex); + return MixMatchStatus::MATCH; } - if (desc != 0) { - desc->mPolicyMix = mix; - return NO_ERROR; + + } else if (mix->mMixType == MIX_TYPE_RECORDERS) { + if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE && + strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + strncmp(attributes.tags + strlen("addr="), + mix->mDeviceAddress.string(), + AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { + return MixMatchStatus::MATCH; } } - return BAD_VALUE; + return MixMatchStatus::NO_MATCH; } sp AudioPolicyMixCollection::getDeviceAndMixForOutput( -- GitLab From 45986c74cd5253bcc81b1b4954e3ff3300f7e6d8 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 18 Dec 2018 18:22:59 -0800 Subject: [PATCH 0903/1530] Audioflinger: add timeout to PatchTrack Add support for timeout that was only supported by PatchRecord. This is implemented by moving the timeout and common code in a base class. Also remove PatchRecord useless virtual inheritance of RecordTrack. Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I833148f31a311ca41092be1d7e2d170f086322c5 Signed-off-by: Kevin Rocard --- services/audioflinger/AudioFlinger.h | 2 ++ services/audioflinger/PlaybackTracks.h | 10 ++---- services/audioflinger/RecordTracks.h | 12 ++----- services/audioflinger/TrackBase.h | 16 ++++++++++ services/audioflinger/Tracks.cpp | 43 +++++++++++++++++--------- 5 files changed, 53 insertions(+), 30 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index d8c0da559d..f16a196ac6 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -21,8 +21,10 @@ #include "Configuration.h" #include #include +#include #include #include +#include #include #include #include diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index bad3ca89df..33048fc366 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -318,7 +318,7 @@ private: }; // end of OutputTrack // playback track, used by PatchPanel -class PatchTrack : public Track, public PatchProxyBufferProvider { +class PatchTrack : public Track, public PatchTrackBase { public: PatchTrack(PlaybackThread *playbackThread, @@ -329,7 +329,8 @@ public: size_t frameCount, void *buffer, size_t bufferSize, - audio_output_flags_t flags); + audio_output_flags_t flags, + const Timeout& timeout = {}); virtual ~PatchTrack(); virtual status_t start(AudioSystem::sync_event_t event = @@ -345,12 +346,7 @@ public: const struct timespec *timeOut = NULL); virtual void releaseBuffer(Proxy::Buffer* buffer); - void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } - private: void restartIfDisabled(); - sp mProxy; - PatchProxyBufferProvider* mPeerProxy; - struct timespec mPeerTimeout; }; // end of PatchTrack diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 32af7d5339..ab4af33bab 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -113,7 +113,7 @@ private: }; // playback track, used by PatchPanel -class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider { +class PatchRecord : public RecordTrack, public PatchTrackBase { public: PatchRecord(RecordThread *recordThread, @@ -123,7 +123,8 @@ public: size_t frameCount, void *buffer, size_t bufferSize, - audio_input_flags_t flags); + audio_input_flags_t flags, + const Timeout& timeout = {}); virtual ~PatchRecord(); // AudioBufferProvider interface @@ -134,11 +135,4 @@ public: virtual status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = NULL); virtual void releaseBuffer(Proxy::Buffer *buffer); - - void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } - -private: - sp mProxy; - PatchProxyBufferProvider* mPeerProxy; - struct timespec mPeerTimeout; }; // end of PatchRecord diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index c94639b4d1..0ba0ab4f30 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -329,3 +329,19 @@ public: const struct timespec *requested = NULL) = 0; virtual void releaseBuffer(Proxy::Buffer* buffer) = 0; }; + +class PatchTrackBase : public PatchProxyBufferProvider +{ +public: + using Timeout = std::optional; + PatchTrackBase(sp proxy, const ThreadBase& thread, + const Timeout& timeout); + void setPeerTimeout(std::chrono::nanoseconds timeout); + void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + +protected: + const sp mProxy; + PatchProxyBufferProvider* mPeerProxy = nullptr; + struct timespec mPeerTimeout{}; + +}; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index e4af6569c8..863dc9e006 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -277,6 +277,27 @@ status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp& return NO_ERROR; } +AudioFlinger::ThreadBase::PatchTrackBase::PatchTrackBase(sp proxy, + const ThreadBase& thread, + const Timeout& timeout) + : mProxy(proxy) +{ + if (timeout) { + setPeerTimeout(*timeout); + } else { + // Double buffer mixer + uint64_t mixBufferNs = ((uint64_t)2 * thread.frameCount() * 1000000000) / + thread.sampleRate(); + setPeerTimeout(std::chrono::nanoseconds{mixBufferNs}); + } +} + +void AudioFlinger::ThreadBase::PatchTrackBase::setPeerTimeout(std::chrono::nanoseconds timeout) { + mPeerTimeout.tv_sec = timeout.count() / std::nano::den; + mPeerTimeout.tv_nsec = timeout.count() % std::nano::den; +} + + // ---------------------------------------------------------------------------- // Playback // ---------------------------------------------------------------------------- @@ -1615,19 +1636,16 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr size_t frameCount, void *buffer, size_t bufferSize, - audio_output_flags_t flags) + audio_output_flags_t flags, + const Timeout& timeout) : Track(playbackThread, NULL, streamType, audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, buffer, bufferSize, nullptr /* sharedBuffer */, AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH), - mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true)) + PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true), + *playbackThread, timeout) { - uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) / - playbackThread->sampleRate(); - mPeerTimeout.tv_sec = mixBufferNs / 1000000000; - mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); - ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec", __func__, mId, sampleRate, (int)mPeerTimeout.tv_sec, @@ -2088,19 +2106,16 @@ AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, size_t frameCount, void *buffer, size_t bufferSize, - audio_input_flags_t flags) + audio_input_flags_t flags, + const Timeout& timeout) : RecordTrack(recordThread, NULL, audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH), - mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true)) + PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true), + *recordThread, timeout) { - uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) / - recordThread->sampleRate(); - mPeerTimeout.tv_sec = mixBufferNs / 1000000000; - mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); - ALOGV("%s(%d): sampleRate %d mPeerTimeout %d.%03d sec", __func__, mId, sampleRate, (int)mPeerTimeout.tv_sec, -- GitLab From 68ac7ada1f7329db65445d05c986899dbb13b745 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 30 Jan 2019 14:03:28 -0800 Subject: [PATCH 0904/1530] Camera: Add HEIC support - Derive HEIC capabilities from camera HAL and media framework. - Add HeicCompositeStream to encode camera buffers to HEIC buffers. - Add ExifUtils to overwrite JPEG APP segments and send to media codec. - Add NDK enums and corresponding format support. Test: Camera CTS Bug: 79465976 Change-Id: I0a885e76335f3eba4be0fd42241edb0b7349f284 --- camera/ndk/impl/ACameraMetadata.cpp | 39 + .../include/camera/NdkCameraMetadataTags.h | 104 +- media/ndk/NdkImageReader.cpp | 2 + media/ndk/include/media/NdkImage.h | 10 +- services/camera/libcameraservice/Android.bp | 6 + .../api1/client2/JpegProcessor.cpp | 3 +- .../api1/client2/JpegProcessor.h | 3 +- .../api2/CameraDeviceClient.cpp | 54 +- .../libcameraservice/api2/CompositeStream.cpp | 3 +- .../libcameraservice/api2/CompositeStream.h | 12 +- .../api2/DepthCompositeStream.h | 6 - .../api2/HeicCompositeStream.cpp | 1606 +++++++++++++++++ .../api2/HeicCompositeStream.h | 250 +++ .../api2/HeicEncoderInfoManager.cpp | 294 +++ .../api2/HeicEncoderInfoManager.h | 77 + .../common/CameraProviderManager.cpp | 132 ++ .../common/CameraProviderManager.h | 6 + .../device3/Camera3Device.cpp | 30 +- .../device3/Camera3Stream.cpp | 5 +- .../libcameraservice/device3/Camera3Stream.h | 3 +- .../device3/Camera3StreamBufferListener.h | 4 +- .../device3/Camera3StreamInterface.h | 5 +- .../libcameraservice/utils/ExifUtils.cpp | 1046 +++++++++++ .../camera/libcameraservice/utils/ExifUtils.h | 245 +++ 24 files changed, 3908 insertions(+), 37 deletions(-) create mode 100644 services/camera/libcameraservice/api2/HeicCompositeStream.cpp create mode 100644 services/camera/libcameraservice/api2/HeicCompositeStream.h create mode 100644 services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp create mode 100644 services/camera/libcameraservice/api2/HeicEncoderInfoManager.h create mode 100644 services/camera/libcameraservice/utils/ExifUtils.cpp create mode 100644 services/camera/libcameraservice/utils/ExifUtils.h diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index 50ad7b28c8..de40990b93 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -36,6 +36,8 @@ ACameraMetadata::ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYP filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS); filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS); filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS); + filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS); + filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS); } // TODO: filter request/result keys } @@ -174,6 +176,16 @@ ACameraMetadata::filterDurations(uint32_t tag) { filteredDurations.push_back(duration); } break; + case ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS: + case ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS: + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_HEIC; + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + } + break; default: // Should not reach here ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag); @@ -247,6 +259,31 @@ ACameraMetadata::filterStreamConfigurations() { filteredDepthStreamConfigs.push_back(isInput); } mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, filteredDepthStreamConfigs); + + entry = mData.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS); + Vector filteredHeicStreamConfigs; + filteredHeicStreamConfigs.setCapacity(entry.count); + + for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) { + int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET]; + int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET]; + int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET]; + int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET]; + if (isInput == ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT) { + // Hide input streams + continue; + } + // Translate HAL formats to NDK format + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_HEIC; + } + + filteredHeicStreamConfigs.push_back(format); + filteredHeicStreamConfigs.push_back(width); + filteredHeicStreamConfigs.push_back(height); + filteredHeicStreamConfigs.push_back(isInput); + } + mData.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs); } bool @@ -485,6 +522,8 @@ std::unordered_set ACameraMetadata::sSystemTags ({ ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, ANDROID_DEPTH_MAX_DEPTH_SAMPLES, + ANDROID_HEIC_INFO_SUPPORTED, + ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT, }); /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 69b9e7e5a2..8c19e1d6cb 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -71,6 +71,8 @@ typedef enum acamera_metadata_section { ACAMERA_DEPTH, ACAMERA_LOGICAL_MULTI_CAMERA, ACAMERA_DISTORTION_CORRECTION, + ACAMERA_HEIC, + ACAMERA_HEIC_INFO, ACAMERA_SECTION_COUNT, ACAMERA_VENDOR = 0x8000 @@ -112,6 +114,8 @@ typedef enum acamera_metadata_section_start { ACAMERA_DISTORTION_CORRECTION_START = ACAMERA_DISTORTION_CORRECTION << 16, + ACAMERA_HEIC_START = ACAMERA_HEIC << 16, + ACAMERA_HEIC_INFO_START = ACAMERA_HEIC_INFO << 16, ACAMERA_VENDOR_START = ACAMERA_VENDOR << 16 } acamera_metadata_section_start_t; @@ -1912,6 +1916,7 @@ typedef enum acamera_metadata_tag { *

  • ACaptureRequest
  • *

    * + *

    This tag is also used for HEIC image capture.

    */ ACAMERA_JPEG_GPS_COORDINATES = // double[3] ACAMERA_JPEG_START, @@ -1927,6 +1932,7 @@ typedef enum acamera_metadata_tag { *
  • ACaptureRequest
  • *

    * + *

    This tag is also used for HEIC image capture.

    */ ACAMERA_JPEG_GPS_PROCESSING_METHOD = // byte ACAMERA_JPEG_START + 1, @@ -1942,6 +1948,7 @@ typedef enum acamera_metadata_tag { *
  • ACaptureRequest
  • *

    * + *

    This tag is also used for HEIC image capture.

    */ ACAMERA_JPEG_GPS_TIMESTAMP = // int64 ACAMERA_JPEG_START + 2, @@ -1986,6 +1993,10 @@ typedef enum acamera_metadata_tag { *
    *

    For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will * also be set to EXTERNAL. The above code is not relevant in such case.

    + *

    This tag is also used to describe the orientation of the HEIC image capture, in which + * case the rotation is reflected by + * EXIF orientation flag, and not by + * rotating the image data itself.

    * * @see ACAMERA_SENSOR_ORIENTATION */ @@ -2003,7 +2014,8 @@ typedef enum acamera_metadata_tag { *
  • ACaptureRequest
  • *

    * - *

    85-95 is typical usage range.

    + *

    85-95 is typical usage range. This tag is also used to describe the quality + * of the HEIC image capture.

    */ ACAMERA_JPEG_QUALITY = // byte ACAMERA_JPEG_START + 4, @@ -2019,6 +2031,7 @@ typedef enum acamera_metadata_tag { *
  • ACaptureRequest
  • *

    * + *

    This tag is also used to describe the quality of the HEIC image capture.

    */ ACAMERA_JPEG_THUMBNAIL_QUALITY = // byte ACAMERA_JPEG_START + 5, @@ -2055,6 +2068,10 @@ typedef enum acamera_metadata_tag { * orientation is requested. LEGACY device will always report unrotated thumbnail * size. * + *

    The tag is also used as thumbnail size for HEIC image format capture, in which case the + * the thumbnail rotation is reflected by + * EXIF orientation flag, and not by + * rotating the thumbnail data itself.

    * * @see ACAMERA_JPEG_ORIENTATION */ @@ -2088,6 +2105,7 @@ typedef enum acamera_metadata_tag { * and vice versa. *
  • All non-(0, 0) sizes will have non-zero widths and heights.
  • * + *

    This list is also used as supported thumbnail sizes for HEIC image format capture.

    * * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS */ @@ -5757,6 +5775,80 @@ typedef enum acamera_metadata_tag { ACAMERA_DISTORTION_CORRECTION_START + 1, ACAMERA_DISTORTION_CORRECTION_END, + /** + *

    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_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    The configurations are listed as (format, width, height, input?) tuples.

    + *

    If the camera device supports HEIC image format, it will support identical set of stream + * combinations involving HEIC image format, compared to the combinations involving JPEG + * image format as required by the device's hardware level and capabilities.

    + *

    All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats. + * Configuring JPEG and HEIC streams at the same time is not supported.

    + *

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

    + */ + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS = // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_t) + ACAMERA_HEIC_START, + /** + *

    This lists the minimum frame duration for each + * format/size combination for HEIC output formats.

    + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This should correspond to the frame duration when only that + * stream is active, with all processing (typically in android.*.mode) + * 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).

    + *

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

    + * + * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS + * @see ACAMERA_SENSOR_FRAME_DURATION + */ + ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS = // int64[4*n] + ACAMERA_HEIC_START + 1, + /** + *

    This lists the maximum stall duration for each + * output format/size combination for HEIC streams.

    + * + *

    Type: int64[4*n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    A stall duration is how much extra time would get added + * to the normal minimum frame duration for a repeating request + * that has streams with non-zero stall.

    + *

    This functions similarly to + * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for HEIC + * streams.

    + *

    All HEIC output stream formats may have a nonzero stall + * duration.

    + * + * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS + */ + ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS = // int64[4*n] + ACAMERA_HEIC_START + 2, + ACAMERA_HEIC_END, + } acamera_metadata_tag_t; /** @@ -8373,6 +8465,16 @@ typedef enum acamera_metadata_enum_acamera_distortion_correction_mode { } acamera_metadata_enum_android_distortion_correction_mode_t; +// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS +typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations { + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT = 0, + + ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT = 1, + +} acamera_metadata_enum_android_heic_available_heic_stream_configurations_t; + + + #endif /* __ANDROID_API__ >= 24 */ __END_DECLS diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index 010c1aa520..c3eb4379db 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -69,6 +69,7 @@ AImageReader::isSupportedFormatAndUsage(int32_t format, uint64_t usage) { case AIMAGE_FORMAT_DEPTH16: case AIMAGE_FORMAT_DEPTH_POINT_CLOUD: case AIMAGE_FORMAT_Y8: + case AIMAGE_FORMAT_HEIC: return true; case AIMAGE_FORMAT_PRIVATE: // For private format, cpu usage is prohibited. @@ -96,6 +97,7 @@ AImageReader::getNumPlanesForFormat(int32_t format) { case AIMAGE_FORMAT_DEPTH16: case AIMAGE_FORMAT_DEPTH_POINT_CLOUD: case AIMAGE_FORMAT_Y8: + case AIMAGE_FORMAT_HEIC: return 1; case AIMAGE_FORMAT_PRIVATE: return 0; diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h index 15b340c2b7..14d88cbbb2 100644 --- a/media/ndk/include/media/NdkImage.h +++ b/media/ndk/include/media/NdkImage.h @@ -526,7 +526,15 @@ enum AIMAGE_FORMATS { * (in bytes) between adjacent rows.

    * */ - AIMAGE_FORMAT_Y8 = 0x20203859 + AIMAGE_FORMAT_Y8 = 0x20203859, + + /** + * Compressed HEIC format. + * + *

    This format defines the HEIC brand of High Efficiency Image File + * Format as described in ISO/IEC 23008-12.

    + */ + AIMAGE_FORMAT_HEIC = 0x48454946, }; /** diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index a090479183..2d923bf49d 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -41,6 +41,8 @@ cc_library_shared { "api2/CameraDeviceClient.cpp", "api2/CompositeStream.cpp", "api2/DepthCompositeStream.cpp", + "api2/HeicEncoderInfoManager.cpp", + "api2/HeicCompositeStream.cpp", "device1/CameraHardwareInterface.cpp", "device3/Camera3Device.cpp", "device3/Camera3Stream.cpp", @@ -62,12 +64,14 @@ cc_library_shared { "hidl/HidlCameraService.cpp", "utils/CameraTraces.cpp", "utils/AutoConditionLock.cpp", + "utils/ExifUtils.cpp", "utils/TagMonitor.cpp", "utils/LatencyHistogram.cpp", ], shared_libs: [ "libdl", + "libexif", "libui", "liblog", "libutilscallstack", @@ -85,8 +89,10 @@ cc_library_shared { "libhidlbase", "libhidltransport", "libjpeg", + "libmedia_omx", "libmemunreachable", "libsensorprivacy", + "libstagefright", "libstagefright_foundation", "android.frameworks.cameraservice.common@2.0", "android.frameworks.cameraservice.service@2.0", diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index e6f75f4d96..36395f3442 100755 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -62,7 +62,8 @@ void JpegProcessor::onFrameAvailable(const BufferItem& /*item*/) { } } -void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/, int /*streamId*/) { +void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/, + int /*streamId*/, const CameraMetadata& /*settings*/) { // Intentionally left empty } diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h index 2ee930e02b..53e6836017 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.h +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h @@ -54,7 +54,8 @@ class JpegProcessor: // Camera3StreamBufferListener implementation void onBufferAcquired(const BufferInfo& bufferInfo) override; void onBufferReleased(const BufferInfo& bufferInfo) override; - void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override; + void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& settings) override; status_t updateStream(const Parameters ¶ms); status_t deleteStream(); diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 9e203da4d4..b512f2b6b1 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -34,6 +34,7 @@ #include #include "DepthCompositeStream.h" +#include "HeicCompositeStream.h" // Convenience methods for constructing binder::Status objects for error returns @@ -711,21 +712,35 @@ binder::Status CameraDeviceClient::isSessionConfigurationSupported( return res; if (!isStreamInfoValid) { - if (camera3::DepthCompositeStream::isDepthCompositeStream(surface)) { + bool isDepthCompositeStream = + camera3::DepthCompositeStream::isDepthCompositeStream(surface); + bool isHeicCompositeStream = + camera3::HeicCompositeStream::isHeicCompositeStream(surface); + if (isDepthCompositeStream || isHeicCompositeStream) { // We need to take in to account that composite streams can have // additional internal camera streams. std::vector compositeStreams; - ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo, + if (isDepthCompositeStream) { + ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo, + mDevice->info(), &compositeStreams); + } else { + ret = camera3::HeicCompositeStream::getCompositeStreamInfo(streamInfo, mDevice->info(), &compositeStreams); + } if (ret != OK) { String8 msg = String8::format( - "Camera %s: Failed adding depth composite streams: %s (%d)", + "Camera %s: Failed adding composite streams: %s (%d)", mCameraIdStr.string(), strerror(-ret), ret); ALOGE("%s: %s", __FUNCTION__, msg.string()); return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string()); } - if (compositeStreams.size() > 1) { + if (compositeStreams.size() == 0) { + // No internal streams means composite stream not + // supported. + *status = false; + return binder::Status::ok(); + } else if (compositeStreams.size() > 1) { streamCount += compositeStreams.size() - 1; streamConfiguration.streams.resize(streamCount); } @@ -937,15 +952,16 @@ binder::Status CameraDeviceClient::createStream( int streamId = camera3::CAMERA3_STREAM_ID_INVALID; std::vector surfaceIds; - if (!camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0])) { - err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width, - streamInfo.height, streamInfo.format, streamInfo.dataSpace, - static_cast(outputConfiguration.getRotation()), - &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(), - isShared); - } else { - sp compositeStream = new camera3::DepthCompositeStream(mDevice, - getRemoteCallback()); + bool isDepthCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]); + bool isHeicCompisiteStream = camera3::HeicCompositeStream::isHeicCompositeStream(surfaces[0]); + if (isDepthCompositeStream || isHeicCompisiteStream) { + sp compositeStream; + if (isDepthCompositeStream) { + compositeStream = new camera3::DepthCompositeStream(mDevice, getRemoteCallback()); + } else { + compositeStream = new camera3::HeicCompositeStream(mDevice, getRemoteCallback()); + } + err = compositeStream->createStream(surfaces, deferredConsumer, streamInfo.width, streamInfo.height, streamInfo.format, static_cast(outputConfiguration.getRotation()), @@ -955,6 +971,12 @@ binder::Status CameraDeviceClient::createStream( mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()), compositeStream); } + } else { + err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width, + streamInfo.height, streamInfo.format, streamInfo.dataSpace, + static_cast(outputConfiguration.getRotation()), + &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(), + isShared); } if (err != OK) { @@ -1437,6 +1459,8 @@ bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t heig camera_metadata_ro_entry streamConfigs = (dataSpace == HAL_DATASPACE_DEPTH) ? info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) : + (dataSpace == static_cast(HAL_DATASPACE_HEIF)) ? + info.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS) : info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); int32_t bestWidth = -1; @@ -1930,6 +1954,10 @@ void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras, remoteCb->onCaptureStarted(resultExtras, timestamp); } Camera2ClientBase::notifyShutter(resultExtras, timestamp); + + for (size_t i = 0; i < mCompositeStreamMap.size(); i++) { + mCompositeStreamMap.valueAt(i)->onShutter(resultExtras, timestamp); + } } void CameraDeviceClient::notifyPrepared(int streamId) { diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp index 796bf42ae0..354eaf93eb 100644 --- a/services/camera/libcameraservice/api2/CompositeStream.cpp +++ b/services/camera/libcameraservice/api2/CompositeStream.cpp @@ -82,7 +82,8 @@ status_t CompositeStream::deleteStream() { return deleteInternalStreams(); } -void CompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) { +void CompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& /*settings*/) { Mutex::Autolock l(mMutex); if (!mErrorState && (streamId == getStreamId())) { mPendingCaptureResults.emplace(frameNumber, CameraMetadata()); diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h index 583774550b..a401a8258c 100644 --- a/services/camera/libcameraservice/api2/CompositeStream.h +++ b/services/camera/libcameraservice/api2/CompositeStream.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "common/CameraDeviceBase.h" #include "device3/Camera3StreamInterface.h" @@ -66,15 +67,24 @@ public: // Return composite stream id. virtual int getStreamId() = 0; + // Notify when shutter notify is triggered + virtual void onShutter(const CaptureResultExtras& /*resultExtras*/, nsecs_t /*timestamp*/) {} + void onResultAvailable(const CaptureResult& result); bool onError(int32_t errorCode, const CaptureResultExtras& resultExtras); // Camera3StreamBufferListener implementation void onBufferAcquired(const BufferInfo& /*bufferInfo*/) override { /*Empty for now */ } void onBufferReleased(const BufferInfo& bufferInfo) override; - void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override; + void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& settings) override; protected: + struct ProducerListener : public BnProducerListener { + // ProducerListener impementation + void onBufferReleased() override { /*No impl. for now*/ }; + }; + status_t registerCompositeStreamListener(int32_t streamId); void eraseResult(int64_t frameNumber); void flagAnErrorFrameNumber(int64_t frameNumber); diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h index e8fe517e13..1bf31f47b3 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.h +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h @@ -21,7 +21,6 @@ #include #include -#include #include #include "CompositeStream.h" @@ -116,11 +115,6 @@ private: static const auto kDepthMapDataSpace = HAL_DATASPACE_DEPTH; static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF; - struct ProducerListener : public BnProducerListener { - // ProducerListener implementation - void onBufferReleased() override { /*No impl. for now*/ }; - }; - int mBlobStreamId, mBlobSurfaceId, mDepthStreamId, mDepthSurfaceId; size_t mBlobWidth, mBlobHeight; sp mBlobConsumer, mDepthConsumer; diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp new file mode 100644 index 0000000000..3eba863108 --- /dev/null +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -0,0 +1,1606 @@ +/* + * Copyright (C) 2019 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 "Camera3-HeicCompositeStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "common/CameraDeviceBase.h" +#include "utils/ExifUtils.h" +#include "HeicEncoderInfoManager.h" +#include "HeicCompositeStream.h" + +using android::hardware::camera::device::V3_5::CameraBlob; +using android::hardware::camera::device::V3_5::CameraBlobId; + +namespace android { +namespace camera3 { + +HeicCompositeStream::HeicCompositeStream(wp device, + wp cb) : + CompositeStream(device, cb), + mUseHeic(false), + mNumOutputTiles(1), + mOutputWidth(0), + mOutputHeight(0), + mMaxHeicBufferSize(0), + mGridWidth(HeicEncoderInfoManager::kGridWidth), + mGridHeight(HeicEncoderInfoManager::kGridHeight), + mGridRows(1), + mGridCols(1), + mUseGrid(false), + mAppSegmentStreamId(-1), + mAppSegmentSurfaceId(-1), + mAppSegmentBufferAcquired(false), + mMainImageStreamId(-1), + mMainImageSurfaceId(-1), + mYuvBufferAcquired(false), + mProducerListener(new ProducerListener()), + mOutputBufferCounter(0), + mGridTimestampUs(0) { +} + +HeicCompositeStream::~HeicCompositeStream() { + // Call deinitCodec in case stream hasn't been deleted yet to avoid any + // memory/resource leak. + deinitCodec(); + + mInputAppSegmentBuffers.clear(); + mCodecOutputBuffers.clear(); + + mAppSegmentStreamId = -1; + mAppSegmentSurfaceId = -1; + mAppSegmentConsumer.clear(); + mAppSegmentSurface.clear(); + + mMainImageStreamId = -1; + mMainImageSurfaceId = -1; + mMainImageConsumer.clear(); + mMainImageSurface.clear(); +} + +bool HeicCompositeStream::isHeicCompositeStream(const sp &surface) { + ANativeWindow *anw = surface.get(); + status_t err; + int format; + if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) { + String8 msg = String8::format("Failed to query Surface format: %s (%d)", strerror(-err), + err); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return false; + } + + int dataspace; + if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE, &dataspace)) != OK) { + String8 msg = String8::format("Failed to query Surface dataspace: %s (%d)", strerror(-err), + err); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return false; + } + + return ((format == HAL_PIXEL_FORMAT_BLOB) && (dataspace == HAL_DATASPACE_HEIF)); +} + +status_t HeicCompositeStream::createInternalStreams(const std::vector>& consumers, + bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int /*streamSetId*/, bool /*isShared*/) { + + sp device = mDevice.promote(); + if (!device.get()) { + ALOGE("%s: Invalid camera device!", __FUNCTION__); + return NO_INIT; + } + + status_t res = initializeCodec(width, height, device); + if (res != OK) { + ALOGE("%s: Failed to initialize HEIC/HEVC codec: %s (%d)", + __FUNCTION__, strerror(-res), res); + return NO_INIT; + } + + sp producer; + sp consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mAppSegmentConsumer = new CpuConsumer(consumer, 1); + mAppSegmentConsumer->setFrameAvailableListener(this); + mAppSegmentConsumer->setName(String8("Camera3-HeicComposite-AppSegmentStream")); + mAppSegmentSurface = new Surface(producer); + + res = device->createStream(mAppSegmentSurface, mAppSegmentMaxSize, 1, format, + kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId, surfaceIds); + if (res == OK) { + mAppSegmentSurfaceId = (*surfaceIds)[0]; + } else { + ALOGE("%s: Failed to create JPEG App segment stream: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + if (!mUseGrid) { + res = mCodec->createInputSurface(&producer); + if (res != OK) { + ALOGE("%s: Failed to create input surface for Heic codec: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } else { + BufferQueue::createBufferQueue(&producer, &consumer); + mMainImageConsumer = new CpuConsumer(consumer, 1); + mMainImageConsumer->setFrameAvailableListener(this); + mMainImageConsumer->setName(String8("Camera3-HeicComposite-HevcInputYUVStream")); + } + mMainImageSurface = new Surface(producer); + + res = mCodec->start(); + if (res != OK) { + ALOGE("%s: Failed to start codec: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + std::vector sourceSurfaceId; + //Use YUV_888 format if framework tiling is needed. + int srcStreamFmt = mUseGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 : + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = device->createStream(mMainImageSurface, width, height, srcStreamFmt, kHeifDataSpace, + rotation, id, physicalCameraId, &sourceSurfaceId); + if (res == OK) { + mMainImageSurfaceId = sourceSurfaceId[0]; + mMainImageStreamId = *id; + } else { + ALOGE("%s: Failed to create main image stream: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + mOutputSurface = consumers[0]; + res = registerCompositeStreamListener(getStreamId()); + if (res != OK) { + ALOGE("%s: Failed to register HAL main image stream", __FUNCTION__); + return res; + } + + return res; +} + +status_t HeicCompositeStream::deleteInternalStreams() { + requestExit(); + auto res = join(); + if (res != OK) { + ALOGE("%s: Failed to join with the main processing thread: %s (%d)", __FUNCTION__, + strerror(-res), res); + } + + deinitCodec(); + + if (mAppSegmentStreamId >= 0) { + sp device = mDevice.promote(); + if (!device.get()) { + ALOGE("%s: Invalid camera device!", __FUNCTION__); + return NO_INIT; + } + + res = device->deleteStream(mAppSegmentStreamId); + mAppSegmentStreamId = -1; + } + + return res; +} + +void HeicCompositeStream::onBufferReleased(const BufferInfo& bufferInfo) { + Mutex::Autolock l(mMutex); + + if (bufferInfo.mError) return; + + mCodecOutputBufferTimestamps.push(bufferInfo.mTimestamp); +} + +// We need to get the settings early to handle the case where the codec output +// arrives earlier than result metadata. +void HeicCompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& settings) { + ATRACE_ASYNC_BEGIN("HEIC capture", frameNumber); + + Mutex::Autolock l(mMutex); + if (mErrorState || (streamId != getStreamId())) { + return; + } + + mPendingCaptureResults.emplace(frameNumber, CameraMetadata()); + + camera_metadata_ro_entry entry; + + int32_t orientation = 0; + entry = settings.find(ANDROID_JPEG_ORIENTATION); + if (entry.count == 1) { + orientation = entry.data.i32[0]; + } + + int32_t quality = kDefaultJpegQuality; + entry = settings.find(ANDROID_JPEG_QUALITY); + if (entry.count == 1) { + quality = entry.data.i32[0]; + } + + mSettingsByFrameNumber[frameNumber] = std::make_pair(orientation, quality); +} + +void HeicCompositeStream::onFrameAvailable(const BufferItem& item) { + if (item.mDataSpace == static_cast(kAppSegmentDataSpace)) { + ALOGV("%s: JPEG APP segments buffer with ts: %" PRIu64 " ms. arrived!", + __func__, ns2ms(item.mTimestamp)); + + Mutex::Autolock l(mMutex); + if (!mErrorState) { + mInputAppSegmentBuffers.push_back(item.mTimestamp); + mInputReadyCondition.signal(); + } + } else if (item.mDataSpace == kHeifDataSpace) { + ALOGV("%s: YUV_888 buffer with ts: %" PRIu64 " ms. arrived!", + __func__, ns2ms(item.mTimestamp)); + + Mutex::Autolock l(mMutex); + if (!mUseGrid) { + ALOGE("%s: YUV_888 internal stream is only supported for HEVC tiling", + __FUNCTION__); + return; + } + if (!mErrorState) { + mInputYuvBuffers.push_back(item.mTimestamp); + mInputReadyCondition.signal(); + } + } else { + ALOGE("%s: Unexpected data space: 0x%x", __FUNCTION__, item.mDataSpace); + } +} + +status_t HeicCompositeStream::getCompositeStreamInfo(const OutputStreamInfo &streamInfo, + const CameraMetadata& ch, std::vector* compositeOutput /*out*/) { + if (compositeOutput == nullptr) { + return BAD_VALUE; + } + + compositeOutput->clear(); + + bool useGrid, useHeic; + bool isSizeSupported = isSizeSupportedByHeifEncoder( + streamInfo.width, streamInfo.height, &useHeic, &useGrid, nullptr); + if (!isSizeSupported) { + // Size is not supported by either encoder. + return OK; + } + + compositeOutput->insert(compositeOutput->end(), 2, streamInfo); + + // JPEG APPS segments Blob stream info + (*compositeOutput)[0].width = calcAppSegmentMaxSize(ch); + (*compositeOutput)[0].height = 1; + (*compositeOutput)[0].format = HAL_PIXEL_FORMAT_BLOB; + (*compositeOutput)[0].dataSpace = kAppSegmentDataSpace; + (*compositeOutput)[0].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN; + + // YUV/IMPLEMENTATION_DEFINED stream info + (*compositeOutput)[1].width = streamInfo.width; + (*compositeOutput)[1].height = streamInfo.height; + (*compositeOutput)[1].format = useGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 : + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + (*compositeOutput)[1].dataSpace = kHeifDataSpace; + (*compositeOutput)[1].consumerUsage = useHeic ? GRALLOC_USAGE_HW_IMAGE_ENCODER : + useGrid ? GRALLOC_USAGE_SW_READ_OFTEN : GRALLOC_USAGE_HW_VIDEO_ENCODER; + + return NO_ERROR; +} + +bool HeicCompositeStream::isSizeSupportedByHeifEncoder(int32_t width, int32_t height, + bool* useHeic, bool* useGrid, int64_t* stall) { + static HeicEncoderInfoManager& heicManager = HeicEncoderInfoManager::getInstance(); + return heicManager.isSizeSupported(width, height, useHeic, useGrid, stall); +} + +bool HeicCompositeStream::isInMemoryTempFileSupported() { + int memfd = syscall(__NR_memfd_create, "HEIF-try-memfd", MFD_CLOEXEC); + if (memfd == -1) { + if (errno != ENOSYS) { + ALOGE("%s: Failed to create tmpfs file. errno %d", __FUNCTION__, errno); + } + return false; + } + close(memfd); + return true; +} + +void HeicCompositeStream::onHeicOutputFrameAvailable( + const CodecOutputBufferInfo& outputBufferInfo) { + Mutex::Autolock l(mMutex); + + ALOGV("%s: index %d, offset %d, size %d, time %" PRId64 ", flags 0x%x", + __FUNCTION__, outputBufferInfo.index, outputBufferInfo.offset, + outputBufferInfo.size, outputBufferInfo.timeUs, outputBufferInfo.flags); + + if (!mErrorState) { + if ((outputBufferInfo.size > 0) && + ((outputBufferInfo.flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0)) { + mCodecOutputBuffers.push_back(outputBufferInfo); + mInputReadyCondition.signal(); + } else { + mCodec->releaseOutputBuffer(outputBufferInfo.index); + } + } else { + mCodec->releaseOutputBuffer(outputBufferInfo.index); + } +} + +void HeicCompositeStream::onHeicInputFrameAvailable(int32_t index) { + Mutex::Autolock l(mMutex); + + if (!mUseGrid) { + ALOGE("%s: Codec YUV input mode must only be used for Hevc tiling mode", __FUNCTION__); + return; + } + + mCodecInputBuffers.push_back(index); + mInputReadyCondition.signal(); +} + +void HeicCompositeStream::onHeicFormatChanged(sp& newFormat) { + if (newFormat == nullptr) { + ALOGE("%s: newFormat must not be null!", __FUNCTION__); + return; + } + + Mutex::Autolock l(mMutex); + + AString mime; + AString mimeHeic(MIMETYPE_IMAGE_ANDROID_HEIC); + newFormat->findString(KEY_MIME, &mime); + if (mime != mimeHeic) { + // For HEVC codec, below keys need to be filled out or overwritten so that the + // muxer can handle them as HEIC output image. + newFormat->setString(KEY_MIME, mimeHeic); + newFormat->setInt32(KEY_WIDTH, mOutputWidth); + newFormat->setInt32(KEY_HEIGHT, mOutputHeight); + if (mUseGrid) { + newFormat->setInt32(KEY_TILE_WIDTH, mGridWidth); + newFormat->setInt32(KEY_TILE_HEIGHT, mGridHeight); + newFormat->setInt32(KEY_GRID_ROWS, mGridRows); + newFormat->setInt32(KEY_GRID_COLUMNS, mGridCols); + } + } + newFormat->setInt32(KEY_IS_DEFAULT, 1 /*isPrimary*/); + + int32_t gridRows, gridCols; + if (newFormat->findInt32(KEY_GRID_ROWS, &gridRows) && + newFormat->findInt32(KEY_GRID_COLUMNS, &gridCols)) { + mNumOutputTiles = gridRows * gridCols; + } else { + mNumOutputTiles = 1; + } + + ALOGV("%s: mNumOutputTiles is %zu", __FUNCTION__, mNumOutputTiles); + mFormat = newFormat; +} + +void HeicCompositeStream::onHeicCodecError() { + Mutex::Autolock l(mMutex); + mErrorState = true; +} + +status_t HeicCompositeStream::configureStream() { + if (isRunning()) { + // Processing thread is already running, nothing more to do. + return NO_ERROR; + } + + if (mOutputSurface.get() == nullptr) { + ALOGE("%s: No valid output surface set!", __FUNCTION__); + return NO_INIT; + } + + auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener); + if (res != OK) { + ALOGE("%s: Unable to connect to native window for stream %d", + __FUNCTION__, mMainImageStreamId); + return res; + } + + if ((res = native_window_set_buffers_format(mOutputSurface.get(), HAL_PIXEL_FORMAT_BLOB)) + != OK) { + ALOGE("%s: Unable to configure stream buffer format for stream %d", __FUNCTION__, + mMainImageStreamId); + return res; + } + + ANativeWindow *anwConsumer = mOutputSurface.get(); + int maxConsumerBuffers; + if ((res = anwConsumer->query(anwConsumer, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &maxConsumerBuffers)) != OK) { + ALOGE("%s: Unable to query consumer undequeued" + " buffer count for stream %d", __FUNCTION__, mMainImageStreamId); + return res; + } + + // Cannot use SourceSurface buffer count since it could be codec's 512*512 tile + // buffer count. + int maxProducerBuffers = 1; + if ((res = native_window_set_buffer_count( + anwConsumer, maxProducerBuffers + maxConsumerBuffers)) != OK) { + ALOGE("%s: Unable to set buffer count for stream %d", __FUNCTION__, mMainImageStreamId); + return res; + } + + if ((res = native_window_set_buffers_dimensions(anwConsumer, mMaxHeicBufferSize, 1)) != OK) { + ALOGE("%s: Unable to set buffer dimension %zu x 1 for stream %d: %s (%d)", + __FUNCTION__, mMaxHeicBufferSize, mMainImageStreamId, strerror(-res), res); + return res; + } + + run("HeicCompositeStreamProc"); + + return NO_ERROR; +} + +status_t HeicCompositeStream::insertGbp(SurfaceMap* /*out*/outSurfaceMap, + Vector* /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) { + if (outSurfaceMap->find(mAppSegmentStreamId) == outSurfaceMap->end()) { + (*outSurfaceMap)[mAppSegmentStreamId] = std::vector(); + outputStreamIds->push_back(mAppSegmentStreamId); + } + (*outSurfaceMap)[mAppSegmentStreamId].push_back(mAppSegmentSurfaceId); + + if (outSurfaceMap->find(mMainImageStreamId) == outSurfaceMap->end()) { + (*outSurfaceMap)[mMainImageStreamId] = std::vector(); + outputStreamIds->push_back(mMainImageStreamId); + } + (*outSurfaceMap)[mMainImageStreamId].push_back(mMainImageSurfaceId); + + if (currentStreamId != nullptr) { + *currentStreamId = mMainImageStreamId; + } + + return NO_ERROR; +} + +void HeicCompositeStream::onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) { + Mutex::Autolock l(mMutex); + if (mErrorState) { + return; + } + + if (mSettingsByFrameNumber.find(resultExtras.frameNumber) != mSettingsByFrameNumber.end()) { + mFrameNumberMap.emplace(resultExtras.frameNumber, timestamp); + mSettingsByTimestamp[timestamp] = mSettingsByFrameNumber[resultExtras.frameNumber]; + mSettingsByFrameNumber.erase(resultExtras.frameNumber); + mInputReadyCondition.signal(); + } +} + +void HeicCompositeStream::compilePendingInputLocked() { + while (!mSettingsByTimestamp.empty()) { + auto it = mSettingsByTimestamp.begin(); + mPendingInputFrames[it->first].orientation = it->second.first; + mPendingInputFrames[it->first].quality = it->second.second; + mSettingsByTimestamp.erase(it); + } + + while (!mInputAppSegmentBuffers.empty() && !mAppSegmentBufferAcquired) { + CpuConsumer::LockedBuffer imgBuffer; + auto it = mInputAppSegmentBuffers.begin(); + auto res = mAppSegmentConsumer->lockNextBuffer(&imgBuffer); + if (res == NOT_ENOUGH_DATA) { + // Canot not lock any more buffers. + break; + } else if ((res != OK) || (*it != imgBuffer.timestamp)) { + if (res != OK) { + ALOGE("%s: Error locking JPEG_APP_SEGMENTS image buffer: %s (%d)", __FUNCTION__, + strerror(-res), res); + } else { + ALOGE("%s: Expecting JPEG_APP_SEGMENTS buffer with time stamp: %" PRId64 + " received buffer with time stamp: %" PRId64, __FUNCTION__, + *it, imgBuffer.timestamp); + } + mPendingInputFrames[*it].error = true; + mInputAppSegmentBuffers.erase(it); + continue; + } + + if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) && + (mPendingInputFrames[imgBuffer.timestamp].error)) { + mAppSegmentConsumer->unlockBuffer(imgBuffer); + } else { + mPendingInputFrames[imgBuffer.timestamp].appSegmentBuffer = imgBuffer; + mAppSegmentBufferAcquired = true; + } + mInputAppSegmentBuffers.erase(it); + } + + while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired) { + CpuConsumer::LockedBuffer imgBuffer; + auto it = mInputYuvBuffers.begin(); + auto res = mMainImageConsumer->lockNextBuffer(&imgBuffer); + if (res == NOT_ENOUGH_DATA) { + // Canot not lock any more buffers. + break; + } else if (res != OK) { + ALOGE("%s: Error locking YUV_888 image buffer: %s (%d)", __FUNCTION__, + strerror(-res), res); + mPendingInputFrames[*it].error = true; + mInputYuvBuffers.erase(it); + continue; + } else if (*it != imgBuffer.timestamp) { + ALOGW("%s: Expecting YUV_888 buffer with time stamp: %" PRId64 " received buffer with " + "time stamp: %" PRId64, __FUNCTION__, *it, imgBuffer.timestamp); + mPendingInputFrames[*it].error = true; + mInputYuvBuffers.erase(it); + continue; + } + + if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) && + (mPendingInputFrames[imgBuffer.timestamp].error)) { + mMainImageConsumer->unlockBuffer(imgBuffer); + } else { + mPendingInputFrames[imgBuffer.timestamp].yuvBuffer = imgBuffer; + mYuvBufferAcquired = true; + } + mInputYuvBuffers.erase(it); + } + + while (!mCodecOutputBuffers.empty()) { + auto it = mCodecOutputBuffers.begin(); + // Bitstream buffer timestamp doesn't necessarily directly correlate with input + // buffer timestamp. Assume encoder input to output is FIFO, use a queue + // to look up timestamp. + int64_t bufferTime = -1; + if (mCodecOutputBufferTimestamps.empty()) { + ALOGE("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__); + } else { + // Direct mapping between camera timestamp (in ns) and codec timestamp (in us). + bufferTime = mCodecOutputBufferTimestamps.front(); + mOutputBufferCounter++; + if (mOutputBufferCounter == mNumOutputTiles) { + mCodecOutputBufferTimestamps.pop(); + mOutputBufferCounter = 0; + } + + mPendingInputFrames[bufferTime].codecOutputBuffers.push_back(*it); + } + mCodecOutputBuffers.erase(it); + } + + while (!mFrameNumberMap.empty()) { + auto it = mFrameNumberMap.begin(); + mPendingInputFrames[it->second].frameNumber = it->first; + mFrameNumberMap.erase(it); + } + + // Heic composition doesn't depend on capture result, so no need to check + // mErrorFrameNumbers. Just remove them. + mErrorFrameNumbers.clear(); + + // Distribute codec input buffers to be filled out from YUV output + for (auto it = mPendingInputFrames.begin(); + it != mPendingInputFrames.end() && mCodecInputBuffers.size() > 0; it++) { + InputFrame& inputFrame(it->second); + if (inputFrame.codecInputCounter < mGridRows * mGridCols) { + // Available input tiles that are required for the current input + // image. + size_t newInputTiles = std::min(mCodecInputBuffers.size(), + mGridRows * mGridCols - inputFrame.codecInputCounter); + for (size_t i = 0; i < newInputTiles; i++) { + CodecInputBufferInfo inputInfo = + { mCodecInputBuffers[0], mGridTimestampUs++, inputFrame.codecInputCounter }; + inputFrame.codecInputBuffers.push_back(inputInfo); + + mCodecInputBuffers.erase(mCodecInputBuffers.begin()); + inputFrame.codecInputCounter++; + } + break; + } + } +} + +bool HeicCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*out*/) { + if (currentTs == nullptr) { + return false; + } + + bool newInputAvailable = false; + for (const auto& it : mPendingInputFrames) { + bool appSegmentBufferReady = (it.second.appSegmentBuffer.data != nullptr) && + !it.second.appSegmentWritten; + bool codecOutputReady = !it.second.codecOutputBuffers.empty(); + bool codecInputReady = (it.second.yuvBuffer.data != nullptr) && + (!it.second.codecInputBuffers.empty()); + if ((!it.second.error) && + (it.first < *currentTs) && + (appSegmentBufferReady || codecOutputReady || codecInputReady)) { + *currentTs = it.first; + newInputAvailable = true; + break; + } + } + + return newInputAvailable; +} + +int64_t HeicCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*out*/) { + int64_t res = -1; + if (currentTs == nullptr) { + return res; + } + + for (const auto& it : mPendingInputFrames) { + if (it.second.error && !it.second.errorNotified && (it.first < *currentTs)) { + *currentTs = it.first; + res = it.second.frameNumber; + break; + } + } + + return res; +} + +status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp, + InputFrame &inputFrame) { + ATRACE_CALL(); + status_t res = OK; + + bool appSegmentBufferReady = inputFrame.appSegmentBuffer.data != nullptr && + !inputFrame.appSegmentWritten; + bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0; + bool codecInputReady = inputFrame.yuvBuffer.data != nullptr && + !inputFrame.codecInputBuffers.empty(); + + if (!appSegmentBufferReady && !codecOutputReady && !codecInputReady) { + ALOGW("%s: No valid appSegmentBuffer/codec input/outputBuffer available!", __FUNCTION__); + return OK; + } + + // Handle inputs for Hevc tiling + if (codecInputReady) { + res = processCodecInputFrame(inputFrame); + if (res != OK) { + ALOGE("%s: Failed to process codec input frame: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + } + + // Initialize and start muxer if not yet done so + if (inputFrame.muxer == nullptr) { + res = startMuxerForInputFrame(timestamp, inputFrame); + if (res != OK) { + ALOGE("%s: Failed to create and start muxer: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + } + + // Write JPEG APP segments data to the muxer. + if (appSegmentBufferReady && inputFrame.muxer != nullptr) { + res = processAppSegment(timestamp, inputFrame); + if (res != OK) { + ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + } + + // Write media codec bitstream buffers to muxer. + while (!inputFrame.codecOutputBuffers.empty()) { + res = processOneCodecOutputFrame(timestamp, inputFrame); + if (res != OK) { + ALOGE("%s: Failed to process codec output frame: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + } + + if (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0) { + res = processCompletedInputFrame(timestamp, inputFrame); + if (res != OK) { + ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + } + + return res; +} + +status_t HeicCompositeStream::startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame) { + sp outputANW = mOutputSurface; + if (inputFrame.codecOutputBuffers.size() == 0) { + // No single codec output buffer has been generated. Continue to + // wait. + return OK; + } + + auto res = outputANW->dequeueBuffer(mOutputSurface.get(), &inputFrame.anb, &inputFrame.fenceFd); + if (res != OK) { + ALOGE("%s: Error retrieving output buffer: %s (%d)", __FUNCTION__, strerror(-res), + res); + return res; + } + + // Combine current thread id, stream id and timestamp to uniquely identify image. + std::ostringstream tempOutputFile; + tempOutputFile << "HEIF-" << pthread_self() << "-" + << getStreamId() << "-" << timestamp; + inputFrame.fileFd = syscall(__NR_memfd_create, tempOutputFile.str().c_str(), MFD_CLOEXEC); + if (inputFrame.fileFd < 0) { + ALOGE("%s: Failed to create file %s. Error no is %d", __FUNCTION__, + tempOutputFile.str().c_str(), errno); + return NO_INIT; + } + inputFrame.muxer = new MediaMuxer(inputFrame.fileFd, MediaMuxer::OUTPUT_FORMAT_HEIF); + if (inputFrame.muxer == nullptr) { + ALOGE("%s: Failed to create MediaMuxer for file fd %d", + __FUNCTION__, inputFrame.fileFd); + return NO_INIT; + } + + res = inputFrame.muxer->setOrientationHint(inputFrame.orientation); + if (res != OK) { + ALOGE("%s: Failed to setOrientationHint: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + // Set encoder quality + { + sp qualityParams = new AMessage; + qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, inputFrame.quality); + res = mCodec->setParameters(qualityParams); + if (res != OK) { + ALOGE("%s: Failed to set codec quality: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } + + ssize_t trackId = inputFrame.muxer->addTrack(mFormat); + if (trackId < 0) { + ALOGE("%s: Failed to addTrack to the muxer: %zd", __FUNCTION__, trackId); + return NO_INIT; + } + + inputFrame.trackIndex = trackId; + inputFrame.pendingOutputTiles = mNumOutputTiles; + + res = inputFrame.muxer->start(); + if (res != OK) { + ALOGE("%s: Failed to start MediaMuxer: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + return OK; +} + +status_t HeicCompositeStream::processAppSegment(nsecs_t timestamp, InputFrame &inputFrame) { + size_t app1Size = 0; + auto appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data, + inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height, + &app1Size); + ALOGV("%s: appSegmentSize is %zu, width %d, height %d, app1Size %zu", __FUNCTION__, + appSegmentSize, inputFrame.appSegmentBuffer.width, + inputFrame.appSegmentBuffer.height, app1Size); + if (appSegmentSize == 0) { + ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__); + return NO_INIT; + } + + std::unique_ptr exifUtils(ExifUtils::create()); + auto exifRes = exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size); + if (!exifRes) { + ALOGE("%s: Failed to initialize ExifUtils object!", __FUNCTION__); + return BAD_VALUE; + } + //TODO: Use capture result metadata and static metadata to fill out the + //rest. + CameraMetadata dummyMeta; + exifRes = exifUtils->setFromMetadata(dummyMeta, mOutputWidth, mOutputHeight); + if (!exifRes) { + ALOGE("%s: Failed to set Exif tags using metadata and main image sizes", __FUNCTION__); + return BAD_VALUE; + } + exifRes = exifUtils->setOrientation(inputFrame.orientation); + if (!exifRes) { + ALOGE("%s: ExifUtils failed to set orientation", __FUNCTION__); + return BAD_VALUE; + } + exifRes = exifUtils->generateApp1(); + if (!exifRes) { + ALOGE("%s: ExifUtils failed to generate APP1 segment", __FUNCTION__); + return BAD_VALUE; + } + + unsigned int newApp1Length = exifUtils->getApp1Length(); + const uint8_t *newApp1Segment = exifUtils->getApp1Buffer(); + + //Assemble the APP1 marker buffer required by MediaCodec + uint8_t kExifApp1Marker[] = {'E', 'x', 'i', 'f', 0xFF, 0xE1, 0x00, 0x00}; + kExifApp1Marker[6] = static_cast(newApp1Length >> 8); + kExifApp1Marker[7] = static_cast(newApp1Length & 0xFF); + size_t appSegmentBufferSize = sizeof(kExifApp1Marker) + + appSegmentSize - app1Size + newApp1Length; + uint8_t* appSegmentBuffer = new uint8_t[appSegmentBufferSize]; + memcpy(appSegmentBuffer, kExifApp1Marker, sizeof(kExifApp1Marker)); + memcpy(appSegmentBuffer + sizeof(kExifApp1Marker), newApp1Segment, newApp1Length); + if (appSegmentSize - app1Size > 0) { + memcpy(appSegmentBuffer + sizeof(kExifApp1Marker) + newApp1Length, + inputFrame.appSegmentBuffer.data + app1Size, appSegmentSize - app1Size); + } + + sp aBuffer = new ABuffer(appSegmentBuffer, appSegmentBufferSize); + auto res = inputFrame.muxer->writeSampleData(aBuffer, inputFrame.trackIndex, + timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA); + delete[] appSegmentBuffer; + + if (res != OK) { + ALOGE("%s: Failed to write JPEG APP segments to muxer: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + inputFrame.appSegmentWritten = true; + + return OK; +} + +status_t HeicCompositeStream::processCodecInputFrame(InputFrame &inputFrame) { + for (auto& inputBuffer : inputFrame.codecInputBuffers) { + sp buffer; + auto res = mCodec->getInputBuffer(inputBuffer.index, &buffer); + if (res != OK) { + ALOGE("%s: Error getting codec input buffer: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + // Copy one tile from source to destination. + size_t tileX = inputBuffer.tileIndex % mGridCols; + size_t tileY = inputBuffer.tileIndex / mGridCols; + size_t top = mGridHeight * tileY; + size_t left = mGridWidth * tileX; + size_t width = (tileX == static_cast(mGridCols) - 1) ? + mOutputWidth - tileX * mGridWidth : mGridWidth; + size_t height = (tileY == static_cast(mGridRows) - 1) ? + mOutputHeight - tileY * mGridHeight : mGridHeight; + ALOGV("%s: inputBuffer tileIndex [%zu, %zu], top %zu, left %zu, width %zu, height %zu", + __FUNCTION__, tileX, tileY, top, left, width, height); + + res = copyOneYuvTile(buffer, inputFrame.yuvBuffer, top, left, width, height); + if (res != OK) { + ALOGE("%s: Failed to copy YUV tile %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + res = mCodec->queueInputBuffer(inputBuffer.index, 0, buffer->capacity(), + inputBuffer.timeUs, 0, nullptr /*errorDetailMsg*/); + if (res != OK) { + ALOGE("%s: Failed to queueInputBuffer to Codec: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } + + inputFrame.codecInputBuffers.clear(); + return OK; +} + +status_t HeicCompositeStream::processOneCodecOutputFrame(nsecs_t timestamp, + InputFrame &inputFrame) { + auto it = inputFrame.codecOutputBuffers.begin(); + sp buffer; + status_t res = mCodec->getOutputBuffer(it->index, &buffer); + if (res != OK) { + ALOGE("%s: Error getting Heic codec output buffer at index %d: %s (%d)", + __FUNCTION__, it->index, strerror(-res), res); + return res; + } + if (buffer == nullptr) { + ALOGE("%s: Invalid Heic codec output buffer at index %d", + __FUNCTION__, it->index); + return BAD_VALUE; + } + + sp aBuffer = new ABuffer(buffer->data(), buffer->size()); + res = inputFrame.muxer->writeSampleData( + aBuffer, inputFrame.trackIndex, timestamp, 0 /*flags*/); + if (res != OK) { + ALOGE("%s: Failed to write buffer index %d to muxer: %s (%d)", + __FUNCTION__, it->index, strerror(-res), res); + return res; + } + + mCodec->releaseOutputBuffer(it->index); + if (inputFrame.pendingOutputTiles == 0) { + ALOGW("%s: Codec generated more tiles than expected!", __FUNCTION__); + } else { + inputFrame.pendingOutputTiles--; + } + + inputFrame.codecOutputBuffers.erase(inputFrame.codecOutputBuffers.begin()); + return OK; +} + +status_t HeicCompositeStream::processCompletedInputFrame(nsecs_t timestamp, + InputFrame &inputFrame) { + sp outputANW = mOutputSurface; + inputFrame.muxer->stop(); + + // Copy the content of the file to memory. + sp gb = GraphicBuffer::from(inputFrame.anb); + void* dstBuffer; + auto res = gb->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &dstBuffer, inputFrame.fenceFd); + if (res != OK) { + ALOGE("%s: Error trying to lock output buffer fence: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + off_t fSize = lseek(inputFrame.fileFd, 0, SEEK_END); + if (static_cast(fSize) > mMaxHeicBufferSize - sizeof(CameraBlob)) { + ALOGE("%s: Error: MediaMuxer output size %ld is larger than buffer sizer %zu", + __FUNCTION__, fSize, mMaxHeicBufferSize - sizeof(CameraBlob)); + return BAD_VALUE; + } + + lseek(inputFrame.fileFd, 0, SEEK_SET); + ssize_t bytesRead = read(inputFrame.fileFd, dstBuffer, fSize); + if (bytesRead < fSize) { + ALOGE("%s: Only %zd of %ld bytes read", __FUNCTION__, bytesRead, fSize); + return BAD_VALUE; + } + + close(inputFrame.fileFd); + inputFrame.fileFd = -1; + + // Fill in HEIC header + uint8_t *header = static_cast(dstBuffer) + mMaxHeicBufferSize - sizeof(CameraBlob); + struct CameraBlob *blobHeader = (struct CameraBlob *)header; + // Must be in sync with CAMERA3_HEIC_BLOB_ID in android_media_Utils.cpp + blobHeader->blobId = static_cast(0x00FE); + blobHeader->blobSize = fSize; + + res = native_window_set_buffers_timestamp(mOutputSurface.get(), timestamp); + if (res != OK) { + ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", + __FUNCTION__, getStreamId(), strerror(-res), res); + return res; + } + + res = outputANW->queueBuffer(mOutputSurface.get(), inputFrame.anb, /*fence*/ -1); + if (res != OK) { + ALOGE("%s: Failed to queueBuffer to Heic stream: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + inputFrame.anb = nullptr; + + return OK; +} + + +void HeicCompositeStream::releaseInputFrameLocked(InputFrame *inputFrame /*out*/) { + if (inputFrame == nullptr) { + return; + } + + if (inputFrame->appSegmentBuffer.data != nullptr) { + mAppSegmentConsumer->unlockBuffer(inputFrame->appSegmentBuffer); + inputFrame->appSegmentBuffer.data = nullptr; + mAppSegmentBufferAcquired = false; + } + + while (!inputFrame->codecOutputBuffers.empty()) { + auto it = inputFrame->codecOutputBuffers.begin(); + ALOGV("%s: releaseOutputBuffer index %d", __FUNCTION__, it->index); + mCodec->releaseOutputBuffer(it->index); + inputFrame->codecOutputBuffers.erase(it); + } + + if (inputFrame->yuvBuffer.data != nullptr) { + mMainImageConsumer->unlockBuffer(inputFrame->yuvBuffer); + inputFrame->yuvBuffer.data = nullptr; + mYuvBufferAcquired = false; + } + + while (!inputFrame->codecInputBuffers.empty()) { + auto it = inputFrame->codecInputBuffers.begin(); + inputFrame->codecInputBuffers.erase(it); + } + + if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) { + notifyError(inputFrame->frameNumber); + inputFrame->errorNotified = true; + } + + if (inputFrame->fileFd >= 0) { + close(inputFrame->fileFd); + inputFrame->fileFd = -1; + } + + if (inputFrame->anb != nullptr) { + sp outputANW = mOutputSurface; + outputANW->cancelBuffer(mOutputSurface.get(), inputFrame->anb, /*fence*/ -1); + inputFrame->anb = nullptr; + } +} + +void HeicCompositeStream::releaseInputFramesLocked(int64_t currentTs) { + auto it = mPendingInputFrames.begin(); + while (it != mPendingInputFrames.end()) { + if (it->first <= currentTs) { + releaseInputFrameLocked(&it->second); + it = mPendingInputFrames.erase(it); + } else { + it++; + } + } +} + +status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height, + const sp& cameraDevice) { + ALOGV("%s", __FUNCTION__); + + bool useGrid = false; + bool isSizeSupported = isSizeSupportedByHeifEncoder(width, height, + &mUseHeic, &useGrid, nullptr); + if (!isSizeSupported) { + ALOGE("%s: Encoder doesnt' support size %u x %u!", + __FUNCTION__, width, height); + return BAD_VALUE; + } + + // Create Looper for MediaCodec. + auto desiredMime = mUseHeic ? MIMETYPE_IMAGE_ANDROID_HEIC : MIMETYPE_VIDEO_HEVC; + mCodecLooper = new ALooper; + mCodecLooper->setName("Camera3-HeicComposite-MediaCodecLooper"); + status_t res = mCodecLooper->start( + false, // runOnCallingThread + false, // canCallJava + PRIORITY_AUDIO); + if (res != OK) { + ALOGE("%s: Failed to start codec looper: %s (%d)", + __FUNCTION__, strerror(-res), res); + return NO_INIT; + } + + // Create HEIC/HEVC codec. + mCodec = MediaCodec::CreateByType(mCodecLooper, desiredMime, true /*encoder*/); + if (mCodec == nullptr) { + ALOGE("%s: Failed to create codec for %s", __FUNCTION__, desiredMime); + return NO_INIT; + } + + // Create Looper and handler for Codec callback. + mCodecCallbackHandler = new CodecCallbackHandler(this); + if (mCodecCallbackHandler == nullptr) { + ALOGE("%s: Failed to create codec callback handler", __FUNCTION__); + return NO_MEMORY; + } + mCallbackLooper = new ALooper; + mCallbackLooper->setName("Camera3-HeicComposite-MediaCodecCallbackLooper"); + res = mCallbackLooper->start( + false, // runOnCallingThread + false, // canCallJava + PRIORITY_AUDIO); + if (res != OK) { + ALOGE("%s: Failed to start media callback looper: %s (%d)", + __FUNCTION__, strerror(-res), res); + return NO_INIT; + } + mCallbackLooper->registerHandler(mCodecCallbackHandler); + + mAsyncNotify = new AMessage(kWhatCallbackNotify, mCodecCallbackHandler); + res = mCodec->setCallback(mAsyncNotify); + if (res != OK) { + ALOGE("%s: Failed to set MediaCodec callback: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + // Create output format and configure the Codec. + sp outputFormat = new AMessage(); + outputFormat->setString(KEY_MIME, desiredMime); + outputFormat->setInt32(KEY_BITRATE_MODE, BITRATE_MODE_CQ); + outputFormat->setInt32(KEY_QUALITY, kDefaultJpegQuality); + // Ask codec to skip timestamp check and encode all frames. + outputFormat->setInt64("max-pts-gap-to-encoder", kNoFrameDropMaxPtsGap); + + int32_t gridWidth, gridHeight, gridRows, gridCols; + if (useGrid || mUseHeic) { + gridWidth = HeicEncoderInfoManager::kGridWidth; + gridHeight = HeicEncoderInfoManager::kGridHeight; + gridRows = (height + gridHeight - 1)/gridHeight; + gridCols = (width + gridWidth - 1)/gridWidth; + + if (mUseHeic) { + outputFormat->setInt32(KEY_TILE_WIDTH, gridWidth); + outputFormat->setInt32(KEY_TILE_HEIGHT, gridHeight); + outputFormat->setInt32(KEY_GRID_COLUMNS, gridCols); + outputFormat->setInt32(KEY_GRID_ROWS, gridRows); + } + + } else { + gridWidth = width; + gridHeight = height; + gridRows = 1; + gridCols = 1; + } + + outputFormat->setInt32(KEY_WIDTH, !useGrid ? width : gridWidth); + outputFormat->setInt32(KEY_HEIGHT, !useGrid ? height : gridHeight); + outputFormat->setInt32(KEY_I_FRAME_INTERVAL, 0); + outputFormat->setInt32(KEY_COLOR_FORMAT, + useGrid ? COLOR_FormatYUV420Flexible : COLOR_FormatSurface); + outputFormat->setInt32(KEY_FRAME_RATE, gridRows * gridCols); + // This only serves as a hint to encoder when encoding is not real-time. + outputFormat->setInt32(KEY_OPERATING_RATE, useGrid ? kGridOpRate : kNoGridOpRate); + + res = mCodec->configure(outputFormat, nullptr /*nativeWindow*/, + nullptr /*crypto*/, CONFIGURE_FLAG_ENCODE); + if (res != OK) { + ALOGE("%s: Failed to configure codec: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + mGridWidth = gridWidth; + mGridHeight = gridHeight; + mGridRows = gridRows; + mGridCols = gridCols; + mUseGrid = useGrid; + mOutputWidth = width; + mOutputHeight = height; + mAppSegmentMaxSize = calcAppSegmentMaxSize(cameraDevice->info()); + mMaxHeicBufferSize = mOutputWidth * mOutputHeight * 3 / 2 + mAppSegmentMaxSize; + + return OK; +} + +void HeicCompositeStream::deinitCodec() { + ALOGV("%s", __FUNCTION__); + if (mCodec != nullptr) { + mCodec->stop(); + mCodec->release(); + mCodec.clear(); + } + + if (mCodecLooper != nullptr) { + mCodecLooper->stop(); + mCodecLooper.clear(); + } + + if (mCallbackLooper != nullptr) { + mCallbackLooper->stop(); + mCallbackLooper.clear(); + } + + mAsyncNotify.clear(); + mFormat.clear(); +} + +// Return the size of the complete list of app segment, 0 indicates failure +size_t HeicCompositeStream::findAppSegmentsSize(const uint8_t* appSegmentBuffer, + size_t maxSize, size_t *app1SegmentSize) { + if (appSegmentBuffer == nullptr || app1SegmentSize == nullptr) { + ALOGE("%s: Invalid input appSegmentBuffer %p, app1SegmentSize %p", + __FUNCTION__, appSegmentBuffer, app1SegmentSize); + return 0; + } + + size_t expectedSize = 0; + // First check for EXIF transport header at the end of the buffer + const uint8_t *header = appSegmentBuffer + (maxSize - sizeof(struct CameraBlob)); + const struct CameraBlob *blob = (const struct CameraBlob*)(header); + if (blob->blobId != CameraBlobId::JPEG_APP_SEGMENTS) { + ALOGE("%s: Invalid EXIF blobId %hu", __FUNCTION__, blob->blobId); + return 0; + } + + expectedSize = blob->blobSize; + if (expectedSize == 0 || expectedSize > maxSize - sizeof(struct CameraBlob)) { + ALOGE("%s: Invalid blobSize %zu.", __FUNCTION__, expectedSize); + return 0; + } + + uint32_t totalSize = 0; + + // Verify APP1 marker (mandatory) + uint8_t app1Marker[] = {0xFF, 0xE1}; + if (memcmp(appSegmentBuffer, app1Marker, sizeof(app1Marker))) { + ALOGE("%s: Invalid APP1 marker: %x, %x", __FUNCTION__, + appSegmentBuffer[0], appSegmentBuffer[1]); + return 0; + } + totalSize += sizeof(app1Marker); + + uint16_t app1Size = (static_cast(appSegmentBuffer[totalSize]) << 8) + + appSegmentBuffer[totalSize+1]; + totalSize += app1Size; + + ALOGV("%s: Expected APP segments size %zu, APP1 segment size %u", + __FUNCTION__, expectedSize, app1Size); + while (totalSize < expectedSize) { + if (appSegmentBuffer[totalSize] != 0xFF || + appSegmentBuffer[totalSize+1] <= 0xE1 || + appSegmentBuffer[totalSize+1] > 0xEF) { + // Invalid APPn marker + ALOGE("%s: Invalid APPn marker: %x, %x", __FUNCTION__, + appSegmentBuffer[totalSize], appSegmentBuffer[totalSize+1]); + return 0; + } + totalSize += 2; + + uint16_t appnSize = (static_cast(appSegmentBuffer[totalSize]) << 8) + + appSegmentBuffer[totalSize+1]; + totalSize += appnSize; + } + + if (totalSize != expectedSize) { + ALOGE("%s: Invalid JPEG APP segments: totalSize %u vs expected size %zu", + __FUNCTION__, totalSize, expectedSize); + return 0; + } + + *app1SegmentSize = app1Size + sizeof(app1Marker); + return expectedSize; +} + +int64_t HeicCompositeStream::findTimestampInNsLocked(int64_t timeInUs) { + for (const auto& fn : mFrameNumberMap) { + if (timeInUs == ns2us(fn.second)) { + return fn.second; + } + } + for (const auto& inputFrame : mPendingInputFrames) { + if (timeInUs == ns2us(inputFrame.first)) { + return inputFrame.first; + } + } + return -1; +} + +status_t HeicCompositeStream::copyOneYuvTile(sp& codecBuffer, + const CpuConsumer::LockedBuffer& yuvBuffer, + size_t top, size_t left, size_t width, size_t height) { + ATRACE_CALL(); + + // Get stride information for codecBuffer + sp imageData; + if (!codecBuffer->meta()->findBuffer("image-data", &imageData)) { + ALOGE("%s: Codec input buffer is not for image data!", __FUNCTION__); + return BAD_VALUE; + } + if (imageData->size() != sizeof(MediaImage2)) { + ALOGE("%s: Invalid codec input image size %zu, expected %zu", + __FUNCTION__, imageData->size(), sizeof(MediaImage2)); + return BAD_VALUE; + } + MediaImage2* imageInfo = reinterpret_cast(imageData->data()); + if (imageInfo->mType != MediaImage2::MEDIA_IMAGE_TYPE_YUV || + imageInfo->mBitDepth != 8 || + imageInfo->mBitDepthAllocated != 8 || + imageInfo->mNumPlanes != 3) { + ALOGE("%s: Invalid codec input image info: mType %d, mBitDepth %d, " + "mBitDepthAllocated %d, mNumPlanes %d!", __FUNCTION__, + imageInfo->mType, imageInfo->mBitDepth, + imageInfo->mBitDepthAllocated, imageInfo->mNumPlanes); + return BAD_VALUE; + } + + ALOGV("%s: yuvBuffer chromaStep %d, chromaStride %d", + __FUNCTION__, yuvBuffer.chromaStep, yuvBuffer.chromaStride); + ALOGV("%s: U offset %u, V offset %u, U rowInc %d, V rowInc %d, U colInc %d, V colInc %d", + __FUNCTION__, imageInfo->mPlane[MediaImage2::U].mOffset, + imageInfo->mPlane[MediaImage2::V].mOffset, + imageInfo->mPlane[MediaImage2::U].mRowInc, + imageInfo->mPlane[MediaImage2::V].mRowInc, + imageInfo->mPlane[MediaImage2::U].mColInc, + imageInfo->mPlane[MediaImage2::V].mColInc); + + // Y + for (auto row = top; row < top+height; row++) { + uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::Y].mOffset + + imageInfo->mPlane[MediaImage2::Y].mRowInc * (row - top); + memcpy(dst, yuvBuffer.data+row*yuvBuffer.stride+left, width); + } + + // U is Cb, V is Cr + bool codecUPlaneFirst = imageInfo->mPlane[MediaImage2::V].mOffset > + imageInfo->mPlane[MediaImage2::U].mOffset; + uint32_t codecUvOffsetDiff = codecUPlaneFirst ? + imageInfo->mPlane[MediaImage2::V].mOffset - imageInfo->mPlane[MediaImage2::U].mOffset : + imageInfo->mPlane[MediaImage2::U].mOffset - imageInfo->mPlane[MediaImage2::V].mOffset; + bool isCodecUvSemiplannar = (codecUvOffsetDiff == 1) && + (imageInfo->mPlane[MediaImage2::U].mRowInc == + imageInfo->mPlane[MediaImage2::V].mRowInc) && + (imageInfo->mPlane[MediaImage2::U].mColInc == 2) && + (imageInfo->mPlane[MediaImage2::V].mColInc == 2); + bool isCodecUvPlannar = + ((codecUPlaneFirst && codecUvOffsetDiff >= + imageInfo->mPlane[MediaImage2::U].mRowInc * imageInfo->mHeight/2) || + ((!codecUPlaneFirst && codecUvOffsetDiff >= + imageInfo->mPlane[MediaImage2::V].mRowInc * imageInfo->mHeight/2))) && + imageInfo->mPlane[MediaImage2::U].mColInc == 1 && + imageInfo->mPlane[MediaImage2::V].mColInc == 1; + bool cameraUPlaneFirst = yuvBuffer.dataCr > yuvBuffer.dataCb; + + if (isCodecUvSemiplannar && yuvBuffer.chromaStep == 2 && + (codecUPlaneFirst == cameraUPlaneFirst)) { + // UV semiplannar + // The chrome plane could be either Cb first, or Cr first. Take the + // smaller address. + uint8_t *src = std::min(yuvBuffer.dataCb, yuvBuffer.dataCr); + MediaImage2::PlaneIndex dstPlane = codecUvOffsetDiff > 0 ? MediaImage2::U : MediaImage2::V; + for (auto row = top/2; row < (top+height)/2; row++) { + uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[dstPlane].mOffset + + imageInfo->mPlane[dstPlane].mRowInc * (row - top/2); + memcpy(dst, src+row*yuvBuffer.chromaStride+left, width); + } + } else if (isCodecUvPlannar && yuvBuffer.chromaStep == 1) { + // U plane + for (auto row = top/2; row < (top+height)/2; row++) { + uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::U].mOffset + + imageInfo->mPlane[MediaImage2::U].mRowInc * (row - top/2); + memcpy(dst, yuvBuffer.dataCb+row*yuvBuffer.chromaStride+left/2, width/2); + } + + // V plane + for (auto row = top/2; row < (top+height)/2; row++) { + uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::V].mOffset + + imageInfo->mPlane[MediaImage2::V].mRowInc * (row - top/2); + memcpy(dst, yuvBuffer.dataCr+row*yuvBuffer.chromaStride+left/2, width/2); + } + } else { + // Convert between semiplannar and plannar + uint8_t *dst = codecBuffer->data(); + for (auto row = top/2; row < (top+height)/2; row++) { + for (auto col = left/2; col < (left+width)/2; col++) { + // U/Cb + int32_t dstIndex = imageInfo->mPlane[MediaImage2::U].mOffset + + imageInfo->mPlane[MediaImage2::U].mRowInc * (row - top/2) + + imageInfo->mPlane[MediaImage2::U].mColInc * (col - left/2); + int32_t srcIndex = row * yuvBuffer.chromaStride + yuvBuffer.chromaStep * col; + dst[dstIndex] = yuvBuffer.dataCb[srcIndex]; + + // V/Cr + dstIndex = imageInfo->mPlane[MediaImage2::V].mOffset + + imageInfo->mPlane[MediaImage2::V].mRowInc * (row - top/2) + + imageInfo->mPlane[MediaImage2::V].mColInc * (col - left/2); + srcIndex = row * yuvBuffer.chromaStride + yuvBuffer.chromaStep * col; + dst[dstIndex] = yuvBuffer.dataCr[srcIndex]; + } + } + } + return OK; +} + +size_t HeicCompositeStream::calcAppSegmentMaxSize(const CameraMetadata& info) { + camera_metadata_ro_entry_t entry = info.find(ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT); + size_t maxAppsSegment = 1; + if (entry.count > 0) { + maxAppsSegment = entry.data.u8[0] < 1 ? 1 : + entry.data.u8[0] > 16 ? 16 : entry.data.u8[0]; + } + return maxAppsSegment * (2 + 0xFFFF) + sizeof(struct CameraBlob); +} + +bool HeicCompositeStream::threadLoop() { + int64_t currentTs = INT64_MAX; + bool newInputAvailable = false; + + { + Mutex::Autolock l(mMutex); + if (mErrorState) { + // In case we landed in error state, return any pending buffers and + // halt all further processing. + compilePendingInputLocked(); + releaseInputFramesLocked(currentTs); + return false; + } + + + while (!newInputAvailable) { + compilePendingInputLocked(); + newInputAvailable = getNextReadyInputLocked(¤tTs); + + if (!newInputAvailable) { + auto failingFrameNumber = getNextFailingInputLocked(¤tTs); + if (failingFrameNumber >= 0) { + // We cannot erase 'mPendingInputFrames[currentTs]' at this point because it is + // possible for two internal stream buffers to fail. In such scenario the + // composite stream should notify the client about a stream buffer error only + // once and this information is kept within 'errorNotified'. + // Any present failed input frames will be removed on a subsequent call to + // 'releaseInputFramesLocked()'. + releaseInputFrameLocked(&mPendingInputFrames[currentTs]); + currentTs = INT64_MAX; + } + + auto ret = mInputReadyCondition.waitRelative(mMutex, kWaitDuration); + if (ret == TIMED_OUT) { + return true; + } else if (ret != OK) { + ALOGE("%s: Timed wait on condition failed: %s (%d)", __FUNCTION__, + strerror(-ret), ret); + return false; + } + } + } + } + + auto res = processInputFrame(currentTs, mPendingInputFrames[currentTs]); + Mutex::Autolock l(mMutex); + if (res != OK) { + ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)", + __FUNCTION__, currentTs, strerror(-res), res); + mPendingInputFrames[currentTs].error = true; + } + + if (mPendingInputFrames[currentTs].error || + (mPendingInputFrames[currentTs].appSegmentWritten && + mPendingInputFrames[currentTs].pendingOutputTiles == 0)) { + releaseInputFramesLocked(currentTs); + } + + return true; +} + +bool HeicCompositeStream::onStreamBufferError(const CaptureResultExtras& resultExtras) { + bool res = false; + // Buffer errors concerning internal composite streams should not be directly visible to + // camera clients. They must only receive a single buffer error with the public composite + // stream id. + if ((resultExtras.errorStreamId == mAppSegmentStreamId) || + (resultExtras.errorStreamId == mMainImageStreamId)) { + flagAnErrorFrameNumber(resultExtras.frameNumber); + res = true; + } + + return res; +} + +void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp &msg) { + sp parent = mParent.promote(); + if (parent == nullptr) return; + + switch (msg->what()) { + case kWhatCallbackNotify: { + int32_t cbID; + if (!msg->findInt32("callbackID", &cbID)) { + ALOGE("kWhatCallbackNotify: callbackID is expected."); + break; + } + + ALOGV("kWhatCallbackNotify: cbID = %d", cbID); + + switch (cbID) { + case MediaCodec::CB_INPUT_AVAILABLE: { + int32_t index; + if (!msg->findInt32("index", &index)) { + ALOGE("CB_INPUT_AVAILABLE: index is expected."); + break; + } + parent->onHeicInputFrameAvailable(index); + break; + } + + case MediaCodec::CB_OUTPUT_AVAILABLE: { + int32_t index; + size_t offset; + size_t size; + int64_t timeUs; + int32_t flags; + + if (!msg->findInt32("index", &index)) { + ALOGE("CB_OUTPUT_AVAILABLE: index is expected."); + break; + } + if (!msg->findSize("offset", &offset)) { + ALOGE("CB_OUTPUT_AVAILABLE: offset is expected."); + break; + } + if (!msg->findSize("size", &size)) { + ALOGE("CB_OUTPUT_AVAILABLE: size is expected."); + break; + } + if (!msg->findInt64("timeUs", &timeUs)) { + ALOGE("CB_OUTPUT_AVAILABLE: timeUs is expected."); + break; + } + if (!msg->findInt32("flags", &flags)) { + ALOGE("CB_OUTPUT_AVAILABLE: flags is expected."); + break; + } + + CodecOutputBufferInfo bufferInfo = { + index, + (int32_t)offset, + (int32_t)size, + timeUs, + (uint32_t)flags}; + + parent->onHeicOutputFrameAvailable(bufferInfo); + break; + } + + case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: { + sp format; + if (!msg->findMessage("format", &format)) { + ALOGE("CB_OUTPUT_FORMAT_CHANGED: format is expected."); + break; + } + + parent->onHeicFormatChanged(format); + break; + } + + case MediaCodec::CB_ERROR: { + status_t err; + int32_t actionCode; + AString detail; + if (!msg->findInt32("err", &err)) { + ALOGE("CB_ERROR: err is expected."); + break; + } + if (!msg->findInt32("action", &actionCode)) { + ALOGE("CB_ERROR: action is expected."); + break; + } + msg->findString("detail", &detail); + ALOGE("Codec reported error(0x%x), actionCode(%d), detail(%s)", + err, actionCode, detail.c_str()); + + parent->onHeicCodecError(); + break; + } + + default: { + ALOGE("kWhatCallbackNotify: callbackID(%d) is unexpected.", cbID); + break; + } + } + break; + } + + default: + ALOGE("shouldn't be here"); + break; + } +} + +}; // namespace camera3 +}; // namespace android diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h new file mode 100644 index 0000000000..0a762566de --- /dev/null +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2019 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_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H +#define ANDROID_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "CompositeStream.h" + +namespace android { +namespace camera3 { + +class HeicCompositeStream : public CompositeStream, public Thread, + public CpuConsumer::FrameAvailableListener { +public: + HeicCompositeStream(wp device, + wp cb); + ~HeicCompositeStream() override; + + static bool isHeicCompositeStream(const sp &surface); + + status_t createInternalStreams(const std::vector>& consumers, + bool hasDeferredConsumer, uint32_t width, uint32_t height, int format, + camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId, + std::vector *surfaceIds, int streamSetId, bool isShared) override; + + status_t deleteInternalStreams() override; + + status_t configureStream() override; + + status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector* /*out*/outputStreamIds, + int32_t* /*out*/currentStreamId) override; + + void onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override; + + int getStreamId() override { return mMainImageStreamId; } + + // Use onShutter to keep track of frame number <-> timestamp mapping. + void onBufferReleased(const BufferInfo& bufferInfo) override; + void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& settings) override; + + // CpuConsumer listener implementation + void onFrameAvailable(const BufferItem& item) override; + + // Return stream information about the internal camera streams + static status_t getCompositeStreamInfo(const OutputStreamInfo &streamInfo, + const CameraMetadata& ch, std::vector* compositeOutput /*out*/); + + static bool isSizeSupportedByHeifEncoder(int32_t width, int32_t height, + bool* useHeic, bool* useGrid, int64_t* stall); + static bool isInMemoryTempFileSupported(); +protected: + + bool threadLoop() override; + bool onStreamBufferError(const CaptureResultExtras& resultExtras) override; + void onResultError(const CaptureResultExtras& /*resultExtras*/) override {} + +private: + // + // HEIC/HEVC Codec related structures, utility functions, and callbacks + // + struct CodecOutputBufferInfo { + int32_t index; + int32_t offset; + int32_t size; + int64_t timeUs; + uint32_t flags; + }; + + struct CodecInputBufferInfo { + int32_t index; + int64_t timeUs; + size_t tileIndex; + }; + + class CodecCallbackHandler : public AHandler { + public: + explicit CodecCallbackHandler(wp parent) { + mParent = parent; + } + virtual void onMessageReceived(const sp &msg); + private: + wp mParent; + }; + + enum { + kWhatCallbackNotify, + }; + + bool mUseHeic; + sp mCodec; + sp mCodecLooper, mCallbackLooper; + sp mCodecCallbackHandler; + sp mAsyncNotify; + sp mFormat; + size_t mNumOutputTiles; + + int32_t mOutputWidth, mOutputHeight; + size_t mMaxHeicBufferSize; + int32_t mGridWidth, mGridHeight; + size_t mGridRows, mGridCols; + bool mUseGrid; // Whether to use framework YUV frame tiling. + + static const int64_t kNoFrameDropMaxPtsGap = -1000000; + static const int32_t kNoGridOpRate = 30; + static const int32_t kGridOpRate = 120; + + void onHeicOutputFrameAvailable(const CodecOutputBufferInfo& bufferInfo); + void onHeicInputFrameAvailable(int32_t index); // Only called for YUV input mode. + void onHeicFormatChanged(sp& newFormat); + void onHeicCodecError(); + + status_t initializeCodec(uint32_t width, uint32_t height, + const sp& cameraDevice); + void deinitCodec(); + + // + // Composite stream related structures, utility functions and callbacks. + // + struct InputFrame { + int32_t orientation; + int32_t quality; + + CpuConsumer::LockedBuffer appSegmentBuffer; + std::vector codecOutputBuffers; + + // Fields that are only applicable to HEVC tiling. + CpuConsumer::LockedBuffer yuvBuffer; + std::vector codecInputBuffers; + + bool error; + bool errorNotified; + int64_t frameNumber; + + sp muxer; + int fenceFd; + int fileFd; + ssize_t trackIndex; + ANativeWindowBuffer *anb; + + bool appSegmentWritten; + size_t pendingOutputTiles; + size_t codecInputCounter; + + InputFrame() : orientation(0), quality(kDefaultJpegQuality), error(false), + errorNotified(false), frameNumber(-1), fenceFd(-1), fileFd(-1), + trackIndex(-1), anb(nullptr), appSegmentWritten(false), + pendingOutputTiles(0), codecInputCounter(0) { } + }; + + void compilePendingInputLocked(); + // Find first complete and valid frame with smallest timestamp + bool getNextReadyInputLocked(int64_t *currentTs /*out*/); + // Find next failing frame number with smallest timestamp and return respective frame number + int64_t getNextFailingInputLocked(int64_t *currentTs /*out*/); + + status_t processInputFrame(nsecs_t timestamp, InputFrame &inputFrame); + status_t processCodecInputFrame(InputFrame &inputFrame); + status_t startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame); + status_t processAppSegment(nsecs_t timestamp, InputFrame &inputFrame); + status_t processOneCodecOutputFrame(nsecs_t timestamp, InputFrame &inputFrame); + status_t processCompletedInputFrame(nsecs_t timestamp, InputFrame &inputFrame); + + void releaseInputFrameLocked(InputFrame *inputFrame /*out*/); + void releaseInputFramesLocked(int64_t currentTs); + + size_t findAppSegmentsSize(const uint8_t* appSegmentBuffer, size_t maxSize, + size_t* app1SegmentSize); + int64_t findTimestampInNsLocked(int64_t timeInUs); + status_t copyOneYuvTile(sp& codecBuffer, + const CpuConsumer::LockedBuffer& yuvBuffer, + size_t top, size_t left, size_t width, size_t height); + static size_t calcAppSegmentMaxSize(const CameraMetadata& info); + + static const nsecs_t kWaitDuration = 10000000; // 10 ms + static const int32_t kDefaultJpegQuality = 99; + static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF; + static const android_dataspace kAppSegmentDataSpace = + static_cast(HAL_DATASPACE_JPEG_APP_SEGMENTS); + static const android_dataspace kHeifDataSpace = + static_cast(HAL_DATASPACE_HEIF); + + int mAppSegmentStreamId, mAppSegmentSurfaceId; + sp mAppSegmentConsumer; + sp mAppSegmentSurface; + bool mAppSegmentBufferAcquired; + size_t mAppSegmentMaxSize; + + int mMainImageStreamId, mMainImageSurfaceId; + sp mMainImageSurface; + sp mMainImageConsumer; // Only applicable for HEVC codec. + bool mYuvBufferAcquired; // Only applicable to HEVC codec + + sp mOutputSurface; + sp mProducerListener; + + + // Map from frame number to JPEG setting of orientation+quality + std::map> mSettingsByFrameNumber; + // Map from timestamp to JPEG setting of orientation+quality + std::map> mSettingsByTimestamp; + + // Keep all incoming APP segment Blob buffer pending further processing. + std::vector mInputAppSegmentBuffers; + + // Keep all incoming HEIC blob buffer pending further processing. + std::vector mCodecOutputBuffers; + std::queue mCodecOutputBufferTimestamps; + size_t mOutputBufferCounter; + + // Keep all incoming Yuv buffer pending tiling and encoding (for HEVC YUV tiling only) + std::vector mInputYuvBuffers; + // Keep all codec input buffers ready to be filled out (for HEVC YUV tiling only) + std::vector mCodecInputBuffers; + + // Artificial strictly incremental YUV grid timestamp to make encoder happy. + int64_t mGridTimestampUs; + + // In most common use case, entries are accessed in order. + std::map mPendingInputFrames; +}; + +}; // namespace camera3 +}; // namespace android + +#endif //ANDROID_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp new file mode 100644 index 0000000000..ed9be6e934 --- /dev/null +++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2019 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 "HeicEncoderInfoManager" +//#define LOG_NDEBUG 0 + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "HeicEncoderInfoManager.h" + +namespace android { +namespace camera3 { + +HeicEncoderInfoManager::HeicEncoderInfoManager() : + mIsInited(false), + mMinSizeHeic(0, 0), + mMaxSizeHeic(INT32_MAX, INT32_MAX), + mHasHEVC(false), + mHasHEIC(false), + mDisableGrid(false) { + if (initialize() == OK) { + mIsInited = true; + } +} + +HeicEncoderInfoManager::~HeicEncoderInfoManager() { +} + +bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool* useHeic, + bool* useGrid, int64_t* stall) const { + if (useHeic == nullptr || useGrid == nullptr) { + ALOGE("%s: invalid parameters: useHeic %p, useGrid %p", + __FUNCTION__, useHeic, useGrid); + return false; + } + if (!mIsInited) return false; + + bool chooseHeic = false, enableGrid = true; + if (mHasHEIC && width >= mMinSizeHeic.first && + height >= mMinSizeHeic.second && width <= mMaxSizeHeic.first && + height <= mMaxSizeHeic.second) { + chooseHeic = true; + enableGrid = false; + } else if (mHasHEVC) { + bool fullSizeSupportedByHevc = (width >= mMinSizeHevc.first && + height >= mMinSizeHevc.second && + width <= mMaxSizeHevc.first && + height <= mMaxSizeHevc.second); + if (fullSizeSupportedByHevc && (mDisableGrid || + (width <= 1920 && height <= 1080))) { + enableGrid = false; + } + } else { + // No encoder available for the requested size. + return false; + } + + if (stall != nullptr) { + // Find preferred encoder which advertise + // "measured-frame-rate-WIDTHxHEIGHT-range" key. + const FrameRateMaps& maps = + (chooseHeic && mHeicFrameRateMaps.size() > 0) ? + mHeicFrameRateMaps : mHevcFrameRateMaps; + const auto& closestSize = findClosestSize(maps, width, height); + if (closestSize == maps.end()) { + // The "measured-frame-rate-WIDTHxHEIGHT-range" key is optional. + // Hardcode to some default value (3.33ms * tile count) based on resolution. + *stall = 3333333LL * width * height / (kGridWidth * kGridHeight); + return true; + } + + // Derive stall durations based on average fps of the closest size. + constexpr int64_t NSEC_PER_SEC = 1000000000LL; + int32_t avgFps = (closestSize->second.first + closestSize->second.second)/2; + float ratio = 1.0f * width * height / + (closestSize->first.first * closestSize->first.second); + *stall = ratio * NSEC_PER_SEC / avgFps; + } + + *useHeic = chooseHeic; + *useGrid = enableGrid; + return true; +} + +status_t HeicEncoderInfoManager::initialize() { + mDisableGrid = property_get_bool("camera.heic.disable_grid", false); + sp codecsList = MediaCodecList::getInstance(); + if (codecsList == nullptr) { + // No media codec available. + return OK; + } + + sp heicDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); + sp hevcDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC); + + if (hevcDetails == nullptr) { + if (heicDetails != nullptr) { + ALOGE("%s: Device must support HEVC codec if HEIC codec is available!", + __FUNCTION__); + return BAD_VALUE; + } + return OK; + } + + // Check CQ mode for HEVC codec + { + AString bitrateModes; + auto hasItem = hevcDetails->findString("feature-bitrate-modes", &bitrateModes); + if (!hasItem) { + ALOGE("%s: Failed to query bitrate modes for HEVC codec", __FUNCTION__); + return BAD_VALUE; + } + ALOGV("%s: HEVC codec's feature-bitrate-modes value is %d, %s", + __FUNCTION__, hasItem, bitrateModes.c_str()); + std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase); + if (!std::regex_search(bitrateModes.c_str(), pattern)) { + return OK; + } + } + + // HEIC size range + if (heicDetails != nullptr) { + auto res = getCodecSizeRange(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, + heicDetails, &mMinSizeHeic, &mMaxSizeHeic, &mHeicFrameRateMaps); + if (res != OK) { + ALOGE("%s: Failed to get HEIC codec size range: %s (%d)", __FUNCTION__, + strerror(-res), res); + return BAD_VALUE; + } + mHasHEIC = true; + } + + // HEVC size range + { + auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC, + hevcDetails, &mMinSizeHevc, &mMaxSizeHevc, &mHevcFrameRateMaps); + if (res != OK) { + ALOGE("%s: Failed to get HEVC codec size range: %s (%d)", __FUNCTION__, + strerror(-res), res); + return BAD_VALUE; + } + + mHasHEVC = true; + } + + return OK; +} + +status_t HeicEncoderInfoManager::getFrameRateMaps(sp details, FrameRateMaps* maps) { + if (details == nullptr || maps == nullptr) { + ALOGE("%s: Invalid input: details: %p, maps: %p", __FUNCTION__, details.get(), maps); + return BAD_VALUE; + } + + for (size_t i = 0; i < details->countEntries(); i++) { + AMessage::Type type; + const char* entryName = details->getEntryNameAt(i, &type); + if (type != AMessage::kTypeString) continue; + std::regex frameRateNamePattern("measured-frame-rate-([0-9]+)[*x]([0-9]+)-range", + std::regex_constants::icase); + std::cmatch sizeMatch; + if (std::regex_match(entryName, sizeMatch, frameRateNamePattern) && + sizeMatch.size() == 3) { + AMessage::ItemData item = details->getEntryAt(i); + AString fpsRangeStr; + if (item.find(&fpsRangeStr)) { + ALOGV("%s: %s", entryName, fpsRangeStr.c_str()); + std::regex frameRatePattern("([0-9]+)-([0-9]+)"); + std::cmatch fpsMatch; + if (std::regex_match(fpsRangeStr.c_str(), fpsMatch, frameRatePattern) && + fpsMatch.size() == 3) { + maps->emplace( + std::make_pair(stoi(sizeMatch[1]), stoi(sizeMatch[2])), + std::make_pair(stoi(fpsMatch[1]), stoi(fpsMatch[2]))); + } else { + return BAD_VALUE; + } + } + } + } + return OK; +} + +status_t HeicEncoderInfoManager::getCodecSizeRange( + const char* codecName, + sp details, + std::pair* minSize, + std::pair* maxSize, + FrameRateMaps* frameRateMaps) { + if (codecName == nullptr || minSize == nullptr || maxSize == nullptr || + details == nullptr || frameRateMaps == nullptr) { + return BAD_VALUE; + } + + AString sizeRange; + auto hasItem = details->findString("size-range", &sizeRange); + if (!hasItem) { + ALOGE("%s: Failed to query size range for codec %s", __FUNCTION__, codecName); + return BAD_VALUE; + } + ALOGV("%s: %s codec's size range is %s", __FUNCTION__, codecName, sizeRange.c_str()); + std::regex pattern("([0-9]+)[*x]([0-9]+)-([0-9]+)[*x]([0-9]+)"); + std::cmatch match; + if (std::regex_match(sizeRange.c_str(), match, pattern)) { + if (match.size() == 5) { + minSize->first = stoi(match[1]); + minSize->second = stoi(match[2]); + maxSize->first = stoi(match[3]); + maxSize->second = stoi(match[4]); + if (minSize->first > maxSize->first || + minSize->second > maxSize->second) { + ALOGE("%s: Invalid %s code size range: %s", + __FUNCTION__, codecName, sizeRange.c_str()); + return BAD_VALUE; + } + } else { + return BAD_VALUE; + } + } + + auto res = getFrameRateMaps(details, frameRateMaps); + if (res != OK) { + return res; + } + + return OK; +} + +HeicEncoderInfoManager::FrameRateMaps::const_iterator HeicEncoderInfoManager::findClosestSize( + const FrameRateMaps& maps, int32_t width, int32_t height) const { + int32_t minDiff = INT32_MAX; + FrameRateMaps::const_iterator closestIter = maps.begin(); + for (auto iter = maps.begin(); iter != maps.end(); iter++) { + // Use area difference between the sizes to approximate size + // difference. + int32_t diff = abs(iter->first.first * iter->first.second - width * height); + if (diff < minDiff) { + closestIter = iter; + minDiff = diff; + } + } + return closestIter; +} + +sp HeicEncoderInfoManager::getCodecDetails( + sp codecsList, const char* name) { + ssize_t idx = codecsList->findCodecByType(name, true /*encoder*/); + if (idx < 0) { + return nullptr; + } + + const sp info = codecsList->getCodecInfo(idx); + if (info == nullptr) { + ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, name); + return nullptr; + } + const sp caps = + info->getCapabilitiesFor(name); + if (caps == nullptr) { + ALOGE("%s: Failed to get capabilities for codec %s", __FUNCTION__, name); + return nullptr; + } + const sp details = caps->getDetails(); + if (details == nullptr) { + ALOGE("%s: Failed to get details for codec %s", __FUNCTION__, name); + return nullptr; + } + + return details; +} +} //namespace camera3 +} // namespace android diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h new file mode 100644 index 0000000000..fb0b9140a9 --- /dev/null +++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 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_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H +#define ANDROID_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H + +#include +#include +#include +#include + +#include +#include + +namespace android { +namespace camera3 { + +class HeicEncoderInfoManager { +public: + static HeicEncoderInfoManager& getInstance() { + static HeicEncoderInfoManager instance; + return instance; + } + + bool isSizeSupported(int32_t width, int32_t height, + bool* useHeic, bool* useGrid, int64_t* stall) const; + + static const auto kGridWidth = 512; + static const auto kGridHeight = 512; +private: + struct SizePairHash { + std::size_t operator () (const std::pair &p) const { + return p.first * 31 + p.second; + } + }; + + typedef std::unordered_map, + std::pair, SizePairHash> FrameRateMaps; + + HeicEncoderInfoManager(); + virtual ~HeicEncoderInfoManager(); + + status_t initialize(); + status_t getFrameRateMaps(sp details, FrameRateMaps* maps); + status_t getCodecSizeRange(const char* codecName, sp details, + std::pair* minSize, std::pair* maxSize, + FrameRateMaps* frameRateMaps); + FrameRateMaps::const_iterator findClosestSize(const FrameRateMaps& maps, + int32_t width, int32_t height) const; + sp getCodecDetails(sp codecsList, const char* name); + + bool mIsInited; + std::pair mMinSizeHeic, mMaxSizeHeic; + std::pair mMinSizeHevc, mMaxSizeHevc; + bool mHasHEVC, mHasHEIC; + FrameRateMaps mHeicFrameRateMaps, mHevcFrameRateMaps; + bool mDisableGrid; + +}; + +} // namespace camera3 +} // namespace android + +#endif // ANDROID_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index 8ee3298ffe..f35c66afe8 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -38,6 +38,8 @@ #include #include +#include "api2/HeicCompositeStream.h" + namespace android { using namespace ::android::hardware::camera; @@ -874,6 +876,130 @@ status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys( return res; } +status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fillHeicStreamCombinations( + std::vector* outputs, + std::vector* durations, + std::vector* stallDurations, + const camera_metadata_entry& halStreamConfigs, + const camera_metadata_entry& halStreamDurations) { + if (outputs == nullptr || durations == nullptr || stallDurations == nullptr) { + return BAD_VALUE; + } + + static bool supportInMemoryTempFile = + camera3::HeicCompositeStream::isInMemoryTempFileSupported(); + if (!supportInMemoryTempFile) { + ALOGI("%s: No HEIC support due to absence of in memory temp file support", + __FUNCTION__); + return OK; + } + + for (size_t i = 0; i < halStreamConfigs.count; i += 4) { + int32_t format = halStreamConfigs.data.i32[i]; + // Only IMPLEMENTATION_DEFINED and YUV_888 can be used to generate HEIC + // image. + if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED && + format != HAL_PIXEL_FORMAT_YCBCR_420_888) { + continue; + } + + bool sizeAvail = false; + for (size_t j = 0; j < outputs->size(); j+= 4) { + if ((*outputs)[j+1] == halStreamConfigs.data.i32[i+1] && + (*outputs)[j+2] == halStreamConfigs.data.i32[i+2]) { + sizeAvail = true; + break; + } + } + if (sizeAvail) continue; + + int64_t stall = 0; + bool useHeic, useGrid; + if (camera3::HeicCompositeStream::isSizeSupportedByHeifEncoder( + halStreamConfigs.data.i32[i+1], halStreamConfigs.data.i32[i+2], + &useHeic, &useGrid, &stall)) { + if (useGrid != (format == HAL_PIXEL_FORMAT_YCBCR_420_888)) { + continue; + } + + // HEIC configuration + int32_t config[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1], + halStreamConfigs.data.i32[i+2], 0 /*isInput*/}; + outputs->insert(outputs->end(), config, config + 4); + + // HEIC minFrameDuration + for (size_t j = 0; j < halStreamDurations.count; j += 4) { + if (halStreamDurations.data.i64[j] == format && + halStreamDurations.data.i64[j+1] == halStreamConfigs.data.i32[i+1] && + halStreamDurations.data.i64[j+2] == halStreamConfigs.data.i32[i+2]) { + int64_t duration[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1], + halStreamConfigs.data.i32[i+2], halStreamDurations.data.i64[j+3]}; + durations->insert(durations->end(), duration, duration+4); + break; + } + } + + // HEIC stallDuration + int64_t stallDuration[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1], + halStreamConfigs.data.i32[i+2], stall}; + stallDurations->insert(stallDurations->end(), stallDuration, stallDuration+4); + } + } + return OK; +} + +status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags() { + auto& c = mCameraCharacteristics; + + camera_metadata_entry halHeicSupport = c.find(ANDROID_HEIC_INFO_SUPPORTED); + if (halHeicSupport.count > 1) { + ALOGE("%s: Invalid entry count %zu for ANDROID_HEIC_INFO_SUPPORTED", + __FUNCTION__, halHeicSupport.count); + return BAD_VALUE; + } else if (halHeicSupport.count == 0 || + halHeicSupport.data.u8[0] == ANDROID_HEIC_INFO_SUPPORTED_FALSE) { + // Camera HAL doesn't support mandatory stream combinations for HEIC. + return OK; + } + + camera_metadata_entry maxJpegAppsSegments = + c.find(ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT); + if (maxJpegAppsSegments.count != 1 || maxJpegAppsSegments.data.u8[0] == 0 || + maxJpegAppsSegments.data.u8[0] > 16) { + ALOGE("%s: ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT must be within [1, 16]", + __FUNCTION__); + return BAD_VALUE; + } + + // Populate HEIC output configurations and its related min frame duration + // and stall duration. + std::vector heicOutputs; + std::vector heicDurations; + std::vector heicStallDurations; + + camera_metadata_entry halStreamConfigs = + c.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + camera_metadata_entry minFrameDurations = + c.find(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS); + + status_t res = fillHeicStreamCombinations(&heicOutputs, &heicDurations, &heicStallDurations, + halStreamConfigs, minFrameDurations); + if (res != OK) { + ALOGE("%s: Failed to fill HEIC stream combinations: %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + c.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, + heicOutputs.data(), heicOutputs.size()); + c.update(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, + heicDurations.data(), heicDurations.size()); + c.update(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, + heicStallDurations.data(), heicStallDurations.size()); + + return OK; +} + bool CameraProviderManager::isLogicalCamera(const std::string& id, std::vector* physicalCameraIds) { std::lock_guard lock(mInterfaceMutex); @@ -1738,6 +1864,12 @@ CameraProviderManager::ProviderInfo::DeviceInfo3::DeviceInfo3(const std::string& ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-stat), stat); } + res = deriveHeicTags(); + if (OK != res) { + ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities: %s (%d)", + __FUNCTION__, strerror(-res), res); + } + camera_metadata_entry flashAvailable = mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE); if (flashAvailable.count == 1 && diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h index 18869f53f6..3173eda1ff 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.h +++ b/services/camera/libcameraservice/common/CameraProviderManager.h @@ -494,6 +494,12 @@ private: std::vector> *internalDepthSizes /*out*/); status_t removeAvailableKeys(CameraMetadata& c, const std::vector& keys, uint32_t keyTag); + status_t fillHeicStreamCombinations(std::vector* outputs, + std::vector* durations, + std::vector* stallDurations, + const camera_metadata_entry& halStreamConfigs, + const camera_metadata_entry& halStreamDurations); + status_t deriveHeicTags(); }; private: diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 82dfc0fba9..918dcf775d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1757,16 +1757,18 @@ status_t Camera3Device::createStream(const std::vector>& consumers, if (format == HAL_PIXEL_FORMAT_BLOB) { ssize_t blobBufferSize; - if (dataSpace != HAL_DATASPACE_DEPTH) { - blobBufferSize = getJpegBufferSize(width, height); + if (dataSpace == HAL_DATASPACE_DEPTH) { + blobBufferSize = getPointCloudBufferSize(); if (blobBufferSize <= 0) { - SET_ERR_L("Invalid jpeg buffer size %zd", blobBufferSize); + SET_ERR_L("Invalid point cloud buffer size %zd", blobBufferSize); return BAD_VALUE; } + } else if (dataSpace == static_cast(HAL_DATASPACE_JPEG_APP_SEGMENTS)) { + blobBufferSize = width * height; } else { - blobBufferSize = getPointCloudBufferSize(); + blobBufferSize = getJpegBufferSize(width, height); if (blobBufferSize <= 0) { - SET_ERR_L("Invalid point cloud buffer size %zd", blobBufferSize); + SET_ERR_L("Invalid jpeg buffer size %zd", blobBufferSize); return BAD_VALUE; } } @@ -5473,8 +5475,22 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { return TIMED_OUT; } } - outputStream->fireBufferRequestForFrameNumber( - captureRequest->mResultExtras.frameNumber); + + { + sp parent = mParent.promote(); + if (parent != nullptr) { + const String8& streamCameraId = outputStream->getPhysicalCameraId(); + for (const auto& settings : captureRequest->mSettingsList) { + if ((streamCameraId.isEmpty() && + parent->getId() == settings.cameraId.c_str()) || + streamCameraId == settings.cameraId.c_str()) { + outputStream->fireBufferRequestForFrameNumber( + captureRequest->mResultExtras.frameNumber, + settings.metadata); + } + } + } + } String8 physicalCameraId = outputStream->getPhysicalCameraId(); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index b296513245..d29e5c083d 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -763,14 +763,15 @@ status_t Camera3Stream::getInputBufferProducer(sp *produ return getInputBufferProducerLocked(producer); } -void Camera3Stream::fireBufferRequestForFrameNumber(uint64_t frameNumber) { +void Camera3Stream::fireBufferRequestForFrameNumber(uint64_t frameNumber, + const CameraMetadata& settings) { ATRACE_CALL(); Mutex::Autolock l(mLock); for (auto &it : mBufferListenerList) { sp listener = it.promote(); if (listener.get() != nullptr) { - listener->onBufferRequestForFrameNumber(frameNumber, getId()); + listener->onBufferRequestForFrameNumber(frameNumber, getId(), settings); } } } diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 06deba9fa9..5eb6a23c15 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -434,7 +434,8 @@ class Camera3Stream : /** * Notify buffer stream listeners about incoming request with particular frame number. */ - void fireBufferRequestForFrameNumber(uint64_t frameNumber) override; + void fireBufferRequestForFrameNumber(uint64_t frameNumber, + const CameraMetadata& settings) override; protected: const int mId; diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h index 0e6104e7ff..d0aee2710d 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h +++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H #define ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H +#include #include #include @@ -42,7 +43,8 @@ public: // Buffer was released by the HAL virtual void onBufferReleased(const BufferInfo& bufferInfo) = 0; // Notify about incoming buffer request frame number - virtual void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) = 0; + virtual void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, + const CameraMetadata& settings) = 0; }; }; //namespace camera3 diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index 7b80cbd687..5cd11b7c70 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -18,6 +18,8 @@ #define ANDROID_SERVERS_CAMERA3_STREAM_INTERFACE_H #include + +#include #include "Camera3StreamBufferListener.h" #include "Camera3StreamBufferFreedListener.h" @@ -346,7 +348,8 @@ class Camera3StreamInterface : public virtual RefBase { /** * Notify buffer stream listeners about incoming request with particular frame number. */ - virtual void fireBufferRequestForFrameNumber(uint64_t frameNumber) = 0; + virtual void fireBufferRequestForFrameNumber(uint64_t frameNumber, + const CameraMetadata& settings) = 0; }; } // namespace camera3 diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp new file mode 100644 index 0000000000..a4027cc4bc --- /dev/null +++ b/services/camera/libcameraservice/utils/ExifUtils.cpp @@ -0,0 +1,1046 @@ +/* + * Copyright (C) 2019 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 "CameraServerExifUtils" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include + +#include "ExifUtils.h" + +extern "C" { +#include +} + +namespace std { + +template <> +struct default_delete { + inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); } +}; + +} // namespace std + + +namespace android { +namespace camera3 { + + +class ExifUtilsImpl : public ExifUtils { +public: + ExifUtilsImpl(); + + virtual ~ExifUtilsImpl(); + + // Initialize() can be called multiple times. The setting of Exif tags will be + // cleared. + virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize); + + // set all known fields from a metadata structure + virtual bool setFromMetadata(const CameraMetadata& metadata, + const size_t imageWidth, + const size_t imageHeight); + + // sets the len aperture. + // Returns false if memory allocation fails. + virtual bool setAperture(uint32_t numerator, uint32_t denominator); + + // sets the value of brightness. + // Returns false if memory allocation fails. + virtual bool setBrightness(int32_t numerator, int32_t denominator); + + // sets the color space. + // Returns false if memory allocation fails. + virtual bool setColorSpace(uint16_t color_space); + + // sets the information to compressed data. + // Returns false if memory allocation fails. + virtual bool setComponentsConfiguration(const std::string& components_configuration); + + // sets the compression scheme used for the image data. + // Returns false if memory allocation fails. + virtual bool setCompression(uint16_t compression); + + // sets image contrast. + // Returns false if memory allocation fails. + virtual bool setContrast(uint16_t contrast); + + // sets the date and time of image last modified. It takes local time. The + // name of the tag is DateTime in IFD0. + // Returns false if memory allocation fails. + virtual bool setDateTime(const struct tm& t); + + // sets the image description. + // Returns false if memory allocation fails. + virtual bool setDescription(const std::string& description); + + // sets the digital zoom ratio. If the numerator is 0, it means digital zoom + // was not used. + // Returns false if memory allocation fails. + virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator); + + // sets the exposure bias. + // Returns false if memory allocation fails. + virtual bool setExposureBias(int32_t numerator, int32_t denominator); + + // sets the exposure mode set when the image was shot. + // Returns false if memory allocation fails. + virtual bool setExposureMode(uint16_t exposure_mode); + + // sets the program used by the camera to set exposure when the picture is + // taken. + // Returns false if memory allocation fails. + virtual bool setExposureProgram(uint16_t exposure_program); + + // sets the exposure time, given in seconds. + // Returns false if memory allocation fails. + virtual bool setExposureTime(uint32_t numerator, uint32_t denominator); + + // sets the status of flash. + // Returns false if memory allocation fails. + virtual bool setFlash(uint16_t flash); + + // sets the F number. + // Returns false if memory allocation fails. + virtual bool setFNumber(uint32_t numerator, uint32_t denominator); + + // sets the focal length of lens used to take the image in millimeters. + // Returns false if memory allocation fails. + virtual bool setFocalLength(uint32_t numerator, uint32_t denominator); + + // sets the degree of overall image gain adjustment. + // Returns false if memory allocation fails. + virtual bool setGainControl(uint16_t gain_control); + + // sets the altitude in meters. + // Returns false if memory allocation fails. + virtual bool setGpsAltitude(double altitude); + + // sets the latitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + virtual bool setGpsLatitude(double latitude); + + // sets the longitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + virtual bool setGpsLongitude(double longitude); + + // sets GPS processing method. + // Returns false if memory allocation fails. + virtual bool setGpsProcessingMethod(const std::string& method); + + // sets GPS date stamp and time stamp (atomic clock). It takes UTC time. + // Returns false if memory allocation fails. + virtual bool setGpsTimestamp(const struct tm& t); + + // sets the length (number of rows) of main image. + // Returns false if memory allocation fails. + virtual bool setImageHeight(uint32_t length); + + // sets the width (number of columes) of main image. + // Returns false if memory allocation fails. + virtual bool setImageWidth(uint32_t width); + + // sets the ISO speed. + // Returns false if memory allocation fails. + virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings); + + // sets the kind of light source. + // Returns false if memory allocation fails. + virtual bool setLightSource(uint16_t light_source); + + // sets the smallest F number of the lens. + // Returns false if memory allocation fails. + virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator); + + // sets the metering mode. + // Returns false if memory allocation fails. + virtual bool setMeteringMode(uint16_t metering_mode); + + // sets image orientation. + // Returns false if memory allocation fails. + virtual bool setOrientation(uint16_t orientation); + + // sets the unit for measuring XResolution and YResolution. + // Returns false if memory allocation fails. + virtual bool setResolutionUnit(uint16_t resolution_unit); + + // sets image saturation. + // Returns false if memory allocation fails. + virtual bool setSaturation(uint16_t saturation); + + // sets the type of scene that was shot. + // Returns false if memory allocation fails. + virtual bool setSceneCaptureType(uint16_t type); + + // sets image sharpness. + // Returns false if memory allocation fails. + virtual bool setSharpness(uint16_t sharpness); + + // sets the shutter speed. + // Returns false if memory allocation fails. + virtual bool setShutterSpeed(int32_t numerator, int32_t denominator); + + // sets the distance to the subject, given in meters. + // Returns false if memory allocation fails. + virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator); + + // sets the fractions of seconds for the tag. + // Returns false if memory allocation fails. + virtual bool setSubsecTime(const std::string& subsec_time); + + // sets the white balance mode set when the image was shot. + // Returns false if memory allocation fails. + virtual bool setWhiteBalance(uint16_t white_balance); + + // sets the number of pixels per resolution unit in the image width. + // Returns false if memory allocation fails. + virtual bool setXResolution(uint32_t numerator, uint32_t denominator); + + // sets the position of chrominance components in relation to the luminance + // component. + // Returns false if memory allocation fails. + virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning); + + // sets the number of pixels per resolution unit in the image length. + // Returns false if memory allocation fails. + virtual bool setYResolution(uint32_t numerator, uint32_t denominator); + + // sets the manufacturer of camera. + // Returns false if memory allocation fails. + virtual bool setMake(const std::string& make); + + // sets the model number of camera. + // Returns false if memory allocation fails. + virtual bool setModel(const std::string& model); + + // Generates APP1 segment. + // Returns false if generating APP1 segment fails. + virtual bool generateApp1(); + + // Gets buffer of APP1 segment. This method must be called only after calling + // GenerateAPP1(). + virtual const uint8_t* getApp1Buffer(); + + // Gets length of APP1 segment. This method must be called only after calling + // GenerateAPP1(). + virtual unsigned int getApp1Length(); + + protected: + // sets the version of this standard supported. + // Returns false if memory allocation fails. + virtual bool setExifVersion(const std::string& exif_version); + + // Resets the pointers and memories. + virtual void reset(); + + // Adds a variable length tag to |exif_data_|. It will remove the original one + // if the tag exists. + // Returns the entry of the tag. The reference count of returned ExifEntry is + // two. + virtual std::unique_ptr addVariableLengthEntry(ExifIfd ifd, + ExifTag tag, ExifFormat format, uint64_t components, unsigned int size); + + // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if + // the tag exists. + // Returns the entry of the tag. It adds one reference count to returned + // ExifEntry. + virtual std::unique_ptr addEntry(ExifIfd ifd, ExifTag tag); + + // Helpe functions to add exif data with different types. + virtual bool setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg); + + virtual bool setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg); + + virtual bool setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, + uint32_t denominator, const std::string& msg); + + virtual bool setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, + int32_t denominator, const std::string& msg); + + virtual bool setString(ExifIfd ifd, ExifTag tag, ExifFormat format, + const std::string& buffer, const std::string& msg); + + // Destroys the buffer of APP1 segment if exists. + virtual void destroyApp1(); + + // The Exif data (APP1). Owned by this class. + ExifData* exif_data_; + // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but + // owned by this class. + uint8_t* app1_buffer_; + // The length of |app1_buffer_|. + unsigned int app1_length_; + +}; + +#define SET_SHORT(ifd, tag, value) \ + do { \ + if (setShort(ifd, tag, value, #tag) == false) \ + return false; \ + } while (0); + +#define SET_LONG(ifd, tag, value) \ + do { \ + if (setLong(ifd, tag, value, #tag) == false) \ + return false; \ + } while (0); + +#define SET_RATIONAL(ifd, tag, numerator, denominator) \ + do { \ + if (setRational(ifd, tag, numerator, denominator, #tag) == false) \ + return false; \ + } while (0); + +#define SET_SRATIONAL(ifd, tag, numerator, denominator) \ + do { \ + if (setSRational(ifd, tag, numerator, denominator, #tag) == false) \ + return false; \ + } while (0); + +#define SET_STRING(ifd, tag, format, buffer) \ + do { \ + if (setString(ifd, tag, format, buffer, #tag) == false) \ + return false; \ + } while (0); + +// This comes from the Exif Version 2.2 standard table 6. +const char gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0}; + +static void setLatitudeOrLongitudeData(unsigned char* data, double num) { + // Take the integer part of |num|. + ExifLong degrees = static_cast(num); + ExifLong minutes = static_cast(60 * (num - degrees)); + ExifLong microseconds = + static_cast(3600000000u * (num - degrees - minutes / 60.0)); + exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1}); + exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, {minutes, 1}); + exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {microseconds, 1000000}); +} + +ExifUtils *ExifUtils::create() { + return new ExifUtilsImpl(); +} + +ExifUtils::~ExifUtils() { +} + +ExifUtilsImpl::ExifUtilsImpl() + : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {} + +ExifUtilsImpl::~ExifUtilsImpl() { + reset(); +} + + +bool ExifUtilsImpl::initialize(const unsigned char *app1Segment, size_t app1SegmentSize) { + reset(); + exif_data_ = exif_data_new_from_data(app1Segment, app1SegmentSize); + if (exif_data_ == nullptr) { + ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__); + return false; + } + // set the image options. + exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); + exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED); + exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL); + + // set exif version to 2.2. + if (!setExifVersion("0220")) { + return false; + } + + return true; +} + +bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) { + SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setColorSpace(uint16_t color_space) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE, color_space); + return true; +} + +bool ExifUtilsImpl::setComponentsConfiguration( + const std::string& components_configuration) { + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION, + EXIF_FORMAT_UNDEFINED, components_configuration); + return true; +} + +bool ExifUtilsImpl::setCompression(uint16_t compression) { + SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression); + return true; +} + +bool ExifUtilsImpl::setContrast(uint16_t contrast) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast); + return true; +} + +bool ExifUtilsImpl::setDateTime(const struct tm& t) { + // The length is 20 bytes including NULL for termination in Exif standard. + char str[20]; + int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + if (result != sizeof(str) - 1) { + ALOGW("%s: Input time is invalid", __FUNCTION__); + return false; + } + std::string buffer(str); + SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer); + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, buffer); + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, buffer); + return true; +} + +bool ExifUtilsImpl::setDescription(const std::string& description) { + SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII, description); + return true; +} + +bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) { + SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode); + return true; +} + +bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program); + return true; +} + +bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setFlash(uint16_t flash) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash); + return true; +} + +bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setGainControl(uint16_t gain_control) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control); + return true; +} + +bool ExifUtilsImpl::setGpsAltitude(double altitude) { + ExifTag refTag = static_cast(EXIF_TAG_GPS_ALTITUDE_REF); + std::unique_ptr refEntry = + addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1); + if (!refEntry) { + ALOGE("%s: Adding GPSAltitudeRef exif entry failed", __FUNCTION__); + return false; + } + if (altitude >= 0) { + *refEntry->data = 0; + } else { + *refEntry->data = 1; + altitude *= -1; + } + + ExifTag tag = static_cast(EXIF_TAG_GPS_ALTITUDE); + std::unique_ptr entry = addVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + ALOGE("%s: Adding GPSAltitude exif entry failed", __FUNCTION__); + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, + {static_cast(altitude * 1000), 1000}); + + return true; +} + +bool ExifUtilsImpl::setGpsLatitude(double latitude) { + const ExifTag refTag = static_cast(EXIF_TAG_GPS_LATITUDE_REF); + std::unique_ptr refEntry = + addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2); + if (!refEntry) { + ALOGE("%s: Adding GPSLatitudeRef exif entry failed", __FUNCTION__); + return false; + } + if (latitude >= 0) { + memcpy(refEntry->data, "N", sizeof("N")); + } else { + memcpy(refEntry->data, "S", sizeof("S")); + latitude *= -1; + } + + const ExifTag tag = static_cast(EXIF_TAG_GPS_LATITUDE); + std::unique_ptr entry = addVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + ALOGE("%s: Adding GPSLatitude exif entry failed", __FUNCTION__); + return false; + } + setLatitudeOrLongitudeData(entry->data, latitude); + + return true; +} + +bool ExifUtilsImpl::setGpsLongitude(double longitude) { + ExifTag refTag = static_cast(EXIF_TAG_GPS_LONGITUDE_REF); + std::unique_ptr refEntry = + addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2); + if (!refEntry) { + ALOGE("%s: Adding GPSLongitudeRef exif entry failed", __FUNCTION__); + return false; + } + if (longitude >= 0) { + memcpy(refEntry->data, "E", sizeof("E")); + } else { + memcpy(refEntry->data, "W", sizeof("W")); + longitude *= -1; + } + + ExifTag tag = static_cast(EXIF_TAG_GPS_LONGITUDE); + std::unique_ptr entry = addVariableLengthEntry( + EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational)); + if (!entry) { + exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get()); + ALOGE("%s: Adding GPSLongitude exif entry failed", __FUNCTION__); + return false; + } + setLatitudeOrLongitudeData(entry->data, longitude); + + return true; +} + +bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) { + std::string buffer = + std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method; + SET_STRING(EXIF_IFD_GPS, static_cast(EXIF_TAG_GPS_PROCESSING_METHOD), + EXIF_FORMAT_UNDEFINED, buffer); + return true; +} + +bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) { + const ExifTag dateTag = static_cast(EXIF_TAG_GPS_DATE_STAMP); + const size_t kGpsDateStampSize = 11; + std::unique_ptr entry = addVariableLengthEntry(EXIF_IFD_GPS, + dateTag, EXIF_FORMAT_ASCII, kGpsDateStampSize, kGpsDateStampSize); + if (!entry) { + ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__); + return false; + } + int result = snprintf(reinterpret_cast(entry->data), kGpsDateStampSize, + "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday); + if (result != kGpsDateStampSize - 1) { + ALOGW("%s: Input time is invalid", __FUNCTION__); + return false; + } + + const ExifTag timeTag = static_cast(EXIF_TAG_GPS_TIME_STAMP); + entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3, + 3 * sizeof(ExifRational)); + if (!entry) { + ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__); + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, + {static_cast(t.tm_hour), 1}); + exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {static_cast(t.tm_min), 1}); + exif_set_rational(entry->data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, + {static_cast(t.tm_sec), 1}); + + return true; +} + +bool ExifUtilsImpl::setImageHeight(uint32_t length) { + SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, length); + SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, length); + return true; +} + +bool ExifUtilsImpl::setImageWidth(uint32_t width) { + SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, width); + SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width); + return true; +} + +bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso_speed_ratings); + return true; +} + +bool ExifUtilsImpl::setLightSource(uint16_t light_source) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source); + return true; +} + +bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode); + return true; +} + +bool ExifUtilsImpl::setOrientation(uint16_t orientation) { + /* + * Orientation value: + * 1 2 3 4 5 6 7 8 + * + * 888888 888888 88 88 8888888888 88 88 8888888888 + * 88 88 88 88 88 88 88 88 88 88 88 88 + * 8888 8888 8888 8888 88 8888888888 8888888888 88 + * 88 88 88 88 + * 88 88 888888 888888 + */ + int value = 1; + switch (orientation) { + case 90: + value = 6; + break; + case 180: + value = 3; + break; + case 270: + value = 8; + break; + default: + break; + } + SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value); + return true; +} + +bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit); + return true; +} + +bool ExifUtilsImpl::setSaturation(uint16_t saturation) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation); + return true; +} + +bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type); + return true; +} + +bool ExifUtilsImpl::setSharpness(uint16_t sharpness) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness); + return true; +} + +bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) { + SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) { + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII, subsec_time); + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII, subsec_time); + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII, subsec_time); + return true; +} + +bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) { + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance); + return true; +} + +bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) { + SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning); + return true; +} + +bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator); + return true; +} + +bool ExifUtilsImpl::generateApp1() { + destroyApp1(); + // Save the result into |app1_buffer_|. + exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_); + if (!app1_length_) { + ALOGE("%s: Allocate memory for app1_buffer_ failed", __FUNCTION__); + return false; + } + /* + * The JPEG segment size is 16 bits in spec. The size of APP1 segment should + * be smaller than 65533 because there are two bytes for segment size field. + */ + if (app1_length_ > 65533) { + destroyApp1(); + ALOGE("%s: The size of APP1 segment is too large", __FUNCTION__); + return false; + } + return true; +} + +const uint8_t* ExifUtilsImpl::getApp1Buffer() { + return app1_buffer_; +} + +unsigned int ExifUtilsImpl::getApp1Length() { + return app1_length_; +} + +bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) { + SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, exif_version); + return true; +} + +bool ExifUtilsImpl::setMake(const std::string& make) { + SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make); + return true; +} + +bool ExifUtilsImpl::setModel(const std::string& model) { + SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model); + return true; +} + +void ExifUtilsImpl::reset() { + destroyApp1(); + if (exif_data_) { + /* + * Since we decided to ignore the original APP1, we are sure that there is + * no thumbnail allocated by libexif. |exif_data_->data| is actually + * allocated by JpegCompressor. sets |exif_data_->data| to nullptr to + * prevent exif_data_unref() destroy it incorrectly. + */ + exif_data_->data = nullptr; + exif_data_->size = 0; + exif_data_unref(exif_data_); + exif_data_ = nullptr; + } +} + +std::unique_ptr ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd, + ExifTag tag, ExifFormat format, uint64_t components, unsigned int size) { + // Remove old entry if exists. + exif_content_remove_entry(exif_data_->ifd[ifd], + exif_content_get_entry(exif_data_->ifd[ifd], tag)); + ExifMem* mem = exif_mem_new_default(); + if (!mem) { + ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__); + return nullptr; + } + std::unique_ptr entry(exif_entry_new_mem(mem)); + if (!entry) { + ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__); + exif_mem_unref(mem); + return nullptr; + } + void* tmpBuffer = exif_mem_alloc(mem, size); + if (!tmpBuffer) { + ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__); + exif_mem_unref(mem); + return nullptr; + } + + entry->data = static_cast(tmpBuffer); + entry->tag = tag; + entry->format = format; + entry->components = components; + entry->size = size; + + exif_content_add_entry(exif_data_->ifd[ifd], entry.get()); + exif_mem_unref(mem); + + return entry; +} + +std::unique_ptr ExifUtilsImpl::addEntry(ExifIfd ifd, ExifTag tag) { + std::unique_ptr entry(exif_content_get_entry(exif_data_->ifd[ifd], tag)); + if (entry) { + // exif_content_get_entry() won't ref the entry, so we ref here. + exif_entry_ref(entry.get()); + return entry; + } + entry.reset(exif_entry_new()); + if (!entry) { + ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__); + return nullptr; + } + entry->tag = tag; + exif_content_add_entry(exif_data_->ifd[ifd], entry.get()); + exif_entry_initialize(entry.get(), tag); + return entry; +} + +bool ExifUtilsImpl::setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg) { + std::unique_ptr entry = addEntry(ifd, tag); + if (!entry) { + ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str()); + return false; + } + exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value); + return true; +} + +bool ExifUtilsImpl::setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg) { + std::unique_ptr entry = addEntry(ifd, tag); + if (!entry) { + ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str()); + return false; + } + exif_set_long(entry->data, EXIF_BYTE_ORDER_INTEL, value); + return true; +} + +bool ExifUtilsImpl::setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, + uint32_t denominator, const std::string& msg) { + std::unique_ptr entry = addEntry(ifd, tag); + if (!entry) { + ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str()); + return false; + } + exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator}); + return true; +} + +bool ExifUtilsImpl::setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, + int32_t denominator, const std::string& msg) { + std::unique_ptr entry = addEntry(ifd, tag); + if (!entry) { + ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str()); + return false; + } + exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator}); + return true; +} + +bool ExifUtilsImpl::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, + const std::string& buffer, const std::string& msg) { + size_t entry_size = buffer.length(); + // Since the exif format is undefined, NULL termination is not necessary. + if (format == EXIF_FORMAT_ASCII) { + entry_size++; + } + std::unique_ptr entry = + addVariableLengthEntry(ifd, tag, format, entry_size, entry_size); + if (!entry) { + ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str()); + return false; + } + memcpy(entry->data, buffer.c_str(), entry_size); + return true; +} + +void ExifUtilsImpl::destroyApp1() { + /* + * Since there is no API to access ExifMem in ExifData->priv, we use free + * here, which is the default free function in libexif. See + * exif_data_save_data() for detail. + */ + free(app1_buffer_); + app1_buffer_ = nullptr; + app1_length_ = 0; +} + +bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, + const size_t imageWidth, const size_t imageHeight) { + // How precise the float-to-rational conversion for EXIF tags would be. + constexpr int kRationalPrecision = 10000; + if (!setImageWidth(imageWidth) || + !setImageHeight(imageHeight)) { + ALOGE("%s: setting image resolution failed.", __FUNCTION__); + return false; + } + + struct timespec tp; + struct tm time_info; + bool time_available = clock_gettime(CLOCK_REALTIME, &tp) != -1; + localtime_r(&tp.tv_sec, &time_info); + if (!setDateTime(time_info)) { + ALOGE("%s: setting data time failed.", __FUNCTION__); + return false; + } + + float focal_length; + camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH); + if (entry.count) { + focal_length = entry.data.f[0]; + + if (!setFocalLength( + static_cast(focal_length * kRationalPrecision), kRationalPrecision)) { + ALOGE("%s: setting focal length failed.", __FUNCTION__); + return false; + } + } else { + ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__); + } + + if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) { + entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES); + if (entry.count < 3) { + ALOGE("%s: Gps coordinates in metadata is not complete.", __FUNCTION__); + return false; + } + if (!setGpsLatitude(entry.data.d[0])) { + ALOGE("%s: setting gps latitude failed.", __FUNCTION__); + return false; + } + if (!setGpsLongitude(entry.data.d[1])) { + ALOGE("%s: setting gps longitude failed.", __FUNCTION__); + return false; + } + if (!setGpsAltitude(entry.data.d[2])) { + ALOGE("%s: setting gps altitude failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) { + entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD); + std::string method_str(reinterpret_cast(entry.data.u8)); + if (!setGpsProcessingMethod(method_str)) { + ALOGE("%s: setting gps processing method failed.", __FUNCTION__); + return false; + } + } + + if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) { + entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP); + time_t timestamp = static_cast(entry.data.i64[0]); + if (gmtime_r(×tamp, &time_info)) { + if (!setGpsTimestamp(time_info)) { + ALOGE("%s: setting gps timestamp failed.", __FUNCTION__); + return false; + } + } else { + ALOGE("%s: Time tranformation failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_JPEG_ORIENTATION)) { + entry = metadata.find(ANDROID_JPEG_ORIENTATION); + if (!setOrientation(entry.data.i32[0])) { + ALOGE("%s: setting orientation failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) { + entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME); + // int64_t of nanoseconds + if (!setExposureTime(entry.data.i64[0],1000000000u)) { + ALOGE("%s: setting exposure time failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_LENS_APERTURE)) { + const int kAperturePrecision = 10000; + entry = metadata.find(ANDROID_LENS_APERTURE); + if (!setFNumber(entry.data.f[0] * kAperturePrecision, kAperturePrecision)) { + ALOGE("%s: setting F number failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) { + entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) { + const uint32_t kNoFlashFunction = 0x20; + if (!setFlash(kNoFlashFunction)) { + ALOGE("%s: setting flash failed.", __FUNCTION__); + return false; + } + } else { + ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]); + return false; + } + } + + if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) { + entry = metadata.find(ANDROID_CONTROL_AWB_MODE); + if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) { + const uint16_t kAutoWhiteBalance = 0; + if (!setWhiteBalance(kAutoWhiteBalance)) { + ALOGE("%s: setting white balance failed.", __FUNCTION__); + return false; + } + } else { + ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]); + return false; + } + } + + if (time_available) { + char str[4]; + if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) { + ALOGE("%s: Subsec is invalid: %ld", __FUNCTION__, tp.tv_nsec); + return false; + } + if (!setSubsecTime(std::string(str))) { + ALOGE("%s: setting subsec time failed.", __FUNCTION__); + return false; + } + } + + return true; +} + +} // namespace camera3 +} // namespace android diff --git a/services/camera/libcameraservice/utils/ExifUtils.h b/services/camera/libcameraservice/utils/ExifUtils.h new file mode 100644 index 0000000000..8ccdd8fdca --- /dev/null +++ b/services/camera/libcameraservice/utils/ExifUtils.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2019 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_SERVERS_CAMERA_EXIF_UTILS_H +#define ANDROID_SERVERS_CAMERA_EXIF_UTILS_H + +#include "CameraMetadata.h" + +namespace android { +namespace camera3 { + +// This is based on the camera HIDL shim implementation, which was in turned +// based on original ChromeOS ARC implementation of a V4L2 HAL + +// ExifUtils can override APP1 segment with tags which caller set. ExifUtils can +// also add a thumbnail in the APP1 segment if thumbnail size is specified. +// ExifUtils can be reused with different images by calling initialize(). +// +// Example of using this class : +// std::unique_ptr utils(ExifUtils::Create()); +// utils->initialize(const unsigned char* app1Segment, size_t app1SegmentSize); +// ... +// // Call ExifUtils functions to set Exif tags. +// ... +// utils->GenerateApp1(); +// unsigned int app1Length = utils->GetApp1Length(); +// uint8_t* app1Buffer = new uint8_t[app1Length]; +// memcpy(app1Buffer, utils->GetApp1Buffer(), app1Length); +class ExifUtils { + +public: + virtual ~ExifUtils(); + + static ExifUtils* create(); + + // Initialize() can be called multiple times. The setting of Exif tags will be + // cleared. + virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize) = 0; + + // Set all known fields from a metadata structure + virtual bool setFromMetadata(const CameraMetadata& metadata, + const size_t imageWidth, const size_t imageHeight) = 0; + + // Sets the len aperture. + // Returns false if memory allocation fails. + virtual bool setAperture(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the value of brightness. + // Returns false if memory allocation fails. + virtual bool setBrightness(int32_t numerator, int32_t denominator) = 0; + + // Sets the color space. + // Returns false if memory allocation fails. + virtual bool setColorSpace(uint16_t color_space) = 0; + + // Sets the information to compressed data. + // Returns false if memory allocation fails. + virtual bool setComponentsConfiguration(const std::string& components_configuration) = 0; + + // Sets the compression scheme used for the image data. + // Returns false if memory allocation fails. + virtual bool setCompression(uint16_t compression) = 0; + + // Sets image contrast. + // Returns false if memory allocation fails. + virtual bool setContrast(uint16_t contrast) = 0; + + // Sets the date and time of image last modified. It takes local time. The + // name of the tag is DateTime in IFD0. + // Returns false if memory allocation fails. + virtual bool setDateTime(const struct tm& t) = 0; + + // Sets the image description. + // Returns false if memory allocation fails. + virtual bool setDescription(const std::string& description) = 0; + + // Sets the digital zoom ratio. If the numerator is 0, it means digital zoom + // was not used. + // Returns false if memory allocation fails. + virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the exposure bias. + // Returns false if memory allocation fails. + virtual bool setExposureBias(int32_t numerator, int32_t denominator) = 0; + + // Sets the exposure mode set when the image was shot. + // Returns false if memory allocation fails. + virtual bool setExposureMode(uint16_t exposure_mode) = 0; + + // Sets the program used by the camera to set exposure when the picture is + // taken. + // Returns false if memory allocation fails. + virtual bool setExposureProgram(uint16_t exposure_program) = 0; + + // Sets the exposure time, given in seconds. + // Returns false if memory allocation fails. + virtual bool setExposureTime(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the status of flash. + // Returns false if memory allocation fails. + virtual bool setFlash(uint16_t flash) = 0; + + // Sets the F number. + // Returns false if memory allocation fails. + virtual bool setFNumber(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the focal length of lens used to take the image in millimeters. + // Returns false if memory allocation fails. + virtual bool setFocalLength(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the degree of overall image gain adjustment. + // Returns false if memory allocation fails. + virtual bool setGainControl(uint16_t gain_control) = 0; + + // Sets the altitude in meters. + // Returns false if memory allocation fails. + virtual bool setGpsAltitude(double altitude) = 0; + + // Sets the latitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + virtual bool setGpsLatitude(double latitude) = 0; + + // Sets the longitude with degrees minutes seconds format. + // Returns false if memory allocation fails. + virtual bool setGpsLongitude(double longitude) = 0; + + // Sets GPS processing method. + // Returns false if memory allocation fails. + virtual bool setGpsProcessingMethod(const std::string& method) = 0; + + // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time. + // Returns false if memory allocation fails. + virtual bool setGpsTimestamp(const struct tm& t) = 0; + + // Sets the height (number of rows) of main image. + // Returns false if memory allocation fails. + virtual bool setImageHeight(uint32_t length) = 0; + + // Sets the width (number of columns) of main image. + // Returns false if memory allocation fails. + virtual bool setImageWidth(uint32_t width) = 0; + + // Sets the ISO speed. + // Returns false if memory allocation fails. + virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings) = 0; + + // Sets the kind of light source. + // Returns false if memory allocation fails. + virtual bool setLightSource(uint16_t light_source) = 0; + + // Sets the smallest F number of the lens. + // Returns false if memory allocation fails. + virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the metering mode. + // Returns false if memory allocation fails. + virtual bool setMeteringMode(uint16_t metering_mode) = 0; + + // Sets image orientation. + // Returns false if memory allocation fails. + virtual bool setOrientation(uint16_t orientation) = 0; + + // Sets the unit for measuring XResolution and YResolution. + // Returns false if memory allocation fails. + virtual bool setResolutionUnit(uint16_t resolution_unit) = 0; + + // Sets image saturation. + // Returns false if memory allocation fails. + virtual bool setSaturation(uint16_t saturation) = 0; + + // Sets the type of scene that was shot. + // Returns false if memory allocation fails. + virtual bool setSceneCaptureType(uint16_t type) = 0; + + // Sets image sharpness. + // Returns false if memory allocation fails. + virtual bool setSharpness(uint16_t sharpness) = 0; + + // Sets the shutter speed. + // Returns false if memory allocation fails. + virtual bool setShutterSpeed(int32_t numerator, int32_t denominator) = 0; + + // Sets the distance to the subject, given in meters. + // Returns false if memory allocation fails. + virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the fractions of seconds for the tag. + // Returns false if memory allocation fails. + virtual bool setSubsecTime(const std::string& subsec_time) = 0; + + // Sets the white balance mode set when the image was shot. + // Returns false if memory allocation fails. + virtual bool setWhiteBalance(uint16_t white_balance) = 0; + + // Sets the number of pixels per resolution unit in the image width. + // Returns false if memory allocation fails. + virtual bool setXResolution(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the position of chrominance components in relation to the luminance + // component. + // Returns false if memory allocation fails. + virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning) = 0; + + // Sets the number of pixels per resolution unit in the image length. + // Returns false if memory allocation fails. + virtual bool setYResolution(uint32_t numerator, uint32_t denominator) = 0; + + // Sets the manufacturer of camera. + // Returns false if memory allocation fails. + virtual bool setMake(const std::string& make) = 0; + + // Sets the model number of camera. + // Returns false if memory allocation fails. + virtual bool setModel(const std::string& model) = 0; + + // Generates APP1 segment. + // Returns false if generating APP1 segment fails. + virtual bool generateApp1() = 0; + + // Gets buffer of APP1 segment. This method must be called only after calling + // GenerateAPP1(). + virtual const uint8_t* getApp1Buffer() = 0; + + // Gets length of APP1 segment. This method must be called only after calling + // GenerateAPP1(). + virtual unsigned int getApp1Length() = 0; +}; + +} // namespace camera3 +} // namespace android + +#endif // ANDROID_SERVERS_CAMERA_EXIF_UTILS_H -- GitLab From df5dd141ba235f4b04d1a4b19728b9c0a82be600 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 6 Feb 2019 10:15:46 -0800 Subject: [PATCH 0905/1530] CCodec: limit number of max output buffers client can keep Bug: 123632127 Test: bug repro steps Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I0040755aa904749e96c1c905e913beab570bd5b8 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 587f83c6e7..6842fa543d 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -94,6 +94,11 @@ public: */ virtual void getArray(Vector> *) const {} + /** + * Return number of buffers the client owns. + */ + virtual size_t numClientBuffers() const = 0; + protected: std::string mComponentName; ///< name of component for debugging std::string mChannelName; ///< name of channel for debugging @@ -152,11 +157,6 @@ public: */ virtual std::unique_ptr toArrayMode(size_t size) = 0; - /** - * Return number of buffers the client owns. - */ - virtual size_t numClientBuffers() const = 0; - protected: // Pool to obtain blocks for input buffers. std::shared_ptr mPool; @@ -1226,6 +1226,10 @@ public: mImpl.realloc(alloc); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + private: BuffersArrayImpl mImpl; }; @@ -1287,6 +1291,10 @@ public: return std::move(array); } + size_t numClientBuffers() const final { + return mImpl.numClientBuffers(); + } + /** * Return an appropriate Codec2Buffer object for the type of buffers. * @@ -1816,9 +1824,17 @@ void CCodecBufferChannel::feedInputBufferIfAvailable() { } void CCodecBufferChannel::feedInputBufferIfAvailableInternal() { - while (!mInputMetEos && - !mReorderStash.lock()->hasPending() && - !mPipelineWatcher.lock()->pipelineFull()) { + if (mInputMetEos || + mReorderStash.lock()->hasPending() || + mPipelineWatcher.lock()->pipelineFull()) { + return; + } else { + Mutexed>::Locked buffers(mOutputBuffers); + if ((*buffers)->numClientBuffers() >= mNumOutputSlots) { + return; + } + } + for (size_t i = 0; i < mNumInputSlots; ++i) { sp inBuffer; size_t index; { -- GitLab From 85916d83e58c2603dda8fb3cc180634491fcb39c Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Tue, 5 Feb 2019 19:51:39 -0800 Subject: [PATCH 0906/1530] MPEG4Writer:Correct track duration BFrames muxing 1. Subtract start time offset from duration of tracks while Bframes muxing 2. Include start time offset while calculating segment_duration for elst entry. Bug: 123093098 Test: 1) android.media.cts.MediaMuxerTest 2) cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation: android.platform.test.annotations.RequiresDevice Change-Id: I4b5375c3f01c432dbf59e3c34632fa7d0fa66a68 --- media/libstagefright/MPEG4Writer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index c4015fbcbc..6259b15a27 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -3595,7 +3595,7 @@ void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) { } int64_t MPEG4Writer::Track::getDurationUs() const { - return mTrackDurationUs + getStartTimeOffsetTimeUs(); + return mTrackDurationUs + getStartTimeOffsetTimeUs() + mOwner->getStartTimeOffsetBFramesUs(); } int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const { @@ -4059,7 +4059,7 @@ void MPEG4Writer::Track::writeEdtsBox(){ // Prepone video playback. if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) { int32_t mvhdTimeScale = mOwner->getTimeScale(); - uint32_t tkhdDuration = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6; + uint32_t tkhdDuration = (getDurationUs() * mvhdTimeScale + 5E5) / 1E6; int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs()) * mTimeScale + 5E5) / 1E6; if (tkhdDuration > 0 && mediaTime > 0) { -- GitLab From 153f92d57b6a1d2fdc7fa601bbc86db31646a8f9 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 18 Dec 2018 18:33:28 -0800 Subject: [PATCH 0907/1530] Add secondary output to audio tracks The secondary are returned from mixes (from DAP loopback&render), from getOutputForAttr. All getOutputForAttr* of the stack are update. Internal getOutputForAttr use descriptor, external one use handles. The secondary output are saved in each track and the track is invalidated if the list of secondary output changes. In audio flinger, create a pair of recordTrack & patchTrack to pipe the intercepted audio audio to the secondary output. Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: Id6523d9e383c15a0e39313d5f355df809b7e72fe --- media/libaudioclient/AudioSystem.cpp | 5 +- media/libaudioclient/IAudioPolicyService.cpp | 36 +++-- .../include/media/AudioSystem.h | 3 +- .../include/media/IAudioPolicyService.h | 3 +- services/audioflinger/AudioFlinger.cpp | 63 ++++++++- services/audioflinger/AudioFlinger.h | 9 ++ services/audioflinger/PlaybackTracks.h | 14 +- services/audioflinger/Threads.cpp | 6 +- services/audioflinger/Tracks.cpp | 40 +++++- services/audiopolicy/AudioPolicyInterface.h | 3 +- .../include/AudioPolicyMix.h | 9 +- .../include/ClientDescriptor.h | 10 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 37 +++-- .../src/ClientDescriptor.cpp | 3 +- .../managerdefault/AudioPolicyManager.cpp | 131 +++++++++++++----- .../managerdefault/AudioPolicyManager.h | 26 ++-- .../service/AudioPolicyInterfaceImpl.cpp | 13 +- .../audiopolicy/service/AudioPolicyService.h | 21 +-- .../tests/audiopolicymanager_tests.cpp | 2 +- 19 files changed, 323 insertions(+), 111 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index b83a441e32..41a7ff0624 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -872,13 +872,14 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, const audio_config_t *config, audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) + audio_port_handle_t *portId, + std::vector *secondaryOutputs) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return NO_INIT; return aps->getOutputForAttr(attr, output, session, stream, pid, uid, config, - flags, selectedDeviceId, portId); + flags, selectedDeviceId, portId, secondaryOutputs); } status_t AudioSystem::startOutput(audio_port_handle_t portId) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 0db56e8609..d9f6e3678b 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -189,16 +189,17 @@ public: return static_cast (reply.readInt32()); } - virtual status_t getOutputForAttr(const audio_attributes_t *attr, - audio_io_handle_t *output, - audio_session_t session, - audio_stream_type_t *stream, - pid_t pid, - uid_t uid, - const audio_config_t *config, - audio_output_flags_t flags, - audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) + status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + pid_t pid, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t flags, + audio_port_handle_t *selectedDeviceId, + audio_port_handle_t *portId, + std::vector *secondaryOutputs) override { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); @@ -224,6 +225,10 @@ public: ALOGE("getOutputForAttr NULL portId - shouldn't happen"); return BAD_VALUE; } + if (secondaryOutputs == NULL) { + ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen"); + return BAD_VALUE; + } if (attr == NULL) { data.writeInt32(0); } else { @@ -258,7 +263,9 @@ public: } *selectedDeviceId = (audio_port_handle_t)reply.readInt32(); *portId = (audio_port_handle_t)reply.readInt32(); - return status; + secondaryOutputs->resize(reply.readInt32()); + return reply.read(secondaryOutputs->data(), + secondaryOutputs->size() * sizeof(audio_io_handle_t)); } virtual status_t startOutput(audio_port_handle_t portId) @@ -1300,16 +1307,19 @@ status_t BnAudioPolicyService::onTransact( audio_port_handle_t selectedDeviceId = data.readInt32(); audio_port_handle_t portId = (audio_port_handle_t)data.readInt32(); audio_io_handle_t output = 0; + std::vector secondaryOutputs; status_t status = getOutputForAttr(hasAttributes ? &attr : NULL, &output, session, &stream, pid, uid, &config, - flags, &selectedDeviceId, &portId); + flags, &selectedDeviceId, &portId, &secondaryOutputs); reply->writeInt32(status); reply->writeInt32(output); reply->writeInt32(stream); reply->writeInt32(selectedDeviceId); reply->writeInt32(portId); - return NO_ERROR; + reply->writeInt32(secondaryOutputs.size()); + return reply->write(secondaryOutputs.data(), + secondaryOutputs.size() * sizeof(audio_io_handle_t)); } break; case START_OUTPUT: { diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 87a9919126..60608943cd 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -231,7 +231,8 @@ public: const audio_config_t *config, audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId); + audio_port_handle_t *portId, + std::vector *secondaryOutputs); static status_t startOutput(audio_port_handle_t portId); static status_t stopOutput(audio_port_handle_t portId); static void releaseOutput(audio_port_handle_t portId); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index b2cda32561..e89a55d173 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -66,7 +66,8 @@ public: const audio_config_t *config, audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) = 0; + audio_port_handle_t *portId, + std::vector *secondaryOutputs) = 0; virtual status_t startOutput(audio_port_handle_t portId) = 0; virtual status_t stopOutput(audio_port_handle_t portId) = 0; virtual void releaseOutput(audio_port_handle_t portId) = 0; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index bc990993d2..befabc5d82 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -292,13 +292,16 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di fullConfig.sample_rate = config->sample_rate; fullConfig.channel_mask = config->channel_mask; fullConfig.format = config->format; + std::vector secondaryOutputs; ret = AudioSystem::getOutputForAttr(attr, &io, actualSessionId, &streamType, client.clientPid, client.clientUid, &fullConfig, (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT), - deviceId, &portId); + deviceId, &portId, &secondaryOutputs); + ALOGW_IF(!secondaryOutputs.empty(), + "%s does not support secondary outputs, ignoring them", __func__); } else { ret = AudioSystem::getInputForAttr(attr, &io, actualSessionId, @@ -678,6 +681,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, status_t lStatus; audio_stream_type_t streamType; audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE; + std::vector secondaryOutputs; bool updatePid = (input.clientInfo.clientPid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); @@ -712,7 +716,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType, clientPid, clientUid, &input.config, input.flags, - &output.selectedDeviceId, &portId); + &output.selectedDeviceId, &portId, &secondaryOutputs); if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) { ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus); @@ -785,6 +789,61 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, output.afLatencyMs = thread->latency(); output.portId = portId; + if (lStatus == NO_ERROR) { + // Connect secondary outputs. Failure on a secondary output must not imped the primary + // Any secondary output setup failure will lead to a desync between the AP and AF until + // the track is destroyed. + TeePatches teePatches; + for (audio_io_handle_t secondaryOutput : secondaryOutputs) { + PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput); + if (secondaryThread == NULL) { + ALOGE("no playback thread found for secondary output %d", output.outputId); + continue; + } + + size_t frameCount = std::lcm(thread->frameCount(), secondaryThread->frameCount()); + + using namespace std::chrono_literals; + auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask); + sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */, + output.sampleRate, + inChannelMask, + input.config.format, + frameCount, + NULL /* buffer */, + (size_t)0 /* bufferSize */, + AUDIO_INPUT_FLAG_DIRECT, + 0ns /* timeout */); + status_t status = patchRecord->initCheck(); + if (status != NO_ERROR) { + ALOGE("Secondary output patchRecord init failed: %d", status); + continue; + } + sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread, + streamType, + output.sampleRate, + input.config.channel_mask, + input.config.format, + frameCount, + patchRecord->buffer(), + patchRecord->bufferSize(), + output.flags, + 0ns /* timeout */); + status = patchTrack->initCheck(); + if (status != NO_ERROR) { + ALOGE("Secondary output patchTrack init failed: %d", status); + continue; + } + teePatches.push_back({patchRecord, patchTrack}); + secondaryThread->addPatchTrack(patchTrack); + patchTrack->setPeerProxy(patchRecord.get()); + patchRecord->setPeerProxy(patchTrack.get()); + + patchTrack->start(); // patchRecord is NOT started as it has no thread + } + track->setTeePatches(std::move(teePatches)); + } + // move effect chain to this output thread if an effect on same session was waiting // for a track to be created if (lStatus == NO_ERROR && effectThread != NULL) { diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index f16a196ac6..1441e15f19 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -528,6 +529,9 @@ private: class EffectChain; struct AudioStreamIn; + struct TeePatch; + using TeePatches = std::vector; + struct stream_type_t { stream_type_t() @@ -727,6 +731,11 @@ using effect_buffer_t = int16_t; audioHwDev(dev), stream(in), flags(flags) {} }; + struct TeePatch { + sp patchRecord; + sp patchTrack; + }; + // for mAudioSessionRefs only struct AudioSessionRef { AudioSessionRef(audio_session_t sessionid, pid_t pid) : diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 33048fc366..468352545a 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -43,9 +43,8 @@ public: void appendDumpHeader(String8& result); void appendDump(String8& result, bool active); - virtual status_t start(AudioSystem::sync_event_t event = - AudioSystem::SYNC_EVENT_NONE, - audio_session_t triggerSession = AUDIO_SESSION_NONE); + virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + audio_session_t triggerSession = AUDIO_SESSION_NONE); virtual void stop(); void pause(); @@ -129,6 +128,8 @@ public: } sp getExternalVibration() const { return mExternalVibration; } + void setTeePatches(TeePatches teePatches); + protected: // for numerous friend class PlaybackThread; @@ -139,8 +140,8 @@ protected: DISALLOW_COPY_AND_ASSIGN(Track); // AudioBufferProvider interface - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - // releaseBuffer() not overridden + status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override; + void releaseBuffer(AudioBufferProvider::Buffer* buffer) override; // ExtendedAudioBufferProvider interface virtual size_t framesReady() const; @@ -220,6 +221,8 @@ protected: sp mExternalVibration; private: + void interceptBuffer(const AudioBufferProvider::Buffer& buffer); + // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; // either mFastIndex == -1 if not isFastTrack() @@ -239,6 +242,7 @@ private: audio_output_flags_t mFlags; // If the last track change was notified to the client with readAndClearHasChanged std::atomic_flag mChangeNotified = ATOMIC_FLAG_INIT; + TeePatches mTeePatches; }; // end of Track diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 5a70864f06..dd1eabf9a1 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -8464,6 +8464,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, audio_output_flags_t flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT); audio_port_handle_t deviceId = mDeviceId; + std::vector secondaryOutputs; ret = AudioSystem::getOutputForAttr(&mAttr, &io, mSessionId, &stream, @@ -8472,7 +8473,10 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, &config, flags, &deviceId, - &portId); + &portId, + &secondaryOutputs); + ALOGD_IF(!secondaryOutputs.empty(), + "MmapThread::start does not support secondary outputs, ignoring them"); } else { audio_config_base_t config; config.sample_rate = mSampleRate; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 863dc9e006..37c3a2d8b8 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -99,7 +99,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mId(android_atomic_inc(&nextTrackId)), mTerminated(false), mType(type), - mThreadIoHandle(thread->id()), + mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE), mPortId(portId), mIsInvalid(false) { @@ -670,8 +670,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const { } // AudioBufferProvider interface -status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( - AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) { ServerProxy::Buffer buf; size_t desiredFrames = buffer->frameCount; @@ -686,10 +685,39 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( } else { mAudioTrackServerProxy->tallyUnderrunFrames(0); } - return status; } +void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + interceptBuffer(*buffer); + TrackBase::releaseBuffer(buffer); +} + +// TODO: compensate for time shift between HW modules. +void AudioFlinger::PlaybackThread::Track::interceptBuffer( + const AudioBufferProvider::Buffer& buffer) { + for (auto& sink : mTeePatches) { + RecordThread::PatchRecord& patchRecord = *sink.patchRecord; + AudioBufferProvider::Buffer patchBuffer; + patchBuffer.frameCount = buffer.frameCount; + auto status = patchRecord.getNextBuffer(&patchBuffer); + if (status != NO_ERROR) { + ALOGW("%s PathRecord getNextBuffer failed with error %d: %s", + __func__, status, strerror(-status)); + continue; + } + // FIXME: On buffer wrap, the frame count will be less then requested, + // retry to write the rest. (unlikely due to lcm buffer sizing) + ALOGW_IF(patchBuffer.frameCount != buffer.frameCount, + "%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames", + __func__, patchBuffer.frameCount, buffer.frameCount, + buffer.frameCount - patchBuffer.frameCount); + memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize); + patchRecord.releaseBuffer(&patchBuffer); + } +} + // releaseBuffer() is not overridden // ExtendedAudioBufferProvider interface @@ -1081,6 +1109,10 @@ void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backI }; } +void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) { + mTeePatches = std::move(teePatches); +} + status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp) { if (!isOffloaded() && !isDirect()) { diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index d7030f9b11..bb5441d2c8 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -110,7 +110,8 @@ public: const audio_config_t *config, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) = 0; + audio_port_handle_t *portId, + std::vector *secondaryOutputs) = 0; // indicates to the audio policy manager that the output starts being used by corresponding stream. virtual status_t startOutput(audio_port_handle_t portId) = 0; // indicates to the audio policy manager that the output stops being used by corresponding stream. diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index e5c7d48bab..1abce6f93d 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -68,13 +68,12 @@ public: * Try to find an output descriptor for the given attributes. * * @param[in] attributes to consider fowr the research of output descriptor. - * @param[out] desc to return if an output could be found. - * - * @return NO_ERROR if an output was found for the given attribute (in this case, the - * descriptor output param is initialized), error code otherwise. + * @param[out] desc to return if an primary output could be found. + * @param[out] secondaryDesc other desc that the audio should be routed to. */ status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid, - sp &desc); + sp &primaryDesc, + std::vector> *secondaryDescs); sp getDeviceAndMixForInputSource(audio_source_t inputSource, const DeviceVector &availableDeviceTypes, diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 4c069e40dc..2e44a60d9b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -86,10 +86,12 @@ public: audio_attributes_t attributes, audio_config_base_t config, audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, product_strategy_t strategy, audio_output_flags_t flags, - bool isPreferredDeviceForExclusiveUse) : + bool isPreferredDeviceForExclusiveUse, + std::vector> secondaryOutputs) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId, isPreferredDeviceForExclusiveUse), - mStream(stream), mStrategy(strategy), mFlags(flags) {} + mStream(stream), mStrategy(strategy), mFlags(flags), + mSecondaryOutputs(std::move(secondaryOutputs)) {} ~TrackClientDescriptor() override = default; using ClientDescriptor::dump; @@ -99,11 +101,15 @@ public: audio_output_flags_t flags() const { return mFlags; } audio_stream_type_t stream() const { return mStream; } product_strategy_t strategy() const { return mStrategy; } + const std::vector>& getSecondaryOutputs() const { + return mSecondaryOutputs; + }; private: const audio_stream_type_t mStream; const product_strategy_t mStrategy; const audio_output_flags_t mFlags; + const std::vector> mSecondaryOutputs; }; class RecordClientDescriptor: public ClientDescriptor diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 32c90b1114..6b6d9d274c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -157,30 +157,49 @@ void AudioPolicyMixCollection::closeOutput(sp &desc) } status_t AudioPolicyMixCollection::getOutputForAttr( - audio_attributes_t attributes, uid_t uid, sp &desc) + audio_attributes_t attributes, uid_t uid, sp &primaryDesc, + std::vector> *secondaryDescs) { ALOGV("getOutputForAttr() querying %zu mixes:", size()); - desc = 0; + primaryDesc = 0; for (size_t i = 0; i < size(); i++) { sp policyMix = valueAt(i); sp policyDesc = policyMix->getOutput(); if (!policyDesc) { - ALOGV("Skiping %zu: Mix has no output", i); + ALOGV("%s: Skiping %zu: Mix has no output", __func__, i); continue; } AudioMix *mix = policyMix->getMix(); + const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags); + + if (primaryOutputMix && primaryDesc != 0) { + ALOGV("%s: Skiping %zu: Primary output already found", __func__, i); + continue; // Primary output already found + } + switch (mixMatch(mix, i, attributes, uid)) { - case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort ? - case MixMatchStatus::NO_MATCH: continue; // skip the mix + case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort? + case MixMatchStatus::NO_MATCH: + ALOGV("%s: Mix %zu: does not match", __func__, i); + continue; // skip the mix case MixMatchStatus::MATCH:; } - desc = policyMix->getOutput(); - desc->mPolicyMix = mix; - return NO_ERROR; + policyDesc->mPolicyMix = mix; + if (primaryOutputMix) { + primaryDesc = policyDesc; + ALOGV("%s: Mix %zu: set primary desc", __func__, i); + } else { + if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) { + ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i); + } else { + ALOGV("%s: Add a secondary desc %zu", __func__, i); + secondaryDescs->push_back(policyDesc); + } + } } - return BAD_VALUE; + return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR; } AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index a6f6c3b5c8..633c40e343 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -85,7 +85,8 @@ SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t product_strategy_t strategy) : TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, - stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false), + stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false, + {} /* Sources do not support secondary outputs*/), mPatchDesc(patchDesc), mSrcDevice(srcDevice) { } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 0eee3f2c31..02b85b43a9 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -907,16 +908,18 @@ status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr, return NO_ERROR; } -status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, - audio_io_handle_t *output, - audio_session_t session, - const audio_attributes_t *attr, - audio_stream_type_t *stream, - uid_t uid, - const audio_config_t *config, - audio_output_flags_t *flags, - audio_port_handle_t *selectedDeviceId, - bool *isRequestedDeviceForExclusiveUse) +status_t AudioPolicyManager::getOutputForAttrInt( + audio_attributes_t *resultAttr, + audio_io_handle_t *output, + audio_session_t session, + const audio_attributes_t *attr, + audio_stream_type_t *stream, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t *flags, + audio_port_handle_t *selectedDeviceId, + bool *isRequestedDeviceForExclusiveUse, + std::vector> *secondaryDescs) { DeviceVector outputDevices; const audio_port_handle_t requestedPortId = *selectedDeviceId; @@ -935,19 +938,26 @@ status_t AudioPolicyManager::getOutputForAttrInt(audio_attributes_t *resultAttr, ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__, toString(*resultAttr).c_str(), toString(*stream).c_str(), session, requestedPortId); - // 1/ First check for explicit routing (eg. setPreferredDevice): NOTE: now handled by engine - // 2/ If no explict route, is there a matching dynamic policy that applies? - // NOTE: new engine product strategy does not make use of dynamic routing, keep it for - // remote-submix and legacy - sp desc; - if (requestedDevice == nullptr && - mPolicyMixes.getOutputForAttr(*resultAttr, uid, desc) == NO_ERROR) { - ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); - if (!audio_has_proportional_frames(config->format)) { - return BAD_VALUE; - } - *output = desc->mIoHandle; - AudioMix *mix = desc->mPolicyMix; + // The primary output is the explicit routing (eg. setPreferredDevice) if specified, + // otherwise, fallback to the dynamic policies, if none match, query the engine. + // Secondary outputs are always found by dynamic policies as the engine do not support them + sp policyDesc; + if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) { + policyDesc = nullptr; // reset getOutputForAttr in case of failure + secondaryDescs->clear(); + } + // Explicit routing is higher priority then any dynamic policy primary output + bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr; + + // FIXME: in case of RENDER policy, the output capabilities should be checked + if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty()) + && !audio_has_proportional_frames(config->format)) { + ALOGW("%s: audio loopback only supports proportional frames", __func__); + return BAD_VALUE; + } + if (usePrimaryOutputFromPolicyMixes) { + *output = policyDesc->mIoHandle; + AudioMix *mix = policyDesc->mPolicyMix; sp deviceDesc = mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress, @@ -1022,7 +1032,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, const audio_config_t *config, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) + audio_port_handle_t *portId, + std::vector *secondaryOutputs) { // The supplied portId must be AUDIO_PORT_HANDLE_NONE if (*portId != AUDIO_PORT_HANDLE_NONE) { @@ -1031,11 +1042,18 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, const audio_port_handle_t requestedPortId = *selectedDeviceId; audio_attributes_t resultAttr; bool isRequestedDeviceForExclusiveUse = false; + std::vector> secondaryOutputDescs; status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid, - config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse); + config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse, + &secondaryOutputDescs); if (status != NO_ERROR) { return status; } + std::vector> weakSecondaryOutputDescs; + for (auto& secondaryDesc : secondaryOutputDescs) { + secondaryOutputs->push_back(secondaryDesc->mIoHandle); + weakSecondaryOutputDescs.push_back(secondaryDesc); + } audio_config_base_t clientConfig = {.sample_rate = config->sample_rate, .format = config->format, @@ -1046,7 +1064,8 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, requestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), - *flags, isRequestedDeviceForExclusiveUse); + *flags, isRequestedDeviceForExclusiveUse, + std::move(weakSecondaryOutputDescs)); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); @@ -1562,13 +1581,15 @@ status_t AudioPolicyManager::startSource(const sp& outp policyMix = outputDesc->mPolicyMix; audio_devices_t newDeviceType; address = policyMix->mDeviceAddress.string(); - if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { - newDeviceType = policyMix->mDeviceType; - } else { + if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) { newDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } else { + newDeviceType = policyMix->mDeviceType; } - devices.add(mAvailableOutputDevices.getDevice(newDeviceType, - String8(address), AUDIO_FORMAT_DEFAULT)); + sp device = mAvailableOutputDevices.getDevice(newDeviceType, String8(address), + AUDIO_FORMAT_DEFAULT); + ALOG_ASSERT(device, "%s: no device found t=%u, a=%s", __func__, newDeviceType, address); + devices.add(device); } // requiresMuteCheck is false when we can bypass mute strategy. @@ -2609,18 +2630,24 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) // examine each mix's route type for (size_t i = 0; i < mixes.size(); i++) { AudioMix mix = mixes[i]; - // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination - if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) { + // Only capture of playback is allowed in LOOP_BACK & RENDER mode + if (is_mix_loopback_render(mix.mRouteFlags) && mix.mMixType != MIX_TYPE_PLAYERS) { + ALOGE("Unsupported Policy Mix %zu of %zu: " + "Only capture of playback is allowed in LOOP_BACK & RENDER mode", + i, mixes.size()); res = INVALID_OPERATION; break; } + // LOOP_BACK and LOOP_BACK | RENDER have the same remote submix backend and are handled + // in the same way. if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) { - ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size()); + ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK %d", i, mixes.size(), + mix.mRouteFlags); if (rSubmixModule == 0) { rSubmixModule = mHwModules.getModuleFromName( AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX); if (rSubmixModule == 0) { - ALOGE(" Unable to find audio module for submix, aborting mix %zu registration", + ALOGE("Unable to find audio module for submix, aborting mix %zu registration", i); res = INVALID_OPERATION; break; @@ -2635,7 +2662,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) } if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) { - ALOGE(" Error registering mix %zu for address %s", i, address.string()); + ALOGE("Error registering mix %zu for address %s", i, address.string()); res = INVALID_OPERATION; break; } @@ -2679,6 +2706,8 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) if (desc->supportedDevices().contains(device)) { if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) { + ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type, + address.string()); res = INVALID_OPERATION; } else { foundOutput = true; @@ -2746,7 +2775,7 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) rSubmixModule->removeOutputProfile(address); rSubmixModule->removeInputProfile(address); - } if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { + } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) { res = INVALID_OPERATION; continue; @@ -3635,9 +3664,11 @@ status_t AudioPolicyManager::connectAudioSource(const sp audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE; bool isRequestedDeviceForExclusiveUse = false; + std::vector> secondaryOutputs; getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, &attributes, &stream, sourceDesc->uid(), &config, &flags, - &selectedDeviceId, &isRequestedDeviceForExclusiveUse); + &selectedDeviceId, &isRequestedDeviceForExclusiveUse, + &secondaryOutputs); if (output == AUDIO_IO_HANDLE_NONE) { ALOGV("%s no output for device %08x", __FUNCTION__, sinkDevices.types()); return INVALID_OPERATION; @@ -4782,6 +4813,7 @@ void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function on // output is suspended before any tracks are moved to it checkA2dpSuspend(); checkOutputForAllStrategies(); + checkSecondaryOutputs(); if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend(); updateDevicesAndOutputs(); if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) { @@ -4870,6 +4902,29 @@ void AudioPolicyManager::checkOutputForAllStrategies() } } +void AudioPolicyManager::checkSecondaryOutputs() { + std::set streamsToInvalidate; + for (size_t i = 0; i < mOutputs.size(); i++) { + const sp& outputDescriptor = mOutputs[i]; + for (const sp& client : outputDescriptor->getClientIterable()) { + // FIXME code duplicated from getOutputForAttrInt + sp desc; + std::vector> secondaryDescs; + mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc, + &secondaryDescs); + if (!std::equal(client->getSecondaryOutputs().begin(), + client->getSecondaryOutputs().end(), + secondaryDescs.begin(), secondaryDescs.end())) { + streamsToInvalidate.insert(client->stream()); + } + } + } + for (audio_stream_type_t stream : streamsToInvalidate) { + ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream); + mpClientInterface->invalidateStream(stream); + } +} + void AudioPolicyManager::checkA2dpSuspend() { audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput(); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 73c3b5683e..70ad6ac7aa 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -113,15 +113,16 @@ public: virtual void setSystemProperty(const char* property, const char* value); virtual status_t initCheck(); virtual audio_io_handle_t getOutput(audio_stream_type_t stream); - virtual status_t getOutputForAttr(const audio_attributes_t *attr, - audio_io_handle_t *output, - audio_session_t session, - audio_stream_type_t *stream, - uid_t uid, - const audio_config_t *config, - audio_output_flags_t *flags, - audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId); + status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t *flags, + audio_port_handle_t *selectedDeviceId, + audio_port_handle_t *portId, + std::vector *secondaryOutputs) override; virtual status_t startOutput(audio_port_handle_t portId); virtual status_t stopOutput(audio_port_handle_t portId); virtual void releaseOutput(audio_port_handle_t portId); @@ -431,6 +432,10 @@ protected: */ void checkOutputForAllStrategies(); + // Same as checkOutputForStrategy but for secondary outputs. Make sure if a secondary + // output condition changes, the track is properly rerouted + void checkSecondaryOutputs(); + // manages A2DP output suspend/restore according to phone state and BT SCO usage void checkA2dpSuspend(); @@ -711,7 +716,8 @@ private: const audio_config_t *config, audio_output_flags_t *flags, audio_port_handle_t *selectedDeviceId, - bool *isRequestedDeviceForExclusiveUse); + bool *isRequestedDeviceForExclusiveUse, + std::vector> *secondaryDescs); // internal method to return the output handle for the given device and format audio_io_handle_t getOutputForDevices( const DeviceVector &devices, diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 7768ea347c..8ddf82435c 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -175,7 +175,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, const audio_config_t *config, audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId) + audio_port_handle_t *portId, + std::vector *secondaryOutputs) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -193,7 +194,8 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, AutoCallerClear acc; status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config, - &flags, selectedDeviceId, portId); + &flags, selectedDeviceId, portId, + secondaryOutputs); // FIXME: Introduce a way to check for the the telephony device before opening the output if ((result == NO_ERROR) && @@ -205,9 +207,10 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, flags = originalFlags; *selectedDeviceId = AUDIO_PORT_HANDLE_NONE; *portId = AUDIO_PORT_HANDLE_NONE; - result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, - config, - &flags, selectedDeviceId, portId); + secondaryOutputs->clear(); + result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config, + &flags, selectedDeviceId, portId, + secondaryOutputs); } if (result == NO_ERROR) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index ee293a7496..8cd6e8157e 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -74,16 +74,17 @@ public: virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); virtual audio_io_handle_t getOutput(audio_stream_type_t stream); - virtual status_t getOutputForAttr(const audio_attributes_t *attr, - audio_io_handle_t *output, - audio_session_t session, - audio_stream_type_t *stream, - pid_t pid, - uid_t uid, - const audio_config_t *config, - audio_output_flags_t flags, - audio_port_handle_t *selectedDeviceId, - audio_port_handle_t *portId); + status_t getOutputForAttr(const audio_attributes_t *attr, + audio_io_handle_t *output, + audio_session_t session, + audio_stream_type_t *stream, + pid_t pid, + uid_t uid, + const audio_config_t *config, + audio_output_flags_t flags, + audio_port_handle_t *selectedDeviceId, + audio_port_handle_t *portId, + std::vector *secondaryOutputs) override; virtual status_t startOutput(audio_port_handle_t portId); virtual status_t stopOutput(audio_port_handle_t portId); virtual void releaseOutput(audio_port_handle_t portId); diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp index e9f465795f..de5670c7c6 100644 --- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp +++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp @@ -214,7 +214,7 @@ void AudioPolicyManagerTest::getOutputForAttr( *portId = AUDIO_PORT_HANDLE_NONE; ASSERT_EQ(OK, mManager->getOutputForAttr( &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags, - selectedDeviceId, portId)); + selectedDeviceId, portId, {})); ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId); } -- GitLab From c43ea147033817dc46b76d8993a84f0e5ee4e4e0 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 31 Jan 2019 18:17:37 -0800 Subject: [PATCH 0908/1530] Propagate track status to interception patch track Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: Icbf627ce36b6d9390439f51237ef9515a2eba8cb Signed-off-by: Kevin Rocard --- services/audioflinger/AudioFlinger.cpp | 2 -- services/audioflinger/PlaybackTracks.h | 4 ++++ services/audioflinger/Tracks.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index befabc5d82..7733071cb8 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -838,8 +838,6 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, secondaryThread->addPatchTrack(patchTrack); patchTrack->setPeerProxy(patchRecord.get()); patchRecord->setPeerProxy(patchTrack.get()); - - patchTrack->start(); // patchRecord is NOT started as it has no thread } track->setTeePatches(std::move(teePatches)); } diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 468352545a..3f62bc3215 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -222,6 +222,10 @@ protected: private: void interceptBuffer(const AudioBufferProvider::Buffer& buffer); + template + void forEachTeePatchTrack(F f) { + for (auto& tp : mTeePatches) { f(tp.patchTrack); } + }; // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 37c3a2d8b8..57dd568157 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -525,6 +525,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() AudioSystem::releaseOutput(mPortId); } } + forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); }); } void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) @@ -865,6 +866,9 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev } else { status = BAD_VALUE; } + if (status == NO_ERROR) { + forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); }); + } return status; } @@ -898,6 +902,7 @@ void AudioFlinger::PlaybackThread::Track::stop() __func__, mId, (int)mThreadIoHandle); } } + forEachTeePatchTrack([](auto patchTrack) { patchTrack->stop(); }); } void AudioFlinger::PlaybackThread::Track::pause() @@ -930,6 +935,8 @@ void AudioFlinger::PlaybackThread::Track::pause() break; } } + // Pausing the TeePatch to avoid a glitch on underrun, at the cost of buffered audio loss. + forEachTeePatchTrack([](auto patchTrack) { patchTrack->pause(); }); } void AudioFlinger::PlaybackThread::Track::flush() @@ -991,6 +998,8 @@ void AudioFlinger::PlaybackThread::Track::flush() // because the hardware buffer could hold a large amount of audio playbackThread->broadcast_l(); } + // Flush the Tee to avoid on resume playing old data and glitching on the transition to new data + forEachTeePatchTrack([](auto patchTrack) { patchTrack->flush(); }); } // must be called with thread lock held @@ -1110,6 +1119,7 @@ void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backI } void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) { + forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); }); mTeePatches = std::move(teePatches); } -- GitLab From c2afbdf2a57dd2e84ae2fe20e72c45c67b1aa4da Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 31 Jan 2019 18:18:06 -0800 Subject: [PATCH 0909/1530] Dynamic audio policy should reject non pcm Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I4691b19b1fa2f3b8e8e5045c9ea5be0a5c9effb6 Signed-off-by: Kevin Rocard --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 02b85b43a9..391e38dac7 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -951,8 +951,8 @@ status_t AudioPolicyManager::getOutputForAttrInt( // FIXME: in case of RENDER policy, the output capabilities should be checked if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty()) - && !audio_has_proportional_frames(config->format)) { - ALOGW("%s: audio loopback only supports proportional frames", __func__); + && !audio_is_linear_pcm(config->format)) { + ALOGD("%s: rejecting request as dynamic audio policy only support pcm", __func__); return BAD_VALUE; } if (usePrimaryOutputFromPolicyMixes) { -- GitLab From b378d62fbce3536d03be62410e97a5382b60417f Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 6 Feb 2019 16:27:46 -0800 Subject: [PATCH 0910/1530] Allows start/stopAudioSource from non-system service A media application takes advantage of HwAudioSource would call start/stopAudioSource directly. Bug: 112161027 Test: Launch FM Tuner application Change-Id: Ic4c23f51010dbb3f2226d9d2200e11feb908867b --- media/libaudioclient/IAudioPolicyService.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 0db56e8609..e0172ca3fd 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -1160,8 +1160,6 @@ status_t BnAudioPolicyService::onTransact( case SET_STREAM_VOLUME: case REGISTER_POLICY_MIXES: case SET_MASTER_MONO: - case START_AUDIO_SOURCE: - case STOP_AUDIO_SOURCE: case GET_SURROUND_FORMATS: case SET_SURROUND_FORMAT_ENABLED: case SET_ASSISTANT_UID: -- GitLab From 4d4ca6ada3d9f1d535bc4bee2362ff93f88cd55f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 1 Feb 2019 18:23:37 -0800 Subject: [PATCH 0911/1530] FastMixer: Enable volume ramp for active tracks Clean up logic for FastTrack AudioMixer track initialization. Test: SoundBar menu scrolling, AudioFlinger tee Bug: 119284313 Change-Id: I551d9d54d82e0029a49c6481ba8669093359d6d1 --- services/audioflinger/FastMixer.cpp | 153 ++++++++++++++-------------- services/audioflinger/FastMixer.h | 8 ++ 2 files changed, 83 insertions(+), 78 deletions(-) diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index e78c98b5b4..c5b9953ba3 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -139,6 +139,75 @@ bool FastMixer::isSubClassCommand(FastThreadState::Command command) } } +void FastMixer::updateMixerTrack(int index, Reason reason) { + const FastMixerState * const current = (const FastMixerState *) mCurrent; + const FastTrack * const fastTrack = ¤t->mFastTracks[index]; + + // check and update generation + if (reason == REASON_MODIFY && mGenerations[index] == fastTrack->mGeneration) { + return; // no change on an already configured track. + } + mGenerations[index] = fastTrack->mGeneration; + + // mMixer == nullptr on configuration failure (check done after generation update). + if (mMixer == nullptr) { + return; + } + + switch (reason) { + case REASON_REMOVE: + mMixer->destroy(index); + break; + case REASON_ADD: { + const status_t status = mMixer->create( + index, fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, + "%s: cannot create fast track index" + " %d, mask %#x, format %#x in AudioMixer", + __func__, index, fastTrack->mChannelMask, fastTrack->mFormat); + } + [[fallthrough]]; // now fallthrough to update the newly created track. + case REASON_MODIFY: + mMixer->setBufferProvider(index, fastTrack->mBufferProvider); + + float vlf, vrf; + if (fastTrack->mVolumeProvider != nullptr) { + const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); + vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); + vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); + } else { + vlf = vrf = AudioMixer::UNITY_GAIN_FLOAT; + } + + // set volume to avoid ramp whenever the track is updated (or created). + // Note: this does not distinguish from starting fresh or + // resuming from a paused state. + mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); + + mMixer->setParameter(index, AudioMixer::RESAMPLE, AudioMixer::REMOVE, nullptr); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + (void *)mMixerBuffer); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, + (void *)(uintptr_t)mMixerBufferFormat); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::FORMAT, + (void *)(uintptr_t)fastTrack->mFormat); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + (void *)(uintptr_t)fastTrack->mChannelMask); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)mSinkChannelMask); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, + (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); + mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, + (void *)(uintptr_t)fastTrack->mHapticIntensity); + + mMixer->enable(index); + break; + default: + LOG_ALWAYS_FATAL("%s: invalid update reason %d", __func__, reason); + } +} + void FastMixer::onStateChange() { const FastMixerState * const current = (const FastMixerState *) mCurrent; @@ -240,21 +309,16 @@ void FastMixer::onStateChange() // check for change in active track set const unsigned currentTrackMask = current->mTrackMask; dumpState->mTrackMask = currentTrackMask; + dumpState->mNumTracks = popcount(currentTrackMask); if (current->mFastTracksGen != mFastTracksGen) { - ALOG_ASSERT(mMixerBuffer != NULL); // process removed tracks first to avoid running out of track names unsigned removedTracks = previousTrackMask & ~currentTrackMask; while (removedTracks != 0) { int i = __builtin_ctz(removedTracks); removedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - ALOG_ASSERT(fastTrack->mBufferProvider == NULL); - if (mMixer != NULL) { - mMixer->destroy(i); - } + updateMixerTrack(i, REASON_REMOVE); // don't reset track dump state, since other side is ignoring it - mGenerations[i] = fastTrack->mGeneration; } // now process added tracks @@ -262,40 +326,7 @@ void FastMixer::onStateChange() while (addedTracks != 0) { int i = __builtin_ctz(addedTracks); addedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - if (mMixer != NULL) { - const int name = i; // for clarity, choose name as fast track index. - status_t status = mMixer->create( - name, - fastTrack->mChannelMask, - fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, - "%s: cannot create track name" - " %d, mask %#x, format %#x, sessionId %d in AudioMixer", - __func__, name, - fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); - mMixer->setBufferProvider(name, bufferProvider); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, - (void *)mMixerBuffer); - // newly allocated track names default to full scale volume - mMixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, - (void *)(uintptr_t)fastTrack->mFormat); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, - (void *)(uintptr_t)fastTrack->mChannelMask); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, - (void *)(uintptr_t)mSinkChannelMask); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, - (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, - (void *)(uintptr_t)fastTrack->mHapticIntensity); - mMixer->enable(name); - } - mGenerations[i] = fastTrack->mGeneration; + updateMixerTrack(i, REASON_ADD); } // finally process (potentially) modified tracks; these use the same slot @@ -304,44 +335,10 @@ void FastMixer::onStateChange() while (modifiedTracks != 0) { int i = __builtin_ctz(modifiedTracks); modifiedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - if (fastTrack->mGeneration != mGenerations[i]) { - // this track was actually modified - AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL); - if (mMixer != NULL) { - const int name = i; - mMixer->setBufferProvider(name, bufferProvider); - if (fastTrack->mVolumeProvider == NULL) { - float f = AudioMixer::UNITY_GAIN_FLOAT; - mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); - mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); - } - mMixer->setParameter(name, AudioMixer::RESAMPLE, - AudioMixer::REMOVE, NULL); - mMixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, - (void *)(uintptr_t)fastTrack->mFormat); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, - (void *)(uintptr_t)fastTrack->mChannelMask); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, - (void *)(uintptr_t)mSinkChannelMask); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, - (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); - mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, - (void *)(uintptr_t)fastTrack->mHapticIntensity); - // already enabled - } - mGenerations[i] = fastTrack->mGeneration; - } + updateMixerTrack(i, REASON_MODIFY); } mFastTracksGen = current->mFastTracksGen; - - dumpState->mNumTracks = popcount(currentTrackMask); } } @@ -408,8 +405,8 @@ void FastMixer::onWork() float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); - mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); - mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); + mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &vlf); + mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &vrf); } // FIXME The current implementation of framesReady() for fast tracks // takes a tryLock, which can block diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index c31d476c48..97ab635fa0 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -59,6 +59,14 @@ private: virtual void onStateChange(); virtual void onWork(); + enum Reason { + REASON_REMOVE, + REASON_ADD, + REASON_MODIFY, + }; + // called when a fast track of index has been removed, added, or modified + void updateMixerTrack(int index, Reason reason); + // FIXME these former local variables need comments static const FastMixerState sInitial; -- GitLab From fc7d169e0d790ee283ae6a8f469e2de819863196 Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Mon, 4 Feb 2019 11:46:37 -0800 Subject: [PATCH 0912/1530] rtsp: do not drop early access units of seekable rtsp Access units of seekable RTSP will be mapped to normal playtime in RTSPSource, so the negative media time can be adjusted and played normally. Test: play the test rtsp stream with and without the patch Bug: 123862806, Bug: 123305488 Author: Lubin Yin CRs-Fixed: 866580 Change-Id: I12793dbbf367650e66532195324adb5b5ad8fe85 --- media/libstagefright/rtsp/MyHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 5d993db8a6..8454ca1aeb 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -1913,7 +1913,7 @@ private: mLastMediaTimeUs = mediaTimeUs; } - if (mediaTimeUs < 0) { + if (mediaTimeUs < 0 && !mSeekable) { ALOGV("dropping early accessUnit."); return false; } -- GitLab From a98c1aa621d62635330fef9cc7cce886cecf01c1 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 31 Jan 2019 15:26:03 -0800 Subject: [PATCH 0913/1530] aaudio_loopback: glitch locations, hang callback Various improvements including: Show frame number of first 32 glitches. Show graph at first glitch. Hang in the callback using -h{msec} option. Test: Run glitch test for 20 seconds, hang for 40 msec Test: adb shell aaudio_loopback -tm -s20 -h40 Change-Id: I8e0f7bfd89efe907c38093e65e7775f48adf88f7 --- .../examples/loopback/src/LoopbackAnalyzer.h | 21 +++- .../examples/loopback/src/loopback.cpp | 117 ++++++++++++++---- .../examples/utils/AAudioArgsParser.h | 2 - 3 files changed, 111 insertions(+), 29 deletions(-) diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h index 9711b862b2..8eb70b1295 100644 --- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h +++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h @@ -310,7 +310,7 @@ public: } // Write SHORT data from the first channel. - int write(int16_t *inputData, int inputChannelCount, int numFrames) { + int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) { // stop at end of buffer if ((mFrameCounter + numFrames) > mMaxFrames) { numFrames = mMaxFrames - mFrameCounter; @@ -322,7 +322,7 @@ public: } // Write FLOAT data from the first channel. - int write(float *inputData, int inputChannelCount, int numFrames) { + int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) { // stop at end of buffer if ((mFrameCounter + numFrames) > mMaxFrames) { numFrames = mMaxFrames - mFrameCounter; @@ -333,7 +333,7 @@ public: return numFrames; } - int size() { + int32_t size() { return mFrameCounter; } @@ -443,9 +443,14 @@ public: virtual ~LoopbackProcessor() = default; + enum process_result { + PROCESS_RESULT_OK, + PROCESS_RESULT_GLITCH + }; + virtual void reset() {} - virtual void process(float *inputData, int inputChannelCount, + virtual process_result process(float *inputData, int inputChannelCount, float *outputData, int outputChannelCount, int numFrames) = 0; @@ -639,7 +644,7 @@ public: return getSampleRate() / 8; } - void process(float *inputData, int inputChannelCount, + process_result process(float *inputData, int inputChannelCount, float *outputData, int outputChannelCount, int numFrames) override { int channelsValid = std::min(inputChannelCount, outputChannelCount); @@ -750,6 +755,7 @@ public: mState = nextState; mLoopCounter++; + return PROCESS_RESULT_OK; } int save(const char *fileName) override { @@ -896,9 +902,10 @@ public: * @param inputData contains microphone data with sine signal feedback * @param outputData contains the reference sine wave */ - void process(float *inputData, int inputChannelCount, + process_result process(float *inputData, int inputChannelCount, float *outputData, int outputChannelCount, int numFrames) override { + process_result result = PROCESS_RESULT_OK; mProcessCount++; float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); @@ -978,6 +985,7 @@ public: mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff); if (absDiff > mTolerance) { mGlitchCount++; + result = PROCESS_RESULT_GLITCH; //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n", // mFrameCounter, mGlitchCount, predicted, sample); mState = STATE_IMMUNE; @@ -1018,6 +1026,7 @@ public: mFrameCounter++; } + return result; } void resetAccumulator() { diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index 3de1514485..75d425f6f0 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -34,6 +34,7 @@ #include "AAudioSimpleRecorder.h" #include "AAudioExampleUtils.h" #include "LoopbackAnalyzer.h" +#include "../../utils/AAudioExampleUtils.h" // V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo #define APP_VERSION "0.4.00" @@ -47,10 +48,14 @@ constexpr int kLogPeriodMillis = 1000; constexpr int kNumInputChannels = 1; constexpr int kNumCallbacksToDrain = 20; +constexpr int kNumCallbacksToNotRead = 0; // let input fill back up constexpr int kNumCallbacksToDiscard = 20; +constexpr int kDefaultHangTimeMillis = 50; +constexpr int kMaxGlitchEventsToSave = 32; struct LoopbackData { AAudioStream *inputStream = nullptr; + AAudioStream *outputStream = nullptr; int32_t inputFramesMaximum = 0; int16_t *inputShortData = nullptr; float *inputFloatData = nullptr; @@ -58,6 +63,7 @@ struct LoopbackData { int32_t actualInputChannelCount = 0; int32_t actualOutputChannelCount = 0; int32_t numCallbacksToDrain = kNumCallbacksToDrain; + int32_t numCallbacksToNotRead = kNumCallbacksToNotRead; int32_t numCallbacksToDiscard = kNumCallbacksToDiscard; int32_t minNumFrames = INT32_MAX; int32_t maxNumFrames = 0; @@ -65,6 +71,9 @@ struct LoopbackData { int32_t insufficientReadFrames = 0; int32_t framesReadTotal = 0; int32_t framesWrittenTotal = 0; + int32_t hangPeriodMillis = 5 * 1000; // time between hangs + int32_t hangCountdownFrames = 5 * 48000; // frames til next hang + int32_t hangTimeMillis = 0; // 0 for no hang bool isDone = false; aaudio_result_t inputError = AAUDIO_OK; @@ -74,6 +83,29 @@ struct LoopbackData { EchoAnalyzer echoAnalyzer; AudioRecording audioRecording; LoopbackProcessor *loopbackProcessor; + + int32_t glitchFrames[kMaxGlitchEventsToSave]; + int32_t numGlitchEvents = 0; + + void hangIfRequested(int32_t numFrames) { + if (hangTimeMillis > 0) { + hangCountdownFrames -= numFrames; + if (hangCountdownFrames <= 0) { + const int64_t startNanos = getNanoseconds(); + usleep(hangTimeMillis * 1000); + const int64_t endNanos = getNanoseconds(); + const int32_t elapsedMicros = (int32_t) + ((endNanos - startNanos) / 1000); + printf("callback hanging for %d millis, actual = %d micros\n", + hangTimeMillis, elapsedMicros); + hangCountdownFrames = (int64_t) hangPeriodMillis + * AAudioStream_getSampleRate(outputStream) + / 1000; + } + } + + + } }; static void convertPcm16ToFloat(const int16_t *source, @@ -166,6 +198,9 @@ static aaudio_data_callback_result_t MyDataCallbackProc( myData->numCallbacksToDrain--; } + } else if (myData->numCallbacksToNotRead > 0) { + // Let the input fill up a bit so we are not so close to the write pointer. + myData->numCallbacksToNotRead--; } else if (myData->numCallbacksToDiscard > 0) { // Ignore. Allow the input to fill back up to equilibrium with the output. actualFramesRead = readFormattedData(myData, numFrames); @@ -175,6 +210,7 @@ static aaudio_data_callback_result_t MyDataCallbackProc( myData->numCallbacksToDiscard--; } else { + myData->hangIfRequested(numFrames); int32_t numInputBytes = numFrames * myData->actualInputChannelCount * sizeof(float); memset(myData->inputFloatData, 0 /* value */, numInputBytes); @@ -191,7 +227,7 @@ static aaudio_data_callback_result_t MyDataCallbackProc( if (actualFramesRead < numFrames) { if(actualFramesRead < (int32_t) framesAvailable) { - printf("insufficient but numFrames = %d" + printf("insufficient for no reason, numFrames = %d" ", actualFramesRead = %d" ", inputFramesWritten = %d" ", inputFramesRead = %d" @@ -212,16 +248,25 @@ static aaudio_data_callback_result_t MyDataCallbackProc( if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_I16) { convertPcm16ToFloat(myData->inputShortData, myData->inputFloatData, numSamples); } - // Save for later. - myData->audioRecording.write(myData->inputFloatData, - myData->actualInputChannelCount, - numFrames); + // Analyze the data. - myData->loopbackProcessor->process(myData->inputFloatData, + LoopbackProcessor::process_result procResult = myData->loopbackProcessor->process(myData->inputFloatData, myData->actualInputChannelCount, outputData, myData->actualOutputChannelCount, numFrames); + + if (procResult == LoopbackProcessor::PROCESS_RESULT_GLITCH) { + if (myData->numGlitchEvents < kMaxGlitchEventsToSave) { + myData->glitchFrames[myData->numGlitchEvents++] = myData->audioRecording.size(); + } + } + + // Save for later. + myData->audioRecording.write(myData->inputFloatData, + myData->actualInputChannelCount, + actualFramesRead); + myData->isDone = myData->loopbackProcessor->isDone(); if (myData->isDone) { result = AAUDIO_CALLBACK_RESULT_STOP; @@ -249,6 +294,7 @@ static void usage() { printf(" -C{channels} number of input channels\n"); printf(" -F{0,1,2} input format, 1=I16, 2=FLOAT\n"); printf(" -g{gain} recirculating loopback gain\n"); + printf(" -h{hangMillis} occasionally hang in the callback\n"); printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n"); printf(" n for _NONE\n"); printf(" l for _LATENCY\n"); @@ -307,9 +353,7 @@ static int parseTestMode(char c) { return testMode; } -void printAudioGraph(AudioRecording &recording, int numSamples) { - int32_t start = recording.size() / 2; - int32_t end = start + numSamples; +void printAudioGraphRegion(AudioRecording &recording, int32_t start, int32_t end) { if (end >= recording.size()) { end = recording.size() - 1; } @@ -360,6 +404,7 @@ int main(int argc, const char **argv) int testMode = TEST_ECHO_LATENCY; double gain = 1.0; + int hangTimeMillis = 0; // Make printf print immediately so that debug info is not stuck // in a buffer if we hang or crash. @@ -389,6 +434,15 @@ int main(int argc, const char **argv) case 'g': gain = atof(&arg[2]); break; + case 'h': + // Was there a number after the "-h"? + if (arg[2]) { + hangTimeMillis = atoi(&arg[2]); + } else { + // If no number then use the default. + hangTimeMillis = kDefaultHangTimeMillis; + } + break; case 'P': inputPerformanceLevel = parsePerformanceMode(arg[2]); break; @@ -453,7 +507,7 @@ int main(int argc, const char **argv) fprintf(stderr, "ERROR - player.open() returned %d\n", result); exit(1); } - outputStream = player.getStream(); + outputStream = loopbackData.outputStream = player.getStream(); actualOutputFormat = AAudioStream_getFormat(outputStream); if (actualOutputFormat != AAUDIO_FORMAT_PCM_FLOAT) { @@ -487,20 +541,24 @@ int main(int argc, const char **argv) } inputStream = loopbackData.inputStream = recorder.getStream(); - { - int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream); - result = AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity); - if (result < 0) { - fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames() returned %d\n", result); - goto finish; - } else {} - } - argParser.compareWithStream(inputStream); - // If the input stream is too small then we cannot satisfy the output callback. { int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream); + (void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity); + + if (testMode == TEST_SINE_MAGNITUDE) { + result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity); + if (result < 0) { + fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames(output) returned %d\n", + result); + goto finish; + } else { + printf("Output buffer size set to match input capacity = %d frames.\n", result); + } + } + + // If the input stream is too small then we cannot satisfy the output callback. if (actualCapacity < 2 * outputFramesPerBurst) { fprintf(stderr, "ERROR - input capacity < 2 * outputFramesPerBurst\n"); goto finish; @@ -525,6 +583,8 @@ int main(int argc, const char **argv) loopbackData.loopbackProcessor->reset(); + loopbackData.hangTimeMillis = hangTimeMillis; + // Start OUTPUT first so INPUT does not overflow. result = player.start(); if (result != AAUDIO_OK) { @@ -611,7 +671,17 @@ int main(int argc, const char **argv) if (loopbackData.inputError == AAUDIO_OK) { if (testMode == TEST_SINE_MAGNITUDE) { - printAudioGraph(loopbackData.audioRecording, 200); + if (loopbackData.numGlitchEvents > 0) { + // Graph around the first glitch if there is one. + const int32_t start = loopbackData.glitchFrames[0] - 8; + const int32_t end = start + outputFramesPerBurst + 8 + 8; + printAudioGraphRegion(loopbackData.audioRecording, start, end); + } else { + // Or graph the middle of the signal. + const int32_t start = loopbackData.audioRecording.size() / 2; + const int32_t end = start + 200; + printAudioGraphRegion(loopbackData.audioRecording, start, end); + } } loopbackData.loopbackProcessor->report(); @@ -661,6 +731,11 @@ finish: delete[] loopbackData.inputShortData; report_result: + + for (int i = 0; i < loopbackData.numGlitchEvents; i++) { + printf(" glitch at frame %d\n", loopbackData.glitchFrames[i]); + } + written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED); if (written > 0) { printf("main() wrote %8d processed samples to \"%s\" on Android device\n", diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h index a5dc55fd89..f5ed7aa867 100644 --- a/media/libaaudio/examples/utils/AAudioArgsParser.h +++ b/media/libaaudio/examples/utils/AAudioArgsParser.h @@ -130,12 +130,10 @@ public: } int32_t getBufferCapacity() const { - printf("%s() returns %d\n", __func__, mBufferCapacity); return mBufferCapacity; } void setBufferCapacity(int32_t frames) { - printf("%s(%d)\n", __func__, frames); mBufferCapacity = frames; } -- GitLab From 24a9fb0d4a3acd5d0c2ef96147650464a91eb8d9 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Fri, 18 Jan 2019 17:51:34 +0100 Subject: [PATCH 0914/1530] libaudioclient: force onAudioDeviceUpdate on registration This CL forces the onAudioDeviceUpdate on register event. It also fixes the loss of callback on AudioTrack or AudioRecord client if received before ioHandle is assigned. Test: audio smoke tests Test: CTS for AudioTrack and AudioRouting Change-Id: I119b5c407da68a5b55162550bea5fa7e724165d1 Signed-off-by: Francois Gaffie --- media/libaudioclient/AudioRecord.cpp | 17 ++++-- media/libaudioclient/AudioSystem.cpp | 57 ++++++++++++------- media/libaudioclient/AudioTrack.cpp | 18 +++--- .../include/media/AudioRecord.h | 2 +- .../include/media/AudioSystem.h | 32 ++++++++++- .../libaudioclient/include/media/AudioTrack.h | 2 +- 6 files changed, 90 insertions(+), 38 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 72a23e3022..8afb1cc5e7 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -355,7 +355,10 @@ status_t AudioRecord::set( } // create the IAudioRecord - status = createRecord_l(0 /*epoch*/, mOpPackageName); + { + AutoMutex lock(mLock); + status = createRecord_l(0 /*epoch*/, mOpPackageName); + } ALOGV("%s(%d): status %d", __func__, mPortId, status); @@ -1358,12 +1361,14 @@ status_t AudioRecord::removeAudioDeviceCallback( ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } - AutoMutex lock(mLock); - if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s(%d): removing different callback!", __func__, mPortId); - return INVALID_OPERATION; + { + AutoMutex lock(mLock); + if (mDeviceCallback.unsafe_get() != callback.get()) { + ALOGW("%s(%d): removing different callback!", __func__, mPortId); + return INVALID_OPERATION; + } + mDeviceCallback.clear(); } - mDeviceCallback.clear(); if (mInput != AUDIO_IO_HANDLE_NONE) { AudioSystem::removeAudioDeviceCallback(this, mInput); } diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index b83a441e32..33c2008480 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -522,8 +522,9 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return; audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE; - Vector < wp > callbacks; - + AudioDeviceCallbacks callbacks; + bool deviceValidOrChanged = false; + Mutex::Autolock _l(mCallbacksLock); { Mutex::Autolock _l(mLock); @@ -546,6 +547,13 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); if (ioIndex >= 0) { callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + deviceValidOrChanged = true; + } + } + if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) { + ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); + if ((ioIndex >= 0) && !mAudioDeviceCallbacks.valueAt(ioIndex).notifiedOnce()) { + callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); } } } @@ -584,6 +592,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); if (deviceId != ioDesc->getDeviceId()) { + deviceValidOrChanged = true; deviceId = ioDesc->getDeviceId(); ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); if (ioIndex >= 0) { @@ -600,22 +609,28 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even } break; } } - bool callbackRemoved = false; // callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid - for (size_t i = 0; i < callbacks.size(); ) { - sp callback = callbacks[i].promote(); - if (callback.get() != nullptr) { - callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); - i++; - } else { - callbacks.removeAt(i); - callbackRemoved = true; + if (callbacks.size() != 0) { + for (size_t i = 0; i < callbacks.size(); ) { + sp callback = callbacks[i].promote(); + if (callback.get() != nullptr) { + // Call the callback only if the device actually changed, the input or output was + // opened or closed or the client was newly registered and the callback was never + // called + if (!callback->notifiedOnce() || deviceValidOrChanged) { + // Must be called without mLock held. May lead to dead lock if calling for + // example getRoutedDevice that updates the device and tries to acquire mLock. + callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); + callback->setNotifiedOnce(); + } + i++; + } else { + callbacks.removeAt(i); + } } - } - // clean up callback list while we are here if some clients have disappeared without - // unregistering their callback - if (callbackRemoved) { - Mutex::Autolock _l(mLock); + callbacks.setNotifiedOnce(); + // clean up callback list while we are here if some clients have disappeared without + // unregistering their callback, or if cb was served for the first time since registered mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks); } } @@ -671,8 +686,8 @@ sp AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_ status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( const wp& callback, audio_io_handle_t audioIo) { - Mutex::Autolock _l(mLock); - Vector < wp > callbacks; + Mutex::Autolock _l(mCallbacksLock); + AudioDeviceCallbacks callbacks; ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); if (ioIndex >= 0) { callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); @@ -684,7 +699,7 @@ status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( } } callbacks.add(callback); - + callbacks.resetNotifiedOnce(); mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks); return NO_ERROR; } @@ -692,12 +707,12 @@ status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( const wp& callback, audio_io_handle_t audioIo) { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mCallbacksLock); ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); if (ioIndex < 0) { return INVALID_OPERATION; } - Vector < wp > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + AudioDeviceCallbacks callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); size_t cbIndex; for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) { diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 79abea07e3..b5a7ebea26 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -621,8 +621,10 @@ status_t AudioTrack::set( } // create the IAudioTrack - status = createTrack_l(); - + { + AutoMutex lock(mLock); + status = createTrack_l(); + } if (status != NO_ERROR) { if (mAudioTrackThread != 0) { mAudioTrackThread->requestExit(); // see comment in AudioTrack.h @@ -2957,12 +2959,14 @@ status_t AudioTrack::removeAudioDeviceCallback( ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } - AutoMutex lock(mLock); - if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s(%d): removing different callback!", __func__, mPortId); - return INVALID_OPERATION; + { + AutoMutex lock(mLock); + if (mDeviceCallback.unsafe_get() != callback.get()) { + ALOGW("%s removing different callback!", __FUNCTION__); + return INVALID_OPERATION; + } + mDeviceCallback.clear(); } - mDeviceCallback.clear(); if (mOutput != AUDIO_IO_HANDLE_NONE) { AudioSystem::removeAudioDeviceCallback(this, mOutput); } diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index 1f718448af..4707c4ae1d 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -677,7 +677,7 @@ private: sp mCblkMemory; audio_track_cblk_t* mCblk; // re-load after mLock.unlock() sp mBufferMemory; - audio_io_handle_t mInput; // returned by AudioSystem::getInput() + audio_io_handle_t mInput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getInputforAttr() int mPreviousPriority; // before start() SchedPolicy mPreviousSchedulingGroup; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 87a9919126..74b994ed01 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -398,6 +398,15 @@ public: virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId) = 0; + bool notifiedOnce() const { return mNotifiedOnce; } + void setNotifiedOnce() { mNotifiedOnce = true; } + private: + /** + * @brief mNotifiedOnce it forces the callback to be called at least once when + * registered with a VALID AudioDevice, and allows not to flood other listeners + * on this iohandle that already know the valid device. + */ + bool mNotifiedOnce = false; }; static status_t addAudioDeviceCallback(const wp& callback, @@ -443,8 +452,27 @@ private: private: Mutex mLock; DefaultKeyedVector > mIoDescriptors; - DefaultKeyedVector > > - mAudioDeviceCallbacks; + + class AudioDeviceCallbacks : public Vector> + { + public: + /** + * @brief notifiedOnce ensures that if a client adds a callback, it must at least be + * called once with the device on which it will be routed to. + * @return true if already notified or nobody waits for a callback, false otherwise. + */ + bool notifiedOnce() const { return (size() == 0) || mNotifiedOnce; } + void setNotifiedOnce() { mNotifiedOnce = true; } + void resetNotifiedOnce() { mNotifiedOnce = false; } + private: + /** + * @brief mNotifiedOnce it forces each callback to be called at least once when + * registered with a VALID AudioDevice + */ + bool mNotifiedOnce = false; + }; + Mutex mCallbacksLock; // prevents race on Callbacks + DefaultKeyedVector mAudioDeviceCallbacks; // cached values for recording getInputBufferSize() queries size_t mInBuffSize; // zero indicates cache is invalid uint32_t mInSamplingRate; diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index cbb750f90d..12f5d71435 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -1021,7 +1021,7 @@ public: sp mAudioTrack; sp mCblkMemory; audio_track_cblk_t* mCblk; // re-load after mLock.unlock() - audio_io_handle_t mOutput; // returned by AudioSystem::getOutputForAttr() + audio_io_handle_t mOutput = AUDIO_IO_HANDLE_NONE; // from AudioSystem::getOutputForAttr() sp mAudioTrackThread; bool mThreadCanCallJava; -- GitLab From 62d62d693dd71a798a628e81548f6da47c03ab2c Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:26:46 -0800 Subject: [PATCH 0915/1530] codec2: C2PlatformStore: signal component traits via interface Prefer interface values for kind, domain, and aliases. Bug: 119631295 Change-Id: I0613ec3fb0524ae4cbedde72a511b3684a9ef9fd --- media/codec2/vndk/C2Store.cpp | 88 ++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index dc7e89c754..0b674810fc 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -687,10 +687,27 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( if (alias != intf->getName()) { ALOGV("%s is alias to %s", alias.c_str(), intf->getName().c_str()); } - traits->name = alias; - // TODO: get this from interface properly. - bool encoder = (traits->name.find("encoder") != std::string::npos); - uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE + traits->name = alias; // TODO: this needs to be intf->getName() once aliases are supported + + C2ComponentKindSetting kind; + C2ComponentDomainSetting domain; + res = intf->query_vb({ &kind, &domain }, {}, C2_MAY_BLOCK, nullptr); + bool fixDomain = res != C2_OK; + if (res == C2_OK) { + traits->kind = kind.value; + traits->domain = domain.value; + } else { + // TODO: remove this fall-back + ALOGD("failed to query interface for kind and domain: %d", res); + + traits->kind = + (traits->name.find("encoder") != std::string::npos) ? C2Component::KIND_ENCODER : + (traits->name.find("decoder") != std::string::npos) ? C2Component::KIND_DECODER : + C2Component::KIND_OTHER; + } + + uint32_t mediaTypeIndex = + traits->kind == C2Component::KIND_ENCODER ? C2PortMimeConfig::output::PARAM_TYPE : C2PortMimeConfig::input::PARAM_TYPE; std::vector> params; res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms); @@ -702,29 +719,54 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( ALOGD("failed to query interface: unexpected vector size: %zu", params.size()); return mInit; } - C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get()); + C2PortMimeConfig *mediaTypeConfig = C2PortMimeConfig::From(params[0].get()); if (mediaTypeConfig == nullptr) { ALOGD("failed to query media type"); return mInit; } - traits->mediaType = mediaTypeConfig->m.value; - // TODO: get this properly. - traits->rank = 0x200; - - // TODO: define these values properly - bool decoder = (traits->name.find("decoder") != std::string::npos); - traits->kind = - decoder ? C2Component::KIND_DECODER : - encoder ? C2Component::KIND_ENCODER : - C2Component::KIND_OTHER; - if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) { - traits->domain = C2Component::DOMAIN_AUDIO; - } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) { - traits->domain = C2Component::DOMAIN_VIDEO; - } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) { - traits->domain = C2Component::DOMAIN_IMAGE; - } else { - traits->domain = C2Component::DOMAIN_OTHER; + traits->mediaType = + std::string(mediaTypeConfig->m.value, + strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount())); + + if (fixDomain) { + if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) { + traits->domain = C2Component::DOMAIN_AUDIO; + } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) { + traits->domain = C2Component::DOMAIN_VIDEO; + } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) { + traits->domain = C2Component::DOMAIN_IMAGE; + } else { + traits->domain = C2Component::DOMAIN_OTHER; + } + } + + // TODO: get this properly from the store during emplace + switch (traits->domain) { + case C2Component::DOMAIN_AUDIO: + traits->rank = 8; + break; + default: + traits->rank = 512; + } + + params.clear(); + res = intf->query_vb({}, { C2ComponentAliasesSetting::PARAM_TYPE }, C2_MAY_BLOCK, ¶ms); + if (res == C2_OK && params.size() == 1u) { + C2ComponentAliasesSetting *aliasesSetting = + C2ComponentAliasesSetting::From(params[0].get()); + if (aliasesSetting) { + // Split aliases on ',' + // This looks simpler in plain C and even std::string would still make a copy. + char *aliases = ::strndup(aliasesSetting->m.value, aliasesSetting->flexCount()); + ALOGD("'%s' has aliases: '%s'", intf->getName().c_str(), aliases); + + for (char *tok, *ptr, *str = aliases; (tok = ::strtok_r(str, ",", &ptr)); + str = nullptr) { + traits->aliases.push_back(tok); + ALOGD("adding alias: '%s'", tok); + } + free(aliases); + } } } mTraits = traits; -- GitLab From 4711827da6483bcc0f582be90acb4be8d71f8991 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:28:04 -0800 Subject: [PATCH 0916/1530] codec2: CCodec plugin CreateInputSurface should create C2 HAL surface This method is used by framework to determine if CCodec's input surface will be C2 HAL surface. Moved compatible surface creation into utility funtion in CCodec. Bug: 119631295 Change-Id: I17c6338694766075d6efd6b142e13d5b09ff5a78 --- media/codec2/sfplugin/CCodec.cpp | 86 ++++++++++++++++++-------------- media/codec2/sfplugin/CCodec.h | 10 ++++ 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 10263dedfb..c72b58d3ff 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -943,6 +943,47 @@ void CCodec::initiateCreateInputSurface() { (new AMessage(kWhatCreateInputSurface, this))->post(); } +sp CCodec::CreateOmxInputSurface() { + using namespace android::hardware::media::omx::V1_0; + using namespace android::hardware::media::omx::V1_0::utils; + using namespace android::hardware::graphics::bufferqueue::V1_0::utils; + typedef android::hardware::media::omx::V1_0::Status OmxStatus; + android::sp omx = IOmx::getService(); + typedef android::hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer HGraphicBufferProducer; + typedef android::hardware::media::omx::V1_0:: + IGraphicBufferSource HGraphicBufferSource; + OmxStatus s; + android::sp gbp; + android::sp gbs; + android::Return transStatus = omx->createInputSurface( + [&s, &gbp, &gbs]( + OmxStatus status, + const android::sp& producer, + const android::sp& source) { + s = status; + gbp = producer; + gbs = source; + }); + if (transStatus.isOk() && s == OmxStatus::OK) { + return new PersistentSurface( + new H2BGraphicBufferProducer(gbp), + sp<::android::IGraphicBufferSource>(new LWGraphicBufferSource(gbs))); + } + + return nullptr; +} + +sp CCodec::CreateCompatibleInputSurface() { + sp surface(CreateInputSurface()); + + if (surface == nullptr) { + surface = CreateOmxInputSurface(); + } + + return surface; +} + void CCodec::createInputSurface() { status_t err; sp bufferProducer; @@ -955,7 +996,7 @@ void CCodec::createInputSurface() { outputFormat = config->mOutputFormat; } - std::shared_ptr persistentSurface(CreateInputSurface()); + sp persistentSurface = CreateCompatibleInputSurface(); if (persistentSurface->getHidlTarget()) { sp hidlInputSurface = IInputSurface::castFrom( @@ -1727,46 +1768,17 @@ extern "C" android::CodecBase *CreateCodec() { return new android::CCodec; } +// Create Codec 2.0 input surface extern "C" android::PersistentSurface *CreateInputSurface() { // Attempt to create a Codec2's input surface. std::shared_ptr inputSurface = android::Codec2Client::CreateInputSurface(); - if (inputSurface) { - return new android::PersistentSurface( - inputSurface->getGraphicBufferProducer(), - static_cast>( - inputSurface->getHalInterface())); - } - - // Fall back to OMX. - using namespace android::hardware::media::omx::V1_0; - using namespace android::hardware::media::omx::V1_0::utils; - using namespace android::hardware::graphics::bufferqueue::V1_0::utils; - typedef android::hardware::media::omx::V1_0::Status OmxStatus; - android::sp omx = IOmx::getService(); - typedef android::hardware::graphics::bufferqueue::V1_0:: - IGraphicBufferProducer HGraphicBufferProducer; - typedef android::hardware::media::omx::V1_0:: - IGraphicBufferSource HGraphicBufferSource; - OmxStatus s; - android::sp gbp; - android::sp gbs; - android::Return transStatus = omx->createInputSurface( - [&s, &gbp, &gbs]( - OmxStatus status, - const android::sp& producer, - const android::sp& source) { - s = status; - gbp = producer; - gbs = source; - }); - if (transStatus.isOk() && s == OmxStatus::OK) { - return new android::PersistentSurface( - new H2BGraphicBufferProducer(gbp), - sp<::android::IGraphicBufferSource>( - new LWGraphicBufferSource(gbs))); + if (!inputSurface) { + return nullptr; } - - return nullptr; + return new android::PersistentSurface( + inputSurface->getGraphicBufferProducer(), + static_cast>( + inputSurface->getHalInterface())); } diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/CCodec.h index 78b009ed80..545ad70f60 100644 --- a/media/codec2/sfplugin/CCodec.h +++ b/media/codec2/sfplugin/CCodec.h @@ -90,6 +90,16 @@ private: void flush(); void release(bool sendCallback); + /** + * Creates an input surface for the current device configuration compatible with CCodec. + * This could be backed by the C2 HAL or the OMX HAL. + */ + static sp CreateCompatibleInputSurface(); + + /// Creates an input surface to the OMX HAL + static sp CreateOmxInputSurface(); + + /// handle a create input surface call void createInputSurface(); void setInputSurface(const sp &surface); status_t setupInputSurface(const std::shared_ptr &surface); -- GitLab From 9d301ec7df43c24c34e3feb6e1d69c2b7d48f733 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:33:12 -0800 Subject: [PATCH 0917/1530] stagefright: rework and simplify OmxInfoBuilder - allow building only codecs that do not use input surface (when using C2 HAL input surface) - remove preferPlatform logic as we now have rank for this Bug: 119631295 Change-Id: I3b4cdb7db27351ee3f13d17dfa6879418d4097f6 --- media/libstagefright/MediaCodecList.cpp | 7 +- media/libstagefright/OmxInfoBuilder.cpp | 180 +++++++----------- .../media/stagefright/OmxInfoBuilder.h | 5 +- media/libstagefright/omx/1.0/OmxStore.cpp | 5 +- 4 files changed, 78 insertions(+), 119 deletions(-) diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 93478e9d60..641328308c 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -77,7 +77,8 @@ bool isProfilingNeeded() { return profilingNeeded; } -OmxInfoBuilder sOmxInfoBuilder; +OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */}; +OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */}; Mutex sCodec2InfoBuilderMutex; std::unique_ptr sCodec2InfoBuilder; @@ -98,7 +99,11 @@ std::vector GetBuilders() { sp surfaceTest = StagefrightPluginLoader::GetCCodecInstance()->createInputSurface(); if (surfaceTest == nullptr) { + ALOGD("Allowing all OMX codecs"); builders.push_back(&sOmxInfoBuilder); + } else { + ALOGD("Allowing only non-surface-encoder OMX codecs"); + builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder); } builders.push_back(GetCodec2InfoBuilder()); return builders; diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp index 382c9476ea..8910463a2c 100644 --- a/media/libstagefright/OmxInfoBuilder.cpp +++ b/media/libstagefright/OmxInfoBuilder.cpp @@ -21,8 +21,8 @@ #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS #endif +#include #include -#include #include #include @@ -53,7 +53,7 @@ using namespace ::android::hardware::media::omx::V1_0; namespace /* unnamed */ { bool hasPrefix(const hidl_string& s, const char* prefix) { - return strncmp(s.c_str(), prefix, strlen(prefix)) == 0; + return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0; } status_t queryCapabilities( @@ -87,7 +87,8 @@ status_t queryCapabilities( } // unnamed namespace -OmxInfoBuilder::OmxInfoBuilder() { +OmxInfoBuilder::OmxInfoBuilder(bool allowSurfaceEncoders) + : mAllowSurfaceEncoders(allowSurfaceEncoders) { } status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { @@ -135,133 +136,87 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // Convert roles to lists of codecs // codec name -> index into swCodecs/hwCodecs - std::map> - swCodecName2Info, hwCodecName2Info; + std::map> codecName2Info; + + uint32_t defaultRank = + ::android::base::GetUintProperty("debug.stagefright.omx_default_rank", 0x100u); + uint32_t defaultSwAudioRank = + ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-audio", 0x10u); + uint32_t defaultSwOtherRank = + ::android::base::GetUintProperty("debug.stagefright.omx_default_rank.sw-other", 0x210u); - char rank[PROPERTY_VALUE_MAX]; - uint32_t defaultRank = 0x100; - if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) { - defaultRank = std::strtoul(rank, nullptr, 10); - } for (const IOmxStore::RoleInfo& role : roles) { const hidl_string& typeName = role.type; bool isEncoder = role.isEncoder; - bool preferPlatformNodes = role.preferPlatformNodes; - // If preferPlatformNodes is true, hardware nodes must be added after - // platform (software) nodes. hwCodecs is used to hold hardware nodes - // that need to be added after software nodes for the same role. - std::vector hwCodecs; - for (const IOmxStore::NodeInfo& node : role.nodes) { + bool isAudio = hasPrefix(role.type, "audio/"); + bool isVideoOrImage = hasPrefix(role.type, "video/") || hasPrefix(role.type, "image/"); + + for (const IOmxStore::NodeInfo &node : role.nodes) { const hidl_string& nodeName = node.name; + + // currently image and video encoders use surface input + if (!mAllowSurfaceEncoders && isVideoOrImage && isEncoder) { + ALOGD("disabling %s for media type %s because we are not using OMX input surface", + nodeName.c_str(), role.type.c_str()); + continue; + } + bool isSoftware = hasPrefix(nodeName, "OMX.google"); - MediaCodecInfoWriter* info; - if (isSoftware) { - auto c2i = swCodecName2Info.find(nodeName); - if (c2i == swCodecName2Info.end()) { - // Create a new MediaCodecInfo for a new node. - c2i = swCodecName2Info.insert(std::make_pair( - nodeName, writer->addMediaCodecInfo())).first; - info = c2i->second.get(); - info->setName(nodeName.c_str()); - info->setOwner(node.owner.c_str()); - info->setAttributes( - // all OMX codecs are vendor codecs (in the vendor partition), but - // treat OMX.google codecs as non-hardware-accelerated and non-vendor - (isEncoder ? MediaCodecInfo::kFlagIsEncoder : 0)); - info->setRank(defaultRank); - } else { - // The node has been seen before. Simply retrieve the - // existing MediaCodecInfoWriter. - info = c2i->second.get(); - } - } else { - auto c2i = hwCodecName2Info.find(nodeName); - if (c2i == hwCodecName2Info.end()) { - // Create a new MediaCodecInfo for a new node. - if (!preferPlatformNodes) { - c2i = hwCodecName2Info.insert(std::make_pair( - nodeName, writer->addMediaCodecInfo())).first; - info = c2i->second.get(); - info->setName(nodeName.c_str()); - info->setOwner(node.owner.c_str()); - typename std::underlying_type::type attrs = - MediaCodecInfo::kFlagIsVendor; - if (isEncoder) { - attrs |= MediaCodecInfo::kFlagIsEncoder; - } - if (std::count_if( - node.attributes.begin(), node.attributes.end(), - [](const IOmxStore::Attribute &i) -> bool { - return i.key == "attribute::software-codec"; - })) { - attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; - } - info->setAttributes(attrs); - info->setRank(defaultRank); - } else { - // If preferPlatformNodes is true, this node must be - // added after all software nodes. - hwCodecs.push_back(&node); - continue; + uint32_t rank = isSoftware + ? (isAudio ? defaultSwAudioRank : defaultSwOtherRank) + : defaultRank; + // get rank from IOmxStore via attribute + for (const IOmxStore::Attribute& attribute : node.attributes) { + if (attribute.key == "rank") { + uint32_t oldRank = rank; + char dummy; + if (sscanf(attribute.value.c_str(), "%u%c", &rank, &dummy) != 1) { + rank = oldRank; } - } else { - // The node has been seen before. Simply retrieve the - // existing MediaCodecInfoWriter. - info = c2i->second.get(); + break; } } - std::unique_ptr caps = - info->addMediaType(typeName.c_str()); - if (queryCapabilities( - node, typeName.c_str(), isEncoder, caps.get()) != OK) { - ALOGW("Fail to add media type %s to codec %s", - typeName.c_str(), nodeName.c_str()); - info->removeMediaType(typeName.c_str()); - } - } - // If preferPlatformNodes is true, hardware nodes will not have been - // added in the loop above, but rather saved in hwCodecs. They are - // going to be added here. - if (preferPlatformNodes) { - for (const IOmxStore::NodeInfo *node : hwCodecs) { - MediaCodecInfoWriter* info; - const hidl_string& nodeName = node->name; - auto c2i = hwCodecName2Info.find(nodeName); - if (c2i == hwCodecName2Info.end()) { - // Create a new MediaCodecInfo for a new node. - c2i = hwCodecName2Info.insert(std::make_pair( - nodeName, writer->addMediaCodecInfo())).first; - info = c2i->second.get(); - info->setName(nodeName.c_str()); - info->setOwner(node->owner.c_str()); - typename std::underlying_type::type attrs = - MediaCodecInfo::kFlagIsVendor; - if (isEncoder) { - attrs |= MediaCodecInfo::kFlagIsEncoder; - } + MediaCodecInfoWriter* info; + auto c2i = codecName2Info.find(nodeName); + if (c2i == codecName2Info.end()) { + // Create a new MediaCodecInfo for a new node. + c2i = codecName2Info.insert(std::make_pair( + nodeName, writer->addMediaCodecInfo())).first; + info = c2i->second.get(); + info->setName(nodeName.c_str()); + info->setOwner(node.owner.c_str()); + info->setRank(rank); + + typename std::underlying_type::type attrs = 0; + // all OMX codecs are vendor codecs (in the vendor partition), but + // treat OMX.google codecs as non-hardware-accelerated and non-vendor + if (!isSoftware) { + attrs |= MediaCodecInfo::kFlagIsVendor; if (std::count_if( - node->attributes.begin(), node->attributes.end(), + node.attributes.begin(), node.attributes.end(), [](const IOmxStore::Attribute &i) -> bool { return i.key == "attribute::software-codec"; })) { attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; } - info->setRank(defaultRank); - } else { - // The node has been seen before. Simply retrieve the - // existing MediaCodecInfoWriter. - info = c2i->second.get(); } - std::unique_ptr caps = - info->addMediaType(typeName.c_str()); - if (queryCapabilities( - *node, typeName.c_str(), isEncoder, caps.get()) != OK) { - ALOGW("Fail to add media type %s to codec %s " - "after software codecs", - typeName.c_str(), nodeName.c_str()); - info->removeMediaType(typeName.c_str()); + if (isEncoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; } + info->setAttributes(attrs); + } else { + // The node has been seen before. Simply retrieve the + // existing MediaCodecInfoWriter. + info = c2i->second.get(); + } + std::unique_ptr caps = + info->addMediaType(typeName.c_str()); + if (queryCapabilities( + node, typeName.c_str(), isEncoder, caps.get()) != OK) { + ALOGW("Fail to add media type %s to codec %s", + typeName.c_str(), nodeName.c_str()); + info->removeMediaType(typeName.c_str()); } } } @@ -269,4 +224,3 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { } } // namespace android - diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h index 28f6094b75..1410a16a48 100644 --- a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h +++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h @@ -23,8 +23,11 @@ namespace android { class OmxInfoBuilder : public MediaCodecListBuilderBase { +private: + bool mAllowSurfaceEncoders; // allow surface encoders + public: - OmxInfoBuilder(); + explicit OmxInfoBuilder(bool allowSurfaceEncoders); ~OmxInfoBuilder() override = default; status_t buildMediaCodecList(MediaCodecListWriter* writer) override; }; diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp index 447af6fc42..2e041e3a39 100644 --- a/media/libstagefright/omx/1.0/OmxStore.cpp +++ b/media/libstagefright/omx/1.0/OmxStore.cpp @@ -61,10 +61,7 @@ OmxStore::OmxStore( role.role = rolePair.first; role.type = rolePair.second.type; role.isEncoder = rolePair.second.isEncoder; - // TODO: Currently, preferPlatformNodes information is not available in - // the xml file. Once we have a way to provide this information, it - // should be parsed properly. - role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0; + role.preferPlatformNodes = false; // deprecated and ignored, using rank instead hidl_vec& nodeList = role.nodes; nodeList.resize(rolePair.second.nodeList.size()); size_t j = 0; -- GitLab From 2df13053ea33922136c5deb4b07ea5a51232e64b Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:36:30 -0800 Subject: [PATCH 0918/1530] stagefright: add method to MediaCodecInfoWriter to find existing info This is used to update existing info - e.g. add new aliases. Bug: 119631295 Change-Id: I7c45f558a507e9c145f26c1f27f6e2adb7b4e4b0 --- media/libstagefright/MediaCodecListWriter.cpp | 10 ++++++++++ .../include/media/stagefright/MediaCodecListWriter.h | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/media/libstagefright/MediaCodecListWriter.cpp b/media/libstagefright/MediaCodecListWriter.cpp index b32e470d7c..c4fb19946d 100644 --- a/media/libstagefright/MediaCodecListWriter.cpp +++ b/media/libstagefright/MediaCodecListWriter.cpp @@ -37,6 +37,16 @@ std::unique_ptr new MediaCodecInfoWriter(info.get())); } +std::unique_ptr + MediaCodecListWriter::findMediaCodecInfo(const char *name) { + for (const sp &info : mCodecInfos) { + if (!strcmp(info->getCodecName(), name)) { + return std::unique_ptr(new MediaCodecInfoWriter(info.get())); + } + } + return nullptr; +} + void MediaCodecListWriter::writeGlobalSettings( const sp &globalSettings) const { for (const std::pair &kv : mGlobalSettings) { diff --git a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h index 59f57c7f7c..f53b23e95c 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h @@ -48,6 +48,13 @@ struct MediaCodecListWriter { * added `MediaCodecInfo` object. */ std::unique_ptr addMediaCodecInfo(); + /** + * Find an existing `MediaCodecInfo` object for a codec name and return a + * `MediaCodecInfoWriter` object associated with the found added `MediaCodecInfo`. + * + * @return The `MediaCodecInfoWriter` object if found, or nullptr if not found. + */ + std::unique_ptr findMediaCodecInfo(const char *codecName); private: MediaCodecListWriter() = default; -- GitLab From 1512c75c170d11b7f155ea62cba4cf6774d5925b Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:38:24 -0800 Subject: [PATCH 0919/1530] stagefright: MediaCodecsXmlParser: add support for parsing rank Rank is supplied in a limit: This overrides the rank provided by the component stores (C2Store, or default values for OMX). Bug: 119631295 Change-Id: I01cfcaf1dcdb60f8bd6f907ebec0ed3f4e3b117b --- .../xmlparser/MediaCodecsXmlParser.cpp | 20 +++++++++++++++++++ .../xmlparser/MediaCodecsXmlParser.h | 1 + 2 files changed, 21 insertions(+) diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp index 6e541ba4f0..7046f6109e 100644 --- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp +++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp @@ -502,6 +502,7 @@ status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( const char *name = nullptr; const char *type = nullptr; const char *update = nullptr; + const char *rank = nullptr; size_t i = 0; while (attrs[i] != nullptr) { @@ -523,6 +524,12 @@ status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( return BAD_VALUE; } update = attrs[i]; + } else if (strEq(attrs[i], "rank")) { + if (attrs[++i] == nullptr) { + ALOGE("addMediaCodecFromAttributes: rank is null"); + return BAD_VALUE; + } + rank = attrs[i]; } else { ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]); return BAD_VALUE; @@ -579,6 +586,15 @@ status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( } } + if (rank != nullptr) { + if (!mCurrentCodec->second.rank.empty() && mCurrentCodec->second.rank != rank) { + ALOGE("addMediaCodecFromAttributes: code \"%s\" rank changed from \"%s\" to \"%s\"", + name, mCurrentCodec->second.rank.c_str(), rank); + return BAD_VALUE; + } + mCurrentCodec->second.rank = rank; + } + return OK; } @@ -1035,6 +1051,7 @@ void MediaCodecsXmlParser::generateRoleMap() const { const auto& codecName = codec.first; bool isEncoder = codec.second.isEncoder; size_t order = codec.second.order; + std::string rank = codec.second.rank; const auto& typeMap = codec.second.typeMap; for (const auto& type : typeMap) { const auto& typeName = type.first; @@ -1090,6 +1107,9 @@ void MediaCodecsXmlParser::generateRoleMap() const { nodeInfo.attributeList.push_back(Attribute{quirk, "present"}); } } + if (!rank.empty()) { + nodeInfo.attributeList.push_back(Attribute{"rank", rank}); + } nodeList->insert(std::make_pair( std::move(order), std::move(nodeInfo))); } diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h index fd949da3c0..7a986b7a72 100644 --- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h +++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h @@ -66,6 +66,7 @@ public: QuirkSet quirkSet; ///< Set of quirks requested by this codec TypeMap typeMap; ///< Map of types supported by this codec std::vector aliases; ///< Name aliases for this codec + std::string rank; ///< Rank of this codec. This is a numeric string. }; typedef std::pair Codec; -- GitLab From 8e577c931805a5974345587efffb85609b94f21f Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 4 Feb 2019 20:11:51 -0800 Subject: [PATCH 0920/1530] stagefright: find codecs by their aliases as well Bug: 119631295 Change-Id: Iec0cc3d29270676f89dddb74c443da3f8a3ccd04 --- media/libstagefright/MediaCodecList.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index 641328308c..b3bf4f0aa9 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -273,10 +273,17 @@ ssize_t MediaCodecList::findCodecByType( } ssize_t MediaCodecList::findCodecByName(const char *name) const { + Vector aliases; for (size_t i = 0; i < mCodecInfos.size(); ++i) { if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) { return i; } + mCodecInfos[i]->getAliases(&aliases); + for (const AString &alias : aliases) { + if (alias == name) { + return i; + } + } } return -ENOENT; -- GitLab From 4fb18c88b54190e235edb3c995ff77b8ddcb8522 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 1 Feb 2019 14:58:15 -0800 Subject: [PATCH 0921/1530] stagefright: list only the first codec for a given name Now that we allow multiple codec stores and aliases, a codec name may appear in multiple stores. List only the first codec for a given name (with the lowest rank). Allow this logic to be disabled for debugging via setprop debug.stagefright.dedupe-codecs 0. Bug: 119631295 Change-Id: I05f8ed86075a263e8becf80433018447f2f18361 --- media/libstagefright/MediaCodecList.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp index b3bf4f0aa9..3d58d4b54d 100644 --- a/media/libstagefright/MediaCodecList.cpp +++ b/media/libstagefright/MediaCodecList.cpp @@ -224,6 +224,21 @@ MediaCodecList::MediaCodecList(std::vector builders) return info1 == nullptr || (info2 != nullptr && info1->getRank() < info2->getRank()); }); + + // remove duplicate entries + bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true); + if (dedupe) { + std::set codecsSeen; + for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) { + std::string codecName = (*it)->getCodecName(); + if (codecsSeen.count(codecName) == 0) { + codecsSeen.emplace(codecName); + it++; + } else { + it = mCodecInfos.erase(it); + } + } + } } MediaCodecList::~MediaCodecList() { -- GitLab From db5751f214bc7e25b525883bdf68e7f48dd6805c Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 17:01:49 -0800 Subject: [PATCH 0922/1530] codec2: Codec2InfoBuilder rework - no longer build OMX codecs as part of codec2 list - move profile-level and color format getters to utility methods - support separate XML entries for store-advertised aliases - support rank specified in XML Bug: 119631295 Change-Id: I3f0cc1ddfd96c1da469209c4d717d7130996a3f3 --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 749 +++++++------------- 1 file changed, 241 insertions(+), 508 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 5f0dd0b637..ead0a9bd3b 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -68,262 +68,146 @@ bool hasSuffix(const std::string& s, const char* suffix) { s.compare(s.size() - suffixLen, suffixLen, suffix) == 0; } -// Constants from ACodec -constexpr OMX_U32 kPortIndexInput = 0; -constexpr OMX_U32 kPortIndexOutput = 1; -constexpr OMX_U32 kMaxIndicesToCheck = 32; - -status_t queryOmxCapabilities( - const char* name, const char* mediaType, bool isEncoder, - MediaCodecInfo::CapabilitiesWriter* caps) { - - const char *role = GetComponentRole(isEncoder, mediaType); - if (role == nullptr) { - return BAD_VALUE; - } - - using namespace ::android::hardware::media::omx::V1_0; - using ::android::hardware::Return; - using ::android::hardware::Void; - using ::android::hardware::hidl_vec; - using ::android::hardware::media::omx::V1_0::utils::LWOmxNode; - - sp omx = IOmx::getService(); - if (!omx) { - ALOGW("Could not obtain IOmx service."); - return NO_INIT; - } - - struct Observer : IOmxObserver { - virtual Return onMessages(const hidl_vec&) override { - return Void(); - } +void addSupportedProfileLevels( + std::shared_ptr intf, + MediaCodecInfo::CapabilitiesWriter *caps, + const Traits& trait, const std::string &mediaType) { + std::shared_ptr mapper = + C2Mapper::GetProfileLevelMapper(trait.mediaType); + // if we don't know the media type, pass through all values unmapped + + // TODO: we cannot find levels that are local 'maxima' without knowing the coding + // e.g. H.263 level 45 and level 30 could be two values for highest level as + // they don't include one another. For now we use the last supported value. + bool encoder = trait.kind == C2Component::KIND_ENCODER; + C2StreamProfileLevelInfo pl(encoder /* output */, 0u); + std::vector profileQuery = { + C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile)) }; - sp observer = new Observer(); - Status status; - sp tOmxNode; - Return transStatus = omx->allocateNode( - name, observer, - [&status, &tOmxNode](Status s, const sp& n) { - status = s; - tOmxNode = n; - }); - if (!transStatus.isOk()) { - ALOGW("IOmx::allocateNode -- transaction failed."); - return NO_INIT; - } - if (status != Status::OK) { - ALOGW("IOmx::allocateNode -- error returned: %d.", - static_cast(status)); - return NO_INIT; + c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK); + ALOGV("query supported profiles -> %s | %s", asString(err), asString(profileQuery[0].status)); + if (err != C2_OK || profileQuery[0].status != C2_OK) { + return; } - sp omxNode = new LWOmxNode(tOmxNode); - - status_t err = SetComponentRole(omxNode, role); - if (err != OK) { - omxNode->freeNode(); - ALOGW("Failed to SetComponentRole: component = %s, role = %s.", - name, role); - return err; + // we only handle enumerated values + if (profileQuery[0].values.type != C2FieldSupportedValues::VALUES) { + return; } - bool isVideo = hasPrefix(mediaType, "video/") == 0; - bool isImage = hasPrefix(mediaType, "image/") == 0; - - if (isVideo || isImage) { - OMX_VIDEO_PARAM_PROFILELEVELTYPE param; - InitOMXParams(¶m); - param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput; - - for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) { - param.nProfileIndex = index; - status_t err = omxNode->getParameter( - OMX_IndexParamVideoProfileLevelQuerySupported, - ¶m, sizeof(param)); - if (err != OK) { + // determine if codec supports HDR + bool supportsHdr = false; + bool supportsHdr10Plus = false; + + std::vector> paramDescs; + c2_status_t err1 = intf->querySupportedParams(¶mDescs); + if (err1 == C2_OK) { + for (const std::shared_ptr &desc : paramDescs) { + switch ((uint32_t)desc->index()) { + case C2StreamHdr10PlusInfo::output::PARAM_TYPE: + supportsHdr10Plus = true; break; - } - caps->addProfileLevel(param.eProfile, param.eLevel); - - // AVC components may not list the constrained profiles explicitly, but - // decoders that support a profile also support its constrained version. - // Encoders must explicitly support constrained profiles. - if (!isEncoder && strcasecmp(mediaType, MEDIA_MIMETYPE_VIDEO_AVC) == 0) { - if (param.eProfile == OMX_VIDEO_AVCProfileHigh) { - caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel); - } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) { - caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel); - } - } - - if (index == kMaxIndicesToCheck) { - ALOGW("[%s] stopping checking profiles after %u: %x/%x", - name, index, - param.eProfile, param.eLevel); - } - } - - // Color format query - // return colors in the order reported by the OMX component - // prefix "flexible" standard ones with the flexible equivalent - OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; - InitOMXParams(&portFormat); - portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput; - for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) { - portFormat.nIndex = index; - status_t err = omxNode->getParameter( - OMX_IndexParamVideoPortFormat, - &portFormat, sizeof(portFormat)); - if (err != OK) { + case C2StreamHdrStaticInfo::output::PARAM_TYPE: + supportsHdr = true; break; - } - - OMX_U32 flexibleEquivalent; - if (IsFlexibleColorFormat( - omxNode, portFormat.eColorFormat, false /* usingNativeWindow */, - &flexibleEquivalent)) { - caps->addColorFormat(flexibleEquivalent); - } - caps->addColorFormat(portFormat.eColorFormat); - - if (index == kMaxIndicesToCheck) { - ALOGW("[%s] stopping checking formats after %u: %s(%x)", - name, index, - asString(portFormat.eColorFormat), portFormat.eColorFormat); - } - } - } else if (strcasecmp(mediaType, MEDIA_MIMETYPE_AUDIO_AAC) == 0) { - // More audio codecs if they have profiles. - OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param; - InitOMXParams(¶m); - param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput; - for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) { - param.nProfileIndex = index; - status_t err = omxNode->getParameter( - (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported, - ¶m, sizeof(param)); - if (err != OK) { + default: break; } - // For audio, level is ignored. - caps->addProfileLevel(param.eProfile, 0 /* level */); - - if (index == kMaxIndicesToCheck) { - ALOGW("[%s] stopping checking profiles after %u: %x", - name, index, - param.eProfile); - } } + } - // NOTE: Without Android extensions, OMX does not provide a way to query - // AAC profile support - if (param.nProfileIndex == 0) { - ALOGW("component %s doesn't support profile query.", name); + // For VP9, the static info is always propagated by framework. + supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9); + + for (C2Value::Primitive profile : profileQuery[0].values.values) { + pl.profile = (C2Config::profile_t)profile.ref(); + std::vector> failures; + err = intf->config({&pl}, C2_DONT_BLOCK, &failures); + ALOGV("set profile to %u -> %s", pl.profile, asString(err)); + std::vector levelQuery = { + C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level)) + }; + err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK); + ALOGV("query supported levels -> %s | %s", asString(err), asString(levelQuery[0].status)); + if (err != C2_OK || levelQuery[0].status != C2_OK + || levelQuery[0].values.type != C2FieldSupportedValues::VALUES + || levelQuery[0].values.values.size() == 0) { + continue; } - } - if (isVideo && !isEncoder) { - native_handle_t *sidebandHandle = nullptr; - if (omxNode->configureVideoTunnelMode( - kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) { - // tunneled playback includes adaptive playback - } else { - // tunneled playback is not supported - caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_TUNNELED_PLAYBACK); - if (omxNode->setPortMode( - kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK || - omxNode->prepareForAdaptivePlayback( - kPortIndexOutput, OMX_TRUE, - 1280 /* width */, 720 /* height */) != OK) { - // adaptive playback is not supported - caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_ADAPTIVE_PLAYBACK); + C2Value::Primitive level = levelQuery[0].values.values.back(); + pl.level = (C2Config::level_t)level.ref(); + ALOGV("supporting level: %u", pl.level); + int32_t sdkProfile, sdkLevel; + if (mapper && mapper->mapProfile(pl.profile, &sdkProfile) + && mapper->mapLevel(pl.level, &sdkLevel)) { + caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel); + // also list HDR profiles if component supports HDR + if (supportsHdr) { + auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(trait.mediaType); + if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) { + caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel); + } + if (supportsHdr10Plus) { + hdrMapper = C2Mapper::GetHdrProfileLevelMapper( + trait.mediaType, true /*isHdr10Plus*/); + if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) { + caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel); + } + } } + } else if (!mapper) { + caps->addProfileLevel(pl.profile, pl.level); } - } - if (isVideo && isEncoder) { - OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params; - InitOMXParams(¶ms); - params.nPortIndex = kPortIndexOutput; - - OMX_VIDEO_PARAM_INTRAREFRESHTYPE fallbackParams; - InitOMXParams(&fallbackParams); - fallbackParams.nPortIndex = kPortIndexOutput; - fallbackParams.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic; - - if (omxNode->getConfig( - (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh, - ¶ms, sizeof(params)) != OK && - omxNode->getParameter( - OMX_IndexParamVideoIntraRefresh, &fallbackParams, - sizeof(fallbackParams)) != OK) { - // intra refresh is not supported - caps->removeDetail(MediaCodecInfo::Capabilities::FEATURE_INTRA_REFRESH); + // for H.263 also advertise the second highest level if the + // codec supports level 45, as level 45 only covers level 10 + // TODO: move this to some form of a setting so it does not + // have to be here + if (mediaType == MIMETYPE_VIDEO_H263) { + C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED; + for (C2Value::Primitive v : levelQuery[0].values.values) { + C2Config::level_t level = (C2Config::level_t)v.ref(); + if (level < C2Config::LEVEL_H263_45 && level > nextLevel) { + nextLevel = level; + } + } + if (nextLevel != C2Config::LEVEL_UNUSED + && nextLevel != pl.level + && mapper + && mapper->mapProfile(pl.profile, &sdkProfile) + && mapper->mapLevel(nextLevel, &sdkLevel)) { + caps->addProfileLevel( + (uint32_t)sdkProfile, (uint32_t)sdkLevel); + } } } - - omxNode->freeNode(); - return OK; } -void buildOmxInfo(const MediaCodecsXmlParser& parser, - MediaCodecListWriter* writer) { - uint32_t omxRank = ::android::base::GetUintProperty( - "debug.stagefright.omx_default_rank", uint32_t(0x100)); - for (const MediaCodecsXmlParser::Codec& codec : parser.getCodecMap()) { - const std::string &name = codec.first; - if (!hasPrefix(codec.first, "OMX.")) { - continue; - } - const MediaCodecsXmlParser::CodecProperties &properties = codec.second; - bool encoder = properties.isEncoder; - std::unique_ptr info = - writer->addMediaCodecInfo(); - info->setName(name.c_str()); - info->setOwner("default"); - typename std::underlying_type::type attrs = 0; - if (encoder) { - attrs |= MediaCodecInfo::kFlagIsEncoder; +void addSupportedColorFormats( + std::shared_ptr intf, + MediaCodecInfo::CapabilitiesWriter *caps, + const Traits& trait, const std::string &mediaType) { + (void)intf; + + // TODO: get this from intf() as well, but how do we map them to + // MediaCodec color formats? + bool encoder = trait.kind == C2Component::KIND_ENCODER; + if (mediaType.find("video") != std::string::npos) { + // vendor video codecs prefer opaque format + if (trait.name.find("android") == std::string::npos) { + caps->addColorFormat(COLOR_FormatSurface); } - // NOTE: we don't support software-only codecs in OMX - if (!hasPrefix(name, "OMX.google.")) { - attrs |= MediaCodecInfo::kFlagIsVendor; - if (properties.quirkSet.find("attribute::software-codec") - == properties.quirkSet.end()) { - attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; - } - } - info->setAttributes(attrs); - info->setRank(omxRank); - // OMX components don't have aliases - for (const MediaCodecsXmlParser::Type &type : properties.typeMap) { - const std::string &mediaType = type.first; - std::unique_ptr caps = - info->addMediaType(mediaType.c_str()); - const MediaCodecsXmlParser::AttributeMap &attrMap = type.second; - for (const MediaCodecsXmlParser::Attribute& attr : attrMap) { - const std::string &key = attr.first; - const std::string &value = attr.second; - if (hasPrefix(key, "feature-") && - !hasPrefix(key, "feature-bitrate-modes")) { - caps->addDetail(key.c_str(), hasPrefix(value, "1") ? 1 : 0); - } else { - caps->addDetail(key.c_str(), value.c_str()); - } - } - status_t err = queryOmxCapabilities( - name.c_str(), - mediaType.c_str(), - encoder, - caps.get()); - if (err != OK) { - ALOGI("Failed to query capabilities for %s (media type: %s). Error: %d", - name.c_str(), - mediaType.c_str(), - static_cast(err)); - } + caps->addColorFormat(COLOR_FormatYUV420Flexible); + caps->addColorFormat(COLOR_FormatYUV420Planar); + caps->addColorFormat(COLOR_FormatYUV420SemiPlanar); + caps->addColorFormat(COLOR_FormatYUV420PackedPlanar); + caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar); + // framework video encoders must support surface format, though it is unclear + // that they will be able to map it if it is opaque + if (encoder && trait.name.find("android") != std::string::npos) { + caps->addColorFormat(COLOR_FormatSurface); } } } @@ -335,7 +219,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // properly. (Assume "full" behavior eventually.) // // debug.stagefright.ccodec supports 5 values. - // 0 - Only OMX components are available. + // 0 - No Codec 2.0 components are available. // 1 - Audio decoders and encoders with prefix "c2.android." are available // and ranked first. // All other components with prefix "c2.android." are available with @@ -366,306 +250,156 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { MediaCodecsXmlParser parser( MediaCodecsXmlParser::defaultSearchDirs, - option == 0 ? "media_codecs.xml" : - "media_codecs_c2.xml", - option == 0 ? "media_codecs_performance.xml" : - "media_codecs_performance_c2.xml"); + "media_codecs_c2.xml", + "media_codecs_performance_c2.xml"); if (parser.getParsingStatus() != OK) { ALOGD("XML parser no good"); return OK; } - bool surfaceTest(Codec2Client::CreateInputSurface()); - if (option == 0 || (option != 4 && !surfaceTest)) { - buildOmxInfo(parser, writer); - } - for (const Traits& trait : traits) { C2Component::rank_t rank = trait.rank; - std::shared_ptr intf = - Codec2Client::CreateInterfaceByName(trait.name.c_str()); - if (!intf || parser.getCodecMap().count(intf->getName()) == 0) { - ALOGD("%s not found in xml", trait.name.c_str()); - continue; - } - std::string canonName = intf->getName(); - - // TODO: Remove this block once all codecs are enabled by default. - switch (option) { - case 0: - continue; - case 1: - if (hasPrefix(canonName, "c2.vda.")) { - break; + // Interface must be accessible for us to list the component, and there also + // must be an XML entry for the codec. Codec aliases listed in the traits + // allow additional XML entries to be specified for each alias. These will + // be listed as separate codecs. If no XML entry is specified for an alias, + // those will be treated as an additional alias specified in the XML entry + // for the interface name. + std::vector nameAndAliases = trait.aliases; + nameAndAliases.insert(nameAndAliases.begin(), trait.name); + for (const std::string &nameOrAlias : nameAndAliases) { + bool isAlias = trait.name != nameOrAlias; + std::shared_ptr intf = + Codec2Client::CreateInterfaceByName(nameOrAlias.c_str()); + if (!intf) { + ALOGD("could not create interface for %s'%s'", + isAlias ? "alias " : "", + nameOrAlias.c_str()); + continue; } - if (hasPrefix(canonName, "c2.android.")) { - if (trait.domain == C2Component::DOMAIN_AUDIO) { + if (parser.getCodecMap().count(nameOrAlias) == 0) { + if (isAlias) { + std::unique_ptr baseCodecInfo = + writer->findMediaCodecInfo(trait.name.c_str()); + if (!baseCodecInfo) { + ALOGD("alias '%s' not found in xml but canonical codec info '%s' missing", + nameOrAlias.c_str(), + trait.name.c_str()); + } else { + ALOGD("alias '%s' not found in xml; use an XML tag for this", + nameOrAlias.c_str()); + // merge alias into existing codec + baseCodecInfo->addAlias(nameOrAlias.c_str()); + } + } else { + ALOGD("component '%s' not found in xml", trait.name.c_str()); + } + continue; + } + std::string canonName = trait.name; + + // TODO: Remove this block once all codecs are enabled by default. + switch (option) { + case 0: + continue; + case 1: + if (hasPrefix(canonName, "c2.vda.")) { + break; + } + if (hasPrefix(canonName, "c2.android.")) { + if (trait.domain == C2Component::DOMAIN_AUDIO) { + rank = 1; + break; + } + break; + } + if (hasSuffix(canonName, ".avc.decoder") || + hasSuffix(canonName, ".avc.encoder")) { + rank = std::numeric_limits::max(); + break; + } + continue; + case 2: + if (hasPrefix(canonName, "c2.vda.")) { + break; + } + if (hasPrefix(canonName, "c2.android.")) { rank = 1; break; } + if (hasSuffix(canonName, ".avc.decoder") || + hasSuffix(canonName, ".avc.encoder")) { + rank = std::numeric_limits::max(); + break; + } + continue; + case 3: + if (hasPrefix(canonName, "c2.android.")) { + rank = 1; + } break; } - if (hasSuffix(canonName, ".avc.decoder") || - hasSuffix(canonName, ".avc.encoder")) { - rank = std::numeric_limits::max(); - break; - } - continue; - case 2: - if (hasPrefix(canonName, "c2.vda.")) { - break; - } - if (hasPrefix(canonName, "c2.android.")) { - rank = 1; - break; - } - if (hasSuffix(canonName, ".avc.decoder") || - hasSuffix(canonName, ".avc.encoder")) { - rank = std::numeric_limits::max(); - break; - } - continue; - case 3: - if (hasPrefix(canonName, "c2.android.")) { - rank = 1; - } - break; - } - ALOGV("canonName = %s", canonName.c_str()); - std::unique_ptr codecInfo = writer->addMediaCodecInfo(); - codecInfo->setName(trait.name.c_str()); - codecInfo->setOwner(("codec2::" + trait.owner).c_str()); - const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(canonName); + ALOGV("adding codec entry for '%s'", nameOrAlias.c_str()); + std::unique_ptr codecInfo = writer->addMediaCodecInfo(); + codecInfo->setName(nameOrAlias.c_str()); + codecInfo->setOwner(("codec2::" + trait.owner).c_str()); + const MediaCodecsXmlParser::CodecProperties &codec = + parser.getCodecMap().at(nameOrAlias); - bool encoder = trait.kind == C2Component::KIND_ENCODER; - typename std::underlying_type::type attrs = 0; + bool encoder = trait.kind == C2Component::KIND_ENCODER; + typename std::underlying_type::type attrs = 0; - if (encoder) { - attrs |= MediaCodecInfo::kFlagIsEncoder; - } - if (trait.owner == "software") { - attrs |= MediaCodecInfo::kFlagIsSoftwareOnly; - } else { - attrs |= MediaCodecInfo::kFlagIsVendor; - if (trait.owner == "vendor-software") { + if (encoder) { + attrs |= MediaCodecInfo::kFlagIsEncoder; + } + if (trait.owner == "software") { attrs |= MediaCodecInfo::kFlagIsSoftwareOnly; - } else if (codec.quirkSet.find("attribute::software-codec") == codec.quirkSet.end()) { - attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } else { + attrs |= MediaCodecInfo::kFlagIsVendor; + if (trait.owner == "vendor-software") { + attrs |= MediaCodecInfo::kFlagIsSoftwareOnly; + } else if (codec.quirkSet.find("attribute::software-codec") + == codec.quirkSet.end()) { + attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated; + } } - } - codecInfo->setAttributes(attrs); - codecInfo->setRank(rank); - - for (const std::string &alias : codec.aliases) { - codecInfo->addAlias(alias.c_str()); - } - - for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) { - const std::string &mediaType = typeIt->first; - const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second; - std::unique_ptr caps = - codecInfo->addMediaType(mediaType.c_str()); - for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) { - std::string key, value; - std::tie(key, value) = *attrIt; - if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) { - caps->addDetail(key.c_str(), std::stoi(value)); - } else { - caps->addDetail(key.c_str(), value.c_str()); + codecInfo->setAttributes(attrs); + if (!codec.rank.empty()) { + uint32_t xmlRank; + char dummy; + if (sscanf(codec.rank.c_str(), "%u%c", &xmlRank, &dummy) == 1) { + rank = xmlRank; } } + codecInfo->setRank(rank); - bool gotProfileLevels = false; - if (intf) { - std::shared_ptr mapper = - C2Mapper::GetProfileLevelMapper(trait.mediaType); - // if we don't know the media type, pass through all values unmapped - - // TODO: we cannot find levels that are local 'maxima' without knowing the coding - // e.g. H.263 level 45 and level 30 could be two values for highest level as - // they don't include one another. For now we use the last supported value. - C2StreamProfileLevelInfo pl(encoder /* output */, 0u); - std::vector profileQuery = { - C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile)) - }; - - c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK); - ALOGV("query supported profiles -> %s | %s", - asString(err), asString(profileQuery[0].status)); - if (err == C2_OK && profileQuery[0].status == C2_OK) { - if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) { - std::vector> paramDescs; - c2_status_t err1 = intf->querySupportedParams(¶mDescs); - bool isHdr = false, isHdr10Plus = false; - if (err1 == C2_OK) { - for (const std::shared_ptr &desc : paramDescs) { - if ((uint32_t)desc->index() == - C2StreamHdr10PlusInfo::output::PARAM_TYPE) { - isHdr10Plus = true; - } else if ((uint32_t)desc->index() == - C2StreamHdrStaticInfo::output::PARAM_TYPE) { - isHdr = true; - } - } - } - // For VP9, the static info is always propagated by framework. - isHdr |= (mediaType == MIMETYPE_VIDEO_VP9); - - for (C2Value::Primitive profile : profileQuery[0].values.values) { - pl.profile = (C2Config::profile_t)profile.ref(); - std::vector> failures; - err = intf->config({&pl}, C2_DONT_BLOCK, &failures); - ALOGV("set profile to %u -> %s", pl.profile, asString(err)); - std::vector levelQuery = { - C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level)) - }; - err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK); - ALOGV("query supported levels -> %s | %s", - asString(err), asString(levelQuery[0].status)); - if (err == C2_OK && levelQuery[0].status == C2_OK) { - if (levelQuery[0].values.type == C2FieldSupportedValues::VALUES - && levelQuery[0].values.values.size() > 0) { - C2Value::Primitive level = levelQuery[0].values.values.back(); - pl.level = (C2Config::level_t)level.ref(); - ALOGV("supporting level: %u", pl.level); - int32_t sdkProfile, sdkLevel; - if (mapper && mapper->mapProfile(pl.profile, &sdkProfile) - && mapper->mapLevel(pl.level, &sdkLevel)) { - caps->addProfileLevel( - (uint32_t)sdkProfile, (uint32_t)sdkLevel); - gotProfileLevels = true; - if (isHdr) { - auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper( - trait.mediaType); - if (hdrMapper && hdrMapper->mapProfile( - pl.profile, &sdkProfile)) { - caps->addProfileLevel( - (uint32_t)sdkProfile, - (uint32_t)sdkLevel); - } - if (isHdr10Plus) { - hdrMapper = C2Mapper::GetHdrProfileLevelMapper( - trait.mediaType, true /*isHdr10Plus*/); - if (hdrMapper && hdrMapper->mapProfile( - pl.profile, &sdkProfile)) { - caps->addProfileLevel( - (uint32_t)sdkProfile, - (uint32_t)sdkLevel); - } - } - } - } else if (!mapper) { - caps->addProfileLevel(pl.profile, pl.level); - gotProfileLevels = true; - } - - // for H.263 also advertise the second highest level if the - // codec supports level 45, as level 45 only covers level 10 - // TODO: move this to some form of a setting so it does not - // have to be here - if (mediaType == MIMETYPE_VIDEO_H263) { - C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED; - for (C2Value::Primitive v : levelQuery[0].values.values) { - C2Config::level_t level = - (C2Config::level_t)v.ref(); - if (level < C2Config::LEVEL_H263_45 - && level > nextLevel) { - nextLevel = level; - } - } - if (nextLevel != C2Config::LEVEL_UNUSED - && nextLevel != pl.level - && mapper - && mapper->mapProfile(pl.profile, &sdkProfile) - && mapper->mapLevel(nextLevel, &sdkLevel)) { - caps->addProfileLevel( - (uint32_t)sdkProfile, (uint32_t)sdkLevel); - } - } - } - } - } - } - } + for (const std::string &alias : codec.aliases) { + ALOGV("adding alias '%s'", alias.c_str()); + codecInfo->addAlias(alias.c_str()); } - if (!gotProfileLevels) { - if (mediaType == MIMETYPE_VIDEO_VP9) { - if (encoder) { - caps->addProfileLevel(VP9Profile0, VP9Level41); - } else { - caps->addProfileLevel(VP9Profile0, VP9Level5); - caps->addProfileLevel(VP9Profile2, VP9Level5); - caps->addProfileLevel(VP9Profile2HDR, VP9Level5); - } - } else if (mediaType == MIMETYPE_VIDEO_AV1 && !encoder) { - caps->addProfileLevel(AV1Profile0, AV1Level2); - caps->addProfileLevel(AV1Profile0, AV1Level21); - caps->addProfileLevel(AV1Profile1, AV1Level22); - caps->addProfileLevel(AV1Profile1, AV1Level3); - caps->addProfileLevel(AV1Profile2, AV1Level31); - caps->addProfileLevel(AV1Profile2, AV1Level32); - } else if (mediaType == MIMETYPE_VIDEO_HEVC && !encoder) { - caps->addProfileLevel(HEVCProfileMain, HEVCMainTierLevel51); - caps->addProfileLevel(HEVCProfileMainStill, HEVCMainTierLevel51); - } else if (mediaType == MIMETYPE_VIDEO_VP8) { - if (encoder) { - caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0); - } else { - caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0); - } - } else if (mediaType == MIMETYPE_VIDEO_AVC) { - if (encoder) { - caps->addProfileLevel(AVCProfileBaseline, AVCLevel41); -// caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel41); - caps->addProfileLevel(AVCProfileMain, AVCLevel41); - } else { - caps->addProfileLevel(AVCProfileBaseline, AVCLevel52); - caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel52); - caps->addProfileLevel(AVCProfileMain, AVCLevel52); - caps->addProfileLevel(AVCProfileConstrainedHigh, AVCLevel52); - caps->addProfileLevel(AVCProfileHigh, AVCLevel52); - } - } else if (mediaType == MIMETYPE_VIDEO_MPEG4) { - if (encoder) { - caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level2); + for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) { + const std::string &mediaType = typeIt->first; + const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second; + std::unique_ptr caps = + codecInfo->addMediaType(mediaType.c_str()); + for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) { + std::string key, value; + std::tie(key, value) = *attrIt; + if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) { + int32_t intValue = 0; + // Ignore trailing bad characters and default to 0. + (void)sscanf(value.c_str(), "%d", &intValue); + caps->addDetail(key.c_str(), intValue); } else { - caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level3); + caps->addDetail(key.c_str(), value.c_str()); } - } else if (mediaType == MIMETYPE_VIDEO_H263) { - if (encoder) { - caps->addProfileLevel(H263ProfileBaseline, H263Level45); - } else { - caps->addProfileLevel(H263ProfileBaseline, H263Level30); - caps->addProfileLevel(H263ProfileBaseline, H263Level45); - caps->addProfileLevel(H263ProfileISWV2, H263Level30); - caps->addProfileLevel(H263ProfileISWV2, H263Level45); - } - } else if (mediaType == MIMETYPE_VIDEO_MPEG2 && !encoder) { - caps->addProfileLevel(MPEG2ProfileSimple, MPEG2LevelHL); - caps->addProfileLevel(MPEG2ProfileMain, MPEG2LevelHL); } - } - // TODO: get this from intf() as well, but how do we map them to - // MediaCodec color formats? - if (mediaType.find("video") != std::string::npos) { - // vendor video codecs prefer opaque format - if (trait.name.find("android") == std::string::npos) { - caps->addColorFormat(COLOR_FormatSurface); - } - caps->addColorFormat(COLOR_FormatYUV420Flexible); - caps->addColorFormat(COLOR_FormatYUV420Planar); - caps->addColorFormat(COLOR_FormatYUV420SemiPlanar); - caps->addColorFormat(COLOR_FormatYUV420PackedPlanar); - caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar); - // framework video encoders must support surface format, though it is unclear - // that they will be able to map it if it is opaque - if (encoder && trait.name.find("android") != std::string::npos) { - caps->addColorFormat(COLOR_FormatSurface); - } + addSupportedProfileLevels(intf, caps.get(), trait, mediaType); + addSupportedColorFormats(intf, caps.get(), trait, mediaType); } } } @@ -677,4 +411,3 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { extern "C" android::MediaCodecListBuilderBase *CreateBuilder() { return new android::Codec2InfoBuilder; } - -- GitLab From c9b4ca0dcef97bbcba195c22875d491f25851dae Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 31 Jan 2019 16:24:45 -0800 Subject: [PATCH 0923/1530] codec2: signal aliases in XML vs. C2Store AOSP codec aliases are meant to imitate OMX codec names, and this is best done through XML. Bug: 119631295 Bug: 112370870 Change-Id: I373db2f4c211d55056d397eb5a1dec8a37c60306 --- media/codec2/vndk/C2Store.cpp | 215 ++++++++---------- .../data/media_codecs_google_c2_audio.xml | 14 ++ .../data/media_codecs_google_c2_telephony.xml | 1 + .../data/media_codecs_google_c2_tv.xml | 1 + .../data/media_codecs_google_c2_video.xml | 11 + 5 files changed, 121 insertions(+), 121 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 0b674810fc..f07d9b0458 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -517,7 +517,6 @@ private: * * \note Only used by ComponentLoader. * - * \param alias[in] module alias * \param libPath[in] library path * * \retval C2_OK the component module has been successfully loaded @@ -527,7 +526,7 @@ private: * \retval C2_REFUSED permission denied to load the component module (unexpected) * \retval C2_TIMED_OUT could not load the module within the time limit (unexpected) */ - c2_status_t init(std::string alias, std::string libPath); + c2_status_t init(std::string libPath); virtual ~ComponentModule() override; @@ -570,7 +569,7 @@ private: std::shared_ptr localModule = mModule.lock(); if (localModule == nullptr) { localModule = std::make_shared(); - res = localModule->init(mAlias, mLibPath); + res = localModule->init(mLibPath); if (res == C2_OK) { mModule = localModule; } @@ -582,13 +581,12 @@ private: /** * Creates a component loader for a specific library path (or name). */ - ComponentLoader(std::string alias, std::string libPath) - : mAlias(alias), mLibPath(libPath) {} + ComponentLoader(std::string libPath) + : mLibPath(libPath) {} private: std::mutex mMutex; ///< mutex guarding the module std::weak_ptr mModule; ///< weak reference to the loaded module - std::string mAlias; ///< component alias std::string mLibPath; ///< library path }; @@ -624,9 +622,10 @@ private: }; /** - * Retrieves the component loader for a component. + * Retrieves the component module for a component. * - * \return a non-ref-holding pointer to the component loader. + * \param module pointer to a shared_pointer where the component module will be stored on + * success. * * \retval C2_OK the component loader has been successfully retrieved * \retval C2_NO_MEMORY not enough memory to locate the component loader @@ -640,16 +639,25 @@ private: * component but some components could not be loaded due to lack of * permissions) */ - c2_status_t findComponent(C2String name, ComponentLoader **loader); + c2_status_t findComponent(C2String name, std::shared_ptr *module); + + /** + * Loads each component module and discover its contents. + */ + void visitComponents(); + + std::mutex mMutex; ///< mutex guarding the component lists during construction + bool mVisited; ///< component modules visited + std::map mComponents; ///< path -> component module + std::map mComponentNameToPath; ///< name -> path + std::vector> mComponentList; - std::map mComponents; ///< map of name -> components - std::vector mComponentsList; ///< list of components std::shared_ptr mReflector; Interface mInterface; }; c2_status_t C2PlatformComponentStore::ComponentModule::init( - std::string alias, std::string libPath) { + std::string libPath) { ALOGV("in %s", __func__); ALOGV("loading dll"); mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE); @@ -684,10 +692,7 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( std::shared_ptr traits(new (std::nothrow) C2Component::Traits); if (traits) { - if (alias != intf->getName()) { - ALOGV("%s is alias to %s", alias.c_str(), intf->getName().c_str()); - } - traits->name = alias; // TODO: this needs to be intf->getName() once aliases are supported + traits->name = intf->getName(); C2ComponentKindSetting kind; C2ComponentDomainSetting domain; @@ -825,82 +830,45 @@ std::shared_ptr C2PlatformComponentStore::ComponentMo } C2PlatformComponentStore::C2PlatformComponentStore() - : mReflector(std::make_shared()), + : mVisited(false), + mReflector(std::make_shared()), mInterface(mReflector) { - auto emplace = [this](const char *alias, const char *libPath) { - // ComponentLoader is neither copiable nor movable, so it must be - // constructed in-place. Now ComponentLoader takes two arguments in - // constructor, so we need to use piecewise_construct to achieve this - // behavior. - mComponents.emplace( - std::piecewise_construct, - std::forward_as_tuple(alias), - std::forward_as_tuple(alias, libPath)); - mComponentsList.emplace_back(alias); + auto emplace = [this](const char *libPath) { + mComponents.emplace(libPath, libPath); }; + // TODO: move this also into a .so so it can be updated - emplace("c2.android.avc.decoder", "libcodec2_soft_avcdec.so"); - emplace("c2.android.avc.encoder", "libcodec2_soft_avcenc.so"); - emplace("c2.android.aac.decoder", "libcodec2_soft_aacdec.so"); - emplace("c2.android.aac.encoder", "libcodec2_soft_aacenc.so"); - emplace("c2.android.amrnb.decoder", "libcodec2_soft_amrnbdec.so"); - emplace("c2.android.amrnb.encoder", "libcodec2_soft_amrnbenc.so"); - emplace("c2.android.amrwb.decoder", "libcodec2_soft_amrwbdec.so"); - emplace("c2.android.amrwb.encoder", "libcodec2_soft_amrwbenc.so"); - emplace("c2.android.hevc.decoder", "libcodec2_soft_hevcdec.so"); - emplace("c2.android.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so"); - emplace("c2.android.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so"); - emplace("c2.android.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so"); - emplace("c2.android.h263.decoder", "libcodec2_soft_h263dec.so"); - emplace("c2.android.h263.encoder", "libcodec2_soft_h263enc.so"); - emplace("c2.android.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so"); - emplace("c2.android.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so"); - emplace("c2.android.mp3.decoder", "libcodec2_soft_mp3dec.so"); - emplace("c2.android.vorbis.decoder", "libcodec2_soft_vorbisdec.so"); - emplace("c2.android.opus.decoder", "libcodec2_soft_opusdec.so"); - emplace("c2.android.opus.encoder", "libcodec2_soft_opusenc.so"); - emplace("c2.android.vp8.decoder", "libcodec2_soft_vp8dec.so"); - emplace("c2.android.vp9.decoder", "libcodec2_soft_vp9dec.so"); - emplace("c2.android.vp8.encoder", "libcodec2_soft_vp8enc.so"); - emplace("c2.android.vp9.encoder", "libcodec2_soft_vp9enc.so"); - emplace("c2.android.av1.decoder", "libcodec2_soft_av1dec.so"); - emplace("c2.android.raw.decoder", "libcodec2_soft_rawdec.so"); - emplace("c2.android.flac.decoder", "libcodec2_soft_flacdec.so"); - emplace("c2.android.flac.encoder", "libcodec2_soft_flacenc.so"); - emplace("c2.android.gsm.decoder", "libcodec2_soft_gsmdec.so"); - emplace("c2.android.xaac.decoder", "libcodec2_soft_xaacdec.so"); - - // "Aliases" - // TODO: use aliases proper from C2Component::Traits - emplace("OMX.google.h264.decoder", "libcodec2_soft_avcdec.so"); - emplace("OMX.google.h264.encoder", "libcodec2_soft_avcenc.so"); - emplace("OMX.google.aac.decoder", "libcodec2_soft_aacdec.so"); - emplace("OMX.google.aac.encoder", "libcodec2_soft_aacenc.so"); - emplace("OMX.google.amrnb.decoder", "libcodec2_soft_amrnbdec.so"); - emplace("OMX.google.amrnb.encoder", "libcodec2_soft_amrnbenc.so"); - emplace("OMX.google.amrwb.decoder", "libcodec2_soft_amrwbdec.so"); - emplace("OMX.google.amrwb.encoder", "libcodec2_soft_amrwbenc.so"); - emplace("OMX.google.hevc.decoder", "libcodec2_soft_hevcdec.so"); - emplace("OMX.google.g711.alaw.decoder", "libcodec2_soft_g711alawdec.so"); - emplace("OMX.google.g711.mlaw.decoder", "libcodec2_soft_g711mlawdec.so"); - emplace("OMX.google.mpeg2.decoder", "libcodec2_soft_mpeg2dec.so"); - emplace("OMX.google.h263.decoder", "libcodec2_soft_h263dec.so"); - emplace("OMX.google.h263.encoder", "libcodec2_soft_h263enc.so"); - emplace("OMX.google.mpeg4.decoder", "libcodec2_soft_mpeg4dec.so"); - emplace("OMX.google.mpeg4.encoder", "libcodec2_soft_mpeg4enc.so"); - emplace("OMX.google.mp3.decoder", "libcodec2_soft_mp3dec.so"); - emplace("OMX.google.vorbis.decoder", "libcodec2_soft_vorbisdec.so"); - emplace("OMX.google.opus.decoder", "libcodec2_soft_opusdec.so"); - emplace("OMX.google.vp8.decoder", "libcodec2_soft_vp8dec.so"); - emplace("OMX.google.vp9.decoder", "libcodec2_soft_vp9dec.so"); - emplace("OMX.google.vp8.encoder", "libcodec2_soft_vp8enc.so"); - emplace("OMX.google.vp9.encoder", "libcodec2_soft_vp9enc.so"); - emplace("OMX.google.raw.decoder", "libcodec2_soft_rawdec.so"); - emplace("OMX.google.flac.decoder", "libcodec2_soft_flacdec.so"); - emplace("OMX.google.flac.encoder", "libcodec2_soft_flacenc.so"); - emplace("OMX.google.gsm.decoder", "libcodec2_soft_gsmdec.so"); - emplace("OMX.google.xaac.decoder", "libcodec2_soft_xaacdec.so"); + emplace("libcodec2_soft_aacdec.so"); + emplace("libcodec2_soft_aacenc.so"); + emplace("libcodec2_soft_amrnbdec.so"); + emplace("libcodec2_soft_amrnbenc.so"); + emplace("libcodec2_soft_amrwbdec.so"); + emplace("libcodec2_soft_amrwbenc.so"); + emplace("libcodec2_soft_av1dec.so"); + emplace("libcodec2_soft_avcdec.so"); + emplace("libcodec2_soft_avcenc.so"); + emplace("libcodec2_soft_flacdec.so"); + emplace("libcodec2_soft_flacenc.so"); + emplace("libcodec2_soft_g711alawdec.so"); + emplace("libcodec2_soft_g711mlawdec.so"); + emplace("libcodec2_soft_gsmdec.so"); + emplace("libcodec2_soft_h263dec.so"); + emplace("libcodec2_soft_h263enc.so"); + emplace("libcodec2_soft_hevcdec.so"); + emplace("libcodec2_soft_mp3dec.so"); + emplace("libcodec2_soft_mpeg2dec.so"); + emplace("libcodec2_soft_mpeg4dec.so"); + emplace("libcodec2_soft_mpeg4enc.so"); + emplace("libcodec2_soft_opusdec.so"); + emplace("libcodec2_soft_opusenc.so"); + emplace("libcodec2_soft_rawdec.so"); + emplace("libcodec2_soft_vorbisdec.so"); + emplace("libcodec2_soft_vp8dec.so"); + emplace("libcodec2_soft_vp8enc.so"); + emplace("libcodec2_soft_vp9dec.so"); + emplace("libcodec2_soft_vp9enc.so"); + emplace("libcodec2_soft_xaacdec.so"); } c2_status_t C2PlatformComponentStore::copyBuffer( @@ -923,47 +891,56 @@ c2_status_t C2PlatformComponentStore::config_sm( return mInterface.config(params, C2_MAY_BLOCK, failures); } -std::vector> C2PlatformComponentStore::listComponents() { - // This method SHALL return within 500ms. - std::vector> list; - for (const C2String &alias : mComponentsList) { - ComponentLoader &loader = mComponents.at(alias); +void C2PlatformComponentStore::visitComponents() { + std::lock_guard lock(mMutex); + if (mVisited) { + return; + } + for (auto &pathAndLoader : mComponents) { + const C2String &path = pathAndLoader.first; + ComponentLoader &loader = pathAndLoader.second; std::shared_ptr module; - c2_status_t res = loader.fetchModule(&module); - if (res == C2_OK) { + if (loader.fetchModule(&module) == C2_OK) { std::shared_ptr traits = module->getTraits(); if (traits) { - list.push_back(traits); + mComponentList.push_back(traits); + mComponentNameToPath.emplace(traits->name, path); + for (const C2String &alias : traits->aliases) { + mComponentNameToPath.emplace(alias, path); + } } } } - return list; + mVisited = true; } -c2_status_t C2PlatformComponentStore::findComponent(C2String name, ComponentLoader **loader) { - *loader = nullptr; - auto pos = mComponents.find(name); - // TODO: check aliases - if (pos == mComponents.end()) { - return C2_NOT_FOUND; +std::vector> C2PlatformComponentStore::listComponents() { + // This method SHALL return within 500ms. + visitComponents(); + return mComponentList; +} + +c2_status_t C2PlatformComponentStore::findComponent( + C2String name, std::shared_ptr *module) { + (*module).reset(); + visitComponents(); + + auto pos = mComponentNameToPath.find(name); + if (pos != mComponentNameToPath.end()) { + return mComponents.at(pos->second).fetchModule(module); } - *loader = &pos->second; - return C2_OK; + return C2_NOT_FOUND; } c2_status_t C2PlatformComponentStore::createComponent( C2String name, std::shared_ptr *const component) { // This method SHALL return within 100ms. component->reset(); - ComponentLoader *loader; - c2_status_t res = findComponent(name, &loader); + std::shared_ptr module; + c2_status_t res = findComponent(name, &module); if (res == C2_OK) { - std::shared_ptr module; - res = loader->fetchModule(&module); - if (res == C2_OK) { - // TODO: get a unique node ID - res = module->createComponent(0, component); - } + // TODO: get a unique node ID + res = module->createComponent(0, component); } return res; } @@ -972,15 +949,11 @@ c2_status_t C2PlatformComponentStore::createInterface( C2String name, std::shared_ptr *const interface) { // This method SHALL return within 100ms. interface->reset(); - ComponentLoader *loader; - c2_status_t res = findComponent(name, &loader); + std::shared_ptr module; + c2_status_t res = findComponent(name, &module); if (res == C2_OK) { - std::shared_ptr module; - res = loader->fetchModule(&module); - if (res == C2_OK) { - // TODO: get a unique node ID - res = module->createInterface(0, interface); - } + // TODO: get a unique node ID + res = module->createInterface(0, interface); } return res; } diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml index 88cd08d2c9..f664395b99 100644 --- a/media/libstagefright/data/media_codecs_google_c2_audio.xml +++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml @@ -17,51 +17,61 @@ + + + + + + + + + + @@ -69,24 +79,28 @@ + + + + diff --git a/media/libstagefright/data/media_codecs_google_c2_telephony.xml b/media/libstagefright/data/media_codecs_google_c2_telephony.xml index d1055b318a..950b092c8b 100644 --- a/media/libstagefright/data/media_codecs_google_c2_telephony.xml +++ b/media/libstagefright/data/media_codecs_google_c2_telephony.xml @@ -17,6 +17,7 @@ + diff --git a/media/libstagefright/data/media_codecs_google_c2_tv.xml b/media/libstagefright/data/media_codecs_google_c2_tv.xml index fa082c7276..1b00dc95b2 100644 --- a/media/libstagefright/data/media_codecs_google_c2_tv.xml +++ b/media/libstagefright/data/media_codecs_google_c2_tv.xml @@ -17,6 +17,7 @@ + diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml index c49789e378..5c2d96d2ae 100644 --- a/media/libstagefright/data/media_codecs_google_c2_video.xml +++ b/media/libstagefright/data/media_codecs_google_c2_video.xml @@ -17,6 +17,7 @@ + @@ -26,6 +27,7 @@ + @@ -34,6 +36,7 @@ + @@ -44,6 +47,7 @@ + @@ -54,6 +58,7 @@ + @@ -63,6 +68,7 @@ + @@ -84,12 +90,14 @@ + + @@ -100,6 +108,7 @@ + @@ -108,6 +117,7 @@ + @@ -118,6 +128,7 @@ + -- GitLab From c7c48f1b2522a639f09b2ed095109482809b47b0 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 16 Nov 2018 16:42:32 -0800 Subject: [PATCH 0924/1530] AudioMixer: Set initial volume to 0 for safer ramping Test: Basic audio sanity, AudioFlinger tee Bug: 117745106 Change-Id: I25f946a4700323ae7505ec582874939bbb012ec6 --- media/libaudioprocessing/AudioMixer.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 86777d6ca7..2c57db72a1 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -113,10 +113,10 @@ status_t AudioMixer::create( // Integer volume. // Currently integer volume is kept for the legacy integer mixer. // Will be removed when the legacy mixer path is removed. - t->volume[0] = UNITY_GAIN_INT; - t->volume[1] = UNITY_GAIN_INT; - t->prevVolume[0] = UNITY_GAIN_INT << 16; - t->prevVolume[1] = UNITY_GAIN_INT << 16; + t->volume[0] = 0; + t->volume[1] = 0; + t->prevVolume[0] = 0 << 16; + t->prevVolume[1] = 0 << 16; t->volumeInc[0] = 0; t->volumeInc[1] = 0; t->auxLevel = 0; @@ -124,10 +124,10 @@ status_t AudioMixer::create( t->prevAuxLevel = 0; // Floating point volume. - t->mVolume[0] = UNITY_GAIN_FLOAT; - t->mVolume[1] = UNITY_GAIN_FLOAT; - t->mPrevVolume[0] = UNITY_GAIN_FLOAT; - t->mPrevVolume[1] = UNITY_GAIN_FLOAT; + t->mVolume[0] = 0.f; + t->mVolume[1] = 0.f; + t->mPrevVolume[0] = 0.f; + t->mPrevVolume[1] = 0.f; t->mVolumeInc[0] = 0.; t->mVolumeInc[1] = 0.; t->mAuxLevel = 0.; -- GitLab From 038e8f8854dd8b7c77d63e5a1cf50e838f443b02 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 6 Feb 2019 19:05:14 -0800 Subject: [PATCH 0925/1530] Make KEY_MAX_PTS_GAP_TO_ENCODER and KEY_MAX_FPS_TO_ENCODER keys public These two keys are in public API now, keep ndk in sync. Also replaced strings with the defined keys in media code. bug: 80314065 Change-Id: Ic0d50c10d1f9ab464068223a1a235b95f59f4c98 --- media/codec2/sfplugin/CCodec.cpp | 15 ++++++++------- media/libmedia/NdkWrapper.cpp | 2 ++ media/libstagefright/ACodec.cpp | 18 +++++++++--------- media/libstagefright/MediaCodecSource.cpp | 19 ++++++++++--------- media/ndk/NdkMediaFormat.cpp | 2 ++ media/ndk/include/media/NdkMediaFormat.h | 2 ++ media/ndk/libmediandk.map.txt | 2 ++ 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 852d6d6b12..fd72ac2d30 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -722,13 +722,14 @@ void CCodec::configure(const sp &msg) { { config->mISConfig->mMinFps = 0; int64_t value; - if (msg->findInt64("repeat-previous-frame-after", &value) && value > 0) { + if (msg->findInt64(KEY_REPEAT_PREVIOUS_FRAME_AFTER, &value) && value > 0) { config->mISConfig->mMinFps = 1e6 / value; } - (void)msg->findFloat("max-fps-to-encoder", &config->mISConfig->mMaxFps); + (void)msg->findFloat( + KEY_MAX_FPS_TO_ENCODER, &config->mISConfig->mMaxFps); config->mISConfig->mMinAdjustedFps = 0; config->mISConfig->mFixedAdjustedFps = 0; - if (msg->findInt64("max-pts-gap-to-encoder", &value)) { + if (msg->findInt64(KEY_MAX_PTS_GAP_TO_ENCODER, &value)) { if (value < 0 && value >= INT32_MIN) { config->mISConfig->mFixedAdjustedFps = -1e6 / value; } else if (value > 0 && value <= INT32_MAX) { @@ -749,7 +750,7 @@ void CCodec::configure(const sp &msg) { config->mISConfig->mSuspended = false; config->mISConfig->mSuspendAtUs = -1; int32_t value; - if (msg->findInt32("create-input-buffers-suspended", &value) && value) { + if (msg->findInt32(KEY_CREATE_INPUT_SURFACE_SUSPENDED, &value) && value) { config->mISConfig->mSuspended = true; } } @@ -1372,7 +1373,7 @@ void CCodec::setParameters(const sp ¶ms) { */ if ((config->mDomain & (Config::IS_VIDEO | Config::IS_IMAGE)) && (config->mDomain & Config::IS_ENCODER) && config->mInputSurface && config->mISConfig) { - (void)params->findInt64("time-offset-us", &config->mISConfig->mTimeOffsetUs); + (void)params->findInt64(PARAMETER_KEY_OFFSET_TIME, &config->mISConfig->mTimeOffsetUs); if (params->findInt64("skip-frames-before", &config->mISConfig->mStartAtUs)) { config->mISConfig->mStopped = false; @@ -1381,10 +1382,10 @@ void CCodec::setParameters(const sp ¶ms) { } int32_t value; - if (params->findInt32("drop-input-frames", &value)) { + if (params->findInt32(PARAMETER_KEY_SUSPEND, &value)) { config->mISConfig->mSuspended = value; config->mISConfig->mSuspendAtUs = -1; - (void)params->findInt64("drop-start-time-us", &config->mISConfig->mSuspendAtUs); + (void)params->findInt64(PARAMETER_KEY_SUSPEND_TIME, &config->mISConfig->mSuspendAtUs); } (void)config->mInputSurface->configure(*config->mISConfig); diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp index 6dbc9b8874..81c44cd6b4 100644 --- a/media/libmedia/NdkWrapper.cpp +++ b/media/libmedia/NdkWrapper.cpp @@ -95,6 +95,7 @@ static const char *AMediaFormatKeyGroupInt32[] = { static const char *AMediaFormatKeyGroupInt64[] = { AMEDIAFORMAT_KEY_DURATION, + AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER, AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER, AMEDIAFORMAT_KEY_TIME_US, }; @@ -126,6 +127,7 @@ static const char *AMediaFormatKeyGroupRect[] = { static const char *AMediaFormatKeyGroupFloatInt32[] = { AMEDIAFORMAT_KEY_FRAME_RATE, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, + AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER, AMEDIAFORMAT_KEY_OPERATING_RATE, }; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index a1a2660fb6..894d56ad16 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -1820,20 +1821,19 @@ status_t ACodec::configureCodec( } if (!msg->findInt64( - "repeat-previous-frame-after", - &mRepeatFrameDelayUs)) { + KEY_REPEAT_PREVIOUS_FRAME_AFTER, &mRepeatFrameDelayUs)) { mRepeatFrameDelayUs = -1LL; } // only allow 32-bit value, since we pass it as U32 to OMX. - if (!msg->findInt64("max-pts-gap-to-encoder", &mMaxPtsGapUs)) { + if (!msg->findInt64(KEY_MAX_PTS_GAP_TO_ENCODER, &mMaxPtsGapUs)) { mMaxPtsGapUs = 0LL; } else if (mMaxPtsGapUs > INT32_MAX || mMaxPtsGapUs < INT32_MIN) { ALOGW("Unsupported value for max pts gap %lld", (long long) mMaxPtsGapUs); mMaxPtsGapUs = 0LL; } - if (!msg->findFloat("max-fps-to-encoder", &mMaxFps)) { + if (!msg->findFloat(KEY_MAX_FPS_TO_ENCODER, &mMaxFps)) { mMaxFps = -1; } @@ -1847,8 +1847,8 @@ status_t ACodec::configureCodec( } if (!msg->findInt32( - "create-input-buffers-suspended", - (int32_t*)&mCreateInputBuffersSuspended)) { + KEY_CREATE_INPUT_SURFACE_SUSPENDED, + (int32_t*)&mCreateInputBuffersSuspended)) { mCreateInputBuffersSuspended = false; } } @@ -7429,7 +7429,7 @@ status_t ACodec::setParameters(const sp ¶ms) { } int64_t timeOffsetUs; - if (params->findInt64("time-offset-us", &timeOffsetUs)) { + if (params->findInt64(PARAMETER_KEY_OFFSET_TIME, &timeOffsetUs)) { if (mGraphicBufferSource == NULL) { ALOGE("[%s] Invalid to set input buffer time offset without surface", mComponentName.c_str()); @@ -7465,7 +7465,7 @@ status_t ACodec::setParameters(const sp ¶ms) { } int32_t dropInputFrames; - if (params->findInt32("drop-input-frames", &dropInputFrames)) { + if (params->findInt32(PARAMETER_KEY_SUSPEND, &dropInputFrames)) { if (mGraphicBufferSource == NULL) { ALOGE("[%s] Invalid to set suspend without surface", mComponentName.c_str()); @@ -7473,7 +7473,7 @@ status_t ACodec::setParameters(const sp ¶ms) { } int64_t suspendStartTimeUs = -1; - (void) params->findInt64("drop-start-time-us", &suspendStartTimeUs); + (void) params->findInt64(PARAMETER_KEY_SUSPEND_TIME, &suspendStartTimeUs); status_t err = statusFromBinderStatus( mGraphicBufferSource->setSuspend(dropInputFrames != 0, suspendStartTimeUs)); diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index 0f758228ba..221bc2a86b 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -362,7 +363,7 @@ sp MediaCodecSource::Create( status_t MediaCodecSource::setInputBufferTimeOffset(int64_t timeOffsetUs) { sp msg = new AMessage(kWhatSetInputBufferTimeOffset, mReflector); - msg->setInt64("time-offset-us", timeOffsetUs); + msg->setInt64(PARAMETER_KEY_OFFSET_TIME, timeOffsetUs); return postSynchronouslyAndReturnError(msg); } @@ -490,7 +491,7 @@ status_t MediaCodecSource::initEncoder() { mCodecLooper->start(); if (mFlags & FLAG_USE_SURFACE_INPUT) { - mOutputFormat->setInt32("create-input-buffers-suspended", 1); + mOutputFormat->setInt32(KEY_CREATE_INPUT_SURFACE_SUSPENDED, 1); } AString outputMIME; @@ -673,9 +674,9 @@ void MediaCodecSource::resume(int64_t resumeStartTimeUs) { CHECK(mFlags & FLAG_USE_SURFACE_INPUT); if (mEncoder != NULL) { sp params = new AMessage; - params->setInt32("drop-input-frames", false); + params->setInt32(PARAMETER_KEY_SUSPEND, false); if (resumeStartTimeUs > 0) { - params->setInt64("drop-start-time-us", resumeStartTimeUs); + params->setInt64(PARAMETER_KEY_SUSPEND_TIME, resumeStartTimeUs); } mEncoder->setParameters(params); } @@ -795,7 +796,7 @@ status_t MediaCodecSource::onStart(MetaData *params) { if (mFlags & FLAG_USE_SURFACE_INPUT) { if (mEncoder != NULL) { sp params = new AMessage; - params->setInt32("drop-input-frames", false); + params->setInt32(PARAMETER_KEY_SUSPEND, false); if (startTimeUs >= 0) { params->setInt64("skip-frames-before", startTimeUs); } @@ -828,8 +829,8 @@ status_t MediaCodecSource::onStart(MetaData *params) { void MediaCodecSource::onPause(int64_t pauseStartTimeUs) { if ((mFlags & FLAG_USE_SURFACE_INPUT) && (mEncoder != NULL)) { sp params = new AMessage; - params->setInt32("drop-input-frames", true); - params->setInt64("drop-start-time-us", pauseStartTimeUs); + params->setInt32(PARAMETER_KEY_SUSPEND, true); + params->setInt64(PARAMETER_KEY_SUSPEND_TIME, pauseStartTimeUs); mEncoder->setParameters(params); } else { CHECK(mPuller != NULL); @@ -1092,12 +1093,12 @@ void MediaCodecSource::onMessageReceived(const sp &msg) { sp replyID; CHECK(msg->senderAwaitsResponse(&replyID)); status_t err = OK; - CHECK(msg->findInt64("time-offset-us", &mInputBufferTimeOffsetUs)); + CHECK(msg->findInt64(PARAMETER_KEY_OFFSET_TIME, &mInputBufferTimeOffsetUs)); // Propagate the timestamp offset to GraphicBufferSource. if (mFlags & FLAG_USE_SURFACE_INPUT) { sp params = new AMessage; - params->setInt64("time-offset-us", mInputBufferTimeOffsetUs); + params->setInt64(PARAMETER_KEY_OFFSET_TIME, mInputBufferTimeOffsetUs); err = mEncoder->setParameters(params); } diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index fcb706d07f..d20c981aaa 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -342,8 +342,10 @@ EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location"; EXPORT const char* AMEDIAFORMAT_KEY_LOOP = "loop"; EXPORT const char* AMEDIAFORMAT_KEY_LYRICIST = "lyricist"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE = "max-bitrate"; +EXPORT const char* AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size"; +EXPORT const char* AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER = "max-pts-gap-to-encoder"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width"; EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime"; EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 2551228c04..af91b957e8 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -214,6 +214,8 @@ extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOOP __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LYRICIST __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_PSSH __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index c50084eb7d..e45708ce10 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -105,8 +105,10 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_LOOP; # var introduced=29 AMEDIAFORMAT_KEY_LYRICIST; # var introduced=29 AMEDIAFORMAT_KEY_MAX_BIT_RATE; # var introduced=29 + AMEDIAFORMAT_KEY_MAX_FPS_TO_ENCODER; # var introduced=29 AMEDIAFORMAT_KEY_MAX_HEIGHT; # var introduced=21 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var introduced=21 + AMEDIAFORMAT_KEY_MAX_PTS_GAP_TO_ENCODER; # var introduced=29 AMEDIAFORMAT_KEY_MAX_WIDTH; # var introduced=21 AMEDIAFORMAT_KEY_MIME; # var introduced=21 AMEDIAFORMAT_KEY_MPEG_USER_DATA; # var introduced=28 -- GitLab From ba2cf0f2e39d8302babbda3a98127daaf2ea7376 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Wed, 12 Dec 2018 16:40:25 +0100 Subject: [PATCH 0926/1530] audiopolicy: fix dynamic device & explicit routing management While a playback is ongoing using explicit routing and the device is disconnected, setOutputDevices() will bail out if the new device is incompatible with the io profile. So the device will not be reset in the output. Moreover, if another device of the same type/address is connected, this device will have a new unique ID. If a track wants to use it as explicit, as the first playback/capture is still on going, the function findPreferredDevice will return the previous device id. This CL enforces: -setOutputDevices() to check if new device is not compatible AND previous devices are still available - When a device is disconnected, go over all clients to clear any preferred device referring to the device being removed. - Sanitize preferred device received by getForAttr() according to connected devices. Test: AudioPolicyTests --gtest_filter=DynamicAddressOutputDevice/DeviceConnectionTest.DeviceConnectionState/* Test: audio smoke tests Test: CTS tests for AudioTrack, AudioRecord, AudioRouting Change-Id: Ib3a2d54d902def0fcfc2a623c25bb92d65b1ab98 Signed-off-by: Francois Gaffie --- .../include/AudioInputDescriptor.h | 10 ++++++ .../include/AudioOutputDescriptor.h | 10 ++++++ .../src/AudioInputDescriptor.cpp | 13 ++++++++ .../src/AudioOutputDescriptor.cpp | 13 ++++++++ .../managerdefault/AudioPolicyManager.cpp | 31 +++++++++++++------ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index d4cfd1ebd0..803cfacc75 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -143,6 +143,16 @@ public: void trackEffectEnabled(const sp &effect, bool enabled); + /** + * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has + * been chosen as the preferred device by any client, the policy manager shall + * prevent from using this device any more by clearing all the session routes involving this + * device. + * In other words, the preferred device port id of these clients will be resetted to NONE. + * @param disconnectedDevice device to be disconnected + */ + void clearSessionRoutesForDevice(const sp &disconnectedDevice); + void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 6132bb4782..c84636e7e6 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -379,6 +379,16 @@ public: const sp& desc, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; + /** + * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has + * been chosen as the preferred device by any client, the policy manager shall + * prevent from using this device any more by clearing all the session routes involving this + * device. + * In other words, the preferred device port id of these clients will be resetted to NONE. + * @param disconnectedDevice device to be disconnected + */ + void clearSessionRoutesForDevice(const sp &disconnectedDevice); + /** * returns the A2DP output handle if it is open or 0 otherwise */ diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index c880e675be..1fa11231b8 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -511,6 +511,19 @@ void AudioInputCollection::trackEffectEnabled(const sp &effect } } +void AudioInputCollection::clearSessionRoutesForDevice( + const sp &disconnectedDevice) +{ + for (size_t i = 0; i < size(); i++) { + sp inputDesc = valueAt(i); + for (const auto& client : inputDesc->getClientIterable()) { + if (client->preferredDeviceId() == disconnectedDevice->getId()) { + client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE); + } + } + } +} + void AudioInputCollection::dump(String8 *dst) const { dst->append("\nInputs dump:\n"); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 1dfd88a20f..77e7add511 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -802,6 +802,19 @@ sp SwAudioOutputCollection::getOutputForClient(audio_po return 0; } +void SwAudioOutputCollection::clearSessionRoutesForDevice( + const sp &disconnectedDevice) +{ + for (size_t i = 0; i < size(); i++) { + sp outputDesc = valueAt(i); + for (const auto& client : outputDesc->getClientIterable()) { + if (client->preferredDeviceId() == disconnectedDevice->getId()) { + client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE); + } + } + } +} + void SwAudioOutputCollection::dump(String8 *dst) const { dst->append("\nOutputs dump:\n"); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ccec93f40a..2854a98d63 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -193,6 +193,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // remove device from available output devices mAvailableOutputDevices.remove(device); + mOutputs.clearSessionRoutesForDevice(device); + checkOutputsForDevice(device, state, outputs); // Reset active device codec @@ -929,8 +931,6 @@ status_t AudioPolicyManager::getOutputForAttrInt( const sp requestedDevice = mAvailableOutputDevices.getDeviceFromId(requestedPortId); - - status_t status = getAudioAttributes(resultAttr, attr, *stream); if (status != NO_ERROR) { return status; @@ -1045,6 +1045,14 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, audio_attributes_t resultAttr; bool isRequestedDeviceForExclusiveUse = false; std::vector> secondaryOutputDescs; + const sp requestedDevice = + mAvailableOutputDevices.getDeviceFromId(requestedPortId); + + // Prevent from storing invalid requested device id in clients + const audio_port_handle_t sanitizedRequestedPortId = + requestedDevice != nullptr ? requestedPortId : AUDIO_PORT_HANDLE_NONE; + *selectedDeviceId = sanitizedRequestedPortId; + status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid, config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse, &secondaryOutputDescs); @@ -1064,15 +1072,15 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, sp clientDesc = new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, - requestedPortId, *stream, + sanitizedRequestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), *flags, isRequestedDeviceForExclusiveUse, std::move(weakSecondaryOutputDescs)); sp outputDesc = mOutputs.valueFor(*output); outputDesc->addClient(clientDesc); - ALOGV("%s() returns output %d selectedDeviceId %d for port ID %d", __func__, - *output, *selectedDeviceId, *portId); + ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__, + *output, requestedPortId, *selectedDeviceId, *portId); return NO_ERROR; } @@ -1953,6 +1961,8 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; } else { + // Prevent from storing invalid requested device id in clients + requestedDeviceId = AUDIO_PORT_HANDLE_NONE; device = mEngine->getInputDeviceForAttributes(attributes, &policyMix); } if (device == nullptr) { @@ -5295,16 +5305,17 @@ uint32_t AudioPolicyManager::setOutputDevices(const sp& // filter devices according to output selected DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices); + DeviceVector prevDevices = outputDesc->devices(); // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current - // output profile - if (!devices.isEmpty() && filteredDevices.isEmpty()) { + // output profile or if new device is not supported AND previous device(s) is(are) still + // available (otherwise reset device must be done on the output) + if (!devices.isEmpty() && filteredDevices.isEmpty() && + !mAvailableOutputDevices.filter(prevDevices).empty()) { ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str()); return 0; } - DeviceVector prevDevices = outputDesc->devices(); - ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str()); if (!filteredDevices.isEmpty()) { @@ -5819,6 +5830,8 @@ void AudioPolicyManager::cleanUpForDevice(const sp& deviceDesc } } + mInputs.clearSessionRoutesForDevice(deviceDesc); + mHwModules.cleanUpForDevice(deviceDesc); } -- GitLab From 4ec1788b4f826bedebedb8d4bb7e957a8c5d14f5 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Thu, 24 Jan 2019 17:16:58 -0800 Subject: [PATCH 0927/1530] Camera: Enable Hal side stream reconfiguration queries Session parameter changes will by default trigger internal stream reconfiguration. If possible query Hal whether this sequence is required for specific parameter values. Bug: 122609098 Test: Manual using application Change-Id: I9eaa55b0a552d9753122c16f9470779e2ed8ffec --- .../device3/Camera3Device.cpp | 67 +++++++++++++++++-- .../libcameraservice/device3/Camera3Device.h | 3 + 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 82dfc0fba9..8be944c79d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -3878,7 +3878,8 @@ Camera3Device::HalInterface::HalInterface( bool useHalBufManager) : mHidlSession(session), mRequestMetadataQueue(queue), - mUseHalBufManager(useHalBufManager) { + mUseHalBufManager(useHalBufManager), + mIsReconfigurationQuerySupported(true) { // Check with hardware service manager if we can downcast these interfaces // Somewhat expensive, so cache the results at startup auto castResult_3_5 = device::V3_5::ICameraDeviceSession::castFrom(mHidlSession); @@ -3984,6 +3985,52 @@ status_t Camera3Device::HalInterface::constructDefaultRequestSettings( return res; } +bool Camera3Device::HalInterface::isReconfigurationRequired(CameraMetadata& oldSessionParams, + CameraMetadata& newSessionParams) { + // We do reconfiguration by default; + bool ret = true; + if ((mHidlSession_3_5 != nullptr) && mIsReconfigurationQuerySupported) { + android::hardware::hidl_vec oldParams, newParams; + camera_metadata_t* oldSessioMeta = const_cast( + oldSessionParams.getAndLock()); + camera_metadata_t* newSessioMeta = const_cast( + newSessionParams.getAndLock()); + oldParams.setToExternal(reinterpret_cast(oldSessioMeta), + get_camera_metadata_size(oldSessioMeta)); + newParams.setToExternal(reinterpret_cast(newSessioMeta), + get_camera_metadata_size(newSessioMeta)); + hardware::camera::common::V1_0::Status callStatus; + bool required; + auto hidlCb = [&callStatus, &required] (hardware::camera::common::V1_0::Status s, + bool requiredFlag) { + callStatus = s; + required = requiredFlag; + }; + auto err = mHidlSession_3_5->isReconfigurationRequired(oldParams, newParams, hidlCb); + oldSessionParams.unlock(oldSessioMeta); + newSessionParams.unlock(newSessioMeta); + if (err.isOk()) { + switch (callStatus) { + case hardware::camera::common::V1_0::Status::OK: + ret = required; + break; + case hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED: + mIsReconfigurationQuerySupported = false; + ret = true; + break; + default: + ALOGV("%s: Reconfiguration query failed: %d", __FUNCTION__, callStatus); + ret = true; + } + } else { + ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, err.description().c_str()); + ret = true; + } + } + + return ret; +} + status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t *sessionParams, camera3_stream_configuration *config, const std::vector& bufferSizes) { ATRACE_NAME("CameraHal::configureStreams"); @@ -5095,9 +5142,10 @@ bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& ATRACE_CALL(); bool updatesDetected = false; + CameraMetadata updatedParams(mLatestSessionParams); for (auto tag : mSessionParamKeys) { camera_metadata_ro_entry entry = settings.find(tag); - camera_metadata_entry lastEntry = mLatestSessionParams.find(tag); + camera_metadata_entry lastEntry = updatedParams.find(tag); if (entry.count > 0) { bool isDifferent = false; @@ -5126,17 +5174,26 @@ bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& if (!skipHFRTargetFPSUpdate(tag, entry, lastEntry)) { updatesDetected = true; } - mLatestSessionParams.update(entry); + updatedParams.update(entry); } } else if (lastEntry.count > 0) { // Value has been removed ALOGV("%s: Session parameter tag id %d removed", __FUNCTION__, tag); - mLatestSessionParams.erase(tag); + updatedParams.erase(tag); updatesDetected = true; } } - return updatesDetected; + bool reconfigureRequired; + if (updatesDetected) { + reconfigureRequired = mInterface->isReconfigurationRequired(mLatestSessionParams, + updatedParams); + mLatestSessionParams = updatedParams; + } else { + reconfigureRequired = false; + } + + return reconfigureRequired; } bool Camera3Device::RequestThread::threadLoop() { diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index e5a38bbf61..b2f0930b5b 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -309,6 +309,8 @@ class Camera3Device : status_t close(); void signalPipelineDrain(const std::vector& streamIds); + bool isReconfigurationRequired(CameraMetadata& oldSessionParams, + CameraMetadata& newSessionParams); // method to extract buffer's unique ID // return pair of (newlySeenBuffer?, bufferId) @@ -401,6 +403,7 @@ class Camera3Device : uint32_t mNextStreamConfigCounter = 1; const bool mUseHalBufManager; + bool mIsReconfigurationQuerySupported; }; sp mInterface; -- GitLab From 05e6b175c806aa4c8a3a99a46f7b0431458a0aa7 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 7 Feb 2019 14:46:26 -0800 Subject: [PATCH 0928/1530] aaudio_loopback: fix -n for sine test Allow -n to specify buffer size for -tm test. It was being forced to the size of the input buffer. Test: aaudio_loopback -tm -s20 -n2 Change-Id: I4b71238f17bb87c22602bf86e2b91cfa41e3ae55 --- .../examples/loopback/src/loopback.cpp | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index 75d425f6f0..6578156870 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -36,8 +36,11 @@ #include "LoopbackAnalyzer.h" #include "../../utils/AAudioExampleUtils.h" -// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo -#define APP_VERSION "0.4.00" +// V0.4.00 = rectify and low-pass filter the echos, auto-correlate entire echo +// V0.4.01 = add -h hang option +// fix -n option to set output buffer for -tm +// plot first glitch +#define APP_VERSION "0.4.01" // Tag for machine readable results as property = value pairs #define RESULT_TAG "RESULT: " @@ -396,7 +399,7 @@ int main(int argc, const char **argv) int32_t requestedInputCapacity = AAUDIO_UNSPECIFIED; aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; - int32_t outputFramesPerBurst = 0; + int32_t outputFramesPerBurst = 0; aaudio_format_t actualOutputFormat = AAUDIO_FORMAT_INVALID; int32_t actualSampleRate = 0; @@ -476,6 +479,8 @@ int main(int argc, const char **argv) int32_t timeMillis = 0; int32_t recordingDuration = std::min(60 * 5, requestedDuration); + int32_t requestedOutputBursts = argParser.getNumberOfBursts(); + switch(testMode) { case TEST_SINE_MAGNITUDE: loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer; @@ -541,20 +546,19 @@ int main(int argc, const char **argv) } inputStream = loopbackData.inputStream = recorder.getStream(); - argParser.compareWithStream(inputStream); - { int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream); (void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity); - if (testMode == TEST_SINE_MAGNITUDE) { + if (testMode == TEST_SINE_MAGNITUDE + && requestedOutputBursts == AAUDIO_UNSPECIFIED) { result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity); if (result < 0) { fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames(output) returned %d\n", result); goto finish; } else { - printf("Output buffer size set to match input capacity = %d frames.\n", result); + printf("Output buffer size set to match input capacity = %d frames!\n", result); } } @@ -565,6 +569,8 @@ int main(int argc, const char **argv) } } + argParser.compareWithStream(inputStream); + // ------- Setup loopbackData ----------------------------- loopbackData.actualInputFormat = AAudioStream_getFormat(inputStream); -- GitLab From f1533004baafc2eacfe6caea0ebc6e462b74abaa Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 7 Feb 2019 13:04:28 -0800 Subject: [PATCH 0929/1530] aaudio: fix hang option in write_sine_callback Was colliding with -z option for capacity. Also print data size in output buffer. Test: adb shell write_sine_callback -pl -n2 -h8 -s10 Change-Id: If64f0876d3a2e1a95426dddf122693dbf3a314cd --- .../examples/utils/AAudioSimplePlayer.h | 12 +++--- .../write_sine/src/write_sine_callback.cpp | 42 +++++++++++++------ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h index 1645986418..4373fa9b9a 100644 --- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h +++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h @@ -32,8 +32,6 @@ // Arbitrary period for glitches #define FORCED_UNDERRUN_PERIOD_FRAMES (2 * 48000) -// How long to sleep in a callback to cause an intentional glitch. For testing. -#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000) #define MAX_TIMESTAMPS 16 @@ -275,7 +273,7 @@ typedef struct SineThreadedData_s { int scheduler = 0; bool schedulerChecked = false; - bool forceUnderruns = false; + int32_t hangTimeMSec = 0; AAudioSimplePlayer simplePlayer; int32_t callbackCount = 0; @@ -327,10 +325,12 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( sineData->setupSineSweeps(); } - if (sineData->forceUnderruns) { + if (sineData->hangTimeMSec > 0) { if (sineData->framesTotal > sineData->nextFrameToGlitch) { - usleep(FORCED_UNDERRUN_SLEEP_MICROS); - printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal); + usleep(sineData->hangTimeMSec * 1000); + printf("Hang callback at %lld frames for %d msec\n", + (long long) sineData->framesTotal, + sineData->hangTimeMSec); sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES; } } diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp index 7a481534d4..2b05f10b31 100644 --- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp @@ -26,11 +26,14 @@ #include #include #include + #include "AAudioExampleUtils.h" #include "AAudioSimplePlayer.h" #include "AAudioArgsParser.h" -#define APP_VERSION "0.1.5" +#define APP_VERSION "0.1.6" + +constexpr int32_t kDefaultHangTimeMSec = 10; /** * Open stream, play some sine waves, then close the stream. @@ -41,7 +44,7 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, int32_t loopCount, int32_t prefixToneMsec, - bool forceUnderruns) + int32_t hangTimeMSec) { SineThreadedData_t myData; AAudioSimplePlayer &player = myData.simplePlayer; @@ -53,10 +56,12 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, printf("----------------------- run complete test --------------------------\n"); myData.schedulerChecked = false; myData.callbackCount = 0; - myData.forceUnderruns = forceUnderruns; // test AAudioStream_getXRunCount() + myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount() result = player.open(argParser, - SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData); + SimplePlayerDataCallbackProc, + SimplePlayerErrorCallbackProc, + &myData); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.open() returned %s\n", AAudio_convertResultToText(result)); @@ -115,12 +120,17 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, int64_t millis = (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND; result = myData.waker.get(); + const int32_t framesWritten = (int32_t) AAudioStream_getFramesWritten(player.getStream()); + const int32_t framesRead = (int32_t) AAudioStream_getFramesRead(player.getStream()); + const int32_t xruns = AAudioStream_getXRunCount(player.getStream()); printf(" waker result = %d, at %6d millis" - ", second = %3d, framesWritten = %8d, underruns = %d\n", + ", second = %3d, frames written %8d - read %8d = %8d, underruns = %d\n", result, (int) millis, second, - (int) AAudioStream_getFramesWritten(player.getStream()), - (int) AAudioStream_getXRunCount(player.getStream())); + framesWritten, + framesRead, + framesWritten - framesRead, + xruns); if (result != AAUDIO_OK) { disconnected = (result == AAUDIO_ERROR_DISCONNECTED); bailOut = true; @@ -210,7 +220,9 @@ static void usage() { AAudioArgsParser::usage(); printf(" -l{count} loopCount start/stop, every other one is silent\n"); printf(" -t{msec} play a high pitched tone at the beginning\n"); - printf(" -z force periodic underruns by sleeping in callback\n"); + printf(" -h{msec} force periodic underruns by hanging in callback\n"); + printf(" If no value specified then %d used.\n", + kDefaultHangTimeMSec); } int main(int argc, const char **argv) @@ -219,13 +231,14 @@ int main(int argc, const char **argv) aaudio_result_t result; int32_t loopCount = 1; int32_t prefixToneMsec = 0; - bool forceUnderruns = false; + int32_t hangTimeMSec = 0; // Make printf print immediately so that debug info is not stuck // in a buffer if we hang or crash. setvbuf(stdout, nullptr, _IONBF, (size_t) 0); - printf("%s - Play a sine sweep using an AAudio callback V%s\n", argv[0], APP_VERSION); + printf("%s - Play a sine sweep using an AAudio callback V%s\n", + argv[0], APP_VERSION); for (int i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -240,8 +253,10 @@ int main(int argc, const char **argv) case 't': prefixToneMsec = atoi(&arg[2]); break; - case 'z': - forceUnderruns = true; // Zzzzzzz + case 'h': + hangTimeMSec = (arg[2]) // value specified? + ? atoi(&arg[2]) + : kDefaultHangTimeMSec; break; default: usage(); @@ -257,7 +272,8 @@ int main(int argc, const char **argv) } // Keep looping until we can complete the test without disconnecting. - while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec, forceUnderruns)) + while((result = testOpenPlayClose(argParser, loopCount, + prefixToneMsec, hangTimeMSec)) == AAUDIO_ERROR_DISCONNECTED); return (result) ? EXIT_FAILURE : EXIT_SUCCESS; -- GitLab From 3fff3f502d536f8a35744c859cc93823ad29a8cc Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 5 Feb 2019 09:24:40 -0800 Subject: [PATCH 0930/1530] AudioPolicy: make mixes const correct Test: compile Bug: 111453086 Change-Id: Ic3b1e414bcf0e2de20bdecebdd71920030ecc1ec Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/include/AudioPolicyMix.h | 4 ++-- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 1abce6f93d..bfd5ab408b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -43,12 +43,12 @@ public: android::AudioMix *getMix(); - void setMix(AudioMix &mix); + void setMix(const AudioMix &mix); void dump(String8 *dst, int spaces, int index) const; private: - AudioMix mMix; // Audio policy mix descriptor + AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 6b6d9d274c..7e33488973 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -42,7 +42,7 @@ void AudioPolicyMix::clearOutput() mOutput.clear(); } -void AudioPolicyMix::setMix(AudioMix &mix) +void AudioPolicyMix::setMix(const AudioMix &mix) { mMix = mix; } @@ -378,7 +378,7 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + const AudioMix *mix = policyMix->getMix(); ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); } #endif @@ -435,7 +435,7 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { // for each player mix: remove existing rules that match or exclude this uid for (size_t i = 0; i < size(); i++) { bool foundUidRule = false; - AudioMix *mix = valueAt(i)->getMix(); + const AudioMix *mix = valueAt(i)->getMix(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } @@ -463,7 +463,7 @@ status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid, // for each player mix: find rules that don't exclude this uid, and add the device to the list for (size_t i = 0; i < size(); i++) { bool ruleAllowsUid = true; - AudioMix *mix = valueAt(i)->getMix(); + const AudioMix *mix = valueAt(i)->getMix(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } -- GitLab From f4f5c817ed4ba6f10287cb37b9f71f6c27723359 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 5 Feb 2019 09:28:02 -0800 Subject: [PATCH 0931/1530] AudioPolicy: make input attributes parameter const& Test: compile Bug: 111453086 Change-Id: I3b08d11481471dc080ed2ab89ae59e6268542c25 Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/include/AudioPolicyMix.h | 4 ++-- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 1abce6f93d..9aee574dc9 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -71,7 +71,7 @@ public: * @param[out] desc to return if an primary output could be found. * @param[out] secondaryDesc other desc that the audio should be routed to. */ - status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid, + status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid, sp &primaryDesc, std::vector> *secondaryDescs); @@ -102,7 +102,7 @@ public: private: enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX }; MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex, - audio_attributes_t attributes, uid_t uid); + const audio_attributes_t& attributes, uid_t uid); }; } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 6b6d9d274c..18e2f31a63 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -157,7 +157,7 @@ void AudioPolicyMixCollection::closeOutput(sp &desc) } status_t AudioPolicyMixCollection::getOutputForAttr( - audio_attributes_t attributes, uid_t uid, sp &primaryDesc, + const audio_attributes_t& attributes, uid_t uid, sp &primaryDesc, std::vector> *secondaryDescs) { ALOGV("getOutputForAttr() querying %zu mixes:", size()); @@ -203,7 +203,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr( } AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( - const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) { + const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) { if (mix->mMixType == MIX_TYPE_PLAYERS) { // TODO if adding more player rules (currently only 2), make rule handling "generic" -- GitLab From 4c9da69753a99e6b5444b08e14f01b4c5aa8077f Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Sat, 5 Jan 2019 12:25:59 +0530 Subject: [PATCH 0932/1530] libeffects: Added sample testbench for downmix module Added sample testbench, necessary makefile to build standalone application and shell script to build and run the standalone application. Test: downmixtest (in this CL) Change-Id: I64651dfae497c0bf8687bfc5ed45020e27a8fdd1 --- media/libeffects/downmix/tests/Android.bp | 31 ++ .../tests/build_and_run_all_unit_tests.sh | 69 ++++ .../libeffects/downmix/tests/downmixtest.cpp | 305 ++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 media/libeffects/downmix/tests/Android.bp create mode 100755 media/libeffects/downmix/tests/build_and_run_all_unit_tests.sh create mode 100644 media/libeffects/downmix/tests/downmixtest.cpp diff --git a/media/libeffects/downmix/tests/Android.bp b/media/libeffects/downmix/tests/Android.bp new file mode 100644 index 0000000000..e2e7dbd50c --- /dev/null +++ b/media/libeffects/downmix/tests/Android.bp @@ -0,0 +1,31 @@ +// Build testbench for downmix module. +cc_test { + name:"downmixtest", + host_supported: false, + proprietary: true, + include_dirs: [ + "frameworks/av/media/libeffects/downmix", + ], + + header_libs: [ + "libaudioeffects", + ], + + shared_libs: [ + "libaudioutils", + "libdownmix", + "liblog", + ], + + relative_install_path: "soundfx", + + srcs: [ + "downmixtest.cpp", + ], + + cflags: [ + "-v", + "-Werror", + "-Wextra", + ], +} diff --git a/media/libeffects/downmix/tests/build_and_run_all_unit_tests.sh b/media/libeffects/downmix/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000000..d0faebeb2f --- /dev/null +++ b/media/libeffects/downmix/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# +#Run tests in this directory. +# + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi +#ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +mm -j + +echo "waiting for device" + +adb root && adb wait-for-device remount + +#location of test files +testdir="/data/local/tmp/downmixtest" + +fs_arr=( + 8000 + 11025 + 12000 + 16000 + 22050 + 24000 + 32000 + 44100 + 48000 + 88200 + 96000 + 176400 + 192000 +) + +echo "========================================" +echo "testing Downmix" +adb shell mkdir $testdir + +adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw \ +$testdir +adb push $OUT/testcases/downmixtest/arm64/downmixtest $testdir + +#run the downmix test application for test. +for fs in ${fs_arr[*]} +do + for f_ch in {1..8} + do + for ch_fmt in {0..4} + do + adb shell LD_LIBRARY_PATH=/vendor/lib64/soundfx \ + $testdir/downmixtest $testdir/sinesweepraw.raw \ + $testdir/sinesweep_fmt_$((ch_fmt))_fch_$((f_ch))_$((fs)).raw \ + -ch_fmt:$ch_fmt -fch:$f_ch -fs:$fs + + # Implementation dependent test: + # check that higher frequencies match 8 kHz result. + if [ $fs != 8000 ] + then + adb shell cmp \ + $testdir/sinesweep_fmt_$((ch_fmt))_fch_$((f_ch))_8000.raw \ + $testdir/sinesweep_fmt_$((ch_fmt))_fch_$((f_ch))_$((fs)).raw + fi + done + done +done +adb shell rm -r $testdir diff --git a/media/libeffects/downmix/tests/downmixtest.cpp b/media/libeffects/downmix/tests/downmixtest.cpp new file mode 100644 index 0000000000..71f83e5ce7 --- /dev/null +++ b/media/libeffects/downmix/tests/downmixtest.cpp @@ -0,0 +1,305 @@ +/* + * 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. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "EffectDownmix.h" +#define FRAME_LENGTH 256 +#define MAX_NUM_CHANNELS 8 + +struct downmix_cntxt_s { + effect_descriptor_t desc; + effect_handle_t handle; + effect_config_t config; + + int numFileChannels; + int numProcessChannels; +}; + +extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM; + +void printUsage() { + printf("\nUsage:"); + printf("\n downmixtest [options]\n"); + printf("\nwhere,"); + printf("\n is the input file name"); + printf("\n on which LVM effects are applied"); + printf("\n processed output file"); + printf("\n and options are mentioned below"); + printf("\n"); + printf("\n -h"); + printf("\n Prints this usage information"); + printf("\n"); + printf("\n -ch_fmt:"); + printf("\n 0:AUDIO_CHANNEL_OUT_7POINT1(default)"); + printf("\n 1:AUDIO_CHANNEL_OUT_5POINT1_SIDE"); + printf("\n 2:AUDIO_CHANNEL_OUT_5POINT1_BACK"); + printf("\n 3:AUDIO_CHANNEL_OUT_QUAD_SIDE"); + printf("\n 4:AUDIO_CHANNEL_OUT_QUAD_BACK"); + printf("\n"); + printf("\n -fch: (1 through 8)"); + printf("\n"); +} + +int32_t DownmixDefaultConfig(effect_config_t *pConfig) { + pConfig->inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pConfig->inputCfg.format = AUDIO_FORMAT_PCM_FLOAT; + pConfig->inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1; + pConfig->inputCfg.bufferProvider.getBuffer = nullptr; + pConfig->inputCfg.bufferProvider.releaseBuffer = nullptr; + pConfig->inputCfg.bufferProvider.cookie = nullptr; + pConfig->inputCfg.mask = EFFECT_CONFIG_ALL; + + pConfig->inputCfg.samplingRate = 44100; + pConfig->outputCfg.samplingRate = pConfig->inputCfg.samplingRate; + + // set a default value for the access mode, but should be overwritten by caller + pConfig->outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; + pConfig->outputCfg.format = AUDIO_FORMAT_PCM_FLOAT; + pConfig->outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + pConfig->outputCfg.bufferProvider.getBuffer = nullptr; + pConfig->outputCfg.bufferProvider.releaseBuffer = nullptr; + pConfig->outputCfg.bufferProvider.cookie = nullptr; + pConfig->outputCfg.mask = EFFECT_CONFIG_ALL; + + return 0; +} + +int32_t DownmixConfiureAndEnable(downmix_cntxt_s *pDescriptor) { + effect_handle_t *effectHandle = &pDescriptor->handle; + downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle; + const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe; + int32_t err = 0; + uint32_t replySize = (uint32_t)sizeof(err); + + err = (Downmix_api->command)(*effectHandle, EFFECT_CMD_SET_CONFIG, + sizeof(effect_config_t), &(pDescriptor->config), + &replySize, &err); + if (err != 0) { + ALOGE("Downmix command to configure returned an error %d", err); + return err; + } + + err = ((Downmix_api->command))(*effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, + &replySize, &err); + if (err != 0) { + ALOGE("Downmix command to enable effect returned an error %d", err); + return err; + } + return 0; +} + +int32_t DownmixExecute(downmix_cntxt_s *pDescriptor, FILE *finp, + FILE *fout) { + effect_handle_t *effectHandle = &pDescriptor->handle; + downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle; + const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe; + + const int numFileChannels = pDescriptor->numFileChannels; + const int numProcessChannels = pDescriptor->numProcessChannels; + const int fileFrameSize = numFileChannels * sizeof(short); + const unsigned int outputChannels = + audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO); + + std::vector outFloat(FRAME_LENGTH * MAX_NUM_CHANNELS); + std::vector inFloat(FRAME_LENGTH * MAX_NUM_CHANNELS); + + audio_buffer_t inbuffer, outbuffer; + inbuffer.f32 = inFloat.data(); + outbuffer.f32 = outFloat.data(); + inbuffer.frameCount = FRAME_LENGTH; + outbuffer.frameCount = FRAME_LENGTH; + + audio_buffer_t *pinbuf, *poutbuf; + pinbuf = &inbuffer; + poutbuf = &outbuffer; + + int frameCounter = 0; + std::vector inS16(FRAME_LENGTH * MAX_NUM_CHANNELS); + std::vector outS16(FRAME_LENGTH * MAX_NUM_CHANNELS); + + while (fread(inS16.data(), fileFrameSize, FRAME_LENGTH, finp) == + FRAME_LENGTH) { + if (numFileChannels != numProcessChannels) { + adjust_channels(inS16.data(), numFileChannels, inS16.data(), + numProcessChannels, sizeof(short), + FRAME_LENGTH * fileFrameSize); + } + + memcpy_to_float_from_i16(inFloat.data(), inS16.data(), + FRAME_LENGTH * numProcessChannels); + + const int32_t err = (Downmix_api->process)(*effectHandle, pinbuf, poutbuf); + if (err != 0) { + ALOGE("DownmixProcess returned an error %d", err); + return -1; + } + + memcpy_to_i16_from_float(outS16.data(), outFloat.data(), + FRAME_LENGTH * outputChannels); + fwrite(outS16.data(), sizeof(short), (FRAME_LENGTH * outputChannels), + fout); + frameCounter++; + } + printf("frameCounter: [%d]\n", frameCounter); + return 0; +} + +int32_t DowmixMainProcess(downmix_cntxt_s *pDescriptor, FILE *finp, + FILE *fout) { + effect_handle_t *effectHandle = &pDescriptor->handle; + int32_t sessionId = 0, ioId = 0; + const effect_uuid_t downmix_uuid = { + 0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}; + + int32_t err = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect( + &downmix_uuid, sessionId, ioId, + effectHandle); + if (err != 0) { + ALOGE("DownmixLib_Create returned an error %d", err); + return -1; + } + + // Passing the init config for time being. + err = DownmixConfiureAndEnable(pDescriptor); + if (err != 0) { + ALOGE("DownmixConfigureAndEnable returned an error %d", err); + return -1; + } + // execute call for downmix. + err = DownmixExecute(pDescriptor, finp, fout); + if (err != 0) { + ALOGE("DownmixExecute returned an error %d", err); + return -1; + } + // Release the library function. + err = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(*effectHandle); + if (err != 0) { + ALOGE("DownmixRelease returned an error %d", err); + return -1; + } + return 0; +} + +int main(int argc, const char *argv[]) { + int numFileChannels = 1, numProcessChannels = 8; + downmix_cntxt_s descriptor = {}; + DownmixDefaultConfig(&(descriptor.config)); + + const char *infile = nullptr; + const char *outfile = nullptr; + for (int i = 1; i < argc; i++) { + printf("%s ", argv[i]); + if (argv[i][0] != '-') { + if (infile == nullptr) { + infile = argv[i]; + } else if (outfile == nullptr) { + outfile = argv[i]; + } else { + printUsage(); + return -1; + } + } else if (!strncmp(argv[i], "-fs:", 4)) { + // Add a check for all the supported streams. + const int samplingFreq = atoi(argv[i] + 4); + if (samplingFreq != 8000 && samplingFreq != 11025 && + samplingFreq != 12000 && samplingFreq != 16000 && + samplingFreq != 22050 && samplingFreq != 24000 && + samplingFreq != 32000 && samplingFreq != 44100 && + samplingFreq != 48000 && samplingFreq != 88200 && + samplingFreq != 96000 && samplingFreq != 176400 && + samplingFreq != 192000) { + printf("Unsupported Sampling Frequency : %d", samplingFreq); + printUsage(); + return -1; + } + + descriptor.config.inputCfg.samplingRate = samplingFreq; + descriptor.config.outputCfg.samplingRate = samplingFreq; + } else if (!strncmp(argv[i], "-ch_fmt:", 8)) { + const int format = atoi(argv[i] + 8); + uint32_t *audioType = &descriptor.config.inputCfg.channels; + switch (format) { + case 0: + *audioType = AUDIO_CHANNEL_OUT_7POINT1; + break; + case 1: + *audioType = AUDIO_CHANNEL_OUT_5POINT1_SIDE; + break; + case 2: + *audioType = AUDIO_CHANNEL_OUT_5POINT1_BACK; + break; + case 3: + *audioType = AUDIO_CHANNEL_OUT_QUAD_SIDE; + break; + case 4: + *audioType = AUDIO_CHANNEL_OUT_QUAD_BACK; + break; + default: + *audioType = AUDIO_CHANNEL_OUT_7POINT1; + break; + } + descriptor.numProcessChannels = + audio_channel_count_from_out_mask(*audioType); + } else if (!strncmp(argv[i], "-fch:", 5)) { + const int fChannels = atoi(argv[i] + 5); + if (fChannels > 8 || fChannels < 1) { + printf("Unsupported number of file channels : %d", fChannels); + printUsage(); + return -1; + } + descriptor.numFileChannels = fChannels; + + } else if (!strncmp(argv[i], "-h", 2)) { + printUsage(); + return 0; + } + } + + if (/*infile == nullptr || */ outfile == nullptr) { + printUsage(); + return -1; + } + + FILE *finp = fopen(infile, "rb"); + if (finp == nullptr) { + printf("Cannot open input file %s", infile); + return -1; + } + FILE *fout = fopen(outfile, "wb"); + if (fout == nullptr) { + printf("Cannot open output file %s", outfile); + fclose(finp); + return -1; + } + + const int err = DowmixMainProcess(&descriptor, finp, fout); + // close input and output files. + fclose(finp); + fclose(fout); + if (err != 0) { + printf("Error: %d\n", err); + } + return err; +} -- GitLab From 20413a95e4a2def7df555c6bf31c9239e0b05d1d Mon Sep 17 00:00:00 2001 From: Revathi Uddaraju Date: Thu, 13 Oct 2016 17:16:14 +0800 Subject: [PATCH 0933/1530] audioflinger: fix for playback paused during track transition The test case here involves two applications both of which play the same music clip. When the 2nd application ends playback, the 1st application must resume playback where it left off. But it is possible that the new AudioTrack.start() command from the 1st application is processed after the 2nd application has paused and terminated. This will prevent a flush to clear any state before playback of the 1st application can resume. Direct thread thus stays in pause state indefintely. Issue unconditional flush for direct thread when a new track is added to a direct output. Test: partner CL submit, check compilation Bug: 123082418 Change-Id: I33d36db35358a1b3b89dd5b2b8cb4db1f9c6d4a0 --- services/audioflinger/Threads.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index dd1eabf9a1..d7c18240b6 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5462,6 +5462,11 @@ void AudioFlinger::DirectOutputThread::onAddNewTrack_l() mFlushPending = true; } } + } else if (previousTrack == 0) { + // there could be an old track added back during track transition for direct + // output, so always issues flush to flush data of the previous track if it + // was already destroyed with HAL paused, then flush can resume the playback + mFlushPending = true; } PlaybackThread::onAddNewTrack_l(); } -- GitLab From 453bcb548feb5661b0468a137950e8db521eb9d2 Mon Sep 17 00:00:00 2001 From: Revathi Uddaraju Date: Mon, 14 Aug 2017 14:19:40 +0800 Subject: [PATCH 0934/1530] audio: don't apply ramp if track is paused before the first mix If a track is paused right after start before it gets a chance to mix the first frame. Don't apply volume ramp if it resumes later. Test: Basic audio sanity. Partner CL. Bug: 123063164 Change-Id: I969ff1b6edce8faf77f2cdc481db9c1e3ef6d3dd --- services/audioflinger/Threads.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index dd1eabf9a1..523b076b1e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4784,7 +4784,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mFillingUpStatus = Track::FS_ACTIVE; if (track->mState == TrackBase::RESUMING) { track->mState = TrackBase::ACTIVE; - param = AudioMixer::RAMP_VOLUME; + // If a new track is paused immediately after start, do not ramp on resume. + if (cblk->mServer != 0) { + param = AudioMixer::RAMP_VOLUME; + } } mAudioMixer->setParameter(trackId, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL); mLeftVolFloat = -1.0; -- GitLab From 4463ff504dc0c13116f252466bdbba61e0b32a81 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 7 Feb 2019 13:56:09 -0800 Subject: [PATCH 0935/1530] AudioSystem: more locking work on audio device callback commit 24a9fb0d left some locking issues with management of device callbacks in AudioSystem, AudioTrack and AudioRecord. This change makes that the AudioSystem mutex is not held when callbacks are called into AudioTrack and AudioRecord removing cross deadlock risks and allowing AudioRecord and AudioTrack to hold their mutexes while installing and handling the callbacks Test: CTS RoutingTest, AudioTrackTest, AudioRecordTest Change-Id: I5d17e77ca26220092deb0bd6e5a33dc32348d460 --- media/libaudioclient/AudioRecord.cpp | 12 +- media/libaudioclient/AudioSystem.cpp | 113 +++++++++--------- media/libaudioclient/AudioTrack.cpp | 12 +- .../include/media/AudioSystem.h | 35 ++++-- 4 files changed, 92 insertions(+), 80 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 8afb1cc5e7..baa1469426 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -1361,14 +1361,12 @@ status_t AudioRecord::removeAudioDeviceCallback( ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } - { - AutoMutex lock(mLock); - if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s(%d): removing different callback!", __func__, mPortId); - return INVALID_OPERATION; - } - mDeviceCallback.clear(); + AutoMutex lock(mLock); + if (mDeviceCallback.unsafe_get() != callback.get()) { + ALOGW("%s(%d): removing different callback!", __func__, mPortId); + return INVALID_OPERATION; } + mDeviceCallback.clear(); if (mInput != AUDIO_IO_HANDLE_NONE) { AudioSystem::removeAudioDeviceCallback(this, mInput); } diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 52ad5a65dc..01d9b3d158 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -522,11 +522,12 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return; audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE; - AudioDeviceCallbacks callbacks; - bool deviceValidOrChanged = false; - Mutex::Autolock _l(mCallbacksLock); + Vector> callbacksToCall; { Mutex::Autolock _l(mLock); + bool deviceValidOrChanged = false; + bool sendCallbacks = false; + ssize_t ioIndex = -1; switch (event) { case AUDIO_OUTPUT_OPENED: @@ -544,17 +545,16 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) { deviceId = ioDesc->getDeviceId(); if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) { - ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); + ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); if (ioIndex >= 0) { - callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + sendCallbacks = true; deviceValidOrChanged = true; } } if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) { - ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); - if ((ioIndex >= 0) && !mAudioDeviceCallbacks.valueAt(ioIndex).notifiedOnce()) { - callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); - } + ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); + sendCallbacks = (ioIndex >= 0) + && !mAudioDeviceCallbackProxies.valueAt(ioIndex).notifiedOnce(); } } ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x " @@ -577,7 +577,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); mIoDescriptors.removeItem(ioDesc->mIoHandle); - mAudioDeviceCallbacks.removeItem(ioDesc->mIoHandle); + mAudioDeviceCallbackProxies.removeItem(ioDesc->mIoHandle); } break; case AUDIO_OUTPUT_CONFIG_CHANGED: @@ -594,10 +594,8 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even if (deviceId != ioDesc->getDeviceId()) { deviceValidOrChanged = true; deviceId = ioDesc->getDeviceId(); - ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle); - if (ioIndex >= 0) { - callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); - } + ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); + sendCallbacks = ioIndex >= 0; } ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x " "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d", @@ -608,30 +606,34 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even } break; } - } - // callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid - if (callbacks.size() != 0) { - for (size_t i = 0; i < callbacks.size(); ) { - sp callback = callbacks[i].promote(); - if (callback.get() != nullptr) { - // Call the callback only if the device actually changed, the input or output was - // opened or closed or the client was newly registered and the callback was never - // called - if (!callback->notifiedOnce() || deviceValidOrChanged) { - // Must be called without mLock held. May lead to dead lock if calling for - // example getRoutedDevice that updates the device and tries to acquire mLock. - callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); - callback->setNotifiedOnce(); + + // sendCallbacks true => ioDesc->mIoHandle and deviceId are valid + if (sendCallbacks) { + AudioDeviceCallbackProxies &callbackProxies = + mAudioDeviceCallbackProxies.editValueAt(ioIndex); + for (size_t i = 0; i < callbackProxies.size(); ) { + sp callback = callbackProxies[i]->callback(); + if (callback.get() != nullptr) { + // Call the callback only if the device actually changed, the input or output + // was opened or closed or the client was newly registered and the callback + // was never called + if (!callbackProxies[i]->notifiedOnce() || deviceValidOrChanged) { + callbacksToCall.add(callback); + callbackProxies[i]->setNotifiedOnce(); + } + i++; + } else { + callbackProxies.removeAt(i); } - i++; - } else { - callbacks.removeAt(i); } + callbackProxies.setNotifiedOnce(); } - callbacks.setNotifiedOnce(); - // clean up callback list while we are here if some clients have disappeared without - // unregistering their callback, or if cb was served for the first time since registered - mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks); + } + + // Callbacks must be called without mLock held. May lead to dead lock if calling for + // example getRoutedDevice that updates the device and tries to acquire mLock. + for (size_t i = 0; i < callbacksToCall.size(); i++) { + callbacksToCall[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); } } @@ -686,48 +688,49 @@ sp AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_ status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( const wp& callback, audio_io_handle_t audioIo) { - Mutex::Autolock _l(mCallbacksLock); - AudioDeviceCallbacks callbacks; - ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); + Mutex::Autolock _l(mLock); + AudioDeviceCallbackProxies callbackProxies; + ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo); if (ioIndex >= 0) { - callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); + callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex); } - for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) { - if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) { + for (size_t cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) { + sp cbk = callbackProxies[cbIndex]->callback(); + if (cbk.get() == callback.unsafe_get()) { return INVALID_OPERATION; } } - callbacks.add(callback); - callbacks.resetNotifiedOnce(); - mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks); + callbackProxies.add(new AudioDeviceCallbackProxy(callback)); + callbackProxies.resetNotifiedOnce(); + mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies); return NO_ERROR; } status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( const wp& callback, audio_io_handle_t audioIo) { - Mutex::Autolock _l(mCallbacksLock); - ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo); + Mutex::Autolock _l(mLock); + ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo); if (ioIndex < 0) { return INVALID_OPERATION; } - AudioDeviceCallbacks callbacks = mAudioDeviceCallbacks.valueAt(ioIndex); - + AudioDeviceCallbackProxies callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex); size_t cbIndex; - for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) { - if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) { + for (cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) { + sp cbk = callbackProxies[cbIndex]->callback(); + if (cbk.get() == callback.unsafe_get()) { break; } } - if (cbIndex == callbacks.size()) { + if (cbIndex == callbackProxies.size()) { return INVALID_OPERATION; } - callbacks.removeAt(cbIndex); - if (callbacks.size() != 0) { - mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks); + callbackProxies.removeAt(cbIndex); + if (callbackProxies.size() != 0) { + mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies); } else { - mAudioDeviceCallbacks.removeItem(audioIo); + mAudioDeviceCallbackProxies.removeItem(audioIo); } return NO_ERROR; } diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index b5a7ebea26..7881bb859c 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -2959,14 +2959,12 @@ status_t AudioTrack::removeAudioDeviceCallback( ALOGW("%s(%d): removing NULL callback!", __func__, mPortId); return BAD_VALUE; } - { - AutoMutex lock(mLock); - if (mDeviceCallback.unsafe_get() != callback.get()) { - ALOGW("%s removing different callback!", __FUNCTION__); - return INVALID_OPERATION; - } - mDeviceCallback.clear(); + AutoMutex lock(mLock); + if (mDeviceCallback.unsafe_get() != callback.get()) { + ALOGW("%s removing different callback!", __FUNCTION__); + return INVALID_OPERATION; } + mDeviceCallback.clear(); if (mOutput != AUDIO_IO_HANDLE_NONE) { AudioSystem::removeAudioDeviceCallback(this, mOutput); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 3df49e6ca5..b9ee24a978 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -399,15 +399,28 @@ public: virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId) = 0; - bool notifiedOnce() const { return mNotifiedOnce; } - void setNotifiedOnce() { mNotifiedOnce = true; } + }; + + class AudioDeviceCallbackProxy : public RefBase + { + public: + + AudioDeviceCallbackProxy(wp callback) + : mCallback(callback) {} + ~AudioDeviceCallbackProxy() override {} + + sp callback() const { return mCallback.promote(); }; + + bool notifiedOnce() const { return mNotifiedOnce; } + void setNotifiedOnce() { mNotifiedOnce = true; } private: - /** - * @brief mNotifiedOnce it forces the callback to be called at least once when - * registered with a VALID AudioDevice, and allows not to flood other listeners - * on this iohandle that already know the valid device. - */ - bool mNotifiedOnce = false; + /** + * @brief mNotifiedOnce it forces the callback to be called at least once when + * registered with a VALID AudioDevice, and allows not to flood other listeners + * on this iohandle that already know the valid device. + */ + bool mNotifiedOnce = false; + wp mCallback; }; static status_t addAudioDeviceCallback(const wp& callback, @@ -454,7 +467,7 @@ private: Mutex mLock; DefaultKeyedVector > mIoDescriptors; - class AudioDeviceCallbacks : public Vector> + class AudioDeviceCallbackProxies : public Vector> { public: /** @@ -472,8 +485,8 @@ private: */ bool mNotifiedOnce = false; }; - Mutex mCallbacksLock; // prevents race on Callbacks - DefaultKeyedVector mAudioDeviceCallbacks; + DefaultKeyedVector + mAudioDeviceCallbackProxies; // cached values for recording getInputBufferSize() queries size_t mInBuffSize; // zero indicates cache is invalid uint32_t mInSamplingRate; -- GitLab From f4b1bcf7661672a3b860ea735755fe9365216ecf Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 7 Feb 2019 16:30:10 -0800 Subject: [PATCH 0936/1530] Fix the wrong condition in querySupportedValues Test: m cts && cts-tradefed run cts -m CtsMediaTestCases \ -t android.media.cts.MediaCodecListTest#testFindEncoderWithAacProfile Bug: 123632015 Change-Id: Id924fa2d19443a092918ce2e72f4054e95a7e4d5 --- media/codec2/hidl/1.0/utils/Configurable.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/Configurable.cpp b/media/codec2/hidl/1.0/utils/Configurable.cpp index a35b74c1e5..ec9c170ead 100644 --- a/media/codec2/hidl/1.0/utils/Configurable.cpp +++ b/media/codec2/hidl/1.0/utils/Configurable.cpp @@ -171,17 +171,15 @@ Return CachedConfigurable::querySupportedValues( c2fields, mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK); hidl_vec outFields(inFields.size()); - { - size_t ix = 0; - for (const C2FieldSupportedValuesQuery &result : c2fields) { - if (!objcpy(&outFields[ix], result)) { - ++ix; - } else { - outFields.resize(ix); - c2res = C2_CORRUPTED; - LOG(WARNING) << "querySupportedValues -- invalid output params."; - break; - } + size_t dstIx = 0; + for (const C2FieldSupportedValuesQuery &result : c2fields) { + if (objcpy(&outFields[dstIx], result)) { + ++dstIx; + } else { + outFields.resize(dstIx); + c2res = C2_CORRUPTED; + LOG(WARNING) << "querySupportedValues -- invalid output params."; + break; } } _hidl_cb((Status)c2res, outFields); -- GitLab From 2c54504df6b633d9840da6b5f781e69c41890d54 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Thu, 7 Feb 2019 10:27:35 -0800 Subject: [PATCH 0937/1530] Camera: Disconnect composite streams from output surface at shutdown When a composite stream is deleted, the previously connected output surface needs to be disconnected. Otherwise when the composite stream is re-created with the same output surface, connect() will fail. Test: TestingCamera2, and Camera CTS Bug: 79465976 Bug: 109735087 Bug: 124072769 Change-Id: I254238235d306f9be8152110b1c55a7ada46424a --- .../camera/libcameraservice/api2/DepthCompositeStream.cpp | 5 +++++ .../camera/libcameraservice/api2/HeicCompositeStream.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index f627b25100..2eec0f7f9c 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -634,6 +634,11 @@ status_t DepthCompositeStream::deleteInternalStreams() { mDepthStreamId = -1; } + if (mOutputSurface != nullptr) { + mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA); + mOutputSurface.clear(); + } + return ret; } diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp index 3eba863108..8e9c39ec00 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -214,6 +214,10 @@ status_t HeicCompositeStream::deleteInternalStreams() { mAppSegmentStreamId = -1; } + if (mOutputSurface != nullptr) { + mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA); + mOutputSurface.clear(); + } return res; } -- GitLab From 61d2e5387b2d9f4ca2191e30c15962c3351d5ee9 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 8 Feb 2019 10:34:52 -0800 Subject: [PATCH 0938/1530] Include correct headerfile Since sfplugin includes its own copy of SkipCutBuffer, it should include its own header as well, not the libstagefright one. Test: build, boot Change-Id: I7e8ee2fdb6c2acc68c10143d312f1ef3cf928df5 --- media/codec2/sfplugin/SkipCutBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/codec2/sfplugin/SkipCutBuffer.cpp b/media/codec2/sfplugin/SkipCutBuffer.cpp index 5762440b99..8d1de65502 100644 --- a/media/codec2/sfplugin/SkipCutBuffer.cpp +++ b/media/codec2/sfplugin/SkipCutBuffer.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include "SkipCutBuffer.h" namespace android { -- GitLab From 7134799369f37e8663ba67c9849c56af0838d8a1 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Wed, 30 Jan 2019 18:36:20 +0800 Subject: [PATCH 0939/1530] MPEG4Extractor supports more file extensions Add "3ga","qt" to extensions[] Bug: 123651180 Test: test mp4 file with extension "3ga" or "qt", and check if file can be played Change-Id: Ia1d265594af11ebb65918b80ed92e45d1ad487a4 --- media/extractors/mp4/MPEG4Extractor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index ac54116833..0243c038c9 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -6214,6 +6214,7 @@ static CreatorFunc Sniff( static const char *extensions[] = { "3g2", + "3ga", "3gp", "3gpp", "3gpp2", @@ -6222,6 +6223,7 @@ static const char *extensions[] = { "m4v", "mov", "mp4", + "qt", NULL }; -- GitLab From 179a074f2706051bbe0e084598482a39e00368cd Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Wed, 30 Jan 2019 14:16:16 -0800 Subject: [PATCH 0940/1530] audioflinger: fix redundant adding to tracksToRemove In order to avoid track to be added to tracksToRemove redundantly, merge the adding operation under exclusive condition check. This can make sure the add can happen only one time within one prepareTracks iterator. authored-by: Zhou Song Test: AF active track removal with audio playback pause Bug: 123375253 Change-Id: If35348af2144a28b84241e1cb93f497d5eec9a8a --- services/audioflinger/Threads.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f438493a2d..6dd9cabb09 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5508,7 +5508,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep doHwPause = true; mHwPaused = true; } - tracksToRemove->add(track); } else if (track->isFlushPending()) { track->flushAck(); if (last) { @@ -5605,7 +5604,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep int64_t framesWritten = mBytesWritten / mFrameSize; if (mStandby || !last || - track->presentationComplete(framesWritten, audioHALFrames)) { + track->presentationComplete(framesWritten, audioHALFrames) || + track->isPaused()) { if (track->isStopping_2()) { track->mState = TrackBase::STOPPED; } -- GitLab From ab18bac6ace8842ec93ef4c5c9eb4e66b3e82481 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Thu, 24 Jan 2019 17:15:10 +0530 Subject: [PATCH 0941/1530] libeffects: Fixed memory leaks in lvmtest The memories created in lvmCreate call have to be freed at the end of execution. Added code to achieve the same. Test: local native test (lvmtest) Bug: 123324940 Change-Id: I83fad7def874da27c79d755e44ec738fa0816a41 --- media/libeffects/lvm/tests/lvmtest.cpp | 74 +++++++++++++++++--------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp index 43271d2212..fe47d0b5e3 100644 --- a/media/libeffects/lvm/tests/lvmtest.cpp +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -571,17 +571,12 @@ int lvmExecute(float *floatIn, float *floatOut, struct EffectContext *pContext, 0); /* Audio Time */ } -int lvmMainProcess(lvmConfigParams_t *plvmConfigParams, FILE *finp, FILE *fout) { - struct EffectContext context; - LVM_ControlParams_t params; - - int errCode = lvmCreate(&context, plvmConfigParams, ¶ms); - if (errCode) { - ALOGE("Error: lvmCreate returned with %d\n", errCode); - return errCode; - } - - errCode = lvmControl(&context, plvmConfigParams, ¶ms); +int lvmMainProcess(EffectContext *pContext, + LVM_ControlParams_t *pParams, + lvmConfigParams_t *plvmConfigParams, + FILE *finp, + FILE *fout) { + int errCode = lvmControl(pContext, plvmConfigParams, pParams); if (errCode) { ALOGE("Error: lvmControl returned with %d\n", errCode); return errCode; @@ -625,7 +620,7 @@ int lvmMainProcess(lvmConfigParams_t *plvmConfigParams, FILE *finp, FILE *fout) } } #if 1 - errCode = lvmExecute(floatIn.data(), floatOut.data(), &context, plvmConfigParams); + errCode = lvmExecute(floatIn.data(), floatOut.data(), pContext, plvmConfigParams); if (errCode) { printf("\nError: lvmExecute returned with %d\n", errCode); return errCode; @@ -654,14 +649,15 @@ int main(int argc, const char *argv[]) { } lvmConfigParams_t lvmConfigParams{}; // default initialize - FILE *finp = nullptr, *fout = nullptr; + const char *infile = nullptr; + const char *outfile = nullptr; for (int i = 1; i < argc; i++) { printf("%s ", argv[i]); if (!strncmp(argv[i], "-i:", 3)) { - finp = fopen(argv[i] + 3, "rb"); + infile = argv[i] + 3; } else if (!strncmp(argv[i], "-o:", 3)) { - fout = fopen(argv[i] + 3, "wb"); + outfile = argv[i] + 3; } else if (!strncmp(argv[i], "-fs:", 4)) { const int samplingFreq = atoi(argv[i] + 4); if (samplingFreq != 8000 && samplingFreq != 11025 && @@ -671,21 +667,21 @@ int main(int argc, const char *argv[]) { samplingFreq != 48000 && samplingFreq != 88200 && samplingFreq != 96000 && samplingFreq != 176400 && samplingFreq != 192000) { - ALOGE("\nError: Unsupported Sampling Frequency : %d\n", samplingFreq); + printf("Error: Unsupported Sampling Frequency : %d\n", samplingFreq); return -1; } lvmConfigParams.samplingFreq = samplingFreq; } else if (!strncmp(argv[i], "-ch:", 4)) { const int nrChannels = atoi(argv[i] + 4); if (nrChannels > 8 || nrChannels < 1) { - ALOGE("\nError: Unsupported number of channels : %d\n", nrChannels); + printf("Error: Unsupported number of channels : %d\n", nrChannels); return -1; } lvmConfigParams.nrChannels = nrChannels; } else if (!strncmp(argv[i], "-fch:", 5)) { const int fChannels = atoi(argv[i] + 5); if (fChannels > 8 || fChannels < 1) { - ALOGE("\nError: Unsupported number of file channels : %d\n", fChannels); + printf("Error: Unsupported number of file channels : %d\n", fChannels); return -1; } lvmConfigParams.fChannels = fChannels; @@ -694,7 +690,7 @@ int main(int argc, const char *argv[]) { } else if (!strncmp(argv[i], "-basslvl:", 9)) { const int bassEffectLevel = atoi(argv[i] + 9); if (bassEffectLevel > 15 || bassEffectLevel < 0) { - ALOGE("\nError: Unsupported Bass Effect Level : %d\n", + printf("Error: Unsupported Bass Effect Level : %d\n", bassEffectLevel); printUsage(); return -1; @@ -703,7 +699,7 @@ int main(int argc, const char *argv[]) { } else if (!strncmp(argv[i], "-eqPreset:", 10)) { const int eqPresetLevel = atoi(argv[i] + 10); if (eqPresetLevel > 9 || eqPresetLevel < 0) { - ALOGE("\nError: Unsupported Equalizer Preset : %d\n", eqPresetLevel); + printf("Error: Unsupported Equalizer Preset : %d\n", eqPresetLevel); printUsage(); return -1; } @@ -722,19 +718,47 @@ int main(int argc, const char *argv[]) { } } - if (finp == nullptr || fout == nullptr) { - ALOGE("\nError: missing input/output files\n"); + if (infile == nullptr || outfile == nullptr) { + printf("Error: missing input/output files\n"); printUsage(); - // ok not to close. return -1; } - const int errCode = lvmMainProcess(&lvmConfigParams, finp, fout); + FILE *finp = fopen(infile, "rb"); + if (finp == nullptr) { + printf("Cannot open input file %s", infile); + return -1; + } + + FILE *fout = fopen(outfile, "wb"); + if (fout == nullptr) { + printf("Cannot open output file %s", outfile); + fclose(finp); + return -1; + } + + EffectContext context; + LVM_ControlParams_t params; + int errCode = lvmCreate(&context, &lvmConfigParams, ¶ms); + if (errCode == 0) { + errCode = lvmMainProcess(&context, ¶ms, &lvmConfigParams, finp, fout); + if (errCode != 0) { + printf("Error: lvmMainProcess returned with the error: %d",errCode); + } + } else { + printf("Error: lvmCreate returned with the error: %d", errCode); + } fclose(finp); fclose(fout); + /* Free the allocated buffers */ + if (context.pBundledContext != nullptr) { + if (context.pBundledContext->hInstance != nullptr) { + LvmEffect_free(&context); + } + free(context.pBundledContext); + } if (errCode) { - ALOGE("Error: lvmMainProcess returns with the error: %d \n", errCode); return -1; } return 0; -- GitLab From 9aeb1770d49bab13ea5c6454c969a713641fe686 Mon Sep 17 00:00:00 2001 From: Weiyin Jiang Date: Fri, 27 Apr 2018 00:39:29 +0800 Subject: [PATCH 0942/1530] audio: ensure effect chain with specific session id is unique It's possible that tracks with the same session id running on various playback outputs, which causes effect chain being created on the same session twice. As a result, the same effect engine will be released twice as the same context is reused. Output that has effect chain with same session id is more preferable. Test: No regression with Play Music and Effects Bug: 123082420 Change-Id: I690ea3cb942d1fdc96b46048e271557d48000f43 --- services/audioflinger/AudioFlinger.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 7733071cb8..2d80bd894c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3277,9 +3277,13 @@ sp AudioFlinger::createEffect( } // look for the thread where the specified audio session is present for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { + uint32_t sessionType = mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); + if (sessionType != 0) { io = mPlaybackThreads.keyAt(i); - break; + // thread with same effect session is preferable + if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) { + break; + } } } if (io == AUDIO_IO_HANDLE_NONE) { -- GitLab From f0de5ce5d1629cd18effe1b70ed6500c1d648955 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 5 Feb 2019 09:28:44 -0800 Subject: [PATCH 0943/1530] Restrict APC to usages and opt-in streams Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I17d48bfa89f3187535d211ef1c9ba6cb435fd777 Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 446a1e651e..2c4695dfa9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -206,6 +206,18 @@ AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) { if (mix->mMixType == MIX_TYPE_PLAYERS) { + // Loopback render mixes are created from a public API and thus restricted + // to non sensible audio that have not opted out. + if (is_mix_loopback_render(mix->mRouteFlags)) { + if ((attributes.flags & AUDIO_FLAG_NO_CAPTURE) == AUDIO_FLAG_NO_CAPTURE) { + return MixMatchStatus::NO_MATCH; + } + if (!(attributes.usage == AUDIO_USAGE_UNKNOWN || + attributes.usage == AUDIO_USAGE_MEDIA || + attributes.usage == AUDIO_USAGE_GAME)) { + return MixMatchStatus::NO_MATCH; + } + } // TODO if adding more player rules (currently only 2), make rule handling "generic" // as there is no difference in the treatment of usage- or uid-based rules bool hasUsageMatchRules = false; -- GitLab From a15ee7a2746cfb334fa9454b1c7a09cbf927d1c1 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 5 Feb 2019 09:11:02 -0800 Subject: [PATCH 0944/1530] Add audio attr flag no capture Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I2ee29a0b76632110493902d6f3a7c387d6499fcd Signed-off-by: Kevin Rocard --- media/libmedia/TypeConverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index aa77cd3a5b..a0daab3315 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -388,6 +388,7 @@ const AudioFlagConverter::Table AudioFlagConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_FLAG_BYPASS_MUTE), MAKE_STRING_FROM_ENUM(AUDIO_FLAG_LOW_LATENCY), MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_CAPTURE), TERMINATOR }; -- GitLab From a134b006bc5714b6c8dcfbe5900a3ee029aa1044 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 7 Feb 2019 18:05:31 -0800 Subject: [PATCH 0945/1530] Audioflinger intercept track retry on buffer end Track::interceptBuffer failed to write all the audio if the source BP could not returned the requested buffer size. This is actually normal when the source circular buffer wraps around. Handle it by retrying if the first buffer is too small. Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: I42a7962449a0f075909a29f5f8f5ba82ca1d0085 --- services/audioflinger/PlaybackTracks.h | 2 ++ services/audioflinger/Tracks.cpp | 50 +++++++++++++++++--------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 3f62bc3215..94ea0421be 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -222,6 +222,8 @@ protected: private: void interceptBuffer(const AudioBufferProvider::Buffer& buffer); + /** Write the source data in the buffer provider. @return written frame count. */ + size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount); template void forEachTeePatchTrack(F f) { for (auto& tp : mTeePatches) { f(tp.patchTrack); } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 57dd568157..922547d9bb 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -697,28 +697,44 @@ void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buf // TODO: compensate for time shift between HW modules. void AudioFlinger::PlaybackThread::Track::interceptBuffer( - const AudioBufferProvider::Buffer& buffer) { + const AudioBufferProvider::Buffer& sourceBuffer) { + const size_t frameCount = sourceBuffer.frameCount; for (auto& sink : mTeePatches) { - RecordThread::PatchRecord& patchRecord = *sink.patchRecord; - AudioBufferProvider::Buffer patchBuffer; - patchBuffer.frameCount = buffer.frameCount; - auto status = patchRecord.getNextBuffer(&patchBuffer); - if (status != NO_ERROR) { - ALOGW("%s PathRecord getNextBuffer failed with error %d: %s", - __func__, status, strerror(-status)); - continue; + RecordThread::PatchRecord* patchRecord = sink.patchRecord.get(); + + size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount); + // On buffer wrap, the buffer frame count will be less than requested, + // when this happens a second buffer needs to be used to write the leftover audio + size_t framesLeft = frameCount - framesWritten; + if (framesWritten != 0 && framesLeft != 0) { + framesWritten += + writeFrames(patchRecord, sourceBuffer.i8 + framesWritten * mFrameSize, framesLeft); + framesLeft = frameCount - framesWritten; } - // FIXME: On buffer wrap, the frame count will be less then requested, - // retry to write the rest. (unlikely due to lcm buffer sizing) - ALOGW_IF(patchBuffer.frameCount != buffer.frameCount, - "%s PatchRecord can not provide big enough buffer %zu/%zu, dropping %zu frames", - __func__, patchBuffer.frameCount, buffer.frameCount, - buffer.frameCount - patchBuffer.frameCount); - memcpy(patchBuffer.raw, buffer.raw, patchBuffer.frameCount * mFrameSize); - patchRecord.releaseBuffer(&patchBuffer); + ALOGW_IF(framesLeft != 0, "%s(%d) PatchRecord %d can not provide big enough " + "buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId, + framesWritten, frameCount, framesLeft); } } +size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest, + const void* src, + size_t frameCount) { + AudioBufferProvider::Buffer patchBuffer; + patchBuffer.frameCount = frameCount; + auto status = dest->getNextBuffer(&patchBuffer); + if (status != NO_ERROR) { + ALOGW("%s PathRecord getNextBuffer failed with error %d: %s", + __func__, status, strerror(-status)); + return 0; + } + ALOG_ASSERT(patchBuffer.frameCount <= frameCount); + memcpy(patchBuffer.raw, src, patchBuffer.frameCount * mFrameSize); + auto framesWritten = patchBuffer.frameCount; + dest->releaseBuffer(&patchBuffer); + return framesWritten; +} + // releaseBuffer() is not overridden // ExtendedAudioBufferProvider interface -- GitLab From 6057fa270854d700cc85804fdd4517a8f8831f1f Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 8 Feb 2019 14:08:07 -0800 Subject: [PATCH 0946/1530] Log if intercepting tracks takes too long Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: Ic56cd0d223e83938a2d5eeb98cbcb754629799d8 Signed-off-by: Kevin Rocard --- services/audioflinger/Tracks.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 922547d9bb..65f799e7f5 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -698,6 +698,7 @@ void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buf // TODO: compensate for time shift between HW modules. void AudioFlinger::PlaybackThread::Track::interceptBuffer( const AudioBufferProvider::Buffer& sourceBuffer) { + auto start = std::chrono::steady_clock::now(); const size_t frameCount = sourceBuffer.frameCount; for (auto& sink : mTeePatches) { RecordThread::PatchRecord* patchRecord = sink.patchRecord.get(); @@ -715,6 +716,11 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( "buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId, framesWritten, frameCount, framesLeft); } + auto spent = ceil(std::chrono::steady_clock::now() - start); + using namespace std::chrono_literals; + // Average is ~20us per track, this should virtually never be logged (Logging takes >200us) + ALOGD_IF(spent > 200us, "%s: took %lldus to intercept %zu tracks", __func__, + spent.count(), mTeePatches.size()); } size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest, -- GitLab From 293eb84e30268ab483a64dc587798a1b5c1d28f9 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Sat, 9 Feb 2019 11:54:04 +0900 Subject: [PATCH 0947/1530] Configure com.android.media APEX to prefer cfi variants Bug: 124128094 Test: m on marlin The extractor libs are found under /system/apex/com.android.media Change-Id: Id0eac85253963777067afb1b795620d969af120c --- apex/Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apex/Android.bp b/apex/Android.bp index c077a77e25..7085d597d6 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -42,6 +42,9 @@ apex { }, }, key: "com.android.media.key", + prefer_sanitize: { + cfi: true, + }, } apex { -- GitLab From 2334e00184aea8b124789a04b92dc2180dbc7df7 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Fri, 18 Jan 2019 18:06:49 +0900 Subject: [PATCH 0948/1530] Mark libs as double_loadable libs (libmedia_helper, libstagefright_omx_utils, libstagefright_xmlparser) are VNDK and also used by LLNDK(libmediandk) which means these libs can be double-loaded. (deps: - libmediandk -> libmedia -> libaudioclient -> libmedia_helper - libmediandk -> libmedia_jni -> libstagefright -> libstagefright_omx_utils - libmediandk -> libmedia_jni -> libstagefright -> libstagefright_xmlparser ) Bug: 121280180 Test: m -j Change-Id: Ibbf5dede1f503e86484f805b252d4ced779445cb --- media/libmedia/Android.bp | 1 + media/libstagefright/omx/Android.bp | 1 + media/libstagefright/xmlparser/Android.bp | 1 + 3 files changed, 3 insertions(+) diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 9a1ac53ab2..5853e4bd31 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -21,6 +21,7 @@ cc_library { vndk: { enabled: true, }, + double_loadable: true, srcs: ["AudioParameter.cpp", "TypeConverter.cpp"], cflags: [ "-Werror", diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 362b7f5a18..4383004e8d 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -87,6 +87,7 @@ cc_library_shared { vndk: { enabled: true, }, + double_loadable: true, srcs: ["OMXUtils.cpp"], export_include_dirs: [ "include", diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp index bebfb3bd64..819058cff2 100644 --- a/media/libstagefright/xmlparser/Android.bp +++ b/media/libstagefright/xmlparser/Android.bp @@ -10,6 +10,7 @@ cc_library_shared { vndk: { enabled: true, }, + double_loadable: true, srcs: [ "MediaCodecsXmlParser.cpp", -- GitLab From 5bb4c0ab86880f322cc2ce59b673d83b5e7a02e4 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 11 Feb 2019 11:09:34 -0800 Subject: [PATCH 0949/1530] improve performance of oggextractor::findNextPage() instead of "test, advance 1, test", look at the 4 bytes that we used in the test to see the soonest our test could possibly succeed (e.g. where do we find the right leading character) and advance that far instead of just 1. Bug: 124238340 Test: manual --- media/extractors/ogg/OggExtractor.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index ba40690327..cb5f17337e 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -364,7 +364,13 @@ status_t MyOggExtractor::findNextPage( return OK; } - ++*pageOffset; + // see how far ahead to skip; avoid some fruitless comparisons + unsigned int i; + for (i = 1; i < 4 ; i++) { + if (signature[i] == 'O') + break; + } + *pageOffset += i; } } -- GitLab From 372e8f2b19f3a0018b926f226c8ad0ca62320652 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 11 Feb 2019 11:38:31 -0800 Subject: [PATCH 0950/1530] Improve concurrency access for updateMetrics() nuplayer's updateMetrics() referenced an unprotected shared stats buffer. It's a small buffer, so we now make a copy during updateMetrics() [at a point where we are mutexed] instead of putting a mutex on the underlying frequently used construct. Ensure that nuplayer2 has the same protections. Bug: 123256408 Test: race condition --- media/libmediaplayer2/nuplayer2/NuPlayer2.cpp | 7 +++++++ media/libmediaplayer2/nuplayer2/NuPlayer2.h | 1 + media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp | 5 ++++- media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp | 7 ++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp index 5da6e2496a..100cdaed2d 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp @@ -1289,6 +1289,7 @@ void NuPlayer2::onMessageReceived(const sp &msg) { } else if (what == DecoderBase::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); if (audio) { + Mutex::Autolock autoLock(mDecoderLock); mAudioDecoder.clear(); mAudioDecoderError = false; ++mAudioDecoderGeneration; @@ -1296,6 +1297,7 @@ void NuPlayer2::onMessageReceived(const sp &msg) { CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER); mFlushingAudio = SHUT_DOWN; } else { + Mutex::Autolock autoLock(mDecoderLock); mVideoDecoder.clear(); mVideoDecoderError = false; ++mVideoDecoderGeneration; @@ -1967,6 +1969,7 @@ void NuPlayer2::restartAudio( int64_t currentPositionUs, bool forceNonOffload, bool needsToCreateAudioDecoder) { if (mAudioDecoder != NULL) { mAudioDecoder->pause(); + Mutex::Autolock autoLock(mDecoderLock); mAudioDecoder.clear(); mAudioDecoderError = false; ++mAudioDecoderGeneration; @@ -2085,6 +2088,8 @@ status_t NuPlayer2::instantiateDecoder( } } + Mutex::Autolock autoLock(mDecoderLock); + if (audio) { sp notify = new AMessage(kWhatAudioNotify, this); ++mAudioDecoderGeneration; @@ -2395,6 +2400,8 @@ void NuPlayer2::getStats(Vector > *mTrackStats) { CHECK(mTrackStats != NULL); mTrackStats->clear(); + + Mutex::Autolock autoLock(mDecoderLock); if (mVideoDecoder != NULL) { mTrackStats->push_back(mVideoDecoder->getStats()); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h index 798c725a6e..b8fb988056 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h @@ -197,6 +197,7 @@ private: sp mVideoDecoder; bool mOffloadAudio; sp mAudioDecoder; + Mutex mDecoderLock; // guard |mAudioDecoder| and |mVideoDecoder|. sp mCCDecoder; sp mRenderer; sp mRendererLooper; diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp index 9729d86964..66bfae5a60 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp @@ -109,7 +109,10 @@ sp NuPlayer2::Decoder::getStats() const { mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); mStats->setFloat("frame-rate-total", mFrameRateTotal); - return mStats; + // i'm mutexed right now. + // make our own copy, so we aren't victim to any later changes. + sp copiedStats = mStats->dup(); + return copiedStats; } status_t NuPlayer2::Decoder::setVideoSurface(const sp &nww) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 6d69d5001a..2f0da2d783 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -107,11 +107,16 @@ NuPlayer::Decoder::~Decoder() { } sp NuPlayer::Decoder::getStats() const { + mStats->setInt64("frames-total", mNumFramesTotal); mStats->setInt64("frames-dropped-input", mNumInputFramesDropped); mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped); mStats->setFloat("frame-rate-total", mFrameRateTotal); - return mStats; + + // i'm mutexed right now. + // make our own copy, so we aren't victim to any later changes. + sp copiedStats = mStats->dup(); + return copiedStats; } status_t NuPlayer::Decoder::setVideoSurface(const sp &surface) { -- GitLab From aa50b1c12307591d6fb18f1d99de67e089aac27a Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Fri, 18 Jan 2019 17:04:01 -0800 Subject: [PATCH 0951/1530] av: stop puller before releasing encoder When encoder is released, it will no longer turn to media codec source for fill-this-buffer. Hence, the buffer queue within puller will not be cleared by encoder. Stop mPuller before releasing encoder to avoid being stucked in AudioSource::waitOutstandingEncodingFrames_l() if audiosource reset() is invoked from SFRecorder destructor. Bug: 123065628 Change-Id: I78ecb2207ae595784204bd6392311dc194af306d --- media/libstagefright/MediaCodecSource.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index 0f758228ba..26797bbc81 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -643,6 +643,10 @@ void MediaCodecSource::signalEOS(status_t err) { output->mBufferQueue.clear(); output->mEncoderReachedEOS = true; output->mErrorCode = err; + if (!(mFlags & FLAG_USE_SURFACE_INPUT)) { + mStopping = true; + mPuller->stop(); + } output->mCond.signal(); reachedEOS = true; -- GitLab From b2a805cdaa1bdd3f47397b05df1984207ba1ee14 Mon Sep 17 00:00:00 2001 From: Yuxiu Zhang Date: Fri, 12 Oct 2018 10:38:15 +0800 Subject: [PATCH 0952/1530] Keep AOT type to support AAC offload feature To support AAC offload feature, AACExtractor needs to parse profile and send to HAL. Therefore, AACExtractor needs to keep AOT type into mMeta, and then AOSP default common flow will pass AOT type to HAL. Bug: 123041584 Test: build pass in Android Qt and verified AAC offload feature in Android Pie Change-Id: Ie954546835c682cd32b5885af92dc715414cd8ea --- media/extractors/aac/AACExtractor.cpp | 1 + media/libstagefright/Utils.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp index beddad031f..9d183d498d 100644 --- a/media/extractors/aac/AACExtractor.cpp +++ b/media/extractors/aac/AACExtractor.cpp @@ -150,6 +150,7 @@ AACExtractor::AACExtractor( mMeta = AMediaFormat_new(); MakeAACCodecSpecificData(mMeta, profile, sf_index, channel); + AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_AAC_PROFILE, profile + 1); off64_t streamSize, numFrames = 0; size_t frameSize = 0; diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 82f7026505..fa45da4b12 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1693,6 +1693,11 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { meta->setInt32(kKeyIsADTS, isADTS); } + int32_t aacProfile = -1; + if (msg->findInt32("aac-profile", &aacProfile)) { + meta->setInt32(kKeyAACAOT, aacProfile); + } + int32_t pcmEncoding; if (msg->findInt32("pcm-encoding", &pcmEncoding)) { meta->setInt32(kKeyPcmEncoding, pcmEncoding); -- GitLab From aa2e78dadbf67824c2735a3f11dd0c0381fc964b Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 1 Feb 2019 16:49:55 +0530 Subject: [PATCH 0953/1530] Fix: Handle incomplete work Test: VtsHidlC2V1_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/ Bug: 80272610 Change-Id: I2a6476c2a205befa2447e94513e32b1a4ddad610 --- .../common/media_c2_hidl_test_common.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp index 64a458cb7a..31da111a89 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp +++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp @@ -107,22 +107,24 @@ void workDone( component->config(configParam, C2_DONT_BLOCK, &failures); ASSERT_EQ(failures.size(), 0u); } - framesReceived++; - eos = (work->worklets.front()->output.flags & - C2FrameData::FLAG_END_OF_STREAM) != 0; - auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(), - work->input.ordinal.frameIndex.peeku()); - ALOGV("WorkDone: frameID received %d", - (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); - work->input.buffers.clear(); - work->worklets.clear(); - { - typedef std::unique_lock ULock; - ULock l(queueLock); - workQueue.push_back(std::move(work)); - if (!flushedIndices.empty()) { - flushedIndices.erase(frameIndexIt); + if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) { + framesReceived++; + eos = (work->worklets.front()->output.flags & + C2FrameData::FLAG_END_OF_STREAM) != 0; + auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(), + work->input.ordinal.frameIndex.peeku()); + ALOGV("WorkDone: frameID received %d", + (int)work->worklets.front()->output.ordinal.frameIndex.peeku()); + work->input.buffers.clear(); + work->worklets.clear(); + { + typedef std::unique_lock ULock; + ULock l(queueLock); + workQueue.push_back(std::move(work)); + if (!flushedIndices.empty()) { + flushedIndices.erase(frameIndexIt); + } + queueCondition.notify_all(); } - queueCondition.notify_all(); } } \ No newline at end of file -- GitLab From 7a04aab821bbe451e14c5f8c5c365678c4898eff Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 1 Feb 2019 12:23:59 +0530 Subject: [PATCH 0954/1530] Codec2: Fix config-start order Test: VtsHidlC2V1_0TargetVideoEncTest -I software -C c2.android.hevc.encoder -P /sdcard/res/ VtsHidlC2V1_0TargetAudioEncTest -I software -C c2.android.aac.encoder -P /sdcard/res/ Bug: 80272610 Change-Id: I808cf1d65b14d22250a3d90a46e043efa30d4629 --- .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 42 ++++++++++++++----- .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 22 ++++++---- .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 36 +++++++++------- 3 files changed, 68 insertions(+), 32 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index d4b973fd5f..d3b37d782b 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -272,7 +272,7 @@ void validateComponent( } // Set Default config param. -void setupConfigParam( +bool setupConfigParam( const std::shared_ptr& component, int32_t* bitStreamInfo) { std::vector> failures; @@ -282,8 +282,8 @@ void setupConfigParam( std::vector configParam{&sampleRateInfo, &channelCountInfo}; c2_status_t status = component->config(configParam, C2_DONT_BLOCK, &failures); - ASSERT_EQ(failures.size(), 0u); - ASSERT_EQ(status, C2_OK); + if (status == C2_OK && failures.size() == 0u) return true; + return false; } // In decoder components, often the input parameters get updated upon @@ -557,7 +557,11 @@ TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { ASSERT_NO_FATAL_FAILURE( getInputChannelInfo(mComponent, mCompName, bitStreamInfo)); } - setupConfigParam(mComponent, bitStreamInfo); + if (!setupConfigParam(mComponent, bitStreamInfo)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); ALOGV("mURL : %s", mURL); eleStream.open(mURL, std::ifstream::binary); ASSERT_EQ(eleStream.is_open(), true); @@ -613,7 +617,6 @@ TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) { description("Test Request for thumbnail"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); char mURL[512], info[512]; std::ifstream eleStream, eleInfo; @@ -642,7 +645,11 @@ TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) { ASSERT_NO_FATAL_FAILURE( getInputChannelInfo(mComponent, mCompName, bitStreamInfo)); } - setupConfigParam(mComponent, bitStreamInfo); + if (!setupConfigParam(mComponent, bitStreamInfo)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); ALOGV("mURL : %s", mURL); // request EOS for thumbnail @@ -711,7 +718,6 @@ TEST_F(Codec2AudioDecHidlTest, FlushTest) { description("Tests Flush calls"); if (mDisableTest) return; typedef std::unique_lock ULock; - ASSERT_EQ(mComponent->start(), C2_OK); char mURL[512], info[512]; std::ifstream eleStream, eleInfo; @@ -741,7 +747,11 @@ TEST_F(Codec2AudioDecHidlTest, FlushTest) { ASSERT_NO_FATAL_FAILURE( getInputChannelInfo(mComponent, mCompName, bitStreamInfo)); } - setupConfigParam(mComponent, bitStreamInfo); + if (!setupConfigParam(mComponent, bitStreamInfo)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); ALOGV("mURL : %s", mURL); eleStream.open(mURL, std::ifstream::binary); ASSERT_EQ(eleStream.is_open(), true); @@ -833,8 +843,6 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) { description("Decode with multiple empty input frames"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); - char mURL[512], info[512]; std::ifstream eleStream, eleInfo; @@ -868,7 +876,19 @@ TEST_F(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) { frameId++; } eleInfo.close(); - + int32_t bitStreamInfo[2] = {0}; + if (mCompName == raw) { + bitStreamInfo[0] = 8000; + bitStreamInfo[1] = 1; + } else { + ASSERT_NO_FATAL_FAILURE( + getInputChannelInfo(mComponent, mCompName, bitStreamInfo)); + } + if (!setupConfigParam(mComponent, bitStreamInfo)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); ALOGV("mURL : %s", mURL); eleStream.open(mURL, std::ifstream::binary); ASSERT_EQ(eleStream.is_open(), true); diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp index 5d66ee5376..a74d43ee44 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp @@ -219,7 +219,7 @@ void validateComponent( } // Set Default config param. -void setupConfigParam( +bool setupConfigParam( const std::shared_ptr& component, int32_t nChannels, int32_t nSampleRate) { std::vector> failures; @@ -229,8 +229,8 @@ void setupConfigParam( std::vector configParam{&sampleRateInfo, &channelCountInfo}; c2_status_t status = component->config(configParam, C2_DONT_BLOCK, &failures); - ASSERT_EQ(failures.size(), 0u); - ASSERT_EQ(status, C2_OK); + if (status == C2_OK && failures.size() == 0u) return true; + return false; } // LookUpTable of clips and metadata for component testing @@ -358,7 +358,6 @@ TEST_F(Codec2AudioEncHidlTest, validateCompName) { TEST_F(Codec2AudioEncHidlTest, EncodeTest) { ALOGV("EncodeTest"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); char mURL[512]; strcpy(mURL, gEnv->getRes().c_str()); GetURLForComponent(mCompName, mURL); @@ -396,7 +395,11 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) { default: ASSERT_TRUE(false); } - setupConfigParam(mComponent, nChannels, nSampleRate); + if (!setupConfigParam(mComponent, nChannels, nSampleRate)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); std::ifstream eleStream; uint32_t numFrames = 128; eleStream.open(mURL, std::ifstream::binary); @@ -469,7 +472,6 @@ TEST_F(Codec2AudioEncHidlTest, EOSTest) { TEST_F(Codec2AudioEncHidlTest, FlushTest) { description("Test Request for flush"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); typedef std::unique_lock ULock; char mURL[512]; @@ -510,7 +512,13 @@ TEST_F(Codec2AudioEncHidlTest, FlushTest) { default: ASSERT_TRUE(false); } - setupConfigParam(mComponent, nChannels, nSampleRate); + + if (!setupConfigParam(mComponent, nChannels, nSampleRate)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); + std::ifstream eleStream; uint32_t numFramesFlushed = 30; uint32_t numFrames = 128; diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 8585c87edf..95d1b726ee 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -118,7 +118,6 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { } mEos = false; mCsd = false; - mConfig = false; mFramesReceived = 0; mFailedWorkReceived = 0; if (mCompName == unknown_comp) mDisableTest = true; @@ -134,7 +133,7 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { Super::TearDown(); } - void setupConfigParam(int32_t nWidth, int32_t nHeight); + bool setupConfigParam(int32_t nWidth, int32_t nHeight); // callback function to process onWorkDone received by Listener void handleWorkDone(std::list>& workItems) { @@ -228,14 +227,14 @@ void validateComponent( } // Set Default config param. -void Codec2VideoEncHidlTest::setupConfigParam(int32_t nWidth, int32_t nHeight) { +bool Codec2VideoEncHidlTest::setupConfigParam(int32_t nWidth, int32_t nHeight) { std::vector> failures; C2VideoSizeStreamTuning::input inputSize(0u, nWidth, nHeight); std::vector configParam{&inputSize}; c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures); - if (failures.size() == 0u ) mConfig = true; - ASSERT_EQ(status, C2_OK); + if (status == C2_OK && failures.size() == 0u) return true; + return false; } // LookUpTable of clips for component testing @@ -360,8 +359,7 @@ TEST_F(Codec2VideoEncHidlTest, EncodeTest) { ASSERT_EQ(eleStream.is_open(), true) << mURL << " file not found"; ALOGV("mURL : %s", mURL); - setupConfigParam(nWidth, nHeight); - if (!mConfig) { + if (!setupConfigParam(nWidth, nHeight)) { std::cout << "[ WARN ] Test Skipped \n"; return; } @@ -439,7 +437,6 @@ TEST_F(Codec2VideoEncHidlTest, EOSTest) { TEST_F(Codec2VideoEncHidlTest, FlushTest) { description("Test Request for flush"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); typedef std::unique_lock ULock; char mURL[512]; @@ -447,7 +444,12 @@ TEST_F(Codec2VideoEncHidlTest, FlushTest) { int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT; strcpy(mURL, gEnv->getRes().c_str()); GetURLForComponent(mURL); - setupConfigParam(nWidth, nHeight); + + if (!setupConfigParam(nWidth, nHeight)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); // Setting default configuration mFlushedIndices.clear(); @@ -522,12 +524,16 @@ TEST_F(Codec2VideoEncHidlTest, FlushTest) { TEST_F(Codec2VideoEncHidlTest, InvalidBufferTest) { description("Tests feeding larger/smaller input buffer"); if (mDisableTest) return; - ASSERT_EQ(mComponent->start(), C2_OK); std::ifstream eleStream; int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH / 2; int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT / 2; - setupConfigParam(nWidth, nHeight); + + if (!setupConfigParam(nWidth, nHeight)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } + ASSERT_EQ(mComponent->start(), C2_OK); ASSERT_NO_FATAL_FAILURE( encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue, @@ -579,10 +585,12 @@ TEST_P(Codec2VideoEncResolutionTest, ResolutionTest) { int32_t nWidth = GetParam().first; int32_t nHeight = GetParam().second; ALOGD("Trying encode for width %d height %d", nWidth, nHeight); - mConfig = false; mEos = false; - setupConfigParam(nWidth, nHeight); - if (!mConfig) return; + + if (!setupConfigParam(nWidth, nHeight)) { + std::cout << "[ WARN ] Test Skipped \n"; + return; + } ASSERT_EQ(mComponent->start(), C2_OK); ASSERT_NO_FATAL_FAILURE( -- GitLab From 44df34d4f85eb6047c43961802524db95f082c5c Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Tue, 12 Feb 2019 09:30:15 -0800 Subject: [PATCH 0955/1530] Camera: Add NDK support for depth jpeg format Depth jpeg image format must be available for native clients using the Camera NDK. Bug: 124064032 Test: Camera CTS Change-Id: I21f14ea63fe4a568c1cfccd0dcd32fce5cad8098 --- camera/ndk/impl/ACameraMetadata.cpp | 38 +++++++++++++++++++++++++++++ media/ndk/NdkImageReader.cpp | 2 ++ media/ndk/include/media/NdkImage.h | 9 +++++++ 3 files changed, 49 insertions(+) diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index de40990b93..359eaed81a 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -38,6 +38,8 @@ ACameraMetadata::ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYP filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS); filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS); filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS); + filterDurations(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS); + filterDurations(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS); } // TODO: filter request/result keys } @@ -186,6 +188,16 @@ ACameraMetadata::filterDurations(uint32_t tag) { filteredDurations.push_back(duration); } break; + case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS: + case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS: + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_DEPTH_JPEG; + filteredDurations.push_back(format); + filteredDurations.push_back(width); + filteredDurations.push_back(height); + filteredDurations.push_back(duration); + } + break; default: // Should not reach here ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag); @@ -284,6 +296,32 @@ ACameraMetadata::filterStreamConfigurations() { filteredHeicStreamConfigs.push_back(isInput); } mData.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs); + + entry = mData.find(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS); + Vector filteredDynamicDepthStreamConfigs; + filteredDynamicDepthStreamConfigs.setCapacity(entry.count); + + for (size_t i = 0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) { + int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET]; + int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET]; + int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET]; + int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET]; + if (isInput == ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_INPUT) { + // Hide input streams + continue; + } + // Translate HAL formats to NDK format + if (format == HAL_PIXEL_FORMAT_BLOB) { + format = AIMAGE_FORMAT_DEPTH_JPEG; + } + + filteredDynamicDepthStreamConfigs.push_back(format); + filteredDynamicDepthStreamConfigs.push_back(width); + filteredDynamicDepthStreamConfigs.push_back(height); + filteredDynamicDepthStreamConfigs.push_back(isInput); + } + mData.update(ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, + filteredDynamicDepthStreamConfigs); } bool diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index c3eb4379db..a14bafbaed 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -70,6 +70,7 @@ AImageReader::isSupportedFormatAndUsage(int32_t format, uint64_t usage) { case AIMAGE_FORMAT_DEPTH_POINT_CLOUD: case AIMAGE_FORMAT_Y8: case AIMAGE_FORMAT_HEIC: + case AIMAGE_FORMAT_DEPTH_JPEG: return true; case AIMAGE_FORMAT_PRIVATE: // For private format, cpu usage is prohibited. @@ -98,6 +99,7 @@ AImageReader::getNumPlanesForFormat(int32_t format) { case AIMAGE_FORMAT_DEPTH_POINT_CLOUD: case AIMAGE_FORMAT_Y8: case AIMAGE_FORMAT_HEIC: + case AIMAGE_FORMAT_DEPTH_JPEG: return 1; case AIMAGE_FORMAT_PRIVATE: return 0; diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h index 14d88cbbb2..3e60de0ab6 100644 --- a/media/ndk/include/media/NdkImage.h +++ b/media/ndk/include/media/NdkImage.h @@ -535,6 +535,15 @@ enum AIMAGE_FORMATS { * Format as described in ISO/IEC 23008-12.

    */ AIMAGE_FORMAT_HEIC = 0x48454946, + + /** + * Depth augmented compressed JPEG format. + * + *

    JPEG compressed main image along with XMP embedded depth metadata + * following ISO 16684-1:2011(E).

    + */ + AIMAGE_FORMAT_DEPTH_JPEG = 0x69656963, + }; /** -- GitLab From 062780418f2d259b3649e18bb48c0502943bf9b4 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 12 Feb 2019 12:20:02 -0800 Subject: [PATCH 0956/1530] Log nuplayer2 version information add apk/pkg version for mediaplayer2 / nuplayer2 media.metrics info. Bug: 119817586 Test: logcat --- media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp | 12 ++++++++++-- media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp index 1b661f291e..1876496c0a 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp @@ -81,7 +81,7 @@ private: }; // key for media statistics -static const char *kKeyPlayer = "nuplayer"; +static const char *kKeyPlayer = "nuplayer2"; // attrs for media statistics // NB: these are matched with public Java API constants defined // in frameworks/base/media/java/android/media/MediaPlayer2.java @@ -108,6 +108,8 @@ static const char *kPlayerRebuffering = "android.media.mediaplayer.rebufferingMs static const char *kPlayerRebufferingCount = "android.media.mediaplayer.rebuffers"; static const char *kPlayerRebufferingAtExit = "android.media.mediaplayer.rebufferExit"; +static const char *kPlayerVersion = "android.media.mediaplayer.version"; + NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp &context) : mState(STATE_IDLE), @@ -127,6 +129,7 @@ NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp & mPlayer(new NuPlayer2(pid, uid, mMediaClock, context)), mPlayerFlags(0), mMetricsHandle(0), + mPlayerVersion(0), mClientUid(uid), mAtEOS(false), mLooping(false), @@ -137,9 +140,13 @@ NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid, const sp & mMediaClock->init(); + // XXX: what version are we? + // Ideally, this ticks with the apk version info for the APEX packaging + // set up media metrics record mMetricsHandle = mediametrics_create(kKeyPlayer); mediametrics_setUid(mMetricsHandle, mClientUid); + mediametrics_setInt64(mMetricsHandle, kPlayerVersion, mPlayerVersion); mNuPlayer2Looper->start( false, /* runOnCallingThread */ @@ -473,7 +480,7 @@ void NuPlayer2Driver::updateMetrics(const char *where) { float frameRate = 0; if (stats->findFloat("frame-rate-output", &frameRate)) { mediametrics_setInt64(mMetricsHandle, kPlayerFrameRate, frameRate); - } + } } else if (mime.startsWith("audio/")) { mediametrics_setCString(mMetricsHandle, kPlayerAMime, mime.c_str()); @@ -524,6 +531,7 @@ void NuPlayer2Driver::logMetrics(const char *where) { mediametrics_delete(mMetricsHandle); mMetricsHandle = mediametrics_create(kKeyPlayer); mediametrics_setUid(mMetricsHandle, mClientUid); + mediametrics_setInt64(mMetricsHandle, kPlayerVersion, mPlayerVersion); } else { ALOGV("did not have anything to record"); } diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h index 3d299f3582..c97e247c14 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h @@ -134,6 +134,7 @@ private: uint32_t mPlayerFlags; mediametrics_handle_t mMetricsHandle; + int64_t mPlayerVersion; uid_t mClientUid; bool mAtEOS; -- GitLab From 610ae69f4d609954ee8a948f0b13864eb77052bf Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 10 Jan 2019 16:42:06 +0800 Subject: [PATCH 0957/1530] Cannot play mp4 file because of abnormal nal length Problem: some mp4 file has abnormal nal size and cannot be played. Solution: ignore when nal size is abnormal Bug: 122749422 Test: Play a file which has abnormal nal size and check if it can ignore abnormal ones and play normally after abnormal frames Change-Id: Ic0a53d20ea7e395ab610b8d4895dd3c64ab68106 --- media/extractors/mp4/MPEG4Extractor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 3415518f5f..f588b753ab 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5659,10 +5659,10 @@ media_status_t MPEG4Source::read( } if (isMalFormed) { - ALOGE("Video is malformed"); - mBuffer->release(); - mBuffer = NULL; - return AMEDIA_ERROR_MALFORMED; + //if nallength abnormal,ignore it. + ALOGW("abnormal nallength, ignore this NAL"); + srcOffset = size; + break; } if (nalLength == 0) { -- GitLab From 3866c7eb51ad35dfcca170b7b2ba8404c5f0c84a Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 31 Jan 2019 05:21:29 -0800 Subject: [PATCH 0958/1530] Use the updated hybrid interface Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 111902308 Change-Id: I7619b6ed3279cc2e31c230b9ab8bd1bca22e16d7 --- media/codec2/hidl/1.0/utils/types.cpp | 3 ++- media/codec2/hidl/client/client.cpp | 4 +++- media/libmedia/IOMX.cpp | 2 +- media/libmedia/include/media/omx/1.0/WOmxNode.h | 2 +- media/libmedia/omx/1.0/WGraphicBufferSource.cpp | 2 +- media/libstagefright/ACodec.cpp | 10 ++++++---- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 02cdc23c50..343bcb5433 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -1803,7 +1803,8 @@ void forEachBlock(const std::list>& workList, } sp getHgbp(const sp& igbp) { - sp hgbp = igbp->getHalInterface(); + sp hgbp = + igbp->getHalInterface(); return hgbp ? hgbp : new TWGraphicBufferProducer(igbp); } diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 3808be57d2..7a2e549f4b 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -1064,7 +1064,9 @@ c2_status_t Codec2Client::Component::setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, uint32_t generation) { - sp igbp = surface->getHalInterface(); + sp igbp = + surface->getHalInterface(); + if (!igbp) { igbp = new TWGraphicBufferProducer(surface); } diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index a073081f52..747b88f6e4 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -552,7 +552,7 @@ public: }; IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); -IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, IOmxNode, "android.hardware.IOMXNode"); +IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); //////////////////////////////////////////////////////////////////////////////// diff --git a/media/libmedia/include/media/omx/1.0/WOmxNode.h b/media/libmedia/include/media/omx/1.0/WOmxNode.h index eebc8c678d..1db4248a5f 100644 --- a/media/libmedia/include/media/omx/1.0/WOmxNode.h +++ b/media/libmedia/include/media/omx/1.0/WOmxNode.h @@ -59,7 +59,7 @@ using ::android::sp; * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object. */ -struct LWOmxNode : public H2BConverter { +struct LWOmxNode : public H2BConverter { LWOmxNode(sp const& base) : CBase(base) {} status_t freeNode() override; status_t sendCommand( diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp index 31d1df905c..1ed1d070c8 100644 --- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp +++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp @@ -32,7 +32,7 @@ LWGraphicBufferSource::LWGraphicBufferSource( BnStatus LWGraphicBufferSource::configure( const sp& omxNode, int32_t dataSpace) { - sp hOmxNode = omxNode->getHalInterface(); + sp hOmxNode = omxNode->getHalInterface(); return toBinderStatus(mBase->configure( hOmxNode == nullptr ? new TWOmxNode(omxNode) : hOmxNode, toHardwareDataspace(dataSpace))); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 52cb5fa305..a105f87d8a 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -6539,8 +6539,10 @@ void ACodec::UninitializedState::stateEntered() { if (mDeathNotifier != NULL) { if (mCodec->mOMXNode != NULL) { - auto tOmxNode = mCodec->mOMXNode->getHalInterface(); - tOmxNode->unlinkToDeath(mDeathNotifier); + auto tOmxNode = mCodec->mOMXNode->getHalInterface(); + if (tOmxNode) { + tOmxNode->unlinkToDeath(mDeathNotifier); + } } mDeathNotifier.clear(); } @@ -6668,8 +6670,8 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { } mDeathNotifier = new DeathNotifier(notify); - auto tOmxNode = omxNode->getHalInterface(); - if (!tOmxNode->linkToDeath(mDeathNotifier, 0)) { + auto tOmxNode = omxNode->getHalInterface(); + if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) { mDeathNotifier.clear(); } -- GitLab From 11279f2e759526f4f750e88d28ce6d350009d693 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 12 Feb 2019 16:27:56 -0800 Subject: [PATCH 0959/1530] media: fix OMX aliases for audio encoders Bug: 124332267 Change-Id: Iba0be7fc6a0c1442933520eabef75d8e8201bc83 --- .../libstagefright/data/media_codecs_google_c2_audio.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml index f664395b99..47a9715184 100644 --- a/media/libstagefright/data/media_codecs_google_c2_audio.xml +++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml @@ -79,28 +79,28 @@
    - + - + - + - + -- GitLab From c48ddcfc9113215b35e79c08ed77afb8998bd919 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 11 Feb 2019 16:16:57 -0800 Subject: [PATCH 0960/1530] CCodec: fix image data update handling Formats should not be written after it's shared with the client. When update is needed, duplicate the format to avoid race condition. Bug: 122987730 Test: run MediaMetadataRetrieverTest#testGetFramesAtIndex for 1000 times Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I6abb5526d8df1e57b70c96f5b32d132e4a5de389 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 30 +++++++++++++++++++ media/codec2/sfplugin/Codec2Buffer.cpp | 17 ++--------- media/codec2/sfplugin/Codec2Buffer.h | 6 +++- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 6842fa543d..7a10e4c4e0 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -255,6 +255,34 @@ public: mSkipCutBuffer = scb; } + void handleImageData(const sp &buffer) { + sp imageDataCandidate = buffer->getImageData(); + if (imageDataCandidate == nullptr) { + return; + } + sp imageData; + if (!mFormat->findBuffer("image-data", &imageData) + || imageDataCandidate->size() != imageData->size() + || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) { + ALOGD("[%s] updating image-data", mName); + sp newFormat = dupFormat(); + newFormat->setBuffer("image-data", imageDataCandidate); + MediaImage2 *img = (MediaImage2*)imageDataCandidate->data(); + if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { + int32_t stride = img->mPlane[0].mRowInc; + newFormat->setInt32(KEY_STRIDE, stride); + ALOGD("[%s] updating stride = %d", mName, stride); + if (img->mNumPlanes > 1 && stride > 0) { + int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; + newFormat->setInt32(KEY_SLICE_HEIGHT, vstride); + ALOGD("[%s] updating vstride = %d", mName, vstride); + } + } + setFormat(newFormat); + buffer->setFormat(newFormat); + } + } + protected: sp mSkipCutBuffer; @@ -1152,6 +1180,7 @@ public: return WOULD_BLOCK; } submit(c2Buffer); + handleImageData(c2Buffer); *clientBuffer = c2Buffer; ALOGV("[%s] grabbed buffer %zu", mName, *index); return OK; @@ -1250,6 +1279,7 @@ public: } newBuffer->setFormat(mFormat); *index = mImpl.assignSlot(newBuffer); + handleImageData(newBuffer); *clientBuffer = newBuffer; ALOGV("[%s] registered buffer %zu", mName, *index); return OK; diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 2dec42ed15..13b63c9f93 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -84,17 +84,7 @@ bool Codec2Buffer::copyLinear(const std::shared_ptr &buffer) { } void Codec2Buffer::setImageData(const sp &imageData) { - meta()->setBuffer("image-data", imageData); - format()->setBuffer("image-data", imageData); - MediaImage2 *img = (MediaImage2*)imageData->data(); - if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { - int32_t stride = img->mPlane[0].mRowInc; - format()->setInt32(KEY_STRIDE, stride); - if (img->mNumPlanes > 1 && stride > 0) { - int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; - format()->setInt32(KEY_SLICE_HEIGHT, vstride); - } - } + mImageData = imageData; } // LocalLinearBuffer @@ -546,7 +536,6 @@ GraphicBlockBuffer::GraphicBlockBuffer( : Codec2Buffer(format, buffer), mView(view), mBlock(block), - mImageData(imageData), mWrapped(wrapped) { setImageData(imageData); } @@ -683,9 +672,7 @@ ConstGraphicBlockBuffer::ConstGraphicBlockBuffer( mView(std::move(view)), mBufferRef(buffer), mWrapped(wrapped) { - if (imageData != nullptr) { - setImageData(imageData); - } + setImageData(imageData); } std::shared_ptr ConstGraphicBlockBuffer::asC2Buffer() { diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h index 481975fd24..dd618aab3d 100644 --- a/media/codec2/sfplugin/Codec2Buffer.h +++ b/media/codec2/sfplugin/Codec2Buffer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,8 @@ public: return false; } + sp getImageData() const { return mImageData; } + protected: /** * canCopy() implementation for linear buffers. @@ -100,6 +103,8 @@ protected: * sets MediaImage data for flexible graphic buffers */ void setImageData(const sp &imageData); + + sp mImageData; }; /** @@ -239,7 +244,6 @@ private: C2GraphicView mView; std::shared_ptr mBlock; - sp mImageData; const bool mWrapped; }; -- GitLab From c548458d0e2a0a0ef32d0498397498a4b210d363 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 12 Feb 2019 16:52:36 -0800 Subject: [PATCH 0961/1530] ACodec: don't enable B frames for AVC by default The default behavior has been no B-frames for AVC encoders, and it should stay that way. The explicit setting for enabling the feature is under discussion. Bug: 122855205 Test: builds Change-Id: I76bae5ad78ef9ecbfadecc64c3bd4e9eb8f58011 --- media/libstagefright/ACodec.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 52cb5fa305..9c4665987b 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -4431,9 +4431,9 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { h264type.nRefFrames = 2; h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1); - // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames - // h264type.nRefFrames = 1; - // h264type.nBFrames = 0; + // disable B-frames until we have explicit settings for enabling the feature. + h264type.nRefFrames = 1; + h264type.nBFrames = 0; h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames); h264type.nAllowedPictureTypes = -- GitLab From 2224c5a0f7ff52d8b40cc3e8b67f3f71560f842e Mon Sep 17 00:00:00 2001 From: juyuchen Date: Mon, 21 Jan 2019 12:00:58 +0800 Subject: [PATCH 0962/1530] audio: use SinkMetaData to route voice call with hearing aid. Bug: 121173695 Test: manual audio test Change-Id: If51b7c5063d0578ec4b8b16ec8ccd1712ae2c85c --- services/audioflinger/PatchPanel.cpp | 12 ++++-------- .../managerdefault/AudioPolicyManager.cpp | 4 ++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 3381e4d9b9..676a575c06 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -211,8 +211,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) && ((patch->sinks[0].ext.device.hw_module != srcModule) || !audioHwDevice->supportsAudioPatches()))) { - audio_devices_t outputDevice = AUDIO_DEVICE_NONE; - String8 outputDeviceAddress; + audio_devices_t outputDevice = patch->sinks[0].ext.device.type; + String8 outputDeviceAddress = String8(patch->sinks[0].ext.device.address); if (patch->num_sources == 2) { if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module != @@ -234,8 +234,6 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa reinterpret_cast(thread.get()), false /*closeThread*/); } else { audio_config_t config = AUDIO_CONFIG_INITIALIZER; - audio_devices_t device = patch->sinks[0].ext.device.type; - String8 address = String8(patch->sinks[0].ext.device.address); audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE; if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { @@ -254,8 +252,8 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa patch->sinks[0].ext.device.hw_module, &output, &config, - device, - address, + outputDevice, + outputDeviceAddress, flags); ALOGV("mAudioFlinger.openOutput_l() returned %p", thread.get()); if (thread == 0) { @@ -263,8 +261,6 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa goto exit; } newPatch.mPlayback.setThread(reinterpret_cast(thread.get())); - outputDevice = device; - outputDeviceAddress = address; } audio_devices_t device = patch->sources[0].ext.device.type; String8 address = String8(patch->sources[0].ext.device.address); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5c2272728b..32cc380719 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -560,6 +560,10 @@ uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, ui muteWaitMs = setOutputDevices(mPrimaryOutput, rxDevices, true, delayMs); } else { // create RX path audio patch mCallRxPatch = createTelephonyPatch(true /*isRx*/, rxDevices.itemAt(0), delayMs); + + // If the TX device is on the primary HW module but RX device is + // on other HW module, SinkMetaData of telephony input should handle it + // assuming the device uses audio HAL V5.0 and above } if (createTxPatch) { // create TX path audio patch mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs); -- GitLab From 46fe0c628136a170029b17d101fef7d5e157011f Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Wed, 13 Feb 2019 20:53:28 +0900 Subject: [PATCH 0963/1530] Revert "Configure com.android.media APEX to prefer cfi variants" This reverts commit 293eb84e30268ab483a64dc587798a1b5c1d28f9. Reason for revert: the new property is no longer needed. Bug: 124128094 Test: m on marlin The extractor libs are found under /system/apex/com.android.media Change-Id: I1c25ff7b94f64ac995bae37fb21d79fd8230e212 --- apex/Android.bp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 7085d597d6..c077a77e25 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -42,9 +42,6 @@ apex { }, }, key: "com.android.media.key", - prefer_sanitize: { - cfi: true, - }, } apex { -- GitLab From 94e65974e03d89863d72f0142cb928b2ddb21912 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 13 Feb 2019 09:06:48 -0800 Subject: [PATCH 0964/1530] media: make stagefright recorder temporal layering limit OEM configurable Bug: 124023478 Change-Id: Ibbe25871805b251878309a3b8aeefa26c1f8f482 --- media/libmediaplayerservice/Android.bp | 1 + .../StagefrightRecorder.cpp | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp index 55867a56a2..22fa495f91 100644 --- a/media/libmediaplayerservice/Android.bp +++ b/media/libmediaplayerservice/Android.bp @@ -12,6 +12,7 @@ cc_library_shared { shared_libs: [ "android.hardware.media.omx@1.0", + "libbase", "libaudioclient", "libbinder", "libcamera_client", diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 37b13f0be6..d1113136ce 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -24,6 +24,7 @@ #include +#include #include #include @@ -1761,13 +1762,26 @@ status_t StagefrightRecorder::setupVideoEncoder( } } + // Enable temporal layering if the expected (max) playback frame rate is greater than ~11% of + // the minimum display refresh rate on a typical device. Add layers until the base layer falls + // under this limit. Allow device manufacturers to override this limit. + + // TODO: make this configurable by the application + std::string maxBaseLayerFpsProperty = + ::android::base::GetProperty("ro.media.recorder-max-base-layer-fps", ""); + float maxBaseLayerFps = (float)::atof(maxBaseLayerFpsProperty.c_str()); + // TRICKY: use !> to fix up any NaN values + if (!(maxBaseLayerFps >= kMinTypicalDisplayRefreshingRate / 0.9)) { + maxBaseLayerFps = kMinTypicalDisplayRefreshingRate / 0.9; + } + for (uint32_t tryLayers = 1; tryLayers <= kMaxNumVideoTemporalLayers; ++tryLayers) { if (tryLayers > tsLayers) { tsLayers = tryLayers; } // keep going until the base layer fps falls below the typical display refresh rate float baseLayerFps = maxPlaybackFps / (1 << (tryLayers - 1)); - if (baseLayerFps < kMinTypicalDisplayRefreshingRate / 0.9) { + if (baseLayerFps < maxBaseLayerFps) { break; } } -- GitLab From 3d1f734702f5053d5a69b3f797e9d902742c43ff Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 12 Feb 2019 15:06:55 -0800 Subject: [PATCH 0965/1530] Add opus extension Bug: 37054258 Test: build Change-Id: I5a9f99b78f31ded8f5c8d56d3d05feb072f003f2 --- media/extractors/ogg/OggExtractor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index cb5f17337e..d99493dce9 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -1388,6 +1388,7 @@ static CreatorFunc Sniff( static const char *extensions[] = { "oga", "ogg", + "opus", NULL }; -- GitLab From 98c7d02d74d86280adbb4178a01f87b9d358b6de Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 10 Jan 2019 15:56:07 +0800 Subject: [PATCH 0966/1530] Cannot play mp4 file which contains ac-3 Problem: ac-3 bit stream identification parse error Solution: skip audio track if ac-3 is not correct Bug: 122746530 Test: Play the video file whose ac-3 is not correct and check if it can skip audio and only play video track Change-Id: I402781a9d7057fd24666652b015d25cc11efa7bd --- media/extractors/mp4/MPEG4Extractor.cpp | 27 ++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 3415518f5f..0e011a379d 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -2685,19 +2685,40 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("ac-3"): { *offset += chunk_size; - return parseAC3SpecificBox(data_offset); + // bypass ac-3 if parse fail + if (parseAC3SpecificBox(data_offset) != OK) { + if (mLastTrack != NULL) { + ALOGW("Fail to parse ac-3"); + mLastTrack->skipTrack = true; + } + } + return OK; } case FOURCC("ec-3"): { *offset += chunk_size; - return parseEAC3SpecificBox(data_offset); + // bypass ec-3 if parse fail + if (parseEAC3SpecificBox(data_offset) != OK) { + if (mLastTrack != NULL) { + ALOGW("Fail to parse ec-3"); + mLastTrack->skipTrack = true; + } + } + return OK; } case FOURCC("ac-4"): { *offset += chunk_size; - return parseAC4SpecificBox(data_offset); + // bypass ac-4 if parse fail + if (parseAC4SpecificBox(data_offset) != OK) { + if (mLastTrack != NULL) { + ALOGW("Fail to parse ac-4"); + mLastTrack->skipTrack = true; + } + } + return OK; } case FOURCC("ftyp"): -- GitLab From cd333fe74677da852cc05dcb24c9e7e4614377ec Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Fri, 8 Feb 2019 13:45:41 -0800 Subject: [PATCH 0967/1530] Camera: bug fixes for HAL buffer manager Test: GCA smoke test + Camera CTS Bug: 120986771 Change-Id: I946fb95d8e685995ebb8cf3d36b0373958bfad09 --- .../device3/Camera3Device.cpp | 38 ++++++++++++------- .../libcameraservice/device3/Camera3Device.h | 2 +- .../device3/Camera3Stream.cpp | 6 ++- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 918dcf775d..2b39254d08 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1027,11 +1027,22 @@ hardware::Return Camera3Device::requestStreamBuffers( return hardware::Void(); } + if (outputStream->isAbandoned()) { + bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED); + allReqsSucceeds = false; + continue; + } + bufRet.streamId = streamId; + size_t handOutBufferCount = outputStream->getOutstandingBuffersCount(); uint32_t numBuffersRequested = bufReq.numBuffersRequested; - size_t totalHandout = outputStream->getOutstandingBuffersCount() + numBuffersRequested; - if (totalHandout > outputStream->asHalStream()->max_buffers) { + size_t totalHandout = handOutBufferCount + numBuffersRequested; + uint32_t maxBuffers = outputStream->asHalStream()->max_buffers; + if (totalHandout > maxBuffers) { // Not able to allocate enough buffer. Exit early for this stream + ALOGE("%s: request too much buffers for stream %d: at HAL: %zu + requesting: %d" + " > max: %d", __FUNCTION__, streamId, handOutBufferCount, + numBuffersRequested, maxBuffers); bufRet.val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED); allReqsSucceeds = false; continue; @@ -2186,12 +2197,11 @@ status_t Camera3Device::waitUntilStateThenRelock(bool active, nsecs_t timeout) { mStatusWaiters++; - // Notify HAL to start draining. We need to notify the HalInterface layer - // even when the device is already IDLE, so HalInterface can reject incoming - // requestStreamBuffers call. if (!active && mUseHalBufManager) { auto streamIds = mOutputStreams.getStreamIds(); - mRequestThread->signalPipelineDrain(streamIds); + if (mStatus == STATUS_ACTIVE) { + mRequestThread->signalPipelineDrain(streamIds); + } mRequestBufferSM.onWaitUntilIdle(); } @@ -5251,6 +5261,11 @@ bool Camera3Device::RequestThread::threadLoop() { ALOGVV("%s: %d: submitting %zu requests in a batch.", __FUNCTION__, __LINE__, mNextRequests.size()); + sp parent = mParent.promote(); + if (parent != nullptr) { + parent->mRequestBufferSM.onSubmittingRequest(); + } + bool submitRequestSuccess = false; nsecs_t tRequestStart = systemTime(SYSTEM_TIME_MONOTONIC); if (mInterface->supportBatchRequest()) { @@ -5261,13 +5276,6 @@ bool Camera3Device::RequestThread::threadLoop() { nsecs_t tRequestEnd = systemTime(SYSTEM_TIME_MONOTONIC); mRequestLatency.add(tRequestStart, tRequestEnd); - if (submitRequestSuccess) { - sp parent = mParent.promote(); - if (parent != nullptr) { - parent->mRequestBufferSM.onRequestSubmitted(); - } - } - if (useFlushLock) { mFlushLock.unlock(); } @@ -6429,9 +6437,11 @@ void Camera3Device::RequestBufferStateMachine::onStreamsConfigured() { return; } -void Camera3Device::RequestBufferStateMachine::onRequestSubmitted() { +void Camera3Device::RequestBufferStateMachine::onSubmittingRequest() { std::lock_guard lock(mLock); mRequestThreadPaused = false; + // inflight map register actually happens in prepareHalRequest now, but it is close enough + // approximation. mInflightMapEmpty = false; if (mStatus == RB_STATUS_STOPPED) { mStatus = RB_STATUS_READY; diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index e5a38bbf61..51a8fd51da 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -1317,7 +1317,7 @@ class Camera3Device : void onInflightMapEmpty(); // Events triggered by RequestThread - void onRequestSubmitted(); + void onSubmittingRequest(); void onRequestThreadPaused(); private: diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index d29e5c083d..0571741ba5 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -588,7 +588,11 @@ status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer, if (mState != STATE_CONFIGURED) { ALOGE("%s: Stream %d: Can't get buffers if stream is not in CONFIGURED state %d", __FUNCTION__, mId, mState); - return INVALID_OPERATION; + if (mState == STATE_ABANDONED) { + return DEAD_OBJECT; + } else { + return INVALID_OPERATION; + } } // Wait for new buffer returned back if we are running into the limit. -- GitLab From 7e5a2042659b0659be82397202261732a797c216 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 6 Feb 2019 16:01:06 -0800 Subject: [PATCH 0968/1530] Camera: rework API1 shim takePicture retry logic onBufferReleased is no longer reliable indicator of capture error due to HAL buffer manager feature. Switch to listen to error callback from HAL directly. Test: API1 CTS + Pixel 3 Bug: 123952355 Change-Id: I7362942f19356583ec66f259b01e963a1af3a205 --- .../libcameraservice/api1/Camera2Client.cpp | 5 ++- .../api1/client2/CaptureSequencer.cpp | 25 +++++++++++++++ .../api1/client2/CaptureSequencer.h | 3 ++ .../api1/client2/JpegProcessor.cpp | 32 ------------------- .../api1/client2/JpegProcessor.h | 9 +----- .../device3/Camera3Device.cpp | 4 +-- 6 files changed, 33 insertions(+), 45 deletions(-) diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index c9c216b3ce..e002e186fb 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -1773,6 +1773,8 @@ void Camera2Client::notifyError(int32_t errorCode, break; } + mCaptureSequencer->notifyError(errorCode, resultExtras); + ALOGE("%s: Error condition %d reported by HAL, requestId %" PRId32, __FUNCTION__, errorCode, resultExtras.requestId); @@ -1927,9 +1929,6 @@ void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) { void Camera2Client::notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) { - (void)resultExtras; - (void)timestamp; - ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64, __FUNCTION__, resultExtras.requestId, timestamp); mCaptureSequencer->notifyShutter(resultExtras, timestamp); diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp index 5029d4babb..88799f969e 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp @@ -117,6 +117,31 @@ void CaptureSequencer::notifyShutter(const CaptureResultExtras& resultExtras, } } +void CaptureSequencer::notifyError(int32_t errorCode, const CaptureResultExtras& resultExtras) { + ATRACE_CALL(); + bool jpegBufferLost = false; + if (errorCode == hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER) { + sp client = mClient.promote(); + if (client == nullptr) { + return; + } + int captureStreamId = client->getCaptureStreamId(); + if (captureStreamId == resultExtras.errorStreamId) { + jpegBufferLost = true; + } + } else if (errorCode == + hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST) { + if (resultExtras.requestId == mShutterCaptureId) { + jpegBufferLost = true; + } + } + + if (jpegBufferLost) { + sp emptyBuffer; + onCaptureAvailable(/*timestamp*/0, emptyBuffer, /*captureError*/true); + } +} + void CaptureSequencer::onResultAvailable(const CaptureResult &result) { ATRACE_CALL(); ALOGV("%s: New result available.", __FUNCTION__); diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h index c23b12da8a..727dd5310b 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h @@ -65,6 +65,9 @@ class CaptureSequencer: void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp); + // Notifications about shutter (capture start) + void notifyError(int32_t errorCode, const CaptureResultExtras& resultExtras); + // Notification from the frame processor virtual void onResultAvailable(const CaptureResult &result); diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index 36395f3442..ddfe5e31dc 100755 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -62,31 +62,6 @@ void JpegProcessor::onFrameAvailable(const BufferItem& /*item*/) { } } -void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/, - int /*streamId*/, const CameraMetadata& /*settings*/) { - // Intentionally left empty -} - -void JpegProcessor::onBufferAcquired(const BufferInfo& /*bufferInfo*/) { - // Intentionally left empty -} - -void JpegProcessor::onBufferReleased(const BufferInfo& bufferInfo) { - ALOGV("%s", __FUNCTION__); - if (bufferInfo.mError) { - // Only lock in case of error, since we get one of these for each - // onFrameAvailable as well, and scheduling may delay this call late - // enough to run into later preview restart operations, for non-error - // cases. - // b/29524651 - ALOGV("%s: JPEG buffer lost", __FUNCTION__); - Mutex::Autolock l(mInputMutex); - mCaptureDone = true; - mCaptureSuccess = false; - mCaptureDoneSignal.signal(); - } -} - status_t JpegProcessor::updateStream(const Parameters ¶ms) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); @@ -181,13 +156,6 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { strerror(-res), res); return res; } - - res = device->addBufferListenerForStream(mCaptureStreamId, this); - if (res != OK) { - ALOGE("%s: Camera %d: Can't add buffer listeneri: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - return res; - } } return OK; } diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h index 53e6836017..977f11d26d 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.h +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h @@ -42,8 +42,7 @@ struct Parameters; * Still image capture output image processing */ class JpegProcessor: - public Thread, public CpuConsumer::FrameAvailableListener, - public camera3::Camera3StreamBufferListener { + public Thread, public CpuConsumer::FrameAvailableListener { public: JpegProcessor(sp client, wp sequencer); ~JpegProcessor(); @@ -51,12 +50,6 @@ class JpegProcessor: // CpuConsumer listener implementation void onFrameAvailable(const BufferItem& item); - // Camera3StreamBufferListener implementation - void onBufferAcquired(const BufferInfo& bufferInfo) override; - void onBufferReleased(const BufferInfo& bufferInfo) override; - void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId, - const CameraMetadata& settings) override; - status_t updateStream(const Parameters ¶ms); status_t deleteStream(); int getStreamId() const; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 2b39254d08..7850e70f1e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -885,14 +885,14 @@ status_t Camera3Device::convertMetadataListToRequestListLocked( return OK; } -status_t Camera3Device::capture(CameraMetadata &request, int64_t* /*lastFrameNumber*/) { +status_t Camera3Device::capture(CameraMetadata &request, int64_t* lastFrameNumber) { ATRACE_CALL(); List requestsList; std::list surfaceMaps; convertToRequestList(requestsList, surfaceMaps, request); - return captureList(requestsList, surfaceMaps, /*lastFrameNumber*/NULL); + return captureList(requestsList, surfaceMaps, lastFrameNumber); } void Camera3Device::convertToRequestList(List& requestsList, -- GitLab From c300a07bf7cc4722540c9068ad5a23ded914ba38 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 13 Feb 2019 14:56:57 -0800 Subject: [PATCH 0969/1530] Camera: fix signalStreamFlush logic 1. Fix off by one error in signalStreamFlush call 2. Make sure signalStreamFlush is called before we toggle request thread idle (which might cause another thread finishing waitUntilIdle() and thus start calling configureStreams) Test: Pixel 3 + camera CTS Bug: 120986771 Change-Id: Ifd6f77ef628ee200c024c7c6a05bde20937c745d --- .../device3/Camera3Device.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 7850e70f1e..865c3f6b01 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4511,7 +4511,7 @@ void Camera3Device::HalInterface::signalPipelineDrain(const std::vector& st return; } - auto err = mHidlSession_3_5->signalStreamFlush(streamIds, mNextStreamConfigCounter); + auto err = mHidlSession_3_5->signalStreamFlush(streamIds, mNextStreamConfigCounter - 1); if (!err.isOk()) { ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str()); return; @@ -5833,16 +5833,16 @@ sp if (mPaused == false) { ALOGV("%s: RequestThread: Going idle", __FUNCTION__); mPaused = true; - // Let the tracker know - sp statusTracker = mStatusTracker.promote(); - if (statusTracker != 0) { - statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE); - } if (mNotifyPipelineDrain) { mInterface->signalPipelineDrain(mStreamIdsToBeDrained); mNotifyPipelineDrain = false; mStreamIdsToBeDrained.clear(); } + // Let the tracker know + sp statusTracker = mStatusTracker.promote(); + if (statusTracker != 0) { + statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE); + } sp parent = mParent.promote(); if (parent != nullptr) { parent->mRequestBufferSM.onRequestThreadPaused(); @@ -5926,16 +5926,16 @@ bool Camera3Device::RequestThread::waitIfPaused() { if (mPaused == false) { mPaused = true; ALOGV("%s: RequestThread: Paused", __FUNCTION__); - // Let the tracker know - sp statusTracker = mStatusTracker.promote(); - if (statusTracker != 0) { - statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE); - } if (mNotifyPipelineDrain) { mInterface->signalPipelineDrain(mStreamIdsToBeDrained); mNotifyPipelineDrain = false; mStreamIdsToBeDrained.clear(); } + // Let the tracker know + sp statusTracker = mStatusTracker.promote(); + if (statusTracker != 0) { + statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE); + } sp parent = mParent.promote(); if (parent != nullptr) { parent->mRequestBufferSM.onRequestThreadPaused(); -- GitLab From 4fa4f2bb4c4beaaf7699c67fcb64e2e4a171f81d Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 13 Feb 2019 11:02:58 -0800 Subject: [PATCH 0970/1530] CCodec: skip first N longest work inside total delay If the client pauses pushing input to the component, work items inside total delay may sit in the pipeline indefinitely. We don't want to error out for those work. Once the client pushes EOS, ensure all work items finishes within reasonable time regardless of delay. Bug: 124355114 Test: manual Test: atest CtsSecurityTestCases:StagefrightTest Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I0e95b2ddb94482da855d70cbe75f6bffaaffe279 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 25 +++++++++++------ media/codec2/sfplugin/CCodecBufferChannel.h | 1 + media/codec2/sfplugin/PipelineWatcher.cpp | 28 ++++++++++--------- media/codec2/sfplugin/PipelineWatcher.h | 2 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 6842fa543d..a278a81d5b 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1557,6 +1557,7 @@ CCodecBufferChannel::CCodecBufferChannel( mCCodecCallback(callback), mNumInputSlots(kSmoothnessFactor), mNumOutputSlots(kSmoothnessFactor), + mDelay(0), mFrameIndex(0u), mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), @@ -2104,11 +2105,13 @@ status_t CCodecBufferChannel::start( } } - mNumInputSlots = - (inputDelay ? inputDelay.value : 0) + - (pipelineDelay ? pipelineDelay.value : 0) + - kSmoothnessFactor; - mNumOutputSlots = (outputDelay ? outputDelay.value : 0) + kSmoothnessFactor; + uint32_t inputDelayValue = inputDelay ? inputDelay.value : 0; + uint32_t pipelineDelayValue = pipelineDelay ? pipelineDelay.value : 0; + uint32_t outputDelayValue = outputDelay ? outputDelay.value : 0; + + mNumInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor; + mNumOutputSlots = outputDelayValue + kSmoothnessFactor; + mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue; // TODO: get this from input format bool secure = mComponent->getName().find(".secure") != std::string::npos; @@ -2397,9 +2400,9 @@ status_t CCodecBufferChannel::start( { Mutexed::Locked watcher(mPipelineWatcher); - watcher->inputDelay(inputDelay ? inputDelay.value : 0) - .pipelineDelay(pipelineDelay ? pipelineDelay.value : 0) - .outputDelay(outputDelay ? outputDelay.value : 0) + watcher->inputDelay(inputDelayValue) + .pipelineDelay(pipelineDelayValue) + .outputDelay(outputDelayValue) .smoothnessFactor(kSmoothnessFactor); watcher->flush(); } @@ -2816,7 +2819,11 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { } PipelineWatcher::Clock::duration CCodecBufferChannel::elapsed() { - return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now()); + // When client pushed EOS, we want all the work to be done quickly. + // Otherwise, component may have stalled work due to input starvation up to + // the sum of the delay in the pipeline. + size_t n = mInputMetEos ? 0 : mDelay; + return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now(), n); } void CCodecBufferChannel::setMetaMode(MetaMode mode) { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 9dccab8918..9ce886aa38 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -236,6 +236,7 @@ private: size_t mNumInputSlots; size_t mNumOutputSlots; + size_t mDelay; Mutexed> mInputBuffers; Mutexed>> mFlushedConfigs; diff --git a/media/codec2/sfplugin/PipelineWatcher.cpp b/media/codec2/sfplugin/PipelineWatcher.cpp index fe0a2c89d3..cdcc41b73e 100644 --- a/media/codec2/sfplugin/PipelineWatcher.cpp +++ b/media/codec2/sfplugin/PipelineWatcher.cpp @@ -127,19 +127,21 @@ bool PipelineWatcher::pipelineFull() const { } PipelineWatcher::Clock::duration PipelineWatcher::elapsed( - const PipelineWatcher::Clock::time_point &now) const { - return std::accumulate( - mFramesInPipeline.begin(), - mFramesInPipeline.end(), - Clock::duration::zero(), - [&now](const Clock::duration ¤t, - const decltype(mFramesInPipeline)::value_type &value) { - Clock::duration elapsed = now - value.second.queuedAt; - ALOGV("elapsed: frameIndex = %llu elapsed = %lldms", - (unsigned long long)value.first, - std::chrono::duration_cast(elapsed).count()); - return current > elapsed ? current : elapsed; - }); + const PipelineWatcher::Clock::time_point &now, size_t n) const { + if (mFramesInPipeline.size() <= n) { + return Clock::duration::zero(); + } + std::vector durations; + for (const decltype(mFramesInPipeline)::value_type &value : mFramesInPipeline) { + Clock::duration elapsed = now - value.second.queuedAt; + ALOGV("elapsed: frameIndex = %llu elapsed = %lldms", + (unsigned long long)value.first, + std::chrono::duration_cast(elapsed).count()); + durations.push_back(elapsed); + } + nth_element(durations.begin(), durations.end(), durations.begin() + n, + std::greater()); + return durations[n]; } } // namespace android diff --git a/media/codec2/sfplugin/PipelineWatcher.h b/media/codec2/sfplugin/PipelineWatcher.h index ce82298730..1c127e4073 100644 --- a/media/codec2/sfplugin/PipelineWatcher.h +++ b/media/codec2/sfplugin/PipelineWatcher.h @@ -54,7 +54,7 @@ public: void flush(); bool pipelineFull() const; - Clock::duration elapsed(const Clock::time_point &now) const; + Clock::duration elapsed(const Clock::time_point &now, size_t n) const; private: uint32_t mInputDelay; -- GitLab From aa484acdc939c84c26e0463783e7c8d5d8d260eb Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 13 Feb 2019 16:54:02 -0800 Subject: [PATCH 0971/1530] CCodec: support legacy bitrate change behavior ACodec used to ignore "video-bitrate" for initial config and "bitrate" for on-demand bitrate change. Let CCodec behave the same way. Bug: 121047280 Test: manual Change-Id: Ief9cbb3e26f888c577d416d2887eb615f80f3dd0 --- media/codec2/sfplugin/CCodec.cpp | 25 +++++++++++++++++++------ media/codec2/sfplugin/CCodec.h | 1 - 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index bc22045bc5..dce3222cef 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -772,8 +772,16 @@ void CCodec::configure(const sp &msg) { } std::vector> configUpdate; + // NOTE: We used to ignore "video-bitrate" at configure; replicate + // the behavior here. + sp sdkParams = msg; + int32_t videoBitrate; + if (sdkParams->findInt32(PARAMETER_KEY_VIDEO_BITRATE, &videoBitrate)) { + sdkParams = msg->dup(); + sdkParams->removeEntryAt(sdkParams->findEntryByName(PARAMETER_KEY_VIDEO_BITRATE)); + } status_t err = config->getConfigUpdateFromSdkParams( - comp, msg, Config::IS_CONFIG, C2_DONT_BLOCK, &configUpdate); + comp, sdkParams, Config::IS_CONFIG, C2_DONT_BLOCK, &configUpdate); if (err != OK) { ALOGW("failed to convert configuration to c2 params"); } @@ -1415,11 +1423,7 @@ void CCodec::signalResume() { (void)mChannel->requestInitialInputBuffers(); } -void CCodec::signalSetParameters(const sp ¶ms) { - setParameters(params); -} - -void CCodec::setParameters(const sp ¶ms) { +void CCodec::signalSetParameters(const sp &msg) { std::shared_ptr comp; auto checkState = [this, &comp] { Mutexed::Locked state(mState); @@ -1433,6 +1437,15 @@ void CCodec::setParameters(const sp ¶ms) { return; } + // NOTE: We used to ignore "bitrate" at setParameters; replicate + // the behavior here. + sp params = msg; + int32_t bitrate; + if (params->findInt32(KEY_BIT_RATE, &bitrate)) { + params = msg->dup(); + params->removeEntryAt(params->findEntryByName(KEY_BIT_RATE)); + } + Mutexed::Locked config(mConfig); /** diff --git a/media/codec2/sfplugin/CCodec.h b/media/codec2/sfplugin/CCodec.h index ba5f5f32e2..b0b3c4f175 100644 --- a/media/codec2/sfplugin/CCodec.h +++ b/media/codec2/sfplugin/CCodec.h @@ -102,7 +102,6 @@ private: void createInputSurface(); void setInputSurface(const sp &surface); status_t setupInputSurface(const std::shared_ptr &surface); - void setParameters(const sp ¶ms); void setDeadline( const TimePoint &now, -- GitLab From 6c3da9786a43a3b354a009fab5c609b07fde13a7 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Fri, 8 Feb 2019 11:28:32 +0530 Subject: [PATCH 0972/1530] libeffects: Fixed bit mismatch seen with 3 channel input The output for a 3 channel input is not bit-exact with that of 2 channel input when concert surround is enabled and tested using lvmtest. Test: local native test (lvmtest) Bug: 124085228 Change-Id: Icb2d1d5d858be0c37cb11dd3734f8a6244d3cc1c --- media/libeffects/lvm/lib/Common/src/Copy_16.c | 2 +- .../lvm/tests/build_and_run_all_unit_tests.sh | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/media/libeffects/lvm/lib/Common/src/Copy_16.c b/media/libeffects/lvm/lib/Common/src/Copy_16.c index 1f9f659964..38584503a6 100644 --- a/media/libeffects/lvm/lib/Common/src/Copy_16.c +++ b/media/libeffects/lvm/lib/Common/src/Copy_16.c @@ -132,8 +132,8 @@ void Copy_Float_Stereo_Mc(const LVM_FLOAT *src, src += NrChannels * (NrFrames - 1); for (ii = NrFrames; ii != 0; ii--) { - dst[0] = src_st[0]; dst[1] = src_st[1]; + dst[0] = src_st[0]; // copy 1 before 0 is required for NrChannels == 3. for (jj = 2; jj < NrChannels; jj++) { dst[jj] = src[jj]; diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 41a4f04448..1a874a3f4f 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -54,21 +54,13 @@ fs_arr=( 192000 ) -ch_arr=( - 1 - 2 - 4 - 6 - 8 -) - # run multichannel effects at different configs, saving only the stereo channel # pair. for flags in "${flags_arr[@]}" do for fs in ${fs_arr[*]} do - for ch in ${ch_arr[*]} + for ch in {1..8} do adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ -o:$testdir/sinesweep_$((ch))_$((fs)).raw -ch:$ch -fs:$fs $flags -- GitLab From bb20d55681f070a201931f11d8cec24793336869 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 14 Feb 2019 11:27:33 -0800 Subject: [PATCH 0973/1530] libmediaextractor: add manufacurer key in Metadata add manufacturer key Bug: 121340676 Test: TBD Change-Id: I59ce39790ab22627ea65684929f1044c9a71bb4c --- media/libstagefright/Utils.cpp | 1 + media/libstagefright/include/media/stagefright/MetaDataBase.h | 3 +++ media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + media/ndk/libmediandk.map.txt | 1 + 5 files changed, 7 insertions(+) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 09424b8859..16b3319c21 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -588,6 +588,7 @@ static std::vector> stringMappings { { "genre", kKeyGenre }, { "location", kKeyLocation }, { "lyricist", kKeyWriter }, + { "manufacturer", kKeyManufacturer }, { "title", kKeyTitle }, { "year", kKeyYear }, } diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index 437bdb79b7..a0407af595 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -144,6 +144,9 @@ enum { // The language code for this media kKeyMediaLanguage = 'lang', // cstring + // The manufacturer code for this media + kKeyManufacturer = 'manu', // cstring + // To store the timed text format data kKeyTextFormatData = 'text', // raw data diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index cd8ecb5d1e..26a6238c6d 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -342,6 +342,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level"; EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location"; EXPORT const char* AMEDIAFORMAT_KEY_LOOP = "loop"; EXPORT const char* AMEDIAFORMAT_KEY_LYRICIST = "lyricist"; +EXPORT const char* AMEDIAFORMAT_KEY_MANUFACTURER = "manufacturer"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE = "max-bitrate"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height"; EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index cc1d9efc31..ddf5291606 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -214,6 +214,7 @@ extern const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOOP __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LYRICIST __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_MANUFACTURER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 171167d62f..7bdd3adba6 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -105,6 +105,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_LOCATION; # var introduced=29 AMEDIAFORMAT_KEY_LOOP; # var introduced=29 AMEDIAFORMAT_KEY_LYRICIST; # var introduced=29 + AMEDIAFORMAT_KEY_MANUFACTURER; # var introduced=29 AMEDIAFORMAT_KEY_MAX_BIT_RATE; # var introduced=29 AMEDIAFORMAT_KEY_MAX_HEIGHT; # var introduced=21 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE; # var introduced=21 -- GitLab From 3ef4f718f24fa593638a6001fba6aa4e3cad4374 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 11 Feb 2019 11:00:31 +0900 Subject: [PATCH 0974/1530] Sign each APEX with different container certificate Each APEX is signed with different certificate. The test certificate (along with the private key) is com.android..x509.pem and com.android..pk8. The files are in the same directory as the APEX is defined and is referenced via android_app_certificate module named com.android..certificate. The test certificate could then be overridden via PRODUCT_CERTIFICATE_OVERRIDES := : Test: jarsigner -verify -verbose -certs out/target/product/blueline/system/apex/com.android.media.apex shows ... X.509, CN=com.android.media, OU=Android, O=Android, L=Mountain View, ST=California, C=US Change-Id: Ic61a7d2ca41254bda79ee5bdd3faf6d429a24e39 --- apex/Android.bp | 6 ++++++ apex/com.android.media.pk8 | Bin 0 -> 2374 bytes apex/com.android.media.x509.pem | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 apex/com.android.media.pk8 create mode 100644 apex/com.android.media.x509.pem diff --git a/apex/Android.bp b/apex/Android.bp index c077a77e25..6e0a908075 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -42,6 +42,7 @@ apex { }, }, key: "com.android.media.key", + certificate: ":com.android.media.certificate", } apex { @@ -65,3 +66,8 @@ apex_key { public_key: "com.android.media.swcodec.avbpubkey", private_key: "com.android.media.swcodec.pem", } + +android_app_certificate { + name: "com.android.media.certificate", + certificate: "com.android.media", +} diff --git a/apex/com.android.media.pk8 b/apex/com.android.media.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..6df741e917e2d26397402d812bd9fe64bcdc8b1f GIT binary patch literal 2374 zcmV-M3Ay$#f(b$b0RS)!1_>&LNQUwEHHuzC;|Zh0)heo0GQbo#YXA< zvfFrO?<%Gj&j509AnBOsVgLW-G-eR|0~8NRx)G*xQ)b=aqV%lcs(ywggr{_n#bz-{ zpL9=73$^7q5ZH_GY0=jfBeh zrAF7VIz-+dC62fk9?~Fg9_W3$-FCaM1zjaeSv@(QV?9x!5UB~C7!Yf&+`(D)i+_}=g?SGG=e z^K27z${cga_RNLb&AHNCYvXX%VsCTbyPjB5b^3WanbaI*CqYMK3j*S+g%xG-U={-` zBxp3zM6`g=rt9t;U4yuDLr}UvLOiz1ue*ASbSO&|pgEb~e*vc86;|z%#e-Xchvi4q zrZY=sVc;A^K0K>@YoWoi5OXTN1QAk`-@VI!@?SAk!1Fkd>?Kg@nT*W4K%?FH63UT- zB;y7B>jg?>Z=j7ewJzjn)VaRy#fOP009Dm0swtOBUxI7 zcKNn#RR&s6W+18nAOKbnj~R(zV5#w+r?{#f;vwBfDJn;rJxP1@mbXwFh4In;aEhh& zY@D_TEHNd`qkyBjqB6Yt9XGMIz_x^jRS~*H(HBMGGR>VT5m8AjwSaWM3qK3m(fv2S z)5texnlKi!W|}koU`Ysm>MLt2o;8y^e+VdlmR$Y&ILZa%3K};RF&Zb8 zcKnsVL`QGDnOM{nNpC-+;W_rl!z`wPf`{nUvjOlaJQ$npU`cE7pOzzpGx6BjPU}y3 z)NyVnN6)*DN(MhVLzXbY5Bpj28KR6*br5)$^CqMDvwzDw!b>#K{1g?Fg9PWIFt`w? zQNw%={}nF3XGcZN*jNUUN!rVe9V)!FmO7QGpsvZLiHU*>k`fY*EoC${OXc0^ZW<(H zh?}7x7519vpL8SIJW=ij7aX^(RsO__sDpa^@7{ zf~7E>3%}7*aAdovU?&o*BJ(Vn?)V*QqB%#s#BcL{jWgp^J@1SfE?Z&cR?-j$({px3 zr>1YSPhzqAw!<*JpPHq*Bxy3Pi^0QhbW%MQ|tD|ZvjrpLC36aDJYAYvG7v#|jsciy+0RaHVdga=R4eYF$ z$T8<)@}EXdLD{Iturhdcrx0kicjT!&#D8Ype&#slw`fYdI1;U59sVhnIyq`Lio8~< zb(W{pw36r^CT8r{mGYnHF+US&{9>@;`xSQi9V4hWrBGWN%R zKC^QgRU_!-FC-%;SNAumTHinZb)S}u*0xC^GQ>MKaa??xl#3#kK{SVwJ zPjCoEsHPJ2K!In7pI~_~A*i6r-1#A=!&6hu*h^Yru7c=Rr^3D=xlhJB$(ECUhRatz3x?3M1e19sk96LsqYkJrAYyXVovgq%~G z^%z6Cp)NN)AAfUBh$xaOVUaA&jP4QkpEngh40u|k%+k5+e}zN3kgG_1wY~U6p*+qu zG|hXXhG)#0X~%P2%tfn{>Am;7A+W`&%=w*MUL}c%S+fMlcYkyAi&mTZ=wsZn1$fYR z2{^T25|mieoW=x+D_kO2=$6YU=)F0*Wv#1zsst*E$ERL@zO(te0)hbm8T{Va8^<`H zJK&=4yed2?X7rmawzI0%+`enTX+qX zRQ(#oex{Gb0TsF+m%ipDQE>mb5u!n)rh(&!G^N9t<5N=Wr1vDjw$8s_yRT4~=< znmIP}wdjSXAH;@swfA+?ppu{`93_Is08TKiQgJ@BU_-is*<@9`1E6I8BJ@dZZ%DBs zYLd)Y3jz2cZrhziR{eSz2WtqUDJTp8N@^8vP&h=2;oB|U3Md=lK4or3vMNR@=G6m# zjCL&rc(xP`opbwT`>au@*e?9GMYB^Sl^#f7A|0a%_OYjd@D7aK0)hbmG}vM^2SHYr z>W9oS{S1KL0omIEyRAw1<(PnZCchVo65U81l)m^vh`LeVnE>)QB6wS7yU@>ZNvLQR zc}wUI{V(vA0uTn|fHF=sza{BqfHm`hUTA|v4T$$1q99BcO-|ogzE0^@5*dmyns8$_ z2>IBBR~Hmot2wgX-Cxkb3OEW;_C3uh91Ftrk#2iE8qrF%q(w^6;8(=gls@sh%ao28 zoNym^7N)R3iEip`!m^@X-vS2H@L#84G>5AdPiNhI+hmtbN~BYu_DKf}xxVe5C`&vZ zRi&wSkcrmBYv2ti$33$nFvc%Mel9$BGaYWOSub`bol`;TIDS+y0)hbn0E441c746U zK3W}I*N|L9-Lb+kmT^`Lhm^*uEBBPCrppQ4Fs`iSXIUYf(w;_c;0dfihuMJK%;U`I zb^=O4g(RHhwF#40dIS%6S59w}bEXKrvA|droM!8maqB^O;#T-Fcc!)p@O{(WF@j=> z3J_1wVe3F2)jYa2bIhZE0i7o_4DY8*FtxYR$P|~e%%br0Fh8n$oIuY-F2ZyfP%0Z` zhp;5GItwq4W1HL+#0)N~pW8$dVOe`9*nv@;KpeE+s?WogUk|0!1IAGmKGrk%HJ&#W stuvke_}g8HijwYYQ Date: Mon, 11 Feb 2019 12:30:20 +0900 Subject: [PATCH 0975/1530] Sign each APEX with different container certificate Each APEX is signed with different certificate. The test certificate (along with the private key) is com.android..x509.pem and com.android..pk8. The files are in the same directory as the APEX is defined and is referenced via android_app_certificate module named com.android..certificate. The test certificate could then be overridden via PRODUCT_CERTIFICATE_OVERRIDES := : Test: jarsigner -verify -verbose -certs out/target/product/blueline/system/apex/com.android.media.swcodec.apex shows ... X.509, CN=com.android.media.swcodec, OU=Android, O=Android, L=Mountain View, ST=California, C=US Change-Id: I3a967fa640ce77177763b78a34a2df05f70ce60f --- apex/Android.bp | 6 +++++ apex/com.android.media.swcodec.pk8 | Bin 0 -> 2374 bytes apex/com.android.media.swcodec.x509.pem | 34 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 apex/com.android.media.swcodec.pk8 create mode 100644 apex/com.android.media.swcodec.x509.pem diff --git a/apex/Android.bp b/apex/Android.bp index 6e0a908075..88b519a3c9 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -53,6 +53,7 @@ apex { ], use_vendor: true, key: "com.android.media.swcodec.key", + certificate: ":com.android.media.swcodec.certificate", } apex_key { @@ -71,3 +72,8 @@ android_app_certificate { name: "com.android.media.certificate", certificate: "com.android.media", } + +android_app_certificate { + name: "com.android.media.swcodec.certificate", + certificate: "com.android.media.swcodec", +} diff --git a/apex/com.android.media.swcodec.pk8 b/apex/com.android.media.swcodec.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..05a4216fef4d286d4bc3e7197be4a8c28c4a3640 GIT binary patch literal 2374 zcmV-M3Ay$#f(b$b0RS)!1_>&LNQUwEHHuzC;|Zh0)heo0J5dT&;k~u zwY^RXww(c~hq9UIy^!{dZ;GIm9}*fqr{ObXuuZ(205TAIs_T^;VRhAHjJu3dLR#); zuY5#if|!nebI6opx;;BKf>^?m$r*%Q+kyA8xO;TA#t!*Ljqt^2WIq2v*o5P(HaNh- zlx> zh1nnGacyxD14BMpa-c-37*0KgV$I%?jcXPz80z8GFCIC`suSk#;!6|6l7})dBCi;g zuHku7Io-sKBaCXGj$vGeFBfH<1&~v-*$$vmCq%NbXektWK_gyV+Bi{lbAo*jS+qCL zWW=qD{M?}d}2=1H}GdaHqdT$y;-pC!Z6GC`Y*fF)(ibgxg9o8?`+?Jz9Z zkInWXjwuwdtxFj*U%qYC>9^?H^qAUuHq?^Kq=ibaCG3kAS7A-bNU|MlQvF0G@|Gaw z4qaG##1LNmKdDvI1X}1+B?J_prli0lPj)DX!w%4U833SHkpcq&009Dm0svfcKnTn$ z-;7kOAPXb#ibObmK-_3K&90*KI(FNwO(P!svurS~8>Ci51lE!km76h`{qcR&J#{S& z76#9)t;T~Nsr*cx%>hSJZvepI--pF^qyt5qVwLFq8sHy0i}6F*L=G%7cV&)GD*W6X zloPKA68&uGo>|D-T?JcFIs?z0b{6yeS`%4-BsT+vKt7~|kZBs*00GURxp#w{lqj2} z+cD~l0eVdWC@LHf6~(1~BXjO~6eH;v_KD0AtVATZJkzH(j88!UPQ9_vM)vfRS}Ay@ zras$SCfnZ;DPV2)ccOM{q}-LH^}fARGfZu-H54%pual8Jk&Q>XI~}Wqj>gbFqU}nw zCnh!RIhU{p905{sH@ZBMPz#Nz?a(R+E5>aiVF!6qu?iQ!2j!JH%j@H|BKYY*c-h@= zXehzG6Vi#}ca#GR@uQM2(p++Kkbxer!QJS~EJZN0ey)kuSD{9)!^zF2VK06u9nqwW z?He*Za@RZ~<KhA=8S_wtgQ(^md(jb5W+F2JjH2Q(=el6x(IGx!9&$8ZhU-@`w9%Ku+f7l!R0eS=%>U7 z%BeZLe;w7_2#N!8Y@S@!!|94%j|`m%iXHC)$iTV&df(R^zC)_!U?8I3WX!nEDj$Uv zRYwCJv4wyjE%gT7S?1%!_z;0l70}6V{5Mb5tlAH+7@%+zqEU%-jlP+@V1Wf@?lU5{{J64zLJyLQ70)hbn0Eqt&`C<7f z9t1XsR>-(jcG86R>V*%C=bY92A@$vB7ZF{o&t-R6+f3R!d~Pv zSD2bdb8*^pu06_Yg$?dsA zWyKO7NhB$4Ah58>HK^i%<=}=aaQl{i}L1 zv}%UP(cyMB@e$R!zBQq~6-9XJjNxZu?w2SVt?Y2qnvg)BS%fSWrzI_0UESWxImL^o zELZm}(o~;mx_;u5wT~&g0flz?p#&{iF+FZNu~<}e8%8FBB`;Th*YuNUg@`4yrgld& zY!L80hp&W74#qtHJY-ur#od|2BMotfk!+dtTZVfW_z~En6q|>mm(O^_4Kg-@9X*N# zR>%!YE08;B*YjKZ0F5d;rJh-P`VoQH=;mByPF+tDCVoUu*Fz4yEdqi80BJ2%YrAaJ zcj*?eyqTJYcR`DDPAA!Pwjf=={>y7_SR8xsHMK=n`taEjWqjURYnI6a+X!JhAA0P* zLpf3prvVV7CTX!{REdQi0tZpbGY$aIq>9veT!Vhi4%xh+9S|089Ea)gH~XD8!f#UM z>WI1uJip2mo<7ZM)rQ`IGw-kn*C;sFN$*=v1-)^SZjS+&b3LwEej}lF&kq7`!|#Q; z%s^$L(kl~H!VwpT>RH{3djvPfw Date: Thu, 14 Feb 2019 21:49:08 -0800 Subject: [PATCH 0976/1530] Add test_com.android.media[.swcodec] Test: make test_com.android.media[.swcodec] vendor/google/build/build_mainline_modules.sh Bug: 123704841 Change-Id: I322cf25460d142b6691aa1e21b51a0cf7840c18e --- apex/Android.bp | 20 +++++++++++++----- apex/testing/Android.bp | 29 +++++++++++++++++++++++++++ apex/testing/test_manifest.json | 4 ++++ apex/testing/test_manifest_codec.json | 4 ++++ 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 apex/testing/Android.bp create mode 100644 apex/testing/test_manifest.json create mode 100644 apex/testing/test_manifest_codec.json diff --git a/apex/Android.bp b/apex/Android.bp index c077a77e25..c316c9f59c 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -apex { - name: "com.android.media", - manifest: "manifest.json", +apex_defaults { + name: "com.android.media-defaults", java_libs: ["updatable-media"], compile_multilib: "both", multilib: { @@ -45,8 +44,13 @@ apex { } apex { - name: "com.android.media.swcodec", - manifest: "manifest_codec.json", + name: "com.android.media", + manifest: "manifest.json", + defaults: ["com.android.media-defaults"], +} + +apex_defaults { + name: "com.android.media.swcodec-defaults", native_shared_libs: [ "libmedia_codecserviceregistrant", ], @@ -54,6 +58,12 @@ apex { key: "com.android.media.swcodec.key", } +apex { + name: "com.android.media.swcodec", + manifest: "manifest_codec.json", + defaults: ["com.android.media.swcodec-defaults"], +} + apex_key { name: "com.android.media.key", public_key: "com.android.media.avbpubkey", diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp new file mode 100644 index 0000000000..701ced732e --- /dev/null +++ b/apex/testing/Android.bp @@ -0,0 +1,29 @@ +// 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. + +apex { + name: "test_com.android.media", + manifest: "test_manifest.json", + file_contexts: "com.android.media", + defaults: ["com.android.media-defaults"], + installable: false, +} + +apex { + name: "test_com.android.media.swcodec", + manifest: "test_manifest_codec.json", + file_contexts: "com.android.media.swcodec", + defaults: ["com.android.media.swcodec-defaults"], + installable: false, +} diff --git a/apex/testing/test_manifest.json b/apex/testing/test_manifest.json new file mode 100644 index 0000000000..9f81f9fe27 --- /dev/null +++ b/apex/testing/test_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.media", + "version": 2 +} diff --git a/apex/testing/test_manifest_codec.json b/apex/testing/test_manifest_codec.json new file mode 100644 index 0000000000..c956454784 --- /dev/null +++ b/apex/testing/test_manifest_codec.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.media.swcodec", + "version": 2 +} -- GitLab From 87e5fa6cb4bed3d9a66102fba4417d9d9d1217cb Mon Sep 17 00:00:00 2001 From: Ivan Lozano Date: Fri, 15 Feb 2019 17:07:29 +0000 Subject: [PATCH 0977/1530] Revert "Add syscalls required by UBSan." This reverts commit ddd87af881ef31976af3d051503fcb00040f4f9a. Reason for revert: Turning down diagnostics mode in codecs. Bug: 111498295 Change-Id: Ida7302ec70f31b79931e2a5df424a5ab4e3d5d62 --- services/mediacodec/seccomp_policy/mediacodec-arm.policy | 4 ---- services/mediacodec/seccomp_policy/mediacodec-x86.policy | 4 ---- 2 files changed, 8 deletions(-) diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy index edf4dabc4d..6ec8895787 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy @@ -55,8 +55,4 @@ ugetrlimit: 1 getdents64: 1 getrandom: 1 -# Used by UBSan diagnostic messages -readlink: 1 -open: 1 - @include /system/etc/seccomp_policy/crash_dump.arm.policy diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index 6e6b276196..bbbe552342 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -55,8 +55,4 @@ sched_yield: 1 getpid: 1 gettid: 1 -# Used by UBSan diagnostic messages -readlink: 1 -open: 1 - @include /system/etc/seccomp_policy/crash_dump.x86.policy -- GitLab From bf6b0ecd91172d6cd6cdd7c44f9ceecab4c29f27 Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 12 Feb 2019 12:30:12 -0800 Subject: [PATCH 0978/1530] Refactor haptic constant. Use the constant from IExternalVibratorService. Bug: 124318236 Test: play audio-haptic coupled file Change-Id: Ice2ac13d0f6a6a749bb6243230524336144fa5f4 --- media/libaudioclient/Android.bp | 1 + .../libaudioclient/include/media/AudioMixer.h | 27 ++++++++++--------- media/libaudioprocessing/Android.bp | 5 ++++ media/libaudioprocessing/tests/Android.bp | 1 + services/audioflinger/AudioFlinger.cpp | 2 +- services/audioflinger/FastMixerState.h | 2 +- services/audioflinger/PlaybackTracks.h | 3 ++- services/audioflinger/Threads.cpp | 2 +- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index bd10d67491..b111b78035 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -74,6 +74,7 @@ cc_library_shared { "libnblog", "libprocessgroup", "libutils", + "libvibrator", ], export_shared_lib_headers: ["libbinder"], diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index fbbbd11135..41b425f6ee 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -103,20 +104,21 @@ public: // parameter 'value' is a pointer to the new playback rate. }; - enum { // Haptic intensity, should keep consistent with VibratorService - HAPTIC_SCALE_VERY_LOW = -2, - HAPTIC_SCALE_LOW = -1, - HAPTIC_SCALE_NONE = 0, - HAPTIC_SCALE_HIGH = 1, - HAPTIC_SCALE_VERY_HIGH = 2, - }; - typedef int32_t haptic_intensity_t; - static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2 / 3; - static constexpr float HAPTIC_SCALE_LOW_RATIO = 3 / 4; - static const CONSTEXPR float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; + typedef enum { // Haptic intensity, should keep consistent with VibratorService + HAPTIC_SCALE_MUTE = os::IExternalVibratorService::SCALE_MUTE, + HAPTIC_SCALE_VERY_LOW = os::IExternalVibratorService::SCALE_VERY_LOW, + HAPTIC_SCALE_LOW = os::IExternalVibratorService::SCALE_LOW, + HAPTIC_SCALE_NONE = os::IExternalVibratorService::SCALE_NONE, + HAPTIC_SCALE_HIGH = os::IExternalVibratorService::SCALE_HIGH, + HAPTIC_SCALE_VERY_HIGH = os::IExternalVibratorService::SCALE_VERY_HIGH, + } haptic_intensity_t; + static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; + static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; + static const constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) { switch (hapticIntensity) { + case HAPTIC_SCALE_MUTE: case HAPTIC_SCALE_VERY_LOW: case HAPTIC_SCALE_LOW: case HAPTIC_SCALE_NONE: @@ -428,8 +430,9 @@ private: case HAPTIC_SCALE_NONE: case HAPTIC_SCALE_HIGH: case HAPTIC_SCALE_VERY_HIGH: - default: return 1.0f; + default: + return 0.0f; } } diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp index 817fb0b4d5..cb78063375 100644 --- a/media/libaudioprocessing/Android.bp +++ b/media/libaudioprocessing/Android.bp @@ -12,6 +12,11 @@ cc_defaults { "libnblog", "libsonic", "libutils", + "libvibrator", + ], + + header_libs: [ + "libbase_headers", ], cflags: [ diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp index 811c16b8d9..0c8e5bb066 100644 --- a/media/libaudioprocessing/tests/Android.bp +++ b/media/libaudioprocessing/tests/Android.bp @@ -10,6 +10,7 @@ cc_defaults { "libcutils", "liblog", "libutils", + "libvibrator", ], cflags: [ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 2d80bd894c..40332479ec 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -347,7 +347,7 @@ int AudioFlinger::onExternalVibrationStart(const sp& exte return ret; } } - return AudioMixer::HAPTIC_SCALE_NONE; + return AudioMixer::HAPTIC_SCALE_MUTE; } /* static */ diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index c27f2b7a9c..396c797284 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -49,7 +49,7 @@ struct FastTrack { audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not - AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; // intensity of + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE; // intensity of // haptic data }; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 94ea0421be..357370e42e 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -124,6 +124,7 @@ public: void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) { if (AudioMixer::isValidHapticIntensity(hapticIntensity)) { mHapticIntensity = hapticIntensity; + setHapticPlaybackEnabled(mHapticIntensity != AudioMixer::HAPTIC_SCALE_MUTE); } } sp getExternalVibration() const { return mExternalVibration; } @@ -208,7 +209,7 @@ protected: bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not // intensity to play haptic data - AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE; class AudioVibrationController : public os::BnExternalVibrationController { public: explicit AudioVibrationController(Track* track) : mTrack(track) {} diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6dd9cabb09..a8c4bd1eaa 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2372,6 +2372,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) const int intensity = AudioFlinger::onExternalVibrationStart( track->getExternalVibration()); mLock.lock(); + track->setHapticIntensity(static_cast(intensity)); // Haptic playback should be enabled by vibrator service. if (track->getHapticPlaybackEnabled()) { // Disable haptic playback of all active track to ensure only @@ -2380,7 +2381,6 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) t->setHapticPlaybackEnabled(false); } } - track->setHapticIntensity(intensity); } track->mResetDone = false; -- GitLab From 070897f840c7def1bf32eb082244aa1317fc1764 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 15 Feb 2019 10:38:54 -0800 Subject: [PATCH 0979/1530] CCodec: add more doc to PipelineWatcher Test: builds Change-Id: Id6534cd2b4f5c68ee568ada06a8c2bb73584c275 --- media/codec2/sfplugin/PipelineWatcher.cpp | 4 +- media/codec2/sfplugin/PipelineWatcher.h | 63 ++++++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/media/codec2/sfplugin/PipelineWatcher.cpp b/media/codec2/sfplugin/PipelineWatcher.cpp index cdcc41b73e..df81d49d4a 100644 --- a/media/codec2/sfplugin/PipelineWatcher.cpp +++ b/media/codec2/sfplugin/PipelineWatcher.cpp @@ -139,8 +139,8 @@ PipelineWatcher::Clock::duration PipelineWatcher::elapsed( std::chrono::duration_cast(elapsed).count()); durations.push_back(elapsed); } - nth_element(durations.begin(), durations.end(), durations.begin() + n, - std::greater()); + std::nth_element(durations.begin(), durations.end(), durations.begin() + n, + std::greater()); return durations[n]; } diff --git a/media/codec2/sfplugin/PipelineWatcher.h b/media/codec2/sfplugin/PipelineWatcher.h index 1c127e4073..1e2314758d 100644 --- a/media/codec2/sfplugin/PipelineWatcher.h +++ b/media/codec2/sfplugin/PipelineWatcher.h @@ -26,7 +26,8 @@ namespace android { /** - * PipelineWatcher watches the status of the work. + * PipelineWatcher watches the pipeline and infers the status of work items from + * events. */ class PipelineWatcher { public: @@ -39,21 +40,81 @@ public: mSmoothnessFactor(0) {} ~PipelineWatcher() = default; + /** + * \param value the new input delay value + * \return this object + */ PipelineWatcher &inputDelay(uint32_t value); + + /** + * \param value the new pipeline delay value + * \return this object + */ PipelineWatcher &pipelineDelay(uint32_t value); + + /** + * \param value the new output delay value + * \return this object + */ PipelineWatcher &outputDelay(uint32_t value); + + /** + * \param value the new smoothness factor value + * \return this object + */ PipelineWatcher &smoothnessFactor(uint32_t value); + /** + * Client queued a work item to the component. + * + * \param frameIndex input frame index of this work + * \param buffers input buffers of the queued work item + * \param queuedAt time when the client queued the buffer + */ void onWorkQueued( uint64_t frameIndex, std::vector> &&buffers, const Clock::time_point &queuedAt); + + /** + * The component released input buffers from a work item. + * + * \param frameIndex input frame index + * \param arrayIndex index of the buffer at the original |buffers| in + * onWorkQueued(). + * \return buffers[arrayIndex] + */ std::shared_ptr onInputBufferReleased( uint64_t frameIndex, size_t arrayIndex); + + /** + * The component finished processing a work item. + * + * \param frameIndex input frame index + */ void onWorkDone(uint64_t frameIndex); + + /** + * Flush the pipeline. + */ void flush(); + /** + * \return true if pipeline does not need more work items to proceed + * smoothly, considering delays and smoothness factor; + * false otherwise. + */ bool pipelineFull() const; + + /** + * Return elapsed processing time of a work item, nth from the longest + * processing time to the shortest. + * + * \param now current timestamp + * \param n nth work item, from the longest processing time to the + * shortest. It's a 0-based index. + * \return elapsed processing time of nth work item. + */ Clock::duration elapsed(const Clock::time_point &now, size_t n) const; private: -- GitLab From 091aa6d37f8d3fb407f6a59b20135b6045a5495e Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Fri, 15 Feb 2019 11:46:00 -0800 Subject: [PATCH 0980/1530] Mark functions in MediaPlayerBase::AudioSink as pure virtual Bug: http://b/116873221 Mark the functions in this class that aren't defined as pure virtual. If not, Clang assumes that the TU which defines the first undefined function will contain the vtable for the class as well. Since there's no out-of-line definition of this funciton, no TU ends up with the vtable. This causes a problem with coverage builds, which are built with -O0, where calls don't get inlined, thereby requiring a definition of the vtable. For non -O0 builds, the vtable is not required since the virtual calls get inlined/optimized-out. Test: Build with and without coverage Change-Id: I4644dcd892c5f47031f1603b0f809c1da95314db --- .../include/MediaPlayerInterface.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h index 311995026e..0ad4d04e71 100644 --- a/media/libmediaplayerservice/include/MediaPlayerInterface.h +++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h @@ -151,13 +151,13 @@ public: virtual media::VolumeShaper::Status applyVolumeShaper( const sp& configuration, - const sp& operation); - virtual sp getVolumeShaperState(int id); + const sp& operation) = 0; + virtual sp getVolumeShaperState(int id) = 0; // AudioRouting - virtual status_t setOutputDevice(audio_port_handle_t deviceId); - virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId); - virtual status_t enableAudioDeviceCallback(bool enabled); + virtual status_t setOutputDevice(audio_port_handle_t deviceId) = 0; + virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId) = 0; + virtual status_t enableAudioDeviceCallback(bool enabled) = 0; }; MediaPlayerBase() {} -- GitLab From e7f4b46a572a0131de6d9d2a2ca81247dec056ff Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 12 Feb 2019 08:43:07 -0800 Subject: [PATCH 0981/1530] Camera: Use capture result metadata to override EXIF Use static and dynamic metadata to override Exif tags. Also added back a missing ATRACE_ASYNC_ENDs statement. Test: Camera CTS Test: ERROR_BUFFER of internal streams is propagated to app Test: ERROR_RESULT only results in EXIF not being overridden Bug: 124066183 Change-Id: Id2c69c6bee04ae724ff5f190b2dd96d0159700c9 --- .../api2/HeicCompositeStream.cpp | 92 +++- .../api2/HeicCompositeStream.h | 10 +- .../libcameraservice/utils/ExifUtils.cpp | 479 ++++++++++-------- .../camera/libcameraservice/utils/ExifUtils.h | 104 +--- 4 files changed, 362 insertions(+), 323 deletions(-) diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp index 8e9c39ec00..a61cdeeac9 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -136,6 +136,8 @@ status_t HeicCompositeStream::createInternalStreams(const std::vectorsetName(String8("Camera3-HeicComposite-AppSegmentStream")); mAppSegmentSurface = new Surface(producer); + mStaticInfo = device->info(); + res = device->createStream(mAppSegmentSurface, mAppSegmentMaxSize, 1, format, kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId, surfaceIds); if (res == OK) { @@ -606,9 +608,42 @@ void HeicCompositeStream::compilePendingInputLocked() { mFrameNumberMap.erase(it); } - // Heic composition doesn't depend on capture result, so no need to check - // mErrorFrameNumbers. Just remove them. - mErrorFrameNumbers.clear(); + while (!mCaptureResults.empty()) { + auto it = mCaptureResults.begin(); + // Negative timestamp indicates that something went wrong during the capture result + // collection process. + if (it->first >= 0) { + if (mPendingInputFrames[it->first].frameNumber == std::get<0>(it->second)) { + mPendingInputFrames[it->first].result = + std::make_unique(std::get<1>(it->second)); + } else { + ALOGE("%s: Capture result frameNumber/timestamp mapping changed between " + "shutter and capture result!", __FUNCTION__); + } + } + mCaptureResults.erase(it); + } + + // mErrorFrameNumbers stores frame number of dropped buffers. + auto it = mErrorFrameNumbers.begin(); + while (it != mErrorFrameNumbers.end()) { + bool frameFound = false; + for (auto &inputFrame : mPendingInputFrames) { + if (inputFrame.second.frameNumber == *it) { + inputFrame.second.error = true; + frameFound = true; + break; + } + } + + if (frameFound) { + it = mErrorFrameNumbers.erase(it); + } else { + ALOGW("%s: Not able to find failing input with frame number: %" PRId64, __FUNCTION__, + *it); + it++; + } + } // Distribute codec input buffers to be filled out from YUV output for (auto it = mPendingInputFrames.begin(); @@ -639,14 +674,14 @@ bool HeicCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*out*/) { bool newInputAvailable = false; for (const auto& it : mPendingInputFrames) { - bool appSegmentBufferReady = (it.second.appSegmentBuffer.data != nullptr) && - !it.second.appSegmentWritten; + bool appSegmentReady = (it.second.appSegmentBuffer.data != nullptr) && + !it.second.appSegmentWritten && it.second.result != nullptr; bool codecOutputReady = !it.second.codecOutputBuffers.empty(); bool codecInputReady = (it.second.yuvBuffer.data != nullptr) && (!it.second.codecInputBuffers.empty()); if ((!it.second.error) && (it.first < *currentTs) && - (appSegmentBufferReady || codecOutputReady || codecInputReady)) { + (appSegmentReady || codecOutputReady || codecInputReady)) { *currentTs = it.first; newInputAvailable = true; break; @@ -678,13 +713,13 @@ status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp, ATRACE_CALL(); status_t res = OK; - bool appSegmentBufferReady = inputFrame.appSegmentBuffer.data != nullptr && - !inputFrame.appSegmentWritten; + bool appSegmentReady = inputFrame.appSegmentBuffer.data != nullptr && + !inputFrame.appSegmentWritten && inputFrame.result != nullptr; bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0; bool codecInputReady = inputFrame.yuvBuffer.data != nullptr && !inputFrame.codecInputBuffers.empty(); - if (!appSegmentBufferReady && !codecOutputReady && !codecInputReady) { + if (!appSegmentReady && !codecOutputReady && !codecInputReady) { ALOGW("%s: No valid appSegmentBuffer/codec input/outputBuffer available!", __FUNCTION__); return OK; } @@ -710,7 +745,7 @@ status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp, } // Write JPEG APP segments data to the muxer. - if (appSegmentBufferReady && inputFrame.muxer != nullptr) { + if (appSegmentReady && inputFrame.muxer != nullptr) { res = processAppSegment(timestamp, inputFrame); if (res != OK) { ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__, @@ -829,10 +864,8 @@ status_t HeicCompositeStream::processAppSegment(nsecs_t timestamp, InputFrame &i ALOGE("%s: Failed to initialize ExifUtils object!", __FUNCTION__); return BAD_VALUE; } - //TODO: Use capture result metadata and static metadata to fill out the - //rest. - CameraMetadata dummyMeta; - exifRes = exifUtils->setFromMetadata(dummyMeta, mOutputWidth, mOutputHeight); + exifRes = exifUtils->setFromMetadata(*inputFrame.result, mStaticInfo, + mOutputWidth, mOutputHeight); if (!exifRes) { ALOGE("%s: Failed to set Exif tags using metadata and main image sizes", __FUNCTION__); return BAD_VALUE; @@ -1012,6 +1045,7 @@ status_t HeicCompositeStream::processCompletedInputFrame(nsecs_t timestamp, } inputFrame.anb = nullptr; + ATRACE_ASYNC_END("HEIC capture", inputFrame.frameNumber); return OK; } @@ -1497,6 +1531,36 @@ bool HeicCompositeStream::onStreamBufferError(const CaptureResultExtras& resultE return res; } +void HeicCompositeStream::onResultError(const CaptureResultExtras& resultExtras) { + // For result error, since the APPS_SEGMENT buffer already contains EXIF, + // simply skip using the capture result metadata to override EXIF. + Mutex::Autolock l(mMutex); + + int64_t timestamp = -1; + for (const auto& fn : mFrameNumberMap) { + if (fn.first == resultExtras.frameNumber) { + timestamp = fn.second; + break; + } + } + if (timestamp == -1) { + for (const auto& inputFrame : mPendingInputFrames) { + if (inputFrame.second.frameNumber == resultExtras.frameNumber) { + timestamp = inputFrame.first; + break; + } + } + } + + if (timestamp == -1) { + ALOGE("%s: Failed to find shutter timestamp for result error!", __FUNCTION__); + return; + } + + mCaptureResults.emplace(timestamp, std::make_tuple(resultExtras.frameNumber, CameraMetadata())); + mInputReadyCondition.signal(); +} + void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp &msg) { sp parent = mParent.promote(); if (parent == nullptr) return; diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h index 0a762566de..4cd9af0668 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.h +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h @@ -77,7 +77,7 @@ protected: bool threadLoop() override; bool onStreamBufferError(const CaptureResultExtras& resultExtras) override; - void onResultError(const CaptureResultExtras& /*resultExtras*/) override {} + void onResultError(const CaptureResultExtras& resultExtras) override; private: // @@ -145,12 +145,13 @@ private: int32_t orientation; int32_t quality; - CpuConsumer::LockedBuffer appSegmentBuffer; + CpuConsumer::LockedBuffer appSegmentBuffer; std::vector codecOutputBuffers; + std::unique_ptr result; // Fields that are only applicable to HEVC tiling. - CpuConsumer::LockedBuffer yuvBuffer; - std::vector codecInputBuffers; + CpuConsumer::LockedBuffer yuvBuffer; + std::vector codecInputBuffers; bool error; bool errorNotified; @@ -209,6 +210,7 @@ private: sp mAppSegmentSurface; bool mAppSegmentBufferAcquired; size_t mAppSegmentMaxSize; + CameraMetadata mStaticInfo; int mMainImageStreamId, mMainImageSurfaceId; sp mMainImageSurface; diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp index a4027cc4bc..4dea8b5d3b 100644 --- a/services/camera/libcameraservice/utils/ExifUtils.cpp +++ b/services/camera/libcameraservice/utils/ExifUtils.cpp @@ -58,79 +58,59 @@ public: // set all known fields from a metadata structure virtual bool setFromMetadata(const CameraMetadata& metadata, + const CameraMetadata& staticInfo, const size_t imageWidth, const size_t imageHeight); // sets the len aperture. // Returns false if memory allocation fails. - virtual bool setAperture(uint32_t numerator, uint32_t denominator); - - // sets the value of brightness. - // Returns false if memory allocation fails. - virtual bool setBrightness(int32_t numerator, int32_t denominator); + virtual bool setAperture(float aperture); // sets the color space. // Returns false if memory allocation fails. virtual bool setColorSpace(uint16_t color_space); - // sets the information to compressed data. - // Returns false if memory allocation fails. - virtual bool setComponentsConfiguration(const std::string& components_configuration); - - // sets the compression scheme used for the image data. - // Returns false if memory allocation fails. - virtual bool setCompression(uint16_t compression); - - // sets image contrast. - // Returns false if memory allocation fails. - virtual bool setContrast(uint16_t contrast); - // sets the date and time of image last modified. It takes local time. The // name of the tag is DateTime in IFD0. // Returns false if memory allocation fails. virtual bool setDateTime(const struct tm& t); - // sets the image description. - // Returns false if memory allocation fails. - virtual bool setDescription(const std::string& description); - // sets the digital zoom ratio. If the numerator is 0, it means digital zoom // was not used. // Returns false if memory allocation fails. - virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator); + virtual bool setDigitalZoomRatio( + uint32_t crop_width, uint32_t crop_height, + uint32_t sensor_width, uint32_t sensor_height); - // sets the exposure bias. + // Sets the exposure bias. // Returns false if memory allocation fails. - virtual bool setExposureBias(int32_t numerator, int32_t denominator); + virtual bool setExposureBias(int32_t ev, + uint32_t ev_step_numerator, uint32_t ev_step_denominator); // sets the exposure mode set when the image was shot. // Returns false if memory allocation fails. - virtual bool setExposureMode(uint16_t exposure_mode); - - // sets the program used by the camera to set exposure when the picture is - // taken. - // Returns false if memory allocation fails. - virtual bool setExposureProgram(uint16_t exposure_program); + virtual bool setExposureMode(uint8_t exposure_mode); // sets the exposure time, given in seconds. // Returns false if memory allocation fails. - virtual bool setExposureTime(uint32_t numerator, uint32_t denominator); + virtual bool setExposureTime(float exposure_time); // sets the status of flash. // Returns false if memory allocation fails. - virtual bool setFlash(uint16_t flash); + virtual bool setFlash(uint8_t flash_available, uint8_t flash_state, uint8_t ae_mode); // sets the F number. // Returns false if memory allocation fails. - virtual bool setFNumber(uint32_t numerator, uint32_t denominator); + virtual bool setFNumber(float f_number); // sets the focal length of lens used to take the image in millimeters. // Returns false if memory allocation fails. - virtual bool setFocalLength(uint32_t numerator, uint32_t denominator); + virtual bool setFocalLength(float focal_length); - // sets the degree of overall image gain adjustment. + // sets the focal length of lens for 35mm film used to take the image in millimeters. // Returns false if memory allocation fails. - virtual bool setGainControl(uint16_t gain_control); + virtual bool setFocalLengthIn35mmFilm(float focal_length, + float sensor_size_x, float sensor_size_y); // sets the altitude in meters. // Returns false if memory allocation fails. @@ -164,45 +144,21 @@ public: // Returns false if memory allocation fails. virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings); - // sets the kind of light source. - // Returns false if memory allocation fails. - virtual bool setLightSource(uint16_t light_source); - // sets the smallest F number of the lens. // Returns false if memory allocation fails. - virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator); - - // sets the metering mode. - // Returns false if memory allocation fails. - virtual bool setMeteringMode(uint16_t metering_mode); + virtual bool setMaxAperture(float aperture); // sets image orientation. // Returns false if memory allocation fails. virtual bool setOrientation(uint16_t orientation); - // sets the unit for measuring XResolution and YResolution. - // Returns false if memory allocation fails. - virtual bool setResolutionUnit(uint16_t resolution_unit); - - // sets image saturation. - // Returns false if memory allocation fails. - virtual bool setSaturation(uint16_t saturation); - - // sets the type of scene that was shot. - // Returns false if memory allocation fails. - virtual bool setSceneCaptureType(uint16_t type); - - // sets image sharpness. - // Returns false if memory allocation fails. - virtual bool setSharpness(uint16_t sharpness); - // sets the shutter speed. // Returns false if memory allocation fails. - virtual bool setShutterSpeed(int32_t numerator, int32_t denominator); + virtual bool setShutterSpeed(float exposure_time); // sets the distance to the subject, given in meters. // Returns false if memory allocation fails. - virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator); + virtual bool setSubjectDistance(float diopters); // sets the fractions of seconds for the tag. // Returns false if memory allocation fails. @@ -210,28 +166,7 @@ public: // sets the white balance mode set when the image was shot. // Returns false if memory allocation fails. - virtual bool setWhiteBalance(uint16_t white_balance); - - // sets the number of pixels per resolution unit in the image width. - // Returns false if memory allocation fails. - virtual bool setXResolution(uint32_t numerator, uint32_t denominator); - - // sets the position of chrominance components in relation to the luminance - // component. - // Returns false if memory allocation fails. - virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning); - - // sets the number of pixels per resolution unit in the image length. - // Returns false if memory allocation fails. - virtual bool setYResolution(uint32_t numerator, uint32_t denominator); - - // sets the manufacturer of camera. - // Returns false if memory allocation fails. - virtual bool setMake(const std::string& make); - - // sets the model number of camera. - // Returns false if memory allocation fails. - virtual bool setModel(const std::string& model); + virtual bool setWhiteBalance(uint8_t white_balance); // Generates APP1 segment. // Returns false if generating APP1 segment fails. @@ -280,6 +215,10 @@ public: virtual bool setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string& buffer, const std::string& msg); + float convertToApex(float val) { + return 2.0f * log2f(val); + } + // Destroys the buffer of APP1 segment if exists. virtual void destroyApp1(); @@ -291,6 +230,8 @@ public: // The length of |app1_buffer_|. unsigned int app1_length_; + // How precise the float-to-rational conversion for EXIF tags would be. + const static int kRationalPrecision = 10000; }; #define SET_SHORT(ifd, tag, value) \ @@ -373,13 +314,11 @@ bool ExifUtilsImpl::initialize(const unsigned char *app1Segment, size_t app1Segm return true; } -bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator); - return true; -} - -bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) { - SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator, denominator); +bool ExifUtilsImpl::setAperture(float aperture) { + float apexValue = convertToApex(aperture); + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, + static_cast(std::round(apexValue * kRationalPrecision)), + kRationalPrecision); return true; } @@ -388,23 +327,6 @@ bool ExifUtilsImpl::setColorSpace(uint16_t color_space) { return true; } -bool ExifUtilsImpl::setComponentsConfiguration( - const std::string& components_configuration) { - SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION, - EXIF_FORMAT_UNDEFINED, components_configuration); - return true; -} - -bool ExifUtilsImpl::setCompression(uint16_t compression) { - SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression); - return true; -} - -bool ExifUtilsImpl::setContrast(uint16_t contrast) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast); - return true; -} - bool ExifUtilsImpl::setDateTime(const struct tm& t) { // The length is 20 bytes including NULL for termination in Exif standard. char str[20]; @@ -421,53 +343,111 @@ bool ExifUtilsImpl::setDateTime(const struct tm& t) { return true; } -bool ExifUtilsImpl::setDescription(const std::string& description) { - SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII, description); - return true; -} - -bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator, denominator); - return true; -} +bool ExifUtilsImpl::setDigitalZoomRatio( + uint32_t crop_width, uint32_t crop_height, + uint32_t sensor_width, uint32_t sensor_height) { + float zoomRatioX = (crop_width == 0) ? 1.0 : 1.0 * sensor_width / crop_width; + float zoomRatioY = (crop_height == 0) ? 1.0 : 1.0 * sensor_height / crop_height; + float zoomRatio = std::max(zoomRatioX, zoomRatioY); + const static float noZoomThreshold = 1.02f; -bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) { - SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator, denominator); + if (zoomRatio <= noZoomThreshold) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, 0, 1); + } else { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, + static_cast(std::round(zoomRatio * kRationalPrecision)), + kRationalPrecision); + } return true; } -bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode); +bool ExifUtilsImpl::setExposureMode(uint8_t exposure_mode) { + uint16_t exposureMode = (exposure_mode == ANDROID_CONTROL_AE_MODE_OFF) ? 1 : 0; + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposureMode); return true; } -bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program); +bool ExifUtilsImpl::setExposureTime(float exposure_time) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, + static_cast(std::round(exposure_time * kRationalPrecision)), + kRationalPrecision); return true; } -bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator); - return true; -} +bool ExifUtilsImpl::setFlash(uint8_t flash_available, uint8_t flash_state, uint8_t ae_mode) { + // EXIF_TAG_FLASH bits layout per EXIF standard: + // Bit 0: 0 - did not fire + // 1 - fired + // Bit 1-2: status of return light + // Bit 3-4: 0 - unknown + // 1 - compulsory flash firing + // 2 - compulsory flash suppression + // 3 - auto mode + // Bit 5: 0 - flash function present + // 1 - no flash function + // Bit 6: 0 - no red-eye reduction mode or unknown + // 1 - red-eye reduction supported + uint16_t flash = 0x20; + + if (flash_available == ANDROID_FLASH_INFO_AVAILABLE_TRUE) { + flash = 0x00; + + if (flash_state == ANDROID_FLASH_STATE_FIRED) { + flash |= 0x1; + } + if (ae_mode == ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) { + flash |= 0x40; + } -bool ExifUtilsImpl::setFlash(uint16_t flash) { + uint16_t flashMode = 0; + switch (ae_mode) { + case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH: + case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: + flashMode = 3; // AUTO + break; + case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH: + case ANDROID_CONTROL_AE_MODE_ON_EXTERNAL_FLASH: + flashMode = 1; // ON + break; + case ANDROID_CONTROL_AE_MODE_OFF: + case ANDROID_CONTROL_AE_MODE_ON: + flashMode = 2; // OFF + break; + default: + flashMode = 0; // UNKNOWN + break; + } + flash |= (flashMode << 3); + } SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash); return true; } -bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator); +bool ExifUtilsImpl::setFNumber(float f_number) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, + static_cast(std::round(f_number * kRationalPrecision)), + kRationalPrecision); return true; } -bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator); +bool ExifUtilsImpl::setFocalLength(float focal_length) { + uint32_t numerator = static_cast(std::round(focal_length * kRationalPrecision)); + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, kRationalPrecision); return true; } -bool ExifUtilsImpl::setGainControl(uint16_t gain_control) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control); +bool ExifUtilsImpl::setFocalLengthIn35mmFilm( + float focal_length, float sensor_size_x, float sensor_size_y) { + static const float filmDiagonal = 43.27; // diagonal of 35mm film + static const float minSensorDiagonal = 0.01; + float sensorDiagonal = std::sqrt( + sensor_size_x * sensor_size_x + sensor_size_y * sensor_size_y); + sensorDiagonal = std::max(sensorDiagonal, minSensorDiagonal); + float focalLength35mmFilm = std::round(focal_length * filmDiagonal / sensorDiagonal); + focalLength35mmFilm = std::min(1.0f * 65535, focalLength35mmFilm); + + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM, + static_cast(focalLength35mmFilm)); return true; } @@ -614,18 +594,18 @@ bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) { return true; } -bool ExifUtilsImpl::setLightSource(uint16_t light_source) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source); +bool ExifUtilsImpl::setMaxAperture(float aperture) { + float maxAperture = convertToApex(aperture); + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, + static_cast(std::round(maxAperture * kRationalPrecision)), + kRationalPrecision); return true; } -bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator, denominator); - return true; -} - -bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode); +bool ExifUtilsImpl::setExposureBias(int32_t ev, + uint32_t ev_step_numerator, uint32_t ev_step_denominator) { + SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, + ev * ev_step_numerator, ev_step_denominator); return true; } @@ -658,33 +638,36 @@ bool ExifUtilsImpl::setOrientation(uint16_t orientation) { return true; } -bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit); - return true; -} - -bool ExifUtilsImpl::setSaturation(uint16_t saturation) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation); - return true; -} - -bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type); +bool ExifUtilsImpl::setShutterSpeed(float exposure_time) { + float shutterSpeed = -log2f(exposure_time); + SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, + static_cast(shutterSpeed * kRationalPrecision), kRationalPrecision); return true; } -bool ExifUtilsImpl::setSharpness(uint16_t sharpness) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness); - return true; -} - -bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) { - SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator, denominator); - return true; -} - -bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) { +bool ExifUtilsImpl::setSubjectDistance(float diopters) { + const static float kInfinityDiopters = 1.0e-6; + uint32_t numerator, denominator; + uint16_t distanceRange; + if (diopters > kInfinityDiopters) { + float focusDistance = 1.0f / diopters; + numerator = static_cast(std::round(focusDistance * kRationalPrecision)); + denominator = kRationalPrecision; + + if (focusDistance < 1.0f) { + distanceRange = 1; // Macro + } else if (focusDistance < 3.0f) { + distanceRange = 2; // Close + } else { + distanceRange = 3; // Distant + } + } else { + numerator = 0xFFFFFFFF; + denominator = 1; + distanceRange = 3; // Distant + } SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator, denominator); + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE_RANGE, distanceRange); return true; } @@ -695,23 +678,9 @@ bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) { return true; } -bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) { - SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance); - return true; -} - -bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator); - return true; -} - -bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) { - SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning); - return true; -} - -bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) { - SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator); +bool ExifUtilsImpl::setWhiteBalance(uint8_t white_balance) { + uint16_t whiteBalance = (white_balance == ANDROID_CONTROL_AWB_MODE_AUTO) ? 0 : 1; + SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, whiteBalance); return true; } @@ -748,16 +717,6 @@ bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) { return true; } -bool ExifUtilsImpl::setMake(const std::string& make) { - SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make); - return true; -} - -bool ExifUtilsImpl::setModel(const std::string& model) { - SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model); - return true; -} - void ExifUtilsImpl::reset() { destroyApp1(); if (exif_data_) { @@ -898,9 +857,8 @@ void ExifUtilsImpl::destroyApp1() { } bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, + const CameraMetadata& staticInfo, const size_t imageWidth, const size_t imageHeight) { - // How precise the float-to-rational conversion for EXIF tags would be. - constexpr int kRationalPrecision = 10000; if (!setImageWidth(imageWidth) || !setImageHeight(imageHeight)) { ALOGE("%s: setting image resolution failed.", __FUNCTION__); @@ -921,15 +879,37 @@ bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, if (entry.count) { focal_length = entry.data.f[0]; - if (!setFocalLength( - static_cast(focal_length * kRationalPrecision), kRationalPrecision)) { + if (!setFocalLength(focal_length)) { ALOGE("%s: setting focal length failed.", __FUNCTION__); return false; } + + camera_metadata_ro_entry sensorSizeEntry = + staticInfo.find(ANDROID_SENSOR_INFO_PHYSICAL_SIZE); + if (sensorSizeEntry.count == 2) { + if (!setFocalLengthIn35mmFilm( + focal_length, sensorSizeEntry.data.f[0], sensorSizeEntry.data.f[1])) { + ALOGE("%s: setting focal length in 35mm failed.", __FUNCTION__); + return false; + } + } } else { ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__); } + if (metadata.exists(ANDROID_SCALER_CROP_REGION) && + staticInfo.exists(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE)) { + entry = metadata.find(ANDROID_SCALER_CROP_REGION); + camera_metadata_ro_entry activeArrayEntry = + staticInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); + + if (!setDigitalZoomRatio(entry.data.i32[2], entry.data.i32[3], + activeArrayEntry.data.i32[2], activeArrayEntry.data.i32[3])) { + ALOGE("%s: setting digital zoom ratio failed.", __FUNCTION__); + return false; + } + } + if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) { entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES); if (entry.count < 3) { @@ -973,6 +953,18 @@ bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, } } + if (staticInfo.exists(ANDROID_CONTROL_AE_COMPENSATION_STEP) && + metadata.exists(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION)) { + entry = metadata.find(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION); + camera_metadata_ro_entry stepEntry = + staticInfo.find(ANDROID_CONTROL_AE_COMPENSATION_STEP); + if (!setExposureBias(entry.data.i32[0], stepEntry.data.r[0].numerator, + stepEntry.data.r[0].denominator)) { + ALOGE("%s: setting exposure bias failed.", __FUNCTION__); + return false; + } + } + if (metadata.exists(ANDROID_JPEG_ORIENTATION)) { entry = metadata.find(ANDROID_JPEG_ORIENTATION); if (!setOrientation(entry.data.i32[0])) { @@ -983,50 +975,97 @@ bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) { entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME); - // int64_t of nanoseconds - if (!setExposureTime(entry.data.i64[0],1000000000u)) { + float exposure_time = 1.0f * entry.data.i64[0] / 1e9; + if (!setExposureTime(exposure_time)) { ALOGE("%s: setting exposure time failed.", __FUNCTION__); return false; } + + if (!setShutterSpeed(exposure_time)) { + ALOGE("%s: setting shutter speed failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_LENS_FOCUS_DISTANCE)) { + entry = metadata.find(ANDROID_LENS_FOCUS_DISTANCE); + if (!setSubjectDistance(entry.data.f[0])) { + ALOGE("%s: setting subject distance failed.", __FUNCTION__); + return false; + } + } + + if (metadata.exists(ANDROID_SENSOR_SENSITIVITY)) { + entry = metadata.find(ANDROID_SENSOR_SENSITIVITY); + int32_t iso = entry.data.i32[0]; + camera_metadata_ro_entry postRawSensEntry = + metadata.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST); + if (postRawSensEntry.count > 0) { + iso = iso * postRawSensEntry.data.i32[0] / 100; + } + + if (!setIsoSpeedRating(static_cast(iso))) { + ALOGE("%s: setting iso rating failed.", __FUNCTION__); + return false; + } } if (metadata.exists(ANDROID_LENS_APERTURE)) { - const int kAperturePrecision = 10000; entry = metadata.find(ANDROID_LENS_APERTURE); - if (!setFNumber(entry.data.f[0] * kAperturePrecision, kAperturePrecision)) { + if (!setFNumber(entry.data.f[0])) { ALOGE("%s: setting F number failed.", __FUNCTION__); return false; } + if (!setAperture(entry.data.f[0])) { + ALOGE("%s: setting aperture failed.", __FUNCTION__); + return false; + } } - if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) { - entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE); - if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) { - const uint32_t kNoFlashFunction = 0x20; - if (!setFlash(kNoFlashFunction)) { - ALOGE("%s: setting flash failed.", __FUNCTION__); - return false; - } - } else { - ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]); + static const uint16_t kSRGBColorSpace = 1; + if (!setColorSpace(kSRGBColorSpace)) { + ALOGE("%s: setting color space failed.", __FUNCTION__); + return false; + } + + if (staticInfo.exists(ANDROID_LENS_INFO_AVAILABLE_APERTURES)) { + entry = staticInfo.find(ANDROID_LENS_INFO_AVAILABLE_APERTURES); + if (!setMaxAperture(entry.data.f[0])) { + ALOGE("%s: setting max aperture failed.", __FUNCTION__); + return false; + } + } + + if (staticInfo.exists(ANDROID_FLASH_INFO_AVAILABLE)) { + entry = staticInfo.find(ANDROID_FLASH_INFO_AVAILABLE); + camera_metadata_ro_entry flashStateEntry = metadata.find(ANDROID_FLASH_STATE); + camera_metadata_ro_entry aeModeEntry = metadata.find(ANDROID_CONTROL_AE_MODE); + uint8_t flashState = flashStateEntry.count > 0 ? + flashStateEntry.data.u8[0] : ANDROID_FLASH_STATE_UNAVAILABLE; + uint8_t aeMode = aeModeEntry.count > 0 ? + aeModeEntry.data.u8[0] : ANDROID_CONTROL_AE_MODE_OFF; + + if (!setFlash(entry.data.u8[0], flashState, aeMode)) { + ALOGE("%s: setting flash failed.", __FUNCTION__); return false; } } if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) { entry = metadata.find(ANDROID_CONTROL_AWB_MODE); - if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) { - const uint16_t kAutoWhiteBalance = 0; - if (!setWhiteBalance(kAutoWhiteBalance)) { - ALOGE("%s: setting white balance failed.", __FUNCTION__); - return false; - } - } else { - ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]); + if (!setWhiteBalance(entry.data.u8[0])) { + ALOGE("%s: setting white balance failed.", __FUNCTION__); return false; } } + if (metadata.exists(ANDROID_CONTROL_AE_MODE)) { + entry = metadata.find(ANDROID_CONTROL_AE_MODE); + if (!setExposureMode(entry.data.u8[0])) { + ALOGE("%s: setting exposure mode failed.", __FUNCTION__); + return false; + } + } if (time_available) { char str[4]; if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) { diff --git a/services/camera/libcameraservice/utils/ExifUtils.h b/services/camera/libcameraservice/utils/ExifUtils.h index 8ccdd8fdca..c78bab9961 100644 --- a/services/camera/libcameraservice/utils/ExifUtils.h +++ b/services/camera/libcameraservice/utils/ExifUtils.h @@ -52,78 +52,57 @@ public: // Set all known fields from a metadata structure virtual bool setFromMetadata(const CameraMetadata& metadata, + const CameraMetadata& staticInfo, const size_t imageWidth, const size_t imageHeight) = 0; // Sets the len aperture. // Returns false if memory allocation fails. - virtual bool setAperture(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setAperture(float aperture) = 0; - // Sets the value of brightness. - // Returns false if memory allocation fails. - virtual bool setBrightness(int32_t numerator, int32_t denominator) = 0; - - // Sets the color space. + // sets the color space. // Returns false if memory allocation fails. virtual bool setColorSpace(uint16_t color_space) = 0; - // Sets the information to compressed data. - // Returns false if memory allocation fails. - virtual bool setComponentsConfiguration(const std::string& components_configuration) = 0; - - // Sets the compression scheme used for the image data. - // Returns false if memory allocation fails. - virtual bool setCompression(uint16_t compression) = 0; - - // Sets image contrast. - // Returns false if memory allocation fails. - virtual bool setContrast(uint16_t contrast) = 0; - // Sets the date and time of image last modified. It takes local time. The // name of the tag is DateTime in IFD0. // Returns false if memory allocation fails. virtual bool setDateTime(const struct tm& t) = 0; - // Sets the image description. - // Returns false if memory allocation fails. - virtual bool setDescription(const std::string& description) = 0; - // Sets the digital zoom ratio. If the numerator is 0, it means digital zoom // was not used. // Returns false if memory allocation fails. - virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setDigitalZoomRatio(uint32_t crop_width, uint32_t crop_height, + uint32_t sensor_width, uint32_t sensor_height) = 0; // Sets the exposure bias. // Returns false if memory allocation fails. - virtual bool setExposureBias(int32_t numerator, int32_t denominator) = 0; + virtual bool setExposureBias(int32_t ev, + uint32_t ev_step_numerator, uint32_t ev_step_denominator) = 0; // Sets the exposure mode set when the image was shot. // Returns false if memory allocation fails. - virtual bool setExposureMode(uint16_t exposure_mode) = 0; - - // Sets the program used by the camera to set exposure when the picture is - // taken. - // Returns false if memory allocation fails. - virtual bool setExposureProgram(uint16_t exposure_program) = 0; + virtual bool setExposureMode(uint8_t exposure_mode) = 0; // Sets the exposure time, given in seconds. // Returns false if memory allocation fails. - virtual bool setExposureTime(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setExposureTime(float exposure_time) = 0; // Sets the status of flash. // Returns false if memory allocation fails. - virtual bool setFlash(uint16_t flash) = 0; + virtual bool setFlash(uint8_t flash_available, uint8_t flash_state, uint8_t ae_mode) = 0; // Sets the F number. // Returns false if memory allocation fails. - virtual bool setFNumber(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setFNumber(float f_number) = 0; // Sets the focal length of lens used to take the image in millimeters. // Returns false if memory allocation fails. - virtual bool setFocalLength(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setFocalLength(float focal_length) = 0; - // Sets the degree of overall image gain adjustment. + // Sets the focal length of lens for 35mm film used to take the image in millimeters. // Returns false if memory allocation fails. - virtual bool setGainControl(uint16_t gain_control) = 0; + virtual bool setFocalLengthIn35mmFilm(float focal_length, + float sensor_size_x, float sensor_size_y) = 0; // Sets the altitude in meters. // Returns false if memory allocation fails. @@ -157,45 +136,21 @@ public: // Returns false if memory allocation fails. virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings) = 0; - // Sets the kind of light source. - // Returns false if memory allocation fails. - virtual bool setLightSource(uint16_t light_source) = 0; - // Sets the smallest F number of the lens. // Returns false if memory allocation fails. - virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator) = 0; - - // Sets the metering mode. - // Returns false if memory allocation fails. - virtual bool setMeteringMode(uint16_t metering_mode) = 0; + virtual bool setMaxAperture(float aperture) = 0; // Sets image orientation. // Returns false if memory allocation fails. virtual bool setOrientation(uint16_t orientation) = 0; - // Sets the unit for measuring XResolution and YResolution. - // Returns false if memory allocation fails. - virtual bool setResolutionUnit(uint16_t resolution_unit) = 0; - - // Sets image saturation. - // Returns false if memory allocation fails. - virtual bool setSaturation(uint16_t saturation) = 0; - - // Sets the type of scene that was shot. - // Returns false if memory allocation fails. - virtual bool setSceneCaptureType(uint16_t type) = 0; - - // Sets image sharpness. - // Returns false if memory allocation fails. - virtual bool setSharpness(uint16_t sharpness) = 0; - // Sets the shutter speed. // Returns false if memory allocation fails. - virtual bool setShutterSpeed(int32_t numerator, int32_t denominator) = 0; + virtual bool setShutterSpeed(float exposure_time) = 0; // Sets the distance to the subject, given in meters. // Returns false if memory allocation fails. - virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator) = 0; + virtual bool setSubjectDistance(float diopters) = 0; // Sets the fractions of seconds for the tag. // Returns false if memory allocation fails. @@ -203,28 +158,7 @@ public: // Sets the white balance mode set when the image was shot. // Returns false if memory allocation fails. - virtual bool setWhiteBalance(uint16_t white_balance) = 0; - - // Sets the number of pixels per resolution unit in the image width. - // Returns false if memory allocation fails. - virtual bool setXResolution(uint32_t numerator, uint32_t denominator) = 0; - - // Sets the position of chrominance components in relation to the luminance - // component. - // Returns false if memory allocation fails. - virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning) = 0; - - // Sets the number of pixels per resolution unit in the image length. - // Returns false if memory allocation fails. - virtual bool setYResolution(uint32_t numerator, uint32_t denominator) = 0; - - // Sets the manufacturer of camera. - // Returns false if memory allocation fails. - virtual bool setMake(const std::string& make) = 0; - - // Sets the model number of camera. - // Returns false if memory allocation fails. - virtual bool setModel(const std::string& model) = 0; + virtual bool setWhiteBalance(uint8_t white_blanace) = 0; // Generates APP1 segment. // Returns false if generating APP1 segment fails. -- GitLab From 5f3fb6f3640d833be33ebe3b6b6135d0accc6f0f Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Thu, 31 Jan 2019 11:53:22 -0800 Subject: [PATCH 0982/1530] Codec2 C2BlockPool: Use C2_BLOCKING instead of C2_TIMED_OUT Currently C2_TIMED_OUT is used for WOULD_BLOCK cases inside BlockPool. Use C2_BLOCKING instead of C2_TIMED_OUT. Bug: 123711537 Bug: 123951795 Change-Id: I3c250b9e50c8aed7eb8bcc4a974ff6dd0b54083f --- .../components/base/SimpleC2Component.cpp | 6 +++--- media/codec2/core/include/C2Buffer.h | 3 +++ media/codec2/vndk/platform/C2BqBuffer.cpp | 20 +++++++++++-------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index b8baec80bf..b158f8f2b0 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -151,7 +151,7 @@ public: c2_status_t status; do { status = mBase->fetchLinearBlock(capacity, usage, block); - } while (status == C2_TIMED_OUT); + } while (status == C2_BLOCKING); return status; } @@ -162,7 +162,7 @@ public: c2_status_t status; do { status = mBase->fetchCircularBlock(capacity, usage, block); - } while (status == C2_TIMED_OUT); + } while (status == C2_BLOCKING); return status; } @@ -174,7 +174,7 @@ public: do { status = mBase->fetchGraphicBlock(width, height, format, usage, block); - } while (status == C2_TIMED_OUT); + } while (status == C2_BLOCKING); return status; } diff --git a/media/codec2/core/include/C2Buffer.h b/media/codec2/core/include/C2Buffer.h index 2997f6e3c9..c428122a1c 100644 --- a/media/codec2/core/include/C2Buffer.h +++ b/media/codec2/core/include/C2Buffer.h @@ -888,6 +888,7 @@ public: * \retval C2_OK the operation was successful * \retval C2_NO_MEMORY not enough memory to complete any required allocation * \retval C2_TIMED_OUT the operation timed out + * \retval C2_BLOCKING the operation is blocked * \retval C2_REFUSED no permission to complete any required allocation * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error) * \retval C2_OMITTED this pool does not support linear blocks @@ -916,6 +917,7 @@ public: * \retval C2_OK the operation was successful * \retval C2_NO_MEMORY not enough memory to complete any required allocation * \retval C2_TIMED_OUT the operation timed out + * \retval C2_BLOCKING the operation is blocked * \retval C2_REFUSED no permission to complete any required allocation * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error) * \retval C2_OMITTED this pool does not support circular blocks @@ -946,6 +948,7 @@ public: * \retval C2_OK the operation was successful * \retval C2_NO_MEMORY not enough memory to complete any required allocation * \retval C2_TIMED_OUT the operation timed out + * \retval C2_BLOCKING the operation is blocked * \retval C2_REFUSED no permission to complete any required allocation * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller * error) diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 7bf3d64c3c..41a5b3ff30 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -207,12 +207,16 @@ private: // dequeueBuffer returns flag. if (!transStatus.isOk() || status < android::OK) { ALOGD("cannot dequeue buffer %d", status); - if (transStatus.isOk() && status == android::INVALID_OPERATION) { - // Too many buffer dequeued. retrying after some time is required. - return C2_TIMED_OUT; - } else { - return C2_BAD_VALUE; + if (transStatus.isOk()) { + if (status == android::INVALID_OPERATION || + status == android::TIMED_OUT || + status == android::WOULD_BLOCK) { + // Dequeue buffer is blocked temporarily. Retrying is + // required. + return C2_BLOCKING; + } } + return C2_BAD_VALUE; } ALOGV("dequeued a buffer successfully"); native_handle_t* nh = nullptr; @@ -227,7 +231,7 @@ private: if (status == -ETIME) { // fence is not signalled yet. (void)mProducer->cancelBuffer(slot, fenceHandle).isOk(); - return C2_TIMED_OUT; + return C2_BLOCKING; } if (status != android::NO_ERROR) { ALOGD("buffer fence wait error %d", status); @@ -353,14 +357,14 @@ public: return C2_OK; } c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block); - if (status == C2_TIMED_OUT) { + if (status == C2_BLOCKING) { lock.unlock(); ::usleep(kMaxIgbpRetryDelayUs); continue; } return status; } - return C2_TIMED_OUT; + return C2_BLOCKING; } void setRenderCallback(const OnRenderCallback &renderCallback) { -- GitLab From 5c5c8a11e8efc9f08eba73f830f5205a8f5ad055 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 15 Feb 2019 17:36:00 -0800 Subject: [PATCH 0983/1530] audio: use proper app ops API to check capture audio permission Use AppOpsManager.checkOp() instead of AppOpsManager.noteOp() to verify capture audio permission when creating an AudioRecord client. This avoid false triggering of mic use UI indication if capture is not actually active. Test: verify main audio capture use cases Change-Id: I38c7429bf2c8cdeaba907c868b94e39b69626c55 --- media/utils/ServiceUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 1c54aec468..599c44685c 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -85,7 +85,7 @@ static bool checkRecordingInternal(const String16& opPackageName, pid_t pid, return false; } } else { - if (appOps.noteOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) { + if (appOps.checkOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) { ALOGE("Request denied by app op: %d", op); return false; } -- GitLab From 9a183af20f950c33af5e4e60b2370b92b747f976 Mon Sep 17 00:00:00 2001 From: Sapir Caduri Date: Thu, 14 Feb 2019 22:23:47 +0200 Subject: [PATCH 0984/1530] audio policy fix: allow cocurrent capture for VOICE_DOWNLINK, VOICE_CALL, VOICE_UPLINK. This update will allow features like Call Screen (go/callscreen), Tap to Caption (go/whatist2c) and Tulip (go/tulip) to work simultaneously. Bug: b/124457248 Test: Manual testing. See videos in folder: http://go/audio-fix-videos and README.txt Change-Id: Ic279dde6c99eca51b880bb340dfc3235d8680121 --- services/audiopolicy/service/AudioPolicyService.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index a39477dd30..76ac191503 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -383,6 +383,8 @@ void AudioPolicyService::updateUidStates_l() // OR The client is an accessibility service // AND is on TOP OR latest started // AND the source is VOICE_RECOGNITION or HOTWORD +// OR the source is one of: AUDIO_SOURCE_VOICE_DOWNLINK, AUDIO_SOURCE_VOICE_UPLINK, +// AUDIO_SOURCE_VOICE_CALL // OR Any other client // AND The assistant is not on TOP // AND is on TOP OR latest started @@ -463,6 +465,10 @@ void AudioPolicyService::updateUidStates_l() (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { forceIdle = false; } + } else if (source == AUDIO_SOURCE_VOICE_DOWNLINK || + source == AUDIO_SOURCE_VOICE_CALL || + (source == AUDIO_SOURCE_VOICE_UPLINK)) { + forceIdle = false; } else { if (!isAssistantOnTop && (isOnTop || isLatest) && (!isSensitiveActive || isLatestSensitive)) { -- GitLab From dfe650a5ebd5912621d31b3d8f22b5122a2a4fca Mon Sep 17 00:00:00 2001 From: Roma Kaul Date: Thu, 2 Aug 2018 17:48:51 +0530 Subject: [PATCH 0985/1530] codec2: add C2SoftHevcEnc Test: screenrecord --codec-name c2.android.hevc.encoder /sdcard/record.mp4 Test: cts-tradefed run cts -m CtsMediaTestCases \ -t android.media.cts.VideoEncoderTest Bug: 110456253 Change-Id: I014f0d2b9cb94f8e81df4c8ae3dc41a8550033e6 --- media/codec2/components/hevc/Android.bp | 15 +- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 802 ++++++++++++++++++ media/codec2/components/hevc/C2SoftHevcEnc.h | 156 ++++ media/codec2/vndk/C2Store.cpp | 1 + .../data/media_codecs_google_c2_video.xml | 9 + services/mediacodec/registrant/Android.bp | 1 + 6 files changed, 981 insertions(+), 3 deletions(-) create mode 100644 media/codec2/components/hevc/C2SoftHevcEnc.cpp create mode 100644 media/codec2/components/hevc/C2SoftHevcEnc.h diff --git a/media/codec2/components/hevc/Android.bp b/media/codec2/components/hevc/Android.bp index 2a045e11a3..369bd784a9 100644 --- a/media/codec2/components/hevc/Android.bp +++ b/media/codec2/components/hevc/Android.bp @@ -9,8 +9,17 @@ cc_library_shared { static_libs: ["libhevcdec"], - include_dirs: [ - "external/libhevc/decoder", - "external/libhevc/common", +} + +cc_library_shared { + name: "libcodec2_soft_hevcenc", + defaults: [ + "libcodec2_soft-defaults", + "libcodec2_soft_sanitize_signed-defaults", ], + + srcs: ["C2SoftHevcEnc.cpp"], + + static_libs: ["libhevcenc"], + } diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp new file mode 100644 index 0000000000..5e6f44f00b --- /dev/null +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -0,0 +1,802 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "C2SoftHevcEnc" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ihevc_typedefs.h" +#include "itt_video_api.h" +#include "ihevce_api.h" +#include "ihevce_plugin.h" +#include "C2SoftHevcEnc.h" + +namespace android { + +class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { + public: + explicit IntfImpl(const std::shared_ptr& helper) + : C2InterfaceHelper(helper) { + setDerivedInstance(this); + + addParameter( + DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + .withConstValue( + new C2StreamFormatConfig::input(0u, C2FormatVideo)) + .build()); + + addParameter( + DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + .withConstValue( + new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + .build()); + + addParameter( + DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) + .withConstValue(AllocSharedString( + MEDIA_MIMETYPE_VIDEO_RAW)) + .build()); + + addParameter( + DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) + .withConstValue(AllocSharedString( + MEDIA_MIMETYPE_VIDEO_HEVC)) + .build()); + + addParameter(DefineParam(mUsage, C2_NAME_INPUT_STREAM_USAGE_SETTING) + .withConstValue(new C2StreamUsageTuning::input( + 0u, (uint64_t)C2MemoryUsage::CPU_READ)) + .build()); + + addParameter( + DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING) + .withDefault(new C2VideoSizeStreamTuning::input(0u, 320, 240)) + .withFields({ + C2F(mSize, width).inRange(320, 1920, 2), + C2F(mSize, height).inRange(128, 1088, 2), + }) + .withSetter(SizeSetter) + .build()); + + addParameter( + DefineParam(mFrameRate, C2_NAME_STREAM_FRAME_RATE_SETTING) + .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) + .withFields({C2F(mFrameRate, value).greaterThan(0.)}) + .withSetter( + Setter::StrictValueWithNoDeps) + .build()); + + addParameter( + DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) + .withDefault(new C2BitrateTuning::output(0u, 64000)) + .withFields({C2F(mBitrate, value).inRange(4096, 12000000)}) + .withSetter(BitrateSetter) + .build()); + + addParameter( + DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) + .withDefault(new C2StreamProfileLevelInfo::output( + 0u, PROFILE_HEVC_MAIN, LEVEL_HEVC_MAIN_1)) + .withFields({ + C2F(mProfileLevel, profile) + .oneOf({C2Config::PROFILE_HEVC_MAIN, + C2Config::PROFILE_HEVC_MAIN_STILL}), + C2F(mProfileLevel, level) + .oneOf({LEVEL_HEVC_MAIN_1, LEVEL_HEVC_MAIN_2, + LEVEL_HEVC_MAIN_2_1, LEVEL_HEVC_MAIN_3, + LEVEL_HEVC_MAIN_3_1, LEVEL_HEVC_MAIN_4, + LEVEL_HEVC_MAIN_4_1, LEVEL_HEVC_MAIN_5, + LEVEL_HEVC_MAIN_5_1, LEVEL_HEVC_MAIN_5_2}), + }) + .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate) + .build()); + + addParameter( + DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME) + .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE)) + .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) }) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); + + addParameter( + DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL) + .withDefault( + new C2StreamSyncFrameIntervalTuning::output(0u, 1000000)) + .withFields({C2F(mSyncFramePeriod, value).any()}) + .withSetter( + Setter::StrictValueWithNoDeps) + .build()); + } + + static C2R BitrateSetter(bool mayBlock, + C2P& me) { + (void)mayBlock; + C2R res = C2R::Ok(); + if (me.v.value <= 4096) { + me.set().value = 4096; + } + return res; + } + + static C2R SizeSetter(bool mayBlock, + const C2P& oldMe, + C2P& me) { + (void)mayBlock; + C2R res = C2R::Ok(); + if (!me.F(me.v.width).supportsAtAll(me.v.width)) { + res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width))); + me.set().width = oldMe.v.width; + } + if (!me.F(me.v.height).supportsAtAll(me.v.height)) { + res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height))); + me.set().height = oldMe.v.height; + } + return res; + } + + static C2R ProfileLevelSetter( + bool mayBlock, + C2P &me, + const C2P &size, + const C2P &frameRate, + const C2P &bitrate) { + (void)mayBlock; + if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) { + me.set().profile = PROFILE_HEVC_MAIN; + } + + struct LevelLimits { + C2Config::level_t level; + uint64_t samplesPerSec; + uint64_t samples; + uint32_t bitrate; + }; + + constexpr LevelLimits kLimits[] = { + { LEVEL_HEVC_MAIN_1, 552960, 36864, 128000 }, + { LEVEL_HEVC_MAIN_2, 3686400, 122880, 1500000 }, + { LEVEL_HEVC_MAIN_2_1, 7372800, 245760, 3000000 }, + { LEVEL_HEVC_MAIN_3, 16588800, 552960, 6000000 }, + { LEVEL_HEVC_MAIN_3_1, 33177600, 983040, 10000000 }, + { LEVEL_HEVC_MAIN_4, 66846720, 2228224, 12000000 }, + { LEVEL_HEVC_MAIN_4_1, 133693440, 2228224, 20000000 }, + { LEVEL_HEVC_MAIN_5, 267386880, 8912896, 25000000 }, + { LEVEL_HEVC_MAIN_5_1, 534773760, 8912896, 40000000 }, + { LEVEL_HEVC_MAIN_5_2, 1069547520, 8912896, 60000000 }, + { LEVEL_HEVC_MAIN_6, 1069547520, 35651584, 60000000 }, + { LEVEL_HEVC_MAIN_6_1, 2139095040, 35651584, 120000000 }, + { LEVEL_HEVC_MAIN_6_2, 4278190080, 35651584, 240000000 }, + }; + + uint64_t samples = size.v.width * size.v.height; + uint64_t samplesPerSec = samples * frameRate.v.value; + + // Check if the supplied level meets the MB / bitrate requirements. If + // not, update the level with the lowest level meeting the requirements. + + bool found = false; + // By default needsUpdate = false in case the supplied level does meet + // the requirements. + bool needsUpdate = false; + for (const LevelLimits &limit : kLimits) { + if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec && + bitrate.v.value <= limit.bitrate) { + // This is the lowest level that meets the requirements, and if + // we haven't seen the supplied level yet, that means we don't + // need the update. + if (needsUpdate) { + ALOGD("Given level %x does not cover current configuration: " + "adjusting to %x", me.v.level, limit.level); + me.set().level = limit.level; + } + found = true; + break; + } + if (me.v.level == limit.level) { + // We break out of the loop when the lowest feasible level is + // found. The fact that we're here means that our level doesn't + // meet the requirement and needs to be updated. + needsUpdate = true; + } + } + if (!found) { + // We set to the highest supported level. + me.set().level = LEVEL_HEVC_MAIN_5_2; + } + return C2R::Ok(); + } + + UWORD32 getProfile_l() const { + switch (mProfileLevel->profile) { + case PROFILE_HEVC_MAIN: [[fallthrough]]; + case PROFILE_HEVC_MAIN_STILL: return 1; + default: + ALOGD("Unrecognized profile: %x", mProfileLevel->profile); + return 1; + } + } + + UWORD32 getLevel_l() const { + struct Level { + C2Config::level_t c2Level; + UWORD32 hevcLevel; + }; + constexpr Level levels[] = { + { LEVEL_HEVC_MAIN_1, 30 }, + { LEVEL_HEVC_MAIN_2, 60 }, + { LEVEL_HEVC_MAIN_2_1, 63 }, + { LEVEL_HEVC_MAIN_3, 90 }, + { LEVEL_HEVC_MAIN_3_1, 93 }, + { LEVEL_HEVC_MAIN_4, 120 }, + { LEVEL_HEVC_MAIN_4_1, 123 }, + { LEVEL_HEVC_MAIN_5, 150 }, + { LEVEL_HEVC_MAIN_5_1, 153 }, + { LEVEL_HEVC_MAIN_5_2, 156 }, + { LEVEL_HEVC_MAIN_6, 180 }, + { LEVEL_HEVC_MAIN_6_1, 183 }, + { LEVEL_HEVC_MAIN_6_2, 186 }, + }; + for (const Level &level : levels) { + if (mProfileLevel->level == level.c2Level) { + return level.hevcLevel; + } + } + ALOGD("Unrecognized level: %x", mProfileLevel->level); + return 156; + } + uint32_t getSyncFramePeriod_l() const { + if (mSyncFramePeriod->value < 0 || + mSyncFramePeriod->value == INT64_MAX) { + return 0; + } + double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value; + return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.); + } + + std::shared_ptr getSize_l() const { + return mSize; + } + std::shared_ptr getFrameRate_l() const { + return mFrameRate; + } + std::shared_ptr getBitrate_l() const { + return mBitrate; + } + std::shared_ptr getRequestSync_l() const { + return mRequestSync; + } + + private: + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; + std::shared_ptr mUsage; + std::shared_ptr mSize; + std::shared_ptr mFrameRate; + std::shared_ptr mRequestSync; + std::shared_ptr mBitrate; + std::shared_ptr mProfileLevel; + std::shared_ptr mSyncFramePeriod; +}; +constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; + +static size_t GetCPUCoreCount() { + long cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +C2SoftHevcEnc::C2SoftHevcEnc(const char* name, c2_node_id_t id, + const std::shared_ptr& intfImpl) + : SimpleC2Component( + std::make_shared>(name, id, intfImpl)), + mIntf(intfImpl), + mIvVideoColorFormat(IV_YUV_420P), + mHevcEncProfile(1), + mHevcEncLevel(30), + mStarted(false), + mSpsPpsHeaderReceived(false), + mSignalledEos(false), + mSignalledError(false), + mCodecCtx(nullptr) { + // If dump is enabled, then create an empty file + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); + CREATE_DUMP_FILE(mOutFile); + + gettimeofday(&mTimeStart, nullptr); + gettimeofday(&mTimeEnd, nullptr); +} + +C2SoftHevcEnc::~C2SoftHevcEnc() { + releaseEncoder(); +} + +c2_status_t C2SoftHevcEnc::onInit() { + return initEncoder(); +} + +c2_status_t C2SoftHevcEnc::onStop() { + if (!mStarted) { + return C2_OK; + } + return releaseEncoder(); +} + +void C2SoftHevcEnc::onReset() { + onStop(); + initEncoder(); +} + +void C2SoftHevcEnc::onRelease() { + onStop(); +} + +c2_status_t C2SoftHevcEnc::onFlush_sm() { + return C2_OK; +} + +static void fillEmptyWork(const std::unique_ptr& work) { + uint32_t flags = 0; + if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) { + flags |= C2FrameData::FLAG_END_OF_STREAM; + ALOGV("Signalling EOS"); + } + work->worklets.front()->output.flags = (C2FrameData::flags_t)flags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; +} + +c2_status_t C2SoftHevcEnc::initEncParams() { + mCodecCtx = nullptr; + mNumCores = MIN(GetCPUCoreCount(), CODEC_MAX_CORES); + memset(&mEncParams, 0, sizeof(ihevce_static_cfg_params_t)); + + // default configuration + IHEVCE_PLUGIN_STATUS_T err = ihevce_set_def_params(&mEncParams); + if (IHEVCE_EOK != err) { + ALOGE("HEVC default init failed : 0x%x", err); + return C2_CORRUPTED; + } + + // update configuration + mEncParams.s_src_prms.i4_width = mSize->width; + mEncParams.s_src_prms.i4_height = mSize->height; + mEncParams.s_src_prms.i4_frm_rate_denom = 1000; + mEncParams.s_src_prms.i4_frm_rate_num = mFrameRate->value * mEncParams.s_src_prms.i4_frm_rate_denom; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0] = + mBitrate->value; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0] = + mBitrate->value << 1; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel; + mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval; + mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIDRInterval; + mIvVideoColorFormat = IV_YUV_420P; + mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; + mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; + mEncParams.s_config_prms.i4_rate_control_mode = 2; + mEncParams.s_lap_prms.i4_rc_look_ahead_pics = 0; + + return C2_OK; +} + +c2_status_t C2SoftHevcEnc::releaseEncoder() { + mSpsPpsHeaderReceived = false; + mSignalledEos = false; + mSignalledError = false; + mStarted = false; + + if (mCodecCtx) { + IHEVCE_PLUGIN_STATUS_T err = ihevce_close(mCodecCtx); + if (IHEVCE_EOK != err) return C2_CORRUPTED; + mCodecCtx = nullptr; + } + return C2_OK; +} + +c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode, + const std::shared_ptr& pool) { + (void)drainMode; + (void)pool; + return C2_OK; +} +c2_status_t C2SoftHevcEnc::initEncoder() { + CHECK(!mCodecCtx); + { + IntfImpl::Lock lock = mIntf->lock(); + mSize = mIntf->getSize_l(); + mBitrate = mIntf->getBitrate_l(); + mFrameRate = mIntf->getFrameRate_l(); + mHevcEncProfile = mIntf->getProfile_l(); + mHevcEncLevel = mIntf->getLevel_l(); + mIDRInterval = mIntf->getSyncFramePeriod_l(); + } + + c2_status_t status = initEncParams(); + + if (C2_OK != status) { + ALOGE("Failed to initialize encoder params : 0x%x", status); + mSignalledError = true; + return status; + } + + IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK; + err = ihevce_init(&mEncParams, &mCodecCtx); + if (IHEVCE_EOK != err) { + ALOGE("HEVC encoder init failed : 0x%x", err); + return C2_CORRUPTED; + } + + mStarted = true; + return C2_OK; +} + +c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, + const C2GraphicView* const input, + uint64_t timestamp) { + ihevce_static_cfg_params_t* params = &mEncParams; + memset(ps_encode_ip, 0, sizeof(ihevce_inp_buf_t)); + + if (!input) { + return C2_OK; + } + + if (input->width() < mSize->width || + input->height() < mSize->height) { + /* Expect width height to be configured */ + ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", input->width(), + mSize->width, input->height(), mSize->height); + return C2_BAD_VALUE; + } + + const C2PlanarLayout& layout = input->layout(); + uint8_t* yPlane = + const_cast(input->data()[C2PlanarLayout::PLANE_Y]); + uint8_t* uPlane = + const_cast(input->data()[C2PlanarLayout::PLANE_U]); + uint8_t* vPlane = + const_cast(input->data()[C2PlanarLayout::PLANE_V]); + int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc; + int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc; + int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc; + + uint32_t width = mSize->width; + uint32_t height = mSize->height; + + // width and height are always even + // width and height are always even (as block size is 16x16) + CHECK_EQ((width & 1u), 0u); + CHECK_EQ((height & 1u), 0u); + + size_t yPlaneSize = width * height; + + switch (layout.type) { + case C2PlanarLayout::TYPE_RGB: + [[fallthrough]]; + case C2PlanarLayout::TYPE_RGBA: { + MemoryBlock conversionBuffer = + mConversionBuffers.fetch(yPlaneSize * 3 / 2); + mConversionBuffersInUse.emplace(conversionBuffer.data(), + conversionBuffer); + yPlane = conversionBuffer.data(); + uPlane = yPlane + yPlaneSize; + vPlane = uPlane + yPlaneSize / 4; + yStride = width; + uStride = vStride = yStride / 2; + ConvertRGBToPlanarYUV(yPlane, yStride, height, + conversionBuffer.size(), *input); + break; + } + case C2PlanarLayout::TYPE_YUV: { + if (!IsYUV420(*input)) { + ALOGE("input is not YUV420"); + return C2_BAD_VALUE; + } + + if (layout.planes[layout.PLANE_Y].colInc == 1 && + layout.planes[layout.PLANE_U].colInc == 1 && + layout.planes[layout.PLANE_V].colInc == 1 && + uStride == vStride && yStride == 2 * vStride) { + // I420 compatible - already set up above + break; + } + + // copy to I420 + yStride = width; + uStride = vStride = yStride / 2; + MemoryBlock conversionBuffer = + mConversionBuffers.fetch(yPlaneSize * 3 / 2); + mConversionBuffersInUse.emplace(conversionBuffer.data(), + conversionBuffer); + MediaImage2 img = + CreateYUV420PlanarMediaImage2(width, height, yStride, height); + status_t err = ImageCopy(conversionBuffer.data(), &img, *input); + if (err != OK) { + ALOGE("Buffer conversion failed: %d", err); + return C2_BAD_VALUE; + } + yPlane = conversionBuffer.data(); + uPlane = yPlane + yPlaneSize; + vPlane = uPlane + yPlaneSize / 4; + break; + } + + case C2PlanarLayout::TYPE_YUVA: + ALOGE("YUVA plane type is not supported"); + return C2_BAD_VALUE; + + default: + ALOGE("Unrecognized plane type: %d", layout.type); + return C2_BAD_VALUE; + } + + switch (mIvVideoColorFormat) { + case IV_YUV_420P: { + // input buffer is supposed to be const but Ittiam API wants bare + // pointer. + ps_encode_ip->apv_inp_planes[0] = yPlane; + ps_encode_ip->apv_inp_planes[1] = uPlane; + ps_encode_ip->apv_inp_planes[2] = vPlane; + + ps_encode_ip->ai4_inp_strd[0] = yStride; + ps_encode_ip->ai4_inp_strd[1] = uStride; + ps_encode_ip->ai4_inp_strd[2] = vStride; + + ps_encode_ip->ai4_inp_size[0] = yStride * height; + ps_encode_ip->ai4_inp_size[1] = uStride * height >> 1; + ps_encode_ip->ai4_inp_size[2] = vStride * height >> 1; + break; + } + + case IV_YUV_422ILE: { + // TODO + break; + } + + case IV_YUV_420SP_UV: + case IV_YUV_420SP_VU: + default: { + ps_encode_ip->apv_inp_planes[0] = yPlane; + ps_encode_ip->apv_inp_planes[1] = uPlane; + ps_encode_ip->apv_inp_planes[2] = nullptr; + + ps_encode_ip->ai4_inp_strd[0] = yStride; + ps_encode_ip->ai4_inp_strd[1] = uStride; + ps_encode_ip->ai4_inp_strd[2] = 0; + + ps_encode_ip->ai4_inp_size[0] = yStride * height; + ps_encode_ip->ai4_inp_size[1] = uStride * height >> 1; + ps_encode_ip->ai4_inp_size[2] = 0; + break; + } + } + + ps_encode_ip->i4_curr_bitrate = + params->s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0]; + ps_encode_ip->i4_curr_peak_bitrate = + params->s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0]; + ps_encode_ip->i4_curr_rate_factor = params->s_config_prms.i4_rate_factor; + ps_encode_ip->u8_pts = timestamp; + return C2_OK; +} + +void C2SoftHevcEnc::process(const std::unique_ptr& work, + const std::shared_ptr& pool) { + // Initialize output work + work->result = C2_OK; + work->workletsProcessed = 1u; + work->worklets.front()->output.flags = work->input.flags; + + if (mSignalledError || mSignalledEos) { + work->result = C2_BAD_VALUE; + ALOGD("Signalled Error / Signalled Eos"); + return; + } + c2_status_t status = C2_OK; + + // Initialize encoder if not already initialized + if (!mStarted) { + status = initEncoder(); + if (C2_OK != status) { + ALOGE("Failed to initialize encoder : 0x%x", status); + mSignalledError = true; + work->result = status; + return; + } + } + + std::shared_ptr view; + std::shared_ptr inputBuffer = nullptr; + bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + if (!work->input.buffers.empty()) { + inputBuffer = work->input.buffers[0]; + view = std::make_shared( + inputBuffer->data().graphicBlocks().front().map().get()); + if (view->error() != C2_OK) { + ALOGE("graphic view map err = %d", view->error()); + mSignalledError = true; + return; + } + } + + IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK; + + fillEmptyWork(work); + if (!mSpsPpsHeaderReceived) { + ihevce_out_buf_t s_header_op{}; + err = ihevce_encode_header(mCodecCtx, &s_header_op); + if (err == IHEVCE_EOK && s_header_op.i4_bytes_generated) { + std::unique_ptr csd = + C2StreamCsdInfo::output::AllocUnique( + s_header_op.i4_bytes_generated, 0u); + if (!csd) { + ALOGE("CSD allocation failed"); + mSignalledError = true; + work->result = C2_NO_MEMORY; + return; + } + memcpy(csd->m.value, s_header_op.pu1_output_buf, + s_header_op.i4_bytes_generated); + DUMP_TO_FILE(mOutFile, csd->m.value, csd->flexCount()); + work->worklets.front()->output.configUpdate.push_back( + std::move(csd)); + mSpsPpsHeaderReceived = true; + } + if (!inputBuffer) { + return; + } + } + ihevce_inp_buf_t s_encode_ip{}; + ihevce_out_buf_t s_encode_op{}; + uint64_t timestamp = work->input.ordinal.timestamp.peekull(); + + status = setEncodeArgs(&s_encode_ip, view.get(), timestamp); + if (C2_OK != status) { + mSignalledError = true; + ALOGE("setEncodeArgs failed : 0x%x", status); + work->result = status; + return; + } + + uint64_t timeDelay = 0; + uint64_t timeTaken = 0; + GETTIME(&mTimeStart, nullptr); + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + ihevce_inp_buf_t* ps_encode_ip = (inputBuffer) ? &s_encode_ip : nullptr; + + err = ihevce_encode(mCodecCtx, ps_encode_ip, &s_encode_op); + if (IHEVCE_EOK != err) { + ALOGE("Encode Frame failed : 0x%x", err); + mSignalledError = true; + work->result = C2_CORRUPTED; + return; + } + + GETTIME(&mTimeEnd, nullptr); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", (int)timeTaken, + (int)timeDelay, s_encode_op.i4_bytes_generated); + + if (s_encode_op.i4_bytes_generated) { + std::shared_ptr block; + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + status = pool->fetchLinearBlock(s_encode_op.i4_bytes_generated, usage, &block); + if (C2_OK != status) { + ALOGE("fetchLinearBlock for Output failed with status 0x%x", status); + work->result = C2_NO_MEMORY; + mSignalledError = true; + return; + } + C2WriteView wView = block->map().get(); + if (C2_OK != wView.error()) { + ALOGE("write view map failed with status 0x%x", wView.error()); + work->result = wView.error(); + mSignalledError = true; + return; + } + memcpy(wView.data(), s_encode_op.pu1_output_buf, + s_encode_op.i4_bytes_generated); + + std::shared_ptr buffer = + createLinearBuffer(block, 0, s_encode_op.i4_bytes_generated); + + DUMP_TO_FILE(mOutFile, s_encode_op.pu1_output_buf, + s_encode_op.i4_bytes_generated); + + work->worklets.front()->output.ordinal.timestamp = s_encode_op.u8_pts; + if (s_encode_op.i4_is_key_frame) { + ALOGV("IDR frame produced"); + buffer->setInfo( + std::make_shared( + 0u /* stream id */, C2PictureTypeKeyFrame)); + } + work->worklets.front()->output.buffers.push_back(buffer); + } + if (eos) { + mSignalledEos = true; + } +} + +class C2SoftHevcEncFactory : public C2ComponentFactory { + public: + C2SoftHevcEncFactory() + : mHelper(std::static_pointer_cast( + GetCodec2PlatformComponentStore()->getParamReflector())) {} + + virtual c2_status_t createComponent( + c2_node_id_t id, std::shared_ptr* const component, + std::function deleter) override { + *component = std::shared_ptr( + new C2SoftHevcEnc( + COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual c2_status_t createInterface( + c2_node_id_t id, std::shared_ptr* const interface, + std::function deleter) override { + *interface = std::shared_ptr( + new SimpleInterface( + COMPONENT_NAME, id, + std::make_shared(mHelper)), + deleter); + return C2_OK; + } + + virtual ~C2SoftHevcEncFactory() override = default; + + private: + std::shared_ptr mHelper; +}; + +} // namespace android + +extern "C" ::C2ComponentFactory* CreateCodec2Factory() { + ALOGV("in %s", __func__); + return new ::android::C2SoftHevcEncFactory(); +} + +extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { + ALOGV("in %s", __func__); + delete factory; +} diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h new file mode 100644 index 0000000000..c22fea2b69 --- /dev/null +++ b/media/codec2/components/hevc/C2SoftHevcEnc.h @@ -0,0 +1,156 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_C2_SOFT_HEVC_ENC_H_ +#define ANDROID_C2_SOFT_HEVC_ENC_H_ + +#include +#include +#include +#include + +#include "ihevc_typedefs.h" + +namespace android { +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ + ((end).tv_usec - (start).tv_usec); + +#define CODEC_MAX_CORES 4 + +struct C2SoftHevcEnc : public SimpleC2Component { + class IntfImpl; + + C2SoftHevcEnc(const char* name, c2_node_id_t id, + const std::shared_ptr& intfImpl); + + // From SimpleC2Component + c2_status_t onInit() override; + c2_status_t onStop() override; + void onReset() override; + void onRelease() override; + c2_status_t onFlush_sm() override; + void process(const std::unique_ptr& work, + const std::shared_ptr& pool) override; + c2_status_t drain(uint32_t drainMode, + const std::shared_ptr& pool) override; + + protected: + virtual ~C2SoftHevcEnc(); + + private: + std::shared_ptr mIntf; + ihevce_static_cfg_params_t mEncParams; + size_t mNumCores; + UWORD32 mIDRInterval; + IV_COLOR_FORMAT_T mIvVideoColorFormat; + UWORD32 mHevcEncProfile; + UWORD32 mHevcEncLevel; + bool mStarted; + bool mSpsPpsHeaderReceived; + bool mSignalledEos; + bool mSignalledError; + void* mCodecCtx; + MemoryBlockPool mConversionBuffers; + std::map mConversionBuffersInUse; + // configurations used by component in process + // (TODO: keep this in intf but make them internal only) + std::shared_ptr mSize; + std::shared_ptr mFrameRate; + std::shared_ptr mBitrate; + +#ifdef FILE_DUMP_ENABLE + char mInFile[200]; + char mOutFile[200]; +#endif /* FILE_DUMP_ENABLE */ + + // profile + struct timeval mTimeStart; + struct timeval mTimeEnd; + + c2_status_t initEncParams(); + c2_status_t initEncoder(); + c2_status_t releaseEncoder(); + c2_status_t setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, + const C2GraphicView* const input, + uint64_t timestamp); + C2_DO_NOT_COPY(C2SoftHevcEnc); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/data/local/tmp/hevc" +#define INPUT_DUMP_EXT "yuv" +#define OUTPUT_DUMP_PATH "/data/local/tmp/hevc" +#define OUTPUT_DUMP_EXT "h265" +#define GENERATE_FILE_NAMES() \ +{ \ + GETTIME(&mTimeStart, NULL); \ + strcpy(mInFile, ""); \ + ALOGD("GENERATE_FILE_NAMES"); \ + sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \ + mTimeStart.tv_usec, INPUT_DUMP_EXT); \ + strcpy(mOutFile, ""); \ + sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH, \ + mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT); \ +} + +#define CREATE_DUMP_FILE(m_filename) \ +{ \ + FILE* fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + ALOGD("Opened file %s", m_filename); \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ +} +#define DUMP_TO_FILE(m_filename, m_buf, m_size) \ +{ \ + FILE* fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL) { \ + int i; \ + ALOGD("Dump to file!"); \ + i = fwrite(m_buf, 1, m_size, fp); \ + if (i != (int)m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + fclose(fp); \ + } else { \ + ALOGD("Could not write to file %s", m_filename); \ + if (fp != NULL) fclose(fp); \ + } \ +} +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // C2_SOFT_HEVC_ENC_H__ diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index f07d9b0458..32588a5ed1 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -856,6 +856,7 @@ C2PlatformComponentStore::C2PlatformComponentStore() emplace("libcodec2_soft_h263dec.so"); emplace("libcodec2_soft_h263enc.so"); emplace("libcodec2_soft_hevcdec.so"); + emplace("libcodec2_soft_hevcenc.so"); emplace("libcodec2_soft_mp3dec.so"); emplace("libcodec2_soft_mpeg2dec.so"); emplace("libcodec2_soft_mpeg4dec.so"); diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml index 5c2d96d2ae..e20174fa41 100644 --- a/media/libstagefright/data/media_codecs_google_c2_video.xml +++ b/media/libstagefright/data/media_codecs_google_c2_video.xml @@ -107,6 +107,15 @@ + + + + + + + + + diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 80d3630937..1470de29f4 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -28,6 +28,7 @@ cc_library_shared { "libcodec2_soft_amrwbdec", "libcodec2_soft_amrwbenc", "libcodec2_soft_hevcdec", + "libcodec2_soft_hevcenc", "libcodec2_soft_g711alawdec", "libcodec2_soft_g711mlawdec", "libcodec2_soft_mpeg2dec", -- GitLab From 9751d6433cb0b3c940a051bc68663cd0ea83f8a4 Mon Sep 17 00:00:00 2001 From: "eunyoung.moon" Date: Thu, 14 Feb 2019 13:55:35 +0900 Subject: [PATCH 0986/1530] Modified that logs do not include URLs. When try to play streaming, URLs are logged. URLs can be contained sensitive information. So, modified that logs do not inlcude URLs. Bug: 123669012 Test: play HLS/RTSP streaming Change-Id: I379e66b7be7588b09bbf6328c964666eb546ccf2 Signed-off-by: Eunyoung Moon --- media/libstagefright/httplive/LiveSession.cpp | 8 ++++---- media/libstagefright/httplive/M3UParser.cpp | 3 +-- media/libstagefright/httplive/PlaylistFetcher.cpp | 6 +++--- media/libstagefright/rtsp/ARTSPConnection.cpp | 2 +- media/libstagefright/rtsp/ASessionDescription.cpp | 2 +- media/libstagefright/rtsp/MyHandler.h | 5 ++--- 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 2ecfa43752..5e7f90a510 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1234,7 +1234,7 @@ bool LiveSession::resumeFetcher( const AString &uri, uint32_t streamMask, int64_t timeUs, bool newUri) { ssize_t index = mFetcherInfos.indexOfKey(uri); if (index < 0) { - ALOGE("did not find fetcher for uri: %s", uri.c_str()); + ALOGE("did not find fetcher for uri: %s", uriDebugString(uri).c_str()); return false; } @@ -2005,7 +2005,7 @@ void LiveSession::tryToFinishBandwidthSwitch(const AString &oldUri) { if ((mNewStreamMask & stream) && mStreams[idx].mNewUri.empty()) { ALOGW("swapping stream type %d %s to empty stream", - stream, mStreams[idx].mUri.c_str()); + stream, uriDebugString(mStreams[idx].mUri).c_str()); } mStreams[idx].mUri = mStreams[idx].mNewUri; mStreams[idx].mNewUri.clear(); @@ -2033,7 +2033,7 @@ void LiveSession::tryToFinishBandwidthSwitch(const AString &oldUri) { CHECK(idx >= 0); if (mStreams[idx].mNewUri.empty()) { ALOGW("swapping extra stream type %d %s to empty stream", - stream, mStreams[idx].mUri.c_str()); + stream, uriDebugString(mStreams[idx].mUri).c_str()); } mStreams[idx].mUri = mStreams[idx].mNewUri; mStreams[idx].mNewUri.clear(); @@ -2138,7 +2138,7 @@ void LiveSession::cancelBandwidthSwitch(bool resume) { ALOGV("stopping newUri = %s", newUri.c_str()); ssize_t index = mFetcherInfos.indexOfKey(newUri); if (index < 0) { - ALOGE("did not find fetcher for newUri: %s", newUri.c_str()); + ALOGE("did not find fetcher for newUri: %s", uriDebugString(newUri).c_str()); continue; } FetcherInfo &info = mFetcherInfos.editValueAt(index); diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 4392799867..b2361b8387 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -1205,8 +1205,7 @@ status_t M3UParser::parseMedia(const AString &line) { if (val.size() < 2 || val.c_str()[0] != '"' || val.c_str()[val.size() - 1] != '"') { - ALOGE("Expected quoted string for URI, got '%s' instead.", - val.c_str()); + ALOGE("Expected quoted string for URI."); return ERROR_MALFORMED; } diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index 562c625798..d153598dc3 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -365,10 +365,10 @@ status_t PlaylistFetcher::decryptBuffer( if (err == ERROR_NOT_CONNECTED) { return ERROR_NOT_CONNECTED; } else if (err < 0) { - ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); + ALOGE("failed to fetch cipher key from '%s'.", uriDebugString(keyURI).c_str()); return ERROR_IO; } else if (key->size() != 16) { - ALOGE("key file '%s' wasn't 16 bytes in size.", keyURI.c_str()); + ALOGE("key file '%s' wasn't 16 bytes in size.", uriDebugString(keyURI).c_str()); return ERROR_MALFORMED; } @@ -1366,7 +1366,7 @@ void PlaylistFetcher::onDownloadNext() { } if (bytesRead < 0) { status_t err = bytesRead; - ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); + ALOGE("failed to fetch .ts segment at url '%s'", uriDebugString(uri).c_str()); notifyError(err); return; } diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 20cb415fe2..789e62a67d 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -255,7 +255,7 @@ void ARTSPConnection::onConnect(const sp &msg) { struct hostent *ent = gethostbyname(host.c_str()); if (ent == NULL) { - ALOGE("Unknown host %s", host.c_str()); + ALOGE("Unknown host %s", uriDebugString(host).c_str()); reply->setInt32("result", -ENOENT); reply->post(); diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index c581e9d644..9263565ad7 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -80,7 +80,7 @@ bool ASessionDescription::parse(const void *data, size_t size) { return false; } - ALOGI("%s", line.c_str()); + ALOGV("%s", line.c_str()); switch (line.c_str()[0]) { case 'v': diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 8454ca1aeb..b4515e4163 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -345,8 +345,7 @@ struct MyHandler : public AHandler { struct hostent *ent = gethostbyname(mSessionHost.c_str()); if (ent == NULL) { - ALOGE("Failed to look up address of session host '%s'", - mSessionHost.c_str()); + ALOGE("Failed to look up address of session host"); return false; } @@ -531,7 +530,7 @@ struct MyHandler : public AHandler { mSessionURL.append(AStringPrintf("%u", port)); mSessionURL.append(path); - ALOGI("rewritten session url: '%s'", mSessionURL.c_str()); + ALOGV("rewritten session url: '%s'", mSessionURL.c_str()); } sp reply = new AMessage('conn', this); -- GitLab From 4b08d5dce8d3a8aef64b493da1fa3e4bf114b347 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 1 Feb 2019 09:53:14 -0800 Subject: [PATCH 0987/1530] Camera: Keep Depth EXIF orientation consistent Depth and confidence maps should always use the same EXIF orientation as the main color image. Bug: 123699590 Test: Manual using application, Camera CTS, adb shell /data/nativetest64/cameraservice_test/cameraservice_test --gtest_filter=DepthProcessorTest.* Change-Id: I0d887798e8717cdff81aba10d595dc3ccfe99197 --- services/camera/libcameraservice/Android.bp | 3 + .../common/DepthPhotoProcessor.cpp | 69 +++- .../camera/libcameraservice/tests/Android.mk | 4 + .../tests/DepthProcessorTest.cpp | 280 ++++++++++++++ .../libcameraservice/tests/NV12Compressor.cpp | 349 ++++++++++++++++++ .../libcameraservice/tests/NV12Compressor.h | 115 ++++++ .../libcameraservice/utils/ExifUtils.cpp | 55 ++- .../camera/libcameraservice/utils/ExifUtils.h | 25 +- 8 files changed, 876 insertions(+), 24 deletions(-) create mode 100644 services/camera/libcameraservice/tests/DepthProcessorTest.cpp create mode 100644 services/camera/libcameraservice/tests/NV12Compressor.cpp create mode 100644 services/camera/libcameraservice/tests/NV12Compressor.h diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 2ca83561a2..3ce11aeab5 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -137,6 +137,7 @@ cc_library_shared { name: "libdepthphoto", srcs: [ + "utils/ExifUtils.cpp", "common/DepthPhotoProcessor.cpp", ], @@ -150,6 +151,8 @@ cc_library_shared { "libcutils", "libjpeg", "libmemunreachable", + "libexif", + "libcamera_client", ], include_dirs: [ diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp index a945aca13b..3af422064f 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -32,9 +32,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include @@ -61,8 +64,44 @@ using dynamic_depth::Profiles; namespace android { namespace camera3 { +ExifOrientation getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize) { + if ((jpegBuffer == nullptr) || (jpegBufferSize == 0)) { + return ExifOrientation::ORIENTATION_UNDEFINED; + } + + auto exifData = exif_data_new(); + exif_data_load_data(exifData, jpegBuffer, jpegBufferSize); + ExifEntry *orientation = exif_content_get_entry(exifData->ifd[EXIF_IFD_0], + EXIF_TAG_ORIENTATION); + if ((orientation == nullptr) || (orientation->size != sizeof(ExifShort))) { + ALOGV("%s: Orientation EXIF entry invalid!", __FUNCTION__); + exif_data_unref(exifData); + return ExifOrientation::ORIENTATION_0_DEGREES; + } + + auto orientationValue = exif_get_short(orientation->data, exif_data_get_byte_order(exifData)); + ExifOrientation ret; + switch (orientationValue) { + case ExifOrientation::ORIENTATION_0_DEGREES: + case ExifOrientation::ORIENTATION_90_DEGREES: + case ExifOrientation::ORIENTATION_180_DEGREES: + case ExifOrientation::ORIENTATION_270_DEGREES: + ret = static_cast (orientationValue); + break; + default: + ALOGE("%s: Unexpected EXIF orientation value: %d, defaulting to 0 degrees", + __FUNCTION__, orientationValue); + ret = ExifOrientation::ORIENTATION_0_DEGREES; + } + + exif_data_unref(exifData); + + return ret; +} + status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out, - const size_t maxOutSize, uint8_t jpegQuality, size_t &actualSize) { + const size_t maxOutSize, uint8_t jpegQuality, ExifOrientation exifOrientation, + size_t &actualSize) { status_t ret; // libjpeg is a C library so we use C-style "inheritance" by // putting libjpeg's jpeg_destination_mgr first in our custom @@ -151,6 +190,23 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out jpeg_start_compress(&cinfo, TRUE); + if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { + std::unique_ptr utils(ExifUtils::create()); + utils->initializeEmpty(); + utils->setImageWidth(width); + utils->setImageHeight(height); + utils->setOrientationValue(exifOrientation); + + if (utils->generateApp1()) { + const uint8_t* exifBuffer = utils->getApp1Buffer(); + size_t exifBufferSize = utils->getApp1Length(); + jpeg_write_marker(&cinfo, JPEG_APP0 + 1, static_cast(exifBuffer), + exifBufferSize); + } else { + ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__); + } + } + for (size_t i = 0; i < cinfo.image_height; i++) { auto currentRow = static_cast(in + i*width); jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1); @@ -169,7 +225,7 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out } std::unique_ptr processDepthMapFrame(DepthPhotoInputFrame inputFrame, - std::vector> *items /*out*/) { + ExifOrientation exifOrientation, std::vector> *items /*out*/) { std::vector points, confidence; size_t pointCount = inputFrame.mDepthMapWidth * inputFrame.mDepthMapHeight; @@ -227,7 +283,7 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra size_t actualJpegSize; auto ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, pointsQuantized.data(), depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, - inputFrame.mJpegQuality, actualJpegSize); + inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Depth map compression failed!", __FUNCTION__); return nullptr; @@ -236,7 +292,7 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, confidenceQuantized.data(), depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, - inputFrame.mJpegQuality, actualJpegSize); + inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Confidence map compression failed!", __FUNCTION__); return nullptr; @@ -262,7 +318,10 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de return BAD_VALUE; } - cameraParams->depth_map = processDepthMapFrame(inputFrame, &items); + ExifOrientation exifOrientation = getExifOrientation( + reinterpret_cast (inputFrame.mMainJpegBuffer), + inputFrame.mMainJpegSize); + cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items); if (cameraParams->depth_map == nullptr) { ALOGE("%s: Depth map processing failed!", __FUNCTION__); return BAD_VALUE; diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk index d777ca1cb1..b4e7c32bef 100644 --- a/services/camera/libcameraservice/tests/Android.mk +++ b/services/camera/libcameraservice/tests/Android.mk @@ -27,6 +27,8 @@ LOCAL_SHARED_LIBRARIES := \ libcamera_client \ libcamera_metadata \ libutils \ + libjpeg \ + libexif \ android.hardware.camera.common@1.0 \ android.hardware.camera.provider@2.4 \ android.hardware.camera.provider@2.5 \ @@ -36,6 +38,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES += \ system/media/private/camera/include \ + external/dynamic_depth/includes \ + external/dynamic_depth/internal \ LOCAL_CFLAGS += -Wall -Wextra -Werror diff --git a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp new file mode 100644 index 0000000000..9898122e9d --- /dev/null +++ b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2019 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 "DepthProcessorTest" + +#include +#include + +#include +#include + +#include "../common/DepthPhotoProcessor.h" +#include "../utils/ExifUtils.h" +#include "NV12Compressor.h" + +using namespace android; +using namespace android::camera3; + +static const size_t kTestBufferWidth = 640; +static const size_t kTestBufferHeight = 480; +static const size_t kTestBufferNV12Size ((((kTestBufferWidth) * (kTestBufferHeight)) * 3) / 2); +static const size_t kTestBufferDepthSize (kTestBufferWidth * kTestBufferHeight); +static const size_t kSeed = 1234; + +void linkToDepthPhotoLibrary(void **libHandle /*out*/, + process_depth_photo_frame *processFrameFunc /*out*/) { + ASSERT_NE(libHandle, nullptr); + ASSERT_NE(processFrameFunc, nullptr); + + *libHandle = dlopen(kDepthPhotoLibrary, RTLD_NOW | RTLD_LOCAL); + if (*libHandle != nullptr) { + *processFrameFunc = reinterpret_cast ( + dlsym(*libHandle, kDepthPhotoProcessFunction)); + ASSERT_NE(*processFrameFunc, nullptr); + } +} + +void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif, + std::vector *colorJpegBuffer /*out*/) { + ASSERT_NE(colorJpegBuffer, nullptr); + + std::array colorSourceBuffer; + std::default_random_engine gen(kSeed); + std::uniform_int_distribution uniDist(0, UINT8_MAX - 1); + for (size_t i = 0; i < colorSourceBuffer.size(); i++) { + colorSourceBuffer[i] = uniDist(gen); + } + NV12Compressor jpegCompressor; + if (includeExif) { + ASSERT_TRUE(jpegCompressor.compressWithExifOrientation( + reinterpret_cast (colorSourceBuffer.data()), + kTestBufferWidth, kTestBufferHeight, jpegQuality, orientationValue)); + } else { + ASSERT_TRUE(jpegCompressor.compress( + reinterpret_cast (colorSourceBuffer.data()), + kTestBufferWidth, kTestBufferHeight, jpegQuality)); + } + + *colorJpegBuffer = std::move(jpegCompressor.getCompressedData()); + ASSERT_FALSE(colorJpegBuffer->empty()); +} + +void generateDepth16Buffer(std::array *depth16Buffer /*out*/) { + ASSERT_NE(depth16Buffer, nullptr); + std::default_random_engine gen(kSeed+1); + std::uniform_int_distribution uniDist(0, UINT16_MAX - 1); + for (size_t i = 0; i < depth16Buffer->size(); i++) { + (*depth16Buffer)[i] = uniDist(gen); + } +} + +TEST(DepthProcessorTest, LinkToLibray) { + void *libHandle; + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle != nullptr) { + dlclose(libHandle); + } +} + +TEST(DepthProcessorTest, BadInput) { + void *libHandle; + int jpegQuality = 95; + + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle == nullptr) { + // Depth library no present, nothing more to test. + return; + } + + DepthPhotoInputFrame inputFrame; + // Worst case both depth and confidence maps have the same size as the main color image. + inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; + + std::vector colorJpegBuffer; + generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, + /*includeExif*/ false, &colorJpegBuffer); + + std::array depth16Buffer; + generateDepth16Buffer(&depth16Buffer); + + std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); + size_t actualDepthPhotoSize = 0; + + inputFrame.mMainJpegWidth = kTestBufferWidth; + inputFrame.mMainJpegHeight = kTestBufferHeight; + inputFrame.mJpegQuality = jpegQuality; + ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + + inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); + inputFrame.mMainJpegSize = colorJpegBuffer.size(); + ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + + inputFrame.mDepthMapBuffer = depth16Buffer.data(); + inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; + inputFrame.mDepthMapHeight = kTestBufferHeight; + ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), nullptr, + &actualDepthPhotoSize), 0); + + ASSERT_NE(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), nullptr), + 0); + + dlclose(libHandle); +} + +TEST(DepthProcessorTest, BasicDepthPhotoValidation) { + void *libHandle; + int jpegQuality = 95; + + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle == nullptr) { + // Depth library no present, nothing more to test. + return; + } + + std::vector colorJpegBuffer; + generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, + /*includeExif*/ false, &colorJpegBuffer); + + std::array depth16Buffer; + generateDepth16Buffer(&depth16Buffer); + + DepthPhotoInputFrame inputFrame; + inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); + inputFrame.mMainJpegSize = colorJpegBuffer.size(); + // Worst case both depth and confidence maps have the same size as the main color image. + inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; + inputFrame.mMainJpegWidth = kTestBufferWidth; + inputFrame.mMainJpegHeight = kTestBufferHeight; + inputFrame.mJpegQuality = jpegQuality; + inputFrame.mDepthMapBuffer = depth16Buffer.data(); + inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; + inputFrame.mDepthMapHeight = kTestBufferHeight; + + std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); + size_t actualDepthPhotoSize = 0; + ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + ASSERT_TRUE((actualDepthPhotoSize > 0) && (depthPhotoBuffer.size() >= actualDepthPhotoSize)); + + // The final depth photo must consist of three jpeg images: + // - the main color image + // - the depth map image + // - the confidence map image + size_t mainJpegSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, + &mainJpegSize), OK); + ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); + size_t depthMapSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, + actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); + ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); + + dlclose(libHandle); +} + +TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { + void *libHandle; + int jpegQuality = 95; + + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle == nullptr) { + // Depth library no present, nothing more to test. + return; + } + + ExifOrientation exifOrientations[] = { ExifOrientation::ORIENTATION_UNDEFINED, + ExifOrientation::ORIENTATION_0_DEGREES, ExifOrientation::ORIENTATION_90_DEGREES, + ExifOrientation::ORIENTATION_180_DEGREES, ExifOrientation::ORIENTATION_270_DEGREES }; + for (auto exifOrientation : exifOrientations) { + std::vector colorJpegBuffer; + generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, + &colorJpegBuffer); + if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { + auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation( + reinterpret_cast (colorJpegBuffer.data()), + colorJpegBuffer.size(), &jpegExifOrientation), OK); + ASSERT_EQ(exifOrientation, jpegExifOrientation); + } + + std::array depth16Buffer; + generateDepth16Buffer(&depth16Buffer); + + DepthPhotoInputFrame inputFrame; + inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); + inputFrame.mMainJpegSize = colorJpegBuffer.size(); + // Worst case both depth and confidence maps have the same size as the main color image. + inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; + inputFrame.mMainJpegWidth = kTestBufferWidth; + inputFrame.mMainJpegHeight = kTestBufferHeight; + inputFrame.mJpegQuality = jpegQuality; + inputFrame.mDepthMapBuffer = depth16Buffer.data(); + inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; + inputFrame.mDepthMapHeight = kTestBufferHeight; + + std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); + size_t actualDepthPhotoSize = 0; + ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + ASSERT_TRUE((actualDepthPhotoSize > 0) && + (depthPhotoBuffer.size() >= actualDepthPhotoSize)); + + size_t mainJpegSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, + &mainJpegSize), OK); + ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); + size_t depthMapSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, + actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); + ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); + size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize); + + //Depth and confidence images must have the same EXIF orientation as the source + auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation( + reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize), + depthMapSize, &depthJpegExifOrientation), OK); + if (exifOrientation == ORIENTATION_UNDEFINED) { + // In case of undefined or missing EXIF orientation, always expect 0 degrees in the + // depth map. + ASSERT_EQ(depthJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES); + } else { + ASSERT_EQ(depthJpegExifOrientation, exifOrientation); + } + + auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation( + reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize + + depthMapSize), confidenceMapSize, &confidenceJpegExifOrientation), OK); + if (exifOrientation == ORIENTATION_UNDEFINED) { + // In case of undefined or missing EXIF orientation, always expect 0 degrees in the + // confidence map. + ASSERT_EQ(confidenceJpegExifOrientation, ExifOrientation::ORIENTATION_0_DEGREES); + } else { + ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation); + } + } + + dlclose(libHandle); +} diff --git a/services/camera/libcameraservice/tests/NV12Compressor.cpp b/services/camera/libcameraservice/tests/NV12Compressor.cpp new file mode 100644 index 0000000000..b9f27faa57 --- /dev/null +++ b/services/camera/libcameraservice/tests/NV12Compressor.cpp @@ -0,0 +1,349 @@ +/* +* Copyright (C) 2019 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 "Test_NV12Compressor" + +#include "NV12Compressor.h" + +#include +#include + +using namespace android; +using namespace android::camera3; + +namespace std { +template <> +struct default_delete { + inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); } +}; + +template <> +struct default_delete { + inline void operator()(ExifData* data) const { exif_data_unref(data); } +}; + +} // namespace std + +bool NV12Compressor::compress(const unsigned char* data, int width, int height, int quality) { + if (!configureCompressor(width, height, quality)) { + // the method will have logged a more detailed error message than we can + // provide here so just return. + return false; + } + + return compressData(data, /*exifData*/ nullptr); +} + +bool NV12Compressor::compressWithExifOrientation(const unsigned char* data, int width, int height, + int quality, android::camera3::ExifOrientation exifValue) { + std::unique_ptr exifData(exif_data_new()); + if (exifData.get() == nullptr) { + return false; + } + + exif_data_set_option(exifData.get(), EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); + exif_data_set_data_type(exifData.get(), EXIF_DATA_TYPE_COMPRESSED); + exif_data_set_byte_order(exifData.get(), EXIF_BYTE_ORDER_INTEL); + std::unique_ptr exifEntry(exif_entry_new()); + if (exifEntry.get() == nullptr) { + return false; + } + + exifEntry->tag = EXIF_TAG_ORIENTATION; + exif_content_add_entry(exifData->ifd[EXIF_IFD_0], exifEntry.get()); + exif_entry_initialize(exifEntry.get(), exifEntry->tag); + exif_set_short(exifEntry->data, EXIF_BYTE_ORDER_INTEL, exifValue); + + if (!configureCompressor(width, height, quality)) { + return false; + } + + return compressData(data, exifData.get()); +} + +const std::vector& NV12Compressor::getCompressedData() const { + return mDestManager.mBuffer; +} + +bool NV12Compressor::configureCompressor(int width, int height, int quality) { + mCompressInfo.err = jpeg_std_error(&mErrorManager); + // NOTE! DANGER! Do not construct any non-trivial objects below setjmp! + // The compiler will not generate code to destroy them during the return + // below so they will leak. Additionally, do not place any calls to libjpeg + // that can fail above this line or any error will cause undefined behavior. + if (setjmp(mErrorManager.mJumpBuffer)) { + // This is where the error handler will jump in case setup fails + // The error manager will ALOG an appropriate error message + return false; + } + + jpeg_create_compress(&mCompressInfo); + + mCompressInfo.image_width = width; + mCompressInfo.image_height = height; + mCompressInfo.input_components = 3; + mCompressInfo.in_color_space = JCS_YCbCr; + jpeg_set_defaults(&mCompressInfo); + + jpeg_set_quality(&mCompressInfo, quality, TRUE); + // It may seem weird to set color space here again but this will also set + // other fields. These fields might be overwritten by jpeg_set_defaults + jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr); + mCompressInfo.raw_data_in = TRUE; + mCompressInfo.dct_method = JDCT_IFAST; + // Set sampling factors + mCompressInfo.comp_info[0].h_samp_factor = 2; + mCompressInfo.comp_info[0].v_samp_factor = 2; + mCompressInfo.comp_info[1].h_samp_factor = 1; + mCompressInfo.comp_info[1].v_samp_factor = 1; + mCompressInfo.comp_info[2].h_samp_factor = 1; + mCompressInfo.comp_info[2].v_samp_factor = 1; + + mCompressInfo.dest = &mDestManager; + + return true; +} + +static void deinterleave(const uint8_t* vuPlanar, std::vector& uRows, + std::vector& vRows, int rowIndex, int width, int height, int stride) { + int numRows = (height - rowIndex) / 2; + if (numRows > 8) numRows = 8; + for (int row = 0; row < numRows; ++row) { + int offset = ((rowIndex >> 1) + row) * stride; + const uint8_t* vu = vuPlanar + offset; + for (int i = 0; i < (width >> 1); ++i) { + int index = row * (width >> 1) + i; + uRows[index] = vu[1]; + vRows[index] = vu[0]; + vu += 2; + } + } +} + +bool NV12Compressor::compressData(const unsigned char* data, ExifData* exifData) { + const uint8_t* y[16]; + const uint8_t* cb[8]; + const uint8_t* cr[8]; + const uint8_t** planes[3] = { y, cb, cr }; + + int i, offset; + int width = mCompressInfo.image_width; + int height = mCompressInfo.image_height; + const uint8_t* yPlanar = data; + const uint8_t* vuPlanar = data + (width * height); + std::vector uRows(8 * (width >> 1)); + std::vector vRows(8 * (width >> 1)); + + // NOTE! DANGER! Do not construct any non-trivial objects below setjmp! + // The compiler will not generate code to destroy them during the return + // below so they will leak. Additionally, do not place any calls to libjpeg + // that can fail above this line or any error will cause undefined behavior. + if (setjmp(mErrorManager.mJumpBuffer)) { + // This is where the error handler will jump in case compression fails + // The error manager will ALOG an appropriate error message + return false; + } + + jpeg_start_compress(&mCompressInfo, TRUE); + + attachExifData(exifData); + + // process 16 lines of Y and 8 lines of U/V each time. + while (mCompressInfo.next_scanline < mCompressInfo.image_height) { + //deinterleave u and v + deinterleave(vuPlanar, uRows, vRows, mCompressInfo.next_scanline, + width, height, width); + + // Jpeg library ignores the rows whose indices are greater than height. + for (i = 0; i < 16; i++) { + // y row + y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width; + + // construct u row and v row + if ((i & 1) == 0) { + // height and width are both halved because of downsampling + offset = (i >> 1) * (width >> 1); + cb[i/2] = &uRows[offset]; + cr[i/2] = &vRows[offset]; + } + } + jpeg_write_raw_data(&mCompressInfo, const_cast(planes), 16); + } + + jpeg_finish_compress(&mCompressInfo); + jpeg_destroy_compress(&mCompressInfo); + + return true; +} + +bool NV12Compressor::attachExifData(ExifData* exifData) { + if (exifData == nullptr) { + // This is not an error, we don't require EXIF data + return true; + } + + // Save the EXIF data to memory + unsigned char* rawData = nullptr; + unsigned int size = 0; + exif_data_save_data(exifData, &rawData, &size); + if (rawData == nullptr) { + ALOGE("Failed to create EXIF data block"); + return false; + } + + jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size); + free(rawData); + return true; +} + +NV12Compressor::ErrorManager::ErrorManager() { + error_exit = &onJpegError; +} + +void NV12Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) { + // NOTE! Do not construct any non-trivial objects in this method at the top + // scope. Their destructors will not be called. If you do need such an + // object create a local scope that does not include the longjmp call, + // that ensures the object is destroyed before longjmp is called. + ErrorManager* errorManager = reinterpret_cast(cinfo->err); + + // Format and log error message + char errorMessage[JMSG_LENGTH_MAX]; + (*errorManager->format_message)(cinfo, errorMessage); + errorMessage[sizeof(errorMessage) - 1] = '\0'; + ALOGE("JPEG compression error: %s", errorMessage); + jpeg_destroy(cinfo); + + // And through the looking glass we go + longjmp(errorManager->mJumpBuffer, 1); +} + +NV12Compressor::DestinationManager::DestinationManager() { + init_destination = &initDestination; + empty_output_buffer = &emptyOutputBuffer; + term_destination = &termDestination; +} + +void NV12Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) { + auto manager = reinterpret_cast(cinfo->dest); + + // Start out with some arbitrary but not too large buffer size + manager->mBuffer.resize(16 * 1024); + manager->next_output_byte = &manager->mBuffer[0]; + manager->free_in_buffer = manager->mBuffer.size(); +} + +boolean NV12Compressor::DestinationManager::emptyOutputBuffer( + j_compress_ptr cinfo) { + auto manager = reinterpret_cast(cinfo->dest); + + // Keep doubling the size of the buffer for a very low, amortized + // performance cost of the allocations + size_t oldSize = manager->mBuffer.size(); + manager->mBuffer.resize(oldSize * 2); + manager->next_output_byte = &manager->mBuffer[oldSize]; + manager->free_in_buffer = manager->mBuffer.size() - oldSize; + return manager->free_in_buffer != 0; +} + +void NV12Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) { + auto manager = reinterpret_cast(cinfo->dest); + + // Resize down to the exact size of the output, that is remove as many + // bytes as there are left in the buffer + manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer); +} + +status_t NV12Compressor::findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_t *size /*out*/) { + if ((size == nullptr) || (jpegBuffer == nullptr)) { + return BAD_VALUE; + } + + if (checkJpegStart(jpegBuffer) == 0) { + return BAD_VALUE; + } + + // Read JFIF segment markers, skip over segment data + *size = kMarkerLength; //jump to Start Of Image + while (*size <= maxSize - kMarkerLength) { + segment_t *segment = (segment_t*)(jpegBuffer + *size); + uint8_t type = checkJpegMarker(segment->marker); + if (type == 0) { // invalid marker, no more segments, begin JPEG data + break; + } + if (type == kEndOfImage || *size > maxSize - sizeof(segment_t)) { + return BAD_VALUE; + } + + size_t length = ntohs(segment->length); + *size += length + kMarkerLength; + } + + // Find End of Image + // Scan JPEG buffer until End of Image + bool foundEnd = false; + for ( ; *size <= maxSize - kMarkerLength; (*size)++) { + if (checkJpegEnd(jpegBuffer + *size)) { + foundEnd = true; + *size += kMarkerLength; + break; + } + } + + if (!foundEnd) { + return BAD_VALUE; + } + + if (*size > maxSize) { + *size = maxSize; + } + + return OK; +} + +status_t NV12Compressor::getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize, + ExifOrientation *exifValue /*out*/) { + if ((jpegBuffer == nullptr) || (exifValue == nullptr) || (jpegBufferSize == 0u)) { + return BAD_VALUE; + } + + std::unique_ptr exifData(exif_data_new()); + exif_data_load_data(exifData.get(), jpegBuffer, jpegBufferSize); + ExifEntry *orientation = exif_content_get_entry(exifData->ifd[EXIF_IFD_0], + EXIF_TAG_ORIENTATION); + if ((orientation == nullptr) || (orientation->size != sizeof(ExifShort))) { + return BAD_VALUE; + } + + auto orientationValue = exif_get_short(orientation->data, + exif_data_get_byte_order(exifData.get())); + status_t ret; + switch (orientationValue) { + case ExifOrientation::ORIENTATION_0_DEGREES: + case ExifOrientation::ORIENTATION_90_DEGREES: + case ExifOrientation::ORIENTATION_180_DEGREES: + case ExifOrientation::ORIENTATION_270_DEGREES: + *exifValue = static_cast (orientationValue); + ret = OK; + break; + default: + ALOGE("%s: Unexpected EXIF orientation value: %u", __FUNCTION__, orientationValue); + ret = BAD_VALUE; + } + + return ret; +} diff --git a/services/camera/libcameraservice/tests/NV12Compressor.h b/services/camera/libcameraservice/tests/NV12Compressor.h new file mode 100644 index 0000000000..92804c10af --- /dev/null +++ b/services/camera/libcameraservice/tests/NV12Compressor.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 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 TEST_CAMERA_JPEG_STUB_NV12_COMPRESSOR_H +#define TEST_CAMERA_JPEG_STUB_NV12_COMPRESSOR_H + +#include +#include +extern "C" { +#include +#include +} + +#include +#include + +#include "../utils/ExifUtils.h" + +struct _ExifData; +typedef _ExifData ExifData; + +class NV12Compressor { +public: + NV12Compressor() {} + + /* Compress |data| which represents raw NV21 encoded data of dimensions + * |width| * |height|. + */ + bool compress(const unsigned char* data, int width, int height, int quality); + bool compressWithExifOrientation(const unsigned char* data, int width, int height, int quality, + android::camera3::ExifOrientation exifValue); + + /* Get a reference to the compressed data, this will return an empty vector + * if compress has not been called yet + */ + const std::vector& getCompressedData() const; + + static android::status_t findJpegSize(uint8_t *jpegBuffer, size_t maxSize, + size_t *size /*out*/); + + + static android::status_t getExifOrientation(const unsigned char *jpegBuffer, + size_t jpegBufferSize, android::camera3::ExifOrientation *exifValue /*out*/); + +private: + + struct DestinationManager : jpeg_destination_mgr { + DestinationManager(); + + static void initDestination(j_compress_ptr cinfo); + static boolean emptyOutputBuffer(j_compress_ptr cinfo); + static void termDestination(j_compress_ptr cinfo); + + std::vector mBuffer; + }; + + struct ErrorManager : jpeg_error_mgr { + ErrorManager(); + + static void onJpegError(j_common_ptr cinfo); + + jmp_buf mJumpBuffer; + }; + + static const size_t kMarkerLength = 2; // length of a marker + static const uint8_t kMarker = 0xFF; // First byte of marker + static const uint8_t kStartOfImage = 0xD8; // Start of Image + static const uint8_t kEndOfImage = 0xD9; // End of Image + + struct __attribute__((packed)) segment_t { + uint8_t marker[kMarkerLength]; + uint16_t length; + }; + + + // check for Start of Image marker + static bool checkJpegStart(uint8_t* buf) { + return buf[0] == kMarker && buf[1] == kStartOfImage; + } + + // check for End of Image marker + static bool checkJpegEnd(uint8_t *buf) { + return buf[0] == kMarker && buf[1] == kEndOfImage; + } + + // check for arbitrary marker, returns marker type (second byte) + // returns 0 if no marker found. Note: 0x00 is not a valid marker type + static uint8_t checkJpegMarker(uint8_t *buf) { + return (buf[0] == kMarker) ? buf[1] : 0; + } + + jpeg_compress_struct mCompressInfo; + DestinationManager mDestManager; + ErrorManager mErrorManager; + + bool configureCompressor(int width, int height, int quality); + bool compressData(const unsigned char* data, ExifData* exifData); + bool attachExifData(ExifData* exifData); +}; + +#endif // TEST_CAMERA_JPEG_STUB_NV12_COMPRESSOR_H + diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp index 4dea8b5d3b..c0afdc18ca 100644 --- a/services/camera/libcameraservice/utils/ExifUtils.cpp +++ b/services/camera/libcameraservice/utils/ExifUtils.cpp @@ -55,6 +55,7 @@ public: // Initialize() can be called multiple times. The setting of Exif tags will be // cleared. virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize); + virtual bool initializeEmpty(); // set all known fields from a metadata structure virtual bool setFromMetadata(const CameraMetadata& metadata, @@ -150,7 +151,11 @@ public: // sets image orientation. // Returns false if memory allocation fails. - virtual bool setOrientation(uint16_t orientation); + virtual bool setOrientation(uint16_t degrees); + + // sets image orientation. + // Returns false if memory allocation fails. + virtual bool setOrientationValue(ExifOrientation orientationValue); // sets the shutter speed. // Returns false if memory allocation fails. @@ -314,6 +319,26 @@ bool ExifUtilsImpl::initialize(const unsigned char *app1Segment, size_t app1Segm return true; } +bool ExifUtilsImpl::initializeEmpty() { + reset(); + exif_data_ = exif_data_new(); + if (exif_data_ == nullptr) { + ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__); + return false; + } + // set the image options. + exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); + exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED); + exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL); + + // set exif version to 2.2. + if (!setExifVersion("0220")) { + return false; + } + + return true; +} + bool ExifUtilsImpl::setAperture(float aperture) { float apexValue = convertToApex(aperture); SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, @@ -609,32 +634,26 @@ bool ExifUtilsImpl::setExposureBias(int32_t ev, return true; } -bool ExifUtilsImpl::setOrientation(uint16_t orientation) { - /* - * Orientation value: - * 1 2 3 4 5 6 7 8 - * - * 888888 888888 88 88 8888888888 88 88 8888888888 - * 88 88 88 88 88 88 88 88 88 88 88 88 - * 8888 8888 8888 8888 88 8888888888 8888888888 88 - * 88 88 88 88 - * 88 88 888888 888888 - */ - int value = 1; - switch (orientation) { +bool ExifUtilsImpl::setOrientation(uint16_t degrees) { + ExifOrientation value = ExifOrientation::ORIENTATION_0_DEGREES; + switch (degrees) { case 90: - value = 6; + value = ExifOrientation::ORIENTATION_90_DEGREES; break; case 180: - value = 3; + value = ExifOrientation::ORIENTATION_180_DEGREES; break; case 270: - value = 8; + value = ExifOrientation::ORIENTATION_270_DEGREES; break; default: break; } - SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value); + return setOrientationValue(value); +} + +bool ExifUtilsImpl::setOrientationValue(ExifOrientation orientationValue) { + SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, orientationValue); return true; } diff --git a/services/camera/libcameraservice/utils/ExifUtils.h b/services/camera/libcameraservice/utils/ExifUtils.h index c78bab9961..f1d0205759 100644 --- a/services/camera/libcameraservice/utils/ExifUtils.h +++ b/services/camera/libcameraservice/utils/ExifUtils.h @@ -22,6 +22,24 @@ namespace android { namespace camera3 { +/* + * Orientation value: + * 1 2 3 4 5 6 7 8 + * + * 888888 888888 88 88 8888888888 88 88 8888888888 + * 88 88 88 88 88 88 88 88 88 88 88 88 + * 8888 8888 8888 8888 88 8888888888 8888888888 88 + * 88 88 88 88 + * 88 88 888888 888888 + */ +enum ExifOrientation : uint16_t { + ORIENTATION_UNDEFINED = 0x0, + ORIENTATION_0_DEGREES = 0x1, + ORIENTATION_90_DEGREES = 0x6, + ORIENTATION_180_DEGREES = 0x3, + ORIENTATION_270_DEGREES = 0x8, +}; + // This is based on the camera HIDL shim implementation, which was in turned // based on original ChromeOS ARC implementation of a V4L2 HAL @@ -49,6 +67,7 @@ public: // Initialize() can be called multiple times. The setting of Exif tags will be // cleared. virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize) = 0; + virtual bool initializeEmpty() = 0; // Set all known fields from a metadata structure virtual bool setFromMetadata(const CameraMetadata& metadata, @@ -142,7 +161,11 @@ public: // Sets image orientation. // Returns false if memory allocation fails. - virtual bool setOrientation(uint16_t orientation) = 0; + virtual bool setOrientation(uint16_t degrees) = 0; + + // Sets image orientation. + // Returns false if memory allocation fails. + virtual bool setOrientationValue(ExifOrientation orientationValue) = 0; // Sets the shutter speed. // Returns false if memory allocation fails. -- GitLab From a992b58130f81f68d73a049f9e2fff8f1c7d00b3 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 29 Jan 2019 17:03:22 -0800 Subject: [PATCH 0988/1530] ACodec: CodecObserver to always have mNotify set Bug: 123385269 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I6ed41102e76c7e857705fc2e1785c9fed706be54 --- media/libstagefright/ACodec.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 998f096c87..ac6ccce9b7 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -171,11 +171,7 @@ static sp getCopyConverter() { } struct CodecObserver : public BnOMXObserver { - CodecObserver() {} - - void setNotificationMessage(const sp &msg) { - mNotify = msg; - } + explicit CodecObserver(const sp &msg) : mNotify(msg) {} // from IOMXObserver virtual void onMessages(const std::list &messages) { @@ -251,7 +247,7 @@ protected: virtual ~CodecObserver() {} private: - sp mNotify; + const sp mNotify; DISALLOW_EVIL_CONSTRUCTORS(CodecObserver); }; @@ -6629,7 +6625,8 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { CHECK(mCodec->mOMXNode == NULL); - sp notify = new AMessage(kWhatOMXDied, mCodec); + sp notify = new AMessage(kWhatOMXMessageList, mCodec); + notify->setInt32("generation", mCodec->mNodeGeneration + 1); sp obj; CHECK(msg->findObject("codecInfo", &obj)); @@ -6644,7 +6641,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { AString componentName; CHECK(msg->findString("componentName", &componentName)); - sp observer = new CodecObserver; + sp observer = new CodecObserver(notify); sp omx; sp omxNode; @@ -6675,9 +6672,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { mDeathNotifier.clear(); } - notify = new AMessage(kWhatOMXMessageList, mCodec); - notify->setInt32("generation", ++mCodec->mNodeGeneration); - observer->setNotificationMessage(notify); + ++mCodec->mNodeGeneration; mCodec->mComponentName = componentName; mCodec->mRenderTracker.setComponentName(componentName); @@ -8572,7 +8567,7 @@ status_t ACodec::queryCapabilities( } sp omx = client.interface(); - sp observer = new CodecObserver; + sp observer = new CodecObserver(new AMessage); sp omxNode; err = omx->allocateNode(name, observer, &omxNode); -- GitLab From 06af8c9d08b2e541b1acf6f2c06100109f948ec2 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Thu, 7 Feb 2019 12:34:41 -0800 Subject: [PATCH 0989/1530] Camera: Apply physical rotation for depth/conf. maps Depth and confidence maps require physical rotation in case the source color image has similar physical rotation. The EXIF orientation value will be kept consistent and set to 0 in case of physical rotation. Bug: 123699590 Test: Manual using application, adb shell /data/nativetest64/cameraservice_test/cameraservice_test --gtest_filter=DepthProcessorTest.* Change-Id: I5cdd41c89368a1841d53f2195790aa1b55258495 --- .../api2/DepthCompositeStream.cpp | 15 ++ .../common/DepthPhotoProcessor.cpp | 158 ++++++++++++++---- .../common/DepthPhotoProcessor.h | 35 ++-- .../tests/DepthProcessorTest.cpp | 132 +++++++++++++-- .../libcameraservice/tests/NV12Compressor.cpp | 32 +++- .../libcameraservice/tests/NV12Compressor.h | 25 ++- 6 files changed, 335 insertions(+), 62 deletions(-) diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index 2eec0f7f9c..9525ad2635 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -339,6 +339,21 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { } else { depthPhoto.mIsLensDistortionValid = 0; } + entry = inputFrame.result.find(ANDROID_JPEG_ORIENTATION); + if (entry.count > 0) { + // The camera jpeg orientation values must be within [0, 90, 180, 270]. + switch (entry.data.i32[0]) { + case 0: + case 90: + case 180: + case 270: + depthPhoto.mOrientation = static_cast (entry.data.i32[0]); + break; + default: + ALOGE("%s: Unexpected jpeg orientation value: %d, default to 0 degrees", + __FUNCTION__, entry.data.i32[0]); + } + } size_t actualJpegSize = 0; res = mDepthPhotoProcess(depthPhoto, finalJpegBufferSize, dstBuffer, &actualJpegSize); diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp index 3af422064f..6d96163b42 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -224,8 +224,106 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out return ret; } +inline void unpackDepth16(uint16_t value, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + // Android densely packed depth map. The units for the range are in + // millimeters and need to be scaled to meters. + // The confidence value is encoded in the 3 most significant bits. + // The confidence data needs to be additionally normalized with + // values 1.0f, 0.0f representing maximum and minimum confidence + // respectively. + auto point = static_cast(value & 0x1FFF) / 1000.f; + points->push_back(point); + + auto conf = (value >> 13) & 0x7; + float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; + confidence->push_back(normConfidence); + + if (*near > point) { + *near = point; + } + if (*far < point) { + *far = point; + } +} + +// Trivial case, read forward from top,left corner. +void rotate0AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) { + for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) { + unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points, + confidence, near, far); + } + } +} + +// 90 degrees CW rotation can be applied by starting to read from bottom, left corner +// transposing rows and columns. +void rotate90AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (size_t i = 0; i < inputFrame.mDepthMapWidth; i++) { + for (ssize_t j = inputFrame.mDepthMapHeight-1; j >= 0; j--) { + unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points, + confidence, near, far); + } + } +} + +// 180 CW degrees rotation can be applied by starting to read backwards from bottom, right corner. +void rotate180AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (ssize_t i = inputFrame.mDepthMapHeight-1; i >= 0; i--) { + for (ssize_t j = inputFrame.mDepthMapWidth-1; j >= 0; j--) { + unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points, + confidence, near, far); + } + } +} + +// 270 degrees CW rotation can be applied by starting to read from top, right corner +// transposing rows and columns. +void rotate270AndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + for (ssize_t i = inputFrame.mDepthMapWidth-1; i >= 0; i--) { + for (size_t j = 0; j < inputFrame.mDepthMapHeight; j++) { + unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points, + confidence, near, far); + } + } +} + +bool rotateAndUnpack(DepthPhotoInputFrame inputFrame, std::vector *points /*out*/, + std::vector *confidence /*out*/, float *near /*out*/, float *far /*out*/) { + switch (inputFrame.mOrientation) { + case DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES: + rotate0AndUnpack(inputFrame, points, confidence, near, far); + return false; + case DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES: + rotate90AndUnpack(inputFrame, points, confidence, near, far); + return true; + case DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES: + rotate180AndUnpack(inputFrame, points, confidence, near, far); + return false; + case DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES: + rotate270AndUnpack(inputFrame, points, confidence, near, far); + return true; + default: + ALOGE("%s: Unsupported depth photo rotation: %d, default to 0", __FUNCTION__, + inputFrame.mOrientation); + rotate0AndUnpack(inputFrame, points, confidence, near, far); + } + + return false; +} + std::unique_ptr processDepthMapFrame(DepthPhotoInputFrame inputFrame, - ExifOrientation exifOrientation, std::vector> *items /*out*/) { + ExifOrientation exifOrientation, std::vector> *items /*out*/, + bool *switchDimensions /*out*/) { + if ((items == nullptr) || (switchDimensions == nullptr)) { + return nullptr; + } + std::vector points, confidence; size_t pointCount = inputFrame.mDepthMapWidth * inputFrame.mDepthMapHeight; @@ -233,29 +331,21 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra confidence.reserve(pointCount); float near = UINT16_MAX; float far = .0f; - for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) { - for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) { - // Android densely packed depth map. The units for the range are in - // millimeters and need to be scaled to meters. - // The confidence value is encoded in the 3 most significant bits. - // The confidence data needs to be additionally normalized with - // values 1.0f, 0.0f representing maximum and minimum confidence - // respectively. - auto value = inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j]; - auto point = static_cast(value & 0x1FFF) / 1000.f; - points.push_back(point); - - auto conf = (value >> 13) & 0x7; - float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; - confidence.push_back(normConfidence); - - if (near > point) { - near = point; - } - if (far < point) { - far = point; - } - } + *switchDimensions = false; + // Physical rotation of depth and confidence maps may be needed in case + // the EXIF orientation is set to 0 degrees and the depth photo orientation + // (source color image) has some different value. + if (exifOrientation == ExifOrientation::ORIENTATION_0_DEGREES) { + *switchDimensions = rotateAndUnpack(inputFrame, &points, &confidence, &near, &far); + } else { + rotate0AndUnpack(inputFrame, &points, &confidence, &near, &far); + } + + size_t width = inputFrame.mDepthMapWidth; + size_t height = inputFrame.mDepthMapHeight; + if (*switchDimensions) { + width = inputFrame.mDepthMapHeight; + height = inputFrame.mDepthMapWidth; } if (near == far) { @@ -281,8 +371,8 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra depthParams.depth_image_data.resize(inputFrame.mMaxJpegSize); depthParams.confidence_data.resize(inputFrame.mMaxJpegSize); size_t actualJpegSize; - auto ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, - pointsQuantized.data(), depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, + auto ret = encodeGrayscaleJpeg(width, height, pointsQuantized.data(), + depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize, inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Depth map compression failed!", __FUNCTION__); @@ -290,8 +380,8 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra } depthParams.depth_image_data.resize(actualJpegSize); - ret = encodeGrayscaleJpeg(inputFrame.mDepthMapWidth, inputFrame.mDepthMapHeight, - confidenceQuantized.data(), depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, + ret = encodeGrayscaleJpeg(width, height, confidenceQuantized.data(), + depthParams.confidence_data.data(), inputFrame.mMaxJpegSize, inputFrame.mJpegQuality, exifOrientation, actualJpegSize); if (ret != NO_ERROR) { ALOGE("%s: Confidence map compression failed!", __FUNCTION__); @@ -321,7 +411,9 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de ExifOrientation exifOrientation = getExifOrientation( reinterpret_cast (inputFrame.mMainJpegBuffer), inputFrame.mMainJpegSize); - cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items); + bool switchDimensions; + cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items, + &switchDimensions); if (cameraParams->depth_map == nullptr) { ALOGE("%s: Depth map processing failed!", __FUNCTION__); return BAD_VALUE; @@ -333,7 +425,13 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] const dynamic_depth::Point focalLength(inputFrame.mInstrinsicCalibration[0], inputFrame.mInstrinsicCalibration[1]); - const Dimension imageSize(inputFrame.mMainJpegWidth, inputFrame.mMainJpegHeight); + size_t width = inputFrame.mMainJpegWidth; + size_t height = inputFrame.mMainJpegHeight; + if (switchDimensions) { + width = inputFrame.mMainJpegHeight; + height = inputFrame.mMainJpegWidth; + } + const Dimension imageSize(width, height); ImagingModelParams imagingParams(focalLength, imageSize); imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2]; imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3]; diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h index 19889a1f3d..6a2fbffb6b 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h @@ -23,19 +23,27 @@ namespace android { namespace camera3 { +enum DepthPhotoOrientation { + DEPTH_ORIENTATION_0_DEGREES = 0, + DEPTH_ORIENTATION_90_DEGREES = 90, + DEPTH_ORIENTATION_180_DEGREES = 180, + DEPTH_ORIENTATION_270_DEGREES = 270, +}; + struct DepthPhotoInputFrame { - const char* mMainJpegBuffer; - size_t mMainJpegSize; - size_t mMainJpegWidth, mMainJpegHeight; - uint16_t* mDepthMapBuffer; - size_t mDepthMapWidth, mDepthMapHeight, mDepthMapStride; - size_t mMaxJpegSize; - uint8_t mJpegQuality; - uint8_t mIsLogical; - float mInstrinsicCalibration[5]; - uint8_t mIsInstrinsicCalibrationValid; - float mLensDistortion[5]; - uint8_t mIsLensDistortionValid; + const char* mMainJpegBuffer; + size_t mMainJpegSize; + size_t mMainJpegWidth, mMainJpegHeight; + uint16_t* mDepthMapBuffer; + size_t mDepthMapWidth, mDepthMapHeight, mDepthMapStride; + size_t mMaxJpegSize; + uint8_t mJpegQuality; + uint8_t mIsLogical; + float mInstrinsicCalibration[5]; + uint8_t mIsInstrinsicCalibrationValid; + float mLensDistortion[5]; + uint8_t mIsLensDistortionValid; + DepthPhotoOrientation mOrientation; DepthPhotoInputFrame() : mMainJpegBuffer(nullptr), @@ -52,7 +60,8 @@ struct DepthPhotoInputFrame { mInstrinsicCalibration{0.f}, mIsInstrinsicCalibrationValid(0), mLensDistortion{0.f}, - mIsLensDistortionValid(0) {} + mIsLensDistortionValid(0), + mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {} }; static const char *kDepthPhotoLibrary = "libdepthphoto.so"; diff --git a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp index 9898122e9d..2162514d14 100644 --- a/services/camera/libcameraservice/tests/DepthProcessorTest.cpp +++ b/services/camera/libcameraservice/tests/DepthProcessorTest.cpp @@ -50,7 +50,7 @@ void linkToDepthPhotoLibrary(void **libHandle /*out*/, } void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, bool includeExif, - std::vector *colorJpegBuffer /*out*/) { + bool switchDimensions, std::vector *colorJpegBuffer /*out*/) { ASSERT_NE(colorJpegBuffer, nullptr); std::array colorSourceBuffer; @@ -59,15 +59,23 @@ void generateColorJpegBuffer(int jpegQuality, ExifOrientation orientationValue, for (size_t i = 0; i < colorSourceBuffer.size(); i++) { colorSourceBuffer[i] = uniDist(gen); } + + size_t width = kTestBufferWidth; + size_t height = kTestBufferHeight; + if (switchDimensions) { + width = kTestBufferHeight; + height = kTestBufferWidth; + } + NV12Compressor jpegCompressor; if (includeExif) { ASSERT_TRUE(jpegCompressor.compressWithExifOrientation( - reinterpret_cast (colorSourceBuffer.data()), - kTestBufferWidth, kTestBufferHeight, jpegQuality, orientationValue)); + reinterpret_cast (colorSourceBuffer.data()), width, height, + jpegQuality, orientationValue)); } else { ASSERT_TRUE(jpegCompressor.compress( - reinterpret_cast (colorSourceBuffer.data()), - kTestBufferWidth, kTestBufferHeight, jpegQuality)); + reinterpret_cast (colorSourceBuffer.data()), width, height, + jpegQuality)); } *colorJpegBuffer = std::move(jpegCompressor.getCompressedData()); @@ -109,7 +117,7 @@ TEST(DepthProcessorTest, BadInput) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, - /*includeExif*/ false, &colorJpegBuffer); + /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); @@ -153,7 +161,7 @@ TEST(DepthProcessorTest, BasicDepthPhotoValidation) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, ExifOrientation::ORIENTATION_UNDEFINED, - /*includeExif*/ false, &colorJpegBuffer); + /*includeExif*/ false, /*switchDimensions*/ false, &colorJpegBuffer); std::array depth16Buffer; generateDepth16Buffer(&depth16Buffer); @@ -209,12 +217,11 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { for (auto exifOrientation : exifOrientations) { std::vector colorJpegBuffer; generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, - &colorJpegBuffer); + /*switchDimensions*/ false, &colorJpegBuffer); if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; - ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (colorJpegBuffer.data()), - colorJpegBuffer.size(), &jpegExifOrientation), OK); + ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), + colorJpegBuffer.size(), &jpegExifOrientation), OK); ASSERT_EQ(exifOrientation, jpegExifOrientation); } @@ -252,8 +259,7 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { //Depth and confidence images must have the same EXIF orientation as the source auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; - ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize), + ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, depthMapSize, &depthJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the @@ -265,8 +271,8 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; ASSERT_EQ(NV12Compressor::getExifOrientation( - reinterpret_cast (depthPhotoBuffer.data() + mainJpegSize + - depthMapSize), confidenceMapSize, &confidenceJpegExifOrientation), OK); + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, + confidenceMapSize, &confidenceJpegExifOrientation), OK); if (exifOrientation == ORIENTATION_UNDEFINED) { // In case of undefined or missing EXIF orientation, always expect 0 degrees in the // confidence map. @@ -278,3 +284,99 @@ TEST(DepthProcessorTest, TestDepthPhotoExifOrientation) { dlclose(libHandle); } + +TEST(DepthProcessorTest, TestDephtPhotoPhysicalRotation) { + void *libHandle; + int jpegQuality = 95; + + process_depth_photo_frame processFunc; + linkToDepthPhotoLibrary(&libHandle, &processFunc); + if (libHandle == nullptr) { + // Depth library no present, nothing more to test. + return; + } + + // In case of physical rotation, the EXIF orientation must always be 0. + auto exifOrientation = ExifOrientation::ORIENTATION_0_DEGREES; + DepthPhotoOrientation depthOrientations[] = { + DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES, + DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES }; + for (auto depthOrientation : depthOrientations) { + std::vector colorJpegBuffer; + bool switchDimensions = false; + size_t expectedWidth = kTestBufferWidth; + size_t expectedHeight = kTestBufferHeight; + if ((depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES) || + (depthOrientation == DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES)) { + switchDimensions = true; + expectedWidth = kTestBufferHeight; + expectedHeight = kTestBufferWidth; + } + generateColorJpegBuffer(jpegQuality, exifOrientation, /*includeExif*/ true, + switchDimensions, &colorJpegBuffer); + auto jpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation(colorJpegBuffer.data(), colorJpegBuffer.size(), + &jpegExifOrientation), OK); + ASSERT_EQ(exifOrientation, jpegExifOrientation); + + std::array depth16Buffer; + generateDepth16Buffer(&depth16Buffer); + + DepthPhotoInputFrame inputFrame; + inputFrame.mMainJpegBuffer = reinterpret_cast (colorJpegBuffer.data()); + inputFrame.mMainJpegSize = colorJpegBuffer.size(); + // Worst case both depth and confidence maps have the same size as the main color image. + inputFrame.mMaxJpegSize = inputFrame.mMainJpegSize * 3; + inputFrame.mMainJpegWidth = kTestBufferWidth; + inputFrame.mMainJpegHeight = kTestBufferHeight; + inputFrame.mJpegQuality = jpegQuality; + inputFrame.mDepthMapBuffer = depth16Buffer.data(); + inputFrame.mDepthMapWidth = inputFrame.mDepthMapStride = kTestBufferWidth; + inputFrame.mDepthMapHeight = kTestBufferHeight; + inputFrame.mOrientation = depthOrientation; + + std::vector depthPhotoBuffer(inputFrame.mMaxJpegSize); + size_t actualDepthPhotoSize = 0; + ASSERT_EQ(processFunc(inputFrame, depthPhotoBuffer.size(), depthPhotoBuffer.data(), + &actualDepthPhotoSize), 0); + ASSERT_TRUE((actualDepthPhotoSize > 0) && + (depthPhotoBuffer.size() >= actualDepthPhotoSize)); + + size_t mainJpegSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data(), actualDepthPhotoSize, + &mainJpegSize), OK); + ASSERT_TRUE((mainJpegSize > 0) && (mainJpegSize < actualDepthPhotoSize)); + size_t depthMapSize = 0; + ASSERT_EQ(NV12Compressor::findJpegSize(depthPhotoBuffer.data() + mainJpegSize, + actualDepthPhotoSize - mainJpegSize, &depthMapSize), OK); + ASSERT_TRUE((depthMapSize > 0) && (depthMapSize < (actualDepthPhotoSize - mainJpegSize))); + size_t confidenceMapSize = actualDepthPhotoSize - (mainJpegSize + depthMapSize); + + //Depth and confidence images must have the same EXIF orientation as the source + auto depthJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation(depthPhotoBuffer.data() + mainJpegSize, + depthMapSize, &depthJpegExifOrientation), OK); + ASSERT_EQ(depthJpegExifOrientation, exifOrientation); + size_t depthMapWidth, depthMapHeight; + ASSERT_EQ(NV12Compressor::getJpegImageDimensions(depthPhotoBuffer.data() + mainJpegSize, + depthMapSize, &depthMapWidth, &depthMapHeight), OK); + ASSERT_EQ(depthMapWidth, expectedWidth); + ASSERT_EQ(depthMapHeight, expectedHeight); + + auto confidenceJpegExifOrientation = ExifOrientation::ORIENTATION_UNDEFINED; + ASSERT_EQ(NV12Compressor::getExifOrientation( + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, + &confidenceJpegExifOrientation), OK); + ASSERT_EQ(confidenceJpegExifOrientation, exifOrientation); + size_t confidenceMapWidth, confidenceMapHeight; + ASSERT_EQ(NV12Compressor::getJpegImageDimensions( + depthPhotoBuffer.data() + mainJpegSize + depthMapSize, confidenceMapSize, + &confidenceMapWidth, &confidenceMapHeight), OK); + ASSERT_EQ(confidenceMapWidth, expectedWidth); + ASSERT_EQ(confidenceMapHeight, expectedHeight); + } + + dlclose(libHandle); +} diff --git a/services/camera/libcameraservice/tests/NV12Compressor.cpp b/services/camera/libcameraservice/tests/NV12Compressor.cpp index b9f27faa57..0a41a1ff82 100644 --- a/services/camera/libcameraservice/tests/NV12Compressor.cpp +++ b/services/camera/libcameraservice/tests/NV12Compressor.cpp @@ -315,7 +315,37 @@ status_t NV12Compressor::findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_ return OK; } -status_t NV12Compressor::getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize, +status_t NV12Compressor::getJpegImageDimensions(uint8_t *jpegBuffer, + size_t jpegBufferSize, size_t *width /*out*/, size_t *height /*out*/) { + if ((jpegBuffer == nullptr) || (width == nullptr) || (height == nullptr) || + (jpegBufferSize == 0u)) { + return BAD_VALUE; + } + + // Scan JPEG buffer until Start of Frame + bool foundSOF = false; + size_t currentPos; + for (currentPos = 0; currentPos <= jpegBufferSize - kMarkerLength; currentPos++) { + if (checkStartOfFrame(jpegBuffer + currentPos)) { + foundSOF = true; + currentPos += kMarkerLength; + break; + } + } + + if (!foundSOF) { + ALOGE("%s: Start of Frame not found", __func__); + return BAD_VALUE; + } + + sof_t *startOfFrame = reinterpret_cast (jpegBuffer + currentPos); + *width = ntohs(startOfFrame->width); + *height = ntohs(startOfFrame->height); + + return OK; +} + +status_t NV12Compressor::getExifOrientation(uint8_t *jpegBuffer, size_t jpegBufferSize, ExifOrientation *exifValue /*out*/) { if ((jpegBuffer == nullptr) || (exifValue == nullptr) || (jpegBufferSize == 0u)) { return BAD_VALUE; diff --git a/services/camera/libcameraservice/tests/NV12Compressor.h b/services/camera/libcameraservice/tests/NV12Compressor.h index 92804c10af..ee22d5e282 100644 --- a/services/camera/libcameraservice/tests/NV12Compressor.h +++ b/services/camera/libcameraservice/tests/NV12Compressor.h @@ -48,13 +48,20 @@ public: */ const std::vector& getCompressedData() const; + // Utility methods static android::status_t findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_t *size /*out*/); - - static android::status_t getExifOrientation(const unsigned char *jpegBuffer, + static android::status_t getExifOrientation(uint8_t *jpegBuffer, size_t jpegBufferSize, android::camera3::ExifOrientation *exifValue /*out*/); + /* Get Jpeg image dimensions from the first Start Of Frame. Please note that due to the + * way the jpeg buffer is scanned if the image contains a thumbnail, then the size returned + * will be of the thumbnail and not the main image. + */ + static android::status_t getJpegImageDimensions(uint8_t *jpegBuffer, size_t jpegBufferSize, + size_t *width /*out*/, size_t *height /*out*/); + private: struct DestinationManager : jpeg_destination_mgr { @@ -79,14 +86,26 @@ private: static const uint8_t kMarker = 0xFF; // First byte of marker static const uint8_t kStartOfImage = 0xD8; // Start of Image static const uint8_t kEndOfImage = 0xD9; // End of Image + static const uint8_t kStartOfFrame = 0xC0; // Start of Frame struct __attribute__((packed)) segment_t { uint8_t marker[kMarkerLength]; uint16_t length; }; + struct __attribute__((packed)) sof_t { + uint16_t length; + uint8_t precision; + uint16_t height; + uint16_t width; + }; + + // check for start of image marker + static bool checkStartOfFrame(uint8_t* buf) { + return buf[0] == kMarker && buf[1] == kStartOfFrame; + } - // check for Start of Image marker + // check for start of image marker static bool checkJpegStart(uint8_t* buf) { return buf[0] == kMarker && buf[1] == kStartOfImage; } -- GitLab From f71ca3b13526e6fb524970edea8a8d81e93000c2 Mon Sep 17 00:00:00 2001 From: lubiny Date: Thu, 9 Oct 2014 07:25:32 -0400 Subject: [PATCH 0990/1530] rtsp: Limit subframe processing to available buffer range With some RTSP servers, the mpeg4 audio subframe count is inconsistent with the available data. When attempting to process subframes that would exceed that available buffer boundaries, this causes a memory access violation. Add buffer size checks to avoid this failure CRs-Fixed: 2241161 Bug: 121339589 Change-Id: I030c6f02c478e216c20d9b24fbd6b3a9c1530889 --- media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index 82a0631fc6..4302aee6a0 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -423,6 +423,11 @@ sp AMPEG4AudioAssembler::removeLATMFraming(const sp &buffer) { CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size()); offset += mOtherDataLenBits / 8; } + + if (i < mNumSubFrames && offset >= buffer->size()) { + ALOGW("Skip subframes after %d, total %d", (int)i, (int)mNumSubFrames); + break; + } } if (offset < buffer->size()) { -- GitLab From 1c87855ff4c3c55e191a2e6da66bb82011c91c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 22 Nov 2018 16:53:21 +0100 Subject: [PATCH 0991/1530] audiopolicy: rework stream activity for volume management on output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Manage duplicated output ref count in client itself -Use VolumeSource to track volume activity in output (today mapped on legacy stream type) -Use accessor APIs to control activity/mute from apm. Test: audio smoke tests Bug: 124767636 Change-Id: I452e3973f6869d41231d984896a5886ebb86afb1 Signed-off-by: François Gaffie --- services/audiopolicy/common/include/Volume.h | 16 ++ .../include/AudioOutputDescriptor.h | 183 ++++++++++---- .../include/ClientDescriptor.h | 40 +++- .../src/AudioOutputDescriptor.cpp | 225 ++++++------------ .../src/ClientDescriptor.cpp | 7 +- .../engineconfigurable/src/Engine.cpp | 12 +- .../audiopolicy/enginedefault/src/Engine.cpp | 18 +- .../managerdefault/AudioPolicyManager.cpp | 84 ++++--- 8 files changed, 325 insertions(+), 260 deletions(-) diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index 5ccc8fdf2e..a3b6b36fd2 100644 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -20,6 +20,22 @@ #include #include +namespace android { +/** + * VolumeSource is the discriminent for volume management on an output. + * It used to be the stream type by legacy, it may be host volume group or a volume curves if + * we allow to have more than one curve per volume group. + */ +enum VolumeSource : std::underlying_type::type; +static const VolumeSource VOLUME_SOURCE_NONE = static_cast(AUDIO_STREAM_DEFAULT); + +static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) { + return static_cast(stream); +} + + +} // namespace android + // Absolute min volume in dB (can be represented in single precision normal float value) #define VOLUME_MIN_DB (-758) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index c84636e7e6..cf9519bcf1 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -73,7 +73,7 @@ public: virtual void dump(String8 *dst, int spaces) const { - dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 " \n", spaces, "", + dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 ", ", spaces, "", getActivityCount(), getStopTime()); } private: @@ -81,6 +81,37 @@ private: nsecs_t mStopTime = 0; }; +/** + * @brief VolumeActivity: it tracks the activity for volume policy (volume index, mute, + * memorize previous stop, and store mute if incompatible device with another strategy. + */ +class VolumeActivity : public ActivityTracking +{ +public: + bool isMuted() const { return mMuteCount > 0; } + int getMuteCount() const { return mMuteCount; } + int incMuteCount() { return ++mMuteCount; } + int decMuteCount() { return mMuteCount > 0 ? --mMuteCount : -1; } + + void dump(String8 *dst, int spaces) const override + { + ActivityTracking::dump(dst, spaces); + dst->appendFormat(", Volume: %.03f, MuteCount: %02d\n", mCurVolumeDb, mMuteCount); + } + void setVolume(float volume) { mCurVolumeDb = volume; } + float getVolume() const { return mCurVolumeDb; } + +private: + int mMuteCount = 0; /**< mute request counter */ + float mCurVolumeDb = NAN; /**< current volume in dB. */ +}; +/** + * Note: volume activities shall be indexed by CurvesId if we want to allow multiple + * curves per volume group, inferring a mute management or volume balancing between HW and SW is + * done + */ +using VolumeActivities = std::map; + /** * @brief The Activity class: it tracks the activity for volume policy (volume index, mute, * memorize previous stop, and store mute if incompatible device with another strategy. @@ -92,6 +123,10 @@ public: void setMutedByDevice( bool isMuted) { mIsMutedByDevice = isMuted; } bool isMutedByDevice() const { return mIsMutedByDevice; } + void dump(String8 *dst, int spaces) const override { + ActivityTracking::dump(dst, spaces); + dst->appendFormat("\n"); + } private: /** * strategies muted because of incompatible device selection. @@ -127,15 +162,6 @@ public: uint32_t delayMs, bool force); - /** - * Changes the stream active count and mActiveClients only. - * This does not change the client->active() state or the output descriptor's - * global active count. - */ - virtual void changeStreamActiveCount(const sp& client, int delta); - uint32_t streamActiveCount(audio_stream_type_t stream) const - { return mActiveCount[stream]; } - /** * @brief setStopTime set the stop time due to the client stoppage or a re routing of this * client @@ -148,13 +174,61 @@ public: * Changes the client->active() state and the output descriptor's global active count, * along with the stream active count and mActiveClients. * The client must be previously added by the base class addClient(). + * In case of duplicating thread, client shall be added on the duplicated thread, not on the + * involved outputs but setClientActive will be called on all output to track strategy and + * active client for a given output. + * Active ref count of the client will be incremented/decremented through setActive API */ - void setClientActive(const sp& client, bool active); - - bool isActive(uint32_t inPastMs = 0) const; - bool isStreamActive(audio_stream_type_t stream, - uint32_t inPastMs = 0, - nsecs_t sysTime = 0) const; + virtual void setClientActive(const sp& client, bool active); + + bool isActive(uint32_t inPastMs) const; + bool isActive(VolumeSource volumeSource = VOLUME_SOURCE_NONE, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + bool isAnyActive(VolumeSource volumeSourceToIgnore) const; + + std::vector getActiveVolumeSources() const { + std::vector activeList; + for (const auto &iter : mVolumeActivities) { + if (iter.second.isActive()) { + activeList.push_back(iter.first); + } + } + return activeList; + } + uint32_t getActivityCount(VolumeSource vs) const + { + return mVolumeActivities.find(vs) != std::end(mVolumeActivities)? + mVolumeActivities.at(vs).getActivityCount() : 0; + } + bool isMuted(VolumeSource vs) const + { + return mVolumeActivities.find(vs) != std::end(mVolumeActivities)? + mVolumeActivities.at(vs).isMuted() : false; + } + int getMuteCount(VolumeSource vs) const + { + return mVolumeActivities.find(vs) != std::end(mVolumeActivities)? + mVolumeActivities.at(vs).getMuteCount() : 0; + } + int incMuteCount(VolumeSource vs) + { + return mVolumeActivities[vs].incMuteCount(); + } + int decMuteCount(VolumeSource vs) + { + return mVolumeActivities[vs].decMuteCount(); + } + void setCurVolume(VolumeSource vs, float volume) + { + // Even if not activity for this group registered, need to create anyway + mVolumeActivities[vs].setVolume(volume); + } + float getCurVolume(VolumeSource vs) const + { + return mVolumeActivities.find(vs) != std::end(mVolumeActivities) ? + mVolumeActivities.at(vs).getVolume() : NAN; + } bool isStrategyActive(product_strategy_t ps, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const { @@ -195,40 +269,36 @@ public: // it is possible that when a client is removed, we could remove its // associated active count by calling changeStreamActiveCount(), // but that would be hiding a problem, so we log fatal instead. - auto it2 = mActiveClients.find(client); - LOG_ALWAYS_FATAL_IF(it2 != mActiveClients.end(), - "%s(%d) removing client portId %d which is active (count %zu)", - __func__, mId, portId, it2->second); + auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client); + LOG_ALWAYS_FATAL_IF(clientIter != mActiveClients.end(), + "%s(%d) removing client portId %d which is active (count %d)", + __func__, mId, portId, client->getActivityCount()); ClientMapHandler::removeClient(portId); } - using ActiveClientMap = std::map, size_t /* count */>; - // required for duplicating thread - const ActiveClientMap& getActiveClients() const { + const TrackClientVector& getActiveClients() const { return mActiveClients; } DeviceVector mDevices; /**< current devices this output is routed to */ - nsecs_t mStopTime[AUDIO_STREAM_CNT]; - int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy protected: const sp mPort; AudioPolicyClientInterface * const mClientInterface; - float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB - uint32_t mActiveCount[AUDIO_STREAM_CNT]; // number of streams of each type active on this output uint32_t mGlobalActiveCount = 0; // non-client-specific active count audio_patch_handle_t mPatchHandle = AUDIO_PATCH_HANDLE_NONE; audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE; - // The ActiveClientMap shows the clients that contribute to the streams counts + // The ActiveClients shows the clients that contribute to the @VolumeSource counts // and may include upstream clients from a duplicating thread. // Compare with the ClientMap (mClients) which are external AudioTrack clients of the // output descriptor (and do not count internal PatchTracks). - ActiveClientMap mActiveClients; + TrackClientVector mActiveClients; RoutingActivities mRoutingActivities; /**< track routing activity on this ouput.*/ + + VolumeActivities mVolumeActivities; /**< track volume activity on this ouput.*/ }; // Audio output driven by a software mixer in audio flinger. @@ -250,8 +320,13 @@ public: virtual bool isFixedVolume(audio_devices_t device); sp subOutput1() { return mOutput1; } sp subOutput2() { return mOutput2; } - void changeStreamActiveCount( - const sp& client, int delta) override; + void setClientActive(const sp& client, bool active) override; + void setAllClientsInactive() + { + for (const auto &client : clientsList(true)) { + setClientActive(client, false); + } + } virtual bool setVolume(float volume, audio_stream_type_t stream, audio_devices_t device, @@ -344,25 +419,27 @@ class SwAudioOutputCollection : public DefaultKeyedVector< audio_io_handle_t, sp > { public: - bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const; /** - * return whether a stream is playing remotely, override to change the definition of + * return whether any source contributing to VolumeSource is playing remotely, override + * to change the definition of * local/remote playback, used for instance by notification manager to not make * media players lose audio focus when not playing locally * For the base implementation, "remotely" means playing during screen mirroring which * uses an output for playback with a non-empty, non "0" address. */ - bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + bool isActiveRemotely(VolumeSource volumeSource, uint32_t inPastMs = 0) const; /** - * return whether a stream is playing, but not on a "remote" device. + * return whether any source contributing to VolumeSource is playing, but not on a "remote" + * device. * Override to change the definition of a local/remote playback. * Used for instance by policy manager to alter the speaker playback ("speaker safe" behavior) * when media plays or not locally. * For the base implementation, "remotely" means playing during screen mirroring. */ - bool isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + bool isActiveLocally(VolumeSource volumeSource, uint32_t inPastMs = 0) const; /** * @brief isStrategyActiveOnSameModule checks if the given strategy is active (or was active @@ -409,9 +486,21 @@ public: sp getPrimaryOutput() const; /** - * return true if any output is playing anything besides the stream to ignore + * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that + * hold the volume source to be ignored + * @param volumeSourceToIgnore source not considered in the activity detection + * @return true if any output is active for any source except the one to be ignored */ - bool isAnyOutputActive(audio_stream_type_t streamToIgnore) const; + bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const + { + for (size_t i = 0; i < size(); i++) { + const sp &outputDesc = valueAt(i); + if (outputDesc->isAnyActive(volumeSourceToIgnore)) { + return true; + } + } + return false; + } audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; @@ -424,12 +513,24 @@ class HwAudioOutputCollection : public DefaultKeyedVector< audio_io_handle_t, sp > { public: - bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const; /** - * return true if any output is playing anything besides the stream to ignore + * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that + * hold the volume source to be ignored + * @param volumeSourceToIgnore source not considered in the activity detection + * @return true if any output is active for any source except the one to be ignored */ - bool isAnyOutputActive(audio_stream_type_t streamToIgnore) const; + bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const + { + for (size_t i = 0; i < size(); i++) { + const sp &outputDesc = valueAt(i); + if (outputDesc->isAnyActive(volumeSourceToIgnore)) { + return true; + } + } + return false; + } void dump(String8 *dst) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 2e44a60d9b..4bb225d7d3 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "AudioPatch.h" #include "EffectDescriptor.h" @@ -62,7 +63,7 @@ public: mPreferredDeviceId = preferredDeviceId; } bool isPreferredDeviceForExclusiveUse() const { return mPreferredDeviceForExclusiveUse; } - void setActive(bool active) { mActive = active; } + virtual void setActive(bool active) { mActive = active; } bool active() const { return mActive; } bool hasPreferredDevice(bool activeOnly = false) const { return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive); @@ -85,12 +86,13 @@ public: TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, audio_attributes_t attributes, audio_config_base_t config, audio_port_handle_t preferredDeviceId, audio_stream_type_t stream, - product_strategy_t strategy, audio_output_flags_t flags, + product_strategy_t strategy, VolumeSource volumeSource, + audio_output_flags_t flags, bool isPreferredDeviceForExclusiveUse, std::vector> secondaryOutputs) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId, isPreferredDeviceForExclusiveUse), - mStream(stream), mStrategy(strategy), mFlags(flags), + mStream(stream), mStrategy(strategy), mVolumeSource(volumeSource), mFlags(flags), mSecondaryOutputs(std::move(secondaryOutputs)) {} ~TrackClientDescriptor() override = default; @@ -104,12 +106,41 @@ public: const std::vector>& getSecondaryOutputs() const { return mSecondaryOutputs; }; + VolumeSource volumeSource() const { return mVolumeSource; } + + void setActive(bool active) override + { + int delta = active ? 1 : -1; + changeActivityCount(delta); + } + void changeActivityCount(int delta) + { + if (delta > 0) { + mActivityCount += delta; + } else { + LOG_ALWAYS_FATAL_IF(!mActivityCount, "%s(%s) invalid delta %d, inactive client", + __func__, toShortString().c_str(), delta); + LOG_ALWAYS_FATAL_IF(static_cast(mActivityCount) < -delta, + "%s(%s) invalid delta %d, active client count %d", + __func__, toShortString().c_str(), delta, mActivityCount); + mActivityCount += delta; + } + ClientDescriptor::setActive(mActivityCount > 0); + } + uint32_t getActivityCount() const { return mActivityCount; } private: const audio_stream_type_t mStream; const product_strategy_t mStrategy; + const VolumeSource mVolumeSource; const audio_output_flags_t mFlags; const std::vector> mSecondaryOutputs; + + /** + * required for duplicating thread, prevent from removing active client from an output + * involved in a duplication. + */ + uint32_t mActivityCount = 0; }; class RecordClientDescriptor: public ClientDescriptor @@ -148,7 +179,8 @@ class SourceClientDescriptor: public TrackClientDescriptor public: SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, const sp& srcDevice, - audio_stream_type_t stream, product_strategy_t strategy); + audio_stream_type_t stream, product_strategy_t strategy, + VolumeSource volumeSource); ~SourceClientDescriptor() override = default; sp patchDesc() const { return mPatchDesc; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 77e7add511..7293bc4143 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -34,16 +34,8 @@ namespace android { AudioOutputDescriptor::AudioOutputDescriptor(const sp& port, AudioPolicyClientInterface *clientInterface) - : mPort(port) - , mClientInterface(clientInterface) + : mPort(port), mClientInterface(clientInterface) { - // clear usage count for all stream types - for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - mActiveCount[i] = 0; - mCurVolume[i] = -1.0; - mMuteCount[i] = 0; - mStopTime[i] = 0; - } if (mPort.get() != nullptr) { mPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat); if (mPort->mGains.size() > 0) { @@ -85,124 +77,73 @@ bool AudioOutputDescriptor::sharesHwModuleWith( return hasSameHwModuleAs(outputDesc); } -void AudioOutputDescriptor::changeStreamActiveCount(const sp& client, - int delta) -{ - if (delta == 0) return; - const audio_stream_type_t stream = client->stream(); - if ((delta + (int)mActiveCount[stream]) < 0) { - // any mismatched active count will abort. - LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, active stream count %d", - __func__, client->toShortString().c_str(), delta, mActiveCount[stream]); - // mActiveCount[stream] = 0; - // return; - } - mActiveCount[stream] += delta; - mRoutingActivities[client->strategy()].changeActivityCount(delta); - - if (delta > 0) { - mActiveClients[client] += delta; - } else { - auto it = mActiveClients.find(client); - if (it == mActiveClients.end()) { // client not found! - LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, inactive client", - __func__, client->toShortString().c_str(), delta); - } else if (it->second < -delta) { // invalid delta! - LOG_ALWAYS_FATAL("%s(%s) invalid delta %d, active client count %zu", - __func__, client->toShortString().c_str(), delta, it->second); - } - it->second += delta; - if (it->second == 0) { - (void)mActiveClients.erase(it); - } - } - - ALOGV("%s stream %d, count %d", __FUNCTION__, stream, mActiveCount[stream]); -} - void AudioOutputDescriptor::setStopTime(const sp& client, nsecs_t sysTime) { - mStopTime[client->stream()] = sysTime; + mVolumeActivities[client->volumeSource()].setStopTime(sysTime); mRoutingActivities[client->strategy()].setStopTime(sysTime); } void AudioOutputDescriptor::setClientActive(const sp& client, bool active) { - LOG_ALWAYS_FATAL_IF(getClient(client->portId()) == nullptr, - "%s(%d) does not exist on output descriptor", __func__, client->portId()); - - if (active == client->active()) { - ALOGW("%s(%s): ignored active: %d, current stream count %d", - __func__, client->toShortString().c_str(), - active, mActiveCount[client->stream()]); + auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client); + if (active == (clientIter != end(mActiveClients))) { + ALOGW("%s(%s): ignored active: %d, current stream count %d", __func__, + client->toShortString().c_str(), active, + mRoutingActivities.at(client->strategy()).getActivityCount()); return; } + if (active) { + mActiveClients.push_back(client); + } else { + mActiveClients.erase(clientIter); + } const int delta = active ? 1 : -1; - changeStreamActiveCount(client, delta); + // If ps is unknown, it is time to track it! + mRoutingActivities[client->strategy()].changeActivityCount(delta); + mVolumeActivities[client->volumeSource()].changeActivityCount(delta); // Handle non-client-specific activity ref count int32_t oldGlobalActiveCount = mGlobalActiveCount; if (!active && mGlobalActiveCount < 1) { ALOGW("%s(%s): invalid deactivation with globalRefCount %d", - __func__, client->toShortString().c_str(), mGlobalActiveCount); + __func__, client->toShortString().c_str(), mGlobalActiveCount); mGlobalActiveCount = 1; } mGlobalActiveCount += delta; - if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) - { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + if ((oldGlobalActiveCount == 0) || (mGlobalActiveCount == 0)) { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, - MIX_STATE_MIXING); - } - } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) - { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, - MIX_STATE_IDLE); + mGlobalActiveCount > 0 ? MIX_STATE_MIXING : MIX_STATE_IDLE); } } - client->setActive(active); } +bool AudioOutputDescriptor::isActive(VolumeSource vs, uint32_t inPastMs, nsecs_t sysTime) const +{ + return (vs == VOLUME_SOURCE_NONE) ? + isActive(inPastMs) : (mVolumeActivities.find(vs) != std::end(mVolumeActivities)? + mVolumeActivities.at(vs).isActive(inPastMs, sysTime) : false); +} + bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const { nsecs_t sysTime = 0; if (inPastMs != 0) { sysTime = systemTime(); } - for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { - if (i == AUDIO_STREAM_PATCH) { + for (const auto &iter : mVolumeActivities) { + if (iter.first == streamToVolumeSource(AUDIO_STREAM_PATCH)) { continue; } - if (isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + if (iter.second.isActive(inPastMs, sysTime)) { return true; } } return false; } -bool AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, - uint32_t inPastMs, - nsecs_t sysTime) const -{ - if (mActiveCount[stream] != 0) { - return true; - } - if (inPastMs == 0) { - return false; - } - if (sysTime == 0) { - sysTime = systemTime(); - } - if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { - return true; - } - return false; -} - - bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused) { return false; @@ -217,9 +158,9 @@ bool AudioOutputDescriptor::setVolume(float volume, // We actually change the volume if: // - the float value returned by computeVolume() changed // - the force flag is set - if (volume != mCurVolume[stream] || force) { + if (volume != getCurVolume(static_cast(stream)) || force) { ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs); - mCurVolume[stream] = volume; + setCurVolume(static_cast(stream), volume); return true; } return false; @@ -266,6 +207,13 @@ TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, product_st return clients; } +bool AudioOutputDescriptor::isAnyActive(VolumeSource volumeSourceToIgnore) const +{ + return std::find_if(begin(mActiveClients), end(mActiveClients), + [&volumeSourceToIgnore](const auto &client) { + return client->volumeSource() != volumeSourceToIgnore; }) != end(mActiveClients); +} + void AudioOutputDescriptor::dump(String8 *dst) const { dst->appendFormat(" ID: %d\n", mId); @@ -274,20 +222,22 @@ void AudioOutputDescriptor::dump(String8 *dst) const dst->appendFormat(" Channels: %08x\n", mChannelMask); dst->appendFormat(" Devices: %s\n", devices().toString().c_str()); dst->appendFormat(" Global active count: %u\n", mGlobalActiveCount); - dst->append(" Stream volume activeCount muteCount\n"); - for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { - dst->appendFormat(" %02d %.03f %02d %02d\n", - i, mCurVolume[i], streamActiveCount((audio_stream_type_t)i), mMuteCount[i]); + for (const auto &iter : mRoutingActivities) { + dst->appendFormat(" Product Strategy id: %d", iter.first); + iter.second.dump(dst, 4); + } + for (const auto &iter : mVolumeActivities) { + dst->appendFormat(" Volume Activities id: %d", iter.first); + iter.second.dump(dst, 4); } dst->append(" AudioTrack Clients:\n"); ClientMapHandler::dump(dst); dst->append("\n"); - if (mActiveClients.size() > 0) { + if (!mActiveClients.empty()) { dst->append(" AudioTrack active (stream) clients:\n"); size_t index = 0; - for (const auto& clientPair : mActiveClients) { - dst->appendFormat(" Refcount: %zu", clientPair.second); - clientPair.first->dump(dst, 2, index++); + for (const auto& client : mActiveClients) { + client->dump(dst, 2, index++); } dst->append(" \n"); } @@ -388,15 +338,14 @@ uint32_t SwAudioOutputDescriptor::latency() } } -void SwAudioOutputDescriptor::changeStreamActiveCount(const sp& client, - int delta) +void SwAudioOutputDescriptor::setClientActive(const sp& client, bool active) { // forward usage count change to attached outputs if (isDuplicated()) { - mOutput1->changeStreamActiveCount(client, delta); - mOutput2->changeStreamActiveCount(client, delta); + mOutput1->setClientActive(client, active); + mOutput2->setClientActive(client, active); } - AudioOutputDescriptor::changeStreamActiveCount(client, delta); + AudioOutputDescriptor::setClientActive(client, active); } bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device) @@ -445,19 +394,16 @@ bool SwAudioOutputDescriptor::setVolume(float volume, uint32_t delayMs, bool force) { - bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force); - - if (changed) { - // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is - // enabled - float volume = Volume::DbToAmpl(mCurVolume[stream]); - if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { - mClientInterface->setStreamVolume( - AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs); - } - mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs); + if (!AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force)) { + return false; } - return changed; + // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled + float volumeAmpl = Volume::DbToAmpl(getCurVolume(static_cast(stream))); + if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { + mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs); + } + mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + return true; } status_t SwAudioOutputDescriptor::open(const audio_config_t *config, @@ -660,24 +606,24 @@ bool HwAudioOutputDescriptor::setVolume(float volume, } // SwAudioOutputCollection implementation -bool SwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +bool SwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < this->size(); i++) { const sp outputDesc = this->valueAt(i); - if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) { return true; } } return false; } -bool SwAudioOutputCollection::isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs) const +bool SwAudioOutputCollection::isActiveLocally(VolumeSource volumeSource, uint32_t inPastMs) const { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < this->size(); i++) { const sp outputDesc = this->valueAt(i); - if (outputDesc->isStreamActive(stream, inPastMs, sysTime) + if (outputDesc->isActive(volumeSource, inPastMs, sysTime) && ((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) == 0)) { return true; } @@ -685,14 +631,13 @@ bool SwAudioOutputCollection::isStreamActiveLocally(audio_stream_type_t stream, return false; } -bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream, - uint32_t inPastMs) const +bool SwAudioOutputCollection::isActiveRemotely(VolumeSource volumeSource, uint32_t inPastMs) const { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < size(); i++) { const sp outputDesc = valueAt(i); if (((outputDesc->devices().types() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && - outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + outputDesc->isActive(volumeSource, inPastMs, sysTime)) { // do not consider re routing (when the output is going to a dynamic policy) // as "remote playback" if (outputDesc->mPolicyMix == NULL) { @@ -775,22 +720,6 @@ sp SwAudioOutputCollection::getOutputFromId(audio_port_ return NULL; } -bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const -{ - for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) { - if (s == (size_t) streamToIgnore) { - continue; - } - for (size_t i = 0; i < size(); i++) { - const sp outputDesc = valueAt(i); - if (outputDesc->streamActiveCount((audio_stream_type_t)s)!= 0) { - return true; - } - } - } - return false; -} - sp SwAudioOutputCollection::getOutputForClient(audio_port_handle_t portId) { for (size_t i = 0; i < size(); i++) { @@ -825,34 +754,18 @@ void SwAudioOutputCollection::dump(String8 *dst) const } // HwAudioOutputCollection implementation -bool HwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +bool HwAudioOutputCollection::isActive(VolumeSource volumeSource, uint32_t inPastMs) const { nsecs_t sysTime = systemTime(); for (size_t i = 0; i < this->size(); i++) { const sp outputDesc = this->valueAt(i); - if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + if (outputDesc->isActive(volumeSource, inPastMs, sysTime)) { return true; } } return false; } -bool HwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const -{ - for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) { - if (s == (size_t) streamToIgnore) { - continue; - } - for (size_t i = 0; i < size(); i++) { - const sp outputDesc = valueAt(i); - if (outputDesc->streamActiveCount((audio_stream_type_t)s) != 0) { - return true; - } - } - } - return false; -} - void HwAudioOutputCollection::dump(String8 *dst) const { dst->append("\nOutputs dump:\n"); diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp index 633c40e343..ad07ab18cc 100644 --- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "AudioGain.h" #include "AudioOutputDescriptor.h" #include "AudioPatch.h" @@ -45,6 +46,7 @@ void ClientDescriptor::dump(String8 *dst, int spaces, int index) const mPortId, mSessionId, mUid); dst->appendFormat("%*s- Format: %08x Sampling rate: %d Channels: %08x\n", spaces, "", mConfig.format, mConfig.sample_rate, mConfig.channel_mask); + dst->appendFormat("%*s- Attributes: %s\n", spaces, "", toString(mAttributes).c_str()); dst->appendFormat("%*s- Preferred Device Id: %08x\n", spaces, "", mPreferredDeviceId); dst->appendFormat("%*s- State: %s\n", spaces, "", mActive ? "Active" : "Inactive"); } @@ -53,6 +55,7 @@ void TrackClientDescriptor::dump(String8 *dst, int spaces, int index) const { ClientDescriptor::dump(dst, spaces, index); dst->appendFormat("%*s- Stream: %d flags: %08x\n", spaces, "", mStream, mFlags); + dst->appendFormat("%*s- Refcount: %d\n", spaces, "", mActivityCount); } std::string TrackClientDescriptor::toShortString() const @@ -82,10 +85,10 @@ void RecordClientDescriptor::dump(String8 *dst, int spaces, int index) const SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes, const sp& patchDesc, const sp& srcDevice, audio_stream_type_t stream, - product_strategy_t strategy) : + product_strategy_t strategy, VolumeSource volumeSource) : TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes, AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, - stream, strategy, AUDIO_OUTPUT_FLAG_NONE, false, + stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false, {} /* Sources do not support secondary outputs*/), mPatchDesc(patchDesc), mSrcDevice(srcDevice) { diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index f486dcac50..719ef01031 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -224,16 +224,16 @@ DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const audio_devices_t devices = AUDIO_DEVICE_NONE; if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) && !is_state_in_call(getPhoneState()) && - !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, - SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && - outputs.isStreamActive(AUDIO_STREAM_MUSIC, - SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + !outputs.isActiveRemotely(streamToVolumeSource(AUDIO_STREAM_MUSIC), + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && + outputs.isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC), + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { product_strategy_t strategyForMedia = getProductStrategyForStream(AUDIO_STREAM_MUSIC); devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia); } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) && - (outputs.isStreamActive(AUDIO_STREAM_RING) || - outputs.isStreamActive(AUDIO_STREAM_ALARM))) { + (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) || + outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM)))) { // do not route accessibility prompts to a digital output currently configured with a // compressed format as they would likely not be mixed and dropped. // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable. diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 93af8a6725..43023a8784 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -182,16 +182,17 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, break; case STRATEGY_SONIFICATION_RESPECTFUL: - if (isInCall() || outputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL)) { + if (isInCall() || outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) { device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); } else { bool media_active_locally = - outputs.isStreamActiveLocally( - AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) - || outputs.isStreamActiveLocally( - AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY); + outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_MUSIC), + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) + || outputs.isActiveLocally( + streamToVolumeSource(AUDIO_STREAM_ACCESSIBILITY), + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY); // routing is same as media without the "remote" device device = getDeviceForStrategyInt(STRATEGY_MEDIA, availableOutputDevices, @@ -324,7 +325,8 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, case STRATEGY_SONIFICATION: // If incall, just select the STRATEGY_PHONE device - if (isInCall() || outputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL)) { + if (isInCall() || + outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) { device = getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); @@ -397,8 +399,8 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, } availableOutputDevices = availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType); - if (outputs.isStreamActive(AUDIO_STREAM_RING) || - outputs.isStreamActive(AUDIO_STREAM_ALARM)) { + if (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) || + outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM))) { return getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 32cc380719..3366141a98 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1078,6 +1078,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, sanitizedRequestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), + streamToVolumeSource(*stream), *flags, isRequestedDeviceForExclusiveUse, std::move(weakSecondaryOutputDescs)); sp outputDesc = mOutputs.valueFor(*output); @@ -1569,11 +1570,13 @@ status_t AudioPolicyManager::startSource(const sp& outp *delayMs = 0; audio_stream_type_t stream = client->stream(); + auto clientVolSrc = client->volumeSource(); auto clientStrategy = client->strategy(); auto clientAttr = client->attributes(); if (stream == AUDIO_STREAM_TTS) { ALOGV("\t found BEACON stream"); - if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { + if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive( + streamToVolumeSource(AUDIO_STREAM_TTS) /*sourceToIgnore*/)) { return INVALID_OPERATION; } else { beaconMuteLatency = handleEventForBeacon(STARTING_BEACON); @@ -1628,7 +1631,7 @@ status_t AudioPolicyManager::startSource(const sp& outp selectOutputForMusicEffects(); } - if (outputDesc->streamActiveCount(stream) == 1 || !devices.isEmpty()) { + if (outputDesc->getActivityCount(clientVolSrc) == 1 || !devices.isEmpty()) { // starting an output being rerouted? if (devices.isEmpty()) { devices = getNewOutputDevices(outputDesc, false /*fromCache*/); @@ -1754,11 +1757,12 @@ status_t AudioPolicyManager::stopSource(const sp& outpu { // always handle stream stop, check which stream type is stopping audio_stream_type_t stream = client->stream(); + auto clientVolSrc = client->volumeSource(); handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); - if (outputDesc->streamActiveCount(stream) > 0) { - if (outputDesc->streamActiveCount(stream) == 1) { + if (outputDesc->getActivityCount(clientVolSrc) > 0) { + if (outputDesc->getActivityCount(clientVolSrc) == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(outputDesc->devices().types()) && @@ -1780,7 +1784,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu outputDesc->setClientActive(client, false); // store time at which the stream was stopped - see isStreamActive() - if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) { + if (outputDesc->getActivityCount(clientVolSrc) == 0 || forceDeviceUpdate) { outputDesc->setStopTime(client, systemTime()); DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when @@ -2411,7 +2415,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) { continue; } - if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) { + if (!(desc->isActive(streamToVolumeSource((audio_stream_type_t)curStream)) || isInCall())) { continue; } audio_devices_t curStreamDevice = Volume::getDeviceForVolume( @@ -2499,7 +2503,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() for (audio_io_handle_t output : outputs) { sp desc = mOutputs.valueFor(output); - if (activeOnly && !desc->isStreamActive(AUDIO_STREAM_MUSIC)) { + if (activeOnly && !desc->isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC))) { continue; } ALOGV("selectOutputForMusicEffects activeOnly %d output %d flags 0x%08x", @@ -2593,14 +2597,14 @@ bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inP if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - active = mOutputs.isStreamActive((audio_stream_type_t)curStream, inPastMs); + active = mOutputs.isActive(streamToVolumeSource((audio_stream_type_t)curStream), inPastMs); } return active; } bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const { - return mOutputs.isStreamActiveRemotely(stream, inPastMs); + return mOutputs.isActiveRemotely(streamToVolumeSource((audio_stream_type_t)stream), inPastMs); } bool AudioPolicyManager::isSourceActive(audio_source_t source) const @@ -3630,10 +3634,11 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so struct audio_patch dummyPatch = {}; sp patchDesc = new AudioPatch(&dummyPatch, uid); - sp sourceDesc = - new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice, - mEngine->getStreamTypeForAttributes(*attributes), - mEngine->getProductStrategyForAttributes(*attributes)); + sp sourceDesc = new SourceClientDescriptor( + *portId, uid, *attributes, patchDesc, srcDevice, + mEngine->getStreamTypeForAttributes(*attributes), + mEngine->getProductStrategyForAttributes(*attributes), + streamToVolumeSource(mEngine->getStreamTypeForAttributes(*attributes))); status_t status = connectAudioSource(sourceDesc); if (status == NO_ERROR) { @@ -4704,37 +4709,31 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) { ALOGV("closeOutput(%d)", output); - sp outputDesc = mOutputs.valueFor(output); - if (outputDesc == NULL) { + sp closingOutput = mOutputs.valueFor(output); + if (closingOutput == NULL) { ALOGW("closeOutput() unknown output %d", output); return; } - mPolicyMixes.closeOutput(outputDesc); + mPolicyMixes.closeOutput(closingOutput); // look for duplicated outputs connected to the output being removed. for (size_t i = 0; i < mOutputs.size(); i++) { - sp dupOutputDesc = mOutputs.valueAt(i); - if (dupOutputDesc->isDuplicated() && - (dupOutputDesc->mOutput1 == outputDesc || - dupOutputDesc->mOutput2 == outputDesc)) { - sp outputDesc2; - if (dupOutputDesc->mOutput1 == outputDesc) { - outputDesc2 = dupOutputDesc->mOutput2; - } else { - outputDesc2 = dupOutputDesc->mOutput1; - } + sp dupOutput = mOutputs.valueAt(i); + if (dupOutput->isDuplicated() && + (dupOutput->mOutput1 == closingOutput || dupOutput->mOutput2 == closingOutput)) { + sp remainingOutput = + dupOutput->mOutput1 == closingOutput ? dupOutput->mOutput2 : dupOutput->mOutput1; // As all active tracks on duplicated output will be deleted, // and as they were also referenced on the other output, the reference // count for their stream type must be adjusted accordingly on // the other output. - const bool wasActive = outputDesc2->isActive(); - for (const auto &clientPair : dupOutputDesc->getActiveClients()) { - outputDesc2->changeStreamActiveCount(clientPair.first, -clientPair.second); - } + const bool wasActive = remainingOutput->isActive(); + // Note: no-op on the closing output where all clients has already been set inactive + dupOutput->setAllClientsInactive(); // stop() will be a no op if the output is still active but is needed in case all // active streams refcounts where cleared above if (wasActive) { - outputDesc2->stop(); + remainingOutput->stop(); } audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i); ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); @@ -4746,7 +4745,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) nextAudioPortGeneration(); - ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle()); + ssize_t index = mAudioPatches.indexOfKey(closingOutput->getPatchHandle()); if (index >= 0) { sp patchDesc = mAudioPatches.valueAt(index); (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); @@ -4754,7 +4753,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) mpClientInterface->onAudioPatchListUpdate(); } - outputDesc->close(); + closingOutput->close(); removeOutput(output); mPreviousOutputs = mOutputs; @@ -5105,7 +5104,7 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre devices.merge(curDevices); for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) { sp outputDesc = mOutputs.valueFor(output); - if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) { + if (outputDesc->isActive(streamToVolumeSource((audio_stream_type_t)curStream))) { activeDevices.merge(outputDesc->devices()); } } @@ -5519,7 +5518,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, // in-call: always cap volume by voice volume + some low headroom if ((stream != AUDIO_STREAM_VOICE_CALL) && - (isInCall() || mOutputs.isStreamActiveLocally(AUDIO_STREAM_VOICE_CALL))) { + (isInCall() || mOutputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL)))) { switch (stream) { case AUDIO_STREAM_SYSTEM: case AUDIO_STREAM_RING: @@ -5637,9 +5636,8 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, bool force) { // do not change actual stream volume if the stream is muted - if (outputDesc->mMuteCount[stream] != 0) { - ALOGVV("checkAndSetVolume() stream %d muted count %d", - stream, outputDesc->mMuteCount[stream]); + if (outputDesc->isMuted(streamToVolumeSource(stream))) { + ALOGVV("%s() stream %d muted count %d", __func__, stream, outputDesc->getMuteCount(stream)); return NO_ERROR; } audio_policy_forced_cfg_t forceUseForComm = @@ -5726,10 +5724,10 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", - stream, on, outputDesc->mMuteCount[stream], device); + stream, on, outputDesc->getMuteCount(stream), device); if (on) { - if (outputDesc->mMuteCount[stream] == 0) { + if (!outputDesc->isMuted(streamToVolumeSource(stream))) { if (mVolumeCurves->canBeMuted(stream) && ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { @@ -5737,13 +5735,13 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } } // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored - outputDesc->mMuteCount[stream]++; + outputDesc->incMuteCount(streamToVolumeSource(stream)); } else { - if (outputDesc->mMuteCount[stream] == 0) { + if (!outputDesc->isMuted(streamToVolumeSource(stream))) { ALOGV("setStreamMute() unmuting non muted stream!"); return; } - if (--outputDesc->mMuteCount[stream] == 0) { + if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { checkAndSetVolume(stream, mVolumeCurves->getVolumeIndex(stream, device), outputDesc, -- GitLab From 219c299fe663f6f1708448370f9ec854bb1af30e Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Fri, 15 Feb 2019 17:24:28 -0800 Subject: [PATCH 0992/1530] Camera: HEIC: Use libyuv utilities to speed up memcpy Use libyuv's optimized CopyRow function to improve performance of YUV tiling. Bug: 124781199 Test: Camera CTS Test: TestingCamera2 smoke test Change-Id: I6af6678099655b7e35ddaccf7cd9aa817ec64a9c --- services/camera/libcameraservice/Android.bp | 1 + .../api2/HeicCompositeStream.cpp | 45 ++++++++++++++++--- .../api2/HeicCompositeStream.h | 4 ++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 2ca83561a2..9ae7fd9fa4 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -94,6 +94,7 @@ cc_library_shared { "libsensorprivacy", "libstagefright", "libstagefright_foundation", + "libyuv", "android.frameworks.cameraservice.common@2.0", "android.frameworks.cameraservice.service@2.0", "android.frameworks.cameraservice.device@2.0", diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp index a61cdeeac9..9fd0e8bfbb 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -192,6 +193,7 @@ status_t HeicCompositeStream::createInternalStreams(const std::vector& codecBuffer, for (auto row = top; row < top+height; row++) { uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::Y].mOffset + imageInfo->mPlane[MediaImage2::Y].mRowInc * (row - top); - memcpy(dst, yuvBuffer.data+row*yuvBuffer.stride+left, width); + mFnCopyRow(yuvBuffer.data+row*yuvBuffer.stride+left, dst, width); } // U is Cb, V is Cr @@ -1406,24 +1408,25 @@ status_t HeicCompositeStream::copyOneYuvTile(sp& codecBuffer, for (auto row = top/2; row < (top+height)/2; row++) { uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[dstPlane].mOffset + imageInfo->mPlane[dstPlane].mRowInc * (row - top/2); - memcpy(dst, src+row*yuvBuffer.chromaStride+left, width); + mFnCopyRow(src+row*yuvBuffer.chromaStride+left, dst, width); } } else if (isCodecUvPlannar && yuvBuffer.chromaStep == 1) { // U plane for (auto row = top/2; row < (top+height)/2; row++) { uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::U].mOffset + imageInfo->mPlane[MediaImage2::U].mRowInc * (row - top/2); - memcpy(dst, yuvBuffer.dataCb+row*yuvBuffer.chromaStride+left/2, width/2); + mFnCopyRow(yuvBuffer.dataCb+row*yuvBuffer.chromaStride+left/2, dst, width/2); } // V plane for (auto row = top/2; row < (top+height)/2; row++) { uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::V].mOffset + imageInfo->mPlane[MediaImage2::V].mRowInc * (row - top/2); - memcpy(dst, yuvBuffer.dataCr+row*yuvBuffer.chromaStride+left/2, width/2); + mFnCopyRow(yuvBuffer.dataCr+row*yuvBuffer.chromaStride+left/2, dst, width/2); } } else { - // Convert between semiplannar and plannar + // Convert between semiplannar and plannar, or when UV orders are + // different. uint8_t *dst = codecBuffer->data(); for (auto row = top/2; row < (top+height)/2; row++) { for (auto col = left/2; col < (left+width)/2; col++) { @@ -1446,6 +1449,38 @@ status_t HeicCompositeStream::copyOneYuvTile(sp& codecBuffer, return OK; } +void HeicCompositeStream::initCopyRowFunction(int32_t width) +{ + using namespace libyuv; + + mFnCopyRow = CopyRow_C; +#if defined(HAS_COPYROW_SSE2) + if (TestCpuFlag(kCpuHasSSE2)) { + mFnCopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2; + } +#endif +#if defined(HAS_COPYROW_AVX) + if (TestCpuFlag(kCpuHasAVX)) { + mFnCopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX; + } +#endif +#if defined(HAS_COPYROW_ERMS) + if (TestCpuFlag(kCpuHasERMS)) { + mFnCopyRow = CopyRow_ERMS; + } +#endif +#if defined(HAS_COPYROW_NEON) + if (TestCpuFlag(kCpuHasNEON)) { + mFnCopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON; + } +#endif +#if defined(HAS_COPYROW_MIPS) + if (TestCpuFlag(kCpuHasMIPS)) { + mFnCopyRow = CopyRow_MIPS; + } +#endif +} + size_t HeicCompositeStream::calcAppSegmentMaxSize(const CameraMetadata& info) { camera_metadata_ro_entry_t entry = info.find(ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT); size_t maxAppsSegment = 1; diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h index 4cd9af0668..2aa3c38bc1 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.h +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h @@ -195,6 +195,7 @@ private: status_t copyOneYuvTile(sp& codecBuffer, const CpuConsumer::LockedBuffer& yuvBuffer, size_t top, size_t left, size_t width, size_t height); + void initCopyRowFunction(int32_t width); static size_t calcAppSegmentMaxSize(const CameraMetadata& info); static const nsecs_t kWaitDuration = 10000000; // 10 ms @@ -244,6 +245,9 @@ private: // In most common use case, entries are accessed in order. std::map mPendingInputFrames; + + // Function pointer of libyuv row copy. + void (*mFnCopyRow)(const uint8_t* src, uint8_t* dst, int width); }; }; // namespace camera3 -- GitLab From 0d2fa08f27ef47730d62e5ea7ef0f9e668eb1069 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 20 Feb 2019 12:31:53 -0800 Subject: [PATCH 0993/1530] Camera: notify captureSequener errors Test: locally modified camera HAL (faking buffer errors) passing CameraTest.java Bug: 120986771 Change-Id: I436ac5d033f68b59eaa7b129370faf0c0ca6a5f3 --- services/camera/libcameraservice/api1/Camera2Client.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index e002e186fb..162b50f5ce 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -1767,14 +1767,13 @@ void Camera2Client::notifyError(int32_t errorCode, case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER: ALOGW("%s: Received recoverable error %d from HAL - ignoring, requestId %" PRId32, __FUNCTION__, errorCode, resultExtras.requestId); + mCaptureSequencer->notifyError(errorCode, resultExtras); return; default: err = CAMERA_ERROR_UNKNOWN; break; } - mCaptureSequencer->notifyError(errorCode, resultExtras); - ALOGE("%s: Error condition %d reported by HAL, requestId %" PRId32, __FUNCTION__, errorCode, resultExtras.requestId); -- GitLab From ece71b2f22195fc8c21f1237eacd81e17dc556ad Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 20 Feb 2019 16:50:51 -0800 Subject: [PATCH 0994/1530] camera2 vndk: Clarify documentation around ACameraDevice::allocateCaptureRequest. Test: builds; not a functional change. Change-Id: Id3cc9013802eca044c24d910e4fce4fc7a640f2d Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 2 +- camera/ndk/ndk_vendor/impl/ACameraDevice.h | 7 ++++++- camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index f7863a5eb6..9aafcd3913 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -289,7 +289,7 @@ camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOut } camera_status_t -CameraDevice::allocateCaptureRequest( +CameraDevice::allocateCaptureRequestLocked( const ACaptureRequest* request, /*out*/sp &outReq) { sp req(new CaptureRequest()); req->mCaptureRequest.physicalCameraSettings.resize(1); diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index c63b97fc0c..d5715854e9 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -169,7 +169,12 @@ class CameraDevice final : public RefBase { camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output); - camera_status_t allocateCaptureRequest( + // Since this writes to ICameraDeviceUser's fmq, clients must take care that: + // a) This function is called serially. + // b) This function is called in accordance with ICameraDeviceUser.submitRequestList, + // otherwise, the wrong capture request might have the wrong settings + // metadata associated with it. + camera_status_t allocateCaptureRequestLocked( const ACaptureRequest* request, sp& outReq); static ACaptureRequest* allocateACaptureRequest(sp& req); diff --git a/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc index 7d2304e63e..8bd5a5227d 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc +++ b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc @@ -73,7 +73,7 @@ camera_status_t CameraDevice::submitRequestsLocked( requestsV.setCapacity(numRequests); for (int i = 0; i < numRequests; i++) { sp req; - ret = allocateCaptureRequest(requests[i], req); + ret = allocateCaptureRequestLocked(requests[i], req); // We need to call this method since after submitRequestList is called, // the request metadata queue might have removed the capture request // metadata. Therefore we simply add the metadata to its wrapper class, -- GitLab From 3bb81cd827ed39f472e0fa46f663afa08685fc72 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 20 Feb 2019 15:10:30 -0800 Subject: [PATCH 0995/1530] codec2: remove deprecated configs and their remaining usage Bug: 124982277 Change-Id: I8521fd76f981838aad8e721a222d15989cb26b9f --- media/codec2/components/aac/C2SoftAacDec.cpp | 26 ++-- media/codec2/components/aac/C2SoftAacEnc.cpp | 38 +++--- .../components/amr_nb_wb/C2SoftAmrDec.cpp | 36 ++--- .../components/amr_nb_wb/C2SoftAmrNbEnc.cpp | 34 ++--- .../components/amr_nb_wb/C2SoftAmrWbEnc.cpp | 34 ++--- media/codec2/components/aom/C2SoftAomDec.cpp | 4 +- media/codec2/components/avc/C2SoftAvcDec.cpp | 4 +- media/codec2/components/avc/C2SoftAvcEnc.cpp | 50 +++---- .../components/base/SimpleC2Component.cpp | 4 +- .../codec2/components/flac/C2SoftFlacDec.cpp | 34 ++--- .../codec2/components/flac/C2SoftFlacEnc.cpp | 38 +++--- .../codec2/components/g711/C2SoftG711Dec.cpp | 34 ++--- media/codec2/components/gsm/C2SoftGsmDec.cpp | 34 ++--- .../codec2/components/hevc/C2SoftHevcDec.cpp | 4 +- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 50 +++---- media/codec2/components/mp3/C2SoftMp3Dec.cpp | 35 +++-- .../components/mpeg2/C2SoftMpeg2Dec.cpp | 6 +- .../components/mpeg4_h263/C2SoftMpeg4Dec.cpp | 6 +- .../components/mpeg4_h263/C2SoftMpeg4Enc.cpp | 46 +++---- .../codec2/components/opus/C2SoftOpusDec.cpp | 34 ++--- .../codec2/components/opus/C2SoftOpusEnc.cpp | 38 +++--- media/codec2/components/raw/C2SoftRawDec.cpp | 34 ++--- .../components/vorbis/C2SoftVorbisDec.cpp | 34 ++--- media/codec2/components/vpx/C2SoftVpxDec.cpp | 4 +- media/codec2/components/vpx/C2SoftVpxEnc.cpp | 2 +- media/codec2/components/vpx/C2SoftVpxEnc.h | 40 +++--- .../codec2/components/xaac/C2SoftXaacDec.cpp | 42 +++--- media/codec2/core/include/C2Buffer.h | 124 ------------------ media/codec2/core/include/C2Config.h | 83 ------------ media/codec2/core/include/C2Param.h | 9 -- media/codec2/hidl/1.0/utils/InputSurface.cpp | 2 +- .../hidl/1.0/utils/InputSurfaceConnection.cpp | 2 +- .../common/media_c2_hidl_test_common.cpp | 4 +- .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 2 +- media/codec2/sfplugin/CCodecBufferChannel.cpp | 14 +- media/codec2/sfplugin/utils/Codec2Mapper.cpp | 8 +- .../tests/C2ComponentInterface_test.cpp | 46 +++---- media/codec2/tests/C2SampleComponent_test.cpp | 16 +-- media/codec2/vndk/C2Store.cpp | 6 +- 39 files changed, 422 insertions(+), 639 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp index 04dda8f52c..4d00d35680 100644 --- a/media/codec2/components/aac/C2SoftAacDec.cpp +++ b/media/codec2/components/aac/C2SoftAacDec.cpp @@ -75,7 +75,7 @@ public: .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).oneOf({ 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 @@ -84,15 +84,15 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(8000, 960000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -103,10 +103,10 @@ public: .build()); addParameter( - DefineParam(mAacFormat, C2_NAME_STREAM_AAC_FORMAT_SETTING) - .withDefault(new C2StreamAacFormatInfo::input(0u, C2AacStreamFormatRaw)) + DefineParam(mAacFormat, C2_PARAMKEY_AAC_PACKAGING) + .withDefault(new C2StreamAacFormatInfo::input(0u, C2Config::AAC_PACKAGING_RAW)) .withFields({C2F(mAacFormat, value).oneOf({ - C2AacStreamFormatRaw, C2AacStreamFormatAdts + C2Config::AAC_PACKAGING_RAW, C2Config::AAC_PACKAGING_ADTS })}) .withSetter(Setter::StrictValueWithNoDeps) .build()); @@ -191,7 +191,7 @@ public: .build()); } - bool isAdts() const { return mAacFormat->value == C2AacStreamFormatAdts; } + bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; } static C2R ProfileLevelSetter(bool mayBlock, C2P &me) { (void)mayBlock; (void)me; // TODO: validate @@ -205,13 +205,13 @@ public: int32_t getDrcEffectType() const { return mDrcEffectType->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mAacFormat; std::shared_ptr mProfileLevel; diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index d1bdf0d513..137e775e9f 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -37,29 +37,29 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_AAC)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::input(0u, 44100)) .withFields({C2F(mSampleRate, value).oneOf({ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 @@ -68,15 +68,15 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 6)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(8000, 960000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -125,13 +125,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mProfileLevel; }; @@ -323,8 +323,8 @@ void C2SoftAacEnc::process( return; } - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique(encInfo.confSize, 0u); + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique(encInfo.confSize, 0u); if (!csd) { ALOGE("CSD allocation failed"); mSignalledError = true; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp index c591e212ac..edad75a4bd 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp @@ -47,18 +47,18 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( #ifdef AMRNB MEDIA_MIMETYPE_AUDIO_AMR_NB #else @@ -67,13 +67,13 @@ public: )).build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) #ifdef AMRNB .withDefault(new C2StreamSampleRateInfo::output(0u, 8000)) .withFields({C2F(mSampleRate, value).equalTo(8000)}) @@ -85,19 +85,19 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).equalTo(1)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) #ifdef AMRNB - .withDefault(new C2BitrateTuning::input(0u, 4750)) + .withDefault(new C2StreamBitrateInfo::input(0u, 4750)) .withFields({C2F(mBitrate, value).inRange(4750, 12200)}) #else - .withDefault(new C2BitrateTuning::input(0u, 6600)) + .withDefault(new C2StreamBitrateInfo::input(0u, 6600)) .withFields({C2F(mBitrate, value).inRange(6600, 23850)}) #endif .withSetter(Setter::NonStrictValueWithNoDeps) @@ -110,13 +110,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp index 8c03257a74..3d3aa7dbf2 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp @@ -36,38 +36,38 @@ class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::input(0u, C2FormatAudio)) + new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_AMR_NB)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) .withFields({C2F(mChannelCount, value).equalTo(1)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::input(0u, 8000)) .withFields({C2F(mSampleRate, value).equalTo(8000)}) .withSetter( @@ -75,8 +75,8 @@ class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 4750)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 4750)) .withFields({C2F(mBitrate, value).inRange(4750, 12200)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -92,13 +92,13 @@ class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper { uint32_t getBitrate() const { return mBitrate->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp index 074493c42a..379cb32fc1 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp @@ -38,38 +38,38 @@ class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::input(0u, C2FormatAudio)) + new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_AMR_WB)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) .withFields({C2F(mChannelCount, value).equalTo(1)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::input(0u, 16000)) .withFields({C2F(mSampleRate, value).equalTo(16000)}) .withSetter( @@ -77,8 +77,8 @@ class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 6600)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 6600)) .withFields({C2F(mBitrate, value).inRange(6600, 23850)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -94,13 +94,13 @@ class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper { uint32_t getBitrate() const { return mBitrate->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp index 6be18078d9..4bcc2c669e 100644 --- a/media/codec2/components/aom/C2SoftAomDec.cpp +++ b/media/codec2/components/aom/C2SoftAomDec.cpp @@ -141,7 +141,7 @@ class C2SoftAomDec::IntfImpl : public SimpleInterface::BaseParams { static C2R SizeSetter(bool mayBlock, const C2P& oldMe, - C2P& me) { + C2P& me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -586,7 +586,7 @@ bool C2SoftAomDec::outputBuffer( mWidth = img->d_w; mHeight = img->d_h; - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); if (err == C2_OK) { diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp index 86cd3d8fb6..9290d743d3 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.cpp +++ b/media/codec2/components/avc/C2SoftAvcDec.cpp @@ -198,7 +198,7 @@ public: } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, - C2P &me) { + C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -845,7 +845,7 @@ void C2SoftAvcDec::process( mHeight = s_decode_op.u4_pic_ht; CHECK_EQ(0u, s_decode_op.u4_output_present); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); if (err == OK) { diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index 6ddb9ff735..b851908749 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -45,36 +45,36 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatVideo)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_AVC)) .build()); addParameter( - DefineParam(mUsage, C2_NAME_INPUT_STREAM_USAGE_SETTING) + DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) .withConstValue(new C2StreamUsageTuning::input( 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); addParameter( - DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING) - .withDefault(new C2VideoSizeStreamTuning::input(0u, 320, 240)) + DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) + .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) .withFields({ C2F(mSize, width).inRange(2, 2560, 2), C2F(mSize, height).inRange(2, 2560, 2), @@ -83,7 +83,7 @@ public: .build()); addParameter( - DefineParam(mFrameRate, C2_NAME_STREAM_FRAME_RATE_SETTING) + DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) // TODO: More restriction? .withFields({C2F(mFrameRate, value).greaterThan(0.)}) @@ -91,8 +91,8 @@ public: .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(4096, 12000000)}) .withSetter(BitrateSetter) .build()); @@ -182,9 +182,9 @@ public: static C2R ProfileLevelSetter( bool mayBlock, C2P &me, - const C2P &size, + const C2P &size, const C2P &frameRate, - const C2P &bitrate) { + const C2P &bitrate) { (void)mayBlock; if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) { me.set().profile = PROFILE_AVC_CONSTRAINED_BASELINE; @@ -325,16 +325,16 @@ public: std::shared_ptr getRequestSync_l() const { return mRequestSync; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; - std::shared_ptr mSize; + std::shared_ptr mSize; std::shared_ptr mFrameRate; std::shared_ptr mRequestSync; std::shared_ptr mIntraRefresh; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; }; @@ -1332,8 +1332,8 @@ void C2SoftAvcEnc::process( mSpsPpsHeaderReceived = true; - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u); + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u); if (!csd) { ALOGE("CSD allocation failed"); mSignalledError = true; @@ -1492,7 +1492,7 @@ void C2SoftAvcEnc::process( if (IV_IDR_FRAME == s_encode_op.u4_encoded_frame_type) { ALOGV("IDR frame produced"); buffer->setInfo(std::make_shared( - 0u /* stream id */, C2PictureTypeKeyFrame)); + 0u /* stream id */, C2Config::SYNC_FRAME)); } work->worklets.front()->output.buffers.push_back(buffer); } diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index b158f8f2b0..44f1fe0677 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -473,7 +473,7 @@ bool SimpleC2Component::processQueue() { if (!mOutputBlockPool) { c2_status_t err = [this] { // TODO: don't use query_vb - C2StreamFormatConfig::output outputFormat(0u); + C2StreamBufferTypeSetting::output outputFormat(0u); std::vector> params; c2_status_t err = intf()->query_vb( { &outputFormat }, @@ -485,7 +485,7 @@ bool SimpleC2Component::processQueue() { return err; } C2BlockPool::local_id_t poolId = - outputFormat.value == C2FormatVideo + outputFormat.value == C2BufferData::GRAPHIC ? C2BlockPool::BASIC_GRAPHIC : C2BlockPool::BASIC_LINEAR; if (params.size()) { diff --git a/media/codec2/components/flac/C2SoftFlacDec.cpp b/media/codec2/components/flac/C2SoftFlacDec.cpp index 86b16e8a64..10b14ce6e2 100644 --- a/media/codec2/components/flac/C2SoftFlacDec.cpp +++ b/media/codec2/components/flac/C2SoftFlacDec.cpp @@ -37,44 +37,44 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_FLAC)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).inRange(1, 655350)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 768000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 768000)) .withFields({C2F(mBitrate, value).inRange(1, 21000000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -99,13 +99,13 @@ public: int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mPcmEncodingInfo; }; diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp index 4ea35c21f9..0ce2543bad 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.cpp +++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp @@ -34,38 +34,38 @@ public: : C2InterfaceHelper(helper) { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_FLAC)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::input(0u, 44100)) .withFields({C2F(mSampleRate, value).inRange(1, 655350)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 2)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 768000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 768000)) .withFields({C2F(mBitrate, value).inRange(1, 21000000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -92,13 +92,13 @@ public: int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mPcmEncodingInfo; }; @@ -223,8 +223,8 @@ void C2SoftFlacEnc::process( } if (!mWroteHeader) { - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique(mHeaderOffset, 0u); + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique(mHeaderOffset, 0u); if (!csd) { ALOGE("CSD allocation failed"); mSignalledError = true; diff --git a/media/codec2/components/g711/C2SoftG711Dec.cpp b/media/codec2/components/g711/C2SoftG711Dec.cpp index 1c71d458c7..504ca787a3 100644 --- a/media/codec2/components/g711/C2SoftG711Dec.cpp +++ b/media/codec2/components/g711/C2SoftG711Dec.cpp @@ -41,18 +41,18 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( #ifdef ALAW MEDIA_MIMETYPE_AUDIO_G711_ALAW #else @@ -61,28 +61,28 @@ public: )).build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 8000)) .withFields({C2F(mSampleRate, value).inRange(8000, 48000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).equalTo(1)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).equalTo(64000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -94,13 +94,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/gsm/C2SoftGsmDec.cpp b/media/codec2/components/gsm/C2SoftGsmDec.cpp index 7101c799fe..69d4885874 100644 --- a/media/codec2/components/gsm/C2SoftGsmDec.cpp +++ b/media/codec2/components/gsm/C2SoftGsmDec.cpp @@ -36,44 +36,44 @@ class C2SoftGsmDec::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_MSGSM)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 8000)) .withFields({C2F(mSampleRate, value).equalTo(8000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).equalTo(1)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 13200)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 13200)) .withFields({C2F(mBitrate, value).equalTo(13200)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -85,13 +85,13 @@ class C2SoftGsmDec::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp index f0d7d887f6..bb8dda03ed 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.cpp +++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp @@ -192,7 +192,7 @@ public: } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, - C2P &me) { + C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -839,7 +839,7 @@ void C2SoftHevcDec::process( mHeight = s_decode_op.u4_pic_ht; CHECK_EQ(0u, s_decode_op.u4_output_present); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 5e6f44f00b..2c0a7a025c 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -45,37 +45,37 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::input(0u, C2FormatVideo)) + new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_HEVC)) .build()); - addParameter(DefineParam(mUsage, C2_NAME_INPUT_STREAM_USAGE_SETTING) + addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) .withConstValue(new C2StreamUsageTuning::input( 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); addParameter( - DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING) - .withDefault(new C2VideoSizeStreamTuning::input(0u, 320, 240)) + DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) + .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) .withFields({ C2F(mSize, width).inRange(320, 1920, 2), C2F(mSize, height).inRange(128, 1088, 2), @@ -84,7 +84,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mFrameRate, C2_NAME_STREAM_FRAME_RATE_SETTING) + DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) .withFields({C2F(mFrameRate, value).greaterThan(0.)}) .withSetter( @@ -92,8 +92,8 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(4096, 12000000)}) .withSetter(BitrateSetter) .build()); @@ -162,9 +162,9 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { static C2R ProfileLevelSetter( bool mayBlock, C2P &me, - const C2P &size, + const C2P &size, const C2P &frameRate, - const C2P &bitrate) { + const C2P &bitrate) { (void)mayBlock; if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) { me.set().profile = PROFILE_HEVC_MAIN; @@ -292,15 +292,15 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; - std::shared_ptr mSize; + std::shared_ptr mSize; std::shared_ptr mFrameRate; std::shared_ptr mRequestSync; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; }; @@ -661,8 +661,8 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, ihevce_out_buf_t s_header_op{}; err = ihevce_encode_header(mCodecCtx, &s_header_op); if (err == IHEVCE_EOK && s_header_op.i4_bytes_generated) { - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique( + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique( s_header_op.i4_bytes_generated, 0u); if (!csd) { ALOGE("CSD allocation failed"); @@ -746,7 +746,7 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, ALOGV("IDR frame produced"); buffer->setInfo( std::make_shared( - 0u /* stream id */, C2PictureTypeKeyFrame)); + 0u /* stream id */, C2Config::SYNC_FRAME)); } work->worklets.front()->output.buffers.push_back(buffer); } diff --git a/media/codec2/components/mp3/C2SoftMp3Dec.cpp b/media/codec2/components/mp3/C2SoftMp3Dec.cpp index c8b8397f34..9db6d8f801 100644 --- a/media/codec2/components/mp3/C2SoftMp3Dec.cpp +++ b/media/codec2/components/mp3/C2SoftMp3Dec.cpp @@ -40,29 +40,29 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_MPEG)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).oneOf({8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000})}) @@ -70,15 +70,15 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 2)) .withFields({C2F(mChannelCount, value).inRange(1, 2)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(8000, 320000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -90,13 +90,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; @@ -555,4 +555,3 @@ extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { ALOGV("in %s", __func__); delete factory; } - diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp index da32ec0275..290677ed44 100644 --- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp +++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp @@ -180,7 +180,7 @@ public: } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, - C2P &me) { + C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -892,7 +892,7 @@ void C2SoftMpeg2Dec::process( ALOGI("Configuring decoder: mWidth %d , mHeight %d ", mWidth, mHeight); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); @@ -931,7 +931,7 @@ void C2SoftMpeg2Dec::process( ALOGI("Configuring decoder out: mWidth %d , mHeight %d ", mWidth, mHeight); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp index 0b89cff68c..3d4a733714 100644 --- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp +++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp @@ -186,7 +186,7 @@ public: } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, - C2P &me) { + C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -574,7 +574,7 @@ void C2SoftMpeg4Dec::process( PVSetPostProcType(mDecHandle, 0); if (handleResChange(work)) { ALOGI("Setting width and height"); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); if (err == OK) { @@ -646,7 +646,7 @@ void C2SoftMpeg4Dec::process( return; } else if (resChange) { ALOGI("Setting width and height"); - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); if (err == OK) { diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp index c8796f3392..89fa59d05d 100644 --- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp +++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp @@ -52,26 +52,26 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::input(0u, C2FormatVideo)) + new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( #ifdef MPEG4 MEDIA_MIMETYPE_VIDEO_MPEG4 #else @@ -80,14 +80,14 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { )) .build()); - addParameter(DefineParam(mUsage, C2_NAME_INPUT_STREAM_USAGE_SETTING) + addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) .withConstValue(new C2StreamUsageTuning::input( 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); addParameter( - DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING) - .withDefault(new C2VideoSizeStreamTuning::input(0u, 176, 144)) + DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) + .withDefault(new C2StreamPictureSizeInfo::input(0u, 176, 144)) .withFields({ #ifdef MPEG4 C2F(mSize, width).inRange(16, 176, 16), @@ -101,7 +101,7 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mFrameRate, C2_NAME_STREAM_FRAME_RATE_SETTING) + DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 17.)) // TODO: More restriction? .withFields({C2F(mFrameRate, value).greaterThan(0.)}) @@ -110,8 +110,8 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(4096, 12000000)}) .withSetter(BitrateSetter) .build()); @@ -217,14 +217,14 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; - std::shared_ptr mSize; + std::shared_ptr mSize; std::shared_ptr mFrameRate; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; }; @@ -446,8 +446,8 @@ void C2SoftMpeg4Enc::process( } ++mNumInputFrames; - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique(outputSize, 0u); + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique(outputSize, 0u); if (!csd) { ALOGE("CSD allocation failed"); mSignalledError = true; @@ -595,7 +595,7 @@ void C2SoftMpeg4Enc::process( work->worklets.front()->output.ordinal.timestamp = inputTimeStamp; if (hintTrack.CodeType == 0) { buffer->setInfo(std::make_shared( - 0u /* stream id */, C2PictureTypeKeyFrame)); + 0u /* stream id */, C2Config::SYNC_FRAME)); } work->worklets.front()->output.buffers.push_back(buffer); } diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp index 3ce1fd67d3..680712e950 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.cpp +++ b/media/codec2/components/opus/C2SoftOpusDec.cpp @@ -40,44 +40,44 @@ class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_OPUS)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 48000)) .withFields({C2F(mSampleRate, value).equalTo(48000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 6000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 6000)) .withFields({C2F(mBitrate, value).inRange(6000, 510000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -89,13 +89,13 @@ class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index 68fcea14e1..a0b244387b 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -42,29 +42,29 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_OPUS)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::input(0u, 48000)) .withFields({C2F(mSampleRate, value).oneOf({ 8000, 12000, 16000, 24000, 48000})}) @@ -72,15 +72,15 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 128000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 128000)) .withFields({C2F(mBitrate, value).inRange(500, 512000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -104,13 +104,13 @@ public: uint32_t getComplexity() const { return mComplexity->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mComplexity; std::shared_ptr mInputMaxBufSize; }; @@ -423,8 +423,8 @@ void C2SoftOpusEnc::process(const std::unique_ptr& work, int headerLen = WriteOpusHeaders(opusHeader, mSampleRate, header, sizeof(header), mCodecDelay, mSeekPreRoll); - std::unique_ptr csd = - C2StreamCsdInfo::output::AllocUnique(headerLen, 0u); + std::unique_ptr csd = + C2StreamInitDataInfo::output::AllocUnique(headerLen, 0u); if (!csd) { ALOGE("CSD allocation failed"); mSignalledError = true; diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp index 5c834814b8..802caa477e 100644 --- a/media/codec2/components/raw/C2SoftRawDec.cpp +++ b/media/codec2/components/raw/C2SoftRawDec.cpp @@ -37,44 +37,44 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).inRange(8000, 192000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 2)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(1, 10000000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -98,13 +98,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mPcmEncodingInfo; }; diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp index 48825e4421..e7393eeee7 100644 --- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp +++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp @@ -45,44 +45,44 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_VORBIS)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 48000)) .withFields({C2F(mSampleRate, value).inRange(8000, 96000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(32000, 500000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -94,13 +94,13 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; }; diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp index 9ba23624e1..3120f7af54 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.cpp +++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp @@ -215,7 +215,7 @@ public: } static C2R SizeSetter(bool mayBlock, const C2P &oldMe, - C2P &me) { + C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); if (!me.F(me.v.width).supportsAtAll(me.v.width)) { @@ -700,7 +700,7 @@ bool C2SoftVpxDec::outputBuffer( mWidth = img->d_w; mHeight = img->d_h; - C2VideoSizeStreamInfo::output size(0u, mWidth, mHeight); + C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight); std::vector> failures; c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures); if (err == C2_OK) { diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp index 155a84f898..6509a88a94 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp +++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp @@ -633,7 +633,7 @@ void C2SoftVpxEnc::process( std::shared_ptr buffer = createLinearBuffer(block); if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) { buffer->setInfo(std::make_shared( - 0u /* stream id */, C2PictureTypeKeyFrame)); + 0u /* stream id */, C2Config::SYNC_FRAME)); } work->worklets.front()->output.buffers.push_back(buffer); work->worklets.front()->output.ordinal = work->input.ordinal; diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h index 87ed1a9e8b..5591a49019 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.h +++ b/media/codec2/components/vpx/C2SoftVpxEnc.h @@ -229,26 +229,26 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::input(0u, C2FormatVideo)) + new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) .withConstValue( - new C2StreamFormatConfig::output(0u, C2FormatCompressed)) + new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_VIDEO_RAW)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( #ifdef VP9 MEDIA_MIMETYPE_VIDEO_VP9 #else @@ -257,14 +257,14 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { )) .build()); - addParameter(DefineParam(mUsage, C2_NAME_INPUT_STREAM_USAGE_SETTING) + addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) .withConstValue(new C2StreamUsageTuning::input( 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); addParameter( - DefineParam(mSize, C2_NAME_STREAM_VIDEO_SIZE_SETTING) - .withDefault(new C2VideoSizeStreamTuning::input(0u, 320, 240)) + DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) + .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) .withFields({ C2F(mSize, width).inRange(2, 2048, 2), C2F(mSize, height).inRange(2, 2048, 2), @@ -285,7 +285,7 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mFrameRate, C2_NAME_STREAM_FRAME_RATE_SETTING) + DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) // TODO: More restriction? .withFields({C2F(mFrameRate, value).greaterThan(0.)}) @@ -312,8 +312,8 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::output(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(4096, 40000000)}) .withSetter(BitrateSetter) .build()); @@ -416,18 +416,18 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; - std::shared_ptr mSize; + std::shared_ptr mSize; std::shared_ptr mFrameRate; std::shared_ptr mLayering; std::shared_ptr mIntraRefresh; std::shared_ptr mRequestSync; std::shared_ptr mSyncFramePeriod; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mBitrateMode; std::shared_ptr mProfileLevel; }; diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp index 1c0e70b2a2..86739c24a2 100644 --- a/media/codec2/components/xaac/C2SoftXaacDec.cpp +++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp @@ -66,29 +66,29 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatCompressed)) + DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING) - .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatAudio)) + DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) + .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) .build()); addParameter( - DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_AAC)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING) - .withConstValue(AllocSharedString( + DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) + .withConstValue(AllocSharedString( MEDIA_MIMETYPE_AUDIO_RAW)) .build()); addParameter( - DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING) + DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) .withFields({C2F(mSampleRate, value).oneOf({ 7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 @@ -97,15 +97,15 @@ public: .build()); addParameter( - DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING) + DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) .withFields({C2F(mChannelCount, value).inRange(1, 8)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); addParameter( - DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING) - .withDefault(new C2BitrateTuning::input(0u, 64000)) + DefineParam(mBitrate, C2_PARAMKEY_BITRATE) + .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) .withFields({C2F(mBitrate, value).inRange(8000, 960000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); @@ -116,10 +116,10 @@ public: .build()); addParameter( - DefineParam(mAacFormat, C2_NAME_STREAM_AAC_FORMAT_SETTING) - .withDefault(new C2StreamAacFormatInfo::input(0u, C2AacStreamFormatRaw)) + DefineParam(mAacFormat, C2_PARAMKEY_AAC_PACKAGING) + .withDefault(new C2StreamAacFormatInfo::input(0u, C2Config::AAC_PACKAGING_RAW)) .withFields({C2F(mAacFormat, value).oneOf({ - C2AacStreamFormatRaw, C2AacStreamFormatAdts + C2Config::AAC_PACKAGING_RAW, C2Config::AAC_PACKAGING_ADTS })}) .withSetter(Setter::StrictValueWithNoDeps) .build()); @@ -203,7 +203,7 @@ public: .build()); } - bool isAdts() const { return mAacFormat->value == C2AacStreamFormatAdts; } + bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; } uint32_t getBitrate() const { return mBitrate->value; } static C2R ProfileLevelSetter(bool mayBlock, C2P &me) { (void)mayBlock; @@ -218,13 +218,13 @@ public: int32_t getDrcEffectType() const { return mDrcEffectType->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; + std::shared_ptr mInputFormat; + std::shared_ptr mOutputFormat; + std::shared_ptr mInputMediaType; + std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; - std::shared_ptr mBitrate; + std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mAacFormat; std::shared_ptr mProfileLevel; diff --git a/media/codec2/core/include/C2Buffer.h b/media/codec2/core/include/C2Buffer.h index c428122a1c..3d3587c513 100644 --- a/media/codec2/core/include/C2Buffer.h +++ b/media/codec2/core/include/C2Buffer.h @@ -1994,7 +1994,6 @@ public: GRAPHIC, ///< the buffer contains a single graphic block GRAPHIC_CHUNKS, ///< the buffer contains one of more graphic blocks }; - typedef type_t Type; // deprecated /** * Gets the type of this buffer (data). @@ -2042,23 +2041,6 @@ public: */ const C2BufferData data() const; - /** - * These will still work if used in onDeathNotify. - */ -#if 0 - inline std::shared_ptr asLinearBuffer() const { - return mType == LINEAR ? std::shared_ptr::reinterpret_cast(this) : nullptr; - } - - inline std::shared_ptr asGraphicBuffer() const { - return mType == GRAPHIC ? std::shared_ptr::reinterpret_cast(this) : nullptr; - } - - inline std::shared_ptr asCircularBuffer() const { - return mType == CIRCULAR ? std::shared_ptr::reinterpret_cast(this) : nullptr; - } -#endif - ///@name Pre-destroy notification handling ///@{ @@ -2163,8 +2145,6 @@ public: */ static std::shared_ptr CreateGraphicBuffer(const C2ConstGraphicBlock &block); - - protected: // no public constructor explicit C2Buffer(const std::vector &blocks); @@ -2173,7 +2153,6 @@ protected: private: class Impl; std::shared_ptr mImpl; -// Type _mType; }; /** @@ -2200,109 +2179,6 @@ public: /// @} -/// \cond INTERNAL - -/// \todo These are no longer used - -/// \addtogroup linear -/// @{ - -/** \deprecated */ -class C2LinearBuffer - : public C2Buffer, public _C2LinearRangeAspect, - public std::enable_shared_from_this { -public: - /** \todo what is this? */ - const C2Handle *handle() const; - -protected: - inline C2LinearBuffer(const C2ConstLinearBlock &block); - -private: - class Impl; - Impl *mImpl; -}; - -class C2ReadCursor; - -class C2WriteCursor { -public: - uint32_t remaining() const; // remaining data to be read - void commit(); // commits the current position. discard data before current position - void reset() const; // resets position to the last committed position - // slices off at most |size| bytes, and moves cursor ahead by the number of bytes - // sliced off. - C2ReadCursor slice(uint32_t size) const; - // slices off at most |size| bytes, and moves cursor ahead by the number of bytes - // sliced off. - C2WriteCursor reserve(uint32_t size); - // bool read(T&); - // bool write(T&); - C2Fence waitForSpace(uint32_t size); -}; - -/// @} - -/// \addtogroup graphic -/// @{ - -struct C2ColorSpace { -//public: - enum Standard { - BT601, - BT709, - BT2020, - // TODO - }; - - enum Range { - LIMITED, - FULL, - // TODO - }; - - enum TransferFunction { - BT709Transfer, - BT2020Transfer, - HybridLogGamma2, - HybridLogGamma4, - // TODO - }; -}; - -/** \deprecated */ -class C2GraphicBuffer : public C2Buffer { -public: - // constant attributes - inline uint32_t width() const { return mWidth; } - inline uint32_t height() const { return mHeight; } - inline uint32_t format() const { return mFormat; } - inline const C2MemoryUsage usage() const { return mUsage; } - - // modifiable attributes - - - virtual const C2ColorSpace colorSpace() const = 0; - // best effort - virtual void setColorSpace_be(const C2ColorSpace &colorSpace) = 0; - virtual bool setColorSpace(const C2ColorSpace &colorSpace) = 0; - - const C2Handle *handle() const; - -protected: - uint32_t mWidth; - uint32_t mHeight; - uint32_t mFormat; - C2MemoryUsage mUsage; - - class Impl; - Impl *mImpl; -}; - -/// @} - -/// \endcond - /// @} #endif // C2BUFFER_H_ diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index fb6edb6114..9545c45375 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -240,19 +240,6 @@ enum C2ParamIndexKind : C2Param::type_index_t { kParamIndexTimestampGapAdjustment, // input-surface, struct kParamIndexSurfaceAllocator, // u32 - - // deprecated indices due to renaming - kParamIndexAacStreamFormat = kParamIndexAacPackaging, - kParamIndexCsd = kParamIndexInitData, - kParamIndexMaxVideoSizeHint = kParamIndexMaxPictureSize, - kParamIndexMime = kParamIndexMediaType, - kParamIndexRequestedInfos = kParamIndexSubscribedParamIndices, - - - // deprecated indices due to removal - kParamIndexSupportedParams = 0xDEAD0000, - kParamIndexReadOnlyParams, - kParamIndexTemporal, }; } @@ -337,14 +324,8 @@ constexpr char C2_PARAMKEY_COMPONENT_KIND[] = "component.kind"; // read-only typedef C2GlobalParam, kParamIndexDomain> C2ComponentDomainSetting; -typedef C2ComponentDomainSetting C2ComponentDomainInfo; // deprecated -typedef C2Component::domain_t C2DomainKind; // deprecated constexpr char C2_PARAMKEY_COMPONENT_DOMAIN[] = "component.domain"; -constexpr C2Component::domain_t C2DomainAudio = C2Component::DOMAIN_AUDIO; // deprecated -constexpr C2Component::domain_t C2DomainOther = C2Component::DOMAIN_OTHER; // deprecate -constexpr C2Component::domain_t C2DomainVideo = C2Component::DOMAIN_VIDEO; // deprecate - /** * Component attributes. * @@ -359,9 +340,6 @@ typedef C2GlobalParam, kPa C2ComponentAttributesSetting; constexpr char C2_PARAMKEY_COMPONENT_ATTRIBUTES[] = "component.attributes"; -// deprecated -typedef C2ComponentAttributesSetting C2ComponentTemporalInfo; - /** * Time stretching. * @@ -707,7 +685,6 @@ struct C2ProfileLevelStruct { typedef C2StreamParam C2StreamProfileLevelInfo; constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl"; -#define C2_PARAMKEY_STREAM_PROFILE_LEVEL C2_PARAMKEY_PROFILE_LEVEL /** * Codec-specific initialization data. @@ -719,9 +696,7 @@ constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl"; * TODO: define for other codecs. */ typedef C2StreamParam C2StreamInitDataInfo; -typedef C2StreamInitDataInfo C2StreamCsdInfo; // deprecated constexpr char C2_PARAMKEY_INIT_DATA[] = "coded.init-data"; -#define C2_PARAMKEY_STREAM_INIT_DATA C2_PARAMKEY_INIT_DATA /** * Supplemental Data. @@ -781,11 +756,8 @@ constexpr char C2_PARAMKEY_SUBSCRIBED_SUPPLEMENTAL_DATA[] = "output.subscribed-s * port media type. */ typedef C2PortParam C2PortMediaTypeSetting; -typedef C2PortMediaTypeSetting C2PortMimeConfig; // deprecated constexpr char C2_PARAMKEY_INPUT_MEDIA_TYPE[] = "input.media-type"; constexpr char C2_PARAMKEY_OUTPUT_MEDIA_TYPE[] = "output.media-type"; -#define C2_NAME_INPUT_PORT_MIME_SETTING C2_PARAMKEY_INPUT_MEDIA_TYPE -#define C2_NAME_OUTPUT_PORT_MIME_SETTING C2_PARAMKEY_OUTPUT_MEDIA_TYPE typedef C2StreamParam C2StreamMediaTypeSetting; @@ -808,24 +780,20 @@ typedef C2StreamParam C2StreamMe */ typedef C2PortParam C2PortRequestedDelayTuning; -typedef C2PortRequestedDelayTuning C2PortRequestedLatencyTuning; // deprecated constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay.requested"; constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay.requested"; typedef C2GlobalParam C2RequestedPipelineDelayTuning; -typedef C2RequestedPipelineDelayTuning C2ComponentRequestedLatencyTuning; // deprecated constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "pipeline-delay.requested"; // read-only typedef C2PortParam C2PortActualDelayTuning; -typedef C2PortActualDelayTuning C2PortLatencyInfo; // deprecated constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay.actual"; constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay.actual"; // read-only typedef C2GlobalParam C2ActualPipelineDelayTuning; -typedef C2ActualPipelineDelayTuning C2ComponentLatencyInfo; // deprecated constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay.actual"; /** @@ -875,7 +843,6 @@ constexpr char C2_PARAMKEY_OUTPUT_REORDER_KEY[] = "output.reorder.key"; */ // private typedef C2PortParam C2PortStreamCountTuning; -typedef C2PortStreamCountTuning C2PortStreamCountConfig; // deprecated constexpr char C2_PARAMKEY_INPUT_STREAM_COUNT[] = "input.stream-count"; constexpr char C2_PARAMKEY_OUTPUT_STREAM_COUNT[] = "output.stream-count"; @@ -985,20 +952,9 @@ constexpr char C2_PARAMKEY_MAX_PRIVATE_BUFFER_COUNT[] = "algo.buffers.max-count" typedef C2StreamParam>, kParamIndexBufferType> C2StreamBufferTypeSetting; - -constexpr C2BufferData::type_t C2FormatAudio = C2BufferData::LINEAR; // deprecated -constexpr C2BufferData::type_t C2FormatCompressed = C2BufferData::LINEAR; // deprecated -constexpr C2BufferData::type_t C2FormatVideo = C2BufferData::GRAPHIC; // deprecated -typedef C2BufferData::type_t C2FormatKind; // deprecated - -typedef C2StreamBufferTypeSetting C2StreamFormatConfig; // deprecated constexpr char C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE[] = "input.buffers.type"; constexpr char C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE[] = "output.buffers.type"; -// deprecated -#define C2_NAME_INPUT_STREAM_FORMAT_SETTING C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE -#define C2_NAME_OUTPUT_STREAM_FORMAT_SETTING C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE - /** * Memory usage. * @@ -1007,8 +963,6 @@ constexpr char C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE[] = "output.buffers.type"; typedef C2StreamParam C2StreamUsageTuning; constexpr char C2_PARAMKEY_INPUT_STREAM_USAGE[] = "input.buffers.usage"; constexpr char C2_PARAMKEY_OUTPUT_STREAM_USAGE[] = "output.buffers.usage"; -// deprecated -#define C2_NAME_INPUT_STREAM_USAGE_SETTING C2_PARAMKEY_INPUT_STREAM_USAGE /** * Picture (video or image frame) size. @@ -1068,8 +1022,6 @@ typedef C2StreamParam C2StreamM constexpr char C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE[] = "input.buffers.max-size"; constexpr char C2_PARAMKEY_OUTPUT_MAX_BUFFER_SIZE[] = "output.buffers.max-size"; -#define C2_NAME_STREAM_MAX_BUFFER_SIZE_SETTING C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE - /* ---------------------------------------- misc. state ---------------------------------------- */ /** @@ -1170,9 +1122,7 @@ constexpr char C2_PARAMKEY_SECURE_MODE[] = "algo.secure-mode"; * Bitrate */ typedef C2StreamParam C2StreamBitrateInfo; -typedef C2StreamBitrateInfo C2BitrateTuning; // deprecated constexpr char C2_PARAMKEY_BITRATE[] = "coded.bitrate"; -#define C2_NAME_STREAM_BITRATE_SETTING C2_PARAMKEY_BITRATE /** * Bitrate mode. @@ -1261,15 +1211,8 @@ constexpr char C2_PARAMKEY_PREPEND_HEADER_MODE[] = "output.buffers.prepend-heade * * This is used for the output of the video decoder, and the input of the video encoder. */ -typedef C2PictureSizeStruct C2VideoSizeStruct; // deprecated - typedef C2StreamParam C2StreamPictureSizeInfo; constexpr char C2_PARAMKEY_PICTURE_SIZE[] = "raw.size"; -#define C2_PARAMKEY_STREAM_PICTURE_SIZE C2_PARAMKEY_PICTURE_SIZE -#define C2_NAME_STREAM_VIDEO_SIZE_INFO C2_PARAMKEY_PICTURE_SIZE -typedef C2StreamPictureSizeInfo C2VideoSizeStreamInfo; // deprecated -typedef C2StreamPictureSizeInfo C2VideoSizeStreamTuning; // deprecated -#define C2_NAME_STREAM_VIDEO_SIZE_SETTING C2_PARAMKEY_PICTURE_SIZE /** * Crop rectangle. @@ -1344,12 +1287,10 @@ typedef C2StreamParam, kParamIndexScalingMethod> C2StreamScalingMethodTuning; constexpr char C2_PARAMKEY_SCALING_MODE[] = "raw.scaling-method"; -#define C2_PARAMKEY_STREAM_SCALING_MODE C2_PARAMKEY_SCALING_MODE typedef C2StreamParam C2StreamScaledPictureSizeTuning; constexpr char C2_PARAMKEY_SCALED_PICTURE_SIZE[] = "raw.scaled-size"; -#define C2_PARAMKEY_STREAM_SCALED_PICTURE_SIZE C2_PARAMKEY_SCALED_PICTURE_SIZE typedef C2StreamParam C2StreamScaledCropRectTuning; @@ -1504,15 +1445,8 @@ C2ENUM(C2Color::matrix_t, uint32_t, MATRIX_BT2020_CONSTANT, ///< Rec.ITU-R BT.2020 constant luminance MATRIX_VENDOR_START = 0x80, ///< vendor-specific matrix coefficient values start here MATRIX_OTHER = 0xff, ///< max value, reserved for undefined values - - MATRIX_SMPTE240M = MATRIX_240M, // deprecated - MATRIX_BT2020CONSTANT = MATRIX_BT2020_CONSTANT, // deprecated ) -constexpr C2Color::matrix_t MATRIX_BT470_6M = MATRIX_FCC47_73_682; // deprecated -constexpr C2Color::matrix_t MATRIX_BT709_5 = MATRIX_BT709; // deprecated -constexpr C2Color::matrix_t MATRIX_BT601_6 = MATRIX_BT601; // deprecated - struct C2ColorAspectsStruct { C2Color::range_t range; C2Color::primaries_t primaries; @@ -1635,7 +1569,6 @@ constexpr char C2_PARAMKEY_BLOCK_RATE[] = "coded.block-rate"; */ typedef C2StreamParam C2StreamFrameRateInfo; constexpr char C2_PARAMKEY_FRAME_RATE[] = "coded.frame-rate"; -#define C2_NAME_STREAM_FRAME_RATE_SETTING C2_PARAMKEY_FRAME_RATE typedef C2PortParam C2PortFrameRateInfo; constexpr char C2_PARAMKEY_INPUT_FRAME_RATE[] = "input.frame-rate"; @@ -1668,9 +1601,6 @@ C2ENUM(C2Config::picture_type_t, uint32_t, B_FRAME = (1 << 3), ///< backward predicted (out-of-order) frame ) -typedef C2Config::picture_type_t C2PictureTypeMask; // deprecated -constexpr C2Config::picture_type_t C2PictureTypeKeyFrame = C2Config::SYNC_FRAME; // deprecated - /** * Allowed picture types. */ @@ -1750,8 +1680,6 @@ constexpr char C2_PARAMKEY_REQUEST_SYNC_FRAME[] = "coding.request-sync-frame"; typedef C2StreamParam C2StreamSyncFrameIntervalTuning; constexpr char C2_PARAMKEY_SYNC_FRAME_INTERVAL[] = "coding.sync-frame-interval"; -// deprecated -#define C2_PARAMKEY_SYNC_FRAME_PERIOD C2_PARAMKEY_SYNC_FRAME_INTERVAL /** * Temporal layering @@ -1885,8 +1813,6 @@ constexpr char C2_PARAMKEY_TILE_HANDLING[] = "coding.tile-handling"; typedef C2StreamParam C2StreamSampleRateInfo; constexpr char C2_PARAMKEY_SAMPLE_RATE[] = "raw.sample-rate"; constexpr char C2_PARAMKEY_CODED_SAMPLE_RATE[] = "coded.sample-rate"; -// deprecated -#define C2_NAME_STREAM_SAMPLE_RATE_SETTING C2_PARAMKEY_SAMPLE_RATE /** * Channel count. @@ -1894,8 +1820,6 @@ constexpr char C2_PARAMKEY_CODED_SAMPLE_RATE[] = "coded.sample-rate"; typedef C2StreamParam C2StreamChannelCountInfo; constexpr char C2_PARAMKEY_CHANNEL_COUNT[] = "raw.channel-count"; constexpr char C2_PARAMKEY_CODED_CHANNEL_COUNT[] = "coded.channel-count"; -// deprecated -#define C2_NAME_STREAM_CHANNEL_COUNT_SETTING C2_PARAMKEY_CHANNEL_COUNT /** * Max channel count. Used to limit the number of coded or decoded channels. @@ -2005,16 +1929,10 @@ C2ENUM(C2Config::aac_packaging_t, uint32_t, AAC_PACKAGING_ADTS ) -typedef C2Config::aac_packaging_t C2AacStreamFormatKind; // deprecated -// deprecated -constexpr C2Config::aac_packaging_t C2AacStreamFormatRaw = C2Config::AAC_PACKAGING_RAW; -constexpr C2Config::aac_packaging_t C2AacStreamFormatAdts = C2Config::AAC_PACKAGING_ADTS; - typedef C2StreamParam>, kParamIndexAacPackaging> C2StreamAacPackagingInfo; typedef C2StreamAacPackagingInfo C2StreamAacFormatInfo; constexpr char C2_PARAMKEY_AAC_PACKAGING[] = "coded.aac-packaging"; -#define C2_NAME_STREAM_AAC_FORMAT_SETTING C2_PARAMKEY_AAC_PACKAGING /* ================================ PLATFORM-DEFINED PARAMETERS ================================ */ @@ -2134,7 +2052,6 @@ constexpr char C2_PARAMKEY_SURFACE_SCALING_MODE[] = "raw.surface-scaling"; typedef C2GlobalParam C2InputSurfaceEosTuning; constexpr char C2_PARAMKEY_INPUT_SURFACE_EOS[] = "input-surface.eos"; -#define C2_NAME_INPUT_SURFACE_EOS_TUNING C2_PARAMKEY_INPUT_SURFACE_EOS /** * Start/suspend/resume/stop controls and timestamps for input surface. diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h index efc5c8944c..d264bf3c91 100644 --- a/media/codec2/core/include/C2Param.h +++ b/media/codec2/core/include/C2Param.h @@ -1012,15 +1012,6 @@ public: _mNamedValues(_NamedValuesGetter::getNamedValues()), _mFieldId(offset) {} -/* - template::type> - inline C2FieldDescriptor(T* offset, const char *name) - : _mType(this->GetType((B*)nullptr)), - _mExtent(std::is_array::value ? std::extent::value : 1), - _mName(name), - _mFieldId(offset) {} -*/ - /// \deprecated template::type> inline C2FieldDescriptor(S*, T S::* field, const char *name) diff --git a/media/codec2/hidl/1.0/utils/InputSurface.cpp b/media/codec2/hidl/1.0/utils/InputSurface.cpp index 2cbe64b967..85c44c3146 100644 --- a/media/codec2/hidl/1.0/utils/InputSurface.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurface.cpp @@ -45,7 +45,7 @@ public: setDerivedInstance(this); addParameter( - DefineParam(mEos, C2_NAME_INPUT_SURFACE_EOS_TUNING) + DefineParam(mEos, C2_PARAMKEY_INPUT_SURFACE_EOS) .withDefault(new C2InputSurfaceEosTuning(false)) .withFields({C2F(mEos, value).oneOf({true, false})}) .withSetter(EosSetter) diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp index 1024f50be0..c9932ef4f4 100644 --- a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp @@ -124,7 +124,7 @@ struct InputSurfaceConnection::Impl : public ComponentWrapper { } // TODO: read settings properly from the interface - C2VideoSizeStreamTuning::input inputSize; + C2StreamPictureSizeInfo::input inputSize; C2StreamUsageTuning::input usage; c2_status_t c2Status = queryFromSink({ &inputSize, &usage }, {}, diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp index 31da111a89..1f36270312 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp +++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp @@ -93,14 +93,14 @@ void workDone( std::vector> failures; for (size_t i = 0; i < updates.size(); ++i) { C2Param* param = updates[i].get(); - if (param->index() == C2StreamCsdInfo::output::PARAM_TYPE) { + if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) { csd = true; } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) || (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) || (param->index() == - C2VideoSizeStreamInfo::output::PARAM_TYPE)) { + C2StreamPictureSizeInfo::output::PARAM_TYPE)) { configParam.push_back(param); } } diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 95d1b726ee..7db41c090b 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -229,7 +229,7 @@ void validateComponent( // Set Default config param. bool Codec2VideoEncHidlTest::setupConfigParam(int32_t nWidth, int32_t nHeight) { std::vector> failures; - C2VideoSizeStreamTuning::input inputSize(0u, nWidth, nHeight); + C2StreamPictureSizeInfo::input inputSize(0u, nWidth, nHeight); std::vector configParam{&inputSize}; c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index ff2419d03b..c0195bfc94 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -186,7 +186,7 @@ public: * MediaCodec behavior. */ virtual status_t registerCsd( - const C2StreamCsdInfo::output * /* csd */, + const C2StreamInitDataInfo::output * /* csd */, size_t * /* index */, sp * /* clientBuffer */) = 0; @@ -1187,7 +1187,7 @@ public: } status_t registerCsd( - const C2StreamCsdInfo::output *csd, + const C2StreamInitDataInfo::output *csd, size_t *index, sp *clientBuffer) final { sp c2Buffer; @@ -1286,7 +1286,7 @@ public: } status_t registerCsd( - const C2StreamCsdInfo::output *csd, + const C2StreamInitDataInfo::output *csd, size_t *index, sp *clientBuffer) final { sp newBuffer = new LocalLinearBuffer( @@ -2153,7 +2153,7 @@ status_t CCodecBufferChannel::start( 1 << C2PlatformAllocatorStore::BUFFERQUEUE); if (inputFormat != nullptr) { - bool graphic = (iStreamFormat.value == C2FormatVideo); + bool graphic = (iStreamFormat.value == C2BufferData::GRAPHIC); std::shared_ptr pool; { Mutexed::Locked pools(mBlockPools); @@ -2274,7 +2274,7 @@ status_t CCodecBufferChannel::start( outputGeneration = output->generation; } - bool graphic = (oStreamFormat.value == C2FormatVideo); + bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC); C2BlockPool::local_id_t outputPoolId_; { @@ -2447,7 +2447,7 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { return OK; } - C2StreamFormatConfig::output oStreamFormat(0u); + C2StreamBufferTypeSetting::output oStreamFormat(0u); c2_status_t err = mComponent->query({ &oStreamFormat }, {}, C2_DONT_BLOCK, nullptr); if (err != C2_OK) { return UNKNOWN_ERROR; @@ -2734,7 +2734,7 @@ bool CCodecBufferChannel::handleWork( // TODO: properly translate these to metadata switch (info->coreIndex().coreIndex()) { case C2StreamPictureTypeMaskInfo::CORE_INDEX: - if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2PictureTypeKeyFrame) { + if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2Config::SYNC_FRAME) { flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME; } break; diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index 0a6a717163..6da131fd45 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -101,7 +101,7 @@ ALookup sColorMatricesSf = { { C2Color::MATRIX_BT709, ColorAspects::MatrixBT709_5 }, { C2Color::MATRIX_FCC47_73_682, ColorAspects::MatrixBT470_6M }, { C2Color::MATRIX_BT601, ColorAspects::MatrixBT601_6 }, - { C2Color::MATRIX_SMPTE240M, ColorAspects::MatrixSMPTE240M }, + { C2Color::MATRIX_240M, ColorAspects::MatrixSMPTE240M }, { C2Color::MATRIX_BT2020, ColorAspects::MatrixBT2020 }, { C2Color::MATRIX_BT2020_CONSTANT, ColorAspects::MatrixBT2020Constant }, { C2Color::MATRIX_OTHER, ColorAspects::MatrixOther }, @@ -855,19 +855,19 @@ bool C2Mapper::map( switch (primaries) { case C2Color::PRIMARIES_BT601_525: - *dataSpace |= (matrix == C2Color::MATRIX_SMPTE240M + *dataSpace |= (matrix == C2Color::MATRIX_240M || matrix == C2Color::MATRIX_BT709) ? HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED : HAL_DATASPACE_STANDARD_BT601_525; break; case C2Color::PRIMARIES_BT601_625: - *dataSpace |= (matrix == C2Color::MATRIX_SMPTE240M + *dataSpace |= (matrix == C2Color::MATRIX_240M || matrix == C2Color::MATRIX_BT709) ? HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED : HAL_DATASPACE_STANDARD_BT601_625; break; case C2Color::PRIMARIES_BT2020: - *dataSpace |= (matrix == C2Color::MATRIX_BT2020CONSTANT + *dataSpace |= (matrix == C2Color::MATRIX_BT2020_CONSTANT ? HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE : HAL_DATASPACE_STANDARD_BT2020); break; diff --git a/media/codec2/tests/C2ComponentInterface_test.cpp b/media/codec2/tests/C2ComponentInterface_test.cpp index e9079646c9..67f733de21 100644 --- a/media/codec2/tests/C2ComponentInterface_test.cpp +++ b/media/codec2/tests/C2ComponentInterface_test.cpp @@ -182,9 +182,9 @@ template std::unique_ptr makeParam() { return std::make_unique(); } -template <> std::unique_ptr makeParam() { +template <> std::unique_ptr makeParam() { // TODO(hiroh): Set more precise length. - return C2PortMimeConfig::input::AllocUnique(100); + return C2PortMediaTypeSetting::input::AllocUnique(100); } #define TRACED_FAILURE(func) \ @@ -323,17 +323,17 @@ void C2CompIntfTest::configWritableParamInvalidValue(const T &newParam) { EXPECT_EQ(C2SettingResult::BAD_VALUE, failures[0]->failure); } -// There is only used enum type for the field type, that is C2DomainKind. +// There is only used enum type for the field type, that is C2Component::domain_t. // If another field type is added, it is necessary to add function for that. template <> void C2CompIntfTest::getTestValues( const C2FieldSupportedValues &validValueInfos, - std::vector *const validValues, - std::vector *const invalidValues) { + std::vector *const validValues, + std::vector *const invalidValues) { UNUSED(validValueInfos); - validValues->emplace_back(C2DomainVideo); - validValues->emplace_back(C2DomainAudio); - validValues->emplace_back(C2DomainOther); + validValues->emplace_back(C2Component::DOMAIN_VIDEO); + validValues->emplace_back(C2Component::DOMAIN_AUDIO); + validValues->emplace_back(C2Component::DOMAIN_OTHER); // There is no invalid value. UNUSED(invalidValues); @@ -634,20 +634,20 @@ void C2CompIntfTest::testMain(std::shared_ptr intf, std::vector> supportedParams; ASSERT_EQ(C2_OK, mIntf->querySupportedParams_nb(&supportedParams)); - EACH_TEST_SELF(C2ComponentLatencyInfo, TEST_U32_WRITABLE_FIELD); - EACH_TEST_SELF(C2ComponentTemporalInfo, TEST_U32_WRITABLE_FIELD); - EACH_TEST_INPUT(C2PortLatencyInfo, TEST_U32_WRITABLE_FIELD); - EACH_TEST_OUTPUT(C2PortLatencyInfo, TEST_U32_WRITABLE_FIELD); - EACH_TEST_INPUT(C2StreamFormatConfig, TEST_U32_WRITABLE_FIELD); - EACH_TEST_OUTPUT(C2StreamFormatConfig, TEST_U32_WRITABLE_FIELD); - EACH_TEST_INPUT(C2PortStreamCountConfig, TEST_U32_WRITABLE_FIELD); - EACH_TEST_OUTPUT(C2PortStreamCountConfig, TEST_U32_WRITABLE_FIELD); + EACH_TEST_SELF(C2ActualPipelineDelayTuning, TEST_U32_WRITABLE_FIELD); + EACH_TEST_SELF(C2ComponentAttributesSetting, TEST_U32_WRITABLE_FIELD); + EACH_TEST_INPUT(C2PortActualDelayTuning, TEST_U32_WRITABLE_FIELD); + EACH_TEST_OUTPUT(C2PortActualDelayTuning, TEST_U32_WRITABLE_FIELD); + EACH_TEST_INPUT(C2StreamBufferTypeSetting, TEST_U32_WRITABLE_FIELD); + EACH_TEST_OUTPUT(C2StreamBufferTypeSetting, TEST_U32_WRITABLE_FIELD); + EACH_TEST_INPUT(C2PortStreamCountTuning, TEST_U32_WRITABLE_FIELD); + EACH_TEST_OUTPUT(C2PortStreamCountTuning, TEST_U32_WRITABLE_FIELD); - EACH_TEST_SELF(C2ComponentDomainInfo, TEST_ENUM_WRITABLE_FIELD); + EACH_TEST_SELF(C2ComponentDomainSetting, TEST_ENUM_WRITABLE_FIELD); // TODO(hiroh): Support parameters based on uint32_t[] and char[]. - // EACH_TEST_INPUT(C2PortMimeConfig, TEST_STRING_WRITABLE_FIELD); - // EACH_TEST_OUTPUT(C2PortMimeConfig, TEST_STRING_WRITABLE_FIELD); + // EACH_TEST_INPUT(C2PortMediaTypeSetting, TEST_STRING_WRITABLE_FIELD); + // EACH_TEST_OUTPUT(C2PortMediaTypeSetting, TEST_STRING_WRITABLE_FIELD); // EACH_TEST_INPUT(C2StreamMimeConfig, TEST_STRING_WRITABLE_FIELD); // EACH_TEST_OUTPUT(C2StreamMimeConfig, TEST_STRING_WRITABLE_FIELD); @@ -656,10 +656,10 @@ void C2CompIntfTest::testMain(std::shared_ptr intf, // EACH_TEST_SELF(C2ReadOnlyParamsInfo, TEST_U32ARRAY_WRITABLE_FIELD); // EACH_TEST_SELF(C2RequestedInfosInfo, TEST_U32ARRAY_WRITABLE_FIELD); - EACH_TEST_INPUT(C2VideoSizeStreamInfo, TEST_VSSTRUCT_WRITABLE_FIELD); - EACH_TEST_OUTPUT(C2VideoSizeStreamInfo, TEST_VSSTRUCT_WRITABLE_FIELD); - EACH_TEST_INPUT(C2VideoSizeStreamTuning, TEST_VSSTRUCT_WRITABLE_FIELD); - EACH_TEST_OUTPUT(C2VideoSizeStreamTuning, TEST_VSSTRUCT_WRITABLE_FIELD); + EACH_TEST_INPUT(C2StreamPictureSizeInfo, TEST_VSSTRUCT_WRITABLE_FIELD); + EACH_TEST_OUTPUT(C2StreamPictureSizeInfo, TEST_VSSTRUCT_WRITABLE_FIELD); + EACH_TEST_INPUT(C2StreamPictureSizeInfo, TEST_VSSTRUCT_WRITABLE_FIELD); + EACH_TEST_OUTPUT(C2StreamPictureSizeInfo, TEST_VSSTRUCT_WRITABLE_FIELD); EACH_TEST_INPUT(C2MaxVideoSizeHintPortSetting, TEST_VSSTRUCT_WRITABLE_FIELD); EACH_TEST_OUTPUT(C2MaxVideoSizeHintPortSetting, TEST_VSSTRUCT_WRITABLE_FIELD); diff --git a/media/codec2/tests/C2SampleComponent_test.cpp b/media/codec2/tests/C2SampleComponent_test.cpp index cd354adf6a..9956834e30 100644 --- a/media/codec2/tests/C2SampleComponent_test.cpp +++ b/media/codec2/tests/C2SampleComponent_test.cpp @@ -152,7 +152,7 @@ public: std::unordered_map mMyParams; - C2ComponentDomainInfo mDomainInfo; + C2ComponentDomainSetting mDomainInfo; MyComponentInstance() { mMyParams.insert({mDomainInfo.index(), mDomainInfo}); @@ -187,12 +187,12 @@ public: c2_blocking_t mayBlock) const override { (void)mayBlock; for (C2FieldSupportedValuesQuery &query : fields) { - if (query.field() == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::value)) { + if (query.field() == C2ParamField(&mDomainInfo, &C2ComponentDomainSetting::value)) { query.values = C2FieldSupportedValues( false /* flag */, &mDomainInfo.value //, - //{(int32_t)C2DomainVideo} + //{(int32_t)C2Component::DOMAIN_VIDEO} ); query.status = C2_OK; } else { @@ -391,20 +391,20 @@ void dumpDesc(const C2ParamDescriptor &pd) { } TEST_F(C2SampleComponentTest, ReflectorTest) { - C2ComponentDomainInfo domainInfo; + C2ComponentDomainSetting domainInfo; std::shared_ptr myComp(new MyComponentInstance); std::shared_ptr comp = myComp; std::unique_ptr desc{ - myComp->getParamReflector()->describe(C2ComponentDomainInfo::CORE_INDEX)}; + myComp->getParamReflector()->describe(C2ComponentDomainSetting::CORE_INDEX)}; dumpStruct(*desc); std::vector query = { - { C2ParamField(&domainInfo, &C2ComponentDomainInfo::value), + { C2ParamField(&domainInfo, &C2ComponentDomainSetting::value), C2FieldSupportedValuesQuery::CURRENT }, - C2FieldSupportedValuesQuery(C2ParamField(&domainInfo, &C2ComponentDomainInfo::value), + C2FieldSupportedValuesQuery(C2ParamField(&domainInfo, &C2ComponentDomainSetting::value), C2FieldSupportedValuesQuery::CURRENT), - C2FieldSupportedValuesQuery::Current(C2ParamField(&domainInfo, &C2ComponentDomainInfo::value)), + C2FieldSupportedValuesQuery::Current(C2ParamField(&domainInfo, &C2ComponentDomainSetting::value)), }; EXPECT_EQ(C2_OK, comp->querySupportedValues_vb(query, C2_DONT_BLOCK)); diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 32588a5ed1..e075849a7e 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -712,8 +712,8 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( } uint32_t mediaTypeIndex = - traits->kind == C2Component::KIND_ENCODER ? C2PortMimeConfig::output::PARAM_TYPE - : C2PortMimeConfig::input::PARAM_TYPE; + traits->kind == C2Component::KIND_ENCODER ? C2PortMediaTypeSetting::output::PARAM_TYPE + : C2PortMediaTypeSetting::input::PARAM_TYPE; std::vector> params; res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms); if (res != C2_OK) { @@ -724,7 +724,7 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( ALOGD("failed to query interface: unexpected vector size: %zu", params.size()); return mInit; } - C2PortMimeConfig *mediaTypeConfig = C2PortMimeConfig::From(params[0].get()); + C2PortMediaTypeSetting *mediaTypeConfig = C2PortMediaTypeSetting::From(params[0].get()); if (mediaTypeConfig == nullptr) { ALOGD("failed to query media type"); return mInit; -- GitLab From f5e5c83f1a18c899f00fed3ae5c9f81bc364def6 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 21 Feb 2019 11:36:05 -0800 Subject: [PATCH 0996/1530] CCodec: calculate max dequeued buffer count Bug: 118397206 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I6a9089feae3b91c3a91c784d4aa903bbf9277846 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 12 +++++++++++- media/codec2/sfplugin/CCodecBufferChannel.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index ff2419d03b..7e8d280821 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1592,6 +1592,7 @@ CCodecBufferChannel::CCodecBufferChannel( mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), mInputMetEos(false) { + mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth; Mutexed>::Locked buffers(mInputBuffers); buffers->reset(new DummyInputBuffers("")); } @@ -2269,8 +2270,12 @@ status_t CCodecBufferChannel::start( uint32_t outputGeneration; { Mutexed::Locked output(mOutputSurface); + output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth; outputSurface = output->surface ? output->surface->getIGraphicBufferProducer() : nullptr; + if (outputSurface) { + output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); + } outputGeneration = output->generation; } @@ -2638,6 +2643,11 @@ bool CCodecBufferChannel::handleWork( mReorderStash.lock()->setDepth(reorderDepth.value); ALOGV("[%s] onWorkDone: updated reorder depth to %u", mName, reorderDepth.value); + Mutexed::Locked output(mOutputSurface); + output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth; + if (output->surface) { + output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); + } } else { ALOGD("[%s] onWorkDone: failed to read reorder depth", mName); } @@ -2813,7 +2823,6 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { sp producer; if (newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - newSurface->setMaxDequeuedBufferCount(mNumOutputSlots + kRenderingDepth); producer = newSurface->getIGraphicBufferProducer(); producer->setGenerationNumber(generation); } else { @@ -2841,6 +2850,7 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { { Mutexed::Locked output(mOutputSurface); + newSurface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); output->surface = newSurface; output->generation = generation; } diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 9ce886aa38..1ea29b426f 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -250,6 +250,7 @@ private: struct OutputSurface { sp surface; uint32_t generation; + int maxDequeueBuffers; }; Mutexed mOutputSurface; -- GitLab From cac53857799091a1922d0e5aa39e97c1bf53800f Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 20 Feb 2019 10:59:10 -0800 Subject: [PATCH 0997/1530] Codec2Buffer: Initialize mediaImage to zero in GraphicView2MediaImageConverter MediaImage2 structure was initialized only for valid planes, but entire structure was compared to signal change in output format. This was resulting in multiple change in output formats signalled in dequeueOutputBuffer() Bug: 124799741 Test: Manually verified that only one ouput format changed is signalled\ per clip using AMediaCodec based decoder application Change-Id: Ib4139fc80f3493e3cb2b019d3890c50b05076469 --- media/codec2/sfplugin/Codec2Buffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 13b63c9f93..0fd5731f42 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -224,6 +224,7 @@ public: mInitCheck = BAD_VALUE; return; } + memset(mediaImage, 0, sizeof(*mediaImage)); mAllocatedDepth = layout.planes[0].allocatedDepth; uint32_t bitDepth = layout.planes[0].bitDepth; -- GitLab From ae9700287cb9ae12c1bb2088bf91c9a3739195f0 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 29 Jan 2019 14:25:04 -0800 Subject: [PATCH 0998/1530] audio policy: remove glitch when registering dynamic policies Bypass systematic playback client route evaluation when connecting a remote submix output device used by a recorder dysnamic policy as no rerouting is needed in this case. Bug: 115429872 Bug: 123986127 Test: play music with GPM on Android TV and use the BLE remote Test: use the BLE remote on Android TV Change-Id: I285d416f54ff398d23742086fcd8c9f511cd289b --- .../managerdefault/AudioPolicyManager.cpp | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 721a65853f..5b752ead9a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -209,7 +209,26 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return BAD_VALUE; } - checkForDeviceAndOutputChanges([&]() { + // No need to evaluate playback routing when connecting a remote submix + // output device used by a dynamic policy of type recorder as no + // playback use case is affected. + bool doCheckForDeviceAndOutputChanges = true; + if (device->type() == AUDIO_DEVICE_OUT_REMOTE_SUBMIX + && strncmp(device_address, "0", AUDIO_DEVICE_MAX_ADDRESS_LEN) != 0) { + for (audio_io_handle_t output : outputs) { + sp desc = mOutputs.valueFor(output); + if (desc->mPolicyMix != nullptr + && desc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS + && strncmp(device_address, + desc->mPolicyMix->mDeviceAddress.string(), + AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { + doCheckForDeviceAndOutputChanges = false; + break; + } + } + } + + auto checkCloseOutputs = [&]() { // outputs must be closed after checkOutputForAllStrategies() is executed if (!outputs.isEmpty()) { for (audio_io_handle_t output : outputs) { @@ -218,7 +237,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // been opened by checkOutputsForDevice() to query dynamic parameters if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && - (desc->mDirectOpenCount == 0))) { + (desc->mDirectOpenCount == 0))) { closeOutput(output); } } @@ -226,7 +245,13 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return true; } return false; - }); + }; + + if (doCheckForDeviceAndOutputChanges) { + checkForDeviceAndOutputChanges(checkCloseOutputs); + } else { + checkCloseOutputs(); + } if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) { DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/); -- GitLab From ae1f662a30fe5aea14a020111fe245b2fad3e5b5 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 21 Feb 2019 15:20:05 -0800 Subject: [PATCH 0999/1530] libaudiohal: Allow get/setMasterVolume invocation on IDevice get/setMasterVolume are defined on IDevice, but DeviceHalHidl was restricting calls to it only for IPrimaryDevice. This seems like a bug lingering from the initial implementation of the shim. Test: make Change-Id: Ia028959690a5ab71aade14d12d5c12488dc83e41 --- media/libaudiohal/impl/DeviceHalHidl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index a1e869f538..b25f82efbc 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -123,15 +123,13 @@ status_t DeviceHalHidl::setVoiceVolume(float volume) { status_t DeviceHalHidl::setMasterVolume(float volume) { if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; - return processReturn("setMasterVolume", mPrimaryDevice->setMasterVolume(volume)); + return processReturn("setMasterVolume", mDevice->setMasterVolume(volume)); } status_t DeviceHalHidl::getMasterVolume(float *volume) { if (mDevice == 0) return NO_INIT; - if (mPrimaryDevice == 0) return INVALID_OPERATION; Result retval; - Return ret = mPrimaryDevice->getMasterVolume( + Return ret = mDevice->getMasterVolume( [&](Result r, float v) { retval = r; if (retval == Result::OK) { -- GitLab From 495d7b1986720f3716bd47cfd838a20f820a08c5 Mon Sep 17 00:00:00 2001 From: Dean Wheatley Date: Wed, 13 Feb 2019 07:23:46 +1100 Subject: [PATCH 1000/1530] Fix EAC3 bsid parsing Comply with ETSI TS 102 366 V1.4.1 E.1.3.1.6 Bug: 125456292 Test: see repro steps Change-Id: Ieb8414d83e63a3c968409c31e8130ada7238c5d4 --- media/extractors/mp4/MPEG4Extractor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 4b4d767bb5..c776c519e7 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -3021,8 +3021,10 @@ status_t MPEG4Extractor::parseEAC3SpecificBox(off64_t offset) { } unsigned bsid = br.getBits(5); - if (bsid < 8) { - ALOGW("Incorrect bsid in EAC3 header. Possibly AC-3?"); + if (bsid == 9 || bsid == 10) { + ALOGW("EAC3 stream (bsid=%d) may be silenced by the decoder", bsid); + } else if (bsid > 16) { + ALOGE("EAC3 stream (bsid=%d) is not compatible with ETSI TS 102 366 v1.4.1", bsid); delete[] chunk; return ERROR_MALFORMED; } -- GitLab From 539311942bdbac65fbf7f2fc1390b6b9ef850a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 6 Nov 2018 17:11:17 +0100 Subject: [PATCH 1001/1530] audiopolicy: moves Stream Volume Curves to Engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL moves the volume curves to the engine. The configuration files of volume are now owned by the engine, that is intended to become vendor specific. Test: build, dumpsys media.audio_policy and checks the volume curves. Bug: 124767636 Change-Id: I9e3b256d2eb89c8eac6b282db0e59ec3af47d76d Signed-off-by: François Gaffie --- .../common/managerdefinitions/Android.bp | 1 - .../include/AudioPolicyConfig.h | 13 +- .../include/IVolumeCurves.h | 44 ++++ .../include/IVolumeCurvesCollection.h | 54 ---- .../managerdefinitions/include/VolumeCurve.h | 239 ------------------ .../managerdefinitions/src/Serializer.cpp | 88 ------- .../config/audio_policy_configuration.xml | 6 +- .../audio_policy_configuration_generic.xml | 6 +- .../engine/common/include/EngineBase.h | 18 +- .../common/include/StreamVolumeCurves.h | 95 +++++++ .../engine/common/include/VolumeCurve.h | 192 ++++++++++++++ .../engine/common/src/EngineBase.cpp | 49 ++++ .../engine/common/src/EngineDefaultConfig.h | 123 ++++++++- .../engine/common/src/StreamVolumeCurves.cpp | 47 ++++ .../common}/src/VolumeCurve.cpp | 51 ++-- .../engine/config/include/EngineConfig.h | 22 ++ .../engine/config/src/EngineConfig.cpp | 126 +++++++++ .../interface/AudioPolicyManagerInterface.h | 16 ++ .../interface/AudioPolicyManagerObserver.h | 3 - .../audiopolicy/engineconfigurable/Android.mk | 2 + .../config/example/Android.mk | 18 ++ .../audio_policy_engine_configuration.xml | 2 + ...o_policy_engine_default_stream_volumes.xml | 136 ++++++++++ .../audio_policy_engine_stream_volumes.xml | 231 +++++++++++++++++ .../engineconfigurable/src/Engine.cpp | 2 +- services/audiopolicy/enginedefault/Android.mk | 2 + .../enginedefault/config/example/Android.mk | 20 +- .../audio_policy_engine_configuration.xml | 2 + ...o_policy_engine_default_stream_volumes.xml | 136 ++++++++++ .../audio_policy_engine_stream_volumes.xml | 231 +++++++++++++++++ .../audiopolicy/enginedefault/src/Engine.cpp | 29 --- .../audiopolicy/enginedefault/src/Engine.h | 2 - .../managerdefault/AudioPolicyManager.cpp | 80 +++--- .../managerdefault/AudioPolicyManager.h | 18 +- 34 files changed, 1590 insertions(+), 514 deletions(-) create mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h create mode 100644 services/audiopolicy/engine/common/include/StreamVolumeCurves.h create mode 100644 services/audiopolicy/engine/common/include/VolumeCurve.h create mode 100644 services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp rename services/audiopolicy/{common/managerdefinitions => engine/common}/src/VolumeCurve.cpp (68%) create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp index e5ebab7e16..c9037a1daa 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.bp +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -19,7 +19,6 @@ cc_library_static { "src/Serializer.cpp", "src/SoundTriggerSession.cpp", "src/TypeConverter.cpp", - "src/VolumeCurve.cpp", ], shared_libs: [ "libcutils", diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index d52eb3d35c..2264d8f718 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -40,13 +39,11 @@ public: AudioPolicyConfig(HwModuleCollection &hwModules, DeviceVector &availableOutputDevices, DeviceVector &availableInputDevices, - sp &defaultOutputDevice, - VolumeCurvesCollection *volumes = nullptr) + sp &defaultOutputDevice) : mHwModules(hwModules), mAvailableOutputDevices(availableOutputDevices), mAvailableInputDevices(availableInputDevices), mDefaultOutputDevice(defaultOutputDevice), - mVolumeCurves(volumes), mIsSpeakerDrcEnabled(false) {} @@ -58,13 +55,6 @@ public: mSource = file; } - void setVolumes(const VolumeCurvesCollection &volumes) - { - if (mVolumeCurves != nullptr) { - *mVolumeCurves = volumes; - } - } - void setHwModules(const HwModuleCollection &hwModules) { mHwModules = hwModules; @@ -182,7 +172,6 @@ private: DeviceVector &mAvailableOutputDevices; DeviceVector &mAvailableInputDevices; sp &mDefaultOutputDevice; - VolumeCurvesCollection *mVolumeCurves; // TODO: remove when legacy conf file is removed. true on devices that use DRC on the // DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly. // Note: remove also speaker_drc_enabled from global configuration of XML config file. diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h new file mode 100644 index 0000000000..93022fb2a0 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { + +class IVolumeCurves +{ +public: + virtual ~IVolumeCurves() = default; + + virtual void clearCurrentVolumeIndex() = 0; + virtual void addCurrentVolumeIndex(audio_devices_t device, int index) = 0; + virtual bool canBeMuted() const = 0; + virtual int getVolumeIndexMin() const = 0; + virtual int getVolumeIndex(audio_devices_t device) const = 0; + virtual int getVolumeIndexMax() const = 0; + virtual float volIndexToDb(device_category device, int indexInUi) const = 0; + virtual bool hasVolumeIndexForDevice(audio_devices_t device) const = 0; + virtual status_t initVolume(int indexMin, int indexMax) = 0; + virtual void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const = 0; +}; + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h deleted file mode 100644 index 750da55519..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace android { - -class IVolumeCurvesCollection -{ -public: - virtual ~IVolumeCurvesCollection() = default; - - virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) = 0; - virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, - int index) = 0; - virtual bool canBeMuted(audio_stream_type_t stream) = 0; - virtual int getVolumeIndexMin(audio_stream_type_t stream) const = 0; - virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) = 0; - virtual int getVolumeIndexMax(audio_stream_type_t stream) const = 0; - virtual float volIndexToDb(audio_stream_type_t stream, device_category device, - int indexInUi) const = 0; - virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) = 0; - - virtual void initializeVolumeCurves(bool /*isSpeakerDrcEnabled*/) {} - virtual void switchVolumeCurve(audio_stream_type_t src, audio_stream_type_t dst) = 0; - virtual void restoreOriginVolumeCurve(audio_stream_type_t stream) - { - switchVolumeCurve(stream, stream); - } - virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, - audio_devices_t device) const = 0; - - virtual void dump(String8 *dst) const = 0; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h deleted file mode 100644 index 76ec198d02..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "IVolumeCurvesCollection.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct CurvePoint -{ - CurvePoint() {} - CurvePoint(int index, int attenuationInMb) : - mIndex(index), mAttenuationInMb(attenuationInMb) {} - uint32_t mIndex; - int mAttenuationInMb; -}; - -inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) -{ - return lhs.mIndex < rhs.mIndex; -} - -// A volume curve for a given use case and device category -// It contains of list of points of this curve expressing the attenuation in Millibels for -// a given volume index from 0 to 100 -class VolumeCurve : public RefBase -{ -public: - VolumeCurve(device_category device, audio_stream_type_t stream) : - mDeviceCategory(device), mStreamType(stream) {} - - device_category getDeviceCategory() const { return mDeviceCategory; } - audio_stream_type_t getStreamType() const { return mStreamType; } - - void add(const CurvePoint &point) { mCurvePoints.add(point); } - - float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; - - void dump(String8 *result) const; - -private: - SortedVector mCurvePoints; - device_category mDeviceCategory; - audio_stream_type_t mStreamType; -}; - -// Volume Curves for a given use case indexed by device category -class VolumeCurvesForStream : public KeyedVector > -{ -public: - VolumeCurvesForStream() : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) - { - mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); - } - - sp getCurvesFor(device_category device) const - { - if (indexOfKey(device) < 0) { - return 0; - } - return valueFor(device); - } - - int getVolumeIndex(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME - if (mIndexCur.indexOfKey(device) < 0) { - device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; - } - return mIndexCur.valueFor(device); - } - - bool canBeMuted() const { return mCanBeMuted; } - void clearCurrentVolumeIndex() { mIndexCur.clear(); } - void addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); } - - void setVolumeIndexMin(int volIndexMin) { mIndexMin = volIndexMin; } - int getVolumeIndexMin() const { return mIndexMin; } - - void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; } - int getVolumeIndexMax() const { return mIndexMax; } - - bool hasVolumeIndexForDevice(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - return mIndexCur.indexOfKey(device) >= 0; - } - - const sp getOriginVolumeCurve(device_category deviceCategory) const - { - ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); - return mOriginVolumeCurves.valueFor(deviceCategory); - } - void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) - { - ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); - replaceValueFor(deviceCategory, volumeCurve); - } - - ssize_t add(const sp &volumeCurve) - { - device_category deviceCategory = volumeCurve->getDeviceCategory(); - ssize_t index = indexOfKey(deviceCategory); - if (index < 0) { - // Keep track of original Volume Curves per device category in order to switch curves. - mOriginVolumeCurves.add(deviceCategory, volumeCurve); - return KeyedVector::add(deviceCategory, volumeCurve); - } - return index; - } - - float volIndexToDb(device_category deviceCat, int indexInUi) const - { - sp vc = getCurvesFor(deviceCat); - if (vc != 0) { - return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); - } else { - ALOGE("Invalid device category %d for Volume Curve", deviceCat); - return 0.0f; - } - } - - void dump(String8 *dst, int spaces, bool curvePoints = false) const; - -private: - KeyedVector > mOriginVolumeCurves; - KeyedVector mIndexCur; /**< current volume index per device. */ - int mIndexMin; /**< min volume index. */ - int mIndexMax; /**< max volume index. */ - bool mCanBeMuted; /**< true is the stream can be muted. */ -}; - -// Collection of Volume Curves indexed by use case -class VolumeCurvesCollection : public KeyedVector, - public IVolumeCurvesCollection -{ -public: - VolumeCurvesCollection() - { - // Create an empty collection of curves - for (ssize_t i = 0 ; i < AUDIO_STREAM_CNT; i++) { - audio_stream_type_t stream = static_cast(i); - KeyedVector::add(stream, VolumeCurvesForStream()); - } - } - - // Once XML has been parsed, must be call first to sanity check table and initialize indexes - virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) - { - editValueAt(stream).setVolumeIndexMin(indexMin); - editValueAt(stream).setVolumeIndexMax(indexMax); - return NO_ERROR; - } - virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) - { - editCurvesFor(stream).clearCurrentVolumeIndex(); - } - virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index) - { - editCurvesFor(stream).addCurrentVolumeIndex(device, index); - } - virtual bool canBeMuted(audio_stream_type_t stream) { return getCurvesFor(stream).canBeMuted(); } - - virtual int getVolumeIndexMin(audio_stream_type_t stream) const - { - return getCurvesFor(stream).getVolumeIndexMin(); - } - virtual int getVolumeIndexMax(audio_stream_type_t stream) const - { - return getCurvesFor(stream).getVolumeIndexMax(); - } - virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) - { - return getCurvesFor(stream).getVolumeIndex(device); - } - virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) - { - const VolumeCurvesForStream &sourceCurves = getCurvesFor(streamSrc); - VolumeCurvesForStream &dstCurves = editCurvesFor(streamDst); - ALOG_ASSERT(sourceCurves.size() == dstCurves.size(), "device category not aligned"); - for (size_t index = 0; index < sourceCurves.size(); index++) { - device_category cat = sourceCurves.keyAt(index); - dstCurves.setVolumeCurve(cat, sourceCurves.getOriginVolumeCurve(cat)); - } - } - virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const - { - return getCurvesFor(stream).volIndexToDb(cat, indexInUi); - } - virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, - audio_devices_t device) const - { - return getCurvesFor(stream).hasVolumeIndexForDevice(device); - } - - void dump(String8 *dst) const override; - - ssize_t add(const sp &volumeCurve) - { - audio_stream_type_t streamType = volumeCurve->getStreamType(); - return editCurvesFor(streamType).add(volumeCurve); - } - VolumeCurvesForStream &editCurvesFor(audio_stream_type_t stream) - { - ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); - return editValueAt(stream); - } - const VolumeCurvesForStream &getCurvesFor(audio_stream_type_t stream) const - { - ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); - return valueFor(stream); - } -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 98d375c115..81d3968c76 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -201,25 +201,6 @@ struct GlobalConfigTraits static status_t deserialize(const xmlNode *root, AudioPolicyConfig *config); }; -struct VolumeTraits : public AndroidCollectionTraits -{ - static constexpr const char *tag = "volume"; - static constexpr const char *collectionTag = "volumes"; - static constexpr const char *volumePointTag = "point"; - static constexpr const char *referenceTag = "reference"; - - struct Attributes - { - static constexpr const char *stream = "stream"; - static constexpr const char *deviceCategory = "deviceCategory"; - static constexpr const char *reference = "ref"; - static constexpr const char *referenceName = "name"; - }; - - static Return deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); - // No Children -}; - struct SurroundSoundTraits { static constexpr const char *tag = "surroundSound"; @@ -703,67 +684,6 @@ status_t GlobalConfigTraits::deserialize(const xmlNode *root, AudioPolicyConfig return NO_ERROR; } -Return VolumeTraits::deserialize(const xmlNode *cur, - PtrSerializingCtx /*serializingContext*/) -{ - std::string streamTypeLiteral = getXmlAttribute(cur, Attributes::stream); - if (streamTypeLiteral.empty()) { - ALOGE("%s: No %s found", __func__, Attributes::stream); - return Status::fromStatusT(BAD_VALUE); - } - audio_stream_type_t streamType; - if (!StreamTypeConverter::fromString(streamTypeLiteral, streamType)) { - ALOGE("%s: Invalid %s", __func__, Attributes::stream); - return Status::fromStatusT(BAD_VALUE); - } - std::string deviceCategoryLiteral = getXmlAttribute(cur, Attributes::deviceCategory); - if (deviceCategoryLiteral.empty()) { - ALOGE("%s: No %s found", __func__, Attributes::deviceCategory); - return Status::fromStatusT(BAD_VALUE); - } - device_category deviceCategory; - if (!DeviceCategoryConverter::fromString(deviceCategoryLiteral, deviceCategory)) { - ALOGE("%s: Invalid %s=%s", __func__, Attributes::deviceCategory, - deviceCategoryLiteral.c_str()); - return Status::fromStatusT(BAD_VALUE); - } - - std::string referenceName = getXmlAttribute(cur, Attributes::reference); - const xmlNode *ref = NULL; - if (!referenceName.empty()) { - ref = getReference(cur->parent, referenceName); - if (ref == NULL) { - ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); - return Status::fromStatusT(BAD_VALUE); - } - } - - Element volCurve = new VolumeCurve(deviceCategory, streamType); - - for (const xmlNode *child = referenceName.empty() ? cur->xmlChildrenNode : ref->xmlChildrenNode; - child != NULL; child = child->next) { - if (!xmlStrcmp(child->name, reinterpret_cast(volumePointTag))) { - auto pointDefinition = make_xmlUnique(xmlNodeListGetString( - child->doc, child->xmlChildrenNode, 1)); - if (pointDefinition == nullptr) { - return Status::fromStatusT(BAD_VALUE); - } - ALOGV("%s: %s=%s", - __func__, tag, reinterpret_cast(pointDefinition.get())); - std::vector point; - collectionFromString>( - reinterpret_cast(pointDefinition.get()), point, ","); - if (point.size() != 2) { - ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, - reinterpret_cast(pointDefinition.get())); - return Status::fromStatusT(BAD_VALUE); - } - volCurve->add(CurvePoint(point[0], point[1])); - } - } - return volCurve; -} - status_t SurroundSoundTraits::deserialize(const xmlNode *root, AudioPolicyConfig *config) { config->setDefaultSurroundFormats(); @@ -851,14 +771,6 @@ status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig } config->setHwModules(modules); - // deserialize volume section - VolumeTraits::Collection volumes; - status = deserializeCollection(root, &volumes, config); - if (status != NO_ERROR) { - return status; - } - config->setVolumes(volumes); - // Global Configuration GlobalConfigTraits::deserialize(root, config); diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index 42c52deec9..b4cc1d3936 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -191,7 +191,11 @@ - + diff --git a/services/audiopolicy/config/audio_policy_configuration_generic.xml b/services/audiopolicy/config/audio_policy_configuration_generic.xml index 40dcc22b58..9ad609df2b 100644 --- a/services/audiopolicy/config/audio_policy_configuration_generic.xml +++ b/services/audiopolicy/config/audio_policy_configuration_generic.xml @@ -30,7 +30,11 @@ - + diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index 5c33fb39b8..bc027e2a3f 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace android { namespace audio_policy { @@ -67,6 +68,10 @@ public: status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override; + VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override; + + VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override; + void dump(String8 *dst) const override; @@ -87,7 +92,16 @@ public: return is_state_in_call(getPhoneState()); } -private: + VolumeSource toVolumeSource(audio_stream_type_t stream) const + { + return static_cast(stream); + } + + status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); + + status_t restoreOriginVolumeCurve(audio_stream_type_t stream); + + private: AudioPolicyManagerObserver *mApmObserver = nullptr; ProductStrategyMap mProductStrategies; @@ -95,6 +109,8 @@ private: /** current forced use configuration. */ audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {}; + + StreamVolumeCurves mStreamVolumeCurves; }; } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h new file mode 100644 index 0000000000..5b0b7d6272 --- /dev/null +++ b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +namespace android { + +class StreamVolumeCurves +{ +public: + StreamVolumeCurves() = default; + + /** + * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves + * from one stream type to another. + * @param src source stream type + * @param dst destination stream type + */ + status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) + { + if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) { + ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst); + return NO_INIT; + } + const VolumeCurves &sourceCurves = getCurvesFor(streamSrc); + VolumeCurves &dstCurves = editCurvesFor(streamDst); + return dstCurves.switchCurvesFrom(sourceCurves); + } + void dump(String8 *dst, int spaces = 0) const; + + void add(const VolumeCurves &curves, audio_stream_type_t streamType) + { + mCurves.emplace(streamType, curves); + } + + bool hasCurvesFor(audio_stream_type_t stream) + { + return mCurves.find(stream) != end(mCurves); + } + + VolumeCurves &editCurvesFor(audio_stream_type_t stream) + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves[stream]; + } + const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves.at(stream); + } + /** + * @brief getVolumeCurvesForStream + * @param stream type for which the volume curves interface is requested + * @return the VolumeCurves for a given stream type. + */ + VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream) + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves[stream]; + } + /** + * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume + * curves for a given stream type + * @param stream for which the volume curves will be restored. + */ + status_t restoreOriginVolumeCurve(audio_stream_type_t stream) + { + if (!hasCurvesFor(stream)) { + ALOGE("%s: No curves defined for streams", __FUNCTION__); + return NO_INIT; + } + return switchVolumeCurve(stream, stream); + } + +private: + std::map mCurves; +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h new file mode 100644 index 0000000000..c2a1da702e --- /dev/null +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "IVolumeCurves.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct CurvePoint +{ + CurvePoint() {} + CurvePoint(int index, int attenuationInMb) : + mIndex(index), mAttenuationInMb(attenuationInMb) {} + uint32_t mIndex; + int mAttenuationInMb; +}; + +inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) +{ + return lhs.mIndex < rhs.mIndex; +} + +// A volume curve for a given use case and device category +// It contains of list of points of this curve expressing the attenuation in Millibels for +// a given volume index from 0 to 100 +class VolumeCurve : public RefBase +{ +public: + VolumeCurve(device_category device) : mDeviceCategory(device) {} + + void add(const CurvePoint &point) { mCurvePoints.add(point); } + + float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; + + void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const; + + device_category getDeviceCategory() const { return mDeviceCategory; } + +private: + const device_category mDeviceCategory; + SortedVector mCurvePoints; +}; + +// Volume Curves for a given use case indexed by device category +class VolumeCurves : public KeyedVector >, + public IVolumeCurves +{ +public: + VolumeCurves(int indexMin = 0, int indexMax = 100) : + mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT) + { + addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); + } + VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) : + mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream) + { + addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); + } + + // Once XML has been parsed, must be call first to sanity check table and initialize indexes + virtual status_t initVolume(int indexMin, int indexMax) + { + mIndexMin = indexMin; + mIndexMax = indexMax; + return NO_ERROR; + } + + sp getCurvesFor(device_category device) const + { + if (indexOfKey(device) < 0) { + return 0; + } + return valueFor(device); + } + + virtual int getVolumeIndex(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME + if (mIndexCur.find(device) == end(mIndexCur)) { + device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; + } + return mIndexCur.at(device); + } + + virtual bool canBeMuted() const { return mCanBeMuted; } + virtual void clearCurrentVolumeIndex() { mIndexCur.clear(); } + void addCurrentVolumeIndex(audio_devices_t device, int index) override + { + mIndexCur[device] = index; + } + + int getVolumeIndexMin() const { return mIndexMin; } + + int getVolumeIndexMax() const { return mIndexMax; } + + bool hasVolumeIndexForDevice(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + return mIndexCur.find(device) != end(mIndexCur); + } + + status_t switchCurvesFrom(const VolumeCurves &referenceCurves) + { + if (size() != referenceCurves.size()) { + ALOGE("%s! device category not aligned, cannot switch", __FUNCTION__); + return BAD_TYPE; + } + for (size_t index = 0; index < size(); index++) { + device_category cat = keyAt(index); + setVolumeCurve(cat, referenceCurves.getOriginVolumeCurve(cat)); + } + return NO_ERROR; + } + status_t restoreOriginVolumeCurve() + { + return switchCurvesFrom(*this); + } + + const sp getOriginVolumeCurve(device_category deviceCategory) const + { + ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); + return mOriginVolumeCurves.valueFor(deviceCategory); + } + void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) + { + ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); + replaceValueFor(deviceCategory, volumeCurve); + } + + ssize_t add(const sp &volumeCurve) + { + device_category deviceCategory = volumeCurve->getDeviceCategory(); + ssize_t index = indexOfKey(deviceCategory); + if (index < 0) { + // Keep track of original Volume Curves per device category in order to switch curves. + mOriginVolumeCurves.add(deviceCategory, volumeCurve); + return KeyedVector::add(deviceCategory, volumeCurve); + } + return index; + } + + virtual float volIndexToDb(device_category deviceCat, int indexInUi) const + { + sp vc = getCurvesFor(deviceCat); + if (vc != 0) { + return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); + } else { + ALOGE("Invalid device category %d for Volume Curve", deviceCat); + return 0.0f; + } + } + + audio_stream_type_t getStreamType() const { return mStream; } + + void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override; + +private: + KeyedVector > mOriginVolumeCurves; + std::map mIndexCur; /**< current volume index per device. */ + /*const*/ int mIndexMin; /**< min volume index. */ + /*const*/ int mIndexMax; /**< max volume index. */ + const bool mCanBeMuted = true; /**< true is the stream can be muted. */ + + const audio_stream_type_t mStream; /**< Keep it for legacy. */ +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 755f2a88e3..2449b61d12 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -55,8 +55,10 @@ status_t EngineBase::setPhoneState(audio_mode_t state) if (!is_state_in_call(oldState) && is_state_in_call(state)) { ALOGV(" Entering call in setPhoneState()"); + switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF); } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { ALOGV(" Exiting call in setPhoneState()"); + restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); } return NO_ERROR; } @@ -108,6 +110,31 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() productStrategies[strategyId] = strategy; } }; + auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) { + for (auto &configVolume : configVolumes) { + audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; + if (configVolume.stream.empty() || + !StreamTypeConverter::fromString(configVolume.stream, streamType)) { + ALOGE("%s: Invalid stream type", __FUNCTION__); + continue; + } + VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax); + for (auto &configCurve : configVolume.volumeCurves) { + device_category deviceCategory = DEVICE_CATEGORY_SPEAKER; + if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, + deviceCategory)) { + ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str()); + continue; + } + sp curve = new VolumeCurve(deviceCategory); + for (auto &point : configCurve.curvePoints) { + curve->add({point.index, point.attenuationInMb}); + } + volumeCurves.add(curve); + } + streamVolumeCollection.add(volumeCurves, streamType); + } + }; auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { @@ -116,6 +143,7 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); + loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves); return result; } @@ -173,9 +201,30 @@ status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &stra return NO_ERROR; } +VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) +{ + return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr)); +} + +VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) +{ + return &mStreamVolumeCurves.getVolumeCurvesForStream(stream); +} + +status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) +{ + return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);; +} + +status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream) +{ + return mStreamVolumeCurves.restoreOriginVolumeCurve(stream); +} + void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); + mStreamVolumeCurves.dump(dst, 2); } } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 3940c0c929..86a59b00ee 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,10 +131,131 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; +const engineConfig::VolumeGroups gVolumeGroups = { + {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, + }, + }, + {"system", "AUDIO_STREAM_SYSTEM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"ring", "AUDIO_STREAM_RING", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"music", "AUDIO_STREAM_MUSIC", 0, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"alarm", "AUDIO_STREAM_ALARM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + }, + }, + {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"tts", "AUDIO_STREAM_TTS", 0, 16, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + }, + }, + {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, + {"patch", "AUDIO_STREAM_PATCH", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, +}; + const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, - {} + {}, + gVolumeGroups }; } // namespace android diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp new file mode 100644 index 0000000000..fe3b0007b6 --- /dev/null +++ b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp @@ -0,0 +1,47 @@ +/* + * 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 "APM::Engine::StreamVolumeCurves" +//#define LOG_NDEBUG 0 + +#include "StreamVolumeCurves.h" +#include + +namespace android { + +void StreamVolumeCurves::dump(String8 *dst, int spaces) const +{ + if (mCurves.empty()) { + return; + } + dst->appendFormat("\n%*sStreams dump:\n", spaces, ""); + dst->appendFormat( + "%*sStream Can be muted Index Min Index Max Index Cur [device : index]...\n", spaces + 2, ""); + for (const auto &streamCurve : mCurves) { + streamCurve.second.dump(dst, spaces + 2, false); + } + dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, ""); + for (const auto &streamCurve : mCurves) { + std::string streamTypeLiteral; + StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral); + dst->appendFormat( + " %s (%02d): Curve points for device category (index, attenuation in millibel)\n", + streamTypeLiteral.c_str(), streamCurve.first); + streamCurve.second.dump(dst, spaces + 2, true); + } +} + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp similarity index 68% rename from services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp rename to services/audiopolicy/engine/common/src/VolumeCurve.cpp index 2625733f20..be2ca730ef 100644 --- a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp +++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp @@ -64,8 +64,7 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) ((float)(mCurvePoints[indexInUiPosition].mIndex - mCurvePoints[indexInUiPosition - 1].mIndex)) ); - ALOGV("VOLUME mDeviceCategory %d, mStreamType %d vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", - mDeviceCategory, mStreamType, + ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", mCurvePoints[indexInUiPosition - 1].mIndex, volIdx, mCurvePoints[indexInUiPosition].mIndex, ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels, @@ -74,55 +73,35 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) return decibels; } -void VolumeCurve::dump(String8 *dst) const +void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const { + if (!curvePoints) { + return; + } dst->append(" {"); for (size_t i = 0; i < mCurvePoints.size(); i++) { - dst->appendFormat("(%3d, %5d)", + dst->appendFormat("%*s (%3d, %5d)", spaces, "", mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb); - dst->append(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); + dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); } } -void VolumeCurvesForStream::dump(String8 *dst, int spaces = 0, bool curvePoints) const +void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const { if (!curvePoints) { - dst->appendFormat("%s %02d %02d ", - mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); - for (size_t i = 0; i < mIndexCur.size(); i++) { - dst->appendFormat("%04x : %02d, ", mIndexCur.keyAt(i), mIndexCur.valueAt(i)); + dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "", + mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + for (const auto &pair : mIndexCur) { + dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second); } - dst->append("\n"); + dst->appendFormat("\n"); return; } - for (size_t i = 0; i < size(); i++) { std::string deviceCatLiteral; DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral); - dst->appendFormat("%*s %s :", - spaces, "", deviceCatLiteral.c_str()); - valueAt(i)->dump(dst); - } - dst->append("\n"); -} - -void VolumeCurvesCollection::dump(String8 *dst) const -{ - dst->append("\nStreams dump:\n"); - dst->append( - " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); - for (size_t i = 0; i < size(); i++) { - dst->appendFormat(" %02zu ", i); - valueAt(i).dump(dst); - } - dst->append("\nVolume Curves for Use Cases (aka Stream types) dump:\n"); - for (size_t i = 0; i < size(); i++) { - std::string streamTypeLiteral; - StreamTypeConverter::toString(keyAt(i), streamTypeLiteral); - dst->appendFormat( - " %s (%02zu): Curve points for device category (index, attenuation in millibel)\n", - streamTypeLiteral.c_str(), i); - valueAt(i).dump(dst, 2, true); + dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str()); + valueAt(i)->dump(dst, 2, true); } } diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index e18f68759d..f7caad238d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -45,6 +45,27 @@ struct AttributesGroup { using AttributesGroups = std::vector; +struct CurvePoint { + int index; + int attenuationInMb; +}; +using CurvePoints = std::vector; + +struct VolumeCurve { + std::string deviceCategory; + CurvePoints curvePoints; +}; +using VolumeCurves = std::vector; + +struct VolumeGroup { + std::string name; + std::string stream; + int indexMin; + int indexMax; + VolumeCurves volumeCurves; +}; +using VolumeGroups = std::vector; + struct ProductStrategy { std::string name; AttributesGroups attributesGroups; @@ -78,6 +99,7 @@ struct Config { ProductStrategies productStrategies; Criteria criteria; CriterionTypes criterionTypes; + VolumeGroups volumeGroups; }; /** Result of `parse(const char*)` */ diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 3aa38cf965..227ebd8cde 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -107,6 +107,35 @@ struct CriterionTraits : public BaseSerializerTraits { static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &collection); }; +struct VolumeTraits : public BaseSerializerTraits { + static constexpr const char *tag = "volume"; + static constexpr const char *collectionTag = "volumes"; + static constexpr const char *volumePointTag = "point"; + + struct Attributes { + static constexpr const char *deviceCategory = "deviceCategory"; + static constexpr const char *reference = "ref"; /**< For volume curves factorization. */ + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; +struct VolumeGroupTraits : public BaseSerializerTraits { + static constexpr const char *tag = "volumeGroup"; + static constexpr const char *collectionTag = "volumeGroups"; + + struct Attributes { + static constexpr const char *name = "name"; + static constexpr const char *stream = "stream"; // For legacy volume curves + static constexpr const char *indexMin = "indexMin"; + static constexpr const char *indexMax = "indexMax"; + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; + +using xmlCharUnique = std::unique_ptr; using xmlCharUnique = std::unique_ptr; @@ -383,6 +412,100 @@ status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, return NO_ERROR; } +status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) +{ + std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory); + if (deviceCategory.empty()) { + ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory); + } + + std::string referenceName = getXmlAttribute(root, Attributes::reference); + const _xmlNode *ref = NULL; + if (!referenceName.empty()) { + getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag); + if (ref == NULL) { + ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str()); + return BAD_VALUE; + } + } + // Retrieve curve point from reference element if found or directly from current curve + CurvePoints curvePoints; + for (const xmlNode *child = referenceName.empty() ? + root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) { + xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (pointXml == NULL) { + return BAD_VALUE; + } + ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast(pointXml.get())); + std::vector point; + collectionFromString>( + reinterpret_cast(pointXml.get()), point, ","); + if (point.size() != 2) { + ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, + reinterpret_cast(pointXml.get())); + return BAD_VALUE; + } + curvePoints.push_back({point[0], point[1]}); + } + } + volumes.push_back({ deviceCategory, curvePoints }); + return NO_ERROR; +} + +status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) +{ + std::string name; + std::string stream = {}; + int indexMin = 0; + int indexMax = 0; + + for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) { + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) { + xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (nameXml == nullptr) { + return BAD_VALUE; + } + name = reinterpret_cast(nameXml.get()); + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) { + xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (streamXml == nullptr) { + return BAD_VALUE; + } + stream = reinterpret_cast(streamXml.get()); + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) { + xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (indexMinXml == nullptr) { + return BAD_VALUE; + } + std::string indexMinLiteral(reinterpret_cast(indexMinXml.get())); + if (!convertTo(indexMinLiteral, indexMin)) { + return BAD_VALUE; + } + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) { + xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (indexMaxXml == nullptr) { + return BAD_VALUE; + } + std::string indexMaxLiteral(reinterpret_cast(indexMaxXml.get())); + if (!convertTo(indexMaxLiteral, indexMax)) { + return BAD_VALUE; + } + } + } + ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d", + __func__, name.c_str(), stream.c_str(), indexMin, indexMax); + + VolumeCurves groupVolumeCurves; + size_t skipped = 0; + deserializeCollection(doc, root, groupVolumeCurves, skipped); + volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves }); + return NO_ERROR; +} + ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -414,6 +537,9 @@ ParsingResult parse(const char* path) { doc, cur, config->criteria, nbSkippedElements); deserializeCollection( doc, cur, config->criterionTypes, nbSkippedElements); + deserializeCollection( + doc, cur, config->volumeGroups, nbSkippedElements); + return {std::move(config), nbSkippedElements}; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 498cc3bf82..c9e9507705 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -234,6 +235,21 @@ public: */ virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0; + /** + * @brief getVolumeCurvesForAttributes retrieves the Volume Curves interface for the + * requested Audio Attributes. + * @param attr to be considered + * @return IVolumeCurves interface pointer if found, nullptr otherwise + */ + virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0; + + /** + * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream + * @param stream to be considered + * @return IVolumeCurves interface pointer if found, nullptr otherwise + */ + virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0; + virtual void dump(String8 *dst) const = 0; protected: diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h index b7902cf2e5..43ba625f9c 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h @@ -16,7 +16,6 @@ #pragma once -#include #include #include #include @@ -51,8 +50,6 @@ public: virtual const DeviceVector &getAvailableInputDevices() const = 0; - virtual IVolumeCurvesCollection &getVolumeCurves() = 0; - virtual const sp &getDefaultOutputDevice() const = 0; protected: diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index bbd9688529..2b7e4c8dbc 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -12,6 +12,8 @@ LOCAL_SRC_FILES := \ src/EngineInstance.cpp \ src/Stream.cpp \ src/InputSource.cpp \ + ../engine/common/src/VolumeCurve.cpp \ + ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index 95a2ecc08d..ef476f765c 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -20,6 +20,8 @@ LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_stream_volumes.xml \ + audio_policy_engine_default_stream_volumes.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml @@ -34,6 +36,22 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml index ab61d8a937..4ca33b4525 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml new file mode 100644 index 0000000000..21e6dd58ac --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml @@ -0,0 +1,136 @@ + + + + + + + + 0,0 + 100,0 + + + 0,-9600 + 100,-9600 + + + + 1,-2400 + 33,-1800 + 66,-1200 + 100,-600 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4680 + 42,-2070 + 85,-540 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + 1,-12700 + 20,-8000 + 60,-4000 + 100,0 + + + + + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + + 0,-12700 + 20,-8000 + 60,-4000 + 100,0 + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml new file mode 100644 index 0000000000..73bde1fb7e --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -0,0 +1,231 @@ + + + + + + + AUDIO_STREAM_VOICE_CALL + 1 + 7 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2700 + 33,-1800 + 66,-900 + 100,0 + + + + + + + AUDIO_STREAM_SYSTEM + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-5100 + 57,-2800 + 71,-2500 + 85,-2300 + 100,-2100 + + + + + + + + + AUDIO_STREAM_RING + 0 + 7 + + + + + + + + + AUDIO_STREAM_MUSIC + 0 + 25 + + + + + + + + + AUDIO_STREAM_ALARM + 1 + 7 + + + + + + + + + AUDIO_STREAM_NOTIFICATION + 0 + 7 + + + + + + + + + AUDIO_STREAM_BLUETOOTH_SCO + 0 + 15 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + + AUDIO_STREAM_ENFORCED_AUDIBLE + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-3400 + 71,-2400 + 100,-2000 + + + + + + + + + AUDIO_STREAM_DTMF + 0 + 15 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-4000 + 71,-2400 + 100,-1400 + + + + + + + + + AUDIO_STREAM_TTS + 0 + 15 + + + + + + + + + AUDIO_STREAM_ACCESSIBILITY + 1 + 15 + + + + + + + + + AUDIO_STREAM_REROUTING + 0 + 1 + + + + + + + + + AUDIO_STREAM_PATCH + 0 + 1 + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 719ef01031..89a1694a92 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -113,7 +113,7 @@ bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &profile) { if (setPropertyForKey(stream, profile)) { - getApmObserver()->getVolumeCurves().switchVolumeCurve(profile, stream); + switchVolumeCurve(profile, stream); return true; } return false; diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index 95eac1c17e..94fa788a90 100644 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -8,6 +8,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/Engine.cpp \ src/EngineInstance.cpp \ + ../engine/common/src/VolumeCurve.cpp \ + ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk index 866466fc14..f06ee4cf13 100644 --- a/services/audiopolicy/enginedefault/config/example/Android.mk +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -16,7 +16,9 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml + audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_stream_volumes.xml \ + audio_policy_engine_default_stream_volumes.xml include $(BUILD_PREBUILT) @@ -29,4 +31,20 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml index ab61d8a937..4ca33b4525 100644 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml new file mode 100644 index 0000000000..21e6dd58ac --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml @@ -0,0 +1,136 @@ + + + + + + + + 0,0 + 100,0 + + + 0,-9600 + 100,-9600 + + + + 1,-2400 + 33,-1800 + 66,-1200 + 100,-600 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4680 + 42,-2070 + 85,-540 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + 1,-12700 + 20,-8000 + 60,-4000 + 100,0 + + + + + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + + 0,-12700 + 20,-8000 + 60,-4000 + 100,0 + + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml new file mode 100644 index 0000000000..73bde1fb7e --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -0,0 +1,231 @@ + + + + + + + AUDIO_STREAM_VOICE_CALL + 1 + 7 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2700 + 33,-1800 + 66,-900 + 100,0 + + + + + + + AUDIO_STREAM_SYSTEM + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-5100 + 57,-2800 + 71,-2500 + 85,-2300 + 100,-2100 + + + + + + + + + AUDIO_STREAM_RING + 0 + 7 + + + + + + + + + AUDIO_STREAM_MUSIC + 0 + 25 + + + + + + + + + AUDIO_STREAM_ALARM + 1 + 7 + + + + + + + + + AUDIO_STREAM_NOTIFICATION + 0 + 7 + + + + + + + + + AUDIO_STREAM_BLUETOOTH_SCO + 0 + 15 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + + AUDIO_STREAM_ENFORCED_AUDIBLE + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-3400 + 71,-2400 + 100,-2000 + + + + + + + + + AUDIO_STREAM_DTMF + 0 + 15 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-4000 + 71,-2400 + 100,-1400 + + + + + + + + + AUDIO_STREAM_TTS + 0 + 15 + + + + + + + + + AUDIO_STREAM_ACCESSIBILITY + 1 + 15 + + + + + + + + + AUDIO_STREAM_REROUTING + 0 + 1 + + + + + + + + + AUDIO_STREAM_PATCH + 0 + 1 + + + + + + + + diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 43023a8784..fd6a013a76 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -66,35 +66,6 @@ Engine::Engine() } } -status_t Engine::setPhoneState(audio_mode_t state) -{ - ALOGV("setPhoneState() state %d", state); - - if (state < 0 || state >= AUDIO_MODE_CNT) { - ALOGW("setPhoneState() invalid state %d", state); - return BAD_VALUE; - } - - if (state == getPhoneState()) { - ALOGW("setPhoneState() setting same state %d", state); - return BAD_VALUE; - } - - // store previous phone state for management of sonification strategy below - int oldState = getPhoneState(); - EngineBase::setPhoneState(state); - - if (!is_state_in_call(oldState) && is_state_in_call(state)) { - ALOGV(" Entering call in setPhoneState()"); - getApmObserver()->getVolumeCurves().switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, - AUDIO_STREAM_DTMF); - } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { - ALOGV(" Exiting call in setPhoneState()"); - getApmObserver()->getVolumeCurves().restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); - } - return NO_ERROR; -} - status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { switch(usage) { diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index 15fc358687..d8a369891d 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -55,8 +55,6 @@ private: /// /// from EngineBase, so from AudioPolicyManagerInterface /// - status_t setPhoneState(audio_mode_t mode) override; - status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5b752ead9a..b563a042b2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1710,7 +1710,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, - mVolumeCurves->getVolumeIndex(stream, outputDesc->devices().types()), + getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()), outputDesc, outputDesc->devices().types()); @@ -2382,14 +2382,15 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, ALOGE("%s for stream %d: invalid min %d or max %d", __func__, stream , indexMin, indexMax); return; } - mVolumeCurves->initStreamVolume(stream, indexMin, indexMax); + // @todo: our proposal now use XML to store Indexes Min & Max + getVolumeCurves(stream).initVolume(indexMin, indexMax); // initialize other private stream volumes which follow this one for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - mVolumeCurves->initStreamVolume((audio_stream_type_t)curStream, indexMin, indexMax); + getVolumeCurves((audio_stream_type_t)curStream).initVolume(indexMin, indexMax); } } @@ -2397,13 +2398,13 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { - + auto &curves = getVolumeCurves(stream); // VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but // can be muted directly by an app that has MODIFY_PHONE_STATE permission. - if (((index < mVolumeCurves->getVolumeIndexMin(stream)) && + if (((index < curves.getVolumeIndexMin()) && !((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && index == 0)) || - (index > mVolumeCurves->getVolumeIndexMax(stream))) { + (index > curves.getVolumeIndexMax())) { return BAD_VALUE; } if (!audio_is_output_device(device)) { @@ -2411,7 +2412,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, } // Force max volume if stream cannot be muted - if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream); + if (!curves.canBeMuted()) index = curves.getVolumeIndexMax(); ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d", stream, device, index); @@ -2421,7 +2422,8 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index); + auto &curCurves = getVolumeCurves(static_cast(curStream)); + curCurves.addCurrentVolumeIndex(device, index); } // update volume on all outputs and streams matching the following: @@ -2455,8 +2457,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, curStreamDevice |= device; applyVolume = (Volume::getDeviceForVolume(curDevice) & curStreamDevice) != 0; } else { - applyVolume = !mVolumeCurves->hasVolumeIndexForDevice( - stream, curStreamDevice); + applyVolume = !curves.hasVolumeIndexForDevice(curStreamDevice); } // rescale index before applying to curStream as ranges may be different for // stream and curStream @@ -2465,9 +2466,10 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, //FIXME: workaround for truncated touch sounds // delayed volume change for system stream to be removed when the problem is // handled by system UI - status_t volStatus = - checkAndSetVolume((audio_stream_type_t)curStream, idx, desc, curDevice, - (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0); + status_t volStatus = checkAndSetVolume( + (audio_stream_type_t)curStream, idx, desc, curDevice, + (stream == AUDIO_STREAM_SYSTEM) ? + TOUCH_SOUND_FIXED_DELAY_MS : 0); if (volStatus != NO_ERROR) { status = volStatus; } @@ -2494,7 +2496,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, } device = Volume::getDeviceForVolume(device); - *index = mVolumeCurves->getVolumeIndex(stream, device); + *index = getVolumeCurves(stream).getVolumeIndex(device); ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); return NO_ERROR; } @@ -2927,7 +2929,6 @@ void AudioPolicyManager::dump(String8 *dst) const mHwModulesAll.dump(dst); mOutputs.dump(dst); mInputs.dump(dst); - mVolumeCurves->dump(dst); mEffects.dump(dst); mAudioPatches.dump(dst); mPolicyMixes.dump(dst); @@ -4114,9 +4115,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mpClientInterface(clientInterface), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mA2dpSuspended(false), - mVolumeCurves(new VolumeCurvesCollection()), - mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, - mDefaultOutputDevice, static_cast(mVolumeCurves.get())), + mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice), mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), @@ -4150,8 +4149,6 @@ void AudioPolicyManager::loadConfig() { } status_t AudioPolicyManager::initialize() { - mVolumeCurves->initializeVolumeCurves(getConfig().isSpeakerDrcEnabled()); - // Once policy config has been parsed, retrieve an instance of the engine and initialize it. audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance(); if (!engineInstance) { @@ -5532,7 +5529,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, int index, audio_devices_t device) { - float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index); + auto &curves = getVolumeCurves(stream); + float volumeDB = curves.volIndexToDb(Volume::getDeviceCategory(device), index); // handle the case of accessibility active while a ringtone is playing: if the ringtone is much // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch @@ -5557,8 +5555,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, case AUDIO_STREAM_ENFORCED_AUDIBLE: case AUDIO_STREAM_DTMF: case AUDIO_STREAM_ACCESSIBILITY: { - int voiceVolumeIndex = - mVolumeCurves->getVolumeIndex(AUDIO_STREAM_VOICE_CALL, device); + int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device); const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; @@ -5592,7 +5589,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && - mVolumeCurves->canBeMuted(stream)) { + getVolumeCurves(stream).canBeMuted()) { // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was // just stopped @@ -5603,9 +5600,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, true /*fromCache*/).types(); float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, - mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC, - musicDevice), - musicDevice); + getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice), + musicDevice); float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; if (volumeDB > minVolDB) { @@ -5640,10 +5636,12 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, if (srcStream == dstStream) { return srcIndex; } - float minSrc = (float)mVolumeCurves->getVolumeIndexMin(srcStream); - float maxSrc = (float)mVolumeCurves->getVolumeIndexMax(srcStream); - float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream); - float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream); + auto &srcCurves = getVolumeCurves(srcStream); + auto &dstCurves = getVolumeCurves(dstStream); + float minSrc = (float)srcCurves.getVolumeIndexMin(); + float maxSrc = (float)srcCurves.getVolumeIndexMax(); + float minDst = (float)dstCurves.getVolumeIndexMin(); + float maxDst = (float)dstCurves.getVolumeIndexMax(); // preserve mute request or correct range if (srcIndex < minSrc) { @@ -5658,11 +5656,11 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, } status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, - int index, - const sp& outputDesc, - audio_devices_t device, - int delayMs, - bool force) + int index, + const sp& outputDesc, + audio_devices_t device, + int delayMs, + bool force) { // do not change actual stream volume if the stream is muted if (outputDesc->isMuted(streamToVolumeSource(stream))) { @@ -5698,7 +5696,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); + voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax(); } else { voiceVolume = 1.0; } @@ -5721,7 +5719,7 @@ void AudioPolicyManager::applyStreamVolumes(const sp& out for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { checkAndSetVolume((audio_stream_type_t)stream, - mVolumeCurves->getVolumeIndex((audio_stream_type_t)stream, device), + getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device), outputDesc, device, delayMs, @@ -5754,10 +5752,10 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", stream, on, outputDesc->getMuteCount(stream), device); - + auto &curves = getVolumeCurves(stream); if (on) { if (!outputDesc->isMuted(streamToVolumeSource(stream))) { - if (mVolumeCurves->canBeMuted(stream) && + if (curves.canBeMuted() && ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { checkAndSetVolume(stream, 0, outputDesc, device, delayMs); @@ -5772,7 +5770,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { checkAndSetVolume(stream, - mVolumeCurves->getVolumeIndex(stream, device), + curves.getVolumeIndex(device), outputDesc, device, delayMs); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 70ad6ac7aa..06a1f3e416 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -49,7 +49,7 @@ #include #include #include -#include +#include "TypeConverter.h" namespace android { @@ -310,12 +310,24 @@ protected: { return mAvailableInputDevices; } - virtual IVolumeCurvesCollection &getVolumeCurves() { return *mVolumeCurves; } virtual const sp &getDefaultOutputDevice() const { return mDefaultOutputDevice; } + IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr) + { + auto *curves = mEngine->getVolumeCurvesForAttributes(attr); + ALOG_ASSERT(curves != nullptr, "No curves for attributes %s", toString(attr).c_str()); + return *curves; + } + IVolumeCurves &getVolumeCurves(audio_stream_type_t stream) + { + auto *curves = mEngine->getVolumeCurvesForStreamType(stream); + ALOG_ASSERT(curves != nullptr, "No curves for stream %s", toString(stream).c_str()); + return *curves; + } + void addOutput(audio_io_handle_t output, const sp& outputDesc); void removeOutput(audio_io_handle_t output); void addInput(audio_io_handle_t input, const sp& inputDesc); @@ -624,12 +636,12 @@ protected: float mLastVoiceVolume; // last voice volume value sent to audio HAL bool mA2dpSuspended; // true if A2DP output is suspended - std::unique_ptr mVolumeCurves; // Volume Curves per use case and device category EffectDescriptorCollection mEffects; // list of registered audio effects sp mDefaultOutputDevice; // output device selected by default at boot time HwModuleCollection mHwModules; // contains only modules that have been loaded successfully HwModuleCollection mHwModulesAll; // normally not needed, used during construction and for // dumps + AudioPolicyConfig mConfig; std::atomic mAudioPortGeneration; -- GitLab From 97717283c8e909e242efa615e66ad0b0faf6e9f6 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Thu, 7 Feb 2019 09:07:29 +0100 Subject: [PATCH 1002/1530] audiopolicy: engine: use apm XML config file for volume curves This CL removes the hard coded volume table in the engine and use as a fallback for compatibility reason the apm config file to load the volume tables. Bug: 124767636 Test: dumpsys media.audio_policy & diff with previous version Change-Id: I0392aad730c67ee79f898093fd1b4f64e26ab3fd Signed-off-by: Francois Gaffie --- .../engine/common/include/VolumeCurve.h | 4 +- .../engine/common/src/EngineBase.cpp | 5 +- .../engine/common/src/EngineDefaultConfig.h | 122 +--------------- services/audiopolicy/engine/config/Android.mk | 3 +- .../engine/config/include/EngineConfig.h | 1 + .../engine/config/src/EngineConfig.cpp | 135 ++++++++++++++++++ 6 files changed, 145 insertions(+), 125 deletions(-) diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h index c2a1da702e..0ec63e19fc 100644 --- a/services/audiopolicy/engine/common/include/VolumeCurve.h +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -182,8 +182,8 @@ public: private: KeyedVector > mOriginVolumeCurves; std::map mIndexCur; /**< current volume index per device. */ - /*const*/ int mIndexMin; /**< min volume index. */ - /*const*/ int mIndexMax; /**< max volume index. */ + int mIndexMin; /**< min volume index. */ + int mIndexMax; /**< max volume index. */ const bool mCanBeMuted = true; /**< true is the stream can be muted. */ const audio_stream_type_t mStream; /**< Keep it for legacy. */ diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 2449b61d12..6e2ab4cdfa 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -139,7 +139,10 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__); - result = {std::make_unique(gDefaultEngineConfig), 0}; + engineConfig::Config config = gDefaultEngineConfig; + android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups); + result = {std::make_unique(config), + static_cast(ret == NO_ERROR ? 0 : 1)}; } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 86a59b00ee..f1642c5e3c 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,131 +131,11 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; -const engineConfig::VolumeGroups gVolumeGroups = { - {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, - }, - }, - {"system", "AUDIO_STREAM_SYSTEM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"ring", "AUDIO_STREAM_RING", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"music", "AUDIO_STREAM_MUSIC", 0, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"alarm", "AUDIO_STREAM_ALARM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - }, - }, - {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"tts", "AUDIO_STREAM_TTS", 0, 16, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - }, - }, - {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, - {"patch", "AUDIO_STREAM_PATCH", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, -}; - const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, {}, - gVolumeGroups + {} }; } // namespace android diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk index fe7d961799..0b292a5fa3 100644 --- a/services/audiopolicy/engine/config/Android.mk +++ b/services/audiopolicy/engine/config/Android.mk @@ -23,7 +23,8 @@ LOCAL_SHARED_LIBRARIES := \ libandroidicu \ libxml2 \ libutils \ - liblog + liblog \ + libcutils LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index f7caad238d..a188115fff 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -113,6 +113,7 @@ struct ParsingResult { * @return audio policy usage @see Config */ ParsingResult parse(const char* path = DEFAULT_PATH); +android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups); } // namespace engineConfig } // namespace android diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 227ebd8cde..00fbac411b 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -19,6 +19,7 @@ #include "EngineConfig.h" #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -506,6 +508,84 @@ status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Coll return NO_ERROR; } +static constexpr const char *legacyVolumecollectionTag = "volumes"; +static constexpr const char *legacyVolumeTag = "volume"; + +status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur, + std::map &legacyVolumes) +{ + std::string streamTypeLiteral = getXmlAttribute(cur, "stream"); + if (streamTypeLiteral.empty()) { + ALOGE("%s: No attribute stream found", __func__); + return BAD_VALUE; + } + std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory"); + if (deviceCategoryLiteral.empty()) { + ALOGE("%s: No attribute deviceCategory found", __func__); + return BAD_VALUE; + } + std::string referenceName = getXmlAttribute(cur, "ref"); + const xmlNode *ref = NULL; + if (!referenceName.empty()) { + getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag); + if (ref == NULL) { + ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); + return BAD_VALUE; + } + ALOGV("%s: reference found for %s", __func__, referenceName.c_str()); + } + CurvePoints curvePoints; + for (const xmlNode *child = referenceName.empty() ? + cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) { + xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (pointXml == NULL) { + return BAD_VALUE; + } + ALOGV("%s: %s=%s", __func__, legacyVolumeTag, + reinterpret_cast(pointXml.get())); + std::vector point; + collectionFromString>( + reinterpret_cast(pointXml.get()), point, ","); + if (point.size() != 2) { + ALOGE("%s: Invalid %s: %s", __func__, VolumeTraits::volumePointTag, + reinterpret_cast(pointXml.get())); + return BAD_VALUE; + } + curvePoints.push_back({point[0], point[1]}); + } + } + legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints }); + return NO_ERROR; +} + +static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur, + VolumeGroups &volumeGroups, + size_t &nbSkippedElement) +{ + std::map legacyVolumeMap; + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) { + continue; + } + const xmlNode *child = cur->xmlChildrenNode; + for (; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) { + + status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap); + if (status != NO_ERROR) { + nbSkippedElement += 1; + } + } + } + } + for (const auto &volumeMapIter : legacyVolumeMap) { + volumeGroups.push_back({ volumeMapIter.first, volumeMapIter.first, 0, 100, + volumeMapIter.second }); + } + return NO_ERROR; +} + ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -543,5 +623,60 @@ ParsingResult parse(const char* path) { return {std::move(config), nbSkippedElements}; } +android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) { + xmlDocPtr doc; + doc = xmlParseFile(path); + if (doc == NULL) { + ALOGE("%s: Could not parse document %s", __FUNCTION__, path); + return BAD_VALUE; + } + xmlNodePtr cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); + xmlFreeDoc(doc); + return BAD_VALUE; + } + if (xmlXIncludeProcess(doc) < 0) { + ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); + return BAD_VALUE; + } + size_t nbSkippedElements = 0; + return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements); +} + +static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"}; +static const int kConfigLocationListSize = + (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0])); +static const int gApmXmlConfigFilePathMaxLength = 128; + +static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml"; +static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName = + "audio_policy_configuration_a2dp_offload_disabled.xml"; + +android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) { + char audioPolicyXmlConfigFile[gApmXmlConfigFilePathMaxLength]; + std::vector fileNames; + status_t ret; + + if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) && + property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // A2DP offload supported but disabled: try to use special XML file + fileNames.push_back(apmA2dpOffloadDisabledXmlConfigFileName); + } + fileNames.push_back(apmXmlConfigFileName); + + for (const char* fileName : fileNames) { + for (int i = 0; i < kConfigLocationListSize; i++) { + snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), + "%s/%s", kConfigLocationList[i], fileName); + ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups); + if (ret == NO_ERROR) { + return ret; + } + } + } + return BAD_VALUE; +} + } // namespace engineConfig } // namespace android -- GitLab From 27d84b795f118b2db5ca632f38d001154fe71cd2 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 21 Feb 2019 15:12:59 +0900 Subject: [PATCH 1003/1530] Use libnativewindow's AHardwareBuffer directly This is a part of cutting dependency from libmediandk to libandroid_runtime. The following function is exported from libandroid_runtime. * android_hardware_HardwareBuffer_convertToGrallocUsageBits This function just delegates to libnativewindow's AHardwareBuffer_convertToGrallocUsageBits function. This commit is for libmediandk to call libnativewindow's function directly in order to cut the dependency to libandroid_runtime. Bug: 124268753 Test: m -j Change-Id: I5f927fc26829031a8c91a7ba51178a34d737a953 --- media/ndk/NdkImage.cpp | 4 ++-- media/ndk/NdkImageReader.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp index 20b1667d1b..1883f6356d 100644 --- a/media/ndk/NdkImage.cpp +++ b/media/ndk/NdkImage.cpp @@ -23,7 +23,7 @@ #include "NdkImageReaderPriv.h" #include -#include +#include #include #include "hardware/camera3.h" @@ -190,7 +190,7 @@ media_status_t AImage::lockImage() { auto lockedBuffer = std::make_unique(); - uint64_t grallocUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage); + uint64_t grallocUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage); status_t ret = lockImageFromBuffer(mBuffer, grallocUsage, mBuffer->mFence->dup(), lockedBuffer.get()); diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index c3eb4379db..b929f7f802 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -272,7 +272,7 @@ AImageReader::init() { PublicFormat publicFormat = static_cast(mFormat); mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat); mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat); - mHalUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage); + mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage); sp gbProducer; sp gbConsumer; -- GitLab From 86cbf71505b7e2f8418f9d4b112731363345634b Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 21 Feb 2019 15:25:02 +0900 Subject: [PATCH 1004/1530] Use PublicFormat from libui This is a part of cutting dependency from libmediandk to libandroid_runtime. PublicFormat which was defined in android_view_Surface has been moved to libui. Bug: 124268753 Test: m -j Change-Id: I5f0e64ee6e2d5eb6b64491356a5bfa50b512a9c9 --- media/ndk/NdkImageReader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index b929f7f802..30fe8d687d 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -270,8 +270,8 @@ AImageReader::AImageReader(int32_t width, media_status_t AImageReader::init() { PublicFormat publicFormat = static_cast(mFormat); - mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat); - mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat); + mHalFormat = mapPublicFormatToHalFormat(publicFormat); + mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat); mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage); sp gbProducer; -- GitLab From c34e45328bac34bd054b3888080e41910c2b8454 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 21 Feb 2019 11:34:07 +0900 Subject: [PATCH 1005/1530] Cut dependency to libandroid.so This is a part of job to cut dependency from libmediandk to libandroid_runtime. As a LL-NDK, libmediandk desn't have to depend on libandroid_runtime. depependcy: * libmediandk -> libandroid -> libandroid_runtime. Libmediandk uses only one symbol(ANativeWindow_aquire) from libandroid and it is re-exported from libnativewindow. So by switching from libandroid to libnativewindow, the dependency from libmediandk to libandroid is cut easily. Bug: 124268753 Test: m -j Change-Id: I5da320780dba7c63ce39679dbf5d40fe9202eb00 --- media/ndk/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 339f6227aa..a9254c7b96 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -84,7 +84,7 @@ cc_library_shared { "liblog", "libutils", "libcutils", - "libandroid", + "libnativewindow", "libandroid_runtime", "libbinder", "libhidlbase", -- GitLab From 92f6462fb38bad4429d26f6db2fc19d313e13bc2 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Fri, 22 Feb 2019 16:19:46 +0900 Subject: [PATCH 1006/1530] Cut dependency to libmedia_jni.so This is a part of job to cut dependency from libmediandk to libandroid_runtime. As a LL-NDK, libmediandk desn't have to depend on libandroid_runtime. depependcy: * libmediandk -> libmedia_jni -> libandroid_runtime. Libmediandk calls three functions defined in libmedia_jni. However those three functions are independent from libandroid_runtime. After extracting those utility functions into a new library (libmedia_jni_utils), libmediandk now depends on it instead of libmedia_jni. Bug: 124268753 Test: m -j Change-Id: If283fefca5cf4e282d1c29876b78037bc9df7625 --- media/ndk/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 339f6227aa..58105fa25e 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -76,7 +76,7 @@ cc_library_shared { "libbinder", "libmedia", "libmedia_omx", - "libmedia_jni", + "libmedia_jni_utils", "libmediadrm", "libstagefright", "libstagefright_foundation", -- GitLab From ebebb5f8085df4231b6d786e1d24de78181cedeb Mon Sep 17 00:00:00 2001 From: juyuchen Date: Fri, 11 Jan 2019 14:41:04 +0800 Subject: [PATCH 1007/1530] audio policy: fix using hearing aid when force use NO_BT_A2DP Bug: 122615787 Test: manual audio test Change-Id: If694d08e56eac0791806b283f6ec07894dcbcccf --- services/audiopolicy/enginedefault/src/Engine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 93af8a6725..6d166a9c16 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -429,7 +429,9 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, outputDeviceTypesToIgnore); break; } - if (device2 == AUDIO_DEVICE_NONE) { + // FIXME: Find a better solution to prevent routing to BT hearing aid(b/122931261). + if ((device2 == AUDIO_DEVICE_NONE) && + (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) { device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HEARING_AID; } if ((device2 == AUDIO_DEVICE_NONE) && -- GitLab From 4d54767b7dad323de5f747096f5cdeea0d7691e5 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 22 Feb 2019 14:19:19 -0800 Subject: [PATCH 1008/1530] audio: Add support for dumping info from effect HAL modules The implementation uses IBase::debug() method which can write debugging info into the provided file descriptor (same model as used for devices and streams). Bug: 69939533 Test: adb shell dumpsys media.audio_flinger Change-Id: I02a74b51977d105edf6f7e624ea26f866c965708 --- media/libaudiohal/impl/EffectHalHidl.cpp | 10 ++++++++++ media/libaudiohal/impl/EffectHalHidl.h | 2 ++ .../include/media/audiohal/EffectHalInterface.h | 2 ++ services/audioflinger/Effects.cpp | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp index b0597b3607..caf575c4d5 100644 --- a/media/libaudiohal/impl/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #include +#include #include #include #include @@ -280,6 +281,15 @@ status_t EffectHalHidl::close() { return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; } +status_t EffectHalHidl::dump(int fd) { + if (mEffect == 0) return NO_INIT; + native_handle_t* hidlHandle = native_handle_create(1, 0); + hidlHandle->data[0] = fd; + Return ret = mEffect->debug(hidlHandle, {} /* options */); + native_handle_delete(hidlHandle); + return ret.isOk() ? OK : FAILED_TRANSACTION; +} + status_t EffectHalHidl::getConfigImpl( uint32_t cmdCode, uint32_t *replySize, void *pReplyData) { if (replySize == NULL || *replySize != sizeof(effect_config_t) || pReplyData == NULL) { diff --git a/media/libaudiohal/impl/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h index 9d9f707c85..1f238c0b98 100644 --- a/media/libaudiohal/impl/EffectHalHidl.h +++ b/media/libaudiohal/impl/EffectHalHidl.h @@ -61,6 +61,8 @@ class EffectHalHidl : public EffectHalInterface // Whether it's a local implementation. virtual bool isLocal() const { return false; } + virtual status_t dump(int fd); + uint64_t effectId() const { return mEffectId; } static void effectDescriptorToHal( diff --git a/media/libaudiohal/include/media/audiohal/EffectHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectHalInterface.h index 92622aa63f..03165bd95c 100644 --- a/media/libaudiohal/include/media/audiohal/EffectHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/EffectHalInterface.h @@ -55,6 +55,8 @@ class EffectHalInterface : public RefBase // Whether it's a local implementation. virtual bool isLocal() const = 0; + virtual status_t dump(int fd) = 0; + protected: // Subclasses can not be constructed directly by clients. EffectHalInterface() {} diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 8455e54bb5..ecaeb52b37 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1500,6 +1500,11 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector& args __unu write(fd, result.string(), result.length()); + if (mEffectInterface != 0) { + dprintf(fd, "\tEffect ID %d HAL dump:\n", mId); + (void)mEffectInterface->dump(fd); + } + if (locked) { mLock.unlock(); } -- GitLab From be20185b2f93e429aa4f69fa4caa3ac9cac2ecc3 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 20 Feb 2019 22:33:28 -0800 Subject: [PATCH 1009/1530] Public Audio playback capture must have a valid projection For privacy, require the app wanting to capture other app audio to have a valid MediaProjection. Test: adb shell audiorecorder --target /data/file.raw Bug: 111453086 Change-Id: Ia9343b2125aea2d9c0fc5a37b6ded9237d1d0f28 Signed-off-by: Kevin Rocard --- services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 8ddf82435c..d31ce53fe9 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -21,6 +21,7 @@ #include "TypeConverter.h" #include #include +#include #include namespace android { @@ -1032,9 +1033,14 @@ status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, bool registration) { Mutex::Autolock _l(mLock); - if(!modifyAudioRoutingAllowed()) { + + // loopback|render only need a MediaProjection (checked in caller AudioService.java) + bool needModifyAudioRouting = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) { + return !is_mix_loopback_render(mix.mRouteFlags); }); + if (needModifyAudioRouting && !modifyAudioRoutingAllowed()) { return PERMISSION_DENIED; } + if (mAudioPolicyManager == NULL) { return NO_INIT; } -- GitLab From 446f4df547e74ac4aeb09a8a09f8138e88a1140a Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 21 Feb 2019 12:26:41 -0800 Subject: [PATCH 1010/1530] AudioFlinger: Add thread statistics for process and io timing Implemented for threads that read/write PCM. Test: audioflinger dumpsys Play Music, Movies, Video Recording Bug: 114112762 Change-Id: I866a8b639d18426633088d6a03d9135f4ee09fd3 --- services/audioflinger/Threads.cpp | 139 +++++++++++++++++++++--------- services/audioflinger/Threads.h | 10 ++- 2 files changed, 109 insertions(+), 40 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index a8c4bd1eaa..ff3395785b 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -872,6 +872,22 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u dprintf(fd, " Timestamp corrected: %s\n", isTimestampCorrectionEnabled() ? "yes" : "no"); } + if (mLastIoBeginNs > 0) { // MMAP may not set this + dprintf(fd, " Last %s occurred (msecs): %lld\n", + isOutput() ? "write" : "read", + (long long) (systemTime() - mLastIoBeginNs) / NANOS_PER_MILLISECOND); + } + + if (mProcessTimeMs.getN() > 0) { + dprintf(fd, " Process time ms stats: %s\n", mProcessTimeMs.toString().c_str()); + } + + if (mIoJitterMs.getN() > 0) { + dprintf(fd, " Hal %s jitter ms stats: %s\n", + isOutput() ? "write" : "read", + mIoJitterMs.toString().c_str()); + } + if (locked) { mLock.unlock(); } @@ -1704,7 +1720,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp& audioFlinge // mStreamTypes[] initialized in constructor body mTracks(type == MIXER), mOutput(output), - mLastWriteTime(-1), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false), + mNumWrites(0), mNumDelayedWrites(0), mInWrite(false), mMixerStatus(MIXER_IDLE), mMixerStatusIgnoringFastTracks(MIXER_IDLE), mStandbyDelayNs(AudioFlinger::mStandbyTimeInNsecs), @@ -1857,8 +1873,6 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector& channelMaskToString(mHapticChannelMask, true /* output */).c_str()); } dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); - dprintf(fd, " Last write occurred (msecs): %llu\n", - (unsigned long long) ns2ms(systemTime() - mLastWriteTime)); dprintf(fd, " Total writes: %d\n", mNumWrites); dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); @@ -3189,8 +3203,8 @@ bool AudioFlinger::PlaybackThread::threadLoop() Vector< sp > tracksToRemove; mStandbyTimeNs = systemTime(); - nsecs_t lastWriteFinished = -1; // time last server write completed - int64_t lastFramesWritten = -1; // track changes in timestamp server frames written + int64_t lastLoopCountWritten = -2; // never matches "previous" loop, when loopCount = 0. + int64_t lastFramesWritten = -1; // track changes in timestamp server frames written // MIXER nsecs_t lastWarning = 0; @@ -3236,7 +3250,8 @@ bool AudioFlinger::PlaybackThread::threadLoop() } audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE; - while (!exitPending()) + // loopCount is used for statistics and diagnostics. + for (int64_t loopCount = 0; !exitPending(); ++loopCount) { // Log merge requests are performed during AudioFlinger binder transactions, but // that does not cover audio playback. It's requested here for that reason. @@ -3394,11 +3409,11 @@ bool AudioFlinger::PlaybackThread::threadLoop() // use the time before we called the HAL write - it is a bit more accurate // to when the server last read data than the current time here. // - // If we haven't written anything, mLastWriteTime will be -1 + // If we haven't written anything, mLastIoBeginNs will be -1 // and we use systemTime(). mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = mFramesWritten; - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastWriteTime == -1 - ? systemTime() : mLastWriteTime; + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastIoBeginNs == -1 + ? systemTime() : mLastIoBeginNs; } for (const sp &t : mActiveTracks) { @@ -3635,43 +3650,68 @@ bool AudioFlinger::PlaybackThread::threadLoop() // mSleepTimeUs == 0 means we must write to audio hardware if (mSleepTimeUs == 0) { ssize_t ret = 0; - // We save lastWriteFinished here, as previousLastWriteFinished, - // for throttling. On thread start, previousLastWriteFinished will be - // set to -1, which properly results in no throttling after the first write. - nsecs_t previousLastWriteFinished = lastWriteFinished; - nsecs_t delta = 0; + // writePeriodNs is updated >= 0 when ret > 0. + int64_t writePeriodNs = -1; if (mBytesRemaining) { // FIXME rewrite to reduce number of system calls - mLastWriteTime = systemTime(); // also used for dumpsys + const int64_t lastIoBeginNs = systemTime(); ret = threadLoop_write(); - lastWriteFinished = systemTime(); - delta = lastWriteFinished - mLastWriteTime; + const int64_t lastIoEndNs = systemTime(); if (ret < 0) { mBytesRemaining = 0; - } else { + } else if (ret > 0) { mBytesWritten += ret; mBytesRemaining -= ret; - mFramesWritten += ret / mFrameSize; + const int64_t frames = ret / mFrameSize; + mFramesWritten += frames; + + writePeriodNs = lastIoEndNs - mLastIoEndNs; + // process information relating to write time. + if (audio_has_proportional_frames(mFormat)) { + // we are in a continuous mixing cycle + if (mMixerStatus == MIXER_TRACKS_READY && + loopCount == lastLoopCountWritten + 1) { + + const double jitterMs = + TimestampVerifier::computeJitterMs( + {frames, writePeriodNs}, + {0, 0} /* lastTimestamp */, mSampleRate); + const double processMs = + (lastIoBeginNs - mLastIoEndNs) * 1e-6; + + Mutex::Autolock _l(mLock); + mIoJitterMs.add(jitterMs); + mProcessTimeMs.add(processMs); + } + + // write blocked detection + const int64_t deltaWriteNs = lastIoEndNs - lastIoBeginNs; + if (mType == MIXER && deltaWriteNs > maxPeriod) { + mNumDelayedWrites++; + if ((lastIoEndNs - lastWarning) > kWarningThrottleNs) { + ATRACE_NAME("underrun"); + ALOGW("write blocked for %lld msecs, " + "%d delayed writes, thread %d", + (long long)deltaWriteNs / NANOS_PER_MILLISECOND, + mNumDelayedWrites, mId); + lastWarning = lastIoEndNs; + } + } + } + // update timing info. + mLastIoBeginNs = lastIoBeginNs; + mLastIoEndNs = lastIoEndNs; + lastLoopCountWritten = loopCount; } } else if ((mMixerStatus == MIXER_DRAIN_TRACK) || (mMixerStatus == MIXER_DRAIN_ALL)) { threadLoop_drain(); } if (mType == MIXER && !mStandby) { - // write blocked detection - if (delta > maxPeriod) { - mNumDelayedWrites++; - if ((lastWriteFinished - lastWarning) > kWarningThrottleNs) { - ATRACE_NAME("underrun"); - ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p", - (unsigned long long) ns2ms(delta), mNumDelayedWrites, this); - lastWarning = lastWriteFinished; - } - } if (mThreadThrottle && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks) - && ret > 0) { // we wrote something + && writePeriodNs > 0) { // we have write period info // Limit MixerThread data processing to no more than twice the // expected processing rate. // @@ -3690,12 +3730,9 @@ bool AudioFlinger::PlaybackThread::threadLoop() // 2. threadLoop_mix (significant for heavy mixing, especially // on low tier processors) - // it's OK if deltaMs (and deltaNs) is an overestimate. - nsecs_t deltaNs; - // deltaNs = lastWriteFinished - previousLastWriteFinished; - __builtin_sub_overflow( - lastWriteFinished,previousLastWriteFinished, &deltaNs); - const int32_t deltaMs = deltaNs / 1000000; + // it's OK if deltaMs is an overestimate. + + const int32_t deltaMs = writePeriodNs / NANOS_PER_MILLISECOND; const int32_t throttleMs = (int32_t)mHalfBufferMs - deltaMs; if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) { @@ -3708,7 +3745,8 @@ bool AudioFlinger::PlaybackThread::threadLoop() mThreadThrottleTimeMs += throttleMs; // Throttle must be attributed to the previous mixer loop's write time // to allow back-to-back throttling. - lastWriteFinished += throttleMs * 1000000; + // This also ensures proper timing statistics. + mLastIoEndNs = systemTime(); // we fetch the write end time again. } else { uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs; if (diff > 0) { @@ -6751,8 +6789,10 @@ reacquire_wakelock: // used to request a deferred sleep, to be executed later while mutex is unlocked uint32_t sleepUs = 0; + int64_t lastLoopCountRead = -2; // never matches "previous" loop, when loopCount = 0. + // loop while there is work to do - for (;;) { + for (int64_t loopCount = 0;; ++loopCount) { // loopCount used for statistics tracking Vector< sp > effectChains; // activeTracks accumulates a copy of a subset of mActiveTracks @@ -6951,6 +6991,7 @@ reacquire_wakelock: int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1); ssize_t framesRead; + const int64_t lastIoBeginNs = systemTime(); // start IO timing // If an NBAIO source is present, use it to read the normal capture's data if (mPipeSource != 0) { @@ -7008,10 +7049,12 @@ reacquire_wakelock: } } + const int64_t lastIoEndNs = systemTime(); // end IO timing + // Update server timestamp with server stats // systemTime() is optional if the hardware supports timestamps. mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead; - mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = systemTime(); + mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs; // Update server timestamp with kernel stats if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) { @@ -7060,6 +7103,24 @@ reacquire_wakelock: ALOG_ASSERT(framesRead > 0); mFramesRead += framesRead; + if (audio_has_proportional_frames(mFormat) + && loopCount == lastLoopCountRead + 1) { + const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs; + const double jitterMs = + TimestampVerifier::computeJitterMs( + {framesRead, readPeriodNs}, + {0, 0} /* lastTimestamp */, mSampleRate); + const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6; + + Mutex::Autolock _l(mLock); + mIoJitterMs.add(jitterMs); + mProcessTimeMs.add(processMs); + } + // update timing info. + mLastIoBeginNs = lastIoBeginNs; + mLastIoEndNs = lastIoEndNs; + lastLoopCountRead = loopCount; + #ifdef TEE_SINK (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead); #endif diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 1131b266bc..4968829cb4 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -512,6 +512,15 @@ protected: TimestampVerifier< // For timestamp statistics. int64_t /* frame count */, int64_t /* time ns */> mTimestampVerifier; audio_devices_t mTimestampCorrectedDevices = AUDIO_DEVICE_NONE; + + // ThreadLoop statistics per iteration. + int64_t mLastIoBeginNs = -1; + int64_t mLastIoEndNs = -1; + + // This should be read under ThreadBase lock (if not on the threadLoop thread). + audio_utils::Statistics mIoJitterMs{0.995 /* alpha */}; + audio_utils::Statistics mProcessTimeMs{0.995 /* alpha */}; + bool mIsMsdDevice = false; // A condition that must be evaluated by the thread loop has changed and // we must not wait for async write callback in the thread loop before evaluating it @@ -1030,7 +1039,6 @@ private: float mMasterVolume; std::atomic mMasterBalance{}; audio_utils::Balance mBalance; - nsecs_t mLastWriteTime; int mNumWrites; int mNumDelayedWrites; bool mInWrite; -- GitLab From cb398aba7dac9c9e3c1b57b78c6ec85fd789fcca Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Sat, 16 Feb 2019 13:12:59 +0530 Subject: [PATCH 1011/1530] libeffects: Fixed minor nits on lvmtest Test: local native tests (lvmtest) Bug: 124319395 Change-Id: I874de1f09e5e57e2c81ce069fbee5b53f9194730 --- media/libeffects/lvm/tests/lvmtest.cpp | 27 ++++++++++++-------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp index fe47d0b5e3..416bdaac9f 100644 --- a/media/libeffects/lvm/tests/lvmtest.cpp +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -15,6 +15,7 @@ */ #include #include +#include #include #include #include @@ -102,20 +103,15 @@ void printUsage() { printf("\n -M"); printf("\n Mono mode (force all input audio channels to be identical)"); printf("\n -basslvl:"); - printf("\n A value that ranges between 0 - 15 default 0"); + printf("\n A value that ranges between %d - %d default 0", LVM_BE_MIN_EFFECTLEVEL, + LVM_BE_MAX_EFFECTLEVEL); printf("\n"); printf("\n -eqPreset:"); - printf("\n 0 - Normal"); - printf("\n 1 - Classical"); - printf("\n 2 - Dance"); - printf("\n 3 - Flat"); - printf("\n 4 - Folk"); - printf("\n 5 - Heavy Metal"); - printf("\n 6 - Hip Hop"); - printf("\n 7 - Jazz"); - printf("\n 8 - Pop"); - printf("\n 9 - Rock"); - printf("\n default 0"); + const size_t numPresetLvls = std::size(gEqualizerPresets); + for (size_t i = 0; i < numPresetLvls; ++i) { + printf("\n %zu - %s", i, gEqualizerPresets[i].name); + } + printf("\n default - 0"); printf("\n -bE "); printf("\n Enable Dynamic Bass Enhancement"); printf("\n"); @@ -619,7 +615,7 @@ int lvmMainProcess(EffectContext *pContext, std::fill(fp + 1, fp + channelCount, *fp); // replicate ch 0 } } -#if 1 +#ifndef BYPASS_EXEC errCode = lvmExecute(floatIn.data(), floatOut.data(), pContext, plvmConfigParams); if (errCode) { printf("\nError: lvmExecute returned with %d\n", errCode); @@ -689,7 +685,7 @@ int main(int argc, const char *argv[]) { lvmConfigParams.monoMode = true; } else if (!strncmp(argv[i], "-basslvl:", 9)) { const int bassEffectLevel = atoi(argv[i] + 9); - if (bassEffectLevel > 15 || bassEffectLevel < 0) { + if (bassEffectLevel > LVM_BE_MAX_EFFECTLEVEL || bassEffectLevel < LVM_BE_MIN_EFFECTLEVEL) { printf("Error: Unsupported Bass Effect Level : %d\n", bassEffectLevel); printUsage(); @@ -698,7 +694,8 @@ int main(int argc, const char *argv[]) { lvmConfigParams.bassEffectLevel = bassEffectLevel; } else if (!strncmp(argv[i], "-eqPreset:", 10)) { const int eqPresetLevel = atoi(argv[i] + 10); - if (eqPresetLevel > 9 || eqPresetLevel < 0) { + const int numPresetLvls = std::size(gEqualizerPresets); + if (eqPresetLevel >= numPresetLvls || eqPresetLevel < 0) { printf("Error: Unsupported Equalizer Preset : %d\n", eqPresetLevel); printUsage(); return -1; -- GitLab From 1462a279faace46266ab1f2151393ecd31c62a5e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 22 Feb 2019 17:22:57 -0800 Subject: [PATCH 1012/1530] Revert "audiopolicy: engine: use apm XML config file for volume curves" This reverts commit 97717283c8e909e242efa615e66ad0b0faf6e9f6. Bug: 125937703 Test: make Change-Id: If7993ca2d9d5a6569ebf7bf4fdc82a7262d29faf --- .../engine/common/include/VolumeCurve.h | 4 +- .../engine/common/src/EngineBase.cpp | 5 +- .../engine/common/src/EngineDefaultConfig.h | 122 +++++++++++++++- services/audiopolicy/engine/config/Android.mk | 3 +- .../engine/config/include/EngineConfig.h | 1 - .../engine/config/src/EngineConfig.cpp | 135 ------------------ 6 files changed, 125 insertions(+), 145 deletions(-) diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h index 0ec63e19fc..c2a1da702e 100644 --- a/services/audiopolicy/engine/common/include/VolumeCurve.h +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -182,8 +182,8 @@ public: private: KeyedVector > mOriginVolumeCurves; std::map mIndexCur; /**< current volume index per device. */ - int mIndexMin; /**< min volume index. */ - int mIndexMax; /**< max volume index. */ + /*const*/ int mIndexMin; /**< min volume index. */ + /*const*/ int mIndexMax; /**< max volume index. */ const bool mCanBeMuted = true; /**< true is the stream can be muted. */ const audio_stream_type_t mStream; /**< Keep it for legacy. */ diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 6e2ab4cdfa..2449b61d12 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -139,10 +139,7 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__); - engineConfig::Config config = gDefaultEngineConfig; - android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups); - result = {std::make_unique(config), - static_cast(ret == NO_ERROR ? 0 : 1)}; + result = {std::make_unique(gDefaultEngineConfig), 0}; } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index f1642c5e3c..86a59b00ee 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,11 +131,131 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; +const engineConfig::VolumeGroups gVolumeGroups = { + {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, + }, + }, + {"system", "AUDIO_STREAM_SYSTEM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"ring", "AUDIO_STREAM_RING", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"music", "AUDIO_STREAM_MUSIC", 0, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"alarm", "AUDIO_STREAM_ALARM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + }, + }, + {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"tts", "AUDIO_STREAM_TTS", 0, 16, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + }, + }, + {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, + {"patch", "AUDIO_STREAM_PATCH", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, +}; + const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, {}, - {} + gVolumeGroups }; } // namespace android diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk index 0b292a5fa3..fe7d961799 100644 --- a/services/audiopolicy/engine/config/Android.mk +++ b/services/audiopolicy/engine/config/Android.mk @@ -23,8 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libandroidicu \ libxml2 \ libutils \ - liblog \ - libcutils + liblog LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index a188115fff..f7caad238d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -113,7 +113,6 @@ struct ParsingResult { * @return audio policy usage @see Config */ ParsingResult parse(const char* path = DEFAULT_PATH); -android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups); } // namespace engineConfig } // namespace android diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 00fbac411b..227ebd8cde 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -19,7 +19,6 @@ #include "EngineConfig.h" #include -#include #include #include #include @@ -27,7 +26,6 @@ #include #include #include -#include #include #include @@ -508,84 +506,6 @@ status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Coll return NO_ERROR; } -static constexpr const char *legacyVolumecollectionTag = "volumes"; -static constexpr const char *legacyVolumeTag = "volume"; - -status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur, - std::map &legacyVolumes) -{ - std::string streamTypeLiteral = getXmlAttribute(cur, "stream"); - if (streamTypeLiteral.empty()) { - ALOGE("%s: No attribute stream found", __func__); - return BAD_VALUE; - } - std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory"); - if (deviceCategoryLiteral.empty()) { - ALOGE("%s: No attribute deviceCategory found", __func__); - return BAD_VALUE; - } - std::string referenceName = getXmlAttribute(cur, "ref"); - const xmlNode *ref = NULL; - if (!referenceName.empty()) { - getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag); - if (ref == NULL) { - ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); - return BAD_VALUE; - } - ALOGV("%s: reference found for %s", __func__, referenceName.c_str()); - } - CurvePoints curvePoints; - for (const xmlNode *child = referenceName.empty() ? - cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { - if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) { - xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (pointXml == NULL) { - return BAD_VALUE; - } - ALOGV("%s: %s=%s", __func__, legacyVolumeTag, - reinterpret_cast(pointXml.get())); - std::vector point; - collectionFromString>( - reinterpret_cast(pointXml.get()), point, ","); - if (point.size() != 2) { - ALOGE("%s: Invalid %s: %s", __func__, VolumeTraits::volumePointTag, - reinterpret_cast(pointXml.get())); - return BAD_VALUE; - } - curvePoints.push_back({point[0], point[1]}); - } - } - legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints }); - return NO_ERROR; -} - -static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur, - VolumeGroups &volumeGroups, - size_t &nbSkippedElement) -{ - std::map legacyVolumeMap; - for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { - if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) { - continue; - } - const xmlNode *child = cur->xmlChildrenNode; - for (; child != NULL; child = child->next) { - if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) { - - status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap); - if (status != NO_ERROR) { - nbSkippedElement += 1; - } - } - } - } - for (const auto &volumeMapIter : legacyVolumeMap) { - volumeGroups.push_back({ volumeMapIter.first, volumeMapIter.first, 0, 100, - volumeMapIter.second }); - } - return NO_ERROR; -} - ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -623,60 +543,5 @@ ParsingResult parse(const char* path) { return {std::move(config), nbSkippedElements}; } -android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) { - xmlDocPtr doc; - doc = xmlParseFile(path); - if (doc == NULL) { - ALOGE("%s: Could not parse document %s", __FUNCTION__, path); - return BAD_VALUE; - } - xmlNodePtr cur = xmlDocGetRootElement(doc); - if (cur == NULL) { - ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); - xmlFreeDoc(doc); - return BAD_VALUE; - } - if (xmlXIncludeProcess(doc) < 0) { - ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); - return BAD_VALUE; - } - size_t nbSkippedElements = 0; - return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements); -} - -static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"}; -static const int kConfigLocationListSize = - (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0])); -static const int gApmXmlConfigFilePathMaxLength = 128; - -static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml"; -static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName = - "audio_policy_configuration_a2dp_offload_disabled.xml"; - -android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) { - char audioPolicyXmlConfigFile[gApmXmlConfigFilePathMaxLength]; - std::vector fileNames; - status_t ret; - - if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) && - property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { - // A2DP offload supported but disabled: try to use special XML file - fileNames.push_back(apmA2dpOffloadDisabledXmlConfigFileName); - } - fileNames.push_back(apmXmlConfigFileName); - - for (const char* fileName : fileNames) { - for (int i = 0; i < kConfigLocationListSize; i++) { - snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), - "%s/%s", kConfigLocationList[i], fileName); - ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups); - if (ret == NO_ERROR) { - return ret; - } - } - } - return BAD_VALUE; -} - } // namespace engineConfig } // namespace android -- GitLab From efdbe3bfeff7c150d99900fddca005d48048f188 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 22 Feb 2019 17:23:37 -0800 Subject: [PATCH 1013/1530] Revert "audiopolicy: moves Stream Volume Curves to Engine" This reverts commit 539311942bdbac65fbf7f2fc1390b6b9ef850a1b. Bug: 125937703 Test: make --- .../common/managerdefinitions/Android.bp | 1 + .../include/AudioPolicyConfig.h | 13 +- .../include/IVolumeCurves.h | 44 ---- .../include/IVolumeCurvesCollection.h | 54 ++++ .../managerdefinitions/include/VolumeCurve.h | 239 ++++++++++++++++++ .../managerdefinitions/src/Serializer.cpp | 88 +++++++ .../managerdefinitions}/src/VolumeCurve.cpp | 51 ++-- .../config/audio_policy_configuration.xml | 6 +- .../audio_policy_configuration_generic.xml | 6 +- .../engine/common/include/EngineBase.h | 18 +- .../common/include/StreamVolumeCurves.h | 95 ------- .../engine/common/include/VolumeCurve.h | 192 -------------- .../engine/common/src/EngineBase.cpp | 49 ---- .../engine/common/src/EngineDefaultConfig.h | 123 +-------- .../engine/common/src/StreamVolumeCurves.cpp | 47 ---- .../engine/config/include/EngineConfig.h | 22 -- .../engine/config/src/EngineConfig.cpp | 126 --------- .../interface/AudioPolicyManagerInterface.h | 16 -- .../interface/AudioPolicyManagerObserver.h | 3 + .../audiopolicy/engineconfigurable/Android.mk | 2 - .../config/example/Android.mk | 18 -- .../audio_policy_engine_configuration.xml | 2 - ...o_policy_engine_default_stream_volumes.xml | 136 ---------- .../audio_policy_engine_stream_volumes.xml | 231 ----------------- .../engineconfigurable/src/Engine.cpp | 2 +- services/audiopolicy/enginedefault/Android.mk | 2 - .../enginedefault/config/example/Android.mk | 20 +- .../audio_policy_engine_configuration.xml | 2 - ...o_policy_engine_default_stream_volumes.xml | 136 ---------- .../audio_policy_engine_stream_volumes.xml | 231 ----------------- .../audiopolicy/enginedefault/src/Engine.cpp | 29 +++ .../audiopolicy/enginedefault/src/Engine.h | 2 + .../managerdefault/AudioPolicyManager.cpp | 80 +++--- .../managerdefault/AudioPolicyManager.h | 18 +- 34 files changed, 514 insertions(+), 1590 deletions(-) delete mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h create mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h create mode 100644 services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h rename services/audiopolicy/{engine/common => common/managerdefinitions}/src/VolumeCurve.cpp (68%) delete mode 100644 services/audiopolicy/engine/common/include/StreamVolumeCurves.h delete mode 100644 services/audiopolicy/engine/common/include/VolumeCurve.h delete mode 100644 services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp delete mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml delete mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml delete mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml delete mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp index c9037a1daa..e5ebab7e16 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.bp +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -19,6 +19,7 @@ cc_library_static { "src/Serializer.cpp", "src/SoundTriggerSession.cpp", "src/TypeConverter.cpp", + "src/VolumeCurve.cpp", ], shared_libs: [ "libcutils", diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index 2264d8f718..d52eb3d35c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -39,11 +40,13 @@ public: AudioPolicyConfig(HwModuleCollection &hwModules, DeviceVector &availableOutputDevices, DeviceVector &availableInputDevices, - sp &defaultOutputDevice) + sp &defaultOutputDevice, + VolumeCurvesCollection *volumes = nullptr) : mHwModules(hwModules), mAvailableOutputDevices(availableOutputDevices), mAvailableInputDevices(availableInputDevices), mDefaultOutputDevice(defaultOutputDevice), + mVolumeCurves(volumes), mIsSpeakerDrcEnabled(false) {} @@ -55,6 +58,13 @@ public: mSource = file; } + void setVolumes(const VolumeCurvesCollection &volumes) + { + if (mVolumeCurves != nullptr) { + *mVolumeCurves = volumes; + } + } + void setHwModules(const HwModuleCollection &hwModules) { mHwModules = hwModules; @@ -172,6 +182,7 @@ private: DeviceVector &mAvailableOutputDevices; DeviceVector &mAvailableInputDevices; sp &mDefaultOutputDevice; + VolumeCurvesCollection *mVolumeCurves; // TODO: remove when legacy conf file is removed. true on devices that use DRC on the // DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly. // Note: remove also speaker_drc_enabled from global configuration of XML config file. diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h deleted file mode 100644 index 93022fb2a0..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android { - -class IVolumeCurves -{ -public: - virtual ~IVolumeCurves() = default; - - virtual void clearCurrentVolumeIndex() = 0; - virtual void addCurrentVolumeIndex(audio_devices_t device, int index) = 0; - virtual bool canBeMuted() const = 0; - virtual int getVolumeIndexMin() const = 0; - virtual int getVolumeIndex(audio_devices_t device) const = 0; - virtual int getVolumeIndexMax() const = 0; - virtual float volIndexToDb(device_category device, int indexInUi) const = 0; - virtual bool hasVolumeIndexForDevice(audio_devices_t device) const = 0; - virtual status_t initVolume(int indexMin, int indexMax) = 0; - virtual void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const = 0; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h new file mode 100644 index 0000000000..750da55519 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace android { + +class IVolumeCurvesCollection +{ +public: + virtual ~IVolumeCurvesCollection() = default; + + virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) = 0; + virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, + int index) = 0; + virtual bool canBeMuted(audio_stream_type_t stream) = 0; + virtual int getVolumeIndexMin(audio_stream_type_t stream) const = 0; + virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) = 0; + virtual int getVolumeIndexMax(audio_stream_type_t stream) const = 0; + virtual float volIndexToDb(audio_stream_type_t stream, device_category device, + int indexInUi) const = 0; + virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) = 0; + + virtual void initializeVolumeCurves(bool /*isSpeakerDrcEnabled*/) {} + virtual void switchVolumeCurve(audio_stream_type_t src, audio_stream_type_t dst) = 0; + virtual void restoreOriginVolumeCurve(audio_stream_type_t stream) + { + switchVolumeCurve(stream, stream); + } + virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, + audio_devices_t device) const = 0; + + virtual void dump(String8 *dst) const = 0; +}; + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h new file mode 100644 index 0000000000..76ec198d02 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "IVolumeCurvesCollection.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct CurvePoint +{ + CurvePoint() {} + CurvePoint(int index, int attenuationInMb) : + mIndex(index), mAttenuationInMb(attenuationInMb) {} + uint32_t mIndex; + int mAttenuationInMb; +}; + +inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) +{ + return lhs.mIndex < rhs.mIndex; +} + +// A volume curve for a given use case and device category +// It contains of list of points of this curve expressing the attenuation in Millibels for +// a given volume index from 0 to 100 +class VolumeCurve : public RefBase +{ +public: + VolumeCurve(device_category device, audio_stream_type_t stream) : + mDeviceCategory(device), mStreamType(stream) {} + + device_category getDeviceCategory() const { return mDeviceCategory; } + audio_stream_type_t getStreamType() const { return mStreamType; } + + void add(const CurvePoint &point) { mCurvePoints.add(point); } + + float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; + + void dump(String8 *result) const; + +private: + SortedVector mCurvePoints; + device_category mDeviceCategory; + audio_stream_type_t mStreamType; +}; + +// Volume Curves for a given use case indexed by device category +class VolumeCurvesForStream : public KeyedVector > +{ +public: + VolumeCurvesForStream() : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) + { + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); + } + + sp getCurvesFor(device_category device) const + { + if (indexOfKey(device) < 0) { + return 0; + } + return valueFor(device); + } + + int getVolumeIndex(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME + if (mIndexCur.indexOfKey(device) < 0) { + device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; + } + return mIndexCur.valueFor(device); + } + + bool canBeMuted() const { return mCanBeMuted; } + void clearCurrentVolumeIndex() { mIndexCur.clear(); } + void addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); } + + void setVolumeIndexMin(int volIndexMin) { mIndexMin = volIndexMin; } + int getVolumeIndexMin() const { return mIndexMin; } + + void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; } + int getVolumeIndexMax() const { return mIndexMax; } + + bool hasVolumeIndexForDevice(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + return mIndexCur.indexOfKey(device) >= 0; + } + + const sp getOriginVolumeCurve(device_category deviceCategory) const + { + ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); + return mOriginVolumeCurves.valueFor(deviceCategory); + } + void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) + { + ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); + replaceValueFor(deviceCategory, volumeCurve); + } + + ssize_t add(const sp &volumeCurve) + { + device_category deviceCategory = volumeCurve->getDeviceCategory(); + ssize_t index = indexOfKey(deviceCategory); + if (index < 0) { + // Keep track of original Volume Curves per device category in order to switch curves. + mOriginVolumeCurves.add(deviceCategory, volumeCurve); + return KeyedVector::add(deviceCategory, volumeCurve); + } + return index; + } + + float volIndexToDb(device_category deviceCat, int indexInUi) const + { + sp vc = getCurvesFor(deviceCat); + if (vc != 0) { + return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); + } else { + ALOGE("Invalid device category %d for Volume Curve", deviceCat); + return 0.0f; + } + } + + void dump(String8 *dst, int spaces, bool curvePoints = false) const; + +private: + KeyedVector > mOriginVolumeCurves; + KeyedVector mIndexCur; /**< current volume index per device. */ + int mIndexMin; /**< min volume index. */ + int mIndexMax; /**< max volume index. */ + bool mCanBeMuted; /**< true is the stream can be muted. */ +}; + +// Collection of Volume Curves indexed by use case +class VolumeCurvesCollection : public KeyedVector, + public IVolumeCurvesCollection +{ +public: + VolumeCurvesCollection() + { + // Create an empty collection of curves + for (ssize_t i = 0 ; i < AUDIO_STREAM_CNT; i++) { + audio_stream_type_t stream = static_cast(i); + KeyedVector::add(stream, VolumeCurvesForStream()); + } + } + + // Once XML has been parsed, must be call first to sanity check table and initialize indexes + virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) + { + editValueAt(stream).setVolumeIndexMin(indexMin); + editValueAt(stream).setVolumeIndexMax(indexMax); + return NO_ERROR; + } + virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) + { + editCurvesFor(stream).clearCurrentVolumeIndex(); + } + virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index) + { + editCurvesFor(stream).addCurrentVolumeIndex(device, index); + } + virtual bool canBeMuted(audio_stream_type_t stream) { return getCurvesFor(stream).canBeMuted(); } + + virtual int getVolumeIndexMin(audio_stream_type_t stream) const + { + return getCurvesFor(stream).getVolumeIndexMin(); + } + virtual int getVolumeIndexMax(audio_stream_type_t stream) const + { + return getCurvesFor(stream).getVolumeIndexMax(); + } + virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) + { + return getCurvesFor(stream).getVolumeIndex(device); + } + virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) + { + const VolumeCurvesForStream &sourceCurves = getCurvesFor(streamSrc); + VolumeCurvesForStream &dstCurves = editCurvesFor(streamDst); + ALOG_ASSERT(sourceCurves.size() == dstCurves.size(), "device category not aligned"); + for (size_t index = 0; index < sourceCurves.size(); index++) { + device_category cat = sourceCurves.keyAt(index); + dstCurves.setVolumeCurve(cat, sourceCurves.getOriginVolumeCurve(cat)); + } + } + virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const + { + return getCurvesFor(stream).volIndexToDb(cat, indexInUi); + } + virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, + audio_devices_t device) const + { + return getCurvesFor(stream).hasVolumeIndexForDevice(device); + } + + void dump(String8 *dst) const override; + + ssize_t add(const sp &volumeCurve) + { + audio_stream_type_t streamType = volumeCurve->getStreamType(); + return editCurvesFor(streamType).add(volumeCurve); + } + VolumeCurvesForStream &editCurvesFor(audio_stream_type_t stream) + { + ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); + return editValueAt(stream); + } + const VolumeCurvesForStream &getCurvesFor(audio_stream_type_t stream) const + { + ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); + return valueFor(stream); + } +}; + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 81d3968c76..98d375c115 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -201,6 +201,25 @@ struct GlobalConfigTraits static status_t deserialize(const xmlNode *root, AudioPolicyConfig *config); }; +struct VolumeTraits : public AndroidCollectionTraits +{ + static constexpr const char *tag = "volume"; + static constexpr const char *collectionTag = "volumes"; + static constexpr const char *volumePointTag = "point"; + static constexpr const char *referenceTag = "reference"; + + struct Attributes + { + static constexpr const char *stream = "stream"; + static constexpr const char *deviceCategory = "deviceCategory"; + static constexpr const char *reference = "ref"; + static constexpr const char *referenceName = "name"; + }; + + static Return deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); + // No Children +}; + struct SurroundSoundTraits { static constexpr const char *tag = "surroundSound"; @@ -684,6 +703,67 @@ status_t GlobalConfigTraits::deserialize(const xmlNode *root, AudioPolicyConfig return NO_ERROR; } +Return VolumeTraits::deserialize(const xmlNode *cur, + PtrSerializingCtx /*serializingContext*/) +{ + std::string streamTypeLiteral = getXmlAttribute(cur, Attributes::stream); + if (streamTypeLiteral.empty()) { + ALOGE("%s: No %s found", __func__, Attributes::stream); + return Status::fromStatusT(BAD_VALUE); + } + audio_stream_type_t streamType; + if (!StreamTypeConverter::fromString(streamTypeLiteral, streamType)) { + ALOGE("%s: Invalid %s", __func__, Attributes::stream); + return Status::fromStatusT(BAD_VALUE); + } + std::string deviceCategoryLiteral = getXmlAttribute(cur, Attributes::deviceCategory); + if (deviceCategoryLiteral.empty()) { + ALOGE("%s: No %s found", __func__, Attributes::deviceCategory); + return Status::fromStatusT(BAD_VALUE); + } + device_category deviceCategory; + if (!DeviceCategoryConverter::fromString(deviceCategoryLiteral, deviceCategory)) { + ALOGE("%s: Invalid %s=%s", __func__, Attributes::deviceCategory, + deviceCategoryLiteral.c_str()); + return Status::fromStatusT(BAD_VALUE); + } + + std::string referenceName = getXmlAttribute(cur, Attributes::reference); + const xmlNode *ref = NULL; + if (!referenceName.empty()) { + ref = getReference(cur->parent, referenceName); + if (ref == NULL) { + ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); + return Status::fromStatusT(BAD_VALUE); + } + } + + Element volCurve = new VolumeCurve(deviceCategory, streamType); + + for (const xmlNode *child = referenceName.empty() ? cur->xmlChildrenNode : ref->xmlChildrenNode; + child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, reinterpret_cast(volumePointTag))) { + auto pointDefinition = make_xmlUnique(xmlNodeListGetString( + child->doc, child->xmlChildrenNode, 1)); + if (pointDefinition == nullptr) { + return Status::fromStatusT(BAD_VALUE); + } + ALOGV("%s: %s=%s", + __func__, tag, reinterpret_cast(pointDefinition.get())); + std::vector point; + collectionFromString>( + reinterpret_cast(pointDefinition.get()), point, ","); + if (point.size() != 2) { + ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, + reinterpret_cast(pointDefinition.get())); + return Status::fromStatusT(BAD_VALUE); + } + volCurve->add(CurvePoint(point[0], point[1])); + } + } + return volCurve; +} + status_t SurroundSoundTraits::deserialize(const xmlNode *root, AudioPolicyConfig *config) { config->setDefaultSurroundFormats(); @@ -771,6 +851,14 @@ status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig } config->setHwModules(modules); + // deserialize volume section + VolumeTraits::Collection volumes; + status = deserializeCollection(root, &volumes, config); + if (status != NO_ERROR) { + return status; + } + config->setVolumes(volumes); + // Global Configuration GlobalConfigTraits::deserialize(root, config); diff --git a/services/audiopolicy/engine/common/src/VolumeCurve.cpp b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp similarity index 68% rename from services/audiopolicy/engine/common/src/VolumeCurve.cpp rename to services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp index be2ca730ef..2625733f20 100644 --- a/services/audiopolicy/engine/common/src/VolumeCurve.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp @@ -64,7 +64,8 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) ((float)(mCurvePoints[indexInUiPosition].mIndex - mCurvePoints[indexInUiPosition - 1].mIndex)) ); - ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", + ALOGV("VOLUME mDeviceCategory %d, mStreamType %d vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", + mDeviceCategory, mStreamType, mCurvePoints[indexInUiPosition - 1].mIndex, volIdx, mCurvePoints[indexInUiPosition].mIndex, ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels, @@ -73,35 +74,55 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) return decibels; } -void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const +void VolumeCurve::dump(String8 *dst) const { - if (!curvePoints) { - return; - } dst->append(" {"); for (size_t i = 0; i < mCurvePoints.size(); i++) { - dst->appendFormat("%*s (%3d, %5d)", spaces, "", + dst->appendFormat("(%3d, %5d)", mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb); - dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); + dst->append(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); } } -void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const +void VolumeCurvesForStream::dump(String8 *dst, int spaces = 0, bool curvePoints) const { if (!curvePoints) { - dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "", - mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); - for (const auto &pair : mIndexCur) { - dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second); + dst->appendFormat("%s %02d %02d ", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + for (size_t i = 0; i < mIndexCur.size(); i++) { + dst->appendFormat("%04x : %02d, ", mIndexCur.keyAt(i), mIndexCur.valueAt(i)); } - dst->appendFormat("\n"); + dst->append("\n"); return; } + for (size_t i = 0; i < size(); i++) { std::string deviceCatLiteral; DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral); - dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str()); - valueAt(i)->dump(dst, 2, true); + dst->appendFormat("%*s %s :", + spaces, "", deviceCatLiteral.c_str()); + valueAt(i)->dump(dst); + } + dst->append("\n"); +} + +void VolumeCurvesCollection::dump(String8 *dst) const +{ + dst->append("\nStreams dump:\n"); + dst->append( + " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); + for (size_t i = 0; i < size(); i++) { + dst->appendFormat(" %02zu ", i); + valueAt(i).dump(dst); + } + dst->append("\nVolume Curves for Use Cases (aka Stream types) dump:\n"); + for (size_t i = 0; i < size(); i++) { + std::string streamTypeLiteral; + StreamTypeConverter::toString(keyAt(i), streamTypeLiteral); + dst->appendFormat( + " %s (%02zu): Curve points for device category (index, attenuation in millibel)\n", + streamTypeLiteral.c_str(), i); + valueAt(i).dump(dst, 2, true); } } diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index b4cc1d3936..42c52deec9 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -191,11 +191,7 @@ - + diff --git a/services/audiopolicy/config/audio_policy_configuration_generic.xml b/services/audiopolicy/config/audio_policy_configuration_generic.xml index 9ad609df2b..40dcc22b58 100644 --- a/services/audiopolicy/config/audio_policy_configuration_generic.xml +++ b/services/audiopolicy/config/audio_policy_configuration_generic.xml @@ -30,11 +30,7 @@ - + diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index bc027e2a3f..5c33fb39b8 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -19,7 +19,6 @@ #include #include #include -#include namespace android { namespace audio_policy { @@ -68,10 +67,6 @@ public: status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override; - VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override; - - VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override; - void dump(String8 *dst) const override; @@ -92,16 +87,7 @@ public: return is_state_in_call(getPhoneState()); } - VolumeSource toVolumeSource(audio_stream_type_t stream) const - { - return static_cast(stream); - } - - status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); - - status_t restoreOriginVolumeCurve(audio_stream_type_t stream); - - private: +private: AudioPolicyManagerObserver *mApmObserver = nullptr; ProductStrategyMap mProductStrategies; @@ -109,8 +95,6 @@ public: /** current forced use configuration. */ audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {}; - - StreamVolumeCurves mStreamVolumeCurves; }; } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h deleted file mode 100644 index 5b0b7d6272..0000000000 --- a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include -#include - -namespace android { - -class StreamVolumeCurves -{ -public: - StreamVolumeCurves() = default; - - /** - * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves - * from one stream type to another. - * @param src source stream type - * @param dst destination stream type - */ - status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) - { - if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) { - ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst); - return NO_INIT; - } - const VolumeCurves &sourceCurves = getCurvesFor(streamSrc); - VolumeCurves &dstCurves = editCurvesFor(streamDst); - return dstCurves.switchCurvesFrom(sourceCurves); - } - void dump(String8 *dst, int spaces = 0) const; - - void add(const VolumeCurves &curves, audio_stream_type_t streamType) - { - mCurves.emplace(streamType, curves); - } - - bool hasCurvesFor(audio_stream_type_t stream) - { - return mCurves.find(stream) != end(mCurves); - } - - VolumeCurves &editCurvesFor(audio_stream_type_t stream) - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves[stream]; - } - const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves.at(stream); - } - /** - * @brief getVolumeCurvesForStream - * @param stream type for which the volume curves interface is requested - * @return the VolumeCurves for a given stream type. - */ - VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream) - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves[stream]; - } - /** - * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume - * curves for a given stream type - * @param stream for which the volume curves will be restored. - */ - status_t restoreOriginVolumeCurve(audio_stream_type_t stream) - { - if (!hasCurvesFor(stream)) { - ALOGE("%s: No curves defined for streams", __FUNCTION__); - return NO_INIT; - } - return switchVolumeCurve(stream, stream); - } - -private: - std::map mCurves; -}; - -} // namespace android diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h deleted file mode 100644 index c2a1da702e..0000000000 --- a/services/audiopolicy/engine/common/include/VolumeCurve.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "IVolumeCurves.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct CurvePoint -{ - CurvePoint() {} - CurvePoint(int index, int attenuationInMb) : - mIndex(index), mAttenuationInMb(attenuationInMb) {} - uint32_t mIndex; - int mAttenuationInMb; -}; - -inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) -{ - return lhs.mIndex < rhs.mIndex; -} - -// A volume curve for a given use case and device category -// It contains of list of points of this curve expressing the attenuation in Millibels for -// a given volume index from 0 to 100 -class VolumeCurve : public RefBase -{ -public: - VolumeCurve(device_category device) : mDeviceCategory(device) {} - - void add(const CurvePoint &point) { mCurvePoints.add(point); } - - float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; - - void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const; - - device_category getDeviceCategory() const { return mDeviceCategory; } - -private: - const device_category mDeviceCategory; - SortedVector mCurvePoints; -}; - -// Volume Curves for a given use case indexed by device category -class VolumeCurves : public KeyedVector >, - public IVolumeCurves -{ -public: - VolumeCurves(int indexMin = 0, int indexMax = 100) : - mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT) - { - addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); - } - VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) : - mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream) - { - addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); - } - - // Once XML has been parsed, must be call first to sanity check table and initialize indexes - virtual status_t initVolume(int indexMin, int indexMax) - { - mIndexMin = indexMin; - mIndexMax = indexMax; - return NO_ERROR; - } - - sp getCurvesFor(device_category device) const - { - if (indexOfKey(device) < 0) { - return 0; - } - return valueFor(device); - } - - virtual int getVolumeIndex(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME - if (mIndexCur.find(device) == end(mIndexCur)) { - device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; - } - return mIndexCur.at(device); - } - - virtual bool canBeMuted() const { return mCanBeMuted; } - virtual void clearCurrentVolumeIndex() { mIndexCur.clear(); } - void addCurrentVolumeIndex(audio_devices_t device, int index) override - { - mIndexCur[device] = index; - } - - int getVolumeIndexMin() const { return mIndexMin; } - - int getVolumeIndexMax() const { return mIndexMax; } - - bool hasVolumeIndexForDevice(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - return mIndexCur.find(device) != end(mIndexCur); - } - - status_t switchCurvesFrom(const VolumeCurves &referenceCurves) - { - if (size() != referenceCurves.size()) { - ALOGE("%s! device category not aligned, cannot switch", __FUNCTION__); - return BAD_TYPE; - } - for (size_t index = 0; index < size(); index++) { - device_category cat = keyAt(index); - setVolumeCurve(cat, referenceCurves.getOriginVolumeCurve(cat)); - } - return NO_ERROR; - } - status_t restoreOriginVolumeCurve() - { - return switchCurvesFrom(*this); - } - - const sp getOriginVolumeCurve(device_category deviceCategory) const - { - ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); - return mOriginVolumeCurves.valueFor(deviceCategory); - } - void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) - { - ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); - replaceValueFor(deviceCategory, volumeCurve); - } - - ssize_t add(const sp &volumeCurve) - { - device_category deviceCategory = volumeCurve->getDeviceCategory(); - ssize_t index = indexOfKey(deviceCategory); - if (index < 0) { - // Keep track of original Volume Curves per device category in order to switch curves. - mOriginVolumeCurves.add(deviceCategory, volumeCurve); - return KeyedVector::add(deviceCategory, volumeCurve); - } - return index; - } - - virtual float volIndexToDb(device_category deviceCat, int indexInUi) const - { - sp vc = getCurvesFor(deviceCat); - if (vc != 0) { - return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); - } else { - ALOGE("Invalid device category %d for Volume Curve", deviceCat); - return 0.0f; - } - } - - audio_stream_type_t getStreamType() const { return mStream; } - - void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override; - -private: - KeyedVector > mOriginVolumeCurves; - std::map mIndexCur; /**< current volume index per device. */ - /*const*/ int mIndexMin; /**< min volume index. */ - /*const*/ int mIndexMax; /**< max volume index. */ - const bool mCanBeMuted = true; /**< true is the stream can be muted. */ - - const audio_stream_type_t mStream; /**< Keep it for legacy. */ -}; - -} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 2449b61d12..755f2a88e3 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -55,10 +55,8 @@ status_t EngineBase::setPhoneState(audio_mode_t state) if (!is_state_in_call(oldState) && is_state_in_call(state)) { ALOGV(" Entering call in setPhoneState()"); - switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF); } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { ALOGV(" Exiting call in setPhoneState()"); - restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); } return NO_ERROR; } @@ -110,31 +108,6 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() productStrategies[strategyId] = strategy; } }; - auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) { - for (auto &configVolume : configVolumes) { - audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; - if (configVolume.stream.empty() || - !StreamTypeConverter::fromString(configVolume.stream, streamType)) { - ALOGE("%s: Invalid stream type", __FUNCTION__); - continue; - } - VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax); - for (auto &configCurve : configVolume.volumeCurves) { - device_category deviceCategory = DEVICE_CATEGORY_SPEAKER; - if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, - deviceCategory)) { - ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str()); - continue; - } - sp curve = new VolumeCurve(deviceCategory); - for (auto &point : configCurve.curvePoints) { - curve->add({point.index, point.attenuationInMb}); - } - volumeCurves.add(curve); - } - streamVolumeCollection.add(volumeCurves, streamType); - } - }; auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { @@ -143,7 +116,6 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); - loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves); return result; } @@ -201,30 +173,9 @@ status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &stra return NO_ERROR; } -VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) -{ - return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr)); -} - -VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) -{ - return &mStreamVolumeCurves.getVolumeCurvesForStream(stream); -} - -status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) -{ - return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);; -} - -status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream) -{ - return mStreamVolumeCurves.restoreOriginVolumeCurve(stream); -} - void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); - mStreamVolumeCurves.dump(dst, 2); } } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 86a59b00ee..3940c0c929 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,131 +131,10 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; -const engineConfig::VolumeGroups gVolumeGroups = { - {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, - }, - }, - {"system", "AUDIO_STREAM_SYSTEM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"ring", "AUDIO_STREAM_RING", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"music", "AUDIO_STREAM_MUSIC", 0, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"alarm", "AUDIO_STREAM_ALARM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - }, - }, - {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"tts", "AUDIO_STREAM_TTS", 0, 16, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - }, - }, - {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, - {"patch", "AUDIO_STREAM_PATCH", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, -}; - const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, - {}, - gVolumeGroups + {} }; } // namespace android diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp deleted file mode 100644 index fe3b0007b6..0000000000 --- a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 "APM::Engine::StreamVolumeCurves" -//#define LOG_NDEBUG 0 - -#include "StreamVolumeCurves.h" -#include - -namespace android { - -void StreamVolumeCurves::dump(String8 *dst, int spaces) const -{ - if (mCurves.empty()) { - return; - } - dst->appendFormat("\n%*sStreams dump:\n", spaces, ""); - dst->appendFormat( - "%*sStream Can be muted Index Min Index Max Index Cur [device : index]...\n", spaces + 2, ""); - for (const auto &streamCurve : mCurves) { - streamCurve.second.dump(dst, spaces + 2, false); - } - dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, ""); - for (const auto &streamCurve : mCurves) { - std::string streamTypeLiteral; - StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral); - dst->appendFormat( - " %s (%02d): Curve points for device category (index, attenuation in millibel)\n", - streamTypeLiteral.c_str(), streamCurve.first); - streamCurve.second.dump(dst, spaces + 2, true); - } -} - -} // namespace android diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index f7caad238d..e18f68759d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -45,27 +45,6 @@ struct AttributesGroup { using AttributesGroups = std::vector; -struct CurvePoint { - int index; - int attenuationInMb; -}; -using CurvePoints = std::vector; - -struct VolumeCurve { - std::string deviceCategory; - CurvePoints curvePoints; -}; -using VolumeCurves = std::vector; - -struct VolumeGroup { - std::string name; - std::string stream; - int indexMin; - int indexMax; - VolumeCurves volumeCurves; -}; -using VolumeGroups = std::vector; - struct ProductStrategy { std::string name; AttributesGroups attributesGroups; @@ -99,7 +78,6 @@ struct Config { ProductStrategies productStrategies; Criteria criteria; CriterionTypes criterionTypes; - VolumeGroups volumeGroups; }; /** Result of `parse(const char*)` */ diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 227ebd8cde..3aa38cf965 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -107,35 +107,6 @@ struct CriterionTraits : public BaseSerializerTraits { static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &collection); }; -struct VolumeTraits : public BaseSerializerTraits { - static constexpr const char *tag = "volume"; - static constexpr const char *collectionTag = "volumes"; - static constexpr const char *volumePointTag = "point"; - - struct Attributes { - static constexpr const char *deviceCategory = "deviceCategory"; - static constexpr const char *reference = "ref"; /**< For volume curves factorization. */ - }; - - static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, - Collection &collection); -}; -struct VolumeGroupTraits : public BaseSerializerTraits { - static constexpr const char *tag = "volumeGroup"; - static constexpr const char *collectionTag = "volumeGroups"; - - struct Attributes { - static constexpr const char *name = "name"; - static constexpr const char *stream = "stream"; // For legacy volume curves - static constexpr const char *indexMin = "indexMin"; - static constexpr const char *indexMax = "indexMax"; - }; - - static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, - Collection &collection); -}; - -using xmlCharUnique = std::unique_ptr; using xmlCharUnique = std::unique_ptr; @@ -412,100 +383,6 @@ status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, return NO_ERROR; } -status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) -{ - std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory); - if (deviceCategory.empty()) { - ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory); - } - - std::string referenceName = getXmlAttribute(root, Attributes::reference); - const _xmlNode *ref = NULL; - if (!referenceName.empty()) { - getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag); - if (ref == NULL) { - ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str()); - return BAD_VALUE; - } - } - // Retrieve curve point from reference element if found or directly from current curve - CurvePoints curvePoints; - for (const xmlNode *child = referenceName.empty() ? - root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { - if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) { - xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (pointXml == NULL) { - return BAD_VALUE; - } - ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast(pointXml.get())); - std::vector point; - collectionFromString>( - reinterpret_cast(pointXml.get()), point, ","); - if (point.size() != 2) { - ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, - reinterpret_cast(pointXml.get())); - return BAD_VALUE; - } - curvePoints.push_back({point[0], point[1]}); - } - } - volumes.push_back({ deviceCategory, curvePoints }); - return NO_ERROR; -} - -status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) -{ - std::string name; - std::string stream = {}; - int indexMin = 0; - int indexMax = 0; - - for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) { - if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) { - xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (nameXml == nullptr) { - return BAD_VALUE; - } - name = reinterpret_cast(nameXml.get()); - } - if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) { - xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (streamXml == nullptr) { - return BAD_VALUE; - } - stream = reinterpret_cast(streamXml.get()); - } - if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) { - xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (indexMinXml == nullptr) { - return BAD_VALUE; - } - std::string indexMinLiteral(reinterpret_cast(indexMinXml.get())); - if (!convertTo(indexMinLiteral, indexMin)) { - return BAD_VALUE; - } - } - if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) { - xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (indexMaxXml == nullptr) { - return BAD_VALUE; - } - std::string indexMaxLiteral(reinterpret_cast(indexMaxXml.get())); - if (!convertTo(indexMaxLiteral, indexMax)) { - return BAD_VALUE; - } - } - } - ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d", - __func__, name.c_str(), stream.c_str(), indexMin, indexMax); - - VolumeCurves groupVolumeCurves; - size_t skipped = 0; - deserializeCollection(doc, root, groupVolumeCurves, skipped); - volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves }); - return NO_ERROR; -} - ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -537,9 +414,6 @@ ParsingResult parse(const char* path) { doc, cur, config->criteria, nbSkippedElements); deserializeCollection( doc, cur, config->criterionTypes, nbSkippedElements); - deserializeCollection( - doc, cur, config->volumeGroups, nbSkippedElements); - return {std::move(config), nbSkippedElements}; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index c9e9507705..498cc3bf82 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -235,21 +234,6 @@ public: */ virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0; - /** - * @brief getVolumeCurvesForAttributes retrieves the Volume Curves interface for the - * requested Audio Attributes. - * @param attr to be considered - * @return IVolumeCurves interface pointer if found, nullptr otherwise - */ - virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0; - - /** - * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream - * @param stream to be considered - * @return IVolumeCurves interface pointer if found, nullptr otherwise - */ - virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0; - virtual void dump(String8 *dst) const = 0; protected: diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h index 43ba625f9c..b7902cf2e5 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -50,6 +51,8 @@ public: virtual const DeviceVector &getAvailableInputDevices() const = 0; + virtual IVolumeCurvesCollection &getVolumeCurves() = 0; + virtual const sp &getDefaultOutputDevice() const = 0; protected: diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index 2b7e4c8dbc..bbd9688529 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -12,8 +12,6 @@ LOCAL_SRC_FILES := \ src/EngineInstance.cpp \ src/Stream.cpp \ src/InputSource.cpp \ - ../engine/common/src/VolumeCurve.cpp \ - ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index ef476f765c..95a2ecc08d 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -20,8 +20,6 @@ LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ audio_policy_engine_product_strategies_phone.xml \ - audio_policy_engine_stream_volumes.xml \ - audio_policy_engine_default_stream_volumes.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml @@ -36,22 +34,6 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) -include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_stream_volumes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml index 4ca33b4525..ab61d8a937 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml @@ -17,8 +17,6 @@ - - diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml deleted file mode 100644 index 21e6dd58ac..0000000000 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - 0,0 - 100,0 - - - 0,-9600 - 100,-9600 - - - - 1,-2400 - 33,-1800 - 66,-1200 - 100,-600 - - - - 1,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - 1,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - 1,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - 1,-4680 - 42,-2070 - 85,-540 - 100,0 - - - - 1,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - 1,-5800 - 20,-4000 - 60,-2100 - 100,-1000 - - - - 1,-12700 - 20,-8000 - 60,-4000 - 100,0 - - - - - - - - - 0,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - - 0,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - - 0,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - - 0,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - - 0,-5800 - 20,-4000 - 60,-2100 - 100,-1000 - - - - - 0,-12700 - 20,-8000 - 60,-4000 - 100,0 - - diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml deleted file mode 100644 index 73bde1fb7e..0000000000 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - AUDIO_STREAM_VOICE_CALL - 1 - 7 - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - 0,-2400 - 33,-1600 - 66,-800 - 100,0 - - - 0,-2700 - 33,-1800 - 66,-900 - 100,0 - - - - - - - AUDIO_STREAM_SYSTEM - 0 - 7 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-5100 - 57,-2800 - 71,-2500 - 85,-2300 - 100,-2100 - - - - - - - - - AUDIO_STREAM_RING - 0 - 7 - - - - - - - - - AUDIO_STREAM_MUSIC - 0 - 25 - - - - - - - - - AUDIO_STREAM_ALARM - 1 - 7 - - - - - - - - - AUDIO_STREAM_NOTIFICATION - 0 - 7 - - - - - - - - - AUDIO_STREAM_BLUETOOTH_SCO - 0 - 15 - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - 0,-2400 - 33,-1600 - 66,-800 - 100,0 - - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - - - - - AUDIO_STREAM_ENFORCED_AUDIBLE - 0 - 7 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-3400 - 71,-2400 - 100,-2000 - - - - - - - - - AUDIO_STREAM_DTMF - 0 - 15 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-4000 - 71,-2400 - 100,-1400 - - - - - - - - - AUDIO_STREAM_TTS - 0 - 15 - - - - - - - - - AUDIO_STREAM_ACCESSIBILITY - 1 - 15 - - - - - - - - - AUDIO_STREAM_REROUTING - 0 - 1 - - - - - - - - - AUDIO_STREAM_PATCH - 0 - 1 - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 89a1694a92..719ef01031 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -113,7 +113,7 @@ bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &profile) { if (setPropertyForKey(stream, profile)) { - switchVolumeCurve(profile, stream); + getApmObserver()->getVolumeCurves().switchVolumeCurve(profile, stream); return true; } return false; diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index 94fa788a90..95eac1c17e 100644 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -8,8 +8,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/Engine.cpp \ src/EngineInstance.cpp \ - ../engine/common/src/VolumeCurve.cpp \ - ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk index f06ee4cf13..866466fc14 100644 --- a/services/audiopolicy/enginedefault/config/example/Android.mk +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -16,9 +16,7 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml \ - audio_policy_engine_stream_volumes.xml \ - audio_policy_engine_default_stream_volumes.xml + audio_policy_engine_product_strategies_phone.xml include $(BUILD_PREBUILT) @@ -31,20 +29,4 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) -include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_stream_volumes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) -include $(BUILD_PREBUILT) - endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml index 4ca33b4525..ab61d8a937 100644 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml @@ -17,8 +17,6 @@ - - diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml deleted file mode 100644 index 21e6dd58ac..0000000000 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - 0,0 - 100,0 - - - 0,-9600 - 100,-9600 - - - - 1,-2400 - 33,-1800 - 66,-1200 - 100,-600 - - - - 1,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - 1,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - 1,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - 1,-4680 - 42,-2070 - 85,-540 - 100,0 - - - - 1,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - 1,-5800 - 20,-4000 - 60,-2100 - 100,-1000 - - - - 1,-12700 - 20,-8000 - 60,-4000 - 100,0 - - - - - - - - - 0,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - - 0,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - - 0,-5800 - 20,-4000 - 60,-1700 - 100,0 - - - - - 0,-4950 - 33,-3350 - 66,-1700 - 100,0 - - - - - 0,-5800 - 20,-4000 - 60,-2100 - 100,-1000 - - - - - 0,-12700 - 20,-8000 - 60,-4000 - 100,0 - - diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml deleted file mode 100644 index 73bde1fb7e..0000000000 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - AUDIO_STREAM_VOICE_CALL - 1 - 7 - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - 0,-2400 - 33,-1600 - 66,-800 - 100,0 - - - 0,-2700 - 33,-1800 - 66,-900 - 100,0 - - - - - - - AUDIO_STREAM_SYSTEM - 0 - 7 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-5100 - 57,-2800 - 71,-2500 - 85,-2300 - 100,-2100 - - - - - - - - - AUDIO_STREAM_RING - 0 - 7 - - - - - - - - - AUDIO_STREAM_MUSIC - 0 - 25 - - - - - - - - - AUDIO_STREAM_ALARM - 1 - 7 - - - - - - - - - AUDIO_STREAM_NOTIFICATION - 0 - 7 - - - - - - - - - AUDIO_STREAM_BLUETOOTH_SCO - 0 - 15 - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - 0,-2400 - 33,-1600 - 66,-800 - 100,0 - - - 0,-4200 - 33,-2800 - 66,-1400 - 100,0 - - - - - - - AUDIO_STREAM_ENFORCED_AUDIBLE - 0 - 7 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-3400 - 71,-2400 - 100,-2000 - - - - - - - - - AUDIO_STREAM_DTMF - 0 - 15 - - 1,-3000 - 33,-2600 - 66,-2200 - 100,-1800 - - - 1,-4000 - 71,-2400 - 100,-1400 - - - - - - - - - AUDIO_STREAM_TTS - 0 - 15 - - - - - - - - - AUDIO_STREAM_ACCESSIBILITY - 1 - 15 - - - - - - - - - AUDIO_STREAM_REROUTING - 0 - 1 - - - - - - - - - AUDIO_STREAM_PATCH - 0 - 1 - - - - - - - - diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index fd6a013a76..43023a8784 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -66,6 +66,35 @@ Engine::Engine() } } +status_t Engine::setPhoneState(audio_mode_t state) +{ + ALOGV("setPhoneState() state %d", state); + + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return BAD_VALUE; + } + + if (state == getPhoneState()) { + ALOGW("setPhoneState() setting same state %d", state); + return BAD_VALUE; + } + + // store previous phone state for management of sonification strategy below + int oldState = getPhoneState(); + EngineBase::setPhoneState(state); + + if (!is_state_in_call(oldState) && is_state_in_call(state)) { + ALOGV(" Entering call in setPhoneState()"); + getApmObserver()->getVolumeCurves().switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, + AUDIO_STREAM_DTMF); + } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { + ALOGV(" Exiting call in setPhoneState()"); + getApmObserver()->getVolumeCurves().restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); + } + return NO_ERROR; +} + status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { switch(usage) { diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index d8a369891d..15fc358687 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -55,6 +55,8 @@ private: /// /// from EngineBase, so from AudioPolicyManagerInterface /// + status_t setPhoneState(audio_mode_t mode) override; + status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b563a042b2..5b752ead9a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1710,7 +1710,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, - getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()), + mVolumeCurves->getVolumeIndex(stream, outputDesc->devices().types()), outputDesc, outputDesc->devices().types()); @@ -2382,15 +2382,14 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, ALOGE("%s for stream %d: invalid min %d or max %d", __func__, stream , indexMin, indexMax); return; } - // @todo: our proposal now use XML to store Indexes Min & Max - getVolumeCurves(stream).initVolume(indexMin, indexMax); + mVolumeCurves->initStreamVolume(stream, indexMin, indexMax); // initialize other private stream volumes which follow this one for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - getVolumeCurves((audio_stream_type_t)curStream).initVolume(indexMin, indexMax); + mVolumeCurves->initStreamVolume((audio_stream_type_t)curStream, indexMin, indexMax); } } @@ -2398,13 +2397,13 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { - auto &curves = getVolumeCurves(stream); + // VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but // can be muted directly by an app that has MODIFY_PHONE_STATE permission. - if (((index < curves.getVolumeIndexMin()) && + if (((index < mVolumeCurves->getVolumeIndexMin(stream)) && !((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && index == 0)) || - (index > curves.getVolumeIndexMax())) { + (index > mVolumeCurves->getVolumeIndexMax(stream))) { return BAD_VALUE; } if (!audio_is_output_device(device)) { @@ -2412,7 +2411,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, } // Force max volume if stream cannot be muted - if (!curves.canBeMuted()) index = curves.getVolumeIndexMax(); + if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream); ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d", stream, device, index); @@ -2422,8 +2421,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - auto &curCurves = getVolumeCurves(static_cast(curStream)); - curCurves.addCurrentVolumeIndex(device, index); + mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index); } // update volume on all outputs and streams matching the following: @@ -2457,7 +2455,8 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, curStreamDevice |= device; applyVolume = (Volume::getDeviceForVolume(curDevice) & curStreamDevice) != 0; } else { - applyVolume = !curves.hasVolumeIndexForDevice(curStreamDevice); + applyVolume = !mVolumeCurves->hasVolumeIndexForDevice( + stream, curStreamDevice); } // rescale index before applying to curStream as ranges may be different for // stream and curStream @@ -2466,10 +2465,9 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, //FIXME: workaround for truncated touch sounds // delayed volume change for system stream to be removed when the problem is // handled by system UI - status_t volStatus = checkAndSetVolume( - (audio_stream_type_t)curStream, idx, desc, curDevice, - (stream == AUDIO_STREAM_SYSTEM) ? - TOUCH_SOUND_FIXED_DELAY_MS : 0); + status_t volStatus = + checkAndSetVolume((audio_stream_type_t)curStream, idx, desc, curDevice, + (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0); if (volStatus != NO_ERROR) { status = volStatus; } @@ -2496,7 +2494,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, } device = Volume::getDeviceForVolume(device); - *index = getVolumeCurves(stream).getVolumeIndex(device); + *index = mVolumeCurves->getVolumeIndex(stream, device); ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); return NO_ERROR; } @@ -2929,6 +2927,7 @@ void AudioPolicyManager::dump(String8 *dst) const mHwModulesAll.dump(dst); mOutputs.dump(dst); mInputs.dump(dst); + mVolumeCurves->dump(dst); mEffects.dump(dst); mAudioPatches.dump(dst); mPolicyMixes.dump(dst); @@ -4115,7 +4114,9 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mpClientInterface(clientInterface), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mA2dpSuspended(false), - mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice), + mVolumeCurves(new VolumeCurvesCollection()), + mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, + mDefaultOutputDevice, static_cast(mVolumeCurves.get())), mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), @@ -4149,6 +4150,8 @@ void AudioPolicyManager::loadConfig() { } status_t AudioPolicyManager::initialize() { + mVolumeCurves->initializeVolumeCurves(getConfig().isSpeakerDrcEnabled()); + // Once policy config has been parsed, retrieve an instance of the engine and initialize it. audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance(); if (!engineInstance) { @@ -5529,8 +5532,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, int index, audio_devices_t device) { - auto &curves = getVolumeCurves(stream); - float volumeDB = curves.volIndexToDb(Volume::getDeviceCategory(device), index); + float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index); // handle the case of accessibility active while a ringtone is playing: if the ringtone is much // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch @@ -5555,7 +5557,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, case AUDIO_STREAM_ENFORCED_AUDIBLE: case AUDIO_STREAM_DTMF: case AUDIO_STREAM_ACCESSIBILITY: { - int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device); + int voiceVolumeIndex = + mVolumeCurves->getVolumeIndex(AUDIO_STREAM_VOICE_CALL, device); const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; @@ -5589,7 +5592,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && - getVolumeCurves(stream).canBeMuted()) { + mVolumeCurves->canBeMuted(stream)) { // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was // just stopped @@ -5600,8 +5603,9 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, true /*fromCache*/).types(); float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, - getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice), - musicDevice); + mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC, + musicDevice), + musicDevice); float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; if (volumeDB > minVolDB) { @@ -5636,12 +5640,10 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, if (srcStream == dstStream) { return srcIndex; } - auto &srcCurves = getVolumeCurves(srcStream); - auto &dstCurves = getVolumeCurves(dstStream); - float minSrc = (float)srcCurves.getVolumeIndexMin(); - float maxSrc = (float)srcCurves.getVolumeIndexMax(); - float minDst = (float)dstCurves.getVolumeIndexMin(); - float maxDst = (float)dstCurves.getVolumeIndexMax(); + float minSrc = (float)mVolumeCurves->getVolumeIndexMin(srcStream); + float maxSrc = (float)mVolumeCurves->getVolumeIndexMax(srcStream); + float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream); + float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream); // preserve mute request or correct range if (srcIndex < minSrc) { @@ -5656,11 +5658,11 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, } status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, - int index, - const sp& outputDesc, - audio_devices_t device, - int delayMs, - bool force) + int index, + const sp& outputDesc, + audio_devices_t device, + int delayMs, + bool force) { // do not change actual stream volume if the stream is muted if (outputDesc->isMuted(streamToVolumeSource(stream))) { @@ -5696,7 +5698,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax(); + voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); } else { voiceVolume = 1.0; } @@ -5719,7 +5721,7 @@ void AudioPolicyManager::applyStreamVolumes(const sp& out for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { checkAndSetVolume((audio_stream_type_t)stream, - getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device), + mVolumeCurves->getVolumeIndex((audio_stream_type_t)stream, device), outputDesc, device, delayMs, @@ -5752,10 +5754,10 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", stream, on, outputDesc->getMuteCount(stream), device); - auto &curves = getVolumeCurves(stream); + if (on) { if (!outputDesc->isMuted(streamToVolumeSource(stream))) { - if (curves.canBeMuted() && + if (mVolumeCurves->canBeMuted(stream) && ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { checkAndSetVolume(stream, 0, outputDesc, device, delayMs); @@ -5770,7 +5772,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { checkAndSetVolume(stream, - curves.getVolumeIndex(device), + mVolumeCurves->getVolumeIndex(stream, device), outputDesc, device, delayMs); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 06a1f3e416..70ad6ac7aa 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -49,7 +49,7 @@ #include #include #include -#include "TypeConverter.h" +#include namespace android { @@ -310,24 +310,12 @@ protected: { return mAvailableInputDevices; } + virtual IVolumeCurvesCollection &getVolumeCurves() { return *mVolumeCurves; } virtual const sp &getDefaultOutputDevice() const { return mDefaultOutputDevice; } - IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr) - { - auto *curves = mEngine->getVolumeCurvesForAttributes(attr); - ALOG_ASSERT(curves != nullptr, "No curves for attributes %s", toString(attr).c_str()); - return *curves; - } - IVolumeCurves &getVolumeCurves(audio_stream_type_t stream) - { - auto *curves = mEngine->getVolumeCurvesForStreamType(stream); - ALOG_ASSERT(curves != nullptr, "No curves for stream %s", toString(stream).c_str()); - return *curves; - } - void addOutput(audio_io_handle_t output, const sp& outputDesc); void removeOutput(audio_io_handle_t output); void addInput(audio_io_handle_t input, const sp& inputDesc); @@ -636,12 +624,12 @@ protected: float mLastVoiceVolume; // last voice volume value sent to audio HAL bool mA2dpSuspended; // true if A2DP output is suspended + std::unique_ptr mVolumeCurves; // Volume Curves per use case and device category EffectDescriptorCollection mEffects; // list of registered audio effects sp mDefaultOutputDevice; // output device selected by default at boot time HwModuleCollection mHwModules; // contains only modules that have been loaded successfully HwModuleCollection mHwModulesAll; // normally not needed, used during construction and for // dumps - AudioPolicyConfig mConfig; std::atomic mAudioPortGeneration; -- GitLab From f5aa58d324bc1d53b7c0ec31edf81814e8553044 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 22 Feb 2019 18:20:11 -0800 Subject: [PATCH 1014/1530] Revert "Revert "audiopolicy: moves Stream Volume Curves to Engine"" This reverts commit efdbe3bfeff7c150d99900fddca005d48048f188. Test: make --- .../common/managerdefinitions/Android.bp | 1 - .../include/AudioPolicyConfig.h | 13 +- .../include/IVolumeCurves.h | 44 ++++ .../include/IVolumeCurvesCollection.h | 54 ---- .../managerdefinitions/include/VolumeCurve.h | 239 ------------------ .../managerdefinitions/src/Serializer.cpp | 88 ------- .../config/audio_policy_configuration.xml | 6 +- .../audio_policy_configuration_generic.xml | 6 +- .../engine/common/include/EngineBase.h | 18 +- .../common/include/StreamVolumeCurves.h | 95 +++++++ .../engine/common/include/VolumeCurve.h | 192 ++++++++++++++ .../engine/common/src/EngineBase.cpp | 49 ++++ .../engine/common/src/EngineDefaultConfig.h | 123 ++++++++- .../engine/common/src/StreamVolumeCurves.cpp | 47 ++++ .../common}/src/VolumeCurve.cpp | 51 ++-- .../engine/config/include/EngineConfig.h | 22 ++ .../engine/config/src/EngineConfig.cpp | 126 +++++++++ .../interface/AudioPolicyManagerInterface.h | 16 ++ .../interface/AudioPolicyManagerObserver.h | 3 - .../audiopolicy/engineconfigurable/Android.mk | 2 + .../config/example/Android.mk | 18 ++ .../audio_policy_engine_configuration.xml | 2 + ...o_policy_engine_default_stream_volumes.xml | 136 ++++++++++ .../audio_policy_engine_stream_volumes.xml | 231 +++++++++++++++++ .../engineconfigurable/src/Engine.cpp | 2 +- services/audiopolicy/enginedefault/Android.mk | 2 + .../enginedefault/config/example/Android.mk | 20 +- .../audio_policy_engine_configuration.xml | 2 + ...o_policy_engine_default_stream_volumes.xml | 136 ++++++++++ .../audio_policy_engine_stream_volumes.xml | 231 +++++++++++++++++ .../audiopolicy/enginedefault/src/Engine.cpp | 29 --- .../audiopolicy/enginedefault/src/Engine.h | 2 - .../managerdefault/AudioPolicyManager.cpp | 80 +++--- .../managerdefault/AudioPolicyManager.h | 18 +- 34 files changed, 1590 insertions(+), 514 deletions(-) create mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h delete mode 100644 services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h create mode 100644 services/audiopolicy/engine/common/include/StreamVolumeCurves.h create mode 100644 services/audiopolicy/engine/common/include/VolumeCurve.h create mode 100644 services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp rename services/audiopolicy/{common/managerdefinitions => engine/common}/src/VolumeCurve.cpp (68%) create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml create mode 100644 services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp index e5ebab7e16..c9037a1daa 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.bp +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -19,7 +19,6 @@ cc_library_static { "src/Serializer.cpp", "src/SoundTriggerSession.cpp", "src/TypeConverter.cpp", - "src/VolumeCurve.cpp", ], shared_libs: [ "libcutils", diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h index d52eb3d35c..2264d8f718 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -40,13 +39,11 @@ public: AudioPolicyConfig(HwModuleCollection &hwModules, DeviceVector &availableOutputDevices, DeviceVector &availableInputDevices, - sp &defaultOutputDevice, - VolumeCurvesCollection *volumes = nullptr) + sp &defaultOutputDevice) : mHwModules(hwModules), mAvailableOutputDevices(availableOutputDevices), mAvailableInputDevices(availableInputDevices), mDefaultOutputDevice(defaultOutputDevice), - mVolumeCurves(volumes), mIsSpeakerDrcEnabled(false) {} @@ -58,13 +55,6 @@ public: mSource = file; } - void setVolumes(const VolumeCurvesCollection &volumes) - { - if (mVolumeCurves != nullptr) { - *mVolumeCurves = volumes; - } - } - void setHwModules(const HwModuleCollection &hwModules) { mHwModules = hwModules; @@ -182,7 +172,6 @@ private: DeviceVector &mAvailableOutputDevices; DeviceVector &mAvailableInputDevices; sp &mDefaultOutputDevice; - VolumeCurvesCollection *mVolumeCurves; // TODO: remove when legacy conf file is removed. true on devices that use DRC on the // DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly. // Note: remove also speaker_drc_enabled from global configuration of XML config file. diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h new file mode 100644 index 0000000000..93022fb2a0 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { + +class IVolumeCurves +{ +public: + virtual ~IVolumeCurves() = default; + + virtual void clearCurrentVolumeIndex() = 0; + virtual void addCurrentVolumeIndex(audio_devices_t device, int index) = 0; + virtual bool canBeMuted() const = 0; + virtual int getVolumeIndexMin() const = 0; + virtual int getVolumeIndex(audio_devices_t device) const = 0; + virtual int getVolumeIndexMax() const = 0; + virtual float volIndexToDb(device_category device, int indexInUi) const = 0; + virtual bool hasVolumeIndexForDevice(audio_devices_t device) const = 0; + virtual status_t initVolume(int indexMin, int indexMax) = 0; + virtual void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const = 0; +}; + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h deleted file mode 100644 index 750da55519..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurvesCollection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace android { - -class IVolumeCurvesCollection -{ -public: - virtual ~IVolumeCurvesCollection() = default; - - virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) = 0; - virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, - int index) = 0; - virtual bool canBeMuted(audio_stream_type_t stream) = 0; - virtual int getVolumeIndexMin(audio_stream_type_t stream) const = 0; - virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) = 0; - virtual int getVolumeIndexMax(audio_stream_type_t stream) const = 0; - virtual float volIndexToDb(audio_stream_type_t stream, device_category device, - int indexInUi) const = 0; - virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) = 0; - - virtual void initializeVolumeCurves(bool /*isSpeakerDrcEnabled*/) {} - virtual void switchVolumeCurve(audio_stream_type_t src, audio_stream_type_t dst) = 0; - virtual void restoreOriginVolumeCurve(audio_stream_type_t stream) - { - switchVolumeCurve(stream, stream); - } - virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, - audio_devices_t device) const = 0; - - virtual void dump(String8 *dst) const = 0; -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h b/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h deleted file mode 100644 index 76ec198d02..0000000000 --- a/services/audiopolicy/common/managerdefinitions/include/VolumeCurve.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "IVolumeCurvesCollection.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct CurvePoint -{ - CurvePoint() {} - CurvePoint(int index, int attenuationInMb) : - mIndex(index), mAttenuationInMb(attenuationInMb) {} - uint32_t mIndex; - int mAttenuationInMb; -}; - -inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) -{ - return lhs.mIndex < rhs.mIndex; -} - -// A volume curve for a given use case and device category -// It contains of list of points of this curve expressing the attenuation in Millibels for -// a given volume index from 0 to 100 -class VolumeCurve : public RefBase -{ -public: - VolumeCurve(device_category device, audio_stream_type_t stream) : - mDeviceCategory(device), mStreamType(stream) {} - - device_category getDeviceCategory() const { return mDeviceCategory; } - audio_stream_type_t getStreamType() const { return mStreamType; } - - void add(const CurvePoint &point) { mCurvePoints.add(point); } - - float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; - - void dump(String8 *result) const; - -private: - SortedVector mCurvePoints; - device_category mDeviceCategory; - audio_stream_type_t mStreamType; -}; - -// Volume Curves for a given use case indexed by device category -class VolumeCurvesForStream : public KeyedVector > -{ -public: - VolumeCurvesForStream() : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) - { - mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); - } - - sp getCurvesFor(device_category device) const - { - if (indexOfKey(device) < 0) { - return 0; - } - return valueFor(device); - } - - int getVolumeIndex(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME - if (mIndexCur.indexOfKey(device) < 0) { - device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; - } - return mIndexCur.valueFor(device); - } - - bool canBeMuted() const { return mCanBeMuted; } - void clearCurrentVolumeIndex() { mIndexCur.clear(); } - void addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); } - - void setVolumeIndexMin(int volIndexMin) { mIndexMin = volIndexMin; } - int getVolumeIndexMin() const { return mIndexMin; } - - void setVolumeIndexMax(int volIndexMax) { mIndexMax = volIndexMax; } - int getVolumeIndexMax() const { return mIndexMax; } - - bool hasVolumeIndexForDevice(audio_devices_t device) const - { - device = Volume::getDeviceForVolume(device); - return mIndexCur.indexOfKey(device) >= 0; - } - - const sp getOriginVolumeCurve(device_category deviceCategory) const - { - ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); - return mOriginVolumeCurves.valueFor(deviceCategory); - } - void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) - { - ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); - replaceValueFor(deviceCategory, volumeCurve); - } - - ssize_t add(const sp &volumeCurve) - { - device_category deviceCategory = volumeCurve->getDeviceCategory(); - ssize_t index = indexOfKey(deviceCategory); - if (index < 0) { - // Keep track of original Volume Curves per device category in order to switch curves. - mOriginVolumeCurves.add(deviceCategory, volumeCurve); - return KeyedVector::add(deviceCategory, volumeCurve); - } - return index; - } - - float volIndexToDb(device_category deviceCat, int indexInUi) const - { - sp vc = getCurvesFor(deviceCat); - if (vc != 0) { - return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); - } else { - ALOGE("Invalid device category %d for Volume Curve", deviceCat); - return 0.0f; - } - } - - void dump(String8 *dst, int spaces, bool curvePoints = false) const; - -private: - KeyedVector > mOriginVolumeCurves; - KeyedVector mIndexCur; /**< current volume index per device. */ - int mIndexMin; /**< min volume index. */ - int mIndexMax; /**< max volume index. */ - bool mCanBeMuted; /**< true is the stream can be muted. */ -}; - -// Collection of Volume Curves indexed by use case -class VolumeCurvesCollection : public KeyedVector, - public IVolumeCurvesCollection -{ -public: - VolumeCurvesCollection() - { - // Create an empty collection of curves - for (ssize_t i = 0 ; i < AUDIO_STREAM_CNT; i++) { - audio_stream_type_t stream = static_cast(i); - KeyedVector::add(stream, VolumeCurvesForStream()); - } - } - - // Once XML has been parsed, must be call first to sanity check table and initialize indexes - virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) - { - editValueAt(stream).setVolumeIndexMin(indexMin); - editValueAt(stream).setVolumeIndexMax(indexMax); - return NO_ERROR; - } - virtual void clearCurrentVolumeIndex(audio_stream_type_t stream) - { - editCurvesFor(stream).clearCurrentVolumeIndex(); - } - virtual void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index) - { - editCurvesFor(stream).addCurrentVolumeIndex(device, index); - } - virtual bool canBeMuted(audio_stream_type_t stream) { return getCurvesFor(stream).canBeMuted(); } - - virtual int getVolumeIndexMin(audio_stream_type_t stream) const - { - return getCurvesFor(stream).getVolumeIndexMin(); - } - virtual int getVolumeIndexMax(audio_stream_type_t stream) const - { - return getCurvesFor(stream).getVolumeIndexMax(); - } - virtual int getVolumeIndex(audio_stream_type_t stream, audio_devices_t device) - { - return getCurvesFor(stream).getVolumeIndex(device); - } - virtual void switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) - { - const VolumeCurvesForStream &sourceCurves = getCurvesFor(streamSrc); - VolumeCurvesForStream &dstCurves = editCurvesFor(streamDst); - ALOG_ASSERT(sourceCurves.size() == dstCurves.size(), "device category not aligned"); - for (size_t index = 0; index < sourceCurves.size(); index++) { - device_category cat = sourceCurves.keyAt(index); - dstCurves.setVolumeCurve(cat, sourceCurves.getOriginVolumeCurve(cat)); - } - } - virtual float volIndexToDb(audio_stream_type_t stream, device_category cat, int indexInUi) const - { - return getCurvesFor(stream).volIndexToDb(cat, indexInUi); - } - virtual bool hasVolumeIndexForDevice(audio_stream_type_t stream, - audio_devices_t device) const - { - return getCurvesFor(stream).hasVolumeIndexForDevice(device); - } - - void dump(String8 *dst) const override; - - ssize_t add(const sp &volumeCurve) - { - audio_stream_type_t streamType = volumeCurve->getStreamType(); - return editCurvesFor(streamType).add(volumeCurve); - } - VolumeCurvesForStream &editCurvesFor(audio_stream_type_t stream) - { - ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); - return editValueAt(stream); - } - const VolumeCurvesForStream &getCurvesFor(audio_stream_type_t stream) const - { - ALOG_ASSERT(indexOfKey(stream) >= 0, "Invalid stream type for Volume Curve"); - return valueFor(stream); - } -}; - -} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 98d375c115..81d3968c76 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -201,25 +201,6 @@ struct GlobalConfigTraits static status_t deserialize(const xmlNode *root, AudioPolicyConfig *config); }; -struct VolumeTraits : public AndroidCollectionTraits -{ - static constexpr const char *tag = "volume"; - static constexpr const char *collectionTag = "volumes"; - static constexpr const char *volumePointTag = "point"; - static constexpr const char *referenceTag = "reference"; - - struct Attributes - { - static constexpr const char *stream = "stream"; - static constexpr const char *deviceCategory = "deviceCategory"; - static constexpr const char *reference = "ref"; - static constexpr const char *referenceName = "name"; - }; - - static Return deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); - // No Children -}; - struct SurroundSoundTraits { static constexpr const char *tag = "surroundSound"; @@ -703,67 +684,6 @@ status_t GlobalConfigTraits::deserialize(const xmlNode *root, AudioPolicyConfig return NO_ERROR; } -Return VolumeTraits::deserialize(const xmlNode *cur, - PtrSerializingCtx /*serializingContext*/) -{ - std::string streamTypeLiteral = getXmlAttribute(cur, Attributes::stream); - if (streamTypeLiteral.empty()) { - ALOGE("%s: No %s found", __func__, Attributes::stream); - return Status::fromStatusT(BAD_VALUE); - } - audio_stream_type_t streamType; - if (!StreamTypeConverter::fromString(streamTypeLiteral, streamType)) { - ALOGE("%s: Invalid %s", __func__, Attributes::stream); - return Status::fromStatusT(BAD_VALUE); - } - std::string deviceCategoryLiteral = getXmlAttribute(cur, Attributes::deviceCategory); - if (deviceCategoryLiteral.empty()) { - ALOGE("%s: No %s found", __func__, Attributes::deviceCategory); - return Status::fromStatusT(BAD_VALUE); - } - device_category deviceCategory; - if (!DeviceCategoryConverter::fromString(deviceCategoryLiteral, deviceCategory)) { - ALOGE("%s: Invalid %s=%s", __func__, Attributes::deviceCategory, - deviceCategoryLiteral.c_str()); - return Status::fromStatusT(BAD_VALUE); - } - - std::string referenceName = getXmlAttribute(cur, Attributes::reference); - const xmlNode *ref = NULL; - if (!referenceName.empty()) { - ref = getReference(cur->parent, referenceName); - if (ref == NULL) { - ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); - return Status::fromStatusT(BAD_VALUE); - } - } - - Element volCurve = new VolumeCurve(deviceCategory, streamType); - - for (const xmlNode *child = referenceName.empty() ? cur->xmlChildrenNode : ref->xmlChildrenNode; - child != NULL; child = child->next) { - if (!xmlStrcmp(child->name, reinterpret_cast(volumePointTag))) { - auto pointDefinition = make_xmlUnique(xmlNodeListGetString( - child->doc, child->xmlChildrenNode, 1)); - if (pointDefinition == nullptr) { - return Status::fromStatusT(BAD_VALUE); - } - ALOGV("%s: %s=%s", - __func__, tag, reinterpret_cast(pointDefinition.get())); - std::vector point; - collectionFromString>( - reinterpret_cast(pointDefinition.get()), point, ","); - if (point.size() != 2) { - ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, - reinterpret_cast(pointDefinition.get())); - return Status::fromStatusT(BAD_VALUE); - } - volCurve->add(CurvePoint(point[0], point[1])); - } - } - return volCurve; -} - status_t SurroundSoundTraits::deserialize(const xmlNode *root, AudioPolicyConfig *config) { config->setDefaultSurroundFormats(); @@ -851,14 +771,6 @@ status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig } config->setHwModules(modules); - // deserialize volume section - VolumeTraits::Collection volumes; - status = deserializeCollection(root, &volumes, config); - if (status != NO_ERROR) { - return status; - } - config->setVolumes(volumes); - // Global Configuration GlobalConfigTraits::deserialize(root, config); diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index 42c52deec9..b4cc1d3936 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -191,7 +191,11 @@ - + diff --git a/services/audiopolicy/config/audio_policy_configuration_generic.xml b/services/audiopolicy/config/audio_policy_configuration_generic.xml index 40dcc22b58..9ad609df2b 100644 --- a/services/audiopolicy/config/audio_policy_configuration_generic.xml +++ b/services/audiopolicy/config/audio_policy_configuration_generic.xml @@ -30,7 +30,11 @@ - + diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index 5c33fb39b8..bc027e2a3f 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace android { namespace audio_policy { @@ -67,6 +68,10 @@ public: status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override; + VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override; + + VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override; + void dump(String8 *dst) const override; @@ -87,7 +92,16 @@ public: return is_state_in_call(getPhoneState()); } -private: + VolumeSource toVolumeSource(audio_stream_type_t stream) const + { + return static_cast(stream); + } + + status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); + + status_t restoreOriginVolumeCurve(audio_stream_type_t stream); + + private: AudioPolicyManagerObserver *mApmObserver = nullptr; ProductStrategyMap mProductStrategies; @@ -95,6 +109,8 @@ private: /** current forced use configuration. */ audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {}; + + StreamVolumeCurves mStreamVolumeCurves; }; } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h new file mode 100644 index 0000000000..5b0b7d6272 --- /dev/null +++ b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +namespace android { + +class StreamVolumeCurves +{ +public: + StreamVolumeCurves() = default; + + /** + * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves + * from one stream type to another. + * @param src source stream type + * @param dst destination stream type + */ + status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) + { + if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) { + ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst); + return NO_INIT; + } + const VolumeCurves &sourceCurves = getCurvesFor(streamSrc); + VolumeCurves &dstCurves = editCurvesFor(streamDst); + return dstCurves.switchCurvesFrom(sourceCurves); + } + void dump(String8 *dst, int spaces = 0) const; + + void add(const VolumeCurves &curves, audio_stream_type_t streamType) + { + mCurves.emplace(streamType, curves); + } + + bool hasCurvesFor(audio_stream_type_t stream) + { + return mCurves.find(stream) != end(mCurves); + } + + VolumeCurves &editCurvesFor(audio_stream_type_t stream) + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves[stream]; + } + const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves.at(stream); + } + /** + * @brief getVolumeCurvesForStream + * @param stream type for which the volume curves interface is requested + * @return the VolumeCurves for a given stream type. + */ + VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream) + { + ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); + return mCurves[stream]; + } + /** + * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume + * curves for a given stream type + * @param stream for which the volume curves will be restored. + */ + status_t restoreOriginVolumeCurve(audio_stream_type_t stream) + { + if (!hasCurvesFor(stream)) { + ALOGE("%s: No curves defined for streams", __FUNCTION__); + return NO_INIT; + } + return switchVolumeCurve(stream, stream); + } + +private: + std::map mCurves; +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h new file mode 100644 index 0000000000..c2a1da702e --- /dev/null +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "IVolumeCurves.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +struct CurvePoint +{ + CurvePoint() {} + CurvePoint(int index, int attenuationInMb) : + mIndex(index), mAttenuationInMb(attenuationInMb) {} + uint32_t mIndex; + int mAttenuationInMb; +}; + +inline bool operator< (const CurvePoint &lhs, const CurvePoint &rhs) +{ + return lhs.mIndex < rhs.mIndex; +} + +// A volume curve for a given use case and device category +// It contains of list of points of this curve expressing the attenuation in Millibels for +// a given volume index from 0 to 100 +class VolumeCurve : public RefBase +{ +public: + VolumeCurve(device_category device) : mDeviceCategory(device) {} + + void add(const CurvePoint &point) { mCurvePoints.add(point); } + + float volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const; + + void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const; + + device_category getDeviceCategory() const { return mDeviceCategory; } + +private: + const device_category mDeviceCategory; + SortedVector mCurvePoints; +}; + +// Volume Curves for a given use case indexed by device category +class VolumeCurves : public KeyedVector >, + public IVolumeCurves +{ +public: + VolumeCurves(int indexMin = 0, int indexMax = 100) : + mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT) + { + addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); + } + VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) : + mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream) + { + addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); + } + + // Once XML has been parsed, must be call first to sanity check table and initialize indexes + virtual status_t initVolume(int indexMin, int indexMax) + { + mIndexMin = indexMin; + mIndexMax = indexMax; + return NO_ERROR; + } + + sp getCurvesFor(device_category device) const + { + if (indexOfKey(device) < 0) { + return 0; + } + return valueFor(device); + } + + virtual int getVolumeIndex(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME + if (mIndexCur.find(device) == end(mIndexCur)) { + device = AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME; + } + return mIndexCur.at(device); + } + + virtual bool canBeMuted() const { return mCanBeMuted; } + virtual void clearCurrentVolumeIndex() { mIndexCur.clear(); } + void addCurrentVolumeIndex(audio_devices_t device, int index) override + { + mIndexCur[device] = index; + } + + int getVolumeIndexMin() const { return mIndexMin; } + + int getVolumeIndexMax() const { return mIndexMax; } + + bool hasVolumeIndexForDevice(audio_devices_t device) const + { + device = Volume::getDeviceForVolume(device); + return mIndexCur.find(device) != end(mIndexCur); + } + + status_t switchCurvesFrom(const VolumeCurves &referenceCurves) + { + if (size() != referenceCurves.size()) { + ALOGE("%s! device category not aligned, cannot switch", __FUNCTION__); + return BAD_TYPE; + } + for (size_t index = 0; index < size(); index++) { + device_category cat = keyAt(index); + setVolumeCurve(cat, referenceCurves.getOriginVolumeCurve(cat)); + } + return NO_ERROR; + } + status_t restoreOriginVolumeCurve() + { + return switchCurvesFrom(*this); + } + + const sp getOriginVolumeCurve(device_category deviceCategory) const + { + ALOG_ASSERT(mOriginVolumeCurves.indexOfKey(deviceCategory) >= 0, "Invalid device category"); + return mOriginVolumeCurves.valueFor(deviceCategory); + } + void setVolumeCurve(device_category deviceCategory, const sp &volumeCurve) + { + ALOG_ASSERT(indexOfKey(deviceCategory) >= 0, "Invalid device category for Volume Curve"); + replaceValueFor(deviceCategory, volumeCurve); + } + + ssize_t add(const sp &volumeCurve) + { + device_category deviceCategory = volumeCurve->getDeviceCategory(); + ssize_t index = indexOfKey(deviceCategory); + if (index < 0) { + // Keep track of original Volume Curves per device category in order to switch curves. + mOriginVolumeCurves.add(deviceCategory, volumeCurve); + return KeyedVector::add(deviceCategory, volumeCurve); + } + return index; + } + + virtual float volIndexToDb(device_category deviceCat, int indexInUi) const + { + sp vc = getCurvesFor(deviceCat); + if (vc != 0) { + return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax); + } else { + ALOGE("Invalid device category %d for Volume Curve", deviceCat); + return 0.0f; + } + } + + audio_stream_type_t getStreamType() const { return mStream; } + + void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override; + +private: + KeyedVector > mOriginVolumeCurves; + std::map mIndexCur; /**< current volume index per device. */ + /*const*/ int mIndexMin; /**< min volume index. */ + /*const*/ int mIndexMax; /**< max volume index. */ + const bool mCanBeMuted = true; /**< true is the stream can be muted. */ + + const audio_stream_type_t mStream; /**< Keep it for legacy. */ +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 755f2a88e3..2449b61d12 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -55,8 +55,10 @@ status_t EngineBase::setPhoneState(audio_mode_t state) if (!is_state_in_call(oldState) && is_state_in_call(state)) { ALOGV(" Entering call in setPhoneState()"); + switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF); } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { ALOGV(" Exiting call in setPhoneState()"); + restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); } return NO_ERROR; } @@ -108,6 +110,31 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() productStrategies[strategyId] = strategy; } }; + auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) { + for (auto &configVolume : configVolumes) { + audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; + if (configVolume.stream.empty() || + !StreamTypeConverter::fromString(configVolume.stream, streamType)) { + ALOGE("%s: Invalid stream type", __FUNCTION__); + continue; + } + VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax); + for (auto &configCurve : configVolume.volumeCurves) { + device_category deviceCategory = DEVICE_CATEGORY_SPEAKER; + if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, + deviceCategory)) { + ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str()); + continue; + } + sp curve = new VolumeCurve(deviceCategory); + for (auto &point : configCurve.curvePoints) { + curve->add({point.index, point.attenuationInMb}); + } + volumeCurves.add(curve); + } + streamVolumeCollection.add(volumeCurves, streamType); + } + }; auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { @@ -116,6 +143,7 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); + loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves); return result; } @@ -173,9 +201,30 @@ status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &stra return NO_ERROR; } +VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) +{ + return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr)); +} + +VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) +{ + return &mStreamVolumeCurves.getVolumeCurvesForStream(stream); +} + +status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) +{ + return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);; +} + +status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream) +{ + return mStreamVolumeCurves.restoreOriginVolumeCurve(stream); +} + void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); + mStreamVolumeCurves.dump(dst, 2); } } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 3940c0c929..86a59b00ee 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,10 +131,131 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; +const engineConfig::VolumeGroups gVolumeGroups = { + {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, + }, + }, + {"system", "AUDIO_STREAM_SYSTEM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"ring", "AUDIO_STREAM_RING", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"music", "AUDIO_STREAM_MUSIC", 0, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"alarm", "AUDIO_STREAM_ALARM", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE + }, + }, + {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, + {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, + { + {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, + {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE + }, + }, + {"tts", "AUDIO_STREAM_TTS", 0, 16, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE + }, + }, + {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE + }, + }, + {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, + {"patch", "AUDIO_STREAM_PATCH", 0, 1, + { + {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE + }, + }, +}; + const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, - {} + {}, + gVolumeGroups }; } // namespace android diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp new file mode 100644 index 0000000000..fe3b0007b6 --- /dev/null +++ b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp @@ -0,0 +1,47 @@ +/* + * 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 "APM::Engine::StreamVolumeCurves" +//#define LOG_NDEBUG 0 + +#include "StreamVolumeCurves.h" +#include + +namespace android { + +void StreamVolumeCurves::dump(String8 *dst, int spaces) const +{ + if (mCurves.empty()) { + return; + } + dst->appendFormat("\n%*sStreams dump:\n", spaces, ""); + dst->appendFormat( + "%*sStream Can be muted Index Min Index Max Index Cur [device : index]...\n", spaces + 2, ""); + for (const auto &streamCurve : mCurves) { + streamCurve.second.dump(dst, spaces + 2, false); + } + dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, ""); + for (const auto &streamCurve : mCurves) { + std::string streamTypeLiteral; + StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral); + dst->appendFormat( + " %s (%02d): Curve points for device category (index, attenuation in millibel)\n", + streamTypeLiteral.c_str(), streamCurve.first); + streamCurve.second.dump(dst, spaces + 2, true); + } +} + +} // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp similarity index 68% rename from services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp rename to services/audiopolicy/engine/common/src/VolumeCurve.cpp index 2625733f20..be2ca730ef 100644 --- a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp +++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp @@ -64,8 +64,7 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) ((float)(mCurvePoints[indexInUiPosition].mIndex - mCurvePoints[indexInUiPosition - 1].mIndex)) ); - ALOGV("VOLUME mDeviceCategory %d, mStreamType %d vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", - mDeviceCategory, mStreamType, + ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", mCurvePoints[indexInUiPosition - 1].mIndex, volIdx, mCurvePoints[indexInUiPosition].mIndex, ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels, @@ -74,55 +73,35 @@ float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) return decibels; } -void VolumeCurve::dump(String8 *dst) const +void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const { + if (!curvePoints) { + return; + } dst->append(" {"); for (size_t i = 0; i < mCurvePoints.size(); i++) { - dst->appendFormat("(%3d, %5d)", + dst->appendFormat("%*s (%3d, %5d)", spaces, "", mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb); - dst->append(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); + dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); } } -void VolumeCurvesForStream::dump(String8 *dst, int spaces = 0, bool curvePoints) const +void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const { if (!curvePoints) { - dst->appendFormat("%s %02d %02d ", - mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); - for (size_t i = 0; i < mIndexCur.size(); i++) { - dst->appendFormat("%04x : %02d, ", mIndexCur.keyAt(i), mIndexCur.valueAt(i)); + dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "", + mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + for (const auto &pair : mIndexCur) { + dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second); } - dst->append("\n"); + dst->appendFormat("\n"); return; } - for (size_t i = 0; i < size(); i++) { std::string deviceCatLiteral; DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral); - dst->appendFormat("%*s %s :", - spaces, "", deviceCatLiteral.c_str()); - valueAt(i)->dump(dst); - } - dst->append("\n"); -} - -void VolumeCurvesCollection::dump(String8 *dst) const -{ - dst->append("\nStreams dump:\n"); - dst->append( - " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); - for (size_t i = 0; i < size(); i++) { - dst->appendFormat(" %02zu ", i); - valueAt(i).dump(dst); - } - dst->append("\nVolume Curves for Use Cases (aka Stream types) dump:\n"); - for (size_t i = 0; i < size(); i++) { - std::string streamTypeLiteral; - StreamTypeConverter::toString(keyAt(i), streamTypeLiteral); - dst->appendFormat( - " %s (%02zu): Curve points for device category (index, attenuation in millibel)\n", - streamTypeLiteral.c_str(), i); - valueAt(i).dump(dst, 2, true); + dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str()); + valueAt(i)->dump(dst, 2, true); } } diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index e18f68759d..f7caad238d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -45,6 +45,27 @@ struct AttributesGroup { using AttributesGroups = std::vector; +struct CurvePoint { + int index; + int attenuationInMb; +}; +using CurvePoints = std::vector; + +struct VolumeCurve { + std::string deviceCategory; + CurvePoints curvePoints; +}; +using VolumeCurves = std::vector; + +struct VolumeGroup { + std::string name; + std::string stream; + int indexMin; + int indexMax; + VolumeCurves volumeCurves; +}; +using VolumeGroups = std::vector; + struct ProductStrategy { std::string name; AttributesGroups attributesGroups; @@ -78,6 +99,7 @@ struct Config { ProductStrategies productStrategies; Criteria criteria; CriterionTypes criterionTypes; + VolumeGroups volumeGroups; }; /** Result of `parse(const char*)` */ diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 3aa38cf965..227ebd8cde 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -107,6 +107,35 @@ struct CriterionTraits : public BaseSerializerTraits { static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &collection); }; +struct VolumeTraits : public BaseSerializerTraits { + static constexpr const char *tag = "volume"; + static constexpr const char *collectionTag = "volumes"; + static constexpr const char *volumePointTag = "point"; + + struct Attributes { + static constexpr const char *deviceCategory = "deviceCategory"; + static constexpr const char *reference = "ref"; /**< For volume curves factorization. */ + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; +struct VolumeGroupTraits : public BaseSerializerTraits { + static constexpr const char *tag = "volumeGroup"; + static constexpr const char *collectionTag = "volumeGroups"; + + struct Attributes { + static constexpr const char *name = "name"; + static constexpr const char *stream = "stream"; // For legacy volume curves + static constexpr const char *indexMin = "indexMin"; + static constexpr const char *indexMax = "indexMax"; + }; + + static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, + Collection &collection); +}; + +using xmlCharUnique = std::unique_ptr; using xmlCharUnique = std::unique_ptr; @@ -383,6 +412,100 @@ status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, return NO_ERROR; } +status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) +{ + std::string deviceCategory = getXmlAttribute(root, Attributes::deviceCategory); + if (deviceCategory.empty()) { + ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory); + } + + std::string referenceName = getXmlAttribute(root, Attributes::reference); + const _xmlNode *ref = NULL; + if (!referenceName.empty()) { + getReference(xmlDocGetRootElement(doc), ref, referenceName, collectionTag); + if (ref == NULL) { + ALOGE("%s: No reference Ptr found for %s", __FUNCTION__, referenceName.c_str()); + return BAD_VALUE; + } + } + // Retrieve curve point from reference element if found or directly from current curve + CurvePoints curvePoints; + for (const xmlNode *child = referenceName.empty() ? + root->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)volumePointTag)) { + xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (pointXml == NULL) { + return BAD_VALUE; + } + ALOGV("%s: %s=%s", __func__, tag, reinterpret_cast(pointXml.get())); + std::vector point; + collectionFromString>( + reinterpret_cast(pointXml.get()), point, ","); + if (point.size() != 2) { + ALOGE("%s: Invalid %s: %s", __func__, volumePointTag, + reinterpret_cast(pointXml.get())); + return BAD_VALUE; + } + curvePoints.push_back({point[0], point[1]}); + } + } + volumes.push_back({ deviceCategory, curvePoints }); + return NO_ERROR; +} + +status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) +{ + std::string name; + std::string stream = {}; + int indexMin = 0; + int indexMax = 0; + + for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) { + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) { + xmlCharUnique nameXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (nameXml == nullptr) { + return BAD_VALUE; + } + name = reinterpret_cast(nameXml.get()); + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) { + xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (streamXml == nullptr) { + return BAD_VALUE; + } + stream = reinterpret_cast(streamXml.get()); + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) { + xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (indexMinXml == nullptr) { + return BAD_VALUE; + } + std::string indexMinLiteral(reinterpret_cast(indexMinXml.get())); + if (!convertTo(indexMinLiteral, indexMin)) { + return BAD_VALUE; + } + } + if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMax)) { + xmlCharUnique indexMaxXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (indexMaxXml == nullptr) { + return BAD_VALUE; + } + std::string indexMaxLiteral(reinterpret_cast(indexMaxXml.get())); + if (!convertTo(indexMaxLiteral, indexMax)) { + return BAD_VALUE; + } + } + } + ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d", + __func__, name.c_str(), stream.c_str(), indexMin, indexMax); + + VolumeCurves groupVolumeCurves; + size_t skipped = 0; + deserializeCollection(doc, root, groupVolumeCurves, skipped); + volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves }); + return NO_ERROR; +} + ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -414,6 +537,9 @@ ParsingResult parse(const char* path) { doc, cur, config->criteria, nbSkippedElements); deserializeCollection( doc, cur, config->criterionTypes, nbSkippedElements); + deserializeCollection( + doc, cur, config->volumeGroups, nbSkippedElements); + return {std::move(config), nbSkippedElements}; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 498cc3bf82..c9e9507705 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -234,6 +235,21 @@ public: */ virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const = 0; + /** + * @brief getVolumeCurvesForAttributes retrieves the Volume Curves interface for the + * requested Audio Attributes. + * @param attr to be considered + * @return IVolumeCurves interface pointer if found, nullptr otherwise + */ + virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0; + + /** + * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream + * @param stream to be considered + * @return IVolumeCurves interface pointer if found, nullptr otherwise + */ + virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0; + virtual void dump(String8 *dst) const = 0; protected: diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h index b7902cf2e5..43ba625f9c 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h @@ -16,7 +16,6 @@ #pragma once -#include #include #include #include @@ -51,8 +50,6 @@ public: virtual const DeviceVector &getAvailableInputDevices() const = 0; - virtual IVolumeCurvesCollection &getVolumeCurves() = 0; - virtual const sp &getDefaultOutputDevice() const = 0; protected: diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index bbd9688529..2b7e4c8dbc 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -12,6 +12,8 @@ LOCAL_SRC_FILES := \ src/EngineInstance.cpp \ src/Stream.cpp \ src/InputSource.cpp \ + ../engine/common/src/VolumeCurve.cpp \ + ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index 95a2ecc08d..ef476f765c 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -20,6 +20,8 @@ LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_stream_volumes.xml \ + audio_policy_engine_default_stream_volumes.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml @@ -34,6 +36,22 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml index ab61d8a937..4ca33b4525 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml new file mode 100644 index 0000000000..21e6dd58ac --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_default_stream_volumes.xml @@ -0,0 +1,136 @@ + + + + + + + + 0,0 + 100,0 + + + 0,-9600 + 100,-9600 + + + + 1,-2400 + 33,-1800 + 66,-1200 + 100,-600 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4680 + 42,-2070 + 85,-540 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + 1,-12700 + 20,-8000 + 60,-4000 + 100,0 + + + + + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + + 0,-12700 + 20,-8000 + 60,-4000 + 100,0 + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml new file mode 100644 index 0000000000..73bde1fb7e --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -0,0 +1,231 @@ + + + + + + + AUDIO_STREAM_VOICE_CALL + 1 + 7 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2700 + 33,-1800 + 66,-900 + 100,0 + + + + + + + AUDIO_STREAM_SYSTEM + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-5100 + 57,-2800 + 71,-2500 + 85,-2300 + 100,-2100 + + + + + + + + + AUDIO_STREAM_RING + 0 + 7 + + + + + + + + + AUDIO_STREAM_MUSIC + 0 + 25 + + + + + + + + + AUDIO_STREAM_ALARM + 1 + 7 + + + + + + + + + AUDIO_STREAM_NOTIFICATION + 0 + 7 + + + + + + + + + AUDIO_STREAM_BLUETOOTH_SCO + 0 + 15 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + + AUDIO_STREAM_ENFORCED_AUDIBLE + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-3400 + 71,-2400 + 100,-2000 + + + + + + + + + AUDIO_STREAM_DTMF + 0 + 15 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-4000 + 71,-2400 + 100,-1400 + + + + + + + + + AUDIO_STREAM_TTS + 0 + 15 + + + + + + + + + AUDIO_STREAM_ACCESSIBILITY + 1 + 15 + + + + + + + + + AUDIO_STREAM_REROUTING + 0 + 1 + + + + + + + + + AUDIO_STREAM_PATCH + 0 + 1 + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 719ef01031..89a1694a92 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -113,7 +113,7 @@ bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream, const audio_stream_type_t &profile) { if (setPropertyForKey(stream, profile)) { - getApmObserver()->getVolumeCurves().switchVolumeCurve(profile, stream); + switchVolumeCurve(profile, stream); return true; } return false; diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk index 95eac1c17e..94fa788a90 100644 --- a/services/audiopolicy/enginedefault/Android.mk +++ b/services/audiopolicy/enginedefault/Android.mk @@ -8,6 +8,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/Engine.cpp \ src/EngineInstance.cpp \ + ../engine/common/src/VolumeCurve.cpp \ + ../engine/common/src/StreamVolumeCurves.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk index 866466fc14..f06ee4cf13 100644 --- a/services/audiopolicy/enginedefault/config/example/Android.mk +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -16,7 +16,9 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml + audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_stream_volumes.xml \ + audio_policy_engine_default_stream_volumes.xml include $(BUILD_PREBUILT) @@ -29,4 +31,20 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_default_stream_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml index ab61d8a937..4ca33b4525 100644 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml new file mode 100644 index 0000000000..21e6dd58ac --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml @@ -0,0 +1,136 @@ + + + + + + + + 0,0 + 100,0 + + + 0,-9600 + 100,-9600 + + + + 1,-2400 + 33,-1800 + 66,-1200 + 100,-600 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + 1,-4680 + 42,-2070 + 85,-540 + 100,0 + + + + 1,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + 1,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + 1,-12700 + 20,-8000 + 60,-4000 + 100,0 + + + + + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-1700 + 100,0 + + + + + 0,-4950 + 33,-3350 + 66,-1700 + 100,0 + + + + + 0,-5800 + 20,-4000 + 60,-2100 + 100,-1000 + + + + + 0,-12700 + 20,-8000 + 60,-4000 + 100,0 + + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml new file mode 100644 index 0000000000..73bde1fb7e --- /dev/null +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -0,0 +1,231 @@ + + + + + + + AUDIO_STREAM_VOICE_CALL + 1 + 7 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2700 + 33,-1800 + 66,-900 + 100,0 + + + + + + + AUDIO_STREAM_SYSTEM + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-5100 + 57,-2800 + 71,-2500 + 85,-2300 + 100,-2100 + + + + + + + + + AUDIO_STREAM_RING + 0 + 7 + + + + + + + + + AUDIO_STREAM_MUSIC + 0 + 25 + + + + + + + + + AUDIO_STREAM_ALARM + 1 + 7 + + + + + + + + + AUDIO_STREAM_NOTIFICATION + 0 + 7 + + + + + + + + + AUDIO_STREAM_BLUETOOTH_SCO + 0 + 15 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + + AUDIO_STREAM_ENFORCED_AUDIBLE + 0 + 7 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-3400 + 71,-2400 + 100,-2000 + + + + + + + + + AUDIO_STREAM_DTMF + 0 + 15 + + 1,-3000 + 33,-2600 + 66,-2200 + 100,-1800 + + + 1,-4000 + 71,-2400 + 100,-1400 + + + + + + + + + AUDIO_STREAM_TTS + 0 + 15 + + + + + + + + + AUDIO_STREAM_ACCESSIBILITY + 1 + 15 + + + + + + + + + AUDIO_STREAM_REROUTING + 0 + 1 + + + + + + + + + AUDIO_STREAM_PATCH + 0 + 1 + + + + + + + + diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 43023a8784..fd6a013a76 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -66,35 +66,6 @@ Engine::Engine() } } -status_t Engine::setPhoneState(audio_mode_t state) -{ - ALOGV("setPhoneState() state %d", state); - - if (state < 0 || state >= AUDIO_MODE_CNT) { - ALOGW("setPhoneState() invalid state %d", state); - return BAD_VALUE; - } - - if (state == getPhoneState()) { - ALOGW("setPhoneState() setting same state %d", state); - return BAD_VALUE; - } - - // store previous phone state for management of sonification strategy below - int oldState = getPhoneState(); - EngineBase::setPhoneState(state); - - if (!is_state_in_call(oldState) && is_state_in_call(state)) { - ALOGV(" Entering call in setPhoneState()"); - getApmObserver()->getVolumeCurves().switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, - AUDIO_STREAM_DTMF); - } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { - ALOGV(" Exiting call in setPhoneState()"); - getApmObserver()->getVolumeCurves().restoreOriginVolumeCurve(AUDIO_STREAM_DTMF); - } - return NO_ERROR; -} - status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { switch(usage) { diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index 15fc358687..d8a369891d 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -55,8 +55,6 @@ private: /// /// from EngineBase, so from AudioPolicyManagerInterface /// - status_t setPhoneState(audio_mode_t mode) override; - status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5b752ead9a..b563a042b2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1710,7 +1710,7 @@ status_t AudioPolicyManager::startSource(const sp& outp // apply volume rules for current stream and device if necessary checkAndSetVolume(stream, - mVolumeCurves->getVolumeIndex(stream, outputDesc->devices().types()), + getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()), outputDesc, outputDesc->devices().types()); @@ -2382,14 +2382,15 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, ALOGE("%s for stream %d: invalid min %d or max %d", __func__, stream , indexMin, indexMax); return; } - mVolumeCurves->initStreamVolume(stream, indexMin, indexMax); + // @todo: our proposal now use XML to store Indexes Min & Max + getVolumeCurves(stream).initVolume(indexMin, indexMax); // initialize other private stream volumes which follow this one for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - mVolumeCurves->initStreamVolume((audio_stream_type_t)curStream, indexMin, indexMax); + getVolumeCurves((audio_stream_type_t)curStream).initVolume(indexMin, indexMax); } } @@ -2397,13 +2398,13 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { - + auto &curves = getVolumeCurves(stream); // VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but // can be muted directly by an app that has MODIFY_PHONE_STATE permission. - if (((index < mVolumeCurves->getVolumeIndexMin(stream)) && + if (((index < curves.getVolumeIndexMin()) && !((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && index == 0)) || - (index > mVolumeCurves->getVolumeIndexMax(stream))) { + (index > curves.getVolumeIndexMax())) { return BAD_VALUE; } if (!audio_is_output_device(device)) { @@ -2411,7 +2412,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, } // Force max volume if stream cannot be muted - if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream); + if (!curves.canBeMuted()) index = curves.getVolumeIndexMax(); ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d", stream, device, index); @@ -2421,7 +2422,8 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } - mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index); + auto &curCurves = getVolumeCurves(static_cast(curStream)); + curCurves.addCurrentVolumeIndex(device, index); } // update volume on all outputs and streams matching the following: @@ -2455,8 +2457,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, curStreamDevice |= device; applyVolume = (Volume::getDeviceForVolume(curDevice) & curStreamDevice) != 0; } else { - applyVolume = !mVolumeCurves->hasVolumeIndexForDevice( - stream, curStreamDevice); + applyVolume = !curves.hasVolumeIndexForDevice(curStreamDevice); } // rescale index before applying to curStream as ranges may be different for // stream and curStream @@ -2465,9 +2466,10 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, //FIXME: workaround for truncated touch sounds // delayed volume change for system stream to be removed when the problem is // handled by system UI - status_t volStatus = - checkAndSetVolume((audio_stream_type_t)curStream, idx, desc, curDevice, - (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0); + status_t volStatus = checkAndSetVolume( + (audio_stream_type_t)curStream, idx, desc, curDevice, + (stream == AUDIO_STREAM_SYSTEM) ? + TOUCH_SOUND_FIXED_DELAY_MS : 0); if (volStatus != NO_ERROR) { status = volStatus; } @@ -2494,7 +2496,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, } device = Volume::getDeviceForVolume(device); - *index = mVolumeCurves->getVolumeIndex(stream, device); + *index = getVolumeCurves(stream).getVolumeIndex(device); ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); return NO_ERROR; } @@ -2927,7 +2929,6 @@ void AudioPolicyManager::dump(String8 *dst) const mHwModulesAll.dump(dst); mOutputs.dump(dst); mInputs.dump(dst); - mVolumeCurves->dump(dst); mEffects.dump(dst); mAudioPatches.dump(dst); mPolicyMixes.dump(dst); @@ -4114,9 +4115,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mpClientInterface(clientInterface), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), mA2dpSuspended(false), - mVolumeCurves(new VolumeCurvesCollection()), - mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, - mDefaultOutputDevice, static_cast(mVolumeCurves.get())), + mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice), mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), @@ -4150,8 +4149,6 @@ void AudioPolicyManager::loadConfig() { } status_t AudioPolicyManager::initialize() { - mVolumeCurves->initializeVolumeCurves(getConfig().isSpeakerDrcEnabled()); - // Once policy config has been parsed, retrieve an instance of the engine and initialize it. audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance(); if (!engineInstance) { @@ -5532,7 +5529,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, int index, audio_devices_t device) { - float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index); + auto &curves = getVolumeCurves(stream); + float volumeDB = curves.volIndexToDb(Volume::getDeviceCategory(device), index); // handle the case of accessibility active while a ringtone is playing: if the ringtone is much // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch @@ -5557,8 +5555,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, case AUDIO_STREAM_ENFORCED_AUDIBLE: case AUDIO_STREAM_DTMF: case AUDIO_STREAM_ACCESSIBILITY: { - int voiceVolumeIndex = - mVolumeCurves->getVolumeIndex(AUDIO_STREAM_VOICE_CALL, device); + int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device); const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; @@ -5592,7 +5589,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && - mVolumeCurves->canBeMuted(stream)) { + getVolumeCurves(stream).canBeMuted()) { // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was // just stopped @@ -5603,9 +5600,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, true /*fromCache*/).types(); float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, - mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC, - musicDevice), - musicDevice); + getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice), + musicDevice); float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; if (volumeDB > minVolDB) { @@ -5640,10 +5636,12 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, if (srcStream == dstStream) { return srcIndex; } - float minSrc = (float)mVolumeCurves->getVolumeIndexMin(srcStream); - float maxSrc = (float)mVolumeCurves->getVolumeIndexMax(srcStream); - float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream); - float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream); + auto &srcCurves = getVolumeCurves(srcStream); + auto &dstCurves = getVolumeCurves(dstStream); + float minSrc = (float)srcCurves.getVolumeIndexMin(); + float maxSrc = (float)srcCurves.getVolumeIndexMax(); + float minDst = (float)dstCurves.getVolumeIndexMin(); + float maxDst = (float)dstCurves.getVolumeIndexMax(); // preserve mute request or correct range if (srcIndex < minSrc) { @@ -5658,11 +5656,11 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, } status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, - int index, - const sp& outputDesc, - audio_devices_t device, - int delayMs, - bool force) + int index, + const sp& outputDesc, + audio_devices_t device, + int delayMs, + bool force) { // do not change actual stream volume if the stream is muted if (outputDesc->isMuted(streamToVolumeSource(stream))) { @@ -5698,7 +5696,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); + voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax(); } else { voiceVolume = 1.0; } @@ -5721,7 +5719,7 @@ void AudioPolicyManager::applyStreamVolumes(const sp& out for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { checkAndSetVolume((audio_stream_type_t)stream, - mVolumeCurves->getVolumeIndex((audio_stream_type_t)stream, device), + getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device), outputDesc, device, delayMs, @@ -5754,10 +5752,10 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", stream, on, outputDesc->getMuteCount(stream), device); - + auto &curves = getVolumeCurves(stream); if (on) { if (!outputDesc->isMuted(streamToVolumeSource(stream))) { - if (mVolumeCurves->canBeMuted(stream) && + if (curves.canBeMuted() && ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { checkAndSetVolume(stream, 0, outputDesc, device, delayMs); @@ -5772,7 +5770,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, } if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { checkAndSetVolume(stream, - mVolumeCurves->getVolumeIndex(stream, device), + curves.getVolumeIndex(device), outputDesc, device, delayMs); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 70ad6ac7aa..06a1f3e416 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -49,7 +49,7 @@ #include #include #include -#include +#include "TypeConverter.h" namespace android { @@ -310,12 +310,24 @@ protected: { return mAvailableInputDevices; } - virtual IVolumeCurvesCollection &getVolumeCurves() { return *mVolumeCurves; } virtual const sp &getDefaultOutputDevice() const { return mDefaultOutputDevice; } + IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr) + { + auto *curves = mEngine->getVolumeCurvesForAttributes(attr); + ALOG_ASSERT(curves != nullptr, "No curves for attributes %s", toString(attr).c_str()); + return *curves; + } + IVolumeCurves &getVolumeCurves(audio_stream_type_t stream) + { + auto *curves = mEngine->getVolumeCurvesForStreamType(stream); + ALOG_ASSERT(curves != nullptr, "No curves for stream %s", toString(stream).c_str()); + return *curves; + } + void addOutput(audio_io_handle_t output, const sp& outputDesc); void removeOutput(audio_io_handle_t output); void addInput(audio_io_handle_t input, const sp& inputDesc); @@ -624,12 +636,12 @@ protected: float mLastVoiceVolume; // last voice volume value sent to audio HAL bool mA2dpSuspended; // true if A2DP output is suspended - std::unique_ptr mVolumeCurves; // Volume Curves per use case and device category EffectDescriptorCollection mEffects; // list of registered audio effects sp mDefaultOutputDevice; // output device selected by default at boot time HwModuleCollection mHwModules; // contains only modules that have been loaded successfully HwModuleCollection mHwModulesAll; // normally not needed, used during construction and for // dumps + AudioPolicyConfig mConfig; std::atomic mAudioPortGeneration; -- GitLab From 3de3ee7025a84cbb1902c222b6d897414862c373 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 22 Feb 2019 18:20:37 -0800 Subject: [PATCH 1015/1530] Revert "Revert "audiopolicy: engine: use apm XML config file for volume curves"" This reverts commit 1462a279faace46266ab1f2151393ecd31c62a5e. Test: make --- .../engine/common/include/VolumeCurve.h | 4 +- .../engine/common/src/EngineBase.cpp | 5 +- .../engine/common/src/EngineDefaultConfig.h | 122 +--------------- services/audiopolicy/engine/config/Android.mk | 3 +- .../engine/config/include/EngineConfig.h | 1 + .../engine/config/src/EngineConfig.cpp | 135 ++++++++++++++++++ 6 files changed, 145 insertions(+), 125 deletions(-) diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h index c2a1da702e..0ec63e19fc 100644 --- a/services/audiopolicy/engine/common/include/VolumeCurve.h +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -182,8 +182,8 @@ public: private: KeyedVector > mOriginVolumeCurves; std::map mIndexCur; /**< current volume index per device. */ - /*const*/ int mIndexMin; /**< min volume index. */ - /*const*/ int mIndexMax; /**< max volume index. */ + int mIndexMin; /**< min volume index. */ + int mIndexMax; /**< max volume index. */ const bool mCanBeMuted = true; /**< true is the stream can be muted. */ const audio_stream_type_t mStream; /**< Keep it for legacy. */ diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 2449b61d12..6e2ab4cdfa 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -139,7 +139,10 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__); - result = {std::make_unique(gDefaultEngineConfig), 0}; + engineConfig::Config config = gDefaultEngineConfig; + android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups); + result = {std::make_unique(config), + static_cast(ret == NO_ERROR ? 0 : 1)}; } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index 86a59b00ee..f1642c5e3c 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -131,131 +131,11 @@ const engineConfig::ProductStrategies gOrderedStrategies = { } }; -const engineConfig::VolumeGroups gVolumeGroups = { - {"voice_call", "AUDIO_STREAM_VOICE_CALL", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -2700}, {33, -1800}, {66, -900}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, - }, - }, - {"system", "AUDIO_STREAM_SYSTEM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -5100}, {57, -2800}, {71, -2500}, {85, -2300}, {100, -2100} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"ring", "AUDIO_STREAM_RING", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"music", "AUDIO_STREAM_MUSIC", 0, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"alarm", "AUDIO_STREAM_ALARM", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_NON_MUTABLE_EXT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"notification", "AUDIO_STREAM_NOTIFICATION", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {1, -4680}, {42, -2070}, {85, -540}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_SPEAKER_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -4950}, {33, -3350}, {66, -1700}, {100, 0} } }, // DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE - }, - }, - {"bluetooth_sco", "AUDIO_STREAM_BLUETOOTH_SCO", 1, 10, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_SPEAKER", { {0, -2400}, {33, -1600}, {66, -800}, {100, 0} } }, - {"DEVICE_CATEGORY_EARPIECE", { {0, -4200}, {33, -2800}, {66, -1400}, {100, 0} } }, - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"enforced_audible", "AUDIO_STREAM_ENFORCED_AUDIBLE", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -3400}, {71, -2400}, {100, -2000} } }, - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"dtmf", "AUDIO_STREAM_DTMF", 0, 100, - { - {"DEVICE_CATEGORY_HEADSET", { {1, -3000}, {33, -2600}, {66, -2200}, {100, -1800} } }, - {"DEVICE_CATEGORY_SPEAKER", { {1, -4000}, {71, -2400}, {100, -1400} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {1, -2400}, {33, -1800}, {66, -1200}, {100, -600} } }, // DEFAULT_SYSTEM_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {1, -5800}, {20, -4000}, {60, -2100}, {100, -1000} } }, // DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {1, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_HEARING_AID_VOLUME_CURVE - }, - }, - {"tts", "AUDIO_STREAM_TTS", 0, 16, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -9600}, {100, -9600} } }, // SILENT_VOLUME_CURVE - }, - }, - {"accessibility", "AUDIO_STREAM_ACCESSIBILITY", 1, 40, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_SPEAKER_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -5800}, {20, -4000}, {60, -1700}, {100, 0} } }, // DEFAULT_NON_MUTABLE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -12700}, {20, -8000}, {60, -4000}, {100, 0} } }, // DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE - }, - }, - {"rerouting", "AUDIO_STREAM_REROUTING", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, - {"patch", "AUDIO_STREAM_PATCH", 0, 1, - { - {"DEVICE_CATEGORY_HEADSET", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_SPEAKER", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EARPIECE", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_EXT_MEDIA", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - {"DEVICE_CATEGORY_HEARING_AID", { {0, -0}, {100, 0} } }, // FULL_SCALE_VOLUME_CURVE - }, - }, -}; - const engineConfig::Config gDefaultEngineConfig = { 1.0, gOrderedStrategies, {}, {}, - gVolumeGroups + {} }; } // namespace android diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk index fe7d961799..0b292a5fa3 100644 --- a/services/audiopolicy/engine/config/Android.mk +++ b/services/audiopolicy/engine/config/Android.mk @@ -23,7 +23,8 @@ LOCAL_SHARED_LIBRARIES := \ libandroidicu \ libxml2 \ libutils \ - liblog + liblog \ + libcutils LOCAL_STATIC_LIBRARIES := \ libaudiopolicycomponents diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index f7caad238d..a188115fff 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -113,6 +113,7 @@ struct ParsingResult { * @return audio policy usage @see Config */ ParsingResult parse(const char* path = DEFAULT_PATH); +android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups); } // namespace engineConfig } // namespace android diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 227ebd8cde..00fbac411b 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -19,6 +19,7 @@ #include "EngineConfig.h" #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -506,6 +508,84 @@ status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Coll return NO_ERROR; } +static constexpr const char *legacyVolumecollectionTag = "volumes"; +static constexpr const char *legacyVolumeTag = "volume"; + +status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur, + std::map &legacyVolumes) +{ + std::string streamTypeLiteral = getXmlAttribute(cur, "stream"); + if (streamTypeLiteral.empty()) { + ALOGE("%s: No attribute stream found", __func__); + return BAD_VALUE; + } + std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory"); + if (deviceCategoryLiteral.empty()) { + ALOGE("%s: No attribute deviceCategory found", __func__); + return BAD_VALUE; + } + std::string referenceName = getXmlAttribute(cur, "ref"); + const xmlNode *ref = NULL; + if (!referenceName.empty()) { + getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag); + if (ref == NULL) { + ALOGE("%s: No reference Ptr found for %s", __func__, referenceName.c_str()); + return BAD_VALUE; + } + ALOGV("%s: reference found for %s", __func__, referenceName.c_str()); + } + CurvePoints curvePoints; + for (const xmlNode *child = referenceName.empty() ? + cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) { + xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); + if (pointXml == NULL) { + return BAD_VALUE; + } + ALOGV("%s: %s=%s", __func__, legacyVolumeTag, + reinterpret_cast(pointXml.get())); + std::vector point; + collectionFromString>( + reinterpret_cast(pointXml.get()), point, ","); + if (point.size() != 2) { + ALOGE("%s: Invalid %s: %s", __func__, VolumeTraits::volumePointTag, + reinterpret_cast(pointXml.get())); + return BAD_VALUE; + } + curvePoints.push_back({point[0], point[1]}); + } + } + legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints }); + return NO_ERROR; +} + +static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur, + VolumeGroups &volumeGroups, + size_t &nbSkippedElement) +{ + std::map legacyVolumeMap; + for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) { + continue; + } + const xmlNode *child = cur->xmlChildrenNode; + for (; child != NULL; child = child->next) { + if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) { + + status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap); + if (status != NO_ERROR) { + nbSkippedElement += 1; + } + } + } + } + for (const auto &volumeMapIter : legacyVolumeMap) { + volumeGroups.push_back({ volumeMapIter.first, volumeMapIter.first, 0, 100, + volumeMapIter.second }); + } + return NO_ERROR; +} + ParsingResult parse(const char* path) { xmlDocPtr doc; doc = xmlParseFile(path); @@ -543,5 +623,60 @@ ParsingResult parse(const char* path) { return {std::move(config), nbSkippedElements}; } +android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) { + xmlDocPtr doc; + doc = xmlParseFile(path); + if (doc == NULL) { + ALOGE("%s: Could not parse document %s", __FUNCTION__, path); + return BAD_VALUE; + } + xmlNodePtr cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path); + xmlFreeDoc(doc); + return BAD_VALUE; + } + if (xmlXIncludeProcess(doc) < 0) { + ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path); + return BAD_VALUE; + } + size_t nbSkippedElements = 0; + return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements); +} + +static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"}; +static const int kConfigLocationListSize = + (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0])); +static const int gApmXmlConfigFilePathMaxLength = 128; + +static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml"; +static constexpr const char *apmA2dpOffloadDisabledXmlConfigFileName = + "audio_policy_configuration_a2dp_offload_disabled.xml"; + +android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) { + char audioPolicyXmlConfigFile[gApmXmlConfigFilePathMaxLength]; + std::vector fileNames; + status_t ret; + + if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false) && + property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // A2DP offload supported but disabled: try to use special XML file + fileNames.push_back(apmA2dpOffloadDisabledXmlConfigFileName); + } + fileNames.push_back(apmXmlConfigFileName); + + for (const char* fileName : fileNames) { + for (int i = 0; i < kConfigLocationListSize; i++) { + snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), + "%s/%s", kConfigLocationList[i], fileName); + ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups); + if (ret == NO_ERROR) { + return ret; + } + } + } + return BAD_VALUE; +} + } // namespace engineConfig } // namespace android -- GitLab From b81347ef8c64fa0f8e830cb4100fd4c5f8a123dd Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 22 Feb 2019 00:42:46 -0800 Subject: [PATCH 1016/1530] Load media extractor plug-ins by using media namespace. Test: build, boot, dumpsys media.extractor Bug: 125134086 Change-Id: I502535900946907bcf7bafae7c5af18b3268d75a --- media/libstagefright/MediaExtractorFactory.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index a938d514b5..f6e112e143 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -277,19 +277,14 @@ void MediaExtractorFactory::RegisterExtractorsInApex( std::string libDirPathEx = libDirPath; libDirPathEx += "/extractors"; - android_namespace_t *extractorNs = android_create_namespace("extractor", - nullptr, // ld_library_path - libDirPath, // default_library_path - ANDROID_NAMESPACE_TYPE_ISOLATED, - libDirPathEx.c_str(), // permitted_when_isolated_path - nullptr); // parent - if (!android_link_namespaces(extractorNs, nullptr, gLinkedLibraries.c_str())) { - ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex."); + android_namespace_t *mediaNs = android_get_exported_namespace("media"); + if (mediaNs == NULL) { + ALOGE("couldn't find media namespace."); return; } const android_dlextinfo dlextinfo = { .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = extractorNs, + .library_namespace = mediaNs, }; // try extractors subfolder first -- GitLab From b52f0a0a9c40b08d7a559c8547ae10eff9d7607b Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 22 Feb 2019 01:02:40 -0800 Subject: [PATCH 1017/1530] Remove unused code path in MediaExtractor(Factory|Service) Test: build, boot, dumpsys media.extractor Bug: 125134086 Change-Id: I723570315285d7c2caadffd71cd2d15efde11819 --- .../libstagefright/MediaExtractorFactory.cpp | 94 ++++--------------- .../media/stagefright/MediaExtractorFactory.h | 10 +- services/mediaextractor/Android.mk | 18 ---- .../mediaextractor/MediaExtractorService.cpp | 4 +- 4 files changed, 25 insertions(+), 101 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index f6e112e143..a309ee4dae 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include #include #include @@ -34,7 +32,6 @@ #include #include #include -#include #include #include @@ -130,13 +127,6 @@ Mutex MediaExtractorFactory::gPluginMutex; std::shared_ptr>> MediaExtractorFactory::gPlugins; bool MediaExtractorFactory::gPluginsRegistered = false; bool MediaExtractorFactory::gIgnoreVersion = false; -std::string MediaExtractorFactory::gLinkedLibraries; - -// static -void MediaExtractorFactory::SetLinkedLibraries(const std::string& linkedLibraries) { - Mutex::Autolock autoLock(gPluginMutex); - gLinkedLibraries = linkedLibraries; -} // static void *MediaExtractorFactory::sniff( @@ -235,66 +225,12 @@ void MediaExtractorFactory::RegisterExtractor(const sp &plugin, } //static -void MediaExtractorFactory::RegisterExtractorsInSystem( - const char *libDirPath, std::list> &pluginList) { - ALOGV("search for plugins at %s", libDirPath); - DIR *libDir = opendir(libDirPath); - if (libDir) { - struct dirent* libEntry; - while ((libEntry = readdir(libDir))) { - if (libEntry->d_name[0] == '.') { - continue; - } - String8 libPath = String8(libDirPath) + "/" + libEntry->d_name; - void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL); - if (libHandle) { - GetExtractorDef getDef = - (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); - if (getDef) { - ALOGV("registering sniffer for %s", libPath.string()); - RegisterExtractor( - new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); - } else { - ALOGW("%s does not contain sniffer", libPath.string()); - dlclose(libHandle); - } - } else { - ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); - } - } - - closedir(libDir); - } else { - ALOGE("couldn't opendir(%s)", libDirPath); - } -} - -//static -void MediaExtractorFactory::RegisterExtractorsInApex( - const char *libDirPath, std::list> &pluginList) { +void MediaExtractorFactory::RegisterExtractors( + const char *libDirPath, const android_dlextinfo* dlextinfo, + std::list> &pluginList) { ALOGV("search for plugins at %s", libDirPath); - ALOGV("linked libs %s", gLinkedLibraries.c_str()); - - std::string libDirPathEx = libDirPath; - libDirPathEx += "/extractors"; - android_namespace_t *mediaNs = android_get_exported_namespace("media"); - if (mediaNs == NULL) { - ALOGE("couldn't find media namespace."); - return; - } - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = mediaNs, - }; - // try extractors subfolder first - DIR *libDir = opendir(libDirPathEx.c_str()); - - if (libDir) { - libDirPath = libDirPathEx.c_str(); - } else { - libDir = opendir(libDirPath); - } + DIR *libDir = opendir(libDirPath); if (libDir) { struct dirent* libEntry; while ((libEntry = readdir(libDir))) { @@ -307,7 +243,7 @@ void MediaExtractorFactory::RegisterExtractorsInApex( } void *libHandle = android_dlopen_ext( libPath.string(), - RTLD_NOW | RTLD_LOCAL, &dlextinfo); + RTLD_NOW | RTLD_LOCAL, dlextinfo); if (libHandle) { GetExtractorDef getDef = (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); @@ -347,17 +283,27 @@ void MediaExtractorFactory::UpdateExtractors() { std::shared_ptr>> newList(new std::list>()); - RegisterExtractorsInApex("/apex/com.android.media/lib" + android_namespace_t *mediaNs = android_get_exported_namespace("media"); + if (mediaNs != NULL) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = mediaNs, + }; + RegisterExtractors("/apex/com.android.media/lib" #ifdef __LP64__ - "64" + "64" #endif - , *newList); + "/extractors", &dlextinfo, *newList); + + } else { + ALOGE("couldn't find media namespace."); + } - RegisterExtractorsInSystem("/system/lib" + RegisterExtractors("/system/lib" #ifdef __LP64__ "64" #endif - "/extractors", *newList); + "/extractors", NULL, *newList); newList->sort(compareFunc); gPlugins = newList; diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index 4358aac9a7..ea87948d61 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -21,6 +21,7 @@ #include #include +#include #include namespace android { @@ -36,19 +37,16 @@ public: const sp &source, const char *mime = NULL); static status_t dump(int fd, const Vector& args); static std::unordered_set getSupportedTypes(); - static void SetLinkedLibraries(const std::string& linkedLibraries); private: static Mutex gPluginMutex; static std::shared_ptr>> gPlugins; static bool gPluginsRegistered; static bool gIgnoreVersion; - static std::string gLinkedLibraries; - static void RegisterExtractorsInSystem( - const char *libDirPath, std::list> &pluginList); - static void RegisterExtractorsInApex( - const char *libDirPath, std::list> &pluginList); + static void RegisterExtractors( + const char *libDirPath, const android_dlextinfo* dlextinfo, + std::list> &pluginList); static void RegisterExtractor( const sp &plugin, std::list> &pluginList); diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 7654982f49..65fcf40e7a 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -8,24 +8,6 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog LOCAL_MODULE:= libmediaextractorservice - -sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\ - $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ - $(UBSAN_RUNTIME_LIBRARY) \ - $(TSAN_RUNTIME_LIBRARY))) - -# $(info Sanitizer: $(sanitizer_runtime_libraries)) - -ndk_libraries := $(call normalize-path-list,$(addprefix lib,$(addsuffix .so,\ - $(NDK_PREBUILT_SHARED_LIBRARIES)))) - -# $(info NDK: $(ndk_libraries)) - -LOCAL_CFLAGS += -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(ndk_libraries)"' - -sanitizer_runtime_libraries := -ndk_libraries := - include $(BUILD_SHARED_LIBRARY) diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp index 0665930394..de5c3e4fef 100644 --- a/services/mediaextractor/MediaExtractorService.cpp +++ b/services/mediaextractor/MediaExtractorService.cpp @@ -30,9 +30,7 @@ namespace android { MediaExtractorService::MediaExtractorService() - : BnMediaExtractorService() { - MediaExtractorFactory::SetLinkedLibraries(std::string(LINKED_LIBRARIES)); -} + : BnMediaExtractorService() { } sp MediaExtractorService::makeExtractor( const sp &remoteSource, const char *mime) { -- GitLab From 86320e3ddb542b9cfb5a8bd78a3cd2cec86cca88 Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Tue, 22 Jan 2019 14:47:04 -0800 Subject: [PATCH 1018/1530] codec2: vndk: Support new ion api for linux kernel 4.12+ Specialize C2AllocationIon::Impl to handle new (v4.12) ion and use this version if the new ion version is detected at run-time. Bug: 123398356 Change-Id: I8a917e91d80203aca88a174c9d0dd7f416bc0229 --- media/codec2/vndk/C2AllocatorIon.cpp | 187 +++++++++++++++++++-------- 1 file changed, 131 insertions(+), 56 deletions(-) diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp index 736aac5841..d22153d85d 100644 --- a/media/codec2/vndk/C2AllocatorIon.cpp +++ b/media/codec2/vndk/C2AllocatorIon.cpp @@ -140,6 +140,7 @@ public: protected: class Impl; + class ImplV2; Impl *mImpl; // TODO: we could make this encapsulate shared_ptr and copiable @@ -147,7 +148,7 @@ protected: }; class C2AllocationIon::Impl { -private: +protected: /** * Constructs an ion allocation. * @@ -191,11 +192,7 @@ public: * \return created ion allocation (implementation) which may be invalid if the * import failed. */ - static Impl *Import(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id) { - ion_user_handle_t buffer = -1; - int ret = ion_import(ionFd, bufferFd, &buffer); - return new Impl(ionFd, capacity, bufferFd, buffer, id, ret); - } + static Impl *Import(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id); /** * Constructs an ion allocation by allocating an ion buffer. @@ -209,24 +206,7 @@ public: * \return created ion allocation (implementation) which may be invalid if the * allocation failed. */ - static Impl *Alloc(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags, C2Allocator::id_t id) { - int bufferFd = -1; - ion_user_handle_t buffer = -1; - size_t alignedSize = align == 0 ? size : (size + align - 1) & ~(align - 1); - int ret = ion_alloc(ionFd, alignedSize, align, heapMask, flags, &buffer); - ALOGV("ion_alloc(ionFd = %d, size = %zu, align = %zu, prot = %d, flags = %d) " - "returned (%d) ; buffer = %d", - ionFd, alignedSize, align, heapMask, flags, ret, buffer); - if (ret == 0) { - // get buffer fd for native handle constructor - ret = ion_share(ionFd, buffer, &bufferFd); - if (ret != 0) { - ion_free(ionFd, buffer); - buffer = -1; - } - } - return new Impl(ionFd, alignedSize, bufferFd, buffer, id, ret); - } + static Impl *Alloc(int ionFd, size_t size, size_t align, unsigned heapMask, unsigned flags, C2Allocator::id_t id); c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) { (void)fence; // TODO: wait for fence @@ -256,32 +236,7 @@ public: size_t mapSize = size + alignmentBytes; Mapping map = { nullptr, alignmentBytes, mapSize }; - c2_status_t err = C2_OK; - if (mMapFd == -1) { - int ret = ion_map(mIonFd, mBuffer, mapSize, prot, - flags, mapOffset, (unsigned char**)&map.addr, &mMapFd); - ALOGV("ion_map(ionFd = %d, handle = %d, size = %zu, prot = %d, flags = %d, " - "offset = %zu) returned (%d)", - mIonFd, mBuffer, mapSize, prot, flags, mapOffset, ret); - if (ret) { - mMapFd = -1; - map.addr = *addr = nullptr; - err = c2_map_errno(-ret); - } else { - *addr = (uint8_t *)map.addr + alignmentBytes; - } - } else { - map.addr = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset); - ALOGV("mmap(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) " - "returned (%d)", - mapSize, prot, flags, mMapFd, mapOffset, errno); - if (map.addr == MAP_FAILED) { - map.addr = *addr = nullptr; - err = c2_map_errno(errno); - } else { - *addr = (uint8_t *)map.addr + alignmentBytes; - } - } + c2_status_t err = mapInternal(mapSize, mapOffset, alignmentBytes, prot, flags, &(map.addr), addr); if (map.addr) { mMappings.push_back(map); } @@ -289,7 +244,7 @@ public: } c2_status_t unmap(void *addr, size_t size, C2Fence *fence) { - if (mMapFd < 0 || mMappings.empty()) { + if (mMappings.empty()) { ALOGD("tried to unmap unmapped buffer"); return C2_NOT_FOUND; } @@ -307,14 +262,14 @@ public: *fence = C2Fence(); // not using fences } (void)mMappings.erase(it); - ALOGV("successfully unmapped: %d", mBuffer); + ALOGV("successfully unmapped: %d", mHandle.bufferFd()); return C2_OK; } ALOGD("unmap failed to find specified map"); return C2_BAD_VALUE; } - ~Impl() { + virtual ~Impl() { if (!mMappings.empty()) { ALOGD("Dangling mappings!"); for (const Mapping &map : mMappings) { @@ -326,7 +281,9 @@ public: mMapFd = -1; } if (mInit == C2_OK) { - (void)ion_free(mIonFd, mBuffer); + if (mBuffer >= 0) { + (void)ion_free(mIonFd, mBuffer); + } native_handle_close(&mHandle); } if (mIonFd >= 0) { @@ -346,11 +303,42 @@ public: return mId; } - ion_user_handle_t ionHandle() const { + virtual ion_user_handle_t ionHandle() const { return mBuffer; } -private: +protected: + virtual c2_status_t mapInternal(size_t mapSize, size_t mapOffset, size_t alignmentBytes, + int prot, int flags, void** base, void** addr) { + c2_status_t err = C2_OK; + if (mMapFd == -1) { + int ret = ion_map(mIonFd, mBuffer, mapSize, prot, + flags, mapOffset, (unsigned char**)base, &mMapFd); + ALOGV("ion_map(ionFd = %d, handle = %d, size = %zu, prot = %d, flags = %d, " + "offset = %zu) returned (%d)", + mIonFd, mBuffer, mapSize, prot, flags, mapOffset, ret); + if (ret) { + mMapFd = -1; + *base = *addr = nullptr; + err = c2_map_errno(-ret); + } else { + *addr = (uint8_t *)*base + alignmentBytes; + } + } else { + *base = mmap(nullptr, mapSize, prot, flags, mMapFd, mapOffset); + ALOGV("mmap(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) " + "returned (%d)", + mapSize, prot, flags, mMapFd, mapOffset, errno); + if (*base == MAP_FAILED) { + *base = *addr = nullptr; + err = c2_map_errno(errno); + } else { + *addr = (uint8_t *)*base + alignmentBytes; + } + } + return err; + } + int mIonFd; C2HandleIon mHandle; ion_user_handle_t mBuffer; @@ -365,6 +353,93 @@ private: std::list mMappings; }; +class C2AllocationIon::ImplV2 : public C2AllocationIon::Impl { +public: + /** + * Constructs an ion allocation for platforms with new (ion_4.12.h) api + * + * \note We always create an ion allocation, even if the allocation or import fails + * so that we can capture the error. + * + * \param ionFd ion client (ownership transferred to created object) + * \param capacity size of allocation + * \param bufferFd buffer handle (ownership transferred to created object). Must be + * invalid if err is not 0. + * \param err errno during buffer allocation or import + */ + ImplV2(int ionFd, size_t capacity, int bufferFd, C2Allocator::id_t id, int err) + : Impl(ionFd, capacity, bufferFd, -1 /*buffer*/, id, err) { + } + + virtual ~ImplV2() = default; + + virtual ion_user_handle_t ionHandle() const { + return mHandle.bufferFd(); + } + +protected: + virtual c2_status_t mapInternal(size_t mapSize, size_t mapOffset, size_t alignmentBytes, + int prot, int flags, void** base, void** addr) { + c2_status_t err = C2_OK; + *base = mmap(nullptr, mapSize, prot, flags, mHandle.bufferFd(), mapOffset); + ALOGV("mmapV2(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) " + "returned (%d)", + mapSize, prot, flags, mHandle.bufferFd(), mapOffset, errno); + if (*base == MAP_FAILED) { + *base = *addr = nullptr; + err = c2_map_errno(errno); + } else { + *addr = (uint8_t *)*base + alignmentBytes; + } + return err; + } + +}; + +C2AllocationIon::Impl *C2AllocationIon::Impl::Import(int ionFd, size_t capacity, int bufferFd, + C2Allocator::id_t id) { + int ret = 0; + if (ion_is_legacy(ionFd)) { + ion_user_handle_t buffer = -1; + ret = ion_import(ionFd, bufferFd, &buffer); + return new Impl(ionFd, capacity, bufferFd, buffer, id, ret); + } else { + return new ImplV2(ionFd, capacity, bufferFd, id, ret); + } +} + +C2AllocationIon::Impl *C2AllocationIon::Impl::Alloc(int ionFd, size_t size, size_t align, + unsigned heapMask, unsigned flags, C2Allocator::id_t id) { + int bufferFd = -1; + ion_user_handle_t buffer = -1; + size_t alignedSize = align == 0 ? size : (size + align - 1) & ~(align - 1); + int ret; + + if (ion_is_legacy(ionFd)) { + ret = ion_alloc(ionFd, alignedSize, align, heapMask, flags, &buffer); + ALOGV("ion_alloc(ionFd = %d, size = %zu, align = %zu, prot = %d, flags = %d) " + "returned (%d) ; buffer = %d", + ionFd, alignedSize, align, heapMask, flags, ret, buffer); + if (ret == 0) { + // get buffer fd for native handle constructor + ret = ion_share(ionFd, buffer, &bufferFd); + if (ret != 0) { + ion_free(ionFd, buffer); + buffer = -1; + } + } + return new Impl(ionFd, alignedSize, bufferFd, buffer, id, ret); + + } else { + ret = ion_alloc_fd(ionFd, alignedSize, align, heapMask, flags, &bufferFd); + ALOGV("ion_alloc_fd(ionFd = %d, size = %zu, align = %zu, prot = %d, flags = %d) " + "returned (%d) ; bufferFd = %d", + ionFd, alignedSize, align, heapMask, flags, ret, bufferFd); + + return new ImplV2(ionFd, alignedSize, bufferFd, id, ret); + } +} + c2_status_t C2AllocationIon::map( size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) { return mImpl->map(offset, size, usage, fence, addr); -- GitLab From 03852c673804159db10b015adedb2844f7478804 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 25 Feb 2019 13:48:14 -0800 Subject: [PATCH 1019/1530] Remove static linking of libstagefright and libmedia_helper from libmedia2_jni Test: build and boot Bug: 126159708 Change-Id: Iac1b4c123a985f6f7f6f88efe320f49981c793f4 --- media/libmedia/Android.bp | 1 + media/libstagefright/Android.bp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 5853e4bd31..9799cad6e3 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -259,6 +259,7 @@ cc_library_static { name: "libmedia_player2_util", srcs: [ + "AudioParameter.cpp", "BufferingSettings.cpp", "DataSourceDesc.cpp", "MediaCodecBuffer.cpp", diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 488890da74..2bd7288eec 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -265,9 +265,11 @@ cc_library_static { srcs: [ "ClearFileSource.cpp", "DataURISource.cpp", + "DataSourceBase.cpp", "HTTPBase.cpp", "HevcUtils.cpp", "MediaClock.cpp", + "MediaSource.cpp", "NdkUtils.cpp", "Utils.cpp", "VideoFrameSchedulerBase.cpp", -- GitLab From 1114eea0a9a48caa6fea6083bbee5a3e2b77c6d5 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 25 Feb 2019 14:35:24 -0800 Subject: [PATCH 1020/1530] CCodec: enforce required configurations Bug: 122324450 Test: Modify MediaCodecTest#testPCMEncoding to lack required params and observe them failing. Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: Ic3f245798b6448bf194ef1a4c2d6f37179c55117 --- media/codec2/sfplugin/CCodec.cpp | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index dce3222cef..98e5951ab7 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -709,6 +709,49 @@ void CCodec::configure(const sp &msg) { Mutexed::Locked config(mConfig); config->mUsingSurface = surface != nullptr; + // Enforce required parameters + int32_t i32; + float flt; + if (config->mDomain & Config::IS_AUDIO) { + if (!msg->findInt32(KEY_SAMPLE_RATE, &i32)) { + ALOGD("sample rate is missing, which is required for audio components."); + return BAD_VALUE; + } + if (!msg->findInt32(KEY_CHANNEL_COUNT, &i32)) { + ALOGD("channel count is missing, which is required for audio components."); + return BAD_VALUE; + } + if ((config->mDomain & Config::IS_ENCODER) + && !mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_FLAC) + && !msg->findInt32(KEY_BIT_RATE, &i32) + && !msg->findFloat(KEY_BIT_RATE, &flt)) { + ALOGD("bitrate is missing, which is required for audio encoders."); + return BAD_VALUE; + } + } + if (config->mDomain & (Config::IS_IMAGE | Config::IS_VIDEO)) { + if (!msg->findInt32(KEY_WIDTH, &i32)) { + ALOGD("width is missing, which is required for image/video components."); + return BAD_VALUE; + } + if (!msg->findInt32(KEY_HEIGHT, &i32)) { + ALOGD("height is missing, which is required for image/video components."); + return BAD_VALUE; + } + if ((config->mDomain & Config::IS_ENCODER) && (config->mDomain & Config::IS_VIDEO)) { + if (!msg->findInt32(KEY_BIT_RATE, &i32) + && !msg->findFloat(KEY_BIT_RATE, &flt)) { + ALOGD("bitrate is missing, which is required for video encoders."); + return BAD_VALUE; + } + if (!msg->findInt32(KEY_I_FRAME_INTERVAL, &i32) + && !msg->findFloat(KEY_I_FRAME_INTERVAL, &flt)) { + ALOGD("I frame interval is missing, which is required for video encoders."); + return BAD_VALUE; + } + } + } + /* * Handle input surface configuration */ -- GitLab From 251c7f0a9dc35dd3024fb4b42d27454abbd89d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 7 Nov 2018 10:41:08 +0100 Subject: [PATCH 1021/1530] audiopolicy: engine: Add Volume Groups to common Engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL adds the concept of Volume Group to the engine. It generalizes the volume management today controled by stream types and hard coded into AOSP. The goal is to control the volume per attributes, being able to define a group of attributes that follow the same volume curves. It intends to replace the concept of aliases in AudioService. Bug: 124767636 Test: build Change-Id: Icd079374cc1680d074b01836eca0bceb0b0c5247 Signed-off-by: François Gaffie --- media/libaudioclient/AudioAttributes.cpp | 4 +- .../include/media/AudioAttributes.h | 7 +- .../include/media/AudioCommonTypes.h | 6 +- services/audiopolicy/common/include/Volume.h | 1 + .../include/IVolumeCurves.h | 2 + .../managerdefinitions/src/HwModule.cpp | 2 +- .../engine/common/include/EngineBase.h | 25 ++- .../engine/common/include/ProductStrategy.h | 21 +- .../common/include/StreamVolumeCurves.h | 95 --------- .../engine/common/include/VolumeCurve.h | 28 +-- .../engine/common/include/VolumeGroup.h | 62 ++++++ .../engine/common/src/EngineBase.cpp | 112 +++++++--- .../engine/common/src/EngineDefaultConfig.h | 26 +-- .../engine/common/src/ProductStrategy.cpp | 73 ++++++- .../engine/common/src/StreamVolumeCurves.cpp | 47 ----- .../engine/common/src/VolumeCurve.cpp | 35 +++- .../engine/common/src/VolumeGroup.cpp | 75 +++++++ .../engine/config/include/EngineConfig.h | 2 +- .../engine/config/src/EngineConfig.cpp | 51 +++-- .../interface/AudioPolicyManagerInterface.h | 42 +++- .../audiopolicy/engineconfigurable/Android.mk | 2 +- .../config/example/Android.mk | 11 +- .../audio_policy_engine_configuration.xml | 1 + ...audio_policy_engine_product_strategies.xml | 39 ++-- .../audio_policy_engine_volumes.xml | 192 ++++++++++++++++++ ...audio_policy_engine_product_strategies.xml | 26 +-- .../audio_policy_engine_stream_volumes.xml | 28 +-- services/audiopolicy/enginedefault/Android.mk | 4 +- ...audio_policy_engine_product_strategies.xml | 26 +-- .../audio_policy_engine_stream_volumes.xml | 28 +-- .../managerdefault/AudioPolicyManager.cpp | 7 +- .../managerdefault/AudioPolicyManager.h | 14 +- 32 files changed, 771 insertions(+), 323 deletions(-) delete mode 100644 services/audiopolicy/engine/common/include/StreamVolumeCurves.h create mode 100644 services/audiopolicy/engine/common/include/VolumeGroup.h delete mode 100644 services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp create mode 100644 services/audiopolicy/engine/common/src/VolumeGroup.cpp create mode 100644 services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml diff --git a/media/libaudioclient/AudioAttributes.cpp b/media/libaudioclient/AudioAttributes.cpp index 0f327cf3b6..1ee6930fbc 100644 --- a/media/libaudioclient/AudioAttributes.cpp +++ b/media/libaudioclient/AudioAttributes.cpp @@ -43,7 +43,7 @@ status_t AudioAttributes::readFromParcel(const Parcel *parcel) strcpy(mAttributes.tags, ""); } mStreamType = static_cast(parcel->readInt32()); - mGroupId = parcel->readUint32(); + mGroupId = static_cast(parcel->readUint32()); return NO_ERROR; } @@ -60,7 +60,7 @@ status_t AudioAttributes::writeToParcel(Parcel *parcel) const parcel->writeUtf8AsUtf16(mAttributes.tags); } parcel->writeInt32(static_cast(mStreamType)); - parcel->writeUint32(mGroupId); + parcel->writeUint32(static_cast(mGroupId)); return NO_ERROR; } diff --git a/media/libaudioclient/include/media/AudioAttributes.h b/media/libaudioclient/include/media/AudioAttributes.h index edf26ebe7c..0a35e9e89d 100644 --- a/media/libaudioclient/include/media/AudioAttributes.h +++ b/media/libaudioclient/include/media/AudioAttributes.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -28,7 +29,7 @@ class AudioAttributes : public Parcelable public: AudioAttributes() = default; AudioAttributes(const audio_attributes_t &attributes) : mAttributes(attributes) {} - AudioAttributes(uint32_t groupId, + AudioAttributes(volume_group_t groupId, audio_stream_type_t stream, const audio_attributes_t &attributes) : mAttributes(attributes), mStreamType(stream), mGroupId(groupId) {} @@ -39,7 +40,7 @@ public: status_t writeToParcel(Parcel *parcel) const override; audio_stream_type_t getStreamType() const { return mStreamType; } - uint32_t getGroupId() const { return mGroupId; } + volume_group_t getGroupId() const { return mGroupId; } private: audio_attributes_t mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; @@ -53,7 +54,7 @@ private: * @brief mGroupId: for future volume management, define groups within a strategy that follows * the same curves of volume (extension of stream types to manage volume) */ - uint32_t mGroupId = 0; + volume_group_t mGroupId = VOLUME_GROUP_NONE; }; } // namespace android diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h index 5188da1fd3..8e446ea48c 100644 --- a/media/libaudioclient/include/media/AudioCommonTypes.h +++ b/media/libaudioclient/include/media/AudioCommonTypes.h @@ -27,7 +27,7 @@ enum product_strategy_t : uint32_t; const product_strategy_t PRODUCT_STRATEGY_NONE = static_cast(-1); using AttributesVector = std::vector; -using StreamTypes = std::vector; +using StreamTypeVector = std::vector; constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_t &rhs) { @@ -38,5 +38,9 @@ constexpr bool operator!=(const audio_attributes_t &lhs, const audio_attributes_ { return !(lhs==rhs); } + +enum volume_group_t : uint32_t; +static const volume_group_t VOLUME_GROUP_NONE = static_cast(-1); + } // namespace android diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index a3b6b36fd2..48b5271932 100644 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -21,6 +21,7 @@ #include namespace android { + /** * VolumeSource is the discriminent for volume management on an output. * It used to be the stream type by legacy, it may be host volume group or a volume curves if diff --git a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h index 93022fb2a0..d408446f62 100644 --- a/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h +++ b/services/audiopolicy/common/managerdefinitions/include/IVolumeCurves.h @@ -38,6 +38,8 @@ public: virtual float volIndexToDb(device_category device, int indexInUi) const = 0; virtual bool hasVolumeIndexForDevice(audio_devices_t device) const = 0; virtual status_t initVolume(int indexMin, int indexMax) = 0; + virtual std::vector getAttributes() const = 0; + virtual std::vector getStreamTypes() const = 0; virtual void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const = 0; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index e0b233d18c..ec7ff572da 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -341,7 +341,7 @@ sp HwModuleCollection::getDeviceDescriptor(const audio_devices } } if (!allowToCreate) { - ALOGE("%s: could not find HW module for device %s %04x address %s", __FUNCTION__, + ALOGV("%s: could not find HW module for device %s %04x address %s", __FUNCTION__, name, deviceType, address); return nullptr; } diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index bc027e2a3f..35d86ee665 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include namespace android { namespace audio_policy { @@ -68,9 +68,25 @@ public: status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) const override; - VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) override; + VolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) const override; - VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) override; + VolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) const override; + + IVolumeCurves *getVolumeCurvesForVolumeGroup(volume_group_t group) const override + { + return mVolumeGroups.find(group) != end(mVolumeGroups) ? + mVolumeGroups.at(group)->getVolumeCurves() : nullptr; + } + + VolumeGroupVector getVolumeGroups() const override; + + volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const override; + + volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const override; + + StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const override; + + AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const override; void dump(String8 *dst) const override; @@ -105,12 +121,11 @@ public: AudioPolicyManagerObserver *mApmObserver = nullptr; ProductStrategyMap mProductStrategies; + VolumeGroupMap mVolumeGroups; audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */ /** current forced use configuration. */ audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {}; - - StreamVolumeCurves mStreamVolumeCurves; }; } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h index 72505b29aa..767a8ed583 100644 --- a/services/audiopolicy/engine/common/include/ProductStrategy.h +++ b/services/audiopolicy/engine/common/include/ProductStrategy.h @@ -16,6 +16,8 @@ #pragma once +#include "VolumeGroup.h" + #include #include #include @@ -38,7 +40,7 @@ class ProductStrategy : public virtual RefBase, private HandleGenerator > { public: + /** + * @brief initialize: set default product strategy in cache. + */ + void initialize(); /** * @brief getProductStrategyForAttribute. The order of the vector is dimensionning. * @param attr @@ -136,9 +148,16 @@ public: std::string getDeviceAddressForProductStrategy(product_strategy_t strategy) const; + volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const; + + volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const; + product_strategy_t getDefault() const; void dump(String8 *dst, int spaces = 0) const; + +private: + product_strategy_t mDefaultStrategy = PRODUCT_STRATEGY_NONE; }; } // namespace android diff --git a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h b/services/audiopolicy/engine/common/include/StreamVolumeCurves.h deleted file mode 100644 index 5b0b7d6272..0000000000 --- a/services/audiopolicy/engine/common/include/StreamVolumeCurves.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include -#include - -namespace android { - -class StreamVolumeCurves -{ -public: - StreamVolumeCurves() = default; - - /** - * @brief switchVolumeCurve control API for Engine, allows to switch the volume curves - * from one stream type to another. - * @param src source stream type - * @param dst destination stream type - */ - status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) - { - if (!hasCurvesFor(streamSrc) || !hasCurvesFor(streamDst)) { - ALOGE("%s: No curves defined for streams %d %d", __FUNCTION__, streamSrc, streamDst); - return NO_INIT; - } - const VolumeCurves &sourceCurves = getCurvesFor(streamSrc); - VolumeCurves &dstCurves = editCurvesFor(streamDst); - return dstCurves.switchCurvesFrom(sourceCurves); - } - void dump(String8 *dst, int spaces = 0) const; - - void add(const VolumeCurves &curves, audio_stream_type_t streamType) - { - mCurves.emplace(streamType, curves); - } - - bool hasCurvesFor(audio_stream_type_t stream) - { - return mCurves.find(stream) != end(mCurves); - } - - VolumeCurves &editCurvesFor(audio_stream_type_t stream) - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves[stream]; - } - const VolumeCurves &getCurvesFor(audio_stream_type_t stream) const - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves.at(stream); - } - /** - * @brief getVolumeCurvesForStream - * @param stream type for which the volume curves interface is requested - * @return the VolumeCurves for a given stream type. - */ - VolumeCurves &getVolumeCurvesForStream(audio_stream_type_t stream) - { - ALOG_ASSERT(mCurves.find(stream) != end(mCurves), "Invalid stream type for Volume Curve"); - return mCurves[stream]; - } - /** - * @brief restoreOriginVolumeCurve helper control API for engine to restore the original volume - * curves for a given stream type - * @param stream for which the volume curves will be restored. - */ - status_t restoreOriginVolumeCurve(audio_stream_type_t stream) - { - if (!hasCurvesFor(stream)) { - ALOGE("%s: No curves defined for streams", __FUNCTION__); - return NO_INIT; - } - return switchVolumeCurve(stream, stream); - } - -private: - std::map mCurves; -}; - -} // namespace android diff --git a/services/audiopolicy/engine/common/include/VolumeCurve.h b/services/audiopolicy/engine/common/include/VolumeCurve.h index 0ec63e19fc..54314e3a03 100644 --- a/services/audiopolicy/engine/common/include/VolumeCurve.h +++ b/services/audiopolicy/engine/common/include/VolumeCurve.h @@ -18,7 +18,9 @@ #include "IVolumeCurves.h" #include +#include #include +#include #include #include #include @@ -71,18 +73,11 @@ class VolumeCurves : public KeyedVector >, { public: VolumeCurves(int indexMin = 0, int indexMax = 100) : - mIndexMin(indexMin), mIndexMax(indexMax), mStream(AUDIO_STREAM_DEFAULT) + mIndexMin(indexMin), mIndexMax(indexMax) { addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); } - VolumeCurves(audio_stream_type_t stream, int indexMin, int indexMax) : - mIndexMin(indexMin), mIndexMax(indexMax), mStream(stream) - { - addCurrentVolumeIndex(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, 0); - } - - // Once XML has been parsed, must be call first to sanity check table and initialize indexes - virtual status_t initVolume(int indexMin, int indexMax) + status_t initVolume(int indexMin, int indexMax) override { mIndexMin = indexMin; mIndexMax = indexMax; @@ -174,8 +169,16 @@ public: return 0.0f; } } - - audio_stream_type_t getStreamType() const { return mStream; } + void addAttributes(const audio_attributes_t &attr) + { + mAttributes.push_back(attr); + } + AttributesVector getAttributes() const override { return mAttributes; } + void addStreamType(audio_stream_type_t stream) + { + mStreams.push_back(stream); + } + StreamTypeVector getStreamTypes() const override { return mStreams; } void dump(String8 *dst, int spaces = 0, bool curvePoints = false) const override; @@ -186,7 +189,8 @@ private: int mIndexMax; /**< max volume index. */ const bool mCanBeMuted = true; /**< true is the stream can be muted. */ - const audio_stream_type_t mStream; /**< Keep it for legacy. */ + AttributesVector mAttributes; + StreamTypeVector mStreams; /**< Keep it for legacy. */ }; } // namespace android diff --git a/services/audiopolicy/engine/common/include/VolumeGroup.h b/services/audiopolicy/engine/common/include/VolumeGroup.h new file mode 100644 index 0000000000..c34b4062ff --- /dev/null +++ b/services/audiopolicy/engine/common/include/VolumeGroup.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class VolumeGroup : public virtual RefBase, private HandleGenerator +{ +public: + VolumeGroup(const std::string &name, int indexMin, int indexMax); + std::string getName() const { return mName; } + volume_group_t getId() const { return mId; } + + void add(const sp &curve); + + VolumeCurves *getVolumeCurves() { return &mGroupVolumeCurves; } + + void addSupportedAttributes(const audio_attributes_t &attr); + AttributesVector getSupportedAttributes() const { return mGroupVolumeCurves.getAttributes(); } + + void addSupportedStream(audio_stream_type_t stream); + StreamTypeVector getStreamTypes() const { return mGroupVolumeCurves.getStreamTypes(); } + + void dump(String8 *dst, int spaces = 0) const; + +private: + const std::string mName; + const volume_group_t mId; + VolumeCurves mGroupVolumeCurves; +}; + +class VolumeGroupMap : public std::map > +{ +public: + void dump(String8 *dst, int spaces = 0) const; +}; + +} // namespace android diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 6e2ab4cdfa..4d7c4a05bb 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -15,7 +15,7 @@ */ #define LOG_TAG "APM::AudioPolicyEngine/Base" -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #include "EngineBase.h" #include "EngineDefaultConfig.h" @@ -96,46 +96,47 @@ product_strategy_t EngineBase::getProductStrategyByName(const std::string &name) engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() { auto loadProductStrategies = - [](auto& strategyConfigs, auto& productStrategies) { - uint32_t groupid = 0; + [](auto& strategyConfigs, auto& productStrategies, auto& volumeGroups) { for (auto& strategyConfig : strategyConfigs) { sp strategy = new ProductStrategy(strategyConfig.name); for (const auto &group : strategyConfig.attributesGroups) { + const auto &iter = std::find_if(begin(volumeGroups), end(volumeGroups), + [&group](const auto &volumeGroup) { + return group.volumeGroup == volumeGroup.second->getName(); }); + ALOG_ASSERT(iter != end(volumeGroups), "Invalid Volume Group Name %s", + group.volumeGroup.c_str()); + if (group.stream != AUDIO_STREAM_DEFAULT) { + iter->second->addSupportedStream(group.stream); + } for (const auto &attr : group.attributesVect) { - strategy->addAttributes({group.stream, groupid, attr}); + strategy->addAttributes({group.stream, iter->second->getId(), attr}); + iter->second->addSupportedAttributes(attr); } - groupid += 1; } product_strategy_t strategyId = strategy->getId(); productStrategies[strategyId] = strategy; } }; - auto loadVolumeCurves = [](const auto &configVolumes, auto &streamVolumeCollection) { - for (auto &configVolume : configVolumes) { - audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; - if (configVolume.stream.empty() || - !StreamTypeConverter::fromString(configVolume.stream, streamType)) { - ALOGE("%s: Invalid stream type", __FUNCTION__); - continue; - } - VolumeCurves volumeCurves(streamType, configVolume.indexMin, configVolume.indexMax); - for (auto &configCurve : configVolume.volumeCurves) { - device_category deviceCategory = DEVICE_CATEGORY_SPEAKER; - if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, - deviceCategory)) { + auto loadVolumeGroups = [](auto &volumeConfigs, auto &volumeGroups) { + for (auto &volumeConfig : volumeConfigs) { + sp volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin, + volumeConfig.indexMax); + volumeGroups[volumeGroup->getId()] = volumeGroup; + + for (auto &configCurve : volumeConfig.volumeCurves) { + device_category deviceCat = DEVICE_CATEGORY_SPEAKER; + if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) { ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str()); continue; } - sp curve = new VolumeCurve(deviceCategory); + sp curve = new VolumeCurve(deviceCat); for (auto &point : configCurve.curvePoints) { curve->add({point.index, point.attenuationInMb}); } - volumeCurves.add(curve); + volumeGroup->add(curve); } - streamVolumeCollection.add(volumeCurves, streamType); } }; - auto result = engineConfig::parse(); if (result.parsedConfig == nullptr) { ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__); @@ -145,8 +146,10 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() static_cast(ret == NO_ERROR ? 0 : 1)}; } ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement); - loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies); - loadVolumeCurves(result.parsedConfig->volumeGroups, mStreamVolumeCurves); + loadVolumeGroups(result.parsedConfig->volumeGroups, mVolumeGroups); + loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies, + mVolumeGroups); + mProductStrategies.initialize(); return result; } @@ -204,30 +207,77 @@ status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &stra return NO_ERROR; } -VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) +VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) const { - return &mStreamVolumeCurves.getVolumeCurvesForStream(getStreamTypeForAttributes(attr)); + volume_group_t volGr = mProductStrategies.getVolumeGroupForAttributes(attr); + const auto &iter = mVolumeGroups.find(volGr); + LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s", toString(attr).c_str()); + return mVolumeGroups.at(volGr)->getVolumeCurves(); } -VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) +VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const { - return &mStreamVolumeCurves.getVolumeCurvesForStream(stream); + volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream); + const auto &iter = mVolumeGroups.find(volGr); + LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s", + toString(stream).c_str()); + return mVolumeGroups.at(volGr)->getVolumeCurves(); } status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst) { - return mStreamVolumeCurves.switchVolumeCurve(streamSrc, streamDst);; + auto srcCurves = getVolumeCurvesForStreamType(streamSrc); + auto dstCurves = getVolumeCurvesForStreamType(streamDst); + + if (srcCurves == nullptr || dstCurves == nullptr) { + return BAD_VALUE; + } + return dstCurves->switchCurvesFrom(*srcCurves); } status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream) { - return mStreamVolumeCurves.restoreOriginVolumeCurve(stream); + VolumeCurves *curves = getVolumeCurvesForStreamType(stream); + return curves != nullptr ? curves->switchCurvesFrom(*curves) : BAD_VALUE; +} + +VolumeGroupVector EngineBase::getVolumeGroups() const +{ + VolumeGroupVector group; + for (const auto &iter : mVolumeGroups) { + group.push_back(iter.first); + } + return group; +} + +volume_group_t EngineBase::getVolumeGroupForAttributes(const audio_attributes_t &attr) const +{ + return mProductStrategies.getVolumeGroupForAttributes(attr); +} + +volume_group_t EngineBase::getVolumeGroupForStreamType(audio_stream_type_t stream) const +{ + return mProductStrategies.getVolumeGroupForStreamType(stream); +} + +StreamTypeVector EngineBase::getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const +{ + // @TODO default music stream to control volume if no group? + return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ? + mVolumeGroups.at(volumeGroup)->getStreamTypes() : + StreamTypeVector(AUDIO_STREAM_MUSIC); +} + +AttributesVector EngineBase::getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const +{ + return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ? + mVolumeGroups.at(volumeGroup)->getSupportedAttributes() : AttributesVector(); } void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); - mStreamVolumeCurves.dump(dst, 2); + mVolumeGroups.dump(dst, 2); } } // namespace audio_policy diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h index f1642c5e3c..fede0d93de 100644 --- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h +++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h @@ -25,11 +25,11 @@ namespace android { const engineConfig::ProductStrategies gOrderedStrategies = { {"STRATEGY_PHONE", { - {"phone", AUDIO_STREAM_VOICE_CALL, + {"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT, 0, ""}}, }, - {"sco", AUDIO_STREAM_BLUETOOTH_SCO, + {"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO, ""}}, } @@ -37,18 +37,18 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_SONIFICATION", { - {"ring", AUDIO_STREAM_RING, + {"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE, AUDIO_SOURCE_DEFAULT, 0, ""}} }, - {"alarm", AUDIO_STREAM_ALARM, + {"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, 0, ""}}, } }, }, {"STRATEGY_ENFORCED_AUDIBLE", { - {"", AUDIO_STREAM_ENFORCED_AUDIBLE, + {"", AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}} } @@ -56,7 +56,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_ACCESSIBILITY", { - {"", AUDIO_STREAM_ACCESSIBILITY, + {"", AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, AUDIO_SOURCE_DEFAULT, 0, ""}} } @@ -64,7 +64,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_SONIFICATION_RESPECTFUL", { - {"", AUDIO_STREAM_NOTIFICATION, + {"", AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT, 0, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST, @@ -81,7 +81,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_MEDIA", { - {"music", AUDIO_STREAM_MUSIC, + {"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, 0, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_GAME, AUDIO_SOURCE_DEFAULT, 0, ""}, @@ -91,7 +91,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""} }, }, - {"system", AUDIO_STREAM_SYSTEM, + {"system", AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION, AUDIO_SOURCE_DEFAULT, 0, ""}} } @@ -99,7 +99,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_DTMF", { - {"", AUDIO_STREAM_DTMF, + {"", AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, AUDIO_SOURCE_DEFAULT, 0, ""} @@ -109,7 +109,7 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER", { - {"", AUDIO_STREAM_TTS, + {"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_BEACON, ""}} } @@ -117,14 +117,14 @@ const engineConfig::ProductStrategies gOrderedStrategies = { }, {"STRATEGY_REROUTING", { - {"", AUDIO_STREAM_REROUTING, + {"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}} } }, }, {"STRATEGY_PATCH", { - {"", AUDIO_STREAM_PATCH, + {"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}} } }, diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp index 71607d1be4..16e6690a58 100644 --- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp +++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp @@ -44,7 +44,7 @@ std::vector ProductStrategy::listAudioAttributes() con { std::vector androidAa; for (const auto &attr : mAttributesVector) { - androidAa.push_back({attr.mGroupId, attr.mStream, attr.mAttributes}); + androidAa.push_back({attr.mVolumeGroup, attr.mStream, attr.mAttributes}); } return androidAa; } @@ -69,7 +69,8 @@ bool ProductStrategy::matches(const audio_attributes_t attr) const }) != end(mAttributesVector); } -audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(const audio_attributes_t &attr) const +audio_stream_type_t ProductStrategy::getStreamTypeForAttributes( + const audio_attributes_t &attr) const { const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector), [&attr](const auto &supportedAttr) { @@ -110,6 +111,33 @@ bool ProductStrategy::supportStreamType(const audio_stream_type_t &streamType) c return supportedAttr.mStream == streamType; }) != end(mAttributesVector); } +volume_group_t ProductStrategy::getVolumeGroupForAttributes(const audio_attributes_t &attr) const +{ + for (const auto &supportedAttr : mAttributesVector) { + if (AudioProductStrategy::attributesMatches(supportedAttr.mAttributes, attr)) { + return supportedAttr.mVolumeGroup; + } + } + return VOLUME_GROUP_NONE; +} + +volume_group_t ProductStrategy::getVolumeGroupForStreamType(audio_stream_type_t stream) const +{ + for (const auto &supportedAttr : mAttributesVector) { + if (supportedAttr.mStream == stream) { + return supportedAttr.mVolumeGroup; + } + } + return VOLUME_GROUP_NONE; +} + +volume_group_t ProductStrategy::getDefaultVolumeGroup() const +{ + const auto &iter = std::find_if(begin(mAttributesVector), end(mAttributesVector), + [](const auto &attr) {return attr.mAttributes == defaultAttr;}); + return iter != end(mAttributesVector) ? iter->mVolumeGroup : VOLUME_GROUP_NONE; +} + void ProductStrategy::dump(String8 *dst, int spaces) const { dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId); @@ -121,7 +149,7 @@ void ProductStrategy::dump(String8 *dst, int spaces) const deviceLiteral.c_str(), mDeviceAddress.c_str()); for (const auto &attr : mAttributesVector) { - dst->appendFormat("%*sGroup: %d stream: %s\n", spaces + 3, "", attr.mGroupId, + dst->appendFormat("%*sGroup: %d stream: %s\n", spaces + 3, "", attr.mVolumeGroup, android::toString(attr.mStream).c_str()); dst->appendFormat("%*s Attributes: ", spaces + 3, ""); std::string attStr = @@ -172,6 +200,9 @@ audio_stream_type_t ProductStrategyMap::getStreamTypeForAttributes( product_strategy_t ProductStrategyMap::getDefault() const { + if (mDefaultStrategy != PRODUCT_STRATEGY_NONE) { + return mDefaultStrategy; + } for (const auto &iter : *this) { if (iter.second->isDefault()) { ALOGV("%s: using default %s", __FUNCTION__, iter.second->getName().c_str()); @@ -231,6 +262,42 @@ std::string ProductStrategyMap::getDeviceAddressForProductStrategy(product_strat return at(psId)->getDeviceAddress(); } +volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(const audio_attributes_t &attr) const +{ + for (const auto &iter : *this) { + volume_group_t group = iter.second->getVolumeGroupForAttributes(attr); + if (group != VOLUME_GROUP_NONE) { + return group; + } + } + product_strategy_t defaultStrategy = getDefault(); + if (defaultStrategy == PRODUCT_STRATEGY_NONE) { + return VOLUME_GROUP_NONE; + } + return at(defaultStrategy)->getDefaultVolumeGroup(); +} + +volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const +{ + for (const auto &iter : *this) { + volume_group_t group = iter.second->getVolumeGroupForStreamType(stream); + if (group != VOLUME_GROUP_NONE) { + return group; + } + } + product_strategy_t defaultStrategy = getDefault(); + if (defaultStrategy == PRODUCT_STRATEGY_NONE) { + return VOLUME_GROUP_NONE; + } + return at(defaultStrategy)->getDefaultVolumeGroup(); +} + +void ProductStrategyMap::initialize() +{ + mDefaultStrategy = getDefault(); + ALOG_ASSERT(mDefaultStrategy != PRODUCT_STRATEGY_NONE, "No default product strategy found"); +} + void ProductStrategyMap::dump(String8 *dst, int spaces) const { dst->appendFormat("%*sProduct Strategies dump:", spaces, ""); diff --git a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp b/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp deleted file mode 100644 index fe3b0007b6..0000000000 --- a/services/audiopolicy/engine/common/src/StreamVolumeCurves.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 "APM::Engine::StreamVolumeCurves" -//#define LOG_NDEBUG 0 - -#include "StreamVolumeCurves.h" -#include - -namespace android { - -void StreamVolumeCurves::dump(String8 *dst, int spaces) const -{ - if (mCurves.empty()) { - return; - } - dst->appendFormat("\n%*sStreams dump:\n", spaces, ""); - dst->appendFormat( - "%*sStream Can be muted Index Min Index Max Index Cur [device : index]...\n", spaces + 2, ""); - for (const auto &streamCurve : mCurves) { - streamCurve.second.dump(dst, spaces + 2, false); - } - dst->appendFormat("\n%*sVolume Curves for Use Cases (aka Stream types) dump:\n", spaces, ""); - for (const auto &streamCurve : mCurves) { - std::string streamTypeLiteral; - StreamTypeConverter::toString(streamCurve.first, streamTypeLiteral); - dst->appendFormat( - " %s (%02d): Curve points for device category (index, attenuation in millibel)\n", - streamTypeLiteral.c_str(), streamCurve.first); - streamCurve.second.dump(dst, spaces + 2, true); - } -} - -} // namespace android diff --git a/services/audiopolicy/engine/common/src/VolumeCurve.cpp b/services/audiopolicy/engine/common/src/VolumeCurve.cpp index be2ca730ef..c352578d5c 100644 --- a/services/audiopolicy/engine/common/src/VolumeCurve.cpp +++ b/services/audiopolicy/engine/common/src/VolumeCurve.cpp @@ -19,13 +19,17 @@ #include "VolumeCurve.h" #include "TypeConverter.h" +#include namespace android { float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const { ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve"); - + if (volIndexMin < 0 || volIndexMax < 0) { + // In order to let AudioService initialize the min and max, convention is to use -1 + return NAN; + } if (indexInUi < volIndexMin) { // an index of 0 means mute request when volIndexMin > 0 if (indexInUi == 0) { @@ -80,8 +84,8 @@ void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const } dst->append(" {"); for (size_t i = 0; i < mCurvePoints.size(); i++) { - dst->appendFormat("%*s (%3d, %5d)", spaces, "", - mCurvePoints[i].mIndex, mCurvePoints[i].mAttenuationInMb); + dst->appendFormat("%*s(%3d, %5d)", spaces, "", mCurvePoints[i].mIndex, + mCurvePoints[i].mAttenuationInMb); dst->appendFormat(i == (mCurvePoints.size() - 1) ? " }\n" : ", "); } } @@ -89,19 +93,36 @@ void VolumeCurve::dump(String8 *dst, int spaces, bool curvePoints) const void VolumeCurves::dump(String8 *dst, int spaces, bool curvePoints) const { if (!curvePoints) { - dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "", - mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); +// dst->appendFormat("%*s%02d %s %03d %03d ", spaces, "", +// mStream, mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + dst->appendFormat("%*s Can be muted Index Min Index Max Index Cur [device : index]...\n", + spaces + 1, ""); + dst->appendFormat("%*s %s %02d %02d ", spaces + 1, "", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); for (const auto &pair : mIndexCur) { - dst->appendFormat("%*s %04x : %02d, ", spaces, "", pair.first, pair.second); + dst->appendFormat("%04x : %02d, ", pair.first, pair.second); } dst->appendFormat("\n"); return; } + std::string streamNames; + for (const auto &stream : mStreams) { + streamNames += android::toString(stream) + "("+std::to_string(stream)+") "; + } + dst->appendFormat("%*sVolume Curves Streams/Attributes, Curve points Streams for device" + " category (index, attenuation in millibel)\n", spaces, ""); + dst->appendFormat("%*s Streams: %s \n", spaces, "", streamNames.c_str()); + if (!mAttributes.empty()) dst->appendFormat("%*s Attributes:", spaces, ""); + for (const auto &attributes : mAttributes) { + std::string attStr = attributes == defaultAttr ? "{ Any }" : android::toString(attributes); + dst->appendFormat("%*s %s\n", attributes == mAttributes.front() ? 0 : spaces + 13, "", + attStr.c_str()); + } for (size_t i = 0; i < size(); i++) { std::string deviceCatLiteral; DeviceCategoryConverter::toString(keyAt(i), deviceCatLiteral); dst->appendFormat("%*s %s :", spaces, "", deviceCatLiteral.c_str()); - valueAt(i)->dump(dst, 2, true); + valueAt(i)->dump(dst, 1, true); } } diff --git a/services/audiopolicy/engine/common/src/VolumeGroup.cpp b/services/audiopolicy/engine/common/src/VolumeGroup.cpp new file mode 100644 index 0000000000..e1898070cb --- /dev/null +++ b/services/audiopolicy/engine/common/src/VolumeGroup.cpp @@ -0,0 +1,75 @@ +/* + * 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 "APM::AudioPolicyEngine/VolumeGroup" +//#define LOG_NDEBUG 0 + +#include "VolumeGroup.h" +#include +#include +#include +#include + +#include + + +namespace android { + +// +// VolumeGroup implementation +// +VolumeGroup::VolumeGroup(const std::string &name, int indexMin, int indexMax) : + mName(name), mId(static_cast(HandleGenerator::getNextHandle())), + mGroupVolumeCurves(VolumeCurves(indexMin, indexMax)) +{ +} + +void VolumeGroup::dump(String8 *dst, int spaces) const +{ + dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId); + mGroupVolumeCurves.dump(dst, spaces + 2, true); + mGroupVolumeCurves.dump(dst, spaces + 2, false); + dst->appendFormat("\n"); +} + +void VolumeGroup::add(const sp &curve) +{ + mGroupVolumeCurves.add(curve); +} + +void VolumeGroup::addSupportedAttributes(const audio_attributes_t &attr) +{ + mGroupVolumeCurves.addAttributes(attr); +} + +void VolumeGroup::addSupportedStream(audio_stream_type_t stream) +{ + mGroupVolumeCurves.addStreamType(stream); +} + +// +// VolumeGroupMap implementation +// +void VolumeGroupMap::dump(String8 *dst, int spaces) const +{ + dst->appendFormat("\n%*sVolume Groups dump:", spaces, ""); + for (const auto &iter : *this) { + iter.second->dump(dst, spaces + 2); + } +} + +} // namespace android + diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h index a188115fff..7f5ed5eb9d 100644 --- a/services/audiopolicy/engine/config/include/EngineConfig.h +++ b/services/audiopolicy/engine/config/include/EngineConfig.h @@ -40,6 +40,7 @@ using StreamVector = std::vector; struct AttributesGroup { std::string name; audio_stream_type_t stream; + std::string volumeGroup; AttributesVector attributesVect; }; @@ -59,7 +60,6 @@ using VolumeCurves = std::vector; struct VolumeGroup { std::string name; - std::string stream; int indexMin; int indexMax; VolumeCurves volumeCurves; diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp index 00fbac411b..1ad773939a 100644 --- a/services/audiopolicy/engine/config/src/EngineConfig.cpp +++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp @@ -59,6 +59,7 @@ struct AttributesGroupTraits : public BaseSerializerTraits { struct Attributes { static constexpr const char *deviceCategory = "deviceCategory"; + static constexpr const char *stream = "stream"; // For legacy volume curves static constexpr const char *reference = "ref"; /**< For volume curves factorization. */ }; @@ -139,8 +141,6 @@ struct VolumeGroupTraits : public BaseSerializerTraits; -using xmlCharUnique = std::unique_ptr; - std::string getXmlAttribute(const xmlNode *cur, const char *attribute) { xmlCharUnique charPtr(xmlGetProp(cur, reinterpret_cast(attribute)), xmlFree); @@ -304,6 +304,12 @@ status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, } ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str()); + std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup); + if (volumeGroup.empty()) { + ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup); + } + ALOGV("%s: %s = %s", __FUNCTION__, Attributes::volumeGroup, volumeGroup.c_str()); + audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; std::string streamTypeXml = getXmlAttribute(child, Attributes::streamType); if (streamTypeXml.empty()) { @@ -318,7 +324,7 @@ status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, AttributesVector attributesVect; deserializeAttributesCollection(doc, child, attributesVect); - attributesGroup.push_back({name, streamType, attributesVect}); + attributesGroup.push_back({name, streamType, volumeGroup, attributesVect}); return NO_ERROR; } @@ -420,7 +426,6 @@ status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collectio if (deviceCategory.empty()) { ALOGW("%s: No %s found", __FUNCTION__, Attributes::deviceCategory); } - std::string referenceName = getXmlAttribute(root, Attributes::reference); const _xmlNode *ref = NULL; if (!referenceName.empty()) { @@ -458,9 +463,10 @@ status_t VolumeTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collectio status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &volumes) { std::string name; - std::string stream = {}; int indexMin = 0; int indexMax = 0; + StreamVector streams = {}; + AttributesVector attributesVect = {}; for (const xmlNode *child = root->xmlChildrenNode; child != NULL; child = child->next) { if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::name)) { @@ -470,13 +476,6 @@ status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Coll } name = reinterpret_cast(nameXml.get()); } - if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::stream)) { - xmlCharUnique streamXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); - if (streamXml == nullptr) { - return BAD_VALUE; - } - stream = reinterpret_cast(streamXml.get()); - } if (not xmlStrcmp(child->name, (const xmlChar *)Attributes::indexMin)) { xmlCharUnique indexMinXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree); if (indexMinXml == nullptr) { @@ -498,13 +497,23 @@ status_t VolumeGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, Coll } } } - ALOGV("%s: group=%s stream=%s indexMin=%d, indexMax=%d", - __func__, name.c_str(), stream.c_str(), indexMin, indexMax); + deserializeAttributesCollection(doc, root, attributesVect); + + std::string streamNames; + for (const auto &stream : streams) { + streamNames += android::toString(stream) + " "; + } + std::string attrmNames; + for (const auto &attr : attributesVect) { + attrmNames += android::toString(attr) + "\n"; + } + ALOGV("%s: group=%s indexMin=%d, indexMax=%d streams=%s attributes=%s", + __func__, name.c_str(), indexMin, indexMax, streamNames.c_str(), attrmNames.c_str( )); VolumeCurves groupVolumeCurves; size_t skipped = 0; deserializeCollection(doc, root, groupVolumeCurves, skipped); - volumes.push_back({ name, stream, indexMin, indexMax, groupVolumeCurves }); + volumes.push_back({ name, indexMin, indexMax, groupVolumeCurves }); return NO_ERROR; } @@ -580,8 +589,16 @@ static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode * } } for (const auto &volumeMapIter : legacyVolumeMap) { - volumeGroups.push_back({ volumeMapIter.first, volumeMapIter.first, 0, 100, - volumeMapIter.second }); + // In order to let AudioService setting the min and max (compatibility), set Min and Max + // to -1 except for private streams + audio_stream_type_t streamType; + if (!StreamTypeConverter::fromString(volumeMapIter.first, streamType)) { + ALOGE("%s: Invalid stream %s", __func__, volumeMapIter.first.c_str()); + return BAD_VALUE; + } + int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1; + int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1; + volumeGroups.push_back({ volumeMapIter.first, indexMin, indexMax, volumeMapIter.second }); } return NO_ERROR; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index c9e9507705..07acd2e5a7 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -32,7 +32,7 @@ namespace android { using DeviceStrategyMap = std::map; using StrategyVector = std::vector; - +using VolumeGroupVector = std::vector; /** * This interface is dedicated to the policy manager that a Policy Engine shall implement. @@ -182,6 +182,7 @@ public: /** * @brief getAttributesForStream get the audio attributes from legacy stream type + * Attributes returned might only be used to check upon routing decision, not volume decisions. * @param stream to consider * @return audio attributes matching the legacy stream type */ @@ -241,14 +242,49 @@ public: * @param attr to be considered * @return IVolumeCurves interface pointer if found, nullptr otherwise */ - virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) = 0; + virtual IVolumeCurves *getVolumeCurvesForAttributes(const audio_attributes_t &attr) const = 0; /** * @brief getVolumeCurvesForStreamType retrieves the Volume Curves interface for the stream * @param stream to be considered * @return IVolumeCurves interface pointer if found, nullptr otherwise */ - virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) = 0; + virtual IVolumeCurves *getVolumeCurvesForStreamType(audio_stream_type_t stream) const = 0; + + /** + * @brief getVolumeCurvesForVolumeGroup retrieves the Volume Curves interface for volume group + * @param group to be considered + * @return IVolumeCurves interface pointer if found, nullptr otherwise + */ + virtual IVolumeCurves *getVolumeCurvesForVolumeGroup(volume_group_t group) const = 0; + + /** + * @brief getVolumeGroups retrieves the collection of volume groups. + * @return vector of volume groups + */ + virtual VolumeGroupVector getVolumeGroups() const = 0; + + /** + * @brief getVolumeGroupForAttributes gets the appropriate volume group to be used for a given + * Audio Attributes. + * @param attr to be considered + * @return volume group associated to the given audio attributes, default group if none + * applicable, VOLUME_GROUP_NONE if no default group defined. + */ + virtual volume_group_t getVolumeGroupForAttributes(const audio_attributes_t &attr) const = 0; + + /** + * @brief getVolumeGroupForStreamType gets the appropriate volume group to be used for a given + * legacy stream type + * @param stream type to be considered + * @return volume group associated to the given stream type, default group if none applicable, + * VOLUME_GROUP_NONE if no default group defined. + */ + virtual volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const = 0; + + virtual StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const = 0; + + virtual AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const = 0; virtual void dump(String8 *dst) const = 0; diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index 2b7e4c8dbc..4eff6e64d6 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -13,7 +13,7 @@ LOCAL_SRC_FILES := \ src/Stream.cpp \ src/InputSource.cpp \ ../engine/common/src/VolumeCurve.cpp \ - ../engine/common/src/StreamVolumeCurves.cpp \ + ../engine/common/src/VolumeGroup.cpp \ ../engine/common/src/ProductStrategy.cpp \ ../engine/common/src/EngineBase.cpp diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index ef476f765c..45419f04b1 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -72,7 +72,8 @@ LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) LOCAL_REQUIRED_MODULES := \ audio_policy_engine_product_strategies_automotive.xml \ audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml + audio_policy_engine_criterion_types.xml \ + audio_policy_engine_volumes.xml include $(BUILD_PREBUILT) @@ -89,6 +90,14 @@ LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml index e2fb02b140..28a140a957 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_configuration.xml @@ -19,6 +19,7 @@ + diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml index 543a2f0971..c487da9e24 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml @@ -31,7 +31,7 @@ limitations under the License. --> - + @@ -39,14 +39,14 @@ limitations under the License. - + - + @@ -70,21 +70,21 @@ limitations under the License. ( type == CAR_AUDIO_TYPE_RADIO ) ) --> - + - + - + @@ -96,7 +96,7 @@ limitations under the License. - + @@ -112,7 +112,7 @@ limitations under the License. - + @@ -121,29 +121,31 @@ limitations under the License. - + - + + + - + - + - + @@ -152,10 +154,17 @@ limitations under the License. - + - + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml new file mode 100644 index 0000000000..b326b50de3 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml @@ -0,0 +1,192 @@ + + + + + + + + + oem_traffic_anouncement + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + oem_adas_2 + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + oem_adas_3 + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + + + media_car_audio_type_3 + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + media_car_audio_type_7 + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + media + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + + speech + 1 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + system + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + phone + 1 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + ring + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + tts + 0 + 15 + + 0,-0 + 100,0 + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml index f72e37916d..939874353a 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_product_strategies.xml @@ -25,37 +25,37 @@ enforced. --> - + - + - + - + - + - + - + @@ -65,20 +65,20 @@ - + - + - + @@ -86,21 +86,21 @@ - + - + - + diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml index 73bde1fb7e..707a18471c 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -16,7 +16,7 @@ - + - + - + - + - + - + - + @@ -65,20 +65,20 @@ - + - + - + @@ -86,21 +86,21 @@ - + - + - + diff --git a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml index 73bde1fb7e..707a18471c 100644 --- a/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml +++ b/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml @@ -16,7 +16,7 @@ + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index b4cc1d3936..b28381b32d 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -1,5 +1,5 @@ - - + + @@ -182,8 +182,8 @@ - - + + diff --git a/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml b/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml new file mode 100644 index 0000000000..b4cc1d3936 --- /dev/null +++ b/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + Speaker + Built-In Mic + Built-In Back Mic + + Speaker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml b/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml new file mode 100644 index 0000000000..ce78eb0031 --- /dev/null +++ b/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b563a042b2..dc5b801be4 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -33,8 +33,8 @@ #define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml" #define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \ "audio_policy_configuration_a2dp_offload_disabled.xml" -#define AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME \ - "audio_policy_configuration_bluetooth_hal_enabled.xml" +#define AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME \ + "audio_policy_configuration_bluetooth_legacy_hal.xml" #include #include @@ -4080,17 +4080,17 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { status_t ret; if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) { - if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) && + property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses + // the legacy hardware module for A2DP and hearing aid. + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); + } else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // A2DP offload supported but disabled: try to use special XML file fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME); - } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { - // This property persist.bluetooth.bluetooth_audio_hal.enabled is temporary only. - // xml files AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME, although having - // the same name, must be different in offload and non offload cases in device - // specific configuration file. - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); - } - } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); + } + } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) { + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); } fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); -- GitLab From 8ec41c1ecaf3c29df14276f45e06cc95e54a4f67 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Thu, 21 Feb 2019 20:17:22 -0800 Subject: [PATCH 1026/1530] Allow VNDK 'users' which are not foreground to connect to camera devices. Bug: 125464062 Test: GCA (sanity) Test: Connect to a camera device using a HAL process, change the device user and connect again; connection is successful Change-Id: Ia25b961baa396fd383d089e400c6d877b9875955 Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/CameraService.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index e06897f492..e00539815e 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -972,8 +972,9 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId, userid_t clientUserId = multiuser_get_user_id(clientUid); // Only allow clients who are being used by the current foreground device user, unless calling - // from our own process. - if (callingPid != getpid() && (mAllowedUsers.find(clientUserId) == mAllowedUsers.end())) { + // from our own process OR the caller is using the cameraserver's HIDL interface. + if (!hardware::IPCThreadState::self()->isServingCall() && callingPid != getpid() && + (mAllowedUsers.find(clientUserId) == mAllowedUsers.end())) { ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from " "device user %d, currently allowed device users: %s)", callingPid, clientUserId, toString(mAllowedUsers).string()); -- GitLab From 4b2018b0c0b65b1f6e20e571bbaf0e8984dccc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 7 Nov 2018 11:18:59 +0100 Subject: [PATCH 1027/1530] audiopolicy: add Volume Group introspection APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib02900bde69ee24685d9d374c0e5f73d33fa59e7 Signed-off-by: François Gaffie Test: make Bug: 124767636 --- include/media/AudioVolumeGroup.h | 1 + media/libaudioclient/Android.bp | 1 + media/libaudioclient/AudioSystem.cpp | 32 +++- media/libaudioclient/AudioVolumeGroup.cpp | 84 ++++++++++ media/libaudioclient/IAudioPolicyService.cpp | 149 +++++++++++++++--- .../include/media/AudioSystem.h | 9 +- .../include/media/AudioVolumeGroup.h | 54 +++++++ .../include/media/IAudioPolicyService.h | 7 +- services/audiopolicy/AudioPolicyInterface.h | 8 +- .../engine/common/include/EngineBase.h | 2 + .../engine/common/src/EngineBase.cpp | 9 ++ .../interface/AudioPolicyManagerInterface.h | 10 ++ .../managerdefault/AudioPolicyManager.h | 18 ++- .../service/AudioPolicyInterfaceImpl.cpp | 27 +++- .../audiopolicy/service/AudioPolicyService.h | 8 +- 15 files changed, 383 insertions(+), 36 deletions(-) create mode 120000 include/media/AudioVolumeGroup.h create mode 100644 media/libaudioclient/AudioVolumeGroup.cpp create mode 100644 media/libaudioclient/include/media/AudioVolumeGroup.h diff --git a/include/media/AudioVolumeGroup.h b/include/media/AudioVolumeGroup.h new file mode 120000 index 0000000000..d6f1c99389 --- /dev/null +++ b/include/media/AudioVolumeGroup.h @@ -0,0 +1 @@ +../../media/libaudioclient/include/media/AudioVolumeGroup.h \ No newline at end of file diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index b111b78035..e9b6fb19a1 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -10,6 +10,7 @@ cc_library_shared { "AudioAttributes.cpp", "AudioPolicy.cpp", "AudioProductStrategy.cpp", + "AudioVolumeGroup.cpp", ], shared_libs: [ "libaudioutils", diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 01d9b3d158..35adb725fd 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1403,12 +1403,16 @@ audio_attributes_t AudioSystem::streamTypeToAttributes(audio_stream_type_t strea audio_stream_type_t AudioSystem::attributesToStreamType(const audio_attributes_t &attr) { - product_strategy_t strategyId = - AudioSystem::getProductStrategyFromAudioAttributes(AudioAttributes(attr)); + product_strategy_t psId; + status_t ret = AudioSystem::getProductStrategyFromAudioAttributes(AudioAttributes(attr), psId); + if (ret != NO_ERROR) { + ALOGE("no strategy found for attributes %s", toString(attr).c_str()); + return AUDIO_STREAM_MUSIC; + } AudioProductStrategyVector strategies; listAudioProductStrategies(strategies); for (const auto &strategy : strategies) { - if (strategy.getId() == strategyId) { + if (strategy.getId() == psId) { auto attrVect = strategy.getAudioAttributes(); auto iter = std::find_if(begin(attrVect), end(attrVect), [&attr](const auto &refAttr) { return AudioProductStrategy::attributesMatches( @@ -1422,11 +1426,27 @@ audio_stream_type_t AudioSystem::attributesToStreamType(const audio_attributes_t return AUDIO_STREAM_MUSIC; } -product_strategy_t AudioSystem::getProductStrategyFromAudioAttributes(const AudioAttributes &aa) +status_t AudioSystem::getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy) { const sp& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return PRODUCT_STRATEGY_NONE; - return aps->getProductStrategyFromAudioAttributes(aa); + if (aps == 0) return PERMISSION_DENIED; + return aps->getProductStrategyFromAudioAttributes(aa,productStrategy); +} + +status_t AudioSystem::listAudioVolumeGroups(AudioVolumeGroupVector &groups) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->listAudioVolumeGroups(groups); +} + +status_t AudioSystem::getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getVolumeGroupFromAudioAttributes(aa, volumeGroup); } // --------------------------------------------------------------------------- diff --git a/media/libaudioclient/AudioVolumeGroup.cpp b/media/libaudioclient/AudioVolumeGroup.cpp new file mode 100644 index 0000000000..e79a362f9f --- /dev/null +++ b/media/libaudioclient/AudioVolumeGroup.cpp @@ -0,0 +1,84 @@ +/* + * 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 "AudioVolumeGroup" + +//#define LOG_NDEBUG 0 + +#include +#include + +#include +#include + +namespace android { + +status_t AudioVolumeGroup::readFromParcel(const Parcel *parcel) +{ + status_t ret = parcel->readUtf8FromUtf16(&mName); + if (ret != NO_ERROR) { + return ret; + } + mGroupId = static_cast(parcel->readInt32()); + size_t size = static_cast(parcel->readInt32()); + for (size_t i = 0; i < size; i++) { + AudioAttributes attribute; + attribute.readFromParcel(parcel); + if (ret != NO_ERROR) { + mAudioAttributes.clear(); + return ret; + } + mAudioAttributes.push_back(attribute.getAttributes()); + } + size = static_cast(parcel->readInt32()); + for (size_t i = 0; i < size; i++) { + audio_stream_type_t stream = static_cast(parcel->readInt32()); + mStreams.push_back(stream); + } + return NO_ERROR; +} + +status_t AudioVolumeGroup::writeToParcel(Parcel *parcel) const +{ + parcel->writeUtf8AsUtf16(mName); + parcel->writeInt32(static_cast(mGroupId)); + size_t size = mAudioAttributes.size(); + size_t sizePosition = parcel->dataPosition(); + parcel->writeInt32(size); + size_t finalSize = size; + for (const auto &attributes : mAudioAttributes) { + size_t position = parcel->dataPosition(); + AudioAttributes attribute(attributes); + status_t ret = attribute.writeToParcel(parcel); + if (ret != NO_ERROR) { + parcel->setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = parcel->dataPosition(); + parcel->setDataPosition(sizePosition); + parcel->writeInt32(finalSize); + parcel->setDataPosition(position); + } + parcel->writeInt32(mStreams.size()); + for (const auto &stream : mStreams) { + parcel->writeInt32(static_cast(stream)); + } + return NO_ERROR; +} + +} // namespace android diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 1bce16f9b6..feb131764e 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -95,6 +95,8 @@ enum { GET_OFFLOAD_FORMATS_A2DP, LIST_AUDIO_PRODUCT_STRATEGIES, GET_STRATEGY_FOR_ATTRIBUTES, + LIST_AUDIO_VOLUME_GROUPS, + GET_VOLUME_GROUP_FOR_ATTRIBUTES }; #define MAX_ITEMS_PER_LIST 1024 @@ -1085,35 +1087,90 @@ public: return status; } status = static_cast(reply.readInt32()); - if (status == NO_ERROR) { - uint32_t numStrategies = static_cast(reply.readInt32()); - for (size_t i = 0; i < numStrategies; i++) { - AudioProductStrategy strategy; - status = strategy.readFromParcel(&reply); - if (status != NO_ERROR) { - ALOGE("%s: failed to read strategies", __FUNCTION__); - strategies.clear(); - return status; - } - strategies.push_back(strategy); + if (status != NO_ERROR) { + return status; + } + uint32_t numStrategies = static_cast(reply.readInt32()); + for (size_t i = 0; i < numStrategies; i++) { + AudioProductStrategy strategy; + status = strategy.readFromParcel(&reply); + if (status != NO_ERROR) { + ALOGE("%s: failed to read strategies", __FUNCTION__); + strategies.clear(); + return status; } + strategies.push_back(strategy); } - return status; + return NO_ERROR; } - virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) + virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); status_t status = aa.writeToParcel(&data); if (status != NO_ERROR) { - return PRODUCT_STRATEGY_NONE; + return status; } status = remote()->transact(GET_STRATEGY_FOR_ATTRIBUTES, data, &reply); - if (status == NO_ERROR) { - return static_cast(reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + status = static_cast(reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + productStrategy = static_cast(reply.readInt32()); + return NO_ERROR; + } + + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + + status_t status = remote()->transact(LIST_AUDIO_VOLUME_GROUPS, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast(reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + uint32_t numGroups = static_cast(reply.readInt32()); + for (size_t i = 0; i < numGroups; i++) { + AudioVolumeGroup group; + status = group.readFromParcel(&reply); + if (status != NO_ERROR) { + ALOGE("%s: failed to read volume groups", __FUNCTION__); + groups.clear(); + return status; + } + groups.push_back(group); + } + return NO_ERROR; + } + + virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + status_t status = aa.writeToParcel(&data); + if (status != NO_ERROR) { + return status; + } + status = remote()->transact(GET_VOLUME_GROUP_FOR_ATTRIBUTES, data, &reply); + if (status != NO_ERROR) { + return status; } - return PRODUCT_STRATEGY_NONE; + status = static_cast(reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + volumeGroup = static_cast(reply.readInt32()); + return NO_ERROR; } }; @@ -1173,7 +1230,9 @@ status_t BnAudioPolicyService::onTransact( case SET_A11Y_SERVICES_UIDS: case SET_UID_DEVICE_AFFINITY: case REMOVE_UID_DEVICE_AFFINITY: - case GET_OFFLOAD_FORMATS_A2DP: { + case GET_OFFLOAD_FORMATS_A2DP: + case LIST_AUDIO_VOLUME_GROUPS: + case GET_VOLUME_GROUP_FOR_ATTRIBUTES: { if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), @@ -1992,7 +2051,7 @@ status_t BnAudioPolicyService::onTransact( status_t status = listAudioProductStrategies(strategies); reply->writeInt32(status); if (status != NO_ERROR) { - return status; + return NO_ERROR; } size_t size = strategies.size(); size_t sizePosition = reply->dataPosition(); @@ -2021,11 +2080,61 @@ status_t BnAudioPolicyService::onTransact( if (status != NO_ERROR) { return status; } - product_strategy_t strategy = getProductStrategyFromAudioAttributes(attributes); + product_strategy_t strategy; + status = getProductStrategyFromAudioAttributes(attributes, strategy); + reply->writeInt32(status); + if (status != NO_ERROR) { + return NO_ERROR; + } reply->writeUint32(static_cast(strategy)); return NO_ERROR; } + case LIST_AUDIO_VOLUME_GROUPS: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + AudioVolumeGroupVector groups; + status_t status = listAudioVolumeGroups(groups); + reply->writeInt32(status); + if (status != NO_ERROR) { + return NO_ERROR; + } + size_t size = groups.size(); + size_t sizePosition = reply->dataPosition(); + reply->writeInt32(size); + size_t finalSize = size; + for (size_t i = 0; i < size; i++) { + size_t position = reply->dataPosition(); + if (groups[i].writeToParcel(reply) != NO_ERROR) { + reply->setDataPosition(position); + finalSize--; + } + } + if (size != finalSize) { + size_t position = reply->dataPosition(); + reply->setDataPosition(sizePosition); + reply->writeInt32(finalSize); + reply->setDataPosition(position); + } + return NO_ERROR; + } + + case GET_VOLUME_GROUP_FOR_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + AudioAttributes attributes; + status_t status = attributes.readFromParcel(&data); + if (status != NO_ERROR) { + return status; + } + volume_group_t group; + status = getVolumeGroupFromAudioAttributes(attributes, group); + reply->writeInt32(status); + if (status != NO_ERROR) { + return NO_ERROR; + } + reply->writeUint32(static_cast(group)); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index b9ee24a978..142d2bbab7 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -367,11 +368,17 @@ public: static bool isHapticPlaybackSupported(); static status_t listAudioProductStrategies(AudioProductStrategyVector &strategies); - static product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa); + static status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy); static audio_attributes_t streamTypeToAttributes(audio_stream_type_t stream); static audio_stream_type_t attributesToStreamType(const audio_attributes_t &attr); + static status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups); + + static status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup); + // ---------------------------------------------------------------------------- class AudioPortCallback : public RefBase diff --git a/media/libaudioclient/include/media/AudioVolumeGroup.h b/media/libaudioclient/include/media/AudioVolumeGroup.h new file mode 100644 index 0000000000..9a6ea0714e --- /dev/null +++ b/media/libaudioclient/include/media/AudioVolumeGroup.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + + +#pragma once + +#include +#include +#include +#include + +namespace android { + +class AudioVolumeGroup : public Parcelable +{ +public: + AudioVolumeGroup() {} + AudioVolumeGroup(const std::string &name, + volume_group_t group, + const AttributesVector &attributes, + const StreamTypeVector &streams) : + mName(name), mGroupId(group), mAudioAttributes(attributes), mStreams(streams) {} + + const std::string &getName() const { return mName; } + volume_group_t getId() const { return mGroupId; } + AttributesVector getAudioAttributes() const { return mAudioAttributes; } + StreamTypeVector getStreamTypes() const { return mStreams; } + + status_t readFromParcel(const Parcel *parcel) override; + status_t writeToParcel(Parcel *parcel) const override; + +private: + std::string mName; + volume_group_t mGroupId = VOLUME_GROUP_NONE; + AttributesVector mAudioAttributes; + StreamTypeVector mStreams; +}; + +using AudioVolumeGroupVector = std::vector; + +} // namespace android diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index e89a55d173..800344d8eb 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -198,7 +198,12 @@ public: virtual bool isHapticPlaybackSupported() = 0; virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0; - virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) = 0; + virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy) = 0; + + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) = 0; + virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) = 0; }; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index bb5441d2c8..9b00a4d696 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -244,7 +244,13 @@ public: virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0; - virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) = 0; + virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy) = 0; + + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) = 0; + + virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) = 0; }; diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index 35d86ee665..6ff8512408 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -88,6 +88,8 @@ public: AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const override; + status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override; + void dump(String8 *dst) const override; diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 4d7c4a05bb..4fe7b42414 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -274,6 +274,15 @@ AttributesVector EngineBase::getAllAttributesForVolumeGroup(volume_group_t volum mVolumeGroups.at(volumeGroup)->getSupportedAttributes() : AttributesVector(); } +status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const +{ + for (const auto &iter : mVolumeGroups) { + groups.push_back({iter.second->getName(), iter.second->getId(), + iter.second->getSupportedAttributes(), iter.second->getStreamTypes()}); + } + return NO_ERROR; +} + void EngineBase::dump(String8 *dst) const { mProductStrategies.dump(dst, 2); diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 07acd2e5a7..38f3401c2b 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -286,6 +287,15 @@ public: virtual AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const = 0; + /** + * @brief listAudioVolumeGroups introspection API to get the Audio Volume Groups, aka + * former stream aliases in Audio Service, defining volume curves attached to one or more + * Audio Attributes. + * @param groups + * @return NO_ERROR if the volume groups were retrieved successfully, error code otherwise + */ + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const = 0; + virtual void dump(String8 *dst) const = 0; protected: diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 3e4d885f80..9fe8d1daa5 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -268,9 +268,23 @@ public: return mEngine->listAudioProductStrategies(strategies); } - virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa) + virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy) { - return mEngine->getProductStrategyForAttributes(aa.getAttributes()); + productStrategy = mEngine->getProductStrategyForAttributes(aa.getAttributes()); + return productStrategy != PRODUCT_STRATEGY_NONE ? NO_ERROR : BAD_VALUE; + } + + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) + { + return mEngine->listAudioVolumeGroups(groups); + } + + virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) + { + volumeGroup = mEngine->getVolumeGroupForAttributes(aa.getAttributes()); + return volumeGroup != VOLUME_GROUP_NONE ? NO_ERROR : BAD_VALUE; } protected: diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index d31ce53fe9..5cdc0e7736 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1204,13 +1204,32 @@ status_t AudioPolicyService::listAudioProductStrategies(AudioProductStrategyVect return mAudioPolicyManager->listAudioProductStrategies(strategies); } -product_strategy_t AudioPolicyService::getProductStrategyFromAudioAttributes( - const AudioAttributes &aa) +status_t AudioPolicyService::getProductStrategyFromAudioAttributes( + const AudioAttributes &aa, product_strategy_t &productStrategy) { if (mAudioPolicyManager == NULL) { - return PRODUCT_STRATEGY_NONE; + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getProductStrategyFromAudioAttributes(aa, productStrategy); +} + +status_t AudioPolicyService::listAudioVolumeGroups(AudioVolumeGroupVector &groups) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->listAudioVolumeGroups(groups); +} + +status_t AudioPolicyService::getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; } Mutex::Autolock _l(mLock); - return mAudioPolicyManager->getProductStrategyFromAudioAttributes(aa); + return mAudioPolicyManager->getVolumeGroupFromAudioAttributes(aa, volumeGroup); } } // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 8cd6e8157e..e19b4e5c22 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -231,7 +231,13 @@ public: virtual bool isHapticPlaybackSupported(); virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies); - virtual product_strategy_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa); + virtual status_t getProductStrategyFromAudioAttributes(const AudioAttributes &aa, + product_strategy_t &productStrategy); + + virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups); + + virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, + volume_group_t &volumeGroup); status_t doStopOutput(audio_port_handle_t portId); void doReleaseOutput(audio_port_handle_t portId); -- GitLab From 3c573f7c0c5cc8b460a272845b3b3d3a0ddb4288 Mon Sep 17 00:00:00 2001 From: Guang Qiao Date: Tue, 22 Jan 2019 17:27:37 +0800 Subject: [PATCH 1028/1530] MPEG4Writer:RaceCondition updating mStartTimestampUs Track1's execution of setting track1's mStartTimestampUs and setting MPEG4Writer's mStartTimestampsUs may be interrupted by track2. If track2 was just running exceedsFileDurationLimit() where all tracks would check if the track mStartTimestampUs was greater than MPEG4Writer's mStartTimestampsUs, track1 would detect that its mStartTimestampUs is less than MPEG4Writer's mStartTimestampUs which was set by track2 and that causes fatal error. Current fix is to update MPEG4Writer's mStartTimeStampUs before updating track's mStartTimeStampsUs so that we avoid getting into the scenario described above. Bug: 123263577 Test: cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice Change-Id: I3be78c92f756250f86fc8bd0c9ff84d95fe4d9e8 --- media/libstagefright/MPEG4Writer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 6259b15a27..f6ed0f1a5d 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -3128,8 +3128,8 @@ status_t MPEG4Writer::Track::threadEntry() { if (!mIsHeic) { if (mStszTableEntries->count() == 0) { mFirstSampleTimeRealUs = systemTime() / 1000; + mOwner->setStartTimestampUs(timestampUs); mStartTimestampUs = timestampUs; - mOwner->setStartTimestampUs(mStartTimestampUs); previousPausedDurationUs = mStartTimestampUs; } -- GitLab From c025be8ce5f1b34bdf293ac367685c969bd430ba Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Tue, 26 Feb 2019 15:47:01 -0800 Subject: [PATCH 1029/1530] Reserve enough space for RTSP CSD make parameters to GetSizeWidth() reflect values being used in corresponding EncodeSize() invocations so we won't overflow the buffer. Bug: 123701862 Test: y Change-Id: I78596176e6042c95582494a8ae1b9c3160bf5955 --- media/libstagefright/rtsp/APacketSource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index cfafaa74bf..a7545ad51c 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -377,8 +377,8 @@ static sp MakeMPEG4VideoCodecSpecificData( ALOGI("VOL dimensions = %dx%d", *width, *height); size_t len1 = config->size() + GetSizeWidth(config->size()) + 1; - size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13; - size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3; + size_t len2 = len1 + GetSizeWidth(len1 + 13) + 1 + 13; + size_t len3 = len2 + GetSizeWidth(len2 + 3) + 1 + 3; sp csd = new ABuffer(len3); uint8_t *dst = csd->data(); -- GitLab From cfe173220a3100c517e688e1bba0ff2da82a7b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 7 Nov 2018 13:41:29 +0100 Subject: [PATCH 1030/1530] audiopolicy: add Volume for attribute and callback native APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I259de42452d2802aa8dbd553f56040dea6995a93 Signed-off-by: François Gaffie Bug: 124767636 Test: make --- media/libaudioclient/AudioSystem.cpp | 109 +++++++++++++ media/libaudioclient/IAudioPolicyService.cpp | 150 ++++++++++++++++++ .../IAudioPolicyServiceClient.cpp | 19 ++- .../include/media/AudioSystem.h | 32 ++++ .../include/media/IAudioPolicyService.h | 13 ++ .../include/media/IAudioPolicyServiceClient.h | 4 + services/audiopolicy/AudioPolicyInterface.h | 15 ++ .../managerdefault/AudioPolicyManager.cpp | 101 ++++++++++++ .../managerdefault/AudioPolicyManager.h | 21 +++ .../service/AudioPolicyClientImpl.cpp | 6 + .../service/AudioPolicyInterfaceImpl.cpp | 47 ++++++ .../service/AudioPolicyService.cpp | 67 +++++++- .../audiopolicy/service/AudioPolicyService.h | 29 ++++ .../audiopolicy/tests/AudioPolicyTestClient.h | 1 + 14 files changed, 612 insertions(+), 2 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 35adb725fd..0ce251344f 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -792,6 +792,7 @@ const sp AudioSystem::get_audio_policy_service() int64_t token = IPCThreadState::self()->clearCallingIdentity(); ap->registerClient(apc); ap->setAudioPortCallbacksEnabled(apc->isAudioPortCbEnabled()); + ap->setAudioVolumeGroupCallbacksEnabled(apc->isAudioVolumeGroupCbEnabled()); IPCThreadState::self()->restoreCallingIdentity(token); } @@ -987,6 +988,38 @@ status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream, return aps->getStreamVolumeIndex(stream, index, device); } +status_t AudioSystem::setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->setVolumeIndexForAttributes(attr, index, device); +} + +status_t AudioSystem::getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getVolumeIndexForAttributes(attr, index, device); +} + +status_t AudioSystem::getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getMaxVolumeIndexForAttributes(attr, index); +} + +status_t AudioSystem::getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->getMinVolumeIndexForAttributes(attr, index); +} + uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) { const sp& aps = AudioSystem::get_audio_policy_service(); @@ -1190,6 +1223,38 @@ status_t AudioSystem::removeAudioPortCallback(const sp& callb return (ret < 0) ? INVALID_OPERATION : NO_ERROR; } +status_t AudioSystem::addAudioVolumeGroupCallback(const sp& callback) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyServiceClient == 0) { + return NO_INIT; + } + int ret = gAudioPolicyServiceClient->addAudioVolumeGroupCallback(callback); + if (ret == 1) { + aps->setAudioVolumeGroupCallbacksEnabled(true); + } + return (ret < 0) ? INVALID_OPERATION : NO_ERROR; +} + +status_t AudioSystem::removeAudioVolumeGroupCallback(const sp& callback) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + + Mutex::Autolock _l(gLockAPS); + if (gAudioPolicyServiceClient == 0) { + return NO_INIT; + } + int ret = gAudioPolicyServiceClient->removeAudioVolumeGroupCallback(callback); + if (ret == 0) { + aps->setAudioVolumeGroupCallbacksEnabled(false); + } + return (ret < 0) ? INVALID_OPERATION : NO_ERROR; +} + status_t AudioSystem::addAudioDeviceCallback( const wp& callback, audio_io_handle_t audioIo) { @@ -1498,6 +1563,47 @@ void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate() } } +// ---------------------------------------------------------------------------- +int AudioSystem::AudioPolicyServiceClient::addAudioVolumeGroupCallback( + const sp& callback) +{ + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioVolumeGroupCallback.size(); i++) { + if (mAudioVolumeGroupCallback[i] == callback) { + return -1; + } + } + mAudioVolumeGroupCallback.add(callback); + return mAudioVolumeGroupCallback.size(); +} + +int AudioSystem::AudioPolicyServiceClient::removeAudioVolumeGroupCallback( + const sp& callback) +{ + Mutex::Autolock _l(mLock); + size_t i; + for (i = 0; i < mAudioVolumeGroupCallback.size(); i++) { + if (mAudioVolumeGroupCallback[i] == callback) { + break; + } + } + if (i == mAudioVolumeGroupCallback.size()) { + return -1; + } + mAudioVolumeGroupCallback.removeAt(i); + return mAudioVolumeGroupCallback.size(); +} + +void AudioSystem::AudioPolicyServiceClient::onAudioVolumeGroupChanged(volume_group_t group, + int flags) +{ + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mAudioVolumeGroupCallback.size(); i++) { + mAudioVolumeGroupCallback[i]->onAudioVolumeGroupChanged(group, flags); + } +} +// ---------------------------------------------------------------------------- + void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate( String8 regId, int32_t state) { @@ -1541,6 +1647,9 @@ void AudioSystem::AudioPolicyServiceClient::binderDied(const wp& who __ for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) { mAudioPortCallbacks[i]->onServiceDied(); } + for (size_t i = 0; i < mAudioVolumeGroupCallback.size(); i++) { + mAudioVolumeGroupCallback[i]->onServiceDied(); + } } { Mutex::Autolock _l(gLockAPS); diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index feb131764e..3bac44f2eb 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -51,6 +51,10 @@ enum { INIT_STREAM_VOLUME, SET_STREAM_VOLUME, GET_STREAM_VOLUME, + SET_VOLUME_ATTRIBUTES, + GET_VOLUME_ATTRIBUTES, + GET_MIN_VOLUME_FOR_ATTRIBUTES, + GET_MAX_VOLUME_FOR_ATTRIBUTES, GET_STRATEGY_FOR_STREAM, GET_OUTPUT_FOR_EFFECT, REGISTER_EFFECT, @@ -78,6 +82,7 @@ enum { START_AUDIO_SOURCE, STOP_AUDIO_SOURCE, SET_AUDIO_PORT_CALLBACK_ENABLED, + SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED, SET_MASTER_MONO, GET_MASTER_MONO, GET_STREAM_VOLUME_DB, @@ -417,6 +422,70 @@ public: return static_cast (reply.readInt32()); } + virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, int index, + audio_devices_t device) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&attr, sizeof(audio_attributes_t)); + data.writeInt32(index); + data.writeInt32(static_cast (device)); + status_t status = remote()->transact(SET_VOLUME_ATTRIBUTES, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, int &index, + audio_devices_t device) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&attr, sizeof(audio_attributes_t)); + data.writeInt32(static_cast (device)); + status_t status = remote()->transact(GET_VOLUME_ATTRIBUTES, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast (reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + index = reply.readInt32(); + return NO_ERROR; + } + virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&attr, sizeof(audio_attributes_t)); + status_t status = remote()->transact(GET_MIN_VOLUME_FOR_ATTRIBUTES, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast (reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + index = reply.readInt32(); + return NO_ERROR; + } + virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.write(&attr, sizeof(audio_attributes_t)); + status_t status = remote()->transact(GET_MAX_VOLUME_FOR_ATTRIBUTES, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = static_cast (reply.readInt32()); + if (status != NO_ERROR) { + return status; + } + index = reply.readInt32(); + return NO_ERROR; + } virtual uint32_t getStrategyForStream(audio_stream_type_t stream) { Parcel data, reply; @@ -694,6 +763,14 @@ public: remote()->transact(SET_AUDIO_PORT_CALLBACK_ENABLED, data, &reply); } + virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(enabled ? 1 : 0); + remote()->transact(SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED, data, &reply); + } + virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device) @@ -1492,6 +1569,73 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case SET_VOLUME_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_attributes_t attributes = {}; + status_t status = data.read(&attributes, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } + int index = data.readInt32(); + audio_devices_t device = static_cast (data.readInt32()); + + reply->writeInt32(static_cast (setVolumeIndexForAttributes(attributes, + index, device))); + return NO_ERROR; + } break; + + case GET_VOLUME_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_attributes_t attributes = {}; + status_t status = data.read(&attributes, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } + audio_devices_t device = static_cast (data.readInt32()); + + int index = 0; + status = getVolumeIndexForAttributes(attributes, index, device); + reply->writeInt32(static_cast (status)); + if (status == NO_ERROR) { + reply->writeInt32(index); + } + return NO_ERROR; + } break; + + case GET_MIN_VOLUME_FOR_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_attributes_t attributes = {}; + status_t status = data.read(&attributes, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } + + int index = 0; + status = getMinVolumeIndexForAttributes(attributes, index); + reply->writeInt32(static_cast (status)); + if (status == NO_ERROR) { + reply->writeInt32(index); + } + return NO_ERROR; + } break; + + case GET_MAX_VOLUME_FOR_ATTRIBUTES: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + audio_attributes_t attributes = {}; + status_t status = data.read(&attributes, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } + + int index = 0; + status = getMaxVolumeIndexForAttributes(attributes, index); + reply->writeInt32(static_cast (status)); + if (status == NO_ERROR) { + reply->writeInt32(index); + } + return NO_ERROR; + } break; + case GET_DEVICES_FOR_STREAM: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_stream_type_t stream = @@ -1740,6 +1884,12 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case SET_AUDIO_VOLUME_GROUP_CALLBACK_ENABLED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + setAudioVolumeGroupCallbacksEnabled(data.readInt32() == 1); + return NO_ERROR; + } break; + case ACQUIRE_SOUNDTRIGGER_SESSION: { CHECK_INTERFACE(IAudioPolicyService, data, reply); sp client = interface_cast( diff --git a/media/libaudioclient/IAudioPolicyServiceClient.cpp b/media/libaudioclient/IAudioPolicyServiceClient.cpp index 1f9eab7fa8..52d8ccd175 100644 --- a/media/libaudioclient/IAudioPolicyServiceClient.cpp +++ b/media/libaudioclient/IAudioPolicyServiceClient.cpp @@ -31,7 +31,8 @@ enum { PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION, PATCH_LIST_UPDATE, MIX_STATE_UPDATE, - RECORDING_CONFIGURATION_UPDATE + RECORDING_CONFIGURATION_UPDATE, + VOLUME_GROUP_CHANGED, }; // ---------------------------------------------------------------------- @@ -108,6 +109,15 @@ public: remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY); } + void onAudioVolumeGroupChanged(volume_group_t group, int flags) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor()); + data.writeUint32(group); + data.writeInt32(flags); + remote()->transact(VOLUME_GROUP_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); + } + void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) { Parcel data, reply; @@ -157,6 +167,13 @@ status_t BnAudioPolicyServiceClient::onTransact( onAudioPatchListUpdate(); return NO_ERROR; } break; + case VOLUME_GROUP_CHANGED: { + CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply); + volume_group_t group = static_cast(data.readUint32()); + int flags = data.readInt32(); + onAudioVolumeGroupChanged(group, flags); + return NO_ERROR; + } break; case MIX_STATE_UPDATE: { CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply); String8 regId = data.readString8(); diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 142d2bbab7..e64f285b09 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -264,6 +264,17 @@ public: int *index, audio_devices_t device); + static status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device); + static status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device); + + static status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); + + static status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); + static uint32_t getStrategyForStream(audio_stream_type_t stream); static audio_devices_t getDevicesForStream(audio_stream_type_t stream); @@ -381,6 +392,21 @@ public: // ---------------------------------------------------------------------------- + class AudioVolumeGroupCallback : public RefBase + { + public: + + AudioVolumeGroupCallback() {} + virtual ~AudioVolumeGroupCallback() {} + + virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags) = 0; + virtual void onServiceDied() = 0; + + }; + + static status_t addAudioVolumeGroupCallback(const sp& callback); + static status_t removeAudioVolumeGroupCallback(const sp& callback); + class AudioPortCallback : public RefBase { public: @@ -513,12 +539,17 @@ private: int removeAudioPortCallback(const sp& callback); bool isAudioPortCbEnabled() const { return (mAudioPortCallbacks.size() != 0); } + int addAudioVolumeGroupCallback(const sp& callback); + int removeAudioVolumeGroupCallback(const sp& callback); + bool isAudioVolumeGroupCbEnabled() const { return (mAudioVolumeGroupCallback.size() != 0); } + // DeathRecipient virtual void binderDied(const wp& who); // IAudioPolicyServiceClient virtual void onAudioPortListUpdate(); virtual void onAudioPatchListUpdate(); + virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags); virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state); virtual void onRecordingConfigurationUpdate(int event, const record_client_info_t *clientInfo, @@ -532,6 +563,7 @@ private: private: Mutex mLock; Vector > mAudioPortCallbacks; + Vector > mAudioVolumeGroupCallback; }; static audio_io_handle_t getOutput(audio_stream_type_t stream); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 800344d8eb..35540f096a 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -93,6 +93,17 @@ public: virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index, audio_devices_t device) = 0; + + virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device) = 0; + virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device) = 0; + virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) = 0; + + virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index) = 0; + virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0; virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0; virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0; @@ -160,6 +171,8 @@ public: virtual void setAudioPortCallbacksEnabled(bool enabled) = 0; + virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled) = 0; + virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device) = 0; diff --git a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h index b3c0381db1..79008c3ccd 100644 --- a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h +++ b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include namespace android { @@ -45,6 +47,8 @@ class IAudioPolicyServiceClient : public IInterface public: DECLARE_META_INTERFACE(AudioPolicyServiceClient); + // Notifies a change of volume group + virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags) = 0; // Notifies a change of audio port configuration. virtual void onAudioPortListUpdate() = 0; // Notifies a change of audio patch configuration. diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 9b00a4d696..d61188f08c 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -159,6 +159,19 @@ public: int *index, audio_devices_t device) = 0; + virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device) = 0; + virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device) = 0; + + virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index) = 0; + + virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index) = 0; + // return the strategy corresponding to a given stream type virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0; @@ -347,6 +360,8 @@ public: virtual void onAudioPatchListUpdate() = 0; + virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags) = 0; + virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use) = 0; virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 4540c4d99a..92aa189296 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2498,6 +2498,107 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, return NO_ERROR; } +status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device) +{ + // Get Volume group matching the Audio Attributes + auto volumeGroup = mEngine->getVolumeGroupForAttributes(attr); + if (volumeGroup == VOLUME_GROUP_NONE) { + ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str()); + return BAD_VALUE; + } + ALOGD("%s: FOUND group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str()); + return setVolumeGroupIndex(getVolumeCurves(attr), volumeGroup, index, device, attr); +} + +status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group, + int index, + audio_devices_t device, + const audio_attributes_t /*attributes*/) +{ + ALOGVV("%s: group=%d", __func__, group); + status_t status = NO_ERROR; + setVolumeCurveIndex(group, index, device, curves); + // update volume on all outputs and streams matching the following: + // - The requested stream (or a stream matching for volume control) is active on the output + // - The device (or devices) selected by the engine for this stream includes + // the requested device + // - For non default requested device, currently selected device on the output is either the + // requested device or one of the devices selected by the engine for this stream + // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if + // no specific device volume value exists for currently selected device. + // @TODO + mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/); + return status; +} + +status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup, + int index, + audio_devices_t device, + IVolumeCurves &volumeCurves) +{ + // VOICE_CALL stream has minVolumeIndex > 0 but can be muted directly by an + // app that has MODIFY_PHONE_STATE permission. + // If voice is member of the volume group, it will contaminate all the member of this group + auto streams = mEngine->getStreamTypesForVolumeGroup(volumeGroup); + if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoiceStream(streams) && index == 0)) || + (index > volumeCurves.getVolumeIndexMax())) { + ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index, + volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax()); + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax(); + + ALOGD("%s device %08x, index %d", __FUNCTION__ , device, index); + volumeCurves.addCurrentVolumeIndex(device, index); + return NO_ERROR; +} + +status_t AudioPolicyManager::getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device) +{ + // if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device selected for this + // stream by the engine. + if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { + device = mEngine->getOutputDevicesForAttributes(attr, nullptr, true /*fromCache*/).types(); + } + return getVolumeIndex(getVolumeCurves(attr), index, device); +} + +status_t AudioPolicyManager::getVolumeIndex(const IVolumeCurves &curves, + int &index, + audio_devices_t device) const +{ + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + device = Volume::getDeviceForVolume(device); + index = curves.getVolumeIndex(device); + ALOGV("%s: device %08x index %d", __FUNCTION__, device, index); + return NO_ERROR; +} + +status_t AudioPolicyManager::getMinVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index) +{ + index = getVolumeCurves(attr).getVolumeIndexMin(); + return NO_ERROR; +} + +status_t AudioPolicyManager::getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index) +{ + index = getVolumeCurves(attr).getVolumeIndexMax(); + return NO_ERROR; +} + audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() { // select one output among several suitable for global effects. diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 9fe8d1daa5..641a03ad88 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -161,6 +161,27 @@ public: int *index, audio_devices_t device); + virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device); + virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device); + virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); + + virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); + + status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index, + audio_devices_t device, const audio_attributes_t attributes); + + status_t setVolumeCurveIndex(volume_group_t volumeGroup, + int index, + audio_devices_t device, + IVolumeCurves &volumeCurves); + + status_t getVolumeIndex(const IVolumeCurves &curves, int &index, + audio_devices_t device) const; + // return the strategy corresponding to a given stream type virtual uint32_t getStrategyForStream(audio_stream_type_t stream) { diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index d826192d9f..5748334e0e 100644 --- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -222,6 +222,12 @@ void AudioPolicyService::AudioPolicyClient::onRecordingConfigurationUpdate( clientConfig, clientEffects, deviceConfig, effects, patchHandle, source); } +void AudioPolicyService::AudioPolicyClient::onAudioVolumeGroupChanged(volume_group_t group, + int flags) +{ + mAudioPolicyService->onAudioVolumeGroupChanged(group, flags); +} + audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId(audio_unique_id_use_t use) { return AudioSystem::newAudioUniqueId(use); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index c19016f612..a1b6b0fb06 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -689,6 +689,53 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, device); } +status_t AudioPolicyService::setVolumeIndexForAttributes(const audio_attributes_t &attributes, + int index, audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->setVolumeIndexForAttributes(attributes, index, device); +} + +status_t AudioPolicyService::getVolumeIndexForAttributes(const audio_attributes_t &attributes, + int &index, audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->getVolumeIndexForAttributes(attributes, index, device); +} + +status_t AudioPolicyService::getMinVolumeIndexForAttributes(const audio_attributes_t &attributes, + int &index) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->getMinVolumeIndexForAttributes(attributes, index); +} + +status_t AudioPolicyService::getMaxVolumeIndexForAttributes(const audio_attributes_t &attributes, + int &index) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->getMaxVolumeIndexForAttributes(attributes, index); +} + uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) { if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 76ac191503..cf9cf71b0e 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -150,6 +150,20 @@ void AudioPolicyService::setAudioPortCallbacksEnabled(bool enabled) mNotificationClients.valueFor(token)->setAudioPortCallbacksEnabled(enabled); } +void AudioPolicyService::setAudioVolumeGroupCallbacksEnabled(bool enabled) +{ + Mutex::Autolock _l(mNotificationClientsLock); + + uid_t uid = IPCThreadState::self()->getCallingUid(); + pid_t pid = IPCThreadState::self()->getCallingPid(); + int64_t token = ((int64_t)uid<<32) | pid; + + if (mNotificationClients.indexOfKey(token) < 0) { + return; + } + mNotificationClients.valueFor(token)->setAudioVolumeGroupCallbacksEnabled(enabled); +} + // removeNotificationClient() is called when the client process dies. void AudioPolicyService::removeNotificationClient(uid_t uid, pid_t pid) { @@ -200,6 +214,19 @@ void AudioPolicyService::doOnAudioPatchListUpdate() } } +void AudioPolicyService::onAudioVolumeGroupChanged(volume_group_t group, int flags) +{ + mOutputCommandThread->changeAudioVolumeGroupCommand(group, flags); +} + +void AudioPolicyService::doOnAudioVolumeGroupChanged(volume_group_t group, int flags) +{ + Mutex::Autolock _l(mNotificationClientsLock); + for (size_t i = 0; i < mNotificationClients.size(); i++) { + mNotificationClients.valueAt(i)->onAudioVolumeGroupChanged(group, flags); + } +} + void AudioPolicyService::onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state) { ALOGV("AudioPolicyService::onDynamicPolicyMixStateUpdate(%s, %d)", @@ -270,7 +297,7 @@ AudioPolicyService::NotificationClient::NotificationClient(const sponAudioVolumeGroupChanged(group, flags); + } +} + + void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate( const String8& regId, int32_t state) { @@ -330,6 +366,10 @@ void AudioPolicyService::NotificationClient::setAudioPortCallbacksEnabled(bool e mAudioPortCallbacksEnabled = enabled; } +void AudioPolicyService::NotificationClient::setAudioVolumeGroupCallbacksEnabled(bool enabled) +{ + mAudioVolumeGroupCallbacksEnabled = enabled; +} void AudioPolicyService::binderDied(const wp& who) { ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(), @@ -1058,6 +1098,18 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() svc->doOnAudioPatchListUpdate(); mLock.lock(); }break; + case CHANGED_AUDIOVOLUMEGROUP: { + AudioVolumeGroupData *data = + static_cast(command->mParam.get()); + ALOGV("AudioCommandThread() processing update audio volume group"); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doOnAudioVolumeGroupChanged(data->mGroup, data->mFlags); + mLock.lock(); + }break; case SET_AUDIOPORT_CONFIG: { SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get(); ALOGV("AudioCommandThread() processing set port config"); @@ -1302,6 +1354,19 @@ void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand() sendCommand(command); } +void AudioPolicyService::AudioCommandThread::changeAudioVolumeGroupCommand(volume_group_t group, + int flags) +{ + spcommand = new AudioCommand(); + command->mCommand = CHANGED_AUDIOVOLUMEGROUP; + AudioVolumeGroupData *data= new AudioVolumeGroupData(); + data->mGroup = group; + data->mFlags = flags; + command->mParam = data; + ALOGV("AudioCommandThread() adding audio volume group changed"); + sendCommand(command); +} + status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand( const struct audio_port_config *config, int delayMs) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index e19b4e5c22..5888841707 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -111,6 +111,17 @@ public: int *index, audio_devices_t device); + virtual status_t setVolumeIndexForAttributes(const audio_attributes_t &attr, + int index, + audio_devices_t device); + virtual status_t getVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index, + audio_devices_t device); + virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index); + virtual status_t getMaxVolumeIndexForAttributes(const audio_attributes_t &attr, + int &index); + virtual uint32_t getStrategyForStream(audio_stream_type_t stream); virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); @@ -192,6 +203,8 @@ public: virtual void setAudioPortCallbacksEnabled(bool enabled); + virtual void setAudioVolumeGroupCallbacksEnabled(bool enabled); + virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device); @@ -275,6 +288,9 @@ public: audio_patch_handle_t patchHandle, audio_source_t source); + void onAudioVolumeGroupChanged(volume_group_t group, int flags); + void doOnAudioVolumeGroupChanged(volume_group_t group, int flags); + private: AudioPolicyService() ANDROID_API; virtual ~AudioPolicyService(); @@ -404,6 +420,7 @@ private: RELEASE_AUDIO_PATCH, UPDATE_AUDIOPORT_LIST, UPDATE_AUDIOPATCH_LIST, + CHANGED_AUDIOVOLUMEGROUP, SET_AUDIOPORT_CONFIG, DYN_POLICY_MIX_STATE_UPDATE, RECORDING_CONFIGURATION_UPDATE @@ -435,6 +452,7 @@ private: int delayMs); void updateAudioPortListCommand(); void updateAudioPatchListCommand(); + void changeAudioVolumeGroupCommand(volume_group_t group, int flags); status_t setAudioPortConfigCommand(const struct audio_port_config *config, int delayMs); void dynamicPolicyMixStateUpdateCommand(const String8& regId, @@ -516,6 +534,12 @@ private: audio_patch_handle_t mHandle; }; + class AudioVolumeGroupData : public AudioCommandData { + public: + volume_group_t mGroup; + int mFlags; + }; + class SetAudioPortConfigData : public AudioCommandData { public: struct audio_port_config mConfig; @@ -648,6 +672,8 @@ private: audio_patch_handle_t patchHandle, audio_source_t source); + virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags); + virtual audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t use); private: @@ -666,6 +692,7 @@ private: void onAudioPatchListUpdate(); void onDynamicPolicyMixStateUpdate(const String8& regId, int32_t state); + void onAudioVolumeGroupChanged(volume_group_t group, int flags); void onRecordingConfigurationUpdate( int event, const record_client_info_t *clientInfo, @@ -676,6 +703,7 @@ private: audio_patch_handle_t patchHandle, audio_source_t source); void setAudioPortCallbacksEnabled(bool enabled); + void setAudioVolumeGroupCallbacksEnabled(bool enabled); uid_t uid() { return mUid; @@ -693,6 +721,7 @@ private: const pid_t mPid; const sp mAudioPolicyServiceClient; bool mAudioPortCallbacksEnabled; + bool mAudioVolumeGroupCallbacksEnabled; }; class AudioClient : public virtual RefBase { diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h index 6ae354bdba..8854eb2e6d 100644 --- a/services/audiopolicy/tests/AudioPolicyTestClient.h +++ b/services/audiopolicy/tests/AudioPolicyTestClient.h @@ -73,6 +73,7 @@ public: int /*delayMs*/) override { return NO_INIT; } void onAudioPortListUpdate() override { } void onAudioPatchListUpdate() override { } + void onAudioVolumeGroupChanged(volume_group_t /*group*/, int /*flags*/) override { } audio_unique_id_t newAudioUniqueId(audio_unique_id_use_t /*use*/) override { return 0; } void onDynamicPolicyMixStateUpdate(String8 /*regId*/, int32_t /*state*/) override { } void onRecordingConfigurationUpdate(int event __unused, -- GitLab From b7ab1e4794384a28a52189d8ef58a6197a817e02 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 20 Feb 2019 15:42:23 -0800 Subject: [PATCH 1031/1530] Camera: Initialize bufferSize for JPEG app segments stream Set bufferSize in @3.4::Stream::bufferSize for JPEG_APP_SEGMENTS stream. Bug: 124999074 Test: Build Change-Id: I6f24f4273b0d3d18b1bdbf263fc12ed48e857004 --- .../libcameraservice/device3/Camera3Device.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 923d17a281..dfbff1803d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -2847,12 +2847,19 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode, } streams.add(outputStream); - if (outputStream->format == HAL_PIXEL_FORMAT_BLOB && - outputStream->data_space == HAL_DATASPACE_V0_JFIF) { + if (outputStream->format == HAL_PIXEL_FORMAT_BLOB) { size_t k = i + ((mInputStream != nullptr) ? 1 : 0); // Input stream if present should // always occupy the initial entry. - bufferSizes[k] = static_cast( - getJpegBufferSize(outputStream->width, outputStream->height)); + if (outputStream->data_space == HAL_DATASPACE_V0_JFIF) { + bufferSizes[k] = static_cast( + getJpegBufferSize(outputStream->width, outputStream->height)); + } else if (outputStream->data_space == + static_cast(HAL_DATASPACE_JPEG_APP_SEGMENTS)) { + bufferSizes[k] = outputStream->width * outputStream->height; + } else { + ALOGW("%s: Blob dataSpace %d not supported", + __FUNCTION__, outputStream->data_space); + } } } -- GitLab From 40180e3d4c8da02999e1d42dc2216063c3c545ed Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 27 Feb 2019 18:59:54 +0000 Subject: [PATCH 1032/1530] Revert "BluetoothAudio HAL: flip to be enabled by default" This reverts commit baa9e4e167b0b9d8b0586871b6178bebcef862c2. Reason for revert: Broke test Change-Id: Idfc9e117e55ad80f281a808789f3e990f47d5cd8 --- .../a2dp_in_audio_policy_configuration.xml | 22 -- .../config/audio_policy_configuration.xml | 10 +- ...icy_configuration_bluetooth_legacy_hal.xml | 211 ------------------ .../bluetooth_audio_policy_configuration.xml | 44 ---- .../managerdefault/AudioPolicyManager.cpp | 24 +- 5 files changed, 17 insertions(+), 294 deletions(-) delete mode 100644 services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml delete mode 100644 services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml delete mode 100644 services/audiopolicy/config/bluetooth_audio_policy_configuration.xml diff --git a/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml b/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml deleted file mode 100644 index 57bd4f814f..0000000000 --- a/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index b28381b32d..b4cc1d3936 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -1,5 +1,5 @@ - - + + @@ -182,8 +182,8 @@ - - + + diff --git a/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml b/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml deleted file mode 100644 index b4cc1d3936..0000000000 --- a/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - - - - - - Speaker - Built-In Mic - Built-In Back Mic - - Speaker - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml b/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml deleted file mode 100644 index ce78eb0031..0000000000 --- a/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index dc5b801be4..b563a042b2 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -33,8 +33,8 @@ #define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml" #define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \ "audio_policy_configuration_a2dp_offload_disabled.xml" -#define AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME \ - "audio_policy_configuration_bluetooth_legacy_hal.xml" +#define AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME \ + "audio_policy_configuration_bluetooth_hal_enabled.xml" #include #include @@ -4080,17 +4080,17 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { status_t ret; if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) { - if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) && - property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { - // Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses - // the legacy hardware module for A2DP and hearing aid. - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); - } else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { - // A2DP offload supported but disabled: try to use special XML file + if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME); - } - } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) { - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); + } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { + // This property persist.bluetooth.bluetooth_audio_hal.enabled is temporary only. + // xml files AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME, although having + // the same name, must be different in offload and non offload cases in device + // specific configuration file. + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); + } + } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); } fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); -- GitLab From 2ffebfcf08c47448540c829f0c0413a757bf64b2 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 27 Feb 2019 13:25:48 -0800 Subject: [PATCH 1033/1530] MediaCodec: don't crash on concurrent release/start/configure Bug: 30739815 Test: atest CtsMediaTestCases:MediaCodecTest#testAsyncRelease Change-Id: I6516a43a698bca2d5e8de724d5e7d84d84795449 --- media/libstagefright/MediaCodec.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 9c58e05ace..d4e4000966 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1973,10 +1973,11 @@ void MediaCodec::onMessageReceived(const sp &msg) { case kWhatComponentConfigured: { - if (mState == UNINITIALIZED || mState == INITIALIZED) { - // In case a kWhatError message came in and replied with error, + if (mState == RELEASING || mState == UNINITIALIZED || mState == INITIALIZED) { + // In case a kWhatError or kWhatRelease message came in and replied, // we log a warning and ignore. - ALOGW("configure interrupted by error, current state %d", mState); + ALOGW("configure interrupted by error or release, current state %d", + mState); break; } CHECK_EQ(mState, CONFIGURING); @@ -2067,6 +2068,13 @@ void MediaCodec::onMessageReceived(const sp &msg) { case kWhatStartCompleted: { + if (mState == RELEASING || mState == UNINITIALIZED) { + // In case a kWhatRelease message came in and replied, + // we log a warning and ignore. + ALOGW("start interrupted by release, current state %d", mState); + break; + } + CHECK_EQ(mState, STARTING); if (mIsVideo) { addResource( @@ -2632,11 +2640,12 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } - // If we're flushing, or we're stopping but received a release - // request, post the reply for the pending call first, and consider - // it done. The reply token will be replaced after this, and we'll - // no longer be able to reply. - if (mState == FLUSHING || mState == STOPPING) { + // If we're flushing, stopping, configuring or starting but + // received a release request, post the reply for the pending call + // first, and consider it done. The reply token will be replaced + // after this, and we'll no longer be able to reply. + if (mState == FLUSHING || mState == STOPPING + || mState == CONFIGURING || mState == STARTING) { (new AMessage)->postReply(mReplyID); } -- GitLab From e598545cb84723da4878c503ac54ca1426fa920b Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Sun, 24 Feb 2019 01:39:15 +0800 Subject: [PATCH 1034/1530] Reland BluetoothAudio HAL: flip to be enabled by default Change to new properties + swap audio XML files, and supports following scenarios: 1) (default) audio_policy_configuration.xml Using BluetoothAudio HAL (+ A2DP offload if supported) 2) audio_policy_configuration_a2dp_offload_disabled.xml Using BluetoothAudio HAL only when A2DP offload is supported but disabled. 3) audio_policy_configuration_bluetooth_legacy_hal.xml No BluetoothAudio HAL nor A2DP offload Bug: 126304229 Test: Switch BluetoothAudio HAL and A2DP offload manully (cherry picked from commit baa9e4e167b0b9d8b0586871b6178bebcef862c2) Change-Id: I3c17b304a12c1eccd9f97ee50a0b7cf6e4b9a1da --- .../a2dp_in_audio_policy_configuration.xml | 22 ++ .../config/audio_policy_configuration.xml | 10 +- ...icy_configuration_bluetooth_legacy_hal.xml | 211 ++++++++++++++++++ .../bluetooth_audio_policy_configuration.xml | 44 ++++ .../managerdefault/AudioPolicyManager.cpp | 24 +- 5 files changed, 294 insertions(+), 17 deletions(-) create mode 100644 services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml create mode 100644 services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml create mode 100644 services/audiopolicy/config/bluetooth_audio_policy_configuration.xml diff --git a/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml b/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml new file mode 100644 index 0000000000..57bd4f814f --- /dev/null +++ b/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml index b4cc1d3936..b28381b32d 100644 --- a/services/audiopolicy/config/audio_policy_configuration.xml +++ b/services/audiopolicy/config/audio_policy_configuration.xml @@ -1,5 +1,5 @@ - - + + @@ -182,8 +182,8 @@ - - + + diff --git a/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml b/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml new file mode 100644 index 0000000000..b4cc1d3936 --- /dev/null +++ b/services/audiopolicy/config/audio_policy_configuration_bluetooth_legacy_hal.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + Speaker + Built-In Mic + Built-In Back Mic + + Speaker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml b/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml new file mode 100644 index 0000000000..ce78eb0031 --- /dev/null +++ b/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ce305a474b..92aa189296 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -33,8 +33,8 @@ #define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml" #define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \ "audio_policy_configuration_a2dp_offload_disabled.xml" -#define AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME \ - "audio_policy_configuration_bluetooth_hal_enabled.xml" +#define AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME \ + "audio_policy_configuration_bluetooth_legacy_hal.xml" #include #include @@ -4178,17 +4178,17 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { status_t ret; if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) { - if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) && + property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses + // the legacy hardware module for A2DP and hearing aid. + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); + } else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) { + // A2DP offload supported but disabled: try to use special XML file fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME); - } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { - // This property persist.bluetooth.bluetooth_audio_hal.enabled is temporary only. - // xml files AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME, although having - // the same name, must be different in offload and non offload cases in device - // specific configuration file. - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); - } - } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.enabled", false)) { - fileNames.push_back(AUDIO_POLICY_BLUETOOTH_HAL_ENABLED_XML_CONFIG_FILE_NAME); + } + } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) { + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); } fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); -- GitLab From e6c3711b2931a98c0f81c86ddba902b1589acf44 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 26 Feb 2019 17:38:10 -0800 Subject: [PATCH 1035/1530] AudioFlinger: Accumulate long term thread latency statistics. This results in a more stable value. Test: AudioFlinger dumpsys Change-Id: I72f5e819076502328222b3bf6bdc761b8aad4a87 --- services/audioflinger/Threads.cpp | 38 +++++++++++++++++++------------ services/audioflinger/Threads.h | 1 + 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 8a45fc2736..8f181a4038 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -842,6 +842,12 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u mIoJitterMs.toString().c_str()); } + if (mLatencyMs.getN() > 0) { + dprintf(fd, " Threadloop %s latency stats: %s\n", + isOutput() ? "write" : "read", + mLatencyMs.toString().c_str()); + } + if (locked) { mLock.unlock(); } @@ -3380,6 +3386,14 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } } + + if (audio_has_proportional_frames(mFormat)) { + const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); + if (latencyMs != 0.) { // note 0. means timestamp is empty. + mLatencyMs.add(latencyMs); + } + } + } // if (mType ... ) { // no indentation #if 0 // logFormat example @@ -5296,13 +5310,6 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar dprintf(fd, " Master balance: %f (%s)\n", mMasterBalance.load(), (hasFastMixer() ? std::to_string(mFastMixer->getMasterBalance()) : mBalance.toString()).c_str()); - const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); - if (latencyMs != 0.) { - dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); - } else { - dprintf(fd, " NormalMixer latency ms: unavail\n"); - } - if (hasFastMixer()) { dprintf(fd, " FastMixer thread %p tid=%d", mFastMixer.get(), mFastMixer->getTid()); @@ -7042,6 +7049,15 @@ reacquire_wakelock: mTimestampVerifier.error(); } } + + // From the timestamp, input read latency is negative output write latency. + const audio_input_flags_t flags = mInput != NULL ? mInput->flags : AUDIO_INPUT_FLAG_NONE; + const double latencyMs = RecordTrack::checkServerLatencySupported(mFormat, flags) + ? - mTimestamp.getOutputServerLatencyMs(mSampleRate) : 0.; + if (latencyMs != 0.) { // note 0. means timestamp is empty. + mLatencyMs.add(latencyMs); + } + // Use this to track timestamp information // ALOGD("%s", mTimestamp.toString().c_str()); @@ -7734,14 +7750,6 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a (void)input->stream->dump(fd); } - const double latencyMs = RecordTrack::checkServerLatencySupported(mFormat, flags) - ? - mTimestamp.getOutputServerLatencyMs(mSampleRate) : 0.; - if (latencyMs != 0.) { - dprintf(fd, " NormalRecord latency ms: %.2lf\n", latencyMs); - } else { - dprintf(fd, " NormalRecord latency ms: unavail\n"); - } - dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no"); dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 4968829cb4..1afea08b73 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -520,6 +520,7 @@ protected: // This should be read under ThreadBase lock (if not on the threadLoop thread). audio_utils::Statistics mIoJitterMs{0.995 /* alpha */}; audio_utils::Statistics mProcessTimeMs{0.995 /* alpha */}; + audio_utils::Statistics mLatencyMs{0.995 /* alpha */}; bool mIsMsdDevice = false; // A condition that must be evaluated by the thread loop has changed and -- GitLab From 8be94978a3ec70d8dd621c9b5548f27ddd41ad7a Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Fri, 22 Feb 2019 13:26:25 -0800 Subject: [PATCH 1036/1530] Check for app manifest opt-out from playback capture Query the package manager to check if the app has not opt-out of its playback being captured. Test: adb shell audiorecorder --target /data/file1.raw & adb shell am start -a android.intent.action.VIEW -d file:///system/media/audio/ringtones/Lollipop.ogg -t audio/ogg adb dumpsys media.audio_policy # check playback is not recorded # change media player manifest to allowPlaybackCapture=true adb dumpsys media.audio_policy # check playback is recorded kill %1 adb pull /data/file1.raw && sox -r 48000 -e signed -b 16 -c 2 file1.raw file.wav&& audacity file.wav # check silence then sound Bug: 111453086 Change-Id: Id6fb7d0e10c02b0473bcbc0786e8360536996f48 Signed-off-by: Kevin Rocard --- media/audioserver/Android.mk | 1 + media/utils/ServiceUtilities.cpp | 84 +++++++++++++++++++ .../include/mediautils/ServiceUtilities.h | 36 ++++++++ services/audiopolicy/Android.mk | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 13 +-- .../service/AudioPolicyService.cpp | 2 + .../audiopolicy/service/AudioPolicyService.h | 3 + 7 files changed, 135 insertions(+), 6 deletions(-) diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk index 10d8b13347..1f24413801 100644 --- a/media/audioserver/Android.mk +++ b/media/audioserver/Android.mk @@ -16,6 +16,7 @@ LOCAL_SHARED_LIBRARIES := \ libhwbinder \ libmedia \ libmedialogservice \ + libmediautils \ libnbaio \ libnblog \ libsoundtriggerservice \ diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 599c44685c..2fb24f576c 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -22,6 +22,9 @@ #include #include "mediautils/ServiceUtilities.h" +#include +#include + /* When performing permission checks we do not use permission cache for * runtime permissions (protection level dangerous) as they may change at * runtime. All other permissions (protection level normal and dangerous) @@ -220,4 +223,85 @@ status_t checkIMemory(const sp& iMemory) return NO_ERROR; } +sp MediaPackageManager::retreivePackageManager() { + const sp sm = defaultServiceManager(); + if (sm == nullptr) { + ALOGW("%s: failed to retrieve defaultServiceManager", __func__); + return nullptr; + } + sp packageManager = sm->checkService(String16(nativePackageManagerName)); + if (packageManager == nullptr) { + ALOGW("%s: failed to retrieve native package manager", __func__); + return nullptr; + } + return interface_cast(packageManager); +} + +std::optional MediaPackageManager::doIsAllowed(uid_t uid) { + if (mPackageManager == nullptr) { + /** Can not fetch package manager at construction it may not yet be registered. */ + mPackageManager = retreivePackageManager(); + if (mPackageManager == nullptr) { + ALOGW("%s: Playback capture is denied as package manager is not reachable", __func__); + return std::nullopt; + } + } + + std::vector packageNames; + auto status = mPackageManager->getNamesForUids({(int32_t)uid}, &packageNames); + if (!status.isOk()) { + ALOGW("%s: Playback capture is denied for uid %u as the package names could not be " + "retrieved from the package manager: %s", __func__, uid, status.toString8().c_str()); + return std::nullopt; + } + if (packageNames.empty()) { + ALOGW("%s: Playback capture for uid %u is denied as no package name could be retrieved " + "from the package manager: %s", __func__, uid, status.toString8().c_str()); + return std::nullopt; + } + std::vector isAllowed; + status = mPackageManager->isAudioPlaybackCaptureAllowed(packageNames, &isAllowed); + if (!status.isOk()) { + ALOGW("%s: Playback capture is denied for uid %u as the manifest property could not be " + "retrieved from the package manager: %s", __func__, uid, status.toString8().c_str()); + return std::nullopt; + } + if (packageNames.size() != isAllowed.size()) { + ALOGW("%s: Playback capture is denied for uid %u as the package manager returned incoherent" + " response size: %zu != %zu", __func__, uid, packageNames.size(), isAllowed.size()); + return std::nullopt; + } + + // Zip together packageNames and isAllowed for debug logs + Packages& packages = mDebugLog[uid]; + packages.resize(packageNames.size()); // Reuse all objects + std::transform(begin(packageNames), end(packageNames), begin(isAllowed), + begin(packages), [] (auto& name, bool isAllowed) -> Package { + return {std::move(name), isAllowed}; + }); + + // Only allow playback record if all packages in this UID allow it + bool playbackCaptureAllowed = std::all_of(begin(isAllowed), end(isAllowed), + [](bool b) { return b; }); + + return playbackCaptureAllowed; +} + +void MediaPackageManager::dump(int fd, int spaces) const { + dprintf(fd, "%*sAllow playback capture log:\n", spaces, ""); + if (mPackageManager == nullptr) { + dprintf(fd, "%*sNo package manager\n", spaces + 2, ""); + } + dprintf(fd, "%*sPackage manager errors: %u\n", spaces + 2, "", mPackageManagerErrors); + + for (const auto& uidCache : mDebugLog) { + for (const auto& package : std::get(uidCache)) { + dprintf(fd, "%*s- uid=%5u, allowPlaybackCapture=%s, packageName=%s\n", spaces + 2, "", + std::get(uidCache), + package.playbackCaptureAllowed ? "true " : "false", + package.name.c_str()); + } + } +} + } // namespace android diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 98f54c2b8b..94370ee91e 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -14,13 +14,22 @@ * limitations under the License. */ +#ifndef ANDROID_MEDIAUTILS_SERVICEUTILITIES_H +#define ANDROID_MEDIAUTILS_SERVICEUTILITIES_H + #include +#include #include #include #include #include +#include +#include +#include +#include + namespace android { // Audio permission utilities @@ -72,4 +81,31 @@ bool modifyDefaultAudioEffectsAllowed(); bool dumpAllowed(); bool modifyPhoneStateAllowed(pid_t pid, uid_t uid); status_t checkIMemory(const sp& iMemory); + +class MediaPackageManager { +public: + /** Query the PackageManager to check if all apps of an UID allow playback capture. */ + bool allowPlaybackCapture(uid_t uid) { + auto result = doIsAllowed(uid); + if (!result) { + mPackageManagerErrors++; + } + return result.value_or(false); + } + void dump(int fd, int spaces = 0) const; +private: + static constexpr const char* nativePackageManagerName = "package_native"; + std::optional doIsAllowed(uid_t uid); + sp retreivePackageManager(); + sp mPackageManager; // To check apps manifest + uint_t mPackageManagerErrors = 0; + struct Package { + std::string name; + bool playbackCaptureAllowed = false; + }; + using Packages = std::vector; + std::map mDebugLog; +}; } + +#endif // ANDROID_MEDIAUTILS_SERVICEUTILITIES_H diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index f72f44a652..9e4eebc478 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -90,7 +90,7 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_SHARED_LIBRARIES += libmedia_helper LOCAL_SHARED_LIBRARIES += libmediametrics -LOCAL_SHARED_LIBRARIES += libhidlbase libxml2 +LOCAL_SHARED_LIBRARIES += libbinder libhidlbase libxml2 ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index d31ce53fe9..4c1ca8c394 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -20,7 +20,6 @@ #include "AudioPolicyService.h" #include "TypeConverter.h" #include -#include #include #include @@ -167,7 +166,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream) return mAudioPolicyManager->getOutput(stream); } -status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, +status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *originalAttr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, @@ -191,9 +190,13 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid); uid = callingUid; } + audio_attributes_t attr = *originalAttr; + if (!mPackageManager.allowPlaybackCapture(uid)) { + attr.flags |= AUDIO_FLAG_NO_CAPTURE; + } audio_output_flags_t originalFlags = flags; AutoCallerClear acc; - status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, + status_t result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, config, &flags, selectedDeviceId, portId, secondaryOutputs); @@ -209,14 +212,14 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, *selectedDeviceId = AUDIO_PORT_HANDLE_NONE; *portId = AUDIO_PORT_HANDLE_NONE; secondaryOutputs->clear(); - result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config, + result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, config, &flags, selectedDeviceId, portId, secondaryOutputs); } if (result == NO_ERROR) { sp client = - new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream); + new AudioPlaybackClient(attr, *output, uid, pid, session, *selectedDeviceId, *stream); mAudioPlaybackClients.add(*portId, client); } return result; diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 76ac191503..1d72931b71 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -551,6 +551,8 @@ status_t AudioPolicyService::dump(int fd, const Vector& args __unused) mAudioPolicyManager->dump(fd); } + mPackageManager.dump(fd); + if (locked) mLock.unlock(); } return NO_ERROR; diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 8cd6e8157e..7fc52dd24b 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "AudioPolicyEffects.h" #include "managerdefault/AudioPolicyManager.h" #include @@ -784,6 +785,8 @@ private: DefaultKeyedVector< audio_port_handle_t, sp > mAudioRecordClients; DefaultKeyedVector< audio_port_handle_t, sp > mAudioPlaybackClients; + + MediaPackageManager mPackageManager; // To check allowPlaybackCapture }; } // namespace android -- GitLab From d83b08ac54d11ed557b3cff9fef60dfa84386550 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 27 Feb 2019 15:05:54 -0800 Subject: [PATCH 1037/1530] Audioflinger: Do not intercept if no audio This was not efficient and leaded to an assert in obtainBuffer. I'm not sure which condition can lead to a getNextBuffer of size 0, but it has been observed and is not forbidden by getNextBuffer documentation. Test: atest android.media.cts.AudioPlaybackCaptureTest#testCaptureMediaUsage Bug: 111453086 Change-Id: I5accf7c1d488ff4686272588329bab71d64f67cd Signed-off-by: Kevin Rocard --- services/audioflinger/Tracks.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 65f799e7f5..ad78a452b4 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -700,8 +700,13 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( const AudioBufferProvider::Buffer& sourceBuffer) { auto start = std::chrono::steady_clock::now(); const size_t frameCount = sourceBuffer.frameCount; - for (auto& sink : mTeePatches) { - RecordThread::PatchRecord* patchRecord = sink.patchRecord.get(); + if (frameCount == 0) { + return; // No audio to intercept. + // Additionally PatchProxyBufferProvider::obtainBuffer (called by PathTrack::getNextBuffer) + // does not allow 0 frame size request contrary to getNextBuffer + } + for (auto& teePatch : mTeePatches) { + RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get(); size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount); // On buffer wrap, the buffer frame count will be less than requested, -- GitLab From 25f9b05b0d1601243f2981e434f062658e15d3d2 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 27 Feb 2019 15:08:54 -0800 Subject: [PATCH 1038/1530] AudioPolicy: allow recording on a APC dyn policy Test: atest android.media.cts.AudioPlaybackCaptureTest#testCaptureMediaUsage Bug: 111453086 Change-Id: Iee6c2f450268ab16eb299827e358468cbf1349e2 Signed-off-by: Kevin Rocard --- services/audiopolicy/AudioPolicyInterface.h | 4 +++- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 6 +++++- services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index d61188f08c..acbfc9ee72 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -58,10 +58,12 @@ public: typedef enum { API_INPUT_INVALID = -1, API_INPUT_LEGACY = 0,// e.g. audio recording from a microphone - API_INPUT_MIX_CAPTURE,// used for "remote submix", capture of the media to play it remotely + API_INPUT_MIX_CAPTURE,// used for "remote submix" legacy mode (no DAP), + // capture of the media to play it remotely API_INPUT_MIX_EXT_POLICY_REROUTE,// used for platform audio rerouting, where mixes are // handled by external and dynamically installed // policies which reroute audio mixes + API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK, // used for playback capture with a MediaProjection API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path } input_type_t; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ce305a474b..c39899c1a5 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1986,7 +1986,11 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (status != NO_ERROR) { goto error; } - *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; + if (is_mix_loopback_render(policyMix->mRouteFlags)) { + *inputType = API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK; + } else { + *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; + } device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, String8(attr->tags + strlen("addr=")), AUDIO_FORMAT_DEFAULT); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index a1b6b0fb06..68eade152c 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -404,6 +404,9 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, if (status == NO_ERROR) { // enforce permission (if any) required for each type of input switch (inputType) { + case AudioPolicyInterface::API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK: + // this use case has been validated in audio service with a MediaProjection token, + // and doesn't rely on regular permissions case AudioPolicyInterface::API_INPUT_LEGACY: break; case AudioPolicyInterface::API_INPUT_TELEPHONY_RX: -- GitLab From 99f4a73adf879fdb1fd2f5664b85d96c1a009042 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 21 Feb 2019 15:27:38 +0900 Subject: [PATCH 1039/1530] Use libandroid_runtime lazily This is a part of cutting dependency from libmediandk to libandroid_runtime. By making a libandroid_runtime to be loaded lazily, libmediandk won't load libandroid_runtime when it is used as a LL-NDK. Bug: 124268753 Test: m -j Change-Id: Ib040856c58d38e11a5f32cd5dd5519910573334b --- media/ndk/Android.bp | 7 +++- media/ndk/NdkMediaCrypto.cpp | 1 - media/ndk/NdkMediaDataSource.cpp | 72 +++++++++++++++++++++++++++++--- media/ndk/NdkMediaExtractor.cpp | 1 - media/ndk/NdkMediaFormat.cpp | 1 - media/ndk/NdkMediaMuxer.cpp | 1 - 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 0b274a78f1..f9f1acc2be 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -85,7 +85,6 @@ cc_library_shared { "libutils", "libcutils", "libnativewindow", - "libandroid_runtime", "libbinder", "libhidlbase", "libgui", @@ -94,6 +93,12 @@ cc_library_shared { "libmediandk_utils", ], + required: [ + // libmediandk may be used by Java and non-Java things. When lower-level things use it, + // they shouldn't have to take on the cost of loading libandroid_runtime. + "libandroid_runtime", + ], + export_include_dirs: ["include"], product_variables: { diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp index b8af5ff09d..ce2c660d11 100644 --- a/media/ndk/NdkMediaCrypto.cpp +++ b/media/ndk/NdkMediaCrypto.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp index 1abee93478..0891f2a5a9 100644 --- a/media/ndk/NdkMediaDataSource.cpp +++ b/media/ndk/NdkMediaDataSource.cpp @@ -23,9 +23,7 @@ #include #include -#include -#include -#include +#include #include #include #include @@ -41,8 +39,67 @@ #include "../../libstagefright/include/NuCachedSource2.h" #include "NdkMediaDataSourceCallbacksPriv.h" +#include // std::call_once,once_flag +#include // dlopen + using namespace android; +// load libandroid_runtime.so lazily. +// A vendor process may use libmediandk but should not depend on libandroid_runtime. +// TODO(jooyung): remove duplicate (b/125550121) +// frameworks/native/libs/binder/ndk/ibinder_jni.cpp +namespace { + +typedef JNIEnv* (*getJNIEnv_t)(); +typedef sp (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); + +getJNIEnv_t getJNIEnv_; +ibinderForJavaObject_t ibinderForJavaObject_; + +std::once_flag mLoadFlag; + +void load() { + std::call_once(mLoadFlag, []() { + void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY); + if (handle == nullptr) { + ALOGE("Could not open libandroid_runtime."); + return; + } + + getJNIEnv_ = reinterpret_cast( + dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv")); + if (getJNIEnv_ == nullptr) { + ALOGE("Could not find AndroidRuntime::getJNIEnv."); + // no return + } + + ibinderForJavaObject_ = reinterpret_cast( + dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject")); + if (ibinderForJavaObject_ == nullptr) { + ALOGE("Could not find ibinderForJavaObject."); + // no return + } + }); +} + +JNIEnv* getJNIEnv() { + load(); + if (getJNIEnv_ == nullptr) { + return nullptr; + } + return (getJNIEnv_)(); +} + +sp ibinderForJavaObject(JNIEnv* env, jobject obj) { + load(); + if (ibinderForJavaObject_ == nullptr) { + return nullptr; + } + return (ibinderForJavaObject_)(env, obj); +} + +} // namespace + struct AMediaDataSource { void *userdata; AMediaDataSourceReadAt readAt; @@ -124,9 +181,14 @@ static sp createMediaHttpServiceFromJavaObj(JNIEnv *env, jobje if (obj == NULL) { return NULL; } + sp binder; switch (version) { case 1: - return interface_cast(ibinderForJavaObject(env, obj)); + binder = ibinderForJavaObject(env, obj); + if (binder == NULL) { + return NULL; + } + return interface_cast(binder); case 2: return new JMedia2HTTPService(env, obj); default: @@ -179,7 +241,7 @@ sp createMediaHttpService(const char *uri, int version) { switch (version) { case 1: - env = AndroidRuntime::getJNIEnv(); + env = getJNIEnv(); clazz = "android/media/MediaHTTPService"; method = "createHttpServiceBinderIfNecessary"; signature = "(Ljava/lang/String;)Landroid/os/IBinder;"; diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp index 28e4f12880..c83b255593 100644 --- a/media/ndk/NdkMediaExtractor.cpp +++ b/media/ndk/NdkMediaExtractor.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 7cc7f1695e..768a7a9adc 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp index e79926d5e7..d1992bf7ed 100644 --- a/media/ndk/NdkMediaMuxer.cpp +++ b/media/ndk/NdkMediaMuxer.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include -- GitLab From e375d4190495514fa18cdb91563870eb4f4253eb Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 26 Feb 2019 12:54:53 -0800 Subject: [PATCH 1040/1530] Support muting haptic channels. When playing audio-haptic channels, there could be request to mute haptic channels according to settings. To support that, do not select haptic output when forcing muting haptic channels. Test: Play audio-haptic coupled data Bug: 126401770 Change-Id: Ifb52385c56ec8d67fe031e1877ea35cbe2319600 --- .../managerdefault/AudioPolicyManager.cpp | 25 +++++++++++-------- .../managerdefault/AudioPolicyManager.h | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b563a042b2..698fbabb74 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1042,7 +1042,8 @@ status_t AudioPolicyManager::getOutputForAttrInt( } } if (*output == AUDIO_IO_HANDLE_NONE) { - *output = getOutputForDevices(outputDevices, session, *stream, config, flags); + *output = getOutputForDevices(outputDevices, session, *stream, config, + flags, attr->flags & AUDIO_FLAG_MUTE_HAPTIC); } if (*output == AUDIO_IO_HANDLE_NONE) { return INVALID_OPERATION; @@ -1120,11 +1121,16 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevices( audio_session_t session, audio_stream_type_t stream, const audio_config_t *config, - audio_output_flags_t *flags) + audio_output_flags_t *flags, + bool forceMutingHaptic) { audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; status_t status; + // Discard haptic channel mask when forcing muting haptic channels. + audio_channel_mask_t channelMask = forceMutingHaptic + ? (config->channel_mask & ~AUDIO_CHANNEL_HAPTIC_ALL) : config->channel_mask; + // open a direct output if required by specified parameters //force direct flag if offload flag is set: offloading implies a direct output stream // and all common behaviors are driven by checking only the direct flag @@ -1161,7 +1167,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevices( // and not explicitly requested if (((*flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && audio_is_linear_pcm(config->format) && config->sample_rate <= SAMPLE_RATE_HZ_MAX && - audio_channel_count_from_out_mask(config->channel_mask) <= 2) { + audio_channel_count_from_out_mask(channelMask) <= 2) { goto non_direct_output; } @@ -1177,7 +1183,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevices( profile = getProfileForOutput(devices, config->sample_rate, config->format, - config->channel_mask, + channelMask, (audio_output_flags_t)*flags, true /* directOnly */); } @@ -1191,7 +1197,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevices( // and configured with same parameters if ((config->sample_rate == desc->mSamplingRate) && (config->format == desc->mFormat) && - (config->channel_mask == desc->mChannelMask) && + (channelMask == desc->mChannelMask) && (session == desc->mDirectClientSession)) { desc->mDirectOpenCount++; ALOGI("%s reusing direct output %d for session %d", __func__, @@ -1233,11 +1239,11 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevices( if (status != NO_ERROR || (config->sample_rate != 0 && config->sample_rate != outputDesc->mSamplingRate) || (config->format != AUDIO_FORMAT_DEFAULT && config->format != outputDesc->mFormat) || - (config->channel_mask != 0 && config->channel_mask != outputDesc->mChannelMask)) { + (channelMask != 0 && channelMask != outputDesc->mChannelMask)) { ALOGV("%s failed opening direct output: output %d sample rate %d %d," "format %d %d, channel mask %04x %04x", __func__, output, config->sample_rate, outputDesc->mSamplingRate, config->format, outputDesc->mFormat, - config->channel_mask, outputDesc->mChannelMask); + channelMask, outputDesc->mChannelMask); if (output != AUDIO_IO_HANDLE_NONE) { outputDesc->close(); } @@ -1278,12 +1284,11 @@ non_direct_output: // at this stage we should ignore the DIRECT flag as no direct output could be found earlier *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT); - output = selectOutput(outputs, *flags, config->format, - config->channel_mask, config->sample_rate); + output = selectOutput(outputs, *flags, config->format, channelMask, config->sample_rate); } ALOGW_IF((output == 0), "getOutputForDevices() could not find output for stream %d, " "sampling rate %d, format %#x, channels %#x, flags %#x", - stream, config->sample_rate, config->format, config->channel_mask, *flags); + stream, config->sample_rate, config->format, channelMask, *flags); return output; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 06a1f3e416..84b4d0ef39 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -736,7 +736,8 @@ private: audio_session_t session, audio_stream_type_t stream, const audio_config_t *config, - audio_output_flags_t *flags); + audio_output_flags_t *flags, + bool forceMutingHaptic = false); /** * @brief getInputForDevice selects an input handle for a given input device and -- GitLab From c3e5e945fd5af4c6022bee1f3706aba1fd606b9f Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 28 Feb 2019 11:59:20 -0800 Subject: [PATCH 1041/1530] CCodec: handle input image for input buffers as well Bug: 126743446 Test: atest CtsMediaTestCases:VideoEncoderTest#testOtherH264Flex480p Change-Id: Idb33f27e43bd579ea4b0e7877581db79ec3c3223 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 7a444a35f1..fb6af9307e 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -99,6 +99,34 @@ public: */ virtual size_t numClientBuffers() const = 0; + void handleImageData(const sp &buffer) { + sp imageDataCandidate = buffer->getImageData(); + if (imageDataCandidate == nullptr) { + return; + } + sp imageData; + if (!mFormat->findBuffer("image-data", &imageData) + || imageDataCandidate->size() != imageData->size() + || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) { + ALOGD("[%s] updating image-data", mName); + sp newFormat = dupFormat(); + newFormat->setBuffer("image-data", imageDataCandidate); + MediaImage2 *img = (MediaImage2*)imageDataCandidate->data(); + if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { + int32_t stride = img->mPlane[0].mRowInc; + newFormat->setInt32(KEY_STRIDE, stride); + ALOGD("[%s] updating stride = %d", mName, stride); + if (img->mNumPlanes > 1 && stride > 0) { + int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; + newFormat->setInt32(KEY_SLICE_HEIGHT, vstride); + ALOGD("[%s] updating vstride = %d", mName, vstride); + } + } + setFormat(newFormat); + buffer->setFormat(newFormat); + } + } + protected: std::string mComponentName; ///< name of component for debugging std::string mChannelName; ///< name of channel for debugging @@ -255,34 +283,6 @@ public: mSkipCutBuffer = scb; } - void handleImageData(const sp &buffer) { - sp imageDataCandidate = buffer->getImageData(); - if (imageDataCandidate == nullptr) { - return; - } - sp imageData; - if (!mFormat->findBuffer("image-data", &imageData) - || imageDataCandidate->size() != imageData->size() - || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) { - ALOGD("[%s] updating image-data", mName); - sp newFormat = dupFormat(); - newFormat->setBuffer("image-data", imageDataCandidate); - MediaImage2 *img = (MediaImage2*)imageDataCandidate->data(); - if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { - int32_t stride = img->mPlane[0].mRowInc; - newFormat->setInt32(KEY_STRIDE, stride); - ALOGD("[%s] updating stride = %d", mName, stride); - if (img->mNumPlanes > 1 && stride > 0) { - int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; - newFormat->setInt32(KEY_SLICE_HEIGHT, vstride); - ALOGD("[%s] updating vstride = %d", mName, vstride); - } - } - setFormat(newFormat); - buffer->setFormat(newFormat); - } - } - protected: sp mSkipCutBuffer; @@ -783,6 +783,7 @@ public: status_t err = mImpl.grabBuffer(index, &c2Buffer); if (err == OK) { c2Buffer->setFormat(mFormat); + handleImageData(c2Buffer); *buffer = c2Buffer; return true; } @@ -1053,6 +1054,7 @@ public: return false; } *index = mImpl.assignSlot(newBuffer); + handleImageData(newBuffer); *buffer = newBuffer; return true; } -- GitLab From 89e82232200387cf9ba4fdee9777a26404fd2a2b Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Sat, 19 Jan 2019 18:14:10 -0800 Subject: [PATCH 1042/1530] audiopolicy: Get Hw offloaded A2DP formats from declared devices Fix implementation of fetching hw offloaded A2DP encoded formats. Fetch formats from declared A2DP devices on primary instead of system property. Bug: 111812273 Test: verify A2DP using multiple headsets, switch A2DP codecs Change-Id: I4857b24ba5dc6cf089c6f4b961492a53f5ca2018 --- .../managerdefault/AudioPolicyManager.cpp | 38 +++++-------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ce305a474b..5b2485a684 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -479,36 +479,16 @@ status_t AudioPolicyManager::getHwOffloadEncodingFormatsSupportedForA2DP( std::vector *formats) { ALOGV("getHwOffloadEncodingFormatsSupportedForA2DP()"); - char *tok = NULL, *saveptr; status_t status = NO_ERROR; - char encoding_formats_list[PROPERTY_VALUE_MAX]; - audio_format_t format = AUDIO_FORMAT_DEFAULT; - // FIXME This list should not come from a property but the supported encoded - // formats of declared A2DP devices in primary module - property_get("persist.bluetooth.a2dp_offload.cap", encoding_formats_list, ""); - tok = strtok_r(encoding_formats_list, "-", &saveptr); - for (;tok != NULL; tok = strtok_r(NULL, "-", &saveptr)) { - if (strcmp(tok, "sbc") == 0) { - ALOGV("%s: SBC offload supported\n",__func__); - format = AUDIO_FORMAT_SBC; - } else if (strcmp(tok, "aptx") == 0) { - ALOGV("%s: APTX offload supported\n",__func__); - format = AUDIO_FORMAT_APTX; - } else if (strcmp(tok, "aptxhd") == 0) { - ALOGV("%s: APTX HD offload supported\n",__func__); - format = AUDIO_FORMAT_APTX_HD; - } else if (strcmp(tok, "ldac") == 0) { - ALOGV("%s: LDAC offload supported\n",__func__); - format = AUDIO_FORMAT_LDAC; - } else if (strcmp(tok, "aac") == 0) { - ALOGV("%s: AAC offload supported\n",__func__); - format = AUDIO_FORMAT_AAC; - } else { - ALOGE("%s: undefined token - %s\n",__func__, tok); - continue; - } - formats->push_back(format); - } + std::unordered_set formatSet; + sp primaryModule = + mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY); + DeviceVector declaredDevices = primaryModule->getDeclaredDevices().getDevicesFromTypeMask( + AUDIO_DEVICE_OUT_ALL_A2DP); + for (const auto& device : declaredDevices) { + formatSet.insert(device->encodedFormats().begin(), device->encodedFormats().end()); + } + formats->assign(formatSet.begin(), formatSet.end()); return status; } -- GitLab From 56849dfd6b5bd4561d6abf1a19502dcee57dd5c1 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 1 Feb 2019 21:10:08 +0800 Subject: [PATCH 1043/1530] Skip incorrect MPEG audio frame Esqueue may receive incorrect MPEG audio frames with wrong layer or wrong framesize after seek. MP3 decoder will fail to decode and play abnormally. The solution is to skip the frames with wrong layer and wrong framesize. Bug: 124268510 Test: play .ts file, seek several times during playback Change-Id: I7fbe7c68871d4257c95235d172a4f11b405bf6d5 --- media/libstagefright/mpeg2ts/ESQueue.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index b91edcdec6..5af7b23fb9 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -1526,6 +1526,7 @@ sp ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() { header, &frameSize, &samplingRate, &numChannels, &bitrate, &numSamples)) { ALOGE("Failed to get audio frame size"); + mBuffer->setRange(0, 0); return NULL; } @@ -1550,6 +1551,22 @@ sp ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() { return NULL; } + if (mFormat != NULL) { + const char *mime; + if (mFormat->findCString(kKeyMIMEType, &mime)) { + if ((layer == 1) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I)) { + ALOGE("Audio layer is not MPEG_LAYER_I"); + return NULL; + } else if ((layer == 2) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II)) { + ALOGE("Audio layer is not MPEG_LAYER_II"); + return NULL; + } else if ((layer == 3) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { + ALOGE("Audio layer is not AUDIO_MPEG"); + return NULL; + } + } + } + accessUnit->meta()->setInt64("timeUs", timeUs); accessUnit->meta()->setInt32("isSync", 1); -- GitLab From 9119ddd19ecf1ec69909d19fb4b1f60e8e7fa61c Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 28 Jan 2019 10:59:48 +0800 Subject: [PATCH 1044/1530] MKV supports MP3 audio whose CodecID is A_MS/ACM CodecID for MKV MP3 audio is not only A_MPEG/L3, but also AMS_ACM. When CodecID is AMS_ACM, we should check format ID and the file is MP3 if format ID is 0x0055. Need to support this MP3 type. Bug: 123594233 Test: Test MKV file whose audio CodecID is AMS_ACM and format ID is 0x0055, and check if the MP3 audio can be played. Change-Id: I15129e276b19338dc4f3f09eee6020aca5a5fa8e --- media/extractors/mkv/MatroskaExtractor.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 4200a46b21..7239302f57 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1557,6 +1557,21 @@ void MatroskaExtractor::addTracks() { } else if (!strcmp("A_FLAC", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_FLAC); err = addFlacMetadata(meta, codecPrivate, codecPrivateSize); + } else if ((!strcmp("A_MS/ACM", codecID))) { + if ((NULL == codecPrivate) || (codecPrivateSize < 30)) { + ALOGW("unsupported audio: A_MS/ACM has no valid private data: %s, size: %zu", + codecPrivate == NULL ? "null" : "non-null", codecPrivateSize); + continue; + } else { + uint16_t ID = *(uint16_t *)codecPrivate; + if (ID == 0x0055) { + AMediaFormat_setString(meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG); + } else { + ALOGW("A_MS/ACM unsupported type , continue"); + continue; + } + } } else { ALOGW("%s is not supported.", codecID); continue; -- GitLab From 43c73440b34c5a20d412dc93bf0bf35c2c88a1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 8 Nov 2018 08:21:55 +0100 Subject: [PATCH 1045/1530] audiopolicy: add a tag to control device port gain from policy volume API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I52cc1bef8cc8e3f6c4fd15f806bdd42e4756871f Signed-off-by: François Gaffie Bug: 124767636 Test: make --- .../managerdefinitions/include/AudioGain.h | 25 ++++++++++++ .../include/AudioOutputDescriptor.h | 12 +++--- .../managerdefinitions/include/AudioPort.h | 11 ++--- .../src/AudioOutputDescriptor.cpp | 40 +++++++++++++++---- .../managerdefinitions/src/AudioPort.cpp | 10 +++++ .../managerdefinitions/src/Serializer.cpp | 16 ++++++-- .../managerdefault/AudioPolicyManager.cpp | 28 ++++++------- 7 files changed, 105 insertions(+), 37 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioGain.h b/services/audiopolicy/common/managerdefinitions/include/AudioGain.h index 996347b7a1..4af93e136f 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioGain.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioGain.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace android { @@ -59,12 +60,36 @@ public: void getDefaultConfig(struct audio_gain_config *config); status_t checkConfig(const struct audio_gain_config *config); + void setUseForVolume(bool canUseForVolume) { mUseForVolume = canUseForVolume; } + bool canUseForVolume() const { return mUseForVolume; } + const struct audio_gain &getGain() const { return mGain; } private: int mIndex; struct audio_gain mGain; bool mUseInChannelMask; + bool mUseForVolume = false; +}; + +class AudioGains : public std::vector > +{ +public: + bool canUseForVolume() const + { + for (const auto &gain: *this) { + if (gain->canUseForVolume()) { + return true; + } + } + return false; + } + + int32_t add(const sp gain) + { + push_back(gain); + return 0; + } }; } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index cf9519bcf1..704f404d9a 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -98,7 +98,7 @@ public: ActivityTracking::dump(dst, spaces); dst->appendFormat(", Volume: %.03f, MuteCount: %02d\n", mCurVolumeDb, mMuteCount); } - void setVolume(float volume) { mCurVolumeDb = volume; } + void setVolume(float volumeDb) { mCurVolumeDb = volumeDb; } float getVolume() const { return mCurVolumeDb; } private: @@ -156,7 +156,7 @@ public: virtual bool isDuplicated() const { return false; } virtual uint32_t latency() { return 0; } virtual bool isFixedVolume(audio_devices_t device); - virtual bool setVolume(float volume, + virtual bool setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, @@ -219,10 +219,10 @@ public: { return mVolumeActivities[vs].decMuteCount(); } - void setCurVolume(VolumeSource vs, float volume) + void setCurVolume(VolumeSource vs, float volumeDb) { // Even if not activity for this group registered, need to create anyway - mVolumeActivities[vs].setVolume(volume); + mVolumeActivities[vs].setVolume(volumeDb); } float getCurVolume(VolumeSource vs) const { @@ -327,7 +327,7 @@ public: setClientActive(client, false); } } - virtual bool setVolume(float volume, + virtual bool setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, @@ -401,7 +401,7 @@ public: void dump(String8 *dst) const override; - virtual bool setVolume(float volume, + virtual bool setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h index 1b5a2d6d8a..2d182bdb27 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -18,6 +18,7 @@ #include "AudioCollections.h" #include "AudioProfile.h" +#include "AudioGain.h" #include "HandleGenerator.h" #include #include @@ -29,9 +30,7 @@ namespace android { class HwModule; -class AudioGain; class AudioRoute; -typedef Vector > AudioGainCollection; class AudioPort : public virtual RefBase, private HandleGenerator { @@ -49,8 +48,8 @@ public: virtual const String8 getTagName() const = 0; - void setGains(const AudioGainCollection &gains) { mGains = gains; } - const AudioGainCollection &getGains() const { return mGains; } + void setGains(const AudioGains &gains) { mGains = gains; } + const AudioGains &getGains() const { return mGains; } virtual void setFlags(uint32_t flags) { @@ -138,7 +137,7 @@ public: void log(const char* indent) const; - AudioGainCollection mGains; // gain controllers + AudioGains mGains; // gain controllers private: void pickChannelMask(audio_channel_mask_t &channelMask, const ChannelsVector &channelMasks) const; @@ -165,6 +164,8 @@ public: return (other != 0) && (other->getAudioPort() != 0) && (getAudioPort() != 0) && (other->getAudioPort()->getModuleHandle() == getAudioPort()->getModuleHandle()); } + bool hasGainController(bool canUseForVolume = false) const; + unsigned int mSamplingRate = 0u; audio_format_t mFormat = AUDIO_FORMAT_INVALID; audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 7293bc4143..fd33649ff1 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -149,7 +149,7 @@ bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused) return false; } -bool AudioOutputDescriptor::setVolume(float volume, +bool AudioOutputDescriptor::setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device __unused, uint32_t delayMs, @@ -158,9 +158,9 @@ bool AudioOutputDescriptor::setVolume(float volume, // We actually change the volume if: // - the float value returned by computeVolume() changed // - the force flag is set - if (volume != getCurVolume(static_cast(stream)) || force) { - ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs); - setCurVolume(static_cast(stream), volume); + if (volumeDb != getCurVolume(static_cast(stream)) || force) { + ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volumeDb, delayMs); + setCurVolume(static_cast(stream), volumeDb); return true; } return false; @@ -388,15 +388,39 @@ void SwAudioOutputDescriptor::toAudioPort( mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL; } -bool SwAudioOutputDescriptor::setVolume(float volume, +bool SwAudioOutputDescriptor::setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, bool force) { - if (!AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force)) { + if (!AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force)) { return false; } + if (!devices().isEmpty()) { + // Assume first device to check upon Gain Crontroller availability + const auto &devicePort = devices().itemAt(0); + ALOGV("%s: device %s hasGC %d", __FUNCTION__, + devicePort->toString().c_str(), devices().itemAt(0)->hasGainController(true)); + if (devicePort->hasGainController(true)) { + // @todo: default stream volume to max (0) when using HW Port gain? + float volumeAmpl = Volume::DbToAmpl(0); + mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + + AudioGains gains = devicePort->getGains(); + int gainMinValueInMb = gains[0]->getMinValueInMb(); + int gainMaxValueInMb = gains[0]->getMaxValueInMb(); + int gainStepValueInMb = gains[0]->getStepValueInMb(); + int gainValueMb = ((volumeDb * 100)/ gainStepValueInMb) * gainStepValueInMb; + gainValueMb = std::max(gainMinValueInMb, std::min(gainValueMb, gainMaxValueInMb)); + + audio_port_config config = {}; + devicePort->toAudioPortConfig(&config); + config.config_mask = AUDIO_PORT_CONFIG_GAIN; + config.gain.values[0] = gainValueMb; + return mClientInterface->setAudioPortConfig(&config, 0) == NO_ERROR; + } + } // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled float volumeAmpl = Volume::DbToAmpl(getCurVolume(static_cast(stream))); if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { @@ -591,13 +615,13 @@ void HwAudioOutputDescriptor::toAudioPort( } -bool HwAudioOutputDescriptor::setVolume(float volume, +bool HwAudioOutputDescriptor::setVolume(float volumeDb, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, bool force) { - bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force); + bool changed = AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force); if (changed) { // TODO: use gain controller on source device if any to adjust volume diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp index 9fcf5e7a9c..a66c6957f9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -479,4 +479,14 @@ void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, dstConfig, srcConfig, AUDIO_PORT_CONFIG_FLAGS, { AUDIO_INPUT_FLAG_NONE }); } +bool AudioPortConfig::hasGainController(bool canUseForVolume) const +{ + sp audioport = getAudioPort(); + if (audioport == nullptr) { + return false; + } + return canUseForVolume ? audioport->getGains().canUseForVolume() + : audioport->getGains().size() > 0; +} + } // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp index 81d3968c76..5f820c2145 100644 --- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp @@ -64,7 +64,7 @@ struct StdCollectionTraits { } }; -struct AudioGainTraits : public AndroidCollectionTraits +struct AudioGainTraits : public AndroidCollectionTraits { static constexpr const char *tag = "gain"; static constexpr const char *collectionTag = "gains"; @@ -84,6 +84,9 @@ struct AudioGainTraits : public AndroidCollectionTraits deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); @@ -375,9 +378,14 @@ Return AudioGainTraits::deserialize(const xmlNode *cur if (!maxRampMsLiteral.empty() && convertTo(maxRampMsLiteral, maxRampMs)) { gain->setMaxRampInMs(maxRampMs); } - ALOGV("%s: adding new gain mode %08x channel mask %08x min mB %d max mB %d", __func__, - gain->getMode(), gain->getChannelMask(), gain->getMinValueInMb(), - gain->getMaxValueInMb()); + std::string useForVolumeLiteral = getXmlAttribute(cur, Attributes::useForVolume); + bool useForVolume = false; + if (!useForVolumeLiteral.empty() && convertTo(useForVolumeLiteral, useForVolume)) { + gain->setUseForVolume(useForVolume); + } + ALOGV("%s: adding new gain mode %08x channel mask %08x min mB %d max mB %d UseForVolume: %d", + __func__, gain->getMode(), gain->getChannelMask(), gain->getMinValueInMb(), + gain->getMaxValueInMb(), useForVolume); if (gain->getMode() != 0) { return gain; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ce305a474b..063bb2b2f6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5628,7 +5628,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, audio_devices_t device) { auto &curves = getVolumeCurves(stream); - float volumeDB = curves.volIndexToDb(Volume::getDeviceCategory(device), index); + float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index); // handle the case of accessibility active while a ringtone is playing: if the ringtone is much // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch @@ -5638,7 +5638,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) && isStreamActive(AUDIO_STREAM_RING, 0)) { const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device); - return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB; + return ringVolumeDB - 4 > volumeDb ? ringVolumeDB - 4 : volumeDb; } // in-call: always cap volume by voice volume + some low headroom @@ -5657,10 +5657,10 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; - if (volumeDB > maxVoiceVolDb) { + if (volumeDb > maxVoiceVolDb) { ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f", - stream, volumeDB, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb); - volumeDB = maxVoiceVolDb; + stream, volumeDb, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb); + volumeDb = maxVoiceVolDb; } } break; default: @@ -5693,7 +5693,7 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, // just stopped if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || mLimitRingtoneVolume) { - volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; + volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; audio_devices_t musicDevice = mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, true /*fromCache*/).types(); @@ -5702,29 +5702,29 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, musicDevice); float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; - if (volumeDB > minVolDB) { - volumeDB = minVolDB; + if (volumeDb > minVolDB) { + volumeDb = minVolDB; ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB); } if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) { // on A2DP, also ensure notification volume is not too low compared to media when // intended to be played - if ((volumeDB > -96.0f) && - (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDB)) { + if ((volumeDb > -96.0f) && + (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) { ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f", stream, device, - volumeDB, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); - volumeDB = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; + volumeDb, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); + volumeDb = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; } } } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) || (stream != AUDIO_STREAM_ALARM && stream != AUDIO_STREAM_RING)) { - volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; + volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; } } - return volumeDB; + return volumeDb; } int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, -- GitLab From b24806f9d0d0dcbac7f55439c899c99bdd4c15d9 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 27 Feb 2019 08:57:43 -0800 Subject: [PATCH 1046/1530] Camera: ndk: Improve docs for activePhysicalId metadata key Test: Read updated docs Bug: 125346954 Change-Id: I0f0c071a98fb5f23c3db257d3e5cb63a96e8fad8 --- camera/ndk/include/camera/NdkCameraMetadataTags.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 8c19e1d6cb..b200abf4e8 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -5688,13 +5688,17 @@ typedef enum acamera_metadata_tag { * *

    The ID of the active physical camera that's backing the logical camera. All camera * streams and metadata that are not physical camera specific will be originating from this - * physical camera. This must be one of valid physical IDs advertised in the physicalIds - * static tag.

    + * physical camera.

    *

    For a logical camera made up of physical cameras where each camera's lenses have * different characteristics, the camera device may choose to switch between the physical * cameras when application changes FOCAL_LENGTH or SCALER_CROP_REGION. * At the time of lens switch, this result metadata reflects the new active physical camera * ID.

    + *

    This key will be available if the camera device advertises this key via {@link ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS }. + * When available, this must be one of valid physical IDs backing this logical multi-camera. + * If this key is not available for a logical multi-camera, the camera device implementation + * may still switch between different active physical cameras based on use case, but the + * current active physical camera information won't be available to the application.

    */ ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID = // byte ACAMERA_LOGICAL_MULTI_CAMERA_START + 2, -- GitLab From f23a771ca699ddb84ae18ce1051491d735e55cb3 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 28 Feb 2019 17:15:40 -0800 Subject: [PATCH 1047/1530] audio policy: fix enforced audible playback When selecting device on a given output stream, check if an enforced audible sound is active on any other output stream and not just on this stream. Bug: 124063442 Test: set property audio.camerasound.force=true and test music + shutter sound concurrency. Change-Id: Ic7f548df2b67041d532afc68685cdbeb3cb3f125 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ce305a474b..e546e2d539 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5155,7 +5155,7 @@ DeviceVector AudioPolicyManager::getNewOutputDevices(const spisStrategyActive(productStrategy)) { // Retrieval of devices for voice DL is done on primary output profile, cannot -- GitLab From 3c9b0120e7ea330b2b638c2457fddd07d94a86ee Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 1 Mar 2019 10:17:32 -0800 Subject: [PATCH 1048/1530] Mediaserver back to 32-bits force mediaserver to 32-bits, 64-bit version failed wider testing. Bug: 126502613 Bug: 16890215 Test: use camera app to record video Test: android.media.cts.MediaPlayer2Test#testRecordedVideoPlayback0 Test: android.media.cts.MediaPlayerTest#testRecordedVideoPlayback0 --- media/mediaserver/Android.bp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp index f01947a841..8377723849 100644 --- a/media/mediaserver/Android.bp +++ b/media/mediaserver/Android.bp @@ -21,6 +21,7 @@ cc_binary { "libutils", "libbinder", "libandroidicu", + "android.hardware.media.omx@1.0", ], static_libs: [ @@ -33,6 +34,9 @@ cc_binary { "frameworks/av/services/mediaresourcemanager", ], + // back to 32-bit, b/126502613 + compile_multilib: "32", + init_rc: ["mediaserver.rc"], cflags: [ -- GitLab From 07a130e3a6465fff2682a76ab7a2475e896c5585 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Fri, 1 Mar 2019 10:30:02 -0800 Subject: [PATCH 1049/1530] Replace postFrameCallback with postFrameCallback64 Bug: 123660572 Test: atest CtsMediaTestCases:android.media.cts.MediaPlayerTest -- --abi armeabi-v7a Change-Id: I4eb25b20449101528b72172de40cbc51f745ed22 --- media/libstagefright/VideoFrameScheduler2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/VideoFrameScheduler2.cpp b/media/libstagefright/VideoFrameScheduler2.cpp index fc76904571..23671f28a6 100644 --- a/media/libstagefright/VideoFrameScheduler2.cpp +++ b/media/libstagefright/VideoFrameScheduler2.cpp @@ -160,13 +160,13 @@ void VsyncTracker::updateModelLocked() { mPhase = (long) (atan2(sampleAvgY, sampleAvgX) / scale); } -static void frameCallback(long frameTimeNanos, void* data) { +static void frameCallback(int64_t frameTimeNanos, void* data) { if (data == NULL) { return; } sp vsyncTracker(static_cast(data)); vsyncTracker->addSample(frameTimeNanos); - AChoreographer_postFrameCallback(AChoreographer_getInstance(), + AChoreographer_postFrameCallback64(AChoreographer_getInstance(), frameCallback, static_cast(vsyncTracker.get())); } @@ -247,7 +247,7 @@ status_t ChoreographerThread::readyToRun() { if (AChoreographer_getInstance() == NULL) { return NO_INIT; } - AChoreographer_postFrameCallback(AChoreographer_getInstance(), frameCallback, mData); + AChoreographer_postFrameCallback64(AChoreographer_getInstance(), frameCallback, mData); return OK; } -- GitLab From 2dc962bf86d9d6f46cfe83400ae3d1e66b550ca0 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 1 Mar 2019 08:25:25 -0800 Subject: [PATCH 1050/1530] audio policy: fix remote submix capture concurrency Capture from REMOTE_SUBMIX audio source should always be allowed in concurrency situation. Also include FM_TUNER as virtual source that should never be silenced. Also remove voice call capture sources from privacy senitive sources as capturing a voice call does not constitute in itself a situation that should prevent other apps from accessing the mic. Bug: 121200677 Test: verify music playback in auto projected mode. Change-Id: Idbd9e9f55f29fb8a8e2b76cea5b85f0d9a555788 --- .../service/AudioPolicyService.cpp | 30 +++++++++++++------ .../audiopolicy/service/AudioPolicyService.h | 3 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index cf9cf71b0e..4e415bc050 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -450,7 +450,7 @@ void AudioPolicyService::updateUidStates_l() for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; if (!current->active) continue; - if (isPrivacySensitive(current->attributes.source)) { + if (isPrivacySensitiveSource(current->attributes.source)) { if (current->startTimeNs > latestSensitiveStartNs) { latestSensitiveActive = current; latestSensitiveStartNs = current->startTimeNs; @@ -489,7 +489,10 @@ void AudioPolicyService::updateUidStates_l() bool isLatest = current == latestActive; bool isLatestSensitive = current == latestSensitiveActive; bool forceIdle = true; - if (mUidPolicy->isAssistantUid(current->uid)) { + + if (isVirtualSource(source)) { + forceIdle = false; + } else if (mUidPolicy->isAssistantUid(current->uid)) { if (isA11yOnTop) { if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { forceIdle = false; @@ -505,10 +508,6 @@ void AudioPolicyService::updateUidStates_l() (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { forceIdle = false; } - } else if (source == AUDIO_SOURCE_VOICE_DOWNLINK || - source == AUDIO_SOURCE_VOICE_CALL || - (source == AUDIO_SOURCE_VOICE_UPLINK)) { - forceIdle = false; } else { if (!isAssistantOnTop && (isOnTop || isLatest) && (!isSensitiveActive || isLatestSensitive)) { @@ -542,14 +541,27 @@ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { } /* static */ -bool AudioPolicyService::isPrivacySensitive(audio_source_t source) +bool AudioPolicyService::isPrivacySensitiveSource(audio_source_t source) +{ + switch (source) { + case AUDIO_SOURCE_CAMCORDER: + case AUDIO_SOURCE_VOICE_COMMUNICATION: + return true; + default: + break; + } + return false; +} + +/* static */ +bool AudioPolicyService::isVirtualSource(audio_source_t source) { switch (source) { case AUDIO_SOURCE_VOICE_UPLINK: case AUDIO_SOURCE_VOICE_DOWNLINK: case AUDIO_SOURCE_VOICE_CALL: - case AUDIO_SOURCE_CAMCORDER: - case AUDIO_SOURCE_VOICE_COMMUNICATION: + case AUDIO_SOURCE_REMOTE_SUBMIX: + case AUDIO_SOURCE_FM_TUNER: return true; default: break; diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 5888841707..6d64671c09 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -326,7 +326,8 @@ private: void silenceAllRecordings_l(); - static bool isPrivacySensitive(audio_source_t source); + static bool isPrivacySensitiveSource(audio_source_t source); + static bool isVirtualSource(audio_source_t source); // If recording we need to make sure the UID is allowed to do that. If the UID is idle // then it cannot record and gets buffers with zeros - silence. As soon as the UID -- GitLab From 84be578ec6387a33e54b716acbb4e95be3b35b6e Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Fri, 1 Mar 2019 11:47:02 -0800 Subject: [PATCH 1051/1530] Camera: improve error handling when HAL crash Return buffers managed by HAL buffer manager in disconnect. Test: kill HAL process and check for buffer leak in cameraserver Bug: 126889012 Change-Id: I83173c5eaae13ee11eb3f185e7204a2dd8855b4e --- .../device3/Camera3Device.cpp | 62 +++++++++++++++++-- .../libcameraservice/device3/Camera3Device.h | 14 +++-- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 923d17a281..bc76ada8d9 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -58,6 +58,8 @@ #include "CameraService.h" #include "utils/CameraThreadState.h" +#include + using namespace android::camera3; using namespace android::hardware::camera; using namespace android::hardware::camera::device::V3_2; @@ -1094,7 +1096,7 @@ hardware::Return Camera3Device::requestStreamBuffers( hBuf.acquireFence.setTo(acquireFence, /*shouldOwn*/true); hBuf.releaseFence = nullptr; - res = mInterface->pushInflightRequestBuffer(bufferId, buffer); + res = mInterface->pushInflightRequestBuffer(bufferId, buffer, streamId); if (res != OK) { ALOGE("%s: Can't get register request buffers for stream %d: %s (%d)", __FUNCTION__, streamId, strerror(-res), res); @@ -3270,7 +3272,15 @@ void Camera3Device::flushInflightRequests() { std::vector> inflightKeys; mInterface->getInflightBufferKeys(&inflightKeys); - int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1; + // Inflight buffers for HAL buffer manager + std::vector inflightRequestBufferKeys; + mInterface->getInflightRequestBufferKeys(&inflightRequestBufferKeys); + + // (streamId, frameNumber, buffer_handle_t*) tuple for all inflight buffers. + // frameNumber will be -1 for buffers from HAL buffer manager + std::vector> inflightBuffers; + inflightBuffers.reserve(inflightKeys.size() + inflightRequestBufferKeys.size()); + for (auto& pair : inflightKeys) { int32_t frameNumber = pair.first; int32_t streamId = pair.second; @@ -3281,6 +3291,26 @@ void Camera3Device::flushInflightRequests() { __FUNCTION__, frameNumber, streamId); continue; } + inflightBuffers.push_back(std::make_tuple(streamId, frameNumber, buffer)); + } + + for (auto& bufferId : inflightRequestBufferKeys) { + int32_t streamId = -1; + buffer_handle_t* buffer = nullptr; + status_t res = mInterface->popInflightRequestBuffer(bufferId, &buffer, &streamId); + if (res != OK) { + ALOGE("%s: cannot find in-flight buffer %" PRIu64, __FUNCTION__, bufferId); + continue; + } + inflightBuffers.push_back(std::make_tuple(streamId, /*frameNumber*/-1, buffer)); + } + + int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1; + for (auto& tuple : inflightBuffers) { + status_t res = OK; + int32_t streamId = std::get<0>(tuple); + int32_t frameNumber = std::get<1>(tuple); + buffer_handle_t* buffer = std::get<2>(tuple); camera3_stream_buffer_t streamBuffer; streamBuffer.buffer = buffer; @@ -4583,6 +4613,17 @@ void Camera3Device::HalInterface::getInflightBufferKeys( return; } +void Camera3Device::HalInterface::getInflightRequestBufferKeys( + std::vector* out) { + std::lock_guard lock(mRequestedBuffersLock); + out->clear(); + out->reserve(mRequestedBuffers.size()); + for (auto& pair : mRequestedBuffers) { + out->push_back(pair.first); + } + return; +} + status_t Camera3Device::HalInterface::pushInflightBufferLocked( int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer, int acquireFence) { uint64_t key = static_cast(frameNumber) << 32 | static_cast(streamId); @@ -4610,9 +4651,9 @@ status_t Camera3Device::HalInterface::popInflightBuffer( } status_t Camera3Device::HalInterface::pushInflightRequestBuffer( - uint64_t bufferId, buffer_handle_t* buf) { + uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) { std::lock_guard lock(mRequestedBuffersLock); - auto pair = mRequestedBuffers.insert({bufferId, buf}); + auto pair = mRequestedBuffers.insert({bufferId, {streamId, buf}}); if (!pair.second) { ALOGE("%s: bufId %" PRIu64 " is already inflight!", __FUNCTION__, bufferId); @@ -4623,7 +4664,13 @@ status_t Camera3Device::HalInterface::pushInflightRequestBuffer( // Find and pop a buffer_handle_t based on bufferId status_t Camera3Device::HalInterface::popInflightRequestBuffer( - uint64_t bufferId, /*out*/ buffer_handle_t **buffer) { + uint64_t bufferId, + /*out*/ buffer_handle_t** buffer, + /*optional out*/ int32_t* streamId) { + if (buffer == nullptr) { + ALOGE("%s: buffer (%p) must not be null", __FUNCTION__, buffer); + return BAD_VALUE; + } std::lock_guard lock(mRequestedBuffersLock); auto it = mRequestedBuffers.find(bufferId); if (it == mRequestedBuffers.end()) { @@ -4631,7 +4678,10 @@ status_t Camera3Device::HalInterface::popInflightRequestBuffer( __FUNCTION__, bufferId); return BAD_VALUE; } - *buffer = it->second; + *buffer = it->second.second; + if (streamId != nullptr) { + *streamId = it->second.first; + } mRequestedBuffers.erase(it); return OK; } diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index b25d89d443..d3bb21228a 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -320,16 +320,22 @@ class Camera3Device : status_t popInflightBuffer(int32_t frameNumber, int32_t streamId, /*out*/ buffer_handle_t **buffer); - // Register a bufId/buffer_handle_t to inflight request buffer - status_t pushInflightRequestBuffer(uint64_t bufferId, buffer_handle_t* buf); + // Register a bufId (streamId, buffer_handle_t) to inflight request buffer + status_t pushInflightRequestBuffer( + uint64_t bufferId, buffer_handle_t* buf, int32_t streamId); // Find a buffer_handle_t based on bufferId - status_t popInflightRequestBuffer(uint64_t bufferId, /*out*/ buffer_handle_t **buffer); + status_t popInflightRequestBuffer(uint64_t bufferId, + /*out*/ buffer_handle_t** buffer, + /*optional out*/ int32_t* streamId = nullptr); // Get a vector of (frameNumber, streamId) pair of currently inflight // buffers void getInflightBufferKeys(std::vector>* out); + // Get a vector of bufferId of currently inflight buffers + void getInflightRequestBufferKeys(std::vector* out); + static const uint64_t BUFFER_ID_NO_BUFFER = 0; private: // Always valid @@ -398,7 +404,7 @@ class Camera3Device : // Buffers given to HAL through requestStreamBuffer API std::mutex mRequestedBuffersLock; - std::unordered_map mRequestedBuffers; + std::unordered_map> mRequestedBuffers; uint32_t mNextStreamConfigCounter = 1; -- GitLab From 9f4c0a33dc2bc348b4bf370e32aabbcf94aa8f74 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Mon, 25 Feb 2019 23:09:42 -0800 Subject: [PATCH 1052/1530] Add asInputSink() to Component Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 126296132 Change-Id: I43b933250a021d99ab2aced74a8ed7063337d395 --- media/codec2/hidl/1.0/utils/Component.cpp | 24 +++++++++---------- .../utils/include/codec2/hidl/1.0/Component.h | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp index 0473b57b19..f3bf6f7fbd 100644 --- a/media/codec2/hidl/1.0/utils/Component.cpp +++ b/media/codec2/hidl/1.0/utils/Component.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -298,19 +297,12 @@ Return Component::setOutputSurface( Return Component::connectToInputSurface( const sp& inputSurface, connectToInputSurface_cb _hidl_cb) { - sp sink; - { - std::lock_guard lock(mSinkMutex); - if (!mSink) { - mSink = new Sink(shared_from_this()); - } - sink = mSink; - } Status status; sp connection; - auto transStatus = inputSurface->connect(sink, - [&status, &connection](Status s, - const sp& c) { + auto transStatus = inputSurface->connect( + asInputSink(), + [&status, &connection]( + Status s, const sp& c) { status = s; connection = c; } @@ -454,6 +446,14 @@ Return> Component::getInterface() { return sp(mInterface); } +Return> Component::asInputSink() { + std::lock_guard lock(mSinkMutex); + if (!mSink) { + mSink = new Sink(shared_from_this()); + } + return {mSink}; +} + std::shared_ptr Component::findLocalComponent( const sp& sink) { return Component::Sink::findLocalComponent(sink); diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h index 4ac95c5d04..e444013271 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -94,6 +95,7 @@ struct Component : public IComponent, virtual Return reset() override; virtual Return release() override; virtual Return> getInterface() override; + virtual Return> asInputSink() override; // Returns a C2Component associated to the given sink if the sink is indeed // a local component. Returns nullptr otherwise. -- GitLab From 53722fa75c1b74b7accdf2293fda9948beaeb461 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 22 Feb 2019 17:47:20 -0800 Subject: [PATCH 1053/1530] Camera: Update listeners about permission changes Camera service listeners must be able to receive information about camera access permission changes. Bug: 121379978 Test: Camera CTS Change-Id: I2e13fdd35a267901a3caa0e0ce78ab1cea83e7ab --- .../aidl/android/hardware/ICameraService.aidl | 2 +- .../hardware/ICameraServiceListener.aidl | 7 ++ camera/ndk/impl/ACameraManager.h | 5 ++ camera/tests/CameraBinderTests.cpp | 5 ++ .../camera/libcameraservice/CameraService.cpp | 88 +++++++++++++++++-- .../camera/libcameraservice/CameraService.h | 37 +++++++- .../hidl/AidlCameraServiceListener.h | 4 + 7 files changed, 138 insertions(+), 10 deletions(-) diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl index 0e969c7662..3e8992aaaf 100644 --- a/camera/aidl/android/hardware/ICameraService.aidl +++ b/camera/aidl/android/hardware/ICameraService.aidl @@ -108,7 +108,7 @@ interface ICameraService * * Also returns the set of currently-known camera IDs and state of each device. * Adding a listener will trigger the torch status listener to fire for all - * devices that have a flash unit + * devices that have a flash unit. */ CameraStatus[] addListener(ICameraServiceListener listener); diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl index f871ce41c9..e9dcbdb6f6 100644 --- a/camera/aidl/android/hardware/ICameraServiceListener.aidl +++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl @@ -76,4 +76,11 @@ interface ICameraServiceListener const int TORCH_STATUS_UNKNOWN = -1; oneway void onTorchStatusChanged(int status, String cameraId); + + /** + * Notify registered clients about camera access priority changes. + * Clients which were previously unable to open a certain camera device + * can retry after receiving this callback. + */ + oneway void onCameraAccessPrioritiesChanged(); } diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index 55bfa7ea54..c3407f0395 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -86,6 +86,11 @@ class CameraManagerGlobal final : public RefBase { return binder::Status::ok(); } + // Access priority API not implemented yet + virtual binder::Status onCameraAccessPrioritiesChanged() { + return binder::Status::ok(); + } + private: const wp mCameraManager; }; diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp index 8534b289ea..8fe029aea7 100644 --- a/camera/tests/CameraBinderTests.cpp +++ b/camera/tests/CameraBinderTests.cpp @@ -90,6 +90,11 @@ public: return binder::Status::ok(); }; + virtual binder::Status onCameraAccessPrioritiesChanged() { + // No op + return binder::Status::ok(); + } + bool waitForNumCameras(size_t num) const { Mutex::Autolock l(mLock); diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index e06897f492..62ec955732 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -227,7 +227,7 @@ void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeS Mutex::Autolock lock(mStatusListenerLock); for (auto& i : mListenerList) { - i.second->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); + i.second->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); } } @@ -1654,6 +1654,18 @@ Status CameraService::notifySystemEvent(int32_t eventId, return Status::ok(); } +void CameraService::notifyMonitoredUids() { + Mutex::Autolock lock(mStatusListenerLock); + + for (const auto& it : mListenerList) { + auto ret = it.second->getListener()->onCameraAccessPrioritiesChanged(); + if (!ret.isOk()) { + ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__, + ret.exceptionCode()); + } + } +} + Status CameraService::notifyDeviceStateChange(int64_t newState) { const int pid = CameraThreadState::getCallingPid(); const int selfPid = getpid(); @@ -1721,15 +1733,25 @@ Status CameraService::addListenerHelper(const sp& listen { Mutex::Autolock lock(mStatusListenerLock); - for (auto& it : mListenerList) { - if (IInterface::asBinder(it.second) == IInterface::asBinder(listener)) { + for (const auto &it : mListenerList) { + if (IInterface::asBinder(it.second->getListener()) == IInterface::asBinder(listener)) { ALOGW("%s: Tried to add listener %p which was already subscribed", __FUNCTION__, listener.get()); return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered"); } } - mListenerList.emplace_back(isVendorListener, listener); + auto clientUid = CameraThreadState::getCallingUid(); + sp serviceListener = new ServiceListener(this, listener, clientUid); + auto ret = serviceListener->initialize(); + if (ret != NO_ERROR) { + String8 msg = String8::format("Failed to initialize service listener: %s (%d)", + strerror(-ret), ret); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string()); + } + mListenerList.emplace_back(isVendorListener, serviceListener); + mUidPolicy->registerMonitorUid(clientUid); } /* Collect current devices and status */ @@ -1776,7 +1798,9 @@ Status CameraService::removeListener(const sp& listener) { Mutex::Autolock lock(mStatusListenerLock); for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { - if (IInterface::asBinder(it->second) == IInterface::asBinder(listener)) { + if (IInterface::asBinder(it->second->getListener()) == IInterface::asBinder(listener)) { + mUidPolicy->unregisterMonitorUid(it->second->getListenerUid()); + IInterface::asBinder(listener)->unlinkToDeath(it->second); mListenerList.erase(it); return Status::ok(); } @@ -2396,6 +2420,8 @@ status_t CameraService::BasicClient::startCameraOps() { sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_OPEN, mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel); + sCameraService->mUidPolicy->registerMonitorUid(mClientUid); + return OK; } @@ -2433,6 +2459,8 @@ status_t CameraService::BasicClient::finishCameraOps() { } mOpsCallback.clear(); + sCameraService->mUidPolicy->unregisterMonitorUid(mClientUid); + return OK; } @@ -2523,7 +2551,7 @@ void CameraService::UidPolicy::registerSelf() { if (mRegistered) return; am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_IDLE - | ActivityManager::UID_OBSERVER_ACTIVE, + | ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE, ActivityManager::PROCESS_STATE_UNKNOWN, String16("cameraserver")); status_t res = am.linkToDeath(this); @@ -2569,6 +2597,51 @@ void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) { } } +void CameraService::UidPolicy::onUidStateChanged(uid_t uid, int32_t procState, + int64_t /*procStateSeq*/) { + bool procStateChange = false; + { + Mutex::Autolock _l(mUidLock); + if ((mMonitoredUids.find(uid) != mMonitoredUids.end()) && + (mMonitoredUids[uid].first != procState)) { + mMonitoredUids[uid].first = procState; + procStateChange = true; + } + } + + if (procStateChange) { + sp service = mService.promote(); + if (service != nullptr) { + service->notifyMonitoredUids(); + } + } +} + +void CameraService::UidPolicy::registerMonitorUid(uid_t uid) { + Mutex::Autolock _l(mUidLock); + auto it = mMonitoredUids.find(uid); + if (it != mMonitoredUids.end()) { + it->second.second++; + } else { + mMonitoredUids.emplace( + std::pair> (uid, + std::pair (ActivityManager::PROCESS_STATE_NONEXISTENT, 1))); + } +} + +void CameraService::UidPolicy::unregisterMonitorUid(uid_t uid) { + Mutex::Autolock _l(mUidLock); + auto it = mMonitoredUids.find(uid); + if (it != mMonitoredUids.end()) { + it->second.second--; + if (it->second.second == 0) { + mMonitoredUids.erase(it); + } + } else { + ALOGE("%s: Trying to unregister uid: %d which is not monitored!", __FUNCTION__, uid); + } +} + bool CameraService::UidPolicy::isUidActive(uid_t uid, String16 callingPackage) { Mutex::Autolock _l(mUidLock); return isUidActiveLocked(uid, callingPackage); @@ -3118,7 +3191,8 @@ void CameraService::updateStatus(StatusInternal status, const String8& cameraId, cameraId.c_str()); continue; } - listener.second->onStatusChanged(mapToInterface(status), String16(cameraId)); + listener.second->getListener()->onStatusChanged(mapToInterface(status), + String16(cameraId)); } }); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index cf0cef84ec..65727ecec5 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -179,6 +179,9 @@ public: /*out*/ std::vector* cameraStatuses, bool isVendor = false); + // Monitored UIDs availability notification + void notifyMonitoredUids(); + ///////////////////////////////////////////////////////////////////// // Client functionality @@ -543,11 +546,14 @@ private: void onUidGone(uid_t uid, bool disabled); void onUidActive(uid_t uid); void onUidIdle(uid_t uid, bool disabled); - void onUidStateChanged(uid_t uid __unused, int32_t procState __unused, int64_t procStateSeq __unused) {} + void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq); void addOverrideUid(uid_t uid, String16 callingPackage, bool active); void removeOverrideUid(uid_t uid, String16 callingPackage); + void registerMonitorUid(uid_t uid); + void unregisterMonitorUid(uid_t uid); + // IBinder::DeathRecipient implementation virtual void binderDied(const wp &who); private: @@ -558,6 +564,8 @@ private: bool mRegistered; wp mService; std::unordered_set mActiveUids; + // Monitored uid map to cached procState and refCount pair + std::unordered_map> mMonitoredUids; std::unordered_map mOverrideUids; }; // class UidPolicy @@ -790,8 +798,33 @@ private: sp mCameraProviderManager; + class ServiceListener : public virtual IBinder::DeathRecipient { + public: + ServiceListener(sp parent, sp listener, + int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {} + + status_t initialize() { + return IInterface::asBinder(mListener)->linkToDeath(this); + } + + virtual void binderDied(const wp &/*who*/) { + auto parent = mParent.promote(); + if (parent.get() != nullptr) { + parent->removeListener(mListener); + } + } + + int getListenerUid() { return mListenerUid; } + sp getListener() { return mListener; } + + private: + wp mParent; + sp mListener; + int mListenerUid; + }; + // Guarded by mStatusListenerMutex - std::vector>> mListenerList; + std::vector>> mListenerList; Mutex mStatusListenerLock; diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h index ca9143d12c..0f6be79be0 100644 --- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h +++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h @@ -50,6 +50,10 @@ struct H2BCameraServiceListener : virtual ::android::binder::Status onTorchStatusChanged( int32_t status, const ::android::String16& cameraId) override; + virtual binder::Status onCameraAccessPrioritiesChanged() { + // TODO: no implementation yet. + return binder::Status::ok(); + } }; } // implementation -- GitLab From e5c53f762febd3ccfe9e7b516a0545934509ccdb Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Sun, 3 Mar 2019 17:30:18 -0800 Subject: [PATCH 1054/1530] Make Codec2 and BufferPool modules vndk Test: Builds Bug: 112362730 Change-Id: I4db5e6af28e05d67584c2ad64766f582ecdf1e17 --- media/bufferpool/2.0/Android.bp | 3 +++ media/codec2/core/Android.bp | 3 +++ media/codec2/hidl/1.0/utils/Android.bp | 3 +++ media/codec2/vndk/Android.bp | 3 +++ 4 files changed, 12 insertions(+) diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp index cd4e06e8c3..c71ac17d32 100644 --- a/media/bufferpool/2.0/Android.bp +++ b/media/bufferpool/2.0/Android.bp @@ -1,6 +1,9 @@ cc_library { name: "libstagefright_bufferpool@2.0", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "Accessor.cpp", "AccessorImpl.cpp", diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp index b723755b1d..a7e89976ca 100644 --- a/media/codec2/core/Android.bp +++ b/media/codec2/core/Android.bp @@ -7,6 +7,9 @@ cc_library_headers { cc_library_shared { name: "libcodec2", vendor_available: true, + vndk: { + enabled: true, + }, srcs: ["C2.cpp"], diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index d0296a5fcd..f5aa65b961 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -3,6 +3,9 @@ cc_library { name: "libcodec2_hidl@1.0", vendor_available: true, + vndk: { + enabled: true, + }, defaults: ["hidl_defaults"], diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index e0b1355da4..ab6a105e07 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -14,6 +14,9 @@ cc_library_headers { cc_library_shared { name: "libcodec2_vndk", vendor_available: true, + vndk: { + enabled: true, + }, srcs: [ "C2AllocatorIon.cpp", -- GitLab From 326bea5e58c6a7618804834824f3d8d731b57a58 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Mon, 4 Mar 2019 13:27:47 -0800 Subject: [PATCH 1055/1530] Set the correct message for OMX's death Test: Play a video, then kill media.codec. Bug: 125567263 Change-Id: I4fffe37b75f21cd15d9ad667b264bf15660887b3 --- media/libstagefright/ACodec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 9d3338bf45..13903ba046 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -6667,7 +6667,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) { return false; } - mDeathNotifier = new DeathNotifier(notify); + mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec)); auto tOmxNode = omxNode->getHalInterface(); if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) { mDeathNotifier.clear(); -- GitLab From 6bd3bdf056c7b248205e092159879dc5011f660a Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Wed, 30 Jan 2019 13:56:25 -0800 Subject: [PATCH 1056/1530] AACWriter: support all possible channel counts Support 1-7 channels when writing raw AAC, since that's what the file format supports. authored-by: Sachin Mohan Gadag Bug: 123375289 Test: builds Change-Id: Ibc44fb575ad2897f936ed7f73f99ce481cdadb7d --- media/libstagefright/AACWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp index 2ea5fcdd19..9eba7e91ed 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -85,7 +85,7 @@ status_t AACWriter::addSource(const sp &source) { CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)); CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount)); CHECK(meta->findInt32(kKeySampleRate, &mSampleRate)); - CHECK(mChannelCount >= 1 && mChannelCount <= 2); + CHECK(mChannelCount >= 1 && mChannelCount <= 7); // Optionally, we want to check whether AACProfile is also set. if (meta->findInt32(kKeyAACProfile, &mAACProfile)) { -- GitLab From 9129dee98e302fdf274875e4673d1f6718b98160 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Tue, 5 Mar 2019 10:40:58 -0800 Subject: [PATCH 1057/1530] Remove unnecessary dependencies. Test: mmm and TH Change-Id: Ia494778ae1a896f25ee861ec49018d133bc64c82 --- media/libstagefright/Android.bp | 1 - services/mediaextractor/Android.mk | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 2bd7288eec..d8b825d040 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -199,7 +199,6 @@ cc_library { "libhidlallocatorutils", "libhidlbase", "libhidlmemory", - "libziparchive", "android.hidl.allocator@1.0", "android.hardware.cas.native@1.0", "android.hardware.media.omx@1.0", diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 65fcf40e7a..661a475afc 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -6,7 +6,7 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_SRC_FILES := \ MediaExtractorService.cpp -LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog +LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils LOCAL_MODULE:= libmediaextractorservice include $(BUILD_SHARED_LIBRARY) @@ -21,7 +21,7 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ - liblog libbase libandroidicu libavservices_minijail + liblog libandroidicu libavservices_minijail LOCAL_STATIC_LIBRARIES := libicuandroid_utils LOCAL_MODULE:= mediaextractor LOCAL_INIT_RC := mediaextractor.rc -- GitLab From 5f840f8daa250b832a492fd368741916f777e0b1 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Tue, 5 Mar 2019 11:59:04 -0800 Subject: [PATCH 1058/1530] Camera: do not register empty buffer as inflight Test: no more wrong log print during disconnect Bug: 120986771 Change-Id: I7926f12ba5d234ddb02ac9e5fddfb8c23df4ddb6 --- services/camera/libcameraservice/device3/Camera3Device.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index bc76ada8d9..7223b8d71b 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4384,8 +4384,11 @@ status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_ dst.status = BufferStatus::OK; dst.releaseFence = nullptr; - pushInflightBufferLocked(captureRequest->frameNumber, streamId, - src->buffer, src->acquire_fence); + // Output buffers are empty when using HAL buffer manager + if (!mUseHalBufManager) { + pushInflightBufferLocked(captureRequest->frameNumber, streamId, + src->buffer, src->acquire_fence); + } } } return OK; -- GitLab From 0d11a7e24815e3f87206d2648b5d8459ab5866b9 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Sat, 2 Mar 2019 20:10:30 -0800 Subject: [PATCH 1059/1530] Cleanup comments on hevc encoder plugin apply comments that didn't make the initial commit Bug: 110456253 Bug: 124991628 Test: y Change-Id: Ieb62971102aeb18880568706ffd41b5e65a67ff7 --- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 55 +++++++++++-------- media/codec2/components/hevc/C2SoftHevcEnc.h | 12 ++-- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 2c0a7a025c..7045b6a494 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -39,7 +39,7 @@ namespace android { class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { - public: + public: explicit IntfImpl(const std::shared_ptr& helper) : C2InterfaceHelper(helper) { setDerivedInstance(this); @@ -73,6 +73,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); + // matches size limits in codec library addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) @@ -91,6 +92,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { Setter::StrictValueWithNoDeps) .build()); + // matches limits in codec library addParameter( DefineParam(mBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) @@ -98,6 +100,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { .withSetter(BitrateSetter) .build()); + // matches levels allowed within codec library addParameter( DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) .withDefault(new C2StreamProfileLevelInfo::output( @@ -137,7 +140,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { C2P& me) { (void)mayBlock; C2R res = C2R::Ok(); - if (me.v.value <= 4096) { + if (me.v.value < 4096) { me.set().value = 4096; } return res; @@ -278,7 +281,7 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.); } - std::shared_ptr getSize_l() const { + std::shared_ptr getSize_l() const { return mSize; } std::shared_ptr getFrameRate_l() const { @@ -304,18 +307,21 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; }; + constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; static size_t GetCPUCoreCount() { - long cpuCoreCount = 1; + long cpuCoreCount = 0; + #if defined(_SC_NPROCESSORS_ONLN) cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); #else // _SC_NPROC_ONLN must be defined... cpuCoreCount = sysconf(_SC_NPROC_ONLN); #endif - CHECK(cpuCoreCount >= 1); - ALOGV("Number of CPU cores: %ld", cpuCoreCount); + + if (cpuCoreCount < 1) + cpuCoreCount = 1; return (size_t)cpuCoreCount; } @@ -383,7 +389,7 @@ static void fillEmptyWork(const std::unique_ptr& work) { c2_status_t C2SoftHevcEnc::initEncParams() { mCodecCtx = nullptr; - mNumCores = MIN(GetCPUCoreCount(), CODEC_MAX_CORES); + mNumCores = std::min(GetCPUCoreCount(), (size_t) CODEC_MAX_CORES); memset(&mEncParams, 0, sizeof(ihevce_static_cfg_params_t)); // default configuration @@ -397,7 +403,8 @@ c2_status_t C2SoftHevcEnc::initEncParams() { mEncParams.s_src_prms.i4_width = mSize->width; mEncParams.s_src_prms.i4_height = mSize->height; mEncParams.s_src_prms.i4_frm_rate_denom = 1000; - mEncParams.s_src_prms.i4_frm_rate_num = mFrameRate->value * mEncParams.s_src_prms.i4_frm_rate_denom; + mEncParams.s_src_prms.i4_frm_rate_num = + mFrameRate->value * mEncParams.s_src_prms.i4_frm_rate_denom; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5; mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0] = mBitrate->value; @@ -470,7 +477,7 @@ c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, const C2GraphicView* const input, uint64_t timestamp) { ihevce_static_cfg_params_t* params = &mEncParams; - memset(ps_encode_ip, 0, sizeof(ihevce_inp_buf_t)); + memset(ps_encode_ip, 0, sizeof(*ps_encode_ip)); if (!input) { return C2_OK; @@ -495,13 +502,14 @@ c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc; int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc; - uint32_t width = mSize->width; - uint32_t height = mSize->height; + const uint32_t width = mSize->width; + const uint32_t height = mSize->height; - // width and height are always even - // width and height are always even (as block size is 16x16) - CHECK_EQ((width & 1u), 0u); - CHECK_EQ((height & 1u), 0u); + // width and height must be even + if (width & 1u || height & 1u) { + ALOGW("height(%u) and width(%u) must both be even", height, width); + return C2_BAD_VALUE; + } size_t yPlaneSize = width * height; @@ -650,6 +658,7 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, if (view->error() != C2_OK) { ALOGE("graphic view map err = %d", view->error()); mSignalledError = true; + work->result = C2_CORRUPTED; return; } } @@ -687,8 +696,8 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, status = setEncodeArgs(&s_encode_ip, view.get(), timestamp); if (C2_OK != status) { - mSignalledError = true; ALOGE("setEncodeArgs failed : 0x%x", status); + mSignalledError = true; work->result = status; return; } @@ -761,8 +770,9 @@ class C2SoftHevcEncFactory : public C2ComponentFactory { : mHelper(std::static_pointer_cast( GetCodec2PlatformComponentStore()->getParamReflector())) {} - virtual c2_status_t createComponent( - c2_node_id_t id, std::shared_ptr* const component, + c2_status_t createComponent( + c2_node_id_t id, + std::shared_ptr* const component, std::function deleter) override { *component = std::shared_ptr( new C2SoftHevcEnc( @@ -772,8 +782,9 @@ class C2SoftHevcEncFactory : public C2ComponentFactory { return C2_OK; } - virtual c2_status_t createInterface( - c2_node_id_t id, std::shared_ptr* const interface, + c2_status_t createInterface( + c2_node_id_t id, + std::shared_ptr* const interface, std::function deleter) override { *interface = std::shared_ptr( new SimpleInterface( @@ -783,7 +794,7 @@ class C2SoftHevcEncFactory : public C2ComponentFactory { return C2_OK; } - virtual ~C2SoftHevcEncFactory() override = default; + ~C2SoftHevcEncFactory() override = default; private: std::shared_ptr mHelper; diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h index c22fea2b69..9d90b95c63 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.h +++ b/media/codec2/components/hevc/C2SoftHevcEnc.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright 2019 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. @@ -17,18 +17,18 @@ #ifndef ANDROID_C2_SOFT_HEVC_ENC_H_ #define ANDROID_C2_SOFT_HEVC_ENC_H_ +#include +#include #include -#include #include -#include +#include #include "ihevc_typedefs.h" namespace android { -#define MIN(a, b) ((a) < (b)) ? (a) : (b) /** Get time */ -#define GETTIME(a, b) gettimeofday(a, b); +#define GETTIME(a, b) gettimeofday(a, b) /** Compute difference between start and end */ #define TIME_DIFF(start, end, diff) \ @@ -55,7 +55,7 @@ struct C2SoftHevcEnc : public SimpleC2Component { const std::shared_ptr& pool) override; protected: - virtual ~C2SoftHevcEnc(); + ~C2SoftHevcEnc() override; private: std::shared_ptr mIntf; -- GitLab From 13ce4f67031a8877edb71efc9e8fbf9060ebfa0c Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 28 Feb 2019 15:11:41 -0800 Subject: [PATCH 1060/1530] audiopolicy: Convert some of .mk files to .bp files - Renamed libaudiopolicyengineconfig to libaudiopolicyengine_config in order to match names libaudiopolicyengine_{common|interface}. - Made libaudiopolicyengine_config static to avoid .so overhead. - Added a cleanspec for it. - Added libaudiopolicyengine_common which includes source files previously included directly into engineconfigurable and enginedefault libraries. Bug: 121208203 Test: make & flash crosshatch Change-Id: If3b7b89fcbd5752ddfdeca6c164a88f55bc35811 --- CleanSpec.mk | 1 + services/audiopolicy/engine/common/Android.bp | 28 +++++++++ services/audiopolicy/engine/config/Android.bp | 31 ++++++++++ services/audiopolicy/engine/config/Android.mk | 42 -------------- .../audiopolicy/engineconfigurable/Android.mk | 11 ++-- services/audiopolicy/enginedefault/Android.bp | 32 +++++++++++ services/audiopolicy/enginedefault/Android.mk | 57 ------------------- 7 files changed, 96 insertions(+), 106 deletions(-) create mode 100644 services/audiopolicy/engine/config/Android.bp delete mode 100644 services/audiopolicy/engine/config/Android.mk create mode 100644 services/audiopolicy/enginedefault/Android.bp delete mode 100644 services/audiopolicy/enginedefault/Android.mk diff --git a/CleanSpec.mk b/CleanSpec.mk index 793cbf48f6..e584ffb530 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -81,6 +81,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmediacodecservice.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_xmlparser@1.0.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_soft_*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/libstagefright_soft_*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicyengineconfig*) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/services/audiopolicy/engine/common/Android.bp b/services/audiopolicy/engine/common/Android.bp index e6ede07383..d0775adcd4 100644 --- a/services/audiopolicy/engine/common/Android.bp +++ b/services/audiopolicy/engine/common/Android.bp @@ -17,3 +17,31 @@ cc_library_headers { host_supported: true, export_include_dirs: ["include"], } + +cc_library_static { + name: "libaudiopolicyengine_common", + srcs: [ + "src/EngineBase.cpp", + "src/ProductStrategy.cpp", + "src/VolumeCurve.cpp", + "src/VolumeGroup.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + header_libs: [ + "libbase_headers", + "libaudiopolicycommon", + "libaudiopolicyengine_common_headers", + "libaudiopolicyengine_interface_headers", + ], + export_header_lib_headers: [ + "libaudiopolicyengine_common_headers", + ], + static_libs: [ + "libaudiopolicycomponents", + "libaudiopolicyengine_config", + ], +} diff --git a/services/audiopolicy/engine/config/Android.bp b/services/audiopolicy/engine/config/Android.bp new file mode 100644 index 0000000000..6e72f2a5e4 --- /dev/null +++ b/services/audiopolicy/engine/config/Android.bp @@ -0,0 +1,31 @@ +cc_library_static { + name: "libaudiopolicyengine_config", + export_include_dirs: ["include"], + include_dirs: [ + "external/libxml2/include", + "external/icu/icu4c/source/common", + ], + srcs: [ + "src/EngineConfig.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libmedia_helper", + "libandroidicu", + "libxml2", + "libutils", + "liblog", + "libcutils", + ], + static_libs: [ + "libaudiopolicycomponents", + ], + header_libs: [ + "libaudio_system_headers", + "libaudiopolicycommon", + ], +} diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk deleted file mode 100644 index 0b292a5fa3..0000000000 --- a/services/audiopolicy/engine/config/Android.mk +++ /dev/null @@ -1,42 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -################################################################## -# Component build -################################################################## - -include $(CLEAR_VARS) - -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include - -LOCAL_C_INCLUDES := \ - $(LOCAL_EXPORT_C_INCLUDE_DIRS) \ - external/libxml2/include \ - external/icu/icu4c/source/common - -LOCAL_SRC_FILES := \ - src/EngineConfig.cpp - -LOCAL_CFLAGS += -Wall -Werror -Wextra - -LOCAL_SHARED_LIBRARIES := \ - libmedia_helper \ - libandroidicu \ - libxml2 \ - libutils \ - liblog \ - libcutils - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE := libaudiopolicyengineconfig -LOCAL_MODULE_TAGS := optional - -LOCAL_HEADER_LIBRARIES := \ - libaudio_system_headers \ - libaudiopolicycommon - -include $(BUILD_SHARED_LIBRARY) - diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk index 4eff6e64d6..84a44228da 100644 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ b/services/audiopolicy/engineconfigurable/Android.mk @@ -12,10 +12,6 @@ LOCAL_SRC_FILES := \ src/EngineInstance.cpp \ src/Stream.cpp \ src/InputSource.cpp \ - ../engine/common/src/VolumeCurve.cpp \ - ../engine/common/src/VolumeGroup.cpp \ - ../engine/common/src/ProductStrategy.cpp \ - ../engine/common/src/EngineBase.cpp audio_policy_engine_includes_common := \ frameworks/av/services/audiopolicy/engineconfigurable/include \ @@ -37,7 +33,6 @@ LOCAL_C_INCLUDES := \ LOCAL_HEADER_LIBRARIES := \ libaudiopolicycommon \ - libaudiopolicyengine_common_headers \ libaudiopolicyengine_interface_headers LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) @@ -47,13 +42,15 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_LIBRARIES := \ libaudiopolicypfwwrapper \ - libaudiopolicycomponents + libaudiopolicycomponents \ + libaudiopolicyengine_common \ + libaudiopolicyengine_config \ LOCAL_SHARED_LIBRARIES := \ - libaudiopolicyengineconfig \ liblog \ libutils \ liblog \ + libcutils \ libaudioutils \ libparameter \ libmedia_helper \ diff --git a/services/audiopolicy/enginedefault/Android.bp b/services/audiopolicy/enginedefault/Android.bp new file mode 100644 index 0000000000..7b42c6aeed --- /dev/null +++ b/services/audiopolicy/enginedefault/Android.bp @@ -0,0 +1,32 @@ +cc_library_shared { + name: "libaudiopolicyenginedefault", + export_include_dirs: ["include"], + srcs: [ + "src/Engine.cpp", + "src/EngineInstance.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + local_include_dirs: ["include"], + header_libs: [ + "libbase_headers", + "libaudiopolicycommon", + "libaudiopolicyengine_interface_headers", + ], + static_libs: [ + "libaudiopolicycomponents", + "libaudiopolicyengine_common", + "libaudiopolicyengine_config", + ], + shared_libs: [ + "liblog", + "libcutils", + "libutils", + "libmedia_helper", + "libaudiopolicy", + "libxml2", + ], +} diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk deleted file mode 100644 index ebf383bda9..0000000000 --- a/services/audiopolicy/enginedefault/Android.mk +++ /dev/null @@ -1,57 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -# Component build -####################################################################### - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - src/Engine.cpp \ - src/EngineInstance.cpp \ - ../engine/common/src/VolumeCurve.cpp \ - ../engine/common/src/ProductStrategy.cpp \ - ../engine/common/src/EngineBase.cpp \ - ../engine/common/src/VolumeGroup.cpp - -audio_policy_engine_includes_common := \ - $(LOCAL_PATH)/include - -LOCAL_CFLAGS += \ - -Wall \ - -Werror \ - -Wextra \ - -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(audio_policy_engine_includes_common) - -LOCAL_C_INCLUDES := \ - $(audio_policy_engine_includes_common) \ - $(TARGET_OUT_HEADERS)/hw \ - $(call include-path-for, frameworks-av) \ - $(call include-path-for, audio-utils) \ - $(call include-path-for, bionic) - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE := libaudiopolicyenginedefault -LOCAL_MODULE_TAGS := optional - -LOCAL_HEADER_LIBRARIES := libbase_headers - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libcutils \ - libutils \ - libmedia_helper \ - libaudiopolicyengineconfig \ - libaudiopolicy - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudiopolicyengine_common_headers \ - libaudiopolicyengine_interface_headers - -include $(BUILD_SHARED_LIBRARY) -- GitLab From d77619a5643b3cff502d38c8c9b8e91f94c1439c Mon Sep 17 00:00:00 2001 From: Cong Jiajia Date: Thu, 10 May 2018 15:36:52 +0800 Subject: [PATCH 1061/1530] HEVCUtils: set array_completeness to 1 Bug: 124020326 according to spec, array_completeness should be 1 for hvc1 atom Test: cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice CRs-Fixed: 2241165 Change-Id: I6183f49fb16b14f28050d2dd54f39c823f4a9d39 --- media/libstagefright/HevcUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp index f152a38627..0c38f2e4de 100644 --- a/media/libstagefright/HevcUtils.cpp +++ b/media/libstagefright/HevcUtils.cpp @@ -457,8 +457,8 @@ status_t HevcParameterSets::makeHvcc(uint8_t *hvcc, size_t *hvccSize, if (numNalus == 0) { continue; } - // array_completeness set to 0. - header[0] = type; + // array_completeness set to 1. + header[0] = type | 0x80; header[1] = (numNalus >> 8) & 0xff; header[2] = numNalus & 0xff; header += 3; -- GitLab From 6c95530307645dc5414d978c813d5bbc7abf9021 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 25 Jan 2019 09:15:51 -0800 Subject: [PATCH 1062/1530] stagefright: Updates to Opus unified CSD syntax Added a marker and length field before OpusHead data as well. This will prevent any potential truncation of OpusHead, when data inside OpusHead matches one of the markers defined Added checks to validate sizes parsed. If the sizes are not as expected, then treat that as an error OggWriter updated to not write codec config data as frame data Test: With a local AMediaCodec api based application to decode opus encoder's output Test: Test with mediamuxer api based application to mux encoders output to ogg file Test: cts-tradefed run cts -m CtsMediaTestCases \ -t android.media.cts.EncoderTest#testOpusEncoders Bug: 123581317 Bug: 124053011 Change-Id: Ic3c7613ff47855e16be39dc60939e1e715522bc6 --- .../codec2/components/opus/C2SoftOpusDec.cpp | 23 +-- media/libstagefright/OggWriter.cpp | 12 ++ .../libstagefright/foundation/OpusHeader.cpp | 151 ++++++++++++++---- .../media/stagefright/foundation/OpusHeader.h | 42 +++-- 4 files changed, 175 insertions(+), 53 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp index 680712e950..13e3df5366 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.cpp +++ b/media/codec2/components/opus/C2SoftOpusDec.cpp @@ -252,20 +252,25 @@ void C2SoftOpusDec::process( const uint8_t *data = rView.data() + inOffset; if (mInputBufferCount < 3) { if (mInputBufferCount == 0) { - size_t opusHeadSize = inSize; + size_t opusHeadSize = 0; size_t codecDelayBufSize = 0; size_t seekPreRollBufSize = 0; - void *opusHeadBuf = (void *)data; + void *opusHeadBuf = NULL; void *codecDelayBuf = NULL; void *seekPreRollBuf = NULL; - GetOpusHeaderBuffers(data, inSize, &opusHeadBuf, - &opusHeadSize, &codecDelayBuf, - &codecDelayBufSize, &seekPreRollBuf, - &seekPreRollBufSize); + if (!GetOpusHeaderBuffers(data, inSize, &opusHeadBuf, + &opusHeadSize, &codecDelayBuf, + &codecDelayBufSize, &seekPreRollBuf, + &seekPreRollBufSize)) { + ALOGE("%s encountered error in GetOpusHeaderBuffers", __func__); + mSignalledError = true; + work->result = C2_CORRUPTED; + return; + } if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) { - ALOGE("Encountered error while Parsing Opus Header."); + ALOGE("%s Encountered error while Parsing Opus Header.", __func__); mSignalledError = true; work->result = C2_CORRUPTED; return; @@ -304,14 +309,14 @@ void C2SoftOpusDec::process( return; } - if (codecDelayBuf && codecDelayBufSize == 8) { + if (codecDelayBuf && codecDelayBufSize == sizeof(uint64_t)) { uint64_t value; memcpy(&value, codecDelayBuf, sizeof(uint64_t)); mCodecDelay = ns_to_samples(value, kRate); mSamplesToDiscard = mCodecDelay; ++mInputBufferCount; } - if (seekPreRollBuf && seekPreRollBufSize == 8) { + if (seekPreRollBuf && seekPreRollBufSize == sizeof(uint64_t)) { uint64_t value; memcpy(&value, codecDelayBuf, sizeof(uint64_t)); mSeekPreRoll = ns_to_samples(value, kRate); diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp index 5c139831a2..cb87b558ab 100644 --- a/media/libstagefright/OggWriter.cpp +++ b/media/libstagefright/OggWriter.cpp @@ -295,6 +295,18 @@ status_t OggWriter::threadFunc() { mEstimatedSizeBytes, mMaxFileSizeLimitBytes); break; } + + int32_t isCodecSpecific; + if ((buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecSpecific) + && isCodecSpecific) + || IsOpusHeader((uint8_t*)buffer->data() + buffer->range_offset(), + buffer->range_length())) { + ALOGV("Drop codec specific info buffer"); + buffer->release(); + buffer = nullptr; + continue; + } + int64_t timestampUs; CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs)); if (timestampUs > mEstimatedDurationUs) { diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp index 9faede1e58..acb9ccf6e9 100644 --- a/media/libstagefright/foundation/OpusHeader.cpp +++ b/media/libstagefright/foundation/OpusHeader.cpp @@ -15,9 +15,9 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "SoftOpus" -#include +#define LOG_TAG "OpusHeader" #include +#include #include #include @@ -91,6 +91,9 @@ static uint16_t ReadLE16(const uint8_t* data, size_t data_size, uint32_t read_of // Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header) { + if (data == NULL) { + return false; + } if (data_size < kOpusHeaderSize) { ALOGV("Header size is too small."); return false; @@ -183,53 +186,88 @@ int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate, ALOGD("Buffer not large enough to hold unified OPUS CSD"); return -1; } + int headerLen = 0; + + // Add opus header + /* + Following is the CSD syntax for signalling OpusHeader + (http://wiki.xiph.org/OggOpus#ID_Header) + + Marker (8 bytes) | Length (8 bytes) | OpusHeader + + Markers supported: + AOPUS_CSD_OPUS_HEADER_MARKER - Signals Opus Header + + Length should be a value within AOPUS_OPUSHEAD_MINSIZE and AOPUS_OPUSHEAD_MAXSIZE. + */ + + memcpy(output + headerLen, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE); + headerLen += AOPUS_MARKER_SIZE; + + // Place holder for opusHeader Size + headerLen += AOPUS_LENGTH_SIZE; - int headerLen = WriteOpusHeader(header, inputSampleRate, output, + int headerSize = WriteOpusHeader(header, inputSampleRate, output + headerLen, outputSize); - if (headerLen < 0) { - ALOGD("WriteOpusHeader failed"); - return -1; - } - if (headerLen >= (outputSize - 2 * AOPUS_TOTAL_CSD_SIZE)) { - ALOGD("Buffer not large enough to hold codec delay and seek pre roll"); + if (headerSize < 0) { + ALOGD("%s: WriteOpusHeader failed", __func__); return -1; } + headerLen += headerSize; - uint64_t length = AOPUS_LENGTH; + // Update opus headerSize after AOPUS_CSD_OPUS_HEADER_MARKER + uint64_t length = headerSize; + memcpy(output + AOPUS_MARKER_SIZE, &length, AOPUS_LENGTH_SIZE); /* Following is the CSD syntax for signalling codec delay and seek pre-roll which is to be appended after OpusHeader - Marker (8 bytes) | Length (8 bytes) | Samples (8 bytes) + Marker (8 bytes) | Length (8 bytes) | Samples in ns (8 bytes) Markers supported: - AOPUSDLY - Signals Codec Delay - AOPUSPRL - Signals seek pre roll + AOPUS_CSD_CODEC_DELAY_MARKER - codec delay as samples in ns, represented in 8 bytes + AOPUS_CSD_SEEK_PREROLL_MARKER - preroll adjustment as samples in ns, represented in 8 bytes - Length should be 8. */ - + length = sizeof(codecDelay); + if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) { + ALOGD("Buffer not large enough to hold codec delay"); + return -1; + } // Add codec delay memcpy(output + headerLen, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE); headerLen += AOPUS_MARKER_SIZE; memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE); headerLen += AOPUS_LENGTH_SIZE; - memcpy(output + headerLen, &codecDelay, AOPUS_CSD_SIZE); - headerLen += AOPUS_CSD_SIZE; + memcpy(output + headerLen, &codecDelay, length); + headerLen += length; + length = sizeof(seekPreRoll); + if (headerLen > (outputSize - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE - length)) { + ALOGD("Buffer not large enough to hold seek pre roll"); + return -1; + } // Add skip pre roll memcpy(output + headerLen, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE); headerLen += AOPUS_MARKER_SIZE; memcpy(output + headerLen, &length, AOPUS_LENGTH_SIZE); headerLen += AOPUS_LENGTH_SIZE; - memcpy(output + headerLen, &seekPreRoll, AOPUS_CSD_SIZE); - headerLen += AOPUS_CSD_SIZE; + memcpy(output + headerLen, &seekPreRoll, length); + headerLen += length; return headerLen; } -void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, +bool IsOpusHeader(const uint8_t *data, size_t data_size) { + if (data_size < AOPUS_MARKER_SIZE) { + return false; + } + + return !memcmp(data, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE); +} + +bool GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, void **opusHeadBuf, size_t *opusHeadSize, void **codecDelayBuf, size_t *codecDelaySize, void **seekPreRollBuf, size_t *seekPreRollSize) { @@ -237,26 +275,77 @@ void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, *codecDelaySize = 0; *seekPreRollBuf = NULL; *seekPreRollSize = 0; - *opusHeadBuf = (void *)data; - *opusHeadSize = data_size; - if (data_size >= AOPUS_UNIFIED_CSD_MINSIZE) { + *opusHeadBuf = NULL; + *opusHeadSize = 0; + + // AOPUS_MARKER_SIZE is 8 "OpusHead" is of size 8 + if (data_size < 8) + return false; + + // Check if the CSD is in legacy format + if (!memcmp("OpusHead", data, 8)) { + if (data_size < AOPUS_OPUSHEAD_MINSIZE || data_size > AOPUS_OPUSHEAD_MAXSIZE) { + ALOGD("Unexpected size for opusHeadSize %zu", data_size); + return false; + } + *opusHeadBuf = (void *)data; + *opusHeadSize = data_size; + return true; + } else if (memcmp(AOPUS_CSD_MARKER_PREFIX, data, AOPUS_CSD_MARKER_PREFIX_SIZE) == 0) { size_t i = 0; - while (i < data_size - AOPUS_TOTAL_CSD_SIZE) { + bool found = false; + while (i <= data_size - AOPUS_MARKER_SIZE - AOPUS_LENGTH_SIZE) { uint8_t *csdBuf = (uint8_t *)data + i; - if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) { - *opusHeadSize = std::min(*opusHeadSize, i); + if (!memcmp(csdBuf, AOPUS_CSD_OPUS_HEADER_MARKER, AOPUS_MARKER_SIZE)) { + uint64_t value; + memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value)); + if (value < AOPUS_OPUSHEAD_MINSIZE || value > AOPUS_OPUSHEAD_MAXSIZE) { + ALOGD("Unexpected size for opusHeadSize %" PRIu64, value); + return false; + } + i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value; + if (i > data_size) { + ALOGD("Marker signals a header that is larger than input"); + return false; + } + *opusHeadBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE; + *opusHeadSize = value; + found = true; + } else if (!memcmp(csdBuf, AOPUS_CSD_CODEC_DELAY_MARKER, AOPUS_MARKER_SIZE)) { + uint64_t value; + memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value)); + if (value != sizeof(uint64_t)) { + ALOGD("Unexpected size for codecDelay %" PRIu64, value); + return false; + } + i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value; + if (i > data_size) { + ALOGD("Marker signals a header that is larger than input"); + return false; + } *codecDelayBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE; - *codecDelaySize = AOPUS_CSD_SIZE; - i += AOPUS_TOTAL_CSD_SIZE; + *codecDelaySize = value; } else if (!memcmp(csdBuf, AOPUS_CSD_SEEK_PREROLL_MARKER, AOPUS_MARKER_SIZE)) { - *opusHeadSize = std::min(*opusHeadSize, i); + uint64_t value; + memcpy(&value, csdBuf + AOPUS_MARKER_SIZE, sizeof(value)); + if (value != sizeof(uint64_t)) { + ALOGD("Unexpected size for seekPreRollSize %" PRIu64, value); + return false; + } + i += AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE + value; + if (i > data_size) { + ALOGD("Marker signals a header that is larger than input"); + return false; + } *seekPreRollBuf = csdBuf + AOPUS_MARKER_SIZE + AOPUS_LENGTH_SIZE; - *seekPreRollSize = AOPUS_CSD_SIZE; - i += AOPUS_TOTAL_CSD_SIZE; + *seekPreRollSize = value; } else { i++; } } + return found; + } else { + return false; // it isn't in either format } } diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h index 9bffccbf44..29037af053 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/OpusHeader.h @@ -25,22 +25,37 @@ namespace android { /* Constants used for delimiting Opus CSD */ -#define AOPUS_CSD_CODEC_DELAY_MARKER "AOPUSDLY" -#define AOPUS_CSD_SEEK_PREROLL_MARKER "AOPUSPRL" -#define AOPUS_CSD_SIZE 8 -#define AOPUS_LENGTH 8 +#define AOPUS_CSD_MARKER_PREFIX "AOPUS" +#define AOPUS_CSD_MARKER_PREFIX_SIZE (sizeof(AOPUS_CSD_MARKER_PREFIX) - 1) +#define AOPUS_CSD_OPUS_HEADER_MARKER AOPUS_CSD_MARKER_PREFIX "HDR" +#define AOPUS_CSD_CODEC_DELAY_MARKER AOPUS_CSD_MARKER_PREFIX "DLY" +#define AOPUS_CSD_SEEK_PREROLL_MARKER AOPUS_CSD_MARKER_PREFIX "PRL" #define AOPUS_MARKER_SIZE 8 -#define AOPUS_LENGTH_SIZE 8 -#define AOPUS_TOTAL_CSD_SIZE \ - ((AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_CSD_SIZE)) -#define AOPUS_CSD0_MINSIZE 19 +#define AOPUS_LENGTH_SIZE sizeof(uint64_t) +#define AOPUS_CSD_CODEC_DELAY_SIZE \ + (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + sizeof(uint64_t) +#define AOPUS_CSD_SEEK_PREROLL_SIZE \ + (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + sizeof(uint64_t) + +/* OpusHead csd minimum size is 19 */ +#define AOPUS_OPUSHEAD_MINSIZE 19 +#define AOPUS_CSD_OPUSHEAD_MINSIZE \ + (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_OPUSHEAD_MINSIZE) + #define AOPUS_UNIFIED_CSD_MINSIZE \ - ((AOPUS_CSD0_MINSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE)) + ((AOPUS_CSD_OPUSHEAD_MINSIZE) + \ + (AOPUS_CSD_CODEC_DELAY_SIZE) + \ + (AOPUS_CSD_SEEK_PREROLL_SIZE)) + +/* OpusHead csd at max can be AOPUS_CSD_OPUSHEAD_MINSIZE + 2 + max number of channels (255) */ +#define AOPUS_OPUSHEAD_MAXSIZE ((AOPUS_OPUSHEAD_MINSIZE) + 2 + 255) +#define AOPUS_CSD_OPUSHEAD_MAXSIZE \ + (AOPUS_MARKER_SIZE) + (AOPUS_LENGTH_SIZE) + (AOPUS_OPUSHEAD_MAXSIZE) -/* CSD0 at max can be 22 bytes + max number of channels (255) */ -#define AOPUS_CSD0_MAXSIZE 277 #define AOPUS_UNIFIED_CSD_MAXSIZE \ - ((AOPUS_CSD0_MAXSIZE) + 2 * (AOPUS_TOTAL_CSD_SIZE)) + ((AOPUS_CSD_OPUSHEAD_MAXSIZE) + \ + (AOPUS_CSD_CODEC_DELAY_SIZE) + \ + (AOPUS_CSD_SEEK_PREROLL_SIZE)) struct OpusHeader { int channels; @@ -54,13 +69,14 @@ struct OpusHeader { bool ParseOpusHeader(const uint8_t* data, size_t data_size, OpusHeader* header); int WriteOpusHeader(const OpusHeader &header, int input_sample_rate, uint8_t* output, size_t output_size); -void GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, +bool GetOpusHeaderBuffers(const uint8_t *data, size_t data_size, void **opusHeadBuf, size_t *opusHeadSize, void **codecDelayBuf, size_t *codecDelaySize, void **seekPreRollBuf, size_t *seekPreRollSize); int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate, uint8_t* output, size_t outputSize, uint64_t codecDelay, uint64_t seekPreRoll); +bool IsOpusHeader(const uint8_t *data, size_t data_size); } // namespace android #endif // OPUS_HEADER_H_ -- GitLab From 630dda97659a62338535ea0096fb0e69a584ed63 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 25 Jan 2019 09:15:51 -0800 Subject: [PATCH 1063/1530] stagefright_foundation: Fix parsing seek pre roll in Opus Seek pre-roll parsing was skipped in OpusHeader because of wrong loop check. Also, C2 opus decoder plugin was reading from codecDelayBuf instead of seekPreRollBuf Test: With a local AMediaCodec api based application to decode opus encoder's output Bug: 123426341 Change-Id: I0924f67c4a070a582b578d2ecdcd2fecd10134ad --- media/codec2/components/opus/C2SoftOpusDec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp index 13e3df5366..7dcd53d718 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.cpp +++ b/media/codec2/components/opus/C2SoftOpusDec.cpp @@ -318,7 +318,7 @@ void C2SoftOpusDec::process( } if (seekPreRollBuf && seekPreRollBufSize == sizeof(uint64_t)) { uint64_t value; - memcpy(&value, codecDelayBuf, sizeof(uint64_t)); + memcpy(&value, seekPreRollBuf, sizeof(uint64_t)); mSeekPreRoll = ns_to_samples(value, kRate); ++mInputBufferCount; } -- GitLab From eb46cf924deae98c5edaad20da3bd123217ab947 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 6 Mar 2019 10:13:38 -0800 Subject: [PATCH 1064/1530] AudioTrackThread, AudioRecordThread: enable Java calling When an AudioTrack or AudioRecord is created by system server, if restoreTrack_l() or restoreRecord_l() is called from AudioTrackThread or AudioRecordThread, and that then calls audioserver through binder, which then calls a Java service, binder recursion may call Java on the original calling thread. Test: bluetooth calls don't crash Bug: 126948865 Bug: 127619936 Change-Id: I41fcb666d07c4ab985f5590e40e0c310709e786d --- media/libaudioclient/AudioRecord.cpp | 6 ++++-- media/libaudioclient/AudioTrack.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index baa1469426..9daa299c8c 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -1426,8 +1426,10 @@ void AudioRecord::DeathNotifier::binderDied(const wp& who __unused) // ========================================================================= -AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), +AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, + bool bCanCallJava __unused) + : Thread(true /* bCanCallJava */) // binder recursion on restoreRecord_l() may call Java. + , mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), mIgnoreNextPausedInt(false) { } diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 7881bb859c..670a2001c9 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -3127,8 +3127,9 @@ void AudioTrack::DeathNotifier::binderDied(const wp& who __unused) // ========================================================================= -AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava __unused) + : Thread(true /* bCanCallJava */) // binder recursion on restoreTrack_l() may call Java. + , mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), mIgnoreNextPausedInt(false) { } -- GitLab From f0d7e1104fdc7602cb7c642ee0abdfaff99a18fc Mon Sep 17 00:00:00 2001 From: Pirama Arumuga Nainar Date: Wed, 6 Mar 2019 11:44:34 -0800 Subject: [PATCH 1065/1530] Remove '-v' from cflags Remove noisy '-v' flag from downmixtest. Test: Build Change-Id: I34ad5e65f4dc33e84efff943f42ba230ac864c2b --- media/libeffects/downmix/tests/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/libeffects/downmix/tests/Android.bp b/media/libeffects/downmix/tests/Android.bp index e2e7dbd50c..63afc540a6 100644 --- a/media/libeffects/downmix/tests/Android.bp +++ b/media/libeffects/downmix/tests/Android.bp @@ -24,7 +24,6 @@ cc_test { ], cflags: [ - "-v", "-Werror", "-Wextra", ], -- GitLab From ca35367b3d176f234bf955d2df95a45126b02287 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 6 Mar 2019 11:54:38 -0800 Subject: [PATCH 1066/1530] Clean up AudioTrackThread and AudioRecordThread constructor Remove unused argument. Test: compile Change-Id: I1f589941686ddcdff166298f8bfe0564b147c137 --- media/libaudioclient/AudioRecord.cpp | 5 ++--- media/libaudioclient/AudioTrack.cpp | 4 ++-- media/libaudioclient/include/media/AudioRecord.h | 2 +- media/libaudioclient/include/media/AudioTrack.h | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 9daa299c8c..cb1517e2d9 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -349,7 +349,7 @@ status_t AudioRecord::set( mCbf = cbf; if (cbf != NULL) { - mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava); + mAudioRecordThread = new AudioRecordThread(*this); mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO); // thread begins in paused state, and will not reference us until start() } @@ -1426,8 +1426,7 @@ void AudioRecord::DeathNotifier::binderDied(const wp& who __unused) // ========================================================================= -AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, - bool bCanCallJava __unused) +AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver) : Thread(true /* bCanCallJava */) // binder recursion on restoreRecord_l() may call Java. , mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), mIgnoreNextPausedInt(false) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 670a2001c9..60e841cf88 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -615,7 +615,7 @@ status_t AudioTrack::set( mCbf = cbf; if (cbf != NULL) { - mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); + mAudioTrackThread = new AudioTrackThread(*this); mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); // thread begins in paused state, and will not reference us until start() } @@ -3127,7 +3127,7 @@ void AudioTrack::DeathNotifier::binderDied(const wp& who __unused) // ========================================================================= -AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava __unused) +AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver) : Thread(true /* bCanCallJava */) // binder recursion on restoreTrack_l() may call Java. , mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL), mIgnoreNextPausedInt(false) diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index 4707c4ae1d..a9f871155a 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -562,7 +562,7 @@ private: class AudioRecordThread : public Thread { public: - AudioRecordThread(AudioRecord& receiver, bool bCanCallJava = false); + AudioRecordThread(AudioRecord& receiver); // Do not call Thread::requestExitAndWait() without first calling requestExit(). // Thread::requestExitAndWait() is not virtual, and the implementation doesn't do enough. diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 12f5d71435..3926ead581 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -934,7 +934,7 @@ public: class AudioTrackThread : public Thread { public: - AudioTrackThread(AudioTrack& receiver, bool bCanCallJava = false); + AudioTrackThread(AudioTrack& receiver); // Do not call Thread::requestExitAndWait() without first calling requestExit(). // Thread::requestExitAndWait() is not virtual, and the implementation doesn't do enough. -- GitLab From 2641ec2a91e2c78d81e0ee00e94139f23168c0d6 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 14 Jan 2019 14:50:56 +0800 Subject: [PATCH 1067/1530] Handle mkv file without cue point Some mkv or mka files don't have any cue point, especially mka file which is transformed from ffmpeg. When user plays such mkv or mka file, user cannot seek to correct position. To handle this issue, we ignore cues and load clusters. When we seek and find that it has no cue point, we call seekwithoutcue function, which will find correct position from loaded clusters. Bug: 123150866 Test: Test with a mkv or mka file without CUES and check if we can can seek to correct position and play normally. Change-Id: I77cb64ebee6261545ab3eff6a38538d854408b47 --- media/extractors/mkv/MatroskaExtractor.cpp | 99 +++++++++++++++++++--- 1 file changed, 86 insertions(+), 13 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 7239302f57..a399940f99 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -119,6 +119,9 @@ private: const mkvparser::BlockEntry *mBlockEntry; long mBlockEntryIndex; + unsigned long mTrackType; + void seekwithoutcue_l(int64_t seekTimeUs, int64_t *actualFrameTimeUs); + void advance_l(); BlockIterator(const BlockIterator &); @@ -290,6 +293,7 @@ BlockIterator::BlockIterator( mCluster(NULL), mBlockEntry(NULL), mBlockEntryIndex(0) { + mTrackType = mExtractor->mSegment->GetTracks()->GetTrackByNumber(trackNum)->GetType(); reset(); } @@ -442,12 +446,14 @@ void BlockIterator::seek( } if (!pCues) { - ALOGE("No Cues in file"); + ALOGV("No Cues in file,seek without cue data"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } } else if (!pSH) { - ALOGE("No SeekHead"); + ALOGV("No SeekHead, seek without cue data"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } @@ -456,7 +462,9 @@ void BlockIterator::seek( while (!pCues->DoneParsing()) { pCues->LoadCuePoint(); pCP = pCues->GetLast(); - CHECK(pCP); + ALOGV("pCP = %s", pCP == NULL ? "NULL" : "not NULL"); + if (pCP == NULL) + continue; size_t trackCount = mExtractor->mTracks.size(); for (size_t index = 0; index < trackCount; ++index) { @@ -494,6 +502,7 @@ void BlockIterator::seek( // Always *search* based on the video track, but finalize based on mTrackNum if (!pTP) { ALOGE("Did not locate the video track for seeking"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } @@ -537,6 +546,31 @@ int64_t BlockIterator::blockTimeUs() const { return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll; } +void BlockIterator::seekwithoutcue_l(int64_t seekTimeUs, int64_t *actualFrameTimeUs) { + mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll); + const long status = mCluster->GetFirst(mBlockEntry); + if (status < 0) { // error + ALOGE("get last blockenry failed!"); + mCluster = NULL; + return; + } + mBlockEntryIndex = 0; + while (!eos() && ((block()->GetTrackNumber() != mTrackNum) || (blockTimeUs() < seekTimeUs))) { + advance_l(); + } + + // video track will seek to the next key frame. + if (mTrackType == 1) { + while (!eos() && ((block()->GetTrackNumber() != mTrackNum) || + !mBlockEntry->GetBlock()->IsKey())) { + advance_l(); + } + } + *actualFrameTimeUs = blockTimeUs(); + ALOGV("seekTimeUs:%lld, actualFrameTimeUs:%lld, tracknum:%lld", + (long long)seekTimeUs, (long long)*actualFrameTimeUs, (long long)mTrackNum); +} + //////////////////////////////////////////////////////////////////////////////// static unsigned U24_AT(const uint8_t *ptr) { @@ -956,17 +990,56 @@ MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) return; } - // from mkvparser::Segment::Load(), but stop at first cluster - ret = mSegment->ParseHeaders(); - if (ret == 0) { - long len; - ret = mSegment->LoadCluster(pos, len); - if (ret >= 1) { - // no more clusters - ret = 0; + if (mIsLiveStreaming) { + // from mkvparser::Segment::Load(), but stop at first cluster + ret = mSegment->ParseHeaders(); + if (ret == 0) { + long len; + ret = mSegment->LoadCluster(pos, len); + if (ret >= 1) { + // no more clusters + ret = 0; + } + } else if (ret > 0) { + ret = mkvparser::E_BUFFER_NOT_FULL; + } + } else { + ret = mSegment->ParseHeaders(); + if (ret < 0) { + ALOGE("Segment parse header return fail %lld", ret); + delete mSegment; + mSegment = NULL; + return; + } else if (ret == 0) { + const mkvparser::Cues* mCues = mSegment->GetCues(); + const mkvparser::SeekHead* mSH = mSegment->GetSeekHead(); + if ((mCues == NULL) && (mSH != NULL)) { + size_t count = mSH->GetCount(); + const mkvparser::SeekHead::Entry* mEntry; + for (size_t index = 0; index < count; index++) { + mEntry = mSH->GetEntry(index); + if (mEntry->id == 0x0C53BB6B) { // Cues ID + long len; + long long pos; + mSegment->ParseCues(mEntry->pos, pos, len); + mCues = mSegment->GetCues(); + ALOGV("find cue data by seekhead"); + break; + } + } + } + + if (mCues) { + long len; + ret = mSegment->LoadCluster(pos, len); + ALOGV("has Cue data, Cluster num=%ld", mSegment->GetCount()); + } else { + long status_Load = mSegment->Load(); + ALOGW("no Cue data,Segment Load status:%ld",status_Load); + } + } else if (ret > 0) { + ret = mkvparser::E_BUFFER_NOT_FULL; } - } else if (ret > 0) { - ret = mkvparser::E_BUFFER_NOT_FULL; } if (ret < 0) { -- GitLab From 366b643d1e674684d7095cdbe36b6791e842fdbc Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Mon, 25 Feb 2019 10:35:51 -0700 Subject: [PATCH 1068/1530] Surface MicrophoneDirection API in MediaRecorder Bug: 126185930 Test: MicrophoneDirectionTest testbed. Change-Id: I6024ede50999aa751d63f272a087f578b8aea93d --- media/libaudioclient/AudioRecord.cpp | 49 ++++++++++++++++--- .../include/media/AudioRecord.h | 13 ++++- media/libmedia/IMediaRecorder.cpp | 37 ++++++++++++++ media/libmedia/include/media/IMediaRecorder.h | 2 + .../include/media/MediaRecorderBase.h | 2 + media/libmedia/include/media/mediarecorder.h | 3 ++ media/libmedia/mediarecorder.cpp | 10 ++++ .../MediaRecorderClient.cpp | 16 ++++++ .../MediaRecorderClient.h | 2 + .../StagefrightRecorder.cpp | 26 +++++++++- .../StagefrightRecorder.h | 5 ++ media/libstagefright/AudioSource.cpp | 24 ++++++++- .../include/media/stagefright/AudioSource.h | 6 ++- services/audioflinger/Threads.cpp | 4 +- 14 files changed, 184 insertions(+), 15 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index baa1469426..eb8ab487ab 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -153,7 +153,9 @@ AudioRecord::AudioRecord(const String16 &opPackageName) : mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName), mSessionId(AUDIO_SESSION_ALLOCATE), mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT), - mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE) + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE), + mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED), + mSelectedMicFieldDimension(MIC_FIELD_DIMENSION_DEFAULT) { } @@ -173,7 +175,9 @@ AudioRecord::AudioRecord( uid_t uid, pid_t pid, const audio_attributes_t* pAttributes, - audio_port_handle_t selectedDeviceId) + audio_port_handle_t selectedDeviceId, + audio_microphone_direction_t selectedMicDirection, + float microphoneFieldDimension) : mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName), @@ -184,7 +188,8 @@ AudioRecord::AudioRecord( { (void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags, - uid, pid, pAttributes, selectedDeviceId); + uid, pid, pAttributes, selectedDeviceId, + selectedMicDirection, microphoneFieldDimension); } AudioRecord::~AudioRecord() @@ -233,7 +238,9 @@ status_t AudioRecord::set( uid_t uid, pid_t pid, const audio_attributes_t* pAttributes, - audio_port_handle_t selectedDeviceId) + audio_port_handle_t selectedDeviceId, + audio_microphone_direction_t selectedMicDirection, + float microphoneFieldDimension) { status_t status = NO_ERROR; uint32_t channelCount; @@ -249,6 +256,8 @@ status_t AudioRecord::set( sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid); mSelectedDeviceId = selectedDeviceId; + mSelectedMicDirection = selectedMicDirection; + mSelectedMicFieldDimension = microphoneFieldDimension; switch (transferType) { case TRANSFER_DEFAULT: @@ -436,6 +445,10 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri status = restoreRecord_l("start"); } + // Call these directly because we are already holding the lock. + mAudioRecord->setMicrophoneDirection(mSelectedMicDirection); + mAudioRecord->setMicrophoneFieldDimension(mSelectedMicFieldDimension); + if (status != NO_ERROR) { mActive = false; ALOGE("%s(%d): status %d", __func__, mPortId, status); @@ -653,6 +666,8 @@ status_t AudioRecord::dump(int fd, const Vector& args __unused) const mNotificationFramesAct, mNotificationFramesReq); result.appendFormat(" input(%d), latency(%u), selected device Id(%d), routed device Id(%d)\n", mInput, mLatency, mSelectedDeviceId, mRoutedDeviceId); + result.appendFormat(" mic direction(%d) mic field dimension(%f)", + mSelectedMicDirection, mSelectedMicFieldDimension); ::write(fd, result.string(), result.size()); return NO_ERROR; } @@ -1405,12 +1420,34 @@ status_t AudioRecord::getActiveMicrophones(std::vector* a status_t AudioRecord::setMicrophoneDirection(audio_microphone_direction_t direction) { AutoMutex lock(mLock); - return mAudioRecord->setMicrophoneDirection(direction).transactionError(); + if (mSelectedMicDirection == direction) { + // NOP + return OK; + } + + mSelectedMicDirection = direction; + if (mAudioRecord == 0) { + // the internal AudioRecord hasn't be created yet, so just stash the attribute. + return OK; + } else { + return mAudioRecord->setMicrophoneDirection(direction).transactionError(); + } } status_t AudioRecord::setMicrophoneFieldDimension(float zoom) { AutoMutex lock(mLock); - return mAudioRecord->setMicrophoneFieldDimension(zoom).transactionError(); + if (mSelectedMicFieldDimension == zoom) { + // NOP + return OK; + } + + mSelectedMicFieldDimension = zoom; + if (mAudioRecord == 0) { + // the internal AudioRecord hasn't be created yet, so just stash the attribute. + return OK; + } else { + return mAudioRecord->setMicrophoneFieldDimension(zoom).transactionError(); + } } // ========================================================================= diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index 4707c4ae1d..81161ad252 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -189,7 +189,10 @@ public: uid_t uid = AUDIO_UID_INVALID, pid_t pid = -1, const audio_attributes_t* pAttributes = NULL, - audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, + audio_microphone_direction_t + selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, + float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT); /* Terminates the AudioRecord and unregisters it from AudioFlinger. * Also destroys all resources associated with the AudioRecord. @@ -228,7 +231,10 @@ public: uid_t uid = AUDIO_UID_INVALID, pid_t pid = -1, const audio_attributes_t* pAttributes = NULL, - audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, + audio_microphone_direction_t + selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, + float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT); /* Result of constructing the AudioRecord. This must be checked for successful initialization * before using any AudioRecord API (except for set()), because using @@ -716,6 +722,9 @@ private: // activity and connected devices wp mDeviceCallback; + audio_microphone_direction_t mSelectedMicDirection; + float mSelectedMicFieldDimension; + private: class MediaMetrics { public: diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index 56ee18ea48..f283569980 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -66,6 +66,8 @@ enum { ENABLE_AUDIO_DEVICE_CALLBACK, GET_ACTIVE_MICROPHONES, GET_PORT_ID, + SET_MICROPHONE_DIRECTION, + SET_MICROPHONE_FIELD_DIMENSION }; class BpMediaRecorder: public BpInterface @@ -407,6 +409,24 @@ public: return status; } + status_t setMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setMicrophoneDirection(%d)", direction); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeInt32(direction); + status_t status = remote()->transact(SET_MICROPHONE_DIRECTION, data, &reply); + return status == NO_ERROR ? (status_t)reply.readInt32() : status; + } + + status_t setMicrophoneFieldDimension(float zoom) { + ALOGV("setMicrophoneFieldDimension(%f)", zoom); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeFloat(zoom); + status_t status = remote()->transact(SET_MICROPHONE_FIELD_DIMENSION, data, &reply); + return status == NO_ERROR ? (status_t)reply.readInt32() : status; + } + status_t getPortId(audio_port_handle_t *portId) { ALOGV("getPortId"); @@ -689,6 +709,23 @@ status_t BnMediaRecorder::onTransact( } return NO_ERROR; } + case SET_MICROPHONE_DIRECTION: { + ALOGV("SET_MICROPHONE_DIRECTION"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + int direction = data.readInt32(); + status_t status = + setMicrophoneDirection(static_cast(direction)); + reply->writeInt32(status); + return NO_ERROR; + } + case SET_MICROPHONE_FIELD_DIMENSION: { + ALOGV("SET_MICROPHONE_FIELD_DIMENSION"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + float zoom = data.readFloat(); + status_t status = setMicrophoneFieldDimension(zoom); + reply->writeInt32(status); + return NO_ERROR; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h index e7c466d4b8..0b09420563 100644 --- a/media/libmedia/include/media/IMediaRecorder.h +++ b/media/libmedia/include/media/IMediaRecorder.h @@ -73,6 +73,8 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) = 0; + virtual status_t setMicrophoneFieldDimension(float zoom) = 0; virtual status_t getPortId(audio_port_handle_t *portId) = 0; }; diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h index e1c5d47b77..88282ac23f 100644 --- a/media/libmedia/include/media/MediaRecorderBase.h +++ b/media/libmedia/include/media/MediaRecorderBase.h @@ -72,6 +72,8 @@ struct MediaRecorderBase { virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) = 0; + virtual status_t setMicrophoneFieldDimension(float zoom) = 0; virtual status_t getPortId(audio_port_handle_t *portId) const = 0; diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index 33be5591d4..85804376de 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -264,6 +264,9 @@ public: status_t getRoutedDeviceId(audio_port_handle_t *deviceId); status_t enableAudioDeviceCallback(bool enabled); status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t setMicrophoneDirection(audio_microphone_direction_t direction); + status_t setMicrophoneFieldDimension(float zoom); + status_t getPortId(audio_port_handle_t *portId) const; private: diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index d07e703895..6c59a29cc6 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -842,6 +842,16 @@ status_t MediaRecorder::getActiveMicrophones(std::vector* return mMediaRecorder->getActiveMicrophones(activeMicrophones); } +status_t MediaRecorder::setMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setMicrophoneDirection(%d)", direction); + return mMediaRecorder->setMicrophoneDirection(direction); +} + +status_t MediaRecorder::setMicrophoneFieldDimension(float zoom) { + ALOGV("setMicrophoneFieldDimension(%f)", zoom); + return mMediaRecorder->setMicrophoneFieldDimension(zoom); +} + status_t MediaRecorder::getPortId(audio_port_handle_t *portId) const { ALOGV("getPortId"); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 3fa8e3f816..d6628d9737 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -538,6 +538,22 @@ status_t MediaRecorderClient::getActiveMicrophones( return NO_INIT; } +status_t MediaRecorderClient::setMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setMicrophoneDirection(%d)", direction); + if (mRecorder != NULL) { + return mRecorder->setMicrophoneDirection(direction); + } + return NO_INIT; +} + +status_t MediaRecorderClient::setMicrophoneFieldDimension(float zoom) { + ALOGV("setMicrophoneFieldDimension(%f)", zoom); + if (mRecorder != NULL) { + return mRecorder->setMicrophoneFieldDimension(zoom); + } + return NO_INIT; +} + status_t MediaRecorderClient::getPortId(audio_port_handle_t *portId) { ALOGV("getPortId"); Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 303cefc709..8da718f995 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -109,6 +109,8 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones( std::vector* activeMicrophones); + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); + virtual status_t setMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) override; private: diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index d1113136ce..3e3872deb7 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -118,7 +118,9 @@ StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName) mVideoSource(VIDEO_SOURCE_LIST_END), mStarted(false), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), - mDeviceCallbackEnabled(false) { + mDeviceCallbackEnabled(false), + mSelectedMicDirection(MIC_DIRECTION_UNSPECIFIED), + mSelectedMicFieldDimension(MIC_FIELD_DIMENSION_NORMAL) { ALOGV("Constructor"); @@ -1090,7 +1092,9 @@ sp StagefrightRecorder::createAudioSource() { mSampleRate, mClientUid, mClientPid, - mSelectedDeviceId); + mSelectedDeviceId, + mSelectedMicDirection, + mSelectedMicFieldDimension); status_t err = audioSource->initCheck(); @@ -2269,6 +2273,24 @@ status_t StagefrightRecorder::getActiveMicrophones( return NO_INIT; } +status_t StagefrightRecorder::setMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setMicrophoneDirection(%d)", direction); + mSelectedMicDirection = direction; + if (mAudioSourceNode != 0) { + return mAudioSourceNode->setMicrophoneDirection(direction); + } + return NO_INIT; +} + +status_t StagefrightRecorder::setMicrophoneFieldDimension(float zoom) { + ALOGV("setMicrophoneFieldDimension(%f)", zoom); + mSelectedMicFieldDimension = zoom; + if (mAudioSourceNode != 0) { + return mAudioSourceNode->setMicrophoneFieldDimension(zoom); + } + return NO_INIT; +} + status_t StagefrightRecorder::getPortId(audio_port_handle_t *portId) const { if (mAudioSourceNode != 0) { return mAudioSourceNode->getPortId(portId); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index a292e58d96..236b19e932 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -77,6 +77,8 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual void setAudioDeviceCallback(const sp& callback); virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones(std::vector* activeMicrophones); + virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); + virtual status_t setMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) const override; private: @@ -159,6 +161,9 @@ private: bool mDeviceCallbackEnabled; wp mAudioDeviceCallback; + audio_microphone_direction_t mSelectedMicDirection; + float mSelectedMicFieldDimension; + static const int kMaxHighSpeedFps = 1000; status_t prepareInternal(); diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 502730330c..5f86bd32c1 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -52,7 +52,9 @@ static void AudioRecordCallbackFunction(int event, void *user, void *info) { AudioSource::AudioSource( audio_source_t inputSource, const String16 &opPackageName, uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate, - uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId) + uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId, + audio_microphone_direction_t selectedMicDirection, + float selectedMicFieldDimension) : mStarted(false), mSampleRate(sampleRate), mOutSampleRate(outSampleRate > 0 ? outSampleRate : sampleRate), @@ -103,7 +105,9 @@ AudioSource::AudioSource( uid, pid, NULL /*pAttributes*/, - selectedDeviceId); + selectedDeviceId, + selectedMicDirection, + selectedMicFieldDimension); mInitCheck = mRecord->initCheck(); if (mInitCheck != OK) { mRecord.clear(); @@ -506,6 +510,22 @@ status_t AudioSource::getActiveMicrophones( return NO_INIT; } +status_t AudioSource::setMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setMicrophoneDirection(%d)", direction); + if (mRecord != 0) { + return mRecord->setMicrophoneDirection(direction); + } + return NO_INIT; +} + +status_t AudioSource::setMicrophoneFieldDimension(float zoom) { + ALOGV("setMicrophoneFieldDimension(%f)", zoom); + if (mRecord != 0) { + return mRecord->setMicrophoneFieldDimension(zoom); + } + return NO_INIT; +} + status_t AudioSource::getPortId(audio_port_handle_t *portId) const { if (mRecord != 0) { *portId = mRecord->getPortId(); diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h index b0e32d0822..18e5f10552 100644 --- a/media/libstagefright/include/media/stagefright/AudioSource.h +++ b/media/libstagefright/include/media/stagefright/AudioSource.h @@ -44,7 +44,9 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { uint32_t outSampleRate = 0, uid_t uid = -1, pid_t pid = -1, - audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, + audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, + float selectedMicFieldDimension = MIC_FIELD_DIMENSION_NORMAL); status_t initCheck() const; @@ -68,6 +70,8 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { status_t removeAudioDeviceCallback(const sp& callback); status_t getActiveMicrophones(std::vector* activeMicrophones); + status_t setMicrophoneDirection(audio_microphone_direction_t direction); + status_t setMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) const; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 8a45fc2736..977f93b6a5 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7653,14 +7653,14 @@ status_t AudioFlinger::RecordThread::getActiveMicrophones( status_t AudioFlinger::RecordThread::setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("RecordThread::setMicrophoneDirection"); + ALOGV("setMicrophoneDirection(%d)", direction); AutoMutex _l(mLock); return mInput->stream->setMicrophoneDirection(direction); } status_t AudioFlinger::RecordThread::setMicrophoneFieldDimension(float zoom) { - ALOGV("RecordThread::setMicrophoneFieldDimension"); + ALOGV("setMicrophoneFieldDimension(%f)", zoom); AutoMutex _l(mLock); return mInput->stream->setMicrophoneFieldDimension(zoom); } -- GitLab From d097981afa129353c42660869e284ae1784506c2 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 21 Feb 2019 15:51:44 -0800 Subject: [PATCH 1069/1530] AudioFlinger: Send Thread statistics to mediametrics Unify with AudioRecord and AudioTrack mediametrics. Test: mediametrics dumpsys after record, playback Test: CTS Audio(Record|Track)Test#testMediaMetrics Bug: 114112762 Change-Id: I6eb554cbf6ff760f9dea568abd3cde82366b51a7 --- media/libaudioclient/AudioRecord.cpp | 80 ++++++++-------------------- media/libaudioclient/AudioTrack.cpp | 70 +++++------------------- services/audioflinger/Android.mk | 1 + services/audioflinger/Threads.cpp | 71 ++++++++++++++++++++++++ services/audioflinger/Threads.h | 7 +++ 5 files changed, 115 insertions(+), 114 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 9daa299c8c..9c34ea2f09 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -70,70 +70,34 @@ status_t AudioRecord::getMinFrameCount( // --------------------------------------------------------------------------- -static std::string audioFormatTypeString(audio_format_t value) { - std::string formatType; - if (FormatConverter::toString(value, formatType)) { - return formatType; - } - char rawbuffer[16]; // room for "%d" - snprintf(rawbuffer, sizeof(rawbuffer), "%d", value); - return rawbuffer; -} - -static std::string audioSourceString(audio_source_t value) { - std::string source; - if (SourceTypeConverter::toString(value, source)) { - return source; - } - char rawbuffer[16]; // room for "%d" - snprintf(rawbuffer, sizeof(rawbuffer), "%d", value); - return rawbuffer; -} - void AudioRecord::MediaMetrics::gather(const AudioRecord *record) { - // key for media statistics is defined in the header - // attrs for media statistics - // NB: these are matched with public Java API constants defined - // in frameworks/base/media/java/android/media/AudioRecord.java - // These must be kept synchronized with the constants there. - static constexpr char kAudioRecordEncoding[] = "android.media.audiorecord.encoding"; - static constexpr char kAudioRecordSource[] = "android.media.audiorecord.source"; - static constexpr char kAudioRecordLatency[] = "android.media.audiorecord.latency"; - static constexpr char kAudioRecordSampleRate[] = "android.media.audiorecord.samplerate"; - static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels"; - static constexpr char kAudioRecordCreated[] = "android.media.audiorecord.createdMs"; - static constexpr char kAudioRecordDuration[] = "android.media.audiorecord.durationMs"; - static constexpr char kAudioRecordCount[] = "android.media.audiorecord.n"; - static constexpr char kAudioRecordError[] = "android.media.audiorecord.errcode"; - static constexpr char kAudioRecordErrorFunction[] = "android.media.audiorecord.errfunc"; - - // constructor guarantees mAnalyticsItem is valid - - mAnalyticsItem->setInt32(kAudioRecordLatency, record->mLatency); - mAnalyticsItem->setInt32(kAudioRecordSampleRate, record->mSampleRate); - mAnalyticsItem->setInt32(kAudioRecordChannelCount, record->mChannelCount); - mAnalyticsItem->setCString(kAudioRecordEncoding, - audioFormatTypeString(record->mFormat).c_str()); - mAnalyticsItem->setCString(kAudioRecordSource, - audioSourceString(record->mAttributes.source).c_str()); - - // log total duration recording, including anything currently running [and count]. - nsecs_t active = 0; +#define MM_PREFIX "android.media.audiorecord." // avoid cut-n-paste errors. + + // Java API 28 entries, do not change. + mAnalyticsItem->setCString(MM_PREFIX "encoding", toString(record->mFormat).c_str()); + mAnalyticsItem->setCString(MM_PREFIX "source", toString(record->mAttributes.source).c_str()); + mAnalyticsItem->setInt32(MM_PREFIX "latency", (int32_t)record->mLatency); // bad estimate. + mAnalyticsItem->setInt32(MM_PREFIX "samplerate", (int32_t)record->mSampleRate); + mAnalyticsItem->setInt32(MM_PREFIX "channels", (int32_t)record->mChannelCount); + + // Non-API entries, these can change. + mAnalyticsItem->setInt32(MM_PREFIX "portId", (int32_t)record->mPortId); + mAnalyticsItem->setInt32(MM_PREFIX "frameCount", (int32_t)record->mFrameCount); + mAnalyticsItem->setCString(MM_PREFIX "attributes", toString(record->mAttributes).c_str()); + mAnalyticsItem->setInt64(MM_PREFIX "channelMask", (int64_t)record->mChannelMask); + + // log total duration recording, including anything currently running. + int64_t activeNs = 0; if (mStartedNs != 0) { - active = systemTime() - mStartedNs; - } - mAnalyticsItem->setInt64(kAudioRecordDuration, (mDurationNs + active) / (1000 * 1000)); - mAnalyticsItem->setInt32(kAudioRecordCount, mCount); - - // XXX I don't know that this adds a lot of value, long term - if (mCreatedNs != 0) { - mAnalyticsItem->setInt64(kAudioRecordCreated, mCreatedNs / (1000 * 1000)); + activeNs = systemTime() - mStartedNs; } + mAnalyticsItem->setDouble(MM_PREFIX "durationMs", (mDurationNs + activeNs) * 1e-6); + mAnalyticsItem->setInt64(MM_PREFIX "startCount", (int64_t)mCount); if (mLastError != NO_ERROR) { - mAnalyticsItem->setInt32(kAudioRecordError, mLastError); - mAnalyticsItem->setCString(kAudioRecordErrorFunction, mLastErrorFunc.c_str()); + mAnalyticsItem->setInt32(MM_PREFIX "lastError.code", (int32_t)mLastError); + mAnalyticsItem->setCString(MM_PREFIX "lastError.at", mLastErrorFunc.c_str()); } } diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 670a2001c9..8352a48446 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -170,44 +170,8 @@ bool AudioTrack::isDirectOutputSupported(const audio_config_base_t& config, // --------------------------------------------------------------------------- -static std::string audioContentTypeString(audio_content_type_t value) { - std::string contentType; - if (AudioContentTypeConverter::toString(value, contentType)) { - return contentType; - } - char rawbuffer[16]; // room for "%d" - snprintf(rawbuffer, sizeof(rawbuffer), "%d", value); - return rawbuffer; -} - -static std::string audioUsageString(audio_usage_t value) { - std::string usage; - if (UsageTypeConverter::toString(value, usage)) { - return usage; - } - char rawbuffer[16]; // room for "%d" - snprintf(rawbuffer, sizeof(rawbuffer), "%d", value); - return rawbuffer; -} - void AudioTrack::MediaMetrics::gather(const AudioTrack *track) { - - // key for media statistics is defined in the header - // attrs for media statistics - // NB: these are matched with public Java API constants defined - // in frameworks/base/media/java/android/media/AudioTrack.java - // These must be kept synchronized with the constants there. - static constexpr char kAudioTrackStreamType[] = "android.media.audiotrack.streamtype"; - static constexpr char kAudioTrackContentType[] = "android.media.audiotrack.type"; - static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage"; - static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate"; - static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask"; - - // NB: These are not yet exposed as public Java API constants. - static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes"; - static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup"; - // only if we're in a good state... // XXX: shall we gather alternative info if failing? const status_t lstatus = track->initCheck(); @@ -216,28 +180,22 @@ void AudioTrack::MediaMetrics::gather(const AudioTrack *track) return; } - // constructor guarantees mAnalyticsItem is valid - - const int32_t underrunFrames = track->getUnderrunFrames(); - if (underrunFrames != 0) { - mAnalyticsItem->setInt32(kAudioTrackUnderrunFrames, underrunFrames); - } +#define MM_PREFIX "android.media.audiotrack." // avoid cut-n-paste errors. - if (track->mTimestampStartupGlitchReported) { - mAnalyticsItem->setInt32(kAudioTrackStartupGlitch, 1); - } + // Java API 28 entries, do not change. + mAnalyticsItem->setCString(MM_PREFIX "streamtype", toString(track->streamType()).c_str()); + mAnalyticsItem->setCString(MM_PREFIX "type", + toString(track->mAttributes.content_type).c_str()); + mAnalyticsItem->setCString(MM_PREFIX "usage", toString(track->mAttributes.usage).c_str()); - if (track->mStreamType != -1) { - // deprecated, but this will tell us who still uses it. - mAnalyticsItem->setInt32(kAudioTrackStreamType, track->mStreamType); - } - // XXX: consider including from mAttributes: source type - mAnalyticsItem->setCString(kAudioTrackContentType, - audioContentTypeString(track->mAttributes.content_type).c_str()); - mAnalyticsItem->setCString(kAudioTrackUsage, - audioUsageString(track->mAttributes.usage).c_str()); - mAnalyticsItem->setInt32(kAudioTrackSampleRate, track->mSampleRate); - mAnalyticsItem->setInt64(kAudioTrackChannelMask, track->mChannelMask); + // Non-API entries, these can change due to a Java string mistake. + mAnalyticsItem->setInt32(MM_PREFIX "sampleRate", (int32_t)track->mSampleRate); + mAnalyticsItem->setInt64(MM_PREFIX "channelMask", (int64_t)track->mChannelMask); + // Non-API entries, these can change. + mAnalyticsItem->setInt32(MM_PREFIX "portId", (int32_t)track->mPortId); + mAnalyticsItem->setCString(MM_PREFIX "encoding", toString(track->mFormat).c_str()); + mAnalyticsItem->setInt32(MM_PREFIX "frameCount", (int32_t)track->mFrameCount); + mAnalyticsItem->setCString(MM_PREFIX "attributes", toString(track->mAttributes).c_str()); } // hand the user a snapshot of the metrics. diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 91b75870ab..40980a6991 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libaudioclient \ libmedialogservice \ + libmediametrics \ libmediautils \ libnbaio \ libnblog \ diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 8f181a4038..468676af00 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -488,6 +488,8 @@ AudioFlinger::ThreadBase::~ThreadBase() sp binder = IInterface::asBinder(mPowerManager); binder->unlinkToDeath(mDeathRecipient); } + + sendStatistics(true /* force */); } status_t AudioFlinger::ThreadBase::readyToRun() @@ -571,6 +573,15 @@ void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event, pi // sendIoConfigEvent_l() must be called with ThreadBase::mLock held void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, pid_t pid) { + // The audio statistics history is exponentially weighted to forget events + // about five or more seconds in the past. In order to have + // crisper statistics for mediametrics, we reset the statistics on + // an IoConfigEvent, to reflect different properties for a new device. + mIoJitterMs.reset(); + mLatencyMs.reset(); + mProcessTimeMs.reset(); + mTimestampVerifier.discontinuity(); + sp configEvent = (ConfigEvent *)new IoConfigEvent(event, pid); sendConfigEvent_l(configEvent); } @@ -1651,6 +1662,65 @@ void AudioFlinger::ThreadBase::broadcast_l() mWaitWorkCV.broadcast(); } +// Call only from threadLoop() or when it is idle. +// Do not call from high performance code as this may do binder rpc to the MediaMetrics service. +void AudioFlinger::ThreadBase::sendStatistics(bool force) +{ + // Do not log if we have no stats. + // We choose the timestamp verifier because it is the most likely item to be present. + const int64_t nstats = mTimestampVerifier.getN() - mLastRecordedTimestampVerifierN; + if (nstats == 0) { + return; + } + + // Don't log more frequently than once per 12 hours. + // We use BOOTTIME to include suspend time. + const int64_t timeNs = systemTime(SYSTEM_TIME_BOOTTIME); + const int64_t sinceNs = timeNs - mLastRecordedTimeNs; // ok if mLastRecordedTimeNs = 0 + if (!force && sinceNs <= 12 * NANOS_PER_HOUR) { + return; + } + + mLastRecordedTimestampVerifierN = mTimestampVerifier.getN(); + mLastRecordedTimeNs = timeNs; + + std::unique_ptr item(MediaAnalyticsItem::create("audiothread")); + +#define MM_PREFIX "android.media.audiothread." // avoid cut-n-paste errors. + + // thread configuration + item->setInt32(MM_PREFIX "id", (int32_t)mId); // IO handle + // item->setInt32(MM_PREFIX "portId", (int32_t)mPortId); + item->setCString(MM_PREFIX "type", threadTypeToString(mType)); + item->setInt32(MM_PREFIX "sampleRate", (int32_t)mSampleRate); + item->setInt64(MM_PREFIX "channelMask", (int64_t)mChannelMask); + item->setCString(MM_PREFIX "encoding", toString(mFormat).c_str()); + item->setInt32(MM_PREFIX "frameCount", (int32_t)mFrameCount); + item->setCString(MM_PREFIX "outDevice", toString(mOutDevice).c_str()); + item->setCString(MM_PREFIX "inDevice", toString(mInDevice).c_str()); + + // thread statistics + if (mIoJitterMs.getN() > 0) { + item->setDouble(MM_PREFIX "ioJitterMs.mean", mIoJitterMs.getMean()); + item->setDouble(MM_PREFIX "ioJitterMs.std", mIoJitterMs.getStdDev()); + } + if (mProcessTimeMs.getN() > 0) { + item->setDouble(MM_PREFIX "processTimeMs.mean", mProcessTimeMs.getMean()); + item->setDouble(MM_PREFIX "processTimeMs.std", mProcessTimeMs.getStdDev()); + } + const auto tsjitter = mTimestampVerifier.getJitterMs(); + if (tsjitter.getN() > 0) { + item->setDouble(MM_PREFIX "timestampJitterMs.mean", tsjitter.getMean()); + item->setDouble(MM_PREFIX "timestampJitterMs.std", tsjitter.getStdDev()); + } + if (mLatencyMs.getN() > 0) { + item->setDouble(MM_PREFIX "latencyMs.mean", mLatencyMs.getMean()); + item->setDouble(MM_PREFIX "latencyMs.std", mLatencyMs.getStdDev()); + } + + item->selfrecord(); +} + // ---------------------------------------------------------------------------- // Playback // ---------------------------------------------------------------------------- @@ -3447,6 +3517,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() LOG_AUDIO_STATE(); } mStandby = true; + sendStatistics(false /* force */); } if (mActiveTracks.isEmpty() && mConfigEvents.isEmpty()) { diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 1afea08b73..97aa9f044a 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -399,6 +399,9 @@ public: virtual void dump(int fd, const Vector& args) = 0; + // deliver stats to mediametrics. + void sendStatistics(bool force); + mutable Mutex mLock; protected: @@ -522,6 +525,10 @@ protected: audio_utils::Statistics mProcessTimeMs{0.995 /* alpha */}; audio_utils::Statistics mLatencyMs{0.995 /* alpha */}; + // Save the last count when we delivered statistics to mediametrics. + int64_t mLastRecordedTimestampVerifierN = 0; + int64_t mLastRecordedTimeNs = 0; // BOOTTIME to include suspend. + bool mIsMsdDevice = false; // A condition that must be evaluated by the thread loop has changed and // we must not wait for async write callback in the thread loop before evaluating it -- GitLab From 4d6916a561adb9215c2deabc3600ad4df01c5a07 Mon Sep 17 00:00:00 2001 From: ybai Date: Thu, 28 Feb 2019 14:34:37 +0800 Subject: [PATCH 1070/1530] Fix AC-4 parser bugs according to specification pre_virtualized and add_emdf_substreams should locate in the same level with single_substream_group checking. Bug: 127685179 Test: see repro steps Change-Id: Ic715b316f2b0d0fc86dfd9db03ba7bf01a1f0a2e --- media/extractors/mp4/AC4Parser.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp index 59a2e9bd9e..13d60c88c6 100644 --- a/media/extractors/mp4/AC4Parser.cpp +++ b/media/extractors/mp4/AC4Parser.cpp @@ -260,7 +260,7 @@ bool AC4DSIParser::parse() { int32_t short_program_id = -1; if (bitstream_version > 1) { - if (ac4_dsi_version == 0){ + if (ac4_dsi_version == 0) { ALOGE("invalid ac4 dsi"); return false; } @@ -295,6 +295,7 @@ bool AC4DSIParser::parse() { bool b_single_substream_group = false; uint32_t presentation_config = 0, presentation_version = 0; uint32_t pres_bytes = 0; + uint64_t start = 0; if (ac4_dsi_version == 0) { CHECK_BITS_LEFT(1 + 5 + 5); @@ -315,6 +316,8 @@ bool AC4DSIParser::parse() { mBitReader.skipBits(pres_bytes * 8); continue; } + /* record a marker, less the size of the presentation_config */ + start = (mDSISize - mBitReader.numBitsLeft()) / 8; // ac4_presentation_v0_dsi(), ac4_presentation_v1_dsi() and ac4_presentation_v2_dsi() // all start with a presentation_config of 5 bits CHECK_BITS_LEFT(5); @@ -338,9 +341,6 @@ bool AC4DSIParser::parse() { (presentation_config >= NELEM(PresentationConfig) ? "reserved" : PresentationConfig[presentation_config])); - /* record a marker, less the size of the presentation_config */ - uint64_t start = (mDSISize - mBitReader.numBitsLeft()) / 8; - bool b_add_emdf_substreams = false; if (!b_single_substream_group && presentation_config == 6) { b_add_emdf_substreams = true; @@ -535,14 +535,14 @@ bool AC4DSIParser::parse() { } break; } - CHECK_BITS_LEFT(1 + 1); - bool b_pre_virtualized = (mBitReader.getBits(1) == 1); - mPresentations[presentation].mPreVirtualized = b_pre_virtualized; - b_add_emdf_substreams = (mBitReader.getBits(1) == 1); - ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized)); - ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, - BOOLSTR(b_add_emdf_substreams)); } + CHECK_BITS_LEFT(1 + 1); + bool b_pre_virtualized = (mBitReader.getBits(1) == 1); + mPresentations[presentation].mPreVirtualized = b_pre_virtualized; + b_add_emdf_substreams = (mBitReader.getBits(1) == 1); + ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized)); + ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, + BOOLSTR(b_add_emdf_substreams)); } if (b_add_emdf_substreams) { CHECK_BITS_LEFT(7); @@ -599,10 +599,6 @@ bool AC4DSIParser::parse() { if (ac4_dsi_version == 1) { uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8; - if (mBitReader.numBitsLeft() % 8 != 0) { - end += 1; - } - uint64_t presentation_bytes = end - start; uint64_t skip_bytes = pres_bytes - presentation_bytes; ALOGV("skipping = %" PRIu64 " bytes", skip_bytes); @@ -612,7 +608,7 @@ bool AC4DSIParser::parse() { // we should know this or something is probably wrong // with the bitstream (or we don't support it) - if (mPresentations[presentation].mChannelMode == -1){ + if (mPresentations[presentation].mChannelMode == -1) { ALOGE("could not determing channel mode of presentation %d", presentation); return false; } -- GitLab From 4e9bfa02a291f69a181e9de6d5f9902cb756d6a0 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Wed, 6 Mar 2019 16:33:59 -0800 Subject: [PATCH 1071/1530] Fixed routing change bug (1) Change logic of addAudioDeviceCallback() since mJAudioTrack is likely to be null when this function is called. (2) Replace add/removeGlobalRef() with JObjectHolder (3) Replace std::vector with utils/Vector.h Test: android.media.cts.RoutingTest#test_MediaPlayer2_RoutingChangedCallback with routing change log Bug: 127314187 Change-Id: I62d9c7600c710b49f0435bd1fc2741d79c928b55 --- media/libmediaplayer2/JAudioTrack.cpp | 26 ++++------------- .../MediaPlayer2AudioOutput.cpp | 28 +++++++++---------- .../include/mediaplayer2/JAudioTrack.h | 19 +++---------- .../mediaplayer2/MediaPlayer2AudioOutput.h | 3 +- 4 files changed, 24 insertions(+), 52 deletions(-) diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index a01afa3acf..910edff99f 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -571,8 +571,8 @@ status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) { } void JAudioTrack::registerRoutingDelegates( - std::vector>& routingDelegates) { - for (std::vector>::iterator it = routingDelegates.begin(); + Vector>& routingDelegates) { + for (Vector>::iterator it = routingDelegates.begin(); it != routingDelegates.end(); it++) { addAudioDeviceCallback(it->second, getHandler(it->second)); } @@ -597,23 +597,9 @@ jobject JAudioTrack::getHandler(const jobject routingDelegateObj) { return env->CallObjectMethod(routingDelegateObj, jGetHandler); } -jobject JAudioTrack::addGlobalRef(const jobject obj) { +jobject JAudioTrack::findByKey(Vector>& mp, const jobject key) { JNIEnv *env = JavaVMHelper::getJNIEnv(); - return reinterpret_cast(env->NewGlobalRef(obj)); -} - -status_t JAudioTrack::removeGlobalRef(const jobject obj) { - if (obj == NULL) { - return BAD_VALUE; - } - JNIEnv *env = JavaVMHelper::getJNIEnv(); - env->DeleteGlobalRef(obj); - return NO_ERROR; -} - -jobject JAudioTrack::findByKey(std::vector>& mp, const jobject key) { - JNIEnv *env = JavaVMHelper::getJNIEnv(); - for (std::vector>::iterator it = mp.begin(); it != mp.end(); it++) { + for (Vector>::iterator it = mp.begin(); it != mp.end(); it++) { if (env->IsSameObject(it->first, key)) { return it->second; } @@ -621,9 +607,9 @@ jobject JAudioTrack::findByKey(std::vector>& mp, con return nullptr; } -void JAudioTrack::eraseByKey(std::vector>& mp, const jobject key) { +void JAudioTrack::eraseByKey(Vector>& mp, const jobject key) { JNIEnv *env = JavaVMHelper::getJNIEnv(); - for (std::vector>::iterator it = mp.begin(); it != mp.end(); it++) { + for (Vector>::iterator it = mp.begin(); it != mp.end(); it++) { if (env->IsSameObject(it->first, key)) { mp.erase(it); return; diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp index 4de92ad13f..7c3063d72c 100644 --- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp +++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp @@ -85,9 +85,6 @@ MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(int32_t sessionId, uid_t uid, i } MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() { - for (auto routingDelegate : mRoutingDelegates) { - JAudioTrack::removeGlobalRef(routingDelegate.second); - } close(); delete mCallbackData; } @@ -524,13 +521,16 @@ jobject MediaPlayer2AudioOutput::getRoutedDevice() { status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) { ALOGV("addAudioDeviceCallback"); Mutex::Autolock lock(mLock); - jobject listener = JAudioTrack::getListener(jRoutingDelegate); - if (mJAudioTrack != nullptr && - JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) { - jobject handler = JAudioTrack::getHandler(jRoutingDelegate); - jobject routingDelegate = JAudioTrack::addGlobalRef(jRoutingDelegate); + jobject listener = (new JObjectHolder( + JAudioTrack::getListener(jRoutingDelegate)))->getJObject(); + if (JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) { + jobject handler = (new JObjectHolder( + JAudioTrack::getHandler(jRoutingDelegate)))->getJObject(); + jobject routingDelegate = (new JObjectHolder(jRoutingDelegate))->getJObject(); mRoutingDelegates.push_back(std::pair(listener, routingDelegate)); - return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler); + if (mJAudioTrack != nullptr) { + return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler); + } } return NO_ERROR; } @@ -539,13 +539,11 @@ status_t MediaPlayer2AudioOutput::removeAudioDeviceCallback(jobject listener) { ALOGV("removeAudioDeviceCallback"); Mutex::Autolock lock(mLock); jobject routingDelegate = nullptr; - if (mJAudioTrack != nullptr && - (routingDelegate = JAudioTrack::findByKey(mRoutingDelegates, listener)) != nullptr) { - mJAudioTrack->removeAudioDeviceCallback(routingDelegate); - JAudioTrack::eraseByKey(mRoutingDelegates, listener); - if (JAudioTrack::removeGlobalRef(routingDelegate) != NO_ERROR) { - return BAD_VALUE; + if ((routingDelegate = JAudioTrack::findByKey(mRoutingDelegates, listener)) != nullptr) { + if (mJAudioTrack != nullptr) { + mJAudioTrack->removeAudioDeviceCallback(routingDelegate); } + JAudioTrack::eraseByKey(mRoutingDelegates, listener); } return NO_ERROR; } diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h index 87dc889786..7381286d8c 100644 --- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h +++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h @@ -17,7 +17,6 @@ #ifndef ANDROID_JAUDIOTRACK_H #define ANDROID_JAUDIOTRACK_H -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #include // It has dependency on audio.h/Errors.h, but doesn't // include them in it. Therefore it is included here at last. @@ -405,7 +405,7 @@ public: * routingDelegates: backed-up routing delegates * */ - void registerRoutingDelegates(std::vector>& routingDelegates); + void registerRoutingDelegates(Vector>& routingDelegates); /* get listener from RoutingDelegate object */ @@ -415,17 +415,6 @@ public: */ static jobject getHandler(const jobject routingDelegateObj); - /* convert local reference to global reference. - */ - static jobject addGlobalRef(const jobject obj); - - /* erase global reference. - * - * Returns NO_ERROR if succeeds - * BAD_VALUE if obj is NULL - */ - static status_t removeGlobalRef(const jobject obj); - /* * Parameters: * map and key @@ -433,13 +422,13 @@ public: * Returns value if key is in the map * nullptr if key is not in the map */ - static jobject findByKey(std::vector>& mp, const jobject key); + static jobject findByKey(Vector>& mp, const jobject key); /* * Parameters: * map and key */ - static void eraseByKey(std::vector>& mp, const jobject key); + static void eraseByKey(Vector>& mp, const jobject key); private: audio_output_flags_t mFlags; diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h index bda4f611fe..1b3f2dc5dc 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -125,7 +124,7 @@ private: audio_output_flags_t mFlags; sp mPreferredDevice; mutable Mutex mLock; - std::vector> mRoutingDelegates; // + Vector> mRoutingDelegates; // // static variables below not protected by mutex static bool mIsOnEmulator; -- GitLab From ea8fa7a5eea3d55c75982787571d70a70fa5280a Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 22 Feb 2019 14:41:50 -0800 Subject: [PATCH 1072/1530] Refactor adjust channels buffer provider. Combine adjust channels buffer provider and adjust channels non-destructive buffer provider. When contracting mono + haptic channels to mono, do not use adjust_channels to avoid mixing haptic channel into audio channel. Bug: 125915810 Test: play audio-haptic coupled files Change-Id: I39916a3e0628a44ed4a148c265564af7135a2176 --- .../libaudioclient/include/media/AudioMixer.h | 15 ++-- media/libaudioprocessing/AudioMixer.cpp | 26 +++--- media/libaudioprocessing/BufferProviders.cpp | 83 ++++++++----------- .../libmedia/include/media/BufferProviders.h | 36 +++----- 4 files changed, 71 insertions(+), 89 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 41b425f6ee..783eef38ec 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -273,7 +273,7 @@ private: mPostDownmixReformatBufferProvider.reset(nullptr); mDownmixerBufferProvider.reset(nullptr); mReformatBufferProvider.reset(nullptr); - mAdjustChannelsNonDestructiveBufferProvider.reset(nullptr); + mContractChannelsNonDestructiveBufferProvider.reset(nullptr); mAdjustChannelsBufferProvider.reset(nullptr); } @@ -347,8 +347,12 @@ private: * all pre-mixer track buffer conversions outside the AudioMixer class. * * 1) mInputBufferProvider: The AudioTrack buffer provider. - * 2) mAdjustChannelsBufferProvider: Expend or contracts data - * 3) mAdjustChannelsNonDestructiveBufferProvider: Non-destructively adjust sample data + * 2) mAdjustChannelsBufferProvider: Expands or contracts sample data from one interleaved + * channel format to another. Expanded channels are filled with zeros and put at the end + * of each audio frame. Contracted channels are copied to the end of the buffer. + * 3) mContractChannelsNonDestructiveBufferProvider: Non-destructively contract sample data. + * This is currently using at audio-haptic coupled playback to separate audio and haptic + * data. Contracted channels could be written to given buffer. * 4) mReformatBufferProvider: If not NULL, performs the audio reformat to * match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer * requires reformat. For example, it may convert floating point input to @@ -360,9 +364,10 @@ private: * 7) mTimestretchBufferProvider: Adds timestretching for playback rate */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. - // TODO: combine AdjustChannelsBufferProvider and AdjustChannelsNonDestructiveBufferProvider + // TODO: combine mAdjustChannelsBufferProvider and + // mContractChannelsNonDestructiveBufferProvider std::unique_ptr mAdjustChannelsBufferProvider; - std::unique_ptr mAdjustChannelsNonDestructiveBufferProvider; + std::unique_ptr mContractChannelsNonDestructiveBufferProvider; std::unique_ptr mReformatBufferProvider; std::unique_ptr mDownmixerBufferProvider; std::unique_ptr mPostDownmixReformatBufferProvider; diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 2c57db72a1..f7cc09620e 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -408,8 +408,8 @@ status_t AudioMixer::Track::prepareForAdjustChannels() void AudioMixer::Track::unprepareForAdjustChannelsNonDestructive() { ALOGV("AUDIOMIXER::unprepareForAdjustChannelsNonDestructive"); - if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { - mAdjustChannelsNonDestructiveBufferProvider.reset(nullptr); + if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { + mContractChannelsNonDestructiveBufferProvider.reset(nullptr); reconfigureBufferProviders(); } } @@ -426,13 +426,13 @@ status_t AudioMixer::Track::prepareForAdjustChannelsNonDestructive(size_t frames ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame( mMixerChannelCount, mMixerFormat) : NULL; - mAdjustChannelsNonDestructiveBufferProvider.reset( - new AdjustChannelsNonDestructiveBufferProvider( + mContractChannelsNonDestructiveBufferProvider.reset( + new AdjustChannelsBufferProvider( mFormat, mAdjustNonDestructiveInChannelCount, mAdjustNonDestructiveOutChannelCount, - mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID, frames, + mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID, buffer)); reconfigureBufferProviders(); } @@ -441,9 +441,9 @@ status_t AudioMixer::Track::prepareForAdjustChannelsNonDestructive(size_t frames void AudioMixer::Track::clearContractedBuffer() { - if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { - static_cast( - mAdjustChannelsNonDestructiveBufferProvider.get())->clearContractedFrames(); + if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { + static_cast( + mContractChannelsNonDestructiveBufferProvider.get())->clearContractedFrames(); } } @@ -455,9 +455,9 @@ void AudioMixer::Track::reconfigureBufferProviders() mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider); bufferProvider = mAdjustChannelsBufferProvider.get(); } - if (mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { - mAdjustChannelsNonDestructiveBufferProvider->setBufferProvider(bufferProvider); - bufferProvider = mAdjustChannelsNonDestructiveBufferProvider.get(); + if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { + mContractChannelsNonDestructiveBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mContractChannelsNonDestructiveBufferProvider.get(); } if (mReformatBufferProvider.get() != nullptr) { mReformatBufferProvider->setBufferProvider(bufferProvider); @@ -966,8 +966,8 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider track->mDownmixerBufferProvider->reset(); } else if (track->mReformatBufferProvider.get() != nullptr) { track->mReformatBufferProvider->reset(); - } else if (track->mAdjustChannelsNonDestructiveBufferProvider.get() != nullptr) { - track->mAdjustChannelsNonDestructiveBufferProvider->reset(); + } else if (track->mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { + track->mContractChannelsNonDestructiveBufferProvider->reset(); } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) { track->mAdjustChannelsBufferProvider->reset(); } diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp index b764ccbc8c..21d25e18ca 100644 --- a/media/libaudioprocessing/BufferProviders.cpp +++ b/media/libaudioprocessing/BufferProviders.cpp @@ -627,79 +627,68 @@ void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames } } -AdjustChannelsBufferProvider::AdjustChannelsBufferProvider(audio_format_t format, - size_t inChannelCount, size_t outChannelCount, size_t frameCount) : - CopyBufferProvider( - audio_bytes_per_frame(inChannelCount, format), - audio_bytes_per_frame(outChannelCount, format), - frameCount), - mFormat(format), - mInChannelCount(inChannelCount), - mOutChannelCount(outChannelCount), - mSampleSizeInBytes(audio_bytes_per_sample(format)) -{ - ALOGV("AdjustBufferProvider(%p)(%#x, %zu, %zu, %zu)", - this, format, inChannelCount, outChannelCount, frameCount); -} - -void AdjustChannelsBufferProvider::copyFrames(void *dst, const void *src, size_t frames) -{ - adjust_channels(src, mInChannelCount, dst, mOutChannelCount, mSampleSizeInBytes, - frames * mInChannelCount * mSampleSizeInBytes); -} - -AdjustChannelsNonDestructiveBufferProvider::AdjustChannelsNonDestructiveBufferProvider( +AdjustChannelsBufferProvider::AdjustChannelsBufferProvider( audio_format_t format, size_t inChannelCount, size_t outChannelCount, - audio_format_t contractedFormat, size_t contractedFrameCount, void* contractedBuffer) : + size_t frameCount, audio_format_t contractedFormat, void* contractedBuffer) : CopyBufferProvider( + audio_bytes_per_frame(inChannelCount, format), audio_bytes_per_frame(std::max(inChannelCount, outChannelCount), format), - audio_bytes_per_frame(std::max(inChannelCount, outChannelCount), format), - contractedFrameCount), + frameCount), mFormat(format), mInChannelCount(inChannelCount), mOutChannelCount(outChannelCount), mSampleSizeInBytes(audio_bytes_per_sample(format)), + mFrameCount(frameCount), mContractedChannelCount(inChannelCount - outChannelCount), mContractedFormat(contractedFormat), - mContractedFrameCount(contractedFrameCount), mContractedBuffer(contractedBuffer), mContractedWrittenFrames(0) { - ALOGV("AdjustChannelsNonDestructiveBufferProvider(%p)(%#x, %zu, %zu, %#x, %p)", - this, format, inChannelCount, outChannelCount, contractedFormat, contractedBuffer); + ALOGV("AdjustChannelsBufferProvider(%p)(%#x, %zu, %zu, %zu, %#x, %p)", this, format, + inChannelCount, outChannelCount, frameCount, contractedFormat, contractedBuffer); if (mContractedFormat != AUDIO_FORMAT_INVALID && mInChannelCount > mOutChannelCount) { mContractedFrameSize = audio_bytes_per_frame(mContractedChannelCount, mContractedFormat); } } -status_t AdjustChannelsNonDestructiveBufferProvider::getNextBuffer( - AudioBufferProvider::Buffer* pBuffer) +status_t AdjustChannelsBufferProvider::getNextBuffer(AudioBufferProvider::Buffer* pBuffer) { - const size_t outFramesLeft = mContractedFrameCount - mContractedWrittenFrames; - if (outFramesLeft < pBuffer->frameCount) { - // Restrict the frame count so that we don't write over the size of the output buffer. - pBuffer->frameCount = outFramesLeft; + if (mContractedBuffer != nullptr) { + // Restrict frame count only when it is needed to save contracted frames. + const size_t outFramesLeft = mFrameCount - mContractedWrittenFrames; + if (outFramesLeft < pBuffer->frameCount) { + // Restrict the frame count so that we don't write over the size of the output buffer. + pBuffer->frameCount = outFramesLeft; + } } return CopyBufferProvider::getNextBuffer(pBuffer); } -void AdjustChannelsNonDestructiveBufferProvider::copyFrames( - void *dst, const void *src, size_t frames) +void AdjustChannelsBufferProvider::copyFrames(void *dst, const void *src, size_t frames) { - adjust_channels_non_destructive(src, mInChannelCount, dst, mOutChannelCount, mSampleSizeInBytes, - frames * mInChannelCount * mSampleSizeInBytes); - if (mContractedFormat != AUDIO_FORMAT_INVALID && mContractedBuffer != NULL - && mInChannelCount > mOutChannelCount) { - const size_t contractedIdx = frames * mOutChannelCount * mSampleSizeInBytes; - memcpy_by_audio_format( - (uint8_t*)mContractedBuffer + mContractedWrittenFrames * mContractedFrameSize, - mContractedFormat, (uint8_t*)dst + contractedIdx, mFormat, - mContractedChannelCount * frames); - mContractedWrittenFrames += frames; + if (mInChannelCount > mOutChannelCount) { + // For case multi to mono, adjust_channels has special logic that will mix first two input + // channels into a single output channel. In that case, use adjust_channels_non_destructive + // to keep only one channel data even when contracting to mono. + adjust_channels_non_destructive(src, mInChannelCount, dst, mOutChannelCount, + mSampleSizeInBytes, frames * mInChannelCount * mSampleSizeInBytes); + if (mContractedFormat != AUDIO_FORMAT_INVALID + && mContractedBuffer != nullptr) { + const size_t contractedIdx = frames * mOutChannelCount * mSampleSizeInBytes; + memcpy_by_audio_format( + (uint8_t*) mContractedBuffer + mContractedWrittenFrames * mContractedFrameSize, + mContractedFormat, (uint8_t*) dst + contractedIdx, mFormat, + mContractedChannelCount * frames); + mContractedWrittenFrames += frames; + } + } else { + // Prefer expanding data from the end of each audio frame. + adjust_channels(src, mInChannelCount, dst, mOutChannelCount, + mSampleSizeInBytes, frames * mInChannelCount * mSampleSizeInBytes); } } -void AdjustChannelsNonDestructiveBufferProvider::reset() +void AdjustChannelsBufferProvider::reset() { mContractedWrittenFrames = 0; CopyBufferProvider::reset(); diff --git a/media/libmedia/include/media/BufferProviders.h b/media/libmedia/include/media/BufferProviders.h index ea41527501..b0388542d5 100644 --- a/media/libmedia/include/media/BufferProviders.h +++ b/media/libmedia/include/media/BufferProviders.h @@ -218,33 +218,21 @@ private: bool mAudioPlaybackRateValid; // flag for current parameters validity }; -// AdjustBufferProvider derives from CopyBufferProvider to adjust sample data. +// AdjustChannelsBufferProvider derives from CopyBufferProvider to adjust sample data. // Expands or contracts sample data from one interleaved channel format to another. -// Expanded channels are filled with zeros and put at the end of each audio frame. -// Contracted channels are omitted from the end of each audio frame. +// Extra expanded channels are filled with zeros and put at the end of each audio frame. +// Contracted channels are copied to the end of the output buffer(storage should be +// allocated appropriately). +// Contracted channels could be written to output buffer. class AdjustChannelsBufferProvider : public CopyBufferProvider { public: AdjustChannelsBufferProvider(audio_format_t format, size_t inChannelCount, - size_t outChannelCount, size_t frameCount); - //Overrides - void copyFrames(void *dst, const void *src, size_t frames) override; - -protected: - const audio_format_t mFormat; - const size_t mInChannelCount; - const size_t mOutChannelCount; - const size_t mSampleSizeInBytes; -}; - -// AdjustChannelsNonDestructiveBufferProvider derives from CopyBufferProvider to adjust sample data. -// Expands or contracts sample data from one interleaved channel format to another. -// Extra expanded channels are interleaved in from the end of the input buffer. -// Contracted channels are copied to the end of the output buffer. -// Contracted channels could be written to output buffer. -class AdjustChannelsNonDestructiveBufferProvider : public CopyBufferProvider { -public: - AdjustChannelsNonDestructiveBufferProvider(audio_format_t format, size_t inChannelCount, - size_t outChannelCount, audio_format_t contractedFormat, size_t contractedFrameCount, + size_t outChannelCount, size_t frameCount) : AdjustChannelsBufferProvider( + format, inChannelCount, outChannelCount, + frameCount, AUDIO_FORMAT_INVALID, nullptr) { } + // Contracted data is converted to contractedFormat and put into contractedBuffer. + AdjustChannelsBufferProvider(audio_format_t format, size_t inChannelCount, + size_t outChannelCount, size_t frameCount, audio_format_t contractedFormat, void* contractedBuffer); //Overrides status_t getNextBuffer(Buffer* pBuffer) override; @@ -258,9 +246,9 @@ protected: const size_t mInChannelCount; const size_t mOutChannelCount; const size_t mSampleSizeInBytes; + const size_t mFrameCount; const size_t mContractedChannelCount; const audio_format_t mContractedFormat; - const size_t mContractedFrameCount; void *mContractedBuffer; size_t mContractedWrittenFrames; size_t mContractedFrameSize; -- GitLab From 0bce0c6bc7dfa58639433d0781d311217e8bb6ac Mon Sep 17 00:00:00 2001 From: Jeff Hamilton Date: Wed, 6 Mar 2019 22:23:40 -0500 Subject: [PATCH 1073/1530] Add manifests for media and media.swcodec APEXs Set the minSdk and targetSdk to 28 for Play ingestion. Bug: 127713324 Change-Id: Ifa0ea6f3e055dc3d244879f5bd54441932cee631 --- apex/Android.bp | 16 ++++++++++++++++ apex/AndroidManifest-media.xml | 25 +++++++++++++++++++++++++ apex/AndroidManifest-swcodec.xml | 25 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 apex/AndroidManifest-media.xml create mode 100644 apex/AndroidManifest-swcodec.xml diff --git a/apex/Android.bp b/apex/Android.bp index 9455290e4e..f1828569f4 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -42,6 +42,9 @@ apex_defaults { }, key: "com.android.media.key", certificate: ":com.android.media.certificate", + + // Use a custom AndroidManifest.xml used for API targeting. + androidManifest: ":com.android.media-androidManifest", } apex { @@ -50,6 +53,16 @@ apex { defaults: ["com.android.media-defaults"], } +filegroup { + name: "com.android.media-androidManifest", + srcs: ["AndroidManifest-media.xml"], +} + +filegroup { + name: "com.android.media.swcodec-androidManifest", + srcs: ["AndroidManifest-swcodec.xml"], +} + apex_defaults { name: "com.android.media.swcodec-defaults", native_shared_libs: [ @@ -58,6 +71,9 @@ apex_defaults { use_vendor: true, key: "com.android.media.swcodec.key", certificate: ":com.android.media.swcodec.certificate", + + // Use a custom AndroidManifest.xml used for API targeting. + androidManifest: ":com.android.media.swcodec-androidManifest", } apex { diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml new file mode 100644 index 0000000000..17d3f3ab55 --- /dev/null +++ b/apex/AndroidManifest-media.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml new file mode 100644 index 0000000000..bd20dc0b8d --- /dev/null +++ b/apex/AndroidManifest-swcodec.xml @@ -0,0 +1,25 @@ + + + + + + + -- GitLab From 4d3e69452332f74a3e6c14381869930fef2e092f Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 7 Mar 2019 09:17:48 -0800 Subject: [PATCH 1074/1530] Multiple extractors from same client vs clients binder space Lowered limits for when to use shared buffers vs inline data and how much total data to allow in a mediasource binder transaction. Fix typo limiting MediaSource / extractor to only 1 mediabuffer as shared memory. Bug: 115848790 Test: poc from bug --- media/extractors/mp4/MPEG4Extractor.cpp | 2 +- media/extractors/wav/WAVExtractor.cpp | 2 +- media/libmedia/include/media/IMediaSource.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index c776c519e7..c812445502 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -90,7 +90,7 @@ public: virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read(MediaBufferHelper **buffer, const ReadOptions *options = NULL); - virtual bool supportNonblockingRead() { return true; } + bool supportsNonBlockingRead() override { return true; } virtual media_status_t fragmentedRead( MediaBufferHelper **buffer, const ReadOptions *options = NULL); diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index e58bd1f3a5..5679de8dad 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -81,7 +81,7 @@ struct WAVSource : public MediaTrackHelper { virtual media_status_t read( MediaBufferHelper **buffer, const ReadOptions *options = NULL); - virtual bool supportNonblockingRead() { return true; } + bool supportsNonBlockingRead() override { return true; } protected: virtual ~WAVSource(); diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h index 7a4b1b949e..5ab6e37464 100644 --- a/media/libmedia/include/media/IMediaSource.h +++ b/media/libmedia/include/media/IMediaSource.h @@ -126,8 +126,8 @@ public: static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource static const size_t kTransferSharedAsSharedThreshold = 4 * 1024; // if >= shared, else inline - static const size_t kTransferInlineAsSharedThreshold = 64 * 1024; // if >= shared, else inline - static const size_t kInlineMaxTransfer = 256 * 1024; // Binder size limited to BINDER_VM_SIZE. + static const size_t kTransferInlineAsSharedThreshold = 8 * 1024; // if >= shared, else inline + static const size_t kInlineMaxTransfer = 64 * 1024; // Binder size limited to BINDER_VM_SIZE. protected: virtual ~BnMediaSource(); -- GitLab From 0a63ec2909f10b72ea7a177373a8a5e3a1d03ab6 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 7 Mar 2019 11:21:06 -0800 Subject: [PATCH 1075/1530] aaudio_loopback: Allow specifying input device id This is useful when there are multiple audio devices in the system. Test: use aaudio_loopback on a TV device with USB soundcard connected specify -d -D Change-Id: I60e84132f5974db6f4314c58f76705018e22b7db --- media/libaaudio/examples/loopback/src/loopback.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index 6578156870..c7c42eb225 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -295,6 +295,7 @@ static void usage() { AAudioArgsParser::usage(); printf(" -B{frames} input capacity in frames\n"); printf(" -C{channels} number of input channels\n"); + printf(" -D{deviceId} input device ID\n"); printf(" -F{0,1,2} input format, 1=I16, 2=FLOAT\n"); printf(" -g{gain} recirculating loopback gain\n"); printf(" -h{hangMillis} occasionally hang in the callback\n"); @@ -393,6 +394,7 @@ int main(int argc, const char **argv) AAudioStream *outputStream = nullptr; aaudio_result_t result = AAUDIO_OK; + int32_t requestedInputDeviceId = AAUDIO_UNSPECIFIED; aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED; int requestedInputChannelCount = kNumInputChannels; aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_UNSPECIFIED; @@ -431,6 +433,9 @@ int main(int argc, const char **argv) case 'C': requestedInputChannelCount = atoi(&arg[2]); break; + case 'D': + requestedInputDeviceId = atoi(&arg[2]); + break; case 'F': requestedInputFormat = atoi(&arg[2]); break; @@ -529,6 +534,7 @@ int main(int argc, const char **argv) printf("INPUT stream ----------------------------------------\n"); // Use different parameters for the input. + argParser.setDeviceId(requestedInputDeviceId); argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED); argParser.setFormat(requestedInputFormat); argParser.setPerformanceMode(inputPerformanceLevel); -- GitLab From c6b74cbf65ed602395119c1eb5886c7b1e444727 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Thu, 7 Mar 2019 11:22:11 -0800 Subject: [PATCH 1076/1530] clearkey hidl: expand mock error handling * handle mock error for offline license APIs * add mock INVALID_STATE and UNKNOWN errors Bug: 118402843 Test: VtsHalDrmV1_2TargetTest Change-Id: Id59610d44f5786b2bb030c46e41aaae3d6c5b954 --- drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp | 15 ++++++++++++++- .../clearkey/hidl/include/ClearKeyDrmProperties.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp index badb99e402..d7dacb8a68 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -527,6 +527,10 @@ Return DrmPlugin::setPropertyString( mMockError = Status_V1_2::ERROR_DRM_SESSION_LOST_STATE; } else if (value == kFrameTooLargeValue) { mMockError = Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE; + } else if (value == kInvalidStateValue) { + mMockError = Status_V1_2::ERROR_DRM_INVALID_STATE; + } else { + mMockError = Status_V1_2::ERROR_DRM_UNKNOWN; } } @@ -683,6 +687,10 @@ Return DrmPlugin::getMetrics(getMetrics_cb _hidl_cb) { Return DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) { std::vector licenseNames = mFileHandle.ListLicenses(); std::vector keySetIds; + if (mMockError != Status_V1_2::OK) { + _hidl_cb(toStatus_1_0(mMockError), keySetIds); + return Void(); + } for (const auto& name : licenseNames) { std::vector keySetId(name.begin(), name.end()); keySetIds.push_back(keySetId); @@ -693,6 +701,9 @@ Return DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb Return DrmPlugin::removeOfflineLicense(const KeySetId& keySetId) { + if (mMockError != Status_V1_2::OK) { + return toStatus_1_0(mMockError); + } std::string licenseName(keySetId.begin(), keySetId.end()); if (mFileHandle.DeleteLicense(licenseName)) { return Status::OK; @@ -706,7 +717,9 @@ Return DrmPlugin::getOfflineLicenseState(const KeySetId& keySetId, DeviceFiles::LicenseState state; std::string license; OfflineLicenseState hLicenseState; - if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) { + if (mMockError != Status_V1_2::OK) { + _hidl_cb(toStatus_1_0(mMockError), OfflineLicenseState::UNKNOWN); + } else if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) { switch (state) { case DeviceFiles::kLicenseStateActive: hLicenseState = OfflineLicenseState::USABLE; diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h index 1bbc822fbf..b83ce69d4e 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h @@ -40,6 +40,7 @@ static const std::string kDrmErrorTestValue(""); static const std::string kResourceContentionValue("resourceContention"); static const std::string kLostStateValue("lostState"); static const std::string kFrameTooLargeValue("frameTooLarge"); +static const std::string kInvalidStateValue("invalidState"); static const std::string kDeviceIdKey("deviceId"); static const uint8_t kTestDeviceIdData[] = -- GitLab From 23ecaaf5481fe75585db2452b710c010955a8e01 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Thu, 7 Mar 2019 11:19:32 -0800 Subject: [PATCH 1077/1530] clearkey hidl: return ERROR_DRM_FRAME_TOO_LARGE when frame is too large Bug: 118402843 Test: VtsHalDrmV1_2TargetTest Change-Id: I4c26c4faac5cdd53f37d0b50b192e18b14d7c8ae --- drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp index fc0cceb66e..23a35e5bdb 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp @@ -135,7 +135,7 @@ Return CryptoPlugin::decrypt_1_2( } if (destBuffer.offset + destBuffer.size > destBase->getSize()) { - _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size"); return Void(); } destPtr = static_cast(base + destination.nonsecureMemory.offset); -- GitLab From 6c17e2107b76dd2b40c79e4236cb690f2361d5d9 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 19 Feb 2019 14:51:47 -0800 Subject: [PATCH 1078/1530] Camera: Enable physical camera setting in camera ndk/vndk Port physical camera settings support to ndk/vndk. Test: Related camera VTS and NDK/VNDK tests pass Bug: 115532726 Change-Id: Ie2d46b4ec041d2cec3c02145fbf06cf70eec5ac3 --- camera/ndk/NdkCameraDevice.cpp | 29 ++- camera/ndk/NdkCaptureRequest.cpp | 53 +++++ camera/ndk/impl/ACameraDevice.cpp | 70 +++++- camera/ndk/impl/ACameraDevice.h | 10 +- camera/ndk/impl/ACaptureRequest.h | 4 +- camera/ndk/include/camera/NdkCameraDevice.h | 52 +++++ camera/ndk/include/camera/NdkCameraManager.h | 6 - .../include/camera/NdkCameraMetadataTags.h | 22 ++ camera/ndk/include/camera/NdkCaptureRequest.h | 213 ++++++++++++++++++ camera/ndk/libcamera2ndk.map.txt | 8 + camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 96 +++++--- camera/ndk/ndk_vendor/impl/ACameraDevice.h | 9 +- .../tests/AImageReaderVendorTest.cpp | 143 +++++++----- 13 files changed, 598 insertions(+), 117 deletions(-) diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp index 98608da774..09b85d5490 100644 --- a/camera/ndk/NdkCameraDevice.cpp +++ b/camera/ndk/NdkCameraDevice.cpp @@ -78,7 +78,34 @@ camera_status_t ACameraDevice_createCaptureRequest( ALOGE("%s: unknown template ID %d", __FUNCTION__, templateId); return ACAMERA_ERROR_INVALID_PARAMETER; } - return device->createCaptureRequest(templateId, request); + return device->createCaptureRequest(templateId, nullptr /*physicalIdList*/, request); +} + +EXPORT +camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds( + const ACameraDevice* device, + ACameraDevice_request_template templateId, + const ACameraIdList* physicalCameraIdList, + ACaptureRequest** request) { + ATRACE_CALL(); + if (device == nullptr || request == nullptr || physicalCameraIdList == nullptr) { + ALOGE("%s: invalid argument! device %p request %p, physicalCameraIdList %p", + __FUNCTION__, device, request, physicalCameraIdList); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + switch (templateId) { + case TEMPLATE_PREVIEW: + case TEMPLATE_STILL_CAPTURE: + case TEMPLATE_RECORD: + case TEMPLATE_VIDEO_SNAPSHOT: + case TEMPLATE_ZERO_SHUTTER_LAG: + case TEMPLATE_MANUAL: + break; + default: + ALOGE("%s: unknown template ID %d", __FUNCTION__, templateId); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + return device->createCaptureRequest(templateId, physicalCameraIdList, request); } EXPORT diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp index c64de3ee4d..87de4a9ba3 100644 --- a/camera/ndk/NdkCaptureRequest.cpp +++ b/camera/ndk/NdkCaptureRequest.cpp @@ -97,6 +97,27 @@ camera_status_t ACaptureRequest_getConstEntry( return req->settings->getConstEntry(tag, entry); } +EXPORT +camera_status_t ACaptureRequest_getConstEntry_physicalCamera( + const ACaptureRequest* req, const char* physicalId, + uint32_t tag, ACameraMetadata_const_entry* entry) { + ATRACE_CALL(); + if (req == nullptr || entry == nullptr || physicalId == nullptr) { + ALOGE("%s: invalid argument! req %p, tag 0x%x, entry %p, physicalId %p", + __FUNCTION__, req, tag, entry, physicalId); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + const auto& physicalSettings = req->physicalSettings.find(physicalId); + if (physicalSettings == req->physicalSettings.end()) { + ALOGE("%s: Failed to find metadata for physical camera id %s", + __FUNCTION__, physicalId); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + return physicalSettings->second->getConstEntry(tag, entry); +} + EXPORT camera_status_t ACaptureRequest_getAllTags( const ACaptureRequest* req, /*out*/int32_t* numTags, /*out*/const uint32_t** tags) { @@ -131,6 +152,34 @@ SET_ENTRY(rational,ACameraMetadata_rational) #undef SET_ENTRY +#define SET_PHYSICAL_ENTRY(NAME,NDK_TYPE) \ +EXPORT \ +camera_status_t ACaptureRequest_setEntry_physicalCamera_##NAME( \ + ACaptureRequest* req, const char* physicalId, uint32_t tag, \ + uint32_t count, const NDK_TYPE* data) { \ + ATRACE_CALL(); \ + if (req == nullptr || (count > 0 && data == nullptr) || physicalId == nullptr) { \ + ALOGE("%s: invalid argument! req %p, tag 0x%x, count %d, data 0x%p, physicalId %p", \ + __FUNCTION__, req, tag, count, data, physicalId); \ + return ACAMERA_ERROR_INVALID_PARAMETER; \ + } \ + if (req->physicalSettings.find(physicalId) == req->physicalSettings.end()) { \ + ALOGE("%s: Failed to find metadata for physical camera id %s", \ + __FUNCTION__, physicalId); \ + return ACAMERA_ERROR_INVALID_PARAMETER; \ + } \ + return req->physicalSettings[physicalId]->update(tag, count, data); \ +} + +SET_PHYSICAL_ENTRY(u8,uint8_t) +SET_PHYSICAL_ENTRY(i32,int32_t) +SET_PHYSICAL_ENTRY(float,float) +SET_PHYSICAL_ENTRY(double,double) +SET_PHYSICAL_ENTRY(i64,int64_t) +SET_PHYSICAL_ENTRY(rational,ACameraMetadata_rational) + +#undef SET_PHYSICAL_ENTRY + EXPORT void ACaptureRequest_free(ACaptureRequest* request) { ATRACE_CALL(); @@ -138,6 +187,7 @@ void ACaptureRequest_free(ACaptureRequest* request) { return; } request->settings.clear(); + request->physicalSettings.clear(); delete request->targets; delete request; return; @@ -174,6 +224,9 @@ ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) { ACaptureRequest* pRequest = new ACaptureRequest(); pRequest->settings = new ACameraMetadata(*(src->settings)); + for (const auto& entry : src->physicalSettings) { + pRequest->physicalSettings[entry.first] = new ACameraMetadata(*(entry.second)); + } pRequest->targets = new ACameraOutputTargets(); *(pRequest->targets) = *(src->targets); pRequest->context = src->context; diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index d8a57650c8..5e4fcd07fd 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -76,7 +76,7 @@ CameraDevice::CameraDevice( __FUNCTION__, strerror(-err), err); setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); } - mHandler = new CallbackHandler(); + mHandler = new CallbackHandler(id); mCbLooper->registerHandler(mHandler); const CameraMetadata& metadata = mChars->getInternalData(); @@ -97,6 +97,14 @@ CameraDevice::CameraDevice( mShadingMapSize[0] = entry.data.i32[0]; mShadingMapSize[1] = entry.data.i32[1]; } + + size_t physicalIdCnt = 0; + const char*const* physicalCameraIds; + if (mChars->isLogicalMultiCamera(&physicalIdCnt, &physicalCameraIds)) { + for (size_t i = 0; i < physicalIdCnt; i++) { + mPhysicalIds.push_back(physicalCameraIds[i]); + } + } } // Device close implementaiton @@ -129,8 +137,29 @@ CameraDevice::postSessionMsgAndCleanup(sp& msg) { camera_status_t CameraDevice::createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalIdList, ACaptureRequest** request) const { Mutex::Autolock _l(mDeviceLock); + + if (physicalIdList != nullptr) { + if (physicalIdList->numCameras > static_cast(mPhysicalIds.size())) { + ALOGE("%s: physicalIdList size %d exceeds number of available physical cameras %zu", + __FUNCTION__, physicalIdList->numCameras, mPhysicalIds.size()); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + for (auto i = 0; i < physicalIdList->numCameras; i++) { + if (physicalIdList->cameraIds[i] == nullptr) { + ALOGE("%s: physicalId is null!", __FUNCTION__); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + if (mPhysicalIds.end() == std::find( + mPhysicalIds.begin(), mPhysicalIds.end(), physicalIdList->cameraIds[i])) { + ALOGE("%s: Invalid physicalId %s!", __FUNCTION__, physicalIdList->cameraIds[i]); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + } + } + camera_status_t ret = checkCameraClosedOrErrorLocked(); if (ret != ACAMERA_OK) { return ret; @@ -151,6 +180,12 @@ CameraDevice::createCaptureRequest( } ACaptureRequest* outReq = new ACaptureRequest(); outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST); + if (physicalIdList != nullptr) { + for (auto i = 0; i < physicalIdList->numCameras; i++) { + outReq->physicalSettings.emplace(physicalIdList->cameraIds[i], + new ACameraMetadata(*(outReq->settings))); + } + } outReq->targets = new ACameraOutputTargets(); *request = outReq; return ACAMERA_OK; @@ -275,8 +310,12 @@ CameraDevice::allocateCaptureRequest( const ACaptureRequest* request, /*out*/sp& outReq) { camera_status_t ret; sp req(new CaptureRequest()); - req->mPhysicalCameraSettings.push_back({std::string(mCameraId.string()), + req->mPhysicalCameraSettings.push_back({getId(), request->settings->getInternalData()}); + for (auto& entry : request->physicalSettings) { + req->mPhysicalCameraSettings.push_back({entry.first, + entry.second->getInternalData()}); + } req->mIsReprocess = false; // NDK does not support reprocessing yet req->mContext = request->context; req->mSurfaceConverted = true; // set to true, and fill in stream/surface idx to speed up IPC @@ -320,10 +359,17 @@ CameraDevice::allocateCaptureRequest( } ACaptureRequest* -CameraDevice::allocateACaptureRequest(sp& req) { +CameraDevice::allocateACaptureRequest(sp& req, const std::string& deviceId) { ACaptureRequest* pRequest = new ACaptureRequest(); - CameraMetadata clone = req->mPhysicalCameraSettings.begin()->settings; - pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + for (auto& entry : req->mPhysicalCameraSettings) { + CameraMetadata clone = entry.settings; + if (entry.id == deviceId) { + pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + } else { + pRequest->physicalSettings.emplace(entry.id, + new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST)); + } + } pRequest->targets = new ACameraOutputTargets(); for (size_t i = 0; i < req->mSurfaceList.size(); i++) { ANativeWindow* anw = static_cast(req->mSurfaceList[i].get()); @@ -340,6 +386,7 @@ CameraDevice::freeACaptureRequest(ACaptureRequest* req) { return; } req->settings.clear(); + req->physicalSettings.clear(); delete req->targets; delete req; } @@ -786,6 +833,9 @@ CameraDevice::onCaptureErrorLocked( return; } +CameraDevice::CallbackHandler::CallbackHandler(const char* id) : mId(id) { +} + void CameraDevice::CallbackHandler::onMessageReceived( const sp &msg) { switch (msg->what()) { @@ -927,7 +977,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( ALOGE("%s: Cannot find timestamp!", __FUNCTION__); return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); (*onStart)(context, session.get(), request, timestamp); freeACaptureRequest(request); break; @@ -950,7 +1000,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } sp result(static_cast(obj.get())); - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); (*onResult)(context, session.get(), request, result.get()); freeACaptureRequest(request); break; @@ -1006,7 +1056,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get()); } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); (*onResult)(context, session.get(), request, result.get(), physicalResultInfo.size(), physicalCameraIdPtrs.data(), physicalMetadataCopyPtrs.data()); @@ -1034,7 +1084,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( static_cast(obj.get())); ACameraCaptureFailure* failure = static_cast(failureSp.get()); - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); (*onFail)(context, session.get(), request, failure); freeACaptureRequest(request); break; @@ -1111,7 +1161,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); (*onBufferLost)(context, session.get(), request, anw, frameNumber); freeACaptureRequest(request); break; diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index d0f363bfbb..103efd5218 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -68,6 +68,7 @@ class CameraDevice final : public RefBase { camera_status_t createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalIdList, ACaptureRequest** request) const; camera_status_t createCaptureSession( @@ -145,7 +146,8 @@ class CameraDevice final : public RefBase { camera_status_t allocateCaptureRequest( const ACaptureRequest* request, sp& outReq); - static ACaptureRequest* allocateACaptureRequest(sp& req); + static ACaptureRequest* allocateACaptureRequest(sp& req, + const std::string& deviceId); static void freeACaptureRequest(ACaptureRequest*); // only For session to hold device lock @@ -230,9 +232,11 @@ class CameraDevice final : public RefBase { class CallbackHandler : public AHandler { public: + explicit CallbackHandler(const char* id); void onMessageReceived(const sp &msg) override; private: + std::string mId; // This handler will cache all capture session sp until kWhatCleanUpSessions // is processed. This is used to guarantee the last session reference is always // being removed in callback thread without holding camera device lock @@ -327,6 +331,7 @@ class CameraDevice final : public RefBase { // Misc variables int32_t mShadingMapSize[2]; // const after constructor int32_t mPartialResultCount; // const after constructor + std::vector mPhysicalIds; // const after constructor }; @@ -351,8 +356,9 @@ struct ACameraDevice { camera_status_t createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalCameraIdList, ACaptureRequest** request) const { - return mDevice->createCaptureRequest(templateId, request); + return mDevice->createCaptureRequest(templateId, physicalCameraIdList, request); } camera_status_t createCaptureSession( diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h index 5c82ab7fa7..2ffcafecb2 100644 --- a/camera/ndk/impl/ACaptureRequest.h +++ b/camera/ndk/impl/ACaptureRequest.h @@ -18,6 +18,7 @@ #include #include +#include using namespace android; @@ -59,7 +60,8 @@ struct ACaptureRequest { return ACAMERA_OK; } - sp settings; + sp settings; + std::unordered_map> physicalSettings; ACameraOutputTargets* targets; void* context; }; diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index 26af4f8787..cedf83a1a5 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -53,6 +53,17 @@ __BEGIN_DECLS */ typedef struct ACameraDevice ACameraDevice; +/** + * Struct to hold list of camera device Ids. This can refer to either the Ids + * of connected camera devices returned from {@link ACameraManager_getCameraIdList}, + * or the physical camera Ids passed into + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + */ +typedef struct ACameraIdList { + int numCameras; ///< Number of camera device Ids + const char** cameraIds; ///< list of camera device Ids +} ACameraIdList; + /// Enum for ACameraDevice_ErrorStateCallback error code enum { /** @@ -793,6 +804,47 @@ camera_status_t ACaptureSessionPhysicalOutput_create( ACameraWindowType* anw, const char* physicalId, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(29); +/** + * Create a logical multi-camera ACaptureRequest for capturing images, initialized with template + * for a target use case, with the ability to specify physical camera settings. + * + *

    The settings are chosen to be the best options for this camera device, + * so it is not recommended to reuse the same request for a different camera device.

    + * + *

    Note that for all keys in physical camera settings, only the keys + * advertised in ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS are + * applicable. All other keys are ignored by the camera device.

    + * + * @param device the camera device of interest + * @param templateId the type of capture request to be created. + * See {@link ACameraDevice_request_template}. + * @param physicalIdList The list of physical camera Ids that can be used to + * customize the request for a specific physical camera. + * @param request the output request will be stored here if the method call succeeds. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds. The created capture request will be + * filled in request argument.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if device, physicalIdList, or request is + * NULL, templateId is undefined or camera device does not support + * requested template, or if some Ids in physicalIdList isn't a + * valid physical camera backing the current camera device.
    • + *
    • {@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed.
    • + *
    • {@link ACAMERA_ERROR_CAMERA_DEVICE} if the camera device encounters fatal error.
    • + *
    • {@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error.
    • + *
    • {@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.
    + * + * @see TEMPLATE_PREVIEW + * @see TEMPLATE_RECORD + * @see TEMPLATE_STILL_CAPTURE + * @see TEMPLATE_VIDEO_SNAPSHOT + * @see TEMPLATE_MANUAL + */ +camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds( + const ACameraDevice* device, ACameraDevice_request_template templateId, + const ACameraIdList* physicalIdList, + /*out*/ACaptureRequest** request) __INTRODUCED_IN(29); + #endif /* __ANDROID_API__ >= 29 */ __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index ea76738806..8d05ddb2a1 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -74,12 +74,6 @@ ACameraManager* ACameraManager_create() __INTRODUCED_IN(24); */ void ACameraManager_delete(ACameraManager* manager) __INTRODUCED_IN(24); -/// Struct to hold list of camera devices -typedef struct ACameraIdList { - int numCameras; ///< Number of connected camera devices - const char** cameraIds; ///< list of identifier of connected camera devices -} ACameraIdList; - /** * Create a list of currently connected camera devices, including * cameras that may be in use by other camera API clients. diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index b200abf4e8..c26ca69efb 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -3046,6 +3046,28 @@ typedef enum acamera_metadata_tag { */ ACAMERA_REQUEST_AVAILABLE_SESSION_KEYS = // int32[n] ACAMERA_REQUEST_START + 16, + /** + *

    A subset of the available request keys that can be overridden for + * physical devices backing a logical multi-camera.

    + * + *

    Type: int32[n]

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This is a subset of ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS which contains a list + * of keys that can be overridden using Builder#setPhysicalCameraKey. + * The respective value of such request key can be obtained by calling + * Builder#getPhysicalCameraKey. Capture requests that contain + * individual physical device requests must be built via + * Set).

    + * + * @see ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS + */ + ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS = // int32[n] + ACAMERA_REQUEST_START + 17, ACAMERA_REQUEST_END, /** diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h index 136989a338..d3f8826002 100644 --- a/camera/ndk/include/camera/NdkCaptureRequest.h +++ b/camera/ndk/include/camera/NdkCaptureRequest.h @@ -358,6 +358,219 @@ ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) __INTRODUCED_I #endif /* __ANDROID_API__ >= 28 */ +#if __ANDROID_API__ >= 29 + +/** + * Get a metadata entry from input {@link ACaptureRequest} for + * a physical camera backing a logical multi-camera device. + * + *

    Same as ACaptureRequest_getConstEntry, except that if the key is contained + * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function + * returns the entry set by ACaptureRequest_setEntry_physicalCamera_* class of + * functions on the particular physical camera.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag the capture request metadata tag in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS} + * that is set by ACaptureRequest_setEntry_physicalCamera_* class of functions. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if metadata, physicalId, or entry is NULL, + * physicalId is not one of the Ids used in creating the request, or if the capture + * request is a regular request with no physical Ids at all.
    • + *
    • {@link ACAMERA_ERROR_METADATA_NOT_FOUND} if the capture request does not contain an + * entry of input tag value.
    + */ +camera_status_t ACaptureRequest_getConstEntry_physicalCamera( + const ACaptureRequest* request, const char* physicalId, uint32_t tag, + ACameraMetadata_const_entry* entry) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not unsigned 8 bits, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_u8( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const uint8_t* data) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not signed 32 bits, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_i32( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const int32_t* data) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not float, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_float( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const float* data) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not signed 64 bits, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_i64( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const int64_t* data) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not double, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_double( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const double* data) __INTRODUCED_IN(29); + +/** + * 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 + * 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 + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored + * by the camera device.

    + * + * @param request the {@link ACaptureRequest} of interest created by + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param physicalId one of the physical Ids used when request is created with + * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}. + * @param tag one of the capture request metadata tags in + * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is + * larger than zero while data is NULL, the data type of the tag is not rational, + * the tag is not controllable by application, physicalId is not one of the Ids used + * in creating the request, or if the capture request is a regular request with no + * physical Ids at all.
    + */ +camera_status_t ACaptureRequest_setEntry_physicalCamera_rational( + ACaptureRequest* request, const char* physicalId, uint32_t tag, + uint32_t count, const ACameraMetadata_rational* data) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif /* _NDK_CAPTURE_REQUEST_H */ diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index 5a0002290a..2441830547 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -11,6 +11,7 @@ LIBCAMERA2NDK { ACameraCaptureSession_updateSharedOutput; # introduced=28 ACameraDevice_close; ACameraDevice_createCaptureRequest; + ACameraDevice_createCaptureRequest_withPhysicalIds; # introduced=29 ACameraDevice_createCaptureSession; ACameraDevice_createCaptureSessionWithSessionParameters; # introduced=28 ACameraDevice_getId; @@ -34,14 +35,21 @@ LIBCAMERA2NDK { ACaptureRequest_free; ACaptureRequest_getAllTags; ACaptureRequest_getConstEntry; + ACaptureRequest_getConstEntry_physicalCamera; # introduced=29 ACaptureRequest_getUserContext; # introduced=28 ACaptureRequest_removeTarget; ACaptureRequest_setEntry_double; + ACaptureRequest_setEntry_physicalCamera_double; # introduced=29 ACaptureRequest_setEntry_float; + ACaptureRequest_setEntry_physicalCamera_float; # introduced=29 ACaptureRequest_setEntry_i32; + ACaptureRequest_setEntry_physicalCamera_i32; # introduced=29 ACaptureRequest_setEntry_i64; + ACaptureRequest_setEntry_physicalCamera_i64; # introduced=29 ACaptureRequest_setEntry_rational; + ACaptureRequest_setEntry_physicalCamera_rational; # introduced=29 ACaptureRequest_setEntry_u8; + ACaptureRequest_setEntry_physicalCamera_u8; # introduced=29 ACaptureRequest_setUserContext; # introduced=28 ACaptureSessionOutputContainer_add; ACaptureSessionOutputContainer_create; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 9aafcd3913..a38a31e79c 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -138,6 +138,7 @@ CameraDevice::postSessionMsgAndCleanup(sp& msg) { camera_status_t CameraDevice::createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalCameraIdList, ACaptureRequest** request) const { Mutex::Autolock _l(mDeviceLock); camera_status_t ret = checkCameraClosedOrErrorLocked(); @@ -168,6 +169,12 @@ CameraDevice::createCaptureRequest( } ACaptureRequest* outReq = new ACaptureRequest(); outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST); + if (physicalCameraIdList != nullptr) { + for (auto i = 0; i < physicalCameraIdList->numCameras; i++) { + outReq->physicalSettings.emplace(physicalCameraIdList->cameraIds[i], + new ACameraMetadata(*(outReq->settings))); + } + } outReq->targets = new ACameraOutputTargets(); *request = outReq; return ACAMERA_OK; @@ -292,29 +299,16 @@ camera_status_t CameraDevice::allocateCaptureRequestLocked( const ACaptureRequest* request, /*out*/sp &outReq) { sp req(new CaptureRequest()); - req->mCaptureRequest.physicalCameraSettings.resize(1); - req->mCaptureRequest.physicalCameraSettings[0].id = mCameraId; - // TODO: Do we really need to copy the metadata here ? - CameraMetadata metadataCopy = request->settings->getInternalData(); - const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock(); - HCameraMetadata hCameraMetadata; - utils::convertToHidl(cameraMetadata, &hCameraMetadata); - metadataCopy.unlock(cameraMetadata); - if (request->settings != nullptr) { - if (hCameraMetadata.data() != nullptr && - mCaptureRequestMetadataQueue != nullptr && - mCaptureRequestMetadataQueue->write( - reinterpret_cast(hCameraMetadata.data()), - hCameraMetadata.size())) { - // The metadata field of the union would've been destructued, so no need - // to re-size it. - req->mCaptureRequest.physicalCameraSettings[0].settings.fmqMetadataSize( - hCameraMetadata.size()); - } else { - ALOGE("Fmq write capture result failed, falling back to hwbinder"); - req->mCaptureRequest.physicalCameraSettings[0].settings.metadata( - std::move(hCameraMetadata)); - } + req->mCaptureRequest.physicalCameraSettings.resize(1 + request->physicalSettings.size()); + + size_t index = 0; + allocateOneCaptureRequestMetadata( + req->mCaptureRequest.physicalCameraSettings[index++], mCameraId, request->settings); + + for (auto& physicalEntry : request->physicalSettings) { + allocateOneCaptureRequestMetadata( + req->mCaptureRequest.physicalCameraSettings[index++], + physicalEntry.first, physicalEntry.second); } std::vector requestStreamIdxList; @@ -356,13 +350,48 @@ CameraDevice::allocateCaptureRequestLocked( return ACAMERA_OK; } +void CameraDevice::allocateOneCaptureRequestMetadata( + PhysicalCameraSettings& cameraSettings, + const std::string& id, const sp& metadata) { + cameraSettings.id = id; + // TODO: Do we really need to copy the metadata here ? + CameraMetadata metadataCopy = metadata->getInternalData(); + const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock(); + HCameraMetadata hCameraMetadata; + utils::convertToHidl(cameraMetadata, &hCameraMetadata); + metadataCopy.unlock(cameraMetadata); + if (metadata != nullptr) { + if (hCameraMetadata.data() != nullptr && + mCaptureRequestMetadataQueue != nullptr && + mCaptureRequestMetadataQueue->write( + reinterpret_cast(hCameraMetadata.data()), + hCameraMetadata.size())) { + // The metadata field of the union would've been destructued, so no need + // to re-size it. + cameraSettings.settings.fmqMetadataSize(hCameraMetadata.size()); + } else { + ALOGE("Fmq write capture result failed, falling back to hwbinder"); + cameraSettings.settings.metadata(std::move(hCameraMetadata)); + } + } +} + + ACaptureRequest* -CameraDevice::allocateACaptureRequest(sp& req) { +CameraDevice::allocateACaptureRequest(sp& req, const char* deviceId) { ACaptureRequest* pRequest = new ACaptureRequest(); - CameraMetadata clone; - utils::convertFromHidlCloned(req->mPhysicalCameraSettings[0].settings.metadata(), &clone); - pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); - pRequest->targets = new ACameraOutputTargets(); + for (size_t i = 0; i < req->mPhysicalCameraSettings.size(); i++) { + const std::string& id = req->mPhysicalCameraSettings[i].id; + CameraMetadata clone; + utils::convertFromHidlCloned(req->mPhysicalCameraSettings[i].settings.metadata(), &clone); + if (id == deviceId) { + pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + } else { + pRequest->physicalSettings[req->mPhysicalCameraSettings[i].id] = + new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + } + } + pRequest->targets = new ACameraOutputTargets(); for (size_t i = 0; i < req->mSurfaceList.size(); i++) { native_handle_t* anw = req->mSurfaceList[i]; ACameraOutputTarget outputTarget(anw); @@ -930,6 +959,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } sp session(static_cast(obj.get())); + ACameraDevice* device = session->getDevice(); mCachedSessions.push(session); sp requestSp = nullptr; switch (msg->what()) { @@ -979,7 +1009,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( ALOGE("%s: Cannot find timestamp!", __FUNCTION__); return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); (*onStart)(context, session.get(), request, timestamp); freeACaptureRequest(request); break; @@ -1002,7 +1032,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } sp result(static_cast(obj.get())); - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); (*onResult)(context, session.get(), request, result.get()); freeACaptureRequest(request); break; @@ -1055,7 +1085,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get()); } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); (*onResult)(context, session.get(), request, result.get(), physicalResultInfo.size(), physicalCameraIdPtrs.data(), physicalMetadataCopyPtrs.data()); @@ -1084,7 +1114,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( static_cast(obj.get())); ACameraCaptureFailure* failure = static_cast(failureSp.get()); - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); (*onFail)(context, session.get(), request, failure); freeACaptureRequest(request); break; @@ -1161,7 +1191,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp); + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); (*onBufferLost)(context, session.get(), request, anw, frameNumber); freeACaptureRequest(request); break; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index d5715854e9..28092fd373 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -92,6 +92,7 @@ class CameraDevice final : public RefBase { camera_status_t createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalCameraIdList, ACaptureRequest** request) const; camera_status_t createCaptureSession( @@ -176,8 +177,11 @@ class CameraDevice final : public RefBase { // metadata associated with it. camera_status_t allocateCaptureRequestLocked( const ACaptureRequest* request, sp& outReq); + void allocateOneCaptureRequestMetadata( + PhysicalCameraSettings& cameraSettings, + const std::string& id, const sp& metadata); - static ACaptureRequest* allocateACaptureRequest(sp& req); + static ACaptureRequest* allocateACaptureRequest(sp& req, const char* deviceId); static void freeACaptureRequest(ACaptureRequest*); // only For session to hold device lock @@ -380,8 +384,9 @@ struct ACameraDevice { camera_status_t createCaptureRequest( ACameraDevice_request_template templateId, + const ACameraIdList* physicalCameraIdList, ACaptureRequest** request) const { - return mDevice->createCaptureRequest(templateId, request); + return mDevice->createCaptureRequest(templateId, physicalCameraIdList, request); } camera_status_t createCaptureSession( diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 93108b0995..2398922048 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -61,7 +61,8 @@ class CameraHelper { native_handle_t* anw; }; int initCamera(native_handle_t* imgReaderAnw, - const std::vector& physicalImgReaders) { + const std::vector& physicalImgReaders, + bool usePhysicalSettings) { if (imgReaderAnw == nullptr) { ALOGE("Cannot initialize camera before image reader get initialized."); return -1; @@ -97,6 +98,7 @@ class CameraHelper { return ret; } + std::vector idPointerList; for (auto& physicalStream : physicalImgReaders) { ACaptureSessionOutput* sessionOutput = nullptr; ret = ACaptureSessionPhysicalOutput_create(physicalStream.anw, @@ -113,7 +115,11 @@ class CameraHelper { mExtraOutputs.push_back(sessionOutput); // Assume that at most one physical stream per physical camera. mPhysicalCameraIds.push_back(physicalStream.physicalCameraId); + idPointerList.push_back(physicalStream.physicalCameraId); } + ACameraIdList cameraIdList; + cameraIdList.numCameras = idPointerList.size(); + cameraIdList.cameraIds = idPointerList.data(); ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession); if (ret != AMEDIA_OK) { @@ -122,7 +128,13 @@ class CameraHelper { } // Create capture request - ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest); + if (usePhysicalSettings) { + ret = ACameraDevice_createCaptureRequest_withPhysicalIds(mDevice, + TEMPLATE_STILL_CAPTURE, &cameraIdList, &mStillRequest); + } else { + ret = ACameraDevice_createCaptureRequest(mDevice, + TEMPLATE_STILL_CAPTURE, &mStillRequest); + } if (ret != AMEDIA_OK) { ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret); return ret; @@ -557,7 +569,8 @@ class AImageReaderVendorTest : public ::testing::Test { } CameraHelper cameraHelper(id, mCameraManager); - ret = cameraHelper.initCamera(testCase.getNativeWindow(), {}); + ret = cameraHelper.initCamera(testCase.getNativeWindow(), + {}/*physicalImageReaders*/, false/*usePhysicalSettings*/); if (ret < 0) { ALOGE("Unable to initialize camera helper"); return false; @@ -695,6 +708,69 @@ class AImageReaderVendorTest : public ::testing::Test { *cameraId = nullptr; return; } + + void testLogicalCameraPhysicalStream(bool usePhysicalSettings) { + const char* cameraId = nullptr; + ACameraMetadata* staticMetadata = nullptr; + std::vector physicalCameraIds; + + findCandidateLogicalCamera(&cameraId, &staticMetadata, &physicalCameraIds); + if (cameraId == nullptr) { + // Couldn't find logical camera to test + return; + } + + // Test streaming the logical multi-camera + uint64_t readerUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; + int32_t readerMaxImages = 8; + bool readerAsync = false; + const int pictureCount = 6; + std::vector testCases; + for (size_t i = 0; i < 3; i++) { + ImageReaderTestCase* testCase = new ImageReaderTestCase( + kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, + readerMaxImages, readerAsync); + ASSERT_EQ(testCase->initImageReader(), 0); + testCases.push_back(testCase); + } + + CameraHelper cameraHelper(cameraId, mCameraManager); + std::vector physicalImgReaderInfo; + physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()}); + physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()}); + + int ret = cameraHelper.initCamera(testCases[0]->getNativeWindow(), + physicalImgReaderInfo, usePhysicalSettings); + ASSERT_EQ(ret, 0); + + if (!cameraHelper.isCameraReady()) { + ALOGW("Camera is not ready after successful initialization. It's either due to camera " + "on board lacks BACKWARDS_COMPATIBLE capability or the device does not have " + "camera on board."); + return; + } + + for (int i = 0; i < pictureCount; i++) { + ret = cameraHelper.takeLogicalCameraPicture(); + ASSERT_EQ(ret, 0); + } + + // Sleep until all capture finished + for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { + usleep(kCaptureWaitUs); + if (testCases[0]->getAcquiredImageCount() == pictureCount) { + ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, + pictureCount); + break; + } + } + ASSERT_EQ(testCases[0]->getAcquiredImageCount(), pictureCount); + ASSERT_EQ(testCases[1]->getAcquiredImageCount(), pictureCount); + ASSERT_EQ(testCases[2]->getAcquiredImageCount(), pictureCount); + ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); + + ACameraMetadata_free(staticMetadata); + } }; TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) { @@ -722,65 +798,8 @@ TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) { } TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) { - const char* cameraId = nullptr; - ACameraMetadata* staticMetadata = nullptr; - std::vector physicalCameraIds; - - findCandidateLogicalCamera(&cameraId, &staticMetadata, &physicalCameraIds); - if (cameraId == nullptr) { - // Couldn't find logical camera to test - return; - } - - // Test streaming the logical multi-camera - uint64_t readerUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; - int32_t readerMaxImages = 8; - bool readerAsync = false; - const int pictureCount = 6; - std::vector testCases; - for (size_t i = 0; i < 3; i++) { - ImageReaderTestCase* testCase = new ImageReaderTestCase( - kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages, - readerAsync); - ASSERT_EQ(testCase->initImageReader(), 0); - testCases.push_back(testCase); - } - - CameraHelper cameraHelper(cameraId, mCameraManager); - std::vector physicalImgReaderInfo; - physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()}); - physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()}); - - int ret = cameraHelper.initCamera(testCases[0]->getNativeWindow(), physicalImgReaderInfo); - ASSERT_EQ(ret, 0); - - if (!cameraHelper.isCameraReady()) { - ALOGW("Camera is not ready after successful initialization. It's either due to camera on " - "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on " - "board."); - return; - } - - for (int i = 0; i < pictureCount; i++) { - ret = cameraHelper.takeLogicalCameraPicture(); - ASSERT_EQ(ret, 0); - } - - // Sleep until all capture finished - for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) { - usleep(kCaptureWaitUs); - if (testCases[0]->getAcquiredImageCount() == pictureCount) { - ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000, - pictureCount); - break; - } - } - ASSERT_EQ(testCases[0]->getAcquiredImageCount(), pictureCount); - ASSERT_EQ(testCases[1]->getAcquiredImageCount(), pictureCount); - ASSERT_EQ(testCases[2]->getAcquiredImageCount(), pictureCount); - ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); - - ACameraMetadata_free(staticMetadata); + testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/); + testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/); } } // namespace -- GitLab From f380958e56b85c47bcce05dba73d227f063d8d8c Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Thu, 21 Feb 2019 10:40:23 +0530 Subject: [PATCH 1079/1530] VTS: Fix DecodeTest For Audio Decoders. Removed extra start() from DecodeTest for audio decoders. Test: VtsHidlC2V1_0TargetAudioDecTest -I software -C c2.android.aac.decoder -P /sdcard/res/ Bug: 127691660 Change-Id: Iff7ecdaad83ea3753c3cd444511f41deee57f782 --- .../1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index d3b37d782b..443faabb08 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -515,7 +515,6 @@ TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { if (mDisableTest) return; uint32_t streamIndex = GetParam(); - ASSERT_EQ(mComponent->start(), C2_OK); mTimestampDevTest = true; char mURL[512], info[512]; std::ifstream eleStream, eleInfo; -- GitLab From 34ad1d90b3a6e2148357731540c748704ab60556 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Thu, 17 Jan 2019 17:54:22 +0530 Subject: [PATCH 1080/1530] codec2: VTS add support for Empty EOS in AudioEnctest Test: VtsHidlC2V1_0TargetAudioEncTest -I software -P /sdcard/res/ -C c2.android.aac.encoder Bug: 127691941 Change-Id: Ibd9696156c19e042584101285551742b7c91d4d0 --- .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp index a74d43ee44..57e54d0334 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp @@ -355,12 +355,17 @@ TEST_F(Codec2AudioEncHidlTest, validateCompName) { ASSERT_EQ(mDisableTest, false); } -TEST_F(Codec2AudioEncHidlTest, EncodeTest) { +class Codec2AudioEncEncodeTest : public Codec2AudioEncHidlTest, + public ::testing::WithParamInterface { +}; + +TEST_P(Codec2AudioEncEncodeTest, EncodeTest) { ALOGV("EncodeTest"); if (mDisableTest) return; char mURL[512]; strcpy(mURL, gEnv->getRes().c_str()); GetURLForComponent(mCompName, mURL); + bool signalEOS = GetParam(); // Setting default configuration int32_t nChannels = 2; @@ -408,7 +413,19 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) { ASSERT_NO_FATAL_FAILURE( encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, mLinearPool, eleStream, numFrames, - samplesPerFrame, nChannels, nSampleRate)); + samplesPerFrame, nChannels, nSampleRate, false, + signalEOS)); + + // If EOS is not sent, sending empty input with EOS flag + if (!signalEOS) { + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1)); + ASSERT_NO_FATAL_FAILURE( + testInputBuffer(mComponent, mQueueLock, mWorkQueue, + C2FrameData::FLAG_END_OF_STREAM, false)); + numFrames += 1; + } + // blocking call to ensures application to Wait till all the inputs are // consumed ASSERT_NO_FATAL_FAILURE( @@ -429,6 +446,10 @@ TEST_F(Codec2AudioEncHidlTest, EncodeTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } +// EncodeTest with EOS / No EOS +INSTANTIATE_TEST_CASE_P(EncodeTestwithEOS, Codec2AudioEncEncodeTest, + ::testing::Values(true, false)); + TEST_F(Codec2AudioEncHidlTest, EOSTest) { description("Test empty input buffer with EOS flag"); if (mDisableTest) return; -- GitLab From 26de5a4feba20c424e6d1db38db694d1e83e3c36 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 15 Jan 2019 16:22:03 +0530 Subject: [PATCH 1081/1530] codec2: VTS add support for Empty EOS in VideoDectest Test: VtsHidlC2V1_0TargetVideoDecTest -I software -P /sdcard/res/ -C c2.android.avc.decoder Bug: 127691941 Change-Id: Ib18425e8eeba5a58b9fdaf2a0777bdf599f5f164 --- .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp index 8cbb7a7a4a..dffcb6e0ed 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp @@ -421,8 +421,9 @@ TEST_F(Codec2VideoDecHidlTest, validateCompName) { ASSERT_EQ(mDisableTest, false); } -class Codec2VideoDecDecodeTest : public Codec2VideoDecHidlTest, - public ::testing::WithParamInterface { +class Codec2VideoDecDecodeTest + : public Codec2VideoDecHidlTest, + public ::testing::WithParamInterface> { }; // Bitstream Test @@ -430,7 +431,8 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { description("Decodes input file"); if (mDisableTest) return; - uint32_t streamIndex = GetParam(); + uint32_t streamIndex = GetParam().first; + bool signalEOS = GetParam().second; char mURL[512], info[512]; std::ifstream eleStream, eleInfo; strcpy(mURL, gEnv->getRes().c_str()); @@ -464,8 +466,18 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { ASSERT_EQ(eleStream.is_open(), true); ASSERT_NO_FATAL_FAILURE(decodeNFrames( mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, - mLinearPool, eleStream, &Info, 0, (int)Info.size())); + mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS)); + // If EOS is not sent, sending empty input with EOS flag + size_t infoSize = Info.size(); + if (!signalEOS) { + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1)); + ASSERT_NO_FATAL_FAILURE( + testInputBuffer(mComponent, mQueueLock, mWorkQueue, + C2FrameData::FLAG_END_OF_STREAM, false)); + infoSize += 1; + } // blocking call to ensures application to Wait till all the inputs are // consumed if (!mEos) { @@ -475,19 +487,22 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { } eleStream.close(); - if (mFramesReceived != Info.size()) { + if (mFramesReceived != infoSize) { ALOGE("Input buffer count and Output buffer count mismatch"); ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, - Info.size()); + infoSize); ASSERT_TRUE(false); } if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true); ASSERT_EQ(mComponent->stop(), C2_OK); } - -INSTANTIATE_TEST_CASE_P(StreamIndexes, Codec2VideoDecDecodeTest, - ::testing::Values(0, 1)); +// DecodeTest with StreamIndex and EOS / No EOS +INSTANTIATE_TEST_CASE_P(StreamIndexAndEOS, Codec2VideoDecDecodeTest, + ::testing::Values(std::make_pair(0, false), + std::make_pair(0, true), + std::make_pair(1, false), + std::make_pair(1, true))); // Adaptive Test TEST_F(Codec2VideoDecHidlTest, AdaptiveDecodeTest) { -- GitLab From 272ba8a3923c4f8d0b71824891fc243b1085c953 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Thu, 17 Jan 2019 17:55:18 +0530 Subject: [PATCH 1082/1530] codec2: VTS add support for Empty EOS in VideoEnctest Test: VtsHidlC2V1_0TargetVideoEncTest -I software -P /sdcard/res/ -C c2.android.avc.encoder Bug: 127691941 Change-Id: I0c72075eca0fb2f4f22c5c25b4e64a749ddb35c3 --- .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 7db41c090b..673dc856e8 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -343,13 +343,18 @@ TEST_F(Codec2VideoEncHidlTest, validateCompName) { ASSERT_EQ(mDisableTest, false); } -TEST_F(Codec2VideoEncHidlTest, EncodeTest) { +class Codec2VideoEncEncodeTest : public Codec2VideoEncHidlTest, + public ::testing::WithParamInterface { +}; + +TEST_P(Codec2VideoEncEncodeTest, EncodeTest) { description("Encodes input file"); if (mDisableTest) return; char mURL[512]; int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH; int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT; + bool signalEOS = GetParam(); strcpy(mURL, gEnv->getRes().c_str()); GetURLForComponent(mURL); @@ -367,7 +372,18 @@ TEST_F(Codec2VideoEncHidlTest, EncodeTest) { ASSERT_NO_FATAL_FAILURE( encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, mGraphicPool, eleStream, - 0, ENC_NUM_FRAMES, nWidth, nHeight)); + 0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS)); + + // If EOS is not sent, sending empty input with EOS flag + uint32_t inputFrames = ENC_NUM_FRAMES; + if (!signalEOS) { + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1)); + ASSERT_NO_FATAL_FAILURE( + testInputBuffer(mComponent, mQueueLock, mWorkQueue, + C2FrameData::FLAG_END_OF_STREAM, false)); + inputFrames += 1; + } // blocking call to ensures application to Wait till all the inputs are // consumed @@ -376,10 +392,10 @@ TEST_F(Codec2VideoEncHidlTest, EncodeTest) { waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue)); eleStream.close(); - if (mFramesReceived != ENC_NUM_FRAMES) { + if (mFramesReceived != inputFrames) { ALOGE("Input buffer count and Output buffer count mismatch"); ALOGE("framesReceived : %d inputFrames : %d", mFramesReceived, - ENC_NUM_FRAMES); + inputFrames); ASSERT_TRUE(false); } @@ -394,6 +410,10 @@ TEST_F(Codec2VideoEncHidlTest, EncodeTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } +// EncodeTest with EOS / No EOS +INSTANTIATE_TEST_CASE_P(EncodeTestwithEOS, Codec2VideoEncEncodeTest, + ::testing::Values(true, false)); + TEST_F(Codec2VideoEncHidlTest, EOSTest) { description("Test empty input buffer with EOS flag"); if (mDisableTest) return; -- GitLab From bc6cddd58953ec71553fabc4228b9e0871de236c Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Wed, 30 Jan 2019 17:57:28 +0530 Subject: [PATCH 1083/1530] codec2: VTS add support for nSamplesPerFrame in AudioEnctest Test: VtsHidlC2V1_0TargetAudioEncTest -I software -P /sdcard/res/ -C c2.android.flac.encoder Bug: 127693424 Change-Id: Ic2c2f07314b52bb0c4febdbc3a8b044abd38f709 --- .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp index 57e54d0334..0946fa6052 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp @@ -123,6 +123,7 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { mFramesReceived = 0; if (mCompName == unknown_comp) mDisableTest = true; if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n"; + getInputMaxBufSize(); } virtual void TearDown() override { @@ -157,6 +158,7 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { bool mDisableTest; standardComp mCompName; uint32_t mFramesReceived; + int32_t mInputMaxBufSize; std::list mFlushedIndices; C2BlockPool::local_id_t mBlockPoolId; @@ -175,6 +177,27 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { static void description(const std::string& description) { RecordProperty("description", description); } + + // In encoder components, fetch the size of input buffer allocated + void getInputMaxBufSize() { + int32_t bitStreamInfo[1] = {0}; + std::vector> inParams; + c2_status_t status = mComponent->query( + {}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE}, C2_DONT_BLOCK, + &inParams); + if (status != C2_OK && inParams.size() == 0) { + ALOGE("Query MaxBufferSizeInfo failed => %d", status); + ASSERT_TRUE(false); + } else { + size_t offset = sizeof(C2Param); + for (size_t i = 0; i < inParams.size(); ++i) { + C2Param* param = inParams[i].get(); + bitStreamInfo[i] = *(int32_t*)((uint8_t*)param + offset); + } + } + mInputMaxBufSize = bitStreamInfo[0]; + } + }; void validateComponent( @@ -355,8 +378,9 @@ TEST_F(Codec2AudioEncHidlTest, validateCompName) { ASSERT_EQ(mDisableTest, false); } -class Codec2AudioEncEncodeTest : public Codec2AudioEncHidlTest, - public ::testing::WithParamInterface { +class Codec2AudioEncEncodeTest + : public Codec2AudioEncHidlTest, + public ::testing::WithParamInterface> { }; TEST_P(Codec2AudioEncEncodeTest, EncodeTest) { @@ -365,48 +389,49 @@ TEST_P(Codec2AudioEncEncodeTest, EncodeTest) { char mURL[512]; strcpy(mURL, gEnv->getRes().c_str()); GetURLForComponent(mCompName, mURL); - bool signalEOS = GetParam(); + bool signalEOS = GetParam().first; + // Ratio w.r.t to mInputMaxBufSize + int32_t inputMaxBufRatio = GetParam().second; - // Setting default configuration + // Setting default sampleRate int32_t nChannels = 2; int32_t nSampleRate = 44100; - int32_t samplesPerFrame = 1024; switch (mCompName) { case aac: nChannels = 2; nSampleRate = 48000; - samplesPerFrame = 1024; break; case flac: nChannels = 2; nSampleRate = 48000; - samplesPerFrame = 1152; break; case opus: nChannels = 2; nSampleRate = 48000; - samplesPerFrame = 960; break; case amrnb: nChannels = 1; nSampleRate = 8000; - samplesPerFrame = 160; break; case amrwb: nChannels = 1; nSampleRate = 16000; - samplesPerFrame = 160; break; default: ASSERT_TRUE(false); } + int32_t samplesPerFrame = + ((mInputMaxBufSize / inputMaxBufRatio) / (nChannels * 2)); + ALOGV("signalEOS %d mInputMaxBufSize %d samplesPerFrame %d", signalEOS, + mInputMaxBufSize, samplesPerFrame); + if (!setupConfigParam(mComponent, nChannels, nSampleRate)) { std::cout << "[ WARN ] Test Skipped \n"; return; } ASSERT_EQ(mComponent->start(), C2_OK); std::ifstream eleStream; - uint32_t numFrames = 128; + uint32_t numFrames = 16; eleStream.open(mURL, std::ifstream::binary); ASSERT_EQ(eleStream.is_open(), true); ALOGV("mURL : %s", mURL); @@ -446,9 +471,14 @@ TEST_P(Codec2AudioEncEncodeTest, EncodeTest) { ASSERT_EQ(mComponent->stop(), C2_OK); } -// EncodeTest with EOS / No EOS -INSTANTIATE_TEST_CASE_P(EncodeTestwithEOS, Codec2AudioEncEncodeTest, - ::testing::Values(true, false)); +// EncodeTest with EOS / No EOS and inputMaxBufRatio +// inputMaxBufRatio is ratio w.r.t. to mInputMaxBufSize +INSTANTIATE_TEST_CASE_P(EncodeTest, Codec2AudioEncEncodeTest, + ::testing::Values(std::make_pair(false, 1), + std::make_pair(false, 2), + std::make_pair(true, 1), + std::make_pair(true, 2))); + TEST_F(Codec2AudioEncHidlTest, EOSTest) { description("Test empty input buffer with EOS flag"); -- GitLab From 602a3419438f2fbc05f791ba02f46b611e783fdd Mon Sep 17 00:00:00 2001 From: Umang Saini Date: Tue, 27 Nov 2018 13:11:31 +0530 Subject: [PATCH 1084/1530] codec2: VTS fix Master test for multiple IComponent services List and Create components for all known services. Test: VtsHidlC2V1_0TargetMasterTest -I default Test: VtsHidlC2V1_0TargetMasterTest -I software Bug: 127693385 Change-Id: I25068c746ac95495d45e3f26602d07386413798a --- .../functional/master/VtsHidlC2V1_0TargetMasterTest.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp index 01e64cb39c..e88fbc7d8d 100644 --- a/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp @@ -64,7 +64,7 @@ TEST_F(Codec2MasterHalTest, ListComponents) { C2String name = mClient->getName(); EXPECT_NE(name.empty(), true) << "Invalid Codec2Client Name"; - // Get List of components from HIDL Codec2Client instance + // Get List of components from all known services const std::vector listTraits = mClient->ListComponents(); @@ -78,8 +78,9 @@ TEST_F(Codec2MasterHalTest, ListComponents) { listener.reset(new CodecListener()); ASSERT_NE(listener, nullptr); - mClient->createComponent(listTraits[i].name.c_str(), listener, - &component); + // Create component from all known services + component = mClient->CreateComponentByName( + listTraits[i].name.c_str(), listener, &mClient); ASSERT_NE(component, nullptr) << "Create component failed for " << listTraits[i].name.c_str(); } -- GitLab From 1a1953b32817ea900444357a15bca16fd42e382f Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 15 Jan 2019 16:34:05 +0530 Subject: [PATCH 1085/1530] codec2: VTS add support for Empty EOS in AudioDectest Test: VtsHidlC2V1_0TargetAudioDecTest -I software -P /sdcard/res/ -C c2.android.mp3.decoder Bug: 127691941 Change-Id: I15ef8157cd09ab4522320e0e717f3e835eb1815d --- .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index 443faabb08..89947d4f20 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -506,15 +506,17 @@ TEST_F(Codec2AudioDecHidlTest, configComp) { ASSERT_EQ(mComponent->stop(), C2_OK); } -class Codec2AudioDecDecodeTest : public Codec2AudioDecHidlTest, - public ::testing::WithParamInterface { +class Codec2AudioDecDecodeTest + : public Codec2AudioDecHidlTest, + public ::testing::WithParamInterface> { }; TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { description("Decodes input file"); if (mDisableTest) return; - uint32_t streamIndex = GetParam(); + uint32_t streamIndex = GetParam().first; + bool signalEOS = GetParam().second; mTimestampDevTest = true; char mURL[512], info[512]; std::ifstream eleStream, eleInfo; @@ -566,16 +568,27 @@ TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { ASSERT_EQ(eleStream.is_open(), true); ASSERT_NO_FATAL_FAILURE(decodeNFrames( mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, - mLinearPool, eleStream, &Info, 0, (int)Info.size())); + mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS)); + + // If EOS is not sent, sending empty input with EOS flag + size_t infoSize = Info.size(); + if (!signalEOS) { + ASSERT_NO_FATAL_FAILURE( + waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1)); + ASSERT_NO_FATAL_FAILURE( + testInputBuffer(mComponent, mQueueLock, mWorkQueue, + C2FrameData::FLAG_END_OF_STREAM, false)); + infoSize += 1; + } // blocking call to ensures application to Wait till all the inputs are // consumed ASSERT_NO_FATAL_FAILURE( waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue)); eleStream.close(); - if (mFramesReceived != Info.size()) { + if (mFramesReceived != infoSize) { ALOGE("Input buffer count and Output buffer count mismatch"); ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived, - Info.size()); + infoSize); ASSERT_TRUE(false); } ASSERT_EQ(mEos, true); @@ -607,9 +620,12 @@ TEST_P(Codec2AudioDecDecodeTest, DecodeTest) { } ASSERT_EQ(mComponent->stop(), C2_OK); } - -INSTANTIATE_TEST_CASE_P(StreamIndexes, Codec2AudioDecDecodeTest, - ::testing::Values(0, 1)); +// DecodeTest with StreamIndex and EOS / No EOS +INSTANTIATE_TEST_CASE_P(StreamIndexAndEOS, Codec2AudioDecDecodeTest, + ::testing::Values(std::make_pair(0, false), + std::make_pair(0, true), + std::make_pair(1, false), + std::make_pair(1, true))); // thumbnail test TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) { -- GitLab From 9bda452cdafeabbf79a28c24ad58786e2cd8144c Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 27 Feb 2019 13:43:50 -0800 Subject: [PATCH 1086/1530] Move mediaswcodec service to APEX bug: 127499775 test: - adb shell lshal debug android.hardware.media.c2@1.0::IComponentStore/software check all software c2 codecs are still listed - clean-built image shouldn't have mediaswcodec in /system/bin - atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small - atest media_swcodec_e2e_tests Change-Id: I96df803c29aa595dfd2b51d5a68d6db2c08726f3 --- apex/Android.bp | 23 +++++- apex/ld.config.txt | 74 +++++++++++++++++ apex/mediaswcodec.rc | 7 ++ services/mediacodec/Android.bp | 62 +++++++++++++++ services/mediacodec/Android.mk | 76 +----------------- .../mediacodec/MediaCodecUpdateService.cpp | 79 ------------------- services/mediacodec/MediaCodecUpdateService.h | 26 ------ services/mediacodec/main_swcodecservice.cpp | 18 ++--- 8 files changed, 175 insertions(+), 190 deletions(-) create mode 100644 apex/ld.config.txt create mode 100644 apex/mediaswcodec.rc create mode 100644 services/mediacodec/Android.bp delete mode 100644 services/mediacodec/MediaCodecUpdateService.cpp delete mode 100644 services/mediacodec/MediaCodecUpdateService.h diff --git a/apex/Android.bp b/apex/Android.bp index f1828569f4..0a9551d4d3 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -65,8 +65,13 @@ filegroup { apex_defaults { name: "com.android.media.swcodec-defaults", - native_shared_libs: [ - "libmedia_codecserviceregistrant", + binaries: [ + "mediaswcodec", + ], + prebuilts: [ + "com.android.media.swcodec-mediaswcodec.rc", + "com.android.media.swcodec-ld.config.txt", + "mediaswcodec.policy", ], use_vendor: true, key: "com.android.media.swcodec.key", @@ -76,6 +81,20 @@ apex_defaults { androidManifest: ":com.android.media.swcodec-androidManifest", } +prebuilt_etc { + name: "com.android.media.swcodec-mediaswcodec.rc", + src: "mediaswcodec.rc", + filename: "init.rc", + installable: false, +} + +prebuilt_etc { + name: "com.android.media.swcodec-ld.config.txt", + src: "ld.config.txt", + filename: "ld.config.txt", + installable: false, +} + apex { name: "com.android.media.swcodec", manifest: "manifest_codec.json", diff --git a/apex/ld.config.txt b/apex/ld.config.txt new file mode 100644 index 0000000000..a3e96c4d6c --- /dev/null +++ b/apex/ld.config.txt @@ -0,0 +1,74 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Bionic loader config file for the media swcodec APEX. +# +# There are no versioned APEX paths here - this APEX module does not support +# having several versions mounted. + +dir.swcodec = /apex/com.android.media.swcodec/bin/ + +[swcodec] +additional.namespaces = platform + +############################################################################### +# "default" namespace +# +# This namespace is for the binaries and libraries on the swcodec APEX. +############################################################################### + +namespace.default.isolated = true +namespace.default.visible = true + +namespace.default.search.paths = /apex/com.android.media.swcodec/${LIB} +namespace.default.asan.search.paths = /apex/com.android.media.swcodec/${LIB} + +# Keep the below in sync with "sphal" namespace in system's /etc/ld.config.txt +# Codec2 has dependencies on some SP-hals (eg. android.hardware.graphics.mapper@2.0) +# These are dlopen'ed by libvndksupport.so. +namespace.default.search.paths += /odm/${LIB} +namespace.default.search.paths += /vendor/${LIB} + +namespace.default.permitted.paths = /odm/${LIB} +namespace.default.permitted.paths += /vendor/${LIB} +namespace.default.permitted.paths += /vendor/${LIB}/hw +namespace.default.permitted.paths += /system/vendor/${LIB} + +namespace.default.asan.search.paths += /data/asan/odm/${LIB} +namespace.default.asan.search.paths += /odm/${LIB} +namespace.default.asan.search.paths += /data/asan/vendor/${LIB} +namespace.default.asan.search.paths += /vendor/${LIB} + +namespace.default.asan.permitted.paths = /data/asan/odm/${LIB} +namespace.default.asan.permitted.paths += /odm/${LIB} +namespace.default.asan.permitted.paths += /data/asan/vendor/${LIB} +namespace.default.asan.permitted.paths += /vendor/${LIB} + +namespace.default.links = platform + +# TODO: replace the following when apex has a way to auto-generate this list +# namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES% +# namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% +namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so + +############################################################################### +# "platform" namespace +# +# This namespace is for linking to LLNDK and ASAN libraries on the system. +############################################################################### + +namespace.platform.isolated = true + +namespace.platform.search.paths = /system/${LIB} +namespace.platform.asan.search.paths = /data/asan/system/${LIB} + +# /system/lib/libc.so, etc are symlinks to /bionic/lib/libc.so, etc. +# Add /bionic/lib to the permitted paths because linker uses realpath(3) +# to check the accessibility of the lib. We could add this to search.paths +# instead but that makes the resolution of bionic libs be dependent on +# the order of /system/lib and /bionic/lib in search.paths. If /bionic/lib +# is after /system/lib, then /bionic/lib is never tried because libc.so +# is always found in /system/lib but fails to pass the accessibility test +# because of its realpath. It's better to not depend on the ordering if +# possible. +namespace.platform.permitted.paths = /bionic/${LIB} +namespace.platform.asan.permitted.paths = /bionic/${LIB} diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc new file mode 100644 index 0000000000..d17481bf98 --- /dev/null +++ b/apex/mediaswcodec.rc @@ -0,0 +1,7 @@ +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/services/mediacodec/Android.bp b/services/mediacodec/Android.bp new file mode 100644 index 0000000000..25c36fa448 --- /dev/null +++ b/services/mediacodec/Android.bp @@ -0,0 +1,62 @@ +cc_binary { + name: "mediaswcodec", + vendor_available: true, + + srcs: [ + "main_swcodecservice.cpp", + ], + + shared_libs: [ + "libavservices_minijail", + "libbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libmedia_codecserviceregistrant", + ], + + target: { + vendor: { + exclude_shared_libs: ["libavservices_minijail"], + shared_libs: ["libavservices_minijail_vendor"], + }, + }, + + header_libs: [ + "libmedia_headers", + ], + + init_rc: ["mediaswcodec.rc"], + + required: ["mediaswcodec.policy"], + + cflags: [ + "-Werror", + "-Wall", + "-Wno-error=deprecated-declarations", + ], + + sanitize: { + scudo: true, + }, +} + +prebuilt_etc { + name: "mediaswcodec.policy", + sub_dir: "seccomp_policy", + arch: { + arm: { + src: "seccomp_policy/mediaswcodec-arm.policy", + }, + arm64: { + src: "seccomp_policy/mediaswcodec-arm64.policy", + }, + x86: { + src: "seccomp_policy/mediacodec-x86.policy", + }, + x86_64: { + src: "seccomp_policy/mediacodec-x86.policy", + }, + }, + required: ["crash_dump.policy"], +} diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index f78c671720..9cc19a378c 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -27,8 +27,8 @@ _software_codecs := \ include $(CLEAR_VARS) # seccomp is not required for coverage build. ifneq ($(NATIVE_COVERAGE),true) -LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediacodec.policy -LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediacodec.policy +LOCAL_REQUIRED_MODULES_arm := mediacodec.policy +LOCAL_REQUIRED_MODULES_x86 := mediacodec.policy endif LOCAL_SRC_FILES := main_codecservice.cpp LOCAL_SHARED_LIBRARIES := \ @@ -65,74 +65,13 @@ include $(BUILD_EXECUTABLE) #################################################################### -# service executable -include $(CLEAR_VARS) -# seccomp is not required for coverage build. -ifneq ($(NATIVE_COVERAGE),true) -LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaswcodec.policy -LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaswcodec.policy -LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaswcodec.policy -LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaswcodec.policy -endif - -LOCAL_SRC_FILES := \ - main_swcodecservice.cpp \ - MediaCodecUpdateService.cpp \ - -sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\ - $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ - $(UBSAN_RUNTIME_LIBRARY) \ - $(TSAN_RUNTIME_LIBRARY) \ - $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \ - $(2ND_UBSAN_RUNTIME_LIBRARY) \ - $(2ND_TSAN_RUNTIME_LIBRARY))) - -# $(info Sanitizer: $(sanitizer_runtime_libraries)) - -llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\ - $(LLNDK_LIBRARIES))) - -# $(info LLNDK: $(llndk_libraries)) - -LOCAL_CFLAGS := -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(llndk_libraries)"' - -LOCAL_SHARED_LIBRARIES := \ - libavservices_minijail \ - libbase \ - libbinder \ - libcutils \ - libhidltransport \ - libhwbinder \ - liblog \ - libmedia \ - libutils \ - libziparchive \ - -LOCAL_HEADER_LIBRARIES := \ - libnativeloader-dummy-headers \ - -LOCAL_MODULE := mediaswcodec -LOCAL_INIT_RC := mediaswcodec.rc -LOCAL_SANITIZE := scudo -ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86_64 arm64)) - LOCAL_MULTILIB := both - LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 - LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE) -endif - -sanitizer_runtime_libraries := -llndk_libraries := - -include $(BUILD_EXECUTABLE) - -#################################################################### - # service seccomp policy ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) include $(CLEAR_VARS) LOCAL_MODULE := mediacodec.policy LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy +LOCAL_REQUIRED_MODULES := crash_dump.policy # mediacodec runs in 32-bit combatibility mode. For 64 bit architectures, # use the 32 bit policy ifdef TARGET_2ND_ARCH @@ -149,14 +88,5 @@ endif #################################################################### -# sw service seccomp policy -ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64)) -include $(CLEAR_VARS) -LOCAL_MODULE := mediaswcodec.policy -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy -LOCAL_SRC_FILES := seccomp_policy/mediaswcodec-$(TARGET_ARCH).policy -include $(BUILD_PREBUILT) -endif include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/mediacodec/MediaCodecUpdateService.cpp b/services/mediacodec/MediaCodecUpdateService.cpp deleted file mode 100644 index 50ccbcef39..0000000000 --- a/services/mediacodec/MediaCodecUpdateService.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MediaCodecUpdateService" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include - -#include "MediaCodecUpdateService.h" - -namespace android { - -void loadFromApex(const char *libDirPath) { - ALOGV("loadFromApex: path=%s", libDirPath); - - String8 libPath = String8(libDirPath) + "/libmedia_codecserviceregistrant.so"; - - android_namespace_t *codecNs = android_create_namespace("codecs", - nullptr, // ld_library_path - libDirPath, - ANDROID_NAMESPACE_TYPE_ISOLATED, - nullptr, // permitted_when_isolated_path - nullptr); // parent - - if (codecNs == nullptr) { - ALOGE("Failed to create codec namespace"); - return; - } - - String8 linked_libraries(LINKED_LIBRARIES); - if (!android_link_namespaces(codecNs, nullptr, linked_libraries.c_str())) { - ALOGE("Failed to link namespace"); - return; - } - - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = codecNs, - }; - - void *registrantLib = android_dlopen_ext( - libPath.string(), - RTLD_NOW | RTLD_LOCAL, &dlextinfo); - - if (registrantLib == nullptr) { - ALOGE("Failed to load lib from archive: %s", dlerror()); - } - - RegisterCodecServicesFunc registerCodecServices = - reinterpret_cast( - dlsym(registrantLib, "RegisterCodecServices")); - - if (registerCodecServices == nullptr) { - ALOGE("Cannot register codec services -- corrupted library."); - return; - } - - registerCodecServices(); -} - -} // namespace android diff --git a/services/mediacodec/MediaCodecUpdateService.h b/services/mediacodec/MediaCodecUpdateService.h deleted file mode 100644 index 09d6dbe730..0000000000 --- a/services/mediacodec/MediaCodecUpdateService.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H -#define ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H - -namespace android { - -void loadFromApex(const char *libDirPath); - -} // namespace android - -#endif // ANDROID_MEDIA_CODEC_UPDATE_SERVICE_H diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index 05b5695414..c44be28021 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -19,24 +19,26 @@ // from LOCAL_C_INCLUDES #include "minijail.h" - #include -#include "MediaCodecUpdateService.h" - using namespace android; +// kSystemSeccompPolicyPath points to the policy for the swcodecs themselves and +// is part of the updates. kVendorSeccompPolicyPath points to any additional +// policies that the vendor may need for the device. static const char kSystemSeccompPolicyPath[] = - "/system/etc/seccomp_policy/mediaswcodec.policy"; + "/apex/com.android.media.swcodec/etc/seccomp_policy/mediaswcodec.policy"; static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediaswcodec.policy"; // Disable Scudo's mismatch allocation check, as it is being triggered // by some third party code. extern "C" const char *__scudo_default_options() { - return "DeallocationTypeMismatch=false"; + return "DeallocationTypeMismatch=false"; } +extern "C" void RegisterCodecServices(); + int main(int argc __unused, char** /*argv*/) { LOG(INFO) << "media swcodec service starting"; @@ -45,11 +47,7 @@ int main(int argc __unused, char** /*argv*/) ::android::hardware::configureRpcThreadpool(64, false); -#ifdef __LP64__ - loadFromApex("/apex/com.android.media.swcodec/lib64"); -#else - loadFromApex("/apex/com.android.media.swcodec/lib"); -#endif + RegisterCodecServices(); ::android::hardware::joinRpcThreadpool(); } -- GitLab From 16a373e7bc075c98ded6a806ae29200d073edb57 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 7 Mar 2019 15:32:47 -0800 Subject: [PATCH 1087/1530] Use double for duration and time calculations to avoid overflows. Bug: 126125702 Test: manual Change-Id: I3045e8962056ca24d1c27428c0e909d240b76bdb --- media/extractors/mp4/MPEG4Extractor.cpp | 35 ++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 9f275283fe..93f34a862c 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1487,8 +1487,13 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } if (duration != 0 && mLastTrack->timescale != 0) { - AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, - (duration * 1000000) / mLastTrack->timescale); + long double durationUs = ((long double)duration * 1000000) / mLastTrack->timescale; + if (durationUs < 0 || durationUs > INT64_MAX) { + ALOGE("cannot represent %lld * 1000000 / %lld in 64 bits", + (long long) duration, (long long) mLastTrack->timescale); + return ERROR_MALFORMED; + } + AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, durationUs); } uint8_t lang[2]; @@ -5402,7 +5407,7 @@ media_status_t MPEG4Source::read( break; } if( mode != ReadOptions::SEEK_FRAME_INDEX) { - seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale; + seekTimeUs += ((long double)mElstShiftStartTicks * 1000000) / mTimescale; } uint32_t sampleIndex; @@ -5550,7 +5555,7 @@ media_status_t MPEG4Source::read( AMediaFormat *meta = mBuffer->meta_data(); AMediaFormat_clear(meta); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1); int32_t byteOrder; @@ -5585,9 +5590,9 @@ media_status_t MPEG4Source::read( AMediaFormat *meta = mBuffer->meta_data(); AMediaFormat_clear(meta); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { AMediaFormat_setInt64( @@ -5641,9 +5646,9 @@ media_status_t MPEG4Source::read( AMediaFormat *meta = mBuffer->meta_data(); AMediaFormat_clear(meta); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { AMediaFormat_setInt64( @@ -5722,9 +5727,9 @@ media_status_t MPEG4Source::read( AMediaFormat *meta = mBuffer->meta_data(); AMediaFormat_clear(meta); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt64( - meta, AMEDIAFORMAT_KEY_DURATION, ((int64_t)stts * 1000000) / mTimescale); + meta, AMEDIAFORMAT_KEY_DURATION, ((long double)stts * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { AMediaFormat_setInt64( @@ -5771,7 +5776,7 @@ media_status_t MPEG4Source::fragmentedRead( ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { - seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale; + seekTimeUs += ((long double)mElstShiftStartTicks * 1000000) / mTimescale; ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRId32, seekTimeUs, mElstShiftStartTicks); @@ -5932,9 +5937,9 @@ media_status_t MPEG4Source::fragmentedRead( CHECK(mBuffer != NULL); mBuffer->set_range(0, size); AMediaFormat_setInt64(bufmeta, - AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt64(bufmeta, - AMEDIAFORMAT_KEY_DURATION, ((int64_t)smpl->duration * 1000000) / mTimescale); + AMEDIAFORMAT_KEY_DURATION, ((long double)smpl->duration * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { AMediaFormat_setInt64(bufmeta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); @@ -6047,9 +6052,9 @@ media_status_t MPEG4Source::fragmentedRead( AMediaFormat *bufmeta = mBuffer->meta_data(); AMediaFormat_setInt64(bufmeta, - AMEDIAFORMAT_KEY_TIME_US, ((int64_t)cts * 1000000) / mTimescale); + AMEDIAFORMAT_KEY_TIME_US, ((long double)cts * 1000000) / mTimescale); AMediaFormat_setInt64(bufmeta, - AMEDIAFORMAT_KEY_DURATION, ((int64_t)smpl->duration * 1000000) / mTimescale); + AMEDIAFORMAT_KEY_DURATION, ((long double)smpl->duration * 1000000) / mTimescale); if (targetSampleTimeUs >= 0) { AMediaFormat_setInt64(bufmeta, AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); -- GitLab From cb03f764eea0e48c192309cd78e0e908a99add44 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 8 Mar 2019 10:01:50 -0800 Subject: [PATCH 1088/1530] services: add some more owners from the video team Change-Id: Ia2eb09da06f5a266630e089af272d2595bce8a68 --- services/OWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/OWNERS b/services/OWNERS index d5d00daa21..66a4bcb156 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -1,4 +1,7 @@ +chz@google.com elaurent@google.com +essick@google.com etalvala@google.com gkasten@google.com hunga@google.com +marcone@google.com -- GitLab From abfab20fdbdc41168f816272c31caa6c1264ba46 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 7 Mar 2019 19:45:54 -0800 Subject: [PATCH 1089/1530] PatchPanel: Keep peer alive during use. Test: BT call, switch from hearing aid to headset Bug: 126789266 Change-Id: I589e5ecaac25c1ce1d504a387662b96cf50f63ba --- services/audioflinger/AudioFlinger.cpp | 6 ++++-- services/audioflinger/PatchPanel.cpp | 5 +++-- services/audioflinger/PatchPanel.h | 6 +++--- services/audioflinger/Threads.cpp | 2 ++ services/audioflinger/TrackBase.h | 11 ++++++++++- services/audioflinger/Tracks.cpp | 2 ++ 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 40332479ec..213c9c3115 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -836,8 +836,10 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, } teePatches.push_back({patchRecord, patchTrack}); secondaryThread->addPatchTrack(patchTrack); - patchTrack->setPeerProxy(patchRecord.get()); - patchRecord->setPeerProxy(patchTrack.get()); + // In case the downstream patchTrack on the secondaryThread temporarily outlives + // our created track, ensure the corresponding patchRecord is still alive. + patchTrack->setPeerProxy(patchRecord, true /* holdReference */); + patchRecord->setPeerProxy(patchTrack, false /* holdReference */); } track->setTeePatches(std::move(teePatches)); } diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 676a575c06..13243b885b 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -527,8 +527,8 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) } // tie playback and record tracks together - mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack.get()); - mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack.get()); + mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack); + mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack); // start capture and playback mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE); @@ -543,6 +543,7 @@ void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) __func__, mRecord.handle(), mPlayback.handle()); mRecord.stopTrack(); mPlayback.stopTrack(); + mRecord.track()->clearPeerProxy(); // mRecord stop is synchronous. Break PeerProxy sp<> cycle. mRecord.closeConnections(panel); mPlayback.closeConnections(panel); } diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index 612855f57c..aba2cc27db 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -122,11 +122,11 @@ private: mThread = thread; mCloseThread = closeThread; } - void setTrackAndPeer(const sp& track, - ThreadBase::PatchProxyBufferProvider *peer) { + template + void setTrackAndPeer(const sp& track, const sp &peer) { mTrack = track; mThread->addPatchTrack(mTrack); - mTrack->setPeerProxy(peer); + mTrack->setPeerProxy(peer, true /* holdReference */); } void stopTrack() { if (mTrack) mTrack->stop(); } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index f4a31edf52..49f74a2660 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7683,6 +7683,8 @@ bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { // note that threadLoop may still be processing the track at this point [without lock] recordTrack->mState = TrackBase::PAUSING; + // NOTE: Waiting here is important to keep stop synchronous. + // This is needed for proper patchRecord peer release. while (recordTrack->mState == TrackBase::PAUSING && !recordTrack->isInvalid()) { mWaitWorkCV.broadcast(); // signal thread to stop mStartStopCond.wait(mLock); diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 0ba0ab4f30..e23173fd0a 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -337,10 +337,19 @@ public: PatchTrackBase(sp proxy, const ThreadBase& thread, const Timeout& timeout); void setPeerTimeout(std::chrono::nanoseconds timeout); - void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + template + void setPeerProxy(const sp &proxy, bool holdReference) { + mPeerReferenceHold = holdReference ? proxy : nullptr; + mPeerProxy = proxy.get(); + } + void clearPeerProxy() { + mPeerReferenceHold.clear(); + mPeerProxy = nullptr; + } protected: const sp mProxy; + sp mPeerReferenceHold; // keeps mPeerProxy alive during access. PatchProxyBufferProvider* mPeerProxy = nullptr; struct timespec mPeerTimeout{}; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index ad78a452b4..5a436962d6 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1723,6 +1723,7 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() { + ALOGV("%s(%d)", __func__, mId); } status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event, @@ -2193,6 +2194,7 @@ AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, AudioFlinger::RecordThread::PatchRecord::~PatchRecord() { + ALOGV("%s(%d)", __func__, mId); } // AudioBufferProvider interface -- GitLab From 38c13a703b4d169d33b2219a546ee278c5bf7c39 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Tue, 26 Feb 2019 14:02:15 -0800 Subject: [PATCH 1090/1530] Camera: fix filter logic for depth only camera Test: partner testing Bug: 126408288 Change-Id: I5c3abcd72e64b40e7cbac467646d99a1f5b362d0 --- camera/ndk/impl/ACameraMetadata.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index de40990b93..c126cf8d40 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -203,7 +203,7 @@ ACameraMetadata::filterStreamConfigurations() { const int STREAM_HEIGHT_OFFSET = 2; const int STREAM_IS_INPUT_OFFSET = 3; camera_metadata_entry entry = mData.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); - if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT32) { + if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) { ALOGE("%s: malformed available stream configuration key! count %zu, type %d", __FUNCTION__, entry.count, entry.type); return; @@ -231,9 +231,17 @@ ACameraMetadata::filterStreamConfigurations() { filteredStreamConfigs.push_back(isInput); } - mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs); + if (filteredStreamConfigs.size() > 0) { + mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs); + } entry = mData.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS); + if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) { + ALOGE("%s: malformed available depth stream configuration key! count %zu, type %d", + __FUNCTION__, entry.count, entry.type); + return; + } + Vector filteredDepthStreamConfigs; filteredDepthStreamConfigs.setCapacity(entry.count); @@ -258,7 +266,11 @@ ACameraMetadata::filterStreamConfigurations() { filteredDepthStreamConfigs.push_back(height); filteredDepthStreamConfigs.push_back(isInput); } - mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, filteredDepthStreamConfigs); + + if (filteredDepthStreamConfigs.size() > 0) { + mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, + filteredDepthStreamConfigs); + } entry = mData.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS); Vector filteredHeicStreamConfigs; -- GitLab From 6252c87518983a343ef76aabb875497dfc97299d Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 8 Mar 2019 11:24:47 -0800 Subject: [PATCH 1091/1530] Verbosity and increase buffer limit New MediaSource inline/shared buffer threshold meant we hit max buffers allowed limit more often -- with verbose diagnostics. max buffers now aligned with primary consumer NuMediaExtractor (4->8) Bug: 115848790 Test: poc --- media/libmedia/IMediaSource.cpp | 4 ++-- media/libmedia/include/media/IMediaSource.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp index 4dece966ab..50826c5599 100644 --- a/media/libmedia/IMediaSource.cpp +++ b/media/libmedia/IMediaSource.cpp @@ -368,13 +368,13 @@ status_t BnMediaSource::onTransact( ALOGV("Use shared memory: %zu", length); transferBuf = buf; } else { - ALOGD("Large buffer %zu without IMemory!", length); + ALOGV("Large buffer %zu without IMemory!", length); ret = mGroup->acquire_buffer( (MediaBufferBase **)&transferBuf, false /* nonBlocking */, length); if (ret != OK || transferBuf == nullptr || transferBuf->mMemory == nullptr) { - ALOGW("Failed to acquire shared memory, size %zu, ret %d", + ALOGV("Failed to acquire shared memory, size %zu, ret %d", length, ret); if (transferBuf != nullptr) { transferBuf->release(); diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h index 5ab6e37464..381df24e0a 100644 --- a/media/libmedia/include/media/IMediaSource.h +++ b/media/libmedia/include/media/IMediaSource.h @@ -124,7 +124,8 @@ public: return false; } - static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource + // align buffer count with video request size in NuMediaExtractor::selectTrack() + static const size_t kBinderMediaBuffers = 8; // buffers managed by BnMediaSource static const size_t kTransferSharedAsSharedThreshold = 4 * 1024; // if >= shared, else inline static const size_t kTransferInlineAsSharedThreshold = 8 * 1024; // if >= shared, else inline static const size_t kInlineMaxTransfer = 64 * 1024; // Binder size limited to BINDER_VM_SIZE. -- GitLab From bf1b8b968a11630d2269380f9e3426e611907ebc Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 6 Mar 2019 14:56:08 -0800 Subject: [PATCH 1092/1530] Camera: fix long dequeueBuffer call Fix the long (>1s) dequeueBuffer call when a stream is managed by Camera3BufferManager and its consumer end discards free buffers. Test: CTS, no more long dequeBuffer call in GCA mode switch Bug: 126054873 Change-Id: I03d6526b076796bb44f15cc2c4a092ff3d04fc1d --- .../device3/Camera3BufferManager.cpp | 11 ++- .../device3/Camera3BufferManager.h | 8 +- .../device3/Camera3OutputStream.cpp | 87 ++++++++++++++----- .../device3/Camera3OutputStream.h | 7 ++ 4 files changed, 88 insertions(+), 25 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp index 8c8b97ac8b..d6bf83eba8 100644 --- a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp +++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp @@ -237,7 +237,7 @@ status_t Camera3BufferManager::checkAndFreeBufferOnOtherStreamsLocked( } status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId, - sp* gb, int* fenceFd) { + sp* gb, int* fenceFd, bool noFreeBufferAtConsumer) { ATRACE_CALL(); Mutex::Autolock l(mLock); @@ -253,14 +253,19 @@ status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId, StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId); BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap; size_t& bufferCount = handOutBufferCounts.editValueFor(streamId); + BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap; + size_t& attachedBufferCount = attachedBufferCounts.editValueFor(streamId); + + if (noFreeBufferAtConsumer) { + attachedBufferCount = bufferCount; + } + if (bufferCount >= streamSet.maxAllowedBufferCount) { ALOGE("%s: bufferCount (%zu) exceeds the max allowed buffer count (%zu) of this stream set", __FUNCTION__, bufferCount, streamSet.maxAllowedBufferCount); return INVALID_OPERATION; } - BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap; - size_t& attachedBufferCount = attachedBufferCounts.editValueFor(streamId); if (attachedBufferCount > bufferCount) { // We've already attached more buffers to this stream than we currently have // outstanding, so have the stream just use an already-attached buffer diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h index 025062e17d..f0de1c1d09 100644 --- a/services/camera/libcameraservice/device3/Camera3BufferManager.h +++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h @@ -112,6 +112,10 @@ public: * * After this call, the client takes over the ownership of this buffer if it is not freed. * + * Sometimes free buffers are discarded from consumer side and the dequeueBuffer call returns + * TIMED_OUT, in this case calling getBufferForStream again with noFreeBufferAtConsumer set to + * true will notify buffer manager to update its states and also tries to allocate a new buffer. + * * Return values: * * OK: Getting buffer for this stream was successful. @@ -122,7 +126,9 @@ public: * to this buffer manager before. * NO_MEMORY: Unable to allocate a buffer for this stream at this time. */ - status_t getBufferForStream(int streamId, int streamSetId, sp* gb, int* fenceFd); + status_t getBufferForStream( + int streamId, int streamSetId, sp* gb, int* fenceFd, + bool noFreeBufferAtConsumer = false); /** * This method notifies the manager that a buffer has been released by the consumer. diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 8cd575d445..baba856d9e 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -359,7 +359,18 @@ status_t Camera3OutputStream::configureQueueLocked() { // Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture. // We need skip these cases as timeout will disable the non-blocking (async) mode. if (!(isConsumedByHWComposer() || isConsumedByHWTexture())) { - mConsumer->setDequeueTimeout(kDequeueBufferTimeout); + if (mUseBufferManager) { + // When buffer manager is handling the buffer, we should have available buffers in + // buffer queue before we calls into dequeueBuffer because buffer manager is tracking + // free buffers. + // There are however some consumer side feature (ImageReader::discardFreeBuffers) that + // can discard free buffers without notifying buffer manager. We want the timeout to + // happen immediately here so buffer manager can try to update its internal state and + // try to allocate a buffer instead of waiting. + mConsumer->setDequeueTimeout(0); + } else { + mConsumer->setDequeueTimeout(kDequeueBufferTimeout); + } } return OK; @@ -526,6 +537,8 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i if (res != OK) { ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)", __FUNCTION__, mId, strerror(-res), res); + + checkRetAndSetAbandonedLocked(res); return res; } gotBufferFromManager = true; @@ -562,33 +575,68 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i mDequeueBufferLatency.add(dequeueStart, dequeueEnd); mLock.lock(); - if (res != OK) { - ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING, - // let prepareNextBuffer handle the error.) - if ((res == NO_INIT || res == DEAD_OBJECT) && mState == STATE_CONFIGURED) { - mState = STATE_ABANDONED; + if (mUseBufferManager && res == TIMED_OUT) { + checkRemovedBuffersLocked(); + + sp gb; + res = mBufferManager->getBufferForStream( + getId(), getStreamSetId(), &gb, fenceFd, /*noFreeBuffer*/true); + + if (res == OK) { + // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after + // a successful return. + *anb = gb.get(); + res = mConsumer->attachBuffer(*anb); + gotBufferFromManager = true; + ALOGV("Stream %d: Attached new buffer", getId()); + + if (res != OK) { + ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + + checkRetAndSetAbandonedLocked(res); + return res; + } + } else { + ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + return res; } + } else if (res != OK) { + ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + checkRetAndSetAbandonedLocked(res); return res; } } if (res == OK) { - std::vector> removedBuffers; - res = mConsumer->getAndFlushRemovedBuffers(&removedBuffers); - if (res == OK) { - onBuffersRemovedLocked(removedBuffers); + checkRemovedBuffersLocked(); + } - if (mUseBufferManager && removedBuffers.size() > 0) { - mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), removedBuffers.size()); - } + return res; +} + +void Camera3OutputStream::checkRemovedBuffersLocked(bool notifyBufferManager) { + std::vector> removedBuffers; + status_t res = mConsumer->getAndFlushRemovedBuffers(&removedBuffers); + if (res == OK) { + onBuffersRemovedLocked(removedBuffers); + + if (notifyBufferManager && mUseBufferManager && removedBuffers.size() > 0) { + mBufferManager->onBuffersRemoved(getId(), getStreamSetId(), removedBuffers.size()); } } +} - return res; +void Camera3OutputStream::checkRetAndSetAbandonedLocked(status_t res) { + // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is + // STATE_PREPARING, let prepareNextBuffer handle the error.) + if ((res == NO_INIT || res == DEAD_OBJECT) && mState == STATE_CONFIGURED) { + mState = STATE_ABANDONED; + } } status_t Camera3OutputStream::disconnectLocked() { @@ -803,11 +851,8 @@ status_t Camera3OutputStream::detachBufferLocked(sp* buffer, int* } } - std::vector> removedBuffers; - res = mConsumer->getAndFlushRemovedBuffers(&removedBuffers); - if (res == OK) { - onBuffersRemovedLocked(removedBuffers); - } + // Here we assume detachBuffer is called by buffer manager so it doesn't need to be notified + checkRemovedBuffersLocked(/*notifyBufferManager*/false); return res; } diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index 2128da29ba..30fc2f77d4 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -309,6 +309,13 @@ class Camera3OutputStream : */ void onBuffersRemovedLocked(const std::vector>&); status_t detachBufferLocked(sp* buffer, int* fenceFd); + // Call this after each dequeueBuffer/attachBuffer/detachNextBuffer call to get update on + // removed buffers. Set notifyBufferManager to false when the call is initiated by buffer + // manager so buffer manager doesn't need to be notified. + void checkRemovedBuffersLocked(bool notifyBufferManager = true); + + // Check return status of IGBP calls and set abandoned state accordingly + void checkRetAndSetAbandonedLocked(status_t res); static const int32_t kDequeueLatencyBinSize = 5; // in ms CameraLatencyHistogram mDequeueBufferLatency; -- GitLab From 6244527af5c55a9887e54bd8cc7e86c8cb6240fd Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Sat, 26 Jan 2019 18:50:29 +0800 Subject: [PATCH 1093/1530] mkv supports more video codec types 1. support MPEG2 video codec 2. support codecs under V_MS/VFW/FOURCC which include H263, AVC, MPEG2, MPEG4 3. handle some codec type with no codec private data 4. add codec type "MJPEG, XVID, DIVX, DIVX3" and support them in matroskaextractor Bug: 123594230 Test: test with file which has added codec type, and check if the codec related track can be played normally Change-Id: I4e0173b80576b1f80bc7a210a8ef029ee3a2af51 --- media/extractors/mkv/MatroskaExtractor.cpp | 211 +++++++++++++++++- media/extractors/mkv/MatroskaExtractor.h | 2 + media/libstagefright/foundation/MediaDefs.cpp | 4 + .../media/stagefright/foundation/MediaDefs.h | 4 + 4 files changed, 217 insertions(+), 4 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index a399940f99..f8aa784829 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -958,6 +958,59 @@ media_status_t MatroskaSource::read( //////////////////////////////////////////////////////////////////////////////// +// trans all FOURCC to lower char +static uint32_t FourCCtoLower(uint32_t fourcc) { + uint8_t ch_1 = tolower((fourcc >> 24) & 0xff); + uint8_t ch_2 = tolower((fourcc >> 16) & 0xff); + uint8_t ch_3 = tolower((fourcc >> 8) & 0xff); + uint8_t ch_4 = tolower((fourcc) & 0xff); + uint32_t fourcc_out = ch_1 << 24 | ch_2 << 16 | ch_3 << 8 | ch_4; + + return fourcc_out; +} + +static const char *MKVFourCC2MIME(uint32_t fourcc) { + ALOGV("MKVFourCC2MIME fourcc 0x%8.8x", fourcc); + uint32_t lowerFourcc = FourCCtoLower(fourcc); + switch (lowerFourcc) { + case FOURCC("mp4v"): + return MEDIA_MIMETYPE_VIDEO_MPEG4; + + case FOURCC("s263"): + case FOURCC("h263"): + return MEDIA_MIMETYPE_VIDEO_H263; + + case FOURCC("avc1"): + case FOURCC("h264"): + return MEDIA_MIMETYPE_VIDEO_AVC; + + case FOURCC("mpg2"): + return MEDIA_MIMETYPE_VIDEO_MPEG2; + + case FOURCC("xvid"): + return MEDIA_MIMETYPE_VIDEO_XVID; + + case FOURCC("divx"): + case FOURCC("dx50"): + return MEDIA_MIMETYPE_VIDEO_DIVX; + + case FOURCC("div3"): + case FOURCC("div4"): + return MEDIA_MIMETYPE_VIDEO_DIVX3; + + case FOURCC("mjpg"): + case FOURCC("mppg"): + return MEDIA_MIMETYPE_VIDEO_MJPEG; + + default: + char fourccString[5]; + MakeFourCCString(fourcc, fourccString); + ALOGW("mkv unsupport fourcc %s", fourccString); + return ""; + } +} + + MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) : mDataSource(source), mReader(new DataSourceBaseReader(mDataSource)), @@ -1308,6 +1361,89 @@ status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) { return OK; } +status_t MatroskaExtractor::synthesizeMPEG2(TrackInfo *trackInfo, size_t index) { + ALOGV("synthesizeMPEG2"); + BlockIterator iter(this, trackInfo->mTrackNum, index); + if (iter.eos()) { + return ERROR_MALFORMED; + } + + const mkvparser::Block *block = iter.block(); + if (block->GetFrameCount() <= 0) { + return ERROR_MALFORMED; + } + + const mkvparser::Block::Frame &frame = block->GetFrame(0); + auto tmpData = heapbuffer(frame.len); + long n = frame.Read(mReader, tmpData.get()); + if (n != 0) { + return ERROR_MALFORMED; + } + + size_t header_start = 0; + size_t header_lenth = 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++) { + if (ntohl(0x000001b8) == *(uint32_t*)((uint8_t*)tmpData.get() + + header_start + header_lenth)) { + isComplete_csd = true; + break; + } + } + if (!isComplete_csd) { + ALOGE("can't parse complete csd for MPEG2!"); + return ERROR_MALFORMED; + } + addESDSFromCodecPrivate(trackInfo->mMeta, false, + (uint8_t*)(tmpData.get()) + header_start, header_lenth); + + return OK; + +} + +status_t MatroskaExtractor::synthesizeMPEG4(TrackInfo *trackInfo, size_t index) { + ALOGV("synthesizeMPEG4"); + BlockIterator iter(this, trackInfo->mTrackNum, index); + if (iter.eos()) { + return ERROR_MALFORMED; + } + + const mkvparser::Block *block = iter.block(); + if (block->GetFrameCount() <= 0) { + return ERROR_MALFORMED; + } + + const mkvparser::Block::Frame &frame = block->GetFrame(0); + auto tmpData = heapbuffer(frame.len); + long n = frame.Read(mReader, tmpData.get()); + if (n != 0) { + return ERROR_MALFORMED; + } + + size_t vosend; + bool isComplete_csd = false; + for (vosend = 0; (long)vosend < frame.len - 4; vosend++) { + if (ntohl(0x000001b6) == *(uint32_t*)((uint8_t*)tmpData.get() + vosend)) { + isComplete_csd = true; + break; // Send VOS until VOP + } + } + if (!isComplete_csd) { + ALOGE("can't parse complete csd for MPEG4!"); + return ERROR_MALFORMED; + } + addESDSFromCodecPrivate(trackInfo->mMeta, false, tmpData.get(), vosend); + + return OK; + +} + + static inline bool isValidInt32ColourValue(long long value) { return value != mkvparser::Colour::kValueNotPresent && value >= INT32_MIN @@ -1490,6 +1626,8 @@ void MatroskaExtractor::addTracks() { status_t err = OK; int32_t nalSize = -1; + bool isSetCsdFrom1stFrame = false; + switch (track->GetType()) { case VIDEO_TRACK: { @@ -1516,15 +1654,15 @@ void MatroskaExtractor::addTracks() { continue; } } else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) { + AMediaFormat_setString(meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG4); if (codecPrivateSize > 0) { - AMediaFormat_setString(meta, - AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG4); addESDSFromCodecPrivate( meta, false, codecPrivate, codecPrivateSize); } else { ALOGW("%s is detected, but does not have configuration.", codecID); - continue; + isSetCsdFrom1stFrame = true; } } else if (!strcmp("V_VP8", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_VP8); @@ -1538,6 +1676,49 @@ void MatroskaExtractor::addTracks() { } } else if (!strcmp("V_AV1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); + } else if (!strcmp("V_MPEG2", codecID) || !strcmp("V_MPEG1", codecID)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, + MEDIA_MIMETYPE_VIDEO_MPEG2); + if (codecPrivate != NULL) { + addESDSFromCodecPrivate(meta, false, codecPrivate, codecPrivateSize); + } else { + ALOGW("No specific codec private data, find it from the first frame"); + isSetCsdFrom1stFrame = true; + } + } else if (!strcmp("V_MJPEG", codecID)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, + MEDIA_MIMETYPE_VIDEO_MJPEG); + } else if (!strcmp("V_MS/VFW/FOURCC", codecID)) { + if (NULL == codecPrivate ||codecPrivateSize < 20) { + ALOGE("V_MS/VFW/FOURCC has no valid private data(%p),codecPrivateSize:%zu", + codecPrivate, codecPrivateSize); + continue; + } else { + uint32_t fourcc = *(uint32_t *)(codecPrivate + 16); + fourcc = ntohl(fourcc); + const char* mime = MKVFourCC2MIME(fourcc); + ALOGV("V_MS/VFW/FOURCC type is %s", mime); + if (!strncasecmp("video/", mime, 6)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, mime); + } else { + ALOGE("V_MS/VFW/FOURCC continue,unsupport video type=%s,fourcc=0x%08x.", + mime, fourcc); + continue; + } + if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_XVID) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DIVX) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DIVX3) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { + isSetCsdFrom1stFrame = true; + } else { + ALOGW("FourCC have unsupport codec, type=%s,fourcc=0x%08x.", + mime, fourcc); + continue; + } + } } else { ALOGW("%s is not supported.", codecID); continue; @@ -1681,13 +1862,35 @@ void MatroskaExtractor::addTracks() { initTrackInfo(track, meta, trackInfo); trackInfo->mNalLengthSize = nalSize; - if (!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) { + const char *mimetype = ""; + AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype); + + if ((!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_AVC) && isSetCsdFrom1stFrame)) { // Attempt to recover from AVC track without codec private data err = synthesizeAVCC(trackInfo, n); if (err != OK) { mTracks.pop(); } + } else if ((!strcmp("V_MPEG2", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG2) && isSetCsdFrom1stFrame)) { + // Attempt to recover from MPEG2 track without codec private data + err = synthesizeMPEG2(trackInfo, n); + if (err != OK) { + mTracks.pop(); + } + } else if ((!strcmp("V_MPEG4/ISO/ASP", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG4) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_XVID) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_DIVX) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_DIVX3) && isSetCsdFrom1stFrame)) { + // Attempt to recover from MPEG4 track without codec private data + err = synthesizeMPEG4(trackInfo, n); + if (err != OK) { + mTracks.pop(); + } } + } } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 3871bdfb9f..d53d9e332b 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -95,6 +95,8 @@ private: int64_t mSeekPreRollNs; status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index); + status_t synthesizeMPEG2(TrackInfo *trackInfo, size_t index); + status_t synthesizeMPEG4(TrackInfo *trackInfo, size_t index); status_t initTrackInfo( const mkvparser::Track *track, AMediaFormat *meta, diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index 9d1ec1fdbb..52b2765fb2 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -32,6 +32,10 @@ const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw"; const char *MEDIA_MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision"; const char *MEDIA_MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX = "video/divx"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX3 = "video/divx3"; +const char *MEDIA_MIMETYPE_VIDEO_XVID = "video/xvid"; +const char *MEDIA_MIMETYPE_VIDEO_MJPEG = "video/x-motion-jpeg"; const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index e68852dce6..007a09b581 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -34,6 +34,10 @@ extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2; extern const char *MEDIA_MIMETYPE_VIDEO_RAW; extern const char *MEDIA_MIMETYPE_VIDEO_DOLBY_VISION; extern const char *MEDIA_MIMETYPE_VIDEO_SCRAMBLED; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX3; +extern const char *MEDIA_MIMETYPE_VIDEO_XVID; +extern const char *MEDIA_MIMETYPE_VIDEO_MJPEG; extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB; extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB; -- GitLab From c6f2ab3c7b927e87d55c8bde9ac02571c3a7ef66 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Mon, 4 Mar 2019 11:18:59 -0800 Subject: [PATCH 1094/1530] Camera: Add NDK support for camera permission update callbacks Camera NDK clients must receive notifications about access permission changes. Bug: 121379978 Test: Camera CTS Change-Id: I66866ee3bbf7d45619995f036f19af50e812c236 --- camera/ndk/NdkCameraManager.cpp | 54 +++++++++++ camera/ndk/impl/ACameraManager.cpp | 55 +++++++++++ camera/ndk/impl/ACameraManager.h | 26 ++++- camera/ndk/include/camera/NdkCameraManager.h | 99 ++++++++++++++++++++ camera/ndk/libcamera2ndk.map.txt | 2 + camera/ndk/ndk_vendor/impl/ACameraManager.h | 5 + 6 files changed, 236 insertions(+), 5 deletions(-) diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp index 8742d9c97e..23d01efcac 100644 --- a/camera/ndk/NdkCameraManager.cpp +++ b/camera/ndk/NdkCameraManager.cpp @@ -104,6 +104,60 @@ camera_status_t ACameraManager_unregisterAvailabilityCallback( return ACAMERA_OK; } +EXPORT +camera_status_t ACameraManager_registerExtendedAvailabilityCallback( + ACameraManager* /*manager*/, const ACameraManager_ExtendedAvailabilityCallbacks *callback) { + ATRACE_CALL(); + if (callback == nullptr) { + ALOGE("%s: invalid argument! callback is null!", __FUNCTION__); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + if ((callback->availabilityCallbacks.onCameraAvailable == nullptr) || + (callback->availabilityCallbacks.onCameraUnavailable == nullptr) || + (callback->onCameraAccessPrioritiesChanged == nullptr)) { + ALOGE("%s: invalid argument! callback %p, " + "onCameraAvailable %p, onCameraUnavailable %p onCameraAccessPrioritiesChanged %p", + __FUNCTION__, callback, + callback->availabilityCallbacks.onCameraAvailable, + callback->availabilityCallbacks.onCameraUnavailable, + callback->onCameraAccessPrioritiesChanged); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + auto reservedEntriesCount = sizeof(callback->reserved) / sizeof(callback->reserved[0]); + for (size_t i = 0; i < reservedEntriesCount; i++) { + if (callback->reserved[i] != nullptr) { + ALOGE("%s: invalid argument! callback reserved entries must be set to NULL", + __FUNCTION__); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + } + CameraManagerGlobal::getInstance().registerExtendedAvailabilityCallback(callback); + return ACAMERA_OK; +} + +EXPORT +camera_status_t ACameraManager_unregisterExtendedAvailabilityCallback( + ACameraManager* /*manager*/, const ACameraManager_ExtendedAvailabilityCallbacks *callback) { + ATRACE_CALL(); + if (callback == nullptr) { + ALOGE("%s: invalid argument! callback is null!", __FUNCTION__); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + if ((callback->availabilityCallbacks.onCameraAvailable == nullptr) || + (callback->availabilityCallbacks.onCameraUnavailable == nullptr) || + (callback->onCameraAccessPrioritiesChanged == nullptr)) { + ALOGE("%s: invalid argument! callback %p, " + "onCameraAvailable %p, onCameraUnavailable %p onCameraAccessPrioritiesChanged %p", + __FUNCTION__, callback, + callback->availabilityCallbacks.onCameraAvailable, + callback->availabilityCallbacks.onCameraUnavailable, + callback->onCameraAccessPrioritiesChanged); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + CameraManagerGlobal::getInstance().unregisterExtendedAvailabilityCallback(callback); + return ACAMERA_OK; +} + EXPORT camera_status_t ACameraManager_getCameraCharacteristics( ACameraManager* mgr, const char* cameraId, ACameraMetadata** chars){ diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index 7d6ecacbb4..9d40fd7144 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -193,6 +193,20 @@ void CameraManagerGlobal::DeathNotifier::binderDied(const wp&) } } +void CameraManagerGlobal::registerExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks *callback) { + Mutex::Autolock _l(mLock); + Callback cb(callback); + mCallbacks.insert(cb); +} + +void CameraManagerGlobal::unregisterExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks *callback) { + Mutex::Autolock _l(mLock); + Callback cb(callback); + mCallbacks.erase(cb); +} + void CameraManagerGlobal::registerAvailabilityCallback( const ACameraManager_AvailabilityCallbacks *callback) { Mutex::Autolock _l(mLock); @@ -289,12 +303,40 @@ void CameraManagerGlobal::CallbackHandler::onMessageReceived( (*cb)(context, cameraId.c_str()); break; } + case kWhatSendSingleAccessCallback: + { + ACameraManager_AccessPrioritiesChangedCallback cb; + void* context; + AString cameraId; + bool found = msg->findPointer(kCallbackFpKey, (void**) &cb); + if (!found) { + ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__); + return; + } + found = msg->findPointer(kContextKey, &context); + if (!found) { + ALOGE("%s: Cannot find callback context!", __FUNCTION__); + return; + } + (*cb)(context); + break; + } default: ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what()); break; } } +binder::Status CameraManagerGlobal::CameraServiceListener::onCameraAccessPrioritiesChanged() { + sp cm = mCameraManager.promote(); + if (cm != nullptr) { + cm->onCameraAccessPrioritiesChanged(); + } else { + ALOGE("Cannot deliver camera access priority callback. Global camera manager died"); + } + return binder::Status::ok(); +} + binder::Status CameraManagerGlobal::CameraServiceListener::onStatusChanged( int32_t status, const String16& cameraId) { sp cm = mCameraManager.promote(); @@ -306,6 +348,19 @@ binder::Status CameraManagerGlobal::CameraServiceListener::onStatusChanged( return binder::Status::ok(); } +void CameraManagerGlobal::onCameraAccessPrioritiesChanged() { + Mutex::Autolock _l(mLock); + for (auto cb : mCallbacks) { + sp msg = new AMessage(kWhatSendSingleAccessCallback, mHandler); + ACameraManager_AccessPrioritiesChangedCallback cbFp = cb.mAccessPriorityChanged; + if (cbFp != nullptr) { + msg->setPointer(kCallbackFpKey, (void *) cbFp); + msg->setPointer(kContextKey, cb.mContext); + msg->post(); + } + } +} + void CameraManagerGlobal::onStatusChanged( int32_t status, const String8& cameraId) { Mutex::Autolock _l(mLock); diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index c3407f0395..8c1da36418 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -54,6 +54,11 @@ class CameraManagerGlobal final : public RefBase { void unregisterAvailabilityCallback( const ACameraManager_AvailabilityCallbacks *callback); + void registerExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks* callback); + void unregisterExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks* callback); + /** * Return camera IDs that support camera2 */ @@ -86,10 +91,7 @@ class CameraManagerGlobal final : public RefBase { return binder::Status::ok(); } - // Access priority API not implemented yet - virtual binder::Status onCameraAccessPrioritiesChanged() { - return binder::Status::ok(); - } + virtual binder::Status onCameraAccessPrioritiesChanged(); private: const wp mCameraManager; @@ -101,11 +103,19 @@ class CameraManagerGlobal final : public RefBase { explicit Callback(const ACameraManager_AvailabilityCallbacks *callback) : mAvailable(callback->onCameraAvailable), mUnavailable(callback->onCameraUnavailable), + mAccessPriorityChanged(nullptr), mContext(callback->context) {} + explicit Callback(const ACameraManager_ExtendedAvailabilityCallbacks *callback) : + mAvailable(callback->availabilityCallbacks.onCameraAvailable), + mUnavailable(callback->availabilityCallbacks.onCameraUnavailable), + mAccessPriorityChanged(callback->onCameraAccessPrioritiesChanged), + mContext(callback->availabilityCallbacks.context) {} + bool operator == (const Callback& other) const { return (mAvailable == other.mAvailable && mUnavailable == other.mUnavailable && + mAccessPriorityChanged == other.mAccessPriorityChanged && mContext == other.mContext); } bool operator != (const Callback& other) const { @@ -114,6 +124,9 @@ class CameraManagerGlobal final : public RefBase { bool operator < (const Callback& other) const { if (*this == other) return false; if (mContext != other.mContext) return mContext < other.mContext; + if (mAccessPriorityChanged != other.mAccessPriorityChanged) { + return mAccessPriorityChanged < other.mAccessPriorityChanged; + } if (mAvailable != other.mAvailable) return mAvailable < other.mAvailable; return mUnavailable < other.mUnavailable; } @@ -122,13 +135,15 @@ class CameraManagerGlobal final : public RefBase { } ACameraManager_AvailabilityCallback mAvailable; ACameraManager_AvailabilityCallback mUnavailable; + ACameraManager_AccessPrioritiesChangedCallback mAccessPriorityChanged; void* mContext; }; std::set mCallbacks; // definition of handler and message enum { - kWhatSendSingleCallback + kWhatSendSingleCallback, + kWhatSendSingleAccessCallback, }; static const char* kCameraIdKey; static const char* kCallbackFpKey; @@ -141,6 +156,7 @@ class CameraManagerGlobal final : public RefBase { sp mHandler; sp mCbLooper; // Looper thread where callbacks actually happen on + void onCameraAccessPrioritiesChanged(); void onStatusChanged(int32_t status, const String8& cameraId); void onStatusChangedLocked(int32_t status, const String8& cameraId); // Utils for status diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index ea76738806..181290102d 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -278,6 +278,105 @@ camera_status_t ACameraManager_openCamera( #endif /* __ANDROID_API__ >= 24 */ +#if __ANDROID_API__ >= 29 + +/** + * Definition of camera access permission change callback. + * + *

    Notification that camera access priorities have changed and the camera may + * now be openable. An application that was previously denied camera access due to + * a higher-priority user already using the camera, or that was disconnected from an + * active camera session due to a higher-priority user trying to open the camera, + * should try to open the camera again if it still wants to use it. Note that + * multiple applications may receive this callback at the same time, and only one of + * them will succeed in opening the camera in practice, depending on exact access + * priority levels and timing. This method is useful in cases where multiple + * applications may be in the resumed state at the same time, and the user switches + * focus between them, or if the current camera-using application moves between + * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera + * available/unavailable callbacks will not be invoked, but another application may + * now have higher priority for camera access than the current camera-using + * application.

    + + * @param context The optional application context provided by user in + * {@link ACameraManager_AvailabilityListener}. + */ +typedef void (*ACameraManager_AccessPrioritiesChangedCallback)(void* context); + +/** + * A listener for camera devices becoming available/unavailable to open or when + * the camera access permissions change. + * + *

    Cameras become available when they are no longer in use, or when a new + * removable camera is connected. They become unavailable when some + * application or service starts using a camera, or when a removable camera + * is disconnected.

    + * + * @see ACameraManager_registerExtendedAvailabilityCallback + */ +typedef struct ACameraManager_ExtendedAvailabilityListener { + /// + ACameraManager_AvailabilityCallbacks availabilityCallbacks; + + /// Called when there is camera access permission change + ACameraManager_AccessPrioritiesChangedCallback onCameraAccessPrioritiesChanged; + + /// Reserved for future use, please ensure that all entries are set to NULL + void *reserved[6]; +} ACameraManager_ExtendedAvailabilityCallbacks; + +/** + * Register camera extended availability callbacks. + * + *

    onCameraUnavailable will be called whenever a camera device is opened by any camera API + * client. Other camera API clients may still be able to open such a camera device, evicting the + * existing client if they have higher priority than the existing client of a camera device. + * See {@link ACameraManager_openCamera} for more details.

    + * + *

    The callbacks will be called on a dedicated thread shared among all ACameraManager + * instances.

    + * + *

    Since this callback will be registered with the camera service, remember to unregister it + * once it is no longer needed; otherwise the callback will continue to receive events + * indefinitely and it may prevent other resources from being released. Specifically, the + * callbacks will be invoked independently of the general activity lifecycle and independently + * of the state of individual ACameraManager instances.

    + * + * @param manager the {@link ACameraManager} of interest. + * @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be registered. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if manager or callback is NULL, or + * {ACameraManager_ExtendedAvailabilityCallbacks#onCameraAccessPrioritiesChanged} + * or {ACameraManager_AvailabilityCallbacks#onCameraAvailable} or + * {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.
    + */ +camera_status_t ACameraManager_registerExtendedAvailabilityCallback( + ACameraManager* manager, + const ACameraManager_ExtendedAvailabilityCallbacks* callback) __INTRODUCED_IN(29); + +/** + * Unregister camera extended availability callbacks. + * + *

    Removing a callback that isn't registered has no effect.

    + * + * @param manager the {@link ACameraManager} of interest. + * @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be unregistered. + * + * @return
      + *
    • {@link ACAMERA_OK} if the method call succeeds.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if callback, + * {ACameraManager_ExtendedAvailabilityCallbacks#onCameraAccessPrioritiesChanged} + * or {ACameraManager_AvailabilityCallbacks#onCameraAvailable} or + * {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.
    + */ +camera_status_t ACameraManager_unregisterExtendedAvailabilityCallback( + ACameraManager* manager, + const ACameraManager_ExtendedAvailabilityCallbacks* callback) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + __END_DECLS #endif /* _NDK_CAMERA_MANAGER_H */ diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index 5a0002290a..b99936873c 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -22,6 +22,8 @@ LIBCAMERA2NDK { ACameraManager_openCamera; ACameraManager_registerAvailabilityCallback; ACameraManager_unregisterAvailabilityCallback; + ACameraManager_registerExtendedAvailabilityCallback; # introduced=29 + ACameraManager_unregisterExtendedAvailabilityCallback; # introduced=29 ACameraMetadata_copy; ACameraMetadata_free; ACameraMetadata_getAllTags; diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h index 6b1365a8c1..df6935336f 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.h +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -64,6 +64,11 @@ class CameraManagerGlobal final : public RefBase { void unregisterAvailabilityCallback( const ACameraManager_AvailabilityCallbacks *callback); + void registerExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks* /*callback*/) {} + void unregisterExtendedAvailabilityCallback( + const ACameraManager_ExtendedAvailabilityCallbacks* /*callback*/) {} + /** * Return camera IDs that support camera2 */ -- GitLab From c21e52b80f68ec771abc5b2b80aa6a5de6d512a1 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Sat, 9 Mar 2019 17:26:48 +0900 Subject: [PATCH 1095/1530] Don't bind-mount bionic files Bind-mounting of the bionic files on /bionic/* paths no longer required as there are direct symlinks from bionic files in /system partition to the corresponding bionic files in the runtime APEX. e.g., /system/lib/libc.so -> /apex/com.android.runtime/lib/bionic/libc.so Bug: 125549215 Bug: 127499775 Test: m; devices boots Change-Id: I499df3a629e01680c1e1c8f0f4062efaec2ab277 --- apex/ld.config.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index a3e96c4d6c..7f9aad2fcf 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -61,14 +61,15 @@ namespace.platform.isolated = true namespace.platform.search.paths = /system/${LIB} namespace.platform.asan.search.paths = /data/asan/system/${LIB} -# /system/lib/libc.so, etc are symlinks to /bionic/lib/libc.so, etc. -# Add /bionic/lib to the permitted paths because linker uses realpath(3) +# /system/lib/libc.so, etc are symlinks to +/apex/com.android.lib/lib/bionic/libc.so, etc. +# Add /apex/... pat to the permitted paths because linker uses realpath(3) # to check the accessibility of the lib. We could add this to search.paths # instead but that makes the resolution of bionic libs be dependent on -# the order of /system/lib and /bionic/lib in search.paths. If /bionic/lib -# is after /system/lib, then /bionic/lib is never tried because libc.so +# the order of /system/lib and /apex/... in search.paths. If /apex/... +# is after /system/lib, then /apex/... is never tried because libc.so # is always found in /system/lib but fails to pass the accessibility test # because of its realpath. It's better to not depend on the ordering if # possible. -namespace.platform.permitted.paths = /bionic/${LIB} -namespace.platform.asan.permitted.paths = /bionic/${LIB} +namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic +namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic -- GitLab From 39f4bc12d049acec9bf8a665f362b8a18ad14f6f Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Sun, 10 Mar 2019 10:04:47 -0700 Subject: [PATCH 1096/1530] Fix memory leaks These are `new`ed, but never passed to anything else. It looks like simply stack allocating will do what we want here. Caught by our static analyzer: frameworks/av/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp:524:5: warning: Potential memory leak [clang-analyzer-cplusplus.NewDeleteLeaks] frameworks/av/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp:527:9: warning: Potential memory leak [clang-analyzer-cplusplus.NewDeleteLeaks] frameworks/av/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp:529:9: warning: Potential memory leak [clang-analyzer-cplusplus.NewDeleteLeaks] Bug: None Test: Ran the analyer again. It was happy. Change-Id: I8f4cda4a5993f43ad50cf5a0d152ed7158d7c8e1 --- media/libmediaplayer2/MediaPlayer2AudioOutput.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp index 7c3063d72c..c1bfa25b76 100644 --- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp +++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp @@ -521,12 +521,15 @@ jobject MediaPlayer2AudioOutput::getRoutedDevice() { status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) { ALOGV("addAudioDeviceCallback"); Mutex::Autolock lock(mLock); - jobject listener = (new JObjectHolder( - JAudioTrack::getListener(jRoutingDelegate)))->getJObject(); + + JObjectHolder listenerHolder{JAudioTrack::getListener(jRoutingDelegate)}; + jobject listener = listenerHolder.getJObject(); if (JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) { - jobject handler = (new JObjectHolder( - JAudioTrack::getHandler(jRoutingDelegate)))->getJObject(); - jobject routingDelegate = (new JObjectHolder(jRoutingDelegate))->getJObject(); + JObjectHolder handlerHolder{JAudioTrack::getHandler(jRoutingDelegate)}; + jobject handler = handlerHolder.getJObject(); + + JObjectHolder routingDelegateHolder = JObjectHolder(jRoutingDelegate); + jobject routingDelegate = routingDelegateHolder.getJObject(); mRoutingDelegates.push_back(std::pair(listener, routingDelegate)); if (mJAudioTrack != nullptr) { return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler); -- GitLab From f2795b52eaf267971dd735dcd5f34c195d66ef1b Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Tue, 12 Mar 2019 00:05:27 +0900 Subject: [PATCH 1097/1530] Remove double_loadable from libstagefright_xmlparser This partially reverts ag/6327294. Since libstagefright_xmlparser is no longer used by libmediandk, double-loading can't happen with this lib. Bug: 124268753 Test: m -j Change-Id: I76c961b2ff03665b9211c3fc8cbdbb073daddbe1 --- media/libstagefright/xmlparser/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp index 819058cff2..bebfb3bd64 100644 --- a/media/libstagefright/xmlparser/Android.bp +++ b/media/libstagefright/xmlparser/Android.bp @@ -10,7 +10,6 @@ cc_library_shared { vndk: { enabled: true, }, - double_loadable: true, srcs: [ "MediaCodecsXmlParser.cpp", -- GitLab From d3f301ccb9431a70c0489e9954e45add600919be Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 11 Mar 2019 08:58:03 -0700 Subject: [PATCH 1098/1530] audioflinger: Fix crash on patch creation attempt The code for clearing software patch connections was missing a check for a null track pointer. Bug: 126789266 Bug: 128269566 Test: see repro steps in b/128269566 Change-Id: Ice6887622d5fa2fa3198ce15146bff3cb05f7451 --- services/audioflinger/PatchPanel.cpp | 2 +- services/audioflinger/PatchPanel.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 13243b885b..a210a6d3df 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -543,7 +543,7 @@ void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel) __func__, mRecord.handle(), mPlayback.handle()); mRecord.stopTrack(); mPlayback.stopTrack(); - mRecord.track()->clearPeerProxy(); // mRecord stop is synchronous. Break PeerProxy sp<> cycle. + mRecord.clearTrackPeer(); // mRecord stop is synchronous. Break PeerProxy sp<> cycle. mRecord.closeConnections(panel); mPlayback.closeConnections(panel); } diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h index aba2cc27db..181e27c2ea 100644 --- a/services/audioflinger/PatchPanel.h +++ b/services/audioflinger/PatchPanel.h @@ -128,6 +128,7 @@ private: mThread->addPatchTrack(mTrack); mTrack->setPeerProxy(peer, true /* holdReference */); } + void clearTrackPeer() { if (mTrack) mTrack->clearPeerProxy(); } void stopTrack() { if (mTrack) mTrack->stop(); } void swap(Endpoint &other) noexcept { -- GitLab From bfac583d0054ee1ca7755378c26631832dff63fc Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Mar 2019 16:55:28 -0800 Subject: [PATCH 1099/1530] audiopolicy: Remove raw pointer references to AudioMix AudioInputDescriptor and AudioOutputDescriptor used to reference AudioMix instances using a raw pointer. This isn't safe as AudioMix was owned by AudioPolicyMix, which is not referenced by descriptors. Change AudioMix* pointers in Audio{Input|Output}Descriptor to wp which reflects their relationship correctly. To ensure that code does not operate on AudioMix instances independently from AudioPolicyMix, and to avoid introducing a lot of getter / setter methods into AudioPolicyMix, make the latter to inherit AudioMix. This makes sense because AudioPolicyMix is essentially a ref-counted version of AudioMix. Bug: 124899895 Test: build and sanity check on crosshatch, build crosshatch with USE_CONFIGURABLE_AUDIO_POLICY := 1 Change-Id: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a --- .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyMix.h | 27 +++--- .../src/AudioInputDescriptor.cpp | 10 ++- .../src/AudioOutputDescriptor.cpp | 6 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 90 +++++++------------ .../interface/AudioPolicyManagerInterface.h | 2 +- .../engineconfigurable/src/Engine.cpp | 2 +- .../engineconfigurable/src/Engine.h | 2 +- .../audiopolicy/enginedefault/src/Engine.cpp | 2 +- .../audiopolicy/enginedefault/src/Engine.h | 2 +- .../managerdefault/AudioPolicyManager.cpp | 55 ++++++------ .../managerdefault/AudioPolicyManager.h | 2 +- 13 files changed, 90 insertions(+), 118 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 803cfacc75..e071fe07b1 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -29,7 +29,7 @@ namespace android { -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input @@ -53,7 +53,7 @@ public: void dump(String8 *dst) const override; audio_io_handle_t mIoHandle = AUDIO_IO_HANDLE_NONE; // input handle - AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from virtual void toAudioPortConfig(struct audio_port_config *dstConfig, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 704f404d9a..73a8249000 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -34,7 +34,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; class ActivityTracking @@ -281,7 +281,7 @@ public: } DeviceVector mDevices; /**< current devices this output is routed to */ - AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy protected: const sp mPort; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index d6f24b2ad4..7a9c26eddf 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -31,24 +31,19 @@ namespace android { /** * custom mix entry in mPolicyMixes */ -class AudioPolicyMix : public RefBase { +class AudioPolicyMix : public AudioMix, public RefBase { public: - AudioPolicyMix() {} + AudioPolicyMix(const AudioMix &mix) : AudioMix(mix) {} + AudioPolicyMix(const AudioPolicyMix&) = delete; + AudioPolicyMix& operator=(const AudioPolicyMix&) = delete; - const sp &getOutput() const; - - void setOutput(sp &output); - - void clearOutput(); - - android::AudioMix *getMix(); - - void setMix(const AudioMix &mix); + const sp &getOutput() const { return mOutput; } + void setOutput(const sp &output) { mOutput = output; } + void clearOutput() { mOutput.clear(); } void dump(String8 *dst, int spaces, int index) const; private: - AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; @@ -77,21 +72,19 @@ public: sp getDeviceAndMixForInputSource(audio_source_t inputSource, const DeviceVector &availableDeviceTypes, - AudioMix **policyMix) const; + sp *policyMix) const; /** * @brief try to find a matching mix for a given output descriptor and returns the associated * output device. * @param output to be considered * @param availableOutputDevices list of output devices currently reachable - * @param policyMix to be returned if any mix matching ouput descriptor * @return device selected from the mix attached to the output, null pointer otherwise */ sp getDeviceAndMixForOutput(const sp &output, - const DeviceVector &availableOutputDevices, - AudioMix **policyMix = nullptr); + const DeviceVector &availableOutputDevices); - status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); status_t removeUidDeviceAffinities(uid_t uid); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 1fa11231b8..635de6f1e4 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -22,6 +22,7 @@ #include #include "AudioInputDescriptor.h" #include "AudioGain.h" +#include "AudioPolicyMix.h" #include "HwModule.h" namespace android { @@ -308,16 +309,17 @@ void AudioInputDescriptor::setClientActive(const sp& cli const int delta = active ? 1 : -1; mGlobalActiveCount += delta; + sp policyMix = mPolicyMix.promote(); if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index fd33649ff1..833c7b9bc9 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -19,6 +19,7 @@ #include #include "AudioOutputDescriptor.h" +#include "AudioPolicyMix.h" #include "IOProfile.h" #include "AudioGain.h" #include "Volume.h" @@ -111,9 +112,10 @@ void AudioOutputDescriptor::setClientActive(const sp& cli } mGlobalActiveCount += delta; - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + sp policyMix = mPolicyMix.promote(); + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { if ((oldGlobalActiveCount == 0) || (mGlobalActiveCount == 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, mGlobalActiveCount > 0 ? MIX_STATE_MIXING : MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 23d764e9d1..f7289ca112 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -27,51 +27,26 @@ namespace android { -void AudioPolicyMix::setOutput(sp &output) -{ - mOutput = output; -} - -const sp &AudioPolicyMix::getOutput() const -{ - return mOutput; -} - -void AudioPolicyMix::clearOutput() -{ - mOutput.clear(); -} - -void AudioPolicyMix::setMix(const AudioMix &mix) -{ - mMix = mix; -} - -android::AudioMix *AudioPolicyMix::getMix() -{ - return &mMix; -} - void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const { dst->appendFormat("%*sAudio Policy Mix %d:\n", spaces, "", index + 1); std::string mixTypeLiteral; - if (!MixTypeConverter::toString(mMix.mMixType, mixTypeLiteral)) { - ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMix.mMixType); + if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) { + ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType); return; } dst->appendFormat("%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str()); std::string routeFlagLiteral; - RouteFlagTypeConverter::maskToString(mMix.mRouteFlags, routeFlagLiteral); + RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral); dst->appendFormat("%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str()); - dst->appendFormat("%*s- device type: %s\n", spaces, "", toString(mMix.mDeviceType).c_str()); + dst->appendFormat("%*s- device type: %s\n", spaces, "", toString(mDeviceType).c_str()); - dst->appendFormat("%*s- device address: %s\n", spaces, "", mMix.mDeviceAddress.string()); + dst->appendFormat("%*s- device address: %s\n", spaces, "", mDeviceAddress.string()); int indexCriterion = 0; - for (const auto &criterion : mMix.mCriteria) { + for (const auto &criterion : mCriteria) { dst->appendFormat("%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++); std::string usageLiteral; @@ -81,7 +56,7 @@ void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const } dst->appendFormat("%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str()); - if (mMix.mMixType == MIX_TYPE_RECORDERS) { + if (mMixType == MIX_TYPE_RECORDERS) { std::string sourceLiteral; if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) { ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource); @@ -109,12 +84,11 @@ status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); return BAD_VALUE; } - sp policyMix = new AudioPolicyMix(); - policyMix->setMix(mix); + sp policyMix = new AudioPolicyMix(mix); add(address, policyMix); if (desc != 0) { - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; policyMix->setOutput(desc); } return NO_ERROR; @@ -168,15 +142,14 @@ status_t AudioPolicyMixCollection::getOutputForAttr( continue; } - AudioMix *mix = policyMix->getMix(); - const bool primaryOutputMix = !is_mix_loopback_render(mix->mRouteFlags); + const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags); if (primaryOutputMix && primaryDesc != 0) { ALOGV("%s: Skiping %zu: Primary output already found", __func__, i); continue; // Primary output already found } - switch (mixMatch(mix, i, attributes, uid)) { + switch (mixMatch(policyMix.get(), i, attributes, uid)) { case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort? case MixMatchStatus::NO_MATCH: ALOGV("%s: Mix %zu: does not match", __func__, i); @@ -184,7 +157,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr( case MixMatchStatus::MATCH:; } - policyDesc->mPolicyMix = mix; + policyDesc->mPolicyMix = policyMix; if (primaryOutputMix) { primaryDesc = policyDesc; ALOGV("%s: Mix %zu: set primary desc", __func__, i); @@ -327,17 +300,13 @@ AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( sp AudioPolicyMixCollection::getDeviceAndMixForOutput( const sp &output, - const DeviceVector &availableOutputDevices, - AudioMix **policyMix) + const DeviceVector &availableOutputDevices) { for (size_t i = 0; i < size(); i++) { if (valueAt(i)->getOutput() == output) { - AudioMix *mix = valueAt(i)->getMix(); - if (policyMix != nullptr) - *policyMix = mix; // This Desc is involved in a Mix, which has the highest prio - audio_devices_t deviceType = mix->mDeviceType; - String8 address = mix->mDeviceAddress; + audio_devices_t deviceType = valueAt(i)->mDeviceType; + String8 address = valueAt(i)->mDeviceAddress; ALOGV("%s: device (0x%x, addr=%s) forced by mix", __FUNCTION__, deviceType, address.c_str()); return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT); @@ -347,10 +316,12 @@ sp AudioPolicyMixCollection::getDeviceAndMixForOutput( } sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( - audio_source_t inputSource, const DeviceVector &availDevices, AudioMix **policyMix) const + audio_source_t inputSource, + const DeviceVector &availDevices, + sp *policyMix) const { for (size_t i = 0; i < size(); i++) { - AudioMix *mix = valueAt(i)->getMix(); + AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; } @@ -365,7 +336,7 @@ sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( auto mixDevice = availDevices.getDevice(device, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT); if (mixDevice != nullptr) { - if (policyMix != NULL) { + if (policyMix != nullptr) { *policyMix = mix; } return mixDevice; @@ -377,7 +348,8 @@ sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( return nullptr; } -status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) +status_t AudioPolicyMixCollection::getInputMixForAttr( + audio_attributes_t attr, sp *policyMix) { if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { return BAD_VALUE; @@ -387,9 +359,8 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A #ifdef LOG_NDEBUG ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - const AudioMix *mix = policyMix->getMix(); - ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); + sp audioPolicyMix = valueAt(i); + ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.string()); } #endif @@ -399,13 +370,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return BAD_VALUE; } sp audioPolicyMix = valueAt(index); - AudioMix *mix = audioPolicyMix->getMix(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } - *policyMix = mix; + if (policyMix != nullptr) { + *policyMix = audioPolicyMix; + } return NO_ERROR; } @@ -416,7 +388,7 @@ status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, // for each player mix: add a rule to match or exclude the uid based on the device for (size_t i = 0; i < size(); i++) { - const AudioMix *mix = valueAt(i)->getMix(); + const AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } @@ -445,7 +417,7 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { // for each player mix: remove existing rules that match or exclude this uid for (size_t i = 0; i < size(); i++) { bool foundUidRule = false; - const AudioMix *mix = valueAt(i)->getMix(); + const AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } @@ -473,7 +445,7 @@ status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid, // for each player mix: find rules that don't exclude this uid, and add the device to the list for (size_t i = 0; i < size(); i++) { bool ruleAllowsUid = true; - const AudioMix *mix = valueAt(i)->getMix(); + const AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 38f3401c2b..4315061d25 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -169,7 +169,7 @@ public: * @return selected input device for the audio attributes, may be null if error. */ virtual sp getInputDeviceForAttributes( - const audio_attributes_t &attr, AudioMix **mix = nullptr) const = 0; + const audio_attributes_t &attr, sp *mix = nullptr) const = 0; /** * Get the legacy stream type for a given audio attributes. diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 89a1694a92..89aaa849f9 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -295,7 +295,7 @@ DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool } sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, - AudioMix **mix) const + sp *mix) const { const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h index 555399447e..4662e7e21b 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.h +++ b/services/audiopolicy/engineconfigurable/src/Engine.h @@ -62,7 +62,7 @@ public: bool fromCache = false) const override; sp getInputDeviceForAttributes( - const audio_attributes_t &attr, AudioMix **mix = nullptr) const override; + const audio_attributes_t &attr, sp *mix = nullptr) const override; void updateDeviceSelectionCache() override; diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index f191738eec..66a6965742 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -727,7 +727,7 @@ DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool } sp Engine::getInputDeviceForAttributes(const audio_attributes_t &attr, - AudioMix **mix) const + sp *mix) const { const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h index d8a369891d..d5dfacce4e 100644 --- a/services/audiopolicy/enginedefault/src/Engine.h +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -66,7 +66,7 @@ private: bool fromCache = false) const override; sp getInputDeviceForAttributes( - const audio_attributes_t &attr, AudioMix **mix = nullptr) const override; + const audio_attributes_t &attr, sp *mix = nullptr) const override; void updateDeviceSelectionCache() override; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ea98253adc..938445b143 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -217,10 +217,11 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT && strncmp(device_address, "0", AUDIO_DEVICE_MAX_ADDRESS_LEN) != 0) { for (audio_io_handle_t output : outputs) { sp desc = mOutputs.valueFor(output); - if (desc->mPolicyMix != nullptr - && desc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS + sp policyMix = desc->mPolicyMix.promote(); + if (policyMix != nullptr + && policyMix->mMixType == MIX_TYPE_RECORDERS && strncmp(device_address, - desc->mPolicyMix->mDeviceAddress.string(), + policyMix->mDeviceAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { doCheckForDeviceAndOutputChanges = false; break; @@ -968,7 +969,7 @@ status_t AudioPolicyManager::getOutputForAttrInt( } if (usePrimaryOutputFromPolicyMixes) { *output = policyDesc->mIoHandle; - AudioMix *mix = policyDesc->mPolicyMix; + sp mix = policyDesc->mPolicyMix.promote(); sp deviceDesc = mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress, @@ -1602,10 +1603,9 @@ status_t AudioPolicyManager::startSource(const sp& outp (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE); DeviceVector devices; - AudioMix *policyMix = NULL; + sp policyMix = outputDesc->mPolicyMix.promote(); const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; + if (policyMix != NULL) { audio_devices_t newDeviceType; address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) { @@ -1775,12 +1775,13 @@ status_t AudioPolicyManager::stopSource(const sp& outpu if (outputDesc->getActivityCount(clientVolSrc) == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS + sp policyMix = outputDesc->mPolicyMix.promote(); if (audio_is_remote_submix_device(outputDesc->devices().types()) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, + policyMix->mDeviceAddress, "remote-submix", AUDIO_FORMAT_DEFAULT); } } @@ -1895,7 +1896,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, status_t status = NO_ERROR; audio_source_t halInputSource; audio_attributes_t attributes = *attr; - AudioMix *policyMix = NULL; + sp policyMix; sp device; sp inputDesc; sp clientDesc; @@ -1992,7 +1993,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, status = BAD_VALUE; goto error; } - if (policyMix != nullptr) { + if (policyMix) { ALOG_ASSERT(policyMix->mMixType == MIX_TYPE_RECORDERS, "Invalid Mix Type"); // there is an external policy, but this input is attached to a mix of recorders, // meaning it receives audio injected into the framework, so the recorder doesn't @@ -2044,7 +2045,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp &policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; audio_source_t halInputSource = attributes.source; @@ -2204,10 +2205,11 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) setInputDevice(input, device, true /* force */); if (inputDesc->activeCount() == 1) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } @@ -2222,10 +2224,10 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId) // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->getDeviceType())) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -2262,10 +2264,11 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) if (inputDesc->isActive()) { setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); } else { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } @@ -2273,10 +2276,10 @@ status_t AudioPolicyManager::stopInput(audio_port_handle_t portId) // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->getDeviceType())) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -4582,7 +4585,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d sp policyMix; if (mPolicyMixes.getAudioPolicyMix(address, policyMix) == NO_ERROR) { policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; } else { ALOGW("checkOutputsForDevice() cannot find policy for address %s", address.string()); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 3a31e1e314..b83e9a8b94 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -799,7 +799,7 @@ private: const audio_attributes_t &attributes, const audio_config_base_t *config, audio_input_flags_t flags, - AudioMix *policyMix); + const sp &policyMix); // event is one of STARTING_OUTPUT, STARTING_BEACON, STOPPING_OUTPUT, STOPPING_BEACON // returns 0 if no mute/unmute event happened, the largest latency of the device where -- GitLab From b71ede1347121e6a5fdefdc18f20587a92863f47 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Mon, 11 Mar 2019 19:10:19 +0000 Subject: [PATCH 1100/1530] Revert "Fix memory leaks" This reverts commit 39f4bc12d049acec9bf8a665f362b8a18ad14f6f. Reason for revert: not the right fix Change-Id: I6b6150f6a7c0df6517406a4c385b153fec839c3a --- media/libmediaplayer2/MediaPlayer2AudioOutput.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp index c1bfa25b76..7c3063d72c 100644 --- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp +++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp @@ -521,15 +521,12 @@ jobject MediaPlayer2AudioOutput::getRoutedDevice() { status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) { ALOGV("addAudioDeviceCallback"); Mutex::Autolock lock(mLock); - - JObjectHolder listenerHolder{JAudioTrack::getListener(jRoutingDelegate)}; - jobject listener = listenerHolder.getJObject(); + jobject listener = (new JObjectHolder( + JAudioTrack::getListener(jRoutingDelegate)))->getJObject(); if (JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) { - JObjectHolder handlerHolder{JAudioTrack::getHandler(jRoutingDelegate)}; - jobject handler = handlerHolder.getJObject(); - - JObjectHolder routingDelegateHolder = JObjectHolder(jRoutingDelegate); - jobject routingDelegate = routingDelegateHolder.getJObject(); + jobject handler = (new JObjectHolder( + JAudioTrack::getHandler(jRoutingDelegate)))->getJObject(); + jobject routingDelegate = (new JObjectHolder(jRoutingDelegate))->getJObject(); mRoutingDelegates.push_back(std::pair(listener, routingDelegate)); if (mJAudioTrack != nullptr) { return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler); -- GitLab From eb91f4821595a17726fb957b2e1b46d02481fb1e Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Mon, 11 Mar 2019 14:23:21 -0700 Subject: [PATCH 1101/1530] Camera: Centralize OWNERS Test: Builds Change-Id: I17830f84ebfeb11bf7cbe0ba5aa9f19838ec37be --- camera/OWNERS | 4 +++- media/ndk/OWNERS | 4 +--- services/camera/OWNERS | 7 +------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/camera/OWNERS b/camera/OWNERS index 18acfee145..d6b95da768 100644 --- a/camera/OWNERS +++ b/camera/OWNERS @@ -1,6 +1,8 @@ -cychen@google.com epeev@google.com etalvala@google.com +jchowdhary@google.com shuzhenwang@google.com yinchiayeh@google.com +# backup owner +cychen@google.com zhijunhe@google.com diff --git a/media/ndk/OWNERS b/media/ndk/OWNERS index 11e83409f2..b015a9ba7a 100644 --- a/media/ndk/OWNERS +++ b/media/ndk/OWNERS @@ -1,5 +1,3 @@ marcone@google.com # For AImage/AImageReader -etalvala@google.com -yinchiayeh@google.com -zhijunhe@google.com +include platform/frameworks/av/camera:/OWNERS diff --git a/services/camera/OWNERS b/services/camera/OWNERS index 18acfee145..f112576ef8 100644 --- a/services/camera/OWNERS +++ b/services/camera/OWNERS @@ -1,6 +1 @@ -cychen@google.com -epeev@google.com -etalvala@google.com -shuzhenwang@google.com -yinchiayeh@google.com -zhijunhe@google.com +include platform/frameworks/av/camera:/OWNERS -- GitLab From bc2a37f4a8087425e8a17cf746270e5acf9de049 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 11 Mar 2019 18:12:37 -0700 Subject: [PATCH 1102/1530] camera2 ndk: Add some null checks before dereferencing captureSequenceId. Test: AImageReaderVendorTest Change-Id: Icdb11569305cdbf4e4f9c791b306292e46395271 Signed-off-by: Jayant Chowdhary --- camera/ndk/NdkCameraCaptureSession.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp index ab796fb751..1ac8482ac4 100644 --- a/camera/ndk/NdkCameraCaptureSession.cpp +++ b/camera/ndk/NdkCameraCaptureSession.cpp @@ -105,7 +105,9 @@ camera_status_t ACameraCaptureSession_logicalCamera_capture( if (session->isClosed()) { ALOGE("%s: session %p is already closed", __FUNCTION__, session); - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + if (captureSequenceId) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } return ACAMERA_ERROR_SESSION_CLOSED; } @@ -127,7 +129,9 @@ camera_status_t ACameraCaptureSession_setRepeatingRequest( if (session->isClosed()) { ALOGE("%s: session %p is already closed", __FUNCTION__, session); - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + if (captureSequenceId) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } return ACAMERA_ERROR_SESSION_CLOSED; } @@ -149,7 +153,9 @@ camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( if (session->isClosed()) { ALOGE("%s: session %p is already closed", __FUNCTION__, session); - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + if (captureSequenceId) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } return ACAMERA_ERROR_SESSION_CLOSED; } -- GitLab From b420379f7256f672f9d6e305adc165d63d7115a4 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 11 Mar 2019 18:13:50 -0700 Subject: [PATCH 1103/1530] Prevent spinning in parseSps on bad loop values parseUEWithFallback could return garbabe values for malformed streams, bail out early if this happens. bug: 124253062 test: poc attached in bug, shouldn't see high cpu usage in Photos Change-Id: I7664384038d100dc55f6e35fbf79cd09ebc204e3 --- media/libstagefright/HevcUtils.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp index 0c38f2e4de..482a1a7dc3 100644 --- a/media/libstagefright/HevcUtils.cpp +++ b/media/libstagefright/HevcUtils.cpp @@ -312,14 +312,23 @@ status_t HevcParameterSets::parseSps(const uint8_t* data, size_t size) { for (uint32_t j = 0; j < numPics; ++j) { skipUE(&reader); // delta_poc_s0|1_minus1[i] reader.skipBits(1); // used_by_curr_pic_s0|1_flag[i] + if (reader.overRead()) { + return ERROR_MALFORMED; + } } } + if (reader.overRead()) { + return ERROR_MALFORMED; + } } if (reader.getBitsWithFallback(1, 0)) { // long_term_ref_pics_present_flag uint32_t numLongTermRefPicSps = parseUEWithFallback(&reader, 0); for (uint32_t i = 0; i < numLongTermRefPicSps; ++i) { reader.skipBits(log2MaxPicOrderCntLsb); // lt_ref_pic_poc_lsb_sps[i] reader.skipBits(1); // used_by_curr_pic_lt_sps_flag[i] + if (reader.overRead()) { + return ERROR_MALFORMED; + } } } reader.skipBits(1); // sps_temporal_mvp_enabled_flag -- GitLab From aaac0fd20d26e6a3a1c2d4bf0638f6a183ecec4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 22 Nov 2018 17:56:39 +0100 Subject: [PATCH 1104/1530] audiopolicy: switch to VolumeGroup for Output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Volume used to be managed per stream type. VolumeGroup is a configurable way to manage volume affinity. This CL allows to switch to VolumeGroup as new volume affinity management. Bug: 124767636 Test: audio smoke tests Change-Id: I71fc214f6db3158f0f05920cc3d700b29db1a4bc Signed-off-by: François Gaffie --- services/audiopolicy/common/include/Volume.h | 9 +- .../include/AudioOutputDescriptor.h | 25 +- .../src/AudioOutputDescriptor.cpp | 49 +- .../engine/common/include/EngineBase.h | 6 +- .../engine/common/include/ProductStrategy.h | 2 + .../engine/common/src/EngineBase.cpp | 17 +- .../engine/common/src/ProductStrategy.cpp | 12 +- .../interface/AudioPolicyManagerInterface.h | 4 - .../parameter-framework/plugin/Android.mk | 4 +- .../managerdefault/AudioPolicyManager.cpp | 496 +++++++++--------- .../managerdefault/AudioPolicyManager.h | 55 +- 11 files changed, 373 insertions(+), 306 deletions(-) diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index 48b5271932..561f100092 100644 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -16,19 +16,22 @@ #pragma once +#include #include #include #include namespace android { + /** * VolumeSource is the discriminent for volume management on an output. * It used to be the stream type by legacy, it may be host volume group or a volume curves if - * we allow to have more than one curve per volume group. + * we allow to have more than one curve per volume group (mandatory to get rid of AudioServer + * stream aliases. */ -enum VolumeSource : std::underlying_type::type; -static const VolumeSource VOLUME_SOURCE_NONE = static_cast(AUDIO_STREAM_DEFAULT); +enum VolumeSource : std::underlying_type::type; +static const VolumeSource VOLUME_SOURCE_NONE = static_cast(VOLUME_GROUP_NONE); static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) { return static_cast(stream); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 704f404d9a..04fc3d086b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -107,7 +107,7 @@ private: }; /** * Note: volume activities shall be indexed by CurvesId if we want to allow multiple - * curves per volume group, inferring a mute management or volume balancing between HW and SW is + * curves per volume source, inferring a mute management or volume balancing between HW and SW is * done */ using VolumeActivities = std::map; @@ -157,7 +157,7 @@ public: virtual uint32_t latency() { return 0; } virtual bool isFixedVolume(audio_devices_t device); virtual bool setVolume(float volumeDb, - audio_stream_type_t stream, + VolumeSource volumeSource, const StreamTypeVector &streams, audio_devices_t device, uint32_t delayMs, bool force); @@ -221,7 +221,7 @@ public: } void setCurVolume(VolumeSource vs, float volumeDb) { - // Even if not activity for this group registered, need to create anyway + // Even if not activity for this source registered, need to create anyway mVolumeActivities[vs].setVolume(volumeDb); } float getCurVolume(VolumeSource vs) const @@ -280,6 +280,11 @@ public: return mActiveClients; } + bool useHwGain() const + { + return !devices().isEmpty() ? devices().itemAt(0)->hasGainController() : false; + } + DeviceVector mDevices; /**< current devices this output is routed to */ AudioMix *mPolicyMix = nullptr; // non NULL when used by a dynamic policy @@ -328,7 +333,7 @@ public: } } virtual bool setVolume(float volumeDb, - audio_stream_type_t stream, + VolumeSource volumeSource, const StreamTypeVector &streams, audio_devices_t device, uint32_t delayMs, bool force); @@ -402,7 +407,7 @@ public: void dump(String8 *dst) const override; virtual bool setVolume(float volumeDb, - audio_stream_type_t stream, + VolumeSource volumeSource, const StreamTypeVector &streams, audio_devices_t device, uint32_t delayMs, bool force); @@ -422,7 +427,7 @@ public: bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const; /** - * return whether any source contributing to VolumeSource is playing remotely, override + * return whether any source contributing to VolumeSource is playing remotely, override * to change the definition of * local/remote playback, used for instance by notification manager to not make * media players lose audio focus when not playing locally @@ -488,8 +493,8 @@ public: /** * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that * hold the volume source to be ignored - * @param volumeSourceToIgnore source not considered in the activity detection - * @return true if any output is active for any source except the one to be ignored + * @param volumeSourceToIgnore source not to be considered in the activity detection + * @return true if any output is active for any volume source except the one to be ignored */ bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const { @@ -518,8 +523,8 @@ public: /** * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that * hold the volume source to be ignored - * @param volumeSourceToIgnore source not considered in the activity detection - * @return true if any output is active for any source except the one to be ignored + * @param volumeSourceToIgnore source not to be considered in the activity detection + * @return true if any output is active for any volume source except the one to be ignored */ bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const { diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index fd33649ff1..4d325593c5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -23,6 +23,7 @@ #include "AudioGain.h" #include "Volume.h" #include "HwModule.h" +#include "TypeConverter.h" #include #include @@ -150,17 +151,18 @@ bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused) } bool AudioOutputDescriptor::setVolume(float volumeDb, - audio_stream_type_t stream, - audio_devices_t device __unused, + VolumeSource volumeSource, + const StreamTypeVector &/*streams*/, + audio_devices_t /*device*/, uint32_t delayMs, bool force) { // We actually change the volume if: // - the float value returned by computeVolume() changed // - the force flag is set - if (volumeDb != getCurVolume(static_cast(stream)) || force) { - ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volumeDb, delayMs); - setCurVolume(static_cast(stream), volumeDb); + if (volumeDb != getCurVolume(volumeSource) || force) { + ALOGV("%s for volumeSrc %d, volume %f, delay %d", __func__, volumeSource, volumeDb, delayMs); + setCurVolume(volumeSource, volumeDb); return true; } return false; @@ -389,23 +391,33 @@ void SwAudioOutputDescriptor::toAudioPort( } bool SwAudioOutputDescriptor::setVolume(float volumeDb, - audio_stream_type_t stream, + VolumeSource vs, const StreamTypeVector &streamTypes, audio_devices_t device, uint32_t delayMs, bool force) { - if (!AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force)) { + StreamTypeVector streams = streamTypes; + if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) { return false; } + if (streams.empty()) { + streams.push_back(AUDIO_STREAM_MUSIC); + } if (!devices().isEmpty()) { // Assume first device to check upon Gain Crontroller availability + // APM loops on all group, so filter on active group to set the port gain, + // let the other groups set the stream volume as per legacy const auto &devicePort = devices().itemAt(0); - ALOGV("%s: device %s hasGC %d", __FUNCTION__, - devicePort->toString().c_str(), devices().itemAt(0)->hasGainController(true)); - if (devicePort->hasGainController(true)) { + if (devicePort->hasGainController(true) && isActive(vs)) { + ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str()); + // @todo: here we might be in trouble if the SwOutput has several active clients with + // different Volume Source (or if we allow several curves within same volume group) + // // @todo: default stream volume to max (0) when using HW Port gain? float volumeAmpl = Volume::DbToAmpl(0); - mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + for (const auto &stream : streams) { + mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + } AudioGains gains = devicePort->getGains(); int gainMinValueInMb = gains[0]->getMinValueInMb(); @@ -422,11 +434,15 @@ bool SwAudioOutputDescriptor::setVolume(float volumeDb, } } // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is enabled - float volumeAmpl = Volume::DbToAmpl(getCurVolume(static_cast(stream))); - if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { + float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs)); + if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) { mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs); } - mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + for (const auto &stream : streams) { + ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__, + mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str()); + mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs); + } return true; } @@ -616,12 +632,13 @@ void HwAudioOutputDescriptor::toAudioPort( bool HwAudioOutputDescriptor::setVolume(float volumeDb, - audio_stream_type_t stream, + VolumeSource volumeSource, const StreamTypeVector &streams, audio_devices_t device, uint32_t delayMs, bool force) { - bool changed = AudioOutputDescriptor::setVolume(volumeDb, stream, device, delayMs, force); + bool changed = + AudioOutputDescriptor::setVolume(volumeDb, volumeSource, streams, device, delayMs, force); if (changed) { // TODO: use gain controller on source device if any to adjust volume diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h index 6ff8512408..cedc78f8b5 100644 --- a/services/audiopolicy/engine/common/include/EngineBase.h +++ b/services/audiopolicy/engine/common/include/EngineBase.h @@ -84,10 +84,6 @@ public: volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const override; - StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const override; - - AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const override; - status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const override; void dump(String8 *dst) const override; @@ -112,7 +108,7 @@ public: VolumeSource toVolumeSource(audio_stream_type_t stream) const { - return static_cast(stream); + return static_cast(getVolumeGroupForStreamType(stream)); } status_t switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst); diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h index 767a8ed583..1a2a198ccd 100644 --- a/services/audiopolicy/engine/common/include/ProductStrategy.h +++ b/services/audiopolicy/engine/common/include/ProductStrategy.h @@ -152,6 +152,8 @@ public: volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const; + volume_group_t getDefaultVolumeGroup() const; + product_strategy_t getDefault() const; void dump(String8 *dst, int spaces = 0) const; diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 4fe7b42414..07a7e65124 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -218,6 +218,9 @@ VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const { volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream); + if (volGr == VOLUME_GROUP_NONE) { + volGr = mProductStrategies.getDefaultVolumeGroup(); + } const auto &iter = mVolumeGroups.find(volGr); LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s", toString(stream).c_str()); @@ -260,20 +263,6 @@ volume_group_t EngineBase::getVolumeGroupForStreamType(audio_stream_type_t strea return mProductStrategies.getVolumeGroupForStreamType(stream); } -StreamTypeVector EngineBase::getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const -{ - // @TODO default music stream to control volume if no group? - return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ? - mVolumeGroups.at(volumeGroup)->getStreamTypes() : - StreamTypeVector(AUDIO_STREAM_MUSIC); -} - -AttributesVector EngineBase::getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const -{ - return (mVolumeGroups.find(volumeGroup) != end(mVolumeGroups)) ? - mVolumeGroups.at(volumeGroup)->getSupportedAttributes() : AttributesVector(); -} - status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const { for (const auto &iter : mVolumeGroups) { diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp index 16e6690a58..f74f190d0d 100644 --- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp +++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp @@ -270,11 +270,7 @@ volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(const audio_attri return group; } } - product_strategy_t defaultStrategy = getDefault(); - if (defaultStrategy == PRODUCT_STRATEGY_NONE) { - return VOLUME_GROUP_NONE; - } - return at(defaultStrategy)->getDefaultVolumeGroup(); + return getDefaultVolumeGroup(); } volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type_t stream) const @@ -285,6 +281,12 @@ volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(audio_stream_type return group; } } + ALOGW("%s: no volume group for %s, using default", __func__, toString(stream).c_str()); + return getDefaultVolumeGroup(); +} + +volume_group_t ProductStrategyMap::getDefaultVolumeGroup() const +{ product_strategy_t defaultStrategy = getDefault(); if (defaultStrategy == PRODUCT_STRATEGY_NONE) { return VOLUME_GROUP_NONE; diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h index 38f3401c2b..4cf72c8c09 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -283,10 +283,6 @@ public: */ virtual volume_group_t getVolumeGroupForStreamType(audio_stream_type_t stream) const = 0; - virtual StreamTypeVector getStreamTypesForVolumeGroup(volume_group_t volumeGroup) const = 0; - - virtual AttributesVector getAllAttributesForVolumeGroup(volume_group_t volumeGroup) const = 0; - /** * @brief listAudioVolumeGroups introspection API to get the Audio Volume Groups, aka * former stream aliases in Audio Service, defining volume curves attached to one or more diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index 65dc9afff0..4706d7d61e 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -36,7 +36,9 @@ LOCAL_HEADER_LIBRARIES := \ LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) -LOCAL_STATIC_LIBRARIES := libpfw_utility +LOCAL_STATIC_LIBRARIES := \ + libpfw_utility \ + libaudiopolicycomponents LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libpolicy-subsystem diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index ea98253adc..494a68ca4d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1084,7 +1084,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig, sanitizedRequestedPortId, *stream, mEngine->getProductStrategyForAttributes(resultAttr), - streamToVolumeSource(*stream), + toVolumeSource(resultAttr), *flags, isRequestedDeviceForExclusiveUse, std::move(weakSecondaryOutputDescs)); sp outputDesc = mOutputs.valueFor(*output); @@ -1694,8 +1694,9 @@ status_t AudioPolicyManager::startSource(const sp& outp setOutputDevices(outputDesc, devices, force, 0, NULL, requiresMuteCheck); // apply volume rules for current stream and device if necessary - checkAndSetVolume(stream, - getVolumeCurves(stream).getVolumeIndex(outputDesc->devices().types()), + auto &curves = getVolumeCurves(client->attributes()); + checkAndSetVolume(curves, client->volumeSource(), + curves.getVolumeIndex(outputDesc->devices().types()), outputDesc, outputDesc->devices().types()); @@ -2384,107 +2385,27 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { - auto &curves = getVolumeCurves(stream); - // VOICE_CALL and BLUETOOTH_SCO stream have minVolumeIndex > 0 but - // can be muted directly by an app that has MODIFY_PHONE_STATE permission. - if (((index < curves.getVolumeIndexMin()) && - !((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && - index == 0)) || - (index > curves.getVolumeIndexMax())) { - return BAD_VALUE; - } - if (!audio_is_output_device(device)) { + auto attributes = mEngine->getAttributesForStreamType(stream); + auto volumeGroup = mEngine->getVolumeGroupForStreamType(stream); + if (volumeGroup == VOLUME_GROUP_NONE) { + ALOGE("%s: no group matching with stream %s", __FUNCTION__, toString(stream).c_str()); return BAD_VALUE; } - - // Force max volume if stream cannot be muted - if (!curves.canBeMuted()) index = curves.getVolumeIndexMax(); - - ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d", - stream, device, index); - - // update other private stream volumes which follow this one - for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { - if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { - continue; - } - auto &curCurves = getVolumeCurves(static_cast(curStream)); - curCurves.addCurrentVolumeIndex(device, index); - } - - // update volume on all outputs and streams matching the following: - // - The requested stream (or a stream matching for volume control) is active on the output - // - The device (or devices) selected by the engine for this stream includes - // the requested device - // - For non default requested device, currently selected device on the output is either the - // requested device or one of the devices selected by the engine for this stream - // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if - // no specific device volume value exists for currently selected device. - status_t status = NO_ERROR; - for (size_t i = 0; i < mOutputs.size(); i++) { - sp desc = mOutputs.valueAt(i); - audio_devices_t curDevice = desc->devices().types(); - for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { - if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) { - continue; - } - if (!(desc->isActive(streamToVolumeSource((audio_stream_type_t)curStream)) || isInCall())) { - continue; - } - audio_devices_t curStreamDevice = Volume::getDeviceForVolume( - mEngine->getOutputDevicesForStream((audio_stream_type_t)curStream, - false /*fromCache*/).types()); - if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && - ((curStreamDevice & device) == 0)) { - continue; - } - bool applyVolume; - if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { - curStreamDevice |= device; - applyVolume = (Volume::getDeviceForVolume(curDevice) & curStreamDevice) != 0; - } else { - applyVolume = !curves.hasVolumeIndexForDevice(curStreamDevice); - } - // rescale index before applying to curStream as ranges may be different for - // stream and curStream - int idx = rescaleVolumeIndex(index, stream, (audio_stream_type_t)curStream); - if (applyVolume) { - //FIXME: workaround for truncated touch sounds - // delayed volume change for system stream to be removed when the problem is - // handled by system UI - status_t volStatus = checkAndSetVolume( - (audio_stream_type_t)curStream, idx, desc, curDevice, - (stream == AUDIO_STREAM_SYSTEM) ? - TOUCH_SOUND_FIXED_DELAY_MS : 0); - if (volStatus != NO_ERROR) { - status = volStatus; - } - } - } - } - return status; + ALOGV("%s: stream %s attributes=%s", __func__, + toString(stream).c_str(), toString(attributes).c_str()); + return setVolumeGroupIndex(getVolumeCurves(stream), volumeGroup, index, device, attributes); } status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, - int *index, - audio_devices_t device) + int *index, + audio_devices_t device) { - if (index == NULL) { - return BAD_VALUE; - } - if (!audio_is_output_device(device)) { - return BAD_VALUE; - } // if device is AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, return volume for device selected for this // stream by the engine. if (device == AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { device = mEngine->getOutputDevicesForStream(stream, true /*fromCache*/).types(); } - device = Volume::getDeviceForVolume(device); - - *index = getVolumeCurves(stream).getVolumeIndex(device); - ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); - return NO_ERROR; + return getVolumeIndex(getVolumeCurves(stream), *index, device); } status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr, @@ -2497,18 +2418,25 @@ status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_ ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str()); return BAD_VALUE; } - ALOGD("%s: FOUND group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str()); + ALOGV("%s: group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str()); return setVolumeGroupIndex(getVolumeCurves(attr), volumeGroup, index, device, attr); } status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group, int index, audio_devices_t device, - const audio_attributes_t /*attributes*/) + const audio_attributes_t attributes) { ALOGVV("%s: group=%d", __func__, group); status_t status = NO_ERROR; - setVolumeCurveIndex(group, index, device, curves); + VolumeSource vs = toVolumeSource(group); + product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes); + + status = setVolumeCurveIndex(index, device, curves); + if (status != NO_ERROR) { + ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device); + return status; + } // update volume on all outputs and streams matching the following: // - The requested stream (or a stream matching for volume control) is active on the output // - The device (or devices) selected by the engine for this stream includes @@ -2517,21 +2445,116 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g // requested device or one of the devices selected by the engine for this stream // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if // no specific device volume value exists for currently selected device. - // @TODO + for (size_t i = 0; i < mOutputs.size(); i++) { + sp desc = mOutputs.valueAt(i); + audio_devices_t curDevice = Volume::getDeviceForVolume(desc->devices().types()); + + // Inter / intra volume group priority management: Loop on strategies arranged by priority + // If a higher priority strategy is active, and the output is routed to a device with a + // HW Gain management, do not change the volume + bool applyVolume = false; + if (desc->useHwGain()) { + if (!(desc->isActive(group) || isInCall())) { + continue; + } + for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) { + auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy, + false /*preferredDevice*/); + if (activeClients.empty()) { + continue; + } + bool isPreempted = false; + bool isHigherPriority = productStrategy < strategy; + for (const auto &client : activeClients) { + if (isHigherPriority && (client->volumeSource() != vs)) { + ALOGV("%s: Strategy=%d (\nrequester:\n" + " group %d, volumeGroup=%d attributes=%s)\n" + " higher priority source active:\n" + " volumeGroup=%d attributes=%s) \n" + " on output %zu, bailing out", __func__, productStrategy, + group, group, toString(attributes).c_str(), + client->volumeSource(), toString(client->attributes()).c_str(), i); + applyVolume = false; + isPreempted = true; + break; + } + // However, continue for loop to ensure no higher prio clients running on output + if (client->volumeSource() == vs) { + applyVolume = true; + } + } + if (isPreempted || applyVolume) { + break; + } + } + if (!applyVolume) { + continue; // next output + } + status_t volStatus = checkAndSetVolume(curves, vs, index, desc, curDevice, + (vs == toVolumeSource(AUDIO_STREAM_SYSTEM)? + TOUCH_SOUND_FIXED_DELAY_MS : 0)); + if (volStatus != NO_ERROR) { + status = volStatus; + } + continue; + } + for (auto curVolGroup : getVolumeGroups()) { + VolumeSource curVolSrc = toVolumeSource(curVolGroup); + if (!(curVolSrc == vs || isInCall())) { + continue; + } + if (!(desc->isActive(vs) || isInCall())) { + continue; + } + audio_devices_t curSrcDevice; + auto &curCurves = getVolumeCurves(curVolSrc); + auto curCurvAttrs = curCurves.getAttributes(); + if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) { + auto attr = curCurvAttrs.front(); + curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types(); + } else if (!curCurves.getStreamTypes().empty()) { + auto stream = curCurves.getStreamTypes().front(); + curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types(); + } else { + ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, curVolSrc); + continue; + } + curSrcDevice = Volume::getDeviceForVolume(curSrcDevice); + if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) { + continue; + } + if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { + curSrcDevice |= device; + applyVolume = (curDevice & curSrcDevice) != 0; + } else { + applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice); + } + if (applyVolume) { + //FIXME: workaround for truncated touch sounds + // delayed volume change for system stream to be removed when the problem is + // handled by system UI + status_t volStatus = checkAndSetVolume( + curCurves, curVolSrc, index, desc, curDevice, + ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))? + TOUCH_SOUND_FIXED_DELAY_MS : 0)); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + } + } mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/); return status; } -status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup, - int index, +status_t AudioPolicyManager::setVolumeCurveIndex(int index, audio_devices_t device, IVolumeCurves &volumeCurves) { // VOICE_CALL stream has minVolumeIndex > 0 but can be muted directly by an // app that has MODIFY_PHONE_STATE permission. - // If voice is member of the volume group, it will contaminate all the member of this group - auto streams = mEngine->getStreamTypesForVolumeGroup(volumeGroup); - if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoiceStream(streams) && index == 0)) || + bool hasVoice = hasVoiceStream(volumeCurves.getStreamTypes()); + if (((index < volumeCurves.getVolumeIndexMin()) && !(hasVoice && index == 0)) || (index > volumeCurves.getVolumeIndexMax())) { ALOGD("%s: wrong index %d min=%d max=%d", __FUNCTION__, index, volumeCurves.getVolumeIndexMin(), volumeCurves.getVolumeIndexMax()); @@ -2544,7 +2567,7 @@ status_t AudioPolicyManager::setVolumeCurveIndex(volume_group_t volumeGroup, // Force max volume if stream cannot be muted if (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax(); - ALOGD("%s device %08x, index %d", __FUNCTION__ , device, index); + ALOGV("%s device %08x, index %d", __FUNCTION__ , device, index); volumeCurves.addCurrentVolumeIndex(device, index); return NO_ERROR; } @@ -2706,19 +2729,12 @@ status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { - bool active = false; - for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT && !active; curStream++) { - if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { - continue; - } - active = mOutputs.isActive(streamToVolumeSource((audio_stream_type_t)curStream), inPastMs); - } - return active; + return mOutputs.isActive(toVolumeSource(stream), inPastMs); } bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const { - return mOutputs.isActiveRemotely(streamToVolumeSource((audio_stream_type_t)stream), inPastMs); + return mOutputs.isActiveRemotely(toVolumeSource(stream), inPastMs); } bool AudioPolicyManager::isSourceActive(audio_source_t source) const @@ -3747,11 +3763,11 @@ status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *so struct audio_patch dummyPatch = {}; sp patchDesc = new AudioPatch(&dummyPatch, uid); - sp sourceDesc = new SourceClientDescriptor( - *portId, uid, *attributes, patchDesc, srcDevice, - mEngine->getStreamTypeForAttributes(*attributes), - mEngine->getProductStrategyForAttributes(*attributes), - streamToVolumeSource(mEngine->getStreamTypeForAttributes(*attributes))); + sp sourceDesc = + new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDevice, + mEngine->getStreamTypeForAttributes(*attributes), + mEngine->getProductStrategyForAttributes(*attributes), + toVolumeSource(*attributes)); status_t status = connectAudioSource(sourceDesc); if (status == NO_ERROR) { @@ -3916,7 +3932,7 @@ status_t AudioPolicyManager::getMasterMono(bool *mono) float AudioPolicyManager::getStreamVolumeDB( audio_stream_type_t stream, int index, audio_devices_t device) { - return computeVolume(stream, index, device); + return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index, device); } status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats, @@ -5292,11 +5308,10 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) { // mute/unmute AUDIO_STREAM_TTS on all outputs ALOGV("\t muting %d", mute); uint32_t maxLatency = 0; + auto ttsVolumeSource = toVolumeSource(AUDIO_STREAM_TTS); for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); - setStreamMute(AUDIO_STREAM_TTS, mute/*on*/, - desc, - 0 /*delay*/, AUDIO_DEVICE_NONE); + setVolumeSourceMute(ttsVolumeSource, mute/*on*/, desc, 0 /*delay*/, AUDIO_DEVICE_NONE); const uint32_t latency = desc->latency() * 2; if (latency > maxLatency) { maxLatency = latency; @@ -5380,15 +5395,12 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(const spisStrategyActive(productStrategy)) { - // make sure that we do not start the temporary mute period too early in case of - // delayed device change - setStrategyMute(productStrategy, true, outputDesc, delayMs); - setStrategyMute(productStrategy, false, outputDesc, delayMs + tempMuteDurationMs, + for (const auto &activeVs : outputDesc->getActiveVolumeSources()) { + // make sure that we do not start the temporary mute period too early in case of + // delayed device change + setVolumeSourceMute(activeVs, true, outputDesc, delayMs); + setVolumeSourceMute(activeVs, false, outputDesc, delayMs + tempMuteDurationMs, devices.types()); - } } } @@ -5612,51 +5624,51 @@ sp AudioPolicyManager::getInputProfile(const sp &de return NULL; } -float AudioPolicyManager::computeVolume(audio_stream_type_t stream, +float AudioPolicyManager::computeVolume(IVolumeCurves &curves, + VolumeSource volumeSource, int index, audio_devices_t device) { - auto &curves = getVolumeCurves(stream); float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index); // handle the case of accessibility active while a ringtone is playing: if the ringtone is much // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch // exploration of the dialer UI. In this situation, bring the accessibility volume closer to // the ringtone volume - if ((stream == AUDIO_STREAM_ACCESSIBILITY) - && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) - && isStreamActive(AUDIO_STREAM_RING, 0)) { - const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device); - return ringVolumeDB - 4 > volumeDb ? ringVolumeDB - 4 : volumeDb; + const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL); + const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING); + const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC); + const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM); + + if (volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY) + && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) && + mOutputs.isActive(ringVolumeSrc, 0)) { + auto &ringCurves = getVolumeCurves(AUDIO_STREAM_RING); + const float ringVolumeDb = computeVolume(ringCurves, ringVolumeSrc, index, device); + return ringVolumeDb - 4 > volumeDb ? ringVolumeDb - 4 : volumeDb; } // in-call: always cap volume by voice volume + some low headroom - if ((stream != AUDIO_STREAM_VOICE_CALL) && - (isInCall() || mOutputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL)))) { - switch (stream) { - case AUDIO_STREAM_SYSTEM: - case AUDIO_STREAM_RING: - case AUDIO_STREAM_MUSIC: - case AUDIO_STREAM_ALARM: - case AUDIO_STREAM_NOTIFICATION: - case AUDIO_STREAM_ENFORCED_AUDIBLE: - case AUDIO_STREAM_DTMF: - case AUDIO_STREAM_ACCESSIBILITY: { - int voiceVolumeIndex = getVolumeCurves(AUDIO_STREAM_VOICE_CALL).getVolumeIndex(device); - const float maxVoiceVolDb = - computeVolume(AUDIO_STREAM_VOICE_CALL, voiceVolumeIndex, device) + if ((volumeSource != callVolumeSrc && (isInCall() || + mOutputs.isActiveLocally(callVolumeSrc))) && + (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM) || + volumeSource == ringVolumeSrc || volumeSource == musicVolumeSrc || + volumeSource == alarmVolumeSrc || + volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION) || + volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) || + volumeSource == toVolumeSource(AUDIO_STREAM_DTMF) || + volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY))) { + auto &voiceCurves = getVolumeCurves(callVolumeSrc); + int voiceVolumeIndex = voiceCurves.getVolumeIndex(device); + const float maxVoiceVolDb = + computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; - if (volumeDb > maxVoiceVolDb) { - ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f", - stream, volumeDb, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb); - volumeDb = maxVoiceVolDb; - } - } break; - default: - break; + if (volumeDb > maxVoiceVolDb) { + ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__, + volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb); + volumeDb = maxVoiceVolDb; } } - // if a headset is connected, apply the following rules to ring tones and notifications // to avoid sound level bursts in user's ears: // - always attenuate notifications volume by 6dB @@ -5664,19 +5676,17 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, // speaker is part of the select devices // - if music is playing, always limit the volume to current music volume, // with a minimum threshold at -36dB so that notification is always perceived. - if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | - AUDIO_DEVICE_OUT_WIRED_HEADSET | - AUDIO_DEVICE_OUT_WIRED_HEADPHONE | - AUDIO_DEVICE_OUT_USB_HEADSET | - AUDIO_DEVICE_OUT_HEARING_AID)) && - ((stream == AUDIO_STREAM_ALARM || stream == AUDIO_STREAM_RING) - || (stream == AUDIO_STREAM_NOTIFICATION) - || (stream == AUDIO_STREAM_SYSTEM) - || ((stream == AUDIO_STREAM_ENFORCED_AUDIBLE) && - (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == - AUDIO_POLICY_FORCE_NONE))) && - getVolumeCurves(stream).canBeMuted()) { + if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_USB_HEADSET | AUDIO_DEVICE_OUT_HEARING_AID)) && + ((volumeSource == alarmVolumeSrc || + volumeSource == ringVolumeSrc) || + (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) || + (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) || + ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE)) && + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && + curves.canBeMuted()) { + // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was // just stopped @@ -5686,29 +5696,29 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, audio_devices_t musicDevice = mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, true /*fromCache*/).types(); - float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, - getVolumeCurves(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice), - musicDevice); - float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? - musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; - if (volumeDb > minVolDB) { - volumeDb = minVolDB; - ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB); + auto &musicCurves = getVolumeCurves(AUDIO_STREAM_MUSIC); + float musicVolDb = computeVolume(musicCurves, musicVolumeSrc, + musicCurves.getVolumeIndex(musicDevice), musicDevice); + float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? + musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB; + if (volumeDb > minVolDb) { + volumeDb = minVolDb; + ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDb, musicVolDb); } if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) { + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) { // on A2DP, also ensure notification volume is not too low compared to media when // intended to be played if ((volumeDb > -96.0f) && - (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) { - ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f", - stream, device, - volumeDb, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); - volumeDb = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; + (musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) { + ALOGV("%s increasing volume for volume source=%d device=0x%X from %f to %f", + __func__, volumeSource, device, volumeDb, + musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); + volumeDb = musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; } } } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) || - (stream != AUDIO_STREAM_ALARM && stream != AUDIO_STREAM_RING)) { + (!(volumeSource == alarmVolumeSrc || volumeSource == ringVolumeSrc))) { volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; } } @@ -5742,58 +5752,61 @@ int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc)); } -status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, +status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves, + VolumeSource volumeSource, int index, const sp& outputDesc, audio_devices_t device, int delayMs, bool force) { - // do not change actual stream volume if the stream is muted - if (outputDesc->isMuted(streamToVolumeSource(stream))) { - ALOGVV("%s() stream %d muted count %d", __func__, stream, outputDesc->getMuteCount(stream)); + // do not change actual attributes volume if the attributes is muted + if (outputDesc->isMuted(volumeSource)) { + ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource, + outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource)); return NO_ERROR; } + VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL); + VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO); + bool isVoiceVolSrc = callVolSrc == volumeSource; + bool isBtScoVolSrc = btScoVolSrc == volumeSource; + audio_policy_forced_cfg_t forceUseForComm = mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION); // do not change in call volume if bluetooth is connected and vice versa - if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) || - (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) { - ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", - stream, forceUseForComm); + // if sco and call follow same curves, bypass forceUseForComm + if ((callVolSrc != btScoVolSrc) && + ((isVoiceVolSrc && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) || + (isBtScoVolSrc && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO))) { + ALOGV("%s cannot set volume group %d volume with force use = %d for comm", __func__, + volumeSource, forceUseForComm); return INVALID_OPERATION; } - if (device == AUDIO_DEVICE_NONE) { device = outputDesc->devices().types(); } - float volumeDb = computeVolume(stream, index, device); + float volumeDb = computeVolume(curves, volumeSource, index, device); if (outputDesc->isFixedVolume(device) || // Force VoIP volume to max for bluetooth SCO - ((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) && - (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) { + ((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) { volumeDb = 0.0f; } + outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force); - outputDesc->setVolume(volumeDb, stream, device, delayMs, force); - - if (stream == AUDIO_STREAM_VOICE_CALL || - stream == AUDIO_STREAM_BLUETOOTH_SCO) { + if (isVoiceVolSrc || isBtScoVolSrc) { float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset - if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)getVolumeCurves(stream).getVolumeIndexMax(); + if (isVoiceVolSrc) { + voiceVolume = (float)index/(float)curves.getVolumeIndexMax(); } else { voiceVolume = 1.0; } - if (voiceVolume != mLastVoiceVolume) { mpClientInterface->setVoiceVolume(voiceVolume, delayMs); mLastVoiceVolume = voiceVolume; } } - return NO_ERROR; } @@ -5803,14 +5816,10 @@ void AudioPolicyManager::applyStreamVolumes(const sp& out bool force) { ALOGVV("applyStreamVolumes() for device %08x", device); - - for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) { - checkAndSetVolume((audio_stream_type_t)stream, - getVolumeCurves((audio_stream_type_t)stream).getVolumeIndex(device), - outputDesc, - device, - delayMs, - force); + for (const auto &volumeGroup : mEngine->getVolumeGroups()) { + auto &curves = getVolumeCurves(toVolumeSource(volumeGroup)); + checkAndSetVolume(curves, toVolumeSource(volumeGroup), + curves.getVolumeIndex(device), outputDesc, device, delayMs, force); } } @@ -5820,43 +5829,54 @@ void AudioPolicyManager::setStrategyMute(product_strategy_t strategy, int delayMs, audio_devices_t device) { - for (auto stream: mEngine->getStreamTypesForProductStrategy(strategy)) { - ALOGVV("%s() stream %d, mute %d, output ID %d", __FUNCTION__, stream, on, - outputDesc->getId()); - setStreamMute(stream, on, outputDesc, delayMs, device); + std::vector sourcesToMute; + for (auto attributes: mEngine->getAllAttributesForProductStrategy(strategy)) { + ALOGVV("%s() attributes %s, mute %d, output ID %d", __func__, + toString(attributes).c_str(), on, outputDesc->getId()); + VolumeSource source = toVolumeSource(attributes); + if (std::find(begin(sourcesToMute), end(sourcesToMute), source) == end(sourcesToMute)) { + sourcesToMute.push_back(source); + } } + for (auto source : sourcesToMute) { + setVolumeSourceMute(source, on, outputDesc, delayMs, device); + } + } -void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, - bool on, - const sp& outputDesc, - int delayMs, - audio_devices_t device) +void AudioPolicyManager::setVolumeSourceMute(VolumeSource volumeSource, + bool on, + const sp& outputDesc, + int delayMs, + audio_devices_t device, + bool activeOnly) { + if (activeOnly && !outputDesc->isActive(volumeSource)) { + return; + } if (device == AUDIO_DEVICE_NONE) { device = outputDesc->devices().types(); } - - ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x", - stream, on, outputDesc->getMuteCount(stream), device); - auto &curves = getVolumeCurves(stream); + auto &curves = getVolumeCurves(volumeSource); if (on) { - if (!outputDesc->isMuted(streamToVolumeSource(stream))) { + if (!outputDesc->isMuted(volumeSource)) { if (curves.canBeMuted() && - ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || - (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { - checkAndSetVolume(stream, 0, outputDesc, device, delayMs); + (volumeSource != toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) || + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == + AUDIO_POLICY_FORCE_NONE))) { + checkAndSetVolume(curves, volumeSource, 0, outputDesc, device, delayMs); } } - // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored - outputDesc->incMuteCount(streamToVolumeSource(stream)); + // increment mMuteCount after calling checkAndSetVolume() so that volume change is not + // ignored + outputDesc->incMuteCount(volumeSource); } else { - if (!outputDesc->isMuted(streamToVolumeSource(stream))) { - ALOGV("setStreamMute() unmuting non muted stream!"); + if (!outputDesc->isMuted(volumeSource)) { + ALOGV("%s unmuting non muted attributes!", __func__); return; } - if (outputDesc->decMuteCount(streamToVolumeSource(stream)) == 0) { - checkAndSetVolume(stream, + if (outputDesc->decMuteCount(volumeSource) == 0) { + checkAndSetVolume(curves, volumeSource, curves.getVolumeIndex(device), outputDesc, device, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 3a31e1e314..2b65f45c02 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -174,8 +174,7 @@ public: status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index, audio_devices_t device, const audio_attributes_t attributes); - status_t setVolumeCurveIndex(volume_group_t volumeGroup, - int index, + status_t setVolumeCurveIndex(int index, audio_devices_t device, IVolumeCurves &volumeCurves); @@ -358,6 +357,30 @@ protected: return mDefaultOutputDevice; } + std::vector getVolumeGroups() const + { + return mEngine->getVolumeGroups(); + } + + VolumeSource toVolumeSource(volume_group_t volumeGroup) const + { + return static_cast(volumeGroup); + } + VolumeSource toVolumeSource(const audio_attributes_t &attributes) const + { + return toVolumeSource(mEngine->getVolumeGroupForAttributes(attributes)); + } + VolumeSource toVolumeSource(audio_stream_type_t stream) const + { + return toVolumeSource(mEngine->getVolumeGroupForStreamType(stream)); + } + IVolumeCurves &getVolumeCurves(VolumeSource volumeSource) + { + auto *curves = mEngine->getVolumeCurvesForVolumeGroup( + static_cast(volumeSource)); + ALOG_ASSERT(curves != nullptr, "No curves for volume source %d", volumeSource); + return *curves; + } IVolumeCurves &getVolumeCurves(const audio_attributes_t &attr) { auto *curves = mEngine->getVolumeCurvesForAttributes(attr); @@ -395,7 +418,8 @@ protected: // compute the actual volume for a given stream according to the requested index and a particular // device - virtual float computeVolume(audio_stream_type_t stream, + virtual float computeVolume(IVolumeCurves &curves, + VolumeSource volumeSource, int index, audio_devices_t device); @@ -404,7 +428,8 @@ protected: audio_stream_type_t srcStream, audio_stream_type_t dstStream); // check that volume change is permitted, compute and send new volume to audio hardware - virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index, + virtual status_t checkAndSetVolume(IVolumeCurves &curves, + VolumeSource volumeSource, int index, const sp& outputDesc, audio_devices_t device, int delayMs = 0, bool force = false); @@ -428,12 +453,22 @@ protected: int delayMs = 0, audio_devices_t device = AUDIO_DEVICE_NONE); - // Mute or unmute the stream on the specified output - void setStreamMute(audio_stream_type_t stream, - bool on, - const sp& outputDesc, - int delayMs = 0, - audio_devices_t device = (audio_devices_t)0); + /** + * @brief setVolumeSourceMute Mute or unmute the volume source on the specified output + * @param volumeSource to be muted/unmute (may host legacy streams or by extension set of + * audio attributes) + * @param on true to mute, false to umute + * @param outputDesc on which the client following the volume group shall be muted/umuted + * @param delayMs + * @param device + * @param activeOnly if true, mute only if the volume group is active on the output. + */ + void setVolumeSourceMute(VolumeSource volumeSource, + bool on, + const sp& outputDesc, + int delayMs = 0, + audio_devices_t device = AUDIO_DEVICE_NONE, + bool activeOnly = false); audio_mode_t getPhoneState(); -- GitLab From bb0f557d3d0b7db071595f306555d715092dc6c5 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 11 Mar 2019 14:29:03 -0700 Subject: [PATCH 1105/1530] AudioPolicy: Fix UAF of a temporary DeviceVector AudioOutputDescriptor::devices() returns a DeviceVector by value. That means, the returned vector must be held as long as we need its elements. Added a FIXME to consider other devices because the current code looks suspicious. Bug: 128327759 Test: make Change-Id: Ifd3a7c8faf89b2c4fc3400aac0060d0e9e673e73 --- .../managerdefinitions/src/AudioOutputDescriptor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 1f3dccde2f..97b7a018c4 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -405,12 +405,12 @@ bool SwAudioOutputDescriptor::setVolume(float volumeDb, if (streams.empty()) { streams.push_back(AUDIO_STREAM_MUSIC); } - if (!devices().isEmpty()) { - // Assume first device to check upon Gain Crontroller availability + for (const auto& devicePort : devices()) { // APM loops on all group, so filter on active group to set the port gain, // let the other groups set the stream volume as per legacy - const auto &devicePort = devices().itemAt(0); - if (devicePort->hasGainController(true) && isActive(vs)) { + // TODO: Pass in the device address and check against it. + if (device == devicePort->type() && + devicePort->hasGainController(true) && isActive(vs)) { ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str()); // @todo: here we might be in trouble if the SwOutput has several active clients with // different Volume Source (or if we allow several curves within same volume group) -- GitLab From 9f685f48458a22ee9bd51be7f8c04a312db606ae Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Tue, 12 Mar 2019 11:22:46 -0700 Subject: [PATCH 1106/1530] Camera: Centralize owners, try two Use the correct include syntax Test: Builds Change-Id: I85fb81445ece687bc68819eadf9e42e474f14368 --- media/ndk/OWNERS | 2 +- services/camera/OWNERS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/ndk/OWNERS b/media/ndk/OWNERS index b015a9ba7a..9dc441e827 100644 --- a/media/ndk/OWNERS +++ b/media/ndk/OWNERS @@ -1,3 +1,3 @@ marcone@google.com # For AImage/AImageReader -include platform/frameworks/av/camera:/OWNERS +include platform/frameworks/av:/camera/OWNERS diff --git a/services/camera/OWNERS b/services/camera/OWNERS index f112576ef8..f48a95c5b3 100644 --- a/services/camera/OWNERS +++ b/services/camera/OWNERS @@ -1 +1 @@ -include platform/frameworks/av/camera:/OWNERS +include platform/frameworks/av:/camera/OWNERS -- GitLab From b2ba4859b9b1c0ff6332916f2826cd1c0c496ec2 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Sat, 9 Mar 2019 16:45:42 -0800 Subject: [PATCH 1107/1530] StagefrightRecorder:close next filedescriptor Bug: 127693962 StagefrightRecorder receives next file descriptor in it's function setNextOutputFile() from MediaRecorder.setNextOutputFile(). It duplicates the received file descriptor, but doesn't close the duplicated one when done. This CL fixes that issue by not duplicating it in StagefrighRecorder as it has to just pass it on to Mpeg4Writer. Mpeg4Writer duplicates received file descriptor. It frees them during release. Test: cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice Change-Id: I24fc12bd957a2abdb8785625261c95da62952736 --- media/libmediaplayerservice/StagefrightRecorder.cpp | 3 +-- media/libstagefright/MPEG4Writer.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index d1113136ce..51bd093a34 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -400,12 +400,11 @@ status_t StagefrightRecorder::setNextOutputFile(int fd) { // start with a clean, empty file ftruncate(fd, 0); - int nextFd = dup(fd); if (mWriter == NULL) { ALOGE("setNextOutputFile failed. Writer has been freed"); return INVALID_OPERATION; } - return mWriter->setNextFd(nextFd); + return mWriter->setNextFd(fd); } // Attempt to parse an float literal optionally surrounded by whitespace, diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 6259b15a27..72cdd05677 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -460,7 +460,7 @@ private: }; MPEG4Writer::MPEG4Writer(int fd) { - initInternal(fd, true /*isFirstSession*/); + initInternal(dup(fd), true /*isFirstSession*/); } MPEG4Writer::~MPEG4Writer() { @@ -481,7 +481,7 @@ MPEG4Writer::~MPEG4Writer() { void MPEG4Writer::initInternal(int fd, bool isFirstSession) { ALOGV("initInternal"); - mFd = dup(fd); + mFd = fd; mNextFd = -1; mInitCheck = mFd < 0? NO_INIT: OK; @@ -1044,6 +1044,10 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) { void MPEG4Writer::release() { close(mFd); mFd = -1; + if (mNextFd != -1) { + close(mNextFd); + mNextFd = -1; + } mInitCheck = NO_INIT; mStarted = false; free(mInMemoryCache); @@ -1981,7 +1985,7 @@ status_t MPEG4Writer::setNextFd(int fd) { // No need to set a new FD yet. return INVALID_OPERATION; } - mNextFd = fd; + mNextFd = dup(fd); return OK; } @@ -2180,11 +2184,11 @@ void MPEG4Writer::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatSwitch: { - finishCurrentSession(); mLock.lock(); int fd = mNextFd; mNextFd = -1; mLock.unlock(); + finishCurrentSession(); initInternal(fd, false /*isFirstSession*/); start(mStartMeta.get()); mSwitchPending = false; -- GitLab From 524b05802d10cf14a187f810da6d547d9dfabe14 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 12 Mar 2019 11:28:57 -0700 Subject: [PATCH 1108/1530] CCodec: don't update pipeline watcher if using input surface Bug: 127911646 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I0b562e518bc7991a6483fcbde8bfbc9f4774881d --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index fb6af9307e..9c30818b42 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -2593,9 +2593,9 @@ bool CCodecBufferChannel::handleWork( return false; } - if (work->worklets.size() != 1u + if (mInputSurface == nullptr && (work->worklets.size() != 1u || !work->worklets.front() - || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE)) { + || !(work->worklets.front()->output.flags & C2FrameData::FLAG_INCOMPLETE))) { mPipelineWatcher.lock()->onWorkDone(work->input.ordinal.frameIndex.peeku()); } -- GitLab From 7d602f00514e66528d4aa5d71b150ce7365d2859 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Wed, 13 Feb 2019 10:37:49 +0100 Subject: [PATCH 1109/1530] audiopolicy: engineconfigurable: prevents typo on strategy structure file This CL allows to generate at build time the ProductStrategy structure file to ensure it is aligned with audio policy engine configuration file. Bug: 124767636 Test: build Change-Id: I1afba9602482a898147c685d4a8b03fbe99c08b1 Signed-off-by: Francois Gaffie --- .../parameter-framework/examples/Android.mk | 21 ++- .../examples/Car/Android.mk | 13 +- .../Car/Structure/ProductStrategies.xml | 39 ----- .../examples/Phone/Android.mk | 13 +- .../{ => common}/Structure/PolicyClass.xml | 0 .../Structure/PolicySubsystem-CommonTypes.xml | 0 .../Structure/PolicySubsystem.xml | 0 .../Structure/ProductStrategies.xml.in} | 16 +- .../plugin/PolicySubsystem.cpp | 2 +- .../plugin/ProductStrategy.cpp | 3 +- .../engineconfigurable/tools/Android.bp | 43 +++++- .../tools/buildStrategiesStructureFile.py | 139 ++++++++++++++++++ .../tools/provision_strategies_structure.mk | 21 +++ 13 files changed, 222 insertions(+), 88 deletions(-) delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml rename services/audiopolicy/engineconfigurable/parameter-framework/examples/{ => common}/Structure/PolicyClass.xml (100%) rename services/audiopolicy/engineconfigurable/parameter-framework/examples/{ => common}/Structure/PolicySubsystem-CommonTypes.xml (100%) rename services/audiopolicy/engineconfigurable/parameter-framework/examples/{ => common}/Structure/PolicySubsystem.xml (100%) rename services/audiopolicy/engineconfigurable/parameter-framework/examples/{Phone/Structure/ProductStrategies.xml => common/Structure/ProductStrategies.xml.in} (57%) create mode 100755 services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py create mode 100644 services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 060830b78b..91b3744c43 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -20,6 +20,8 @@ PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk +PROVISION_STRATEGIES_STRUCTURE := $(TOOLS)/provision_strategies_structure.mk + endif ################################################################## @@ -57,7 +59,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) +LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) @@ -66,9 +68,22 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE) +LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := ProductStrategies.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_configuration.xml +AUDIO_POLICY_ENGINE_CONFIGURATION_FILE := \ + $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_configuration.xml +STRATEGIES_STRUCTURE_FILE := $(LOCAL_PATH)/common/Structure/$(LOCAL_MODULE).in + +include $(PROVISION_STRATEGIES_STRUCTURE) + endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) ########## Policy PFW Example Structures ######### @@ -86,7 +101,7 @@ LOCAL_REQUIRED_MODULES := \ libpolicy-subsystem \ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk index ea4a58f24c..53f53b4e94 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -31,7 +31,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_REQUIRED_MODULES := \ - ProductStrategies.xml.car \ + ProductStrategies.xml \ PolicySubsystem-Volume.xml \ PolicySubsystem-CommonTypes.xml \ libpolicy-subsystem @@ -40,17 +40,6 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := ProductStrategies.xml.car -LOCAL_MODULE_STEM := ProductStrategies.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) -include $(BUILD_PREBUILT) - ######### Policy PFW Settings ######### include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.car diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml deleted file mode 100644 index 53bba0361a..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/ProductStrategies.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk index e9d67e9119..880bb8611d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -31,7 +31,7 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_REQUIRED_MODULES := \ PolicySubsystem-CommonTypes.xml \ - ProductStrategies.xml.phone \ + ProductStrategies.xml \ PolicySubsystem-Volume.xml \ libpolicy-subsystem \ @@ -39,17 +39,6 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := ProductStrategies.xml.phone -LOCAL_MODULE_STEM := ProductStrategies.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) -include $(BUILD_PREBUILT) - ######### Policy PFW Settings ######### include $(CLEAR_VARS) LOCAL_MODULE := parameter-framework.policy.phone diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicyClass.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicyClass.xml similarity index 100% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicyClass.xml rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicyClass.xml diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml similarity index 100% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem-CommonTypes.xml rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml similarity index 100% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Structure/PolicySubsystem.xml rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/ProductStrategies.xml.in similarity index 57% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/ProductStrategies.xml.in index 4cbb3da197..2760b254c1 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/ProductStrategies.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/ProductStrategies.xml.in @@ -1,5 +1,5 @@ - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp index bfc1bca582..8bd7f66b04 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystem.cpp @@ -72,7 +72,7 @@ PolicySubsystem::PolicySubsystem(const std::string &name, core::log::Logger &log ); addSubsystemObjectFactory( new TSubsystemObjectFactory( - mProductStrategyComponentName, 0) + mProductStrategyComponentName, (1 << MappingKeyName)) ); } diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp index bb29ef108c..ebd945600d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.cpp @@ -32,6 +32,8 @@ ProductStrategy::ProductStrategy(const string &mappingValue, (MappingKeyAmendEnd - MappingKeyAmend1 + 1), context) { + std::string name(context.getItem(MappingKeyName)); + ALOG_ASSERT(instanceConfigurableElement != nullptr, "Invalid Configurable Element"); mPolicySubsystem = static_cast( instanceConfigurableElement->getBelongingSubsystem()); @@ -40,7 +42,6 @@ ProductStrategy::ProductStrategy(const string &mappingValue, mPolicyPluginInterface = mPolicySubsystem->getPolicyPluginInterface(); ALOG_ASSERT(mPolicyPluginInterface != nullptr, "Invalid Policy Plugin Interface"); - std::string name(instanceConfigurableElement->getName()); mId = mPolicyPluginInterface->getProductStrategyByName(name); ALOG_ASSERT(mId != PRODUCT_STRATEGY_INVALID, "Product Strategy %s not found", name.c_str()); diff --git a/services/audiopolicy/engineconfigurable/tools/Android.bp b/services/audiopolicy/engineconfigurable/tools/Android.bp index d8f29dca3d..8c16972ae4 100644 --- a/services/audiopolicy/engineconfigurable/tools/Android.bp +++ b/services/audiopolicy/engineconfigurable/tools/Android.bp @@ -12,13 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -python_binary_host { - name: "buildPolicyCriterionTypes.py", - owner: "renault", - main: "buildPolicyCriterionTypes.py", - srcs: [ - "buildPolicyCriterionTypes.py", - ], +python_defaults { + name: "tools_default", version: { py2: { enabled: true, @@ -29,3 +24,37 @@ python_binary_host { }, } +python_binary_host { + name: "buildPolicyCriterionTypes.py", + main: "buildPolicyCriterionTypes.py", + srcs: [ + "buildPolicyCriterionTypes.py", + ], + defaults: ["tools_default"], +} + +python_binary_host { + name: "domainGeneratorPolicy.py", + main: "domainGeneratorPolicy.py", + srcs: [ + "domainGeneratorPolicy.py", + ], + defaults: ["tools_default"], + libs: [ + "EddParser.py", + "hostConfig.py", + "PFWScriptGenerator.py", + ], + required: [ + "domainGeneratorConnector", + ], +} + +python_binary_host { + name: "buildStrategiesStructureFile.py", + main: "buildStrategiesStructureFile.py", + srcs: [ + "buildStrategiesStructureFile.py", + ], + defaults: ["tools_default"], +} diff --git a/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py new file mode 100755 index 0000000000..ee70b2613f --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py @@ -0,0 +1,139 @@ +#!/usr/bin/python + +# +# Copyright 2019, 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. +# + +import argparse +import re +import sys +import tempfile +import os +import logging +import subprocess +import xml.etree.ElementTree as ET +import xml.etree.ElementInclude as EI +import xml.dom.minidom as MINIDOM +from collections import OrderedDict + +# +# Helper script that helps to feed at build time the XML Product Strategies Structure file file used +# by the engineconfigurable to start the parameter-framework. +# It prevents to fill them manually and avoid divergences with android. +# +# The Product Strategies Structure file is fed from the audio policy engine configuration file +# in order to discover all the strategies available for the current platform. +# --audiopolicyengineconfigurationfile +# +# The reference file of ProductStrategies structure must also be set as an input of the script: +# --productstrategiesstructurefile +# +# At last, the output of the script shall be set also: +# --outputfile /etc/ProductStrategies.xml> +# + +def parseArgs(): + argparser = argparse.ArgumentParser(description="Parameter-Framework XML \ + product strategies structure file generator.\n\ + Exit with the number of (recoverable or not) error that occured.") + argparser.add_argument('--audiopolicyengineconfigurationfile', + help="Android Audio Policy Engine Configuration file, Mandatory.", + metavar="(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE)", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--productstrategiesstructurefile', + help="Product Strategies Structure XML base file, Mandatory.", + metavar="STRATEGIES_STRUCTURE_FILE", + type=argparse.FileType('r'), + required=True) + argparser.add_argument('--outputfile', + help="Product Strategies Structure output file, Mandatory.", + metavar="STRATEGIES_STRUCTURE_OUTPUT_FILE", + type=argparse.FileType('w'), + required=True) + argparser.add_argument('--verbose', + action='store_true') + + return argparser.parse_args() + + +def generateXmlStructureFile(strategies, strategyStructureInFile, outputFile): + + logging.info("Importing strategyStructureInFile {}".format(strategyStructureInFile)) + strategies_in_tree = ET.parse(strategyStructureInFile) + + strategies_root = strategies_in_tree.getroot() + strategy_components = strategies_root.find('ComponentType') + + for strategy_name in strategies: + context_mapping = "".join(map(str, ["Name:", strategy_name])) + strategy_pfw_name = strategy_name.replace('STRATEGY_', '').lower() + strategy_component_node = ET.SubElement(strategy_components, "Component", Name=strategy_pfw_name, Type="ProductStrategy", Mapping=context_mapping) + + xmlstr = ET.tostring(strategies_root, encoding='utf8', method='xml') + reparsed = MINIDOM.parseString(xmlstr) + prettyXmlStr = reparsed.toprettyxml(newl='\r\n') + prettyXmlStr = os.linesep.join([s for s in prettyXmlStr.splitlines() if s.strip()]) + outputFile.write(prettyXmlStr.encode('utf-8')) + +def capitalizeLine(line): + return ' '.join((w.capitalize() for w in line.split(' '))) + + +# +# Parse the audio policy configuration file and output a dictionary of device criteria addresses +# +def parseAndroidAudioPolicyEngineConfigurationFile(audiopolicyengineconfigurationfile): + + logging.info("Checking Audio Policy Engine Configuration file {}".format(audiopolicyengineconfigurationfile)) + # + # extract all product strategies name from audio policy engine configuration file + # + strategy_names = [] + + oldWorkingDir = os.getcwd() + print "Current working directory %s" % oldWorkingDir + + newDir = os.path.join(oldWorkingDir , audiopolicyengineconfigurationfile.name) + + policy_engine_in_tree = ET.parse(audiopolicyengineconfigurationfile) + os.chdir(os.path.dirname(os.path.normpath(newDir))) + + print "new working directory %s" % os.getcwd() + + policy_engine_root = policy_engine_in_tree.getroot() + EI.include(policy_engine_root) + + os.chdir(oldWorkingDir) + + for strategy in policy_engine_root.iter('ProductStrategy'): + strategy_names.append(strategy.get('name')) + + return strategy_names + + +def main(): + logging.root.setLevel(logging.INFO) + args = parseArgs() + + strategies = parseAndroidAudioPolicyEngineConfigurationFile(args.audiopolicyengineconfigurationfile) + + product_strategies_structure = args.productstrategiesstructurefile + + generateXmlStructureFile(strategies, product_strategies_structure, args.outputfile) + +# If this file is directly executed +if __name__ == "__main__": + exit(main()) diff --git a/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk b/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk new file mode 100644 index 0000000000..72c938c151 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk @@ -0,0 +1,21 @@ +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): MY_STRATEGIES_STRUCTURE_FILE := $(STRATEGIES_STRUCTURE_FILE) +$(LOCAL_BUILT_MODULE): MY_AUDIO_POLICY_ENGINE_CONFIGURATION_FILE := $(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE) +$(LOCAL_BUILT_MODULE): MY_PROVISION_TOOL := $(HOST_OUT)/bin/buildStrategiesStructureFile.py +$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) \ + buildStrategiesStructureFile.py \ + $(STRATEGIES_STRUCTURE_FILE) \ + $(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE) + + "$(MY_PROVISION_TOOL)" \ + --audiopolicyengineconfigurationfile "$(MY_AUDIO_POLICY_ENGINE_CONFIGURATION_FILE)" \ + --productstrategiesstructurefile "$(MY_STRATEGIES_STRUCTURE_FILE)" \ + --outputfile "$(@)" + +# Clear variables for further use +STRATEGIES_STRUCTURE_FILE := +AUDIO_POLICY_ENGINE_CONFIGURATION_FILE := -- GitLab From abd0b54cf0d670c19938c99ecfd2774eb6a3e618 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Wed, 13 Feb 2019 14:00:11 +0100 Subject: [PATCH 1110/1530] audiopolicy: engineconfigurable: fix configurations Bug: 124767636 Test: build Change-Id: I377a1343e8ffbaa0d0ea6eea3229e383a50a8cf6 Signed-off-by: Francois Gaffie --- services/audiopolicy/Android.mk | 4 - .../config/example/Android.mk | 25 +++--- .../audio_policy_engine_volumes.xml | 2 +- .../parameter-framework/examples/Android.mk | 25 ++++-- .../examples/Car/Android.mk | 21 +---- .../device_for_product_strategies.pfw | 43 +++++++++ .../examples/Phone/Android.mk | 25 +----- ... => device_for_product_strategy_patch.pfw} | 6 +- .../device_for_product_strategy_rerouting.pfw | 9 +- .../Phone/Structure/PolicySubsystem.xml | 88 ------------------- .../PolicySubsystem-no-strategy.xml} | 18 +--- .../common/Structure/PolicySubsystem.xml | 18 +++- .../enginedefault/config/example/Android.mk | 12 ++- 13 files changed, 107 insertions(+), 189 deletions(-) rename services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/{device_for_product_strategy_unknown.pfw => device_for_product_strategy_patch.pfw} (80%) delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml rename services/audiopolicy/engineconfigurable/parameter-framework/examples/{Car/Structure/PolicySubsystem.xml => common/Structure/PolicySubsystem-no-strategy.xml} (85%) diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 9e4eebc478..3badda1f44 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -62,10 +62,6 @@ ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) $(error Configurable policy does not support legacy conf file) endif #ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) -LOCAL_REQUIRED_MODULES := \ - parameter-framework.policy \ - audio_policy_criteria.conf \ - LOCAL_C_INCLUDES += frameworks/av/services/audiopolicy/engineconfigurable/include LOCAL_C_INCLUDES += frameworks/av/include diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index 45419f04b1..4fabd2068d 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -10,16 +10,15 @@ PROVISION_CRITERION_TYPES := $(TOOLS)/provision_criterion_types_from_android_hea ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_configuration_phone.xml -LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml +LOCAL_MODULE := audio_policy_engine_configuration.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_product_strategies.xml \ audio_policy_engine_stream_volumes.xml \ audio_policy_engine_default_stream_volumes.xml \ audio_policy_engine_criteria.xml \ @@ -28,12 +27,11 @@ LOCAL_REQUIRED_MODULES := \ include $(BUILD_PREBUILT) include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_product_strategies_phone.xml -LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE := audio_policy_engine_product_strategies.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) @@ -61,16 +59,14 @@ ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) # AUTOMOTIVE CONFIGURATION TOP FILE ################################################################## include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_configuration_automotive.xml -LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml - +LOCAL_MODULE := audio_policy_engine_configuration.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_automotive.xml \ + audio_policy_engine_product_strategies.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml \ audio_policy_engine_volumes.xml @@ -82,12 +78,11 @@ include $(BUILD_PREBUILT) ################################################################## include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_product_strategies_automotive.xml -LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE := audio_policy_engine_product_strategies.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := automotive/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml index b326b50de3..9ec3d77141 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml @@ -159,7 +159,7 @@ phone 1 40 - + 0,-4200 33,-2800 66,-1400 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 91b3744c43..954ce2fd2d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -53,6 +53,21 @@ include $(BUILD_PREBUILT) ########## Policy PFW Common Structures ######### +include $(CLEAR_VARS) +LOCAL_MODULE := PolicySubsystem.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem-CommonTypes.xml \ + ProductStrategies.xml \ + PolicySubsystem-Volume.xml \ + libpolicy-subsystem \ + +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy +LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + include $(CLEAR_VARS) LOCAL_MODULE := PolicySubsystem-CommonTypes.xml LOCAL_MODULE_TAGS := optional @@ -90,7 +105,7 @@ endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_ ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem.xml.common +LOCAL_MODULE := PolicySubsystem-no-strategy.xml LOCAL_MODULE_STEM := PolicySubsystem.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC @@ -110,7 +125,7 @@ endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable) include $(CLEAR_VARS) -LOCAL_MODULE := parameter-framework.policy.no-output +LOCAL_MODULE := parameter-framework.policy LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoOutputDevice.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true @@ -118,7 +133,7 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml \ - PolicySubsystem.xml.common \ + PolicySubsystem-no-strategy.xml \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml @@ -136,7 +151,7 @@ endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) include $(CLEAR_VARS) -LOCAL_MODULE := parameter-framework.policy.no-input +LOCAL_MODULE := parameter-framework.policy LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoInputDevice.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true @@ -144,7 +159,7 @@ LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy LOCAL_REQUIRED_MODULES := \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml \ - PolicySubsystem.xml.common \ + PolicySubsystem-no-strategy.xml \ PolicyClass.xml \ ParameterFrameworkConfigurationPolicy.xml diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk index 53f53b4e94..20ca8e2195 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -23,26 +23,9 @@ BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk ################################################################## ########## Policy PFW Structures ######### - -include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem.xml.car -LOCAL_MODULE_STEM := PolicySubsystem.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_REQUIRED_MODULES := \ - ProductStrategies.xml \ - PolicySubsystem-Volume.xml \ - PolicySubsystem-CommonTypes.xml \ - libpolicy-subsystem - -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) -include $(BUILD_PREBUILT) - ######### Policy PFW Settings ######### include $(CLEAR_VARS) -LOCAL_MODULE := parameter-framework.policy.car +LOCAL_MODULE := parameter-framework.policy LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true @@ -57,7 +40,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := \ $(PFW_EDD_FILES) LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml.car \ + PolicySubsystem.xml \ PolicyClass.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw index 196d82c625..57ad592852 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Settings/device_for_product_strategies.pfw @@ -714,4 +714,47 @@ supDomain: DeviceForProductStrategies speaker = 0 bus = 0 + supDomain: Tts + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/tts/device_address = BUS00_MEDIA + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + bus = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk index 880bb8611d..d1845b83f9 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -22,26 +22,9 @@ BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk # CONFIGURATION FILES ################################################################## ########## Policy PFW Structures ######### - -include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem.xml.phone -LOCAL_MODULE_STEM := PolicySubsystem.xml -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := ETC -LOCAL_VENDOR_MODULE := true -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem-CommonTypes.xml \ - ProductStrategies.xml \ - PolicySubsystem-Volume.xml \ - libpolicy-subsystem \ - -LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := Structure/$(LOCAL_MODULE_STEM) -include $(BUILD_PREBUILT) - ######### Policy PFW Settings ######### include $(CLEAR_VARS) -LOCAL_MODULE := parameter-framework.policy.phone +LOCAL_MODULE := parameter-framework.policy LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true @@ -57,15 +40,15 @@ PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_product_strategy_phone.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_sonification.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_sonification_respectful.pfw \ - $(LOCAL_PATH)/Settings/device_for_product_strategy_rerouting.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_transmitted_through_speaker.pfw \ - $(LOCAL_PATH)/Settings/device_for_product_strategy_unknown.pfw + $(LOCAL_PATH)/Settings/device_for_product_strategy_rerouting.pfw \ + $(LOCAL_PATH)/Settings/device_for_product_strategy_patch.pfw LOCAL_ADDITIONAL_DEPENDENCIES := \ $(PFW_EDD_FILES) LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml.phone \ + PolicySubsystem.xml \ PolicyClass.xml \ audio_policy_engine_criteria.xml \ audio_policy_engine_criterion_types.xml \ diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw similarity index 80% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw index c46cf568ac..d2cc090c56 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_unknown.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_patch.pfw @@ -1,8 +1,8 @@ supDomain: DeviceForProductStrategy - supDomain: Unknown + supDomain: Patch domain: UnreachableDevices conf: calibration - component: /Policy/policy/product_strategies/unknown/selected_output_devices/mask + component: /Policy/policy/product_strategies/patch/selected_output_devices/mask earpiece = 0 speaker = 0 wired_headset = 0 @@ -31,6 +31,6 @@ supDomain: DeviceForProductStrategy usb_headset = 0 bus = 0 stub = 0 - /Policy/policy/product_strategies/unknown/device_address = + /Policy/policy/product_strategies/patch/device_address = diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw index c064c18bcc..10f8814372 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Settings/device_for_product_strategy_rerouting.pfw @@ -29,15 +29,8 @@ supDomain: DeviceForProductStrategy ip = 0 proxy = 0 usb_headset = 0 + bus = 0 stub = 0 /Policy/policy/product_strategies/rerouting/device_address = - domain: SelectedDevice - conf: Bus - component: /Policy/policy/product_strategies/rerouting/selected_output_devices/mask - bus = 1 - - conf: Default - component: /Policy/policy/product_strategies/rerouting/selected_output_devices/mask - bus = 0 diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml deleted file mode 100644 index b55ce2c58d..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Structure/PolicySubsystem.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml similarity index 85% rename from services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml rename to services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml index b55ce2c58d..a4e7537318 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-no-strategy.xml @@ -1,18 +1,4 @@ - - - @@ -78,11 +62,11 @@ + - diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml index a4e7537318..b55ce2c58d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem.xml @@ -1,4 +1,18 @@ + + + @@ -62,11 +78,11 @@ - + diff --git a/services/audiopolicy/enginedefault/config/example/Android.mk b/services/audiopolicy/enginedefault/config/example/Android.mk index f06ee4cf13..0badac8cdf 100644 --- a/services/audiopolicy/enginedefault/config/example/Android.mk +++ b/services/audiopolicy/enginedefault/config/example/Android.mk @@ -7,28 +7,26 @@ LOCAL_PATH := $(call my-dir) ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_default) include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_configuration_phone.xml -LOCAL_MODULE_STEM := audio_policy_engine_configuration.xml +LOCAL_MODULE := audio_policy_engine_configuration.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_product_strategies_phone.xml \ + audio_policy_engine_product_strategies.xml \ audio_policy_engine_stream_volumes.xml \ audio_policy_engine_default_stream_volumes.xml include $(BUILD_PREBUILT) include $(CLEAR_VARS) -LOCAL_MODULE := audio_policy_engine_product_strategies_phone.xml -LOCAL_MODULE_STEM := audio_policy_engine_product_strategies.xml +LOCAL_MODULE := audio_policy_engine_product_strategies.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_SRC_FILES := phone/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := phone/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) -- GitLab From 946d4b15bef5b7e16b1f36fe8f1436be13e256ec Mon Sep 17 00:00:00 2001 From: David Brageul Date: Wed, 13 Feb 2019 14:07:56 +0100 Subject: [PATCH 1111/1530] audiopolicy: engineconfigurable: add configuration for caremu Bug: 124767636 Test: build Change-Id: Ib540a78559b51ac18ccf62043e743fe068b41ac0 Signed-off-by: Francois Gaffie --- .../config/example/Android.mk | 34 +- ...audio_policy_engine_product_strategies.xml | 170 +++++ .../caremu/audio_policy_engine_volumes.xml | 192 +++++ .../parameter-framework/examples/Android.mk | 6 +- .../examples/CarEmu/Android.mk | 58 ++ .../device_for_product_strategies.pfw | 690 ++++++++++++++++++ 6 files changed, 1144 insertions(+), 6 deletions(-) create mode 100644 services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml create mode 100644 services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index 4fabd2068d..37271b511f 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -53,7 +53,7 @@ include $(BUILD_PREBUILT) endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), phone_configurable) -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),automotive_configurable caremu_configurable)) ################################################################## # AUTOMOTIVE CONFIGURATION TOP FILE @@ -73,6 +73,10 @@ LOCAL_REQUIRED_MODULES := \ include $(BUILD_PREBUILT) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),automotive_configurable caremu_configurable)) + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) + ################################################################## # CONFIGURATION FILES ################################################################## @@ -95,7 +99,31 @@ include $(BUILD_PREBUILT) endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), automotive_configurable) -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), caremu_configurable) + +################################################################## +# CONFIGURATION FILES +################################################################## + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_product_strategies.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := caremu/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_policy_engine_volumes.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_SRC_FILES := caremu/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), caremu_configurable) + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable)) include $(CLEAR_VARS) LOCAL_MODULE := audio_policy_engine_criteria.xml @@ -118,4 +146,4 @@ CRITERION_TYPES_FILE := $(LOCAL_PATH)/common/$(LOCAL_MODULE).in include $(PROVISION_CRITERION_TYPES) -endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable)) diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml new file mode 100644 index 0000000000..c487da9e24 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml new file mode 100644 index 0000000000..9ec3d77141 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml @@ -0,0 +1,192 @@ + + + + + + + + + oem_traffic_anouncement + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + + oem_adas_2 + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + oem_adas_3 + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + + + media_car_audio_type_3 + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + media_car_audio_type_7 + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + media + 0 + 40 + + 0,-2400 + 33,-1600 + 66,-800 + 100,0 + + + + + speech + 1 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + system + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + phone + 1 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + ring + 0 + 40 + + 0,-4200 + 33,-2800 + 66,-1400 + 100,0 + + + + + tts + 0 + 15 + + 0,-0 + 100,0 + + + + diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 954ce2fd2d..782fe83d8a 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -9,7 +9,7 @@ LOCAL_PATH := $(call my-dir) -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable no-output_configurable no-input_configurable)) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable no-output_configurable no-input_configurable)) PFW_CORE := external/parameter-framework #@TODO: upstream new domain generator @@ -29,7 +29,7 @@ endif ################################################################## ######### Policy PFW top level file ######### -ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable)) include $(CLEAR_VARS) LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml @@ -99,7 +99,7 @@ STRATEGIES_STRUCTURE_FILE := $(LOCAL_PATH)/common/Structure/$(LOCAL_MODULE).in include $(PROVISION_STRATEGIES_STRUCTURE) -endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable)) +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable)) ########## Policy PFW Example Structures ######### ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk new file mode 100644 index 0000000000..8fa8f0a5b2 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk @@ -0,0 +1,58 @@ +################################################################################################ +# +# @NOTE: +# Audio Policy Engine configurable example for generic device build +# +# Any vendor shall have its own configuration within the corresponding device folder +# +################################################################################################ + +ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), caremu_configurable) +LOCAL_PATH := $(call my-dir) + +PFW_CORE := external/parameter-framework +PFW_DEFAULT_SCHEMAS_DIR := $(PFW_CORE)/upstream/schemas +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools +BUILD_PFW_SETTINGS := $(TOOLS)/build_audio_pfw_settings.mk + + +################################################################## +# CONFIGURATION FILES +################################################################## + +########## Policy PFW Structures ######### +######### Policy PFW Settings ######### +include $(CLEAR_VARS) +LOCAL_MODULE := parameter-framework.policy +LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy + +PFW_EDD_FILES := \ + $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ + $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ + $(LOCAL_PATH)/../Settings/volumes.pfw + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(PFW_EDD_FILES) + +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem.xml \ + PolicyClass.xml \ + audio_policy_engine_criteria.xml \ + audio_policy_engine_criterion_types.xml \ + ParameterFrameworkConfigurationPolicy.xml + +PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml +PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml + +PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml + +PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) + +include $(BUILD_PFW_SETTINGS) + +endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION), caremu_configurable) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw new file mode 100644 index 0000000000..ca3464f58d --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Settings/device_for_product_strategies.pfw @@ -0,0 +1,690 @@ +supDomain: DeviceForProductStrategies + supDomain: OemTrafficAnouncement + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_traffic_anouncement/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_traffic_anouncement/selected_output_devices/mask + bus = 0 + + supDomain: OemStrategy1 + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_strategy_1/device_address = BUS02_OEM1 + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS02_OEM1 + + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_strategy_1/selected_output_devices/mask + bus = 0 + + + + supDomain: OemStrategy2 + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/oem_strategy_2/device_address = BUS01_NAV_GUIDANCE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS01_NAV_GUIDANCE + + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/oem_strategy_2/selected_output_devices/mask + bus = 0 + + + + supDomain: Radio + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/radio/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/radio/selected_output_devices/mask + bus = 0 + + supDomain: ExtAudioSource + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/ext_audio_source/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/ext_audio_source/selected_output_devices/mask + bus = 0 + + + + supDomain: VoiceCommand + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/voice_command/device_address = BUS04_VOICE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS04_VOICE + + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/voice_command/selected_output_devices/mask + bus = 0 + + + supDomain: SafetyAlert + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/safety_alert/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/safety_alert/selected_output_devices/mask + bus = 0 + + + supDomain: Music + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/music/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/music/selected_output_devices/mask + bus = 0 + + + + supDomain: NavGuidance + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/nav_guidance/device_address = BUS01_NAV_GUIDANCE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS01_NAV_GUIDANCE + + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/nav_guidance/selected_output_devices/mask + bus = 0 + + + supDomain: VoiceCall + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/voice_call/device_address = BUS04_VOICE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS04_VOICE + + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/voice_call/selected_output_devices/mask + bus = 0 + + + supDomain: Alarm + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/alarm/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/alarm/selected_output_devices/mask + bus = 0 + + + supDomain: Ring + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/ring/device_address = BUS04_VOICE + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS04_VOICE + + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/ring/selected_output_devices/mask + bus = 0 + + + supDomain: Notification + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/notification/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/notification/selected_output_devices/mask + bus = 0 + + + supDomain: System + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/system/device_address = BUS03_SYSTEM_SOUND + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS03_SYSTEM_SOUND + + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/system/selected_output_devices/mask + bus = 0 + + supDomain: Tts + domain: UnreachableDevices + conf: calibration + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + earpiece = 0 + speaker = 0 + wired_headset = 0 + wired_headphone = 0 + bluetooth_sco = 0 + bluetooth_sco_headset = 0 + bluetooth_sco_carkit = 0 + bluetooth_a2dp = 0 + bluetooth_a2dp_headphones = 0 + bluetooth_a2dp_speaker = 0 + hdmi = 0 + angl_dock_headset = 0 + dgtl_dock_headset = 0 + usb_accessory = 0 + usb_device = 0 + remote_submix = 0 + telephony_tx = 0 + line = 0 + hdmi_arc = 0 + spdif = 0 + fm = 0 + aux_line = 0 + speaker_safe = 0 + ip = 0 + proxy = 0 + usb_headset = 0 + stub = 0 + /Policy/policy/product_strategies/tts/device_address = BUS00_MEDIA + + domain: SelectedDevice + conf: Bus + AvailableOutputDevices Includes Bus + AvailableOutputDevicesAddresses Includes BUS00_MEDIA + + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + bus = 1 + + conf: Default + component: /Policy/policy/product_strategies/tts/selected_output_devices/mask + bus = 0 -- GitLab From 1631f06feb36df5406ad00e850dcca9394f67772 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 12 Mar 2019 19:39:03 -0700 Subject: [PATCH 1112/1530] AudioFlinger: Prevent multiple effect chains with same sessionId Allow at most one effect chain with same sessionId on mPlaybackThreads. Test: poc, CTS effect tests Bug: 123237974 Change-Id: Ide46cd23b0a9f4295f0dca2fea23379a76b836ee --- services/audioflinger/AudioFlinger.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 213c9c3115..cd09de6404 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3311,6 +3311,21 @@ sp AudioFlinger::createEffect( io = mPlaybackThreads.keyAt(0); } ALOGV("createEffect() got io %d for effect %s", io, desc.name); + } else if (checkPlaybackThread_l(io) != nullptr) { + // allow only one effect chain per sessionId on mPlaybackThreads. + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i); + if (io == checkIo) continue; + const uint32_t sessionType = + mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); + if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) { + ALOGE("%s: effect %s io %d denied because session %d effect exists on io %d", + __func__, desc.name, (int)io, (int)sessionId, (int)checkIo); + android_errorWriteLog(0x534e4554, "123237974"); + lStatus = BAD_VALUE; + goto Exit; + } + } } ThreadBase *thread = checkRecordThread_l(io); if (thread == NULL) { -- GitLab From 14c10ebbf4d05c5dc8ca08371410437f47077bde Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Sat, 9 Mar 2019 13:33:41 -0800 Subject: [PATCH 1113/1530] StagefrighRecorder:check mWriter before using fd ftruncate(fd) would be useless if mWriter was NULL already. Hence moved mWriter nullptr check before ftruncate(fd). Bug: 128366317 Test: atest CtsMediaTestCases:android.media.cts.MediaRecorderTest Change-Id: I46b8c4718bc8a90712b7ec0b54d8239106106018 --- media/libmediaplayerservice/StagefrightRecorder.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 624f596c14..77777b8712 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -400,12 +400,14 @@ status_t StagefrightRecorder::setNextOutputFile(int fd) { return -EBADF; } - // start with a clean, empty file - ftruncate(fd, 0); - if (mWriter == NULL) { + if (mWriter == nullptr) { ALOGE("setNextOutputFile failed. Writer has been freed"); return INVALID_OPERATION; } + + // start with a clean, empty file + ftruncate(fd, 0); + return mWriter->setNextFd(fd); } -- GitLab From 6096dcd538ef55fa2f121260956bdc6ed508b07a Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 13 Mar 2019 13:20:30 -0700 Subject: [PATCH 1114/1530] AudioFlinger: createEffect - check for proper sessionId Output error messages on failure. Test: CTS effects, dedicated test with bad sessionId Bug: 128528126 Change-Id: Icdd306c30e8eb818b0a27d6463fa785bac2fb707 --- services/audioflinger/AudioFlinger.cpp | 50 ++++++++++++++++---------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index cd09de6404..6bfeeb155f 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3208,22 +3208,42 @@ sp AudioFlinger::createEffect( goto Exit; } - // check audio settings permission for global effects - if (sessionId == AUDIO_SESSION_OUTPUT_MIX && !settingsAllowed()) { - lStatus = PERMISSION_DENIED; + if (mEffectsFactoryHal == 0) { + ALOGE("%s: no effects factory hal", __func__); + lStatus = NO_INIT; goto Exit; } - // Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects - // that can only be created by audio policy manager - if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && !isAudioServerUid(callingUid)) { - lStatus = PERMISSION_DENIED; - goto Exit; - } + // check audio settings permission for global effects + if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { + if (!settingsAllowed()) { + ALOGE("%s: no permission for AUDIO_SESSION_OUTPUT_MIX", __func__); + lStatus = PERMISSION_DENIED; + goto Exit; + } + } else if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { + if (!isAudioServerUid(callingUid)) { + ALOGE("%s: only APM can create using AUDIO_SESSION_OUTPUT_STAGE", __func__); + lStatus = PERMISSION_DENIED; + goto Exit; + } - if (mEffectsFactoryHal == 0) { - lStatus = NO_INIT; - goto Exit; + if (io == AUDIO_IO_HANDLE_NONE) { + ALOGE("%s: APM must specify output when using AUDIO_SESSION_OUTPUT_STAGE", __func__); + lStatus = BAD_VALUE; + goto Exit; + } + } else { + // general sessionId. + + if (audio_unique_id_get_use(sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) { + ALOGE("%s: invalid sessionId %d", __func__, sessionId); + lStatus = BAD_VALUE; + goto Exit; + } + + // TODO: should we check if the callingUid (limited to pid) is in mAudioSessionRefs + // to prevent creating an effect when one doesn't actually have track with that session? } { @@ -3271,12 +3291,6 @@ sp AudioFlinger::createEffect( // because of code checking output when entering the function. // Note: io is never 0 when creating an effect on an input if (io == AUDIO_IO_HANDLE_NONE) { - if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { - // output must be specified by AudioPolicyManager when using session - // AUDIO_SESSION_OUTPUT_STAGE - lStatus = BAD_VALUE; - goto Exit; - } // look for the thread where the specified audio session is present for (size_t i = 0; i < mPlaybackThreads.size(); i++) { uint32_t sessionType = mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); -- GitLab From caa77efe666dcea0799828627a3887e896de5e16 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 13 Mar 2019 14:13:44 -0700 Subject: [PATCH 1115/1530] Mark WAV extractor for single buffers Change WAV extractor so it doesn't try to generate multiple buffers to return more per readMultiple() transaction. Working around an apparent buffer management problem when trying to do more than 1 buffer. Bug: 128249893 Test: play wav file before/after --- media/extractors/wav/WAVExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 5679de8dad..020951bcdf 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -81,7 +81,7 @@ struct WAVSource : public MediaTrackHelper { virtual media_status_t read( MediaBufferHelper **buffer, const ReadOptions *options = NULL); - bool supportsNonBlockingRead() override { return true; } + bool supportsNonBlockingRead() override { return false; } protected: virtual ~WAVSource(); -- GitLab From f963b2bfdaf406b42d371322402172b4380bbba5 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 12 Mar 2019 19:39:03 -0700 Subject: [PATCH 1116/1530] AudioFlinger: Prevent multiple effect chains with same sessionId Allow at most one effect chain with same sessionId on mPlaybackThreads. Test: poc, CTS effect tests Bug: 123237974 Merged-In: Ide46cd23b0a9f4295f0dca2fea23379a76b836ee Change-Id: Ide46cd23b0a9f4295f0dca2fea23379a76b836ee (cherry picked from commit 1631f06feb36df5406ad00e850dcca9394f67772) --- services/audioflinger/AudioFlinger.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 153023e52f..f5c136052d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2751,6 +2751,21 @@ sp AudioFlinger::createEffect( io = mPlaybackThreads.keyAt(0); } ALOGV("createEffect() got io %d for effect %s", io, desc.name); + } else if (checkPlaybackThread_l(io) != nullptr) { + // allow only one effect chain per sessionId on mPlaybackThreads. + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i); + if (io == checkIo) continue; + const uint32_t sessionType = + mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); + if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) { + ALOGE("%s: effect %s io %d denied because session %d effect exists on io %d", + __func__, desc.name, (int)io, (int)sessionId, (int)checkIo); + android_errorWriteLog(0x534e4554, "123237974"); + lStatus = BAD_VALUE; + goto Exit; + } + } } ThreadBase *thread = checkRecordThread_l(io); if (thread == NULL) { -- GitLab From 5fa1276d36862e95d8893ec2e1f627b8b59ef1f9 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 13 Mar 2019 15:01:58 -0700 Subject: [PATCH 1117/1530] Add RAW_DEPTH private format and allow AImageReader creation with it. Bug: 128451427 Test: AImageReaderWindowHandleTest Change-Id: Ieb1e51afe2cd9f8c6c1eb55aae98969af5ed83e2 Signed-off-by: Jayant Chowdhary --- media/ndk/NdkImageReader.cpp | 3 ++ media/ndk/include/private/media/NdkImage.h | 30 +++++++++++++++++++ .../tests/AImageReaderWindowHandleTest.cpp | 22 ++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 media/ndk/include/private/media/NdkImage.h diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index bcc7ff3da8..22e15d3f33 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -21,6 +21,7 @@ #include "NdkImagePriv.h" #include "NdkImageReaderPriv.h" +#include #include #include @@ -63,6 +64,7 @@ AImageReader::isSupportedFormatAndUsage(int32_t format, uint64_t usage) { case AIMAGE_FORMAT_YUV_420_888: case AIMAGE_FORMAT_JPEG: case AIMAGE_FORMAT_RAW16: + case AIMAGE_FORMAT_RAW_DEPTH: case AIMAGE_FORMAT_RAW_PRIVATE: case AIMAGE_FORMAT_RAW10: case AIMAGE_FORMAT_RAW12: @@ -92,6 +94,7 @@ AImageReader::getNumPlanesForFormat(int32_t format) { case AIMAGE_FORMAT_RGBA_FP16: case AIMAGE_FORMAT_JPEG: case AIMAGE_FORMAT_RAW16: + case AIMAGE_FORMAT_RAW_DEPTH: case AIMAGE_FORMAT_RAW_PRIVATE: case AIMAGE_FORMAT_RAW10: case AIMAGE_FORMAT_RAW12: diff --git a/media/ndk/include/private/media/NdkImage.h b/media/ndk/include/private/media/NdkImage.h new file mode 100644 index 0000000000..4368a5663e --- /dev/null +++ b/media/ndk/include/private/media/NdkImage.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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 _PRIVATE_MEDIA_NDKIMAGE_H_ +#define _PRIVATE_MEDIA_NDKIMAGE_H_ +// Formats not listed in the public API, but still available to AImageReader +enum AIMAGE_PRIVATE_FORMATS { + /** + * Unprocessed implementation-dependent raw + * depth measurements, opaque with 16 bit + * samples. + * + */ + + AIMAGE_FORMAT_RAW_DEPTH = 0x1002, +}; +#endif // _PRIVATE_MEDIA_NDKIMAGE diff --git a/media/ndk/tests/AImageReaderWindowHandleTest.cpp b/media/ndk/tests/AImageReaderWindowHandleTest.cpp index ef0ff675a5..5f112529f6 100644 --- a/media/ndk/tests/AImageReaderWindowHandleTest.cpp +++ b/media/ndk/tests/AImageReaderWindowHandleTest.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -182,4 +183,25 @@ TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) { EXPECT_TRUE(imageAvailable_) << "Timed out waiting for image data to be handled!\n"; } +class AImageReaderPrivateFormatTest : public ::testing::Test { + public: + void SetUp() override { + auto status = AImageReader_new(kImageWidth, kImageHeight, AIMAGE_FORMAT_RAW_DEPTH, + kMaxImages, &imgReader); + EXPECT_TRUE(status == AMEDIA_OK); + } + + void TearDown() override { + if (imgReader) { + AImageReader_delete(imgReader); + } + } + AImageReader *imgReader = nullptr; +}; + +TEST_F(AImageReaderPrivateFormatTest, CreateTest) { + EXPECT_TRUE(imgReader != nullptr); +} + + } // namespace android -- GitLab From 13850be2d899889d2f594db5296787ac6f3da333 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 14 Mar 2019 11:33:09 -0700 Subject: [PATCH 1118/1530] AudioFlinger: Do not process MMap effects under thread lock Not required and do not assume it. Test: no regression with atest CtsNativeMediaAAudioTestCases Bug: 128615806 Change-Id: I29d77d01aad417cc6aa6f882b7bc0812775e13c6 --- services/audioflinger/Threads.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 49f74a2660..dfba70026e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -8747,9 +8747,11 @@ bool AudioFlinger::MmapThread::threadLoop() while (!exitPending()) { - Mutex::Autolock _l(mLock); Vector< sp > effectChains; + { // under Thread lock + Mutex::Autolock _l(mLock); + if (mSignalPending) { // A signal was raised while we were unlocked mSignalPending = false; @@ -8784,10 +8786,13 @@ bool AudioFlinger::MmapThread::threadLoop() updateMetadata_l(); lockEffectChains_l(effectChains); + } // release Thread lock + for (size_t i = 0; i < effectChains.size(); i ++) { - effectChains[i]->process_l(); + effectChains[i]->process_l(); // Thread is not locked, but effect chain is locked } - // enable changes in effect chain + + // enable changes in effect chain, including moving to another thread. unlockEffectChains(effectChains); // Effect chains will be actually deleted here if they were removed from // mEffectChains list during mixing or effects processing -- GitLab From 4f5d6fd75075a30bb387e8a15b118922cc188bd5 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 14 Mar 2019 09:38:22 -0700 Subject: [PATCH 1119/1530] clean up compilation warnings change from a blanket 'using namespace...' directive to a more explicit qualification on the entities that prompted adding the using in the first place. Bug: 128606338 Test: compilation Change-Id: Id8c8cfcd18801661b82b73d1c59952d1e75d3c7d --- services/mediaanalytics/MediaAnalyticsService.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp index 4f3ac1ba8d..06baac962d 100644 --- a/services/mediaanalytics/MediaAnalyticsService.cpp +++ b/services/mediaanalytics/MediaAnalyticsService.cpp @@ -76,9 +76,6 @@ namespace android { - using namespace android::base; - using namespace android::content::pm; - // individual records kept in memory: age or count // age: <= 28 hours (1 1/6 days) // count: hard limit of # records @@ -649,7 +646,8 @@ void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool } if (binder != NULL) { - sp package_mgr = interface_cast(binder); + sp package_mgr = + interface_cast(binder); binder::Status status; std::vector uids; -- GitLab From 4c1f95845160320a689d388598a11f2aeb067d78 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 13 Mar 2019 15:15:53 +0900 Subject: [PATCH 1120/1530] Fix thumbnail hvcc key in ItemTable In HEIF files the hvcc for main image and the thumbnail are separate. Add AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC for thumbnail hvcc, and set it in ItemTable. test: - thumbnail test from the bug; - manual testing of HEIF image decoding of various files; - cts MediaMetadataRetriverTest. bug: 128494513 Change-Id: I219a86cf362f641d546236dda78e8559ffcfa8aa --- media/extractors/mp4/ItemTable.cpp | 3 ++- media/libstagefright/Utils.cpp | 1 + media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + media/ndk/libmediandk.map.txt | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index a72e589b59..d56abaa274 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -1540,7 +1540,8 @@ AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) { AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height); AMediaFormat_setBuffer(meta, - AMEDIAFORMAT_KEY_CSD_HEVC, thumbnail.hvcc->data(), thumbnail.hvcc->size()); + AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC, + thumbnail.hvcc->data(), thumbnail.hvcc->size()); ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd", imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex); } else { diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 16b3319c21..64bbf08b8e 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -642,6 +642,7 @@ static std::vector> bufferMappings { { "icc-profile", kKeyIccProfile }, { "sei", kKeySEI }, { "text-format-data", kKeyTextFormatData }, + { "thumbnail-csd-hevc", kKeyThumbnailHVCC }, } }; diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index 768a7a9adc..ed88cf349a 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -371,6 +371,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id"; EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema"; EXPORT const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA = "text-format-data"; +EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC = "thumbnail-csd-hevc"; EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT = "thumbnail-height"; EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME = "thumbnail-time"; EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH = "thumbnail-width"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 56bcaabe1a..259481dc3c 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -226,6 +226,7 @@ extern const char* AMEDIAFORMAT_KEY_SAR_WIDTH __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TARGET_TIME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 9756926e41..4725e9ec26 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -135,6 +135,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID; # var introduced=28 AMEDIAFORMAT_KEY_TEMPORAL_LAYERING; # var introduced=28 AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA; # var introduced=29 + AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC; # var introduced=29 AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT; # var introduced=29 AMEDIAFORMAT_KEY_THUMBNAIL_TIME; # var introduced=29 AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH; # var introduced=29 -- GitLab From e778c4254d3018553ff245448aa9d750c5628699 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 14 Mar 2019 15:04:30 -0700 Subject: [PATCH 1121/1530] AudioFlinger: createEffect always look for same session effect chain. Test: CTS AudioEffect tests Bug: 128630419 Change-Id: Ieb08abfcb7a983e05fa8f25f6609468d9bd4620b --- services/audioflinger/AudioFlinger.cpp | 25 +++---------------------- services/audioflinger/AudioFlinger.h | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index cd09de6404..f7dbbc057a 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3278,31 +3278,12 @@ sp AudioFlinger::createEffect( goto Exit; } // look for the thread where the specified audio session is present - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - uint32_t sessionType = mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); - if (sessionType != 0) { - io = mPlaybackThreads.keyAt(i); - // thread with same effect session is preferable - if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) { - break; - } - } - } + io = findIoHandleBySessionId_l(sessionId, mPlaybackThreads); if (io == AUDIO_IO_HANDLE_NONE) { - for (size_t i = 0; i < mRecordThreads.size(); i++) { - if (mRecordThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { - io = mRecordThreads.keyAt(i); - break; - } - } + io = findIoHandleBySessionId_l(sessionId, mRecordThreads); } if (io == AUDIO_IO_HANDLE_NONE) { - for (size_t i = 0; i < mMmapThreads.size(); i++) { - if (mMmapThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { - io = mMmapThreads.keyAt(i); - break; - } - } + io = findIoHandleBySessionId_l(sessionId, mMmapThreads); } // If no output thread contains the requested session ID, default to // first output. The effect chain will be moved to the correct output diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 1441e15f19..ec5dfb177d 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -558,6 +558,26 @@ using effect_buffer_t = int16_t; #include "PatchPanel.h" + // Find io handle by session id. + // Preference is given to an io handle with a matching effect chain to session id. + // If none found, AUDIO_IO_HANDLE_NONE is returned. + template + static audio_io_handle_t findIoHandleBySessionId_l( + audio_session_t sessionId, const T& threads) { + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE; + + for (size_t i = 0; i < threads.size(); i++) { + const uint32_t sessionType = threads.valueAt(i)->hasAudioSession(sessionId); + if (sessionType != 0) { + io = threads.keyAt(i); + if ((sessionType & AudioFlinger::ThreadBase::EFFECT_SESSION) != 0) { + break; // effect chain here. + } + } + } + return io; + } + // server side of the client's IAudioTrack class TrackHandle : public android::BnAudioTrack { public: -- GitLab From b4946b6d3719cde8187b106f7aad2d6be2b6dadb Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 14 Mar 2019 11:19:28 -0700 Subject: [PATCH 1122/1530] AudioFlinger: Close MMap threads in destructor For aesthetic symmetry. Test: compiles Change-Id: I5876409cf0ebf0c1945def19bcfd455d87dd4542 --- services/audioflinger/AudioFlinger.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index cd09de6404..e13e7aa06f 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -224,6 +224,14 @@ AudioFlinger::~AudioFlinger() // closeOutput_nonvirtual() will remove specified entry from mPlaybackThreads closeOutput_nonvirtual(mPlaybackThreads.keyAt(0)); } + while (!mMmapThreads.isEmpty()) { + const audio_io_handle_t io = mMmapThreads.keyAt(0); + if (mMmapThreads.valueAt(0)->isOutput()) { + closeOutput_nonvirtual(io); // removes entry from mMmapThreads + } else { + closeInput_nonvirtual(io); // removes entry from mMmapThreads + } + } for (size_t i = 0; i < mAudioHwDevs.size(); i++) { // no mHardwareLock needed, as there are no other references to this -- GitLab From c3d62f9950ad0cb402082f19ea856d13986c289b Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 14 Mar 2019 13:38:51 -0700 Subject: [PATCH 1123/1530] Do not count invalid or terminated tracks for audio session purposes. Test: CTS audio effect tests. SoloTester. Bug: 128623061 Change-Id: I3f8d34d82aec14fe6ac9496c0e02726d94acdf85 --- services/audioflinger/Threads.cpp | 65 ------------------------------- services/audioflinger/Threads.h | 34 ++++++++++++++-- services/audioflinger/TrackBase.h | 11 ++---- 3 files changed, 34 insertions(+), 76 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 49f74a2660..968f8a402e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2788,28 +2788,6 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui } } -// hasAudioSession_l() must be called with ThreadBase::mLock held -uint32_t AudioFlinger::PlaybackThread::hasAudioSession_l(audio_session_t sessionId) const -{ - uint32_t result = 0; - if (getEffectChain_l(sessionId) != 0) { - result = EFFECT_SESSION; - } - - for (size_t i = 0; i < mTracks.size(); ++i) { - sp track = mTracks[i]; - if (sessionId == track->sessionId() && !track->isInvalid()) { - result |= TRACK_SESSION; - if (track->isFastTrack()) { - result |= FAST_SESSION; - } - break; - } - } - - return result; -} - uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(audio_session_t sessionId) { // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that @@ -8219,27 +8197,6 @@ uint32_t AudioFlinger::RecordThread::getInputFramesLost() return 0; } -// hasAudioSession_l() must be called with ThreadBase::mLock held -uint32_t AudioFlinger::RecordThread::hasAudioSession_l(audio_session_t sessionId) const -{ - uint32_t result = 0; - if (getEffectChain_l(sessionId) != 0) { - result = EFFECT_SESSION; - } - - for (size_t i = 0; i < mTracks.size(); ++i) { - if (sessionId == mTracks[i]->sessionId()) { - result |= TRACK_SESSION; - if (mTracks[i]->isFastTrack()) { - result |= FAST_SESSION; - } - break; - } - } - - return result; -} - KeyedVector AudioFlinger::RecordThread::sessionIds() const { KeyedVector ids; @@ -9048,28 +9005,6 @@ size_t AudioFlinger::MmapThread::removeEffectChain_l(const sp& chai return mEffectChains.size(); } -// hasAudioSession_l() must be called with ThreadBase::mLock held -uint32_t AudioFlinger::MmapThread::hasAudioSession_l(audio_session_t sessionId) const -{ - uint32_t result = 0; - if (getEffectChain_l(sessionId) != 0) { - result = EFFECT_SESSION; - } - - for (size_t i = 0; i < mActiveTracks.size(); i++) { - sp track = mActiveTracks[i]; - if (sessionId == track->sessionId()) { - result |= TRACK_SESSION; - if (track->isFastTrack()) { - result |= FAST_SESSION; - } - break; - } - } - - return result; -} - void AudioFlinger::MmapThread::threadLoop_standby() { mHalStream->standby(); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 97aa9f044a..47e580bf8c 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -356,6 +356,27 @@ public: return hasAudioSession_l(sessionId); } + template + uint32_t hasAudioSession_l(audio_session_t sessionId, const T& tracks) const { + uint32_t result = 0; + if (getEffectChain_l(sessionId) != 0) { + result = EFFECT_SESSION; + } + for (size_t i = 0; i < tracks.size(); ++i) { + const sp& track = tracks[i]; + if (sessionId == track->sessionId() + && !track->isInvalid() // not yet removed from tracks. + && !track->isTerminated()) { + result |= TRACK_SESSION; + if (track->isFastTrack()) { + result |= FAST_SESSION; // caution, only represents first track. + } + break; + } + } + return result; + } + // the value returned by default implementation is not important as the // strategy is only meaningful for PlaybackThread which implements this method virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused) @@ -810,7 +831,9 @@ public: virtual status_t addEffectChain_l(const sp& chain); virtual size_t removeEffectChain_l(const sp& chain); - virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const; + uint32_t hasAudioSession_l(audio_session_t sessionId) const override { + return ThreadBase::hasAudioSession_l(sessionId, mTracks); + } virtual uint32_t getStrategyForSession_l(audio_session_t sessionId); @@ -1550,7 +1573,9 @@ public: virtual status_t addEffectChain_l(const sp& chain); virtual size_t removeEffectChain_l(const sp& chain); - virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const; + uint32_t hasAudioSession_l(audio_session_t sessionId) const override { + return ThreadBase::hasAudioSession_l(sessionId, mTracks); + } // Return the set of unique session IDs across all tracks. // The keys are the session IDs, and the associated values are meaningless. @@ -1725,7 +1750,10 @@ class MmapThread : public ThreadBase virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc, audio_session_t sessionId); - virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const; + uint32_t hasAudioSession_l(audio_session_t sessionId) const override { + // Note: using mActiveTracks as no mTracks here. + return ThreadBase::hasAudioSession_l(sessionId, mActiveTracks); + } virtual status_t setSyncEvent(const sp& event); virtual bool isValidSyncEvent(const sp& event) const; diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index e23173fd0a..4402d99314 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -94,6 +94,9 @@ public: virtual void invalidate() { mIsInvalid = true; } bool isInvalid() const { return mIsInvalid; } + void terminate() { mTerminated = true; } + bool isTerminated() const { return mTerminated; } + audio_attributes_t attributes() const { return mAttr; } #ifdef TEE_SINK @@ -228,14 +231,6 @@ protected: return mState == STOPPING_2; } - bool isTerminated() const { - return mTerminated; - } - - void terminate() { - mTerminated = true; - } - // Upper case characters are final states. // Lower case characters are transitory. const char *getTrackStateString() const { -- GitLab From 5945746bcabff8d833229a6c230cbe873474087f Mon Sep 17 00:00:00 2001 From: Weiyin Jiang Date: Fri, 27 Apr 2018 00:39:29 +0800 Subject: [PATCH 1124/1530] audio: ensure effect chain with specific session id is unique It's possible that tracks with the same session id running on various playback outputs, which causes effect chain being created on the same session twice. As a result, the same effect engine will be released twice as the same context is reused. Output that has effect chain with same session id is more preferable. Test: No regression with Play Music and Effects Bug: 123082420 Bug: 123237974 Merged-In: I690ea3cb942d1fdc96b46048e271557d48000f43 Change-Id: I690ea3cb942d1fdc96b46048e271557d48000f43 (cherry picked from commit 9aeb1770d49bab13ea5c6454c969a713641fe686) --- services/audioflinger/AudioFlinger.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 153023e52f..bacbdedb8c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2731,9 +2731,13 @@ sp AudioFlinger::createEffect( } // look for the thread where the specified audio session is present for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { + uint32_t sessionType = mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId); + if (sessionType != 0) { io = mPlaybackThreads.keyAt(i); - break; + // thread with same effect session is preferable + if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) { + break; + } } } if (io == 0) { -- GitLab From 2a1a52ce489357d58f03c4e3455187ea70fa68ef Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 14 Mar 2019 16:57:47 -0700 Subject: [PATCH 1125/1530] Fix addAudioDeviceCallback() memory leak bug Store jobjects and JObjectHolders as backup, delete JObjectHolders after usage Bug: 128341809 Test: RoutingTest#test_MediaPlayer2_RoutingChangedCallback Change-Id: I70b2312519ab0efef0ed40ce721ae8c26dc1aed2 --- media/libmediaplayer2/JAudioTrack.cpp | 23 ++++++++++--------- .../MediaPlayer2AudioOutput.cpp | 17 ++++++++------ .../include/mediaplayer2/JAudioTrack.h | 9 +++++--- .../mediaplayer2/MediaPlayer2AudioOutput.h | 4 +++- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp index 910edff99f..fab6c649fc 100644 --- a/media/libmediaplayer2/JAudioTrack.cpp +++ b/media/libmediaplayer2/JAudioTrack.cpp @@ -571,10 +571,9 @@ status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) { } void JAudioTrack::registerRoutingDelegates( - Vector>& routingDelegates) { - for (Vector>::iterator it = routingDelegates.begin(); - it != routingDelegates.end(); it++) { - addAudioDeviceCallback(it->second, getHandler(it->second)); + Vector, sp>>& routingDelegates) { + for (auto it = routingDelegates.begin(); it != routingDelegates.end(); it++) { + addAudioDeviceCallback(it->second->getJObject(), getHandler(it->second->getJObject())); } } @@ -597,20 +596,22 @@ jobject JAudioTrack::getHandler(const jobject routingDelegateObj) { return env->CallObjectMethod(routingDelegateObj, jGetHandler); } -jobject JAudioTrack::findByKey(Vector>& mp, const jobject key) { +jobject JAudioTrack::findByKey( + Vector, sp>>& mp, const jobject key) { JNIEnv *env = JavaVMHelper::getJNIEnv(); - for (Vector>::iterator it = mp.begin(); it != mp.end(); it++) { - if (env->IsSameObject(it->first, key)) { - return it->second; + for (auto it = mp.begin(); it != mp.end(); it++) { + if (env->IsSameObject(it->first->getJObject(), key)) { + return it->second->getJObject(); } } return nullptr; } -void JAudioTrack::eraseByKey(Vector>& mp, const jobject key) { +void JAudioTrack::eraseByKey( + Vector, sp>>& mp, const jobject key) { JNIEnv *env = JavaVMHelper::getJNIEnv(); - for (Vector>::iterator it = mp.begin(); it != mp.end(); it++) { - if (env->IsSameObject(it->first, key)) { + for (auto it = mp.begin(); it != mp.end(); it++) { + if (env->IsSameObject(it->first->getJObject(), key)) { mp.erase(it); return; } diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp index 7c3063d72c..b4fa0c1cf5 100644 --- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp +++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp @@ -521,15 +521,18 @@ jobject MediaPlayer2AudioOutput::getRoutedDevice() { status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) { ALOGV("addAudioDeviceCallback"); Mutex::Autolock lock(mLock); - jobject listener = (new JObjectHolder( - JAudioTrack::getListener(jRoutingDelegate)))->getJObject(); + jobject listener = JAudioTrack::getListener(jRoutingDelegate); if (JAudioTrack::findByKey(mRoutingDelegates, listener) == nullptr) { - jobject handler = (new JObjectHolder( - JAudioTrack::getHandler(jRoutingDelegate)))->getJObject(); - jobject routingDelegate = (new JObjectHolder(jRoutingDelegate))->getJObject(); - mRoutingDelegates.push_back(std::pair(listener, routingDelegate)); + sp listenerHolder = new JObjectHolder(listener); + jobject handler = JAudioTrack::getHandler(jRoutingDelegate); + sp routingDelegateHolder = new JObjectHolder(jRoutingDelegate); + + mRoutingDelegates.push_back(std::pair, sp>( + listenerHolder, routingDelegateHolder)); + if (mJAudioTrack != nullptr) { - return mJAudioTrack->addAudioDeviceCallback(routingDelegate, handler); + return mJAudioTrack->addAudioDeviceCallback( + routingDelegateHolder->getJObject(), handler); } } return NO_ERROR; diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h index 7381286d8c..2ed46322c7 100644 --- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h +++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h @@ -405,7 +405,8 @@ public: * routingDelegates: backed-up routing delegates * */ - void registerRoutingDelegates(Vector>& routingDelegates); + void registerRoutingDelegates( + Vector, sp>>& routingDelegates); /* get listener from RoutingDelegate object */ @@ -422,13 +423,15 @@ public: * Returns value if key is in the map * nullptr if key is not in the map */ - static jobject findByKey(Vector>& mp, const jobject key); + static jobject findByKey( + Vector, sp>>& mp, const jobject key); /* * Parameters: * map and key */ - static void eraseByKey(Vector>& mp, const jobject key); + static void eraseByKey( + Vector, sp>>& mp, const jobject key); private: audio_output_flags_t mFlags; diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h index 1b3f2dc5dc..f38b7cc2c3 100644 --- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h +++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h @@ -124,7 +124,9 @@ private: audio_output_flags_t mFlags; sp mPreferredDevice; mutable Mutex mLock; - Vector> mRoutingDelegates; // + + // + Vector, sp>> mRoutingDelegates; // static variables below not protected by mutex static bool mIsOnEmulator; -- GitLab From 9447c68921904a3cb05faf1fed358a7d1d78313c Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Mon, 11 Mar 2019 19:35:25 +0000 Subject: [PATCH 1126/1530] Switch to use aicu/AIcu.h Switch to using a dynamically-linked library function to initialize ICU, not a statically-linked function. This means the knowledge of file paths stays in the runtime module. Bug: 120493361 Test: build/boot Change-Id: Ibe1d5ffd50e5cce76b1b01788954dcfb4762b1c6 --- media/mediaserver/Android.bp | 1 - media/mediaserver/main_mediaserver.cpp | 4 ++-- services/mediaextractor/Android.mk | 1 - services/mediaextractor/main_extractorservice.cpp | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp index 8377723849..6a1cc71333 100644 --- a/media/mediaserver/Android.bp +++ b/media/mediaserver/Android.bp @@ -25,7 +25,6 @@ cc_binary { ], static_libs: [ - "libicuandroid_utils", "libregistermsext", ], diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index ecddc48272..7b22b05b60 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "mediaserver" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -25,7 +26,6 @@ #include "RegisterExtensions.h" // from LOCAL_C_INCLUDES -#include "IcuUtils.h" #include "MediaPlayerService.h" #include "ResourceManagerService.h" @@ -38,7 +38,7 @@ int main(int argc __unused, char **argv __unused) sp proc(ProcessState::self()); sp sm(defaultServiceManager()); ALOGI("ServiceManager: %p", sm.get()); - InitializeIcuOrDie(); + AIcu_initializeIcuOrDie(); MediaPlayerService::instantiate(); ResourceManagerService::instantiate(); registerExtensions(); diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index 661a475afc..fd34d5b1ba 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -22,7 +22,6 @@ LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy LOCAL_SRC_FILES := main_extractorservice.cpp LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ liblog libandroidicu libavservices_minijail -LOCAL_STATIC_LIBRARIES := libicuandroid_utils LOCAL_MODULE:= mediaextractor LOCAL_INIT_RC := mediaextractor.rc LOCAL_C_INCLUDES := frameworks/av/media/libmedia diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp index 3c15bfdffc..06b532df15 100644 --- a/services/mediaextractor/main_extractorservice.cpp +++ b/services/mediaextractor/main_extractorservice.cpp @@ -15,6 +15,7 @@ ** limitations under the License. */ +#include #include #include #include @@ -29,7 +30,6 @@ #include // from LOCAL_C_INCLUDES -#include "IcuUtils.h" #include "MediaExtractorService.h" #include "MediaUtils.h" #include "minijail.h" @@ -64,7 +64,7 @@ int main(int argc __unused, char** argv) SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath); - InitializeIcuOrDie(); + AIcu_initializeIcuOrDie(); strcpy(argv[0], "media.extractor"); sp proc(ProcessState::self()); -- GitLab From 635ebee7b8e9529178ba7cb8b651e32b76c6c9ad Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Tue, 12 Mar 2019 18:05:35 -0700 Subject: [PATCH 1127/1530] Camera: Add low latency snapshot use case id Additionally initializey the stream id to invalid negative value in case the Hal tries to verify it during stream combination queries. Bug: 128450197 Test: Camera CTS Change-Id: Ife058e22ef72ee84be82799ed397ca49cd8ea99f --- .../include/camera/NdkCameraMetadataTags.h | 32 +++++++++++++------ .../api2/CameraDeviceClient.cpp | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index c26ca69efb..acf699925c 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7762,7 +7762,9 @@ typedef enum acamera_metadata_enum_acamera_scaler_cropping_type { typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_configurations { /** *

    Preview must only include non-stalling processed stream configurations with - * output formats like YUV_420_888, IMPLEMENTATION_DEFINED, etc.

    + * output formats like + * {@link AIMAGE_FORMAT_YUV_420_888 }, + * {@link AIMAGE_FORMAT_PRIVATE }, etc.

    */ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PREVIEW = 0x0, @@ -7777,19 +7779,20 @@ typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_c /** *

    Video snapshot must include stream configurations at least as big as - * the maximum RECORD resolutions and only with format BLOB + DATASPACE_JFIF - * format/dataspace combination (JPEG). Additionally the configurations shouldn't cause - * preview glitches and also be able to run at 30 fps.

    + * the maximum RECORD resolutions and only with + * {@link AIMAGE_FORMAT_JPEG JPEG output format}. + * Additionally the configurations shouldn't cause preview glitches and also be able to + * run at 30 fps.

    */ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VIDEO_SNAPSHOT = 0x2, /** *

    Recommended snapshot stream configurations must include at least one with - * size close to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE with BLOB + DATASPACE_JFIF - * format/dataspace combination (JPEG). Taking into account restrictions on aspect - * ratio, alignment etc. the area of the maximum suggested size shouldn’t be less than - * 97% of the sensor array size area.

    + * size close to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE and + * {@link AIMAGE_FORMAT_JPEG JPEG output format}. + * Taking into account restrictions on aspect ratio, alignment etc. the area of the + * maximum suggested size shouldn’t be less than 97% of the sensor array size area.

    * * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE */ @@ -7808,9 +7811,20 @@ typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_c */ ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RAW = 0x5, - ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END + /** + *

    If supported, the recommended low latency stream configurations must have + * end-to-end latency that does not exceed 200 ms. under standard operating conditions + * (reasonable light levels, not loaded system) and using template + * TEMPLATE_STILL_CAPTURE. This is primarily for listing configurations for the + * {@link AIMAGE_FORMAT_JPEG JPEG output format} + * however other supported output formats can be added as well.

    + */ + ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_LOW_LATENCY_SNAPSHOT = 0x6, + ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END + = 0x7, + /** *

    Vendor defined use cases. These depend on the vendor implementation.

    */ diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index b512f2b6b1..c7a4f2bfad 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -604,6 +604,7 @@ void CameraDeviceClient::mapStreamInfo(const OutputStreamInfo &streamInfo, stream->v3_2.usage = Camera3Device::mapToConsumerUsage(u); stream->v3_2.dataSpace = Camera3Device::mapToHidlDataspace(streamInfo.dataSpace); stream->v3_2.rotation = Camera3Device::mapToStreamRotation(rotation); + stream->v3_2.id = -1; // Invalid stream id stream->physicalCameraId = std::string(physicalId.string()); stream->bufferSize = 0; } -- GitLab From aee3f31e1fde37a5b3510a41296f78e0db7802c6 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 15 Mar 2019 15:27:28 -0700 Subject: [PATCH 1128/1530] actually convert between codec2 and mediacodecinfo for av1 had the tables, didn't have the mapping function. Added the mapping function and plugged it into the "for this type, apply this mapping" routine. Bug: 128552878 Test: y --- media/codec2/sfplugin/utils/Codec2Mapper.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index 6da131fd45..d62944a843 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -589,6 +589,21 @@ private: bool mIsHdr10Plus; }; +struct Av1ProfileLevelMapper : ProfileLevelMapperHelper { + virtual bool simpleMap(C2Config::level_t from, int32_t *to) { + return sAv1Levels.map(from, to); + } + virtual bool simpleMap(int32_t from, C2Config::level_t *to) { + return sAv1Levels.map(from, to); + } + virtual bool simpleMap(C2Config::profile_t from, int32_t *to) { + return sAv1Profiles.map(from, to); + } + virtual bool simpleMap(int32_t from, C2Config::profile_t *to) { + return sAv1Profiles.map(from, to); + } +}; + } // namespace // static @@ -613,6 +628,8 @@ C2Mapper::GetProfileLevelMapper(std::string mediaType) { return std::make_shared(); } else if (mediaType == MIMETYPE_VIDEO_VP9) { return std::make_shared(); + } else if (mediaType == MIMETYPE_VIDEO_AV1) { + return std::make_shared(); } return nullptr; } -- GitLab From f97d64cd3a1588b22b47dd3805be291525bb17d9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Sat, 16 Mar 2019 10:23:46 -0700 Subject: [PATCH 1129/1530] audio policy: fix call volume Fix setGroupVolumeIndex() which was applying volume for all groups if in call and causing the wrong index to be used. when DTMF are muted when entering call, voice call was also muted. Bug: 128499415 Test: call with voice volume set to 6 Change-Id: Ie1296a7a48fdfba3710acb3d7a7e4f00e7b576a5 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 4a0e764ed7..5574e1ce8d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2503,7 +2503,7 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g } for (auto curVolGroup : getVolumeGroups()) { VolumeSource curVolSrc = toVolumeSource(curVolGroup); - if (!(curVolSrc == vs || isInCall())) { + if (curVolSrc != vs) { continue; } if (!(desc->isActive(vs) || isInCall())) { -- GitLab From 720ad3f97bdd4bbc405444f3652b44641dc313ff Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Sat, 16 Mar 2019 23:54:50 -0700 Subject: [PATCH 1130/1530] M3UParser: Update codec list Bug: 118506355 Test: http://146.148.91.8/medialinks/medialinks.html Change-Id: I1b37b8cfd53f3d02ba9234c346b6d1b99e8e54c8 --- media/libstagefright/httplive/M3UParser.cpp | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index b2361b8387..cb97a3cc68 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -1397,6 +1397,18 @@ bool M3UParser::codecIsType(const AString &codec, const char *type) { case 'ulaw': case 'vdva': case 'ac-4': + case 'Opus': + case 'a3ds': + case 'dts+': + case 'dts-': + case 'dtsx': + case 'dtsy': + case 'ec+3': + case 'mha1': + case 'mha2': + case 'mhm1': + case 'mhm2': + case 'sevs': return !strcmp("audio", type); case 'avc1': @@ -1445,6 +1457,35 @@ bool M3UParser::codecIsType(const AString &codec, const char *type) { case 'tga ': case 'tiff': case 'WRLE': + case 'a3d1': + case 'a3d2': + case 'a3d3': + case 'a3d4': + case 'avc3': + case 'avc4': + case 'dva1': + case 'dvav': + case 'dvh1': + case 'dvhe': + case 'hev1': + case 'hev2': + case 'hvc1': + case 'hvc2': + case 'hvt1': + case 'lhe1': + case 'lht1': + case 'lhv1': + case 'mjpg': + case 'mvc3': + case 'mvc4': + case 'mvd1': + case 'mvd2': + case 'mvd3': + case 'mvd4': + case 'rv60': + case 'svc2': + case 'vp08': + case 'vp09': return !strcmp("video", type); default: -- GitLab From 82022c1db816f715db4af7b01a1f251e975a42bb Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 18 Mar 2019 11:53:26 -0700 Subject: [PATCH 1131/1530] Fix typo on ld.config.txt for swcodec Test: build, boot, and check swcodec process. Change-Id: I4c06269c5be7ff2b6dabcc619ccca00688089c9c --- apex/ld.config.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index 7f9aad2fcf..b3422069a9 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -61,8 +61,7 @@ namespace.platform.isolated = true namespace.platform.search.paths = /system/${LIB} namespace.platform.asan.search.paths = /data/asan/system/${LIB} -# /system/lib/libc.so, etc are symlinks to -/apex/com.android.lib/lib/bionic/libc.so, etc. +# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc. # Add /apex/... pat to the permitted paths because linker uses realpath(3) # to check the accessibility of the lib. We could add this to search.paths # instead but that makes the resolution of bionic libs be dependent on -- GitLab From 70bfcec27553516ce22b1513d28da5d09d3a3c82 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 18 Mar 2019 12:52:28 -0700 Subject: [PATCH 1132/1530] Replace "max-pts-gap-to-encoder" with defined key Replace the raw string with defined key in ndk. bug: 80314065 test: builds Change-Id: I483d63d05c766f34e45d6a391fd1e241a7b875bc --- services/camera/libcameraservice/api2/HeicCompositeStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp index 9fd0e8bfbb..743c8163cc 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -1177,7 +1177,7 @@ status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height, outputFormat->setInt32(KEY_BITRATE_MODE, BITRATE_MODE_CQ); outputFormat->setInt32(KEY_QUALITY, kDefaultJpegQuality); // Ask codec to skip timestamp check and encode all frames. - outputFormat->setInt64("max-pts-gap-to-encoder", kNoFrameDropMaxPtsGap); + outputFormat->setInt64(KEY_MAX_PTS_GAP_TO_ENCODER, kNoFrameDropMaxPtsGap); int32_t gridWidth, gridHeight, gridRows, gridCols; if (useGrid || mUseHeic) { -- GitLab From d9b70d53091a6bfdf3abdd288b303ab1a73f95f9 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 28 Jan 2019 11:26:11 +0800 Subject: [PATCH 1133/1530] MKV handles MP3 audio whose frame is not complete MP3 frames of some special mkv files are not complete, so the frames read by MatroskaSource can't be played normally. MP3 frameSize for these special files is bigger than the buffer length of the frame read by readBlock. Read function can't get a complete MP3 frame which will be sent to MP3 decoder. Bug: 123590715 Test: Test MKV file whose MP3 audio frameSize in MP3 header is not consistent with buffer length and check if MP3 audio can be played normally. Change-Id: If1deca29fffc50012edb3af63d96ec9e99aa3fba --- media/extractors/mkv/MatroskaExtractor.cpp | 213 ++++++++++++++++++++- 1 file changed, 212 insertions(+), 1 deletion(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index f8aa784829..20cc6438ee 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -147,6 +148,7 @@ private: AVC, AAC, HEVC, + MP3, OTHER }; @@ -159,6 +161,15 @@ private: List mPendingFrames; + int64_t mCurrentTS; // add for mp3 + uint32_t mMP3Header; + + media_status_t findMP3Header(uint32_t * header, + const uint8_t *dataSource, int length, int *outStartPos); + media_status_t mp3FrameRead( + MediaBufferHelper **out, const ReadOptions *options, + int64_t targetSampleTimeUs); + status_t advance(); status_t setWebmBlockCryptoInfo(MediaBufferHelper *mbuf); @@ -225,7 +236,9 @@ MatroskaSource::MatroskaSource( mBlockIter(mExtractor, mExtractor->mTracks.itemAt(index).mTrackNum, index), - mNALSizeLen(-1) { + mNALSizeLen(-1), + mCurrentTS(0), + mMP3Header(0) { MatroskaExtractor::TrackInfo &trackInfo = mExtractor->mTracks.editItemAt(index); AMediaFormat *meta = trackInfo.mMeta; @@ -254,6 +267,8 @@ MatroskaSource::MatroskaSource( } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { + mType = MP3; } } @@ -270,6 +285,16 @@ media_status_t MatroskaSource::start() { mBufferGroup->init(1 /* number of buffers */, 1024 /* buffer size */, 64 /* growth limit */); mBlockIter.reset(); + if (mType == MP3 && mMP3Header == 0) { + int start = -1; + media_status_t err = findMP3Header(&mMP3Header, NULL, 0, &start); + if (err != OK) { + ALOGE("No mp3 header found"); + clearPendingFrames(); + return err; + } + } + return AMEDIA_OK; } @@ -796,6 +821,188 @@ media_status_t MatroskaSource::readBlock() { return AMEDIA_OK; } +//the value of kMP3HeaderMask is from MP3Extractor +static const uint32_t kMP3HeaderMask = 0xfffe0c00; + +media_status_t MatroskaSource::findMP3Header(uint32_t * header, + const uint8_t *dataSource, int length, int *outStartPos) { + if (NULL == header) { + ALOGE("header is null!"); + return AMEDIA_ERROR_END_OF_STREAM; + } + + //to find header start position + if (0 != *header) { + if (NULL == dataSource) { + *outStartPos = -1; + return AMEDIA_OK; + } + uint32_t tmpCode = 0; + for (int i = 0; i < length; i++) { + tmpCode = (tmpCode << 8) + dataSource[i]; + if ((tmpCode & kMP3HeaderMask) == (*header & kMP3HeaderMask)) { + *outStartPos = i - 3; + return AMEDIA_OK; + } + } + *outStartPos = -1; + return AMEDIA_OK; + } + + //to find mp3 header + uint32_t code = 0; + while (0 == *header) { + while (mPendingFrames.empty()) { + media_status_t err = readBlock(); + if (err != OK) { + clearPendingFrames(); + return err; + } + } + MediaBufferHelper *frame = *mPendingFrames.begin(); + size_t size = frame->range_length(); + size_t offset = frame->range_offset(); + size_t i; + size_t frame_size; + for (i = 0; i < size; i++) { + ALOGV("data[%zu]=%x", i, *((uint8_t*)frame->data() + offset + i)); + code = (code << 8) + *((uint8_t*)frame->data() + offset + i); + if (GetMPEGAudioFrameSize(code, &frame_size, NULL, NULL, NULL)) { + *header = code; + mBlockIter.reset(); + clearPendingFrames(); + return AMEDIA_OK; + } + } + } + + return AMEDIA_ERROR_END_OF_STREAM; +} + +media_status_t MatroskaSource::mp3FrameRead( + MediaBufferHelper **out, const ReadOptions *options, + int64_t targetSampleTimeUs) { + MediaBufferHelper *frame = *mPendingFrames.begin(); + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + CHECK(AMediaFormat_getInt64(frame->meta_data(), + AMEDIAFORMAT_KEY_TIME_US, &mCurrentTS)); + if (mCurrentTS < 0) { + mCurrentTS = 0; + AMediaFormat_setInt64(frame->meta_data(), + AMEDIAFORMAT_KEY_TIME_US, mCurrentTS); + } + } + + int32_t start = -1; + while (start < 0) { + //find header start position + findMP3Header(&mMP3Header, + (const uint8_t*)frame->data() + frame->range_offset(), + frame->range_length(), &start); + ALOGV("start=%d, frame->range_length() = %zu, frame->range_offset() =%zu", + start, frame->range_length(), frame->range_offset()); + if (start >= 0) + break; + frame->release(); + mPendingFrames.erase(mPendingFrames.begin()); + while (mPendingFrames.empty()) { + media_status_t err = readBlock(); + if (err != OK) { + clearPendingFrames(); + return err; + } + } + frame = *mPendingFrames.begin(); + } + + frame->set_range(frame->range_offset() + start, frame->range_length() - start); + + uint32_t header = *(uint32_t*)((uint8_t*)frame->data() + frame->range_offset()); + header = ((header >> 24) & 0xff) | ((header >> 8) & 0xff00) | + ((header << 8) & 0xff0000) | ((header << 24) & 0xff000000); + size_t frame_size; + int out_sampling_rate; + int out_channels; + int out_bitrate; + if (!GetMPEGAudioFrameSize(header, &frame_size, + &out_sampling_rate, &out_channels, &out_bitrate)) { + ALOGE("MP3 Header read fail!!"); + return AMEDIA_ERROR_UNSUPPORTED; + } + + MediaBufferHelper *buffer; + mBufferGroup->acquire_buffer(&buffer, false /* nonblocking */, frame_size /* requested size */); + buffer->set_range(0, frame_size); + + uint8_t *data = static_cast(buffer->data()); + ALOGV("MP3 frame %zu frame->range_length() %zu", frame_size, frame->range_length()); + + if (frame_size > frame->range_length()) { + memcpy(data, (uint8_t*)(frame->data()) + frame->range_offset(), frame->range_length()); + size_t sumSize = 0; + sumSize += frame->range_length(); + size_t needSize = frame_size - frame->range_length(); + frame->release(); + mPendingFrames.erase(mPendingFrames.begin()); + while (mPendingFrames.empty()) { + media_status_t err = readBlock(); + if (err != OK) { + clearPendingFrames(); + return err; + } + } + frame = *mPendingFrames.begin(); + size_t offset = frame->range_offset(); + size_t size = frame->range_length(); + + // the next buffer frame is not enough to fullfill mp3 frame, + // we have to read until mp3 frame is completed. + while (size < needSize) { + memcpy(data + sumSize, (uint8_t*)(frame->data()) + offset, size); + needSize -= size; + sumSize += size; + frame->release(); + mPendingFrames.erase(mPendingFrames.begin()); + while (mPendingFrames.empty()) { + media_status_t err = readBlock(); + if (err != OK) { + clearPendingFrames(); + return err; + } + } + frame = *mPendingFrames.begin(); + offset = frame->range_offset(); + size = frame->range_length(); + } + memcpy(data + sumSize, (uint8_t*)(frame->data()) + offset, needSize); + frame->set_range(offset + needSize, size - needSize); + } else { + size_t offset = frame->range_offset(); + size_t size = frame->range_length(); + memcpy(data, (uint8_t*)(frame->data()) + offset, frame_size); + frame->set_range(offset + frame_size, size - frame_size); + } + if (frame->range_length() < 4) { + frame->release(); + frame = NULL; + mPendingFrames.erase(mPendingFrames.begin()); + } + ALOGV("MatroskaSource::read MP3 frame kKeyTime=%lld,kKeyTargetTime=%lld", + (long long)mCurrentTS, (long long)targetSampleTimeUs); + AMediaFormat_setInt64(buffer->meta_data(), + AMEDIAFORMAT_KEY_TIME_US, mCurrentTS); + mCurrentTS += (int64_t)frame_size * 8000ll / out_bitrate; + + if (targetSampleTimeUs >= 0ll) + AMediaFormat_setInt64(buffer->meta_data(), + AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); + *out = buffer; + ALOGV("MatroskaSource::read MP3, keyTime=%lld for next frame", (long long)mCurrentTS); + return AMEDIA_OK; +} + media_status_t MatroskaSource::read( MediaBufferHelper **out, const ReadOptions *options) { *out = NULL; @@ -833,6 +1040,10 @@ media_status_t MatroskaSource::read( } } + if (mType == MP3) { + return mp3FrameRead(out, options, targetSampleTimeUs); + } + MediaBufferHelper *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); -- GitLab From 2ffdfce9d49040563a4bd9542793aeabf31acc10 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Tue, 12 Mar 2019 11:26:42 +0100 Subject: [PATCH 1134/1530] audiopolicy: optimize set volume call sequence This CL simplifies call flow to set volume from stream or attributes. It also removes unused parameter from mute setter and fix a volume source comparison. Also refactor logic in setVolumeIndexForAttributes() by removing the loop on all volume sources on a given output given it was ignoring all sources not equal to current source anyway. Bug: 124767636 Test: AudioPolicyTests --gtest_filter=StreamTypeVolumeTest.* AudioPolicyTests --gtest_filter=AttributeVolume/AttributeVolumeTest.* AudioPolicyTests --gtest_filter=AudioProductStrategiesPlaybackVolume/AudioProductStrategiesPlaybackVolumeTest.* Change-Id: Id816de023e917b0c0e34bb0888c8b0ad29029f8b Signed-off-by: Francois Gaffie --- .../managerdefault/AudioPolicyManager.cpp | 124 +++++++----------- .../managerdefault/AudioPolicyManager.h | 11 +- 2 files changed, 54 insertions(+), 81 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 5574e1ce8d..e8e9fa6ac5 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2389,14 +2389,9 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, audio_devices_t device) { auto attributes = mEngine->getAttributesForStreamType(stream); - auto volumeGroup = mEngine->getVolumeGroupForStreamType(stream); - if (volumeGroup == VOLUME_GROUP_NONE) { - ALOGE("%s: no group matching with stream %s", __FUNCTION__, toString(stream).c_str()); - return BAD_VALUE; - } ALOGV("%s: stream %s attributes=%s", __func__, toString(stream).c_str(), toString(attributes).c_str()); - return setVolumeGroupIndex(getVolumeCurves(stream), volumeGroup, index, device, attributes); + return setVolumeIndexForAttributes(attributes, index, device); } status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, @@ -2411,27 +2406,19 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, return getVolumeIndex(getVolumeCurves(stream), *index, device); } -status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attr, +status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attributes, int index, audio_devices_t device) { // Get Volume group matching the Audio Attributes - auto volumeGroup = mEngine->getVolumeGroupForAttributes(attr); - if (volumeGroup == VOLUME_GROUP_NONE) { - ALOGD("%s: could not find group matching with %s", __FUNCTION__, toString(attr).c_str()); + auto group = mEngine->getVolumeGroupForAttributes(attributes); + if (group == VOLUME_GROUP_NONE) { + ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str()); return BAD_VALUE; } - ALOGV("%s: group %d matching with %s", __FUNCTION__, volumeGroup, toString(attr).c_str()); - return setVolumeGroupIndex(getVolumeCurves(attr), volumeGroup, index, device, attr); -} - -status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_group_t group, - int index, - audio_devices_t device, - const audio_attributes_t attributes) -{ - ALOGVV("%s: group=%d", __func__, group); + ALOGV("%s: group %d matching with %s", __FUNCTION__, group, toString(attributes).c_str()); status_t status = NO_ERROR; + IVolumeCurves &curves = getVolumeCurves(attributes); VolumeSource vs = toVolumeSource(group); product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes); @@ -2440,6 +2427,21 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device); return status; } + + audio_devices_t curSrcDevice; + auto curCurvAttrs = curves.getAttributes(); + if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) { + auto attr = curCurvAttrs.front(); + curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types(); + } else if (!curves.getStreamTypes().empty()) { + auto stream = curves.getStreamTypes().front(); + curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types(); + } else { + ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, vs); + return BAD_VALUE; + } + curSrcDevice = Volume::getDeviceForVolume(curSrcDevice); + // update volume on all outputs and streams matching the following: // - The requested stream (or a stream matching for volume control) is active on the output // - The device (or devices) selected by the engine for this stream includes @@ -2450,7 +2452,7 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g // no specific device volume value exists for currently selected device. for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); - audio_devices_t curDevice = Volume::getDeviceForVolume(desc->devices().types()); + audio_devices_t curDevice = desc->devices().types(); // Inter / intra volume group priority management: Loop on strategies arranged by priority // If a higher priority strategy is active, and the output is routed to a device with a @@ -2501,48 +2503,28 @@ status_t AudioPolicyManager::setVolumeGroupIndex(IVolumeCurves &curves, volume_g } continue; } - for (auto curVolGroup : getVolumeGroups()) { - VolumeSource curVolSrc = toVolumeSource(curVolGroup); - if (curVolSrc != vs) { - continue; - } - if (!(desc->isActive(vs) || isInCall())) { - continue; - } - audio_devices_t curSrcDevice; - auto &curCurves = getVolumeCurves(curVolSrc); - auto curCurvAttrs = curCurves.getAttributes(); - if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) { - auto attr = curCurvAttrs.front(); - curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types(); - } else if (!curCurves.getStreamTypes().empty()) { - auto stream = curCurves.getStreamTypes().front(); - curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types(); - } else { - ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, curVolSrc); - continue; - } - curSrcDevice = Volume::getDeviceForVolume(curSrcDevice); - if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) { - continue; - } - if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { - curSrcDevice |= device; - applyVolume = (curDevice & curSrcDevice) != 0; - } else { - applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice); - } - if (applyVolume) { - //FIXME: workaround for truncated touch sounds - // delayed volume change for system stream to be removed when the problem is - // handled by system UI - status_t volStatus = checkAndSetVolume( - curCurves, curVolSrc, index, desc, curDevice, - ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))? - TOUCH_SOUND_FIXED_DELAY_MS : 0)); - if (volStatus != NO_ERROR) { - status = volStatus; - } + if (!(desc->isActive(vs) || isInCall())) { + continue; + } + if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) { + continue; + } + if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { + curSrcDevice |= device; + applyVolume = (Volume::getDeviceForVolume(curDevice) & curSrcDevice) != 0; + } else { + applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice); + } + if (applyVolume) { + //FIXME: workaround for truncated touch sounds + // delayed volume change for system stream to be removed when the problem is + // handled by system UI + status_t volStatus = checkAndSetVolume( + curves, vs, index, desc, curDevice, + ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))? + TOUCH_SOUND_FIXED_DELAY_MS : 0)); + if (volStatus != NO_ERROR) { + status = volStatus; } } } @@ -5730,14 +5712,14 @@ float AudioPolicyManager::computeVolume(IVolumeCurves &curves, } int AudioPolicyManager::rescaleVolumeIndex(int srcIndex, - audio_stream_type_t srcStream, - audio_stream_type_t dstStream) + VolumeSource fromVolumeSource, + VolumeSource toVolumeSource) { - if (srcStream == dstStream) { + if (fromVolumeSource == toVolumeSource) { return srcIndex; } - auto &srcCurves = getVolumeCurves(srcStream); - auto &dstCurves = getVolumeCurves(dstStream); + auto &srcCurves = getVolumeCurves(fromVolumeSource); + auto &dstCurves = getVolumeCurves(toVolumeSource); float minSrc = (float)srcCurves.getVolumeIndexMin(); float maxSrc = (float)srcCurves.getVolumeIndexMax(); float minDst = (float)dstCurves.getVolumeIndexMin(); @@ -5851,12 +5833,8 @@ void AudioPolicyManager::setVolumeSourceMute(VolumeSource volumeSource, bool on, const sp& outputDesc, int delayMs, - audio_devices_t device, - bool activeOnly) + audio_devices_t device) { - if (activeOnly && !outputDesc->isActive(volumeSource)) { - return; - } if (device == AUDIO_DEVICE_NONE) { device = outputDesc->devices().types(); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 1fc61e5bd4..1c9868419e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -171,9 +171,6 @@ public: virtual status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); - status_t setVolumeGroupIndex(IVolumeCurves &volumeCurves, volume_group_t group, int index, - audio_devices_t device, const audio_attributes_t attributes); - status_t setVolumeCurveIndex(int index, audio_devices_t device, IVolumeCurves &volumeCurves); @@ -425,8 +422,8 @@ protected: // rescale volume index from srcStream within range of dstStream int rescaleVolumeIndex(int srcIndex, - audio_stream_type_t srcStream, - audio_stream_type_t dstStream); + VolumeSource fromVolumeSource, + VolumeSource toVolumeSource); // check that volume change is permitted, compute and send new volume to audio hardware virtual status_t checkAndSetVolume(IVolumeCurves &curves, VolumeSource volumeSource, int index, @@ -461,14 +458,12 @@ protected: * @param outputDesc on which the client following the volume group shall be muted/umuted * @param delayMs * @param device - * @param activeOnly if true, mute only if the volume group is active on the output. */ void setVolumeSourceMute(VolumeSource volumeSource, bool on, const sp& outputDesc, int delayMs = 0, - audio_devices_t device = AUDIO_DEVICE_NONE, - bool activeOnly = false); + audio_devices_t device = AUDIO_DEVICE_NONE); audio_mode_t getPhoneState(); -- GitLab From 48616156f80645f6a751f52c0f12d2e4a914c2b4 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 21 Feb 2019 06:28:10 -0800 Subject: [PATCH 1135/1530] Export libgui headers from media modules libstagefright, libmedia_ndk and libmedia_omx Also, fix the reference to HGraphicBufferProducer in AImageReaderWindowHandleTest. Test: Builds Bug: 112508112 Change-Id: I2ce0e4fd395a0c733aaf98b7397d7183c7475672 --- media/libmedia/Android.bp | 1 + media/libstagefright/Android.bp | 1 + media/ndk/Android.bp | 4 ++++ media/ndk/tests/AImageReaderWindowHandleTest.cpp | 2 ++ 4 files changed, 8 insertions(+) diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 9799cad6e3..a51a2cf258 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -86,6 +86,7 @@ cc_library_shared { export_shared_lib_headers: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", + "libgui", "libstagefright_foundation", "libui", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index d8b825d040..dc51b16164 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -225,6 +225,7 @@ cc_library { ], export_shared_lib_headers: [ + "libgui", "libmedia", "android.hidl.allocator@1.0", ], diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index f9f1acc2be..f4cc704dce 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -101,6 +101,10 @@ cc_library_shared { export_include_dirs: ["include"], + export_shared_lib_headers: [ + "libgui", + ], + product_variables: { pdk: { enabled: false, diff --git a/media/ndk/tests/AImageReaderWindowHandleTest.cpp b/media/ndk/tests/AImageReaderWindowHandleTest.cpp index 5f112529f6..5b65064068 100644 --- a/media/ndk/tests/AImageReaderWindowHandleTest.cpp +++ b/media/ndk/tests/AImageReaderWindowHandleTest.cpp @@ -27,6 +27,8 @@ namespace android { +using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer; using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer; using aimg::AImageReader_getHGBPFromHandle; -- GitLab From ef939bf16875d414b46a7f474cba721f5b901d66 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Sun, 3 Mar 2019 04:44:59 -0800 Subject: [PATCH 1136/1530] Use @2.0::IGraphicBufferProducer in Codec2 modules Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 33350696 Bug: 112362730 Change-Id: Ia5ed8da1c1fa3ab9decbafd814783b68e9ac9adb --- media/codec2/hidl/1.0/utils/Android.bp | 2 + media/codec2/hidl/1.0/utils/Component.cpp | 4 +- .../codec2/hidl/1.0/utils/ComponentStore.cpp | 8 +- media/codec2/hidl/1.0/utils/InputSurface.cpp | 2 - .../utils/include/codec2/hidl/1.0/Component.h | 8 +- .../include/codec2/hidl/1.0/InputSurface.h | 4 +- media/codec2/hidl/1.0/utils/types.cpp | 12 +- media/codec2/hidl/client/client.cpp | 29 ++-- .../hidl/client/include/codec2/hidl/client.h | 56 ++----- media/codec2/vndk/Android.bp | 4 +- media/codec2/vndk/include/C2BqBufferPriv.h | 9 +- media/codec2/vndk/internal/C2BlockInternal.h | 6 +- media/codec2/vndk/platform/C2BqBuffer.cpp | 151 ++++++++++-------- 13 files changed, 152 insertions(+), 143 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index f5aa65b961..0bb24186fb 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -27,6 +27,7 @@ cc_library { 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", @@ -36,6 +37,7 @@ cc_library { "libcodec2", "libcodec2_vndk", "libcutils", + "libgui", "libhidlbase", "libhidltransport", "libhwbinder", diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp index f3bf6f7fbd..5897dce8f9 100644 --- a/media/codec2/hidl/1.0/utils/Component.cpp +++ b/media/codec2/hidl/1.0/utils/Component.cpp @@ -272,7 +272,7 @@ Return Component::drain(bool withEos) { Return Component::setOutputSurface( uint64_t blockPoolId, - const sp& surface) { + const sp& surface) { std::shared_ptr pool; GetCodec2BlockPool(blockPoolId, mComponent, &pool); if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) { @@ -312,7 +312,7 @@ Return Component::connectToInputSurface( } Return Component::connectToOmxInputSurface( - const sp& producer, + const sp& producer, const sp<::android::hardware::media::omx::V1_0:: IGraphicBufferSource>& source, connectToOmxInputSurface_cb _hidl_cb) { diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp index bb5faa5fc3..53bb6d2688 100644 --- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -219,13 +219,11 @@ Return ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) _hidl_cb(Status::CORRUPTED, nullptr); return Void(); } - typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IGraphicBufferProducer HGbp; - typedef ::android::TWGraphicBufferProducer B2HGbp; sp inputSurface = new InputSurface( this, std::make_shared(), - new B2HGbp(source->getIGraphicBufferProducer()), + new ::android::hardware::graphics::bufferqueue::V2_0::utils:: + B2HGraphicBufferProducer(source->getIGraphicBufferProducer()), source); _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY, inputSurface); diff --git a/media/codec2/hidl/1.0/utils/InputSurface.cpp b/media/codec2/hidl/1.0/utils/InputSurface.cpp index 85c44c3146..2b4ca85be2 100644 --- a/media/codec2/hidl/1.0/utils/InputSurface.cpp +++ b/media/codec2/hidl/1.0/utils/InputSurface.cpp @@ -151,8 +151,6 @@ Return InputSurface::connect( return Void(); } -// Derived methods from IGraphicBufferProducer - // Constructor is exclusive to ComponentStore. InputSurface::InputSurface( const sp& store, diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h index e444013271..86dccd074c 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h @@ -68,7 +68,9 @@ struct Component : public IComponent, c2_status_t status() const; typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IGraphicBufferProducer HGraphicBufferProducer; + IGraphicBufferProducer HGraphicBufferProducer1; + typedef ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer HGraphicBufferProducer2; // Methods from IComponent follow. virtual Return queue(const WorkBundle& workBundle) override; @@ -76,12 +78,12 @@ struct Component : public IComponent, virtual Return drain(bool withEos) override; virtual Return setOutputSurface( uint64_t blockPoolId, - const sp& surface) override; + const sp& surface) override; virtual Return connectToInputSurface( const sp& inputSurface, connectToInputSurface_cb _hidl_cb) override; virtual Return connectToOmxInputSurface( - const sp& producer, + const sp& producer, const sp<::android::hardware::media::omx::V1_0:: IGraphicBufferSource>& source, connectToOmxInputSurface_cb _hidl_cb) override; diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h index 2682c13bfc..34ea959d13 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h @@ -19,7 +19,7 @@ #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ using ::android::sp; struct InputSurface : public IInputSurface { - typedef ::android::hardware::graphics::bufferqueue::V1_0:: + typedef ::android::hardware::graphics::bufferqueue::V2_0:: IGraphicBufferProducer HGraphicBufferProducer; typedef ::android:: diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 343bcb5433..031e6371af 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -18,9 +18,9 @@ #define LOG_TAG "Codec2-types" #include +#include #include - -#include +#include #include #include @@ -46,7 +46,6 @@ namespace c2 { namespace V1_0 { namespace utils { -using namespace ::android; using ::android::hardware::Return; using ::android::hardware::media::bufferpool::BufferPoolData; using ::android::hardware::media::bufferpool::V2_0::BufferStatusMessage; @@ -55,7 +54,10 @@ using ::android::hardware::media::bufferpool::V2_0::implementation:: ClientManager; using ::android::hardware::media::bufferpool::V2_0::implementation:: TransactionId; -using ::android::TWGraphicBufferProducer; +using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: + V2_0::IGraphicBufferProducer; +using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: + V2_0::utils::B2HGraphicBufferProducer; const char* asString(Status status, const char* def) { return asString(static_cast(status), def); @@ -1806,7 +1808,7 @@ sp getHgbp(const sp& igbp) { sp hgbp = igbp->getHalInterface(); return hgbp ? hgbp : - new TWGraphicBufferProducer(igbp); + new B2HGraphicBufferProducer(igbp); } } // unnamed namespace diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 7a2e549f4b..0fe8376ba5 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -29,9 +29,9 @@ #include #include #include -#include +#include +#include #include -#include #include #include @@ -50,13 +50,21 @@ using ::android::hardware::hidl_vec; using ::android::hardware::hidl_string; using ::android::hardware::Return; using ::android::hardware::Void; -using ::android::TWGraphicBufferProducer; using namespace ::android::hardware::media::c2::V1_0; using namespace ::android::hardware::media::c2::V1_0::utils; using namespace ::android::hardware::media::bufferpool::V2_0; using namespace ::android::hardware::media::bufferpool::V2_0::implementation; +using HGraphicBufferProducer1 = ::android::hardware::graphics::bufferqueue:: + V1_0::IGraphicBufferProducer; +using HGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: + V2_0::IGraphicBufferProducer; +using B2HGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: + V2_0::utils::B2HGraphicBufferProducer; +using H2BGraphicBufferProducer2 = ::android::hardware::graphics::bufferqueue:: + V2_0::utils::H2BGraphicBufferProducer; + namespace /* unnamed */ { // c2_status_t value that corresponds to hwbinder transaction failure. @@ -1064,11 +1072,11 @@ c2_status_t Codec2Client::Component::setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, uint32_t generation) { - sp igbp = - surface->getHalInterface(); + sp igbp = + surface->getHalInterface(); if (!igbp) { - igbp = new TWGraphicBufferProducer(surface); + igbp = new B2HGraphicBufferProducer2(surface); } Return transStatus = mBase->setOutputSurface( @@ -1195,7 +1203,7 @@ c2_status_t Codec2Client::Component::connectToInputSurface( } c2_status_t Codec2Client::Component::connectToOmxInputSurface( - const sp& producer, + const sp& producer, const sp& source, std::shared_ptr* connection) { c2_status_t status; @@ -1284,12 +1292,11 @@ Codec2Client::InputSurface::InputSurface(const sp& base) }, mBase{base}, mGraphicBufferProducer{new - ::android::hardware::graphics::bufferqueue::V1_0::utils:: - H2BGraphicBufferProducer([base]() -> sp { - Return> transResult = + H2BGraphicBufferProducer2([base]() -> sp { + Return> transResult = base->getGraphicBufferProducer(); return transResult.isOk() ? - static_cast>(transResult) : + static_cast>(transResult) : nullptr; }())} { } diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 478ce6ebeb..cd422056d3 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -63,55 +63,29 @@ * Codec2Client are all subclasses of Configurable. */ -// Forward declaration of Codec2.0 HIDL interfaces -namespace android { -namespace hardware { -namespace media { -namespace c2 { -namespace V1_0 { +// Forward declaration of relevant HIDL interfaces + +namespace android::hardware::media::c2::V1_0 { struct IConfigurable; struct IComponent; struct IComponentInterface; struct IComponentStore; +struct IInputSink; struct IInputSurface; struct IInputSurfaceConnection; -} // namespace V1_0 -} // namespace c2 -} // namespace media -} // namespace hardware -} // namespace android +} // namespace android::hardware::media::c2::V1_0 -namespace android { -namespace hardware { -namespace media { -namespace bufferpool { -namespace V2_0 { +namespace android::hardware::media::bufferpool::V2_0 { struct IClientManager; -} // namespace V2_0 -} // namespace bufferpool -} // namespace media -} // namespace hardware -} // namespace android +} // namespace android::hardware::media::bufferpool::V2_0 -// Forward declarations of other classes -namespace android { -namespace hardware { -namespace graphics { -namespace bufferqueue { -namespace V1_0 { +namespace android::hardware::graphics::bufferqueue::V1_0 { struct IGraphicBufferProducer; -} // namespace V1_0 -} // namespace bufferqueue -} // namespace graphics -namespace media { -namespace omx { -namespace V1_0 { +} // android::hardware::graphics::bufferqueue::V1_0 + +namespace android::hardware::media::omx::V1_0 { struct IGraphicBufferSource; -} // namespace V1_0 -} // namespace omx -} // namespace media -} // namespace hardware -} // namespace android +} // namespace android::hardware::media::omx::V1_0 namespace android { @@ -324,7 +298,9 @@ struct Codec2Client::Component : public Codec2Client::Configurable { QueueBufferOutput QueueBufferOutput; typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IGraphicBufferProducer HGraphicBufferProducer; + IGraphicBufferProducer HGraphicBufferProducer1; + typedef ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer HGraphicBufferProducer2; typedef ::android::hardware::media::omx::V1_0:: IGraphicBufferSource HGraphicBufferSource; @@ -362,7 +338,7 @@ struct Codec2Client::Component : public Codec2Client::Configurable { std::shared_ptr* connection); c2_status_t connectToOmxInputSurface( - const sp& producer, + const sp& producer, const sp& source, std::shared_ptr* connection); diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index ab6a105e07..c6ca670baa 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -38,6 +38,7 @@ cc_library_shared { export_shared_lib_headers: [ "libbase", + "libgui", "android.hardware.media.bufferpool@2.0", ], @@ -52,13 +53,14 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.mapper@2.0", "android.hardware.media.bufferpool@2.0", "libbase", "libbinder", "libcutils", "libdl", + "libgui", "libhardware", "libhidlbase", "libion", diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h index 9271a71b5b..e1a8138666 100644 --- a/media/codec2/vndk/include/C2BqBufferPriv.h +++ b/media/codec2/vndk/include/C2BqBufferPriv.h @@ -17,10 +17,11 @@ #ifndef STAGEFRIGHT_CODEC2_BQ_BUFFER_PRIV_H_ #define STAGEFRIGHT_CODEC2_BQ_BUFFER_PRIV_H_ -#include +#include #include -#include + +#include class C2BufferQueueBlockPool : public C2BlockPool { public: @@ -52,6 +53,8 @@ public: */ virtual void setRenderCallback(const OnRenderCallback &renderCallback = OnRenderCallback()); + typedef ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer HGraphicBufferProducer; /** * 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 @@ -62,7 +65,7 @@ public: * * \param producer the IGBP, which will be used to fetch blocks */ - virtual void configureProducer(const android::sp &producer); + virtual void configureProducer(const android::sp &producer); private: const std::shared_ptr mAllocator; diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h index 2abf3ac925..84ce70ad2d 100644 --- a/media/codec2/vndk/internal/C2BlockInternal.h +++ b/media/codec2/vndk/internal/C2BlockInternal.h @@ -17,7 +17,7 @@ #ifndef ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_ #define ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_ -#include +#include #include @@ -291,7 +291,7 @@ struct _C2BlockFactory { bool AssignBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& poolData, const ::android::sp<::android::hardware::graphics::bufferqueue:: - V1_0::IGraphicBufferProducer>& igbp, + V2_0::IGraphicBufferProducer>& igbp, uint32_t generation, uint64_t bqId, int32_t bqSlot, @@ -314,7 +314,7 @@ struct _C2BlockFactory { bool HoldBlockFromBufferQueue( const std::shared_ptr<_C2BlockPoolData>& poolData, const ::android::sp<::android::hardware::graphics::bufferqueue:: - V1_0::IGraphicBufferProducer>& igbp = nullptr); + V2_0::IGraphicBufferProducer>& igbp = nullptr); /** * Yield a block to the designated bufferqueue. This causes the destruction diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 41a5b3ff30..9cc5677b3c 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -19,29 +19,37 @@ #include #include -#include -#include -#include +#include +#include #include #include #include -using ::android::AnwBuffer; +#include +#include +#include + using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS; using ::android::C2AllocatorGralloc; using ::android::C2AndroidMemoryUsage; using ::android::Fence; using ::android::GraphicBuffer; -using ::android::HGraphicBufferProducer; using ::android::IGraphicBufferProducer; -using ::android::hidl_handle; using ::android::sp; using ::android::status_t; using ::android::wp; - +using ::android::hardware::hidl_handle; using ::android::hardware::Return; -using ::android::hardware::graphics::common::V1_0::PixelFormat; + +using HBuffer = ::android::hardware::graphics::common::V1_2::HardwareBuffer; +using HStatus = ::android::hardware::graphics::bufferqueue::V2_0::Status; +using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h; +using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b; +using ::android::hardware::graphics::bufferqueue::V2_0::utils::HFenceWrapper; + +using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::V2_0 + ::IGraphicBufferProducer; struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { @@ -185,57 +193,67 @@ private: C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { // We have an IGBP now. - sp fence = new Fence(); C2AndroidMemoryUsage androidUsage = usage; - status_t status; - PixelFormat pixelFormat = static_cast(format); - int slot; + status_t status{}; + int slot{}; + bool bufferNeedsReallocation{}; + sp fence = new Fence(); ALOGV("tries to dequeue buffer"); - Return transStatus = mProducer->dequeueBuffer( - width, height, pixelFormat, androidUsage.asGrallocUsage(), false, - [&status, &slot, &fence]( - int32_t tStatus, int32_t tSlot, hidl_handle const& tFence, - HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) { - status = tStatus; - slot = tSlot; - if (!android::conversion::convertTo(fence.get(), tFence) && - status == android::NO_ERROR) { - status = android::BAD_VALUE; + + { // 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) { + ALOGD("cannot dequeue buffer %d", status); + if (transResult.isOk()) { + if (status == android::INVALID_OPERATION || + status == android::TIMED_OUT || + status == android::WOULD_BLOCK) { + // Dequeue buffer is blocked temporarily. Retrying is + // required. + return C2_BLOCKING; } - (void) tTs; - }); - // dequeueBuffer returns flag. - if (!transStatus.isOk() || status < android::OK) { - ALOGD("cannot dequeue buffer %d", status); - if (transStatus.isOk()) { - if (status == android::INVALID_OPERATION || - status == android::TIMED_OUT || - status == android::WOULD_BLOCK) { - // Dequeue buffer is blocked temporarily. Retrying is - // required. - return C2_BLOCKING; } + return C2_BAD_VALUE; } + } + HFenceWrapper hFenceWrapper{}; + if (!b2h(fence, &hFenceWrapper)) { + ALOGE("Invalid fence received from dequeueBuffer."); return C2_BAD_VALUE; } ALOGV("dequeued a buffer successfully"); - native_handle_t* nh = nullptr; - hidl_handle fenceHandle; - if (fence) { - android::conversion::wrapAs(&fenceHandle, &nh, *fence); - } 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, fenceHandle).isOk(); + (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, fenceHandle).isOk(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); return C2_BAD_VALUE; } else if (mRenderCallback) { nsecs_t signalTime = fence->getSignalTime(); @@ -248,27 +266,31 @@ private: } sp &slotBuffer = mBuffers[slot]; - if (status & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || !slotBuffer) { + if (bufferNeedsReallocation || !slotBuffer) { if (!slotBuffer) { slotBuffer = new GraphicBuffer(); } // N.B. This assumes requestBuffer# returns an existing allocation // instead of a new allocation. - Return transStatus = mProducer->requestBuffer( + Return transResult = mProducer->requestBuffer( slot, - [&status, &slotBuffer](int32_t tStatus, AnwBuffer const& tBuffer){ - status = tStatus; - if (!android::conversion::convertTo(slotBuffer.get(), tBuffer) && - status == android::NO_ERROR) { + [&status, &slotBuffer]( + HStatus hStatus, + HBuffer const& hBuffer, + uint32_t generationNumber){ + if (h2b(hStatus, &status) && + h2b(hBuffer, &slotBuffer) && + slotBuffer) { + slotBuffer->setGenerationNumber(generationNumber); + } else { status = android::BAD_VALUE; } }); - - if (!transStatus.isOk()) { + if (!transResult.isOk()) { return C2_BAD_VALUE; } else if (status != android::NO_ERROR) { slotBuffer.clear(); - (void)mProducer->cancelBuffer(slot, fenceHandle).isOk(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); return C2_BAD_VALUE; } } @@ -292,13 +314,14 @@ private: std::shared_ptr poolData = std::make_shared( slotBuffer->getGenerationNumber(), - mProducerId, slot, shared_from_this()); + mProducerId, slot, + shared_from_this()); *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); return C2_OK; } // Block was not created. call requestBuffer# again next time. slotBuffer.clear(); - (void)mProducer->cancelBuffer(slot, fenceHandle).isOk(); + (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); } return C2_BAD_VALUE; } @@ -312,10 +335,10 @@ public: bool noInit = false; for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { if (!noInit && mProducer) { - Return transResult = + Return transResult = mProducer->detachBuffer(static_cast(i)); noInit = !transResult.isOk() || - static_cast(transResult) == android::NO_INIT; + static_cast(transResult) == HStatus::NO_INIT; } mBuffers[i].clear(); } @@ -373,32 +396,28 @@ public: } void configureProducer(const sp &producer) { - int32_t status = android::OK; uint64_t producerId = 0; if (producer) { - Return transStatus = producer->getUniqueId( - [&status, &producerId](int32_t tStatus, int64_t tProducerId) { - status = tStatus; - producerId = tProducerId; - }); - if (!transStatus.isOk()) { + Return transResult = producer->getUniqueId(); + if (!transResult.isOk()) { ALOGD("configureProducer -- failed to connect to the producer"); return; } + producerId = static_cast(transResult); } { std::lock_guard lock(mMutex); bool noInit = false; for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { if (!noInit && mProducer) { - Return transResult = + Return transResult = mProducer->detachBuffer(static_cast(i)); noInit = !transResult.isOk() || - static_cast(transResult) == android::NO_INIT; + static_cast(transResult) == HStatus::NO_INIT; } mBuffers[i].clear(); } - if (producer && status == android::OK) { + if (producer) { mProducer = producer; mProducerId = producerId; } else { @@ -414,7 +433,7 @@ private: void cancel(uint64_t igbp_id, int32_t igbp_slot) { std::lock_guard lock(mMutex); if (igbp_id == mProducerId && mProducer) { - (void)mProducer->cancelBuffer(igbp_slot, nullptr).isOk(); + (void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk(); } } @@ -455,7 +474,7 @@ C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() { if (local && localPool) { localPool->cancel(bqId, bqSlot); } else if (igbp) { - igbp->cancelBuffer(bqSlot, nullptr); + igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk(); } } -- GitLab From c3e8781dd15d1946f017437e2eb52d197081e820 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Mon, 18 Mar 2019 14:28:23 -0700 Subject: [PATCH 1137/1530] audio: Add type conversion entry for APTX_TWSP Add type conversion entry for APTX_TWSP audio format. Bug: 111812273 Bug: 128825638 Test: make Change-Id: Ib202746469dfe2f3d71334b18bb5a0a241dfd456 --- media/libmedia/TypeConverter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index 0301b21ab5..469c5b6948 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -217,6 +217,7 @@ const FormatConverter::Table FormatConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_ADAPTIVE), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC), MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LHDC_LL), + MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_TWSP), TERMINATOR }; -- GitLab From cc7c195bb55544019a704b6dab7c69013689a700 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 14 Mar 2019 15:57:24 -0700 Subject: [PATCH 1138/1530] Add metadata keys for opaque CSD This provides a generic 'passthrough' of codec specific data from extractor to codec, without having to add conversion to/from a format-specific key in Utils.cpp Remove unused kKeyFlacMetadata, and unneeded vorbis conversions in Utils.cpp Test: build, CTS Change-Id: I177090c0b0cb5174031c68ec7ec2abf6fa15c360 --- media/libstagefright/Utils.cpp | 67 ++++++++----------- .../include/media/stagefright/MetaDataBase.h | 6 +- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 16b3319c21..e4a6e67435 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -645,6 +645,14 @@ static std::vector> bufferMappings { } }; +static std::vector> CSDMappings { + { + { "csd-0", kKeyOpaqueCSD0 }, + { "csd-1", kKeyOpaqueCSD1 }, + { "csd-2", kKeyOpaqueCSD2 }, + } +}; + void convertMessageToMetaDataFromMappings(const sp &msg, sp &meta) { for (auto elem : stringMappings) { AString value; @@ -681,6 +689,14 @@ void convertMessageToMetaDataFromMappings(const sp &msg, sp MetaDataBase::Type::TYPE_NONE, value->data(), value->size()); } } + + for (auto elem : CSDMappings) { + sp value; + if (msg->findBuffer(elem.first, &value)) { + meta->setData(elem.second, + MetaDataBase::Type::TYPE_NONE, value->data(), value->size()); + } + } } void convertMetaDataToMessageFromMappings(const MetaDataBase *meta, sp format) { @@ -721,6 +737,18 @@ void convertMetaDataToMessageFromMappings(const MetaDataBase *meta, sp format->setBuffer(elem.first, buf); } } + + for (auto elem : CSDMappings) { + uint32_t type; + const void* data; + size_t size; + if (meta->findData(elem.second, &type, &data, &size)) { + sp buf = ABuffer::CreateAsCopy(data, size); + buf->meta()->setInt32("csd", true); + buf->meta()->setInt64("timeUs", 0); + format->setBuffer(elem.first, buf); + } + } } status_t convertMetaDataToMessage( @@ -1248,30 +1276,6 @@ status_t convertMetaDataToMessage( } else if (meta->findData(kKeyD263, &type, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; parseH263ProfileLevelFromD263(ptr, size, msg); - } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { - sp buffer = new (std::nothrow) ABuffer(size); - if (buffer.get() == NULL || buffer->base() == NULL) { - return NO_MEMORY; - } - memcpy(buffer->data(), data, size); - - buffer->meta()->setInt32("csd", true); - buffer->meta()->setInt64("timeUs", 0); - msg->setBuffer("csd-0", buffer); - - if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { - return -EINVAL; - } - - buffer = new (std::nothrow) ABuffer(size); - if (buffer.get() == NULL || buffer->base() == NULL) { - return NO_MEMORY; - } - memcpy(buffer->data(), data, size); - - buffer->meta()->setInt32("csd", true); - buffer->meta()->setInt64("timeUs", 0); - msg->setBuffer("csd-1", buffer); } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) { sp buffer = new (std::nothrow) ABuffer(size); if (buffer.get() == NULL || buffer->base() == NULL) { @@ -1310,16 +1314,6 @@ status_t convertMetaDataToMessage( buffer->meta()->setInt32("csd", true); buffer->meta()->setInt64("timeUs", 0); msg->setBuffer("csd-2", buffer); - } else if (meta->findData(kKeyFlacMetadata, &type, &data, &size)) { - sp buffer = new (std::nothrow) ABuffer(size); - if (buffer.get() == NULL || buffer->base() == NULL) { - return NO_MEMORY; - } - memcpy(buffer->data(), data, size); - - buffer->meta()->setInt32("csd", true); - buffer->meta()->setInt64("timeUs", 0); - msg->setBuffer("csd-0", buffer); } else if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) { sp buffer = new (std::nothrow) ABuffer(size); if (buffer.get() == NULL || buffer->base() == NULL) { @@ -1796,11 +1790,6 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { if (seekPreRollBuf) { meta->setData(kKeyOpusSeekPreRoll, 0, seekPreRollBuf, seekPreRollBufSize); } - } else if (mime == MEDIA_MIMETYPE_AUDIO_VORBIS) { - meta->setData(kKeyVorbisInfo, 0, csd0->data(), csd0->size()); - if (msg->findBuffer("csd-1", &csd1)) { - meta->setData(kKeyVorbisBooks, 0, csd1->data(), csd1->size()); - } } else if (mime == MEDIA_MIMETYPE_AUDIO_ALAC) { meta->setData(kKeyAlacMagicCookie, 0, csd0->data(), csd0->size()); } diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index a0407af595..75fd0d9032 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -67,7 +67,6 @@ enum { kKeyOpusHeader = 'ohdr', // raw data kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns) kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns) - kKeyFlacMetadata = 'flMd', // raw data kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information) kKeyIsSyncFrame = 'sync', // int32_t (bool) kKeyIsCodecConfig = 'conf', // int32_t (bool) @@ -234,6 +233,11 @@ enum { // AC-4 AudioPresentationInfo kKeyAudioPresentationInfo = 'audP', // raw data + + // opaque codec specific data being passed from extractor to codec + kKeyOpaqueCSD0 = 'csd0', + kKeyOpaqueCSD1 = 'csd1', + kKeyOpaqueCSD2 = 'csd2', }; enum { -- GitLab From 95ba0164b3fd9b2a64870026913ea6c71b11e5f6 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 19 Mar 2019 15:51:54 -0700 Subject: [PATCH 1139/1530] CCodec: fix MAX_PTS_GAP behavior - Fix MAX_PTS_GAP calculation to avoid undefined behavior. - Restore input timestamp for input surface use case. Bug: 128934821 Test: atest CtsMediaTestCases:SurfaceEncodeTimestampTest Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I381d9c5e2d097961388c04d7ef5e8aacd51624a2 --- media/codec2/sfplugin/CCodec.cpp | 14 ++++++++++---- media/codec2/sfplugin/CCodecBufferChannel.cpp | 4 ++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 2d10c6724c..5f60378f77 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -271,9 +271,12 @@ public: if (mNode != nullptr) { OMX_PARAM_U32TYPE ptrGapParam = {}; ptrGapParam.nSize = sizeof(OMX_PARAM_U32TYPE); - ptrGapParam.nU32 = (config.mMinAdjustedFps > 0) + float gap = (config.mMinAdjustedFps > 0) ? c2_min(INT32_MAX + 0., 1e6 / config.mMinAdjustedFps + 0.5) : c2_max(0. - INT32_MAX, -1e6 / config.mFixedAdjustedFps - 0.5); + // float -> uint32_t is undefined if the value is negative. + // First convert to int32_t to ensure the expected behavior. + ptrGapParam.nU32 = int32_t(gap); (void)mNode->setParameter( (OMX_INDEXTYPE)OMX_IndexParamMaxFrameDurationForBitrateControl, &ptrGapParam, sizeof(ptrGapParam)); @@ -282,7 +285,7 @@ public: // max fps // TRICKY: we do not unset max fps to 0 unless using fixed fps - if ((config.mMaxFps > 0 || (config.mFixedAdjustedFps > 0 && config.mMaxFps == 0)) + if ((config.mMaxFps > 0 || (config.mFixedAdjustedFps > 0 && config.mMaxFps == -1)) && config.mMaxFps != mConfig.mMaxFps) { status_t res = GetStatus(mSource->setMaxFps(config.mMaxFps)); status << " maxFps=" << config.mMaxFps; @@ -764,13 +767,16 @@ void CCodec::configure(const sp &msg) { if (msg->findInt64(KEY_REPEAT_PREVIOUS_FRAME_AFTER, &value) && value > 0) { config->mISConfig->mMinFps = 1e6 / value; } - (void)msg->findFloat( - KEY_MAX_FPS_TO_ENCODER, &config->mISConfig->mMaxFps); + if (!msg->findFloat( + KEY_MAX_FPS_TO_ENCODER, &config->mISConfig->mMaxFps)) { + config->mISConfig->mMaxFps = -1; + } config->mISConfig->mMinAdjustedFps = 0; config->mISConfig->mFixedAdjustedFps = 0; if (msg->findInt64(KEY_MAX_PTS_GAP_TO_ENCODER, &value)) { if (value < 0 && value >= INT32_MIN) { config->mISConfig->mFixedAdjustedFps = -1e6 / value; + config->mISConfig->mMaxFps = -1; } else if (value > 0 && value <= INT32_MAX) { config->mISConfig->mMinAdjustedFps = 1e6 / value; } diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index fb6af9307e..7d0a02128f 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -2709,6 +2709,10 @@ bool CCodecBufferChannel::handleWork( c2_cntr64_t timestamp = worklet->output.ordinal.timestamp + work->input.ordinal.customOrdinal - work->input.ordinal.timestamp; + if (mInputSurface != nullptr) { + // When using input surface we need to restore the original input timestamp. + timestamp = work->input.ordinal.customOrdinal; + } ALOGV("[%s] onWorkDone: input %lld, codec %lld => output %lld => %lld", mName, work->input.ordinal.customOrdinal.peekll(), -- GitLab From c76cdaebfd41d5be267a00a835545cc45d94bc7d Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 12 Mar 2019 10:56:36 -0700 Subject: [PATCH 1140/1530] codec2: Remove unnecessary symbol exports from components This reduces library size by about 10-15% Bug: 128423900 Test: cts-tradefed run commandAndExit cts-dev -m CtsMediaTestCases \ -t android.media.cts.DecoderTest Change-Id: I52bf6ee760fe5f29c628a66fc95c657fba2d1541 --- media/codec2/components/base/Android.bp | 7 ++++++- media/codec2/components/base/exports.lds | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 media/codec2/components/base/exports.lds diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp index 78a444bdbc..f10835f366 100644 --- a/media/codec2/components/base/Android.bp +++ b/media/codec2/components/base/Android.bp @@ -36,13 +36,18 @@ cc_library_shared { ldflags: ["-Wl,-Bsymbolic"], } +filegroup { + name: "codec2_soft_exports", + srcs: [ "exports.lds" ], +} + // public dependency for software codec implementation // to be used by code under media/codecs/* only as its stability is not guaranteed cc_defaults { name: "libcodec2_soft-defaults", defaults: ["libcodec2-impl-defaults"], vendor_available: true, - + version_script: ":codec2_soft_exports", export_shared_lib_headers: [ "libsfplugin_ccodec_utils", ], diff --git a/media/codec2/components/base/exports.lds b/media/codec2/components/base/exports.lds new file mode 100644 index 0000000000..641bae88c0 --- /dev/null +++ b/media/codec2/components/base/exports.lds @@ -0,0 +1,7 @@ +{ + global: + CreateCodec2Factory; + DestroyCodec2Factory; + local: *; +}; + -- GitLab From 128a767f20f6c44128c3f2283d5026202fa4707d Mon Sep 17 00:00:00 2001 From: dimitry Date: Wed, 20 Mar 2019 13:42:18 +0100 Subject: [PATCH 1141/1530] Add dependency on libdl_android platform private symbols have been moved to libdl_android.so since they are not intended to be accessible from apps. Test: make Change-Id: I2ad02e0a6cdbf5b9aba323523dd56a2c378e72f0 --- media/libstagefright/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index dc51b16164..da35889911 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -181,6 +181,7 @@ cc_library { "libcamera_client", "libcutils", "libdl", + "libdl_android", "libdrmframework", "libgui", "liblog", -- GitLab From 76cffe24dba78b36aff7070618e373b6c00d4c79 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Wed, 20 Mar 2019 09:17:17 -0700 Subject: [PATCH 1142/1530] Fix implicit-fallthrough warnings. Placeholder empty commit to avoid auto-merger merging the change to internal master. Test: N/A Bug: 112564944 Change-Id: Iab132093acb3a57dcecf055773d6dabaafa43635 -- GitLab From 24810e7a6725e9c3275bd9a8115a3049a73685d1 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 18 Mar 2019 10:55:01 -0700 Subject: [PATCH 1143/1530] Camera: Add isSessionConfigurationSupported in NDK/VNDK Test: NativeCameraDeviceTest and AImageReaderVendorTest Bug: 128933069 Change-Id: I26ca4c0ca12f7bd1b872c2f33e8fa63a056ae068 --- camera/ndk/NdkCameraDevice.cpp | 13 +++++ camera/ndk/impl/ACameraDevice.cpp | 49 +++++++++++++++++++ camera/ndk/impl/ACameraDevice.h | 9 ++++ camera/ndk/include/camera/NdkCameraDevice.h | 37 ++++++++++++++ camera/ndk/include/camera/NdkCameraError.h | 8 ++- camera/ndk/libcamera2ndk.map.txt | 1 + camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 42 ++++++++++++++++ camera/ndk/ndk_vendor/impl/ACameraDevice.h | 8 +++ .../tests/AImageReaderVendorTest.cpp | 6 +++ .../camera/libcameraservice/hidl/Convert.cpp | 15 ++++++ .../camera/libcameraservice/hidl/Convert.h | 4 ++ .../hidl/HidlCameraDeviceUser.cpp | 13 +++++ .../hidl/HidlCameraDeviceUser.h | 5 ++ 13 files changed, 209 insertions(+), 1 deletion(-) diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp index 09b85d5490..691996bdbf 100644 --- a/camera/ndk/NdkCameraDevice.cpp +++ b/camera/ndk/NdkCameraDevice.cpp @@ -287,3 +287,16 @@ camera_status_t ACameraDevice_createCaptureSessionWithSessionParameters( } return device->createCaptureSession(outputs, sessionParameters, callbacks, session); } + +EXPORT +camera_status_t ACameraDevice_isSessionConfigurationSupported( + const ACameraDevice* device, + const ACaptureSessionOutputContainer* sessionOutputContainer) { + ATRACE_CALL(); + if (device == nullptr || sessionOutputContainer == nullptr) { + ALOGE("%s: Error: invalid input: device %p, sessionOutputContainer %p", + __FUNCTION__, device, sessionOutputContainer); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + return device->isSessionConfigurationSupported(sessionOutputContainer); +} diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 5e4fcd07fd..c9db01e0bd 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -227,6 +227,55 @@ CameraDevice::createCaptureSession( return ACAMERA_OK; } +camera_status_t CameraDevice::isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const { + Mutex::Autolock _l(mDeviceLock); + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + + SessionConfiguration sessionConfiguration(0 /*inputWidth*/, 0 /*inputHeight*/, + -1 /*inputFormat*/, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE); + for (const auto& output : sessionOutputContainer->mOutputs) { + sp iGBP(nullptr); + ret = getIGBPfromAnw(output.mWindow, iGBP); + if (ret != ACAMERA_OK) { + ALOGE("Camera device %s failed to extract graphic producer from native window", + getId()); + return ret; + } + + String16 physicalId16(output.mPhysicalCameraId.c_str()); + OutputConfiguration outConfig(iGBP, output.mRotation, physicalId16, + OutputConfiguration::INVALID_SET_ID, true); + + for (auto& anw : output.mSharedWindows) { + ret = getIGBPfromAnw(anw, iGBP); + if (ret != ACAMERA_OK) { + ALOGE("Camera device %s failed to extract graphic producer from native window", + getId()); + return ret; + } + outConfig.addGraphicProducer(iGBP); + } + + sessionConfiguration.addOutputConfiguration(outConfig); + } + + bool supported = false; + binder::Status remoteRet = mRemote->isSessionConfigurationSupported( + sessionConfiguration, &supported); + if (remoteRet.serviceSpecificErrorCode() == + hardware::ICameraService::ERROR_INVALID_OPERATION) { + return ACAMERA_ERROR_UNSUPPORTED_OPERATION; + } else if (!remoteRet.isOk()) { + return ACAMERA_ERROR_UNKNOWN; + } else { + return supported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL; + } +} + camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { camera_status_t ret = checkCameraClosedOrErrorLocked(); if (ret != ACAMERA_OK) { diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 103efd5218..56741cef00 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,9 @@ class CameraDevice final : public RefBase { const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session); + camera_status_t isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const; + // Callbacks from camera service class ServiceCallback : public hardware::camera2::BnCameraDeviceCallbacks { public: @@ -369,6 +373,11 @@ struct ACameraDevice { return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session); } + camera_status_t isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const { + return mDevice->isSessionConfigurationSupported(sessionOutputContainer); + } + /*********************** * Device interal APIs * ***********************/ diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index cedf83a1a5..bc544e3034 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -845,6 +845,43 @@ camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds( const ACameraIdList* physicalIdList, /*out*/ACaptureRequest** request) __INTRODUCED_IN(29); +/** + * Check whether a particular {@ACaptureSessionOutputContainer} is supported by + * the camera device. + * + *

    This method performs a runtime check of a given {@link + * ACaptureSessionOutputContainer}. The result confirms whether or not the + * passed CaptureSession outputs can be successfully used to create a camera + * capture session using {@link ACameraDevice_createCaptureSession}.

    + * + *

    This method can be called at any point before, during and after active + * capture session. It must not impact normal camera behavior in any way and + * must complete significantly faster than creating a capture session.

    + * + *

    Although this method is faster than creating a new capture session, it is not intended + * to be used for exploring the entire space of supported stream combinations.

    + * + * @param device the camera device of interest + * @param sessionOutputContainer the {@link ACaptureSessionOutputContainer} of + * interest. + * + * @return
      + *
    • {@link ACAMERA_OK} if the given {@link ACaptureSessionOutputContainer} + * is supported by the camera device.
    • + *
    • {@link ACAMERA_ERROR_INVALID_PARAMETER} if device, or sessionOutputContainer + * is NULL.
    • + *
    • {@link ACAMERA_ERROR_STREAM_CONFIGURE_FAIL} if the given + * {@link ACaptureSessionOutputContainer} + * is not supported by + * the camera + * 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, + const ACaptureSessionOutputContainer* sessionOutputContainer) __INTRODUCED_IN(29); + #endif /* __ANDROID_API__ >= 29 */ __END_DECLS diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h index 6b5815502b..fc618eee50 100644 --- a/camera/ndk/include/camera/NdkCameraError.h +++ b/camera/ndk/include/camera/NdkCameraError.h @@ -106,7 +106,8 @@ typedef enum { /** * Camera device does not support the stream configuration provided by application in - * {@link ACameraDevice_createCaptureSession}. + * {@link ACameraDevice_createCaptureSession} or {@link + * ACameraDevice_isSessionConfigurationSupported}. */ ACAMERA_ERROR_STREAM_CONFIGURE_FAIL = ACAMERA_ERROR_BASE - 9, @@ -130,6 +131,11 @@ typedef enum { * The application does not have permission to open camera. */ ACAMERA_ERROR_PERMISSION_DENIED = ACAMERA_ERROR_BASE - 13, + + /** + * The operation is not supported by the camera device. + */ + ACAMERA_ERROR_UNSUPPORTED_OPERATION = ACAMERA_ERROR_BASE - 14, } camera_status_t; #endif /* __ANDROID_API__ >= 24 */ diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index 946a98eff3..b6f1553ee3 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -14,6 +14,7 @@ LIBCAMERA2NDK { ACameraDevice_createCaptureRequest_withPhysicalIds; # introduced=29 ACameraDevice_createCaptureSession; ACameraDevice_createCaptureSessionWithSessionParameters; # introduced=28 + ACameraDevice_isSessionConfigurationSupported; # introduced=29 ACameraDevice_getId; ACameraManager_create; ACameraManager_delete; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index a38a31e79c..d7d774b727 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -38,6 +38,7 @@ namespace acam { using HCameraMetadata = frameworks::cameraservice::device::V2_0::CameraMetadata; using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration; +using SessionConfiguration = frameworks::cameraservice::device::V2_0::SessionConfiguration; using hardware::Void; // Static member definitions @@ -216,6 +217,47 @@ CameraDevice::createCaptureSession( return ACAMERA_OK; } +camera_status_t CameraDevice::isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const { + Mutex::Autolock _l(mDeviceLock); + camera_status_t ret = checkCameraClosedOrErrorLocked(); + if (ret != ACAMERA_OK) { + return ret; + } + + SessionConfiguration sessionConfig; + sessionConfig.inputWidth = 0; + sessionConfig.inputHeight = 0; + sessionConfig.inputFormat = -1; + sessionConfig.operationMode = StreamConfigurationMode::NORMAL_MODE; + sessionConfig.outputStreams.resize(sessionOutputContainer->mOutputs.size()); + size_t index = 0; + for (const auto& output : sessionOutputContainer->mOutputs) { + sessionConfig.outputStreams[index].rotation = utils::convertToHidl(output.mRotation); + sessionConfig.outputStreams[index].windowGroupId = -1; + sessionConfig.outputStreams[index].windowHandles.resize(output.mSharedWindows.size() + 1); + sessionConfig.outputStreams[index].windowHandles[0] = output.mWindow; + sessionConfig.outputStreams[index].physicalCameraId = output.mPhysicalCameraId; + index++; + } + + bool configSupported = false; + Status status = Status::NO_ERROR; + auto remoteRet = mRemote->isSessionConfigurationSupported(sessionConfig, + [&status, &configSupported](auto s, auto supported) { + status = s; + configSupported = supported; + }); + + if (status == Status::INVALID_OPERATION) { + return ACAMERA_ERROR_UNSUPPORTED_OPERATION; + } else if (!remoteRet.isOk()) { + return ACAMERA_ERROR_UNKNOWN; + } else { + return configSupported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL; + } +} + void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, sp &req) { CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData(); diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 28092fd373..47e6f56ef8 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -101,6 +101,9 @@ class CameraDevice final : public RefBase { const ACameraCaptureSession_stateCallbacks* callbacks, /*out*/ACameraCaptureSession** session); + camera_status_t isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const; + // Callbacks from camera service class ServiceCallback : public ICameraDeviceCallback { public: @@ -397,6 +400,11 @@ struct ACameraDevice { return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session); } + camera_status_t isSessionConfigurationSupported( + const ACaptureSessionOutputContainer* sessionOutputContainer) const { + return mDevice->isSessionConfigurationSupported(sessionOutputContainer); + } + /*********************** * Device interal APIs * ***********************/ diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 2398922048..c51f93b198 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -121,6 +121,12 @@ class CameraHelper { cameraIdList.numCameras = idPointerList.size(); cameraIdList.cameraIds = idPointerList.data(); + ret = ACameraDevice_isSessionConfigurationSupported(mDevice, mOutputs); + if (ret != ACAMERA_OK && ret != ACAMERA_ERROR_UNSUPPORTED_OPERATION) { + ALOGE("ACameraDevice_isSessionConfigurationSupported failed, ret=%d", ret); + return ret; + } + ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession); if (ret != AMEDIA_OK) { ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret); diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index a87812b6b4..c2ed23a61b 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -97,6 +97,21 @@ hardware::camera2::params::OutputConfiguration convertFromHidl( return outputConfiguration; } +hardware::camera2::params::SessionConfiguration convertFromHidl( + const HSessionConfiguration &hSessionConfiguration) { + hardware::camera2::params::SessionConfiguration sessionConfig( + hSessionConfiguration.inputWidth, hSessionConfiguration.inputHeight, + hSessionConfiguration.inputFormat, + static_cast(hSessionConfiguration.operationMode)); + + for (const auto& hConfig : hSessionConfiguration.outputStreams) { + hardware::camera2::params::OutputConfiguration config = convertFromHidl(hConfig); + sessionConfig.addOutputConfiguration(config); + } + + return sessionConfig; +} + // The camera metadata here is cloned. Since we're reading metadata over // hwbinder we would need to clone it in order to avoid aligment issues. bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst) { diff --git a/services/camera/libcameraservice/hidl/Convert.h b/services/camera/libcameraservice/hidl/Convert.h index 82937a30d6..79683f6b00 100644 --- a/services/camera/libcameraservice/hidl/Convert.h +++ b/services/camera/libcameraservice/hidl/Convert.h @@ -53,6 +53,7 @@ using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::IGraphicBu using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration; using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings; using HPhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo; +using HSessionConfiguration = frameworks::cameraservice::device::V2_0::SessionConfiguration; using HSubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo; using HStatus = frameworks::cameraservice::common::V2_0::Status; using HStreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode; @@ -70,6 +71,9 @@ bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst); hardware::camera2::params::OutputConfiguration convertFromHidl( const HOutputConfiguration &hOutputConfiguration); +hardware::camera2::params::SessionConfiguration convertFromHidl( + const HSessionConfiguration &hSessionConfiguration); + HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status); void convertToHidl(const std::vector &src, diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp index d22ba5aa61..675ad24bcf 100644 --- a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp +++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp @@ -41,6 +41,7 @@ using hardware::Return; using hardware::Void; using HSubmitInfo = device::V2_0::SubmitInfo; using hardware::camera2::params::OutputConfiguration; +using hardware::camera2::params::SessionConfiguration; static constexpr int32_t CAMERA_REQUEST_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */; static constexpr int32_t CAMERA_RESULT_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */; @@ -255,6 +256,18 @@ Return HidlCameraDeviceUser::updateOutputConfiguration( return B2HStatus(ret); } +Return HidlCameraDeviceUser::isSessionConfigurationSupported( + const HSessionConfiguration& hSessionConfiguration, + isSessionConfigurationSupported_cb _hidl_cb) { + bool supported = false; + SessionConfiguration sessionConfiguration = convertFromHidl(hSessionConfiguration); + binder::Status ret = mDeviceRemote->isSessionConfigurationSupported( + sessionConfiguration, &supported); + HStatus status = B2HStatus(ret); + _hidl_cb(status, supported); + return Void(); +} + } // implementation } // V2_0 } // device diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h index be8f1d63c5..c3a80fe9e0 100644 --- a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h +++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h @@ -53,6 +53,7 @@ using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId; using HCameraDeviceUser = device::V2_0::ICameraDeviceUser; using HCameraMetadata = cameraservice::service::V2_0::CameraMetadata; using HCaptureRequest = device::V2_0::CaptureRequest; +using HSessionConfiguration = frameworks::cameraservice::device::V2_0::SessionConfiguration; using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration; using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings; using HStatus = frameworks::cameraservice::common::V2_0::Status; @@ -97,6 +98,10 @@ struct HidlCameraDeviceUser final : public HCameraDeviceUser { virtual Return updateOutputConfiguration( int32_t streamId, const HOutputConfiguration& outputConfiguration) override; + virtual Return isSessionConfigurationSupported( + const HSessionConfiguration& sessionConfiguration, + isSessionConfigurationSupported_cb _hidl_cb) override; + bool initStatus() { return mInitSuccess; } std::shared_ptr getCaptureResultMetadataQueue() { -- GitLab From 7b66e3b61a2e14f971334dbbe41d132f6f40623b Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Fri, 8 Feb 2019 15:18:54 +0530 Subject: [PATCH 1144/1530] libeffects: Added multichannel support for volume balance Test: local native test (lvmtest) Bug: 120941390 Change-Id: If982e03a57fb3a1e03d67f531c615bc016e7f3a5 --- media/libeffects/lvm/lib/Android.bp | 3 + media/libeffects/lvm/lib/Bundle/lib/LVM.h | 1 + .../lvm/lib/Bundle/src/LVM_Control.c | 2 + .../lvm/lib/Bundle/src/LVM_Private.h | 1 + .../lvm/lib/Bundle/src/LVM_Process.c | 32 ++- .../src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c | 25 +++ .../src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c | 45 ++++ .../src/LVC_MixSoft_1St_2i_D16C31_SAT.c | 199 ++++++++++++++++++ .../libeffects/lvm/lib/Common/src/LVC_Mixer.h | 8 + .../lvm/lib/Common/src/LVC_Mixer_Private.h | 14 ++ .../lvm/tests/build_and_run_all_unit_tests.sh | 20 +- media/libeffects/lvm/tests/lvmtest.cpp | 92 ++++++-- .../lvm/wrapper/Bundle/EffectBundle.cpp | 1 + 13 files changed, 411 insertions(+), 32 deletions(-) diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp index 7a32d3f9a8..d150f18558 100644 --- a/media/libeffects/lvm/lib/Android.bp +++ b/media/libeffects/lvm/lib/Android.bp @@ -132,6 +132,9 @@ cc_library_static { shared_libs: [ "liblog", ], + header_libs: [ + "libhardware_headers" + ], cflags: [ "-fvisibility=hidden", "-DBUILD_FLOAT", diff --git a/media/libeffects/lvm/lib/Bundle/lib/LVM.h b/media/libeffects/lvm/lib/Bundle/lib/LVM.h index 83ecae1477..5082a53272 100644 --- a/media/libeffects/lvm/lib/Bundle/lib/LVM.h +++ b/media/libeffects/lvm/lib/Bundle/lib/LVM.h @@ -298,6 +298,7 @@ typedef struct LVM_PSA_DecaySpeed_en PSA_PeakDecayRate; /* Peak value decay rate*/ #ifdef SUPPORT_MC LVM_INT32 NrChannels; + LVM_INT32 ChMask; #endif } LVM_ControlParams_t; diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c index 62b4c7360b..1d953420c4 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c @@ -93,6 +93,7 @@ LVM_ReturnStatus_en LVM_SetControlParameters(LVM_Handle_t hInstance, #ifdef SUPPORT_MC pInstance->Params.NrChannels = pParams->NrChannels; + pInstance->Params.ChMask = pParams->ChMask; #endif /* * Cinema Sound parameters @@ -584,6 +585,7 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) #ifdef SUPPORT_MC pInstance->NrChannels = LocalParams.NrChannels; + pInstance->ChMask = LocalParams.ChMask; #endif /* Clear all internal data if format change*/ diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h index 19d15328ac..cdd3134bd2 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Private.h @@ -291,6 +291,7 @@ typedef struct #ifdef SUPPORT_MC LVM_INT16 NrChannels; + LVM_INT32 ChMask; #endif } LVM_Instance_t; diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c index 94ba278dc2..8d30a61544 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c @@ -21,6 +21,7 @@ /* Includes */ /* */ /****************************************************************************************/ +#include #include "LVM_Private.h" #include "VectorArithmetic.h" @@ -67,6 +68,7 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, LVM_ReturnStatus_en Status; #ifdef SUPPORT_MC LVM_INT32 NrChannels = pInstance->NrChannels; + LVM_INT32 ChMask = pInstance->ChMask; #define NrFrames SampleCount // alias for clarity #endif @@ -119,6 +121,7 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, #ifdef SUPPORT_MC /* Update the local variable NrChannels from pInstance->NrChannels value */ NrChannels = pInstance->NrChannels; + ChMask = pInstance->ChMask; #endif if(Status != LVM_SUCCESS) @@ -140,6 +143,7 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, pToProcess = pOutData; #ifdef SUPPORT_MC NrChannels = 2; + ChMask = AUDIO_CHANNEL_OUT_STEREO; #endif } @@ -254,18 +258,24 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, } #ifdef SUPPORT_MC - /* TODO - Multichannel support to be added */ - if (NrChannels == 2) + /* + * Volume balance + */ + LVC_MixSoft_1St_MC_float_SAT(&pInstance->VC_BalanceMix, + pProcessed, + pProcessed, + NrFrames, + NrChannels, + ChMask); +#else + /* + * Volume balance + */ + LVC_MixSoft_1St_2i_D16C31_SAT(&pInstance->VC_BalanceMix, + pProcessed, + pProcessed, + SampleCount); #endif - { - /* - * Volume balance - */ - LVC_MixSoft_1St_2i_D16C31_SAT(&pInstance->VC_BalanceMix, - pProcessed, - pProcessed, - SampleCount); - } /* * Perform Parametric Spectum Analysis diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c index eb5755e2ac..db76cd113a 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c @@ -59,6 +59,31 @@ void LVC_Core_MixHard_1St_2i_D16C31_SAT( LVMixer3_FLOAT_st *ptrInstance1, } +#ifdef SUPPORT_MC +void LVC_Core_MixHard_1St_MC_float_SAT (Mix_Private_FLOAT_st **ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + LVM_FLOAT Temp; + LVM_INT16 ii, jj; + for (ii = NrFrames; ii != 0; ii--) + { + for (jj = 0; jj < NrChannels; jj++) + { + Mix_Private_FLOAT_st *pInstance1 = (Mix_Private_FLOAT_st *)(ptrInstance[jj]); + Temp = ((LVM_FLOAT)*(src++) * (LVM_FLOAT)pInstance1->Current); + if (Temp > 1.0f) + *dst++ = 1.0f; + else if (Temp < -1.0f) + *dst++ = -1.0f; + else + *dst++ = (LVM_FLOAT)Temp; + } + } +} +#endif #else void LVC_Core_MixHard_1St_2i_D16C31_SAT( LVMixer3_st *ptrInstance1, LVMixer3_st *ptrInstance2, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c index 656a117044..56b5dae803 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c @@ -146,6 +146,51 @@ void LVC_Core_MixSoft_1St_2i_D16C31_WRA( LVMixer3_FLOAT_st *ptrInstance1, pInstanceR->Current = CurrentR; } +#ifdef SUPPORT_MC +void LVC_Core_MixSoft_1St_MC_float_WRA (Mix_Private_FLOAT_st **ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels) +{ + LVM_INT32 ii, ch; + LVM_FLOAT Temp =0.0f; + LVM_FLOAT tempCurrent[NrChannels]; + for (ch = 0; ch < NrChannels; ch++) + { + tempCurrent[ch] = ptrInstance[ch]->Current; + } + for (ii = NrFrames; ii > 0; ii--) + { + for (ch = 0; ch < NrChannels; ch++) + { + Mix_Private_FLOAT_st *pInstance = ptrInstance[ch]; + const LVM_FLOAT Delta = pInstance->Delta; + LVM_FLOAT Current = tempCurrent[ch]; + const LVM_FLOAT Target = pInstance->Target; + if (Current < Target) + { + ADD2_SAT_FLOAT(Current, Delta, Temp); + Current = Temp; + if (Current > Target) + Current = Target; + } + else + { + Current -= Delta; + if (Current < Target) + Current = Target; + } + *dst++ = *src++ * Current; + tempCurrent[ch] = Current; + } + } + for (ch = 0; ch < NrChannels; ch++) + { + ptrInstance[ch]->Current = tempCurrent[ch]; + } +} +#endif #else void LVC_Core_MixSoft_1St_2i_D16C31_WRA( LVMixer3_st *ptrInstance1, LVMixer3_st *ptrInstance2, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c index bd5a925c24..a4682d3712 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c +++ b/media/libeffects/lvm/lib/Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c @@ -19,6 +19,8 @@ INCLUDE FILES ***********************************************************************************/ +#include + #include "LVC_Mixer_Private.h" #include "VectorArithmetic.h" #include "ScalarArithmetic.h" @@ -30,10 +32,207 @@ #define TRUE 1 #define FALSE 0 +#define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof(*(a)))) + /********************************************************************************** FUNCTION LVC_MixSoft_1St_2i_D16C31_SAT ***********************************************************************************/ #ifdef BUILD_FLOAT +#ifdef SUPPORT_MC +/* This threshold is used to decide on the processing to be applied on + * front center and back center channels + */ +#define LVM_VOL_BAL_THR (0.000016f) +void LVC_MixSoft_1St_MC_float_SAT (LVMixer3_2St_FLOAT_st *ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT32 NrChannels, + LVM_INT32 ChMask) +{ + char HardMixing = TRUE; + LVM_FLOAT TargetGain; + Mix_Private_FLOAT_st Target_lfe = {LVM_MAXFLOAT, LVM_MAXFLOAT, LVM_MAXFLOAT}; + Mix_Private_FLOAT_st Target_ctr = {LVM_MAXFLOAT, LVM_MAXFLOAT, LVM_MAXFLOAT}; + Mix_Private_FLOAT_st *pInstance1 = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[0].PrivateParams); + Mix_Private_FLOAT_st *pInstance2 = \ + (Mix_Private_FLOAT_st *)(ptrInstance->MixerStream[1].PrivateParams); + Mix_Private_FLOAT_st *pMixPrivInst[4] = {pInstance1, pInstance2, &Target_ctr, &Target_lfe}; + Mix_Private_FLOAT_st *pInstance[NrChannels]; + + if (audio_channel_mask_get_representation(ChMask) + == AUDIO_CHANNEL_REPRESENTATION_INDEX) + { + for (int i = 0; i < 2; i++) + { + pInstance[i] = pMixPrivInst[i]; + } + for (int i = 2; i < NrChannels; i++) + { + pInstance[i] = pMixPrivInst[2]; + } + } + else + { + // TODO: Combine with system/media/audio_utils/Balance.cpp + // Constants in system/media/audio/include/system/audio-base.h + // 'mixInstIdx' is used to map the appropriate mixer instance for each channel. + const int mixInstIdx[] = { + 0, // AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1u, + 1, // AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2u, + 2, // AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4u, + 3, // AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8u, + 0, // AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10u, + 1, // AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20u, + 0, // AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40u, + 1, // AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80u, + 2, // AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100u, + 0, // AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200u, + 1, // AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400u, + 2, // AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800u, + 0, // AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000u, + 2, // AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000u, + 1, // AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000u, + 0, // AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000u, + 2, // AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000u, + 1, // AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000u, + 0, // AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT = 0x40000u, + 1, // AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT = 0x80000u + }; + if (pInstance1->Target <= LVM_VOL_BAL_THR || + pInstance2->Target <= LVM_VOL_BAL_THR) + { + Target_ctr.Target = 0.0f; + Target_ctr.Current = 0.0f; + Target_ctr.Delta = 0.0f; + } + const unsigned int idxArrSize = ARRAY_SIZE(mixInstIdx); + for (unsigned int i = 0, channel = ChMask; channel !=0 ; ++i) + { + const unsigned int idx = __builtin_ctz(channel); + if (idx < idxArrSize) + { + pInstance[i] = pMixPrivInst[mixInstIdx[idx]]; + } + else + { + pInstance[i] = pMixPrivInst[2]; + } + channel &= ~(1 << idx); + } + } + + if (NrFrames <= 0) return; + + /****************************************************************************** + SOFT MIXING + *******************************************************************************/ + + if ((pInstance1->Current != pInstance1->Target) || + (pInstance2->Current != pInstance2->Target)) + { + // TODO: combine similar checks below. + if (pInstance1->Delta == LVM_MAXFLOAT + || Abs_Float(pInstance1->Current - pInstance1->Target) < pInstance1->Delta) + { + /* Difference is not significant anymore. Make them equal. */ + pInstance1->Current = pInstance1->Target; + TargetGain = pInstance1->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[0]), TargetGain); + } + else + { + /* Soft mixing has to be applied */ + HardMixing = FALSE; + } + + if (HardMixing == TRUE) + { + if (pInstance2->Delta == LVM_MAXFLOAT + || Abs_Float(pInstance2->Current - pInstance2->Target) < pInstance2->Delta) + { + /* Difference is not significant anymore. Make them equal. */ + pInstance2->Current = pInstance2->Target; + TargetGain = pInstance2->Target; + LVC_Mixer_SetTarget(&(ptrInstance->MixerStream[1]), TargetGain); + } + else + { + /* Soft mixing has to be applied */ + HardMixing = FALSE; + } + } + + if (HardMixing == FALSE) + { + LVC_Core_MixSoft_1St_MC_float_WRA (&pInstance[0], + src, dst, NrFrames, NrChannels); + } + } + + /****************************************************************************** + HARD MIXING + *******************************************************************************/ + + if (HardMixing == TRUE) + { + if ((pInstance1->Target == LVM_MAXFLOAT) && (pInstance2->Target == LVM_MAXFLOAT)) + { + if (src != dst) + { + Copy_Float(src, dst, NrFrames*NrChannels); + } + } + else + { + LVC_Core_MixHard_1St_MC_float_SAT(&(pInstance[0]), + src, dst, NrFrames, NrChannels); + } + } + + /****************************************************************************** + CALL BACK + *******************************************************************************/ + + if (ptrInstance->MixerStream[0].CallbackSet) + { + if (Abs_Float(pInstance1->Current - pInstance1->Target) < pInstance1->Delta) + { + pInstance1->Current = pInstance1->Target; /* Difference is not significant anymore. \ + Make them equal. */ + TargetGain = pInstance1->Target; + LVC_Mixer_SetTarget(&ptrInstance->MixerStream[0], TargetGain); + ptrInstance->MixerStream[0].CallbackSet = FALSE; + if (ptrInstance->MixerStream[0].pCallBack != 0) + { + (*ptrInstance->MixerStream[0].pCallBack) (\ + ptrInstance->MixerStream[0].pCallbackHandle, + ptrInstance->MixerStream[0].pGeneralPurpose, + ptrInstance->MixerStream[0].CallbackParam); + } + } + } + if (ptrInstance->MixerStream[1].CallbackSet) + { + if (Abs_Float(pInstance2->Current - pInstance2->Target) < pInstance2->Delta) + { + pInstance2->Current = pInstance2->Target; /* Difference is not significant anymore. + Make them equal. */ + TargetGain = pInstance2->Target; + LVC_Mixer_SetTarget(&ptrInstance->MixerStream[1], TargetGain); + ptrInstance->MixerStream[1].CallbackSet = FALSE; + if (ptrInstance->MixerStream[1].pCallBack != 0) + { + (*ptrInstance->MixerStream[1].pCallBack) (\ + ptrInstance->MixerStream[1].pCallbackHandle, + ptrInstance->MixerStream[1].pGeneralPurpose, + ptrInstance->MixerStream[1].CallbackParam); + } + } + } +} +#endif void LVC_MixSoft_1St_2i_D16C31_SAT( LVMixer3_2St_FLOAT_st *ptrInstance, const LVM_FLOAT *src, LVM_FLOAT *dst, diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h index 7f18747ed6..199d529dab 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer.h @@ -224,6 +224,14 @@ void LVC_MixSoft_2St_D16C31_SAT( LVMixer3_2St_st *pInstance, /* Gain values should not be more that 1.0 */ /**********************************************************************************/ #ifdef BUILD_FLOAT +#ifdef SUPPORT_MC +void LVC_MixSoft_1St_MC_float_SAT(LVMixer3_2St_FLOAT_st *pInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, /* dst can be equal to src */ + LVM_INT16 NrFrames, + LVM_INT32 NrChannels, + LVM_INT32 ChMask); +#endif void LVC_MixSoft_1St_2i_D16C31_SAT(LVMixer3_2St_FLOAT_st *pInstance, const LVM_FLOAT *src, LVM_FLOAT *dst, /* dst can be equal to src */ diff --git a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h index f10094bd3d..453a6a5325 100644 --- a/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h +++ b/media/libeffects/lvm/lib/Common/src/LVC_Mixer_Private.h @@ -116,6 +116,13 @@ void LVC_Core_MixHard_2St_D16C31_SAT( LVMixer3_st *pInstance1, /* Gain values should not be more that 1.0 */ /**********************************************************************************/ #ifdef BUILD_FLOAT +#ifdef SUPPORT_MC +void LVC_Core_MixSoft_1St_MC_float_WRA(Mix_Private_FLOAT_st **ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif void LVC_Core_MixSoft_1St_2i_D16C31_WRA( LVMixer3_FLOAT_st *ptrInstance1, LVMixer3_FLOAT_st *ptrInstance2, const LVM_FLOAT *src, @@ -136,6 +143,13 @@ void LVC_Core_MixSoft_1St_2i_D16C31_WRA( LVMixer3_st *ptrInstance1, /* Gain values should not be more that 1.0 */ /**********************************************************************************/ #ifdef BUILD_FLOAT +#ifdef SUPPORT_MC +void LVC_Core_MixHard_1St_MC_float_SAT(Mix_Private_FLOAT_st **ptrInstance, + const LVM_FLOAT *src, + LVM_FLOAT *dst, + LVM_INT16 NrFrames, + LVM_INT16 NrChannels); +#endif void LVC_Core_MixHard_1St_2i_D16C31_SAT( LVMixer3_FLOAT_st *ptrInstance1, LVMixer3_FLOAT_st *ptrInstance2, const LVM_FLOAT *src, diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 1a874a3f4f..fd215455e0 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -36,6 +36,10 @@ flags_arr=( "-csE -tE" "-csE -eqE" "-tE -eqE" "-csE -tE -bE -M -eqE" + "-tE -eqE -vcBal:96 -M" + "-tE -eqE -vcBal:-96 -M" + "-tE -eqE -vcBal:0 -M" + "-tE -eqE -bE -vcBal:30 -M" ) fs_arr=( @@ -60,22 +64,22 @@ for flags in "${flags_arr[@]}" do for fs in ${fs_arr[*]} do - for ch in {1..8} + for chMask in {0..22} do adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ - -o:$testdir/sinesweep_$((ch))_$((fs)).raw -ch:$ch -fs:$fs $flags + -o:$testdir/sinesweep_$((chMask))_$((fs)).raw -chMask:$chMask -fs:$fs $flags # two channel files should be identical to higher channel # computation (first 2 channels). # Do not compare cases where -bE is in flags (due to mono computation) - if [[ $flags != *"-bE"* ]] && [ "$ch" -gt 2 ] + if [[ $flags != *"-bE"* ]] && [[ "$chMask" -gt 1 ]] then - adb shell cmp $testdir/sinesweep_2_$((fs)).raw \ - $testdir/sinesweep_$((ch))_$((fs)).raw - elif [[ $flags == *"-bE"* ]] && [ "$ch" -gt 2 ] + adb shell cmp $testdir/sinesweep_1_$((fs)).raw \ + $testdir/sinesweep_$((chMask))_$((fs)).raw + elif [[ $flags == *"-bE"* ]] && [[ "$chMask" -gt 1 ]] then - adb shell $testdir/snr $testdir/sinesweep_2_$((fs)).raw \ - $testdir/sinesweep_$((ch))_$((fs)).raw -thr:90.308998 + adb shell $testdir/snr $testdir/sinesweep_1_$((fs)).raw \ + $testdir/sinesweep_$((chMask))_$((fs)).raw -thr:90.308998 fi done diff --git a/media/libeffects/lvm/tests/lvmtest.cpp b/media/libeffects/lvm/tests/lvmtest.cpp index 416bdaac9f..5b58dd1818 100644 --- a/media/libeffects/lvm/tests/lvmtest.cpp +++ b/media/libeffects/lvm/tests/lvmtest.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "EffectBundle.h" #include "LVM_Private.h" @@ -76,6 +77,8 @@ struct lvmConfigParams_t { int samplingFreq = 44100; int nrChannels = 2; + int chMask = AUDIO_CHANNEL_OUT_STEREO; + int vcBal = 0; int fChannels = 2; bool monoMode = false; int bassEffectLevel = 0; @@ -87,9 +90,36 @@ struct lvmConfigParams_t { LVM_Mode_en csEnable = LVM_MODE_OFF; }; +constexpr audio_channel_mask_t lvmConfigChMask[] = { + AUDIO_CHANNEL_OUT_MONO, + AUDIO_CHANNEL_OUT_STEREO, + AUDIO_CHANNEL_OUT_2POINT1, + AUDIO_CHANNEL_OUT_2POINT0POINT2, + AUDIO_CHANNEL_OUT_QUAD, + AUDIO_CHANNEL_OUT_QUAD_BACK, + AUDIO_CHANNEL_OUT_QUAD_SIDE, + AUDIO_CHANNEL_OUT_SURROUND, + (1 << 4) - 1, + AUDIO_CHANNEL_OUT_2POINT1POINT2, + AUDIO_CHANNEL_OUT_3POINT0POINT2, + AUDIO_CHANNEL_OUT_PENTA, + (1 << 5) - 1, + AUDIO_CHANNEL_OUT_3POINT1POINT2, + AUDIO_CHANNEL_OUT_5POINT1, + AUDIO_CHANNEL_OUT_5POINT1_BACK, + AUDIO_CHANNEL_OUT_5POINT1_SIDE, + (1 << 6) - 1, + AUDIO_CHANNEL_OUT_6POINT1, + (1 << 7) - 1, + AUDIO_CHANNEL_OUT_5POINT1POINT2, + AUDIO_CHANNEL_OUT_7POINT1, + (1 << 8) - 1, +}; + + void printUsage() { printf("\nUsage: "); - printf("\n -i: -o: [options]\n"); + printf("\n -i: -o: [options]\n"); printf("\nwhere, \n is the input file name"); printf("\n on which LVM effects are applied"); printf("\n processed output file"); @@ -98,7 +128,34 @@ void printUsage() { printf("\n -help (or) -h"); printf("\n Prints this usage information"); printf("\n"); - printf("\n -ch: (1 through 8)\n\n"); + printf("\n -chMask:\n"); + printf("\n 0 - AUDIO_CHANNEL_OUT_MONO"); + printf("\n 1 - AUDIO_CHANNEL_OUT_STEREO"); + printf("\n 2 - AUDIO_CHANNEL_OUT_2POINT1"); + printf("\n 3 - AUDIO_CHANNEL_OUT_2POINT0POINT2"); + printf("\n 4 - AUDIO_CHANNEL_OUT_QUAD"); + printf("\n 5 - AUDIO_CHANNEL_OUT_QUAD_BACK"); + printf("\n 6 - AUDIO_CHANNEL_OUT_QUAD_SIDE"); + printf("\n 7 - AUDIO_CHANNEL_OUT_SURROUND"); + printf("\n 8 - canonical channel index mask for 4 ch: (1 << 4) - 1"); + printf("\n 9 - AUDIO_CHANNEL_OUT_2POINT1POINT2"); + printf("\n 10 - AUDIO_CHANNEL_OUT_3POINT0POINT2"); + printf("\n 11 - AUDIO_CHANNEL_OUT_PENTA"); + printf("\n 12 - canonical channel index mask for 5 ch: (1 << 5) - 1"); + printf("\n 13 - AUDIO_CHANNEL_OUT_3POINT1POINT2"); + printf("\n 14 - AUDIO_CHANNEL_OUT_5POINT1"); + printf("\n 15 - AUDIO_CHANNEL_OUT_5POINT1_BACK"); + printf("\n 16 - AUDIO_CHANNEL_OUT_5POINT1_SIDE"); + printf("\n 17 - canonical channel index mask for 6 ch: (1 << 6) - 1"); + printf("\n 18 - AUDIO_CHANNEL_OUT_6POINT1"); + printf("\n 19 - canonical channel index mask for 7 ch: (1 << 7) - 1"); + printf("\n 20 - AUDIO_CHANNEL_OUT_5POINT1POINT2"); + printf("\n 21 - AUDIO_CHANNEL_OUT_7POINT1"); + printf("\n 22 - canonical channel index mask for 8 ch: (1 << 8) - 1"); + printf("\n default 0"); + printf("\n -vcBal:"); + printf("\n -ve values reduce Right channel while +ve value reduces Left channel"); + printf("\n default 0"); printf("\n -fch: (1 through 8)\n\n"); printf("\n -M"); printf("\n Mono mode (force all input audio channels to be identical)"); @@ -298,6 +355,7 @@ int LvmBundle_init(struct EffectContext *pContext, LVM_ControlParams_t *params) params->OperatingMode = LVM_MODE_ON; params->SampleRate = LVM_FS_44100; params->SourceFormat = LVM_STEREO; + params->ChMask = AUDIO_CHANNEL_OUT_STEREO; params->SpeakerType = LVM_HEADPHONES; pContext->pBundledContext->SampleRate = LVM_FS_44100; @@ -452,13 +510,13 @@ int lvmControl(struct EffectContext *pContext, params->OperatingMode = LVM_MODE_ON; params->SpeakerType = LVM_HEADPHONES; - const int nrChannels = plvmConfigParams->nrChannels; - params->NrChannels = nrChannels; - if (nrChannels == 1) { + params->ChMask = plvmConfigParams->chMask; + params->NrChannels = plvmConfigParams->nrChannels; + if (params->NrChannels == 1) { params->SourceFormat = LVM_MONO; - } else if (nrChannels == 2) { + } else if (params->NrChannels == 2) { params->SourceFormat = LVM_STEREO; - } else if (nrChannels > 2 && nrChannels <= 8) { // FCC_2 FCC_8 + } else if (params->NrChannels > 2 && params->NrChannels <= 8) { // FCC_2 FCC_8 params->SourceFormat = LVM_MULTICHANNEL; } else { return -EINVAL; @@ -531,7 +589,7 @@ int lvmControl(struct EffectContext *pContext, /* Volume Control parameters */ params->VC_EffectLevel = 0; - params->VC_Balance = 0; + params->VC_Balance = plvmConfigParams->vcBal; /* Treble Enhancement parameters */ params->TE_OperatingMode = plvmConfigParams->trebleEnable; @@ -667,13 +725,21 @@ int main(int argc, const char *argv[]) { return -1; } lvmConfigParams.samplingFreq = samplingFreq; - } else if (!strncmp(argv[i], "-ch:", 4)) { - const int nrChannels = atoi(argv[i] + 4); - if (nrChannels > 8 || nrChannels < 1) { - printf("Error: Unsupported number of channels : %d\n", nrChannels); + } else if (!strncmp(argv[i], "-chMask:", 8)) { + const int chMaskConfigIdx = atoi(argv[i] + 8); + if (chMaskConfigIdx < 0 || (size_t)chMaskConfigIdx >= std::size(lvmConfigChMask)) { + ALOGE("\nError: Unsupported Channel Mask : %d\n", chMaskConfigIdx); return -1; } - lvmConfigParams.nrChannels = nrChannels; + const audio_channel_mask_t chMask = lvmConfigChMask[chMaskConfigIdx]; + lvmConfigParams.chMask = chMask; + lvmConfigParams.nrChannels = audio_channel_count_from_out_mask(chMask); + } else if (!strncmp(argv[i], "-vcBal:", 7)) { + const int vcBalance = atoi(argv[i] + 7); + if (vcBalance > 96 || vcBalance < -96) { + ALOGE("\nError: Unsupported volume balance value: %d\n", vcBalance); + } + lvmConfigParams.vcBal = vcBalance; } else if (!strncmp(argv[i], "-fch:", 5)) { const int fChannels = atoi(argv[i] + 5); if (fChannels > 8 || fChannels < 1) { diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 0c6f8de3c0..3a97905d23 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -1315,6 +1315,7 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ #ifdef SUPPORT_MC ActiveParams.NrChannels = NrChannels; + ActiveParams.ChMask = pConfig->inputCfg.channels; #endif LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams); -- GitLab From 175fbd3859bbf02c69a8bf126feb8ea60d294660 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 18 Mar 2019 12:20:27 -0700 Subject: [PATCH 1145/1530] AudioEffect: explicitly show errors in lvmtest Test: instrumented test Change-Id: I406c23517ef401540fb497134a42abb1261d9225 --- .../lvm/tests/build_and_run_all_unit_tests.sh | 17 +++++++++++++++++ media/libeffects/lvm/tests/snr.cpp | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 1a874a3f4f..f2ffa22328 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -56,6 +56,7 @@ fs_arr=( # run multichannel effects at different configs, saving only the stereo channel # pair. +error_count=0 for flags in "${flags_arr[@]}" do for fs in ${fs_arr[*]} @@ -65,6 +66,13 @@ do adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ -o:$testdir/sinesweep_$((ch))_$((fs)).raw -ch:$ch -fs:$fs $flags + shell_ret=$? + if [ $shell_ret -ne 0 ]; then + echo "error: $shell_ret" + ((++error_count)) + fi + + # two channel files should be identical to higher channel # computation (first 2 channels). # Do not compare cases where -bE is in flags (due to mono computation) @@ -78,8 +86,17 @@ do $testdir/sinesweep_$((ch))_$((fs)).raw -thr:90.308998 fi + # both cmp and snr return EXIT_FAILURE on mismatch. + shell_ret=$? + if [ $shell_ret -ne 0 ]; then + echo "error: $shell_ret" + ((++error_count)) + fi + done done done adb shell rm -r $testdir +echo "$error_count errors" +exit $error_count diff --git a/media/libeffects/lvm/tests/snr.cpp b/media/libeffects/lvm/tests/snr.cpp index 88110c0f84..885994cdc2 100644 --- a/media/libeffects/lvm/tests/snr.cpp +++ b/media/libeffects/lvm/tests/snr.cpp @@ -84,6 +84,7 @@ int main(int argc, const char *argv[]) { printf("\nError: missing input/reference files\n"); return -1; } + int ret = EXIT_SUCCESS; auto sn = pcm_format == 0 ? getSignalNoise(finp, fref) : getSignalNoise(finp, fref); @@ -92,6 +93,7 @@ int main(int argc, const char *argv[]) { // compare the measured snr value with threshold if (snr < thr) { printf("%.6f less than threshold %.6f\n", snr, thr); + ret = EXIT_FAILURE; } else { printf("%.6f\n", snr); } @@ -99,5 +101,5 @@ int main(int argc, const char *argv[]) { fclose(finp); fclose(fref); - return 0; + return ret; } -- GitLab From 9e0ad0278471500b833aa58ccc29b3c542551932 Mon Sep 17 00:00:00 2001 From: jiabin Date: Wed, 20 Mar 2019 14:42:16 -0700 Subject: [PATCH 1146/1530] Add key for haptic channel count in metadata. This key could be useful to query if a file contains haptic channels or not. Test: test with files (not) containing haptic channels Bug: 128012181 Change-Id: I5f0a9ca9fde23ce7d6f48921acc9a1074a6a1601 --- media/extractors/ogg/OggExtractor.cpp | 4 +++- media/libmedia/NdkWrapper.cpp | 1 + media/libstagefright/MetaDataUtils.cpp | 7 +++---- media/libstagefright/Utils.cpp | 10 ++++++++++ .../include/media/stagefright/MetaDataBase.h | 2 ++ media/ndk/NdkMediaFormat.cpp | 1 + media/ndk/include/media/NdkMediaFormat.h | 1 + media/ndk/libmediandk.map.txt | 1 + 8 files changed, 22 insertions(+), 5 deletions(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index d99493dce9..b63ae6becb 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -1280,7 +1280,7 @@ void MyOggExtractor::parseFileMetaData() { //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); } - AMediaFormat_getInt32(mFileMeta, "haptic", &mHapticChannelCount); + AMediaFormat_getInt32(mFileMeta, AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT, &mHapticChannelCount); } void MyOggExtractor::setChannelMask(int channelCount) { @@ -1297,6 +1297,8 @@ void MyOggExtractor::setChannelMask(int channelCount) { const audio_channel_mask_t channelMask = audio_channel_out_mask_from_count( audioChannelCount) | hapticChannelMask; AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, channelMask); + AMediaFormat_setInt32( + mMeta, AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT, mHapticChannelCount); } } else { AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp index ea0547c3fb..c150407e9b 100644 --- a/media/libmedia/NdkWrapper.cpp +++ b/media/libmedia/NdkWrapper.cpp @@ -65,6 +65,7 @@ static const char *AMediaFormatKeyGroupInt32[] = { AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL, AMEDIAFORMAT_KEY_GRID_COLUMNS, AMEDIAFORMAT_KEY_GRID_ROWS, + AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT, AMEDIAFORMAT_KEY_HEIGHT, AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD, AMEDIAFORMAT_KEY_IS_ADTS, diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp index dbc287eb16..3f0bc7df43 100644 --- a/media/libstagefright/MetaDataUtils.cpp +++ b/media/libstagefright/MetaDataUtils.cpp @@ -309,7 +309,6 @@ static void extractAlbumArt( void parseVorbisComment( AMediaFormat *fileMeta, const char *comment, size_t commentLength) { // Haptic tag is only kept here as it will only be used in extractor to generate channel mask. - const char* const haptic = "haptic"; struct { const char *const mTag; const char *mKey; @@ -330,7 +329,7 @@ void parseVorbisComment( { "LYRICIST", AMEDIAFORMAT_KEY_LYRICIST }, { "METADATA_BLOCK_PICTURE", AMEDIAFORMAT_KEY_ALBUMART }, { "ANDROID_LOOP", AMEDIAFORMAT_KEY_LOOP }, - { "ANDROID_HAPTIC", haptic }, + { "ANDROID_HAPTIC", AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT }, }; for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { @@ -346,12 +345,12 @@ void parseVorbisComment( if (!strcasecmp(&comment[tagLen + 1], "true")) { AMediaFormat_setInt32(fileMeta, AMEDIAFORMAT_KEY_LOOP, 1); } - } else if (kMap[j].mKey == haptic) { + } else if (kMap[j].mKey == AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT) { char *end; errno = 0; const int hapticChannelCount = strtol(&comment[tagLen + 1], &end, 10); if (errno == 0) { - AMediaFormat_setInt32(fileMeta, haptic, hapticChannelCount); + AMediaFormat_setInt32(fileMeta, kMap[j].mKey, hapticChannelCount); } else { ALOGE("Error(%d) when parsing haptic channel count", errno); } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 2aa9ed87c7..c7b27192bb 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -967,6 +967,11 @@ status_t convertMetaDataToMessage( if (meta->findInt32(kKeyPcmEncoding, &pcmEncoding)) { msg->setInt32("pcm-encoding", pcmEncoding); } + + int32_t hapticChannelCount; + if (meta->findInt32(kKeyHapticChannelCount, &hapticChannelCount)) { + msg->setInt32("haptic-channel-count", hapticChannelCount); + } } int32_t maxInputSize; @@ -1708,6 +1713,11 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { if (msg->findInt32("pcm-encoding", &pcmEncoding)) { meta->setInt32(kKeyPcmEncoding, pcmEncoding); } + + int32_t hapticChannelCount; + if (msg->findInt32("haptic-channel-count", &hapticChannelCount)) { + meta->setInt32(kKeyHapticChannelCount, hapticChannelCount); + } } int32_t maxInputSize; diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index 75fd0d9032..93e31bf632 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -238,6 +238,8 @@ enum { kKeyOpaqueCSD0 = 'csd0', kKeyOpaqueCSD1 = 'csd1', kKeyOpaqueCSD2 = 'csd2', + + kKeyHapticChannelCount = 'hapC', }; enum { diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp index ed88cf349a..51138c8eb3 100644 --- a/media/ndk/NdkMediaFormat.cpp +++ b/media/ndk/NdkMediaFormat.cpp @@ -324,6 +324,7 @@ EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate"; EXPORT const char* AMEDIAFORMAT_KEY_GENRE = "genre"; EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLUMNS = "grid-cols"; EXPORT const char* AMEDIAFORMAT_KEY_GRID_ROWS = "grid-rows"; +EXPORT const char* AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT = "haptic-channel-count"; EXPORT const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO = "hdr-static-info"; EXPORT const char* AMEDIAFORMAT_KEY_HDR10_PLUS_INFO = "hdr10-plus-info"; EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height"; diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h index 259481dc3c..fd43f36cb0 100644 --- a/media/ndk/include/media/NdkMediaFormat.h +++ b/media/ndk/include/media/NdkMediaFormat.h @@ -209,6 +209,7 @@ extern const char* AMEDIAFORMAT_KEY_EXIF_OFFSET __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_EXIF_SIZE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_FRAME_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_GENRE __INTRODUCED_IN(29); +extern const char* AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_ICC_PROFILE __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_IS_SYNC_FRAME __INTRODUCED_IN(29); extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29); diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt index 4725e9ec26..f666ad00b7 100644 --- a/media/ndk/libmediandk.map.txt +++ b/media/ndk/libmediandk.map.txt @@ -89,6 +89,7 @@ LIBMEDIANDK { AMEDIAFORMAT_KEY_GENRE; # var introduced=29 AMEDIAFORMAT_KEY_GRID_COLUMNS; # var introduced=28 AMEDIAFORMAT_KEY_GRID_ROWS; # var introduced=28 + AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT; # var introduced=29 AMEDIAFORMAT_KEY_HDR_STATIC_INFO; # var introduced=28 AMEDIAFORMAT_KEY_HEIGHT; # var introduced=21 AMEDIAFORMAT_KEY_ICC_PROFILE; # var introduced=29 -- GitLab From 7d355db285c285d212908e08756c5ca7739b7ad3 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 18 Mar 2019 13:41:29 -0700 Subject: [PATCH 1147/1530] cameraserver: Check for dataspace override during finishConfiguration. Bug: 120407707 Test: CTS Test: Use camera to take pictures / record videos (sanity) Change-Id: I7b29c337d0e217d2eb6a62e2c75ccc550d795e61 Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/device3/Camera3Stream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 0571741ba5..12ff1308da 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -321,7 +321,7 @@ status_t Camera3Stream::finishConfiguration() { // so. As documented in hardware/camera3.h:configure_streams(). if (mState == STATE_IN_RECONFIG && mOldUsage == mUsage && - mOldMaxBuffers == camera3_stream::max_buffers) { + mOldMaxBuffers == camera3_stream::max_buffers && !mDataSpaceOverridden) { mState = STATE_CONFIGURED; return OK; } -- GitLab From 688031f85db7ff14eabafaa9d118d26a727b3719 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 21 Mar 2019 07:32:15 -0700 Subject: [PATCH 1148/1530] Fix webm writer Webm writer still made use of kKeyVorbisInfo. Fix it, and remove that key altogether. Bug: 128994325 Test: CTS Change-Id: I38b73871c1db65fa66416892934d2a697719cca3 --- media/libstagefright/include/media/stagefright/MetaDataBase.h | 2 -- media/libstagefright/webm/WebmWriter.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h index 75fd0d9032..0a631403c3 100644 --- a/media/libstagefright/include/media/stagefright/MetaDataBase.h +++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h @@ -62,8 +62,6 @@ enum { kKeyAV1C = 'av1c', // raw data kKeyThumbnailHVCC = 'thvc', // raw data kKeyD263 = 'd263', // raw data - kKeyVorbisInfo = 'vinf', // raw data - kKeyVorbisBooks = 'vboo', // raw data kKeyOpusHeader = 'ohdr', // raw data kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns) kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns) diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp index b0a303e80b..26e08844b3 100644 --- a/media/libstagefright/webm/WebmWriter.cpp +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -177,8 +177,8 @@ sp WebmWriter::audioTrack(const sp& md) { const void *headerData3; size_t headerSize1, headerSize2 = sizeof(headerData2), headerSize3; - if (!md->findData(kKeyVorbisInfo, &type, &headerData1, &headerSize1) - || !md->findData(kKeyVorbisBooks, &type, &headerData3, &headerSize3)) { + if (!md->findData(kKeyOpaqueCSD0, &type, &headerData1, &headerSize1) + || !md->findData(kKeyOpaqueCSD1, &type, &headerData3, &headerSize3)) { ALOGE("Missing header format keys for vorbis track"); md->dumpToLog(); return NULL; -- GitLab From 076443f630f007efdd0c7bd641784a71b0416f77 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 21 Mar 2019 11:45:14 -0700 Subject: [PATCH 1149/1530] Better size validation when parsing httplive/aac headers. ensure no underflow when pulling header from front of AAC packet. Indicate malformed error in parent routine if we don't have the expected header data. Bug: 128433933 Test: y --- media/libstagefright/httplive/PlaylistFetcher.cpp | 5 ++++- media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index d153598dc3..c62c2cd5a9 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -2125,7 +2125,10 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( size_t offset = 0; while (offset < buffer->size()) { const uint8_t *adtsHeader = buffer->data() + offset; - CHECK_LT(offset + 5, buffer->size()); + if (buffer->size() <= offset+5) { + ALOGV("buffer does not contain a complete header"); + return ERROR_MALFORMED; + } // non-const pointer for decryption if needed uint8_t *adtsFrame = buffer->data() + offset; diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp index e32f6764a9..7d446ab2ef 100644 --- a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp +++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp @@ -149,6 +149,11 @@ void HlsSampleDecryptor::processAAC(size_t adtsHdrSize, uint8_t *data, size_t si } // ADTS header is included in the size + if (size < adtsHdrSize) { + ALOGV("processAAC: size (%zu) < adtsHdrSize (%zu)", size, adtsHdrSize); + android_errorWriteLog(0x534e4554, "128433933"); + return; + } size_t offset = adtsHdrSize; size_t remainingBytes = size - adtsHdrSize; -- GitLab From 4970a6c944fed7a1763509b7105c83089a923856 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 21 Mar 2019 14:16:40 -0700 Subject: [PATCH 1150/1530] Fix multiple-track mkv files Structures stored in Vector might have their destructor called when the Vector needs to be extended, so ensure we handle that correctly. Bug: 128371202 Change-Id: I6eeb4aa67edaa63e0068f6102457f693f63768bc --- media/extractors/mkv/MatroskaExtractor.cpp | 34 ++++++++++++++++++---- media/extractors/mkv/MatroskaExtractor.h | 4 +-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 20cc6438ee..d6d24c18bd 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1336,6 +1336,13 @@ MatroskaExtractor::~MatroskaExtractor() { mReader = NULL; delete mDataSource; + + for (size_t i = 0; i < mTracks.size(); ++i) { + TrackInfo *info = &mTracks.editItemAt(i); + if (info->mMeta) { + AMediaFormat_delete(info->mMeta); + } + } } size_t MatroskaExtractor::countTracks() { @@ -1808,6 +1815,8 @@ status_t MatroskaExtractor::initTrackInfo( void MatroskaExtractor::addTracks() { const mkvparser::Tracks *tracks = mSegment->GetTracks(); + AMediaFormat *meta = nullptr; + for (size_t index = 0; index < tracks->GetTracksCount(); ++index) { const mkvparser::Track *track = tracks->GetTrackByIndex(index); @@ -1832,7 +1841,11 @@ void MatroskaExtractor::addTracks() { enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 }; - AMediaFormat *meta = AMediaFormat_new(); + if (meta) { + AMediaFormat_clear(meta); + } else { + meta = AMediaFormat_new(); + } status_t err = OK; int32_t nalSize = -1; @@ -2067,21 +2080,26 @@ void MatroskaExtractor::addTracks() { long long durationNs = mSegment->GetDuration(); AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_DURATION, (durationNs + 500) / 1000); + const char *mimetype = ""; + if (!AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype)) { + // do not add this track to the track list + ALOGW("ignoring track with unknown mime"); + continue; + } + mTracks.push(); size_t n = mTracks.size() - 1; TrackInfo *trackInfo = &mTracks.editItemAt(n); initTrackInfo(track, meta, trackInfo); trackInfo->mNalLengthSize = nalSize; - const char *mimetype = ""; - AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype); - if ((!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) || (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_AVC) && isSetCsdFrom1stFrame)) { // Attempt to recover from AVC track without codec private data err = synthesizeAVCC(trackInfo, n); if (err != OK) { mTracks.pop(); + continue; } } else if ((!strcmp("V_MPEG2", codecID) && codecPrivateSize == 0) || (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG2) && isSetCsdFrom1stFrame)) { @@ -2089,6 +2107,7 @@ void MatroskaExtractor::addTracks() { err = synthesizeMPEG2(trackInfo, n); if (err != OK) { mTracks.pop(); + continue; } } else if ((!strcmp("V_MPEG4/ISO/ASP", codecID) && codecPrivateSize == 0) || (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG4) && isSetCsdFrom1stFrame) || @@ -2099,9 +2118,14 @@ void MatroskaExtractor::addTracks() { err = synthesizeMPEG4(trackInfo, n); if (err != OK) { mTracks.pop(); + continue; } } - + // the TrackInfo owns the metadata now + meta = nullptr; + } + if (meta) { + AMediaFormat_delete(meta); } } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index d53d9e332b..99fad170da 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -61,10 +61,8 @@ private: TrackInfo() { mMeta = NULL; } + ~TrackInfo() { - if (mMeta) { - AMediaFormat_delete(mMeta); - } } unsigned long mTrackNum; bool mEncrypted; -- GitLab From c30f0d8ed4166f88650798b11026b3b897ed9095 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 21 Mar 2019 15:07:54 -0700 Subject: [PATCH 1151/1530] skip -1 default values in stagefrightrecorder metrics don't emit the -1 (default, unspecified) values for movie, audio, and video timescale fields in the 'recorder' mediametrics type. Bug: 78600173 Test: y --- media/libmediaplayerservice/StagefrightRecorder.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 77777b8712..d55b5f76f4 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -164,9 +164,12 @@ void StagefrightRecorder::updateMetrics() { mAnalyticsItem->setInt32(kRecorderVideoIframeInterval, mIFramesIntervalSec); // TBD mAudioSourceNode = 0; // TBD mUse64BitFileOffset = false; - mAnalyticsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale); - mAnalyticsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale); - mAnalyticsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale); + if (mMovieTimeScale != -1) + mAnalyticsItem->setInt32(kRecorderMovieTimescale, mMovieTimeScale); + if (mAudioTimeScale != -1) + mAnalyticsItem->setInt32(kRecorderAudioTimescale, mAudioTimeScale); + if (mVideoTimeScale != -1) + mAnalyticsItem->setInt32(kRecorderVideoTimescale, mVideoTimeScale); // TBD mCameraId = 0; // TBD mStartTimeOffsetMs = -1; mAnalyticsItem->setInt32(kRecorderVideoProfile, mVideoEncoderProfile); @@ -2210,7 +2213,7 @@ status_t StagefrightRecorder::getMaxAmplitude(int *max) { } status_t StagefrightRecorder::getMetrics(Parcel *reply) { - ALOGD("StagefrightRecorder::getMetrics"); + ALOGV("StagefrightRecorder::getMetrics"); if (reply == NULL) { ALOGE("Null pointer argument"); -- GitLab From 1234008771662d2b250d6d4972adb04de963fb0d Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Tue, 19 Mar 2019 09:35:05 -0600 Subject: [PATCH 1152/1530] API changes in MicrophoneDirection Bug: 128523813 Test: Build, Testbed app Change-Id: I7b5ee8ba4eec1090866d8aef891a98d50cd09cc5 --- media/libaudioclient/AudioRecord.cpp | 12 ++++---- .../aidl/android/media/IAudioRecord.aidl | 4 +-- .../include/media/AudioRecord.h | 4 +-- media/libaudiohal/impl/StreamHalHidl.cpp | 15 +++++----- media/libaudiohal/impl/StreamHalHidl.h | 5 ++-- media/libaudiohal/impl/StreamHalLocal.cpp | 11 +++++--- media/libaudiohal/impl/StreamHalLocal.h | 4 +-- .../media/audiohal/StreamHalInterface.h | 4 +-- media/libmedia/IMediaRecorder.cpp | 28 +++++++++---------- media/libmedia/include/media/IMediaRecorder.h | 4 +-- .../include/media/MediaRecorderBase.h | 4 +-- media/libmedia/include/media/mediarecorder.h | 4 +-- media/libmedia/mediarecorder.cpp | 12 ++++---- .../MediaRecorderClient.cpp | 13 +++++---- .../MediaRecorderClient.h | 4 +-- .../StagefrightRecorder.cpp | 12 ++++---- .../StagefrightRecorder.h | 4 +-- media/libstagefright/AudioSource.cpp | 12 ++++---- .../include/media/stagefright/AudioSource.h | 4 +-- services/audioflinger/AudioFlinger.h | 4 +-- services/audioflinger/RecordTracks.h | 4 +-- services/audioflinger/Threads.cpp | 13 +++++---- services/audioflinger/Threads.h | 4 +-- services/audioflinger/Tracks.cpp | 16 +++++------ 24 files changed, 104 insertions(+), 97 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 58515338ec..f07be46ed3 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -410,8 +410,8 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri } // Call these directly because we are already holding the lock. - mAudioRecord->setMicrophoneDirection(mSelectedMicDirection); - mAudioRecord->setMicrophoneFieldDimension(mSelectedMicFieldDimension); + mAudioRecord->setPreferredMicrophoneDirection(mSelectedMicDirection); + mAudioRecord->setPreferredMicrophoneFieldDimension(mSelectedMicFieldDimension); if (status != NO_ERROR) { mActive = false; @@ -1381,7 +1381,7 @@ status_t AudioRecord::getActiveMicrophones(std::vector* a return mAudioRecord->getActiveMicrophones(activeMicrophones).transactionError(); } -status_t AudioRecord::setMicrophoneDirection(audio_microphone_direction_t direction) +status_t AudioRecord::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { AutoMutex lock(mLock); if (mSelectedMicDirection == direction) { @@ -1394,11 +1394,11 @@ status_t AudioRecord::setMicrophoneDirection(audio_microphone_direction_t direct // the internal AudioRecord hasn't be created yet, so just stash the attribute. return OK; } else { - return mAudioRecord->setMicrophoneDirection(direction).transactionError(); + return mAudioRecord->setPreferredMicrophoneDirection(direction).transactionError(); } } -status_t AudioRecord::setMicrophoneFieldDimension(float zoom) { +status_t AudioRecord::setPreferredMicrophoneFieldDimension(float zoom) { AutoMutex lock(mLock); if (mSelectedMicFieldDimension == zoom) { // NOP @@ -1410,7 +1410,7 @@ status_t AudioRecord::setMicrophoneFieldDimension(float zoom) { // the internal AudioRecord hasn't be created yet, so just stash the attribute. return OK; } else { - return mAudioRecord->setMicrophoneFieldDimension(zoom).transactionError(); + return mAudioRecord->setPreferredMicrophoneFieldDimension(zoom).transactionError(); } } diff --git a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl index cf9c7f44d2..ecf58b6234 100644 --- a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl +++ b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl @@ -39,9 +39,9 @@ interface IAudioRecord { /* Set the microphone direction (for processing). */ - void setMicrophoneDirection(int /*audio_microphone_direction_t*/ direction); + void setPreferredMicrophoneDirection(int /*audio_microphone_direction_t*/ direction); /* Set the microphone zoom (for processing). */ - void setMicrophoneFieldDimension(float zoom); + void setPreferredMicrophoneFieldDimension(float zoom); } diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index b4ddb69a65..9c81bb7a4b 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -542,11 +542,11 @@ public: /* Set the Microphone direction (for processing purposes). */ - status_t setMicrophoneDirection(audio_microphone_direction_t direction); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); /* Set the Microphone zoom factor (for processing purposes). */ - status_t setMicrophoneFieldDimension(float zoom); + status_t setPreferredMicrophoneFieldDimension(float zoom); /* Get the unique port ID assigned to this AudioRecord instance by audio policy manager. * The ID is unique across all audioserver clients and can change during the life cycle diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index e396cf3878..6c8e6a4465 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -855,25 +855,26 @@ status_t StreamInHalHidl::updateSinkMetadata(const #endif #if MAJOR_VERSION < 5 -status_t StreamInHalHidl::setMicrophoneDirection(audio_microphone_direction_t direction __unused) { +status_t StreamInHalHidl::setPreferredMicrophoneDirection( + audio_microphone_direction_t direction __unused) { if (mStream == 0) return NO_INIT; return INVALID_OPERATION; } -status_t StreamInHalHidl::setMicrophoneFieldDimension(float zoom __unused) { +status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom __unused) { if (mStream == 0) return NO_INIT; return INVALID_OPERATION; } #else -status_t StreamInHalHidl::setMicrophoneDirection(audio_microphone_direction_t direction) { +status_t StreamInHalHidl::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { if (!mStream) return NO_INIT; - return processReturn("setMicrophoneDirection", - mStream->setMicrophoneDirection(static_cast(direction))); + return processReturn("setPreferredMicrophoneDirection", + mStream->setMicrophoneDirection(static_cast(direction))); } -status_t StreamInHalHidl::setMicrophoneFieldDimension(float zoom) { +status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom) { if (!mStream) return NO_INIT; - return processReturn("setMicrophoneFieldDimension", + return processReturn("setPreferredMicrophoneFieldDimension", mStream->setMicrophoneFieldDimension(zoom)); } #endif diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h index 9ac10674d1..f587889aa6 100644 --- a/media/libaudiohal/impl/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -221,10 +221,11 @@ class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl { virtual status_t getActiveMicrophones(std::vector *microphones); // Set microphone direction (for processing) - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) override; + virtual status_t setPreferredMicrophoneDirection( + audio_microphone_direction_t direction) override; // Set microphone zoom (for processing) - virtual status_t setMicrophoneFieldDimension(float zoom) override; + virtual status_t setPreferredMicrophoneFieldDimension(float zoom) override; // Called when the metadata of the stream's sink has been changed. status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp index fcb809bf53..7d5ce059df 100644 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -369,20 +369,21 @@ status_t StreamInHalLocal::getActiveMicrophones(std::vectorset_microphone_direction == NULL) return INVALID_OPERATION; return mStream->set_microphone_direction(mStream, direction); } -status_t StreamInHalLocal::setMicrophoneFieldDimension(float zoom) { +status_t StreamInHalLocal::setPreferredMicrophoneFieldDimension(float zoom) { if (mStream->set_microphone_field_dimension == NULL) return INVALID_OPERATION; return mStream->set_microphone_field_dimension(mStream, zoom); @@ -391,3 +392,5 @@ status_t StreamInHalLocal::setMicrophoneFieldDimension(float zoom) { } // namespace CPP_VERSION } // namespace android + + diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h index 3d6c50ef97..34f2bd8bdc 100644 --- a/media/libaudiohal/impl/StreamHalLocal.h +++ b/media/libaudiohal/impl/StreamHalLocal.h @@ -205,10 +205,10 @@ class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { virtual status_t getActiveMicrophones(std::vector *microphones); // Sets microphone direction (for processing) - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); // Sets microphone zoom (for processing) - virtual status_t setMicrophoneFieldDimension(float zoom); + virtual status_t setPreferredMicrophoneFieldDimension(float zoom); // Called when the metadata of the stream's sink has been changed. status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h index ed8282fdfc..6c3b21c7f1 100644 --- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h @@ -180,10 +180,10 @@ class StreamInHalInterface : public virtual StreamHalInterface { virtual status_t getActiveMicrophones(std::vector *microphones) = 0; // Set direction for capture processing - virtual status_t setMicrophoneDirection(audio_microphone_direction_t) = 0; + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t) = 0; // Set zoom factor for capture stream - virtual status_t setMicrophoneFieldDimension(float zoom) = 0; + virtual status_t setPreferredMicrophoneFieldDimension(float zoom) = 0; struct SinkMetadata { std::vector tracks; diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index f283569980..a354ce160d 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -66,8 +66,8 @@ enum { ENABLE_AUDIO_DEVICE_CALLBACK, GET_ACTIVE_MICROPHONES, GET_PORT_ID, - SET_MICROPHONE_DIRECTION, - SET_MICROPHONE_FIELD_DIMENSION + SET_PREFERRED_MICROPHONE_DIRECTION, + SET_PREFERRED_MICROPHONE_FIELD_DIMENSION }; class BpMediaRecorder: public BpInterface @@ -409,21 +409,21 @@ public: return status; } - status_t setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setPreferredMicrophoneDirection(%d)", direction); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); data.writeInt32(direction); - status_t status = remote()->transact(SET_MICROPHONE_DIRECTION, data, &reply); + status_t status = remote()->transact(SET_PREFERRED_MICROPHONE_DIRECTION, data, &reply); return status == NO_ERROR ? (status_t)reply.readInt32() : status; } - status_t setMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); + status_t setPreferredMicrophoneFieldDimension(float zoom) { + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); data.writeFloat(zoom); - status_t status = remote()->transact(SET_MICROPHONE_FIELD_DIMENSION, data, &reply); + status_t status = remote()->transact(SET_PREFERRED_MICROPHONE_FIELD_DIMENSION, data, &reply); return status == NO_ERROR ? (status_t)reply.readInt32() : status; } @@ -709,20 +709,20 @@ status_t BnMediaRecorder::onTransact( } return NO_ERROR; } - case SET_MICROPHONE_DIRECTION: { - ALOGV("SET_MICROPHONE_DIRECTION"); + case SET_PREFERRED_MICROPHONE_DIRECTION: { + ALOGV("SET_PREFERRED_MICROPHONE_DIRECTION"); CHECK_INTERFACE(IMediaRecorder, data, reply); int direction = data.readInt32(); - status_t status = - setMicrophoneDirection(static_cast(direction)); + status_t status = setPreferredMicrophoneDirection( + static_cast(direction)); reply->writeInt32(status); return NO_ERROR; } - case SET_MICROPHONE_FIELD_DIMENSION: { + case SET_PREFERRED_MICROPHONE_FIELD_DIMENSION: { ALOGV("SET_MICROPHONE_FIELD_DIMENSION"); CHECK_INTERFACE(IMediaRecorder, data, reply); float zoom = data.readFloat(); - status_t status = setMicrophoneFieldDimension(zoom); + status_t status = setPreferredMicrophoneFieldDimension(zoom); reply->writeInt32(status); return NO_ERROR; } diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h index 0b09420563..f9c557cc9c 100644 --- a/media/libmedia/include/media/IMediaRecorder.h +++ b/media/libmedia/include/media/IMediaRecorder.h @@ -73,8 +73,8 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) = 0; - virtual status_t setMicrophoneFieldDimension(float zoom) = 0; + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) = 0; + virtual status_t setPreferredMicrophoneFieldDimension(float zoom) = 0; virtual status_t getPortId(audio_port_handle_t *portId) = 0; }; diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h index 88282ac23f..a2dff31f16 100644 --- a/media/libmedia/include/media/MediaRecorderBase.h +++ b/media/libmedia/include/media/MediaRecorderBase.h @@ -72,8 +72,8 @@ struct MediaRecorderBase { virtual status_t enableAudioDeviceCallback(bool enabled) = 0; virtual status_t getActiveMicrophones( std::vector* activeMicrophones) = 0; - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction) = 0; - virtual status_t setMicrophoneFieldDimension(float zoom) = 0; + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction) = 0; + virtual status_t setPreferredMicrophoneFieldDimension(float zoom) = 0; virtual status_t getPortId(audio_port_handle_t *portId) const = 0; diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index 85804376de..2dd4b7f807 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -264,8 +264,8 @@ public: status_t getRoutedDeviceId(audio_port_handle_t *deviceId); status_t enableAudioDeviceCallback(bool enabled); status_t getActiveMicrophones(std::vector* activeMicrophones); - status_t setMicrophoneDirection(audio_microphone_direction_t direction); - status_t setMicrophoneFieldDimension(float zoom); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + status_t setPreferredMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) const; diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 6c59a29cc6..4570af9d4e 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -842,14 +842,14 @@ status_t MediaRecorder::getActiveMicrophones(std::vector* return mMediaRecorder->getActiveMicrophones(activeMicrophones); } -status_t MediaRecorder::setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); - return mMediaRecorder->setMicrophoneDirection(direction); +status_t MediaRecorder::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setPreferredMicrophoneDirection(%d)", direction); + return mMediaRecorder->setPreferredMicrophoneDirection(direction); } -status_t MediaRecorder::setMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); - return mMediaRecorder->setMicrophoneFieldDimension(zoom); +status_t MediaRecorder::setPreferredMicrophoneFieldDimension(float zoom) { + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); + return mMediaRecorder->setPreferredMicrophoneFieldDimension(zoom); } status_t MediaRecorder::getPortId(audio_port_handle_t *portId) const diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index d6628d9737..9f4265b021 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -538,18 +538,19 @@ status_t MediaRecorderClient::getActiveMicrophones( return NO_INIT; } -status_t MediaRecorderClient::setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); +status_t MediaRecorderClient::setPreferredMicrophoneDirection( + audio_microphone_direction_t direction) { + ALOGV("setPreferredMicrophoneDirection(%d)", direction); if (mRecorder != NULL) { - return mRecorder->setMicrophoneDirection(direction); + return mRecorder->setPreferredMicrophoneDirection(direction); } return NO_INIT; } -status_t MediaRecorderClient::setMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); +status_t MediaRecorderClient::setPreferredMicrophoneFieldDimension(float zoom) { + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); if (mRecorder != NULL) { - return mRecorder->setMicrophoneFieldDimension(zoom); + return mRecorder->setPreferredMicrophoneFieldDimension(zoom); } return NO_INIT; } diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 8da718f995..e698819854 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -109,8 +109,8 @@ public: virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones( std::vector* activeMicrophones); - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); - virtual status_t setMicrophoneFieldDimension(float zoom); + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + virtual status_t setPreferredMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) override; private: diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 77777b8712..93dd1c3380 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -2274,20 +2274,20 @@ status_t StagefrightRecorder::getActiveMicrophones( return NO_INIT; } -status_t StagefrightRecorder::setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); +status_t StagefrightRecorder::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setPreferredMicrophoneDirection(%d)", direction); mSelectedMicDirection = direction; if (mAudioSourceNode != 0) { - return mAudioSourceNode->setMicrophoneDirection(direction); + return mAudioSourceNode->setPreferredMicrophoneDirection(direction); } return NO_INIT; } -status_t StagefrightRecorder::setMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); +status_t StagefrightRecorder::setPreferredMicrophoneFieldDimension(float zoom) { + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); mSelectedMicFieldDimension = zoom; if (mAudioSourceNode != 0) { - return mAudioSourceNode->setMicrophoneFieldDimension(zoom); + return mAudioSourceNode->setPreferredMicrophoneFieldDimension(zoom); } return NO_INIT; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 236b19e932..8bf083a11e 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -77,8 +77,8 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual void setAudioDeviceCallback(const sp& callback); virtual status_t enableAudioDeviceCallback(bool enabled); virtual status_t getActiveMicrophones(std::vector* activeMicrophones); - virtual status_t setMicrophoneDirection(audio_microphone_direction_t direction); - virtual status_t setMicrophoneFieldDimension(float zoom); + virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + virtual status_t setPreferredMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) const override; private: diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 5f86bd32c1..5194e03224 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -510,18 +510,18 @@ status_t AudioSource::getActiveMicrophones( return NO_INIT; } -status_t AudioSource::setMicrophoneDirection(audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); +status_t AudioSource::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { + ALOGV("setPreferredMicrophoneDirection(%d)", direction); if (mRecord != 0) { - return mRecord->setMicrophoneDirection(direction); + return mRecord->setPreferredMicrophoneDirection(direction); } return NO_INIT; } -status_t AudioSource::setMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); +status_t AudioSource::setPreferredMicrophoneFieldDimension(float zoom) { + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); if (mRecord != 0) { - return mRecord->setMicrophoneFieldDimension(zoom); + return mRecord->setPreferredMicrophoneFieldDimension(zoom); } return NO_INIT; } diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h index 18e5f10552..af04dad5fe 100644 --- a/media/libstagefright/include/media/stagefright/AudioSource.h +++ b/media/libstagefright/include/media/stagefright/AudioSource.h @@ -70,8 +70,8 @@ struct AudioSource : public MediaSource, public MediaBufferObserver { status_t removeAudioDeviceCallback(const sp& callback); status_t getActiveMicrophones(std::vector* activeMicrophones); - status_t setMicrophoneDirection(audio_microphone_direction_t direction); - status_t setMicrophoneFieldDimension(float zoom); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + status_t setPreferredMicrophoneFieldDimension(float zoom); status_t getPortId(audio_port_handle_t *portId) const; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index ec5dfb177d..8ac3366299 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -615,9 +615,9 @@ using effect_buffer_t = int16_t; virtual binder::Status stop(); virtual binder::Status getActiveMicrophones( std::vector* activeMicrophones); - virtual binder::Status setMicrophoneDirection( + virtual binder::Status setPreferredMicrophoneDirection( int /*audio_microphone_direction_t*/ direction); - virtual binder::Status setMicrophoneFieldDimension(float zoom); + virtual binder::Status setPreferredMicrophoneFieldDimension(float zoom); private: const sp mRecordTrack; diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index ab4af33bab..ec1f86cc11 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -71,8 +71,8 @@ public: status_t getActiveMicrophones(std::vector* activeMicrophones); - status_t setMicrophoneDirection(audio_microphone_direction_t direction); - status_t setMicrophoneFieldDimension(float zoom); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + status_t setPreferredMicrophoneFieldDimension(float zoom); static bool checkServerLatencySupported( audio_format_t format, audio_input_flags_t flags) { diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 3ecb37d405..e94fb49cba 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7718,18 +7718,19 @@ status_t AudioFlinger::RecordThread::getActiveMicrophones( return status; } -status_t AudioFlinger::RecordThread::setMicrophoneDirection(audio_microphone_direction_t direction) +status_t AudioFlinger::RecordThread::setPreferredMicrophoneDirection( + audio_microphone_direction_t direction) { - ALOGV("setMicrophoneDirection(%d)", direction); + ALOGV("setPreferredMicrophoneDirection(%d)", direction); AutoMutex _l(mLock); - return mInput->stream->setMicrophoneDirection(direction); + return mInput->stream->setPreferredMicrophoneDirection(direction); } -status_t AudioFlinger::RecordThread::setMicrophoneFieldDimension(float zoom) +status_t AudioFlinger::RecordThread::setPreferredMicrophoneFieldDimension(float zoom) { - ALOGV("setMicrophoneFieldDimension(%f)", zoom); + ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom); AutoMutex _l(mLock); - return mInput->stream->setMicrophoneFieldDimension(zoom); + return mInput->stream->setPreferredMicrophoneFieldDimension(zoom); } void AudioFlinger::RecordThread::updateMetadata_l() diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 47e580bf8c..e5abce7793 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1607,8 +1607,8 @@ public: status_t getActiveMicrophones(std::vector* activeMicrophones); - status_t setMicrophoneDirection(audio_microphone_direction_t direction); - status_t setMicrophoneFieldDimension(float zoom); + status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); + status_t setPreferredMicrophoneFieldDimension(float zoom); void updateMetadata_l() override; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 5a436962d6..fbf8fef953 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1838,16 +1838,16 @@ binder::Status AudioFlinger::RecordHandle::getActiveMicrophones( mRecordTrack->getActiveMicrophones(activeMicrophones)); } -binder::Status AudioFlinger::RecordHandle::setMicrophoneDirection( +binder::Status AudioFlinger::RecordHandle::setPreferredMicrophoneDirection( int /*audio_microphone_direction_t*/ direction) { ALOGV("%s()", __func__); - return binder::Status::fromStatusT(mRecordTrack->setMicrophoneDirection( + return binder::Status::fromStatusT(mRecordTrack->setPreferredMicrophoneDirection( static_cast(direction))); } -binder::Status AudioFlinger::RecordHandle::setMicrophoneFieldDimension(float zoom) { +binder::Status AudioFlinger::RecordHandle::setPreferredMicrophoneFieldDimension(float zoom) { ALOGV("%s()", __func__); - return binder::Status::fromStatusT(mRecordTrack->setMicrophoneFieldDimension(zoom)); + return binder::Status::fromStatusT(mRecordTrack->setPreferredMicrophoneFieldDimension(zoom)); } // ---------------------------------------------------------------------------- @@ -2144,22 +2144,22 @@ status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones( } } -status_t AudioFlinger::RecordThread::RecordTrack::setMicrophoneDirection( +status_t AudioFlinger::RecordThread::RecordTrack::setPreferredMicrophoneDirection( audio_microphone_direction_t direction) { sp thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - return recordThread->setMicrophoneDirection(direction); + return recordThread->setPreferredMicrophoneDirection(direction); } else { return BAD_VALUE; } } -status_t AudioFlinger::RecordThread::RecordTrack::setMicrophoneFieldDimension(float zoom) { +status_t AudioFlinger::RecordThread::RecordTrack::setPreferredMicrophoneFieldDimension(float zoom) { sp thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - return recordThread->setMicrophoneFieldDimension(zoom); + return recordThread->setPreferredMicrophoneFieldDimension(zoom); } else { return BAD_VALUE; } -- GitLab From 1ff16a772159dbc7ebe851e474bb155a2c8e74ab Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 14 Mar 2019 18:35:04 -0700 Subject: [PATCH 1153/1530] audio policy: fix several problems with capture policy - Fix accessibility service not able to get same policy as regular apps when not using VOICE_RECOGNITION source b/126785297 - Make sure two captures from same app do not silence each other (e.g two privacy sensitive captures concurrently) - Ignore clients in idle state when considering priority active clients in silencing logic. - Prevent capture while in call if privileged permission CAPTURE_AUDIO_OUTPUT is not granted - Allow capture concurrently with privacy sensitive capture or call if permission CAPTURE_AUDIO_OUTPUT is granted. - Do not silence virtual sources when sensor privacy is enabled. - Consider system persistent service as top app state. Bug: 128419018 Bug: 126785297 Test: audio smoke tests Change-Id: I7f8c7c511c674d4d46a815eaa7e9cf95e95cc3f9 --- .../service/AudioPolicyInterfaceImpl.cpp | 11 +- .../service/AudioPolicyService.cpp | 131 ++++++++++++------ .../audiopolicy/service/AudioPolicyService.h | 8 +- 3 files changed, 98 insertions(+), 52 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index a672521de8..ea6ca39a55 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -376,15 +376,17 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, return PERMISSION_DENIED; } + bool canCaptureOutput = captureAudioOutputAllowed(pid, uid); if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK || attr->source == AUDIO_SOURCE_VOICE_DOWNLINK || attr->source == AUDIO_SOURCE_VOICE_CALL || attr->source == AUDIO_SOURCE_ECHO_REFERENCE) && - !captureAudioOutputAllowed(pid, uid)) { + !canCaptureOutput) { return PERMISSION_DENIED; } - if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) { + bool canCaptureHotword = captureHotwordAllowed(pid, uid); + if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) { return BAD_VALUE; } @@ -415,7 +417,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, case AudioPolicyInterface::API_INPUT_TELEPHONY_RX: // FIXME: use the same permission as for remote submix for now. case AudioPolicyInterface::API_INPUT_MIX_CAPTURE: - if (!captureAudioOutputAllowed(pid, uid)) { + if (!canCaptureOutput) { ALOGE("getInputForAttr() permission denied: capture not allowed"); status = PERMISSION_DENIED; } @@ -442,7 +444,8 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, } sp client = new AudioRecordClient(*attr, *input, uid, pid, session, - *selectedDeviceId, opPackageName); + *selectedDeviceId, opPackageName, + canCaptureOutput, canCaptureHotword); mAudioRecordClients.add(*portId, client); } diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 8cbf3af16d..e858e8d4d0 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -414,32 +414,35 @@ void AudioPolicyService::updateUidStates_l() { // Go over all active clients and allow capture (does not force silence) in the // following cases: -// The client is the assistant +// Another client in the same UID has already been allowed to capture +// OR The client is the assistant // AND an accessibility service is on TOP // AND the source is VOICE_RECOGNITION or HOTWORD // OR uses VOICE_RECOGNITION AND is on TOP OR latest started // OR uses HOTWORD -// AND there is no privacy sensitive active capture +// AND there is no active privacy sensitive capture or call +// OR client has CAPTURE_AUDIO_OUTPUT privileged permission // OR The client is an accessibility service // AND is on TOP OR latest started // AND the source is VOICE_RECOGNITION or HOTWORD -// OR the source is one of: AUDIO_SOURCE_VOICE_DOWNLINK, AUDIO_SOURCE_VOICE_UPLINK, -// AUDIO_SOURCE_VOICE_CALL +// OR the client source is virtual (remote submix, call audio TX or RX...) // OR Any other client // AND The assistant is not on TOP -// AND is on TOP OR latest started -// AND there is no privacy sensitive active capture +// AND there is no active privacy sensitive capture or call +// OR client has CAPTURE_AUDIO_OUTPUT privileged permission //TODO: mamanage pre processing effects according to use case priority sp topActive; sp latestActive; sp latestSensitiveActive; + nsecs_t topStartNs = 0; nsecs_t latestStartNs = 0; nsecs_t latestSensitiveStartNs = 0; bool isA11yOnTop = mUidPolicy->isA11yOnTop(); bool isAssistantOnTop = false; bool isSensitiveActive = false; + bool isInCall = mPhoneState == AUDIO_MODE_IN_CALL; // if Sensor Privacy is enabled then all recordings should be silenced. if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { @@ -449,15 +452,18 @@ void AudioPolicyService::updateUidStates_l() for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; - if (!current->active) continue; - if (isPrivacySensitiveSource(current->attributes.source)) { - if (current->startTimeNs > latestSensitiveStartNs) { - latestSensitiveActive = current; - latestSensitiveStartNs = current->startTimeNs; - } - isSensitiveActive = true; + if (!current->active) { + continue; + } + + app_state_t appState = apmStatFromAmState(mUidPolicy->getUidState(current->uid)); + // clients which app is in IDLE state are not eligible for top active or + // latest active + if (appState == APP_STATE_IDLE) { + continue; } - if (mUidPolicy->getUidState(current->uid) == ActivityManager::PROCESS_STATE_TOP) { + + if (appState == APP_STATE_TOP) { if (current->startTimeNs > topStartNs) { topActive = current; topStartNs = current->startTimeNs; @@ -470,72 +476,105 @@ void AudioPolicyService::updateUidStates_l() latestActive = current; latestStartNs = current->startTimeNs; } + if (isPrivacySensitiveSource(current->attributes.source)) { + if (current->startTimeNs > latestSensitiveStartNs) { + latestSensitiveActive = current; + latestSensitiveStartNs = current->startTimeNs; + } + isSensitiveActive = true; + } } - if (topActive == nullptr && latestActive == nullptr) { - return; + // if no active client with UI on Top, consider latest active as top + if (topActive == nullptr) { + topActive = latestActive; } - if (topActive != nullptr) { - latestActive = nullptr; - } + std::vector enabledUids; for (size_t i =0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; - if (!current->active) continue; + if (!current->active) { + continue; + } + + // keep capture allowed if another client with the same UID has already + // been allowed to capture + if (std::find(enabledUids.begin(), enabledUids.end(), current->uid) + != enabledUids.end()) { + continue; + } audio_source_t source = current->attributes.source; - bool isOnTop = current == topActive; - bool isLatest = current == latestActive; - bool isLatestSensitive = current == latestSensitiveActive; - bool forceIdle = true; + bool isTopOrLatestActive = topActive == nullptr ? false : current->uid == topActive->uid; + bool isLatestSensitive = latestSensitiveActive == nullptr ? + false : current->uid == latestSensitiveActive->uid; + + // By default allow capture if: + // The assistant is not on TOP + // AND there is no active privacy sensitive capture or call + // OR client has CAPTURE_AUDIO_OUTPUT privileged permission + bool allowCapture = !isAssistantOnTop + && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureOutput)) + && !(isInCall && !current->canCaptureOutput); if (isVirtualSource(source)) { - forceIdle = false; + // Allow capture for virtual (remote submix, call audio TX or RX...) sources + allowCapture = true; } else if (mUidPolicy->isAssistantUid(current->uid)) { + // For assistant allow capture if: + // An accessibility service is on TOP + // AND the source is VOICE_RECOGNITION or HOTWORD + // OR is on TOP OR latest started AND uses VOICE_RECOGNITION + // OR uses HOTWORD + // AND there is no active privacy sensitive capture or call + // OR client has CAPTURE_AUDIO_OUTPUT privileged permission if (isA11yOnTop) { if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { - forceIdle = false; + allowCapture = true; } } else { - if ((((isOnTop || isLatest) && source == AUDIO_SOURCE_VOICE_RECOGNITION) || - source == AUDIO_SOURCE_HOTWORD) && !isSensitiveActive) { - forceIdle = false; + if (((isTopOrLatestActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) || + source == AUDIO_SOURCE_HOTWORD) && + (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) { + allowCapture = true; } } } else if (mUidPolicy->isA11yUid(current->uid)) { - if ((isOnTop || isLatest) && - (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { - forceIdle = false; - } - } else { - if (!isAssistantOnTop && (isOnTop || isLatest) && - (!isSensitiveActive || isLatestSensitive)) { - forceIdle = false; + // For accessibility service allow capture if: + // Is on TOP OR latest started + // AND the source is VOICE_RECOGNITION or HOTWORD + if (isTopOrLatestActive && + (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { + allowCapture = true; } } setAppState_l(current->uid, - forceIdle ? APP_STATE_IDLE : - apmStatFromAmState(mUidPolicy->getUidState(current->uid))); + allowCapture ? apmStatFromAmState(mUidPolicy->getUidState(current->uid)) : + APP_STATE_IDLE); + if (allowCapture) { + enabledUids.push_back(current->uid); + } } } void AudioPolicyService::silenceAllRecordings_l() { for (size_t i = 0; i < mAudioRecordClients.size(); i++) { sp current = mAudioRecordClients[i]; - setAppState_l(current->uid, APP_STATE_IDLE); + if (!isVirtualSource(current->attributes.source)) { + setAppState_l(current->uid, APP_STATE_IDLE); + } } } /* static */ app_state_t AudioPolicyService::apmStatFromAmState(int amState) { - switch (amState) { - case ActivityManager::PROCESS_STATE_UNKNOWN: + + if (amState == ActivityManager::PROCESS_STATE_UNKNOWN) { return APP_STATE_IDLE; - case ActivityManager::PROCESS_STATE_TOP: - return APP_STATE_TOP; - default: - break; + } else if (amState <= ActivityManager::PROCESS_STATE_TOP) { + // include persistent services + return APP_STATE_TOP; } return APP_STATE_FOREGROUND; } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index a2e75cde41..160f70fede 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -753,13 +753,17 @@ private: AudioRecordClient(const audio_attributes_t attributes, const audio_io_handle_t io, uid_t uid, pid_t pid, const audio_session_t session, const audio_port_handle_t deviceId, - const String16& opPackageName) : + const String16& opPackageName, + bool canCaptureOutput, bool canCaptureHotword) : AudioClient(attributes, io, uid, pid, session, deviceId), - opPackageName(opPackageName), startTimeNs(0) {} + opPackageName(opPackageName), startTimeNs(0), + canCaptureOutput(canCaptureOutput), canCaptureHotword(canCaptureHotword) {} ~AudioRecordClient() override = default; const String16 opPackageName; // client package name nsecs_t startTimeNs; + const bool canCaptureOutput; + const bool canCaptureHotword; }; // --- AudioPlaybackClient --- -- GitLab From 9b8cba239e2369219e3209bdbd654e3644a37adb Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 22 Mar 2019 09:33:34 -0700 Subject: [PATCH 1154/1530] Fix test that checks if metadata is already set Bug: 128565395 Test: manual Change-Id: Ic983efe66f70a409ec4ec40eec6d6d3aa8fe540b --- media/extractors/mp4/MPEG4Extractor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 22819cb4cf..647540a749 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -3675,8 +3675,10 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { void *tmpData; size_t tmpDataSize; + const char *s; if (size >= 8 && metadataKey && - !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize)) { + !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize) && + !AMediaFormat_getString(mFileMetaData, metadataKey, &s)) { if (!strcmp(metadataKey, "albumart")) { AMediaFormat_setBuffer(mFileMetaData, metadataKey, buffer + 8, size - 8); @@ -3918,10 +3920,9 @@ void MPEG4Extractor::parseID3v2MetaData(off64_t offset) { }; static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); - void *tmpData; - size_t tmpDataSize; for (size_t i = 0; i < kNumMapEntries; ++i) { - if (!AMediaFormat_getBuffer(mFileMetaData, kMap[i].key, &tmpData, &tmpDataSize)) { + const char *ss; + if (!AMediaFormat_getString(mFileMetaData, kMap[i].key, &ss)) { ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1); if (it->done()) { delete it; -- GitLab From c47d1d18ceba50936187f7c9dc4246b84d8d147d Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 22 Mar 2019 12:51:04 -0700 Subject: [PATCH 1155/1530] handle empty NAL when looking for HEVC layerId parseHEVCLayerId() would CHECK() if there was no data, we now return that there was no valid HEVC layer info. This was triggered by changes so a bad NAL would be skipped rather than aborting. Bug: 127833120 Bug: 122749422 Test: re-ran CTS Change-Id: I239697f9d1f77995f9ccf5e8c77cfaa01994e326 --- media/extractors/mp4/MPEG4Extractor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 22819cb4cf..a3bf7f4727 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5318,7 +5318,9 @@ size_t MPEG4Source::parseNALSize(const uint8_t *data) const { } int32_t MPEG4Source::parseHEVCLayerId(const uint8_t *data, size_t size) { - CHECK(data != nullptr && size >= (mNALLengthSize + 2)); + if (data == nullptr || size < mNALLengthSize + 2) { + return -1; + } // HEVC NAL-header (16-bit) // 1 6 6 3 -- GitLab From edec62de621a69e66db6b753dacb4919da16f3ae Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Tue, 19 Mar 2019 17:59:24 -0700 Subject: [PATCH 1156/1530] Camera: Include the physical camera device during errors The physical camera device ID must be present as part of the capture result extras in case of corresponding result failure notification. Bug: 128835627 Test: Camera CTS, AImageReaderVendorTest --gtest_filter=AImageReaderVendorTest.LogicalCameraPhysicalStream Change-Id: I042af8bd85eaadd389b059c2833f352ceb2f40fc --- camera/CaptureResult.cpp | 20 +++++++ camera/include/camera/CaptureResult.h | 10 +++- camera/ndk/impl/ACameraDevice.cpp | 52 ++++++++++++++++- camera/ndk/impl/ACameraDevice.h | 5 +- .../include/camera/NdkCameraCaptureSession.h | 52 ++++++++++++++++- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 57 +++++++++++++++++-- camera/ndk/ndk_vendor/impl/ACameraDevice.h | 5 +- .../tests/AImageReaderVendorTest.cpp | 8 ++- .../device3/Camera3Device.cpp | 24 +++++++- .../camera/libcameraservice/hidl/Convert.cpp | 2 + 10 files changed, 221 insertions(+), 14 deletions(-) diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp index 928a6bca0a..1d8e8c42d0 100644 --- a/camera/CaptureResult.cpp +++ b/camera/CaptureResult.cpp @@ -39,6 +39,16 @@ status_t CaptureResultExtras::readFromParcel(const android::Parcel *parcel) { parcel->readInt64(&frameNumber); parcel->readInt32(&partialResultCount); parcel->readInt32(&errorStreamId); + auto physicalCameraIdPresent = parcel->readBool(); + if (physicalCameraIdPresent) { + String16 cameraId; + status_t res = OK; + if ((res = parcel->readString16(&cameraId)) != OK) { + ALOGE("%s: Failed to read camera id: %d", __FUNCTION__, res); + return res; + } + errorPhysicalCameraId = cameraId; + } return OK; } @@ -56,6 +66,16 @@ status_t CaptureResultExtras::writeToParcel(android::Parcel *parcel) const { parcel->writeInt64(frameNumber); parcel->writeInt32(partialResultCount); parcel->writeInt32(errorStreamId); + if (errorPhysicalCameraId.size() > 0) { + parcel->writeBool(true); + status_t res = OK; + if ((res = parcel->writeString16(errorPhysicalCameraId)) != OK) { + ALOGE("%s: Failed to write physical camera ID to parcel: %d", __FUNCTION__, res); + return res; + } + } else { + parcel->writeBool(false); + } return OK; } diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h index 56fa17886f..ef830b50b7 100644 --- a/camera/include/camera/CaptureResult.h +++ b/camera/include/camera/CaptureResult.h @@ -69,6 +69,13 @@ struct CaptureResultExtras : public android::Parcelable { */ int32_t errorStreamId; + /** + * For capture result errors, the physical camera ID in case the respective request contains + * a reference to physical camera device. + * Empty otherwise. + */ + String16 errorPhysicalCameraId; + /** * Constructor initializes object as invalid by setting requestId to be -1. */ @@ -79,7 +86,8 @@ struct CaptureResultExtras : public android::Parcelable { precaptureTriggerId(0), frameNumber(0), partialResultCount(0), - errorStreamId(-1) { + errorStreamId(-1), + errorPhysicalCameraId() { } /** diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 5e4fcd07fd..e52f075750 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -45,6 +45,7 @@ const char* CameraDevice::kCaptureFailureKey = "CaptureFailure"; const char* CameraDevice::kSequenceIdKey = "SequenceId"; const char* CameraDevice::kFrameNumberKey = "FrameNumber"; const char* CameraDevice::kAnwKey = "Anw"; +const char* CameraDevice::kFailingPhysicalCameraId= "FailingPhysicalCameraId"; /** * CameraDevice Implementation @@ -818,10 +819,19 @@ CameraDevice::onCaptureErrorLocked( failure->wasImageCaptured = (errorCode == hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT); - sp msg = new AMessage(kWhatCaptureFail, mHandler); + sp msg = new AMessage(cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureFail : + kWhatCaptureFail, mHandler); msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onError); + if (cbh.mIsLogicalCameraCallback) { + if (resultExtras.errorPhysicalCameraId.size() > 0) { + String8 cameraId(resultExtras.errorPhysicalCameraId); + msg->setString(kFailingPhysicalCameraId, cameraId.string(), cameraId.size()); + } + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnLogicalCameraCaptureFailed); + } else { + msg->setPointer(kCallbackFpKey, (void*) onError); + } msg->setObject(kCaptureRequestKey, request); msg->setObject(kCaptureFailureKey, failure); postSessionMsgAndCleanup(msg); @@ -846,6 +856,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: case kWhatCaptureBufferLost: @@ -917,6 +928,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: case kWhatCaptureBufferLost: @@ -935,6 +947,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureBufferLost: found = msg->findObject(kCaptureRequestKey, &obj); if (!found) { @@ -1089,6 +1102,39 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatLogicalCaptureFail: + { + ACameraCaptureSession_logicalCamera_captureCallback_failed onFail; + found = msg->findPointer(kCallbackFpKey, (void**) &onFail); + if (!found) { + ALOGE("%s: Cannot find capture fail callback!", __FUNCTION__); + return; + } + if (onFail == nullptr) { + return; + } + + found = msg->findObject(kCaptureFailureKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture failure!", __FUNCTION__); + return; + } + sp failureSp( + static_cast(obj.get())); + ALogicalCameraCaptureFailure failure; + AString physicalCameraId; + found = msg->findString(kFailingPhysicalCameraId, &physicalCameraId); + if (found && !physicalCameraId.empty()) { + failure.physicalCameraId = physicalCameraId.c_str(); + } else { + failure.physicalCameraId = nullptr; + } + failure.captureFailure = *failureSp; + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); + (*onFail)(context, session.get(), request, &failure); + freeACaptureRequest(request); + break; + } case kWhatCaptureSeqEnd: { ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd; @@ -1184,6 +1230,7 @@ CameraDevice::CallbackHolder::CallbackHolder( if (cbs != nullptr) { mOnCaptureCompleted = cbs->onCaptureCompleted; + mOnCaptureFailed = cbs->onCaptureFailed; } } @@ -1199,6 +1246,7 @@ CameraDevice::CallbackHolder::CallbackHolder( if (lcbs != nullptr) { mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed; } } diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 103efd5218..88916b2fae 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -210,6 +210,7 @@ class CameraDevice final : public RefBase { kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed + kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed kWhatCaptureSeqEnd, // onCaptureSequenceCompleted kWhatCaptureSeqAbort, // onCaptureSequenceAborted kWhatCaptureBufferLost,// onCaptureBufferLost @@ -229,6 +230,7 @@ class CameraDevice final : public RefBase { static const char* kSequenceIdKey; static const char* kFrameNumberKey; static const char* kAnwKey; + static const char* kFailingPhysicalCameraId; class CallbackHandler : public AHandler { public: @@ -277,6 +279,7 @@ class CameraDevice final : public RefBase { mOnCaptureProgressed = nullptr; mOnCaptureCompleted = nullptr; mOnLogicalCameraCaptureCompleted = nullptr; + mOnLogicalCameraCaptureFailed = nullptr; mOnCaptureFailed = nullptr; mOnCaptureSequenceCompleted = nullptr; mOnCaptureSequenceAborted = nullptr; @@ -285,7 +288,6 @@ class CameraDevice final : public RefBase { mContext = cbs->context; mOnCaptureStarted = cbs->onCaptureStarted; mOnCaptureProgressed = cbs->onCaptureProgressed; - mOnCaptureFailed = cbs->onCaptureFailed; mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; mOnCaptureBufferLost = cbs->onCaptureBufferLost; @@ -301,6 +303,7 @@ class CameraDevice final : public RefBase { ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; + ACameraCaptureSession_logicalCamera_captureCallback_failed mOnLogicalCameraCaptureFailed; ACameraCaptureSession_captureCallback_failed mOnCaptureFailed; ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted; ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted; diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index d13a818648..07176cf7f9 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -676,6 +676,41 @@ typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_result)( size_t physicalResultCount, const char** physicalCameraIds, const ACameraMetadata** physicalResults); +/// Struct to describe a logical camera capture failure +typedef struct ALogicalCameraCaptureFailure { + /** + * The {@link ACameraCaptureFailure} contains information about regular logical device capture + * failure. + */ + struct ACameraCaptureFailure captureFailure; + + /** + * The physical camera device ID in case the capture failure comes from a capture request + * with configured physical camera streams for a logical camera. physicalCameraId will be set + * to NULL in case the capture request has no associated physical camera device. + * + */ + const char* physicalCameraId; +} ALogicalCameraCaptureFailure; + +/** + * The definition of logical camera capture failure callback. + * + * @param context The optional application context provided by user in + * {@link ACameraCaptureSession_captureCallbacks}. + * @param session The camera capture session of interest. + * @param request The capture request of interest. Note that this pointer points to a copy of + * capture request sent by application, so the address is different to what + * application sent but the content will match. This request will be freed by + * framework immediately after this callback returns. + * @param failure The {@link ALogicalCameraCaptureFailure} desribes the capture failure. The memory + * is managed by camera framework. Do not access this pointer after this callback + * returns. + */ +typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_failed)( + void* context, ACameraCaptureSession* session, + ACaptureRequest* request, ALogicalCameraCaptureFailure* failure); + /** * This has the same functionality as ACameraCaptureSession_captureCallbacks, * with the exception that an onLogicalCameraCaptureCompleted callback is @@ -707,10 +742,25 @@ typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks { */ ACameraCaptureSession_logicalCamera_captureCallback_result onLogicalCameraCaptureCompleted; + /** + * This callback is called instead of {@link onLogicalCameraCaptureCompleted} when the + * camera device failed to produce a capture result for the + * request. + * + *

      Other requests are unaffected, and some or all image buffers from + * the capture may have been pushed to their respective output + * streams.

      + * + *

      Note that the ACaptureRequest pointer in the callback will not match what application has + * submitted, but the contents the ACaptureRequest will match what application submitted.

      + * + * @see ALogicalCameraCaptureFailure + */ + ACameraCaptureSession_logicalCamera_captureCallback_failed onLogicalCameraCaptureFailed; + /** * Same as ACameraCaptureSession_captureCallbacks */ - ACameraCaptureSession_captureCallback_failed onCaptureFailed; ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted; ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted; ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index a38a31e79c..bb1bf44e39 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -54,6 +54,7 @@ const char* CameraDevice::kCaptureFailureKey = "CaptureFailure"; const char* CameraDevice::kSequenceIdKey = "SequenceId"; const char* CameraDevice::kFrameNumberKey = "FrameNumber"; const char* CameraDevice::kAnwKey = "Anw"; +const char* CameraDevice::kFailingPhysicalCameraId= "FailingPhysicalCameraId"; /** * CameraDevice Implementation @@ -852,10 +853,19 @@ CameraDevice::onCaptureErrorLocked( failure->sequenceId = sequenceId; failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT); - sp msg = new AMessage(kWhatCaptureFail, mHandler); + sp msg = new AMessage(cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureFail : + kWhatCaptureFail, mHandler); msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onError); + if (cbh.mIsLogicalCameraCallback) { + if (resultExtras.errorPhysicalCameraId.size() > 0) { + msg->setString(kFailingPhysicalCameraId, resultExtras.errorPhysicalCameraId.c_str(), + resultExtras.errorPhysicalCameraId.size()); + } + msg->setPointer(kCallbackFpKey, (void*) cbh.mOnLogicalCameraCaptureFailed); + } else { + msg->setPointer(kCallbackFpKey, (void*) onError); + } msg->setObject(kCaptureRequestKey, request); msg->setObject(kCaptureFailureKey, failure); postSessionMsgAndCleanup(msg); @@ -877,6 +887,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: case kWhatCaptureBufferLost: @@ -948,6 +959,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureSeqEnd: case kWhatCaptureSeqAbort: case kWhatCaptureBufferLost: @@ -967,6 +979,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: + case kWhatLogicalCaptureFail: case kWhatCaptureBufferLost: found = msg->findObject(kCaptureRequestKey, &obj); if (!found) { @@ -1119,6 +1132,39 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatLogicalCaptureFail: + { + ACameraCaptureSession_logicalCamera_captureCallback_failed onFail; + found = msg->findPointer(kCallbackFpKey, (void**) &onFail); + if (!found) { + ALOGE("%s: Cannot find capture fail callback!", __FUNCTION__); + return; + } + if (onFail == nullptr) { + return; + } + + found = msg->findObject(kCaptureFailureKey, &obj); + if (!found) { + ALOGE("%s: Cannot find capture failure!", __FUNCTION__); + return; + } + sp failureSp( + static_cast(obj.get())); + ALogicalCameraCaptureFailure failure; + AString physicalCameraId; + found = msg->findString(kFailingPhysicalCameraId, &physicalCameraId); + if (found && !physicalCameraId.empty()) { + failure.physicalCameraId = physicalCameraId.c_str(); + } else { + failure.physicalCameraId = nullptr; + } + failure.captureFailure = *failureSp; + ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + (*onFail)(context, session.get(), request, &failure); + freeACaptureRequest(request); + break; + } case kWhatCaptureSeqEnd: { ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd; @@ -1214,6 +1260,7 @@ CameraDevice::CallbackHolder::CallbackHolder( if (cbs != nullptr) { mOnCaptureCompleted = cbs->onCaptureCompleted; + mOnCaptureFailed = cbs->onCaptureFailed; } } @@ -1229,6 +1276,7 @@ CameraDevice::CallbackHolder::CallbackHolder( if (lcbs != nullptr) { mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed; } } @@ -1326,8 +1374,9 @@ android::hardware::Return CameraDevice::ServiceCallback::onDeviceError( ErrorCode errorCode, const CaptureResultExtras& resultExtras) { - ALOGD("Device error received, code %d, frame number %" PRId64 ", request ID %d, subseq ID %d", - errorCode, resultExtras.frameNumber, resultExtras.requestId, resultExtras.burstId); + ALOGD("Device error received, code %d, frame number %" PRId64 ", request ID %d, subseq ID %d" + " physical camera ID %s", errorCode, resultExtras.frameNumber, resultExtras.requestId, + resultExtras.burstId, resultExtras.errorPhysicalCameraId.c_str()); auto ret = Void(); sp dev = mDevice.promote(); if (dev == nullptr) { diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 28092fd373..d4d74d1d8c 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -239,6 +239,7 @@ class CameraDevice final : public RefBase { kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed + kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed kWhatCaptureSeqEnd, // onCaptureSequenceCompleted kWhatCaptureSeqAbort, // onCaptureSequenceAborted kWhatCaptureBufferLost,// onCaptureBufferLost @@ -258,6 +259,7 @@ class CameraDevice final : public RefBase { static const char* kSequenceIdKey; static const char* kFrameNumberKey; static const char* kAnwKey; + static const char* kFailingPhysicalCameraId; class CallbackHandler : public AHandler { public: @@ -304,6 +306,7 @@ class CameraDevice final : public RefBase { mOnCaptureProgressed = nullptr; mOnCaptureCompleted = nullptr; mOnLogicalCameraCaptureCompleted = nullptr; + mOnLogicalCameraCaptureFailed = nullptr; mOnCaptureFailed = nullptr; mOnCaptureSequenceCompleted = nullptr; mOnCaptureSequenceAborted = nullptr; @@ -312,7 +315,6 @@ class CameraDevice final : public RefBase { mContext = cbs->context; mOnCaptureStarted = cbs->onCaptureStarted; mOnCaptureProgressed = cbs->onCaptureProgressed; - mOnCaptureFailed = cbs->onCaptureFailed; mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; mOnCaptureBufferLost = cbs->onCaptureBufferLost; @@ -329,6 +331,7 @@ class CameraDevice final : public RefBase { ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; + ACameraCaptureSession_logicalCamera_captureCallback_failed mOnLogicalCameraCaptureFailed; ACameraCaptureSession_captureCallback_failed mOnCaptureFailed; ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted; ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted; diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 2398922048..f78188e81a 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -317,7 +317,13 @@ class CameraHelper { } ch->mCompletedCaptureCallbackCount++; }, - nullptr, // onCaptureFailed + [] (void * /*ctx*/, ACameraCaptureSession* /*session*/, ACaptureRequest* /*request*/, + ALogicalCameraCaptureFailure* failure) { + if (failure->physicalCameraId) { + ALOGD("%s: Physical camera id: %s result failure", __FUNCTION__, + failure->physicalCameraId); + } + }, nullptr, // onCaptureSequenceCompleted nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 22e09e4f16..7ff4f18a24 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -3777,10 +3777,12 @@ void Camera3Device::notifyError(const camera3_error_msg_t &msg, hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR; int streamId = 0; + String16 physicalCameraId; if (msg.error_stream != NULL) { Camera3Stream *stream = Camera3Stream::cast(msg.error_stream); streamId = stream->getId(); + physicalCameraId = String16(stream->physicalCameraId()); } ALOGV("Camera %s: %s: HAL error, frame %d, stream %d: %d", mId.string(), __FUNCTION__, msg.frame_number, @@ -3802,13 +3804,29 @@ void Camera3Device::notifyError(const camera3_error_msg_t &msg, InFlightRequest &r = mInFlightMap.editValueAt(idx); r.requestStatus = msg.error_code; resultExtras = r.resultExtras; - if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == errorCode + bool logicalDeviceResultError = false; + if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == + errorCode) { + if (physicalCameraId.size() > 0) { + String8 cameraId(physicalCameraId); + if (r.physicalCameraIds.find(cameraId) == r.physicalCameraIds.end()) { + ALOGE("%s: Reported result failure for physical camera device: %s " + " which is not part of the respective request!", + __FUNCTION__, cameraId.string()); + break; + } + resultExtras.errorPhysicalCameraId = physicalCameraId; + } else { + logicalDeviceResultError = true; + } + } + + if (logicalDeviceResultError || hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST == errorCode) { r.skipResultMetadata = true; } - if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == - errorCode) { + if (logicalDeviceResultError) { // In case of missing result check whether the buffers // returned. If they returned, then remove inflight // request. diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp index a87812b6b4..51d08ff604 100644 --- a/services/camera/libcameraservice/hidl/Convert.cpp +++ b/services/camera/libcameraservice/hidl/Convert.cpp @@ -142,6 +142,8 @@ HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtra hCaptureResultExtras.frameNumber = captureResultExtras.frameNumber; hCaptureResultExtras.partialResultCount = captureResultExtras.partialResultCount; hCaptureResultExtras.errorStreamId = captureResultExtras.errorStreamId; + hCaptureResultExtras.errorPhysicalCameraId = hidl_string(String8( + captureResultExtras.errorPhysicalCameraId).string()); return hCaptureResultExtras; } -- GitLab From 32ebca3611c530fc0f36225dcb1ca2ec50e8d9f1 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 22 Mar 2019 15:42:52 -0700 Subject: [PATCH 1157/1530] AudioPolicyManager: Stop the descriptor when closing its output This fixes the case when the SwAudioOutputDescriptor is part of duplicated output. In this case, APM::closeOutput finds the duplicated output, deactivates all the clients for all outputs, then calls 'close' on the descriptor. Since there are no active clients, closing the descriptor does not stop it, leaving pending 'active count' in the profile. Bug: 128362999 Test: steps from b/124415601 Change-Id: I8fa60ef50efa74ca8cf6e3312e1075a9ec9c63f3 --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e8e9fa6ac5..c97129903c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -4828,6 +4828,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) ALOGW("closeOutput() unknown output %d", output); return; } + const bool closingOutputWasActive = closingOutput->isActive(); mPolicyMixes.closeOutput(closingOutput); // look for duplicated outputs connected to the output being removed. @@ -4867,6 +4868,9 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) mpClientInterface->onAudioPatchListUpdate(); } + if (closingOutputWasActive) { + closingOutput->stop(); + } closingOutput->close(); removeOutput(output); -- GitLab From 664274322c652f5b4ccf3b3fbcc79f290a0204ea Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 21 Mar 2019 15:06:22 -0700 Subject: [PATCH 1158/1530] CCodec: fix EOS placement with reordering Bug: 118397206 Test: manual tests Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I0172ba38c85e25fd173c51261db609ba929b6d28 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index a6fa33362a..9f2fd0e650 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1525,6 +1525,7 @@ void CCodecBufferChannel::ReorderStash::setDepth(uint32_t depth) { mPending.splice(mPending.end(), mStash); mDepth = depth; } + void CCodecBufferChannel::ReorderStash::setKey(C2Config::ordinal_key_t key) { mPending.splice(mPending.end(), mStash); mKey = key; @@ -1547,13 +1548,25 @@ void CCodecBufferChannel::ReorderStash::emplace( int64_t timestamp, int32_t flags, const C2WorkOrdinalStruct &ordinal) { - auto it = mStash.begin(); - for (; it != mStash.end(); ++it) { - if (less(ordinal, it->ordinal)) { - break; + bool eos = flags & MediaCodec::BUFFER_FLAG_EOS; + if (!buffer && eos) { + // TRICKY: we may be violating ordering of the stash here. Because we + // don't expect any more emplace() calls after this, the ordering should + // not matter. + mStash.emplace_back(buffer, timestamp, flags, ordinal); + } else { + flags = flags & ~MediaCodec::BUFFER_FLAG_EOS; + auto it = mStash.begin(); + for (; it != mStash.end(); ++it) { + if (less(ordinal, it->ordinal)) { + break; + } + } + mStash.emplace(it, buffer, timestamp, flags, ordinal); + if (eos) { + mStash.back().flags = mStash.back().flags | MediaCodec::BUFFER_FLAG_EOS; } } - mStash.emplace(it, buffer, timestamp, flags, ordinal); while (!mStash.empty() && mStash.size() > mDepth) { mPending.push_back(mStash.front()); mStash.pop_front(); @@ -2814,8 +2827,9 @@ void CCodecBufferChannel::sendOutputBuffers() { outBuffer->meta()->setInt64("timeUs", entry.timestamp); outBuffer->meta()->setInt32("flags", entry.flags); - ALOGV("[%s] sendOutputBuffers: out buffer index = %zu [%p] => %p + %zu", - mName, index, outBuffer.get(), outBuffer->data(), outBuffer->size()); + ALOGV("[%s] sendOutputBuffers: out buffer index = %zu [%p] => %p + %zu (%lld)", + mName, index, outBuffer.get(), outBuffer->data(), outBuffer->size(), + (long long)entry.timestamp); mCallback->onOutputBufferAvailable(index, outBuffer); } } -- GitLab From 36b17553b2c3aad4d8f600edd2e82f9a583ee05a Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 7 Mar 2019 18:48:07 -0800 Subject: [PATCH 1159/1530] Introduce a system APC with its corresponding opt-out For system application with the new permission CAPTURE_MEDIA_OUTPUT, or CAPTURE_AUDIO_OUTPUT, allow to capture the audio of playing apps that allow it. Test: adb shell audiorecorder --target /data/file1.raw Test: atest android.media.cts.AudioPlaybackCaptureTest Bug: 111453086 Change-Id: I5bfca51e48992234508897c595a076d066db26b2 Signed-off-by: Kevin Rocard --- media/libaudioclient/AudioPolicy.cpp | 2 ++ media/libaudioclient/include/media/AudioPolicy.h | 2 ++ media/libmedia/TypeConverter.cpp | 3 ++- media/utils/ServiceUtilities.cpp | 8 ++++++++ media/utils/include/mediautils/ServiceUtilities.h | 1 + .../common/managerdefinitions/src/AudioPolicyMix.cpp | 7 ++++++- .../audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 10 +++++++++- 7 files changed, 30 insertions(+), 3 deletions(-) diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp index 3ab38cd259..65e797fa1e 100644 --- a/media/libaudioclient/AudioPolicy.cpp +++ b/media/libaudioclient/AudioPolicy.cpp @@ -97,6 +97,7 @@ status_t AudioMix::readFromParcel(Parcel *parcel) mDeviceType = (audio_devices_t) parcel->readInt32(); mDeviceAddress = parcel->readString8(); mCbFlags = (uint32_t)parcel->readInt32(); + mAllowPrivilegedPlaybackCapture = parcel->readBool(); size_t size = (size_t)parcel->readInt32(); if (size > MAX_CRITERIA_PER_MIX) { size = MAX_CRITERIA_PER_MIX; @@ -120,6 +121,7 @@ status_t AudioMix::writeToParcel(Parcel *parcel) const parcel->writeInt32(mDeviceType); parcel->writeString8(mDeviceAddress); parcel->writeInt32(mCbFlags); + parcel->writeBool(mAllowPrivilegedPlaybackCapture); size_t size = mCriteria.size(); if (size > MAX_CRITERIA_PER_MIX) { size = MAX_CRITERIA_PER_MIX; diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index bf8d62795c..4b94c12987 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -114,6 +114,8 @@ public: audio_devices_t mDeviceType; String8 mDeviceAddress; uint32_t mCbFlags; // flags indicating which callbacks to use, see kCbFlag* + /** Ignore the AUDIO_FLAG_NO_MEDIA_PROJECTION */ + bool mAllowPrivilegedPlaybackCapture = false; }; diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp index 469c5b6948..5be78d17fa 100644 --- a/media/libmedia/TypeConverter.cpp +++ b/media/libmedia/TypeConverter.cpp @@ -392,7 +392,8 @@ const AudioFlagConverter::Table AudioFlagConverter::mTable[] = { MAKE_STRING_FROM_ENUM(AUDIO_FLAG_BYPASS_MUTE), MAKE_STRING_FROM_ENUM(AUDIO_FLAG_LOW_LATENCY), MAKE_STRING_FROM_ENUM(AUDIO_FLAG_DEEP_BUFFER), - MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_CAPTURE), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_MEDIA_PROJECTION), + MAKE_STRING_FROM_ENUM(AUDIO_FLAG_NO_SYSTEM_CAPTURE), TERMINATOR }; diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 2fb24f576c..cb681e0f44 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -130,6 +130,14 @@ bool captureAudioOutputAllowed(pid_t pid, uid_t uid) { return ok; } +bool captureMediaOutputAllowed(pid_t pid, uid_t uid) { + if (isAudioServerOrRootUid(uid)) return true; + static const String16 sCaptureMediaOutput("android.permission.CAPTURE_MEDIA_OUTPUT"); + bool ok = PermissionCache::checkPermission(sCaptureMediaOutput, pid, uid); + if (!ok) ALOGE("Request requires android.permission.CAPTURE_MEDIA_OUTPUT"); + return ok; +} + bool captureHotwordAllowed(pid_t pid, uid_t uid) { // CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission bool ok = recordingAllowed(String16(""), pid, uid); diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 94370ee91e..9377ff31f1 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -74,6 +74,7 @@ bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid); bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid); void finishRecording(const String16& opPackageName, uid_t uid); bool captureAudioOutputAllowed(pid_t pid, uid_t uid); +bool captureMediaOutputAllowed(pid_t pid, uid_t uid); bool captureHotwordAllowed(pid_t pid, uid_t uid); bool settingsAllowed(); bool modifyAudioRoutingAllowed(); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index f7289ca112..f02db6a936 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -180,7 +180,12 @@ AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( // Loopback render mixes are created from a public API and thus restricted // to non sensible audio that have not opted out. if (is_mix_loopback_render(mix->mRouteFlags)) { - if ((attributes.flags & AUDIO_FLAG_NO_CAPTURE) == AUDIO_FLAG_NO_CAPTURE) { + auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; }; + if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) { + return MixMatchStatus::NO_MATCH; + } + if (!mix->mAllowPrivilegedPlaybackCapture && + hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) { return MixMatchStatus::NO_MATCH; } if (!(attributes.usage == AUDIO_USAGE_UNKNOWN || diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index a672521de8..1ec326254e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -192,7 +192,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *original } audio_attributes_t attr = *originalAttr; if (!mPackageManager.allowPlaybackCapture(uid)) { - attr.flags |= AUDIO_FLAG_NO_CAPTURE; + attr.flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION; } audio_output_flags_t originalFlags = flags; AutoCallerClear acc; @@ -1080,6 +1080,14 @@ status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, return PERMISSION_DENIED; } + bool needCaptureMediaOutput = std::any_of(mixes.begin(), mixes.end(), [](auto& mix) { + return mix.mAllowPrivilegedPlaybackCapture; }); + const uid_t callingUid = IPCThreadState::self()->getCallingUid(); + const pid_t callingPid = IPCThreadState::self()->getCallingPid(); + if (needCaptureMediaOutput && !captureMediaOutputAllowed(callingPid, callingUid)) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { return NO_INIT; } -- GitLab From 68646ba64107a19ac0d1af781ddd0b1edd2ed57a Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 20 Mar 2019 13:26:49 -0700 Subject: [PATCH 1160/1530] Add aaudio APC opt-out The opt-out was only present in the java API but not on the native ones. Test: atest test_attributes Change-Id: I1b84f1a428508e00de65e615b59405b9ee2ba009 Signed-off-by: Kevin Rocard --- .../examples/utils/AAudioArgsParser.h | 58 ++++++++++++++ media/libaaudio/include/aaudio/AAudio.h | 76 ++++++++++++++++++- .../src/binding/AAudioStreamConfiguration.cpp | 5 ++ media/libaaudio/src/core/AAudioAudio.cpp | 13 ++++ .../src/core/AAudioStreamParameters.cpp | 58 ++++++++------ .../src/core/AAudioStreamParameters.h | 31 +++++--- media/libaaudio/src/core/AudioStream.cpp | 8 +- media/libaaudio/src/core/AudioStream.h | 12 +++ .../libaaudio/src/legacy/AudioStreamTrack.cpp | 4 +- .../libaaudio/src/utility/AAudioUtilities.cpp | 16 ++++ media/libaaudio/src/utility/AAudioUtilities.h | 8 ++ media/libaaudio/tests/test_attributes.cpp | 40 +++++++++- .../oboeservice/AAudioServiceEndpointMMAP.cpp | 4 +- 13 files changed, 293 insertions(+), 40 deletions(-) diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h index f5ed7aa867..755ecc53ae 100644 --- a/media/libaaudio/examples/utils/AAudioArgsParser.h +++ b/media/libaaudio/examples/utils/AAudioArgsParser.h @@ -36,11 +36,14 @@ static void (*s_setContentType)(AAudioStreamBuilder* builder, aaudio_content_type_t contentType) = nullptr; static void (*s_setInputPreset)(AAudioStreamBuilder* builder, aaudio_input_preset_t inputPreset) = nullptr; +static void (*s_setAllowedCapturePolicy)(AAudioStreamBuilder* builder, + aaudio_allowed_capture_policy_t usage) = nullptr; static bool s_loadAttempted = false; static aaudio_usage_t (*s_getUsage)(AAudioStream *stream) = nullptr; static aaudio_content_type_t (*s_getContentType)(AAudioStream *stream) = nullptr; static aaudio_input_preset_t (*s_getInputPreset)(AAudioStream *stream) = nullptr; +static aaudio_allowed_capture_policy_t (*s_getAllowedCapturePolicy)(AAudioStream *stream) = nullptr; // Link to test functions in shared library. static void loadFutureFunctions() { @@ -61,6 +64,10 @@ static void loadFutureFunctions() { dlsym(handle, "AAudioStreamBuilder_setInputPreset"); if (s_setInputPreset == nullptr) goto error; + s_setAllowedCapturePolicy = (void (*)(AAudioStreamBuilder *, aaudio_input_preset_t)) + dlsym(handle, "AAudioStreamBuilder_setAllowedCapturePolicy"); + if (s_setAllowedCapturePolicy == nullptr) goto error; + s_getUsage = (aaudio_usage_t (*)(AAudioStream *)) dlsym(handle, "AAudioStream_getUsage"); if (s_getUsage == nullptr) goto error; @@ -72,6 +79,10 @@ static void loadFutureFunctions() { s_getInputPreset = (aaudio_input_preset_t (*)(AAudioStream *)) dlsym(handle, "AAudioStream_getInputPreset"); if (s_getInputPreset == nullptr) goto error; + + s_getAllowedCapturePolicy = (aaudio_input_preset_t (*)(AAudioStream *)) + dlsym(handle, "AAudioStream_getAllowedCapturePolicy"); + if (s_getAllowedCapturePolicy == nullptr) goto error; } return; @@ -169,6 +180,14 @@ public: mInputPreset = inputPreset; } + aaudio_allowed_capture_policy_t getAllowedCapturePolicy() const { + return mAllowedCapturePolicy; + } + + void setAllowedCapturePolicy(aaudio_allowed_capture_policy_t policy) { + mAllowedCapturePolicy = policy; + } + int32_t getDeviceId() const { return mDeviceId; } @@ -223,6 +242,13 @@ public: } else if (mUsage != AAUDIO_UNSPECIFIED){ printf("WARNING: setInputPreset not supported"); } + + // Call Q functions if supported. + if (s_setAllowedCapturePolicy != nullptr) { + s_setAllowedCapturePolicy(builder, mAllowedCapturePolicy); + } else if (mAllowedCapturePolicy != AAUDIO_UNSPECIFIED){ + printf("WARNING: setAllowedCapturePolicy not supported"); + } } private: @@ -238,6 +264,7 @@ private: aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED; aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; + aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED; int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED; int32_t mFramesPerCallback = AAUDIO_UNSPECIFIED; @@ -267,6 +294,9 @@ public: case 'c': setChannelCount(atoi(&arg[2])); break; + case 'C': + setAllowedCapturePolicy(parseAllowedCapturePolicy(arg[2])); + break; case 'd': setDeviceId(atoi(&arg[2])); break; @@ -341,6 +371,10 @@ public: printf(" Default values are UNSPECIFIED unless otherwise stated.\n"); printf(" -b{bufferCapacity} frames\n"); printf(" -c{channels} for example 2 for stereo\n"); + printf(" -C{a|s|n} set playback capture policy\n"); + printf(" a = _ALL (default)\n"); + printf(" s = _SYSTEM\n"); + printf(" n = _NONE\n"); printf(" -d{deviceId} default is %d\n", AAUDIO_UNSPECIFIED); printf(" -f{0|1|2} set format\n"); printf(" 0 = UNSPECIFIED\n"); @@ -365,6 +399,25 @@ public: printf(" -z{callbackSize} or block size, in frames, default = 0\n"); } + static aaudio_performance_mode_t parseAllowedCapturePolicy(char c) { + aaudio_allowed_capture_policy_t policy = AAUDIO_ALLOW_CAPTURE_BY_ALL; + switch (c) { + case 'a': + policy = AAUDIO_ALLOW_CAPTURE_BY_ALL; + break; + case 's': + policy = AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; + break; + case 'n': + policy = AAUDIO_ALLOW_CAPTURE_BY_NONE; + break; + default: + printf("ERROR: invalid playback capture policy %c\n", c); + break; + } + return policy; + } + static aaudio_performance_mode_t parsePerformanceMode(char c) { aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE; switch (c) { @@ -449,6 +502,11 @@ public: printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream) ? "yes" : "no"); + if (s_getAllowedCapturePolicy != nullptr) { + printf(" ContentType: requested = %d, actual = %d\n", + getAllowedCapturePolicy(), s_getAllowedCapturePolicy(stream)); + } + } int32_t getDurationSeconds() const { diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 8e36c77a3d..19e22f194f 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -232,7 +232,8 @@ typedef int32_t aaudio_performance_mode_t; * This information is used by certain platforms or routing policies * to make more refined volume or routing decisions. * - * Note that these match the equivalent values in AudioAttributes in the Android Java API. + * Note that these match the equivalent values in {@link android.media.AudioAttributes} + * in the Android Java API. * * Added in API level 28. */ @@ -308,7 +309,8 @@ typedef int32_t aaudio_usage_t; * an audio book application) this information might be used by the audio framework to * enforce audio focus. * - * Note that these match the equivalent values in AudioAttributes in the Android Java API. + * Note that these match the equivalent values in {@link android.media.AudioAttributes} + * in the Android Java API. * * Added in API level 28. */ @@ -383,6 +385,48 @@ enum { }; typedef int32_t aaudio_input_preset_t; +/** + * Specifying if audio may or may not be captured by other apps or the system. + * + * Note that these match the equivalent values in {@link android.media.AudioAttributes} + * in the Android Java API. + * + * Added in API level 29. + */ +enum { + /** + * Indicates that the audio may be captured by any app. + * + * For privacy, the following usages can not be recorded: AAUDIO_VOICE_COMMUNICATION*, + * AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and AAUDIO_USAGE_ASSISTANT. + * + * On {@link Android.os.Build.VERSION_CODES.Q}, this means only {@link AAUDIO_USAGE_MEDIA} + * and {@link AAUDIO_USAGE_GAME} may be captured. + * + * See {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL}. + */ + AAUDIO_ALLOW_CAPTURE_BY_ALL = 1, + /** + * Indicates that the audio may only be captured by system apps. + * + * System apps can capture for many purposes like accessibility, user guidance... + * but have strong restriction. See + * {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM} for what the system apps + * can do with the capture audio. + */ + AAUDIO_ALLOW_CAPTURE_BY_SYSTEM = 2, + /** + * Indicates that the audio may not be recorded by any app, even if it is a system app. + * + * It is encouraged to use {@link ALLOW_CAPTURE_BY_SYSTEM} instead of this value as system apps + * provide significant and useful features for the user (eg. accessibility). + * See {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE}. + */ + AAUDIO_ALLOW_CAPTURE_BY_NONE = 3, +}; + +typedef int32_t aaudio_allowed_capture_policy_t; + /** * These may be used with AAudioStreamBuilder_setSessionId(). * @@ -643,6 +687,22 @@ AAUDIO_API void AAudioStreamBuilder_setContentType(AAudioStreamBuilder* builder, AAUDIO_API void AAudioStreamBuilder_setInputPreset(AAudioStreamBuilder* builder, aaudio_input_preset_t inputPreset) __INTRODUCED_IN(28); +/** + * Specify whether this stream audio may or may not be captured by other apps or the system. + * + * The default is AAUDIO_ALLOW_CAPTURE_BY_ALL. + * + * Note that an application can also set its global policy, in which case the most restrictive + * policy is always applied. See {@link android.media.AudioAttributes.setAllowedCapturePolicy} + * + * Added in API level 29. + * + * @param builder reference provided by AAudio_createStreamBuilder() + * @param inputPreset the desired level of opt-out from being captured. + */ +AAUDIO_API void AAudioStreamBuilder_setAllowedCapturePolicy(AAudioStreamBuilder* builder, + aaudio_allowed_capture_policy_t capturePolicy) __INTRODUCED_IN(29); + /** Set the requested session ID. * * The session ID can be used to associate a stream with effects processors. @@ -1277,6 +1337,18 @@ AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* strea AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* stream) __INTRODUCED_IN(28); +/** + * Return the policy that determines whether the audio may or may not be captured + * by other apps or the system. + * + * Added in API level 29. + * + * @param stream reference provided by AAudioStreamBuilder_openStream() + * @return the allowed capture policy, for example AAUDIO_ALLOW_CAPTURE_BY_ALL + */ +AAUDIO_API aaudio_allowed_capture_policy_t AAudioStream_getAllowedCapturePolicy( + AAudioStream* stream) __INTRODUCED_IN(29); + #ifdef __cplusplus } #endif diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp index 3d1bc9b7a9..a987fab90a 100644 --- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp +++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp @@ -62,6 +62,8 @@ status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const { if (status != NO_ERROR) goto error; status = parcel->writeInt32((int32_t) getInputPreset()); if (status != NO_ERROR) goto error; + status = parcel->writeInt32((int32_t) getAllowedCapturePolicy()); + if (status != NO_ERROR) goto error; status = parcel->writeInt32(getSessionId()); if (status != NO_ERROR) goto error; return NO_ERROR; @@ -105,6 +107,9 @@ status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) { setInputPreset((aaudio_input_preset_t) value); status = parcel->readInt32(&value); if (status != NO_ERROR) goto error; + setAllowedCapturePolicy((aaudio_allowed_capture_policy_t) value); + status = parcel->readInt32(&value); + if (status != NO_ERROR) goto error; setSessionId(value); return NO_ERROR; diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp index 0d71efc22e..8ae2644a4e 100644 --- a/media/libaaudio/src/core/AAudioAudio.cpp +++ b/media/libaaudio/src/core/AAudioAudio.cpp @@ -204,6 +204,12 @@ AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilde streamBuilder->setBufferCapacity(frames); } +AAUDIO_API void AAudioStreamBuilder_setAllowedCapturePolicy( + AAudioStreamBuilder* builder, aaudio_allowed_capture_policy_t policy) { + AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder); + streamBuilder->setAllowedCapturePolicy(policy); +} + AAUDIO_API void AAudioStreamBuilder_setSessionId(AAudioStreamBuilder* builder, aaudio_session_id_t sessionId) { @@ -494,6 +500,13 @@ AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* strea return audioStream->getInputPreset(); } +AAUDIO_API aaudio_allowed_capture_policy_t AAudioStream_getAllowedCapturePolicy( + AAudioStream* stream) +{ + AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); + return audioStream->getAllowedCapturePolicy(); +} + AAUDIO_API int32_t AAudioStream_getSessionId(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp index 88da53a3e8..e5bda30864 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.cpp +++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp @@ -35,17 +35,18 @@ AAudioStreamParameters::AAudioStreamParameters() {} AAudioStreamParameters::~AAudioStreamParameters() {} void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) { - mSamplesPerFrame = other.mSamplesPerFrame; - mSampleRate = other.mSampleRate; - mDeviceId = other.mDeviceId; - mSessionId = other.mSessionId; - mSharingMode = other.mSharingMode; - mAudioFormat = other.mAudioFormat; - mDirection = other.mDirection; - mBufferCapacity = other.mBufferCapacity; - mUsage = other.mUsage; - mContentType = other.mContentType; - mInputPreset = other.mInputPreset; + mSamplesPerFrame = other.mSamplesPerFrame; + mSampleRate = other.mSampleRate; + mDeviceId = other.mDeviceId; + mSessionId = other.mSessionId; + mSharingMode = other.mSharingMode; + mAudioFormat = other.mAudioFormat; + mDirection = other.mDirection; + mBufferCapacity = other.mBufferCapacity; + mUsage = other.mUsage; + mContentType = other.mContentType; + mInputPreset = other.mInputPreset; + mAllowedCapturePolicy = other.mAllowedCapturePolicy; } static aaudio_result_t isFormatValid(audio_format_t format) { @@ -166,19 +167,32 @@ aaudio_result_t AAudioStreamParameters::validate() const { // break; } + switch (mAllowedCapturePolicy) { + case AAUDIO_UNSPECIFIED: + case AAUDIO_ALLOW_CAPTURE_BY_ALL: + case AAUDIO_ALLOW_CAPTURE_BY_SYSTEM: + case AAUDIO_ALLOW_CAPTURE_BY_NONE: + break; // valid + default: + ALOGE("allowed capture policy not valid = %d", mAllowedCapturePolicy); + return AAUDIO_ERROR_ILLEGAL_ARGUMENT; + // break; + } + return AAUDIO_OK; } void AAudioStreamParameters::dump() const { - ALOGD("mDeviceId = %6d", mDeviceId); - ALOGD("mSessionId = %6d", mSessionId); - ALOGD("mSampleRate = %6d", mSampleRate); - ALOGD("mSamplesPerFrame = %6d", mSamplesPerFrame); - ALOGD("mSharingMode = %6d", (int)mSharingMode); - ALOGD("mAudioFormat = %6d", (int)mAudioFormat); - ALOGD("mDirection = %6d", mDirection); - ALOGD("mBufferCapacity = %6d", mBufferCapacity); - ALOGD("mUsage = %6d", mUsage); - ALOGD("mContentType = %6d", mContentType); - ALOGD("mInputPreset = %6d", mInputPreset); + ALOGD("mDeviceId = %6d", mDeviceId); + ALOGD("mSessionId = %6d", mSessionId); + ALOGD("mSampleRate = %6d", mSampleRate); + ALOGD("mSamplesPerFrame = %6d", mSamplesPerFrame); + ALOGD("mSharingMode = %6d", (int)mSharingMode); + ALOGD("mAudioFormat = %6d", (int)mAudioFormat); + ALOGD("mDirection = %6d", mDirection); + ALOGD("mBufferCapacity = %6d", mBufferCapacity); + ALOGD("mUsage = %6d", mUsage); + ALOGD("mContentType = %6d", mContentType); + ALOGD("mInputPreset = %6d", mInputPreset); + ALOGD("mAllowedCapturePolicy = %6d", mAllowedCapturePolicy); } diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h index 6beb4b2469..2e21a8d62c 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.h +++ b/media/libaaudio/src/core/AAudioStreamParameters.h @@ -112,6 +112,14 @@ public: mInputPreset = inputPreset; } + aaudio_allowed_capture_policy_t getAllowedCapturePolicy() const { + return mAllowedCapturePolicy; + } + + void setAllowedCapturePolicy(aaudio_allowed_capture_policy_t policy) { + mAllowedCapturePolicy = policy; + } + aaudio_session_id_t getSessionId() const { return mSessionId; } @@ -138,17 +146,18 @@ public: void dump() const; private: - int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED; - int32_t mSampleRate = AAUDIO_UNSPECIFIED; - int32_t mDeviceId = AAUDIO_UNSPECIFIED; - aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED; - audio_format_t mAudioFormat = AUDIO_FORMAT_DEFAULT; - aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT; - aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED; - aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; - aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; - int32_t mBufferCapacity = AAUDIO_UNSPECIFIED; - aaudio_session_id_t mSessionId = AAUDIO_SESSION_ID_NONE; + int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED; + int32_t mSampleRate = AAUDIO_UNSPECIFIED; + int32_t mDeviceId = AAUDIO_UNSPECIFIED; + aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED; + audio_format_t mAudioFormat = AUDIO_FORMAT_DEFAULT; + aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT; + aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED; + aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; + aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; + int32_t mBufferCapacity = AAUDIO_UNSPECIFIED; + aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_UNSPECIFIED; + aaudio_session_id_t mSessionId = AAUDIO_SESSION_ID_NONE; }; } /* namespace aaudio */ diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp index e39a075161..732d45c96f 100644 --- a/media/libaaudio/src/core/AudioStream.cpp +++ b/media/libaaudio/src/core/AudioStream.cpp @@ -94,6 +94,10 @@ aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) if (mInputPreset == AAUDIO_UNSPECIFIED) { mInputPreset = AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; } + mAllowedCapturePolicy = builder.getAllowedCapturePolicy(); + if (mAllowedCapturePolicy == AAUDIO_UNSPECIFIED) { + mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL; + } // callbacks mFramesPerDataCallback = builder.getFramesPerDataCallback(); @@ -113,8 +117,8 @@ aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) mPerformanceMode, (isDataCallbackSet() ? "ON" : "OFF"), mFramesPerDataCallback); - ALOGI("open() usage = %d, contentType = %d, inputPreset = %d", - mUsage, mContentType, mInputPreset); + ALOGI("open() usage = %d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d", + mUsage, mContentType, mInputPreset, mAllowedCapturePolicy); return AAUDIO_OK; } diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h index 46951f50fe..32713b1f41 100644 --- a/media/libaaudio/src/core/AudioStream.h +++ b/media/libaaudio/src/core/AudioStream.h @@ -219,6 +219,10 @@ public: return mInputPreset; } + aaudio_allowed_capture_policy_t getAllowedCapturePolicy() const { + return mAllowedCapturePolicy; + } + int32_t getSessionId() const { return mSessionId; } @@ -525,6 +529,13 @@ protected: mInputPreset = inputPreset; } + /** + * This should not be called after the open() call. + */ + void setAllowedCapturePolicy(aaudio_allowed_capture_policy_t policy) { + mAllowedCapturePolicy = policy; + } + private: aaudio_result_t safeStop(); @@ -546,6 +557,7 @@ private: aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED; aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; + aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL; int32_t mSessionId = AAUDIO_UNSPECIFIED; diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index ff95aedcb6..e359c1c9df 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -134,12 +134,14 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) AAudioConvert_contentTypeToInternal(builder.getContentType()); const audio_usage_t usage = AAudioConvert_usageToInternal(builder.getUsage()); + const audio_flags_mask_t attributesFlags = + AAudioConvert_allowCapturePolicyToAudioFlagsMask(builder.getAllowedCapturePolicy()); const audio_attributes_t attributes = { .content_type = contentType, .usage = usage, .source = AUDIO_SOURCE_DEFAULT, // only used for recording - .flags = AUDIO_FLAG_NONE, // Different than the AUDIO_OUTPUT_FLAGS + .flags = attributesFlags, .tags = "" }; diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp index 723cbf11f7..96ed56a284 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.cpp +++ b/media/libaaudio/src/utility/AAudioUtilities.cpp @@ -217,6 +217,22 @@ audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t pres return (audio_source_t) preset; // same value } +audio_flags_mask_t AAudioConvert_allowCapturePolicyToAudioFlagsMask( + aaudio_allowed_capture_policy_t policy) { + switch (policy) { + case AAUDIO_UNSPECIFIED: + case AAUDIO_ALLOW_CAPTURE_BY_ALL: + return AUDIO_FLAG_NONE; + case AAUDIO_ALLOW_CAPTURE_BY_SYSTEM: + return AUDIO_FLAG_NO_MEDIA_PROJECTION; + case AAUDIO_ALLOW_CAPTURE_BY_NONE: + return AUDIO_FLAG_NO_MEDIA_PROJECTION | AUDIO_FLAG_NO_SYSTEM_CAPTURE; + default: + ALOGE("%s() 0x%08X unrecognized", __func__, policy); + return AUDIO_FLAG_NONE; // + } +} + int32_t AAudioConvert_framesToBytes(int32_t numFrames, int32_t bytesPerFrame, int32_t *sizeInBytes) { diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h index dc2b198d8e..76d04578e9 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.h +++ b/media/libaaudio/src/utility/AAudioUtilities.h @@ -84,6 +84,14 @@ audio_content_type_t AAudioConvert_contentTypeToInternal(aaudio_content_type_t c */ audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t preset); +/** + * Note that this function does not validate the passed in value. + * That is done somewhere else. + * @return internal audio flags mask + */ +audio_flags_mask_t AAudioConvert_allowCapturePolicyToAudioFlagsMask( + aaudio_allowed_capture_policy_t policy); + // Note that this code may be replaced by Settings or by some other system configuration tool. #define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy" diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp index dbf87122a9..32ee2a33a5 100644 --- a/media/libaaudio/tests/test_attributes.cpp +++ b/media/libaaudio/tests/test_attributes.cpp @@ -32,6 +32,7 @@ static void checkAttributes(aaudio_performance_mode_t perfMode, aaudio_usage_t usage, aaudio_content_type_t contentType, aaudio_input_preset_t preset = DONT_SET, + aaudio_allowed_capture_policy_t capturePolicy = DONT_SET, aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT) { float *buffer = new float[kNumFrames * kChannelCount]; @@ -56,6 +57,9 @@ static void checkAttributes(aaudio_performance_mode_t perfMode, if (preset != DONT_SET) { AAudioStreamBuilder_setInputPreset(aaudioBuilder, preset); } + if (capturePolicy != DONT_SET) { + AAudioStreamBuilder_setAllowedCapturePolicy(aaudioBuilder, capturePolicy); + } // Create an AAudioStream using the Builder. ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream)); @@ -80,6 +84,12 @@ static void checkAttributes(aaudio_performance_mode_t perfMode, : preset; EXPECT_EQ(expectedPreset, AAudioStream_getInputPreset(aaudioStream)); + aaudio_allowed_capture_policy_t expectedCapturePolicy = + (capturePolicy == DONT_SET || capturePolicy == AAUDIO_UNSPECIFIED) + ? AAUDIO_ALLOW_CAPTURE_BY_ALL // default + : preset; + EXPECT_EQ(expectedCapturePolicy, AAudioStream_getAllowedCapturePolicy(aaudioStream)); + EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); if (direction == AAUDIO_DIRECTION_INPUT) { @@ -133,13 +143,21 @@ static const aaudio_input_preset_t sInputPresets[] = { AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE, }; +static const aaudio_input_preset_t sAllowCapturePolicies[] = { + DONT_SET, + AAUDIO_UNSPECIFIED, + AAUDIO_ALLOW_CAPTURE_BY_ALL, + AAUDIO_ALLOW_CAPTURE_BY_SYSTEM, + AAUDIO_ALLOW_CAPTURE_BY_NONE, +}; + static void checkAttributesUsage(aaudio_performance_mode_t perfMode) { for (aaudio_usage_t usage : sUsages) { checkAttributes(perfMode, usage, DONT_SET); } } -static void checkAttributesContentType(aaudio_input_preset_t perfMode) { +static void checkAttributesContentType(aaudio_performance_mode_t perfMode) { for (aaudio_content_type_t contentType : sContentypes) { checkAttributes(perfMode, DONT_SET, contentType); } @@ -151,6 +169,18 @@ static void checkAttributesInputPreset(aaudio_performance_mode_t perfMode) { DONT_SET, DONT_SET, inputPreset, + DONT_SET, + AAUDIO_DIRECTION_INPUT); + } +} + +static void checkAttributesAllowedCapturePolicy(aaudio_performance_mode_t perfMode) { + for (aaudio_allowed_capture_policy_t policy : sAllowCapturePolicies) { + checkAttributes(perfMode, + DONT_SET, + DONT_SET, + DONT_SET, + policy, AAUDIO_DIRECTION_INPUT); } } @@ -167,6 +197,10 @@ TEST(test_attributes, aaudio_input_preset_perfnone) { checkAttributesInputPreset(AAUDIO_PERFORMANCE_MODE_NONE); } +TEST(test_attributes, aaudio_allowed_capture_policy_perfnone) { + checkAttributesAllowedCapturePolicy(AAUDIO_PERFORMANCE_MODE_NONE); +} + TEST(test_attributes, aaudio_usage_lowlat) { checkAttributesUsage(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); } @@ -178,3 +212,7 @@ TEST(test_attributes, aaudio_content_type_lowlat) { TEST(test_attributes, aaudio_input_preset_lowlat) { checkAttributesInputPreset(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); } + +TEST(test_attributes, aaudio_allowed_capture_policy_lowlat) { + checkAttributesAllowedCapturePolicy(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); +} diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index 6c280834c7..fbf7d1049b 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -93,12 +93,14 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques const audio_source_t source = (direction == AAUDIO_DIRECTION_INPUT) ? AAudioConvert_inputPresetToAudioSource(getInputPreset()) : AUDIO_SOURCE_DEFAULT; + const audio_flags_mask_t flags = AUDIO_FLAG_LOW_LATENCY | + AAudioConvert_allowCapturePolicyToAudioFlagsMask(getAllowedCapturePolicy()); const audio_attributes_t attributes = { .content_type = contentType, .usage = usage, .source = source, - .flags = AUDIO_FLAG_LOW_LATENCY, + .flags = flags, .tags = "" }; -- GitLab From b99cc75f01c8e4d76295f3efcd768ebd9d8942c3 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 21 Mar 2019 20:52:24 -0700 Subject: [PATCH 1161/1530] Add application wide capture policy Apps were previously forced to set their allowed capture policy from either their manifest which is not flexible or from each track which is a very fine grain but difficult when using libraries like exoplayer. Thus add an application level policy set with AudioManager. Test: atest android.media.cts.AudioPlaybackCaptureTest Bug: 111453086 Change-Id: Ic890b5b041affea757fbd3f2707ff2ce18771828 Signed-off-by: Kevin Rocard --- media/libaudioclient/AudioSystem.cpp | 6 +++++ media/libaudioclient/IAudioPolicyService.cpp | 23 +++++++++++++++++-- .../include/media/AudioSystem.h | 2 ++ .../include/media/IAudioPolicyService.h | 1 + services/audiopolicy/AudioPolicyInterface.h | 1 + .../managerdefault/AudioPolicyManager.cpp | 14 +++++++++++ .../managerdefault/AudioPolicyManager.h | 3 +++ .../service/AudioPolicyInterfaceImpl.cpp | 14 +++++++++++ .../audiopolicy/service/AudioPolicyService.h | 1 + 9 files changed, 63 insertions(+), 2 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 0ce251344f..f3246695cb 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1133,6 +1133,12 @@ void AudioSystem::clearAudioConfigCache() } } +status_t AudioSystem::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) { + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == nullptr) return PERMISSION_DENIED; + return aps->setAllowedCapturePolicy(uid, flags); +} + bool AudioSystem::isOffloadSupported(const audio_offload_info_t& info) { ALOGV("isOffloadSupported()"); diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 3bac44f2eb..bf98c60299 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -101,7 +101,8 @@ enum { LIST_AUDIO_PRODUCT_STRATEGIES, GET_STRATEGY_FOR_ATTRIBUTES, LIST_AUDIO_VOLUME_GROUPS, - GET_VOLUME_GROUP_FOR_ATTRIBUTES + GET_VOLUME_GROUP_FOR_ATTRIBUTES, + SET_ALLOWED_CAPTURE_POLICY, }; #define MAX_ITEMS_PER_LIST 1024 @@ -603,6 +604,15 @@ public: return status; } + status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) override { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeInt32(flags); + remote()->transact(SET_ALLOWED_CAPTURE_POLICY, data, &reply); + return reply.readInt32(); + } + virtual bool isOffloadSupported(const audio_offload_info_t& info) { Parcel data, reply; @@ -2168,7 +2178,7 @@ status_t BnAudioPolicyService::onTransact( CHECK_INTERFACE(IAudioPolicyService, data, reply); bool isSupported = isHapticPlaybackSupported(); reply->writeBool(isSupported); - return NO_ERROR; + return NO_ERROR; } case SET_UID_DEVICE_AFFINITY: { @@ -2285,6 +2295,15 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case SET_ALLOWED_CAPTURE_POLICY: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + uid_t uid = data.readInt32(); + audio_flags_mask_t flags = data.readInt32(); + status_t status = setAllowedCapturePolicy(uid, flags); + reply->writeInt32(status); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index e64f285b09..05a1d562db 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -299,6 +299,8 @@ public: static status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory); + static status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags); + // Check if hw offload is possible for given format, stream type, sample rate, // bit rate, duration, video and streaming or offload property is enabled static bool isOffloadSupported(const audio_offload_info_t& info); diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 35540f096a..95530acd13 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -135,6 +135,7 @@ public: audio_unique_id_t* id) = 0; virtual status_t removeSourceDefaultEffect(audio_unique_id_t id) = 0; virtual status_t removeStreamDefaultEffect(audio_unique_id_t id) = 0; + virtual status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) = 0; // Check if offload is possible for given format, stream type, sample rate, // bit rate, duration, video and streaming or offload property is enabled virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0; diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index acbfc9ee72..a2cf7aa9bb 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -198,6 +198,7 @@ public: //dump state virtual status_t dump(int fd) = 0; + virtual status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t flags) = 0; virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0; virtual bool isDirectOutputSupported(const audio_config_base_t& config, const audio_attributes_t& attributes) = 0; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e8e9fa6ac5..ee0e1bd1f6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -945,6 +945,9 @@ status_t AudioPolicyManager::getOutputForAttrInt( if (status != NO_ERROR) { return status; } + if (auto it = mAllowedCapturePolicies.find(uid); it != end(mAllowedCapturePolicies)) { + resultAttr->flags |= it->second; + } *stream = mEngine->getStreamTypeForAttributes(*resultAttr); ALOGV("%s() attributes=%s stream=%s session %d selectedDeviceId %d", __func__, @@ -3022,6 +3025,11 @@ void AudioPolicyManager::dump(String8 *dst) const mPolicyMixes.dump(dst); mAudioSources.dump(dst); + dst->appendFormat(" AllowedCapturePolicies:\n"); + for (auto& policy : mAllowedCapturePolicies) { + dst->appendFormat(" - uid=%d flag_mask=%#x\n", policy.first, policy.second); + } + dst->appendFormat("\nPolicy Engine dump:\n"); mEngine->dump(dst); } @@ -3034,6 +3042,12 @@ status_t AudioPolicyManager::dump(int fd) return NO_ERROR; } +status_t AudioPolicyManager::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) +{ + mAllowedCapturePolicies[uid] = capturePolicy; + return NO_ERROR; +} + // This function checks for the parameters which can be offloaded. // This can be enhanced depending on the capability of the DSP and policy // of the system. diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 1c9868419e..41327bc9d0 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -218,6 +218,7 @@ public: status_t dump(int fd) override; + status_t setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) override; virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo); virtual bool isDirectOutputSupported(const audio_config_base_t& config, @@ -754,6 +755,8 @@ protected: // Surround formats that are enabled manually. Taken into account when // "encoded surround" is forced into "manual" mode. std::unordered_set mManualSurroundFormats; + + std::unordered_map mAllowedCapturePolicies; private: // Add or remove AC3 DTS encodings based on user preferences. void modifySurroundFormats(const sp& devDesc, FormatVector *formatsPtr); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 1ec326254e..0dbf652961 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -945,6 +945,20 @@ status_t AudioPolicyService::removeStreamDefaultEffect(audio_unique_id_t id) return audioPolicyEffects->removeStreamDefaultEffect(id); } +status_t AudioPolicyService::setAllowedCapturePolicy(uid_t uid, audio_flags_mask_t capturePolicy) { + Mutex::Autolock _l(mLock); + if (mAudioPolicyManager == NULL) { + ALOGV("%s() mAudioPolicyManager == NULL", __func__); + return NO_INIT; + } + uint_t callingUid = IPCThreadState::self()->getCallingUid(); + if (uid != callingUid) { + ALOGD("%s() uid invalid %d != %d", __func__, uid, callingUid); + return PERMISSION_DENIED; + } + return mAudioPolicyManager->setAllowedCapturePolicy(uid, capturePolicy); +} + bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) { if (mAudioPolicyManager == NULL) { diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index a2e75cde41..73cb793343 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -182,6 +182,7 @@ public: audio_io_handle_t output, int delayMs = 0); virtual status_t setVoiceVolume(float volume, int delayMs = 0); + status_t setAllowedCapturePolicy(uint_t uid, audio_flags_mask_t capturePolicy) override; virtual bool isOffloadSupported(const audio_offload_info_t &config); virtual bool isDirectOutputSupported(const audio_config_base_t& config, const audio_attributes_t& attributes); -- GitLab From b6cc72b87f3701f1da12ec6b9d1fece592ad5734 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 20 Mar 2019 13:26:49 -0700 Subject: [PATCH 1162/1530] Add aaudio APC opt-out to libaaudio.map.txt The opt-out was not published in the map.txt Test: atest CtsNativeMediaAAudioTestCases Change-Id: Ie4dec4abd9c569e2459dd02f9f372e30dd42267f Signed-off-by: Kevin Rocard --- media/libaaudio/libaaudio.map.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/libaaudio/libaaudio.map.txt b/media/libaaudio/libaaudio.map.txt index cbf59213b7..a87ede305a 100644 --- a/media/libaaudio/libaaudio.map.txt +++ b/media/libaaudio/libaaudio.map.txt @@ -20,6 +20,7 @@ LIBAAUDIO { AAudioStreamBuilder_setUsage; # introduced=28 AAudioStreamBuilder_setContentType; # introduced=28 AAudioStreamBuilder_setInputPreset; # introduced=28 + AAudioStreamBuilder_setAllowedCapturePolicy; # introduced=29 AAudioStreamBuilder_setSessionId; # introduced=28 AAudioStreamBuilder_openStream; AAudioStreamBuilder_delete; @@ -49,6 +50,7 @@ LIBAAUDIO { AAudioStream_getUsage; # introduced=28 AAudioStream_getContentType; # introduced=28 AAudioStream_getInputPreset; # introduced=28 + AAudioStream_getAllowedCapturePolicy; # introduced=29 AAudioStream_getFramesWritten; AAudioStream_getFramesRead; AAudioStream_getSessionId; # introduced=28 -- GitLab From 444bb5555e064271b2eafc1478f238b73f417941 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 14 Mar 2019 17:06:39 -0700 Subject: [PATCH 1163/1530] AudioFlinger: Clarify effect creation comments and warnings Test: CTS audio effect tests, instrumented test Bug: 128633662 Change-Id: I31e4cbec6c34d2419d2f7bbcced98e8bbb8ce78d --- services/audioflinger/AudioFlinger.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index b8f88cfe35..43260c26d8 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -3297,7 +3297,8 @@ sp AudioFlinger::createEffect( // output threads. // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX // because of code checking output when entering the function. - // Note: io is never 0 when creating an effect on an input + // Note: io is never AUDIO_IO_HANDLE_NONE when creating an effect on an input by APM. + // An AudioEffect created from the Java API will have io as AUDIO_IO_HANDLE_NONE. if (io == AUDIO_IO_HANDLE_NONE) { // look for the thread where the specified audio session is present io = findIoHandleBySessionId_l(sessionId, mPlaybackThreads); @@ -3307,6 +3308,25 @@ sp AudioFlinger::createEffect( if (io == AUDIO_IO_HANDLE_NONE) { io = findIoHandleBySessionId_l(sessionId, mMmapThreads); } + + // If you wish to create a Record preprocessing AudioEffect in Java, + // you MUST create an AudioRecord first and keep it alive so it is picked up above. + // Otherwise it will fail when created on a Playback thread by legacy + // handling below. Ditto with Mmap, the associated Mmap track must be created + // before creating the AudioEffect or the io handle must be specified. + // + // Detect if the effect is created after an AudioRecord is destroyed. + if (getOrphanEffectChain_l(sessionId).get() != nullptr) { + ALOGE("%s: effect %s with no specified io handle is denied because the AudioRecord" + " for session %d no longer exists", + __func__, desc.name, sessionId); + lStatus = PERMISSION_DENIED; + goto Exit; + } + + // Legacy handling of creating an effect on an expired or made-up + // session id. We think that it is a Playback effect. + // // If no output thread contains the requested session ID, default to // first output. The effect chain will be moved to the correct output // thread when a track with the same session ID is created -- GitLab From 0fb19c0e4a3aa5c42783f141a54469b7800633c6 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Wed, 6 Mar 2019 13:02:33 +0530 Subject: [PATCH 1164/1530] libeffects: Added code to update sourceFormat field properly for MC input Added code to update SourceFormat field properly for MC input in CS and EQ modules. Test: lvmtest Bug: 127583584 Change-Id: If5aa175e2557f64f04cb1f2de5a5822ecb553f2e --- .../lvm/lib/Bundle/src/LVM_Control.c | 18 ++++++++++++++++++ media/libeffects/lvm/lib/Eq/lib/LVEQNB.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c index 1d953420c4..1b27cb4bfe 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Control.c @@ -793,6 +793,15 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) { EQNB_Params.SourceFormat = LVEQNB_STEREO; } +#ifdef SUPPORT_MC + /* Note: Currently SourceFormat field of EQNB is not been + * used by the module. + */ + else if (LocalParams.SourceFormat == LVM_MULTICHANNEL) + { + EQNB_Params.SourceFormat = LVEQNB_MULTICHANNEL; + } +#endif else { EQNB_Params.SourceFormat = LVEQNB_MONOINSTEREO; /* Force to Mono-in-Stereo mode */ @@ -862,7 +871,16 @@ LVM_ReturnStatus_en LVM_ApplyNewSettings(LVM_Handle_t hInstance) CS_Params.SpeakerType = LVCS_HEADPHONES; } +#ifdef SUPPORT_MC + /* Concert sound module processes only the left and right channels + * data. So the Source Format is set to LVCS_STEREO for multichannel + * input also. + */ + if (LocalParams.SourceFormat == LVM_STEREO || + LocalParams.SourceFormat == LVM_MULTICHANNEL) +#else if (LocalParams.SourceFormat == LVM_STEREO) /* Mono format not supported */ +#endif { CS_Params.SourceFormat = LVCS_STEREO; } diff --git a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h index 385dbcf870..804f1bfa00 100644 --- a/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h +++ b/media/libeffects/lvm/lib/Eq/lib/LVEQNB.h @@ -184,6 +184,9 @@ typedef enum { LVEQNB_STEREO = 0, LVEQNB_MONOINSTEREO = 1, +#ifdef SUPPORT_MC + LVEQNB_MULTICHANNEL = 2, +#endif LVEQNB_SOURCE_MAX = LVM_MAXINT_32 } LVEQNB_SourceFormat_en; -- GitLab From 74300e0e5b195d235838c2996500e746a37f4914 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Sat, 23 Mar 2019 18:09:17 -0700 Subject: [PATCH 1165/1530] media.extractor pass client uid for attribution media.extractor runs as as a centralized service, have it use the client's uid for attribution of who requested the extraction. Bug: 129191926 Test: boot, dumpsys media.metrics --- media/libstagefright/RemoteMediaExtractor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp index b0ce688e74..29c3a35a08 100644 --- a/media/libstagefright/RemoteMediaExtractor.cpp +++ b/media/libstagefright/RemoteMediaExtractor.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "RemoteMediaExtractor" #include +#include #include #include #include @@ -51,6 +52,11 @@ RemoteMediaExtractor::RemoteMediaExtractor( if (MEDIA_LOG) { mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor); + // we're in the extractor service, we want to attribute to the app + // that invoked us. + int uid = IPCThreadState::self()->getCallingUid(); + mAnalyticsItem->setUid(uid); + // track the container format (mpeg, aac, wvm, etc) size_t ntracks = extractor->countTracks(); mAnalyticsItem->setCString(kExtractorFormat, extractor->name()); -- GitLab From 665f8cd61aaca2f0094c5d0a067bba3838ff1c9b Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Mon, 18 Mar 2019 11:03:58 -0700 Subject: [PATCH 1166/1530] Sanity checking when parsing 'trun' box. Check for unreasonable sampleCounts, unreasonable sampleDurations, especially when mapped as same duration for all samples. Bug: 124389881 Test: poc Change-Id: Ic1afecc42c1ec5c63b8dee1743ee0e473e82ba8b --- media/extractors/mp4/MPEG4Extractor.cpp | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 5ff1c59ea0..1f4b585ac6 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -5239,8 +5239,30 @@ status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) { sampleCtsOffset = 0; } - if (size < (off64_t)sampleCount * bytesPerSample) { - return -EINVAL; + if (bytesPerSample != 0) { + if (size < (off64_t)sampleCount * bytesPerSample) { + return -EINVAL; + } + } else { + if (sampleDuration == 0) { + ALOGW("b/123389881 sampleDuration == 0"); + android_errorWriteLog(0x534e4554, "124389881 zero"); + return -EINVAL; + } + + // apply some sanity (vs strict legality) checks + // + // clamp the count of entries in the trun box, to avoid spending forever parsing + // this box. Clamping (vs error) lets us play *something*. + // 1 million is about 400 msecs on a Pixel3, should be no more than a couple seconds + // on the slowest devices. + static constexpr uint32_t kMaxTrunSampleCount = 1000000; + if (sampleCount > kMaxTrunSampleCount) { + ALOGW("b/123389881 clamp sampleCount(%u) @ kMaxTrunSampleCount(%u)", + sampleCount, kMaxTrunSampleCount); + android_errorWriteLog(0x534e4554, "124389881 count"); + + } } Sample tmp; -- GitLab From ab1f5ee22cf477cf833900994e9fdc5b70ec969f Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Fri, 4 Jan 2019 17:33:24 +0530 Subject: [PATCH 1167/1530] codec2: VTS add TimeoutTest to test API's Timeout Test: VtsHidlC2V1_0TargetComponentTest -I software -C c2.android.aac.decoder Bug: 127694424 Change-Id: I244dee8c8b727aa1cab845b96c36bb428a4a96fc --- .../common/media_c2_hidl_test_common.cpp | 10 +- .../common/media_c2_hidl_test_common.h | 2 + .../VtsHidlC2V1_0TargetComponentTest.cpp | 113 ++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp index 1f36270312..d73b731c67 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp +++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp @@ -127,4 +127,12 @@ void workDone( queueCondition.notify_all(); } } -} \ No newline at end of file +} + +// Return current time in micro seconds +int64_t getNowUs() { + struct timeval tv; + gettimeofday(&tv, NULL); + + return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll; +} diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h index fca2902277..c577dac6d7 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h +++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h @@ -201,4 +201,6 @@ void workDone( std::list>& workQueue, bool& eos, bool& csd, uint32_t& framesReceived); +int64_t getNowUs(); + #endif // MEDIA_C2_HIDL_TEST_COMMON_H diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp index ec803d7015..74548b5ba3 100644 --- a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp @@ -26,6 +26,32 @@ #include #include "media_c2_hidl_test_common.h" +/* Time_Out for start(), stop(), reset(), release(), flush(), queue() are + * defined in hardware/interfaces/media/c2/1.0/IComponent.hal. Adding 50ms + * extra in case of timeout is 500ms, 1ms extra in case timeout is 1ms/5ms. All + * timeout is calculated in us. + */ +#define START_TIME_OUT 550000 +#define STOP_TIME_OUT 550000 +#define RESET_TIME_OUT 550000 +#define RELEASE_TIME_OUT 550000 +#define FLUSH_TIME_OUT 6000 +#define QUEUE_TIME_OUT 2000 + +// Time_Out for config(), query(), querySupportedParams() are defined in +// hardware/interfaces/media/c2/1.0/IConfigurable.hal. +#define CONFIG_TIME_OUT 6000 +#define QUERY_TIME_OUT 6000 +#define QUERYSUPPORTEDPARAMS_TIME_OUT 2000 + +#define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName) \ + if (timeConsumed > TIME_OUT) { \ + ALOGW( \ + "TIMED_OUT %s timeConsumed=%" PRId64 " us is " \ + "greater than threshold %d us", \ + FuncName, timeConsumed, TIME_OUT); \ + } + static ComponentTestEnvironment* gEnv = nullptr; namespace { @@ -244,6 +270,93 @@ INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Valu std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false), std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false))); +// Test API's Timeout +TEST_F(Codec2ComponentHidlTest, Timeout) { + ALOGV("Timeout Test"); + c2_status_t err = C2_OK; + + int64_t startTime = getNowUs(); + err = mComponent->start(); + int64_t timeConsumed = getNowUs() - startTime; + CHECK_TIMEOUT(timeConsumed, START_TIME_OUT, "start()"); + ALOGV("mComponent->start() timeConsumed=%" PRId64 " us", timeConsumed); + ASSERT_EQ(err, C2_OK); + + startTime = getNowUs(); + err = mComponent->reset(); + timeConsumed = getNowUs() - startTime; + CHECK_TIMEOUT(timeConsumed, RESET_TIME_OUT, "reset()"); + ALOGV("mComponent->reset() timeConsumed=%" PRId64 " us", timeConsumed); + ASSERT_EQ(err, C2_OK); + + err = mComponent->start(); + ASSERT_EQ(err, C2_OK); + + // Query supported params by the component + std::vector> params; + startTime = getNowUs(); + err = mComponent->querySupportedParams(¶ms); + timeConsumed = getNowUs() - startTime; + CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT, + "querySupportedParams()"); + ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us", + timeConsumed); + ASSERT_EQ(err, C2_OK); + + std::vector> queried; + std::vector> failures; + // Query and config all the supported params + for (std::shared_ptr p : params) { + startTime = getNowUs(); + err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried); + timeConsumed = getNowUs() - startTime; + CHECK_TIMEOUT(timeConsumed, QUERY_TIME_OUT, "query()"); + EXPECT_NE(queried.size(), 0u); + EXPECT_EQ(err, C2_OK); + ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us", + p->name().c_str(), timeConsumed); + + startTime = getNowUs(); + err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures); + timeConsumed = getNowUs() - startTime; + CHECK_TIMEOUT(timeConsumed, CONFIG_TIME_OUT, "config()"); + ASSERT_EQ(err, C2_OK); + ASSERT_EQ(failures.size(), 0u); + ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us", + p->name().c_str(), timeConsumed); + } + + std::list> workList; + startTime = getNowUs(); + err = mComponent->queue(&workList); + timeConsumed = getNowUs() - startTime; + ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed); + CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()"); + ASSERT_EQ(err, C2_OK); + + startTime = getNowUs(); + err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList); + timeConsumed = getNowUs() - startTime; + ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed); + CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()"); + ASSERT_EQ(err, C2_OK); + + startTime = getNowUs(); + err = mComponent->stop(); + timeConsumed = getNowUs() - startTime; + ALOGV("mComponent->stop() timeConsumed=%" PRId64 " us", timeConsumed); + CHECK_TIMEOUT(timeConsumed, STOP_TIME_OUT, "stop()"); + ASSERT_EQ(err, C2_OK); + + startTime = getNowUs(); + err = mComponent->release(); + timeConsumed = getNowUs() - startTime; + ALOGV("mComponent->release() timeConsumed=%" PRId64 " us", timeConsumed); + CHECK_TIMEOUT(timeConsumed, RELEASE_TIME_OUT, "release()"); + ASSERT_EQ(err, C2_OK); + +} + } // anonymous namespace // TODO: Add test for Invalid work, -- GitLab From c3d86f89d50ff6fa26d98c6f60615040294d1041 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 20 Mar 2019 10:24:55 -0700 Subject: [PATCH 1168/1530] Separate libstagefright_omx into two libs Extract the part in libstagefright_omx that's the base of all soft omx components into a separate lib, libstagefright_omx_soft, and make the omx plugin libs only depend on this lib and can be built more independently. This way the bufferqueue/gui reference in omx server side will be restricted to libstagefright_omx, which hosts only OMX master and node instances. Also remove unnecessary gui references in some soft plugins, they don't have a real dependency on gui. bug: 129272021 Change-Id: I81f789e44c4133579230bff0e0fd8633d349ecc4 --- media/libstagefright/codecs/aacdec/Android.bp | 6 +- media/libstagefright/codecs/aacenc/Android.bp | 8 +-- .../codecs/amrnb/dec/Android.bp | 6 +- .../codecs/amrnb/enc/Android.bp | 6 +- .../libstagefright/codecs/amrwbenc/Android.bp | 6 +- media/libstagefright/codecs/avcdec/Android.bp | 7 +- media/libstagefright/codecs/avcenc/Android.bp | 7 +- .../libstagefright/codecs/flac/dec/Android.bp | 6 +- .../libstagefright/codecs/flac/enc/Android.bp | 8 +-- .../libstagefright/codecs/g711/dec/Android.bp | 7 +- .../libstagefright/codecs/gsm/dec/Android.bp | 7 +- .../libstagefright/codecs/hevcdec/Android.bp | 7 +- .../codecs/m4v_h263/dec/Android.bp | 7 +- .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 1 - .../codecs/m4v_h263/enc/Android.bp | 7 +- media/libstagefright/codecs/mp3dec/Android.bp | 7 +- .../libstagefright/codecs/mpeg2dec/Android.bp | 7 +- .../libstagefright/codecs/on2/dec/Android.bp | 7 +- .../libstagefright/codecs/on2/enc/Android.bp | 8 +-- .../codecs/on2/enc/SoftVP8Encoder.h | 2 - .../codecs/on2/enc/SoftVP9Encoder.h | 2 - .../codecs/on2/enc/SoftVPXEncoder.h | 2 - .../libstagefright/codecs/opus/dec/Android.bp | 6 +- media/libstagefright/codecs/raw/Android.bp | 8 +-- .../codecs/vorbis/dec/Android.bp | 6 +- .../libstagefright/codecs/xaacdec/Android.bp | 6 +- media/libstagefright/omx/Android.bp | 64 +++++++++++++++++-- .../omx/SoftVideoEncoderOMXComponent.cpp | 2 +- .../media/stagefright/omx/SoftOMXComponent.h | 5 +- .../omx/SoftVideoDecoderOMXComponent.h | 5 +- .../omx/SoftVideoEncoderOMXComponent.h | 4 +- 31 files changed, 104 insertions(+), 133 deletions(-) diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp index 25628a20d3..e0bb5cd22e 100644 --- a/media/libstagefright/codecs/aacdec/Android.bp +++ b/media/libstagefright/codecs/aacdec/Android.bp @@ -29,12 +29,10 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", "libcutils", - "liblog", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp index ec1151b3bf..0d677fe21f 100644 --- a/media/libstagefright/codecs/aacenc/Android.bp +++ b/media/libstagefright/codecs/aacenc/Android.bp @@ -26,11 +26,7 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp index 880f1617c0..f3b272b989 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.bp +++ b/media/libstagefright/codecs/amrnb/dec/Android.bp @@ -101,11 +101,9 @@ cc_library_shared { "libstagefright_amrwbdec", ], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp index 19fd4a85c6..1c8b511b32 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.bp +++ b/media/libstagefright/codecs/amrnb/enc/Android.bp @@ -110,11 +110,9 @@ cc_library_shared { static_libs: ["libstagefright_amrnbenc"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index b9d45c1a63..8327500b66 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -167,11 +167,9 @@ cc_library_shared { static_libs: ["libstagefright_amrwbenc"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_enc_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp index 8a34845bf8..567bcca181 100644 --- a/media/libstagefright/codecs/avcdec/Android.bp +++ b/media/libstagefright/codecs/avcdec/Android.bp @@ -22,12 +22,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp index 63718283e6..0cd39e1b46 100644 --- a/media/libstagefright/codecs/avcenc/Android.bp +++ b/media/libstagefright/codecs/avcenc/Android.bp @@ -16,12 +16,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp index 3d4a44f0c4..18a3f6bd19 100644 --- a/media/libstagefright/codecs/flac/dec/Android.bp +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -28,12 +28,10 @@ cc_library_shared { cfi: true, }, + defaults: ["omx_soft_libs"], + shared_libs: [ - "liblog", "libstagefright_flacdec", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp index b32ab08a6c..4149ccde22 100644 --- a/media/libstagefright/codecs/flac/enc/Android.bp +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -19,13 +19,7 @@ cc_library_shared { ], cfi: true, }, - - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], header_libs: ["libbase_headers"], static_libs: [ diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp index 7097688ef3..c273179ac6 100644 --- a/media/libstagefright/codecs/g711/dec/Android.bp +++ b/media/libstagefright/codecs/g711/dec/Android.bp @@ -12,12 +12,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp index a973f708fe..3c5ebfe22e 100644 --- a/media/libstagefright/codecs/gsm/dec/Android.bp +++ b/media/libstagefright/codecs/gsm/dec/Android.bp @@ -25,12 +25,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], static_libs: ["libgsm"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp index 60fc446547..cc91d534b3 100644 --- a/media/libstagefright/codecs/hevcdec/Android.bp +++ b/media/libstagefright/codecs/hevcdec/Android.bp @@ -30,12 +30,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], // We need this because the current asm generates the following link error: // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp index 41141b13c8..0523143724 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp @@ -91,12 +91,7 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263dec"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index a8fcdd1e3a..9893c6f39c 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "mp4dec_api.h" diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp index d4f7d50493..d38f4b1359 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp @@ -77,12 +77,7 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263enc"], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp index 2154f843bc..9173ed6611 100644 --- a/media/libstagefright/codecs/mp3dec/Android.bp +++ b/media/libstagefright/codecs/mp3dec/Android.bp @@ -105,12 +105,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], static_libs: ["libstagefright_mp3dec"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp index c65554414b..26e786e7f6 100644 --- a/media/libstagefright/codecs/mpeg2dec/Android.bp +++ b/media/libstagefright/codecs/mpeg2dec/Android.bp @@ -20,12 +20,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp index 174f183eee..abd21d7e49 100644 --- a/media/libstagefright/codecs/on2/dec/Android.bp +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -14,12 +14,7 @@ cc_library_shared { static_libs: ["libvpx"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp index 891a771c34..ea46badfcb 100644 --- a/media/libstagefright/codecs/on2/enc/Android.bp +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -30,11 +30,7 @@ cc_library_shared { static_libs: ["libvpx"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h index b4904bfd37..c5c2abfa0e 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h index 85df69ac05..308a9ac95f 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 263d13450f..7208d694ac 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp index afe459da14..bfcae070d5 100644 --- a/media/libstagefright/codecs/opus/dec/Android.bp +++ b/media/libstagefright/codecs/opus/dec/Android.bp @@ -12,12 +12,10 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], + defaults: ["omx_soft_libs"], + shared_libs: [ "libopus", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp index f822445872..1c23badc38 100644 --- a/media/libstagefright/codecs/raw/Android.bp +++ b/media/libstagefright/codecs/raw/Android.bp @@ -24,11 +24,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp index a9265cbabc..2d1a92250d 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.bp +++ b/media/libstagefright/codecs/vorbis/dec/Android.bp @@ -12,12 +12,10 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], + defaults: ["omx_soft_libs"], + shared_libs: [ "libvorbisidec", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp index 7392f1e1d1..e49eb8fa65 100644 --- a/media/libstagefright/codecs/xaacdec/Android.bp +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -24,12 +24,10 @@ cc_library_shared { static_libs: ["libxaacdec"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", "libcutils", - "liblog", ], compile_multilib: "32", diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 4383004e8d..b959f6cc3e 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -11,11 +11,6 @@ cc_library_shared { "OMXNodeInstance.cpp", "OMXUtils.cpp", "OmxGraphicBufferSource.cpp", - "SimpleSoftOMXComponent.cpp", - "SoftOMXComponent.cpp", - "SoftOMXPlugin.cpp", - "SoftVideoDecoderOMXComponent.cpp", - "SoftVideoEncoderOMXComponent.cpp", "1.0/Omx.cpp", "1.0/OmxStore.cpp", "1.0/WGraphicBufferSource.cpp", @@ -56,6 +51,7 @@ cc_library_shared { "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", + "libstagefright_omx_soft", ], export_shared_lib_headers: [ @@ -81,6 +77,64 @@ cc_library_shared { }, } +cc_defaults { + name: "omx_soft_libs", + shared_libs: [ + "libutils", + "liblog", + "libstagefright_foundation", + "libstagefright_omx_soft", + ], +} + +cc_library_shared { + name: "libstagefright_omx_soft", + vendor_available: true, + vndk: { + enabled: true, + }, + + srcs: [ + "SimpleSoftOMXComponent.cpp", + "SoftOMXComponent.cpp", + "SoftOMXPlugin.cpp", + "SoftVideoDecoderOMXComponent.cpp", + "SoftVideoEncoderOMXComponent.cpp", + ], + + export_include_dirs: [ + "include", + ], + + shared_libs: [ + "libutils", + "liblog", + "libui", + "libstagefright_foundation", + ], + + export_shared_lib_headers: [ + "libstagefright_foundation", + "libutils", + "liblog", + ], + + cflags: [ + "-Werror", + "-Wall", + "-Wno-unused-parameter", + "-Wno-documentation", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} + cc_library_shared { name: "libstagefright_omx_utils", vendor_available: true, diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index 2fbbb4474d..d75acdabf8 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -559,7 +559,7 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( if (nativeMeta.nFenceFd >= 0) { sp fence = new Fence(nativeMeta.nFenceFd); nativeMeta.nFenceFd = -1; - status_t err = fence->wait(IOMX::kFenceTimeoutMs); + status_t err = fence->wait(kFenceTimeoutMs); if (err != OK) { ALOGE("Timed out waiting on input fence"); return NULL; diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h index 3ab6f88762..79f0c77c50 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h @@ -21,12 +21,15 @@ #include #include #include - +#include #include namespace android { struct SoftOMXComponent : public RefBase { + enum { + kFenceTimeoutMs = 1000 + }; SoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h index 3b381cea02..d7c1658a57 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h @@ -23,7 +23,10 @@ #include #include #include -#include +#include +#include +#include + #include #include diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h index 2d6f31b84f..9cb72dd6da 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h @@ -18,7 +18,9 @@ #define SOFT_VIDEO_ENCODER_OMX_COMPONENT_H_ -#include +#include +#include +#include #include "SimpleSoftOMXComponent.h" -- GitLab From f5d25e6f10c7bda31a533bb5903e4ca8e6c077b2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 25 Mar 2019 16:33:26 -0700 Subject: [PATCH 1169/1530] audiopolicy: fix AudioRecordingConfiguration callback Suppress first callback sent by startInput() as the input device is not yet set and it results in a spurious AudioRecordingConfiguration callback sent by RecordingActivityMonitor with a null audio device. Also fix a bug in getInputForAttr() where the wrong audio attributes were stored in the RecordClientDescriptor resulting in a failure to create the audio patch when DEFAULT audio source is used. Bug: 129287962 Test: cts-tradefed run cts-dev -m CtsMediaTestCases -t android.media.cts.AudioRecordingConfigurationTest Change-Id: Ia67abfa56ea653cf69e68bfabbb3d19a12adc310 --- .../common/managerdefinitions/src/AudioInputDescriptor.cpp | 7 +++++++ services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 635de6f1e4..5b4e2eb680 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -334,6 +334,13 @@ void AudioInputDescriptor::setClientActive(const sp& cli void AudioInputDescriptor::updateClientRecordingConfiguration( int event, const sp& client) { + // do not send callback if starting and no device is selected yet to avoid + // double callbacks from startInput() before and after the device is selected + if (event == RECORD_CONFIG_EVENT_START + && mPatchHandle == AUDIO_PATCH_HANDLE_NONE) { + return; + } + const audio_config_base_t sessionConfig = client->config(); const record_client_info_t recordClientInfo{client->uid(), client->session(), client->source(), client->portId(), diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e8e9fa6ac5..bcb6d77f29 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2025,7 +2025,7 @@ exit: mSoundTriggerSessions.indexOfKey(session) > 0; *portId = AudioPort::getNextUniqueId(); - clientDesc = new RecordClientDescriptor(*portId, uid, session, *attr, *config, + clientDesc = new RecordClientDescriptor(*portId, uid, session, attributes, *config, requestedDeviceId, attributes.source, flags, isSoundTrigger); inputDesc = mInputs.valueFor(*input); -- GitLab From e92cb6f8896d7e11c45f958768819109f0e5e8b4 Mon Sep 17 00:00:00 2001 From: Rajat Kumar Date: Tue, 26 Feb 2019 12:12:58 +0530 Subject: [PATCH 1170/1530] Fix for integer over flow in LVPSA_process in libeffects Test: ubsan and lvmtest Bug: 123388592 Change-Id: I6f60b555d316accceacba55503a5027d853efb84 --- .../libeffects/lvm/lib/Common/lib/LVM_Types.h | 1 + .../lib/SpectrumAnalyzer/src/LVPSA_Process.c | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h index 59586e012f..fbfdd4df5c 100644 --- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h +++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h @@ -94,6 +94,7 @@ typedef uint16_t LVM_UINT16; /* Unsigned 16-bit word */ typedef int32_t LVM_INT32; /* Signed 32-bit word */ typedef uint32_t LVM_UINT32; /* Unsigned 32-bit word */ +typedef int64_t LVM_INT64; /* Signed 64-bit word */ #ifdef BUILD_FLOAT diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c index ea5f74a8d1..61899fe0c7 100644 --- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c +++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Process.c @@ -22,6 +22,17 @@ #define LVM_MININT_32 0x80000000 +static LVM_INT32 mult32x32in32_shiftr(LVM_INT32 a, LVM_INT32 b, LVM_INT32 c) { + LVM_INT64 result = ((LVM_INT64)a * b) >> c; + + if (result >= INT32_MAX) { + return INT32_MAX; + } else if (result <= INT32_MIN) { + return INT32_MIN; + } else { + return (LVM_INT32)result; + } +} /************************************************************************************/ /* */ @@ -123,10 +134,10 @@ LVPSA_RETURN LVPSA_Process ( pLVPSA_Handle_t hInstance, if(pLVPSA_Inst->pSpectralDataBufferWritePointer != pWrite_Save) { - MUL32x32INTO32((AudioTime + (LVM_INT32)((LVM_INT32)pLVPSA_Inst->LocalSamplesCount*1000)), - (LVM_INT32)LVPSA_SampleRateInvTab[pLVPSA_Inst->CurrentParams.Fs], - AudioTimeInc, - LVPSA_FsInvertShift) + AudioTimeInc = mult32x32in32_shiftr( + (AudioTime + ((LVM_INT32)pLVPSA_Inst->LocalSamplesCount * 1000)), + (LVM_INT32)LVPSA_SampleRateInvTab[pLVPSA_Inst->CurrentParams.Fs], + LVPSA_FsInvertShift); pLVPSA_Inst->SpectralDataBufferAudioTime = AudioTime + AudioTimeInc; } -- GitLab From bdc0cc415e649197addee1f81d763017fe57a1f5 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Fri, 22 Mar 2019 07:44:58 -0700 Subject: [PATCH 1171/1530] Use safe_union in FieldSupportedValues Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 129084824 Change-Id: I1e6ae9c4ae19feac532b5ec8ee6bb829cb5616a0 --- media/codec2/hidl/1.0/utils/Android.bp | 1 + .../1.0/utils/include/codec2/hidl/1.0/types.h | 1 + media/codec2/hidl/1.0/utils/types.cpp | 106 +++++++++--------- 3 files changed, 53 insertions(+), 55 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index 0bb24186fb..97dde71626 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -33,6 +33,7 @@ cc_library { "android.hardware.media.bufferpool@2.0", "android.hardware.media.c2@1.0", "android.hardware.media.omx@1.0", + "android.hidl.safe_union@1.0", "libbase", "libcodec2", "libcodec2_vndk", diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index b9f3aa88ea..817d14881b 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 031e6371af..74320e75cb 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -120,9 +120,9 @@ bool objcpy(C2WorkOrdinalStruct *d, const WorkOrdinal &s) { return true; } -// C2FieldSupportedValues::range's type -> FieldSupportedValues::Range +// C2FieldSupportedValues::range's type -> ValueRange bool objcpy( - FieldSupportedValues::Range* d, + ValueRange* d, const decltype(C2FieldSupportedValues::range)& s) { d->min = static_cast(s.min.u64); d->max = static_cast(s.max.u64); @@ -135,45 +135,45 @@ bool objcpy( // C2FieldSupportedValues -> FieldSupportedValues bool objcpy(FieldSupportedValues *d, const C2FieldSupportedValues &s) { switch (s.type) { - case C2FieldSupportedValues::EMPTY: - d->type = FieldSupportedValues::Type::EMPTY; - d->values.resize(0); - break; - case C2FieldSupportedValues::RANGE: - d->type = FieldSupportedValues::Type::RANGE; - if (!objcpy(&d->range, s.range)) { - LOG(ERROR) << "Invalid C2FieldSupportedValues::range."; - return false; - } - d->values.resize(0); - break; - default: - switch (s.type) { - case C2FieldSupportedValues::VALUES: - d->type = FieldSupportedValues::Type::VALUES; + case C2FieldSupportedValues::EMPTY: { + d->empty(::android::hidl::safe_union::V1_0::Monostate{}); break; - case C2FieldSupportedValues::FLAGS: - d->type = FieldSupportedValues::Type::FLAGS; - break; - default: - LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t " - << "with underlying value " << underlying_value(s.type) - << "."; - d->type = static_cast(s.type); - if (!objcpy(&d->range, s.range)) { + } + case C2FieldSupportedValues::RANGE: { + ValueRange range{}; + if (!objcpy(&range, s.range)) { LOG(ERROR) << "Invalid C2FieldSupportedValues::range."; + d->range(range); return false; } + d->range(range); + break; } - copyVector(&d->values, s.values); + case C2FieldSupportedValues::VALUES: { + hidl_vec values; + copyVector(&values, s.values); + d->values(values); + break; + } + case C2FieldSupportedValues::FLAGS: { + hidl_vec flags; + copyVector(&flags, s.values); + d->flags(flags); + break; + } + default: + LOG(DEBUG) << "Unrecognized C2FieldSupportedValues::type_t " + << "with underlying value " << underlying_value(s.type) + << "."; + return false; } return true; } -// FieldSupportedValues::Range -> C2FieldSupportedValues::range's type +// ValueRange -> C2FieldSupportedValues::range's type bool objcpy( decltype(C2FieldSupportedValues::range)* d, - const FieldSupportedValues::Range& s) { + const ValueRange& s) { d->min.u64 = static_cast(s.min); d->max.u64 = static_cast(s.max); d->step.u64 = static_cast(s.step); @@ -184,37 +184,33 @@ bool objcpy( // FieldSupportedValues -> C2FieldSupportedValues bool objcpy(C2FieldSupportedValues *d, const FieldSupportedValues &s) { - switch (s.type) { - case FieldSupportedValues::Type::EMPTY: - d->type = C2FieldSupportedValues::EMPTY; - break; - case FieldSupportedValues::Type::RANGE: - d->type = C2FieldSupportedValues::RANGE; - if (!objcpy(&d->range, s.range)) { - LOG(ERROR) << "Invalid FieldSupportedValues::range."; - return false; + switch (s.getDiscriminator()) { + case FieldSupportedValues::hidl_discriminator::empty: { + d->type = C2FieldSupportedValues::EMPTY; + break; } - d->values.resize(0); - break; - default: - switch (s.type) { - case FieldSupportedValues::Type::VALUES: + case FieldSupportedValues::hidl_discriminator::range: { + d->type = C2FieldSupportedValues::RANGE; + if (!objcpy(&d->range, s.range())) { + LOG(ERROR) << "Invalid FieldSupportedValues::range."; + return false; + } + d->values.resize(0); + break; + } + case FieldSupportedValues::hidl_discriminator::values: { d->type = C2FieldSupportedValues::VALUES; + copyVector(&d->values, s.values()); break; - case FieldSupportedValues::Type::FLAGS: + } + case FieldSupportedValues::hidl_discriminator::flags: { d->type = C2FieldSupportedValues::FLAGS; + copyVector(&d->values, s.flags()); break; - default: - LOG(DEBUG) << "Unrecognized FieldSupportedValues::Type " - << "with underlying value " << underlying_value(s.type) - << "."; - d->type = static_cast(s.type); - if (!objcpy(&d->range, s.range)) { - LOG(ERROR) << "Invalid FieldSupportedValues::range."; - return false; - } } - copyVector(&d->values, s.values); + default: + LOG(WARNING) << "Unrecognized FieldSupportedValues::getDiscriminator()"; + return false; } return true; } -- GitLab From 83e5dd90020f3c95ef1d6f5fc3e7f84c15b221ed Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 20 Mar 2019 12:06:49 -0700 Subject: [PATCH 1172/1530] Camera: Relax logical camera stream combination requirement Logical camera now doesn't mandate concurrent physical streams combination. Instead, require is_stream_combination_required to query whether such a combination is supported by HAL. Test: Camera CTS, ITS Bug: 119325664 Change-Id: I8ea7abc6fe1e34e687e86dcd1e7529d030bcc71a --- .../include/camera/NdkCameraMetadataTags.h | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index acf699925c..99b613e1f3 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -5695,6 +5695,8 @@ typedef enum acamera_metadata_tag { *

      In both cases, all images generated for a particular capture request still carry the same * timestamps, so that they can be used to look up the matching frame number and * onCaptureStarted callback.

      + *

      This tag is only applicable if the logical camera device supports concurrent physical + * streams from different physical cameras.

      */ ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE = // byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t) ACAMERA_LOGICAL_MULTI_CAMERA_START + 1, @@ -7581,14 +7583,23 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10, /** - *

      The camera device is a logical camera backed by two or more physical cameras. In - * API level 28, the physical cameras must also be exposed to the application via - * CameraManager#getCameraIdList. Starting from API - * level 29, some or all physical cameras may not be independently exposed to the - * application, in which case the physical camera IDs will not be available in - * CameraManager#getCameraIdList. But the application - * can still query the physical cameras' characteristics by calling - * CameraManager#getCameraCharacteristics.

      + *

      The camera device is a logical camera backed by two or more physical cameras.

      + *

      In API level 28, the physical cameras must also be exposed to the application via + * CameraManager#getCameraIdList.

      + *

      Starting from API level 29, some or all physical cameras may not be independently + * exposed to the application, in which case the physical camera IDs will not be + * available in CameraManager#getCameraIdList. But the + * application can still query the physical cameras' characteristics by calling + * CameraManager#getCameraCharacteristics. Additionally, + * if a physical camera is hidden from camera ID list, the mandatory stream combinations + * for that physical camera must be supported through the logical camera using physical + * streams.

      + *

      Combinations of logical and physical streams, or physical streams from different + * physical cameras are not guaranteed. However, if the camera device supports + * {@link ACameraDevice_isSessionConfigurationSupported }, + * application must be able to query whether a stream combination involving physical + * streams is supported by calling + * {@link ACameraDevice_isSessionConfigurationSupported }.

      *

      Camera application shouldn't assume that there are at most 1 rear camera and 1 front * camera in the system. For an application that switches between front and back cameras, * the recommendation is to switch between the first rear camera and the first front @@ -7613,24 +7624,6 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * the same. *

    • The logical camera must be LIMITED or higher device.
    • *
    - *

    Both the logical camera device and its underlying physical devices support the - * mandatory stream combinations required for their device levels.

    - *

    Additionally, for each guaranteed stream combination, the logical camera supports:

    - *
      - *
    • For each guaranteed stream combination, the logical camera supports replacing one - * logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888} - * or raw stream with two physical streams of the same size and format, each from a - * separate physical camera, given that the size and format are supported by both - * physical cameras.
    • - *
    • If the logical camera doesn't advertise RAW capability, but the underlying physical - * cameras do, the logical camera will support guaranteed stream combinations for RAW - * capability, except that the RAW streams will be physical streams, each from a separate - * physical camera. This is usually the case when the physical cameras have different - * sensor sizes.
    • - *
    - *

    Using physical streams in place of a logical stream of the same size and format will - * not slow down the frame rate of the capture, as long as the minimum frame duration - * of the physical and logical streams are the same.

    *

    A logical camera device's dynamic metadata may contain * ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID to notify the application of the current * active physical camera Id. An active physical camera is the physical camera from which -- GitLab From 6d91ba5028dd1ab7870ac3c71fa870c50adadb08 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 26 Mar 2019 14:35:28 -0700 Subject: [PATCH 1173/1530] audioflinger: Fix unsigned integer overflow in RecordThread Bug: 129159931 Test: atest CtsMediaTestCases:AudioRecordTest on cuttlefish Change-Id: I87b72d6d98806327278b80d0a4765f7bed9ce2a7 --- services/audioflinger/Threads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index e94fb49cba..984d9fee95 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7245,7 +7245,7 @@ reacquire_wakelock: } else { // FIXME could do a partial drop of framesOut if (activeTrack->mFramesToDrop > 0) { - activeTrack->mFramesToDrop -= framesOut; + activeTrack->mFramesToDrop -= (ssize_t)framesOut; if (activeTrack->mFramesToDrop <= 0) { activeTrack->clearSyncStartEvent(); } -- GitLab From 57d7cc9e6022151513abff78f4ad744855b9c06b Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 5 Feb 2019 08:34:55 -0800 Subject: [PATCH 1174/1530] C2SoftHEVCEnc: Add support for constant quality encoding Quality (0 to 100) is used to map to Qp Complexity (0 to 10) is used to map to presets Test: Manual using an AMediaCodec based application Bug: 125010575 Change-Id: Ia664c15bc1521b9f6183cb873705134ddb0692d8 --- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 86 ++++++++++++++++++- media/codec2/components/hevc/C2SoftHevcEnc.h | 3 + .../data/media_codecs_google_c2_video.xml | 3 + 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 7045b6a494..402d9aae25 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -93,6 +93,20 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { .build()); // matches limits in codec library + addParameter( + DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE) + .withDefault(new C2StreamBitrateModeTuning::output( + 0u, C2Config::BITRATE_VARIABLE)) + .withFields({ + C2F(mBitrateMode, value).oneOf({ + C2Config::BITRATE_CONST, + C2Config::BITRATE_VARIABLE, + C2Config::BITRATE_IGNORE}) + }) + .withSetter( + Setter::StrictValueWithNoDeps) + .build()); + addParameter( DefineParam(mBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::output(0u, 64000)) @@ -101,6 +115,20 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { .build()); // matches levels allowed within codec library + addParameter( + DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY) + .withDefault(new C2StreamComplexityTuning::output(0u, 0)) + .withFields({C2F(mComplexity, value).inRange(0, 10)}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); + + addParameter( + DefineParam(mQuality, C2_PARAMKEY_QUALITY) + .withDefault(new C2StreamQualityTuning::output(0u, 80)) + .withFields({C2F(mQuality, value).inRange(0, 100)}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); + addParameter( DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) .withDefault(new C2StreamProfileLevelInfo::output( @@ -287,12 +315,21 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { std::shared_ptr getFrameRate_l() const { return mFrameRate; } + std::shared_ptr getBitrateMode_l() const { + return mBitrateMode; + } std::shared_ptr getBitrate_l() const { return mBitrate; } std::shared_ptr getRequestSync_l() const { return mRequestSync; } + std::shared_ptr getComplexity_l() const { + return mComplexity; + } + std::shared_ptr getQuality_l() const { + return mQuality; + } private: std::shared_ptr mInputFormat; @@ -304,6 +341,9 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { std::shared_ptr mFrameRate; std::shared_ptr mRequestSync; std::shared_ptr mBitrate; + std::shared_ptr mBitrateMode; + std::shared_ptr mComplexity; + std::shared_ptr mQuality; std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; }; @@ -387,6 +427,19 @@ static void fillEmptyWork(const std::unique_ptr& work) { work->workletsProcessed = 1u; } +static int getQpFromQuality(int quality) { + int qp; +#define MIN_QP 4 +#define MAX_QP 50 + /* Quality: 100 -> Qp : MIN_QP + * Quality: 0 -> Qp : MAX_QP + * Qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP; + */ + qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP; + qp = std::min(qp, MAX_QP); + qp = std::max(qp, MIN_QP); + return qp; +} c2_status_t C2SoftHevcEnc::initEncParams() { mCodecCtx = nullptr; mNumCores = std::min(GetCPUCoreCount(), (size_t) CODEC_MAX_CORES); @@ -416,9 +469,37 @@ c2_status_t C2SoftHevcEnc::initEncParams() { mIvVideoColorFormat = IV_YUV_420P; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; - mEncParams.s_config_prms.i4_rate_control_mode = 2; mEncParams.s_lap_prms.i4_rc_look_ahead_pics = 0; + switch (mBitrateMode->value) { + case C2Config::BITRATE_IGNORE: + mEncParams.s_config_prms.i4_rate_control_mode = 3; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_frame_qp[0] = + getQpFromQuality(mQuality->value); + break; + case C2Config::BITRATE_CONST: + mEncParams.s_config_prms.i4_rate_control_mode = 5; + break; + case C2Config::BITRATE_VARIABLE: + [[fallthrough]]; + default: + mEncParams.s_config_prms.i4_rate_control_mode = 2; + break; + break; + } + + if (mComplexity->value == 10) { + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P0; + } else if (mComplexity->value >= 8) { + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P2; + } else if (mComplexity->value >= 7) { + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P3; + } else if (mComplexity->value >= 5) { + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P4; + } else { + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5; + } + return C2_OK; } @@ -447,11 +528,14 @@ c2_status_t C2SoftHevcEnc::initEncoder() { { IntfImpl::Lock lock = mIntf->lock(); mSize = mIntf->getSize_l(); + mBitrateMode = mIntf->getBitrateMode_l(); mBitrate = mIntf->getBitrate_l(); mFrameRate = mIntf->getFrameRate_l(); mHevcEncProfile = mIntf->getProfile_l(); mHevcEncLevel = mIntf->getLevel_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); + mComplexity = mIntf->getComplexity_l(); + mQuality = mIntf->getQuality_l(); } c2_status_t status = initEncParams(); diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h index 9d90b95c63..8569a3ea3a 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.h +++ b/media/codec2/components/hevc/C2SoftHevcEnc.h @@ -77,6 +77,9 @@ struct C2SoftHevcEnc : public SimpleC2Component { std::shared_ptr mSize; std::shared_ptr mFrameRate; std::shared_ptr mBitrate; + std::shared_ptr mBitrateMode; + std::shared_ptr mComplexity; + std::shared_ptr mQuality; #ifdef FILE_DUMP_ENABLE char mInFile[200]; diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml index e20174fa41..f785bfae6b 100644 --- a/media/libstagefright/data/media_codecs_google_c2_video.xml +++ b/media/libstagefright/data/media_codecs_google_c2_video.xml @@ -115,6 +115,9 @@ + + + -- GitLab From 817d318247aeba55d3243a99e755c33ed71f4288 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Mon, 11 Mar 2019 16:37:47 -0700 Subject: [PATCH 1175/1530] CCodec: Allow bitrate to be not set in CQ mode for video encoders In constant quality mode, bitrate field is optional for video encoders. Update the check to look for quality for CQ Mode and bitrate for other modes. Bug: 125010575 Test: Manual with HEIF encoding application Change-Id: Icb294e1becaf47c8fdabb6839d1aa57178e17737 --- media/codec2/sfplugin/CCodec.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 5f60378f77..9c679aa4a9 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -742,10 +742,21 @@ void CCodec::configure(const sp &msg) { return BAD_VALUE; } if ((config->mDomain & Config::IS_ENCODER) && (config->mDomain & Config::IS_VIDEO)) { - if (!msg->findInt32(KEY_BIT_RATE, &i32) - && !msg->findFloat(KEY_BIT_RATE, &flt)) { - ALOGD("bitrate is missing, which is required for video encoders."); - return BAD_VALUE; + C2Config::bitrate_mode_t mode = C2Config::BITRATE_VARIABLE; + if (msg->findInt32(KEY_BITRATE_MODE, &i32)) { + mode = (C2Config::bitrate_mode_t) i32; + } + if (mode == BITRATE_MODE_CQ) { + if (!msg->findInt32(KEY_QUALITY, &i32)) { + ALOGD("quality is missing, which is required for video encoders in CQ."); + return BAD_VALUE; + } + } else { + if (!msg->findInt32(KEY_BIT_RATE, &i32) + && !msg->findFloat(KEY_BIT_RATE, &flt)) { + ALOGD("bitrate is missing, which is required for video encoders."); + return BAD_VALUE; + } } if (!msg->findInt32(KEY_I_FRAME_INTERVAL, &i32) && !msg->findFloat(KEY_I_FRAME_INTERVAL, &flt)) { -- GitLab From 5cd1f1d962446f48eecda9c5c8b004c79b6cb716 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Tue, 26 Mar 2019 15:21:11 -0700 Subject: [PATCH 1176/1530] startAudioSource should be created using the calling uid Bug: 128850122 Test: dumpsys media.audio_policy Change-Id: I374b528164bbe3168a5a0c730b34dc07b5694b40 --- services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index ea6ca39a55..93e3c444d0 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -1127,9 +1127,10 @@ status_t AudioPolicyService::startAudioSource(const struct audio_port_config *so if (mAudioPolicyManager == NULL) { return NO_INIT; } + // startAudioSource should be created as the calling uid + const uid_t callingUid = IPCThreadState::self()->getCallingUid(); AutoCallerClear acc; - return mAudioPolicyManager->startAudioSource(source, attributes, portId, - IPCThreadState::self()->getCallingUid()); + return mAudioPolicyManager->startAudioSource(source, attributes, portId, callingUid); } status_t AudioPolicyService::stopAudioSource(audio_port_handle_t portId) -- GitLab From 3b5874665de9ca8ad3ff2197017da62648223330 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Tue, 26 Mar 2019 16:41:11 -0700 Subject: [PATCH 1177/1530] Update raw audio codec profile Bug: 119201624 Test: TBD Change-Id: I2f9558b476a90d4aa618a24711dd69b61163231e --- media/libstagefright/data/media_codecs_google_audio.xml | 2 +- media/libstagefright/data/media_codecs_google_c2_audio.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml index 632088ae7e..8899adc96a 100644 --- a/media/libstagefright/data/media_codecs_google_audio.xml +++ b/media/libstagefright/data/media_codecs_google_audio.xml @@ -58,7 +58,7 @@ - + diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml index 47a9715184..be2404d179 100644 --- a/media/libstagefright/data/media_codecs_google_c2_audio.xml +++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml @@ -67,7 +67,7 @@ - + -- GitLab From 4f3314d28ef1113b526b8ef198a6616d02b45353 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 26 Mar 2019 17:00:34 -0700 Subject: [PATCH 1178/1530] CCodec: fix C2OMXNode assumption that components are in the same process Bug: 128213533 Test: atest CtsMediaTestCases:HeifWriterTest Change-Id: Ieab560be49018dd500fe2d530aebb0649e62a8b5 --- media/codec2/sfplugin/C2OMXNode.cpp | 24 +++++++++++++++------ media/codec2/sfplugin/C2OMXNode.h | 16 ++++++++++++++ media/codec2/sfplugin/CCodec.cpp | 14 ++++++++++++ media/codec2/sfplugin/InputSurfaceWrapper.h | 7 ++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 03d859aaee..962df0f5c0 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -272,19 +272,14 @@ status_t C2OMXNode::emptyBuffer( work->input.buffers.clear(); if (block) { std::shared_ptr c2Buffer( - // TODO: fence new Buffer2D(block->share( - C2Rect(block->width(), block->height()), ::C2Fence())), - [buffer, source = getSource()](C2Buffer *ptr) { - delete ptr; - // TODO: fence - (void)source->onInputBufferEmptied(buffer, -1); - }); + C2Rect(block->width(), block->height()), ::C2Fence()))); work->input.buffers.push_back(c2Buffer); } work->worklets.clear(); work->worklets.emplace_back(new C2Worklet); std::list> items; + uint64_t index = work->input.ordinal.frameIndex.peeku(); items.push_back(std::move(work)); c2_status_t err = comp->queue(&items); @@ -292,6 +287,7 @@ status_t C2OMXNode::emptyBuffer( return UNKNOWN_ERROR; } + (void)mBufferIdsInUse.emplace(index, buffer); return OK; } @@ -326,4 +322,18 @@ void C2OMXNode::setFrameSize(uint32_t width, uint32_t height) { mHeight = height; } +void C2OMXNode::onInputBufferDone(c2_cntr64_t index) { + if (!mBufferSource) { + ALOGD("Buffer source not set (index=%llu)", index.peekull()); + return; + } + auto it = mBufferIdsInUse.find(index.peeku()); + if (it == mBufferIdsInUse.end()) { + ALOGV("Untracked input index %llu (maybe already removed)", index.peekull()); + return; + } + (void)mBufferSource->onInputBufferEmptied(it->second, -1); + (void)mBufferIdsInUse.erase(it); +} + } // namespace android diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h index b5a815ec0f..b7bd696f75 100644 --- a/media/codec2/sfplugin/C2OMXNode.h +++ b/media/codec2/sfplugin/C2OMXNode.h @@ -75,9 +75,23 @@ struct C2OMXNode : public BnOMXNode { OMX_INDEXTYPE *index) override; status_t dispatchMessage(const omx_message &msg) override; + /** + * Returns underlying IOMXBufferSource object. + */ sp getSource(); + + /** + * Configure the frame size. + */ void setFrameSize(uint32_t width, uint32_t height); + /** + * Clean up work item reference. + * + * \param index input work index + */ + void onInputBufferDone(c2_cntr64_t index); + private: std::weak_ptr mComp; sp mBufferSource; @@ -96,6 +110,8 @@ private: bool mFirstInputFrame; // true for first input c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame + + std::map mBufferIdsInUse; }; } // namespace android diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 9c679aa4a9..8474ce831a 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -370,6 +370,10 @@ public: return err; } + void onInputBufferDone(c2_cntr64_t index) override { + mNode->onInputBufferDone(index); + } + private: sp mSource; sp mNode; @@ -1583,6 +1587,13 @@ void CCodec::onWorkDone(std::list> &workItems) { void CCodec::onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) { mChannel->onInputBufferDone(frameIndex, arrayIndex); + if (arrayIndex == 0) { + // We always put no more than one buffer per work, if we use an input surface. + Mutexed::Locked config(mConfig); + if (config->mInputSurface) { + config->mInputSurface->onInputBufferDone(frameIndex); + } + } } void CCodec::onMessageReceived(const sp &msg) { @@ -1715,6 +1726,9 @@ void CCodec::onMessageReceived(const sp &msg) { ++stream; } } + if (config->mInputSurface) { + config->mInputSurface->onInputBufferDone(work->input.ordinal.frameIndex); + } mChannel->onWorkDone( std::move(work), changed ? config->mOutputFormat : nullptr, initData.hasChanged() ? initData.update().get() : nullptr); diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h index d9c4eece0c..8341fd577b 100644 --- a/media/codec2/sfplugin/InputSurfaceWrapper.h +++ b/media/codec2/sfplugin/InputSurfaceWrapper.h @@ -98,6 +98,13 @@ public: mDataSpace = dataSpace; } + /** + * Clean up C2Work related references if necessary. No-op by default. + * + * \param index index of input work. + */ + virtual void onInputBufferDone(c2_cntr64_t /* index */) {} + protected: android_dataspace mDataSpace; }; -- GitLab From ba329b1662b293b34f9a8054136cd49c0d3fba80 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 26 Mar 2019 17:04:53 -0700 Subject: [PATCH 1179/1530] stagefright: Fix overflow in AudioSource::dataCallback Bug: 129158294 Test: atest CtsMediaTestCases:MediaRecorderTest on cuttlefish Change-Id: I01204576fc0c5091317d723bc5d5f519b058cafc --- media/libstagefright/AudioSource.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 5194e03224..a71390065c 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -385,11 +385,11 @@ status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) { } mLastFrameTimestampUs = timeUs; - size_t numLostBytes = 0; + uint64_t numLostBytes = 0; // AudioRecord::getInputFramesLost() returns uint32_t if (mNumFramesReceived > 0) { // Ignore earlier frame lost // getInputFramesLost() returns the number of lost frames. // Convert number of frames lost to number of bytes lost. - numLostBytes = mRecord->getInputFramesLost() * mRecord->frameSize(); + numLostBytes = (uint64_t)mRecord->getInputFramesLost() * mRecord->frameSize(); } CHECK_EQ(numLostBytes & 1, 0u); @@ -397,11 +397,11 @@ status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) { if (numLostBytes > 0) { // Loss of audio frames should happen rarely; thus the LOGW should // not cause a logging spam - ALOGW("Lost audio record data: %zu bytes", numLostBytes); + ALOGW("Lost audio record data: %" PRIu64 " bytes", numLostBytes); } while (numLostBytes > 0) { - size_t bufferSize = numLostBytes; + uint64_t bufferSize = numLostBytes; if (numLostBytes > kMaxBufferSize) { numLostBytes -= kMaxBufferSize; bufferSize = kMaxBufferSize; -- GitLab From 472c738c3410e42534d9fbd3bd5cc8f0236289b8 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Tue, 26 Mar 2019 18:13:58 -0700 Subject: [PATCH 1180/1530] Remove buffers from mFlushedConfigs when queued Bug: 129368298 Change-Id: Iaf631449bd5d2f6fc6ab523a5fbd5169d9ec9835 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index a6fa33362a..55a525e4c0 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -2483,6 +2483,7 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { bool post = true; if (!configs->empty()) { sp config = configs->front(); + configs->pop_front(); if (buffer->capacity() >= config->size()) { memcpy(buffer->base(), config->data(), config->size()); buffer->setRange(0, config->size()); -- GitLab From f92acc7039cdb8cb50d5e9d4ae9dfab314f89d47 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 7 Mar 2019 17:43:25 +0900 Subject: [PATCH 1181/1530] libmediandk: use libandroid_runtime_lazy libmediandk and libbinder_ndk have a duplicate code that loads libandroid_runtime lazily. That part has been extracted into its own library: libandroid_runtime_lazy. Bug: 125550121 Test: m -j Test: readelf -d $OUT/system/lib64/libmediandk.so | grep NEED Test: # see if there is not libandroid_runtime) Change-Id: If7cde2f9d3076cec4e69fd8f6e7b19c99804f42e --- media/ndk/Android.bp | 7 +--- media/ndk/NdkMediaDataSource.cpp | 70 ++------------------------------ 2 files changed, 5 insertions(+), 72 deletions(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index f4cc704dce..a4f5730b6e 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -73,6 +73,7 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", "android.hidl.token@1.0-utils", + "libandroid_runtime_lazy", "libbinder", "libmedia", "libmedia_omx", @@ -93,12 +94,6 @@ cc_library_shared { "libmediandk_utils", ], - required: [ - // libmediandk may be used by Java and non-Java things. When lower-level things use it, - // they shouldn't have to take on the cost of loading libandroid_runtime. - "libandroid_runtime", - ], - export_include_dirs: ["include"], export_shared_lib_headers: [ diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp index 0891f2a5a9..7979c2f968 100644 --- a/media/ndk/NdkMediaDataSource.cpp +++ b/media/ndk/NdkMediaDataSource.cpp @@ -23,7 +23,8 @@ #include #include -#include +#include +#include #include #include #include @@ -39,67 +40,9 @@ #include "../../libstagefright/include/NuCachedSource2.h" #include "NdkMediaDataSourceCallbacksPriv.h" -#include // std::call_once,once_flag -#include // dlopen using namespace android; -// load libandroid_runtime.so lazily. -// A vendor process may use libmediandk but should not depend on libandroid_runtime. -// TODO(jooyung): remove duplicate (b/125550121) -// frameworks/native/libs/binder/ndk/ibinder_jni.cpp -namespace { - -typedef JNIEnv* (*getJNIEnv_t)(); -typedef sp (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); - -getJNIEnv_t getJNIEnv_; -ibinderForJavaObject_t ibinderForJavaObject_; - -std::once_flag mLoadFlag; - -void load() { - std::call_once(mLoadFlag, []() { - void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY); - if (handle == nullptr) { - ALOGE("Could not open libandroid_runtime."); - return; - } - - getJNIEnv_ = reinterpret_cast( - dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv")); - if (getJNIEnv_ == nullptr) { - ALOGE("Could not find AndroidRuntime::getJNIEnv."); - // no return - } - - ibinderForJavaObject_ = reinterpret_cast( - dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject")); - if (ibinderForJavaObject_ == nullptr) { - ALOGE("Could not find ibinderForJavaObject."); - // no return - } - }); -} - -JNIEnv* getJNIEnv() { - load(); - if (getJNIEnv_ == nullptr) { - return nullptr; - } - return (getJNIEnv_)(); -} - -sp ibinderForJavaObject(JNIEnv* env, jobject obj) { - load(); - if (ibinderForJavaObject_ == nullptr) { - return nullptr; - } - return (ibinderForJavaObject_)(env, obj); -} - -} // namespace - struct AMediaDataSource { void *userdata; AMediaDataSourceReadAt readAt; @@ -181,14 +124,9 @@ static sp createMediaHttpServiceFromJavaObj(JNIEnv *env, jobje if (obj == NULL) { return NULL; } - sp binder; switch (version) { case 1: - binder = ibinderForJavaObject(env, obj); - if (binder == NULL) { - return NULL; - } - return interface_cast(binder); + return interface_cast(ibinderForJavaObject(env, obj)); case 2: return new JMedia2HTTPService(env, obj); default: @@ -241,7 +179,7 @@ sp createMediaHttpService(const char *uri, int version) { switch (version) { case 1: - env = getJNIEnv(); + env = AndroidRuntime::getJNIEnv(); clazz = "android/media/MediaHTTPService"; method = "createHttpServiceBinderIfNecessary"; signature = "(Ljava/lang/String;)Landroid/os/IBinder;"; -- GitLab From a842529e19d1a8d23a21a1d47bab21d5ac71a198 Mon Sep 17 00:00:00 2001 From: John Tsai Date: Wed, 27 Mar 2019 04:15:40 +0000 Subject: [PATCH 1182/1530] Revert "Separate libstagefright_omx into two libs" This reverts commit c3d86f89d50ff6fa26d98c6f60615040294d1041. Reason for revert: build break at elfin-userdebug 5409972 5410031 Change-Id: I2940521df410fcaf2ba3e2698ca912cb5182f496 --- media/libstagefright/codecs/aacdec/Android.bp | 6 +- media/libstagefright/codecs/aacenc/Android.bp | 8 ++- .../codecs/amrnb/dec/Android.bp | 6 +- .../codecs/amrnb/enc/Android.bp | 6 +- .../libstagefright/codecs/amrwbenc/Android.bp | 6 +- media/libstagefright/codecs/avcdec/Android.bp | 7 +- media/libstagefright/codecs/avcenc/Android.bp | 7 +- .../libstagefright/codecs/flac/dec/Android.bp | 6 +- .../libstagefright/codecs/flac/enc/Android.bp | 8 ++- .../libstagefright/codecs/g711/dec/Android.bp | 7 +- .../libstagefright/codecs/gsm/dec/Android.bp | 7 +- .../libstagefright/codecs/hevcdec/Android.bp | 7 +- .../codecs/m4v_h263/dec/Android.bp | 7 +- .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 1 + .../codecs/m4v_h263/enc/Android.bp | 7 +- media/libstagefright/codecs/mp3dec/Android.bp | 7 +- .../libstagefright/codecs/mpeg2dec/Android.bp | 7 +- .../libstagefright/codecs/on2/dec/Android.bp | 7 +- .../libstagefright/codecs/on2/enc/Android.bp | 8 ++- .../codecs/on2/enc/SoftVP8Encoder.h | 2 + .../codecs/on2/enc/SoftVP9Encoder.h | 2 + .../codecs/on2/enc/SoftVPXEncoder.h | 2 + .../libstagefright/codecs/opus/dec/Android.bp | 6 +- media/libstagefright/codecs/raw/Android.bp | 8 ++- .../codecs/vorbis/dec/Android.bp | 6 +- .../libstagefright/codecs/xaacdec/Android.bp | 6 +- media/libstagefright/omx/Android.bp | 64 ++----------------- .../omx/SoftVideoEncoderOMXComponent.cpp | 2 +- .../media/stagefright/omx/SoftOMXComponent.h | 5 +- .../omx/SoftVideoDecoderOMXComponent.h | 5 +- .../omx/SoftVideoEncoderOMXComponent.h | 4 +- 31 files changed, 133 insertions(+), 104 deletions(-) diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp index e0bb5cd22e..25628a20d3 100644 --- a/media/libstagefright/codecs/aacdec/Android.bp +++ b/media/libstagefright/codecs/aacdec/Android.bp @@ -29,10 +29,12 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], - defaults: ["omx_soft_libs"], - shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", "libcutils", + "liblog", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp index 0d677fe21f..ec1151b3bf 100644 --- a/media/libstagefright/codecs/aacenc/Android.bp +++ b/media/libstagefright/codecs/aacenc/Android.bp @@ -26,7 +26,11 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], - defaults: ["omx_soft_libs"], - + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp index f3b272b989..880f1617c0 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.bp +++ b/media/libstagefright/codecs/amrnb/dec/Android.bp @@ -101,9 +101,11 @@ cc_library_shared { "libstagefright_amrwbdec", ], - defaults: ["omx_soft_libs"], - shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp index 1c8b511b32..19fd4a85c6 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.bp +++ b/media/libstagefright/codecs/amrnb/enc/Android.bp @@ -110,9 +110,11 @@ cc_library_shared { static_libs: ["libstagefright_amrnbenc"], - defaults: ["omx_soft_libs"], - shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index 8327500b66..b9d45c1a63 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -167,9 +167,11 @@ cc_library_shared { static_libs: ["libstagefright_amrwbenc"], - defaults: ["omx_soft_libs"], - shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", "libstagefright_enc_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp index 567bcca181..8a34845bf8 100644 --- a/media/libstagefright/codecs/avcdec/Android.bp +++ b/media/libstagefright/codecs/avcdec/Android.bp @@ -22,7 +22,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp index 0cd39e1b46..63718283e6 100644 --- a/media/libstagefright/codecs/avcenc/Android.bp +++ b/media/libstagefright/codecs/avcenc/Android.bp @@ -16,7 +16,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_foundation", + "libstagefright_omx", + "libutils", + "liblog", + ], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp index 18a3f6bd19..3d4a44f0c4 100644 --- a/media/libstagefright/codecs/flac/dec/Android.bp +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -28,10 +28,12 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - shared_libs: [ + "liblog", "libstagefright_flacdec", + "libstagefright_omx", + "libstagefright_foundation", + "libutils", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp index 4149ccde22..b32ab08a6c 100644 --- a/media/libstagefright/codecs/flac/enc/Android.bp +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -19,7 +19,13 @@ cc_library_shared { ], cfi: true, }, - defaults: ["omx_soft_libs"], + + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], header_libs: ["libbase_headers"], static_libs: [ diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp index c273179ac6..7097688ef3 100644 --- a/media/libstagefright/codecs/g711/dec/Android.bp +++ b/media/libstagefright/codecs/g711/dec/Android.bp @@ -12,7 +12,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_foundation", + "libstagefright_omx", + "libutils", + "liblog", + ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp index 3c5ebfe22e..a973f708fe 100644 --- a/media/libstagefright/codecs/gsm/dec/Android.bp +++ b/media/libstagefright/codecs/gsm/dec/Android.bp @@ -25,7 +25,12 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_foundation", + "libstagefright_omx", + "libutils", + "liblog", + ], static_libs: ["libgsm"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp index cc91d534b3..60fc446547 100644 --- a/media/libstagefright/codecs/hevcdec/Android.bp +++ b/media/libstagefright/codecs/hevcdec/Android.bp @@ -30,7 +30,12 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], // We need this because the current asm generates the following link error: // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp index 0523143724..41141b13c8 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp @@ -91,7 +91,12 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263dec"], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index 9893c6f39c..a8fcdd1e3a 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "mp4dec_api.h" diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp index d38f4b1359..d4f7d50493 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp @@ -77,7 +77,12 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263enc"], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_foundation", + "libstagefright_omx", + "libutils", + "liblog", + ], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp index 9173ed6611..2154f843bc 100644 --- a/media/libstagefright/codecs/mp3dec/Android.bp +++ b/media/libstagefright/codecs/mp3dec/Android.bp @@ -105,7 +105,12 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], static_libs: ["libstagefright_mp3dec"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp index 26e786e7f6..c65554414b 100644 --- a/media/libstagefright/codecs/mpeg2dec/Android.bp +++ b/media/libstagefright/codecs/mpeg2dec/Android.bp @@ -20,7 +20,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp index abd21d7e49..174f183eee 100644 --- a/media/libstagefright/codecs/on2/dec/Android.bp +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -14,7 +14,12 @@ cc_library_shared { static_libs: ["libvpx"], - defaults: ["omx_soft_libs"], + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp index ea46badfcb..891a771c34 100644 --- a/media/libstagefright/codecs/on2/enc/Android.bp +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -30,7 +30,11 @@ cc_library_shared { static_libs: ["libvpx"], - defaults: ["omx_soft_libs"], - + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h index c5c2abfa0e..b4904bfd37 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h @@ -23,6 +23,8 @@ #include #include +#include + #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h index 308a9ac95f..85df69ac05 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h @@ -23,6 +23,8 @@ #include #include +#include + #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 7208d694ac..263d13450f 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -23,6 +23,8 @@ #include #include +#include + #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp index bfcae070d5..afe459da14 100644 --- a/media/libstagefright/codecs/opus/dec/Android.bp +++ b/media/libstagefright/codecs/opus/dec/Android.bp @@ -12,10 +12,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], - shared_libs: [ "libopus", + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp index 1c23badc38..f822445872 100644 --- a/media/libstagefright/codecs/raw/Android.bp +++ b/media/libstagefright/codecs/raw/Android.bp @@ -24,7 +24,11 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - + shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", + ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp index 2d1a92250d..a9265cbabc 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.bp +++ b/media/libstagefright/codecs/vorbis/dec/Android.bp @@ -12,10 +12,12 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - defaults: ["omx_soft_libs"], - shared_libs: [ "libvorbisidec", + "libstagefright_omx", + "libstagefright_foundation", + "libutils", + "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp index e49eb8fa65..7392f1e1d1 100644 --- a/media/libstagefright/codecs/xaacdec/Android.bp +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -24,10 +24,12 @@ cc_library_shared { static_libs: ["libxaacdec"], - defaults: ["omx_soft_libs"], - shared_libs: [ + "libstagefright_omx", + "libstagefright_foundation", + "libutils", "libcutils", + "liblog", ], compile_multilib: "32", diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index b959f6cc3e..4383004e8d 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -11,6 +11,11 @@ cc_library_shared { "OMXNodeInstance.cpp", "OMXUtils.cpp", "OmxGraphicBufferSource.cpp", + "SimpleSoftOMXComponent.cpp", + "SoftOMXComponent.cpp", + "SoftOMXPlugin.cpp", + "SoftVideoDecoderOMXComponent.cpp", + "SoftVideoEncoderOMXComponent.cpp", "1.0/Omx.cpp", "1.0/OmxStore.cpp", "1.0/WGraphicBufferSource.cpp", @@ -51,7 +56,6 @@ cc_library_shared { "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", - "libstagefright_omx_soft", ], export_shared_lib_headers: [ @@ -77,64 +81,6 @@ cc_library_shared { }, } -cc_defaults { - name: "omx_soft_libs", - shared_libs: [ - "libutils", - "liblog", - "libstagefright_foundation", - "libstagefright_omx_soft", - ], -} - -cc_library_shared { - name: "libstagefright_omx_soft", - vendor_available: true, - vndk: { - enabled: true, - }, - - srcs: [ - "SimpleSoftOMXComponent.cpp", - "SoftOMXComponent.cpp", - "SoftOMXPlugin.cpp", - "SoftVideoDecoderOMXComponent.cpp", - "SoftVideoEncoderOMXComponent.cpp", - ], - - export_include_dirs: [ - "include", - ], - - shared_libs: [ - "libutils", - "liblog", - "libui", - "libstagefright_foundation", - ], - - export_shared_lib_headers: [ - "libstagefright_foundation", - "libutils", - "liblog", - ], - - cflags: [ - "-Werror", - "-Wall", - "-Wno-unused-parameter", - "-Wno-documentation", - ], - - sanitize: { - misc_undefined: [ - "signed-integer-overflow", - "unsigned-integer-overflow", - ], - cfi: true, - }, -} - cc_library_shared { name: "libstagefright_omx_utils", vendor_available: true, diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index d75acdabf8..2fbbb4474d 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -559,7 +559,7 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( if (nativeMeta.nFenceFd >= 0) { sp fence = new Fence(nativeMeta.nFenceFd); nativeMeta.nFenceFd = -1; - status_t err = fence->wait(kFenceTimeoutMs); + status_t err = fence->wait(IOMX::kFenceTimeoutMs); if (err != OK) { ALOGE("Timed out waiting on input fence"); return NULL; diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h index 79f0c77c50..3ab6f88762 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h @@ -21,15 +21,12 @@ #include #include #include -#include + #include namespace android { struct SoftOMXComponent : public RefBase { - enum { - kFenceTimeoutMs = 1000 - }; SoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h index d7c1658a57..3b381cea02 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h @@ -23,10 +23,7 @@ #include #include #include -#include -#include -#include - +#include #include #include diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h index 9cb72dd6da..2d6f31b84f 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h @@ -18,9 +18,7 @@ #define SOFT_VIDEO_ENCODER_OMX_COMPONENT_H_ -#include -#include -#include +#include #include "SimpleSoftOMXComponent.h" -- GitLab From 0016740cca36b1994ecddf7a31a04ec622062596 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Wed, 27 Mar 2019 14:15:54 +0000 Subject: [PATCH 1183/1530] Convert libaudioflinger to Android.bp This makes the libaudioflinger build deterministic between devices. A few notes: - the include-path-for include is obsolete, so it was removed - the AUDIOSERVER_MULTILIB use was removed, as I believe it's unused Bug: 121208203 Test: make Change-Id: I08eb0d7f53f1fbcef3e14faa3ea75b5326383ef8 --- services/audioflinger/Android.bp | 73 +++++++++++++++++++++++++++++++ services/audioflinger/Android.mk | 74 -------------------------------- 2 files changed, 73 insertions(+), 74 deletions(-) create mode 100644 services/audioflinger/Android.bp delete mode 100644 services/audioflinger/Android.mk diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp new file mode 100644 index 0000000000..96ad54be2c --- /dev/null +++ b/services/audioflinger/Android.bp @@ -0,0 +1,73 @@ + + +cc_library_shared { + name: "libaudioflinger", + + srcs: [ + "AudioFlinger.cpp", + "AudioHwDevice.cpp", + "AudioStreamOut.cpp", + "AudioWatchdog.cpp", + "BufLog.cpp", + "Effects.cpp", + "FastCapture.cpp", + "FastCaptureDumpState.cpp", + "FastCaptureState.cpp", + "FastMixer.cpp", + "FastMixerDumpState.cpp", + "FastMixerState.cpp", + "FastThread.cpp", + "FastThreadDumpState.cpp", + "FastThreadState.cpp", + "NBAIO_Tee.cpp", + "PatchPanel.cpp", + "SpdifStreamOut.cpp", + "StateQueue.cpp", + "Threads.cpp", + "Tracks.cpp", + "TypedLogger.cpp", + ], + + include_dirs: [ + "frameworks/av/services/audiopolicy", + "frameworks/av/services/medialog", + ], + + shared_libs: [ + "libaudiohal", + "libaudioprocessing", + "libaudiospdif", + "libaudioutils", + "libcutils", + "libutils", + "liblog", + "libbinder", + "libaudioclient", + "libmedialogservice", + "libmediametrics", + "libmediautils", + "libnbaio", + "libnblog", + "libpowermanager", + "libmediautils", + "libmemunreachable", + "libmedia_helper", + "libvibrator", + ], + + static_libs: [ + "libcpustats", + "libsndfile", + ], + + cflags: [ + "-DSTATE_QUEUE_INSTANTIATIONS=\"StateQueueInstantiations.cpp\"", + "-fvisibility=hidden", + "-Werror", + "-Wall", + ], + sanitize: { + integer_overflow: true, + }, + +} diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk deleted file mode 100644 index 40980a6991..0000000000 --- a/services/audioflinger/Android.mk +++ /dev/null @@ -1,74 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - AudioFlinger.cpp \ - Threads.cpp \ - Tracks.cpp \ - AudioHwDevice.cpp \ - AudioStreamOut.cpp \ - SpdifStreamOut.cpp \ - Effects.cpp \ - PatchPanel.cpp \ - StateQueue.cpp \ - BufLog.cpp \ - TypedLogger.cpp \ - NBAIO_Tee.cpp \ - -LOCAL_C_INCLUDES := \ - frameworks/av/services/audiopolicy \ - frameworks/av/services/medialog \ - $(call include-path-for, audio-utils) - -LOCAL_SHARED_LIBRARIES := \ - libaudiohal \ - libaudioprocessing \ - libaudiospdif \ - libaudioutils \ - libcutils \ - libutils \ - liblog \ - libbinder \ - libaudioclient \ - libmedialogservice \ - libmediametrics \ - libmediautils \ - libnbaio \ - libnblog \ - libpowermanager \ - libmediautils \ - libmemunreachable \ - libmedia_helper \ - libvibrator - -LOCAL_STATIC_LIBRARIES := \ - libcpustats \ - libsndfile \ - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE:= libaudioflinger - -LOCAL_SRC_FILES += \ - AudioWatchdog.cpp \ - FastCapture.cpp \ - FastCaptureDumpState.cpp \ - FastCaptureState.cpp \ - FastMixer.cpp \ - FastMixerDumpState.cpp \ - FastMixerState.cpp \ - FastThread.cpp \ - FastThreadDumpState.cpp \ - FastThreadState.cpp - -LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' - -LOCAL_CFLAGS += -fvisibility=hidden - -LOCAL_CFLAGS += -Werror -Wall -LOCAL_SANITIZE := integer_overflow - -include $(BUILD_SHARED_LIBRARY) - -include $(call all-makefiles-under,$(LOCAL_PATH)) -- GitLab From db0354ee412add8835e35593ba205422f5355602 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 27 Mar 2019 09:49:26 -0700 Subject: [PATCH 1184/1530] Rework how DataURISource::Create()'s strips cr,lf change the removal algorithm so worst case goes from O(n^2) to O(n). Bug: 128431761 Test: POC --- media/libstagefright/DataURISource.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/media/libstagefright/DataURISource.cpp b/media/libstagefright/DataURISource.cpp index 3dc345f3a5..b975b38820 100644 --- a/media/libstagefright/DataURISource.cpp +++ b/media/libstagefright/DataURISource.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include #include @@ -39,15 +38,27 @@ sp DataURISource::Create(const char *uri) { AString tmp(&uri[5], commaPos - &uri[5]); if (tmp.endsWith(";base64")) { - AString encoded(commaPos + 1); - // Strip CR and LF... - for (size_t i = encoded.size(); i > 0;) { - i--; - if (encoded.c_str()[i] == '\r' || encoded.c_str()[i] == '\n') { - encoded.erase(i, 1); + // strip all CR and LF characters. + const char *src = commaPos+1; + int len = strlen(src) + 1; + char *cleansed = (char *) malloc(len); + if (cleansed == NULL) return NULL; + char *keeping = cleansed; + int left = len; + for (int i = 0; i < len ; i++) + { + const char c = *src++; + if (c == '\r' || c == '\n') { + continue; } + *keeping++ = c; + left--; } + memset(keeping, 0, left); + + AString encoded(cleansed); + free(cleansed); buffer = decodeBase64(encoded); -- GitLab From d400724c9e75ce6afd21ef1f1bdfe9d321b10f67 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 27 Mar 2019 12:42:16 -0700 Subject: [PATCH 1185/1530] audio policy: fix typo in doReleaseOutput() Fix use use of mAudioRecordClients instead of mAudioPlaybackClients in doReleaseOutput(). Test: make. Change-Id: I5c695582828300c0297f4f58bcba37e17b51bbbd --- services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 93e3c444d0..9a39275164 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -322,7 +322,7 @@ void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId) return; } sp client = mAudioPlaybackClients.valueAt(index); - mAudioRecordClients.removeItem(portId); + mAudioPlaybackClients.removeItem(portId); // called from internal thread: no need to clear caller identity mAudioPolicyManager->releaseOutput(portId); -- GitLab From 62d719f099cf975d72cfcd1a7b692a84a2249275 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 27 Mar 2019 08:39:24 -0700 Subject: [PATCH 1186/1530] Fix out of bounds access Bug: 109891727 Test: poc, manual w/ CTS files Change-Id: I9056f993d1f2cf18f9f70fa60c41799e7c3585e0 --- media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index a8fcdd1e3a..3e05ff4a8e 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -118,9 +118,14 @@ void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) { outHeader->nFlags = OMX_BUFFERFLAG_EOS; List::iterator it = outQueue.begin(); - while ((*it)->mHeader != outHeader) { + while (it != outQueue.end() && (*it)->mHeader != outHeader) { ++it; } + if (it == outQueue.end()) { + ALOGE("couldn't find port buffer %d in outQueue: b/109891727", mNumSamplesOutput & 1); + android_errorWriteLog(0x534e4554, "109891727"); + return; + } BufferInfo *outInfo = *it; outInfo->mOwnedByUs = false; -- GitLab From 1f2f5058f7b920ff63c974d0fc1e1b4eb5a0c702 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 12 Mar 2019 10:55:56 -0700 Subject: [PATCH 1187/1530] ACodec: enable B frame support for AVC Bug: 127315857 Test: screenrecord --codec-name OMX.google.h264.encoder --bframes 1 /sdcard/a.mp4 Change-Id: Ia9b53e916978fb3aa2edde9f5934d5c03b56c1bb --- media/libstagefright/ACodec.cpp | 14 +++++++------- .../include/media/stagefright/ACodec.h | 2 +- .../media/stagefright/MediaCodecConstants.h | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index f00c895eb3..cf1a6f1393 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -569,7 +569,6 @@ ACodec::ACodec() mFps(-1.0), mCaptureFps(-1.0), mCreateInputBuffersSuspended(false), - mLatency(0), mTunneled(false), mDescribeColorAspectsIndex((OMX_INDEXTYPE)0), mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0), @@ -4425,12 +4424,13 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { h264type.eProfile == OMX_VIDEO_AVCProfileHigh) { h264type.nSliceHeaderSpacing = 0; h264type.bUseHadamard = OMX_TRUE; - h264type.nRefFrames = 2; - h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1); - - // disable B-frames until we have explicit settings for enabling the feature. - h264type.nRefFrames = 1; - h264type.nBFrames = 0; + int32_t maxBframes = 0; + (void)msg->findInt32(KEY_MAX_B_FRAMES, &maxBframes); + h264type.nBFrames = uint32_t(maxBframes); + if (mLatency && h264type.nBFrames > *mLatency) { + h264type.nBFrames = *mLatency; + } + h264type.nRefFrames = h264type.nBFrames == 0 ? 1 : 2; h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames); h264type.nAllowedPictureTypes = diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h index 9d46d2d6a9..784fd36e64 100644 --- a/media/libstagefright/include/media/stagefright/ACodec.h +++ b/media/libstagefright/include/media/stagefright/ACodec.h @@ -286,7 +286,7 @@ private: double mFps; double mCaptureFps; bool mCreateInputBuffersSuspended; - uint32_t mLatency; + std::optional mLatency; bool mTunneled; diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index 2dca5c3cd4..8b6944bd9f 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -772,6 +772,7 @@ constexpr char KEY_IS_TIMED_TEXT[] = "is-timed-text"; constexpr char KEY_LANGUAGE[] = "language"; constexpr char KEY_LATENCY[] = "latency"; constexpr char KEY_LEVEL[] = "level"; +constexpr char KEY_MAX_B_FRAMES[] = "max-bframes"; constexpr char KEY_MAX_BIT_RATE[] = "max-bitrate"; constexpr char KEY_MAX_FPS_TO_ENCODER[] = "max-fps-to-encoder"; constexpr char KEY_MAX_HEIGHT[] = "max-height"; -- GitLab From 15a5130330371594d669dbbe1fee0e23b1f50224 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 12 Mar 2019 10:59:41 -0700 Subject: [PATCH 1188/1530] screenrecord: add bframes option Test: screenrecord --codec-name OMX.google.h264.encoder --bframes 1 /sdcard/a.mp4 Change-Id: I6ae9310fb5ef644138af0becb7428f4a2c6393fe --- cmds/screenrecord/screenrecord.cpp | 33 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index c361690eb0..7aa655ff37 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -83,8 +84,6 @@ using android::status_t; using android::DISPLAY_ORIENTATION_0; using android::DISPLAY_ORIENTATION_180; using android::DISPLAY_ORIENTATION_90; -using android::INFO_FORMAT_CHANGED; -using android::INFO_OUTPUT_BUFFERS_CHANGED; using android::INVALID_OPERATION; using android::NAME_NOT_FOUND; using android::NO_ERROR; @@ -113,6 +112,7 @@ static uint32_t gVideoWidth = 0; // default width+height static uint32_t gVideoHeight = 0; static uint32_t gBitRate = 20000000; // 20Mbps static uint32_t gTimeLimitSec = kMaxTimeLimitSec; +static uint32_t gBframes = 0; // Set by signal handler to stop recording. static volatile bool gStopRequested = false; @@ -184,13 +184,18 @@ static status_t prepareEncoder(float displayFps, sp* pCodec, } sp format = new AMessage; - format->setInt32("width", gVideoWidth); - format->setInt32("height", gVideoHeight); - format->setString("mime", kMimeTypeAvc); - format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); - format->setInt32("bitrate", gBitRate); - format->setFloat("frame-rate", displayFps); - format->setInt32("i-frame-interval", 10); + format->setInt32(KEY_WIDTH, gVideoWidth); + format->setInt32(KEY_HEIGHT, gVideoHeight); + format->setString(KEY_MIME, kMimeTypeAvc); + format->setInt32(KEY_COLOR_FORMAT, OMX_COLOR_FormatAndroidOpaque); + format->setInt32(KEY_BIT_RATE, gBitRate); + format->setFloat(KEY_FRAME_RATE, displayFps); + format->setInt32(KEY_I_FRAME_INTERVAL, 10); + format->setInt32(KEY_MAX_B_FRAMES, gBframes); + if (gBframes > 0) { + format->setInt32(KEY_PROFILE, AVCProfileMain); + format->setInt32(KEY_LEVEL, AVCLevel41); + } sp looper = new android::ALooper; looper->setName("screenrecord_looper"); @@ -478,7 +483,7 @@ static status_t runEncoder(const sp& encoder, case -EAGAIN: // INFO_TRY_AGAIN_LATER ALOGV("Got -EAGAIN, looping"); break; - case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED + case android::INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED { // Format includes CSD, which we must provide to muxer. ALOGV("Encoder format changed"); @@ -495,7 +500,7 @@ static status_t runEncoder(const sp& encoder, } } break; - case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED + case android::INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED // Not expected for an encoder; handle it anyway. ALOGV("Encoder buffers changed"); err = encoder->getOutputBuffers(&buffers); @@ -960,6 +965,7 @@ int main(int argc, char* const argv[]) { { "codec-name", required_argument, NULL, 'N' }, { "monotonic-time", no_argument, NULL, 'm' }, { "persistent-surface", no_argument, NULL, 'p' }, + { "bframes", required_argument, NULL, 'B' }, { NULL, 0, NULL, 0 } }; @@ -1052,6 +1058,11 @@ int main(int argc, char* const argv[]) { case 'p': gPersistentSurface = true; break; + case 'B': + if (parseValueWithUnit(optarg, &gBframes) != NO_ERROR) { + return 2; + } + break; default: if (ic != '?') { fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); -- GitLab From 12806e31bdffea6eb79e4957fb1777140a8d0a8f Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 20 Mar 2019 10:24:55 -0700 Subject: [PATCH 1189/1530] Revert "Revert "Separate libstagefright_omx into two libs"" This reverts commit a842529e19d1a8d23a21a1d47bab21d5ac71a198. Reason for revert: Reland with fixes to leave old libstagefright_ omx intact until partner switches to use the new lib. Change-Id: Icf747111028f22e41f79d87c7f481d9c14c02791 --- media/libstagefright/codecs/aacdec/Android.bp | 6 +- media/libstagefright/codecs/aacenc/Android.bp | 8 +-- .../codecs/amrnb/dec/Android.bp | 6 +- .../codecs/amrnb/enc/Android.bp | 6 +- .../libstagefright/codecs/amrwbenc/Android.bp | 6 +- media/libstagefright/codecs/avcdec/Android.bp | 7 +-- media/libstagefright/codecs/avcenc/Android.bp | 7 +-- .../libstagefright/codecs/flac/dec/Android.bp | 6 +- .../libstagefright/codecs/flac/enc/Android.bp | 8 +-- .../libstagefright/codecs/g711/dec/Android.bp | 7 +-- .../libstagefright/codecs/gsm/dec/Android.bp | 7 +-- .../libstagefright/codecs/hevcdec/Android.bp | 7 +-- .../codecs/m4v_h263/dec/Android.bp | 7 +-- .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 1 - .../codecs/m4v_h263/enc/Android.bp | 7 +-- media/libstagefright/codecs/mp3dec/Android.bp | 7 +-- .../libstagefright/codecs/mpeg2dec/Android.bp | 7 +-- .../libstagefright/codecs/on2/dec/Android.bp | 7 +-- .../libstagefright/codecs/on2/enc/Android.bp | 8 +-- .../codecs/on2/enc/SoftVP8Encoder.h | 2 - .../codecs/on2/enc/SoftVP9Encoder.h | 2 - .../codecs/on2/enc/SoftVPXEncoder.h | 2 - .../libstagefright/codecs/opus/dec/Android.bp | 6 +- media/libstagefright/codecs/raw/Android.bp | 8 +-- .../codecs/vorbis/dec/Android.bp | 6 +- .../libstagefright/codecs/xaacdec/Android.bp | 6 +- media/libstagefright/omx/Android.bp | 61 +++++++++++++++++++ .../omx/SoftVideoEncoderOMXComponent.cpp | 2 +- .../media/stagefright/omx/SoftOMXComponent.h | 5 +- .../omx/SoftVideoDecoderOMXComponent.h | 5 +- .../omx/SoftVideoEncoderOMXComponent.h | 4 +- 31 files changed, 106 insertions(+), 128 deletions(-) diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp index 25628a20d3..e0bb5cd22e 100644 --- a/media/libstagefright/codecs/aacdec/Android.bp +++ b/media/libstagefright/codecs/aacdec/Android.bp @@ -29,12 +29,10 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", "libcutils", - "liblog", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp index ec1151b3bf..0d677fe21f 100644 --- a/media/libstagefright/codecs/aacenc/Android.bp +++ b/media/libstagefright/codecs/aacenc/Android.bp @@ -26,11 +26,7 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp index 880f1617c0..f3b272b989 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.bp +++ b/media/libstagefright/codecs/amrnb/dec/Android.bp @@ -101,11 +101,9 @@ cc_library_shared { "libstagefright_amrwbdec", ], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp index 19fd4a85c6..1c8b511b32 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.bp +++ b/media/libstagefright/codecs/amrnb/enc/Android.bp @@ -110,11 +110,9 @@ cc_library_shared { static_libs: ["libstagefright_amrnbenc"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_amrnb_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index b9d45c1a63..8327500b66 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -167,11 +167,9 @@ cc_library_shared { static_libs: ["libstagefright_amrwbenc"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", "libstagefright_enc_common", ], compile_multilib: "32", diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp index 8a34845bf8..567bcca181 100644 --- a/media/libstagefright/codecs/avcdec/Android.bp +++ b/media/libstagefright/codecs/avcdec/Android.bp @@ -22,12 +22,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp index 63718283e6..0cd39e1b46 100644 --- a/media/libstagefright/codecs/avcenc/Android.bp +++ b/media/libstagefright/codecs/avcenc/Android.bp @@ -16,12 +16,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp index 3d4a44f0c4..18a3f6bd19 100644 --- a/media/libstagefright/codecs/flac/dec/Android.bp +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -28,12 +28,10 @@ cc_library_shared { cfi: true, }, + defaults: ["omx_soft_libs"], + shared_libs: [ - "liblog", "libstagefright_flacdec", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", ], compile_multilib: "32", } diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp index b32ab08a6c..4149ccde22 100644 --- a/media/libstagefright/codecs/flac/enc/Android.bp +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -19,13 +19,7 @@ cc_library_shared { ], cfi: true, }, - - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], header_libs: ["libbase_headers"], static_libs: [ diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp index 7097688ef3..c273179ac6 100644 --- a/media/libstagefright/codecs/g711/dec/Android.bp +++ b/media/libstagefright/codecs/g711/dec/Android.bp @@ -12,12 +12,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp index a973f708fe..3c5ebfe22e 100644 --- a/media/libstagefright/codecs/gsm/dec/Android.bp +++ b/media/libstagefright/codecs/gsm/dec/Android.bp @@ -25,12 +25,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], static_libs: ["libgsm"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp index 60fc446547..cc91d534b3 100644 --- a/media/libstagefright/codecs/hevcdec/Android.bp +++ b/media/libstagefright/codecs/hevcdec/Android.bp @@ -30,12 +30,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], // We need this because the current asm generates the following link error: // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp index 41141b13c8..0523143724 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp @@ -91,12 +91,7 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263dec"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp index a8fcdd1e3a..9893c6f39c 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "mp4dec_api.h" diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp index d4f7d50493..d38f4b1359 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp @@ -77,12 +77,7 @@ cc_library_shared { static_libs: ["libstagefright_m4vh263enc"], - shared_libs: [ - "libstagefright_foundation", - "libstagefright_omx", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], sanitize: { misc_undefined: [ diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp index 2154f843bc..9173ed6611 100644 --- a/media/libstagefright/codecs/mp3dec/Android.bp +++ b/media/libstagefright/codecs/mp3dec/Android.bp @@ -105,12 +105,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], static_libs: ["libstagefright_mp3dec"], compile_multilib: "32", diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp index c65554414b..26e786e7f6 100644 --- a/media/libstagefright/codecs/mpeg2dec/Android.bp +++ b/media/libstagefright/codecs/mpeg2dec/Android.bp @@ -20,12 +20,7 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], ldflags: ["-Wl,-Bsymbolic"], diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp index 174f183eee..abd21d7e49 100644 --- a/media/libstagefright/codecs/on2/dec/Android.bp +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -14,12 +14,7 @@ cc_library_shared { static_libs: ["libvpx"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp index 891a771c34..ea46badfcb 100644 --- a/media/libstagefright/codecs/on2/enc/Android.bp +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -30,11 +30,7 @@ cc_library_shared { static_libs: ["libvpx"], - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h index b4904bfd37..c5c2abfa0e 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h index 85df69ac05..308a9ac95f 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h index 263d13450f..7208d694ac 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -23,8 +23,6 @@ #include #include -#include - #include "vpx/vpx_encoder.h" #include "vpx/vpx_codec.h" #include "vpx/vp8cx.h" diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp index afe459da14..bfcae070d5 100644 --- a/media/libstagefright/codecs/opus/dec/Android.bp +++ b/media/libstagefright/codecs/opus/dec/Android.bp @@ -12,12 +12,10 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], + defaults: ["omx_soft_libs"], + shared_libs: [ "libopus", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp index f822445872..1c23badc38 100644 --- a/media/libstagefright/codecs/raw/Android.bp +++ b/media/libstagefright/codecs/raw/Android.bp @@ -24,11 +24,7 @@ cc_library_shared { cfi: true, }, - shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", - ], + defaults: ["omx_soft_libs"], + compile_multilib: "32", } diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp index a9265cbabc..2d1a92250d 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.bp +++ b/media/libstagefright/codecs/vorbis/dec/Android.bp @@ -12,12 +12,10 @@ cc_library_shared { "frameworks/native/include/media/openmax", ], + defaults: ["omx_soft_libs"], + shared_libs: [ "libvorbisidec", - "libstagefright_omx", - "libstagefright_foundation", - "libutils", - "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp index 7392f1e1d1..e49eb8fa65 100644 --- a/media/libstagefright/codecs/xaacdec/Android.bp +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -24,12 +24,10 @@ cc_library_shared { static_libs: ["libxaacdec"], + defaults: ["omx_soft_libs"], + shared_libs: [ - "libstagefright_omx", - "libstagefright_foundation", - "libutils", "libcutils", - "liblog", ], compile_multilib: "32", diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 4383004e8d..b8f9aea76f 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -11,6 +11,8 @@ cc_library_shared { "OMXNodeInstance.cpp", "OMXUtils.cpp", "OmxGraphicBufferSource.cpp", + //TODO: remove the soft component code here and use + //libstagefright_omx_soft, once partner build is fixed "SimpleSoftOMXComponent.cpp", "SoftOMXComponent.cpp", "SoftOMXPlugin.cpp", @@ -56,6 +58,7 @@ cc_library_shared { "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", + //"libstagefright_omx_soft", ], export_shared_lib_headers: [ @@ -81,6 +84,64 @@ cc_library_shared { }, } +cc_defaults { + name: "omx_soft_libs", + shared_libs: [ + "libutils", + "liblog", + "libstagefright_foundation", + "libstagefright_omx_soft", + ], +} + +cc_library_shared { + name: "libstagefright_omx_soft", + vendor_available: true, + vndk: { + enabled: true, + }, + + srcs: [ + "SimpleSoftOMXComponent.cpp", + "SoftOMXComponent.cpp", + "SoftOMXPlugin.cpp", + "SoftVideoDecoderOMXComponent.cpp", + "SoftVideoEncoderOMXComponent.cpp", + ], + + export_include_dirs: [ + "include", + ], + + shared_libs: [ + "libutils", + "liblog", + "libui", + "libstagefright_foundation", + ], + + export_shared_lib_headers: [ + "libstagefright_foundation", + "libutils", + "liblog", + ], + + cflags: [ + "-Werror", + "-Wall", + "-Wno-unused-parameter", + "-Wno-documentation", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} + cc_library_shared { name: "libstagefright_omx_utils", vendor_available: true, diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp index 2fbbb4474d..d75acdabf8 100644 --- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp +++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp @@ -559,7 +559,7 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer( if (nativeMeta.nFenceFd >= 0) { sp fence = new Fence(nativeMeta.nFenceFd); nativeMeta.nFenceFd = -1; - status_t err = fence->wait(IOMX::kFenceTimeoutMs); + status_t err = fence->wait(kFenceTimeoutMs); if (err != OK) { ALOGE("Timed out waiting on input fence"); return NULL; diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h index 3ab6f88762..79f0c77c50 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXComponent.h @@ -21,12 +21,15 @@ #include #include #include - +#include #include namespace android { struct SoftOMXComponent : public RefBase { + enum { + kFenceTimeoutMs = 1000 + }; SoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h index 3b381cea02..d7c1658a57 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoDecoderOMXComponent.h @@ -23,7 +23,10 @@ #include #include #include -#include +#include +#include +#include + #include #include diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h index 2d6f31b84f..9cb72dd6da 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftVideoEncoderOMXComponent.h @@ -18,7 +18,9 @@ #define SOFT_VIDEO_ENCODER_OMX_COMPONENT_H_ -#include +#include +#include +#include #include "SimpleSoftOMXComponent.h" -- GitLab From c6eacf4a4d0e2960e625a327ebf2c840f743ee6c Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 28 Mar 2019 10:25:53 -0700 Subject: [PATCH 1190/1530] Update seccomp policy to support crash dumps Bug: 129426284 Test: manual Change-Id: Ic25ac665e5ff16ac98311f91bf598404313b0616 --- .../seccomp_policy/mediaswcodec-arm64.policy | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy index 78ecaf58a9..4faf8b20c1 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy @@ -28,7 +28,6 @@ newfstatat: 1 fstatfs: 1 memfd_create: 1 ftruncate: 1 -ftruncate64: 1 # mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail # parser support for '<' is in this needs to be modified to also prevent @@ -62,3 +61,21 @@ rt_sigreturn: 1 getrandom: 1 madvise: 1 +# crash dump policy additions +clock_gettime: 1 +getpid: 1 +gettid: 1 +pipe2: 1 +recvmsg: 1 +process_vm_readv: 1 +tgkill: 1 +rt_sigaction: 1 +rt_tgsigqueueinfo: 1 +#mprotect: arg2 in 0x1|0x2 +munmap: 1 +#mmap: arg2 in 0x1|0x2 +geteuid: 1 +getgid: 1 +getegid: 1 +getgroups: 1 + -- GitLab From 959e2d068b21e6b9443d5e98fa25a607d1f93993 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 28 Mar 2019 11:08:19 -0700 Subject: [PATCH 1191/1530] audioserver: Use timed lock instead of bouncing for dumps The technique of bouncing lock attempts does not work well in the case when another lock has higher priority and acquires the lock frequently. Timed lock works better in this case because the acquiring thread is then put in a queue and gets the lock as soon as another thread releases it. The wait time in the worst case (deadlock) is the same. Bug: 118842894 Test: Run VOICE_COMMUNICATION capture + AEC effect, dump audioflinger Change-Id: Idc4fc2b6f5faf6988979f9354dd92441af33e600 --- services/audioflinger/AudioFlinger.cpp | 11 ++------- services/audioflinger/AudioFlinger.h | 3 +-- .../service/AudioPolicyService.cpp | 23 +++++++------------ services/soundtrigger/Android.bp | 1 + .../soundtrigger/SoundTriggerHwService.cpp | 19 +++++---------- 5 files changed, 18 insertions(+), 39 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 43260c26d8..0825cb4c33 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -466,15 +466,8 @@ void AudioFlinger::dumpPermissionDenial(int fd, const Vector& args __u bool AudioFlinger::dumpTryLock(Mutex& mutex) { - bool locked = false; - for (int i = 0; i < kDumpLockRetries; ++i) { - if (mutex.tryLock() == NO_ERROR) { - locked = true; - break; - } - usleep(kDumpLockSleepUs); - } - return locked; + status_t err = mutex.timedLock(kDumpLockTimeoutNs); + return err == NO_ERROR; } status_t AudioFlinger::dump(int fd, const Vector& args) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 8ac3366299..5a65ea8c09 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -431,8 +431,7 @@ private: static uint32_t mScreenState; // Internal dump utilities. - static const int kDumpLockRetries = 50; - static const int kDumpLockSleepUs = 20000; + static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND; static bool dumpTryLock(Mutex& mutex); void dumpPermissionDenial(int fd, const Vector& args); void dumpClients(int fd, const Vector& args); diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index e858e8d4d0..f63fa8123a 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -22,8 +22,9 @@ #define __STDINT_LIMITS #define __STDC_LIMIT_MACROS #include - #include + +#include #include #include #include @@ -48,8 +49,7 @@ namespace android { static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n"; -static const int kDumpLockRetries = 50; -static const int kDumpLockSleepUs = 20000; +static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND; static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds @@ -376,17 +376,10 @@ void AudioPolicyService::binderDied(const wp& who) { IPCThreadState::self()->getCallingPid()); } -static bool tryLock(Mutex& mutex) +static bool dumpTryLock(Mutex& mutex) { - bool locked = false; - for (int i = 0; i < kDumpLockRetries; ++i) { - if (mutex.tryLock() == NO_ERROR) { - locked = true; - break; - } - usleep(kDumpLockSleepUs); - } - return locked; + status_t err = mutex.timedLock(kDumpLockTimeoutNs); + return err == NO_ERROR; } status_t AudioPolicyService::dumpInternals(int fd) @@ -627,7 +620,7 @@ status_t AudioPolicyService::dump(int fd, const Vector& args __unused) if (!dumpAllowed()) { dumpPermissionDenial(fd); } else { - bool locked = tryLock(mLock); + bool locked = dumpTryLock(mLock); if (!locked) { String8 result(kDeadlockedString); write(fd, result.string(), result.size()); @@ -1260,7 +1253,7 @@ status_t AudioPolicyService::AudioCommandThread::dump(int fd) result.append(buffer); write(fd, result.string(), result.size()); - bool locked = tryLock(mLock); + bool locked = dumpTryLock(mLock); if (!locked) { String8 result2(kCmdDeadlockedString); write(fd, result2.string(), result2.size()); diff --git a/services/soundtrigger/Android.bp b/services/soundtrigger/Android.bp index 1f2283acaa..3f02f48aa5 100644 --- a/services/soundtrigger/Android.bp +++ b/services/soundtrigger/Android.bp @@ -28,6 +28,7 @@ cc_library_shared { "libhardware", "libsoundtrigger", "libaudioclient", + "libaudioutils", "libmediautils", "libhwbinder", diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index fe2ccf24ca..f89683a86c 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -146,20 +147,12 @@ status_t SoundTriggerHwService::setCaptureState(bool active) } -static const int kDumpLockRetries = 50; -static const int kDumpLockSleep = 60000; +static const int kDumpLockTimeoutNs = 1 * NANOS_PER_SECOND; -static bool tryLock(Mutex& mutex) +static bool dumpTryLock(Mutex& mutex) { - bool locked = false; - for (int i = 0; i < kDumpLockRetries; ++i) { - if (mutex.tryLock() == NO_ERROR) { - locked = true; - break; - } - usleep(kDumpLockSleep); - } - return locked; + status_t err = mutex.timedLock(kDumpLockTimeoutNs); + return err == NO_ERROR; } status_t SoundTriggerHwService::dump(int fd, const Vector& args __unused) { @@ -168,7 +161,7 @@ status_t SoundTriggerHwService::dump(int fd, const Vector& args __unus result.appendFormat("Permission Denial: can't dump SoundTriggerHwService"); write(fd, result.string(), result.size()); } else { - bool locked = tryLock(mServiceLock); + bool locked = dumpTryLock(mServiceLock); // failed to lock - SoundTriggerHwService is probably deadlocked if (!locked) { result.append("SoundTriggerHwService may be deadlocked\n"); -- GitLab From a1b56c8df7cde6a294a5a878e276c71995e5e309 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 27 Mar 2019 15:50:39 -0700 Subject: [PATCH 1192/1530] Camera: reduce logspam from abandonded streams 1. Change Camera3Device logs to ALOGV 2. In Camera3OutputStream, only log before we mark stream state to STATE_ABANDONED 3. Also changed BUFFER_ERROR log to ALOGV Test: manually check log of GCA mode switch Bug: 125415787 Change-Id: Ibd83b7010932a8be25d85573d9c9dce9c394f6bb --- .../device3/Camera3Device.cpp | 25 ++++++----- .../device3/Camera3OutputStream.cpp | 44 +++++++++++++------ .../device3/Camera3OutputStream.h | 4 ++ .../libcameraservice/device3/Camera3Stream.h | 2 +- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 22e09e4f16..ef99deac01 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1062,14 +1062,18 @@ hardware::Return Camera3Device::requestStreamBuffers( nsecs_t waitDuration = kBaseGetBufferWait + getExpectedInFlightDuration(); status_t res = outputStream->getBuffer(&sb, waitDuration); if (res != OK) { - ALOGE("%s: Can't get output buffer for stream %d: %s (%d)", - __FUNCTION__, streamId, strerror(-res), res); if (res == NO_INIT || res == DEAD_OBJECT) { + ALOGV("%s: Can't get output buffer for stream %d: %s (%d)", + __FUNCTION__, streamId, strerror(-res), res); bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED); - } else if (res == TIMED_OUT || res == NO_MEMORY) { - bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE); } else { - bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR); + ALOGE("%s: Can't get output buffer for stream %d: %s (%d)", + __FUNCTION__, streamId, strerror(-res), res); + if (res == TIMED_OUT || res == NO_MEMORY) { + bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE); + } else { + bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR); + } } currentReqSucceeds = false; break; @@ -3154,9 +3158,10 @@ void Camera3Device::returnOutputBuffers( // Note: stream may be deallocated at this point, if this buffer was // the last reference to it. - if (res != OK) { - ALOGE("Can't return buffer to its stream: %s (%d)", - strerror(-res), res); + if (res == NO_INIT || res == DEAD_OBJECT) { + ALOGV("Can't return buffer to its stream: %s (%d)", strerror(-res), res); + } else if (res != OK) { + ALOGE("Can't return buffer to its stream: %s (%d)", strerror(-res), res); } // Long processing consumers can cause returnBuffer timeout for shared stream @@ -5580,7 +5585,7 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { if (mUseHalBufManager) { if (outputStream->isAbandoned()) { - ALOGE("%s: stream %d is abandoned.", __FUNCTION__, streamId); + ALOGV("%s: stream %d is abandoned, skipping request", __FUNCTION__, streamId); return TIMED_OUT; } // HAL will request buffer through requestStreamBuffer API @@ -5598,7 +5603,7 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { // Can't get output buffer from gralloc queue - this could be due to // abandoned queue or other consumer misbehavior, so not a fatal // error - ALOGE("RequestThread: Can't get output buffer, skipping request:" + ALOGV("RequestThread: Can't get output buffer, skipping request:" " %s (%d)", strerror(-res), res); return TIMED_OUT; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index baba856d9e..1c7758143e 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -233,6 +233,7 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( * queueBuffer */ sp currentConsumer = mConsumer; + StreamState state = mState; mLock.unlock(); ANativeWindowBuffer *anwBuffer = container_of(buffer.buffer, ANativeWindowBuffer, handle); @@ -244,7 +245,7 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( if (mDropBuffers) { ALOGV("%s: Dropping a frame for stream %d.", __FUNCTION__, mId); } else if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { - ALOGW("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId); + ALOGV("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId); } else { ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId); } @@ -252,7 +253,7 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( res = currentConsumer->cancelBuffer(currentConsumer.get(), anwBuffer, anwReleaseFence); - if (res != OK) { + if (shouldLogError(res, state)) { ALOGE("%s: Stream %d: Error cancelling buffer to native window:" " %s (%d)", __FUNCTION__, mId, strerror(-res), res); } @@ -284,9 +285,9 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( } res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids); - if (res != OK) { - ALOGE("%s: Stream %d: Error queueing buffer to native window: " - "%s (%d)", __FUNCTION__, mId, strerror(-res), res); + if (shouldLogError(res, state)) { + ALOGE("%s: Stream %d: Error queueing buffer to native window:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); } } mLock.lock(); @@ -534,10 +535,11 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i // successful return. *anb = gb.get(); res = mConsumer->attachBuffer(*anb); - if (res != OK) { + if (shouldLogError(res, mState)) { ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)", __FUNCTION__, mId, strerror(-res), res); - + } + if (res != OK) { checkRetAndSetAbandonedLocked(res); return res; } @@ -592,9 +594,10 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i ALOGV("Stream %d: Attached new buffer", getId()); if (res != OK) { - ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - + if (shouldLogError(res, mState)) { + ALOGE("%s: Stream %d: Can't attach the output buffer to this surface:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + } checkRetAndSetAbandonedLocked(res); return res; } @@ -604,9 +607,10 @@ status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, i return res; } } else if (res != OK) { - ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - + if (shouldLogError(res, mState)) { + ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + } checkRetAndSetAbandonedLocked(res); return res; } @@ -639,6 +643,16 @@ void Camera3OutputStream::checkRetAndSetAbandonedLocked(status_t res) { } } +bool Camera3OutputStream::shouldLogError(status_t res, StreamState state) { + if (res == OK) { + return false; + } + if ((res == DEAD_OBJECT || res == NO_INIT) && state == STATE_ABANDONED) { + return false; + } + return true; +} + status_t Camera3OutputStream::disconnectLocked() { status_t res; @@ -838,7 +852,9 @@ status_t Camera3OutputStream::detachBufferLocked(sp* buffer, int* ALOGW("%s: the released buffer has already been freed by the buffer queue!", __FUNCTION__); } else if (res != OK) { // Treat other errors as abandonment - ALOGE("%s: detach next buffer failed: %s (%d).", __FUNCTION__, strerror(-res), res); + if (shouldLogError(res, mState)) { + ALOGE("%s: detach next buffer failed: %s (%d).", __FUNCTION__, strerror(-res), res); + } mState = STATE_ABANDONED; return res; } diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index 30fc2f77d4..729c655d57 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -317,6 +317,10 @@ class Camera3OutputStream : // Check return status of IGBP calls and set abandoned state accordingly void checkRetAndSetAbandonedLocked(status_t res); + // If the status indicates abandonded stream, only log when state hasn't been updated to + // STATE_ABANDONED + static bool shouldLogError(status_t res, StreamState state); + static const int32_t kDequeueLatencyBinSize = 5; // in ms CameraLatencyHistogram mDequeueBufferLatency; diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 5eb6a23c15..3d21029892 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -458,7 +458,7 @@ class Camera3Stream : // Zero for formats with fixed buffer size for given dimensions. const size_t mMaxSize; - enum { + enum StreamState { STATE_ERROR, STATE_CONSTRUCTED, STATE_IN_CONFIG, -- GitLab From d0fa5fb4c84c15e6eb30b11fc4213705b2bfe8a6 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 20 Mar 2019 10:24:55 -0700 Subject: [PATCH 1193/1530] Remove the soft component sources from libstagefright_omx These were left in because some vendor libs link to libstagefright_omx for soft component base. Vendor code now switched to link to libstagefright_omx_soft. bug: 129272021 test: elfin-userdebug builds Change-Id: Iee4f77f98c92f1a21fec0e2c351a7d4be3942d4b --- media/libstagefright/omx/Android.bp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index b8f9aea76f..b959f6cc3e 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -11,13 +11,6 @@ cc_library_shared { "OMXNodeInstance.cpp", "OMXUtils.cpp", "OmxGraphicBufferSource.cpp", - //TODO: remove the soft component code here and use - //libstagefright_omx_soft, once partner build is fixed - "SimpleSoftOMXComponent.cpp", - "SoftOMXComponent.cpp", - "SoftOMXPlugin.cpp", - "SoftVideoDecoderOMXComponent.cpp", - "SoftVideoEncoderOMXComponent.cpp", "1.0/Omx.cpp", "1.0/OmxStore.cpp", "1.0/WGraphicBufferSource.cpp", @@ -58,7 +51,7 @@ cc_library_shared { "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", - //"libstagefright_omx_soft", + "libstagefright_omx_soft", ], export_shared_lib_headers: [ -- GitLab From 19740cae5b12dadd5edb221d2aab678fc88bbaee Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 28 Mar 2019 12:25:01 -0700 Subject: [PATCH 1194/1530] audioflinger: Fix EffectChain::dump Fix missing log line in the case when there are no effects in the chain. Bug: 118842894 Test: adb shell dumpsys media.audio_flinger Change-Id: I31fe4760caa61610de4f143b3a6ae185efb55baa --- services/audioflinger/Effects.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index ecaeb52b37..2b34267bf1 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -2335,13 +2335,10 @@ void AudioFlinger::EffectChain::syncHalEffectsState() void AudioFlinger::EffectChain::dump(int fd, const Vector& args) { - const size_t SIZE = 256; - char buffer[SIZE]; String8 result; - size_t numEffects = mEffects.size(); - snprintf(buffer, SIZE, " %zu effects for session %d\n", numEffects, mSessionId); - result.append(buffer); + const size_t numEffects = mEffects.size(); + result.appendFormat(" %zu effects for session %d\n", numEffects, mSessionId); if (numEffects) { bool locked = AudioFlinger::dumpTryLock(mLock); @@ -2369,6 +2366,8 @@ void AudioFlinger::EffectChain::dump(int fd, const Vector& args) if (locked) { mLock.unlock(); } + } else { + write(fd, result.string(), result.size()); } } -- GitLab From fb7e846fa2b56548f8946fd1ffb71708f9fd0110 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 20 Mar 2019 13:26:49 -0700 Subject: [PATCH 1195/1530] AAudio Fix some documentation @link issues Test: None Change-Id: I21421db8c4be0421fc5ce5f492a85ba84c8a3b3b Signed-off-by: Kevin Rocard --- media/libaaudio/include/aaudio/AAudio.h | 164 +++++++++++++----------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 19e22f194f..1c1df6cad3 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -37,7 +37,7 @@ extern "C" { /** * This is used to represent a value that has not been specified. - * For example, an application could use AAUDIO_UNSPECIFIED to indicate + * For example, an application could use {@link #AAUDIO_UNSPECIFIED} to indicate * that is did not not care what the specific value of a parameter was * and would accept whatever it was given. */ @@ -304,8 +304,8 @@ typedef int32_t aaudio_usage_t; /** * The CONTENT_TYPE attribute describes "what" you are playing. * It expresses the general category of the content. This information is optional. - * But in case it is known (for instance {@link #AAUDIO_CONTENT_TYPE_MOVIE} for a - * movie streaming service or {@link #AAUDIO_CONTENT_TYPE_SPEECH} for + * But in case it is known (for instance AAUDIO_CONTENT_TYPE_MOVIE for a + * movie streaming service or AAUDIO_CONTENT_TYPE_SPEECH for * an audio book application) this information might be used by the audio framework to * enforce audio focus. * @@ -398,12 +398,12 @@ enum { * Indicates that the audio may be captured by any app. * * For privacy, the following usages can not be recorded: AAUDIO_VOICE_COMMUNICATION*, - * AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and AAUDIO_USAGE_ASSISTANT. + * AAUDIO_USAGE_NOTIFICATION*, AAUDIO_USAGE_ASSISTANCE* and {@link #AAUDIO_USAGE_ASSISTANT}. * - * On {@link Android.os.Build.VERSION_CODES.Q}, this means only {@link AAUDIO_USAGE_MEDIA} - * and {@link AAUDIO_USAGE_GAME} may be captured. + * On {@link android.os.Build.VERSION_CODES#Q}, this means only {@link #AAUDIO_USAGE_MEDIA} + * and {@link #AAUDIO_USAGE_GAME} may be captured. * - * See {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL}. + * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_ALL}. */ AAUDIO_ALLOW_CAPTURE_BY_ALL = 1, /** @@ -411,16 +411,16 @@ enum { * * System apps can capture for many purposes like accessibility, user guidance... * but have strong restriction. See - * {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM} for what the system apps + * {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM} for what the system apps * can do with the capture audio. */ AAUDIO_ALLOW_CAPTURE_BY_SYSTEM = 2, /** * Indicates that the audio may not be recorded by any app, even if it is a system app. * - * It is encouraged to use {@link ALLOW_CAPTURE_BY_SYSTEM} instead of this value as system apps + * It is encouraged to use {@link #AAUDIO_ALLOW_CAPTURE_BY_SYSTEM} instead of this value as system apps * provide significant and useful features for the user (eg. accessibility). - * See {@link android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE}. + * See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_NONE}. */ AAUDIO_ALLOW_CAPTURE_BY_NONE = 3, }; @@ -496,8 +496,8 @@ AAUDIO_API const char * AAudio_convertStreamStateToText(aaudio_stream_state_t st * * The deviceId is initially unspecified, meaning that the current default device will be used. * - * The default direction is AAUDIO_DIRECTION_OUTPUT. - * The default sharing mode is AAUDIO_SHARING_MODE_SHARED. + * The default direction is {@link #AAUDIO_DIRECTION_OUTPUT}. + * The default sharing mode is {@link #AAUDIO_SHARING_MODE_SHARED}. * The data format, samplesPerFrames and sampleRate are unspecified and will be * chosen by the device when it is opened. * @@ -522,7 +522,7 @@ AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, /** * Request a sample rate in Hertz. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED. + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}. * An optimal value will then be chosen when the stream is opened. * After opening a stream with an unspecified value, the application must * query for the actual value, which may vary by device. @@ -539,7 +539,7 @@ AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder, /** * Request a number of channels for the stream. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED. + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}. * An optimal value will then be chosen when the stream is opened. * After opening a stream with an unspecified value, the application must * query for the actual value, which may vary by device. @@ -563,9 +563,9 @@ AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* buil int32_t samplesPerFrame) __INTRODUCED_IN(26); /** - * Request a sample data format, for example AAUDIO_FORMAT_PCM_I16. + * Request a sample data format, for example {@link #AAUDIO_FORMAT_PCM_I16}. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED. + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}. * An optimal value will then be chosen when the stream is opened. * After opening a stream with an unspecified value, the application must * query for the actual value, which may vary by device. @@ -574,7 +574,8 @@ AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* buil * If a stream cannot be opened with the specified value then the open will fail. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param format common formats are AAUDIO_FORMAT_PCM_FLOAT and AAUDIO_FORMAT_PCM_I16. + * @param format common formats are {@link #AAUDIO_FORMAT_PCM_FLOAT} and + * {@link #AAUDIO_FORMAT_PCM_I16}. */ AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, aaudio_format_t format) __INTRODUCED_IN(26); @@ -582,13 +583,13 @@ AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, /** * Request a mode for sharing the device. * - * The default, if you do not call this function, is AAUDIO_SHARING_MODE_SHARED. + * The default, if you do not call this function, is {@link #AAUDIO_SHARING_MODE_SHARED}. * * The requested sharing mode may not be available. * The application can query for the actual mode after the stream is opened. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param sharingMode AAUDIO_SHARING_MODE_SHARED or AAUDIO_SHARING_MODE_EXCLUSIVE + * @param sharingMode {@link #AAUDIO_SHARING_MODE_SHARED} or {@link #AAUDIO_SHARING_MODE_EXCLUSIVE} */ AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder, aaudio_sharing_mode_t sharingMode) __INTRODUCED_IN(26); @@ -596,10 +597,10 @@ AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder, /** * Request the direction for a stream. * - * The default, if you do not call this function, is AAUDIO_DIRECTION_OUTPUT. + * The default, if you do not call this function, is {@link #AAUDIO_DIRECTION_OUTPUT}. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param direction AAUDIO_DIRECTION_OUTPUT or AAUDIO_DIRECTION_INPUT + * @param direction {@link #AAUDIO_DIRECTION_OUTPUT} or {@link #AAUDIO_DIRECTION_INPUT} */ AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, aaudio_direction_t direction) __INTRODUCED_IN(26); @@ -608,10 +609,10 @@ AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, * Set the requested buffer capacity in frames. * The final AAudioStream capacity may differ, but will probably be at least this big. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED. + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param numFrames the desired buffer capacity in frames or AAUDIO_UNSPECIFIED + * @param numFrames the desired buffer capacity in frames or {@link #AAUDIO_UNSPECIFIED} */ AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder, int32_t numFrames) __INTRODUCED_IN(26); @@ -619,16 +620,17 @@ AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilde /** * Set the requested performance mode. * - * Supported modes are AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_PERFORMANCE_MODE_POWER_SAVING - * and AAUDIO_PERFORMANCE_MODE_LOW_LATENCY. + * Supported modes are {@link #AAUDIO_PERFORMANCE_MODE_NONE}, + * {@link #AAUDIO_PERFORMANCE_MODE_POWER_SAVING} * and {@link #AAUDIO_PERFORMANCE_MODE_LOW_LATENCY}. * - * The default, if you do not call this function, is AAUDIO_PERFORMANCE_MODE_NONE. + * The default, if you do not call this function, is {@link #AAUDIO_PERFORMANCE_MODE_NONE}. * * You may not get the mode you requested. - * You can call AAudioStream_getPerformanceMode() to find out the final mode for the stream. + * You can call AAudioStream_getPerformanceMode() + * to find out the final mode for the stream. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param mode the desired performance mode, eg. AAUDIO_PERFORMANCE_MODE_LOW_LATENCY + * @param mode the desired performance mode, eg. {@link #AAUDIO_PERFORMANCE_MODE_LOW_LATENCY} */ AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder, aaudio_performance_mode_t mode) __INTRODUCED_IN(26); @@ -640,12 +642,12 @@ AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* buil * behavior of the stream. * This could, for example, affect how volume and focus is handled for the stream. * - * The default, if you do not call this function, is AAUDIO_USAGE_MEDIA. + * The default, if you do not call this function, is {@link #AAUDIO_USAGE_MEDIA}. * * Added in API level 28. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param usage the desired usage, eg. AAUDIO_USAGE_GAME + * @param usage the desired usage, eg. {@link #AAUDIO_USAGE_GAME} */ AAUDIO_API void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, aaudio_usage_t usage) __INTRODUCED_IN(28); @@ -657,12 +659,12 @@ AAUDIO_API void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, * behavior of the stream. * This could, for example, affect whether a stream is paused when a notification occurs. * - * The default, if you do not call this function, is AAUDIO_CONTENT_TYPE_MUSIC. + * The default, if you do not call this function, is {@link #AAUDIO_CONTENT_TYPE_MUSIC}. * * Added in API level 28. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param contentType the type of audio data, eg. AAUDIO_CONTENT_TYPE_SPEECH + * @param contentType the type of audio data, eg. {@link #AAUDIO_CONTENT_TYPE_SPEECH} */ AAUDIO_API void AAudioStreamBuilder_setContentType(AAudioStreamBuilder* builder, aaudio_content_type_t contentType) __INTRODUCED_IN(28); @@ -675,7 +677,7 @@ AAUDIO_API void AAudioStreamBuilder_setContentType(AAudioStreamBuilder* builder, * This could, for example, affect which microphones are used and how the * recorded data is processed. * - * The default, if you do not call this function, is AAUDIO_INPUT_PRESET_VOICE_RECOGNITION. + * The default, if you do not call this function, is {@link #AAUDIO_INPUT_PRESET_VOICE_RECOGNITION}. * That is because VOICE_RECOGNITION is the preset with the lowest latency * on many platforms. * @@ -690,10 +692,10 @@ AAUDIO_API void AAudioStreamBuilder_setInputPreset(AAudioStreamBuilder* builder, /** * Specify whether this stream audio may or may not be captured by other apps or the system. * - * The default is AAUDIO_ALLOW_CAPTURE_BY_ALL. + * The default is {@link #AAUDIO_ALLOW_CAPTURE_BY_ALL}. * * Note that an application can also set its global policy, in which case the most restrictive - * policy is always applied. See {@link android.media.AudioAttributes.setAllowedCapturePolicy} + * policy is always applied. See {@link android.media.AudioAttributes#setAllowedCapturePolicy(int)} * * Added in API level 29. * @@ -708,9 +710,9 @@ AAUDIO_API void AAudioStreamBuilder_setAllowedCapturePolicy(AAudioStreamBuilder* * The session ID can be used to associate a stream with effects processors. * The effects are controlled using the Android AudioEffect Java API. * - * The default, if you do not call this function, is AAUDIO_SESSION_ID_NONE. + * The default, if you do not call this function, is {@link #AAUDIO_SESSION_ID_NONE}. * - * If set to AAUDIO_SESSION_ID_ALLOCATE then a session ID will be allocated + * If set to {@link #AAUDIO_SESSION_ID_ALLOCATE} then a session ID will be allocated * when the stream is opened. * * The allocated session ID can be obtained by calling AAudioStream_getSessionId() @@ -728,7 +730,7 @@ AAUDIO_API void AAudioStreamBuilder_setAllowedCapturePolicy(AAudioStreamBuilder* * Added in API level 28. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param sessionId an allocated sessionID or AAUDIO_SESSION_ID_ALLOCATE + * @param sessionId an allocated sessionID or {@link #AAUDIO_SESSION_ID_ALLOCATE} */ AAUDIO_API void AAudioStreamBuilder_setSessionId(AAudioStreamBuilder* builder, aaudio_session_id_t sessionId) __INTRODUCED_IN(28); @@ -811,15 +813,16 @@ typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)( * * Note that when using this callback, the audio data will be passed in or out * of the function as an argument. - * So you cannot call AAudioStream_write() or AAudioStream_read() on the same stream - * that has an active data callback. + * So you cannot call AAudioStream_write() or AAudioStream_read() + * on the same stream that has an active data callback. * - * The callback function will start being called after AAudioStream_requestStart() is called. + * The callback function will start being called after AAudioStream_requestStart() + * is called. * It will stop being called after AAudioStream_requestPause() or * AAudioStream_requestStop() is called. * * This callback function will be called on a real-time thread owned by AAudio. See - * {@link AAudioStream_dataCallback} for more information. + * {@link #AAudioStream_dataCallback} for more information. * * Note that the AAudio callbacks will never be called simultaneously from multiple threads. * @@ -833,9 +836,9 @@ AAUDIO_API void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder /** * Set the requested data callback buffer size in frames. - * See {@link AAudioStream_dataCallback}. + * See {@link #AAudioStream_dataCallback}. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED. + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}. * * For the lowest possible latency, do not call this function. AAudio will then * call the dataProc callback function with whatever size is optimal. @@ -852,7 +855,7 @@ AAUDIO_API void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder * half the buffer capacity, to allow double buffering. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param numFrames the desired buffer size in frames or AAUDIO_UNSPECIFIED + * @param numFrames the desired buffer size in frames or {@link #AAUDIO_UNSPECIFIED} */ AAUDIO_API void AAudioStreamBuilder_setFramesPerDataCallback(AAudioStreamBuilder* builder, int32_t numFrames) __INTRODUCED_IN(26); @@ -913,12 +916,12 @@ AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builde /** * Open a stream based on the options in the StreamBuilder. * - * AAudioStream_close must be called when finished with the stream to recover + * AAudioStream_close() must be called when finished with the stream to recover * the memory and to free the associated resources. * * @param builder reference provided by AAudio_createStreamBuilder() * @param stream pointer to a variable to receive the new stream reference - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder, AAudioStream** stream) __INTRODUCED_IN(26); @@ -927,7 +930,7 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* * Delete the resources associated with the StreamBuilder. * * @param builder reference provided by AAudio_createStreamBuilder() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder) __INTRODUCED_IN(26); @@ -940,7 +943,7 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* buil * Free the resources associated with a stream created by AAudioStreamBuilder_openStream() * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) __INTRODUCED_IN(26); @@ -948,24 +951,26 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) __INTRODUCE * Asynchronously request to start playing the stream. For output streams, one should * write to the stream to fill the buffer before starting. * Otherwise it will underflow. - * After this call the state will be in AAUDIO_STREAM_STATE_STARTING or AAUDIO_STREAM_STATE_STARTED. + * After this call the state will be in {@link #AAUDIO_STREAM_STATE_STARTING} or + * {@link #AAUDIO_STREAM_STATE_STARTED}. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronous request for the stream to pause. * Pausing a stream will freeze the data flow but not flush any buffers. - * Use AAudioStream_Start() to resume playback after a pause. - * After this call the state will be in AAUDIO_STREAM_STATE_PAUSING or AAUDIO_STREAM_STATE_PAUSED. + * Use AAudioStream_requestStart() to resume playback after a pause. + * After this call the state will be in {@link #AAUDIO_STREAM_STATE_PAUSING} or + * {@link #AAUDIO_STREAM_STATE_PAUSED}. * - * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams. + * This will return {@link #AAUDIO_ERROR_UNIMPLEMENTED} for input streams. * For input streams use AAudioStream_requestStop(). * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream) __INTRODUCED_IN(26); @@ -974,32 +979,34 @@ AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream) __IN * Flushing will discard any pending data. * This call only works if the stream is pausing or paused. TODO review * Frame counters are not reset by a flush. They may be advanced. - * After this call the state will be in AAUDIO_STREAM_STATE_FLUSHING or AAUDIO_STREAM_STATE_FLUSHED. + * After this call the state will be in {@link #AAUDIO_STREAM_STATE_FLUSHING} or + * {@link #AAUDIO_STREAM_STATE_FLUSHED}. * - * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams. + * This will return {@link #AAUDIO_ERROR_UNIMPLEMENTED} for input streams. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream) __INTRODUCED_IN(26); /** * Asynchronous request for the stream to stop. * The stream will stop after all of the data currently buffered has been played. - * After this call the state will be in AAUDIO_STREAM_STATE_STOPPING or AAUDIO_STREAM_STATE_STOPPED. + * After this call the state will be in {@link #AAUDIO_STREAM_STATE_STOPPING} or + * {@link #AAUDIO_STREAM_STATE_STOPPED}. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream) __INTRODUCED_IN(26); /** - * Query the current state of the client, eg. AAUDIO_STREAM_STATE_PAUSING + * Query the current state of the client, eg. {@link #AAUDIO_STREAM_STATE_PAUSING} * * This function will immediately return the state without updating the state. * If you want to update the client state based on the server state then * call AAudioStream_waitForStateChange() with currentState - * set to AAUDIO_STREAM_STATE_UNKNOWN and a zero timeout. + * set to {@link #AAUDIO_STREAM_STATE_UNKNOWN} and a zero timeout. * * @param stream reference provided by AAudioStreamBuilder_openStream() */ @@ -1025,7 +1032,7 @@ AAUDIO_API aaudio_stream_state_t AAudioStream_getState(AAudioStream* stream) __I * @param inputState The state we want to avoid. * @param nextState Pointer to a variable that will be set to the new state. * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. - * @return AAUDIO_OK or a negative error. + * @return {@link #AAUDIO_OK} or a negative error. */ AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, @@ -1142,17 +1149,17 @@ AAUDIO_API int32_t AAudioStream_getBufferCapacityInFrames(AAudioStream* stream) * This call can be used if the application needs to know the value of numFrames before * the stream is started. This is not normally necessary. * - * If a specific size was requested by calling AAudioStreamBuilder_setFramesPerDataCallback() - * then this will be the same size. + * If a specific size was requested by calling + * AAudioStreamBuilder_setFramesPerDataCallback() then this will be the same size. * * If AAudioStreamBuilder_setFramesPerDataCallback() was not called then this will - * return the size chosen by AAudio, or AAUDIO_UNSPECIFIED. + * return the size chosen by AAudio, or {@link #AAUDIO_UNSPECIFIED}. * - * AAUDIO_UNSPECIFIED indicates that the callback buffer size for this stream + * {@link #AAUDIO_UNSPECIFIED} indicates that the callback buffer size for this stream * may vary from one dataProc callback to the next. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return callback buffer size in frames or AAUDIO_UNSPECIFIED + * @return callback buffer size in frames or {@link #AAUDIO_UNSPECIFIED} */ AAUDIO_API int32_t AAudioStream_getFramesPerDataCallback(AAudioStream* stream) __INTRODUCED_IN(26); @@ -1262,21 +1269,22 @@ AAUDIO_API int64_t AAudioStream_getFramesRead(AAudioStream* stream) __INTRODUCED * The session ID can be used to associate a stream with effects processors. * The effects are controlled using the Android AudioEffect Java API. * - * If AAudioStreamBuilder_setSessionId() was called with AAUDIO_SESSION_ID_ALLOCATE + * If AAudioStreamBuilder_setSessionId() was + * called with {@link #AAUDIO_SESSION_ID_ALLOCATE} * then a new session ID should be allocated once when the stream is opened. * * If AAudioStreamBuilder_setSessionId() was called with a previously allocated * session ID then that value should be returned. * * If AAudioStreamBuilder_setSessionId() was not called then this function should - * return AAUDIO_SESSION_ID_NONE. + * return {@link #AAUDIO_SESSION_ID_NONE}. * * The sessionID for a stream should not change once the stream has been opened. * * Added in API level 28. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return session ID or AAUDIO_SESSION_ID_NONE + * @return session ID or {@link #AAUDIO_SESSION_ID_NONE} */ AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream) __INTRODUCED_IN(28); @@ -1285,11 +1293,11 @@ AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream) _ * This can be used to synchronize audio with video or MIDI. * It can also be used to align a recorded stream with a playback stream. * - * Timestamps are only valid when the stream is in AAUDIO_STREAM_STATE_STARTED. - * AAUDIO_ERROR_INVALID_STATE will be returned if the stream is not started. + * Timestamps are only valid when the stream is in {@link #AAUDIO_STREAM_STATE_STARTED}. + * {@link #AAUDIO_ERROR_INVALID_STATE} will be returned if the stream is not started. * Note that because requestStart() is asynchronous, timestamps will not be valid until * a short time after calling requestStart(). - * So AAUDIO_ERROR_INVALID_STATE should not be considered a fatal error. + * So {@link #AAUDIO_ERROR_INVALID_STATE} should not be considered a fatal error. * Just try calling again later. * * If an error occurs, then the position and time will not be modified. @@ -1300,7 +1308,7 @@ AAUDIO_API aaudio_session_id_t AAudioStream_getSessionId(AAudioStream* stream) _ * @param clockid CLOCK_MONOTONIC or CLOCK_BOOTTIME * @param framePosition pointer to a variable to receive the position * @param timeNanoseconds pointer to a variable to receive the time - * @return AAUDIO_OK or a negative error + * @return {@link #AAUDIO_OK} or a negative error */ AAUDIO_API aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds) __INTRODUCED_IN(26); @@ -1321,7 +1329,7 @@ AAUDIO_API aaudio_usage_t AAudioStream_getUsage(AAudioStream* stream) __INTRODUC * Added in API level 28. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return content type, for example AAUDIO_CONTENT_TYPE_MUSIC + * @return content type, for example {@link #AAUDIO_CONTENT_TYPE_MUSIC} */ AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* stream) __INTRODUCED_IN(28); @@ -1332,7 +1340,7 @@ AAUDIO_API aaudio_content_type_t AAudioStream_getContentType(AAudioStream* strea * Added in API level 28. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return input preset, for example AAUDIO_INPUT_PRESET_CAMCORDER + * @return input preset, for example {@link #AAUDIO_INPUT_PRESET_CAMCORDER} */ AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* stream) __INTRODUCED_IN(28); @@ -1344,7 +1352,7 @@ AAUDIO_API aaudio_input_preset_t AAudioStream_getInputPreset(AAudioStream* strea * Added in API level 29. * * @param stream reference provided by AAudioStreamBuilder_openStream() - * @return the allowed capture policy, for example AAUDIO_ALLOW_CAPTURE_BY_ALL + * @return the allowed capture policy, for example {@link #AAUDIO_ALLOW_CAPTURE_BY_ALL} */ AAUDIO_API aaudio_allowed_capture_policy_t AAudioStream_getAllowedCapturePolicy( AAudioStream* stream) __INTRODUCED_IN(29); -- GitLab From a1ca8708dd8a862e43ae4ea9252162940b6dd134 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Wed, 20 Mar 2019 21:08:49 -0700 Subject: [PATCH 1196/1530] MPEG4Writer: Increase duration between out of order frames Bug: 128348228 Increased kMaxCTTSOffetTimeUs value from 1 second to 30 minutes as out of order frames for use cases like LongShot can cause duration between two frames to be more than several seconds. Increased the max duration to 30 minutes as that constant is used only to keep CTTS values positive in CTTS table. Test: cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Change-Id: I294b3086abb711e302a7a2a228da485bf9b90aac --- media/libstagefright/MPEG4Writer.cpp | 8 +------- .../include/media/stagefright/MPEG4Writer.h | 4 ---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a52da450b1..b322670ce9 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -71,6 +71,7 @@ static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; static const int64_t kInitialDelayTimeUs = 700000LL; static const int64_t kMaxMetadataSize = 0x4000000LL; // 64MB max per-frame metadata size +static const int64_t kMaxCttsOffsetTimeUs = 30 * 60 * 1000000LL; // 30 minutes static const char kMetaKey_Version[] = "com.android.version"; static const char kMetaKey_Manufacturer[] = "com.android.manufacturer"; @@ -136,13 +137,6 @@ public: void resetInternal(); private: - enum { - // TODO: need to increase this considering the bug - // about camera app not sending video frames continuously? - kMaxCttsOffsetTimeUs = 1000000LL, // 1 second - kSampleArraySize = 1000, - }; - // A helper class to handle faster write box with table entries template // ENTRY_SIZE: # of values in each entry diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h index 803155df12..6f190230c1 100644 --- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h +++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h @@ -82,10 +82,6 @@ private: kWhatSwitch = 'swch', }; - enum { - kMaxCttsOffsetTimeUs = 1000000LL, // 1 second - }; - int mFd; int mNextFd; sp mStartMeta; -- GitLab From 736a102604f2fed737e737552f6ff461cd326081 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 27 Mar 2019 18:28:46 -0700 Subject: [PATCH 1197/1530] audio policy: fix configurable engine and dynamic policies Make sure the Engine is never aware of remote submix devices with non "0" (legacy) addresses as the Engine does not have to deal with devices only used by dynamic audio policies. Bug: 129144725 Test: make Change-Id: Ifa19c952e22ac18c5d47e731cbfa41f81fe5c32c --- .../src/DeviceDescriptor.cpp | 2 +- .../managerdefault/AudioPolicyManager.cpp | 31 ++++++++++++------- .../managerdefault/AudioPolicyManager.h | 4 +++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 91961d0b25..1a74f4814b 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -40,7 +40,7 @@ DeviceDescriptor::DeviceDescriptor(audio_devices_t type, const FormatVector &enc mTagName(tagName), mDeviceType(type), mEncodedFormats(encodedFormats) { mCurrentEncodedFormat = AUDIO_FORMAT_DEFAULT; - if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { + if (audio_is_remote_submix_device(type)) { mAddress = String8("0"); } /* If framework runs against a pre 5.0 Audio HAL, encoded formats are absent from the config. diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 762a4b1651..e5721ee209 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -169,8 +169,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); return INVALID_OPERATION; } - // Propagate device availability to Engine - mEngine->setDeviceConnectionState(device, state); // outputs should never be empty here ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():" @@ -200,8 +198,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // Reset active device codec device->setEncodedFormat(AUDIO_FORMAT_DEFAULT); - // Propagate device availability to Engine - mEngine->setDeviceConnectionState(device, state); } break; default: @@ -209,6 +205,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return BAD_VALUE; } + // Propagate device availability to Engine + setEngineDeviceConnectionState(device, state); + // No need to evaluate playback routing when connecting a remote submix // output device used by a dynamic policy of type recorder as no // playback use case is affected. @@ -318,9 +317,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT if (mAvailableInputDevices.add(device) < 0) { return NO_MEMORY; } - - // Propagate device availability to Engine - mEngine->setDeviceConnectionState(device, state); } break; // handle input device disconnection @@ -337,9 +333,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT checkInputsForDevice(device, state, inputs); mAvailableInputDevices.remove(device); - - // Propagate device availability to Engine - mEngine->setDeviceConnectionState(device, state); } break; default: @@ -347,6 +340,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return BAD_VALUE; } + // Propagate device availability to Engine + setEngineDeviceConnectionState(device, state); + closeAllInputs(); // As the input device list can impact the output device selection, update // getDeviceForStrategy() cache @@ -369,6 +365,17 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return BAD_VALUE; } +void AudioPolicyManager::setEngineDeviceConnectionState(const sp device, + audio_policy_dev_state_t state) { + + // the Engine does not have to know about remote submix devices used by dynamic audio policies + if (audio_is_remote_submix_device(device->type()) && device->address() != "0") { + return; + } + mEngine->setDeviceConnectionState(device, state); +} + + audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devices_t device, const char *device_address) { @@ -4385,7 +4392,7 @@ status_t AudioPolicyManager::initialize() { continue; } // Device is now validated and can be appended to the available devices of the engine - mEngine->setDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE); + setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE); i++; } }; @@ -4399,7 +4406,7 @@ status_t AudioPolicyManager::initialize() { status = NO_INIT; } // If microphones address is empty, set it according to device type - for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { + for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { if (mAvailableInputDevices[i]->address().isEmpty()) { if (mAvailableInputDevices[i]->type() == AUDIO_DEVICE_IN_BUILTIN_MIC) { mAvailableInputDevices[i]->setAddress(String8(AUDIO_BOTTOM_MICROPHONE_ADDRESS)); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 1c9868419e..32eeedc489 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -844,6 +844,10 @@ private: const char *device_address, const char *device_name, audio_format_t encodedFormat); + + void setEngineDeviceConnectionState(const sp device, + audio_policy_dev_state_t state); + void updateMono(audio_io_handle_t output) { AudioParameter param; param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono); -- GitLab From 73d25278987accf34b9edddbe7d08dca8bdccb7c Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Thu, 28 Mar 2019 15:50:42 -0700 Subject: [PATCH 1198/1530] camera2 vndk: Initialize uninitialized members of OutputConfiguration in wrapper constructor. Test: AImageReaderVendorTest Change-Id: Ia612e68680c307c8a1b635893173a2fcd0f3d213 Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 1 - camera/ndk/ndk_vendor/impl/utils.h | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index d7d774b727..6f92a38797 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -299,7 +299,6 @@ camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOut OutputConfigurationWrapper outConfigW; OutputConfiguration &outConfig = outConfigW.mOutputConfiguration; outConfig.rotation = utils::convertToHidl(output->mRotation); - outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing. outConfig.windowHandles.resize(output->mSharedWindows.size() + 1); outConfig.windowHandles[0] = output->mWindow; outConfig.physicalCameraId = output->mPhysicalCameraId; diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h index 2f1006d0eb..a03c7bc5b5 100644 --- a/camera/ndk/ndk_vendor/impl/utils.h +++ b/camera/ndk/ndk_vendor/impl/utils.h @@ -99,7 +99,15 @@ struct OutputConfigurationWrapper { return mOutputConfiguration; } - OutputConfigurationWrapper() = default; + OutputConfigurationWrapper() { + mOutputConfiguration.rotation = OutputConfiguration::Rotation::R0; + // The ndk currently doesn't support deferred surfaces + mOutputConfiguration.isDeferred = false; + mOutputConfiguration.width = 0; + mOutputConfiguration.height = 0; + // ndk doesn't support inter OutputConfiguration buffer sharing. + mOutputConfiguration.windowGroupId = -1; + }; OutputConfigurationWrapper(OutputConfiguration &outputConfiguration) : mOutputConfiguration((outputConfiguration)) { } -- GitLab From 11966872eedd949041dfe0a905794654a3df49cf Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Fri, 22 Feb 2019 17:54:19 -0800 Subject: [PATCH 1199/1530] Remove subdirs from Android.bp subdirs is meaningless now. Test: Builds Bug: 129513796 Change-Id: I60130e38b5924b73c5520d884868dd580f5345bf --- Android.bp | 7 ------- camera/Android.bp | 2 -- drm/libdrmframework/Android.bp | 1 - drm/libdrmframework/plugins/common/Android.bp | 1 - .../plugins/forward-lock/Android.bp | 4 ---- .../forward-lock/internal-format/Android.bp | 5 ----- drm/mediadrm/Android.bp | 1 - .../plugins/clearkey/default/Android.bp | 4 ---- media/extractors/Android.bp | 3 --- media/libaaudio/Android.bp | 1 - media/libaaudio/examples/Android.bp | 2 -- media/libmediaplayerservice/Android.bp | 2 -- media/libstagefright/Android.bp | 18 ------------------ media/libstagefright/codecs/amrnb/Android.bp | 1 - .../libstagefright/codecs/amrwbenc/Android.bp | 3 --- media/libstagefright/codecs/flac/Android.bp | 1 - media/libstagefright/codecs/g711/Android.bp | 1 - media/libstagefright/codecs/gsm/Android.bp | 1 - .../libstagefright/codecs/m4v_h263/Android.bp | 1 - media/libstagefright/codecs/on2/Android.bp | 1 - media/libstagefright/codecs/opus/Android.bp | 1 - media/libstagefright/codecs/vorbis/Android.bp | 1 - media/libstagefright/omx/Android.bp | 3 --- media/mtp/Android.bp | 3 --- 24 files changed, 68 deletions(-) delete mode 100644 Android.bp delete mode 100644 drm/libdrmframework/plugins/common/Android.bp delete mode 100644 drm/libdrmframework/plugins/forward-lock/Android.bp delete mode 100644 drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp delete mode 100644 drm/mediadrm/Android.bp delete mode 100644 media/extractors/Android.bp delete mode 100644 media/libstagefright/codecs/amrnb/Android.bp delete mode 100644 media/libstagefright/codecs/flac/Android.bp delete mode 100644 media/libstagefright/codecs/g711/Android.bp delete mode 100644 media/libstagefright/codecs/gsm/Android.bp delete mode 100644 media/libstagefright/codecs/m4v_h263/Android.bp delete mode 100644 media/libstagefright/codecs/on2/Android.bp delete mode 100644 media/libstagefright/codecs/opus/Android.bp delete mode 100644 media/libstagefright/codecs/vorbis/Android.bp diff --git a/Android.bp b/Android.bp deleted file mode 100644 index e4f12c8460..0000000000 --- a/Android.bp +++ /dev/null @@ -1,7 +0,0 @@ -subdirs = [ - "camera", - "drm/*", - "media/*", - "services/*", - "soundtrigger", -] diff --git a/camera/Android.bp b/camera/Android.bp index 21588d48d6..2800595f4a 100644 --- a/camera/Android.bp +++ b/camera/Android.bp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -subdirs = ["ndk"] - cc_library_shared { name: "libcamera_client", diff --git a/drm/libdrmframework/Android.bp b/drm/libdrmframework/Android.bp index 43ba72bcb3..940c17d2b8 100644 --- a/drm/libdrmframework/Android.bp +++ b/drm/libdrmframework/Android.bp @@ -39,4 +39,3 @@ cc_library_shared { cflags: ["-Werror"], } -subdirs = ["plugins/*"] diff --git a/drm/libdrmframework/plugins/common/Android.bp b/drm/libdrmframework/plugins/common/Android.bp deleted file mode 100644 index 213e57f859..0000000000 --- a/drm/libdrmframework/plugins/common/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["util"] diff --git a/drm/libdrmframework/plugins/forward-lock/Android.bp b/drm/libdrmframework/plugins/forward-lock/Android.bp deleted file mode 100644 index f884c14986..0000000000 --- a/drm/libdrmframework/plugins/forward-lock/Android.bp +++ /dev/null @@ -1,4 +0,0 @@ -subdirs = [ - "FwdLockEngine", - "internal-format", -] diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp deleted file mode 100644 index 9f58e264fd..0000000000 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp +++ /dev/null @@ -1,5 +0,0 @@ -subdirs = [ - "common", - "converter", - "decoder", -] diff --git a/drm/mediadrm/Android.bp b/drm/mediadrm/Android.bp deleted file mode 100644 index b9f07f1ecd..0000000000 --- a/drm/mediadrm/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["plugins/*"] diff --git a/drm/mediadrm/plugins/clearkey/default/Android.bp b/drm/mediadrm/plugins/clearkey/default/Android.bp index 7ba57088cc..9803d32d96 100644 --- a/drm/mediadrm/plugins/clearkey/default/Android.bp +++ b/drm/mediadrm/plugins/clearkey/default/Android.bp @@ -61,7 +61,3 @@ cc_library_shared { }, } -//######################################################################## -// Build unit tests - -subdirs = ["tests"] diff --git a/media/extractors/Android.bp b/media/extractors/Android.bp deleted file mode 100644 index e8176cf6b6..0000000000 --- a/media/extractors/Android.bp +++ /dev/null @@ -1,3 +0,0 @@ -subdirs = [ - "*", -] diff --git a/media/libaaudio/Android.bp b/media/libaaudio/Android.bp index 4857008028..16958f95af 100644 --- a/media/libaaudio/Android.bp +++ b/media/libaaudio/Android.bp @@ -35,4 +35,3 @@ cc_library_headers { version_script: "libaaudio.map.txt", } -subdirs = ["*"] diff --git a/media/libaaudio/examples/Android.bp b/media/libaaudio/examples/Android.bp index 639fab2acc..49bd5eef22 100644 --- a/media/libaaudio/examples/Android.bp +++ b/media/libaaudio/examples/Android.bp @@ -1,5 +1,3 @@ -subdirs = ["*"] - cc_library_headers { name: "libaaudio_example_utils", export_include_dirs: ["utils"], diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp index 46a1c24b73..007f83646c 100644 --- a/media/libmediaplayerservice/Android.bp +++ b/media/libmediaplayerservice/Android.bp @@ -70,5 +70,3 @@ cc_library_shared { } -subdirs = ["*"] - diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index da35889911..73349d9342 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -319,21 +319,3 @@ cc_library_static { }, } -subdirs = [ - "codec2", - "codecs/*", - "colorconversion", - "filters", - "flac/dec", - "foundation", - "http", - "httplive", - "id3", - "mpeg2ts", - "omx", - "rtsp", - "tests", - "timedtext", - "webm", - "xmlparser", -] diff --git a/media/libstagefright/codecs/amrnb/Android.bp b/media/libstagefright/codecs/amrnb/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/amrnb/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index 8327500b66..262962fe0b 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -175,6 +175,3 @@ cc_library_shared { compile_multilib: "32", } -//############################################################################### - -subdirs = ["SampleCode"] diff --git a/media/libstagefright/codecs/flac/Android.bp b/media/libstagefright/codecs/flac/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/flac/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/g711/Android.bp b/media/libstagefright/codecs/g711/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/g711/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/gsm/Android.bp b/media/libstagefright/codecs/gsm/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/gsm/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/m4v_h263/Android.bp b/media/libstagefright/codecs/m4v_h263/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/m4v_h263/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/on2/Android.bp b/media/libstagefright/codecs/on2/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/on2/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/opus/Android.bp b/media/libstagefright/codecs/opus/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/opus/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/codecs/vorbis/Android.bp b/media/libstagefright/codecs/vorbis/Android.bp deleted file mode 100644 index b44c296012..0000000000 --- a/media/libstagefright/codecs/vorbis/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index b959f6cc3e..c06aca551f 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -170,6 +170,3 @@ cc_library_shared { cflags: ["-Wall", "-Werror"], } -//############################################################################### - -subdirs = ["tests"] diff --git a/media/mtp/Android.bp b/media/mtp/Android.bp index 2cf9b8290d..66a31392fd 100644 --- a/media/mtp/Android.bp +++ b/media/mtp/Android.bp @@ -54,6 +54,3 @@ cc_library_shared { ], } -subdirs = [ - "tests", -] -- GitLab From aba3b4b493fe84cd95780971f9a4ba2e22095264 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 27 Mar 2019 12:53:29 -0700 Subject: [PATCH 1200/1530] audiopolicy: Fix dump for AudioPolicyMix criterias The dump code was not using AudioMixMatchCriterion class fields correctly. Bug: 112151391 Test: 1. run and pause AudioPolicyTest#testMixByUidCapturing 2. adb shell dumpsys media.audio_policy Change-Id: I5f74a0e6dc1a7039eab1470402179b7f9d1cd962 --- .../managerdefinitions/src/AudioPolicyMix.cpp | 43 +++++++++---------- .../managerdefinitions/src/TypeConverter.cpp | 1 - 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index f7289ca112..91f72862e8 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -47,32 +47,29 @@ void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const int indexCriterion = 0; for (const auto &criterion : mCriteria) { - dst->appendFormat("%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++); - - std::string usageLiteral; - if (!UsageTypeConverter::toString(criterion.mValue.mUsage, usageLiteral)) { - ALOGE("%s: failed to convert usage %d", __FUNCTION__, criterion.mValue.mUsage); - return; - } - dst->appendFormat("%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str()); - - if (mMixType == MIX_TYPE_RECORDERS) { - std::string sourceLiteral; - if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) { - ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource); - return; - } - dst->appendFormat("%*s- Source:%s\n", spaces + 4, "", sourceLiteral.c_str()); - + dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++); + + std::string ruleType, ruleValue; + bool unknownRule = !RuleTypeConverter::toString(criterion.mRule, ruleType); + switch (criterion.mRule & ~RULE_EXCLUSION_MASK) { // no need to match RULE_EXCLUDE_... + case RULE_MATCH_ATTRIBUTE_USAGE: + UsageTypeConverter::toString(criterion.mValue.mUsage, ruleValue); + break; + case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: + SourceTypeConverter::toString(criterion.mValue.mSource, ruleValue); + break; + case RULE_MATCH_UID: + ruleValue = std::to_string(criterion.mValue.mUid); + break; + default: + unknownRule = true; } - dst->appendFormat("%*s- Uid:%d\n", spaces + 4, "", criterion.mValue.mUid); - std::string ruleLiteral; - if (!RuleTypeConverter::toString(criterion.mRule, ruleLiteral)) { - ALOGE("%s: failed to convert source %d", __FUNCTION__,criterion.mRule); - return; + if (!unknownRule) { + dst->appendFormat("%s %s\n", ruleType.c_str(), ruleValue.c_str()); + } else { + dst->appendFormat("Unknown rule type value 0x%x\n", criterion.mRule); } - dst->appendFormat("%*s- Rule:%s\n", spaces + 4, "", ruleLiteral.c_str()); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp index 7c76d8a35b..2b5455e8c6 100644 --- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp @@ -52,7 +52,6 @@ const RouteFlagTypeConverter::Table RouteFlagTypeConverter::mTable[] = { template <> const RuleTypeConverter::Table RuleTypeConverter::mTable[] = { - MAKE_STRING_FROM_ENUM(RULE_EXCLUSION_MASK), MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_USAGE), MAKE_STRING_FROM_ENUM(RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET), MAKE_STRING_FROM_ENUM(RULE_MATCH_UID), -- GitLab From 01dc5ca26ce985d441e1e5e7763e1e3e93633b36 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 29 Mar 2019 10:12:12 -0700 Subject: [PATCH 1201/1530] audioflinger: Fix thread fields locking for dumps Previously, ThreadBase::mLock was only acquired inside ThreadBase::dumpBase method. That means, dumping of fields of descendant classes, tracks, and effect chains was performed without holding ThreadBase::mLock. This patch changes the way of how dumping is driven. Now only ThreadBase has a public 'dump' method which is non-virtual. This method takes the lock and dumps all the fields, tracks, and effect chains. It calls virtual methods for dumping the contents of descendant classes. Bug: 118842894 Test: compare audioflinger dumps A/B Change-Id: Iaafc75d13935a6a92ca37f9567b7ac7c31374b3e --- services/audioflinger/Threads.cpp | 119 ++++++++++++------------------ services/audioflinger/Threads.h | 50 +++++++------ 2 files changed, 73 insertions(+), 96 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 984d9fee95..b51c570eb8 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -786,12 +786,8 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { } } -void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __unused) +void AudioFlinger::ThreadBase::dump(int fd, const Vector& args) { - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - dprintf(fd, "\n%s thread %p, name %s, tid %d, type %d (%s):\n", isOutput() ? "Output" : "Input", this, mThreadName, getTid(), type(), threadTypeToString(type())); @@ -800,6 +796,21 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u dprintf(fd, " Thread may be deadlocked\n"); } + dumpBase_l(fd, args); + dumpInternals_l(fd, args); + dumpTracks_l(fd, args); + dumpEffectChains_l(fd, args); + + if (locked) { + mLock.unlock(); + } + + dprintf(fd, " Local log:\n"); + mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); +} + +void AudioFlinger::ThreadBase::dumpBase_l(int fd, const Vector& args __unused) +{ dprintf(fd, " I/O handle: %d\n", mId); dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); dprintf(fd, " Sample rate: %u Hz\n", mSampleRate); @@ -814,6 +825,8 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { + const size_t SIZE = 256; + char buffer[SIZE]; for (size_t i = 0; i < numConfig; i++) { mConfigEvents[i]->dump(buffer, SIZE); dprintf(fd, "\n %s", buffer); @@ -858,17 +871,12 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u isOutput() ? "write" : "read", mLatencyMs.toString().c_str()); } - - if (locked) { - mLock.unlock(); - } } -void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector& args) +void AudioFlinger::ThreadBase::dumpEffectChains_l(int fd, const Vector& args) { const size_t SIZE = 256; char buffer[SIZE]; - String8 result; size_t numEffectChains = mEffectChains.size(); snprintf(buffer, SIZE, " %zu Effect Chains\n", numEffectChains); @@ -1819,16 +1827,24 @@ AudioFlinger::PlaybackThread::~PlaybackThread() free(mEffectBuffer); } -void AudioFlinger::PlaybackThread::dump(int fd, const Vector& args) +// Thread virtuals + +void AudioFlinger::PlaybackThread::onFirstRef() { - dumpInternals(fd, args); - dumpTracks(fd, args); - dumpEffectChains(fd, args); - dprintf(fd, " Local log:\n"); - mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); + run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO); } -void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& args __unused) +// ThreadBase virtuals +void AudioFlinger::PlaybackThread::preExit() +{ + ALOGV(" preExit()"); + // FIXME this is using hard-coded strings but in the future, this functionality will be + // converted to use audio HAL extensions required to support tunneling + status_t result = mOutput->stream->setParameters(String8("exiting=1")); + ALOGE_IF(result != OK, "Error when setting parameters on exit: %d", result); +} + +void AudioFlinger::PlaybackThread::dumpTracks_l(int fd, const Vector& args __unused) { String8 result; @@ -1893,10 +1909,8 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector& ar write(fd, result.string(), result.size()); } -void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::PlaybackThread::dumpInternals_l(int fd, const Vector& args __unused) { - dumpBase(fd, args); - dprintf(fd, " Master mute: %s\n", mMasterMute ? "on" : "off"); if (mHapticChannelMask != AUDIO_CHANNEL_NONE) { dprintf(fd, " Haptic channel mask: %#x (%s)\n", mHapticChannelMask, @@ -1927,23 +1941,6 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector& } } -// Thread virtuals - -void AudioFlinger::PlaybackThread::onFirstRef() -{ - run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO); -} - -// ThreadBase virtuals -void AudioFlinger::PlaybackThread::preExit() -{ - ALOGV(" preExit()"); - // FIXME this is using hard-coded strings but in the future, this functionality will be - // converted to use audio HAL extensions required to support tunneling - status_t result = mOutput->stream->setParameters(String8("exiting=1")); - ALOGE_IF(result != OK, "Error when setting parameters on exit: %d", result); -} - // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held sp AudioFlinger::PlaybackThread::createTrack_l( const sp& client, @@ -5350,9 +5347,9 @@ bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePa } -void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::MixerThread::dumpInternals_l(int fd, const Vector& args) { - PlaybackThread::dumpInternals(fd, args); + PlaybackThread::dumpInternals_l(fd, args); dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs); dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); @@ -5426,9 +5423,9 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread() { } -void AudioFlinger::DirectOutputThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::DirectOutputThread::dumpInternals_l(int fd, const Vector& args) { - PlaybackThread::dumpInternals(fd, args); + PlaybackThread::dumpInternals_l(fd, args); dprintf(fd, " Master balance: %f Left: %f Right: %f\n", mMasterBalance.load(), mMasterBalanceLeft, mMasterBalanceRight); } @@ -6443,9 +6440,9 @@ void AudioFlinger::DuplicatingThread::threadLoop_standby() } } -void AudioFlinger::DuplicatingThread::dumpInternals(int fd, const Vector& args __unused) +void AudioFlinger::DuplicatingThread::dumpInternals_l(int fd, const Vector& args __unused) { - MixerThread::dumpInternals(fd, args); + MixerThread::dumpInternals_l(fd, args); std::stringstream ss; const size_t numTracks = mOutputTracks.size(); @@ -7775,19 +7772,8 @@ void AudioFlinger::RecordThread::removeTrack_l(const sp& track) } } -void AudioFlinger::RecordThread::dump(int fd, const Vector& args) -{ - dumpInternals(fd, args); - dumpTracks(fd, args); - dumpEffectChains(fd, args); - dprintf(fd, " Local log:\n"); - mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); -} - -void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::RecordThread::dumpInternals_l(int fd, const Vector& args __unused) { - dumpBase(fd, args); - AudioStreamIn *input = mInput; audio_input_flags_t flags = input != NULL ? input->flags : AUDIO_INPUT_FLAG_NONE; dprintf(fd, " AudioStreamIn: %p flags %#x (%s)\n", @@ -7814,7 +7800,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector& a copy->dump(fd); } -void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector& args __unused) +void AudioFlinger::RecordThread::dumpTracks_l(int fd, const Vector& args __unused) { String8 result; size_t numtracks = mTracks.size(); @@ -9079,19 +9065,8 @@ void AudioFlinger::MmapThread::checkInvalidTracks_l() } } -void AudioFlinger::MmapThread::dump(int fd, const Vector& args) -{ - dumpInternals(fd, args); - dumpTracks(fd, args); - dumpEffectChains(fd, args); - dprintf(fd, " Local log:\n"); - mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */); -} - -void AudioFlinger::MmapThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::MmapThread::dumpInternals_l(int fd, const Vector& args __unused) { - dumpBase(fd, args); - dprintf(fd, " Attributes: content type %d usage %d source %d\n", mAttr.content_type, mAttr.usage, mAttr.source); dprintf(fd, " Session: %d port Id: %d\n", mSessionId, mPortId); @@ -9100,7 +9075,7 @@ void AudioFlinger::MmapThread::dumpInternals(int fd, const Vector& arg } } -void AudioFlinger::MmapThread::dumpTracks(int fd, const Vector& args __unused) +void AudioFlinger::MmapThread::dumpTracks_l(int fd, const Vector& args __unused) { String8 result; size_t numtracks = mActiveTracks.size(); @@ -9323,9 +9298,9 @@ void AudioFlinger::MmapPlaybackThread::toAudioPortConfig(struct audio_port_confi } } -void AudioFlinger::MmapPlaybackThread::dumpInternals(int fd, const Vector& args) +void AudioFlinger::MmapPlaybackThread::dumpInternals_l(int fd, const Vector& args) { - MmapThread::dumpInternals(fd, args); + MmapThread::dumpInternals_l(fd, args); dprintf(fd, " Stream type: %d Stream volume: %f HAL volume: %f Stream mute %d\n", mStreamType, mStreamVolume, mHalVolFloat, mStreamMute); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index e5abce7793..18cb361d3b 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -43,9 +43,6 @@ public: virtual status_t readyToRun(); - void dumpBase(int fd, const Vector& args); - void dumpEffectChains(int fd, const Vector& args); - void clearPowerManager(); // base for record and playback @@ -418,7 +415,7 @@ public: bool isMsdDevice() const { return mIsMsdDevice; } - virtual void dump(int fd, const Vector& args) = 0; + void dump(int fd, const Vector& args); // deliver stats to mediametrics. void sendStatistics(bool force); @@ -470,6 +467,11 @@ protected: return INVALID_OPERATION; } + virtual void dumpInternals_l(int fd __unused, const Vector& args __unused) + { } + virtual void dumpTracks_l(int fd __unused, const Vector& args __unused) { } + + friend class AudioFlinger; // for mEffectChains const type_t mType; @@ -657,6 +659,10 @@ protected: }; SimpleLog mLocalLog; + +private: + void dumpBase_l(int fd, const Vector& args); + void dumpEffectChains_l(int fd, const Vector& args); }; class VolumeInterface { @@ -709,8 +715,6 @@ public: audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady); virtual ~PlaybackThread(); - void dump(int fd, const Vector& args) override; - // Thread virtuals virtual bool threadLoop(); @@ -760,6 +764,9 @@ protected: mActiveTracks.updatePowerState(this, true /* force */); } + void dumpInternals_l(int fd, const Vector& args) override; + void dumpTracks_l(int fd, const Vector& args) override; + public: virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; } @@ -1009,9 +1016,6 @@ private: void updateMetadata_l() final; virtual void sendMetadataToBackend_l(const StreamOutHalInterface::SourceMetadata& metadata); - virtual void dumpInternals(int fd, const Vector& args); - void dumpTracks(int fd, const Vector& args); - // The Tracks class manages tracks added and removed from the Thread. template class Tracks { @@ -1166,7 +1170,6 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); - virtual void dumpInternals(int fd, const Vector& args); virtual bool isTrackAllowed_l( audio_channel_mask_t channelMask, audio_format_t format, @@ -1185,6 +1188,8 @@ protected: } } + void dumpInternals_l(int fd, const Vector& args) override; + // threadLoop snippets virtual ssize_t threadLoop_write(); virtual void threadLoop_standby(); @@ -1266,8 +1271,6 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); - void dumpInternals(int fd, const Vector& args) override; - virtual void flushHw_l(); void setMasterBalance(float balance) override; @@ -1278,6 +1281,8 @@ protected: virtual uint32_t suspendSleepTimeUs() const; virtual void cacheParameters_l(); + void dumpInternals_l(int fd, const Vector& args) override; + // threadLoop snippets virtual mixer_state prepareTracks_l(Vector< sp > *tracksToRemove); virtual void threadLoop_mix(); @@ -1397,8 +1402,6 @@ public: virtual ~DuplicatingThread(); // Thread virtuals - virtual void dumpInternals(int fd, const Vector& args) override; - void addOutputTrack(MixerThread* thread); void removeOutputTrack(MixerThread* thread); uint32_t waitTimeMs() const { return mWaitTimeMs; } @@ -1407,6 +1410,7 @@ public: const StreamOutHalInterface::SourceMetadata& metadata) override; protected: virtual uint32_t activeSleepTimeUs() const; + void dumpInternals_l(int fd, const Vector& args) override; private: bool outputsReady(const SortedVector< sp > &outputTracks); @@ -1512,9 +1516,6 @@ public: void destroyTrack_l(const sp& track); void removeTrack_l(const sp& track); - void dumpInternals(int fd, const Vector& args); - void dumpTracks(int fd, const Vector& args); - // Thread virtuals virtual bool threadLoop(); virtual void preExit(); @@ -1551,7 +1552,6 @@ public: // return true if the caller should then do it's part of the stopping process bool stop(RecordTrack* recordTrack); - void dump(int fd, const Vector& args) override; AudioStreamIn* clearInput(); virtual sp stream() const; @@ -1619,6 +1619,11 @@ public: return audio_is_input_device( mInDevice & mTimestampCorrectedDevices); } + +protected: + void dumpInternals_l(int fd, const Vector& args) override; + void dumpTracks_l(int fd, const Vector& args) override; + private: // Enter standby if not already in standby, and set mStandby flag void standbyIfNotAlreadyInStandby(); @@ -1768,11 +1773,9 @@ class MmapThread : public ThreadBase // Sets the UID records silence virtual void setRecordSilenced(uid_t uid __unused, bool silenced __unused) {} - void dump(int fd, const Vector& args) override; - virtual void dumpInternals(int fd, const Vector& args); - void dumpTracks(int fd, const Vector& args); - protected: + void dumpInternals_l(int fd, const Vector& args) override; + void dumpTracks_l(int fd, const Vector& args) override; audio_attributes_t mAttr; audio_session_t mSessionId; @@ -1822,8 +1825,6 @@ public: virtual void checkSilentMode_l(); void processVolume_l() override; - virtual void dumpInternals(int fd, const Vector& args); - virtual bool isOutput() const override { return true; } void updateMetadata_l() override; @@ -1831,6 +1832,7 @@ public: virtual void toAudioPortConfig(struct audio_port_config *config); protected: + void dumpInternals_l(int fd, const Vector& args) override; audio_stream_type_t mStreamType; float mMasterVolume; -- GitLab From 41852688c913af0961a72bc5e2c2155404ae2038 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 29 Mar 2019 10:58:15 -0700 Subject: [PATCH 1202/1530] aaudio: set buffer size to at least one burst Clip setBufferSizeInFrames() to at least one burst for Legacy streams to avoid underruns. Bug: 129545119 Test: Look at BufferSize when you run this. Test: adb shell write_sine_callback -pn -n0 Test: It should not glitch. Change-Id: Id82862d59122ba2d9dd98dff579b5c147e40fab5 --- media/libaaudio/src/legacy/AudioStreamTrack.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index ff95aedcb6..16b10bc35e 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -420,6 +420,10 @@ aaudio_result_t AudioStreamTrack::write(const void *buffer, aaudio_result_t AudioStreamTrack::setBufferSize(int32_t requestedFrames) { + // Do not ask for less than one burst. + if (requestedFrames < getFramesPerBurst()) { + requestedFrames = getFramesPerBurst(); + } ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames); if (result < 0) { return AAudioConvert_androidToAAudioResult(result); -- GitLab From 2d07958891af80d65029b4298a7843ffa6a844ee Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Sat, 23 Mar 2019 14:31:23 -0700 Subject: [PATCH 1203/1530] MPEG4Writer: Better logging in DumpTimeStamps() Current Log messages when recording stops before receiving first frame. E MPEG4Writer: Dumping Video track's last 10 frames timestamp and frame type E MPEG4Writer: E MPEG4Writer: That is split between two prints. After this change, it would be a one print statement with all data. E/MPEG4Writer: Dumping Video track's last 10 frames' timestamps(pts, dts) and frame type : (369851us, 369851us Non-Key frame) (403171us, 403171us Non-Key frame) E/MPEG4Writer: 0 frames to dump in Audio track Bug: 129554199 Test: Checked that expected log messages were present when recording as mentioned in the description above for both cases. Change-Id: If6c3b96f30b6ebe9e0cca479c7258b5fe3c5dec8 --- media/libstagefright/MPEG4Writer.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a52da450b1..6b5fd87e9d 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -2920,14 +2920,18 @@ void MPEG4Writer::Track::updateDriftTime(const sp& meta) { } void MPEG4Writer::Track::dumpTimeStamps() { - ALOGE("Dumping %s track's last 10 frames timestamp and frame type ", getTrackType()); - std::string timeStampString; - for (std::list::iterator entry = mTimestampDebugHelper.begin(); - entry != mTimestampDebugHelper.end(); ++entry) { - timeStampString += "(" + std::to_string(entry->pts)+ - "us, " + std::to_string(entry->dts) + "us " + entry->frameType + ") "; - } - ALOGE("%s", timeStampString.c_str()); + if (!mTimestampDebugHelper.empty()) { + std::string timeStampString = "Dumping " + std::string(getTrackType()) + " track's last " + + std::to_string(mTimestampDebugHelper.size()) + + " frames' timestamps(pts, dts) and frame type : "; + for (const TimestampDebugHelperEntry& entry : mTimestampDebugHelper) { + timeStampString += "\n(" + std::to_string(entry.pts) + "us, " + + std::to_string(entry.dts) + "us " + entry.frameType + ") "; + } + ALOGE("%s", timeStampString.c_str()); + } else { + ALOGE("0 frames to dump timeStamps in %s track ", getTrackType()); + } } status_t MPEG4Writer::Track::threadEntry() { -- GitLab From bad0f578565f8fa360b8eb72b1d497afae89e2b0 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 29 Mar 2019 11:03:13 -0700 Subject: [PATCH 1204/1530] aaudio examples: allow -n0 for minimum buffer size Default buffer size will be 2. Bug: 129545119 Test: should see requested BufferSize = 0 Test: adb shell write_sine_callback -pl -n0 Change-Id: I85d2792537aebec17d96dc7df6f79e34b9f8fddd --- .../examples/loopback/src/loopback.cpp | 5 ++-- .../examples/utils/AAudioArgsParser.h | 25 ++++++++++++++----- .../examples/utils/AAudioSimplePlayer.h | 7 +++--- .../write_sine/src/write_sine_callback.cpp | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp index c7c42eb225..49d921ff1d 100644 --- a/media/libaaudio/examples/loopback/src/loopback.cpp +++ b/media/libaaudio/examples/loopback/src/loopback.cpp @@ -40,7 +40,8 @@ // V0.4.01 = add -h hang option // fix -n option to set output buffer for -tm // plot first glitch -#define APP_VERSION "0.4.01" +// V0.4.02 = allow -n0 for minimal buffer size +#define APP_VERSION "0.4.02" // Tag for machine readable results as property = value pairs #define RESULT_TAG "RESULT: " @@ -535,7 +536,7 @@ int main(int argc, const char **argv) printf("INPUT stream ----------------------------------------\n"); // Use different parameters for the input. argParser.setDeviceId(requestedInputDeviceId); - argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED); + argParser.setNumberOfBursts(AAudioParameters::kDefaultNumberOfBursts); argParser.setFormat(requestedInputFormat); argParser.setPerformanceMode(inputPerformanceLevel); argParser.setChannelCount(requestedInputChannelCount); diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h index f5ed7aa867..8bb2e861ee 100644 --- a/media/libaaudio/examples/utils/AAudioArgsParser.h +++ b/media/libaaudio/examples/utils/AAudioArgsParser.h @@ -225,6 +225,8 @@ public: } } + static constexpr int32_t kDefaultNumberOfBursts = 2; + private: int32_t mChannelCount = AAUDIO_UNSPECIFIED; aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED; @@ -239,7 +241,7 @@ private: aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED; aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; - int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED; + int32_t mNumberOfBursts = kDefaultNumberOfBursts; int32_t mFramesPerCallback = AAUDIO_UNSPECIFIED; }; @@ -352,7 +354,7 @@ public: printf(" 1 = _NEVER, never use MMAP\n"); printf(" 2 = _AUTO, use MMAP if available, default for -m with no number\n"); printf(" 3 = _ALWAYS, use MMAP or fail\n"); - printf(" -n{numberOfBursts} for setBufferSize\n"); + printf(" -n{numberOfBursts} for setBufferSize, default %d\n", kDefaultNumberOfBursts); printf(" -p{performanceMode} set output AAUDIO_PERFORMANCE_MODE*, default NONE\n"); printf(" n for _NONE\n"); printf(" l for _LATENCY\n"); @@ -407,17 +409,28 @@ public: getFormat(), AAudioStream_getFormat(stream)); int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream); - int32_t sizeFrames = AAudioStream_getBufferSizeInFrames(stream); printf(" Buffer: burst = %d\n", framesPerBurst); + + int32_t sizeFrames = AAudioStream_getBufferSizeInFrames(stream); if (framesPerBurst > 0) { - printf(" Buffer: size = %d = (%d * %d) + %d\n", + int32_t requestedSize = getNumberOfBursts() * framesPerBurst; + printf(" BufferSize: requested = %4d, actual = %4d = (%d * %d) + %d\n", + requestedSize, sizeFrames, (sizeFrames / framesPerBurst), framesPerBurst, (sizeFrames % framesPerBurst)); + } else { + printf(" BufferSize: %d\n", sizeFrames); } - printf(" Capacity: requested = %d, actual = %d\n", getBufferCapacity(), - AAudioStream_getBufferCapacityInFrames(stream)); + + int32_t capacityFrames = AAudioStream_getBufferCapacityInFrames(stream); + printf(" Capacity: requested = %4d, actual = %4d = (%d * %d) + %d\n", + getBufferCapacity(), + capacityFrames, + (capacityFrames / framesPerBurst), + framesPerBurst, + (capacityFrames % framesPerBurst)); printf(" CallbackSize: requested = %d, actual = %d\n", getFramesPerCallback(), AAudioStream_getFramesPerDataCallback(stream)); diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h index 4373fa9b9a..fd1fc45c83 100644 --- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h +++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h @@ -120,10 +120,9 @@ public: if (result == AAUDIO_OK) { int32_t sizeInBursts = parameters.getNumberOfBursts(); - if (sizeInBursts > 0) { - int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream); - AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst); - } + int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream); + int32_t bufferSizeFrames = sizeInBursts * framesPerBurst; + AAudioStream_setBufferSizeInFrames(mStream, bufferSizeFrames); } AAudioStreamBuilder_delete(builder); diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp index 2b05f10b31..ca602330dd 100644 --- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp @@ -31,7 +31,7 @@ #include "AAudioSimplePlayer.h" #include "AAudioArgsParser.h" -#define APP_VERSION "0.1.6" +#define APP_VERSION "0.1.7" constexpr int32_t kDefaultHangTimeMSec = 10; -- GitLab From c1d75d4c10a0f4378925c23f2ce43f4c49ffdc92 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 29 Mar 2019 11:33:56 -0700 Subject: [PATCH 1205/1530] Allow access to libz.so in ld.config.txt Bug: 129095969 Test: atest CtsMediaTestCases:android.media.cts.DecoderTest#testVp9HdrStaticMetadata Change-Id: Ia292629f42dfc2b867e9d5ac48b1388d34e4a4e1 --- apex/ld.config.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index b3422069a9..d50b35336c 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -49,6 +49,8 @@ namespace.default.links = platform # namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES% # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so +# FIXME: b/129552044 +namespace.default.link.platform.shared_libs += libz.so ############################################################################### # "platform" namespace -- GitLab From 06131bca4df34763e5a8237c936e4e332bd30dee Mon Sep 17 00:00:00 2001 From: Jeff Hamilton Date: Fri, 29 Mar 2019 13:49:05 -0400 Subject: [PATCH 1206/1530] Set the version code to 0200000000 Bug: 129093117 Change-Id: Iae962d3d8a38c59be1b8f1940ce01a8ab21534b6 --- apex/manifest.json | 2 +- apex/manifest_codec.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/manifest.json b/apex/manifest.json index e2df3a3b2f..c6c63f6965 100644 --- a/apex/manifest.json +++ b/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.media", - "version": 1 + "version": 200000000 } diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json index e2bceec65f..4f31b1517c 100644 --- a/apex/manifest_codec.json +++ b/apex/manifest_codec.json @@ -1,4 +1,4 @@ { "name": "com.android.media.swcodec", - "version": 1 + "version": 200000000 } -- GitLab From af3777782de2e0a0d4e38d9c6f9667e2e055a4a7 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 29 Mar 2019 14:50:21 -0700 Subject: [PATCH 1207/1530] audio policy: fix configurable engine and dynamic policies take 2 Exclude remote submix devices with non "0" (legacy) addresses from devices returned to Engine by AudioPolicyManagerObserver::getAvailableOutputDevices() and AudioPolicyManagerObserver::getAvailableInputDevices() Bug: 129144725 Test: make Change-Id: If1ce89acb2a35a6bede195f520b4047f92b928bd --- .../managerdefinitions/include/DeviceDescriptor.h | 9 ++++++++- .../managerdefinitions/src/DeviceDescriptor.cpp | 12 ++++++++++++ .../engine/interface/AudioPolicyManagerObserver.h | 4 ++-- .../audiopolicy/engineconfigurable/src/Engine.cpp | 6 +++--- services/audiopolicy/enginedefault/src/Engine.cpp | 8 ++++---- .../managerdefault/AudioPolicyManager.cpp | 3 +-- .../audiopolicy/managerdefault/AudioPolicyManager.h | 8 ++++---- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h index cc43fe69e0..33e506f1b7 100644 --- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -135,10 +135,17 @@ public: /** * @brief filter the devices supported by this collection against another collection * @param devices to filter against - * @return + * @return a filtered DeviceVector */ DeviceVector filter(const DeviceVector &devices) const; + /** + * @brief filter the devices supported by this collection before sending + * then to the Engine via AudioPolicyManagerObserver interface + * @return a filtered DeviceVector + */ + DeviceVector filterForEngine() const; + /** * @brief merge two vectors. As SortedVector Implementation is buggy (it does not check the size * of the destination vector, only of the source, it provides a safe implementation diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp index 1a74f4814b..ecd5b34b6e 100644 --- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -399,6 +399,18 @@ bool DeviceVector::containsAllDevices(const DeviceVector &devices) const return filter(devices).size() == devices.size(); } +DeviceVector DeviceVector::filterForEngine() const +{ + DeviceVector filteredDevices; + for (const auto &device : *this) { + if (audio_is_remote_submix_device(device->type()) && device->address() != "0") { + continue; + } + filteredDevices.add(device); + } + return filteredDevices; +} + void DeviceDescriptor::log() const { ALOGI("Device id:%d type:0x%08X:%s, addr:%s", mId, mDeviceType, diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h index 43ba625f9c..ebd82a70e7 100644 --- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h @@ -46,9 +46,9 @@ public: virtual const AudioInputCollection &getInputs() const = 0; - virtual const DeviceVector &getAvailableOutputDevices() const = 0; + virtual const DeviceVector getAvailableOutputDevices() const = 0; - virtual const DeviceVector &getAvailableInputDevices() const = 0; + virtual const DeviceVector getAvailableInputDevices() const = 0; virtual const sp &getDefaultOutputDevice() const = 0; diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index 89aaa849f9..e59d9834a0 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -207,7 +207,7 @@ DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps); return {}; } - const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); uint32_t availableOutputDevicesType = availableOutputDevices.types(); @@ -272,7 +272,7 @@ DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &att return DeviceVector(preferredDevice); } product_strategy_t strategy = getProductStrategyForAttributes(attributes); - const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); // // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to @@ -298,7 +298,7 @@ sp Engine::getInputDeviceForAttributes(const audio_attributes_ sp *mix) const { const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); - const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const auto availableInputDevices = getApmObserver()->getAvailableInputDevices(); const auto &inputs = getApmObserver()->getInputs(); std::string address; // diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 66a6965742..592a0b9bab 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -502,8 +502,8 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const { - const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); - const DeviceVector &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices(); const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; @@ -704,7 +704,7 @@ DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &att return DeviceVector(preferredDevice); } product_strategy_t strategy = getProductStrategyForAttributes(attributes); - const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); + const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices(); const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); // // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to @@ -730,7 +730,7 @@ sp Engine::getInputDeviceForAttributes(const audio_attributes_ sp *mix) const { const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection(); - const auto &availableInputDevices = getApmObserver()->getAvailableInputDevices(); + const auto availableInputDevices = getApmObserver()->getAvailableInputDevices(); const auto &inputs = getApmObserver()->getInputs(); std::string address; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index a12bdaac4d..b9cd7d0886 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -423,8 +423,7 @@ status_t AudioPolicyManager::handleDeviceConfigChange(audio_devices_t device, if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; // Check if the device is currently connected - DeviceVector availableDevices = getAvailableOutputDevices(); - DeviceVector deviceList = availableDevices.getDevicesFromTypeMask(device); + DeviceVector deviceList = mAvailableOutputDevices.getDevicesFromTypeMask(device); if (deviceList.empty()) { // Nothing to do: device is not connected return NO_ERROR; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 26208c85a8..a700868da7 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -342,13 +342,13 @@ protected: { return mInputs; } - virtual const DeviceVector &getAvailableOutputDevices() const + virtual const DeviceVector getAvailableOutputDevices() const { - return mAvailableOutputDevices; + return mAvailableOutputDevices.filterForEngine(); } - virtual const DeviceVector &getAvailableInputDevices() const + virtual const DeviceVector getAvailableInputDevices() const { - return mAvailableInputDevices; + return mAvailableInputDevices.filterForEngine(); } virtual const sp &getDefaultOutputDevice() const { -- GitLab From a0c98417c829c0b85ab207c8117a147f6b8c3040 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 29 Mar 2019 12:10:19 -0700 Subject: [PATCH 1208/1530] Notify swcodec crashes too Bug: 129426284 Test: manual/CTS Change-Id: I086a2b8253757f941e4dd2e160490cbb59edb627 --- media/libmediaplayerservice/Android.bp | 2 +- .../MediaPlayerService.cpp | 63 ++++++++++++++----- .../MediaPlayerService.h | 10 ++- media/libstagefright/Android.bp | 1 + 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp index 46a1c24b73..90097f2554 100644 --- a/media/libmediaplayerservice/Android.bp +++ b/media/libmediaplayerservice/Android.bp @@ -11,6 +11,7 @@ cc_library_shared { ], shared_libs: [ + "android.hardware.media.c2@1.0", "android.hardware.media.omx@1.0", "libbase", "libaudioclient", @@ -21,7 +22,6 @@ cc_library_shared { "libdl", "libgui", "libhidlbase", - "libhidlmemory", "liblog", "libmedia", "libmedia_omx", diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index da95817512..71a73709f0 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -34,6 +34,8 @@ #include +#include +#include #include #include #include @@ -650,17 +652,17 @@ MediaPlayerService::Client::ServiceDeathNotifier::ServiceDeathNotifier( const sp& listener, int which) { mService = service; - mOmx = nullptr; + mHService = nullptr; mListener = listener; mWhich = which; } MediaPlayerService::Client::ServiceDeathNotifier::ServiceDeathNotifier( - const sp& omx, + const sp& hService, const sp& listener, int which) { mService = nullptr; - mOmx = omx; + mHService = hService; mListener = listener; mWhich = which; } @@ -692,9 +694,9 @@ void MediaPlayerService::Client::ServiceDeathNotifier::unlinkToDeath() { if (mService != nullptr) { mService->unlinkToDeath(this); mService = nullptr; - } else if (mOmx != nullptr) { - mOmx->unlinkToDeath(this); - mOmx = nullptr; + } else if (mHService != nullptr) { + mHService->unlinkToDeath(this); + mHService = nullptr; } } @@ -714,10 +716,12 @@ void MediaPlayerService::Client::clearDeathNotifiers_l() { mExtractorDeathListener->unlinkToDeath(); mExtractorDeathListener = nullptr; } - if (mCodecDeathListener != nullptr) { - mCodecDeathListener->unlinkToDeath(); - mCodecDeathListener = nullptr; + for (const sp& codecDeathListener : mCodecDeathListeners) { + if (codecDeathListener != nullptr) { + codecDeathListener->unlinkToDeath(); + } } + mCodecDeathListeners.clear(); } sp MediaPlayerService::Client::setDataSource_pre( @@ -741,20 +745,45 @@ sp MediaPlayerService::Client::setDataSource_pre( new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH); binder->linkToDeath(extractorDeathListener); - sp omx = IOmx::getService(); - if (omx == nullptr) { - ALOGE("IOmx service is not available"); - return NULL; + std::vector> codecDeathListeners; + { + using ::android::hidl::base::V1_0::IBase; + + // Listen to OMX's IOmxStore/default + { + sp store = ::android::hardware::media::omx::V1_0:: + IOmxStore::getService(); + if (store == nullptr) { + ALOGD("OMX service is not available"); + } else { + sp codecDeathListener = + new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); + store->linkToDeath(codecDeathListener, 0); + codecDeathListeners.emplace_back(codecDeathListener); + } + } + + // Listen to Codec2's IComponentStore/software + // TODO: Listen to all Codec2 services. + { + sp store = ::android::hardware::media::c2::V1_0:: + IComponentStore::getService(); + if (store == nullptr) { + ALOGD("Codec2 system service is not available"); + } else { + sp codecDeathListener = + new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); + store->linkToDeath(codecDeathListener, 0); + codecDeathListeners.emplace_back(codecDeathListener); + } + } } - sp codecDeathListener = - new ServiceDeathNotifier(omx, p, MEDIACODEC_PROCESS_DEATH); - omx->linkToDeath(codecDeathListener, 0); Mutex::Autolock lock(mLock); clearDeathNotifiers_l(); mExtractorDeathListener = extractorDeathListener; - mCodecDeathListener = codecDeathListener; + mCodecDeathListeners.swap(codecDeathListeners); mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p); if (!p->hardwareOutput()) { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index bfb7cc2574..26bfa71c25 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include @@ -42,7 +42,6 @@ struct AVSyncSettings; class IDataSource; class IMediaRecorder; class IMediaMetadataRetriever; -class IOMX; class IRemoteDisplay; class IRemoteDisplayClient; class MediaRecorderClient; @@ -70,7 +69,6 @@ private: class MediaPlayerService : public BnMediaPlayerService { class Client; - typedef ::android::hardware::media::omx::V1_0::IOmx IOmx; class AudioOutput : public MediaPlayerBase::AudioSink { @@ -400,7 +398,7 @@ private: const sp& listener, int which); ServiceDeathNotifier( - const sp& omx, + const sp& hService, const sp& listener, int which); virtual ~ServiceDeathNotifier(); @@ -413,7 +411,7 @@ private: private: int mWhich; sp mService; - sp mOmx; + sp mHService; // HIDL service wp mListener; }; @@ -509,7 +507,7 @@ private: media::Metadata::Filter mMetadataUpdated; // protected by mLock sp mExtractorDeathListener; - sp mCodecDeathListener; + std::vector> mCodecDeathListeners; sp mAudioDeviceUpdatedListener; #if CALLBACK_ANTAGONIZER Antagonizer* mAntagonizer; diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index da35889911..ba832d6424 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -227,6 +227,7 @@ cc_library { export_shared_lib_headers: [ "libgui", + "libhidlmemory", "libmedia", "android.hidl.allocator@1.0", ], -- GitLab From 034e53ed6ffcb4a564783b8dbf9825491f3af389 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Fri, 29 Mar 2019 13:10:53 -0700 Subject: [PATCH 1209/1530] Add libcgrouprc to APEX shared_libs list. It is linked through libprocessgroup. Test: CtsMediaTestCases:android.media.cts.MediaPlayer2Test#testPlayVideo Bug: 123664216 Change-Id: I59672f1920cb4d4808d6e46ba2db30afdfcb39cb --- apex/ld.config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index d50b35336c..2daeeac235 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -48,7 +48,7 @@ namespace.default.links = platform # TODO: replace the following when apex has a way to auto-generate this list # namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES% # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% -namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so +namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so # FIXME: b/129552044 namespace.default.link.platform.shared_libs += libz.so -- GitLab From 7307cc0f6734dbad9644cf4adac3091e42bd9972 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 23 Jan 2019 15:27:24 -0800 Subject: [PATCH 1210/1530] Camera: add QCIF resolution exception Test: checkbuild Bug: 120158047 Bug: 122461651 Change-Id: Ic7ed2e3a93278fbc95e7c6edd7eb6c35a38e1f4d --- camera/ndk/include/camera/NdkCameraDevice.h | 8 ++++++++ camera/ndk/include/camera/NdkCameraMetadataTags.h | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h index b715b12e15..92dad1c056 100644 --- a/camera/ndk/include/camera/NdkCameraDevice.h +++ b/camera/ndk/include/camera/NdkCameraDevice.h @@ -643,6 +643,14 @@ camera_status_t ACaptureSessionOutputContainer_remove( * target combinations with sizes outside of these guarantees, but this can only be tested for * by attempting to create a session with such targets.

    * + *

    Exception on 176x144 (QCIF) resolution: + * Camera devices usually have a fixed capability for downscaling from larger resolution to + * smaller, and the QCIF resolution sometimes cannot be fully supported due to this + * limitation on devices with high-resolution image sensors. Therefore, trying to configure a + * QCIF resolution stream together with any other stream larger than 1920x1080 resolution + * (either width or height) might not be supported, and capture session creation will fail if it + * is not.

    + * * @param device the camera device of interest. * @param outputs the {@link ACaptureSessionOutputContainer} describes all output streams. * @param callbacks the {@link ACameraCaptureSession_stateCallbacks capture session state callbacks}. diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index c1f5ddcb42..bee1a4621d 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -3157,6 +3157,12 @@ typedef enum acamera_metadata_tag { * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |

    *

    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 + * downscaling from larger resolution to smaller, and the QCIF resolution sometimes is not + * fully supported due to this limitation on devices with high-resolution image sensors. + * Therefore, trying to configure a QCIF resolution stream together with any other + * stream larger than 1920x1080 resolution (either width or height) might not be supported, + * and capture session creation will fail if it is not.

    * * @see ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES @@ -5154,7 +5160,7 @@ typedef enum acamera_metadata_tag { *
  • LEVEL_3 devices additionally support YUV reprocessing and RAW image capture, along * with additional output stream configurations.
  • *
  • EXTERNAL devices are similar to LIMITED devices with exceptions like some sensor or - * lens information not reorted or less stable framerates.
  • + * lens information not reported or less stable framerates. * *

    See the individual level enums for full descriptions of the supported capabilities. The * ACAMERA_REQUEST_AVAILABLE_CAPABILITIES entry describes the device's capabilities at a -- GitLab From 8ead39dda87e07627703ec8eb84881ee9ef96414 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 1 Apr 2019 13:56:12 -0700 Subject: [PATCH 1211/1530] aac: set initial timestamp to zero Bug: 127405861 Test: bug repro steps Change-Id: Id080eeb2345c19651329da3a6b7d7928a9aa3463 --- media/codec2/components/aac/C2SoftAacEnc.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index 137e775e9f..71eb1ac0cc 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -153,7 +153,7 @@ C2SoftAacEnc::C2SoftAacEnc( mSentCodecSpecificData(false), mInputTimeSet(false), mInputSize(0), - mInputTimeUs(-1ll), + mInputTimeUs(0), mSignalledError(false), mOutIndex(0u) { } @@ -179,7 +179,7 @@ c2_status_t C2SoftAacEnc::onStop() { mSentCodecSpecificData = false; mInputTimeSet = false; mInputSize = 0u; - mInputTimeUs = -1ll; + mInputTimeUs = 0; mSignalledError = false; return C2_OK; } @@ -197,6 +197,7 @@ c2_status_t C2SoftAacEnc::onFlush_sm() { mSentCodecSpecificData = false; mInputTimeSet = false; mInputSize = 0u; + mInputTimeUs = 0; return C2_OK; } @@ -558,6 +559,7 @@ c2_status_t C2SoftAacEnc::drain( mSentCodecSpecificData = false; mInputTimeSet = false; mInputSize = 0u; + mInputTimeUs = 0; // TODO: we don't have any pending work at this time to drain. return C2_OK; -- GitLab From 3afa9b1b80a90cfffdbf5aff5e8d17351422fda6 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 1 Apr 2019 14:02:18 -0700 Subject: [PATCH 1212/1530] audio policy: fix dynamic policy remote submix device removal The life cycle of an IO profile on remote submix module associated with a dynamic audio policy is different from other profiles supporting removable devices with addresses: the profile must not be cleaned up when the device is disconnected but when the dynamic policy is unregistered. Bug: 121200677 Test: phone call while connected to Android auto head unit simulator Change-Id: I8c9f64697b736dab379a7d85b9d93568139e4e84 --- .../audiopolicy/common/managerdefinitions/src/HwModule.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp index ec7ff572da..96a83377e1 100644 --- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -398,6 +398,13 @@ void HwModuleCollection::cleanUpForDevice(const sp &device) if (!moduleDevices.contains(device)) { continue; } + + // removal of remote submix devices associated with a dynamic policy is + // handled by removeOutputProfile() and removeInputProfile() + if (audio_is_remote_submix_device(device->type()) && device->address() != "0") { + continue; + } + device->detach(); // Only remove from dynamic list, not from declared list!!! if (!hwModule->getDynamicDevices().contains(device)) { -- GitLab From e4da6c62ba4a9ee1b414b91afcba0c84de0cf329 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 1 Apr 2019 15:01:41 -0700 Subject: [PATCH 1213/1530] Notify software service Bug: 129426284 Test: manual Change-Id: Ie938f73d79574bcfff5818766ded58b280136110 --- media/libmediaplayerservice/MediaPlayerService.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 71a73709f0..5061024f8f 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -776,6 +776,17 @@ sp MediaPlayerService::Client::setDataSource_pre( store->linkToDeath(codecDeathListener, 0); codecDeathListeners.emplace_back(codecDeathListener); } + + store = ::android::hardware::media::c2::V1_0:: + IComponentStore::getService("software"); + if (store == nullptr) { + ALOGD("Codec2 swcodec service is not available"); + } else { + sp codecDeathListener = + new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); + store->linkToDeath(codecDeathListener, 0); + codecDeathListeners.emplace_back(codecDeathListener); + } } } -- GitLab From 8c2e58218d9fc78c3beefc0f7c12afb39e7d0b61 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 1 Apr 2019 18:09:07 -0700 Subject: [PATCH 1214/1530] StreamHal: Enable audio power log for user builds Test: Flash user build, check AudioFlinger dumpsys Bug: 129704879 Change-Id: I9a4665b8588d30279701ca5ae18d5bd353702f4a --- media/libaudiohal/impl/StreamHalHidl.cpp | 2 +- media/libaudiohal/impl/StreamHalLocal.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 6c8e6a4465..c08dddb397 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -49,7 +49,7 @@ StreamHalHidl::StreamHalHidl(IStream *stream) // Instrument audio signal power logging. // Note: This assumes channel mask, format, and sample rate do not change after creation. - if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { + if (mStream != nullptr /* && mStreamPowerLog.isUserDebugOrEngBuild() */) { // Obtain audio properties (see StreamHalHidl::getAudioProperties() below). Return ret = mStream->getAudioProperties( [&](auto sr, auto m, auto f) { diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp index 7d5ce059df..4818fd8404 100644 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ b/media/libaudiohal/impl/StreamHalLocal.cpp @@ -33,7 +33,7 @@ StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device mStream(stream) { // Instrument audio signal power logging. // Note: This assumes channel mask, format, and sample rate do not change after creation. - if (mStream != nullptr && mStreamPowerLog.isUserDebugOrEngBuild()) { + if (mStream != nullptr /* && mStreamPowerLog.isUserDebugOrEngBuild() */) { mStreamPowerLog.init(mStream->get_sample_rate(mStream), mStream->get_channels(mStream), mStream->get_format(mStream)); -- GitLab From fcd1022c533f0c640714e79f0f8d5d1ae56f45fd Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 2 Apr 2019 10:34:00 -0700 Subject: [PATCH 1215/1530] Fix potential overflow Check width and height are not negative, to avoid overflow after casting to uint64_t. Bug: 129405342 Test: manual Change-Id: Ic540879a59cd69a29a1c4dce88ea38316792586c --- media/libstagefright/MediaCodec.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index d4e4000966..b6b7784466 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1052,8 +1052,9 @@ status_t MediaCodec::configure( } // Prevent possible integer overflow in downstream code. - if ((uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) { - ALOGE("buffer size is too big, width=%d, height=%d", mVideoWidth, mVideoHeight); + if (mVideoWidth < 0 || mVideoHeight < 0 || + (uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) { + ALOGE("Invalid size(s), width=%d, height=%d", mVideoWidth, mVideoHeight); return BAD_VALUE; } } -- GitLab From 950e751325d882befcd692d893b3311cb5b550d4 Mon Sep 17 00:00:00 2001 From: Guang Qiao Date: Mon, 26 Nov 2018 17:01:36 +0800 Subject: [PATCH 1216/1530] Fix NE in pause process when encoder has been released When the encoder generates an error or the recording file reaches its maximum size, the MediaRecorder would enter its stop process automatically and the encoder would be released in this stop process. If user tries to pause the recorder when the encoder has been released, the code would run into a wrong branch to CHECK(mPuller!=NULL), and this would cause NE for there is no puller in SURFACE mode. For we don't need to do anything in stop process within SURFACE mode when the encoder has been released, this patch just adds an empty branch. Bug: 127673985 Test: CTS - MediaRecorderTest ANDROID_BUILD_droid-cts/tools/cts-tradefed run cts -m CtsMediaTestCases --test android.media.cts.MediaRecorderTest Change-Id: I6403855f1abb86a6b125fc5ba2c5e5c31a5241db --- media/libstagefright/MediaCodecSource.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp index c3d85eeffd..50e454cb52 100644 --- a/media/libstagefright/MediaCodecSource.cpp +++ b/media/libstagefright/MediaCodecSource.cpp @@ -831,7 +831,9 @@ status_t MediaCodecSource::onStart(MetaData *params) { } void MediaCodecSource::onPause(int64_t pauseStartTimeUs) { - if ((mFlags & FLAG_USE_SURFACE_INPUT) && (mEncoder != NULL)) { + if (mStopping || mOutput.lock()->mEncoderReachedEOS) { + // Nothing to do + } else if ((mFlags & FLAG_USE_SURFACE_INPUT) && (mEncoder != NULL)) { sp params = new AMessage; params->setInt32(PARAMETER_KEY_SUSPEND, true); params->setInt64(PARAMETER_KEY_SUSPEND_TIME, pauseStartTimeUs); -- GitLab From 4c682b8d29b42094cbe33bb2aba2e1a13cf4ecf3 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Mon, 1 Apr 2019 19:35:52 -0700 Subject: [PATCH 1217/1530] MPEG4Extractor: Keep CTS values positive Media files with zero STTS delta in video track exists, probably malformed files. If the same video track has edit shift entry to shift(prepone) video frames, then CTS could get negative. Since negative CTS values are not meaningful, this CL fixes that by only subtracting the lower of CTS and edit shift value from CTS. Bug: 117402420 Test: cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases: include-annotation:android.platform.test.annotations.RequiresDevice Change-Id: I49657457f90e9bbee5fc8bf45235eeb7b0873ab6 --- media/extractors/mp4/MPEG4Extractor.cpp | 30 ++++++++++++++++--------- media/extractors/mp4/MPEG4Extractor.h | 6 ++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 5ff1c59ea0..88045d7e93 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -81,7 +81,7 @@ public: const Trex *trex, off64_t firstMoofOffset, const sp &itemTable, - int32_t elstShiftStartTicks); + uint64_t elstShiftStartTicks); virtual status_t init(); virtual media_status_t start(); @@ -147,7 +147,7 @@ private: // Start offset from composition time to presentation time. // Support shift only for video tracks through mElstShiftStartTicks for now. - int32_t mElstShiftStartTicks; + uint64_t mElstShiftStartTicks; size_t parseNALSize(const uint8_t *data) const; status_t parseChunk(off64_t *offset); @@ -4040,7 +4040,7 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) { if (track->has_elst and !strncasecmp("video/", mime, 6) and track->elst_media_time > 0) { track->elstShiftStartTicks = track->elst_media_time; - ALOGV("video track->elstShiftStartTicks :%" PRId64, track->elst_media_time); + ALOGV("video track->elstShiftStartTicks :%" PRIu64, track->elstShiftStartTicks); } MPEG4Source *source = new MPEG4Source( @@ -4450,7 +4450,7 @@ MPEG4Source::MPEG4Source( const Trex *trex, off64_t firstMoofOffset, const sp &itemTable, - int32_t elstShiftStartTicks) + uint64_t elstShiftStartTicks) : mFormat(format), mDataSource(dataSource), mTimescale(timeScale), @@ -4576,7 +4576,7 @@ status_t MPEG4Source::init() { // Start offset should be less or equal to composition time of first sample. // ISO : sample_composition_time_offset, version 0 (unsigned) for major brands. mElstShiftStartTicks = std::min(mElstShiftStartTicks, - (*mCurrentSamples.begin()).compositionOffset); + (uint64_t)(*mCurrentSamples.begin()).compositionOffset); } return err; } @@ -4586,7 +4586,7 @@ status_t MPEG4Source::init() { err = mSampleTable->getMetaDataForSample(0, NULL, NULL, &firstSampleCTS); // Start offset should be less or equal to composition time of first sample. // Composition time stamp of first sample cannot be negative. - mElstShiftStartTicks = std::min(mElstShiftStartTicks, (int32_t)firstSampleCTS); + mElstShiftStartTicks = std::min(mElstShiftStartTicks, firstSampleCTS); } return err; @@ -5496,7 +5496,11 @@ media_status_t MPEG4Source::read( err = mSampleTable->getMetaDataForSample( mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts); if(err == OK) { - cts -= mElstShiftStartTicks; + /* Composition Time Stamp cannot be negative. Some files have video Sample + * Time(STTS)delta with zero value(b/117402420). Hence subtract only + * min(cts, mElstShiftStartTicks), so that audio tracks can be played. + */ + cts -= std::min(cts, mElstShiftStartTicks); } } else { @@ -5780,8 +5784,8 @@ media_status_t MPEG4Source::fragmentedRead( if (options && options->getSeekTo(&seekTimeUs, &mode)) { seekTimeUs += ((long double)mElstShiftStartTicks * 1000000) / mTimescale; - ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRId32, seekTimeUs, - mElstShiftStartTicks); + ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRIu64, seekTimeUs, + mElstShiftStartTicks); int numSidxEntries = mSegments.size(); if (numSidxEntries != 0) { @@ -5837,7 +5841,7 @@ media_status_t MPEG4Source::fragmentedRead( off64_t offset = 0; size_t size = 0; - uint32_t cts = 0; + uint64_t cts = 0; bool isSyncSample = false; bool newBuffer = false; if (mBuffer == NULL || mCurrentSampleIndex >= mCurrentSamples.size()) { @@ -5869,7 +5873,11 @@ media_status_t MPEG4Source::fragmentedRead( offset = smpl->offset; size = smpl->size; cts = mCurrentTime + smpl->compositionOffset; - cts -= mElstShiftStartTicks; + /* Composition Time Stamp cannot be negative. Some files have video Sample + * Time(STTS)delta with zero value(b/117402420). Hence subtract only + * min(cts, mElstShiftStartTicks), so that audio tracks can be played. + */ + cts -= std::min(cts, mElstShiftStartTicks); mCurrentTime += smpl->duration; isSyncSample = (mCurrentSampleIndex == 0); diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index fadfb5061f..ab4f251aee 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -83,9 +83,13 @@ private: bool includes_expensive_metadata; bool skipTrack; bool has_elst; + /* signed int, ISO Spec allows media_time = -1 for other use cases. + * but we don't support empty edits for now. + */ int64_t elst_media_time; uint64_t elst_segment_duration; - int32_t elstShiftStartTicks; + // unsigned int, shift start offset only when media_time > 0. + uint64_t elstShiftStartTicks; bool subsample_encryption; }; -- GitLab From f878a8dc99922d0af080bb7d5f61b00e62d00e65 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 29 Mar 2019 17:23:00 -0700 Subject: [PATCH 1218/1530] aaudio: only send timestamps if queue less than half full This prevents the stream from getting suspended if not read or written fast enough. Bug: 129140370 Test: test_full_queue Change-Id: Icc5ddc72c99fb3c518736f2a50d6c6a0276ee196 --- .../oboeservice/AAudioServiceStreamBase.cpp | 21 +++++++++++++++++++ .../oboeservice/AAudioServiceStreamBase.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp index b16b5dc29b..880a3d7d25 100644 --- a/services/oboeservice/AAudioServiceStreamBase.cpp +++ b/services/oboeservice/AAudioServiceStreamBase.cpp @@ -343,6 +343,20 @@ aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t return writeUpMessageQueue(&command); } +bool AAudioServiceStreamBase::isUpMessageQueueBusy() { + std::lock_guard lock(mUpMessageQueueLock); + if (mUpMessageQueue == nullptr) { + ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); + return true; + } + int32_t framesAvailable = mUpMessageQueue->getFifoBuffer() + ->getFullFramesAvailable(); + int32_t capacity = mUpMessageQueue->getFifoBuffer() + ->getBufferCapacityInFrames(); + // Is it half full or more + return framesAvailable >= (capacity / 2); +} + aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) { std::lock_guard lock(mUpMessageQueueLock); if (mUpMessageQueue == nullptr) { @@ -366,6 +380,13 @@ aaudio_result_t AAudioServiceStreamBase::sendXRunCount(int32_t xRunCount) { aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() { AAudioServiceMessage command; + // It is not worth filling up the queue with timestamps. + // That can cause the stream to get suspended. + // So just drop the timestamp if the queue is getting full. + if (isUpMessageQueueBusy()) { + return AAUDIO_OK; + } + // Send a timestamp for the clock model. aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position, &command.timestamp.timestamp); diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h index ffc768b79a..097bc64004 100644 --- a/services/oboeservice/AAudioServiceStreamBase.h +++ b/services/oboeservice/AAudioServiceStreamBase.h @@ -312,6 +312,12 @@ protected: android::wp mServiceEndpointWeak; private: + + /** + * @return true if the queue is getting full. + */ + bool isUpMessageQueueBusy(); + aaudio_handle_t mHandle = -1; bool mFlowing = false; -- GitLab From a7138f1d2204d69c223925f39d6ab9d3c11d9632 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 29 Mar 2019 17:21:25 -0700 Subject: [PATCH 1219/1530] audio tests: test for up message queue getting full This can cause a stream to get suspended if not processed. Bug: 129140370 Test: this is a test Test: test_full_queue Change-Id: Ib8961ad708cb258c08a956c6ced813f23c437fc4 --- media/libaaudio/tests/Android.bp | 7 ++ media/libaaudio/tests/test_full_queue.cpp | 93 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 media/libaaudio/tests/test_full_queue.cpp diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index cb243a0016..958bb2e323 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -196,3 +196,10 @@ cc_test { "libutils", ], } + +cc_test { + name: "test_full_queue", + defaults: ["libaaudio_tests_defaults"], + srcs: ["test_full_queue.cpp"], + shared_libs: ["libaaudio"], +} diff --git a/media/libaaudio/tests/test_full_queue.cpp b/media/libaaudio/tests/test_full_queue.cpp new file mode 100644 index 0000000000..12d4fa3bed --- /dev/null +++ b/media/libaaudio/tests/test_full_queue.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test whether a stream dies if it is written to after a delay. +// Maybe because the message queue from the AAudio service fills up. + +#include +#include + +#include +#include + +constexpr int64_t kNanosPerSecond = 1000000000; +constexpr int64_t kTimeoutNanos = kNanosPerSecond / 2; +constexpr int kNumFrames = 256; +constexpr int kChannelCount = 2; + +static void checkFullQueue(aaudio_performance_mode_t perfMode, + int32_t sleepMillis) { + std::unique_ptr buffer = std::make_unique( + kNumFrames * kChannelCount); + + AAudioStreamBuilder *aaudioBuilder = nullptr; + + // Use an AAudioStreamBuilder to contain requested parameters. + ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder)); + + AAudioStreamBuilder_setChannelCount(aaudioBuilder, kChannelCount); + + // Request stream properties. + AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode); + + // Create an AAudioStream using the Builder. + AAudioStream *aaudioStream = nullptr; + ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, + &aaudioStream)); + AAudioStreamBuilder_delete(aaudioBuilder); + + EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream)); + + // Sleep for awhile. This might kill the stream. + usleep(sleepMillis * 1000); // 1000 millis in a microsecond + + for (int i = 0; i < 10; i++) { + const aaudio_result_t result = AAudioStream_write(aaudioStream, + buffer.get(), + kNumFrames, + kTimeoutNanos); + EXPECT_EQ(kNumFrames, result); + if (kNumFrames != result) break; + } + + EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream)); + + EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream)); +} + +TEST(test_full_queue, aaudio_full_queue_perf_none_50) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_NONE, 50 /* sleepMillis */); +} + +TEST(test_full_queue, aaudio_full_queue_perf_none_200) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_NONE, 200 /* sleepMillis */); +} + +TEST(test_full_queue, aaudio_full_queue_perf_none_1000) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_NONE, 1000 /* sleepMillis */); +} + +TEST(test_full_queue, aaudio_full_queue_low_latency_50) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 50 /* sleepMillis */); +} + +TEST(test_full_queue, aaudio_full_queue_low_latency_200) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 200 /* sleepMillis */); +} + +TEST(test_full_queue, aaudio_full_queue_low_latency_1000) { + checkFullQueue(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, 1000 /* sleepMillis */); +} -- GitLab From 3d43afe267883d2024d654af11b99264f20bf3d7 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 8 Mar 2019 11:14:05 -0800 Subject: [PATCH 1220/1530] Bound sizes for tx3g atom parsing per track Detect when media contains too much tx3g data, defined as max atom size even if spread across multiple tx3g atoms. Also validate that any tx3g atom has non-zero size. When assembling the possibly many atoms, use a more efficient append/grow in a local buffer and only attach it to the track meta data when the track is complete; saves a possible O(n*n) copy/re-copy/re-copy behavior. Bug: 124330204 Test: poc Change-Id: I55ab88a4b650e508c0d49f9bd07284d9ddcf6361 --- media/extractors/mp4/MPEG4Extractor.cpp | 83 +++++++++++++++---------- media/extractors/mp4/MPEG4Extractor.h | 31 +++++++++ 2 files changed, 82 insertions(+), 32 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 5ff1c59ea0..32566ca26b 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include "AC4Parser.h" @@ -397,7 +398,6 @@ MPEG4Extractor::~MPEG4Extractor() { while (track) { Track *next = track->next; - AMediaFormat_delete(track->meta); delete track; track = next; } @@ -672,7 +672,6 @@ status_t MPEG4Extractor::readMetaData() { ALOGV("adding HEIF image track %u", imageIndex); Track *track = new Track; - track->next = NULL; if (mLastTrack != NULL) { mLastTrack->next = track; } else { @@ -682,10 +681,7 @@ status_t MPEG4Extractor::readMetaData() { track->meta = meta; AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_TRACK_ID, imageIndex); - track->includes_expensive_metadata = false; - track->skipTrack = false; track->timescale = 1000000; - track->elstShiftStartTicks = 0; } } @@ -967,7 +963,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { ALOGV("adding new track"); Track *track = new Track; - track->next = NULL; if (mLastTrack) { mLastTrack->next = track; } else { @@ -975,15 +970,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } mLastTrack = track; - track->includes_expensive_metadata = false; - track->skipTrack = false; - track->timescale = 0; track->meta = AMediaFormat_new(); AMediaFormat_setString(track->meta, AMEDIAFORMAT_KEY_MIME, "application/octet-stream"); - track->has_elst = false; - track->subsample_encryption = false; - track->elstShiftStartTicks = 0; } off64_t stop_offset = *offset + chunk_size; @@ -1033,6 +1022,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->skipTrack = true; } + if (mLastTrack->skipTrack) { ALOGV("skipping this track..."); Track *cur = mFirstTrack; @@ -1053,6 +1043,21 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return OK; } + + // place things we built elsewhere into their final locations + + // put aggregated tx3g data into the metadata + if (mLastTrack->mTx3gFilled > 0) { + ALOGV("Putting %zu bytes of tx3g data into meta data", + mLastTrack->mTx3gFilled); + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, + mLastTrack->mTx3gBuffer, mLastTrack->mTx3gFilled); + // drop it now to reduce our footprint + free(mLastTrack->mTx3gBuffer); + mLastTrack->mTx3gBuffer = NULL; + } + } else if (chunk_type == FOURCC("moov")) { mInitCheck = OK; @@ -2553,41 +2558,55 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (mLastTrack == NULL) return ERROR_MALFORMED; - void *data; - size_t size = 0; - if (!AMediaFormat_getBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, &data, &size)) { - size = 0; - } - - if ((chunk_size > SIZE_MAX) || (SIZE_MAX - chunk_size <= size)) { + // complain about ridiculous chunks + if (chunk_size > kMaxAtomSize) { return ERROR_MALFORMED; } - uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size]; - if (buffer == NULL) { + // complain about empty atoms + if (chunk_data_size <= 0) { + ALOGE("b/124330204"); + android_errorWriteLog(0x534e4554, "124330204"); return ERROR_MALFORMED; } - if (size > 0) { - memcpy(buffer, data, size); + // should fill buffer based on "data_offset" and "chunk_data_size" + // instead of *offset and chunk_size; + // but we've been feeding the extra data to consumers for multiple releases and + // if those apps are compensating for it, we'd break them with such a change + // + + if (mLastTrack->mTx3gSize - mLastTrack->mTx3gFilled < chunk_size) { + size_t growth = kTx3gGrowth; + if (growth < chunk_size) { + growth = chunk_size; + } + // although this disallows 2 tx3g atoms of nearly kMaxAtomSize... + if ((uint64_t) mLastTrack->mTx3gSize + growth > kMaxAtomSize) { + ALOGE("b/124330204 - too much space"); + android_errorWriteLog(0x534e4554, "124330204"); + return ERROR_MALFORMED; + } + uint8_t *updated = (uint8_t *)realloc(mLastTrack->mTx3gBuffer, + mLastTrack->mTx3gSize + growth); + if (updated == NULL) { + return ERROR_MALFORMED; + } + mLastTrack->mTx3gBuffer = updated; + mLastTrack->mTx3gSize += growth; } - if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size)) + if ((size_t)(mDataSource->readAt(*offset, + mLastTrack->mTx3gBuffer + mLastTrack->mTx3gFilled, + chunk_size)) < chunk_size) { - delete[] buffer; - buffer = NULL; // advance read pointer so we don't end up reading this again *offset += chunk_size; return ERROR_IO; } - AMediaFormat_setBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, buffer, size + chunk_size); - - delete[] buffer; - + mLastTrack->mTx3gFilled += chunk_size; *offset += chunk_size; break; } diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index fadfb5061f..83fc4fe123 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -87,8 +87,39 @@ private: uint64_t elst_segment_duration; int32_t elstShiftStartTicks; bool subsample_encryption; + + uint8_t *mTx3gBuffer; + size_t mTx3gSize, mTx3gFilled; + + + Track() { + next = NULL; + meta = NULL; + timescale = 0; + includes_expensive_metadata = false; + skipTrack = false; + has_elst = false; + elst_media_time = 0; + elstShiftStartTicks = 0; + subsample_encryption = false; + mTx3gBuffer = NULL; + mTx3gSize = mTx3gFilled = 0; + } + ~Track() { + if (meta) { + AMediaFormat_delete(meta); + meta = NULL; + } + free (mTx3gBuffer); + mTx3gBuffer = NULL; + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(Track); }; + static const int kTx3gGrowth = 16 * 1024; + Vector mSidxEntries; off64_t mMoofOffset; bool mMoofFound; -- GitLab From f5943271b08f67939020c45340f2df06a5c39a18 Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Thu, 19 Oct 2017 09:53:51 +0800 Subject: [PATCH 1221/1530] MPEG4Writer: Large CodecSpecificData in MP4 ESDS box Some external media files have CodecSpecificData greater than 128 bytes. Currently, that size isn't fitting in one byte. Hence, added support to store large CodecSpecificDataSize, as per ISO standard, by extending to more than one byte as required. Bug: 127878894 Test: CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation: android.platform.test.annotations.RequiresDevice Change-Id: I2923adbecbef0aed034b3ccccf04084a7c6a34e4 --- media/libstagefright/MPEG4Writer.cpp | 60 +++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 1ec419a77a..2f13dc9d57 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -72,6 +72,7 @@ static const uint8_t kNalUnitTypePicParamSet = 0x08; static const int64_t kInitialDelayTimeUs = 700000LL; static const int64_t kMaxMetadataSize = 0x4000000LL; // 64MB max per-frame metadata size static const int64_t kMaxCttsOffsetTimeUs = 30 * 60 * 1000000LL; // 30 minutes +static const size_t kESDSScratchBufferSize = 10; // kMaxAtomSize in Mpeg4Extractor 64MB static const char kMetaKey_Version[] = "com.android.version"; static const char kMetaKey_Manufacturer[] = "com.android.manufacturer"; @@ -3882,22 +3883,52 @@ void MPEG4Writer::Track::writeAudioFourCCBox() { mOwner->endBox(); } +static void generateEsdsSize(size_t dataLength, size_t* sizeGenerated, uint8_t* buffer) { + size_t offset = 0, cur = 0; + size_t more = 0x00; + *sizeGenerated = 0; + /* Start with the LSB(7 bits) of dataLength and build the byte sequence upto MSB. + * Continuation flag(most significant bit) will be set on the first N-1 bytes. + */ + do { + buffer[cur++] = (dataLength & 0x7f) | more; + dataLength >>= 7; + more = 0x80; + ++(*sizeGenerated); + } while (dataLength > 0u); + --cur; + // Reverse the newly formed byte sequence. + while (cur > offset) { + uint8_t tmp = buffer[cur]; + buffer[cur--] = buffer[offset]; + buffer[offset++] = tmp; + } +} + void MPEG4Writer::Track::writeMp4aEsdsBox() { - mOwner->beginBox("esds"); CHECK(mCodecSpecificData); CHECK_GT(mCodecSpecificDataSize, 0u); - // Make sure all sizes encode to a single byte. - CHECK_LT(mCodecSpecificDataSize + 23, 128u); + uint8_t sizeESDBuffer[kESDSScratchBufferSize]; + uint8_t sizeDCDBuffer[kESDSScratchBufferSize]; + uint8_t sizeDSIBuffer[kESDSScratchBufferSize]; + size_t sizeESD = 0; + size_t sizeDCD = 0; + size_t sizeDSI = 0; + generateEsdsSize(mCodecSpecificDataSize, &sizeDSI, sizeDSIBuffer); + generateEsdsSize(mCodecSpecificDataSize + sizeDSI + 14, &sizeDCD, sizeDCDBuffer); + generateEsdsSize(mCodecSpecificDataSize + sizeDSI + sizeDCD + 21, &sizeESD, sizeESDBuffer); + + mOwner->beginBox("esds"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt8(0x03); // ES_DescrTag - mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->write(sizeESDBuffer, sizeESD); mOwner->writeInt16(0x0000);// ES_ID mOwner->writeInt8(0x00); mOwner->writeInt8(0x04); // DecoderConfigDescrTag - mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->write(sizeDCDBuffer, sizeDCD); mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 mOwner->writeInt8(0x15); // streamType AudioStream @@ -3912,7 +3943,7 @@ void MPEG4Writer::Track::writeMp4aEsdsBox() { mOwner->writeInt32(avgBitrate); mOwner->writeInt8(0x05); // DecoderSpecificInfoTag - mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(sizeDSIBuffer, sizeDSI); mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); static const uint8_t kData2[] = { @@ -3929,20 +3960,27 @@ void MPEG4Writer::Track::writeMp4vEsdsBox() { CHECK(mCodecSpecificData); CHECK_GT(mCodecSpecificDataSize, 0u); - // Make sure all sizes encode to a single byte. - CHECK_LT(23 + mCodecSpecificDataSize, 128u); + uint8_t sizeESDBuffer[kESDSScratchBufferSize]; + uint8_t sizeDCDBuffer[kESDSScratchBufferSize]; + uint8_t sizeDSIBuffer[kESDSScratchBufferSize]; + size_t sizeESD = 0; + size_t sizeDCD = 0; + size_t sizeDSI = 0; + generateEsdsSize(mCodecSpecificDataSize, &sizeDSI, sizeDSIBuffer); + generateEsdsSize(mCodecSpecificDataSize + sizeDSI + 14, &sizeDCD, sizeDCDBuffer); + generateEsdsSize(mCodecSpecificDataSize + sizeDSI + sizeDCD + 21, &sizeESD, sizeESDBuffer); mOwner->beginBox("esds"); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt8(0x03); // ES_DescrTag - mOwner->writeInt8(23 + mCodecSpecificDataSize); + mOwner->write(sizeESDBuffer, sizeESD); mOwner->writeInt16(0x0000); // ES_ID mOwner->writeInt8(0x1f); mOwner->writeInt8(0x04); // DecoderConfigDescrTag - mOwner->writeInt8(15 + mCodecSpecificDataSize); + mOwner->write(sizeDCDBuffer, sizeDCD); mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 mOwner->writeInt8(0x11); // streamType VisualStream @@ -3960,7 +3998,7 @@ void MPEG4Writer::Track::writeMp4vEsdsBox() { mOwner->writeInt8(0x05); // DecoderSpecificInfoTag - mOwner->writeInt8(mCodecSpecificDataSize); + mOwner->write(sizeDSIBuffer, sizeDSI); mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); static const uint8_t kData2[] = { -- GitLab From b47712a9eb1843e25f19f78d015e19b1f6698a83 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 1 Apr 2019 14:43:50 -0700 Subject: [PATCH 1222/1530] Move codec and extractor to libc_scudo. The mediaswcodec and mediaextractor use the scudo libraries as a sanitizer. This has some downsides since it bypasses all of the normal allocation handlers used by the rest of the system. Switching from the plain sanitizer to a wrapper library libc_scudo.so allows the use of the allocation handler code from bionic/libc/bionic/malloc_common.cpp. This is a temporary workaround, later versions of Android should support scudo as a first class native allocator. NOTE: The two libraries that make up the scudo override combined are smaller than the libclang_rt.scudo-XXX.so library, so this is a space win too. Bug: 123689570 Test: Builds and boots. Verified that scudo is used as the allocator. Test: Verified that the allocation limit is properly set for mediaextractor. Test: Ran a few of the CtsMediaTestCases tests. Change-Id: I3bdf76bfeea503b33da765e093e38818b620a481 --- services/mediacodec/Android.bp | 13 +++++++++---- services/mediacodec/main_swcodecservice.cpp | 6 ------ services/mediaextractor/Android.mk | 7 +++++-- services/mediaextractor/main_extractorservice.cpp | 6 ------ 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp index 25c36fa448..32d2fde0fb 100644 --- a/services/mediacodec/Android.bp +++ b/services/mediacodec/Android.bp @@ -20,6 +20,15 @@ cc_binary { exclude_shared_libs: ["libavservices_minijail"], shared_libs: ["libavservices_minijail_vendor"], }, + android: { + product_variables: { + malloc_not_svelte: { + // Scudo increases memory footprint, so only enable on + // non-svelte devices. + shared_libs: ["libc_scudo"], + }, + }, + }, }, header_libs: [ @@ -35,10 +44,6 @@ cc_binary { "-Wall", "-Wno-error=deprecated-declarations", ], - - sanitize: { - scudo: true, - }, } prebuilt_etc { diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index c44be28021..a5db031dc5 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -31,12 +31,6 @@ static const char kSystemSeccompPolicyPath[] = static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediaswcodec.policy"; -// Disable Scudo's mismatch allocation check, as it is being triggered -// by some third party code. -extern "C" const char *__scudo_default_options() { - return "DeallocationTypeMismatch=false"; -} - extern "C" void RegisterCodecServices(); int main(int argc __unused, char** /*argv*/) diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk index fd34d5b1ba..9db6ed1838 100644 --- a/services/mediaextractor/Android.mk +++ b/services/mediaextractor/Android.mk @@ -20,13 +20,16 @@ LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy LOCAL_SRC_FILES := main_extractorservice.cpp -LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \ +ifneq (true, $(filter true, $(MALLOC_SVELTE))) +# Scudo increases memory footprint, so only use on non-svelte configs. +LOCAL_SHARED_LIBRARIES := libc_scudo +endif +LOCAL_SHARED_LIBRARIES += libmedia libmediaextractorservice libbinder libutils \ liblog libandroidicu libavservices_minijail LOCAL_MODULE:= mediaextractor LOCAL_INIT_RC := mediaextractor.rc LOCAL_C_INCLUDES := frameworks/av/media/libmedia LOCAL_CFLAGS := -Wall -Werror -LOCAL_SANITIZE := scudo include $(BUILD_EXECUTABLE) # service seccomp filter diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp index 06b532df15..bb9a56baf5 100644 --- a/services/mediaextractor/main_extractorservice.cpp +++ b/services/mediaextractor/main_extractorservice.cpp @@ -41,12 +41,6 @@ static const char kSystemSeccompPolicyPath[] = static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediaextractor.policy"; -// Disable Scudo's mismatch allocation check, as it is being triggered -// by some third party code. -extern "C" const char *__scudo_default_options() { - return "DeallocationTypeMismatch=false"; -} - int main(int argc __unused, char** argv) { limitProcessMemory( -- GitLab From 74e01fa7d96b71952b32ec98f1ac352e6e74186b Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 25 Feb 2019 12:18:09 -0800 Subject: [PATCH 1223/1530] Audioflinger: tracks monitor OP_PLAY_AUDIO Mute/unmute tracks according to changes in OP_PLAY_AUDIO for the current usage. In audio policy: always assign AUDIO_STREAM_ENFORCED_AUDIBLE to sonification tracks with AUDIBILITY_ENFORCED flag. Do not mute tracks from root / audio server. Do not mute UI sounds on AUDIO_STREAM_ENFORCED_AUDIBLE stream type. Bug: 112339570 Test: enter DnD, play notifications, verify not heard Change-Id: Ia5f1118481cf0573101acf2092fbd0ce2cf8c038 --- services/audioflinger/AudioFlinger.h | 2 + services/audioflinger/PlaybackTracks.h | 34 ++++++++ services/audioflinger/Threads.cpp | 12 ++- services/audioflinger/Tracks.cpp | 77 +++++++++++++++++++ .../engine/common/src/EngineBase.cpp | 15 +++- .../managerdefault/AudioPolicyManager.cpp | 2 +- 6 files changed, 137 insertions(+), 5 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 8ac3366299..38044186ed 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -54,7 +54,9 @@ #include #include +#include #include +#include #include #include diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 357370e42e..4fd72a7917 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -19,6 +19,36 @@ #error This header file should only be included from AudioFlinger.h #endif +// Checks and monitors OP_PLAY_AUDIO +class OpPlayAudioMonitor : public RefBase { +public: + OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType); + ~OpPlayAudioMonitor() override; + bool hasOpPlayAudio() const; + +private: + AppOpsManager mAppOpsManager; + + class PlayAudioOpCallback : public BnAppOpsCallback { + public: + explicit PlayAudioOpCallback(const wp& monitor); + void opChanged(int32_t op, const String16& packageName) override; + + private: + const wp mMonitor; + }; + + sp mOpCallback; + // called by PlayAudioOpCallback when OP_PLAY_AUDIO is updated in AppOp callback + void checkPlayAudioForUsage(); + + std::atomic_bool mHasOpPlayAudio; + Vector mPackages; + const uid_t mUid; + const int32_t mUsage; // on purpose not audio_usage_t because always checked in appOps as int32_t + const int mId; // for logging purposes only +}; + // playback track class Track : public TrackBase, public VolumeProvider { public: @@ -179,6 +209,8 @@ public: int fastIndex() const { return mFastIndex; } + bool isPlaybackRestricted() const { return !mOpPlayAudioMonitor->hasOpPlayAudio(); } + protected: // FILLED state is used for suppressing volume ramp at begin of playing @@ -207,6 +239,8 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations + sp mOpPlayAudioMonitor; + bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not // intensity to play haptic data AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 984d9fee95..0e1e97f657 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4708,9 +4708,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // lacks any synchronization or barrier so VolumeProvider may read a stale value const float vh = track->getVolumeHandler()->getVolume( proxy->framesReleased()).first; - float volume = masterVolume + float volume; + if (track->isPlaybackRestricted()) { + volume = 0.f; + } else { + volume = masterVolume * mStreamTypes[track->streamType()].volume * vh; + } track->mCachedVolume = volume; gain_minifloat_packed_t vlr = proxy->getVolumeLR(); float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr)); @@ -4860,7 +4865,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac float typeVolume = mStreamTypes[track->streamType()].volume; float v = masterVolume * typeVolume; - if (track->isPausing() || mStreamTypes[track->streamType()].mute) { + if (track->isPausing() || mStreamTypes[track->streamType()].mute + || track->isPlaybackRestricted()) { vl = vr = 0; vlf = vrf = vaf = 0.; if (track->isPausing()) { @@ -5447,7 +5453,7 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr { float left, right; - if (mMasterMute || mStreamTypes[track->streamType()].mute) { + if (mMasterMute || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) { left = right = 0; } else { float typeVolume = mStreamTypes[track->streamType()].volume; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index fbf8fef953..bbda17f94d 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -378,6 +378,82 @@ status_t AudioFlinger::TrackHandle::onTransact( return BnAudioTrack::onTransact(code, data, reply, flags); } +// ---------------------------------------------------------------------------- +// AppOp for audio playback +// ------------------------------- +AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, + int id, audio_stream_type_t streamType) + : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id) +{ + if (isAudioServerOrRootUid(uid)) { + ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", mId, usage); + return; + } + // stream type has been filtered by audio policy to indicate whether it can be muted + if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) { + ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", mId, usage); + return; + } + PermissionController permissionController; + permissionController.getPackagesForUid(uid, mPackages); + checkPlayAudioForUsage(); + if (!mPackages.isEmpty()) { + mOpCallback = new PlayAudioOpCallback(this); + mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mPackages[0], mOpCallback); + } +} + +AudioFlinger::PlaybackThread::OpPlayAudioMonitor::~OpPlayAudioMonitor() +{ + if (mOpCallback != 0) { + mAppOpsManager.stopWatchingMode(mOpCallback); + } + mOpCallback.clear(); +} + +bool AudioFlinger::PlaybackThread::OpPlayAudioMonitor::hasOpPlayAudio() const { + return mHasOpPlayAudio.load(); +} + +// Note this method is never called (and never to be) for audio server / root track +// - not called from constructor due to check on UID, +// - not called from PlayAudioOpCallback because the callback is not installed in this case +void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::checkPlayAudioForUsage() +{ + if (mPackages.isEmpty()) { + mHasOpPlayAudio.store(false); + } else { + bool hasIt = true; + for (const String16& packageName : mPackages) { + const int32_t mode = mAppOpsManager.checkAudioOpNoThrow(AppOpsManager::OP_PLAY_AUDIO, + mUsage, mUid, packageName); + if (mode != AppOpsManager::MODE_ALLOWED) { + hasIt = false; + break; + } + } + ALOGD("OpPlayAudio: track:%d usage:%d %smuted", mId, mUsage, hasIt ? "not " : ""); + mHasOpPlayAudio.store(hasIt); + } +} + +AudioFlinger::PlaybackThread::OpPlayAudioMonitor::PlayAudioOpCallback::PlayAudioOpCallback( + const wp& monitor) : mMonitor(monitor) +{ } + +void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::PlayAudioOpCallback::opChanged(int32_t op, + const String16& packageName) { + // we only have uid, so we need to check all package names anyway + UNUSED(packageName); + if (op != AppOpsManager::OP_PLAY_AUDIO) { + return; + } + sp monitor = mMonitor.promote(); + if (monitor != NULL) { + monitor->checkPlayAudioForUsage(); + } +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::Track" @@ -416,6 +492,7 @@ AudioFlinger::PlaybackThread::Track::Track( mPresentationCompleteFrames(0), mFrameMap(16 /* sink-frame-to-track-frame map memory */), mVolumeHandler(new media::VolumeHandler(sampleRate)), + mOpPlayAudioMonitor(new OpPlayAudioMonitor(uid, attr.usage, id(), streamType)), // mSinkTimestamp mFastIndex(-1), mCachedVolume(1.0), diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 07a7e65124..530a2e44ab 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -70,7 +70,20 @@ product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attri audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const { - return mProductStrategies.getStreamTypeForAttributes(attr); + audio_stream_type_t engineStream = mProductStrategies.getStreamTypeForAttributes(attr); + // ensure the audibility flag for sonification is honored for stream types + // Note this is typically implemented in the product strategy configuration files, but is + // duplicated here for safety. + if (attr.usage == AUDIO_USAGE_ASSISTANCE_SONIFICATION + && ((attr.flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) != 0)) { + engineStream = AUDIO_STREAM_ENFORCED_AUDIBLE; + } + // ensure the ENFORCED_AUDIBLE stream type reflects the "force use" setting: + if ((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) + && (engineStream == AUDIO_STREAM_ENFORCED_AUDIBLE)) { + return AUDIO_STREAM_SYSTEM; + } + return engineStream; } audio_attributes_t EngineBase::getAttributesForStreamType(audio_stream_type_t stream) const diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 762a4b1651..651017d9f7 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1832,7 +1832,7 @@ status_t AudioPolicyManager::stopSource(const sp& outpu if (stream == AUDIO_STREAM_ENFORCED_AUDIBLE && mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - setStrategyMute(streamToStrategy(AUDIO_STREAM_RING), false, outputDesc); + setStrategyMute(streamToStrategy(AUDIO_STREAM_ALARM), false, outputDesc); } if (followsSameRouting(client->attributes(), attributes_initializer(AUDIO_USAGE_MEDIA))) { -- GitLab From e1c7348e1c3fed25c16ae4673101f48b1ed95b7e Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Mon, 1 Apr 2019 16:18:53 -0700 Subject: [PATCH 1224/1530] NuPlayerCCDecoder: fix memory OOB Test: cts Bug: 129068792 Change-Id: Id78ddc983f245feda3a81da3448196340b57f5c9 --- .../nuplayer/NuPlayerCCDecoder.cpp | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp index 0156ad2f3b..a2cc13e086 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp @@ -328,6 +328,11 @@ bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp &accessUnit) const size_t *userData = (size_t *)mpegUserData->data(); for (size_t i = 0; i < mpegUserData->size() / sizeof(size_t); ++i) { + if (accessUnit->size() < userData[i]) { + ALOGW("b/129068792, skip invalid offset for user data"); + android_errorWriteLog(0x534e4554, "129068792"); + continue; + } trackAdded |= parseMPEGUserDataUnit( timeUs, accessUnit->data() + userData[i], accessUnit->size() - userData[i]); } @@ -337,6 +342,12 @@ bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp &accessUnit) // returns true if a new CC track is found bool NuPlayer::CCDecoder::parseMPEGUserDataUnit(int64_t timeUs, const uint8_t *data, size_t size) { + if (size < 9) { + ALOGW("b/129068792, MPEG user data size too small %zu", size); + android_errorWriteLog(0x534e4554, "129068792"); + return false; + } + ABitReader br(data + 4, 5); uint32_t user_identifier = br.getBits(32); @@ -389,8 +400,14 @@ bool NuPlayer::CCDecoder::parseMPEGCCData(int64_t timeUs, const uint8_t *data, s mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); br.skipBits(16); } else if (mDTVCCPacket->size() > 0 && cc_type == 2) { - memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); - mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); + if (mDTVCCPacket->capacity() - mDTVCCPacket->size() >= 2) { + memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); + mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); + } else { + ALOGW("b/129068792, skip CC due to too much data(%zu, %zu)", + mDTVCCPacket->capacity(), mDTVCCPacket->size()); + android_errorWriteLog(0x534e4554, "129068792"); + } br.skipBits(16); } else if (cc_type == 0 || cc_type == 1) { uint8_t cc_data_1 = br.getBits(8) & 0x7f; @@ -477,6 +494,11 @@ bool NuPlayer::CCDecoder::parseDTVCCPacket(int64_t timeUs, const uint8_t *data, size_t trackIndex = getTrackIndex(kTrackTypeCEA708, service_number, &trackAdded); if (mSelectedTrack == (ssize_t)trackIndex) { sp ccPacket = new ABuffer(block_size); + if (ccPacket->capacity() == 0) { + ALOGW("b/129068792, no memory available, %zu", block_size); + android_errorWriteLog(0x534e4554, "129068792"); + return false; + } memcpy(ccPacket->data(), br.data(), block_size); mCCMap.add(timeUs, ccPacket); } -- GitLab From 83d17c208422b6c5df57716e9782925b0a787ee2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 2 Apr 2019 17:10:01 -0700 Subject: [PATCH 1225/1530] audio policy: fix stream to volume source mapping remove use of obsolete method streamToVolumeSource() and replace it by toVolumeSource() which actually queries the engine for stream to volume source mapping. Bug: 129439600 Test: make Change-Id: Ie4e4e93aa5925315078fa96a957bb3ed71e7dc76 --- services/audiopolicy/common/include/Volume.h | 5 ----- .../managerdefinitions/src/AudioOutputDescriptor.cpp | 3 --- .../audiopolicy/engineconfigurable/src/Engine.cpp | 8 ++++---- services/audiopolicy/enginedefault/src/Engine.cpp | 12 ++++++------ .../managerdefault/AudioPolicyManager.cpp | 6 +++--- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index 561f100092..54c5c76bf1 100644 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -33,11 +33,6 @@ namespace android { enum VolumeSource : std::underlying_type::type; static const VolumeSource VOLUME_SOURCE_NONE = static_cast(VOLUME_GROUP_NONE); -static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) { - return static_cast(stream); -} - - } // namespace android // Absolute min volume in dB (can be represented in single precision normal float value) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 97b7a018c4..8a60cf2c46 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -137,9 +137,6 @@ bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const sysTime = systemTime(); } for (const auto &iter : mVolumeActivities) { - if (iter.first == streamToVolumeSource(AUDIO_STREAM_PATCH)) { - continue; - } if (iter.second.isActive(inPastMs, sysTime)) { return true; } diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp index e59d9834a0..cb45fcf5ad 100644 --- a/services/audiopolicy/engineconfigurable/src/Engine.cpp +++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp @@ -224,16 +224,16 @@ DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const audio_devices_t devices = AUDIO_DEVICE_NONE; if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) && !is_state_in_call(getPhoneState()) && - !outputs.isActiveRemotely(streamToVolumeSource(AUDIO_STREAM_MUSIC), + !outputs.isActiveRemotely(toVolumeSource(AUDIO_STREAM_MUSIC), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) && - outputs.isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC), + outputs.isActive(toVolumeSource(AUDIO_STREAM_MUSIC), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { product_strategy_t strategyForMedia = getProductStrategyForStream(AUDIO_STREAM_MUSIC); devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia); } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) && - (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) || - outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM)))) { + (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) || + outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM)))) { // do not route accessibility prompts to a digital output currently configured with a // compressed format as they would likely not be mixed and dropped. // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable. diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 592a0b9bab..4135f01f7c 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -153,16 +153,16 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, break; case STRATEGY_SONIFICATION_RESPECTFUL: - if (isInCall() || outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) { + if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) { device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); } else { bool media_active_locally = - outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_MUSIC), + outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) || outputs.isActiveLocally( - streamToVolumeSource(AUDIO_STREAM_ACCESSIBILITY), + toVolumeSource(AUDIO_STREAM_ACCESSIBILITY), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY); // routing is same as media without the "remote" device device = getDeviceForStrategyInt(STRATEGY_MEDIA, @@ -297,7 +297,7 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, // If incall, just select the STRATEGY_PHONE device if (isInCall() || - outputs.isActiveLocally(streamToVolumeSource(AUDIO_STREAM_VOICE_CALL))) { + outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) { device = getDeviceForStrategyInt( STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); @@ -370,8 +370,8 @@ audio_devices_t Engine::getDeviceForStrategyInt(legacy_strategy strategy, } availableOutputDevices = availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType); - if (outputs.isActive(streamToVolumeSource(AUDIO_STREAM_RING)) || - outputs.isActive(streamToVolumeSource(AUDIO_STREAM_ALARM))) { + if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) || + outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) { return getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs, outputDeviceTypesToIgnore); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index b9cd7d0886..6bd12c982e 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1596,7 +1596,7 @@ status_t AudioPolicyManager::startSource(const sp& outp if (stream == AUDIO_STREAM_TTS) { ALOGV("\t found BEACON stream"); if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive( - streamToVolumeSource(AUDIO_STREAM_TTS) /*sourceToIgnore*/)) { + toVolumeSource(AUDIO_STREAM_TTS) /*sourceToIgnore*/)) { return INVALID_OPERATION; } else { beaconMuteLatency = handleEventForBeacon(STARTING_BEACON); @@ -2634,7 +2634,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() for (audio_io_handle_t output : outputs) { sp desc = mOutputs.valueFor(output); - if (activeOnly && !desc->isActive(streamToVolumeSource(AUDIO_STREAM_MUSIC))) { + if (activeOnly && !desc->isActive(toVolumeSource(AUDIO_STREAM_MUSIC))) { continue; } ALOGV("selectOutputForMusicEffects activeOnly %d output %d flags 0x%08x", @@ -5242,7 +5242,7 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre devices.merge(curDevices); for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) { sp outputDesc = mOutputs.valueFor(output); - if (outputDesc->isActive(streamToVolumeSource((audio_stream_type_t)curStream))) { + if (outputDesc->isActive(toVolumeSource(curStream))) { activeDevices.merge(outputDesc->devices()); } } -- GitLab From 68e0df70cdd57dbe32f11b5de50dd83c6492d075 Mon Sep 17 00:00:00 2001 From: jiabin Date: Mon, 18 Mar 2019 17:55:35 -0700 Subject: [PATCH 1226/1530] Use package name, pid, uid to check permission of capturing hotword. Package name will be cached in ModuleClient when attaching a client. It will be used when querying permission of capturing hotword. Test: test with logging. Bug: 74078996 Bug: 122721589 Change-Id: Icd2911f5d331d243c9eb5d58003ce5525c70c81e --- include/soundtrigger/ISoundTriggerHwService.h | 10 +++-- include/soundtrigger/SoundTrigger.h | 8 ++-- media/utils/ServiceUtilities.cpp | 6 +-- .../include/mediautils/ServiceUtilities.h | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 2 +- .../soundtrigger/SoundTriggerHwService.cpp | 42 ++++++++++++------- services/soundtrigger/SoundTriggerHwService.h | 13 ++++-- soundtrigger/ISoundTriggerHwService.cpp | 22 ++++++++-- soundtrigger/SoundTrigger.cpp | 15 ++++--- 9 files changed, 79 insertions(+), 41 deletions(-) diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h index ae0cb01dbd..1faeb0f37d 100644 --- a/include/soundtrigger/ISoundTriggerHwService.h +++ b/include/soundtrigger/ISoundTriggerHwService.h @@ -33,12 +33,14 @@ public: DECLARE_META_INTERFACE(SoundTriggerHwService); - virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + virtual status_t listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, uint32_t *numModules) = 0; - virtual status_t attach(const sound_trigger_module_handle_t handle, - const sp& client, - sp& module) = 0; + virtual status_t attach(const String16& opPackageName, + const sound_trigger_module_handle_t handle, + const sp& client, + sp& module) = 0; virtual status_t setCaptureState(bool active) = 0; }; diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h index 2e2ff7ad16..ccc61dceba 100644 --- a/include/soundtrigger/SoundTrigger.h +++ b/include/soundtrigger/SoundTrigger.h @@ -36,10 +36,12 @@ public: virtual ~SoundTrigger(); - static status_t listModules(struct sound_trigger_module_descriptor *modules, + static status_t listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, uint32_t *numModules); - static sp attach(const sound_trigger_module_handle_t module, - const sp& callback); + static sp attach(const String16& opPackageName, + const sound_trigger_module_handle_t module, + const sp& callback); static status_t setCaptureState(bool active); diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index cb681e0f44..768cd1eb07 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -138,14 +138,14 @@ bool captureMediaOutputAllowed(pid_t pid, uid_t uid) { return ok; } -bool captureHotwordAllowed(pid_t pid, uid_t uid) { +bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid) { // CAPTURE_AUDIO_HOTWORD permission implies RECORD_AUDIO permission - bool ok = recordingAllowed(String16(""), pid, uid); + bool ok = recordingAllowed(opPackageName, pid, uid); if (ok) { static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD"); // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. - ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed); + ok = PermissionCache::checkPermission(sCaptureHotwordAllowed, pid, uid); } if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD"); return ok; diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index 9377ff31f1..c5fe05f9f0 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -75,7 +75,7 @@ bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid); void finishRecording(const String16& opPackageName, uid_t uid); bool captureAudioOutputAllowed(pid_t pid, uid_t uid); bool captureMediaOutputAllowed(pid_t pid, uid_t uid); -bool captureHotwordAllowed(pid_t pid, uid_t uid); +bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid); bool settingsAllowed(); bool modifyAudioRoutingAllowed(); bool modifyDefaultAudioEffectsAllowed(); diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index b2a2344ccb..b8036bbb8d 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -385,7 +385,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, return PERMISSION_DENIED; } - bool canCaptureHotword = captureHotwordAllowed(pid, uid); + bool canCaptureHotword = captureHotwordAllowed(opPackageName, pid, uid); if ((attr->source == AUDIO_SOURCE_HOTWORD) && !canCaptureHotword) { return BAD_VALUE; } diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp index f89683a86c..377d30bcde 100644 --- a/services/soundtrigger/SoundTriggerHwService.cpp +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -82,11 +82,13 @@ SoundTriggerHwService::~SoundTriggerHwService() } } -status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules, +status_t SoundTriggerHwService::listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, uint32_t *numModules) { ALOGV("listModules"); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(opPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -103,12 +105,14 @@ status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descript return NO_ERROR; } -status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle, +status_t SoundTriggerHwService::attach(const String16& opPackageName, + const sound_trigger_module_handle_t handle, const sp& client, sp& moduleInterface) { ALOGV("attach module %d", handle); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(opPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -124,7 +128,7 @@ status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handl } sp module = mModules.valueAt(index); - sp moduleClient = module->addClient(client); + sp moduleClient = module->addClient(client, opPackageName); if (moduleClient == 0) { return NO_INIT; } @@ -479,7 +483,8 @@ SoundTriggerHwService::Module::~Module() { } sp -SoundTriggerHwService::Module::addClient(const sp& client) +SoundTriggerHwService::Module::addClient(const sp& client, + const String16& opPackageName) { AutoMutex lock(mLock); sp moduleClient; @@ -490,7 +495,7 @@ SoundTriggerHwService::Module::addClient(const sp& client) return moduleClient; } } - moduleClient = new ModuleClient(this, client); + moduleClient = new ModuleClient(this, client, opPackageName); ALOGV("addClient() client %p", moduleClient.get()); mModuleClients.add(moduleClient); @@ -913,8 +918,9 @@ SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t #define LOG_TAG "SoundTriggerHwService::ModuleClient" SoundTriggerHwService::ModuleClient::ModuleClient(const sp& module, - const sp& client) - : mModule(module), mClient(client) + const sp& client, + const String16& opPackageName) + : mModule(module), mClient(client), mOpPackageName(opPackageName) { } @@ -938,7 +944,8 @@ status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused, void SoundTriggerHwService::ModuleClient::detach() { ALOGV("detach()"); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return; } @@ -962,7 +969,8 @@ status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp& sound_model_handle_t *handle) { ALOGV("loadSoundModel() handle"); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -980,7 +988,8 @@ status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp& status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle) { ALOGV("unloadSoundModel() model handle %d", handle); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -996,7 +1005,8 @@ status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handl const sp& dataMemory) { ALOGV("startRecognition() model handle %d", handle); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -1014,7 +1024,8 @@ status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handl status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle) { ALOGV("stopRecognition() model handle %d", handle); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } @@ -1029,7 +1040,8 @@ status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle) { ALOGV("getModelState() model handle %d", handle); - if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(), + if (!captureHotwordAllowed(mOpPackageName, + IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid())) { return PERMISSION_DENIED; } diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h index 4258ec0467..43ad6112b8 100644 --- a/services/soundtrigger/SoundTriggerHwService.h +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -47,10 +47,12 @@ public: virtual ~SoundTriggerHwService(); // ISoundTriggerHwService - virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + virtual status_t listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, uint32_t *numModules); - virtual status_t attach(const sound_trigger_module_handle_t handle, + virtual status_t attach(const String16& opPackageName, + const sound_trigger_module_handle_t handle, const sp& client, sp& module); @@ -133,7 +135,8 @@ public: void setCaptureState_l(bool active); - sp addClient(const sp& client); + sp addClient(const sp& client, + const String16& opPackageName); void detach(const sp& moduleClient); @@ -156,7 +159,8 @@ public: public: ModuleClient(const sp& module, - const sp& client); + const sp& client, + const String16& opPackageName); virtual ~ModuleClient(); @@ -190,6 +194,7 @@ public: mutable Mutex mLock; wp mModule; sp mClient; + const String16 mOpPackageName; }; // class ModuleClient class CallbackThread : public Thread { diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp index d44f5cb3b1..bd107b4e87 100644 --- a/soundtrigger/ISoundTriggerHwService.cpp +++ b/soundtrigger/ISoundTriggerHwService.cpp @@ -50,7 +50,8 @@ public: { } - virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + virtual status_t listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, uint32_t *numModules) { if (numModules == NULL || (*numModules != 0 && modules == NULL)) { @@ -58,6 +59,7 @@ public: } Parcel data, reply; data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + data.writeString16(opPackageName); unsigned int numModulesReq = (modules == NULL) ? 0 : *numModules; data.writeInt32(numModulesReq); status_t status = remote()->transact(LIST_MODULES, data, &reply); @@ -77,12 +79,14 @@ public: return status; } - virtual status_t attach(const sound_trigger_module_handle_t handle, + virtual status_t attach(const String16& opPackageName, + const sound_trigger_module_handle_t handle, const sp& client, sp& module) { Parcel data, reply; data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + data.writeString16(opPackageName); data.write(&handle, sizeof(sound_trigger_module_handle_t)); data.writeStrongBinder(IInterface::asBinder(client)); status_t status = remote()->transact(ATTACH, data, &reply); @@ -120,6 +124,11 @@ status_t BnSoundTriggerHwService::onTransact( switch(code) { case LIST_MODULES: { CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + String16 opPackageName; + status_t status = data.readString16(&opPackageName); + if (status != NO_ERROR) { + return status; + } unsigned int numModulesReq = data.readInt32(); if (numModulesReq > MAX_ITEMS_PER_LIST) { numModulesReq = MAX_ITEMS_PER_LIST; @@ -133,7 +142,7 @@ status_t BnSoundTriggerHwService::onTransact( reply->writeInt32(0); return NO_ERROR; } - status_t status = listModules(modules, &numModules); + status = listModules(opPackageName, modules, &numModules); reply->writeInt32(status); reply->writeInt32(numModules); ALOGV("LIST_MODULES status %d got numModules %d", status, numModules); @@ -151,12 +160,17 @@ status_t BnSoundTriggerHwService::onTransact( case ATTACH: { CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + String16 opPackageName; + status_t status = data.readString16(&opPackageName); + if (status != NO_ERROR) { + return status; + } sound_trigger_module_handle_t handle; data.read(&handle, sizeof(sound_trigger_module_handle_t)); sp client = interface_cast(data.readStrongBinder()); sp module; - status_t status = attach(handle, client, module); + status = attach(opPackageName, handle, client, module); reply->writeInt32(status); if (module != 0) { reply->writeInt32(1); diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp index d1eb367763..9708ea726f 100644 --- a/soundtrigger/SoundTrigger.cpp +++ b/soundtrigger/SoundTrigger.cpp @@ -80,19 +80,21 @@ const sp SoundTrigger::getSoundTriggerHwService() } // Static methods -status_t SoundTrigger::listModules(struct sound_trigger_module_descriptor *modules, - uint32_t *numModules) +status_t SoundTrigger::listModules(const String16& opPackageName, + struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) { ALOGV("listModules()"); const sp service = getSoundTriggerHwService(); if (service == 0) { return NO_INIT; } - return service->listModules(modules, numModules); + return service->listModules(opPackageName, modules, numModules); } -sp SoundTrigger::attach(const sound_trigger_module_handle_t module, - const sp& callback) +sp SoundTrigger::attach(const String16& opPackageName, + const sound_trigger_module_handle_t module, + const sp& callback) { ALOGV("attach()"); sp soundTrigger; @@ -101,7 +103,8 @@ sp SoundTrigger::attach(const sound_trigger_module_handle_t module return soundTrigger; } soundTrigger = new SoundTrigger(module, callback); - status_t status = service->attach(module, soundTrigger, soundTrigger->mISoundTrigger); + status_t status = service->attach(opPackageName, module, soundTrigger, + soundTrigger->mISoundTrigger); if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) { IInterface::asBinder(soundTrigger->mISoundTrigger)->linkToDeath(soundTrigger); -- GitLab From 7795e4a3fda5623acbb8dd985150207642059218 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Tue, 2 Apr 2019 18:21:51 -0700 Subject: [PATCH 1227/1530] Audio Policy: Do not stop getOutputForAttr on invalid Mix This would allow an app that successfully registered an invalid mix to block all following mixes. Test: none Bug: 111453086 Change-Id: I3c1630b7b697dd70e08a5708d1515e097d683401 Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 8f15016eab..1b812c0ff5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -147,7 +147,10 @@ status_t AudioPolicyMixCollection::getOutputForAttr( } switch (mixMatch(policyMix.get(), i, attributes, uid)) { - case MixMatchStatus::INVALID_MIX: return BAD_VALUE; // TODO: Do we really want to abort? + case MixMatchStatus::INVALID_MIX: + // The mix has contradictory rules, ignore it + // TODO: reject invalid mix at registration + continue; case MixMatchStatus::NO_MATCH: ALOGV("%s: Mix %zu: does not match", __func__, i); continue; // skip the mix -- GitLab From 6309d88219a68fa0195a5138b738241f237e2d6b Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Wed, 20 Mar 2019 13:26:49 -0700 Subject: [PATCH 1228/1530] AAudio Fix some documentation @link issues Test: None Change-Id: I70cf5752e3b7115bc2ba751bd7d4c789d4e953f7 Signed-off-by: Kevin Rocard --- media/libaaudio/include/aaudio/AAudio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 1c1df6cad3..ee5d089eb6 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -510,11 +510,11 @@ AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** buil * Request an audio device identified device using an ID. * On Android, for example, the ID could be obtained from the Java AudioManager. * - * The default, if you do not call this function, is AAUDIO_UNSPECIFIED, + * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}, * in which case the primary device will be used. * * @param builder reference provided by AAudio_createStreamBuilder() - * @param deviceId device identifier or AAUDIO_UNSPECIFIED + * @param deviceId device identifier or {@link #AAUDIO_UNSPECIFIED} */ AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, int32_t deviceId) __INTRODUCED_IN(26); -- GitLab From 3f692411d2769d32b29b71995e6bc89bcc16b070 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 2 Apr 2019 15:48:22 -0700 Subject: [PATCH 1229/1530] AudioResampler: Minor test code fixes Test: resampler_tests Bug: 123920996 Change-Id: I11aa8f883854ebc492e230bc48311009d5a8920f --- media/libaudioprocessing/AudioResamplerDyn.cpp | 6 +++--- media/libaudioprocessing/AudioResamplerFirGen.h | 4 ++-- media/libaudioprocessing/tests/Android.bp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp index eeeeccea7c..52936cc30f 100644 --- a/media/libaudioprocessing/AudioResamplerDyn.cpp +++ b/media/libaudioprocessing/AudioResamplerDyn.cpp @@ -243,7 +243,7 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, { // compute the normalized transition bandwidth const double tbw = firKaiserTbw(c.mHalfNumCoefs, stopBandAtten); - const double halfbw = tbw / 2.; + const double halfbw = tbw * 0.5; double fcr; // compute fcr, the 3 dB amplitude cut-off. if (inSampleRate < outSampleRate) { // upsample @@ -290,7 +290,7 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, #if 0 // Keep this debug code in case an app causes resampler design issues. - const double halfbw = tbw / 2.; + const double halfbw = tbw * 0.5; // print basic filter stats ALOGD("L:%d hnc:%d stopBandAtten:%lf fcr:%lf atten:%lf tbw:%lf\n", c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, attenuation, tbw); @@ -305,7 +305,7 @@ void AudioResamplerDyn::createKaiserFir(Constants &c, const int32_t passSteps = 1000; - testFir(coefs, c.mL, c.mHalfNumCoefs, fp, fs, passSteps, passSteps * c.ML /*stopSteps*/, + testFir(coefs, c.mL, c.mHalfNumCoefs, fp, fs, passSteps, passSteps * c.mL /*stopSteps*/, passMin, passMax, passRipple, stopMax, stopRipple); ALOGD("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple); ALOGD("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple); diff --git a/media/libaudioprocessing/AudioResamplerFirGen.h b/media/libaudioprocessing/AudioResamplerFirGen.h index 39cafeb56e..100baa3ef3 100644 --- a/media/libaudioprocessing/AudioResamplerFirGen.h +++ b/media/libaudioprocessing/AudioResamplerFirGen.h @@ -547,8 +547,8 @@ static void testFir(const T* coef, int L, int halfNumCoef, wstart += wstep; } // renormalize - this is needed for integer filter types, use 1 for float or double. - constexpr int64_t integralShift = std::is_integral::value ? (sizeof(T) * 8 - 1) : 0; - const double norm = 1. / (L << integralShift); + constexpr int integralShift = std::is_integral::value ? (sizeof(T) * CHAR_BIT - 1) : 0; + const double norm = 1. / (int64_t{L} << integralShift); firMin = fmin * norm; firMax = fmax * norm; diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp index 0c8e5bb066..d990111bd6 100644 --- a/media/libaudioprocessing/tests/Android.bp +++ b/media/libaudioprocessing/tests/Android.bp @@ -5,8 +5,8 @@ cc_defaults { header_libs: ["libbase_headers"], shared_libs: [ - "libaudioutils", "libaudioprocessing", + "libaudioutils", "libcutils", "liblog", "libutils", -- GitLab From 2eaa1ee2ebab87ffa0d5ba0cbb7e403d685ad7f7 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 2 Apr 2019 15:49:55 -0700 Subject: [PATCH 1230/1530] AudioResampler: Add downsampling filter response test Test: resampler_tests Bug: 123920996 Change-Id: I835ec033bd1fc6fd6905e32c660c779fafb853cb --- .../tests/resampler_tests.cpp | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/media/libaudioprocessing/tests/resampler_tests.cpp b/media/libaudioprocessing/tests/resampler_tests.cpp index e1623f7584..829229128f 100644 --- a/media/libaudioprocessing/tests/resampler_tests.cpp +++ b/media/libaudioprocessing/tests/resampler_tests.cpp @@ -246,7 +246,8 @@ void testStopbandDownconversion(size_t channels, } void testFilterResponse( - size_t channels, unsigned inputFreq, unsigned outputFreq) + size_t channels, unsigned inputFreq, unsigned outputFreq, + android::AudioResampler::src_quality quality = android::AudioResampler::DYN_HIGH_QUALITY) { // create resampler using ResamplerType = android::AudioResamplerDyn; @@ -256,7 +257,7 @@ void testFilterResponse( AUDIO_FORMAT_PCM_FLOAT, channels, outputFreq, - android::AudioResampler::DYN_HIGH_QUALITY))); + quality))); rdyn->setSampleRate(inputFreq); // get design parameters @@ -268,17 +269,20 @@ void testFilterResponse( const double attenuation = rdyn->getFilterAttenuation(); const double stopbandDb = rdyn->getStopbandAttenuationDb(); const double passbandDb = rdyn->getPassbandRippleDb(); - const double fp = fcr - tbw / 2; - const double fs = fcr + tbw / 2; + const double fp = fcr - tbw * 0.5; + const double fs = fcr + tbw * 0.5; + const double idealfs = inputFreq <= outputFreq + ? 0.5 // upsample + : 0.5 * outputFreq / inputFreq; // downsample - printf("inputFreq:%d outputFreq:%d design" + printf("inputFreq:%d outputFreq:%d design quality %d" " phases:%d halfLength:%d" - " fcr:%lf fp:%lf fs:%lf tbw:%lf" + " fcr:%lf fp:%lf fs:%lf tbw:%lf fcrp:%lf" " attenuation:%lf stopRipple:%.lf passRipple:%lf" "\n", - inputFreq, outputFreq, + inputFreq, outputFreq, quality, phases, halfLength, - fcr, fp, fs, tbw, + fcr, fp, fs, tbw, fcr * 100. / idealfs, attenuation, stopbandDb, passbandDb); // verify design parameters @@ -541,8 +545,36 @@ TEST(audioflinger_resampler, stopbandresponse_float_multichannel) { } } -TEST(audioflinger_resampler, filterresponse) { - std::vector inSampleRates{ +// Selected downsampling responses for various frequencies relating to hearing aid. +TEST(audioflinger_resampler, downsamplingresponse) { + static constexpr android::AudioResampler::src_quality qualities[] = { + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + static constexpr int inSampleRates[] = { + 32000, + 44100, + 48000, + }; + static constexpr int outSampleRates[] = { + 16000, + 24000, + }; + + for (auto quality : qualities) { + for (int outSampleRate : outSampleRates) { + for (int inSampleRate : inSampleRates) { + testFilterResponse(2 /* channels */, inSampleRate, outSampleRate, quality); + } + } + } +} + +// General responses for typical output device scenarios - 44.1, 48, 96 kHz +// (48, 96 are part of the same resampler generation family). +TEST(audioflinger_resampler, generalresponse) { + static constexpr int inSampleRates[] = { 8000, 11025, 12000, @@ -557,7 +589,8 @@ TEST(audioflinger_resampler, filterresponse) { 176400, 192000, }; - std::vector outSampleRates{ + static constexpr int outSampleRates[] = { + 44100, 48000, 96000, }; -- GitLab From 06b40f98f1cfd0656033a179a4d9ea53ac269a48 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 26 Mar 2019 15:51:41 -0700 Subject: [PATCH 1231/1530] AudioResampler: Improve resampling for voice quality devices Enabled for devices with sampling rates 16000 or less. Test: instrumented log, resampler_tests Bug: 123920996 Change-Id: Icb2a410b09eccb97188100fd78217123444b2eff --- .../libaudioprocessing/AudioResamplerDyn.cpp | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp index 52936cc30f..0a389bb398 100644 --- a/media/libaudioprocessing/AudioResamplerDyn.cpp +++ b/media/libaudioprocessing/AudioResamplerDyn.cpp @@ -383,8 +383,16 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) ? 0.5 : 0.5 * mSampleRate / mInSampleRate; fcr *= mPropertyCutoffPercent / 100.; } else { + // Voice quality devices have lower sampling rates + // (and may be a consequence of downstream AMR-WB / G.722 codecs). + // For these devices, we ensure a wider resampler passband + // at the expense of aliasing noise (stopband attenuation + // and stopband frequency). + // + constexpr uint32_t kVoiceDeviceSampleRate = 16000; + if (mFilterQuality == DYN_HIGH_QUALITY) { - // 32b coefficients, 64 length + // float or 32b coefficients useS32 = true; stopBandAtten = 98.; if (inSampleRate >= mSampleRate * 4) { @@ -394,8 +402,18 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) } else { halfLength = 32; } + + if (mSampleRate <= kVoiceDeviceSampleRate) { + if (inSampleRate >= mSampleRate * 2) { + halfLength += 16; + } else { + halfLength += 8; + } + stopBandAtten = 84.; + tbwCheat = 1.05; + } } else if (mFilterQuality == DYN_LOW_QUALITY) { - // 16b coefficients, 16-32 length + // float or 16b coefficients useS32 = false; stopBandAtten = 80.; if (inSampleRate >= mSampleRate * 4) { @@ -405,13 +423,18 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) } else { halfLength = 8; } - if (inSampleRate <= mSampleRate) { + if (mSampleRate <= kVoiceDeviceSampleRate) { + if (inSampleRate >= mSampleRate * 2) { + halfLength += 8; + } + tbwCheat = 1.05; + } else if (inSampleRate <= mSampleRate) { tbwCheat = 1.05; } else { tbwCheat = 1.03; } } else { // DYN_MED_QUALITY - // 16b coefficients, 32-64 length + // float or 16b coefficients // note: > 64 length filters with 16b coefs can have quantization noise problems useS32 = false; stopBandAtten = 84.; @@ -422,7 +445,15 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) } else { halfLength = 16; } - if (inSampleRate <= mSampleRate) { + + if (mSampleRate <= kVoiceDeviceSampleRate) { + if (inSampleRate >= mSampleRate * 2) { + halfLength += 16; + } else { + halfLength += 8; + } + tbwCheat = 1.05; + } else if (inSampleRate <= mSampleRate) { tbwCheat = 1.03; } else { tbwCheat = 1.01; @@ -430,6 +461,19 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) } } + if (fcr > 0.) { + ALOGV("%s: mFilterQuality:%d inSampleRate:%d mSampleRate:%d halfLength:%d " + "stopBandAtten:%lf fcr:%lf", + __func__, mFilterQuality, inSampleRate, mSampleRate, halfLength, + stopBandAtten, fcr); + } else { + ALOGV("%s: mFilterQuality:%d inSampleRate:%d mSampleRate:%d halfLength:%d " + "stopBandAtten:%lf tbwCheat:%lf", + __func__, mFilterQuality, inSampleRate, mSampleRate, halfLength, + stopBandAtten, tbwCheat); + } + + // determine the number of polyphases in the filterbank. // for 16b, it is desirable to have 2^(16/2) = 256 phases. // https://ccrma.stanford.edu/~jos/resample/Relation_Interpolation_Error_Quantization.html -- GitLab From 6ecd1c5c838f4988f2eadb2e93a3a7b23122374f Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Wed, 3 Apr 2019 12:52:01 -0700 Subject: [PATCH 1232/1530] mkvCue Entry's id is wrong mkvparser.cc SeekHead::ParseEntry extracts an entry by using: (android P) pEntry->id = readUInt(pReader, pos, len) [yielding 0x0C53BB6B (android Q) pEntry->id = readID(pReader, pos, len) [yielding 0x1C53BB6B This updates mkv extractor to accept 0x1C53BB6B (using symbolic value from webmids.h) Bug: 129839777 Test: play mkv file Change-Id: Ida3f0863d59e5b67446e325d186dfe04a00420f2 --- media/extractors/mkv/MatroskaExtractor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index d6d24c18bd..e284cdaa01 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -20,6 +20,7 @@ #include "FLACDecoder.h" #include "MatroskaExtractor.h" +#include "common/webmids.h" #include #include @@ -461,7 +462,7 @@ void BlockIterator::seek( for (size_t index = 0; index < count; index++) { pEntry = pSH->GetEntry(index); - if (pEntry->id == 0x0C53BB6B) { // Cues ID + if (pEntry->id == libwebm::kMkvCues) { // Cues ID long len; long long pos; pSegment->ParseCues(pEntry->pos, pos, len); pCues = pSegment->GetCues(); @@ -1282,7 +1283,7 @@ MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) const mkvparser::SeekHead::Entry* mEntry; for (size_t index = 0; index < count; index++) { mEntry = mSH->GetEntry(index); - if (mEntry->id == 0x0C53BB6B) { // Cues ID + if (mEntry->id == libwebm::kMkvCues) { // Cues ID long len; long long pos; mSegment->ParseCues(mEntry->pos, pos, len); -- GitLab From 8146873468226172153661b2bb8104f1ff69635f Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 2 Apr 2019 10:15:38 -0700 Subject: [PATCH 1233/1530] Add sphal namespace to swcodec apex Add sphal namespace for swcodec apex, and keep the search/ permitted paths in sync with system's sphal/vndk namespaces. This change also removes libz.so from the list of shared libs linking from default to platform namespace. bug: 129552044 bug: 129095969 test: cts-tradefed run singleCommand cts -d --module CtsMediaTestCases --test android.media.cts.DecoderTest#testVp9HdrStaticMetadata Change-Id: Id3148dacde43e4bd9c1782ffd67acb21ef095811 --- apex/ld.config.txt | 100 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 24 deletions(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index 2daeeac235..b6896f5e1e 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -8,7 +8,7 @@ dir.swcodec = /apex/com.android.media.swcodec/bin/ [swcodec] -additional.namespaces = platform +additional.namespaces = platform,sphal ############################################################################### # "default" namespace @@ -22,35 +22,12 @@ namespace.default.visible = true namespace.default.search.paths = /apex/com.android.media.swcodec/${LIB} namespace.default.asan.search.paths = /apex/com.android.media.swcodec/${LIB} -# Keep the below in sync with "sphal" namespace in system's /etc/ld.config.txt -# Codec2 has dependencies on some SP-hals (eg. android.hardware.graphics.mapper@2.0) -# These are dlopen'ed by libvndksupport.so. -namespace.default.search.paths += /odm/${LIB} -namespace.default.search.paths += /vendor/${LIB} - -namespace.default.permitted.paths = /odm/${LIB} -namespace.default.permitted.paths += /vendor/${LIB} -namespace.default.permitted.paths += /vendor/${LIB}/hw -namespace.default.permitted.paths += /system/vendor/${LIB} - -namespace.default.asan.search.paths += /data/asan/odm/${LIB} -namespace.default.asan.search.paths += /odm/${LIB} -namespace.default.asan.search.paths += /data/asan/vendor/${LIB} -namespace.default.asan.search.paths += /vendor/${LIB} - -namespace.default.asan.permitted.paths = /data/asan/odm/${LIB} -namespace.default.asan.permitted.paths += /odm/${LIB} -namespace.default.asan.permitted.paths += /data/asan/vendor/${LIB} -namespace.default.asan.permitted.paths += /vendor/${LIB} - namespace.default.links = platform # TODO: replace the following when apex has a way to auto-generate this list # namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES% # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so -# FIXME: b/129552044 -namespace.default.link.platform.shared_libs += libz.so ############################################################################### # "platform" namespace @@ -74,3 +51,78 @@ namespace.platform.asan.search.paths = /data/asan/system/${LIB} # possible. namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic + +############################################################################### +# "sphal" namespace +# +############################################################################### +namespace.sphal.isolated = true +namespace.sphal.visible = true + +# Keep the below in sync with "sphal" namespace in system's /etc/ld.config.txt +# Codec2 has dependencies on some SP-hals (eg. android.hardware.graphics.mapper@2.0) +# These are dlopen'ed by libvndksupport.so. +namespace.sphal.search.paths = /odm/${LIB} +namespace.sphal.search.paths += /vendor/${LIB} + +namespace.sphal.permitted.paths = /odm/${LIB} +namespace.sphal.permitted.paths += /vendor/${LIB} +namespace.sphal.permitted.paths += /vendor/${LIB}/hw +namespace.sphal.permitted.paths += /system/vendor/${LIB} + +namespace.sphal.asan.search.paths = /data/asan/odm/${LIB} +namespace.sphal.asan.search.paths += /odm/${LIB} +namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB} +namespace.sphal.asan.search.paths += /vendor/${LIB} + +namespace.sphal.asan.permitted.paths = /data/asan/odm/${LIB} +namespace.sphal.asan.permitted.paths += /odm/${LIB} +namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB} +namespace.sphal.asan.permitted.paths += /vendor/${LIB} + +# Keep the below in sync with "vndk" namespace in system's /etc/ld.config.txt +# System's sphal namespace links to vndk namespace for %VNDK_SAMEPROCESS_LIBRARIES%, +# since we don't have a good way to auto-expand %VNDK_SAMEPROCESS_LIBRARIES%, +# we'll add the vndk paths below. + +namespace.sphal.search.paths += /odm/${LIB}/vndk-sp +namespace.sphal.search.paths += /vendor/${LIB}/vndk-sp +namespace.sphal.search.paths += /system/${LIB}/vndk-sp${VNDK_VER} + +namespace.sphal.permitted.paths += /odm/${LIB}/hw +namespace.sphal.permitted.paths += /odm/${LIB}/egl +namespace.sphal.permitted.paths += /vendor/${LIB}/hw +namespace.sphal.permitted.paths += /vendor/${LIB}/egl +namespace.sphal.permitted.paths += /system/vendor/${LIB}/hw +namespace.sphal.permitted.paths += /system/vendor/${LIB}/egl +# This is exceptionally required since android.hidl.memory@1.0-impl.so is here +namespace.sphal.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw + +namespace.sphal.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp +namespace.sphal.asan.search.paths += /odm/${LIB}/vndk-sp +namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp +namespace.sphal.asan.search.paths += /vendor/${LIB}/vndk-sp +namespace.sphal.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER} +namespace.sphal.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER} + +namespace.sphal.asan.permitted.paths += /data/asan/odm/${LIB}/hw +namespace.sphal.asan.permitted.paths += /odm/${LIB}/hw +namespace.sphal.asan.permitted.paths += /data/asan/odm/${LIB}/egl +namespace.sphal.asan.permitted.paths += /odm/${LIB}/egl +namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}/hw +namespace.sphal.asan.permitted.paths += /vendor/${LIB}/hw +namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}/egl +namespace.sphal.asan.permitted.paths += /vendor/${LIB}/egl + +namespace.sphal.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw +namespace.sphal.asan.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw + +# Once in this namespace, access to libraries in /system/lib is restricted. Only +# libs listed here can be used. +namespace.sphal.links = platform + +# TODO: replace the following when apex has a way to auto-generate this list +# namespace.sphal.link.platform.shared_libs = %LLNDK_LIBRARIES% +# namespace.sphal.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% +namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so + -- GitLab From 38e119b73d432a0dc518b39638f08d9c8867dd5d Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 1 Oct 2018 16:03:53 -0700 Subject: [PATCH 1234/1530] Camera1: Handle multiple logical cameras with same facing For Camera1-HAL3 shim, the camera ID filtering logic is revised to handle case of multiple logical cameras facing the same direction, and are backed by same/different set of physical camera IDs. Example 1 (all facing back): ID1 = ID3 + ID4 ID2 = ID5 + ID6 Example 2 (all facing back): ID5 = ID1 + ID2 ID6 = ID3 + ID4 In both examples, only ID1 will be advertised to camera1 app. Test: Check cameras on devices with multiple logical cameras Test: Camera CTS Bug: 113705942 Change-Id: I76f370938b3311bbe7adcac8eddf8b6cf08e4571 --- .../common/CameraProviderManager.cpp | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp index d6789a42f1..09638d0289 100644 --- a/services/camera/libcameraservice/common/CameraProviderManager.cpp +++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp @@ -120,8 +120,8 @@ std::vector CameraProviderManager::getAPI1CompatibleCameraDeviceIds std::vector providerDeviceIds = provider->mUniqueAPI1CompatibleCameraIds; // API1 app doesn't handle logical and physical camera devices well. So - // for each [logical, physical1, physical2, ...] id combo, only take the - // first id advertised by HAL, and filter out the rest. + // for each camera facing, only take the first id advertised by HAL in + // all [logical, physical1, physical2, ...] id combos, and filter out the rest. filterLogicalCameraIdsLocked(providerDeviceIds); deviceIds.insert(deviceIds.end(), providerDeviceIds.begin(), providerDeviceIds.end()); @@ -2500,8 +2500,11 @@ status_t CameraProviderManager::getCameraCharacteristicsLocked(const std::string void CameraProviderManager::filterLogicalCameraIdsLocked( std::vector& deviceIds) const { - std::unordered_set removedIds; + // Map between camera facing and camera IDs related to logical camera. + std::map> idCombos; + // Collect all logical and its underlying physical camera IDs for each + // facing. for (auto& deviceId : deviceIds) { auto deviceInfo = findDeviceInfoLocked(deviceId); if (deviceInfo == nullptr) continue; @@ -2509,25 +2512,38 @@ void CameraProviderManager::filterLogicalCameraIdsLocked( if (!deviceInfo->mIsLogicalCamera) { continue; } - // idCombo contains the ids of a logical camera and its physical cameras - std::vector idCombo = deviceInfo->mPhysicalIds; - idCombo.push_back(deviceId); + // combo contains the ids of a logical camera and its physical cameras + std::vector combo = deviceInfo->mPhysicalIds; + combo.push_back(deviceId); + + hardware::CameraInfo info; + status_t res = deviceInfo->getCameraInfo(&info); + if (res != OK) { + ALOGE("%s: Error reading camera info: %s (%d)", __FUNCTION__, strerror(-res), res); + continue; + } + idCombos[info.facing].insert(combo.begin(), combo.end()); + } + + // Only expose one camera ID per facing for all logical and underlying + // physical camera IDs. + for (auto& r : idCombos) { + auto& removedIds = r.second; for (auto& id : deviceIds) { - auto foundId = std::find(idCombo.begin(), idCombo.end(), id); - if (foundId == idCombo.end()) { + auto foundId = std::find(removedIds.begin(), removedIds.end(), id); + if (foundId == removedIds.end()) { continue; } - idCombo.erase(foundId); - removedIds.insert(idCombo.begin(), idCombo.end()); + removedIds.erase(foundId); break; } + deviceIds.erase(std::remove_if(deviceIds.begin(), deviceIds.end(), + [&removedIds](const std::string& s) { + return removedIds.find(s) != removedIds.end();}), + deviceIds.end()); } - - deviceIds.erase(std::remove_if(deviceIds.begin(), deviceIds.end(), - [&removedIds](const std::string& s) {return removedIds.find(s) != removedIds.end();}), - deviceIds.end()); } } // namespace android -- GitLab From cc9515118a01c27f9942782606bb1de0c5d47550 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 3 Apr 2019 16:10:42 -0700 Subject: [PATCH 1235/1530] NuPlayerRenderer: allow video rendering after flushing audio Test: test file can play Bug: 129733716 Change-Id: Iaaac854267901bd577496689a2d997271c942000 --- .../nuplayer2/NuPlayer2Renderer.cpp | 21 +++++++++++++++---- .../nuplayer/NuPlayerRenderer.cpp | 21 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp index a8c993292c..fd459df781 100644 --- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp +++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp @@ -76,6 +76,10 @@ static const int64_t kMaxAllowedAudioSinkDelayUs = 1500000LL; static const int64_t kMinimumAudioClockUpdatePeriodUs = 20 /* msec */ * 1000; +// Default video frame display duration when only video exists. +// Used to set max media time in MediaClock. +static const int64_t kDefaultVideoFrameIntervalUs = 100000LL; + // static const NuPlayer2::Renderer::PcmInfo NuPlayer2::Renderer::AUDIO_PCMINFO_INITIALIZER = { AUDIO_CHANNEL_NONE, @@ -305,11 +309,11 @@ void NuPlayer2::Renderer::flush(bool audio, bool notifyComplete) { mNotifyCompleteVideo |= notifyComplete; ++mVideoQueueGeneration; ++mVideoDrainGeneration; + mNextVideoTimeMediaUs = -1; } mMediaClock->clearAnchor(); mVideoLateByUs = 0; - mNextVideoTimeMediaUs = -1; mSyncQueues = false; } @@ -1288,7 +1292,7 @@ void NuPlayer2::Renderer::postDrainVideoQueue() { mNextVideoTimeMediaUs = mediaTimeUs; if (!mHasAudio) { // smooth out videos >= 10fps - mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs); } if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { @@ -1355,7 +1359,7 @@ void NuPlayer2::Renderer::onDrainVideoQueue() { && mediaTimeUs > mLastAudioMediaTimeUs) { // If audio ends before video, video continues to drive media clock. // Also smooth out videos >= 10fps. - mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs); } } } else { @@ -1430,7 +1434,8 @@ void NuPlayer2::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t } } else { mMediaClock->updateAnchor( - mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000); + mNextVideoTimeMediaUs, nowUs, + mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs); } } } @@ -1583,6 +1588,14 @@ void NuPlayer2::Renderer::onFlush(const sp &msg) { notifyComplete = mNotifyCompleteAudio; mNotifyCompleteAudio = false; mLastAudioMediaTimeUs = -1; + + mHasAudio = false; + if (mNextVideoTimeMediaUs >= 0) { + int64_t nowUs = ALooper::GetNowUs(); + mMediaClock->updateAnchor( + mNextVideoTimeMediaUs, nowUs, + mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs); + } } else { notifyComplete = mNotifyCompleteVideo; mNotifyCompleteVideo = false; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 65d6d61720..39be40ddcc 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -77,6 +77,10 @@ static const int64_t kMaxAllowedAudioSinkDelayUs = 1500000LL; static const int64_t kMinimumAudioClockUpdatePeriodUs = 20 /* msec */ * 1000; +// Default video frame display duration when only video exists. +// Used to set max media time in MediaClock. +static const int64_t kDefaultVideoFrameIntervalUs = 100000LL; + // static const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER = { AUDIO_CHANNEL_NONE, @@ -314,11 +318,11 @@ void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) { mNotifyCompleteVideo |= notifyComplete; ++mVideoQueueGeneration; ++mVideoDrainGeneration; + mNextVideoTimeMediaUs = -1; } mMediaClock->clearAnchor(); mVideoLateByUs = 0; - mNextVideoTimeMediaUs = -1; mSyncQueues = false; } @@ -1302,7 +1306,7 @@ void NuPlayer::Renderer::postDrainVideoQueue() { mNextVideoTimeMediaUs = mediaTimeUs; if (!mHasAudio) { // smooth out videos >= 10fps - mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs); } if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { @@ -1369,7 +1373,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() { && mediaTimeUs > mLastAudioMediaTimeUs) { // If audio ends before video, video continues to drive media clock. // Also smooth out videos >= 10fps. - mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); + mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs); } } } else { @@ -1444,7 +1448,8 @@ void NuPlayer::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t d } } else { mMediaClock->updateAnchor( - mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000); + mNextVideoTimeMediaUs, nowUs, + mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs); } } } @@ -1597,6 +1602,14 @@ void NuPlayer::Renderer::onFlush(const sp &msg) { notifyComplete = mNotifyCompleteAudio; mNotifyCompleteAudio = false; mLastAudioMediaTimeUs = -1; + + mHasAudio = false; + if (mNextVideoTimeMediaUs >= 0) { + int64_t nowUs = ALooper::GetNowUs(); + mMediaClock->updateAnchor( + mNextVideoTimeMediaUs, nowUs, + mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs); + } } else { notifyComplete = mNotifyCompleteVideo; mNotifyCompleteVideo = false; -- GitLab From 06c6b9a0f9c964270d78d62a08e964d9212faa0c Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 3 Apr 2019 16:46:37 -0700 Subject: [PATCH 1236/1530] fix AudioAttributes matching rule in AudioProductStrategy The matching rule on audio attributes flags must be that ALL flags set in the reference attributes are present in the compared attributes and not just some flags. Bug: 129721367 Test: change volume in Play Music Change-Id: I6b5ada937aa169dbf1b0315d56172ae3b7bb4f47 (cherry picked from commit c0c6074f55a0775ff127814c58c56a748e1a1b50) --- media/libaudioclient/AudioProductStrategy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp index 1da1114709..0e1dfac023 100644 --- a/media/libaudioclient/AudioProductStrategy.cpp +++ b/media/libaudioclient/AudioProductStrategy.cpp @@ -86,7 +86,7 @@ bool AudioProductStrategy::attributesMatches(const audio_attributes_t refAttribu (clientAttritubes.content_type == refAttributes.content_type)) && ((refAttributes.flags == AUDIO_FLAG_NONE) || (clientAttritubes.flags != AUDIO_FLAG_NONE && - (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) && + (clientAttritubes.flags & refAttributes.flags) == refAttributes.flags)) && ((strlen(refAttributes.tags) == 0) || (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0)); } -- GitLab From 3787336d368bc5162b1038e1c5abc5e71f490b41 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 27 Feb 2019 11:57:56 -0800 Subject: [PATCH 1237/1530] media: omx: reorganize makefiles into a common defaults for soft OMX Remove the software OMX codecs from VNDK as they are deprecated. Bug: 129710438 Change-Id: Icb70aaaaa4189aded5ff6d3f247d9971d938bd14 --- media/libstagefright/codecs/aacdec/Android.bp | 15 +---- media/libstagefright/codecs/aacenc/Android.bp | 16 +---- .../codecs/amrnb/dec/Android.bp | 16 +---- .../codecs/amrnb/enc/Android.bp | 16 +---- media/libstagefright/codecs/amrwb/Android.bp | 2 +- .../libstagefright/codecs/amrwbenc/Android.bp | 15 +---- media/libstagefright/codecs/avcdec/Android.bp | 16 +---- media/libstagefright/codecs/avcenc/Android.bp | 18 +----- .../codecs/avcenc/SoftAVCEnc.cpp | 4 +- .../libstagefright/codecs/flac/dec/Android.bp | 17 +----- .../libstagefright/codecs/flac/enc/Android.bp | 19 +----- .../libstagefright/codecs/g711/dec/Android.bp | 15 +---- .../libstagefright/codecs/gsm/dec/Android.bp | 16 +---- .../libstagefright/codecs/hevcdec/Android.bp | 16 +---- .../codecs/m4v_h263/dec/Android.bp | 22 ++----- .../codecs/m4v_h263/enc/Android.bp | 20 +------ .../codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp | 4 +- media/libstagefright/codecs/mp3dec/Android.bp | 14 +---- .../libstagefright/codecs/mpeg2dec/Android.bp | 13 +--- .../libstagefright/codecs/on2/dec/Android.bp | 15 +---- .../libstagefright/codecs/on2/enc/Android.bp | 16 +---- .../libstagefright/codecs/opus/dec/Android.bp | 15 +---- media/libstagefright/codecs/raw/Android.bp | 16 +---- .../codecs/vorbis/dec/Android.bp | 15 +---- .../libstagefright/codecs/xaacdec/Android.bp | 12 +--- media/libstagefright/flac/dec/Android.bp | 14 +++-- media/libstagefright/omx/Android.bp | 60 +++++++++++++------ 27 files changed, 84 insertions(+), 353 deletions(-) diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp index e0bb5cd22e..46b3b8fd85 100644 --- a/media/libstagefright/codecs/aacdec/Android.bp +++ b/media/libstagefright/codecs/aacdec/Android.bp @@ -1,22 +1,12 @@ cc_library_shared { name: "libstagefright_soft_aacdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: [ "SoftAAC2.cpp", "DrcPresModeWrap.cpp", ], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -29,10 +19,7 @@ cc_library_shared { static_libs: ["libFraunhoferAAC"], - defaults: ["omx_soft_libs"], - shared_libs: [ "libcutils", ], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp index 0d677fe21f..bf789c401c 100644 --- a/media/libstagefright/codecs/aacenc/Android.bp +++ b/media/libstagefright/codecs/aacenc/Android.bp @@ -1,19 +1,9 @@ cc_library_shared { name: "libstagefright_soft_aacenc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftAACEncoder2.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -25,8 +15,4 @@ cc_library_shared { }, static_libs: ["libFraunhoferAAC"], - - defaults: ["omx_soft_libs"], - - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp index f3b272b989..e18117e1e4 100644 --- a/media/libstagefright/codecs/amrnb/dec/Android.bp +++ b/media/libstagefright/codecs/amrnb/dec/Android.bp @@ -40,7 +40,6 @@ cc_library_static { "src/wmf_to_ets.cpp", ], - include_dirs: ["frameworks/av/media/libstagefright/include"], export_include_dirs: ["src"], cflags: [ @@ -68,23 +67,14 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_amrdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftAMR.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/av/media/libstagefright/codecs/amrwb/src", - "frameworks/native/include/media/openmax", - ], local_include_dirs: ["src"], cflags: [ "-DOSCL_IMPORT_REF=", - "-Werror", ], version_script: "exports.lds", @@ -101,13 +91,11 @@ cc_library_shared { "libstagefright_amrwbdec", ], - defaults: ["omx_soft_libs"], - shared_libs: [ "libstagefright_amrnb_common", ], - compile_multilib: "32", } + //############################################################################### cc_test { name: "libstagefright_amrnbdec_test", diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp index 1c8b511b32..438ed04ce0 100644 --- a/media/libstagefright/codecs/amrnb/enc/Android.bp +++ b/media/libstagefright/codecs/amrnb/enc/Android.bp @@ -62,7 +62,7 @@ cc_library_static { "src/ton_stab.cpp", ], - include_dirs: ["frameworks/av/media/libstagefright/include"], + header_libs: ["libstagefright_headers"], export_include_dirs: ["src"], cflags: [ @@ -86,21 +86,12 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_amrnbenc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftAMRNBEncoder.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], local_include_dirs: ["src"], - cflags: ["-Werror"], - //addressing b/25409744 //sanitize: { // misc_undefined: [ @@ -110,12 +101,9 @@ cc_library_shared { static_libs: ["libstagefright_amrnbenc"], - defaults: ["omx_soft_libs"], - shared_libs: [ "libstagefright_amrnb_common", ], - compile_multilib: "32", } //############################################################################### diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp index 9fefd81054..88cf7f2fd0 100644 --- a/media/libstagefright/codecs/amrwb/Android.bp +++ b/media/libstagefright/codecs/amrwb/Android.bp @@ -44,7 +44,7 @@ cc_library_static { "src/weight_amrwb_lpc.cpp", ], - include_dirs: ["frameworks/av/media/libstagefright/include"], + header_libs: ["libstagefright_headers"], export_include_dirs: [ "src", diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index 262962fe0b..3beed66d20 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -142,20 +142,10 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_amrwbenc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftAMRWBEncoder.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -167,11 +157,8 @@ cc_library_shared { static_libs: ["libstagefright_amrwbenc"], - defaults: ["omx_soft_libs"], - shared_libs: [ "libstagefright_enc_common", ], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp index 567bcca181..0bb6bb067e 100644 --- a/media/libstagefright/codecs/avcdec/Android.bp +++ b/media/libstagefright/codecs/avcdec/Android.bp @@ -1,29 +1,16 @@ cc_library_shared { name: "libstagefright_soft_avcdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], static_libs: ["libavcdec"], srcs: ["SoftAVCDec.cpp"], cflags: [ "-Wall", - "-Werror", ], version_script: "exports.lds", - include_dirs: [ - "external/libavc/decoder", - "external/libavc/common", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - sanitize: { misc_undefined: [ "signed-integer-overflow", @@ -32,5 +19,4 @@ cc_library_shared { }, ldflags: ["-Wl,-Bsymbolic"], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp index 0cd39e1b46..980261c2a1 100644 --- a/media/libstagefright/codecs/avcenc/Android.bp +++ b/media/libstagefright/codecs/avcenc/Android.bp @@ -1,23 +1,10 @@ cc_library_shared { name: "libstagefright_soft_avcenc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], static_libs: ["libavcenc"], srcs: ["SoftAVCEnc.cpp"], - include_dirs: [ - "external/libavc/encoder", - "external/libavc/common", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/hardware", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - sanitize: { misc_undefined: [ "signed-integer-overflow", @@ -27,12 +14,9 @@ cc_library_shared { cflags: [ "-Wall", - "-Werror", "-Wno-unused-variable", ], ldflags: ["-Wl,-Bsymbolic"], version_script: "exports.lds", - - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp index e0f26831bf..9db64652e8 100644 --- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp @@ -21,8 +21,8 @@ #include "OMX_Video.h" -#include -#include +#include +#include #include #include #include diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp index 18a3f6bd19..4064751b3d 100644 --- a/media/libstagefright/codecs/flac/dec/Android.bp +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -1,23 +1,11 @@ cc_library_shared { name: "libstagefright_soft_flacdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: [ "SoftFlacDecoder.cpp", ], - include_dirs: [ - "external/flac/include", - "frameworks/av/media/libstagefright/flac/dec", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -28,10 +16,7 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - shared_libs: [ "libstagefright_flacdec", ], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp index 4149ccde22..d7d871a9d0 100644 --- a/media/libstagefright/codecs/flac/enc/Android.bp +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -1,15 +1,9 @@ cc_library_shared { + name: "libstagefright_soft_flacenc", + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftFlacEncoder.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - "external/flac/include", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -19,19 +13,10 @@ cc_library_shared { ], cfi: true, }, - defaults: ["omx_soft_libs"], header_libs: ["libbase_headers"], static_libs: [ "libaudioutils", "libFLAC", ], - - name: "libstagefright_soft_flacenc", - vendor_available: true, - vndk: { - enabled: true, - }, - - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp index c273179ac6..f5357f4781 100644 --- a/media/libstagefright/codecs/g711/dec/Android.bp +++ b/media/libstagefright/codecs/g711/dec/Android.bp @@ -1,21 +1,9 @@ cc_library_shared { name: "libstagefright_soft_g711dec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftG711.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -25,5 +13,4 @@ cc_library_shared { ], cfi: true, }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp index 3c5ebfe22e..5672d89426 100644 --- a/media/libstagefright/codecs/gsm/dec/Android.bp +++ b/media/libstagefright/codecs/gsm/dec/Android.bp @@ -1,20 +1,9 @@ cc_library_shared { name: "libstagefright_soft_gsmdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftGSM.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - "external/libgsm/inc", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -25,8 +14,5 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - static_libs: ["libgsm"], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp index cc91d534b3..ec436ce7d9 100644 --- a/media/libstagefright/codecs/hevcdec/Android.bp +++ b/media/libstagefright/codecs/hevcdec/Android.bp @@ -1,28 +1,17 @@ cc_library_shared { name: "libstagefright_soft_hevcdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], static_libs: ["libhevcdec"], srcs: ["SoftHEVC.cpp"], cflags: [ "-Wall", - "-Werror", "-Wno-unused-variable", ], version_script: "exports.lds", - include_dirs: [ - "external/libhevc/decoder", - "external/libhevc/common", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - sanitize: { misc_undefined: [ "signed-integer-overflow", @@ -30,11 +19,8 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - // We need this because the current asm generates the following link error: // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC // Bug: 16853291 ldflags: ["-Wl,-Bsymbolic"], - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp index 0523143724..6b45ea20b4 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp @@ -38,9 +38,9 @@ cc_library_static { "src/zigzag_tab.cpp", ], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", + header_libs: [ + "media_plugin_headers", + "libstagefright_headers" ], local_include_dirs: ["src"], @@ -67,37 +67,23 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_mpeg4dec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftMPEG4.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - local_include_dirs: ["src"], - export_include_dirs: ["include"], cflags: [ "-DOSCL_EXPORT_REF=", "-DOSCL_IMPORT_REF=", - - "-Werror", ], static_libs: ["libstagefright_m4vh263dec"], - defaults: ["omx_soft_libs"], - sanitize: { misc_undefined: [ "signed-integer-overflow", ], cfi: true, }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp index d38f4b1359..273818700e 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp @@ -32,10 +32,6 @@ cc_library_static { version_script: "exports.lds", - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], local_include_dirs: ["src"], export_include_dirs: ["include"], @@ -51,41 +47,27 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_mpeg4enc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftMPEG4Encoder.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - "frameworks/native/include/media/hardware", - ], local_include_dirs: ["src"], - export_include_dirs: ["include"], cflags: [ "-DBX_RC", "-DOSCL_IMPORT_REF=", "-DOSCL_UNUSED_ARG(x)=(void)(x)", "-DOSCL_EXPORT_REF=", - - "-Werror", ], static_libs: ["libstagefright_m4vh263enc"], - defaults: ["omx_soft_libs"], - sanitize: { misc_undefined: [ "signed-integer-overflow", ], cfi: true, }, - compile_multilib: "32", } //############################################################################### diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp index f6a7b0e0e1..fa7db813a6 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -22,8 +22,8 @@ #include "mp4enc_api.h" #include "OMX_Video.h" -#include -#include +#include +#include #include #include #include diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp index 9173ed6611..b630524ec5 100644 --- a/media/libstagefright/codecs/mp3dec/Android.bp +++ b/media/libstagefright/codecs/mp3dec/Android.bp @@ -78,24 +78,15 @@ cc_library_static { cc_library_shared { name: "libstagefright_soft_mp3dec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftMP3.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], local_include_dirs: [ "src", "include", ], - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -105,10 +96,7 @@ cc_library_shared { cfi: true, }, - defaults: ["omx_soft_libs"], - static_libs: ["libstagefright_mp3dec"], - compile_multilib: "32", } //############################################################################### diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp index 26e786e7f6..e849410b99 100644 --- a/media/libstagefright/codecs/mpeg2dec/Android.bp +++ b/media/libstagefright/codecs/mpeg2dec/Android.bp @@ -1,27 +1,17 @@ cc_library_shared { name: "libstagefright_soft_mpeg2dec", - vendor_available: true, + defaults: ["libstagefright_softomx-defaults"], static_libs: ["libmpeg2dec"], srcs: ["SoftMPEG2.cpp"], cflags: [ "-Wall", - "-Werror", "-Wno-unused-variable", ], version_script: "exports.lds", - include_dirs: [ - "external/libmpeg2/decoder", - "external/libmpeg2/common", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - ldflags: ["-Wl,-Bsymbolic"], sanitize: { @@ -30,5 +20,4 @@ cc_library_shared { ], cfi: true, }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp index abd21d7e49..577231cc11 100644 --- a/media/libstagefright/codecs/on2/dec/Android.bp +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -1,23 +1,11 @@ cc_library_shared { name: "libstagefright_soft_vpxdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftVPX.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - static_libs: ["libvpx"], - defaults: ["omx_soft_libs"], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -27,5 +15,4 @@ cc_library_shared { ], cfi: true, }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp index ea46badfcb..82c215e042 100644 --- a/media/libstagefright/codecs/on2/enc/Android.bp +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -1,9 +1,6 @@ cc_library_shared { name: "libstagefright_soft_vpxenc", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: [ "SoftVPXEncoder.cpp", @@ -11,15 +8,10 @@ cc_library_shared { "SoftVP9Encoder.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: ["-Wall"], version_script: "exports.lds", - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - sanitize: { misc_undefined: [ "signed-integer-overflow", @@ -29,8 +21,4 @@ cc_library_shared { }, static_libs: ["libvpx"], - - defaults: ["omx_soft_libs"], - - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp index bfcae070d5..71a2a0d3cd 100644 --- a/media/libstagefright/codecs/opus/dec/Android.bp +++ b/media/libstagefright/codecs/opus/dec/Android.bp @@ -1,25 +1,13 @@ cc_library_shared { name: "libstagefright_soft_opusdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftOpus.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - shared_libs: [ "libopus", ], - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -29,5 +17,4 @@ cc_library_shared { ], cfi: true, }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp index 1c23badc38..fcc7a0a100 100644 --- a/media/libstagefright/codecs/raw/Android.bp +++ b/media/libstagefright/codecs/raw/Android.bp @@ -1,19 +1,9 @@ cc_library_shared { name: "libstagefright_soft_rawdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftRaw.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -23,8 +13,4 @@ cc_library_shared { ], cfi: true, }, - - defaults: ["omx_soft_libs"], - - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp index 2d1a92250d..3efb952c73 100644 --- a/media/libstagefright/codecs/vorbis/dec/Android.bp +++ b/media/libstagefright/codecs/vorbis/dec/Android.bp @@ -1,25 +1,13 @@ cc_library_shared { name: "libstagefright_soft_vorbisdec", - vendor_available: true, - vndk: { - enabled: true, - }, + defaults: ["libstagefright_softomx-defaults"], srcs: ["SoftVorbis.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - - defaults: ["omx_soft_libs"], - shared_libs: [ "libvorbisidec", ], - cflags: ["-Werror"], - version_script: "exports.lds", sanitize: { @@ -28,5 +16,4 @@ cc_library_shared { "unsigned-integer-overflow", ], }, - compile_multilib: "32", } diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp index e49eb8fa65..5385dbcbab 100644 --- a/media/libstagefright/codecs/xaacdec/Android.bp +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -1,18 +1,12 @@ cc_library_shared { name: "libstagefright_soft_xaacdec", - vendor_available: true, + defaults: ["libstagefright_softomx-defaults"], srcs: [ "SoftXAAC.cpp", ], - include_dirs: [ - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - cflags: [ - "-Werror", "-DENABLE_MPEG_D_DRC" ], @@ -24,11 +18,7 @@ cc_library_shared { static_libs: ["libxaacdec"], - defaults: ["omx_soft_libs"], - shared_libs: [ "libcutils", ], - - compile_multilib: "32", } diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp index 307c9b0e99..b270808efc 100644 --- a/media/libstagefright/flac/dec/Android.bp +++ b/media/libstagefright/flac/dec/Android.bp @@ -11,11 +11,6 @@ cc_library { export_include_dirs: [ "." ], - include_dirs: [ - "external/flac/include", - "frameworks/av/media/libstagefright/include", - ], - cflags: ["-Werror"], sanitize: { @@ -38,10 +33,17 @@ cc_library { "libFLAC", "libaudioutils", ], + export_static_lib_headers: [ + "libFLAC", + ], }, shared_libs: [ "liblog", ], - header_libs: ["libmedia_headers"], + + header_libs: [ + "libmedia_headers", + "libFLAC-headers", + ], } diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index c06aca551f..15952e35df 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -43,6 +43,7 @@ cc_library_shared { "libcutils", "libstagefright_foundation", "libstagefright_bufferqueue_helper", + "libstagefright_softomx", "libstagefright_xmlparser", "libdl", "libhidlbase", @@ -51,7 +52,6 @@ cc_library_shared { "libvndksupport", "android.hardware.media.omx@1.0", "android.hardware.graphics.bufferqueue@1.0", - "libstagefright_omx_soft", ], export_shared_lib_headers: [ @@ -75,20 +75,12 @@ cc_library_shared { ], cfi: true, }, -} -cc_defaults { - name: "omx_soft_libs", - shared_libs: [ - "libutils", - "liblog", - "libstagefright_foundation", - "libstagefright_omx_soft", - ], + compile_multilib: "32", } cc_library_shared { - name: "libstagefright_omx_soft", + name: "libstagefright_softomx", vendor_available: true, vndk: { enabled: true, @@ -106,17 +98,19 @@ cc_library_shared { "include", ], - shared_libs: [ - "libutils", - "liblog", - "libui", - "libstagefright_foundation", + header_libs: [ + "media_plugin_headers", ], - export_shared_lib_headers: [ + export_header_lib_headers: [ + "media_plugin_headers", + ], + + shared_libs: [ "libstagefright_foundation", - "libutils", "liblog", + "libui", + "libutils", ], cflags: [ @@ -135,6 +129,36 @@ cc_library_shared { }, } +cc_defaults { + name: "libstagefright_softomx-defaults", + vendor_available: true, + + cflags: [ + "-Werror", + ], + + header_libs: [ + "media_plugin_headers" + ], + + shared_libs: [ + "libstagefright_softomx", + "libstagefright_foundation", + "libutils", + "liblog", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + compile_multilib: "32", +} + cc_library_shared { name: "libstagefright_omx_utils", vendor_available: true, -- GitLab From 86571a97654dbe09272b0504fe17c71211f4a5e7 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 2 Apr 2019 15:40:54 -0700 Subject: [PATCH 1238/1530] AudioResampler: Add property for resampler testing. Add ro.audio.resampler.psd.tbwcheat to allow partner experimentation Test: native resampler_tests. Bug: 123920996 Merged-In: I983ab3ef7303fc9d93fc680de05d0cf3cfd7ff5a Change-Id: I983ab3ef7303fc9d93fc680de05d0cf3cfd7ff5a (cherry picked from commit 076f690ab192b8f2281ffdf2af6f324553656d89) --- media/libaudioprocessing/AudioResamplerDyn.cpp | 16 +++++++++++++--- media/libaudioprocessing/AudioResamplerDyn.h | 7 +++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp index 0a389bb398..ec56b0015c 100644 --- a/media/libaudioprocessing/AudioResamplerDyn.cpp +++ b/media/libaudioprocessing/AudioResamplerDyn.cpp @@ -201,6 +201,8 @@ AudioResamplerDyn::AudioResamplerDyn( "ro.audio.resampler.psd.stopband", mPropertyStopbandAttenuation); mPropertyCutoffPercent = property_get_int32( "ro.audio.resampler.psd.cutoff_percent", mPropertyCutoffPercent); + mPropertyTransitionBandwidthCheat = property_get_int32( + "ro.audio.resampler.psd.tbwcheat", mPropertyTransitionBandwidthCheat); } template @@ -379,9 +381,17 @@ void AudioResamplerDyn::setSampleRate(int32_t inSampleRate) halfLength = mPropertyHalfFilterLength; stopBandAtten = mPropertyStopbandAttenuation; useS32 = true; - fcr = mInSampleRate <= mSampleRate - ? 0.5 : 0.5 * mSampleRate / mInSampleRate; - fcr *= mPropertyCutoffPercent / 100.; + + // Use either the stopband location for design (tbwCheat) + // or use the 3dB cutoff location for design (fcr). + // This choice is exclusive and based on whether fcr > 0. + if (mPropertyTransitionBandwidthCheat != 0) { + tbwCheat = mPropertyTransitionBandwidthCheat / 100.; + } else { + fcr = mInSampleRate <= mSampleRate + ? 0.5 : 0.5 * mSampleRate / mInSampleRate; + fcr *= mPropertyCutoffPercent / 100.; + } } else { // Voice quality devices have lower sampling rates // (and may be a consequence of downstream AMR-WB / G.722 codecs). diff --git a/media/libaudioprocessing/AudioResamplerDyn.h b/media/libaudioprocessing/AudioResamplerDyn.h index 479142e467..82b9505b29 100644 --- a/media/libaudioprocessing/AudioResamplerDyn.h +++ b/media/libaudioprocessing/AudioResamplerDyn.h @@ -193,6 +193,13 @@ private: int32_t mPropertyCutoffPercent = 100; // "ro.audio.resampler.psd.cutoff_percent" + // Specify the transition bandwidth extension beyond Nyquist. + // If this is nonzero then mPropertyCutoffPercent is ignored. + // A value of 100 or greater is typically used, where 100 means the + // stopband is at Nyquist (this is a typical design). + int32_t mPropertyTransitionBandwidthCheat = 0; + // "ro.audio.resampler.psd.tbwcheat" + // Filter creation design parameters, see setSampleRate() double mStopbandAttenuationDb = 0.; double mPassbandRippleDb = 0.; -- GitLab From 9c58bd569a3a5e82faf6e4ef70d0f0bb6413f7e7 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 4 Apr 2019 12:13:47 -0700 Subject: [PATCH 1239/1530] Camera NDK: document not all cameras are supported Test: check html output of: frameworks/native/doc/make website (with a tweak in Doxyfile to add "__ANDROID__API__=24" to PREDEFINED) Bug: 120916880 Change-Id: Idf93a13b9bbe2974fa4256de1f3ca67b7eee1bb6 --- camera/ndk/include/camera/NdkCameraManager.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index 136a4979fc..5c810bbc5d 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -85,6 +85,11 @@ void ACameraManager_delete(ACameraManager* manager) __INTRODUCED_IN(24); *

    ACameraManager_getCameraIdList will allocate and return an {@link ACameraIdList}. * The caller must call {@link ACameraManager_deleteCameraIdList} to free the memory

    * + *

    Note: the returned camera list might be a subset to the output of + * SDK CameraManager#getCameraIdList API as the NDK API does not support some legacy camera + * hardware.

    + * * @param manager the {@link ACameraManager} of interest * @param cameraIdList the output {@link ACameraIdList} will be filled in here if the method call * succeeds. -- GitLab From 9ca01d383c067ecb15e292bd56be3cd9ed035212 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 1 Apr 2019 14:45:47 -0700 Subject: [PATCH 1240/1530] media: fix "prepend-sps-pps-to-idr-frames" behavior - Add constant. - ACodec: Check for support only for video encoders, ignore otherwise. - codec2: fix C2PrependHeaderModeSetting definition. - CCodec: fail configure if the feature is not supported. - CCodec: fix mapping between SDK and codec2 Bug: 32746212 Test: atest CtsMediaTestCases:MediaCodecTest#testPrependHeadersToSyncFrames Change-Id: I8b86b4c8c139d6addbafc1a3558c640b1add42c9 --- media/codec2/core/include/C2Config.h | 3 ++- media/codec2/sfplugin/CCodec.cpp | 13 +++++++++- media/codec2/sfplugin/CCodecConfig.cpp | 26 ++++++++++++++----- media/libstagefright/ACodec.cpp | 2 +- .../media/stagefright/MediaCodecConstants.h | 1 + 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index 9545c45375..c395d62cfe 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -1194,7 +1194,8 @@ C2ENUM(C2Config::prepend_header_mode_t, uint32_t, PREPEND_HEADER_TO_ALL_SYNC, ) -typedef C2GlobalParam +typedef C2GlobalParam, + kParamIndexPrependHeaderMode> C2PrependHeaderModeSetting; constexpr char C2_PARAMKEY_PREPEND_HEADER_MODE[] = "output.buffers.prepend-header"; diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 8474ce831a..8a2b3fe640 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -859,11 +859,12 @@ void CCodec::configure(const sp &msg) { std::vector> params; C2StreamUsageTuning::input usage(0u, 0u); C2StreamMaxBufferSizeInfo::input maxInputSize(0u, 0u); + C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE); std::initializer_list indices { }; c2_status_t c2err = comp->query( - { &usage, &maxInputSize }, + { &usage, &maxInputSize, &prepend }, indices, C2_DONT_BLOCK, ¶ms); @@ -931,6 +932,16 @@ void CCodec::configure(const sp &msg) { } } + int32_t clientPrepend; + if ((config->mDomain & Config::IS_VIDEO) + && (config->mDomain & Config::IS_ENCODER) + && msg->findInt32(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, &clientPrepend) + && clientPrepend + && (!prepend || prepend.value != PREPEND_HEADER_TO_ALL_SYNC)) { + ALOGE("Failed to set KEY_PREPEND_HEADERS_TO_SYNC_FRAMES"); + return BAD_VALUE; + } + if ((config->mDomain & (Config::IS_VIDEO | Config::IS_IMAGE))) { // propagate HDR static info to output format for both encoders and decoders // if component supports this info, we will update from component, but only the raw port, diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index ef02e74e1c..428f0322b6 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -486,19 +486,31 @@ void CCodecConfig::initializeStandardParams() { add(ConfigMapper(std::string(KEY_FEATURE_) + FEATURE_SecurePlayback, C2_PARAMKEY_SECURE_MODE, "value")); - add(ConfigMapper("prepend-sps-pps-to-idr-frames", + add(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, C2_PARAMKEY_PREPEND_HEADER_MODE, "value") .limitTo(D::ENCODER & D::VIDEO) - .withMapper([](C2Value v) -> C2Value { + .withMappers([](C2Value v) -> C2Value { int32_t value; - if (v.get(&value) && value) { - return C2Value(C2Config::PREPEND_HEADER_TO_ALL_SYNC); - } else { - return C2Value(C2Config::PREPEND_HEADER_TO_NONE); + if (v.get(&value)) { + return value ? C2Value(C2Config::PREPEND_HEADER_TO_ALL_SYNC) + : C2Value(C2Config::PREPEND_HEADER_TO_NONE); + } + return C2Value(); + }, [](C2Value v) -> C2Value { + C2Config::prepend_header_mode_t value; + using C2ValueType=typename _c2_reduce_enum_to_underlying_type::type; + if (v.get((C2ValueType *)&value)) { + switch (value) { + case C2Config::PREPEND_HEADER_TO_NONE: return 0; + case C2Config::PREPEND_HEADER_TO_ALL_SYNC: return 1; + case C2Config::PREPEND_HEADER_ON_CHANGE: [[fallthrough]]; + default: return C2Value(); + } } + return C2Value(); })); // remove when codecs switch to PARAMKEY - deprecated(ConfigMapper("prepend-sps-pps-to-idr-frames", + deprecated(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, "coding.add-csd-to-sync-frames", "value") .limitTo(D::ENCODER & D::VIDEO)); // convert to timestamp base diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index cf1a6f1393..d8de103ea9 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1776,7 +1776,7 @@ status_t ACodec::configureCodec( } int32_t prependSPSPPS = 0; - if (encoder + if (encoder && mIsVideo && msg->findInt32("prepend-sps-pps-to-idr-frames", &prependSPSPPS) && prependSPSPPS != 0) { OMX_INDEXTYPE index; diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index 8b6944bd9f..c84614ae92 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -783,6 +783,7 @@ constexpr char KEY_MIME[] = "mime"; constexpr char KEY_OPERATING_RATE[] = "operating-rate"; constexpr char KEY_OUTPUT_REORDER_DEPTH[] = "output-reorder-depth"; constexpr char KEY_PCM_ENCODING[] = "pcm-encoding"; +constexpr char KEY_PREPEND_HEADERS_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames"; constexpr char KEY_PRIORITY[] = "priority"; constexpr char KEY_PROFILE[] = "profile"; constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown"; -- GitLab From 00e1af968cd4bb83fc0d03dd696f0c0e64ea2a84 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 2 Apr 2019 15:41:49 -0700 Subject: [PATCH 1241/1530] MediaCodec: don't crash on concurrent release/allocate Bug: 128357480 Test: atest CtsMediaTestCases:MediaCodecTest#testAsyncRelease Change-Id: I814f50c43225a06c87d51d6eb57ab1957dcd517a --- media/libstagefright/MediaCodec.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index b6b7784466..a7d37e5970 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1934,6 +1934,13 @@ void MediaCodec::onMessageReceived(const sp &msg) { case kWhatComponentAllocated: { + if (mState == RELEASING || mState == UNINITIALIZED) { + // In case a kWhatError or kWhatRelease message came in and replied, + // we log a warning and ignore. + ALOGW("allocate interrupted by error or release, current state %d", + mState); + break; + } CHECK_EQ(mState, INITIALIZING); setState(INITIALIZED); mFlags |= kFlagIsComponentAllocated; -- GitLab From 892c81d52d90475d04243380fc0d8290776957a1 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Tue, 12 Mar 2019 00:56:50 -0700 Subject: [PATCH 1242/1530] Obtain Codec2 instances from hwservicemanager In libcodec2_client, the list of services listed in the manifest is now queried from hwservicemanager. The caching mechanism has to be modified accordingly. This CL also modifies the way component traits are cached---component traits are assumed to be static even if some services die and restart. Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 112362730 Bug: 121132171 Bug: 128320274 Change-Id: I312f3281565aa6f1ee54f86963672f56777f418f --- media/codec2/hidl/client/Android.bp | 2 +- media/codec2/hidl/client/client.cpp | 383 +++++++++++++----- .../hidl/client/include/codec2/hidl/client.h | 46 ++- media/codec2/sfplugin/CCodec.cpp | 2 +- 4 files changed, 299 insertions(+), 134 deletions(-) diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index a2a498d2b7..f92d1afde2 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -30,8 +30,8 @@ cc_library { ], export_shared_lib_headers: [ - "libcodec2_hidl@1.0", "libcodec2", + "libcodec2_hidl@1.0", ], } diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 0fe8376ba5..53adbbc6d9 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -21,8 +21,12 @@ #include #include +#include #include #include +#include +#include +#include #include #include @@ -39,6 +43,7 @@ #include #include #include +#include #include #include @@ -70,35 +75,181 @@ namespace /* unnamed */ { // c2_status_t value that corresponds to hwbinder transaction failure. constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED; -// List of known IComponentStore services in the decreasing order of preference. -constexpr const char* kClientNames[] = { - "default", - "software", - }; - -// Number of known IComponentStore services. -constexpr size_t kNumClients = std::extent::value; - -typedef std::array, kNumClients> ClientList; +// Returns the list of IComponentStore service names that are available on the +// device. This list is specified at the build time in manifest files. +// Note: A software service will have "_software" as a suffix. +std::vector const& getServiceNames() { + static std::vector sServiceNames{[]() { + using ::android::hardware::media::c2::V1_0::IComponentStore; + using ::android::hidl::manager::V1_2::IServiceManager; + + while (true) { + sp serviceManager = IServiceManager::getService(); + CHECK(serviceManager) << "Hardware service manager is not running."; + + // There are three categories of services based on names. + std::vector defaultNames; // Prefixed with "default" + std::vector vendorNames; // Prefixed with "vendor" + std::vector otherNames; // Others + Return transResult; + transResult = serviceManager->listManifestByInterface( + IComponentStore::descriptor, + [&defaultNames, &vendorNames, &otherNames]( + hidl_vec const& instanceNames) { + for (hidl_string const& instanceName : instanceNames) { + char const* name = instanceName.c_str(); + if (strncmp(name, "default", 7) == 0) { + defaultNames.emplace_back(name); + } else if (strncmp(name, "vendor", 6) == 0) { + vendorNames.emplace_back(name); + } else { + otherNames.emplace_back(name); + } + } + }); + if (transResult.isOk()) { + // Sort service names in each category. + std::sort(defaultNames.begin(), defaultNames.end()); + std::sort(vendorNames.begin(), vendorNames.end()); + std::sort(otherNames.begin(), otherNames.end()); + + // Concatenate the three lists in this order: default, vendor, + // other. + std::vector& names = defaultNames; + names.reserve(names.size() + vendorNames.size() + otherNames.size()); + names.insert(names.end(), + std::make_move_iterator(vendorNames.begin()), + std::make_move_iterator(vendorNames.end())); + names.insert(names.end(), + std::make_move_iterator(otherNames.begin()), + std::make_move_iterator(otherNames.end())); + + // Summarize to logcat. + if (names.empty()) { + LOG(INFO) << "No Codec2 services declared in the manifest."; + } else { + std::stringstream stringOutput; + stringOutput << "Available Codec2 services:"; + for (std::string const& name : names) { + stringOutput << " \"" << name << "\""; + } + LOG(INFO) << stringOutput.str(); + } -// Convenience methods to obtain known clients. -std::shared_ptr getClient(size_t index) { - uint32_t serviceMask = ::android::base::GetUintProperty( - "debug.media.codec2", uint32_t(0)); - return Codec2Client::CreateFromService( - kClientNames[index], - (serviceMask & (1 << index)) != 0); + return names; + } + LOG(ERROR) << "Could not retrieve the list of service instances of " + << IComponentStore::descriptor + << ". Retrying..."; + } + }()}; + return sServiceNames; } -ClientList getClientList() { - ClientList list; - for (size_t i = 0; i < list.size(); ++i) { - list[i] = getClient(i); +// 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(). +size_t getServiceIndex(char const* name) { + std::vector const& names = getServiceNames(); + size_t i = 0; + for (; i < names.size(); ++i) { + if (name == names[i]) { + break; + } } - return list; + return i; } -} // unnamed +} // unnamed namespace + +// This class caches a Codec2Client object and its component traits. The client +// will be created the first time it is needed, and it can be refreshed if the +// service dies (by calling invalidate()). The first time listComponents() is +// called from the client, the result will be cached. +class Codec2Client::Cache { + // Cached client + std::shared_ptr mClient; + mutable std::mutex mClientMutex; + + // Cached component traits + std::vector mTraits; + std::once_flag mTraitsInitializationFlag; + + // The index of the service. This is based on getServiceNames(). + size_t mIndex; + // A "valid" cache object must have its mIndex set with init(). + bool mValid{false}; + // Called by s() exactly once to initialize the cache. The index must be a + // valid index into the vector returned by getServiceNames(). Calling + // init(index) will associate the cache to the service with name + // getServiceNames()[index]. + void init(size_t index) { + mIndex = index; + mValid = true; + } + +public: + Cache() = default; + + // Initializes mClient if needed, then returns mClient. + // If the service is unavailable but listed in the manifest, this function + // will block indefinitely. + std::shared_ptr getClient() { + CHECK(mValid) << "Uninitialized cache"; + std::scoped_lock lock{mClientMutex}; + if (!mClient) { + mClient = Codec2Client::_CreateFromIndex(mIndex); + } + return mClient; + } + + // Causes a subsequent call to getClient() to create a new client. This + // function should be called after the service dies. + // + // Note: This function is called only by ForAllServices(). + void invalidate() { + CHECK(mValid) << "Uninitialized cache"; + std::scoped_lock lock{mClientMutex}; + mClient = nullptr; + } + + // Returns a list of traits for components supported by the service. This + // list is cached. + std::vector const& getTraits() { + CHECK(mValid) << "Uninitialized cache"; + std::call_once(mTraitsInitializationFlag, [this]() { + bool success{false}; + // Spin until _listComponents() is successful. + while (true) { + std::shared_ptr client = getClient(); + mTraits = client->_listComponents(&success); + if (success) { + break; + } + using namespace std::chrono_literals; + static constexpr auto kServiceRetryPeriod = 5s; + LOG(INFO) << "Failed to retrieve component traits from service " + "\"" << getServiceNames()[mIndex] << "\". " + "Retrying..."; + std::this_thread::sleep_for(kServiceRetryPeriod); + } + }); + return mTraits; + } + + // List() returns the list of all caches. + static std::vector& List() { + static std::vector sCaches{[]() { + size_t numServices = getServiceNames().size(); + std::vector caches(numServices); + for (size_t i = 0; i < numServices; ++i) { + caches[i].init(i); + } + return caches; + }()}; + return sCaches; + } +}; // Codec2ConfigurableClient @@ -439,7 +590,7 @@ struct Codec2Client::Component::HidlListener : public IComponentListener { // Codec2Client Codec2Client::Codec2Client(const sp& base, - std::string serviceName) + size_t serviceIndex) : Configurable{ [base]() -> sp { Return> transResult = @@ -450,8 +601,7 @@ Codec2Client::Codec2Client(const sp& base, }() }, mBase{base}, - mListed{false}, - mServiceName{serviceName} { + mServiceIndex{serviceIndex} { Return> transResult = base->getPoolClientManager(); if (!transResult.isOk()) { LOG(ERROR) << "getPoolClientManager -- transaction failed."; @@ -460,6 +610,10 @@ Codec2Client::Codec2Client(const sp& base, } } +std::string const& Codec2Client::getServiceName() const { + return getServiceNames()[mServiceIndex]; +} + c2_status_t Codec2Client::createComponent( const C2String& name, const std::shared_ptr& listener, @@ -558,33 +712,38 @@ c2_status_t Codec2Client::createInputSurface( return status; } -const std::vector& Codec2Client::listComponents() const { - std::lock_guard lock(mMutex); - if (mListed) { - return mTraitsList; - } +std::vector const& Codec2Client::listComponents() const { + return Cache::List()[mServiceIndex].getTraits(); +} + +std::vector Codec2Client::_listComponents( + bool* success) const { + std::vector traits; + std::string const& serviceName = getServiceName(); Return transStatus = mBase->listComponents( - [this](Status s, + [&traits, &serviceName](Status s, const hidl_vec& t) { if (s != Status::OK) { - LOG(DEBUG) << "listComponents -- call failed: " + LOG(DEBUG) << "_listComponents -- call failed: " << static_cast(s) << "."; return; } - mTraitsList.resize(t.size()); + traits.resize(t.size()); for (size_t i = 0; i < t.size(); ++i) { - if (!objcpy(&mTraitsList[i], t[i])) { - LOG(ERROR) << "listComponents -- corrupted output."; + if (!objcpy(&traits[i], t[i])) { + LOG(ERROR) << "_listComponents -- corrupted output."; return; } - mTraitsList[i].owner = mServiceName; + traits[i].owner = serviceName; } }); if (!transStatus.isOk()) { - LOG(ERROR) << "listComponents -- transaction failed."; + LOG(ERROR) << "_listComponents -- transaction failed."; + *success = false; + } else { + *success = true; } - mListed = true; - return mTraitsList; + return traits; } c2_status_t Codec2Client::copyBuffer( @@ -649,34 +808,29 @@ std::shared_ptr }; std::shared_ptr Codec2Client::CreateFromService( - const char* serviceName, bool waitForService) { - if (!serviceName) { - return nullptr; - } - sp baseStore = waitForService ? - Base::getService(serviceName) : - Base::tryGetService(serviceName); - if (!baseStore) { - if (waitForService) { - LOG(WARNING) << "Codec2.0 service \"" << serviceName << "\"" - " inaccessible. Check the device manifest."; - } else { - LOG(DEBUG) << "Codec2.0 service \"" << serviceName << "\"" - " unavailable at the moment. " - " Wait or check the device manifest."; - } - return nullptr; - } - return std::make_shared(baseStore, serviceName); + const char* name) { + size_t index = getServiceIndex(name); + return index == getServiceNames().size() ? + nullptr : _CreateFromIndex(index); } -c2_status_t Codec2Client::ForAllStores( +std::shared_ptr Codec2Client::_CreateFromIndex(size_t index) { + std::string const& name = getServiceNames()[index]; + LOG(INFO) << "Creating a Codec2 client to service \"" << name << "\""; + sp baseStore = Base::getService(name); + CHECK(baseStore) << "Codec2 service \"" << name << "\"" + " inaccessible for unknown reasons."; + LOG(INFO) << "Client to Codec2 service \"" << name << "\" created"; + return std::make_shared(baseStore, index); +} + +c2_status_t Codec2Client::ForAllServices( const std::string &key, std::function&)> predicate) { c2_status_t status = C2_NO_INIT; // no IComponentStores present - // Cache the mapping key -> index of Codec2Client in getClient(). + // Cache the mapping key -> index of Codec2Client in Cache::List(). static std::mutex key2IndexMutex; static std::map key2Index; @@ -684,33 +838,36 @@ c2_status_t Codec2Client::ForAllStores( // the last known client fails, retry once. We do this by pushing the last // known client in front of the list of all clients. std::deque indices; - for (size_t index = kNumClients; index > 0; ) { + for (size_t index = Cache::List().size(); index > 0; ) { indices.push_front(--index); } bool wasMapped = false; - std::unique_lock lock(key2IndexMutex); - auto it = key2Index.find(key); - if (it != key2Index.end()) { - indices.push_front(it->second); - wasMapped = true; + { + std::scoped_lock lock{key2IndexMutex}; + auto it = key2Index.find(key); + if (it != key2Index.end()) { + indices.push_front(it->second); + wasMapped = true; + } } - lock.unlock(); for (size_t index : indices) { - std::shared_ptr client = getClient(index); + Cache& cache = Cache::List()[index]; + std::shared_ptr client{cache.getClient()}; if (client) { status = predicate(client); if (status == C2_OK) { - lock.lock(); + std::scoped_lock lock{key2IndexMutex}; key2Index[key] = index; // update last known client index - return status; + return C2_OK; } } if (wasMapped) { LOG(INFO) << "Could not find \"" << key << "\"" " in the last instance. Retrying..."; wasMapped = false; + cache.invalidate(); } } return status; // return the last status from a valid client @@ -722,7 +879,7 @@ std::shared_ptr const std::shared_ptr& listener, std::shared_ptr* owner) { std::shared_ptr component; - c2_status_t status = ForAllStores( + c2_status_t status = ForAllServices( componentName, [owner, &component, componentName, &listener]( const std::shared_ptr &client) @@ -755,7 +912,7 @@ std::shared_ptr const char* interfaceName, std::shared_ptr* owner) { std::shared_ptr interface; - c2_status_t status = ForAllStores( + c2_status_t status = ForAllServices( interfaceName, [owner, &interface, interfaceName]( const std::shared_ptr &client) @@ -782,50 +939,54 @@ std::shared_ptr return interface; } -std::shared_ptr Codec2Client::CreateInputSurface() { - uint32_t serviceMask = ::android::base::GetUintProperty( +std::vector const& Codec2Client::ListComponents() { + static std::vector sList{[]() { + std::vector list; + for (Cache& cache : Cache::List()) { + std::vector const& traits = cache.getTraits(); + list.insert(list.end(), traits.begin(), traits.end()); + } + return list; + }()}; + return sList; +} + +std::shared_ptr Codec2Client::CreateInputSurface( + char const* serviceName) { + uint32_t inputSurfaceSetting = ::android::base::GetUintProperty( "debug.stagefright.c2inputsurface", uint32_t(0)); - for (size_t i = 0; i < kNumClients; ++i) { - if ((1 << i) & serviceMask) { - std::shared_ptr client = getClient(i); - std::shared_ptr inputSurface; - if (client && - client->createInputSurface(&inputSurface) == C2_OK && - inputSurface) { - return inputSurface; - } + if (inputSurfaceSetting == 0) { + return nullptr; + } + size_t index = getServiceNames().size(); + if (serviceName) { + index = getServiceIndex(serviceName); + if (index == getServiceNames().size()) { + LOG(DEBUG) << "CreateInputSurface -- invalid service name: \"" + << serviceName << "\""; } } - LOG(INFO) << "Could not create an input surface " - "from any Codec2.0 services."; - return nullptr; -} -const std::vector& Codec2Client::ListComponents() { - static std::vector traitsList = [](){ - std::vector list; - size_t listSize = 0; - ClientList clientList = getClientList(); - for (const std::shared_ptr& client : clientList) { - if (!client) { - continue; - } - listSize += client->listComponents().size(); + std::shared_ptr inputSurface; + if (index != getServiceNames().size()) { + std::shared_ptr client = Cache::List()[index].getClient(); + if (client->createInputSurface(&inputSurface) == C2_OK) { + return inputSurface; } - list.reserve(listSize); - for (const std::shared_ptr& client : clientList) { - if (!client) { - continue; - } - list.insert( - list.end(), - client->listComponents().begin(), - client->listComponents().end()); + } + LOG(INFO) << "CreateInputSurface -- attempting to create an input surface " + "from all services..."; + for (Cache& cache : Cache::List()) { + std::shared_ptr client = cache.getClient(); + if (client->createInputSurface(&inputSurface) == C2_OK) { + LOG(INFO) << "CreateInputSurface -- input surface obtained from " + "service \"" << client->getServiceName() << "\""; + return inputSurface; } - return list; - }(); - - return traitsList; + } + LOG(WARNING) << "CreateInputSurface -- failed to create an input surface " + "from all services"; + return nullptr; } // Codec2Client::Listener diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index cd422056d3..8265380bf9 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -144,53 +144,52 @@ struct Codec2Client : public Codec2ConfigurableClient { typedef Codec2Client Store; - std::string getServiceName() const { return mServiceName; } + std::string const& getServiceName() const; c2_status_t createComponent( - const C2String& name, - const std::shared_ptr& listener, + C2String const& name, + std::shared_ptr const& listener, std::shared_ptr* const component); c2_status_t createInterface( - const C2String& name, + C2String const& name, std::shared_ptr* const interface); c2_status_t createInputSurface( std::shared_ptr* const inputSurface); - const std::vector& listComponents() const; + std::vector const& listComponents() const; c2_status_t copyBuffer( - const std::shared_ptr& src, - const std::shared_ptr& dst); + std::shared_ptr const& src, + std::shared_ptr const& dst); std::shared_ptr getParamReflector(); - static std::shared_ptr CreateFromService( - const char* serviceName, - bool waitForService = true); + static std::shared_ptr CreateFromService(char const* name); // Try to create a component with a given name from all known // IComponentStore services. static std::shared_ptr CreateComponentByName( - const char* componentName, - const std::shared_ptr& listener, + char const* componentName, + std::shared_ptr const& listener, std::shared_ptr* owner = nullptr); // Try to create a component interface with a given name from all known // IComponentStore services. static std::shared_ptr CreateInterfaceByName( - const char* interfaceName, + char const* interfaceName, std::shared_ptr* owner = nullptr); // List traits from all known IComponentStore services. - static const std::vector& ListComponents(); + static std::vector const& ListComponents(); // Create an input surface. - static std::shared_ptr CreateInputSurface(); + static std::shared_ptr CreateInputSurface( + char const* serviceName = nullptr); // base cannot be null. - Codec2Client(const sp& base, std::string serviceName); + Codec2Client(sp const& base, size_t serviceIndex); protected: sp mBase; @@ -198,17 +197,22 @@ protected: // Finds the first store where the predicate returns OK, and returns the last // predicate result. Uses key to remember the last store found, and if cached, // it tries that store before trying all stores (one retry). - static c2_status_t ForAllStores( + static c2_status_t ForAllServices( const std::string& key, - std::function&)> predicate); + std::function const&)> + predicate); - mutable std::mutex mMutex; - mutable bool mListed; - std::string mServiceName; + size_t mServiceIndex; mutable std::vector mTraitsList; sp<::android::hardware::media::bufferpool::V2_0::IClientManager> mHostPoolManager; + + static std::shared_ptr _CreateFromIndex(size_t index); + + std::vector _listComponents(bool* success) const; + + class Cache; }; struct Codec2Client::Interface : public Codec2Client::Configurable { diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 8474ce831a..89088521c8 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -600,7 +600,7 @@ void CCodec::allocate(const sp &codecInfo) { std::shared_ptr client; // set up preferred component store to access vendor store parameters - client = Codec2Client::CreateFromService("default", false); + client = Codec2Client::CreateFromService("default"); if (client) { ALOGI("setting up '%s' as default (vendor) store", client->getServiceName().c_str()); SetPreferredCodec2ComponentStore( -- GitLab From 62899df9cf57f974532acecf6eb11872eb54352e Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 2 Apr 2019 16:53:34 -0700 Subject: [PATCH 1243/1530] camera2 ndk: Move check for ndk support for capabilities from a white-list to a black-list. Bug: 129979644 Test: Fake SECURE_IMAGE_DATA by updating camera characteristics in cameraserver; Retrieve capabilities in AImageReader test and confirm that SECURE_IMAGE_DATA is supported Test: AImageReaderVendorTest Change-Id: Ia105969ce1df52408e6b7663a658d89f47cd90c2 Signed-off-by: Jayant Chowdhary --- camera/ndk/impl/ACameraMetadata.cpp | 16 +++------------- camera/ndk/impl/ACameraMetadata.h | 2 ++ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp index d832deb028..77dcd488d9 100644 --- a/camera/ndk/impl/ACameraMetadata.cpp +++ b/camera/ndk/impl/ACameraMetadata.cpp @@ -47,24 +47,14 @@ ACameraMetadata::ACameraMetadata(camera_metadata_t* buffer, ACAMERA_METADATA_TYP bool ACameraMetadata::isNdkSupportedCapability(int32_t capability) { switch (capability) { - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT: - case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA: - return true; case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING: case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING: case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO: return false; default: - // Newly defined capabilities will be unsupported by default (blacklist) - // TODO: Should we do whitelist or blacklist here? - ALOGE("%s: Unknonwn capability %d", __FUNCTION__, capability); - return false; + // Assuming every capability passed to this function is actually a + // valid capability. + return true; } } diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h index 3d895cb21f..97f7f48c1c 100644 --- a/camera/ndk/impl/ACameraMetadata.h +++ b/camera/ndk/impl/ACameraMetadata.h @@ -70,6 +70,8 @@ struct ACameraMetadata : public RefBase { private: + // This function does not check whether the capability passed to it is valid. + // The caller must make sure that it is. bool isNdkSupportedCapability(const int32_t capability); static inline bool isVendorTag(const uint32_t tag); static bool isCaptureRequestTag(const uint32_t tag); -- GitLab From e1226f5b2a0318fc65c56dfd700a381822cf2629 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 4 Apr 2019 15:24:41 -0700 Subject: [PATCH 1244/1530] codec2 components: merge WorkQueue and PendingWork Bug: 126621696 Test: /data/nativetest64/VtsHidlC2V1_0TargetAudioDecTest/VtsHidlC2V1_0TargetAudioDecTest --gtest_repeat=50 -I software -C c2.android.aac.decoder -P /sdcard/res/ Change-Id: I78b627b083821be313e67fdba6958c9af369687a --- .../components/base/SimpleC2Component.cpp | 80 ++++++++----------- .../base/include/SimpleC2Component.h | 7 +- 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index 44f1fe0677..fb3fbd05e3 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -272,12 +272,9 @@ c2_status_t SimpleC2Component::flush_sm( flushedWork->push_back(std::move(work)); } } - } - { - Mutexed::Locked pending(mPendingWork); - while (!pending->empty()) { - flushedWork->push_back(std::move(pending->begin()->second)); - pending->erase(pending->begin()); + while (!queue->pending().empty()) { + flushedWork->push_back(std::move(queue->pending().begin()->second)); + queue->pending().erase(queue->pending().begin()); } } @@ -342,10 +339,7 @@ c2_status_t SimpleC2Component::stop() { { Mutexed::Locked queue(mWorkQueue); queue->clear(); - } - { - Mutexed::Locked pending(mPendingWork); - pending->clear(); + queue->pending().clear(); } sp reply; (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply); @@ -366,10 +360,7 @@ c2_status_t SimpleC2Component::reset() { { Mutexed::Locked queue(mWorkQueue); queue->clear(); - } - { - Mutexed::Locked pending(mPendingWork); - pending->clear(); + queue->pending().clear(); } sp reply; (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply); @@ -401,13 +392,13 @@ void SimpleC2Component::finish( uint64_t frameIndex, std::function &)> fillWork) { std::unique_ptr work; { - Mutexed::Locked pending(mPendingWork); - if (pending->count(frameIndex) == 0) { + Mutexed::Locked queue(mWorkQueue); + if (queue->pending().count(frameIndex) == 0) { ALOGW("unknown frame index: %" PRIu64, frameIndex); return; } - work = std::move(pending->at(frameIndex)); - pending->erase(frameIndex); + work = std::move(queue->pending().at(frameIndex)); + queue->pending().erase(frameIndex); } if (work) { fillWork(work); @@ -426,13 +417,13 @@ void SimpleC2Component::cloneAndSend( work->input.flags = currentWork->input.flags; work->input.ordinal = currentWork->input.ordinal; } else { - Mutexed::Locked pending(mPendingWork); - if (pending->count(frameIndex) == 0) { + Mutexed::Locked queue(mWorkQueue); + if (queue->pending().count(frameIndex) == 0) { ALOGW("unknown frame index: %" PRIu64, frameIndex); return; } - work->input.flags = pending->at(frameIndex)->input.flags; - work->input.ordinal = pending->at(frameIndex)->input.ordinal; + work->input.flags = queue->pending().at(frameIndex)->input.flags; + work->input.ordinal = queue->pending().at(frameIndex)->input.ordinal; } work->worklets.emplace_back(new C2Worklet); if (work) { @@ -552,24 +543,21 @@ bool SimpleC2Component::processQueue() { } process(work, mOutputBlockPool); ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku()); - { - Mutexed::Locked queue(mWorkQueue); - if (queue->generation() != generation) { - ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64, - queue->generation(), generation); - work->result = C2_NOT_FOUND; - queue.unlock(); - { - Mutexed::Locked state(mExecState); - std::shared_ptr listener = state->mListener; - state.unlock(); - listener->onWorkDone_nb(shared_from_this(), vec(work)); - } - queue.lock(); - return hasQueuedWork; - } + Mutexed::Locked queue(mWorkQueue); + if (queue->generation() != generation) { + ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64, + queue->generation(), generation); + work->result = C2_NOT_FOUND; + queue.unlock(); + + Mutexed::Locked state(mExecState); + std::shared_ptr listener = state->mListener; + state.unlock(); + listener->onWorkDone_nb(shared_from_this(), vec(work)); + return hasQueuedWork; } if (work->workletsProcessed != 0u) { + queue.unlock(); Mutexed::Locked state(mExecState); ALOGV("returning this work"); std::shared_ptr listener = state->mListener; @@ -579,15 +567,15 @@ bool SimpleC2Component::processQueue() { ALOGV("queue pending work"); work->input.buffers.clear(); std::unique_ptr unexpected; - { - Mutexed::Locked pending(mPendingWork); - uint64_t frameIndex = work->input.ordinal.frameIndex.peeku(); - if (pending->count(frameIndex) != 0) { - unexpected = std::move(pending->at(frameIndex)); - pending->erase(frameIndex); - } - (void)pending->insert({ frameIndex, std::move(work) }); + + uint64_t frameIndex = work->input.ordinal.frameIndex.peeku(); + if (queue->pending().count(frameIndex) != 0) { + unexpected = std::move(queue->pending().at(frameIndex)); + queue->pending().erase(frameIndex); } + (void)queue->pending().insert({ frameIndex, std::move(work) }); + + queue.unlock(); if (unexpected) { ALOGD("unexpected pending work"); unexpected->result = C2_CORRUPTED; diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h index 43029a9b48..22d5714995 100644 --- a/media/codec2/components/base/include/SimpleC2Component.h +++ b/media/codec2/components/base/include/SimpleC2Component.h @@ -202,6 +202,8 @@ private: class WorkQueue { public: + typedef std::unordered_map> PendingWork; + inline WorkQueue() : mFlush(false), mGeneration(0ul) {} inline uint64_t generation() const { return mGeneration; } @@ -218,6 +220,7 @@ private: return flush; } void clear(); + PendingWork &pending() { return mPendingWork; } private: struct Entry { @@ -228,12 +231,10 @@ private: bool mFlush; uint64_t mGeneration; std::list mQueue; + PendingWork mPendingWork; }; Mutexed mWorkQueue; - typedef std::unordered_map> PendingWork; - Mutexed mPendingWork; - class BlockingBlockPool; std::shared_ptr mOutputBlockPool; -- GitLab From 0d87629d8d37d8d1df87b86ab8f7e9b5850f5124 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 4 Apr 2019 14:29:08 -0700 Subject: [PATCH 1245/1530] Update version numbers to 0210000000 Update version number to 0210000000. The Beta 2 builds (which are in dogfood an public beta) will receive modules via the Play Store with version numbers 0200000000. Hence, we must bump the version in qt-dev and hence beta 3. This is so that prebuilts in the beta 3 OTA will be higher than any Play Store pushes on beta 2. We are bumping it by 10000000 so that we have name space to do intermediate cherrypick builds if needed. Bug: 129977903 Change-Id: I4f608a2bc0739b8b0fd4eefa1ec1223f977431cd --- apex/manifest.json | 2 +- apex/manifest_codec.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/manifest.json b/apex/manifest.json index c6c63f6965..cee94e26f9 100644 --- a/apex/manifest.json +++ b/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.media", - "version": 200000000 + "version": 210000000 } diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json index 4f31b1517c..b83e65aaa5 100644 --- a/apex/manifest_codec.json +++ b/apex/manifest_codec.json @@ -1,4 +1,4 @@ { "name": "com.android.media.swcodec", - "version": 200000000 + "version": 210000000 } -- GitLab From 94114a21c855fb7525cf47fcfe965bf0735e19f1 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 1 Apr 2019 19:38:23 -0700 Subject: [PATCH 1246/1530] APC: Disable MMAP when a stream is to be captured As MMAP does not support APC, it is disabled when a render loopback policy is installed. Test: adb shell write_sine_callback -pl # mmap is used adb shell audiorecorder --target /data/file1.raw & adb shell write_sine_callback -pl # mmap is NOT used Bug: 129948989 Change-Id: Ia90c24953890de7e19fd99485bf760788f81126d Signed-off-by: Kevin Rocard (cherry picked from commit c1dde631401a9cda122cf3058a99e36274906801) --- .../include/AudioPolicyMix.h | 7 +++++-- .../managerdefinitions/src/AudioPolicyMix.cpp | 20 +++++++++++++++---- .../managerdefault/AudioPolicyManager.cpp | 15 +++++++------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 7a9c26eddf..f2b51d982c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -65,10 +65,13 @@ public: * @param[in] attributes to consider fowr the research of output descriptor. * @param[out] desc to return if an primary output could be found. * @param[out] secondaryDesc other desc that the audio should be routed to. + * @return OK if the request is valid + * otherwise if the request is not supported */ status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid, - sp &primaryDesc, - std::vector> *secondaryDescs); + audio_output_flags_t flags, + sp &primaryDesc, + std::vector> *secondaryDescs); sp getDeviceAndMixForInputSource(audio_source_t inputSource, const DeviceVector &availableDeviceTypes, diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 1b812c0ff5..26bb354284 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -126,21 +126,33 @@ void AudioPolicyMixCollection::closeOutput(sp &desc) } status_t AudioPolicyMixCollection::getOutputForAttr( - const audio_attributes_t& attributes, uid_t uid, sp &primaryDesc, + const audio_attributes_t& attributes, uid_t uid, + audio_output_flags_t flags, + sp &primaryDesc, std::vector> *secondaryDescs) { ALOGV("getOutputForAttr() querying %zu mixes:", size()); primaryDesc = 0; for (size_t i = 0; i < size(); i++) { sp policyMix = valueAt(i); + const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags); + if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) { + // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with + // the current MmapStreamInterface::start to reject a specific client added to a shared + // mmap stream. + // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render + // policy is present. That ensures no shared mmap stream is used when an loopback + // render policy is registered. + ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__); + return INVALID_OPERATION; + } + sp policyDesc = policyMix->getOutput(); if (!policyDesc) { ALOGV("%s: Skiping %zu: Mix has no output", __func__, i); continue; } - const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags); - if (primaryOutputMix && primaryDesc != 0) { ALOGV("%s: Skiping %zu: Primary output already found", __func__, i); continue; // Primary output already found @@ -170,7 +182,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr( } } } - return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR; + return NO_ERROR; } AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch( diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index bd53f0ffdb..4d121a094c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -963,10 +963,11 @@ status_t AudioPolicyManager::getOutputForAttrInt( // otherwise, fallback to the dynamic policies, if none match, query the engine. // Secondary outputs are always found by dynamic policies as the engine do not support them sp policyDesc; - if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) { - policyDesc = nullptr; // reset getOutputForAttr in case of failure - secondaryDescs->clear(); + status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, policyDesc, secondaryDescs); + if (status != OK) { + return status; } + // Explicit routing is higher priority then any dynamic policy primary output bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr; @@ -5064,12 +5065,12 @@ void AudioPolicyManager::checkSecondaryOutputs() { for (size_t i = 0; i < mOutputs.size(); i++) { const sp& outputDescriptor = mOutputs[i]; for (const sp& client : outputDescriptor->getClientIterable()) { - // FIXME code duplicated from getOutputForAttrInt sp desc; std::vector> secondaryDescs; - mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc, - &secondaryDescs); - if (!std::equal(client->getSecondaryOutputs().begin(), + status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), + client->flags(), desc, &secondaryDescs); + if (status != OK || + !std::equal(client->getSecondaryOutputs().begin(), client->getSecondaryOutputs().end(), secondaryDescs.begin(), secondaryDescs.end())) { streamsToInvalidate.insert(client->stream()); -- GitLab From 03a8b52d0a79c72b32c851713d3b69c90ccb530a Mon Sep 17 00:00:00 2001 From: Sundong Ahn Date: Wed, 3 Apr 2019 11:29:05 +0900 Subject: [PATCH 1247/1530] Support multiple If the parent element is multiple even though the element is not multiple, it must be set to multiple. As a result, the element is changed to multiple and the APIs are changed. Bug: 128380795 Test: m -j && make check-api Merged-In: Ifce1e86a91c23eee5ec945b36c7fc97a5db26c8a Change-Id: Ifce1e86a91c23eee5ec945b36c7fc97a5db26c8a (cherry picked from commit 85fc3f4718e3d0a4f524e1f4d0fb214525f0d2a4) --- media/libstagefright/xmlparser/api/current.txt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt index 5443f2c201..f7f4c36eb4 100644 --- a/media/libstagefright/xmlparser/api/current.txt +++ b/media/libstagefright/xmlparser/api/current.txt @@ -37,13 +37,10 @@ package media.codecs { public class Included { ctor public Included(); - method public media.codecs.Decoders getDecoders_optional(); - method public media.codecs.Encoders getEncoders_optional(); + method public java.util.List getDecoders_optional(); + method public java.util.List getEncoders_optional(); method public java.util.List getInclude_optional(); - method public media.codecs.Settings getSettings_optional(); - method public void setDecoders_optional(media.codecs.Decoders); - method public void setEncoders_optional(media.codecs.Encoders); - method public void setSettings_optional(media.codecs.Settings); + method public java.util.List getSettings_optional(); } public class Limit { @@ -85,13 +82,10 @@ package media.codecs { public class MediaCodecs { ctor public MediaCodecs(); - method public media.codecs.Decoders getDecoders_optional(); - method public media.codecs.Encoders getEncoders_optional(); + method public java.util.List getDecoders_optional(); + method public java.util.List getEncoders_optional(); method public java.util.List getInclude_optional(); - method public media.codecs.Settings getSettings_optional(); - method public void setDecoders_optional(media.codecs.Decoders); - method public void setEncoders_optional(media.codecs.Encoders); - method public void setSettings_optional(media.codecs.Settings); + method public java.util.List getSettings_optional(); } public class Quirk { -- GitLab From 03de478873adc5679f4f74b0852e35a0e9d07249 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 5 Apr 2019 14:37:24 +0200 Subject: [PATCH 1248/1530] Bump mediacodec test apex version. The system version recently bumped to 200000000, which caused failures in tests, because we aren't allowing downgrades. Bump to 300000000 to make sure the tests pass again. Bug: 129093117 Test: atest media_swcodec_e2e_tests Change-Id: Icc84f4a0f749916aded6746923f0d82b2b9dc473 --- apex/testing/test_manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apex/testing/test_manifest.json b/apex/testing/test_manifest.json index 9f81f9fe27..ddd642ed3c 100644 --- a/apex/testing/test_manifest.json +++ b/apex/testing/test_manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.media", - "version": 2 + "version": 300000000 } -- GitLab From e5cccd7f34ae0d280d4f88eef8be4a34c3df7907 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 5 Apr 2019 17:51:28 +0200 Subject: [PATCH 1249/1530] Add apex TEST_MAPPING to frameworks/av/apex. To make sure we catch certain breakages related to the media codec (test) APEX at presubmit. Bug: 129996486 Test: run atest in frameworks/av/apex Change-Id: Icf5933aee02ebad80c709ec3de340bbc733a14bd --- apex/TEST_MAPPING | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 apex/TEST_MAPPING diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING new file mode 100644 index 0000000000..a2e98cc4bc --- /dev/null +++ b/apex/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "system/apex/tests" + } + ] +} -- GitLab From 482f60ccccc12e6afe24f9ba503496ce65d1ba43 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 21 Mar 2019 16:41:28 -0700 Subject: [PATCH 1250/1530] Remove direct libgui reference from omx server side - Make mediacodec service/libstagefright_omx not dependent on libgui. Server should only reference bqhelper (which currently still have a dependency on libgui, but could be removed later as it's only used internally). - Make libmedia_omx not dependent on libgui by moving the IOMX wrapper code into a separately that's only used by the platform. - Move ConsumerListener references in GraphicBufferSource to implementation only, so that other libs including GraphicBufferSource doesn't need to link libgui. - Remove bqhelper references from some client side libs. - Remove IOMX ipc related code, as IOMX interface is no longer used for ipc. It's only used as an interface on client side. bug: 129272021 test: builds; atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I49d80bf72eba325ee80b223d4a5ba7b0c1ab4d35 --- media/codec2/hidl/client/Android.bp | 1 - media/codec2/sfplugin/Android.bp | 1 - media/libmedia/Android.bp | 75 +++++++- media/libmedia/IOMX.cpp | 151 --------------- media/libmedia/include/media/IOMX.h | 11 +- .../include/media/omx/1.0/Conversion.h | 6 +- media/libmedia/include/media/omx/1.0/WOmx.h | 3 +- media/libmediaplayer2/Android.bp | 1 + media/libstagefright/Android.bp | 1 + .../bqhelper/GraphicBufferSource.cpp | 24 ++- .../bqhelper/GraphicBufferSource.h | 9 +- media/libstagefright/omx/Android.bp | 3 - .../omx/BWGraphicBufferSource.cpp | 180 ------------------ .../stagefright/omx/BWGraphicBufferSource.h | 64 ------- media/ndk/Android.bp | 1 - media/ndk/NdkImageReader.cpp | 2 +- services/mediacodec/Android.mk | 1 - services/mediacodec/main_codecservice.cpp | 1 - 18 files changed, 105 insertions(+), 430 deletions(-) delete mode 100644 media/libstagefright/omx/BWGraphicBufferSource.cpp delete mode 100644 media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index f92d1afde2..965e438aa1 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -20,7 +20,6 @@ cc_library { "libhidltransport", "liblog", "libstagefright_bufferpool@2.0", - "libstagefright_bufferqueue_helper", "libui", "libutils", ], diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index a212651570..66457e7e3b 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -40,7 +40,6 @@ cc_library_shared { "libmedia", "libmedia_omx", "libsfplugin_ccodec_utils", - "libstagefright_bufferqueue_helper", "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx_utils", diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index a529628d83..1d335901c5 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -65,7 +65,6 @@ cc_library_shared { "MediaCodecInfo.cpp", "OMXBuffer.cpp", "omx/1.0/WGraphicBufferSource.cpp", - "omx/1.0/WOmx.cpp", "omx/1.0/WOmxBufferSource.cpp", "omx/1.0/WOmxNode.cpp", "omx/1.0/WOmxObserver.cpp", @@ -75,13 +74,16 @@ cc_library_shared { local_include_dirs: ["aidl"], export_aidl_headers: true, }, + + local_include_dirs: [ + "include", + ], shared_libs: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", "libbinder", "libcutils", - "libgui", "libhidlbase", "libhidltransport", "liblog", @@ -93,21 +95,84 @@ cc_library_shared { export_shared_lib_headers: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", - "libgui", "libstagefright_foundation", "libui", ], header_libs: [ - "libmedia_headers", + "libstagefright_headers", + "media_plugin_headers", ], export_header_lib_headers: [ - "libmedia_headers", + "libstagefright_headers", + "media_plugin_headers", ], export_include_dirs: [ "aidl", + "include", + ], + + cflags: [ + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wall", + ], + + sanitize: { + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + cfi: true, + }, +} + + +cc_library_shared { + name: "libmedia_omx_client", + + srcs: [ + "omx/1.0/WOmx.cpp", + ], + + local_include_dirs: [ + "include", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libgui", + "libhidlbase", + "libhidltransport", + "liblog", + "libmedia_omx", + "libstagefright_foundation", + "libui", + "libutils", + ], + + export_shared_lib_headers: [ + "libgui", + "libmedia_omx", + "libstagefright_foundation", + "libui", + ], + + header_libs: [ + "libstagefright_headers", + "media_plugin_headers", + ], + + export_header_lib_headers: [ + "libstagefright_headers", + "media_plugin_headers", + ], + + export_include_dirs: [ + "include", ], cflags: [ diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 747b88f6e4..bc0c2cd917 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -62,79 +61,6 @@ enum { SET_QUIRKS, }; -class BpOMX : public BpInterface { -public: - explicit BpOMX(const sp &impl) - : BpInterface(impl) { - } - - virtual status_t listNodes(List *list) { - list->clear(); - - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - remote()->transact(LIST_NODES, data, &reply); - - int32_t n = reply.readInt32(); - for (int32_t i = 0; i < n; ++i) { - list->push_back(ComponentInfo()); - ComponentInfo &info = *--list->end(); - - info.mName = reply.readString8(); - int32_t numRoles = reply.readInt32(); - for (int32_t j = 0; j < numRoles; ++j) { - info.mRoles.push_back(reply.readString8()); - } - } - - return OK; - } - - virtual status_t allocateNode( - const char *name, const sp &observer, - sp *omxNode) { - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - data.writeCString(name); - data.writeStrongBinder(IInterface::asBinder(observer)); - remote()->transact(ALLOCATE_NODE, data, &reply); - - status_t err = reply.readInt32(); - if (err == OK) { - *omxNode = IOMXNode::asInterface(reply.readStrongBinder()); - } else { - omxNode->clear(); - } - - return err; - } - - virtual status_t createInputSurface( - sp *bufferProducer, - sp *bufferSource) { - Parcel data, reply; - status_t err; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply); - if (err != OK) { - ALOGW("binder transaction failed: %d", err); - return err; - } - - err = reply.readInt32(); - if (err != OK) { - return err; - } - - *bufferProducer = IGraphicBufferProducer::asInterface( - reply.readStrongBinder()); - *bufferSource = IGraphicBufferSource::asInterface( - reply.readStrongBinder()); - - return err; - } -}; - class BpOMXNode : public BpInterface { public: explicit BpOMXNode(const sp &impl) @@ -551,7 +477,6 @@ public: } }; -IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); //////////////////////////////////////////////////////////////////////////////// @@ -562,82 +487,6 @@ IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); return PERMISSION_DENIED; \ } } while (0) -status_t BnOMX::onTransact( - uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { - switch (code) { - case LIST_NODES: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - List list; - listNodes(&list); - - reply->writeInt32(list.size()); - for (List::iterator it = list.begin(); - it != list.end(); ++it) { - ComponentInfo &cur = *it; - - reply->writeString8(cur.mName); - reply->writeInt32(cur.mRoles.size()); - for (List::iterator role_it = cur.mRoles.begin(); - role_it != cur.mRoles.end(); ++role_it) { - reply->writeString8(*role_it); - } - } - - return NO_ERROR; - } - - case ALLOCATE_NODE: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - const char *name = data.readCString(); - - sp observer = - interface_cast(data.readStrongBinder()); - - if (name == NULL || observer == NULL) { - ALOGE("b/26392700"); - reply->writeInt32(INVALID_OPERATION); - return NO_ERROR; - } - - sp omxNode; - - status_t err = allocateNode(name, observer, &omxNode); - - reply->writeInt32(err); - if (err == OK) { - reply->writeStrongBinder(IInterface::asBinder(omxNode)); - } - - return NO_ERROR; - } - - case CREATE_INPUT_SURFACE: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - sp bufferProducer; - sp bufferSource; - status_t err = createInputSurface(&bufferProducer, &bufferSource); - - reply->writeInt32(err); - - if (err == OK) { - reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); - reply->writeStrongBinder(IInterface::asBinder(bufferSource)); - } - - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - status_t BnOMXNode::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h index e69c02dda1..7e7c2d23dc 100644 --- a/media/libmedia/include/media/IOMX.h +++ b/media/libmedia/include/media/IOMX.h @@ -47,9 +47,8 @@ struct omx_message; using hardware::media::omx::V1_0::IOmxNode; -class IOMX : public IInterface { +class IOMX : public RefBase { public: - DECLARE_META_INTERFACE(OMX); typedef uint32_t buffer_id; @@ -224,14 +223,6 @@ public: }; //////////////////////////////////////////////////////////////////////////////// - -class BnOMX : public BnInterface { -public: - virtual status_t onTransact( - uint32_t code, const Parcel &data, Parcel *reply, - uint32_t flags = 0); -}; - class BnOMXNode : public BnInterface { public: virtual status_t onTransact( diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h index 3700a237f1..babda222bf 100644 --- a/media/libmedia/include/media/omx/1.0/Conversion.h +++ b/media/libmedia/include/media/omx/1.0/Conversion.h @@ -31,12 +31,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include @@ -282,8 +282,8 @@ inline Status toStatus(status_t l) { case TIMED_OUT: case ERROR_UNSUPPORTED: case UNKNOWN_ERROR: - case IGraphicBufferProducer::RELEASE_ALL_BUFFERS: - case IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION: + case BufferQueueDefs::RELEASE_ALL_BUFFERS: + case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION: return static_cast(l); case NOT_ENOUGH_DATA: return Status::BUFFER_NEEDS_REALLOCATION; diff --git a/media/libmedia/include/media/omx/1.0/WOmx.h b/media/libmedia/include/media/omx/1.0/WOmx.h index f13546e1e0..0680eecab9 100644 --- a/media/libmedia/include/media/omx/1.0/WOmx.h +++ b/media/libmedia/include/media/omx/1.0/WOmx.h @@ -47,7 +47,6 @@ using ::android::sp; using ::android::List; using ::android::IOMX; -using ::android::BnOMX; /** * Wrapper classes for conversion @@ -58,7 +57,7 @@ using ::android::BnOMX; * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object. */ -struct LWOmx : public BnOMX { +struct LWOmx : public IOMX { sp mBase; LWOmx(sp const& base); status_t listNodes(List* list) override; diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 08519cdab4..dca6bb694f 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -38,6 +38,7 @@ cc_library_static { export_shared_lib_headers: [ "libaudioclient", "libbinder", + "libgui", "libmedia_omx", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index b05718ce02..5932518211 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -187,6 +187,7 @@ cc_library { "liblog", "libmedia", "libmedia_omx", + "libmedia_omx_client", "libaudioclient", "libmediametrics", "libmediautils", diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index a4374c9d91..c897d2dde2 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -281,6 +281,27 @@ private: } }; +struct GraphicBufferSource::ConsumerProxy : public BufferQueue::ConsumerListener { + ConsumerProxy(const sp &gbs) : mGbs(gbs) {} + + ~ConsumerProxy() = default; + + void onFrameAvailable(const BufferItem& item) override { + mGbs->onFrameAvailable(item); + } + + void onBuffersReleased() override { + mGbs->onBuffersReleased(); + } + + void onSidebandStreamChanged() override { + mGbs->onSidebandStreamChanged(); + } + +private: + sp mGbs; +}; + GraphicBufferSource::GraphicBufferSource() : mInitCheck(UNKNOWN_ERROR), mNumAvailableUnacquiredBuffers(0), @@ -317,8 +338,7 @@ GraphicBufferSource::GraphicBufferSource() : // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp listener = - static_cast(this); + wp listener = new ConsumerProxy(this); sp proxy = new BufferQueue::ProxyConsumerListener(listener); diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h index abc89105e5..f182f2fe56 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h @@ -68,7 +68,7 @@ struct FrameDropper; * (even if it was dropped) to reencode it after an interval if no further * frames are sent by the producer. */ -class GraphicBufferSource : public BufferQueue::ConsumerListener { +class GraphicBufferSource : public RefBase { public: GraphicBufferSource(); @@ -192,6 +192,7 @@ public: protected: // BQ::ConsumerListener interface // ------------------------------ + struct ConsumerProxy; // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -199,17 +200,17 @@ protected: // into the codec buffer, and call Empty[This]Buffer. If we're not yet // executing or there's no codec buffer available, we just increment // mNumFramesAvailable and return. - void onFrameAvailable(const BufferItem& item) override; + void onFrameAvailable(const BufferItem& item) ; // BufferQueue::ConsumerListener interface, called when the client has // released one or more GraphicBuffers. We clear out the appropriate // set of mBufferSlot entries. - void onBuffersReleased() override; + void onBuffersReleased() ; // BufferQueue::ConsumerListener interface, called when the client has // changed the sideband stream. GraphicBufferSource doesn't handle sideband // streams so this is a no-op (and should never be called). - void onSidebandStreamChanged() override; + void onSidebandStreamChanged() ; private: // Lock, covers all member variables. diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 15952e35df..0c5075236f 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -6,7 +6,6 @@ cc_library_shared { }, srcs: [ - "BWGraphicBufferSource.cpp", "OMXMaster.cpp", "OMXNodeInstance.cpp", "OMXUtils.cpp", @@ -39,7 +38,6 @@ cc_library_shared { "libutils", "liblog", "libui", - "libgui", "libcutils", "libstagefright_foundation", "libstagefright_bufferqueue_helper", @@ -55,7 +53,6 @@ cc_library_shared { ], export_shared_lib_headers: [ - "libmedia_omx", "libstagefright_foundation", "libstagefright_xmlparser", "libutils", diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp deleted file mode 100644 index fa30a46e53..0000000000 --- a/media/libstagefright/omx/BWGraphicBufferSource.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "BWGraphicBufferSource" - -#include -#include -#include -#include -#include -#include - -namespace android { - -static const OMX_U32 kPortIndexInput = 0; - -struct BWGraphicBufferSource::BWOmxNodeWrapper : public IOmxNodeWrapper { - sp mOMXNode; - - BWOmxNodeWrapper(const sp &omxNode): mOMXNode(omxNode) { - } - - virtual status_t emptyBuffer( - int32_t bufferId, uint32_t flags, - const sp &buffer, - int64_t timestamp, int fenceFd) override { - return mOMXNode->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd); - } - - virtual void dispatchDataSpaceChanged( - int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { - omx_message msg; - msg.type = omx_message::EVENT; - msg.fenceFd = -1; - msg.u.event_data.event = OMX_EventDataSpaceChanged; - msg.u.event_data.data1 = dataSpace; - msg.u.event_data.data2 = aspects; - msg.u.event_data.data3 = pixelFormat; - mOMXNode->dispatchMessage(msg); - } -}; - -struct BWGraphicBufferSource::BWOMXBufferSource : public BnOMXBufferSource { - sp mSource; - - BWOMXBufferSource(const sp &source): mSource(source) { - } - - Status onOmxExecuting() override { - return mSource->onOmxExecuting(); - } - - Status onOmxIdle() override { - return mSource->onOmxIdle(); - } - - Status onOmxLoaded() override { - return mSource->onOmxLoaded(); - } - - Status onInputBufferAdded(int bufferId) override { - return mSource->onInputBufferAdded(bufferId); - } - - Status onInputBufferEmptied( - int bufferId, const OMXFenceParcelable& fenceParcel) override { - return mSource->onInputBufferEmptied(bufferId, fenceParcel.get()); - } -}; - -BWGraphicBufferSource::BWGraphicBufferSource( - sp const& base) : - mBase(base), - mOMXBufferSource(new BWOMXBufferSource(base)) { -} - -::android::binder::Status BWGraphicBufferSource::configure( - const sp& omxNode, int32_t dataSpace) { - // Do setInputSurface() first, the node will try to enable metadata - // mode on input, and does necessary error checking. If this fails, - // we can't use this input surface on the node. - status_t err = omxNode->setInputSurface(mOMXBufferSource); - if (err != NO_ERROR) { - ALOGE("Unable to set input surface: %d", err); - return Status::fromStatusT(err); - } - - // use consumer usage bits queried from encoder, but always add - // HW_VIDEO_ENCODER for backward compatibility. - uint32_t consumerUsage; - if (omxNode->getParameter( - (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits, - &consumerUsage, sizeof(consumerUsage)) != OK) { - consumerUsage = 0; - } - - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - def.nPortIndex = kPortIndexInput; - - err = omxNode->getParameter( - OMX_IndexParamPortDefinition, &def, sizeof(def)); - if (err != NO_ERROR) { - ALOGE("Failed to get port definition: %d", err); - return Status::fromStatusT(UNKNOWN_ERROR); - } - - return Status::fromStatusT(mBase->configure( - new BWOmxNodeWrapper(omxNode), - dataSpace, - def.nBufferCountActual, - def.format.video.nFrameWidth, - def.format.video.nFrameHeight, - consumerUsage)); -} - -::android::binder::Status BWGraphicBufferSource::setSuspend( - bool suspend, int64_t timeUs) { - return Status::fromStatusT(mBase->setSuspend(suspend, timeUs)); -} - -::android::binder::Status BWGraphicBufferSource::setRepeatPreviousFrameDelayUs( - int64_t repeatAfterUs) { - return Status::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs)); -} - -::android::binder::Status BWGraphicBufferSource::setMaxFps(float maxFps) { - return Status::fromStatusT(mBase->setMaxFps(maxFps)); -} - -::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig( - double fps, double captureFps) { - return Status::fromStatusT(mBase->setTimeLapseConfig( - fps, captureFps)); -} - -::android::binder::Status BWGraphicBufferSource::setStartTimeUs( - int64_t startTimeUs) { - return Status::fromStatusT(mBase->setStartTimeUs(startTimeUs)); -} - -::android::binder::Status BWGraphicBufferSource::setStopTimeUs( - int64_t stopTimeUs) { - return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs)); -} - -::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs( - int64_t *stopTimeOffsetUs) { - return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs)); -} - -::android::binder::Status BWGraphicBufferSource::setColorAspects( - int32_t aspects) { - return Status::fromStatusT(mBase->setColorAspects(aspects)); -} - -::android::binder::Status BWGraphicBufferSource::setTimeOffsetUs( - int64_t timeOffsetsUs) { - return Status::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs)); -} - -::android::binder::Status BWGraphicBufferSource::signalEndOfInputStream() { - return Status::fromStatusT(mBase->signalEndOfInputStream()); -} - -} // namespace android diff --git a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h deleted file mode 100644 index 0efff2277e..0000000000 --- a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BWGRAPHIC_BUFFER_SOURCE_H_ -#define BWGRAPHIC_BUFFER_SOURCE_H_ - -#include -#include -#include -#include -#include - -#include "OmxGraphicBufferSource.h" -#include "IOmxNodeWrapper.h" - -namespace android { - -using ::android::binder::Status; -using ::android::BnGraphicBufferSource; -using ::android::OmxGraphicBufferSource; -using ::android::IOMXNode; -using ::android::sp; - -struct BWGraphicBufferSource : public BnGraphicBufferSource { - struct BWOMXBufferSource; - struct BWOmxNodeWrapper; - - sp mBase; - sp mOMXBufferSource; - - BWGraphicBufferSource(sp const &base); - - Status configure( - const sp& omxNode, int32_t dataSpace) override; - Status setSuspend(bool suspend, int64_t timeUs) override; - Status setRepeatPreviousFrameDelayUs( - int64_t repeatAfterUs) override; - Status setMaxFps(float maxFps) override; - Status setTimeLapseConfig( - double fps, double captureFps) override; - Status setStartTimeUs(int64_t startTimeUs) override; - Status setStopTimeUs(int64_t stopTimeUs) override; - Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override; - Status setColorAspects(int32_t aspects) override; - Status setTimeOffsetUs(int64_t timeOffsetsUs) override; - Status signalEndOfInputStream() override; -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERSOURCE_H diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index a4f5730b6e..7d1c88bd8c 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -81,7 +81,6 @@ cc_library_shared { "libmediadrm", "libstagefright", "libstagefright_foundation", - "libstagefright_bufferqueue_helper", "liblog", "libutils", "libcutils", diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index 22e15d3f33..baa4fc77ab 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include using namespace android; diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 9cc19a378c..473e21c0a6 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -32,7 +32,6 @@ LOCAL_REQUIRED_MODULES_x86 := mediacodec.policy endif LOCAL_SRC_FILES := main_codecservice.cpp LOCAL_SHARED_LIBRARIES := \ - libmedia_omx \ libbinder \ libutils \ liblog \ diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp index 6d47be60d6..6ffbd268a3 100644 --- a/services/mediacodec/main_codecservice.cpp +++ b/services/mediacodec/main_codecservice.cpp @@ -25,7 +25,6 @@ #include #include -#include #include using namespace android; -- GitLab From a362033b87bad8bacf3b9fe91cb251d2bb9b18d6 Mon Sep 17 00:00:00 2001 From: Nicholas Sauer Date: Wed, 3 Apr 2019 14:05:17 -0700 Subject: [PATCH 1251/1530] Allow a --user param for cmd media.camera commands. This allows testing on secondary users. For more information, please see go/aae-cts-on-u10 bug: 126472822 Test: Create and switch to secondary user. $ cts-tradefed run cts -a arm64-v8a -m CtsCameraTestCases -t android.hardware.camera2.cts.IdleUidTest and ... $ adb install testcases/CtsCameraTestCases.apk Success $ adb shell cmd media.camera get-uid-state android.camera.cts --user 10 idle $ adb shell cmd media.camera set-uid-state android.camera.cts active --user 10 $ adb shell cmd media.camera get-uid-state android.camera.cts --user 10 active $ adb shell cmd media.camera get-uid-state android.camera.cts --user 0 idle $ adb shell cmd media.camera get-uid-state android.camera.cts idle $ adb shell cmd media.camera reset-uid-state android.camera.cts --user 10 $ adb shell cmd media.camera get-uid-state android.camera.cts idle $ adb shell cmd media.camera get-uid-state android.camera.cts --user 10 idle Change-Id: I7a5a3455d36ce6b95638ff13313f06e7e9b92feb --- .../camera/libcameraservice/CameraService.cpp | 87 +++++++++++++------ 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 8113c3f19b..8354ed5f94 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -858,6 +858,25 @@ static bool isTrustedCallingUid(uid_t uid) { } } +static status_t getUidForPackage(String16 packageName, int userId, /*inout*/uid_t& uid, int err) { + PermissionController pc; + uid = pc.getPackageUid(packageName, 0); + if (uid <= 0) { + ALOGE("Unknown package: '%s'", String8(packageName).string()); + dprintf(err, "Unknown package: '%s'\n", String8(packageName).string()); + return BAD_VALUE; + } + + if (userId < 0) { + ALOGE("Invalid user: %d", userId); + dprintf(err, "Invalid user: %d\n", userId); + return BAD_VALUE; + } + + uid = multiuser_get_uid(userId, uid); + return NO_ERROR; +} + Status CameraService::validateConnectLocked(const String8& cameraId, const String8& clientName8, /*inout*/int& clientUid, /*inout*/int& clientPid, /*out*/int& originalClientPid) const { @@ -3315,11 +3334,11 @@ status_t CameraService::shellCommand(int in, int out, int err, const Vector= 3 && args[0] == String16("set-uid-state")) { return handleSetUidState(args, err); - } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) { + } else if (args.size() >= 2 && args[0] == String16("reset-uid-state")) { return handleResetUidState(args, err); - } else if (args.size() == 2 && args[0] == String16("get-uid-state")) { + } else if (args.size() >= 2 && args[0] == String16("get-uid-state")) { return handleGetUidState(args, out, err); } else if (args.size() == 1 && args[0] == String16("help")) { printHelp(out); @@ -3330,13 +3349,8 @@ status_t CameraService::shellCommand(int in, int out, int err, const Vector& args, int err) { - PermissionController pc; - int uid = pc.getPackageUid(args[1], 0); - if (uid <= 0) { - ALOGE("Unknown package: '%s'", String8(args[1]).string()); - dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); - return BAD_VALUE; - } + String16 packageName = args[1]; + bool active = false; if (args[2] == String16("active")) { active = true; @@ -3344,31 +3358,52 @@ status_t CameraService::handleSetUidState(const Vector& args, int err) ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string()); return BAD_VALUE; } - mUidPolicy->addOverrideUid(uid, args[1], active); + + int userId = 0; + if (args.size() >= 5 && args[3] == String16("--user")) { + userId = atoi(String8(args[4])); + } + + uid_t uid; + if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) { + return BAD_VALUE; + } + + mUidPolicy->addOverrideUid(uid, packageName, active); return NO_ERROR; } status_t CameraService::handleResetUidState(const Vector& args, int err) { - PermissionController pc; - int uid = pc.getPackageUid(args[1], 0); - if (uid < 0) { - ALOGE("Unknown package: '%s'", String8(args[1]).string()); - dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); + String16 packageName = args[1]; + + int userId = 0; + if (args.size() >= 4 && args[2] == String16("--user")) { + userId = atoi(String8(args[3])); + } + + uid_t uid; + if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) { return BAD_VALUE; } - mUidPolicy->removeOverrideUid(uid, args[1]); + + mUidPolicy->removeOverrideUid(uid, packageName); return NO_ERROR; } status_t CameraService::handleGetUidState(const Vector& args, int out, int err) { - PermissionController pc; - int uid = pc.getPackageUid(args[1], 0); - if (uid <= 0) { - ALOGE("Unknown package: '%s'", String8(args[1]).string()); - dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); + String16 packageName = args[1]; + + int userId = 0; + if (args.size() >= 4 && args[2] == String16("--user")) { + userId = atoi(String8(args[3])); + } + + uid_t uid; + if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) { return BAD_VALUE; } - if (mUidPolicy->isUidActive(uid, args[1])) { + + if (mUidPolicy->isUidActive(uid, packageName)) { return dprintf(out, "active\n"); } else { return dprintf(out, "idle\n"); @@ -3377,9 +3412,9 @@ status_t CameraService::handleGetUidState(const Vector& args, int out, status_t CameraService::printHelp(int out) { return dprintf(out, "Camera service commands:\n" - " get-uid-state gets the uid state\n" - " set-uid-state overrides the uid state\n" - " reset-uid-state clears the uid state override\n" + " get-uid-state [--user USER_ID] gets the uid state\n" + " set-uid-state [--user USER_ID] overrides the uid state\n" + " reset-uid-state [--user USER_ID] clears the uid state override\n" " help print this message\n"); } -- GitLab From 2cb17e305d67f74cd084b529c5dfc78bc5be599c Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 5 Apr 2019 22:54:28 +0200 Subject: [PATCH 1252/1530] Bump test_manifest_code.json version as well. Bug: 129996486 Test: atest media_swcodec_e2e_tests Change-Id: I8987d6d7b60fe6182c3bb460e26a3d7b729f255f --- apex/testing/test_manifest_codec.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apex/testing/test_manifest_codec.json b/apex/testing/test_manifest_codec.json index c956454784..2320fd76f6 100644 --- a/apex/testing/test_manifest_codec.json +++ b/apex/testing/test_manifest_codec.json @@ -1,4 +1,4 @@ { "name": "com.android.media.swcodec", - "version": 2 + "version": 300000000 } -- GitLab From cccbc765d39c2f017d6626b4c83758d853d156ea Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 5 Apr 2019 14:20:05 -0700 Subject: [PATCH 1253/1530] audio flinger: fix cross deadlock in RecordThread loop Fix a cross deadlock between thread and effect chain mutex introduced by commit 446f4df5. Bug: 130044136 Test: start/stop audio capture for voice communication in 2 different apps Change-Id: I23bdbd203476d2bc0b3d8d87ab66e1a9c00f7bcd --- services/audioflinger/Threads.cpp | 35 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 04d62fac4c..0b203c42f6 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7125,24 +7125,6 @@ reacquire_wakelock: ALOG_ASSERT(framesRead > 0); mFramesRead += framesRead; - if (audio_has_proportional_frames(mFormat) - && loopCount == lastLoopCountRead + 1) { - const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs; - const double jitterMs = - TimestampVerifier::computeJitterMs( - {framesRead, readPeriodNs}, - {0, 0} /* lastTimestamp */, mSampleRate); - const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6; - - Mutex::Autolock _l(mLock); - mIoJitterMs.add(jitterMs); - mProcessTimeMs.add(processMs); - } - // update timing info. - mLastIoBeginNs = lastIoBeginNs; - mLastIoEndNs = lastIoEndNs; - lastLoopCountRead = loopCount; - #ifdef TEE_SINK (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead); #endif @@ -7302,6 +7284,23 @@ unlock: // enable changes in effect chain unlockEffectChains(effectChains); // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end + if (audio_has_proportional_frames(mFormat) + && loopCount == lastLoopCountRead + 1) { + const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs; + const double jitterMs = + TimestampVerifier::computeJitterMs( + {framesRead, readPeriodNs}, + {0, 0} /* lastTimestamp */, mSampleRate); + const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6; + + Mutex::Autolock _l(mLock); + mIoJitterMs.add(jitterMs); + mProcessTimeMs.add(processMs); + } + // update timing info. + mLastIoBeginNs = lastIoBeginNs; + mLastIoEndNs = lastIoEndNs; + lastLoopCountRead = loopCount; } standbyIfNotAlreadyInStandby(); -- GitLab From 57af40231b6b974b1a98122f4066c334c0872ca3 Mon Sep 17 00:00:00 2001 From: mtk08122 Date: Wed, 30 Jan 2019 17:35:11 +0800 Subject: [PATCH 1254/1530] Fix abnormal consumption of mobile data Problem: When user is watching http live streaming video, user does some operation to let browser run in the background, but mobile data is still consumed abnormally. Root cause: When browser runs in the background, browser will call mediaplayer pause function, which would pause video renderer. But if network speed is fast and buffered data is more than PlaylistFecher:: kMinBufferedDurationUs, data can't be consumed and it will call postMonitorQueue() frequently after delayUS time's up. At this time, if http server returns error when refreshPlaylist, delayUs will be 0 because mLastPlaylistFetchTimeUs is not updated, which will cause postMonitorQueue and refreshPlaylist more frequently, and consumes more mobile data. Solution: On PlaylistFetcher::onMonitorQueue(), if refreshPlaylist returns error, it calls notifyError and then end this playback. Bug: 123665682 Test: play http live streaming video, pause this playback, then close Wi-Fi to simulate accessing http server error Change-Id: Ic30d446d1ab6e34ac3e5ef414c2e71a09c296f92 (cherry picked from commit 4ae6a2ab791fc3d4b3f9f481ffca65c292be2fd3) --- media/libstagefright/httplive/PlaylistFetcher.cpp | 13 ++++++++++++- media/libstagefright/httplive/PlaylistFetcher.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp index c62c2cd5a9..635ecfe489 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.cpp +++ b/media/libstagefright/httplive/PlaylistFetcher.cpp @@ -160,6 +160,7 @@ PlaylistFetcher::PlaylistFetcher( mPlaylistTimeUs(-1LL), mSeqNumber(-1), mNumRetries(0), + mNumRetriesForMonitorQueue(0), mStartup(true), mIDRFound(false), mSeekMode(LiveSession::kSeekModeExactPosition), @@ -849,7 +850,17 @@ void PlaylistFetcher::onMonitorQueue() { // in the middle of an unfinished download, delay // playlist refresh as it'll change seq numbers if (!mDownloadState->hasSavedState()) { - refreshPlaylist(); + status_t err = refreshPlaylist(); + if (err != OK) { + if (mNumRetriesForMonitorQueue < kMaxNumRetries) { + ++mNumRetriesForMonitorQueue; + } else { + notifyError(err); + } + return; + } else { + mNumRetriesForMonitorQueue = 0; + } } int64_t targetDurationUs = kMinBufferedDurationUs; diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h index d7db54a557..5d3f9c1384 100644 --- a/media/libstagefright/httplive/PlaylistFetcher.h +++ b/media/libstagefright/httplive/PlaylistFetcher.h @@ -145,6 +145,7 @@ private: sp mPlaylist; int32_t mSeqNumber; int32_t mNumRetries; + int32_t mNumRetriesForMonitorQueue; bool mStartup; bool mIDRFound; int32_t mSeekMode; -- GitLab From 08006ea91d39c02ce93a02018dfbdf26143c5563 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 4 Apr 2019 20:25:09 -0700 Subject: [PATCH 1255/1530] Use vintf_fragments for media.c2/software This makes the entry for media.c2 in the framework manifest device-independent. Test: adb shell stagefright -i | grep -E "c2.*android" Bug: 129999541 Change-Id: Idba276c512f495937aec32c4217dfd447e562139 --- apex/Android.bp | 1 + apex/manifest_media_c2_software.xml | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 apex/manifest_media_c2_software.xml diff --git a/apex/Android.bp b/apex/Android.bp index 0a9551d4d3..6673543dab 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -99,6 +99,7 @@ apex { name: "com.android.media.swcodec", manifest: "manifest_codec.json", defaults: ["com.android.media.swcodec-defaults"], + vintf_fragments: ["manifest_media_c2_software.xml"], } apex_key { diff --git a/apex/manifest_media_c2_software.xml b/apex/manifest_media_c2_software.xml new file mode 100644 index 0000000000..5196336c15 --- /dev/null +++ b/apex/manifest_media_c2_software.xml @@ -0,0 +1,11 @@ + + + android.hardware.media.c2 + hwbinder + 1.0 + + IComponentStore + software + + + -- GitLab From 5e66e79ec60d0389fe025076dd0839bf695f1495 Mon Sep 17 00:00:00 2001 From: Robert Lee Date: Wed, 3 Apr 2019 18:37:15 +0800 Subject: [PATCH 1256/1530] audiopolicy: fix ring stream volume cannot apply when previewing ringtone Using AUDIO_DEVICE_OUT_SPEAKER to replace AUDIO_DEVICE_OUT_SPEAKER_SAFE for device match. Bug: 129456342 Test: volume can adjust while previewing ringtone Change-Id: I3015f811ac6d2e3f3cdd606f453348c4bc526935 Signed-off-by: Robert Lee --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index bd53f0ffdb..9582daf900 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2463,6 +2463,11 @@ status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_ sp desc = mOutputs.valueAt(i); audio_devices_t curDevice = desc->devices().types(); + if (curDevice & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { + curDevice |= AUDIO_DEVICE_OUT_SPEAKER; + curDevice &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } + // Inter / intra volume group priority management: Loop on strategies arranged by priority // If a higher priority strategy is active, and the output is routed to a device with a // HW Gain management, do not change the volume -- GitLab From b4979c59d93a6ca2be7e65489fc39896bfdc2c96 Mon Sep 17 00:00:00 2001 From: Sung-fang Tsai Date: Sun, 7 Apr 2019 00:29:22 +0000 Subject: [PATCH 1257/1530] Revert "Remove direct libgui reference from omx server side" This reverts commit 482f60ccccc12e6afe24f9ba503496ce65d1ba43. Reason for revert: b/130058749 camera recording hang Bug: 130058749 Change-Id: I358bc9c62c48328c01c8611c7e6f77d16affff54 --- media/codec2/hidl/client/Android.bp | 1 + media/codec2/sfplugin/Android.bp | 1 + media/libmedia/Android.bp | 75 +------- media/libmedia/IOMX.cpp | 151 +++++++++++++++ media/libmedia/include/media/IOMX.h | 11 +- .../include/media/omx/1.0/Conversion.h | 6 +- media/libmedia/include/media/omx/1.0/WOmx.h | 3 +- media/libmediaplayer2/Android.bp | 1 - media/libstagefright/Android.bp | 1 - .../bqhelper/GraphicBufferSource.cpp | 24 +-- .../bqhelper/GraphicBufferSource.h | 9 +- media/libstagefright/omx/Android.bp | 3 + .../omx/BWGraphicBufferSource.cpp | 180 ++++++++++++++++++ .../stagefright/omx/BWGraphicBufferSource.h | 64 +++++++ media/ndk/Android.bp | 1 + media/ndk/NdkImageReader.cpp | 2 +- services/mediacodec/Android.mk | 1 + services/mediacodec/main_codecservice.cpp | 1 + 18 files changed, 430 insertions(+), 105 deletions(-) create mode 100644 media/libstagefright/omx/BWGraphicBufferSource.cpp create mode 100644 media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index 965e438aa1..f92d1afde2 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -20,6 +20,7 @@ cc_library { "libhidltransport", "liblog", "libstagefright_bufferpool@2.0", + "libstagefright_bufferqueue_helper", "libui", "libutils", ], diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index 66457e7e3b..a212651570 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -40,6 +40,7 @@ cc_library_shared { "libmedia", "libmedia_omx", "libsfplugin_ccodec_utils", + "libstagefright_bufferqueue_helper", "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx_utils", diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 1d335901c5..a529628d83 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -65,6 +65,7 @@ cc_library_shared { "MediaCodecInfo.cpp", "OMXBuffer.cpp", "omx/1.0/WGraphicBufferSource.cpp", + "omx/1.0/WOmx.cpp", "omx/1.0/WOmxBufferSource.cpp", "omx/1.0/WOmxNode.cpp", "omx/1.0/WOmxObserver.cpp", @@ -74,16 +75,13 @@ cc_library_shared { local_include_dirs: ["aidl"], export_aidl_headers: true, }, - - local_include_dirs: [ - "include", - ], shared_libs: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", "libbinder", "libcutils", + "libgui", "libhidlbase", "libhidltransport", "liblog", @@ -95,84 +93,21 @@ cc_library_shared { export_shared_lib_headers: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", - "libstagefright_foundation", - "libui", - ], - - header_libs: [ - "libstagefright_headers", - "media_plugin_headers", - ], - - export_header_lib_headers: [ - "libstagefright_headers", - "media_plugin_headers", - ], - - export_include_dirs: [ - "aidl", - "include", - ], - - cflags: [ - "-Werror", - "-Wno-error=deprecated-declarations", - "-Wall", - ], - - sanitize: { - misc_undefined: [ - "unsigned-integer-overflow", - "signed-integer-overflow", - ], - cfi: true, - }, -} - - -cc_library_shared { - name: "libmedia_omx_client", - - srcs: [ - "omx/1.0/WOmx.cpp", - ], - - local_include_dirs: [ - "include", - ], - - shared_libs: [ - "libbinder", - "libcutils", "libgui", - "libhidlbase", - "libhidltransport", - "liblog", - "libmedia_omx", - "libstagefright_foundation", - "libui", - "libutils", - ], - - export_shared_lib_headers: [ - "libgui", - "libmedia_omx", "libstagefright_foundation", "libui", ], header_libs: [ - "libstagefright_headers", - "media_plugin_headers", + "libmedia_headers", ], export_header_lib_headers: [ - "libstagefright_headers", - "media_plugin_headers", + "libmedia_headers", ], export_include_dirs: [ - "include", + "aidl", ], cflags: [ diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index bc0c2cd917..747b88f6e4 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,79 @@ enum { SET_QUIRKS, }; +class BpOMX : public BpInterface { +public: + explicit BpOMX(const sp &impl) + : BpInterface(impl) { + } + + virtual status_t listNodes(List *list) { + list->clear(); + + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + remote()->transact(LIST_NODES, data, &reply); + + int32_t n = reply.readInt32(); + for (int32_t i = 0; i < n; ++i) { + list->push_back(ComponentInfo()); + ComponentInfo &info = *--list->end(); + + info.mName = reply.readString8(); + int32_t numRoles = reply.readInt32(); + for (int32_t j = 0; j < numRoles; ++j) { + info.mRoles.push_back(reply.readString8()); + } + } + + return OK; + } + + virtual status_t allocateNode( + const char *name, const sp &observer, + sp *omxNode) { + Parcel data, reply; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeCString(name); + data.writeStrongBinder(IInterface::asBinder(observer)); + remote()->transact(ALLOCATE_NODE, data, &reply); + + status_t err = reply.readInt32(); + if (err == OK) { + *omxNode = IOMXNode::asInterface(reply.readStrongBinder()); + } else { + omxNode->clear(); + } + + return err; + } + + virtual status_t createInputSurface( + sp *bufferProducer, + sp *bufferSource) { + Parcel data, reply; + status_t err; + data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply); + if (err != OK) { + ALOGW("binder transaction failed: %d", err); + return err; + } + + err = reply.readInt32(); + if (err != OK) { + return err; + } + + *bufferProducer = IGraphicBufferProducer::asInterface( + reply.readStrongBinder()); + *bufferSource = IGraphicBufferSource::asInterface( + reply.readStrongBinder()); + + return err; + } +}; + class BpOMXNode : public BpInterface { public: explicit BpOMXNode(const sp &impl) @@ -477,6 +551,7 @@ public: } }; +IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); //////////////////////////////////////////////////////////////////////////////// @@ -487,6 +562,82 @@ IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); return PERMISSION_DENIED; \ } } while (0) +status_t BnOMX::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + switch (code) { + case LIST_NODES: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + List list; + listNodes(&list); + + reply->writeInt32(list.size()); + for (List::iterator it = list.begin(); + it != list.end(); ++it) { + ComponentInfo &cur = *it; + + reply->writeString8(cur.mName); + reply->writeInt32(cur.mRoles.size()); + for (List::iterator role_it = cur.mRoles.begin(); + role_it != cur.mRoles.end(); ++role_it) { + reply->writeString8(*role_it); + } + } + + return NO_ERROR; + } + + case ALLOCATE_NODE: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + const char *name = data.readCString(); + + sp observer = + interface_cast(data.readStrongBinder()); + + if (name == NULL || observer == NULL) { + ALOGE("b/26392700"); + reply->writeInt32(INVALID_OPERATION); + return NO_ERROR; + } + + sp omxNode; + + status_t err = allocateNode(name, observer, &omxNode); + + reply->writeInt32(err); + if (err == OK) { + reply->writeStrongBinder(IInterface::asBinder(omxNode)); + } + + return NO_ERROR; + } + + case CREATE_INPUT_SURFACE: + { + CHECK_OMX_INTERFACE(IOMX, data, reply); + + sp bufferProducer; + sp bufferSource; + status_t err = createInputSurface(&bufferProducer, &bufferSource); + + reply->writeInt32(err); + + if (err == OK) { + reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); + reply->writeStrongBinder(IInterface::asBinder(bufferSource)); + } + + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + status_t BnOMXNode::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h index 7e7c2d23dc..e69c02dda1 100644 --- a/media/libmedia/include/media/IOMX.h +++ b/media/libmedia/include/media/IOMX.h @@ -47,8 +47,9 @@ struct omx_message; using hardware::media::omx::V1_0::IOmxNode; -class IOMX : public RefBase { +class IOMX : public IInterface { public: + DECLARE_META_INTERFACE(OMX); typedef uint32_t buffer_id; @@ -223,6 +224,14 @@ public: }; //////////////////////////////////////////////////////////////////////////////// + +class BnOMX : public BnInterface { +public: + virtual status_t onTransact( + uint32_t code, const Parcel &data, Parcel *reply, + uint32_t flags = 0); +}; + class BnOMXNode : public BnInterface { public: virtual status_t onTransact( diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h index babda222bf..3700a237f1 100644 --- a/media/libmedia/include/media/omx/1.0/Conversion.h +++ b/media/libmedia/include/media/omx/1.0/Conversion.h @@ -31,12 +31,12 @@ #include #include -#include #include #include #include #include #include +#include #include #include @@ -282,8 +282,8 @@ inline Status toStatus(status_t l) { case TIMED_OUT: case ERROR_UNSUPPORTED: case UNKNOWN_ERROR: - case BufferQueueDefs::RELEASE_ALL_BUFFERS: - case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION: + case IGraphicBufferProducer::RELEASE_ALL_BUFFERS: + case IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION: return static_cast(l); case NOT_ENOUGH_DATA: return Status::BUFFER_NEEDS_REALLOCATION; diff --git a/media/libmedia/include/media/omx/1.0/WOmx.h b/media/libmedia/include/media/omx/1.0/WOmx.h index 0680eecab9..f13546e1e0 100644 --- a/media/libmedia/include/media/omx/1.0/WOmx.h +++ b/media/libmedia/include/media/omx/1.0/WOmx.h @@ -47,6 +47,7 @@ using ::android::sp; using ::android::List; using ::android::IOMX; +using ::android::BnOMX; /** * Wrapper classes for conversion @@ -57,7 +58,7 @@ using ::android::IOMX; * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object. */ -struct LWOmx : public IOMX { +struct LWOmx : public BnOMX { sp mBase; LWOmx(sp const& base); status_t listNodes(List* list) override; diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index dca6bb694f..08519cdab4 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -38,7 +38,6 @@ cc_library_static { export_shared_lib_headers: [ "libaudioclient", "libbinder", - "libgui", "libmedia_omx", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 5932518211..b05718ce02 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -187,7 +187,6 @@ cc_library { "liblog", "libmedia", "libmedia_omx", - "libmedia_omx_client", "libaudioclient", "libmediametrics", "libmediautils", diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index c897d2dde2..a4374c9d91 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -281,27 +281,6 @@ private: } }; -struct GraphicBufferSource::ConsumerProxy : public BufferQueue::ConsumerListener { - ConsumerProxy(const sp &gbs) : mGbs(gbs) {} - - ~ConsumerProxy() = default; - - void onFrameAvailable(const BufferItem& item) override { - mGbs->onFrameAvailable(item); - } - - void onBuffersReleased() override { - mGbs->onBuffersReleased(); - } - - void onSidebandStreamChanged() override { - mGbs->onSidebandStreamChanged(); - } - -private: - sp mGbs; -}; - GraphicBufferSource::GraphicBufferSource() : mInitCheck(UNKNOWN_ERROR), mNumAvailableUnacquiredBuffers(0), @@ -338,7 +317,8 @@ GraphicBufferSource::GraphicBufferSource() : // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. - wp listener = new ConsumerProxy(this); + wp listener = + static_cast(this); sp proxy = new BufferQueue::ProxyConsumerListener(listener); diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h index f182f2fe56..abc89105e5 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h @@ -68,7 +68,7 @@ struct FrameDropper; * (even if it was dropped) to reencode it after an interval if no further * frames are sent by the producer. */ -class GraphicBufferSource : public RefBase { +class GraphicBufferSource : public BufferQueue::ConsumerListener { public: GraphicBufferSource(); @@ -192,7 +192,6 @@ public: protected: // BQ::ConsumerListener interface // ------------------------------ - struct ConsumerProxy; // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -200,17 +199,17 @@ protected: // into the codec buffer, and call Empty[This]Buffer. If we're not yet // executing or there's no codec buffer available, we just increment // mNumFramesAvailable and return. - void onFrameAvailable(const BufferItem& item) ; + void onFrameAvailable(const BufferItem& item) override; // BufferQueue::ConsumerListener interface, called when the client has // released one or more GraphicBuffers. We clear out the appropriate // set of mBufferSlot entries. - void onBuffersReleased() ; + void onBuffersReleased() override; // BufferQueue::ConsumerListener interface, called when the client has // changed the sideband stream. GraphicBufferSource doesn't handle sideband // streams so this is a no-op (and should never be called). - void onSidebandStreamChanged() ; + void onSidebandStreamChanged() override; private: // Lock, covers all member variables. diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 0c5075236f..15952e35df 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -6,6 +6,7 @@ cc_library_shared { }, srcs: [ + "BWGraphicBufferSource.cpp", "OMXMaster.cpp", "OMXNodeInstance.cpp", "OMXUtils.cpp", @@ -38,6 +39,7 @@ cc_library_shared { "libutils", "liblog", "libui", + "libgui", "libcutils", "libstagefright_foundation", "libstagefright_bufferqueue_helper", @@ -53,6 +55,7 @@ cc_library_shared { ], export_shared_lib_headers: [ + "libmedia_omx", "libstagefright_foundation", "libstagefright_xmlparser", "libutils", diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp new file mode 100644 index 0000000000..fa30a46e53 --- /dev/null +++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "BWGraphicBufferSource" + +#include +#include +#include +#include +#include +#include + +namespace android { + +static const OMX_U32 kPortIndexInput = 0; + +struct BWGraphicBufferSource::BWOmxNodeWrapper : public IOmxNodeWrapper { + sp mOMXNode; + + BWOmxNodeWrapper(const sp &omxNode): mOMXNode(omxNode) { + } + + virtual status_t emptyBuffer( + int32_t bufferId, uint32_t flags, + const sp &buffer, + int64_t timestamp, int fenceFd) override { + return mOMXNode->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd); + } + + virtual void dispatchDataSpaceChanged( + int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { + omx_message msg; + msg.type = omx_message::EVENT; + msg.fenceFd = -1; + msg.u.event_data.event = OMX_EventDataSpaceChanged; + msg.u.event_data.data1 = dataSpace; + msg.u.event_data.data2 = aspects; + msg.u.event_data.data3 = pixelFormat; + mOMXNode->dispatchMessage(msg); + } +}; + +struct BWGraphicBufferSource::BWOMXBufferSource : public BnOMXBufferSource { + sp mSource; + + BWOMXBufferSource(const sp &source): mSource(source) { + } + + Status onOmxExecuting() override { + return mSource->onOmxExecuting(); + } + + Status onOmxIdle() override { + return mSource->onOmxIdle(); + } + + Status onOmxLoaded() override { + return mSource->onOmxLoaded(); + } + + Status onInputBufferAdded(int bufferId) override { + return mSource->onInputBufferAdded(bufferId); + } + + Status onInputBufferEmptied( + int bufferId, const OMXFenceParcelable& fenceParcel) override { + return mSource->onInputBufferEmptied(bufferId, fenceParcel.get()); + } +}; + +BWGraphicBufferSource::BWGraphicBufferSource( + sp const& base) : + mBase(base), + mOMXBufferSource(new BWOMXBufferSource(base)) { +} + +::android::binder::Status BWGraphicBufferSource::configure( + const sp& omxNode, int32_t dataSpace) { + // Do setInputSurface() first, the node will try to enable metadata + // mode on input, and does necessary error checking. If this fails, + // we can't use this input surface on the node. + status_t err = omxNode->setInputSurface(mOMXBufferSource); + if (err != NO_ERROR) { + ALOGE("Unable to set input surface: %d", err); + return Status::fromStatusT(err); + } + + // use consumer usage bits queried from encoder, but always add + // HW_VIDEO_ENCODER for backward compatibility. + uint32_t consumerUsage; + if (omxNode->getParameter( + (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits, + &consumerUsage, sizeof(consumerUsage)) != OK) { + consumerUsage = 0; + } + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + + err = omxNode->getParameter( + OMX_IndexParamPortDefinition, &def, sizeof(def)); + if (err != NO_ERROR) { + ALOGE("Failed to get port definition: %d", err); + return Status::fromStatusT(UNKNOWN_ERROR); + } + + return Status::fromStatusT(mBase->configure( + new BWOmxNodeWrapper(omxNode), + dataSpace, + def.nBufferCountActual, + def.format.video.nFrameWidth, + def.format.video.nFrameHeight, + consumerUsage)); +} + +::android::binder::Status BWGraphicBufferSource::setSuspend( + bool suspend, int64_t timeUs) { + return Status::fromStatusT(mBase->setSuspend(suspend, timeUs)); +} + +::android::binder::Status BWGraphicBufferSource::setRepeatPreviousFrameDelayUs( + int64_t repeatAfterUs) { + return Status::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs)); +} + +::android::binder::Status BWGraphicBufferSource::setMaxFps(float maxFps) { + return Status::fromStatusT(mBase->setMaxFps(maxFps)); +} + +::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig( + double fps, double captureFps) { + return Status::fromStatusT(mBase->setTimeLapseConfig( + fps, captureFps)); +} + +::android::binder::Status BWGraphicBufferSource::setStartTimeUs( + int64_t startTimeUs) { + return Status::fromStatusT(mBase->setStartTimeUs(startTimeUs)); +} + +::android::binder::Status BWGraphicBufferSource::setStopTimeUs( + int64_t stopTimeUs) { + return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs)); +} + +::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs( + int64_t *stopTimeOffsetUs) { + return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs)); +} + +::android::binder::Status BWGraphicBufferSource::setColorAspects( + int32_t aspects) { + return Status::fromStatusT(mBase->setColorAspects(aspects)); +} + +::android::binder::Status BWGraphicBufferSource::setTimeOffsetUs( + int64_t timeOffsetsUs) { + return Status::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs)); +} + +::android::binder::Status BWGraphicBufferSource::signalEndOfInputStream() { + return Status::fromStatusT(mBase->signalEndOfInputStream()); +} + +} // namespace android diff --git a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h new file mode 100644 index 0000000000..0efff2277e --- /dev/null +++ b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h @@ -0,0 +1,64 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BWGRAPHIC_BUFFER_SOURCE_H_ +#define BWGRAPHIC_BUFFER_SOURCE_H_ + +#include +#include +#include +#include +#include + +#include "OmxGraphicBufferSource.h" +#include "IOmxNodeWrapper.h" + +namespace android { + +using ::android::binder::Status; +using ::android::BnGraphicBufferSource; +using ::android::OmxGraphicBufferSource; +using ::android::IOMXNode; +using ::android::sp; + +struct BWGraphicBufferSource : public BnGraphicBufferSource { + struct BWOMXBufferSource; + struct BWOmxNodeWrapper; + + sp mBase; + sp mOMXBufferSource; + + BWGraphicBufferSource(sp const &base); + + Status configure( + const sp& omxNode, int32_t dataSpace) override; + Status setSuspend(bool suspend, int64_t timeUs) override; + Status setRepeatPreviousFrameDelayUs( + int64_t repeatAfterUs) override; + Status setMaxFps(float maxFps) override; + Status setTimeLapseConfig( + double fps, double captureFps) override; + Status setStartTimeUs(int64_t startTimeUs) override; + Status setStopTimeUs(int64_t stopTimeUs) override; + Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override; + Status setColorAspects(int32_t aspects) override; + Status setTimeOffsetUs(int64_t timeOffsetsUs) override; + Status signalEndOfInputStream() override; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERSOURCE_H diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 7d1c88bd8c..a4f5730b6e 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -81,6 +81,7 @@ cc_library_shared { "libmediadrm", "libstagefright", "libstagefright_foundation", + "libstagefright_bufferqueue_helper", "liblog", "libutils", "libcutils", diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index baa4fc77ab..22e15d3f33 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include using namespace android; diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 473e21c0a6..9cc19a378c 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -32,6 +32,7 @@ LOCAL_REQUIRED_MODULES_x86 := mediacodec.policy endif LOCAL_SRC_FILES := main_codecservice.cpp LOCAL_SHARED_LIBRARIES := \ + libmedia_omx \ libbinder \ libutils \ liblog \ diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp index 6ffbd268a3..6d47be60d6 100644 --- a/services/mediacodec/main_codecservice.cpp +++ b/services/mediacodec/main_codecservice.cpp @@ -25,6 +25,7 @@ #include #include +#include #include using namespace android; -- GitLab From 7d5569f51ff64ca8896d90128e085ca33596433d Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Mon, 8 Apr 2019 13:57:54 -0700 Subject: [PATCH 1258/1530] Cameraservice: Cache camera service proxy interface To improve camera launch performance, cache the proxy service interface. System tracing indicates fetching the interface can take up to 8-10 ms during real camera opens, which is a percent or two of total camera startup time. Test: atest CameraCtsTestCases Bug: 130173970 Change-Id: Icdf5218b04f608b897dcbf2085f971b04a913f3b --- .../camera/libcameraservice/CameraService.cpp | 23 +++++++++++-------- .../camera/libcameraservice/CameraService.h | 5 ++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 8113c3f19b..cb9ece7a46 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -117,6 +117,9 @@ static void setLogLevel(int level) { static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA"); +Mutex CameraService::sProxyMutex; +sp CameraService::sCameraServiceProxy; + CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH), mNumberOfCameras(0), @@ -203,18 +206,20 @@ status_t CameraService::enumerateProviders() { } sp CameraService::getCameraServiceProxy() { - sp proxyBinder = nullptr; #ifndef __BRILLO__ - sp sm = defaultServiceManager(); - // Use checkService because cameraserver normally starts before the - // system server and the proxy service. So the long timeout that getService - // has before giving up is inappropriate. - sp binder = sm->checkService(String16("media.camera.proxy")); - if (binder != nullptr) { - proxyBinder = interface_cast(binder); + Mutex::Autolock al(sProxyMutex); + if (sCameraServiceProxy == nullptr) { + sp sm = defaultServiceManager(); + // Use checkService because cameraserver normally starts before the + // system server and the proxy service. So the long timeout that getService + // has before giving up is inappropriate. + sp binder = sm->checkService(String16("media.camera.proxy")); + if (binder != nullptr) { + sCameraServiceProxy = interface_cast(binder); + } } #endif - return proxyBinder; + return sCameraServiceProxy; } void CameraService::pingCameraServiceProxy() { diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 344dd92cfc..4bcdeb2442 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -930,6 +930,11 @@ private: static StatusInternal mapToInternal(hardware::camera::common::V1_0::CameraDeviceStatus status); static int32_t mapToInterface(StatusInternal status); + // Guard mCameraServiceProxy + static Mutex sProxyMutex; + // Cached interface to the camera service proxy in system service + static sp sCameraServiceProxy; + static sp getCameraServiceProxy(); static void pingCameraServiceProxy(); -- GitLab From 0f7ff70737d58abda69fa6d4524b1943d6c41461 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Mon, 1 Apr 2019 16:18:53 -0700 Subject: [PATCH 1259/1530] NuPlayerCCDecoder: fix memory OOB Test: cts Bug: 129068792 Change-Id: Id78ddc983f245feda3a81da3448196340b57f5c9 (cherry picked from commit e1c7348e1c3fed25c16ae4673101f48b1ed95b7e) --- .../nuplayer/NuPlayerCCDecoder.cpp | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp index 13716cf4c7..eec1731c88 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp @@ -308,6 +308,11 @@ bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp &accessUnit) const size_t *userData = (size_t *)mpegUserData->data(); for (size_t i = 0; i < mpegUserData->size() / sizeof(size_t); ++i) { + if (accessUnit->size() < userData[i]) { + ALOGW("b/129068792, skip invalid offset for user data"); + android_errorWriteLog(0x534e4554, "129068792"); + continue; + } trackAdded |= parseMPEGUserDataUnit( timeUs, accessUnit->data() + userData[i], accessUnit->size() - userData[i]); } @@ -317,6 +322,12 @@ bool NuPlayer::CCDecoder::extractFromMPEGUserData(const sp &accessUnit) // returns true if a new CC track is found bool NuPlayer::CCDecoder::parseMPEGUserDataUnit(int64_t timeUs, const uint8_t *data, size_t size) { + if (size < 9) { + ALOGW("b/129068792, MPEG user data size too small %zu", size); + android_errorWriteLog(0x534e4554, "129068792"); + return false; + } + ABitReader br(data + 4, 5); uint32_t user_identifier = br.getBits(32); @@ -369,8 +380,14 @@ bool NuPlayer::CCDecoder::parseMPEGCCData(int64_t timeUs, const uint8_t *data, s mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); br.skipBits(16); } else if (mDTVCCPacket->size() > 0 && cc_type == 2) { - memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); - mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); + if (mDTVCCPacket->capacity() - mDTVCCPacket->size() >= 2) { + memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2); + mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2); + } else { + ALOGW("b/129068792, skip CC due to too much data(%zu, %zu)", + mDTVCCPacket->capacity(), mDTVCCPacket->size()); + android_errorWriteLog(0x534e4554, "129068792"); + } br.skipBits(16); } else if (cc_type == 0 || cc_type == 1) { uint8_t cc_data_1 = br.getBits(8) & 0x7f; @@ -457,6 +474,11 @@ bool NuPlayer::CCDecoder::parseDTVCCPacket(int64_t timeUs, const uint8_t *data, size_t trackIndex = getTrackIndex(kTrackTypeCEA708, service_number, &trackAdded); if (mSelectedTrack == (ssize_t)trackIndex) { sp ccPacket = new ABuffer(block_size); + if (ccPacket->capacity() == 0) { + ALOGW("b/129068792, no memory available, %zu", block_size); + android_errorWriteLog(0x534e4554, "129068792"); + return false; + } memcpy(ccPacket->data(), br.data(), block_size); mCCMap.add(timeUs, ccPacket); } -- GitLab From 0fe4c4795015357290b098715f6d3cd17cf554df Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 8 Apr 2019 21:51:46 +0000 Subject: [PATCH 1260/1530] Revert "Revert "Remove direct libgui reference from omx server side"" This reverts commit b4979c59d93a6ca2be7e65489fc39896bfdc2c96. Reason for revert: reland with fixes: 1) make OMXHarness use OMXClient, not directly IOMX wrappers. 2) fix ref of consumer listener interface in GraphicBufferSource. bug: 129272021 Test: camera recording, aosp_x86 build Change-Id: Idaaaf412c65cbe89c95a2cb54f3833ff1fc4c304 --- media/codec2/hidl/client/Android.bp | 1 - media/codec2/sfplugin/Android.bp | 1 - media/libmedia/Android.bp | 75 +++++++- media/libmedia/IOMX.cpp | 151 --------------- media/libmedia/include/media/IOMX.h | 11 +- .../include/media/omx/1.0/Conversion.h | 6 +- media/libmedia/include/media/omx/1.0/WOmx.h | 3 +- media/libmediaplayer2/Android.bp | 1 + media/libstagefright/Android.bp | 1 + .../bqhelper/GraphicBufferSource.cpp | 33 +++- .../bqhelper/GraphicBufferSource.h | 15 +- media/libstagefright/omx/Android.bp | 3 - .../omx/BWGraphicBufferSource.cpp | 180 ------------------ .../stagefright/omx/BWGraphicBufferSource.h | 64 ------- media/libstagefright/omx/tests/Android.bp | 6 - media/libstagefright/omx/tests/OMXHarness.cpp | 12 +- media/ndk/Android.bp | 1 - media/ndk/NdkImageReader.cpp | 2 +- services/mediacodec/Android.mk | 1 - services/mediacodec/main_codecservice.cpp | 1 - 20 files changed, 119 insertions(+), 449 deletions(-) delete mode 100644 media/libstagefright/omx/BWGraphicBufferSource.cpp delete mode 100644 media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index f92d1afde2..965e438aa1 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -20,7 +20,6 @@ cc_library { "libhidltransport", "liblog", "libstagefright_bufferpool@2.0", - "libstagefright_bufferqueue_helper", "libui", "libutils", ], diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index a212651570..66457e7e3b 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -40,7 +40,6 @@ cc_library_shared { "libmedia", "libmedia_omx", "libsfplugin_ccodec_utils", - "libstagefright_bufferqueue_helper", "libstagefright_codecbase", "libstagefright_foundation", "libstagefright_omx_utils", diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index a529628d83..1d335901c5 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -65,7 +65,6 @@ cc_library_shared { "MediaCodecInfo.cpp", "OMXBuffer.cpp", "omx/1.0/WGraphicBufferSource.cpp", - "omx/1.0/WOmx.cpp", "omx/1.0/WOmxBufferSource.cpp", "omx/1.0/WOmxNode.cpp", "omx/1.0/WOmxObserver.cpp", @@ -75,13 +74,16 @@ cc_library_shared { local_include_dirs: ["aidl"], export_aidl_headers: true, }, + + local_include_dirs: [ + "include", + ], shared_libs: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", "libbinder", "libcutils", - "libgui", "libhidlbase", "libhidltransport", "liblog", @@ -93,21 +95,84 @@ cc_library_shared { export_shared_lib_headers: [ "android.hidl.token@1.0-utils", "android.hardware.media.omx@1.0", - "libgui", "libstagefright_foundation", "libui", ], header_libs: [ - "libmedia_headers", + "libstagefright_headers", + "media_plugin_headers", ], export_header_lib_headers: [ - "libmedia_headers", + "libstagefright_headers", + "media_plugin_headers", ], export_include_dirs: [ "aidl", + "include", + ], + + cflags: [ + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wall", + ], + + sanitize: { + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + cfi: true, + }, +} + + +cc_library_shared { + name: "libmedia_omx_client", + + srcs: [ + "omx/1.0/WOmx.cpp", + ], + + local_include_dirs: [ + "include", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libgui", + "libhidlbase", + "libhidltransport", + "liblog", + "libmedia_omx", + "libstagefright_foundation", + "libui", + "libutils", + ], + + export_shared_lib_headers: [ + "libgui", + "libmedia_omx", + "libstagefright_foundation", + "libui", + ], + + header_libs: [ + "libstagefright_headers", + "media_plugin_headers", + ], + + export_header_lib_headers: [ + "libstagefright_headers", + "media_plugin_headers", + ], + + export_include_dirs: [ + "include", ], cflags: [ diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index 747b88f6e4..bc0c2cd917 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -62,79 +61,6 @@ enum { SET_QUIRKS, }; -class BpOMX : public BpInterface { -public: - explicit BpOMX(const sp &impl) - : BpInterface(impl) { - } - - virtual status_t listNodes(List *list) { - list->clear(); - - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - remote()->transact(LIST_NODES, data, &reply); - - int32_t n = reply.readInt32(); - for (int32_t i = 0; i < n; ++i) { - list->push_back(ComponentInfo()); - ComponentInfo &info = *--list->end(); - - info.mName = reply.readString8(); - int32_t numRoles = reply.readInt32(); - for (int32_t j = 0; j < numRoles; ++j) { - info.mRoles.push_back(reply.readString8()); - } - } - - return OK; - } - - virtual status_t allocateNode( - const char *name, const sp &observer, - sp *omxNode) { - Parcel data, reply; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - data.writeCString(name); - data.writeStrongBinder(IInterface::asBinder(observer)); - remote()->transact(ALLOCATE_NODE, data, &reply); - - status_t err = reply.readInt32(); - if (err == OK) { - *omxNode = IOMXNode::asInterface(reply.readStrongBinder()); - } else { - omxNode->clear(); - } - - return err; - } - - virtual status_t createInputSurface( - sp *bufferProducer, - sp *bufferSource) { - Parcel data, reply; - status_t err; - data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); - err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply); - if (err != OK) { - ALOGW("binder transaction failed: %d", err); - return err; - } - - err = reply.readInt32(); - if (err != OK) { - return err; - } - - *bufferProducer = IGraphicBufferProducer::asInterface( - reply.readStrongBinder()); - *bufferSource = IGraphicBufferSource::asInterface( - reply.readStrongBinder()); - - return err; - } -}; - class BpOMXNode : public BpInterface { public: explicit BpOMXNode(const sp &impl) @@ -551,7 +477,6 @@ public: } }; -IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX"); IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); //////////////////////////////////////////////////////////////////////////////// @@ -562,82 +487,6 @@ IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode"); return PERMISSION_DENIED; \ } } while (0) -status_t BnOMX::onTransact( - uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { - switch (code) { - case LIST_NODES: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - List list; - listNodes(&list); - - reply->writeInt32(list.size()); - for (List::iterator it = list.begin(); - it != list.end(); ++it) { - ComponentInfo &cur = *it; - - reply->writeString8(cur.mName); - reply->writeInt32(cur.mRoles.size()); - for (List::iterator role_it = cur.mRoles.begin(); - role_it != cur.mRoles.end(); ++role_it) { - reply->writeString8(*role_it); - } - } - - return NO_ERROR; - } - - case ALLOCATE_NODE: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - const char *name = data.readCString(); - - sp observer = - interface_cast(data.readStrongBinder()); - - if (name == NULL || observer == NULL) { - ALOGE("b/26392700"); - reply->writeInt32(INVALID_OPERATION); - return NO_ERROR; - } - - sp omxNode; - - status_t err = allocateNode(name, observer, &omxNode); - - reply->writeInt32(err); - if (err == OK) { - reply->writeStrongBinder(IInterface::asBinder(omxNode)); - } - - return NO_ERROR; - } - - case CREATE_INPUT_SURFACE: - { - CHECK_OMX_INTERFACE(IOMX, data, reply); - - sp bufferProducer; - sp bufferSource; - status_t err = createInputSurface(&bufferProducer, &bufferSource); - - reply->writeInt32(err); - - if (err == OK) { - reply->writeStrongBinder(IInterface::asBinder(bufferProducer)); - reply->writeStrongBinder(IInterface::asBinder(bufferSource)); - } - - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - status_t BnOMXNode::onTransact( uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { switch (code) { diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h index e69c02dda1..7e7c2d23dc 100644 --- a/media/libmedia/include/media/IOMX.h +++ b/media/libmedia/include/media/IOMX.h @@ -47,9 +47,8 @@ struct omx_message; using hardware::media::omx::V1_0::IOmxNode; -class IOMX : public IInterface { +class IOMX : public RefBase { public: - DECLARE_META_INTERFACE(OMX); typedef uint32_t buffer_id; @@ -224,14 +223,6 @@ public: }; //////////////////////////////////////////////////////////////////////////////// - -class BnOMX : public BnInterface { -public: - virtual status_t onTransact( - uint32_t code, const Parcel &data, Parcel *reply, - uint32_t flags = 0); -}; - class BnOMXNode : public BnInterface { public: virtual status_t onTransact( diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h index 3700a237f1..babda222bf 100644 --- a/media/libmedia/include/media/omx/1.0/Conversion.h +++ b/media/libmedia/include/media/omx/1.0/Conversion.h @@ -31,12 +31,12 @@ #include #include +#include #include #include #include #include #include -#include #include #include @@ -282,8 +282,8 @@ inline Status toStatus(status_t l) { case TIMED_OUT: case ERROR_UNSUPPORTED: case UNKNOWN_ERROR: - case IGraphicBufferProducer::RELEASE_ALL_BUFFERS: - case IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION: + case BufferQueueDefs::RELEASE_ALL_BUFFERS: + case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION: return static_cast(l); case NOT_ENOUGH_DATA: return Status::BUFFER_NEEDS_REALLOCATION; diff --git a/media/libmedia/include/media/omx/1.0/WOmx.h b/media/libmedia/include/media/omx/1.0/WOmx.h index f13546e1e0..0680eecab9 100644 --- a/media/libmedia/include/media/omx/1.0/WOmx.h +++ b/media/libmedia/include/media/omx/1.0/WOmx.h @@ -47,7 +47,6 @@ using ::android::sp; using ::android::List; using ::android::IOMX; -using ::android::BnOMX; /** * Wrapper classes for conversion @@ -58,7 +57,7 @@ using ::android::BnOMX; * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object. */ -struct LWOmx : public BnOMX { +struct LWOmx : public IOMX { sp mBase; LWOmx(sp const& base); status_t listNodes(List* list) override; diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp index 08519cdab4..dca6bb694f 100644 --- a/media/libmediaplayer2/Android.bp +++ b/media/libmediaplayer2/Android.bp @@ -38,6 +38,7 @@ cc_library_static { export_shared_lib_headers: [ "libaudioclient", "libbinder", + "libgui", "libmedia_omx", ], diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index b05718ce02..5932518211 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -187,6 +187,7 @@ cc_library { "liblog", "libmedia", "libmedia_omx", + "libmedia_omx_client", "libaudioclient", "libmediametrics", "libmediautils", diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index a4374c9d91..35df6d7dde 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -281,6 +281,27 @@ private: } }; +struct GraphicBufferSource::ConsumerProxy : public BufferQueue::ConsumerListener { + ConsumerProxy(const sp &gbs) : mGbs(gbs) {} + + ~ConsumerProxy() = default; + + void onFrameAvailable(const BufferItem& item) override { + mGbs->onFrameAvailable(item); + } + + void onBuffersReleased() override { + mGbs->onBuffersReleased(); + } + + void onSidebandStreamChanged() override { + mGbs->onSidebandStreamChanged(); + } + +private: + sp mGbs; +}; + GraphicBufferSource::GraphicBufferSource() : mInitCheck(UNKNOWN_ERROR), mNumAvailableUnacquiredBuffers(0), @@ -313,14 +334,12 @@ GraphicBufferSource::GraphicBufferSource() : BufferQueue::createBufferQueue(&mProducer, &mConsumer); mConsumer->setConsumerName(name); - // Note that we can't create an sp<...>(this) in a ctor that will not keep a - // reference once the ctor ends, as that would cause the refcount of 'this' - // dropping to 0 at the end of the ctor. Since all we need is a wp<...> - // that's what we create. - wp listener = - static_cast(this); + // create the consumer listener interface, and hold sp so that this + // interface lives as long as the GraphicBufferSource. + mConsumerProxy = new ConsumerProxy(this); + sp proxy = - new BufferQueue::ProxyConsumerListener(listener); + new BufferQueue::ProxyConsumerListener(mConsumerProxy); mInitCheck = mConsumer->consumerConnect(proxy, false); if (mInitCheck != NO_ERROR) { diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h index abc89105e5..99e444b910 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h @@ -68,7 +68,7 @@ struct FrameDropper; * (even if it was dropped) to reencode it after an interval if no further * frames are sent by the producer. */ -class GraphicBufferSource : public BufferQueue::ConsumerListener { +class GraphicBufferSource : public RefBase { public: GraphicBufferSource(); @@ -190,8 +190,6 @@ public: status_t setColorAspects(int32_t aspectsPacked); protected: - // BQ::ConsumerListener interface - // ------------------------------ // BufferQueue::ConsumerListener interface, called when a new frame of // data is available. If we're executing and a codec buffer is @@ -199,19 +197,24 @@ protected: // into the codec buffer, and call Empty[This]Buffer. If we're not yet // executing or there's no codec buffer available, we just increment // mNumFramesAvailable and return. - void onFrameAvailable(const BufferItem& item) override; + void onFrameAvailable(const BufferItem& item) ; // BufferQueue::ConsumerListener interface, called when the client has // released one or more GraphicBuffers. We clear out the appropriate // set of mBufferSlot entries. - void onBuffersReleased() override; + void onBuffersReleased() ; // BufferQueue::ConsumerListener interface, called when the client has // changed the sideband stream. GraphicBufferSource doesn't handle sideband // streams so this is a no-op (and should never be called). - void onSidebandStreamChanged() override; + void onSidebandStreamChanged() ; private: + // BQ::ConsumerListener interface + // ------------------------------ + struct ConsumerProxy; + sp mConsumerProxy; + // Lock, covers all member variables. mutable Mutex mMutex; diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 15952e35df..0c5075236f 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -6,7 +6,6 @@ cc_library_shared { }, srcs: [ - "BWGraphicBufferSource.cpp", "OMXMaster.cpp", "OMXNodeInstance.cpp", "OMXUtils.cpp", @@ -39,7 +38,6 @@ cc_library_shared { "libutils", "liblog", "libui", - "libgui", "libcutils", "libstagefright_foundation", "libstagefright_bufferqueue_helper", @@ -55,7 +53,6 @@ cc_library_shared { ], export_shared_lib_headers: [ - "libmedia_omx", "libstagefright_foundation", "libstagefright_xmlparser", "libutils", diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp deleted file mode 100644 index fa30a46e53..0000000000 --- a/media/libstagefright/omx/BWGraphicBufferSource.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "BWGraphicBufferSource" - -#include -#include -#include -#include -#include -#include - -namespace android { - -static const OMX_U32 kPortIndexInput = 0; - -struct BWGraphicBufferSource::BWOmxNodeWrapper : public IOmxNodeWrapper { - sp mOMXNode; - - BWOmxNodeWrapper(const sp &omxNode): mOMXNode(omxNode) { - } - - virtual status_t emptyBuffer( - int32_t bufferId, uint32_t flags, - const sp &buffer, - int64_t timestamp, int fenceFd) override { - return mOMXNode->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd); - } - - virtual void dispatchDataSpaceChanged( - int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override { - omx_message msg; - msg.type = omx_message::EVENT; - msg.fenceFd = -1; - msg.u.event_data.event = OMX_EventDataSpaceChanged; - msg.u.event_data.data1 = dataSpace; - msg.u.event_data.data2 = aspects; - msg.u.event_data.data3 = pixelFormat; - mOMXNode->dispatchMessage(msg); - } -}; - -struct BWGraphicBufferSource::BWOMXBufferSource : public BnOMXBufferSource { - sp mSource; - - BWOMXBufferSource(const sp &source): mSource(source) { - } - - Status onOmxExecuting() override { - return mSource->onOmxExecuting(); - } - - Status onOmxIdle() override { - return mSource->onOmxIdle(); - } - - Status onOmxLoaded() override { - return mSource->onOmxLoaded(); - } - - Status onInputBufferAdded(int bufferId) override { - return mSource->onInputBufferAdded(bufferId); - } - - Status onInputBufferEmptied( - int bufferId, const OMXFenceParcelable& fenceParcel) override { - return mSource->onInputBufferEmptied(bufferId, fenceParcel.get()); - } -}; - -BWGraphicBufferSource::BWGraphicBufferSource( - sp const& base) : - mBase(base), - mOMXBufferSource(new BWOMXBufferSource(base)) { -} - -::android::binder::Status BWGraphicBufferSource::configure( - const sp& omxNode, int32_t dataSpace) { - // Do setInputSurface() first, the node will try to enable metadata - // mode on input, and does necessary error checking. If this fails, - // we can't use this input surface on the node. - status_t err = omxNode->setInputSurface(mOMXBufferSource); - if (err != NO_ERROR) { - ALOGE("Unable to set input surface: %d", err); - return Status::fromStatusT(err); - } - - // use consumer usage bits queried from encoder, but always add - // HW_VIDEO_ENCODER for backward compatibility. - uint32_t consumerUsage; - if (omxNode->getParameter( - (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits, - &consumerUsage, sizeof(consumerUsage)) != OK) { - consumerUsage = 0; - } - - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - def.nPortIndex = kPortIndexInput; - - err = omxNode->getParameter( - OMX_IndexParamPortDefinition, &def, sizeof(def)); - if (err != NO_ERROR) { - ALOGE("Failed to get port definition: %d", err); - return Status::fromStatusT(UNKNOWN_ERROR); - } - - return Status::fromStatusT(mBase->configure( - new BWOmxNodeWrapper(omxNode), - dataSpace, - def.nBufferCountActual, - def.format.video.nFrameWidth, - def.format.video.nFrameHeight, - consumerUsage)); -} - -::android::binder::Status BWGraphicBufferSource::setSuspend( - bool suspend, int64_t timeUs) { - return Status::fromStatusT(mBase->setSuspend(suspend, timeUs)); -} - -::android::binder::Status BWGraphicBufferSource::setRepeatPreviousFrameDelayUs( - int64_t repeatAfterUs) { - return Status::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs)); -} - -::android::binder::Status BWGraphicBufferSource::setMaxFps(float maxFps) { - return Status::fromStatusT(mBase->setMaxFps(maxFps)); -} - -::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig( - double fps, double captureFps) { - return Status::fromStatusT(mBase->setTimeLapseConfig( - fps, captureFps)); -} - -::android::binder::Status BWGraphicBufferSource::setStartTimeUs( - int64_t startTimeUs) { - return Status::fromStatusT(mBase->setStartTimeUs(startTimeUs)); -} - -::android::binder::Status BWGraphicBufferSource::setStopTimeUs( - int64_t stopTimeUs) { - return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs)); -} - -::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs( - int64_t *stopTimeOffsetUs) { - return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs)); -} - -::android::binder::Status BWGraphicBufferSource::setColorAspects( - int32_t aspects) { - return Status::fromStatusT(mBase->setColorAspects(aspects)); -} - -::android::binder::Status BWGraphicBufferSource::setTimeOffsetUs( - int64_t timeOffsetsUs) { - return Status::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs)); -} - -::android::binder::Status BWGraphicBufferSource::signalEndOfInputStream() { - return Status::fromStatusT(mBase->signalEndOfInputStream()); -} - -} // namespace android diff --git a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h deleted file mode 100644 index 0efff2277e..0000000000 --- a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BWGRAPHIC_BUFFER_SOURCE_H_ -#define BWGRAPHIC_BUFFER_SOURCE_H_ - -#include -#include -#include -#include -#include - -#include "OmxGraphicBufferSource.h" -#include "IOmxNodeWrapper.h" - -namespace android { - -using ::android::binder::Status; -using ::android::BnGraphicBufferSource; -using ::android::OmxGraphicBufferSource; -using ::android::IOMXNode; -using ::android::sp; - -struct BWGraphicBufferSource : public BnGraphicBufferSource { - struct BWOMXBufferSource; - struct BWOmxNodeWrapper; - - sp mBase; - sp mOMXBufferSource; - - BWGraphicBufferSource(sp const &base); - - Status configure( - const sp& omxNode, int32_t dataSpace) override; - Status setSuspend(bool suspend, int64_t timeUs) override; - Status setRepeatPreviousFrameDelayUs( - int64_t repeatAfterUs) override; - Status setMaxFps(float maxFps) override; - Status setTimeLapseConfig( - double fps, double captureFps) override; - Status setStartTimeUs(int64_t startTimeUs) override; - Status setStopTimeUs(int64_t stopTimeUs) override; - Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override; - Status setColorAspects(int32_t aspects) override; - Status setTimeOffsetUs(int64_t timeOffsetsUs) override; - Status signalEndOfInputStream() override; -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERSOURCE_H diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp index fb03229d99..569fa88106 100644 --- a/media/libstagefright/omx/tests/Android.bp +++ b/media/libstagefright/omx/tests/Android.bp @@ -18,12 +18,6 @@ cc_test { "libnativewindow", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", - "android.hardware.media.omx@1.0", - ], - - include_dirs: [ - "frameworks/av/media/libstagefright", - "frameworks/native/include/media/openmax", ], header_libs: [ diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index c2f1072469..cc8c2340d0 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -40,9 +40,8 @@ #include #include #include +#include #include -#include -#include #include #define DEFAULT_TIMEOUT 500000 @@ -81,12 +80,13 @@ status_t Harness::initCheck() const { } status_t Harness::initOMX() { - using namespace ::android::hardware::media::omx::V1_0; - sp tOmx = IOmx::getService(); - if (tOmx == nullptr) { + OMXClient client; + if (client.connect() != OK) { + ALOGE("Failed to connect to OMX to create persistent input surface."); return NO_INIT; } - mOMX = new utils::LWOmx(tOmx); + + mOMX = client.interface(); return mOMX != 0 ? OK : NO_INIT; } diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index a4f5730b6e..7d1c88bd8c 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -81,7 +81,6 @@ cc_library_shared { "libmediadrm", "libstagefright", "libstagefright_foundation", - "libstagefright_bufferqueue_helper", "liblog", "libutils", "libcutils", diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp index 22e15d3f33..baa4fc77ab 100644 --- a/media/ndk/NdkImageReader.cpp +++ b/media/ndk/NdkImageReader.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include using namespace android; diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 9cc19a378c..473e21c0a6 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -32,7 +32,6 @@ LOCAL_REQUIRED_MODULES_x86 := mediacodec.policy endif LOCAL_SRC_FILES := main_codecservice.cpp LOCAL_SHARED_LIBRARIES := \ - libmedia_omx \ libbinder \ libutils \ liblog \ diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp index 6d47be60d6..6ffbd268a3 100644 --- a/services/mediacodec/main_codecservice.cpp +++ b/services/mediacodec/main_codecservice.cpp @@ -25,7 +25,6 @@ #include #include -#include #include using namespace android; -- GitLab From 47affe5c842bdab14d5889d7a12da74b9ac97eb5 Mon Sep 17 00:00:00 2001 From: jiabin Date: Thu, 4 Apr 2019 18:02:07 -0700 Subject: [PATCH 1261/1530] Copy haptic data directly from effect in buffer to out buffer. The haptic data will be partially interleaved at the end of the buffer after processing audio mixing. When processing audio effect, only audio data will be handled. In that case, haptic data will be missed if there is any audio effect. Copying haptic data directly from audio effect in buffer to out buffer could help solve the problem. Bug: 129956425 Test: play haptic with audio effect. Change-Id: I2b48bb43bec10167d4eacbcaa5c27959e0d44c32 --- services/audioflinger/Threads.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 04d62fac4c..822e490a5e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3610,8 +3610,30 @@ bool AudioFlinger::PlaybackThread::threadLoop() // only process effects if we're going to write if (mSleepTimeUs == 0 && mType != OFFLOAD) { + audio_session_t activeHapticId = AUDIO_SESSION_NONE; + if (mHapticChannelCount > 0 && effectChains.size() > 0) { + for (auto track : mActiveTracks) { + if (track->getHapticPlaybackEnabled()) { + activeHapticId = track->sessionId(); + break; + } + } + } for (size_t i = 0; i < effectChains.size(); i ++) { effectChains[i]->process_l(); + // TODO: Write haptic data directly to sink buffer when mixing. + if (activeHapticId != AUDIO_SESSION_NONE + && activeHapticId == effectChains[i]->sessionId()) { + // Haptic data is active in this case, copy it directly from + // in buffer to out buffer. + const size_t audioBufferSize = mNormalFrameCount + * audio_bytes_per_frame(mChannelCount, EFFECT_BUFFER_FORMAT); + memcpy_by_audio_format( + (uint8_t*)effectChains[i]->outBuffer() + audioBufferSize, + EFFECT_BUFFER_FORMAT, + (const uint8_t*)effectChains[i]->inBuffer() + audioBufferSize, + EFFECT_BUFFER_FORMAT, mNormalFrameCount * mHapticChannelCount); + } } } } -- GitLab From ccd5914d720d3bc3c2a17e2fe6b7bd01a63992ee Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Mon, 8 Apr 2019 17:55:59 -0700 Subject: [PATCH 1262/1530] mediaplayer2: fix state during data source switching Test: cts Bug: 130126316 Change-Id: Ic2022435954521a113082a13b05947abbe84ba2f --- media/libmediaplayer2/mediaplayer2.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index ae7ac59993..c34f1c9a54 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -1026,8 +1026,16 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play case MEDIA2_NOP: // interface test message break; case MEDIA2_PREPARED: - ALOGV("MediaPlayer2::notify() prepared"); - mCurrentState = MEDIA_PLAYER2_PREPARED; + ALOGV("MediaPlayer2::notify() prepared, srcId=%lld", (long long)srcId); + if (srcId == mSrcId) { + mCurrentState = MEDIA_PLAYER2_PREPARED; + } + break; + case MEDIA2_STARTED: + ALOGV("MediaPlayer2::notify() started, srcId=%lld", (long long)srcId); + if (srcId == mSrcId) { + mCurrentState = MEDIA_PLAYER2_STARTED; + } break; case MEDIA2_DRM_INFO: ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)", @@ -1038,7 +1046,7 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play if (mCurrentState == MEDIA_PLAYER2_IDLE) { ALOGE("playback complete in idle state"); } - if (!mLoop) { + if (!mLoop && srcId == mSrcId) { mCurrentState = MEDIA_PLAYER2_PLAYBACK_COMPLETE; } break; @@ -1054,6 +1062,10 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play // ext2: Implementation dependant error code. if (ext1 != MEDIA2_INFO_VIDEO_TRACK_LAGGING) { ALOGW("info/warning (%d, %d)", ext1, ext2); + + if (ext1 == MEDIA2_INFO_DATA_SOURCE_START && srcId == mSrcId) { + mCurrentState = MEDIA_PLAYER2_STARTED; + } } break; case MEDIA2_SEEK_COMPLETE: -- GitLab From 98731a2c2559d9986b0892c6a8c8ffca17cd68fc Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 8 Apr 2019 19:19:07 -0700 Subject: [PATCH 1263/1530] AudioTimestamp: Use current time when data pipeline is empty Handles change in Q, where we do not return an invalid kernel time in the extended timestamp when there is a reported error from the HAL. Test: Photos, File playback, AudioTrackSurroundTest#testPlaySineSweepBytes Bug: 113294730 Bug: 118506063 Bug: 119895849 Bug: 120631792 Change-Id: I90f2c7c314baa474c3dd3a24042f1d4b8bc0a12b --- media/libaudioclient/AudioTrack.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index e59f7e021a..ded9cb7f24 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -2603,6 +2603,20 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) ALOGV_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_SERVER, "%s(%d): location moved from server to kernel", __func__, mPortId); + + if (ets.mPosition[ExtendedTimestamp::LOCATION_SERVER] == + ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL]) { + // In Q, we don't return errors as an invalid time + // but instead we leave the last kernel good timestamp alone. + // + // If server is identical to kernel, the device data pipeline is idle. + // A better start time is now. The retrograde check ensures + // timestamp monotonicity. + const int64_t nowNs = systemTime(); + ALOGD("%s(%d) device stall, using current time %lld", + __func__, mPortId, (long long)nowNs); + timestamp.mTime = convertNsToTimespec(nowNs); + } } // We update the timestamp time even when paused. -- GitLab From 3b8c633f3907218cda99f1f65e633b76419640bf Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Wed, 3 Apr 2019 19:29:36 -0700 Subject: [PATCH 1264/1530] AudioTimestamp: advance first timestamp time after start if stale Test: Photos, File playback, AudioTrackSurroundTest#testPlaySineSweepBytes Bug: 113294730 Bug: 118506063 Bug: 119895849 Bug: 120631792 Change-Id: I83ec1e4c0ae2318fb69744a6265663a50682c485 --- media/libaudioclient/AudioTrack.cpp | 39 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index ded9cb7f24..4a1c717c08 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -2754,27 +2754,32 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // Prevent retrograde motion in timestamp. // This is sometimes caused by erratic reports of the available space in the ALSA drivers. if (status == NO_ERROR) { + // Fix stale time when checking timestamp right after start(). + // The position is at the last reported location but the time can be stale + // due to pause or standby or cold start latency. + // + // We keep advancing the time (but not the position) to ensure that the + // stale value does not confuse the application. + // + // For offload compatibility, use a default lag value here. + // Any time discrepancy between this update and the pause timestamp is handled + // by the retrograde check afterwards. + int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime); + const int64_t lagNs = int64_t(mAfLatency * 1000000LL); + const int64_t limitNs = mStartNs - lagNs; + if (currentTimeNanos < limitNs) { + ALOGD("%s(%d): correcting timestamp time for pause, " + "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld", + __func__, mPortId, + (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs); + timestamp.mTime = convertNsToTimespec(limitNs); + currentTimeNanos = limitNs; + } + // previousTimestampValid is set to false when starting after a stop or flush. if (previousTimestampValid) { const int64_t previousTimeNanos = audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime); - int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime); - - // Fix stale time when checking timestamp right after start(). - // - // For offload compatibility, use a default lag value here. - // Any time discrepancy between this update and the pause timestamp is handled - // by the retrograde check afterwards. - const int64_t lagNs = int64_t(mAfLatency * 1000000LL); - const int64_t limitNs = mStartNs - lagNs; - if (currentTimeNanos < limitNs) { - ALOGD("%s(%d): correcting timestamp time for pause, " - "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld", - __func__, mPortId, - (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs); - timestamp.mTime = convertNsToTimespec(limitNs); - currentTimeNanos = limitNs; - } // retrograde check if (currentTimeNanos < previousTimeNanos) { -- GitLab From c976f1260aa56ffdd6778754f208bc619c9dbf94 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 10 Apr 2019 02:08:28 -0700 Subject: [PATCH 1265/1530] Move vintf_fragments for sw C2 to mediaserver This is a workaround for b/130058564. Test: adb shell stagefright -i | grep "c2.android" Bug: 130254909 Change-Id: I8e8f7bbd9c1f777285cdcc58ea6610639122c928 --- apex/Android.bp | 1 - media/mediaserver/Android.bp | 1 + {apex => media/mediaserver}/manifest_media_c2_software.xml | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {apex => media/mediaserver}/manifest_media_c2_software.xml (100%) diff --git a/apex/Android.bp b/apex/Android.bp index 6673543dab..0a9551d4d3 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -99,7 +99,6 @@ apex { name: "com.android.media.swcodec", manifest: "manifest_codec.json", defaults: ["com.android.media.swcodec-defaults"], - vintf_fragments: ["manifest_media_c2_software.xml"], } apex_key { diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp index 6a1cc71333..a968890b32 100644 --- a/media/mediaserver/Android.bp +++ b/media/mediaserver/Android.bp @@ -43,4 +43,5 @@ cc_binary { "-Wall", ], + vintf_fragments: ["manifest_media_c2_software.xml"], } diff --git a/apex/manifest_media_c2_software.xml b/media/mediaserver/manifest_media_c2_software.xml similarity index 100% rename from apex/manifest_media_c2_software.xml rename to media/mediaserver/manifest_media_c2_software.xml -- GitLab From bcfe5be7cd16530269aaa204d25013d2df18b9e2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 9 Apr 2019 19:56:39 -0700 Subject: [PATCH 1266/1530] audio policy: fix output session effect leak Clean up session effects in releaseOutput in case stopOutput is not received before release. Bug: 124689305 Test: start/stop cast screen while playing music Change-Id: Ie0588dd3336d56d34c2d717268fcd0918cbf5717 --- .../service/AudioPolicyInterfaceImpl.cpp | 56 ++++++++++--------- .../audiopolicy/service/AudioPolicyService.h | 6 ++ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index b8036bbb8d..2eb272e111 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -225,6 +225,21 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *original return result; } +void AudioPolicyService::getPlaybackClientAndEffects(audio_port_handle_t portId, + sp& client, + sp& effects, + const char *context) +{ + Mutex::Autolock _l(mLock); + const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); + if (index < 0) { + ALOGE("%s AudioTrack client not found for portId %d", context, portId); + return; + } + client = mAudioPlaybackClients.valueAt(index); + effects = mAudioPolicyEffects; +} + status_t AudioPolicyService::startOutput(audio_port_handle_t portId) { if (mAudioPolicyManager == NULL) { @@ -233,16 +248,9 @@ status_t AudioPolicyService::startOutput(audio_port_handle_t portId) ALOGV("startOutput()"); sp client; spaudioPolicyEffects; - { - Mutex::Autolock _l(mLock); - const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); - if (index < 0) { - ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); - return INVALID_OPERATION; - } - client = mAudioPlaybackClients.valueAt(index); - audioPolicyEffects = mAudioPolicyEffects; - } + + getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__); + if (audioPolicyEffects != 0) { // create audio processors according to stream status_t status = audioPolicyEffects->addOutputSessionEffects( @@ -275,17 +283,9 @@ status_t AudioPolicyService::doStopOutput(audio_port_handle_t portId) ALOGV("doStopOutput"); sp client; spaudioPolicyEffects; - { - Mutex::Autolock _l(mLock); - const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); - if (index < 0) { - ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); - return INVALID_OPERATION; - } - client = mAudioPlaybackClients.valueAt(index); - audioPolicyEffects = mAudioPolicyEffects; - } + getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__); + if (audioPolicyEffects != 0) { // release audio processors from the stream status_t status = audioPolicyEffects->releaseOutputSessionEffects( @@ -315,13 +315,17 @@ void AudioPolicyService::releaseOutput(audio_port_handle_t portId) void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId) { ALOGV("doReleaseOutput from tid %d", gettid()); - Mutex::Autolock _l(mLock); - const ssize_t index = mAudioPlaybackClients.indexOfKey(portId); - if (index < 0) { - ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId); - return; + sp client; + sp audioPolicyEffects; + + getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__); + + if (audioPolicyEffects != 0 && client->active) { + // clean up effects if output was not stopped before being released + audioPolicyEffects->releaseOutputSessionEffects( + client->io, client->stream, client->session); } - sp client = mAudioPlaybackClients.valueAt(index); + Mutex::Autolock _l(mLock); mAudioPlaybackClients.removeItem(portId); // called from internal thread: no need to clear caller identity diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index efdba5601b..189322f594 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -782,6 +782,12 @@ private: const audio_stream_type_t stream; }; + void getPlaybackClientAndEffects(audio_port_handle_t portId, + sp& client, + sp& effects, + const char *context); + + // A class automatically clearing and restoring binder caller identity inside // a code block (scoped variable) // Declare one systematically before calling AudioPolicyManager methods so that they are -- GitLab From 698d353587d6d0563a6451b2d701358e6cb4496e Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Wed, 10 Apr 2019 15:20:05 +0200 Subject: [PATCH 1267/1530] audiopolicy: fix mix recorder regression Engine is not only responsible of legacy remote submix for input device selection (aka "0" adress) but also regular remote submix with a given address. Filtering the remote submixes prevents engine to select the remote submix for input stream in case a policy mix rule has been set to record specific sources. Bug: 130284799 Test: AudioPolicyEmulatorTests --gtest_filter=RecordInjectionSourceMatch/DynamicPolicyMixRecordInjectionTest Change-Id: Iaafd1195b9ab5c82ad3bd2766061d982a10bb9da Signed-off-by: Francois Gaffie --- services/audiopolicy/managerdefault/AudioPolicyManager.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index a700868da7..de447fbf57 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -348,7 +348,8 @@ protected: } virtual const DeviceVector getAvailableInputDevices() const { - return mAvailableInputDevices.filterForEngine(); + // legacy and non-legacy remote-submix are managed by the engine, do not filter + return mAvailableInputDevices; } virtual const sp &getDefaultOutputDevice() const { -- GitLab From 393f0e05e3fa7351c81a470e5bd841451bcc13ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 10 Apr 2019 09:09:08 +0200 Subject: [PATCH 1268/1530] audiopolicy: fix attributes match helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression caught on Attributes referred by their flags only (like BT SCO). Client may have more flags than the product strategy, checks to be done on all the flags of the product strategy are requested by the client. Bug: 130284799 Test: make Change-Id: Ie8d41a592342bc0a7a0326f9766e7e7503154e62 Signed-off-by: François Gaffie --- media/libaudioclient/AudioTrack.cpp | 10 ++-------- media/libmedia/include/media/TypeConverter.h | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index e59f7e021a..3462876acf 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -250,10 +250,7 @@ AudioTrack::AudioTrack( mPreviousSchedulingGroup(SP_DEFAULT), mPausedPosition(0) { - mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; - mAttributes.usage = AUDIO_USAGE_UNKNOWN; - mAttributes.flags = 0x0; - strcpy(mAttributes.tags, ""); + mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; (void)set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, @@ -286,10 +283,7 @@ AudioTrack::AudioTrack( mPausedPosition(0), mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE) { - mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; - mAttributes.usage = AUDIO_USAGE_UNKNOWN; - mAttributes.flags = 0x0; - strcpy(mAttributes.tags, ""); + mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; (void)set(streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, cbf, user, notificationFrames, diff --git a/media/libmedia/include/media/TypeConverter.h b/media/libmedia/include/media/TypeConverter.h index 3acfe981ec..2f8c209ac1 100644 --- a/media/libmedia/include/media/TypeConverter.h +++ b/media/libmedia/include/media/TypeConverter.h @@ -305,8 +305,8 @@ static inline std::string toString(const audio_attributes_t& attributes) result << "{ Content type: " << toString(attributes.content_type) << " Usage: " << toString(attributes.usage) << " Source: " << toString(attributes.source) - << " Flags: " << attributes.flags - << " Tags: " << attributes.tags + << std::hex << " Flags: 0x" << attributes.flags + << std::dec << " Tags: " << attributes.tags << " }"; return result.str(); -- GitLab From f96e543868c4ba0c24a060d15e20748921593db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 9 Apr 2019 17:13:56 +0200 Subject: [PATCH 1269/1530] audiopolicy: fix routing of exclusive preferred device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In call music played on remote is called an "exclusive" preferred device. It may not be propagated to other client that may play locally. The client of the exclusive preferred device is not routed as the exclusive preferred device are ignored from findPreferredDevice helper. This CL fixes the routing Bug: 130284799 Test: AudioPolicyEmulatorTests --gtest_filter=FilteringExpliciRoutingTest* Change-Id: Iec7e14cb170d0f67c8c0b231f8219c133d7d2185 Signed-off-by: François Gaffie --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 80393ca556..af29f87eda 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1641,7 +1641,14 @@ status_t AudioPolicyManager::startSource(const sp& outp outputDesc->setClientActive(client, true); if (client->hasPreferredDevice(true)) { - devices = getNewOutputDevices(outputDesc, false /*fromCache*/); + if (outputDesc->clientsList(true /*activeOnly*/).size() == 1 && + client->isPreferredDeviceForExclusiveUse()) { + // Preferred device may be exclusive, use only if no other active clients on this output + devices = DeviceVector( + mAvailableOutputDevices.getDeviceFromId(client->preferredDeviceId())); + } else { + devices = getNewOutputDevices(outputDesc, false /*fromCache*/); + } if (devices != outputDesc->devices()) { checkStrategyRoute(clientStrategy, outputDesc->mIoHandle); } -- GitLab From a261e381909c63ad0c681bfc7f0e509f1acc00f4 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 10 Apr 2019 11:37:50 -0700 Subject: [PATCH 1270/1530] CCodec: protect C2OMXNode::mBufferIdsInUse Bug: 129736016 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: Ifad57b5b0da49d2acdcfba063671c9748d06dc12 --- media/codec2/sfplugin/C2OMXNode.cpp | 20 +++++++++++++------- media/codec2/sfplugin/C2OMXNode.h | 5 +++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 962df0f5c0..6ae1c13ac4 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -287,7 +287,7 @@ status_t C2OMXNode::emptyBuffer( return UNKNOWN_ERROR; } - (void)mBufferIdsInUse.emplace(index, buffer); + mBufferIdsInUse.lock()->emplace(index, buffer); return OK; } @@ -327,13 +327,19 @@ void C2OMXNode::onInputBufferDone(c2_cntr64_t index) { ALOGD("Buffer source not set (index=%llu)", index.peekull()); return; } - auto it = mBufferIdsInUse.find(index.peeku()); - if (it == mBufferIdsInUse.end()) { - ALOGV("Untracked input index %llu (maybe already removed)", index.peekull()); - return; + + int32_t bufferId = 0; + { + decltype(mBufferIdsInUse)::Locked bufferIds(mBufferIdsInUse); + auto it = bufferIds->find(index.peeku()); + if (it == bufferIds->end()) { + ALOGV("Untracked input index %llu (maybe already removed)", index.peekull()); + return; + } + bufferId = it->second; + (void)bufferIds->erase(it); } - (void)mBufferSource->onInputBufferEmptied(it->second, -1); - (void)mBufferIdsInUse.erase(it); + (void)mBufferSource->onInputBufferEmptied(bufferId, -1); } } // namespace android diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h index b7bd696f75..3ca6c0aeca 100644 --- a/media/codec2/sfplugin/C2OMXNode.h +++ b/media/codec2/sfplugin/C2OMXNode.h @@ -20,9 +20,10 @@ #include #include +#include +#include #include #include -#include namespace android { @@ -111,7 +112,7 @@ private: c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame - std::map mBufferIdsInUse; + Mutexed> mBufferIdsInUse; }; } // namespace android -- GitLab From 8437d138d15676256a227f39ae46eed0eb169f63 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 10 Apr 2019 14:45:01 -0700 Subject: [PATCH 1271/1530] OWNERS: add andrewlewis@google.com Test: n/a Bug: 130307840 Change-Id: Ib62f509cf533c39a6d31782f9233a18a87c92c90 --- media/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/media/OWNERS b/media/OWNERS index 1e2d123d17..1afc253502 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -1,3 +1,4 @@ +andrewlewis@google.com chz@google.com dwkang@google.com elaurent@google.com -- GitLab From 4383fdc52899bf614e7c555c252dd4b9a3c81d32 Mon Sep 17 00:00:00 2001 From: Wei Jia Date: Wed, 10 Apr 2019 11:18:29 -0700 Subject: [PATCH 1272/1530] mediaplayer2: fix state during transitioning Test: cts Bug: 130292355 Change-Id: I18dd97aebe816280abf498a856bb387e73b0511a --- .../include/mediaplayer2/mediaplayer2.h | 1 + media/libmediaplayer2/mediaplayer2.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h index 2993ab1063..1e8a1d515f 100644 --- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h +++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h @@ -143,6 +143,7 @@ private: Mutex mNotifyLock; sp mListener; media_player2_internal_states mCurrentState; + bool mTransitionToNext; int64_t mCurrentPosition; MediaPlayer2SeekMode mCurrentSeekMode; int64_t mSeekPosition; diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp index c34f1c9a54..de65f8d9a9 100644 --- a/media/libmediaplayer2/mediaplayer2.cpp +++ b/media/libmediaplayer2/mediaplayer2.cpp @@ -241,6 +241,7 @@ MediaPlayer2::MediaPlayer2(int32_t sessionId, jobject context) { mSeekPosition = -1; mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC; mCurrentState = MEDIA_PLAYER2_IDLE; + mTransitionToNext = false; mLoop = false; mVolume = 1.0; mVideoWidth = mVideoHeight = 0; @@ -389,6 +390,7 @@ status_t MediaPlayer2::playNextDataSource(int64_t srcId) { return INVALID_OPERATION; } mSrcId = srcId; + mTransitionToNext = true; return mPlayer->playNextDataSource(srcId); } @@ -568,6 +570,7 @@ status_t MediaPlayer2::pause() { mCurrentState = MEDIA_PLAYER2_STATE_ERROR; } else { mCurrentState = MEDIA_PLAYER2_PAUSED; + mTransitionToNext = false; } return ret; } @@ -815,6 +818,7 @@ status_t MediaPlayer2::reset_l() { } else { mPlayer->setListener(NULL); mCurrentState = MEDIA_PLAYER2_IDLE; + mTransitionToNext = false; } // setDataSource has to be called again to create a // new mediaplayer. @@ -1031,12 +1035,6 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play mCurrentState = MEDIA_PLAYER2_PREPARED; } break; - case MEDIA2_STARTED: - ALOGV("MediaPlayer2::notify() started, srcId=%lld", (long long)srcId); - if (srcId == mSrcId) { - mCurrentState = MEDIA_PLAYER2_STARTED; - } - break; case MEDIA2_DRM_INFO: ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)", (long long)srcId, msg, ext1, ext2, obj); @@ -1063,8 +1061,9 @@ void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Play if (ext1 != MEDIA2_INFO_VIDEO_TRACK_LAGGING) { ALOGW("info/warning (%d, %d)", ext1, ext2); - if (ext1 == MEDIA2_INFO_DATA_SOURCE_START && srcId == mSrcId) { + if (ext1 == MEDIA2_INFO_DATA_SOURCE_START && srcId == mSrcId && mTransitionToNext) { mCurrentState = MEDIA_PLAYER2_STARTED; + mTransitionToNext = false; } } break; -- GitLab From 9a022b9ecff6b15d1644d4ef97aa8ddb50e16815 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 10 Apr 2019 15:45:19 -0700 Subject: [PATCH 1273/1530] Add null checking on NuMediaExtractor::getMetrics() Test: MediaExtractorTest Bug: 123895599 Change-Id: If8f11ae182bc6a753fec1410a11a463d3ed7aed9 --- media/libstagefright/NuMediaExtractor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index 83b87d9845..2fee637fd6 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -756,6 +756,9 @@ status_t NuMediaExtractor::getSampleMeta(sp *sampleMeta) { } status_t NuMediaExtractor::getMetrics(Parcel *reply) { + if (mImpl == NULL) { + return -EINVAL; + } status_t status = mImpl->getMetrics(reply); return status; } -- GitLab From 84114c3fc63f1b3a7535d1b9df171c40d7969be2 Mon Sep 17 00:00:00 2001 From: jiabin Date: Wed, 10 Apr 2019 16:38:07 -0700 Subject: [PATCH 1274/1530] Init haptic intensity for the first fast track. The haptic intensity for fast track should be initialized, or it will be HAPTIC_SCALE_MUTE by default, which may result in no haptics. Bug: 130312331 Test: play haptics stream with FastMixer enabled Change-Id: If5a1ea12c563c4af1422b2e32451b80e69d2ef58 --- services/audioflinger/Threads.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 0bb2213798..6e2497f8bc 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4191,6 +4191,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // audio to FastMixer fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE; + fastTrack->mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; fastTrack->mGeneration++; state->mFastTracksGen++; state->mTrackMask = 1; -- GitLab From 5e95ecbd8a029a0f5c63cac38f4f2a8c1e38c594 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Wed, 10 Apr 2019 17:30:33 -0700 Subject: [PATCH 1275/1530] Camera: Document LEGACY devices not being allowed on new devices Bug: 130315905 Test: Builds Change-Id: Ia1380344ba73216eaf078f2480ce278e99c03820 --- camera/ndk/include/camera/NdkCameraMetadataTags.h | 1 + 1 file changed, 1 insertion(+) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 320e7b3119..ef5f26cbf1 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -8311,6 +8311,7 @@ typedef enum acamera_metadata_enum_acamera_info_supported_hardware_level { * fire the flash for flash power metering during precapture, and then fire the flash * for the final capture, if a flash is available on the device and the AE mode is set to * enable the flash.

    + *

    Devices that initially shipped with Android version Q or newer will not include any LEGACY-level devices.

    * * @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES -- GitLab From 02adaebec0e489825cda799c2df9b5b685a99ae9 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 5 Apr 2019 14:51:49 -0700 Subject: [PATCH 1276/1530] Make NuMediaExtractor behave more like mediaextractor NuMediaExtractor does limited read-ahead, then fails the current read immediately if an error was encountered during the read-ahead. This prevents reading the first buffer when there's an error reading the second buffer, making it impossible to reproduce some behaviors that *can* be reproduced with stagefright or by talking to the service directly. This change make it behave more like the underlying mediaextractor. In addition, this fixes the behavior of advance(), which is supposed to return false if no more samples are left. Prior to this change, advance would return true, but then the next read would fail. Bug: 121204004 Test: manual, CTS Change-Id: I8f906945713717ee6be9dff30165635e03389153 (cherry picked from commit 70ad09df0f5959c1de51f34247e5ea2d63a95725) --- media/libstagefright/NuMediaExtractor.cpp | 42 +++++++++---------- .../media/stagefright/NuMediaExtractor.h | 1 - 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp index 83b87d9845..86486db62b 100644 --- a/media/libstagefright/NuMediaExtractor.cpp +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -430,18 +430,6 @@ status_t NuMediaExtractor::unselectTrack(size_t index) { return OK; } -void NuMediaExtractor::releaseOneSample(TrackInfo *info) { - if (info == NULL || info->mSamples.empty()) { - return; - } - - auto it = info->mSamples.begin(); - if (it->mBuffer != NULL) { - it->mBuffer->release(); - } - info->mSamples.erase(it); -} - void NuMediaExtractor::releaseTrackSamples(TrackInfo *info) { if (info == NULL) { return; @@ -472,7 +460,7 @@ ssize_t NuMediaExtractor::fetchAllTrackSamples( fetchTrackSamples(info, seekTimeUs, mode); status_t err = info->mFinalResult; - if (err != OK && err != ERROR_END_OF_STREAM) { + if (err != OK && err != ERROR_END_OF_STREAM && info->mSamples.empty()) { return err; } @@ -527,14 +515,6 @@ void NuMediaExtractor::fetchTrackSamples(TrackInfo *info, info->mFinalResult = err; if (err != OK && err != ERROR_END_OF_STREAM) { ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err); - size_t count = mediaBuffers.size(); - for (size_t id = 0; id < count; ++id) { - MediaBufferBase *mbuf = mediaBuffers[id]; - if (mbuf != NULL) { - mbuf->release(); - } - } - return; } size_t count = mediaBuffers.size(); @@ -584,8 +564,26 @@ status_t NuMediaExtractor::advance() { TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); - releaseOneSample(info); + if (info == NULL || info->mSamples.empty()) { + return ERROR_END_OF_STREAM; + } + auto it = info->mSamples.begin(); + if (it->mBuffer != NULL) { + it->mBuffer->release(); + } + info->mSamples.erase(it); + + if (info->mSamples.empty()) { + minIndex = fetchAllTrackSamples(); + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + info = &mSelectedTracks.editItemAt(minIndex); + if (info == NULL || info->mSamples.empty()) { + return ERROR_END_OF_STREAM; + } + } return OK; } diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h index f34f9b63c9..4307110796 100644 --- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h +++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h @@ -149,7 +149,6 @@ private: MediaSource::ReadOptions::SeekMode mode = MediaSource::ReadOptions::SEEK_CLOSEST_SYNC); - void releaseOneSample(TrackInfo *info); void releaseTrackSamples(TrackInfo *info); void releaseAllTrackSamples(); -- GitLab From 9bacfeb522a52afd559fa8ba3b8dcac4a4a2ceef Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Tue, 9 Apr 2019 19:45:06 +0530 Subject: [PATCH 1277/1530] libeffects: Fix for crash during playback on bluetooth headset Crash was because of uninitialized (invalid) number of channels. Initialized channels to default value and ensured that during set config channel and channel mask value are updated per the input config. Test: BT playback with Spotify, GPM (Sailfish) Bug: 129804987 Bug: 130013184 Change-Id: I71069b13a90e02bfc3b5cb9bf69c4c65c10ae36f --- .../lvm/wrapper/Bundle/EffectBundle.cpp | 16 ++++++++++++++++ .../libeffects/lvm/wrapper/Bundle/EffectBundle.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 3a97905d23..3fbbc096f5 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -650,6 +650,9 @@ int LvmBundle_init(EffectContext *pContext){ params.SpeakerType = LVM_HEADPHONES; pContext->pBundledContext->SampleRate = LVM_FS_44100; +#ifdef SUPPORT_MC + pContext->pBundledContext->ChMask = AUDIO_CHANNEL_OUT_STEREO; +#endif /* Concert Sound parameters */ params.VirtualizerOperatingMode = LVM_MODE_OFF; @@ -695,6 +698,11 @@ int LvmBundle_init(EffectContext *pContext){ params.TE_OperatingMode = LVM_TE_OFF; params.TE_EffectLevel = 0; +#ifdef SUPPORT_MC + params.NrChannels = + audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO); + params.ChMask = AUDIO_CHANNEL_OUT_STEREO; +#endif /* Activate the initial settings */ LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, ¶ms); @@ -1297,7 +1305,12 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ return -EINVAL; } +#ifdef SUPPORT_MC + if (pContext->pBundledContext->SampleRate != SampleRate || + pContext->pBundledContext->ChMask != pConfig->inputCfg.channels) { +#else if(pContext->pBundledContext->SampleRate != SampleRate){ +#endif LVM_ControlParams_t ActiveParams; LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; @@ -1323,6 +1336,9 @@ int Effect_setConfig(EffectContext *pContext, effect_config_t *pConfig){ LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "Effect_setConfig") ALOGV("\tEffect_setConfig Succesfully called LVM_SetControlParameters\n"); pContext->pBundledContext->SampleRate = SampleRate; +#ifdef SUPPORT_MC + pContext->pBundledContext->ChMask = pConfig->inputCfg.channels; +#endif LvmEffect_limitLevel(pContext); diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h index 6bf045d018..6af4554fc2 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h @@ -107,6 +107,9 @@ struct BundledEffectContext{ LVM_FLOAT *pInputBuffer; LVM_FLOAT *pOutputBuffer; #endif +#ifdef SUPPORT_MC + LVM_INT32 ChMask; +#endif }; /* SessionContext : One session */ -- GitLab From dda6ed1e22ed6adea3adb7c0a9a71d55ef2be4dd Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 11 Apr 2019 10:29:09 -0700 Subject: [PATCH 1278/1530] add readlink to mediacodec-x86.policy bug: 130216896 Change-Id: Id46881507b0ccfcdf9fbea681c68b000efc113bb --- services/mediacodec/seccomp_policy/mediacodec-x86.policy | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy index 845f84b0ba..d9c4045f82 100644 --- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy +++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy @@ -45,6 +45,7 @@ exit: 1 exit_group: 1 rt_sigreturn: 1 ugetrlimit: 1 +readlink: 1 readlinkat: 1 _llseek: 1 fstatfs64: 1 -- GitLab From 469c834047ca4718588224de2ae8cb0aa2935656 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 11 Apr 2019 16:46:09 -0700 Subject: [PATCH 1279/1530] CCodec: move Buffers out into a separate file Seperate Buffers out before we add complex logic to them. Also add missing documentation. Bug: 130223947 Test: builds Change-Id: I8f992cb36499401ee2c6d729aa52f3888a7bcea5 --- media/codec2/sfplugin/Android.bp | 1 + media/codec2/sfplugin/CCodecBufferChannel.cpp | 1392 +---------------- media/codec2/sfplugin/CCodecBufferChannel.h | 6 +- media/codec2/sfplugin/CCodecBuffers.cpp | 963 ++++++++++++ media/codec2/sfplugin/CCodecBuffers.h | 808 ++++++++++ 5 files changed, 1775 insertions(+), 1395 deletions(-) create mode 100644 media/codec2/sfplugin/CCodecBuffers.cpp create mode 100644 media/codec2/sfplugin/CCodecBuffers.h diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index 66457e7e3b..8ae80eeb9d 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -5,6 +5,7 @@ cc_library_shared { "C2OMXNode.cpp", "CCodec.cpp", "CCodecBufferChannel.cpp", + "CCodecBuffers.cpp", "CCodecConfig.cpp", "Codec2Buffer.cpp", "Codec2InfoBuilder.cpp", diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index d1fa920623..00e0c165d4 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -56,1398 +56,10 @@ using namespace hardware::cas::native::V1_0; using CasStatus = hardware::cas::V1_0::Status; -/** - * Base class for representation of buffers at one port. - */ -class CCodecBufferChannel::Buffers { -public: - Buffers(const char *componentName, const char *name = "Buffers") - : mComponentName(componentName), - mChannelName(std::string(componentName) + ":" + name), - mName(mChannelName.c_str()) { - } - virtual ~Buffers() = default; - - /** - * Set format for MediaCodec-facing buffers. - */ - void setFormat(const sp &format) { - CHECK(format != nullptr); - mFormat = format; - } - - /** - * Return a copy of current format. - */ - sp dupFormat() { - return mFormat != nullptr ? mFormat->dup() : nullptr; - } - - /** - * Returns true if the buffers are operating under array mode. - */ - virtual bool isArrayMode() const { return false; } - - /** - * Fills the vector with MediaCodecBuffer's if in array mode; otherwise, - * no-op. - */ - virtual void getArray(Vector> *) const {} - - /** - * Return number of buffers the client owns. - */ - virtual size_t numClientBuffers() const = 0; - - void handleImageData(const sp &buffer) { - sp imageDataCandidate = buffer->getImageData(); - if (imageDataCandidate == nullptr) { - return; - } - sp imageData; - if (!mFormat->findBuffer("image-data", &imageData) - || imageDataCandidate->size() != imageData->size() - || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) { - ALOGD("[%s] updating image-data", mName); - sp newFormat = dupFormat(); - newFormat->setBuffer("image-data", imageDataCandidate); - MediaImage2 *img = (MediaImage2*)imageDataCandidate->data(); - if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { - int32_t stride = img->mPlane[0].mRowInc; - newFormat->setInt32(KEY_STRIDE, stride); - ALOGD("[%s] updating stride = %d", mName, stride); - if (img->mNumPlanes > 1 && stride > 0) { - int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; - newFormat->setInt32(KEY_SLICE_HEIGHT, vstride); - ALOGD("[%s] updating vstride = %d", mName, vstride); - } - } - setFormat(newFormat); - buffer->setFormat(newFormat); - } - } - -protected: - std::string mComponentName; ///< name of component for debugging - std::string mChannelName; ///< name of channel for debugging - const char *mName; ///< C-string version of channel name - // Format to be used for creating MediaCodec-facing buffers. - sp mFormat; - -private: - DISALLOW_EVIL_CONSTRUCTORS(Buffers); -}; - -class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers { -public: - InputBuffers(const char *componentName, const char *name = "Input[]") - : Buffers(componentName, name) { } - virtual ~InputBuffers() = default; - - /** - * Set a block pool to obtain input memory blocks. - */ - void setPool(const std::shared_ptr &pool) { mPool = pool; } - - /** - * Get a new MediaCodecBuffer for input and its corresponding index. - * Returns false if no new buffer can be obtained at the moment. - */ - virtual bool requestNewBuffer(size_t *index, sp *buffer) = 0; - - /** - * Release the buffer obtained from requestNewBuffer() and get the - * associated C2Buffer object back. Returns true if the buffer was on file - * and released successfully. - */ - virtual bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) = 0; - - /** - * Release the buffer that is no longer used by the codec process. Return - * true if and only if the buffer was on file and released successfully. - */ - virtual bool expireComponentBuffer( - const std::shared_ptr &c2buffer) = 0; - - /** - * Flush internal state. After this call, no index or buffer previously - * returned from requestNewBuffer() is valid. - */ - virtual void flush() = 0; - - /** - * Return array-backed version of input buffers. The returned object - * shall retain the internal state so that it will honor index and - * buffer from previous calls of requestNewBuffer(). - */ - virtual std::unique_ptr toArrayMode(size_t size) = 0; - -protected: - // Pool to obtain blocks for input buffers. - std::shared_ptr mPool; - -private: - DISALLOW_EVIL_CONSTRUCTORS(InputBuffers); -}; - -class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers { -public: - OutputBuffers(const char *componentName, const char *name = "Output") - : Buffers(componentName, name) { } - virtual ~OutputBuffers() = default; - - /** - * Register output C2Buffer from the component and obtain corresponding - * index and MediaCodecBuffer object. Returns false if registration - * fails. - */ - virtual status_t registerBuffer( - const std::shared_ptr &buffer, - size_t *index, - sp *clientBuffer) = 0; - - /** - * Register codec specific data as a buffer to be consistent with - * MediaCodec behavior. - */ - virtual status_t registerCsd( - const C2StreamInitDataInfo::output * /* csd */, - size_t * /* index */, - sp * /* clientBuffer */) = 0; - - /** - * Release the buffer obtained from registerBuffer() and get the - * associated C2Buffer object back. Returns true if the buffer was on file - * and released successfully. - */ - virtual bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) = 0; - - /** - * Flush internal state. After this call, no index or buffer previously - * returned from registerBuffer() is valid. - */ - virtual void flush(const std::list> &flushedWork) = 0; - - /** - * Return array-backed version of output buffers. The returned object - * shall retain the internal state so that it will honor index and - * buffer from previous calls of registerBuffer(). - */ - virtual std::unique_ptr toArrayMode(size_t size) = 0; - - /** - * Initialize SkipCutBuffer object. - */ - void initSkipCutBuffer( - int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) { - CHECK(mSkipCutBuffer == nullptr); - mDelay = delay; - mPadding = padding; - mSampleRate = sampleRate; - setSkipCutBuffer(delay, padding, channelCount); - } - - /** - * Update the SkipCutBuffer object. No-op if it's never initialized. - */ - void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) { - if (mSkipCutBuffer == nullptr) { - return; - } - int32_t delay = mDelay; - int32_t padding = mPadding; - if (sampleRate != mSampleRate) { - delay = ((int64_t)delay * sampleRate) / mSampleRate; - padding = ((int64_t)padding * sampleRate) / mSampleRate; - } - setSkipCutBuffer(delay, padding, channelCount); - } - - /** - * Submit buffer to SkipCutBuffer object, if initialized. - */ - void submit(const sp &buffer) { - if (mSkipCutBuffer != nullptr) { - mSkipCutBuffer->submit(buffer); - } - } - - /** - * Transfer SkipCutBuffer object to the other Buffers object. - */ - void transferSkipCutBuffer(const sp &scb) { - mSkipCutBuffer = scb; - } - -protected: - sp mSkipCutBuffer; - -private: - int32_t mDelay; - int32_t mPadding; - int32_t mSampleRate; - - void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) { - if (mSkipCutBuffer != nullptr) { - size_t prevSize = mSkipCutBuffer->size(); - if (prevSize != 0u) { - ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize); - } - } - mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount); - } - - DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers); -}; - namespace { -const static size_t kSmoothnessFactor = 4; -const static size_t kRenderingDepth = 3; -const static size_t kLinearBufferSize = 1048576; -// This can fit 4K RGBA frame, and most likely client won't need more than this. -const static size_t kMaxLinearBufferSize = 3840 * 2160 * 4; - -/** - * Simple local buffer pool backed by std::vector. - */ -class LocalBufferPool : public std::enable_shared_from_this { -public: - /** - * Create a new LocalBufferPool object. - * - * \param poolCapacity max total size of buffers managed by this pool. - * - * \return a newly created pool object. - */ - static std::shared_ptr Create(size_t poolCapacity) { - return std::shared_ptr(new LocalBufferPool(poolCapacity)); - } - - /** - * Return an ABuffer object whose size is at least |capacity|. - * - * \param capacity requested capacity - * \return nullptr if the pool capacity is reached - * an ABuffer object otherwise. - */ - sp newBuffer(size_t capacity) { - Mutex::Autolock lock(mMutex); - auto it = std::find_if( - mPool.begin(), mPool.end(), - [capacity](const std::vector &vec) { - return vec.capacity() >= capacity; - }); - if (it != mPool.end()) { - sp buffer = new VectorBuffer(std::move(*it), shared_from_this()); - mPool.erase(it); - return buffer; - } - if (mUsedSize + capacity > mPoolCapacity) { - while (!mPool.empty()) { - mUsedSize -= mPool.back().capacity(); - mPool.pop_back(); - } - if (mUsedSize + capacity > mPoolCapacity) { - ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu", - mUsedSize, capacity, mPoolCapacity); - return nullptr; - } - } - std::vector vec(capacity); - mUsedSize += vec.capacity(); - return new VectorBuffer(std::move(vec), shared_from_this()); - } - -private: - /** - * ABuffer backed by std::vector. - */ - class VectorBuffer : public ::android::ABuffer { - public: - /** - * Construct a VectorBuffer by taking the ownership of supplied vector. - * - * \param vec backing vector of the buffer. this object takes - * ownership at construction. - * \param pool a LocalBufferPool object to return the vector at - * destruction. - */ - VectorBuffer(std::vector &&vec, const std::shared_ptr &pool) - : ABuffer(vec.data(), vec.capacity()), - mVec(std::move(vec)), - mPool(pool) { - } - - ~VectorBuffer() override { - std::shared_ptr pool = mPool.lock(); - if (pool) { - // If pool is alive, return the vector back to the pool so that - // it can be recycled. - pool->returnVector(std::move(mVec)); - } - } - - private: - std::vector mVec; - std::weak_ptr mPool; - }; - - Mutex mMutex; - size_t mPoolCapacity; - size_t mUsedSize; - std::list> mPool; - - /** - * Private constructor to prevent constructing non-managed LocalBufferPool. - */ - explicit LocalBufferPool(size_t poolCapacity) - : mPoolCapacity(poolCapacity), mUsedSize(0) { - } - - /** - * Take back the ownership of vec from the destructed VectorBuffer and put - * it in front of the pool. - */ - void returnVector(std::vector &&vec) { - Mutex::Autolock lock(mMutex); - mPool.push_front(std::move(vec)); - } - - DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool); -}; - -sp AllocateGraphicBuffer( - const std::shared_ptr &pool, - const sp &format, - uint32_t pixelFormat, - const C2MemoryUsage &usage, - const std::shared_ptr &localBufferPool) { - int32_t width, height; - if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) { - ALOGD("format lacks width or height"); - return nullptr; - } - - std::shared_ptr block; - c2_status_t err = pool->fetchGraphicBlock( - width, height, pixelFormat, usage, &block); - if (err != C2_OK) { - ALOGD("fetch graphic block failed: %d", err); - return nullptr; - } - - return GraphicBlockBuffer::Allocate( - format, - block, - [localBufferPool](size_t capacity) { - return localBufferPool->newBuffer(capacity); - }); -} - -class BuffersArrayImpl; - -/** - * Flexible buffer slots implementation. - */ -class FlexBuffersImpl { -public: - FlexBuffersImpl(const char *name) - : mImplName(std::string(name) + ".Impl"), - mName(mImplName.c_str()) { } - - /** - * Assign an empty slot for a buffer and return the index. If there's no - * empty slot, just add one at the end and return it. - * - * \param buffer[in] a new buffer to assign a slot. - * \return index of the assigned slot. - */ - size_t assignSlot(const sp &buffer) { - for (size_t i = 0; i < mBuffers.size(); ++i) { - if (mBuffers[i].clientBuffer == nullptr - && mBuffers[i].compBuffer.expired()) { - mBuffers[i].clientBuffer = buffer; - return i; - } - } - mBuffers.push_back({ buffer, std::weak_ptr() }); - return mBuffers.size() - 1; - } - - /** - * Release the slot from the client, and get the C2Buffer object back from - * the previously assigned buffer. Note that the slot is not completely free - * until the returned C2Buffer object is freed. - * - * \param buffer[in] the buffer previously assigned a slot. - * \param c2buffer[in,out] pointer to C2Buffer to be populated. Ignored - * if null. - * \return true if the buffer is successfully released from a slot - * false otherwise - */ - bool releaseSlot( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) { - sp clientBuffer; - size_t index = mBuffers.size(); - for (size_t i = 0; i < mBuffers.size(); ++i) { - if (mBuffers[i].clientBuffer == buffer) { - clientBuffer = mBuffers[i].clientBuffer; - if (release) { - mBuffers[i].clientBuffer.clear(); - } - index = i; - break; - } - } - if (clientBuffer == nullptr) { - ALOGV("[%s] %s: No matching buffer found", mName, __func__); - return false; - } - std::shared_ptr result = mBuffers[index].compBuffer.lock(); - if (!result) { - result = clientBuffer->asC2Buffer(); - mBuffers[index].compBuffer = result; - } - if (c2buffer) { - *c2buffer = result; - } - return true; - } - - bool expireComponentBuffer(const std::shared_ptr &c2buffer) { - for (size_t i = 0; i < mBuffers.size(); ++i) { - std::shared_ptr compBuffer = - mBuffers[i].compBuffer.lock(); - if (!compBuffer || compBuffer != c2buffer) { - continue; - } - mBuffers[i].compBuffer.reset(); - ALOGV("[%s] codec released buffer #%zu", mName, i); - return true; - } - ALOGV("[%s] codec released an unknown buffer", mName); - return false; - } - - void flush() { - ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size()); - mBuffers.clear(); - } - - size_t numClientBuffers() const { - return std::count_if( - mBuffers.begin(), mBuffers.end(), - [](const Entry &entry) { - return (entry.clientBuffer != nullptr); - }); - } - -private: - friend class BuffersArrayImpl; - - std::string mImplName; ///< name for debugging - const char *mName; ///< C-string version of name - - struct Entry { - sp clientBuffer; - std::weak_ptr compBuffer; - }; - std::vector mBuffers; -}; - -/** - * Static buffer slots implementation based on a fixed-size array. - */ -class BuffersArrayImpl { -public: - BuffersArrayImpl() - : mImplName("BuffersArrayImpl"), - mName(mImplName.c_str()) { } - - /** - * Initialize buffer array from the original |impl|. The buffers known by - * the client is preserved, and the empty slots are populated so that the - * array size is at least |minSize|. - * - * \param impl[in] FlexBuffersImpl object used so far. - * \param minSize[in] minimum size of the buffer array. - * \param allocate[in] function to allocate a client buffer for an empty slot. - */ - void initialize( - const FlexBuffersImpl &impl, - size_t minSize, - std::function()> allocate) { - mImplName = impl.mImplName + "[N]"; - mName = mImplName.c_str(); - for (size_t i = 0; i < impl.mBuffers.size(); ++i) { - sp clientBuffer = impl.mBuffers[i].clientBuffer; - bool ownedByClient = (clientBuffer != nullptr); - if (!ownedByClient) { - clientBuffer = allocate(); - } - mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient }); - } - ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize); - for (size_t i = impl.mBuffers.size(); i < minSize; ++i) { - mBuffers.push_back({ allocate(), std::weak_ptr(), false }); - } - } - - /** - * Grab a buffer from the underlying array which matches the criteria. - * - * \param index[out] index of the slot. - * \param buffer[out] the matching buffer. - * \param match[in] a function to test whether the buffer matches the - * criteria or not. - * \return OK if successful, - * WOULD_BLOCK if slots are being used, - * NO_MEMORY if no slot matches the criteria, even though it's - * available - */ - status_t grabBuffer( - size_t *index, - sp *buffer, - std::function &)> match = - [](const sp &) { return true; }) { - // allBuffersDontMatch remains true if all buffers are available but - // match() returns false for every buffer. - bool allBuffersDontMatch = true; - for (size_t i = 0; i < mBuffers.size(); ++i) { - if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) { - if (match(mBuffers[i].clientBuffer)) { - mBuffers[i].ownedByClient = true; - *buffer = mBuffers[i].clientBuffer; - (*buffer)->meta()->clear(); - (*buffer)->setRange(0, (*buffer)->capacity()); - *index = i; - return OK; - } - } else { - allBuffersDontMatch = false; - } - } - return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK; - } - - /** - * Return the buffer from the client, and get the C2Buffer object back from - * the buffer. Note that the slot is not completely free until the returned - * C2Buffer object is freed. - * - * \param buffer[in] the buffer previously grabbed. - * \param c2buffer[in,out] pointer to C2Buffer to be populated. Ignored - * if null. - * \return true if the buffer is successfully returned - * false otherwise - */ - bool returnBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) { - sp clientBuffer; - size_t index = mBuffers.size(); - for (size_t i = 0; i < mBuffers.size(); ++i) { - if (mBuffers[i].clientBuffer == buffer) { - if (!mBuffers[i].ownedByClient) { - ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", mName, i); - } - clientBuffer = mBuffers[i].clientBuffer; - if (release) { - mBuffers[i].ownedByClient = false; - } - index = i; - break; - } - } - if (clientBuffer == nullptr) { - ALOGV("[%s] %s: No matching buffer found", mName, __func__); - return false; - } - ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index); - std::shared_ptr result = mBuffers[index].compBuffer.lock(); - if (!result) { - result = clientBuffer->asC2Buffer(); - mBuffers[index].compBuffer = result; - } - if (c2buffer) { - *c2buffer = result; - } - return true; - } - - bool expireComponentBuffer(const std::shared_ptr &c2buffer) { - for (size_t i = 0; i < mBuffers.size(); ++i) { - std::shared_ptr compBuffer = - mBuffers[i].compBuffer.lock(); - if (!compBuffer) { - continue; - } - if (c2buffer == compBuffer) { - if (mBuffers[i].ownedByClient) { - // This should not happen. - ALOGD("[%s] codec released a buffer owned by client " - "(index %zu)", mName, i); - } - mBuffers[i].compBuffer.reset(); - ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i); - return true; - } - } - ALOGV("[%s] codec released an unknown buffer (array mode)", mName); - return false; - } - - /** - * Populate |array| with the underlying buffer array. - * - * \param array[out] an array to be filled with the underlying buffer array. - */ - void getArray(Vector> *array) const { - array->clear(); - for (const Entry &entry : mBuffers) { - array->push(entry.clientBuffer); - } - } - - /** - * The client abandoned all known buffers, so reclaim the ownership. - */ - void flush() { - for (Entry &entry : mBuffers) { - entry.ownedByClient = false; - } - } - - void realloc(std::function()> alloc) { - size_t size = mBuffers.size(); - mBuffers.clear(); - for (size_t i = 0; i < size; ++i) { - mBuffers.push_back({ alloc(), std::weak_ptr(), false }); - } - } - - size_t numClientBuffers() const { - return std::count_if( - mBuffers.begin(), mBuffers.end(), - [](const Entry &entry) { - return entry.ownedByClient; - }); - } - -private: - std::string mImplName; ///< name for debugging - const char *mName; ///< C-string version of name - - struct Entry { - const sp clientBuffer; - std::weak_ptr compBuffer; - bool ownedByClient; - }; - std::vector mBuffers; -}; - -class InputBuffersArray : public CCodecBufferChannel::InputBuffers { -public: - InputBuffersArray(const char *componentName, const char *name = "Input[N]") - : InputBuffers(componentName, name) { } - ~InputBuffersArray() override = default; - - void initialize( - const FlexBuffersImpl &impl, - size_t minSize, - std::function()> allocate) { - mImpl.initialize(impl, minSize, allocate); - } - - bool isArrayMode() const final { return true; } - - std::unique_ptr toArrayMode( - size_t) final { - return nullptr; - } - - void getArray(Vector> *array) const final { - mImpl.getArray(array); - } - - bool requestNewBuffer(size_t *index, sp *buffer) override { - sp c2Buffer; - status_t err = mImpl.grabBuffer(index, &c2Buffer); - if (err == OK) { - c2Buffer->setFormat(mFormat); - handleImageData(c2Buffer); - *buffer = c2Buffer; - return true; - } - return false; - } - - bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) override { - return mImpl.returnBuffer(buffer, c2buffer, release); - } - - bool expireComponentBuffer( - const std::shared_ptr &c2buffer) override { - return mImpl.expireComponentBuffer(c2buffer); - } - - void flush() override { - mImpl.flush(); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - -private: - BuffersArrayImpl mImpl; -}; - -class LinearInputBuffers : public CCodecBufferChannel::InputBuffers { -public: - LinearInputBuffers(const char *componentName, const char *name = "1D-Input") - : InputBuffers(componentName, name), - mImpl(mName) { } - - bool requestNewBuffer(size_t *index, sp *buffer) override { - int32_t capacity = kLinearBufferSize; - (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); - if ((size_t)capacity > kMaxLinearBufferSize) { - ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); - capacity = kMaxLinearBufferSize; - } - // TODO: proper max input size - // TODO: read usage from intf - sp newBuffer = alloc((size_t)capacity); - if (newBuffer == nullptr) { - return false; - } - *index = mImpl.assignSlot(newBuffer); - *buffer = newBuffer; - return true; - } - - bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) override { - return mImpl.releaseSlot(buffer, c2buffer, release); - } - - bool expireComponentBuffer( - const std::shared_ptr &c2buffer) override { - return mImpl.expireComponentBuffer(c2buffer); - } - - void flush() override { - // This is no-op by default unless we're in array mode where we need to keep - // track of the flushed work. - mImpl.flush(); - } - - std::unique_ptr toArrayMode( - size_t size) final { - int32_t capacity = kLinearBufferSize; - (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); - if ((size_t)capacity > kMaxLinearBufferSize) { - ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); - capacity = kMaxLinearBufferSize; - } - // TODO: proper max input size - // TODO: read usage from intf - std::unique_ptr array( - new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]")); - array->setPool(mPool); - array->setFormat(mFormat); - array->initialize( - mImpl, - size, - [this, capacity] () -> sp { return alloc(capacity); }); - return std::move(array); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - - virtual sp alloc(size_t size) { - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - std::shared_ptr block; - - c2_status_t err = mPool->fetchLinearBlock(size, usage, &block); - if (err != C2_OK) { - return nullptr; - } - - return LinearBlockBuffer::Allocate(mFormat, block); - } - -private: - FlexBuffersImpl mImpl; -}; - -class EncryptedLinearInputBuffers : public LinearInputBuffers { -public: - EncryptedLinearInputBuffers( - bool secure, - const sp &dealer, - const sp &crypto, - int32_t heapSeqNum, - size_t capacity, - size_t numInputSlots, - const char *componentName, const char *name = "EncryptedInput") - : LinearInputBuffers(componentName, name), - mUsage({0, 0}), - mDealer(dealer), - mCrypto(crypto), - mHeapSeqNum(heapSeqNum) { - if (secure) { - mUsage = { C2MemoryUsage::READ_PROTECTED, 0 }; - } else { - mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - } - for (size_t i = 0; i < numInputSlots; ++i) { - sp memory = mDealer->allocate(capacity); - if (memory == nullptr) { - ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", mName, i); - break; - } - mMemoryVector.push_back({std::weak_ptr(), memory}); - } - } - - ~EncryptedLinearInputBuffers() override { - } - - sp alloc(size_t size) override { - sp memory; - size_t slot = 0; - for (; slot < mMemoryVector.size(); ++slot) { - if (mMemoryVector[slot].block.expired()) { - memory = mMemoryVector[slot].memory; - break; - } - } - if (memory == nullptr) { - return nullptr; - } - - std::shared_ptr block; - c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block); - if (err != C2_OK || block == nullptr) { - return nullptr; - } - - mMemoryVector[slot].block = block; - return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum); - } - -private: - C2MemoryUsage mUsage; - sp mDealer; - sp mCrypto; - int32_t mHeapSeqNum; - struct Entry { - std::weak_ptr block; - sp memory; - }; - std::vector mMemoryVector; -}; - -class GraphicMetadataInputBuffers : public CCodecBufferChannel::InputBuffers { -public: - GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput") - : InputBuffers(componentName, name), - mImpl(mName), - mStore(GetCodec2PlatformAllocatorStore()) { } - ~GraphicMetadataInputBuffers() override = default; - - bool requestNewBuffer(size_t *index, sp *buffer) override { - std::shared_ptr alloc; - c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); - if (err != C2_OK) { - return false; - } - sp newBuffer = new GraphicMetadataBuffer(mFormat, alloc); - if (newBuffer == nullptr) { - return false; - } - *index = mImpl.assignSlot(newBuffer); - *buffer = newBuffer; - return true; - } - - bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) override { - return mImpl.releaseSlot(buffer, c2buffer, release); - } - - bool expireComponentBuffer( - const std::shared_ptr &c2buffer) override { - return mImpl.expireComponentBuffer(c2buffer); - } - - void flush() override { - // This is no-op by default unless we're in array mode where we need to keep - // track of the flushed work. - } - - std::unique_ptr toArrayMode( - size_t size) final { - std::shared_ptr alloc; - c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); - if (err != C2_OK) { - return nullptr; - } - std::unique_ptr array( - new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]")); - array->setPool(mPool); - array->setFormat(mFormat); - array->initialize( - mImpl, - size, - [format = mFormat, alloc]() -> sp { - return new GraphicMetadataBuffer(format, alloc); - }); - return std::move(array); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - -private: - FlexBuffersImpl mImpl; - std::shared_ptr mStore; -}; - -class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers { -public: - GraphicInputBuffers( - size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input") - : InputBuffers(componentName, name), - mImpl(mName), - mLocalBufferPool(LocalBufferPool::Create( - kMaxLinearBufferSize * numInputSlots)) { } - ~GraphicInputBuffers() override = default; - - bool requestNewBuffer(size_t *index, sp *buffer) override { - // TODO: proper max input size - // TODO: read usage from intf - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - sp newBuffer = AllocateGraphicBuffer( - mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool); - if (newBuffer == nullptr) { - return false; - } - *index = mImpl.assignSlot(newBuffer); - handleImageData(newBuffer); - *buffer = newBuffer; - return true; - } - - bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer, - bool release) override { - return mImpl.releaseSlot(buffer, c2buffer, release); - } - - bool expireComponentBuffer( - const std::shared_ptr &c2buffer) override { - return mImpl.expireComponentBuffer(c2buffer); - } - - void flush() override { - // This is no-op by default unless we're in array mode where we need to keep - // track of the flushed work. - } - - std::unique_ptr toArrayMode( - size_t size) final { - std::unique_ptr array( - new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]")); - array->setPool(mPool); - array->setFormat(mFormat); - array->initialize( - mImpl, - size, - [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp { - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - return AllocateGraphicBuffer( - pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp); - }); - return std::move(array); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - -private: - FlexBuffersImpl mImpl; - std::shared_ptr mLocalBufferPool; -}; - -class DummyInputBuffers : public CCodecBufferChannel::InputBuffers { -public: - DummyInputBuffers(const char *componentName, const char *name = "2D-Input") - : InputBuffers(componentName, name) { } - - bool requestNewBuffer(size_t *, sp *) override { - return false; - } - - bool releaseBuffer( - const sp &, std::shared_ptr *, bool) override { - return false; - } - - bool expireComponentBuffer(const std::shared_ptr &) override { - return false; - } - void flush() override { - } - - std::unique_ptr toArrayMode( - size_t) final { - return nullptr; - } - - bool isArrayMode() const final { return true; } - - void getArray(Vector> *array) const final { - array->clear(); - } - - size_t numClientBuffers() const final { - return 0u; - } -}; - -class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers { -public: - OutputBuffersArray(const char *componentName, const char *name = "Output[N]") - : OutputBuffers(componentName, name) { } - ~OutputBuffersArray() override = default; - - void initialize( - const FlexBuffersImpl &impl, - size_t minSize, - std::function()> allocate) { - mImpl.initialize(impl, minSize, allocate); - } - - bool isArrayMode() const final { return true; } - - std::unique_ptr toArrayMode( - size_t) final { - return nullptr; - } - - status_t registerBuffer( - const std::shared_ptr &buffer, - size_t *index, - sp *clientBuffer) final { - sp c2Buffer; - status_t err = mImpl.grabBuffer( - index, - &c2Buffer, - [buffer](const sp &clientBuffer) { - return clientBuffer->canCopy(buffer); - }); - if (err == WOULD_BLOCK) { - ALOGV("[%s] buffers temporarily not available", mName); - return err; - } else if (err != OK) { - ALOGD("[%s] grabBuffer failed: %d", mName, err); - return err; - } - c2Buffer->setFormat(mFormat); - if (!c2Buffer->copy(buffer)) { - ALOGD("[%s] copy buffer failed", mName); - return WOULD_BLOCK; - } - submit(c2Buffer); - handleImageData(c2Buffer); - *clientBuffer = c2Buffer; - ALOGV("[%s] grabbed buffer %zu", mName, *index); - return OK; - } - - status_t registerCsd( - const C2StreamInitDataInfo::output *csd, - size_t *index, - sp *clientBuffer) final { - sp c2Buffer; - status_t err = mImpl.grabBuffer( - index, - &c2Buffer, - [csd](const sp &clientBuffer) { - return clientBuffer->base() != nullptr - && clientBuffer->capacity() >= csd->flexCount(); - }); - if (err != OK) { - return err; - } - memcpy(c2Buffer->base(), csd->m.value, csd->flexCount()); - c2Buffer->setRange(0, csd->flexCount()); - c2Buffer->setFormat(mFormat); - *clientBuffer = c2Buffer; - return OK; - } - - bool releaseBuffer( - const sp &buffer, std::shared_ptr *c2buffer) override { - return mImpl.returnBuffer(buffer, c2buffer, true); - } - - void flush(const std::list> &flushedWork) override { - (void)flushedWork; - mImpl.flush(); - if (mSkipCutBuffer != nullptr) { - mSkipCutBuffer->clear(); - } - } - - void getArray(Vector> *array) const final { - mImpl.getArray(array); - } - - void realloc(const std::shared_ptr &c2buffer) { - std::function()> alloc; - switch (c2buffer->data().type()) { - case C2BufferData::LINEAR: { - uint32_t size = kLinearBufferSize; - const C2ConstLinearBlock &block = c2buffer->data().linearBlocks().front(); - if (block.size() < kMaxLinearBufferSize / 2) { - size = block.size() * 2; - } else { - size = kMaxLinearBufferSize; - } - alloc = [format = mFormat, size] { - return new LocalLinearBuffer(format, new ABuffer(size)); - }; - break; - } - - // TODO: add support - case C2BufferData::GRAPHIC: FALLTHROUGH_INTENDED; - - case C2BufferData::INVALID: FALLTHROUGH_INTENDED; - case C2BufferData::LINEAR_CHUNKS: FALLTHROUGH_INTENDED; - case C2BufferData::GRAPHIC_CHUNKS: FALLTHROUGH_INTENDED; - default: - ALOGD("Unsupported type: %d", (int)c2buffer->data().type()); - return; - } - mImpl.realloc(alloc); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - -private: - BuffersArrayImpl mImpl; -}; - -class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers { -public: - FlexOutputBuffers(const char *componentName, const char *name = "Output[]") - : OutputBuffers(componentName, name), - mImpl(mName) { } - - status_t registerBuffer( - const std::shared_ptr &buffer, - size_t *index, - sp *clientBuffer) override { - sp newBuffer = wrap(buffer); - if (newBuffer == nullptr) { - return NO_MEMORY; - } - newBuffer->setFormat(mFormat); - *index = mImpl.assignSlot(newBuffer); - handleImageData(newBuffer); - *clientBuffer = newBuffer; - ALOGV("[%s] registered buffer %zu", mName, *index); - return OK; - } - - status_t registerCsd( - const C2StreamInitDataInfo::output *csd, - size_t *index, - sp *clientBuffer) final { - sp newBuffer = new LocalLinearBuffer( - mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount())); - *index = mImpl.assignSlot(newBuffer); - *clientBuffer = newBuffer; - return OK; - } - - bool releaseBuffer( - const sp &buffer, - std::shared_ptr *c2buffer) override { - return mImpl.releaseSlot(buffer, c2buffer, true); - } - - void flush( - const std::list> &flushedWork) override { - (void) flushedWork; - // This is no-op by default unless we're in array mode where we need to keep - // track of the flushed work. - } - - std::unique_ptr toArrayMode( - size_t size) override { - std::unique_ptr array(new OutputBuffersArray(mComponentName.c_str())); - array->setFormat(mFormat); - array->transferSkipCutBuffer(mSkipCutBuffer); - array->initialize( - mImpl, - size, - [this]() { return allocateArrayBuffer(); }); - return std::move(array); - } - - size_t numClientBuffers() const final { - return mImpl.numClientBuffers(); - } - - /** - * Return an appropriate Codec2Buffer object for the type of buffers. - * - * \param buffer C2Buffer object to wrap. - * - * \return appropriate Codec2Buffer object to wrap |buffer|. - */ - virtual sp wrap(const std::shared_ptr &buffer) = 0; - - /** - * Return an appropriate Codec2Buffer object for the type of buffers, to be - * used as an empty array buffer. - * - * \return appropriate Codec2Buffer object which can copy() from C2Buffers. - */ - virtual sp allocateArrayBuffer() = 0; - -private: - FlexBuffersImpl mImpl; -}; - -class LinearOutputBuffers : public FlexOutputBuffers { -public: - LinearOutputBuffers(const char *componentName, const char *name = "1D-Output") - : FlexOutputBuffers(componentName, name) { } - - void flush( - const std::list> &flushedWork) override { - if (mSkipCutBuffer != nullptr) { - mSkipCutBuffer->clear(); - } - FlexOutputBuffers::flush(flushedWork); - } - - sp wrap(const std::shared_ptr &buffer) override { - if (buffer == nullptr) { - ALOGV("[%s] using a dummy buffer", mName); - return new LocalLinearBuffer(mFormat, new ABuffer(0)); - } - if (buffer->data().type() != C2BufferData::LINEAR) { - ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type()); - // We expect linear output buffers from the component. - return nullptr; - } - if (buffer->data().linearBlocks().size() != 1u) { - ALOGV("[%s] no linear buffers", mName); - // We expect one and only one linear block from the component. - return nullptr; - } - sp clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer); - if (clientBuffer == nullptr) { - ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName); - return nullptr; - } - submit(clientBuffer); - return clientBuffer; - } - - sp allocateArrayBuffer() override { - // TODO: proper max output size - return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize)); - } -}; - -class GraphicOutputBuffers : public FlexOutputBuffers { -public: - GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output") - : FlexOutputBuffers(componentName, name) { } - - sp wrap(const std::shared_ptr &buffer) override { - return new DummyContainerBuffer(mFormat, buffer); - } - - sp allocateArrayBuffer() override { - return new DummyContainerBuffer(mFormat); - } -}; - -class RawGraphicOutputBuffers : public FlexOutputBuffers { -public: - RawGraphicOutputBuffers( - size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output") - : FlexOutputBuffers(componentName, name), - mLocalBufferPool(LocalBufferPool::Create( - kMaxLinearBufferSize * numOutputSlots)) { } - ~RawGraphicOutputBuffers() override = default; - - sp wrap(const std::shared_ptr &buffer) override { - 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; - } else { - return ConstGraphicBlockBuffer::Allocate( - mFormat, - buffer, - [lbp = mLocalBufferPool](size_t capacity) { - return lbp->newBuffer(capacity); - }); - } - } - - sp allocateArrayBuffer() override { - return ConstGraphicBlockBuffer::AllocateEmpty( - mFormat, - [lbp = mLocalBufferPool](size_t capacity) { - return lbp->newBuffer(capacity); - }); - } - -private: - std::shared_ptr mLocalBufferPool; -}; +constexpr size_t kSmoothnessFactor = 4; +constexpr size_t kRenderingDepth = 3; } // namespace diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 1ea29b426f..b7a8445f2b 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -33,6 +33,7 @@ #include #include +#include "CCodecBuffers.h" #include "InputSurfaceWrapper.h" #include "PipelineWatcher.h" @@ -151,11 +152,6 @@ public: void setMetaMode(MetaMode mode); - // Internal classes - class Buffers; - class InputBuffers; - class OutputBuffers; - private: class QueueGuard; diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp new file mode 100644 index 0000000000..fb0efce45d --- /dev/null +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -0,0 +1,963 @@ +/* + * Copyright 2019, 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 "CCodecBuffers" +#include + +#include + +#include +#include + +#include "CCodecBuffers.h" + +namespace android { + +namespace { + +sp AllocateGraphicBuffer( + const std::shared_ptr &pool, + const sp &format, + uint32_t pixelFormat, + const C2MemoryUsage &usage, + const std::shared_ptr &localBufferPool) { + int32_t width, height; + if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) { + ALOGD("format lacks width or height"); + return nullptr; + } + + std::shared_ptr block; + c2_status_t err = pool->fetchGraphicBlock( + width, height, pixelFormat, usage, &block); + if (err != C2_OK) { + ALOGD("fetch graphic block failed: %d", err); + return nullptr; + } + + return GraphicBlockBuffer::Allocate( + format, + block, + [localBufferPool](size_t capacity) { + return localBufferPool->newBuffer(capacity); + }); +} + +} // namespace + +// CCodecBuffers + +void CCodecBuffers::setFormat(const sp &format) { + CHECK(format != nullptr); + mFormat = format; +} + +sp CCodecBuffers::dupFormat() { + return mFormat != nullptr ? mFormat->dup() : nullptr; +} + +void CCodecBuffers::handleImageData(const sp &buffer) { + sp imageDataCandidate = buffer->getImageData(); + if (imageDataCandidate == nullptr) { + return; + } + sp imageData; + if (!mFormat->findBuffer("image-data", &imageData) + || imageDataCandidate->size() != imageData->size() + || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) { + ALOGD("[%s] updating image-data", mName); + sp newFormat = dupFormat(); + newFormat->setBuffer("image-data", imageDataCandidate); + MediaImage2 *img = (MediaImage2*)imageDataCandidate->data(); + if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) { + int32_t stride = img->mPlane[0].mRowInc; + newFormat->setInt32(KEY_STRIDE, stride); + ALOGD("[%s] updating stride = %d", mName, stride); + if (img->mNumPlanes > 1 && stride > 0) { + int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride; + newFormat->setInt32(KEY_SLICE_HEIGHT, vstride); + ALOGD("[%s] updating vstride = %d", mName, vstride); + } + } + setFormat(newFormat); + buffer->setFormat(newFormat); + } +} + +// OutputBuffers + +void OutputBuffers::initSkipCutBuffer( + int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) { + CHECK(mSkipCutBuffer == nullptr); + mDelay = delay; + mPadding = padding; + mSampleRate = sampleRate; + setSkipCutBuffer(delay, padding, channelCount); +} + +void OutputBuffers::updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) { + if (mSkipCutBuffer == nullptr) { + return; + } + int32_t delay = mDelay; + int32_t padding = mPadding; + if (sampleRate != mSampleRate) { + delay = ((int64_t)delay * sampleRate) / mSampleRate; + padding = ((int64_t)padding * sampleRate) / mSampleRate; + } + setSkipCutBuffer(delay, padding, channelCount); +} + +void OutputBuffers::submit(const sp &buffer) { + if (mSkipCutBuffer != nullptr) { + mSkipCutBuffer->submit(buffer); + } +} + +void OutputBuffers::transferSkipCutBuffer(const sp &scb) { + mSkipCutBuffer = scb; +} + +void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) { + if (mSkipCutBuffer != nullptr) { + size_t prevSize = mSkipCutBuffer->size(); + if (prevSize != 0u) { + ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize); + } + } + mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount); +} + +// LocalBufferPool + +std::shared_ptr LocalBufferPool::Create(size_t poolCapacity) { + return std::shared_ptr(new LocalBufferPool(poolCapacity)); +} + +sp LocalBufferPool::newBuffer(size_t capacity) { + Mutex::Autolock lock(mMutex); + auto it = std::find_if( + mPool.begin(), mPool.end(), + [capacity](const std::vector &vec) { + return vec.capacity() >= capacity; + }); + if (it != mPool.end()) { + sp buffer = new VectorBuffer(std::move(*it), shared_from_this()); + mPool.erase(it); + return buffer; + } + if (mUsedSize + capacity > mPoolCapacity) { + while (!mPool.empty()) { + mUsedSize -= mPool.back().capacity(); + mPool.pop_back(); + } + if (mUsedSize + capacity > mPoolCapacity) { + ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu", + mUsedSize, capacity, mPoolCapacity); + return nullptr; + } + } + std::vector vec(capacity); + mUsedSize += vec.capacity(); + return new VectorBuffer(std::move(vec), shared_from_this()); +} + +LocalBufferPool::VectorBuffer::VectorBuffer( + std::vector &&vec, const std::shared_ptr &pool) + : ABuffer(vec.data(), vec.capacity()), + mVec(std::move(vec)), + mPool(pool) { +} + +LocalBufferPool::VectorBuffer::~VectorBuffer() { + std::shared_ptr pool = mPool.lock(); + if (pool) { + // If pool is alive, return the vector back to the pool so that + // it can be recycled. + pool->returnVector(std::move(mVec)); + } +} + +void LocalBufferPool::returnVector(std::vector &&vec) { + Mutex::Autolock lock(mMutex); + mPool.push_front(std::move(vec)); +} + +size_t FlexBuffersImpl::assignSlot(const sp &buffer) { + for (size_t i = 0; i < mBuffers.size(); ++i) { + if (mBuffers[i].clientBuffer == nullptr + && mBuffers[i].compBuffer.expired()) { + mBuffers[i].clientBuffer = buffer; + return i; + } + } + mBuffers.push_back({ buffer, std::weak_ptr() }); + return mBuffers.size() - 1; +} + +// FlexBuffersImpl + +bool FlexBuffersImpl::releaseSlot( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + sp clientBuffer; + size_t index = mBuffers.size(); + for (size_t i = 0; i < mBuffers.size(); ++i) { + if (mBuffers[i].clientBuffer == buffer) { + clientBuffer = mBuffers[i].clientBuffer; + if (release) { + mBuffers[i].clientBuffer.clear(); + } + index = i; + break; + } + } + if (clientBuffer == nullptr) { + ALOGV("[%s] %s: No matching buffer found", mName, __func__); + return false; + } + std::shared_ptr result = mBuffers[index].compBuffer.lock(); + if (!result) { + result = clientBuffer->asC2Buffer(); + mBuffers[index].compBuffer = result; + } + if (c2buffer) { + *c2buffer = result; + } + return true; +} + +bool FlexBuffersImpl::expireComponentBuffer(const std::shared_ptr &c2buffer) { + for (size_t i = 0; i < mBuffers.size(); ++i) { + std::shared_ptr compBuffer = + mBuffers[i].compBuffer.lock(); + if (!compBuffer || compBuffer != c2buffer) { + continue; + } + mBuffers[i].compBuffer.reset(); + ALOGV("[%s] codec released buffer #%zu", mName, i); + return true; + } + ALOGV("[%s] codec released an unknown buffer", mName); + return false; +} + +void FlexBuffersImpl::flush() { + ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size()); + mBuffers.clear(); +} + +size_t FlexBuffersImpl::numClientBuffers() const { + return std::count_if( + mBuffers.begin(), mBuffers.end(), + [](const Entry &entry) { + return (entry.clientBuffer != nullptr); + }); +} + +// BuffersArrayImpl + +void BuffersArrayImpl::initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate) { + mImplName = impl.mImplName + "[N]"; + mName = mImplName.c_str(); + for (size_t i = 0; i < impl.mBuffers.size(); ++i) { + sp clientBuffer = impl.mBuffers[i].clientBuffer; + bool ownedByClient = (clientBuffer != nullptr); + if (!ownedByClient) { + clientBuffer = allocate(); + } + mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient }); + } + ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize); + for (size_t i = impl.mBuffers.size(); i < minSize; ++i) { + mBuffers.push_back({ allocate(), std::weak_ptr(), false }); + } +} + +status_t BuffersArrayImpl::grabBuffer( + size_t *index, + sp *buffer, + std::function &)> match) { + // allBuffersDontMatch remains true if all buffers are available but + // match() returns false for every buffer. + bool allBuffersDontMatch = true; + for (size_t i = 0; i < mBuffers.size(); ++i) { + if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) { + if (match(mBuffers[i].clientBuffer)) { + mBuffers[i].ownedByClient = true; + *buffer = mBuffers[i].clientBuffer; + (*buffer)->meta()->clear(); + (*buffer)->setRange(0, (*buffer)->capacity()); + *index = i; + return OK; + } + } else { + allBuffersDontMatch = false; + } + } + return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK; +} + +bool BuffersArrayImpl::returnBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + sp clientBuffer; + size_t index = mBuffers.size(); + for (size_t i = 0; i < mBuffers.size(); ++i) { + if (mBuffers[i].clientBuffer == buffer) { + if (!mBuffers[i].ownedByClient) { + ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", + mName, i); + } + clientBuffer = mBuffers[i].clientBuffer; + if (release) { + mBuffers[i].ownedByClient = false; + } + index = i; + break; + } + } + if (clientBuffer == nullptr) { + ALOGV("[%s] %s: No matching buffer found", mName, __func__); + return false; + } + ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index); + std::shared_ptr result = mBuffers[index].compBuffer.lock(); + if (!result) { + result = clientBuffer->asC2Buffer(); + mBuffers[index].compBuffer = result; + } + if (c2buffer) { + *c2buffer = result; + } + return true; +} + +bool BuffersArrayImpl::expireComponentBuffer(const std::shared_ptr &c2buffer) { + for (size_t i = 0; i < mBuffers.size(); ++i) { + std::shared_ptr compBuffer = + mBuffers[i].compBuffer.lock(); + if (!compBuffer) { + continue; + } + if (c2buffer == compBuffer) { + if (mBuffers[i].ownedByClient) { + // This should not happen. + ALOGD("[%s] codec released a buffer owned by client " + "(index %zu)", mName, i); + } + mBuffers[i].compBuffer.reset(); + ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i); + return true; + } + } + ALOGV("[%s] codec released an unknown buffer (array mode)", mName); + return false; +} + +void BuffersArrayImpl::getArray(Vector> *array) const { + array->clear(); + for (const Entry &entry : mBuffers) { + array->push(entry.clientBuffer); + } +} + +void BuffersArrayImpl::flush() { + for (Entry &entry : mBuffers) { + entry.ownedByClient = false; + } +} + +void BuffersArrayImpl::realloc(std::function()> alloc) { + size_t size = mBuffers.size(); + mBuffers.clear(); + for (size_t i = 0; i < size; ++i) { + mBuffers.push_back({ alloc(), std::weak_ptr(), false }); + } +} + +size_t BuffersArrayImpl::numClientBuffers() const { + return std::count_if( + mBuffers.begin(), mBuffers.end(), + [](const Entry &entry) { + return entry.ownedByClient; + }); +} + +// InputBuffersArray + +void InputBuffersArray::initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate) { + mImpl.initialize(impl, minSize, allocate); +} + +void InputBuffersArray::getArray(Vector> *array) const { + mImpl.getArray(array); +} + +bool InputBuffersArray::requestNewBuffer(size_t *index, sp *buffer) { + sp c2Buffer; + status_t err = mImpl.grabBuffer(index, &c2Buffer); + if (err == OK) { + c2Buffer->setFormat(mFormat); + handleImageData(c2Buffer); + *buffer = c2Buffer; + return true; + } + return false; +} + +bool InputBuffersArray::releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + return mImpl.returnBuffer(buffer, c2buffer, release); +} + +bool InputBuffersArray::expireComponentBuffer( + const std::shared_ptr &c2buffer) { + return mImpl.expireComponentBuffer(c2buffer); +} + +void InputBuffersArray::flush() { + mImpl.flush(); +} + +size_t InputBuffersArray::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +// LinearInputBuffers + +bool LinearInputBuffers::requestNewBuffer(size_t *index, sp *buffer) { + int32_t capacity = kLinearBufferSize; + (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); + if ((size_t)capacity > kMaxLinearBufferSize) { + ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); + capacity = kMaxLinearBufferSize; + } + // TODO: proper max input size + // TODO: read usage from intf + sp newBuffer = alloc((size_t)capacity); + if (newBuffer == nullptr) { + return false; + } + *index = mImpl.assignSlot(newBuffer); + *buffer = newBuffer; + return true; +} + +bool LinearInputBuffers::releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + return mImpl.releaseSlot(buffer, c2buffer, release); +} + +bool LinearInputBuffers::expireComponentBuffer( + const std::shared_ptr &c2buffer) { + return mImpl.expireComponentBuffer(c2buffer); +} + +void LinearInputBuffers::flush() { + // This is no-op by default unless we're in array mode where we need to keep + // track of the flushed work. + mImpl.flush(); +} + +std::unique_ptr LinearInputBuffers::toArrayMode( + size_t size) { + int32_t capacity = kLinearBufferSize; + (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); + if ((size_t)capacity > kMaxLinearBufferSize) { + ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); + capacity = kMaxLinearBufferSize; + } + // TODO: proper max input size + // TODO: read usage from intf + std::unique_ptr array( + new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]")); + array->setPool(mPool); + array->setFormat(mFormat); + array->initialize( + mImpl, + size, + [this, capacity] () -> sp { return alloc(capacity); }); + return std::move(array); +} + +size_t LinearInputBuffers::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +sp LinearInputBuffers::alloc(size_t size) { + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + std::shared_ptr block; + + c2_status_t err = mPool->fetchLinearBlock(size, usage, &block); + if (err != C2_OK) { + return nullptr; + } + + return LinearBlockBuffer::Allocate(mFormat, block); +} + +// EncryptedLinearInputBuffers + +EncryptedLinearInputBuffers::EncryptedLinearInputBuffers( + bool secure, + const sp &dealer, + const sp &crypto, + int32_t heapSeqNum, + size_t capacity, + size_t numInputSlots, + const char *componentName, const char *name) + : LinearInputBuffers(componentName, name), + mUsage({0, 0}), + mDealer(dealer), + mCrypto(crypto), + mHeapSeqNum(heapSeqNum) { + if (secure) { + mUsage = { C2MemoryUsage::READ_PROTECTED, 0 }; + } else { + mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + } + for (size_t i = 0; i < numInputSlots; ++i) { + sp memory = mDealer->allocate(capacity); + if (memory == nullptr) { + ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", + mName, i); + break; + } + mMemoryVector.push_back({std::weak_ptr(), memory}); + } +} + +sp EncryptedLinearInputBuffers::alloc(size_t size) { + sp memory; + size_t slot = 0; + for (; slot < mMemoryVector.size(); ++slot) { + if (mMemoryVector[slot].block.expired()) { + memory = mMemoryVector[slot].memory; + break; + } + } + if (memory == nullptr) { + return nullptr; + } + + std::shared_ptr block; + c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block); + if (err != C2_OK || block == nullptr) { + return nullptr; + } + + mMemoryVector[slot].block = block; + return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum); +} + +// GraphicMetadataInputBuffers + +GraphicMetadataInputBuffers::GraphicMetadataInputBuffers( + const char *componentName, const char *name) + : InputBuffers(componentName, name), + mImpl(mName), + mStore(GetCodec2PlatformAllocatorStore()) { } + +bool GraphicMetadataInputBuffers::requestNewBuffer( + size_t *index, sp *buffer) { + std::shared_ptr alloc; + c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); + if (err != C2_OK) { + return false; + } + sp newBuffer = new GraphicMetadataBuffer(mFormat, alloc); + if (newBuffer == nullptr) { + return false; + } + *index = mImpl.assignSlot(newBuffer); + *buffer = newBuffer; + return true; +} + +bool GraphicMetadataInputBuffers::releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + return mImpl.releaseSlot(buffer, c2buffer, release); +} + +bool GraphicMetadataInputBuffers::expireComponentBuffer( + const std::shared_ptr &c2buffer) { + return mImpl.expireComponentBuffer(c2buffer); +} + +void GraphicMetadataInputBuffers::flush() { + // This is no-op by default unless we're in array mode where we need to keep + // track of the flushed work. +} + +std::unique_ptr GraphicMetadataInputBuffers::toArrayMode( + size_t size) { + std::shared_ptr alloc; + c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); + if (err != C2_OK) { + return nullptr; + } + std::unique_ptr array( + new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]")); + array->setPool(mPool); + array->setFormat(mFormat); + array->initialize( + mImpl, + size, + [format = mFormat, alloc]() -> sp { + return new GraphicMetadataBuffer(format, alloc); + }); + return std::move(array); +} + +size_t GraphicMetadataInputBuffers::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +// GraphicInputBuffers + +GraphicInputBuffers::GraphicInputBuffers( + size_t numInputSlots, const char *componentName, const char *name) + : InputBuffers(componentName, name), + mImpl(mName), + mLocalBufferPool(LocalBufferPool::Create( + kMaxLinearBufferSize * numInputSlots)) { } + +bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp *buffer) { + // TODO: proper max input size + // TODO: read usage from intf + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + sp newBuffer = AllocateGraphicBuffer( + mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool); + if (newBuffer == nullptr) { + return false; + } + *index = mImpl.assignSlot(newBuffer); + handleImageData(newBuffer); + *buffer = newBuffer; + return true; +} + +bool GraphicInputBuffers::releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) { + return mImpl.releaseSlot(buffer, c2buffer, release); +} + +bool GraphicInputBuffers::expireComponentBuffer( + const std::shared_ptr &c2buffer) { + return mImpl.expireComponentBuffer(c2buffer); +} + +void GraphicInputBuffers::flush() { + // This is no-op by default unless we're in array mode where we need to keep + // track of the flushed work. +} + +std::unique_ptr GraphicInputBuffers::toArrayMode(size_t size) { + std::unique_ptr array( + new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]")); + array->setPool(mPool); + array->setFormat(mFormat); + array->initialize( + mImpl, + size, + [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp { + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + return AllocateGraphicBuffer( + pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp); + }); + return std::move(array); +} + +size_t GraphicInputBuffers::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +// OutputBuffersArray + +void OutputBuffersArray::initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate) { + mImpl.initialize(impl, minSize, allocate); +} + +status_t OutputBuffersArray::registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *clientBuffer) { + sp c2Buffer; + status_t err = mImpl.grabBuffer( + index, + &c2Buffer, + [buffer](const sp &clientBuffer) { + return clientBuffer->canCopy(buffer); + }); + if (err == WOULD_BLOCK) { + ALOGV("[%s] buffers temporarily not available", mName); + return err; + } else if (err != OK) { + ALOGD("[%s] grabBuffer failed: %d", mName, err); + return err; + } + c2Buffer->setFormat(mFormat); + if (!c2Buffer->copy(buffer)) { + ALOGD("[%s] copy buffer failed", mName); + return WOULD_BLOCK; + } + submit(c2Buffer); + handleImageData(c2Buffer); + *clientBuffer = c2Buffer; + ALOGV("[%s] grabbed buffer %zu", mName, *index); + return OK; +} + +status_t OutputBuffersArray::registerCsd( + const C2StreamInitDataInfo::output *csd, + size_t *index, + sp *clientBuffer) { + sp c2Buffer; + status_t err = mImpl.grabBuffer( + index, + &c2Buffer, + [csd](const sp &clientBuffer) { + return clientBuffer->base() != nullptr + && clientBuffer->capacity() >= csd->flexCount(); + }); + if (err != OK) { + return err; + } + memcpy(c2Buffer->base(), csd->m.value, csd->flexCount()); + c2Buffer->setRange(0, csd->flexCount()); + c2Buffer->setFormat(mFormat); + *clientBuffer = c2Buffer; + return OK; +} + +bool OutputBuffersArray::releaseBuffer( + const sp &buffer, std::shared_ptr *c2buffer) { + return mImpl.returnBuffer(buffer, c2buffer, true); +} + +void OutputBuffersArray::flush(const std::list> &flushedWork) { + (void)flushedWork; + mImpl.flush(); + if (mSkipCutBuffer != nullptr) { + mSkipCutBuffer->clear(); + } +} + +void OutputBuffersArray::getArray(Vector> *array) const { + mImpl.getArray(array); +} + +void OutputBuffersArray::realloc(const std::shared_ptr &c2buffer) { + std::function()> alloc; + switch (c2buffer->data().type()) { + case C2BufferData::LINEAR: { + uint32_t size = kLinearBufferSize; + const C2ConstLinearBlock &block = c2buffer->data().linearBlocks().front(); + if (block.size() < kMaxLinearBufferSize / 2) { + size = block.size() * 2; + } else { + size = kMaxLinearBufferSize; + } + alloc = [format = mFormat, size] { + return new LocalLinearBuffer(format, new ABuffer(size)); + }; + break; + } + + // TODO: add support + case C2BufferData::GRAPHIC: [[fallthrough]]; + + case C2BufferData::INVALID: [[fallthrough]]; + case C2BufferData::LINEAR_CHUNKS: [[fallthrough]]; + case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]]; + default: + ALOGD("Unsupported type: %d", (int)c2buffer->data().type()); + return; + } + mImpl.realloc(alloc); +} + +size_t OutputBuffersArray::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +// FlexOutputBuffers + +status_t FlexOutputBuffers::registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *clientBuffer) { + sp newBuffer = wrap(buffer); + if (newBuffer == nullptr) { + return NO_MEMORY; + } + newBuffer->setFormat(mFormat); + *index = mImpl.assignSlot(newBuffer); + handleImageData(newBuffer); + *clientBuffer = newBuffer; + ALOGV("[%s] registered buffer %zu", mName, *index); + return OK; +} + +status_t FlexOutputBuffers::registerCsd( + const C2StreamInitDataInfo::output *csd, + size_t *index, + sp *clientBuffer) { + sp newBuffer = new LocalLinearBuffer( + mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount())); + *index = mImpl.assignSlot(newBuffer); + *clientBuffer = newBuffer; + return OK; +} + +bool FlexOutputBuffers::releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer) { + return mImpl.releaseSlot(buffer, c2buffer, true); +} + +void FlexOutputBuffers::flush( + const std::list> &flushedWork) { + (void) flushedWork; + // This is no-op by default unless we're in array mode where we need to keep + // track of the flushed work. +} + +std::unique_ptr FlexOutputBuffers::toArrayMode(size_t size) { + std::unique_ptr array(new OutputBuffersArray(mComponentName.c_str())); + array->setFormat(mFormat); + array->transferSkipCutBuffer(mSkipCutBuffer); + array->initialize( + mImpl, + size, + [this]() { return allocateArrayBuffer(); }); + return std::move(array); +} + +size_t FlexOutputBuffers::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + +// LinearOutputBuffers + +void LinearOutputBuffers::flush( + const std::list> &flushedWork) { + if (mSkipCutBuffer != nullptr) { + mSkipCutBuffer->clear(); + } + FlexOutputBuffers::flush(flushedWork); +} + +sp LinearOutputBuffers::wrap(const std::shared_ptr &buffer) { + if (buffer == nullptr) { + ALOGV("[%s] using a dummy buffer", mName); + return new LocalLinearBuffer(mFormat, new ABuffer(0)); + } + if (buffer->data().type() != C2BufferData::LINEAR) { + ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type()); + // We expect linear output buffers from the component. + return nullptr; + } + if (buffer->data().linearBlocks().size() != 1u) { + ALOGV("[%s] no linear buffers", mName); + // We expect one and only one linear block from the component. + return nullptr; + } + sp clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer); + if (clientBuffer == nullptr) { + ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName); + return nullptr; + } + submit(clientBuffer); + return clientBuffer; +} + +sp LinearOutputBuffers::allocateArrayBuffer() { + // TODO: proper max output size + return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize)); +} + +// GraphicOutputBuffers + +sp GraphicOutputBuffers::wrap(const std::shared_ptr &buffer) { + return new DummyContainerBuffer(mFormat, buffer); +} + +sp GraphicOutputBuffers::allocateArrayBuffer() { + return new DummyContainerBuffer(mFormat); +} + +// RawGraphicOutputBuffers + +RawGraphicOutputBuffers::RawGraphicOutputBuffers( + size_t numOutputSlots, const char *componentName, const char *name) + : FlexOutputBuffers(componentName, name), + mLocalBufferPool(LocalBufferPool::Create( + kMaxLinearBufferSize * numOutputSlots)) { } + +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; + } else { + return ConstGraphicBlockBuffer::Allocate( + mFormat, + buffer, + [lbp = mLocalBufferPool](size_t capacity) { + return lbp->newBuffer(capacity); + }); + } +} + +sp RawGraphicOutputBuffers::allocateArrayBuffer() { + return ConstGraphicBlockBuffer::AllocateEmpty( + mFormat, + [lbp = mLocalBufferPool](size_t capacity) { + return lbp->newBuffer(capacity); + }); +} + +} // namespace android diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h new file mode 100644 index 0000000000..f5d9fee1aa --- /dev/null +++ b/media/codec2/sfplugin/CCodecBuffers.h @@ -0,0 +1,808 @@ +/* + * Copyright 2019, 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 CCODEC_BUFFERS_H_ + +#define CCODEC_BUFFERS_H_ + +#include + +#include +#include +#include + +#include "Codec2Buffer.h" +#include "SkipCutBuffer.h" + +namespace android { + +constexpr size_t kLinearBufferSize = 1048576; +// This can fit 4K RGBA frame, and most likely client won't need more than this. +constexpr size_t kMaxLinearBufferSize = 3840 * 2160 * 4; + +/** + * Base class for representation of buffers at one port. + */ +class CCodecBuffers { +public: + CCodecBuffers(const char *componentName, const char *name = "Buffers") + : mComponentName(componentName), + mChannelName(std::string(componentName) + ":" + name), + mName(mChannelName.c_str()) { + } + virtual ~CCodecBuffers() = default; + + /** + * Set format for MediaCodec-facing buffers. + */ + void setFormat(const sp &format); + + /** + * Return a copy of current format. + */ + sp dupFormat(); + + /** + * Returns true if the buffers are operating under array mode. + */ + virtual bool isArrayMode() const { return false; } + + /** + * Fills the vector with MediaCodecBuffer's if in array mode; otherwise, + * no-op. + */ + virtual void getArray(Vector> *) const {} + + /** + * Return number of buffers the client owns. + */ + virtual size_t numClientBuffers() const = 0; + + /** + * Examine image data from the buffer and update the format if necessary. + */ + void handleImageData(const sp &buffer); + +protected: + std::string mComponentName; ///< name of component for debugging + std::string mChannelName; ///< name of channel for debugging + const char *mName; ///< C-string version of channel name + // Format to be used for creating MediaCodec-facing buffers. + sp mFormat; + +private: + DISALLOW_EVIL_CONSTRUCTORS(CCodecBuffers); +}; + +class InputBuffers : public CCodecBuffers { +public: + InputBuffers(const char *componentName, const char *name = "Input[]") + : CCodecBuffers(componentName, name) { } + virtual ~InputBuffers() = default; + + /** + * Set a block pool to obtain input memory blocks. + */ + void setPool(const std::shared_ptr &pool) { mPool = pool; } + + /** + * Get a new MediaCodecBuffer for input and its corresponding index. + * Returns false if no new buffer can be obtained at the moment. + */ + virtual bool requestNewBuffer(size_t *index, sp *buffer) = 0; + + /** + * Release the buffer obtained from requestNewBuffer() and get the + * associated C2Buffer object back. Returns true if the buffer was on file + * and released successfully. + */ + virtual bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) = 0; + + /** + * Release the buffer that is no longer used by the codec process. Return + * true if and only if the buffer was on file and released successfully. + */ + virtual bool expireComponentBuffer( + const std::shared_ptr &c2buffer) = 0; + + /** + * Flush internal state. After this call, no index or buffer previously + * returned from requestNewBuffer() is valid. + */ + virtual void flush() = 0; + + /** + * Return array-backed version of input buffers. The returned object + * shall retain the internal state so that it will honor index and + * buffer from previous calls of requestNewBuffer(). + */ + virtual std::unique_ptr toArrayMode(size_t size) = 0; + +protected: + // Pool to obtain blocks for input buffers. + std::shared_ptr mPool; + +private: + DISALLOW_EVIL_CONSTRUCTORS(InputBuffers); +}; + +class OutputBuffers : public CCodecBuffers { +public: + OutputBuffers(const char *componentName, const char *name = "Output") + : CCodecBuffers(componentName, name) { } + virtual ~OutputBuffers() = default; + + /** + * Register output C2Buffer from the component and obtain corresponding + * index and MediaCodecBuffer object. Returns false if registration + * fails. + */ + virtual status_t registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *clientBuffer) = 0; + + /** + * Register codec specific data as a buffer to be consistent with + * MediaCodec behavior. + */ + virtual status_t registerCsd( + const C2StreamInitDataInfo::output * /* csd */, + size_t * /* index */, + sp * /* clientBuffer */) = 0; + + /** + * Release the buffer obtained from registerBuffer() and get the + * associated C2Buffer object back. Returns true if the buffer was on file + * and released successfully. + */ + virtual bool releaseBuffer( + const sp &buffer, std::shared_ptr *c2buffer) = 0; + + /** + * Flush internal state. After this call, no index or buffer previously + * returned from registerBuffer() is valid. + */ + virtual void flush(const std::list> &flushedWork) = 0; + + /** + * Return array-backed version of output buffers. The returned object + * shall retain the internal state so that it will honor index and + * buffer from previous calls of registerBuffer(). + */ + virtual std::unique_ptr toArrayMode(size_t size) = 0; + + /** + * Initialize SkipCutBuffer object. + */ + void initSkipCutBuffer( + int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount); + + /** + * Update the SkipCutBuffer object. No-op if it's never initialized. + */ + void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount); + + /** + * Submit buffer to SkipCutBuffer object, if initialized. + */ + void submit(const sp &buffer); + + /** + * Transfer SkipCutBuffer object to the other Buffers object. + */ + void transferSkipCutBuffer(const sp &scb); + +protected: + sp mSkipCutBuffer; + +private: + int32_t mDelay; + int32_t mPadding; + int32_t mSampleRate; + + void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount); + + DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers); +}; + +/** + * Simple local buffer pool backed by std::vector. + */ +class LocalBufferPool : public std::enable_shared_from_this { +public: + /** + * Create a new LocalBufferPool object. + * + * \param poolCapacity max total size of buffers managed by this pool. + * + * \return a newly created pool object. + */ + static std::shared_ptr Create(size_t poolCapacity); + + /** + * Return an ABuffer object whose size is at least |capacity|. + * + * \param capacity requested capacity + * \return nullptr if the pool capacity is reached + * an ABuffer object otherwise. + */ + sp newBuffer(size_t capacity); + +private: + /** + * ABuffer backed by std::vector. + */ + class VectorBuffer : public ::android::ABuffer { + public: + /** + * Construct a VectorBuffer by taking the ownership of supplied vector. + * + * \param vec backing vector of the buffer. this object takes + * ownership at construction. + * \param pool a LocalBufferPool object to return the vector at + * destruction. + */ + VectorBuffer(std::vector &&vec, const std::shared_ptr &pool); + + ~VectorBuffer() override; + + private: + std::vector mVec; + std::weak_ptr mPool; + }; + + Mutex mMutex; + size_t mPoolCapacity; + size_t mUsedSize; + std::list> mPool; + + /** + * Private constructor to prevent constructing non-managed LocalBufferPool. + */ + explicit LocalBufferPool(size_t poolCapacity) + : mPoolCapacity(poolCapacity), mUsedSize(0) { + } + + /** + * Take back the ownership of vec from the destructed VectorBuffer and put + * it in front of the pool. + */ + void returnVector(std::vector &&vec); + + DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool); +}; + +class BuffersArrayImpl; + +/** + * Flexible buffer slots implementation. + */ +class FlexBuffersImpl { +public: + FlexBuffersImpl(const char *name) + : mImplName(std::string(name) + ".Impl"), + mName(mImplName.c_str()) { } + + /** + * Assign an empty slot for a buffer and return the index. If there's no + * empty slot, just add one at the end and return it. + * + * \param buffer[in] a new buffer to assign a slot. + * \return index of the assigned slot. + */ + size_t assignSlot(const sp &buffer); + + /** + * Release the slot from the client, and get the C2Buffer object back from + * the previously assigned buffer. Note that the slot is not completely free + * until the returned C2Buffer object is freed. + * + * \param buffer[in] the buffer previously assigned a slot. + * \param c2buffer[in,out] pointer to C2Buffer to be populated. Ignored + * if null. + * \return true if the buffer is successfully released from a slot + * false otherwise + */ + bool releaseSlot( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release); + + /** + * Expire the C2Buffer object in the slot. + * + * \param c2buffer[in] C2Buffer object which the component released. + * \return true if the buffer is found in one of the slots and + * successfully released + * false otherwise + */ + bool expireComponentBuffer(const std::shared_ptr &c2buffer); + + /** + * The client abandoned all known buffers, so reclaim the ownership. + */ + void flush(); + + /** + * Return the number of buffers that are sent to the client but not released + * yet. + */ + size_t numClientBuffers() const; + +private: + friend class BuffersArrayImpl; + + std::string mImplName; ///< name for debugging + const char *mName; ///< C-string version of name + + struct Entry { + sp clientBuffer; + std::weak_ptr compBuffer; + }; + std::vector mBuffers; +}; + +/** + * Static buffer slots implementation based on a fixed-size array. + */ +class BuffersArrayImpl { +public: + BuffersArrayImpl() + : mImplName("BuffersArrayImpl"), + mName(mImplName.c_str()) { } + + /** + * Initialize buffer array from the original |impl|. The buffers known by + * the client is preserved, and the empty slots are populated so that the + * array size is at least |minSize|. + * + * \param impl[in] FlexBuffersImpl object used so far. + * \param minSize[in] minimum size of the buffer array. + * \param allocate[in] function to allocate a client buffer for an empty slot. + */ + void initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate); + + /** + * Grab a buffer from the underlying array which matches the criteria. + * + * \param index[out] index of the slot. + * \param buffer[out] the matching buffer. + * \param match[in] a function to test whether the buffer matches the + * criteria or not. + * \return OK if successful, + * WOULD_BLOCK if slots are being used, + * NO_MEMORY if no slot matches the criteria, even though it's + * available + */ + status_t grabBuffer( + size_t *index, + sp *buffer, + std::function &)> match = + [](const sp &) { return true; }); + + /** + * Return the buffer from the client, and get the C2Buffer object back from + * the buffer. Note that the slot is not completely free until the returned + * C2Buffer object is freed. + * + * \param buffer[in] the buffer previously grabbed. + * \param c2buffer[in,out] pointer to C2Buffer to be populated. Ignored + * if null. + * \return true if the buffer is successfully returned + * false otherwise + */ + bool returnBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release); + + /** + * Expire the C2Buffer object in the slot. + * + * \param c2buffer[in] C2Buffer object which the component released. + * \return true if the buffer is found in one of the slots and + * successfully released + * false otherwise + */ + bool expireComponentBuffer(const std::shared_ptr &c2buffer); + + /** + * Populate |array| with the underlying buffer array. + * + * \param array[out] an array to be filled with the underlying buffer array. + */ + void getArray(Vector> *array) const; + + /** + * The client abandoned all known buffers, so reclaim the ownership. + */ + void flush(); + + /** + * Reallocate the array with the given allocation function. + * + * \param alloc[in] the allocation function for client buffers. + */ + void realloc(std::function()> alloc); + + /** + * Return the number of buffers that are sent to the client but not released + * yet. + */ + size_t numClientBuffers() const; + +private: + std::string mImplName; ///< name for debugging + const char *mName; ///< C-string version of name + + struct Entry { + const sp clientBuffer; + std::weak_ptr compBuffer; + bool ownedByClient; + }; + std::vector mBuffers; +}; + +class InputBuffersArray : public InputBuffers { +public: + InputBuffersArray(const char *componentName, const char *name = "Input[N]") + : InputBuffers(componentName, name) { } + ~InputBuffersArray() override = default; + + /** + * Initialize this object from the non-array state. We keep existing slots + * at the same index, and for empty slots we allocate client buffers with + * the given allocate function. If the number of slots is less than minSize, + * we fill the array to the minimum size. + * + * \param impl[in] existing non-array state + * \param minSize[in] minimum size of the array + * \param allocate[in] allocate function to fill empty slots + */ + void initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate); + + bool isArrayMode() const final { return true; } + + std::unique_ptr toArrayMode(size_t) final { + return nullptr; + } + + void getArray(Vector> *array) const final; + + bool requestNewBuffer(size_t *index, sp *buffer) override; + + bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override; + + bool expireComponentBuffer( + const std::shared_ptr &c2buffer) override; + + void flush() override; + + size_t numClientBuffers() const final; + +private: + BuffersArrayImpl mImpl; +}; + +class LinearInputBuffers : public InputBuffers { +public: + LinearInputBuffers(const char *componentName, const char *name = "1D-Input") + : InputBuffers(componentName, name), + mImpl(mName) { } + ~LinearInputBuffers() override = default; + + bool requestNewBuffer(size_t *index, sp *buffer) override; + + bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override; + + bool expireComponentBuffer( + const std::shared_ptr &c2buffer) override; + + void flush() override; + + std::unique_ptr toArrayMode(size_t size) final; + + size_t numClientBuffers() const final; + + /** + * Allocate a client buffer with the given size. This method may be + * overridden to support different kind of linear buffers (e.g. encrypted). + */ + virtual sp alloc(size_t size); + +private: + FlexBuffersImpl mImpl; +}; + +class EncryptedLinearInputBuffers : public LinearInputBuffers { +public: + EncryptedLinearInputBuffers( + bool secure, + const sp &dealer, + const sp &crypto, + int32_t heapSeqNum, + size_t capacity, + size_t numInputSlots, + const char *componentName, const char *name = "EncryptedInput"); + + ~EncryptedLinearInputBuffers() override = default; + + sp alloc(size_t size) override; + +private: + C2MemoryUsage mUsage; + sp mDealer; + sp mCrypto; + int32_t mHeapSeqNum; + struct Entry { + std::weak_ptr block; + sp memory; + }; + std::vector mMemoryVector; +}; + +class GraphicMetadataInputBuffers : public InputBuffers { +public: + GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput"); + ~GraphicMetadataInputBuffers() override = default; + + bool requestNewBuffer(size_t *index, sp *buffer) override; + + bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override; + + bool expireComponentBuffer( + const std::shared_ptr &c2buffer) override; + + void flush() override; + + std::unique_ptr toArrayMode(size_t size) final; + + size_t numClientBuffers() const final; + +private: + FlexBuffersImpl mImpl; + std::shared_ptr mStore; +}; + +class GraphicInputBuffers : public InputBuffers { +public: + GraphicInputBuffers( + size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input"); + ~GraphicInputBuffers() override = default; + + bool requestNewBuffer(size_t *index, sp *buffer) override; + + bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer, + bool release) override; + + bool expireComponentBuffer( + const std::shared_ptr &c2buffer) override; + + void flush() override; + + std::unique_ptr toArrayMode( + size_t size) final; + + size_t numClientBuffers() const final; + +private: + FlexBuffersImpl mImpl; + std::shared_ptr mLocalBufferPool; +}; + +class DummyInputBuffers : public InputBuffers { +public: + DummyInputBuffers(const char *componentName, const char *name = "2D-Input") + : InputBuffers(componentName, name) { } + ~DummyInputBuffers() override = default; + + bool requestNewBuffer(size_t *, sp *) override { + return false; + } + + bool releaseBuffer( + const sp &, std::shared_ptr *, bool) override { + return false; + } + + bool expireComponentBuffer(const std::shared_ptr &) override { + return false; + } + void flush() override { + } + + std::unique_ptr toArrayMode(size_t) final { + return nullptr; + } + + bool isArrayMode() const final { return true; } + + void getArray(Vector> *array) const final { + array->clear(); + } + + size_t numClientBuffers() const final { + return 0u; + } +}; + +class OutputBuffersArray : public OutputBuffers { +public: + OutputBuffersArray(const char *componentName, const char *name = "Output[N]") + : OutputBuffers(componentName, name) { } + ~OutputBuffersArray() override = default; + + /** + * Initialize this object from the non-array state. We keep existing slots + * at the same index, and for empty slots we allocate client buffers with + * the given allocate function. If the number of slots is less than minSize, + * we fill the array to the minimum size. + * + * \param impl[in] existing non-array state + * \param minSize[in] minimum size of the array + * \param allocate[in] allocate function to fill empty slots + */ + void initialize( + const FlexBuffersImpl &impl, + size_t minSize, + std::function()> allocate); + + bool isArrayMode() const final { return true; } + + std::unique_ptr toArrayMode(size_t) final { + return nullptr; + } + + status_t registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *clientBuffer) final; + + status_t registerCsd( + const C2StreamInitDataInfo::output *csd, + size_t *index, + sp *clientBuffer) final; + + bool releaseBuffer( + const sp &buffer, std::shared_ptr *c2buffer) override; + + void flush(const std::list> &flushedWork) override; + + void getArray(Vector> *array) const final; + + /** + * Reallocate the array, filled with buffers with the same size as given + * buffer. + * + * \param c2buffer[in] the reference buffer + */ + void realloc(const std::shared_ptr &c2buffer); + + size_t numClientBuffers() const final; + +private: + BuffersArrayImpl mImpl; +}; + +class FlexOutputBuffers : public OutputBuffers { +public: + FlexOutputBuffers(const char *componentName, const char *name = "Output[]") + : OutputBuffers(componentName, name), + mImpl(mName) { } + + status_t registerBuffer( + const std::shared_ptr &buffer, + size_t *index, + sp *clientBuffer) override; + + status_t registerCsd( + const C2StreamInitDataInfo::output *csd, + size_t *index, + sp *clientBuffer) final; + + bool releaseBuffer( + const sp &buffer, + std::shared_ptr *c2buffer) override; + + void flush( + const std::list> &flushedWork) override; + + std::unique_ptr toArrayMode(size_t size) override; + + size_t numClientBuffers() const final; + + /** + * Return an appropriate Codec2Buffer object for the type of buffers. + * + * \param buffer C2Buffer object to wrap. + * + * \return appropriate Codec2Buffer object to wrap |buffer|. + */ + virtual sp wrap(const std::shared_ptr &buffer) = 0; + + /** + * Return an appropriate Codec2Buffer object for the type of buffers, to be + * used as an empty array buffer. + * + * \return appropriate Codec2Buffer object which can copy() from C2Buffers. + */ + virtual sp allocateArrayBuffer() = 0; + +private: + FlexBuffersImpl mImpl; +}; + +class LinearOutputBuffers : public FlexOutputBuffers { +public: + LinearOutputBuffers(const char *componentName, const char *name = "1D-Output") + : FlexOutputBuffers(componentName, name) { } + + void flush( + const std::list> &flushedWork) override; + + sp wrap(const std::shared_ptr &buffer) override; + + sp allocateArrayBuffer() override; +}; + +class GraphicOutputBuffers : public FlexOutputBuffers { +public: + GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output") + : FlexOutputBuffers(componentName, name) { } + + sp wrap(const std::shared_ptr &buffer) override; + + sp allocateArrayBuffer() override; +}; + +class RawGraphicOutputBuffers : public FlexOutputBuffers { +public: + RawGraphicOutputBuffers( + size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output"); + ~RawGraphicOutputBuffers() override = default; + + sp wrap(const std::shared_ptr &buffer) override; + + sp allocateArrayBuffer() override; + +private: + std::shared_ptr mLocalBufferPool; +}; + +} // namespace android + +#endif // CCODEC_BUFFERS_H_ -- GitLab From a1ce07c1e0de58c40bfd342b37bec7f7733aade3 Mon Sep 17 00:00:00 2001 From: dimitry Date: Fri, 12 Apr 2019 15:04:27 +0200 Subject: [PATCH 1280/1530] Add libdl_android.so to the list of accessible libs. The default namespace need access to libdl_android.so from platform namespace for libgraphicsenv.so Bug: http://b/130373059 Test: boot a sailfish with the change wait until it gets to setup wizard Change-Id: I55faa4b42f863dfbcc3b959d113cf481c7e2e337 --- apex/ld.config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index b6896f5e1e..715113d432 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -27,7 +27,7 @@ namespace.default.links = platform # TODO: replace the following when apex has a way to auto-generate this list # namespace.default.link.platform.shared_libs = %LLNDK_LIBRARIES% # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% -namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so +namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so ############################################################################### # "platform" namespace -- GitLab From 370875f93ecd5f4340cb3fb663e8df4dc331f561 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Fri, 12 Apr 2019 12:40:27 -0700 Subject: [PATCH 1281/1530] Camera: Camera3Device: Fix off-by-one error These things are ceaseless: / code reviews, design docs - / off-by-one errors making a mess Test: atest CtsCameraTestCases Bug: 129561652 Change-Id: I807d7ad4740ffe267053fe5da2080f9ecb45aa72 --- services/camera/libcameraservice/device3/Camera3Device.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index bc0dafefd5..a0d019819f 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4278,12 +4278,13 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * // Start scan at i, with the assumption that the stream order matches size_t realIdx = i; bool found = false; - for (size_t idx = 0; idx < finalConfiguration.streams.size(); idx++) { + size_t halStreamCount = finalConfiguration.streams.size(); + for (size_t idx = 0; idx < halStreamCount; idx++) { if (finalConfiguration.streams[realIdx].v3_2.id == streamId) { found = true; break; } - realIdx = (realIdx >= finalConfiguration.streams.size()) ? 0 : realIdx + 1; + realIdx = (realIdx >= halStreamCount - 1) ? 0 : realIdx + 1; } if (!found) { ALOGE("%s: Stream %d not found in stream configuration response from HAL", -- GitLab From fa11780f3d3f5ce87120c27cc539e8672ec4636f Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 12 Apr 2019 13:50:39 -0700 Subject: [PATCH 1282/1530] AudioFlinger: Ensure triple buffering is preserved for notifications Test: write_sine_callback, com.zebproj.etherpad Bug: 26230944 Bug: 129545307 Change-Id: I0b3abb571f706b947430ac36810260289ae0c627 --- services/audioflinger/Threads.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6e2497f8bc..2da098f6c3 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2136,9 +2136,15 @@ sp AudioFlinger::PlaybackThread::createTrac // notify every HAL buffer, regardless of the size of the track buffer maxNotificationFrames = mFrameCount; } else { - // For normal tracks, use at least double-buffering if no sample rate conversion, - // or at least triple-buffering if there is sample rate conversion - const int nBuffering = sampleRate == mSampleRate ? 2 : 3; + // Triple buffer the notification period for a triple buffered mixer period; + // otherwise, double buffering for the notification period is fine. + // + // TODO: This should be moved to AudioTrack to modify the notification period + // on AudioTrack::setBufferSizeInFrames() changes. + const int nBuffering = + (uint64_t{frameCount} * mSampleRate) + / (uint64_t{mNormalFrameCount} * sampleRate) == 3 ? 3 : 2; + maxNotificationFrames = frameCount / nBuffering; // If client requested a fast track but this was denied, then use the smaller maximum. if (requestedFlags & AUDIO_OUTPUT_FLAG_FAST) { -- GitLab From ab6f2f3870ebbe9b19cd766066797b0f00c2eb13 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Fri, 15 Feb 2019 14:43:51 -0800 Subject: [PATCH 1283/1530] Make BufferQueue based block pool non-blocking Bug: 129899822 Bug: 122433957 Change-Id: I720e0e3228cd14e3fe21ecc52934a6fd4b06a427 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 5 ++ media/codec2/vndk/platform/C2BqBuffer.cpp | 52 ++++++++----------- media/libstagefright/ACodec.cpp | 3 ++ media/libstagefright/MediaCodec.cpp | 2 + 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 00e0c165d4..90265dee3d 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -61,6 +61,10 @@ namespace { constexpr size_t kSmoothnessFactor = 4; constexpr size_t kRenderingDepth = 3; +// This is for keeping IGBP's buffer dropping logic in legacy mode other +// than making it non-blocking. Do not change this value. +const static size_t kDequeueTimeoutNs = 0; + } // namespace CCodecBufferChannel::QueueGuard::QueueGuard( @@ -1456,6 +1460,7 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { sp producer; if (newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + newSurface->setDequeueTimeout(kDequeueTimeoutNs); producer = newSurface->getIGraphicBufferProducer(); producer->setGenerationNumber(generation); } else { diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 9cc5677b3c..ae39220df0 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -223,7 +223,6 @@ private: } }); if (!transResult.isOk() || status != android::OK) { - ALOGD("cannot dequeue buffer %d", status); if (transResult.isOk()) { if (status == android::INVALID_OPERATION || status == android::TIMED_OUT || @@ -233,6 +232,7 @@ private: return C2_BLOCKING; } } + ALOGD("cannot dequeue buffer %d", status); return C2_BAD_VALUE; } } @@ -355,39 +355,31 @@ public: return mInit; } - static int kMaxIgbpRetry = 20; // TODO: small number can cause crash in releasing. static int kMaxIgbpRetryDelayUs = 10000; - int curTry = 0; - - while (curTry++ < kMaxIgbpRetry) { - std::unique_lock lock(mMutex); - // TODO: return C2_NO_INIT - if (mProducerId == 0) { - std::shared_ptr alloc; - c2_status_t err = mAllocator->newGraphicAllocation( - width, height, format, usage, &alloc); - if (err != C2_OK) { - return err; - } - std::shared_ptr poolData = - std::make_shared( - 0, (uint64_t)0, ~0, shared_from_this()); - // TODO: config? - *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); - ALOGV("allocated a buffer successfully"); - - return C2_OK; + std::unique_lock lock(mMutex); + if (mProducerId == 0) { + std::shared_ptr alloc; + c2_status_t err = mAllocator->newGraphicAllocation( + width, height, format, usage, &alloc); + if (err != C2_OK) { + return err; } - c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block); - if (status == C2_BLOCKING) { - lock.unlock(); - ::usleep(kMaxIgbpRetryDelayUs); - continue; - } - return status; + std::shared_ptr poolData = + std::make_shared( + 0, (uint64_t)0, ~0, shared_from_this()); + *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); + ALOGV("allocated a buffer successfully"); + + return C2_OK; + } + c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block); + if (status == C2_BLOCKING) { + lock.unlock(); + // in order not to drain cpu from component's spinning + ::usleep(kMaxIgbpRetryDelayUs); } - return C2_BLOCKING; + return status; } void setRenderCallback(const OnRenderCallback &renderCallback) { diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index d8de103ea9..3b87462f04 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -780,6 +780,9 @@ status_t ACodec::handleSetSurface(const sp &surface) { // need to enable allocation when attaching surface->getIGraphicBufferProducer()->allowAllocation(true); + // dequeueBuffer cannot time out + surface->setDequeueTimeout(-1); + // for meta data mode, we move dequeud buffers to the new surface. // for non-meta mode, we must move all registered buffers for (size_t i = 0; i < buffers.size(); ++i) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index a7d37e5970..b1fdb2c56b 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -2221,6 +2221,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { } if (mime.startsWithIgnoreCase("video/")) { + mSurface->setDequeueTimeout(-1); mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees); } } @@ -2508,6 +2509,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { && (mFlags & kFlagPushBlankBuffersOnShutdown)) { pushBlankBuffersToNativeWindow(mSurface.get()); } + surface->setDequeueTimeout(-1); mSoftRenderer = new SoftwareRenderer(surface); // TODO: check if this was successful } else { -- GitLab From 768fc2d63562253e5c4d3dcdabc6af0f24296636 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 15 Apr 2019 11:54:59 -0700 Subject: [PATCH 1284/1530] Make ConsumerProxy hold weak ref to GraphicBufferSource bug: 130387875 test: atest android.hardware.camera2.cts.RecordingTest#testAbandonedHighSpeedRequest MediaRecorderTest, EncodeDecodeTest Change-Id: I6c65bf493f96725d7a78d113fe1b70edd420eb38 --- .../bqhelper/GraphicBufferSource.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index 35df6d7dde..6728ce3f5f 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -282,24 +282,36 @@ private: }; struct GraphicBufferSource::ConsumerProxy : public BufferQueue::ConsumerListener { - ConsumerProxy(const sp &gbs) : mGbs(gbs) {} + ConsumerProxy(const wp &gbs) : mGbs(gbs) {} ~ConsumerProxy() = default; void onFrameAvailable(const BufferItem& item) override { - mGbs->onFrameAvailable(item); + sp gbs = mGbs.promote(); + if (gbs != nullptr) { + gbs->onFrameAvailable(item); + } } void onBuffersReleased() override { - mGbs->onBuffersReleased(); + sp gbs = mGbs.promote(); + if (gbs != nullptr) { + gbs->onBuffersReleased(); + } } void onSidebandStreamChanged() override { - mGbs->onSidebandStreamChanged(); + sp gbs = mGbs.promote(); + if (gbs != nullptr) { + gbs->onSidebandStreamChanged(); + } } private: - sp mGbs; + // Note that GraphicBufferSource is holding an sp to us, we can't hold + // an sp back to GraphicBufferSource as the circular dependency will + // make both immortal. + wp mGbs; }; GraphicBufferSource::GraphicBufferSource() : -- GitLab From 37f813cf709cc225acedde07f3df176c468f7a0d Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 18 Sep 2018 18:01:18 -0700 Subject: [PATCH 1285/1530] SoftAVCEnc: Increased max supported bitrate Bug: 116040772 Test: CTS test. Change-Id: I702cec41b41b72513bfebbf88e91efee3141ff1e --- media/libstagefright/codecs/avcenc/SoftAVCEnc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h index 8253b7db0e..6d2e0840bc 100644 --- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h @@ -36,7 +36,7 @@ namespace android { #define DEFAULT_MAX_REORDER_FRM 0 #define DEFAULT_QP_MIN 10 #define DEFAULT_QP_MAX 40 -#define DEFAULT_MAX_BITRATE 20000000 +#define DEFAULT_MAX_BITRATE 240000000 #define DEFAULT_MAX_SRCH_RANGE_X 256 #define DEFAULT_MAX_SRCH_RANGE_Y 256 #define DEFAULT_MAX_FRAMERATE 120000 -- GitLab From 2915cb3fb1cd718e335aae21b1f4ab6a0d0918c2 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 18 Sep 2018 17:59:26 -0700 Subject: [PATCH 1286/1530] C2SoftAVCEnc: Increased max supported bitrate Bug: 116040772 Test: CTS test Change-Id: I51d7f57805823fdb389f86e223a9d46947e42ec3 --- media/codec2/components/avc/C2SoftAvcEnc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h index aa3ca615c8..58a86d8718 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.h +++ b/media/codec2/components/avc/C2SoftAvcEnc.h @@ -38,7 +38,7 @@ namespace android { #define DEFAULT_MAX_REORDER_FRM 0 #define DEFAULT_QP_MIN 10 #define DEFAULT_QP_MAX 40 -#define DEFAULT_MAX_BITRATE 20000000 +#define DEFAULT_MAX_BITRATE 240000000 #define DEFAULT_MAX_SRCH_RANGE_X 256 #define DEFAULT_MAX_SRCH_RANGE_Y 256 #define DEFAULT_MAX_FRAMERATE 120000 -- GitLab From 95e3768e1936673cacf185c721f9b2c117a94a0a Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Fri, 12 Apr 2019 11:13:36 -0700 Subject: [PATCH 1287/1530] AudioFlinger::Thread ensure proper add / sub Bug: 126772841 Bug: 130371615 Test: 24 hours recording with 64 bit audioserver Change-Id: I71305448ee194d1cc325ab238f1681aee93ab18d --- media/libaudioclient/AudioTrackShared.cpp | 33 ++++++----------------- services/audioflinger/Threads.cpp | 12 +++++---- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp index c997cfa59e..ee6c3351e0 100644 --- a/media/libaudioclient/AudioTrackShared.cpp +++ b/media/libaudioclient/AudioTrackShared.cpp @@ -20,30 +20,13 @@ #include #include #include +#include #include #include namespace android { -// TODO: consider pulling this into a shared header. -// safe_sub_overflow is used ensure that subtraction occurs in the same native type -// with proper 2's complement overflow. Without calling this function, it is possible, -// for example, that optimizing compilers may elect to treat 32 bit subtraction -// as 64 bit subtraction when storing into a 64 bit destination as integer overflow is -// technically undefined. -template, - std::decay_t>{}>> - // ensure arguments are same type (ignoring volatile, which is used in cblk variables). -auto safe_sub_overflow(const T& a, const U& b) { - std::decay_t result; - (void)__builtin_sub_overflow(a, b, &result); - // note if __builtin_sub_overflow returns true, an overflow occurred. - return result; -} - // used to clamp a value to size_t. TODO: move to another file. template size_t clampToSize(T x) { @@ -204,7 +187,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques front = cblk->u.mStreaming.mFront; } // write to rear, read from front - ssize_t filled = safe_sub_overflow(rear, front); + ssize_t filled = audio_utils::safe_sub_overflow(rear, front); // pipe should not be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { if (mIsOut) { @@ -702,7 +685,7 @@ void ServerProxy::flushBufferIfNeeded() const size_t overflowBit = mFrameCountP2 << 1; const size_t mask = overflowBit - 1; int32_t newFront = (front & ~mask) | (flush & mask); - ssize_t filled = safe_sub_overflow(rear, newFront); + ssize_t filled = audio_utils::safe_sub_overflow(rear, newFront); if (filled >= (ssize_t)overflowBit) { // front and rear offsets span the overflow bit of the p2 mask // so rebasing newFront on the front offset is off by the overflow bit. @@ -744,7 +727,7 @@ int32_t AudioTrackServerProxy::getRear() const const size_t overflowBit = mFrameCountP2 << 1; const size_t mask = overflowBit - 1; int32_t newRear = (rear & ~mask) | (stop & mask); - ssize_t filled = safe_sub_overflow(newRear, front); + ssize_t filled = audio_utils::safe_sub_overflow(newRear, front); // overflowBit is unsigned, so cast to signed for comparison. if (filled >= (ssize_t)overflowBit) { // front and rear offsets span the overflow bit of the p2 mask @@ -796,7 +779,7 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush) front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); rear = cblk->u.mStreaming.mRear; } - ssize_t filled = safe_sub_overflow(rear, front); + ssize_t filled = audio_utils::safe_sub_overflow(rear, front); // pipe should not already be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); shutting down", @@ -923,7 +906,7 @@ size_t AudioTrackServerProxy::framesReady() return mFrameCount; } const int32_t rear = getRear(); - ssize_t filled = safe_sub_overflow(rear, cblk->u.mStreaming.mFront); + ssize_t filled = audio_utils::safe_sub_overflow(rear, cblk->u.mStreaming.mFront); // pipe should not already be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); shutting down", @@ -949,7 +932,7 @@ size_t AudioTrackServerProxy::framesReadySafe() const return mFrameCount; } const int32_t rear = getRear(); - const ssize_t filled = safe_sub_overflow(rear, cblk->u.mStreaming.mFront); + const ssize_t filled = audio_utils::safe_sub_overflow(rear, cblk->u.mStreaming.mFront); if (!(0 <= filled && (size_t) filled <= mFrameCount)) { return 0; // error condition, silently return 0. } @@ -1259,7 +1242,7 @@ size_t AudioRecordServerProxy::framesReadySafe() const } const int32_t front = android_atomic_acquire_load(&mCblk->u.mStreaming.mFront); const int32_t rear = mCblk->u.mStreaming.mRear; - const ssize_t filled = safe_sub_overflow(rear, front); + const ssize_t filled = audio_utils::safe_sub_overflow(rear, front); if (!(0 <= filled && (size_t) filled <= mFrameCount)) { return 0; // error condition, silently return 0. } diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 0b203c42f6..6b9bf195cc 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -7875,7 +7876,7 @@ void AudioFlinger::RecordThread::ResamplerBufferProvider::sync( RecordThread *recordThread = (RecordThread *) threadBase.get(); const int32_t rear = recordThread->mRsmpInRear; const int32_t front = mRsmpInFront; - const ssize_t filled = rear - front; + const ssize_t filled = audio_utils::safe_sub_overflow(rear, front); size_t framesIn; bool overrun = false; @@ -7889,7 +7890,8 @@ void AudioFlinger::RecordThread::ResamplerBufferProvider::sync( } else { // client is not keeping up with server, but give it latest data framesIn = recordThread->mRsmpInFrames; - mRsmpInFront = /* front = */ rear - framesIn; + mRsmpInFront = /* front = */ audio_utils::safe_sub_overflow( + rear, static_cast(framesIn)); overrun = true; } if (framesAvailable != NULL) { @@ -7913,7 +7915,7 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( RecordThread *recordThread = (RecordThread *) threadBase.get(); int32_t rear = recordThread->mRsmpInRear; int32_t front = mRsmpInFront; - ssize_t filled = rear - front; + ssize_t filled = audio_utils::safe_sub_overflow(rear, front); // FIXME should not be P2 (don't want to increase latency) // FIXME if client not keeping up, discard LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames)); @@ -7946,13 +7948,13 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer( AudioBufferProvider::Buffer* buffer) { - size_t stepCount = buffer->frameCount; + int32_t stepCount = static_cast(buffer->frameCount); if (stepCount == 0) { return; } ALOG_ASSERT(stepCount <= mRsmpInUnrel); mRsmpInUnrel -= stepCount; - mRsmpInFront += stepCount; + mRsmpInFront = audio_utils::safe_add_overflow(mRsmpInFront, stepCount); buffer->raw = NULL; buffer->frameCount = 0; } -- GitLab From c8ce1d8b31fc439b30b17895965b71f746b9e41a Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 27 Mar 2019 10:18:38 -0700 Subject: [PATCH 1288/1530] Remove ligbui dep from libcodec2_vndk and libcodec2_hidl@1.0 bug: 128894663 test: builds; atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I3bb8792bd6eec46a9131bc6bd0c773648f430538 --- media/codec2/hidl/1.0/utils/Android.bp | 59 +++- .../hidl/1.0/utils/ClientBlockHelper.cpp | 232 +++++++++++++ .../codec2/hidl/1.0/utils/ComponentStore.cpp | 3 +- .../codec2/hidl/1.0/ClientBlockHelper.h | 95 ++++++ .../1.0/utils/include/codec2/hidl/1.0/types.h | 55 ---- media/codec2/hidl/1.0/utils/types.cpp | 149 --------- .../hidl/1.0/vts/functional/common/Android.bp | 4 +- media/codec2/hidl/client/Android.bp | 4 +- .../hidl/client/include/codec2/hidl/client.h | 3 +- media/codec2/sfplugin/CCodec.cpp | 4 +- media/codec2/sfplugin/CCodecBufferChannel.h | 2 - media/codec2/vndk/Android.bp | 5 +- media/codec2/vndk/include/types.h | 130 ++++++++ media/codec2/vndk/platform/C2BqBuffer.cpp | 9 +- media/codec2/vndk/types.cpp | 309 ++++++++++++++++++ .../bqhelper/GraphicBufferSource.cpp | 7 + .../bqhelper/GraphicBufferSource.h | 5 + 17 files changed, 850 insertions(+), 225 deletions(-) create mode 100644 media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp create mode 100644 media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h create mode 100644 media/codec2/vndk/include/types.h create mode 100644 media/codec2/vndk/types.cpp diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index 97dde71626..b2a5feef1b 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -1,3 +1,48 @@ +// DO NOT DEPEND ON THIS DIRECTLY +// use libcodec2-hidl-client-defaults instead +cc_library { + name: "libcodec2_hidl_client@1.0", + + defaults: ["hidl_defaults"], + + srcs: [ + "ClientBlockHelper.cpp", + "types.cpp", + ], + + header_libs: [ + "libcodec2_internal", // private + ], + + shared_libs: [ + "android.hardware.media.bufferpool@2.0", + "android.hardware.media.c2@1.0", + "libbase", + "libcodec2", + "libcodec2_vndk", + "libcutils", + "libgui", + "libhidlbase", + "liblog", + "libstagefright_bufferpool@2.0", + "libui", + "libutils", + ], + + export_include_dirs: [ + "include", + ], + + export_shared_lib_headers: [ + "android.hardware.media.c2@1.0", + "libcodec2", + "libgui", + "libstagefright_bufferpool@2.0", + "libui", + ], +} + + // DO NOT DEPEND ON THIS DIRECTLY // use libcodec2-hidl-defaults instead cc_library { @@ -33,12 +78,10 @@ cc_library { "android.hardware.media.bufferpool@2.0", "android.hardware.media.c2@1.0", "android.hardware.media.omx@1.0", - "android.hidl.safe_union@1.0", "libbase", "libcodec2", "libcodec2_vndk", "libcutils", - "libgui", "libhidlbase", "libhidltransport", "libhwbinder", @@ -58,7 +101,6 @@ cc_library { "libcodec2", "libhidlbase", "libstagefright_bufferpool@2.0", - "libstagefright_bufferqueue_helper", "libui", ], } @@ -73,3 +115,14 @@ cc_defaults { "libcodec2_hidl@1.0", ], } + +// public dependency for Codec 2.0 HAL client +cc_defaults { + name: "libcodec2-hidl-client-defaults", + defaults: ["libcodec2-impl-defaults"], + + shared_libs: [ + "android.hardware.media.c2@1.0", + "libcodec2_hidl_client@1.0", + ], +} diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp new file mode 100644 index 0000000000..1786c69e90 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-block_helper" +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + +using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: + V2_0::IGraphicBufferProducer; +using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: + V2_0::utils::B2HGraphicBufferProducer; + +namespace /* unnamed */ { + +// Create a GraphicBuffer object from a graphic block. +sp createGraphicBuffer(const C2ConstGraphicBlock& block) { + uint32_t width; + uint32_t height; + uint32_t format; + uint64_t usage; + uint32_t stride; + uint32_t generation; + uint64_t bqId; + int32_t bqSlot; + _UnwrapNativeCodec2GrallocMetadata( + block.handle(), &width, &height, &format, &usage, + &stride, &generation, &bqId, reinterpret_cast(&bqSlot)); + native_handle_t *grallocHandle = + UnwrapNativeCodec2GrallocHandle(block.handle()); + sp graphicBuffer = + new GraphicBuffer(grallocHandle, + GraphicBuffer::CLONE_HANDLE, + width, height, format, + 1, usage, stride); + native_handle_delete(grallocHandle); + return graphicBuffer; +} + +template +void forEachBlock(C2FrameData& frameData, + BlockProcessor process) { + for (const std::shared_ptr& buffer : frameData.buffers) { + if (buffer) { + for (const C2ConstGraphicBlock& block : + buffer->data().graphicBlocks()) { + process(block); + } + } + } +} + +template +void forEachBlock(const std::list>& workList, + BlockProcessor process, + bool processInput, bool processOutput) { + for (const std::unique_ptr& work : workList) { + if (!work) { + continue; + } + if (processInput) { + forEachBlock(work->input, process); + } + if (processOutput) { + for (const std::unique_ptr& worklet : work->worklets) { + if (worklet) { + forEachBlock(worklet->output, + process); + } + } + } + } +} + +sp getHgbp(const sp& igbp) { + sp hgbp = + igbp->getHalInterface(); + return hgbp ? hgbp : + new B2HGraphicBufferProducer(igbp); +} + +} // unnamed namespace + +status_t attachToBufferQueue(const C2ConstGraphicBlock& block, + const sp& igbp, + uint32_t generation, + int32_t* bqSlot) { + if (!igbp) { + LOG(WARNING) << "attachToBufferQueue -- null producer."; + return NO_INIT; + } + + sp graphicBuffer = createGraphicBuffer(block); + graphicBuffer->setGenerationNumber(generation); + + LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:" + << " block dimension " << block.width() << "x" + << block.height() + << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x" + << graphicBuffer->getHeight() + << std::hex << std::setfill('0') + << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat() + << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage() + << std::dec << std::setfill(' ') + << ", stride " << graphicBuffer->getStride() + << ", generation " << graphicBuffer->getGenerationNumber(); + + status_t result = igbp->attachBuffer(bqSlot, graphicBuffer); + if (result != OK) { + LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: " + "status = " << result << "."; + return result; + } + LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #" + << *bqSlot << "."; + return OK; +} + +bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, + uint32_t* generation, + uint64_t* bqId, + int32_t* bqSlot) { + return _C2BlockFactory::GetBufferQueueData( + _C2BlockFactory::GetGraphicBlockPoolData(block), + generation, bqId, bqSlot); +} + +bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, + const sp& igbp, + uint64_t bqId, + uint32_t generation) { + std::shared_ptr<_C2BlockPoolData> data = + _C2BlockFactory::GetGraphicBlockPoolData(block); + if (!data) { + return false; + } + + uint32_t oldGeneration; + uint64_t oldId; + int32_t oldSlot; + // If the block is not bufferqueue-based, do nothing. + if (!_C2BlockFactory::GetBufferQueueData( + data, &oldGeneration, &oldId, &oldSlot) || + (oldId == 0)) { + return false; + } + + // If the block's bqId is the same as the desired bqId, just hold. + if ((oldId == bqId) && (oldGeneration == generation)) { + LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" + << " bqId " << oldId + << ", bqSlot " << oldSlot + << ", generation " << generation + << "."; + _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); + return true; + } + + // Otherwise, attach to the given igbp, which must not be null. + if (!igbp) { + return false; + } + + int32_t bqSlot; + status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot); + + if (result != OK) { + LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" + << " target bqId " << bqId + << ", generation " << generation + << "."; + return false; + } + + LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" + << " bqId " << bqId + << ", bqSlot " << bqSlot + << ", generation " << generation + << "."; + _C2BlockFactory::AssignBlockToBufferQueue( + data, getHgbp(igbp), generation, bqId, bqSlot, true); + return true; +} + +void holdBufferQueueBlocks(const std::list>& workList, + const sp& igbp, + uint64_t bqId, + uint32_t generation, + bool forInput) { + forEachBlock(workList, + std::bind(holdBufferQueueBlock, + std::placeholders::_1, igbp, bqId, generation), + forInput, !forInput); +} + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp index 53bb6d2688..d6f84b4152 100644 --- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp @@ -222,8 +222,7 @@ Return ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) sp inputSurface = new InputSurface( this, std::make_shared(), - new ::android::hardware::graphics::bufferqueue::V2_0::utils:: - B2HGraphicBufferProducer(source->getIGraphicBufferProducer()), + source->getHGraphicBufferProducer(), source); _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY, inputSurface); diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h new file mode 100644 index 0000000000..be429ace28 --- /dev/null +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h @@ -0,0 +1,95 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLIENT_BLOCK_HELPER_H +#define CLIENT_BLOCK_HELPER_H + +#include +#include +#include + +namespace android { +namespace hardware { +namespace media { +namespace c2 { +namespace V1_0 { +namespace utils { + + +// BufferQueue-Based Block Operations +// ================================== + +// Create a GraphicBuffer object from a graphic block and attach it to an +// IGraphicBufferProducer. +status_t attachToBufferQueue(const C2ConstGraphicBlock& block, + const sp& igbp, + uint32_t generation, + int32_t* bqSlot); + +// Return false if block does not come from a bufferqueue-based blockpool. +// Otherwise, extract generation, bqId and bqSlot and return true. +bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, + uint32_t* generation, + uint64_t* bqId, + int32_t* bqSlot); + +// Assign the given block to a bufferqueue so that when the block is destroyed, +// cancelBuffer() will be called. +// +// If the block does not come from a bufferqueue-based blockpool, this function +// returns false. +// +// If the block already has a bufferqueue assignment that matches the given one, +// the function returns true. +// +// If the block already has a bufferqueue assignment that does not match the +// given one, the block will be reassigned to the given bufferqueue. This +// will call attachBuffer() on the given igbp. The function then returns true on +// success or false on any failure during the operation. +// +// Note: This function should be called after detachBuffer() or dequeueBuffer() +// is called manually. +bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, + const sp& igbp, + uint64_t bqId, + uint32_t generation); + +// Call holdBufferQueueBlock() on input or output blocks in the given workList. +// Since the bufferqueue assignment for input and output buffers can be +// different, this function takes forInput to determine whether the given +// bufferqueue is for input buffers or output buffers. (The default value of +// forInput is false.) +// +// In the (rare) case that both input and output buffers are bufferqueue-based, +// this function must be called twice, once for the input buffers and once for +// the output buffers. +// +// Note: This function should be called after WorkBundle has been received from +// another process. +void holdBufferQueueBlocks(const std::list>& workList, + const sp& igbp, + uint64_t bqId, + uint32_t generation, + bool forInput = false); + +} // namespace utils +} // namespace V1_0 +} // namespace c2 +} // namespace media +} // namespace hardware +} // namespace android + +#endif // CLIENT_BLOCK_HELPER_H diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index 817d14881b..0ddfec51ba 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -50,7 +49,6 @@ using ::android::status_t; using ::android::sp; using ::android::hardware::media::bufferpool::V2_0::implementation:: ConnectionId; -using ::android::IGraphicBufferProducer; // Types of metadata for Blocks. struct C2Hidl_Range { @@ -301,20 +299,6 @@ c2_status_t toC2Status(::android::hardware::media::bufferpool::V2_0:: // BufferQueue-Based Block Operations // ================================== -// Create a GraphicBuffer object from a graphic block and attach it to an -// IGraphicBufferProducer. -status_t attachToBufferQueue(const C2ConstGraphicBlock& block, - const sp& igbp, - uint32_t generation, - int32_t* bqSlot); - -// Return false if block does not come from a bufferqueue-based blockpool. -// Otherwise, extract generation, bqId and bqSlot and return true. -bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, - uint32_t* generation, - uint64_t* bqId, - int32_t* bqSlot); - // Disassociate the given block with its designated bufferqueue so that // cancelBuffer() will not be called when the block is destroyed. If the block // does not have a designated bufferqueue, the function returns false. @@ -336,45 +320,6 @@ void yieldBufferQueueBlocks(const std::list>& workList, bool processInput = false, bool processOutput = true); -// Assign the given block to a bufferqueue so that when the block is destroyed, -// cancelBuffer() will be called. -// -// If the block does not come from a bufferqueue-based blockpool, this function -// returns false. -// -// If the block already has a bufferqueue assignment that matches the given one, -// the function returns true. -// -// If the block already has a bufferqueue assignment that does not match the -// given one, the block will be reassigned to the given bufferqueue. This -// will call attachBuffer() on the given igbp. The function then returns true on -// success or false on any failure during the operation. -// -// Note: This function should be called after detachBuffer() or dequeueBuffer() -// is called manually. -bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, - const sp& igbp, - uint64_t bqId, - uint32_t generation); - -// Call holdBufferQueueBlock() on input or output blocks in the given workList. -// Since the bufferqueue assignment for input and output buffers can be -// different, this function takes forInput to determine whether the given -// bufferqueue is for input buffers or output buffers. (The default value of -// forInput is false.) -// -// In the (rare) case that both input and output buffers are bufferqueue-based, -// this function must be called twice, once for the input buffers and once for -// the output buffers. -// -// Note: This function should be called after WorkBundle has been received from -// another process. -void holdBufferQueueBlocks(const std::list>& workList, - const sp& igbp, - uint64_t bqId, - uint32_t generation, - bool forInput = false); - } // namespace utils } // namespace V1_0 } // namespace c2 diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 74320e75cb..3fd2f921ae 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -18,9 +18,7 @@ #define LOG_TAG "Codec2-types" #include -#include #include -#include #include #include @@ -54,10 +52,6 @@ using ::android::hardware::media::bufferpool::V2_0::implementation:: ClientManager; using ::android::hardware::media::bufferpool::V2_0::implementation:: TransactionId; -using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: - V2_0::IGraphicBufferProducer; -using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue:: - V2_0::utils::B2HGraphicBufferProducer; const char* asString(Status status, const char* def) { return asString(static_cast(status), def); @@ -1741,30 +1735,6 @@ c2_status_t toC2Status(ResultStatus rs) { namespace /* unnamed */ { -// Create a GraphicBuffer object from a graphic block. -sp createGraphicBuffer(const C2ConstGraphicBlock& block) { - uint32_t width; - uint32_t height; - uint32_t format; - uint64_t usage; - uint32_t stride; - uint32_t generation; - uint64_t bqId; - int32_t bqSlot; - _UnwrapNativeCodec2GrallocMetadata( - block.handle(), &width, &height, &format, &usage, - &stride, &generation, &bqId, reinterpret_cast(&bqSlot)); - native_handle_t *grallocHandle = - UnwrapNativeCodec2GrallocHandle(block.handle()); - sp graphicBuffer = - new GraphicBuffer(grallocHandle, - GraphicBuffer::CLONE_HANDLE, - width, height, format, - 1, usage, stride); - native_handle_delete(grallocHandle); - return graphicBuffer; -} - template void forEachBlock(C2FrameData& frameData, BlockProcessor process) { @@ -1800,59 +1770,8 @@ void forEachBlock(const std::list>& workList, } } -sp getHgbp(const sp& igbp) { - sp hgbp = - igbp->getHalInterface(); - return hgbp ? hgbp : - new B2HGraphicBufferProducer(igbp); -} - } // unnamed namespace -status_t attachToBufferQueue(const C2ConstGraphicBlock& block, - const sp& igbp, - uint32_t generation, - int32_t* bqSlot) { - if (!igbp) { - LOG(WARNING) << "attachToBufferQueue -- null producer."; - return NO_INIT; - } - - sp graphicBuffer = createGraphicBuffer(block); - graphicBuffer->setGenerationNumber(generation); - - LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:" - << " block dimension " << block.width() << "x" - << block.height() - << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x" - << graphicBuffer->getHeight() - << std::hex << std::setfill('0') - << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat() - << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage() - << std::dec << std::setfill(' ') - << ", stride " << graphicBuffer->getStride() - << ", generation " << graphicBuffer->getGenerationNumber(); - - status_t result = igbp->attachBuffer(bqSlot, graphicBuffer); - if (result != OK) { - LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: " - "status = " << result << "."; - return result; - } - LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #" - << *bqSlot << "."; - return OK; -} - -bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, - uint32_t* generation, - uint64_t* bqId, - int32_t* bqSlot) { - return _C2BlockFactory::GetBufferQueueData( - _C2BlockFactory::GetGraphicBlockPoolData(block), - generation, bqId, bqSlot); -} - bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); @@ -1869,74 +1788,6 @@ void yieldBufferQueueBlocks( forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput); } -bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, - const sp& igbp, - uint64_t bqId, - uint32_t generation) { - std::shared_ptr<_C2BlockPoolData> data = - _C2BlockFactory::GetGraphicBlockPoolData(block); - if (!data) { - return false; - } - - uint32_t oldGeneration; - uint64_t oldId; - int32_t oldSlot; - // If the block is not bufferqueue-based, do nothing. - if (!_C2BlockFactory::GetBufferQueueData( - data, &oldGeneration, &oldId, &oldSlot) || - (oldId == 0)) { - return false; - } - - // If the block's bqId is the same as the desired bqId, just hold. - if ((oldId == bqId) && (oldGeneration == generation)) { - LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" - << " bqId " << oldId - << ", bqSlot " << oldSlot - << ", generation " << generation - << "."; - _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); - return true; - } - - // Otherwise, attach to the given igbp, which must not be null. - if (!igbp) { - return false; - } - - int32_t bqSlot; - status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot); - - if (result != OK) { - LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" - << " target bqId " << bqId - << ", generation " << generation - << "."; - return false; - } - - LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" - << " bqId " << bqId - << ", bqSlot " << bqSlot - << ", generation " << generation - << "."; - _C2BlockFactory::AssignBlockToBufferQueue( - data, getHgbp(igbp), generation, bqId, bqSlot, true); - return true; -} - -void holdBufferQueueBlocks(const std::list>& workList, - const sp& igbp, - uint64_t bqId, - uint32_t generation, - bool forInput) { - forEachBlock(workList, - std::bind(holdBufferQueueBlock, - std::placeholders::_1, igbp, bqId, generation), - forInput, !forInput); -} - } // namespace utils } // namespace V1_0 } // namespace c2 diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp index 94f46edffe..da0061ad05 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp @@ -2,7 +2,7 @@ cc_library_static { name: "VtsMediaC2V1_0CommonUtil", defaults: [ "VtsHalTargetTestDefaults", - "libcodec2-hidl-defaults", + "libcodec2-hidl-client-defaults", ], include_dirs: [ @@ -20,7 +20,7 @@ cc_defaults { name: "VtsMediaC2V1_0Defaults", defaults: [ "VtsHalTargetTestDefaults", - "libcodec2-hidl-defaults", + "libcodec2-hidl-client-defaults", ], static_libs: [ diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index 965e438aa1..a174008b09 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -12,7 +12,7 @@ cc_library { "libbase", "libbinder", "libcodec2", - "libcodec2_hidl@1.0", + "libcodec2_hidl_client@1.0", "libcodec2_vndk", "libcutils", "libgui", @@ -30,7 +30,7 @@ cc_library { export_shared_lib_headers: [ "libcodec2", - "libcodec2_hidl@1.0", + "libcodec2_hidl_client@1.0", ], } diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 8265380bf9..18517527fa 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -18,8 +18,7 @@ #define CODEC2_HIDL_CLIENT_H #include -#include - +#include #include #include #include diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 751c8c53f6..85c783bec1 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include "C2OMXNode.h" #include "CCodec.h" @@ -1034,7 +1033,8 @@ sp CCodec::CreateOmxInputSurface() { OmxStatus s; android::sp gbp; android::sp gbs; - android::Return transStatus = omx->createInputSurface( + using ::android::hardware::Return; + Return transStatus = omx->createInputSurface( [&s, &gbp, &gbs]( OmxStatus status, const android::sp& producer, diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 1ea29b426f..6a24a1a005 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -27,8 +27,6 @@ #include #include -#include -#include #include #include #include diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index c6ca670baa..198bd72f44 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -26,6 +26,7 @@ cc_library_shared { "C2PlatformStorePluginLoader.cpp", "C2Store.cpp", "platform/C2BqBuffer.cpp", + "types.cpp", "util/C2Debug.cpp", "util/C2InterfaceHelper.cpp", "util/C2InterfaceUtils.cpp", @@ -38,7 +39,6 @@ cc_library_shared { export_shared_lib_headers: [ "libbase", - "libgui", "android.hardware.media.bufferpool@2.0", ], @@ -60,13 +60,12 @@ cc_library_shared { "libbinder", "libcutils", "libdl", - "libgui", "libhardware", "libhidlbase", "libion", "libfmq", "liblog", - "libstagefright_bufferqueue_helper", + "libnativewindow", "libstagefright_foundation", "libstagefright_bufferpool@2.0", "libui", diff --git a/media/codec2/vndk/include/types.h b/media/codec2/vndk/include/types.h new file mode 100644 index 0000000000..b41d3f8909 --- /dev/null +++ b/media/codec2/vndk/include/types.h @@ -0,0 +1,130 @@ +/* + * Copyright 2019 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_VNDK_TYPES_H +#define CODEC2_VNDK_TYPES_H + +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// Status +// ====== + +using HStatus = ::android::hardware::graphics::bufferqueue::V2_0:: + Status; + +// A status_t value may have flags encoded. These flags are decoded into boolean +// values if their corresponding output pointers are not null. +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation = nullptr, + bool* releaseAllBuffers = nullptr); +// Simple 1-to-1 mapping. If BUFFER_NEEDS_REALLOCATION or RELEASE_ALL_BUFFERS +// needs to be added, it must be done manually afterwards. +bool h2b(HStatus from, status_t* to); + +// Fence +// ===== + +using BFence = ::android::Fence; +// This class manages the lifetime of a copied handle. Its destructor calls +// native_handle_delete() but not native_handle_close(). +struct HFenceWrapper { + HFenceWrapper() = default; + // Sets mHandle to a new value. + HFenceWrapper(native_handle_t* h); + // Deletes mHandle without closing. + ~HFenceWrapper(); + // Deletes mHandle without closing, then sets mHandle to a new value. + HFenceWrapper& set(native_handle_t* h); + HFenceWrapper& operator=(native_handle_t* h); + // Returns a non-owning hidl_handle pointing to mHandle. + hidl_handle getHandle() const; + operator hidl_handle() const; +protected: + native_handle_t* mHandle{nullptr}; +}; + +// Does not clone the fd---only copy the fd. The returned HFenceWrapper should +// not outlive the input Fence object. +bool b2h(sp const& from, HFenceWrapper* to); +// Clones the fd and puts it in a new Fence object. +bool h2b(native_handle_t const* from, sp* to); + +// ConnectionType +// ============== + +using HConnectionType = ::android::hardware::graphics::bufferqueue::V2_0:: + ConnectionType; + +bool b2h(int from, HConnectionType* to); +bool h2b(HConnectionType from, int* to); + +// Rect +// ==== + +using BRect = ::android::Rect; +using HRect = ::android::hardware::graphics::common::V1_2::Rect; + +bool b2h(BRect const& from, HRect* to); +bool h2b(HRect const& from, BRect* to); + +// Region +// ====== + +using BRegion = ::android::Region; +using HRegion = ::android::hardware::hidl_vec; + +bool b2h(BRegion const& from, HRegion* to); +bool h2b(HRegion const& from, BRegion* to); + +// GraphicBuffer +// ============= + +using HardwareBuffer = ::android::hardware::graphics::common::V1_2:: + HardwareBuffer; +using HardwareBufferDescription = ::android::hardware::graphics::common::V1_2:: + HardwareBufferDescription; + +// Does not clone the handle. The returned HardwareBuffer should not outlive the +// input GraphicBuffer. Note that HardwareBuffer does not carry the generation +// number, so this function needs another output argument. +bool b2h(sp const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber = nullptr); +// Clones the handle and creates a new GraphicBuffer from the cloned handle. +// Note that the generation number of the GraphicBuffer has to be set manually +// afterwards because HardwareBuffer does not have such information. +bool h2b(HardwareBuffer const& from, sp* to); + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // CODEC2_VNDK_TYPES_H + diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 9cc5677b3c..fcf846b088 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -18,8 +18,12 @@ #define LOG_TAG "C2BqBuffer" #include -#include -#include +#include +#include +#include + +#include + #include #include @@ -35,7 +39,6 @@ using ::android::C2AllocatorGralloc; using ::android::C2AndroidMemoryUsage; using ::android::Fence; using ::android::GraphicBuffer; -using ::android::IGraphicBufferProducer; using ::android::sp; using ::android::status_t; using ::android::wp; diff --git a/media/codec2/vndk/types.cpp b/media/codec2/vndk/types.cpp new file mode 100644 index 0000000000..99b1e9ba95 --- /dev/null +++ b/media/codec2/vndk/types.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2019 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 +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// TODO: move this into ui/BufferQueueDefs.h so that we don't need +// to include headers from libgui. +enum { + // The API number used to indicate the currently connected producer + CURRENTLY_CONNECTED_API = -1, + // The API number used to indicate that no producer is connected + NO_CONNECTED_API = 0, +}; + +// Status +// ====== + +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation, bool* releaseAllBuffers) { + switch (from) { + case OK: + *to = HStatus::OK; break; + case NO_MEMORY: + *to = HStatus::NO_MEMORY; break; + case NO_INIT: + *to = HStatus::NO_INIT; break; + case BAD_VALUE: + *to = HStatus::BAD_VALUE; break; + case DEAD_OBJECT: + *to = HStatus::DEAD_OBJECT; break; + case INVALID_OPERATION: + *to = HStatus::INVALID_OPERATION; break; + case TIMED_OUT: + *to = HStatus::TIMED_OUT; break; + case WOULD_BLOCK: + *to = HStatus::WOULD_BLOCK; break; + case UNKNOWN_ERROR: + *to = HStatus::UNKNOWN_ERROR; break; + default: + status_t mask = + (bufferNeedsReallocation ? BufferQueueDefs::BUFFER_NEEDS_REALLOCATION : 0) + | (releaseAllBuffers ? BufferQueueDefs::RELEASE_ALL_BUFFERS : 0); + if (from & ~mask) { + *to = static_cast(from); + } else { + *to = HStatus::OK; + if (bufferNeedsReallocation) { + *bufferNeedsReallocation = from & BufferQueueDefs::BUFFER_NEEDS_REALLOCATION; + } + if (releaseAllBuffers) { + *releaseAllBuffers = from & BufferQueueDefs::RELEASE_ALL_BUFFERS; + } + } + } + return true; +} + +bool h2b(HStatus from, status_t* to) { + switch (from) { + case HStatus::OK: + *to = OK; break; + case HStatus::NO_MEMORY: + *to = NO_MEMORY; break; + case HStatus::NO_INIT: + *to = NO_INIT; break; + case HStatus::BAD_VALUE: + *to = BAD_VALUE; break; + case HStatus::DEAD_OBJECT: + *to = DEAD_OBJECT; break; + case HStatus::INVALID_OPERATION: + *to = INVALID_OPERATION; break; + case HStatus::TIMED_OUT: + *to = TIMED_OUT; break; + case HStatus::WOULD_BLOCK: + *to = WOULD_BLOCK; break; + case HStatus::UNKNOWN_ERROR: + *to = UNKNOWN_ERROR; break; + default: + *to = static_cast(from); + } + return true; +} + +// Fence +// ===== + +HFenceWrapper::HFenceWrapper(native_handle_t* h) : mHandle{h} { +} + +HFenceWrapper::~HFenceWrapper() { + native_handle_delete(mHandle); +} + +HFenceWrapper& HFenceWrapper::set(native_handle_t* h) { + native_handle_delete(mHandle); + mHandle = h; + return *this; +} + +HFenceWrapper& HFenceWrapper::operator=(native_handle_t* h) { + return set(h); +} + +hidl_handle HFenceWrapper::getHandle() const { + return hidl_handle{mHandle}; +} + +HFenceWrapper::operator hidl_handle() const { + return getHandle(); +} + +bool b2h(sp const& from, HFenceWrapper* to) { + if (!from) { + to->set(nullptr); + return true; + } + int fenceFd = from->get(); + if (fenceFd == -1) { + to->set(nullptr); + return true; + } + native_handle_t* nh = native_handle_create(1, 0); + if (!nh) { + return false; + } + nh->data[0] = fenceFd; + to->set(nh); + return true; +} + +bool h2b(native_handle_t const* from, sp* to) { + if (!from || from->numFds == 0) { + *to = new ::android::Fence(); + return true; + } + if (from->numFds != 1 || from->numInts != 0) { + return false; + } + *to = new BFence(dup(from->data[0])); + return true; +} + +// ConnectionType +// ============== + +bool b2h(int from, HConnectionType* to) { + *to = static_cast(from); + switch (from) { + case CURRENTLY_CONNECTED_API: + *to = HConnectionType::CURRENTLY_CONNECTED; break; + case NATIVE_WINDOW_API_EGL: + *to = HConnectionType::EGL; break; + case NATIVE_WINDOW_API_CPU: + *to = HConnectionType::CPU; break; + case NATIVE_WINDOW_API_MEDIA: + *to = HConnectionType::MEDIA; break; + case NATIVE_WINDOW_API_CAMERA: + *to = HConnectionType::CAMERA; break; + } + return true; +} + +bool h2b(HConnectionType from, int* to) { + *to = static_cast(from); + switch (from) { + case HConnectionType::CURRENTLY_CONNECTED: + *to = CURRENTLY_CONNECTED_API; break; + case HConnectionType::EGL: + *to = NATIVE_WINDOW_API_EGL; break; + case HConnectionType::CPU: + *to = NATIVE_WINDOW_API_CPU; break; + case HConnectionType::MEDIA: + *to = NATIVE_WINDOW_API_MEDIA; break; + case HConnectionType::CAMERA: + *to = NATIVE_WINDOW_API_CAMERA; break; + } + return true; +} + +// Rect +// ==== + +bool b2h(BRect const& from, HRect* to) { + BRect* dst = reinterpret_cast(to->data()); + dst->left = from.left; + dst->top = from.top; + dst->right = from.right; + dst->bottom = from.bottom; + return true; +} + +bool h2b(HRect const& from, BRect* to) { + BRect const* src = reinterpret_cast(from.data()); + to->left = src->left; + to->top = src->top; + to->right = src->right; + to->bottom = src->bottom; + return true; +} + +// Region +// ====== + +bool b2h(BRegion const& from, HRegion* to) { + size_t numRects; + BRect const* rectArray = from.getArray(&numRects); + to->resize(numRects); + for (size_t i = 0; i < numRects; ++i) { + if (!b2h(rectArray[i], &(*to)[i])) { + return false; + } + } + return true; +} + +bool h2b(HRegion const& from, BRegion* to) { + if (from.size() > 0) { + BRect bRect; + if (!h2b(from[0], &bRect)) { + return false; + } + to->set(bRect); + for (size_t i = 1; i < from.size(); ++i) { + if (!h2b(from[i], &bRect)) { + return false; + } + to->addRectUnchecked( + static_cast(bRect.left), + static_cast(bRect.top), + static_cast(bRect.right), + static_cast(bRect.bottom)); + } + } else { + to->clear(); + } + return true; +} + +// GraphicBuffer +// ============= + +// The handle is not cloned. Its lifetime is tied to the original GraphicBuffer. +bool b2h(sp const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber) { + if (!from) { + return false; + } + AHardwareBuffer* hwBuffer = from->toAHardwareBuffer(); + to->nativeHandle.setTo( + const_cast( + AHardwareBuffer_getNativeHandle(hwBuffer)), + false); + AHardwareBuffer_describe( + hwBuffer, + reinterpret_cast(to->description.data())); + if (toGenerationNumber) { + *toGenerationNumber = from->getGenerationNumber(); + } + return true; +} + +// The handle is cloned. +bool h2b(HardwareBuffer const& from, sp* to) { + AHardwareBuffer_Desc const* desc = + reinterpret_cast( + from.description.data()); + native_handle_t const* handle = from.nativeHandle; + AHardwareBuffer* hwBuffer; + if (AHardwareBuffer_createFromHandle( + desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, + &hwBuffer) != OK) { + return false; + } + *to = GraphicBuffer::fromAHardwareBuffer(hwBuffer); + AHardwareBuffer_release(hwBuffer); + return true; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index 35df6d7dde..9204bc3448 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -376,6 +377,12 @@ GraphicBufferSource::~GraphicBufferSource() { } } +sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer> +GraphicBufferSource::getHGraphicBufferProducer() const { + return new ::android::hardware::graphics::bufferqueue::V2_0::utils:: + B2HGraphicBufferProducer(getIGraphicBufferProducer()); +} + Status GraphicBufferSource::start() { Mutex::Autolock autoLock(mMutex); ALOGV("--> start; available=%zu, submittable=%zd", diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h index 99e444b910..bf329b9006 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h @@ -86,6 +86,11 @@ public: return mProducer; } + // Returns the handle to the bufferqueue HAL producer side of the BufferQueue. + // Buffers queued on this will be received by GraphicBufferSource. + sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer> + getHGraphicBufferProducer() const; + // This is called when component transitions to running state, which means // we can start handing it buffers. If we already have buffers of data // sitting in the BufferQueue, this will send them to the codec. -- GitLab From 672e68f03fc994f34f787813d7e6d936ae2f261b Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Tue, 16 Apr 2019 11:44:56 -0700 Subject: [PATCH 1289/1530] Remove MediaPlayer2 from media apex. Test: build, atest MediaSession2Test Bug: 130637522 Change-Id: Id096b344fcaddbc79b333ee53bb8da59100bcd13 --- apex/Android.bp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 0a9551d4d3..2cc6fcb854 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,7 +15,6 @@ apex_defaults { name: "com.android.media-defaults", java_libs: ["updatable-media"], - compile_multilib: "both", multilib: { first: { // Extractor process runs only with the primary ABI. @@ -33,12 +32,6 @@ apex_defaults { "libwavextractor", ], }, - both: { - native_shared_libs: [ - // MediaPlayer2 - "libmedia2_jni", - ], - }, }, key: "com.android.media.key", certificate: ":com.android.media.certificate", -- GitLab From b6578908df3c3f1730d286c1897ef9e9859e3495 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Tue, 16 Apr 2019 13:36:16 -0700 Subject: [PATCH 1290/1530] Camera: don't enable buffer manager for display/texture streams These streams have their own buffer management logic. Test: camera CTS tests Bug: 130050659 Change-Id: I6943b10d48a2e78fc8626d20c3f6bcc3327c936c --- .../camera/libcameraservice/device3/Camera3OutputStream.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 1c7758143e..acb8b3cac1 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -491,8 +491,11 @@ status_t Camera3OutputStream::configureConsumerQueueLocked() { * sets the mBufferManager if device version is > HAL3.2, which guarantees that the buffer * manager setup is skipped in below code. Note that HAL3.2 is also excluded here, as some * HAL3.2 devices may not support the dynamic buffer registeration. + * Also Camera3BufferManager does not support display/texture streams as they have its own + * buffer management logic. */ - if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID) { + if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID && + !(isConsumedByHWComposer() || isConsumedByHWTexture())) { uint64_t consumerUsage = 0; getEndpointUsage(&consumerUsage); StreamInfo streamInfo( -- GitLab From 4c7b61e7e26d69133800d4bc4191fa2c34c22926 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 11 Mar 2019 19:33:03 +0800 Subject: [PATCH 1291/1530] mkv support more audio codec mkv support audio codec as bellow: codecID: A_MPEG/L2 (mp2 audio) codecID: A_PCM/INT/LIT and A_PCM/INT/BIG (pcm audio) codecID: A_MS/ACM We can check the supported type in MKVWave2MIME function, including mp2, mp3, pcm, adpcm.wma Bug: 128470241 Test: play the files with new supported codecs and check if they can be played normally. Change-Id: Id92303b42ef763d1ce08a3c96265eda69622c79d (cherry picked from commit 7f20f58a4b4b38327092954a67ce25dd056d5c82) --- media/extractors/mkv/MatroskaExtractor.cpp | 100 +++++++++++++++++- media/libstagefright/Utils.cpp | 6 +- media/libstagefright/foundation/MediaDefs.cpp | 4 + .../media/stagefright/foundation/MediaDefs.h | 4 + 4 files changed, 108 insertions(+), 6 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index e284cdaa01..a1c81f3005 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -150,6 +150,7 @@ private: AAC, HEVC, MP3, + PCM, OTHER }; @@ -270,6 +271,8 @@ MatroskaSource::MatroskaSource( mType = AAC; } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { mType = MP3; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { + mType = PCM; } } @@ -1054,6 +1057,27 @@ media_status_t MatroskaSource::read( AMEDIAFORMAT_KEY_TARGET_TIME, targetSampleTimeUs); } + if (mType == PCM) { + int32_t bitPerFrame = 16; + int32_t bigEndian = 0; + AMediaFormat *meta = AMediaFormat_new(); + if (getFormat(meta) == AMEDIA_OK && meta != NULL) { + AMediaFormat_getInt32(meta, + AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &bitPerFrame); + AMediaFormat_getInt32(meta, + AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &bigEndian); + } + AMediaFormat_delete(meta); + if (bigEndian == 1 && bitPerFrame == 16) { + // Big-endian -> little-endian + uint16_t *dstData = (uint16_t *)frame->data() + frame->range_offset(); + uint16_t *srcData = (uint16_t *)frame->data() + frame->range_offset(); + for (size_t i = 0; i < frame->range_length() / 2; i++) { + dstData[i] = ntohs(srcData[i]); + } + } + } + *out = frame; return AMEDIA_OK; @@ -1170,6 +1194,51 @@ media_status_t MatroskaSource::read( //////////////////////////////////////////////////////////////////////////////// +enum WaveID { + MKV_RIFF_WAVE_FORMAT_PCM = 0x0001, + MKV_RIFF_WAVE_FORMAT_ADPCM_ms = 0x0002, + MKV_RIFF_WAVE_FORMAT_ADPCM_ima_wav = 0x0011, + MKV_RIFF_WAVE_FORMAT_MPEGL12 = 0x0050, + MKV_RIFF_WAVE_FORMAT_MPEGL3 = 0x0055, + MKV_RIFF_WAVE_FORMAT_WMAV1 = 0x0160, + MKV_RIFF_WAVE_FORMAT_WMAV2 = 0x0161, +}; + +static const char *MKVWave2MIME(uint16_t id) { + switch (id) { + case MKV_RIFF_WAVE_FORMAT_MPEGL12: + return MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II; + + case MKV_RIFF_WAVE_FORMAT_MPEGL3: + return MEDIA_MIMETYPE_AUDIO_MPEG; + + case MKV_RIFF_WAVE_FORMAT_PCM: + return MEDIA_MIMETYPE_AUDIO_RAW; + + case MKV_RIFF_WAVE_FORMAT_ADPCM_ms: + return MEDIA_MIMETYPE_AUDIO_MS_ADPCM; + case MKV_RIFF_WAVE_FORMAT_ADPCM_ima_wav: + return MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM; + + case MKV_RIFF_WAVE_FORMAT_WMAV1: + case MKV_RIFF_WAVE_FORMAT_WMAV2: + return MEDIA_MIMETYPE_AUDIO_WMA; + default: + ALOGW("unknown wave %x", id); + return ""; + }; +} + +static bool isMkvAudioCsdSizeOK(const char* mime, size_t csdSize) { + if ((!strcmp(mime, MEDIA_MIMETYPE_AUDIO_MS_ADPCM) && csdSize < 50) || + (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM) && csdSize < 20) || + (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_WMA) && csdSize < 28) || + (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) && csdSize < 30)) { + return false; + } + return true; +} + // trans all FOURCC to lower char static uint32_t FourCCtoLower(uint32_t fourcc) { uint8_t ch_1 = tolower((fourcc >> 24) & 0xff); @@ -2036,20 +2105,40 @@ void MatroskaExtractor::addTracks() { } else if (!strcmp("A_FLAC", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_FLAC); err = addFlacMetadata(meta, codecPrivate, codecPrivateSize); + } else if (!strcmp("A_MPEG/L2", codecID)) { + AMediaFormat_setString(meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II); + } else if (!strcmp("A_PCM/INT/LIT", codecID) || + !strcmp("A_PCM/INT/BIG", codecID)) { + AMediaFormat_setString(meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_RAW); + int32_t bigEndian = !strcmp("A_PCM/INT/BIG", codecID) ? 1: 0; + AMediaFormat_setInt32(meta, + AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, bigEndian); } else if ((!strcmp("A_MS/ACM", codecID))) { - if ((NULL == codecPrivate) || (codecPrivateSize < 30)) { + if ((NULL == codecPrivate) || (codecPrivateSize < 18)) { ALOGW("unsupported audio: A_MS/ACM has no valid private data: %s, size: %zu", codecPrivate == NULL ? "null" : "non-null", codecPrivateSize); continue; } else { uint16_t ID = *(uint16_t *)codecPrivate; - if (ID == 0x0055) { - AMediaFormat_setString(meta, - AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG); + const char* mime = MKVWave2MIME(ID); + ALOGV("A_MS/ACM type is %s", mime); + if (!strncasecmp("audio/", mime, 6) && + isMkvAudioCsdSizeOK(mime, codecPrivateSize)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, mime); } else { - ALOGW("A_MS/ACM unsupported type , continue"); + ALOGE("A_MS/ACM continue, unsupported audio type=%s, csdSize:%zu", + mime, codecPrivateSize); continue; } + if (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_WMA)) { + addESDSFromCodecPrivate(meta, true, codecPrivate, codecPrivateSize); + } else if (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_MS_ADPCM) || + !strcmp(mime, MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM)) { + uint32_t blockAlign = *(uint16_t*)(codecPrivate + 12); + addESDSFromCodecPrivate(meta, true, &blockAlign, sizeof(blockAlign)); + } } } else { ALOGW("%s is not supported.", codecID); @@ -2058,6 +2147,7 @@ void MatroskaExtractor::addTracks() { AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, atrack->GetSamplingRate()); AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, atrack->GetChannels()); + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, atrack->GetBitDepth()); break; } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index c7b27192bb..537e4c0817 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1756,7 +1756,11 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { size_t outsize = reassembleAVCC(csd0, csd1, avcc.data()); meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize); } - } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) { + } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || + mime == MEDIA_MIMETYPE_VIDEO_MPEG4 || + mime == MEDIA_MIMETYPE_AUDIO_WMA || + mime == MEDIA_MIMETYPE_AUDIO_MS_ADPCM || + mime == MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM) { std::vector esds(csd0size + 31); // The written ESDS is actually for an audio stream, but it's enough // for transporting the CSD to muxers. diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index 52b2765fb2..a08fed19a4 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -59,6 +59,10 @@ const char *MEDIA_MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc"; const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4"; const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; const char *MEDIA_MIMETYPE_AUDIO_ALAC = "audio/alac"; +const char *MEDIA_MIMETYPE_AUDIO_WMA = "audio/x-ms-wma"; +const char *MEDIA_MIMETYPE_AUDIO_MS_ADPCM = "audio/x-adpcm-ms"; +const char *MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM = "audio/x-adpcm-dvi-ima"; + const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index 007a09b581..1f9e636b8d 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -61,6 +61,10 @@ extern const char *MEDIA_MIMETYPE_AUDIO_EAC3_JOC; extern const char *MEDIA_MIMETYPE_AUDIO_AC4; extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED; extern const char *MEDIA_MIMETYPE_AUDIO_ALAC; +extern const char *MEDIA_MIMETYPE_AUDIO_WMA; +extern const char *MEDIA_MIMETYPE_AUDIO_MS_ADPCM; +extern const char *MEDIA_MIMETYPE_AUDIO_DVI_IMA_ADPCM; + extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; -- GitLab From b42f1ebb71e709fc4e035a88121ac2c1ac75271a Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Tue, 16 Apr 2019 14:45:13 -0700 Subject: [PATCH 1292/1530] Include calling uid proc state in CameraService reject log. Bug: 123762288 Test: builds. Change-Id: Ibaf080a7c60e52af014664f882cc37e0920353fe --- .../camera/libcameraservice/CameraService.cpp | 20 +++++++++++++++++-- .../camera/libcameraservice/CameraService.h | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 8354ed5f94..3b93a4d091 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -968,11 +968,14 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId, // Make sure the UID is in an active state to use the camera if (!mUidPolicy->isUidActive(callingUid, String16(clientName8))) { + int32_t procState = mUidPolicy->getProcState(callingUid); ALOGE("Access Denial: can't use the camera from an idle UID pid=%d, uid=%d", clientPid, clientUid); return STATUS_ERROR_FMT(ERROR_DISABLED, - "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" from background", - clientName8.string(), clientUid, clientPid, cameraId.string()); + "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" from background (" + "calling UID %d proc state %" PRId32 ")", + clientName8.string(), clientUid, clientPid, cameraId.string(), + callingUid, procState); } // If sensor privacy is enabled then prevent access to the camera @@ -2731,6 +2734,19 @@ bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid, String16 callingPack return active; } +int32_t CameraService::UidPolicy::getProcState(uid_t uid) { + Mutex::Autolock _l(mUidLock); + return getProcStateLocked(uid); +} + +int32_t CameraService::UidPolicy::getProcStateLocked(uid_t uid) { + int32_t procState = ActivityManager::PROCESS_STATE_UNKNOWN; + if (mMonitoredUids.find(uid) != mMonitoredUids.end()) { + procState = mMonitoredUids[uid].first; + } + return procState; +} + void CameraService::UidPolicy::UidPolicy::addOverrideUid(uid_t uid, String16 callingPackage, bool active) { updateOverrideUid(uid, callingPackage, active, true); diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 344dd92cfc..643b8cb130 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -542,6 +542,7 @@ private: void unregisterSelf(); bool isUidActive(uid_t uid, String16 callingPackage); + int32_t getProcState(uid_t uid); void onUidGone(uid_t uid, bool disabled); void onUidActive(uid_t uid); @@ -558,6 +559,7 @@ private: virtual void binderDied(const wp &who); private: bool isUidActiveLocked(uid_t uid, String16 callingPackage); + int32_t getProcStateLocked(uid_t uid); void updateOverrideUid(uid_t uid, String16 callingPackage, bool active, bool insert); Mutex mUidLock; -- GitLab From 4f6fa9daa4557de78789b937276e45bbaf0b019c Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Fri, 29 Mar 2019 10:40:35 -0700 Subject: [PATCH 1293/1530] Camera: Clarify multi-camera RAW and FOV behaviors - RAW capability can exist for multi-camera even if physical cameras are of different sizes/capabilities. - FOV for all processes streams must be the same regardless of logical or physical streams. - All metadata tags related to pixelArray/preCorrectionActiveArray/activeArray needs to be mapped properly by the camera HAL. - Do distortion correction mapping for physical subcamera as well. Test: Build and read docs, camera CTS, ITS. Bug: 118906351 Bug: 126220135 Change-Id: I29a61fc3a603561c1d74dc2261600ce4cd3d34cd --- .../include/camera/NdkCameraMetadataTags.h | 18 +++++++ .../device3/Camera3Device.cpp | 54 +++++++++++++++---- .../libcameraservice/device3/Camera3Device.h | 6 ++- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 320e7b3119..3ca0b8ea84 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7670,6 +7670,24 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { *
  • ACAMERA_LENS_POSE_REFERENCE
  • *
  • ACAMERA_LENS_DISTORTION
  • * + *

    The field of view of all non-RAW physical streams must be the same or as close as + * possible to that of non-RAW logical streams. If the requested FOV is outside of the + * range supported by the physical camera, the physical stream for that physical camera + * will use either the maximum or minimum scaler crop region, depending on which one is + * closer to the requested FOV. For example, for a logical camera with wide-tele lens + * configuration where the wide lens is the default, if the logical camera's crop region + * is set to maximum, the physical stream for the tele lens will be configured to its + * maximum crop region. On the other hand, if the logical camera has a normal-wide lens + * configuration where the normal lens is the default, when the logical camera's crop + * region is set to maximum, the FOV of the logical streams will be that of the normal + * lens. The FOV of the physical streams for the wide 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.

    *

    To maintain backward compatibility, the capture request and result metadata tags * required for basic camera functionalities will be solely based on the * logical camera capabiltity. Other request and result metadata tags, on the other diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index bc0dafefd5..54c26b13cf 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -132,13 +132,24 @@ status_t Camera3Device::initialize(sp manager, const Stri bool isLogical = manager->isLogicalCamera(mId.string(), &physicalCameraIds); if (isLogical) { for (auto& physicalId : physicalCameraIds) { - res = manager->getCameraCharacteristics(physicalId, &mPhysicalDeviceInfoMap[physicalId]); + res = manager->getCameraCharacteristics( + physicalId, &mPhysicalDeviceInfoMap[physicalId]); if (res != OK) { SET_ERR_L("Could not retrieve camera %s characteristics: %s (%d)", physicalId.c_str(), strerror(-res), res); session->close(); return res; } + + if (DistortionMapper::isDistortionSupported(mPhysicalDeviceInfoMap[physicalId])) { + mDistortionMappers[physicalId].setupStaticInfo(mPhysicalDeviceInfoMap[physicalId]); + if (res != OK) { + SET_ERR_L("Unable to read camera %s's calibration fields for distortion " + "correction", physicalId.c_str()); + session->close(); + return res; + } + } } } @@ -308,7 +319,7 @@ status_t Camera3Device::initializeCommonLocked() { } if (DistortionMapper::isDistortionSupported(mDeviceInfo)) { - res = mDistortionMapper.setupStaticInfo(mDeviceInfo); + res = mDistortionMappers[mId.c_str()].setupStaticInfo(mDeviceInfo); if (res != OK) { SET_ERR_L("Unable to read necessary calibration fields for distortion correction"); return res; @@ -3503,12 +3514,27 @@ void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata, } // Fix up some result metadata to account for HAL-level distortion correction - status_t res = mDistortionMapper.correctCaptureResult(&captureResult.mMetadata); + status_t res = + mDistortionMappers[mId.c_str()].correctCaptureResult(&captureResult.mMetadata); if (res != OK) { SET_ERR("Unable to correct capture result metadata for frame %d: %s (%d)", frameNumber, strerror(res), res); return; } + for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) { + String8 cameraId8(physicalMetadata.mPhysicalCameraId); + if (mDistortionMappers.find(cameraId8.c_str()) == mDistortionMappers.end()) { + continue; + } + res = mDistortionMappers[cameraId8.c_str()].correctCaptureResult( + &physicalMetadata.mPhysicalCameraMetadata); + if (res != OK) { + SET_ERR("Unable to correct physical capture result metadata for frame %d: %s (%d)", + frameNumber, strerror(res), res); + return; + } + } + // Fix up result metadata for monochrome camera. res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata); if (res != OK) { @@ -5481,13 +5507,21 @@ status_t Camera3Device::RequestThread::prepareHalRequests() { // Correct metadata regions for distortion correction if enabled sp parent = mParent.promote(); if (parent != nullptr) { - res = parent->mDistortionMapper.correctCaptureRequest( - &(captureRequest->mSettingsList.begin()->metadata)); - if (res != OK) { - SET_ERR("RequestThread: Unable to correct capture requests " - "for lens distortion for request %d: %s (%d)", - halRequest->frame_number, strerror(-res), res); - return INVALID_OPERATION; + List::iterator it; + for (it = captureRequest->mSettingsList.begin(); + it != captureRequest->mSettingsList.end(); it++) { + if (parent->mDistortionMappers.find(it->cameraId) == + parent->mDistortionMappers.end()) { + continue; + } + res = parent->mDistortionMappers[it->cameraId].correctCaptureRequest( + &(it->metadata)); + if (res != OK) { + SET_ERR("RequestThread: Unable to correct capture requests " + "for lens distortion for request %d: %s (%d)", + halRequest->frame_number, strerror(-res), res); + return INVALID_OPERATION; + } } } } diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index d3bb21228a..f8245df821 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -1250,8 +1250,10 @@ class Camera3Device : /** * Distortion correction support */ - - camera3::DistortionMapper mDistortionMapper; + // Map from camera IDs to its corresponding distortion mapper. Only contains + // 1 ID if the device isn't a logical multi-camera. Otherwise contains both + // logical camera and its physical subcameras. + std::unordered_map mDistortionMappers; // Debug tracker for metadata tag value changes // - Enabled with the -m option to dumpsys, such as -- GitLab From 0808b1745a73923f48be34aeb6acbad80adbeae4 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 16 Apr 2019 17:27:07 -0700 Subject: [PATCH 1294/1530] Fix cpu hint for sw codecs Add cpu hint for c2 sw codecs as well as omx. bug: 130669553 test: manually test that when playing YouTube 1080pHDR content, adb logcat shows traces from SchedulingPolicyService: SchedulingPolicyService: Moving xxxx to group 5 Change-Id: Ie7afeb207fc0a898669bc7b98b270982471e6b65 --- media/libstagefright/MediaCodec.cpp | 9 ++++++--- .../include/media/stagefright/MediaCodec.h | 1 + services/mediacodec/main_swcodecservice.cpp | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index a7d37e5970..d04b137fff 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1659,10 +1659,12 @@ void MediaCodec::requestCpuBoostIfNeeded() { return; } int32_t colorFormat; - if (mSoftRenderer != NULL - && mOutputFormat->contains("hdr-static-info") + if (mOutputFormat->contains("hdr-static-info") && mOutputFormat->findInt32("color-format", &colorFormat) - && (colorFormat == OMX_COLOR_FormatYUV420Planar16)) { + // check format for OMX only, for C2 the format is always opaque since the + // software rendering doesn't go through client + && ((mSoftRenderer != NULL && colorFormat == OMX_COLOR_FormatYUV420Planar16) + || mOwnerName.equalsIgnoreCase("codec2::software"))) { int32_t left, top, right, bottom, width, height; int64_t totalPixel = 0; if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) { @@ -1958,6 +1960,7 @@ void MediaCodec::onMessageReceived(const sp &msg) { } else { mFlags &= ~kFlagUsesSoftwareRenderer; } + mOwnerName = owner; MediaResource::Type resourceType; if (mComponentName.endsWith(".secure")) { diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index 7f6aae6281..6cd4265b4e 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -313,6 +313,7 @@ private: sp mCodecLooper; sp mCodec; AString mComponentName; + AString mOwnerName; sp mCodecInfo; sp mReplyID; uint32_t mFlags; diff --git a/services/mediacodec/main_swcodecservice.cpp b/services/mediacodec/main_swcodecservice.cpp index a5db031dc5..d91b7887a4 100644 --- a/services/mediacodec/main_swcodecservice.cpp +++ b/services/mediacodec/main_swcodecservice.cpp @@ -33,11 +33,12 @@ static const char kVendorSeccompPolicyPath[] = extern "C" void RegisterCodecServices(); -int main(int argc __unused, char** /*argv*/) +int main(int argc __unused, char** argv) { LOG(INFO) << "media swcodec service starting"; signal(SIGPIPE, SIG_IGN); SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath); + strcpy(argv[0], "media.swcodec"); ::android::hardware::configureRpcThreadpool(64, false); -- GitLab From 6c79632bbece71f7af0c2453bb62a5d6f0cca664 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 9 Apr 2019 14:13:17 -0700 Subject: [PATCH 1295/1530] refactor mutexes for audio effects in audio flinger and audio policy Remove effect specific mutex (mEffectLock) in AudioPolicyService: Due to concurrent capture (among other reasons), it is necessary that the audio policy manager state preserved by mLock includes audio effects registration and enabling. Moved all audio policy API calls from audio flinger out of locked regions for audio flinger, thread and effects mutexes to avoid cross deadlocks between audioflinger and audio policy manager: - centralized audio policy API calls in EffectModule::updatePolicyState() - the enabled state now reflects the state requested by the controlling handle, not the actual effect processing state: a suspended effect is now considered enabled. A new audio policy manager API moveEffectsToIo() is added to atomically handle moving effects to a new input or output without having to call unregister > register > enable sequence. Also fix assert in setStreamVolume to match volume group refactoring in audio policy manager. Bug: 128419018 Test: CTS tests for audio effects. Test: manual tests with Duo calls, Play Music, Youtube, notifications with and without Bluetooth and wired headset. Change-Id: I8bd3af81026c55b6be283b3a9b41fe4998e060fd --- media/libaudioclient/AudioSystem.cpp | 7 + media/libaudioclient/IAudioPolicyService.cpp | 43 ++++ .../include/media/AudioSystem.h | 1 + .../include/media/IAudioPolicyService.h | 1 + services/audioflinger/AudioFlinger.cpp | 220 ++++++++++++------ services/audioflinger/AudioFlinger.h | 10 +- services/audioflinger/Effects.cpp | 118 ++++++++-- services/audioflinger/Effects.h | 13 ++ services/audioflinger/Threads.cpp | 21 +- services/audioflinger/Threads.h | 4 +- services/audioflinger/Tracks.cpp | 59 ++--- services/audiopolicy/AudioPolicyInterface.h | 1 + .../include/EffectDescriptor.h | 5 + .../src/EffectDescriptor.cpp | 26 +++ .../managerdefault/AudioPolicyManager.cpp | 8 + .../managerdefault/AudioPolicyManager.h | 1 + .../audiopolicy/service/AudioPolicyEffects.h | 2 + .../service/AudioPolicyInterfaceImpl.cpp | 18 +- .../audiopolicy/service/AudioPolicyService.h | 4 +- 19 files changed, 399 insertions(+), 163 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index f3246695cb..de82d2bef4 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1067,6 +1067,13 @@ status_t AudioSystem::setEffectEnabled(int id, bool enabled) return aps->setEffectEnabled(id, enabled); } +status_t AudioSystem::moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->moveEffectsToIo(ids, io); +} + status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, uint32_t inPastMs) { const sp& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index bf98c60299..4a8bb522d2 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -103,6 +103,7 @@ enum { LIST_AUDIO_VOLUME_GROUPS, GET_VOLUME_GROUP_FOR_ATTRIBUTES, SET_ALLOWED_CAPTURE_POLICY, + MOVE_EFFECTS_TO_IO, }; #define MAX_ITEMS_PER_LIST 1024 @@ -550,6 +551,22 @@ public: return static_cast (reply.readInt32()); } + status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) override + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(ids.size()); + for (auto id : ids) { + data.writeInt32(id); + } + data.writeInt32(io); + status_t status = remote()->transact(MOVE_EFFECTS_TO_IO, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast (reply.readInt32()); + } + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { Parcel data, reply; @@ -1284,6 +1301,7 @@ status_t BnAudioPolicyService::onTransact( case GET_OUTPUT_FOR_ATTR: case ACQUIRE_SOUNDTRIGGER_SESSION: case RELEASE_SOUNDTRIGGER_SESSION: + case MOVE_EFFECTS_TO_IO: ALOGW("%s: transaction %d received from PID %d", __func__, code, IPCThreadState::self()->getCallingPid()); // return status only for non void methods @@ -1700,6 +1718,31 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } break; + case MOVE_EFFECTS_TO_IO: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + std::vector ids; + int32_t size; + status_t status = data.readInt32(&size); + if (status != NO_ERROR) { + return status; + } + if (size > MAX_ITEMS_PER_LIST) { + return BAD_VALUE; + } + for (int32_t i = 0; i < size; i++) { + int id; + status = data.readInt32(&id); + if (status != NO_ERROR) { + return status; + } + ids.push_back(id); + } + + audio_io_handle_t io = data.readInt32(); + reply->writeInt32(static_cast (moveEffectsToIo(ids, io))); + return NO_ERROR; + } break; + case IS_STREAM_ACTIVE: { CHECK_INTERFACE(IAudioPolicyService, data, reply); audio_stream_type_t stream = (audio_stream_type_t) data.readInt32(); diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 05a1d562db..d180bbc948 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -286,6 +286,7 @@ public: int id); static status_t unregisterEffect(int id); static status_t setEffectEnabled(int id, bool enabled); + static status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io); // clear stream to output mapping cache (gStreamOutputMap) // and output configuration cache (gOutputs) diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 95530acd13..11983d5149 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -114,6 +114,7 @@ public: int id) = 0; virtual status_t unregisterEffect(int id) = 0; virtual status_t setEffectEnabled(int id, bool enabled) = 0; + virtual status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) = 0; virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0; virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0825cb4c33..4b318166cb 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -687,6 +687,10 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, bool updatePid = (input.clientInfo.clientPid == -1); const uid_t callingUid = IPCThreadState::self()->getCallingUid(); uid_t clientUid = input.clientInfo.clientUid; + audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE; + std::vector effectIds; + + if (!isAudioServerOrMediaServerUid(callingUid)) { ALOGW_IF(clientUid != callingUid, "%s uid %d tried to pass itself off as %d", @@ -851,7 +855,10 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, // no risk of deadlock because AudioFlinger::mLock is held Mutex::Autolock _dl(thread->mLock); Mutex::Autolock _sl(effectThread->mLock); - moveEffectChain_l(sessionId, effectThread, thread, true); + if (moveEffectChain_l(sessionId, effectThread, thread) == NO_ERROR) { + effectThreadId = thread->id(); + effectIds = thread->getEffectIds_l(sessionId); + } } // Look for sync events awaiting for a session to be used. @@ -885,6 +892,12 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, goto Exit; } + // effectThreadId is not NONE if an effect chain corresponding to the track session + // was found on another thread and must be moved on this thread + if (effectThreadId != AUDIO_IO_HANDLE_NONE) { + AudioSystem::moveEffectsToIo(effectIds, effectThreadId); + } + // return handle to client trackHandle = new TrackHandle(track); @@ -1225,7 +1238,8 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, if (output == AUDIO_IO_HANDLE_NONE) { return BAD_VALUE; } - ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume"); + ALOG_ASSERT(stream != AUDIO_STREAM_PATCH || value == 1.0, + "attempt to change AUDIO_STREAM_PATCH volume"); AutoMutex lock(mLock); VolumeInterface *volumeInterface = getVolumeInterface_l(output); @@ -1630,30 +1644,36 @@ void AudioFlinger::registerClient(const sp& client) void AudioFlinger::removeNotificationClient(pid_t pid) { - Mutex::Autolock _l(mLock); + std::vector< sp > removedEffects; { - Mutex::Autolock _cl(mClientLock); - mNotificationClients.removeItem(pid); - } + Mutex::Autolock _l(mLock); + { + Mutex::Autolock _cl(mClientLock); + mNotificationClients.removeItem(pid); + } - ALOGV("%d died, releasing its sessions", pid); - size_t num = mAudioSessionRefs.size(); - bool removed = false; - for (size_t i = 0; i < num; ) { - AudioSessionRef *ref = mAudioSessionRefs.itemAt(i); - ALOGV(" pid %d @ %zu", ref->mPid, i); - if (ref->mPid == pid) { - ALOGV(" removing entry for pid %d session %d", pid, ref->mSessionid); - mAudioSessionRefs.removeAt(i); - delete ref; - removed = true; - num--; - } else { - i++; + ALOGV("%d died, releasing its sessions", pid); + size_t num = mAudioSessionRefs.size(); + bool removed = false; + for (size_t i = 0; i < num; ) { + AudioSessionRef *ref = mAudioSessionRefs.itemAt(i); + ALOGV(" pid %d @ %zu", ref->mPid, i); + if (ref->mPid == pid) { + ALOGV(" removing entry for pid %d session %d", pid, ref->mSessionid); + mAudioSessionRefs.removeAt(i); + delete ref; + removed = true; + num--; + } else { + i++; + } + } + if (removed) { + removedEffects = purgeStaleEffects_l(); } } - if (removed) { - purgeStaleEffects_l(); + for (auto& effect : removedEffects) { + effect->updatePolicyState(); } } @@ -2425,7 +2445,7 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) Vector< sp > effectChains = playbackThread->getEffectChains_l(); for (size_t i = 0; i < effectChains.size(); i ++) { moveEffectChain_l(effectChains[i]->sessionId(), playbackThread.get(), - dstThread, true); + dstThread); } } } @@ -2792,31 +2812,40 @@ void AudioFlinger::acquireAudioSessionId(audio_session_t audioSession, pid_t pid void AudioFlinger::releaseAudioSessionId(audio_session_t audioSession, pid_t pid) { - Mutex::Autolock _l(mLock); - pid_t caller = IPCThreadState::self()->getCallingPid(); - ALOGV("releasing %d from %d for %d", audioSession, caller, pid); - const uid_t callerUid = IPCThreadState::self()->getCallingUid(); - if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId() - caller = pid; - } - size_t num = mAudioSessionRefs.size(); - for (size_t i = 0; i < num; i++) { - AudioSessionRef *ref = mAudioSessionRefs.itemAt(i); - if (ref->mSessionid == audioSession && ref->mPid == caller) { - ref->mCnt--; - ALOGV(" decremented refcount to %d", ref->mCnt); - if (ref->mCnt == 0) { - mAudioSessionRefs.removeAt(i); - delete ref; - purgeStaleEffects_l(); + std::vector< sp > removedEffects; + { + Mutex::Autolock _l(mLock); + pid_t caller = IPCThreadState::self()->getCallingPid(); + ALOGV("releasing %d from %d for %d", audioSession, caller, pid); + const uid_t callerUid = IPCThreadState::self()->getCallingUid(); + if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId() + caller = pid; + } + size_t num = mAudioSessionRefs.size(); + for (size_t i = 0; i < num; i++) { + AudioSessionRef *ref = mAudioSessionRefs.itemAt(i); + if (ref->mSessionid == audioSession && ref->mPid == caller) { + ref->mCnt--; + ALOGV(" decremented refcount to %d", ref->mCnt); + if (ref->mCnt == 0) { + mAudioSessionRefs.removeAt(i); + delete ref; + std::vector< sp > effects = purgeStaleEffects_l(); + removedEffects.insert(removedEffects.end(), effects.begin(), effects.end()); + } + goto Exit; } - return; } + // If the caller is audioserver it is likely that the session being released was acquired + // on behalf of a process not in notification clients and we ignore the warning. + ALOGW_IF(!isAudioServerUid(callerUid), + "session id %d not found for pid %d", audioSession, caller); + } + +Exit: + for (auto& effect : removedEffects) { + effect->updatePolicyState(); } - // If the caller is audioserver it is likely that the session being released was acquired - // on behalf of a process not in notification clients and we ignore the warning. - ALOGW_IF(!isAudioServerUid(callerUid), - "session id %d not found for pid %d", audioSession, caller); } bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession) @@ -2831,11 +2860,12 @@ bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession) return false; } -void AudioFlinger::purgeStaleEffects_l() { +std::vector> AudioFlinger::purgeStaleEffects_l() { ALOGV("purging stale effects"); Vector< sp > chains; + std::vector< sp > removedEffects; for (size_t i = 0; i < mPlaybackThreads.size(); i++) { sp t = mPlaybackThreads.valueAt(i); @@ -2847,6 +2877,7 @@ void AudioFlinger::purgeStaleEffects_l() { } } } + for (size_t i = 0; i < mRecordThreads.size(); i++) { sp t = mRecordThreads.valueAt(i); Mutex::Autolock _l(t->mLock); @@ -2856,6 +2887,15 @@ void AudioFlinger::purgeStaleEffects_l() { } } + for (size_t i = 0; i < mMmapThreads.size(); i++) { + sp t = mMmapThreads.valueAt(i); + Mutex::Autolock _l(t->mLock); + for (size_t j = 0; j < t->mEffectChains.size(); j++) { + sp ec = t->mEffectChains[j]; + chains.push(ec); + } + } + for (size_t i = 0; i < chains.size(); i++) { sp ec = chains[i]; int sessionid = ec->sessionId(); @@ -2884,11 +2924,11 @@ void AudioFlinger::purgeStaleEffects_l() { if (effect->purgeHandles()) { t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId()); } - AudioSystem::unregisterEffect(effect->id()); + removedEffects.push_back(effect); } } } - return; + return removedEffects; } // dumpToThreadLog_l() must be called with AudioFlinger::mLock held @@ -3380,8 +3420,16 @@ sp AudioFlinger::createEffect( } } - if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) { - // handle must be cleared outside lock. + if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) { + // Check CPU and memory usage + sp effect = handle->effect().promote(); + if (effect != nullptr) { + status_t rStatus = effect->updatePolicyState(); + if (rStatus != NO_ERROR) { + lStatus = rStatus; + } + } + } else { handle.clear(); } @@ -3413,14 +3461,13 @@ status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t Mutex::Autolock _dl(dstThread->mLock); Mutex::Autolock _sl(srcThread->mLock); - return moveEffectChain_l(sessionId, srcThread, dstThread, false); + return moveEffectChain_l(sessionId, srcThread, dstThread); } // moveEffectChain_l must be called with both srcThread and dstThread mLocks held status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId, AudioFlinger::PlaybackThread *srcThread, - AudioFlinger::PlaybackThread *dstThread, - bool reRegister) + AudioFlinger::PlaybackThread *dstThread) { ALOGV("moveEffectChain_l() session %d from thread %p to thread %p", sessionId, srcThread, dstThread); @@ -3476,36 +3523,67 @@ status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId, } strategy = dstChain->strategy(); } - if (reRegister) { - AudioSystem::unregisterEffect(effect->id()); - AudioSystem::registerEffect(&effect->desc(), - dstThread->id(), - strategy, - sessionId, - effect->id()); - AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled()); - } effect = chain->getEffectFromId_l(0); } if (status != NO_ERROR) { for (size_t i = 0; i < removed.size(); i++) { srcThread->addEffect_l(removed[i]); - if (dstChain != 0 && reRegister) { - AudioSystem::unregisterEffect(removed[i]->id()); - AudioSystem::registerEffect(&removed[i]->desc(), - srcThread->id(), - strategy, - sessionId, - removed[i]->id()); - AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled()); - } } } return status; } +status_t AudioFlinger::moveAuxEffectToIo(int EffectId, + const sp& dstThread, + sp *srcThread) +{ + status_t status = NO_ERROR; + Mutex::Autolock _l(mLock); + sp thread = getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId); + + if (EffectId != 0 && thread != 0 && dstThread != thread.get()) { + Mutex::Autolock _dl(dstThread->mLock); + Mutex::Autolock _sl(thread->mLock); + sp srcChain = thread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX); + sp dstChain; + if (srcChain == 0) { + return INVALID_OPERATION; + } + + sp effect = srcChain->getEffectFromId_l(EffectId); + if (effect == 0) { + return INVALID_OPERATION; + } + thread->removeEffect_l(effect); + status = dstThread->addEffect_l(effect); + if (status != NO_ERROR) { + thread->addEffect_l(effect); + status = INVALID_OPERATION; + goto Exit; + } + + dstChain = effect->chain().promote(); + if (dstChain == 0) { + thread->addEffect_l(effect); + status = INVALID_OPERATION; + } + +Exit: + // removeEffect_l() has stopped the effect if it was active so it must be restarted + if (effect->state() == EffectModule::ACTIVE || + effect->state() == EffectModule::STOPPING) { + effect->start(); + } + } + + if (status == NO_ERROR && srcThread != nullptr) { + *srcThread = thread; + } + return status; +} + bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l() { if (mGlobalEffectEnableTime != 0 && diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 9960f0efc7..8bf89c8a63 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -369,7 +369,6 @@ private: AudioHwDevice* findSuitableHwDev_l(audio_module_handle_t module, audio_devices_t devices); - void purgeStaleEffects_l(); // Set kEnableExtendedChannels to true to enable greater than stereo output // for the MixerThread and device sink. Number of channels allowed is @@ -696,8 +695,11 @@ using effect_buffer_t = int16_t; status_t moveEffectChain_l(audio_session_t sessionId, PlaybackThread *srcThread, - PlaybackThread *dstThread, - bool reRegister); + PlaybackThread *dstThread); + + status_t moveAuxEffectToIo(int EffectId, + const sp& dstThread, + sp *srcThread); // return thread associated with primary hardware device, or NULL PlaybackThread *primaryPlaybackThread_l() const; @@ -732,6 +734,8 @@ using effect_buffer_t = int16_t; // Return true if the effect was found in mOrphanEffectChains, false otherwise. bool updateOrphanEffectChains(const sp& effect); + std::vector< sp > purgeStaleEffects_l(); + void broacastParametersToRecordThreads_l(const String8& keyValuePairs); void forwardParametersToDownstreamPatches_l( audio_io_handle_t upStream, const String8& keyValuePairs, diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 2b34267bf1..50ab634960 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -168,6 +168,68 @@ status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle) return status; } +status_t AudioFlinger::EffectModule::updatePolicyState() +{ + status_t status = NO_ERROR; + bool doRegister = false; + bool registered = false; + bool doEnable = false; + bool enabled = false; + audio_io_handle_t io; + uint32_t strategy; + + { + Mutex::Autolock _l(mLock); + // register effect when first handle is attached and unregister when last handle is removed + if (mPolicyRegistered != mHandles.size() > 0) { + doRegister = true; + mPolicyRegistered = mHandles.size() > 0; + if (mPolicyRegistered) { + sp chain = mChain.promote(); + sp thread = mThread.promote(); + + if (thread == nullptr || chain == nullptr) { + return INVALID_OPERATION; + } + io = thread->id(); + strategy = chain->strategy(); + } + } + // enable effect when registered according to enable state requested by controlling handle + if (mHandles.size() > 0) { + EffectHandle *handle = controlHandle_l(); + if (handle != nullptr && mPolicyEnabled != handle->enabled()) { + doEnable = true; + mPolicyEnabled = handle->enabled(); + } + } + registered = mPolicyRegistered; + enabled = mPolicyEnabled; + mPolicyLock.lock(); + } + ALOGV("%s name %s id %d session %d doRegister %d registered %d doEnable %d enabled %d", + __func__, mDescriptor.name, mId, mSessionId, doRegister, registered, doEnable, enabled); + if (doRegister) { + if (registered) { + status = AudioSystem::registerEffect( + &mDescriptor, + io, + strategy, + mSessionId, + mId); + } else { + status = AudioSystem::unregisterEffect(mId); + } + } + if (registered && doEnable) { + status = AudioSystem::setEffectEnabled(mId, enabled); + } + mPolicyLock.unlock(); + + return status; +} + + ssize_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle) { Mutex::Autolock _l(mLock); @@ -230,7 +292,6 @@ ssize_t AudioFlinger::EffectModule::disconnectHandle(EffectHandle *handle, bool Mutex::Autolock _l(mLock); ssize_t numHandles = removeHandle_l(handle); if ((numHandles == 0) && (!mPinned || unpinIfLast)) { - AudioSystem::unregisterEffect(mId); sp af = mAudioFlinger.promote(); if (af != 0) { mLock.unlock(); @@ -943,11 +1004,6 @@ status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled) ALOGV("setEnabled %p enabled %d", this, enabled); if (enabled != isEnabled()) { - status_t status = AudioSystem::setEffectEnabled(mId, enabled); - if (enabled && status != NO_ERROR) { - return status; - } - switch (mState) { // going from disabled to enabled case IDLE: @@ -1253,14 +1309,11 @@ bool AudioFlinger::EffectModule::purgeHandles() { bool enabled = false; Mutex::Autolock _l(mLock); - for (size_t i = 0; i < mHandles.size(); i++) { - EffectHandle *handle = mHandles[i]; - if (handle != NULL && !handle->disconnected()) { - if (handle->hasControl()) { - enabled = handle->enabled(); - } - } + EffectHandle *handle = controlHandle_l(); + if (handle != NULL) { + enabled = handle->enabled(); } + mHandles.clear(); return enabled; } @@ -1572,6 +1625,12 @@ status_t AudioFlinger::EffectHandle::enable() mEnabled = true; + status_t status = effect->updatePolicyState(); + if (status != NO_ERROR) { + mEnabled = false; + return status; + } + sp thread = effect->thread().promote(); if (thread != 0) { thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId()); @@ -1582,7 +1641,7 @@ status_t AudioFlinger::EffectHandle::enable() return NO_ERROR; } - status_t status = effect->setEnabled(true); + status = effect->setEnabled(true); if (status != NO_ERROR) { if (thread != 0) { thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId()); @@ -1625,6 +1684,8 @@ status_t AudioFlinger::EffectHandle::disable() } mEnabled = false; + effect->updatePolicyState(); + if (effect->suspended()) { return NO_ERROR; } @@ -1660,20 +1721,17 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast) return; } mDisconnected = true; - sp thread; { sp effect = mEffect.promote(); if (effect != 0) { - thread = effect->thread().promote(); - } - } - if (thread != 0) { - thread->disconnectEffectHandle(this, unpinIfLast); - } else { - // try to cleanup as much as we can - sp effect = mEffect.promote(); - if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) { - ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this); + sp thread = effect->thread().promote(); + if (thread != 0) { + thread->disconnectEffectHandle(this, unpinIfLast); + } else if (effect->disconnectHandle(this, unpinIfLast) > 0) { + ALOGW("%s Effect handle %p disconnected after thread destruction", + __func__, this); + } + effect->updatePolicyState(); } } @@ -1947,6 +2005,16 @@ sp AudioFlinger::EffectChain::getEffectFromType_l( return 0; } +std::vector AudioFlinger::EffectChain::getEffectIds() +{ + std::vector ids; + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mEffects.size(); i++) { + ids.push_back(mEffects[i]->id()); + } + return ids; +} + void AudioFlinger::EffectChain::clearInputBuffer() { Mutex::Autolock _l(mLock); diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index 58ce351997..220874dd62 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -142,6 +142,8 @@ public: void addEffectToHal_l(); void release_l(); + status_t updatePolicyState(); + void dump(int fd, const Vector& args); private: @@ -204,6 +206,16 @@ mutable Mutex mLock; // mutex for process, commands and handl static constexpr pid_t INVALID_PID = (pid_t)-1; // this tid is allowed to call setVolume() without acquiring the mutex. pid_t mSetVolumeReentrantTid = INVALID_PID; + + // Audio policy effect state management + // Mutex protecting transactions with audio policy manager as mLock cannot + // be held to avoid cross deadlocks with audio policy mutex + Mutex mPolicyLock; + // Effect is registered in APM or not + bool mPolicyRegistered = false; + // Effect enabled state communicated to APM. Enabled state corresponds to + // state requested by the EffectHandle with control + bool mPolicyEnabled = false; }; // The EffectHandle class implements the IEffect interface. It provides resources @@ -334,6 +346,7 @@ public: sp getEffectFromDesc_l(effect_descriptor_t *descriptor); sp getEffectFromId_l(int id); sp getEffectFromType_l(const effect_uuid_t *type); + std::vector getEffectIds(); // FIXME use float to improve the dynamic range bool setVolume_l(uint32_t *left, uint32_t *right, bool force = false); void resetVolume_l(); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 6fea8befbf..e71f0c6a96 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1306,7 +1306,6 @@ sp AudioFlinger::ThreadBase::createEffect_l( sp chain; bool chainCreated = false; bool effectCreated = false; - bool effectRegistered = false; audio_unique_id_t effectId = AUDIO_UNIQUE_ID_USE_UNSPECIFIED; lStatus = initCheck(); @@ -1342,13 +1341,6 @@ sp AudioFlinger::ThreadBase::createEffect_l( if (effect == 0) { effectId = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); - // Check CPU and memory usage - lStatus = AudioSystem::registerEffect( - desc, mId, chain->strategy(), sessionId, effectId); - if (lStatus != NO_ERROR) { - goto Exit; - } - effectRegistered = true; // create a new effect module if none present in the chain lStatus = chain->createEffect_l(effect, this, desc, effectId, sessionId, pinned); if (lStatus != NO_ERROR) { @@ -1378,9 +1370,6 @@ Exit: if (effectCreated) { chain->removeEffect_l(effect); } - if (effectRegistered) { - AudioSystem::unregisterEffect(effectId); - } if (chainCreated) { removeEffectChain_l(chain); } @@ -1411,7 +1400,6 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle, } if (remove) { mAudioFlinger->updateOrphanEffectChains(effect); - AudioSystem::unregisterEffect(effect->id()); if (handle->enabled()) { checkSuspendOnEffectEnabled(effect, false, effect->sessionId()); } @@ -1432,6 +1420,12 @@ sp AudioFlinger::ThreadBase::getEffect_l(audio_sessi return chain != 0 ? chain->getEffectFromId_l(effectId) : 0; } +std::vector AudioFlinger::ThreadBase::getEffectIds_l(audio_session_t sessionId) +{ + sp chain = getEffectChain_l(sessionId); + return chain != nullptr ? chain->getEffectIds() : std::vector{}; +} + // PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and // PlaybackThread::mLock held status_t AudioFlinger::ThreadBase::addEffect_l(const sp& effect) @@ -2732,7 +2726,8 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains Vector< sp > effectChains = mEffectChains; for (size_t i = 0; i < effectChains.size(); i ++) { - mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false); + mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), + this/* srcThread */, this/* dstThread */); } } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 18cb361d3b..37b2d08ca3 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -315,6 +315,7 @@ public: sp getEffectChain(audio_session_t sessionId); // same as getEffectChain() but must be called with ThreadBase mutex locked sp getEffectChain_l(audio_session_t sessionId) const; + std::vector getEffectIds_l(audio_session_t sessionId); // add an effect chain to the chain list (mEffectChains) virtual status_t addEffectChain_l(const sp& chain) = 0; // remove an effect chain from the chain list (mEffectChains) @@ -334,7 +335,8 @@ public: sp getEffect(audio_session_t sessionId, int effectId); sp getEffect_l(audio_session_t sessionId, int effectId); // add and effect module. Also creates the effect chain is none exists for - // the effects audio session + // the effects audio session. Only called in a context of moving an effect + // from one thread to another status_t addEffect_l(const sp< EffectModule>& effect); // remove and effect module. Also removes the effect chain is this was the last // effect diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index bbda17f94d..2ff80c65ea 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1244,54 +1244,25 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId) { - status_t status = DEAD_OBJECT; sp thread = mThread.promote(); - if (thread != 0) { - PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); - sp af = mClient->audioFlinger(); - - Mutex::Autolock _l(af->mLock); - - sp srcThread = af->getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId); + if (thread == nullptr) { + return DEAD_OBJECT; + } - if (EffectId != 0 && srcThread != 0 && playbackThread != srcThread.get()) { - Mutex::Autolock _dl(playbackThread->mLock); - Mutex::Autolock _sl(srcThread->mLock); - sp chain = srcThread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX); - if (chain == 0) { - return INVALID_OPERATION; - } + sp dstThread = (PlaybackThread *)thread.get(); + sp srcThread; // srcThread is initialized by call to moveAuxEffectToIo() + sp af = mClient->audioFlinger(); + status_t status = af->moveAuxEffectToIo(EffectId, dstThread, &srcThread); - sp effect = chain->getEffectFromId_l(EffectId); - if (effect == 0) { - return INVALID_OPERATION; - } - srcThread->removeEffect_l(effect); - status = playbackThread->addEffect_l(effect); - if (status != NO_ERROR) { - srcThread->addEffect_l(effect); - return INVALID_OPERATION; - } - // removeEffect_l() has stopped the effect if it was active so it must be restarted - if (effect->state() == EffectModule::ACTIVE || - effect->state() == EffectModule::STOPPING) { - effect->start(); - } - - sp dstChain = effect->chain().promote(); - if (dstChain == 0) { - srcThread->addEffect_l(effect); - return INVALID_OPERATION; - } - AudioSystem::unregisterEffect(effect->id()); - AudioSystem::registerEffect(&effect->desc(), - srcThread->id(), - dstChain->strategy(), - AUDIO_SESSION_OUTPUT_MIX, - effect->id()); - AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled()); + if (EffectId != 0 && status == NO_ERROR) { + status = dstThread->attachAuxEffect(this, EffectId); + if (status == NO_ERROR) { + AudioSystem::moveEffectsToIo(std::vector(EffectId), dstThread->id()); } - status = playbackThread->attachAuxEffect(this, EffectId); + } + + if (status != NO_ERROR && srcThread != nullptr) { + af->moveAuxEffectToIo(EffectId, srcThread, &dstThread); } return status; } diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index a2cf7aa9bb..caf0d7eb36 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -189,6 +189,7 @@ public: int id) = 0; virtual status_t unregisterEffect(int id) = 0; virtual status_t setEffectEnabled(int id, bool enabled) = 0; + virtual status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) = 0; virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0; virtual bool isStreamActiveRemotely(audio_stream_type_t stream, diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h index 7f01dc522d..c729ef9f28 100644 --- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h @@ -65,6 +65,11 @@ public: uint32_t getMaxEffectsMemory() const; bool isNonOffloadableEffectEnabled() const; + void moveEffects(audio_session_t session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput); + void moveEffects(const std::vector& ids, audio_io_handle_t dstOutput); + void dump(String8 *dst, int spaces = 0, bool verbose = true) const; private: diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp index 89f989943d..627fa8d842 100644 --- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp @@ -174,6 +174,32 @@ uint32_t EffectDescriptorCollection::getMaxEffectsMemory() const return MAX_EFFECTS_MEMORY; } +void EffectDescriptorCollection::moveEffects(audio_session_t session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput) +{ + ALOGV("%s session %d srcOutput %d dstOutput %d", __func__, session, srcOutput, dstOutput); + for (size_t i = 0; i < size(); i++) { + sp effect = valueAt(i); + if (effect->mSession == session && effect->mIo == srcOutput) { + effect->mIo = dstOutput; + } + } +} + +void EffectDescriptorCollection::moveEffects(const std::vector& ids, + audio_io_handle_t dstOutput) +{ + ALOGV("%s num effects %zu, first ID %d, dstOutput %d", + __func__, ids.size(), ids.size() ? ids[0] : 0, dstOutput); + for (size_t i = 0; i < size(); i++) { + sp effect = valueAt(i); + if (std::find(begin(ids), end(ids), effect->mId) != end(ids)) { + effect->mIo = dstOutput; + } + } +} + void EffectDescriptorCollection::dump(String8 *dst, int spaces, bool verbose) const { if (verbose) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index af29f87eda..d8fbc381fc 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2675,6 +2675,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForMusicEffects() } if (output != mMusicEffectOutput) { + mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output); mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output); mMusicEffectOutput = output; } @@ -2734,6 +2735,13 @@ status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) return status; } + +status_t AudioPolicyManager::moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) +{ + mEffects.moveEffects(ids, io); + return NO_ERROR; +} + bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { return mOutputs.isActive(toVolumeSource(stream), inPastMs); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index de447fbf57..fd8884181f 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -200,6 +200,7 @@ public: int id); virtual status_t unregisterEffect(int id); virtual status_t setEffectEnabled(int id, bool enabled); + status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) override; virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; // return whether a stream is playing remotely, override to change the definition of diff --git a/services/audiopolicy/service/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 6ad01f7611..dcf093b978 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h @@ -225,6 +225,8 @@ private: size_t *totSize); // protects access to mInputSources, mInputSessions, mOutputStreams, mOutputSessions + // never hold AudioPolicyService::mLock when calling AudioPolicyEffects methods as + // those can call back into AudioPolicyService methods and try to acquire the mutex Mutex mLock; // Automatic input effects are configured per audio_source_t KeyedVector< audio_source_t, EffectDescVector* > mInputSources; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 2eb272e111..2e47eb67cf 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -797,7 +797,7 @@ status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, if (mAudioPolicyManager == NULL) { return NO_INIT; } - Mutex::Autolock _l(mEffectsLock); + Mutex::Autolock _l(mLock); AutoCallerClear acc; return mAudioPolicyManager->registerEffect(desc, io, strategy, session, id); } @@ -807,7 +807,7 @@ status_t AudioPolicyService::unregisterEffect(int id) if (mAudioPolicyManager == NULL) { return NO_INIT; } - Mutex::Autolock _l(mEffectsLock); + Mutex::Autolock _l(mLock); AutoCallerClear acc; return mAudioPolicyManager->unregisterEffect(id); } @@ -817,11 +817,21 @@ status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) if (mAudioPolicyManager == NULL) { return NO_INIT; } - Mutex::Autolock _l(mEffectsLock); + Mutex::Autolock _l(mLock); AutoCallerClear acc; return mAudioPolicyManager->setEffectEnabled(id, enabled); } +status_t AudioPolicyService::moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + AutoCallerClear acc; + return mAudioPolicyManager->moveEffectsToIo(ids, io); +} + bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const { if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { @@ -973,8 +983,6 @@ bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) return false; } Mutex::Autolock _l(mLock); - Mutex::Autolock _le(mEffectsLock); // isOffloadSupported queries for - // non-offloadable effects AutoCallerClear acc; return mAudioPolicyManager->isOffloadSupported(info); } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 189322f594..08426496f7 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -134,6 +134,7 @@ public: int id); virtual status_t unregisterEffect(int id); virtual status_t setEffectEnabled(int id, bool enabled); + status_t moveEffectsToIo(const std::vector& ids, audio_io_handle_t io) override; virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; virtual bool isSourceActive(audio_source_t source) const; @@ -810,7 +811,6 @@ private: mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing // device connection state or routing - mutable Mutex mEffectsLock; // serialize access to Effect state within APM. // Note: lock acquisition order is always mLock > mEffectsLock: // mLock protects AudioPolicyManager methods that can call into audio flinger // and possibly back in to audio policy service and acquire mEffectsLock. @@ -824,6 +824,8 @@ private: DefaultKeyedVector< int64_t, sp > mNotificationClients; Mutex mNotificationClientsLock; // protects mNotificationClients // Manage all effects configured in audio_effects.conf + // never hold AudioPolicyService::mLock when calling AudioPolicyEffects methods as + // those can call back into AudioPolicyService methods and try to acquire the mutex sp mAudioPolicyEffects; audio_mode_t mPhoneState; -- GitLab From 29ccc29d482b6e4a2c46c2b8e731d44938888e32 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 15 Apr 2019 08:58:08 -0700 Subject: [PATCH 1296/1530] aaudio: reduce error logging, improve security Reduce log spam and error logging as part of the Green Log effort. Replace logging of addresses with integer IDs. Bug: 36785118 Bug: 122073229 Bug: 130569151 Test: Open an AAudio stream at 44100 and look at logcat. Test: adb shell write_sine_callback -pl -r44100 Test: adb logcat | grep -i aaudio | grep " E " Test: Should see no "E" logs from AAudio. Test: Should see no addresses from AAudio. Change-Id: I1d4e0bc349095f818fad52603d2bb8e7317d3681 Merged-In: I1d4e0bc349095f818fad52603d2bb8e7317d3681 (cherry picked from commit 7ba46558fbd28b555ba751837cc9094a7c785f75) --- .../src/binding/AAudioBinderClient.cpp | 3 +- .../src/binding/SharedMemoryParcelable.cpp | 1 - .../libaaudio/src/client/AAudioFlowGraph.cpp | 2 +- .../src/client/AudioStreamInternal.cpp | 22 ++++----- .../src/client/AudioStreamInternalPlay.cpp | 4 +- media/libaaudio/src/core/AAudioAudio.cpp | 30 ++++++------ .../src/core/AAudioStreamParameters.cpp | 22 ++++----- media/libaaudio/src/core/AudioStream.cpp | 47 +++++-------------- media/libaaudio/src/core/AudioStream.h | 9 ++++ .../libaaudio/src/core/AudioStreamBuilder.cpp | 40 ++++++++++++++++ media/libaaudio/src/core/AudioStreamBuilder.h | 2 + .../src/legacy/AudioStreamLegacy.cpp | 4 +- services/oboeservice/AAudioClientTracker.cpp | 12 ++--- services/oboeservice/AAudioService.cpp | 3 +- .../oboeservice/AAudioServiceEndpoint.cpp | 3 +- services/oboeservice/AAudioServiceEndpoint.h | 2 +- .../oboeservice/AAudioServiceEndpointMMAP.cpp | 34 ++++++-------- .../oboeservice/AAudioServiceEndpointPlay.cpp | 1 - .../oboeservice/AAudioServiceStreamShared.cpp | 15 +++--- 19 files changed, 135 insertions(+), 121 deletions(-) diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp index dd620e3e0a..7b0d31f88a 100644 --- a/media/libaaudio/src/binding/AAudioBinderClient.cpp +++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp @@ -97,8 +97,7 @@ const sp AAudioBinderClient::getAAudioService() { status_t status = binder->linkToDeath(mAAudioClient); // TODO review what we should do if this fails if (status != NO_ERROR) { - ALOGE("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d", - mAAudioClient.get(), status); + ALOGE("%s() - linkToDeath() returned %d", __func__, status); } mAAudioService = interface_cast(binder); needToRegister = true; diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp index 67955e8ffe..b6e8472703 100644 --- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp +++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp @@ -157,5 +157,4 @@ aaudio_result_t SharedMemoryParcelable::validate() const { void SharedMemoryParcelable::dump() { ALOGD("mFd = %d", mFd.get()); ALOGD("mSizeInBytes = %d", mSizeInBytes); - ALOGD("mResolvedAddress = %p", mResolvedAddress); } diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp index 3e43c6b609..8f2c4886ef 100644 --- a/media/libaaudio/src/client/AAudioFlowGraph.cpp +++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp @@ -38,7 +38,7 @@ aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat, int32_t sinkChannelCount) { AudioFloatOutputPort *lastOutput = nullptr; - ALOGD("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d", + ALOGV("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d", __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount); switch (sourceFormat) { diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index ec270f3745..db98d58b3b 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -195,13 +195,13 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { if (isDataCallbackSet()) { mCallbackFrames = builder.getFramesPerDataCallback(); if (mCallbackFrames > getBufferCapacity() / 2) { - ALOGE("%s - framesPerCallback too big = %d, capacity = %d", + ALOGW("%s - framesPerCallback too big = %d, capacity = %d", __func__, mCallbackFrames, getBufferCapacity()); result = AAUDIO_ERROR_OUT_OF_RANGE; goto error; } else if (mCallbackFrames < 0) { - ALOGE("%s - framesPerCallback negative", __func__); + ALOGW("%s - framesPerCallback negative", __func__); result = AAUDIO_ERROR_OUT_OF_RANGE; goto error; @@ -225,7 +225,7 @@ error: aaudio_result_t AudioStreamInternal::close() { aaudio_result_t result = AAUDIO_OK; - ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle); + ALOGV("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle); if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) { // Don't close a stream while it is running. aaudio_stream_state_t currentState = getState(); @@ -236,7 +236,7 @@ aaudio_result_t AudioStreamInternal::close() { result = waitForStateChange(currentState, &nextState, timeoutNanoseconds); if (result != AAUDIO_OK) { - ALOGE("%s() waitForStateChange() returned %d %s", + ALOGW("%s() waitForStateChange() returned %d %s", __func__, result, AAudio_convertResultToText(result)); } } @@ -283,17 +283,17 @@ aaudio_result_t AudioStreamInternal::requestStart() { int64_t startTime; if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("requestStart() mServiceStreamHandle invalid"); + ALOGD("requestStart() mServiceStreamHandle invalid"); return AAUDIO_ERROR_INVALID_STATE; } if (isActive()) { - ALOGE("requestStart() already active"); + ALOGD("requestStart() already active"); return AAUDIO_ERROR_INVALID_STATE; } aaudio_stream_state_t originalState = getState(); if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) { - ALOGE("requestStart() but DISCONNECTED"); + ALOGD("requestStart() but DISCONNECTED"); return AAUDIO_ERROR_DISCONNECTED; } setState(AAUDIO_STREAM_STATE_STARTING); @@ -356,8 +356,8 @@ aaudio_result_t AudioStreamInternal::requestStop() { } if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X", - mServiceStreamHandle); + ALOGW("%s() mServiceStreamHandle invalid = 0x%08X", + __func__, mServiceStreamHandle); return AAUDIO_ERROR_INVALID_STATE; } @@ -370,7 +370,7 @@ aaudio_result_t AudioStreamInternal::requestStop() { aaudio_result_t AudioStreamInternal::registerThread() { if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("registerThread() mServiceStreamHandle invalid"); + ALOGW("%s() mServiceStreamHandle invalid", __func__); return AAUDIO_ERROR_INVALID_STATE; } return mServiceInterface.registerAudioThread(mServiceStreamHandle, @@ -380,7 +380,7 @@ aaudio_result_t AudioStreamInternal::registerThread() { aaudio_result_t AudioStreamInternal::unregisterThread() { if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("unregisterThread() mServiceStreamHandle invalid"); + ALOGW("%s() mServiceStreamHandle invalid", __func__); return AAUDIO_ERROR_INVALID_STATE; } return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid()); diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index e1443d913f..164ad2b786 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -65,7 +65,7 @@ aaudio_result_t AudioStreamInternalPlay::requestPause() return result; } if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("%s() mServiceStreamHandle invalid", __func__); + ALOGW("%s() mServiceStreamHandle invalid", __func__); return AAUDIO_ERROR_INVALID_STATE; } @@ -77,7 +77,7 @@ aaudio_result_t AudioStreamInternalPlay::requestPause() aaudio_result_t AudioStreamInternalPlay::requestFlush() { if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { - ALOGE("%s() mServiceStreamHandle invalid", __func__); + ALOGW("%s() mServiceStreamHandle invalid", __func__); return AAUDIO_ERROR_INVALID_STATE; } diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp index 8ae2644a4e..44d5122db6 100644 --- a/media/libaaudio/src/core/AAudioAudio.cpp +++ b/media/libaaudio/src/core/AAudioAudio.cpp @@ -246,18 +246,20 @@ AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* AAudioStream** streamPtr) { AudioStream *audioStream = nullptr; + aaudio_stream_id_t id = 0; // Please leave these logs because they are very helpful when debugging. - ALOGD("%s() called ----------------------------------------", __func__); + ALOGI("%s() called ----------------------------------------", __func__); AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr); aaudio_result_t result = streamBuilder->build(&audioStream); - ALOGD("AAudioStreamBuilder_openStream() returns %d = %s for (%p) ----------------", - result, AAudio_convertResultToText(result), audioStream); if (result == AAUDIO_OK) { audioStream->registerPlayerBase(); *streamPtr = (AAudioStream*) audioStream; + id = audioStream->getId(); } else { *streamPtr = nullptr; } + ALOGI("%s() returns %d = %s for s#%u ----------------", + __func__, result, AAudio_convertResultToText(result), id); return result; } @@ -275,8 +277,9 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) { aaudio_result_t result = AAUDIO_ERROR_NULL; AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("%s(%p) called ---------------", __func__, stream); if (audioStream != nullptr) { + aaudio_stream_id_t id = audioStream->getId(); + ALOGD("%s(s#%u) called ---------------", __func__, id); result = audioStream->safeClose(); // Close will only fail if called illegally, for example, from a callback. // That would result in deleting an active stream, which would cause a crash. @@ -286,42 +289,39 @@ AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream) } else { ALOGW("%s attempt to close failed. Close it from another thread.", __func__); } + ALOGD("%s(s#%u) returned %d ---------", __func__, id, result); } - // We're potentially freeing `stream` above, so its use here makes some - // static analysis tools unhappy. Casting to uintptr_t helps assure - // said tools that we're not doing anything bad here. - ALOGD("%s(%#" PRIxPTR ") returned %d ---------", __func__, - reinterpret_cast(stream), result); return result; } AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("%s(%p) called --------------", __func__, stream); + aaudio_stream_id_t id = audioStream->getId(); + ALOGD("%s(s#%u) called --------------", __func__, id); aaudio_result_t result = audioStream->systemStart(); - ALOGD("%s(%p) returned %d ---------", __func__, stream, result); + ALOGD("%s(s#%u) returned %d ---------", __func__, id, result); return result; } AAUDIO_API aaudio_result_t AAudioStream_requestPause(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("%s(%p) called", __func__, stream); + ALOGD("%s(s#%u) called", __func__, audioStream->getId()); return audioStream->systemPause(); } AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("%s(%p) called", __func__, stream); + ALOGD("%s(s#%u) called", __func__, audioStream->getId()); return audioStream->safeFlush(); } AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream) { AudioStream *audioStream = convertAAudioStreamToAudioStream(stream); - ALOGD("%s(%p) called", __func__, stream); + ALOGD("%s(s#%u) called", __func__, audioStream->getId()); return audioStream->systemStopFromApp(); } @@ -371,7 +371,7 @@ AAUDIO_API aaudio_result_t AAudioStream_write(AAudioStream* stream, // Don't allow writes when playing with a callback. if (audioStream->isDataCallbackActive()) { - ALOGE("Cannot write to a callback stream when running."); + ALOGD("Cannot write to a callback stream when running."); return AAUDIO_ERROR_INVALID_STATE; } diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp index e5bda30864..c9711dab8a 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.cpp +++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp @@ -56,7 +56,7 @@ static aaudio_result_t isFormatValid(audio_format_t format) { case AUDIO_FORMAT_PCM_FLOAT: break; // valid default: - ALOGE("audioFormat not valid, audio_format_t = 0x%08x", format); + ALOGD("audioFormat not valid, audio_format_t = 0x%08x", format); return AAUDIO_ERROR_INVALID_FORMAT; // break; } @@ -66,12 +66,12 @@ static aaudio_result_t isFormatValid(audio_format_t format) { aaudio_result_t AAudioStreamParameters::validate() const { if (mSamplesPerFrame != AAUDIO_UNSPECIFIED && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) { - ALOGE("channelCount out of range = %d", mSamplesPerFrame); + ALOGD("channelCount out of range = %d", mSamplesPerFrame); return AAUDIO_ERROR_OUT_OF_RANGE; } if (mDeviceId < 0) { - ALOGE("deviceId out of range = %d", mDeviceId); + ALOGD("deviceId out of range = %d", mDeviceId); return AAUDIO_ERROR_OUT_OF_RANGE; } @@ -89,7 +89,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_SHARING_MODE_SHARED: break; default: - ALOGE("illegal sharingMode = %d", mSharingMode); + ALOGD("illegal sharingMode = %d", mSharingMode); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } @@ -99,12 +99,12 @@ aaudio_result_t AAudioStreamParameters::validate() const { if (mSampleRate != AAUDIO_UNSPECIFIED && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) { - ALOGE("sampleRate out of range = %d", mSampleRate); + ALOGD("sampleRate out of range = %d", mSampleRate); return AAUDIO_ERROR_INVALID_RATE; } if (mBufferCapacity < 0) { - ALOGE("bufferCapacity out of range = %d", mBufferCapacity); + ALOGD("bufferCapacity out of range = %d", mBufferCapacity); return AAUDIO_ERROR_OUT_OF_RANGE; } @@ -113,7 +113,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_DIRECTION_OUTPUT: break; // valid default: - ALOGE("direction not valid = %d", mDirection); + ALOGD("direction not valid = %d", mDirection); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } @@ -134,7 +134,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_USAGE_ASSISTANT: break; // valid default: - ALOGE("usage not valid = %d", mUsage); + ALOGD("usage not valid = %d", mUsage); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } @@ -147,7 +147,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_CONTENT_TYPE_SPEECH: break; // valid default: - ALOGE("content type not valid = %d", mContentType); + ALOGD("content type not valid = %d", mContentType); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } @@ -162,7 +162,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE: break; // valid default: - ALOGE("input preset not valid = %d", mInputPreset); + ALOGD("input preset not valid = %d", mInputPreset); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } @@ -174,7 +174,7 @@ aaudio_result_t AAudioStreamParameters::validate() const { case AAUDIO_ALLOW_CAPTURE_BY_NONE: break; // valid default: - ALOGE("allowed capture policy not valid = %d", mAllowedCapturePolicy); + ALOGD("allowed capture policy not valid = %d", mAllowedCapturePolicy); return AAUDIO_ERROR_ILLEGAL_ARGUMENT; // break; } diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp index 732d45c96f..25669bed74 100644 --- a/media/libaaudio/src/core/AudioStream.cpp +++ b/media/libaaudio/src/core/AudioStream.cpp @@ -28,9 +28,17 @@ using namespace aaudio; + +// Sequential number assigned to streams solely for debugging purposes. +static aaudio_stream_id_t AAudio_getNextStreamId() { + static std::atomic nextStreamId{1}; + return nextStreamId++; +} + AudioStream::AudioStream() : mPlayerBase(new MyPlayerBase(this)) -{ + , mStreamId(AAudio_getNextStreamId()) + { // mThread is a pthread_t of unknown size so we need memset. memset(&mThread, 0, sizeof(mThread)); setPeriodNanoseconds(0); @@ -48,22 +56,6 @@ AudioStream::~AudioStream() { mPlayerBase->clearParentReference(); // remove reference to this AudioStream } -static const char *AudioStream_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) { - const char *result; - switch (sharingMode) { - case AAUDIO_SHARING_MODE_EXCLUSIVE: - result = "EX"; - break; - case AAUDIO_SHARING_MODE_SHARED: - result = "SH"; - break; - default: - result = "?!"; - break; - } - return result; -} - aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) { // Call here as well because the AAudioService will call this without calling build(). @@ -106,20 +98,6 @@ aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) mDataCallbackUserData = builder.getDataCallbackUserData(); mErrorCallbackUserData = builder.getErrorCallbackUserData(); - // This is very helpful for debugging in the future. Please leave it in. - ALOGI("open() rate = %d, channels = %d, format = %d, sharing = %s, dir = %s", - mSampleRate, mSamplesPerFrame, mFormat, - AudioStream_convertSharingModeToShortText(mSharingMode), - (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "OUTPUT" : "INPUT"); - ALOGI("open() device = %d, sessionId = %d, perfMode = %d, callback: %s with frames = %d", - mDeviceId, - mSessionId, - mPerformanceMode, - (isDataCallbackSet() ? "ON" : "OFF"), - mFramesPerDataCallback); - ALOGI("open() usage = %d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d", - mUsage, mContentType, mInputPreset, mAllowedCapturePolicy); - return AAUDIO_OK; } @@ -278,15 +256,15 @@ aaudio_result_t AudioStream::safeClose() { } void AudioStream::setState(aaudio_stream_state_t state) { - ALOGV("%s(%p) from %d to %d", __func__, this, mState, state); + ALOGV("%s(%d) from %d to %d", __func__, getId(), mState, state); // CLOSED is a final state if (mState == AAUDIO_STREAM_STATE_CLOSED) { - ALOGE("%s(%p) tried to set to %d but already CLOSED", __func__, this, state); + ALOGE("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state); // Once DISCONNECTED, we can only move to CLOSED state. } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED && state != AAUDIO_STREAM_STATE_CLOSED) { - ALOGE("%s(%p) tried to set to %d but already DISCONNECTED", __func__, this, state); + ALOGE("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state); } else { mState = state; @@ -485,7 +463,6 @@ AudioStream::MyPlayerBase::MyPlayerBase(AudioStream *parent) : mParent(parent) { } AudioStream::MyPlayerBase::~MyPlayerBase() { - ALOGV("MyPlayerBase::~MyPlayerBase(%p) deleted", this); } void AudioStream::MyPlayerBase::registerWithAudioManager() { diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h index 32713b1f41..044c979f6a 100644 --- a/media/libaaudio/src/core/AudioStream.h +++ b/media/libaaudio/src/core/AudioStream.h @@ -36,6 +36,7 @@ namespace aaudio { typedef void *(*aaudio_audio_thread_proc_t)(void *); +typedef uint32_t aaudio_stream_id_t; class AudioStreamBuilder; @@ -121,6 +122,12 @@ public: return AAUDIO_OK; } + // This is only used to identify a stream in the logs without + // revealing any pointers. + aaudio_stream_id_t getId() { + return mStreamId; + } + virtual aaudio_result_t setBufferSize(int32_t requestedFrames) = 0; virtual aaudio_result_t createThread(int64_t periodNanoseconds, @@ -587,6 +594,8 @@ private: void *mThreadArg = nullptr; aaudio_result_t mThreadRegistrationResult = AAUDIO_OK; + const aaudio_stream_id_t mStreamId; + }; } /* namespace aaudio */ diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp index 4ef765d7ba..08f4958c0a 100644 --- a/media/libaaudio/src/core/AudioStreamBuilder.cpp +++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp @@ -104,6 +104,8 @@ aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) { } *streamPtr = nullptr; + logParameters(); + aaudio_result_t result = validate(); if (result != AAUDIO_OK) { return result; @@ -217,3 +219,41 @@ aaudio_result_t AudioStreamBuilder::validate() const { return AAUDIO_OK; } + +static const char *AAudio_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) { + switch (sharingMode) { + case AAUDIO_SHARING_MODE_EXCLUSIVE: + return "EX"; + case AAUDIO_SHARING_MODE_SHARED: + return "SH"; + default: + return "?!"; + } +} + +static const char *AAudio_convertDirectionToText(aaudio_direction_t direction) { + switch (direction) { + case AAUDIO_DIRECTION_OUTPUT: + return "OUTPUT"; + case AAUDIO_DIRECTION_INPUT: + return "INPUT"; + default: + return "?!"; + } +} + +void AudioStreamBuilder::logParameters() const { + // This is very helpful for debugging in the future. Please leave it in. + ALOGI("rate = %6d, channels = %d, format = %d, sharing = %s, dir = %s", + getSampleRate(), getSamplesPerFrame(), getFormat(), + AAudio_convertSharingModeToShortText(getSharingMode()), + AAudio_convertDirectionToText(getDirection())); + ALOGI("device = %6d, sessionId = %d, perfMode = %d, callback: %s with frames = %d", + getDeviceId(), + getSessionId(), + getPerformanceMode(), + ((getDataCallbackProc() != nullptr) ? "ON" : "OFF"), + mFramesPerDataCallback); + ALOGI("usage = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d", + getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy()); +} diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h index a43cfa8918..8149af24c7 100644 --- a/media/libaaudio/src/core/AudioStreamBuilder.h +++ b/media/libaaudio/src/core/AudioStreamBuilder.h @@ -102,6 +102,8 @@ public: virtual aaudio_result_t validate() const override; + void logParameters() const; + private: bool mSharingModeMatchRequired = false; // must match sharing mode requested aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE; diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp index 2edab58321..91d2eff7cc 100644 --- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp +++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp @@ -206,7 +206,9 @@ aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId, void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId) { - ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId); + // Device routing is a common source of errors and DISCONNECTS. + // Please leave this log in place. + ALOGD("%s() devId %d => %d", __func__, (int) getDeviceId(), (int)deviceId); if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId && getState() != AAUDIO_STREAM_STATE_DISCONNECTED) { // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING. diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp index 7264a9bd25..83704ba83f 100644 --- a/services/oboeservice/AAudioClientTracker.cpp +++ b/services/oboeservice/AAudioClientTracker.cpp @@ -101,7 +101,7 @@ int32_t AAudioClientTracker::getStreamCount(pid_t pid) { aaudio_result_t AAudioClientTracker::registerClientStream(pid_t pid, sp serviceStream) { aaudio_result_t result = AAUDIO_OK; - ALOGV("registerClientStream(%d, %p)\n", pid, serviceStream.get()); + ALOGV("registerClientStream(%d,)\n", pid); std::lock_guard lock(mLock); sp notificationClient = mNotificationClients[pid]; if (notificationClient == 0) { @@ -118,27 +118,23 @@ AAudioClientTracker::registerClientStream(pid_t pid, sp aaudio_result_t AAudioClientTracker::unregisterClientStream(pid_t pid, sp serviceStream) { - ALOGV("unregisterClientStream(%d, %p)\n", pid, serviceStream.get()); + ALOGV("unregisterClientStream(%d,)\n", pid); std::lock_guard lock(mLock); auto it = mNotificationClients.find(pid); if (it != mNotificationClients.end()) { - ALOGV("unregisterClientStream(%d, %p) found NotificationClient\n", - pid, serviceStream.get()); + ALOGV("unregisterClientStream(%d,) found NotificationClient\n", pid); it->second->unregisterClientStream(serviceStream); } else { - ALOGE("unregisterClientStream(%d, %p) missing NotificationClient\n", - pid, serviceStream.get()); + ALOGE("unregisterClientStream(%d,) missing NotificationClient\n", pid); } return AAUDIO_OK; } AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid) : mProcessId(pid) { - //ALOGD("NotificationClient(%d) created %p\n", pid, this); } AAudioClientTracker::NotificationClient::~NotificationClient() { - //ALOGD("~NotificationClient() destroyed %p\n", this); } int32_t AAudioClientTracker::NotificationClient::getStreamCount() { diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp index 2fbaeb453e..e6a83758ff 100644 --- a/services/oboeservice/AAudioService.cpp +++ b/services/oboeservice/AAudioService.cpp @@ -132,11 +132,10 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req if (result != AAUDIO_OK) { serviceStream.clear(); - ALOGW("openStream(): failed, return %d = %s", result, AAudio_convertResultToText(result)); return result; } else { aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get()); - ALOGD("openStream(): handle = 0x%08X", handle); + ALOGV("openStream(): handle = 0x%08X", handle); serviceStream->setHandle(handle); pid_t pid = request.getProcessId(); AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream); diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp index 4dfb62a345..553754edee 100644 --- a/services/oboeservice/AAudioServiceEndpoint.cpp +++ b/services/oboeservice/AAudioServiceEndpoint.cpp @@ -90,7 +90,8 @@ void AAudioServiceEndpoint::disconnectRegisteredStreams() { std::lock_guard lock(mLockStreams); mConnected.store(false); for (const auto& stream : mRegisteredStreams) { - ALOGD("disconnectRegisteredStreams() stop and disconnect %p", stream.get()); + ALOGD("disconnectRegisteredStreams() stop and disconnect port %d", + stream->getPortHandle()); stream->stop(); stream->disconnect(); } diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h index 3616fa2949..a2f66a50f5 100644 --- a/services/oboeservice/AAudioServiceEndpoint.h +++ b/services/oboeservice/AAudioServiceEndpoint.h @@ -61,7 +61,7 @@ public: virtual aaudio_result_t startClient(const android::AudioClient& client, audio_port_handle_t *clientHandle) { - ALOGD("AAudioServiceEndpoint::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client); + ALOGD("AAudioServiceEndpoint::startClient(...) AAUDIO_ERROR_UNAVAILABLE"); return AAUDIO_ERROR_UNAVAILABLE; } diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index fbf7d1049b..447f32c388 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -163,7 +163,9 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n", __func__, mMmapClient.clientUid, mMmapClient.clientPid, mPortHandle); if (status != OK) { - ALOGE("%s() - openMmapStream() returned status %d", __func__, status); + // This can happen if the resource is busy or the config does + // not match the hardware. + ALOGD("%s() - openMmapStream() returned status %d", __func__, status); return AAUDIO_ERROR_UNAVAILABLE; } @@ -196,9 +198,9 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques result = AAUDIO_ERROR_UNAVAILABLE; goto error; } else { - ALOGD("%s() createMmapBuffer() returned = %d, buffer_size = %d, burst_size %d" + ALOGD("%s() createMmapBuffer() buffer_size = %d fr, burst_size %d fr" ", Sharable FD: %s", - __func__, status, + __func__, mMmapBufferinfo.buffer_size_frames, mMmapBufferinfo.burst_size_frames, isBufferShareable ? "Yes" : "No"); @@ -243,14 +245,13 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques burstMicros = mFramesPerBurst * static_cast(1000000) / getSampleRate(); } while (burstMicros < burstMinMicros); - ALOGD("%s() original burst = %d, minMicros = %d, to burst = %d\n", + ALOGD("%s() original burst = %d, minMicros = %d => burst = %d\n", __func__, mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst); - ALOGD("%s() actual rate = %d, channels = %d" - ", deviceId = %d, capacity = %d\n", - __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity()); + ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d\n", + __func__, getSampleRate(), getSamplesPerFrame(), deviceId); - ALOGD("%s() format = =x%08x, frame size = %d", + ALOGD("%s() format = 0x%08x, frame size = %d", __func__, getFormat(), calculateBytesPerFrame()); return result; @@ -262,7 +263,6 @@ error: aaudio_result_t AAudioServiceEndpointMMAP::close() { if (mMmapStream != 0) { - ALOGD("%s() clear() endpoint", __func__); // Needs to be explicitly cleared or CTS will fail but it is not clear why. mMmapStream.clear(); // Apparently the above close is asynchronous. An attempt to open a new device @@ -284,7 +284,7 @@ aaudio_result_t AAudioServiceEndpointMMAP::startStream(spstart(client, clientHandle); - aaudio_result_t result = AAudioConvert_androidToAAudioResult(status); - ALOGD("%s() , portHandle %d => %d, returns %d", __func__, originalHandle, *clientHandle, result); - return result; + return AAudioConvert_androidToAAudioResult(status); } aaudio_result_t AAudioServiceEndpointMMAP::stopClient(audio_port_handle_t clientHandle) { - ALOGD("%s(portHandle = %d), called", __func__, clientHandle); if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL; aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle)); - ALOGD("%s(portHandle = %d), returns %d", __func__, clientHandle, result); return result; } @@ -371,7 +365,7 @@ void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels, // TODO Do we really need a different volume for each channel? // We get called with an array filled with a single value! float volume = values[0]; - ALOGD("%s(%p) volume[0] = %f", __func__, this, volume); + ALOGD("%s() volume[0] = %f", __func__, volume); std::lock_guard lock(mLockStreams); for(const auto& stream : mRegisteredStreams) { stream->onVolumeChanged(volume); @@ -379,7 +373,7 @@ void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels, }; void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) { - ALOGD("%s(%p) called with dev %d, old = %d", __func__, this, deviceId, getDeviceId()); + ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId()); if (getDeviceId() != AUDIO_PORT_HANDLE_NONE && getDeviceId() != deviceId) { disconnectRegisteredStreams(); } diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp index 1e1c552a0c..bda4b902de 100644 --- a/services/oboeservice/AAudioServiceEndpointPlay.cpp +++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp @@ -43,7 +43,6 @@ using namespace aaudio; // TODO just import names needed AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService) : mStreamInternalPlay(audioService, true) { - ALOGD("%s(%p) created", __func__, this); mStreamInternal = &mStreamInternalPlay; } diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp index 14742dd112..2ca847a052 100644 --- a/services/oboeservice/AAudioServiceStreamShared.cpp +++ b/services/oboeservice/AAudioServiceStreamShared.cpp @@ -111,7 +111,7 @@ int32_t AAudioServiceStreamShared::calculateBufferCapacity(int32_t requestedCapa capacityInFrames, MAX_FRAMES_PER_BUFFER); return AAUDIO_ERROR_OUT_OF_RANGE; } - ALOGD("calculateBufferCapacity() requested %d frames, actual = %d", + ALOGV("calculateBufferCapacity() requested %d frames, actual = %d", requestedCapacityFrames, capacityInFrames); return capacityInFrames; } @@ -144,7 +144,7 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques if (getFormat() == AUDIO_FORMAT_DEFAULT) { setFormat(AUDIO_FORMAT_PCM_FLOAT); } else if (getFormat() != AUDIO_FORMAT_PCM_FLOAT) { - ALOGE("%s() audio_format_t mAudioFormat = %d, need FLOAT", __func__, getFormat()); + ALOGD("%s() audio_format_t mAudioFormat = %d, need FLOAT", __func__, getFormat()); result = AAUDIO_ERROR_INVALID_FORMAT; goto error; } @@ -153,7 +153,7 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques if (getSampleRate() == AAUDIO_UNSPECIFIED) { setSampleRate(endpoint->getSampleRate()); } else if (getSampleRate() != endpoint->getSampleRate()) { - ALOGE("%s() mSampleRate = %d, need %d", + ALOGD("%s() mSampleRate = %d, need %d", __func__, getSampleRate(), endpoint->getSampleRate()); result = AAUDIO_ERROR_INVALID_RATE; goto error; @@ -163,7 +163,7 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) { setSamplesPerFrame(endpoint->getSamplesPerFrame()); } else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) { - ALOGE("%s() mSamplesPerFrame = %d, need %d", + ALOGD("%s() mSamplesPerFrame = %d, need %d", __func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame()); result = AAUDIO_ERROR_OUT_OF_RANGE; goto error; @@ -190,9 +190,6 @@ aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamReques } } - ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d", - __func__, getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId()); - result = endpoint->registerStream(keep); if (result != AAUDIO_OK) { goto error; @@ -227,7 +224,7 @@ aaudio_result_t AAudioServiceStreamShared::getAudioDataDescription( { std::lock_guard lock(mAudioDataQueueLock); if (mAudioDataQueue == nullptr) { - ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); + ALOGW("%s(): mUpMessageQueue null! - stream not open", __func__); return AAUDIO_ERROR_NULL; } // Gather information on the data queue. @@ -262,7 +259,7 @@ aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positio int64_t position = 0; sp endpoint = mServiceEndpointWeak.promote(); if (endpoint == nullptr) { - ALOGE("%s() has no endpoint", __func__); + ALOGW("%s() has no endpoint", __func__); return AAUDIO_ERROR_INVALID_STATE; } -- GitLab From 4b8d5ce58d98d4a163f63e17735450a7cac4821c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 11 Apr 2019 15:57:20 +0200 Subject: [PATCH 1297/1530] audiopolicy: align input and output devices for engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to filter anymore the remote submix to engine as both configurable and default engine take care of legacy remote submix address (aka "0") and wont only care about type. Bug: 130284799 Test: AudioPolicyEmulatorTests --gtest_filter=*.DynamicPolicyMixRecordInjectionTest.* AudioPolicyEmulatorTests --gtest_filter=*.DynamicPolicyMixPlaybackReRoutingTest.* Change-Id: Ia90c65ccfc51220a2344cecec2fbba7f2bad776a Signed-off-by: François Gaffie --- services/audiopolicy/managerdefault/AudioPolicyManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index fd8884181f..43637f6054 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -345,7 +345,7 @@ protected: } virtual const DeviceVector getAvailableOutputDevices() const { - return mAvailableOutputDevices.filterForEngine(); + return mAvailableOutputDevices; } virtual const DeviceVector getAvailableInputDevices() const { -- GitLab From 9e1533cd0b8f3bf08eb9c81c21cd42f5782474f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Thu, 11 Apr 2019 16:07:36 +0200 Subject: [PATCH 1298/1530] audiopolicy: audioflinger: manage stream patch volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AudioPolicy used to hide activity of stream patch to prevent changing the volume. This CL replaces the filtering of the stream patch activity on the policy by a full volume range within audio flinger. It updates also product strategies example for automotive to add associated strategy and full range volume. Bug: 130284799 Test: make Signed-off-by: François Gaffie Change-Id: I6e77dc2cbed0289f0a1341f7597b1c25abee5f7d --- services/audioflinger/AudioFlinger.cpp | 4 ++-- services/audiopolicy/common/include/Volume.h | 1 - ...audio_policy_engine_product_strategies.xml | 15 +++++++++++++ .../audio_policy_engine_volumes.xml | 21 +++++++++++++++++++ ...audio_policy_engine_product_strategies.xml | 14 +++++++++++++ .../caremu/audio_policy_engine_volumes.xml | 21 +++++++++++++++++++ .../managerdefault/AudioPolicyManager.cpp | 2 +- 7 files changed, 74 insertions(+), 4 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 4b318166cb..95271959a2 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1238,8 +1238,8 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, if (output == AUDIO_IO_HANDLE_NONE) { return BAD_VALUE; } - ALOG_ASSERT(stream != AUDIO_STREAM_PATCH || value == 1.0, - "attempt to change AUDIO_STREAM_PATCH volume"); + LOG_ALWAYS_FATAL_IF(stream == AUDIO_STREAM_PATCH && value != 1.0f, + "AUDIO_STREAM_PATCH must have full scale volume"); AutoMutex lock(mLock); VolumeInterface *volumeInterface = getVolumeInterface_l(output); diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h index 54c5c76bf1..1dbd1eb224 100644 --- a/services/audiopolicy/common/include/Volume.h +++ b/services/audiopolicy/common/include/Volume.h @@ -23,7 +23,6 @@ namespace android { - /** * VolumeSource is the discriminent for volume management on an output. * It used to be the stream type by legacy, it may be host volume group or a volume curves if diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml index c487da9e24..0ee83a2233 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_product_strategies.xml @@ -166,5 +166,20 @@ limitations under the License. + + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml index 9ec3d77141..6e72dc525d 100644 --- a/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml +++ b/services/audiopolicy/engineconfigurable/config/example/automotive/audio_policy_engine_volumes.xml @@ -188,5 +188,26 @@ 100,0 + + + rerouting + 0 + 1 + + 0,0 + 100,0 + + + + + patch + 0 + 1 + + 0,0 + 100,0 + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml index c487da9e24..adcbd834f4 100644 --- a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml +++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_product_strategies.xml @@ -166,5 +166,19 @@ limitations under the License. + + + + + + + + + + + + + + diff --git a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml index 9ec3d77141..6e72dc525d 100644 --- a/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml +++ b/services/audiopolicy/engineconfigurable/config/example/caremu/audio_policy_engine_volumes.xml @@ -188,5 +188,26 @@ 100,0 + + + rerouting + 0 + 1 + + 0,0 + 100,0 + + + + + patch + 0 + 1 + + 0,0 + 100,0 + + + diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d8fbc381fc..6c06332066 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2481,7 +2481,7 @@ status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_ // HW Gain management, do not change the volume bool applyVolume = false; if (desc->useHwGain()) { - if (!(desc->isActive(group) || isInCall())) { + if (!(desc->isActive(toVolumeSource(group)) || isInCall())) { continue; } for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) { -- GitLab From 993f390f8076868a4e42ebbe0b1072c4cd110d34 Mon Sep 17 00:00:00 2001 From: Francois Gaffie Date: Wed, 10 Apr 2019 15:39:27 +0200 Subject: [PATCH 1299/1530] audiopolicy: remove double call to attach / getModuleForDeviceType For a2dp hybrid mode, device retrieval is done through type / code pair. When the device is connected, getDeviceDescriptor is called with allowToCreate boolean. As a result, HwModuleCollection::createDevice will be called, invoking getModuleForDeviceTypes as done laterly in setDeviceConnectionState. This CL removes double call to these function. Bug: 130284799 Test: make Change-Id: I188f11e9325cfc6af2ae872e00485e00ccd9096b Signed-off-by: Francois Gaffie --- .../managerdefault/AudioPolicyManager.cpp | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6c06332066..4992fd415d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -142,18 +142,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT __func__, device->toString().c_str(), encodedFormat); // register new device as available - index = mAvailableOutputDevices.add(device); - if (index >= 0) { - sp module = mHwModules.getModuleForDevice(device, encodedFormat); - if (module == 0) { - ALOGD("setDeviceConnectionState() could not find HW module for device %s", - device->toString().c_str()); - mAvailableOutputDevices.remove(device); - return INVALID_OPERATION; - } - ALOGV("setDeviceConnectionState() module name=%s", module->getName()); - mAvailableOutputDevices[index]->attach(module); - } else { + if (mAvailableOutputDevices.add(device) < 0) { return NO_MEMORY; } @@ -295,13 +284,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } - sp module = mHwModules.getModuleForDevice(device, AUDIO_FORMAT_DEFAULT); - if (module == NULL) { - ALOGW("setDeviceConnectionState(): could not find HW module for device %s", - device->toString().c_str()); - return INVALID_OPERATION; - } - // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) broadcastDeviceConnectionState(device, state); -- GitLab From b20cf7db9e669169bbae5f0da97018a866c3f634 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 5 Apr 2019 19:37:34 -0700 Subject: [PATCH 1300/1530] audio policy: concurrent capture effects Add pre processing effect management for concurrent capture scenarii. When several clients on the same input stream have enabled audio effects, only the effects attached to the top priotity client are active. Other effects are suspended. Add AudioFlinger API to suspend/restore audio effects build on top of exisiting internal effect suspend mechanism. RecordThread now supports more than one effect chain. AOSP pre processing implementation supports more than one effect session per input. Refactor AudioPolicyManager::closeAllInputs() to call closeInput() on all inputs instead of partially duplicated code. Bug: 128419018 Test: make Change-Id: I685286da4c2905a8894a4354679f9787b1400621 --- media/libaudioclient/IAudioFlinger.cpp | 23 ++++++ .../include/media/IAudioFlinger.h | 4 + .../preprocessing/PreProcessing.cpp | 10 +-- services/audioflinger/AudioFlinger.cpp | 47 +++++++++-- services/audioflinger/AudioFlinger.h | 6 +- services/audioflinger/Effects.cpp | 7 +- services/audioflinger/Threads.cpp | 18 ++-- services/audiopolicy/AudioPolicyInterface.h | 4 + .../include/AudioInputDescriptor.h | 6 ++ .../include/EffectDescriptor.h | 16 ++-- .../src/AudioInputDescriptor.cpp | 82 +++++++++++++++---- .../src/EffectDescriptor.cpp | 15 +++- .../managerdefault/AudioPolicyManager.cpp | 45 ++++------ .../managerdefault/AudioPolicyManager.h | 2 + .../service/AudioPolicyClientImpl.cpp | 7 ++ .../service/AudioPolicyService.cpp | 36 ++++++++ .../audiopolicy/service/AudioPolicyService.h | 20 ++++- .../audiopolicy/tests/AudioPolicyTestClient.h | 3 + 18 files changed, 274 insertions(+), 77 deletions(-) diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 825cd4ec3d..dd95e347dc 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -89,6 +89,7 @@ enum { GET_MICROPHONES, SET_MASTER_BALANCE, GET_MASTER_BALANCE, + SET_EFFECT_SUSPENDED, }; #define MAX_ITEMS_PER_LIST 1024 @@ -718,6 +719,18 @@ public: return reply.readInt32(); } + virtual void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeInt32(effectId); + data.writeInt32(sessionId); + data.writeInt32(suspended ? 1 : 0); + remote()->transact(SET_EFFECT_SUSPENDED, data, &reply); + } + virtual audio_module_handle_t loadHwModule(const char *name) { Parcel data, reply; @@ -913,6 +926,7 @@ status_t BnAudioFlinger::onTransact( case INVALIDATE_STREAM: case SET_VOICE_VOLUME: case MOVE_EFFECTS: + case SET_EFFECT_SUSPENDED: case LOAD_HW_MODULE: case LIST_AUDIO_PORTS: case GET_AUDIO_PORT: @@ -926,6 +940,7 @@ status_t BnAudioFlinger::onTransact( // return status only for non void methods switch (code) { case SET_RECORD_SILENCED: + case SET_EFFECT_SUSPENDED: break; default: reply->writeInt32(static_cast (INVALID_OPERATION)); @@ -1371,6 +1386,14 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(moveEffects(session, srcOutput, dstOutput)); return NO_ERROR; } break; + case SET_EFFECT_SUSPENDED: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + int effectId = data.readInt32(); + audio_session_t sessionId = (audio_session_t) data.readInt32(); + bool suspended = data.readInt32() == 1; + setEffectSuspended(effectId, sessionId, suspended); + return NO_ERROR; + } break; case LOAD_HW_MODULE: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32(loadHwModule(data.readCString())); diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index ef0ed0c560..dcc18b6603 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -457,6 +457,10 @@ public: virtual status_t moveEffects(audio_session_t session, audio_io_handle_t srcOutput, audio_io_handle_t dstOutput) = 0; + virtual void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) = 0; + virtual audio_module_handle_t loadHwModule(const char *name) = 0; // helpers for android.media.AudioManager.getProperty(), see description there for meaning diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp index 50c33c6df5..5fab5be1c3 100644 --- a/media/libeffects/preprocessing/PreProcessing.cpp +++ b/media/libeffects/preprocessing/PreProcessing.cpp @@ -926,7 +926,7 @@ int Session_ReleaseEffect(preproc_session_t *session, delete session->revBuf; session->revBuf = NULL; - session->io = 0; + session->id = 0; } return 0; @@ -1155,7 +1155,7 @@ preproc_session_t *PreProc_GetSession(int32_t procId, int32_t sessionId, int32_ { size_t i; for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { - if (sSessions[i].io == ioId) { + if (sSessions[i].id == sessionId) { if (sSessions[i].createdMsk & (1 << procId)) { return NULL; } @@ -1163,7 +1163,7 @@ preproc_session_t *PreProc_GetSession(int32_t procId, int32_t sessionId, int32_ } } for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { - if (sSessions[i].io == 0) { + if (sSessions[i].id == 0) { sSessions[i].id = sessionId; sSessions[i].io = ioId; return &sSessions[i]; @@ -1915,7 +1915,7 @@ int PreProcessingLib_Create(const effect_uuid_t *uuid, status = Session_CreateEffect(session, procId, pInterface); if (status < 0 && session->createdMsk == 0) { - session->io = 0; + session->id = 0; } return status; } @@ -1929,7 +1929,7 @@ int PreProcessingLib_Release(effect_handle_t interface) preproc_effect_t *fx = (preproc_effect_t *)interface; - if (fx->session->io == 0) { + if (fx->session->id == 0) { return -EINVAL; } return Session_ReleaseEffect(fx->session, fx); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 4b318166cb..809d16d29d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1699,18 +1699,35 @@ void AudioFlinger::removeClient_l(pid_t pid) } // getEffectThread_l() must be called with AudioFlinger::mLock held -sp AudioFlinger::getEffectThread_l(audio_session_t sessionId, - int EffectId) +sp AudioFlinger::getEffectThread_l(audio_session_t sessionId, + int effectId) { - sp thread; + sp thread; for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->getEffect(sessionId, EffectId) != 0) { + if (mPlaybackThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) { ALOG_ASSERT(thread == 0); thread = mPlaybackThreads.valueAt(i); } } - + if (thread != nullptr) { + return thread; + } + for (size_t i = 0; i < mRecordThreads.size(); i++) { + if (mRecordThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) { + ALOG_ASSERT(thread == 0); + thread = mRecordThreads.valueAt(i); + } + } + if (thread != nullptr) { + return thread; + } + for (size_t i = 0; i < mMmapThreads.size(); i++) { + if (mMmapThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) { + ALOG_ASSERT(thread == 0); + thread = mMmapThreads.valueAt(i); + } + } return thread; } @@ -3464,6 +3481,23 @@ status_t AudioFlinger::moveEffects(audio_session_t sessionId, audio_io_handle_t return moveEffectChain_l(sessionId, srcThread, dstThread); } + +void AudioFlinger::setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) +{ + Mutex::Autolock _l(mLock); + + sp thread = getEffectThread_l(sessionId, effectId); + if (thread == nullptr) { + return; + } + Mutex::Autolock _sl(thread->mLock); + sp effect = thread->getEffect_l(sessionId, effectId); + thread->setEffectSuspended_l(&effect->desc().type, suspended, sessionId); +} + + // moveEffectChain_l must be called with both srcThread and dstThread mLocks held status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId, AudioFlinger::PlaybackThread *srcThread, @@ -3541,7 +3575,8 @@ status_t AudioFlinger::moveAuxEffectToIo(int EffectId, { status_t status = NO_ERROR; Mutex::Autolock _l(mLock); - sp thread = getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId); + sp thread = + static_cast(getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId).get()); if (EffectId != 0 && thread != 0 && dstThread != thread.get()) { Mutex::Autolock _dl(dstThread->mLock); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 8bf89c8a63..72e669a2a5 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -239,6 +239,10 @@ public: virtual status_t moveEffects(audio_session_t sessionId, audio_io_handle_t srcOutput, audio_io_handle_t dstOutput); + void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) override; + virtual audio_module_handle_t loadHwModule(const char *name); virtual uint32_t getPrimaryOutputSamplingRate(); @@ -708,7 +712,7 @@ using effect_buffer_t = int16_t; // return the playback thread with smallest HAL buffer size, and prefer fast PlaybackThread *fastPlaybackThread_l() const; - sp getEffectThread_l(audio_session_t sessionId, int EffectId); + sp getEffectThread_l(audio_session_t sessionId, int effectId); void removeClient_l(pid_t pid); diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 50ab634960..3c4fbba156 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1488,9 +1488,10 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector& args __unu result.append("\t\tCould not lock Fx mutex:\n"); } - result.append("\t\tSession Status State Engine:\n"); - result.appendFormat("\t\t%05d %03d %03d %p\n", - mSessionId, mStatus, mState, mEffectInterface.get()); + result.append("\t\tSession Status State Registered Enabled Suspended Engine:\n"); + result.appendFormat("\t\t%05d %03d %03d %s %s %s %p\n", + mSessionId, mStatus, mState, mPolicyRegistered ? "y" : "n", mPolicyEnabled ? "y" : "n", + mSuspended ? "y" : "n", mEffectInterface.get()); result.append("\t\tDescriptor:\n"); char uuidStr[64]; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index e71f0c6a96..59ced26797 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -8248,11 +8248,6 @@ sp AudioFlinger::RecordThread::stream() const status_t AudioFlinger::RecordThread::addEffectChain_l(const sp& chain) { - // only one chain per input thread - if (!mEffectChains.isEmpty()) { - ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this); - return INVALID_OPERATION; - } ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this); chain->setThread(this); chain->setInBuffer(NULL); @@ -8272,13 +8267,14 @@ status_t AudioFlinger::RecordThread::addEffectChain_l(const sp& cha size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp& chain) { ALOGV("removeEffectChain_l() %p from thread %p", chain.get(), this); - ALOGW_IF(mEffectChains.size() != 1, - "removeEffectChain_l() %p invalid chain size %zu on thread %p", - chain.get(), mEffectChains.size(), this); - if (mEffectChains.size() == 1) { - mEffectChains.removeAt(0); + + for (size_t i = 0; i < mEffectChains.size(); i++) { + if (chain == mEffectChains[i]) { + mEffectChains.removeAt(i); + break; + } } - return 0; + return mEffectChains.size(); } status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *patch, diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index caf0d7eb36..4b56a46b83 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -348,6 +348,10 @@ public: audio_io_handle_t srcOutput, audio_io_handle_t dstOutput) = 0; + virtual void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) = 0; + /* Create a patch between several source and sink ports */ virtual status_t createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index e071fe07b1..37f9d14ea6 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -68,11 +68,13 @@ public: bool isSourceActive(audio_source_t source) const; audio_source_t source() const; bool isSoundTrigger() const; + sp getHighestPriorityClient() const; audio_attributes_t getHighestPriorityAttributes() const; void setClientActive(const sp& client, bool active); int32_t activeCount() { return mGlobalActiveCount; } void trackEffectEnabled(const sp &effect, bool enabled); EffectDescriptorCollection getEnabledEffects() const; + EffectDescriptorCollection getActiveEffects() const; // enabled and not suspended // implementation of AudioIODescriptorInterface audio_config_base_t getConfig() const override; audio_patch_handle_t getPatchHandle() const override; @@ -100,6 +102,10 @@ public: // implementation of ClientMapHandler void addClient(const sp &client) override; + // Go over all active clients and suspend or restore effects according highest priority + // active use case + void checkSuspendEffects(); + private: void updateClientRecordingConfiguration(int event, const sp& client); diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h index c729ef9f28..c4eab304a2 100644 --- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h @@ -29,8 +29,8 @@ class EffectDescriptor : public RefBase { public: EffectDescriptor(const effect_descriptor_t *desc, bool isMusicEffect, - int id, int io, int session) : - mId(id), mIo(io), mSession(session), mEnabled(false), + int id, audio_io_handle_t io, audio_session_t session) : + mId(id), mIo(io), mSession(session), mEnabled(false), mSuspended(false), mIsMusicEffect(isMusicEffect) { memcpy (&mDesc, desc, sizeof(effect_descriptor_t)); @@ -38,11 +38,12 @@ public: void dump(String8 *dst, int spaces = 0) const; - int mId; // effect unique ID - int mIo; // io the effect is attached to - int mSession; // audio session the effect is on - effect_descriptor_t mDesc; // effect descriptor - bool mEnabled; // enabled state: CPU load being used or not + int mId; // effect unique ID + audio_io_handle_t mIo; // io the effect is attached to + audio_session_t mSession; // audio session the effect is on + effect_descriptor_t mDesc; // effect descriptor + bool mEnabled; // enabled state: CPU load being used or not + bool mSuspended; // enabled but suspended by concurent capture policy bool isMusicEffect() const { return mIsMusicEffect; } @@ -59,6 +60,7 @@ public: int session, int id, bool isMusicEffect); status_t unregisterEffect(int id); sp getEffect(int id) const; + EffectDescriptorCollection getEffectsForIo(audio_io_handle_t io) const; status_t setEffectEnabled(int id, bool enabled); bool isEffectEnabled(int id) const; uint32_t getMaxEffectsCpuLoad() const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 5b4e2eb680..58683be797 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -128,6 +128,13 @@ bool AudioInputDescriptor::isSourceActive(audio_source_t source) const audio_attributes_t AudioInputDescriptor::getHighestPriorityAttributes() const { audio_attributes_t attributes = { .source = AUDIO_SOURCE_DEFAULT }; + sp topClient = getHighestPriorityClient(); + return topClient ? topClient->attributes() : attributes; +} + +sp AudioInputDescriptor::getHighestPriorityClient() const +{ + sp topClient; for (bool activeOnly : { true, false }) { int32_t topPriority = -1; @@ -139,18 +146,18 @@ audio_attributes_t AudioInputDescriptor::getHighestPriorityAttributes() const app_state_t curState = client->appState(); if (curState >= topState) { int32_t curPriority = source_priority(client->source()); - if (curPriority > topPriority) { - attributes = client->attributes(); + if (curPriority >= topPriority) { + topClient = client; topPriority = curPriority; } topState = curState; } } - if (attributes.source != AUDIO_SOURCE_DEFAULT) { + if (topClient != nullptr) { break; } } - return attributes; + return topClient; } bool AudioInputDescriptor::isSoundTrigger() const { @@ -326,9 +333,10 @@ void AudioInputDescriptor::setClientActive(const sp& cli client->setActive(active); + checkSuspendEffects(); + int event = active ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_STOP; updateClientRecordingConfiguration(event, client); - } void AudioInputDescriptor::updateClientRecordingConfiguration( @@ -397,42 +405,88 @@ void AudioInputDescriptor::trackEffectEnabled(const sp &effect mEnabledEffects.replaceValueFor(effect->mId, effect); } else { mEnabledEffects.removeItem(effect->mId); + // always exit from suspend when disabling an effect as only enabled effects + // are managed by checkSuspendEffects() + if (effect->mSuspended) { + effect->mSuspended = false; + mClientInterface->setEffectSuspended(effect->mId, effect->mSession, effect->mSuspended); + } } RecordClientVector clients = getClientsForSession((audio_session_t)effect->mSession); + RecordClientVector updatedClients; + for (const auto& client : clients) { sp clientEffect = client->getEnabledEffects().getEffect(effect->mId); bool changed = (enabled && clientEffect == nullptr) || (!enabled && clientEffect != nullptr); client->trackEffectEnabled(effect, enabled); if (changed && client->active()) { - updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + updatedClients.push_back(client); } } + + checkSuspendEffects(); + + for (const auto& client : updatedClients) { + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + } } EffectDescriptorCollection AudioInputDescriptor::getEnabledEffects() const { - EffectDescriptorCollection enabledEffects; // report effects for highest priority active source as applied to all clients - RecordClientVector clients = - clientsList(true /*activeOnly*/, source(), false /*preferredDeviceOnly*/); - if (clients.size() > 0) { - enabledEffects = clients[0]->getEnabledEffects(); + EffectDescriptorCollection enabledEffects; + sp topClient = getHighestPriorityClient(); + if (topClient != nullptr) { + enabledEffects = topClient->getEnabledEffects(); } return enabledEffects; } -void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state) { +void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state) +{ RecordClientVector clients = clientsList(false /*activeOnly*/); + RecordClientVector updatedClients; for (const auto& client : clients) { if (uid == client->uid()) { bool wasSilenced = client->isSilenced(); client->setAppState(state); if (client->active() && wasSilenced != client->isSilenced()) { - updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + updatedClients.push_back(client); + } + } + } + + checkSuspendEffects(); + + for (const auto& client : updatedClients) { + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + } +} + +void AudioInputDescriptor::checkSuspendEffects() +{ + sp topClient = getHighestPriorityClient(); + if (topClient == nullptr) { + return; + } + + for (size_t i = 0; i < mEnabledEffects.size(); i++) { + sp effect = mEnabledEffects.valueAt(i); + if (effect->mSession == topClient->session()) { + if (effect->mSuspended) { + effect->mSuspended = false; + mClientInterface->setEffectSuspended(effect->mId, + effect->mSession, + effect->mSuspended); } + } else if (!effect->mSuspended) { + effect->mSuspended = true; + mClientInterface->setEffectSuspended(effect->mId, + effect->mSession, + effect->mSuspended); } } } @@ -444,7 +498,7 @@ void AudioInputDescriptor::dump(String8 *dst) const dst->appendFormat(" Format: %d\n", mFormat); dst->appendFormat(" Channels: %08x\n", mChannelMask); dst->appendFormat(" Devices %s\n", mDevice->toString().c_str()); - getEnabledEffects().dump(dst, 1 /*spaces*/, false /*verbose*/); + mEnabledEffects.dump(dst, 1 /*spaces*/, false /*verbose*/); dst->append(" AudioRecord Clients:\n"); ClientMapHandler::dump(dst); dst->append("\n"); diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp index 627fa8d842..415962a939 100644 --- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp @@ -30,6 +30,7 @@ void EffectDescriptor::dump(String8 *dst, int spaces) const dst->appendFormat("%*sSession: %d\n", spaces, "", mSession); dst->appendFormat("%*sName: %s\n", spaces, "", mDesc.name); dst->appendFormat("%*s%s\n", spaces, "", mEnabled ? "Enabled" : "Disabled"); + dst->appendFormat("%*s%s\n", spaces, "", mSuspended ? "Suspended" : "Active"); } EffectDescriptorCollection::EffectDescriptorCollection() : @@ -63,7 +64,8 @@ status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *d desc->name, io, session, id); ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); - sp effectDesc = new EffectDescriptor(desc, isMusicEffect, id, io, session); + sp effectDesc = + new EffectDescriptor(desc, isMusicEffect, id, io, (audio_session_t)session); add(id, effectDesc); return NO_ERROR; @@ -200,6 +202,17 @@ void EffectDescriptorCollection::moveEffects(const std::vector& ids, } } +EffectDescriptorCollection EffectDescriptorCollection::getEffectsForIo(audio_io_handle_t io) const +{ + EffectDescriptorCollection effects; + for (size_t i = 0; i < size(); i++) { + if (valueAt(i)->mIo == io) { + effects.add(keyAt(i), valueAt(i)); + } + } + return effects; +} + void EffectDescriptorCollection::dump(String8 *dst, int spaces, bool verbose) const { if (verbose) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index d8fbc381fc..c6d55bc954 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2361,25 +2361,8 @@ void AudioPolicyManager::closeClient(audio_port_handle_t portId) } void AudioPolicyManager::closeAllInputs() { - bool patchRemoved = false; - - for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { - sp inputDesc = mInputs.valueAt(input_index); - ssize_t patch_index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle()); - if (patch_index >= 0) { - sp patchDesc = mAudioPatches.valueAt(patch_index); - (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); - mAudioPatches.removeItemsAt(patch_index); - patchRemoved = true; - } - inputDesc->close(); - } - mInputs.clear(); - SoundTrigger::setCaptureState(false); - nextAudioPortGeneration(); - - if (patchRemoved) { - mpClientInterface->onAudioPatchListUpdate(); + while (mInputs.size() != 0) { + closeInput(mInputs.keyAt(0)); } } @@ -2713,7 +2696,6 @@ status_t AudioPolicyManager::unregisterEffect(int id) if (mEffects.getEffect(id) == nullptr) { return INVALID_OPERATION; } - if (mEffects.isEffectEnabled(id)) { ALOGW("%s effect %d enabled", __FUNCTION__, id); setEffectEnabled(id, false); @@ -2721,6 +2703,16 @@ status_t AudioPolicyManager::unregisterEffect(int id) return mEffects.unregisterEffect(id); } +void AudioPolicyManager::cleanUpEffectsForIo(audio_io_handle_t io) +{ + EffectDescriptorCollection effects = mEffects.getEffectsForIo(io); + for (size_t i = 0; i < effects.size(); i++) { + ALOGW("%s removing stale effect %s, id %d on closed IO %d", + __func__, effects.valueAt(i)->mDesc.name, effects.keyAt(i), io); + unregisterEffect(effects.keyAt(i)); + } +} + status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) { sp effect = mEffects.getEffect(id); @@ -4113,15 +4105,8 @@ status_t AudioPolicyManager::setSurroundFormatEnabled(audio_format_t audioFormat void AudioPolicyManager::setAppState(uid_t uid, app_state_t state) { ALOGV("%s(uid:%d, state:%d)", __func__, uid, state); - for (size_t i = 0; i < mInputs.size(); i++) { - sp inputDesc = mInputs.valueAt(i); - RecordClientVector clients = inputDesc->clientsList(false /*activeOnly*/); - for (const auto& client : clients) { - if (uid == client->uid()) { - client->setAppState(state); - } - } + mInputs.valueAt(i)->setAppState(uid, state); } } @@ -4932,6 +4917,8 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) setMsdPatch(); } } + + cleanUpEffectsForIo(output); } void AudioPolicyManager::closeInput(audio_io_handle_t input) @@ -4963,6 +4950,8 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) { SoundTrigger::setCaptureState(false); } + + cleanUpEffectsForIo(input); } SortedVector AudioPolicyManager::getOutputsForDevices( diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index fd8884181f..587f2b888d 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -870,6 +870,8 @@ private: int delayMs, uid_t uid, sp *patchDescPtr); + + void cleanUpEffectsForIo(audio_io_handle_t io); }; }; diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index 5748334e0e..d51cc6ecc6 100644 --- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp @@ -172,6 +172,13 @@ status_t AudioPolicyService::AudioPolicyClient::moveEffects(audio_session_t sess return af->moveEffects(session, src_output, dst_output); } +void AudioPolicyService::AudioPolicyClient::setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) +{ + mAudioPolicyService->setEffectSuspended(effectId, sessionId, suspended); +} + status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, int delayMs) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index f63fa8123a..5389c08a1a 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -1196,6 +1196,17 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() data->mPatchHandle, data->mSource); mLock.lock(); } break; + case SET_EFFECT_SUSPENDED: { + SetEffectSuspendedData *data = (SetEffectSuspendedData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set effect suspended"); + sp af = AudioSystem::get_audio_flinger(); + if (af != 0) { + mLock.unlock(); + af->setEffectSuspended(data->mEffectId, data->mSessionId, data->mSuspended); + mLock.lock(); + } + } break; + default: ALOGW("AudioCommandThread() unknown command %d", command->mCommand); } @@ -1327,6 +1338,23 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume return sendCommand(command, delayMs); } +void AudioPolicyService::AudioCommandThread::setEffectSuspendedCommand(int effectId, + audio_session_t sessionId, + bool suspended) +{ + sp command = new AudioCommand(); + command->mCommand = SET_EFFECT_SUSPENDED; + sp data = new SetEffectSuspendedData(); + data->mEffectId = effectId; + data->mSessionId = sessionId; + data->mSuspended = suspended; + command->mParam = data; + ALOGV("AudioCommandThread() adding set suspended effectId %d sessionId %d suspended %d", + effectId, sessionId, suspended); + sendCommand(command); +} + + void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_port_handle_t portId) { sp command = new AudioCommand(); @@ -1707,6 +1735,14 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs) return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); } +void AudioPolicyService::setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) +{ + mAudioCommandThread->setEffectSuspendedCommand(effectId, sessionId, suspended); +} + + extern "C" { audio_module_handle_t aps_load_hw_module(void *service __unused, const char *name); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 08426496f7..6c19912f16 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -293,6 +293,9 @@ public: void onAudioVolumeGroupChanged(volume_group_t group, int flags); void doOnAudioVolumeGroupChanged(volume_group_t group, int flags); + void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended); private: AudioPolicyService() ANDROID_API; @@ -427,7 +430,8 @@ private: CHANGED_AUDIOVOLUMEGROUP, SET_AUDIOPORT_CONFIG, DYN_POLICY_MIX_STATE_UPDATE, - RECORDING_CONFIGURATION_UPDATE + RECORDING_CONFIGURATION_UPDATE, + SET_EFFECT_SUSPENDED, }; AudioCommandThread (String8 name, const wp& service); @@ -470,6 +474,9 @@ private: std::vector effects, audio_patch_handle_t patchHandle, audio_source_t source); + void setEffectSuspendedCommand(int effectId, + audio_session_t sessionId, + bool suspended); void insertCommand_l(AudioCommand *command, int delayMs = 0); private: class AudioCommandData; @@ -567,6 +574,13 @@ private: audio_source_t mSource; }; + class SetEffectSuspendedData : public AudioCommandData { + public: + int mEffectId; + audio_session_t mSessionId; + bool mSuspended; + }; + Mutex mLock; Condition mWaitWorkCV; Vector < sp > mAudioCommands; // list of pending commands @@ -652,6 +666,10 @@ private: audio_io_handle_t srcOutput, audio_io_handle_t dstOutput); + void setEffectSuspended(int effectId, + audio_session_t sessionId, + bool suspended) override; + /* Create a patch between several source and sink ports */ virtual status_t createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h index 8854eb2e6d..e4c64e511f 100644 --- a/services/audiopolicy/tests/AudioPolicyTestClient.h +++ b/services/audiopolicy/tests/AudioPolicyTestClient.h @@ -84,6 +84,9 @@ public: std::vector effects __unused, audio_patch_handle_t patchHandle __unused, audio_source_t source __unused) override { } + void setEffectSuspended(int effectId __unused, + audio_session_t sessionId __unused, + bool suspended __unused) {} }; } // namespace android -- GitLab From 7d8c37634fb71b27e94cd2dff10b75f004027fb6 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Mon, 15 Apr 2019 16:03:56 -0700 Subject: [PATCH 1301/1530] C2SoftVorbisDec: Ignore subsequent headers Ignore subsequent headers after decoding the headers once Bug: 130569201 Test: atest android.media.cts.DecoderTest#testDecodeOgg Change-Id: Ifa8b67f00d16bfaa5e11097ef3b61ea3f183ced0 --- media/codec2/components/vorbis/C2SoftVorbisDec.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp index e7393eeee7..e3bafd3f9d 100644 --- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp +++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp @@ -270,7 +270,8 @@ void C2SoftVorbisDec::process( const uint8_t *data = rView.data() + inOffset; int32_t numChannels = mVi->channels; int32_t samplingRate = mVi->rate; - if (inSize > 7 && !memcmp(&data[1], "vorbis", 6)) { + /* Decode vorbis headers only once */ + if (inSize > 7 && !memcmp(&data[1], "vorbis", 6) && (!mInfoUnpacked || !mBooksUnpacked)) { if ((data[0] != 1) && (data[0] != 5)) { ALOGE("unexpected type received %d", data[0]); mSignalledError = true; -- GitLab From b06277eff473ca8c2d7f403fd39cd954b282484a Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 18 Apr 2019 12:19:30 -0700 Subject: [PATCH 1302/1530] RecordBufferConverter: Release converter buffer after conversion Required to ensure the RecordThread understands the true number of frames consumed / available. Otherwise the RecordThread thinks there are more frames than actually present, causing a buffer provider underrun in the AudioResampler and the resampler to reset. Test: BT phone call, Tee sink Bug: 130419319 Change-Id: I96fac6f52233e8519c0cc098329e5b9b89c1dd89 --- media/libaudioprocessing/RecordBufferConverter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/media/libaudioprocessing/RecordBufferConverter.cpp b/media/libaudioprocessing/RecordBufferConverter.cpp index 54151f596d..7d6fe72b9e 100644 --- a/media/libaudioprocessing/RecordBufferConverter.cpp +++ b/media/libaudioprocessing/RecordBufferConverter.cpp @@ -117,6 +117,11 @@ size_t RecordBufferConverter::convert(void *dst, // format convert to destination buffer convertResampler(dst, mBuf, frames); } + // Release unused frames in the InputConverterProvider buffer so that + // the RecordThread is able to properly account for consumed frames. + if (mInputConverterProvider != nullptr) { + mInputConverterProvider->reset(); + } return frames; } -- GitLab From f7529dd062ff3d5b198c70735b065557302baf2b Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 18 Apr 2019 17:35:53 -0700 Subject: [PATCH 1303/1530] CCodec: reduce log amount for render warning Bug: 124096926 Test: builds Change-Id: I7fd7bd0da8e32793f87c0c8278c208d1b4d5bbb2 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 10 ++++++---- media/codec2/sfplugin/CCodecBufferChannel.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index d1fa920623..1b21df161d 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1917,10 +1917,12 @@ status_t CCodecBufferChannel::renderOutputBuffer( feedInputBufferIfAvailable(); if (!c2Buffer) { if (released) { - ALOGD("[%s] The app is calling releaseOutputBuffer() with " - "timestamp or render=true with non-video buffers. Apps should " - "call releaseOutputBuffer() with render=false for those.", - mName); + std::call_once(mRenderWarningFlag, [this] { + ALOGW("[%s] The app is calling releaseOutputBuffer() with " + "timestamp or render=true with non-video buffers. Apps should " + "call releaseOutputBuffer() with render=false for those.", + mName); + }); } return INVALID_OPERATION; } diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 1ea29b426f..964121e525 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -311,6 +311,7 @@ private: Mutexed mReorderStash; std::atomic_bool mInputMetEos; + std::once_flag mRenderWarningFlag; inline bool hasCryptoOrDescrambler() { return mCrypto != nullptr || mDescrambler != nullptr; -- GitLab From df7f0a201b4b91ee86bd0b8d3a2a05beba9f46f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Tue, 2 Apr 2019 17:42:15 +0200 Subject: [PATCH 1304/1530] audiopolicy: engineconfigurable: fix build issues on Q environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL fixes the compilation issues met while generating configuration files. @todo: TEMPORARY_DISABLE_PATH_RESTRICTIONS=true is still required to use subprocess.Popen(["domainGeneratorConnector",...) on external/parameter-framework @todo: CL mixing in parameter-framework leading to validation failure Bug: 130284799 Test: make Change-Id: I495026781af63f3c3a538b00fe467d39f58baa70 Signed-off-by: François Gaffie --- .../config/example/Android.mk | 4 +- .../parameter-framework/examples/Android.mk | 48 ++++++++++--------- .../examples/Car/Android.mk | 19 ++------ .../examples/CarEmu/Android.mk | 20 ++------ .../examples/Phone/Android.mk | 14 +----- .../parameter-framework/plugin/Android.mk | 5 -- .../tools/buildPolicyCriterionTypes.py | 12 ++++- .../tools/buildStrategiesStructureFile.py | 2 +- .../tools/build_audio_pfw_settings.mk | 7 ++- .../tools/domainGeneratorPolicy.py | 4 +- ...on_criterion_types_from_android_headers.mk | 10 ++-- .../tools/provision_strategies_structure.mk | 10 ++-- 12 files changed, 64 insertions(+), 91 deletions(-) diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index 37271b511f..f879afc8c0 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -137,9 +137,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := audio_policy_engine_criterion_types.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml - +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_VENDOR_ETC)/primary_audio_policy_configuration.xml ANDROID_AUDIO_BASE_HEADER_FILE := system/media/audio/include/system/audio-base.h AUDIO_POLICY_CONFIGURATION_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_configuration.xml CRITERION_TYPES_FILE := $(LOCAL_PATH)/common/$(LOCAL_MODULE).in diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 782fe83d8a..ddc8721540 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -38,6 +38,9 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework LOCAL_SRC_FILES := $(LOCAL_MODULE).in +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem.xml\ + PolicyClass.xml # external/parameter-framework prevents from using debug interface AUDIO_PATTERN = @TUNING_ALLOWED@ @@ -60,9 +63,7 @@ LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_REQUIRED_MODULES := \ PolicySubsystem-CommonTypes.xml \ - ProductStrategies.xml \ - PolicySubsystem-Volume.xml \ - libpolicy-subsystem \ + ProductStrategies.xml LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE) @@ -92,7 +93,7 @@ LOCAL_MODULE := ProductStrategies.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_configuration.xml + AUDIO_POLICY_ENGINE_CONFIGURATION_FILE := \ $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_configuration.xml STRATEGIES_STRUCTURE_FILE := $(LOCAL_PATH)/common/Structure/$(LOCAL_MODULE).in @@ -110,15 +111,27 @@ LOCAL_MODULE_STEM := PolicySubsystem.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem-CommonTypes.xml \ - PolicySubsystem-Volume.xml \ - libpolicy-subsystem \ +LOCAL_REQUIRED_MODULES := PolicySubsystem-CommonTypes.xml LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE_STEM) include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_RELATIVE_PATH := parameter-framework +LOCAL_SRC_FILES := $(LOCAL_MODULE).in +LOCAL_REQUIRED_MODULES := \ + PolicySubsystem-no-strategy.xml\ + PolicyClass.xml +AUDIO_VALUE = false +LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE) + +include $(BUILD_PREBUILT) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) ######### Policy PFW Settings - No Output ######### @@ -130,12 +143,6 @@ LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoOutputDevice.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy -LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml \ - PolicySubsystem-no-strategy.xml \ - PolicyClass.xml \ - ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml @@ -143,9 +150,10 @@ PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoOutput/device_for_strategies.pfw \ $(LOCAL_PATH)/Settings/device_for_input_source.pfw \ - $(LOCAL_PATH)/Settings/volumes.pfw - + $(LOCAL_PATH)/Settings/volumes.pfw +LOCAL_REQUIRED_MODULES := libpolicy-subsystem include $(BUILD_PFW_SETTINGS) + endif # ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable) ######### Policy PFW Settings - No Input ######### ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) @@ -156,12 +164,6 @@ LOCAL_MODULE_STEM := PolicyConfigurableDomains-NoInputDevice.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy -LOCAL_REQUIRED_MODULES := \ - audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml \ - PolicySubsystem-no-strategy.xml \ - PolicyClass.xml \ - ParameterFrameworkConfigurationPolicy.xml PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml @@ -169,7 +171,7 @@ PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml PFW_EDD_FILES := \ $(LOCAL_PATH)/SettingsNoInput/device_for_input_source.pfw \ $(LOCAL_PATH)/Settings/volumes.pfw - +LOCAL_REQUIRED_MODULES := libpolicy-subsystem include $(BUILD_PFW_SETTINGS) endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk index 20ca8e2195..7304ec2005 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Car/Android.mk @@ -30,27 +30,16 @@ LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy +LOCAL_REQUIRED_MODULES := libpolicy-subsystem PFW_EDD_FILES := \ - $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ - $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ - $(LOCAL_PATH)/../Settings/volumes.pfw - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(PFW_EDD_FILES) - -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml \ - PolicyClass.xml \ - audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml \ - ParameterFrameworkConfigurationPolicy.xml + $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ + $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ + $(LOCAL_PATH)/../Settings/volumes.pfw PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml - PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml - PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) include $(BUILD_PFW_SETTINGS) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk index 8fa8f0a5b2..f5eb7d1f45 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/CarEmu/Android.mk @@ -30,27 +30,15 @@ LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy +LOCAL_REQUIRED_MODULES := libpolicy-subsystem PFW_EDD_FILES := \ - $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ - $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ - $(LOCAL_PATH)/../Settings/volumes.pfw - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(PFW_EDD_FILES) - -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml \ - PolicyClass.xml \ - audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml \ - ParameterFrameworkConfigurationPolicy.xml - + $(LOCAL_PATH)/Settings/device_for_product_strategies.pfw \ + $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ + $(LOCAL_PATH)/../Settings/volumes.pfw PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml - PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml - PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) include $(BUILD_PFW_SETTINGS) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk index d1845b83f9..0b20781491 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Phone/Android.mk @@ -29,6 +29,7 @@ LOCAL_MODULE_STEM := PolicyConfigurableDomains.xml LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Settings/Policy +LOCAL_REQUIRED_MODULES := libpolicy-subsystem PFW_EDD_FILES := \ $(LOCAL_PATH)/../Settings/device_for_input_source.pfw \ @@ -43,22 +44,9 @@ PFW_EDD_FILES := \ $(LOCAL_PATH)/Settings/device_for_product_strategy_transmitted_through_speaker.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_rerouting.pfw \ $(LOCAL_PATH)/Settings/device_for_product_strategy_patch.pfw - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(PFW_EDD_FILES) - -LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml \ - PolicyClass.xml \ - audio_policy_engine_criteria.xml \ - audio_policy_engine_criterion_types.xml \ - ParameterFrameworkConfigurationPolicy.xml - PFW_CRITERION_TYPES_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criterion_types.xml PFW_CRITERIA_FILE := $(TARGET_OUT_VENDOR_ETC)/audio_policy_engine_criteria.xml - PFW_TOPLEVEL_FILE := $(TARGET_OUT_VENDOR_ETC)/parameter-framework/ParameterFrameworkConfigurationPolicy.xml - PFW_SCHEMAS_DIR := $(PFW_DEFAULT_SCHEMAS_DIR) include $(BUILD_PFW_SETTINGS) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk index 4706d7d61e..b060524859 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk @@ -1,7 +1,5 @@ LOCAL_PATH := $(call my-dir) -ifneq ($(USE_CUSTOM_PARAMETER_FRAMEWORK), true) - include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional @@ -40,9 +38,6 @@ LOCAL_STATIC_LIBRARIES := \ libpfw_utility \ libaudiopolicycomponents -LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libpolicy-subsystem include $(BUILD_SHARED_LIBRARY) - -endif # ifneq ($(USE_CUSTOM_PARAMETER_FRAMEWORK), true) diff --git a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py index 0fb70a6c9d..a63c8587a8 100755 --- a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py +++ b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py @@ -101,8 +101,16 @@ def generateXmlCriterionTypesFile(criterionTypes, addressCriteria, criterionType for criterion_name, values_list in addressCriteria.items(): for criterion_type in criterion_types_root.findall('criterion_type'): if criterion_type.get('name') == criterion_name: - values_node = ET.SubElement(criterion_type, "values") index = 0 + existing_values_node = criterion_type.find("values") + if existing_values_node is not None: + for existing_value in existing_values_node.findall('value'): + if existing_value.get('numerical') == str(1 << index): + index += 1 + values_node = existing_values_node + else: + values_node = ET.SubElement(criterion_type, "values") + for value in values_list: value_node = ET.SubElement(values_node, "value", literal=value) value_node.set('numerical', str(1 << index)) @@ -240,4 +248,4 @@ def main(): # If this file is directly executed if __name__ == "__main__": - exit(main()) + sys.exit(main()) diff --git a/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py index ee70b2613f..af40602eca 100755 --- a/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py +++ b/services/audiopolicy/engineconfigurable/tools/buildStrategiesStructureFile.py @@ -136,4 +136,4 @@ def main(): # If this file is directly executed if __name__ == "__main__": - exit(main()) + sys.exit(main()) diff --git a/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk b/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk index 2b8646962b..ac60ef7556 100644 --- a/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk +++ b/services/audiopolicy/engineconfigurable/tools/build_audio_pfw_settings.mk @@ -1,10 +1,13 @@ LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES += \ + $(HOST_OUT_EXECUTABLES)/domainGeneratorPolicy.py \ + $(PFW_TOPLEVEL_FILE) $(PFW_CRITERIA_FILE) $(PFW_CRITERION_TYPES_FILE) include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(PFW_CRITERION_TYPES_FILE) -$(LOCAL_BUILT_MODULE): MY_TOOL := domainGeneratorPolicy.py +$(LOCAL_BUILT_MODULE): MY_TOOL := $(HOST_OUT_EXECUTABLES)/domainGeneratorPolicy.py $(LOCAL_BUILT_MODULE): MY_TOPLEVEL_FILE := $(PFW_TOPLEVEL_FILE) $(LOCAL_BUILT_MODULE): MY_CRITERIA_FILE := $(PFW_CRITERIA_FILE) $(LOCAL_BUILT_MODULE): MY_TUNING_FILE := $(PFW_TUNING_FILE) @@ -12,7 +15,7 @@ $(LOCAL_BUILT_MODULE): MY_EDD_FILES := $(PFW_EDD_FILES) $(LOCAL_BUILT_MODULE): MY_DOMAIN_FILES := $(PFW_DOMAIN_FILES) $(LOCAL_BUILT_MODULE): MY_SCHEMAS_DIR := $(PFW_SCHEMAS_DIR) $(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(PFW_CRITERION_TYPES_FILE) -$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) domainGeneratorPolicy.py +$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) "$(MY_TOOL)" --validate \ --toplevel-config "$(MY_TOPLEVEL_FILE)" \ diff --git a/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py b/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py index 7929402030..4dec9a2bc8 100755 --- a/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py +++ b/services/audiopolicy/engineconfigurable/tools/domainGeneratorPolicy.py @@ -129,7 +129,7 @@ def parseCriteriaAndCriterionTypes(criteriaFile, criterionTypesFile): criterion_values = [] values_node = criterion_types.find('values') - if values_node: + if values_node is not None: for value in values_node.findall('value'): criterion_values.append(value.get('literal')) @@ -265,4 +265,4 @@ def main(): # If this file is directly executed if __name__ == "__main__": - exit(main()) + sys.exit(main()) diff --git a/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk index eebdfd63f0..dab5a0f662 100644 --- a/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk +++ b/services/audiopolicy/engineconfigurable/tools/provision_criterion_types_from_android_headers.mk @@ -1,15 +1,17 @@ LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES += \ + $(HOST_OUT_EXECUTABLES)/buildPolicyCriterionTypes.py \ + $(CRITERION_TYPES_FILE) $(AUDIO_POLICY_CONFIGURATION_FILE) \ + $(ANDROID_AUDIO_BASE_HEADER_FILE) include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): MY_CRITERION_TYPES_FILE := $(CRITERION_TYPES_FILE) $(LOCAL_BUILT_MODULE): MY_ANDROID_AUDIO_BASE_HEADER_FILE := $(ANDROID_AUDIO_BASE_HEADER_FILE) $(LOCAL_BUILT_MODULE): MY_AUDIO_POLICY_CONFIGURATION_FILE := $(AUDIO_POLICY_CONFIGURATION_FILE) -$(LOCAL_BUILT_MODULE): MY_CRITERION_TOOL := $(HOST_OUT)/bin/buildPolicyCriterionTypes.py -$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) \ - $(CRITERION_TYPES_FILE) \ - $(ANDROID_AUDIO_BASE_HEADER_FILE) +$(LOCAL_BUILT_MODULE): MY_CRITERION_TOOL := $(HOST_OUT_EXECUTABLES)/buildPolicyCriterionTypes.py +$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) "$(MY_CRITERION_TOOL)" \ --androidaudiobaseheader "$(MY_ANDROID_AUDIO_BASE_HEADER_FILE)" \ diff --git a/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk b/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk index 72c938c151..f2b1a196a1 100644 --- a/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk +++ b/services/audiopolicy/engineconfigurable/tools/provision_strategies_structure.mk @@ -1,15 +1,15 @@ LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES += \ + $(HOST_OUT_EXECUTABLES)/buildStrategiesStructureFile.py \ + $(STRATEGIES_STRUCTURE_FILE) $(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE) include $(BUILD_SYSTEM)/base_rules.mk $(LOCAL_BUILT_MODULE): MY_STRATEGIES_STRUCTURE_FILE := $(STRATEGIES_STRUCTURE_FILE) $(LOCAL_BUILT_MODULE): MY_AUDIO_POLICY_ENGINE_CONFIGURATION_FILE := $(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE) -$(LOCAL_BUILT_MODULE): MY_PROVISION_TOOL := $(HOST_OUT)/bin/buildStrategiesStructureFile.py -$(LOCAL_BUILT_MODULE): $(LOCAL_REQUIRED_MODULES) $(LOCAL_ADDITIONAL_DEPENDENCIES) \ - buildStrategiesStructureFile.py \ - $(STRATEGIES_STRUCTURE_FILE) \ - $(AUDIO_POLICY_ENGINE_CONFIGURATION_FILE) +$(LOCAL_BUILT_MODULE): MY_PROVISION_TOOL := $(HOST_OUT_EXECUTABLES)/buildStrategiesStructureFile.py +$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES) "$(MY_PROVISION_TOOL)" \ --audiopolicyengineconfigurationfile "$(MY_AUDIO_POLICY_ENGINE_CONFIGURATION_FILE)" \ -- GitLab From a1e9bbb1dfcc8a2967ed75c5543c85751da00ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 3 Apr 2019 13:32:14 +0200 Subject: [PATCH 1305/1530] audiopolicy: engineconfigurable: fix phone configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 130284799 Test: make Change-Id: I84230302af0b22dfc5b279df14c69c66836e6851 Signed-off-by: François Gaffie --- .../config/example/phone/audio_policy_engine_configuration.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml index 4ca33b4525..3faf9b9307 100644 --- a/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml +++ b/services/audiopolicy/engineconfigurable/config/example/phone/audio_policy_engine_configuration.xml @@ -17,6 +17,8 @@ + + -- GitLab From ae5b0043994b5a5410bde2361164c9457ff8e5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 3 Apr 2019 13:32:39 +0200 Subject: [PATCH 1306/1530] audiopolicy: engineconfigurable: update structure for Q MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 130284799 Test: make Change-Id: I8cb2a9e60c9ad55829fa5d7caa7bcd0cf444dc05 Signed-off-by: François Gaffie --- .../common/Structure/PolicySubsystem-CommonTypes.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml index 56c5ed3684..d17c02146d 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml @@ -74,6 +74,8 @@ + + @@ -128,8 +130,10 @@ - - + + + + -- GitLab From 270dd6a1a6fc700dde00f982dcd87bf98e4b73a4 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Sat, 6 Apr 2019 04:41:15 -0700 Subject: [PATCH 1307/1530] Make mediaserver listen to Codec2 services' deaths Test: Kill media.extractor or mediaswcodec and observe logcat Bug: 130071409 Change-Id: Ie785642da99dfe4ff13e78d878817dae3de9f34a --- media/codec2/hidl/1.0/utils/Android.bp | 1 + media/codec2/hidl/client/Android.bp | 1 + media/codec2/hidl/client/client.cpp | 188 +++++++++--------- .../hidl/client/include/codec2/hidl/client.h | 11 + media/libmediaplayerservice/Android.bp | 2 + media/libmediaplayerservice/DeathNotifier.cpp | 80 ++++++++ media/libmediaplayerservice/DeathNotifier.h | 48 +++++ media/libmediaplayerservice/DeathRecipient.h | 59 ++++++ .../MediaPlayerService.cpp | 161 ++++++--------- .../MediaPlayerService.h | 35 +--- .../MediaRecorderClient.cpp | 172 ++++++++-------- .../MediaRecorderClient.h | 37 +--- 12 files changed, 442 insertions(+), 353 deletions(-) create mode 100644 media/libmediaplayerservice/DeathNotifier.cpp create mode 100644 media/libmediaplayerservice/DeathNotifier.h create mode 100644 media/libmediaplayerservice/DeathRecipient.h diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index b2a5feef1b..b73f0c87ca 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -99,6 +99,7 @@ cc_library { export_shared_lib_headers: [ "android.hardware.media.c2@1.0", "libcodec2", + "libcodec2_vndk", "libhidlbase", "libstagefright_bufferpool@2.0", "libui", diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index a174008b09..6038a4043f 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -31,6 +31,7 @@ cc_library { export_shared_lib_headers: [ "libcodec2", "libcodec2_hidl_client@1.0", + "libcodec2_vndk", ], } diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 53adbbc6d9..6aca4a321d 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -75,82 +75,11 @@ namespace /* unnamed */ { // c2_status_t value that corresponds to hwbinder transaction failure. constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED; -// Returns the list of IComponentStore service names that are available on the -// device. This list is specified at the build time in manifest files. -// Note: A software service will have "_software" as a suffix. -std::vector const& getServiceNames() { - static std::vector sServiceNames{[]() { - using ::android::hardware::media::c2::V1_0::IComponentStore; - using ::android::hidl::manager::V1_2::IServiceManager; - - while (true) { - sp serviceManager = IServiceManager::getService(); - CHECK(serviceManager) << "Hardware service manager is not running."; - - // There are three categories of services based on names. - std::vector defaultNames; // Prefixed with "default" - std::vector vendorNames; // Prefixed with "vendor" - std::vector otherNames; // Others - Return transResult; - transResult = serviceManager->listManifestByInterface( - IComponentStore::descriptor, - [&defaultNames, &vendorNames, &otherNames]( - hidl_vec const& instanceNames) { - for (hidl_string const& instanceName : instanceNames) { - char const* name = instanceName.c_str(); - if (strncmp(name, "default", 7) == 0) { - defaultNames.emplace_back(name); - } else if (strncmp(name, "vendor", 6) == 0) { - vendorNames.emplace_back(name); - } else { - otherNames.emplace_back(name); - } - } - }); - if (transResult.isOk()) { - // Sort service names in each category. - std::sort(defaultNames.begin(), defaultNames.end()); - std::sort(vendorNames.begin(), vendorNames.end()); - std::sort(otherNames.begin(), otherNames.end()); - - // Concatenate the three lists in this order: default, vendor, - // other. - std::vector& names = defaultNames; - names.reserve(names.size() + vendorNames.size() + otherNames.size()); - names.insert(names.end(), - std::make_move_iterator(vendorNames.begin()), - std::make_move_iterator(vendorNames.end())); - names.insert(names.end(), - std::make_move_iterator(otherNames.begin()), - std::make_move_iterator(otherNames.end())); - - // Summarize to logcat. - if (names.empty()) { - LOG(INFO) << "No Codec2 services declared in the manifest."; - } else { - std::stringstream stringOutput; - stringOutput << "Available Codec2 services:"; - for (std::string const& name : names) { - stringOutput << " \"" << name << "\""; - } - LOG(INFO) << stringOutput.str(); - } - - return names; - } - LOG(ERROR) << "Could not retrieve the list of service instances of " - << IComponentStore::descriptor - << ". Retrying..."; - } - }()}; - return sServiceNames; -} - -// Searches for a name in getServiceNames() and returns the index found. If the +// 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(). +// GetServiceNames().size(). size_t getServiceIndex(char const* name) { - std::vector const& names = getServiceNames(); + std::vector const& names = Codec2Client::GetServiceNames(); size_t i = 0; for (; i < names.size(); ++i) { if (name == names[i]) { @@ -175,17 +104,14 @@ class Codec2Client::Cache { std::vector mTraits; std::once_flag mTraitsInitializationFlag; - // The index of the service. This is based on getServiceNames(). + // The index of the service. This is based on GetServiceNames(). size_t mIndex; - // A "valid" cache object must have its mIndex set with init(). - bool mValid{false}; // Called by s() exactly once to initialize the cache. The index must be a - // valid index into the vector returned by getServiceNames(). Calling + // valid index into the vector returned by GetServiceNames(). Calling // init(index) will associate the cache to the service with name - // getServiceNames()[index]. + // GetServiceNames()[index]. void init(size_t index) { mIndex = index; - mValid = true; } public: @@ -195,7 +121,6 @@ public: // If the service is unavailable but listed in the manifest, this function // will block indefinitely. std::shared_ptr getClient() { - CHECK(mValid) << "Uninitialized cache"; std::scoped_lock lock{mClientMutex}; if (!mClient) { mClient = Codec2Client::_CreateFromIndex(mIndex); @@ -208,7 +133,6 @@ public: // // Note: This function is called only by ForAllServices(). void invalidate() { - CHECK(mValid) << "Uninitialized cache"; std::scoped_lock lock{mClientMutex}; mClient = nullptr; } @@ -216,7 +140,6 @@ public: // Returns a list of traits for components supported by the service. This // list is cached. std::vector const& getTraits() { - CHECK(mValid) << "Uninitialized cache"; std::call_once(mTraitsInitializationFlag, [this]() { bool success{false}; // Spin until _listComponents() is successful. @@ -229,7 +152,7 @@ public: using namespace std::chrono_literals; static constexpr auto kServiceRetryPeriod = 5s; LOG(INFO) << "Failed to retrieve component traits from service " - "\"" << getServiceNames()[mIndex] << "\". " + "\"" << GetServiceNames()[mIndex] << "\". " "Retrying..."; std::this_thread::sleep_for(kServiceRetryPeriod); } @@ -240,7 +163,7 @@ public: // List() returns the list of all caches. static std::vector& List() { static std::vector sCaches{[]() { - size_t numServices = getServiceNames().size(); + size_t numServices = GetServiceNames().size(); std::vector caches(numServices); for (size_t i = 0; i < numServices; ++i) { caches[i].init(i); @@ -610,8 +533,12 @@ Codec2Client::Codec2Client(const sp& base, } } +sp const& Codec2Client::getBase() const { + return mBase; +} + std::string const& Codec2Client::getServiceName() const { - return getServiceNames()[mServiceIndex]; + return GetServiceNames()[mServiceIndex]; } c2_status_t Codec2Client::createComponent( @@ -807,15 +734,94 @@ std::shared_ptr return std::make_shared(mBase); }; +std::vector const& Codec2Client::GetServiceNames() { + static std::vector sServiceNames{[]() { + using ::android::hardware::media::c2::V1_0::IComponentStore; + using ::android::hidl::manager::V1_2::IServiceManager; + + while (true) { + sp serviceManager = IServiceManager::getService(); + CHECK(serviceManager) << "Hardware service manager is not running."; + + // There are three categories of services based on names. + std::vector defaultNames; // Prefixed with "default" + std::vector vendorNames; // Prefixed with "vendor" + std::vector otherNames; // Others + Return transResult; + transResult = serviceManager->listManifestByInterface( + IComponentStore::descriptor, + [&defaultNames, &vendorNames, &otherNames]( + hidl_vec const& instanceNames) { + for (hidl_string const& instanceName : instanceNames) { + char const* name = instanceName.c_str(); + if (strncmp(name, "default", 7) == 0) { + defaultNames.emplace_back(name); + } else if (strncmp(name, "vendor", 6) == 0) { + vendorNames.emplace_back(name); + } else { + otherNames.emplace_back(name); + } + } + }); + if (transResult.isOk()) { + // Sort service names in each category. + std::sort(defaultNames.begin(), defaultNames.end()); + std::sort(vendorNames.begin(), vendorNames.end()); + std::sort(otherNames.begin(), otherNames.end()); + + // Concatenate the three lists in this order: default, vendor, + // other. + std::vector& names = defaultNames; + names.reserve(names.size() + vendorNames.size() + otherNames.size()); + names.insert(names.end(), + std::make_move_iterator(vendorNames.begin()), + std::make_move_iterator(vendorNames.end())); + names.insert(names.end(), + std::make_move_iterator(otherNames.begin()), + std::make_move_iterator(otherNames.end())); + + // Summarize to logcat. + if (names.empty()) { + LOG(INFO) << "No Codec2 services declared in the manifest."; + } else { + std::stringstream stringOutput; + stringOutput << "Available Codec2 services:"; + for (std::string const& name : names) { + stringOutput << " \"" << name << "\""; + } + LOG(INFO) << stringOutput.str(); + } + + return names; + } + LOG(ERROR) << "Could not retrieve the list of service instances of " + << IComponentStore::descriptor + << ". Retrying..."; + } + }()}; + return sServiceNames; +} + std::shared_ptr Codec2Client::CreateFromService( const char* name) { size_t index = getServiceIndex(name); - return index == getServiceNames().size() ? + return index == GetServiceNames().size() ? nullptr : _CreateFromIndex(index); } +std::vector> Codec2Client:: + CreateFromAllServices() { + std::vector> clients( + GetServiceNames().size()); + for (size_t i = GetServiceNames().size(); i > 0; ) { + --i; + clients[i] = _CreateFromIndex(i); + } + return clients; +} + std::shared_ptr Codec2Client::_CreateFromIndex(size_t index) { - std::string const& name = getServiceNames()[index]; + std::string const& name = GetServiceNames()[index]; LOG(INFO) << "Creating a Codec2 client to service \"" << name << "\""; sp baseStore = Base::getService(name); CHECK(baseStore) << "Codec2 service \"" << name << "\"" @@ -958,17 +964,17 @@ std::shared_ptr Codec2Client::CreateInputSurface( if (inputSurfaceSetting == 0) { return nullptr; } - size_t index = getServiceNames().size(); + size_t index = GetServiceNames().size(); if (serviceName) { index = getServiceIndex(serviceName); - if (index == getServiceNames().size()) { + if (index == GetServiceNames().size()) { LOG(DEBUG) << "CreateInputSurface -- invalid service name: \"" << serviceName << "\""; } } std::shared_ptr inputSurface; - if (index != getServiceNames().size()) { + if (index != GetServiceNames().size()) { std::shared_ptr client = Cache::List()[index].getClient(); if (client->createInputSurface(&inputSurface) == C2_OK) { return inputSurface; diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 18517527fa..03db5154a1 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -143,6 +143,8 @@ struct Codec2Client : public Codec2ConfigurableClient { typedef Codec2Client Store; + sp const& getBase() const; + std::string const& getServiceName() const; c2_status_t createComponent( @@ -165,8 +167,17 @@ struct Codec2Client : public Codec2ConfigurableClient { std::shared_ptr getParamReflector(); + // Returns the list of IComponentStore service names that are available on + // the device. This list is specified at the build time in manifest files. + // Note: A software service will have "_software" as a suffix. + static std::vector const& GetServiceNames(); + + // Create a service with a given service name. static std::shared_ptr CreateFromService(char const* name); + // Get clients to all services. + static std::vector> CreateFromAllServices(); + // Try to create a component with a given name from all known // IComponentStore services. static std::shared_ptr CreateComponentByName( diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp index 0776172db4..670958510c 100644 --- a/media/libmediaplayerservice/Android.bp +++ b/media/libmediaplayerservice/Android.bp @@ -2,6 +2,7 @@ cc_library_shared { srcs: [ "ActivityManager.cpp", + "DeathNotifier.cpp", "MediaPlayerFactory.cpp", "MediaPlayerService.cpp", "MediaRecorderClient.cpp", @@ -17,6 +18,7 @@ cc_library_shared { "libaudioclient", "libbinder", "libcamera_client", + "libcodec2_client", "libcrypto", "libcutils", "libdl", diff --git a/media/libmediaplayerservice/DeathNotifier.cpp b/media/libmediaplayerservice/DeathNotifier.cpp new file mode 100644 index 0000000000..d13bdf53ff --- /dev/null +++ b/media/libmediaplayerservice/DeathNotifier.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2019 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 "MediaPlayerService-DeathNotifier" +#include + +#include "DeathNotifier.h" + +namespace android { + +class DeathNotifier::DeathRecipient : + public IBinder::DeathRecipient, + public hardware::hidl_death_recipient { +public: + using Notify = DeathNotifier::Notify; + + DeathRecipient(Notify const& notify): mNotify{notify} { + } + + virtual void binderDied(wp const&) override { + mNotify(); + } + + virtual void serviceDied(uint64_t, wp const&) override { + mNotify(); + } + +private: + Notify mNotify; +}; + +DeathNotifier::DeathNotifier(sp const& service, Notify const& notify) + : mService{std::in_place_index<1>, service}, + mDeathRecipient{new DeathRecipient(notify)} { + service->linkToDeath(mDeathRecipient); +} + +DeathNotifier::DeathNotifier(sp const& service, Notify const& notify) + : mService{std::in_place_index<2>, service}, + mDeathRecipient{new DeathRecipient(notify)} { + service->linkToDeath(mDeathRecipient, 0); +} + +DeathNotifier::DeathNotifier(DeathNotifier&& other) + : mService{other.mService}, mDeathRecipient{other.mDeathRecipient} { + other.mService.emplace<0>(); + other.mDeathRecipient = nullptr; +} + +DeathNotifier::~DeathNotifier() { + switch (mService.index()) { + case 0: + break; + case 1: + std::get<1>(mService)->unlinkToDeath(mDeathRecipient); + break; + case 2: + std::get<2>(mService)->unlinkToDeath(mDeathRecipient); + break; + default: + CHECK(false) << "Corrupted service type during destruction."; + } +} + +} // namespace android + diff --git a/media/libmediaplayerservice/DeathNotifier.h b/media/libmediaplayerservice/DeathNotifier.h new file mode 100644 index 0000000000..7bc261178c --- /dev/null +++ b/media/libmediaplayerservice/DeathNotifier.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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_MEDIASERVICE_DEATHNOTIFIER_H +#define ANDROID_MEDIASERVICE_DEATHNOTIFIER_H + +#include +#include +#include + +#include + +namespace android { + +class DeathNotifier { +public: + using HBase = hidl::base::V1_0::IBase; + using Notify = std::function; + + DeathNotifier(sp const& service, Notify const& notify); + DeathNotifier(sp const& service, Notify const& notify); + DeathNotifier(DeathNotifier&& other); + ~DeathNotifier(); + +private: + std::variant, sp> mService; + + class DeathRecipient; + sp mDeathRecipient; +}; + +} // namespace android + +#endif // ANDROID_MEDIASERVICE_DEATHNOTIFIER_H + diff --git a/media/libmediaplayerservice/DeathRecipient.h b/media/libmediaplayerservice/DeathRecipient.h new file mode 100644 index 0000000000..bf5ae5c0f8 --- /dev/null +++ b/media/libmediaplayerservice/DeathRecipient.h @@ -0,0 +1,59 @@ +/* + * Copyright 2019 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_MEDIASERVICE_DEATHRECIPIENT_H +#define ANDROID_MEDIASERVICE_DEATHRECIPIENT_H + +#include +#include + +#include + +class DeathNotifier : + public IBinder::DeathRecipient, + public ::android::hardware::hidl_death_recipient { +public: + using Service = std::variant< + sp const&, + sp const&>; + + DeathNotifier(std::variant const&, + const sp& service, + const sp& listener, + int which, + const std::string& name); + DeathNotifier( + const sp& hService, + const sp& listener, + int which, + const std::string& name); + virtual ~DeathNotifier() = default; + virtual void binderDied(const wp& who); + virtual void serviceDied( + uint64_t cookie, + const wp<::android::hidl::base::V1_0::IBase>& who); + void unlinkToDeath(); + +private: + sp mService; + sp mHService; // HIDL service + wp mListener; + int mWhich; + std::string mName; +}; + +#endif // ANDROID_MEDIASERVICE_DEATHRECIPIENT_H + diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 5061024f8f..dfd3933245 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -34,7 +34,7 @@ #include -#include +#include #include #include #include @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -591,7 +592,6 @@ MediaPlayerService::Client::~Client() if (mAudioAttributes != NULL) { free(mAudioAttributes); } - clearDeathNotifiers_l(); mAudioDeviceUpdatedListener.clear(); } @@ -647,59 +647,6 @@ sp MediaPlayerService::Client::createPlayer(player_type playerT return p; } -MediaPlayerService::Client::ServiceDeathNotifier::ServiceDeathNotifier( - const sp& service, - const sp& listener, - int which) { - mService = service; - mHService = nullptr; - mListener = listener; - mWhich = which; -} - -MediaPlayerService::Client::ServiceDeathNotifier::ServiceDeathNotifier( - const sp& hService, - const sp& listener, - int which) { - mService = nullptr; - mHService = hService; - mListener = listener; - mWhich = which; -} - -MediaPlayerService::Client::ServiceDeathNotifier::~ServiceDeathNotifier() { -} - -void MediaPlayerService::Client::ServiceDeathNotifier::binderDied(const wp& /*who*/) { - sp listener = mListener.promote(); - if (listener != NULL) { - listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, mWhich); - } else { - ALOGW("listener for process %d death is gone", mWhich); - } -} - -void MediaPlayerService::Client::ServiceDeathNotifier::serviceDied( - uint64_t /* cookie */, - const wp<::android::hidl::base::V1_0::IBase>& /* who */) { - sp listener = mListener.promote(); - if (listener != NULL) { - listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, mWhich); - } else { - ALOGW("listener for process %d death is gone", mWhich); - } -} - -void MediaPlayerService::Client::ServiceDeathNotifier::unlinkToDeath() { - if (mService != nullptr) { - mService->unlinkToDeath(this); - mService = nullptr; - } else if (mHService != nullptr) { - mHService->unlinkToDeath(this); - mHService = nullptr; - } -} - void MediaPlayerService::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate( audio_io_handle_t audioIo, audio_port_handle_t deviceId) { @@ -711,19 +658,6 @@ void MediaPlayerService::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate } } -void MediaPlayerService::Client::clearDeathNotifiers_l() { - if (mExtractorDeathListener != nullptr) { - mExtractorDeathListener->unlinkToDeath(); - mExtractorDeathListener = nullptr; - } - for (const sp& codecDeathListener : mCodecDeathListeners) { - if (codecDeathListener != nullptr) { - codecDeathListener->unlinkToDeath(); - } - } - mCodecDeathListeners.clear(); -} - sp MediaPlayerService::Client::setDataSource_pre( player_type playerType) { @@ -735,66 +669,83 @@ sp MediaPlayerService::Client::setDataSource_pre( return p; } + std::vector deathNotifiers; + + // Listen to death of media.extractor service sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.extractor")); if (binder == NULL) { ALOGE("extractor service not available"); return NULL; } - sp extractorDeathListener = - new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH); - binder->linkToDeath(extractorDeathListener); + deathNotifiers.emplace_back( + binder, [l = wp(p)]() { + sp listener = l.promote(); + if (listener) { + ALOGI("media.extractor died. Sending death notification."); + listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MEDIAEXTRACTOR_PROCESS_DEATH); + } else { + ALOGW("media.extractor died without a death handler."); + } + }); - std::vector> codecDeathListeners; { using ::android::hidl::base::V1_0::IBase; - // Listen to OMX's IOmxStore/default + // Listen to death of OMX service { - sp store = ::android::hardware::media::omx::V1_0:: - IOmxStore::getService(); - if (store == nullptr) { + sp base = ::android::hardware::media::omx::V1_0:: + IOmx::getService(); + if (base == nullptr) { ALOGD("OMX service is not available"); } else { - sp codecDeathListener = - new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); - store->linkToDeath(codecDeathListener, 0); - codecDeathListeners.emplace_back(codecDeathListener); + deathNotifiers.emplace_back( + base, [l = wp(p)]() { + sp listener = l.promote(); + if (listener) { + ALOGI("OMX service died. " + "Sending death notification."); + listener->sendEvent( + MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MEDIACODEC_PROCESS_DEATH); + } else { + ALOGW("OMX service died without a death handler."); + } + }); } } - // Listen to Codec2's IComponentStore/software - // TODO: Listen to all Codec2 services. + // Listen to death of Codec2 services { - sp store = ::android::hardware::media::c2::V1_0:: - IComponentStore::getService(); - if (store == nullptr) { - ALOGD("Codec2 system service is not available"); - } else { - sp codecDeathListener = - new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); - store->linkToDeath(codecDeathListener, 0); - codecDeathListeners.emplace_back(codecDeathListener); - } - - store = ::android::hardware::media::c2::V1_0:: - IComponentStore::getService("software"); - if (store == nullptr) { - ALOGD("Codec2 swcodec service is not available"); - } else { - sp codecDeathListener = - new ServiceDeathNotifier(store, p, MEDIACODEC_PROCESS_DEATH); - store->linkToDeath(codecDeathListener, 0); - codecDeathListeners.emplace_back(codecDeathListener); + for (std::shared_ptr const& client : + Codec2Client::CreateFromAllServices()) { + sp base = client->getBase(); + deathNotifiers.emplace_back( + base, [l = wp(p), + name = std::string(client->getServiceName())]() { + sp listener = l.promote(); + if (listener) { + ALOGI("Codec2 service \"%s\" died. " + "Sending death notification.", + name.c_str()); + listener->sendEvent( + MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MEDIACODEC_PROCESS_DEATH); + } else { + ALOGW("Codec2 service \"%s\" died " + "without a death handler.", + name.c_str()); + } + }); } } } Mutex::Autolock lock(mLock); - clearDeathNotifiers_l(); - mExtractorDeathListener = extractorDeathListener; - mCodecDeathListeners.swap(codecDeathListeners); + mDeathNotifiers.clear(); + mDeathNotifiers.swap(deathNotifiers); mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p); if (!p->hardwareOutput()) { diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 26bfa71c25..49688ce997 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -30,8 +30,6 @@ #include #include -#include - #include namespace android { @@ -39,6 +37,7 @@ namespace android { struct AudioPlaybackRate; class AudioTrack; struct AVSyncSettings; +class DeathNotifier; class IDataSource; class IMediaRecorder; class IMediaMetadataRetriever; @@ -388,33 +387,6 @@ private: virtual status_t enableAudioDeviceCallback(bool enabled); private: - class ServiceDeathNotifier: - public IBinder::DeathRecipient, - public ::android::hardware::hidl_death_recipient - { - public: - ServiceDeathNotifier( - const sp& service, - const sp& listener, - int which); - ServiceDeathNotifier( - const sp& hService, - const sp& listener, - int which); - virtual ~ServiceDeathNotifier(); - virtual void binderDied(const wp& who); - virtual void serviceDied( - uint64_t cookie, - const wp<::android::hidl::base::V1_0::IBase>& who); - void unlinkToDeath(); - - private: - int mWhich; - sp mService; - sp mHService; // HIDL service - wp mListener; - }; - class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback { public: @@ -430,8 +402,6 @@ private: wp mListener; }; - void clearDeathNotifiers_l(); - friend class MediaPlayerService; Client( const sp& service, pid_t pid, @@ -506,8 +476,7 @@ private: // getMetadata clears this set. media::Metadata::Filter mMetadataUpdated; // protected by mLock - sp mExtractorDeathListener; - std::vector> mCodecDeathListeners; + std::vector mDeathNotifiers; sp mAudioDeviceUpdatedListener; #if CALLBACK_ANTAGONIZER Antagonizer* mAntagonizer; diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 9f4265b021..703da4b98f 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -18,27 +18,28 @@ #define LOG_TAG "MediaRecorderService" #include -#include -#include -#include -#include -#include -#include -#include // for property_get +#include "MediaRecorderClient.h" +#include "MediaPlayerService.h" +#include "StagefrightRecorder.h" + +#include +#include #include #include #include #include - -#include - +#include +#include +#include // for property_get +#include +#include +#include #include +#include -#include "MediaRecorderClient.h" -#include "MediaPlayerService.h" - -#include "StagefrightRecorder.h" -#include +#include +#include +#include namespace android { @@ -339,7 +340,7 @@ status_t MediaRecorderClient::release() wp client(this); mMediaPlayerService->removeMediaRecorderClient(client); } - clearDeathNotifiers_l(); + mDeathNotifiers.clear(); return NO_ERROR; } @@ -358,59 +359,6 @@ MediaRecorderClient::~MediaRecorderClient() release(); } -MediaRecorderClient::ServiceDeathNotifier::ServiceDeathNotifier( - const sp& service, - const sp& listener, - int which) { - mService = service; - mOmx = nullptr; - mListener = listener; - mWhich = which; -} - -MediaRecorderClient::ServiceDeathNotifier::ServiceDeathNotifier( - const sp& omx, - const sp& listener, - int which) { - mService = nullptr; - mOmx = omx; - mListener = listener; - mWhich = which; -} - -MediaRecorderClient::ServiceDeathNotifier::~ServiceDeathNotifier() { -} - -void MediaRecorderClient::ServiceDeathNotifier::binderDied(const wp& /*who*/) { - sp listener = mListener.promote(); - if (listener != NULL) { - listener->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, mWhich); - } else { - ALOGW("listener for process %d death is gone", mWhich); - } -} - -void MediaRecorderClient::ServiceDeathNotifier::serviceDied( - uint64_t /* cookie */, - const wp<::android::hidl::base::V1_0::IBase>& /* who */) { - sp listener = mListener.promote(); - if (listener != NULL) { - listener->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, mWhich); - } else { - ALOGW("listener for process %d death is gone", mWhich); - } -} - -void MediaRecorderClient::ServiceDeathNotifier::unlinkToDeath() { - if (mService != nullptr) { - mService->unlinkToDeath(this); - mService = nullptr; - } else if (mOmx != nullptr) { - mOmx->unlinkToDeath(this); - mOmx = nullptr; - } -} - MediaRecorderClient::AudioDeviceUpdatedNotifier::AudioDeviceUpdatedNotifier( const sp& listener) { mListener = listener; @@ -430,22 +378,11 @@ void MediaRecorderClient::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate( } } -void MediaRecorderClient::clearDeathNotifiers_l() { - if (mCameraDeathListener != nullptr) { - mCameraDeathListener->unlinkToDeath(); - mCameraDeathListener = nullptr; - } - if (mCodecDeathListener != nullptr) { - mCodecDeathListener->unlinkToDeath(); - mCodecDeathListener = nullptr; - } -} - status_t MediaRecorderClient::setListener(const sp& listener) { ALOGV("setListener"); Mutex::Autolock lock(mLock); - clearDeathNotifiers_l(); + mDeathNotifiers.clear(); if (mRecorder == NULL) { ALOGE("recorder is not initialized"); return NO_INIT; @@ -463,20 +400,73 @@ status_t MediaRecorderClient::setListener(const sp& listen // If the device does not have a camera, do not create a death listener for it. if (binder != NULL) { sCameraVerified = true; - mCameraDeathListener = new ServiceDeathNotifier(binder, listener, - MediaPlayerService::CAMERA_PROCESS_DEATH); - binder->linkToDeath(mCameraDeathListener); + mDeathNotifiers.emplace_back( + binder, [l = wp(listener)](){ + sp listener = l.promote(); + if (listener) { + ALOGV("media.camera service died. " + "Sending death notification."); + listener->notify( + MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MediaPlayerService::CAMERA_PROCESS_DEATH); + } else { + ALOGW("media.camera service died without a death handler."); + } + }); } sCameraChecked = true; - sp omx = IOmx::getService(); - if (omx == nullptr) { - ALOGE("IOmx service is not available"); - return NO_INIT; + { + using ::android::hidl::base::V1_0::IBase; + + // Listen to OMX's IOmxStore/default + { + sp base = ::android::hardware::media::omx::V1_0:: + IOmx::getService(); + if (base == nullptr) { + ALOGD("OMX service is not available"); + } else { + mDeathNotifiers.emplace_back( + base, [l = wp(listener)](){ + sp listener = l.promote(); + if (listener) { + ALOGV("OMX service died. " + "Sending death notification."); + listener->notify( + MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MediaPlayerService::MEDIACODEC_PROCESS_DEATH); + } else { + ALOGW("OMX service died without a death handler."); + } + }); + } + } + + // Listen to Codec2's IComponentStore instances + { + for (std::shared_ptr const& client : + Codec2Client::CreateFromAllServices()) { + sp base = client->getBase(); + mDeathNotifiers.emplace_back( + base, [l = wp(listener), + name = std::string(client->getServiceName())]() { + sp listener = l.promote(); + if (listener) { + ALOGV("Codec2 service \"%s\" died. " + "Sending death notification", + name.c_str()); + listener->notify( + MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, + MediaPlayerService::MEDIACODEC_PROCESS_DEATH); + } else { + ALOGW("Codec2 service \"%s\" died " + "without a death handler", + name.c_str()); + } + }); + } + } } - mCodecDeathListener = new ServiceDeathNotifier(omx, listener, - MediaPlayerService::MEDIACODEC_PROCESS_DEATH); - omx->linkToDeath(mCodecDeathListener, 0); mAudioDeviceUpdatedNotifier = new AudioDeviceUpdatedNotifier(listener); mRecorder->setAudioDeviceCallback(mAudioDeviceUpdatedNotifier); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index e698819854..9e0f87779f 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -18,10 +18,12 @@ #ifndef ANDROID_MEDIARECORDERCLIENT_H #define ANDROID_MEDIARECORDERCLIENT_H +#include "DeathNotifier.h" + #include #include -#include +#include namespace android { @@ -31,34 +33,6 @@ class ICameraRecordingProxy; class MediaRecorderClient : public BnMediaRecorder { - typedef ::android::hardware::media::omx::V1_0::IOmx IOmx; - - class ServiceDeathNotifier : - public IBinder::DeathRecipient, - public ::android::hardware::hidl_death_recipient - { - public: - ServiceDeathNotifier( - const sp& service, - const sp& listener, - int which); - ServiceDeathNotifier( - const sp& omx, - const sp& listener, - int which); - virtual ~ServiceDeathNotifier(); - virtual void binderDied(const wp& who); - virtual void serviceDied( - uint64_t cookie, - const wp<::android::hidl::base::V1_0::IBase>& who); - void unlinkToDeath(); - private: - int mWhich; - sp mService; - sp mOmx; - wp mListener; - }; - class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback { public: @@ -71,8 +45,6 @@ class MediaRecorderClient : public BnMediaRecorder wp mListener; }; - void clearDeathNotifiers_l(); - public: virtual status_t setCamera(const sp& camera, const sp& proxy); @@ -122,8 +94,7 @@ private: const String16& opPackageName); virtual ~MediaRecorderClient(); - sp mCameraDeathListener; - sp mCodecDeathListener; + std::vector mDeathNotifiers; sp mAudioDeviceUpdatedNotifier; pid_t mPid; -- GitLab From 97aaad8ef2770d3f58d17d7e88fc43adc266e032 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Thu, 18 Apr 2019 12:30:24 +0530 Subject: [PATCH 1308/1530] libeffects: Corrected the Copy_Float in LVM_Process.c to handle MC Copy_Float function present after the LVDBE_Process function in LVM_Process.c was not taking into consideration NumChannels. Corrected this. Test: discovered by code inspection, CTS, Solotester Bug: 130781096 Change-Id: Ib0bccd36f3986463b602fa20fc09ab0741f7e39e --- media/libeffects/lvm/lib/Bundle/src/LVM_Process.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c index 8d30a61544..bc666a9445 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Process.c @@ -230,9 +230,15 @@ LVM_ReturnStatus_en LVM_Process(LVM_Handle_t hInstance, */ if (pToProcess != pProcessed) { +#ifdef SUPPORT_MC + Copy_Float(pToProcess, /* Source */ + pProcessed, /* Destination */ + (LVM_INT16)(NrChannels * NrFrames)); /* Copy all samples */ +#else Copy_Float(pToProcess, /* Source */ pProcessed, /* Destination */ (LVM_INT16)(2 * SampleCount)); /* Left and right */ +#endif } /* -- GitLab From a90adf0a05c7582227493cc90dcaa917a6f1a445 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 17 Apr 2019 13:26:15 -0700 Subject: [PATCH 1309/1530] Move to android_mallopt for malloc debug calls. Bug: 130028357 Test: Enable backtrace for mediaserver, run dumpsys media.player -m Change-Id: I75adddfc5048f732ee7ec27c17192efd5857af38 Merged-In: I75adddfc5048f732ee7ec27c17192efd5857af38 (cherry picked from commit bd0195ecacaa5381278d8f96b51af36631dc0cd9) --- media/utils/Android.bp | 4 ++++ media/utils/MemoryLeakTrackUtil.cpp | 31 ++++++++++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/media/utils/Android.bp b/media/utils/Android.bp index a11602b97a..3adb40f5a3 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -49,6 +49,10 @@ cc_library { }, }, + include_dirs: [ + // For android_mallopt definitions. + "bionic/libc/private" + ], local_include_dirs: ["include"], export_include_dirs: ["include"], } diff --git a/media/utils/MemoryLeakTrackUtil.cpp b/media/utils/MemoryLeakTrackUtil.cpp index 18f5f2502c..2988b52615 100644 --- a/media/utils/MemoryLeakTrackUtil.cpp +++ b/media/utils/MemoryLeakTrackUtil.cpp @@ -22,6 +22,8 @@ #include "media/MemoryLeakTrackUtil.h" #include +#include + /* * The code here originally resided in MediaPlayerService.cpp */ @@ -47,29 +49,22 @@ extern std::string backtrace_string(const uintptr_t* frames, size_t frame_count) namespace android { -extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, - size_t* infoSize, size_t* totalMemory, size_t* backtraceSize); - -extern "C" void free_malloc_leak_info(uint8_t* info); - std::string dumpMemoryAddresses(size_t limit) { - uint8_t *info; - size_t overallSize; - size_t infoSize; - size_t totalMemory; - size_t backtraceSize; - get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize); + android_mallopt_leak_info_t leak_info; + if (!android_mallopt(M_GET_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))) { + return ""; + } size_t count; - if (info == nullptr || overallSize == 0 || infoSize == 0 - || (count = overallSize / infoSize) == 0) { + if (leak_info.buffer == nullptr || leak_info.overall_size == 0 || leak_info.info_size == 0 + || (count = leak_info.overall_size / leak_info.info_size) == 0) { ALOGD("no malloc info, libc.debug.malloc.program property should be set"); - return std::string(); + return ""; } std::ostringstream oss; - oss << totalMemory << " bytes in " << count << " allocations\n"; + oss << leak_info.total_memory << " bytes in " << count << " allocations\n"; oss << " ABI: '" ABI_STRING "'" << "\n\n"; if (count > limit) count = limit; @@ -83,14 +78,14 @@ std::string dumpMemoryAddresses(size_t limit) uintptr_t backtrace[]; }; - const AllocEntry * const e = (AllocEntry *)(info + i * infoSize); + const AllocEntry * const e = (AllocEntry *)(leak_info.buffer + i * leak_info.info_size); oss << (e->size * e->allocations) << " bytes ( " << e->size << " bytes * " << e->allocations << " allocations )\n"; - oss << backtrace_string(e->backtrace, backtraceSize) << "\n"; + oss << backtrace_string(e->backtrace, leak_info.backtrace_size) << "\n"; } oss << "\n"; - free_malloc_leak_info(info); + android_mallopt(M_FREE_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info)); return oss.str(); } -- GitLab From 7ed66a1a879399edf356e54f09d59c1a7ca2d5f7 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 18 Apr 2019 13:20:30 -0700 Subject: [PATCH 1310/1530] AudioFlinger: allow small requested capacity for FAST path AudioFlinger used to reject requests for the low latency FAST path when the capacity was != mPipeFramesP2. But if the caller requests < mPipeFramesP2 than we can just bump it up to mPipeFramesP2 because the API allows that. For example, on Pixel, mPipeFramesP2 is 4096 Bug: 80308183 Test: adb shell input_monitor_callback -pl -b4000 -m1 Test: should see performance mode 12 for LOW_LATENCY Change-Id: I987067910de3194fb52370d0ea1626cffad6248c --- services/audioflinger/Threads.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 59ced26797..711a6ddfc2 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -7454,8 +7454,10 @@ sp AudioFlinger::RecordThread::createRe // we formerly checked for a callback handler (non-0 tid), // but that is no longer required for TRANSFER_OBTAIN mode // - // frame count is not specified, or is exactly the pipe depth - ((frameCount == 0) || (frameCount == mPipeFramesP2)) && + // Frame count is not specified (0), or is less than or equal the pipe depth. + // It is OK to provide a higher capacity than requested. + // We will force it to mPipeFramesP2 below. + (frameCount <= mPipeFramesP2) && // PCM data audio_is_linear_pcm(format) && // hardware format -- GitLab From 5ee8d120318266334aabc21d4f676813b3ac288f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 19 Apr 2019 15:41:52 -0700 Subject: [PATCH 1311/1530] audio policy: fix HOTWORD capture device Use only devices attached to primary HAL when capturing for audio source HOTWORD as they have to be accessible by the audio DSP. Bug: 123311424 Test: capture use cases: phone calls, Duo, Camcorder, Assistant. Change-Id: Ib54622b69612e6ed12f36193d4b1918ba892a2cd --- .../audiopolicy/enginedefault/src/Engine.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp index 4135f01f7c..04170ac3ee 100644 --- a/services/audiopolicy/enginedefault/src/Engine.cpp +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -506,7 +506,9 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons const DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices(); const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs(); audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; - + sp primaryOutput = outputs.getPrimaryOutput(); + audio_devices_t availablePrimaryDeviceTypes = availableInputDevices.getDeviceTypesFromHwModule( + primaryOutput->getModuleHandle()) & ~AUDIO_DEVICE_BIT_IN; uint32_t device = AUDIO_DEVICE_NONE; // when a call is active, force device selection to match source VOICE_COMMUNICATION @@ -528,13 +530,6 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons } switch (inputSource) { - case AUDIO_SOURCE_VOICE_UPLINK: - if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { - device = AUDIO_DEVICE_IN_VOICE_CALL; - break; - } - break; - case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { @@ -558,9 +553,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons // to voice call path. if ((getPhoneState() == AUDIO_MODE_IN_CALL) && (availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { - sp primaryOutput = outputs.getPrimaryOutput(); - availableDeviceTypes = availableInputDevices.getDeviceTypesFromHwModule( - primaryOutput->getModuleHandle()) & ~AUDIO_DEVICE_BIT_IN; + availableDeviceTypes = availablePrimaryDeviceTypes; } switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) { @@ -597,6 +590,9 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons case AUDIO_SOURCE_VOICE_RECOGNITION: case AUDIO_SOURCE_UNPROCESSED: case AUDIO_SOURCE_HOTWORD: + if (inputSource == AUDIO_SOURCE_HOTWORD) { + availableDeviceTypes = availablePrimaryDeviceTypes; + } if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO && availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; @@ -622,6 +618,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons break; case AUDIO_SOURCE_VOICE_DOWNLINK: case AUDIO_SOURCE_VOICE_CALL: + case AUDIO_SOURCE_VOICE_UPLINK: if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { device = AUDIO_DEVICE_IN_VOICE_CALL; } -- GitLab From 8300dc1549954d8e50431ca9da0cf0601a8e14fa Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Fri, 19 Apr 2019 16:08:44 -0700 Subject: [PATCH 1312/1530] camera2 vndk: Don't try to retrieve ACameraDevice in CallbackHandler. Its possible that the device is closing, however, it hasn't stopped its looper yet. In that case, if we receive a callback, we'll receive a null ACameraDevice. Cache the camera id during CallbackHandler's construction instead, like the ndk does. Bug: 130910407 Test: AImageReaderVendorTest Change-Id: Ia7cd40ff1ce4fe52abb5528c68e3557523a5367d Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 18 ++++++++++-------- camera/ndk/ndk_vendor/impl/ACameraDevice.h | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index b18c897c6c..1fdff40841 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -87,7 +87,7 @@ CameraDevice::CameraDevice( __FUNCTION__, strerror(-err), err); setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE); } - mHandler = new CallbackHandler(); + mHandler = new CallbackHandler(id); mCbLooper->registerHandler(mHandler); const CameraMetadata& metadata = mChars->getInternalData(); @@ -918,6 +918,8 @@ CameraDevice::onCaptureErrorLocked( return; } +CameraDevice::CallbackHandler::CallbackHandler(const char *id) : mId(id) { } + void CameraDevice::CallbackHandler::onMessageReceived( const sp &msg) { switch (msg->what()) { @@ -1012,9 +1014,9 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } sp session(static_cast(obj.get())); - ACameraDevice* device = session->getDevice(); mCachedSessions.push(session); sp requestSp = nullptr; + const char *id_cstr = mId.c_str(); switch (msg->what()) { case kWhatCaptureStart: case kWhatCaptureResult: @@ -1063,7 +1065,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( ALOGE("%s: Cannot find timestamp!", __FUNCTION__); return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onStart)(context, session.get(), request, timestamp); freeACaptureRequest(request); break; @@ -1086,7 +1088,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } sp result(static_cast(obj.get())); - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onResult)(context, session.get(), request, result.get()); freeACaptureRequest(request); break; @@ -1139,7 +1141,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get()); } - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onResult)(context, session.get(), request, result.get(), physicalResultInfo.size(), physicalCameraIdPtrs.data(), physicalMetadataCopyPtrs.data()); @@ -1168,7 +1170,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( static_cast(obj.get())); ACameraCaptureFailure* failure = static_cast(failureSp.get()); - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onFail)(context, session.get(), request, failure); freeACaptureRequest(request); break; @@ -1201,7 +1203,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( failure.physicalCameraId = nullptr; } failure.captureFailure = *failureSp; - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onFail)(context, session.get(), request, &failure); freeACaptureRequest(request); break; @@ -1278,7 +1280,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( return; } - ACaptureRequest* request = allocateACaptureRequest(requestSp, device->getId()); + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); (*onBufferLost)(context, session.get(), request, anw, frameNumber); freeACaptureRequest(request); break; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 7036017ab7..829b08452e 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -266,9 +266,11 @@ class CameraDevice final : public RefBase { class CallbackHandler : public AHandler { public: + explicit CallbackHandler(const char *id); void onMessageReceived(const sp &msg) override; private: + std::string mId; // This handler will cache all capture session sp until kWhatCleanUpSessions // is processed. This is used to guarantee the last session reference is always // being removed in callback thread without holding camera device lock -- GitLab From 6bdd081e1660e4c3af3b96fd215c132fd283416f Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Sat, 16 Feb 2019 15:08:39 +0530 Subject: [PATCH 1313/1530] libeffects: Added support to test effects using 32 and 64 bit binaries Test: local native test (lvmtest) Bug: 124317351 Change-Id: I4e363c453b567db87f8ca02170ab2bfb921fb739 --- .../lvm/tests/build_and_run_all_unit_tests.sh | 94 ++++++++++++------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh index 5079634301..a97acc9ade 100755 --- a/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh +++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests.sh @@ -24,9 +24,32 @@ echo "========================================" echo "testing lvm" adb shell mkdir -p $testdir adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir -adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir adb push $OUT/testcases/snr/arm64/snr $testdir +E_VAL=1 +if [ -z "$1" ] +then + cmds=("adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir" + "adb push $OUT/testcases/lvmtest/arm/lvmtest $testdir" + ) +elif [ "$1" == "32" ] +then + cmds="adb push $OUT/testcases/lvmtest/arm/lvmtest $testdir" +elif [ "$1" == "64" ] +then + cmds="adb push $OUT/testcases/lvmtest/arm64/lvmtest $testdir" +else + echo "" + echo "Invalid \"val\"" + echo "Usage:" + echo " "$0" [val]" + echo " where, val can be either 32 or 64." + echo "" + echo " If val is not specified then both 32 bit and 64 bit binaries" + echo " are tested." + exit $E_VAL +fi + flags_arr=( "-csE" "-eqE" @@ -61,42 +84,45 @@ fs_arr=( # run multichannel effects at different configs, saving only the stereo channel # pair. error_count=0 -for flags in "${flags_arr[@]}" +for cmd in "${cmds[@]}" do - for fs in ${fs_arr[*]} + $cmd + for flags in "${flags_arr[@]}" do - for chMask in {0..22} + for fs in ${fs_arr[*]} do - adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ - -o:$testdir/sinesweep_$((chMask))_$((fs)).raw -chMask:$chMask -fs:$fs $flags - - shell_ret=$? - if [ $shell_ret -ne 0 ]; then - echo "error: $shell_ret" - ((++error_count)) - fi - - - # two channel files should be identical to higher channel - # computation (first 2 channels). - # Do not compare cases where -bE is in flags (due to mono computation) - if [[ $flags != *"-bE"* ]] && [[ "$chMask" -gt 1 ]] - then - adb shell cmp $testdir/sinesweep_1_$((fs)).raw \ - $testdir/sinesweep_$((chMask))_$((fs)).raw - elif [[ $flags == *"-bE"* ]] && [[ "$chMask" -gt 1 ]] - then - adb shell $testdir/snr $testdir/sinesweep_1_$((fs)).raw \ - $testdir/sinesweep_$((chMask))_$((fs)).raw -thr:90.308998 - fi - - # both cmp and snr return EXIT_FAILURE on mismatch. - shell_ret=$? - if [ $shell_ret -ne 0 ]; then - echo "error: $shell_ret" - ((++error_count)) - fi - + for chMask in {0..22} + do + adb shell $testdir/lvmtest -i:$testdir/sinesweepraw.raw \ + -o:$testdir/sinesweep_$((chMask))_$((fs)).raw -chMask:$chMask -fs:$fs $flags + + shell_ret=$? + if [ $shell_ret -ne 0 ]; then + echo "error: $shell_ret" + ((++error_count)) + fi + + + # two channel files should be identical to higher channel + # computation (first 2 channels). + # Do not compare cases where -bE is in flags (due to mono computation) + if [[ $flags != *"-bE"* ]] && [[ "$chMask" -gt 1 ]] + then + adb shell cmp $testdir/sinesweep_1_$((fs)).raw \ + $testdir/sinesweep_$((chMask))_$((fs)).raw + elif [[ $flags == *"-bE"* ]] && [[ "$chMask" -gt 1 ]] + then + adb shell $testdir/snr $testdir/sinesweep_1_$((fs)).raw \ + $testdir/sinesweep_$((chMask))_$((fs)).raw -thr:90.308998 + fi + + # both cmp and snr return EXIT_FAILURE on mismatch. + shell_ret=$? + if [ $shell_ret -ne 0 ]; then + echo "error: $shell_ret" + ((++error_count)) + fi + done done done done -- GitLab From bc9357f513a9578fa776d660bb5e1e4d035343d3 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Wed, 17 Apr 2019 10:56:32 -0700 Subject: [PATCH 1314/1530] NuPlayer: Fix ANR while resetting RTSP playback - When network is poor, the response of RTSP teardown won't be received in time, so ANR will happen. - With this patch, a teardown message will be sent when timeout expires, in order to avoid ANR. Bug: 123862808 Test: rtsp://107.178.217.141:1935/vod/avatar-movie-trailer.mp4 Author: lubiny CRs-Fixed: 650866 Change-Id: I3f9efd9fefa66104ad452559ced5ff5218d73a66 --- media/libstagefright/rtsp/MyHandler.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index b4515e4163..48bc8cefb6 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -75,6 +75,8 @@ static int64_t kPauseDelayUs = 3000000ll; // a new sequence. static int32_t kMaxAllowedStaleAccessUnits = 20; +static int64_t kTearDownTimeoutUs = 3000000ll; + namespace android { static bool GetAttribute(const char *s, const char *key, AString *value) { @@ -930,6 +932,14 @@ struct MyHandler : public AHandler { request.append("\r\n"); mConn->sendRequest(request.c_str(), reply); + + // If the response of teardown hasn't been received in 3 seconds, + // post 'tear' message to avoid ANR. + if (!msg->findInt32("reconnect", &reconnect) || !reconnect) { + sp teardown = reply->dup(); + teardown->setInt32("result", -ECONNABORTED); + teardown->post(kTearDownTimeoutUs); + } break; } -- GitLab From cf3b71546d5bb04ba45d1d784108f1681f75c827 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Fri, 19 Apr 2019 18:29:21 -0700 Subject: [PATCH 1315/1530] AudioTrack: Fix timestamp spam Test: logcat check during Photos playback, seek, etc. Bug: 130586209 Change-Id: I71c5e6b83f94485be8f2e9bb164314756a16c87a --- media/libaudioclient/AudioTrack.cpp | 50 +++++++++++++------ .../libaudioclient/include/media/AudioTrack.h | 7 ++- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 8d1f511fef..bd48f56ca3 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -605,7 +605,10 @@ status_t AudioTrack::set( mInUnderrun = false; mPreviousTimestampValid = false; mTimestampStartupGlitchReported = false; - mRetrogradeMotionReported = false; + mTimestampRetrogradePositionReported = false; + mTimestampRetrogradeTimeReported = false; + mTimestampStallReported = false; + mTimestampStaleTimeReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; mStartTs.mPosition = 0; mUnderrunCountOffset = 0; @@ -656,7 +659,10 @@ status_t AudioTrack::start() mPosition = 0; mPreviousTimestampValid = false; mTimestampStartupGlitchReported = false; - mRetrogradeMotionReported = false; + mTimestampRetrogradePositionReported = false; + mTimestampRetrogradeTimeReported = false; + mTimestampStallReported = false; + mTimestampStaleTimeReported = false; mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID; if (!isOffloadedOrDirect_l() @@ -2607,9 +2613,14 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // A better start time is now. The retrograde check ensures // timestamp monotonicity. const int64_t nowNs = systemTime(); - ALOGD("%s(%d) device stall, using current time %lld", - __func__, mPortId, (long long)nowNs); + if (!mTimestampStallReported) { + ALOGD("%s(%d): device stall time corrected using current time %lld", + __func__, mPortId, (long long)nowNs); + mTimestampStallReported = true; + } timestamp.mTime = convertNsToTimespec(nowNs); + } else { + mTimestampStallReported = false; } } @@ -2762,12 +2773,17 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) const int64_t lagNs = int64_t(mAfLatency * 1000000LL); const int64_t limitNs = mStartNs - lagNs; if (currentTimeNanos < limitNs) { - ALOGD("%s(%d): correcting timestamp time for pause, " - "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld", - __func__, mPortId, - (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs); + if (!mTimestampStaleTimeReported) { + ALOGD("%s(%d): stale timestamp time corrected, " + "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld", + __func__, mPortId, + (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs); + mTimestampStaleTimeReported = true; + } timestamp.mTime = convertNsToTimespec(limitNs); currentTimeNanos = limitNs; + } else { + mTimestampStaleTimeReported = false; } // previousTimestampValid is set to false when starting after a stop or flush. @@ -2777,11 +2793,15 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) // retrograde check if (currentTimeNanos < previousTimeNanos) { - ALOGW("%s(%d): retrograde timestamp time corrected, %lld < %lld", - __func__, mPortId, - (long long)currentTimeNanos, (long long)previousTimeNanos); + if (!mTimestampRetrogradeTimeReported) { + ALOGW("%s(%d): retrograde timestamp time corrected, %lld < %lld", + __func__, mPortId, + (long long)currentTimeNanos, (long long)previousTimeNanos); + mTimestampRetrogradeTimeReported = true; + } timestamp.mTime = mPreviousTimestamp.mTime; - // currentTimeNanos not used below. + } else { + mTimestampRetrogradeTimeReported = false; } // Looking at signed delta will work even when the timestamps @@ -2790,16 +2810,16 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp) - mPreviousTimestamp.mPosition).signedValue(); if (deltaPosition < 0) { // Only report once per position instead of spamming the log. - if (!mRetrogradeMotionReported) { + if (!mTimestampRetrogradePositionReported) { ALOGW("%s(%d): retrograde timestamp position corrected, %d = %u - %u", __func__, mPortId, deltaPosition, timestamp.mPosition, mPreviousTimestamp.mPosition); - mRetrogradeMotionReported = true; + mTimestampRetrogradePositionReported = true; } } else { - mRetrogradeMotionReported = false; + mTimestampRetrogradePositionReported = false; } if (deltaPosition < 0) { timestamp.mPosition = mPreviousTimestamp.mPosition; diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 3926ead581..df5eabc5cc 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -1151,8 +1151,11 @@ public: // AudioTracks. bool mPreviousTimestampValid;// true if mPreviousTimestamp is valid - bool mTimestampStartupGlitchReported; // reduce log spam - bool mRetrogradeMotionReported; // reduce log spam + bool mTimestampStartupGlitchReported; // reduce log spam + bool mTimestampRetrogradePositionReported; // reduce log spam + bool mTimestampRetrogradeTimeReported; // reduce log spam + bool mTimestampStallReported; // reduce log spam + bool mTimestampStaleTimeReported; // reduce log spam AudioTimestamp mPreviousTimestamp; // used to detect retrograde motion ExtendedTimestamp::Location mPreviousLocation; // location used for previous timestamp -- GitLab From 0dd5185555db64af2cb5ff1e161629054086abb6 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 19 Apr 2019 18:18:58 -0700 Subject: [PATCH 1316/1530] audio policy: improve input device connection logic Do not systematically close all inputs when an input device is connected or disconnected but only those affected by the change. Also: - refactor setDeviceConnectionStateInt() to align input and output devices connection sequence - remove unused "inputs" argument to checkInputsForDevice() Bug: 126370328 Test: phone call, video call , camcorder, assistant, auto projected Change-Id: I27734b32da97ad4790d6cd74eaaf866e414db211 --- .../managerdefault/AudioPolicyManager.cpp | 86 +++++++++++-------- .../managerdefault/AudioPolicyManager.h | 5 +- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6bd64d61fd..7011ef7213 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -273,8 +273,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // handle input devices if (audio_is_input_device(deviceType)) { - SortedVector inputs; - ssize_t index = mAvailableInputDevices.indexOf(device); switch (state) { @@ -284,11 +282,18 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT ALOGW("%s() device already connected: %s", __func__, device->toString().c_str()); return INVALID_OPERATION; } + + if (mAvailableInputDevices.add(device) < 0) { + return NO_MEMORY; + } + // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic // parameters on newly connected devices (instead of opening the inputs...) broadcastDeviceConnectionState(device, state); - if (checkInputsForDevice(device, state, inputs) != NO_ERROR) { + if (checkInputsForDevice(device, state) != NO_ERROR) { + mAvailableInputDevices.remove(device); + broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE); mHwModules.cleanUpForDevice(device); @@ -296,9 +301,6 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT return INVALID_OPERATION; } - if (mAvailableInputDevices.add(device) < 0) { - return NO_MEMORY; - } } break; // handle input device disconnection @@ -313,8 +315,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // Set Disconnect to HALs broadcastDeviceConnectionState(device, state); - checkInputsForDevice(device, state, inputs); mAvailableInputDevices.remove(device); + + checkInputsForDevice(device, state); } break; default: @@ -325,7 +328,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceT // Propagate device availability to Engine setEngineDeviceConnectionState(device, state); - closeAllInputs(); + checkCloseInputs(); // As the input device list can impact the output device selection, update // getDeviceForStrategy() cache updateDevicesAndOutputs(); @@ -2342,9 +2345,39 @@ void AudioPolicyManager::closeClient(audio_port_handle_t portId) releaseInput(portId); } -void AudioPolicyManager::closeAllInputs() { - while (mInputs.size() != 0) { - closeInput(mInputs.keyAt(0)); +void AudioPolicyManager::checkCloseInputs() { + // After connecting or disconnecting an input device, close input if: + // - it has no client (was just opened to check profile) OR + // - none of its supported devices are connected anymore OR + // - one of its clients cannot be routed to one of its supported + // devices anymore. Otherwise update device selection + std::vector inputsToClose; + for (size_t i = 0; i < mInputs.size(); i++) { + const sp input = mInputs.valueAt(i); + if (input->clientsList().size() == 0 + || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())) { + inputsToClose.push_back(mInputs.keyAt(i)); + } else { + bool close = false; + for (const auto& client : input->clientsList()) { + sp device = + mEngine->getInputDeviceForAttributes(client->attributes()); + if (!input->supportedDevices().contains(device)) { + close = true; + break; + } + } + if (close) { + inputsToClose.push_back(mInputs.keyAt(i)); + } else { + setInputDevice(input->mIoHandle, getNewInputDevice(input)); + } + } + } + + for (const audio_io_handle_t handle : inputsToClose) { + ALOGV("%s closing input %d", __func__, handle); + closeInput(handle); } } @@ -4684,8 +4717,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d } status_t AudioPolicyManager::checkInputsForDevice(const sp& device, - audio_policy_dev_state_t state, - SortedVector& inputs) + audio_policy_dev_state_t state) { sp desc; @@ -4695,16 +4727,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de } if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { - // first list already open inputs that can be routed to this device - for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { - desc = mInputs.valueAt(input_index); - if (desc->mProfile->supportsDeviceTypes(device->type())) { - ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index)); - inputs.add(mInputs.keyAt(input_index)); - } - } - - // then look for input profiles that can be routed to this device + // look for input profiles that can be routed to this device SortedVector< sp > profiles; for (const auto& hwModule : mHwModules) { for (size_t profile_index = 0; @@ -4720,8 +4743,9 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de } } - if (profiles.isEmpty() && inputs.isEmpty()) { - ALOGW("%s: No input available for device %s", __func__, device->toString().c_str()); + if (profiles.isEmpty()) { + ALOGW("%s: No input profile available for device %s", + __func__, device->toString().c_str()); return BAD_VALUE; } @@ -4774,7 +4798,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de input = AUDIO_IO_HANDLE_NONE; } - if (input != 0) { + if (input != AUDIO_IO_HANDLE_NONE) { addInput(input, desc); } } // endif input != 0 @@ -4785,7 +4809,6 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de profiles.removeAt(profile_index); profile_index--; } else { - inputs.add(input); if (audio_device_is_digital(device->type())) { device->importAudioPort(profile); } @@ -4799,15 +4822,6 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp& de } } else { // Disconnect - // check if one opened input is not needed any more after disconnecting one device - for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { - desc = mInputs.valueAt(input_index); - if (!mAvailableInputDevices.containsAtLeastOne(desc->supportedDevices())) { - ALOGV("checkInputsForDevice(): disconnecting adding input %d", - mInputs.keyAt(input_index)); - inputs.add(mInputs.keyAt(input_index)); - } - } // Clear any profiles associated with the disconnected device. for (const auto& hwModule : mHwModules) { for (size_t profile_index = 0; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 3376965a57..8ca06e764c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -142,7 +142,7 @@ public: // indicates to the audio policy manager that the input stops being used. virtual status_t stopInput(audio_port_handle_t portId); virtual void releaseInput(audio_port_handle_t portId); - virtual void closeAllInputs(); + virtual void checkCloseInputs(); /** * @brief initStreamVolume: even if the engine volume files provides min and max, keep this * api for compatibility reason. @@ -486,8 +486,7 @@ protected: SortedVector& outputs); status_t checkInputsForDevice(const sp& device, - audio_policy_dev_state_t state, - SortedVector& inputs); + audio_policy_dev_state_t state); // close an output and its companion duplicating output. void closeOutput(audio_io_handle_t output); -- GitLab From ace192e7f35cbe97b2dbe678617261cc95a714d4 Mon Sep 17 00:00:00 2001 From: Saketh Sathuvalli Date: Mon, 22 Apr 2019 17:37:48 +0530 Subject: [PATCH 1317/1530] libeffects: Correct LVM_BufferUnmanagedOut function for mono input Use stereo to match internal computations in LVM_Process. Test: dedicated POC see bug below. Bug: 131065273 Change-Id: Icdc15d0da2772979f82a4ad08d360c81b965f705 --- media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c index 37272e311e..bdca5e3981 100644 --- a/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c +++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Buffers.c @@ -1128,6 +1128,11 @@ void LVM_BufferUnmanagedOut(LVM_Handle_t hInstance, LVM_Instance_t *pInstance = (LVM_Instance_t *)hInstance; #ifdef SUPPORT_MC LVM_INT16 NumChannels = pInstance->NrChannels; + if (NumChannels == 1) + { + /* Mono input is processed as stereo by LVM module */ + NumChannels = 2; + } #undef NrFrames #define NrFrames (*pNumSamples) // alias for clarity #else -- GitLab From 0e2eefd580aeae1e74fdc06a070116faa702d629 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Thu, 18 Apr 2019 14:05:43 -0700 Subject: [PATCH 1318/1530] cameraserver: log setTorchMode calls. Bug: 120314037 Test: Turn torch on / off; logcat | grep 'Torch turned' Test: Turn torch on / off; adb shell dumpsys media.camera Change-Id: Ib98e8847f49c6977b9cba65ae0605b8abe774a15 Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/CameraService.cpp | 11 +++++++++++ services/camera/libcameraservice/CameraService.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index a1cb8eea3a..fc6d6be3e6 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1638,6 +1638,11 @@ Status CameraService::setTorchMode(const String16& cameraId, bool enabled, } } + int clientPid = CameraThreadState::getCallingPid(); + const char *id_cstr = id.c_str(); + const char *torchState = enabled ? "on" : "off"; + ALOGI("Torch for camera id %s turned %s for client PID %d", id_cstr, torchState, clientPid); + logTorchEvent(id_cstr, torchState , clientPid); return Status::ok(); } @@ -2122,6 +2127,12 @@ void CameraService::logRejected(const char* cameraId, int clientPid, cameraId, clientPackage, clientPid, reason)); } +void CameraService::logTorchEvent(const char* cameraId, const char *torchState, int clientPid) { + // Log torch event + logEvent(String8::format("Torch for camera id %s turned %s for client PID %d", cameraId, + torchState, clientPid)); +} + void CameraService::logUserSwitch(const std::set& oldUserIds, const std::set& newUserIds) { String8 newUsers = toString(newUserIds); diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index a8c2606553..b8cec2c82b 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -747,6 +747,11 @@ private: void logRejected(const char* cameraId, int clientPid, const char* clientPackage, const char* reason); + /** + * Add an event log message when a client calls setTorchMode succesfully. + */ + void logTorchEvent(const char* cameraId, const char *torchState, int clientPid); + /** * Add an event log message that the current device user has been switched. */ -- GitLab From e8322b5584596350b2bb7ccffacd9af8ffe8194c Mon Sep 17 00:00:00 2001 From: zhangshuxiao Date: Tue, 18 Dec 2018 19:27:28 +0800 Subject: [PATCH 1319/1530] Camera: The vector memory is freed after clear The clear function of vector will release memory, so mBins will use overflow. Test: enable asan for cameraserver Bug: 131103281 Change-Id: Iaaa353332d7ac3992f018aa667fb8ef20a810f20 Signed-off-by: zhangshuxiao --- services/camera/libcameraservice/utils/LatencyHistogram.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/utils/LatencyHistogram.cpp b/services/camera/libcameraservice/utils/LatencyHistogram.cpp index 538bb6edfc..e2bdc4397e 100644 --- a/services/camera/libcameraservice/utils/LatencyHistogram.cpp +++ b/services/camera/libcameraservice/utils/LatencyHistogram.cpp @@ -46,7 +46,7 @@ void CameraLatencyHistogram::add(nsecs_t start, nsecs_t end) { } void CameraLatencyHistogram::reset() { - mBins.clear(); + memset(mBins.data(), 0, mBins.size() * sizeof(int64_t)); mTotalCount = 0; } -- GitLab From ac64098127785c34c866e704e46283a0cb3c7a37 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 9 Apr 2019 23:06:32 -0700 Subject: [PATCH 1320/1530] Camera: Relax BURST_CAPTURE requirement Allow resolutions larger or equal to 24 megapixels to stream at 10fps to meet BURST_CAPTURE requirement. Test: Build Bug: 129693371 Change-Id: I8f53b6a6f725e11d9deb1505d9d63d142e971006 --- camera/ndk/include/camera/NdkCameraMetadataTags.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 4563b410fe..7cd832afbb 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7511,18 +7511,19 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { /** *

    The camera device supports capturing high-resolution images at >= 20 frames per - * second, in at least the uncompressed YUV format, when post-processing settings are set - * to FAST. Additionally, maximum-resolution images can be captured at >= 10 frames - * per second. Here, 'high resolution' means at least 8 megapixels, or the maximum - * resolution of the device, whichever is smaller.

    + * second, in at least the uncompressed YUV format, when post-processing settings are + * set to FAST. Additionally, all image resolutions less than 24 megapixels can be + * captured at >= 10 frames per second. Here, 'high resolution' means at least 8 + * megapixels, or the maximum resolution of the device, whichever is smaller.

    *

    More specifically, this means that at least one output {@link AIMAGE_FORMAT_YUV_420_888 } size listed in * {@link ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS } * is larger or equal to the 'high resolution' defined above, and can be captured at at * least 20 fps. For the largest {@link AIMAGE_FORMAT_YUV_420_888 } size listed in * {@link ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS }, - * camera device can capture this size for at least 10 frames per second. Also the - * ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES entry lists at least one FPS range where - * the minimum FPS is >= 1 / minimumFrameDuration for the largest YUV_420_888 size.

    + * camera device can capture this size for at least 10 frames per second if the size is + * less than 24 megapixels. Also the ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES entry + * lists at least one FPS range where the minimum FPS is >= 1 / minimumFrameDuration + * for the largest YUV_420_888 size.

    *

    If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, {@link AIMAGE_FORMAT_Y8 }, then those can also be * captured at the same rate as the maximum-size YUV_420_888 resolution is.

    *

    In addition, the ACAMERA_SYNC_MAX_LATENCY field is guaranted to have a value between 0 -- GitLab From dceacb5304184a14fd80190ad61bfa886fbe8d6a Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 16 Apr 2019 11:28:08 -0700 Subject: [PATCH 1321/1530] DngCreator: Handle off-by-1 issue for DNG rects In DNG sdk, rectangle's bottom and right are exclusive. Update DngUtils to be consistent with that. Also fixed normalized optical center calculation given pixel [0, 0] is a square centered at (0.5, 0.5). Test: DngCreatorTest, and visually inspect DNG images taken Bug: 119566614 Change-Id: I73ab9327f75c24282ae14ef798fef797bb700bae --- media/img_utils/src/DngUtils.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp index 9304f5347c..791403029a 100644 --- a/media/img_utils/src/DngUtils.cpp +++ b/media/img_utils/src/DngUtils.cpp @@ -173,8 +173,8 @@ status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth, status_t err = addGainMap(/*top*/redTop, /*left*/redLeft, - /*bottom*/activeAreaHeight - 1, - /*right*/activeAreaWidth - 1, + /*bottom*/activeAreaHeight, + /*right*/activeAreaWidth, /*plane*/0, /*planes*/1, /*rowPitch*/2, @@ -191,8 +191,8 @@ status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth, err = addGainMap(/*top*/greenEvenTop, /*left*/greenEvenLeft, - /*bottom*/activeAreaHeight - 1, - /*right*/activeAreaWidth - 1, + /*bottom*/activeAreaHeight, + /*right*/activeAreaWidth, /*plane*/0, /*planes*/1, /*rowPitch*/2, @@ -209,8 +209,8 @@ status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth, err = addGainMap(/*top*/greenOddTop, /*left*/greenOddLeft, - /*bottom*/activeAreaHeight - 1, - /*right*/activeAreaWidth - 1, + /*bottom*/activeAreaHeight, + /*right*/activeAreaWidth, /*plane*/0, /*planes*/1, /*rowPitch*/2, @@ -227,8 +227,8 @@ status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth, err = addGainMap(/*top*/blueTop, /*left*/blueLeft, - /*bottom*/activeAreaHeight - 1, - /*right*/activeAreaWidth - 1, + /*bottom*/activeAreaHeight, + /*right*/activeAreaWidth, /*plane*/0, /*planes*/1, /*rowPitch*/2, @@ -265,8 +265,8 @@ status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth, status_t err = addGainMap(/*top*/0, /*left*/0, - /*bottom*/activeAreaHeight - 1, - /*right*/activeAreaWidth - 1, + /*bottom*/activeAreaHeight, + /*right*/activeAreaWidth, /*plane*/0, /*planes*/1, /*rowPitch*/1, @@ -364,8 +364,8 @@ status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs, return BAD_VALUE; } - double normalizedOCX = opticalCenterX / static_cast(activeArrayWidth - 1); - double normalizedOCY = opticalCenterY / static_cast(activeArrayHeight - 1); + double normalizedOCX = opticalCenterX / static_cast(activeArrayWidth); + double normalizedOCY = opticalCenterY / static_cast(activeArrayHeight); normalizedOCX = CLAMP(normalizedOCX, 0, 1); normalizedOCY = CLAMP(normalizedOCY, 0, 1); -- GitLab From f7e3a3a2eb753da4500b9000c113ae0f3b058792 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 22 Apr 2019 16:43:26 -0700 Subject: [PATCH 1322/1530] audioflinger: Fix reference counting protocol in OpPlayAudioMonitor OpPlayAudioMonitor was constructing a weak pointer to itself in the constructor. This practice can lead to crashes due to race conditions vs object destruction. This code is now moved to onFirstRef method which is called when at least one strong reference exists. This change also reduces the number of created OpPlayAudioMonitor objects by using a factory method. Bug: 130038586 Test: enable / disable DND mode Change-Id: I22e63a883ebaa25b9c96e79271bb9693b5ed75cd --- services/audioflinger/PlaybackTracks.h | 11 +++++-- services/audioflinger/Tracks.cpp | 43 +++++++++++++++++--------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 4fd72a7917..56be433d4b 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -22,11 +22,16 @@ // Checks and monitors OP_PLAY_AUDIO class OpPlayAudioMonitor : public RefBase { public: - OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType); ~OpPlayAudioMonitor() override; bool hasOpPlayAudio() const; + static sp createIfNeeded( + uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType); + private: + OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id); + void onFirstRef() override; + AppOpsManager mAppOpsManager; class PlayAudioOpCallback : public BnAppOpsCallback { @@ -209,7 +214,9 @@ public: int fastIndex() const { return mFastIndex; } - bool isPlaybackRestricted() const { return !mOpPlayAudioMonitor->hasOpPlayAudio(); } + bool isPlaybackRestricted() const { + // The monitor is only created for tracks that can be silenced. + return mOpPlayAudioMonitor ? !mOpPlayAudioMonitor->hasOpPlayAudio() : false; } protected: diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 2ff80c65ea..8d59431a65 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -381,26 +381,28 @@ status_t AudioFlinger::TrackHandle::onTransact( // ---------------------------------------------------------------------------- // AppOp for audio playback // ------------------------------- -AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, - int id, audio_stream_type_t streamType) - : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id) + +// static +sp +AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded( + uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType) { if (isAudioServerOrRootUid(uid)) { - ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", mId, usage); - return; + ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, usage); + return nullptr; } // stream type has been filtered by audio policy to indicate whether it can be muted if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) { - ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", mId, usage); - return; - } - PermissionController permissionController; - permissionController.getPackagesForUid(uid, mPackages); - checkPlayAudioForUsage(); - if (!mPackages.isEmpty()) { - mOpCallback = new PlayAudioOpCallback(this); - mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mPackages[0], mOpCallback); + ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", id, usage); + return nullptr; } + return new OpPlayAudioMonitor(uid, usage, id); +} + +AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor( + uid_t uid, audio_usage_t usage, int id) + : mHasOpPlayAudio(true), mUid(uid), mUsage((int32_t) usage), mId(id) +{ } AudioFlinger::PlaybackThread::OpPlayAudioMonitor::~OpPlayAudioMonitor() @@ -411,6 +413,17 @@ AudioFlinger::PlaybackThread::OpPlayAudioMonitor::~OpPlayAudioMonitor() mOpCallback.clear(); } +void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::onFirstRef() +{ + PermissionController permissionController; + permissionController.getPackagesForUid(mUid, mPackages); + checkPlayAudioForUsage(); + if (!mPackages.isEmpty()) { + mOpCallback = new PlayAudioOpCallback(this); + mAppOpsManager.startWatchingMode(AppOpsManager::OP_PLAY_AUDIO, mPackages[0], mOpCallback); + } +} + bool AudioFlinger::PlaybackThread::OpPlayAudioMonitor::hasOpPlayAudio() const { return mHasOpPlayAudio.load(); } @@ -492,7 +505,7 @@ AudioFlinger::PlaybackThread::Track::Track( mPresentationCompleteFrames(0), mFrameMap(16 /* sink-frame-to-track-frame map memory */), mVolumeHandler(new media::VolumeHandler(sampleRate)), - mOpPlayAudioMonitor(new OpPlayAudioMonitor(uid, attr.usage, id(), streamType)), + mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(uid, attr.usage, id(), streamType)), // mSinkTimestamp mFastIndex(-1), mCachedVolume(1.0), -- GitLab From 2f5f0ace276ed69c3a61bb011812cb1bdc3fea40 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 22 Apr 2019 15:46:21 -0700 Subject: [PATCH 1323/1530] Don't read more than the buffer size. Bug: 130593895 Test: manual Change-Id: I54612d14e7cadde1d4a447040dbe9086e970c549 --- media/extractors/wav/WAVExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 020951bcdf..8b539c2ff6 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -461,7 +461,7 @@ media_status_t WAVSource::read( } // maxBytesToRead may be reduced so that in-place data conversion will fit in buffer size. - const size_t bufferSize = buffer->size(); + const size_t bufferSize = std::min(buffer->size(), kMaxFrameSize); size_t maxBytesToRead; if (mOutputFloat) { // destination is float at 4 bytes per sample, source may be less. maxBytesToRead = (mBitsPerSample / 8) * (bufferSize / 4); -- GitLab From bc2d30d07d8addc560e68beea0614da2fbcadea0 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Mon, 22 Apr 2019 18:54:16 -0700 Subject: [PATCH 1324/1530] Make sure that loading failure is reported to dropbox. Test: adb shell dumpsys media.extractor Check 'ls /data/system/dropbox' after failure Bug: 131106476 Change-Id: Ib25c762fd8e50bc0637ec903dc691a070e66ce75 --- .../libstagefright/MediaExtractorFactory.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index a309ee4dae..d97591f8a3 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -244,20 +245,17 @@ void MediaExtractorFactory::RegisterExtractors( void *libHandle = android_dlopen_ext( libPath.string(), RTLD_NOW | RTLD_LOCAL, dlextinfo); - if (libHandle) { - GetExtractorDef getDef = - (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); - if (getDef) { - ALOGV("registering sniffer for %s", libPath.string()); - RegisterExtractor( - new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); - } else { - ALOGW("%s does not contain sniffer", libPath.string()); - dlclose(libHandle); - } - } else { - ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); - } + CHECK(libHandle != nullptr) + << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno); + + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + CHECK(getDef != nullptr) + << libPath.string() << " does not contain sniffer"; + + ALOGV("registering sniffer for %s", libPath.string()); + RegisterExtractor( + new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); } closedir(libDir); } else { -- GitLab From 3c4e6b503848623a0ff9bb1bee78e5b0d7c248ea Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 22 Jan 2019 15:53:36 -0800 Subject: [PATCH 1325/1530] aaudio: use actual DSP burst size in timing model This will more accurately track the read or write position of the DSP. The client sometimes aggregates multiple hardware bursts to avoid waking up too often. This can make it look like the DSP has processed a larger amount of memory than it really has. With this change, the timing model will advance in sync with the hardware. Bug: 117834966 Bug: 130911274 Test: see bug for repro steps Change-Id: Id22f1f313e02f0514665ee5ac144cab4684e3bc1 --- .../src/client/AudioStreamInternal.cpp | 24 +++++++++++++++--- .../oboeservice/AAudioServiceEndpointMMAP.cpp | 25 ++++--------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index db98d58b3b..c7e8088b02 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -76,6 +76,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { aaudio_result_t result = AAUDIO_OK; int32_t capacity; int32_t framesPerBurst; + int32_t framesPerHardwareBurst; AAudioStreamRequest request; AAudioStreamConfiguration configurationOutput; @@ -90,6 +91,9 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { return result; } + const int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros(); + int32_t burstMicros = 0; + // We have to do volume scaling. So we prefer FLOAT format. if (getFormat() == AUDIO_FORMAT_DEFAULT) { setFormat(AUDIO_FORMAT_PCM_FLOAT); @@ -173,8 +177,22 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { goto error; } - // Validate result from server. - framesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst; + framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst; + + // Scale up the burst size to meet the minimum equivalent in microseconds. + // This is to avoid waking the CPU too often when the HW burst is very small + // or at high sample rates. + framesPerBurst = framesPerHardwareBurst; + do { + if (burstMicros > 0) { // skip first loop + framesPerBurst *= 2; + } + burstMicros = framesPerBurst * static_cast(1000000) / getSampleRate(); + } while (burstMicros < burstMinMicros); + ALOGD("%s() original HW burst = %d, minMicros = %d => SW burst = %d\n", + __func__, framesPerHardwareBurst, burstMinMicros, framesPerBurst); + + // Validate final burst size. if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) { ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst); result = AAUDIO_ERROR_OUT_OF_RANGE; @@ -190,7 +208,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { } mClockModel.setSampleRate(getSampleRate()); - mClockModel.setFramesPerBurst(mFramesPerBurst); + mClockModel.setFramesPerBurst(framesPerHardwareBurst); if (isDataCallbackSet()) { mCallbackFrames = builder.getFramesPerDataCallback(); diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp index 447f32c388..b05baa4632 100644 --- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp +++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp @@ -77,9 +77,6 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques audio_config_base_t config; audio_port_handle_t deviceId; - int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros(); - int32_t burstMicros = 0; - copyFrom(request.getConstantConfiguration()); aaudio_direction_t direction = getDirection(); @@ -235,24 +232,12 @@ aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamReques setFormat(config.format); setSampleRate(config.sample_rate); - // Scale up the burst size to meet the minimum equivalent in microseconds. - // This is to avoid waking the CPU too often when the HW burst is very small - // or at high sample rates. - do { - if (burstMicros > 0) { // skip first loop - mFramesPerBurst *= 2; - } - burstMicros = mFramesPerBurst * static_cast(1000000) / getSampleRate(); - } while (burstMicros < burstMinMicros); - - ALOGD("%s() original burst = %d, minMicros = %d => burst = %d\n", - __func__, mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst); - - ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d\n", - __func__, getSampleRate(), getSamplesPerFrame(), deviceId); + ALOGD("%s() actual rate = %d, channels = %d" + ", deviceId = %d, capacity = %d\n", + __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity()); - ALOGD("%s() format = 0x%08x, frame size = %d", - __func__, getFormat(), calculateBytesPerFrame()); + ALOGD("%s() format = 0x%08x, frame size = %d, burst size = %d", + __func__, getFormat(), calculateBytesPerFrame(), mFramesPerBurst); return result; -- GitLab From 0682c79f1a65be0af818925f14d9c267fa7e0c8e Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 23 Apr 2019 10:22:14 -0700 Subject: [PATCH 1326/1530] Support arbitrary sampling rate in FLAC Bug: 122491377 Test: build Change-Id: Id7b92480312a8e5dbc214072b7f0e305e27670b0 --- media/extractors/flac/FLACExtractor.cpp | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp index 8854631332..5329bd112f 100644 --- a/media/extractors/flac/FLACExtractor.cpp +++ b/media/extractors/flac/FLACExtractor.cpp @@ -531,23 +531,9 @@ status_t FLACParser::init() return NO_INIT; } // check sample rate - switch (getSampleRate()) { - case 8000: - case 11025: - case 12000: - case 16000: - case 22050: - case 24000: - case 32000: - case 44100: - case 48000: - case 88200: - case 96000: - case 176400: - case 192000: - break; - default: - // Note: internally we support arbitrary sample rates from 8kHz to 192kHz. + // Note: flac supports arbitrary sample rates up to 655350 Hz, but Android + // supports sample rates from 8kHz to 192kHz, so use that as the limit. + if (getSampleRate() < 8000 || getSampleRate() > 192000) { ALOGE("unsupported sample rate %u", getSampleRate()); return NO_INIT; } -- GitLab From f5c7ca807a588754c0d04fc9782d3e56e57d957f Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 23 Apr 2019 17:06:21 -0700 Subject: [PATCH 1327/1530] Log fatal error when c2 sw codec fails to load bug: 131106476 test: manually add crash and make sure crash dump saved in dropbox Change-Id: I5ffc7158b04aa0e19e401b903df9a47b5c830ccb --- media/codec2/vndk/C2Store.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index e075849a7e..10c4dcc574 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -661,24 +661,27 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( ALOGV("in %s", __func__); ALOGV("loading dll"); mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE); - if (mLibHandle == nullptr) { - // could be access/symbol or simply not being there - ALOGD("could not dlopen %s: %s", libPath.c_str(), dlerror()); - mInit = C2_CORRUPTED; + LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, + "could not dlopen %s: %s", libPath.c_str(), dlerror()); + + createFactory = + (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory"); + LOG_ALWAYS_FATAL_IF(createFactory == nullptr, + "createFactory is null in %s", libPath.c_str()); + + destroyFactory = + (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory"); + LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr, + "destroyFactory is null in %s", libPath.c_str()); + + mComponentFactory = createFactory(); + if (mComponentFactory == nullptr) { + ALOGD("could not create factory in %s", libPath.c_str()); + mInit = C2_NO_MEMORY; } else { - createFactory = - (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory"); - destroyFactory = - (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory"); - - mComponentFactory = createFactory(); - if (mComponentFactory == nullptr) { - ALOGD("could not create factory in %s", libPath.c_str()); - mInit = C2_NO_MEMORY; - } else { - mInit = C2_OK; - } + mInit = C2_OK; } + if (mInit != C2_OK) { return mInit; } -- GitLab From 962ba215fbffcf27b3c69827a9221e0a82108ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 3 Apr 2019 16:21:39 +0200 Subject: [PATCH 1328/1530] audiopolicy: fix build issue by going on switching to bp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 130284799 Test: make Change-Id: I60d41eaa41564fecdeef6d4cb223a8367883db50 Signed-off-by: François Gaffie --- services/audiopolicy/Android.bp | 5 + services/audiopolicy/Android.mk | 136 ------------------ .../common/managerdefinitions/Android.bp | 1 + services/audiopolicy/engine/Android.mk | 9 -- .../audiopolicy/engineconfigurable/Android.bp | 44 ++++++ .../audiopolicy/engineconfigurable/Android.mk | 67 --------- .../engineconfigurable/config/Android.mk | 9 -- .../config/example/Android.mk | 4 + .../parameter-framework/Android.mk | 7 - .../parameter-framework/examples/Android.mk | 15 +- .../parameter-framework/plugin/Android.bp | 36 +++++ .../parameter-framework/plugin/Android.mk | 43 ------ .../engineconfigurable/wrapper/Android.bp | 21 +++ .../engineconfigurable/wrapper/Android.mk | 39 ----- .../enginedefault/config/Android.mk | 9 -- services/audiopolicy/manager/Android.mk | 32 +++++ .../manager/AudioPolicyFactory.cpp | 2 +- .../audiopolicy/managerdefault/Android.mk | 56 ++++++++ services/audiopolicy/service/Android.mk | 49 +++++++ .../audiopolicy/service/AudioPolicyService.h | 2 +- services/audiopolicy/tests/Android.mk | 3 +- 21 files changed, 261 insertions(+), 328 deletions(-) create mode 100644 services/audiopolicy/Android.bp delete mode 100644 services/audiopolicy/Android.mk delete mode 100644 services/audiopolicy/engine/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/Android.bp delete mode 100644 services/audiopolicy/engineconfigurable/Android.mk delete mode 100644 services/audiopolicy/engineconfigurable/config/Android.mk delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp delete mode 100644 services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk create mode 100644 services/audiopolicy/engineconfigurable/wrapper/Android.bp delete mode 100644 services/audiopolicy/engineconfigurable/wrapper/Android.mk delete mode 100644 services/audiopolicy/enginedefault/config/Android.mk create mode 100644 services/audiopolicy/manager/Android.mk create mode 100644 services/audiopolicy/managerdefault/Android.mk create mode 100644 services/audiopolicy/service/Android.mk diff --git a/services/audiopolicy/Android.bp b/services/audiopolicy/Android.bp new file mode 100644 index 0000000000..a42b89fcf6 --- /dev/null +++ b/services/audiopolicy/Android.bp @@ -0,0 +1,5 @@ +cc_library_headers { + name: "libaudiopolicymanager_interface_headers", + host_supported: true, + export_include_dirs: ["."], +} diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk deleted file mode 100644 index 3badda1f44..0000000000 --- a/services/audiopolicy/Android.mk +++ /dev/null @@ -1,136 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - service/AudioPolicyService.cpp \ - service/AudioPolicyEffects.cpp \ - service/AudioPolicyInterfaceImpl.cpp \ - service/AudioPolicyClientImpl.cpp - -LOCAL_C_INCLUDES := \ - frameworks/av/services/audioflinger \ - $(call include-path-for, audio-utils) \ - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudiopolicyengine_interface_headers \ - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - liblog \ - libbinder \ - libaudioclient \ - libhardware_legacy \ - libaudiopolicymanager \ - libmedia_helper \ - libmediametrics \ - libmediautils \ - libeffectsconfig \ - libsensorprivacy - -LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \ - libsensorprivacy - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE:= libaudiopolicyservice - -LOCAL_CFLAGS += -fvisibility=hidden -LOCAL_CFLAGS += -Wall -Werror - -include $(BUILD_SHARED_LIBRARY) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= managerdefault/AudioPolicyManager.cpp - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - liblog \ - libaudiopolicy \ - libsoundtrigger - -ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) - -ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) -$(error Configurable policy does not support legacy conf file) -endif #ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) - -LOCAL_C_INCLUDES += frameworks/av/services/audiopolicy/engineconfigurable/include -LOCAL_C_INCLUDES += frameworks/av/include - -LOCAL_SHARED_LIBRARIES += libaudiopolicyengineconfigurable - -else - -LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault - -endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) - -LOCAL_C_INCLUDES += \ - $(call include-path-for, audio-utils) \ - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudiopolicyengine_interface_headers - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_SHARED_LIBRARIES += libmedia_helper -LOCAL_SHARED_LIBRARIES += libmediametrics - -LOCAL_SHARED_LIBRARIES += libbinder libhidlbase libxml2 - -ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) -LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF -endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) - -LOCAL_CFLAGS += -Wall -Werror - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE:= libaudiopolicymanagerdefault - -include $(BUILD_SHARED_LIBRARY) - -ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - manager/AudioPolicyFactory.cpp - -LOCAL_SHARED_LIBRARIES := \ - libaudiopolicymanagerdefault - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_C_INCLUDES += \ - $(call include-path-for, audio-utils) \ - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudiopolicyengine_interface_headers - -LOCAL_CFLAGS := -Wall -Werror - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE:= libaudiopolicymanager - -include $(BUILD_SHARED_LIBRARY) - -endif - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp index c9037a1daa..f02f3cf800 100644 --- a/services/audiopolicy/common/managerdefinitions/Android.bp +++ b/services/audiopolicy/common/managerdefinitions/Android.bp @@ -34,6 +34,7 @@ cc_library_static { ], header_libs: [ "libaudiopolicycommon", + "libaudiopolicymanager_interface_headers", ], export_header_lib_headers: ["libaudiopolicycommon"], diff --git a/services/audiopolicy/engine/Android.mk b/services/audiopolicy/engine/Android.mk deleted file mode 100644 index dcce8e3d28..0000000000 --- a/services/audiopolicy/engine/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/services/audiopolicy/engineconfigurable/Android.bp b/services/audiopolicy/engineconfigurable/Android.bp new file mode 100644 index 0000000000..c27dc885e4 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/Android.bp @@ -0,0 +1,44 @@ +cc_library_headers { + name: "libaudiopolicyengineconfigurable_interface_headers", + host_supported: true, + export_include_dirs: ["interface"], +} + +cc_library_shared { + name: "libaudiopolicyengineconfigurable", + export_include_dirs: ["include"], + srcs: [ + "src/Engine.cpp", + "src/EngineInstance.cpp", + "src/Stream.cpp", + "src/InputSource.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + local_include_dirs: ["include"], + header_libs: [ + "libbase_headers", + "libaudiopolicycommon", + "libaudiopolicyengine_interface_headers", + "libaudiopolicyengineconfigurable_interface_headers", + ], + static_libs: [ + "libaudiopolicycomponents", + "libaudiopolicyengine_common", + "libaudiopolicyengine_config", + "libaudiopolicyengineconfigurable_pfwwrapper", + + ], + shared_libs: [ + "liblog", + "libcutils", + "libutils", + "libmedia_helper", + "libaudiopolicy", + "libparameter", + "libxml2", + ], +} diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk deleted file mode 100644 index 84a44228da..0000000000 --- a/services/audiopolicy/engineconfigurable/Android.mk +++ /dev/null @@ -1,67 +0,0 @@ -ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) - -LOCAL_PATH := $(call my-dir) - -# Component build -####################################################################### - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - src/Engine.cpp \ - src/EngineInstance.cpp \ - src/Stream.cpp \ - src/InputSource.cpp \ - -audio_policy_engine_includes_common := \ - frameworks/av/services/audiopolicy/engineconfigurable/include \ - frameworks/av/services/audiopolicy/engineconfigurable/interface - -LOCAL_CFLAGS += \ - -Wall \ - -Werror \ - -Wextra \ - -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(audio_policy_engine_includes_common) - -LOCAL_C_INCLUDES := \ - $(audio_policy_engine_includes_common) \ - $(TARGET_OUT_HEADERS)/hw \ - $(call include-path-for, frameworks-av) \ - $(call include-path-for, audio-utils) - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudiopolicyengine_interface_headers - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE := libaudiopolicyengineconfigurable -LOCAL_MODULE_TAGS := optional - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicypfwwrapper \ - libaudiopolicycomponents \ - libaudiopolicyengine_common \ - libaudiopolicyengine_config \ - -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libutils \ - liblog \ - libcutils \ - libaudioutils \ - libparameter \ - libmedia_helper \ - libaudiopolicy \ - libxml2 - -include $(BUILD_SHARED_LIBRARY) - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) - -endif diff --git a/services/audiopolicy/engineconfigurable/config/Android.mk b/services/audiopolicy/engineconfigurable/config/Android.mk deleted file mode 100644 index dcce8e3d28..0000000000 --- a/services/audiopolicy/engineconfigurable/config/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/services/audiopolicy/engineconfigurable/config/example/Android.mk b/services/audiopolicy/engineconfigurable/config/example/Android.mk index f879afc8c0..a0f1a90d05 100644 --- a/services/audiopolicy/engineconfigurable/config/example/Android.mk +++ b/services/audiopolicy/engineconfigurable/config/example/Android.mk @@ -1,5 +1,7 @@ LOCAL_PATH := $(call my-dir) +ifdef BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION + TOOLS := frameworks/av/services/audiopolicy/engineconfigurable/tools PROVISION_CRITERION_TYPES := $(TOOLS)/provision_criterion_types_from_android_headers.mk @@ -145,3 +147,5 @@ CRITERION_TYPES_FILE := $(LOCAL_PATH)/common/$(LOCAL_MODULE).in include $(PROVISION_CRITERION_TYPES) endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable)) + +endif #ifdef BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/Android.mk deleted file mode 100644 index c402fd51b5..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/Android.mk +++ /dev/null @@ -1,7 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -####################################################################### -# Recursive call sub-folder Android.mk -####################################################################### - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index ddc8721540..82891a8336 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -9,6 +9,8 @@ LOCAL_PATH := $(call my-dir) +ifdef BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION + ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),phone_configurable automotive_configurable caremu_configurable no-output_configurable no-input_configurable)) PFW_CORE := external/parameter-framework @@ -39,7 +41,7 @@ LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework LOCAL_SRC_FILES := $(LOCAL_MODULE).in LOCAL_REQUIRED_MODULES := \ - PolicySubsystem.xml\ + PolicySubsystem.xml \ PolicyClass.xml # external/parameter-framework prevents from using debug interface @@ -106,26 +108,26 @@ endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_ ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),$(filter $(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-output_configurable no-input_configurable)) include $(CLEAR_VARS) -LOCAL_MODULE := PolicySubsystem-no-strategy.xml -LOCAL_MODULE_STEM := PolicySubsystem.xml +LOCAL_MODULE := PolicySubsystem.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_REQUIRED_MODULES := PolicySubsystem-CommonTypes.xml LOCAL_MODULE_RELATIVE_PATH := parameter-framework/Structure/Policy -LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE_STEM) +LOCAL_SRC_FILES := common/Structure/$(LOCAL_MODULE) include $(BUILD_PREBUILT) include $(CLEAR_VARS) -LOCAL_MODULE := ParameterFrameworkConfigurationPolicy.xml +LOCAL_MODULE := ParameterFrameworkConfigurationPolicy-no-strategy.xml +LOCAL_MODULE_STEM := ParameterFrameworkConfigurationPolicy.xml LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC LOCAL_VENDOR_MODULE := true LOCAL_MODULE_RELATIVE_PATH := parameter-framework LOCAL_SRC_FILES := $(LOCAL_MODULE).in LOCAL_REQUIRED_MODULES := \ - PolicySubsystem-no-strategy.xml\ + PolicySubsystem.xml \ PolicyClass.xml AUDIO_VALUE = false LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE) @@ -181,4 +183,5 @@ endif #ifeq ($(BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION),no-input_configurable) include $(call all-makefiles-under,$(LOCAL_PATH)) +endif #ifdef BUILD_AUDIO_POLICY_EXAMPLE_CONFIGURATION diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp new file mode 100644 index 0000000000..2685c6dc8c --- /dev/null +++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.bp @@ -0,0 +1,36 @@ +cc_library_shared { + name: "libpolicy-subsystem", + srcs: [ + "PolicySubsystemBuilder.cpp", + "PolicySubsystem.cpp", + "InputSource.cpp", + "Stream.cpp", + "ProductStrategy.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + "-fvisibility-inlines-hidden", + "-fvisibility=hidden", + ], + header_libs: [ + "libbase_headers", + "libaudiopolicycommon", + "libaudioclient_headers", + "libaudiopolicyengine_interface_headers", + "libaudiopolicyengineconfigurable_interface_headers", + ], + static_libs: [ + "libaudiopolicycomponents", + "libaudiopolicyengine_common", + "libpfw_utility", + ], + shared_libs: [ + "libaudiopolicyengineconfigurable", + "liblog", + "libutils", + "libmedia_helper", + "libparameter" + ], +} diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk deleted file mode 100644 index b060524859..0000000000 --- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/Android.mk +++ /dev/null @@ -1,43 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - PolicySubsystemBuilder.cpp \ - PolicySubsystem.cpp \ - InputSource.cpp \ - Stream.cpp \ - ProductStrategy.cpp - -LOCAL_CFLAGS += \ - -Wall \ - -Werror \ - -Wextra \ - -fvisibility-inlines-hidden \ - -fvisibility=hidden - -LOCAL_C_INCLUDES := \ - frameworks/av/services/audiopolicy/engineconfigurable/include \ - frameworks/av/services/audiopolicy/engineconfigurable/interface - -LOCAL_SHARED_LIBRARIES := \ - libaudiopolicyengineconfigurable \ - libparameter \ - libmedia_helper \ - liblog \ - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon \ - libaudioclient_headers \ - libbase_headers - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_STATIC_LIBRARIES := \ - libpfw_utility \ - libaudiopolicycomponents - -LOCAL_MODULE := libpolicy-subsystem - -include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.bp b/services/audiopolicy/engineconfigurable/wrapper/Android.bp new file mode 100644 index 0000000000..6f59487df9 --- /dev/null +++ b/services/audiopolicy/engineconfigurable/wrapper/Android.bp @@ -0,0 +1,21 @@ +cc_library { + name: "libaudiopolicyengineconfigurable_pfwwrapper", + export_include_dirs: ["include"], + srcs: ["ParameterManagerWrapper.cpp"], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + header_libs: [ + "libbase_headers", + "libaudiopolicycommon", + ], + static_libs: ["libaudiopolicycomponents"], + shared_libs: [ + "liblog", + "libutils", + "libmedia_helper", + "libparameter", + ], +} diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk deleted file mode 100644 index c7d8d34f10..0000000000 --- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk +++ /dev/null @@ -1,39 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -################################################################## -# WRAPPER LIBRARY -################################################################## - -include $(CLEAR_VARS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - frameworks/av/services/audiopolicy/engineconfigurable/include \ - frameworks/av/services/audiopolicy/engineconfigurable/interface \ - external/libxml2/include \ - external/icu/icu4c/source/common - -LOCAL_SRC_FILES:= \ - ParameterManagerWrapper.cpp - -LOCAL_SHARED_LIBRARIES := \ - libparameter \ - libmedia_helper \ - libxml2 - -LOCAL_HEADER_LIBRARIES := \ - libaudiopolicycommon - -LOCAL_STATIC_LIBRARIES := \ - libaudiopolicycomponents - -LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) - -LOCAL_MODULE:= libaudiopolicypfwwrapper -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include - -LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS := -Wall -Werror -Wextra - -include $(BUILD_STATIC_LIBRARY) - diff --git a/services/audiopolicy/enginedefault/config/Android.mk b/services/audiopolicy/enginedefault/config/Android.mk deleted file mode 100644 index dcce8e3d28..0000000000 --- a/services/audiopolicy/enginedefault/config/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -####################################################################### -# Recursive call sub-folder Android.mk -# -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/services/audiopolicy/manager/Android.mk b/services/audiopolicy/manager/Android.mk new file mode 100644 index 0000000000..d6ca2f26fc --- /dev/null +++ b/services/audiopolicy/manager/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH:= $(call my-dir) + +ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyFactory.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudiopolicymanagerdefault + +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents + +LOCAL_C_INCLUDES += \ + $(call include-path-for, audio-utils) + +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers \ + libaudiopolicymanager_interface_headers + +LOCAL_CFLAGS := -Wall -Werror + +LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) + +LOCAL_MODULE:= libaudiopolicymanager + +include $(BUILD_SHARED_LIBRARY) + +endif #ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) diff --git a/services/audiopolicy/manager/AudioPolicyFactory.cpp b/services/audiopolicy/manager/AudioPolicyFactory.cpp index 3efa1b0e80..7aff6a9dec 100644 --- a/services/audiopolicy/manager/AudioPolicyFactory.cpp +++ b/services/audiopolicy/manager/AudioPolicyFactory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "managerdefault/AudioPolicyManager.h" +#include namespace android { diff --git a/services/audiopolicy/managerdefault/Android.mk b/services/audiopolicy/managerdefault/Android.mk new file mode 100644 index 0000000000..684fc9f2ba --- /dev/null +++ b/services/audiopolicy/managerdefault/Android.mk @@ -0,0 +1,56 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= AudioPolicyManager.cpp + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libaudiopolicy \ + libsoundtrigger + +ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) + +ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) +$(error Configurable policy does not support legacy conf file) +endif #ifneq ($(USE_XML_AUDIO_POLICY_CONF), 1) + +LOCAL_SHARED_LIBRARIES += libaudiopolicyengineconfigurable + +else + +LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault + +endif # ifeq ($(USE_CONFIGURABLE_AUDIO_POLICY), 1) + +LOCAL_C_INCLUDES += \ + $(call include-path-for, audio-utils) + +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers \ + libaudiopolicymanager_interface_headers + +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents + +LOCAL_SHARED_LIBRARIES += libmedia_helper +LOCAL_SHARED_LIBRARIES += libmediametrics + +LOCAL_SHARED_LIBRARIES += libbinder libhidlbase libxml2 + +ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) +LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF +endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1) + +LOCAL_CFLAGS += -Wall -Werror + +LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) + +LOCAL_MODULE:= libaudiopolicymanagerdefault + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk new file mode 100644 index 0000000000..c4f4c56087 --- /dev/null +++ b/services/audiopolicy/service/Android.mk @@ -0,0 +1,49 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyService.cpp \ + AudioPolicyEffects.cpp \ + AudioPolicyInterfaceImpl.cpp \ + AudioPolicyClientImpl.cpp + +LOCAL_C_INCLUDES := \ + frameworks/av/services/audioflinger \ + $(call include-path-for, audio-utils) + +LOCAL_HEADER_LIBRARIES := \ + libaudiopolicycommon \ + libaudiopolicyengine_interface_headers \ + libaudiopolicymanager_interface_headers + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libaudioclient \ + libaudioutils \ + libhardware_legacy \ + libaudiopolicymanager \ + libmedia_helper \ + libmediametrics \ + libmediautils \ + libeffectsconfig \ + libsensorprivacy + +LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \ + libsensorprivacy + +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents + +LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB) + +LOCAL_MODULE:= libaudiopolicyservice + +LOCAL_CFLAGS += -fvisibility=hidden +LOCAL_CFLAGS += -Wall -Werror + +include $(BUILD_SHARED_LIBRARY) + diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 6c19912f16..2926acb585 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -33,7 +33,7 @@ #include #include #include "AudioPolicyEffects.h" -#include "managerdefault/AudioPolicyManager.h" +#include #include #include diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk index 97be44cea2..ab9f78b4d5 100644 --- a/services/audiopolicy/tests/Android.mk +++ b/services/audiopolicy/tests/Android.mk @@ -18,7 +18,8 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_HEADER_LIBRARIES := \ libaudiopolicycommon \ - libaudiopolicyengine_interface_headers + libaudiopolicyengine_interface_headers \ + libaudiopolicymanager_interface_headers LOCAL_SRC_FILES := \ audiopolicymanager_tests.cpp \ -- GitLab From dcfecd0db52d7880566662f8dd7cfd623b500471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 17 Apr 2019 16:58:56 +0200 Subject: [PATCH 1329/1530] audiopolicy: engineconfigurable: align with PFW unix socket convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL aligns the engineconfigurable PFW top file to the convention adopted within parameter-framework. Bug: 130284799 Test: make Change-Id: I146430a1a6618d73273dbfd85b060d9fd04a6f9a Signed-off-by: François Gaffie --- .../parameter-framework/examples/Android.mk | 8 ++++---- .../examples/ParameterFrameworkConfigurationPolicy.xml.in | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk index 82891a8336..19f93b3b27 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/Android.mk @@ -46,11 +46,11 @@ LOCAL_REQUIRED_MODULES := \ # external/parameter-framework prevents from using debug interface AUDIO_PATTERN = @TUNING_ALLOWED@ -#ifeq ($(TARGET_BUILD_VARIANT),user) +ifeq ($(TARGET_BUILD_VARIANT),user) AUDIO_VALUE = false -#else -#AUDIO_VALUE = true -#endif +else +AUDIO_VALUE = true +endif LOCAL_POST_INSTALL_CMD := $(hide) sed -i -e 's|$(AUDIO_PATTERN)|$(AUDIO_VALUE)|g' $(TARGET_OUT_VENDOR_ETC)/$(LOCAL_MODULE_RELATIVE_PATH)/$(LOCAL_MODULE) diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in b/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in index f80a07fc1f..1be67dd6ff 100644 --- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in +++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/ParameterFrameworkConfigurationPolicy.xml.in @@ -1,6 +1,6 @@ -- GitLab From b16194d50f62eee1f4da85552a8ae4661665ada9 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Fri, 19 Apr 2019 16:04:01 -0700 Subject: [PATCH 1330/1530] camera2 vndk: Add ACameraMananger_getTagFromName. In some situations, clients of the vndk might need to be able to query tags at runtime, based on tag names. For example, when client hals are de-coupled from the camera HAL. Bug: 131093919 Test: AImageReaderVendorTest Test: Modify AImageReaderVendorTest to retrieve vendor tags given their names, using ACameraMetadata_getTagFromName Change-Id: I1cdec5b154037185e99d29be2c6890e4fdc4a32a Signed-off-by: Jayant Chowdhary --- camera/ndk/Android.bp | 8 +++++++ camera/ndk/NdkCameraManager.cpp | 14 ++++++++++++ camera/ndk/include/camera/NdkCameraManager.h | 17 ++++++++++++++ camera/ndk/ndk_vendor/impl/ACameraManager.cpp | 22 +++++++++++++++++++ camera/ndk/ndk_vendor/impl/ACameraManager.h | 1 + .../tests/AImageReaderVendorTest.cpp | 9 ++++++++ 6 files changed, 71 insertions(+) diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp index d96f4039e9..77868567c9 100644 --- a/camera/ndk/Android.bp +++ b/camera/ndk/Android.bp @@ -64,6 +64,10 @@ cc_library_shared { "-Wextra", "-Werror", ], + // TODO: jchowdhary@, use header_libs instead b/131165718 + include_dirs: [ + "system/media/private/camera/include", + ], export_include_dirs: ["include"], export_shared_lib_headers: [ "libnativewindow", @@ -123,6 +127,10 @@ cc_library_shared { "android.hardware.camera.common@1.0-helper", "libarect", ], + // TODO: jchowdhary@, use header_libs instead b/131165718 + include_dirs: [ + "system/media/private/camera/include", + ], product_variables: { pdk: { enabled: false, diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp index 23d01efcac..3d231a8107 100644 --- a/camera/ndk/NdkCameraManager.cpp +++ b/camera/ndk/NdkCameraManager.cpp @@ -190,3 +190,17 @@ camera_status_t ACameraManager_openCamera( } return mgr->openCamera(cameraId, callback, device); } + +#ifdef __ANDROID_VNDK__ +EXPORT +camera_status_t ACameraManager_getTagFromName(ACameraManager *mgr, const char* cameraId, + const char *name, /*out*/uint32_t *tag) { + ATRACE_CALL(); + if (mgr == nullptr || cameraId == nullptr || name == nullptr) { + ALOGE("%s: invalid argument! mgr %p cameraId %p name %p", + __FUNCTION__, mgr, cameraId, name); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + return mgr->getTagFromName(cameraId, name, tag); +} +#endif diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index 5c810bbc5d..2cc8a9743b 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -374,6 +374,23 @@ camera_status_t ACameraManager_unregisterExtendedAvailabilityCallback( ACameraManager* manager, const ACameraManager_ExtendedAvailabilityCallbacks* callback) __INTRODUCED_IN(29); +#ifdef __ANDROID_VNDK__ +/** + * Retrieve the tag value, given the tag name and camera id. + * This method is device specific since some metadata might be defined by device manufacturers + * and might only be accessible for specific cameras. + * @param manager The {@link ACameraManager} of interest. + * @param cameraId The cameraId, which is used to query camera characteristics. + * @param name The name of the tag being queried. + * @param tag The output tag assigned by this method. + * + * @return ACAMERA_OK only if the function call was successful. + */ +camera_status_t ACameraManager_getTagFromName(ACameraManager *manager, const char* cameraId, + const char *name, /*out*/uint32_t *tag) + __INTRODUCED_IN(29); +#endif + #endif /* __ANDROID_API__ >= 29 */ __END_DECLS diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp index 575ee9dd6f..70c887acea 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -22,6 +22,8 @@ #include "ACameraMetadata.h" #include "ndk_vendor/impl/ACameraDevice.h" #include "utils.h" +#include +#include #include #include @@ -587,6 +589,26 @@ ACameraManager::openCamera( return ACAMERA_OK; } +camera_status_t +ACameraManager::getTagFromName(const char *cameraId, const char *name, uint32_t *tag) { + sp rawChars; + camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars); + if (ret != ACAMERA_OK) { + ALOGE("%s, Cannot retrieve camera characteristics for camera id %s", __FUNCTION__, + cameraId); + return ACAMERA_ERROR_METADATA_NOT_FOUND; + } + const CameraMetadata& metadata = rawChars->getInternalData(); + const camera_metadata_t *rawMetadata = metadata.getAndLock(); + metadata_vendor_id_t vendorTagId = get_camera_metadata_vendor_id(rawMetadata); + metadata.unlock(rawMetadata); + sp vtCache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + sp vTags = nullptr; + vtCache->getVendorTagDescriptor(vendorTagId, &vTags); + status_t status= metadata.getTagFromName(name, vTags.get(), tag); + return status == OK ? ACAMERA_OK : ACAMERA_ERROR_METADATA_NOT_FOUND; +} + ACameraManager::~ACameraManager() { } diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h index df6935336f..2c62d44b0f 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.h +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -204,6 +204,7 @@ struct ACameraManager { camera_status_t openCamera(const char* cameraId, ACameraDevice_StateCallbacks* callback, /*out*/ACameraDevice** device); + camera_status_t getTagFromName(const char *cameraId, const char *name, uint32_t *tag); private: enum { diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index 7368775711..37de30ab01 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -799,6 +799,15 @@ TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) { bool isBC = isCapabilitySupported(staticMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); + uint32_t namedTag = 0; + // Test that ACameraMetadata_getTagFromName works as expected for public tag + // names + camera_status_t status = ACameraManager_getTagFromName(mCameraManager, cameraId, + "android.control.aeMode", &namedTag); + + ASSERT_EQ(status, ACAMERA_OK); + ASSERT_EQ(namedTag, ACAMERA_CONTROL_AE_MODE); + ACameraMetadata_free(staticMetadata); if (!isBC) { -- GitLab From e55d13cea8946ba51fa51d9e07e29bda141b12f8 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 24 Apr 2019 11:01:49 -0700 Subject: [PATCH 1331/1530] Make tombstone in the child process on loading failure. Test: dumpsys media.extractor lshal debug android.hardware.media.c2@1.0::IComponentStore/software Check 'ls /data/system/dropbox' after failure Bug: 131106476 Change-Id: I6619a16f579678d0e0c2675dd1457c917f3883c2 --- media/codec2/vndk/C2Store.cpp | 29 ++++++++++++------- .../libstagefright/MediaExtractorFactory.cpp | 28 ++++++++++-------- .../media/stagefright/foundation/ADebug.h | 9 ++++++ 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 10c4dcc574..f5dc838f5e 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -661,31 +662,37 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( ALOGV("in %s", __func__); ALOGV("loading dll"); mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE); - LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, - "could not dlopen %s: %s", libPath.c_str(), dlerror()); + if (mLibHandle == nullptr) { + LOG_ALWAYS_FATAL_IN_CHILD_PROC("could not dlopen %s: %s", libPath.c_str(), dlerror()); + mInit = C2_CORRUPTED; + return mInit; + } createFactory = (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory"); - LOG_ALWAYS_FATAL_IF(createFactory == nullptr, - "createFactory is null in %s", libPath.c_str()); + if (createFactory == nullptr) { + LOG_ALWAYS_FATAL_IN_CHILD_PROC("createFactory is null in %s", libPath.c_str()); + mInit = C2_CORRUPTED; + return mInit; + } destroyFactory = (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory"); - LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr, - "destroyFactory is null in %s", libPath.c_str()); + if (destroyFactory == nullptr) { + LOG_ALWAYS_FATAL_IN_CHILD_PROC("destroyFactory is null in %s", libPath.c_str()); + mInit = C2_CORRUPTED; + return mInit; + } mComponentFactory = createFactory(); if (mComponentFactory == nullptr) { ALOGD("could not create factory in %s", libPath.c_str()); mInit = C2_NO_MEMORY; - } else { - mInit = C2_OK; - } - - if (mInit != C2_OK) { return mInit; } + mInit = C2_OK; + std::shared_ptr intf; c2_status_t res = createInterface(0, &intf); if (res != C2_OK) { diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index d97591f8a3..81d2abbced 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -19,11 +19,11 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -245,17 +245,21 @@ void MediaExtractorFactory::RegisterExtractors( void *libHandle = android_dlopen_ext( libPath.string(), RTLD_NOW | RTLD_LOCAL, dlextinfo); - CHECK(libHandle != nullptr) - << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno); - - GetExtractorDef getDef = - (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); - CHECK(getDef != nullptr) - << libPath.string() << " does not contain sniffer"; - - ALOGV("registering sniffer for %s", libPath.string()); - RegisterExtractor( - new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); + if (libHandle) { + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + if (getDef) { + ALOGV("registering sniffer for %s", libPath.string()); + RegisterExtractor( + new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); + } else { + LOG_ALWAYS_FATAL_IN_CHILD_PROC("%s does not contain sniffer", libPath.string()); + dlclose(libHandle); + } + } else { + LOG_ALWAYS_FATAL_IN_CHILD_PROC( + "couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); + } } closedir(libDir); } else { diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h index a8b88fdd64..180694b4d4 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h @@ -123,6 +123,15 @@ inline static const char *asString(status_t i, const char *def = "??") { #define TRESPASS_DBG(...) #endif +#ifndef LOG_ALWAYS_FATAL_IN_CHILD_PROC +#define LOG_ALWAYS_FATAL_IN_CHILD_PROC(...) \ + do { \ + if (fork() == 0) { \ + LOG_ALWAYS_FATAL(__VA_ARGS__); \ + } \ + } while (false) +#endif + struct ADebug { enum Level { kDebugNone, // no debug -- GitLab From 38560f281024985d486b24ab8cb1a5f36d6f3b6a Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Wed, 24 Apr 2019 13:36:50 -0700 Subject: [PATCH 1332/1530] VideoFrameSchedulerBase: fix overflow in PLL::fit() PLL::fit() may have addition or multiplication overflow problem. Bug: 129491498 Test: TBD Change-Id: Id9b4bc0d2e53c1b4c239027c8b087020019a0147 (cherry picked from commit 3ba81e73eb15b809373da491dae4e7fe2792aa13) --- media/libstagefright/VideoFrameSchedulerBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/libstagefright/VideoFrameSchedulerBase.cpp b/media/libstagefright/VideoFrameSchedulerBase.cpp index 77107ff90f..912dcf3269 100644 --- a/media/libstagefright/VideoFrameSchedulerBase.cpp +++ b/media/libstagefright/VideoFrameSchedulerBase.cpp @@ -115,6 +115,7 @@ void VideoFrameSchedulerBase::PLL::test() { #endif +__attribute__((no_sanitize("integer"))) bool VideoFrameSchedulerBase::PLL::fit( nsecs_t phase, nsecs_t period, size_t numSamplesToUse, int64_t *a, int64_t *b, int64_t *err) { -- GitLab From 2996f67767ff90617cc5f7801c7bd8dc67c9dd66 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 18 Apr 2019 12:29:59 -0700 Subject: [PATCH 1333/1530] AudioPolicy/AudioFlinger: Track AudioRecords via Record IDs The client must provide a unique Record ID (RIID) when creating an AudioRecord. This RIID is passed down to AudioInputDescriptor in AudioPolicyManager which sends configuration updates via IAudioPolicyServiceClient callback. By supplying RIID, the Audio Service can coalesce start / stop events coming from clients (apps) with recording configuration update events. For AAudio MMap clients everything is handled at the server side because they correspond directly to audioserver objects. Bug: 123312504 Test: android.media.cts.AudioRecordingConfigurationTest AudioRecordTest#testAudioRecordInfoCallback MediaRecorderTest#testAudioRecordInfoCallback manual testing using Oboe and Solo test apps Change-Id: I3d32241752d9a747736606dc4cb1e068e6b7aa3b --- media/libaudioclient/Android.bp | 1 + media/libaudioclient/AudioRecord.cpp | 15 +++-- media/libaudioclient/AudioSystem.cpp | 4 +- media/libaudioclient/IAudioPolicyService.cpp | 5 +- .../IAudioPolicyServiceClient.cpp | 2 + .../RecordingActivityTracker.cpp | 63 +++++++++++++++++++ .../include/media/AudioPolicy.h | 10 +-- .../include/media/AudioRecord.h | 7 ++- .../include/media/AudioSystem.h | 1 + .../include/media/IAudioFlinger.h | 5 ++ .../include/media/IAudioPolicyService.h | 1 + .../include/media/IAudioPolicyServiceClient.h | 1 + .../include/media/RecordingActivityTracker.h | 44 +++++++++++++ services/audioflinger/AudioFlinger.cpp | 3 + services/audioflinger/Threads.cpp | 2 + services/audiopolicy/AudioPolicyInterface.h | 1 + .../include/ClientDescriptor.h | 13 ++-- .../src/AudioInputDescriptor.cpp | 19 ++++-- .../managerdefault/AudioPolicyManager.cpp | 3 +- .../managerdefault/AudioPolicyManager.h | 1 + .../service/AudioPolicyInterfaceImpl.cpp | 3 +- .../audiopolicy/service/AudioPolicyService.h | 1 + 22 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 media/libaudioclient/RecordingActivityTracker.cpp create mode 100644 media/libaudioclient/include/media/RecordingActivityTracker.h diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index e9b6fb19a1..03bd6ce945 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -59,6 +59,7 @@ cc_library_shared { "IEffectClient.cpp", "ToneGenerator.cpp", "PlayerBase.cpp", + "RecordingActivityTracker.cpp", "TrackPlayerBase.cpp", ], shared_libs: [ diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index f07be46ed3..0cce5bc89c 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -22,7 +22,11 @@ #include #include +#include +#include +#include #include +#include #include #include #include @@ -219,6 +223,8 @@ status_t AudioRecord::set( inputSource, sampleRate, format, channelMask, frameCount, notificationFrames, sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid); + mTracker.reset(new RecordingActivityTracker()); + mSelectedDeviceId = selectedDeviceId; mSelectedMicDirection = selectedMicDirection; mSelectedMicFieldDimension = microphoneFieldDimension; @@ -396,6 +402,7 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri // This is legacy behavior. This is not done in stop() to avoid a race condition // where the last marker event is issued twice. mMarkerReached = false; + // mActive is checked by restoreRecord_l mActive = true; status_t status = NO_ERROR; @@ -416,7 +423,9 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri if (status != NO_ERROR) { mActive = false; ALOGE("%s(%d): status %d", __func__, mPortId, status); + mMediaMetrics.markError(status, __FUNCTION__); } else { + mTracker->recordingStarted(); sp t = mAudioRecordThread; if (t != 0) { t->resume(); @@ -429,10 +438,6 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t tri // we've successfully started, log that time mMediaMetrics.logStart(systemTime()); } - - if (status != NO_ERROR) { - mMediaMetrics.markError(status, __FUNCTION__); - } return status; } @@ -447,6 +452,7 @@ void AudioRecord::stop() mActive = false; mProxy->interrupt(); mAudioRecord->stop(); + mTracker->recordingStopped(); // Note: legacy handling - stop does not clear record marker and // periodic update position; we update those on start(). @@ -711,6 +717,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String } } input.opPackageName = opPackageName; + input.riid = mTracker->getRiid(); input.flags = mFlags; // The notification frame count is the period between callbacks, as suggested by the client diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index de82d2bef4..47e2c2810f 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -428,6 +428,7 @@ uint32_t AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) audio_unique_id_t AudioSystem::newAudioUniqueId(audio_unique_id_use_t use) { + // Must not use AF as IDs will re-roll on audioserver restart, b/130369529. const sp& af = AudioSystem::get_audio_flinger(); if (af == 0) return AUDIO_UNIQUE_ID_ALLOCATE; return af->newAudioUniqueId(use); @@ -924,6 +925,7 @@ void AudioSystem::releaseOutput(audio_port_handle_t portId) status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, @@ -936,7 +938,7 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return NO_INIT; return aps->getInputForAttr( - attr, input, session, pid, uid, opPackageName, + attr, input, riid, session, pid, uid, opPackageName, config, flags, selectedDeviceId, portId); } diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 4a8bb522d2..9b4221c929 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -305,6 +305,7 @@ public: virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, @@ -334,6 +335,7 @@ public: } data.write(attr, sizeof(audio_attributes_t)); data.writeInt32(*input); + data.writeInt32(riid); data.writeInt32(session); data.writeInt32(pid); data.writeInt32(uid); @@ -1511,6 +1513,7 @@ status_t BnAudioPolicyService::onTransact( data.read(&attr, sizeof(audio_attributes_t)); sanetizeAudioAttributes(&attr); audio_io_handle_t input = (audio_io_handle_t)data.readInt32(); + audio_unique_id_t riid = (audio_unique_id_t)data.readInt32(); audio_session_t session = (audio_session_t)data.readInt32(); pid_t pid = (pid_t)data.readInt32(); uid_t uid = (uid_t)data.readInt32(); @@ -1521,7 +1524,7 @@ status_t BnAudioPolicyService::onTransact( audio_input_flags_t flags = (audio_input_flags_t) data.readInt32(); audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32(); audio_port_handle_t portId = (audio_port_handle_t)data.readInt32(); - status_t status = getInputForAttr(&attr, &input, session, pid, uid, + status_t status = getInputForAttr(&attr, &input, riid, session, pid, uid, opPackageName, &config, flags, &selectedDeviceId, &portId); reply->writeInt32(status); diff --git a/media/libaudioclient/IAudioPolicyServiceClient.cpp b/media/libaudioclient/IAudioPolicyServiceClient.cpp index 52d8ccd175..0f9580c3ff 100644 --- a/media/libaudioclient/IAudioPolicyServiceClient.cpp +++ b/media/libaudioclient/IAudioPolicyServiceClient.cpp @@ -50,6 +50,7 @@ inline void writeAudioConfigBaseToParcel(Parcel& data, const audio_config_base_t } inline void readRecordClientInfoFromParcel(const Parcel& data, record_client_info_t *clientInfo) { + clientInfo->riid = (audio_unique_id_t) data.readInt32(); clientInfo->uid = (uid_t) data.readUint32(); clientInfo->session = (audio_session_t) data.readInt32(); clientInfo->source = (audio_source_t) data.readInt32(); @@ -58,6 +59,7 @@ inline void readRecordClientInfoFromParcel(const Parcel& data, record_client_inf } inline void writeRecordClientInfoToParcel(Parcel& data, const record_client_info_t *clientInfo) { + data.writeInt32((int32_t) clientInfo->riid); data.writeUint32((uint32_t) clientInfo->uid); data.writeInt32((int32_t) clientInfo->session); data.writeInt32((int32_t) clientInfo->source); diff --git a/media/libaudioclient/RecordingActivityTracker.cpp b/media/libaudioclient/RecordingActivityTracker.cpp new file mode 100644 index 0000000000..bd10895daf --- /dev/null +++ b/media/libaudioclient/RecordingActivityTracker.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 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 +#include +#include +#include +#include + +namespace android { + +RecordingActivityTracker::RecordingActivityTracker() + : mRIId(RECORD_RIID_INVALID), mToken(new BBinder()) +{ + // use checkService() to avoid blocking if audio service is not up yet + sp binder = defaultServiceManager()->checkService(String16("audio")); + if (binder != 0) { + mAudioManager = interface_cast(binder); + } else { + ALOGE("RecordingActivityTracker(): binding to audio service failed, service up?"); + } +} + +RecordingActivityTracker::~RecordingActivityTracker() +{ +} + +audio_unique_id_t RecordingActivityTracker::getRiid() +{ + if (mRIId == RECORD_RIID_INVALID && mAudioManager) { + mRIId = mAudioManager->trackRecorder(mToken); + } + return mRIId; +} + +void RecordingActivityTracker::recordingStarted() +{ + if (getRiid() != RECORD_RIID_INVALID && mAudioManager) { + mAudioManager->recorderEvent(mRIId, RECORDER_STATE_STARTED); + } +} + +void RecordingActivityTracker::recordingStopped() +{ + if (getRiid() != RECORD_RIID_INVALID && mAudioManager) { + mAudioManager->recorderEvent(mRIId, RECORDER_STATE_STOPPED); + } +} + +} // namespace android diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index 4b94c12987..a40e019d3e 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -119,11 +119,11 @@ public: }; -// definitions for audio recording configuration updates -// which update type is reported -#define RECORD_CONFIG_EVENT_NONE -1 -#define RECORD_CONFIG_EVENT_START 1 -#define RECORD_CONFIG_EVENT_STOP 0 +// definitions for audio recording configuration updates; +// keep in sync with AudioManager.java for values used from native code +#define RECORD_CONFIG_EVENT_START 0 +#define RECORD_CONFIG_EVENT_STOP 1 +#define RECORD_CONFIG_EVENT_UPDATE 2 static inline bool is_mix_loopback_render(uint32_t routeFlags) { return (routeFlags & MIX_ROUTE_FLAG_LOOP_BACK_AND_RENDER) diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index 9c81bb7a4b..a3c0fe4ac5 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -17,6 +17,9 @@ #ifndef ANDROID_AUDIORECORD_H #define ANDROID_AUDIORECORD_H +#include +#include + #include #include #include @@ -24,9 +27,9 @@ #include #include #include +#include #include #include -#include #include "android/media/IAudioRecord.h" @@ -618,6 +621,8 @@ private: sp mAudioRecordThread; mutable Mutex mLock; + std::unique_ptr mTracker; + // Current client state: false = stopped, true = active. Protected by mLock. If more states // are added, consider changing this to enum State { ... } mState as in AudioTrack. bool mActive; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index d180bbc948..d3035dacb6 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -242,6 +242,7 @@ public: // or release it with releaseInput(). static status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index dcc18b6603..8ec8931347 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -205,6 +205,9 @@ public: return DEAD_OBJECT; } opPackageName = parcel->readString16(); + if (parcel->read(&riid, sizeof(audio_unique_id_t)) != NO_ERROR) { + return DEAD_OBJECT; + } /* input/output arguments*/ (void)parcel->read(&flags, sizeof(audio_input_flags_t)); @@ -221,6 +224,7 @@ public: (void)parcel->write(&config, sizeof(audio_config_base_t)); (void)clientInfo.writeToParcel(parcel); (void)parcel->writeString16(opPackageName); + (void)parcel->write(&riid, sizeof(audio_unique_id_t)); /* input/output arguments*/ (void)parcel->write(&flags, sizeof(audio_input_flags_t)); @@ -236,6 +240,7 @@ public: audio_config_base_t config; AudioClient clientInfo; String16 opPackageName; + audio_unique_id_t riid; /* input/output */ audio_input_flags_t flags; diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 11983d5149..b6390442c4 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -73,6 +73,7 @@ public: virtual void releaseOutput(audio_port_handle_t portId) = 0; virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, diff --git a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h index 79008c3ccd..47b31eedc3 100644 --- a/media/libaudioclient/include/media/IAudioPolicyServiceClient.h +++ b/media/libaudioclient/include/media/IAudioPolicyServiceClient.h @@ -31,6 +31,7 @@ namespace android { // ---------------------------------------------------------------------------- struct record_client_info { + audio_unique_id_t riid; uid_t uid; audio_session_t session; audio_source_t source; diff --git a/media/libaudioclient/include/media/RecordingActivityTracker.h b/media/libaudioclient/include/media/RecordingActivityTracker.h new file mode 100644 index 0000000000..9891a701a5 --- /dev/null +++ b/media/libaudioclient/include/media/RecordingActivityTracker.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 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_RECORDING_ACTIVITY_TRACKER_H__ +#define __ANDROID_RECORDING_ACTIVITY_TRACKER_H__ + +#include + +namespace android { + +class IAudioManager; +class IBinder; + +class RecordingActivityTracker +{ +public: + RecordingActivityTracker(); + ~RecordingActivityTracker(); + audio_unique_id_t getRiid(); + void recordingStarted(); + void recordingStopped(); + +private: + sp mAudioManager; + audio_unique_id_t mRIId; + sp mToken; +}; + +} // namespace android + +#endif // __ANDROID_RECORDING_ACTIVITY_TRACKER_H__ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 55db699e02..0f03b7e58d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -46,6 +46,7 @@ #include #include +#include #include "AudioFlinger.h" #include "NBAIO_Tee.h" @@ -312,6 +313,7 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di "%s does not support secondary outputs, ignoring them", __func__); } else { ret = AudioSystem::getInputForAttr(attr, &io, + RECORD_RIID_INVALID, actualSessionId, client.clientPid, client.clientUid, @@ -1889,6 +1891,7 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu portId = AUDIO_PORT_HANDLE_NONE; } lStatus = AudioSystem::getInputForAttr(&input.attr, &output.inputId, + input.riid, sessionId, // FIXME compare to AudioTrack clientPid, diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 711a6ddfc2..fd29f31bc3 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -59,6 +59,7 @@ #include #include +#include #include #include @@ -8561,6 +8562,7 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, config.format = mFormat; audio_port_handle_t deviceId = mDeviceId; ret = AudioSystem::getInputForAttr(&mAttr, &io, + RECORD_RIID_INVALID, mSessionId, client.clientPid, client.clientUid, diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 4b56a46b83..49937f0afa 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -124,6 +124,7 @@ public: // request an input appropriate for record from the supplied device with supplied parameters. virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, uid_t uid, const audio_config_base_t *config, diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h index 4bb225d7d3..0d05a636d5 100644 --- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -146,20 +147,23 @@ private: class RecordClientDescriptor: public ClientDescriptor { public: - RecordClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId, - audio_attributes_t attributes, audio_config_base_t config, - audio_port_handle_t preferredDeviceId, + RecordClientDescriptor(audio_port_handle_t portId, audio_unique_id_t riid, uid_t uid, + audio_session_t sessionId, audio_attributes_t attributes, + audio_config_base_t config, audio_port_handle_t preferredDeviceId, audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) : ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId), - mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mAppState(APP_STATE_IDLE) {} + mRIId(riid), mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), + mAppState(APP_STATE_IDLE) {} ~RecordClientDescriptor() override = default; using ClientDescriptor::dump; void dump(String8 *dst, int spaces, int index) const override; + audio_unique_id_t riid() const { return mRIId; } audio_source_t source() const { return mSource; } audio_input_flags_t flags() const { return mFlags; } bool isSoundTrigger() const { return mIsSoundTrigger; } + bool isLowLevel() const { return mRIId == RECORD_RIID_INVALID; } void setAppState(app_state_t appState) { mAppState = appState; } app_state_t appState() { return mAppState; } bool isSilenced() const { return mAppState == APP_STATE_IDLE; } @@ -167,6 +171,7 @@ public: EffectDescriptorCollection getEnabledEffects() const { return mEnabledEffects; } private: + const audio_unique_id_t mRIId; const audio_source_t mSource; const audio_input_flags_t mFlags; const bool mIsSoundTrigger; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 58683be797..a096e8fd2b 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "APM::AudioInputDescriptor" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -179,7 +180,9 @@ void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle) mPatchHandle = handle; for (const auto &client : getClientIterable()) { if (client->active()) { - updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + updateClientRecordingConfiguration( + client->isLowLevel() ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_UPDATE, + client); } } } @@ -342,15 +345,19 @@ void AudioInputDescriptor::setClientActive(const sp& cli void AudioInputDescriptor::updateClientRecordingConfiguration( int event, const sp& client) { + ALOGV("%s riid %d uid %d port %d session %d event %d", + __func__, client->riid(), client->uid(), client->portId(), client->session(), event); // do not send callback if starting and no device is selected yet to avoid // double callbacks from startInput() before and after the device is selected - if (event == RECORD_CONFIG_EVENT_START - && mPatchHandle == AUDIO_PATCH_HANDLE_NONE) { + // "start" and "stop" events for "high level" clients (AudioRecord) are sent by the client side + if ((event == RECORD_CONFIG_EVENT_START && mPatchHandle == AUDIO_PATCH_HANDLE_NONE) + || (!client->isLowLevel() + && (event == RECORD_CONFIG_EVENT_START || event == RECORD_CONFIG_EVENT_STOP))) { return; } const audio_config_base_t sessionConfig = client->config(); - const record_client_info_t recordClientInfo{client->uid(), client->session(), + const record_client_info_t recordClientInfo{client->riid(), client->uid(), client->session(), client->source(), client->portId(), client->isSilenced()}; const audio_config_base_t config = getConfig(); @@ -429,7 +436,7 @@ void AudioInputDescriptor::trackEffectEnabled(const sp &effect checkSuspendEffects(); for (const auto& client : updatedClients) { - updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_UPDATE, client); } } @@ -462,7 +469,7 @@ void AudioInputDescriptor::setAppState(uid_t uid, app_state_t state) checkSuspendEffects(); for (const auto& client : updatedClients) { - updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client); + updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_UPDATE, client); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6bd64d61fd..066363f54a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1881,6 +1881,7 @@ void AudioPolicyManager::releaseOutput(audio_port_handle_t portId) status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, uid_t uid, const audio_config_base_t *config, @@ -2024,7 +2025,7 @@ exit: mSoundTriggerSessions.indexOfKey(session) > 0; *portId = AudioPort::getNextUniqueId(); - clientDesc = new RecordClientDescriptor(*portId, uid, session, attributes, *config, + clientDesc = new RecordClientDescriptor(*portId, riid, uid, session, attributes, *config, requestedDeviceId, attributes.source, flags, isSoundTrigger); inputDesc = mInputs.valueFor(*input); diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 3376965a57..835fbdc88c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -128,6 +128,7 @@ public: virtual void releaseOutput(audio_port_handle_t portId); virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, uid_t uid, const audio_config_base_t *config, diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 2e47eb67cf..06e68a9a19 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -334,6 +334,7 @@ void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId) status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, @@ -403,7 +404,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, { AutoCallerClear acc; // the audio_in_acoustics_t parameter is ignored by get_input() - status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid, + status = mAudioPolicyManager->getInputForAttr(attr, input, riid, session, uid, config, flags, selectedDeviceId, &inputType, portId); diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 6c19912f16..be6d1ac98e 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -91,6 +91,7 @@ public: virtual void releaseOutput(audio_port_handle_t portId); virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, + audio_unique_id_t riid, audio_session_t session, pid_t pid, uid_t uid, -- GitLab From 08ad212360ddab7eec9210b204352b0a7fb77981 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Thu, 25 Apr 2019 12:45:55 +0900 Subject: [PATCH 1334/1530] Add /system/lib to the search paths for asan When running with asan, the platform namespace didn't have /system/lib, but /data/asan/system/lib in its search paths. As a result, clang runtime libraries like libclang-rt.asan-i686-android.so which are still in /system/lib cound not be found. Fixing the problem by adding /system/lib to the search paths after the asan path. Bug: 131170196 Test: check if mediaswcodec process is running in aosp_cf_x86_pasan Change-Id: Iffe5d25613999a00ead5eb03c0efca1d6c9ce012 --- apex/ld.config.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index 715113d432..87af5a17cf 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -38,7 +38,8 @@ namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv namespace.platform.isolated = true namespace.platform.search.paths = /system/${LIB} -namespace.platform.asan.search.paths = /data/asan/system/${LIB} +namespace.platform.asan.search.paths = /data/asan/system/${LIB} +namespace.platform.asan.search.paths += /system/${LIB} # /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc. # Add /apex/... pat to the permitted paths because linker uses realpath(3) -- GitLab From 784bfac0b0e32c668aefdae2281e1f1c370c9097 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Thu, 25 Apr 2019 17:42:06 +0900 Subject: [PATCH 1335/1530] Install extractors for 1st and 2nd ABIs to the test APEX The test APEX is built with aosp_x86_64 where first ABI is 64-bit. But the test APEX can be installed to 32-bit target like cf_x86_phone. The lack of 32-bit extractor libraries in the 32-bit target can cause problem. So, install both ABIs of the extractor libraries to the test APEX. Bug: 125914810 Test: choosecombo to aosp_x86_64 and build test_com.android.media Check that the test APEX has both lib and lib64 directories. Change-Id: Ib073f61838fea0b823c870c8c71b49a7ecc9346e --- apex/Android.bp | 37 ++++++++++++++++--------------- apex/testing/Android.bp | 7 ++++++ media/extractors/aac/Android.bp | 2 +- media/extractors/amr/Android.bp | 2 +- media/extractors/flac/Android.bp | 2 +- media/extractors/midi/Android.bp | 2 +- media/extractors/mkv/Android.bp | 2 +- media/extractors/mp3/Android.bp | 2 +- media/extractors/mp4/Android.bp | 2 +- media/extractors/mpeg2/Android.bp | 2 +- media/extractors/ogg/Android.bp | 2 +- media/extractors/wav/Android.bp | 2 +- 12 files changed, 36 insertions(+), 28 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index 2cc6fcb854..ec0efe6985 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -12,27 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +com_android_media_extractors = [ + "libaacextractor", + "libamrextractor", + "libflacextractor", + "libmidiextractor", + "libmkvextractor", + "libmp3extractor", + "libmp4extractor", + "libmpeg2extractor", + "liboggextractor", + "libwavextractor", +] + apex_defaults { name: "com.android.media-defaults", java_libs: ["updatable-media"], - multilib: { - first: { - // Extractor process runs only with the primary ABI. - native_shared_libs: [ - // Extractor plugins - "libaacextractor", - "libamrextractor", - "libflacextractor", - "libmidiextractor", - "libmkvextractor", - "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", - "libwavextractor", - ], - }, - }, key: "com.android.media.key", certificate: ":com.android.media.certificate", @@ -44,6 +39,12 @@ apex { name: "com.android.media", manifest: "manifest.json", defaults: ["com.android.media-defaults"], + multilib: { + first: { + // Extractor process runs only with the primary ABI. + native_shared_libs: com_android_media_extractors, + }, + }, } filegroup { diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp index 701ced732e..297d864c29 100644 --- a/apex/testing/Android.bp +++ b/apex/testing/Android.bp @@ -17,6 +17,13 @@ apex { manifest: "test_manifest.json", file_contexts: "com.android.media", defaults: ["com.android.media-defaults"], + multilib: { + both: { + // for test apex, built for both ABIs + native_shared_libs: com_android_media_extractors, + }, + }, + compile_multilib: "both", installable: false, } diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp index a58167a655..6fe5970dd0 100644 --- a/media/extractors/aac/Android.bp +++ b/media/extractors/aac/Android.bp @@ -20,7 +20,7 @@ cc_library_shared { name: "libaacextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp index 4bd933deb5..b26b2d86c6 100644 --- a/media/extractors/amr/Android.bp +++ b/media/extractors/amr/Android.bp @@ -18,7 +18,7 @@ cc_library_shared { name: "libamrextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp index 3a3d051b70..3e830902f7 100644 --- a/media/extractors/flac/Android.bp +++ b/media/extractors/flac/Android.bp @@ -24,7 +24,7 @@ cc_library_shared { name: "libflacextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp index 7d42e703f3..6790dd6af0 100644 --- a/media/extractors/midi/Android.bp +++ b/media/extractors/midi/Android.bp @@ -19,7 +19,7 @@ cc_library_shared { name: "libmidiextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp index 1744d3d63f..7c941490b3 100644 --- a/media/extractors/mkv/Android.bp +++ b/media/extractors/mkv/Android.bp @@ -25,7 +25,7 @@ cc_library_shared { name: "libmkvextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp index 4e2f2480ac..168ef6801b 100644 --- a/media/extractors/mp3/Android.bp +++ b/media/extractors/mp3/Android.bp @@ -24,7 +24,7 @@ cc_library_shared { name: "libmp3extractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp index 1b308aa7ef..9b9c9310c1 100644 --- a/media/extractors/mp4/Android.bp +++ b/media/extractors/mp4/Android.bp @@ -32,7 +32,7 @@ cc_defaults { ], version_script: "exports.lds", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", } cc_library_shared { diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index 0f0c72cb82..14f49ae111 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -40,7 +40,7 @@ cc_library_shared { name: "libmpeg2extractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp index 604ec5945e..fd03f6479a 100644 --- a/media/extractors/ogg/Android.bp +++ b/media/extractors/ogg/Android.bp @@ -26,7 +26,7 @@ cc_library_shared { name: "liboggextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp index 7e89271e45..15fd7964aa 100644 --- a/media/extractors/wav/Android.bp +++ b/media/extractors/wav/Android.bp @@ -21,7 +21,7 @@ cc_library_shared { name: "libwavextractor", relative_install_path: "extractors", - compile_multilib: "first", + compile_multilib: "both", cflags: [ "-Werror", -- GitLab From 09940bbc0e7eb687c1b08c1ba0252852752f68ab Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Wed, 24 Apr 2019 14:58:51 -0700 Subject: [PATCH 1336/1530] Add comments This follows Id9b4bc0d2e53c1b4c239027c8b087020019a0147 Bug: 129491498 Test: TBD Change-Id: I8070836b24d46b1152c0af3584ffd444008632f6 --- media/libstagefright/VideoFrameSchedulerBase.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libstagefright/VideoFrameSchedulerBase.cpp b/media/libstagefright/VideoFrameSchedulerBase.cpp index 912dcf3269..0d1517b7cc 100644 --- a/media/libstagefright/VideoFrameSchedulerBase.cpp +++ b/media/libstagefright/VideoFrameSchedulerBase.cpp @@ -115,6 +115,9 @@ void VideoFrameSchedulerBase::PLL::test() { #endif +// If overflow happens, the value is already incorrect, and no mater what value we get is OK. +// And this part of calculation is not important, so it's OK to simply disable overflow check +// instead of using double which makes code more complicated. __attribute__((no_sanitize("integer"))) bool VideoFrameSchedulerBase::PLL::fit( nsecs_t phase, nsecs_t period, size_t numSamplesToUse, -- GitLab From 22a123984c7d6d43dbc8dffeb0c59d2e02818415 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Fri, 18 Jan 2019 15:02:33 +0800 Subject: [PATCH 1337/1530] MPEG2TSExtractor: support m2ts container m2ts is based on the MPEG-2 transport stream container. The standard MPEG2-2 TS 188-byte packet is prefixed with a 4-byte extra header to a total size of 192 bytes. This patch makes MPEG2TSextractor support both MPEG-2 transport stream and m2ts. mts file use the same container as ts file Bug: 123261411 Test: play m2ts file and check if it's normal Change-Id: I7798a6bf3fcddf064d6bb6e39cd303bf83c3489e (cherry picked from commit c1a464670859ecacf6c84aff855e2686fd18302b) --- media/extractors/mpeg2/ExtractorBundle.cpp | 2 ++ media/extractors/mpeg2/MPEG2TSExtractor.cpp | 28 ++++++++++++++++----- media/extractors/mpeg2/MPEG2TSExtractor.h | 1 + 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp index 946a2a9d99..a7c756bd05 100644 --- a/media/extractors/mpeg2/ExtractorBundle.cpp +++ b/media/extractors/mpeg2/ExtractorBundle.cpp @@ -29,6 +29,8 @@ struct CDataSource; static const char *extensions[] = { "m2p", + "m2ts", + "mts", "ts", NULL }; diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 49dd0b4e11..50ce657465 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -238,6 +238,12 @@ MPEG2TSExtractor::MPEG2TSExtractor(DataSourceHelper *source) mParser(new ATSParser), mLastSyncEvent(0), mOffset(0) { + char header; + if (source->readAt(0, &header, 1) == 1 && header == 0x47) { + mHeaderSkip = 0; + } else { + mHeaderSkip = 4; + } init(); } @@ -460,7 +466,7 @@ status_t MPEG2TSExtractor::feedMore(bool isInit) { Mutex::Autolock autoLock(mLock); uint8_t packet[kTSPacketSize]; - ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); + ssize_t n = mDataSource->readAt(mOffset + mHeaderSkip, packet, kTSPacketSize); if (n < (ssize_t)kTSPacketSize) { if (n >= 0) { @@ -470,7 +476,7 @@ status_t MPEG2TSExtractor::feedMore(bool isInit) { } ATSParser::SyncEvent event(mOffset); - mOffset += n; + mOffset += mHeaderSkip + n; status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event); if (event.hasReturnedData()) { if (isInit) { @@ -539,15 +545,15 @@ status_t MPEG2TSExtractor::estimateDurationsFromTimesUsAtEnd() { break; } - ssize_t n = mDataSource->readAt(offset, packet, kTSPacketSize); + ssize_t n = mDataSource->readAt(offset+mHeaderSkip, packet, kTSPacketSize); if (n < 0) { return n; } else if (n < (ssize_t)kTSPacketSize) { break; } - offset += kTSPacketSize; - bytesRead += kTSPacketSize; + offset += kTSPacketSize + mHeaderSkip; + bytesRead += kTSPacketSize + mHeaderSkip; err = parser->feedTSPacket(packet, kTSPacketSize, &ev); if (err != OK) { return err; @@ -791,7 +797,17 @@ bool SniffMPEG2TS(DataSourceHelper *source, float *confidence) { char header; if (source->readAt(kTSPacketSize * i, &header, 1) != 1 || header != 0x47) { - return false; + // not ts file, check if m2ts file + for (int j = 0; j < 5; ++j) { + char headers[5]; + if (source->readAt((kTSPacketSize + 4) * j, &headers, 5) != 5 + || headers[4] != 0x47) { + // not m2ts file too, return + return false; + } + } + ALOGV("this is m2ts file\n"); + break; } } diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h index 2537d3bd7f..dcd1e7ba05 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/MPEG2TSExtractor.h @@ -102,6 +102,7 @@ private: status_t estimateDurationsFromTimesUsAtEnd(); + size_t mHeaderSkip; DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor); }; -- GitLab From e3d0b7f473042d3d85946f6b3530deaf63e4fd02 Mon Sep 17 00:00:00 2001 From: Xu Date: Wed, 13 Feb 2019 13:00:10 +0800 Subject: [PATCH 1338/1530] oggextractor: fix duration calculation Bug: 122489262 Test: CTS, manual Change-Id: I1a3f7f7a1ec2dc8bb67ef1d3c15b8a8aae457296 Signed-off-by: melvin xu melvin.xu@spreadtrum.com --- media/extractors/ogg/OggExtractor.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp index b63ae6becb..72b94bb97b 100644 --- a/media/extractors/ogg/OggExtractor.cpp +++ b/media/extractors/ogg/OggExtractor.cpp @@ -323,6 +323,7 @@ MyOggExtractor::MyOggExtractor( mFirstDataOffset(-1), mHapticChannelCount(0) { mCurrentPage.mNumSegments = 0; + mCurrentPage.mFlags = 0; vorbis_info_init(&mVi); vorbis_comment_init(&mVc); @@ -414,19 +415,18 @@ status_t MyOggExtractor::findPrevGranulePosition( ALOGV("prevPageOffset at %lld, pageOffset at %lld", (long long)prevPageOffset, (long long)pageOffset); - + uint8_t flag = 0; for (;;) { Page prevPage; ssize_t n = readPage(prevPageOffset, &prevPage); if (n <= 0) { - return (status_t)n; + return (flag & 0x4) ? OK : (status_t)n; } - + flag = prevPage.mFlags; prevPageOffset += n; - + *granulePos = prevPage.mGranulePosition; if (prevPageOffset == pageOffset) { - *granulePos = prevPage.mGranulePosition; return OK; } } @@ -688,7 +688,7 @@ uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferHelper *buffer) const TRESPASS(); } - uint32_t numSamples = frameSizeUs * numFrames * kOpusSampleRate / 1000000; + uint32_t numSamples = (uint32_t)((uint64_t)frameSizeUs * numFrames * kOpusSampleRate) / 1000000; return numSamples; } @@ -868,6 +868,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool cal CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments); mOffset += mCurrentPageSize; + uint8_t flag = mCurrentPage.mFlags; ssize_t n = readPage(mOffset, &mCurrentPage); if (n <= 0) { @@ -878,6 +879,7 @@ media_status_t MyOggExtractor::_readNextPacket(MediaBufferHelper **out, bool cal ALOGV("readPage returned %zd", n); + if (flag & 0x04) return AMEDIA_ERROR_END_OF_STREAM; return (media_status_t) n; } -- GitLab From e3c87346e82aeaeed7f91e6b93351b16362debd5 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Tue, 16 Apr 2019 15:59:29 -0700 Subject: [PATCH 1339/1530] Disable buffer dropping from BufferQueueProducer Disable legacy buffer dropping behavior from app targetted Android Q or above. Bug: 130039639 Change-Id: I492a41495f011309910f6e79ff566228d43ff35b --- media/libstagefright/MediaCodec.cpp | 9 ++++++- media/libstagefright/SurfaceUtils.cpp | 26 +++++++++++++++++++ .../include/media/stagefright/SurfaceUtils.h | 11 ++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index 2ca5eee84d..e56750befe 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1110,6 +1110,9 @@ status_t MediaCodec::configure( reset(); } if (!isResourceError(err)) { + if (err == OK) { + disableLegacyBufferDropPostQ(surface); + } break; } } @@ -1175,7 +1178,11 @@ status_t MediaCodec::setSurface(const sp &surface) { msg->setObject("surface", surface); sp response; - return PostAndAwaitResponse(msg, &response); + status_t result = PostAndAwaitResponse(msg, &response); + if (result == OK) { + disableLegacyBufferDropPostQ(surface); + } + return result; } status_t MediaCodec::createInputSurface( diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp index 9e11a94a51..4c94baabe0 100644 --- a/media/libstagefright/SurfaceUtils.cpp +++ b/media/libstagefright/SurfaceUtils.cpp @@ -18,10 +18,13 @@ #define LOG_TAG "SurfaceUtils" #include +#include #include #include #include +extern "C" int android_get_application_target_sdk_version(); + namespace android { status_t setNativeWindowSizeFormatAndUsage( @@ -291,5 +294,28 @@ status_t nativeWindowDisconnect(ANativeWindow *surface, const char *reason) { return err; } + +status_t disableLegacyBufferDropPostQ(const sp &surface) { + sp igbp = + surface ? surface->getIGraphicBufferProducer() : nullptr; + if (igbp) { + int targetSdk = android_get_application_target_sdk_version(); + // When the caller is not an app (e.g. MediaPlayer in mediaserver) + // targetSdk is __ANDROID_API_FUTURE__. + bool drop = + targetSdk < __ANDROID_API_Q__ || + targetSdk == __ANDROID_API_FUTURE__; + if (!drop) { + status_t err = igbp->setLegacyBufferDrop(false); + if (err == NO_ERROR) { + ALOGD("legacy buffer drop disabled: target sdk (%d)", + targetSdk); + } else { + ALOGD("disabling legacy buffer drop failed: %d", err); + } + } + } + return NO_ERROR; +} } // namespace android diff --git a/media/libstagefright/include/media/stagefright/SurfaceUtils.h b/media/libstagefright/include/media/stagefright/SurfaceUtils.h index 689e4587e4..ae55c651cc 100644 --- a/media/libstagefright/include/media/stagefright/SurfaceUtils.h +++ b/media/libstagefright/include/media/stagefright/SurfaceUtils.h @@ -19,8 +19,10 @@ #define SURFACE_UTILS_H_ #include +#include struct ANativeWindow; +class Surface; namespace android { @@ -40,6 +42,15 @@ status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull * status_t nativeWindowConnect(ANativeWindow *surface, const char *reason); status_t nativeWindowDisconnect(ANativeWindow *surface, const char *reason); +/** + * Disable buffer dropping behavior of BufferQueue if target sdk of application + * is Q or later. If the caller is not an app (e.g. MediaPlayer in mediaserver) + * retain buffer dropping behavior. + * + * @return NO_ERROR + */ +status_t disableLegacyBufferDropPostQ(const sp &surface); + } // namespace android #endif // SURFACE_UTILS_H_ -- GitLab From ab2274707383170bc0f8be8a0ecb6901cfcf7561 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 21 Feb 2019 13:53:42 +0800 Subject: [PATCH 1340/1530] Support mkv whose hevc configurationVersion is not 1 Some mkv files may have hevc configurationVersion as 0 and we need to support them. Bug:125176285 Test: Play a mkv file whose configurationVersion is 0 Change-Id: I946767c8e790b3d62d8feb2c588ae56b2cabedab --- media/libstagefright/Utils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 537e4c0817..3de934f54a 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -1101,7 +1101,9 @@ status_t convertMetaDataToMessage( } else if (meta->findData(kKeyHVCC, &type, &data, &size)) { const uint8_t *ptr = (const uint8_t *)data; - if (size < 23 || ptr[0] != 1) { // configurationVersion == 1 + if (size < 23 || (ptr[0] != 1 && ptr[0] != 0)) { + // configurationVersion == 1 or 0 + // 1 is what the standard dictates, but some old muxers may have used 0. ALOGE("b/23680780"); return BAD_VALUE; } -- GitLab From 5eaa57a5de115b50bfbe1b8d6764116c9f39a10f Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 26 Apr 2019 10:35:50 -0700 Subject: [PATCH 1341/1530] Remove unused AVIExtractor source Bug: 130651570 Test: compilation Change-Id: I36e8221879593e0560002e404215c6efcd531ab8 --- media/libstagefright/AVIExtractor.cpp | 1307 ------------------------- 1 file changed, 1307 deletions(-) delete mode 100644 media/libstagefright/AVIExtractor.cpp diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp deleted file mode 100644 index 5a6211e1a6..0000000000 --- a/media/libstagefright/AVIExtractor.cpp +++ /dev/null @@ -1,1307 +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 "AVIExtractor" -#include - -#include "include/avc_utils.h" -#include "include/AVIExtractor.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -struct AVIExtractor::AVISource : public MediaSource { - AVISource(const sp &extractor, size_t trackIndex); - - virtual status_t start(MetaData *params); - virtual status_t stop(); - - virtual sp getFormat(); - - virtual status_t read( - MediaBuffer **buffer, const ReadOptions *options); - -protected: - virtual ~AVISource(); - -private: - sp mExtractor; - size_t mTrackIndex; - const AVIExtractor::Track &mTrack; - MediaBufferGroup *mBufferGroup; - size_t mSampleIndex; - - sp mSplitter; - - DISALLOW_EVIL_CONSTRUCTORS(AVISource); -}; - -//////////////////////////////////////////////////////////////////////////////// - -struct AVIExtractor::MP3Splitter : public RefBase { - MP3Splitter(); - - void clear(); - void append(MediaBuffer *buffer); - status_t read(MediaBuffer **buffer); - -protected: - virtual ~MP3Splitter(); - -private: - bool mFindSync; - int64_t mBaseTimeUs; - int64_t mNumSamplesRead; - sp mBuffer; - - bool resync(); - - DISALLOW_EVIL_CONSTRUCTORS(MP3Splitter); -}; - -//////////////////////////////////////////////////////////////////////////////// - -AVIExtractor::AVISource::AVISource( - const sp &extractor, size_t trackIndex) - : mExtractor(extractor), - mTrackIndex(trackIndex), - mTrack(mExtractor->mTracks.itemAt(trackIndex)), - mBufferGroup(NULL) { -} - -AVIExtractor::AVISource::~AVISource() { - if (mBufferGroup) { - stop(); - } -} - -status_t AVIExtractor::AVISource::start(MetaData *params) { - CHECK(!mBufferGroup); - - mBufferGroup = new MediaBufferGroup; - - mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize)); - mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize)); - mSampleIndex = 0; - - const char *mime; - CHECK(mTrack.mMeta->findCString(kKeyMIMEType, &mime)); - - if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { - mSplitter = new MP3Splitter; - } else { - mSplitter.clear(); - } - - return OK; -} - -status_t AVIExtractor::AVISource::stop() { - CHECK(mBufferGroup); - - delete mBufferGroup; - mBufferGroup = NULL; - - mSplitter.clear(); - - return OK; -} - -sp AVIExtractor::AVISource::getFormat() { - return mTrack.mMeta; -} - -status_t AVIExtractor::AVISource::read( - MediaBuffer **buffer, const ReadOptions *options) { - CHECK(mBufferGroup); - - *buffer = NULL; - - int64_t seekTimeUs; - ReadOptions::SeekMode seekMode; - if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { - status_t err = - mExtractor->getSampleIndexAtTime( - mTrackIndex, seekTimeUs, seekMode, &mSampleIndex); - - if (err != OK) { - return ERROR_END_OF_STREAM; - } - - if (mSplitter != NULL) { - mSplitter->clear(); - } - } - - for (;;) { - if (mSplitter != NULL) { - status_t err = mSplitter->read(buffer); - - if (err == OK) { - break; - } else if (err != -EAGAIN) { - return err; - } - } - - off64_t offset; - size_t size; - bool isKey; - int64_t timeUs; - status_t err = mExtractor->getSampleInfo( - mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs); - - ++mSampleIndex; - - if (err != OK) { - return ERROR_END_OF_STREAM; - } - - MediaBuffer *out; - CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK); - - ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED; - } - - out->set_range(0, size); - - out->meta_data()->setInt64(kKeyTime, timeUs); - - if (isKey) { - out->meta_data()->setInt32(kKeyIsSyncFrame, 1); - } - - if (mSplitter == NULL) { - *buffer = out; - break; - } - - mSplitter->append(out); - out->release(); - out = NULL; - } - - return OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -AVIExtractor::MP3Splitter::MP3Splitter() - : mFindSync(true), - mBaseTimeUs(-1ll), - mNumSamplesRead(0) { -} - -AVIExtractor::MP3Splitter::~MP3Splitter() { -} - -void AVIExtractor::MP3Splitter::clear() { - mFindSync = true; - mBaseTimeUs = -1ll; - mNumSamplesRead = 0; - - if (mBuffer != NULL) { - mBuffer->setRange(0, 0); - } -} - -void AVIExtractor::MP3Splitter::append(MediaBuffer *buffer) { - size_t prevCapacity = (mBuffer != NULL) ? mBuffer->capacity() : 0; - - if (mBaseTimeUs < 0) { - CHECK(mBuffer == NULL || mBuffer->size() == 0); - CHECK(buffer->meta_data()->findInt64(kKeyTime, &mBaseTimeUs)); - mNumSamplesRead = 0; - } - - if (mBuffer != NULL && mBuffer->offset() > 0) { - memmove(mBuffer->base(), mBuffer->data(), mBuffer->size()); - mBuffer->setRange(0, mBuffer->size()); - } - - if (mBuffer == NULL - || mBuffer->size() + buffer->range_length() > prevCapacity) { - size_t newCapacity = - (prevCapacity + buffer->range_length() + 1023) & ~1023; - - sp newBuffer = new ABuffer(newCapacity); - if (mBuffer != NULL) { - memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size()); - newBuffer->setRange(0, mBuffer->size()); - } else { - newBuffer->setRange(0, 0); - } - mBuffer = newBuffer; - } - - memcpy(mBuffer->data() + mBuffer->size(), - (const uint8_t *)buffer->data() + buffer->range_offset(), - buffer->range_length()); - - mBuffer->setRange(0, mBuffer->size() + buffer->range_length()); -} - -bool AVIExtractor::MP3Splitter::resync() { - if (mBuffer == NULL) { - return false; - } - - bool foundSync = false; - for (size_t offset = 0; offset + 3 < mBuffer->size(); ++offset) { - uint32_t firstHeader = U32_AT(mBuffer->data() + offset); - - size_t frameSize; - if (!GetMPEGAudioFrameSize(firstHeader, &frameSize)) { - continue; - } - - size_t subsequentOffset = offset + frameSize; - size_t i = 3; - while (i > 0) { - if (subsequentOffset + 3 >= mBuffer->size()) { - break; - } - - static const uint32_t kMask = 0xfffe0c00; - - uint32_t header = U32_AT(mBuffer->data() + subsequentOffset); - if ((header & kMask) != (firstHeader & kMask)) { - break; - } - - if (!GetMPEGAudioFrameSize(header, &frameSize)) { - break; - } - - subsequentOffset += frameSize; - --i; - } - - if (i == 0) { - foundSync = true; - memmove(mBuffer->data(), - mBuffer->data() + offset, - mBuffer->size() - offset); - - mBuffer->setRange(0, mBuffer->size() - offset); - break; - } - } - - return foundSync; -} - -status_t AVIExtractor::MP3Splitter::read(MediaBuffer **out) { - *out = NULL; - - if (mFindSync) { - if (!resync()) { - return -EAGAIN; - } - - mFindSync = false; - } - - if (mBuffer->size() < 4) { - return -EAGAIN; - } - - uint32_t header = U32_AT(mBuffer->data()); - size_t frameSize; - int sampleRate; - int numSamples; - if (!GetMPEGAudioFrameSize( - header, &frameSize, &sampleRate, NULL, NULL, &numSamples)) { - return ERROR_MALFORMED; - } - - if (mBuffer->size() < frameSize) { - return -EAGAIN; - } - - MediaBuffer *mbuf = new MediaBuffer(frameSize); - memcpy(mbuf->data(), mBuffer->data(), frameSize); - - int64_t timeUs = mBaseTimeUs + (mNumSamplesRead * 1000000ll) / sampleRate; - mNumSamplesRead += numSamples; - - mbuf->meta_data()->setInt64(kKeyTime, timeUs); - - mBuffer->setRange( - mBuffer->offset() + frameSize, mBuffer->size() - frameSize); - - *out = mbuf; - - return OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -AVIExtractor::AVIExtractor(const sp &dataSource) - : mDataSource(dataSource) { - mInitCheck = parseHeaders(); - - if (mInitCheck != OK) { - mTracks.clear(); - } -} - -AVIExtractor::~AVIExtractor() { -} - -size_t AVIExtractor::countTracks() { - return mTracks.size(); -} - -sp AVIExtractor::getTrack(size_t index) { - return index < mTracks.size() ? new AVISource(this, index) : NULL; -} - -sp AVIExtractor::getTrackMetaData( - size_t index, uint32_t flags) { - return index < mTracks.size() ? mTracks.editItemAt(index).mMeta : NULL; -} - -sp AVIExtractor::getMetaData() { - sp meta = new MetaData; - - if (mInitCheck == OK) { - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_AVI); - } - - return meta; -} - -status_t AVIExtractor::parseHeaders() { - mTracks.clear(); - mMovieOffset = 0; - mFoundIndex = false; - mOffsetsAreAbsolute = false; - - ssize_t res = parseChunk(0ll, -1ll); - - if (res < 0) { - return (status_t)res; - } - - if (mMovieOffset == 0ll || !mFoundIndex) { - return ERROR_MALFORMED; - } - - return OK; -} - -ssize_t AVIExtractor::parseChunk(off64_t offset, off64_t size, int depth) { - if (size >= 0 && size < 8) { - return ERROR_MALFORMED; - } - - uint8_t tmp[12]; - ssize_t n = mDataSource->readAt(offset, tmp, 8); - - if (n < 8) { - return (n < 0) ? n : (ssize_t)ERROR_MALFORMED; - } - - uint32_t fourcc = U32_AT(tmp); - uint32_t chunkSize = U32LE_AT(&tmp[4]); - - if (size >= 0 && chunkSize + 8 > size) { - return ERROR_MALFORMED; - } - - static const char kPrefix[] = " "; - const char *prefix = &kPrefix[strlen(kPrefix) - 2 * depth]; - - if (fourcc == FOURCC('L', 'I', 'S', 'T') - || fourcc == FOURCC('R', 'I', 'F', 'F')) { - // It's a list of chunks - - if (size >= 0 && size < 12) { - return ERROR_MALFORMED; - } - - n = mDataSource->readAt(offset + 8, &tmp[8], 4); - - if (n < 4) { - return (n < 0) ? n : (ssize_t)ERROR_MALFORMED; - } - - uint32_t subFourcc = U32_AT(&tmp[8]); - - ALOGV("%s offset 0x%08llx LIST of '%c%c%c%c', size %d", - prefix, - offset, - (char)(subFourcc >> 24), - (char)((subFourcc >> 16) & 0xff), - (char)((subFourcc >> 8) & 0xff), - (char)(subFourcc & 0xff), - chunkSize - 4); - - if (subFourcc == FOURCC('m', 'o', 'v', 'i')) { - // We're not going to parse this, but will take note of the - // offset. - - mMovieOffset = offset; - } else { - off64_t subOffset = offset + 12; - off64_t subOffsetLimit = subOffset + chunkSize - 4; - while (subOffset < subOffsetLimit) { - ssize_t res = - parseChunk(subOffset, subOffsetLimit - subOffset, depth + 1); - - if (res < 0) { - return res; - } - - subOffset += res; - } - } - } else { - ALOGV("%s offset 0x%08llx CHUNK '%c%c%c%c'", - prefix, - offset, - (char)(fourcc >> 24), - (char)((fourcc >> 16) & 0xff), - (char)((fourcc >> 8) & 0xff), - (char)(fourcc & 0xff)); - - status_t err = OK; - - switch (fourcc) { - case FOURCC('s', 't', 'r', 'h'): - { - err = parseStreamHeader(offset + 8, chunkSize); - break; - } - - case FOURCC('s', 't', 'r', 'f'): - { - err = parseStreamFormat(offset + 8, chunkSize); - break; - } - - case FOURCC('i', 'd', 'x', '1'): - { - err = parseIndex(offset + 8, chunkSize); - break; - } - - default: - break; - } - - if (err != OK) { - return err; - } - } - - if (chunkSize & 1) { - ++chunkSize; - } - - return chunkSize + 8; -} - -static const char *GetMIMETypeForHandler(uint32_t handler) { - switch (handler) { - // Wow... shamelessly copied from - // http://wiki.multimedia.cx/index.php?title=ISO_MPEG-4 - - case FOURCC('3', 'I', 'V', '2'): - case FOURCC('3', 'i', 'v', '2'): - case FOURCC('B', 'L', 'Z', '0'): - case FOURCC('D', 'I', 'G', 'I'): - case FOURCC('D', 'I', 'V', '1'): - case FOURCC('d', 'i', 'v', '1'): - case FOURCC('D', 'I', 'V', 'X'): - case FOURCC('d', 'i', 'v', 'x'): - case FOURCC('D', 'X', '5', '0'): - case FOURCC('d', 'x', '5', '0'): - case FOURCC('D', 'X', 'G', 'M'): - case FOURCC('E', 'M', '4', 'A'): - case FOURCC('E', 'P', 'H', 'V'): - case FOURCC('F', 'M', 'P', '4'): - case FOURCC('f', 'm', 'p', '4'): - case FOURCC('F', 'V', 'F', 'W'): - case FOURCC('H', 'D', 'X', '4'): - case FOURCC('h', 'd', 'x', '4'): - case FOURCC('M', '4', 'C', 'C'): - case FOURCC('M', '4', 'S', '2'): - case FOURCC('m', '4', 's', '2'): - case FOURCC('M', 'P', '4', 'S'): - case FOURCC('m', 'p', '4', 's'): - case FOURCC('M', 'P', '4', 'V'): - case FOURCC('m', 'p', '4', 'v'): - case FOURCC('M', 'V', 'X', 'M'): - case FOURCC('R', 'M', 'P', '4'): - case FOURCC('S', 'E', 'D', 'G'): - case FOURCC('S', 'M', 'P', '4'): - case FOURCC('U', 'M', 'P', '4'): - case FOURCC('W', 'V', '1', 'F'): - case FOURCC('X', 'V', 'I', 'D'): - case FOURCC('X', 'v', 'i', 'D'): - case FOURCC('x', 'v', 'i', 'd'): - case FOURCC('X', 'V', 'I', 'X'): - return MEDIA_MIMETYPE_VIDEO_MPEG4; - - // from http://wiki.multimedia.cx/index.php?title=H264 - case FOURCC('a', 'v', 'c', '1'): - case FOURCC('d', 'a', 'v', 'c'): - case FOURCC('x', '2', '6', '4'): - case FOURCC('H', '2', '6', '4'): - case FOURCC('v', 's', 's', 'h'): - return MEDIA_MIMETYPE_VIDEO_AVC; - - default: - return NULL; - } -} - -status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) { - if (size != 56) { - return ERROR_MALFORMED; - } - - if (mTracks.size() > 99) { - return -ERANGE; - } - - sp buffer = new ABuffer(size); - ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - uint32_t type = U32_AT(data); - uint32_t handler = U32_AT(&data[4]); - uint32_t flags = U32LE_AT(&data[8]); - - sp meta = new MetaData; - - uint32_t rate = U32LE_AT(&data[20]); - uint32_t scale = U32LE_AT(&data[24]); - - uint32_t sampleSize = U32LE_AT(&data[44]); - - const char *mime = NULL; - Track::Kind kind = Track::OTHER; - - if (type == FOURCC('v', 'i', 'd', 's')) { - mime = GetMIMETypeForHandler(handler); - - if (mime && strncasecmp(mime, "video/", 6)) { - return ERROR_MALFORMED; - } - - if (mime == NULL) { - ALOGW("Unsupported video format '%c%c%c%c'", - (char)(handler >> 24), - (char)((handler >> 16) & 0xff), - (char)((handler >> 8) & 0xff), - (char)(handler & 0xff)); - } - - kind = Track::VIDEO; - } else if (type == FOURCC('a', 'u', 'd', 's')) { - if (mime && strncasecmp(mime, "audio/", 6)) { - return ERROR_MALFORMED; - } - - kind = Track::AUDIO; - } - - if (!mime) { - mime = "application/octet-stream"; - } - - meta->setCString(kKeyMIMEType, mime); - - mTracks.push(); - Track *track = &mTracks.editItemAt(mTracks.size() - 1); - - track->mMeta = meta; - track->mRate = rate; - track->mScale = scale; - track->mBytesPerSample = sampleSize; - track->mKind = kind; - track->mNumSyncSamples = 0; - track->mThumbnailSampleSize = 0; - track->mThumbnailSampleIndex = -1; - track->mMaxSampleSize = 0; - track->mAvgChunkSize = 1.0; - track->mFirstChunkSize = 0; - - return OK; -} - -status_t AVIExtractor::parseStreamFormat(off64_t offset, size_t size) { - if (mTracks.isEmpty()) { - return ERROR_MALFORMED; - } - - Track *track = &mTracks.editItemAt(mTracks.size() - 1); - - if (track->mKind == Track::OTHER) { - // We don't support this content, but that's not a parsing error. - return OK; - } - - bool isVideo = (track->mKind == Track::VIDEO); - - if ((isVideo && size < 40) || (!isVideo && size < 16)) { - // Expected a BITMAPINFO or WAVEFORMAT(EX) structure, respectively. - return ERROR_MALFORMED; - } - - sp buffer = new ABuffer(size); - ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - if (isVideo) { - uint32_t width = U32LE_AT(&data[4]); - uint32_t height = U32LE_AT(&data[8]); - - track->mMeta->setInt32(kKeyWidth, width); - track->mMeta->setInt32(kKeyHeight, height); - } else { - uint32_t format = U16LE_AT(data); - - if (format == 0x55) { - track->mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); - } else { - ALOGW("Unsupported audio format = 0x%04x", format); - } - - uint32_t numChannels = U16LE_AT(&data[2]); - uint32_t sampleRate = U32LE_AT(&data[4]); - - track->mMeta->setInt32(kKeyChannelCount, numChannels); - track->mMeta->setInt32(kKeySampleRate, sampleRate); - } - - return OK; -} - -// static -bool AVIExtractor::IsCorrectChunkType( - ssize_t trackIndex, Track::Kind kind, uint32_t chunkType) { - uint32_t chunkBase = chunkType & 0xffff; - - switch (kind) { - case Track::VIDEO: - { - if (chunkBase != FOURCC(0, 0, 'd', 'c') - && chunkBase != FOURCC(0, 0, 'd', 'b')) { - return false; - } - break; - } - - case Track::AUDIO: - { - if (chunkBase != FOURCC(0, 0, 'w', 'b')) { - return false; - } - break; - } - - default: - break; - } - - if (trackIndex < 0) { - return true; - } - - uint8_t hi = chunkType >> 24; - uint8_t lo = (chunkType >> 16) & 0xff; - - if (hi < '0' || hi > '9' || lo < '0' || lo > '9') { - return false; - } - - if (trackIndex != (10 * (hi - '0') + (lo - '0'))) { - return false; - } - - return true; -} - -status_t AVIExtractor::parseIndex(off64_t offset, size_t size) { - if ((size % 16) != 0) { - return ERROR_MALFORMED; - } - - sp buffer = new ABuffer(size); - ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : ERROR_MALFORMED; - } - - const uint8_t *data = buffer->data(); - - while (size > 0) { - uint32_t chunkType = U32_AT(data); - - uint8_t hi = chunkType >> 24; - uint8_t lo = (chunkType >> 16) & 0xff; - - if (hi < '0' || hi > '9' || lo < '0' || lo > '9') { - return ERROR_MALFORMED; - } - - size_t trackIndex = 10 * (hi - '0') + (lo - '0'); - - if (trackIndex >= mTracks.size()) { - return ERROR_MALFORMED; - } - - Track *track = &mTracks.editItemAt(trackIndex); - - if (!IsCorrectChunkType(-1, track->mKind, chunkType)) { - return ERROR_MALFORMED; - } - - if (track->mKind == Track::OTHER) { - data += 16; - size -= 16; - continue; - } - - uint32_t flags = U32LE_AT(&data[4]); - uint32_t offset = U32LE_AT(&data[8]); - uint32_t chunkSize = U32LE_AT(&data[12]); - - if (chunkSize > track->mMaxSampleSize) { - track->mMaxSampleSize = chunkSize; - } - - track->mSamples.push(); - - SampleInfo *info = - &track->mSamples.editItemAt(track->mSamples.size() - 1); - - info->mOffset = offset; - info->mIsKey = (flags & 0x10) != 0; - - if (info->mIsKey) { - static const size_t kMaxNumSyncSamplesToScan = 20; - - if (track->mNumSyncSamples < kMaxNumSyncSamplesToScan) { - if (chunkSize > track->mThumbnailSampleSize) { - track->mThumbnailSampleSize = chunkSize; - - track->mThumbnailSampleIndex = - track->mSamples.size() - 1; - } - } - - ++track->mNumSyncSamples; - } - - data += 16; - size -= 16; - } - - if (!mTracks.isEmpty()) { - off64_t offset; - size_t size; - bool isKey; - int64_t timeUs; - status_t err = getSampleInfo(0, 0, &offset, &size, &isKey, &timeUs); - - if (err != OK) { - mOffsetsAreAbsolute = !mOffsetsAreAbsolute; - err = getSampleInfo(0, 0, &offset, &size, &isKey, &timeUs); - - if (err != OK) { - return err; - } - } - - ALOGV("Chunk offsets are %s", - mOffsetsAreAbsolute ? "absolute" : "movie-chunk relative"); - } - - for (size_t i = 0; i < mTracks.size(); ++i) { - Track *track = &mTracks.editItemAt(i); - - if (track->mBytesPerSample > 0) { - // Assume all chunks are roughly the same size for now. - - // Compute the avg. size of the first 128 chunks (if there are - // that many), but exclude the size of the first one, since - // it may be an outlier. - size_t numSamplesToAverage = track->mSamples.size(); - if (numSamplesToAverage > 256) { - numSamplesToAverage = 256; - } - - double avgChunkSize = 0; - size_t j; - for (j = 0; j <= numSamplesToAverage; ++j) { - off64_t offset; - size_t size; - bool isKey; - int64_t dummy; - - status_t err = - getSampleInfo( - i, j, - &offset, &size, &isKey, &dummy); - - if (err != OK) { - return err; - } - - if (j == 0) { - track->mFirstChunkSize = size; - continue; - } - - avgChunkSize += size; - } - - avgChunkSize /= numSamplesToAverage; - - track->mAvgChunkSize = avgChunkSize; - } - - int64_t durationUs; - CHECK_EQ((status_t)OK, - getSampleTime(i, track->mSamples.size() - 1, &durationUs)); - - ALOGV("track %d duration = %.2f secs", i, durationUs / 1E6); - - track->mMeta->setInt64(kKeyDuration, durationUs); - track->mMeta->setInt32(kKeyMaxInputSize, track->mMaxSampleSize); - - const char *tmp; - CHECK(track->mMeta->findCString(kKeyMIMEType, &tmp)); - - AString mime = tmp; - - if (!strncasecmp("video/", mime.c_str(), 6)) { - if (track->mThumbnailSampleIndex >= 0) { - int64_t thumbnailTimeUs; - CHECK_EQ((status_t)OK, - getSampleTime(i, track->mThumbnailSampleIndex, - &thumbnailTimeUs)); - - track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); - } - - status_t err = OK; - - if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) { - err = addMPEG4CodecSpecificData(i); - } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) { - err = addH264CodecSpecificData(i); - } - - if (err != OK) { - return err; - } - } - } - - mFoundIndex = true; - - return OK; -} - -static size_t GetSizeWidth(size_t x) { - size_t n = 1; - while (x > 127) { - ++n; - x >>= 7; - } - return n; -} - -static uint8_t *EncodeSize(uint8_t *dst, size_t x) { - while (x > 127) { - *dst++ = (x & 0x7f) | 0x80; - x >>= 7; - } - *dst++ = x; - return dst; -} - -sp MakeMPEG4VideoCodecSpecificData(const sp &config) { - size_t len1 = config->size() + GetSizeWidth(config->size()) + 1; - size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13; - size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3; - - sp csd = new ABuffer(len3); - uint8_t *dst = csd->data(); - *dst++ = 0x03; - dst = EncodeSize(dst, len2 + 3); - *dst++ = 0x00; // ES_ID - *dst++ = 0x00; - *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag - - *dst++ = 0x04; - dst = EncodeSize(dst, len1 + 13); - *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile - for (size_t i = 0; i < 12; ++i) { - *dst++ = 0x00; - } - - *dst++ = 0x05; - dst = EncodeSize(dst, config->size()); - memcpy(dst, config->data(), config->size()); - dst += config->size(); - - // hexdump(csd->data(), csd->size()); - - return csd; -} - -status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) { - Track *track = &mTracks.editItemAt(trackIndex); - - off64_t offset; - size_t size; - bool isKey; - int64_t timeUs; - status_t err = - getSampleInfo(trackIndex, 0, &offset, &size, &isKey, &timeUs); - - if (err != OK) { - return err; - } - - sp buffer = new ABuffer(size); - ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : ERROR_MALFORMED; - } - - // Extract everything up to the first VOP start code from the first - // frame's encoded data and use it to construct an ESDS with the - // codec specific data. - - size_t i = 0; - bool found = false; - while (i + 3 < buffer->size()) { - if (!memcmp("\x00\x00\x01\xb6", &buffer->data()[i], 4)) { - found = true; - break; - } - - ++i; - } - - if (!found) { - return ERROR_MALFORMED; - } - - buffer->setRange(0, i); - - sp csd = MakeMPEG4VideoCodecSpecificData(buffer); - track->mMeta->setData(kKeyESDS, kTypeESDS, csd->data(), csd->size()); - - return OK; -} - -status_t AVIExtractor::addH264CodecSpecificData(size_t trackIndex) { - Track *track = &mTracks.editItemAt(trackIndex); - - off64_t offset; - size_t size; - bool isKey; - int64_t timeUs; - - // Extract codec specific data from the first non-empty sample. - - size_t sampleIndex = 0; - for (;;) { - status_t err = - getSampleInfo( - trackIndex, sampleIndex, &offset, &size, &isKey, &timeUs); - - if (err != OK) { - return err; - } - - if (size > 0) { - break; - } - - ++sampleIndex; - } - - sp buffer = new ABuffer(size); - ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size()); - - if (n < (ssize_t)size) { - return n < 0 ? (status_t)n : ERROR_MALFORMED; - } - - sp meta = MakeAVCCodecSpecificData(buffer); - - if (meta == NULL) { - ALOGE("Unable to extract AVC codec specific data"); - return ERROR_MALFORMED; - } - - int32_t width, height; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); - - uint32_t type; - const void *csd; - size_t csdSize; - CHECK(meta->findData(kKeyAVCC, &type, &csd, &csdSize)); - - track->mMeta->setInt32(kKeyWidth, width); - track->mMeta->setInt32(kKeyHeight, height); - track->mMeta->setData(kKeyAVCC, type, csd, csdSize); - - return OK; -} - -status_t AVIExtractor::getSampleInfo( - size_t trackIndex, size_t sampleIndex, - off64_t *offset, size_t *size, bool *isKey, - int64_t *sampleTimeUs) { - if (trackIndex >= mTracks.size()) { - return -ERANGE; - } - - const Track &track = mTracks.itemAt(trackIndex); - - if (sampleIndex >= track.mSamples.size()) { - return -ERANGE; - } - - const SampleInfo &info = track.mSamples.itemAt(sampleIndex); - - if (!mOffsetsAreAbsolute) { - *offset = info.mOffset + mMovieOffset + 8; - } else { - *offset = info.mOffset; - } - - *size = 0; - - uint8_t tmp[8]; - ssize_t n = mDataSource->readAt(*offset, tmp, 8); - - if (n < 8) { - return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED; - } - - uint32_t chunkType = U32_AT(tmp); - - if (!IsCorrectChunkType(trackIndex, track.mKind, chunkType)) { - return ERROR_MALFORMED; - } - - *offset += 8; - *size = U32LE_AT(&tmp[4]); - - *isKey = info.mIsKey; - - if (track.mBytesPerSample > 0) { - size_t sampleStartInBytes; - if (sampleIndex == 0) { - sampleStartInBytes = 0; - } else { - sampleStartInBytes = - track.mFirstChunkSize + track.mAvgChunkSize * (sampleIndex - 1); - } - - sampleIndex = sampleStartInBytes / track.mBytesPerSample; - } - - *sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale; - - return OK; -} - -status_t AVIExtractor::getSampleTime( - size_t trackIndex, size_t sampleIndex, int64_t *sampleTimeUs) { - off64_t offset; - size_t size; - bool isKey; - return getSampleInfo( - trackIndex, sampleIndex, &offset, &size, &isKey, sampleTimeUs); -} - -status_t AVIExtractor::getSampleIndexAtTime( - size_t trackIndex, - int64_t timeUs, MediaSource::ReadOptions::SeekMode mode, - size_t *sampleIndex) const { - if (trackIndex >= mTracks.size()) { - return -ERANGE; - } - - const Track &track = mTracks.itemAt(trackIndex); - - ssize_t closestSampleIndex; - - if (track.mBytesPerSample > 0) { - size_t closestByteOffset = - (timeUs * track.mBytesPerSample) - / track.mRate * track.mScale / 1000000ll; - - if (closestByteOffset <= track.mFirstChunkSize) { - closestSampleIndex = 0; - } else { - closestSampleIndex = - (closestByteOffset - track.mFirstChunkSize) - / track.mAvgChunkSize; - } - } else { - // Each chunk contains a single sample. - closestSampleIndex = timeUs / track.mRate * track.mScale / 1000000ll; - } - - ssize_t numSamples = track.mSamples.size(); - - if (closestSampleIndex < 0) { - closestSampleIndex = 0; - } else if (closestSampleIndex >= numSamples) { - closestSampleIndex = numSamples - 1; - } - - if (mode == MediaSource::ReadOptions::SEEK_CLOSEST) { - *sampleIndex = closestSampleIndex; - - return OK; - } - - ssize_t prevSyncSampleIndex = closestSampleIndex; - while (prevSyncSampleIndex >= 0) { - const SampleInfo &info = - track.mSamples.itemAt(prevSyncSampleIndex); - - if (info.mIsKey) { - break; - } - - --prevSyncSampleIndex; - } - - ssize_t nextSyncSampleIndex = closestSampleIndex; - while (nextSyncSampleIndex < numSamples) { - const SampleInfo &info = - track.mSamples.itemAt(nextSyncSampleIndex); - - if (info.mIsKey) { - break; - } - - ++nextSyncSampleIndex; - } - - switch (mode) { - case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC: - { - *sampleIndex = prevSyncSampleIndex; - - return prevSyncSampleIndex >= 0 ? OK : UNKNOWN_ERROR; - } - - case MediaSource::ReadOptions::SEEK_NEXT_SYNC: - { - *sampleIndex = nextSyncSampleIndex; - - return nextSyncSampleIndex < numSamples ? OK : UNKNOWN_ERROR; - } - - case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC: - { - if (prevSyncSampleIndex < 0 && nextSyncSampleIndex >= numSamples) { - return UNKNOWN_ERROR; - } - - if (prevSyncSampleIndex < 0) { - *sampleIndex = nextSyncSampleIndex; - return OK; - } - - if (nextSyncSampleIndex >= numSamples) { - *sampleIndex = prevSyncSampleIndex; - return OK; - } - - size_t dist1 = closestSampleIndex - prevSyncSampleIndex; - size_t dist2 = nextSyncSampleIndex - closestSampleIndex; - - *sampleIndex = - (dist1 < dist2) ? prevSyncSampleIndex : nextSyncSampleIndex; - - return OK; - } - - default: - TRESPASS(); - break; - } -} - -bool SniffAVI( - const sp &source, String8 *mimeType, float *confidence, - sp *) { - char tmp[12]; - if (source->readAt(0, tmp, 12) < 12) { - return false; - } - - if (!memcmp(tmp, "RIFF", 4) && !memcmp(&tmp[8], "AVI ", 4)) { - mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_AVI); - - // Just a tad over the mp3 extractor's confidence, since - // these .avi files may contain .mp3 content that otherwise would - // mistakenly lead to us identifying the entire file as a .mp3 file. - *confidence = 0.21; - - return true; - } - - return false; -} - -} // namespace android -- GitLab From c61bdcf5ddcef693ff30a8b17d58d6a9b16ce5b1 Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Tue, 23 Apr 2019 12:15:56 -0700 Subject: [PATCH 1342/1530] ACodec: Update max temporal layer count Update max temporal layer count when client uses ts-schema for temporal settings. The max count is set same as the layer count when codec hasn't started. After codec starts, ts-schema settings are used to dynamically change the actual layer count CRs-Fixed: 2151883 Author: Saurabh Kothawade Bug: 111198040 Change-Id: I3eeeb077064b03bbcebf5643e873729ff092729c --- media/libstagefright/ACodec.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 3b87462f04..317b5ec51d 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -2580,6 +2580,8 @@ status_t ACodec::configureTemporalLayers( layerParams.nPLayerCountActual = numLayers - numBLayers; layerParams.nBLayerCountActual = numBLayers; layerParams.bBitrateRatiosSpecified = OMX_FALSE; + layerParams.nLayerCountMax = numLayers; + layerParams.nBLayerCountMax = numBLayers; err = mOMXNode->setParameter( (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering, -- GitLab From 212ef6cbe0fee15e98c114dac38ebabdf578acb5 Mon Sep 17 00:00:00 2001 From: Umang Saini Date: Wed, 17 Apr 2019 20:25:07 +0530 Subject: [PATCH 1343/1530] Add Opus support in MP4 extractor Test: stagefright -ao /sdcard/opus.mp4 Bug: 129712302 Change-Id: I8f7d943e7fa9a7b520974044b458005fab769920 --- media/extractors/mp4/MPEG4Extractor.cpp | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 5a31c585fc..7d4f586ca5 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -338,6 +338,8 @@ static const char *FourCC2MIME(uint32_t fourcc) { return MEDIA_MIMETYPE_VIDEO_HEVC; case FOURCC("ac-4"): return MEDIA_MIMETYPE_AUDIO_AC4; + case FOURCC("Opus"): + return MEDIA_MIMETYPE_AUDIO_OPUS; case FOURCC("twos"): case FOURCC("sowt"): @@ -1640,6 +1642,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("enca"): case FOURCC("samr"): case FOURCC("sawb"): + case FOURCC("Opus"): case FOURCC("twos"): case FOURCC("sowt"): case FOURCC("alac"): @@ -1729,6 +1732,47 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels); AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate); + if (chunk_type == FOURCC("Opus")) { + uint8_t opusInfo[19]; + data_offset += sizeof(buffer); + // Read Opus Header + if (mDataSource->readAt( + data_offset, opusInfo, sizeof(opusInfo)) < (ssize_t)sizeof(opusInfo)) { + return ERROR_IO; + } + + // OpusHeader must start with this magic sequence + // http://wiki.xiph.org/OggOpus#ID_Header + strncpy((char *)opusInfo, "OpusHead", 8); + + // Read Opus Specific Box values + size_t opusOffset = 10; + uint16_t pre_skip = U16_AT(&opusInfo[opusOffset]); + uint32_t sample_rate = U32_AT(&opusInfo[opusOffset + 2]); + uint16_t out_gain = U16_AT(&opusInfo[opusOffset + 6]); + + // Convert Opus Specific Box values. ParseOpusHeader expects + // the values in LE, however MP4 stores these values as BE + // https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2 + memcpy(&opusInfo[opusOffset], &pre_skip, sizeof(pre_skip)); + memcpy(&opusInfo[opusOffset + 2], &sample_rate, sizeof(sample_rate)); + memcpy(&opusInfo[opusOffset + 6], &out_gain, sizeof(out_gain)); + + int64_t codecDelay = 6500000; + int64_t seekPreRollNs = 80000000; // Fixed 80 msec + + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_0, opusInfo, sizeof(opusInfo)); + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_1, &codecDelay, sizeof(codecDelay)); + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_2, &seekPreRollNs, sizeof(seekPreRollNs)); + + data_offset += sizeof(opusInfo); + *offset = data_offset; + CHECK_EQ(*offset, stop_offset); + } + if (chunk_type == FOURCC("alac")) { // See 'external/alac/ALACMagicCookieDescription.txt for the detail'. -- GitLab From f77314d41d13460cbcb4db9001507b8e69f755c4 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Wed, 17 Apr 2019 17:26:41 +0530 Subject: [PATCH 1344/1530] Add flac support in MP4 extractor Test: stagefright -ao flac.mp4 Bug: 129712302 Change-Id: I05aad7853a937ff1c13b24058f3406c3d143e9d7 --- media/extractors/mp4/MPEG4Extractor.cpp | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 7d4f586ca5..e1bfb622e2 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -346,7 +346,8 @@ static const char *FourCC2MIME(uint32_t fourcc) { return MEDIA_MIMETYPE_AUDIO_RAW; case FOURCC("alac"): return MEDIA_MIMETYPE_AUDIO_ALAC; - + case FOURCC("fLaC"): + return MEDIA_MIMETYPE_AUDIO_FLAC; case FOURCC("av01"): return MEDIA_MIMETYPE_VIDEO_AV1; case FOURCC(".mp3"): @@ -1646,6 +1647,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC("twos"): case FOURCC("sowt"): case FOURCC("alac"): + case FOURCC("fLaC"): case FOURCC(".mp3"): case 0x6D730055: // "ms U" mp3 audio { @@ -1810,6 +1812,29 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { CHECK_EQ(*offset, stop_offset); } + if (chunk_type == FOURCC("fLaC")) { + + // From https://github.com/xiph/flac/blob/master/doc/isoflac.txt + // 4 for mime, 4 for blockType and BlockLen, 34 for metadata + uint8_t flacInfo[4 + 4 + 34]; + // skipping dFla, version + data_offset += sizeof(buffer) + 12; + size_t flacOffset = 4; + // Add flaC header mime type to CSD + strncpy((char *)flacInfo, "fLaC", 4); + if (mDataSource->readAt( + data_offset, flacInfo + flacOffset, sizeof(flacInfo) - flacOffset) < + (ssize_t)sizeof(flacInfo) - flacOffset) { + return ERROR_IO; + } + data_offset += sizeof(flacInfo) - flacOffset; + + AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, flacInfo, + sizeof(flacInfo)); + *offset = data_offset; + CHECK_EQ(*offset, stop_offset); + } + while (*offset < stop_offset) { status_t err = parseChunk(offset, depth + 1); if (err != OK) { -- GitLab From f93a454266a5e2d8301de2ecbbac08b4bae01951 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 26 Mar 2019 11:01:00 +0530 Subject: [PATCH 1345/1530] Update to new AV1 profile definitions and support HDR info Test: cts-tradefed run commandAndExit cts-dev -m CtsMediaTestCases \ -t android.media.cts.DecoderTest#testAV1HdrStaticMetadata Bug: 123756486 Change-Id: I673f91e7b6bd3c0f31fd67f035b5ece7f6a71844 --- media/codec2/components/aom/C2SoftAomDec.cpp | 59 ++++++++++++++++++- media/codec2/sfplugin/utils/Codec2Mapper.cpp | 30 ++++++++-- .../media/stagefright/MediaCodecConstants.h | 16 ++--- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp index 4bcc2c669e..df4dadbba0 100644 --- a/media/codec2/components/aom/C2SoftAomDec.cpp +++ b/media/codec2/components/aom/C2SoftAomDec.cpp @@ -78,6 +78,26 @@ class C2SoftAomDec::IntfImpl : public SimpleInterface::BaseParams { .withSetter(ProfileLevelSetter, mSize) .build()); + mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0); + addParameter( + DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO) + .withDefault(mHdr10PlusInfoInput) + .withFields({ + C2F(mHdr10PlusInfoInput, m.value).any(), + }) + .withSetter(Hdr10PlusInfoInputSetter) + .build()); + + mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0); + addParameter( + DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO) + .withDefault(mHdr10PlusInfoOutput) + .withFields({ + C2F(mHdr10PlusInfoOutput, m.value).any(), + }) + .withSetter(Hdr10PlusInfoOutputSetter) + .build()); + addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE) .withDefault(new C2StreamMaxPictureSizeTuning::output( 0u, 320, 240)) @@ -203,6 +223,18 @@ class C2SoftAomDec::IntfImpl : public SimpleInterface::BaseParams { return mDefaultColorAspects; } + static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + (void)me; // TODO: validate + return C2R::Ok(); + } + + static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + (void)me; // TODO: validate + return C2R::Ok(); + } + private: std::shared_ptr mProfileLevel; std::shared_ptr mSize; @@ -211,6 +243,8 @@ class C2SoftAomDec::IntfImpl : public SimpleInterface::BaseParams { std::shared_ptr mColorInfo; std::shared_ptr mPixelFormat; std::shared_ptr mDefaultColorAspects; + std::shared_ptr mHdr10PlusInfoInput; + std::shared_ptr mHdr10PlusInfoOutput; }; C2SoftAomDec::C2SoftAomDec(const char* name, c2_node_id_t id, @@ -341,7 +375,8 @@ void C2SoftAomDec::finishWork(uint64_t index, const std::shared_ptr& block) { std::shared_ptr buffer = createGraphicBuffer(block, C2Rect(mWidth, mHeight)); - auto fillWork = [buffer, index](const std::unique_ptr& work) { + auto fillWork = [buffer, index, intf = this->mIntf]( + const std::unique_ptr& work) { uint32_t flags = 0; if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) && (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) { @@ -353,6 +388,28 @@ void C2SoftAomDec::finishWork(uint64_t index, work->worklets.front()->output.buffers.push_back(buffer); work->worklets.front()->output.ordinal = work->input.ordinal; work->workletsProcessed = 1u; + + for (const std::unique_ptr ¶m: work->input.configUpdate) { + if (param) { + C2StreamHdr10PlusInfo::input *hdr10PlusInfo = + C2StreamHdr10PlusInfo::input::From(param.get()); + + if (hdr10PlusInfo != nullptr) { + std::vector> failures; + std::unique_ptr outParam = C2Param::CopyAsStream( + *param.get(), true /*output*/, param->stream()); + c2_status_t err = intf->config( + { outParam.get() }, C2_MAY_BLOCK, &failures); + if (err == C2_OK) { + work->worklets.front()->output.configUpdate.push_back( + C2Param::Copy(*outParam.get())); + } else { + ALOGE("finishWork: Config update size failed"); + } + break; + } + } + } }; if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { fillWork(work); diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp index d62944a843..40160c7f74 100644 --- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp +++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp @@ -379,11 +379,19 @@ ALookup sAv1Levels = { ALookup sAv1Profiles = { - { C2Config::PROFILE_AV1_0, AV1Profile0 }, - { C2Config::PROFILE_AV1_1, AV1Profile1 }, - { C2Config::PROFILE_AV1_2, AV1Profile2 }, + // TODO: will need to disambiguate between Main8 and Main10 + { C2Config::PROFILE_AV1_0, AV1ProfileMain8 }, + { C2Config::PROFILE_AV1_0, AV1ProfileMain10 }, }; +ALookup sAv1HdrProfiles = { + { C2Config::PROFILE_AV1_0, AV1ProfileMain10 }, + { C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10 }, +}; + +ALookup sAv1Hdr10PlusProfiles = { + { C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10Plus }, +}; /** * A helper that passes through vendor extension profile and level values. @@ -590,6 +598,10 @@ private: }; struct Av1ProfileLevelMapper : ProfileLevelMapperHelper { + Av1ProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false) : + ProfileLevelMapperHelper(), + mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus) {} + virtual bool simpleMap(C2Config::level_t from, int32_t *to) { return sAv1Levels.map(from, to); } @@ -597,11 +609,19 @@ struct Av1ProfileLevelMapper : ProfileLevelMapperHelper { return sAv1Levels.map(from, to); } virtual bool simpleMap(C2Config::profile_t from, int32_t *to) { - return sAv1Profiles.map(from, to); + return mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) : + mIsHdr ? sAv1HdrProfiles.map(from, to) : + sAv1Profiles.map(from, to); } virtual bool simpleMap(int32_t from, C2Config::profile_t *to) { - return sAv1Profiles.map(from, to); + return mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) : + mIsHdr ? sAv1HdrProfiles.map(from, to) : + sAv1Profiles.map(from, to); } + +private: + bool mIsHdr; + bool mIsHdr10Plus; }; } // namespace diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h index c84614ae92..50d7724867 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h +++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h @@ -356,16 +356,18 @@ inline static const char *asString_VP9Level(int32_t i, const char *def = "??") { } } -constexpr int32_t AV1Profile0 = 0x01; -constexpr int32_t AV1Profile1 = 0x02; -constexpr int32_t AV1Profile2 = 0x04; +constexpr int32_t AV1ProfileMain8 = 0x1; +constexpr int32_t AV1ProfileMain10 = 0x2; +constexpr int32_t AV1ProfileMain10HDR10 = 0x1000; +constexpr int32_t AV1ProfileMain10HDR10Plus = 0x2000; inline static const char *asString_AV1Profile(int32_t i, const char *def = "??") { switch (i) { - case AV1Profile0: return "0"; - case AV1Profile1: return "1"; - case AV1Profile2: return "2"; - default: return def; + case AV1ProfileMain8: return "Main8"; + case AV1ProfileMain10: return "Main10HDR"; + case AV1ProfileMain10HDR10: return "Main10HDR10"; + case AV1ProfileMain10HDR10Plus: return "Main10HDRPlus"; + default: return def; } } -- GitLab From 1b25d681064d6fa499451c9a5762af393ad6d76c Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 26 Apr 2019 16:26:35 -0700 Subject: [PATCH 1346/1530] Update version number to 220000000 The Beta 3 builds (which are in dogfood and public beta) will receive modules via the Play Store with version numbers 210000000. Hence, we must bump the version in qt-dev and hence beta 4. BUG: 131240537 Change-Id: Ia568319821f3bc8765ff3c2096e963a32ffb15d6 --- apex/manifest.json | 2 +- apex/manifest_codec.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/manifest.json b/apex/manifest.json index cee94e26f9..03b9dd0930 100644 --- a/apex/manifest.json +++ b/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.media", - "version": 210000000 + "version": 220000000 } diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json index b83e65aaa5..58ce86879b 100644 --- a/apex/manifest_codec.json +++ b/apex/manifest_codec.json @@ -1,4 +1,4 @@ { "name": "com.android.media.swcodec", - "version": 210000000 + "version": 220000000 } -- GitLab From ffd6cbc2cfbd9fefa9ebad744c8a8364d1cf90cc Mon Sep 17 00:00:00 2001 From: James O'Leary Date: Fri, 26 Apr 2019 10:56:03 -0400 Subject: [PATCH 1347/1530] Fix Opus encoder being set to CBR The code is currently setting unconstrained CBR, instead of constrained VBR. The OPUS_SET_VBR argument should be 1, or not set at all, to be VBR. (libopus docs for OPUS_SET_VBR: https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoderctls.html#ga34d09ae06cab7e1a6c49876249b67892) Bug: 131410994 Test: Verify AGSA Opus encoder receives varying Opus frame sizes. Change-Id: Ife8340f720a0faedd748c61d6a69bb30dfe30706 --- media/codec2/components/opus/C2SoftOpusEnc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index a0b244387b..f1020c378d 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -194,12 +194,12 @@ c2_status_t C2SoftOpusEnc::configureEncoder() { return C2_BAD_VALUE; } - // Unconstrained VBR - if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR(0) != OPUS_OK)) { + // Constrained VBR + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR(1) != OPUS_OK)) { ALOGE("failed to set vbr type"); return C2_BAD_VALUE; } - if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR_CONSTRAINT(0) != + if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR_CONSTRAINT(1) != OPUS_OK)) { ALOGE("failed to set vbr constraint"); return C2_BAD_VALUE; -- GitLab From 09f1ed2cb4479c93859557f5b4d40e1c3732f6f2 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 24 Apr 2019 17:45:17 -0700 Subject: [PATCH 1348/1530] audio: improve audio routing callbacks Do not force audio device changed callback when the client (AudioTrack or AudioRecord) registers to AudioFlinger but only when it starts playback or capture. Doing so prevents a spurious callback happing at registration time where a stale device (previously selected by AudioFlinger thread) but irrelevant to this client is indicated. This causes a disconnection of AAudio streams despite no real device change. Bug: 128630993 Test: CTS: android.nativemedia.aaudio.AAudioTests, android.media.cts.RoutingTest CTS Verifier: Audio Input/Output Routing Notifications Test Change-Id: Ia7f1d11490989b0287c97479466c1c07a223aab3 --- media/libaudioclient/AudioRecord.cpp | 20 +-- media/libaudioclient/AudioSystem.cpp | 131 ++++++++---------- media/libaudioclient/AudioTrack.cpp | 20 +-- media/libaudioclient/IAudioFlingerClient.cpp | 2 + .../include/media/AudioIoDescriptor.h | 4 +- .../include/media/AudioSystem.h | 56 ++------ services/audioflinger/AudioFlinger.cpp | 9 +- services/audioflinger/MmapTracks.h | 1 + services/audioflinger/PlaybackTracks.h | 1 + services/audioflinger/RecordTracks.h | 1 + services/audioflinger/Threads.cpp | 43 ++++-- services/audioflinger/Threads.h | 30 ++-- services/audioflinger/TrackBase.h | 5 + services/audioflinger/Tracks.cpp | 20 ++- 14 files changed, 169 insertions(+), 174 deletions(-) diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 0cce5bc89c..a1b04caa92 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -177,7 +177,7 @@ AudioRecord::~AudioRecord() } // No lock here: worst case we remove a NULL callback which will be a nop if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) { - AudioSystem::removeAudioDeviceCallback(this, mInput); + AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId); } IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this); mAudioRecord.clear(); @@ -790,14 +790,13 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String mAudioRecord = record; mCblkMemory = output.cblk; mBufferMemory = output.buffers; - mPortId = output.portId; IPCThreadState::self()->flushCommands(); mCblk = cblk; // note that output.frameCount is the (possibly revised) value of mReqFrameCount if (output.frameCount < mReqFrameCount || (mReqFrameCount == 0 && output.frameCount == 0)) { ALOGW("%s(%d): Requested frameCount %zu but received frameCount %zu", - __func__, mPortId, + __func__, output.portId, mReqFrameCount, output.frameCount); } @@ -805,19 +804,20 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch, const String // The computation is done on server side. if (mNotificationFramesReq > 0 && output.notificationFrameCount != mNotificationFramesReq) { ALOGW("%s(%d): Server adjusted notificationFrames from %u to %zu for frameCount %zu", - __func__, mPortId, + __func__, output.portId, mNotificationFramesReq, output.notificationFrameCount, output.frameCount); } mNotificationFramesAct = (uint32_t)output.notificationFrameCount; //mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation - if (mDeviceCallback != 0 && mInput != output.inputId) { + if (mDeviceCallback != 0) { if (mInput != AUDIO_IO_HANDLE_NONE) { - AudioSystem::removeAudioDeviceCallback(this, mInput); + AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId); } - AudioSystem::addAudioDeviceCallback(this, output.inputId); + AudioSystem::addAudioDeviceCallback(this, output.inputId, output.portId); } + mPortId = output.portId; // We retain a copy of the I/O handle, but don't own the reference mInput = output.inputId; mRefreshRemaining = true; @@ -1332,9 +1332,9 @@ status_t AudioRecord::addAudioDeviceCallback(const spmIoHandle == AUDIO_IO_HANDLE_NONE) return; audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE; - Vector> callbacksToCall; + std::vector> callbacksToCall; { Mutex::Autolock _l(mLock); - bool deviceValidOrChanged = false; - bool sendCallbacks = false; - ssize_t ioIndex = -1; + auto callbacks = std::map>(); switch (event) { case AUDIO_OUTPUT_OPENED: @@ -546,17 +544,11 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) { deviceId = ioDesc->getDeviceId(); if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) { - ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); - if (ioIndex >= 0) { - sendCallbacks = true; - deviceValidOrChanged = true; + auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + if (it != mAudioDeviceCallbacks.end()) { + callbacks = it->second; } } - if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) { - ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); - sendCallbacks = (ioIndex >= 0) - && !mAudioDeviceCallbackProxies.valueAt(ioIndex).notifiedOnce(); - } } ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x " "frameCount %zu deviceId %d", @@ -578,7 +570,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); mIoDescriptors.removeItem(ioDesc->mIoHandle); - mAudioDeviceCallbackProxies.removeItem(ioDesc->mIoHandle); + mAudioDeviceCallbacks.erase(ioDesc->mIoHandle); } break; case AUDIO_OUTPUT_CONFIG_CHANGED: @@ -593,10 +585,11 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); if (deviceId != ioDesc->getDeviceId()) { - deviceValidOrChanged = true; deviceId = ioDesc->getDeviceId(); - ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle); - sendCallbacks = ioIndex >= 0; + auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + if (it != mAudioDeviceCallbacks.end()) { + callbacks = it->second; + } } ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x " "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d", @@ -606,35 +599,40 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event even ioDesc->getDeviceId()); } break; + case AUDIO_CLIENT_STARTED: { + sp oldDesc = getIoDescriptor_l(ioDesc->mIoHandle); + if (oldDesc == 0) { + ALOGW("ioConfigChanged() start client on unknown io! %d", ioDesc->mIoHandle); + break; + } + ALOGV("ioConfigChanged() AUDIO_CLIENT_STARTED io %d port %d num callbacks %zu", + ioDesc->mIoHandle, ioDesc->mPortId, mAudioDeviceCallbacks.size()); + oldDesc->mPatch = ioDesc->mPatch; + auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + if (it != mAudioDeviceCallbacks.end()) { + auto cbks = it->second; + auto it2 = cbks.find(ioDesc->mPortId); + if (it2 != cbks.end()) { + callbacks.emplace(ioDesc->mPortId, it2->second); + deviceId = oldDesc->getDeviceId(); + } + } + } break; } - // sendCallbacks true => ioDesc->mIoHandle and deviceId are valid - if (sendCallbacks) { - AudioDeviceCallbackProxies &callbackProxies = - mAudioDeviceCallbackProxies.editValueAt(ioIndex); - for (size_t i = 0; i < callbackProxies.size(); ) { - sp callback = callbackProxies[i]->callback(); - if (callback.get() != nullptr) { - // Call the callback only if the device actually changed, the input or output - // was opened or closed or the client was newly registered and the callback - // was never called - if (!callbackProxies[i]->notifiedOnce() || deviceValidOrChanged) { - callbacksToCall.add(callback); - callbackProxies[i]->setNotifiedOnce(); - } - i++; - } else { - callbackProxies.removeAt(i); - } + for (auto wpCbk : callbacks) { + sp spCbk = wpCbk.second.promote(); + if (spCbk != nullptr) { + callbacksToCall.push_back(spCbk); } - callbackProxies.setNotifiedOnce(); } } // Callbacks must be called without mLock held. May lead to dead lock if calling for // example getRoutedDevice that updates the device and tries to acquire mLock. - for (size_t i = 0; i < callbacksToCall.size(); i++) { - callbacksToCall[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); + for (auto cb : callbacksToCall) { + // If callbacksToCall is not empty, it implies ioDesc->mIoHandle and deviceId are valid + cb->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); } } @@ -687,51 +685,34 @@ sp AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_ } status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback( - const wp& callback, audio_io_handle_t audioIo) + const wp& callback, audio_io_handle_t audioIo, + audio_port_handle_t portId) { + ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId); Mutex::Autolock _l(mLock); - AudioDeviceCallbackProxies callbackProxies; - ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo); - if (ioIndex >= 0) { - callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex); - } - - for (size_t cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) { - sp cbk = callbackProxies[cbIndex]->callback(); - if (cbk.get() == callback.unsafe_get()) { - return INVALID_OPERATION; - } + auto& callbacks = mAudioDeviceCallbacks.emplace(audioIo, std::map>()).first->second; + auto result = callbacks.try_emplace(portId, callback); + if (!result.second) { + return INVALID_OPERATION; } - callbackProxies.add(new AudioDeviceCallbackProxy(callback)); - callbackProxies.resetNotifiedOnce(); - mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies); return NO_ERROR; } status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( - const wp& callback, audio_io_handle_t audioIo) + const wp& callback __unused, audio_io_handle_t audioIo, + audio_port_handle_t portId) { + ALOGV("%s audioIo %d portId %d", __func__, audioIo, portId); Mutex::Autolock _l(mLock); - ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo); - if (ioIndex < 0) { + auto it = mAudioDeviceCallbacks.find(audioIo); + if (it == mAudioDeviceCallbacks.end()) { return INVALID_OPERATION; } - AudioDeviceCallbackProxies callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex); - size_t cbIndex; - for (cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) { - sp cbk = callbackProxies[cbIndex]->callback(); - if (cbk.get() == callback.unsafe_get()) { - break; - } - } - if (cbIndex == callbackProxies.size()) { + if (it->second.erase(portId) == 0) { return INVALID_OPERATION; } - callbackProxies.removeAt(cbIndex); - if (callbackProxies.size() != 0) { - mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies); - } else { - mAudioDeviceCallbackProxies.removeItem(audioIo); + if (it->second.size() == 0) { + mAudioDeviceCallbacks.erase(audioIo); } return NO_ERROR; } @@ -1271,13 +1252,14 @@ status_t AudioSystem::removeAudioVolumeGroupCallback(const sp& callback, audio_io_handle_t audioIo) + const wp& callback, audio_io_handle_t audioIo, + audio_port_handle_t portId) { const sp afc = getAudioFlingerClient(); if (afc == 0) { return NO_INIT; } - status_t status = afc->addAudioDeviceCallback(callback, audioIo); + status_t status = afc->addAudioDeviceCallback(callback, audioIo, portId); if (status == NO_ERROR) { const sp& af = AudioSystem::get_audio_flinger(); if (af != 0) { @@ -1288,13 +1270,14 @@ status_t AudioSystem::addAudioDeviceCallback( } status_t AudioSystem::removeAudioDeviceCallback( - const wp& callback, audio_io_handle_t audioIo) + const wp& callback, audio_io_handle_t audioIo, + audio_port_handle_t portId) { const sp afc = getAudioFlingerClient(); if (afc == 0) { return NO_INIT; } - return afc->removeAudioDeviceCallback(callback, audioIo); + return afc->removeAudioDeviceCallback(callback, audioIo, portId); } audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index bd48f56ca3..9387d4d612 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -309,7 +309,7 @@ AudioTrack::~AudioTrack() } // No lock here: worst case we remove a NULL callback which will be a nop if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) { - AudioSystem::removeAudioDeviceCallback(this, mOutput); + AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId); } IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this); mAudioTrack.clear(); @@ -1495,7 +1495,6 @@ status_t AudioTrack::createTrack_l() mAfFrameCount = output.afFrameCount; mAfSampleRate = output.afSampleRate; mAfLatency = output.afLatencyMs; - mPortId = output.portId; mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate; @@ -1543,14 +1542,15 @@ status_t AudioTrack::createTrack_l() mFlags = output.flags; //mOutput != output includes the case where mOutput == AUDIO_IO_HANDLE_NONE for first creation - if (mDeviceCallback != 0 && mOutput != output.outputId) { + if (mDeviceCallback != 0) { if (mOutput != AUDIO_IO_HANDLE_NONE) { - AudioSystem::removeAudioDeviceCallback(this, mOutput); + AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId); } - AudioSystem::addAudioDeviceCallback(this, output.outputId); + AudioSystem::addAudioDeviceCallback(this, output.outputId, output.portId); callbackAdded = true; } + mPortId = output.portId; // We retain a copy of the I/O handle, but don't own the reference mOutput = output.outputId; mRefreshRemaining = true; @@ -1615,7 +1615,7 @@ status_t AudioTrack::createTrack_l() exit: if (status != NO_ERROR && callbackAdded) { // note: mOutput is always valid is callbackAdded is true - AudioSystem::removeAudioDeviceCallback(this, mOutput); + AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId); } mStatus = status; @@ -2922,6 +2922,7 @@ uint32_t AudioTrack::getUnderrunFrames() const status_t AudioTrack::addAudioDeviceCallback(const sp& callback) { + if (callback == 0) { ALOGW("%s(%d): adding NULL callback!", __func__, mPortId); return BAD_VALUE; @@ -2935,9 +2936,9 @@ status_t AudioTrack::addAudioDeviceCallback(const sponAudioDeviceUpdate(mOutput, mRoutedDeviceId); } diff --git a/media/libaudioclient/IAudioFlingerClient.cpp b/media/libaudioclient/IAudioFlingerClient.cpp index b2dbc4c814..47eb7dc528 100644 --- a/media/libaudioclient/IAudioFlingerClient.cpp +++ b/media/libaudioclient/IAudioFlingerClient.cpp @@ -52,6 +52,7 @@ public: data.writeInt64(ioDesc->mFrameCount); data.writeInt64(ioDesc->mFrameCountHAL); data.writeInt32(ioDesc->mLatency); + data.writeInt32(ioDesc->mPortId); remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); } }; @@ -76,6 +77,7 @@ status_t BnAudioFlingerClient::onTransact( ioDesc->mFrameCount = data.readInt64(); ioDesc->mFrameCountHAL = data.readInt64(); ioDesc->mLatency = data.readInt32(); + ioDesc->mPortId = data.readInt32(); ioConfigChanged(event, ioDesc); return NO_ERROR; } break; diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h index 859f1a918e..981d33a39a 100644 --- a/media/libaudioclient/include/media/AudioIoDescriptor.h +++ b/media/libaudioclient/include/media/AudioIoDescriptor.h @@ -28,6 +28,7 @@ enum audio_io_config_event { AUDIO_INPUT_OPENED, AUDIO_INPUT_CLOSED, AUDIO_INPUT_CONFIG_CHANGED, + AUDIO_CLIENT_STARTED, }; // audio input/output descriptor used to cache output configurations in client process to avoid @@ -37,7 +38,7 @@ public: AudioIoDescriptor() : mIoHandle(AUDIO_IO_HANDLE_NONE), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE), - mFrameCount(0), mFrameCountHAL(0), mLatency(0) + mFrameCount(0), mFrameCountHAL(0), mLatency(0), mPortId(AUDIO_PORT_HANDLE_NONE) { memset(&mPatch, 0, sizeof(struct audio_patch)); } @@ -66,6 +67,7 @@ public: size_t mFrameCount; size_t mFrameCountHAL; uint32_t mLatency; // only valid for output + audio_port_handle_t mPortId; // valid for event AUDIO_CLIENT_STARTED }; diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index d3035dacb6..f79ec21597 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -438,32 +438,12 @@ public: audio_port_handle_t deviceId) = 0; }; - class AudioDeviceCallbackProxy : public RefBase - { - public: - - AudioDeviceCallbackProxy(wp callback) - : mCallback(callback) {} - ~AudioDeviceCallbackProxy() override {} - - sp callback() const { return mCallback.promote(); }; - - bool notifiedOnce() const { return mNotifiedOnce; } - void setNotifiedOnce() { mNotifiedOnce = true; } - private: - /** - * @brief mNotifiedOnce it forces the callback to be called at least once when - * registered with a VALID AudioDevice, and allows not to flood other listeners - * on this iohandle that already know the valid device. - */ - bool mNotifiedOnce = false; - wp mCallback; - }; - static status_t addAudioDeviceCallback(const wp& callback, - audio_io_handle_t audioIo); + audio_io_handle_t audioIo, + audio_port_handle_t portId); static status_t removeAudioDeviceCallback(const wp& callback, - audio_io_handle_t audioIo); + audio_io_handle_t audioIo, + audio_port_handle_t portId); static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo); @@ -494,9 +474,11 @@ private: status_t addAudioDeviceCallback(const wp& callback, - audio_io_handle_t audioIo); + audio_io_handle_t audioIo, + audio_port_handle_t portId); status_t removeAudioDeviceCallback(const wp& callback, - audio_io_handle_t audioIo); + audio_io_handle_t audioIo, + audio_port_handle_t portId); audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo); @@ -504,26 +486,8 @@ private: Mutex mLock; DefaultKeyedVector > mIoDescriptors; - class AudioDeviceCallbackProxies : public Vector> - { - public: - /** - * @brief notifiedOnce ensures that if a client adds a callback, it must at least be - * called once with the device on which it will be routed to. - * @return true if already notified or nobody waits for a callback, false otherwise. - */ - bool notifiedOnce() const { return (size() == 0) || mNotifiedOnce; } - void setNotifiedOnce() { mNotifiedOnce = true; } - void resetNotifiedOnce() { mNotifiedOnce = false; } - private: - /** - * @brief mNotifiedOnce it forces each callback to be called at least once when - * registered with a VALID AudioDevice - */ - bool mNotifiedOnce = false; - }; - DefaultKeyedVector - mAudioDeviceCallbackProxies; + std::map>> + mAudioDeviceCallbacks; // cached values for recording getInputBufferSize() queries size_t mInBuffSize; // zero indicates cache is invalid uint32_t mInSamplingRate; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0f03b7e58d..5e5ea1120b 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -701,8 +701,8 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, updatePid = true; } pid_t clientPid = input.clientInfo.clientPid; + const pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (updatePid) { - const pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW_IF(clientPid != -1 && clientPid != callingPid, "%s uid %d pid %d tried to pass itself off as pid %d", __func__, callingUid, callingPid, clientPid); @@ -787,7 +787,8 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, &output.frameCount, &output.notificationFrameCount, input.notificationsPerBuffer, input.speed, input.sharedBuffer, sessionId, &output.flags, - input.clientInfo.clientTid, clientUid, &lStatus, portId); + callingPid, input.clientInfo.clientTid, clientUid, + &lStatus, portId); LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0)); // we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless @@ -1841,8 +1842,8 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu updatePid = true; } pid_t clientPid = input.clientInfo.clientPid; + const pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (updatePid) { - const pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW_IF(clientPid != -1 && clientPid != callingPid, "%s uid %d pid %d tried to pass itself off as pid %d", __func__, callingUid, callingPid, clientPid); @@ -1919,7 +1920,7 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu input.config.format, input.config.channel_mask, &output.frameCount, sessionId, &output.notificationFrameCount, - clientUid, &output.flags, + callingPid, clientUid, &output.flags, input.clientInfo.clientTid, &lStatus, portId); LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0)); diff --git a/services/audioflinger/MmapTracks.h b/services/audioflinger/MmapTracks.h index 968d5aa3f1..b83f6b5b17 100644 --- a/services/audioflinger/MmapTracks.h +++ b/services/audioflinger/MmapTracks.h @@ -31,6 +31,7 @@ public: bool isOut, uid_t uid, pid_t pid, + pid_t creatorPid, audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); virtual ~MmapTrack(); diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 56be433d4b..bb97f8d3f0 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -69,6 +69,7 @@ public: size_t bufferSize, const sp& sharedBuffer, audio_session_t sessionId, + pid_t creatorPid, uid_t uid, audio_output_flags_t flags, track_type type, diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index ec1f86cc11..08660dd67a 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -32,6 +32,7 @@ public: void *buffer, size_t bufferSize, audio_session_t sessionId, + pid_t creatorPid, uid_t uid, audio_input_flags_t flags, track_type type, diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index fd29f31bc3..0c6cfa1292 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -566,14 +566,16 @@ status_t AudioFlinger::ThreadBase::sendConfigEvent_l(sp& event) return status; } -void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event, pid_t pid) +void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId) { Mutex::Autolock _l(mLock); - sendIoConfigEvent_l(event, pid); + sendIoConfigEvent_l(event, pid, portId); } // sendIoConfigEvent_l() must be called with ThreadBase::mLock held -void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, pid_t pid) +void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId) { // The audio statistics history is exponentially weighted to forget events // about five or more seconds in the past. In order to have @@ -584,7 +586,7 @@ void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, mProcessTimeMs.reset(); mTimestampVerifier.discontinuity(); - sp configEvent = (ConfigEvent *)new IoConfigEvent(event, pid); + sp configEvent = (ConfigEvent *)new IoConfigEvent(event, pid, portId); sendConfigEvent_l(configEvent); } @@ -667,7 +669,7 @@ void AudioFlinger::ThreadBase::processConfigEvents_l() } break; case CFG_EVENT_IO: { IoConfigEventData *data = (IoConfigEventData *)event->mData.get(); - ioConfigChanged(data->mEvent, data->mPid); + ioConfigChanged(data->mEvent, data->mPid, data->mPortId); } break; case CFG_EVENT_SET_PARAMETER: { SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get(); @@ -1952,6 +1954,7 @@ sp AudioFlinger::PlaybackThread::createTrac const sp& sharedBuffer, audio_session_t sessionId, audio_output_flags_t *flags, + pid_t creatorPid, pid_t tid, uid_t uid, status_t *status, @@ -2235,7 +2238,7 @@ sp AudioFlinger::PlaybackThread::createTrac track = new Track(this, client, streamType, attr, sampleRate, format, channelMask, frameCount, nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer, - sessionId, uid, *flags, TrackBase::TYPE_DEFAULT, portId); + sessionId, creatorPid, uid, *flags, TrackBase::TYPE_DEFAULT, portId); lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY; if (lStatus != NO_ERROR) { @@ -2391,6 +2394,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) // to track the speaker usage addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart); #endif + sendIoConfigEvent_l(AUDIO_CLIENT_STARTED, track->creatorPid(), track->portId()); } // set retry count for buffer fill @@ -2499,7 +2503,8 @@ status_t AudioFlinger::DirectOutputThread::selectPresentation(int presentationId return mOutput->stream->selectPresentation(presentationId, programId); } -void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { +void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId) { sp desc = new AudioIoDescriptor(); ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event); @@ -2518,7 +2523,10 @@ void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, desc->mFrameCountHAL = mFrameCount; desc->mLatency = latency_l(); break; - + case AUDIO_CLIENT_STARTED: + desc->mPatch = mPatch; + desc->mPortId = portId; + break; case AUDIO_OUTPUT_CLOSED: default: break; @@ -7405,6 +7413,7 @@ sp AudioFlinger::RecordThread::createRe size_t *pFrameCount, audio_session_t sessionId, size_t *pNotificationFrameCount, + pid_t creatorPid, uid_t uid, audio_input_flags_t *flags, pid_t tid, @@ -7542,7 +7551,7 @@ sp AudioFlinger::RecordThread::createRe track = new RecordTrack(this, client, attr, sampleRate, format, channelMask, frameCount, - nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, uid, + nullptr /* buffer */, (size_t)0 /* bufferSize */, sessionId, creatorPid, uid, *flags, TrackBase::TYPE_DEFAULT, portId); lStatus = track->initCheck(); @@ -7650,6 +7659,8 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac recordTrack->clearSyncStartEvent(); return status; } + sendIoConfigEvent_l( + AUDIO_CLIENT_STARTED, recordTrack->creatorPid(), recordTrack->portId()); } // Catch up with current buffer indices if thread is already running. // This is what makes a new client discard all buffered data. If the track's mRsmpInFront @@ -8131,7 +8142,8 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return String8(); } -void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { +void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId) { sp desc = new AudioIoDescriptor(); desc->mIoHandle = mId; @@ -8148,7 +8160,10 @@ void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event, pi desc->mFrameCountHAL = mFrameCount; desc->mLatency = 0; break; - + case AUDIO_CLIENT_STARTED: + desc->mPatch = mPatch; + desc->mPortId = portId; + break; case AUDIO_INPUT_CLOSED: default: break; @@ -8606,7 +8621,8 @@ status_t AudioFlinger::MmapThread::start(const AudioClient& client, // Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ? sp track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId, - isOutput(), client.clientUid, client.clientPid, portId); + isOutput(), client.clientUid, client.clientPid, + IPCThreadState::self()->getCallingPid(), portId); if (isOutput()) { // force volume update when a new track is added @@ -8832,7 +8848,8 @@ String8 AudioFlinger::MmapThread::getParameters(const String8& keys) return String8(); } -void AudioFlinger::MmapThread::ioConfigChanged(audio_io_config_event event, pid_t pid) { +void AudioFlinger::MmapThread::ioConfigChanged(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId __unused) { sp desc = new AudioIoDescriptor(); desc->mIoHandle = mId; diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 37b2d08ca3..336c2b40a9 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -103,8 +103,9 @@ public: class IoConfigEventData : public ConfigEventData { public: - IoConfigEventData(audio_io_config_event event, pid_t pid) : - mEvent(event), mPid(pid) {} + IoConfigEventData(audio_io_config_event event, pid_t pid, + audio_port_handle_t portId) : + mEvent(event), mPid(pid), mPortId(portId) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "IO event: event %d\n", mEvent); @@ -112,13 +113,14 @@ public: const audio_io_config_event mEvent; const pid_t mPid; + const audio_port_handle_t mPortId; }; class IoConfigEvent : public ConfigEvent { public: - IoConfigEvent(audio_io_config_event event, pid_t pid) : + IoConfigEvent(audio_io_config_event event, pid_t pid, audio_port_handle_t portId) : ConfigEvent(CFG_EVENT_IO) { - mData = new IoConfigEventData(event, pid); + mData = new IoConfigEventData(event, pid, portId); } virtual ~IoConfigEvent() {} }; @@ -260,13 +262,16 @@ public: status_t& status) = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0) = 0; + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) = 0; // sendConfigEvent_l() must be called with ThreadBase::mLock held // Can temporarily release the lock if waiting for a reply from // processConfigEvents_l(). status_t sendConfigEvent_l(sp& event); - void sendIoConfigEvent(audio_io_config_event event, pid_t pid = 0); - void sendIoConfigEvent_l(audio_io_config_event event, pid_t pid = 0); + void sendIoConfigEvent(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); + void sendIoConfigEvent_l(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio, bool forApp); void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio, bool forApp); status_t sendSetParameterConfigEvent_l(const String8& keyValuePair); @@ -802,6 +807,7 @@ public: const sp& sharedBuffer, audio_session_t sessionId, audio_output_flags_t *flags, + pid_t creatorPid, pid_t tid, uid_t uid, status_t *status /*non-NULL*/, @@ -825,7 +831,8 @@ public: { return android_atomic_acquire_load(&mSuspended) > 0; } virtual String8 getParameters(const String8& keys); - virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0); + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); // Consider also removing and passing an explicit mMainBuffer initialization // parameter to AF::PlaybackThread::Track::Track(). @@ -1540,6 +1547,7 @@ public: size_t *pFrameCount, audio_session_t sessionId, size_t *pNotificationFrameCount, + pid_t creatorPid, uid_t uid, audio_input_flags_t *flags, pid_t tid, @@ -1562,7 +1570,8 @@ public: status_t& status); virtual void cacheParameters_l() {} virtual String8 getParameters(const String8& keys); - virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0); + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); virtual status_t createAudioPatch_l(const struct audio_patch *patch, audio_patch_handle_t *handle); virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); @@ -1743,7 +1752,8 @@ class MmapThread : public ThreadBase virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); virtual String8 getParameters(const String8& keys); - virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0); + virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE); void readHalParameters_l(); virtual void cacheParameters_l() {} virtual status_t createAudioPatch_l(const struct audio_patch *patch, diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 4402d99314..8f720b58d5 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -64,6 +64,7 @@ public: void *buffer, size_t bufferSize, audio_session_t sessionId, + pid_t creatorPid, uid_t uid, bool isOut, alloc_type alloc = ALLOC_CBLK, @@ -79,6 +80,8 @@ public: audio_track_cblk_t* cblk() const { return mCblk; } audio_session_t sessionId() const { return mSessionId; } uid_t uid() const { return mUid; } + pid_t creatorPid() const { return mCreatorPid; } + audio_port_handle_t portId() const { return mPortId; } virtual status_t setSyncEvent(const sp& event); @@ -310,6 +313,8 @@ protected: std::atomic mServerLatencyFromTrack{}; // latency from track or server timestamp. std::atomic mServerLatencyMs{}; // last latency pushed from server thread. std::atomic mKernelFrameTime{}; // last frame time on kernel side. + const pid_t mCreatorPid; // can be different from mclient->pid() for instance + // when created by NuPlayer on behalf of a client }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 8d59431a65..e1f00c190d 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -73,6 +73,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( void *buffer, size_t bufferSize, audio_session_t sessionId, + pid_t creatorPid, uid_t clientUid, bool isOut, alloc_type alloc, @@ -101,7 +102,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mType(type), mThreadIoHandle(thread ? thread->id() : AUDIO_IO_HANDLE_NONE), mPortId(portId), - mIsInvalid(false) + mIsInvalid(false), + mCreatorPid(creatorPid) { const uid_t callingUid = IPCThreadState::self()->getCallingUid(); if (!isAudioServerOrMediaServerUid(callingUid) || clientUid == AUDIO_UID_INVALID) { @@ -485,6 +487,7 @@ AudioFlinger::PlaybackThread::Track::Track( size_t bufferSize, const sp& sharedBuffer, audio_session_t sessionId, + pid_t creatorPid, uid_t uid, audio_output_flags_t flags, track_type type, @@ -492,7 +495,7 @@ AudioFlinger::PlaybackThread::Track::Track( : TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount, (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer, (sharedBuffer != 0) ? sharedBuffer->size() : bufferSize, - sessionId, uid, true /*isOut*/, + sessionId, creatorPid, uid, true /*isOut*/, (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK, type, portId), mFillingUpStatus(FS_INVALID), @@ -1543,7 +1546,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( audio_attributes_t{} /* currently unused for output track */, sampleRate, format, channelMask, frameCount, nullptr /* buffer */, (size_t)0 /* bufferSize */, nullptr /* sharedBuffer */, - AUDIO_SESSION_NONE, uid, AUDIO_OUTPUT_FLAG_NONE, + AUDIO_SESSION_NONE, getpid(), uid, AUDIO_OUTPUT_FLAG_NONE, TYPE_OUTPUT), mActive(false), mSourceThread(sourceThread) { @@ -1772,7 +1775,7 @@ AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThr audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, buffer, bufferSize, nullptr /* sharedBuffer */, - AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH), + AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER, flags, TYPE_PATCH), PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true), *playbackThread, timeout) { @@ -1927,12 +1930,14 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( void *buffer, size_t bufferSize, audio_session_t sessionId, + pid_t creatorPid, uid_t uid, audio_input_flags_t flags, track_type type, audio_port_handle_t portId) : TrackBase(thread, client, attr, sampleRate, format, - channelMask, frameCount, buffer, bufferSize, sessionId, uid, false /*isOut*/, + channelMask, frameCount, buffer, bufferSize, sessionId, + creatorPid, uid, false /*isOut*/, (type == TYPE_DEFAULT) ? ((flags & AUDIO_INPUT_FLAG_FAST) ? ALLOC_PIPE : ALLOC_CBLK) : ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE), @@ -2242,7 +2247,7 @@ AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, : RecordTrack(recordThread, NULL, audio_attributes_t{} /* currently unused for patch track */, sampleRate, format, channelMask, frameCount, - buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER, + buffer, bufferSize, AUDIO_SESSION_NONE, getpid(), AID_AUDIOSERVER, flags, TYPE_PATCH), PatchTrackBase(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true), *recordThread, timeout) @@ -2310,11 +2315,12 @@ AudioFlinger::MmapThread::MmapTrack::MmapTrack(ThreadBase *thread, bool isOut, uid_t uid, pid_t pid, + pid_t creatorPid, audio_port_handle_t portId) : TrackBase(thread, NULL, attr, sampleRate, format, channelMask, (size_t)0 /* frameCount */, nullptr /* buffer */, (size_t)0 /* bufferSize */, - sessionId, uid, isOut, + sessionId, creatorPid, uid, isOut, ALLOC_NONE, TYPE_DEFAULT, portId), mPid(pid), mSilenced(false), mSilencedNotified(false) -- GitLab From 3c3d1d267efea85412a70eb1e18089a7d56a413e Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 8 Apr 2019 17:22:26 -0700 Subject: [PATCH 1349/1530] omx: only list existing OMX codecs in OmxStore Bug: 129710438 Change-Id: Iaeba313ec94838880a8d20d775866f20454104be --- media/libstagefright/omx/1.0/OmxStore.cpp | 27 +++++++++++++++++++ .../media/stagefright/omx/1.0/OmxStore.h | 2 ++ services/mediacodec/main_codecservice.cpp | 12 ++++----- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp index 2e041e3a39..89292e84c4 100644 --- a/media/libstagefright/omx/1.0/OmxStore.cpp +++ b/media/libstagefright/omx/1.0/OmxStore.cpp @@ -17,6 +17,8 @@ #include #include +#define LOG_TAG "OmxStore" + #include #include @@ -30,12 +32,29 @@ namespace omx { namespace V1_0 { namespace implementation { +using ::android::hardware::media::omx::V1_0::Status; +using ::android::hardware::media::omx::V1_0::IOmx; + OmxStore::OmxStore( + const sp &omx, const char* owner, const char* const* searchDirs, const char* mainXmlName, const char* performanceXmlName, const char* profilingResultsXmlPath) { + // retrieve list of omx nodes + std::set nodes; + if (omx != nullptr) { + omx->listNodes([&nodes](const Status &status, + const hidl_vec &nodeList) { + if (status == Status::OK) { + for (const IOmx::ComponentInfo& info : nodeList) { + nodes.emplace(info.mName.c_str()); + } + } + }); + } + MediaCodecsXmlParser parser(searchDirs, mainXmlName, performanceXmlName, @@ -66,6 +85,13 @@ OmxStore::OmxStore( nodeList.resize(rolePair.second.nodeList.size()); size_t j = 0; for (const auto& nodePair : rolePair.second.nodeList) { + if (!nodes.count(nodePair.second.name)) { + // not supported by this OMX instance + if (!strncasecmp(nodePair.second.name.c_str(), "omx.", 4)) { + LOG(INFO) << "node [" << nodePair.second.name.c_str() << "] not found in IOmx"; + } + continue; + } NodeInfo node; node.name = nodePair.second.name; node.owner = owner; @@ -82,6 +108,7 @@ OmxStore::OmxStore( nodeList[j] = std::move(node); ++j; } + nodeList.resize(j); mRoleList[i] = std::move(role); ++i; } diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h index 006d2d96c3..0fcea481e9 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -43,6 +44,7 @@ using ::android::wp; struct OmxStore : public IOmxStore { OmxStore( + const sp &omx = nullptr, const char* owner = "default", const char* const* searchDirs = MediaCodecsXmlParser::defaultSearchDirs, diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp index 6ffbd268a3..f668c3381b 100644 --- a/services/mediacodec/main_codecservice.cpp +++ b/services/mediacodec/main_codecservice.cpp @@ -49,12 +49,6 @@ int main(int argc __unused, char** argv) // Default codec services using namespace ::android::hardware::media::omx::V1_0; - sp omxStore = new implementation::OmxStore(); - if (omxStore == nullptr) { - LOG(ERROR) << "Cannot create IOmxStore HAL service."; - } else if (omxStore->registerAsService() != OK) { - LOG(ERROR) << "Cannot register IOmxStore HAL service."; - } sp omx = new implementation::Omx(); if (omx == nullptr) { LOG(ERROR) << "Cannot create IOmx HAL service."; @@ -63,6 +57,12 @@ int main(int argc __unused, char** argv) } else { LOG(INFO) << "IOmx HAL service created."; } + sp omxStore = new implementation::OmxStore(omx); + if (omxStore == nullptr) { + LOG(ERROR) << "Cannot create IOmxStore HAL service."; + } else if (omxStore->registerAsService() != OK) { + LOG(ERROR) << "Cannot register IOmxStore HAL service."; + } ::android::hardware::joinRpcThreadpool(); } -- GitLab From eb6e65034b3ac0979bfe8b5161d57cc279a3b945 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 29 Apr 2019 13:47:40 -0700 Subject: [PATCH 1350/1530] AudioTrack: Fix callback timing if less than one AF mixer period. Callback may wait nearly the entire period when double buffered which can lead to random glitches. Limit this to half the mixer period. Test: aaudio test write_sine_callback -pl -n1 -r44100 -s20 Bug: 129545307 Bug: 131601263 Change-Id: I6b7478728fe7ead570251662ee48a8f8adad0d5d --- media/libaudioclient/AudioTrack.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index bd48f56ca3..0e8d139ab6 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -2144,9 +2144,27 @@ nsecs_t AudioTrack::processAudioBuffer() const nsecs_t timeNow = systemTime(); ns = max((nsecs_t)0, ns - (timeNow - timeAfterCallbacks)); } - nsecs_t myns = framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed); - if (ns < 0 /* NS_WHENEVER */ || myns < ns) { - ns = myns; + + // delayNs is first computed by the additional frames required in the buffer. + nsecs_t delayNs = framesToNanoseconds( + mRemainingFrames - avail, sampleRate, speed); + + // afNs is the AudioFlinger mixer period in ns. + const nsecs_t afNs = framesToNanoseconds(mAfFrameCount, mAfSampleRate, speed); + + // If the AudioTrack is double buffered based on the AudioFlinger mixer period, + // we may have a race if we wait based on the number of frames desired. + // This is a possible issue with resampling and AAudio. + // + // The granularity of audioflinger processing is one mixer period; if + // our wait time is less than one mixer period, wait at most half the period. + if (delayNs < afNs) { + delayNs = std::min(delayNs, afNs / 2); + } + + // adjust our ns wait by delayNs. + if (ns < 0 /* NS_WHENEVER */ || delayNs < ns) { + ns = delayNs; } return ns; } -- GitLab From 0e4c08eb08e39138993cde40c986b569ce595ad1 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 19 Apr 2019 10:00:45 -0700 Subject: [PATCH 1351/1530] build core bufferqueue functionalities into bqhelper Statically build the bufferqueue logic from libgui into libstagefright_bufferqueue_helper, so that we don't need to link to the entire libgui. In updateable codecs, the dependencies (except llndk) are copied onto the apex, and loaded into a separate namespace at runtime. Trimming down unused dependencies help on both storage and runtime memory footprint. bug: 128894663 Change-Id: I2f696aa85143f74f753fbb0320dce5aee88846c4 --- .../stagefright/codec2/1.0/InputSurface.h | 1 - .../codec2/1.0/InputSurfaceConnection.h | 1 - .../codec2/hidl/1.0/utils/ComponentStore.cpp | 1 - .../include/codec2/hidl/1.0/InputSurface.h | 1 - .../include/media/omx/1.0/Conversion.h | 13 +- media/libstagefright/bqhelper/Android.bp | 18 +- media/libstagefright/bqhelper/Conversion.cpp | 1542 ----------------- .../bqhelper/GraphicBufferSource.cpp | 16 + .../bqhelper/WProducerListener.cpp | 50 - .../media/stagefright/bqhelper/Conversion.h | 769 -------- .../bqhelper/GraphicBufferSource.h | 17 +- .../bqhelper/WGraphicBufferProducer.h | 380 ---- .../stagefright/bqhelper/WProducerListener.h | 62 - media/libstagefright/omx/1.0/Omx.cpp | 7 +- media/libstagefright/omx/OMXNodeInstance.cpp | 3 +- .../media/stagefright/omx/1.0/Conversion.h | 84 +- .../omx/1.0/WGraphicBufferProducer.h | 39 - 17 files changed, 64 insertions(+), 2940 deletions(-) delete mode 100644 media/libstagefright/bqhelper/Conversion.cpp delete mode 100644 media/libstagefright/bqhelper/WProducerListener.cpp delete mode 100644 media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h delete mode 100644 media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h delete mode 100644 media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h delete mode 100644 media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h diff --git a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h index b011a06147..0a82a6813b 100644 --- a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h +++ b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h @@ -20,7 +20,6 @@ #include #include -#include #include namespace android { diff --git a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h index b24a41684f..5eae3af4cd 100644 --- a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h +++ b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h @@ -21,7 +21,6 @@ #include #include -#include #include namespace android { diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp index d6f84b4152..1e0a1905a6 100644 --- a/media/codec2/hidl/1.0/utils/ComponentStore.cpp +++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h index 34ea959d13..29ed7ff082 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h index babda222bf..80e8f3a7bd 100644 --- a/media/libmedia/include/media/omx/1.0/Conversion.h +++ b/media/libmedia/include/media/omx/1.0/Conversion.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H -#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H +#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_UTILS_CONVERSION_H +#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_UTILS_CONVERSION_H #include #include @@ -258,7 +258,12 @@ inline status_t toStatusT(Status const& t) { */ // convert: Status -> status_t inline status_t toStatusT(Return const& t) { - return t.isOk() ? toStatusT(static_cast(t)) : UNKNOWN_ERROR; + if (t.isOk()) { + return toStatusT(static_cast(t)); + } else if (t.isDeadObject()) { + return DEAD_OBJECT; + } + return UNKNOWN_ERROR; } /** @@ -938,4 +943,4 @@ inline OMX_TICKS toOMXTicks(uint64_t t) { } // namespace hardware } // namespace android -#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H +#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_UTILS_CONVERSION_H diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp index 218fe1569e..db67034f82 100644 --- a/media/libstagefright/bqhelper/Android.bp +++ b/media/libstagefright/bqhelper/Android.bp @@ -6,10 +6,8 @@ cc_library_shared { }, double_loadable: true, srcs: [ - "Conversion.cpp", "FrameDropper.cpp", "GraphicBufferSource.cpp", - "WProducerListener.cpp", ], export_include_dirs: [ @@ -27,7 +25,6 @@ cc_library_shared { shared_libs: [ "libbinder", "libcutils", - "libgui", "libhidlbase", "libhidlmemory", "libhidltransport", @@ -37,12 +34,25 @@ cc_library_shared { "libutils", "android.hardware.graphics.bufferqueue@1.0", + // Following libs are from libgui_bufferqueue_static + "android.hardware.graphics.bufferqueue@2.0", + "android.hidl.token@1.0-utils", + "libbase", + "libEGL", + "libhwbinder", + "libnativewindow", + "libvndksupport", + ], + + static_libs: [ + "libgui_bufferqueue_static" ], export_shared_lib_headers: [ - "libgui", "libhidlmemory", "libstagefright_foundation", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", ], cflags: [ diff --git a/media/libstagefright/bqhelper/Conversion.cpp b/media/libstagefright/bqhelper/Conversion.cpp deleted file mode 100644 index 91d7c74dd6..0000000000 --- a/media/libstagefright/bqhelper/Conversion.cpp +++ /dev/null @@ -1,1542 +0,0 @@ -/* - * Copyright 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -namespace android { -namespace conversion { - -// native_handle_t helper functions. - -/** - * \brief Take an fd and create a native handle containing only the given fd. - * The created handle will need to be deleted manually with - * `native_handle_delete()`. - * - * \param[in] fd The source file descriptor (of type `int`). - * \return The create `native_handle_t*` that contains the given \p fd. If the - * supplied \p fd is negative, the created native handle will contain no file - * descriptors. - * - * If the native handle cannot be created, the return value will be - * `nullptr`. - * - * This function does not duplicate the file descriptor. - */ -native_handle_t* native_handle_create_from_fd(int fd) { - if (fd < 2) { - return native_handle_create(0, 0); - } - native_handle_t* nh = native_handle_create(1, 0); - if (nh == nullptr) { - return nullptr; - } - nh->data[0] = fd; - return nh; -} - -/** - * \brief Extract a file descriptor from a native handle. - * - * \param[in] nh The source `native_handle_t*`. - * \param[in] index The index of the file descriptor in \p nh to read from. This - * input has the default value of `0`. - * \return The `index`-th file descriptor in \p nh. If \p nh does not have - * enough file descriptors, the returned value will be `-1`. - * - * This function does not duplicate the file descriptor. - */ -int native_handle_read_fd(native_handle_t const* nh, int index) { - return ((nh == nullptr) || (nh->numFds == 0) || - (nh->numFds <= index) || (index < 0)) ? - -1 : nh->data[index]; -} - -/** - * Conversion functions - * ==================== - * - * There are two main directions of conversion: - * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the - * input. The wrapper has type `TargetType`. - * - `toTargetType(...)`: Create a standalone object of type `TargetType` that - * corresponds to the input. The lifetime of the output does not depend on the - * lifetime of the input. - * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType` - * that cannot be copied and/or moved efficiently, or when there are multiple - * output arguments. - * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for - * `TargetType` that cannot be copied and/or moved efficiently, or when there - * are multiple output arguments. - * - * `wrapIn()` and `convertTo()` functions will take output arguments before - * input arguments. Some of these functions might return a value to indicate - * success or error. - * - * In converting or wrapping something as a Treble type that contains a - * `hidl_handle`, `native_handle_t*` will need to be created and returned as - * an additional output argument, hence only `wrapIn()` or `convertTo()` would - * be available. The caller must call `native_handle_delete()` to deallocate the - * returned native handle when it is no longer needed. - * - * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do - * not perform duplication of file descriptors, while `toTargetType()` and - * `convertTo()` do. - */ - -/** - * \brief Convert `Return` to `status_t`. This is for legacy binder calls. - * - * \param[in] t The source `Return`. - * \return The corresponding `status_t`. - */ -// convert: Return -> status_t -status_t toStatusT(Return const& t) { - return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); -} - -/** - * \brief Convert `Return` to `binder::Status`. - * - * \param[in] t The source `Return`. - * \return The corresponding `binder::Status`. - */ -// convert: Return -> ::android::binder::Status -::android::binder::Status toBinderStatus( - Return const& t) { - return ::android::binder::Status::fromExceptionCode( - toStatusT(t), - t.description().c_str()); -} - -/** - * \brief Wrap `native_handle_t*` in `hidl_handle`. - * - * \param[in] nh The source `native_handle_t*`. - * \return The `hidl_handle` that points to \p nh. - */ -// wrap: native_handle_t* -> hidl_handle -hidl_handle inHidlHandle(native_handle_t const* nh) { - return hidl_handle(nh); -} - -/** - * \brief Convert `int32_t` to `Dataspace`. - * - * \param[in] l The source `int32_t`. - * \result The corresponding `Dataspace`. - */ -// convert: int32_t -> Dataspace -Dataspace toHardwareDataspace(int32_t l) { - return static_cast(l); -} - -/** - * \brief Convert `Dataspace` to `int32_t`. - * - * \param[in] t The source `Dataspace`. - * \result The corresponding `int32_t`. - */ -// convert: Dataspace -> int32_t -int32_t toRawDataspace(Dataspace const& t) { - return static_cast(t); -} - -/** - * \brief Wrap an opaque buffer inside a `hidl_vec`. - * - * \param[in] l The pointer to the beginning of the opaque buffer. - * \param[in] size The size of the buffer. - * \return A `hidl_vec` that points to the buffer. - */ -// wrap: void*, size_t -> hidl_vec -hidl_vec inHidlBytes(void const* l, size_t size) { - hidl_vec t; - t.setToExternal(static_cast(const_cast(l)), size, false); - return t; -} - -/** - * \brief Create a `hidl_vec` that is a copy of an opaque buffer. - * - * \param[in] l The pointer to the beginning of the opaque buffer. - * \param[in] size The size of the buffer. - * \return A `hidl_vec` that is a copy of the input buffer. - */ -// convert: void*, size_t -> hidl_vec -hidl_vec toHidlBytes(void const* l, size_t size) { - hidl_vec t; - t.resize(size); - uint8_t const* src = static_cast(l); - std::copy(src, src + size, t.data()); - return t; -} - -/** - * \brief Wrap `GraphicBuffer` in `AnwBuffer`. - * - * \param[out] t The wrapper of type `AnwBuffer`. - * \param[in] l The source `GraphicBuffer`. - */ -// wrap: GraphicBuffer -> AnwBuffer -void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { - t->attr.width = l.getWidth(); - t->attr.height = l.getHeight(); - t->attr.stride = l.getStride(); - t->attr.format = static_cast(l.getPixelFormat()); - t->attr.layerCount = l.getLayerCount(); - t->attr.usage = l.getUsage(); - t->attr.id = l.getId(); - t->attr.generationNumber = l.getGenerationNumber(); - t->nativeHandle = hidl_handle(l.handle); -} - -/** - * \brief Convert `AnwBuffer` to `GraphicBuffer`. - * - * \param[out] l The destination `GraphicBuffer`. - * \param[in] t The source `AnwBuffer`. - * - * This function will duplicate all file descriptors in \p t. - */ -// convert: AnwBuffer -> GraphicBuffer -// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten -bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { - native_handle_t* handle = t.nativeHandle == nullptr ? - nullptr : native_handle_clone(t.nativeHandle); - - size_t const numInts = 12 + (handle ? handle->numInts : 0); - int32_t* ints = new int32_t[numInts]; - - size_t numFds = static_cast(handle ? handle->numFds : 0); - int* fds = new int[numFds]; - - ints[0] = 'GBFR'; - ints[1] = static_cast(t.attr.width); - ints[2] = static_cast(t.attr.height); - ints[3] = static_cast(t.attr.stride); - ints[4] = static_cast(t.attr.format); - ints[5] = static_cast(t.attr.layerCount); - ints[6] = static_cast(t.attr.usage); - ints[7] = static_cast(t.attr.id >> 32); - ints[8] = static_cast(t.attr.id & 0xFFFFFFFF); - ints[9] = static_cast(t.attr.generationNumber); - ints[10] = 0; - ints[11] = 0; - if (handle) { - ints[10] = static_cast(handle->numFds); - ints[11] = static_cast(handle->numInts); - int* intsStart = handle->data + handle->numFds; - std::copy(handle->data, intsStart, fds); - std::copy(intsStart, intsStart + handle->numInts, &ints[12]); - } - - void const* constBuffer = static_cast(ints); - size_t size = numInts * sizeof(int32_t); - int const* constFds = static_cast(fds); - status_t status = l->unflatten(constBuffer, size, constFds, numFds); - - delete [] fds; - delete [] ints; - native_handle_delete(handle); - return status == NO_ERROR; -} - -/** - * Conversion functions for types outside media - * ============================================ - * - * Some objects in libui and libgui that were made to go through binder calls do - * not expose ways to read or write their fields to the public. To pass an - * object of this kind through the HIDL boundary, translation functions need to - * work around the access restriction by using the publicly available - * `flatten()` and `unflatten()` functions. - * - * All `flatten()` and `unflatten()` overloads follow the same convention as - * follows: - * - * status_t flatten(ObjectType const& object, - * [OtherType const& other, ...] - * void*& buffer, size_t& size, - * int*& fds, size_t& numFds) - * - * status_t unflatten(ObjectType* object, - * [OtherType* other, ...,] - * void*& buffer, size_t& size, - * int*& fds, size_t& numFds) - * - * The number of `other` parameters varies depending on the `ObjectType`. For - * example, in the process of unflattening an object that contains - * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will - * be created. - * - * The last four parameters always work the same way in all overloads of - * `flatten()` and `unflatten()`: - * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled, - * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`, - * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the - * size (in ints) of the fd buffer pointed to by `fds`. - * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read - * from, `size` is the size (in bytes) of the non-fd buffer pointed to by - * `buffer`, `fds` is the pointer to the fd buffer to be read from, and - * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`. - * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds` - * will be advanced, while `size` and `numFds` will be decreased to reflect - * how much storage/data of the two buffers (fd and non-fd) have been used. - * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and - * `numFds` are invalid. - * - * The return value of a successful `flatten()` or `unflatten()` call will be - * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure. - * - * For each object type that supports flattening, there will be two accompanying - * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will - * return the size of the non-fd buffer that the object will need for - * flattening. `getFdCount()` will return the size of the fd buffer that the - * object will need for flattening. - * - * The set of these four functions, `getFlattenedSize()`, `getFdCount()`, - * `flatten()` and `unflatten()`, are similar to functions of the same name in - * the abstract class `Flattenable`. The only difference is that functions in - * this file are not member functions of the object type. For example, we write - * - * flatten(x, buffer, size, fds, numFds) - * - * instead of - * - * x.flatten(buffer, size, fds, numFds) - * - * because we cannot modify the type of `x`. - * - * There is one exception to the naming convention: `hidl_handle` that - * represents a fence. The four functions for this "Fence" type have the word - * "Fence" attched to their names because the object type, which is - * `hidl_handle`, does not carry the special meaning that the object itself can - * only contain zero or one file descriptor. - */ - -// Ref: frameworks/native/libs/ui/Fence.cpp - -/** - * \brief Return the size of the non-fd buffer required to flatten a fence. - * - * \param[in] fence The input fence of type `hidl_handle`. - * \return The required size of the flat buffer. - * - * The current version of this function always returns 4, which is the number of - * bytes required to store the number of file descriptors contained in the fd - * part of the flat buffer. - */ -size_t getFenceFlattenedSize(hidl_handle const& /* fence */) { - return 4; -}; - -/** - * \brief Return the number of file descriptors contained in a fence. - * - * \param[in] fence The input fence of type `hidl_handle`. - * \return `0` if \p fence does not contain a valid file descriptor, or `1` - * otherwise. - */ -size_t getFenceFdCount(hidl_handle const& fence) { - return native_handle_read_fd(fence) == -1 ? 0 : 1; -} - -/** - * \brief Unflatten `Fence` to `hidl_handle`. - * - * \param[out] fence The destination `hidl_handle`. - * \param[out] nh The underlying native handle. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will point to a newly created - * native handle, which needs to be deleted with `native_handle_delete()` - * afterwards. - */ -status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { - if (size < 4) { - return NO_MEMORY; - } - - uint32_t numFdsInHandle; - FlattenableUtils::read(buffer, size, numFdsInHandle); - - if (numFdsInHandle > 1) { - return BAD_VALUE; - } - - if (numFds < numFdsInHandle) { - return NO_MEMORY; - } - - if (numFdsInHandle) { - *nh = native_handle_create_from_fd(*fds); - if (*nh == nullptr) { - return NO_MEMORY; - } - *fence = *nh; - ++fds; - --numFds; - } else { - *nh = nullptr; - *fence = hidl_handle(); - } - - return NO_ERROR; -} - -/** - * \brief Flatten `hidl_handle` as `Fence`. - * - * \param[in] t The source `hidl_handle`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t flattenFence(hidl_handle const& fence, - void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (size < getFenceFlattenedSize(fence) || - numFds < getFenceFdCount(fence)) { - return NO_MEMORY; - } - // Cast to uint32_t since the size of a size_t can vary between 32- and - // 64-bit processes - FlattenableUtils::write(buffer, size, - static_cast(getFenceFdCount(fence))); - int fd = native_handle_read_fd(fence); - if (fd != -1) { - *fds = fd; - ++fds; - --numFds; - } - return NO_ERROR; -} - -/** - * \brief Wrap `Fence` in `hidl_handle`. - * - * \param[out] t The wrapper of type `hidl_handle`. - * \param[out] nh The native handle pointed to by \p t. - * \param[in] l The source `Fence`. - * - * On success, \p nh will hold a newly created native handle, which must be - * deleted manually with `native_handle_delete()` afterwards. - */ -// wrap: Fence -> hidl_handle -bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) { - size_t const baseSize = l.getFlattenedSize(); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - size_t const baseNumFds = l.getFdCount(); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = static_cast(baseFds.get()); - size_t numFds = baseNumFds; - if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (unflattenFence(t, nh, constBuffer, size, constFds, numFds) - != NO_ERROR) { - return false; - } - - return true; -} - -/** - * \brief Convert `hidl_handle` to `Fence`. - * - * \param[out] l The destination `Fence`. `l` must not have been used - * (`l->isValid()` must return `false`) before this function is called. - * \param[in] t The source `hidl_handle`. - * - * If \p t contains a valid file descriptor, it will be duplicated. - */ -// convert: hidl_handle -> Fence -bool convertTo(Fence* l, hidl_handle const& t) { - int fd = native_handle_read_fd(t); - if (fd != -1) { - fd = dup(fd); - if (fd == -1) { - return false; - } - } - native_handle_t* nh = native_handle_create_from_fd(fd); - if (nh == nullptr) { - if (fd != -1) { - close(fd); - } - return false; - } - - size_t const baseSize = getFenceFlattenedSize(t); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - native_handle_delete(nh); - return false; - } - - size_t const baseNumFds = getFenceFdCount(t); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - native_handle_delete(nh); - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = static_cast(baseFds.get()); - size_t numFds = baseNumFds; - if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) { - native_handle_delete(nh); - return false; - } - native_handle_delete(nh); - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { - return false; - } - - return true; -} - -// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `FenceTimeSnapshot`. - * - * \param[in] t The input `FenceTimeSnapshot`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize( - HGraphicBufferProducer::FenceTimeSnapshot const& t) { - constexpr size_t min = sizeof(t.state); - switch (t.state) { - case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: - return min; - case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: - return min + getFenceFlattenedSize(t.fence); - case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: - return min + sizeof( - ::android::FenceTime::Snapshot::signalTime); - } - return 0; -} - -/** - * \brief Return the number of file descriptors contained in - * `FenceTimeSnapshot`. - * - * \param[in] t The input `FenceTimeSnapshot`. - * \return The number of file descriptors contained in \p snapshot. - */ -size_t getFdCount( - HGraphicBufferProducer::FenceTimeSnapshot const& t) { - return t.state == - HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ? - getFenceFdCount(t.fence) : 0; -} - -/** - * \brief Flatten `FenceTimeSnapshot`. - * - * \param[in] t The source `FenceTimeSnapshot`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate the file descriptor in `t.fence` if `t.state == - * FENCE`. - */ -status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, - void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (size < getFlattenedSize(t)) { - return NO_MEMORY; - } - - switch (t.state) { - case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: - FlattenableUtils::write(buffer, size, - ::android::FenceTime::Snapshot::State::EMPTY); - return NO_ERROR; - case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: - FlattenableUtils::write(buffer, size, - ::android::FenceTime::Snapshot::State::FENCE); - return flattenFence(t.fence, buffer, size, fds, numFds); - case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: - FlattenableUtils::write(buffer, size, - ::android::FenceTime::Snapshot::State::SIGNAL_TIME); - FlattenableUtils::write(buffer, size, t.signalTimeNs); - return NO_ERROR; - } - return NO_ERROR; -} - -/** - * \brief Unflatten `FenceTimeSnapshot`. - * - * \param[out] t The destination `FenceTimeSnapshot`. - * \param[out] nh The underlying native handle. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR` and the constructed snapshot contains a - * file descriptor, \p nh will be created to hold that file descriptor. In this - * case, \p nh needs to be deleted with `native_handle_delete()` afterwards. - */ -status_t unflatten( - HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { - if (size < sizeof(t->state)) { - return NO_MEMORY; - } - - *nh = nullptr; - ::android::FenceTime::Snapshot::State state; - FlattenableUtils::read(buffer, size, state); - switch (state) { - case ::android::FenceTime::Snapshot::State::EMPTY: - t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY; - return NO_ERROR; - case ::android::FenceTime::Snapshot::State::FENCE: - t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE; - return unflattenFence(&t->fence, nh, buffer, size, fds, numFds); - case ::android::FenceTime::Snapshot::State::SIGNAL_TIME: - t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME; - if (size < sizeof(t->signalTimeNs)) { - return NO_MEMORY; - } - FlattenableUtils::read(buffer, size, t->signalTimeNs); - return NO_ERROR; - } - return NO_ERROR; -} - -// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta - -/** - * \brief Return a lower bound on the size of the non-fd buffer required to - * flatten `FrameEventsDelta`. - * - * \param[in] t The input `FrameEventsDelta`. - * \return A lower bound on the size of the flat buffer. - */ -constexpr size_t minFlattenedSize( - HGraphicBufferProducer::FrameEventsDelta const& /* t */) { - return sizeof(uint64_t) + // mFrameNumber - sizeof(uint8_t) + // mIndex - sizeof(uint8_t) + // mAddPostCompositeCalled - sizeof(uint8_t) + // mAddRetireCalled - sizeof(uint8_t) + // mAddReleaseCalled - sizeof(nsecs_t) + // mPostedTime - sizeof(nsecs_t) + // mRequestedPresentTime - sizeof(nsecs_t) + // mLatchTime - sizeof(nsecs_t) + // mFirstRefreshStartTime - sizeof(nsecs_t); // mLastRefreshStartTime -} - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `FrameEventsDelta`. - * - * \param[in] t The input `FrameEventsDelta`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize( - HGraphicBufferProducer::FrameEventsDelta const& t) { - return minFlattenedSize(t) + - getFlattenedSize(t.gpuCompositionDoneFence) + - getFlattenedSize(t.displayPresentFence) + - getFlattenedSize(t.displayRetireFence) + - getFlattenedSize(t.releaseFence); -}; - -/** - * \brief Return the number of file descriptors contained in - * `FrameEventsDelta`. - * - * \param[in] t The input `FrameEventsDelta`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount( - HGraphicBufferProducer::FrameEventsDelta const& t) { - return getFdCount(t.gpuCompositionDoneFence) + - getFdCount(t.displayPresentFence) + - getFdCount(t.displayRetireFence) + - getFdCount(t.releaseFence); -}; - -/** - * \brief Unflatten `FrameEventsDelta`. - * - * \param[out] t The destination `FrameEventsDelta`. - * \param[out] nh The underlying array of native handles. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be - * populated with `nullptr` or newly created handles. Each non-null slot in \p - * nh will need to be deleted manually with `native_handle_delete()`. - */ -status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, - std::vector* nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { - if (size < minFlattenedSize(*t)) { - return NO_MEMORY; - } - FlattenableUtils::read(buffer, size, t->frameNumber); - - // These were written as uint8_t for alignment. - uint8_t temp = 0; - FlattenableUtils::read(buffer, size, temp); - size_t index = static_cast(temp); - if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) { - return BAD_VALUE; - } - t->index = static_cast(index); - - FlattenableUtils::read(buffer, size, temp); - t->addPostCompositeCalled = static_cast(temp); - FlattenableUtils::read(buffer, size, temp); - t->addRetireCalled = static_cast(temp); - FlattenableUtils::read(buffer, size, temp); - t->addReleaseCalled = static_cast(temp); - - FlattenableUtils::read(buffer, size, t->postedTimeNs); - FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs); - FlattenableUtils::read(buffer, size, t->latchTimeNs); - FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs); - FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs); - FlattenableUtils::read(buffer, size, t->dequeueReadyTime); - - // Fences - HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4]; - tSnapshot[0] = &t->gpuCompositionDoneFence; - tSnapshot[1] = &t->displayPresentFence; - tSnapshot[2] = &t->displayRetireFence; - tSnapshot[3] = &t->releaseFence; - nh->resize(4); - for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { - status_t status = unflatten( - tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]), - buffer, size, fds, numFds); - if (status != NO_ERROR) { - while (snapshotIndex > 0) { - --snapshotIndex; - if ((*nh)[snapshotIndex] != nullptr) { - native_handle_delete((*nh)[snapshotIndex]); - } - } - return status; - } - } - return NO_ERROR; -} - -/** - * \brief Flatten `FrameEventsDelta`. - * - * \param[in] t The source `FrameEventsDelta`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate file descriptors contained in \p t. - */ -// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: -// FrameEventsDelta::flatten -status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, - void*& buffer, size_t& size, int*& fds, size_t numFds) { - // Check that t.index is within a valid range. - if (t.index >= static_cast(FrameEventHistory::MAX_FRAME_HISTORY) - || t.index > std::numeric_limits::max()) { - return BAD_VALUE; - } - - FlattenableUtils::write(buffer, size, t.frameNumber); - - // These are static_cast to uint8_t for alignment. - FlattenableUtils::write(buffer, size, static_cast(t.index)); - FlattenableUtils::write( - buffer, size, static_cast(t.addPostCompositeCalled)); - FlattenableUtils::write( - buffer, size, static_cast(t.addRetireCalled)); - FlattenableUtils::write( - buffer, size, static_cast(t.addReleaseCalled)); - - FlattenableUtils::write(buffer, size, t.postedTimeNs); - FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs); - FlattenableUtils::write(buffer, size, t.latchTimeNs); - FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs); - FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs); - FlattenableUtils::write(buffer, size, t.dequeueReadyTime); - - // Fences - HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4]; - tSnapshot[0] = &t.gpuCompositionDoneFence; - tSnapshot[1] = &t.displayPresentFence; - tSnapshot[2] = &t.displayRetireFence; - tSnapshot[3] = &t.releaseFence; - for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { - status_t status = flatten( - *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds); - if (status != NO_ERROR) { - return status; - } - } - return NO_ERROR; -} - -// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize( - HGraphicBufferProducer::FrameEventHistoryDelta const& t) { - size_t size = 4 + // mDeltas.size() - sizeof(t.compositorTiming); - for (size_t i = 0; i < t.deltas.size(); ++i) { - size += getFlattenedSize(t.deltas[i]); - } - return size; -} - -/** - * \brief Return the number of file descriptors contained in - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount( - HGraphicBufferProducer::FrameEventHistoryDelta const& t) { - size_t numFds = 0; - for (size_t i = 0; i < t.deltas.size(); ++i) { - numFds += getFdCount(t.deltas[i]); - } - return numFds; -} - -/** - * \brief Unflatten `FrameEventHistoryDelta`. - * - * \param[out] t The destination `FrameEventHistoryDelta`. - * \param[out] nh The underlying array of arrays of native handles. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or - * newly created handles. The second dimension of \p nh will be 4. Each non-null - * slot in \p nh will need to be deleted manually with `native_handle_delete()`. - */ -status_t unflatten( - HGraphicBufferProducer::FrameEventHistoryDelta* t, - std::vector >* nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { - if (size < 4) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, t->compositorTiming); - - uint32_t deltaCount = 0; - FlattenableUtils::read(buffer, size, deltaCount); - if (static_cast(deltaCount) > - ::android::FrameEventHistory::MAX_FRAME_HISTORY) { - return BAD_VALUE; - } - t->deltas.resize(deltaCount); - nh->resize(deltaCount); - for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) { - status_t status = unflatten( - &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]), - buffer, size, fds, numFds); - if (status != NO_ERROR) { - return status; - } - } - return NO_ERROR; -} - -/** - * \brief Flatten `FrameEventHistoryDelta`. - * - * \param[in] t The source `FrameEventHistoryDelta`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate file descriptors contained in \p t. - */ -status_t flatten( - HGraphicBufferProducer::FrameEventHistoryDelta const& t, - void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { - return BAD_VALUE; - } - if (size < getFlattenedSize(t)) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, t.compositorTiming); - - FlattenableUtils::write(buffer, size, static_cast(t.deltas.size())); - for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) { - status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds); - if (status != NO_ERROR) { - return status; - } - } - return NO_ERROR; -} - -/** - * \brief Wrap `::android::FrameEventHistoryData` in - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \param[out] nh The array of array of native handles that are referred to by - * members of \p t. - * \param[in] l The source `::android::FrameEventHistoryDelta`. - * - * On success, each member of \p nh will be either `nullptr` or a newly created - * native handle. All the non-`nullptr` elements must be deleted individually - * with `native_handle_delete()`. - */ -bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t, - std::vector >* nh, - ::android::FrameEventHistoryDelta const& l) { - - size_t const baseSize = l.getFlattenedSize(); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - size_t const baseNumFds = l.getFdCount(); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = baseFds.get(); - size_t numFds = baseNumFds; - if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { - return false; - } - - return true; -} - -/** - * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to - * `::android::FrameEventHistoryDelta`. - * - * \param[out] l The destination `::android::FrameEventHistoryDelta`. - * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * This function will duplicate all file descriptors contained in \p t. - */ -bool convertTo( - ::android::FrameEventHistoryDelta* l, - HGraphicBufferProducer::FrameEventHistoryDelta const& t) { - - size_t const baseSize = getFlattenedSize(t); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - size_t const baseNumFds = getFdCount(t); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = static_cast(baseFds.get()); - size_t numFds = baseNumFds; - if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { - return false; - } - - return true; -} - -// Ref: frameworks/native/libs/ui/Region.cpp - -/** - * \brief Return the size of the buffer required to flatten `Region`. - * - * \param[in] t The input `Region`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(Region const& t) { - return sizeof(uint32_t) + t.size() * sizeof(::android::Rect); -} - -/** - * \brief Unflatten `Region`. - * - * \param[out] t The destination `Region`. - * \param[in,out] buffer The pointer to the flat buffer. - * \param[in,out] size The size of the flat buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t unflatten(Region* t, void const*& buffer, size_t& size) { - if (size < sizeof(uint32_t)) { - return NO_MEMORY; - } - - uint32_t numRects = 0; - FlattenableUtils::read(buffer, size, numRects); - if (size < numRects * sizeof(Rect)) { - return NO_MEMORY; - } - if (numRects > (UINT32_MAX / sizeof(Rect))) { - return NO_MEMORY; - } - - t->resize(numRects); - for (size_t r = 0; r < numRects; ++r) { - ::android::Rect rect(::android::Rect::EMPTY_RECT); - status_t status = rect.unflatten(buffer, size); - if (status != NO_ERROR) { - return status; - } - FlattenableUtils::advance(buffer, size, sizeof(rect)); - (*t)[r] = Rect{ - static_cast(rect.left), - static_cast(rect.top), - static_cast(rect.right), - static_cast(rect.bottom)}; - } - return NO_ERROR; -} - -/** - * \brief Flatten `Region`. - * - * \param[in] t The source `Region`. - * \param[in,out] buffer The pointer to the flat buffer. - * \param[in,out] size The size of the flat buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t flatten(Region const& t, void*& buffer, size_t& size) { - if (size < getFlattenedSize(t)) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, static_cast(t.size())); - for (size_t r = 0; r < t.size(); ++r) { - ::android::Rect rect( - static_cast(t[r].left), - static_cast(t[r].top), - static_cast(t[r].right), - static_cast(t[r].bottom)); - status_t status = rect.flatten(buffer, size); - if (status != NO_ERROR) { - return status; - } - FlattenableUtils::advance(buffer, size, sizeof(rect)); - } - return NO_ERROR; -} - -/** - * \brief Convert `::android::Region` to `Region`. - * - * \param[out] t The destination `Region`. - * \param[in] l The source `::android::Region`. - */ -// convert: ::android::Region -> Region -bool convertTo(Region* t, ::android::Region const& l) { - size_t const baseSize = l.getFlattenedSize(); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - if (l.flatten(buffer, size) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - if (unflatten(t, constBuffer, size) != NO_ERROR) { - return false; - } - - return true; -} - -/** - * \brief Convert `Region` to `::android::Region`. - * - * \param[out] l The destination `::android::Region`. - * \param[in] t The source `Region`. - */ -// convert: Region -> ::android::Region -bool convertTo(::android::Region* l, Region const& t) { - size_t const baseSize = getFlattenedSize(t); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - if (flatten(t, buffer, size) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - if (l->unflatten(constBuffer, size) != NO_ERROR) { - return false; - } - - return true; -} - -// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: -// BGraphicBufferProducer::QueueBufferInput - -/** - * \brief Return a lower bound on the size of the buffer required to flatten - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. - * \return A lower bound on the size of the flat buffer. - */ -constexpr size_t minFlattenedSize( - HGraphicBufferProducer::QueueBufferInput const& /* t */) { - return sizeof(int64_t) + // timestamp - sizeof(int) + // isAutoTimestamp - sizeof(android_dataspace) + // dataSpace - sizeof(::android::Rect) + // crop - sizeof(int) + // scalingMode - sizeof(uint32_t) + // transform - sizeof(uint32_t) + // stickyTransform - sizeof(bool); // getFrameTimestamps -} - -/** - * \brief Return the size of the buffer required to flatten - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) { - return minFlattenedSize(t) + - getFenceFlattenedSize(t.fence) + - getFlattenedSize(t.surfaceDamage) + - sizeof(HdrMetadata::validTypes); -} - -/** - * \brief Return the number of file descriptors contained in - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount( - HGraphicBufferProducer::QueueBufferInput const& t) { - return getFenceFdCount(t.fence); -} - -/** - * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The native handle cloned from `t.fence`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate the file descriptor in `t.fence`. */ -status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t, - native_handle_t** nh, - void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (size < getFlattenedSize(t)) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, t.timestamp); - FlattenableUtils::write(buffer, size, static_cast(t.isAutoTimestamp)); - FlattenableUtils::write(buffer, size, - static_cast(t.dataSpace)); - FlattenableUtils::write(buffer, size, ::android::Rect( - static_cast(t.crop.left), - static_cast(t.crop.top), - static_cast(t.crop.right), - static_cast(t.crop.bottom))); - FlattenableUtils::write(buffer, size, static_cast(t.scalingMode)); - FlattenableUtils::write(buffer, size, t.transform); - FlattenableUtils::write(buffer, size, t.stickyTransform); - FlattenableUtils::write(buffer, size, t.getFrameTimestamps); - - *nh = t.fence.getNativeHandle() == nullptr ? - nullptr : native_handle_clone(t.fence); - status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds); - if (status != NO_ERROR) { - return status; - } - status = flatten(t.surfaceDamage, buffer, size); - if (status != NO_ERROR) { - return status; - } - FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0)); - return NO_ERROR; -} - -/** - * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The underlying native handle for `t->fence`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR` and `t->fence` contains a valid file - * descriptor, \p nh will be a newly created native handle holding that file - * descriptor. \p nh needs to be deleted with `native_handle_delete()` - * afterwards. - */ -status_t unflatten( - HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { - if (size < minFlattenedSize(*t)) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, t->timestamp); - int lIsAutoTimestamp; - FlattenableUtils::read(buffer, size, lIsAutoTimestamp); - t->isAutoTimestamp = static_cast(lIsAutoTimestamp); - android_dataspace_t lDataSpace; - FlattenableUtils::read(buffer, size, lDataSpace); - t->dataSpace = static_cast(lDataSpace); - Rect lCrop; - FlattenableUtils::read(buffer, size, lCrop); - t->crop = Rect{ - static_cast(lCrop.left), - static_cast(lCrop.top), - static_cast(lCrop.right), - static_cast(lCrop.bottom)}; - int lScalingMode; - FlattenableUtils::read(buffer, size, lScalingMode); - t->scalingMode = static_cast(lScalingMode); - FlattenableUtils::read(buffer, size, t->transform); - FlattenableUtils::read(buffer, size, t->stickyTransform); - FlattenableUtils::read(buffer, size, t->getFrameTimestamps); - - status_t status = unflattenFence(&(t->fence), nh, - buffer, size, fds, numFds); - if (status != NO_ERROR) { - return status; - } - // HdrMetadata ignored - return unflatten(&(t->surfaceDamage), buffer, size); -} - -/** - * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The underlying native handle for `t->fence`. - * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`. - * - * If the return value is `true` and `t->fence` contains a valid file - * descriptor, \p nh will be a newly created native handle holding that file - * descriptor. \p nh needs to be deleted with `native_handle_delete()` - * afterwards. - */ -bool wrapAs( - HGraphicBufferProducer::QueueBufferInput* t, - native_handle_t** nh, - BGraphicBufferProducer::QueueBufferInput const& l) { - - size_t const baseSize = l.getFlattenedSize(); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - size_t const baseNumFds = l.getFdCount(); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = baseFds.get(); - size_t numFds = baseNumFds; - if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { - return false; - } - - return true; -} - -/** - * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to - * `BGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`. - * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. - * - * If `t.fence` has a valid file descriptor, it will be duplicated. - */ -bool convertTo( - BGraphicBufferProducer::QueueBufferInput* l, - HGraphicBufferProducer::QueueBufferInput const& t) { - - size_t const baseSize = getFlattenedSize(t); - std::unique_ptr baseBuffer( - new (std::nothrow) uint8_t[baseSize]); - if (!baseBuffer) { - return false; - } - - size_t const baseNumFds = getFdCount(t); - std::unique_ptr baseFds( - new (std::nothrow) int[baseNumFds]); - if (!baseFds) { - return false; - } - - void* buffer = static_cast(baseBuffer.get()); - size_t size = baseSize; - int* fds = baseFds.get(); - size_t numFds = baseNumFds; - native_handle_t* nh; - if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) { - return false; - } - - void const* constBuffer = static_cast(baseBuffer.get()); - size = baseSize; - int const* constFds = static_cast(baseFds.get()); - numFds = baseNumFds; - if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { - if (nh != nullptr) { - native_handle_close(nh); - native_handle_delete(nh); - } - return false; - } - - native_handle_delete(nh); - return true; -} - -// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: -// BGraphicBufferProducer::QueueBufferOutput - -/** - * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in - * `HGraphicBufferProducer::QueueBufferOutput`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::QueueBufferOutput`. - * \param[out] nh The array of array of native handles that are referred to by - * members of \p t. - * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`. - * - * On success, each member of \p nh will be either `nullptr` or a newly created - * native handle. All the non-`nullptr` elements must be deleted individually - * with `native_handle_delete()`. - */ -// wrap: BGraphicBufferProducer::QueueBufferOutput -> -// HGraphicBufferProducer::QueueBufferOutput -bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t, - std::vector >* nh, - BGraphicBufferProducer::QueueBufferOutput const& l) { - if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) { - return false; - } - t->width = l.width; - t->height = l.height; - t->transformHint = l.transformHint; - t->numPendingBuffers = l.numPendingBuffers; - t->nextFrameNumber = l.nextFrameNumber; - t->bufferReplaced = l.bufferReplaced; - return true; -} - -/** - * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to - * `BGraphicBufferProducer::QueueBufferOutput`. - * - * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`. - * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. - * - * This function will duplicate all file descriptors contained in \p t. - */ -// convert: HGraphicBufferProducer::QueueBufferOutput -> -// BGraphicBufferProducer::QueueBufferOutput -bool convertTo( - BGraphicBufferProducer::QueueBufferOutput* l, - HGraphicBufferProducer::QueueBufferOutput const& t) { - if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) { - return false; - } - l->width = t.width; - l->height = t.height; - l->transformHint = t.transformHint; - l->numPendingBuffers = t.numPendingBuffers; - l->nextFrameNumber = t.nextFrameNumber; - l->bufferReplaced = t.bufferReplaced; - return true; -} - -/** - * \brief Convert `BGraphicBufferProducer::DisconnectMode` to - * `HGraphicBufferProducer::DisconnectMode`. - * - * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`. - * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. - */ -HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode( - BGraphicBufferProducer::DisconnectMode l) { - switch (l) { - case BGraphicBufferProducer::DisconnectMode::Api: - return HGraphicBufferProducer::DisconnectMode::API; - case BGraphicBufferProducer::DisconnectMode::AllLocal: - return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL; - } - return HGraphicBufferProducer::DisconnectMode::API; -} - -/** - * \brief Convert `HGraphicBufferProducer::DisconnectMode` to - * `BGraphicBufferProducer::DisconnectMode`. - * - * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`. - * \return The corresponding `BGraphicBufferProducer::DisconnectMode`. - */ -BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode( - HGraphicBufferProducer::DisconnectMode t) { - switch (t) { - case HGraphicBufferProducer::DisconnectMode::API: - return BGraphicBufferProducer::DisconnectMode::Api; - case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL: - return BGraphicBufferProducer::DisconnectMode::AllLocal; - } - return BGraphicBufferProducer::DisconnectMode::Api; -} - -} // namespace conversion -} // namespace android - diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp index 68abcb50cf..59317e74d4 100644 --- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp +++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp @@ -32,7 +32,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include @@ -389,6 +393,18 @@ GraphicBufferSource::~GraphicBufferSource() { } } +sp GraphicBufferSource::getIGraphicBufferProducer() const { + return mProducer; +} + +sp<::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer> +GraphicBufferSource::getHGraphicBufferProducer_V1_0() const { + using TWGraphicBufferProducer = ::android::TWGraphicBufferProducer< + ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer>; + + return new TWGraphicBufferProducer(getIGraphicBufferProducer()); +} + sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer> GraphicBufferSource::getHGraphicBufferProducer() const { return new ::android::hardware::graphics::bufferqueue::V2_0::utils:: diff --git a/media/libstagefright/bqhelper/WProducerListener.cpp b/media/libstagefright/bqhelper/WProducerListener.cpp deleted file mode 100644 index 2ca13bebd1..0000000000 --- a/media/libstagefright/bqhelper/WProducerListener.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -namespace android { - -// TWProducerListener -TWProducerListener::TWProducerListener( - sp const& base): - mBase(base) { -} - -Return TWProducerListener::onBufferReleased() { - mBase->onBufferReleased(); - return Void(); -} - -Return TWProducerListener::needsReleaseNotify() { - return mBase->needsReleaseNotify(); -} - -// LWProducerListener -LWProducerListener::LWProducerListener( - sp const& base): - mBase(base) { -} - -void LWProducerListener::onBufferReleased() { - mBase->onBufferReleased(); -} - -bool LWProducerListener::needsReleaseNotify() { - return static_cast(mBase->needsReleaseNotify()); -} - -} // namespace android diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h deleted file mode 100644 index 60d8c1e43a..0000000000 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h +++ /dev/null @@ -1,769 +0,0 @@ -/* - * Copyright 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_ -#define MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_ - -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace android { -namespace conversion { - -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_handle; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::sp; -using ::android::status_t; - -using ::android::String8; - -using ::android::hardware::media::V1_0::Rect; -using ::android::hardware::media::V1_0::Region; - -using ::android::hardware::graphics::common::V1_0::Dataspace; - -using ::android::hardware::graphics::common::V1_0::PixelFormat; - -using ::android::hardware::media::V1_0::AnwBuffer; -using ::android::GraphicBuffer; - -typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer - HGraphicBufferProducer; -typedef ::android::IGraphicBufferProducer - BGraphicBufferProducer; - -// native_handle_t helper functions. - -/** - * \brief Take an fd and create a native handle containing only the given fd. - * The created handle will need to be deleted manually with - * `native_handle_delete()`. - * - * \param[in] fd The source file descriptor (of type `int`). - * \return The create `native_handle_t*` that contains the given \p fd. If the - * supplied \p fd is negative, the created native handle will contain no file - * descriptors. - * - * If the native handle cannot be created, the return value will be - * `nullptr`. - * - * This function does not duplicate the file descriptor. - */ -native_handle_t* native_handle_create_from_fd(int fd); - -/** - * \brief Extract a file descriptor from a native handle. - * - * \param[in] nh The source `native_handle_t*`. - * \param[in] index The index of the file descriptor in \p nh to read from. This - * input has the default value of `0`. - * \return The `index`-th file descriptor in \p nh. If \p nh does not have - * enough file descriptors, the returned value will be `-1`. - * - * This function does not duplicate the file descriptor. - */ -int native_handle_read_fd(native_handle_t const* nh, int index = 0); - -/** - * Conversion functions - * ==================== - * - * There are two main directions of conversion: - * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the - * input. The wrapper has type `TargetType`. - * - `toTargetType(...)`: Create a standalone object of type `TargetType` that - * corresponds to the input. The lifetime of the output does not depend on the - * lifetime of the input. - * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType` - * that cannot be copied and/or moved efficiently, or when there are multiple - * output arguments. - * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for - * `TargetType` that cannot be copied and/or moved efficiently, or when there - * are multiple output arguments. - * - * `wrapIn()` and `convertTo()` functions will take output arguments before - * input arguments. Some of these functions might return a value to indicate - * success or error. - * - * In converting or wrapping something as a Treble type that contains a - * `hidl_handle`, `native_handle_t*` will need to be created and returned as - * an additional output argument, hence only `wrapIn()` or `convertTo()` would - * be available. The caller must call `native_handle_delete()` to deallocate the - * returned native handle when it is no longer needed. - * - * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do - * not perform duplication of file descriptors, while `toTargetType()` and - * `convertTo()` do. - */ - -/** - * \brief Convert `Return` to `binder::Status`. - * - * \param[in] t The source `Return`. - * \return The corresponding `binder::Status`. - */ -// convert: Return -> ::android::binder::Status -::android::binder::Status toBinderStatus(Return const& t); - -/** - * \brief Convert `Return` to `status_t`. This is for legacy binder calls. - * - * \param[in] t The source `Return`. - * \return The corresponding `status_t`. - */ -// convert: Return -> status_t -status_t toStatusT(Return const& t); - -/** - * \brief Wrap `native_handle_t*` in `hidl_handle`. - * - * \param[in] nh The source `native_handle_t*`. - * \return The `hidl_handle` that points to \p nh. - */ -// wrap: native_handle_t* -> hidl_handle -hidl_handle inHidlHandle(native_handle_t const* nh); - -/** - * \brief Convert `int32_t` to `Dataspace`. - * - * \param[in] l The source `int32_t`. - * \result The corresponding `Dataspace`. - */ -// convert: int32_t -> Dataspace -Dataspace toHardwareDataspace(int32_t l); - -/** - * \brief Convert `Dataspace` to `int32_t`. - * - * \param[in] t The source `Dataspace`. - * \result The corresponding `int32_t`. - */ -// convert: Dataspace -> int32_t -int32_t toRawDataspace(Dataspace const& t); - -/** - * \brief Wrap an opaque buffer inside a `hidl_vec`. - * - * \param[in] l The pointer to the beginning of the opaque buffer. - * \param[in] size The size of the buffer. - * \return A `hidl_vec` that points to the buffer. - */ -// wrap: void*, size_t -> hidl_vec -hidl_vec inHidlBytes(void const* l, size_t size); - -/** - * \brief Create a `hidl_vec` that is a copy of an opaque buffer. - * - * \param[in] l The pointer to the beginning of the opaque buffer. - * \param[in] size The size of the buffer. - * \return A `hidl_vec` that is a copy of the input buffer. - */ -// convert: void*, size_t -> hidl_vec -hidl_vec toHidlBytes(void const* l, size_t size); - -/** - * \brief Wrap `GraphicBuffer` in `AnwBuffer`. - * - * \param[out] t The wrapper of type `AnwBuffer`. - * \param[in] l The source `GraphicBuffer`. - */ -// wrap: GraphicBuffer -> AnwBuffer -void wrapAs(AnwBuffer* t, GraphicBuffer const& l); - -/** - * \brief Convert `AnwBuffer` to `GraphicBuffer`. - * - * \param[out] l The destination `GraphicBuffer`. - * \param[in] t The source `AnwBuffer`. - * - * This function will duplicate all file descriptors in \p t. - */ -// convert: AnwBuffer -> GraphicBuffer -// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten -bool convertTo(GraphicBuffer* l, AnwBuffer const& t); - -/** - * Conversion functions for types outside media - * ============================================ - * - * Some objects in libui and libgui that were made to go through binder calls do - * not expose ways to read or write their fields to the public. To pass an - * object of this kind through the HIDL boundary, translation functions need to - * work around the access restriction by using the publicly available - * `flatten()` and `unflatten()` functions. - * - * All `flatten()` and `unflatten()` overloads follow the same convention as - * follows: - * - * status_t flatten(ObjectType const& object, - * [OtherType const& other, ...] - * void*& buffer, size_t& size, - * int*& fds, size_t& numFds) - * - * status_t unflatten(ObjectType* object, - * [OtherType* other, ...,] - * void*& buffer, size_t& size, - * int*& fds, size_t& numFds) - * - * The number of `other` parameters varies depending on the `ObjectType`. For - * example, in the process of unflattening an object that contains - * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will - * be created. - * - * The last four parameters always work the same way in all overloads of - * `flatten()` and `unflatten()`: - * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled, - * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`, - * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the - * size (in ints) of the fd buffer pointed to by `fds`. - * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read - * from, `size` is the size (in bytes) of the non-fd buffer pointed to by - * `buffer`, `fds` is the pointer to the fd buffer to be read from, and - * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`. - * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds` - * will be advanced, while `size` and `numFds` will be decreased to reflect - * how much storage/data of the two buffers (fd and non-fd) have been used. - * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and - * `numFds` are invalid. - * - * The return value of a successful `flatten()` or `unflatten()` call will be - * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure. - * - * For each object type that supports flattening, there will be two accompanying - * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will - * return the size of the non-fd buffer that the object will need for - * flattening. `getFdCount()` will return the size of the fd buffer that the - * object will need for flattening. - * - * The set of these four functions, `getFlattenedSize()`, `getFdCount()`, - * `flatten()` and `unflatten()`, are similar to functions of the same name in - * the abstract class `Flattenable`. The only difference is that functions in - * this file are not member functions of the object type. For example, we write - * - * flatten(x, buffer, size, fds, numFds) - * - * instead of - * - * x.flatten(buffer, size, fds, numFds) - * - * because we cannot modify the type of `x`. - * - * There is one exception to the naming convention: `hidl_handle` that - * represents a fence. The four functions for this "Fence" type have the word - * "Fence" attched to their names because the object type, which is - * `hidl_handle`, does not carry the special meaning that the object itself can - * only contain zero or one file descriptor. - */ - -// Ref: frameworks/native/libs/ui/Fence.cpp - -/** - * \brief Return the size of the non-fd buffer required to flatten a fence. - * - * \param[in] fence The input fence of type `hidl_handle`. - * \return The required size of the flat buffer. - * - * The current version of this function always returns 4, which is the number of - * bytes required to store the number of file descriptors contained in the fd - * part of the flat buffer. - */ -size_t getFenceFlattenedSize(hidl_handle const& fence); - -/** - * \brief Return the number of file descriptors contained in a fence. - * - * \param[in] fence The input fence of type `hidl_handle`. - * \return `0` if \p fence does not contain a valid file descriptor, or `1` - * otherwise. - */ -size_t getFenceFdCount(hidl_handle const& fence); - -/** - * \brief Unflatten `Fence` to `hidl_handle`. - * - * \param[out] fence The destination `hidl_handle`. - * \param[out] nh The underlying native handle. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will point to a newly created - * native handle, which needs to be deleted with `native_handle_delete()` - * afterwards. - */ -status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds); - -/** - * \brief Flatten `hidl_handle` as `Fence`. - * - * \param[in] t The source `hidl_handle`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t flattenFence(hidl_handle const& fence, - void*& buffer, size_t& size, int*& fds, size_t& numFds); - -/** - * \brief Wrap `Fence` in `hidl_handle`. - * - * \param[out] t The wrapper of type `hidl_handle`. - * \param[out] nh The native handle pointed to by \p t. - * \param[in] l The source `Fence`. - * - * On success, \p nh will hold a newly created native handle, which must be - * deleted manually with `native_handle_delete()` afterwards. - */ -// wrap: Fence -> hidl_handle -bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l); - -/** - * \brief Convert `hidl_handle` to `Fence`. - * - * \param[out] l The destination `Fence`. `l` must not have been used - * (`l->isValid()` must return `false`) before this function is called. - * \param[in] t The source `hidl_handle`. - * - * If \p t contains a valid file descriptor, it will be duplicated. - */ -// convert: hidl_handle -> Fence -bool convertTo(Fence* l, hidl_handle const& t); - -// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `FenceTimeSnapshot`. - * - * \param[in] t The input `FenceTimeSnapshot`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(HGraphicBufferProducer::FenceTimeSnapshot const& t); - -/** - * \brief Return the number of file descriptors contained in - * `FenceTimeSnapshot`. - * - * \param[in] t The input `FenceTimeSnapshot`. - * \return The number of file descriptors contained in \p snapshot. - */ -size_t getFdCount(HGraphicBufferProducer::FenceTimeSnapshot const& t); - -/** - * \brief Flatten `FenceTimeSnapshot`. - * - * \param[in] t The source `FenceTimeSnapshot`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate the file descriptor in `t.fence` if `t.state == - * FENCE`. - */ -status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, - void*& buffer, size_t& size, int*& fds, size_t& numFds); - -/** - * \brief Unflatten `FenceTimeSnapshot`. - * - * \param[out] t The destination `FenceTimeSnapshot`. - * \param[out] nh The underlying native handle. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR` and the constructed snapshot contains a - * file descriptor, \p nh will be created to hold that file descriptor. In this - * case, \p nh needs to be deleted with `native_handle_delete()` afterwards. - */ -status_t unflatten( - HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds); - -// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `FrameEventsDelta`. - * - * \param[in] t The input `FrameEventsDelta`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(HGraphicBufferProducer::FrameEventsDelta const& t); - -/** - * \brief Return the number of file descriptors contained in - * `FrameEventsDelta`. - * - * \param[in] t The input `FrameEventsDelta`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount(HGraphicBufferProducer::FrameEventsDelta const& t); - -/** - * \brief Unflatten `FrameEventsDelta`. - * - * \param[out] t The destination `FrameEventsDelta`. - * \param[out] nh The underlying array of native handles. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be - * populated with `nullptr` or newly created handles. Each non-null slot in \p - * nh will need to be deleted manually with `native_handle_delete()`. - */ -status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, - std::vector* nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds); - -/** - * \brief Flatten `FrameEventsDelta`. - * - * \param[in] t The source `FrameEventsDelta`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate file descriptors contained in \p t. - */ -// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: -// FrameEventsDelta::flatten -status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, - void*& buffer, size_t& size, int*& fds, size_t numFds); - -// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta - -/** - * \brief Return the size of the non-fd buffer required to flatten - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize( - HGraphicBufferProducer::FrameEventHistoryDelta const& t); - -/** - * \brief Return the number of file descriptors contained in - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount( - HGraphicBufferProducer::FrameEventHistoryDelta const& t); - -/** - * \brief Unflatten `FrameEventHistoryDelta`. - * - * \param[out] t The destination `FrameEventHistoryDelta`. - * \param[out] nh The underlying array of arrays of native handles. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or - * newly created handles. The second dimension of \p nh will be 4. Each non-null - * slot in \p nh will need to be deleted manually with `native_handle_delete()`. - */ -status_t unflatten( - HGraphicBufferProducer::FrameEventHistoryDelta* t, - std::vector >* nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds); - -/** - * \brief Flatten `FrameEventHistoryDelta`. - * - * \param[in] t The source `FrameEventHistoryDelta`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate file descriptors contained in \p t. - */ -status_t flatten( - HGraphicBufferProducer::FrameEventHistoryDelta const& t, - void*& buffer, size_t& size, int*& fds, size_t& numFds); - -/** - * \brief Wrap `::android::FrameEventHistoryData` in - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::FrameEventHistoryDelta`. - * \param[out] nh The array of array of native handles that are referred to by - * members of \p t. - * \param[in] l The source `::android::FrameEventHistoryDelta`. - * - * On success, each member of \p nh will be either `nullptr` or a newly created - * native handle. All the non-`nullptr` elements must be deleted individually - * with `native_handle_delete()`. - */ -bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t, - std::vector >* nh, - ::android::FrameEventHistoryDelta const& l); - -/** - * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to - * `::android::FrameEventHistoryDelta`. - * - * \param[out] l The destination `::android::FrameEventHistoryDelta`. - * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. - * - * This function will duplicate all file descriptors contained in \p t. - */ -bool convertTo( - ::android::FrameEventHistoryDelta* l, - HGraphicBufferProducer::FrameEventHistoryDelta const& t); - -// Ref: frameworks/native/libs/ui/Region.cpp - -/** - * \brief Return the size of the buffer required to flatten `Region`. - * - * \param[in] t The input `Region`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(Region const& t); - -/** - * \brief Unflatten `Region`. - * - * \param[out] t The destination `Region`. - * \param[in,out] buffer The pointer to the flat buffer. - * \param[in,out] size The size of the flat buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t unflatten(Region* t, void const*& buffer, size_t& size); - -/** - * \brief Flatten `Region`. - * - * \param[in] t The source `Region`. - * \param[in,out] buffer The pointer to the flat buffer. - * \param[in,out] size The size of the flat buffer. - * \return `NO_ERROR` on success; other value on failure. - */ -status_t flatten(Region const& t, void*& buffer, size_t& size); - -/** - * \brief Convert `::android::Region` to `Region`. - * - * \param[out] t The destination `Region`. - * \param[in] l The source `::android::Region`. - */ -// convert: ::android::Region -> Region -bool convertTo(Region* t, ::android::Region const& l); - -/** - * \brief Convert `Region` to `::android::Region`. - * - * \param[out] l The destination `::android::Region`. - * \param[in] t The source `Region`. - */ -// convert: Region -> ::android::Region -bool convertTo(::android::Region* l, Region const& t); - -// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: -// BGraphicBufferProducer::QueueBufferInput - -/** - * \brief Return the size of the buffer required to flatten - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. - * \return The required size of the flat buffer. - */ -size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t); - -/** - * \brief Return the number of file descriptors contained in - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. - * \return The number of file descriptors contained in \p t. - */ -size_t getFdCount( - HGraphicBufferProducer::QueueBufferInput const& t); -/** - * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The native handle cloned from `t.fence`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * This function will duplicate the file descriptor in `t.fence`. */ -status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t, - native_handle_t** nh, - void*& buffer, size_t& size, int*& fds, size_t& numFds); - -/** - * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The underlying native handle for `t->fence`. - * \param[in,out] buffer The pointer to the flat non-fd buffer. - * \param[in,out] size The size of the flat non-fd buffer. - * \param[in,out] fds The pointer to the flat fd buffer. - * \param[in,out] numFds The size of the flat fd buffer. - * \return `NO_ERROR` on success; other value on failure. - * - * If the return value is `NO_ERROR` and `t->fence` contains a valid file - * descriptor, \p nh will be a newly created native handle holding that file - * descriptor. \p nh needs to be deleted with `native_handle_delete()` - * afterwards. - */ -status_t unflatten( - HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, - void const*& buffer, size_t& size, int const*& fds, size_t& numFds); - -/** - * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in - * `HGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::QueueBufferInput`. - * \param[out] nh The underlying native handle for `t->fence`. - * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`. - * - * If the return value is `true` and `t->fence` contains a valid file - * descriptor, \p nh will be a newly created native handle holding that file - * descriptor. \p nh needs to be deleted with `native_handle_delete()` - * afterwards. - */ -bool wrapAs( - HGraphicBufferProducer::QueueBufferInput* t, - native_handle_t** nh, - BGraphicBufferProducer::QueueBufferInput const& l); - -/** - * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to - * `BGraphicBufferProducer::QueueBufferInput`. - * - * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`. - * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. - * - * If `t.fence` has a valid file descriptor, it will be duplicated. - */ -bool convertTo( - BGraphicBufferProducer::QueueBufferInput* l, - HGraphicBufferProducer::QueueBufferInput const& t); - -// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: -// BGraphicBufferProducer::QueueBufferOutput - -/** - * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in - * `HGraphicBufferProducer::QueueBufferOutput`. - * - * \param[out] t The wrapper of type - * `HGraphicBufferProducer::QueueBufferOutput`. - * \param[out] nh The array of array of native handles that are referred to by - * members of \p t. - * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`. - * - * On success, each member of \p nh will be either `nullptr` or a newly created - * native handle. All the non-`nullptr` elements must be deleted individually - * with `native_handle_delete()`. - */ -// wrap: BGraphicBufferProducer::QueueBufferOutput -> -// HGraphicBufferProducer::QueueBufferOutput -bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t, - std::vector >* nh, - BGraphicBufferProducer::QueueBufferOutput const& l); - -/** - * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to - * `BGraphicBufferProducer::QueueBufferOutput`. - * - * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`. - * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. - * - * This function will duplicate all file descriptors contained in \p t. - */ -// convert: HGraphicBufferProducer::QueueBufferOutput -> -// BGraphicBufferProducer::QueueBufferOutput -bool convertTo( - BGraphicBufferProducer::QueueBufferOutput* l, - HGraphicBufferProducer::QueueBufferOutput const& t); - -/** - * \brief Convert `BGraphicBufferProducer::DisconnectMode` to - * `HGraphicBufferProducer::DisconnectMode`. - * - * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`. - * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. - */ -HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode( - BGraphicBufferProducer::DisconnectMode l); - -/** - * \brief Convert `HGraphicBufferProducer::DisconnectMode` to - * `BGraphicBufferProducer::DisconnectMode`. - * - * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`. - * \return The corresponding `BGraphicBufferProducer::DisconnectMode`. - */ -BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode( - HGraphicBufferProducer::DisconnectMode t); - -} // namespace conversion -} // namespace android - -#endif // MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_ diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h index bf329b9006..b4c4d4a9c1 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h @@ -19,8 +19,6 @@ #define GRAPHIC_BUFFER_SOURCE_H_ #include -#include -#include #include #include @@ -28,13 +26,17 @@ #include #include #include +#include +#include namespace android { using ::android::binder::Status; struct FrameDropper; - +class BufferItem; +class IGraphicBufferProducer; +class IGraphicBufferConsumer; /* * This class is used to feed codecs from a Surface via BufferQueue or * HW producer. @@ -82,9 +84,12 @@ public: // Returns the handle to the producer side of the BufferQueue. Buffers // queued on this will be received by GraphicBufferSource. - sp getIGraphicBufferProducer() const { - return mProducer; - } + sp getIGraphicBufferProducer() const; + + // Returns the handle to the bufferqueue HAL (V1_0) producer side of the BufferQueue. + // Buffers queued on this will be received by GraphicBufferSource. + sp<::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer> + getHGraphicBufferProducer_V1_0() const; // Returns the handle to the bufferqueue HAL producer side of the BufferQueue. // Buffers queued on this will be received by GraphicBufferSource. diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h deleted file mode 100644 index 8ddf20f942..0000000000 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_ -#define MEDIA_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -namespace android { - -using ::android::hardware::media::V1_0::AnwBuffer; -using ::android::hidl::base::V1_0::IBase; -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_handle; -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::sp; - -typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IGraphicBufferProducer HGraphicBufferProducer; -typedef ::android::hardware::graphics::bufferqueue::V1_0:: - IProducerListener HProducerListener; - -typedef ::android::IGraphicBufferProducer BGraphicBufferProducer; -typedef ::android::IProducerListener BProducerListener; -using ::android::BnGraphicBufferProducer; - -#ifndef LOG -struct LOG_dummy { - template - LOG_dummy& operator<< (const T&) { return *this; } -}; - -#define LOG(x) LOG_dummy() -#endif - -// Instantiate only if HGraphicBufferProducer is base of BASE. -template ::value>::type> -struct TWGraphicBufferProducer : public BASE { - TWGraphicBufferProducer(sp const& base) : mBase(base) {} - Return requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override { - sp buf; - status_t status = mBase->requestBuffer(slot, &buf); - AnwBuffer anwBuffer; - if (buf != nullptr) { - ::android::conversion::wrapAs(&anwBuffer, *buf); - } - _hidl_cb(static_cast(status), anwBuffer); - return Void(); - } - - Return setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) override { - return static_cast(mBase->setMaxDequeuedBufferCount( - static_cast(maxDequeuedBuffers))); - } - - Return setAsyncMode(bool async) override { - return static_cast(mBase->setAsyncMode(async)); - } - - Return dequeueBuffer( - uint32_t width, uint32_t height, - ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage, - bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override { - int slot; - sp fence; - ::android::FrameEventHistoryDelta outTimestamps; - status_t status = mBase->dequeueBuffer( - &slot, &fence, width, height, - static_cast<::android::PixelFormat>(format), usage, nullptr, - getFrameTimestamps ? &outTimestamps : nullptr); - hidl_handle tFence; - HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps; - - native_handle_t* nh = nullptr; - if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) { - LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - " - "Invalid output fence"; - _hidl_cb(static_cast(status), - static_cast(slot), - tFence, - tOutTimestamps); - return Void(); - } - std::vector > nhAA; - if (getFrameTimestamps && !::android::conversion::wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) { - LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - " - "Invalid output timestamps"; - _hidl_cb(static_cast(status), - static_cast(slot), - tFence, - tOutTimestamps); - native_handle_delete(nh); - return Void(); - } - - _hidl_cb(static_cast(status), - static_cast(slot), - tFence, - tOutTimestamps); - native_handle_delete(nh); - if (getFrameTimestamps) { - for (auto& nhA : nhAA) { - for (auto& handle : nhA) { - native_handle_delete(handle); - } - } - } - return Void(); - } - - Return detachBuffer(int32_t slot) override { - return static_cast(mBase->detachBuffer(slot)); - } - - Return detachNextBuffer(HGraphicBufferProducer::detachNextBuffer_cb _hidl_cb) override { - sp outBuffer; - sp outFence; - status_t status = mBase->detachNextBuffer(&outBuffer, &outFence); - AnwBuffer tBuffer; - hidl_handle tFence; - - if (outBuffer == nullptr) { - LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " - "Invalid output buffer"; - _hidl_cb(static_cast(status), tBuffer, tFence); - return Void(); - } - ::android::conversion::wrapAs(&tBuffer, *outBuffer); - native_handle_t* nh = nullptr; - if ((outFence != nullptr) && !::android::conversion::wrapAs(&tFence, &nh, *outFence)) { - LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " - "Invalid output fence"; - _hidl_cb(static_cast(status), tBuffer, tFence); - return Void(); - } - - _hidl_cb(static_cast(status), tBuffer, tFence); - native_handle_delete(nh); - return Void(); - } - - Return attachBuffer(const AnwBuffer& buffer, HGraphicBufferProducer::attachBuffer_cb _hidl_cb) override { - int outSlot; - sp lBuffer = new GraphicBuffer(); - if (!::android::conversion::convertTo(lBuffer.get(), buffer)) { - LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - " - "Invalid input native window buffer"; - _hidl_cb(static_cast(BAD_VALUE), -1); - return Void(); - } - status_t status = mBase->attachBuffer(&outSlot, lBuffer); - - _hidl_cb(static_cast(status), static_cast(outSlot)); - return Void(); - } - - Return queueBuffer( - int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input, - HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override { - HGraphicBufferProducer::QueueBufferOutput tOutput; - BGraphicBufferProducer::QueueBufferInput lInput( - 0, false, HAL_DATASPACE_UNKNOWN, - ::android::Rect(0, 0, 1, 1), - NATIVE_WINDOW_SCALING_MODE_FREEZE, - 0, ::android::Fence::NO_FENCE); - if (!::android::conversion::convertTo(&lInput, input)) { - LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - " - "Invalid input"; - _hidl_cb(static_cast(BAD_VALUE), tOutput); - return Void(); - } - BGraphicBufferProducer::QueueBufferOutput lOutput; - status_t status = mBase->queueBuffer( - static_cast(slot), lInput, &lOutput); - - std::vector > nhAA; - if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) { - LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - " - "Invalid output"; - _hidl_cb(static_cast(BAD_VALUE), tOutput); - return Void(); - } - - _hidl_cb(static_cast(status), tOutput); - for (auto& nhA : nhAA) { - for (auto& nh : nhA) { - native_handle_delete(nh); - } - } - return Void(); - } - - Return cancelBuffer(int32_t slot, const hidl_handle& fence) override { - sp lFence = new Fence(); - if (!::android::conversion::convertTo(lFence.get(), fence)) { - LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - " - "Invalid input fence"; - return static_cast(BAD_VALUE); - } - return static_cast(mBase->cancelBuffer(static_cast(slot), lFence)); - } - - Return query(int32_t what, HGraphicBufferProducer::query_cb _hidl_cb) override { - int lValue; - int lReturn = mBase->query(static_cast(what), &lValue); - _hidl_cb(static_cast(lReturn), static_cast(lValue)); - return Void(); - } - - Return connect(const sp& listener, - int32_t api, bool producerControlledByApp, - HGraphicBufferProducer::connect_cb _hidl_cb) override { - sp lListener = listener == nullptr ? - nullptr : new LWProducerListener(listener); - BGraphicBufferProducer::QueueBufferOutput lOutput; - status_t status = mBase->connect(lListener, - static_cast(api), - producerControlledByApp, - &lOutput); - - HGraphicBufferProducer::QueueBufferOutput tOutput; - std::vector > nhAA; - if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) { - LOG(ERROR) << "TWGraphicBufferProducer::connect - " - "Invalid output"; - _hidl_cb(static_cast(status), tOutput); - return Void(); - } - - _hidl_cb(static_cast(status), tOutput); - for (auto& nhA : nhAA) { - for (auto& nh : nhA) { - native_handle_delete(nh); - } - } - return Void(); - } - - Return disconnect( - int32_t api, - HGraphicBufferProducer::DisconnectMode mode) override { - return static_cast(mBase->disconnect( - static_cast(api), - ::android::conversion::toGuiDisconnectMode(mode))); - } - - Return setSidebandStream(const hidl_handle& stream) override { - return static_cast(mBase->setSidebandStream(NativeHandle::create( - stream ? native_handle_clone(stream) : NULL, true))); - } - - Return allocateBuffers( - uint32_t width, uint32_t height, - ::android::hardware::graphics::common::V1_0::PixelFormat format, - uint32_t usage) override { - mBase->allocateBuffers( - width, height, - static_cast<::android::PixelFormat>(format), - usage); - return Void(); - } - - Return allowAllocation(bool allow) override { - return static_cast(mBase->allowAllocation(allow)); - } - - Return setGenerationNumber(uint32_t generationNumber) override { - return static_cast(mBase->setGenerationNumber(generationNumber)); - } - - Return getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override { - _hidl_cb(mBase->getConsumerName().string()); - return Void(); - } - - Return setSharedBufferMode(bool sharedBufferMode) override { - return static_cast(mBase->setSharedBufferMode(sharedBufferMode)); - } - - Return setAutoRefresh(bool autoRefresh) override { - return static_cast(mBase->setAutoRefresh(autoRefresh)); - } - - Return setDequeueTimeout(int64_t timeoutNs) override { - return static_cast(mBase->setDequeueTimeout(timeoutNs)); - } - - Return getLastQueuedBuffer(HGraphicBufferProducer::getLastQueuedBuffer_cb _hidl_cb) override { - sp lOutBuffer = new GraphicBuffer(); - sp lOutFence = new Fence(); - float lOutTransformMatrix[16]; - status_t status = mBase->getLastQueuedBuffer( - &lOutBuffer, &lOutFence, lOutTransformMatrix); - - AnwBuffer tOutBuffer; - if (lOutBuffer != nullptr) { - ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer); - } - hidl_handle tOutFence; - native_handle_t* nh = nullptr; - if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) { - LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - " - "Invalid output fence"; - _hidl_cb(static_cast(status), - tOutBuffer, - tOutFence, - hidl_array()); - return Void(); - } - hidl_array tOutTransformMatrix(lOutTransformMatrix); - - _hidl_cb(static_cast(status), tOutBuffer, tOutFence, tOutTransformMatrix); - native_handle_delete(nh); - return Void(); - } - - Return getFrameTimestamps(HGraphicBufferProducer::getFrameTimestamps_cb _hidl_cb) override { - ::android::FrameEventHistoryDelta lDelta; - mBase->getFrameTimestamps(&lDelta); - - HGraphicBufferProducer::FrameEventHistoryDelta tDelta; - std::vector > nhAA; - if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) { - LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - " - "Invalid output frame timestamps"; - _hidl_cb(tDelta); - return Void(); - } - - _hidl_cb(tDelta); - for (auto& nhA : nhAA) { - for (auto& nh : nhA) { - native_handle_delete(nh); - } - } - return Void(); - } - - Return getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override { - uint64_t outId; - status_t status = mBase->getUniqueId(&outId); - _hidl_cb(static_cast(status), outId); - return Void(); - } - -private: - sp mBase; -}; - -} // namespace android - -#endif // MEDIA_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_ diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h deleted file mode 100644 index febba87407..0000000000 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_ -#define MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_ - -#include -#include - -#include -#include - -#include - -namespace android { - -using ::android::hidl::base::V1_0::IBase; -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::sp; - -typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener - HProducerListener; -typedef ::android::IProducerListener - BProducerListener; -using ::android::BnProducerListener; - -struct TWProducerListener : public HProducerListener { - sp mBase; - TWProducerListener(sp const& base); - Return onBufferReleased() override; - Return needsReleaseNotify() override; -}; - -class LWProducerListener : public BnProducerListener { -public: - sp mBase; - LWProducerListener(sp const& base); - void onBufferReleased() override; - bool needsReleaseNotify() override; -}; - -} // namespace android - -#endif // MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_ diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp index 121bb1a7be..bb79f54801 100644 --- a/media/libstagefright/omx/1.0/Omx.cpp +++ b/media/libstagefright/omx/1.0/Omx.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -28,7 +27,6 @@ #include #include -#include #include #include #include @@ -146,8 +144,6 @@ Return Omx::allocateNode( } Return Omx::createInputSurface(createInputSurface_cb _hidl_cb) { - sp<::android::IGraphicBufferProducer> bufferProducer; - sp graphicBufferSource = new OmxGraphicBufferSource(); status_t err = graphicBufferSource->initCheck(); if (err != OK) { @@ -157,10 +153,9 @@ Return Omx::createInputSurface(createInputSurface_cb _hidl_cb) { _hidl_cb(toStatus(err), nullptr, nullptr); return Void(); } - bufferProducer = graphicBufferSource->getIGraphicBufferProducer(); _hidl_cb(toStatus(OK), - new TWGraphicBufferProducer(bufferProducer), + graphicBufferSource->getHGraphicBufferProducer_V1_0(), new TWGraphicBufferSource(graphicBufferSource)); return Void(); } diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index d7aacff520..ddb4ba0bc3 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -33,12 +33,13 @@ #include #include -#include #include #include #include #include #include +#include +#include #include #include #include diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h index 0f229f7545..96696779d7 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H -#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H +#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_IMPL_CONVERSION_H +#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_IMPL_CONVERSION_H #include #include @@ -29,15 +29,15 @@ #include #include +#include #include #include -#include #include #include +#include #include #include -#include #include #include @@ -99,16 +99,9 @@ using ::android::IOMXBufferSource; typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer HGraphicBufferProducer; -typedef ::android::IGraphicBufferProducer - BGraphicBufferProducer; -// We want to use all functions declared in ::android::conversion -using namespace ::android::conversion; - -// Now specifically inject these two functions here, because we're going to -// declare functions with the same name in this namespace. -using ::android::conversion::convertTo; -using ::android::conversion::toStatusT; +// Use the conversion functions from libmedia_omx so that we don't need libgui +using namespace ::android::hardware::media::omx::V1_0::utils; /** * Conversion functions @@ -142,62 +135,6 @@ using ::android::conversion::toStatusT; * `convertTo()` do. */ -/** - * \brief Convert `Status` to `status_t`. This is for legacy binder calls. - * - * \param[in] t The source `Status`. - * \return the corresponding `status_t`. - */ -// convert: Status -> status_t -inline status_t toStatusT(Status const& t) { - switch (t) { - case Status::NO_ERROR: - case Status::NAME_NOT_FOUND: - case Status::WOULD_BLOCK: - case Status::NO_MEMORY: - case Status::ALREADY_EXISTS: - case Status::NO_INIT: - case Status::BAD_VALUE: - case Status::DEAD_OBJECT: - case Status::INVALID_OPERATION: - case Status::TIMED_OUT: - case Status::ERROR_UNSUPPORTED: - case Status::UNKNOWN_ERROR: - case Status::RELEASE_ALL_BUFFERS: - return static_cast(t); - case Status::BUFFER_NEEDS_REALLOCATION: - return NOT_ENOUGH_DATA; - default: - ALOGW("Unrecognized status value: %" PRId32, static_cast(t)); - return static_cast(t); - } -} - -/** - * \brief Convert `Return` to `status_t`. This is for legacy binder - * calls. - * - * \param[in] t The source `Return`. - * \return The corresponding `status_t`. - * - * This function first check if \p t has a transport error. If it does, then the - * return value is the transport error code. Otherwise, the return value is - * converted from `Status` contained inside \p t. - * - * Note: - * - This `Status` is omx-specific. It is defined in `types.hal`. - * - The name of this function is not `convert`. - */ -// convert: Status -> status_t -inline status_t toStatusT(Return const& t) { - if (t.isOk()) { - return toStatusT(static_cast(t)); - } else if (t.isDeadObject()) { - return DEAD_OBJECT; - } - return UNKNOWN_ERROR; -} - /** * \brief Convert `status_t` to `Status`. * @@ -219,8 +156,8 @@ inline Status toStatus(status_t l) { case TIMED_OUT: case ERROR_UNSUPPORTED: case UNKNOWN_ERROR: - case IGraphicBufferProducer::RELEASE_ALL_BUFFERS: - case IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION: + case BufferQueueDefs::RELEASE_ALL_BUFFERS: + case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION: return static_cast(l); case NOT_ENOUGH_DATA: return Status::BUFFER_NEEDS_REALLOCATION; @@ -572,7 +509,8 @@ inline bool convertTo(OMXBuffer* l, CodecBuffer const& t) { anwBuffer.nativeHandle = t.nativeHandle; anwBuffer.attr = t.attr.anwBuffer; sp graphicBuffer = new GraphicBuffer(); - if (!convertTo(graphicBuffer.get(), anwBuffer)) { + if (!::android::hardware::media::omx::V1_0::utils::convertTo( + graphicBuffer.get(), anwBuffer)) { return false; } *l = OMXBuffer(graphicBuffer); @@ -756,4 +694,4 @@ inline OMX_TICKS toOMXTicks(uint64_t t) { } // namespace hardware } // namespace android -#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__CONVERSION_H +#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_IMPL_CONVERSION_H diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h deleted file mode 100644 index 322a699554..0000000000 --- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERPRODUCER_H -#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERPRODUCER_H - -#include - -namespace android { -namespace hardware { -namespace media { -namespace omx { -namespace V1_0 { -namespace implementation { - -using TWGraphicBufferProducer = ::android::TWGraphicBufferProducer< - ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer>; - -} // namespace implementation -} // namespace V1_0 -} // namespace omx -} // namespace media -} // namespace hardware -} // namespace android - -#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WOMXBUFFERPRODUCER_H -- GitLab From d1c60298edff98fb613476f50fd88776a396bb9d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 30 Apr 2019 13:39:39 -0700 Subject: [PATCH 1352/1530] add crash dump policies to mediaswcodec-arm.policy bug: 131328009 bug: 129426284 test: test on 32bit Android Go device and see mediaswcodec starts on boot Change-Id: I21c2b3d228dfa4916bbdbe4f899024f69fd0c77e --- .../seccomp_policy/mediaswcodec-arm.policy | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy index 02cedba8f0..f75515abfc 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy @@ -61,3 +61,24 @@ exit_group: 1 restart_syscall: 1 rt_sigreturn: 1 getrandom: 1 +madvise: 1 + +# crash dump policy additions +sigreturn: 1 +clock_gettime: 1 +futex: 1 +getpid: 1 +gettid: 1 +pipe2: 1 +recvmsg: 1 +process_vm_readv: 1 +tgkill: 1 +rt_sigaction: 1 +rt_tgsigqueueinfo: 1 +#prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 +#mprotect: arg2 in 0x1|0x2 +#mmap2: arg2 in 0x1|0x2 +geteuid32: 1 +getgid32: 1 +getegid32: 1 +getgroups32: 1 -- GitLab From 983e8ca3a2f12de9b272743efb836148db77f7e2 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 12 Apr 2019 15:12:05 -0700 Subject: [PATCH 1353/1530] SoftHevcDec: notify error for fatal errors in decoder Bug: 129796771 Bug: 130578045 Test: adb shell stagefright -s -S /sdcard/crowd422_hevc.mp4 Test: Ensure decoder doesn't attemp decoding all frames Test: atest android.media.cts.DecoderTest Test: android.media.cts.AdaptivePlaybackTest Change-Id: Icb183d9aaa4280416c468e71cf319dacacf3b2ea --- media/libstagefright/codecs/hevcdec/SoftHEVC.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp index bb7d361bae..f6ae1f4f47 100644 --- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp @@ -569,7 +569,7 @@ void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); bool unsupportedResolution = - (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); + (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); /* Check for unsupported dimensions */ if (unsupportedResolution) { @@ -579,7 +579,8 @@ void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { return; } - bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & 0xFF)); + bool allocationFailed = + (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); if (allocationFailed) { ALOGE("Allocation failure in decoder"); notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); @@ -587,7 +588,14 @@ void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { return; } - bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); + if (IS_IVD_FATAL_ERROR(s_dec_op.u4_error_code)) { + ALOGE("Fatal Error : 0x%x", s_dec_op.u4_error_code); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); getVUIParams(); -- GitLab From 7d1d572c683d2d74af9bbe217e2093d4303ae7bc Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 12 Apr 2019 15:13:26 -0700 Subject: [PATCH 1354/1530] C2SoftHevcDec: return error for fatal errors in decoder Bug: 130578045 Test: adb shell stagefright -s -S /sdcard/crowd422_hevc.mp4 Test: Ensure decoder doesn't attemp decoding all frames Test: atest android.media.cts.DecoderTest Test: android.media.cts.AdaptivePlaybackTest Change-Id: I801ad18ef5ea29d1f5dc4721ff154111023bcb68 --- media/codec2/components/hevc/C2SoftHevcDec.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp index bb8dda03ed..5da59bdafc 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.cpp +++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp @@ -806,19 +806,20 @@ void C2SoftHevcDec::process( TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay, s_decode_op.u4_num_bytes_consumed); - if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & 0xFF)) { + if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGE("allocation failure in decoder"); mSignalledError = true; work->workletsProcessed = 1u; work->result = C2_CORRUPTED; return; - } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & 0xFF)) { + } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == + (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGE("unsupported resolution : %dx%d", mWidth, mHeight); mSignalledError = true; work->workletsProcessed = 1u; work->result = C2_CORRUPTED; return; - } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) { + } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGV("resolution changed"); drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work); resetDecoder(); @@ -828,6 +829,12 @@ void C2SoftHevcDec::process( /* Decode header and get new dimensions */ setParams(mStride, IVD_DECODE_HEADER); (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op); + } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) { + ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code); + mSignalledError = true; + work->workletsProcessed = 1u; + work->result = C2_CORRUPTED; + return; } if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) { if (mHeaderDecoded == false) { -- GitLab From 6e6a2e6190b63a19c8788c475269d711f7853743 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 30 Apr 2019 16:38:41 -0700 Subject: [PATCH 1355/1530] AudioFlinger: Fix haptic race condition Test: compiles Bug: 131715193 Change-Id: I57f136903b9ecc40ecd10642c0bfaad0134495f4 --- services/audioflinger/Threads.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index fd29f31bc3..5c6f353845 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3268,6 +3268,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() cpuStats.sample(myName); Vector< sp > effectChains; + audio_session_t activeHapticSessionId = AUDIO_SESSION_NONE; // If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency. // @@ -3541,6 +3542,19 @@ bool AudioFlinger::PlaybackThread::threadLoop() // during mixing and effect process as the audio buffers could be deleted // or modified if an effect is created or deleted lockEffectChains_l(effectChains); + + // Determine which session to pick up haptic data. + // This must be done under the same lock as prepareTracks_l(). + // TODO: Write haptic data directly to sink buffer when mixing. + if (mHapticChannelCount > 0 && effectChains.size() > 0) { + for (const auto& track : mActiveTracks) { + if (track->getHapticPlaybackEnabled()) { + activeHapticSessionId = track->sessionId(); + break; + } + } + } + } // mLock scope ends if (mBytesRemaining == 0) { @@ -3613,20 +3627,11 @@ bool AudioFlinger::PlaybackThread::threadLoop() // only process effects if we're going to write if (mSleepTimeUs == 0 && mType != OFFLOAD) { - audio_session_t activeHapticId = AUDIO_SESSION_NONE; - if (mHapticChannelCount > 0 && effectChains.size() > 0) { - for (auto track : mActiveTracks) { - if (track->getHapticPlaybackEnabled()) { - activeHapticId = track->sessionId(); - break; - } - } - } for (size_t i = 0; i < effectChains.size(); i ++) { effectChains[i]->process_l(); // TODO: Write haptic data directly to sink buffer when mixing. - if (activeHapticId != AUDIO_SESSION_NONE - && activeHapticId == effectChains[i]->sessionId()) { + if (activeHapticSessionId != AUDIO_SESSION_NONE + && activeHapticSessionId == effectChains[i]->sessionId()) { // Haptic data is active in this case, copy it directly from // in buffer to out buffer. const size_t audioBufferSize = mNormalFrameCount -- GitLab From 17830f092062a033d854703ca595270a430c4517 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 16 Apr 2019 11:38:34 -0700 Subject: [PATCH 1356/1530] SoftAVCDec: notify error for fatal errors in decoder Bug: 130578045 Test: adb shell stagefright -s -S /sdcard/crowd422_h264.mp4 Test: Ensure decoder doesn't attemp decoding all frames Test: atest android.media.cts.DecoderTest Test: android.media.cts.AdaptivePlaybackTest Change-Id: I98b9e3b0d8bf5828b6f7a657d8c256f564d00164 --- media/libstagefright/codecs/avcdec/SoftAVCDec.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp index bf5e2435dc..5a4b2f8764 100644 --- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp @@ -580,7 +580,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); bool unsupportedResolution = - (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); + (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); /* Check for unsupported dimensions */ if (unsupportedResolution) { @@ -590,7 +590,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { return; } - bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & 0xFF)); + bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); if (allocationFailed) { ALOGE("Allocation failure in decoder"); notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); @@ -598,7 +598,14 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) { return; } - bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); + if (IS_IVD_FATAL_ERROR(s_dec_op.u4_error_code)) { + ALOGE("Fatal Error : 0x%x", s_dec_op.u4_error_code); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); getVUIParams(); -- GitLab From 8f98eee3e2edbf6916c40886b745e8c2c962edec Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 16 Apr 2019 11:34:33 -0700 Subject: [PATCH 1357/1530] C2SoftAvcDec: return error for fatal errors in decoder Bug: 130578045 Test: adb shell stagefright -s -S /sdcard/crowd422_h264.mp4 Test: Ensure decoder doesn't attemp decoding all frames Test: atest android.media.cts.DecoderTest Test: android.media.cts.AdaptivePlaybackTest Change-Id: If4ef7f860bf39ca976fad32a51acea67d3b66740 --- media/codec2/components/avc/C2SoftAvcDec.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp index 9290d743d3..063c537dc5 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.cpp +++ b/media/codec2/components/avc/C2SoftAvcDec.cpp @@ -812,19 +812,19 @@ void C2SoftAvcDec::process( ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay, s_decode_op.u4_num_bytes_consumed); } - if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & 0xFF)) { + if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGE("allocation failure in decoder"); mSignalledError = true; work->workletsProcessed = 1u; work->result = C2_CORRUPTED; return; - } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & 0xFF)) { + } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGE("unsupported resolution : %dx%d", mWidth, mHeight); mSignalledError = true; work->workletsProcessed = 1u; work->result = C2_CORRUPTED; return; - } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) { + } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) { ALOGV("resolution changed"); drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work); resetDecoder(); @@ -834,6 +834,12 @@ void C2SoftAvcDec::process( /* Decode header and get new dimensions */ setParams(mStride, IVD_DECODE_HEADER); (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op); + } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) { + ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code); + mSignalledError = true; + work->workletsProcessed = 1u; + work->result = C2_CORRUPTED; + return; } if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) { if (mHeaderDecoded == false) { -- GitLab From b03c5db991623c9c8c8d08b10faba927d57545a4 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 30 Apr 2019 15:18:12 -0700 Subject: [PATCH 1358/1530] LiveSession: fix getBandwidthIndex mul overflow Bug: 123514963 Test: StreamingMediaPlayerTest Change-Id: Id8cdc5b3fd5c4b4f04f84c475daf5a26e20f2b86 --- media/libstagefright/httplive/LiveSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 5e7f90a510..9cf97c77b8 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1384,7 +1384,7 @@ size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) { while (index > lowestBandwidth) { // be conservative (70%) to avoid overestimating and immediately // switching down again. - size_t adjustedBandwidthBps = bandwidthBps * 7 / 10; + size_t adjustedBandwidthBps = bandwidthBps * .7f; const BandwidthItem &item = mBandwidthItems[index]; if (item.mBandwidth <= adjustedBandwidthBps && isBandwidthValid(item)) { -- GitLab From ff22462b92abfa11f9e29e1c0a92806f5f1ab55c Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 24 Apr 2019 16:58:40 -0700 Subject: [PATCH 1359/1530] Zero hidl-generated structs (bufferpool) Bug: 131356202 Test: boot Change-Id: Icc42b852795ac2f151d552dd245bf7f432a2c6b2 --- media/bufferpool/1.0/Connection.cpp | 12 ++++++++++-- media/bufferpool/2.0/Connection.cpp | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/media/bufferpool/1.0/Connection.cpp b/media/bufferpool/1.0/Connection.cpp index e58f59531e..be8970111a 100644 --- a/media/bufferpool/1.0/Connection.cpp +++ b/media/bufferpool/1.0/Connection.cpp @@ -32,14 +32,22 @@ Return Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_ status = mAccessor->fetch( mConnectionId, transactionId, bufferId, &handle); if (status == ResultStatus::OK) { - _hidl_cb(status, Buffer{bufferId, handle}); + Buffer buffer = {}; + buffer.id = bufferId; + buffer.buffer = handle; + _hidl_cb(status, buffer); return Void(); } } else { mAccessor->cleanUp(false); } } - _hidl_cb(status, Buffer{0, nullptr}); + + Buffer buffer = {}; + buffer.id = 0; + buffer.buffer = nullptr; + + _hidl_cb(status, buffer); return Void(); } diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp index 6bd0e7956f..57d0c7eb63 100644 --- a/media/bufferpool/2.0/Connection.cpp +++ b/media/bufferpool/2.0/Connection.cpp @@ -32,14 +32,22 @@ Return Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_ status = mAccessor->fetch( mConnectionId, transactionId, bufferId, &handle); if (status == ResultStatus::OK) { - _hidl_cb(status, Buffer{bufferId, handle}); + Buffer buffer = {}; + buffer.id = bufferId; + buffer.buffer = handle; + _hidl_cb(status, buffer); return Void(); } } else { mAccessor->cleanUp(false); } } - _hidl_cb(status, Buffer{0, nullptr}); + + Buffer buffer = {}; + buffer.id = 0; + buffer.buffer = nullptr; + + _hidl_cb(status, buffer); return Void(); } -- GitLab From ef7eaaf8d873e9d832673fd0a8e07371fa47a0a0 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 1 May 2019 11:26:35 -0700 Subject: [PATCH 1360/1530] aaudio: protect against null client AAudioService would fail if a null client was passed. Two null checks were added. One where we know the null client first appears. And one where the client is first used in case other calls are passing null. Bug: 116230453 Test: Bug has a POC apk that triggers the bug. Test: Look for messages like: Test: AAudio : BnAAudioService::onTransact() client is NULL! Change-Id: Id9c4fc154226ab40df97335da8bc9361cfc99a73 --- media/libaaudio/src/binding/IAAudioService.cpp | 11 +++++++++-- services/oboeservice/AAudioClientTracker.cpp | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp index 9b32543830..97ad2b02a0 100644 --- a/media/libaaudio/src/binding/IAAudioService.cpp +++ b/media/libaaudio/src/binding/IAAudioService.cpp @@ -251,8 +251,15 @@ status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data, CHECK_INTERFACE(IAAudioService, data, reply); sp client = interface_cast( data.readStrongBinder()); - registerClient(client); - return NO_ERROR; + // readStrongBinder() can return null + if (client.get() == nullptr) { + ALOGE("BnAAudioService::%s(REGISTER_CLIENT) client is NULL!", __func__); + android_errorWriteLog(0x534e4554, "116230453"); + return DEAD_OBJECT; + } else { + registerClient(client); + return NO_ERROR; + } } break; case OPEN_STREAM: { diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp index 83704ba83f..857256156e 100644 --- a/services/oboeservice/AAudioClientTracker.cpp +++ b/services/oboeservice/AAudioClientTracker.cpp @@ -67,6 +67,12 @@ aaudio_result_t AAudioClientTracker::registerClient(pid_t pid, const sp& client) { ALOGV("registerClient(), calling pid = %d, getpid() = %d\n", pid, getpid()); + if (client.get() == nullptr) { + ALOGE("AAudioClientTracker::%s() client is NULL!", __func__); + android_errorWriteLog(0x534e4554, "116230453"); + return AAUDIO_ERROR_NULL; + } + std::lock_guard lock(mLock); if (mNotificationClients.count(pid) == 0) { sp notificationClient = new NotificationClient(pid); -- GitLab From 4b2a821891b8c271ca7e1dc4a152220c923cd1e3 Mon Sep 17 00:00:00 2001 From: Oscar Azucena Date: Fri, 26 Apr 2019 23:48:59 -0700 Subject: [PATCH 1361/1530] Enabled removeUidDeviceAffinities Added code to removeUidDeviceAffinities in AudioPolicyManager.cpp Bug: 131447931 Test: ran test in android auto multizone audio and policies were correctly removed. Change-Id: I70a8452d6c4ce5812f2c3f03ddf10b96cb0bb947 --- .../managerdefault/AudioPolicyManager.cpp | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index c0ca440dc5..6f62b905a1 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3003,22 +3003,11 @@ status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid, status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) { ALOGV("%s() uid=%d", __FUNCTION__, uid); - Vector devices; - status_t res = mPolicyMixes.getDevicesForUid(uid, devices); - if (res == NO_ERROR) { - // reevaluate outputs for all found devices - for (size_t i = 0; i < devices.size(); i++) { - sp devDesc = mHwModules.getDeviceDescriptor( - devices[i].mType, devices[i].mAddress, String8(), - AUDIO_FORMAT_DEFAULT); - SortedVector outputs; - if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputs) != NO_ERROR) { - ALOGE("%s() error in checkOutputsForDevice for device=%08x addr=%s", - __FUNCTION__, devices[i].mType, devices[i].mAddress.string()); - return INVALID_OPERATION; - } - } + status_t res = mPolicyMixes.removeUidDeviceAffinities(uid); + if (res != NO_ERROR) { + ALOGE("%s() Could not remove all device affinities fo uid = %d", + __FUNCTION__, uid); + return INVALID_OPERATION; } return res; -- GitLab From c16463885b106429146b6f89fbde6ef8eb372538 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 30 Apr 2019 16:12:10 -0700 Subject: [PATCH 1362/1530] AudioFlinger: Properly account for underruns When the mixer status was MIXER_ENABLED twice in a row zeroes were forced to the audio sink. Test: aaudio test write_sine_callback -pl -n1 -r44100 -s20 and then use rapid volume changes. Bug: 131767115 Change-Id: I24b41084138210c63e8c551ae4a5f3adddb9e607 --- services/audioflinger/Threads.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index a48b7331f2..7daa9291ec 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3277,6 +3277,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() Vector< sp > effectChains; audio_session_t activeHapticSessionId = AUDIO_SESSION_NONE; + std::vector> activeTracks; // If the device is AUDIO_DEVICE_OUT_BUS, check for downstream latency. // @@ -3563,6 +3564,12 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } + // Acquire a local copy of active tracks with lock (release w/o lock). + // + // Control methods on the track acquire the ThreadBase lock (e.g. start() + // stop(), pause(), etc.), but the threadLoop is entitled to call audio + // data / buffer methods on tracks from activeTracks without the ThreadBase lock. + activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end()); } // mLock scope ends if (mBytesRemaining == 0) { @@ -3577,6 +3584,13 @@ bool AudioFlinger::PlaybackThread::threadLoop() threadLoop_sleepTime(); if (mSleepTimeUs == 0) { mCurrentWriteLength = mSinkBufferSize; + + // Tally underrun frames as we are inserting 0s here. + for (const auto& track : activeTracks) { + if (track->mFillingUpStatus == Track::FS_ACTIVE) { + track->mAudioTrackServerProxy->tallyUnderrunFrames(mNormalFrameCount); + } + } } } // Either threadLoop_mix() or threadLoop_sleepTime() should have set -- GitLab From 8bd93d3cdc95b240f54e95d344887b60bd943791 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 3 May 2019 14:58:08 +0000 Subject: [PATCH 1363/1530] Revert "Install extractors for 1st and 2nd ABIs to the test APEX" This reverts commit 784bfac0b0e32c668aefdae2281e1f1c370c9097. Reason for revert: decided to use device which matches to the test suite. Bug: 131869673, Bug: 125914810 Change-Id: I5738a5303f3368f63fcd51074ee9b2db7344d518 --- apex/Android.bp | 37 +++++++++++++++---------------- apex/testing/Android.bp | 7 ------ media/extractors/aac/Android.bp | 2 +- media/extractors/amr/Android.bp | 2 +- media/extractors/flac/Android.bp | 2 +- media/extractors/midi/Android.bp | 2 +- media/extractors/mkv/Android.bp | 2 +- media/extractors/mp3/Android.bp | 2 +- media/extractors/mp4/Android.bp | 2 +- media/extractors/mpeg2/Android.bp | 2 +- media/extractors/ogg/Android.bp | 2 +- media/extractors/wav/Android.bp | 2 +- 12 files changed, 28 insertions(+), 36 deletions(-) diff --git a/apex/Android.bp b/apex/Android.bp index ec0efe6985..2cc6fcb854 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -12,22 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -com_android_media_extractors = [ - "libaacextractor", - "libamrextractor", - "libflacextractor", - "libmidiextractor", - "libmkvextractor", - "libmp3extractor", - "libmp4extractor", - "libmpeg2extractor", - "liboggextractor", - "libwavextractor", -] - apex_defaults { name: "com.android.media-defaults", java_libs: ["updatable-media"], + multilib: { + first: { + // Extractor process runs only with the primary ABI. + native_shared_libs: [ + // Extractor plugins + "libaacextractor", + "libamrextractor", + "libflacextractor", + "libmidiextractor", + "libmkvextractor", + "libmp3extractor", + "libmp4extractor", + "libmpeg2extractor", + "liboggextractor", + "libwavextractor", + ], + }, + }, key: "com.android.media.key", certificate: ":com.android.media.certificate", @@ -39,12 +44,6 @@ apex { name: "com.android.media", manifest: "manifest.json", defaults: ["com.android.media-defaults"], - multilib: { - first: { - // Extractor process runs only with the primary ABI. - native_shared_libs: com_android_media_extractors, - }, - }, } filegroup { diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp index 297d864c29..701ced732e 100644 --- a/apex/testing/Android.bp +++ b/apex/testing/Android.bp @@ -17,13 +17,6 @@ apex { manifest: "test_manifest.json", file_contexts: "com.android.media", defaults: ["com.android.media-defaults"], - multilib: { - both: { - // for test apex, built for both ABIs - native_shared_libs: com_android_media_extractors, - }, - }, - compile_multilib: "both", installable: false, } diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp index 6fe5970dd0..a58167a655 100644 --- a/media/extractors/aac/Android.bp +++ b/media/extractors/aac/Android.bp @@ -20,7 +20,7 @@ cc_library_shared { name: "libaacextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp index b26b2d86c6..4bd933deb5 100644 --- a/media/extractors/amr/Android.bp +++ b/media/extractors/amr/Android.bp @@ -18,7 +18,7 @@ cc_library_shared { name: "libamrextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp index 3e830902f7..3a3d051b70 100644 --- a/media/extractors/flac/Android.bp +++ b/media/extractors/flac/Android.bp @@ -24,7 +24,7 @@ cc_library_shared { name: "libflacextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp index 6790dd6af0..7d42e703f3 100644 --- a/media/extractors/midi/Android.bp +++ b/media/extractors/midi/Android.bp @@ -19,7 +19,7 @@ cc_library_shared { name: "libmidiextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp index 7c941490b3..1744d3d63f 100644 --- a/media/extractors/mkv/Android.bp +++ b/media/extractors/mkv/Android.bp @@ -25,7 +25,7 @@ cc_library_shared { name: "libmkvextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp index 168ef6801b..4e2f2480ac 100644 --- a/media/extractors/mp3/Android.bp +++ b/media/extractors/mp3/Android.bp @@ -24,7 +24,7 @@ cc_library_shared { name: "libmp3extractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp index 9b9c9310c1..1b308aa7ef 100644 --- a/media/extractors/mp4/Android.bp +++ b/media/extractors/mp4/Android.bp @@ -32,7 +32,7 @@ cc_defaults { ], version_script: "exports.lds", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", } cc_library_shared { diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index 14f49ae111..0f0c72cb82 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -40,7 +40,7 @@ cc_library_shared { name: "libmpeg2extractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp index fd03f6479a..604ec5945e 100644 --- a/media/extractors/ogg/Android.bp +++ b/media/extractors/ogg/Android.bp @@ -26,7 +26,7 @@ cc_library_shared { name: "liboggextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp index 15fd7964aa..7e89271e45 100644 --- a/media/extractors/wav/Android.bp +++ b/media/extractors/wav/Android.bp @@ -21,7 +21,7 @@ cc_library_shared { name: "libwavextractor", relative_install_path: "extractors", - compile_multilib: "both", + compile_multilib: "first", cflags: [ "-Werror", -- GitLab From cd04584186c96b48de31e54ec21c4681ab5fc4c8 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 3 May 2019 11:50:15 -0700 Subject: [PATCH 1364/1530] proper mask input stream in AMR-WB routine resolve ubsan integer overflows by properly masking input data. Bug: 123303229 Test: poc Change-Id: Ic8fe88bad481eb508d1cb15cfcf4500b78172883 --- media/libstagefright/codecs/amrwb/src/deemphasis_32.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/codecs/amrwb/src/deemphasis_32.cpp b/media/libstagefright/codecs/amrwb/src/deemphasis_32.cpp index b80555b6bb..c0e4c51d05 100644 --- a/media/libstagefright/codecs/amrwb/src/deemphasis_32.cpp +++ b/media/libstagefright/codecs/amrwb/src/deemphasis_32.cpp @@ -131,7 +131,7 @@ void deemphasis_32( int16 lo, hi; L_tmp = ((int32)x_hi[0]) << 16; - L_tmp += ((int32)x_lo[0]) << 4; + L_tmp += (((int32)x_lo[0]) << 4) & 0xFFFF; L_tmp = shl_int32(L_tmp, 3); L_tmp = fxp_mac_16by16(*mem, mu, L_tmp), @@ -144,7 +144,7 @@ void deemphasis_32( for (i = 1; i < L - 1; i++) { L_tmp = ((int32)hi) << 16; - L_tmp += ((int32)lo) << 4; + L_tmp += (((int32)lo) << 4) & 0xFFFF; L_tmp = shl_int32(L_tmp, 3); L_tmp = fxp_mac_16by16(y[i - 1], mu, L_tmp), L_tmp = shl_int32(L_tmp, 1); /* saturation can occur here */ @@ -153,7 +153,7 @@ void deemphasis_32( hi = x_hi[i+1]; } L_tmp = ((int32)hi) << 16; - L_tmp += ((int32)lo) << 4; + L_tmp += (((int32)lo) << 4) & 0xFFFF; L_tmp = shl_int32(L_tmp, 3); L_tmp = fxp_mac_16by16(y[i - 1], mu, L_tmp), L_tmp = shl_int32(L_tmp, 1); /* saturation can occur here */ -- GitLab From 573a270f26d063a388eb467d381a06c94bcfe7c4 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Wed, 17 Apr 2019 10:08:55 -0700 Subject: [PATCH 1365/1530] Camera: clear buffer cache when stream is reconfigured Also fix a issue for finishConfiguration is unintentionally delayed till first capture request. Test: Camera CTS + partner device testing Bug: 126390310 Change-Id: Ibca740a7160cbf41e01884dbcef8ba51eb4c75f7 --- .../device3/Camera3Device.cpp | 59 ++++++++++++------- .../libcameraservice/device3/Camera3Device.h | 2 + .../device3/Camera3Stream.cpp | 11 +++- .../libcameraservice/device3/Camera3Stream.h | 4 +- .../device3/Camera3StreamInterface.h | 4 +- 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 415b2d80a5..f4abba432d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -2522,6 +2522,9 @@ status_t Camera3Device::setConsumerSurfaces(int streamId, CLOGE("Stream %d is unknown", streamId); return BAD_VALUE; } + + // isConsumerConfigurationDeferred will be off after setConsumers + bool isDeferred = stream->isConsumerConfigurationDeferred(); status_t res = stream->setConsumers(consumers); if (res != OK) { CLOGE("Stream %d set consumer failed (error %d %s) ", streamId, res, strerror(-res)); @@ -2537,7 +2540,7 @@ status_t Camera3Device::setConsumerSurfaces(int streamId, surfaceIds->push_back(id); } - if (stream->isConsumerConfigurationDeferred()) { + if (isDeferred) { if (!stream->isConfiguring()) { CLOGE("Stream %d was already fully configured.", streamId); return INVALID_OPERATION; @@ -2612,7 +2615,6 @@ status_t Camera3Device::dropStreamBuffers(bool dropping, int streamId) { sp Camera3Device::createCaptureRequest( const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) { ATRACE_CALL(); - status_t res; sp newRequest = new CaptureRequest; newRequest->mSettingsList = request; @@ -2626,16 +2628,11 @@ sp Camera3Device::createCaptureRequest( inputStreams.data.u8[0]); return NULL; } - // Lazy completion of stream configuration (allocation/registration) - // on first use + if (mInputStream->isConfiguring()) { - res = mInputStream->finishConfiguration(); - if (res != OK) { - SET_ERR_L("Unable to finish configuring input stream %d:" - " %s (%d)", - mInputStream->getId(), strerror(-res), res); - return NULL; - } + SET_ERR_L("%s: input stream %d is not configured!", + __FUNCTION__, mInputStream->getId()); + return NULL; } // Check if stream prepare is blocking requests. if (mInputStream->isBlockedByPrepare()) { @@ -2675,15 +2672,9 @@ sp Camera3Device::createCaptureRequest( newRequest->mOutputSurfaces[streams.data.i32[i]] = surfaces; } - // Lazy completion of stream configuration (allocation/registration) - // on first use if (stream->isConfiguring()) { - res = stream->finishConfiguration(); - if (res != OK) { - SET_ERR_L("Unable to finish configuring stream %d: %s (%d)", - stream->getId(), strerror(-res), res); - return NULL; - } + SET_ERR_L("%s: stream %d is not configured!", __FUNCTION__, stream->getId()); + return NULL; } // Check if stream prepare is blocking requests. if (stream->isBlockedByPrepare()) { @@ -2908,7 +2899,8 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode, // faster if (mInputStream != NULL && mInputStream->isConfiguring()) { - res = mInputStream->finishConfiguration(); + bool streamReConfigured = false; + res = mInputStream->finishConfiguration(&streamReConfigured); if (res != OK) { CLOGE("Can't finish configuring input stream %d: %s (%d)", mInputStream->getId(), strerror(-res), res); @@ -2918,12 +2910,16 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode, } return BAD_VALUE; } + if (streamReConfigured) { + mInterface->onStreamReConfigured(mInputStream->getId()); + } } for (size_t i = 0; i < mOutputStreams.size(); i++) { sp outputStream = mOutputStreams[i]; if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) { - res = outputStream->finishConfiguration(); + bool streamReConfigured = false; + res = outputStream->finishConfiguration(&streamReConfigured); if (res != OK) { CLOGE("Can't finish configuring output stream %d: %s (%d)", outputStream->getId(), strerror(-res), res); @@ -2933,6 +2929,9 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode, } return BAD_VALUE; } + if (streamReConfigured) { + mInterface->onStreamReConfigured(outputStream->getId()); + } } } @@ -4780,7 +4779,7 @@ void Camera3Device::HalInterface::onBufferFreed( __FUNCTION__, handle, streamId); return; } else { - bufferId = it->second; + bufferId = it->second; bIdMap.erase(it); ALOGV("%s: stream %d now have %zu buffer caches after removing buf %p", __FUNCTION__, streamId, bIdMap.size(), handle); @@ -4788,6 +4787,22 @@ void Camera3Device::HalInterface::onBufferFreed( mFreedBuffers.push_back(std::make_pair(streamId, bufferId)); } +void Camera3Device::HalInterface::onStreamReConfigured(int streamId) { + std::lock_guard lock(mBufferIdMapLock); + auto mapIt = mBufferIdMaps.find(streamId); + if (mapIt == mBufferIdMaps.end()) { + ALOGE("%s: streamId %d not found!", __FUNCTION__, streamId); + return; + } + + BufferIdMap& bIdMap = mapIt->second; + for (const auto& it : bIdMap) { + uint64_t bufferId = it.second; + mFreedBuffers.push_back(std::make_pair(streamId, bufferId)); + } + bIdMap.clear(); +} + /** * RequestThread inner class methods */ diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index f8245df821..23df3c739d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -336,6 +336,8 @@ class Camera3Device : // Get a vector of bufferId of currently inflight buffers void getInflightRequestBufferKeys(std::vector* out); + void onStreamReConfigured(int streamId); + static const uint64_t BUFFER_ID_NO_BUFFER = 0; private: // Always valid diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 12ff1308da..d73a2f985d 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -287,8 +287,11 @@ bool Camera3Stream::isConfiguring() const { return (mState == STATE_IN_CONFIG) || (mState == STATE_IN_RECONFIG); } -status_t Camera3Stream::finishConfiguration() { +status_t Camera3Stream::finishConfiguration(/*out*/bool* streamReconfigured) { ATRACE_CALL(); + if (streamReconfigured != nullptr) { + *streamReconfigured = false; + } Mutex::Autolock l(mLock); switch (mState) { case STATE_ERROR: @@ -313,7 +316,7 @@ status_t Camera3Stream::finishConfiguration() { // Register for idle tracking sp statusTracker = mStatusTracker.promote(); - if (statusTracker != 0) { + if (statusTracker != 0 && mStatusId == StatusTracker::NO_STATUS_ID) { mStatusId = statusTracker->addComponent(); } @@ -332,6 +335,7 @@ status_t Camera3Stream::finishConfiguration() { mPrepareBlockRequest = true; mStreamUnpreparable = false; + bool reconfiguring = (mState == STATE_IN_RECONFIG); status_t res; res = configureQueueLocked(); // configureQueueLocked could return error in case of abandoned surface. @@ -348,6 +352,9 @@ status_t Camera3Stream::finishConfiguration() { return res; } + if (reconfiguring && streamReconfigured != nullptr) { + *streamReconfigured = true; + } mState = STATE_CONFIGURED; return res; diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 3d21029892..c916fe8a01 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -197,6 +197,8 @@ class Camera3Stream : * after this call, but can still be read until the destruction of the * stream. * + * streamReconfigured: set to true when a stream is being reconfigured. + * * Returns: * OK on a successful configuration * NO_INIT in case of a serious error from the HAL device @@ -204,7 +206,7 @@ class Camera3Stream : * INVALID_OPERATION in case connecting to the consumer failed or consumer * doesn't exist yet. */ - status_t finishConfiguration(); + status_t finishConfiguration(/*out*/bool* streamReconfigured = nullptr); /** * Cancels the stream configuration process. This returns the stream to the diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index 5cd11b7c70..73f501a57b 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -130,13 +130,15 @@ class Camera3StreamInterface : public virtual RefBase { * modified after this call, but can still be read until the destruction of * the stream. * + * streamReconfigured: set to true when a stream is being reconfigured. + * * Returns: * OK on a successful configuration * NO_INIT in case of a serious error from the HAL device * NO_MEMORY in case of an error registering buffers * INVALID_OPERATION in case connecting to the consumer failed */ - virtual status_t finishConfiguration() = 0; + virtual status_t finishConfiguration(/*out*/bool* streamReconfigured = nullptr) = 0; /** * Cancels the stream configuration process. This returns the stream to the -- GitLab From 16c66dd1e60d6b65b846f371c2ae4bf2b673545f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 1 May 2019 17:54:10 -0700 Subject: [PATCH 1366/1530] aaudio: fix multichannel legacy streams Two fixes for multichannel playback over USB from AAudio legacy streams: 1: AAudio use a channel index mask and not a positional mask when channel count is greater than 2. 2: audio policy gives priority to an output mixer which can support the requested channel mask without downmixing Bug: 131685020 Test: play multichannel audio over USB Change-Id: I13f2da48d47fcf81f522096d90ba59a89379cffb --- .../src/legacy/AudioStreamRecord.cpp | 4 +- .../libaaudio/src/legacy/AudioStreamTrack.cpp | 4 +- .../managerdefinitions/include/AudioPort.h | 3 + .../managerdefinitions/src/AudioPort.cpp | 35 ++- .../managerdefault/AudioPolicyManager.cpp | 199 ++++++++++-------- 5 files changed, 132 insertions(+), 113 deletions(-) diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp index 4a65fc9d18..71efc30e57 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp +++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp @@ -63,7 +63,9 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) // TODO Support UNSPECIFIED in AudioRecord. For now, use stereo if unspecified. int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) ? 2 : getSamplesPerFrame(); - audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame); + audio_channel_mask_t channelMask = samplesPerFrame <= 2 ? + audio_channel_in_mask_from_count(samplesPerFrame) : + audio_channel_mask_for_index_assignment_from_count(samplesPerFrame); size_t frameCount = (builder.getBufferCapacity() == AAUDIO_UNSPECIFIED) ? 0 : builder.getBufferCapacity(); diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index d628bf7cb9..094cdd1050 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -66,7 +66,9 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) // Use stereo if unspecified. int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) ? 2 : getSamplesPerFrame(); - audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame); + audio_channel_mask_t channelMask = samplesPerFrame <= 2 ? + audio_channel_out_mask_from_count(samplesPerFrame) : + audio_channel_mask_for_index_assignment_from_count(samplesPerFrame); audio_output_flags_t flags; aaudio_performance_mode_t perfMode = getPerformanceMode(); diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h index 2d182bdb27..d906f11aa2 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -112,6 +112,9 @@ public: static bool isBetterFormatMatch(audio_format_t newFormat, audio_format_t currentFormat, audio_format_t targetFormat); + static uint32_t formatDistance(audio_format_t format1, + audio_format_t format2); + static const uint32_t kFormatDistanceMax = 4; audio_module_handle_t getModuleHandle() const; uint32_t getModuleVersionMajor() const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp index a66c6957f9..c11490a222 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -282,30 +282,25 @@ int AudioPort::compareFormats(audio_format_t format1, audio_format_t format2) return index1 - index2; } -bool AudioPort::isBetterFormatMatch(audio_format_t newFormat, - audio_format_t currentFormat, - audio_format_t targetFormat) +uint32_t AudioPort::formatDistance(audio_format_t format1, audio_format_t format2) { - if (newFormat == currentFormat) { - return false; - } - if (currentFormat == AUDIO_FORMAT_INVALID) { - return true; + if (format1 == format2) { + return 0; } - if (newFormat == targetFormat) { - return true; + if (format1 == AUDIO_FORMAT_INVALID || format2 == AUDIO_FORMAT_INVALID) { + return kFormatDistanceMax; } - int currentDiffBytes = (int)audio_bytes_per_sample(targetFormat) - - audio_bytes_per_sample(currentFormat); - int newDiffBytes = (int)audio_bytes_per_sample(targetFormat) - - audio_bytes_per_sample(newFormat); + int diffBytes = (int)audio_bytes_per_sample(format1) - + audio_bytes_per_sample(format2); - if (abs(newDiffBytes) < abs(currentDiffBytes)) { - return true; - } else if (abs(newDiffBytes) == abs(currentDiffBytes)) { - return (newDiffBytes >= 0); - } - return false; + return abs(diffBytes); +} + +bool AudioPort::isBetterFormatMatch(audio_format_t newFormat, + audio_format_t currentFormat, + audio_format_t targetFormat) +{ + return formatDistance(newFormat, targetFormat) < formatDistance(currentFormat, targetFormat); } void AudioPort::pickAudioProfile(uint32_t &samplingRate, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index c0ca440dc5..e3cac79b91 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -36,12 +36,12 @@ #define AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME \ "audio_policy_configuration_bluetooth_legacy_hal.xml" +#include #include #include #include #include #include - #include #include #include @@ -592,7 +592,7 @@ sp AudioPolicyManager::createTelephonyPatch( AUDIO_DEVICE_OUT_TELEPHONY_TX, String8(), AUDIO_FORMAT_DEFAULT); SortedVector outputs = getOutputsForDevices(DeviceVector(outputDevice), mOutputs); - audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); + const audio_io_handle_t output = selectOutput(outputs); // request to reuse existing output stream if one is already opened to reach the target device if (output != AUDIO_IO_HANDLE_NONE) { sp outputDesc = mOutputs.valueFor(output); @@ -883,7 +883,7 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream) // and AudioSystem::getOutputSamplingRate(). SortedVector outputs = getOutputsForDevices(devices, mOutputs); - audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE, AUDIO_FORMAT_INVALID); + const audio_io_handle_t output = selectOutput(outputs); ALOGV("getOutput() stream %d selected devices %s, output %d", stream, devices.toString().c_str(), output); @@ -1430,108 +1430,125 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector bestMatchCriteria(8, 0); + + const uint32_t channelCount = audio_channel_count_from_out_mask(channelMask); + const uint32_t hapticChannelCount = audio_channel_count_from_out_mask( + channelMask & AUDIO_CHANNEL_HAPTIC_ALL); - if (outputs.size() == 0) { - return AUDIO_IO_HANDLE_NONE; - } - if (outputs.size() == 1) { - return outputs[0]; - } + for (audio_io_handle_t output : outputs) { + sp outputDesc = mOutputs.valueFor(output); + // matching criteria values in priority order for current output + std::vector currentMatchCriteria(8, 0); - int maxCommonFlags = 0; - const size_t hapticChannelCount = audio_channel_count_from_out_mask( - channelMask & AUDIO_CHANNEL_HAPTIC_ALL); - audio_io_handle_t outputForFlags = AUDIO_IO_HANDLE_NONE; - audio_io_handle_t outputForPrimary = AUDIO_IO_HANDLE_NONE; - audio_io_handle_t outputForFormat = AUDIO_IO_HANDLE_NONE; - audio_format_t bestFormat = AUDIO_FORMAT_INVALID; - audio_format_t bestFormatForFlags = AUDIO_FORMAT_INVALID; + if (outputDesc->isDuplicated()) { + continue; + } + if ((kExcludedFlags & outputDesc->mFlags) != 0) { + continue; + } - // Flags which must be present on both the request and the selected output - static const audio_output_flags_t kMandatedFlags = (audio_output_flags_t) - (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ); + // If haptic channel is specified, use the haptic output if present. + // When using haptic output, same audio format and sample rate are required. + const uint32_t outputHapticChannelCount = audio_channel_count_from_out_mask( + outputDesc->mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL); + if ((hapticChannelCount == 0) != (outputHapticChannelCount == 0)) { + continue; + } + if (outputHapticChannelCount >= hapticChannelCount + && format == outputDesc->mFormat + && samplingRate == outputDesc->mSamplingRate) { + currentMatchCriteria[0] = outputHapticChannelCount; + } - for (audio_io_handle_t output : outputs) { - sp outputDesc = mOutputs.valueFor(output); - if (!outputDesc->isDuplicated()) { - if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { - continue; - } - // If haptic channel is specified, use the haptic output if present. - // When using haptic output, same audio format and sample rate are required. - if (hapticChannelCount > 0) { - // If haptic channel is specified, use the first output that - // support haptic playback. - if (audio_channel_count_from_out_mask( - outputDesc->mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) >= hapticChannelCount - && format == outputDesc->mFormat - && samplingRate == outputDesc->mSamplingRate) { - return output; - } - } else { - // When haptic channel is not specified, skip haptic output. - if (outputDesc->mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) { - continue; - } - } - if ((kMandatedFlags & flags) != - (kMandatedFlags & outputDesc->mProfile->getFlags())) { - continue; - } + // functional flags match + currentMatchCriteria[1] = popcount(outputDesc->mFlags & functionalFlags); - // if a valid format is specified, skip output if not compatible - if (format != AUDIO_FORMAT_INVALID) { - if (!audio_is_linear_pcm(format)) { - continue; - } - if (AudioPort::isBetterFormatMatch( - outputDesc->mFormat, bestFormat, format)) { - outputForFormat = output; - bestFormat = outputDesc->mFormat; - } + // channel mask and channel count match + uint32_t outputChannelCount = audio_channel_count_from_out_mask(outputDesc->mChannelMask); + if (channelMask != AUDIO_CHANNEL_NONE && channelCount > 2 && + channelCount <= outputChannelCount) { + if ((audio_channel_mask_get_representation(channelMask) == + audio_channel_mask_get_representation(outputDesc->mChannelMask)) && + ((channelMask & outputDesc->mChannelMask) == channelMask)) { + currentMatchCriteria[2] = outputChannelCount; } + currentMatchCriteria[3] = outputChannelCount; + } - int commonFlags = popcount(outputDesc->mProfile->getFlags() & flags); - if (commonFlags >= maxCommonFlags) { - if (commonFlags == maxCommonFlags) { - if (format != AUDIO_FORMAT_INVALID - && AudioPort::isBetterFormatMatch( - outputDesc->mFormat, bestFormatForFlags, format)) { - outputForFlags = output; - bestFormatForFlags = outputDesc->mFormat; - } - } else { - outputForFlags = output; - maxCommonFlags = commonFlags; - bestFormatForFlags = outputDesc->mFormat; - } - ALOGV("selectOutput() commonFlags for output %d, %04x", output, commonFlags); - } - if (outputDesc->mProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) { - outputForPrimary = output; - } + // sampling rate match + if (samplingRate > SAMPLE_RATE_HZ_DEFAULT && + samplingRate <= outputDesc->mSamplingRate) { + currentMatchCriteria[4] = outputDesc->mSamplingRate; } - } - if (outputForFlags != AUDIO_IO_HANDLE_NONE) { - return outputForFlags; - } - if (outputForFormat != AUDIO_IO_HANDLE_NONE) { - return outputForFormat; - } - if (outputForPrimary != AUDIO_IO_HANDLE_NONE) { - return outputForPrimary; + // performance flags match + currentMatchCriteria[5] = popcount(outputDesc->mFlags & performanceFlags); + + // format match + if (format != AUDIO_FORMAT_INVALID) { + currentMatchCriteria[6] = + AudioPort::kFormatDistanceMax - + AudioPort::formatDistance(format, outputDesc->mFormat); + } + + // primary output match + currentMatchCriteria[7] = outputDesc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY; + + // compare match criteria by priority then value + if (std::lexicographical_compare(bestMatchCriteria.begin(), bestMatchCriteria.end(), + currentMatchCriteria.begin(), currentMatchCriteria.end())) { + bestMatchCriteria = currentMatchCriteria; + bestOutput = output; + + std::stringstream result; + std::copy(bestMatchCriteria.begin(), bestMatchCriteria.end(), + std::ostream_iterator(result, " ")); + ALOGV("%s new bestOutput %d criteria %s", + __func__, bestOutput, result.str().c_str()); + } } - return outputs[0]; + return bestOutput; } status_t AudioPolicyManager::startOutput(audio_port_handle_t portId) @@ -3485,7 +3502,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, getOutputsForDevices(DeviceVector(sinkDevice), mOutputs); // if the sink device is reachable via an opened output stream, request to go via // this output stream by adding a second source to the patch description - audio_io_handle_t output = selectOutput(outputs); + const audio_io_handle_t output = selectOutput(outputs); if (output != AUDIO_IO_HANDLE_NONE) { sp outputDesc = mOutputs.valueFor(output); if (outputDesc->isDuplicated()) { -- GitLab From 33f0363ef1c22781f4dffc8644591543431eda89 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 3 May 2019 15:10:56 -0700 Subject: [PATCH 1367/1530] Fix NULL pointer dereference issues in CryptoHal::toSharedBuffer bug:111636206 test: gts media tests Change-Id: Ia3b664ccff554000084438f74e004a82c69a20da --- drm/libmediadrm/CryptoHal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp index 4dda5d7a32..d62ccd6be1 100644 --- a/drm/libmediadrm/CryptoHal.cpp +++ b/drm/libmediadrm/CryptoHal.cpp @@ -301,7 +301,7 @@ status_t CryptoHal::toSharedBuffer(const sp& memory, int32_t seqNum, :: ssize_t offset; size_t size; - if (memory == NULL && buffer == NULL) { + if (memory == NULL || buffer == NULL) { return UNEXPECTED_NULL; } -- GitLab From 4ec85ae9be632aab5f9aac357e08715840751fe9 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Wed, 13 Feb 2019 00:50:07 +0530 Subject: [PATCH 1368/1530] codec2: Fix timestampDev issues for AvcDec If last_frame come with EOS then we need to cloneandSend it. Test: VtsHidlC2V1_0TargetVideoDecTest -I software -P /sdcard/res/ -C c2.android.avc.decoder Bug: 123267886 Change-Id: Ieac0d83bd685a7ed601cc70bc1c93cc68f8f58f4 --- media/codec2/components/avc/C2SoftAvcDec.cpp | 45 +++++++++++++++++++- media/codec2/components/avc/C2SoftAvcDec.h | 2 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp index 9290d743d3..6b651f0f77 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.cpp +++ b/media/codec2/components/avc/C2SoftAvcDec.cpp @@ -335,7 +335,8 @@ C2SoftAvcDec::C2SoftAvcDec( mIvColorFormat(IV_YUV_420P), mWidth(320), mHeight(240), - mHeaderDecoded(false) { + mHeaderDecoded(false), + mOutIndex(0u) { GENERATE_FILE_NAMES(); CREATE_DUMP_FILE(mInFile); } @@ -692,6 +693,33 @@ void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr &wor buffer->setInfo(mIntf->getColorAspects_l()); } + class FillWork { + public: + FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal, + const std::shared_ptr& buffer) + : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {} + ~FillWork() = default; + + void operator()(const std::unique_ptr& work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = mOrdinal; + work->workletsProcessed = 1u; + work->result = C2_OK; + if (mBuffer) { + work->worklets.front()->output.buffers.push_back(mBuffer); + } + ALOGV("timestamp = %lld, index = %lld, w/%s buffer", + mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(), + mBuffer ? "" : "o"); + } + + private: + const uint32_t mFlags; + const C2WorkOrdinalStruct mOrdinal; + const std::shared_ptr mBuffer; + }; + auto fillWork = [buffer](const std::unique_ptr &work) { work->worklets.front()->output.flags = (C2FrameData::flags_t)0; work->worklets.front()->output.buffers.clear(); @@ -700,7 +728,20 @@ void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr &wor work->workletsProcessed = 1u; }; if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { - fillWork(work); + bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining + if (eos) { + if (buffer) { + mOutIndex = index; + C2WorkOrdinalStruct outOrdinal = work->input.ordinal; + cloneAndSend( + mOutIndex, work, + FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer)); + buffer.reset(); + } + } else { + fillWork(work); + } } else { finish(index, fillWork); } diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h index 2127a93377..72ee583a33 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.h +++ b/media/codec2/components/avc/C2SoftAvcDec.h @@ -21,6 +21,7 @@ #include +#include #include #include "ih264_typedefs.h" @@ -163,6 +164,7 @@ private: bool mSignalledOutputEos; bool mSignalledError; bool mHeaderDecoded; + std::atomic_uint64_t mOutIndex; // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid // converting them to C2 values for each frame struct VuiColorAspects { -- GitLab From 3bfa6e7705247a1f2b58bab5369d83a596ebfbcc Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Mon, 18 Feb 2019 10:41:14 +0530 Subject: [PATCH 1369/1530] codec2: Fix timestampDev issues for HevcDec If last_frame come with EOS then we need to cloneandSend it. Test: VtsHidlC2V1_0TargetVideoDecTest -I software -P /sdcard/res/ -C c2.android.hevc.decoder Bug: 123267886 Change-Id: I9ce3eac328d4825199569bfa49397f60e8ff3e85 --- .../codec2/components/hevc/C2SoftHevcDec.cpp | 45 ++++++++++++++++++- media/codec2/components/hevc/C2SoftHevcDec.h | 2 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp index bb8dda03ed..3d0ce1c343 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.cpp +++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp @@ -329,7 +329,8 @@ C2SoftHevcDec::C2SoftHevcDec( mIvColorformat(IV_YUV_420P), mWidth(320), mHeight(240), - mHeaderDecoded(false) { + mHeaderDecoded(false), + mOutIndex(0u) { } C2SoftHevcDec::~C2SoftHevcDec() { @@ -688,6 +689,33 @@ void C2SoftHevcDec::finishWork(uint64_t index, const std::unique_ptr &wo buffer->setInfo(mIntf->getColorAspects_l()); } + class FillWork { + public: + FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal, + const std::shared_ptr& buffer) + : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {} + ~FillWork() = default; + + void operator()(const std::unique_ptr& work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = mOrdinal; + work->workletsProcessed = 1u; + work->result = C2_OK; + if (mBuffer) { + work->worklets.front()->output.buffers.push_back(mBuffer); + } + ALOGV("timestamp = %lld, index = %lld, w/%s buffer", + mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(), + mBuffer ? "" : "o"); + } + + private: + const uint32_t mFlags; + const C2WorkOrdinalStruct mOrdinal; + const std::shared_ptr mBuffer; + }; + auto fillWork = [buffer](const std::unique_ptr &work) { work->worklets.front()->output.flags = (C2FrameData::flags_t)0; work->worklets.front()->output.buffers.clear(); @@ -696,7 +724,20 @@ void C2SoftHevcDec::finishWork(uint64_t index, const std::unique_ptr &wo work->workletsProcessed = 1u; }; if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { - fillWork(work); + bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining + if (eos) { + if (buffer) { + mOutIndex = index; + C2WorkOrdinalStruct outOrdinal = work->input.ordinal; + cloneAndSend( + mOutIndex, work, + FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer)); + buffer.reset(); + } + } else { + fillWork(work); + } } else { finish(index, fillWork); } diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h index 75111fc271..b7664e65e5 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.h +++ b/media/codec2/components/hevc/C2SoftHevcDec.h @@ -19,6 +19,7 @@ #include +#include #include #include "ihevc_typedefs.h" @@ -121,6 +122,7 @@ struct C2SoftHevcDec : public SimpleC2Component { bool mSignalledOutputEos; bool mSignalledError; bool mHeaderDecoded; + std::atomic_uint64_t mOutIndex; // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid // converting them to C2 values for each frame -- GitLab From d91bc4855b0688c332f35c1395b5fbdaefd150a5 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Mon, 18 Feb 2019 10:42:41 +0530 Subject: [PATCH 1370/1530] codec2: Fix timestampDev issues for Mpeg2Dec If last_frame come with EOS then we need to cloneandSend it. Test: VtsHidlC2V1_0TargetVideoDecTest -I software -P /sdcard/res/ -C c2.android.mpeg2.decoder Bug: 123267886 Change-Id: Iec3d5d8befe7d9e323d8dce4ab194098d47e43d6 --- .../components/mpeg2/C2SoftMpeg2Dec.cpp | 45 ++++++++++++++++++- .../codec2/components/mpeg2/C2SoftMpeg2Dec.h | 2 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp index 290677ed44..df7b4036ff 100644 --- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp +++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp @@ -315,7 +315,8 @@ C2SoftMpeg2Dec::C2SoftMpeg2Dec( mOutBufferDrain(nullptr), mIvColorformat(IV_YUV_420P), mWidth(320), - mHeight(240) { + mHeight(240), + mOutIndex(0u) { // If input dump is enabled, then open create an empty file GENERATE_FILE_NAMES(); CREATE_DUMP_FILE(mInFile); @@ -766,6 +767,33 @@ void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr &w buffer->setInfo(mIntf->getColorAspects_l()); } + class FillWork { + public: + FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal, + const std::shared_ptr& buffer) + : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {} + ~FillWork() = default; + + void operator()(const std::unique_ptr& work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.ordinal = mOrdinal; + work->workletsProcessed = 1u; + work->result = C2_OK; + if (mBuffer) { + work->worklets.front()->output.buffers.push_back(mBuffer); + } + ALOGV("timestamp = %lld, index = %lld, w/%s buffer", + mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(), + mBuffer ? "" : "o"); + } + + private: + const uint32_t mFlags; + const C2WorkOrdinalStruct mOrdinal; + const std::shared_ptr mBuffer; + }; + auto fillWork = [buffer](const std::unique_ptr &work) { work->worklets.front()->output.flags = (C2FrameData::flags_t)0; work->worklets.front()->output.buffers.clear(); @@ -774,7 +802,20 @@ void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr &w work->workletsProcessed = 1u; }; if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { - fillWork(work); + bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining + if (eos) { + if (buffer) { + mOutIndex = index; + C2WorkOrdinalStruct outOrdinal = work->input.ordinal; + cloneAndSend( + mOutIndex, work, + FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer)); + buffer.reset(); + } + } else { + fillWork(work); + } } else { finish(index, fillWork); } diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h index 99998723f3..65d3b87fcd 100644 --- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h +++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h @@ -17,6 +17,7 @@ #ifndef ANDROID_C2_SOFT_MPEG2_DEC_H_ #define ANDROID_C2_SOFT_MPEG2_DEC_H_ +#include #include #include @@ -161,6 +162,7 @@ struct C2SoftMpeg2Dec : public SimpleC2Component { uint32_t mStride; bool mSignalledOutputEos; bool mSignalledError; + std::atomic_uint64_t mOutIndex; // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid // converting them to C2 values for each frame -- GitLab From 4a7c6cb4b36029dedd1be224f15f7919094ef49b Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 22 Jan 2019 18:59:24 +0530 Subject: [PATCH 1371/1530] codec2: VTS add support for timestamp tests in VideoEncTest and VideoDecTest Test: VtsHidlC2V1_0TargetVideoEncTest -I software -P /sdcard/res/ -C c2.android.avc.encoder Test: VtsHidlC2V1_0TargetVideoDecTest -I software -P /sdcard/res/ -C c2.android.avc.decoder Bug: 80272610 Change-Id: Ic773a118ac699e99cc5dc83a10bb3bd90555e2fc --- .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 3 + .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 59 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp index dffcb6e0ed..9bfcdd55af 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp @@ -445,6 +445,9 @@ TEST_P(Codec2VideoDecDecodeTest, DecodeTest) { int bytesCount = 0; uint32_t flags = 0; uint32_t timestamp = 0; + mTimestampDevTest = true; + mFlushedIndices.clear(); + mTimestampUslist.clear(); while (1) { if (!(eleInfo >> bytesCount)) break; eleInfo >> flags; diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 673dc856e8..3c9e96c09a 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -120,6 +120,8 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { mCsd = false; mFramesReceived = 0; mFailedWorkReceived = 0; + mTimestampUs = 0u; + mTimestampDevTest = false; if (mCompName == unknown_comp) mDisableTest = true; if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n"; } @@ -139,6 +141,45 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { void handleWorkDone(std::list>& workItems) { for (std::unique_ptr& work : workItems) { if (!work->worklets.empty()) { + // For encoder components current timestamp always exceeds + // previous timestamp + typedef std::unique_lock ULock; + if (!mTimestampUslist.empty()) { + EXPECT_GE((work->worklets.front() + ->output.ordinal.timestamp.peeku()), + mTimestampUs); + mTimestampUs = work->worklets.front() + ->output.ordinal.timestamp.peeku(); + // Currently this lock is redundant as no mTimestampUslist is only initialized + // before queuing any work to component. Once AdaptiveTest is added similar to + // the one in video decoders, this is needed. + ULock l(mQueueLock); + + if (mTimestampDevTest) { + bool tsHit = false; + std::list::iterator it = + mTimestampUslist.begin(); + while (it != mTimestampUslist.end()) { + if (*it == mTimestampUs) { + mTimestampUslist.erase(it); + tsHit = true; + break; + } + it++; + } + if (tsHit == false) { + if (mTimestampUslist.empty() == false) { + EXPECT_EQ(tsHit, true) + << "TimeStamp not recognized"; + } else { + std::cout + << "[ INFO ] Received non-zero " + "output / TimeStamp not recognized \n"; + } + } + } + } + if (work->result != C2_OK) mFailedWorkReceived++; workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue, mEos, mCsd, @@ -161,10 +202,13 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { bool mCsd; bool mDisableTest; bool mConfig; + bool mTimestampDevTest; standardComp mCompName; uint32_t mFramesReceived; uint32_t mFailedWorkReceived; + uint64_t mTimestampUs; + std::list mTimestampUslist; std::list mFlushedIndices; C2BlockPool::local_id_t mBlockPoolId; @@ -364,6 +408,18 @@ TEST_P(Codec2VideoEncEncodeTest, EncodeTest) { ASSERT_EQ(eleStream.is_open(), true) << mURL << " file not found"; ALOGV("mURL : %s", mURL); + mTimestampUs = 0; + mTimestampDevTest = true; + mFlushedIndices.clear(); + mTimestampUslist.clear(); + uint32_t inputFrames = ENC_NUM_FRAMES; + uint32_t timestamp = 0; + // Add input timestamp to timestampUslist + while (inputFrames) { + if (mTimestampDevTest) mTimestampUslist.push_back(timestamp); + timestamp += ENCODER_TIMESTAMP_INCREMENT; + inputFrames--; + } if (!setupConfigParam(nWidth, nHeight)) { std::cout << "[ WARN ] Test Skipped \n"; return; @@ -375,7 +431,7 @@ TEST_P(Codec2VideoEncEncodeTest, EncodeTest) { 0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS)); // If EOS is not sent, sending empty input with EOS flag - uint32_t inputFrames = ENC_NUM_FRAMES; + inputFrames = ENC_NUM_FRAMES; if (!signalEOS) { ASSERT_NO_FATAL_FAILURE( waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1)); @@ -407,6 +463,7 @@ TEST_P(Codec2VideoEncEncodeTest, EncodeTest) { ASSERT_TRUE(false) << "CSD Buffer not expected"; } + if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true); ASSERT_EQ(mComponent->stop(), C2_OK); } -- GitLab From dd9faf18a44e8005c64290616bca8788961af7a2 Mon Sep 17 00:00:00 2001 From: Umang Saini Date: Fri, 3 May 2019 17:28:15 +0530 Subject: [PATCH 1372/1530] codec2: Allow all component names in VTS Test: VtsHidlC2V1_0TargetVideoDecTest -I default -C c2.qti.avc.decoder -P /sdcard/res/ Test: VtsHidlC2V1_0TargetVideoEncTest -I default -C c2.qti.avc.encoder -P /sdcard/res/ Bug: 131905215 Change-Id: I8bbdff7b53778ed4e21e7c598363591b07eb5f60 --- .../audio/VtsHidlC2V1_0TargetAudioDecTest.cpp | 13 +++------- .../audio/VtsHidlC2V1_0TargetAudioEncTest.cpp | 13 +++------- .../hidl/1.0/vts/functional/common/README.md | 25 +++++++++++++------ .../video/VtsHidlC2V1_0TargetVideoDecTest.cpp | 13 +++------- .../video/VtsHidlC2V1_0TargetVideoEncTest.cpp | 13 +++------- 5 files changed, 29 insertions(+), 48 deletions(-) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp index 89947d4f20..6469735611 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp @@ -117,17 +117,10 @@ class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]); - std::string substring; - std::string comp; - substring = std::string(gEnv->getComponent()); - /* TODO: better approach to find the component */ - /* "c2.android." => 11th position */ - size_t pos = 11; - size_t len = substring.find(".decoder", pos); - comp = substring.substr(pos, len - pos); - + // Find the component type + std::string comp = std::string(gEnv->getComponent()); for (size_t i = 0; i < kNumStringToName; ++i) { - if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) { + if (strcasestr(comp.c_str(), kStringToName[i].Name)) { mCompName = kStringToName[i].CompName; break; } diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp index 0946fa6052..01baf7ee83 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp @@ -103,17 +103,10 @@ class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]); - std::string substring; - std::string comp; - substring = std::string(gEnv->getComponent()); - /* TODO: better approach to find the component */ - /* "c2.android." => 11th position */ - size_t pos = 11; - size_t len = substring.find(".encoder", pos); - comp = substring.substr(pos, len - pos); - + // Find the component type + std::string comp = std::string(gEnv->getComponent()); for (size_t i = 0; i < kNumStringToName; ++i) { - if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) { + if (strcasestr(comp.c_str(), kStringToName[i].Name)) { mCompName = kStringToName[i].CompName; break; } diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md index da569a8769..3deab1056f 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/README.md +++ b/media/codec2/hidl/1.0/vts/functional/common/README.md @@ -1,22 +1,31 @@ -## Codec2 Hal @ 1.0 tests ## +## Codec2 VTS Hal @ 1.0 tests ## --- #### master : Functionality of master is to enumerate all the Codec2 components available in C2 media service. -usage: MtsHidlC2V1\_0TargetMasterTest -I software +usage: VtsHidlC2V1\_0TargetMasterTest -I default #### component : -Functionality of component is to test common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass. +Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass. -usage: MtsHidlC2V1\_0TargetComponentTest -I software -C +usage: VtsHidlC2V1\_0TargetComponentTest -I software -C +example: VtsHidlC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder #### audio : -Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from hardware/interfaces/media/res. The path to these files on the device is required to be given for bitstream tests. +Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests. -usage: MtsHidlC2V1\_0TargetAudioDecTest -I software -C -P /sdcard/media +usage: VtsHidlC2V1\_0TargetAudioDecTest -I default -C -P /sdcard/res/ +usage: VtsHidlC2V1\_0TargetAudioEncTest -I software -C -P /sdcard/res/ + +example: VtsHidlC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/res/ +example: VtsHidlC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/ #### video : -Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from hardware/interfaces/media/res. The path to these files on the device is required to be given for bitstream tests. +Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests. + +usage: VtsHidlC2V1\_0TargetVideoDecTest -I default -C -P /sdcard/res/ +usage: VtsHidlC2V1\_0TargetVideoEncTest -I software -C -P /sdcard/res/ -usage: MtsHidlC2V1\_0TargetVideoDecTest -I software -C -P /sdcard/media +example: VtsHidlC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/ +example: VtsHidlC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/res/ diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp index 9bfcdd55af..33fa848073 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp @@ -107,17 +107,10 @@ class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase { const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]); - std::string substring; - std::string comp; - substring = std::string(gEnv->getComponent()); - /* TODO: better approach to find the component */ - /* "c2.android." => 11th position */ - size_t pos = 11; - size_t len = substring.find(".decoder", pos); - comp = substring.substr(pos, len - pos); - + // Find the component type + std::string comp = std::string(gEnv->getComponent()); for (size_t i = 0; i < kNumStringToName; ++i) { - if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) { + if (strcasestr(comp.c_str(), kStringToName[i].Name)) { mCompName = kStringToName[i].CompName; break; } diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp index 3c9e96c09a..6bcf84040e 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp +++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp @@ -101,17 +101,10 @@ class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase { const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]); - std::string substring; - std::string comp; - substring = std::string(gEnv->getComponent()); - /* TODO: better approach to find the component */ - /* "c2.android." => 11th position */ - size_t pos = 11; - size_t len = substring.find(".encoder", pos); - comp = substring.substr(pos, len - pos); - + // Find the component type + std::string comp = std::string(gEnv->getComponent()); for (size_t i = 0; i < kNumStringToName; ++i) { - if (!strcasecmp(comp.c_str(), kStringToName[i].Name)) { + if (strcasestr(comp.c_str(), kStringToName[i].Name)) { mCompName = kStringToName[i].CompName; break; } -- GitLab From 5ff38f71f4ac86bc126d43a165aafa010673a8a7 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Fri, 3 May 2019 15:50:27 -0700 Subject: [PATCH 1373/1530] Remove libcodec2_vndk from vndk Test: Builds Bug: 128894663 Change-Id: I092a696df5f3a535c9aff5b228daf84d7519bad7 --- media/codec2/hidl/1.0/utils/Android.bp | 3 --- media/codec2/vndk/Android.bp | 3 --- 2 files changed, 6 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index b73f0c87ca..63fe36bfe5 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -48,9 +48,6 @@ cc_library { cc_library { name: "libcodec2_hidl@1.0", vendor_available: true, - vndk: { - enabled: true, - }, defaults: ["hidl_defaults"], diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index 198bd72f44..ca69810039 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -14,9 +14,6 @@ cc_library_headers { cc_library_shared { name: "libcodec2_vndk", vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "C2AllocatorIon.cpp", -- GitLab From 91b8fe3126bf76e611a579fd4ee3471ef390ddd0 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 9 Apr 2019 08:45:32 -0700 Subject: [PATCH 1374/1530] media/omx: Make OMX software plugin an actual plugin - This allows devices to ship with or without software OMX codecs. Bug: 129710438 Change-Id: Ie23fd3b39b2607ae657b43521a3e1870a7a25976 --- media/libstagefright/omx/Android.bp | 48 +++++++++++++++-- media/libstagefright/omx/OMXMaster.cpp | 54 ++++++++++--------- media/libstagefright/omx/SoftOMXPlugin.cpp | 10 ++++ .../include/media/stagefright/omx/OMXMaster.h | 9 ++-- 4 files changed, 88 insertions(+), 33 deletions(-) diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 0c5075236f..e260cae115 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -41,7 +41,6 @@ cc_library_shared { "libcutils", "libstagefright_foundation", "libstagefright_bufferqueue_helper", - "libstagefright_softomx", "libstagefright_xmlparser", "libdl", "libhidlbase", @@ -79,14 +78,10 @@ cc_library_shared { cc_library_shared { name: "libstagefright_softomx", vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "SimpleSoftOMXComponent.cpp", "SoftOMXComponent.cpp", - "SoftOMXPlugin.cpp", "SoftVideoDecoderOMXComponent.cpp", "SoftVideoEncoderOMXComponent.cpp", ], @@ -126,6 +121,49 @@ cc_library_shared { }, } +cc_library_shared { + name: "libstagefright_softomx_plugin", + vendor_available: true, + + srcs: [ + "SoftOMXPlugin.cpp", + ], + + export_include_dirs: [ + "include", + ], + + header_libs: [ + "media_plugin_headers", + ], + + export_header_lib_headers: [ + "media_plugin_headers", + ], + + shared_libs: [ + "libstagefright_softomx", + "libstagefright_foundation", + "liblog", + "libutils", + ], + + cflags: [ + "-Werror", + "-Wall", + "-Wno-unused-parameter", + "-Wno-documentation", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} + cc_defaults { name: "libstagefright_softomx-defaults", vendor_available: true, diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp index 0967b5fa60..8c5ca6e28a 100644 --- a/media/libstagefright/omx/OMXMaster.cpp +++ b/media/libstagefright/omx/OMXMaster.cpp @@ -29,8 +29,7 @@ namespace android { -OMXMaster::OMXMaster() - : mVendorLibHandle(NULL) { +OMXMaster::OMXMaster() { pid_t pid = getpid(); char filename[20]; @@ -52,47 +51,52 @@ OMXMaster::OMXMaster() } addVendorPlugin(); - addPlugin(new SoftOMXPlugin); + addPlatformPlugin(); } OMXMaster::~OMXMaster() { clearPlugins(); - - if (mVendorLibHandle != NULL) { - dlclose(mVendorLibHandle); - mVendorLibHandle = NULL; - } } void OMXMaster::addVendorPlugin() { addPlugin("libstagefrighthw.so"); } +void OMXMaster::addPlatformPlugin() { + addPlugin("libstagefright_softomx_plugin.so"); +} + void OMXMaster::addPlugin(const char *libname) { - mVendorLibHandle = android_load_sphal_library(libname, RTLD_NOW); + void *libHandle = android_load_sphal_library(libname, RTLD_NOW); - if (mVendorLibHandle == NULL) { + if (libHandle == NULL) { return; } typedef OMXPluginBase *(*CreateOMXPluginFunc)(); CreateOMXPluginFunc createOMXPlugin = (CreateOMXPluginFunc)dlsym( - mVendorLibHandle, "createOMXPlugin"); + libHandle, "createOMXPlugin"); if (!createOMXPlugin) createOMXPlugin = (CreateOMXPluginFunc)dlsym( - mVendorLibHandle, "_ZN7android15createOMXPluginEv"); + libHandle, "_ZN7android15createOMXPluginEv"); + OMXPluginBase *plugin = nullptr; if (createOMXPlugin) { - addPlugin((*createOMXPlugin)()); + plugin = (*createOMXPlugin)(); + } + + if (plugin) { + mPlugins.push_back({ plugin, libHandle }); + addPlugin(plugin); + } else { + android_unload_sphal_library(libHandle); } } void OMXMaster::addPlugin(OMXPluginBase *plugin) { Mutex::Autolock autoLock(mLock); - mPlugins.push_back(plugin); - OMX_U32 index = 0; char name[128]; @@ -120,20 +124,20 @@ void OMXMaster::addPlugin(OMXPluginBase *plugin) { void OMXMaster::clearPlugins() { Mutex::Autolock autoLock(mLock); - typedef void (*DestroyOMXPluginFunc)(OMXPluginBase*); - DestroyOMXPluginFunc destroyOMXPlugin = - (DestroyOMXPluginFunc)dlsym( - mVendorLibHandle, "destroyOMXPlugin"); - mPluginByComponentName.clear(); + mPluginByInstance.clear(); - for (List::iterator it = mPlugins.begin(); - it != mPlugins.end(); ++it) { + typedef void (*DestroyOMXPluginFunc)(OMXPluginBase*); + for (const Plugin &plugin : mPlugins) { + DestroyOMXPluginFunc destroyOMXPlugin = + (DestroyOMXPluginFunc)dlsym( + plugin.mLibHandle, "destroyOMXPlugin"); if (destroyOMXPlugin) - destroyOMXPlugin(*it); + destroyOMXPlugin(plugin.mOmx); else - delete *it; - *it = NULL; + delete plugin.mOmx; + + android_unload_sphal_library(plugin.mLibHandle); } mPlugins.clear(); diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 1f3e8c17b4..a720bc958c 100644 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -71,6 +71,16 @@ static const struct { static const size_t kNumComponents = sizeof(kComponents) / sizeof(kComponents[0]); +extern "C" OMXPluginBase* createOMXPlugin() { + ALOGI("createOMXPlugin"); + return new SoftOMXPlugin(); +} + +extern "C" void destroyOMXPlugin(OMXPluginBase* plugin) { + ALOGI("destroyOMXPlugin"); + delete plugin; +} + SoftOMXPlugin::SoftOMXPlugin() { } diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h index 897f287873..93eaef1569 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h @@ -52,13 +52,16 @@ struct OMXMaster : public OMXPluginBase { private: char mProcessName[16]; Mutex mLock; - List mPlugins; + struct Plugin { + OMXPluginBase *mOmx; + void *mLibHandle; + }; + List mPlugins; KeyedVector mPluginByComponentName; KeyedVector mPluginByInstance; - void *mVendorLibHandle; - void addVendorPlugin(); + void addPlatformPlugin(); void addPlugin(const char *libname); void addPlugin(OMXPluginBase *plugin); void clearPlugins(); -- GitLab From 80e1a6dd0dde5302bd00e742d840550288427094 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 30 Apr 2019 15:52:10 -0700 Subject: [PATCH 1375/1530] Move mediaextractor seccomp policy into apex Bug: 112767732 Test: build, run Change-Id: I3d63b6346c1305ba662077eb81b714cbe24ccfb9 Merged-In: I3d63b6346c1305ba662077eb81b714cbe24ccfb9 --- apex/Android.bp | 3 + services/mediaextractor/Android.bp | 71 +++++++++++++++++++ services/mediaextractor/Android.mk | 43 ----------- .../mediaextractor/main_extractorservice.cpp | 5 +- 4 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 services/mediaextractor/Android.bp delete mode 100644 services/mediaextractor/Android.mk diff --git a/apex/Android.bp b/apex/Android.bp index 2cc6fcb854..15ff7705ed 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -33,6 +33,9 @@ apex_defaults { ], }, }, + prebuilts: [ + "mediaextractor.policy", + ], key: "com.android.media.key", certificate: ":com.android.media.certificate", diff --git a/services/mediaextractor/Android.bp b/services/mediaextractor/Android.bp new file mode 100644 index 0000000000..b812244170 --- /dev/null +++ b/services/mediaextractor/Android.bp @@ -0,0 +1,71 @@ +// service library +cc_library_shared { + name: "libmediaextractorservice", + cflags: [ + "-Wall", + "-Werror", + ], + srcs: ["MediaExtractorService.cpp"], + + shared_libs: [ + "libmedia", + "libstagefright", + "libbinder", + "libutils", + ], +} + +// service executable +cc_binary { + name: "mediaextractor", + + srcs: ["main_extractorservice.cpp"], + shared_libs: [ + "libmedia", + "libmediaextractorservice", + "libbinder", + "libutils", + "liblog", + "libavservices_minijail", + ], + target: { + android: { + product_variables: { + malloc_not_svelte: { + // Scudo increases memory footprint, so only enable on + // non-svelte devices. + shared_libs: ["libc_scudo"], + }, + }, + }, + }, + init_rc: ["mediaextractor.rc"], + + include_dirs: ["frameworks/av/media/libmedia"], + + cflags: [ + "-Wall", + "-Werror", + ], +} + +prebuilt_etc { + name: "mediaextractor.policy", + sub_dir: "seccomp_policy", + arch: { + arm: { + src: "seccomp_policy/mediaextractor-arm.policy", + }, + arm64: { + src: "seccomp_policy/mediaextractor-arm64.policy", + }, + x86: { + src: "seccomp_policy/mediaextractor-x86.policy", + }, + x86_64: { + src: "seccomp_policy/mediaextractor-x86_64.policy", + }, + }, + required: ["crash_dump.policy"], +} + diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk deleted file mode 100644 index 9db6ed1838..0000000000 --- a/services/mediaextractor/Android.mk +++ /dev/null @@ -1,43 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -# service library -include $(CLEAR_VARS) -LOCAL_CFLAGS := -Wall -Werror -LOCAL_SRC_FILES := \ - MediaExtractorService.cpp - -LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils -LOCAL_MODULE:= libmediaextractorservice -include $(BUILD_SHARED_LIBRARY) - - -# service executable -include $(CLEAR_VARS) -# seccomp filters are defined for the following architectures: -LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaextractor.policy -LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaextractor.policy -LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy -LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy - -LOCAL_SRC_FILES := main_extractorservice.cpp -ifneq (true, $(filter true, $(MALLOC_SVELTE))) -# Scudo increases memory footprint, so only use on non-svelte configs. -LOCAL_SHARED_LIBRARIES := libc_scudo -endif -LOCAL_SHARED_LIBRARIES += libmedia libmediaextractorservice libbinder libutils \ - liblog libandroidicu libavservices_minijail -LOCAL_MODULE:= mediaextractor -LOCAL_INIT_RC := mediaextractor.rc -LOCAL_C_INCLUDES := frameworks/av/media/libmedia -LOCAL_CFLAGS := -Wall -Werror -include $(BUILD_EXECUTABLE) - -# service seccomp filter -ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86 x86_64)) -include $(CLEAR_VARS) -LOCAL_MODULE := mediaextractor.policy -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy -LOCAL_SRC_FILES := seccomp_policy/mediaextractor-$(TARGET_ARCH).policy -include $(BUILD_PREBUILT) -endif diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp index bb9a56baf5..3c4125be92 100644 --- a/services/mediaextractor/main_extractorservice.cpp +++ b/services/mediaextractor/main_extractorservice.cpp @@ -15,7 +15,6 @@ ** limitations under the License. */ -#include #include #include #include @@ -37,7 +36,7 @@ using namespace android; static const char kSystemSeccompPolicyPath[] = - "/system/etc/seccomp_policy/mediaextractor.policy"; + "/apex/com.android.media/etc/seccomp_policy/mediaextractor.policy"; static const char kVendorSeccompPolicyPath[] = "/vendor/etc/seccomp_policy/mediaextractor.policy"; @@ -58,8 +57,6 @@ int main(int argc __unused, char** argv) SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath); - AIcu_initializeIcuOrDie(); - strcpy(argv[0], "media.extractor"); sp proc(ProcessState::self()); sp sm = defaultServiceManager(); -- GitLab From 5ee9984930753d3231f457be1bbcdba8910b7666 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Fri, 12 Apr 2019 11:55:48 -0700 Subject: [PATCH 1376/1530] Camera: Allow out of order ZSL shutter vs regular shutter HAL ZSL request may be out-of-order compared to normal/reprocess requests. Allow such case to improve shot lantecy. Test: Camera CTS, 3P camera app sanity test. Bug: 120604717 Change-Id: Id994efbe392094cdae694afaa2d159bc9c49d5f0 --- .../device3/Camera3Device.cpp | 28 ++++++++++++++++--- .../libcameraservice/device3/Camera3Device.h | 7 ++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 415b2d80a5..6f9f039f8e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -77,8 +77,10 @@ Camera3Device::Camera3Device(const String8 &id): mTimestampOffset(0), mNextResultFrameNumber(0), mNextReprocessResultFrameNumber(0), + mNextZslStillResultFrameNumber(0), mNextShutterFrameNumber(0), mNextReprocessShutterFrameNumber(0), + mNextZslStillShutterFrameNumber(0), mListener(NULL), mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID), mLastTemplateId(-1), @@ -3457,7 +3459,7 @@ void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata, CaptureResultExtras &resultExtras, CameraMetadata &collectedPartialResult, uint32_t frameNumber, - bool reprocess, + bool reprocess, bool zslStillCapture, const std::vector& physicalMetadatas) { ATRACE_CALL(); if (pendingMetadata.isEmpty()) @@ -3474,6 +3476,14 @@ void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata, return; } mNextReprocessResultFrameNumber = frameNumber + 1; + } else if (zslStillCapture) { + if (frameNumber < mNextZslStillResultFrameNumber) { + SET_ERR("Out-of-order ZSL still capture result metadata submitted! " + "(got frame number %d, expecting %d)", + frameNumber, mNextZslStillResultFrameNumber); + return; + } + mNextZslStillResultFrameNumber = frameNumber + 1; } else { if (frameNumber < mNextResultFrameNumber) { SET_ERR("Out-of-order capture result metadata submitted! " @@ -3728,7 +3738,8 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { metadata = result->result; sendCaptureResult(metadata, request.resultExtras, collectedPartialResult, frameNumber, - hasInputBufferInRequest, request.physicalMetadatas); + hasInputBufferInRequest, request.zslCapture && request.stillCapture, + request.physicalMetadatas); } } @@ -3906,12 +3917,20 @@ void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, // TODO: need to track errors for tighter bounds on expected frame number. if (r.hasInputBuffer) { if (msg.frame_number < mNextReprocessShutterFrameNumber) { - SET_ERR("Shutter notification out-of-order. Expected " + SET_ERR("Reprocess shutter notification out-of-order. Expected " "notification for frame %d, got frame %d", mNextReprocessShutterFrameNumber, msg.frame_number); return; } mNextReprocessShutterFrameNumber = msg.frame_number + 1; + } else if (r.zslCapture && r.stillCapture) { + if (msg.frame_number < mNextZslStillShutterFrameNumber) { + SET_ERR("ZSL still capture shutter notification out-of-order. Expected " + "notification for frame %d, got frame %d", + mNextZslStillShutterFrameNumber, msg.frame_number); + return; + } + mNextZslStillShutterFrameNumber = msg.frame_number + 1; } else { if (msg.frame_number < mNextShutterFrameNumber) { SET_ERR("Shutter notification out-of-order. Expected " @@ -3935,7 +3954,8 @@ void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, // send pending result and buffers sendCaptureResult(r.pendingMetadata, r.resultExtras, r.collectedPartialResult, msg.frame_number, - r.hasInputBuffer, r.physicalMetadatas); + r.hasInputBuffer, r.zslCapture && r.stillCapture, + r.physicalMetadatas); } bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer); returnOutputBuffers(r.pendingOutputBuffers.array(), diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index f8245df821..f32a90b5b2 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -1183,10 +1183,14 @@ class Camera3Device : uint32_t mNextResultFrameNumber; // the minimal frame number of the next reprocess result uint32_t mNextReprocessResultFrameNumber; + // the minimal frame number of the next ZSL still capture result + uint32_t mNextZslStillResultFrameNumber; // the minimal frame number of the next non-reprocess shutter uint32_t mNextShutterFrameNumber; // the minimal frame number of the next reprocess shutter uint32_t mNextReprocessShutterFrameNumber; + // the minimal frame number of the next ZSL still capture shutter + uint32_t mNextZslStillShutterFrameNumber; List mResultQueue; Condition mResultSignal; wp mListener; @@ -1223,7 +1227,8 @@ class Camera3Device : void sendCaptureResult(CameraMetadata &pendingMetadata, CaptureResultExtras &resultExtras, CameraMetadata &collectedPartialResult, uint32_t frameNumber, - bool reprocess, const std::vector& physicalMetadatas); + bool reprocess, bool zslStillCapture, + const std::vector& physicalMetadatas); bool isLastFullResult(const InFlightRequest& inFlightRequest); -- GitLab From 6ce27e5ffa056948b506bf7320a9f9c961150684 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 15 Feb 2019 10:58:05 -0800 Subject: [PATCH 1377/1530] connect mediametrics service to statsd take the collected media.metrics (as they arrive) and push a copy over to statsd, the statistics logging subsystem. gather data, wrap in protobufs apppropriately, and submit it to statsd. Bug: 118782504 Test: statsd's test_drive app Change-Id: Ieb82c43633851075e9eaf65d2a95c8cba87441ea --- .../nuplayer/NuPlayerDriver.cpp | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 4 + services/mediaanalytics/Android.bp | 21 +- .../mediaanalytics/MediaAnalyticsService.cpp | 11 +- .../mediaanalytics/MediaAnalyticsService.h | 3 + services/mediaanalytics/iface_statsd.cpp | 92 ++++++++ services/mediaanalytics/iface_statsd.h | 34 +++ .../mediaanalytics/statsd_audiopolicy.cpp | 133 +++++++++++ .../mediaanalytics/statsd_audiorecord.cpp | 164 +++++++++++++ .../mediaanalytics/statsd_audiothread.cpp | 217 ++++++++++++++++++ services/mediaanalytics/statsd_audiotrack.cpp | 156 +++++++++++++ services/mediaanalytics/statsd_codec.cpp | 185 +++++++++++++++ services/mediaanalytics/statsd_drm.cpp | 107 +++++++++ services/mediaanalytics/statsd_extractor.cpp | 98 ++++++++ services/mediaanalytics/statsd_nuplayer.cpp | 178 ++++++++++++++ services/mediaanalytics/statsd_recorder.cpp | 193 ++++++++++++++++ 16 files changed, 1592 insertions(+), 6 deletions(-) create mode 100644 services/mediaanalytics/iface_statsd.cpp create mode 100644 services/mediaanalytics/iface_statsd.h create mode 100644 services/mediaanalytics/statsd_audiopolicy.cpp create mode 100644 services/mediaanalytics/statsd_audiorecord.cpp create mode 100644 services/mediaanalytics/statsd_audiothread.cpp create mode 100644 services/mediaanalytics/statsd_audiotrack.cpp create mode 100644 services/mediaanalytics/statsd_codec.cpp create mode 100644 services/mediaanalytics/statsd_drm.cpp create mode 100644 services/mediaanalytics/statsd_extractor.cpp create mode 100644 services/mediaanalytics/statsd_nuplayer.cpp create mode 100644 services/mediaanalytics/statsd_recorder.cpp diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 2b813e77f6..865cb2af10 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -635,7 +635,7 @@ void NuPlayerDriver::logMetrics(const char *where) { // re-init in case we prepare() and start() again. delete mAnalyticsItem ; - mAnalyticsItem = MediaAnalyticsItem::create("nuplayer"); + mAnalyticsItem = MediaAnalyticsItem::create(kKeyPlayer); if (mAnalyticsItem) { mAnalyticsItem->setUid(mClientUid); } diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 06e68a9a19..07a5a431f1 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -511,6 +511,7 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) } // including successes gets very verbose + // but once we cut over to westworld, log them all. if (status != NO_ERROR) { static constexpr char kAudioPolicy[] = "audiopolicy"; @@ -571,6 +572,9 @@ status_t AudioPolicyService::startInput(audio_port_handle_t portId) delete item; item = NULL; } + } + + if (status != NO_ERROR) { client->active = false; client->startTimeNs = 0; updateUidStates_l(); diff --git a/services/mediaanalytics/Android.bp b/services/mediaanalytics/Android.bp index c93c120142..72f4b52bee 100644 --- a/services/mediaanalytics/Android.bp +++ b/services/mediaanalytics/Android.bp @@ -7,8 +7,22 @@ cc_binary { srcs: [ "main_mediametrics.cpp", "MediaAnalyticsService.cpp", + "iface_statsd.cpp", + "statsd_audiopolicy.cpp", + "statsd_audiorecord.cpp", + "statsd_audiothread.cpp", + "statsd_audiotrack.cpp", + "statsd_codec.cpp", + "statsd_drm.cpp", + "statsd_extractor.cpp", + "statsd_nuplayer.cpp", + "statsd_recorder.cpp", ], + proto: { + type: "lite", + }, + shared_libs: [ "libcutils", "liblog", @@ -21,10 +35,15 @@ cc_binary { "libmediautils", "libmediametrics", "libstagefright_foundation", + "libstatslog", "libutils", + "libprotobuf-cpp-lite", ], - static_libs: ["libregistermsext"], + static_libs: [ + "libplatformprotos", + "libregistermsext", + ], include_dirs: [ "frameworks/av/media/libstagefright/include", diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp index 06baac962d..3626ad1514 100644 --- a/services/mediaanalytics/MediaAnalyticsService.cpp +++ b/services/mediaanalytics/MediaAnalyticsService.cpp @@ -210,21 +210,24 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem // XXX: if we have a sessionid in the new record, look to make // sure it doesn't appear in the finalized list. - // XXX: this is for security / DOS prevention. - // may also require that we persist the unique sessionIDs - // across boots [instead of within a single boot] if (item->count() == 0) { - // drop empty records + ALOGV("dropping empty record..."); delete item; item = NULL; return MediaAnalyticsItem::SessionIDInvalid; } // save the new record + // + // send a copy to statsd + dump2Statsd(item); + + // and keep our copy for dumpsys MediaAnalyticsItem::SessionID_t id = item->getSessionID(); saveItem(item); mItemsFinalized++; + return id; } diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h index 632c692ce6..6c9cbaae5a 100644 --- a/services/mediaanalytics/MediaAnalyticsService.h +++ b/services/mediaanalytics/MediaAnalyticsService.h @@ -112,6 +112,9 @@ class MediaAnalyticsService : public BnMediaAnalyticsService }; +// hook to send things off to the statsd subsystem +extern bool dump2Statsd(MediaAnalyticsItem *item); + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/mediaanalytics/iface_statsd.cpp b/services/mediaanalytics/iface_statsd.cpp new file mode 100644 index 0000000000..6845f0660f --- /dev/null +++ b/services/mediaanalytics/iface_statsd.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 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 "iface_statsd" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "MediaAnalyticsService.h" +#include "iface_statsd.h" + +#include + +namespace android { + +// set of routines that crack a MediaAnalyticsItem +// and send it off to statsd with the appropriate hooks +// +// each MediaAnalyticsItem type (extractor, codec, nuplayer, etc) +// has its own routine to handle this. +// + +bool enabled_statsd = true; + +struct statsd_hooks { + const char *key; + bool (*handler)(MediaAnalyticsItem *); +}; + +// keep this sorted, so we can do binary searches +struct statsd_hooks statsd_handlers[] = +{ + { "audiopolicy", statsd_audiopolicy }, + { "audiorecord", statsd_audiorecord }, + { "audiothread", statsd_audiothread }, + { "audiotrack", statsd_audiotrack }, + { "codec", statsd_codec}, + { "drm.vendor.Google.WidevineCDM", statsd_widevineCDM }, + { "extractor", statsd_extractor }, + { "mediadrm", statsd_mediadrm }, + { "nuplayer", statsd_nuplayer }, + { "nuplayer2", statsd_nuplayer }, + { "recorder", statsd_recorder }, +}; + + +// give me a record, i'll look at the type and upload appropriately +bool dump2Statsd(MediaAnalyticsItem *item) { + if (item == NULL) return false; + + // get the key + std::string key = item->getKey(); + + if (!enabled_statsd) { + ALOGV("statsd logging disabled for record key=%s", key.c_str()); + return false; + } + + int i; + for(i = 0;i < sizeof(statsd_handlers) / sizeof(statsd_handlers[0]) ; i++) { + if (key == statsd_handlers[i].key) { + return (*statsd_handlers[i].handler)(item); + } + } + return false; +} + +} // namespace android diff --git a/services/mediaanalytics/iface_statsd.h b/services/mediaanalytics/iface_statsd.h new file mode 100644 index 0000000000..f85d303388 --- /dev/null +++ b/services/mediaanalytics/iface_statsd.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 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. + */ + +namespace android { + +extern bool enabled_statsd; + +// component specific dumpers +extern bool statsd_audiopolicy(MediaAnalyticsItem *); +extern bool statsd_audiorecord(MediaAnalyticsItem *); +extern bool statsd_audiothread(MediaAnalyticsItem *); +extern bool statsd_audiotrack(MediaAnalyticsItem *); +extern bool statsd_codec(MediaAnalyticsItem *); +extern bool statsd_extractor(MediaAnalyticsItem *); +extern bool statsd_nuplayer(MediaAnalyticsItem *); +extern bool statsd_recorder(MediaAnalyticsItem *); + +extern bool statsd_mediadrm(MediaAnalyticsItem *); +extern bool statsd_widevineCDM(MediaAnalyticsItem *); + +} // namespace android diff --git a/services/mediaanalytics/statsd_audiopolicy.cpp b/services/mediaanalytics/statsd_audiopolicy.cpp new file mode 100644 index 0000000000..06c4ddebcf --- /dev/null +++ b/services/mediaanalytics/statsd_audiopolicy.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 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 "statsd_audiopolicy" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_audiopolicy(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::AudioPolicyData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + //int32 char kAudioPolicyStatus[] = "android.media.audiopolicy.status"; + int32_t status = -1; + if (item->getInt32("android.media.audiopolicy.status", &status)) { + metrics_proto.set_status(status); + } + //string char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src"; + char *rqst_src = NULL; + if (item->getCString("android.media.audiopolicy.rqst.src", &rqst_src)) { + metrics_proto.set_request_source(rqst_src); + } + //string char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg"; + char *rqst_pkg = NULL; + if (item->getCString("android.media.audiopolicy.rqst.pkg", &rqst_pkg)) { + metrics_proto.set_request_package(rqst_pkg); + } + //int32 char kAudioPolicyRqstSession[] = "android.media.audiopolicy.rqst.session"; + int32_t rqst_session = -1; + if (item->getInt32("android.media.audiopolicy.rqst.session", &rqst_session)) { + metrics_proto.set_request_session(rqst_session); + } + //string char kAudioPolicyRqstDevice[] = "android.media.audiopolicy.rqst.device"; + char *rqst_device = NULL; + if (item->getCString("android.media.audiopolicy.rqst.device", &rqst_device)) { + metrics_proto.set_request_device(rqst_device); + } + + //string char kAudioPolicyActiveSrc[] = "android.media.audiopolicy.active.src"; + char *active_src = NULL; + if (item->getCString("android.media.audiopolicy.active.src", &active_src)) { + metrics_proto.set_active_source(active_src); + } + //string char kAudioPolicyActivePkg[] = "android.media.audiopolicy.active.pkg"; + char *active_pkg = NULL; + if (item->getCString("android.media.audiopolicy.active.pkg", &active_pkg)) { + metrics_proto.set_active_package(active_pkg); + } + //int32 char kAudioPolicyActiveSession[] = "android.media.audiopolicy.active.session"; + int32_t active_session = -1; + if (item->getInt32("android.media.audiopolicy.active.session", &active_session)) { + metrics_proto.set_active_session(active_session); + } + //string char kAudioPolicyActiveDevice[] = "android.media.audiopolicy.active.device"; + char *active_device = NULL; + if (item->getCString("android.media.audiopolicy.active.device", &active_device)) { + metrics_proto.set_active_device(active_device); + } + + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize audipolicy metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOPOLICY_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(rqst_src); + free(rqst_pkg); + free(rqst_device); + free(active_src); + free(active_pkg); + free(active_device); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_audiorecord.cpp b/services/mediaanalytics/statsd_audiorecord.cpp new file mode 100644 index 0000000000..c9edb27f4a --- /dev/null +++ b/services/mediaanalytics/statsd_audiorecord.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 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 "statsd_audiorecord" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_audiorecord(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::AudioRecordData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + char *encoding = NULL; + if (item->getCString("android.media.audiorecord.encoding", &encoding)) { + metrics_proto.set_encoding(encoding); + } + + char *source = NULL; + if (item->getCString("android.media.audiorecord.source", &source)) { + metrics_proto.set_source(source); + } + + int32_t latency = -1; + if (item->getInt32("android.media.audiorecord.latency", &latency)) { + metrics_proto.set_latency(latency); + } + + int32_t samplerate = -1; + if (item->getInt32("android.media.audiorecord.samplerate", &samplerate)) { + metrics_proto.set_samplerate(samplerate); + } + + int32_t channels = -1; + if (item->getInt32("android.media.audiorecord.channels", &channels)) { + metrics_proto.set_channels(channels); + } + + int64_t createdMs = -1; + if (item->getInt64("android.media.audiorecord.createdMs", &createdMs)) { + metrics_proto.set_created_millis(createdMs); + } + + int64_t durationMs = -1; + if (item->getInt64("android.media.audiorecord.durationMs", &durationMs)) { + metrics_proto.set_duration_millis(durationMs); + } + + int32_t count = -1; + if (item->getInt32("android.media.audiorecord.n", &count)) { + metrics_proto.set_count(count); + } + + int32_t errcode = -1; + if (item->getInt32("android.media.audiorecord.errcode", &errcode)) { + metrics_proto.set_error_code(errcode); + } else if (item->getInt32("android.media.audiorecord.lastError.code", &errcode)) { + metrics_proto.set_error_code(errcode); + } + + char *errfunc = NULL; + if (item->getCString("android.media.audiorecord.errfunc", &errfunc)) { + metrics_proto.set_error_function(errfunc); + } else if (item->getCString("android.media.audiorecord.lastError.at", &errfunc)) { + metrics_proto.set_error_function(errfunc); + } + + // portId (int32) + int32_t port_id = -1; + if (item->getInt32("android.media.audiorecord.portId", &port_id)) { + metrics_proto.set_port_id(count); + } + // frameCount (int32) + int32_t frameCount = -1; + if (item->getInt32("android.media.audiorecord.frameCount", &frameCount)) { + metrics_proto.set_frame_count(frameCount); + } + // attributes (string) + char *attributes = NULL; + if (item->getCString("android.media.audiorecord.attributes", &attributes)) { + metrics_proto.set_attributes(attributes); + } + // channelMask (int64) + int64_t channelMask = -1; + if (item->getInt64("android.media.audiorecord.channelMask", &channelMask)) { + metrics_proto.set_channel_mask(channelMask); + } + // startcount (int64) + int64_t startcount = -1; + if (item->getInt64("android.media.audiorecord.startcount", &startcount)) { + metrics_proto.set_start_count(startcount); + } + + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize audiorecord metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(encoding); + free(source); + free(errfunc); + free(attributes); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_audiothread.cpp b/services/mediaanalytics/statsd_audiothread.cpp new file mode 100644 index 0000000000..8232424839 --- /dev/null +++ b/services/mediaanalytics/statsd_audiothread.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2019 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 "statsd_audiothread" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_audiothread(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::AudioThreadData metrics_proto; + +#define MM_PREFIX "android.media.audiothread." + + // flesh out the protobuf we'll hand off with our data + // + char *mytype = NULL; + if (item->getCString(MM_PREFIX "type", &mytype)) { + metrics_proto.set_type(mytype); + } + int32_t framecount = -1; + if (item->getInt32(MM_PREFIX "framecount", &framecount)) { + metrics_proto.set_framecount(framecount); + } + int32_t samplerate = -1; + if (item->getInt32(MM_PREFIX "samplerate", &samplerate)) { + metrics_proto.set_samplerate(samplerate); + } + char *workhist = NULL; + if (item->getCString(MM_PREFIX "workMs.hist", &workhist)) { + metrics_proto.set_work_millis_hist(workhist); + } + char *latencyhist = NULL; + if (item->getCString(MM_PREFIX "latencyMs.hist", &latencyhist)) { + metrics_proto.set_latency_millis_hist(latencyhist); + } + char *warmuphist = NULL; + if (item->getCString(MM_PREFIX "warmupMs.hist", &warmuphist)) { + metrics_proto.set_warmup_millis_hist(warmuphist); + } + int64_t underruns = -1; + if (item->getInt64(MM_PREFIX "underruns", &underruns)) { + metrics_proto.set_underruns(underruns); + } + int64_t overruns = -1; + if (item->getInt64(MM_PREFIX "overruns", &overruns)) { + metrics_proto.set_overruns(overruns); + } + int64_t activeMs = -1; + if (item->getInt64(MM_PREFIX "activeMs", &activeMs)) { + metrics_proto.set_active_millis(activeMs); + } + int64_t durationMs = -1; + if (item->getInt64(MM_PREFIX "durationMs", &durationMs)) { + metrics_proto.set_duration_millis(durationMs); + } + + // item->setInt32(MM_PREFIX "id", (int32_t)mId); // IO handle + int32_t id = -1; + if (item->getInt32(MM_PREFIX "id", &id)) { + metrics_proto.set_id(id); + } + // item->setInt32(MM_PREFIX "portId", (int32_t)mPortId); + int32_t port_id = -1; + if (item->getInt32(MM_PREFIX "portId", &id)) { + metrics_proto.set_port_id(port_id); + } + // item->setCString(MM_PREFIX "type", threadTypeToString(mType)); + char *type = NULL; + if (item->getCString(MM_PREFIX "type", &type)) { + metrics_proto.set_type(type); + } + // item->setInt32(MM_PREFIX "sampleRate", (int32_t)mSampleRate); + int32_t sample_rate = -1; + if (item->getInt32(MM_PREFIX "sampleRate", &sample_rate)) { + metrics_proto.set_sample_rate(sample_rate); + } + // item->setInt64(MM_PREFIX "channelMask", (int64_t)mChannelMask); + int32_t channel_mask = -1; + if (item->getInt32(MM_PREFIX "channelMask", &channel_mask)) { + metrics_proto.set_channel_mask(channel_mask); + } + // item->setCString(MM_PREFIX "encoding", toString(mFormat).c_str()); + char *encoding = NULL; + if (item->getCString(MM_PREFIX "encoding", &encoding)) { + metrics_proto.set_encoding(encoding); + } + // item->setInt32(MM_PREFIX "frameCount", (int32_t)mFrameCount); + int32_t frame_count = -1; + if (item->getInt32(MM_PREFIX "frameCount", &frame_count)) { + metrics_proto.set_frame_count(frame_count); + } + // item->setCString(MM_PREFIX "outDevice", toString(mOutDevice).c_str()); + char *outDevice = NULL; + if (item->getCString(MM_PREFIX "outDevice", &outDevice)) { + metrics_proto.set_output_device(outDevice); + } + // item->setCString(MM_PREFIX "inDevice", toString(mInDevice).c_str()); + char *inDevice = NULL; + if (item->getCString(MM_PREFIX "inDevice", &inDevice)) { + metrics_proto.set_input_device(inDevice); + } + // item->setDouble(MM_PREFIX "ioJitterMs.mean", mIoJitterMs.getMean()); + double iojitters_ms_mean = -1; + if (item->getDouble(MM_PREFIX "ioJitterMs.mean", &iojitters_ms_mean)) { + metrics_proto.set_io_jitter_mean_millis(iojitters_ms_mean); + } + // item->setDouble(MM_PREFIX "ioJitterMs.std", mIoJitterMs.getStdDev()); + double iojitters_ms_std = -1; + if (item->getDouble(MM_PREFIX "ioJitterMs.std", &iojitters_ms_std)) { + metrics_proto.set_io_jitter_stddev_millis(iojitters_ms_std); + } + // item->setDouble(MM_PREFIX "processTimeMs.mean", mProcessTimeMs.getMean()); + double process_time_ms_mean = -1; + if (item->getDouble(MM_PREFIX "processTimeMs.mean", &process_time_ms_mean)) { + metrics_proto.set_process_time_mean_millis(process_time_ms_mean); + } + // item->setDouble(MM_PREFIX "processTimeMs.std", mProcessTimeMs.getStdDev()); + double process_time_ms_std = -1; + if (item->getDouble(MM_PREFIX "processTimeMs.std", &process_time_ms_std)) { + metrics_proto.set_process_time_stddev_millis(process_time_ms_std); + } + // item->setDouble(MM_PREFIX "timestampJitterMs.mean", tsjitter.getMean()); + double timestamp_jitter_ms_mean = -1; + if (item->getDouble(MM_PREFIX "timestampJitterMs.mean", ×tamp_jitter_ms_mean)) { + metrics_proto.set_timestamp_jitter_mean_millis(timestamp_jitter_ms_mean); + } + // item->setDouble(MM_PREFIX "timestampJitterMs.std", tsjitter.getStdDev()); + double timestamp_jitter_ms_stddev = -1; + if (item->getDouble(MM_PREFIX "timestampJitterMs.std", ×tamp_jitter_ms_stddev)) { + metrics_proto.set_timestamp_jitter_stddev_millis(timestamp_jitter_ms_stddev); + } + // item->setDouble(MM_PREFIX "latencyMs.mean", mLatencyMs.getMean()); + double latency_ms_mean = -1; + if (item->getDouble(MM_PREFIX "latencyMs.mean", &latency_ms_mean)) { + metrics_proto.set_latency_mean_millis(latency_ms_mean); + } + // item->setDouble(MM_PREFIX "latencyMs.std", mLatencyMs.getStdDev()); + double latency_ms_stddev = -1; + if (item->getDouble(MM_PREFIX "latencyMs.std", &latency_ms_stddev)) { + metrics_proto.set_latency_stddev_millis(latency_ms_stddev); + } + + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize audiothread metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTHREAD_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(mytype); + free(workhist); + free(latencyhist); + free(warmuphist); + free(type); + free(encoding); + free(inDevice); + free(outDevice); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_audiotrack.cpp b/services/mediaanalytics/statsd_audiotrack.cpp new file mode 100644 index 0000000000..f250ced8d1 --- /dev/null +++ b/services/mediaanalytics/statsd_audiotrack.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2019 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 "statsd_audiotrack" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_audiotrack(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::AudioTrackData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + + // static constexpr char kAudioTrackStreamType[] = "android.media.audiotrack.streamtype"; + // optional string streamType; + char *streamtype = NULL; + if (item->getCString("android.media.audiotrack.streamtype", &streamtype)) { + metrics_proto.set_stream_type(streamtype); + } + + // static constexpr char kAudioTrackContentType[] = "android.media.audiotrack.type"; + // optional string contentType; + char *contenttype = NULL; + if (item->getCString("android.media.audiotrack.type", &contenttype)) { + metrics_proto.set_content_type(contenttype); + } + + // static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage"; + // optional string trackUsage; + char *trackusage = NULL; + if (item->getCString("android.media.audiotrack.usage", &trackusage)) { + metrics_proto.set_track_usage(trackusage); + } + + // static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate"; + // optional int32 samplerate; + int32_t samplerate = -1; + if (item->getInt32("android.media.audiotrack.samplerate", &samplerate)) { + metrics_proto.set_sample_rate(samplerate); + } + + // static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask"; + // optional int64 channelMask; + int64_t channelMask = -1; + if (item->getInt64("android.media.audiotrack.channelmask", &channelMask)) { + metrics_proto.set_channel_mask(channelMask); + } + + // NB: These are not yet exposed as public Java API constants. + // static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes"; + // optional int32 underrunframes; + int32_t underrunframes = -1; + if (item->getInt32("android.media.audiotrack.underrunframes", &underrunframes)) { + metrics_proto.set_underrun_frames(underrunframes); + } + + // static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup"; + // optional int32 startupglitch; + int32_t startupglitch = -1; + if (item->getInt32("android.media.audiotrack.glitch.startup", &startupglitch)) { + metrics_proto.set_startup_glitch(startupglitch); + } + + // portId (int32) + int32_t port_id = -1; + if (item->getInt32("android.media.audiotrack.portId", &port_id)) { + metrics_proto.set_port_id(port_id); + } + // encoding (string) + char *encoding = NULL; + if (item->getCString("android.media.audiotrack.encoding", &encoding)) { + metrics_proto.set_encoding(encoding); + } + // frameCount (int32) + int32_t frame_count = -1; + if (item->getInt32("android.media.audiotrack.frameCount", &frame_count)) { + metrics_proto.set_frame_count(frame_count); + } + // attributes (string) + char *attributes = NULL; + if (item->getCString("android.media.audiotrack.attributes", &attributes)) { + metrics_proto.set_attributes(attributes); + } + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize audiotrack metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(streamtype); + free(contenttype); + free(trackusage); + free(encoding); + free(attributes); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_codec.cpp b/services/mediaanalytics/statsd_codec.cpp new file mode 100644 index 0000000000..dc8e4ef18c --- /dev/null +++ b/services/mediaanalytics/statsd_codec.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2019 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 "statsd_codec" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_codec(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::CodecData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + // android.media.mediacodec.codec string + char *codec = NULL; + if (item->getCString("android.media.mediacodec.codec", &codec)) { + metrics_proto.set_codec(codec); + } + // android.media.mediacodec.mime string + char *mime = NULL; + if (item->getCString("android.media.mediacodec.mime", &mime)) { + metrics_proto.set_mime(mime); + } + // android.media.mediacodec.mode string + char *mode = NULL; + if ( item->getCString("android.media.mediacodec.mode", &mode)) { + metrics_proto.set_mode(mode); + } + // android.media.mediacodec.encoder int32 + int32_t encoder = -1; + if ( item->getInt32("android.media.mediacodec.encoder", &encoder)) { + metrics_proto.set_encoder(encoder); + } + // android.media.mediacodec.secure int32 + int32_t secure = -1; + if ( item->getInt32("android.media.mediacodec.secure", &secure)) { + metrics_proto.set_secure(secure); + } + // android.media.mediacodec.width int32 + int32_t width = -1; + if ( item->getInt32("android.media.mediacodec.width", &width)) { + metrics_proto.set_width(width); + } + // android.media.mediacodec.height int32 + int32_t height = -1; + if ( item->getInt32("android.media.mediacodec.height", &height)) { + metrics_proto.set_height(height); + } + // android.media.mediacodec.rotation-degrees int32 + int32_t rotation = -1; + if ( item->getInt32("android.media.mediacodec.rotation-degrees", &rotation)) { + metrics_proto.set_rotation(rotation); + } + // android.media.mediacodec.crypto int32 (although missing if not needed + int32_t crypto = -1; + if ( item->getInt32("android.media.mediacodec.crypto", &crypto)) { + metrics_proto.set_crypto(crypto); + } + // android.media.mediacodec.profile int32 + int32_t profile = -1; + if ( item->getInt32("android.media.mediacodec.profile", &profile)) { + metrics_proto.set_profile(profile); + } + // android.media.mediacodec.level int32 + int32_t level = -1; + if ( item->getInt32("android.media.mediacodec.level", &level)) { + metrics_proto.set_level(level); + } + // android.media.mediacodec.maxwidth int32 + int32_t maxwidth = -1; + if ( item->getInt32("android.media.mediacodec.maxwidth", &maxwidth)) { + metrics_proto.set_max_width(maxwidth); + } + // android.media.mediacodec.maxheight int32 + int32_t maxheight = -1; + if ( item->getInt32("android.media.mediacodec.maxheight", &maxheight)) { + metrics_proto.set_max_height(maxheight); + } + // android.media.mediacodec.errcode int32 + int32_t errcode = -1; + if ( item->getInt32("android.media.mediacodec.errcode", &errcode)) { + metrics_proto.set_error_code(errcode); + } + // android.media.mediacodec.errstate string + char *errstate = NULL; + if ( item->getCString("android.media.mediacodec.errstate", &errstate)) { + metrics_proto.set_error_state(errstate); + } + // android.media.mediacodec.latency.max int64 + int64_t latency_max = -1; + if ( item->getInt64("android.media.mediacodec.latency.max", &latency_max)) { + metrics_proto.set_latency_max(latency_max); + } + // android.media.mediacodec.latency.min int64 + int64_t latency_min = -1; + if ( item->getInt64("android.media.mediacodec.latency.min", &latency_min)) { + metrics_proto.set_latency_min(latency_min); + } + // android.media.mediacodec.latency.avg int64 + int64_t latency_avg = -1; + if ( item->getInt64("android.media.mediacodec.latency.avg", &latency_avg)) { + metrics_proto.set_latency_avg(latency_avg); + } + // android.media.mediacodec.latency.n int64 + int64_t latency_count = -1; + if ( item->getInt64("android.media.mediacodec.latency.n", &latency_count)) { + metrics_proto.set_latency_count(latency_count); + } + // android.media.mediacodec.latency.unknown int64 + int64_t latency_unknown = -1; + if ( item->getInt64("android.media.mediacodec.latency.unknown", &latency_unknown)) { + metrics_proto.set_latency_unknown(latency_unknown); + } + // android.media.mediacodec.latency.hist NOT EMITTED + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize codec metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_CODEC_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(codec); + free(mime); + free(mode); + free(errstate); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_drm.cpp b/services/mediaanalytics/statsd_drm.cpp new file mode 100644 index 0000000000..902483a045 --- /dev/null +++ b/services/mediaanalytics/statsd_drm.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 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 "statsd_drm" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "MediaAnalyticsService.h" +#include "iface_statsd.h" + +#include + +namespace android { + +// mediadrm +bool statsd_mediadrm(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + char *vendor = NULL; + (void) item->getCString("vendor", &vendor); + char *description = NULL; + (void) item->getCString("description", &description); + char *serialized_metrics = NULL; + (void) item->getCString("serialized_metrics", &serialized_metrics); + + if (enabled_statsd) { + android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL, + serialized_metrics ? strlen(serialized_metrics) + : 0); + android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + vendor, description, + bf_serialized); + } else { + ALOGV("NOT sending: mediadrm private data (len=%zu)", + serialized_metrics ? strlen(serialized_metrics) : 0); + } + + free(vendor); + free(description); + free(serialized_metrics); + return true; +} + +// widevineCDM +bool statsd_widevineCDM(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + char *serialized_metrics = NULL; + (void) item->getCString("serialized_metrics", &serialized_metrics); + + if (enabled_statsd) { + android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : NULL, + serialized_metrics ? strlen(serialized_metrics) + : 0); + android::util::stats_write(android::util::MEDIAMETRICS_DRM_WIDEVINE_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + } else { + ALOGV("NOT sending: widevine private data (len=%zu)", + serialized_metrics ? strlen(serialized_metrics) : 0); + } + + free(serialized_metrics); + return true; +} + +} // namespace android diff --git a/services/mediaanalytics/statsd_extractor.cpp b/services/mediaanalytics/statsd_extractor.cpp new file mode 100644 index 0000000000..395c91202d --- /dev/null +++ b/services/mediaanalytics/statsd_extractor.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 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 "statsd_extractor" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_extractor(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::ExtractorData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + + // android.media.mediaextractor.fmt string + char *fmt = NULL; + if (item->getCString("android.media.mediaextractor.fmt", &fmt)) { + metrics_proto.set_format(fmt); + } + // android.media.mediaextractor.mime string + char *mime = NULL; + if (item->getCString("android.media.mediaextractor.mime", &mime)) { + metrics_proto.set_mime(mime); + } + // android.media.mediaextractor.ntrk int32 + int32_t ntrk = -1; + if (item->getInt32("android.media.mediaextractor.ntrk", &ntrk)) { + metrics_proto.set_tracks(ntrk); + } + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize extractor metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_EXTRACTOR_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(fmt); + free(mime); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_nuplayer.cpp b/services/mediaanalytics/statsd_nuplayer.cpp new file mode 100644 index 0000000000..5ec118a6dc --- /dev/null +++ b/services/mediaanalytics/statsd_nuplayer.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2019 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 "statsd_nuplayer" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +/* + * handles nuplayer AND nuplayer2 + * checks for the union of what the two players generate + */ +bool statsd_nuplayer(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::NuPlayerData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + + // differentiate between nuplayer and nuplayer2 + metrics_proto.set_whichplayer(item->getKey().c_str()); + + char *video_mime = NULL; + if (item->getCString("android.media.mediaplayer.video.mime", &video_mime)) { + metrics_proto.set_video_mime(video_mime); + } + char *video_codec = NULL; + if (item->getCString("android.media.mediaplayer.video.codec", &video_codec)) { + metrics_proto.set_video_codec(video_codec); + } + + int32_t width = -1; + if (item->getInt32("android.media.mediaplayer.width", &width)) { + metrics_proto.set_width(width); + } + int32_t height = -1; + if (item->getInt32("android.media.mediaplayer.height", &height)) { + metrics_proto.set_height(height); + } + + int64_t frames = -1; + if (item->getInt64("android.media.mediaplayer.frames", &frames)) { + metrics_proto.set_frames(frames); + } + int64_t frames_dropped = -1; + if (item->getInt64("android.media.mediaplayer.dropped", &frames_dropped)) { + metrics_proto.set_frames_dropped(frames_dropped); + } + int64_t frames_dropped_startup = -1; + if (item->getInt64("android.media.mediaplayer.startupdropped", &frames_dropped_startup)) { + metrics_proto.set_frames_dropped_startup(frames_dropped_startup); + } + double fps = -1.0; + if (item->getDouble("android.media.mediaplayer.fps", &fps)) { + metrics_proto.set_framerate(fps); + } + + char *audio_mime = NULL; + if (item->getCString("android.media.mediaplayer.audio.mime", &audio_mime)) { + metrics_proto.set_audio_mime(audio_mime); + } + char *audio_codec = NULL; + if (item->getCString("android.media.mediaplayer.audio.codec", &audio_codec)) { + metrics_proto.set_audio_codec(audio_codec); + } + + int64_t duration_ms = -1; + if (item->getInt64("android.media.mediaplayer.durationMs", &duration_ms)) { + metrics_proto.set_duration_millis(duration_ms); + } + int64_t playing_ms = -1; + if (item->getInt64("android.media.mediaplayer.playingMs", &playing_ms)) { + metrics_proto.set_playing_millis(playing_ms); + } + + int32_t err = -1; + if (item->getInt32("android.media.mediaplayer.err", &err)) { + metrics_proto.set_error(err); + } + int32_t error_code = -1; + if (item->getInt32("android.media.mediaplayer.errcode", &error_code)) { + metrics_proto.set_error_code(error_code); + } + char *error_state = NULL; + if (item->getCString("android.media.mediaplayer.errstate", &error_state)) { + metrics_proto.set_error_state(error_state); + } + + char *data_source_type = NULL; + if (item->getCString("android.media.mediaplayer.dataSource", &data_source_type)) { + metrics_proto.set_data_source_type(data_source_type); + } + + int64_t rebufferingMs = -1; + if (item->getInt64("android.media.mediaplayer.rebufferingMs", &rebufferingMs)) { + metrics_proto.set_rebuffering_millis(rebufferingMs); + } + int32_t rebuffers = -1; + if (item->getInt32("android.media.mediaplayer.rebuffers", &rebuffers)) { + metrics_proto.set_rebuffers(rebuffers); + } + int32_t rebufferExit = -1; + if (item->getInt32("android.media.mediaplayer.rebufferExit", &rebufferExit)) { + metrics_proto.set_rebuffer_at_exit(rebufferExit); + } + + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize nuplayer metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_NUPLAYER_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(video_mime); + free(video_codec); + free(audio_mime); + free(audio_codec); + free(error_state); + free(data_source_type); + + return true; +} + +}; diff --git a/services/mediaanalytics/statsd_recorder.cpp b/services/mediaanalytics/statsd_recorder.cpp new file mode 100644 index 0000000000..4d981b44cc --- /dev/null +++ b/services/mediaanalytics/statsd_recorder.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 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 "statsd_recorder" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "MediaAnalyticsService.h" +#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h" +#include "iface_statsd.h" + +namespace android { + +bool statsd_recorder(MediaAnalyticsItem *item) +{ + if (item == NULL) return false; + + // these go into the statsd wrapper + nsecs_t timestamp = item->getTimestamp(); + std::string pkgName = item->getPkgName(); + int64_t pkgVersionCode = item->getPkgVersionCode(); + int64_t mediaApexVersion = 0; + + + // the rest into our own proto + // + ::android::stats::mediametrics::RecorderData metrics_proto; + + // flesh out the protobuf we'll hand off with our data + // + + // string kRecorderAudioMime = "android.media.mediarecorder.audio.mime"; + char *audio_mime = NULL; + if (item->getCString("android.media.mediarecorder.audio.mime", &audio_mime)) { + metrics_proto.set_audio_mime(audio_mime); + } + // string kRecorderVideoMime = "android.media.mediarecorder.video.mime"; + char *video_mime = NULL; + if (item->getCString("android.media.mediarecorder.video.mime", &video_mime)) { + metrics_proto.set_video_mime(video_mime); + } + // int32 kRecorderVideoProfile = "android.media.mediarecorder.video-encoder-profile"; + int32_t videoProfile = -1; + if (item->getInt32("android.media.mediarecorder.video-encoder-profile", &videoProfile)) { + metrics_proto.set_video_profile(videoProfile); + } + // int32 kRecorderVideoLevel = "android.media.mediarecorder.video-encoder-level"; + int32_t videoLevel = -1; + if (item->getInt32("android.media.mediarecorder.video-encoder-level", &videoLevel)) { + metrics_proto.set_video_level(videoLevel); + } + // int32 kRecorderWidth = "android.media.mediarecorder.width"; + int32_t width = -1; + if (item->getInt32("android.media.mediarecorder.width", &width)) { + metrics_proto.set_width(width); + } + // int32 kRecorderHeight = "android.media.mediarecorder.height"; + int32_t height = -1; + if (item->getInt32("android.media.mediarecorder.height", &height)) { + metrics_proto.set_height(height); + } + // int32 kRecorderRotation = "android.media.mediarecorder.rotation"; + int32_t rotation = -1; // default to 0? + if (item->getInt32("android.media.mediarecorder.rotation", &rotation)) { + metrics_proto.set_rotation(rotation); + } + // int32 kRecorderFrameRate = "android.media.mediarecorder.frame-rate"; + int32_t framerate = -1; + if (item->getInt32("android.media.mediarecorder.frame-rate", &framerate)) { + metrics_proto.set_framerate(framerate); + } + + // int32 kRecorderCaptureFps = "android.media.mediarecorder.capture-fps"; + int32_t captureFps = -1; + if (item->getInt32("android.media.mediarecorder.capture-fps", &captureFps)) { + metrics_proto.set_capture_fps(captureFps); + } + // double kRecorderCaptureFpsEnable = "android.media.mediarecorder.capture-fpsenable"; + double captureFpsEnable = -1; + if (item->getDouble("android.media.mediarecorder.capture-fpsenable", &captureFpsEnable)) { + metrics_proto.set_capture_fps_enable(captureFpsEnable); + } + + // int64 kRecorderDurationMs = "android.media.mediarecorder.durationMs"; + int64_t durationMs = -1; + if (item->getInt64("android.media.mediarecorder.durationMs", &durationMs)) { + metrics_proto.set_duration_millis(durationMs); + } + // int64 kRecorderPaused = "android.media.mediarecorder.pausedMs"; + int64_t pausedMs = -1; + if (item->getInt64("android.media.mediarecorder.pausedMs", &pausedMs)) { + metrics_proto.set_paused_millis(pausedMs); + } + // int32 kRecorderNumPauses = "android.media.mediarecorder.NPauses"; + int32_t pausedCount = -1; + if (item->getInt32("android.media.mediarecorder.NPauses", &pausedCount)) { + metrics_proto.set_paused_count(pausedCount); + } + + // int32 kRecorderAudioBitrate = "android.media.mediarecorder.audio-bitrate"; + int32_t audioBitrate = -1; + if (item->getInt32("android.media.mediarecorder.audio-bitrate", &audioBitrate)) { + metrics_proto.set_audio_bitrate(audioBitrate); + } + // int32 kRecorderAudioChannels = "android.media.mediarecorder.audio-channels"; + int32_t audioChannels = -1; + if (item->getInt32("android.media.mediarecorder.audio-channels", &audioChannels)) { + metrics_proto.set_audio_channels(audioChannels); + } + // int32 kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate"; + int32_t audioSampleRate = -1; + if (item->getInt32("android.media.mediarecorder.audio-samplerate", &audioSampleRate)) { + metrics_proto.set_audio_samplerate(audioSampleRate); + } + + // int32 kRecorderMovieTimescale = "android.media.mediarecorder.movie-timescale"; + int32_t movieTimescale = -1; + if (item->getInt32("android.media.mediarecorder.movie-timescale", &movieTimescale)) { + metrics_proto.set_movie_timescale(movieTimescale); + } + // int32 kRecorderAudioTimescale = "android.media.mediarecorder.audio-timescale"; + int32_t audioTimescale = -1; + if (item->getInt32("android.media.mediarecorder.audio-timescale", &audioTimescale)) { + metrics_proto.set_audio_timescale(audioTimescale); + } + // int32 kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale"; + int32_t videoTimescale = -1; + if (item->getInt32("android.media.mediarecorder.video-timescale", &videoTimescale)) { + metrics_proto.set_video_timescale(videoTimescale); + } + + // int32 kRecorderVideoBitrate = "android.media.mediarecorder.video-bitrate"; + int32_t videoBitRate = -1; + if (item->getInt32("android.media.mediarecorder.video-bitrate", &videoBitRate)) { + metrics_proto.set_video_bitrate(videoBitRate); + } + // int32 kRecorderVideoIframeInterval = "android.media.mediarecorder.video-iframe-interval"; + int32_t iFrameInterval = -1; + if (item->getInt32("android.media.mediarecorder.video-iframe-interval", &iFrameInterval)) { + metrics_proto.set_iframe_interval(iFrameInterval); + } + + std::string serialized; + if (!metrics_proto.SerializeToString(&serialized)) { + ALOGE("Failed to serialize recorder metrics"); + return false; + } + + if (enabled_statsd) { + android::util::BytesField bf_serialized( serialized.c_str(), serialized.size()); + (void)android::util::stats_write(android::util::MEDIAMETRICS_RECORDER_REPORTED, + timestamp, pkgName.c_str(), pkgVersionCode, + mediaApexVersion, + bf_serialized); + + } else { + ALOGV("NOT sending: private data (len=%zu)", strlen(serialized.c_str())); + } + + // must free the strings that we were given + free(audio_mime); + free(video_mime); + + return true; +} + +}; -- GitLab From da666891a4bfb9988731a4520b589aaa7beb505d Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 8 Apr 2019 17:25:34 -0700 Subject: [PATCH 1378/1530] media: expand media_codecs.xml to support variants and domains Added support to variant, domain and enabled attributes for . Added support for tag. Bug: 129710438 Change-Id: Icf04665fdf36d8a3e9a74918978b0eb03f0e4f6c --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 7 +- .../media/stagefright/foundation/ADebug.h | 23 +- media/libstagefright/omx/1.0/Omx.cpp | 2 + media/libstagefright/omx/1.0/OmxStore.cpp | 14 +- .../media/stagefright/omx/1.0/OmxStore.h | 14 +- media/libstagefright/xmlparser/Android.bp | 5 +- .../xmlparser/MediaCodecsXmlParser.cpp | 1527 +++++++++++------ .../xmlparser/MediaCodecsXmlParser.h | 117 +- 8 files changed, 1026 insertions(+), 683 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index ead0a9bd3b..5231b6384c 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -248,10 +248,9 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // Obtain Codec2Client std::vector traits = Codec2Client::ListComponents(); - MediaCodecsXmlParser parser( - MediaCodecsXmlParser::defaultSearchDirs, - "media_codecs_c2.xml", - "media_codecs_performance_c2.xml"); + MediaCodecsXmlParser parser; + parser.parseXmlFilesInSearchDirs( + { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" }); if (parser.getParsingStatus() != OK) { ALOGD("XML parser no good"); return OK; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h index a8b88fdd64..ab17a02b58 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h @@ -53,9 +53,11 @@ inline static const char *asString(status_t i, const char *def = "??") { #define LITERAL_TO_STRING_INTERNAL(x) #x #define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x) -#ifdef CHECK -#undef CHECK -#endif +// allow to use CHECK_OP from android-base/logging.h +// TODO: longterm replace this with android-base/logging.h, but there are some nuances, e.g. +// android-base CHECK_OP requires a copy constructor, whereas we don't. +#ifndef CHECK_OP + #define CHECK(condition) \ LOG_ALWAYS_FATAL_IF( \ !(condition), \ @@ -63,10 +65,6 @@ inline static const char *asString(status_t i, const char *def = "??") { __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ " CHECK(" #condition ") failed.") -#ifdef CHECK_OP -#undef CHECK_OP -#endif - #define CHECK_OP(x,y,suffix,op) \ do { \ const auto &a = x; \ @@ -82,15 +80,6 @@ inline static const char *asString(status_t i, const char *def = "??") { } \ } while (false) -#ifdef CHECK_EQ -#undef CHECK_EQ -#undef CHECK_NE -#undef CHECK_LE -#undef CHECK_LT -#undef CHECK_GE -#undef CHECK_GT -#endif - #define CHECK_EQ(x,y) CHECK_OP(x,y,EQ,==) #define CHECK_NE(x,y) CHECK_OP(x,y,NE,!=) #define CHECK_LE(x,y) CHECK_OP(x,y,LE,<=) @@ -98,6 +87,8 @@ inline static const char *asString(status_t i, const char *def = "??") { #define CHECK_GE(x,y) CHECK_OP(x,y,GE,>=) #define CHECK_GT(x,y) CHECK_OP(x,y,GT,>) +#endif + #define TRESPASS(...) \ LOG_ALWAYS_FATAL( \ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp index 121bb1a7be..556a69487f 100644 --- a/media/libstagefright/omx/1.0/Omx.cpp +++ b/media/libstagefright/omx/1.0/Omx.cpp @@ -45,6 +45,8 @@ constexpr size_t kMaxNodeInstances = (1 << 16); Omx::Omx() : mMaster(new OMXMaster()), mParser() { + (void)mParser.parseXmlFilesInSearchDirs(); + (void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath); } Omx::~Omx() { diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp index 89292e84c4..67f478ead0 100644 --- a/media/libstagefright/omx/1.0/OmxStore.cpp +++ b/media/libstagefright/omx/1.0/OmxStore.cpp @@ -38,9 +38,8 @@ using ::android::hardware::media::omx::V1_0::IOmx; OmxStore::OmxStore( const sp &omx, const char* owner, - const char* const* searchDirs, - const char* mainXmlName, - const char* performanceXmlName, + const std::vector &searchDirs, + const std::vector &xmlNames, const char* profilingResultsXmlPath) { // retrieve list of omx nodes std::set nodes; @@ -55,10 +54,11 @@ OmxStore::OmxStore( }); } - MediaCodecsXmlParser parser(searchDirs, - mainXmlName, - performanceXmlName, - profilingResultsXmlPath); + MediaCodecsXmlParser parser; + parser.parseXmlFilesInSearchDirs(xmlNames, searchDirs); + if (profilingResultsXmlPath != nullptr) { + parser.parseXmlPath(profilingResultsXmlPath); + } mParsingStatus = toStatus(parser.getParsingStatus()); const auto& serviceAttributeMap = parser.getServiceAttributeMap(); diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h index 0fcea481e9..15d4699151 100644 --- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h +++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h @@ -46,14 +46,12 @@ struct OmxStore : public IOmxStore { OmxStore( const sp &omx = nullptr, const char* owner = "default", - const char* const* searchDirs - = MediaCodecsXmlParser::defaultSearchDirs, - const char* mainXmlName - = MediaCodecsXmlParser::defaultMainXmlName, - const char* performanceXmlName - = MediaCodecsXmlParser::defaultPerformanceXmlName, - const char* profilingResultsXmlPath - = MediaCodecsXmlParser::defaultProfilingResultsXmlPath); + const std::vector &searchDirs = + MediaCodecsXmlParser::getDefaultSearchDirs(), + const std::vector &xmlFiles = + MediaCodecsXmlParser::getDefaultXmlNames(), + const char *xmlProfilingResultsPath = + MediaCodecsXmlParser::defaultProfilingResultsXmlPath); virtual ~OmxStore(); diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp index 202e964ec0..38de831d2e 100644 --- a/media/libstagefright/xmlparser/Android.bp +++ b/media/libstagefright/xmlparser/Android.bp @@ -20,15 +20,12 @@ cc_library_shared { ], shared_libs: [ + "libbase", "libexpat", "liblog", "libstagefright_omx_utils", ], - header_libs: [ - "libbase_headers", - ], - cflags: [ "-Werror", "-Wall", diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp index 7046f6109e..6a1b9a8931 100644 --- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp +++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp @@ -19,12 +19,18 @@ #include +#include #include #include + #include +#include #include -#include + #include +#include +#include +#include #include #include @@ -32,8 +38,15 @@ namespace android { +using MCXP = MediaCodecsXmlParser; + namespace { +bool fileExists(const std::string &path) { + struct stat fileStat; + return stat(path.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode); +} + /** * Search for a file in a list of search directories. * @@ -44,7 +57,7 @@ namespace { * search continues until the `nullptr` element in `searchDirs` is reached, at * which point the function returns `false`. * - * \param[in] searchDirs Null-terminated array of search paths. + * \param[in] searchDirs array of search paths. * \param[in] fileName Name of the file to search. * \param[out] outPath Full path of the file. `outPath` will hold a valid file * name if the return value of this function is `true`. @@ -52,14 +65,13 @@ namespace { * valid file name; `false` otherwise. */ bool findFileInDirs( - const char* const* searchDirs, - const char *fileName, + const std::vector &searchDirs, + const std::string &fileName, std::string *outPath) { - for (; *searchDirs != nullptr; ++searchDirs) { - *outPath = std::string(*searchDirs) + "/" + fileName; - struct stat fileStat; - if (stat(outPath->c_str(), &fileStat) == 0 && - S_ISREG(fileStat.st_mode)) { + for (const std::string &searchDir : searchDirs) { + std::string path = searchDir + "/" + fileName; + if (fileExists(path)) { + *outPath = path; return true; } } @@ -85,123 +97,435 @@ bool strHasPrefix(const char* test, const char* prefix) { bool parseBoolean(const char* s) { return striEq(s, "y") || striEq(s, "yes") || + striEq(s, "enabled") || striEq(s, "t") || striEq(s, "true") || striEq(s, "1"); } -status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) { - ALOGE("limit '%s' with %s'%s' attribute", name, - (found ? "" : "no "), attr); - return BAD_VALUE; -} -status_t limitError(const char* name, const char *msg) { - ALOGE("limit '%s' %s", name, msg); - return BAD_VALUE; +status_t combineStatus(status_t a, status_t b) { + if (a == NO_INIT) { + return b; + } else if ((a == OK && (b == NAME_NOT_FOUND || b == ALREADY_EXISTS || b == NO_INIT)) + || (b == OK && (a == NAME_NOT_FOUND || a == ALREADY_EXISTS))) { + // ignore NAME_NOT_FOUND and ALREADY_EXIST errors as long as the other error is OK + // also handle OK + NO_INIT here + return OK; + } else { + // prefer the first error result + return a ? : b; + } } -status_t limitInvalidAttr(const char* name, const char* attr, const char* value) { - ALOGE("limit '%s' with invalid '%s' attribute (%s)", name, - attr, value); - return BAD_VALUE; +MCXP::StringSet parseCommaSeparatedStringSet(const char *s) { + MCXP::StringSet result; + for (const char *ptr = s ? : ""; *ptr; ) { + const char *end = strchrnul(ptr, ','); + if (ptr != end) { // skip empty values + result.emplace(ptr, end - ptr); + } + ptr = end + ('\0' != *end); + } + return result; } -}; // unnamed namespace +#define PLOGD(msg, ...) \ + ALOGD(msg " at line %zu of %s", ##__VA_ARGS__, \ + (size_t)::XML_GetCurrentLineNumber(mParser.get()), mPath.c_str()); + +} // unnamed namespace + +struct MediaCodecsXmlParser::Impl { + // status + error message + struct Result { + private: + status_t mStatus; + std::string mError; + + public: + Result(status_t s, std::string error = "") + : mStatus(s), + mError(error) { + if (error.empty() && s) { + error = "Failed (" + std::string(asString(s)) + ")"; + } + } + operator status_t() const { return mStatus; } + std::string error() const { return mError; } + }; + + + // Parsed data + struct Data { + // Service attributes + AttributeMap mServiceAttributeMap; + CodecMap mCodecMap; + Result addGlobal(std::string key, std::string value, bool updating); + }; + + enum Section { + SECTION_TOPLEVEL, + SECTION_SETTINGS, + SECTION_DECODERS, + SECTION_DECODER, + SECTION_DECODER_TYPE, + SECTION_ENCODERS, + SECTION_ENCODER, + SECTION_ENCODER_TYPE, + SECTION_INCLUDE, + SECTION_VARIANT, + SECTION_UNKNOWN, + }; + + // XML parsing state + struct State { + private: + Data *mData; + + // current codec and/or type, plus whether we are updating + struct CodecAndType { + std::string mName; + CodecMap::iterator mCodec; + TypeMap::iterator mType; + bool mUpdating; + }; + + // using vectors as we need to reset their sizes + std::vector mIncludeStack; + std::vector

    mSectionStack; + std::vector mVariantsStack; + std::vector mCurrent; + + public: + State(Data *data); + + Data &data() { return *mData; } + + // used to restore parsing state at XML include boundaries, in case parsing the included + // file fails. + struct RestorePoint { + size_t numIncludes; + size_t numSections; + size_t numVariantSets; + size_t numCodecAndTypes; + }; + + // method manipulating restore points (all state stacks) + RestorePoint createRestorePoint() const { + return { + mIncludeStack.size(), mSectionStack.size(), mVariantsStack.size(), mCurrent.size() + }; + } -constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[]; -constexpr char const* MediaCodecsXmlParser::defaultMainXmlName; -constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName; -constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath; + void restore(RestorePoint rp) { + CHECK_GE(mIncludeStack.size(), rp.numIncludes); + CHECK_GE(mSectionStack.size(), rp.numSections); + CHECK_GE(mVariantsStack.size(), rp.numVariantSets); + CHECK_GE(mCurrent.size(), rp.numCodecAndTypes); -MediaCodecsXmlParser::MediaCodecsXmlParser( - const char* const* searchDirs, - const char* mainXmlName, - const char* performanceXmlName, - const char* profilingResultsXmlPath) : - mParsingStatus(NO_INIT), - mUpdate(false), - mCodecCounter(0) { - std::string path; - if (findFileInDirs(searchDirs, mainXmlName, &path)) { - parseTopLevelXMLFile(path.c_str(), false); - } else { - ALOGE("Cannot find %s", mainXmlName); - mParsingStatus = NAME_NOT_FOUND; + mIncludeStack.resize(rp.numIncludes); + mSectionStack.resize(rp.numSections); + mVariantsStack.resize(rp.numVariantSets); + mCurrent.resize(rp.numCodecAndTypes); + } + + // methods manipulating the include stack + Result enterInclude(const std::string &path); + void exitInclude() { + mIncludeStack.pop_back(); + } + + // methods manipulating the codec/type stack/state + bool inCodec() const { + return !mCurrent.empty() && mCurrent.back().mCodec != mData->mCodecMap.end(); + } + + bool inType() const { + return inCodec() + && mCurrent.back().mType != mCurrent.back().mCodec->second.typeMap.end(); + } + + Result enterMediaCodec(bool encoder, const char *name, const char *type, bool update); + Result enterType(const char *name, bool update); + void exitCodecOrType() { + mCurrent.pop_back(); + } + + // can only be called when inCodec() + MediaCodecsXmlParser::CodecProperties &codec() { + return mCurrent.back().mCodec->second; + } + // can only be called when inCodec() + std::string codecName() const { + return mCurrent.back().mName; + } + // can only be called when inCodec() + bool updating() const { + return mCurrent.back().mUpdating; + } + // can only be called when inType() + MediaCodecsXmlParser::AttributeMap &type() { + return mCurrent.back().mType->second; + } + + // methods manipulating the section stack + Section section() const { + return mSectionStack.back(); + } + Section lastNonIncludeSection() const; + void enterSection(Section s) { + mSectionStack.push_back(s); + } + void exitSection() { + mSectionStack.pop_back(); + CHECK(!mSectionStack.empty()); + } + + // methods manipulating the variants stack + StringSet variants() const { + return mVariantsStack.back(); + } + void enterVariants(StringSet variants) { + mVariantsStack.push_back(variants); + } + void exitVariants() { + mVariantsStack.pop_back(); + } + + // utility methods + + // updates rank, domains, variants and enabledness on the current codec/type + Result updateCodec( + const char *rank, StringSet domains, StringSet variants, const char *enabled); + // adds a key-value attribute detail to the current type of the current codec + void addDetail(const std::string &key, const std::string &value); + }; + + /** XML Parser (state) */ + struct Parser { + State *mState; + + Parser(State *state, std::string path); + + // keep track of the parser state + std::shared_ptr mParser; + std::string mPath; + std::string mHrefBase; + status_t mStatus; + + void parseXmlFile(); + + // XML parser callbacks + static void StartElementHandlerWrapper(void *me, const char *name, const char **attrs); + static void EndElementHandlerWrapper(void *me, const char *name); + + void startElementHandler(const char *name, const char **attrs); + void endElementHandler(const char *name); + + void updateStatus(status_t status); + void logAnyErrors(const Result &status) const; + status_t getStatus() const { return mStatus; } + + status_t addAlias(const char **attrs); + status_t addFeature(const char **attrs); + status_t addLimit(const char **attrs); + status_t addQuirk(const char **attrs, const char *prefix = nullptr); + status_t addSetting(const char **attrs, const char *prefix = nullptr); + status_t enterMediaCodec(const char **attrs, bool encoder); + status_t enterType(const char **attrs); + status_t includeXmlFile(const char **attrs); + status_t limitVariants(const char **attrs); + + status_t updateMediaCodec( + const char *rank, const StringSet &domain, const StringSet &variants, + const char *enabled); + }; + + status_t parseXmlFilesInSearchDirs( + const std::vector &fileNames, + const std::vector &searchDirs); + + status_t parseXmlPath(const std::string &path); + + // Computed longest common prefix + Data mData; + State mState; + + // Role map + mutable std::string mCommonPrefix; + mutable RoleMap mRoleMap; + mutable std::mutex mLock; + + status_t mParsingStatus; + + Impl() + : mState(&mData), + mParsingStatus(NO_INIT) { } - if (findFileInDirs(searchDirs, performanceXmlName, &path)) { - parseTopLevelXMLFile(path.c_str(), true); + + void generateRoleMap() const; + void generateCommonPrefix() const; + + const AttributeMap& getServiceAttributeMap() const { + std::lock_guard guard(mLock); + return mData.mServiceAttributeMap; } - if (profilingResultsXmlPath != nullptr) { - parseTopLevelXMLFile(profilingResultsXmlPath, true); + + const CodecMap& getCodecMap() const { + std::lock_guard guard(mLock); + return mData.mCodecMap; } -} -bool MediaCodecsXmlParser::parseTopLevelXMLFile( - const char *codecs_xml, - bool ignore_errors) { - // get href_base - const char *href_base_end = strrchr(codecs_xml, '/'); - if (href_base_end != nullptr) { - mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1); + const RoleMap& getRoleMap() const; + const char* getCommonPrefix() const; + + status_t getParsingStatus() const { + std::lock_guard guard(mLock); + return mParsingStatus; } +}; + +constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath; + +MediaCodecsXmlParser::MediaCodecsXmlParser() + : mImpl(new Impl()) { +} - mParsingStatus = OK; // keeping this here for safety - mCurrentSection = SECTION_TOPLEVEL; +status_t MediaCodecsXmlParser::parseXmlFilesInSearchDirs( + const std::vector &fileNames, + const std::vector &searchDirs) { + return mImpl->parseXmlFilesInSearchDirs(fileNames, searchDirs); +} - parseXMLFile(codecs_xml); +status_t MediaCodecsXmlParser::parseXmlPath(const std::string &path) { + return mImpl->parseXmlPath(path); +} - if (mParsingStatus != OK) { - ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml); - if (ignore_errors) { - mParsingStatus = OK; - return false; +status_t MediaCodecsXmlParser::Impl::parseXmlFilesInSearchDirs( + const std::vector &fileNames, + const std::vector &searchDirs) { + status_t res = NO_INIT; + for (const std::string fileName : fileNames) { + status_t err = NO_INIT; + std::string path; + if (findFileInDirs(searchDirs, fileName, &path)) { + err = parseXmlPath(path); + } else { + ALOGD("Cannot find %s", path.c_str()); } - mCodecMap.clear(); - return false; + res = combineStatus(res, err); } - return true; + return res; +} + +status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) { + std::lock_guard guard(mLock); + if (!fileExists(path)) { + ALOGD("Cannot find %s", path.c_str()); + mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND); + return NAME_NOT_FOUND; + } + + // save state (even though we should always be at toplevel here) + State::RestorePoint rp = mState.createRestorePoint(); + Parser parser(&mState, path); + parser.parseXmlFile(); + mState.restore(rp); + + if (parser.getStatus() != OK) { + ALOGD("parseXmlPath(%s) failed with %s", path.c_str(), asString(parser.getStatus())); + } + mParsingStatus = combineStatus(mParsingStatus, parser.getStatus()); + return parser.getStatus(); } MediaCodecsXmlParser::~MediaCodecsXmlParser() { } -void MediaCodecsXmlParser::parseXMLFile(const char *path) { +MediaCodecsXmlParser::Impl::State::State(MediaCodecsXmlParser::Impl::Data *data) + : mData(data) { + mSectionStack.emplace_back(SECTION_TOPLEVEL); +} + +MediaCodecsXmlParser::Impl::Section +MediaCodecsXmlParser::Impl::State::lastNonIncludeSection() const { + for (auto it = mSectionStack.end(); it != mSectionStack.begin(); --it) { + if (it[-1] != SECTION_INCLUDE) { + return it[-1]; + } + } + TRESPASS("must have non-include section"); +} + +void MediaCodecsXmlParser::Impl::Parser::updateStatus(status_t status) { + mStatus = combineStatus(mStatus, status); +} + +void MediaCodecsXmlParser::Impl::Parser::logAnyErrors(const Result &status) const { + if (status) { + if (status.error().empty()) { + PLOGD("error %s", asString((status_t)status)); + } else { + PLOGD("%s", status.error().c_str()); + } + } +} + +MediaCodecsXmlParser::Impl::Parser::Parser(State *state, std::string path) + : mState(state), + mPath(path), + mStatus(NO_INIT) { + // determine href_base + std::string::size_type end = path.rfind("/"); + if (end != std::string::npos) { + mHrefBase = path.substr(0, end + 1); + } +} + +void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() { + const char *path = mPath.c_str(); + ALOGD("parsing %s...", path); FILE *file = fopen(path, "r"); if (file == nullptr) { - ALOGW("unable to open media codecs configuration xml file: %s", path); - mParsingStatus = NAME_NOT_FOUND; + ALOGD("unable to open media codecs configuration xml file: %s", path); + mStatus = NAME_NOT_FOUND; return; } - XML_Parser parser = ::XML_ParserCreate(nullptr); - LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed."); + mParser = std::shared_ptr( + ::XML_ParserCreate(nullptr), + [](XML_ParserStruct *parser) { ::XML_ParserFree(parser); }); + LOG_FATAL_IF(!mParser, "XML_MediaCodecsXmlParserCreate() failed."); - ::XML_SetUserData(parser, this); - ::XML_SetElementHandler( - parser, StartElementHandlerWrapper, EndElementHandlerWrapper); + ::XML_SetUserData(mParser.get(), this); + ::XML_SetElementHandler(mParser.get(), StartElementHandlerWrapper, EndElementHandlerWrapper); static constexpr int BUFF_SIZE = 512; - while (mParsingStatus == OK) { - void *buff = ::XML_GetBuffer(parser, BUFF_SIZE); + // updateStatus(OK); + if (mStatus == NO_INIT) { + mStatus = OK; + } + while (mStatus == OK) { + void *buff = ::XML_GetBuffer(mParser.get(), BUFF_SIZE); if (buff == nullptr) { - ALOGE("failed in call to XML_GetBuffer()"); - mParsingStatus = UNKNOWN_ERROR; + ALOGD("failed in call to XML_GetBuffer()"); + mStatus = UNKNOWN_ERROR; break; } int bytes_read = ::fread(buff, 1, BUFF_SIZE, file); if (bytes_read < 0) { - ALOGE("failed in call to read"); - mParsingStatus = ERROR_IO; + ALOGD("failed in call to read"); + mStatus = ERROR_IO; break; } - XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0); + XML_Status status = ::XML_ParseBuffer(mParser.get(), bytes_read, bytes_read == 0); if (status != XML_STATUS_OK) { - ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser))); - mParsingStatus = ERROR_MALFORMED; + PLOGD("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(mParser.get()))); + mStatus = ERROR_MALFORMED; break; } @@ -210,39 +534,47 @@ void MediaCodecsXmlParser::parseXMLFile(const char *path) { } } - ::XML_ParserFree(parser); + mParser.reset(); fclose(file); file = nullptr; } // static -void MediaCodecsXmlParser::StartElementHandlerWrapper( +void MediaCodecsXmlParser::Impl::Parser::StartElementHandlerWrapper( void *me, const char *name, const char **attrs) { - static_cast(me)->startElementHandler(name, attrs); + static_cast(me)->startElementHandler(name, attrs); } // static -void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) { - static_cast(me)->endElementHandler(name); +void MediaCodecsXmlParser::Impl::Parser::EndElementHandlerWrapper(void *me, const char *name) { + static_cast(me)->endElementHandler(name); } -status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) { +status_t MediaCodecsXmlParser::Impl::Parser::includeXmlFile(const char **attrs) { const char *href = nullptr; size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Include: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "href")) { - if (attrs[++i] == nullptr) { - return BAD_VALUE; - } - href = attrs[i]; + href = attrs[++i]; } else { - ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Include: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } + if (href == nullptr) { + PLOGD("Include with no 'href' attribute"); + return BAD_VALUE; + } + // For security reasons and for simplicity, file names can only contain // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml for (i = 0; href[i] != '\0'; i++) { @@ -252,74 +584,114 @@ status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) { (href[i] >= 'a' && href[i] <= 'z')) { continue; } - ALOGE("invalid include file name: %s", href); + PLOGD("invalid include file name: %s", href); return BAD_VALUE; } std::string filename = href; if (filename.compare(0, 13, "media_codecs_") != 0 || filename.compare(filename.size() - 4, 4, ".xml") != 0) { - ALOGE("invalid include file name: %s", href); + PLOGD("invalid include file name: %s", href); return BAD_VALUE; } filename.insert(0, mHrefBase); - status_t oldParsingStatus = mParsingStatus; + Result res = mState->enterInclude(filename); + if (res) { + logAnyErrors(res); + return res; + } - parseXMLFile(filename.c_str()); + // save state so that we can resume even if XML parsing of the included file failed midway + State::RestorePoint rp = mState->createRestorePoint(); + Parser parser(mState, filename); + parser.parseXmlFile(); + mState->restore(rp); + mState->exitInclude(); + return parser.getStatus(); +} - status_t newParsingStatus = mParsingStatus; - mParsingStatus = oldParsingStatus; - return newParsingStatus; +MediaCodecsXmlParser::Impl::Result +MediaCodecsXmlParser::Impl::State::enterInclude(const std::string &fileName) { + if (std::find(mIncludeStack.begin(), mIncludeStack.end(), fileName) + != mIncludeStack.end()) { + return { BAD_VALUE, "recursive include chain" }; + } + mIncludeStack.emplace_back(fileName); + return OK; } -void MediaCodecsXmlParser::startElementHandler( +void MediaCodecsXmlParser::Impl::Parser::startElementHandler( const char *name, const char **attrs) { bool inType = true; + Result err = NO_INIT; + Section section = mState->section(); + + // handle include at any level if (strEq(name, "Include")) { - if (includeXMLFile(attrs) == OK) { - mSectionStack.push_back(mCurrentSection); - mCurrentSection = SECTION_INCLUDE; - } + mState->enterSection(SECTION_INCLUDE); + updateStatus(includeXmlFile(attrs)); return; } - switch (mCurrentSection) { + // handle include section (top level) + if (section == SECTION_INCLUDE) { + if (strEq(name, "Included")) { + return; + } + // imitate prior level + section = mState->lastNonIncludeSection(); + } + + switch (section) { case SECTION_TOPLEVEL: { + Section nextSection; if (strEq(name, "Decoders")) { - mCurrentSection = SECTION_DECODERS; + nextSection = SECTION_DECODERS; } else if (strEq(name, "Encoders")) { - mCurrentSection = SECTION_ENCODERS; + nextSection = SECTION_ENCODERS; } else if (strEq(name, "Settings")) { - mCurrentSection = SECTION_SETTINGS; + nextSection = SECTION_SETTINGS; + } else if (strEq(name, "MediaCodecs")) { + return; + } else { + break; } - break; + mState->enterSection(nextSection); + return; } case SECTION_SETTINGS: { if (strEq(name, "Setting")) { - (void)addSettingFromAttributes(attrs); + err = addSetting(attrs); + } else if (strEq(name, "Variant")) { + err = addSetting(attrs, "variant-"); + } else if (strEq(name, "Domain")) { + err = addSetting(attrs, "domain-"); + } else { + break; } - break; + updateStatus(err); + return; } case SECTION_DECODERS: - { - if (strEq(name, "MediaCodec")) { - (void)addMediaCodecFromAttributes(false /* encoder */, attrs); - mCurrentSection = SECTION_DECODER; - } - break; - } - case SECTION_ENCODERS: { if (strEq(name, "MediaCodec")) { - (void)addMediaCodecFromAttributes(true /* encoder */, attrs); - mCurrentSection = SECTION_ENCODER; + err = enterMediaCodec(attrs, section == SECTION_ENCODERS); + updateStatus(err); + if (err != OK) { // skip this element on error + mState->enterSection(SECTION_UNKNOWN); + } else { + mState->enterVariants(mState->codec().variantSet); + mState->enterSection( + section == SECTION_DECODERS ? SECTION_DECODER : SECTION_ENCODER); + } + return; } break; } @@ -327,14 +699,21 @@ void MediaCodecsXmlParser::startElementHandler( case SECTION_DECODER: case SECTION_ENCODER: { - if (strEq(name, "Quirk") || strEq(name, "Attribute")) { - (void)addQuirk(attrs, name); + if (strEq(name, "Quirk")) { + err = addQuirk(attrs, "quirk::"); + } else if (strEq(name, "Attribute")) { + err = addQuirk(attrs, "attribute::"); + } else if (strEq(name, "Alias")) { + err = addAlias(attrs); } else if (strEq(name, "Type")) { - (void)addTypeFromAttributes(attrs, - (mCurrentSection == SECTION_ENCODER)); - mCurrentSection = - (mCurrentSection == SECTION_DECODER ? - SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); + err = enterType(attrs); + if (err != OK) { // skip this element on error + mState->enterSection(SECTION_UNKNOWN); + } else { + mState->enterSection( + section == SECTION_DECODER + ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); + } } } inType = false; @@ -342,91 +721,102 @@ void MediaCodecsXmlParser::startElementHandler( case SECTION_DECODER_TYPE: case SECTION_ENCODER_TYPE: + case SECTION_VARIANT: { // ignore limits and features specified outside of type - bool outside = !inType && - mCurrentType == mCurrentCodec->second.typeMap.end(); - if (outside && - (strEq(name, "Limit") || strEq(name, "Feature"))) { - ALOGW("ignoring %s specified outside of a Type", name); - } else if (strEq(name, "Alias")) { - (void)addAlias(attrs); + if (!mState->inType() + && (strEq(name, "Limit") || strEq(name, "Feature") || strEq(name, "Variant"))) { + PLOGD("ignoring %s specified outside of a Type", name); + return; } else if (strEq(name, "Limit")) { - (void)addLimit(attrs); + err = addLimit(attrs); } else if (strEq(name, "Feature")) { - (void)addFeature(attrs); + err = addFeature(attrs); + } else if (strEq(name, "Variant") && section != SECTION_VARIANT) { + err = limitVariants(attrs); + mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN); + } else if (inType + && (strEq(name, "Alias") || strEq(name, "Attribute") || strEq(name, "Quirk"))) { + PLOGD("ignoring %s specified not directly in a MediaCodec", name); + return; + } else if (err == NO_INIT) { + break; } - break; + updateStatus(err); + return; } default: break; } + if (section != SECTION_UNKNOWN) { + PLOGD("Ignoring unrecognized tag <%s>", name); + } + mState->enterSection(SECTION_UNKNOWN); } -void MediaCodecsXmlParser::endElementHandler(const char *name) { - switch (mCurrentSection) { - case SECTION_SETTINGS: +void MediaCodecsXmlParser::Impl::Parser::endElementHandler(const char *name) { + // XMLParser handles tag matching, so we really just need to handle the section state here + Section section = mState->section(); + switch (section) { + case SECTION_INCLUDE: { - if (strEq(name, "Settings")) { - mCurrentSection = SECTION_TOPLEVEL; + // this could also be any of: Included, MediaCodecs + if (strEq(name, "Include")) { + mState->exitSection(); + return; } break; } - case SECTION_DECODERS: + case SECTION_SETTINGS: { - if (strEq(name, "Decoders")) { - mCurrentSection = SECTION_TOPLEVEL; + // this could also be any of: Domain, Variant, Setting + if (strEq(name, "Settings")) { + mState->exitSection(); } break; } + case SECTION_DECODERS: case SECTION_ENCODERS: + case SECTION_UNKNOWN: { - if (strEq(name, "Encoders")) { - mCurrentSection = SECTION_TOPLEVEL; - } + mState->exitSection(); break; } case SECTION_DECODER_TYPE: case SECTION_ENCODER_TYPE: { + // this could also be any of: Alias, Limit, Feature if (strEq(name, "Type")) { - mCurrentSection = - (mCurrentSection == SECTION_DECODER_TYPE ? - SECTION_DECODER : SECTION_ENCODER); - - mCurrentType = mCurrentCodec->second.typeMap.end(); + mState->exitSection(); + mState->exitCodecOrType(); } break; } case SECTION_DECODER: - { - if (strEq(name, "MediaCodec")) { - mCurrentSection = SECTION_DECODERS; - mCurrentName.clear(); - } - break; - } - case SECTION_ENCODER: { + // this could also be any of: Alias, Limit, Quirk, Variant if (strEq(name, "MediaCodec")) { - mCurrentSection = SECTION_ENCODERS; - mCurrentName.clear(); + mState->exitSection(); + mState->exitCodecOrType(); + mState->exitVariants(); } break; } - case SECTION_INCLUDE: + case SECTION_VARIANT: { - if (strEq(name, "Include") && (mSectionStack.size() > 0)) { - mCurrentSection = mSectionStack.back(); - mSectionStack.pop_back(); + // this could also be any of: Alias, Limit, Quirk + if (strEq(name, "Variant")) { + mState->exitSection(); + mState->exitVariants(); + return; } break; } @@ -434,264 +824,302 @@ void MediaCodecsXmlParser::endElementHandler(const char *name) { default: break; } - } -status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) { - const char *name = nullptr; - const char *value = nullptr; - const char *update = nullptr; +status_t MediaCodecsXmlParser::Impl::Parser::addSetting(const char **attrs, const char *prefix) { + const char *a_name = nullptr; + const char *a_value = nullptr; + const char *a_update = nullptr; + bool isBoolean = false; size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Setting: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addSettingFromAttributes: name is null"); - return BAD_VALUE; - } - name = attrs[i]; - } else if (strEq(attrs[i], "value")) { - if (attrs[++i] == nullptr) { - ALOGE("addSettingFromAttributes: value is null"); + a_name = attrs[++i]; + } else if (strEq(attrs[i], "value") || strEq(attrs[i], "enabled")) { + if (a_value) { + PLOGD("Setting: redundant attribute '%s'", attrs[i]); return BAD_VALUE; } - value = attrs[i]; + isBoolean = strEq(attrs[i], "enabled"); + a_value = attrs[++i]; } else if (strEq(attrs[i], "update")) { - if (attrs[++i] == nullptr) { - ALOGE("addSettingFromAttributes: update is null"); - return BAD_VALUE; - } - update = attrs[i]; + a_update = attrs[++i]; } else { - ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Setting: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } - if (name == nullptr || value == nullptr) { - ALOGE("addSettingFromAttributes: name or value unspecified"); + if (a_name == nullptr || a_value == nullptr) { + PLOGD("Setting with no 'name' or 'value' attribute"); return BAD_VALUE; } // Boolean values are converted to "0" or "1". - if (strHasPrefix(name, "supports-")) { - value = parseBoolean(value) ? "1" : "0"; + if (strHasPrefix(a_name, "supports-") || isBoolean) { + a_value = parseBoolean(a_value) ? "1" : "0"; } - mUpdate = (update != nullptr) && parseBoolean(update); - auto attribute = mServiceAttributeMap.find(name); + bool update = (a_update != nullptr) && parseBoolean(a_update); + Result res = mState->data().addGlobal(std::string(prefix ? : "") + a_name, a_value, update); + if (res != OK) { + PLOGD("Setting: %s", res.error().c_str()); + } + return res; +} + +MediaCodecsXmlParser::Impl::Result MediaCodecsXmlParser::Impl::Data::addGlobal( + std::string key, std::string value, bool updating) { + auto attribute = mServiceAttributeMap.find(key); if (attribute == mServiceAttributeMap.end()) { // New attribute name - if (mUpdate) { - ALOGE("addSettingFromAttributes: updating non-existing setting"); - return BAD_VALUE; + if (updating) { + return { NAME_NOT_FOUND, "cannot update non-existing setting" }; } - mServiceAttributeMap.insert(Attribute(name, value)); + mServiceAttributeMap.insert(Attribute(key, value)); } else { // Existing attribute name - if (!mUpdate) { - ALOGE("addSettingFromAttributes: adding existing setting"); - } attribute->second = value; + if (!updating) { + return { ALREADY_EXISTS, "updating existing setting" }; + } } return OK; } -status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( - bool encoder, const char **attrs) { - const char *name = nullptr; - const char *type = nullptr; - const char *update = nullptr; - const char *rank = nullptr; +status_t MediaCodecsXmlParser::Impl::Parser::enterMediaCodec( + const char **attrs, bool encoder) { + const char *a_name = nullptr; + const char *a_type = nullptr; + const char *a_update = nullptr; + const char *a_rank = nullptr; + const char *a_domain = nullptr; + const char *a_variant = nullptr; + const char *a_enabled = nullptr; size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("MediaCodec: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addMediaCodecFromAttributes: name is null"); - return BAD_VALUE; - } - name = attrs[i]; + a_name = attrs[++i]; } else if (strEq(attrs[i], "type")) { - if (attrs[++i] == nullptr) { - ALOGE("addMediaCodecFromAttributes: type is null"); - return BAD_VALUE; - } - type = attrs[i]; + a_type = attrs[++i]; } else if (strEq(attrs[i], "update")) { - if (attrs[++i] == nullptr) { - ALOGE("addMediaCodecFromAttributes: update is null"); - return BAD_VALUE; - } - update = attrs[i]; + a_update = attrs[++i]; } else if (strEq(attrs[i], "rank")) { - if (attrs[++i] == nullptr) { - ALOGE("addMediaCodecFromAttributes: rank is null"); - return BAD_VALUE; - } - rank = attrs[i]; + a_rank = attrs[++i]; + } else if (strEq(attrs[i], "domain")) { + a_domain = attrs[++i]; + } else if (strEq(attrs[i], "variant")) { + a_variant = attrs[++i]; + } else if (strEq(attrs[i], "enabled")) { + a_enabled = attrs[++i]; } else { - ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("MediaCodec: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } - if (name == nullptr) { - ALOGE("addMediaCodecFromAttributes: name not found"); + if (a_name == nullptr) { + PLOGD("MediaCodec with no 'name' attribute"); return BAD_VALUE; } - mUpdate = (update != nullptr) && parseBoolean(update); - mCurrentCodec = mCodecMap.find(name); - if (mCurrentCodec == mCodecMap.end()) { // New codec name - if (mUpdate) { - ALOGW("addMediaCodecFromAttributes: cannot update " - "non-existing codec \"%s\".", name); - return BAD_VALUE; + bool update = (a_update != nullptr) && parseBoolean(a_update); + if (a_domain != nullptr) { + // disable codecs with domain by default (unless updating) + if (!a_enabled && !update) { + a_enabled = "false"; + } + } + + Result res = mState->enterMediaCodec(encoder, a_name, a_type, update); + if (res != OK) { + logAnyErrors(res); + return res; + } + + return updateMediaCodec( + a_rank, parseCommaSeparatedStringSet(a_domain), + parseCommaSeparatedStringSet(a_variant), a_enabled); +} + +MediaCodecsXmlParser::Impl::Result +MediaCodecsXmlParser::Impl::State::enterMediaCodec( + bool encoder, const char *name, const char *type, bool updating) { + // store name even in case of an error + CodecMap::iterator codecIt = mData->mCodecMap.find(name); + TypeMap::iterator typeIt; + if (codecIt == mData->mCodecMap.end()) { // New codec name + if (updating) { + return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing codec" }; } // Create a new codec in mCodecMap - mCurrentCodec = mCodecMap.insert( - Codec(name, CodecProperties())).first; + codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first; if (type != nullptr) { - mCurrentType = mCurrentCodec->second.typeMap.insert( - Type(type, AttributeMap())).first; + typeIt = codecIt->second.typeMap.insert(Type(type, AttributeMap())).first; } else { - mCurrentType = mCurrentCodec->second.typeMap.end(); + typeIt = codecIt->second.typeMap.end(); } - mCurrentCodec->second.isEncoder = encoder; - mCurrentCodec->second.order = mCodecCounter++; + codecIt->second.isEncoder = encoder; + codecIt->second.order = mData->mCodecMap.size(); } else { // Existing codec name - if (!mUpdate) { - ALOGW("addMediaCodecFromAttributes: trying to add " - "existing codec \"%s\"", name); - return ALREADY_EXISTS; + if (!updating) { + return { ALREADY_EXISTS, "MediaCodec: cannot add existing codec" }; } if (type != nullptr) { - mCurrentType = mCurrentCodec->second.typeMap.find(type); - if (mCurrentType == mCurrentCodec->second.typeMap.end()) { - ALOGE("addMediaCodecFromAttributes: cannot update " - "non-existing type \"%s\" for codec \"%s\"", - type, name); - return BAD_VALUE; + typeIt = codecIt->second.typeMap.find(type); + if (typeIt == codecIt->second.typeMap.end()) { + return { NAME_NOT_FOUND, "MediaCodec: cannot update non-existing type for codec" }; } } else { // This should happen only when the codec has at most one type. - mCurrentType = mCurrentCodec->second.typeMap.begin(); - if (mCurrentType == mCurrentCodec->second.typeMap.end()) { - ALOGE("addMediaCodecFromAttributes: cannot update " - "codec \"%s\" without type specified", name); - return BAD_VALUE; + typeIt = codecIt->second.typeMap.begin(); + if (typeIt == codecIt->second.typeMap.end() + || codecIt->second.typeMap.size() != 1) { + return { BAD_VALUE, "MediaCodec: cannot update codec without type specified" }; } } } + mCurrent.emplace_back(CodecAndType{name, codecIt, typeIt, updating}); + return OK; +} + +status_t MediaCodecsXmlParser::Impl::Parser::updateMediaCodec( + const char *rank, const StringSet &domains, const StringSet &variants, + const char *enabled) { + CHECK(mState->inCodec()); + CodecProperties &codec = mState->codec(); if (rank != nullptr) { - if (!mCurrentCodec->second.rank.empty() && mCurrentCodec->second.rank != rank) { - ALOGE("addMediaCodecFromAttributes: code \"%s\" rank changed from \"%s\" to \"%s\"", - name, mCurrentCodec->second.rank.c_str(), rank); - return BAD_VALUE; + ALOGD_IF(!codec.rank.empty() && codec.rank != rank, + "codec '%s' rank changed from '%s' to '%s'", + mState->codecName().c_str(), codec.rank.c_str(), rank); + codec.rank = rank; + } + + codec.variantSet = variants; + + for (const std::string &domain : domains) { + if (domain.size() && domain.at(0) == '!') { + codec.domainSet.erase(domain.substr(1)); + } else { + codec.domainSet.emplace(domain); } - mCurrentCodec->second.rank = rank; } + if (enabled != nullptr) { + if (parseBoolean(enabled)) { + codec.quirkSet.erase("attribute::disabled"); + ALOGD("enabling %s", mState->codecName().c_str()); + } else { + codec.quirkSet.emplace("attribute::disabled"); + ALOGD("disabling %s", mState->codecName().c_str()); + } + } return OK; } -status_t MediaCodecsXmlParser::addQuirk(const char **attrs, const char *tag) { - if (mCurrentCodec == mCodecMap.end()) { - return BAD_VALUE; - } - - const char *name = nullptr; +status_t MediaCodecsXmlParser::Impl::Parser::addQuirk(const char **attrs, const char *prefix) { + CHECK(mState->inCodec()); + const char *a_name = nullptr; size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Quirk: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addQuirk: name is null"); - return BAD_VALUE; - } - name = attrs[i]; + a_name = attrs[++i]; } else { - ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Quirk: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } - if (name == nullptr) { - ALOGE("addQuirk: name not found"); + if (a_name == nullptr) { + PLOGD("Quirk with no 'name' attribute"); return BAD_VALUE; } - std::string tagString = tag; - std::transform(tagString.begin(), tagString.end(), tagString.begin(), ::tolower); - tagString.append("::"); - tagString.append(name); - mCurrentCodec->second.quirkSet.emplace(tagString.c_str()); - ALOGI("adding %s to %s", tagString.c_str(), mCurrentCodec->first.c_str()); + std::string key = std::string(prefix ? : "") + a_name; + mState->codec().quirkSet.emplace(key); + ALOGV("adding %s to %s", key.c_str(), mState->codecName().c_str()); return OK; } -status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) { - if (mCurrentCodec == mCodecMap.end()) { - return BAD_VALUE; - } +status_t MediaCodecsXmlParser::Impl::Parser::enterType(const char **attrs) { + CHECK(mState->inCodec()); - const char *name = nullptr; - const char *update = nullptr; + const char *a_name = nullptr; + const char *a_update = nullptr; size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Type: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addTypeFromAttributes: name is null"); - return BAD_VALUE; - } - name = attrs[i]; + a_name = attrs[++i]; } else if (strEq(attrs[i], "update")) { - if (attrs[++i] == nullptr) { - ALOGE("addTypeFromAttributes: update is null"); - return BAD_VALUE; - } - update = attrs[i]; + a_update = attrs[++i]; } else { - ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Type: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } - if (name == nullptr) { + if (a_name == nullptr) { + PLOGD("Type with no 'name' attribute"); return BAD_VALUE; } - mCurrentCodec->second.isEncoder = encoder; - mCurrentType = mCurrentCodec->second.typeMap.find(name); - if (!mUpdate) { - if (mCurrentType != mCurrentCodec->second.typeMap.end()) { - ALOGW("addTypeFromAttributes: trying to update " - "existing type \"%s\"", name); - return ALREADY_EXISTS; + bool update = (a_update != nullptr) && parseBoolean(a_update); + return mState->enterType(a_name, update); +} + +MediaCodecsXmlParser::Impl::Result +MediaCodecsXmlParser::Impl::State::enterType(const char *name, bool update) { + update = update || updating(); // handle parent + + CodecMap::iterator codecIt = mCurrent.back().mCodec; + TypeMap::iterator typeIt = codecIt->second.typeMap.find(name); + if (!update) { + if (typeIt != codecIt->second.typeMap.end()) { + return { ALREADY_EXISTS, "trying to update existing type '" + std::string(name) + "'" }; } - mCurrentType = mCurrentCodec->second.typeMap.insert( - Type(name, AttributeMap())).first; - } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) { - ALOGE("addTypeFromAttributes: updating non-existing type"); - return BAD_VALUE; + typeIt = codecIt->second.typeMap.insert(Type(name, AttributeMap())).first; + } else if (typeIt == codecIt->second.typeMap.end()) { + return { NAME_NOT_FOUND, "addType: updating non-existing type" }; } + mCurrent.push_back({ codecName(), codecIt, typeIt, update }); + CHECK(inType()); return OK; } -status_t MediaCodecsXmlParser::addLimit(const char **attrs) { - if (mCurrentCodec == mCodecMap.end()) { - return BAD_VALUE; - } - if (mCurrentType == mCurrentCodec->second.typeMap.end()) { - return BAD_VALUE; - } - +status_t MediaCodecsXmlParser::Impl::Parser::addLimit(const char **attrs) { + CHECK(mState->inType()); const char* a_name = nullptr; const char* a_default = nullptr; const char* a_in = nullptr; @@ -704,69 +1132,39 @@ status_t MediaCodecsXmlParser::addLimit(const char **attrs) { size_t i = 0; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Limit: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: name is null"); - return BAD_VALUE; - } - a_name = attrs[i]; + a_name = attrs[++i]; } else if (strEq(attrs[i], "default")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: default is null"); - return BAD_VALUE; - } - a_default = attrs[i]; + a_default = attrs[++i]; } else if (strEq(attrs[i], "in")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: in is null"); - return BAD_VALUE; - } - a_in = attrs[i]; + a_in = attrs[++i]; } else if (strEq(attrs[i], "max")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: max is null"); - return BAD_VALUE; - } - a_max = attrs[i]; + a_max = attrs[++i]; } else if (strEq(attrs[i], "min")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: min is null"); - return BAD_VALUE; - } - a_min = attrs[i]; + a_min = attrs[++i]; } else if (strEq(attrs[i], "range")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: range is null"); - return BAD_VALUE; - } - a_range = attrs[i]; + a_range = attrs[++i]; } else if (strEq(attrs[i], "ranges")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: ranges is null"); - return BAD_VALUE; - } - a_ranges = attrs[i]; + a_ranges = attrs[++i]; } else if (strEq(attrs[i], "scale")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: scale is null"); - return BAD_VALUE; - } - a_scale = attrs[i]; + a_scale = attrs[++i]; } else if (strEq(attrs[i], "value")) { - if (attrs[++i] == nullptr) { - ALOGE("addLimit: value is null"); - return BAD_VALUE; - } - a_value = attrs[i]; + a_value = attrs[++i]; } else { - ALOGE("addLimit: unrecognized limit: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Limit: ignoring unrecognized limit: %s", attrs[i]); + ++i; } ++i; } if (a_name == nullptr) { - ALOGE("limit with no 'name' attribute"); + PLOGD("Limit with no 'name' attribute"); return BAD_VALUE; } @@ -774,8 +1172,50 @@ status_t MediaCodecsXmlParser::addLimit(const char **attrs) { // measured-frame-rate, measured-blocks-per-second: range // quality: range + default + [scale] // complexity: range + default - std::string range; - if (strEq(a_name, "aspect-ratio") || + std::string key = a_name, value; + + // don't allow specifying more than one of value, range or min/max + if ((a_value != nullptr) + (a_range != nullptr) + (a_ranges != nullptr) + + (a_min != nullptr || a_max != nullptr) > 1) { + PLOGD("Limit '%s' has multiple 'min'/'max', 'range', 'ranges' or 'value' attributes", + a_name); + return BAD_VALUE; + } + + // Min/max limits (only containing min or max attribute) + // + // Current "max" limits are "channel-count", "concurrent-instances". + // There are no current "min" limits + // + // Range limits. "range" is specified in exactly one of the following forms: + // 1) min-max + // 2) value-value + // 3) range + // + // Current range limits are "aspect-ratio", "bitrate", "block-count", "blocks-per-second", + // "complexity", "frame-rate", "quality", "size", "measured-blocks-per-second", + // "performance-point-*", "measured-frame-rate-*" + // + // Other limits (containing only value or ranges) + // + // Current ranges limit is "sample-rate" + if ((a_min != nullptr) ^ (a_max != nullptr)) { + // min/max limit + if (a_max != nullptr) { + key = "max-" + key; + value = a_max; + } else if (a_min != nullptr) { + key = "min-" + key; + value = a_min; + } + } else if (a_min != nullptr && a_max != nullptr) { + // min-max + key += "-range"; + value = a_min + std::string("-") + a_max; + } else if (a_value != nullptr) { + // value-value or value + value = a_value; + if (strEq(a_name, "aspect-ratio") || strEq(a_name, "bitrate") || strEq(a_name, "block-count") || strEq(a_name, "blocks-per-second") || @@ -786,249 +1226,199 @@ status_t MediaCodecsXmlParser::addLimit(const char **attrs) { strEq(a_name, "measured-blocks-per-second") || strHasPrefix(a_name, "performance-point-") || strHasPrefix(a_name, "measured-frame-rate-")) { - // "range" is specified in exactly one of the following forms: - // 1) min-max - // 2) value-value - // 3) range - if (a_min != nullptr && a_max != nullptr) { - // min-max - if (a_range != nullptr || a_value != nullptr) { - return limitError(a_name, "has 'min' and 'max' as well as 'range' or " - "'value' attributes"); - } - range = a_min; - range += '-'; - range += a_max; - } else if (a_min != nullptr || a_max != nullptr) { - return limitError(a_name, "has only 'min' or 'max' attribute"); - } else if (a_value != nullptr) { - // value-value - if (a_range != nullptr) { - return limitError(a_name, "has both 'range' and 'value' attributes"); - } - range = a_value; - range += '-'; - range += a_value; - } else if (a_range == nullptr) { - return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes"); - } else { - // range - range = a_range; + key += "-range"; + value += std::string("-") + a_value; } + } else if (a_range != nullptr) { + // range + key += "-range"; + value = a_range; + } else if (a_ranges != nullptr) { + // ranges + key += "-ranges"; + value = a_ranges; + } else { + PLOGD("Limit '%s' with no 'range', 'value' or 'min'/'max' attributes", a_name); + return BAD_VALUE; + } - // "aspect-ratio" requires some special treatment. - if (strEq(a_name, "aspect-ratio")) { - // "aspect-ratio" must have "in". - if (a_in == nullptr) { - return limitFoundMissingAttr(a_name, "in", false); - } - // "in" must be either "pixels" or "blocks". - if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) { - return limitInvalidAttr(a_name, "in", a_in); - } - // name will be "pixel-aspect-ratio-range" or - // "block-aspect-ratio-range". - mCurrentType->second[ - std::string(a_in).substr(0, strlen(a_in) - 1) + - "-aspect-ratio-range"] = range; - } else { - // For everything else (apart from "aspect-ratio"), simply append - // "-range" to the name for the range-type property. - mCurrentType->second[std::string(a_name) + "-range"] = range; - - // Only "quality" may have "scale". - if (!strEq(a_name, "quality") && a_scale != nullptr) { - return limitFoundMissingAttr(a_name, "scale"); - } else if (strEq(a_name, "quality")) { - // The default value of "quality-scale" is "linear". - mCurrentType->second["quality-scale"] = a_scale == nullptr ? - "linear" : a_scale; - } + // handle 'in' attribute - this changes the key + if (a_in != nullptr) { + // Currently "aspect-ratio" uses in attribute + const size_t a_in_len = strlen(a_in); + key = std::string(a_in, a_in_len - a_in[a_in_len] == 's') + '-' + key; + } - // "quality" and "complexity" must have "default". - // Other limits must not have "default". - if (strEq(a_name, "quality") || strEq(a_name, "complexity")) { - if (a_default == nullptr) { - return limitFoundMissingAttr(a_name, "default", false); - } - // name will be "quality-default" or "complexity-default". - mCurrentType->second[std::string(a_name) + "-default"] = a_default; - } else if (a_default != nullptr) { - return limitFoundMissingAttr(a_name, "default", true); - } - } + // handle 'scale' attribute - this adds a new detail + if (a_scale != nullptr) { + mState->addDetail(a_name + std::string("-scale"), a_scale); + } else if (strEq(a_name, "quality")) { + // The default value of "quality-scale" is "linear" even if unspecified. + mState->addDetail(a_name + std::string("-scale"), "linear"); + } + + // handle 'default' attribute - this adds a new detail + if (a_default != nullptr) { + mState->addDetail(a_name + std::string("-default"), a_default); + } + + mState->addDetail(key, value); + return OK; +} + +void MediaCodecsXmlParser::Impl::State::addDetail( + const std::string &key, const std::string &value) { + CHECK(inType()); + ALOGI("limit: %s = %s", key.c_str(), value.c_str()); + const StringSet &variants = mVariantsStack.back(); + if (variants.empty()) { + type()[key] = value; } else { - if (a_default != nullptr) { - return limitFoundMissingAttr(a_name, "default"); - } - if (a_in != nullptr) { - return limitFoundMissingAttr(a_name, "in"); - } - if (a_scale != nullptr) { - return limitFoundMissingAttr(a_name, "scale"); - } - if (a_range != nullptr) { - return limitFoundMissingAttr(a_name, "range"); - } - if (a_min != nullptr) { - return limitFoundMissingAttr(a_name, "min"); + for (const std::string &variant : variants) { + type()[variant + ":::" + key] = value; } + } +} - if (a_max != nullptr) { - // "max" must exist if and only if name is "channel-count" or - // "concurrent-instances". - // "min" is not ncessary. - if (strEq(a_name, "channel-count") || - strEq(a_name, "concurrent-instances")) { - mCurrentType->second[std::string("max-") + a_name] = a_max; - } else { - return limitFoundMissingAttr(a_name, "max", false); - } - } else if (strEq(a_name, "channel-count") || - strEq(a_name, "concurrent-instances")) { - return limitFoundMissingAttr(a_name, "max"); - } +status_t MediaCodecsXmlParser::Impl::Parser::limitVariants(const char **attrs) { + const char* a_name = nullptr; - if (a_ranges != nullptr) { - // "ranges" must exist if and only if name is "sample-rate". - if (strEq(a_name, "sample-rate")) { - mCurrentType->second["sample-rate-ranges"] = a_ranges; - } else { - return limitFoundMissingAttr(a_name, "ranges", false); - } - } else if (strEq(a_name, "sample-rate")) { - return limitFoundMissingAttr(a_name, "ranges"); + size_t i = 0; + while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Variant: attribute '%s' is null", attrs[i]); + return BAD_VALUE; } - - if (a_value != nullptr) { - // "value" must exist if and only if name is "alignment" or - // "block-size". - if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) { - mCurrentType->second[a_name] = a_value; - } else { - return limitFoundMissingAttr(a_name, "value", false); - } - } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) { - return limitFoundMissingAttr(a_name, "value", false); + if (strEq(attrs[i], "name")) { + a_name = attrs[++i]; + } else { + PLOGD("Variant: ignoring unrecognized attribute: %s", attrs[i]); + ++i; } - + ++i; } - return OK; -} - -status_t MediaCodecsXmlParser::addFeature(const char **attrs) { - if (mCurrentCodec == mCodecMap.end()) { + if (a_name == nullptr || *a_name == '\0') { + PLOGD("Variant with no or empty 'name' attribute"); return BAD_VALUE; } - if (mCurrentType == mCurrentCodec->second.typeMap.end()) { - return BAD_VALUE; + + StringSet variants; + for (const std::string &variant : parseCommaSeparatedStringSet(a_name)) { + if (mState->variants().count(variant)) { + variants.emplace(variant); + } else { + PLOGD("Variant: variant '%s' not in parent variants", variant.c_str()); + return BAD_VALUE; + } } + mState->enterVariants(variants); + return OK; +} +status_t MediaCodecsXmlParser::Impl::Parser::addFeature(const char **attrs) { + CHECK(mState->inType()); size_t i = 0; - const char *name = nullptr; + const char *a_name = nullptr; int32_t optional = -1; int32_t required = -1; - const char *value = nullptr; + const char *a_value = nullptr; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Feature: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addFeature: name is null"); - return BAD_VALUE; - } - name = attrs[i]; + a_name = attrs[++i]; } else if (strEq(attrs[i], "optional")) { - if (attrs[++i] == nullptr) { - ALOGE("addFeature: optional is null"); - return BAD_VALUE; - } - optional = parseBoolean(attrs[i]) ? 1 : 0; + optional = parseBoolean(attrs[++i]) ? 1 : 0; } else if (strEq(attrs[i], "required")) { - if (attrs[++i] == nullptr) { - ALOGE("addFeature: required is null"); - return BAD_VALUE; - } - required = parseBoolean(attrs[i]) ? 1 : 0; + required = parseBoolean(attrs[++i]) ? 1 : 0; } else if (strEq(attrs[i], "value")) { - if (attrs[++i] == nullptr) { - ALOGE("addFeature: value is null"); - return BAD_VALUE; - } - value = attrs[i]; + a_value = attrs[++i]; } else { - ALOGE("addFeature: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Feature: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } // Every feature must have a name. - if (name == nullptr) { - ALOGE("feature with no 'name' attribute"); + if (a_name == nullptr) { + PLOGD("Feature with no 'name' attribute"); return BAD_VALUE; } - if ((optional != -1) || (required != -1)) { - if (optional == required) { - ALOGE("feature '%s' is both/neither optional and required", name); + if (a_value != nullptr) { + if (optional != -1 || required != -1) { + PLOGD("Feature '%s' has both value and optional/required attributes", a_name); return BAD_VALUE; } - if ((optional == 1) || (required == 1)) { - if (value != nullptr) { - ALOGE("feature '%s' cannot have extra 'value'", name); - return BAD_VALUE; - } - mCurrentType->second[std::string("feature-") + name] = - optional == 1 ? "0" : "1"; - return OK; + } else { + if (optional == required && optional != -1) { + PLOGD("Feature '%s' is both/neither optional and required", a_name); + return BAD_VALUE; } + a_value = (required == 1 || optional == 0) ? "1" : "0"; } - mCurrentType->second[std::string("feature-") + name] = value == nullptr ? - "0" : value; + + mState->addDetail(std::string("feature-") + a_name, a_value ? : "0"); return OK; } -status_t MediaCodecsXmlParser::addAlias(const char **attrs) { +status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) { + CHECK(mState->inCodec()); size_t i = 0; - const char *name = nullptr; + const char *a_name = nullptr; while (attrs[i] != nullptr) { + CHECK((i & 1) == 0); + if (attrs[i + 1] == nullptr) { + PLOGD("Alias: attribute '%s' is null", attrs[i]); + return BAD_VALUE; + } + if (strEq(attrs[i], "name")) { - if (attrs[++i] == nullptr) { - ALOGE("addAlias: name is null"); - return BAD_VALUE; - } - name = attrs[i]; + a_name = attrs[++i]; } else { - ALOGE("addAlias: unrecognized attribute: %s", attrs[i]); - return BAD_VALUE; + PLOGD("Alias: ignoring unrecognized attribute '%s'", attrs[i]); + ++i; } ++i; } // Every feature must have a name. - if (name == nullptr) { - ALOGE("alias with no 'name' attribute"); + if (a_name == nullptr) { + PLOGD("Alias with no 'name' attribute"); return BAD_VALUE; } - mCurrentCodec->second.aliases.emplace_back(name); + mState->codec().aliases.emplace_back(a_name); return OK; } const MediaCodecsXmlParser::AttributeMap& - MediaCodecsXmlParser::getServiceAttributeMap() const { - return mServiceAttributeMap; +MediaCodecsXmlParser::getServiceAttributeMap() const { + return mImpl->getServiceAttributeMap(); } const MediaCodecsXmlParser::CodecMap& - MediaCodecsXmlParser::getCodecMap() const { - return mCodecMap; +MediaCodecsXmlParser::getCodecMap() const { + return mImpl->getCodecMap(); +} + +const MediaCodecsXmlParser::RoleMap& +MediaCodecsXmlParser::getRoleMap() const { + return mImpl->getRoleMap(); } const MediaCodecsXmlParser::RoleMap& - MediaCodecsXmlParser::getRoleMap() const { +MediaCodecsXmlParser::Impl::getRoleMap() const { + std::lock_guard guard(mLock); if (mRoleMap.empty()) { generateRoleMap(); } @@ -1036,6 +1426,11 @@ const MediaCodecsXmlParser::RoleMap& } const char* MediaCodecsXmlParser::getCommonPrefix() const { + return mImpl->getCommonPrefix(); +} + +const char* MediaCodecsXmlParser::Impl::getCommonPrefix() const { + std::lock_guard guard(mLock); if (mCommonPrefix.empty()) { generateCommonPrefix(); } @@ -1043,12 +1438,15 @@ const char* MediaCodecsXmlParser::getCommonPrefix() const { } status_t MediaCodecsXmlParser::getParsingStatus() const { - return mParsingStatus; + return mImpl->getParsingStatus(); } -void MediaCodecsXmlParser::generateRoleMap() const { - for (const auto& codec : mCodecMap) { - const auto& codecName = codec.first; +void MediaCodecsXmlParser::Impl::generateRoleMap() const { + for (const auto& codec : mData.mCodecMap) { + const auto &codecName = codec.first; + if (codecName == "") { + continue; + } bool isEncoder = codec.second.isEncoder; size_t order = codec.second.order; std::string rank = codec.second.rank; @@ -1116,14 +1514,14 @@ void MediaCodecsXmlParser::generateRoleMap() const { } } -void MediaCodecsXmlParser::generateCommonPrefix() const { - if (mCodecMap.empty()) { +void MediaCodecsXmlParser::Impl::generateCommonPrefix() const { + if (mData.mCodecMap.empty()) { return; } - auto i = mCodecMap.cbegin(); + auto i = mData.mCodecMap.cbegin(); auto first = i->first.cbegin(); auto last = i->first.cend(); - for (++i; i != mCodecMap.cend(); ++i) { + for (++i; i != mData.mCodecMap.cend(); ++i) { last = std::mismatch( first, last, i->first.cbegin(), i->first.cend()).first; } @@ -1131,4 +1529,3 @@ void MediaCodecsXmlParser::generateCommonPrefix() const { } } // namespace android - diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h index 7a986b7a72..b666de4956 100644 --- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h +++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h @@ -19,34 +19,31 @@ #include #include -#include -#include -#include -#include #include +#include +#include +#include #include +struct XML_ParserStruct; // from expat library + namespace android { class MediaCodecsXmlParser { public: // Treblized media codec list will be located in /odm/etc or /vendor/etc. - static constexpr char const* defaultSearchDirs[] = - {"/odm/etc", "/vendor/etc", "/etc", nullptr}; - static constexpr char const* defaultMainXmlName = - "media_codecs.xml"; - static constexpr char const* defaultPerformanceXmlName = - "media_codecs_performance.xml"; + static std::vector getDefaultSearchDirs() { + return { "/odm/etc", "/vendor/etc", "/etc" }; + } + static std::vector getDefaultXmlNames() { + return { "media_codecs.xml", "media_codecs_performance.xml" }; + } static constexpr char const* defaultProfilingResultsXmlPath = "/data/misc/media/media_codecs_profiling_results.xml"; - MediaCodecsXmlParser( - const char* const* searchDirs = defaultSearchDirs, - const char* mainXmlName = defaultMainXmlName, - const char* performanceXmlName = defaultPerformanceXmlName, - const char* profilingResultsXmlPath = defaultProfilingResultsXmlPath); + MediaCodecsXmlParser(); ~MediaCodecsXmlParser(); typedef std::pair Attribute; @@ -55,7 +52,7 @@ public: typedef std::pair Type; typedef std::map TypeMap; - typedef std::set QuirkSet; + typedef std::set StringSet; /** * Properties of a codec (node) @@ -63,7 +60,9 @@ public: struct CodecProperties { bool isEncoder; ///< Whether this codec is an encoder or a decoder size_t order; ///< Order of appearance in the file (starting from 0) - QuirkSet quirkSet; ///< Set of quirks requested by this codec + StringSet quirkSet; ///< Set of quirks requested by this codec + StringSet domainSet; ///< Set of domains this codec is in + StringSet variantSet; ///< Set of variants this codec is enabled on TypeMap typeMap; ///< Map of types supported by this codec std::vector aliases; ///< Name aliases for this codec std::string rank; ///< Rank of this codec. This is a numeric string. @@ -119,70 +118,31 @@ public: status_t getParsingStatus() const; -private: - enum Section { - SECTION_TOPLEVEL, - SECTION_SETTINGS, - SECTION_DECODERS, - SECTION_DECODER, - SECTION_DECODER_TYPE, - SECTION_ENCODERS, - SECTION_ENCODER, - SECTION_ENCODER_TYPE, - SECTION_INCLUDE, - }; - - status_t mParsingStatus; - Section mCurrentSection; - bool mUpdate; - std::vector
    mSectionStack; - std::string mHrefBase; - - // Service attributes - AttributeMap mServiceAttributeMap; - - // Codec attributes - std::string mCurrentName; - std::set mCodecSet; - Codec mCodecListTemp[2048]; - CodecMap mCodecMap; - size_t mCodecCounter; - CodecMap::iterator mCurrentCodec; - TypeMap::iterator mCurrentType; - - // Role map - mutable RoleMap mRoleMap; - - // Computed longest common prefix - mutable std::string mCommonPrefix; - - bool parseTopLevelXMLFile(const char *path, bool ignore_errors = false); - - void parseXMLFile(const char *path); - - static void StartElementHandlerWrapper( - void *me, const char *name, const char **attrs); - - static void EndElementHandlerWrapper(void *me, const char *name); - - void startElementHandler(const char *name, const char **attrs); - void endElementHandler(const char *name); + /** + * Parse top level XML files from a group of search directories. + * + * @param xmlFiles ordered list of XML file names (no paths) + * @param searchDirs ordered list of paths to consider + * + * @return parsing status + */ + status_t parseXmlFilesInSearchDirs( + const std::vector &xmlFiles = getDefaultXmlNames(), + const std::vector &searchDirs = getDefaultSearchDirs()); - status_t includeXMLFile(const char **attrs); - status_t addSettingFromAttributes(const char **attrs); - status_t addMediaCodecFromAttributes(bool encoder, const char **attrs); - void addMediaCodec(bool encoder, const char *name, - const char *type = nullptr); - status_t addQuirk(const char **attrs, const char *tag); - status_t addTypeFromAttributes(const char **attrs, bool encoder); - status_t addAlias(const char **attrs); - status_t addLimit(const char **attrs); - status_t addFeature(const char **attrs); - void addType(const char *name); + /** + * Parse a top level XML file. + * + * @param path XML file path + * + * @return parsing status + */ + status_t parseXmlPath(const std::string &path); - void generateRoleMap() const; - void generateCommonPrefix() const; +private: + struct Impl; + std::shared_ptr mImpl; MediaCodecsXmlParser(const MediaCodecsXmlParser&) = delete; MediaCodecsXmlParser& operator=(const MediaCodecsXmlParser&) = delete; @@ -191,4 +151,3 @@ private: } // namespace android #endif // MEDIA_STAGEFRIGHT_XMLPARSER_H_ - -- GitLab From 882d2388062726f11c32ec712fad608dfd939a09 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 8 Apr 2019 17:47:33 -0700 Subject: [PATCH 1379/1530] media: add media_codecs.xml to apex module Bug: 129710438 Change-Id: Idadb0de735b5784637e9f37e55515ba9f7e645a2 --- apex/Android.bp | 1 + media/libstagefright/data/Android.bp | 6 + media/libstagefright/data/media_codecs_sw.xml | 321 ++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 media/libstagefright/data/Android.bp create mode 100644 media/libstagefright/data/media_codecs_sw.xml diff --git a/apex/Android.bp b/apex/Android.bp index ec0efe6985..5b3fcde226 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -66,6 +66,7 @@ apex_defaults { "com.android.media.swcodec-mediaswcodec.rc", "com.android.media.swcodec-ld.config.txt", "mediaswcodec.policy", + "mediaswcodec.xml", ], use_vendor: true, key: "com.android.media.swcodec.key", diff --git a/media/libstagefright/data/Android.bp b/media/libstagefright/data/Android.bp new file mode 100644 index 0000000000..616b4b3639 --- /dev/null +++ b/media/libstagefright/data/Android.bp @@ -0,0 +1,6 @@ +prebuilt_etc { + name: "mediaswcodec.xml", + src: "media_codecs_sw.xml", + filename: "media_codecs.xml", + installable: false, +} diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml new file mode 100644 index 0000000000..7ee1f4daf7 --- /dev/null +++ b/media/libstagefright/data/media_codecs_sw.xml @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From 424cfb5c35aedac5973097edb093a3cca3f8cdf7 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 8 Apr 2019 17:48:00 -0700 Subject: [PATCH 1380/1530] codec2: use media_codecs from apex and support variants/domains - prefer XML from apex - allow vendors/OEMs to override the enablement of codecs, media types domains and variant selectors - propagate global settings from this info builder Bug: 129710438 Change-Id: I0858ae982b4228ad1498cc1e7175956d8f5737af --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 157 +++++++++++++++++++- 1 file changed, 152 insertions(+), 5 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 5231b6384c..1919597a07 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -212,6 +212,76 @@ void addSupportedColorFormats( } } +class Switch { + enum Flags : uint8_t { + // flags + IS_ENABLED = (1 << 0), + BY_DEFAULT = (1 << 1), + }; + + constexpr Switch(uint8_t flags) : mFlags(flags) {} + + uint8_t mFlags; + +public: + // have to create class due to this bool conversion operator... + constexpr operator bool() const { + return mFlags & IS_ENABLED; + } + + constexpr Switch operator!() const { + return Switch(mFlags ^ IS_ENABLED); + } + + static constexpr Switch DISABLED() { return 0; }; + static constexpr Switch ENABLED() { return IS_ENABLED; }; + static constexpr Switch DISABLED_BY_DEFAULT() { return BY_DEFAULT; }; + static constexpr Switch ENABLED_BY_DEFAULT() { return IS_ENABLED | BY_DEFAULT; }; + + const char *toString(const char *def = "??") const { + switch (mFlags) { + case 0: return "0"; + case IS_ENABLED: return "1"; + case BY_DEFAULT: return "(0)"; + case IS_ENABLED | BY_DEFAULT: return "(1)"; + default: return def; + } + } + +}; + +const char *asString(const Switch &s, const char *def = "??") { + return s.toString(def); +} + +Switch isSettingEnabled( + std::string setting, const MediaCodecsXmlParser::AttributeMap &settings, + Switch def = Switch::DISABLED_BY_DEFAULT()) { + const auto enablement = settings.find(setting); + if (enablement == settings.end()) { + return def; + } + return enablement->second == "1" ? Switch::ENABLED() : Switch::DISABLED(); +} + +Switch isVariantEnabled( + std::string variant, const MediaCodecsXmlParser::AttributeMap &settings) { + return isSettingEnabled("variant-" + variant, settings); +} + +Switch isVariantExpressionEnabled( + std::string exp, const MediaCodecsXmlParser::AttributeMap &settings) { + if (!exp.empty() && exp.at(0) == '!') { + return !isVariantEnabled(exp.substr(1, exp.size() - 1), settings); + } + return isVariantEnabled(exp, settings); +} + +Switch isDomainEnabled( + std::string domain, const MediaCodecsXmlParser::AttributeMap &settings) { + return isSettingEnabled("domain-" + domain, settings); +} + } // unnamed namespace status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { @@ -248,14 +318,33 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // Obtain Codec2Client std::vector traits = Codec2Client::ListComponents(); + // parse APEX XML first, followed by vendor XML MediaCodecsXmlParser parser; + parser.parseXmlFilesInSearchDirs( + parser.getDefaultXmlNames(), + { "/apex/com.android.media.swcodec/etc" }); + + // TODO: remove these c2-specific files once product moved to default file names parser.parseXmlFilesInSearchDirs( { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" }); + + // parse default XML files + parser.parseXmlFilesInSearchDirs(); + if (parser.getParsingStatus() != OK) { ALOGD("XML parser no good"); return OK; } + MediaCodecsXmlParser::AttributeMap settings = parser.getServiceAttributeMap(); + for (const auto &v : settings) { + if (!hasPrefix(v.first, "media-type-") + && !hasPrefix(v.first, "domain-") + && !hasPrefix(v.first, "variant-")) { + writer->addGlobalSetting(v.first.c_str(), v.second.c_str()); + } + } + for (const Traits& trait : traits) { C2Component::rank_t rank = trait.rank; @@ -340,12 +429,42 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { break; } + const MediaCodecsXmlParser::CodecProperties &codec = + parser.getCodecMap().at(nameOrAlias); + + // verify that either the codec is explicitly enabled, or one of its domains is + bool codecEnabled = codec.quirkSet.find("attribute::disabled") == codec.quirkSet.end(); + if (!codecEnabled) { + for (const std::string &domain : codec.domainSet) { + const Switch enabled = isDomainEnabled(domain, settings); + ALOGV("codec entry '%s' is in domain '%s' that is '%s'", + nameOrAlias.c_str(), domain.c_str(), asString(enabled)); + if (enabled) { + codecEnabled = true; + break; + } + } + } + // if codec has variants, also check that at least one of them is enabled + bool variantEnabled = codec.variantSet.empty(); + for (const std::string &variant : codec.variantSet) { + const Switch enabled = isVariantExpressionEnabled(variant, settings); + ALOGV("codec entry '%s' has a variant '%s' that is '%s'", + nameOrAlias.c_str(), variant.c_str(), asString(enabled)); + if (enabled) { + variantEnabled = true; + break; + } + } + if (!codecEnabled || !variantEnabled) { + ALOGD("codec entry for '%s' is disabled", nameOrAlias.c_str()); + continue; + } + ALOGV("adding codec entry for '%s'", nameOrAlias.c_str()); std::unique_ptr codecInfo = writer->addMediaCodecInfo(); codecInfo->setName(nameOrAlias.c_str()); codecInfo->setOwner(("codec2::" + trait.owner).c_str()); - const MediaCodecsXmlParser::CodecProperties &codec = - parser.getCodecMap().at(nameOrAlias); bool encoder = trait.kind == C2Component::KIND_ENCODER; typename std::underlying_type::type attrs = 0; @@ -372,6 +491,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { rank = xmlRank; } } + ALOGV("rank: %u", (unsigned)rank); codecInfo->setRank(rank); for (const std::string &alias : codec.aliases) { @@ -381,12 +501,39 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) { const std::string &mediaType = typeIt->first; + const Switch typeEnabled = isSettingEnabled( + "media-type-" + mediaType, settings, Switch::ENABLED_BY_DEFAULT()); + const Switch domainTypeEnabled = isSettingEnabled( + "media-type-" + mediaType + (encoder ? "-encoder" : "-decoder"), + settings, Switch::ENABLED_BY_DEFAULT()); + ALOGV("type '%s-%s' is '%s/%s'", + mediaType.c_str(), (encoder ? "encoder" : "decoder"), + asString(typeEnabled), asString(domainTypeEnabled)); + if (!typeEnabled || !domainTypeEnabled) { + ALOGD("media type '%s' for codec entry '%s' is disabled", mediaType.c_str(), + nameOrAlias.c_str()); + continue; + } + + ALOGI("adding type '%s'", typeIt->first.c_str()); const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second; std::unique_ptr caps = codecInfo->addMediaType(mediaType.c_str()); - for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) { - std::string key, value; - std::tie(key, value) = *attrIt; + for (const auto &v : attrMap) { + std::string key = v.first; + std::string value = v.second; + + size_t variantSep = key.find(":::"); + if (variantSep != std::string::npos) { + std::string variant = key.substr(0, variantSep); + const Switch enabled = isVariantExpressionEnabled(variant, settings); + ALOGV("variant '%s' is '%s'", variant.c_str(), asString(enabled)); + if (!enabled) { + continue; + } + key = key.substr(variantSep + 3); + } + if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) { int32_t intValue = 0; // Ignore trailing bad characters and default to 0. -- GitLab From 83dd509b5191b8c75a3d14c44abab346596736cb Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 3 May 2019 16:14:34 -0700 Subject: [PATCH 1381/1530] apexes: set maxSdk to 29 (Q) Test: build & adb install to beta3 devices Bug: 127495630 Change-Id: I88600ee6823fcd066c17262bc998724d605f502f --- apex/AndroidManifest-media.xml | 2 ++ apex/AndroidManifest-swcodec.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml index 17d3f3ab55..78ed0edcdf 100644 --- a/apex/AndroidManifest-media.xml +++ b/apex/AndroidManifest-media.xml @@ -18,8 +18,10 @@ package="com.android.media"> + diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml index bd20dc0b8d..9558644bb3 100644 --- a/apex/AndroidManifest-swcodec.xml +++ b/apex/AndroidManifest-swcodec.xml @@ -18,8 +18,10 @@ package="com.android.media.swcodec"> + -- GitLab From a503326e7af89f27c2e65c2c4447e8105547274a Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Mon, 6 May 2019 14:15:12 -0700 Subject: [PATCH 1382/1530] DrmHal: add key status usable_in_future Bug:116738851 Test: DrmMetrics_test Change-Id: Ifdd63ce94d3e145840b155a982612ba980cc456d --- drm/libmediadrm/Android.bp | 2 ++ drm/libmediadrm/DrmHal.cpp | 16 +++++++++++++++- drm/libmediadrm/DrmMetrics.cpp | 2 +- drm/libmediadrm/tests/DrmMetrics_test.cpp | 2 +- .../plugins/clearkey/hidl/InitDataParser.cpp | 12 ++++++------ .../plugins/clearkey/hidl/include/DrmPlugin.h | 10 ++++++++++ media/libmedia/include/media/DrmHal.h | 8 ++++++-- media/libmedia/include/media/DrmMetrics.h | 3 ++- 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index 01efb223c0..d6db1d4e2b 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -59,6 +59,7 @@ cc_library_shared { shared_libs: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", + "android.hardware.drm@1.2", "libbinder", "libhidlbase", "liblog", @@ -89,6 +90,7 @@ cc_library_shared { shared_libs: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", + "android.hardware.drm@1.2", "libbase", "libbinder", "libhidlbase", diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 5888af0e58..919f4ee1df 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -40,7 +40,6 @@ #include using drm::V1_0::KeyedVector; -using drm::V1_0::KeyStatusType; using drm::V1_0::KeyRequestType; using drm::V1_0::KeyType; using drm::V1_0::KeyValue; @@ -51,6 +50,7 @@ using drm::V1_1::HdcpLevel; using drm::V1_1::SecureStopRelease; using drm::V1_1::SecurityLevel; using drm::V1_2::KeySetId; +using drm::V1_2::KeyStatusType; using ::android::hardware::drm::V1_1::DrmMetricGroup; using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; @@ -517,6 +517,17 @@ Return DrmHal::sendExpirationUpdate(const hidl_vec& sessionId, } Return DrmHal::sendKeysChange(const hidl_vec& sessionId, + const hidl_vec& keyStatusList_V1_0, bool hasNewUsableKey) { + std::vector keyStatusVec; + for (const auto &keyStatus_V1_0 : keyStatusList_V1_0) { + keyStatusVec.push_back({keyStatus_V1_0.keyId, + static_cast(keyStatus_V1_0.type)}); + } + hidl_vec keyStatusList_V1_2(keyStatusVec); + return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey); +} + +Return DrmHal::sendKeysChange_1_2(const hidl_vec& sessionId, const hidl_vec& keyStatusList, bool hasNewUsableKey) { mEventLock.lock(); @@ -546,6 +557,9 @@ Return DrmHal::sendKeysChange(const hidl_vec& sessionId, case KeyStatusType::STATUSPENDING: type = DrmPlugin::kKeyStatusType_StatusPending; break; + case KeyStatusType::USABLEINFUTURE: + type = DrmPlugin::kKeyStatusType_UsableInFuture; + break; case KeyStatusType::INTERNALERROR: default: type = DrmPlugin::kKeyStatusType_InternalError; diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp index 4fed707017..308080275b 100644 --- a/drm/libmediadrm/DrmMetrics.cpp +++ b/drm/libmediadrm/DrmMetrics.cpp @@ -32,7 +32,7 @@ using ::android::drm_metrics::DrmFrameworkMetrics; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::drm::V1_0::EventType; -using ::android::hardware::drm::V1_0::KeyStatusType; +using ::android::hardware::drm::V1_2::KeyStatusType; using ::android::hardware::drm::V1_1::DrmMetricGroup; using ::android::os::PersistableBundle; diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp index 64aa9d0c99..5c8a1b0064 100644 --- a/drm/libmediadrm/tests/DrmMetrics_test.cpp +++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp @@ -30,7 +30,7 @@ using ::android::drm_metrics::DrmFrameworkMetrics; using ::android::hardware::hidl_vec; using ::android::hardware::drm::V1_0::EventType; -using ::android::hardware::drm::V1_0::KeyStatusType; +using ::android::hardware::drm::V1_2::KeyStatusType; using ::android::hardware::drm::V1_0::Status; using ::android::hardware::drm::V1_1::DrmMetricGroup; using ::android::os::PersistableBundle; diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp index 8ebb42bc7b..b988ce03ca 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp @@ -51,16 +51,16 @@ Status InitDataParser::parse(const std::vector& initData, // Build a list of the key IDs std::vector keyIds; - if (mimeType == kIsoBmffVideoMimeType || - mimeType == kIsoBmffAudioMimeType || - mimeType == kCencInitDataFormat) { + if (mimeType == kIsoBmffVideoMimeType.c_str() || + mimeType == kIsoBmffAudioMimeType.c_str() || + mimeType == kCencInitDataFormat.c_str()) { Status res = parsePssh(initData, &keyIds); if (res != Status::OK) { return res; } - } else if (mimeType == kWebmVideoMimeType || - mimeType == kWebmAudioMimeType || - mimeType == kWebmInitDataFormat) { + } else if (mimeType == kWebmVideoMimeType.c_str() || + mimeType == kWebmAudioMimeType.c_str() || + mimeType == kWebmInitDataFormat.c_str()) { // WebM "init data" is just a single key ID if (initData.size() != kKeyIdSize) { return Status::ERROR_DRM_CANNOT_HANDLE; diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h index ba5fa65092..f294d4d736 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -62,6 +62,7 @@ using ::android::sp; typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; +typedef drm::V1_2::KeyStatus KeyStatus_V1_2; typedef drm::V1_2::Status Status_V1_2; typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2; @@ -335,6 +336,15 @@ struct DrmPlugin : public IDrmPlugin { return Void(); } + Return sendKeysChange_1_2( + const hidl_vec& sessionId, + const hidl_vec& keyStatusList, bool hasNewUsableKey) { + if (mListenerV1_2 != NULL) { + mListenerV1_2->sendKeysChange_1_2(sessionId, keyStatusList, hasNewUsableKey); + } + return Void(); + } + Return sendSessionLostState( const hidl_vec& sessionId) { if (mListenerV1_2 != NULL) { diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h index a630bfd431..bdf1b3009b 100644 --- a/media/libmedia/include/media/DrmHal.h +++ b/media/libmedia/include/media/DrmHal.h @@ -37,14 +37,15 @@ using drm::V1_0::EventType; using drm::V1_0::IDrmFactory; using drm::V1_0::IDrmPlugin; using drm::V1_0::IDrmPluginListener; -using drm::V1_0::KeyStatus; using drm::V1_1::SecurityLevel; +using drm::V1_2::KeyStatus; using drm::V1_2::OfflineLicenseState; using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; +typedef drm::V1_0::KeyStatus KeyStatus_V1_0; namespace android { @@ -56,7 +57,7 @@ inline bool operator==(const Vector &l, const Vector &r) { } struct DrmHal : public BnDrm, - public IBinder::DeathRecipient, + public IBinder::DeathRecipient, public IDrmPluginListener_V1_2 { DrmHal(); virtual ~DrmHal(); @@ -180,6 +181,9 @@ struct DrmHal : public BnDrm, int64_t expiryTimeInMS); Return sendKeysChange(const hidl_vec& sessionId, + const hidl_vec& keyStatusList, bool hasNewUsableKey); + + Return sendKeysChange_1_2(const hidl_vec& sessionId, const hidl_vec& keyStatusList, bool hasNewUsableKey); Return sendSessionLostState(const hidl_vec& sessionId); diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h index 261998f986..6f132bfb36 100644 --- a/media/libmedia/include/media/DrmMetrics.h +++ b/media/libmedia/include/media/DrmMetrics.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -50,7 +51,7 @@ class MediaDrmMetrics { CounterMetric mProvideProvisionResponseCounter; // Count of key status events broken out by status type. - CounterMetric<::android::hardware::drm::V1_0::KeyStatusType> + CounterMetric<::android::hardware::drm::V1_2::KeyStatusType> mKeyStatusChangeCounter; // Count of events broken out by event type CounterMetric<::android::hardware::drm::V1_0::EventType> mEventCounter; -- GitLab From f80fbfe6441875a98d5ea311e9cb9bc381ed7576 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 7 May 2019 07:48:54 -0700 Subject: [PATCH 1383/1530] Zero-initialize HIDL structs before passing Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 131267328 Bug: 131356202 Merged-In: Ie91b7946f8f4406fd06e9cb4ad883b3a2704f366 Change-Id: Ie91b7946f8f4406fd06e9cb4ad883b3a2704f366 --- .../omx/1.0/WGraphicBufferProducer.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp index 650db8e136..631f52e597 100644 --- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp +++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp @@ -39,7 +39,7 @@ Return TWGraphicBufferProducer::requestBuffer( int32_t slot, requestBuffer_cb _hidl_cb) { sp buf; status_t status = mBase->requestBuffer(slot, &buf); - AnwBuffer anwBuffer; + AnwBuffer anwBuffer{}; wrapAs(&anwBuffer, *buf); _hidl_cb(static_cast(status), anwBuffer); return Void(); @@ -59,7 +59,7 @@ Return TWGraphicBufferProducer::dequeueBuffer( uint32_t width, uint32_t height, PixelFormat format, uint32_t usage, bool getFrameTimestamps, dequeueBuffer_cb _hidl_cb) { - int slot; + int slot{}; sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( @@ -67,8 +67,8 @@ Return TWGraphicBufferProducer::dequeueBuffer( width, height, static_cast<::android::PixelFormat>(format), usage, getFrameTimestamps ? &outTimestamps : nullptr); - hidl_handle tFence; - FrameEventHistoryDelta tOutTimestamps; + hidl_handle tFence{}; + FrameEventHistoryDelta tOutTimestamps{}; native_handle_t* nh = nullptr; if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) { @@ -116,8 +116,8 @@ Return TWGraphicBufferProducer::detachNextBuffer( sp outBuffer; sp outFence; status_t status = mBase->detachNextBuffer(&outBuffer, &outFence); - AnwBuffer tBuffer; - hidl_handle tFence; + AnwBuffer tBuffer{}; + hidl_handle tFence{}; if (outBuffer == nullptr) { LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " @@ -159,7 +159,7 @@ Return TWGraphicBufferProducer::attachBuffer( Return TWGraphicBufferProducer::queueBuffer( int32_t slot, const QueueBufferInput& input, queueBuffer_cb _hidl_cb) { - QueueBufferOutput tOutput; + QueueBufferOutput tOutput{}; BGraphicBufferProducer::QueueBufferInput lInput( 0, false, HAL_DATASPACE_UNKNOWN, ::android::Rect(0, 0, 1, 1), @@ -221,7 +221,7 @@ Return TWGraphicBufferProducer::connect( producerControlledByApp, &lOutput); - QueueBufferOutput tOutput; + QueueBufferOutput tOutput{}; std::vector > nhAA; if (!wrapAs(&tOutput, &nhAA, lOutput)) { LOG(ERROR) << "TWGraphicBufferProducer::connect - " @@ -293,11 +293,11 @@ Return TWGraphicBufferProducer::getLastQueuedBuffer( status_t status = mBase->getLastQueuedBuffer( &lOutBuffer, &lOutFence, lOutTransformMatrix); - AnwBuffer tOutBuffer; + AnwBuffer tOutBuffer{}; if (lOutBuffer != nullptr) { wrapAs(&tOutBuffer, *lOutBuffer); } - hidl_handle tOutFence; + hidl_handle tOutFence{}; native_handle_t* nh = nullptr; if ((lOutFence == nullptr) || !wrapAs(&tOutFence, &nh, *lOutFence)) { LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - " @@ -320,7 +320,7 @@ Return TWGraphicBufferProducer::getFrameTimestamps( ::android::FrameEventHistoryDelta lDelta; mBase->getFrameTimestamps(&lDelta); - FrameEventHistoryDelta tDelta; + FrameEventHistoryDelta tDelta{}; std::vector > nhAA; if (!wrapAs(&tDelta, &nhAA, lDelta)) { LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - " @@ -339,7 +339,7 @@ Return TWGraphicBufferProducer::getFrameTimestamps( } Return TWGraphicBufferProducer::getUniqueId(getUniqueId_cb _hidl_cb) { - uint64_t outId; + uint64_t outId{}; status_t status = mBase->getUniqueId(&outId); _hidl_cb(static_cast(status), outId); return Void(); -- GitLab From 56173f4146914217b4872502007e8aa02b3521dd Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 7 May 2019 07:48:54 -0700 Subject: [PATCH 1384/1530] Zero-initialize HIDL structs before passing Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 131267328 Bug: 131356202 Merged-In: Ie91b7946f8f4406fd06e9cb4ad883b3a2704f366 Change-Id: Ie91b7946f8f4406fd06e9cb4ad883b3a2704f366 --- .../omx/1.0/WGraphicBufferProducer.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp index c4499dc21f..639b35f630 100644 --- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp +++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp @@ -40,7 +40,7 @@ Return TWGraphicBufferProducer::requestBuffer( int32_t slot, requestBuffer_cb _hidl_cb) { sp buf; status_t status = mBase->requestBuffer(slot, &buf); - AnwBuffer anwBuffer; + AnwBuffer anwBuffer{}; if (buf != nullptr) { wrapAs(&anwBuffer, *buf); } @@ -62,15 +62,15 @@ Return TWGraphicBufferProducer::dequeueBuffer( uint32_t width, uint32_t height, PixelFormat format, uint32_t usage, bool getFrameTimestamps, dequeueBuffer_cb _hidl_cb) { - int slot; + int slot{}; sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( &slot, &fence, width, height, static_cast<::android::PixelFormat>(format), usage, nullptr, getFrameTimestamps ? &outTimestamps : nullptr); - hidl_handle tFence; - FrameEventHistoryDelta tOutTimestamps; + hidl_handle tFence{}; + FrameEventHistoryDelta tOutTimestamps{}; native_handle_t* nh = nullptr; if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) { @@ -118,8 +118,8 @@ Return TWGraphicBufferProducer::detachNextBuffer( sp outBuffer; sp outFence; status_t status = mBase->detachNextBuffer(&outBuffer, &outFence); - AnwBuffer tBuffer; - hidl_handle tFence; + AnwBuffer tBuffer{}; + hidl_handle tFence{}; if (outBuffer == nullptr) { LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " @@ -161,7 +161,7 @@ Return TWGraphicBufferProducer::attachBuffer( Return TWGraphicBufferProducer::queueBuffer( int32_t slot, const QueueBufferInput& input, queueBuffer_cb _hidl_cb) { - QueueBufferOutput tOutput; + QueueBufferOutput tOutput{}; BGraphicBufferProducer::QueueBufferInput lInput( 0, false, HAL_DATASPACE_UNKNOWN, ::android::Rect(0, 0, 1, 1), @@ -223,7 +223,7 @@ Return TWGraphicBufferProducer::connect( producerControlledByApp, &lOutput); - QueueBufferOutput tOutput; + QueueBufferOutput tOutput{}; std::vector > nhAA; if (!wrapAs(&tOutput, &nhAA, lOutput)) { LOG(ERROR) << "TWGraphicBufferProducer::connect - " @@ -295,11 +295,11 @@ Return TWGraphicBufferProducer::getLastQueuedBuffer( status_t status = mBase->getLastQueuedBuffer( &lOutBuffer, &lOutFence, lOutTransformMatrix); - AnwBuffer tOutBuffer; + AnwBuffer tOutBuffer{}; if (lOutBuffer != nullptr) { wrapAs(&tOutBuffer, *lOutBuffer); } - hidl_handle tOutFence; + hidl_handle tOutFence{}; native_handle_t* nh = nullptr; if ((lOutFence == nullptr) || !wrapAs(&tOutFence, &nh, *lOutFence)) { LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - " @@ -322,7 +322,7 @@ Return TWGraphicBufferProducer::getFrameTimestamps( ::android::FrameEventHistoryDelta lDelta; mBase->getFrameTimestamps(&lDelta); - FrameEventHistoryDelta tDelta; + FrameEventHistoryDelta tDelta{}; std::vector > nhAA; if (!wrapAs(&tDelta, &nhAA, lDelta)) { LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - " @@ -341,7 +341,7 @@ Return TWGraphicBufferProducer::getFrameTimestamps( } Return TWGraphicBufferProducer::getUniqueId(getUniqueId_cb _hidl_cb) { - uint64_t outId; + uint64_t outId{}; status_t status = mBase->getUniqueId(&outId); _hidl_cb(static_cast(status), outId); return Void(); -- GitLab From cad25f332373d834f80872239b5b06ab38274ee2 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Mon, 6 May 2019 20:45:04 -0700 Subject: [PATCH 1385/1530] Zero-initialize HIDL structs before passing Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 131267328 Bug: 131356202 Change-Id: Ie91b7946f8f4406fd06e9cb4ad883b3a2704f366 Merged-In: I2f696aa85143f74f753fbb0320dce5aee88846c4 --- .../bqhelper/WGraphicBufferProducer.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h index 8ddf20f942..c14a1c2597 100644 --- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h +++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h @@ -68,7 +68,7 @@ struct TWGraphicBufferProducer : public BASE { Return requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override { sp buf; status_t status = mBase->requestBuffer(slot, &buf); - AnwBuffer anwBuffer; + AnwBuffer anwBuffer{}; if (buf != nullptr) { ::android::conversion::wrapAs(&anwBuffer, *buf); } @@ -89,15 +89,15 @@ struct TWGraphicBufferProducer : public BASE { uint32_t width, uint32_t height, ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage, bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override { - int slot; + int slot{}; sp fence; ::android::FrameEventHistoryDelta outTimestamps; status_t status = mBase->dequeueBuffer( &slot, &fence, width, height, static_cast<::android::PixelFormat>(format), usage, nullptr, getFrameTimestamps ? &outTimestamps : nullptr); - hidl_handle tFence; - HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps; + hidl_handle tFence{}; + HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps{}; native_handle_t* nh = nullptr; if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) { @@ -144,8 +144,8 @@ struct TWGraphicBufferProducer : public BASE { sp outBuffer; sp outFence; status_t status = mBase->detachNextBuffer(&outBuffer, &outFence); - AnwBuffer tBuffer; - hidl_handle tFence; + AnwBuffer tBuffer{}; + hidl_handle tFence{}; if (outBuffer == nullptr) { LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " @@ -185,7 +185,7 @@ struct TWGraphicBufferProducer : public BASE { Return queueBuffer( int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input, HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override { - HGraphicBufferProducer::QueueBufferOutput tOutput; + HGraphicBufferProducer::QueueBufferOutput tOutput{}; BGraphicBufferProducer::QueueBufferInput lInput( 0, false, HAL_DATASPACE_UNKNOWN, ::android::Rect(0, 0, 1, 1), @@ -246,7 +246,7 @@ struct TWGraphicBufferProducer : public BASE { producerControlledByApp, &lOutput); - HGraphicBufferProducer::QueueBufferOutput tOutput; + HGraphicBufferProducer::QueueBufferOutput tOutput{}; std::vector > nhAA; if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) { LOG(ERROR) << "TWGraphicBufferProducer::connect - " @@ -320,11 +320,11 @@ struct TWGraphicBufferProducer : public BASE { status_t status = mBase->getLastQueuedBuffer( &lOutBuffer, &lOutFence, lOutTransformMatrix); - AnwBuffer tOutBuffer; + AnwBuffer tOutBuffer{}; if (lOutBuffer != nullptr) { ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer); } - hidl_handle tOutFence; + hidl_handle tOutFence{}; native_handle_t* nh = nullptr; if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) { LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - " @@ -346,7 +346,7 @@ struct TWGraphicBufferProducer : public BASE { ::android::FrameEventHistoryDelta lDelta; mBase->getFrameTimestamps(&lDelta); - HGraphicBufferProducer::FrameEventHistoryDelta tDelta; + HGraphicBufferProducer::FrameEventHistoryDelta tDelta{}; std::vector > nhAA; if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) { LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - " @@ -365,7 +365,7 @@ struct TWGraphicBufferProducer : public BASE { } Return getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override { - uint64_t outId; + uint64_t outId{}; status_t status = mBase->getUniqueId(&outId); _hidl_cb(static_cast(status), outId); return Void(); -- GitLab From a171e359408050194cbc68f3fa37f2b8d8ca361c Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 7 May 2019 13:04:45 -0700 Subject: [PATCH 1386/1530] audio policy: fix concurrent capture policy Restrict concurrent capture scenarii by not allowing 3rd party applications to capture concurrently Bug: 131758164 Test: manual test of main capture use cases Test: CTS tests for audio recording Change-Id: Ica17d6eea81671eeb872fe55c1e7e06ec9ad279b --- .../audiopolicy/service/AudioPolicyService.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index 5389c08a1a..e3c72aeb80 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -411,19 +411,19 @@ void AudioPolicyService::updateUidStates_l() // OR The client is the assistant // AND an accessibility service is on TOP // AND the source is VOICE_RECOGNITION or HOTWORD -// OR uses VOICE_RECOGNITION AND is on TOP OR latest started +// OR uses VOICE_RECOGNITION AND is on TOP // OR uses HOTWORD // AND there is no active privacy sensitive capture or call // OR client has CAPTURE_AUDIO_OUTPUT privileged permission // OR The client is an accessibility service -// AND is on TOP OR latest started +// AND is on TOP // AND the source is VOICE_RECOGNITION or HOTWORD // OR the client source is virtual (remote submix, call audio TX or RX...) -// OR Any other client +// OR Any client // AND The assistant is not on TOP +// AND is on TOP or latest started // AND there is no active privacy sensitive capture or call // OR client has CAPTURE_AUDIO_OUTPUT privileged permission -//TODO: mamanage pre processing effects according to use case priority sp topActive; sp latestActive; @@ -505,9 +505,11 @@ void AudioPolicyService::updateUidStates_l() // By default allow capture if: // The assistant is not on TOP + // AND is on TOP or latest started // AND there is no active privacy sensitive capture or call // OR client has CAPTURE_AUDIO_OUTPUT privileged permission bool allowCapture = !isAssistantOnTop + && ((isTopOrLatestActive && !isLatestSensitive) || isLatestSensitive) && !(isSensitiveActive && !(isLatestSensitive || current->canCaptureOutput)) && !(isInCall && !current->canCaptureOutput); @@ -518,7 +520,7 @@ void AudioPolicyService::updateUidStates_l() // For assistant allow capture if: // An accessibility service is on TOP // AND the source is VOICE_RECOGNITION or HOTWORD - // OR is on TOP OR latest started AND uses VOICE_RECOGNITION + // OR is on TOP AND uses VOICE_RECOGNITION // OR uses HOTWORD // AND there is no active privacy sensitive capture or call // OR client has CAPTURE_AUDIO_OUTPUT privileged permission @@ -527,7 +529,7 @@ void AudioPolicyService::updateUidStates_l() allowCapture = true; } } else { - if (((isTopOrLatestActive && source == AUDIO_SOURCE_VOICE_RECOGNITION) || + if (((isAssistantOnTop && source == AUDIO_SOURCE_VOICE_RECOGNITION) || source == AUDIO_SOURCE_HOTWORD) && (!(isSensitiveActive || isInCall) || current->canCaptureOutput)) { allowCapture = true; @@ -535,9 +537,9 @@ void AudioPolicyService::updateUidStates_l() } } else if (mUidPolicy->isA11yUid(current->uid)) { // For accessibility service allow capture if: - // Is on TOP OR latest started + // Is on TOP // AND the source is VOICE_RECOGNITION or HOTWORD - if (isTopOrLatestActive && + if (isA11yOnTop && (source == AUDIO_SOURCE_VOICE_RECOGNITION || source == AUDIO_SOURCE_HOTWORD)) { allowCapture = true; } -- GitLab From 329ddab0a26a06f828314860d290b5d6fb3bd0c9 Mon Sep 17 00:00:00 2001 From: Abhijith Shastry Date: Tue, 23 Apr 2019 11:53:26 -0700 Subject: [PATCH 1387/1530] Do not cap volume for ring stream when voice stream has been programatically muted. Test: Manual Bug: 131171138 Change-Id: Iea9aaa618cfef60cae592a8a21026d59212c0740 --- .../audiopolicy/managerdefault/AudioPolicyManager.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 2dc7cad495..eaba828b4a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5685,7 +5685,16 @@ float AudioPolicyManager::computeVolume(IVolumeCurves &curves, const float maxVoiceVolDb = computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device) + IN_CALL_EARPIECE_HEADROOM_DB; - if (volumeDb > maxVoiceVolDb) { + // FIXME: Workaround for call screening applications until a proper audio mode is defined + // to support this scenario : Exempt the RING stream from the audio cap if the audio was + // programmatically muted. + // VOICE_CALL stream has minVolumeIndex > 0 : Users cannot set the volume of voice calls to + // 0. We don't want to cap volume when the system has programmatically muted the voice call + // stream. See setVolumeCurveIndex() for more information. + bool exemptFromCapping = (volumeSource == ringVolumeSrc) && (voiceVolumeIndex == 0); + ALOGV_IF(exemptFromCapping, "%s volume source %d at vol=%f not capped", __func__, + volumeSource, volumeDb); + if ((volumeDb > maxVoiceVolDb) && !exemptFromCapping) { ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__, volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb); volumeDb = maxVoiceVolDb; -- GitLab From 9c4e0fa92912a1fdd7235cd7ae108ad32fe5ea0f Mon Sep 17 00:00:00 2001 From: "John W. Bruce" Date: Fri, 3 May 2019 17:12:44 -0700 Subject: [PATCH 1388/1530] Do Not Pass Blank App Package Name to MediaDrm Plugins Previously, the NDK has always passed a blank app package name to the MediaDrm plugins. However, some DRM systems need this value to be filled in to operate correctly. Unfortunately, since supplying or not supplying this value can invalidate existing DRM licenses, we cannot start passing it in for devices that might have previously run without it. As such, passing it is only supported on devices for which Q is their first Android release. Bug: 65680731 Test: CTS NativeMediaDrmClearkeyTest Change-Id: Icccde9708766a86ccd352c82be25285fc9f8ebca --- media/ndk/Android.bp | 1 + media/ndk/NdkMediaDrm.cpp | 48 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp index 7d1c88bd8c..a3cabd877a 100644 --- a/media/ndk/Android.bp +++ b/media/ndk/Android.bp @@ -74,6 +74,7 @@ cc_library_shared { "android.hardware.graphics.bufferqueue@1.0", "android.hidl.token@1.0-utils", "libandroid_runtime_lazy", + "libbase", "libbinder", "libmedia", "libmedia_omx", diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp index 2deb1a49bb..85dbffe16d 100644 --- a/media/ndk/NdkMediaDrm.cpp +++ b/media/ndk/NdkMediaDrm.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "NdkMediaDrm" #include +#include #include @@ -26,6 +27,8 @@ #include #include +#include +#include #include #include #include @@ -231,6 +234,39 @@ static media_status_t translateStatus(status_t status) { return result; } +static bool ShouldGetAppPackageName(void) { + // Check what this device's first API level was. + int32_t firstApiLevel = android::base::GetIntProperty("ro.product.first_api_level", 0); + if (firstApiLevel == 0) { + // First API Level is 0 on factory ROMs, but we can assume the current SDK + // version is the first if it's a factory ROM. + firstApiLevel = android::base::GetIntProperty("ro.build.version.sdk", 0); + } + return firstApiLevel >= 29; // Android Q +} + +static status_t GetAppPackageName(String8 *packageName) { + sp serviceManager = defaultServiceManager(); + sp binder = serviceManager->getService(String16("permission")); + + sp permissionContol = interface_cast(binder); + if (permissionContol == NULL) { + ALOGE("Failed to get permission service"); + return UNKNOWN_ERROR; + } + + Vector packages; + permissionContol->getPackagesForUid(getuid(), packages); + + if (packages.isEmpty()) { + ALOGE("Unable to get package name for current UID"); + return UNKNOWN_ERROR; + } + + *packageName = String8(packages[0]); + return OK; +} + static sp CreateDrm() { sp sm = defaultServiceManager(); sp binder = sm->getService(String16("media.drm")); @@ -255,8 +291,16 @@ static sp CreateDrmFromUUID(const AMediaUUID uuid) { return NULL; } - String8 nullPackageName; - status_t err = drm->createPlugin(uuid, nullPackageName); + String8 packageName; + if (ShouldGetAppPackageName()) { + status_t err = GetAppPackageName(&packageName); + + if (err != OK) { + return NULL; + } + } + + status_t err = drm->createPlugin(uuid, packageName); if (err != OK) { return NULL; -- GitLab From bfb487a62500acbabebe13e0489137e58a211b2f Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Wed, 8 May 2019 06:04:48 -0700 Subject: [PATCH 1389/1530] media: Fix MediaCodesXmlParser Result bug In the constructor, we want to assign our new error string to the struct member, instead of to the function argument. Bug: 129710438 Test: TreeHugger Change-Id: Id3a16fb048b3f5db97e455f6baaaec153b6bf6f0 --- media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp index 6a1b9a8931..b1e5fbf5f6 100644 --- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp +++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp @@ -148,7 +148,7 @@ struct MediaCodecsXmlParser::Impl { : mStatus(s), mError(error) { if (error.empty() && s) { - error = "Failed (" + std::string(asString(s)) + ")"; + mError = "Failed (" + std::string(asString(s)) + ")"; } } operator status_t() const { return mStatus; } -- GitLab From d0cce2ebcb9e4f95729d1f5da447f345428a8315 Mon Sep 17 00:00:00 2001 From: Carter Hsu Date: Fri, 3 May 2019 17:36:28 +0800 Subject: [PATCH 1390/1530] audio: correct the boundary of soundtrigger index Bug: 129111371 Test: manual Change-Id: Ic3e2d5b8b6b53be2184307b076ba8680c6f81a29 Signed-off-by: Carter Hsu --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index c0ca440dc5..084fbf1646 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2025,7 +2025,7 @@ exit: device->getId() : AUDIO_PORT_HANDLE_NONE; isSoundTrigger = attributes.source == AUDIO_SOURCE_HOTWORD && - mSoundTriggerSessions.indexOfKey(session) > 0; + mSoundTriggerSessions.indexOfKey(session) >= 0; *portId = AudioPort::getNextUniqueId(); clientDesc = new RecordClientDescriptor(*portId, riid, uid, session, attributes, *config, -- GitLab From 673236b3a962335013baddee4065be138eb610b8 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 8 May 2019 09:02:21 -0700 Subject: [PATCH 1391/1530] Revert "Make tombstone in the child process on loading failure." This reverts commit e55d13cea8946ba51fa51d9e07e29bda141b12f8. Test: manualy added crash loop and check if it triggers rollback. Bug: 131106476 Change-Id: Ie49cec343467f58835f8ee01f72ecbedbadb4b79 --- media/codec2/vndk/C2Store.cpp | 29 +++++++------------ .../libstagefright/MediaExtractorFactory.cpp | 28 ++++++++---------- .../media/stagefright/foundation/ADebug.h | 9 ------ 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index f5dc838f5e..10c4dcc574 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -662,36 +661,30 @@ c2_status_t C2PlatformComponentStore::ComponentModule::init( ALOGV("in %s", __func__); ALOGV("loading dll"); mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE); - if (mLibHandle == nullptr) { - LOG_ALWAYS_FATAL_IN_CHILD_PROC("could not dlopen %s: %s", libPath.c_str(), dlerror()); - mInit = C2_CORRUPTED; - return mInit; - } + LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, + "could not dlopen %s: %s", libPath.c_str(), dlerror()); createFactory = (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory"); - if (createFactory == nullptr) { - LOG_ALWAYS_FATAL_IN_CHILD_PROC("createFactory is null in %s", libPath.c_str()); - mInit = C2_CORRUPTED; - return mInit; - } + LOG_ALWAYS_FATAL_IF(createFactory == nullptr, + "createFactory is null in %s", libPath.c_str()); destroyFactory = (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory"); - if (destroyFactory == nullptr) { - LOG_ALWAYS_FATAL_IN_CHILD_PROC("destroyFactory is null in %s", libPath.c_str()); - mInit = C2_CORRUPTED; - return mInit; - } + LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr, + "destroyFactory is null in %s", libPath.c_str()); mComponentFactory = createFactory(); if (mComponentFactory == nullptr) { ALOGD("could not create factory in %s", libPath.c_str()); mInit = C2_NO_MEMORY; - return mInit; + } else { + mInit = C2_OK; } - mInit = C2_OK; + if (mInit != C2_OK) { + return mInit; + } std::shared_ptr intf; c2_status_t res = createInterface(0, &intf); diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index 81d2abbced..d97591f8a3 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -19,11 +19,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -245,21 +245,17 @@ void MediaExtractorFactory::RegisterExtractors( void *libHandle = android_dlopen_ext( libPath.string(), RTLD_NOW | RTLD_LOCAL, dlextinfo); - if (libHandle) { - GetExtractorDef getDef = - (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); - if (getDef) { - ALOGV("registering sniffer for %s", libPath.string()); - RegisterExtractor( - new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); - } else { - LOG_ALWAYS_FATAL_IN_CHILD_PROC("%s does not contain sniffer", libPath.string()); - dlclose(libHandle); - } - } else { - LOG_ALWAYS_FATAL_IN_CHILD_PROC( - "couldn't dlopen(%s) %s", libPath.string(), strerror(errno)); - } + CHECK(libHandle != nullptr) + << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno); + + GetExtractorDef getDef = + (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF"); + CHECK(getDef != nullptr) + << libPath.string() << " does not contain sniffer"; + + ALOGV("registering sniffer for %s", libPath.string()); + RegisterExtractor( + new ExtractorPlugin(getDef(), libHandle, libPath), pluginList); } closedir(libDir); } else { diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h index 180694b4d4..a8b88fdd64 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ADebug.h @@ -123,15 +123,6 @@ inline static const char *asString(status_t i, const char *def = "??") { #define TRESPASS_DBG(...) #endif -#ifndef LOG_ALWAYS_FATAL_IN_CHILD_PROC -#define LOG_ALWAYS_FATAL_IN_CHILD_PROC(...) \ - do { \ - if (fork() == 0) { \ - LOG_ALWAYS_FATAL(__VA_ARGS__); \ - } \ - } while (false) -#endif - struct ADebug { enum Level { kDebugNone, // no debug -- GitLab From c728a42d4f9f03f74b901fc7dd7fba1bc06579e9 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 8 May 2019 11:17:01 -0700 Subject: [PATCH 1392/1530] extractor: load extractors on boot, not on request. Test: build + dumpsys media.extractor Bug: 131106476 Change-Id: Idbea788340c4849f1286727c159a8eb43d6a7bb0 --- media/libstagefright/MediaExtractorFactory.cpp | 4 +--- .../include/media/stagefright/MediaExtractorFactory.h | 3 +-- services/mediaextractor/MediaExtractorService.cpp | 4 +++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp index d97591f8a3..4c8be1f193 100644 --- a/media/libstagefright/MediaExtractorFactory.cpp +++ b/media/libstagefright/MediaExtractorFactory.cpp @@ -71,8 +71,6 @@ sp MediaExtractorFactory::CreateFromService( ALOGV("MediaExtractorFactory::CreateFromService %s", mime); - UpdateExtractors(); - // initialize source decryption if needed source->DrmInitialization(nullptr /* mime */); @@ -270,7 +268,7 @@ static bool compareFunc(const sp& first, const sp gSupportedExtensions; // static -void MediaExtractorFactory::UpdateExtractors() { +void MediaExtractorFactory::LoadExtractors() { Mutex::Autolock autoLock(gPluginMutex); if (gPluginsRegistered) { diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h index ea87948d61..2ab98e1408 100644 --- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h +++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h @@ -37,6 +37,7 @@ public: const sp &source, const char *mime = NULL); static status_t dump(int fd, const Vector& args); static std::unordered_set getSupportedTypes(); + static void LoadExtractors(); private: static Mutex gPluginMutex; @@ -53,8 +54,6 @@ private: static void *sniff(const sp &source, float *confidence, void **meta, FreeMetaFunc *freeMeta, sp &plugin, uint32_t *creatorVersion); - - static void UpdateExtractors(); }; } // namespace android diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp index de5c3e4fef..36e084bc26 100644 --- a/services/mediaextractor/MediaExtractorService.cpp +++ b/services/mediaextractor/MediaExtractorService.cpp @@ -30,7 +30,9 @@ namespace android { MediaExtractorService::MediaExtractorService() - : BnMediaExtractorService() { } + : BnMediaExtractorService() { + MediaExtractorFactory::LoadExtractors(); +} sp MediaExtractorService::makeExtractor( const sp &remoteSource, const char *mime) { -- GitLab From c578a50c9d54e18441ffea41ce81eefbb17360ff Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 8 May 2019 10:57:54 -0700 Subject: [PATCH 1393/1530] cameraservice: re-map oom_adj scores and states of native vendor clients. Override the oom_adj scores of vendor clients from to PERCEPTIBLE_APP_ADJ. Override the process state of vendor clients to to PROCESS_STATE_PERSISTENT_UI. This is in order for app processes to take priority over vendor clients. For example, this could help in the case of the default camera app and face auth contending for resources from the lock screen. Bug: 132117718 Test: manual test on lockscreen, try to double press and open camera app while a vendor client is connected to a contending camera device. Change-Id: Ie5fabb59c876cb02eb706f3dda8f748c69e3c063 Signed-off-by: Jayant Chowdhary --- .../camera/libcameraservice/CameraService.cpp | 14 ++++- .../libcameraservice/utils/ClientManager.h | 54 +++++++++++++++---- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index fc6d6be3e6..a87ebdf770 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -117,6 +117,11 @@ static void setLogLevel(int level) { static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA"); +// Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java +static constexpr int32_t kVendorClientScore = 200; +// Matches with PROCESS_STATE_PERSISTENT_UI in ActivityManager.java +static constexpr int32_t kVendorClientState = 1; + Mutex CameraService::sProxyMutex; sp CameraService::sCameraServiceProxy; @@ -1120,7 +1125,8 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien std::map pidToPriorityMap; for (size_t i = 0; i < ownerPids.size() - 1; i++) { pidToPriorityMap.emplace(ownerPids[i], - resource_policy::ClientPriority(priorityScores[i], states[i])); + resource_policy::ClientPriority(priorityScores[i], states[i], + /* isVendorClient won't get copied over*/ false)); } mActiveClientManager.updatePriorities(pidToPriorityMap); @@ -2980,8 +2986,12 @@ CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescr const std::set& conflictingKeys, int32_t score, int32_t ownerId, int32_t state) { + bool isVendorClient = hardware::IPCThreadState::self()->isServingCall(); + int32_t score_adj = isVendorClient ? kVendorClientScore : score; + int32_t state_adj = isVendorClient ? kVendorClientState: state; + return std::make_shared>>( - key, value, cost, conflictingKeys, score, ownerId, state); + key, value, cost, conflictingKeys, score_adj, ownerId, state_adj, isVendorClient); } CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h index d7135f1bdf..ec6f01c040 100644 --- a/services/camera/libcameraservice/utils/ClientManager.h +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -33,12 +33,38 @@ namespace resource_policy { class ClientPriority { public: - ClientPriority(int32_t score, int32_t state) : - mScore(score), mState(state) {} + /** + * Choosing to set mIsVendorClient through a parameter instead of calling + * hardware::IPCThreadState::self()->isServingCall() to protect against the + * case where the construction is offloaded to another thread which isn't a + * hwbinder thread. + */ + ClientPriority(int32_t score, int32_t state, bool isVendorClient) : + mScore(score), mState(state), mIsVendorClient(isVendorClient) { } int32_t getScore() const { return mScore; } int32_t getState() const { return mState; } + void setScore(int32_t score) { + // For vendor clients, the score is set once and for all during + // construction. Otherwise, it can get reset each time cameraserver + // queries ActivityManagerService for oom_adj scores / states . + if (!mIsVendorClient) { + mScore = score; + } + } + + void setState(int32_t state) { + // For vendor clients, the score is set once and for all during + // construction. Otherwise, it can get reset each time cameraserver + // queries ActivityManagerService for oom_adj scores / states + // (ActivityManagerService returns a vendor process' state as + // PROCESS_STATE_NONEXISTENT. + if (!mIsVendorClient) { + mState = state; + } + } + bool operator==(const ClientPriority& rhs) const { return (this->mScore == rhs.mScore) && (this->mState == rhs.mState); } @@ -66,6 +92,7 @@ public: private: int32_t mScore; int32_t mState; + bool mIsVendorClient = false; }; // -------------------------------------------------------------------------------- @@ -82,9 +109,10 @@ template class ClientDescriptor final { public: ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, - const std::set& conflictingKeys, int32_t score, int32_t ownerId, int32_t state); + const std::set& conflictingKeys, int32_t score, int32_t ownerId, int32_t state, + bool isVendorClient); ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set&& conflictingKeys, - int32_t score, int32_t ownerId, int32_t state); + int32_t score, int32_t ownerId, int32_t state, bool isVendorClient); ~ClientDescriptor(); @@ -148,17 +176,19 @@ bool operator < (const ClientDescriptor& a, const ClientDescriptor& template ClientDescriptor::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, - const std::set& conflictingKeys, int32_t score, int32_t ownerId, int32_t state) : + const std::set& conflictingKeys, int32_t score, int32_t ownerId, int32_t state, + bool isVendorClient) : mKey{key}, mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, - mPriority(score, state), + mPriority(score, state, isVendorClient), mOwnerId{ownerId} {} template ClientDescriptor::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, - std::set&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state) : + std::set&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state, + bool isVendorClient) : mKey{std::forward(key)}, mValue{std::forward(value)}, mCost{cost}, mConflicting{std::forward>(conflictingKeys)}, - mPriority(score, state), mOwnerId{ownerId} {} + mPriority(score, state, isVendorClient), mOwnerId{ownerId} {} template ClientDescriptor::~ClientDescriptor() {} @@ -204,7 +234,13 @@ std::set ClientDescriptor::getConflicting() const { template void ClientDescriptor::setPriority(const ClientPriority& priority) { - mPriority = priority; + // We don't use the usual copy constructor here since we want to remember + // whether a client is a vendor client or not. This could have been wiped + // off in the incoming priority argument since an AIDL thread might have + // called hardware::IPCThreadState::self()->isServingCall() after refreshing + // priorities for old clients through ProcessInfoService::getProcessStatesScoresFromPids(). + mPriority.setScore(priority.getScore()); + mPriority.setState(priority.getState()); } // -------------------------------------------------------------------------------- -- GitLab From 7216cce1a9f3c4cc054840b85fda3cc6a96ecfed Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Mon, 29 Apr 2019 08:26:46 -0700 Subject: [PATCH 1394/1530] MPEG4Extractor: read codecDelay from opus header Instead of hard coding codecDelay, read it from opus header. Also allow for variable sized opusheader to support multiple channels Bug:129712302 Test: atest android.media.cts.DecoderTest Change-Id: I5e134f688125d275163dfa496acd8133354b5617 --- media/extractors/mp4/MPEG4Extractor.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index e1bfb622e2..348ea498fa 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -1735,15 +1736,21 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate); if (chunk_type == FOURCC("Opus")) { - uint8_t opusInfo[19]; + uint8_t opusInfo[AOPUS_OPUSHEAD_MAXSIZE]; data_offset += sizeof(buffer); + size_t opusInfoSize = chunk_data_size - sizeof(buffer); + + if (opusInfoSize < AOPUS_OPUSHEAD_MINSIZE || + opusInfoSize > AOPUS_OPUSHEAD_MAXSIZE) { + return ERROR_MALFORMED; + } // Read Opus Header if (mDataSource->readAt( - data_offset, opusInfo, sizeof(opusInfo)) < (ssize_t)sizeof(opusInfo)) { + data_offset, opusInfo, opusInfoSize) < opusInfoSize) { return ERROR_IO; } - // OpusHeader must start with this magic sequence + // OpusHeader must start with this magic sequence, overwrite first 8 bytes // http://wiki.xiph.org/OggOpus#ID_Header strncpy((char *)opusInfo, "OpusHead", 8); @@ -1760,17 +1767,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { memcpy(&opusInfo[opusOffset + 2], &sample_rate, sizeof(sample_rate)); memcpy(&opusInfo[opusOffset + 6], &out_gain, sizeof(out_gain)); - int64_t codecDelay = 6500000; - int64_t seekPreRollNs = 80000000; // Fixed 80 msec + static const int64_t kSeekPreRollNs = 80000000; // Fixed 80 msec + static const int32_t kOpusSampleRate = 48000; + int64_t codecDelay = pre_skip * 1000000000ll / kOpusSampleRate; AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, opusInfo, sizeof(opusInfo)); AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_1, &codecDelay, sizeof(codecDelay)); AMediaFormat_setBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_CSD_2, &seekPreRollNs, sizeof(seekPreRollNs)); + AMEDIAFORMAT_KEY_CSD_2, &kSeekPreRollNs, sizeof(kSeekPreRollNs)); - data_offset += sizeof(opusInfo); + data_offset += opusInfoSize; *offset = data_offset; CHECK_EQ(*offset, stop_offset); } -- GitLab From 714ad81087d53a4bad7a7658d78a04694d6e393b Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Tue, 23 Apr 2019 17:34:55 +0530 Subject: [PATCH 1395/1530] Add Vorbis support in MP4 extractor Test: stagefright -ao /sdcard/vorbis.mp4 Bug: 129712302 Change-Id: I39b0709816a6abc518fb001a82aa297c6ec351e1 --- media/extractors/mp4/MPEG4Extractor.cpp | 71 +++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 348ea498fa..b4fd8112f9 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -4305,6 +4305,77 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_MALFORMED; } + if (objectTypeIndication == 0xdd) { + // vorbis audio + if (csd[0] != 0x02) { + return ERROR_MALFORMED; + } + + // codecInfo starts with two lengths, len1 and len2, that are + // "Xiph-style-lacing encoded".. + + size_t offset = 1; + size_t len1 = 0; + while (offset < csd_size && csd[offset] == 0xff) { + if (__builtin_add_overflow(len1, 0xff, &len1)) { + return ERROR_MALFORMED; + } + ++offset; + } + if (offset >= csd_size) { + return ERROR_MALFORMED; + } + if (__builtin_add_overflow(len1, csd[offset], &len1)) { + return ERROR_MALFORMED; + } + ++offset; + if (len1 == 0) { + return ERROR_MALFORMED; + } + + size_t len2 = 0; + while (offset < csd_size && csd[offset] == 0xff) { + if (__builtin_add_overflow(len2, 0xff, &len2)) { + return ERROR_MALFORMED; + } + ++offset; + } + if (offset >= csd_size) { + return ERROR_MALFORMED; + } + if (__builtin_add_overflow(len2, csd[offset], &len2)) { + return ERROR_MALFORMED; + } + ++offset; + if (len2 == 0) { + return ERROR_MALFORMED; + } + if (offset >= csd_size || csd[offset] != 0x01) { + return ERROR_MALFORMED; + } + // formerly kKeyVorbisInfo + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_0, &csd[offset], len1); + + if (__builtin_add_overflow(offset, len1, &offset) || + offset >= csd_size || csd[offset] != 0x03) { + return ERROR_MALFORMED; + } + + if (__builtin_add_overflow(offset, len2, &offset) || + offset >= csd_size || csd[offset] != 0x05) { + return ERROR_MALFORMED; + } + + // formerly kKeyVorbisBooks + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_1, &csd[offset], csd_size - offset); + AMediaFormat_setString(mLastTrack->meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_VORBIS); + + return OK; + } + static uint32_t kSamplingRate[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 -- GitLab From 24ea4727726adbeebfc5779614a3cb0e44208cde Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Mar 2019 16:55:28 -0800 Subject: [PATCH 1396/1530] DO NOT MERGE: audiopolicy: Remove raw pointer references to AudioMix AudioInputDescriptor, AudioOutputDescriptor, and AudioSession used to reference AudioMix instances using a raw pointer. This isn't safe as AudioMix was owned by AudioPolicyMix, which is not referenced by descriptors. Change AudioMix* pointers in Audio{Input|Output}Descriptor and AudioSession to wp which reflects their relationship correctly. To ensure that code does not operate on AudioMix instances independently from AudioPolicyMix, and to avoid introducing a lot of getter / setter methods into AudioPolicyMix, make the latter to inherit AudioMix. This makes sense because AudioPolicyMix is essentially a ref-counted version of AudioMix. Bug: 124899895 Test: build and sanity check on angler, build angler with USE_CONFIGURABLE_AUDIO_POLICY := 1 Merged-In: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a Change-Id: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a --- .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyMix.h | 15 +++--- .../managerdefinitions/include/AudioSession.h | 5 +- .../src/AudioInputDescriptor.cpp | 1 + .../src/AudioOutputDescriptor.cpp | 10 ++-- .../managerdefinitions/src/AudioPolicyMix.cpp | 48 ++++++++----------- .../managerdefinitions/src/AudioSession.cpp | 8 ++-- .../managerdefault/AudioPolicyManager.cpp | 46 +++++++++--------- .../managerdefault/AudioPolicyManager.h | 4 +- 10 files changed, 72 insertions(+), 73 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index 46309ed818..f1081aeb0c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -27,7 +27,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. @@ -44,7 +44,7 @@ public: audio_io_handle_t mIoHandle; // input handle audio_devices_t mDevice; // current device this input is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from virtual void toAudioPortConfig(struct audio_port_config *dstConfig, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index dd3f8ae353..b0cdba94cf 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -27,7 +27,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; class DeviceDescriptor; @@ -124,7 +124,7 @@ public: audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy sp mOutput1; // used by duplicated outputs: first output sp mOutput2; // used by duplicated outputs: second output uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 8f5ebefbae..c70da39de5 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -29,9 +29,11 @@ class SwAudioOutputDescriptor; /** * custom mix entry in mPolicyMixes */ -class AudioPolicyMix : public RefBase { +class AudioPolicyMix : public AudioMix, public RefBase { public: - AudioPolicyMix() {} + AudioPolicyMix(const AudioMix &mix); + AudioPolicyMix(const AudioPolicyMix&) = delete; + AudioPolicyMix& operator=(const AudioPolicyMix&) = delete; const sp &getOutput() const; @@ -39,12 +41,7 @@ public: void clearOutput(); - android::AudioMix *getMix(); - - void setMix(AudioMix &mix); - private: - AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; @@ -74,9 +71,9 @@ public: audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, audio_devices_t availableDeviceTypes, - AudioMix **policyMix); + sp *policyMix); - status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); }; }; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index 388c25d11b..3b0ce71380 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -27,6 +27,7 @@ namespace android { class AudioPolicyClientInterface; +class AudioPolicyMix; class AudioSession : public RefBase, public AudioSessionInfoUpdateListener { @@ -39,7 +40,7 @@ public: audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface); status_t dump(int fd, int spaces, int index) const; @@ -72,7 +73,7 @@ private: bool mIsSoundTrigger; uint32_t mOpenCount; uint32_t mActiveCount; - AudioMix* mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy AudioPolicyClientInterface* mClientInterface; const AudioSessionInfoProvider* mInfoProvider; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 6dacaa4f4f..a0405f90c2 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -20,6 +20,7 @@ #include "AudioInputDescriptor.h" #include "IOProfile.h" #include "AudioGain.h" +#include "AudioPolicyMix.h" #include "HwModule.h" #include #include diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 79bbc548c6..124b73512a 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -19,6 +19,7 @@ #include #include "AudioOutputDescriptor.h" +#include "AudioPolicyMix.h" #include "IOProfile.h" #include "AudioGain.h" #include "Volume.h" @@ -312,17 +313,18 @@ void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, } else { mGlobalRefCount += delta; } + sp policyMix = mPolicyMix.promote(); if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 7ee98b675e..d88b2652a0 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -26,6 +26,10 @@ namespace android { +AudioPolicyMix::AudioPolicyMix(const AudioMix &mix) : AudioMix(mix) +{ +} + void AudioPolicyMix::setOutput(sp &output) { mOutput = output; @@ -41,16 +45,6 @@ void AudioPolicyMix::clearOutput() mOutput.clear(); } -void AudioPolicyMix::setMix(AudioMix &mix) -{ - mMix = mix; -} - -android::AudioMix *AudioPolicyMix::getMix() -{ - return &mMix; -} - status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix, sp desc) { @@ -59,12 +53,11 @@ status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix, ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); return BAD_VALUE; } - sp policyMix = new AudioPolicyMix(); - policyMix->setMix(mix); + sp policyMix = new AudioPolicyMix(mix); add(address, policyMix); if (desc != 0) { - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; policyMix->setOutput(desc); } return NO_ERROR; @@ -110,8 +103,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute ALOGV("getOutputForAttr() querying %zu mixes:", size()); desc = 0; for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); if (mix->mMixType == MIX_TYPE_PLAYERS) { // TODO if adding more player rules (currently only 2), make rule handling "generic" @@ -208,7 +200,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute (hasUidExcludeRules && uidExclusionFound) || (hasUidMatchRules && !uidMatchFound))) { ALOGV("\tgetOutputForAttr will use mix %zu", i); - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } else if (mix->mMixType == MIX_TYPE_RECORDERS) { @@ -217,7 +209,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute strncmp(attributes.tags + strlen("addr="), mix->mDeviceAddress.string(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } if (desc != 0) { @@ -228,12 +220,13 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } -audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availDevices, - AudioMix **policyMix) +audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource( + audio_source_t inputSource, + audio_devices_t availDevices, + sp *policyMix) { for (size_t i = 0; i < size(); i++) { - AudioMix *mix = valueAt(i)->getMix(); + AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; @@ -256,7 +249,8 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so return AUDIO_DEVICE_NONE; } -status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) +status_t AudioPolicyMixCollection::getInputMixForAttr( + audio_attributes_t attr, sp *policyMix) { if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { return BAD_VALUE; @@ -266,8 +260,7 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A #ifdef LOG_NDEBUG ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); } #endif @@ -278,13 +271,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return BAD_VALUE; } sp audioPolicyMix = valueAt(index); - AudioMix *mix = audioPolicyMix->getMix(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } - *policyMix = mix; + if (policyMix != nullptr) { + *policyMix = audioPolicyMix; + } return NO_ERROR; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index da983c5f94..498fa62135 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #include +#include "AudioPolicyMix.h" #include "AudioSession.h" #include "AudioGain.h" #include "TypeConverter.h" @@ -34,7 +35,7 @@ AudioSession::AudioSession(audio_session_t session, audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface) : mSession(session), mInputSource(inputSource), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), @@ -77,9 +78,10 @@ uint32_t AudioSession::changeActiveCount(int delta) if (event != RECORD_CONFIG_EVENT_NONE) { // Dynamic policy callback: // if input maps to a dynamic policy with an activity listener, notify of state change - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + sp policyMix = mPolicyMix.promote(); + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 00fd05a6c1..6d77699bc1 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1090,10 +1090,9 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, mOutputRoutes.incRouteActivity(session); audio_devices_t newDevice; - AudioMix *policyMix = NULL; + sp policyMix = outputDesc->mPolicyMix.promote(); const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; + if (policyMix != NULL) { address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { newDevice = policyMix->mDeviceType; @@ -1234,12 +1233,13 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, if (outputDesc->mRefCount[stream] == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS + sp policyMix = outputDesc->mPolicyMix.promote(); if (audio_is_remote_submix_device(outputDesc->mDevice) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, + policyMix->mDeviceAddress, "remote-submix"); } } @@ -1381,7 +1381,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, String8 address = String8(""); audio_source_t inputSource = attr->source; audio_source_t halInputSource; - AudioMix *policyMix = NULL; + sp policyMix; if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; @@ -1457,7 +1457,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix) + const sp &policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; audio_source_t halInputSource = inputSource; @@ -1645,10 +1645,11 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, mInputRoutes.incRouteActivity(session); if (!inputDesc->isActive() || mInputRoutes.hasRouteChanged(session)) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } @@ -1662,10 +1663,10 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -1709,10 +1710,11 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, mInputRoutes.decRouteActivity(session); if (!inputDesc->isActive()) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } @@ -1720,10 +1722,10 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -3757,7 +3759,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp de address.string()); } policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) { @@ -4870,7 +4872,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) + sp *policyMix) { audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; audio_devices_t selectedDeviceFromMix = diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 6c3e4163cd..df69ece47b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -627,7 +627,7 @@ private: audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix); + const sp &policyMix); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); @@ -641,7 +641,7 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp *policyMix = NULL); // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t device, -- GitLab From 66d9d0617a80943f9211863cb141e8fccb5d6ec3 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 12 Mar 2019 17:46:17 +0530 Subject: [PATCH 1397/1530] codec2: Add support to derive from base class for audio encoders Test: run cts -m CtsMediaTestCases --test android.media.cts.EncoderTest Bug: 124962433 Change-Id: I35dfb7e8b87c1d4b356b02cdee5fdd850e1002b1 --- media/codec2/components/aac/C2SoftAacEnc.cpp | 49 ++++++++--------- .../components/amr_nb_wb/C2SoftAmrNbEnc.cpp | 51 ++++++++---------- .../components/amr_nb_wb/C2SoftAmrWbEnc.cpp | 52 ++++++++----------- .../codec2/components/flac/C2SoftFlacEnc.cpp | 45 ++++++++-------- .../codec2/components/opus/C2SoftOpusEnc.cpp | 45 +++++++--------- 5 files changed, 106 insertions(+), 136 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index 71eb1ac0cc..7efc7f1511 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -29,33 +29,32 @@ namespace android { -class C2SoftAacEnc::IntfImpl : public C2InterfaceHelper { -public: - explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); +namespace { - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); +constexpr char COMPONENT_NAME[] = "c2.android.aac.encoder"; - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); +} // namespace - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); +class C2SoftAacEnc::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_AAC) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_AAC)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -125,10 +124,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; @@ -136,8 +131,6 @@ private: std::shared_ptr mProfileLevel; }; -constexpr char COMPONENT_NAME[] = "c2.android.aac.encoder"; - C2SoftAacEnc::C2SoftAacEnc( const char *name, c2_node_id_t id, diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp index 3d3aa7dbf2..e2d8cb6fe3 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp @@ -27,36 +27,33 @@ namespace android { -constexpr char COMPONENT_NAME[] = "c2.android.amrnb.encoder"; +namespace { -class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +constexpr char COMPONENT_NAME[] = "c2.android.amrnb.encoder"; - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); +} // namespace - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); +class C2SoftAmrNbEnc::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_AMR_NB) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_AMR_NB)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -91,11 +88,7 @@ class C2SoftAmrNbEnc::IntfImpl : public C2InterfaceHelper { uint32_t getChannelCount() const { return mChannelCount->value; } uint32_t getBitrate() const { return mBitrate->value; } - private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; +private: std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp index 379cb32fc1..84ae4b7be9 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp @@ -29,36 +29,32 @@ namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.amrwb.encoder"; -class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +} // namespace - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); +class C2SoftAmrWbEnc::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_AMR_WB) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_AMR_WB)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -93,11 +89,7 @@ class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper { uint32_t getChannelCount() const { return mChannelCount->value; } uint32_t getBitrate() const { return mBitrate->value; } - private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; +private: std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp index 0ce2543bad..cf34dffacb 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.cpp +++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp @@ -28,28 +28,32 @@ namespace android { -class C2SoftFlacEnc::IntfImpl : public C2InterfaceHelper { +namespace { + +constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder"; + +} // namespace + +class C2SoftFlacEnc::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_FLAC) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); + addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_FLAC)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) @@ -92,17 +96,12 @@ public: int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mPcmEncodingInfo; }; -constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder"; C2SoftFlacEnc::C2SoftFlacEnc( const char *name, diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index f1020c378d..7b58c9b054 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -32,35 +32,32 @@ extern "C" { #define DEFAULT_FRAME_DURATION_MS 20 namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder"; -class C2SoftOpusEnc::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftOpusEnc::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_OPUS) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_OPUS)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -104,10 +101,6 @@ public: uint32_t getComplexity() const { return mComplexity->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; -- GitLab From ed134640332fea70ca4b05694289d91a5265bb46 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 12 Mar 2019 14:18:42 +0530 Subject: [PATCH 1398/1530] codec2: Add support to derive from base class for audio decoders Test: run cts -m CtsMediaTestCases --test android.media.cts.DecoderTest Bug: 124962433 Change-Id: I7b53404423eb1978b9739128324b1ffa009d9926 --- media/codec2/components/aac/C2SoftAacDec.cpp | 4 -- .../components/amr_nb_wb/C2SoftAmrDec.cpp | 50 ++++++++----------- .../codec2/components/flac/C2SoftFlacDec.cpp | 45 +++++++---------- .../codec2/components/g711/C2SoftG711Dec.cpp | 50 ++++++++----------- media/codec2/components/gsm/C2SoftGsmDec.cpp | 48 ++++++++---------- media/codec2/components/mp3/C2SoftMp3Dec.cpp | 45 +++++++---------- .../codec2/components/opus/C2SoftOpusDec.cpp | 50 ++++++++----------- media/codec2/components/raw/C2SoftRawDec.cpp | 45 +++++++---------- .../components/vorbis/C2SoftVorbisDec.cpp | 45 +++++++---------- .../codec2/components/xaac/C2SoftXaacDec.cpp | 46 +++++++---------- 10 files changed, 182 insertions(+), 246 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp index 4d00d35680..2d4e1264c9 100644 --- a/media/codec2/components/aac/C2SoftAacDec.cpp +++ b/media/codec2/components/aac/C2SoftAacDec.cpp @@ -205,10 +205,6 @@ public: int32_t getDrcEffectType() const { return mDrcEffectType->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp index edad75a4bd..6578ad2ece 100644 --- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp +++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp @@ -33,43 +33,41 @@ namespace android { +namespace { + #ifdef AMRNB constexpr char COMPONENT_NAME[] = "c2.android.amrnb.decoder"; #else constexpr char COMPONENT_NAME[] = "c2.android.amrwb.decoder"; #endif -class C2SoftAmrDec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftAmrDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); - - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, #ifdef AMRNB - MEDIA_MIMETYPE_AUDIO_AMR_NB + MEDIA_MIMETYPE_AUDIO_AMR_NB #else - MEDIA_MIMETYPE_AUDIO_AMR_WB + MEDIA_MIMETYPE_AUDIO_AMR_WB #endif - )).build()); + ) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -110,10 +108,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/flac/C2SoftFlacDec.cpp b/media/codec2/components/flac/C2SoftFlacDec.cpp index 10b14ce6e2..4039b9b7e1 100644 --- a/media/codec2/components/flac/C2SoftFlacDec.cpp +++ b/media/codec2/components/flac/C2SoftFlacDec.cpp @@ -27,35 +27,32 @@ namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.flac.decoder"; -class C2SoftFlacDec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftFlacDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_FLAC) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_FLAC)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -99,10 +96,6 @@ public: int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/g711/C2SoftG711Dec.cpp b/media/codec2/components/g711/C2SoftG711Dec.cpp index 504ca787a3..43b843ae88 100644 --- a/media/codec2/components/g711/C2SoftG711Dec.cpp +++ b/media/codec2/components/g711/C2SoftG711Dec.cpp @@ -27,43 +27,41 @@ namespace android { +namespace { + #ifdef ALAW constexpr char COMPONENT_NAME[] = "c2.android.g711.alaw.decoder"; #else constexpr char COMPONENT_NAME[] = "c2.android.g711.mlaw.decoder"; #endif -class C2SoftG711Dec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftG711Dec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); - - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, #ifdef ALAW - MEDIA_MIMETYPE_AUDIO_G711_ALAW + MEDIA_MIMETYPE_AUDIO_G711_ALAW #else - MEDIA_MIMETYPE_AUDIO_G711_MLAW + MEDIA_MIMETYPE_AUDIO_G711_MLAW #endif - )).build()); + ) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -94,10 +92,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/gsm/C2SoftGsmDec.cpp b/media/codec2/components/gsm/C2SoftGsmDec.cpp index 69d4885874..287cfc65c2 100644 --- a/media/codec2/components/gsm/C2SoftGsmDec.cpp +++ b/media/codec2/components/gsm/C2SoftGsmDec.cpp @@ -27,34 +27,32 @@ namespace android { -constexpr char COMPONENT_NAME[] = "c2.android.gsm.decoder"; - -class C2SoftGsmDec::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +namespace { - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); +constexpr char COMPONENT_NAME[] = "c2.android.gsm.decoder"; - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); +} // namespace - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_MSGSM)) - .build()); +class C2SoftGsmDec::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_MSGSM) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -85,10 +83,6 @@ class C2SoftGsmDec::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/mp3/C2SoftMp3Dec.cpp b/media/codec2/components/mp3/C2SoftMp3Dec.cpp index 9db6d8f801..5ba7e3d78f 100644 --- a/media/codec2/components/mp3/C2SoftMp3Dec.cpp +++ b/media/codec2/components/mp3/C2SoftMp3Dec.cpp @@ -30,35 +30,32 @@ namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.mp3.decoder"; -class C2SoftMP3::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftMP3::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_MPEG) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_MPEG)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -90,10 +87,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp index 7dcd53d718..6b6974f6a4 100644 --- a/media/codec2/components/opus/C2SoftOpusDec.cpp +++ b/media/codec2/components/opus/C2SoftOpusDec.cpp @@ -31,34 +31,32 @@ extern "C" { namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.opus.decoder"; -class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +} // namespace - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_OPUS)) - .build()); +class C2SoftOpusDec::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_OPUS) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -88,11 +86,7 @@ class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper { .build()); } - private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; +private: std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp index 802caa477e..8e94a186f7 100644 --- a/media/codec2/components/raw/C2SoftRawDec.cpp +++ b/media/codec2/components/raw/C2SoftRawDec.cpp @@ -27,35 +27,32 @@ namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.raw.decoder"; -class C2SoftRawDec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftRawDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_RAW) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -98,10 +95,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp index e3bafd3f9d..18e6db2576 100644 --- a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp +++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp @@ -35,35 +35,32 @@ extern "C" { namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.vorbis.decoder"; -class C2SoftVorbisDec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftVorbisDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_VORBIS) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_VORBIS)) - .build()); - - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -94,10 +91,6 @@ public: } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp index ed730c377a..a3ebadbacb 100644 --- a/media/codec2/components/xaac/C2SoftXaacDec.cpp +++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp @@ -56,35 +56,31 @@ namespace android { +namespace { + constexpr char COMPONENT_NAME[] = "c2.android.xaac.decoder"; -class C2SoftXaacDec::IntfImpl : public C2InterfaceHelper { +} // namespace + +class C2SoftXaacDec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); - - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_AAC)) - .build()); + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_DECODER, + C2Component::DOMAIN_AUDIO, + MEDIA_MIMETYPE_AUDIO_AAC) { + noPrivateBuffers(); + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_AUDIO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( @@ -218,10 +214,6 @@ public: int32_t getDrcEffectType() const { return mDrcEffectType->value; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; -- GitLab From 9acd73d19e8f6f35d0c2ca357fcdac0f9fe50435 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Tue, 12 Mar 2019 14:08:05 +0530 Subject: [PATCH 1399/1530] codec2: Derive video encoder components from SimpleInterface Test: run cts -m CtsMediaTestCases --test android.media.cts.VideoEncoderTest Bug: 124962433 Change-Id: I2f0a7719c6d9f311b617f8e9054a2ae7dd1f27cc --- media/codec2/components/avc/C2SoftAvcEnc.cpp | 55 +++++++--------- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 57 +++++++--------- .../components/mpeg4_h263/C2SoftMpeg4Enc.cpp | 60 +++++++---------- media/codec2/components/vpx/C2SoftVp8Enc.cpp | 2 - media/codec2/components/vpx/C2SoftVp9Enc.cpp | 2 - media/codec2/components/vpx/C2SoftVpxEnc.h | 66 +++++++++---------- 6 files changed, 105 insertions(+), 137 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index b851908749..0ae2a5a573 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -37,34 +37,27 @@ namespace android { -class C2SoftAvcEnc::IntfImpl : public C2InterfaceHelper { -public: - explicit IntfImpl(const std::shared_ptr &helper) - : C2InterfaceHelper(helper) { - - setDerivedInstance(this); - - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) - .build()); +namespace { - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); +constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder"; - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_RAW)) - .build()); +} // namespace - addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_AVC)) - .build()); +class C2SoftAvcEnc::IntfImpl : public SimpleInterface::BaseParams { +public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_VIDEO, + MEDIA_MIMETYPE_VIDEO_AVC) { + noPrivateBuffers(); // TODO: account for our buffers here + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) @@ -72,6 +65,12 @@ public: 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); + addParameter( + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) + .build()); + addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) @@ -325,10 +324,6 @@ public: std::shared_ptr getRequestSync_l() const { return mRequestSync; } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; std::shared_ptr mSize; std::shared_ptr mFrameRate; @@ -341,8 +336,6 @@ private: #define ive_api_function ih264e_api_function -constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder"; - namespace { // From external/libavc/encoder/ih264e_bitstream.h diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 402d9aae25..b27ee4e554 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -38,41 +38,40 @@ namespace android { -class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +namespace { - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) - .build()); +constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); +} // namepsace + +class C2SoftHevcEnc::IntfImpl : public SimpleInterface::BaseParams { + public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_VIDEO, + MEDIA_MIMETYPE_VIDEO_HEVC) { + noPrivateBuffers(); // TODO: account for our buffers here + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_HEVC)) + DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) + .withConstValue(new C2StreamUsageTuning::input( + 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); - addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) - .withConstValue(new C2StreamUsageTuning::input( - 0u, (uint64_t)C2MemoryUsage::CPU_READ)) - .build()); - // matches size limits in codec library addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) @@ -332,10 +331,6 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; std::shared_ptr mSize; std::shared_ptr mFrameRate; @@ -348,8 +343,6 @@ class C2SoftHevcEnc::IntfImpl : public C2InterfaceHelper { std::shared_ptr mSyncFramePeriod; }; -constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder"; - static size_t GetCPUCoreCount() { long cpuCoreCount = 0; diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp index 89fa59d05d..36053f6885 100644 --- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp +++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp @@ -39,52 +39,46 @@ namespace android { +namespace { + #ifdef MPEG4 constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.encoder"; +const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_MPEG4; #else constexpr char COMPONENT_NAME[] = "c2.android.h263.encoder"; +const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_H263; #endif -class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { +} // namepsace + +class C2SoftMpeg4Enc::IntfImpl : public SimpleInterface::BaseParams { public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_VIDEO, + MEDIA_MIMETYPE_VIDEO) { + noPrivateBuffers(); // TODO: account for our buffers here + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); setDerivedInstance(this); addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) - .build()); - - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); - - addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( -#ifdef MPEG4 - MEDIA_MIMETYPE_VIDEO_MPEG4 -#else - MEDIA_MIMETYPE_VIDEO_H263 -#endif - )) + DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) + .withConstValue(new C2StreamUsageTuning::input( + 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); - addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) - .withConstValue(new C2StreamUsageTuning::input( - 0u, (uint64_t)C2MemoryUsage::CPU_READ)) - .build()); - addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 176, 144)) @@ -217,10 +211,6 @@ class C2SoftMpeg4Enc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; std::shared_ptr mSize; std::shared_ptr mFrameRate; diff --git a/media/codec2/components/vpx/C2SoftVp8Enc.cpp b/media/codec2/components/vpx/C2SoftVp8Enc.cpp index 0ae717a5d3..f18f5d0459 100644 --- a/media/codec2/components/vpx/C2SoftVp8Enc.cpp +++ b/media/codec2/components/vpx/C2SoftVp8Enc.cpp @@ -23,8 +23,6 @@ namespace android { -constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder"; - C2SoftVp8Enc::C2SoftVp8Enc(const char* name, c2_node_id_t id, const std::shared_ptr& intfImpl) : C2SoftVpxEnc(name, id, intfImpl), mDCTPartitions(0), mProfile(1) {} diff --git a/media/codec2/components/vpx/C2SoftVp9Enc.cpp b/media/codec2/components/vpx/C2SoftVp9Enc.cpp index b26170f901..740dbda6e5 100644 --- a/media/codec2/components/vpx/C2SoftVp9Enc.cpp +++ b/media/codec2/components/vpx/C2SoftVp9Enc.cpp @@ -23,8 +23,6 @@ namespace android { -constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder"; - C2SoftVp9Enc::C2SoftVp9Enc(const char* name, c2_node_id_t id, const std::shared_ptr& intfImpl) : C2SoftVpxEnc(name, id, intfImpl), diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h index 5591a49019..90758f9d20 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.h +++ b/media/codec2/components/vpx/C2SoftVpxEnc.h @@ -222,46 +222,46 @@ struct C2SoftVpxEnc : public SimpleC2Component { C2_DO_NOT_COPY(C2SoftVpxEnc); }; -class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { - public: - explicit IntfImpl(const std::shared_ptr& helper) - : C2InterfaceHelper(helper) { - setDerivedInstance(this); +namespace { - addParameter( - DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::input(0u, C2BufferData::GRAPHIC)) - .build()); +#ifdef VP9 +constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder"; +const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP9; +#else +constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder"; +const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP8; +#endif - addParameter( - DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) - .withConstValue( - new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) - .build()); +} // namepsace + +class C2SoftVpxEnc::IntfImpl : public SimpleInterface::BaseParams { + public: + explicit IntfImpl(const std::shared_ptr &helper) + : SimpleInterface::BaseParams( + helper, + COMPONENT_NAME, + C2Component::KIND_ENCODER, + C2Component::DOMAIN_VIDEO, + MEDIA_MIMETYPE_VIDEO) { + noPrivateBuffers(); // TODO: account for our buffers here + noInputReferences(); + noOutputReferences(); + noInputLatency(); + noTimeStretch(); + setDerivedInstance(this); addParameter( - DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( - MEDIA_MIMETYPE_VIDEO_RAW)) + DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) + .withConstValue(new C2ComponentAttributesSetting( + C2Component::ATTRIB_IS_TEMPORAL)) .build()); addParameter( - DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) - .withConstValue(AllocSharedString( -#ifdef VP9 - MEDIA_MIMETYPE_VIDEO_VP9 -#else - MEDIA_MIMETYPE_VIDEO_VP8 -#endif - )) + DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) + .withConstValue(new C2StreamUsageTuning::input( + 0u, (uint64_t)C2MemoryUsage::CPU_READ)) .build()); - addParameter(DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE) - .withConstValue(new C2StreamUsageTuning::input( - 0u, (uint64_t)C2MemoryUsage::CPU_READ)) - .build()); - addParameter( DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) @@ -416,10 +416,6 @@ class C2SoftVpxEnc::IntfImpl : public C2InterfaceHelper { } private: - std::shared_ptr mInputFormat; - std::shared_ptr mOutputFormat; - std::shared_ptr mInputMediaType; - std::shared_ptr mOutputMediaType; std::shared_ptr mUsage; std::shared_ptr mSize; std::shared_ptr mFrameRate; -- GitLab From c1de2dfe8602f10ed3086ab601b626bec632b198 Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 7 May 2019 14:26:40 -0700 Subject: [PATCH 1400/1530] Fix NPE when trying to get input device for attributes. 1. Return error when remote submix device is null. Remote submix device could be null in some case, such as device address error. Returning error to avoid native crash. 2. Unregister policy mix if set device connection fails. In that case, get input device could fail when policy mix is not found. Bug: 131906484 Test: Manually Change-Id: I03ab2bf217341b672c14adfba96d92ad9b453b01 --- services/audioflinger/AudioFlinger.cpp | 6 +++- .../managerdefault/AudioPolicyManager.cpp | 29 ++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 5e5ea1120b..66466b218a 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1900,12 +1900,16 @@ sp AudioFlinger::createRecord(const CreateRecordInput& inpu input.opPackageName, &input.config, output.flags, &output.selectedDeviceId, &portId); + if (lStatus != NO_ERROR) { + ALOGE("createRecord() getInputForAttr return error %d", lStatus); + goto Exit; + } { Mutex::Autolock _l(mLock); RecordThread *thread = checkRecordThread_l(output.inputId); if (thread == NULL) { - ALOGE("createRecord() checkRecordThread_l failed"); + ALOGE("createRecord() checkRecordThread_l failed, input handle %d", output.inputId); lStatus = BAD_VALUE; goto Exit; } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 2dc7cad495..0b3ea7a048 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1991,16 +1991,25 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, strncmp(attributes.tags, "addr=", strlen("addr=")) == 0) { status = mPolicyMixes.getInputMixForAttr(attributes, &policyMix); if (status != NO_ERROR) { + ALOGW("%s could not find input mix for attr %s", + __func__, toString(attributes).c_str()); goto error; } + device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, + String8(attr->tags + strlen("addr=")), + AUDIO_FORMAT_DEFAULT); + if (device == nullptr) { + ALOGW("%s could not find device for source %d, tags %s", + __func__, attributes.source, attributes.tags); + status = BAD_VALUE; + goto error; + } + if (is_mix_loopback_render(policyMix->mRouteFlags)) { *inputType = API_INPUT_MIX_PUBLIC_CAPTURE_PLAYBACK; } else { *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; } - device = mAvailableInputDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - String8(attr->tags + strlen("addr=")), - AUDIO_FORMAT_DEFAULT); } else { if (explicitRoutingDevice != nullptr) { device = explicitRoutingDevice; @@ -2867,14 +2876,12 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) rSubmixModule->addInputProfile(address, &inputConfig, AUDIO_DEVICE_IN_REMOTE_SUBMIX, address); - if (mix.mMixType == MIX_TYPE_PLAYERS) { - setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); - } else { - setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); + if ((res = setDeviceConnectionStateInt(mix.mDeviceType, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT)) != NO_ERROR) { + ALOGE("Failed to set remote submix device available, type %u, address %s", + mix.mDeviceType, address.string()); + break; } } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { String8 address = mix.mDeviceAddress; -- GitLab From 69b6ab33e6883c57523ba2b5f51db61b3005b58f Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Wed, 8 May 2019 17:11:39 -0700 Subject: [PATCH 1401/1530] MP4: limit extent_count in iloc box. ItemLoc::getLoc() has a CHECK for extents.size() == 1. Test: poc & MediaMetadataRetrieverTest Bug: 124389881 Change-Id: Ic45cafb8ce47aea106aabdffeccd031b446579b2 --- media/extractors/mp4/ItemTable.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp index d56abaa274..8c8e6d19f9 100644 --- a/media/extractors/mp4/ItemTable.cpp +++ b/media/extractors/mp4/ItemTable.cpp @@ -426,10 +426,8 @@ status_t IlocBox::parse(off64_t offset, size_t size) { } ALOGV("extent_count %d", extent_count); - if (extent_count > 1 && (offset_size == 0 || length_size == 0)) { - // if the item is dividec into more than one extents, offset and - // length must be present. - return ERROR_MALFORMED; + if (extent_count > 1) { + return ERROR_UNSUPPORTED; } offset += 2; -- GitLab From dab73397f0a24be548eee9160fbb0894686547ab Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 8 May 2019 17:18:10 -0700 Subject: [PATCH 1402/1530] media: temporarily allow as top-level media_codecs.xml tag Bug: 132269037 Change-Id: I239fa6dde6cdb9a88abb5ff557001ecb3836e9d9 --- media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp index 6a1b9a8931..e70cafc04b 100644 --- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp +++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp @@ -654,7 +654,7 @@ void MediaCodecsXmlParser::Impl::Parser::startElementHandler( nextSection = SECTION_ENCODERS; } else if (strEq(name, "Settings")) { nextSection = SECTION_SETTINGS; - } else if (strEq(name, "MediaCodecs")) { + } else if (strEq(name, "MediaCodecs") || strEq(name, "Included")) { return; } else { break; -- GitLab From 2dab03030c95b40902e66b81a247738e375ebcf9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 8 May 2019 18:15:55 -0700 Subject: [PATCH 1403/1530] audioflinger: honor flag AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY commit 74e01fa7 did not bypass app ops policy when flag AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY is set. Bug: 131873101 Test: repro steps in bug Change-Id: Idbce26cfdcddbb7a2ae8702ce3d135ef5a69f047 --- services/audioflinger/PlaybackTracks.h | 2 +- services/audioflinger/Tracks.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index bb97f8d3f0..7008ceed41 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -26,7 +26,7 @@ public: bool hasOpPlayAudio() const; static sp createIfNeeded( - uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType); + uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType); private: OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id); diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index e1f00c190d..b0817ed3ea 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -387,18 +387,24 @@ status_t AudioFlinger::TrackHandle::onTransact( // static sp AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded( - uid_t uid, audio_usage_t usage, int id, audio_stream_type_t streamType) + uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType) { if (isAudioServerOrRootUid(uid)) { - ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, usage); + ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, attr.usage); return nullptr; } // stream type has been filtered by audio policy to indicate whether it can be muted if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) { - ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", id, usage); + ALOGD("OpPlayAudio: not muting track:%d usage:%d ENFORCED_AUDIBLE", id, attr.usage); return nullptr; } - return new OpPlayAudioMonitor(uid, usage, id); + if ((attr.flags & AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY) + == AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY) { + ALOGD("OpPlayAudio: not muting track:%d flags %#x have FLAG_BYPASS_INTERRUPTION_POLICY", + id, attr.flags); + return nullptr; + } + return new OpPlayAudioMonitor(uid, attr.usage, id); } AudioFlinger::PlaybackThread::OpPlayAudioMonitor::OpPlayAudioMonitor( @@ -508,7 +514,7 @@ AudioFlinger::PlaybackThread::Track::Track( mPresentationCompleteFrames(0), mFrameMap(16 /* sink-frame-to-track-frame map memory */), mVolumeHandler(new media::VolumeHandler(sampleRate)), - mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(uid, attr.usage, id(), streamType)), + mOpPlayAudioMonitor(OpPlayAudioMonitor::createIfNeeded(uid, attr, id(), streamType)), // mSinkTimestamp mFastIndex(-1), mCachedVolume(1.0), -- GitLab From 3daf5d9146b74f3c03b1e2bd72bab547962ec351 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 8 May 2019 22:34:49 -0700 Subject: [PATCH 1404/1530] media: include softomx plugin in omx HAL service Bug: 132213171 Change-Id: I1a35db1344701a7e125e45543b4282b572a28182 --- services/mediacodec/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index 473e21c0a6..ecd437b5db 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -22,6 +22,7 @@ _software_codecs := \ libstagefright_soft_vorbisdec \ libstagefright_soft_vpxdec \ libstagefright_soft_vpxenc \ + libstagefright_softomx_plugin \ # service executable include $(CLEAR_VARS) -- GitLab From 2a49bc31d02b65c412b2af17b09eda90291606e9 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 9 May 2019 08:59:07 -0700 Subject: [PATCH 1405/1530] audioclient: Release a recorder when AudioRecord dies When AudioRecord object is destroyed, it must unregister with AudioManager so it removes the tracker from the tracking list. Bug: 123312504 Test: start and stop audio recording, check "dumpsys audio" Change-Id: Ida71512cb811d7602da349a58a91c95111078ef4 --- media/libaudioclient/RecordingActivityTracker.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/libaudioclient/RecordingActivityTracker.cpp b/media/libaudioclient/RecordingActivityTracker.cpp index bd10895daf..42d4361ce3 100644 --- a/media/libaudioclient/RecordingActivityTracker.cpp +++ b/media/libaudioclient/RecordingActivityTracker.cpp @@ -36,6 +36,9 @@ RecordingActivityTracker::RecordingActivityTracker() RecordingActivityTracker::~RecordingActivityTracker() { + if (mRIId != RECORD_RIID_INVALID && mAudioManager) { + mAudioManager->releaseRecorder(mRIId); + } } audio_unique_id_t RecordingActivityTracker::getRiid() -- GitLab From 5d482c1ac092956960675ace30d13250a717b64e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Mar 2019 16:55:28 -0800 Subject: [PATCH 1406/1530] DO NOT MERGE: audiopolicy: Remove raw pointer references to AudioMix AudioInputDescriptor, AudioOutputDescriptor, and AudioSession used to reference AudioMix instances using a raw pointer. This isn't safe as AudioMix was owned by AudioPolicyMix, which is not referenced by descriptors. Change AudioMix* pointers in Audio{Input|Output}Descriptor and AudioSession to wp which reflects their relationship correctly. To ensure that code does not operate on AudioMix instances independently from AudioPolicyMix, and to avoid introducing a lot of getter / setter methods into AudioPolicyMix, make the latter to inherit AudioMix. This makes sense because AudioPolicyMix is essentially a ref-counted version of AudioMix. Bug: 124899895 Test: build and sanity check on angler, build angler with USE_CONFIGURABLE_AUDIO_POLICY := 1 Merged-In: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a Change-Id: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a --- .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyMix.h | 15 ++--- .../managerdefinitions/include/AudioSession.h | 5 +- .../src/AudioInputDescriptor.cpp | 1 + .../src/AudioOutputDescriptor.cpp | 10 +-- .../managerdefinitions/src/AudioPolicyMix.cpp | 62 +++++++++---------- .../managerdefinitions/src/AudioSession.cpp | 8 ++- .../managerdefault/AudioPolicyManager.cpp | 46 +++++++------- .../managerdefault/AudioPolicyManager.h | 4 +- 10 files changed, 79 insertions(+), 80 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index b169bac72f..00d2c924ef 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -27,7 +27,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. @@ -44,7 +44,7 @@ public: audio_io_handle_t mIoHandle; // input handle audio_devices_t mDevice; // current device this input is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from virtual void toAudioPortConfig(struct audio_port_config *dstConfig, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index c09cb5a942..804f73ffd8 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -29,7 +29,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; class DeviceDescriptor; @@ -126,7 +126,7 @@ public: audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy sp mOutput1; // used by duplicated outputs: first output sp mOutput2; // used by duplicated outputs: second output uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 0bacef7362..9d01ca6d6b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -29,9 +29,11 @@ class SwAudioOutputDescriptor; /** * custom mix entry in mPolicyMixes */ -class AudioPolicyMix : public RefBase { +class AudioPolicyMix : public AudioMix, public RefBase { public: - AudioPolicyMix() {} + AudioPolicyMix(const AudioMix &mix); + AudioPolicyMix(const AudioPolicyMix&) = delete; + AudioPolicyMix& operator=(const AudioPolicyMix&) = delete; const sp &getOutput() const; @@ -39,14 +41,9 @@ public: void clearOutput(); - android::AudioMix *getMix(); - - void setMix(AudioMix &mix); - status_t dump(int fd, int spaces, int index) const; private: - AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; @@ -76,9 +73,9 @@ public: audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, audio_devices_t availableDeviceTypes, - AudioMix **policyMix); + sp *policyMix); - status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index 18fba2570f..56e282f364 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -27,6 +27,7 @@ namespace android { class AudioPolicyClientInterface; +class AudioPolicyMix; class AudioSession : public RefBase, public AudioSessionInfoUpdateListener { @@ -39,7 +40,7 @@ public: audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface); status_t dump(int fd, int spaces, int index) const; @@ -72,7 +73,7 @@ private: bool mIsSoundTrigger; uint32_t mOpenCount; uint32_t mActiveCount; - AudioMix* mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy AudioPolicyClientInterface* mClientInterface; const AudioSessionInfoProvider* mInfoProvider; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 2492ed628f..3beb001ba1 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -20,6 +20,7 @@ #include "AudioInputDescriptor.h" #include "IOProfile.h" #include "AudioGain.h" +#include "AudioPolicyMix.h" #include "HwModule.h" #include #include diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 8593444aba..3ac3cfff8f 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -19,6 +19,7 @@ #include #include "AudioOutputDescriptor.h" +#include "AudioPolicyMix.h" #include "IOProfile.h" #include "AudioGain.h" #include "Volume.h" @@ -313,17 +314,18 @@ void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, } else { mGlobalRefCount += delta; } + sp policyMix = mPolicyMix.promote(); if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 08930f1cdf..327dc4d328 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -27,6 +27,10 @@ namespace android { +AudioPolicyMix::AudioPolicyMix(const AudioMix &mix) : AudioMix(mix) +{ +} + void AudioPolicyMix::setOutput(sp &output) { mOutput = output; @@ -42,16 +46,6 @@ void AudioPolicyMix::clearOutput() mOutput.clear(); } -void AudioPolicyMix::setMix(AudioMix &mix) -{ - mMix = mix; -} - -android::AudioMix *AudioPolicyMix::getMix() -{ - return &mMix; -} - status_t AudioPolicyMix::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; @@ -61,25 +55,25 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const snprintf(buffer, SIZE, "%*sAudio Policy Mix %d:\n", spaces, "", index+1); result.append(buffer); std::string mixTypeLiteral; - if (!MixTypeConverter::toString(mMix.mMixType, mixTypeLiteral)) { - ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMix.mMixType); + if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) { + ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType); return BAD_VALUE; } snprintf(buffer, SIZE, "%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str()); result.append(buffer); std::string routeFlagLiteral; - RouteFlagTypeConverter::maskToString(mMix.mRouteFlags, routeFlagLiteral); + RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral); snprintf(buffer, SIZE, "%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str()); result.append(buffer); std::string deviceLiteral; - deviceToString(mMix.mDeviceType, deviceLiteral); + deviceToString(mDeviceType, deviceLiteral); snprintf(buffer, SIZE, "%*s- device type: %s\n", spaces, "", deviceLiteral.c_str()); result.append(buffer); - snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mMix.mDeviceAddress.string()); + snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mDeviceAddress.string()); result.append(buffer); int indexCriterion = 0; - for (const auto &criterion : mMix.mCriteria) { + for (const auto &criterion : mCriteria) { snprintf(buffer, SIZE, "%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++); result.append(buffer); std::string usageLiteral; @@ -89,7 +83,7 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const } snprintf(buffer, SIZE, "%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str()); result.append(buffer); - if (mMix.mMixType == MIX_TYPE_RECORDERS) { + if (mMixType == MIX_TYPE_RECORDERS) { std::string sourceLiteral; if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) { ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource); @@ -120,12 +114,11 @@ status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); return BAD_VALUE; } - sp policyMix = new AudioPolicyMix(); - policyMix->setMix(mix); + sp policyMix = new AudioPolicyMix(mix); add(address, policyMix); if (desc != 0) { - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; policyMix->setOutput(desc); } return NO_ERROR; @@ -171,8 +164,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute ALOGV("getOutputForAttr() querying %zu mixes:", size()); desc = 0; for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); if (mix->mMixType == MIX_TYPE_PLAYERS) { // TODO if adding more player rules (currently only 2), make rule handling "generic" @@ -269,7 +261,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute (hasUidExcludeRules && uidExclusionFound) || (hasUidMatchRules && !uidMatchFound))) { ALOGV("\tgetOutputForAttr will use mix %zu", i); - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } else if (mix->mMixType == MIX_TYPE_RECORDERS) { @@ -278,7 +270,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute strncmp(attributes.tags + strlen("addr="), mix->mDeviceAddress.string(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } if (desc != 0) { @@ -289,12 +281,13 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } -audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availDevices, - AudioMix **policyMix) +audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource( + audio_source_t inputSource, + audio_devices_t availDevices, + sp *policyMix) { for (size_t i = 0; i < size(); i++) { - AudioMix *mix = valueAt(i)->getMix(); + AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; @@ -317,7 +310,8 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so return AUDIO_DEVICE_NONE; } -status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) +status_t AudioPolicyMixCollection::getInputMixForAttr( + audio_attributes_t attr, sp *policyMix) { if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { return BAD_VALUE; @@ -327,8 +321,7 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A #ifdef LOG_NDEBUG ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); } #endif @@ -339,13 +332,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return BAD_VALUE; } sp audioPolicyMix = valueAt(index); - AudioMix *mix = audioPolicyMix->getMix(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } - *policyMix = mix; + if (policyMix != nullptr) { + *policyMix = audioPolicyMix; + } return NO_ERROR; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index bea9f4f443..2c458337a2 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -19,6 +19,7 @@ #include #include "policy.h" +#include "AudioPolicyMix.h" #include "AudioSession.h" #include "AudioGain.h" #include "TypeConverter.h" @@ -36,7 +37,7 @@ AudioSession::AudioSession(audio_session_t session, audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface) : mSession(session), mInputSource(inputSource), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), @@ -79,9 +80,10 @@ uint32_t AudioSession::changeActiveCount(int delta) if (event != RECORD_CONFIG_EVENT_NONE) { // Dynamic policy callback: // if input maps to a dynamic policy with an activity listener, notify of state change - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + sp policyMix = mPolicyMix.promote(); + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index e8e531ad4c..853a6bb8a7 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1180,10 +1180,9 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, mOutputRoutes.incRouteActivity(session); audio_devices_t newDevice; - AudioMix *policyMix = NULL; + sp policyMix = outputDesc->mPolicyMix.promote(); const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; + if (policyMix != NULL) { address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { newDevice = policyMix->mDeviceType; @@ -1341,12 +1340,13 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, if (outputDesc->mRefCount[stream] == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS + sp policyMix = outputDesc->mPolicyMix.promote(); if (audio_is_remote_submix_device(outputDesc->mDevice) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, + policyMix->mDeviceAddress, "remote-submix"); } } @@ -1490,7 +1490,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, String8 address = String8(""); audio_source_t inputSource = attr->source; audio_source_t halInputSource; - AudioMix *policyMix = NULL; + sp policyMix; if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; @@ -1572,7 +1572,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix) + const sp &policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; audio_source_t halInputSource = inputSource; @@ -1902,10 +1902,11 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, setInputDevice(input, device, true /* force */); if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } @@ -1920,10 +1921,10 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -1971,10 +1972,11 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, if (inputDesc->isActive()) { setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); } else { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } @@ -1982,10 +1984,10 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -4077,7 +4079,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d address.string()); } policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) { @@ -5186,7 +5188,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) + sp *policyMix) { audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; audio_devices_t selectedDeviceFromMix = diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index c831d46a14..0f31279ad9 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -643,7 +643,7 @@ private: audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix); + const sp &policyMix); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); @@ -657,7 +657,7 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp *policyMix = NULL); // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t device, -- GitLab From a306e2a3577d4155345a24e5214f77870f63359b Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Mar 2019 16:55:28 -0800 Subject: [PATCH 1407/1530] DO NOT MERGE: audiopolicy: Remove raw pointer references to AudioMix AudioInputDescriptor, AudioOutputDescriptor, and AudioSession used to reference AudioMix instances using a raw pointer. This isn't safe as AudioMix was owned by AudioPolicyMix, which is not referenced by descriptors. Change AudioMix* pointers in Audio{Input|Output}Descriptor and AudioSession to wp which reflects their relationship correctly. To ensure that code does not operate on AudioMix instances independently from AudioPolicyMix, and to avoid introducing a lot of getter / setter methods into AudioPolicyMix, make the latter to inherit AudioMix. This makes sense because AudioPolicyMix is essentially a ref-counted version of AudioMix. Bug: 124899895 Test: build and sanity check on marlin, build marlin with USE_CONFIGURABLE_AUDIO_POLICY := 1 Merged-In: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a Change-Id: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a --- .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyMix.h | 15 ++--- .../managerdefinitions/include/AudioSession.h | 5 +- .../src/AudioInputDescriptor.cpp | 1 + .../src/AudioOutputDescriptor.cpp | 10 +-- .../managerdefinitions/src/AudioPolicyMix.cpp | 62 +++++++++---------- .../managerdefinitions/src/AudioSession.cpp | 8 ++- .../managerdefault/AudioPolicyManager.cpp | 46 +++++++------- .../managerdefault/AudioPolicyManager.h | 4 +- 10 files changed, 79 insertions(+), 80 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index b25d6d424c..df1cf26b7c 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -27,7 +27,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. @@ -44,7 +44,7 @@ public: audio_io_handle_t mIoHandle; // input handle audio_devices_t mDevice; // current device this input is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from virtual void toAudioPortConfig(struct audio_port_config *dstConfig, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index 5e5d38b069..247f20f5f9 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -29,7 +29,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; class DeviceDescriptor; @@ -141,7 +141,7 @@ public: audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy sp mOutput1; // used by duplicated outputs: first output sp mOutput2; // used by duplicated outputs: second output uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 8fc6fe95f2..33455793b8 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -29,9 +29,11 @@ class SwAudioOutputDescriptor; /** * custom mix entry in mPolicyMixes */ -class AudioPolicyMix : public RefBase { +class AudioPolicyMix : public AudioMix, public RefBase { public: - AudioPolicyMix() {} + AudioPolicyMix(const AudioMix &mix); + AudioPolicyMix(const AudioPolicyMix&) = delete; + AudioPolicyMix& operator=(const AudioPolicyMix&) = delete; const sp &getOutput() const; @@ -39,14 +41,9 @@ public: void clearOutput(); - android::AudioMix *getMix(); - - void setMix(AudioMix &mix); - status_t dump(int fd, int spaces, int index) const; private: - AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; @@ -76,9 +73,9 @@ public: audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, audio_devices_t availableDeviceTypes, - AudioMix **policyMix); + sp *policyMix); - status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index dd5247d9d2..f7d8bf3e46 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -28,6 +28,7 @@ namespace android { class AudioPolicyClientInterface; +class AudioPolicyMix; class AudioSession : public RefBase, public AudioSessionInfoUpdateListener { @@ -40,7 +41,7 @@ public: audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface); status_t dump(int fd, int spaces, int index) const; @@ -75,7 +76,7 @@ private: bool mSilenced; uint32_t mOpenCount; uint32_t mActiveCount; - AudioMix* mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy AudioPolicyClientInterface* mClientInterface; const AudioSessionInfoProvider* mInfoProvider; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 92332fb112..27bc4c63aa 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -21,6 +21,7 @@ #include "AudioInputDescriptor.h" #include "IOProfile.h" #include "AudioGain.h" +#include "AudioPolicyMix.h" #include "HwModule.h" #include #include diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 294a2a695e..d15ba4bafb 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -19,6 +19,7 @@ #include #include "AudioOutputDescriptor.h" +#include "AudioPolicyMix.h" #include "IOProfile.h" #include "AudioGain.h" #include "Volume.h" @@ -307,17 +308,18 @@ void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, } else { mGlobalRefCount += delta; } + sp policyMix = mPolicyMix.promote(); if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 08930f1cdf..327dc4d328 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -27,6 +27,10 @@ namespace android { +AudioPolicyMix::AudioPolicyMix(const AudioMix &mix) : AudioMix(mix) +{ +} + void AudioPolicyMix::setOutput(sp &output) { mOutput = output; @@ -42,16 +46,6 @@ void AudioPolicyMix::clearOutput() mOutput.clear(); } -void AudioPolicyMix::setMix(AudioMix &mix) -{ - mMix = mix; -} - -android::AudioMix *AudioPolicyMix::getMix() -{ - return &mMix; -} - status_t AudioPolicyMix::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; @@ -61,25 +55,25 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const snprintf(buffer, SIZE, "%*sAudio Policy Mix %d:\n", spaces, "", index+1); result.append(buffer); std::string mixTypeLiteral; - if (!MixTypeConverter::toString(mMix.mMixType, mixTypeLiteral)) { - ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMix.mMixType); + if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) { + ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType); return BAD_VALUE; } snprintf(buffer, SIZE, "%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str()); result.append(buffer); std::string routeFlagLiteral; - RouteFlagTypeConverter::maskToString(mMix.mRouteFlags, routeFlagLiteral); + RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral); snprintf(buffer, SIZE, "%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str()); result.append(buffer); std::string deviceLiteral; - deviceToString(mMix.mDeviceType, deviceLiteral); + deviceToString(mDeviceType, deviceLiteral); snprintf(buffer, SIZE, "%*s- device type: %s\n", spaces, "", deviceLiteral.c_str()); result.append(buffer); - snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mMix.mDeviceAddress.string()); + snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mDeviceAddress.string()); result.append(buffer); int indexCriterion = 0; - for (const auto &criterion : mMix.mCriteria) { + for (const auto &criterion : mCriteria) { snprintf(buffer, SIZE, "%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++); result.append(buffer); std::string usageLiteral; @@ -89,7 +83,7 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const } snprintf(buffer, SIZE, "%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str()); result.append(buffer); - if (mMix.mMixType == MIX_TYPE_RECORDERS) { + if (mMixType == MIX_TYPE_RECORDERS) { std::string sourceLiteral; if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) { ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource); @@ -120,12 +114,11 @@ status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); return BAD_VALUE; } - sp policyMix = new AudioPolicyMix(); - policyMix->setMix(mix); + sp policyMix = new AudioPolicyMix(mix); add(address, policyMix); if (desc != 0) { - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; policyMix->setOutput(desc); } return NO_ERROR; @@ -171,8 +164,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute ALOGV("getOutputForAttr() querying %zu mixes:", size()); desc = 0; for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); if (mix->mMixType == MIX_TYPE_PLAYERS) { // TODO if adding more player rules (currently only 2), make rule handling "generic" @@ -269,7 +261,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute (hasUidExcludeRules && uidExclusionFound) || (hasUidMatchRules && !uidMatchFound))) { ALOGV("\tgetOutputForAttr will use mix %zu", i); - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } else if (mix->mMixType == MIX_TYPE_RECORDERS) { @@ -278,7 +270,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute strncmp(attributes.tags + strlen("addr="), mix->mDeviceAddress.string(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } if (desc != 0) { @@ -289,12 +281,13 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } -audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availDevices, - AudioMix **policyMix) +audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource( + audio_source_t inputSource, + audio_devices_t availDevices, + sp *policyMix) { for (size_t i = 0; i < size(); i++) { - AudioMix *mix = valueAt(i)->getMix(); + AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; @@ -317,7 +310,8 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so return AUDIO_DEVICE_NONE; } -status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) +status_t AudioPolicyMixCollection::getInputMixForAttr( + audio_attributes_t attr, sp *policyMix) { if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { return BAD_VALUE; @@ -327,8 +321,7 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A #ifdef LOG_NDEBUG ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); } #endif @@ -339,13 +332,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return BAD_VALUE; } sp audioPolicyMix = valueAt(index); - AudioMix *mix = audioPolicyMix->getMix(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } - *policyMix = mix; + if (policyMix != nullptr) { + *policyMix = audioPolicyMix; + } return NO_ERROR; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index 7cda46b8ab..694c3f2fdd 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -19,6 +19,7 @@ #include #include "policy.h" +#include "AudioPolicyMix.h" #include "AudioSession.h" #include "AudioGain.h" #include "TypeConverter.h" @@ -36,7 +37,7 @@ AudioSession::AudioSession(audio_session_t session, audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface) : mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), @@ -79,9 +80,10 @@ uint32_t AudioSession::changeActiveCount(int delta) if (event != RECORD_CONFIG_EVENT_NONE) { // Dynamic policy callback: // if input maps to a dynamic policy with an activity listener, notify of state change - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + sp policyMix = mPolicyMix.promote(); + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 9d618b0f6c..2736fd422a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1155,10 +1155,9 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, mOutputRoutes.incRouteActivity(session); audio_devices_t newDevice; - AudioMix *policyMix = NULL; + sp policyMix = outputDesc->mPolicyMix.promote(); const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; + if (policyMix != NULL) { address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { newDevice = policyMix->mDeviceType; @@ -1353,12 +1352,13 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, if (outputDesc->mRefCount[stream] == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS + sp policyMix = outputDesc->mPolicyMix.promote(); if (audio_is_remote_submix_device(outputDesc->mDevice) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, + policyMix->mDeviceAddress, "remote-submix"); } } @@ -1498,7 +1498,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, String8 address = String8(""); audio_source_t halInputSource; audio_source_t inputSource = attr->source; - AudioMix *policyMix = NULL; + sp policyMix; DeviceVector inputDevices; if (inputSource == AUDIO_SOURCE_DEFAULT) { @@ -1639,7 +1639,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, audio_source_t inputSource, const audio_config_base_t *config, audio_input_flags_t flags, - AudioMix *policyMix) + const sp &policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; audio_source_t halInputSource = inputSource; @@ -2051,10 +2051,11 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } @@ -2069,10 +2070,10 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -2120,10 +2121,11 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, if (inputDesc->isActive()) { setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); } else { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } @@ -2131,10 +2133,10 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -4351,7 +4353,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d address.string()); } policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) { @@ -5492,7 +5494,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) + sp *policyMix) { audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; audio_devices_t selectedDeviceFromMix = diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 48e0472b2b..45dca2ef52 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -658,7 +658,7 @@ private: audio_source_t inputSource, const audio_config_base_t *config, audio_input_flags_t flags, - AudioMix *policyMix); + const sp &policyMix); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); @@ -672,7 +672,7 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp *policyMix = NULL); // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t device, -- GitLab From 48bfa1fe6db3569733a5ffd5ce861bc4d5f66d83 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 8 May 2019 14:43:28 -0700 Subject: [PATCH 1408/1530] Fix color conversion from OMX_COLOR_FormatYUV420SemiPlanar Use NV12ToABGR for OMX_COLOR_Format32BitRGBA8888, and NV12ToARGB for OMX_COLOR_Format32bitBGRA8888. The naming convention is reverse in libyuv and omx. bug: 129908793 test: locally modify StagefrightMetadataRetriever to prefer hw decoder and output in OMX_COLOR_FormatYUV420SemiPlanar format. The following tests would fail without this patch and pass with this patch: CtsMediaTestCases --android.media.cts.HeifWriterTest#testInputSurface_Grid_Handler fail CtsMediaTestCases --android.media.cts.HeifWriterTest#testInputSurface_Grid_NoHandler fail CtsMediaTestCases --android.media.cts.HeifWriterTest#testInputSurface_NoGrid_Handler fail CtsMediaTestCases --android.media.cts.HeifWriterTest#testInputSurface_NoGrid_NoHandler fail CtsMediaTestCases --android.media.cts.MediaMetadataRetrieverTest#testGetImageAtIndex fail (These tests fail because they use bitmap format of RGB, while others use RGB565.) Change-Id: I94feea2c29479e72d6111ee6a7ce44805b921e73 --- media/libstagefright/colorconversion/ColorConverter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index d136d9e4b1..d685321028 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -395,11 +395,16 @@ status_t ColorConverter::convertYUV420SemiPlanarUseLibYUV( dst.mStride, src.cropWidth(), src.cropHeight()); break; - case OMX_COLOR_Format32BitRGBA8888: + case OMX_COLOR_Format32bitBGRA8888: libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight()); break; + case OMX_COLOR_Format32BitRGBA8888: + libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr, + dst.mStride, src.cropWidth(), src.cropHeight()); + break; + default: return ERROR_UNSUPPORTED; } -- GitLab From b61a9d4cdebb0d572db81930535e63f4a0f3f9d0 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Wed, 8 May 2019 16:05:50 -0700 Subject: [PATCH 1409/1530] libcamera2ndk_vendor: Fix potential use after free of camera_metadata_t Bug: 131566406 Test: Use libcamera2ndk_vendor multiple times without seeing logs / assertions indicating null metadata / corrupted metadata in allocateACaptureRequest. Change-Id: I2154a83bb97a4dd945f15328769b811e9485a0ac Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 16 ++++++++-------- camera/ndk/ndk_vendor/impl/utils.cpp | 7 ++++--- camera/ndk/ndk_vendor/impl/utils.h | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 1fdff40841..b7a995d3fd 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -262,12 +262,12 @@ camera_status_t CameraDevice::isSessionConfigurationSupported( void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, sp &req) { CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData(); - const camera_metadata_t *camera_metadata = metadataCopy.getAndLock(); + camera_metadata_t *camera_metadata = metadataCopy.release(); HCameraMetadata hCameraMetadata; - utils::convertToHidl(camera_metadata, &hCameraMetadata); - metadataCopy.unlock(camera_metadata); + utils::convertToHidl(camera_metadata, &hCameraMetadata, true); req->mPhysicalCameraSettings.resize(1); req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata)); + req->mPhysicalCameraSettings[0].id = getId(); } camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { @@ -398,10 +398,9 @@ void CameraDevice::allocateOneCaptureRequestMetadata( cameraSettings.id = id; // TODO: Do we really need to copy the metadata here ? CameraMetadata metadataCopy = metadata->getInternalData(); - const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock(); + camera_metadata_t *cameraMetadata = metadataCopy.release(); HCameraMetadata hCameraMetadata; - utils::convertToHidl(cameraMetadata, &hCameraMetadata); - metadataCopy.unlock(cameraMetadata); + utils::convertToHidl(cameraMetadata, &hCameraMetadata, true); if (metadata != nullptr) { if (hCameraMetadata.data() != nullptr && mCaptureRequestMetadataQueue != nullptr && @@ -426,11 +425,12 @@ CameraDevice::allocateACaptureRequest(sp& req, const char* devic const std::string& id = req->mPhysicalCameraSettings[i].id; CameraMetadata clone; utils::convertFromHidlCloned(req->mPhysicalCameraSettings[i].settings.metadata(), &clone); + camera_metadata_t *clonep = clone.release(); if (id == deviceId) { - pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + pRequest->settings = new ACameraMetadata(clonep, ACameraMetadata::ACM_REQUEST); } else { pRequest->physicalSettings[req->mPhysicalCameraSettings[i].id] = - new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST); + new ACameraMetadata(clonep, ACameraMetadata::ACM_REQUEST); } } pRequest->targets = new ACameraOutputTargets(); diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp index 5d2d47c175..e4fb204883 100644 --- a/camera/ndk/ndk_vendor/impl/utils.cpp +++ b/camera/ndk/ndk_vendor/impl/utils.cpp @@ -64,13 +64,14 @@ bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawM return true; } -// Note: existing data in dst will be gone. Caller still owns the memory of src -void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) { +// Note: existing data in dst will be gone. dst owns memory if shouldOwn is set +// to true. +void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn) { if (src == nullptr) { return; } size_t size = get_camera_metadata_size(src); - dst->setToExternal((uint8_t *) src, size); + dst->setToExternal((uint8_t *) src, size, shouldOwn); return; } diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h index a03c7bc5b5..f389f03d79 100644 --- a/camera/ndk/ndk_vendor/impl/utils.h +++ b/camera/ndk/ndk_vendor/impl/utils.h @@ -168,8 +168,8 @@ HRotation convertToHidl(int rotation); bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata); -// Note: existing data in dst will be gone. Caller still owns the memory of src -void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst); +// Note: existing data in dst will be gone. +void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn = false); TemplateId convertToHidl(ACameraDevice_request_template templateId); -- GitLab From 98a4990f3159175d20435e7f05949e2ab8fa8b40 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Thu, 9 May 2019 12:52:40 -0700 Subject: [PATCH 1410/1530] libcamera2ndk_vendor: Add missing physicalCameraSettings metadata in saved cached CaptureRequest metadata. Bug: 131925326 Test: AImageReaderVendorTest; test using vendor process using libcamera2ndk_vendor Change-Id: Ie86ec7f070e985121cdc89367bb0d4b227b41985 Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 26 ++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index b7a995d3fd..529c167edc 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -259,15 +259,27 @@ camera_status_t CameraDevice::isSessionConfigurationSupported( } } -void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, - sp &req) { - CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData(); +static void addMetadataToPhysicalCameraSettings(const CameraMetadata *metadata, + const std::string &cameraId, PhysicalCameraSettings *physicalCameraSettings) { + CameraMetadata metadataCopy = *metadata; camera_metadata_t *camera_metadata = metadataCopy.release(); HCameraMetadata hCameraMetadata; - utils::convertToHidl(camera_metadata, &hCameraMetadata, true); - req->mPhysicalCameraSettings.resize(1); - req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata)); - req->mPhysicalCameraSettings[0].id = getId(); + utils::convertToHidl(camera_metadata, &hCameraMetadata, /*shouldOwn*/ true); + physicalCameraSettings->settings.metadata(std::move(hCameraMetadata)); + physicalCameraSettings->id = cameraId; +} + +void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, + sp &req) { + req->mPhysicalCameraSettings.resize(1 + aCaptureRequest->physicalSettings.size()); + addMetadataToPhysicalCameraSettings(&(aCaptureRequest->settings->getInternalData()), getId(), + &(req->mPhysicalCameraSettings[0])); + size_t i = 1; + for (auto &physicalSetting : aCaptureRequest->physicalSettings) { + addMetadataToPhysicalCameraSettings(&(physicalSetting.second->getInternalData()), + physicalSetting.first, &(req->mPhysicalCameraSettings[i])); + i++; + } } camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) { -- GitLab From 0899a9f97ba3b2835b524d5aeb11fe6d2173ec8f Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 5 Mar 2019 16:55:28 -0800 Subject: [PATCH 1411/1530] DO NOT MERGE: audiopolicy: Remove raw pointer references to AudioMix AudioInputDescriptor, AudioOutputDescriptor, and AudioSession used to reference AudioMix instances using a raw pointer. This isn't safe as AudioMix was owned by AudioPolicyMix, which is not referenced by descriptors. Change AudioMix* pointers in Audio{Input|Output}Descriptor and AudioSession to wp which reflects their relationship correctly. To ensure that code does not operate on AudioMix instances independently from AudioPolicyMix, and to avoid introducing a lot of getter / setter methods into AudioPolicyMix, make the latter to inherit AudioMix. This makes sense because AudioPolicyMix is essentially a ref-counted version of AudioMix. Bug: 124899895 Test: build and sanity check on marlin, build marlin with USE_CONFIGURABLE_AUDIO_POLICY := 1 Merged-In: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a Change-Id: Ic508caedefe721ed7e7ba6ee3e9175ba9e8dc23a --- .../include/AudioInputDescriptor.h | 4 +- .../include/AudioOutputDescriptor.h | 4 +- .../include/AudioPolicyMix.h | 15 ++--- .../managerdefinitions/include/AudioSession.h | 5 +- .../src/AudioInputDescriptor.cpp | 1 + .../src/AudioOutputDescriptor.cpp | 10 +-- .../managerdefinitions/src/AudioPolicyMix.cpp | 62 +++++++++---------- .../managerdefinitions/src/AudioSession.cpp | 8 ++- .../managerdefault/AudioPolicyManager.cpp | 46 +++++++------- .../managerdefault/AudioPolicyManager.h | 4 +- 10 files changed, 79 insertions(+), 80 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h index b169bac72f..00d2c924ef 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -27,7 +27,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; // descriptor for audio inputs. Used to maintain current configuration of each opened audio input // and keep track of the usage of this input. @@ -44,7 +44,7 @@ public: audio_io_handle_t mIoHandle; // input handle audio_devices_t mDevice; // current device this input is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy const sp mProfile; // I/O profile this output derives from virtual void toAudioPortConfig(struct audio_port_config *dstConfig, diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h index c09cb5a942..804f73ffd8 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -29,7 +29,7 @@ namespace android { class IOProfile; -class AudioMix; +class AudioPolicyMix; class AudioPolicyClientInterface; class DeviceDescriptor; @@ -126,7 +126,7 @@ public: audio_io_handle_t mIoHandle; // output handle uint32_t mLatency; // audio_output_flags_t mFlags; // - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy sp mOutput1; // used by duplicated outputs: first output sp mOutput2; // used by duplicated outputs: second output uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 0bacef7362..9d01ca6d6b 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -29,9 +29,11 @@ class SwAudioOutputDescriptor; /** * custom mix entry in mPolicyMixes */ -class AudioPolicyMix : public RefBase { +class AudioPolicyMix : public AudioMix, public RefBase { public: - AudioPolicyMix() {} + AudioPolicyMix(const AudioMix &mix); + AudioPolicyMix(const AudioPolicyMix&) = delete; + AudioPolicyMix& operator=(const AudioPolicyMix&) = delete; const sp &getOutput() const; @@ -39,14 +41,9 @@ public: void clearOutput(); - android::AudioMix *getMix(); - - void setMix(AudioMix &mix); - status_t dump(int fd, int spaces, int index) const; private: - AudioMix mMix; // Audio policy mix descriptor sp mOutput; // Corresponding output stream }; @@ -76,9 +73,9 @@ public: audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, audio_devices_t availableDeviceTypes, - AudioMix **policyMix); + sp *policyMix); - status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix); + status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); status_t dump(int fd) const; }; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h index cedf22dd84..3a2a7adc57 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h @@ -28,6 +28,7 @@ namespace android { class AudioPolicyClientInterface; +class AudioPolicyMix; class AudioSession : public RefBase, public AudioSessionInfoUpdateListener { @@ -40,7 +41,7 @@ public: audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface); status_t dump(int fd, int spaces, int index) const; @@ -72,7 +73,7 @@ private: bool mIsSoundTrigger; uint32_t mOpenCount; uint32_t mActiveCount; - AudioMix* mPolicyMix; // non NULL when used by a dynamic policy + wp mPolicyMix; // non NULL when used by a dynamic policy AudioPolicyClientInterface* mClientInterface; const AudioSessionInfoProvider* mInfoProvider; }; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp index 2492ed628f..3beb001ba1 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -20,6 +20,7 @@ #include "AudioInputDescriptor.h" #include "IOProfile.h" #include "AudioGain.h" +#include "AudioPolicyMix.h" #include "HwModule.h" #include #include diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 8593444aba..3ac3cfff8f 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -19,6 +19,7 @@ #include #include "AudioOutputDescriptor.h" +#include "AudioPolicyMix.h" #include "IOProfile.h" #include "AudioGain.h" #include "Volume.h" @@ -313,17 +314,18 @@ void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, } else { mGlobalRefCount += delta; } + sp policyMix = mPolicyMix.promote(); if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 08930f1cdf..327dc4d328 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -27,6 +27,10 @@ namespace android { +AudioPolicyMix::AudioPolicyMix(const AudioMix &mix) : AudioMix(mix) +{ +} + void AudioPolicyMix::setOutput(sp &output) { mOutput = output; @@ -42,16 +46,6 @@ void AudioPolicyMix::clearOutput() mOutput.clear(); } -void AudioPolicyMix::setMix(AudioMix &mix) -{ - mMix = mix; -} - -android::AudioMix *AudioPolicyMix::getMix() -{ - return &mMix; -} - status_t AudioPolicyMix::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; @@ -61,25 +55,25 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const snprintf(buffer, SIZE, "%*sAudio Policy Mix %d:\n", spaces, "", index+1); result.append(buffer); std::string mixTypeLiteral; - if (!MixTypeConverter::toString(mMix.mMixType, mixTypeLiteral)) { - ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMix.mMixType); + if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) { + ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType); return BAD_VALUE; } snprintf(buffer, SIZE, "%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str()); result.append(buffer); std::string routeFlagLiteral; - RouteFlagTypeConverter::maskToString(mMix.mRouteFlags, routeFlagLiteral); + RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral); snprintf(buffer, SIZE, "%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str()); result.append(buffer); std::string deviceLiteral; - deviceToString(mMix.mDeviceType, deviceLiteral); + deviceToString(mDeviceType, deviceLiteral); snprintf(buffer, SIZE, "%*s- device type: %s\n", spaces, "", deviceLiteral.c_str()); result.append(buffer); - snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mMix.mDeviceAddress.string()); + snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mDeviceAddress.string()); result.append(buffer); int indexCriterion = 0; - for (const auto &criterion : mMix.mCriteria) { + for (const auto &criterion : mCriteria) { snprintf(buffer, SIZE, "%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++); result.append(buffer); std::string usageLiteral; @@ -89,7 +83,7 @@ status_t AudioPolicyMix::dump(int fd, int spaces, int index) const } snprintf(buffer, SIZE, "%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str()); result.append(buffer); - if (mMix.mMixType == MIX_TYPE_RECORDERS) { + if (mMixType == MIX_TYPE_RECORDERS) { std::string sourceLiteral; if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) { ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource); @@ -120,12 +114,11 @@ status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); return BAD_VALUE; } - sp policyMix = new AudioPolicyMix(); - policyMix->setMix(mix); + sp policyMix = new AudioPolicyMix(mix); add(address, policyMix); if (desc != 0) { - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; policyMix->setOutput(desc); } return NO_ERROR; @@ -171,8 +164,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute ALOGV("getOutputForAttr() querying %zu mixes:", size()); desc = 0; for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); if (mix->mMixType == MIX_TYPE_PLAYERS) { // TODO if adding more player rules (currently only 2), make rule handling "generic" @@ -269,7 +261,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute (hasUidExcludeRules && uidExclusionFound) || (hasUidMatchRules && !uidMatchFound))) { ALOGV("\tgetOutputForAttr will use mix %zu", i); - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } else if (mix->mMixType == MIX_TYPE_RECORDERS) { @@ -278,7 +270,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute strncmp(attributes.tags + strlen("addr="), mix->mDeviceAddress.string(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = policyMix->getOutput(); + desc = mix->getOutput(); } } if (desc != 0) { @@ -289,12 +281,13 @@ status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attribute return BAD_VALUE; } -audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, - audio_devices_t availDevices, - AudioMix **policyMix) +audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource( + audio_source_t inputSource, + audio_devices_t availDevices, + sp *policyMix) { for (size_t i = 0; i < size(); i++) { - AudioMix *mix = valueAt(i)->getMix(); + AudioPolicyMix *mix = valueAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; @@ -317,7 +310,8 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so return AUDIO_DEVICE_NONE; } -status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix) +status_t AudioPolicyMixCollection::getInputMixForAttr( + audio_attributes_t attr, sp *policyMix) { if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { return BAD_VALUE; @@ -327,8 +321,7 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A #ifdef LOG_NDEBUG ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); - AudioMix *mix = policyMix->getMix(); + sp mix = valueAt(i); ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string()); } #endif @@ -339,13 +332,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A return BAD_VALUE; } sp audioPolicyMix = valueAt(index); - AudioMix *mix = audioPolicyMix->getMix(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } - *policyMix = mix; + if (policyMix != nullptr) { + *policyMix = audioPolicyMix; + } return NO_ERROR; } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp index 5b57d3d2fa..8c4733acb5 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp @@ -19,6 +19,7 @@ #include #include "policy.h" +#include "AudioPolicyMix.h" #include "AudioSession.h" #include "AudioGain.h" #include "TypeConverter.h" @@ -36,7 +37,7 @@ AudioSession::AudioSession(audio_session_t session, audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, - AudioMix* policyMix, + const sp &policyMix, AudioPolicyClientInterface *clientInterface) : mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), @@ -79,9 +80,10 @@ uint32_t AudioSession::changeActiveCount(int delta) if (event != RECORD_CONFIG_EVENT_NONE) { // Dynamic policy callback: // if input maps to a dynamic policy with an activity listener, notify of state change - if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + sp policyMix = mPolicyMix.promote(); + if ((policyMix != NULL) && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, + mClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index bdfaf2f811..ed7a23b4d6 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1197,10 +1197,9 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, mOutputRoutes.incRouteActivity(session); audio_devices_t newDevice; - AudioMix *policyMix = NULL; + sp policyMix = outputDesc->mPolicyMix.promote(); const char *address = NULL; - if (outputDesc->mPolicyMix != NULL) { - policyMix = outputDesc->mPolicyMix; + if (policyMix != NULL) { address = policyMix->mDeviceAddress.string(); if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { newDevice = policyMix->mDeviceType; @@ -1358,12 +1357,13 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, if (outputDesc->mRefCount[stream] == 1) { // Automatically disable the remote submix input when output is stopped on a // re routing mix of type MIX_TYPE_RECORDERS + sp policyMix = outputDesc->mPolicyMix.promote(); if (audio_is_remote_submix_device(outputDesc->mDevice) && - outputDesc->mPolicyMix != NULL && - outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { + policyMix != NULL && + policyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mDeviceAddress, + policyMix->mDeviceAddress, "remote-submix"); } } @@ -1504,7 +1504,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, String8 address = String8(""); audio_source_t halInputSource; audio_source_t inputSource = attr->source; - AudioMix *policyMix = NULL; + sp policyMix; DeviceVector inputDevices; // Explicit routing? @@ -1646,7 +1646,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix) + const sp &policyMix) { audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; audio_source_t halInputSource = inputSource; @@ -2028,10 +2028,11 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, setInputDevice(input, device, true /* force */); if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_MIXING); } @@ -2046,10 +2047,10 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, // For remote submix (a virtual device), we open only one input per capture request. if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -2097,10 +2098,11 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, if (inputDesc->isActive()) { setInputDevice(input, getNewInputDevice(inputDesc), false /* force */); } else { + sp policyMix = inputDesc->mPolicyMix.promote(); // if input maps to a dynamic policy with an activity listener, notify of state change - if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { - mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress, + if ((policyMix != NULL) + && ((policyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { + mpClientInterface->onDynamicPolicyMixStateUpdate(policyMix->mDeviceAddress, MIX_STATE_IDLE); } @@ -2108,10 +2110,10 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, // used by a policy mix of type MIX_TYPE_RECORDERS if (audio_is_remote_submix_device(inputDesc->mDevice)) { String8 address = String8(""); - if (inputDesc->mPolicyMix == NULL) { + if (policyMix == NULL) { address = String8("0"); - } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) { - address = inputDesc->mPolicyMix->mDeviceAddress; + } else if (policyMix->mMixType == MIX_TYPE_PLAYERS) { + address = policyMix->mDeviceAddress; } if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, @@ -4211,7 +4213,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d address.string()); } policyMix->setOutput(desc); - desc->mPolicyMix = policyMix->getMix(); + desc->mPolicyMix = policyMix; } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) { @@ -5325,7 +5327,7 @@ sp AudioPolicyManager::getInputProfile(audio_devices_t device, audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) + sp *policyMix) { audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; audio_devices_t selectedDeviceFromMix = diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index 82c4c3503b..ea8d5c884a 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -645,7 +645,7 @@ private: audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, - AudioMix *policyMix); + const sp &policyMix); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); @@ -659,7 +659,7 @@ private: // select input device corresponding to requested audio source and return associated policy // mix if any. Calls getDeviceForInputSource(). audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix = NULL); + sp *policyMix = NULL); // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t device, -- GitLab From ae22b4843b3cfcbbf6fdbc21e60f1bd0c1d1f763 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 9 May 2019 15:38:55 -0700 Subject: [PATCH 1412/1530] AudioFlinger: Do not permit fast patch tracks if not compatible Test: atest AudioPlaybackCaptureTest Bug: 132346038 Change-Id: I67939ed7f98c546d41148d36764d93723353c893 --- services/audioflinger/AudioFlinger.cpp | 8 +++++++- services/audioflinger/PatchPanel.cpp | 3 +++ services/audioflinger/Threads.cpp | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 5e5ea1120b..612c5e14d5 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -827,6 +827,12 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, ALOGE("Secondary output patchRecord init failed: %d", status); continue; } + + // TODO: We could check compatibility of the secondaryThread with the PatchTrack + // for fast usage: thread has fast mixer, sample rate matches, etc.; + // for now, we exclude fast tracks by removing the Fast flag. + const audio_output_flags_t outputFlags = + (audio_output_flags_t)(output.flags & ~AUDIO_OUTPUT_FLAG_FAST); sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread, streamType, output.sampleRate, @@ -835,7 +841,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, frameCount, patchRecord->buffer(), patchRecord->bufferSize(), - output.flags, + outputFlags, 0ns /* timeout */); status = patchTrack->initCheck(); if (status != NO_ERROR) { diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index a210a6d3df..edb331d4ae 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -506,7 +506,10 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) } if (mPlayback.thread()->hasFastMixer()) { // Create a fast track if the playback thread has fast mixer to get better performance. + // Note: we should have matching channel mask, sample rate, and format by the logic above. outputFlags = (audio_output_flags_t) (outputFlags | AUDIO_OUTPUT_FLAG_FAST); + } else { + outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST); } // create a special playback track to render to playback thread. diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 7daa9291ec..1d99b88410 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4618,6 +4618,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // process fast tracks if (track->isFastTrack()) { + LOG_ALWAYS_FATAL_IF(mFastMixer.get() == nullptr, + "%s(%d): FastTrack(%d) present without FastMixer", + __func__, id(), track->id()); + if (track->getHapticPlaybackEnabled()) { noFastHapticTrack = false; } -- GitLab From 04ed046665ff8466b061eccf63e21fde10508441 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 2 May 2019 17:53:24 -0700 Subject: [PATCH 1413/1530] AudioPolicy: Improve logging on Remote submix errors Bug: 131064451 Test: atest AudioPlaybackCaptureTest Change-Id: I13e662514de11e66f7ca145b320f351640a55ddb Signed-off-by: Kevin Rocard --- .../managerdefault/AudioPolicyManager.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6dc5eb842b..d7dc51a4bb 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1999,7 +1999,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, String8(attr->tags + strlen("addr=")), AUDIO_FORMAT_DEFAULT); if (device == nullptr) { - ALOGW("%s could not find device for source %d, tags %s", + ALOGW("%s could not find in Remote Submix device for source %d, tags %s", __func__, attributes.source, attributes.tags); status = BAD_VALUE; goto error; @@ -2957,17 +2957,17 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) continue; } - if (getDeviceConnectionState(AUDIO_DEVICE_IN_REMOTE_SUBMIX, address.string()) == - AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { - setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); - } - if (getDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address.string()) == - AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { - setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT); + for (auto device : {AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_DEVICE_OUT_REMOTE_SUBMIX}) { + if (getDeviceConnectionState(device, address.string()) == + AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + res = setDeviceConnectionStateInt(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address.string(), "remote-submix", + AUDIO_FORMAT_DEFAULT); + if (res != OK) { + ALOGE("Error making RemoteSubmix device unavailable for mix " + "with type %d, address %s", device, address.string()); + } + } } rSubmixModule->removeOutputProfile(address); rSubmixModule->removeInputProfile(address); -- GitLab From fcc10c6677d1f7c355b5c80e31d46f73fbad34f9 Mon Sep 17 00:00:00 2001 From: Sundong Ahn Date: Fri, 10 May 2019 15:42:32 +0900 Subject: [PATCH 1414/1530] Modify the media_profiles.xsd On some devices, ImageEncoding and ImageDecoding tags are used in any order. And parser in libmedia library can support it. So xsd file is modified for supporting any orther. Bug: 132049541 Test: make -j40 vts Test: vts-tradefed run vts -m VtsValidateMediaProfiles Change-Id: Ib32cfcce82fc72294533905c29227f6e443aa7d6 --- media/libmedia/xsd/api/current.txt | 14 +++++++------- media/libmedia/xsd/media_profiles.xsd | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/media/libmedia/xsd/api/current.txt b/media/libmedia/xsd/api/current.txt index 05e8a4947c..73b5f8ddac 100644 --- a/media/libmedia/xsd/api/current.txt +++ b/media/libmedia/xsd/api/current.txt @@ -44,20 +44,20 @@ package media.profiles { public class CamcorderProfiles { ctor public CamcorderProfiles(); method public int getCameraId(); - method public java.util.List getEncoderProfile(); - method public java.util.List getImageDecoding(); - method public java.util.List getImageEncoding(); + method public java.util.List getEncoderProfile_optional(); + method public java.util.List getImageDecoding_optional(); + method public java.util.List getImageEncoding_optional(); method public void setCameraId(int); } - public static class CamcorderProfiles.ImageDecoding { - ctor public CamcorderProfiles.ImageDecoding(); + public static class CamcorderProfiles.ImageDecodingOptional { + ctor public CamcorderProfiles.ImageDecodingOptional(); method public int getMemCap(); method public void setMemCap(int); } - public static class CamcorderProfiles.ImageEncoding { - ctor public CamcorderProfiles.ImageEncoding(); + public static class CamcorderProfiles.ImageEncodingOptional { + ctor public CamcorderProfiles.ImageEncodingOptional(); method public int getQuality(); method public void setQuality(int); } diff --git a/media/libmedia/xsd/media_profiles.xsd b/media/libmedia/xsd/media_profiles.xsd index a02252af4f..9664456e09 100644 --- a/media/libmedia/xsd/media_profiles.xsd +++ b/media/libmedia/xsd/media_profiles.xsd @@ -35,19 +35,19 @@ - - - + + + - + - + -- GitLab From 7892ceabede55576e4035adbd4f0bf2f27b0655d Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Wed, 8 May 2019 19:43:30 -0700 Subject: [PATCH 1415/1530] CCodec: Detect pipeline fullness based on pending inputs Indicate pipeline fullness if there are {inputDelay + pipelineDelay + smoothnessFactor} pending inputs in the pipeline to avoid allocating more input. Test: make cts -j123 && cts-tradefed run cts-dev -m \ CtsMediaTestCases --compatibility:module-arg \ CtsMediaTestCases:include-annotation:\ android.platform.test.annotations.RequiresDevice Bug: 131217771 Change-Id: Ie08e59d1c6eecce5f07b98b899a0affd8602b04a --- media/codec2/sfplugin/PipelineWatcher.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/codec2/sfplugin/PipelineWatcher.cpp b/media/codec2/sfplugin/PipelineWatcher.cpp index df81d49d4a..74d14e865e 100644 --- a/media/codec2/sfplugin/PipelineWatcher.cpp +++ b/media/codec2/sfplugin/PipelineWatcher.cpp @@ -121,6 +121,13 @@ bool PipelineWatcher::pipelineFull() const { sizeWithInputReleased); return true; } + + size_t sizeWithInputsPending = mFramesInPipeline.size() - sizeWithInputReleased; + if (sizeWithInputsPending > mPipelineDelay + mInputDelay + mSmoothnessFactor) { + ALOGV("pipelineFull: too many inputs pending (%zu) in pipeline, with inputs released (%zu)", + sizeWithInputsPending, sizeWithInputReleased); + return true; + } ALOGV("pipeline has room (total: %zu, input released: %zu)", mFramesInPipeline.size(), sizeWithInputReleased); return false; -- GitLab From e2f34be1b70a564a9e7866934f0b626e98be999f Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Thu, 9 May 2019 12:31:06 -0700 Subject: [PATCH 1416/1530] APM: uid-device affinity: fix for multiple concurrent apps Audio mix rules that contain both a match and exclude for a uid or usage are ignored for routing evaluation. The implementation of the uid-device affinity caused uid matches and exclusions to be added to any mixes, causing previously valid mixes to be discarded in the routing logic. The fix consists in implementing the uid-device affinity by only applying uid exclusions on mixes that: a/ are compatible with the concept (mixes of players that are render only), b/ do not route to the selected devices c/ do not have a uid match rule (such mixes are already exclusive to the uid to match) d/ do not already have a uid exclusion for the uid Fix removeUidDeviceAffinities() that skipped the wrong mixes. Bug: 124319394 Bug: 131180578 Test: atest AudioHostTest#testUidDeviceAffinity Change-Id: I813c4cfb15956e56599740948775c32562266c21 --- media/libaudioclient/AudioPolicy.cpp | 25 +++++++++++++ .../include/media/AudioPolicy.h | 6 +++ .../include/AudioPolicyMix.h | 10 +++++ .../managerdefinitions/src/AudioPolicyMix.cpp | 37 ++++++++++++++----- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/media/libaudioclient/AudioPolicy.cpp b/media/libaudioclient/AudioPolicy.cpp index 65e797fa1e..3cdf0959ab 100644 --- a/media/libaudioclient/AudioPolicy.cpp +++ b/media/libaudioclient/AudioPolicy.cpp @@ -159,4 +159,29 @@ void AudioMix::setMatchUid(uid_t uid) const { mCriteria.add(crit); } +bool AudioMix::hasUidRule(bool match, uid_t uid) const { + const uint32_t rule = match ? RULE_MATCH_UID : RULE_EXCLUDE_UID; + for (size_t i = 0; i < mCriteria.size(); i++) { + if (mCriteria[i].mRule == rule + && mCriteria[i].mValue.mUid == uid) { + return true; + } + } + return false; +} + +bool AudioMix::hasMatchUidRule() const { + for (size_t i = 0; i < mCriteria.size(); i++) { + if (mCriteria[i].mRule == RULE_MATCH_UID) { + return true; + } + } + return false; +} + +bool AudioMix::isDeviceAffinityCompatible() const { + return ((mMixType == MIX_TYPE_PLAYERS) + && (mRouteFlags == MIX_ROUTE_FLAG_RENDER)); +} + } // namespace android diff --git a/media/libaudioclient/include/media/AudioPolicy.h b/media/libaudioclient/include/media/AudioPolicy.h index a40e019d3e..ef39fd127f 100644 --- a/media/libaudioclient/include/media/AudioPolicy.h +++ b/media/libaudioclient/include/media/AudioPolicy.h @@ -106,6 +106,12 @@ public: void setExcludeUid(uid_t uid) const; void setMatchUid(uid_t uid) const; + /** returns true if this mix has a rule to match or exclude the given uid */ + bool hasUidRule(bool match, uid_t uid) const; + /** returns true if this mix has a rule for uid match (any uid) */ + bool hasMatchUidRule() const; + /** returns true if this mix can be used for uid-device affinity routing */ + bool isDeviceAffinityCompatible() const; mutable Vector mCriteria; uint32_t mMixType; diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index f2b51d982c..12b5e7d4bb 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -89,6 +89,16 @@ public: status_t getInputMixForAttr(audio_attributes_t attr, sp *policyMix); + /** + * Updates the mix rules in order to make streams associated with the given uid + * be routed to the given audio devices. + * @param uid the uid for which the device affinity is set + * @param devices the vector of devices that this uid may be routed to. A typical + * use is to pass the devices associated with a given zone in a multi-zone setup. + * @return NO_ERROR if the update was successful, INVALID_OPERATION otherwise. + * An example of failure is when there are already rules in place to restrict + * a mix to the given uid (i.e. when a MATCH_UID rule was set for it). + */ status_t setUidDeviceAffinities(uid_t uid, const Vector& devices); status_t removeUidDeviceAffinities(uid_t uid); status_t getDevicesForUid(uid_t uid, Vector& devices) const; diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 26bb354284..98a7800753 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -400,13 +400,29 @@ status_t AudioPolicyMixCollection::getInputMixForAttr( status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, const Vector& devices) { + // verify feasibility: for each player mix: if it already contains a + // "match uid" rule for this uid, return an error + // (adding a uid-device affinity would result in contradictory rules) + for (size_t i = 0; i < size(); i++) { + const AudioPolicyMix* mix = valueAt(i).get(); + if (!mix->isDeviceAffinityCompatible()) { + continue; + } + if (mix->hasUidRule(true /*match*/, uid)) { + return INVALID_OPERATION; + } + } + // remove existing rules for this uid removeUidDeviceAffinities(uid); - // for each player mix: add a rule to match or exclude the uid based on the device + // for each player mix: + // IF device is not a target for the mix, + // AND it doesn't have a "match uid" rule + // THEN add a rule to exclude the uid for (size_t i = 0; i < size(); i++) { const AudioPolicyMix *mix = valueAt(i).get(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (!mix->isDeviceAffinityCompatible()) { continue; } // check if this mix goes to a device in the list of devices @@ -418,12 +434,14 @@ status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, break; } } - if (deviceMatch) { - mix->setMatchUid(uid); - } else { + if (!deviceMatch && !mix->hasMatchUidRule()) { // this mix doesn't go to one of the listed devices for the given uid, + // and it's not already restricting the mix on a uid, // modify its rules to exclude the uid - mix->setExcludeUid(uid); + if (!mix->hasUidRule(false /*match*/, uid)) { + // no need to do it again if uid is already excluded + mix->setExcludeUid(uid); + } } } @@ -435,14 +453,15 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { for (size_t i = 0; i < size(); i++) { bool foundUidRule = false; const AudioPolicyMix *mix = valueAt(i).get(); - if (mix->mMixType != MIX_TYPE_PLAYERS) { + if (!mix->isDeviceAffinityCompatible()) { continue; } std::vector criteriaToRemove; for (size_t j = 0; j < mix->mCriteria.size(); j++) { const uint32_t rule = mix->mCriteria[j].mRule; - // is this rule affecting the uid? - if ((rule == RULE_EXCLUDE_UID || rule == RULE_MATCH_UID) + // is this rule excluding the uid? (not considering uid match rules + // as those are not used for uid-device affinity) + if (rule == RULE_EXCLUDE_UID && uid == mix->mCriteria[j].mValue.mUid) { foundUidRule = true; criteriaToRemove.insert(criteriaToRemove.begin(), j); -- GitLab From 863bcfc2760b4214cfb8854421e8804e068f9278 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 22 Mar 2019 16:32:55 +0530 Subject: [PATCH 1417/1530] codec2: add support for B Frames in C2SoftHevcEnc Test: VtsHidlC2V1_0TargetVideoEncTest -I software -C c2.android.hevc.encoder -P /sdcard/res/ Bug: 129121741 Change-Id: I6ddff3f815549425fe12bf809c5d8695127fee3c --- .../codec2/components/hevc/C2SoftHevcEnc.cpp | 180 ++++++++++++------ media/codec2/components/hevc/C2SoftHevcEnc.h | 16 +- 2 files changed, 139 insertions(+), 57 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index b27ee4e554..efeab6c9c2 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -56,10 +56,19 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface::BaseParams { noPrivateBuffers(); // TODO: account for our buffers here noInputReferences(); noOutputReferences(); - noInputLatency(); noTimeStretch(); setDerivedInstance(this); + addParameter( + DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) + .withDefault(new C2PortActualDelayTuning::input( + DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD)) + .withFields({C2F(mActualInputDelay, value).inRange( + 0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)}) + .withSetter( + Setter::StrictValueWithNoDeps) + .build()); + addParameter( DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES) .withConstValue(new C2ComponentAttributesSetting( @@ -462,7 +471,8 @@ c2_status_t C2SoftHevcEnc::initEncParams() { mIvVideoColorFormat = IV_YUV_420P; mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores; mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile; - mEncParams.s_lap_prms.i4_rc_look_ahead_pics = 0; + mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD; + mEncParams.s_coding_tools_prms.i4_max_temporal_layers = DEFAULT_B_FRAMES; switch (mBitrateMode->value) { case C2Config::BITRATE_IGNORE: @@ -512,10 +522,9 @@ c2_status_t C2SoftHevcEnc::releaseEncoder() { c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode, const std::shared_ptr& pool) { - (void)drainMode; - (void)pool; - return C2_OK; + return drainInternal(drainMode, pool, nullptr); } + c2_status_t C2SoftHevcEnc::initEncoder() { CHECK(!mCodecCtx); { @@ -552,7 +561,7 @@ c2_status_t C2SoftHevcEnc::initEncoder() { c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, const C2GraphicView* const input, - uint64_t timestamp) { + uint64_t workIndex) { ihevce_static_cfg_params_t* params = &mEncParams; memset(ps_encode_ip, 0, sizeof(*ps_encode_ip)); @@ -696,7 +705,92 @@ c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, ps_encode_ip->i4_curr_peak_bitrate = params->s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0]; ps_encode_ip->i4_curr_rate_factor = params->s_config_prms.i4_rate_factor; - ps_encode_ip->u8_pts = timestamp; + ps_encode_ip->u8_pts = workIndex; + return C2_OK; +} + +void C2SoftHevcEnc::finishWork(uint64_t index, + const std::unique_ptr& work, + const std::shared_ptr& pool, + ihevce_out_buf_t* ps_encode_op) { + std::shared_ptr block; + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; + c2_status_t status = + pool->fetchLinearBlock(ps_encode_op->i4_bytes_generated, usage, &block); + if (C2_OK != status) { + ALOGE("fetchLinearBlock for Output failed with status 0x%x", status); + mSignalledError = true; + work->result = status; + work->workletsProcessed = 1u; + return; + } + C2WriteView wView = block->map().get(); + if (C2_OK != wView.error()) { + ALOGE("write view map failed with status 0x%x", wView.error()); + mSignalledError = true; + work->result = wView.error(); + work->workletsProcessed = 1u; + return; + } + memcpy(wView.data(), ps_encode_op->pu1_output_buf, + ps_encode_op->i4_bytes_generated); + + std::shared_ptr buffer = + createLinearBuffer(block, 0, ps_encode_op->i4_bytes_generated); + + DUMP_TO_FILE(mOutFile, ps_encode_op->pu1_output_buf, + ps_encode_op->i4_bytes_generated); + + if (ps_encode_op->i4_is_key_frame) { + ALOGV("IDR frame produced"); + buffer->setInfo(std::make_shared( + 0u /* stream id */, C2Config::SYNC_FRAME)); + } + + auto fillWork = [buffer](const std::unique_ptr& work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)0; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.buffers.push_back(buffer); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; + }; + if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) { + fillWork(work); + if (mSignalledEos) { + work->worklets.front()->output.flags = + C2FrameData::FLAG_END_OF_STREAM; + } + } else { + finish(index, fillWork); + } +} + +c2_status_t C2SoftHevcEnc::drainInternal( + uint32_t drainMode, + const std::shared_ptr &pool, + const std::unique_ptr &work) { + + if (drainMode == NO_DRAIN) { + ALOGW("drain with NO_DRAIN: no-op"); + return C2_OK; + } + if (drainMode == DRAIN_CHAIN) { + ALOGW("DRAIN_CHAIN not supported"); + return C2_OMITTED; + } + + while (true) { + ihevce_out_buf_t s_encode_op{}; + memset(&s_encode_op, 0, sizeof(s_encode_op)); + + ihevce_encode(mCodecCtx, nullptr, &s_encode_op); + if (s_encode_op.i4_bytes_generated) { + finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op); + } else { + if (work->workletsProcessed != 1u) fillEmptyWork(work); + break; + } + } return C2_OK; } @@ -704,7 +798,7 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, const std::shared_ptr& pool) { // Initialize output work work->result = C2_OK; - work->workletsProcessed = 1u; + work->workletsProcessed = 0u; work->worklets.front()->output.flags = work->input.flags; if (mSignalledError || mSignalledEos) { @@ -721,6 +815,7 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, ALOGE("Failed to initialize encoder : 0x%x", status); mSignalledError = true; work->result = status; + work->workletsProcessed = 1u; return; } } @@ -728,6 +823,8 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, std::shared_ptr view; std::shared_ptr inputBuffer = nullptr; bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); + if (eos) mSignalledEos = true; + if (!work->input.buffers.empty()) { inputBuffer = work->input.buffers[0]; view = std::make_shared( @@ -736,13 +833,12 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, ALOGE("graphic view map err = %d", view->error()); mSignalledError = true; work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; return; } } - IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK; - fillEmptyWork(work); if (!mSpsPpsHeaderReceived) { ihevce_out_buf_t s_header_op{}; err = ihevce_encode_header(mCodecCtx, &s_header_op); @@ -754,6 +850,7 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, ALOGE("CSD allocation failed"); mSignalledError = true; work->result = C2_NO_MEMORY; + work->workletsProcessed = 1u; return; } memcpy(csd->m.value, s_header_op.pu1_output_buf, @@ -764,34 +861,40 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, mSpsPpsHeaderReceived = true; } if (!inputBuffer) { + work->workletsProcessed = 1u; return; } } ihevce_inp_buf_t s_encode_ip{}; ihevce_out_buf_t s_encode_op{}; - uint64_t timestamp = work->input.ordinal.timestamp.peekull(); + uint64_t workIndex = work->input.ordinal.frameIndex.peekull(); - status = setEncodeArgs(&s_encode_ip, view.get(), timestamp); + status = setEncodeArgs(&s_encode_ip, view.get(), workIndex); if (C2_OK != status) { ALOGE("setEncodeArgs failed : 0x%x", status); mSignalledError = true; work->result = status; + work->workletsProcessed = 1u; return; } uint64_t timeDelay = 0; uint64_t timeTaken = 0; + memset(&s_encode_op, 0, sizeof(s_encode_op)); GETTIME(&mTimeStart, nullptr); TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); - ihevce_inp_buf_t* ps_encode_ip = (inputBuffer) ? &s_encode_ip : nullptr; - - err = ihevce_encode(mCodecCtx, ps_encode_ip, &s_encode_op); - if (IHEVCE_EOK != err) { - ALOGE("Encode Frame failed : 0x%x", err); - mSignalledError = true; - work->result = C2_CORRUPTED; - return; + if (inputBuffer) { + err = ihevce_encode(mCodecCtx, &s_encode_ip, &s_encode_op); + if (IHEVCE_EOK != err) { + ALOGE("Encode Frame failed : 0x%x", err); + mSignalledError = true; + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + return; + } + } else if (!eos) { + fillEmptyWork(work); } GETTIME(&mTimeEnd, nullptr); @@ -802,42 +905,11 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, (int)timeDelay, s_encode_op.i4_bytes_generated); if (s_encode_op.i4_bytes_generated) { - std::shared_ptr block; - C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; - status = pool->fetchLinearBlock(s_encode_op.i4_bytes_generated, usage, &block); - if (C2_OK != status) { - ALOGE("fetchLinearBlock for Output failed with status 0x%x", status); - work->result = C2_NO_MEMORY; - mSignalledError = true; - return; - } - C2WriteView wView = block->map().get(); - if (C2_OK != wView.error()) { - ALOGE("write view map failed with status 0x%x", wView.error()); - work->result = wView.error(); - mSignalledError = true; - return; - } - memcpy(wView.data(), s_encode_op.pu1_output_buf, - s_encode_op.i4_bytes_generated); - - std::shared_ptr buffer = - createLinearBuffer(block, 0, s_encode_op.i4_bytes_generated); - - DUMP_TO_FILE(mOutFile, s_encode_op.pu1_output_buf, - s_encode_op.i4_bytes_generated); - - work->worklets.front()->output.ordinal.timestamp = s_encode_op.u8_pts; - if (s_encode_op.i4_is_key_frame) { - ALOGV("IDR frame produced"); - buffer->setInfo( - std::make_shared( - 0u /* stream id */, C2Config::SYNC_FRAME)); - } - work->worklets.front()->output.buffers.push_back(buffer); + finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op); } + if (eos) { - mSignalledEos = true; + drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work); } } diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h index 8569a3ea3a..f2c76421e6 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.h +++ b/media/codec2/components/hevc/C2SoftHevcEnc.h @@ -35,7 +35,12 @@ namespace android { diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ ((end).tv_usec - (start).tv_usec); -#define CODEC_MAX_CORES 4 +#define CODEC_MAX_CORES 4 +#define MAX_B_FRAMES 1 +#define MAX_RC_LOOKAHEAD 1 + +#define DEFAULT_B_FRAMES 0 +#define DEFAULT_RC_LOOKAHEAD 0 struct C2SoftHevcEnc : public SimpleC2Component { class IntfImpl; @@ -95,10 +100,15 @@ struct C2SoftHevcEnc : public SimpleC2Component { c2_status_t releaseEncoder(); c2_status_t setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, const C2GraphicView* const input, - uint64_t timestamp); + uint64_t workIndex); + void finishWork(uint64_t index, const std::unique_ptr& work, + const std::shared_ptr& pool, + ihevce_out_buf_t* ps_encode_op); + c2_status_t drainInternal(uint32_t drainMode, + const std::shared_ptr& pool, + const std::unique_ptr& work); C2_DO_NOT_COPY(C2SoftHevcEnc); }; - #ifdef FILE_DUMP_ENABLE #define INPUT_DUMP_PATH "/data/local/tmp/hevc" -- GitLab From 26b946deeb4690c3c5363faf92312776b75aaf78 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 15 Mar 2019 18:15:11 +0530 Subject: [PATCH 1418/1530] codec2: Add support for B frames in C2SoftAvcEnc plugin Test: VtsHidlC2V1_0TargetVideoEncTest -I software -C c2.android.avc.encoder -P /sdcard/res/ Bug: 122593474 Change-Id: Id9b40accd4185183e12e20e9dea5e88e8295fae0 --- media/codec2/components/avc/C2SoftAvcEnc.cpp | 217 +++++++++++++++---- media/codec2/components/avc/C2SoftAvcEnc.h | 12 +- 2 files changed, 180 insertions(+), 49 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index 0ae2a5a573..8d9f21ada6 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -55,7 +55,6 @@ public: noPrivateBuffers(); // TODO: account for our buffers here noInputReferences(); noOutputReferences(); - noInputLatency(); noTimeStretch(); setDerivedInstance(this); @@ -81,6 +80,13 @@ public: .withSetter(SizeSetter) .build()); + addParameter( + DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) + .withDefault(new C2PortActualDelayTuning::input(DEFAULT_B_FRAMES)) + .withFields({C2F(mActualInputDelay, value).inRange(0, MAX_B_FRAMES)}) + .withSetter(Setter::StrictValueWithNoDeps) + .build()); + addParameter( DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE) .withDefault(new C2StreamFrameRateInfo::output(0u, 30.)) @@ -365,9 +371,9 @@ C2SoftAvcEnc::C2SoftAvcEnc( mAVCEncLevel(41), mStarted(false), mSawInputEOS(false), - mSawOutputEOS(false), mSignalledError(false), mCodecCtx(nullptr), + mOutBlock(nullptr), // TODO: output buffer size mOutBufferSize(524288) { @@ -380,7 +386,7 @@ C2SoftAvcEnc::C2SoftAvcEnc( } C2SoftAvcEnc::~C2SoftAvcEnc() { - releaseEncoder(); + onRelease(); } c2_status_t C2SoftAvcEnc::onInit() { @@ -394,11 +400,17 @@ c2_status_t C2SoftAvcEnc::onStop() { void C2SoftAvcEnc::onReset() { // TODO: use IVE_CMD_CTL_RESET? releaseEncoder(); + if (mOutBlock) { + mOutBlock.reset(); + } initEncParams(); } void C2SoftAvcEnc::onRelease() { releaseEncoder(); + if (mOutBlock) { + mOutBlock.reset(); + } } c2_status_t C2SoftAvcEnc::onFlush_sm() { @@ -1106,8 +1118,10 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( const C2GraphicView *const input, uint8_t *base, uint32_t capacity, - uint64_t timestamp) { + uint64_t workIndex) { iv_raw_buf_t *ps_inp_raw_buf; + memset(ps_encode_ip, 0, sizeof(*ps_encode_ip)); + memset(ps_encode_op, 0, sizeof(*ps_encode_op)); ps_inp_raw_buf = &ps_encode_ip->s_inp_buf; ps_encode_ip->s_out_buf.pv_buf = base; @@ -1123,8 +1137,8 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( ps_encode_ip->u4_mb_info_type = 0; ps_encode_ip->u4_pic_info_type = 0; ps_encode_ip->u4_is_last = 0; - ps_encode_ip->u4_timestamp_high = timestamp >> 32; - ps_encode_ip->u4_timestamp_low = timestamp & 0xFFFFFFFF; + ps_encode_ip->u4_timestamp_high = workIndex >> 32; + ps_encode_ip->u4_timestamp_low = workIndex & 0xFFFFFFFF; ps_encode_op->s_out_buf.pv_buf = nullptr; /* Initialize color formats */ @@ -1132,7 +1146,7 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( ps_inp_raw_buf->u4_size = sizeof(iv_raw_buf_t); ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat; if (input == nullptr) { - if (mSawInputEOS){ + if (mSawInputEOS) { ps_encode_ip->u4_is_last = 1; } return C2_OK; @@ -1271,17 +1285,46 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( return C2_OK; } +void C2SoftAvcEnc::finishWork(uint64_t workIndex, const std::unique_ptr &work, + ive_video_encode_op_t *ps_encode_op) { + std::shared_ptr buffer = + createLinearBuffer(mOutBlock, 0, ps_encode_op->s_out_buf.u4_bytes); + if (IV_IDR_FRAME == ps_encode_op->u4_encoded_frame_type) { + ALOGV("IDR frame produced"); + buffer->setInfo(std::make_shared( + 0u /* stream id */, C2Config::SYNC_FRAME)); + } + mOutBlock = nullptr; + + auto fillWork = [buffer](const std::unique_ptr &work) { + work->worklets.front()->output.flags = (C2FrameData::flags_t)0; + work->worklets.front()->output.buffers.clear(); + work->worklets.front()->output.buffers.push_back(buffer); + work->worklets.front()->output.ordinal = work->input.ordinal; + work->workletsProcessed = 1u; + }; + if (work && c2_cntr64_t(workIndex) == work->input.ordinal.frameIndex) { + fillWork(work); + if (mSawInputEOS) { + work->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM; + } + } else { + finish(workIndex, fillWork); + } +} + void C2SoftAvcEnc::process( const std::unique_ptr &work, const std::shared_ptr &pool) { // Initialize output work work->result = C2_OK; - work->workletsProcessed = 1u; + work->workletsProcessed = 0u; work->worklets.front()->output.flags = work->input.flags; IV_STATUS_T status; - WORD32 timeDelay, timeTaken; - uint64_t timestamp = work->input.ordinal.timestamp.peekull(); + WORD32 timeDelay = 0; + WORD32 timeTaken = 0; + uint64_t workIndex = work->input.ordinal.frameIndex.peekull(); // Initialize encoder if not already initialized if (mCodecCtx == nullptr) { @@ -1289,27 +1332,29 @@ void C2SoftAvcEnc::process( ALOGE("Failed to initialize encoder"); mSignalledError = true; work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; return; } } if (mSignalledError) { return; } - // while (!mSawOutputEOS && !outQueue.empty()) { c2_status_t error; ive_video_encode_ip_t s_encode_ip; ive_video_encode_op_t s_encode_op; + memset(&s_encode_op, 0, sizeof(s_encode_op)); if (!mSpsPpsHeaderReceived) { constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE; uint8_t header[kHeaderLength]; error = setEncodeArgs( - &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, timestamp); + &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, workIndex); if (error != C2_OK) { ALOGE("setEncodeArgs failed: %d", error); mSignalledError = true; work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; return; } status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op); @@ -1317,6 +1362,7 @@ void C2SoftAvcEnc::process( if (IV_SUCCESS != status) { ALOGE("Encode header failed = 0x%x\n", s_encode_op.u4_error_code); + work->workletsProcessed = 1u; return; } else { ALOGV("Bytes Generated in header %d\n", @@ -1331,6 +1377,7 @@ void C2SoftAvcEnc::process( ALOGE("CSD allocation failed"); mSignalledError = true; work->result = C2_NO_MEMORY; + work->workletsProcessed = 1u; return; } memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes); @@ -1338,6 +1385,10 @@ void C2SoftAvcEnc::process( DUMP_TO_FILE( mOutFile, csd->m.value, csd->flexCount()); + if (work->input.buffers.empty()) { + work->workletsProcessed = 1u; + return; + } } // handle dynamic config parameters @@ -1394,34 +1445,41 @@ void C2SoftAvcEnc::process( inputBuffer->data().graphicBlocks().front().map().get()); if (view->error() != C2_OK) { ALOGE("graphic view map err = %d", view->error()); + work->workletsProcessed = 1u; return; } } - std::shared_ptr block; - do { - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - // TODO: error handling, proper usage, etc. - c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block); - if (err != C2_OK) { - ALOGE("fetch linear block err = %d", err); - work->result = err; - return; + if (mSawInputEOS && work->input.buffers.empty()) break; + if (!mOutBlock) { + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, + C2MemoryUsage::CPU_WRITE}; + // TODO: error handling, proper usage, etc. + c2_status_t err = + pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock); + if (err != C2_OK) { + ALOGE("fetch linear block err = %d", err); + work->result = err; + work->workletsProcessed = 1u; + return; + } } - C2WriteView wView = block->map().get(); + C2WriteView wView = mOutBlock->map().get(); if (wView.error() != C2_OK) { ALOGE("write view map err = %d", wView.error()); work->result = wView.error(); + work->workletsProcessed = 1u; return; } error = setEncodeArgs( - &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), timestamp); + &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), workIndex); if (error != C2_OK) { ALOGE("setEncodeArgs failed : %d", error); mSignalledError = true; work->result = error; + work->workletsProcessed = 1u; return; } @@ -1439,12 +1497,14 @@ void C2SoftAvcEnc::process( if ((s_encode_op.u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) { // TODO: use IVE_CMD_CTL_GETBUFINFO for proper max input size? mOutBufferSize *= 2; + mOutBlock.reset(); continue; } ALOGE("Encode Frame failed = 0x%x\n", s_encode_op.u4_error_code); mSignalledError = true; work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; return; } } while (IV_SUCCESS != status); @@ -1473,41 +1533,104 @@ void C2SoftAvcEnc::process( } } - work->worklets.front()->output.flags = work->input.flags; - work->worklets.front()->output.ordinal = work->input.ordinal; - work->worklets.front()->output.ordinal.timestamp = - ((uint64_t)s_encode_op.u4_timestamp_high << 32) | s_encode_op.u4_timestamp_low; - work->worklets.front()->output.buffers.clear(); - - if (s_encode_op.s_out_buf.u4_bytes) { - std::shared_ptr buffer = - createLinearBuffer(block, 0, s_encode_op.s_out_buf.u4_bytes); - if (IV_IDR_FRAME == s_encode_op.u4_encoded_frame_type) { - ALOGV("IDR frame produced"); - buffer->setInfo(std::make_shared( - 0u /* stream id */, C2Config::SYNC_FRAME)); + if (s_encode_op.output_present) { + if (!s_encode_op.s_out_buf.u4_bytes) { + ALOGE("Error: Output present but bytes generated is zero"); + mSignalledError = true; + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + return; } - work->worklets.front()->output.buffers.push_back(buffer); + uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) | + s_encode_op.u4_timestamp_low; + finishWork(workId, work, &s_encode_op); + } + if (mSawInputEOS) { + drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work); + } +} + +c2_status_t C2SoftAvcEnc::drainInternal( + uint32_t drainMode, + const std::shared_ptr &pool, + const std::unique_ptr &work) { + + if (drainMode == NO_DRAIN) { + ALOGW("drain with NO_DRAIN: no-op"); + return C2_OK; + } + if (drainMode == DRAIN_CHAIN) { + ALOGW("DRAIN_CHAIN not supported"); + return C2_OMITTED; } - if (s_encode_op.u4_is_last) { - // outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS; - mSawOutputEOS = true; - } else { - // outputBufferHeader->nFlags &= ~OMX_BUFFERFLAG_EOS; + while (true) { + if (!mOutBlock) { + C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, + C2MemoryUsage::CPU_WRITE}; + // TODO: error handling, proper usage, etc. + c2_status_t err = + pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock); + if (err != C2_OK) { + ALOGE("fetch linear block err = %d", err); + work->result = err; + work->workletsProcessed = 1u; + return err; + } + } + C2WriteView wView = mOutBlock->map().get(); + if (wView.error()) { + ALOGE("graphic view map failed %d", wView.error()); + return C2_CORRUPTED; + } + ive_video_encode_ip_t s_encode_ip; + ive_video_encode_op_t s_encode_op; + if (C2_OK != setEncodeArgs(&s_encode_ip, &s_encode_op, nullptr, + wView.base(), wView.capacity(), 0)) { + ALOGE("setEncodeArgs failed for drainInternal"); + mSignalledError = true; + work->result = C2_CORRUPTED; + work->workletsProcessed = 1u; + return C2_CORRUPTED; + } + (void)ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op); + + void *freed = s_encode_op.s_inp_buf.apv_bufs[0]; + /* If encoder frees up an input buffer, mark it as free */ + if (freed != nullptr) { + if (mBuffers.count(freed) == 0u) { + ALOGD("buffer not tracked"); + } else { + // Release input buffer reference + mBuffers.erase(freed); + mConversionBuffersInUse.erase(freed); + } + } + + if (s_encode_op.output_present) { + uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) | + s_encode_op.u4_timestamp_low; + finishWork(workId, work, &s_encode_op); + } else { + if (work->workletsProcessed != 1u) { + work->worklets.front()->output.flags = work->input.flags; + work->worklets.front()->output.ordinal = work->input.ordinal; + work->worklets.front()->output.buffers.clear(); + work->workletsProcessed = 1u; + } + break; + } } + + return C2_OK; } c2_status_t C2SoftAvcEnc::drain( uint32_t drainMode, const std::shared_ptr &pool) { - // TODO: use IVE_CMD_CTL_FLUSH? - (void)drainMode; - (void)pool; - return C2_OK; + return drainInternal(drainMode, pool, nullptr); } - class C2SoftAvcEncFactory : public C2ComponentFactory { public: C2SoftAvcEncFactory() : mHelper(std::static_pointer_cast( diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h index 58a86d8718..555055b1ef 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.h +++ b/media/codec2/components/avc/C2SoftAvcEnc.h @@ -33,6 +33,7 @@ namespace android { #define LEN_STATUS_BUFFER (10 * 1024) #define MAX_VBV_BUFF_SIZE (120 * 16384) #define MAX_NUM_IO_BUFS 3 +#define MAX_B_FRAMES 1 #define DEFAULT_MAX_REF_FRM 2 #define DEFAULT_MAX_REORDER_FRM 0 @@ -167,7 +168,6 @@ private: bool mSpsPpsHeaderReceived; bool mSawInputEOS; - bool mSawOutputEOS; bool mSignalledError; bool mIntra4x4; bool mEnableFastSad; @@ -183,6 +183,8 @@ private: size_t mNumMemRecords; // Number of memory records requested by codec size_t mNumCores; // Number of cores used by the codec + std::shared_ptr mOutBlock; + // configurations used by component in process // (TODO: keep this in intf but make them internal only) std::shared_ptr mSize; @@ -230,7 +232,13 @@ private: const C2GraphicView *const input, uint8_t *base, uint32_t capacity, - uint64_t timestamp); + uint64_t workIndex); + void finishWork(uint64_t workIndex, + const std::unique_ptr &work, + ive_video_encode_op_t *ps_encode_op); + c2_status_t drainInternal(uint32_t drainMode, + const std::shared_ptr &pool, + const std::unique_ptr &work); C2_DO_NOT_COPY(C2SoftAvcEnc); }; -- GitLab From c2cba12587834d9f9b7d076d740e8abb2cca3d51 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Thu, 17 May 2018 18:10:24 -0700 Subject: [PATCH 1419/1530] Camera: Add logical camera related metadata in dumpsys Add below logical camera support to dumpsys: - Physical camera id to stream info. - Physical camera request/result metadata. - Physical camera metadata in tag monitor. Also fixed an issue of missing vendor tags in physical metadata. Test: Run physical streams and observe dumpsys Bug: 111940580 Change-Id: I02889b213ff5e7ec29506c0483ef40de9d107ccb --- .../common/FrameProcessorBase.cpp | 19 ++- .../common/FrameProcessorBase.h | 2 + .../device3/Camera3Device.cpp | 112 +++++++++-------- .../libcameraservice/device3/Camera3Device.h | 10 +- .../device3/Camera3IOStreamBase.cpp | 3 + .../libcameraservice/utils/TagMonitor.cpp | 116 ++++++++++-------- .../libcameraservice/utils/TagMonitor.h | 14 ++- 7 files changed, 169 insertions(+), 107 deletions(-) diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp index 41c953bbe2..3d56cd2328 100644 --- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp +++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp @@ -89,12 +89,27 @@ void FrameProcessorBase::dump(int fd, const Vector& /*args*/) { write(fd, result.string(), result.size()); CameraMetadata lastFrame; + std::map lastPhysicalFrames; { // Don't race while dumping metadata Mutex::Autolock al(mLastFrameMutex); lastFrame = CameraMetadata(mLastFrame); + + for (const auto& physicalFrame : mLastPhysicalFrames) { + lastPhysicalFrames.emplace(String8(physicalFrame.mPhysicalCameraId), + physicalFrame.mPhysicalCameraMetadata); + } + } + lastFrame.dump(fd, /*verbosity*/2, /*indentation*/6); + + for (const auto& physicalFrame : lastPhysicalFrames) { + result = String8::format(" Latest received frame for physical camera %s:\n", + physicalFrame.first.c_str()); + write(fd, result.string(), result.size()); + CameraMetadata lastPhysicalMetadata = CameraMetadata(physicalFrame.second); + lastPhysicalMetadata.sort(); + lastPhysicalMetadata.dump(fd, /*verbosity*/2, /*indentation*/6); } - lastFrame.dump(fd, 2, 6); } bool FrameProcessorBase::threadLoop() { @@ -145,6 +160,8 @@ void FrameProcessorBase::processNewFrames(const sp &device) { if (!result.mMetadata.isEmpty()) { Mutex::Autolock al(mLastFrameMutex); mLastFrame.acquire(result.mMetadata); + + mLastPhysicalFrames = std::move(result.mPhysicalMetadatas); } } if (res != NOT_ENOUGH_DATA) { diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h index 00763a4cc2..ae6d15d3e5 100644 --- a/services/camera/libcameraservice/common/FrameProcessorBase.h +++ b/services/camera/libcameraservice/common/FrameProcessorBase.h @@ -83,6 +83,8 @@ class FrameProcessorBase: public Thread { const sp &device); CameraMetadata mLastFrame; + std::vector mLastPhysicalFrames; + }; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 415b2d80a5..dcbcabaa52 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1306,7 +1306,7 @@ status_t Camera3Device::readOneCameraMetadataLocked( void Camera3Device::processOneCaptureResultLocked( const hardware::camera::device::V3_2::CaptureResult& result, const hardware::hidl_vec< - hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas) { + hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata) { camera3_capture_result r; status_t res; r.frame_number = result.frameNumber; @@ -1322,21 +1322,21 @@ void Camera3Device::processOneCaptureResultLocked( r.result = reinterpret_cast(resultMetadata.data()); // Read and validate physical camera metadata - size_t physResultCount = physicalCameraMetadatas.size(); + size_t physResultCount = physicalCameraMetadata.size(); std::vector physCamIds(physResultCount); std::vector phyCamMetadatas(physResultCount); std::vector physResultMetadata; physResultMetadata.resize(physResultCount); - for (size_t i = 0; i < physicalCameraMetadatas.size(); i++) { - res = readOneCameraMetadataLocked(physicalCameraMetadatas[i].fmqMetadataSize, - physResultMetadata[i], physicalCameraMetadatas[i].metadata); + for (size_t i = 0; i < physicalCameraMetadata.size(); i++) { + res = readOneCameraMetadataLocked(physicalCameraMetadata[i].fmqMetadataSize, + physResultMetadata[i], physicalCameraMetadata[i].metadata); if (res != OK) { ALOGE("%s: Frame %d: Failed to read capture result metadata for camera %s", __FUNCTION__, result.frameNumber, - physicalCameraMetadatas[i].physicalCameraId.c_str()); + physicalCameraMetadata[i].physicalCameraId.c_str()); return; } - physCamIds[i] = physicalCameraMetadatas[i].physicalCameraId.c_str(); + physCamIds[i] = physicalCameraMetadata[i].physicalCameraId.c_str(); phyCamMetadatas[i] = reinterpret_cast( physResultMetadata[i].data()); } @@ -3420,6 +3420,14 @@ void Camera3Device::insertResultLocked(CaptureResult *result, return; } + // Update vendor tag id for physical metadata + for (auto& physicalMetadata : result->mPhysicalMetadatas) { + camera_metadata_t *pmeta = const_cast( + physicalMetadata.mPhysicalCameraMetadata.getAndLock()); + set_camera_metadata_vendor_id(pmeta, mVendorTagId); + physicalMetadata.mPhysicalCameraMetadata.unlock(pmeta); + } + // Valid result, insert into queue List::iterator queuedResult = mResultQueue.insert(mResultQueue.end(), CaptureResult(*result)); @@ -3551,8 +3559,14 @@ void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata, } } + std::unordered_map monitoredPhysicalMetadata; + for (auto& m : physicalMetadatas) { + monitoredPhysicalMetadata.emplace(String8(m.mPhysicalCameraId).string(), + CameraMetadata(m.mPhysicalCameraMetadata)); + } mTagMonitor.monitorMetadata(TagMonitor::RESULT, - frameNumber, timestamp.data.i64[0], captureResult.mMetadata); + frameNumber, timestamp.data.i64[0], captureResult.mMetadata, + monitoredPhysicalMetadata); insertResultLocked(&captureResult, frameNumber); } @@ -3966,8 +3980,11 @@ CameraMetadata Camera3Device::getLatestRequestLocked() { void Camera3Device::monitorMetadata(TagMonitor::eventSource source, - int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata) { - mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata); + int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata, + const std::unordered_map& physicalMetadata) { + + mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata, + physicalMetadata); } /** @@ -5107,28 +5124,7 @@ bool Camera3Device::RequestThread::sendRequestsBatch() { NextRequest& nextRequest = mNextRequests.editItemAt(i); nextRequest.submitted = true; - - // Update the latest request sent to HAL - if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged - Mutex::Autolock al(mLatestRequestMutex); - - camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings); - mLatestRequest.acquire(cloned); - - sp parent = mParent.promote(); - if (parent != NULL) { - parent->monitorMetadata(TagMonitor::REQUEST, - nextRequest.halRequest.frame_number, - 0, mLatestRequest); - } - } - - if (nextRequest.halRequest.settings != NULL) { - nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock( - nextRequest.halRequest.settings); - } - - cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest); + updateNextRequest(nextRequest); if (!triggerRemoveFailed) { // Remove any previously queued triggers (after unlock) @@ -5183,26 +5179,7 @@ bool Camera3Device::RequestThread::sendRequestsOneByOne() { // Mark that the request has be submitted successfully. nextRequest.submitted = true; - // Update the latest request sent to HAL - if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged - Mutex::Autolock al(mLatestRequestMutex); - - camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings); - mLatestRequest.acquire(cloned); - - sp parent = mParent.promote(); - if (parent != NULL) { - parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number, - 0, mLatestRequest); - } - } - - if (nextRequest.halRequest.settings != NULL) { - nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock( - nextRequest.halRequest.settings); - } - - cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest); + updateNextRequest(nextRequest); // Remove any previously queued triggers (after unlock) res = removeTriggers(mPrevRequest); @@ -5264,6 +5241,37 @@ bool Camera3Device::RequestThread::skipHFRTargetFPSUpdate(int32_t tag, return false; } +void Camera3Device::RequestThread::updateNextRequest(NextRequest& nextRequest) { + // Update the latest request sent to HAL + if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged + Mutex::Autolock al(mLatestRequestMutex); + + camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings); + mLatestRequest.acquire(cloned); + + mLatestPhysicalRequest.clear(); + for (uint32_t i = 0; i < nextRequest.halRequest.num_physcam_settings; i++) { + cloned = clone_camera_metadata(nextRequest.halRequest.physcam_settings[i]); + mLatestPhysicalRequest.emplace(nextRequest.halRequest.physcam_id[i], + CameraMetadata(cloned)); + } + + sp parent = mParent.promote(); + if (parent != NULL) { + parent->monitorMetadata(TagMonitor::REQUEST, + nextRequest.halRequest.frame_number, + 0, mLatestRequest, mLatestPhysicalRequest); + } + } + + if (nextRequest.halRequest.settings != NULL) { + nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock( + nextRequest.halRequest.settings); + } + + cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest); +} + bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) { ATRACE_CALL(); bool updatesDetected = false; diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index f8245df821..6376e6fd2e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -554,7 +554,7 @@ class Camera3Device : void processOneCaptureResultLocked( const hardware::camera::device::V3_2::CaptureResult& result, const hardware::hidl_vec< - hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas); + hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadata); status_t readOneCameraMetadataLocked(uint64_t fmqResultSize, hardware::camera::device::V3_2::CameraMetadata& resultMetadata, const hardware::camera::device::V3_2::CameraMetadata& result); @@ -910,8 +910,8 @@ class Camera3Device : bool skipHFRTargetFPSUpdate(int32_t tag, const camera_metadata_ro_entry_t& newEntry, const camera_metadata_entry_t& currentEntry); - // Re-configure camera using the latest session parameters. - bool reconfigureCamera(); + // Update next request sent to HAL + void updateNextRequest(NextRequest& nextRequest); wp mParent; wp mStatusTracker; @@ -955,6 +955,7 @@ class Camera3Device : // android.request.id for latest process_capture_request int32_t mLatestRequestId; CameraMetadata mLatestRequest; + std::unordered_map mLatestPhysicalRequest; typedef KeyedVector TriggerMap; Mutex mTriggerMutex; @@ -1263,7 +1264,8 @@ class Camera3Device : TagMonitor mTagMonitor; void monitorMetadata(TagMonitor::eventSource source, int64_t frameNumber, - nsecs_t timestamp, const CameraMetadata& metadata); + nsecs_t timestamp, const CameraMetadata& metadata, + const std::unordered_map& physicalMetadata); metadata_vendor_id_t mVendorTagId; diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index 2e909a04ad..ef0d919310 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -82,6 +82,9 @@ void Camera3IOStreamBase::dump(int fd, const Vector &args) const { lines.appendFormat(" Max size: %zu\n", mMaxSize); lines.appendFormat(" Combined usage: %" PRIu64 ", max HAL buffers: %d\n", mUsage | consumerUsage, camera3_stream::max_buffers); + if (strlen(camera3_stream::physical_camera_id) > 0) { + lines.appendFormat(" Physical camera id: %s\n", camera3_stream::physical_camera_id); + } lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n", mFrameCount, mLastTimestamp); lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n", diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp index f4c49ec995..4037a669cc 100644 --- a/services/camera/libcameraservice/utils/TagMonitor.cpp +++ b/services/camera/libcameraservice/utils/TagMonitor.cpp @@ -100,10 +100,13 @@ void TagMonitor::disableMonitoring() { mMonitoringEnabled = false; mLastMonitoredRequestValues.clear(); mLastMonitoredResultValues.clear(); + mLastMonitoredPhysicalRequestKeys.clear(); + mLastMonitoredPhysicalResultKeys.clear(); } void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp, - const CameraMetadata& metadata) { + const CameraMetadata& metadata, + const std::unordered_map& physicalMetadata) { if (!mMonitoringEnabled) return; std::lock_guard lock(mMonitorMutex); @@ -112,62 +115,77 @@ void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_ timestamp = systemTime(SYSTEM_TIME_BOOTTIME); } + std::string emptyId; for (auto tag : mMonitoredTagList) { - camera_metadata_ro_entry entry = metadata.find(tag); - CameraMetadata &lastValues = (source == REQUEST) ? - mLastMonitoredRequestValues : mLastMonitoredResultValues; - if (lastValues.isEmpty()) { - lastValues = CameraMetadata(mMonitoredTagList.size()); - const camera_metadata_t *metaBuffer = - lastValues.getAndLock(); - set_camera_metadata_vendor_id( - const_cast (metaBuffer), mVendorTagId); - lastValues.unlock(metaBuffer); + monitorSingleMetadata(source, frameNumber, timestamp, emptyId, tag, metadata); + + for (auto& m : physicalMetadata) { + monitorSingleMetadata(source, frameNumber, timestamp, m.first, tag, m.second); } + } +} - camera_metadata_entry lastEntry = lastValues.find(tag); - - if (entry.count > 0) { - bool isDifferent = false; - if (lastEntry.count > 0) { - // Have a last value, compare to see if changed - if (lastEntry.type == entry.type && - lastEntry.count == entry.count) { - // Same type and count, compare values - size_t bytesPerValue = camera_metadata_type_size[lastEntry.type]; - size_t entryBytes = bytesPerValue * lastEntry.count; - int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes); - if (cmp != 0) { - isDifferent = true; - } - } else { - // Count or type has changed +void TagMonitor::monitorSingleMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp, + const std::string& cameraId, uint32_t tag, const CameraMetadata& metadata) { + + CameraMetadata &lastValues = (source == REQUEST) ? + (cameraId.empty() ? mLastMonitoredRequestValues : + mLastMonitoredPhysicalRequestKeys[cameraId]) : + (cameraId.empty() ? mLastMonitoredResultValues : + mLastMonitoredPhysicalResultKeys[cameraId]); + + camera_metadata_ro_entry entry = metadata.find(tag); + if (lastValues.isEmpty()) { + lastValues = CameraMetadata(mMonitoredTagList.size()); + const camera_metadata_t *metaBuffer = + lastValues.getAndLock(); + set_camera_metadata_vendor_id( + const_cast (metaBuffer), mVendorTagId); + lastValues.unlock(metaBuffer); + } + + camera_metadata_entry lastEntry = lastValues.find(tag); + + if (entry.count > 0) { + bool isDifferent = false; + if (lastEntry.count > 0) { + // Have a last value, compare to see if changed + if (lastEntry.type == entry.type && + lastEntry.count == entry.count) { + // Same type and count, compare values + size_t bytesPerValue = camera_metadata_type_size[lastEntry.type]; + size_t entryBytes = bytesPerValue * lastEntry.count; + int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes); + if (cmp != 0) { isDifferent = true; } } else { - // No last entry, so always consider to be different + // Count or type has changed isDifferent = true; } + } else { + // No last entry, so always consider to be different + isDifferent = true; + } - if (isDifferent) { - ALOGV("%s: Tag %s changed", __FUNCTION__, - get_local_camera_metadata_tag_name_vendor_id( - tag, mVendorTagId)); - lastValues.update(entry); - mMonitoringEvents.emplace(source, frameNumber, timestamp, entry); - } - } else if (lastEntry.count > 0) { - // Value has been removed - ALOGV("%s: Tag %s removed", __FUNCTION__, + if (isDifferent) { + ALOGV("%s: Tag %s changed", __FUNCTION__, get_local_camera_metadata_tag_name_vendor_id( tag, mVendorTagId)); - lastValues.erase(tag); - entry.tag = tag; - entry.type = get_local_camera_metadata_tag_type_vendor_id(tag, - mVendorTagId); - entry.count = 0; - mMonitoringEvents.emplace(source, frameNumber, timestamp, entry); + lastValues.update(entry); + mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId); } + } else if (lastEntry.count > 0) { + // Value has been removed + ALOGV("%s: Tag %s removed", __FUNCTION__, + get_local_camera_metadata_tag_name_vendor_id( + tag, mVendorTagId)); + lastValues.erase(tag); + entry.tag = tag; + entry.type = get_local_camera_metadata_tag_type_vendor_id(tag, + mVendorTagId); + entry.count = 0; + mMonitoringEvents.emplace(source, frameNumber, timestamp, entry, cameraId); } } @@ -190,8 +208,9 @@ void TagMonitor::dumpMonitoredMetadata(int fd) { dprintf(fd, " Monitored tag event log:\n"); for (const auto& event : mMonitoringEvents) { int indentation = (event.source == REQUEST) ? 15 : 30; - dprintf(fd, " f%d:%" PRId64 "ns: %*s%s.%s: ", + dprintf(fd, " f%d:%" PRId64 "ns:%*s%*s%s.%s: ", event.frameNumber, event.timestamp, + 2, event.cameraId.c_str(), indentation, event.source == REQUEST ? "REQ:" : "RES:", get_local_camera_metadata_section_name_vendor_id(event.tag, @@ -296,13 +315,14 @@ void TagMonitor::printData(int fd, const uint8_t *data_ptr, uint32_t tag, template TagMonitor::MonitorEvent::MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp, - const T &value) : + const T &value, const std::string& cameraId) : source(src), frameNumber(frameNumber), timestamp(timestamp), tag(value.tag), type(value.type), - newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count) { + newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count), + cameraId(cameraId) { } TagMonitor::MonitorEvent::~MonitorEvent() { diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h index 2dece6209a..1b7b0330fe 100644 --- a/services/camera/libcameraservice/utils/TagMonitor.h +++ b/services/camera/libcameraservice/utils/TagMonitor.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,8 @@ class TagMonitor { // Scan through the metadata and update the monitoring information void monitorMetadata(eventSource source, int64_t frameNumber, - nsecs_t timestamp, const CameraMetadata& metadata); + nsecs_t timestamp, const CameraMetadata& metadata, + const std::unordered_map& physicalMetadata); // Dump current event log to the provided fd void dumpMonitoredMetadata(int fd); @@ -72,6 +74,10 @@ class TagMonitor { static void printData(int fd, const uint8_t *data_ptr, uint32_t tag, int type, int count, int indentation); + void monitorSingleMetadata(TagMonitor::eventSource source, int64_t frameNumber, + nsecs_t timestamp, const std::string& cameraId, uint32_t tag, + const CameraMetadata& metadata); + std::atomic mMonitoringEnabled; std::mutex mMonitorMutex; @@ -82,6 +88,9 @@ class TagMonitor { CameraMetadata mLastMonitoredRequestValues; CameraMetadata mLastMonitoredResultValues; + std::unordered_map mLastMonitoredPhysicalRequestKeys; + std::unordered_map mLastMonitoredPhysicalResultKeys; + /** * A monitoring event * Stores a new metadata field value and the timestamp at which it changed. @@ -90,7 +99,7 @@ class TagMonitor { struct MonitorEvent { template MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp, - const T &newValue); + const T &newValue, const std::string& cameraId); ~MonitorEvent(); eventSource source; @@ -99,6 +108,7 @@ class TagMonitor { uint32_t tag; uint8_t type; std::vector newData; + std::string cameraId; }; // A ring buffer for tracking the last kMaxMonitorEvents metadata changes -- GitLab From cb7aaf94231fd5d330b949842dac793c527fa339 Mon Sep 17 00:00:00 2001 From: Umang Saini Date: Fri, 3 May 2019 17:12:28 +0530 Subject: [PATCH 1420/1530] Fix: Send valid frames for Vorbis in MKV Bug: 132184674 Test: atest android.media.cts.DecoderTest#testDecodeOggMkv Change-Id: I8e7d74b8f8780b30575195a0c923157a1ce24d16 --- media/extractors/mkv/MatroskaExtractor.cpp | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index a1c81f3005..ab76edc35b 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -151,6 +151,7 @@ private: HEVC, MP3, PCM, + VORBIS, OTHER }; @@ -273,6 +274,8 @@ MatroskaSource::MatroskaSource( mType = MP3; } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) { mType = PCM; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { + mType = VORBIS; } } @@ -802,6 +805,26 @@ media_status_t MatroskaSource::readBlock() { AMediaFormat_setInt64(meta, AMEDIAFORMAT_KEY_TIME_US, timeUs); AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, block->IsKey()); + if (mType == VORBIS) { + int32_t sampleRate; + if (!AMediaFormat_getInt32(trackInfo->mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, + &sampleRate)) { + return AMEDIA_ERROR_MALFORMED; + } + int64_t durationUs; + if (!AMediaFormat_getInt64(trackInfo->mMeta, AMEDIAFORMAT_KEY_DURATION, + &durationUs)) { + return AMEDIA_ERROR_MALFORMED; + } + // TODO: Explore if this can be handled similar to MPEG4 extractor where padding is + // signalled instead of VALID_SAMPLES + // Remaining valid samples in Vorbis track + if (durationUs > timeUs) { + int32_t validSamples = ((durationUs - timeUs) * sampleRate) / 1000000ll; + AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_VALID_SAMPLES, validSamples); + } + } + status_t err = frame.Read(mExtractor->mReader, data + trackInfo->mHeaderLen); if (err == OK && mExtractor->mIsWebm -- GitLab From 38bbaf299649d1addcaec105417af0aeca4c2051 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 10 May 2019 12:58:18 -0700 Subject: [PATCH 1421/1530] Camera: Prune depth samples with low confidence Depth samples with low confidence can skew the near/far values and impact the range inverse coding. Avoid using such samples when searching for the near and far points and clamp their values if necessary. Bug: 132248813 Test: Camera CTS Change-Id: I7dc134b50e46c664f9fc8750b9b9b37c416c9afe --- .../common/DepthPhotoProcessor.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp index 6d96163b42..fc79150351 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -64,6 +64,10 @@ using dynamic_depth::Profiles; namespace android { namespace camera3 { +// Depth samples with low confidence can skew the +// near/far values and impact the range inverse coding. +static const float CONFIDENCE_THRESHOLD = .15f; + ExifOrientation getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize) { if ((jpegBuffer == nullptr) || (jpegBufferSize == 0)) { return ExifOrientation::ORIENTATION_UNDEFINED; @@ -238,6 +242,9 @@ inline void unpackDepth16(uint16_t value, std::vector *points /*out*/, auto conf = (value >> 13) & 0x7; float normConfidence = (conf == 0) ? 1.f : (static_cast(conf) - 1) / 7.f; confidence->push_back(normConfidence); + if (normConfidence < CONFIDENCE_THRESHOLD) { + return; + } if (*near > point) { *near = point; @@ -358,8 +365,12 @@ std::unique_ptr processDepthMapFrame(DepthPhotoInputFra auto pointIt = points.begin(); auto confidenceIt = confidence.begin(); while ((pointIt != points.end()) && (confidenceIt != confidence.end())) { - pointsQuantized.push_back(floorf(((far * (*pointIt - near)) / - (*pointIt * (far - near))) * 255.0f)); + auto point = *pointIt; + if ((*confidenceIt) < CONFIDENCE_THRESHOLD) { + point = std::clamp(point, near, far); + } + pointsQuantized.push_back(floorf(((far * (point - near)) / + (point * (far - near))) * 255.0f)); confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f)); confidenceIt++; pointIt++; } -- GitLab From 429844193f7149f3b642f1f260a7f20398eb3778 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 9 May 2019 17:57:03 -0700 Subject: [PATCH 1422/1530] audio policy: add permission check for privileged attributes flags Add check for privileged permission if attributes flags BYPASS_INTERRUPTION_POLICY or BYPASS_MUTE are set. Bug: 131873101 Test: Manual smoke tests for audio playback. Test: Emergency cell broadcast in Change-Id: I073cf669f606f0d081ff5c8d19f39b5e12efe297 --- media/libaudioclient/AudioSystem.cpp | 2 +- media/libaudioclient/IAudioPolicyService.cpp | 57 +++++++++---------- .../include/media/AudioSystem.h | 2 +- .../include/media/IAudioPolicyService.h | 2 +- media/utils/ServiceUtilities.cpp | 19 +++++-- .../include/mediautils/ServiceUtilities.h | 2 + services/audioflinger/AudioFlinger.cpp | 15 ++--- .../managerdefault/AudioPolicyManager.cpp | 2 +- .../service/AudioPolicyInterfaceImpl.cpp | 14 +++-- .../audiopolicy/service/AudioPolicyService.h | 2 +- 10 files changed, 65 insertions(+), 52 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index d359f52ffc..1e7f9fa7a4 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -864,7 +864,7 @@ audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream) return aps->getOutput(stream); } -status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr, +status_t AudioSystem::getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index 9b4221c929..c5484570d4 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -198,7 +198,7 @@ public: return static_cast (reply.readInt32()); } - status_t getOutputForAttr(const audio_attributes_t *attr, + status_t getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, @@ -212,38 +212,27 @@ public: { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); - if (attr == NULL) { - if (stream == NULL) { - ALOGE("getOutputForAttr(): NULL audio attributes and stream type"); - return BAD_VALUE; - } - if (*stream == AUDIO_STREAM_DEFAULT) { - ALOGE("getOutputForAttr unspecified stream type"); - return BAD_VALUE; - } - } - if (output == NULL) { - ALOGE("getOutputForAttr NULL output - shouldn't happen"); + if (attr == nullptr) { + ALOGE("%s NULL audio attributes", __func__); return BAD_VALUE; } - if (selectedDeviceId == NULL) { - ALOGE("getOutputForAttr NULL selectedDeviceId - shouldn't happen"); + if (output == nullptr) { + ALOGE("%s NULL output - shouldn't happen", __func__); return BAD_VALUE; } - if (portId == NULL) { - ALOGE("getOutputForAttr NULL portId - shouldn't happen"); + if (selectedDeviceId == nullptr) { + ALOGE("%s NULL selectedDeviceId - shouldn't happen", __func__); return BAD_VALUE; } - if (secondaryOutputs == NULL) { - ALOGE("getOutputForAttr NULL secondaryOutputs - shouldn't happen"); + if (portId == nullptr) { + ALOGE("%s NULL portId - shouldn't happen", __func__); return BAD_VALUE; } - if (attr == NULL) { - data.writeInt32(0); - } else { - data.writeInt32(1); - data.write(attr, sizeof(audio_attributes_t)); + if (secondaryOutputs == nullptr) { + ALOGE("%s NULL secondaryOutputs - shouldn't happen", __func__); + return BAD_VALUE; } + data.write(attr, sizeof(audio_attributes_t)); data.writeInt32(session); if (stream == NULL) { data.writeInt32(0); @@ -265,6 +254,10 @@ public: if (status != NO_ERROR) { return status; } + status = (status_t)reply.read(&attr, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } *output = (audio_io_handle_t)reply.readInt32(); audio_stream_type_t lStream = (audio_stream_type_t)reply.readInt32(); if (stream != NULL) { @@ -1449,12 +1442,12 @@ status_t BnAudioPolicyService::onTransact( case GET_OUTPUT_FOR_ATTR: { CHECK_INTERFACE(IAudioPolicyService, data, reply); - audio_attributes_t attr = {}; - bool hasAttributes = data.readInt32() != 0; - if (hasAttributes) { - data.read(&attr, sizeof(audio_attributes_t)); - sanetizeAudioAttributes(&attr); + audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER; + status_t status = data.read(&attr, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; } + sanetizeAudioAttributes(&attr); audio_session_t session = (audio_session_t)data.readInt32(); audio_stream_type_t stream = AUDIO_STREAM_DEFAULT; bool hasStream = data.readInt32() != 0; @@ -1472,11 +1465,15 @@ status_t BnAudioPolicyService::onTransact( audio_port_handle_t portId = (audio_port_handle_t)data.readInt32(); audio_io_handle_t output = 0; std::vector secondaryOutputs; - status_t status = getOutputForAttr(hasAttributes ? &attr : NULL, + status = getOutputForAttr(&attr, &output, session, &stream, pid, uid, &config, flags, &selectedDeviceId, &portId, &secondaryOutputs); reply->writeInt32(status); + status = reply->write(&attr, sizeof(audio_attributes_t)); + if (status != NO_ERROR) { + return status; + } reply->writeInt32(output); reply->writeInt32(stream); reply->writeInt32(selectedDeviceId); diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index f79ec21597..56c69f6b56 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -223,7 +223,7 @@ public: static status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); static audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); - static status_t getOutputForAttr(const audio_attributes_t *attr, + static status_t getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index b6390442c4..33ab1f956b 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -57,7 +57,7 @@ public: audio_policy_forced_cfg_t config) = 0; virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0; virtual audio_io_handle_t getOutput(audio_stream_type_t stream) = 0; - virtual status_t getOutputForAttr(const audio_attributes_t *attr, + virtual status_t getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 768cd1eb07..16fdeaf064 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -35,6 +35,8 @@ namespace android { static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO"); +static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE"); +static const String16 sModifyAudioRouting("android.permission.MODIFY_AUDIO_ROUTING"); static String16 resolveCallingPackage(PermissionController& permissionController, const String16& opPackageName, uid_t uid) { @@ -162,9 +164,8 @@ bool settingsAllowed() { } bool modifyAudioRoutingAllowed() { - static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING"); // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. - bool ok = PermissionCache::checkCallingPermission(sModifyAudioRoutingAllowed); + bool ok = PermissionCache::checkCallingPermission(sModifyAudioRouting); if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING"); return ok; } @@ -200,9 +201,19 @@ bool dumpAllowed() { } bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) { - static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE"); bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid); - if (!ok) ALOGE("Request requires android.permission.MODIFY_PHONE_STATE"); + ALOGE_IF(!ok, "Request requires %s", String8(sModifyPhoneState).c_str()); + return ok; +} + +// privileged behavior needed by Dialer, Settings, SetupWizard and CellBroadcastReceiver +bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid) { + static const String16 sWriteSecureSettings("android.permission.WRITE_SECURE_SETTINGS"); + bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid) + || PermissionCache::checkPermission(sWriteSecureSettings, pid, uid) + || PermissionCache::checkPermission(sModifyAudioRouting, pid, uid); + ALOGE_IF(!ok, "Request requires %s or %s", + String8(sModifyPhoneState).c_str(), String8(sWriteSecureSettings).c_str()); return ok; } diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h index c5fe05f9f0..2a6e60966f 100644 --- a/media/utils/include/mediautils/ServiceUtilities.h +++ b/media/utils/include/mediautils/ServiceUtilities.h @@ -81,6 +81,8 @@ bool modifyAudioRoutingAllowed(); bool modifyDefaultAudioEffectsAllowed(); bool dumpAllowed(); bool modifyPhoneStateAllowed(pid_t pid, uid_t uid); +bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid); + status_t checkIMemory(const sp& iMemory); class MediaPackageManager { diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 66466b218a..8f803be5fe 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -296,13 +296,15 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT; audio_io_handle_t io = AUDIO_IO_HANDLE_NONE; audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE; + audio_attributes_t localAttr = *attr; if (direction == MmapStreamInterface::DIRECTION_OUTPUT) { audio_config_t fullConfig = AUDIO_CONFIG_INITIALIZER; fullConfig.sample_rate = config->sample_rate; fullConfig.channel_mask = config->channel_mask; fullConfig.format = config->format; std::vector secondaryOutputs; - ret = AudioSystem::getOutputForAttr(attr, &io, + + ret = AudioSystem::getOutputForAttr(&localAttr, &io, actualSessionId, &streamType, client.clientPid, client.clientUid, &fullConfig, @@ -312,7 +314,7 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di ALOGW_IF(!secondaryOutputs.empty(), "%s does not support secondary outputs, ignoring them", __func__); } else { - ret = AudioSystem::getInputForAttr(attr, &io, + ret = AudioSystem::getInputForAttr(&localAttr, &io, RECORD_RIID_INVALID, actualSessionId, client.clientPid, @@ -330,7 +332,7 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di sp thread = mMmapThreads.valueFor(io); if (thread != 0) { interface = new MmapThreadHandle(thread); - thread->configure(attr, streamType, actualSessionId, callback, *deviceId, portId); + thread->configure(&localAttr, streamType, actualSessionId, callback, *deviceId, portId); *handle = portId; *sessionId = actualSessionId; } else { @@ -691,7 +693,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, uid_t clientUid = input.clientInfo.clientUid; audio_io_handle_t effectThreadId = AUDIO_IO_HANDLE_NONE; std::vector effectIds; - + audio_attributes_t localAttr = input.attr; if (!isAudioServerOrMediaServerUid(callingUid)) { ALOGW_IF(clientUid != callingUid, @@ -720,8 +722,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, output.sessionId = sessionId; output.outputId = AUDIO_IO_HANDLE_NONE; output.selectedDeviceId = input.selectedDeviceId; - - lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType, + lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType, clientPid, clientUid, &input.config, input.flags, &output.selectedDeviceId, &portId, &secondaryOutputs); @@ -782,7 +783,7 @@ sp AudioFlinger::createTrack(const CreateTrackInput& input, output.notificationFrameCount = input.notificationFrameCount; output.flags = input.flags; - track = thread->createTrack_l(client, streamType, input.attr, &output.sampleRate, + track = thread->createTrack_l(client, streamType, localAttr, &output.sampleRate, input.config.format, input.config.channel_mask, &output.frameCount, &output.notificationFrameCount, input.notificationsPerBuffer, input.speed, diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6dc5eb842b..7060f6789b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -1019,7 +1019,7 @@ status_t AudioPolicyManager::getOutputForAttrInt( } if (*output == AUDIO_IO_HANDLE_NONE) { *output = getOutputForDevices(outputDevices, session, *stream, config, - flags, attr->flags & AUDIO_FLAG_MUTE_HAPTIC); + flags, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC); } if (*output == AUDIO_IO_HANDLE_NONE) { return INVALID_OPERATION; diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 06e68a9a19..4b2f48db39 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -166,7 +166,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream) return mAudioPolicyManager->getOutput(stream); } -status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *originalAttr, +status_t AudioPolicyService::getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, @@ -190,13 +190,15 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *original "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid); uid = callingUid; } - audio_attributes_t attr = *originalAttr; if (!mPackageManager.allowPlaybackCapture(uid)) { - attr.flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION; + attr->flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION; + } + if (!bypassInterruptionPolicyAllowed(pid, uid)) { + attr->flags &= ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE); } audio_output_flags_t originalFlags = flags; AutoCallerClear acc; - status_t result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, + status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config, &flags, selectedDeviceId, portId, secondaryOutputs); @@ -212,14 +214,14 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *original *selectedDeviceId = AUDIO_PORT_HANDLE_NONE; *portId = AUDIO_PORT_HANDLE_NONE; secondaryOutputs->clear(); - result = mAudioPolicyManager->getOutputForAttr(&attr, output, session, stream, uid, config, + result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config, &flags, selectedDeviceId, portId, secondaryOutputs); } if (result == NO_ERROR) { sp client = - new AudioPlaybackClient(attr, *output, uid, pid, session, *selectedDeviceId, *stream); + new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream); mAudioPlaybackClients.add(*portId, client); } return result; diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 58256f79bb..8db63a5773 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -75,7 +75,7 @@ public: virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); virtual audio_io_handle_t getOutput(audio_stream_type_t stream); - status_t getOutputForAttr(const audio_attributes_t *attr, + status_t getOutputForAttr(audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, -- GitLab From 6e16157202e8c273d3c84be001e68bf7b13c9ee8 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 10 May 2019 15:53:08 -0700 Subject: [PATCH 1423/1530] audio policy: fix channel match rule for input profiles Modify channel mask match rule for input profiles in order to favor profiles with positional masks and less than 2 channels. This reflects the audio HAL convention to not list index channel masks for less than 2 channels. Bug: 131943885 Test: repro steps in bug. Change-Id: I85e06f3afc2833b6aa7d52675bfd1ffda524fbcf --- .../common/managerdefinitions/src/AudioProfile.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp index a645e0220e..69d6b0c340 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp @@ -178,6 +178,7 @@ status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMa const bool isRecordThread = portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK; const bool isIndex = audio_channel_mask_get_representation(channelMask) == AUDIO_CHANNEL_REPRESENTATION_INDEX; + const uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); int bestMatch = 0; for (size_t i = 0; i < mChannelMasks.size(); i ++) { audio_channel_mask_t supported = mChannelMasks[i]; @@ -201,11 +202,15 @@ status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMa // OR // match score += 100 if the channel mask representations match // match score += number of channels matched. + // match score += 100 if the channel mask representations DO NOT match + // but the profile has positional channel mask and less than 2 channels. + // This is for audio HAL convention to not list index masks for less than 2 channels // // If there are no matched channels, the mask may still be accepted // but the playback or record will be silent. const bool isSupportedIndex = (audio_channel_mask_get_representation(supported) == AUDIO_CHANNEL_REPRESENTATION_INDEX); + const uint32_t supportedChannelCount = audio_channel_count_from_in_mask(supported); int match; if (isIndex && isSupportedIndex) { // index equivalence @@ -213,13 +218,14 @@ status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMa audio_channel_mask_get_bits(channelMask) & audio_channel_mask_get_bits(supported)); } else if (isIndex && !isSupportedIndex) { - const uint32_t equivalentBits = - (1 << audio_channel_count_from_in_mask(supported)) - 1 ; + const uint32_t equivalentBits = (1 << supportedChannelCount) - 1 ; match = __builtin_popcount( audio_channel_mask_get_bits(channelMask) & equivalentBits); + if (supportedChannelCount <= FCC_2) { + match += 100; + } } else if (!isIndex && isSupportedIndex) { - const uint32_t equivalentBits = - (1 << audio_channel_count_from_in_mask(channelMask)) - 1; + const uint32_t equivalentBits = (1 << channelCount) - 1; match = __builtin_popcount( equivalentBits & audio_channel_mask_get_bits(supported)); } else { -- GitLab From 7a2cc2b56bbe16343dea49059ca774e00850a602 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Fri, 10 May 2019 22:52:22 -0700 Subject: [PATCH 1424/1530] Fix MediaMuxerTest#testWebmOutput failure bug Change audio/video synchronization method for webm file. Previous method shifts audio/video separately to 0. This change will get the smallest starting time among all tracks and shifts them together. i.e. audio starts at 15 and video starts at 10, old method will shift both of them to 0 separately, which is not right. New method will shift both of them by 10 (the smaller of them) where after shifting, audio starts at 5 and video starts at 0. Bug: 130363039 Test: MediaMuxerTest#testWebmOutput Change-Id: I97addc72c9e13df649ae65716dc63243dad3849d --- media/libstagefright/webm/WebmFrame.cpp | 8 ++++++++ media/libstagefright/webm/WebmFrame.h | 4 +++- media/libstagefright/webm/WebmFrameThread.cpp | 10 +++++++++- media/libstagefright/webm/WebmFrameThread.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/webm/WebmFrame.cpp b/media/libstagefright/webm/WebmFrame.cpp index 4b0d47cd06..52c30ec196 100644 --- a/media/libstagefright/webm/WebmFrame.cpp +++ b/media/libstagefright/webm/WebmFrame.cpp @@ -62,6 +62,14 @@ sp WebmFrame::SimpleBlock(uint64_t baseTimecode) const { mData); } +uint64_t WebmFrame::getAbsTimecode() { + return mAbsTimecode; +} + +void WebmFrame::updateAbsTimecode(uint64_t newAbsTimecode) { + mAbsTimecode = newAbsTimecode; +} + bool WebmFrame::operator<(const WebmFrame &other) const { if (this->mEos) { return false; diff --git a/media/libstagefright/webm/WebmFrame.h b/media/libstagefright/webm/WebmFrame.h index a410a87480..47f25235bc 100644 --- a/media/libstagefright/webm/WebmFrame.h +++ b/media/libstagefright/webm/WebmFrame.h @@ -25,7 +25,7 @@ struct WebmFrame : LightRefBase { public: const int mType; const bool mKey; - const uint64_t mAbsTimecode; + uint64_t mAbsTimecode; const sp mData; const bool mEos; @@ -33,6 +33,8 @@ public: WebmFrame(int type, bool key, uint64_t absTimecode, MediaBufferBase *buf); ~WebmFrame() {} + uint64_t getAbsTimecode(); + void updateAbsTimecode(uint64_t newAbsTimecode); sp SimpleBlock(uint64_t baseTimecode) const; bool operator<(const WebmFrame &other) const; diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp index 4b6f9283b4..631a2abcb9 100644 --- a/media/libstagefright/webm/WebmFrameThread.cpp +++ b/media/libstagefright/webm/WebmFrameThread.cpp @@ -78,6 +78,7 @@ WebmFrameSinkThread::WebmFrameSinkThread( mVideoFrames(videoThread->mSink), mAudioFrames(audioThread->mSink), mCues(cues), + mStartOffsetTimecode(UINT64_MAX), mDone(true) { } @@ -92,6 +93,7 @@ WebmFrameSinkThread::WebmFrameSinkThread( mVideoFrames(videoSource), mAudioFrames(audioSource), mCues(cues), + mStartOffsetTimecode(UINT64_MAX), mDone(true) { } @@ -213,6 +215,11 @@ void WebmFrameSinkThread::run() { const sp audioFrame = mAudioFrames.peek(); ALOGV("a frame: %p", audioFrame.get()); + if (mStartOffsetTimecode == UINT64_MAX) { + mStartOffsetTimecode = + std::min(audioFrame->getAbsTimecode(), videoFrame->getAbsTimecode()); + } + if (videoFrame->mEos && audioFrame->mEos) { break; } @@ -220,10 +227,12 @@ void WebmFrameSinkThread::run() { if (*audioFrame < *videoFrame) { ALOGV("take a frame"); mAudioFrames.take(); + audioFrame->updateAbsTimecode(audioFrame->getAbsTimecode() - mStartOffsetTimecode); outstandingFrames.push_back(audioFrame); } else { ALOGV("take v frame"); mVideoFrames.take(); + videoFrame->updateAbsTimecode(videoFrame->getAbsTimecode() - mStartOffsetTimecode); outstandingFrames.push_back(videoFrame); if (videoFrame->mKey) numVideoKeyFrames++; @@ -350,7 +359,6 @@ void WebmFrameMediaSourceThread::run() { if (mStartTimeUs == kUninitialized) { mStartTimeUs = timestampUs; } - timestampUs -= mStartTimeUs; if (mPaused && !mResumed) { lastDurationUs = timestampUs - lastTimestampUs; diff --git a/media/libstagefright/webm/WebmFrameThread.h b/media/libstagefright/webm/WebmFrameThread.h index 76c91f1fbd..1ddaf9a9d8 100644 --- a/media/libstagefright/webm/WebmFrameThread.h +++ b/media/libstagefright/webm/WebmFrameThread.h @@ -83,6 +83,7 @@ private: LinkedBlockingQueue >& mVideoFrames; LinkedBlockingQueue >& mAudioFrames; List >& mCues; + uint64_t mStartOffsetTimecode; volatile bool mDone; -- GitLab From 7c856a23927dd2e9e2b7017dbd5336961c5d2c9e Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Mon, 13 May 2019 13:43:26 -0700 Subject: [PATCH 1425/1530] camera: make libcamera2ndk_vendor vendor only Test: build and CGA Bug: 132625347 Change-Id: I7c8676c7ffcc64cc280ce3d7250f94b96eb1de00 --- camera/ndk/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp index 77868567c9..a2ee65dc98 100644 --- a/camera/ndk/Android.bp +++ b/camera/ndk/Android.bp @@ -77,7 +77,7 @@ cc_library_shared { cc_library_shared { name: "libcamera2ndk_vendor", - vendor_available: true, + vendor: true, srcs: [ "ndk_vendor/impl/ACameraDevice.cpp", "ndk_vendor/impl/ACameraManager.cpp", -- GitLab From 97bab91e628cb0b8029a422939e2ceef83bf6a7b Mon Sep 17 00:00:00 2001 From: Oliver Nguyen Date: Tue, 7 May 2019 13:12:20 -0700 Subject: [PATCH 1426/1530] Mark setAppState as pure virtual. Fixes build issues with -O0, due to missing vtable entries. Bug: 116873221 Test: Build with -O0 Test: make NATIVE_COVERAGE=true COVERAGE_PATHS=frameworks/av Change-Id: Ice06940f4ccc55609abf0d820f9b3c6d4e43d2c6 (cherry picked from commit 24fe48807f4f2d86677459bb5b1a9ca2cac757ff) --- services/audiopolicy/AudioPolicyInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 49937f0afa..30f29d6f2f 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -258,7 +258,7 @@ public: virtual status_t getHwOffloadEncodingFormatsSupportedForA2DP( std::vector *formats) = 0; - virtual void setAppState(uid_t uid, app_state_t state); + virtual void setAppState(uid_t uid, app_state_t state) = 0; virtual status_t listAudioProductStrategies(AudioProductStrategyVector &strategies) = 0; -- GitLab From 3c4b455145752aa57ee4777ac94e8b4c32fd59f7 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 10 May 2019 22:06:40 -0700 Subject: [PATCH 1427/1530] media: enable Codec 2.0 by default Bug: 129710438 Change-Id: Idb114c7a95f9e751788f36850f0e8299eb805c9d --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 2 +- media/libstagefright/StagefrightPluginLoader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 1919597a07..c54c6019c7 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -313,7 +313,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // // Note: Currently, OMX components have default rank 0x100, while all // Codec2.0 software components have default rank 0x200. - int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 1); + int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 4); // Obtain Codec2Client std::vector traits = Codec2Client::ListComponents(); diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index b90649cc7b..ee289660e4 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -35,7 +35,7 @@ constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so"; } // unnamed namespace StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) { - if (android::base::GetIntProperty("debug.media.codec2", 0) == 0) { + if (android::base::GetIntProperty("ro.media.codec2.enable", 1) == 0) { ALOGD("CCodec is disabled."); return; } -- GitLab From 8e2cfb54916d235185ab333d3608c3eb0aaae54c Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 15 May 2019 05:20:52 -0700 Subject: [PATCH 1428/1530] Ignore channel's onInputBufferDone while using input surface Test: Record a video while running logcat Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Bug: 132461433 Change-Id: I996fa9d469bc395afb2589c6c0d9e43c8f4b0d69 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 715e78b63e..7669421ec4 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1203,6 +1203,9 @@ void CCodecBufferChannel::onWorkDone( void CCodecBufferChannel::onInputBufferDone( uint64_t frameIndex, size_t arrayIndex) { + if (mInputSurface) { + return; + } std::shared_ptr buffer = mPipelineWatcher.lock()->onInputBufferReleased(frameIndex, arrayIndex); bool newInputSlotAvailable; -- GitLab From a2f5a657fd5371bb1024aad300427ce5d0ea3d96 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 10 May 2019 19:33:03 -0700 Subject: [PATCH 1429/1530] Change libvpx to shared library Bug: 132466615 Test: Build and flash Change-Id: I6cb494ef5422e901236921fb3ff376d758c40007 --- media/codec2/components/vpx/Android.bp | 8 ++++---- media/libstagefright/Android.bp | 1 - media/libstagefright/codecs/on2/dec/Android.bp | 2 +- media/libstagefright/codecs/on2/enc/Android.bp | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/media/codec2/components/vpx/Android.bp b/media/codec2/components/vpx/Android.bp index abfd3797d1..34f5753ac3 100644 --- a/media/codec2/components/vpx/Android.bp +++ b/media/codec2/components/vpx/Android.bp @@ -7,7 +7,7 @@ cc_library_shared { srcs: ["C2SoftVpxDec.cpp"], - static_libs: ["libvpx"], + shared_libs: ["libvpx"], cflags: [ "-DVP9", @@ -23,7 +23,7 @@ cc_library_shared { srcs: ["C2SoftVpxDec.cpp"], - static_libs: ["libvpx"], + shared_libs: ["libvpx"], } cc_library_shared { @@ -38,7 +38,7 @@ cc_library_shared { "C2SoftVpxEnc.cpp", ], - static_libs: ["libvpx"], + shared_libs: ["libvpx"], cflags: ["-DVP9"], } @@ -55,6 +55,6 @@ cc_library_shared { "C2SoftVpxEnc.cpp", ], - static_libs: ["libvpx"], + shared_libs: ["libvpx"], } diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp index 5932518211..917080511f 100644 --- a/media/libstagefright/Android.bp +++ b/media/libstagefright/Android.bp @@ -212,7 +212,6 @@ cc_library { "libstagefright_mediafilter", "libstagefright_webm", "libstagefright_timedtext", - "libvpx", "libogg", "libwebm", "libstagefright_esds", diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp index 577231cc11..82bb8d1da4 100644 --- a/media/libstagefright/codecs/on2/dec/Android.bp +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -4,7 +4,7 @@ cc_library_shared { srcs: ["SoftVPX.cpp"], - static_libs: ["libvpx"], + shared_libs: ["libvpx"], version_script: "exports.lds", diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp index 82c215e042..cd69e0dbc8 100644 --- a/media/libstagefright/codecs/on2/enc/Android.bp +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -20,5 +20,5 @@ cc_library_shared { cfi: true, }, - static_libs: ["libvpx"], + shared_libs: ["libvpx"], } -- GitLab From 7ec47840797570b34042ba593dc02a39263a978f Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 14 May 2019 16:54:54 -0700 Subject: [PATCH 1430/1530] OpusHeader: Fix OOB write in WriteOpusHeaders() writeOpusHeader() was called with a wrong output size which was leading to a OOB write in memset Test: Manually tested with AMediaCodec based application for opus encode Bug: 132794989 Change-Id: I7e1daf935f94d80dc2655ce732fda8cde104947c --- media/libstagefright/foundation/OpusHeader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp index acb9ccf6e9..513e41f4a9 100644 --- a/media/libstagefright/foundation/OpusHeader.cpp +++ b/media/libstagefright/foundation/OpusHeader.cpp @@ -208,7 +208,7 @@ int WriteOpusHeaders(const OpusHeader &header, int inputSampleRate, headerLen += AOPUS_LENGTH_SIZE; int headerSize = WriteOpusHeader(header, inputSampleRate, output + headerLen, - outputSize); + outputSize - headerLen); if (headerSize < 0) { ALOGD("%s: WriteOpusHeader failed", __func__); return -1; -- GitLab From ca67e38e7029ed2f6f277fca23210094dcfa3c59 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 14 May 2019 09:48:28 -0700 Subject: [PATCH 1431/1530] MPEG4Extractor: Fix opus CSD size MPEG4Extractor was signally wrong CSD size for csd-0 in case of opus Instead of signally actual CSD size, size of the array used to hold the header was being sent. Bug: 132692852 Test: Manually verified using AMediaExtractor based test application Change-Id: I88f247a24ab09d0a39b32934659705d7bc9d3f7d --- media/extractors/mp4/MPEG4Extractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index b4fd8112f9..e021d17179 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1772,7 +1772,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { int64_t codecDelay = pre_skip * 1000000000ll / kOpusSampleRate; AMediaFormat_setBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_CSD_0, opusInfo, sizeof(opusInfo)); + AMEDIAFORMAT_KEY_CSD_0, opusInfo, opusInfoSize); AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_1, &codecDelay, sizeof(codecDelay)); AMediaFormat_setBuffer(mLastTrack->meta, -- GitLab From 9924d5ad544cabbc1de06ab6871aaa696f90b2a0 Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Wed, 15 May 2019 16:00:14 -0700 Subject: [PATCH 1432/1530] mediacodec: Add a couple 32-bit permissions We add permissions for 'readlink' and 'open' for 32-bit targets. These are used when grabbing bug reports, at least on some 32-bit devices. Test: Tried this change on a 32-bit device that was getting fatal aborts when taking bugreports, and confirm we no longer get fatal aborts. Bug: 132807384 Change-Id: I526217e9201d2a709baa2a6d9f078244578d678d --- services/mediacodec/seccomp_policy/mediaswcodec-arm.policy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy index f75515abfc..9042cd77b2 100644 --- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy +++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy @@ -42,6 +42,7 @@ exit: 1 lseek: 1 rt_sigprocmask: 1 openat: 1 +open: 1 fstat64: 1 write: 1 nanosleep: 1 @@ -49,6 +50,7 @@ setpriority: 1 set_tid_address: 1 getdents64: 1 readlinkat: 1 +readlink: 1 read: 1 pread64: 1 fstatfs64: 1 -- GitLab From c0433422f74249f7ffc6b629e234f9a931f2e4e0 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 15 May 2019 17:00:01 -0700 Subject: [PATCH 1433/1530] Remove a few libs used by soft omx from vndk Soft omx plugins are already removed from vndk, these should have been removed together. Keeping them in vndk with have both 32 and 64 bit on system, but soft omx only uses one set. bug: 132466615 test: build and check these libs are now on vendor/lib. Change-Id: I09ce20820ca96d71ddd02a45b68147c8388c841d --- media/libstagefright/codecs/amrnb/common/Android.bp | 3 --- media/libstagefright/codecs/common/Android.bp | 3 --- media/libstagefright/flac/dec/Android.bp | 3 --- 3 files changed, 9 deletions(-) diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp index 5177593646..772ebf9754 100644 --- a/media/libstagefright/codecs/amrnb/common/Android.bp +++ b/media/libstagefright/codecs/amrnb/common/Android.bp @@ -1,9 +1,6 @@ cc_library_shared { name: "libstagefright_amrnb_common", vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "src/add.cpp", diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp index 3726922c46..c5a076aee2 100644 --- a/media/libstagefright/codecs/common/Android.bp +++ b/media/libstagefright/codecs/common/Android.bp @@ -1,9 +1,6 @@ cc_library { name: "libstagefright_enc_common", vendor_available: true, - vndk: { - enabled: true, - }, srcs: ["cmnMemory.c"], diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp index b270808efc..b494e16a99 100644 --- a/media/libstagefright/flac/dec/Android.bp +++ b/media/libstagefright/flac/dec/Android.bp @@ -1,9 +1,6 @@ cc_library { name: "libstagefright_flacdec", vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "FLACDecoder.cpp", -- GitLab From 9a645a9355f697182bd11ba7fbdddae7e0b13708 Mon Sep 17 00:00:00 2001 From: Carter Hsu Date: Wed, 15 May 2019 12:15:32 +0800 Subject: [PATCH 1434/1530] audio: fix index of input vector Bug: 131666114 Test: manual Change-Id: I7de750aa2f212e6b7e6296ce2caf544a0188902b Signed-off-by: Carter Hsu --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 2dc7cad495..3c98e3817c 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2127,6 +2127,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp desc = mInputs.valueAt(i); if (desc->mProfile != profile) { + i++; continue; } // if sound trigger, reuse input if used by other sound trigger on same session -- GitLab From 90f411141ba768400bbde7893e68eb2194a5aea5 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Thu, 16 May 2019 13:01:29 +0900 Subject: [PATCH 1435/1530] Remove min sdk version and set target/max sdk version to 29 Min sdk version has been force set to 28 (P) to support beta devices. Since we now don't need to support them, we delete it. It is set to 29 by the build system. Target and max sdk versions are bumped to 29. Bug: 131128233 Test: m and check the sdk version numbers Change-Id: I9ecf2b7542bde239955daf91c88b8e612d7d7069 --- apex/AndroidManifest-media.xml | 5 ++--- apex/AndroidManifest-swcodec.xml | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml index 78ed0edcdf..1af4586177 100644 --- a/apex/AndroidManifest-media.xml +++ b/apex/AndroidManifest-media.xml @@ -18,10 +18,9 @@ package="com.android.media"> - + diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml index 9558644bb3..de50864aaa 100644 --- a/apex/AndroidManifest-swcodec.xml +++ b/apex/AndroidManifest-swcodec.xml @@ -18,10 +18,9 @@ package="com.android.media.swcodec"> - + -- GitLab From 93cf3dfbf8f3b96dedc4a999ae81d7b7d208d62c Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 6 May 2019 13:53:43 +0800 Subject: [PATCH 1436/1530] Handle mp4 file with abnormal pcm audio playback stuck When we play the mp4 file whose pcm audio's sample size of stsz box is not correct, it will stuck. The root cause is it may take wrong buffer to pcm decoder, and AudioTrack might not be able to play the decoded pcm data and cannot get right timestamp, so video will stuck by audio. Bug: 132042434 Test: test with the mp4 file whose pcm audio sampleSize is abnormal and check if the video can be played normally. Change-Id: I06c69e43922512f43d996fafcbe8b9c23ce3e7dd --- media/extractors/mp4/MPEG4Extractor.cpp | 16 ++++++++++++++++ media/extractors/mp4/MPEG4Extractor.h | 1 + media/extractors/mp4/SampleTable.h | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index b4fd8112f9..e01fc90fae 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1972,6 +1972,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return err; } + adjustRawDefaultFrameSize(); + size_t max_size; err = mLastTrack->sampleTable->getMaxSampleSize(&max_size); @@ -4606,6 +4608,20 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return OK; } +void MPEG4Extractor::adjustRawDefaultFrameSize() { + int32_t chanCount = 0; + int32_t bitWidth = 0; + const char *mimeStr = NULL; + + if(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mimeStr) && + !strcasecmp(mimeStr, MEDIA_MIMETYPE_AUDIO_RAW) && + AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &chanCount) && + AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &bitWidth)) { + // samplesize in stsz may not right , so updade default samplesize + mLastTrack->sampleTable->setPredictSampleSize(chanCount * bitWidth / 8); + } +} + //////////////////////////////////////////////////////////////////////////////// MPEG4Source::MPEG4Source( diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index 031e793dd5..e10bf8a055 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -179,6 +179,7 @@ private: status_t parseAC3SpecificBox(off64_t offset); status_t parseEAC3SpecificBox(off64_t offset); status_t parseAC4SpecificBox(off64_t offset); + void adjustRawDefaultFrameSize(); MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/SampleTable.h index 57f6e624e7..076f4c30e2 100644 --- a/media/extractors/mp4/SampleTable.h +++ b/media/extractors/mp4/SampleTable.h @@ -89,6 +89,10 @@ public: status_t findThumbnailSample(uint32_t *sample_index); + void setPredictSampleSize(uint32_t sampleSize) { + mDefaultSampleSize = sampleSize; + } + protected: ~SampleTable(); -- GitLab From dfa3f5ac632dc376194c456f57357ddc059716a3 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 2 May 2019 12:10:24 -0700 Subject: [PATCH 1437/1530] codec2: Update stride calculations in mpeg4, vpx and aom decoder plugins Bug: 131844219 Test: atest android.media.cts.DecoderTest Test: Play HDR videos on YouTube Change-Id: Iec4823cf6e061b10a9fd8d7d4429fd0b6d6f6bf6 --- media/codec2/components/aom/C2SoftAomDec.cpp | 29 ++++++++++--------- .../components/mpeg4_h263/C2SoftMpeg4Dec.cpp | 18 ++++++++---- media/codec2/components/vpx/C2SoftVpxDec.cpp | 28 ++++++++++-------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp index df4dadbba0..769895cec9 100644 --- a/media/codec2/components/aom/C2SoftAomDec.cpp +++ b/media/codec2/components/aom/C2SoftAomDec.cpp @@ -501,15 +501,13 @@ void C2SoftAomDec::process(const std::unique_ptr& work, } } -static void copyOutputBufferToYV12Frame(uint8_t *dst, - const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, +static void copyOutputBufferToYuvPlanarFrame( + uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstYStride, size_t dstUVStride, uint32_t width, uint32_t height) { - size_t dstYStride = align(width, 16); - size_t dstUVStride = align(dstYStride / 2, 16); uint8_t* dstStart = dst; - for (size_t i = 0; i < height; ++i) { memcpy(dst, srcY, width); srcY += srcYStride; @@ -597,11 +595,10 @@ static void convertYUV420Planar16ToY410(uint32_t *dst, static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, size_t srcYStride, size_t srcUStride, size_t srcVStride, - size_t dstStride, size_t width, size_t height) { + size_t dstYStride, size_t dstUVStride, size_t width, size_t height) { uint8_t *dstY = (uint8_t *)dst; - size_t dstYSize = dstStride * height; - size_t dstUVStride = align(dstStride / 2, 16); + size_t dstYSize = dstYStride * height; size_t dstUVSize = dstUVStride * height / 2; uint8_t *dstV = dstY + dstYSize; uint8_t *dstU = dstV + dstUVSize; @@ -612,7 +609,7 @@ static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, } srcY += srcYStride; - dstY += dstStride; + dstY += dstYStride; } for (size_t y = 0; y < (height + 1) / 2; ++y) { @@ -699,6 +696,9 @@ bool C2SoftAomDec::outputBuffer( size_t srcYStride = img->stride[AOM_PLANE_Y]; size_t srcUStride = img->stride[AOM_PLANE_U]; size_t srcVStride = img->stride[AOM_PLANE_V]; + C2PlanarLayout layout = wView.layout(); + size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc; + size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc; if (img->fmt == AOM_IMG_FMT_I42016) { const uint16_t *srcY = (const uint16_t *)img->planes[AOM_PLANE_Y]; @@ -708,20 +708,23 @@ bool C2SoftAomDec::outputBuffer( if (format == HAL_PIXEL_FORMAT_RGBA_1010102) { convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2, srcVStride / 2, - align(mWidth, 16), + dstYStride / sizeof(uint32_t), mWidth, mHeight); } else { convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2, srcVStride / 2, - align(mWidth, 16), + dstYStride, dstUVStride, mWidth, mHeight); } } else { const uint8_t *srcY = (const uint8_t *)img->planes[AOM_PLANE_Y]; const uint8_t *srcU = (const uint8_t *)img->planes[AOM_PLANE_U]; const uint8_t *srcV = (const uint8_t *)img->planes[AOM_PLANE_V]; - copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, - srcYStride, srcUStride, srcVStride, mWidth, mHeight); + copyOutputBufferToYuvPlanarFrame( + dst, srcY, srcU, srcV, + srcYStride, srcUStride, srcVStride, + dstYStride, dstUVStride, + mWidth, mHeight); } finishWork(*(int64_t*)img->user_priv, work, std::move(block)); block = nullptr; diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp index 3d4a733714..7e6685e22f 100644 --- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp +++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp @@ -466,9 +466,11 @@ bool C2SoftMpeg4Dec::handleResChange(const std::unique_ptr &work) { /* TODO: can remove temporary copy after library supports writing to display * buffer Y, U and V plane pointers using stride info. */ -static void copyOutputBufferToYV12Frame(uint8_t *dst, uint8_t *src, size_t dstYStride, - size_t srcYStride, uint32_t width, uint32_t height) { - size_t dstUVStride = align(dstYStride / 2, 16); +static void copyOutputBufferToYuvPlanarFrame( + uint8_t *dst, uint8_t *src, + size_t dstYStride, size_t dstUVStride, + size_t srcYStride, uint32_t width, + uint32_t height) { size_t srcUVStride = srcYStride / 2; uint8_t *srcStart = src; uint8_t *dstStart = dst; @@ -673,8 +675,14 @@ void C2SoftMpeg4Dec::process( } uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y]; - (void)copyOutputBufferToYV12Frame(outputBufferY, mOutputBuffer[mNumSamplesOutput & 1], - wView.width(), align(mWidth, 16), mWidth, mHeight); + C2PlanarLayout layout = wView.layout(); + size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc; + size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc; + (void)copyOutputBufferToYuvPlanarFrame( + outputBufferY, + mOutputBuffer[mNumSamplesOutput & 1], + dstYStride, dstUVStride, + align(mWidth, 16), mWidth, mHeight); inPos += inSize - (size_t)tmpInSize; finishWork(workIndex, work); diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp index 3120f7af54..42f507f317 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.cpp +++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp @@ -559,12 +559,11 @@ void C2SoftVpxDec::process( } } -static void copyOutputBufferToYV12Frame(uint8_t *dst, - const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, +static void copyOutputBufferToYuvPlanarFrame( + uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride, size_t srcUStride, size_t srcVStride, + size_t dstYStride, size_t dstUVStride, uint32_t width, uint32_t height) { - size_t dstYStride = align(width, 16); - size_t dstUVStride = align(dstYStride / 2, 16); uint8_t *dstStart = dst; for (size_t i = 0; i < height; ++i) { @@ -654,11 +653,10 @@ static void convertYUV420Planar16ToY410(uint32_t *dst, static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV, size_t srcYStride, size_t srcUStride, size_t srcVStride, - size_t dstStride, size_t width, size_t height) { + size_t dstYStride, size_t dstUVStride, size_t width, size_t height) { uint8_t *dstY = (uint8_t *)dst; - size_t dstYSize = dstStride * height; - size_t dstUVStride = align(dstStride / 2, 16); + size_t dstYSize = dstYStride * height; size_t dstUVSize = dstUVStride * height / 2; uint8_t *dstV = dstY + dstYSize; uint8_t *dstU = dstV + dstUVSize; @@ -669,7 +667,7 @@ static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst, } srcY += srcYStride; - dstY += dstStride; + dstY += dstYStride; } for (size_t y = 0; y < (height + 1) / 2; ++y) { @@ -751,6 +749,9 @@ bool C2SoftVpxDec::outputBuffer( size_t srcYStride = img->stride[VPX_PLANE_Y]; size_t srcUStride = img->stride[VPX_PLANE_U]; size_t srcVStride = img->stride[VPX_PLANE_V]; + C2PlanarLayout layout = wView.layout(); + size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc; + size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc; if (img->fmt == VPX_IMG_FMT_I42016) { const uint16_t *srcY = (const uint16_t *)img->planes[VPX_PLANE_Y]; @@ -760,20 +761,23 @@ bool C2SoftVpxDec::outputBuffer( if (format == HAL_PIXEL_FORMAT_RGBA_1010102) { convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2, srcVStride / 2, - align(mWidth, 16), + dstYStride / sizeof(uint32_t), mWidth, mHeight); } else { convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2, srcVStride / 2, - align(mWidth, 16), + dstYStride, dstUVStride, mWidth, mHeight); } } else { const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y]; const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U]; const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V]; - copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, - srcYStride, srcUStride, srcVStride, mWidth, mHeight); + copyOutputBufferToYuvPlanarFrame( + dst, srcY, srcU, srcV, + srcYStride, srcUStride, srcVStride, + dstYStride, dstUVStride, + mWidth, mHeight); } finishWork(*(int64_t *)img->user_priv, work, std::move(block)); return true; -- GitLab From 675253c49cc6ad7ab4a1620793a6586aa653cd3f Mon Sep 17 00:00:00 2001 From: Gopalakrishnan Nallasamy Date: Wed, 15 May 2019 18:21:22 -0700 Subject: [PATCH 1438/1530] WebmWriter:read kKeyBitsPerSample from input kKeyBitsPerSample information from input was not passed to audioTrackEntry while remuxing. Hence, the metadata of input file and muxed file didn't match. Now kKeyBitsPerSample is passed as it is in the input to muxed file. Bug: 131842669 Test: cts-tradefed run cts-dev -m CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice Change-Id: I04ab296c39596d02c3f2cd9e219d8d80fe2f9185 --- media/libstagefright/webm/WebmWriter.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp index 26e08844b3..5eaadbd05e 100644 --- a/media/libstagefright/webm/WebmWriter.cpp +++ b/media/libstagefright/webm/WebmWriter.cpp @@ -124,6 +124,11 @@ sp WebmWriter::audioTrack(const sp& md) { return NULL; } + int32_t bitsPerSample = 0; + if (!md->findInt32(kKeyBitsPerSample, &bitsPerSample)) { + ALOGV("kKeyBitsPerSample not available"); + } + if (!strncasecmp(mimeType, MEDIA_MIMETYPE_AUDIO_OPUS, strlen(MEDIA_MIMETYPE_AUDIO_OPUS))) { // Opus in WebM is a well-known, yet under-documented, format. The codec private data // of the track is an Opus Ogg header (https://tools.ietf.org/html/rfc7845#section-5.1) @@ -164,8 +169,8 @@ sp WebmWriter::audioTrack(const sp& md) { uint8_t* codecPrivateData = codecPrivateBuf->data(); memcpy(codecPrivateData + off, (uint8_t*)header_data, headerSize); - sp entry = - WebmElement::AudioTrackEntry("A_OPUS", nChannels, samplerate, codecPrivateBuf); + sp entry = WebmElement::AudioTrackEntry("A_OPUS", nChannels, samplerate, + codecPrivateBuf, bitsPerSample); return entry; } else if (!strncasecmp(mimeType, MEDIA_MIMETYPE_AUDIO_VORBIS, @@ -203,8 +208,8 @@ sp WebmWriter::audioTrack(const sp& md) { off += headerSize2; memcpy(codecPrivateData + off, headerData3, headerSize3); - sp entry = - WebmElement::AudioTrackEntry("A_VORBIS", nChannels, samplerate, codecPrivateBuf); + sp entry = WebmElement::AudioTrackEntry("A_VORBIS", nChannels, samplerate, + codecPrivateBuf, bitsPerSample); return entry; } else { ALOGE("Track (%s) is not a supported audio format", mimeType); -- GitLab From 11648852d7e9b92d36879bb2ed6d9953751121c5 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 16 May 2019 10:42:54 -0700 Subject: [PATCH 1439/1530] Camera: cleanup some dead code We no longer have code path running non-HIDL interface. This cleanup also simplifies some logic needed for fixing b/132594861 Bug: 132594861 Test: Camera CTS Change-Id: If15ea359a1a59c5a8e7a59818ce4db8120000bc4 --- .../device3/Camera3Device.cpp | 62 +------------------ .../libcameraservice/device3/Camera3Device.h | 12 ++-- 2 files changed, 7 insertions(+), 67 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 7573089b52..e18415bba8 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4032,10 +4032,6 @@ void Camera3Device::HalInterface::clear() { mHidlSession.clear(); } -bool Camera3Device::HalInterface::supportBatchRequest() { - return mHidlSession != nullptr; -} - status_t Camera3Device::HalInterface::constructDefaultRequestSettings( camera3_request_template_t templateId, /*out*/ camera_metadata_t **requestTemplate) { @@ -4608,20 +4604,6 @@ status_t Camera3Device::HalInterface::processBatchCaptureRequests( return CameraProviderManager::mapToStatusT(status); } -status_t Camera3Device::HalInterface::processCaptureRequest( - camera3_capture_request_t *request) { - ATRACE_NAME("CameraHal::processCaptureRequest"); - if (!valid()) return INVALID_OPERATION; - status_t res = OK; - - uint32_t numRequestProcessed = 0; - std::vector requests(1); - requests[0] = request; - res = processBatchCaptureRequests(requests, &numRequestProcessed); - - return res; -} - status_t Camera3Device::HalInterface::flush() { ATRACE_NAME("CameraHal::flush"); if (!valid()) return INVALID_OPERATION; @@ -5172,43 +5154,6 @@ bool Camera3Device::RequestThread::sendRequestsBatch() { return true; } -bool Camera3Device::RequestThread::sendRequestsOneByOne() { - status_t res; - - for (auto& nextRequest : mNextRequests) { - // Submit request and block until ready for next one - ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number); - res = mInterface->processCaptureRequest(&nextRequest.halRequest); - - if (res != OK) { - // Should only get a failure here for malformed requests or device-level - // errors, so consider all errors fatal. Bad metadata failures should - // come through notify. - SET_ERR("RequestThread: Unable to submit capture request %d to HAL" - " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res), - res); - cleanUpFailedRequests(/*sendRequestError*/ false); - return false; - } - - // Mark that the request has be submitted successfully. - nextRequest.submitted = true; - - updateNextRequest(nextRequest); - - // Remove any previously queued triggers (after unlock) - res = removeTriggers(mPrevRequest); - if (res != OK) { - SET_ERR("RequestThread: Unable to remove triggers " - "(capture request %d, HAL device: %s (%d)", - nextRequest.halRequest.frame_number, strerror(-res), res); - cleanUpFailedRequests(/*sendRequestError*/ false); - return false; - } - } - return true; -} - nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_metadata_t *request) { nsecs_t maxExpectedDuration = kDefaultExpectedDuration; camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t(); @@ -5462,11 +5407,8 @@ bool Camera3Device::RequestThread::threadLoop() { bool submitRequestSuccess = false; nsecs_t tRequestStart = systemTime(SYSTEM_TIME_MONOTONIC); - if (mInterface->supportBatchRequest()) { - submitRequestSuccess = sendRequestsBatch(); - } else { - submitRequestSuccess = sendRequestsOneByOne(); - } + submitRequestSuccess = sendRequestsBatch(); + nsecs_t tRequestEnd = systemTime(SYSTEM_TIME_MONOTONIC); mRequestLatency.add(tRequestStart, tRequestEnd); diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 8f74611849..fe94f8e86d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -289,9 +289,6 @@ class Camera3Device : // Reset this HalInterface object (does not call close()) void clear(); - // Check if HalInterface support sending requests in batch - bool supportBatchRequest(); - // Calls into the HAL interface // Caller takes ownership of requestTemplate @@ -300,7 +297,11 @@ class Camera3Device : status_t configureStreams(const camera_metadata_t *sessionParams, /*inout*/ camera3_stream_configuration *config, const std::vector& bufferSizes); - status_t processCaptureRequest(camera3_capture_request_t *request); + + // When the call succeeds, the ownership of acquire fences in requests is transferred to + // HalInterface. More specifically, the current implementation will send the fence to + // HAL process and close the FD in cameraserver process. When the call fails, the ownership + // of the acquire fence still belongs to the caller. status_t processBatchCaptureRequests( std::vector& requests, /*out*/uint32_t* numRequestProcessed); @@ -895,9 +896,6 @@ class Camera3Device : // Clear repeating requests. Must be called with mRequestLock held. status_t clearRepeatingRequestsLocked(/*out*/ int64_t *lastFrameNumber = NULL); - // send request in mNextRequests to HAL one by one. Return true = sucssess - bool sendRequestsOneByOne(); - // send request in mNextRequests to HAL in a batch. Return true = sucssess bool sendRequestsBatch(); -- GitLab From 0ae7c72cd1e29d3282c92f9d492cee7fdd46e430 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 15 May 2019 19:20:54 -0700 Subject: [PATCH 1440/1530] Set dequeue timeout of surface on ACodec configuration Set dequeue timeout of surface to -1 during ACodec configuration. Test: atest CtsMediaTestCases:VideoEncoderTest#testGoogH264SurfQCIF Bug: 132699263 Change-Id: Id28a16d72f38be89edb0bbebebf91dfe915d36e0 --- media/libstagefright/ACodec.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 317b5ec51d..3d67c911a2 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1138,6 +1138,8 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( return err; } + static_cast(mNativeWindow.get())->setDequeueTimeout(-1); + // Exits here for tunneled video playback codecs -- i.e. skips native window // buffer allocation step as this is managed by the tunneled OMX omponent // itself and explicitly sets def.nBufferCountActual to 0. -- GitLab From 596250cb844a93296a8ae298d1a91c18de7246e3 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Fri, 17 May 2019 02:24:01 +0000 Subject: [PATCH 1441/1530] Revert "media: enable Codec 2.0 by default" This reverts commit 3c4b455145752aa57ee4777ac94e8b4c32fd59f7. Reason for revert: Possibly causing b/132887737 and blocking presubmit Change-Id: Ib6da7c3e1506965a5d2c202e8bd6cf0e4a81dc3c --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 2 +- media/libstagefright/StagefrightPluginLoader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index c54c6019c7..1919597a07 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -313,7 +313,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // // Note: Currently, OMX components have default rank 0x100, while all // Codec2.0 software components have default rank 0x200. - int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 4); + int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 1); // Obtain Codec2Client std::vector traits = Codec2Client::ListComponents(); diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index ee289660e4..b90649cc7b 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -35,7 +35,7 @@ constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so"; } // unnamed namespace StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) { - if (android::base::GetIntProperty("ro.media.codec2.enable", 1) == 0) { + if (android::base::GetIntProperty("debug.media.codec2", 0) == 0) { ALOGD("CCodec is disabled."); return; } -- GitLab From 097090588fa2d4beebb7defb9481f6e990b55c98 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Fri, 17 May 2019 11:04:03 -0700 Subject: [PATCH 1442/1530] Pull xaac codec from build remove references that pull xaac codec into the build; the codec is still considered experimental and not to be included in product. Bug: 131342273 Test: compilation Change-Id: Ic7ff3871c88555bd006c0c79beebada3732a8b8e --- media/codec2/vndk/C2Store.cpp | 1 - services/mediacodec/registrant/Android.bp | 1 - 2 files changed, 2 deletions(-) diff --git a/media/codec2/vndk/C2Store.cpp b/media/codec2/vndk/C2Store.cpp index 10c4dcc574..f8afa7c539 100644 --- a/media/codec2/vndk/C2Store.cpp +++ b/media/codec2/vndk/C2Store.cpp @@ -872,7 +872,6 @@ C2PlatformComponentStore::C2PlatformComponentStore() emplace("libcodec2_soft_vp8enc.so"); emplace("libcodec2_soft_vp9dec.so"); emplace("libcodec2_soft_vp9enc.so"); - emplace("libcodec2_soft_xaacdec.so"); } c2_status_t C2PlatformComponentStore::copyBuffer( diff --git a/services/mediacodec/registrant/Android.bp b/services/mediacodec/registrant/Android.bp index 1470de29f4..17c2e0261b 100644 --- a/services/mediacodec/registrant/Android.bp +++ b/services/mediacodec/registrant/Android.bp @@ -49,7 +49,6 @@ cc_library_shared { "libcodec2_soft_flacdec", "libcodec2_soft_flacenc", "libcodec2_soft_gsmdec", - "libcodec2_soft_xaacdec", ], } -- GitLab From 8635fc832085b0548b40232b13646bbca15842ff Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 17 May 2019 17:35:10 +0000 Subject: [PATCH 1443/1530] media: enable Codec 2.0 by default again This reverts commit 596250cb844a93296a8ae298d1a91c18de7246e3. Reason for revert: we can disable on cuttlefish specifically Bug: 129710438 Change-Id: I65bc168661f46314a0263e6aab77c7f6f1b5953f --- media/codec2/sfplugin/Codec2InfoBuilder.cpp | 2 +- media/libstagefright/StagefrightPluginLoader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 1919597a07..c54c6019c7 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -313,7 +313,7 @@ status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // // Note: Currently, OMX components have default rank 0x100, while all // Codec2.0 software components have default rank 0x200. - int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 1); + int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 4); // Obtain Codec2Client std::vector traits = Codec2Client::ListComponents(); diff --git a/media/libstagefright/StagefrightPluginLoader.cpp b/media/libstagefright/StagefrightPluginLoader.cpp index b90649cc7b..fb03c5ebcd 100644 --- a/media/libstagefright/StagefrightPluginLoader.cpp +++ b/media/libstagefright/StagefrightPluginLoader.cpp @@ -35,7 +35,7 @@ constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so"; } // unnamed namespace StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) { - if (android::base::GetIntProperty("debug.media.codec2", 0) == 0) { + if (android::base::GetIntProperty("debug.stagefright.ccodec", 1) == 0) { ALOGD("CCodec is disabled."); return; } -- GitLab From f8e28fb940d725afb26a37c1964fbd1fcdb50fe8 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Thu, 16 May 2019 11:46:54 -0700 Subject: [PATCH 1444/1530] Camera: fix acquire fence FD double close This patch fixes several issues: * Change the timing the acquire fence FD is closed: we used to close the FD when the buffer is returned from HAL. This patch changes that to after the request is sent to HAL (or failed to send to HAL) * Cleanup inflight buffer map if the request fails to be sent to HAL * With the CL, the acquire fence FDs are now closed by - HalInterface::processBatchCaptureRequests if the HIDL processCaptureRequests call succeeds and HAL is running in binderized mode (for passthrough mode the FD is owned by HAL if the HIDL call succeeds) - Camera3Device::cleanupFailedRequests otherwise Test: Camera CTS tests Bug: 132594861 Change-Id: I5f67ae9e7b8008738bd9a24246d754a6a3669b0c --- .../device3/Camera3Device.cpp | 84 ++++++++++++++----- .../libcameraservice/device3/Camera3Device.h | 16 +++- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index e18415bba8..306f9b65b7 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -4381,11 +4381,12 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request, /*out*/device::V3_2::CaptureRequest* captureRequest, - /*out*/std::vector* handlesCreated) { + /*out*/std::vector* handlesCreated, + /*out*/std::vector>* inflightBuffers) { ATRACE_CALL(); - if (captureRequest == nullptr || handlesCreated == nullptr) { - ALOGE("%s: captureRequest (%p) and handlesCreated (%p) must not be null", - __FUNCTION__, captureRequest, handlesCreated); + if (captureRequest == nullptr || handlesCreated == nullptr || inflightBuffers == nullptr) { + ALOGE("%s: captureRequest (%p), handlesCreated (%p), and inflightBuffers(%p) " + "must not be null", __FUNCTION__, captureRequest, handlesCreated, inflightBuffers); return BAD_VALUE; } @@ -4415,8 +4416,8 @@ status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_ captureRequest->inputBuffer.releaseFence = nullptr; pushInflightBufferLocked(captureRequest->frameNumber, streamId, - request->input_buffer->buffer, - request->input_buffer->acquire_fence); + request->input_buffer->buffer); + inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId)); } else { captureRequest->inputBuffer.streamId = -1; captureRequest->inputBuffer.bufferId = BUFFER_ID_NO_BUFFER; @@ -4455,14 +4456,31 @@ status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_ // Output buffers are empty when using HAL buffer manager if (!mUseHalBufManager) { - pushInflightBufferLocked(captureRequest->frameNumber, streamId, - src->buffer, src->acquire_fence); + pushInflightBufferLocked(captureRequest->frameNumber, streamId, src->buffer); + inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId)); } } } return OK; } +void Camera3Device::HalInterface::cleanupNativeHandles( + std::vector *handles, bool closeFd) { + if (handles == nullptr) { + return; + } + if (closeFd) { + for (auto& handle : *handles) { + native_handle_close(handle); + } + } + for (auto& handle : *handles) { + native_handle_delete(handle); + } + handles->clear(); + return; +} + status_t Camera3Device::HalInterface::processBatchCaptureRequests( std::vector& requests,/*out*/uint32_t* numRequestProcessed) { ATRACE_NAME("CameraHal::processBatchCaptureRequests"); @@ -4483,17 +4501,20 @@ status_t Camera3Device::HalInterface::processBatchCaptureRequests( captureRequests.resize(batchSize); } std::vector handlesCreated; + std::vector> inflightBuffers; status_t res = OK; for (size_t i = 0; i < batchSize; i++) { if (hidlSession_3_4 != nullptr) { res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2, - /*out*/&handlesCreated); + /*out*/&handlesCreated, /*out*/&inflightBuffers); } else { - res = wrapAsHidlRequest(requests[i], - /*out*/&captureRequests[i], /*out*/&handlesCreated); + res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], + /*out*/&handlesCreated, /*out*/&inflightBuffers); } if (res != OK) { + popInflightBuffers(inflightBuffers); + cleanupNativeHandles(&handlesCreated); return res; } } @@ -4590,18 +4611,30 @@ status_t Camera3Device::HalInterface::processBatchCaptureRequests( } if (!err.isOk()) { ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str()); - return DEAD_OBJECT; + status = common::V1_0::Status::CAMERA_DISCONNECTED; } + if (status == common::V1_0::Status::OK && *numRequestProcessed != batchSize) { ALOGE("%s: processCaptureRequest returns OK but processed %d/%zu requests", __FUNCTION__, *numRequestProcessed, batchSize); status = common::V1_0::Status::INTERNAL_ERROR; } - for (auto& handle : handlesCreated) { - native_handle_delete(handle); + res = CameraProviderManager::mapToStatusT(status); + if (res == OK) { + if (mHidlSession->isRemote()) { + // Only close acquire fence FDs when the HIDL transaction succeeds (so the FDs have been + // sent to camera HAL processes) + cleanupNativeHandles(&handlesCreated, /*closeFd*/true); + } else { + // In passthrough mode the FDs are now owned by HAL + cleanupNativeHandles(&handlesCreated); + } + } else { + popInflightBuffers(inflightBuffers); + cleanupNativeHandles(&handlesCreated); } - return CameraProviderManager::mapToStatusT(status); + return res; } status_t Camera3Device::HalInterface::flush() { @@ -4683,10 +4716,9 @@ void Camera3Device::HalInterface::getInflightRequestBufferKeys( } status_t Camera3Device::HalInterface::pushInflightBufferLocked( - int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer, int acquireFence) { + int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer) { uint64_t key = static_cast(frameNumber) << 32 | static_cast(streamId); - auto pair = std::make_pair(buffer, acquireFence); - mInflightBufferMap[key] = pair; + mInflightBufferMap[key] = buffer; return OK; } @@ -4698,16 +4730,22 @@ status_t Camera3Device::HalInterface::popInflightBuffer( uint64_t key = static_cast(frameNumber) << 32 | static_cast(streamId); auto it = mInflightBufferMap.find(key); if (it == mInflightBufferMap.end()) return NAME_NOT_FOUND; - auto pair = it->second; - *buffer = pair.first; - int acquireFence = pair.second; - if (acquireFence > 0) { - ::close(acquireFence); + if (buffer != nullptr) { + *buffer = it->second; } mInflightBufferMap.erase(it); return OK; } +void Camera3Device::HalInterface::popInflightBuffers( + const std::vector>& buffers) { + for (const auto& pair : buffers) { + int32_t frameNumber = pair.first; + int32_t streamId = pair.second; + popInflightBuffer(frameNumber, streamId, nullptr); + } +} + status_t Camera3Device::HalInterface::pushInflightRequestBuffer( uint64_t bufferId, buffer_handle_t* buf, int32_t streamId) { std::lock_guard lock(mRequestedBuffersLock); diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index fe94f8e86d..1e0040c3e2 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -358,13 +358,21 @@ class Camera3Device : // Do not free input camera3_capture_request_t before output HIDL request status_t wrapAsHidlRequest(camera3_capture_request_t* in, /*out*/hardware::camera::device::V3_2::CaptureRequest* out, - /*out*/std::vector* handlesCreated); + /*out*/std::vector* handlesCreated, + /*out*/std::vector>* inflightBuffers); status_t pushInflightBufferLocked(int32_t frameNumber, int32_t streamId, - buffer_handle_t *buffer, int acquireFence); + buffer_handle_t *buffer); + + // Pop inflight buffers based on pairs of (frameNumber,streamId) + void popInflightBuffers(const std::vector>& buffers); + // Cache of buffer handles keyed off (frameNumber << 32 | streamId) - // value is a pair of (buffer_handle_t*, acquire_fence FD) - std::unordered_map> mInflightBufferMap; + std::unordered_map mInflightBufferMap; + + // Delete and optionally close native handles and clear the input vector afterward + static void cleanupNativeHandles( + std::vector *handles, bool closeFd = false); struct BufferHasher { size_t operator()(const buffer_handle_t& buf) const { -- GitLab From 82cb46d0d55a407f468023977204eb7133b7fd77 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 17 May 2019 07:29:07 -0700 Subject: [PATCH 1445/1530] AMR WB encoder: prevent OOB write in ACELP_4t64_fx In ACELP_4t64_fx, when iterating over ind array, check index against array size to prevent OOB write, log an error if such an access was about to happen. Bug: 132647222 Test: atest EncoderTest#testAMRWBEncoders Change-Id: I33f476d94baec2feffc7bcccd0ad0481b8452518 --- media/libstagefright/codecs/amrwbenc/Android.bp | 1 + .../codecs/amrwbenc/SampleCode/Android.bp | 1 + media/libstagefright/codecs/amrwbenc/src/c4t64fx.c | 13 +++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index 3beed66d20..084be0aaab 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -129,6 +129,7 @@ cc_library_static { shared_libs: [ "libstagefright_enc_common", + "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp index 95f9494816..9442fc4a1b 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp @@ -14,6 +14,7 @@ cc_test { shared_libs: [ "libdl", + "liblog", ], static_libs: [ diff --git a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c index 8cebb097bd..f2e28c42dd 100644 --- a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c +++ b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c @@ -47,6 +47,10 @@ #include "q_pulse.h" +#undef LOG_TAG +#define LOG_TAG "amrwbenc" +#include "log/log.h" + static Word16 tipos[36] = { 0, 1, 2, 3, /* starting point &ipos[0], 1st iter */ 1, 2, 3, 0, /* starting point &ipos[4], 2nd iter */ @@ -745,11 +749,16 @@ void ACELP_4t64_fx( i = (Word16)((vo_L_mult(track, NPMAXPT) >> 1)); - while (ind[i] >= 0) + while (i < NPMAXPT * NB_TRACK && ind[i] >= 0) { i += 1; } - ind[i] = index; + if (i < NPMAXPT * NB_TRACK) { + ind[i] = index; + } else { + ALOGE("b/132647222, OOB access in ind array track=%d i=%d", track, i); + android_errorWriteLog(0x534e4554, "132647222"); + } } k = 0; -- GitLab From d8e3af08e643a0923f96ceb3e8b64ce63148aab7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 17 May 2019 13:28:13 -0700 Subject: [PATCH 1446/1530] Replace unused dependency on libmemunreachable libmediautils was accidentally getting backtrace_string through libmemunreachable. Remove the dependency, which is causing problems for making libmemunreachable vendor_available, and use libc_malloc_debug_backtrace instead. Bug: 132302484 Test: m libmediautils Change-Id: Ide83ce43027f28d548a76c607a84bab6604213cf --- media/utils/Android.bp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/utils/Android.bp b/media/utils/Android.bp index 3adb40f5a3..d81cde8d62 100644 --- a/media/utils/Android.bp +++ b/media/utils/Android.bp @@ -25,11 +25,13 @@ cc_library { "ServiceUtilities.cpp", "TimeCheck.cpp", ], + static_libs: [ + "libc_malloc_debug_backtrace", + ], shared_libs: [ "libbinder", "liblog", "libutils", - "libmemunreachable", "libhidlbase", "android.hardware.graphics.bufferqueue@1.0", "android.hidl.token@1.0-utils", -- GitLab From 22fcda29e5982a052d2f6c6ce312d1f65edc7920 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 17 May 2019 16:28:47 -0700 Subject: [PATCH 1447/1530] audio policy: fix camera shutter sound Fix regression in camera shutter sound when system stream volume is 0. Bug: 132696227 Test: repro steps in bug. Test: regressions when camera sound is enforced Change-Id: I76b4a5ee041f97c5f771c003bf4f99327d797096 --- .../audiopolicy/engine/common/src/EngineBase.cpp | 15 +-------------- .../managerdefault/AudioPolicyManager.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp index 530a2e44ab..07a7e65124 100644 --- a/services/audiopolicy/engine/common/src/EngineBase.cpp +++ b/services/audiopolicy/engine/common/src/EngineBase.cpp @@ -70,20 +70,7 @@ product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attri audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const { - audio_stream_type_t engineStream = mProductStrategies.getStreamTypeForAttributes(attr); - // ensure the audibility flag for sonification is honored for stream types - // Note this is typically implemented in the product strategy configuration files, but is - // duplicated here for safety. - if (attr.usage == AUDIO_USAGE_ASSISTANCE_SONIFICATION - && ((attr.flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) != 0)) { - engineStream = AUDIO_STREAM_ENFORCED_AUDIBLE; - } - // ensure the ENFORCED_AUDIBLE stream type reflects the "force use" setting: - if ((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) - && (engineStream == AUDIO_STREAM_ENFORCED_AUDIBLE)) { - return AUDIO_STREAM_SYSTEM; - } - return engineStream; + return mProductStrategies.getStreamTypeForAttributes(attr); } audio_attributes_t EngineBase::getAttributesForStreamType(audio_stream_type_t stream) const diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index c430488722..575a6c20fd 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -777,6 +777,12 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, // check for device and output changes triggered by new force usage checkForDeviceAndOutputChanges(); + // force client reconnection to reevaluate flag AUDIO_FLAG_AUDIBILITY_ENFORCED + if (usage == AUDIO_POLICY_FORCE_FOR_SYSTEM) { + mpClientInterface->invalidateStream(AUDIO_STREAM_SYSTEM); + mpClientInterface->invalidateStream(AUDIO_STREAM_ENFORCED_AUDIBLE); + } + //FIXME: workaround for truncated touch sounds // to be removed when the problem is handled by system UI uint32_t delayMs = 0; @@ -910,6 +916,13 @@ status_t AudioPolicyManager::getAudioAttributes(audio_attributes_t *dstAttr, } *dstAttr = mEngine->getAttributesForStreamType(srcStream); } + + // Only honor audibility enforced when required. The client will be + // forced to reconnect if the forced usage changes. + if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + dstAttr->flags &= ~AUDIO_FLAG_AUDIBILITY_ENFORCED; + } + return NO_ERROR; } -- GitLab From ded2fd3a3f687b9dce194ecbcc0000d02125776e Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 15 May 2019 15:36:25 -0700 Subject: [PATCH 1448/1530] C2SoftHevcEnc: Reduce min supported resolution to 2x2 libhevc encoder library earlier had a restriction on min resolution supported as 320x128. It now supports upto 2x2. This commit updates min supported resolution in c2 plugin and as well in xml files. Also add supported frame-rate range in xml for c2 soft hevc encoder Bug: 130537033 Test: atest android.media.cts.MediaCodecCapabilitiesTest#\ testAllAdvertisedVideoEncoderBitrateModes Test: atest android.media.cts.MediaCodecCapabilitiesTest#\ testAllNonTunneledVideoCodecsSupportFlexibleYUV Change-Id: I0cfcf3cb3098a2a63c9386a047c702ab3c5c5a32 --- media/codec2/components/hevc/C2SoftHevcEnc.cpp | 4 ++-- media/libstagefright/data/media_codecs_google_c2_video.xml | 3 ++- media/libstagefright/data/media_codecs_sw.xml | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index efeab6c9c2..0d3357fed4 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -86,8 +86,8 @@ class C2SoftHevcEnc::IntfImpl : public SimpleInterface::BaseParams { DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240)) .withFields({ - C2F(mSize, width).inRange(320, 1920, 2), - C2F(mSize, height).inRange(128, 1088, 2), + C2F(mSize, width).inRange(2, 1920, 2), + C2F(mSize, height).inRange(2, 1088, 2), }) .withSetter(SizeSetter) .build()); diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml index f785bfae6b..04041ebfc1 100644 --- a/media/libstagefright/data/media_codecs_google_c2_video.xml +++ b/media/libstagefright/data/media_codecs_google_c2_video.xml @@ -109,11 +109,12 @@ - + + diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml index 7ee1f4daf7..37f3f61248 100644 --- a/media/libstagefright/data/media_codecs_sw.xml +++ b/media/libstagefright/data/media_codecs_sw.xml @@ -296,11 +296,12 @@ - + + -- GitLab From f61206fb63d5a3295ba31327228e2682f9d57e1d Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Sat, 18 May 2019 16:52:46 -0700 Subject: [PATCH 1449/1530] vpxdec: parallel execution of 10bit format conversion Bug: 132046952 Test: manual Change-Id: I210bbfb0de63200c1355fb7d6e4bc7b3cbdc4d0b --- media/codec2/components/vpx/C2SoftVpxDec.cpp | 97 ++++++++++++++++++-- media/codec2/components/vpx/C2SoftVpxDec.h | 22 +++++ 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp index 42f507f317..a52ca15777 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.cpp +++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "C2SoftVpxDec" #include +#include + #include #include @@ -303,13 +305,43 @@ private: #endif }; +C2SoftVpxDec::ConverterThread::ConverterThread( + const std::shared_ptr> &queue) + : Thread(false), mQueue(queue) {} + +bool C2SoftVpxDec::ConverterThread::threadLoop() { + Mutexed::Locked queue(*mQueue); + if (queue->entries.empty()) { + queue.waitForCondition(queue->cond); + if (queue->entries.empty()) { + return true; + } + } + std::function convert = queue->entries.front(); + queue->entries.pop_front(); + if (!queue->entries.empty()) { + queue->cond.signal(); + } + queue.unlock(); + + convert(); + + queue.lock(); + if (--queue->numPending == 0u) { + queue->cond.broadcast(); + } + return true; +} + C2SoftVpxDec::C2SoftVpxDec( const char *name, c2_node_id_t id, const std::shared_ptr &intfImpl) : SimpleC2Component(std::make_shared>(name, id, intfImpl)), mIntf(intfImpl), - mCodecCtx(nullptr) { + mCodecCtx(nullptr), + mCoreCount(1), + mQueue(new Mutexed) { } C2SoftVpxDec::~C2SoftVpxDec() { @@ -399,7 +431,7 @@ status_t C2SoftVpxDec::initDecoder() { vpx_codec_dec_cfg_t cfg; memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); - cfg.threads = GetCPUCoreCount(); + cfg.threads = mCoreCount = GetCPUCoreCount(); vpx_codec_flags_t flags; memset(&flags, 0, sizeof(vpx_codec_flags_t)); @@ -413,6 +445,18 @@ status_t C2SoftVpxDec::initDecoder() { return UNKNOWN_ERROR; } + if (mMode == MODE_VP9) { + using namespace std::string_literals; + for (int i = 0; i < mCoreCount; ++i) { + sp thread(new ConverterThread(mQueue)); + mConverterThreads.push_back(thread); + if (thread->run(("vp9conv #"s + std::to_string(i)).c_str(), + ANDROID_PRIORITY_AUDIO) != OK) { + return UNKNOWN_ERROR; + } + } + } + return OK; } @@ -422,6 +466,21 @@ status_t C2SoftVpxDec::destroyDecoder() { delete mCodecCtx; mCodecCtx = nullptr; } + bool running = true; + for (const sp &thread : mConverterThreads) { + thread->requestExit(); + } + while (running) { + mQueue->lock()->cond.broadcast(); + running = false; + for (const sp &thread : mConverterThreads) { + if (thread->isRunning()) { + running = true; + break; + } + } + } + mConverterThreads.clear(); return OK; } @@ -759,15 +818,35 @@ bool C2SoftVpxDec::outputBuffer( const uint16_t *srcV = (const uint16_t *)img->planes[VPX_PLANE_V]; if (format == HAL_PIXEL_FORMAT_RGBA_1010102) { - convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, - srcUStride / 2, srcVStride / 2, - dstYStride / sizeof(uint32_t), - mWidth, mHeight); + Mutexed::Locked queue(*mQueue); + size_t i = 0; + constexpr size_t kHeight = 64; + for (; i < mHeight; i += kHeight) { + queue->entries.push_back( + [dst, srcY, srcU, srcV, + srcYStride, srcUStride, srcVStride, dstYStride, + width = mWidth, height = std::min(mHeight - i, kHeight)] { + convertYUV420Planar16ToY410( + (uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, + srcUStride / 2, srcVStride / 2, dstYStride / sizeof(uint32_t), + width, height); + }); + srcY += srcYStride / 2 * kHeight; + srcU += srcUStride / 2 * (kHeight / 2); + srcV += srcVStride / 2 * (kHeight / 2); + dst += dstYStride * kHeight; + } + CHECK_EQ(0u, queue->numPending); + queue->numPending = queue->entries.size(); + while (queue->numPending > 0) { + queue->cond.signal(); + queue.waitForCondition(queue->cond); + } } else { convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2, - srcUStride / 2, srcVStride / 2, - dstYStride, dstUVStride, - mWidth, mHeight); + srcUStride / 2, srcVStride / 2, + dstYStride, dstUVStride, + mWidth, mHeight); } } else { const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y]; diff --git a/media/codec2/components/vpx/C2SoftVpxDec.h b/media/codec2/components/vpx/C2SoftVpxDec.h index 60c84847db..e51bcee0de 100644 --- a/media/codec2/components/vpx/C2SoftVpxDec.h +++ b/media/codec2/components/vpx/C2SoftVpxDec.h @@ -50,6 +50,19 @@ struct C2SoftVpxDec : public SimpleC2Component { MODE_VP9, } mMode; + struct ConversionQueue; + + class ConverterThread : public Thread { + public: + explicit ConverterThread( + const std::shared_ptr> &queue); + ~ConverterThread() override = default; + bool threadLoop() override; + + private: + std::shared_ptr> mQueue; + }; + std::shared_ptr mIntf; vpx_codec_ctx_t *mCodecCtx; bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder. @@ -59,6 +72,15 @@ struct C2SoftVpxDec : public SimpleC2Component { bool mSignalledOutputEos; bool mSignalledError; + int mCoreCount; + struct ConversionQueue { + std::list> entries; + Condition cond; + size_t numPending{0u}; + }; + std::shared_ptr> mQueue; + std::vector> mConverterThreads; + status_t initDecoder(); status_t destroyDecoder(); void finishWork(uint64_t index, const std::unique_ptr &work, -- GitLab From 41c42ab9e398b983df013864a739fb14c5d6d94d Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Wed, 15 May 2019 18:21:37 +0530 Subject: [PATCH 1450/1530] libstagefright: Add support for parsing profile and level from CSD for AV1 Also set CSD from codecPrivate data in mkv extractor Test: stagefright /sdcard/av1.mp4 Test: Manually verified profile present in format string returned by extractor Bug: 132439629 Change-Id: I1683539c3a37c88edd0edcb9f3e073c5ea077372 --- media/extractors/mkv/MatroskaExtractor.cpp | 6 ++ media/libstagefright/Utils.cpp | 64 ++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index ab76edc35b..b1eb301e05 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -1993,6 +1993,12 @@ void MatroskaExtractor::addTracks() { } } else if (!strcmp("V_AV1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); + if (codecPrivateSize > 0) { + // 'csd-0' for AV1 is the Blob of Codec Private data as + // specified in https://aomediacodec.github.io/av1-isobmff/. + AMediaFormat_setBuffer( + meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate, codecPrivateSize); + } } else if (!strcmp("V_MPEG2", codecID) || !strcmp("V_MPEG1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG2); diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 3de934f54a..135151f122 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -573,6 +574,68 @@ static void parseVp9ProfileLevelFromCsd(const sp &csd, sp &fo } } +static void parseAV1ProfileLevelFromCsd(const sp &csd, sp &format) { + // Parse CSD structure to extract profile level information + // https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox + const uint8_t *data = csd->data(); + size_t remaining = csd->size(); + if (remaining < 4 || data[0] != 0x81) { // configurationVersion == 1 + return; + } + uint8_t profileData = (data[1] & 0xE0) >> 5; + uint8_t levelData = data[1] & 0x1F; + uint8_t highBitDepth = (data[2] & 0x40) >> 6; + + const static ALookup, int32_t> profiles { + { { 0, 0 }, AV1ProfileMain8 }, + { { 1, 0 }, AV1ProfileMain10 }, + }; + + int32_t profile; + if (profiles.map(std::make_pair(highBitDepth, profileData), &profile)) { + // bump to HDR profile + if (isHdr(format) && profile == AV1ProfileMain10) { + if (format->contains("hdr10-plus-info")) { + profile = AV1ProfileMain10HDR10Plus; + } else { + profile = AV1ProfileMain10HDR10; + } + } + format->setInt32("profile", profile); + } + const static ALookup levels { + { 0, AV1Level2 }, + { 1, AV1Level21 }, + { 2, AV1Level22 }, + { 3, AV1Level23 }, + { 4, AV1Level3 }, + { 5, AV1Level31 }, + { 6, AV1Level32 }, + { 7, AV1Level33 }, + { 8, AV1Level4 }, + { 9, AV1Level41 }, + { 10, AV1Level42 }, + { 11, AV1Level43 }, + { 12, AV1Level5 }, + { 13, AV1Level51 }, + { 14, AV1Level52 }, + { 15, AV1Level53 }, + { 16, AV1Level6 }, + { 17, AV1Level61 }, + { 18, AV1Level62 }, + { 19, AV1Level63 }, + { 20, AV1Level7 }, + { 21, AV1Level71 }, + { 22, AV1Level72 }, + { 23, AV1Level73 }, + }; + + int32_t level; + if (levels.map(levelData, &level)) { + format->setInt32("level", level); + } +} + static std::vector> stringMappings { { @@ -1234,6 +1297,7 @@ status_t convertMetaDataToMessage( buffer->meta()->setInt32("csd", true); buffer->meta()->setInt64("timeUs", 0); msg->setBuffer("csd-0", buffer); + parseAV1ProfileLevelFromCsd(buffer, msg); } else if (meta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds((const char *)data, size); if (esds.InitCheck() != (status_t)OK) { -- GitLab From 8045d592d555ce962ae083642917cf60ec5ef4cd Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Sat, 18 May 2019 18:59:21 -0700 Subject: [PATCH 1451/1530] C2SoftHevcEnc: Add support for dynamic bitrate change Test: Manual with a application based on AMediaCodec api Bug: 125938526 Change-Id: Idefe6291d8d7185400d20a7c95c9ea5070a0b196 --- media/codec2/components/hevc/C2SoftHevcEnc.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 0d3357fed4..fb2da7c997 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -865,6 +865,22 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, return; } } + + // handle dynamic config parameters + { + IntfImpl::Lock lock = mIntf->lock(); + std::shared_ptr bitrate = mIntf->getBitrate_l(); + lock.unlock(); + + if (bitrate != mBitrate) { + mBitrate = bitrate; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0] = + mBitrate->value; + mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0] = + mBitrate->value << 1; + } + } + ihevce_inp_buf_t s_encode_ip{}; ihevce_out_buf_t s_encode_op{}; uint64_t workIndex = work->input.ordinal.frameIndex.peekull(); -- GitLab From 881b100bad8d7a598d4fa962a42bef60ba4bf520 Mon Sep 17 00:00:00 2001 From: Sundong Ahn Date: Tue, 21 May 2019 14:25:57 +0900 Subject: [PATCH 1452/1530] Update media_codecs.xsd schema The media_codecs.xsd is updated. Add Domain and Variant elements. Add rank, domain, variant and enabled attributes. Bug: 133114972 Test: vts-tradefed run vts -m VtsValidateMediaCodecs Change-Id: I411351a7af877a1289ce5e2e9b8bbdfb97f910da --- .../libstagefright/xmlparser/api/current.txt | 24 ++++++++++++++++++- .../libstagefright/xmlparser/media_codecs.xsd | 19 ++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt index f7f4c36eb4..9d7c57dfc8 100644 --- a/media/libstagefright/xmlparser/api/current.txt +++ b/media/libstagefright/xmlparser/api/current.txt @@ -68,16 +68,26 @@ package media.codecs { public class MediaCodec { ctor public MediaCodec(); method public java.util.List getAlias_optional(); + method public java.util.List getAttribute_optional(); + method public String getDomain(); + method public String getEnabled(); method public java.util.List getFeature_optional(); method public java.util.List getLimit_optional(); method public String getName(); method public java.util.List getQuirk_optional(); + method public String getRank(); method public String getType(); method public java.util.List getType_optional(); method public String getUpdate(); + method public String getVariant(); + method public java.util.List getVariant_optional(); + method public void setDomain(String); + method public void setEnabled(String); method public void setName(String); + method public void setRank(String); method public void setType(String); method public void setUpdate(String); + method public void setVariant(String); } public class MediaCodecs { @@ -91,14 +101,18 @@ package media.codecs { public class Quirk { ctor public Quirk(); method public String getName(); + method public String getValue(); method public void setName(String); + method public void setValue(String); } public class Setting { ctor public Setting(); + method public String getEnabled(); method public String getName(); method public String getUpdate(); method public String getValue(); + method public void setEnabled(String); method public void setName(String); method public void setUpdate(String); method public void setValue(String); @@ -106,7 +120,9 @@ package media.codecs { public class Settings { ctor public Settings(); - method public java.util.List getSetting(); + method public java.util.List getDomain_optional(); + method public java.util.List getSetting_optional(); + method public java.util.List getVariant_optional(); } public class Type { @@ -120,6 +136,12 @@ package media.codecs { method public void setUpdate(String); } + public class Variant { + ctor public Variant(); + method public String getName(); + method public void setName(String); + } + public class XmlParser { ctor public XmlParser(); method public static media.codecs.Included readIncluded(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd index 77193a26b1..63ec5d049a 100644 --- a/media/libstagefright/xmlparser/media_codecs.xsd +++ b/media/libstagefright/xmlparser/media_codecs.xsd @@ -49,24 +49,33 @@ - - - + + + + + + + + + + + + @@ -97,9 +106,13 @@ + + + + -- GitLab From 688abaa2865dab9ba955a6543a965d10e4c506db Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Fri, 17 May 2019 16:32:23 -0700 Subject: [PATCH 1453/1530] Enumerate all hevc encoders to find one that support CQ @ 512x512 size bug: 132905160 test: ImageReaderTest#testHeic Change-Id: I497b203f7e21299eb4e322af4d4c5c925cf5a48a --- .../api2/HeicCompositeStream.cpp | 13 +- .../api2/HeicCompositeStream.h | 2 +- .../api2/HeicEncoderInfoManager.cpp | 113 +++++++++++++----- .../api2/HeicEncoderInfoManager.h | 4 +- 4 files changed, 94 insertions(+), 38 deletions(-) diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp index 743c8163cc..5a87134743 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp @@ -330,9 +330,9 @@ status_t HeicCompositeStream::getCompositeStreamInfo(const OutputStreamInfo &str } bool HeicCompositeStream::isSizeSupportedByHeifEncoder(int32_t width, int32_t height, - bool* useHeic, bool* useGrid, int64_t* stall) { + bool* useHeic, bool* useGrid, int64_t* stall, AString* hevcName) { static HeicEncoderInfoManager& heicManager = HeicEncoderInfoManager::getInstance(); - return heicManager.isSizeSupported(width, height, useHeic, useGrid, stall); + return heicManager.isSizeSupported(width, height, useHeic, useGrid, stall, hevcName); } bool HeicCompositeStream::isInMemoryTempFileSupported() { @@ -1115,8 +1115,9 @@ status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height, ALOGV("%s", __FUNCTION__); bool useGrid = false; + AString hevcName; bool isSizeSupported = isSizeSupportedByHeifEncoder(width, height, - &mUseHeic, &useGrid, nullptr); + &mUseHeic, &useGrid, nullptr, &hevcName); if (!isSizeSupported) { ALOGE("%s: Encoder doesnt' support size %u x %u!", __FUNCTION__, width, height); @@ -1138,7 +1139,11 @@ status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height, } // Create HEIC/HEVC codec. - mCodec = MediaCodec::CreateByType(mCodecLooper, desiredMime, true /*encoder*/); + if (mUseHeic) { + mCodec = MediaCodec::CreateByType(mCodecLooper, desiredMime, true /*encoder*/); + } else { + mCodec = MediaCodec::CreateByComponentName(mCodecLooper, hevcName); + } if (mCodec == nullptr) { ALOGE("%s: Failed to create codec for %s", __FUNCTION__, desiredMime); return NO_INIT; diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h index 2aa3c38bc1..260c68e081 100644 --- a/services/camera/libcameraservice/api2/HeicCompositeStream.h +++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h @@ -71,7 +71,7 @@ public: const CameraMetadata& ch, std::vector* compositeOutput /*out*/); static bool isSizeSupportedByHeifEncoder(int32_t width, int32_t height, - bool* useHeic, bool* useGrid, int64_t* stall); + bool* useHeic, bool* useGrid, int64_t* stall, AString* hevcName = nullptr); static bool isInMemoryTempFileSupported(); protected: diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp index ed9be6e934..d7cc2bf113 100644 --- a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp +++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp @@ -49,7 +49,7 @@ HeicEncoderInfoManager::~HeicEncoderInfoManager() { } bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool* useHeic, - bool* useGrid, int64_t* stall) const { + bool* useGrid, int64_t* stall, AString* hevcName) const { if (useHeic == nullptr || useGrid == nullptr) { ALOGE("%s: invalid parameters: useHeic %p, useGrid %p", __FUNCTION__, useHeic, useGrid); @@ -72,6 +72,9 @@ bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool (width <= 1920 && height <= 1080))) { enableGrid = false; } + if (hevcName != nullptr) { + *hevcName = mHevcName; + } } else { // No encoder available for the requested size. return false; @@ -113,9 +116,8 @@ status_t HeicEncoderInfoManager::initialize() { } sp heicDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC); - sp hevcDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC); - if (hevcDetails == nullptr) { + if (!getHevcCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC)) { if (heicDetails != nullptr) { ALOGE("%s: Device must support HEVC codec if HEIC codec is available!", __FUNCTION__); @@ -123,22 +125,7 @@ status_t HeicEncoderInfoManager::initialize() { } return OK; } - - // Check CQ mode for HEVC codec - { - AString bitrateModes; - auto hasItem = hevcDetails->findString("feature-bitrate-modes", &bitrateModes); - if (!hasItem) { - ALOGE("%s: Failed to query bitrate modes for HEVC codec", __FUNCTION__); - return BAD_VALUE; - } - ALOGV("%s: HEVC codec's feature-bitrate-modes value is %d, %s", - __FUNCTION__, hasItem, bitrateModes.c_str()); - std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase); - if (!std::regex_search(bitrateModes.c_str(), pattern)) { - return OK; - } - } + mHasHEVC = true; // HEIC size range if (heicDetails != nullptr) { @@ -152,19 +139,6 @@ status_t HeicEncoderInfoManager::initialize() { mHasHEIC = true; } - // HEVC size range - { - auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC, - hevcDetails, &mMinSizeHevc, &mMaxSizeHevc, &mHevcFrameRateMaps); - if (res != OK) { - ALOGE("%s: Failed to get HEVC codec size range: %s (%d)", __FUNCTION__, - strerror(-res), res); - return BAD_VALUE; - } - - mHasHEVC = true; - } - return OK; } @@ -290,5 +264,80 @@ sp HeicEncoderInfoManager::getCodecDetails( return details; } + +bool HeicEncoderInfoManager::getHevcCodecDetails( + sp codecsList, const char* mime) { + bool found = false; + ssize_t idx = 0; + while ((idx = codecsList->findCodecByType(mime, true /*encoder*/, idx)) >= 0) { + const sp info = codecsList->getCodecInfo(idx++); + if (info == nullptr) { + ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, mime); + break; + } + + // Filter out software ones as they may be too slow + if (!(info->getAttributes() & MediaCodecInfo::kFlagIsHardwareAccelerated)) { + continue; + } + + const sp caps = + info->getCapabilitiesFor(mime); + if (caps == nullptr) { + ALOGE("%s: [%s] Failed to get capabilities", __FUNCTION__, + info->getCodecName()); + break; + } + const sp details = caps->getDetails(); + if (details == nullptr) { + ALOGE("%s: [%s] Failed to get details", __FUNCTION__, + info->getCodecName()); + break; + } + + // Check CQ mode + AString bitrateModes; + auto hasItem = details->findString("feature-bitrate-modes", &bitrateModes); + if (!hasItem) { + ALOGE("%s: [%s] Failed to query bitrate modes", __FUNCTION__, + info->getCodecName()); + break; + } + ALOGV("%s: [%s] feature-bitrate-modes value is %d, %s", + __FUNCTION__, info->getCodecName(), hasItem, bitrateModes.c_str()); + std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase); + if (!std::regex_search(bitrateModes.c_str(), pattern)) { + continue; // move on to next encoder + } + + std::pair minSizeHevc, maxSizeHevc; + FrameRateMaps hevcFrameRateMaps; + auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC, + details, &minSizeHevc, &maxSizeHevc, &hevcFrameRateMaps); + if (res != OK) { + ALOGE("%s: [%s] Failed to get size range: %s (%d)", __FUNCTION__, + info->getCodecName(), strerror(-res), res); + break; + } + if (kGridWidth < minSizeHevc.first + || kGridWidth > maxSizeHevc.first + || kGridHeight < minSizeHevc.second + || kGridHeight > maxSizeHevc.second) { + continue; // move on to next encoder + } + + // Found: save name, size, frame rate + mHevcName = info->getCodecName(); + mMinSizeHevc = minSizeHevc; + mMaxSizeHevc = maxSizeHevc; + mHevcFrameRateMaps = hevcFrameRateMaps; + + found = true; + break; + } + + return found; +} + } //namespace camera3 } // namespace android diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h index fb0b9140a9..58edba2d3c 100644 --- a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h +++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h @@ -36,7 +36,7 @@ public: } bool isSizeSupported(int32_t width, int32_t height, - bool* useHeic, bool* useGrid, int64_t* stall) const; + bool* useHeic, bool* useGrid, int64_t* stall, AString* hevcName) const; static const auto kGridWidth = 512; static const auto kGridHeight = 512; @@ -61,11 +61,13 @@ private: FrameRateMaps::const_iterator findClosestSize(const FrameRateMaps& maps, int32_t width, int32_t height) const; sp getCodecDetails(sp codecsList, const char* name); + bool getHevcCodecDetails(sp codecsList, const char* mime); bool mIsInited; std::pair mMinSizeHeic, mMaxSizeHeic; std::pair mMinSizeHevc, mMaxSizeHevc; bool mHasHEVC, mHasHEIC; + AString mHevcName; FrameRateMaps mHeicFrameRateMaps, mHevcFrameRateMaps; bool mDisableGrid; -- GitLab From 9066ad3846deb54fe2bba86b25a934d5bf6033d7 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 20 May 2019 14:40:10 -0700 Subject: [PATCH 1454/1530] audioflinger: fix App Ops for services Do not check app ops for audio playback for services in general, not just for audioserver or root user. Bug: 133178934 Test: audio smoke tests Change-Id: I3a34e6418dc750cc12ed35d465ca8874b0ce0f73 --- services/audioflinger/PlaybackTracks.h | 1 + services/audioflinger/Tracks.cpp | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 7008ceed41..a093893fac 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -31,6 +31,7 @@ public: private: OpPlayAudioMonitor(uid_t uid, audio_usage_t usage, int id); void onFirstRef() override; + static void getPackagesForUid(uid_t uid, Vector& packages); AppOpsManager mAppOpsManager; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index b0817ed3ea..78db80ca0a 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -389,9 +389,16 @@ sp AudioFlinger::PlaybackThread::OpPlayAudioMonitor::createIfNeeded( uid_t uid, const audio_attributes_t& attr, int id, audio_stream_type_t streamType) { - if (isAudioServerOrRootUid(uid)) { - ALOGD("OpPlayAudio: not muting track:%d usage:%d root or audioserver", id, attr.usage); - return nullptr; + if (isServiceUid(uid)) { + Vector packages; + getPackagesForUid(uid, packages); + if (packages.isEmpty()) { + ALOGD("OpPlayAudio: not muting track:%d usage:%d for service UID %d", + id, + attr.usage, + uid); + return nullptr; + } } // stream type has been filtered by audio policy to indicate whether it can be muted if (streamType == AUDIO_STREAM_ENFORCED_AUDIBLE) { @@ -423,8 +430,7 @@ AudioFlinger::PlaybackThread::OpPlayAudioMonitor::~OpPlayAudioMonitor() void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::onFirstRef() { - PermissionController permissionController; - permissionController.getPackagesForUid(mUid, mPackages); + getPackagesForUid(mUid, mPackages); checkPlayAudioForUsage(); if (!mPackages.isEmpty()) { mOpCallback = new PlayAudioOpCallback(this); @@ -475,6 +481,14 @@ void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::PlayAudioOpCallback::opCh } } +// static +void AudioFlinger::PlaybackThread::OpPlayAudioMonitor::getPackagesForUid( + uid_t uid, Vector& packages) +{ + PermissionController permissionController; + permissionController.getPackagesForUid(uid, packages); +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::Track" -- GitLab From 603467bfb57344f3552b3478e3f69244326174e9 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Thu, 16 May 2019 16:48:00 -0700 Subject: [PATCH 1455/1530] Bufferpool: Check hidl call return value. Hidl will crash if hidl call failed and return value was not checked. Check hidl call return values. Bug: 132182836 Change-Id: I910025a7b3c508b3c8f289a5286abbcc77e2d9b8 --- media/bufferpool/1.0/BufferPoolClient.cpp | 4 ++++ media/bufferpool/2.0/AccessorImpl.cpp | 8 +++++++- media/bufferpool/2.0/BufferPoolClient.cpp | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/media/bufferpool/1.0/BufferPoolClient.cpp b/media/bufferpool/1.0/BufferPoolClient.cpp index 41520ca544..d7123984c5 100644 --- a/media/bufferpool/1.0/BufferPoolClient.cpp +++ b/media/bufferpool/1.0/BufferPoolClient.cpp @@ -528,6 +528,10 @@ void BufferPoolClient::Impl::trySyncFromRemote() { (void) outStatus; (void) outBuffer; }); + if(!transResult.isOk()) { + ALOGD("sync from client %lld failed: bufferpool process died.", + (long long)mConnectionId); + } } mRemoteSyncLock.unlock(); } diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp index 526090943c..94cf006952 100644 --- a/media/bufferpool/2.0/AccessorImpl.cpp +++ b/media/bufferpool/2.0/AccessorImpl.cpp @@ -261,13 +261,19 @@ void Accessor::Impl::handleInvalidateAck() { mBufferPool.mInvalidation.onHandleAck(&observers, &invalidationId); } // Do not hold lock for send invalidations + size_t deadClients = 0; for (auto it = observers.begin(); it != observers.end(); ++it) { const sp observer = it->second; if (observer) { Return transResult = observer->onMessage(it->first, invalidationId); - (void) transResult; + if (!transResult.isOk()) { + ++deadClients; + } } } + if (deadClients > 0) { + ALOGD("During invalidation found %zu dead clients", deadClients); + } } bool Accessor::Impl::isValid() { diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp index f907de5306..342fef6b9d 100644 --- a/media/bufferpool/2.0/BufferPoolClient.cpp +++ b/media/bufferpool/2.0/BufferPoolClient.cpp @@ -582,6 +582,10 @@ void BufferPoolClient::Impl::trySyncFromRemote() { (void) outStatus; (void) outBuffer; }); + if (!transResult.isOk()) { + ALOGD("sync from client %lld failed: bufferpool process died.", + (long long)mConnectionId); + } } mRemoteSyncLock.unlock(); } -- GitLab From 8eec9d08a6653c86c8c65c70780c21eccb6e2532 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Wed, 22 May 2019 15:08:49 -0700 Subject: [PATCH 1456/1530] codec2: cache ion buffers with CPU access flags. Bug: 131600580 Bug: 132046952 Change-Id: I72a63c18ee1e43bf55507683e01c6746eba6ade9 --- media/codec2/vndk/C2AllocatorIon.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp index d22153d85d..752bc46814 100644 --- a/media/codec2/vndk/C2AllocatorIon.cpp +++ b/media/codec2/vndk/C2AllocatorIon.cpp @@ -554,7 +554,11 @@ c2_status_t C2AllocatorIon::mapUsage( } else { *align = 0; // TODO make this 1 *heapMask = ~0; // default mask - *flags = 0; // default flags + if (usage.expected & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE)) { + *flags = ION_FLAG_CACHED; // cache CPU accessed buffers + } else { + *flags = 0; // default flags + } res = C2_NO_INIT; } // add usage to cache -- GitLab From 5592ee268507a8ee90fd374530e55a6279dbf0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Gaffie?= Date: Wed, 22 May 2019 08:00:05 +0200 Subject: [PATCH 1457/1530] audiopolicy: fix source client lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upon startAudioSource -> connectAudioSource, the source client is attached to the output. Upon stopAudioSource -> disconnectAudioSource, the source client is never removed. As a result, if the same client is trying to call startAudioSource again, asthe client is already attached, getClient will lead to bailing out in connectAudioSource and the source will not be started. The stopAudioSource if called would claim that the client refcount is already 0. Any following call to start would fail again on outputDesc->start as invalid as the profile fails on canStartNewIo. This CL fixes this issue by removing the client source desc on disconnectAudioSource. Bug: 133367802 Test: build & run AudioPolicyEmulatorTests --gtest_filter=*AudioSourceEffectTest* multiple consecutive times. Change-Id: I78370be248ff24cf4135d51f709d965c5bfae4e8 Signed-off-by: François Gaffie --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 575a6c20fd..9025119b73 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -3924,6 +3924,8 @@ status_t AudioPolicyManager::connectAudioSource(const sp if (status != NO_ERROR) { mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0); + outputDesc->removeClient(sourceDesc->portId()); + outputDesc->stop(); return status; } sourceDesc->setSwOutput(outputDesc); @@ -4188,6 +4190,7 @@ status_t AudioPolicyManager::disconnectAudioSource(const spstop(); } + swOutputDesc->removeClient(sourceDesc->portId()); mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); } else { sp hwOutputDesc = sourceDesc->hwOutput().promote(); -- GitLab From 9eac4d1485d69e44c467f0595dc3537a7707d004 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 23 May 2019 12:58:48 -0700 Subject: [PATCH 1458/1530] CCodec: pass consumer usage from component to input surface Bug: 129670826 Test: manual Change-Id: I258b739a5ab032a4390d34d5449fbe8473a4faf9 --- media/codec2/sfplugin/C2OMXNode.cpp | 34 ++++++++++++--------- media/codec2/sfplugin/CCodec.cpp | 32 +++++++++++++++---- media/codec2/sfplugin/InputSurfaceWrapper.h | 1 + 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 6ae1c13ac4..f0f62f66d7 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -50,14 +50,8 @@ public: } // namespace C2OMXNode::C2OMXNode(const std::shared_ptr &comp) - : mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), + : mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), mUsage(0), mAdjustTimestampGapUs(0), mFirstInputFrame(true) { - // TODO: read from intf() - if (!strncmp(comp->getName().c_str(), "c2.android.", 11)) { - mUsage = GRALLOC_USAGE_SW_READ_OFTEN; - } else { - mUsage = GRALLOC_USAGE_HW_VIDEO_ENCODER; - } } status_t C2OMXNode::freeNode() { @@ -103,13 +97,25 @@ status_t C2OMXNode::getParameter(OMX_INDEXTYPE index, void *params, size_t size) } status_t C2OMXNode::setParameter(OMX_INDEXTYPE index, const void *params, size_t size) { - // handle max/fixed frame duration control - if (index == (OMX_INDEXTYPE)OMX_IndexParamMaxFrameDurationForBitrateControl - && params != NULL - && size == sizeof(OMX_PARAM_U32TYPE)) { - // The incoming number is an int32_t contained in OMX_U32. - mAdjustTimestampGapUs = (int32_t)((OMX_PARAM_U32TYPE*)params)->nU32; - return OK; + if (params == NULL) { + return BAD_VALUE; + } + switch ((uint32_t)index) { + case OMX_IndexParamMaxFrameDurationForBitrateControl: + // handle max/fixed frame duration control + if (size != sizeof(OMX_PARAM_U32TYPE)) { + return BAD_VALUE; + } + // The incoming number is an int32_t contained in OMX_U32. + mAdjustTimestampGapUs = (int32_t)((OMX_PARAM_U32TYPE*)params)->nU32; + return OK; + + case OMX_IndexParamConsumerUsageBits: + if (size != sizeof(OMX_U32)) { + return BAD_VALUE; + } + mUsage = *((OMX_U32 *)params); + return OK; } return ERROR_UNSUPPORTED; } diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 85c783bec1..008e20e985 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -183,9 +183,11 @@ public: GraphicBufferSourceWrapper( const sp &source, uint32_t width, - uint32_t height) + uint32_t height, + uint64_t usage) : mSource(source), mWidth(width), mHeight(height) { mDataSpace = HAL_DATASPACE_BT709; + mConfig.mUsage = usage; } ~GraphicBufferSourceWrapper() override = default; @@ -193,6 +195,12 @@ public: mNode = new C2OMXNode(comp); mNode->setFrameSize(mWidth, mHeight); + // Usage is queried during configure(), so setting it beforehand. + OMX_U32 usage = mConfig.mUsage & 0xFFFFFFFF; + (void)mNode->setParameter( + (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits, + &usage, sizeof(usage)); + // NOTE: we do not use/pass through color aspects from GraphicBufferSource as we // communicate that directly to the component. mSource->configure(mNode, mDataSpace); @@ -364,7 +372,8 @@ public: // color aspects (android._color-aspects) - // consumer usage + // consumer usage is queried earlier. + ALOGD("ISConfig%s", status.str().c_str()); return err; } @@ -813,6 +822,7 @@ void CCodec::configure(const sp &msg) { config->mISConfig->mSuspended = true; } } + config->mISConfig->mUsage = 0; } /* @@ -876,8 +886,14 @@ void CCodec::configure(const sp &msg) { indices.size(), params.size()); return UNKNOWN_ERROR; } - if (usage && (usage.value & C2MemoryUsage::CPU_READ)) { - config->mInputFormat->setInt32("using-sw-read-often", true); + if (usage) { + if (usage.value & C2MemoryUsage::CPU_READ) { + config->mInputFormat->setInt32("using-sw-read-often", true); + } + if (config->mISConfig) { + C2AndroidMemoryUsage androidUsage(C2MemoryUsage(usage.value)); + config->mISConfig->mUsage = androidUsage.asGrallocUsage(); + } } // NOTE: we don't blindly use client specified input size if specified as clients @@ -1068,10 +1084,12 @@ void CCodec::createInputSurface() { sp inputFormat; sp outputFormat; + uint64_t usage = 0; { Mutexed::Locked config(mConfig); inputFormat = config->mInputFormat; outputFormat = config->mOutputFormat; + usage = config->mISConfig ? config->mISConfig->mUsage : 0; } sp persistentSurface = CreateCompatibleInputSurface(); @@ -1095,7 +1113,7 @@ void CCodec::createInputSurface() { int32_t height = 0; (void)outputFormat->findInt32("height", &height); err = setupInputSurface(std::make_shared( - persistentSurface->getBufferSource(), width, height)); + persistentSurface->getBufferSource(), width, height, usage)); bufferProducer = persistentSurface->getBufferProducer(); } @@ -1155,10 +1173,12 @@ void CCodec::initiateSetInputSurface(const sp &surface) { void CCodec::setInputSurface(const sp &surface) { sp inputFormat; sp outputFormat; + uint64_t usage = 0; { Mutexed::Locked config(mConfig); inputFormat = config->mInputFormat; outputFormat = config->mOutputFormat; + usage = config->mISConfig ? config->mISConfig->mUsage : 0; } auto hidlTarget = surface->getHidlTarget(); if (hidlTarget) { @@ -1182,7 +1202,7 @@ void CCodec::setInputSurface(const sp &surface) { int32_t height = 0; (void)outputFormat->findInt32("height", &height); status_t err = setupInputSurface(std::make_shared( - surface->getBufferSource(), width, height)); + surface->getBufferSource(), width, height, usage)); if (err != OK) { ALOGE("Failed to set up input surface: %d", err); mCallback->onInputSurfaceDeclined(err); diff --git a/media/codec2/sfplugin/InputSurfaceWrapper.h b/media/codec2/sfplugin/InputSurfaceWrapper.h index 8341fd577b..bb35763f41 100644 --- a/media/codec2/sfplugin/InputSurfaceWrapper.h +++ b/media/codec2/sfplugin/InputSurfaceWrapper.h @@ -78,6 +78,7 @@ public: // IN PARAMS (CODEC WRAPPER) float mFixedAdjustedFps; // fixed fps via PTS manipulation float mMinAdjustedFps; // minimum fps via PTS manipulation + uint64_t mUsage; // consumer usage }; /** -- GitLab From 851e22d1dc89a7f708b9d2b56947f69cd1a08b94 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 14 May 2019 10:53:06 -0700 Subject: [PATCH 1459/1530] Fix overflow/dos in 3gg text description parsing Bug: 124781927 Test: run pocs Change-Id: I8765ac9746c3de7d711ef866d4ec0e29972320c0 --- media/libstagefright/timedtext/TextDescriptions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp index c762a74d08..83d15a28ca 100644 --- a/media/libstagefright/timedtext/TextDescriptions.cpp +++ b/media/libstagefright/timedtext/TextDescriptions.cpp @@ -383,7 +383,7 @@ status_t TextDescriptions::extract3GPPGlobalDescriptions( tmpData += 8; size_t remaining = size - 8; - if (size < chunkSize) { + if (chunkSize <= 8 || size < chunkSize) { return OK; } switch(chunkType) { -- GitLab From 65457f6513c6e8d806ee090f435829041ee11f42 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 21 May 2019 15:28:26 -0700 Subject: [PATCH 1460/1530] Update bitrate modes for opus encoder in media_codecs_sw.xml Supported Bitrate mode was wrongly listed as CQ Test: Build, flash verify media_codecs.xml on device Bug: 133260064 Change-Id: I974f18f9303cff063bcadc71e1ecb10fc6bcfce3 --- media/libstagefright/data/media_codecs_google_c2_audio.xml | 2 +- media/libstagefright/data/media_codecs_sw.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml index be2404d179..509f7a997b 100644 --- a/media/libstagefright/data/media_codecs_google_c2_audio.xml +++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml @@ -112,7 +112,7 @@ - + diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml index 37f3f61248..67d3f1af12 100644 --- a/media/libstagefright/data/media_codecs_sw.xml +++ b/media/libstagefright/data/media_codecs_sw.xml @@ -237,7 +237,7 @@ - + -- GitLab From b99d91fc925147caee29b853f6ed0261b35f35b7 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Sat, 18 May 2019 18:33:16 -0700 Subject: [PATCH 1461/1530] C2SoftFlacEnc: Add support for complexity parameter "flac-compression-level" is mapped to complexity in C2 mapper Test: Manually using AMediaCodec based application Bug: 133058238 Change-Id: I8b7d5afc8620bc397c5b0b519a9b983f41a53efc --- media/codec2/components/flac/C2SoftFlacEnc.cpp | 15 ++++++++++++--- media/codec2/components/flac/C2SoftFlacEnc.h | 1 - 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp index cf34dffacb..408db7e00c 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.cpp +++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp @@ -73,6 +73,14 @@ public: .withFields({C2F(mBitrate, value).inRange(1, 21000000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); + addParameter( + DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY) + .withDefault(new C2StreamComplexityTuning::output(0u, + FLAC_COMPRESSION_LEVEL_DEFAULT)) + .withFields({C2F(mComplexity, value).inRange( + FLAC_COMPRESSION_LEVEL_MIN, FLAC_COMPRESSION_LEVEL_MAX)}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); addParameter( DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 4608)) @@ -93,12 +101,14 @@ public: uint32_t getSampleRate() const { return mSampleRate->value; } uint32_t getChannelCount() const { return mChannelCount->value; } uint32_t getBitrate() const { return mBitrate->value; } + uint32_t getComplexity() const { return mComplexity->value; } int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; } private: std::shared_ptr mSampleRate; std::shared_ptr mChannelCount; std::shared_ptr mBitrate; + std::shared_ptr mComplexity; std::shared_ptr mInputMaxBufSize; std::shared_ptr mPcmEncodingInfo; }; @@ -127,7 +137,6 @@ c2_status_t C2SoftFlacEnc::onInit() { mSignalledError = false; mSignalledOutputEos = false; - mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT; mIsFirstFrame = true; mAnchorTimeStamp = 0ull; mProcessedSamples = 0u; @@ -153,7 +162,6 @@ void C2SoftFlacEnc::onRelease() { } void C2SoftFlacEnc::onReset() { - mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT; (void) onStop(); } @@ -369,7 +377,8 @@ status_t C2SoftFlacEnc::configureEncoder() { ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mIntf->getChannelCount()); ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mIntf->getSampleRate()); ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, bitsPerSample); - ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel); + ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, + mIntf->getComplexity()); ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false); if (!ok) { ALOGE("unknown error when configuring encoder"); diff --git a/media/codec2/components/flac/C2SoftFlacEnc.h b/media/codec2/components/flac/C2SoftFlacEnc.h index cdf305e2cb..b3f01d544a 100644 --- a/media/codec2/components/flac/C2SoftFlacEnc.h +++ b/media/codec2/components/flac/C2SoftFlacEnc.h @@ -69,7 +69,6 @@ private: std::shared_ptr mOutputBlock; bool mSignalledError; bool mSignalledOutputEos; - uint32_t mCompressionLevel; uint32_t mBlockSize; bool mIsFirstFrame; uint64_t mAnchorTimeStamp; -- GitLab From d493e98942febb5113b0dbfd936abbfe47c7ff91 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Sat, 18 May 2019 19:55:57 -0700 Subject: [PATCH 1462/1530] C2SoftHevcEnc: Do not initialize encoder in onInit() Like c2 soft avc encoder, defer encoder initialization to process() This change allows multiple start() and reset() calls. Test: adb shell /data/local/tmp/vts/VtsHidlC2V1_0TargetComponentTest\ -C c2.android.hevc.encoder -P /sdcard/media/ -I software Test: atest android.media.cts.VideoEncoderTest Bug: 133067331 Change-Id: Ia66206e9cfc008508222900dc498ff3b30e2fb4e --- media/codec2/components/hevc/C2SoftHevcEnc.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 0d3357fed4..9b93cf3a72 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -390,27 +390,23 @@ C2SoftHevcEnc::C2SoftHevcEnc(const char* name, c2_node_id_t id, } C2SoftHevcEnc::~C2SoftHevcEnc() { - releaseEncoder(); + onRelease(); } c2_status_t C2SoftHevcEnc::onInit() { - return initEncoder(); + return C2_OK; } c2_status_t C2SoftHevcEnc::onStop() { - if (!mStarted) { - return C2_OK; - } - return releaseEncoder(); + return C2_OK; } void C2SoftHevcEnc::onReset() { - onStop(); - initEncoder(); + releaseEncoder(); } void C2SoftHevcEnc::onRelease() { - onStop(); + releaseEncoder(); } c2_status_t C2SoftHevcEnc::onFlush_sm() { -- GitLab From 679172768d86e29a47b9e15dd56776694128cf59 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 22 May 2019 11:54:37 -0700 Subject: [PATCH 1463/1530] APM: make AudioPolicyMixCollection inherit from Vector Modify AudioPolicyMixCollection to inherit from Vector instead of KeyedVector so all so the collection of mixes stays in the same order as the mixes were added. Also make the mix selection take the deviceType into consideration, not just the address to enable the same address to be associated to different devices. Bug: 131090322 Test: see bug for multi bus scenario Test: atest AudioPlaybackCaptureTest Change-Id: I4dc7f23bef19a7d47afc2998102da07dde41fbca --- .../include/AudioPolicyMix.h | 11 +- .../managerdefinitions/src/AudioPolicyMix.cpp | 106 +++++++++++------- .../managerdefault/AudioPolicyManager.cpp | 20 ++-- 3 files changed, 85 insertions(+), 52 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h index 12b5e7d4bb..094f5069ac 100644 --- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -19,7 +19,7 @@ #include "DeviceDescriptor.h" #include #include -#include +#include #include #include @@ -48,14 +48,15 @@ private: }; -class AudioPolicyMixCollection : public DefaultKeyedVector > +class AudioPolicyMixCollection : public Vector> { public: - status_t getAudioPolicyMix(const String8& address, sp &policyMix) const; + status_t getAudioPolicyMix(audio_devices_t deviceType, + const String8& address, sp &policyMix) const; - status_t registerMix(const String8& address, AudioMix mix, sp desc); + status_t registerMix(AudioMix mix, sp desc); - status_t unregisterMix(const String8& address); + status_t unregisterMix(const AudioMix& mix); void closeOutput(sp &desc); diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 98a7800753..dca84c0bf6 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -73,16 +73,21 @@ void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const } } -status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix mix, - sp desc) +status_t AudioPolicyMixCollection::registerMix(AudioMix mix, sp desc) { - ssize_t index = indexOfKey(address); - if (index >= 0) { - ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); - return BAD_VALUE; + for (size_t i = 0; i < size(); i++) { + const sp& registeredMix = itemAt(i); + if (mix.mDeviceType == registeredMix->mDeviceType + && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) { + ALOGE("registerMix(): mix already registered for dev=0x%x addr=%s", + mix.mDeviceType, mix.mDeviceAddress.string()); + return BAD_VALUE; + } } sp policyMix = new AudioPolicyMix(mix); - add(address, policyMix); + add(policyMix); + ALOGD("registerMix(): adding mix for dev=0x%x addr=%s", + policyMix->mDeviceType, policyMix->mDeviceAddress.string()); if (desc != 0) { desc->mPolicyMix = policyMix; @@ -91,34 +96,48 @@ status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix return NO_ERROR; } -status_t AudioPolicyMixCollection::unregisterMix(const String8& address) +status_t AudioPolicyMixCollection::unregisterMix(const AudioMix& mix) { - ssize_t index = indexOfKey(address); - if (index < 0) { - ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); - return BAD_VALUE; + for (size_t i = 0; i < size(); i++) { + const sp& registeredMix = itemAt(i); + if (mix.mDeviceType == registeredMix->mDeviceType + && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) { + ALOGD("unregisterMix(): removing mix for dev=0x%x addr=%s", + mix.mDeviceType, mix.mDeviceAddress.string()); + removeAt(i); + return NO_ERROR; + } } - removeItemsAt(index); - return NO_ERROR; + ALOGE("unregisterMix(): mix not registered for dev=0x%x addr=%s", + mix.mDeviceType, mix.mDeviceAddress.string()); + return BAD_VALUE; } -status_t AudioPolicyMixCollection::getAudioPolicyMix(const String8& address, - sp &policyMix) const +status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType, + const String8& address, sp &policyMix) const { - ssize_t index = indexOfKey(address); - if (index < 0) { - ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); - return BAD_VALUE; + + ALOGV("getAudioPolicyMix() for dev=0x%x addr=%s", deviceType, address.string()); + for (ssize_t i = 0; i < size(); i++) { + if (itemAt(i)->mDeviceType == deviceType + && itemAt(i)->mDeviceAddress.compare(address) == 0) { + policyMix = itemAt(i); + ALOGV("getAudioPolicyMix: found mix %zu match (devType=0x%x addr=%s)", + i, deviceType, address.string()); + return NO_ERROR; + } } - policyMix = valueAt(index); - return NO_ERROR; + + ALOGE("getAudioPolicyMix(): mix not registered for dev=0x%x addr=%s", + deviceType, address.string()); + return BAD_VALUE; } void AudioPolicyMixCollection::closeOutput(sp &desc) { for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); + sp policyMix = itemAt(i); if (policyMix->getOutput() == desc) { policyMix->clearOutput(); } @@ -134,7 +153,7 @@ status_t AudioPolicyMixCollection::getOutputForAttr( ALOGV("getOutputForAttr() querying %zu mixes:", size()); primaryDesc = 0; for (size_t i = 0; i < size(); i++) { - sp policyMix = valueAt(i); + sp policyMix = itemAt(i); const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags); if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) { // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with @@ -320,10 +339,10 @@ sp AudioPolicyMixCollection::getDeviceAndMixForOutput( const DeviceVector &availableOutputDevices) { for (size_t i = 0; i < size(); i++) { - if (valueAt(i)->getOutput() == output) { + if (itemAt(i)->getOutput() == output) { // This Desc is involved in a Mix, which has the highest prio - audio_devices_t deviceType = valueAt(i)->mDeviceType; - String8 address = valueAt(i)->mDeviceAddress; + audio_devices_t deviceType = itemAt(i)->mDeviceType; + String8 address = itemAt(i)->mDeviceAddress; ALOGV("%s: device (0x%x, addr=%s) forced by mix", __FUNCTION__, deviceType, address.c_str()); return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT); @@ -338,7 +357,7 @@ sp AudioPolicyMixCollection::getDeviceAndMixForInputSource( sp *policyMix) const { for (size_t i = 0; i < size(); i++) { - AudioPolicyMix *mix = valueAt(i).get(); + AudioPolicyMix *mix = itemAt(i).get(); if (mix->mMixType != MIX_TYPE_RECORDERS) { continue; } @@ -374,19 +393,28 @@ status_t AudioPolicyMixCollection::getInputMixForAttr( String8 address(attr.tags + strlen("addr=")); #ifdef LOG_NDEBUG - ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string()); + ALOGV("getInputMixForAttr looking for address %s for source %d\n mixes available:", + address.string(), attr.source); for (size_t i = 0; i < size(); i++) { - sp audioPolicyMix = valueAt(i); - ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.string()); + const sp audioPolicyMix = itemAt(i); + ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.string()); } #endif - ssize_t index = indexOfKey(address); - if (index < 0) { + size_t index; + for (index = 0; index < size(); index++) { + const sp& registeredMix = itemAt(index); + if (registeredMix->mDeviceAddress.compare(address) == 0) { + ALOGD("getInputMixForAttr found addr=%s dev=0x%x", + registeredMix->mDeviceAddress.string(), registeredMix->mDeviceType); + break; + } + } + if (index == size()) { ALOGW("getInputMixForAttr() no policy for address %s", address.string()); return BAD_VALUE; } - sp audioPolicyMix = valueAt(index); + const sp audioPolicyMix = itemAt(index); if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); @@ -404,7 +432,7 @@ status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, // "match uid" rule for this uid, return an error // (adding a uid-device affinity would result in contradictory rules) for (size_t i = 0; i < size(); i++) { - const AudioPolicyMix* mix = valueAt(i).get(); + const AudioPolicyMix* mix = itemAt(i).get(); if (!mix->isDeviceAffinityCompatible()) { continue; } @@ -421,7 +449,7 @@ status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid, // AND it doesn't have a "match uid" rule // THEN add a rule to exclude the uid for (size_t i = 0; i < size(); i++) { - const AudioPolicyMix *mix = valueAt(i).get(); + const AudioPolicyMix *mix = itemAt(i).get(); if (!mix->isDeviceAffinityCompatible()) { continue; } @@ -452,7 +480,7 @@ status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) { // for each player mix: remove existing rules that match or exclude this uid for (size_t i = 0; i < size(); i++) { bool foundUidRule = false; - const AudioPolicyMix *mix = valueAt(i).get(); + const AudioPolicyMix *mix = itemAt(i).get(); if (!mix->isDeviceAffinityCompatible()) { continue; } @@ -481,7 +509,7 @@ status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid, // for each player mix: find rules that don't exclude this uid, and add the device to the list for (size_t i = 0; i < size(); i++) { bool ruleAllowsUid = true; - const AudioPolicyMix *mix = valueAt(i).get(); + const AudioPolicyMix *mix = itemAt(i).get(); if (mix->mMixType != MIX_TYPE_PLAYERS) { continue; } @@ -504,7 +532,7 @@ void AudioPolicyMixCollection::dump(String8 *dst) const { dst->append("\nAudio Policy Mix:\n"); for (size_t i = 0; i < size(); i++) { - valueAt(i)->dump(dst, 2, i); + itemAt(i)->dump(dst, 2, i); } } diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 9025119b73..3ca759133b 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -2868,13 +2868,16 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) } String8 address = mix.mDeviceAddress; + audio_devices_t deviceTypeToMakeAvailable; if (mix.mMixType == MIX_TYPE_PLAYERS) { - mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX; - } else { mix.mDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + deviceTypeToMakeAvailable = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } else { + mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + deviceTypeToMakeAvailable = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } - if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) { + if (mPolicyMixes.registerMix(mix, 0 /*output desc*/) != NO_ERROR) { ALOGE("Error registering mix %zu for address %s", i, address.string()); res = INVALID_OPERATION; break; @@ -2890,7 +2893,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) rSubmixModule->addInputProfile(address, &inputConfig, AUDIO_DEVICE_IN_REMOTE_SUBMIX, address); - if ((res = setDeviceConnectionStateInt(mix.mDeviceType, + if ((res = setDeviceConnectionStateInt(deviceTypeToMakeAvailable, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, address.string(), "remote-submix", AUDIO_FORMAT_DEFAULT)) != NO_ERROR) { ALOGE("Failed to set remote submix device available, type %u, address %s", @@ -2916,7 +2919,7 @@ status_t AudioPolicyManager::registerPolicyMixes(const Vector& mixes) sp desc = mOutputs.valueAt(j); if (desc->supportedDevices().contains(device)) { - if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) { + if (mPolicyMixes.registerMix(mix, desc) != NO_ERROR) { ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type, address.string()); res = INVALID_OPERATION; @@ -2966,7 +2969,7 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) String8 address = mix.mDeviceAddress; - if (mPolicyMixes.unregisterMix(address) != NO_ERROR) { + if (mPolicyMixes.unregisterMix(mix) != NO_ERROR) { res = INVALID_OPERATION; continue; } @@ -2987,7 +2990,7 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector mixes) rSubmixModule->removeInputProfile(address); } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) { - if (mPolicyMixes.unregisterMix(mix.mDeviceAddress) != NO_ERROR) { + if (mPolicyMixes.unregisterMix(mix) != NO_ERROR) { res = INVALID_OPERATION; continue; } @@ -4653,7 +4656,8 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp& d addOutput(output, desc); if (device_distinguishes_on_address(deviceType) && address != "0") { sp policyMix; - if (mPolicyMixes.getAudioPolicyMix(address, policyMix) == NO_ERROR) { + if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) + == NO_ERROR) { policyMix->setOutput(desc); desc->mPolicyMix = policyMix; } else { -- GitLab From 0f8a1cdd39299aab134a5004c303b5df904f3924 Mon Sep 17 00:00:00 2001 From: Ricardo Garcia Date: Thu, 16 May 2019 16:20:13 -0700 Subject: [PATCH 1464/1530] Splitting analysis window in frequency implementation. Other fixes. Split the analysis/resynthesis window into using sqrt() of Hanning. Removed unnecessary zero padding of initial circular buffer input. Bug: 133453863 Test: manual and listening tests Change-Id: I05a7204d055a8de93bb9183c32709d3464877065 --- media/libeffects/dynamicsproc/dsp/DPFrequency.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp index 1f5397803f..ae5c020693 100644 --- a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp +++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp @@ -62,11 +62,6 @@ void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize cBInput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE); cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE); - //fill input with half block size... - for (unsigned int k = 0; k < mBlockSize/2; k++) { - cBInput.write(0); - } - //temp vectors input.resize(mBlockSize); output.resize(mBlockSize); @@ -170,6 +165,11 @@ void DPFrequency::configure(size_t blockSize, size_t overlapSize, fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize); + //split window into analysis and synthesis. Both are the sqrt() of original + //window + Eigen::Map eWindow(&mVWindow[0], mVWindow.size()); + eWindow = eWindow.array().sqrt(); + //compute window rms for energy compensation mWindowRms = 0; for (size_t i = 0; i < mVWindow.size(); i++) { @@ -666,6 +666,11 @@ size_t DPFrequency::processLastStages(ChannelBuffer &cb) { //##ifft directly to output. Eigen::Map eOutput(&cb.output[0], cb.output.size()); mFftServer.inv(eOutput, cb.complexTemp); + + //apply rest of window for resynthesis + Eigen::Map eWindow(&mVWindow[0], mVWindow.size()); + eOutput = eOutput.cwiseProduct(eWindow); + return mBlockSize; } -- GitLab From 3ac371484399da6efbd0c835aa461d3326263ab1 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 24 May 2019 20:58:09 -0700 Subject: [PATCH 1465/1530] swcodec apex: add a link for libz.so from sphal to platform. On devices where VNDK is not enforced, libz.so is llndk. Test: stagefright -s -S /sdcard/crowd_350x350p50f32_200kbps_meta.webm Bug: 133351160 Change-Id: I8a563f86fe820e49ed6bae4e7e0f4a64bf1bddfa --- apex/ld.config.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apex/ld.config.txt b/apex/ld.config.txt index 87af5a17cf..a5937fd9d8 100644 --- a/apex/ld.config.txt +++ b/apex/ld.config.txt @@ -127,3 +127,5 @@ namespace.sphal.links = platform # namespace.sphal.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES% namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so +# Add a link for libz.so which is llndk on devices where VNDK is not enforced. +namespace.sphal.link.platform.shared_libs += libz.so -- GitLab From aab2eea323292f18a0ea0d20eea174e7271fd77e Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 22 May 2019 10:37:58 -0700 Subject: [PATCH 1466/1530] CCodec: add max B frames support Bug: 127315857 Test: screenrecord --codec-name c2.android.avc.encoder --bframes 1 /sdcard/a.mp4 Change-Id: Id0f6cf618767b3b23e11122f7abee2d5b125d59c --- media/codec2/core/include/C2Config.h | 1 + media/codec2/sfplugin/CCodec.cpp | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index c395d62cfe..9f484a3fa2 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -1644,6 +1644,7 @@ constexpr char C2_PARAMKEY_PICTURE_TYPE[] = "coded.picture-type"; * frames. */ struct C2GopLayerStruct { + C2GopLayerStruct() : type_((C2Config::picture_type_t)0), count(0) {} C2GopLayerStruct(C2Config::picture_type_t type, uint32_t count_) : type_(type), count(count_) { } diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 85c783bec1..0f958c9674 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -766,6 +766,11 @@ void CCodec::configure(const sp &msg) { ALOGD("I frame interval is missing, which is required for video encoders."); return BAD_VALUE; } + if (!msg->findInt32(KEY_FRAME_RATE, &i32) + && !msg->findFloat(KEY_FRAME_RATE, &flt)) { + ALOGD("frame rate is missing, which is required for video encoders."); + return BAD_VALUE; + } } } @@ -849,6 +854,22 @@ void CCodec::configure(const sp &msg) { if (err != OK) { ALOGW("failed to convert configuration to c2 params"); } + + int32_t maxBframes = 0; + if ((config->mDomain & Config::IS_ENCODER) + && (config->mDomain & Config::IS_VIDEO) + && sdkParams->findInt32(KEY_MAX_B_FRAMES, &maxBframes) + && maxBframes > 0) { + std::unique_ptr gop = + C2StreamGopTuning::output::AllocUnique(2 /* flexCount */, 0u /* stream */); + gop->m.values[0] = { P_FRAME, UINT32_MAX }; + gop->m.values[1] = { + C2Config::picture_type_t(P_FRAME | B_FRAME), + uint32_t(maxBframes) + }; + configUpdate.push_back(std::move(gop)); + } + err = config->setParameters(comp, configUpdate, C2_DONT_BLOCK); if (err != OK) { ALOGW("failed to configure c2 params"); -- GitLab From 71cbb9d4e4757033b23a0ba600cba4e279d5c73f Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Tue, 21 May 2019 11:21:27 -0700 Subject: [PATCH 1467/1530] CCodecConfig: Fix mapping to bitrate-mode bitrate-mode was not mapped from c2 params to sdk params Also fix typecast of bitrate mode while checking config params for quality/bitrate. It was wrongly typecast to c2 enum Test: Manual using a test application using AMediaCodec api Bug: 133241887 Change-Id: I2544a7cbb4001152197b9ec164fe94b1a6da40d3 --- media/codec2/sfplugin/CCodec.cpp | 7 ++----- media/codec2/sfplugin/CCodecConfig.cpp | 9 +-------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 85c783bec1..400b02f745 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -745,11 +745,8 @@ void CCodec::configure(const sp &msg) { return BAD_VALUE; } if ((config->mDomain & Config::IS_ENCODER) && (config->mDomain & Config::IS_VIDEO)) { - C2Config::bitrate_mode_t mode = C2Config::BITRATE_VARIABLE; - if (msg->findInt32(KEY_BITRATE_MODE, &i32)) { - mode = (C2Config::bitrate_mode_t) i32; - } - if (mode == BITRATE_MODE_CQ) { + int32_t mode = BITRATE_MODE_VBR; + if (msg->findInt32(KEY_BITRATE_MODE, &mode) && mode == BITRATE_MODE_CQ) { if (!msg->findInt32(KEY_QUALITY, &i32)) { ALOGD("quality is missing, which is required for video encoders in CQ."); return BAD_VALUE; diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index 428f0322b6..4c3fff7ecb 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -358,14 +358,7 @@ void CCodecConfig::initializeStandardParams() { .limitTo(D::ENCODER & D::VIDEO & D::PARAM)); add(ConfigMapper(KEY_BITRATE_MODE, C2_PARAMKEY_BITRATE_MODE, "value") .limitTo(D::ENCODER & D::CODED) - .withMapper([](C2Value v) -> C2Value { - int32_t value; - C2Config::bitrate_mode_t mode; - if (v.get(&value) && C2Mapper::map(value, &mode)) { - return mode; - } - return C2Value(); - })); + .withC2Mappers()); // remove when codecs switch to PARAMKEY and new modes deprecated(ConfigMapper(KEY_BITRATE_MODE, "coded.bitrate-mode", "value") .limitTo(D::ENCODER)); -- GitLab From 4ae31f36f297d7e3d32c7f30119cb2747443d4cd Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 23 May 2019 14:52:41 -0700 Subject: [PATCH 1468/1530] MPEG4Extractor: Overwrite opus version as 1 mp4 Opus Specific box says Version should be 0. OpusHead requires this to 1. MPEG4Extractor now validates that version before overwriting it with 1. Bug: 133448278 Test: Extract an mp4 file with opus track Test: Mux the extracted file to ogg Test: Extract opus from the muxed ogg file Change-Id: Iee31eed69ea906b8f34f75f01a4d2c77b3e32077 --- media/extractors/mp4/MPEG4Extractor.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index f54690d8bd..527bb77acd 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1754,6 +1754,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // http://wiki.xiph.org/OggOpus#ID_Header strncpy((char *)opusInfo, "OpusHead", 8); + // Version shall be 0 as per mp4 Opus Specific Box + // (https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2) + if (opusInfo[8]) { + return ERROR_MALFORMED; + } + // Force version to 1 as per OpusHead definition + // (http://wiki.xiph.org/OggOpus#ID_Header) + opusInfo[8] = 1; + // Read Opus Specific Box values size_t opusOffset = 10; uint16_t pre_skip = U16_AT(&opusInfo[opusOffset]); -- GitLab From 4054c97d5e14427593f40a1214aa285a96f47385 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 28 May 2019 11:28:51 -0700 Subject: [PATCH 1469/1530] Update raw decoder sample and bitrate limits Support up to 384 kHz sampling rate, and update the bitrate limit according, since it wasn't even high enough to begin with. Bug: 133475669 Test: test file Change-Id: I9a3c578f06ae591c2dffa0f5985b769591703b93 --- media/codec2/components/raw/C2SoftRawDec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp index 8e94a186f7..95eb909206 100644 --- a/media/codec2/components/raw/C2SoftRawDec.cpp +++ b/media/codec2/components/raw/C2SoftRawDec.cpp @@ -58,7 +58,7 @@ public: addParameter( DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) .withDefault(new C2StreamSampleRateInfo::output(0u, 44100)) - .withFields({C2F(mSampleRate, value).inRange(8000, 192000)}) + .withFields({C2F(mSampleRate, value).inRange(8000, 384000)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); @@ -72,7 +72,7 @@ public: addParameter( DefineParam(mBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::input(0u, 64000)) - .withFields({C2F(mBitrate, value).inRange(1, 10000000)}) + .withFields({C2F(mBitrate, value).inRange(1, 98304000)}) .withSetter(Setter::NonStrictValueWithNoDeps) .build()); -- GitLab From 1971e2c1f5cafb8c24cc68dae6896f04ed0be9ff Mon Sep 17 00:00:00 2001 From: Pin-chih Lin Date: Mon, 15 Apr 2019 19:36:26 +0800 Subject: [PATCH 1470/1530] Codec2Buffer: add size info to client-based output byte buffer In byte-buffer mode of Codec2.0 ConstGraphicBlockBuffer would be allocated bigger size (width * height * 4). Adding size info as image convertor reported by setRange() is beneficial for app to be able to get actual frame buffer size in byte-buffer mode. Bug: 130398258 Bug: 132140935 Test: manual Test: run CtsMediaTestCases Change-Id: Ieb04d77ac28e88d687fa79fbd8dad2584e83a63b --- media/codec2/sfplugin/Codec2Buffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 0fd5731f42..c6cbad3fba 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -739,6 +739,7 @@ bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed"); return false; } + setRange(0, aBuffer->size()); // align size info converter.copyToMediaImage(); setImageData(converter.imageData()); mBufferRef = buffer; -- GitLab From 602749a2d2ed76e6b98592ebf3dcde244e25dce3 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 24 May 2019 11:49:08 -0700 Subject: [PATCH 1471/1530] C2SoftOpusEnc: Populate skip_samples in OpusHeader skip_samples in opusHeader is now populated to signal codec delay Without this, an ogg file created from this encoders output resulted in codec delay being set to zero when it was played out in Android. Test: Encode raw file to Opus -> Mux to Ogg -> Extract and decode Test: Verify codec delay in decode step Test: atest android.media.cts.EncoderTest#testOpusEncoders Bug: 133506289 Change-Id: I05aeb909abf8b3fa62e709a099f5e544a7aa555c --- .../codec2/components/opus/C2SoftOpusEnc.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index 7b58c9b054..2d110f749a 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -205,15 +205,6 @@ c2_status_t C2SoftOpusEnc::configureEncoder() { return C2_BAD_VALUE; } - // Get codecDelay - int32_t lookahead; - if (opus_multistream_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&lookahead)) != - OPUS_OK) { - ALOGE("failed to get lookahead"); - return C2_BAD_VALUE; - } - mCodecDelay = lookahead * 1000000000ll / mSampleRate; - // Set seek preroll to 80 ms mSeekPreRoll = 80000000; return C2_OK; @@ -406,13 +397,26 @@ void C2SoftOpusEnc::process(const std::unique_ptr& work, if (!mHeaderGenerated) { uint8_t header[AOPUS_UNIFIED_CSD_MAXSIZE]; memset(header, 0, sizeof(header)); + + // Get codecDelay + int32_t lookahead; + if (opus_multistream_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&lookahead)) != + OPUS_OK) { + ALOGE("failed to get lookahead"); + mSignalledError = true; + work->result = C2_CORRUPTED; + return; + } + mCodecDelay = lookahead * 1000000000ll / mSampleRate; + OpusHeader opusHeader; + memset(&opusHeader, 0, sizeof(opusHeader)); opusHeader.channels = mChannelCount; opusHeader.num_streams = mChannelCount; opusHeader.num_coupled = 0; opusHeader.channel_mapping = ((mChannelCount > 8) ? 255 : (mChannelCount > 2)); opusHeader.gain_db = 0; - opusHeader.skip_samples = 0; + opusHeader.skip_samples = lookahead; int headerLen = WriteOpusHeaders(opusHeader, mSampleRate, header, sizeof(header), mCodecDelay, mSeekPreRoll); -- GitLab From 494fb48e6ee46f34d129f2c0c93c585036164f0f Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Tue, 28 May 2019 18:44:19 -0700 Subject: [PATCH 1472/1530] clearkey hidl: mock USABLEINFUTURE Bug: 116738851 Test: android.media.cts.MediaDrmMockTest#testKeyStatusChange Change-Id: Icf91e4009832ff8795675840bda1f4fe06623874 --- drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp index d7dacb8a68..d74bc53d8b 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -401,20 +401,25 @@ Return DrmPlugin::provideKeyResponse( sendExpirationUpdate(sessionId, 100); - std::vector keysStatus; - KeyStatus keyStatus; + std::vector keysStatus; + KeyStatus_V1_2 keyStatus; std::vector keyId1 = { 0xA, 0xB, 0xC }; keyStatus.keyId = keyId1; - keyStatus.type = V1_0::KeyStatusType::USABLE; + keyStatus.type = V1_2::KeyStatusType::USABLE; keysStatus.push_back(keyStatus); std::vector keyId2 = { 0xD, 0xE, 0xF }; keyStatus.keyId = keyId2; - keyStatus.type = V1_0::KeyStatusType::EXPIRED; + keyStatus.type = V1_2::KeyStatusType::EXPIRED; keysStatus.push_back(keyStatus); - sendKeysChange(sessionId, keysStatus, true); + std::vector keyId3 = { 0x0, 0x1, 0x2 }; + keyStatus.keyId = keyId3; + keyStatus.type = V1_2::KeyStatusType::USABLEINFUTURE; + keysStatus.push_back(keyStatus); + + sendKeysChange_1_2(sessionId, keysStatus, true); installSecureStop(sessionId); } else { -- GitLab From 333ab963ed35ee99efa076db100e70a45e6ffff9 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 28 May 2019 20:23:35 -0700 Subject: [PATCH 1473/1530] VolumeShaper: Ensure consistent observable state when muted Prior to this patch, VolumeShaper state for direct and offloaded tracks did not appear to advance for master mute and all tracks did not appear to advance for stream mute. Test: atest android.media.cts.VolumeShaperTest with and without muting Bug: 133354056 Change-Id: I589f4e0837124a185509718f1175e06102a1be96 --- services/audioflinger/Threads.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 1d99b88410..ce408be113 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4918,6 +4918,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // read original volumes with volume control float typeVolume = mStreamTypes[track->streamType()].volume; float v = masterVolume * typeVolume; + // Always fetch volumeshaper volume to ensure state is updated. + const sp proxy = track->mAudioTrackServerProxy; + const float vh = track->getVolumeHandler()->getVolume( + track->mAudioTrackServerProxy->framesReleased()).first; if (track->isPausing() || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) { @@ -4927,7 +4931,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->setPaused(); } } else { - sp proxy = track->mAudioTrackServerProxy; gain_minifloat_packed_t vlr = proxy->getVolumeLR(); vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); @@ -4940,8 +4943,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac ALOGV("Track right volume out of range: %.3g", vrf); vrf = GAIN_FLOAT_UNITY; } - const float vh = track->getVolumeHandler()->getVolume( - track->mAudioTrackServerProxy->framesReleased()).first; // now apply the master volume and stream type volume and shaper volume vlf *= v * vh; vrf *= v * vh; @@ -5021,7 +5022,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac (void *)(uintptr_t)(mChannelMask | mHapticChannelMask)); // limit track sample rate to 2 x output sample rate, which changes at re-configuration uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX; - uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate(); + uint32_t reqSampleRate = proxy->getSampleRate(); if (reqSampleRate == 0) { reqSampleRate = mSampleRate; } else if (reqSampleRate > maxSampleRate) { @@ -5033,7 +5034,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac AudioMixer::SAMPLE_RATE, (void *)(uintptr_t)reqSampleRate); - AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate(); + AudioPlaybackRate playbackRate = proxy->getPlaybackRate(); mAudioMixer->setParameter( trackId, AudioMixer::TIMESTRETCH, @@ -5507,19 +5508,17 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr { float left, right; + // Ensure volumeshaper state always advances even when muted. + const sp proxy = track->mAudioTrackServerProxy; + const auto [shaperVolume, shaperActive] = track->getVolumeHandler()->getVolume( + proxy->framesReleased()); + mVolumeShaperActive = shaperActive; + if (mMasterMute || mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) { left = right = 0; } else { float typeVolume = mStreamTypes[track->streamType()].volume; - float v = mMasterVolume * typeVolume; - sp proxy = track->mAudioTrackServerProxy; - - // Get volumeshaper scaling - std::pair - vh = track->getVolumeHandler()->getVolume( - track->mAudioTrackServerProxy->framesReleased()); - v *= vh.first; - mVolumeShaperActive = vh.second; + const float v = mMasterVolume * typeVolume * shaperVolume; gain_minifloat_packed_t vlr = proxy->getVolumeLR(); left = float_from_gain(gain_minifloat_unpack_left(vlr)); -- GitLab From e1c360a5b80ccf002a0ae60f089831e5fc31244a Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Wed, 22 May 2019 10:38:58 -0700 Subject: [PATCH 1474/1530] avcenc: add GOP support Bug: 127315857 Test: screenrecord --codec-name c2.android.avc.encoder --bframes 1 /sdcard/a.mp4 Change-Id: I3c756d610675644abd71712c2d576f10a451613a --- media/codec2/components/avc/C2SoftAvcEnc.cpp | 92 +++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index 8d9f21ada6..b41c271aea 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -41,6 +41,37 @@ namespace { constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder"; +void ParseGop( + const C2StreamGopTuning::output &gop, + uint32_t *syncInterval, uint32_t *iInterval, uint32_t *maxBframes) { + uint32_t syncInt = 1; + uint32_t iInt = 1; + for (size_t i = 0; i < gop.flexCount(); ++i) { + const C2GopLayerStruct &layer = gop.m.values[i]; + if (layer.count == UINT32_MAX) { + syncInt = 0; + } else if (syncInt <= UINT32_MAX / (layer.count + 1)) { + syncInt *= (layer.count + 1); + } + if ((layer.type_ & I_FRAME) == 0) { + if (layer.count == UINT32_MAX) { + iInt = 0; + } else if (iInt <= UINT32_MAX / (layer.count + 1)) { + iInt *= (layer.count + 1); + } + } + if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && maxBframes) { + *maxBframes = layer.count; + } + } + if (syncInterval) { + *syncInterval = syncInt; + } + if (iInterval) { + *iInterval = iInt; + } +} + } // namespace class C2SoftAvcEnc::IntfImpl : public SimpleInterface::BaseParams { @@ -80,11 +111,20 @@ public: .withSetter(SizeSetter) .build()); + addParameter( + DefineParam(mGop, C2_PARAMKEY_GOP) + .withDefault(C2StreamGopTuning::output::AllocShared( + 0 /* flexCount */, 0u /* stream */)) + .withFields({C2F(mGop, m.values[0].type_).any(), + C2F(mGop, m.values[0].count).any()}) + .withSetter(GopSetter) + .build()); + addParameter( DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY) .withDefault(new C2PortActualDelayTuning::input(DEFAULT_B_FRAMES)) .withFields({C2F(mActualInputDelay, value).inRange(0, MAX_B_FRAMES)}) - .withSetter(Setter::StrictValueWithNoDeps) + .calculatedAs(InputDelaySetter, mGop) .build()); addParameter( @@ -160,6 +200,17 @@ public: .build()); } + static C2R InputDelaySetter( + bool mayBlock, + C2P &me, + const C2P &gop) { + (void)mayBlock; + uint32_t maxBframes = 0; + ParseGop(gop.v, nullptr, nullptr, &maxBframes); + me.set().value = maxBframes; + return C2R::Ok(); + } + static C2R BitrateSetter(bool mayBlock, C2P &me) { (void)mayBlock; C2R res = C2R::Ok(); @@ -273,6 +324,18 @@ public: return res; } + static C2R GopSetter(bool mayBlock, C2P &me) { + (void)mayBlock; + for (size_t i = 0; i < me.v.flexCount(); ++i) { + const C2GopLayerStruct &layer = me.v.m.values[0]; + if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) + && layer.count > MAX_B_FRAMES) { + me.set().m.values[i].count = MAX_B_FRAMES; + } + } + return C2R::Ok(); + } + IV_PROFILE_T getProfile_l() const { switch (mProfileLevel->profile) { case PROFILE_AVC_CONSTRAINED_BASELINE: [[fallthrough]]; @@ -314,6 +377,7 @@ public: ALOGD("Unrecognized level: %x", mProfileLevel->level); return 41; } + uint32_t getSyncFramePeriod_l() const { if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) { return 0; @@ -328,6 +392,7 @@ public: std::shared_ptr getFrameRate_l() const { return mFrameRate; } std::shared_ptr getBitrate_l() const { return mBitrate; } std::shared_ptr getRequestSync_l() const { return mRequestSync; } + std::shared_ptr getGop_l() const { return mGop; } private: std::shared_ptr mUsage; @@ -338,6 +403,7 @@ private: std::shared_ptr mBitrate; std::shared_ptr mProfileLevel; std::shared_ptr mSyncFramePeriod; + std::shared_ptr mGop; }; #define ive_api_function ih264e_api_function @@ -850,6 +916,7 @@ c2_status_t C2SoftAvcEnc::initEncoder() { c2_status_t errType = C2_OK; + std::shared_ptr gop; { IntfImpl::Lock lock = mIntf->lock(); mSize = mIntf->getSize_l(); @@ -859,6 +926,25 @@ c2_status_t C2SoftAvcEnc::initEncoder() { mAVCEncLevel = mIntf->getLevel_l(); mIInterval = mIntf->getSyncFramePeriod_l(); mIDRInterval = mIntf->getSyncFramePeriod_l(); + gop = mIntf->getGop_l(); + } + if (gop && gop->flexCount() > 0) { + uint32_t syncInterval = 1; + uint32_t iInterval = 1; + uint32_t maxBframes = 0; + ParseGop(*gop, &syncInterval, &iInterval, &maxBframes); + if (syncInterval > 0) { + ALOGD("Updating IDR interval from GOP: old %u new %u", mIDRInterval, syncInterval); + mIDRInterval = syncInterval; + } + if (iInterval > 0) { + ALOGD("Updating I interval from GOP: old %u new %u", mIInterval, iInterval); + mIInterval = iInterval; + } + if (mBframes != maxBframes) { + ALOGD("Updating max B frames from GOP: old %u new %u", mBframes, maxBframes); + mBframes = maxBframes; + } } uint32_t width = mSize->width; uint32_t height = mSize->height; @@ -868,8 +954,8 @@ c2_status_t C2SoftAvcEnc::initEncoder() { // TODO mIvVideoColorFormat = IV_YUV_420P; - ALOGD("Params width %d height %d level %d colorFormat %d", width, - height, mAVCEncLevel, mIvVideoColorFormat); + ALOGD("Params width %d height %d level %d colorFormat %d bframes %d", width, + height, mAVCEncLevel, mIvVideoColorFormat, mBframes); /* Getting Number of MemRecords */ { -- GitLab From b09409a0149127261ce65d3abf7157123013007e Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Thu, 23 May 2019 18:57:52 +0530 Subject: [PATCH 1475/1530] C2SoftAacEnc: add support for setting Profile and SBR mode Test: Manually using AMediaCodec based application Test: atest android.media.cts.EncoderTest#testAACEncoders Bug: 133482105 Change-Id: If2e3f0ffde38610ae1ae1b7e860e65bfea6e56b8 --- media/codec2/components/aac/C2SoftAacEnc.cpp | 65 ++++++++++++-------- media/codec2/components/aac/C2SoftAacEnc.h | 3 - 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp index 7efc7f1511..8e3852c110 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.cpp +++ b/media/codec2/components/aac/C2SoftAacEnc.cpp @@ -103,11 +103,24 @@ public: }) .withSetter(ProfileLevelSetter) .build()); + + addParameter( + DefineParam(mSBRMode, C2_PARAMKEY_AAC_SBR_MODE) + .withDefault(new C2StreamAacSbrModeTuning::input(0u, AAC_SBR_AUTO)) + .withFields({C2F(mSBRMode, value).oneOf({ + C2Config::AAC_SBR_OFF, + C2Config::AAC_SBR_SINGLE_RATE, + C2Config::AAC_SBR_DUAL_RATE, + C2Config::AAC_SBR_AUTO })}) + .withSetter(Setter::NonStrictValueWithNoDeps) + .build()); } uint32_t getSampleRate() const { return mSampleRate->value; } uint32_t getChannelCount() const { return mChannelCount->value; } uint32_t getBitrate() const { return mBitrate->value; } + uint32_t getSBRMode() const { return mSBRMode->value; } + uint32_t getProfile() const { return mProfileLevel->profile; } static C2R ProfileLevelSetter(bool mayBlock, C2P &me) { (void)mayBlock; (void)me; // TODO: validate @@ -129,6 +142,7 @@ private: std::shared_ptr mBitrate; std::shared_ptr mInputMaxBufSize; std::shared_ptr mProfileLevel; + std::shared_ptr mSBRMode; }; C2SoftAacEnc::C2SoftAacEnc( @@ -138,9 +152,6 @@ C2SoftAacEnc::C2SoftAacEnc( : SimpleC2Component(std::make_shared>(name, id, intfImpl)), mIntf(intfImpl), mAACEncoder(nullptr), - mSBRMode(-1), - mSBRRatio(0), - mAACProfile(AOT_AAC_LC), mNumBytesPerInputFrame(0u), mOutBufferSize(0u), mSentCodecSpecificData(false), @@ -208,31 +219,37 @@ static CHANNEL_MODE getChannelMode(uint32_t nChannels) { return chMode; } -//static AUDIO_OBJECT_TYPE getAOTFromProfile(OMX_U32 profile) { -// if (profile == OMX_AUDIO_AACObjectLC) { -// return AOT_AAC_LC; -// } else if (profile == OMX_AUDIO_AACObjectHE) { -// return AOT_SBR; -// } else if (profile == OMX_AUDIO_AACObjectHE_PS) { -// return AOT_PS; -// } else if (profile == OMX_AUDIO_AACObjectLD) { -// return AOT_ER_AAC_LD; -// } else if (profile == OMX_AUDIO_AACObjectELD) { -// return AOT_ER_AAC_ELD; -// } else { -// ALOGW("Unsupported AAC profile - defaulting to AAC-LC"); -// return AOT_AAC_LC; -// } -//} +static AUDIO_OBJECT_TYPE getAOTFromProfile(uint32_t profile) { + if (profile == C2Config::PROFILE_AAC_LC) { + return AOT_AAC_LC; + } else if (profile == C2Config::PROFILE_AAC_HE) { + return AOT_SBR; + } else if (profile == C2Config::PROFILE_AAC_HE_PS) { + return AOT_PS; + } else if (profile == C2Config::PROFILE_AAC_LD) { + return AOT_ER_AAC_LD; + } else if (profile == C2Config::PROFILE_AAC_ELD) { + return AOT_ER_AAC_ELD; + } else { + ALOGW("Unsupported AAC profile - defaulting to AAC-LC"); + return AOT_AAC_LC; + } +} status_t C2SoftAacEnc::setAudioParams() { // We call this whenever sample rate, number of channels, bitrate or SBR mode change // in reponse to setParameter calls. + int32_t sbrRatio = 0; + uint32_t sbrMode = mIntf->getSBRMode(); + if (sbrMode == AAC_SBR_SINGLE_RATE) sbrRatio = 1; + else if (sbrMode == AAC_SBR_DUAL_RATE) sbrRatio = 2; ALOGV("setAudioParams: %u Hz, %u channels, %u bps, %i sbr mode, %i sbr ratio", - mIntf->getSampleRate(), mIntf->getChannelCount(), mIntf->getBitrate(), mSBRMode, mSBRRatio); + mIntf->getSampleRate(), mIntf->getChannelCount(), mIntf->getBitrate(), + sbrMode, sbrRatio); - if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, mAACProfile)) { + uint32_t aacProfile = mIntf->getProfile(); + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, getAOTFromProfile(aacProfile))) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } @@ -255,8 +272,8 @@ status_t C2SoftAacEnc::setAudioParams() { return UNKNOWN_ERROR; } - if (mSBRMode != -1 && mAACProfile == AOT_ER_AAC_ELD) { - if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, mSBRMode)) { + if (sbrMode != -1 && aacProfile == C2Config::PROFILE_AAC_ELD) { + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, sbrMode)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } @@ -268,7 +285,7 @@ status_t C2SoftAacEnc::setAudioParams() { 1: Downsampled SBR (default for ELD) 2: Dualrate SBR (default for HE-AAC) */ - if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, mSBRRatio)) { + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, sbrRatio)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } diff --git a/media/codec2/components/aac/C2SoftAacEnc.h b/media/codec2/components/aac/C2SoftAacEnc.h index 779365bf61..a38be190a1 100644 --- a/media/codec2/components/aac/C2SoftAacEnc.h +++ b/media/codec2/components/aac/C2SoftAacEnc.h @@ -50,9 +50,6 @@ private: HANDLE_AACENCODER mAACEncoder; - int32_t mSBRMode; - int32_t mSBRRatio; - AUDIO_OBJECT_TYPE mAACProfile; UINT mNumBytesPerInputFrame; UINT mOutBufferSize; -- GitLab From 0dcd24ddeec724314333036bcb6fbf9502057038 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 14 May 2019 10:53:06 -0700 Subject: [PATCH 1476/1530] Fix overflow/dos in 3gg text description parsing Bug: 124781927 Test: run pocs Change-Id: I8765ac9746c3de7d711ef866d4ec0e29972320c0 Merged-In: I8765ac9746c3de7d711ef866d4ec0e29972320c0 --- media/libstagefright/timedtext/TextDescriptions.cpp | 2 +- media/libstagefright/timedtext/TextDescriptions2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp index 088eaaee49..0dc7722f6f 100644 --- a/media/libstagefright/timedtext/TextDescriptions.cpp +++ b/media/libstagefright/timedtext/TextDescriptions.cpp @@ -383,7 +383,7 @@ status_t TextDescriptions::extract3GPPGlobalDescriptions( tmpData += 8; size_t remaining = size - 8; - if (size < chunkSize) { + if (chunkSize <= 8 || size < chunkSize) { return OK; } switch(chunkType) { diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp index f48eaccbe3..fd42d3ac61 100644 --- a/media/libstagefright/timedtext/TextDescriptions2.cpp +++ b/media/libstagefright/timedtext/TextDescriptions2.cpp @@ -145,7 +145,7 @@ status_t TextDescriptions2::extract3GPPGlobalDescriptions( tmpData += 8; size_t remaining = size - 8; - if (size < chunkSize) { + if (chunkSize <= 8 || size < chunkSize) { return OK; } switch(chunkType) { -- GitLab From a0a5fde902e1cfd8b75c97c34f8c3f424027be4b Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 17 May 2019 11:41:18 -0700 Subject: [PATCH 1477/1530] C2SoftOpusEnc: Fix mono encoding Number of coupled streams was wrongly initialized during codec create which is now fixed based on number of channels Also, limit number of channels supported to 2 Bug: 132798253 Test: atest android.media.cts.EncoderTest#testOpusEncoders Change-Id: Ib0705c805c5518f94765a605a928f002df21cd5f --- media/codec2/components/opus/C2SoftOpusEnc.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index 7b58c9b054..d6517ea827 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -38,6 +38,8 @@ constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder"; } // namespace +static const int kMaxNumChannelsSupported = 2; + class C2SoftOpusEnc::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) @@ -71,7 +73,7 @@ public: addParameter( DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) .withDefault(new C2StreamChannelCountInfo::input(0u, 1)) - .withFields({C2F(mChannelCount, value).inRange(1, 8)}) + .withFields({C2F(mChannelCount, value).inRange(1, kMaxNumChannelsSupported)}) .withSetter((Setter::StrictValueWithNoDeps)) .build()); @@ -128,9 +130,8 @@ c2_status_t C2SoftOpusEnc::onInit() { } c2_status_t C2SoftOpusEnc::configureEncoder() { - unsigned char mono_mapping[256] = {0}; - unsigned char stereo_mapping[256] = {0, 1}; - unsigned char surround_mapping[256] = {0, 1, 255}; + static const unsigned char mono_mapping[256] = {0}; + static const unsigned char stereo_mapping[256] = {0, 1}; mSampleRate = mIntf->getSampleRate(); mChannelCount = mIntf->getChannelCount(); uint32_t bitrate = mIntf->getBitrate(); @@ -140,13 +141,14 @@ c2_status_t C2SoftOpusEnc::configureEncoder() { mChannelCount * mNumSamplesPerFrame * sizeof(int16_t); int err = C2_OK; - unsigned char* mapping; - if (mChannelCount < 2) { + const unsigned char* mapping; + if (mChannelCount == 1) { mapping = mono_mapping; } else if (mChannelCount == 2) { mapping = stereo_mapping; } else { - mapping = surround_mapping; + ALOGE("Number of channels (%d) is not supported", mChannelCount); + return C2_BAD_VALUE; } if (mEncoder != nullptr) { @@ -154,7 +156,7 @@ c2_status_t C2SoftOpusEnc::configureEncoder() { } mEncoder = opus_multistream_encoder_create(mSampleRate, mChannelCount, - 1, 1, mapping, OPUS_APPLICATION_AUDIO, &err); + 1, mChannelCount - 1, mapping, OPUS_APPLICATION_AUDIO, &err); if (err) { ALOGE("Could not create libopus encoder. Error code: %i", err); return C2_CORRUPTED; -- GitLab From f7c49e2779445b6d2d2c1526a27d8009f1bd1851 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 24 May 2019 14:19:16 -0700 Subject: [PATCH 1478/1530] codec2: Fix width and height when output is cropped Return cropped width and height instead of block's width and height Test: cts-tradefed run cts -m CtsMediaBitstreamsTestCases \ -t android.media.cts.bitstreams.H264Yuv420_8bitBpBitstreamsTest Bug: 132042537 Change-Id: I72538248fff4aeb9f0ad51e6e0d8844f66f20f92 --- media/codec2/sfplugin/CCodec.cpp | 2 +- media/codec2/sfplugin/Codec2Buffer.cpp | 4 ++-- media/codec2/sfplugin/utils/Codec2BufferUtils.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index f5a4d9439e..1b6266fe3c 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -1739,7 +1739,7 @@ void CCodec::onMessageReceived(const sp &msg) { // block.width(), block.height()); updates.emplace_back(new C2StreamCropRectInfo::output(stream, block.crop())); updates.emplace_back(new C2StreamPictureSizeInfo::output( - stream, block.width(), block.height())); + stream, block.crop().width, block.crop().height)); break; // for now only do the first block } ++stream; diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index c6cbad3fba..d2b31ee89d 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -391,8 +391,8 @@ public: return; } mediaImage->mNumPlanes = layout.numPlanes; - mediaImage->mWidth = mWidth; - mediaImage->mHeight = mHeight; + mediaImage->mWidth = view.crop().width; + mediaImage->mHeight = view.crop().height; mediaImage->mBitDepth = bitDepth; mediaImage->mBitDepthAllocated = mAllocatedDepth; diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp index 6b8663fedc..1e884ef8e7 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp @@ -118,7 +118,7 @@ static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBa } // namespace status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) { - if (view.width() != img->mWidth || view.height() != img->mHeight) { + if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) { return BAD_VALUE; } if ((IsNV12(view) && IsI420(img)) || (IsI420(view) && IsNV12(img))) { @@ -153,7 +153,7 @@ status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView } status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) { - if (view.width() != img->mWidth || view.height() != img->mHeight) { + if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) { return BAD_VALUE; } if ((IsNV12(img) && IsI420(view)) || (IsI420(img) && IsNV12(view))) { -- GitLab From 8bfa17a39b833f463cfaeea67b85f5de0993e7bc Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 30 May 2019 22:12:30 -0700 Subject: [PATCH 1479/1530] codec2: fix graphic buffer copying in byte buffer mode - Use crop instead of raw width/height from the view. - Reduce memory allocation amount using the fact that we only support YUV420 for raw graphic buffers. - Slightly increase max linear input buffer size. Bug: 132042537 Test: cts-tradefed run cts -m CtsMediaBitstreamsTestCases Change-Id: Idb79c773e51bb99ba931384abdfd68772c719c31 --- media/codec2/sfplugin/CCodecBuffers.h | 2 +- media/codec2/sfplugin/Codec2Buffer.cpp | 29 +++++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h index f5d9fee1aa..bbba6d1512 100644 --- a/media/codec2/sfplugin/CCodecBuffers.h +++ b/media/codec2/sfplugin/CCodecBuffers.h @@ -31,7 +31,7 @@ namespace android { constexpr size_t kLinearBufferSize = 1048576; // This can fit 4K RGBA frame, and most likely client won't need more than this. -constexpr size_t kMaxLinearBufferSize = 3840 * 2160 * 4; +constexpr size_t kMaxLinearBufferSize = 4096 * 2304 * 4; /** * Base class for representation of buffers at one port. diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index d2b31ee89d..c20ca7392d 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -229,8 +229,8 @@ public: uint32_t bitDepth = layout.planes[0].bitDepth; // align width and height to support subsampling cleanly - uint32_t mStride = align(mWidth, 2) * divUp(layout.planes[0].allocatedDepth, 8u); - uint32_t mVStride = align(mHeight, 2); + uint32_t stride = align(view.crop().width, 2) * divUp(layout.planes[0].allocatedDepth, 8u); + uint32_t vStride = align(view.crop().height, 2); switch (layout.type) { case C2PlanarLayout::TYPE_YUV: @@ -295,19 +295,19 @@ public: case COLOR_FormatYUV420PackedPlanar: mediaImage->mPlane[mediaImage->Y].mOffset = 0; mediaImage->mPlane[mediaImage->Y].mColInc = 1; - mediaImage->mPlane[mediaImage->Y].mRowInc = mStride; + mediaImage->mPlane[mediaImage->Y].mRowInc = stride; mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1; mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1; - mediaImage->mPlane[mediaImage->U].mOffset = mStride * mVStride; + mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride; mediaImage->mPlane[mediaImage->U].mColInc = 1; - mediaImage->mPlane[mediaImage->U].mRowInc = mStride / 2; + mediaImage->mPlane[mediaImage->U].mRowInc = stride / 2; mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2; mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2; - mediaImage->mPlane[mediaImage->V].mOffset = mStride * mVStride * 5 / 4; + mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride * 5 / 4; mediaImage->mPlane[mediaImage->V].mColInc = 1; - mediaImage->mPlane[mediaImage->V].mRowInc = mStride / 2; + mediaImage->mPlane[mediaImage->V].mRowInc = stride / 2; mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2; mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2; break; @@ -316,19 +316,19 @@ public: case COLOR_FormatYUV420PackedSemiPlanar: mediaImage->mPlane[mediaImage->Y].mOffset = 0; mediaImage->mPlane[mediaImage->Y].mColInc = 1; - mediaImage->mPlane[mediaImage->Y].mRowInc = mStride; + mediaImage->mPlane[mediaImage->Y].mRowInc = stride; mediaImage->mPlane[mediaImage->Y].mHorizSubsampling = 1; mediaImage->mPlane[mediaImage->Y].mVertSubsampling = 1; - mediaImage->mPlane[mediaImage->U].mOffset = mStride * mVStride; + mediaImage->mPlane[mediaImage->U].mOffset = stride * vStride; mediaImage->mPlane[mediaImage->U].mColInc = 2; - mediaImage->mPlane[mediaImage->U].mRowInc = mStride; + mediaImage->mPlane[mediaImage->U].mRowInc = stride; mediaImage->mPlane[mediaImage->U].mHorizSubsampling = 2; mediaImage->mPlane[mediaImage->U].mVertSubsampling = 2; - mediaImage->mPlane[mediaImage->V].mOffset = mStride * mVStride + 1; + mediaImage->mPlane[mediaImage->V].mOffset = stride * vStride + 1; mediaImage->mPlane[mediaImage->V].mColInc = 2; - mediaImage->mPlane[mediaImage->V].mRowInc = mStride; + mediaImage->mPlane[mediaImage->V].mRowInc = stride; mediaImage->mPlane[mediaImage->V].mHorizSubsampling = 2; mediaImage->mPlane[mediaImage->V].mVertSubsampling = 2; break; @@ -415,7 +415,7 @@ public: mInitCheck = BAD_VALUE; return; } - bufferSize += mStride * mVStride + bufferSize += stride * vStride / plane.rowSampling / plane.colSampling; } @@ -652,7 +652,8 @@ sp ConstGraphicBlockBuffer::AllocateEmpty( ALOGD("format had no width / height"); return nullptr; } - sp aBuffer(alloc(width * height * 4)); + // NOTE: we currently only support YUV420 formats for byte-buffer mode. + sp aBuffer(alloc(align(width, 16) * align(height, 16) * 3 / 2)); return new ConstGraphicBlockBuffer( format, aBuffer, -- GitLab From 5ecf383ef43e8303da5d18c55eba58663832f5bc Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 18 Apr 2019 10:28:58 -0700 Subject: [PATCH 1480/1530] CCodec: handle delay config update Bug: 130223947 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Test: manual test with software codecs changing delay randomly Change-Id: Ia574522a44df22f9638bc4049d7418843a76b57b --- media/codec2/sfplugin/CCodec.cpp | 7 +- media/codec2/sfplugin/CCodecBufferChannel.cpp | 275 +++++++++++++----- media/codec2/sfplugin/CCodecBufferChannel.h | 23 +- media/codec2/sfplugin/CCodecBuffers.cpp | 222 +++++++++----- media/codec2/sfplugin/CCodecBuffers.h | 106 +++++-- media/codec2/sfplugin/Codec2Buffer.h | 11 +- 6 files changed, 465 insertions(+), 179 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index f5a4d9439e..de28820d76 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -1724,8 +1724,11 @@ void CCodec::onMessageReceived(const sp &msg) { & C2FrameData::FLAG_DISCARD_FRAME) == 0) { // copy buffer info to config - std::vector> updates = - std::move(work->worklets.front()->output.configUpdate); + std::vector> updates; + for (const std::unique_ptr ¶m + : work->worklets.front()->output.configUpdate) { + updates.push_back(C2Param::Copy(*param)); + } unsigned stream = 0; for (const std::shared_ptr &buf : work->worklets.front()->output.buffers) { for (const std::shared_ptr &info : buf->info()) { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 7669421ec4..eb20b20aa3 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -210,22 +210,36 @@ bool CCodecBufferChannel::ReorderStash::less( } } +// Input + +CCodecBufferChannel::Input::Input() : extraBuffers("extra") {} + // CCodecBufferChannel CCodecBufferChannel::CCodecBufferChannel( const std::shared_ptr &callback) : mHeapSeqNum(-1), mCCodecCallback(callback), - mNumInputSlots(kSmoothnessFactor), - mNumOutputSlots(kSmoothnessFactor), mDelay(0), mFrameIndex(0u), mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), mInputMetEos(false) { mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth; - Mutexed>::Locked buffers(mInputBuffers); - buffers->reset(new DummyInputBuffers("")); + { + Mutexed::Locked input(mInput); + input->buffers.reset(new DummyInputBuffers("")); + input->extraBuffers.flush(); + input->inputDelay = 0u; + input->pipelineDelay = 0u; + input->numSlots = kSmoothnessFactor; + input->numExtraSlots = 0u; + } + { + Mutexed::Locked output(mOutput); + output->outputDelay = 0u; + output->numSlots = kSmoothnessFactor; + } } CCodecBufferChannel::~CCodecBufferChannel() { @@ -255,7 +269,7 @@ status_t CCodecBufferChannel::signalEndOfInputStream() { return mInputSurface->signalEndOfInputStream(); } -status_t CCodecBufferChannel::queueInputBufferInternal(const sp &buffer) { +status_t CCodecBufferChannel::queueInputBufferInternal(sp buffer) { int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); @@ -287,13 +301,31 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const spinput.ordinal.frameIndex.peeku(); std::vector> queuedBuffers; + sp copy; if (buffer->size() > 0u) { - Mutexed>::Locked buffers(mInputBuffers); + Mutexed::Locked input(mInput); std::shared_ptr c2buffer; - if (!(*buffers)->releaseBuffer(buffer, &c2buffer, false)) { + if (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) { return -ENOENT; } + // TODO: we want to delay copying buffers. + if (input->extraBuffers.numComponentBuffers() < input->numExtraSlots) { + copy = input->buffers->cloneAndReleaseBuffer(buffer); + if (copy != nullptr) { + (void)input->extraBuffers.assignSlot(copy); + if (!input->extraBuffers.releaseSlot(copy, &c2buffer, false)) { + return UNKNOWN_ERROR; + } + bool released = input->buffers->releaseBuffer(buffer, nullptr, true); + ALOGV("[%s] queueInputBuffer: buffer copied; %sreleased", + mName, released ? "" : "not "); + buffer.clear(); + } else { + ALOGW("[%s] queueInputBuffer: failed to copy a buffer; this may cause input " + "buffer starvation on component.", mName); + } + } work->input.buffers.push_back(c2buffer); queuedBuffers.push_back(c2buffer); } else if (eos) { @@ -343,9 +375,15 @@ status_t CCodecBufferChannel::queueInputBufferInternal(const sp>::Locked buffers(mInputBuffers); - bool released = (*buffers)->releaseBuffer(buffer, nullptr, true); - ALOGV("[%s] queueInputBuffer: buffer %sreleased", mName, released ? "" : "not "); + Mutexed::Locked input(mInput); + bool released = false; + if (buffer) { + released = input->buffers->releaseBuffer(buffer, nullptr, true); + } else if (copy) { + released = input->extraBuffers.releaseSlot(copy, nullptr, true); + } + ALOGV("[%s] queueInputBuffer: buffer%s %sreleased", + mName, (buffer == nullptr) ? "(copy)" : "", released ? "" : "not "); } feedInputBufferIfAvailableInternal(); @@ -492,20 +530,21 @@ void CCodecBufferChannel::feedInputBufferIfAvailableInternal() { mPipelineWatcher.lock()->pipelineFull()) { return; } else { - Mutexed>::Locked buffers(mOutputBuffers); - if ((*buffers)->numClientBuffers() >= mNumOutputSlots) { + Mutexed::Locked output(mOutput); + if (output->buffers->numClientBuffers() >= output->numSlots) { return; } } - for (size_t i = 0; i < mNumInputSlots; ++i) { + size_t numInputSlots = mInput.lock()->numSlots; + for (size_t i = 0; i < numInputSlots; ++i) { sp inBuffer; size_t index; { - Mutexed>::Locked buffers(mInputBuffers); - if ((*buffers)->numClientBuffers() >= mNumInputSlots) { + Mutexed::Locked input(mInput); + if (input->buffers->numClientBuffers() >= input->numSlots) { return; } - if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) { + if (!input->buffers->requestNewBuffer(&index, &inBuffer)) { ALOGV("[%s] no new buffer available", mName); break; } @@ -521,9 +560,9 @@ status_t CCodecBufferChannel::renderOutputBuffer( std::shared_ptr c2Buffer; bool released = false; { - Mutexed>::Locked buffers(mOutputBuffers); - if (*buffers) { - released = (*buffers)->releaseBuffer(buffer, &c2Buffer); + Mutexed::Locked output(mOutput); + if (output->buffers) { + released = output->buffers->releaseBuffer(buffer, &c2Buffer); } } // NOTE: some apps try to releaseOutputBuffer() with timestamp and/or render @@ -685,14 +724,14 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) ALOGV("[%s] discardBuffer: %p", mName, buffer.get()); bool released = false; { - Mutexed>::Locked buffers(mInputBuffers); - if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr, true)) { + Mutexed::Locked input(mInput); + if (input->buffers && input->buffers->releaseBuffer(buffer, nullptr, true)) { released = true; } } { - Mutexed>::Locked buffers(mOutputBuffers); - if (*buffers && (*buffers)->releaseBuffer(buffer, nullptr)) { + Mutexed::Locked output(mOutput); + if (output->buffers && output->buffers->releaseBuffer(buffer, nullptr)) { released = true; } } @@ -707,24 +746,24 @@ status_t CCodecBufferChannel::discardBuffer(const sp &buffer) void CCodecBufferChannel::getInputBufferArray(Vector> *array) { array->clear(); - Mutexed>::Locked buffers(mInputBuffers); + Mutexed::Locked input(mInput); - if (!(*buffers)->isArrayMode()) { - *buffers = (*buffers)->toArrayMode(mNumInputSlots); + if (!input->buffers->isArrayMode()) { + input->buffers = input->buffers->toArrayMode(input->numSlots); } - (*buffers)->getArray(array); + input->buffers->getArray(array); } void CCodecBufferChannel::getOutputBufferArray(Vector> *array) { array->clear(); - Mutexed>::Locked buffers(mOutputBuffers); + Mutexed::Locked output(mOutput); - if (!(*buffers)->isArrayMode()) { - *buffers = (*buffers)->toArrayMode(mNumOutputSlots); + if (!output->buffers->isArrayMode()) { + output->buffers = output->buffers->toArrayMode(output->numSlots); } - (*buffers)->getArray(array); + output->buffers->getArray(array); } status_t CCodecBufferChannel::start( @@ -773,8 +812,8 @@ status_t CCodecBufferChannel::start( uint32_t pipelineDelayValue = pipelineDelay ? pipelineDelay.value : 0; uint32_t outputDelayValue = outputDelay ? outputDelay.value : 0; - mNumInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor; - mNumOutputSlots = outputDelayValue + kSmoothnessFactor; + size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor; + size_t numOutputSlots = outputDelayValue + kSmoothnessFactor; mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue; // TODO: get this from input format @@ -848,14 +887,17 @@ status_t CCodecBufferChannel::start( } bool forceArrayMode = false; - Mutexed>::Locked buffers(mInputBuffers); + Mutexed::Locked input(mInput); + input->numSlots = numInputSlots; + input->extraBuffers.flush(); + input->numExtraSlots = 0u; if (graphic) { if (mInputSurface) { - buffers->reset(new DummyInputBuffers(mName)); + input->buffers.reset(new DummyInputBuffers(mName)); } else if (mMetaMode == MODE_ANW) { - buffers->reset(new GraphicMetadataInputBuffers(mName)); + input->buffers.reset(new GraphicMetadataInputBuffers(mName)); } else { - buffers->reset(new GraphicInputBuffers(mNumInputSlots, mName)); + input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName)); } } else { if (hasCryptoOrDescrambler()) { @@ -868,7 +910,7 @@ status_t CCodecBufferChannel::start( if (mDealer == nullptr) { mDealer = new MemoryDealer( align(capacity, MemoryDealer::getAllocationAlignment()) - * (mNumInputSlots + 1), + * (numInputSlots + 1), "EncryptedLinearInputBuffers"); mDecryptDestination = mDealer->allocate((size_t)capacity); } @@ -877,24 +919,24 @@ status_t CCodecBufferChannel::start( } else { mHeapSeqNum = -1; } - buffers->reset(new EncryptedLinearInputBuffers( + input->buffers.reset(new EncryptedLinearInputBuffers( secure, mDealer, mCrypto, mHeapSeqNum, (size_t)capacity, - mNumInputSlots, mName)); + numInputSlots, mName)); forceArrayMode = true; } else { - buffers->reset(new LinearInputBuffers(mName)); + input->buffers.reset(new LinearInputBuffers(mName)); } } - (*buffers)->setFormat(inputFormat); + input->buffers->setFormat(inputFormat); if (err == C2_OK) { - (*buffers)->setPool(pool); + input->buffers->setPool(pool); } else { // TODO: error } if (forceArrayMode) { - *buffers = (*buffers)->toArrayMode(mNumInputSlots); + input->buffers = input->buffers->toArrayMode(numInputSlots); } } @@ -903,7 +945,7 @@ status_t CCodecBufferChannel::start( uint32_t outputGeneration; { Mutexed::Locked output(mOutputSurface); - output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth; + output->maxDequeueBuffers = numOutputSlots + reorderDepth.value + kRenderingDepth; outputSurface = output->surface ? output->surface->getIGraphicBufferProducer() : nullptr; if (outputSurface) { @@ -1011,18 +1053,18 @@ status_t CCodecBufferChannel::start( outputPoolId_ = pools->outputPoolId; } - Mutexed>::Locked buffers(mOutputBuffers); - + Mutexed::Locked output(mOutput); + output->numSlots = numOutputSlots; if (graphic) { if (outputSurface) { - buffers->reset(new GraphicOutputBuffers(mName)); + output->buffers.reset(new GraphicOutputBuffers(mName)); } else { - buffers->reset(new RawGraphicOutputBuffers(mNumOutputSlots, mName)); + output->buffers.reset(new RawGraphicOutputBuffers(numOutputSlots, mName)); } } else { - buffers->reset(new LinearOutputBuffers(mName)); + output->buffers.reset(new LinearOutputBuffers(mName)); } - (*buffers)->setFormat(outputFormat->dup()); + output->buffers->setFormat(outputFormat->dup()); // Try to set output surface to created block pool if given. @@ -1038,7 +1080,7 @@ status_t CCodecBufferChannel::start( // WORKAROUND: if we're using early CSD workaround we convert to // array mode, to appease apps assuming the output // buffers to be of the same size. - (*buffers) = (*buffers)->toArrayMode(mNumOutputSlots); + output->buffers = output->buffers->toArrayMode(numOutputSlots); int32_t channelCount; int32_t sampleRate; @@ -1055,7 +1097,7 @@ status_t CCodecBufferChannel::start( if (delay || padding) { // We need write access to the buffers, and we're already in // array mode. - (*buffers)->initSkipCutBuffer(delay, padding, sampleRate, channelCount); + output->buffers->initSkipCutBuffer(delay, padding, sampleRate, channelCount); } } } @@ -1090,14 +1132,14 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { if (err != C2_OK) { return UNKNOWN_ERROR; } + size_t numInputSlots = mInput.lock()->numSlots; std::vector> toBeQueued; - // TODO: use proper buffer depth instead of this random value - for (size_t i = 0; i < mNumInputSlots; ++i) { + for (size_t i = 0; i < numInputSlots; ++i) { size_t index; sp buffer; { - Mutexed>::Locked buffers(mInputBuffers); - if (!(*buffers)->requestNewBuffer(&index, &buffer)) { + Mutexed::Locked input(mInput); + if (!input->buffers->requestNewBuffer(&index, &buffer)) { if (i == 0) { ALOGW("[%s] start: cannot allocate memory at all", mName); return NO_MEMORY; @@ -1182,12 +1224,13 @@ void CCodecBufferChannel::flush(const std::list> &flushe } } { - Mutexed>::Locked buffers(mInputBuffers); - (*buffers)->flush(); + Mutexed::Locked input(mInput); + input->buffers->flush(); + input->extraBuffers.flush(); } { - Mutexed>::Locked buffers(mOutputBuffers); - (*buffers)->flush(flushedWork); + Mutexed::Locked output(mOutput); + output->buffers->flush(flushedWork); } mReorderStash.lock()->flush(); mPipelineWatcher.lock()->flush(); @@ -1210,8 +1253,11 @@ void CCodecBufferChannel::onInputBufferDone( mPipelineWatcher.lock()->onInputBufferReleased(frameIndex, arrayIndex); bool newInputSlotAvailable; { - Mutexed>::Locked buffers(mInputBuffers); - newInputSlotAvailable = (*buffers)->expireComponentBuffer(buffer); + Mutexed::Locked input(mInput); + newInputSlotAvailable = input->buffers->expireComponentBuffer(buffer); + if (!newInputSlotAvailable) { + (void)input->extraBuffers.expireComponentBuffer(buffer); + } } if (newInputSlotAvailable) { feedInputBufferIfAvailable(); @@ -1269,6 +1315,7 @@ bool CCodecBufferChannel::handleWork( } } + std::optional newInputDelay, newPipelineDelay; while (!worklet->output.configUpdate.empty()) { std::unique_ptr param; worklet->output.configUpdate.back().swap(param); @@ -1280,8 +1327,10 @@ bool CCodecBufferChannel::handleWork( mReorderStash.lock()->setDepth(reorderDepth.value); ALOGV("[%s] onWorkDone: updated reorder depth to %u", mName, reorderDepth.value); + size_t numOutputSlots = mOutput.lock()->numSlots; Mutexed::Locked output(mOutputSurface); - output->maxDequeueBuffers = mNumOutputSlots + reorderDepth.value + kRenderingDepth; + output->maxDequeueBuffers = + numOutputSlots + reorderDepth.value + kRenderingDepth; if (output->surface) { output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); } @@ -1301,18 +1350,86 @@ bool CCodecBufferChannel::handleWork( } break; } + case C2PortActualDelayTuning::CORE_INDEX: { + if (param->isGlobal()) { + C2ActualPipelineDelayTuning pipelineDelay; + if (pipelineDelay.updateFrom(*param)) { + ALOGV("[%s] onWorkDone: updating pipeline delay %u", + mName, pipelineDelay.value); + newPipelineDelay = pipelineDelay.value; + (void)mPipelineWatcher.lock()->pipelineDelay(pipelineDelay.value); + } + } + if (param->forInput()) { + C2PortActualDelayTuning::input inputDelay; + if (inputDelay.updateFrom(*param)) { + ALOGV("[%s] onWorkDone: updating input delay %u", + mName, inputDelay.value); + newInputDelay = inputDelay.value; + (void)mPipelineWatcher.lock()->inputDelay(inputDelay.value); + } + } + if (param->forOutput()) { + C2PortActualDelayTuning::output outputDelay; + if (outputDelay.updateFrom(*param)) { + ALOGV("[%s] onWorkDone: updating output delay %u", + mName, outputDelay.value); + (void)mPipelineWatcher.lock()->outputDelay(outputDelay.value); + + bool outputBuffersChanged = false; + Mutexed::Locked output(mOutput); + output->outputDelay = outputDelay.value; + size_t numOutputSlots = outputDelay.value + kSmoothnessFactor; + if (output->numSlots < numOutputSlots) { + output->numSlots = numOutputSlots; + if (output->buffers->isArrayMode()) { + OutputBuffersArray *array = + (OutputBuffersArray *)output->buffers.get(); + ALOGV("[%s] onWorkDone: growing output buffer array to %zu", + mName, numOutputSlots); + array->grow(numOutputSlots); + outputBuffersChanged = true; + } + } + output.unlock(); + + if (outputBuffersChanged) { + mCCodecCallback->onOutputBuffersChanged(); + } + } + } + break; + } default: ALOGV("[%s] onWorkDone: unrecognized config update (%08X)", mName, param->index()); break; } } + if (newInputDelay || newPipelineDelay) { + Mutexed::Locked input(mInput); + size_t newNumSlots = + newInputDelay.value_or(input->inputDelay) + + newPipelineDelay.value_or(input->pipelineDelay) + + kSmoothnessFactor; + if (input->buffers->isArrayMode()) { + if (input->numSlots >= newNumSlots) { + input->numExtraSlots = 0; + } else { + input->numExtraSlots = newNumSlots - input->numSlots; + } + ALOGV("[%s] onWorkDone: updated number of extra slots to %zu (input array mode)", + mName, input->numExtraSlots); + } else { + input->numSlots = newNumSlots; + } + } if (outputFormat != nullptr) { - Mutexed>::Locked buffers(mOutputBuffers); + Mutexed::Locked output(mOutput); ALOGD("[%s] onWorkDone: output format changed to %s", mName, outputFormat->debugString().c_str()); - (*buffers)->setFormat(outputFormat); + output->buffers->setFormat(outputFormat); AString mediaType; if (outputFormat->findString(KEY_MIME, &mediaType) @@ -1321,7 +1438,7 @@ bool CCodecBufferChannel::handleWork( int32_t sampleRate; if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount) && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) { - (*buffers)->updateSkipCutBuffer(sampleRate, channelCount); + output->buffers->updateSkipCutBuffer(sampleRate, channelCount); } } } @@ -1356,20 +1473,18 @@ bool CCodecBufferChannel::handleWork( timestamp.peekll()); if (initData != nullptr) { - Mutexed>::Locked buffers(mOutputBuffers); - if ((*buffers)->registerCsd(initData, &index, &outBuffer) == OK) { + Mutexed::Locked output(mOutput); + if (output->buffers->registerCsd(initData, &index, &outBuffer) == OK) { outBuffer->meta()->setInt64("timeUs", timestamp.peek()); outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG); ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get()); - buffers.unlock(); + output.unlock(); mCallback->onOutputBufferAvailable(index, outBuffer); - buffers.lock(); } else { ALOGD("[%s] onWorkDone: unable to register csd", mName); - buffers.unlock(); + output.unlock(); mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); - buffers.lock(); return false; } } @@ -1421,22 +1536,22 @@ void CCodecBufferChannel::sendOutputBuffers() { break; } - Mutexed>::Locked buffers(mOutputBuffers); - status_t err = (*buffers)->registerBuffer(entry.buffer, &index, &outBuffer); + Mutexed::Locked output(mOutput); + status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer); if (err != OK) { bool outputBuffersChanged = false; if (err != WOULD_BLOCK) { - if (!(*buffers)->isArrayMode()) { - *buffers = (*buffers)->toArrayMode(mNumOutputSlots); + if (!output->buffers->isArrayMode()) { + output->buffers = output->buffers->toArrayMode(output->numSlots); } - OutputBuffersArray *array = (OutputBuffersArray *)buffers->get(); + OutputBuffersArray *array = (OutputBuffersArray *)output->buffers.get(); array->realloc(entry.buffer); outputBuffersChanged = true; } ALOGV("[%s] sendOutputBuffers: unable to register output buffer", mName); reorder->defer(entry); - buffers.unlock(); + output.unlock(); reorder.unlock(); if (outputBuffersChanged) { @@ -1444,7 +1559,7 @@ void CCodecBufferChannel::sendOutputBuffers() { } return; } - buffers.unlock(); + output.unlock(); reorder.unlock(); outBuffer->meta()->setInt64("timeUs", entry.timestamp); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index 9aec82da3d..ae5767888a 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -209,7 +209,7 @@ private: void feedInputBufferIfAvailable(); void feedInputBufferIfAvailableInternal(); - status_t queueInputBufferInternal(const sp &buffer); + status_t queueInputBufferInternal(sp buffer); bool handleWork( std::unique_ptr work, const sp &outputFormat, const C2StreamInitDataInfo::output *initData); @@ -228,13 +228,26 @@ private: QueueSync mQueueSync; std::vector> mParamsToBeSet; - size_t mNumInputSlots; - size_t mNumOutputSlots; size_t mDelay; - Mutexed> mInputBuffers; + struct Input { + Input(); + + std::unique_ptr buffers; + size_t numSlots; + FlexBuffersImpl extraBuffers; + size_t numExtraSlots; + uint32_t inputDelay; + uint32_t pipelineDelay; + }; + Mutexed mInput; + struct Output { + std::unique_ptr buffers; + size_t numSlots; + uint32_t outputDelay; + }; + Mutexed mOutput; Mutexed>> mFlushedConfigs; - Mutexed> mOutputBuffers; std::atomic_uint64_t mFrameIndex; std::atomic_uint64_t mFirstValidFrameIndex; diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp index fb0efce45d..5ebd5bd317 100644 --- a/media/codec2/sfplugin/CCodecBuffers.cpp +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -98,6 +98,26 @@ void CCodecBuffers::handleImageData(const sp &buffer) { } } +// InputBuffers + +sp InputBuffers::cloneAndReleaseBuffer(const sp &buffer) { + sp copy = createNewBuffer(); + if (copy == nullptr) { + return nullptr; + } + std::shared_ptr c2buffer; + if (!releaseBuffer(buffer, &c2buffer, true)) { + return nullptr; + } + if (!copy->canCopy(c2buffer)) { + return nullptr; + } + if (!copy->copy(c2buffer)) { + return nullptr; + } + return copy; +} + // OutputBuffers void OutputBuffers::initSkipCutBuffer( @@ -197,6 +217,8 @@ void LocalBufferPool::returnVector(std::vector &&vec) { mPool.push_front(std::move(vec)); } +// FlexBuffersImpl + size_t FlexBuffersImpl::assignSlot(const sp &buffer) { for (size_t i = 0; i < mBuffers.size(); ++i) { if (mBuffers[i].clientBuffer == nullptr @@ -209,8 +231,6 @@ size_t FlexBuffersImpl::assignSlot(const sp &buffer) { return mBuffers.size() - 1; } -// FlexBuffersImpl - bool FlexBuffersImpl::releaseSlot( const sp &buffer, std::shared_ptr *c2buffer, @@ -270,6 +290,14 @@ size_t FlexBuffersImpl::numClientBuffers() const { }); } +size_t FlexBuffersImpl::numComponentBuffers() const { + return std::count_if( + mBuffers.begin(), mBuffers.end(), + [](const Entry &entry) { + return !entry.compBuffer.expired(); + }); +} + // BuffersArrayImpl void BuffersArrayImpl::initialize( @@ -395,6 +423,14 @@ void BuffersArrayImpl::realloc(std::function()> alloc) { } } +void BuffersArrayImpl::grow( + size_t newSize, std::function()> alloc) { + CHECK_LT(mBuffers.size(), newSize); + while (mBuffers.size() < newSize) { + mBuffers.push_back({ alloc(), std::weak_ptr(), false }); + } +} + size_t BuffersArrayImpl::numClientBuffers() const { return std::count_if( mBuffers.begin(), mBuffers.end(), @@ -409,6 +445,7 @@ void InputBuffersArray::initialize( const FlexBuffersImpl &impl, size_t minSize, std::function()> allocate) { + mAllocate = allocate; mImpl.initialize(impl, minSize, allocate); } @@ -448,18 +485,14 @@ size_t InputBuffersArray::numClientBuffers() const { return mImpl.numClientBuffers(); } +sp InputBuffersArray::createNewBuffer() { + return mAllocate(); +} + // LinearInputBuffers bool LinearInputBuffers::requestNewBuffer(size_t *index, sp *buffer) { - int32_t capacity = kLinearBufferSize; - (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); - if ((size_t)capacity > kMaxLinearBufferSize) { - ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); - capacity = kMaxLinearBufferSize; - } - // TODO: proper max input size - // TODO: read usage from intf - sp newBuffer = alloc((size_t)capacity); + sp newBuffer = createNewBuffer(); if (newBuffer == nullptr) { return false; } @@ -486,16 +519,7 @@ void LinearInputBuffers::flush() { mImpl.flush(); } -std::unique_ptr LinearInputBuffers::toArrayMode( - size_t size) { - int32_t capacity = kLinearBufferSize; - (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity); - if ((size_t)capacity > kMaxLinearBufferSize) { - ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); - capacity = kMaxLinearBufferSize; - } - // TODO: proper max input size - // TODO: read usage from intf +std::unique_ptr LinearInputBuffers::toArrayMode(size_t size) { std::unique_ptr array( new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]")); array->setPool(mPool); @@ -503,7 +527,9 @@ std::unique_ptr LinearInputBuffers::toArrayMode( array->initialize( mImpl, size, - [this, capacity] () -> sp { return alloc(capacity); }); + [pool = mPool, format = mFormat] () -> sp { + return Alloc(pool, format); + }); return std::move(array); } @@ -511,16 +537,30 @@ size_t LinearInputBuffers::numClientBuffers() const { return mImpl.numClientBuffers(); } -sp LinearInputBuffers::alloc(size_t size) { +// static +sp LinearInputBuffers::Alloc( + const std::shared_ptr &pool, const sp &format) { + int32_t capacity = kLinearBufferSize; + (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity); + if ((size_t)capacity > kMaxLinearBufferSize) { + ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); + capacity = kMaxLinearBufferSize; + } + + // TODO: read usage from intf C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; std::shared_ptr block; - c2_status_t err = mPool->fetchLinearBlock(size, usage, &block); + c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block); if (err != C2_OK) { return nullptr; } - return LinearBlockBuffer::Allocate(mFormat, block); + return LinearBlockBuffer::Allocate(format, block); +} + +sp LinearInputBuffers::createNewBuffer() { + return Alloc(mPool, mFormat); } // EncryptedLinearInputBuffers @@ -537,7 +577,7 @@ EncryptedLinearInputBuffers::EncryptedLinearInputBuffers( mUsage({0, 0}), mDealer(dealer), mCrypto(crypto), - mHeapSeqNum(heapSeqNum) { + mMemoryVector(new std::vector){ if (secure) { mUsage = { C2MemoryUsage::READ_PROTECTED, 0 }; } else { @@ -550,16 +590,48 @@ EncryptedLinearInputBuffers::EncryptedLinearInputBuffers( mName, i); break; } - mMemoryVector.push_back({std::weak_ptr(), memory}); + mMemoryVector->push_back({std::weak_ptr(), memory, heapSeqNum}); } } -sp EncryptedLinearInputBuffers::alloc(size_t size) { +std::unique_ptr EncryptedLinearInputBuffers::toArrayMode(size_t size) { + std::unique_ptr array( + new InputBuffersArray(mComponentName.c_str(), "1D-EncryptedInput[N]")); + array->setPool(mPool); + array->setFormat(mFormat); + array->initialize( + mImpl, + size, + [pool = mPool, + format = mFormat, + usage = mUsage, + memoryVector = mMemoryVector] () -> sp { + return Alloc(pool, format, usage, memoryVector); + }); + return std::move(array); +} + + +// static +sp EncryptedLinearInputBuffers::Alloc( + const std::shared_ptr &pool, + const sp &format, + C2MemoryUsage usage, + const std::shared_ptr> &memoryVector) { + int32_t capacity = kLinearBufferSize; + (void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity); + if ((size_t)capacity > kMaxLinearBufferSize) { + ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize); + capacity = kMaxLinearBufferSize; + } + sp memory; size_t slot = 0; - for (; slot < mMemoryVector.size(); ++slot) { - if (mMemoryVector[slot].block.expired()) { - memory = mMemoryVector[slot].memory; + int32_t heapSeqNum = -1; + for (; slot < memoryVector->size(); ++slot) { + if (memoryVector->at(slot).block.expired()) { + memory = memoryVector->at(slot).memory; + heapSeqNum = memoryVector->at(slot).heapSeqNum; break; } } @@ -568,13 +640,18 @@ sp EncryptedLinearInputBuffers::alloc(size_t size) { } std::shared_ptr block; - c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block); + c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block); if (err != C2_OK || block == nullptr) { return nullptr; } - mMemoryVector[slot].block = block; - return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum); + memoryVector->at(slot).block = block; + return new EncryptedLinearBlockBuffer(format, block, memory, heapSeqNum); +} + +sp EncryptedLinearInputBuffers::createNewBuffer() { + // TODO: android_2020 + return nullptr; } // GraphicMetadataInputBuffers @@ -587,12 +664,7 @@ GraphicMetadataInputBuffers::GraphicMetadataInputBuffers( bool GraphicMetadataInputBuffers::requestNewBuffer( size_t *index, sp *buffer) { - std::shared_ptr alloc; - c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); - if (err != C2_OK) { - return false; - } - sp newBuffer = new GraphicMetadataBuffer(mFormat, alloc); + sp newBuffer = createNewBuffer(); if (newBuffer == nullptr) { return false; } @@ -642,6 +714,15 @@ size_t GraphicMetadataInputBuffers::numClientBuffers() const { return mImpl.numClientBuffers(); } +sp GraphicMetadataInputBuffers::createNewBuffer() { + std::shared_ptr alloc; + c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc); + if (err != C2_OK) { + return nullptr; + } + return new GraphicMetadataBuffer(mFormat, alloc); +} + // GraphicInputBuffers GraphicInputBuffers::GraphicInputBuffers( @@ -652,11 +733,7 @@ GraphicInputBuffers::GraphicInputBuffers( kMaxLinearBufferSize * numInputSlots)) { } bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp *buffer) { - // TODO: proper max input size - // TODO: read usage from intf - C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - sp newBuffer = AllocateGraphicBuffer( - mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool); + sp newBuffer = createNewBuffer(); if (newBuffer == nullptr) { return false; } @@ -703,12 +780,20 @@ size_t GraphicInputBuffers::numClientBuffers() const { return mImpl.numClientBuffers(); } +sp GraphicInputBuffers::createNewBuffer() { + // TODO: read usage from intf + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + return AllocateGraphicBuffer( + mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool); +} + // OutputBuffersArray void OutputBuffersArray::initialize( const FlexBuffersImpl &impl, size_t minSize, std::function()> allocate) { + mAlloc = allocate; mImpl.initialize(impl, minSize, allocate); } @@ -781,8 +866,11 @@ void OutputBuffersArray::getArray(Vector> *array) const { mImpl.getArray(array); } +size_t OutputBuffersArray::numClientBuffers() const { + return mImpl.numClientBuffers(); +} + void OutputBuffersArray::realloc(const std::shared_ptr &c2buffer) { - std::function()> alloc; switch (c2buffer->data().type()) { case C2BufferData::LINEAR: { uint32_t size = kLinearBufferSize; @@ -792,7 +880,7 @@ void OutputBuffersArray::realloc(const std::shared_ptr &c2buffer) { } else { size = kMaxLinearBufferSize; } - alloc = [format = mFormat, size] { + mAlloc = [format = mFormat, size] { return new LocalLinearBuffer(format, new ABuffer(size)); }; break; @@ -808,11 +896,11 @@ void OutputBuffersArray::realloc(const std::shared_ptr &c2buffer) { ALOGD("Unsupported type: %d", (int)c2buffer->data().type()); return; } - mImpl.realloc(alloc); + mImpl.realloc(mAlloc); } -size_t OutputBuffersArray::numClientBuffers() const { - return mImpl.numClientBuffers(); +void OutputBuffersArray::grow(size_t newSize) { + mImpl.grow(newSize, mAlloc); } // FlexOutputBuffers @@ -861,10 +949,8 @@ std::unique_ptr FlexOutputBuffers::toArrayMode(size_t size) { std::unique_ptr array(new OutputBuffersArray(mComponentName.c_str())); array->setFormat(mFormat); array->transferSkipCutBuffer(mSkipCutBuffer); - array->initialize( - mImpl, - size, - [this]() { return allocateArrayBuffer(); }); + std::function()> alloc = getAlloc(); + array->initialize(mImpl, size, alloc); return std::move(array); } @@ -906,9 +992,11 @@ sp LinearOutputBuffers::wrap(const std::shared_ptr &buff return clientBuffer; } -sp LinearOutputBuffers::allocateArrayBuffer() { - // TODO: proper max output size - return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize)); +std::function()> LinearOutputBuffers::getAlloc() { + return [format = mFormat]{ + // TODO: proper max output size + return new LocalLinearBuffer(format, new ABuffer(kLinearBufferSize)); + }; } // GraphicOutputBuffers @@ -917,8 +1005,10 @@ sp GraphicOutputBuffers::wrap(const std::shared_ptr &buf return new DummyContainerBuffer(mFormat, buffer); } -sp GraphicOutputBuffers::allocateArrayBuffer() { - return new DummyContainerBuffer(mFormat); +std::function()> GraphicOutputBuffers::getAlloc() { + return [format = mFormat]{ + return new DummyContainerBuffer(format); + }; } // RawGraphicOutputBuffers @@ -952,12 +1042,14 @@ sp RawGraphicOutputBuffers::wrap(const std::shared_ptr & } } -sp RawGraphicOutputBuffers::allocateArrayBuffer() { - return ConstGraphicBlockBuffer::AllocateEmpty( - mFormat, - [lbp = mLocalBufferPool](size_t capacity) { - return lbp->newBuffer(capacity); - }); +std::function()> RawGraphicOutputBuffers::getAlloc() { + return [format = mFormat, lbp = mLocalBufferPool]{ + return ConstGraphicBlockBuffer::AllocateEmpty( + format, + [lbp](size_t capacity) { + return lbp->newBuffer(capacity); + }); + }; } } // namespace android diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h index f5d9fee1aa..461fbd5a94 100644 --- a/media/codec2/sfplugin/CCodecBuffers.h +++ b/media/codec2/sfplugin/CCodecBuffers.h @@ -134,7 +134,18 @@ public: */ virtual std::unique_ptr toArrayMode(size_t size) = 0; + /** + * Release the buffer obtained from requestNewBuffer(), and create a deep + * copy clone of the buffer. + * + * \return the deep copy clone of the buffer; nullptr if cloning is not + * possible. + */ + sp cloneAndReleaseBuffer(const sp &buffer); + protected: + virtual sp createNewBuffer() = 0; + // Pool to obtain blocks for input buffers. std::shared_ptr mPool; @@ -346,6 +357,12 @@ public: */ size_t numClientBuffers() const; + /** + * Return the number of buffers that are sent to the component but not + * returned back yet. + */ + size_t numComponentBuffers() const; + private: friend class BuffersArrayImpl; @@ -445,6 +462,16 @@ public: */ void realloc(std::function()> alloc); + /** + * Grow the array to the new size. It is a programming error to supply + * smaller size as the new size. + * + * \param newSize[in] new size of the array. + * \param alloc[in] the alllocation function for client buffers to fill + * the new empty slots. + */ + void grow(size_t newSize, std::function()> alloc); + /** * Return the number of buffers that are sent to the client but not released * yet. @@ -506,8 +533,12 @@ public: size_t numClientBuffers() const final; +protected: + sp createNewBuffer() override; + private: BuffersArrayImpl mImpl; + std::function()> mAllocate; }; class LinearInputBuffers : public InputBuffers { @@ -529,18 +560,18 @@ public: void flush() override; - std::unique_ptr toArrayMode(size_t size) final; + std::unique_ptr toArrayMode(size_t size) override; size_t numClientBuffers() const final; - /** - * Allocate a client buffer with the given size. This method may be - * overridden to support different kind of linear buffers (e.g. encrypted). - */ - virtual sp alloc(size_t size); +protected: + sp createNewBuffer() override; -private: FlexBuffersImpl mImpl; + +private: + static sp Alloc( + const std::shared_ptr &pool, const sp &format); }; class EncryptedLinearInputBuffers : public LinearInputBuffers { @@ -556,18 +587,28 @@ public: ~EncryptedLinearInputBuffers() override = default; - sp alloc(size_t size) override; + std::unique_ptr toArrayMode(size_t size) override; + +protected: + sp createNewBuffer() override; private: - C2MemoryUsage mUsage; - sp mDealer; - sp mCrypto; - int32_t mHeapSeqNum; struct Entry { std::weak_ptr block; sp memory; + int32_t heapSeqNum; }; - std::vector mMemoryVector; + + static sp Alloc( + const std::shared_ptr &pool, + const sp &format, + C2MemoryUsage usage, + const std::shared_ptr> &memoryVector); + + C2MemoryUsage mUsage; + sp mDealer; + sp mCrypto; + std::shared_ptr> mMemoryVector; }; class GraphicMetadataInputBuffers : public InputBuffers { @@ -591,6 +632,9 @@ public: size_t numClientBuffers() const final; +protected: + sp createNewBuffer() override; + private: FlexBuffersImpl mImpl; std::shared_ptr mStore; @@ -619,6 +663,9 @@ public: size_t numClientBuffers() const final; +protected: + sp createNewBuffer() override; + private: FlexBuffersImpl mImpl; std::shared_ptr mLocalBufferPool; @@ -658,6 +705,11 @@ public: size_t numClientBuffers() const final { return 0u; } + +protected: + sp createNewBuffer() override { + return nullptr; + } }; class OutputBuffersArray : public OutputBuffers { @@ -704,6 +756,8 @@ public: void getArray(Vector> *array) const final; + size_t numClientBuffers() const final; + /** * Reallocate the array, filled with buffers with the same size as given * buffer. @@ -712,10 +766,17 @@ public: */ void realloc(const std::shared_ptr &c2buffer); - size_t numClientBuffers() const final; + /** + * Grow the array to the new size. It is a programming error to supply + * smaller size as the new size. + * + * \param newSize[in] new size of the array. + */ + void grow(size_t newSize); private: BuffersArrayImpl mImpl; + std::function()> mAlloc; }; class FlexOutputBuffers : public OutputBuffers { @@ -755,12 +816,15 @@ public: virtual sp wrap(const std::shared_ptr &buffer) = 0; /** - * Return an appropriate Codec2Buffer object for the type of buffers, to be - * used as an empty array buffer. + * Return a function that allocates an appropriate Codec2Buffer object for + * the type of buffers, to be used as an empty array buffer. The function + * must not refer to this pointer, since it may be used after this object + * destructs. * - * \return appropriate Codec2Buffer object which can copy() from C2Buffers. + * \return a function that allocates appropriate Codec2Buffer object, + * which can copy() from C2Buffers. */ - virtual sp allocateArrayBuffer() = 0; + virtual std::function()> getAlloc() = 0; private: FlexBuffersImpl mImpl; @@ -776,7 +840,7 @@ public: sp wrap(const std::shared_ptr &buffer) override; - sp allocateArrayBuffer() override; + std::function()> getAlloc() override; }; class GraphicOutputBuffers : public FlexOutputBuffers { @@ -786,7 +850,7 @@ public: sp wrap(const std::shared_ptr &buffer) override; - sp allocateArrayBuffer() override; + std::function()> getAlloc() override; }; class RawGraphicOutputBuffers : public FlexOutputBuffers { @@ -797,7 +861,7 @@ public: sp wrap(const std::shared_ptr &buffer) override; - sp allocateArrayBuffer() override; + std::function()> getAlloc() override; private: std::shared_ptr mLocalBufferPool; diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h index dd618aab3d..36dcab97dc 100644 --- a/media/codec2/sfplugin/Codec2Buffer.h +++ b/media/codec2/sfplugin/Codec2Buffer.h @@ -226,10 +226,10 @@ public: const std::shared_ptr &block, std::function(size_t)> alloc); - std::shared_ptr asC2Buffer() override; - virtual ~GraphicBlockBuffer() = default; + std::shared_ptr asC2Buffer() override; + private: GraphicBlockBuffer( const sp &format, @@ -260,11 +260,10 @@ public: */ GraphicMetadataBuffer( const sp &format, const std::shared_ptr &alloc); + virtual ~GraphicMetadataBuffer() = default; std::shared_ptr asC2Buffer() override; - virtual ~GraphicMetadataBuffer() = default; - private: GraphicMetadataBuffer() = delete; @@ -307,12 +306,12 @@ public: const sp &format, std::function(size_t)> alloc); + virtual ~ConstGraphicBlockBuffer() = default; + std::shared_ptr asC2Buffer() override; bool canCopy(const std::shared_ptr &buffer) const override; bool copy(const std::shared_ptr &buffer) override; - virtual ~ConstGraphicBlockBuffer() = default; - private: ConstGraphicBlockBuffer( const sp &format, -- GitLab From 9a4c8ebc809ec927ff1332c24c4fdfec0c8be051 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 29 May 2019 15:41:20 -0700 Subject: [PATCH 1481/1530] codec2: Remove quality from output format for CBR, VBR mode Test: Manually verify output format using AMediaCodec based application Test: atest android.media.cts.VideoEncoderTest#testGoogH265FlexMaxMax Bug: 133263311 Change-Id: If084d33c7cf114874fa7db86b0561efdee0725d5 --- media/codec2/sfplugin/CCodecConfig.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index 4c3fff7ecb..077a91f528 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -532,7 +532,6 @@ void CCodecConfig::initializeStandardParams() { } return C2Value(); })); - add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value")); deprecated(ConfigMapper(PARAMETER_KEY_REQUEST_SYNC_FRAME, "coding.request-sync", "value") .limitTo(D::PARAM & D::ENCODER) @@ -798,11 +797,12 @@ void CCodecConfig::initializeStandardParams() { } })); - add(ConfigMapper(KEY_QUALITY, C2_PARAMKEY_QUALITY, "value")); + 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") .limitTo(D::AUDIO & D::ENCODER)); add(ConfigMapper("complexity", C2_PARAMKEY_COMPLEXITY, "value") - .limitTo(D::ENCODER)); + .limitTo(D::ENCODER & (D::CONFIG | D::PARAM))); add(ConfigMapper(KEY_GRID_COLUMNS, C2_PARAMKEY_TILE_LAYOUT, "columns") .limitTo(D::IMAGE)); -- GitLab From 02af5a19abcdcccb23f23b13ef2b6ec5d9ddd805 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Thu, 30 May 2019 09:22:21 -0700 Subject: [PATCH 1482/1530] codec2: use an index flag for split parameters Bug: 124982277 Change-Id: I88dd8f568a5616f12cf6c176ab97fbfc89083962 --- media/codec2/core/include/C2Config.h | 29 +++++++++------ media/codec2/core/include/C2Param.h | 22 +++++++++--- .../codec2/sfplugin/ReflectedParamUpdater.cpp | 7 ++++ media/codec2/vndk/util/C2InterfaceHelper.cpp | 35 ++++++++++++++++++- 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index 9f484a3fa2..3820f9026d 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -117,7 +117,7 @@ enum C2ParamIndexKind : C2Param::type_index_t { /* pipeline characteristics */ kParamIndexMediaType, - kParamIndexDelayRequest, + __kParamIndexRESERVED_0, kParamIndexDelay, kParamIndexMaxReferenceAge, kParamIndexMaxReferenceCount, @@ -151,6 +151,9 @@ enum C2ParamIndexKind : C2Param::type_index_t { /* protected content */ kParamIndexSecureMode, + // deprecated + kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG, + /* ------------------------------------ (trans/en)coders ------------------------------------ */ kParamIndexBitrate = C2_PARAM_INDEX_CODER_PARAM_START, @@ -779,22 +782,26 @@ typedef C2StreamParam C2StreamMe * outstanding input frames queued to the component, it shall produce output. */ -typedef C2PortParam C2PortRequestedDelayTuning; -constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay.requested"; -constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay.requested"; +typedef C2PortParam + C2PortRequestedDelayTuning; +constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay"; // deprecated +constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay"; // deprecated -typedef C2GlobalParam +typedef C2GlobalParam C2RequestedPipelineDelayTuning; -constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "pipeline-delay.requested"; +constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "algo.delay"; // deprecated // read-only -typedef C2PortParam C2PortActualDelayTuning; -constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay.actual"; -constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay.actual"; +typedef C2PortParam C2PortDelayTuning; +typedef C2PortDelayTuning C2PortActualDelayTuning; // deprecated +constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay"; +constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay"; // read-only -typedef C2GlobalParam C2ActualPipelineDelayTuning; -constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay.actual"; +typedef C2GlobalParam C2PipelineDelayTuning; +typedef C2PipelineDelayTuning C2ActualPipelineDelayTuning; // deprecated +constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay"; /** * Reference characteristics. diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h index d264bf3c91..cc8c17a507 100644 --- a/media/codec2/core/include/C2Param.h +++ b/media/codec2/core/include/C2Param.h @@ -158,7 +158,8 @@ public: struct CoreIndex { //public: enum : uint32_t { - IS_FLEX_FLAG = 0x00010000, + IS_FLEX_FLAG = 0x00010000, + IS_REQUEST_FLAG = 0x00020000, }; protected: @@ -175,9 +176,9 @@ public: DIR_INPUT = 0x00000000, DIR_OUTPUT = 0x10000000, - IS_STREAM_FLAG = 0x02000000, - STREAM_ID_MASK = 0x01FE0000, - STREAM_ID_SHIFT = 17, + IS_STREAM_FLAG = 0x00100000, + STREAM_ID_MASK = 0x03E00000, + STREAM_ID_SHIFT = 21, MAX_STREAM_ID = STREAM_ID_MASK >> STREAM_ID_SHIFT, STREAM_MASK = IS_STREAM_FLAG | STREAM_ID_MASK, @@ -360,6 +361,10 @@ public: mIndex = (mIndex & ~(DIR_MASK | IS_STREAM_FLAG)) | DIR_GLOBAL; } + inline void convertToRequest() { + mIndex = mIndex | IS_REQUEST_FLAG; + } + /** * Sets the stream index. * \return true on success, false if could not set index (e.g. not a stream param). @@ -476,6 +481,15 @@ public: return copy; } + /// Returns managed clone of |orig| as a stream parameter at heap. + inline static std::unique_ptr CopyAsRequest(const C2Param &orig) { + std::unique_ptr copy = Copy(orig); + if (copy) { + copy->_mIndex.convertToRequest(); + } + return copy; + } + #if 0 template P *As() { return P::From(this); } diff --git a/media/codec2/sfplugin/ReflectedParamUpdater.cpp b/media/codec2/sfplugin/ReflectedParamUpdater.cpp index 880d4a555f..55b0ec936c 100644 --- a/media/codec2/sfplugin/ReflectedParamUpdater.cpp +++ b/media/codec2/sfplugin/ReflectedParamUpdater.cpp @@ -205,6 +205,13 @@ void ReflectedParamUpdater::addParamDesc( const std::shared_ptr &reflector, bool markVendor) { C2String paramName = desc->name(); + // Do not reflect requested parameters + // TODO: split these once aliases are introduced into '.actual' and '.requested' and alias + // the name to '.actual'. + if (desc->index() & C2Param::CoreIndex::IS_REQUEST_FLAG) { + return; + } + // prefix vendor parameters if (desc->index().isVendor() && markVendor) { paramName = "vendor." + paramName; diff --git a/media/codec2/vndk/util/C2InterfaceHelper.cpp b/media/codec2/vndk/util/C2InterfaceHelper.cpp index e447fbe6b3..3bd709c248 100644 --- a/media/codec2/vndk/util/C2InterfaceHelper.cpp +++ b/media/codec2/vndk/util/C2InterfaceHelper.cpp @@ -609,6 +609,9 @@ c2_status_t C2InterfaceHelper::config( // { depIx, paramIx } may be a suitable key std::map> dependencies; + std::vector> paramRequests; + std::vector lateReadParams; + // we cannot determine the last valid parameter, so add an extra // loop iteration after the last parameter for (size_t p_ix = 0; p_ix <= params.size(); ++p_ix) { @@ -625,7 +628,27 @@ c2_status_t C2InterfaceHelper::config( } paramIx = p->index(); - paramDepIx = getDependencyIndex_l(paramIx); + + // convert parameter to request in case this is a split parameter + C2Param::Index requestParamIx = paramIx | C2Param::CoreIndex::IS_REQUEST_FLAG; + + // setting a request directly is handled as normal + if (paramIx != requestParamIx) { + paramDepIx = getDependencyIndex_l(requestParamIx); + if (paramDepIx == SIZE_MAX) { + // not a split parameter, handle it normally + paramDepIx = getDependencyIndex_l(paramIx); + } else { + // split parameter - replace with setting for the request - and queue to + // read back actual value + // TODO: read late params at the right time + lateReadParams.emplace_back(p); + std::unique_ptr request(C2Param::CopyAsRequest(*p)); + p = request.get(); + paramRequests.emplace_back(std::move(request)); + } + } + if (paramDepIx == SIZE_MAX) { // unsupported parameter paramNotFound = true; @@ -715,6 +738,16 @@ c2_status_t C2InterfaceHelper::config( } } + // get late read parameters + for (C2Param *p : lateReadParams) { + std::shared_ptr value = _mFactory->getParamValue(p->index()); + if (value) { + p->updateFrom(*value); + } else { + p->invalidate(); + } + } + return (paramCorrupted ? C2_CORRUPTED : paramBlocking ? C2_BLOCKING : paramTimedOut ? C2_TIMED_OUT : -- GitLab From 13e556b764fc1cccd29ac98df776e131e693913c Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Fri, 31 May 2019 13:59:55 -0700 Subject: [PATCH 1483/1530] media/codec2: reserve a slot for failed query results Bug: 124982277 Change-Id: I94d0314057805a3a360b716c5e2c6b8e81060ff6 --- media/codec2/vndk/util/C2InterfaceHelper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/codec2/vndk/util/C2InterfaceHelper.cpp b/media/codec2/vndk/util/C2InterfaceHelper.cpp index e447fbe6b3..486760c3f3 100644 --- a/media/codec2/vndk/util/C2InterfaceHelper.cpp +++ b/media/codec2/vndk/util/C2InterfaceHelper.cpp @@ -761,9 +761,11 @@ c2_status_t C2InterfaceHelper::query( if (p != nullptr) { heapParams->push_back(std::move(p)); } else { + heapParams->push_back(nullptr); paramNoMemory = true; } } else { + heapParams->push_back(nullptr); paramNotFound = true; } } -- GitLab From f3808d3a2e8c646d5edad451a80daa61a4e5d836 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Thu, 30 May 2019 19:07:47 -0700 Subject: [PATCH 1484/1530] httplive: detect oom if playlist is infinite Bug: 68399439 Test: StagefrightTest#testStagefright_cve_2017_13279 Change-Id: Icf39c2ae58d9d6ba7c74bbcfbe2154e66e6c9e40 --- media/libstagefright/httplive/HTTPDownloader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp index 861b85a64d..5d6634efa4 100644 --- a/media/libstagefright/httplive/HTTPDownloader.cpp +++ b/media/libstagefright/httplive/HTTPDownloader.cpp @@ -157,6 +157,12 @@ ssize_t HTTPDownloader::fetchBlock( buffer->size() + bufferRemaining); sp copy = new ABuffer(buffer->size() + bufferRemaining); + if (copy->data() == NULL) { + android_errorWriteLog(0x534e4554, "68399439"); + ALOGE("not enough memory to download: requesting %zu + %zu", + buffer->size(), bufferRemaining); + return NO_MEMORY; + } memcpy(copy->data(), buffer->data(), buffer->size()); copy->setRange(0, buffer->size()); -- GitLab From 98be67ae8950a9805e755e22563fa9bfe9405e90 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Tue, 28 May 2019 15:49:52 +0800 Subject: [PATCH 1485/1530] Check buffer size after read Ensure the WAV extractor never outputs a partial sample, which would trigger a CHECK in NuPlayerRenderer:onDrainAudioQueue(). Also ensure it never emits a partial frame (e.g. only a left sample for a stereo file). Bug: 133824968 Test: play the corrupted file and there is no NE Change-Id: I9143f859c55351e06126e4ad8f33f5752113d7f4 (cherry picked from commit 8fb6b7e1ff804ddff1a39319575a4c570abd5b3e) --- media/extractors/wav/WAVExtractor.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp index 8b539c2ff6..4fa7f271cf 100644 --- a/media/extractors/wav/WAVExtractor.cpp +++ b/media/extractors/wav/WAVExtractor.cpp @@ -505,45 +505,40 @@ media_status_t WAVSource::read( // TODO: add capability to return data as float PCM instead of 16 bit PCM. if (mWaveFormat == WAVE_FORMAT_PCM) { + const size_t bytesPerFrame = (mBitsPerSample >> 3) * mNumChannels; + const size_t numFrames = n / bytesPerFrame; + const size_t numSamples = numFrames * mNumChannels; if (mOutputFloat) { float *fdest = (float *)buffer->data(); + buffer->set_range(0, 4 * numSamples); switch (mBitsPerSample) { case 8: { - buffer->set_range(0, 4 * n); - memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), n); + memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), numSamples); } break; case 16: { - const size_t numSamples = n / 2; - buffer->set_range(0, 4 * numSamples); memcpy_to_float_from_i16(fdest, (const int16_t *)buffer->data(), numSamples); } break; case 24: { - const size_t numSamples = n / 3; - buffer->set_range(0, 4 * numSamples); memcpy_to_float_from_p24(fdest, (const uint8_t *)buffer->data(), numSamples); } break; case 32: { // buffer range is correct - const size_t numSamples = n / 4; memcpy_to_float_from_i32(fdest, (const int32_t *)buffer->data(), numSamples); } break; } } else { int16_t *idest = (int16_t *)buffer->data(); + buffer->set_range(0, 2 * numSamples); switch (mBitsPerSample) { case 8: { - buffer->set_range(0, 2 * n); - memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), n); + memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), numSamples); } break; case 16: - break; // no translation needed + // no conversion needed + break; case 24: { - const size_t numSamples = n / 3; - buffer->set_range(0, 2 * numSamples); memcpy_to_i16_from_p24(idest, (const uint8_t *)buffer->data(), numSamples); } break; case 32: { - const size_t numSamples = n / 4; - buffer->set_range(0, 2 * numSamples); memcpy_to_i16_from_i32(idest, (const int32_t *)buffer->data(), numSamples); } break; } -- GitLab From d3b761d13563b22de6ad58ad5183a925a022c671 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Tue, 15 Jan 2019 18:41:50 +0800 Subject: [PATCH 1486/1530] Attemp to make mkv's AAC CSD data if codecprivate is short Some mkv file's AAC codecprivate is incomplete, so CHECK(codecPrivateSize >= 2) fails and it results in a native exception. Our solution is to make it skip the track. Bug: 132738230 Test: Test a mkv file whose AAC codecprivate is short and check if native exception occurs. Change-Id: Ia8cdd863df1bd1e7570cc3b509d1d76d52d76be6 --- media/extractors/mkv/MatroskaExtractor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index b1eb301e05..23022e44e2 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -2109,8 +2109,10 @@ void MatroskaExtractor::addTracks() { if (!strcmp("A_AAC", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC); - CHECK(codecPrivateSize >= 2); - + if (codecPrivateSize < 2) { + ALOGW("Incomplete AAC Codec Info %zu byte", codecPrivateSize); + continue; + } addESDSFromCodecPrivate( meta, true, codecPrivate, codecPrivateSize); } else if (!strcmp("A_VORBIS", codecID)) { -- GitLab From a75cd798184169a4effc0f6e454a3a6e7386d4b3 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Thu, 30 May 2019 14:34:57 +0800 Subject: [PATCH 1487/1530] Cannot play a mov file with alac audio When a mov file includes alac audio, the alac codec info is included under bellow box: stsd->alac->wave->alac The .mov cannot work with the original code for alac. So we need to handle it by judging if it is a mov file. Bug: 134102659 Test: Play a mov video with alac audio and check if it can play normally. Change-Id: Ibadfbd5b4036b220bd6ebf431fd53e4d1a69be8b --- media/extractors/mp4/MPEG4Extractor.cpp | 132 ++++++++++++------------ media/extractors/mp4/MPEG4Extractor.h | 1 + 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 527bb77acd..9d5890c42c 100755 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -1197,45 +1197,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate); } - // If format type is 'alac', it is necessary to get the parameters - // from a alac atom spreading behind the frma atom. - // See 'external/alac/ALACMagicCookieDescription.txt'. - if (original_fourcc == FOURCC("alac")) { - // Store ALAC magic cookie (decoder needs it). - uint8_t alacInfo[12]; - data_offset = *offset; - if (mDataSource->readAt( - data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) { - return ERROR_IO; - } - uint32_t size = U32_AT(&alacInfo[0]); - if ((size != ALAC_SPECIFIC_INFO_SIZE) || - (U32_AT(&alacInfo[4]) != FOURCC("alac")) || - (U32_AT(&alacInfo[8]) != 0)) { - return ERROR_MALFORMED; - } - - data_offset += sizeof(alacInfo); - uint8_t cookie[size - sizeof(alacInfo)]; - if (mDataSource->readAt( - data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) { - return ERROR_IO; + if (!mIsQT && original_fourcc == FOURCC("alac")) { + off64_t tmpOffset = *offset; + status_t err = parseALACSampleEntry(&tmpOffset); + if (err != OK) { + ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__); + return err; } - - uint8_t bitsPerSample = cookie[5]; - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample); - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]); - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20])); - AMediaFormat_setBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie)); - - // Add the size of ALAC Specific Info (36 bytes) and terminator - // atom (8 bytes). - *offset += (size + 8); + *offset = tmpOffset + 8; } + break; } @@ -1653,7 +1624,18 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case 0x6D730055: // "ms U" mp3 audio { if (mIsQT && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) { + + if (chunk_type == FOURCC("alac")) { + off64_t offsetTmp = *offset; + status_t err = parseALACSampleEntry(&offsetTmp); + if (err != OK) { + ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__); + return err; + } + } + // Ignore all atoms embedded in QT wave atom + ALOGV("Ignore all atoms embedded in QT wave atom"); *offset += chunk_size; break; } @@ -1792,39 +1774,14 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { CHECK_EQ(*offset, stop_offset); } - if (chunk_type == FOURCC("alac")) { - - // See 'external/alac/ALACMagicCookieDescription.txt for the detail'. - // Store ALAC magic cookie (decoder needs it). - uint8_t alacInfo[12]; + if (!mIsQT && chunk_type == FOURCC("alac")) { data_offset += sizeof(buffer); - if (mDataSource->readAt( - data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) { - return ERROR_IO; - } - uint32_t size = U32_AT(&alacInfo[0]); - if ((size != ALAC_SPECIFIC_INFO_SIZE) || - (U32_AT(&alacInfo[4]) != FOURCC("alac")) || - (U32_AT(&alacInfo[8]) != 0)) { - return ERROR_MALFORMED; - } - data_offset += sizeof(alacInfo); - uint8_t cookie[size - sizeof(alacInfo)]; - if (mDataSource->readAt( - data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) { - return ERROR_IO; - } - uint8_t bitsPerSample = cookie[5]; - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample); - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]); - AMediaFormat_setInt32(mLastTrack->meta, - AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20])); - AMediaFormat_setBuffer(mLastTrack->meta, - AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie)); - data_offset += sizeof(cookie); + status_t err = parseALACSampleEntry(&data_offset); + if (err != OK) { + ALOGE("parseALACSampleEntry err:%d Line:%d", err, __LINE__); + return err; + } *offset = data_offset; CHECK_EQ(*offset, stop_offset); } @@ -3289,6 +3246,45 @@ status_t MPEG4Extractor::parseAC3SpecificBox(off64_t offset) { return OK; } +status_t MPEG4Extractor::parseALACSampleEntry(off64_t *offset) { + // See 'external/alac/ALACMagicCookieDescription.txt for the detail'. + // Store ALAC magic cookie (decoder needs it). + uint8_t alacInfo[12]; + off64_t data_offset = *offset; + + if (mDataSource->readAt( + data_offset, alacInfo, sizeof(alacInfo)) < (ssize_t)sizeof(alacInfo)) { + return ERROR_IO; + } + uint32_t size = U32_AT(&alacInfo[0]); + if ((size != ALAC_SPECIFIC_INFO_SIZE) || + (U32_AT(&alacInfo[4]) != FOURCC("alac")) || + (U32_AT(&alacInfo[8]) != 0)) { + ALOGV("Size:%u, U32_AT(&alacInfo[4]):%u, U32_AT(&alacInfo[8]):%u", + size, U32_AT(&alacInfo[4]), U32_AT(&alacInfo[8])); + return ERROR_MALFORMED; + } + data_offset += sizeof(alacInfo); + uint8_t cookie[size - sizeof(alacInfo)]; + if (mDataSource->readAt( + data_offset, cookie, sizeof(cookie)) < (ssize_t)sizeof(cookie)) { + return ERROR_IO; + } + + uint8_t bitsPerSample = cookie[5]; + AMediaFormat_setInt32(mLastTrack->meta, + AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample); + AMediaFormat_setInt32(mLastTrack->meta, + AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]); + AMediaFormat_setInt32(mLastTrack->meta, + AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20])); + AMediaFormat_setBuffer(mLastTrack->meta, + AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie)); + data_offset += sizeof(cookie); + *offset = data_offset; + return OK; +} + status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { ALOGV("MPEG4Extractor::parseSegmentIndex"); diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h index e10bf8a055..fcddbb81ec 100644 --- a/media/extractors/mp4/MPEG4Extractor.h +++ b/media/extractors/mp4/MPEG4Extractor.h @@ -179,6 +179,7 @@ private: status_t parseAC3SpecificBox(off64_t offset); status_t parseEAC3SpecificBox(off64_t offset); status_t parseAC4SpecificBox(off64_t offset); + status_t parseALACSampleEntry(off64_t *offset); void adjustRawDefaultFrameSize(); MPEG4Extractor(const MPEG4Extractor &); -- GitLab From 9a44849c88b306e1b4fb37bd9aa34d6ba0607b7a Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 17 May 2019 07:29:07 -0700 Subject: [PATCH 1488/1530] AMR WB encoder: prevent OOB write in ACELP_4t64_fx In ACELP_4t64_fx, when iterating over ind array, check index against array size to prevent OOB write, log an error if such an access was about to happen. Bug: 132647222 Test: atest EncoderTest#testAMRWBEncoders Change-Id: I33f476d94baec2feffc7bcccd0ad0481b8452518 (cherry picked from commit 82cb46d0d55a407f468023977204eb7133b7fd77) Merged-in: I33f476d94baec2feffc7bcccd0ad0481b8452518 --- media/libstagefright/codecs/amrwbenc/Android.bp | 1 + .../codecs/amrwbenc/SampleCode/Android.bp | 1 + media/libstagefright/codecs/amrwbenc/src/c4t64fx.c | 13 +++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp index 89689917ce..6ded9ffea5 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -129,6 +129,7 @@ cc_library_static { shared_libs: [ "libstagefright_enc_common", + "liblog", ], cflags: ["-Werror"], diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp index d52fed389d..79a5d6718b 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.bp @@ -12,6 +12,7 @@ cc_test { shared_libs: [ "libdl", + "liblog", ], static_libs: [ diff --git a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c index 8cebb097bd..f2e28c42dd 100644 --- a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c +++ b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c @@ -47,6 +47,10 @@ #include "q_pulse.h" +#undef LOG_TAG +#define LOG_TAG "amrwbenc" +#include "log/log.h" + static Word16 tipos[36] = { 0, 1, 2, 3, /* starting point &ipos[0], 1st iter */ 1, 2, 3, 0, /* starting point &ipos[4], 2nd iter */ @@ -745,11 +749,16 @@ void ACELP_4t64_fx( i = (Word16)((vo_L_mult(track, NPMAXPT) >> 1)); - while (ind[i] >= 0) + while (i < NPMAXPT * NB_TRACK && ind[i] >= 0) { i += 1; } - ind[i] = index; + if (i < NPMAXPT * NB_TRACK) { + ind[i] = index; + } else { + ALOGE("b/132647222, OOB access in ind array track=%d i=%d", track, i); + android_errorWriteLog(0x534e4554, "132647222"); + } } k = 0; -- GitLab From 7d966311d724a14f7332d673726d1076c589dafa Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 4 Jun 2019 14:00:49 -0700 Subject: [PATCH 1489/1530] codec2: fix graphic buffer copy Bug: 134498476 Test: atest CtsMediaTestCases:ImageReaderDecoderTest Change-Id: I4de9f5f0410c308bd87b59124d536a671a5d2b61 --- media/codec2/sfplugin/Codec2Buffer.cpp | 76 ++++++++++--------- .../sfplugin/utils/Codec2BufferUtils.cpp | 8 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index c20ca7392d..702ad6f79c 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -201,9 +201,10 @@ public: * \param colorFormat desired SDK color format for the MediaImage (if this is a flexible format, * an attempt is made to simply represent the graphic view as a flexible SDK format * without a memcpy) + * \param copy whether the converter is used for copy or not */ GraphicView2MediaImageConverter( - const C2GraphicView &view, int32_t colorFormat) + const C2GraphicView &view, int32_t colorFormat, bool copy) : mInitCheck(NO_INIT), mView(view), mWidth(view.width()), @@ -255,41 +256,44 @@ public: } switch (mColorFormat) { case COLOR_FormatYUV420Flexible: - { // try to map directly. check if the planes are near one another - const uint8_t *minPtr = mView.data()[0]; - const uint8_t *maxPtr = mView.data()[0]; - int32_t planeSize = 0; - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo &plane = layout.planes[i]; - ssize_t minOffset = plane.minOffset(mWidth, mHeight); - ssize_t maxOffset = plane.maxOffset(mWidth, mHeight); - if (minPtr > mView.data()[i] + minOffset) { - minPtr = mView.data()[i] + minOffset; - } - if (maxPtr < mView.data()[i] + maxOffset) { - maxPtr = mView.data()[i] + maxOffset; - } - planeSize += std::abs(plane.rowInc) * align(mHeight, 64) - / plane.rowSampling / plane.colSampling * divUp(mAllocatedDepth, 8u); - } - - if ((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 + if (!copy) { + // try to map directly. check if the planes are near one another + const uint8_t *minPtr = mView.data()[0]; + const uint8_t *maxPtr = mView.data()[0]; + int32_t planeSize = 0; for (uint32_t i = 0; i < layout.numPlanes; ++i) { const C2PlaneInfo &plane = layout.planes[i]; - mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr; - mediaImage->mPlane[i].mColInc = plane.colInc; - mediaImage->mPlane[i].mRowInc = plane.rowInc; - mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; - mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + ssize_t minOffset = plane.minOffset(mWidth, mHeight); + ssize_t maxOffset = plane.maxOffset(mWidth, mHeight); + if (minPtr > mView.data()[i] + minOffset) { + minPtr = mView.data()[i] + minOffset; + } + if (maxPtr < mView.data()[i] + maxOffset) { + maxPtr = mView.data()[i] + maxOffset; + } + planeSize += std::abs(plane.rowInc) * align(mHeight, 64) + / plane.rowSampling / plane.colSampling + * divUp(mAllocatedDepth, 8u); + } + + if ((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 + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr; + mediaImage->mPlane[i].mColInc = plane.colInc; + mediaImage->mPlane[i].mRowInc = plane.rowInc; + mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling; + mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling; + } + mWrapped = new ABuffer(const_cast(minPtr), + maxPtr - minPtr + 1); + break; } - mWrapped = new ABuffer(const_cast(minPtr), maxPtr - minPtr + 1); - break; } - } - [[fallthrough]]; + [[fallthrough]]; case COLOR_FormatYUV420Planar: case COLOR_FormatYUV420PackedPlanar: @@ -503,7 +507,7 @@ sp GraphicBlockBuffer::Allocate( int32_t colorFormat = COLOR_FormatYUV420Flexible; (void)format->findInt32("color-format", &colorFormat); - GraphicView2MediaImageConverter converter(view, colorFormat); + GraphicView2MediaImageConverter converter(view, colorFormat, false /* copy */); if (converter.initCheck() != OK) { ALOGD("Converter init failed: %d", converter.initCheck()); return nullptr; @@ -615,7 +619,7 @@ sp ConstGraphicBlockBuffer::Allocate( int32_t colorFormat = COLOR_FormatYUV420Flexible; (void)format->findInt32("color-format", &colorFormat); - GraphicView2MediaImageConverter converter(*view, colorFormat); + GraphicView2MediaImageConverter converter(*view, colorFormat, false /* copy */); if (converter.initCheck() != OK) { ALOGD("Converter init failed: %d", converter.initCheck()); return nullptr; @@ -708,7 +712,7 @@ bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr &buffer) c const_cast(this)->format()->findInt32("color-format", &colorFormat); GraphicView2MediaImageConverter converter( - buffer->data().graphicBlocks()[0].map().get(), colorFormat); + buffer->data().graphicBlocks()[0].map().get(), colorFormat, true /* copy */); if (converter.initCheck() != OK) { ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck()); return false; @@ -730,7 +734,7 @@ bool ConstGraphicBlockBuffer::copy(const std::shared_ptr &buffer) { format()->findInt32("color-format", &colorFormat); GraphicView2MediaImageConverter converter( - buffer->data().graphicBlocks()[0].map().get(), colorFormat); + buffer->data().graphicBlocks()[0].map().get(), colorFormat, true /* copy */); if (converter.initCheck() != OK) { ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck()); return false; diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp index 1e884ef8e7..bf2a07ee52 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp @@ -137,14 +137,14 @@ status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView int32_t dst_stride_v = img->mPlane[2].mRowInc; if (IsNV12(view) && IsI420(img)) { if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y, - dst_u, dst_stride_u, dst_v, dst_stride_v, view.width(), - view.height())) { + dst_u, dst_stride_u, dst_v, dst_stride_v, view.crop().width, + view.crop().height)) { return OK; } } else { if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, - dst_y, dst_stride_y, dst_u, dst_stride_u, view.width(), - view.height())) { + dst_y, dst_stride_y, dst_u, dst_stride_u, view.crop().width, + view.crop().height)) { return OK; } } -- GitLab From 881a266f9e88a92e004dace2faa2bb28d60a20d9 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 30 May 2019 14:32:22 -0700 Subject: [PATCH 1490/1530] C2SoftOpusEnc: Fix higher bitrate encoding at lower sample rates Output buffer allocated was not large enough to hold higher bitrate outputs for lower sample rates (eg: 512kbps at 8000 Hz) Also pass remaining bytes in the allocated output buffer correctly to encoder. Bug: 134087860 Test: Apply CTS cl mentioned in the bug Test: atest android.media.cts.EncoderTest#testOpusEncoders Change-Id: I01a75cb76d15b733690e35fa107ec973668d19bb --- media/codec2/components/opus/C2SoftOpusEnc.cpp | 4 ++-- media/codec2/components/opus/C2SoftOpusEnc.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp index 384d58b712..70d1965f62 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.cpp +++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp @@ -471,11 +471,11 @@ void C2SoftOpusEnc::process(const std::unique_ptr& work, uint8_t* outPtr = wView.data() + mBytesEncoded; int encodedBytes = opus_multistream_encode(mEncoder, mInputBufferPcm16, - mNumSamplesPerFrame, outPtr, kMaxPayload); + mNumSamplesPerFrame, outPtr, kMaxPayload - mBytesEncoded); ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes, processSize); - if (encodedBytes < 0 || encodedBytes > kMaxPayload) { + if (encodedBytes < 0 || encodedBytes > (kMaxPayload - mBytesEncoded)) { ALOGE("opus_encode failed, encodedBytes : %d", encodedBytes); mSignalledError = true; work->result = C2_CORRUPTED; diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h index 69e5240062..2b4d8f2320 100644 --- a/media/codec2/components/opus/C2SoftOpusEnc.h +++ b/media/codec2/components/opus/C2SoftOpusEnc.h @@ -47,7 +47,9 @@ struct C2SoftOpusEnc : public SimpleC2Component { private: /* OPUS_FRAMESIZE_20_MS */ const int kFrameSize = 960; - const int kMaxPayload = 4000; + const int kMaxSampleRate = 48000; + const int kMinSampleRate = 8000; + const int kMaxPayload = (4000 * kMaxSampleRate) / kMinSampleRate; const int kMaxNumChannels = 8; std::shared_ptr mIntf; -- GitLab From faeab0faa7ff42eba8935d90e3f69581229002da Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Fri, 14 Sep 2018 21:17:46 -0700 Subject: [PATCH 1491/1530] Match src paths with aidl package name In order for the build system to track updates to the header files during incremental builds, always specify the src files using the same path as the package for C++ compilations. Bug: 112114177 Test: m Change-Id: I5fb25124a26f190c462e2e60fc75a88d48643c10 --- camera/Android.bp | 2 ++ media/libaudioclient/Android.bp | 13 ++++++++++++- media/libmedia/Android.bp | 12 ++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/camera/Android.bp b/camera/Android.bp index 2800595f4a..b288bcfce0 100644 --- a/camera/Android.bp +++ b/camera/Android.bp @@ -86,6 +86,7 @@ filegroup { "aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl", "aidl/android/hardware/camera2/ICameraDeviceUser.aidl", ], + path: "aidl", } // Extra AIDL files that are used by framework.jar but not libcamera_client @@ -96,4 +97,5 @@ filegroup { "aidl/android/hardware/ICamera.aidl", "aidl/android/hardware/ICameraClient.aidl", ], + path: "aidl", } diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index 03bd6ce945..c516d20f2b 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -42,7 +42,7 @@ cc_library_shared { // AIDL files for audioclient interfaces // The headers for these interfaces will be available to any modules that // include libaudioclient, at the path "aidl/package/path/BnFoo.h" - "aidl/android/media/IAudioRecord.aidl", + ":libaudioclient_aidl_private", ":libaudioclient_aidl", "AudioEffect.cpp", @@ -110,4 +110,15 @@ filegroup { srcs: [ "aidl/android/media/IPlayer.aidl", ], + path: "aidl", +} + +// Used to strip the "aidl/" from the path, so the build system can predict the +// output filename. +filegroup { + name: "libaudioclient_aidl_private", + srcs: [ + "aidl/android/media/IAudioRecord.aidl", + ], + path: "aidl", } diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 1d335901c5..d1412874f1 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -47,6 +47,15 @@ cc_library { clang: true, } +filegroup { + name: "libmedia_omx_aidl", + srcs: [ + "aidl/android/IGraphicBufferSource.aidl", + "aidl/android/IOMXBufferSource.aidl", + ], + path: "aidl", +} + cc_library_shared { name: "libmedia_omx", vendor_available: true, @@ -56,8 +65,7 @@ cc_library_shared { double_loadable: true, srcs: [ - "aidl/android/IGraphicBufferSource.aidl", - "aidl/android/IOMXBufferSource.aidl", + ":libmedia_omx_aidl", "IMediaCodecList.cpp", "IOMX.cpp", -- GitLab From 0da67bb0e7facf146be789e4c67afa4fc5788995 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 3 Jun 2019 16:46:03 -0700 Subject: [PATCH 1492/1530] libcamera2ndk_vendor: Don't retrieve value from failed HIDL calls. Bug: 134424054 Test: AImageReaderVendorTest; use libcamera2ndk_vendor client on device. Change-Id: Id6ebfc7c37af8d814a1bedfffdde14a8397591a5 Signed-off-by: Jayant Chowdhary --- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 76 +++++++------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 529c167edc..a43d707e8e 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -31,6 +31,17 @@ #include "ACameraCaptureSession.inc" +#define CHECK_TRANSACTION_AND_RET(remoteRet, status, callName) \ + if (!remoteRet.isOk()) { \ + ALOGE("%s: Transaction error during %s call %s", __FUNCTION__, callName, \ + remoteRet.description().c_str()); \ + return ACAMERA_ERROR_UNKNOWN; \ + } \ + if (status != Status::NO_ERROR) { \ + ALOGE("%s: %s call failed", __FUNCTION__, callName); \ + return utils::convertFromHidl(status); \ + } + using namespace android; namespace android { @@ -151,7 +162,7 @@ CameraDevice::createCaptureRequest( return ACAMERA_ERROR_CAMERA_DISCONNECTED; } CameraMetadata rawRequest; - Status status = Status::NO_ERROR; + Status status = Status::UNKNOWN_ERROR; auto remoteRet = mRemote->createDefaultRequest( utils::convertToHidl(templateId), [&status, &rawRequest](auto s, const hidl_vec &metadata) { @@ -161,14 +172,7 @@ CameraDevice::createCaptureRequest( ALOGE("%s: Couldn't create default request", __FUNCTION__); } }); - if (!remoteRet.isOk()) { - ALOGE("%s: Transaction error while trying to create default request %s", __FUNCTION__, - remoteRet.description().c_str()); - return ACAMERA_ERROR_UNKNOWN; - } - if (status != Status::NO_ERROR) { - return utils::convertFromHidl(status); - } + CHECK_TRANSACTION_AND_RET(remoteRet, status, "createDefaultRequest()") ACaptureRequest* outReq = new ACaptureRequest(); outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST); if (physicalCameraIdList != nullptr) { @@ -243,20 +247,15 @@ camera_status_t CameraDevice::isSessionConfigurationSupported( } bool configSupported = false; - Status status = Status::NO_ERROR; + Status status = Status::UNKNOWN_ERROR; auto remoteRet = mRemote->isSessionConfigurationSupported(sessionConfig, [&status, &configSupported](auto s, auto supported) { status = s; configSupported = supported; }); - if (status == Status::INVALID_OPERATION) { - return ACAMERA_ERROR_UNSUPPORTED_OPERATION; - } else if (!remoteRet.isOk()) { - return ACAMERA_ERROR_UNKNOWN; - } else { - return configSupported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL; - } + CHECK_TRANSACTION_AND_RET(remoteRet, status, "isSessionConfigurationSupported()"); + return configSupported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL; } static void addMetadataToPhysicalCameraSettings(const CameraMetadata *metadata, @@ -525,16 +524,13 @@ CameraDevice::stopRepeatingLocked() { mRepeatingSequenceId = REQUEST_ID_NONE; int64_t lastFrameNumber; - Status status = Status::NO_ERROR; + Status status = Status::UNKNOWN_ERROR; auto remoteRet = mRemote->cancelRepeatingRequest( [&status, &lastFrameNumber](Status s, auto frameNumber) { status = s; lastFrameNumber = frameNumber; }); - if (!remoteRet.isOk() || status != Status::NO_ERROR) { - ALOGE("%s: Unable to cancel active repeating request", __FUNCTION__); - return utils::convertFromHidl(status); - } + CHECK_TRANSACTION_AND_RET(remoteRet, status, "cancelRepeatingRequest()"); checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber); } return ACAMERA_OK; @@ -582,15 +578,12 @@ CameraDevice::flushLocked(ACameraCaptureSession* session) { } int64_t lastFrameNumber; - Status status; + Status status = Status::UNKNOWN_ERROR; auto remoteRet = mRemote->flush([&status, &lastFrameNumber](auto s, auto frameNumber) { status = s; lastFrameNumber = frameNumber; }); - if (!remoteRet.isOk() || status != Status::NO_ERROR) { - ALOGE("%s: Abort captures failed", __FUNCTION__); - return utils::convertFromHidl(status); - } + CHECK_TRANSACTION_AND_RET(remoteRet, status, "flush()") if (mRepeatingSequenceId != REQUEST_ID_NONE) { checkRepeatingSequenceCompleteLocked(mRepeatingSequenceId, lastFrameNumber); } @@ -611,10 +604,7 @@ CameraDevice::waitUntilIdleLocked() { } auto remoteRet = mRemote->waitUntilIdle(); - if (!remoteRet.isOk()) { - ALOGE("%s: Transaction waitUntilIdle failed", __FUNCTION__); - return utils::convertFromHidl(remoteRet); - } + CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "waitUntilIdle()") return ACAMERA_OK; } @@ -689,34 +679,25 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu mIdle = true; auto remoteRet = mRemote->beginConfigure(); - if (!remoteRet.isOk()|| remoteRet != Status::NO_ERROR) { - ALOGE("Camera device %s begin configure failed", getId()); - return utils::convertFromHidl(remoteRet); - } + CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "beginConfigure()") // delete to-be-deleted streams for (auto streamId : deleteList) { remoteRet = mRemote->deleteStream(streamId); - if (!remoteRet.isOk() || remoteRet != Status::NO_ERROR) { - ALOGE("Camera device %s failed to remove stream %d", getId(), streamId); - return utils::convertFromHidl(remoteRet); - } + CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "deleteStream()") mConfiguredOutputs.erase(streamId); } // add new streams for (auto outputPair : addSet) { int streamId; - Status status; + Status status = Status::UNKNOWN_ERROR; auto ret = mRemote->createStream(outputPair.second, [&status, &streamId](Status s, auto stream_id) { status = s; streamId = stream_id; }); - if (!remoteRet.isOk() || status != Status::NO_ERROR) { - ALOGE("Camera device %s failed to create stream", getId()); - return utils::convertFromHidl(status); - } + CHECK_TRANSACTION_AND_RET(ret, status, "createStream()") mConfiguredOutputs.insert(std::make_pair(streamId, outputPair)); } @@ -729,11 +710,8 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu params.unlock(params_metadata); } remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams); - if (!remoteRet.isOk()) { - ALOGE("Transaction error: endConfigure failed %s", remoteRet.description().c_str()); - } - - return utils::convertFromHidl(remoteRet); + CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "endConfigure()") + return ACAMERA_OK; } void -- GitLab From 04e48ed870c1c4163948bceee034d750ae60b5d4 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 5 Jun 2019 09:11:39 -0700 Subject: [PATCH 1493/1530] [DO NOT MERGE] AMR WB encoder: prevent OOB write in ACELP_4t64_fx In ACELP_4t64_fx, when iterating over ind array, check index against array size to prevent OOB write, log an error if such an access was about to happen. Bug 132647222 Test: atest EncoderTest#testAMRWBEncoders Merged-in: I33f476d94baec2feffc7bcccd0ad0481b8452518 Change-Id: I814a05217f20ea3b941deddb70edb31cd342de6b --- media/libstagefright/codecs/amrwbenc/Android.mk | 3 ++- .../codecs/amrwbenc/SampleCode/Android.mk | 3 ++- media/libstagefright/codecs/amrwbenc/src/c4t64fx.c | 13 +++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk index 026006e23f..006a034007 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/Android.mk @@ -92,7 +92,8 @@ LOCAL_ARM_MODE := arm LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := +LOCAL_SHARED_LIBRARIES := \ + liblog LOCAL_C_INCLUDES := \ frameworks/av/include \ diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk index 65d69a2c28..0d6549eea7 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk @@ -14,7 +14,8 @@ LOCAL_CFLAGS := LOCAL_SHARED_LIBRARIES := \ libstagefright \ - libdl + libdl \ + liblog LOCAL_STATIC_LIBRARIES := \ libstagefright_amrwbenc diff --git a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c index 8cebb097bd..b26eedb508 100644 --- a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c +++ b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c @@ -47,6 +47,10 @@ #include "q_pulse.h" +#undef LOG_TAG +#define LOG_TAG "amrwbenc" +#include + static Word16 tipos[36] = { 0, 1, 2, 3, /* starting point &ipos[0], 1st iter */ 1, 2, 3, 0, /* starting point &ipos[4], 2nd iter */ @@ -745,11 +749,16 @@ void ACELP_4t64_fx( i = (Word16)((vo_L_mult(track, NPMAXPT) >> 1)); - while (ind[i] >= 0) + while (i < NPMAXPT * NB_TRACK && ind[i] >= 0) { i += 1; } - ind[i] = index; + if (i < NPMAXPT * NB_TRACK) { + ind[i] = index; + } else { + ALOGE("b/132647222, OOB access in ind array track=%d i=%d", track, i); + android_errorWriteLog(0x534e4554, "132647222"); + } } k = 0; -- GitLab From 9265395f3429d1c324e66a91a6a42cf5525b4498 Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Tue, 7 May 2019 15:11:43 -0700 Subject: [PATCH 1494/1530] Camera: Use original format and dataSpace for reconfigure Starting from HAL device version 3.5, the format and dataSpace for IMPLEMENTATION_DEFINED pixel format uses original instead of overridden values. This makes HAL interface behavior consistent in that HAL doesn't need to distinguish between first-time configureStreams and subsequent reconfigure. Test: Camera CTS and partner testing Bug: 131864007 Change-Id: Ie5fdc7e9b6c11c1c96a069262b9458455855bcef --- .../device3/Camera3Device.cpp | 32 +++++++++++++------ .../device3/Camera3SharedOutputStream.cpp | 2 +- .../device3/Camera3Stream.cpp | 10 ++++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 00f0d86387..9771f9ee29 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -2876,7 +2876,8 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode, config.streams = streams.editArray(); // Do the HAL configuration; will potentially touch stream - // max_buffers, usage, priv fields. + // max_buffers, usage, and priv fields, as well as data_space and format + // fields for IMPLEMENTATION_DEFINED formats. const camera_metadata_t *sessionBuffer = sessionParams.getAndLock(); res = mInterface->configureStreams(sessionBuffer, &config, bufferSizes); @@ -4202,10 +4203,19 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * dst3_2.streamType = streamType; dst3_2.width = src->width; dst3_2.height = src->height; - dst3_2.format = mapToPixelFormat(src->format); dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage()); - dst3_2.dataSpace = mapToHidlDataspace(src->data_space); dst3_2.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation); + // For HidlSession version 3.5 or newer, the format and dataSpace sent + // to HAL are original, not the overriden ones. + if (mHidlSession_3_5 != nullptr) { + dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden() ? + cam3stream->getOriginalFormat() : src->format); + dst3_2.dataSpace = mapToHidlDataspace(cam3stream->isDataSpaceOverridden() ? + cam3stream->getOriginalDataSpace() : src->data_space); + } else { + dst3_2.format = mapToPixelFormat(src->format); + dst3_2.dataSpace = mapToHidlDataspace(src->data_space); + } dst3_4.v3_2 = dst3_2; dst3_4.bufferSize = bufferSizes[i]; if (src->physical_camera_id != nullptr) { @@ -4266,7 +4276,7 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * return OK; }; - // See if we have v3.4 or v3.3 HAL + // See which version of HAL we have if (mHidlSession_3_5 != nullptr) { ALOGV("%s: v3.5 device found", __FUNCTION__); device::V3_5::StreamConfiguration requestedConfiguration3_5; @@ -4281,7 +4291,6 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * } else if (mHidlSession_3_4 != nullptr) { // We do; use v3.4 for the call ALOGV("%s: v3.4 device found", __FUNCTION__); - device::V3_4::HalStreamConfiguration finalConfiguration3_4; auto err = mHidlSession_3_4->configureStreams_3_4( requestedConfiguration3_4, configStream34Cb); res = postprocConfigStream34(err); @@ -4352,12 +4361,12 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * device::V3_3::HalStream &src = finalConfiguration.streams[realIdx]; Camera3Stream* dstStream = Camera3Stream::cast(dst); - dstStream->setFormatOverride(false); - dstStream->setDataSpaceOverride(false); int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat); android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace); if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + dstStream->setFormatOverride(false); + dstStream->setDataSpaceOverride(false); if (dst->format != overrideFormat) { ALOGE("%s: Stream %d: Format override not allowed for format 0x%x", __FUNCTION__, streamId, dst->format); @@ -4367,10 +4376,13 @@ status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t * streamId, dst->format); } } else { - dstStream->setFormatOverride((dst->format != overrideFormat) ? true : false); - dstStream->setDataSpaceOverride((dst->data_space != overrideDataSpace) ? true : false); - + bool needFormatOverride = + requestedConfiguration3_2.streams[i].format != src.v3_2.overrideFormat; + bool needDataspaceOverride = + requestedConfiguration3_2.streams[i].dataSpace != src.overrideDataSpace; // Override allowed with IMPLEMENTATION_DEFINED + dstStream->setFormatOverride(needFormatOverride); + dstStream->setDataSpaceOverride(needDataspaceOverride); dst->format = overrideFormat; dst->data_space = overrideDataSpace; } diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp index e3b74d7a44..86b45cb2db 100644 --- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp @@ -55,7 +55,7 @@ status_t Camera3SharedOutputStream::connectStreamSplitterLocked() { mStreamSplitter = new Camera3StreamSplitter(mUseHalBufManager); - uint64_t usage; + uint64_t usage = 0; getEndpointUsage(&usage); std::unordered_map> initialSurfaces; diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index d73a2f985d..6d7680240b 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -67,6 +67,8 @@ Camera3Stream::Camera3Stream(int id, mBufferLimitLatency(kBufferLimitLatencyBinSize), mFormatOverridden(false), mOriginalFormat(-1), + mDataSpaceOverridden(false), + mOriginalDataSpace(HAL_DATASPACE_UNKNOWN), mPhysicalCameraId(physicalCameraId), mLastTimestamp(0) { @@ -121,7 +123,9 @@ void Camera3Stream::setUsage(uint64_t usage) { void Camera3Stream::setFormatOverride(bool formatOverridden) { mFormatOverridden = formatOverridden; - if (formatOverridden) mOriginalFormat = camera3_stream::format; + if (formatOverridden && mOriginalFormat == -1) { + mOriginalFormat = camera3_stream::format; + } } bool Camera3Stream::isFormatOverridden() const { @@ -134,7 +138,9 @@ int Camera3Stream::getOriginalFormat() const { void Camera3Stream::setDataSpaceOverride(bool dataSpaceOverridden) { mDataSpaceOverridden = dataSpaceOverridden; - if (dataSpaceOverridden) mOriginalDataSpace = camera3_stream::data_space; + if (dataSpaceOverridden && mOriginalDataSpace == HAL_DATASPACE_UNKNOWN) { + mOriginalDataSpace = camera3_stream::data_space; + } } bool Camera3Stream::isDataSpaceOverridden() const { -- GitLab From b61526c6a080af7bc062b231c52d1b7e6545868d Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 13 May 2019 19:37:42 -0700 Subject: [PATCH 1495/1530] cameraserver: Stop appOps camera reporting for vendor clients. Since vendor native clients don't have package names, appOps reporting would be inaccurate for them. In order to avoid that, we stop appOps reporting for vendor clients. Bug: 132718220 Test: AImageReaderVendorTest, use hal client for cameraserver, GCA(sanity) Change-Id: Ie6f80e8ab530aa755df57f8d25d9f1a15a20d7f7 Signed-off-by: Jayant Chowdhary --- services/camera/libcameraservice/Android.bp | 1 + .../camera/libcameraservice/CameraService.cpp | 76 ++++++++++--------- .../camera/libcameraservice/CameraService.h | 2 +- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp index 7ec0e4ca7c..1c1f5e6e45 100644 --- a/services/camera/libcameraservice/Android.bp +++ b/services/camera/libcameraservice/Android.bp @@ -70,6 +70,7 @@ cc_library_shared { ], shared_libs: [ + "libbase", "libdl", "libexif", "libui", diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index a87ebdf770..3e6210294b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -81,6 +82,7 @@ namespace { namespace android { +using base::StringPrintf; using binder::Status; using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService; using hardware::ICamera; @@ -2366,6 +2368,13 @@ CameraService::BasicClient::BasicClient(const sp& cameraService, } mClientPackageName = packages[0]; } + if (hardware::IPCThreadState::self()->isServingCall()) { + std::string vendorClient = + StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid()); + mClientPackageName = String16(vendorClient.c_str()); + } else { + mAppOpsManager = std::make_unique(); + } } CameraService::BasicClient::~BasicClient() { @@ -2381,8 +2390,7 @@ binder::Status CameraService::BasicClient::disconnect() { mDisconnected = true; sCameraService->removeByClient(this); - sCameraService->logDisconnected(mCameraIdStr, mClientPid, - String8(mClientPackageName)); + sCameraService->logDisconnected(mCameraIdStr, mClientPid, String8(mClientPackageName)); sCameraService->mCameraProviderManager->removeRef(CameraProviderManager::DeviceMode::CAMERA, mCameraIdStr.c_str()); @@ -2432,31 +2440,31 @@ bool CameraService::BasicClient::canCastToApiClient(apiLevel level) const { status_t CameraService::BasicClient::startCameraOps() { ATRACE_CALL(); - int32_t res; - // Notify app ops that the camera is not available - mOpsCallback = new OpsCallback(this); - { ALOGV("%s: Start camera ops, package name = %s, client UID = %d", __FUNCTION__, String8(mClientPackageName).string(), mClientUid); } + if (mAppOpsManager != nullptr) { + // Notify app ops that the camera is not available + mOpsCallback = new OpsCallback(this); + int32_t res; + mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA, + mClientPackageName, mOpsCallback); + res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, + mClientUid, mClientPackageName, /*startIfModeDefault*/ false); - mAppOpsManager.startWatchingMode(AppOpsManager::OP_CAMERA, - mClientPackageName, mOpsCallback); - res = mAppOpsManager.startOpNoThrow(AppOpsManager::OP_CAMERA, - mClientUid, mClientPackageName, /*startIfModeDefault*/ false); - - if (res == AppOpsManager::MODE_ERRORED) { - ALOGI("Camera %s: Access for \"%s\" has been revoked", - mCameraIdStr.string(), String8(mClientPackageName).string()); - return PERMISSION_DENIED; - } + if (res == AppOpsManager::MODE_ERRORED) { + ALOGI("Camera %s: Access for \"%s\" has been revoked", + mCameraIdStr.string(), String8(mClientPackageName).string()); + return PERMISSION_DENIED; + } - if (res == AppOpsManager::MODE_IGNORED) { - ALOGI("Camera %s: Access for \"%s\" has been restricted", - mCameraIdStr.string(), String8(mClientPackageName).string()); - // Return the same error as for device policy manager rejection - return -EACCES; + if (res == AppOpsManager::MODE_IGNORED) { + ALOGI("Camera %s: Access for \"%s\" has been restricted", + mCameraIdStr.string(), String8(mClientPackageName).string()); + // Return the same error as for device policy manager rejection + return -EACCES; + } } mOpsActive = true; @@ -2483,10 +2491,11 @@ status_t CameraService::BasicClient::finishCameraOps() { // Check if startCameraOps succeeded, and if so, finish the camera op if (mOpsActive) { // Notify app ops that the camera is available again - mAppOpsManager.finishOp(AppOpsManager::OP_CAMERA, mClientUid, - mClientPackageName); - mOpsActive = false; - + if (mAppOpsManager != nullptr) { + mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid, + mClientPackageName); + mOpsActive = false; + } // This function is called when a client disconnects. This should // release the camera, but actually only if it was in a proper // functional state, i.e. with status NOT_AVAILABLE @@ -2506,8 +2515,8 @@ status_t CameraService::BasicClient::finishCameraOps() { mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel); } // Always stop watching, even if no camera op is active - if (mOpsCallback != NULL) { - mAppOpsManager.stopWatchingMode(mOpsCallback); + if (mOpsCallback != nullptr && mAppOpsManager != nullptr) { + mAppOpsManager->stopWatchingMode(mOpsCallback); } mOpsCallback.clear(); @@ -2516,19 +2525,18 @@ status_t CameraService::BasicClient::finishCameraOps() { return OK; } -void CameraService::BasicClient::opChanged(int32_t op, const String16& packageName) { +void CameraService::BasicClient::opChanged(int32_t op, const String16&) { ATRACE_CALL(); - - String8 name(packageName); - String8 myName(mClientPackageName); - + if (mAppOpsManager == nullptr) { + return; + } if (op != AppOpsManager::OP_CAMERA) { ALOGW("Unexpected app ops notification received: %d", op); return; } int32_t res; - res = mAppOpsManager.checkOp(AppOpsManager::OP_CAMERA, + res = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, mClientUid, mClientPackageName); ALOGV("checkOp returns: %d, %s ", res, res == AppOpsManager::MODE_ALLOWED ? "ALLOWED" : @@ -2538,7 +2546,7 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa if (res != AppOpsManager::MODE_ALLOWED) { ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.string(), - myName.string()); + String8(mClientPackageName).string()); block(); } } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index b8cec2c82b..065157dad4 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -293,7 +293,7 @@ public: status_t finishCameraOps(); private: - AppOpsManager mAppOpsManager; + std::unique_ptr mAppOpsManager = nullptr; class OpsCallback : public BnAppOpsCallback { public: -- GitLab From f30859cd4b94e6f855cc85969a7a9b50a7891dad Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Wed, 5 Jun 2019 09:11:39 -0700 Subject: [PATCH 1496/1530] [DO NOT MERGE] AMR WB encoder: prevent OOB write in ACELP_4t64_fx In ACELP_4t64_fx, when iterating over ind array, check index against array size to prevent OOB write, log an error if such an access was about to happen. Bug 132647222 Test: atest EncoderTest#testAMRWBEncoders Merged-in: I33f476d94baec2feffc7bcccd0ad0481b8452518 Change-Id: I814a05217f20ea3b941deddb70edb31cd342de6b --- media/libstagefright/codecs/amrwbenc/Android.mk | 3 ++- .../codecs/amrwbenc/SampleCode/Android.mk | 3 ++- media/libstagefright/codecs/amrwbenc/src/c4t64fx.c | 13 +++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk index 262f6c842a..573363217d 100644 --- a/media/libstagefright/codecs/amrwbenc/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/Android.mk @@ -92,7 +92,8 @@ LOCAL_ARM_MODE := arm LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := +LOCAL_SHARED_LIBRARIES := \ + liblog LOCAL_C_INCLUDES := \ frameworks/av/include \ diff --git a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk index 65d69a2c28..0d6549eea7 100644 --- a/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk +++ b/media/libstagefright/codecs/amrwbenc/SampleCode/Android.mk @@ -14,7 +14,8 @@ LOCAL_CFLAGS := LOCAL_SHARED_LIBRARIES := \ libstagefright \ - libdl + libdl \ + liblog LOCAL_STATIC_LIBRARIES := \ libstagefright_amrwbenc diff --git a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c index 8cebb097bd..b26eedb508 100644 --- a/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c +++ b/media/libstagefright/codecs/amrwbenc/src/c4t64fx.c @@ -47,6 +47,10 @@ #include "q_pulse.h" +#undef LOG_TAG +#define LOG_TAG "amrwbenc" +#include + static Word16 tipos[36] = { 0, 1, 2, 3, /* starting point &ipos[0], 1st iter */ 1, 2, 3, 0, /* starting point &ipos[4], 2nd iter */ @@ -745,11 +749,16 @@ void ACELP_4t64_fx( i = (Word16)((vo_L_mult(track, NPMAXPT) >> 1)); - while (ind[i] >= 0) + while (i < NPMAXPT * NB_TRACK && ind[i] >= 0) { i += 1; } - ind[i] = index; + if (i < NPMAXPT * NB_TRACK) { + ind[i] = index; + } else { + ALOGE("b/132647222, OOB access in ind array track=%d i=%d", track, i); + android_errorWriteLog(0x534e4554, "132647222"); + } } k = 0; -- GitLab From 13497d02b5bd63c4867e2284833a6bf57cdf119e Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 6 Jun 2019 13:54:12 -0700 Subject: [PATCH 1497/1530] Fix android.media.cts.MediaPlayerFlakyNetworkTest failure Added mDisconnectLock to seperate it from mLock, used for protecting mDataSource, mHttpSource and mDisconnected in order that disconnect() won't be blocked by mLock. Bug: 133900877 Test: android.media.cts.MediaPlayerFlakyNetworkTest Change-Id: I15693c0a61f898aad4035d0fa55c8af25de4fc96 --- .../nuplayer/GenericSource.cpp | 47 +++++++++++++++---- .../nuplayer/GenericSource.h | 1 + 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 5a58aa0939..5c77e41332 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -90,8 +90,11 @@ void NuPlayer::GenericSource::resetDataSource() { ALOGV("resetDataSource"); mHTTPService.clear(); - mHttpSource.clear(); - mDisconnected = false; + { + Mutex::Autolock _l_d(mDisconnectLock); + mHttpSource.clear(); + mDisconnected = false; + } mUri.clear(); mUriHeaders.clear(); mSources.clear(); @@ -152,7 +155,10 @@ status_t NuPlayer::GenericSource::setDataSource(const sp& source) { ALOGV("setDataSource (source: %p)", source.get()); resetDataSource(); - mDataSource = source; + { + Mutex::Autolock _l_d(mDisconnectLock); + mDataSource = source; + } return OK; } @@ -163,8 +169,12 @@ sp NuPlayer::GenericSource::getFileFormatMeta() const { status_t NuPlayer::GenericSource::initFromDataSource() { sp extractor; - CHECK(mDataSource != NULL); - sp dataSource = mDataSource; + sp dataSource; + { + Mutex::Autolock _l_d(mDisconnectLock); + dataSource = mDataSource; + } + CHECK(dataSource != NULL); mLock.unlock(); // This might take long time if data source is not reliable. @@ -359,6 +369,7 @@ void NuPlayer::GenericSource::prepareAsync() { } void NuPlayer::GenericSource::onPrepareAsync() { + mDisconnectLock.lock(); ALOGV("onPrepareAsync: mDataSource: %d", (mDataSource != NULL)); // delayed data source creation @@ -372,20 +383,30 @@ void NuPlayer::GenericSource::onPrepareAsync() { String8 contentType; if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) { - mHttpSource = DataSourceFactory::CreateMediaHTTP(mHTTPService); - if (mHttpSource == NULL) { + sp httpSource; + mDisconnectLock.unlock(); + httpSource = DataSourceFactory::CreateMediaHTTP(mHTTPService); + if (httpSource == NULL) { ALOGE("Failed to create http source!"); notifyPreparedAndCleanup(UNKNOWN_ERROR); + mDisconnectLock.lock(); return; } + mDisconnectLock.lock(); + + if (!mDisconnected) { + mHttpSource = httpSource; + } } mLock.unlock(); + mDisconnectLock.unlock(); // This might take long time if connection has some issue. sp dataSource = DataSourceFactory::CreateFromURI( mHTTPService, uri, &mUriHeaders, &contentType, static_cast(mHttpSource.get())); mLock.lock(); + mDisconnectLock.lock(); if (!mDisconnected) { mDataSource = dataSource; } @@ -437,6 +458,8 @@ void NuPlayer::GenericSource::onPrepareAsync() { mCachedSource = static_cast(mDataSource.get()); } + mDisconnectLock.unlock(); + // For cached streaming cases, we need to wait for enough // buffering before reporting prepared. mIsStreaming = (mCachedSource != NULL); @@ -503,9 +526,13 @@ void NuPlayer::GenericSource::finishPrepareAsync() { void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { if (err != OK) { - mDataSource.clear(); + { + Mutex::Autolock _l_d(mDisconnectLock); + mDataSource.clear(); + mHttpSource.clear(); + } + mCachedSource.clear(); - mHttpSource.clear(); mBitrate = -1; mPrevBufferPercentage = -1; @@ -547,7 +574,7 @@ void NuPlayer::GenericSource::resume() { void NuPlayer::GenericSource::disconnect() { sp dataSource, httpSource; { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l_d(mDisconnectLock); dataSource = mDataSource; httpSource = mHttpSource; mDisconnected = true; diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index 065cac1048..4d1905dc29 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -170,6 +170,7 @@ private: sp mGlobalTimedText; mutable Mutex mLock; + mutable Mutex mDisconnectLock; // Protects mDataSource, mHttpSource and mDisconnected sp mLooper; -- GitLab From 0080d7ec3d795a0e420abd20c6085329b7aa0eb1 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 6 Jun 2019 14:20:26 -0700 Subject: [PATCH 1498/1530] CCodec: fix i-frame-interval and aac configs Bug: 134533155 Test: manual Change-Id: I95ad7638148dfdfe7ecb76e8b2682e86c23812e8 Merged-In: I95ad7638148dfdfe7ecb76e8b2682e86c23812e8 --- media/codec2/sfplugin/CCodecConfig.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index 077a91f528..1cfdc19dad 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -508,7 +508,7 @@ void CCodecConfig::initializeStandardParams() { .limitTo(D::ENCODER & D::VIDEO)); // convert to timestamp base add(ConfigMapper(KEY_I_FRAME_INTERVAL, C2_PARAMKEY_SYNC_FRAME_INTERVAL, "value") - .withMapper([](C2Value v) -> C2Value { + .withMappers([](C2Value v) -> C2Value { // convert from i32 to float int32_t i32Value; float fpValue; @@ -518,6 +518,12 @@ void CCodecConfig::initializeStandardParams() { return int64_t(c2_min(1000000 * fpValue + 0.5, (double)INT64_MAX)); } return C2Value(); + }, [](C2Value v) -> C2Value { + int64_t i64; + if (v.get(&i64)) { + return float(i64) / 1000000; + } + return C2Value(); })); // remove when codecs switch to proper coding.gop (add support for calculating gop) deprecated(ConfigMapper("i-frame-period", "coding.gop", "intra-period") @@ -711,7 +717,7 @@ void CCodecConfig::initializeStandardParams() { // convert to dBFS and add default add(ConfigMapper(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < 0) { @@ -722,7 +728,7 @@ void CCodecConfig::initializeStandardParams() { // convert to 0-1 (%) and add default add(ConfigMapper(KEY_AAC_DRC_ATTENUATION_FACTOR, C2_PARAMKEY_DRC_ATTENUATION_FACTOR, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < 0) { @@ -733,7 +739,7 @@ void CCodecConfig::initializeStandardParams() { // convert to 0-1 (%) and add default add(ConfigMapper(KEY_AAC_DRC_BOOST_FACTOR, C2_PARAMKEY_DRC_BOOST_FACTOR, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < 0) { @@ -744,7 +750,7 @@ void CCodecConfig::initializeStandardParams() { // convert to compression type and add default add(ConfigMapper(KEY_AAC_DRC_HEAVY_COMPRESSION, C2_PARAMKEY_DRC_COMPRESSION_MODE, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < 0) { @@ -755,7 +761,7 @@ void CCodecConfig::initializeStandardParams() { // convert to dBFS and add default add(ConfigMapper(KEY_AAC_ENCODED_TARGET_LEVEL, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < 0) { @@ -766,7 +772,7 @@ void CCodecConfig::initializeStandardParams() { // convert to effect type (these map to SDK values) and add default add(ConfigMapper(KEY_AAC_DRC_EFFECT_TYPE, C2_PARAMKEY_DRC_EFFECT_TYPE, "value") - .limitTo(D::AUDIO & D::DECODER) + .limitTo(D::AUDIO & D::DECODER & D::CONFIG) .withMapper([](C2Value v) -> C2Value { int32_t value; if (!v.get(&value) || value < -1 || value > 8) { -- GitLab From ceb9efad60fbb25f523b9e54604cea59c55f363b Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 7 Jun 2019 13:20:56 -0700 Subject: [PATCH 1499/1530] qt-dev should be v290000000 and targetSdkVersion=29 Bug: 129546105 Change-Id: Ib035b5aab4f781b0b3a32b3c54c9efd2eb767a97 --- apex/manifest.json | 2 +- apex/manifest_codec.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/manifest.json b/apex/manifest.json index 03b9dd0930..3011ee832e 100644 --- a/apex/manifest.json +++ b/apex/manifest.json @@ -1,4 +1,4 @@ { "name": "com.android.media", - "version": 220000000 + "version": 290000000 } diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json index 58ce86879b..83a517859b 100644 --- a/apex/manifest_codec.json +++ b/apex/manifest_codec.json @@ -1,4 +1,4 @@ { "name": "com.android.media.swcodec", - "version": 220000000 + "version": 290000000 } -- GitLab From 5fee0f2b7c6fc2ac8ca7c908aef25864ce8894a2 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Mon, 10 Jun 2019 13:35:00 -0700 Subject: [PATCH 1500/1530] libhidltransport users user libhidlbase libhidltransport symbols are being moved into libhidlbase in order to optimize linking/memory usage. libhidltransport will no longer be required in the future (however removing references to it will come separately). Bug: 134961554 Test: boot Change-Id: Ie8b9b03a53ae1f5672ce2565550768b4bcd321ee --- camera/cameraserver/Android.bp | 1 + media/audioserver/Android.mk | 1 + services/mediacodec/Android.bp | 1 + services/mediacodec/Android.mk | 1 + 4 files changed, 4 insertions(+) diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp index 92b06c2d10..ecaba3ac11 100644 --- a/camera/cameraserver/Android.bp +++ b/camera/cameraserver/Android.bp @@ -24,6 +24,7 @@ cc_binary { "libui", "libgui", "libbinder", + "libhidlbase", "libhidltransport", "android.hardware.camera.common@1.0", "android.hardware.camera.provider@2.4", diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk index 1f24413801..33b36b806a 100644 --- a/media/audioserver/Android.mk +++ b/media/audioserver/Android.mk @@ -12,6 +12,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ + libhidlbase \ libhidltransport \ libhwbinder \ libmedia \ diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp index 32d2fde0fb..99a6d6b807 100644 --- a/services/mediacodec/Android.bp +++ b/services/mediacodec/Android.bp @@ -9,6 +9,7 @@ cc_binary { shared_libs: [ "libavservices_minijail", "libbase", + "libhidlbase", "libhidltransport", "libhwbinder", "liblog", diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk index ecd437b5db..ecc84087b3 100644 --- a/services/mediacodec/Android.mk +++ b/services/mediacodec/Android.mk @@ -40,6 +40,7 @@ LOCAL_SHARED_LIBRARIES := \ libavservices_minijail_vendor \ libcutils \ libhwbinder \ + libhidlbase \ libhidltransport \ libstagefright_omx \ libstagefright_xmlparser \ -- GitLab From 4e5f402253592726d05f4864b2f30b740abf15fc Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Sat, 8 Jun 2019 02:32:21 -0700 Subject: [PATCH 1501/1530] Fix remote-submix not found for MIX_TYPE_RECORDERS LOOPBACK [Context] When a LOOPBACK dynamic audio policy mix PLAYER/RECORDER is registered, first the REMOTE_SUBMIX device that will be used by the app to capture/play respectively is activated. Eg a mix RECORDER (used to inject audio to apps recording), when registered will activate an OUT_REMOTE_SUBMIX. Then the app can open a record/playback track on this REMOTE_SUBMIX, which will trigger the other end of the pipe to be activated. Following this the playback/Record Tracks that match the mix will be routed to the IN/OUT REMOTE_SUBMIX. [Example for an app FUU] 1) App FUU register Playback/record mix 2) APM activates out/in remote submix 3) App FUU opens track to out/in remote submix 4) APM activates in/out remote submix 5) tracks matching mix are redirected to the out/in remote submix When an OUT_REMOTE_SUBMIX is activated, it tries to check if it matches a mix, if it does, it saves the output in the mix (in mOutput). This output is then used to find a matching mix output in getOutputForAttr(). If a mix has no output is is considered inactive. Note that IN_REMOTE_SUBMIX do not save their input in the mix, the input is queried by APM each time in its mAvailableInputDevices. [Issue] The issue is that in step (2), when an OUT_REMOTE_SUBMIX is activated (thus for a MIX_TYPE_RECORDERS), no mix is matched because the mix is of type IN_REMOTE_SUBMIX as it will be used to route AudioTracks in step (5). Thus no mix output is found in getOutputForAttr and the step (3) fails. This is a recent regression as previously the type was not checked when looking up for a mix in step (1). The Change-Id of the patch that caused the regression is: I4dc7f23bef19a7d47afc2998102da07dde41fbca, its sha1 is: 679172768d86e29a47b9e15dd56776694128cf59 [Workaround] For now, given the release timing, implement a minimal fix that restore the previous behavior for the OUT_REMOTE_SUBMIX output save lookup. Aka, make sure to query for a IN_REMOTE_SUBMIX mix in step (2). Step (4) is fine as the mix type is the same as the REMOTE_SUBMIX type. As IN_REMOTE_SUBMIX do not attempt to save their output in the mix, no issue is present on this code path. A proper fix needs to be implemented on master, preferably by having the same device save behaviour on both mix type. Bug: 134068143 Test: Wifi calling call screening Test: atest AudioPlaybackCaptureTest AudioHostTest Change-Id: I1547948ae412dbdeb2d85cc62bf18f7ac5f1efc0 Signed-off-by: Kevin Rocard --- .../common/managerdefinitions/src/AudioPolicyMix.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index dca84c0bf6..c42923adff 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -120,8 +120,9 @@ status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType, ALOGV("getAudioPolicyMix() for dev=0x%x addr=%s", deviceType, address.string()); for (ssize_t i = 0; i < size(); i++) { - if (itemAt(i)->mDeviceType == deviceType - && itemAt(i)->mDeviceAddress.compare(address) == 0) { + // Workaround: when an in audio policy is registered, it opens an output + // that tries to find the audio policy, thus the device must be ignored. + if (itemAt(i)->mDeviceAddress.compare(address) == 0) { policyMix = itemAt(i); ALOGV("getAudioPolicyMix: found mix %zu match (devType=0x%x addr=%s)", i, deviceType, address.string()); -- GitLab From f9b4eb9e3f74ee03adccdf471d9e1073b6bc9d8c Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Mon, 10 Jun 2019 11:06:01 -0700 Subject: [PATCH 1502/1530] Camera: Compare old and new overrides for finishConfigure With the new dataSpace/format override logic, during finishConfiguration, we shouldn't check mDataSpaceOverride flag anymore because now mDataSpaceOverride will be true for reconfigure. Instead, we should check if the new override is the same as old override. Test: testPrepare on HAL device with dataSpace override Bug: 134800141 Change-Id: I1ddc258100dfd7e3c2cc86f9e476d8d52c710e3f --- .../camera/libcameraservice/device3/Camera3Stream.cpp | 8 +++++++- services/camera/libcameraservice/device3/Camera3Stream.h | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 6d7680240b..2df084b7bd 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -60,6 +60,8 @@ Camera3Stream::Camera3Stream(int id, mUsage(0), mOldUsage(0), mOldMaxBuffers(0), + mOldFormat(-1), + mOldDataSpace(HAL_DATASPACE_UNKNOWN), mPrepared(false), mPrepareBlockRequest(true), mPreparedBufferIdx(0), @@ -256,6 +258,8 @@ camera3_stream* Camera3Stream::startConfiguration() { mOldUsage = mUsage; mOldMaxBuffers = camera3_stream::max_buffers; + mOldFormat = camera3_stream::format; + mOldDataSpace = camera3_stream::data_space; res = getEndpointUsage(&mUsage); if (res != OK) { @@ -330,7 +334,9 @@ status_t Camera3Stream::finishConfiguration(/*out*/bool* streamReconfigured) { // so. As documented in hardware/camera3.h:configure_streams(). if (mState == STATE_IN_RECONFIG && mOldUsage == mUsage && - mOldMaxBuffers == camera3_stream::max_buffers && !mDataSpaceOverridden) { + mOldMaxBuffers == camera3_stream::max_buffers && + mOldDataSpace == camera3_stream::data_space && + mOldFormat == camera3_stream::format) { mState = STATE_CONFIGURED; return OK; } diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index c916fe8a01..533318faa6 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -540,8 +540,12 @@ class Camera3Stream : uint64_t mUsage; private: + // Previously configured stream properties (post HAL override) uint64_t mOldUsage; uint32_t mOldMaxBuffers; + int mOldFormat; + android_dataspace mOldDataSpace; + Condition mOutputBufferReturnedSignal; Condition mInputBufferReturnedSignal; static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms -- GitLab From 16f2fa2d5ef25a517376a0de4d07903483cf1f14 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Wed, 5 Jun 2019 10:19:55 -0700 Subject: [PATCH 1503/1530] OggWriter: Support format without CSD for opus If format doesn't contain CSD, then look for it when writing sample data. When CSD is present in both Format and sample data, the one in format is used like MPEG4Writer. Bug: 131747486 Bug: 134509194 Test: poc from bug Change-Id: If2cab79d540688283afb4d74a6b043ea2e089ef0 --- media/libstagefright/OggWriter.cpp | 49 ++++++++++++++++--- .../include/media/stagefright/OggWriter.h | 3 ++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/OggWriter.cpp b/media/libstagefright/OggWriter.cpp index cb87b558ab..b738fef63c 100644 --- a/media/libstagefright/OggWriter.cpp +++ b/media/libstagefright/OggWriter.cpp @@ -52,6 +52,7 @@ namespace android { OggWriter::OggWriter(int fd) : mFd(dup(fd)), + mHaveAllCodecSpecificData(false), mInitCheck(mFd < 0 ? NO_INIT : OK) { // empty } @@ -115,17 +116,26 @@ status_t OggWriter::addSource(const sp& source) { mSampleRate = sampleRate; uint32_t type; - const void *header_data; - size_t packet_size; + const void *header_data = NULL; + size_t packet_size = 0; + if (!source->getFormat()->findData(kKeyOpusHeader, &type, &header_data, &packet_size)) { - ALOGE("opus header not found"); - return UNKNOWN_ERROR; + ALOGV("opus header not found in format"); + } else if (header_data && packet_size) { + writeOggHeaderPackets((unsigned char *)header_data, packet_size); + } else { + ALOGD("ignoring incomplete opus header data in format"); } + mSource = source; + return OK; +} + +status_t OggWriter::writeOggHeaderPackets(unsigned char *buf, size_t size) { ogg_packet op; ogg_page og; - op.packet = (unsigned char *)header_data; - op.bytes = packet_size; + op.packet = buf; + op.bytes = size; op.b_o_s = 1; op.e_o_s = 0; op.granulepos = 0; @@ -169,8 +179,8 @@ status_t OggWriter::addSource(const sp& source) { write(mFd, og.body, og.body_len); } - mSource = source; free(comments); + mHaveAllCodecSpecificData = true; return OK; } @@ -301,12 +311,35 @@ status_t OggWriter::threadFunc() { && isCodecSpecific) || IsOpusHeader((uint8_t*)buffer->data() + buffer->range_offset(), buffer->range_length())) { - ALOGV("Drop codec specific info buffer"); + if (mHaveAllCodecSpecificData == false) { + size_t opusHeadSize = 0; + size_t codecDelayBufSize = 0; + size_t seekPreRollBufSize = 0; + void *opusHeadBuf = NULL; + void *codecDelayBuf = NULL; + void *seekPreRollBuf = NULL; + GetOpusHeaderBuffers((uint8_t*)buffer->data() + buffer->range_offset(), + buffer->range_length(), &opusHeadBuf, + &opusHeadSize, &codecDelayBuf, + &codecDelayBufSize, &seekPreRollBuf, + &seekPreRollBufSize); + writeOggHeaderPackets((unsigned char *)opusHeadBuf, opusHeadSize); + } else { + ALOGV("ignoring later copy of CSD contained in info buffer"); + } buffer->release(); buffer = nullptr; continue; } + if (mHaveAllCodecSpecificData == false) { + ALOGE("Did not get valid opus header before first sample data"); + buffer->release(); + buffer = nullptr; + err = ERROR_MALFORMED; + break; + } + int64_t timestampUs; CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs)); if (timestampUs > mEstimatedDurationUs) { diff --git a/media/libstagefright/include/media/stagefright/OggWriter.h b/media/libstagefright/include/media/stagefright/OggWriter.h index e3837cd9bb..1a0a1d2958 100644 --- a/media/libstagefright/include/media/stagefright/OggWriter.h +++ b/media/libstagefright/include/media/stagefright/OggWriter.h @@ -43,6 +43,7 @@ protected: private: int mFd; + bool mHaveAllCodecSpecificData; status_t mInitCheck; sp mSource; bool mStarted = false; @@ -66,6 +67,8 @@ private: OggWriter(const OggWriter&); OggWriter& operator=(const OggWriter&); + + status_t writeOggHeaderPackets(unsigned char *buf, size_t size); }; } // namespace android -- GitLab From 6ede98f9b3e3e1988f401d79b625ead653027ccc Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 11 Jun 2019 14:50:30 -0700 Subject: [PATCH 1504/1530] audioservice: add RTT mode observer Enable audio capture by the Assistant when RTT is ON during a call similarly to when an accessibility service is in the foreground. Also remove log when permission checked for hotword and audio output capture fail as those checks are now done systematically. Also do not check permission to bypass interruption policy if bypass flags are not set. Bug: 132976361 Test: use voice input during a call with RTT enabled. Change-Id: Iff45b0816dac889262ec29ea115f74dea4dc6b6d --- media/libaudioclient/AudioSystem.cpp | 7 ++++++ media/libaudioclient/IAudioPolicyService.cpp | 24 ++++++++++++++++++- .../include/media/AudioSystem.h | 2 ++ .../include/media/IAudioPolicyService.h | 2 ++ media/utils/ServiceUtilities.cpp | 4 ++-- .../service/AudioPolicyInterfaceImpl.cpp | 11 ++++++++- .../service/AudioPolicyService.cpp | 9 ++++--- .../audiopolicy/service/AudioPolicyService.h | 8 ++++++- 8 files changed, 59 insertions(+), 8 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 1e7f9fa7a4..02dc516e8a 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1512,6 +1512,13 @@ status_t AudioSystem::getVolumeGroupFromAudioAttributes(const AudioAttributes &a return aps->getVolumeGroupFromAudioAttributes(aa, volumeGroup); } +status_t AudioSystem::setRttEnabled(bool enabled) +{ + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return PERMISSION_DENIED; + return aps->setRttEnabled(enabled); +} + // --------------------------------------------------------------------------- int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback( diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp index c5484570d4..64f0aca6cd 100644 --- a/media/libaudioclient/IAudioPolicyService.cpp +++ b/media/libaudioclient/IAudioPolicyService.cpp @@ -104,6 +104,7 @@ enum { GET_VOLUME_GROUP_FOR_ATTRIBUTES, SET_ALLOWED_CAPTURE_POLICY, MOVE_EFFECTS_TO_IO, + SET_RTT_ENABLED }; #define MAX_ITEMS_PER_LIST 1024 @@ -1271,6 +1272,18 @@ public: volumeGroup = static_cast(reply.readInt32()); return NO_ERROR; } + + virtual status_t setRttEnabled(bool enabled) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); + data.writeInt32(static_cast(enabled)); + status_t status = remote()->transact(SET_RTT_ENABLED, data, &reply); + if (status != NO_ERROR) { + return status; + } + return static_cast(reply.readInt32()); + } }; IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService"); @@ -1332,7 +1345,8 @@ status_t BnAudioPolicyService::onTransact( case REMOVE_UID_DEVICE_AFFINITY: case GET_OFFLOAD_FORMATS_A2DP: case LIST_AUDIO_VOLUME_GROUPS: - case GET_VOLUME_GROUP_FOR_ATTRIBUTES: { + case GET_VOLUME_GROUP_FOR_ATTRIBUTES: + case SET_RTT_ENABLED: { if (!isServiceUid(IPCThreadState::self()->getCallingUid())) { ALOGW("%s: transaction %d received from PID %d unauthorized UID %d", __func__, code, IPCThreadState::self()->getCallingPid(), @@ -2347,6 +2361,14 @@ status_t BnAudioPolicyService::onTransact( return NO_ERROR; } + case SET_RTT_ENABLED: { + CHECK_INTERFACE(IAudioPolicyService, data, reply); + bool enabled = static_cast(data.readInt32()); + status_t status = setRttEnabled(enabled); + reply->writeInt32(status); + return NO_ERROR; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 56c69f6b56..09e80b253a 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -394,6 +394,8 @@ public: static status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, volume_group_t &volumeGroup); + static status_t setRttEnabled(bool enabled); + // ---------------------------------------------------------------------------- class AudioVolumeGroupCallback : public RefBase diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h index 33ab1f956b..32275cf0ef 100644 --- a/media/libaudioclient/include/media/IAudioPolicyService.h +++ b/media/libaudioclient/include/media/IAudioPolicyService.h @@ -220,6 +220,8 @@ public: virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) = 0; virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, volume_group_t &volumeGroup) = 0; + + virtual status_t setRttEnabled(bool enabled) = 0; }; diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp index 16fdeaf064..b824212b51 100644 --- a/media/utils/ServiceUtilities.cpp +++ b/media/utils/ServiceUtilities.cpp @@ -128,7 +128,7 @@ bool captureAudioOutputAllowed(pid_t pid, uid_t uid) { if (isAudioServerOrRootUid(uid)) return true; static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT"); bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid); - if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT"); + if (!ok) ALOGV("Request requires android.permission.CAPTURE_AUDIO_OUTPUT"); return ok; } @@ -149,7 +149,7 @@ bool captureHotwordAllowed(const String16& opPackageName, pid_t pid, uid_t uid) // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. ok = PermissionCache::checkPermission(sCaptureHotwordAllowed, pid, uid); } - if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD"); + if (!ok) ALOGV("android.permission.CAPTURE_AUDIO_HOTWORD"); return ok; } diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index b0a85415af..fa8da8916e 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -193,7 +193,8 @@ status_t AudioPolicyService::getOutputForAttr(audio_attributes_t *attr, if (!mPackageManager.allowPlaybackCapture(uid)) { attr->flags |= AUDIO_FLAG_NO_MEDIA_PROJECTION; } - if (!bypassInterruptionPolicyAllowed(pid, uid)) { + if (((attr->flags & (AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE)) != 0) + && !bypassInterruptionPolicyAllowed(pid, uid)) { attr->flags &= ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE); } audio_output_flags_t originalFlags = flags; @@ -1316,4 +1317,12 @@ status_t AudioPolicyService::getVolumeGroupFromAudioAttributes(const AudioAttrib Mutex::Autolock _l(mLock); return mAudioPolicyManager->getVolumeGroupFromAudioAttributes(aa, volumeGroup); } + +status_t AudioPolicyService::setRttEnabled(bool enabled) +{ + Mutex::Autolock _l(mLock); + mUidPolicy->setRttEnabled(enabled); + return NO_ERROR; +} + } // namespace android diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index e3c72aeb80..77f7997a2a 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -409,7 +409,7 @@ void AudioPolicyService::updateUidStates_l() // following cases: // Another client in the same UID has already been allowed to capture // OR The client is the assistant -// AND an accessibility service is on TOP +// AND an accessibility service is on TOP or a RTT call is active // AND the source is VOICE_RECOGNITION or HOTWORD // OR uses VOICE_RECOGNITION AND is on TOP // OR uses HOTWORD @@ -436,6 +436,9 @@ void AudioPolicyService::updateUidStates_l() bool isAssistantOnTop = false; bool isSensitiveActive = false; bool isInCall = mPhoneState == AUDIO_MODE_IN_CALL; + bool rttCallActive = + (mPhoneState == AUDIO_MODE_IN_CALL || mPhoneState == AUDIO_MODE_IN_COMMUNICATION) + && mUidPolicy->isRttEnabled(); // if Sensor Privacy is enabled then all recordings should be silenced. if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { @@ -518,13 +521,13 @@ void AudioPolicyService::updateUidStates_l() allowCapture = true; } else if (mUidPolicy->isAssistantUid(current->uid)) { // For assistant allow capture if: - // An accessibility service is on TOP + // An accessibility service is on TOP or a RTT call is active // AND the source is VOICE_RECOGNITION or HOTWORD // OR is on TOP AND uses VOICE_RECOGNITION // OR uses HOTWORD // AND there is no active privacy sensitive capture or call // OR client has CAPTURE_AUDIO_OUTPUT privileged permission - if (isA11yOnTop) { + if (isA11yOnTop || rttCallActive) { if (source == AUDIO_SOURCE_HOTWORD || source == AUDIO_SOURCE_VOICE_RECOGNITION) { allowCapture = true; } diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 8db63a5773..e467f704b3 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -256,6 +256,8 @@ public: virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa, volume_group_t &volumeGroup); + virtual status_t setRttEnabled(bool enabled); + status_t doStopOutput(audio_port_handle_t portId); void doReleaseOutput(audio_port_handle_t portId); @@ -345,7 +347,8 @@ private: class UidPolicy : public BnUidObserver, public virtual IBinder::DeathRecipient { public: explicit UidPolicy(wp service) - : mService(service), mObserverRegistered(false), mAssistantUid(0) {} + : mService(service), mObserverRegistered(false), + mAssistantUid(0), mRttEnabled(false) {} void registerSelf(); void unregisterSelf(); @@ -360,6 +363,8 @@ private: void setA11yUids(const std::vector& uids) { mA11yUids.clear(); mA11yUids = uids; } bool isA11yUid(uid_t uid); bool isA11yOnTop(); + void setRttEnabled(bool enabled) { mRttEnabled = enabled; } + bool isRttEnabled() { return mRttEnabled; } // BnUidObserver implementation void onUidActive(uid_t uid) override; @@ -387,6 +392,7 @@ private: std::unordered_map> mCachedUids; uid_t mAssistantUid; std::vector mA11yUids; + bool mRttEnabled; }; // If sensor privacy is enabled then all apps, including those that are active, should be -- GitLab From 33d60a5f0c4249cf8350bc61d16d11b7a6d09c85 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 12 Jun 2019 13:45:01 -0700 Subject: [PATCH 1505/1530] avoid crash in emptyBuffer when input buffer handle is invalid Try to fail with error instead of crashing when the input buffer handle to TWOmxNode::emptyBuffer is invalid. Save an unnecessary handle clone, and let the convertTo to GraphicBuffer do the clone. If the clone fails, return with error. bug: 133254412 test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I6abb5526d8df1e57b70c96f5b32d132e4a5de389 Change-Id: If5ca8fae449a3cdf790c967add3713ad73369f03 --- .../include/media/omx/1.0/Conversion.h | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h index 80e8f3a7bd..6dc46b7943 100644 --- a/media/libmedia/include/media/omx/1.0/Conversion.h +++ b/media/libmedia/include/media/omx/1.0/Conversion.h @@ -625,8 +625,18 @@ inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { // convert: AnwBuffer -> GraphicBuffer // Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { - native_handle_t* handle = t.nativeHandle == nullptr ? - nullptr : native_handle_clone(t.nativeHandle); + native_handle_t* handle = nullptr; + + if (t.nativeHandle != nullptr) { + handle = native_handle_clone(t.nativeHandle); + if (handle == nullptr) { + ALOGE("Failed to clone handle: numFds=%d, data[0]=%d, data[1]=%d", + t.nativeHandle->numFds, + (t.nativeHandle->numFds > 0) ? t.nativeHandle->data[0] : -1, + (t.nativeHandle->numFds > 1) ? t.nativeHandle->data[1] : -1); + return false; + } + } size_t const numInts = 12 + (handle ? handle->numInts : 0); int32_t* ints = new int32_t[numInts]; @@ -756,7 +766,12 @@ inline bool convertTo(OMXBuffer* l, CodecBuffer const& t) { return true; } AnwBuffer anwBuffer; - anwBuffer.nativeHandle = t.nativeHandle; + // Explicitly get the native_handle_t* (in stead of assigning t.nativeHandle) + // so that we don't do an extra native_handle_clone() in this step, as the + // convertion to GraphicBuffer below will do a clone regardless. + // If we encounter an invalid handle, the convertTo() below would fail (while + // the assigning of hidl_handle would abort and cause a crash). + anwBuffer.nativeHandle = t.nativeHandle.getNativeHandle(); anwBuffer.attr = t.attr.anwBuffer; sp graphicBuffer = new GraphicBuffer(); if (!convertTo(graphicBuffer.get(), anwBuffer)) { -- GitLab From 13d3d83b02506c73f921d825036b66f855d9647e Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 10 Jun 2019 14:36:48 -0700 Subject: [PATCH 1506/1530] aaudio: fix STOP hang by unlocking around join() Unlock and lock around the join() because the callback thread that we are joining may be blocked by the lock. Check for DISCONNECTED state so the error callback is also joined correctly. Remove unnecessary waitForStateChange that could cause a timeout delay on DISCONNECT. Bug: 134963902 Test: test_stop_hang (see bug report) Change-Id: I5080fa655da35b3221234a2bcafb6ccd71d3ca27 --- .../src/client/AudioStreamInternal.cpp | 29 ++++++++++++------- .../src/client/AudioStreamInternalPlay.cpp | 1 + media/libaaudio/src/core/AudioStream.cpp | 6 ++++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index c7e8088b02..52eadd43d0 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -241,22 +241,18 @@ error: return result; } +// This must be called under mStreamLock. aaudio_result_t AudioStreamInternal::close() { aaudio_result_t result = AAUDIO_OK; ALOGV("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle); if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) { // Don't close a stream while it is running. aaudio_stream_state_t currentState = getState(); - if (isActive()) { + // Don't close a stream while it is running. Stop it first. + // If DISCONNECTED then we should still try to stop in case the + // error callback is still running. + if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) { requestStop(); - aaudio_stream_state_t nextState; - int64_t timeoutNanoseconds = MIN_TIMEOUT_NANOS; - result = waitForStateChange(currentState, &nextState, - timeoutNanoseconds); - if (result != AAUDIO_OK) { - ALOGW("%s() waitForStateChange() returned %d %s", - __func__, result, AAudio_convertResultToText(result)); - } } setState(AAUDIO_STREAM_STATE_CLOSING); aaudio_handle_t serviceStreamHandle = mServiceStreamHandle; @@ -357,21 +353,31 @@ int64_t AudioStreamInternal::calculateReasonableTimeout() { return calculateReasonableTimeout(getFramesPerBurst()); } +// This must be called under mStreamLock. aaudio_result_t AudioStreamInternal::stopCallback() { - if (isDataCallbackActive()) { + if (isDataCallbackSet() + && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) { mCallbackEnabled.store(false); - return joinThread(NULL); + return joinThread(NULL); // may temporarily unlock mStreamLock } else { return AAUDIO_OK; } } +// This must be called under mStreamLock. aaudio_result_t AudioStreamInternal::requestStop() { aaudio_result_t result = stopCallback(); if (result != AAUDIO_OK) { return result; } + // The stream may have been unlocked temporarily to let a callback finish + // and the callback may have stopped the stream. + // Check to make sure the stream still needs to be stopped. + // See also AudioStream::safeStop(). + if (!(isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) { + return AAUDIO_OK; + } if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) { ALOGW("%s() mServiceStreamHandle invalid = 0x%08X", @@ -728,6 +734,7 @@ int32_t AudioStreamInternal::getFramesPerBurst() const { return mFramesPerBurst; } +// This must be called under mStreamLock. aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) { return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst())); } diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 164ad2b786..b8ef247c80 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -58,6 +58,7 @@ aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) return result; } +// This must be called under mStreamLock. aaudio_result_t AudioStreamInternalPlay::requestPause() { aaudio_result_t result = stopCallback(); diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp index 25669bed74..9b772237e0 100644 --- a/media/libaaudio/src/core/AudioStream.cpp +++ b/media/libaaudio/src/core/AudioStream.cpp @@ -211,6 +211,7 @@ aaudio_result_t AudioStream::systemStopFromApp() { return result; } +// This must be called under mStreamLock. aaudio_result_t AudioStream::safeStop() { switch (getState()) { @@ -247,6 +248,7 @@ aaudio_result_t AudioStream::safeStop() { } aaudio_result_t AudioStream::safeClose() { + // This get temporarily unlocked in the close when joining callback threads. std::lock_guard lock(mStreamLock); if (collidesWithCallback()) { ALOGE("%s cannot be called from a callback!", __func__); @@ -363,6 +365,7 @@ aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds, } } +// This must be called under mStreamLock. aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds __unused) { if (!mHasThread) { @@ -374,6 +377,8 @@ aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanosec // then we don't need to join(). The thread is already about to exit. if (pthread_self() != mThread) { // Called from an app thread. Not the callback. + // Unlock because the callback may be trying to stop the stream but is blocked. + mStreamLock.unlock(); #if 0 // TODO implement equivalent of pthread_timedjoin_np() struct timespec abstime; @@ -381,6 +386,7 @@ aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanosec #else int err = pthread_join(mThread, returnArg); #endif + mStreamLock.lock(); if (err) { ALOGE("%s() pthread_join() returns err = %d", __func__, err); result = AAudioConvert_androidToAAudioResult(-err); -- GitLab From c4270cc3a9287d72163cacd0cadfa91523764855 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 13 Jun 2019 11:32:07 -0700 Subject: [PATCH 1507/1530] fix bug: release mDisconnectLock when early terminate Bug: 134995545 Tests: (1) described in buganizer comment #2 (2) atest CtsCameraTestCases:android.hardware.camera2.cts.FastBasicsTest#testCamera1 Change-Id: Ie134e503cd7602a754b57bcc5c1355dea19d4eab --- media/libmediaplayerservice/nuplayer/GenericSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 5c77e41332..0f72c0dd80 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -389,7 +389,6 @@ void NuPlayer::GenericSource::onPrepareAsync() { if (httpSource == NULL) { ALOGE("Failed to create http source!"); notifyPreparedAndCleanup(UNKNOWN_ERROR); - mDisconnectLock.lock(); return; } mDisconnectLock.lock(); @@ -449,6 +448,7 @@ void NuPlayer::GenericSource::onPrepareAsync() { if (mDataSource == NULL) { ALOGE("Failed to create data source!"); + mDisconnectLock.unlock(); notifyPreparedAndCleanup(UNKNOWN_ERROR); return; } -- GitLab From 42471cc7beb1703730e15401362b8bf02d1b0cfa Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 10 Jun 2019 14:39:01 -0700 Subject: [PATCH 1508/1530] aaudio test: trigger race condition and hang Close a stream at the same time the callback is trying to stop the stream. This replicates a hang that was detected in SynthMark. Bug: 134963902 Test: this is a test Change-Id: I7c87ecfefa2498fd1f4f9a1e406118c42a78545b --- media/libaaudio/tests/Android.bp | 12 ++ media/libaaudio/tests/test_stop_hang.cpp | 159 +++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 media/libaaudio/tests/test_stop_hang.cpp diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index 958bb2e323..6101e99aba 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -197,6 +197,18 @@ cc_test { ], } +cc_test { + name: "test_stop_hang", + defaults: ["libaaudio_tests_defaults"], + srcs: ["test_stop_hang.cpp"], + shared_libs: [ + "libaaudio", + "libbinder", + "libcutils", + "libutils", + ], +} + cc_test { name: "test_full_queue", defaults: ["libaaudio_tests_defaults"], diff --git a/media/libaaudio/tests/test_stop_hang.cpp b/media/libaaudio/tests/test_stop_hang.cpp new file mode 100644 index 0000000000..2397b6ca13 --- /dev/null +++ b/media/libaaudio/tests/test_stop_hang.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2019 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. + */ + +/** + * Return stop from the callback + * and then close the stream immediately. + */ + +#include +#include +#include +#include +#include + +#include + +#define DURATION_SECONDS 5 + +struct AudioEngine { + AAudioStreamBuilder *builder = nullptr; + AAudioStream *stream = nullptr; + std::thread *thread = nullptr; + + std::atomic started{false}; + std::mutex doneLock; // Use a mutex so we can sleep on it while join()ing. + std::atomic done{false}; + + aaudio_result_t join() { + aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE; + if (stream != nullptr) { + while (true) { + { + // Will block if the thread is running. + // This mutex is used to close() immediately after the callback returns + // and before the requestStop() is called. + std::lock_guard lock(doneLock); + if (done) break; + } + printf("join() got mutex but stream not done!"); + usleep(10 * 1000); // sleep then check again + } + result = AAudioStream_close(stream); + stream = nullptr; + } + return result; + } +}; + +// Callback function that fills the audio output buffer. +static aaudio_data_callback_result_t s_myDataCallbackProc( + AAudioStream *stream, + void *userData, + void *audioData, + int32_t numFrames +) { + (void) stream; + (void) audioData; + (void) numFrames; + AudioEngine *engine = (struct AudioEngine *)userData; + std::lock_guard lock(engine->doneLock); + engine->started = true; + usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure. + engine->done = true; + return AAUDIO_CALLBACK_RESULT_STOP; +} + +static void s_myErrorCallbackProc( + AAudioStream *stream __unused, + void *userData __unused, + aaudio_result_t error) { + printf("%s() - error = %d\n", __func__, error); +} + +static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) { + // Use an AAudioStreamBuilder to contain requested parameters. + aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder); + if (result != AAUDIO_OK) { + printf("AAudio_createStreamBuilder returned %s", + AAudio_convertResultToText(result)); + return result; + } + + // Request stream properties. + AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine); + AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine); + + // Create an AAudioStream using the Builder. + result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream); + if (result != AAUDIO_OK) { + printf("AAudioStreamBuilder_openStream returned %s", + AAudio_convertResultToText(result)); + return result; + } + + return result; +} + +int main(int argc, char **argv) { + (void) argc; + (void) argv; + struct AudioEngine engine; + aaudio_result_t result = AAUDIO_OK; + int errorCount = 0; + + // Make printf print immediately so that debug info is not stuck + // in a buffer if we hang or crash. + setvbuf(stdout, nullptr, _IONBF, (size_t) 0); + + printf("Test Return Stop Hang V1.0\n"); + + result = s_OpenAudioStream(&engine); + if (result != AAUDIO_OK) { + printf("s_OpenAudioStream returned %s\n", + AAudio_convertResultToText(result)); + errorCount++; + } + + // Check to see what kind of stream we actually got. + int32_t deviceId = AAudioStream_getDeviceId(engine.stream); + aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream); + printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode); + + // Start stream. + result = AAudioStream_requestStart(engine.stream); + printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result); + if (result != AAUDIO_OK) { + errorCount++; + } else { + int counter = 0; + while (!engine.started) { + printf("Waiting for stream to start, %d\n", counter++); + usleep(5 * 1000); + } + printf("You should see more messages %d seconds after this. If not then the test failed!\n", + DURATION_SECONDS); + result = engine.join(); // This might hang! + AAudioStreamBuilder_delete(engine.builder); + engine.builder = nullptr; + } + + printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result)); + printf("test %s\n", errorCount ? "FAILED" : "PASSED"); + + return errorCount ? EXIT_FAILURE : EXIT_SUCCESS; +} -- GitLab From fad001d05d2a928d6196978ba94adf95f0297244 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 11 Jun 2019 19:17:57 -0700 Subject: [PATCH 1509/1530] audio policy: mute call RX when SCO volume is 0 Set voice volume to 0 when SCO volume index is 0 to allow muting RX audio. when index is not 0, voice volume is set to max to allow volume control by headset as before. Bug: 111195322 Test: check call volume with SCO headset Change-Id: I90466b01a9899929cd3e6fb1ac0a4985099497fc --- services/audiopolicy/managerdefault/AudioPolicyManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 3ca759133b..a6730fc4be 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -5855,11 +5855,11 @@ status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves, if (isVoiceVolSrc || isBtScoVolSrc) { float voiceVolume; - // Force voice volume to max for bluetooth SCO as volume is managed by the headset + // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset if (isVoiceVolSrc) { voiceVolume = (float)index/(float)curves.getVolumeIndexMax(); } else { - voiceVolume = 1.0; + voiceVolume = index == 0 ? 0.0 : 1.0; } if (voiceVolume != mLastVoiceVolume) { mpClientInterface->setVoiceVolume(voiceVolume, delayMs); -- GitLab From 08515811a510b9a9dce6072b9ae39fe894784f92 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Wed, 5 Jun 2019 11:16:32 -0700 Subject: [PATCH 1510/1530] Migrate buffers during surface change Migrate graphic buffers during surface change in order to avoid BufferQueue handling complexity later on. Test: Manually using Chrome and google photo app Bug: 132302078 Bug: 130862880 Change-Id: Ifb348b5d6a8f5a89dcc10a9f0be075057a5d3a6d --- .../hidl/1.0/utils/ClientBlockHelper.cpp | 269 +++++++++---- media/codec2/hidl/1.0/utils/Component.cpp | 8 +- .../codec2/hidl/1.0/ClientBlockHelper.h | 80 ++-- .../1.0/utils/include/codec2/hidl/1.0/types.h | 56 ++- media/codec2/hidl/1.0/utils/types.cpp | 51 ++- media/codec2/hidl/client/client.cpp | 125 ++---- .../hidl/client/include/codec2/hidl/client.h | 6 +- media/codec2/sfplugin/CCodecBufferChannel.cpp | 2 +- media/codec2/vndk/C2AllocatorGralloc.cpp | 23 ++ .../codec2/vndk/include/C2AllocatorGralloc.h | 10 + media/codec2/vndk/internal/C2BlockInternal.h | 160 +++++--- media/codec2/vndk/platform/C2BqBuffer.cpp | 355 ++++++++++++++++-- 12 files changed, 801 insertions(+), 344 deletions(-) diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp index 1786c69e90..50790bcb53 100644 --- a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp +++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp @@ -82,21 +82,14 @@ void forEachBlock(C2FrameData& frameData, template void forEachBlock(const std::list>& workList, - BlockProcessor process, - bool processInput, bool processOutput) { + BlockProcessor process) { for (const std::unique_ptr& work : workList) { if (!work) { continue; } - if (processInput) { - forEachBlock(work->input, process); - } - if (processOutput) { - for (const std::unique_ptr& worklet : work->worklets) { - if (worklet) { - forEachBlock(worklet->output, - process); - } + for (const std::unique_ptr& worklet : work->worklets) { + if (worklet) { + forEachBlock(worklet->output, process); } } } @@ -109,8 +102,6 @@ sp getHgbp(const sp& igbp) { new B2HGraphicBufferProducer(igbp); } -} // unnamed namespace - status_t attachToBufferQueue(const C2ConstGraphicBlock& block, const sp& igbp, uint32_t generation, @@ -154,73 +145,221 @@ bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, _C2BlockFactory::GetGraphicBlockPoolData(block), generation, bqId, bqSlot); } +} // unnamed namespace -bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, - const sp& igbp, - uint64_t bqId, - uint32_t generation) { - std::shared_ptr<_C2BlockPoolData> data = - _C2BlockFactory::GetGraphicBlockPoolData(block); - if (!data) { - return false; - } +class OutputBufferQueue::Impl { + std::mutex mMutex; + sp mIgbp; + uint32_t mGeneration; + uint64_t mBqId; + 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]; - uint32_t oldGeneration; - uint64_t oldId; - int32_t oldSlot; - // If the block is not bufferqueue-based, do nothing. - if (!_C2BlockFactory::GetBufferQueueData( - data, &oldGeneration, &oldId, &oldSlot) || - (oldId == 0)) { - return false; - } +public: + Impl(): mGeneration(0), mBqId(0) {} - // If the block's bqId is the same as the desired bqId, just hold. - if ((oldId == bqId) && (oldGeneration == generation)) { - LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" - << " bqId " << oldId - << ", bqSlot " << oldSlot - << ", generation " << generation - << "."; - _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp)); + bool configure(const sp& igbp, + uint32_t generation, + uint64_t bqId) { + size_t tryNum = 0; + size_t success = 0; + sp buffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; + std::weak_ptr<_C2BlockPoolData> + poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS]; + { + std::scoped_lock l(mMutex); + if (generation == mGeneration) { + return false; + } + mIgbp = igbp; + mGeneration = generation; + mBqId = bqId; + mOwner = std::make_shared(0); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) { + if (mBqId == 0 || !mBuffers[i]) { + continue; + } + std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock(); + if (!data || + !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) { + continue; + } + ++tryNum; + int bqSlot; + mBuffers[i]->setGenerationNumber(generation); + status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]); + if (result != OK) { + continue; + } + bool attach = + _C2BlockFactory::EndAttachBlockToBufferQueue( + data, mOwner, getHgbp(mIgbp), + generation, bqId, bqSlot); + if (!attach) { + igbp->cancelBuffer(bqSlot, Fence::NO_FENCE); + continue; + } + buffers[bqSlot] = mBuffers[i]; + poolDatas[bqSlot] = data; + ++success; + } + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) { + mBuffers[i] = buffers[i]; + mPoolDatas[i] = poolDatas[i]; + } + } + ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum); return true; } - // Otherwise, attach to the given igbp, which must not be null. - if (!igbp) { + bool registerBuffer(const C2ConstGraphicBlock& block) { + std::shared_ptr<_C2BlockPoolData> data = + _C2BlockFactory::GetGraphicBlockPoolData(block); + if (!data) { + return false; + } + std::scoped_lock l(mMutex); + + if (!mIgbp) { + return false; + } + + uint32_t oldGeneration; + uint64_t oldId; + int32_t oldSlot; + // If the block is not bufferqueue-based, do nothing. + if (!_C2BlockFactory::GetBufferQueueData( + data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) { + return false; + } + // If the block's bqId is the same as the desired bqId, just hold. + if ((oldId == mBqId) && (oldGeneration == mGeneration)) { + LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:" + << " bqId " << oldId + << ", bqSlot " << oldSlot + << ", generation " << mGeneration + << "."; + _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp)); + mPoolDatas[oldSlot] = data; + mBuffers[oldSlot] = createGraphicBuffer(block); + mBuffers[oldSlot]->setGenerationNumber(mGeneration); + return true; + } + int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration; + LOG(WARNING) << "receiving stale buffer: generation " + << mGeneration << " , diff " << d << " : slot " + << oldSlot; return false; } - int32_t bqSlot; - status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot); + status_t outputBuffer( + const C2ConstGraphicBlock& block, + const BnGraphicBufferProducer::QueueBufferInput& input, + BnGraphicBufferProducer::QueueBufferOutput* output) { + uint32_t generation; + uint64_t bqId; + int32_t bqSlot; + bool display = displayBufferQueueBlock(block); + if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) || + bqId == 0) { + // Block not from bufferqueue -- it must be attached before queuing. - if (result != OK) { - LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:" - << " target bqId " << bqId - << ", generation " << generation - << "."; - return false; + mMutex.lock(); + sp outputIgbp = mIgbp; + uint32_t outputGeneration = mGeneration; + mMutex.unlock(); + + status_t status = attachToBufferQueue( + block, outputIgbp, outputGeneration, &bqSlot); + if (status != OK) { + LOG(WARNING) << "outputBuffer -- attaching failed."; + return INVALID_OPERATION; + } + + status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + if (status != OK) { + LOG(ERROR) << "outputBuffer -- queueBuffer() failed " + "on non-bufferqueue-based block. " + "Error = " << status << "."; + return status; + } + return OK; + } + + mMutex.lock(); + sp outputIgbp = mIgbp; + uint32_t outputGeneration = mGeneration; + uint64_t outputBqId = mBqId; + mMutex.unlock(); + + if (!outputIgbp) { + LOG(VERBOSE) << "outputBuffer -- output surface is null."; + return NO_INIT; + } + + if (!display) { + LOG(WARNING) << "outputBuffer -- cannot display " + "bufferqueue-based block to the bufferqueue."; + return UNKNOWN_ERROR; + } + if (bqId != outputBqId || generation != outputGeneration) { + int32_t diff = (int32_t) outputGeneration - (int32_t) generation; + LOG(WARNING) << "outputBuffer -- buffers from old generation to " + << outputGeneration << " , diff: " << diff + << " , slot: " << bqSlot; + return DEAD_OBJECT; + } + + status_t status = outputIgbp->queueBuffer(static_cast(bqSlot), + input, output); + if (status != OK) { + LOG(ERROR) << "outputBuffer -- queueBuffer() failed " + "on bufferqueue-based block. " + "Error = " << status << "."; + return status; + } + return OK; } - LOG(VERBOSE) << "holdBufferQueueBlock -- attached:" - << " bqId " << bqId - << ", bqSlot " << bqSlot - << ", generation " << generation - << "."; - _C2BlockFactory::AssignBlockToBufferQueue( - data, getHgbp(igbp), generation, bqId, bqSlot, true); - return true; + Impl *getPtr() { + return this; + } + + ~Impl() {} +}; + +OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {} + +OutputBufferQueue::~OutputBufferQueue() {} + +bool OutputBufferQueue::configure(const sp& igbp, + uint32_t generation, + uint64_t bqId) { + return mImpl && mImpl->configure(igbp, generation, bqId); +} + +status_t OutputBufferQueue::outputBuffer( + const C2ConstGraphicBlock& block, + const BnGraphicBufferProducer::QueueBufferInput& input, + BnGraphicBufferProducer::QueueBufferOutput* output) { + if (mImpl) { + return mImpl->outputBuffer(block, input, output); + } + return DEAD_OBJECT; } -void holdBufferQueueBlocks(const std::list>& workList, - const sp& igbp, - uint64_t bqId, - uint32_t generation, - bool forInput) { +void OutputBufferQueue::holdBufferQueueBlocks( + const std::list>& workList) { + if (!mImpl) { + return; + } forEachBlock(workList, - std::bind(holdBufferQueueBlock, - std::placeholders::_1, igbp, bqId, generation), - forInput, !forInput); + std::bind(&OutputBufferQueue::Impl::registerBuffer, + mImpl->getPtr(), std::placeholders::_1)); } } // namespace utils diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp index 5897dce8f9..a9f20a4ceb 100644 --- a/media/codec2/hidl/1.0/utils/Component.cpp +++ b/media/codec2/hidl/1.0/utils/Component.cpp @@ -107,19 +107,22 @@ struct Component::Listener : public C2Component::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; } - yieldBufferQueueBlocks(c2workItems, true); + endTransferBufferQueueBlocks(c2workItems, true, true); } } @@ -254,13 +257,14 @@ Return Component::flush(flush_cb _hidl_cb) { 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); - yieldBufferQueueBlocks(c2flushedWorks, true); + endTransferBufferQueueBlocks(c2flushedWorks, true, true); return Void(); } diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h index be429ace28..0a2298c01a 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h @@ -28,62 +28,42 @@ namespace c2 { namespace V1_0 { namespace utils { - // BufferQueue-Based Block Operations // ================================== -// Create a GraphicBuffer object from a graphic block and attach it to an -// IGraphicBufferProducer. -status_t attachToBufferQueue(const C2ConstGraphicBlock& block, - const sp& igbp, - uint32_t generation, - int32_t* bqSlot); +// Manage BufferQueue and graphic blocks for both component and codec. +// Manage graphic blocks ownership consistently during surface change. +struct OutputBufferQueue { + + OutputBufferQueue(); + + ~OutputBufferQueue(); + + // Configure a new surface to render graphic blocks. + // Graphic blocks from older surface will be migrated to new surface. + bool configure(const sp& igbp, + uint32_t generation, + uint64_t bqId); + + // Render a graphic block to current surface. + status_t outputBuffer( + const C2ConstGraphicBlock& block, + const BnGraphicBufferProducer::QueueBufferInput& input, + BnGraphicBufferProducer::QueueBufferOutput* output); -// Return false if block does not come from a bufferqueue-based blockpool. -// Otherwise, extract generation, bqId and bqSlot and return true. -bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, - uint32_t* generation, - uint64_t* bqId, - int32_t* bqSlot); + // Call holdBufferQueueBlock() on output blocks in the given workList. + // The OutputBufferQueue will take the ownership of output blocks. + // + // Note: This function should be called after WorkBundle has been received + // from another process. + void holdBufferQueueBlocks( + const std::list>& workList); -// Assign the given block to a bufferqueue so that when the block is destroyed, -// cancelBuffer() will be called. -// -// If the block does not come from a bufferqueue-based blockpool, this function -// returns false. -// -// If the block already has a bufferqueue assignment that matches the given one, -// the function returns true. -// -// If the block already has a bufferqueue assignment that does not match the -// given one, the block will be reassigned to the given bufferqueue. This -// will call attachBuffer() on the given igbp. The function then returns true on -// success or false on any failure during the operation. -// -// Note: This function should be called after detachBuffer() or dequeueBuffer() -// is called manually. -bool holdBufferQueueBlock(const C2ConstGraphicBlock& block, - const sp& igbp, - uint64_t bqId, - uint32_t generation); +private: -// Call holdBufferQueueBlock() on input or output blocks in the given workList. -// Since the bufferqueue assignment for input and output buffers can be -// different, this function takes forInput to determine whether the given -// bufferqueue is for input buffers or output buffers. (The default value of -// forInput is false.) -// -// In the (rare) case that both input and output buffers are bufferqueue-based, -// this function must be called twice, once for the input buffers and once for -// the output buffers. -// -// Note: This function should be called after WorkBundle has been received from -// another process. -void holdBufferQueueBlocks(const std::list>& workList, - const sp& igbp, - uint64_t bqId, - uint32_t generation, - bool forInput = false); + class Impl; + std::unique_ptr mImpl; +}; } // namespace utils } // namespace V1_0 diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h index 0ddfec51ba..a9928b31ce 100644 --- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h +++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h @@ -299,26 +299,46 @@ c2_status_t toC2Status(::android::hardware::media::bufferpool::V2_0:: // BufferQueue-Based Block Operations // ================================== -// Disassociate the given block with its designated bufferqueue so that -// cancelBuffer() will not be called when the block is destroyed. If the block -// does not have a designated bufferqueue, the function returns false. -// Otherwise, it returns true. +// Call before transferring block to other processes. // -// Note: This function should be called after attachBuffer() or queueBuffer() is -// called manually. -bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block); - -// Call yieldBufferQueueBlock() on blocks in the given workList. processInput -// determines whether input blocks are yielded. processOutput works similarly on -// output blocks. (The default value of processInput is false while the default -// value of processOutput is true. This implies that in most cases, only output -// buffers contain bufferqueue-based blocks.) +// The given block is ready to transfer to other processes. This will guarantee +// the given block data is not mutated by bufferqueue migration. +bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block); + +// Call beginTransferBufferQueueBlock() on blocks in the given workList. +// processInput determines whether input blocks are yielded. processOutput +// works similarly on output blocks. (The default value of processInput is +// false while the default value of processOutput is true. This implies that in +// most cases, only output buffers contain bufferqueue-based blocks.) +void beginTransferBufferQueueBlocks( + const std::list>& workList, + bool processInput = false, + bool processOutput = true); + +// Call after transferring block is finished and make sure that +// beginTransferBufferQueueBlock() is called before. // -// Note: This function should be called after WorkBundle has been successfully -// sent over the Treble boundary to another process. -void yieldBufferQueueBlocks(const std::list>& workList, - bool processInput = false, - bool processOutput = true); +// The transfer of given block is finished. If transfer is successful the given +// block is not owned by process anymore. Since transfer is finished the given +// block data is OK to mutate by bufferqueue migration after this call. +bool endTransferBufferQueueBlock(const C2ConstGraphicBlock& block, + bool transfer); + +// Call endTransferBufferQueueBlock() on blocks in the given workList. +// processInput determines whether input blocks are yielded. processOutput +// works similarly on output blocks. (The default value of processInput is +// false while the default value of processOutput is true. This implies that in +// most cases, only output buffers contain bufferqueue-based blocks.) +void endTransferBufferQueueBlocks( + const std::list>& workList, + bool transfer, + bool processInput = false, + bool processOutput = true); + +// The given block is ready to be rendered. the given block is not owned by +// process anymore. If migration is in progress, this returns false in order +// not to render. +bool displayBufferQueueBlock(const C2ConstGraphicBlock& block); } // namespace utils } // namespace V1_0 diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp index 3fd2f921ae..07dbf6777f 100644 --- a/media/codec2/hidl/1.0/utils/types.cpp +++ b/media/codec2/hidl/1.0/utils/types.cpp @@ -737,7 +737,15 @@ bool addBaseBlock( bufferPoolSender, baseBlocks, baseBlockIndices); } case _C2BlockPoolData::TYPE_BUFFERQUEUE: - // Do the same thing as a NATIVE block. + uint32_t gen; + uint64_t bqId; + int32_t bqSlot; + // Update handle if migration happened. + if (_C2BlockFactory::GetBufferQueueData( + blockPoolData, &gen, &bqId, &bqSlot)) { + android::MigrateNativeCodec2GrallocHandle( + const_cast(handle), gen, bqId, bqSlot); + } return _addBaseBlock( index, handle, baseBlocks, baseBlockIndices); @@ -1772,20 +1780,53 @@ void forEachBlock(const std::list>& workList, } // unnamed namespace -bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) { +bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); if (data && _C2BlockFactory::GetBufferQueueData(data)) { - _C2BlockFactory::YieldBlockToBufferQueue(data); + _C2BlockFactory::BeginTransferBlockToClient(data); return true; } return false; } -void yieldBufferQueueBlocks( +void beginTransferBufferQueueBlocks( const std::list>& workList, bool processInput, bool processOutput) { - forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput); + forEachBlock(workList, beginTransferBufferQueueBlock, + processInput, processOutput); +} + +bool endTransferBufferQueueBlock( + const C2ConstGraphicBlock& block, + bool transfer) { + std::shared_ptr<_C2BlockPoolData> data = + _C2BlockFactory::GetGraphicBlockPoolData(block); + if (data && _C2BlockFactory::GetBufferQueueData(data)) { + _C2BlockFactory::EndTransferBlockToClient(data, transfer); + return true; + } + return false; +} + +void endTransferBufferQueueBlocks( + const std::list>& workList, + bool transfer, + bool processInput, bool processOutput) { + forEachBlock(workList, + std::bind(endTransferBufferQueueBlock, + std::placeholders::_1, transfer), + processInput, processOutput); +} + +bool displayBufferQueueBlock(const C2ConstGraphicBlock& block) { + std::shared_ptr<_C2BlockPoolData> data = + _C2BlockFactory::GetGraphicBlockPoolData(block); + if (data && _C2BlockFactory::GetBufferQueueData(data)) { + _C2BlockFactory::DisplayBlockToBufferQueue(data); + return true; + } + return false; } } // namespace utils diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 6aca4a321d..2b417a648e 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -1080,15 +1080,7 @@ c2_status_t Codec2Client::Component::destroyBlockPool( void Codec2Client::Component::handleOnWorkDone( const std::list> &workItems) { // Output bufferqueue-based blocks' lifetime management - mOutputBufferQueueMutex.lock(); - sp igbp = mOutputIgbp; - uint64_t bqId = mOutputBqId; - uint32_t generation = mOutputGeneration; - mOutputBufferQueueMutex.unlock(); - - if (igbp) { - holdBufferQueueBlocks(workItems, igbp, bqId, generation); - } + mOutputBufferQueue.holdBufferQueueBlocks(workItems); } c2_status_t Codec2Client::Component::queue( @@ -1151,15 +1143,7 @@ c2_status_t Codec2Client::Component::flush( } // Output bufferqueue-based blocks' lifetime management - mOutputBufferQueueMutex.lock(); - sp igbp = mOutputIgbp; - uint64_t bqId = mOutputBqId; - uint32_t generation = mOutputGeneration; - mOutputBufferQueueMutex.unlock(); - - if (igbp) { - holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation); - } + mOutputBufferQueue.holdBufferQueueBlocks(*flushedWork); return status; } @@ -1239,15 +1223,31 @@ c2_status_t Codec2Client::Component::setOutputSurface( C2BlockPool::local_id_t blockPoolId, const sp& surface, uint32_t generation) { - sp igbp = - surface->getHalInterface(); + uint64_t bqId = 0; + sp nullIgbp; + sp nullHgbp; - if (!igbp) { + sp igbp = surface ? + surface->getHalInterface() : nullHgbp; + if (surface && !igbp) { igbp = new B2HGraphicBufferProducer2(surface); } + if (!surface) { + mOutputBufferQueue.configure(nullIgbp, generation, 0); + } else if (surface->getUniqueId(&bqId) != OK) { + LOG(ERROR) << "setOutputSurface -- " + "cannot obtain bufferqueue id."; + bqId = 0; + mOutputBufferQueue.configure(nullIgbp, generation, 0); + } else { + mOutputBufferQueue.configure(surface, generation, bqId); + } + ALOGD("generation remote change %u", generation); + Return transStatus = mBase->setOutputSurface( - static_cast(blockPoolId), igbp); + static_cast(blockPoolId), + bqId == 0 ? nullHgbp : igbp); if (!transStatus.isOk()) { LOG(ERROR) << "setOutputSurface -- transaction failed."; return C2_TRANSACTION_FAILED; @@ -1256,18 +1256,6 @@ c2_status_t Codec2Client::Component::setOutputSurface( static_cast(static_cast(transStatus)); if (status != C2_OK) { LOG(DEBUG) << "setOutputSurface -- call failed: " << status << "."; - } else { - std::lock_guard lock(mOutputBufferQueueMutex); - if (mOutputIgbp != surface) { - mOutputIgbp = surface; - if (!surface) { - mOutputBqId = 0; - } else if (surface->getUniqueId(&mOutputBqId) != OK) { - LOG(ERROR) << "setOutputSurface -- " - "cannot obtain bufferqueue id."; - } - } - mOutputGeneration = generation; } return status; } @@ -1276,74 +1264,7 @@ status_t Codec2Client::Component::queueToOutputSurface( const C2ConstGraphicBlock& block, const QueueBufferInput& input, QueueBufferOutput* output) { - uint32_t generation; - uint64_t bqId; - int32_t bqSlot; - if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) || - bqId == 0) { - // Block not from bufferqueue -- it must be attached before queuing. - - mOutputBufferQueueMutex.lock(); - sp outputIgbp = mOutputIgbp; - uint32_t outputGeneration = mOutputGeneration; - mOutputBufferQueueMutex.unlock(); - - status_t status = attachToBufferQueue(block, - outputIgbp, - outputGeneration, - &bqSlot); - if (status != OK) { - LOG(WARNING) << "queueToOutputSurface -- attaching failed."; - return INVALID_OPERATION; - } - - status = outputIgbp->queueBuffer(static_cast(bqSlot), - input, output); - if (status != OK) { - LOG(ERROR) << "queueToOutputSurface -- queueBuffer() failed " - "on non-bufferqueue-based block. " - "Error = " << status << "."; - return status; - } - return OK; - } - - mOutputBufferQueueMutex.lock(); - sp outputIgbp = mOutputIgbp; - uint64_t outputBqId = mOutputBqId; - uint32_t outputGeneration = mOutputGeneration; - mOutputBufferQueueMutex.unlock(); - - if (!outputIgbp) { - LOG(VERBOSE) << "queueToOutputSurface -- output surface is null."; - return NO_INIT; - } - - if (bqId != outputBqId || generation != outputGeneration) { - if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) { - LOG(ERROR) << "queueToOutputSurface -- migration failed."; - return DEAD_OBJECT; - } - if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) { - LOG(ERROR) << "queueToOutputSurface -- corrupted bufferqueue assignment."; - return UNKNOWN_ERROR; - } - } - - status_t status = outputIgbp->queueBuffer(static_cast(bqSlot), - input, output); - if (status != OK) { - LOG(DEBUG) << "queueToOutputSurface -- queueBuffer() failed " - "on bufferqueue-based block. " - "Error = " << status << "."; - return status; - } - if (!yieldBufferQueueBlock(block)) { - LOG(DEBUG) << "queueToOutputSurface -- cannot yield " - "bufferqueue-based block to the bufferqueue."; - return UNKNOWN_ERROR; - } - return OK; + return mOutputBufferQueue.outputBuffer(block, input, output); } c2_status_t Codec2Client::Component::connectToInputSurface( diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 03db5154a1..b8a7fb5d5b 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -369,10 +369,8 @@ protected: ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender mBufferPoolSender; - std::mutex mOutputBufferQueueMutex; - sp mOutputIgbp; - uint64_t mOutputBqId; - uint32_t mOutputGeneration; + ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue + mOutputBufferQueue; static c2_status_t setDeathListener( const std::shared_ptr& component, diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index eb20b20aa3..8be9a1d125 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -1581,6 +1581,7 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { if (newSurface) { newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); newSurface->setDequeueTimeout(kDequeueTimeoutNs); + newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers); producer = newSurface->getIGraphicBufferProducer(); producer->setGenerationNumber(generation); } else { @@ -1608,7 +1609,6 @@ status_t CCodecBufferChannel::setSurface(const sp &newSurface) { { Mutexed::Locked output(mOutputSurface); - newSurface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); output->surface = newSurface; output->generation = generation; } diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index e698bf4b9a..286c48ae74 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -201,6 +201,22 @@ public: return res; } + static bool MigrateNativeHandle( + native_handle_t *handle, + uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) { + if (handle == nullptr || !isValid(handle)) { + return false; + } + ExtraData *ed = getExtraData(handle); + if (!ed) return false; + ed->generation = generation; + ed->igbp_id_lo = uint32_t(igbp_id & 0xFFFFFFFF); + ed->igbp_id_hi = uint32_t(igbp_id >> 32); + ed->igbp_slot = igbp_slot; + return true; + } + + static native_handle_t* UnwrapNativeHandle( const C2Handle *const handle) { const ExtraData *xd = getExtraData(handle); @@ -270,6 +286,13 @@ C2Handle *WrapNativeCodec2GrallocHandle( generation, igbp_id, igbp_slot); } +bool MigrateNativeCodec2GrallocHandle( + native_handle_t *handle, + uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) { + return C2HandleGralloc::MigrateNativeHandle(handle, generation, igbp_id, igbp_slot); +} + + class C2AllocationGralloc : public C2GraphicAllocation { public: virtual ~C2AllocationGralloc() override; diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h index 05d989e743..ee7524e668 100644 --- a/media/codec2/vndk/include/C2AllocatorGralloc.h +++ b/media/codec2/vndk/include/C2AllocatorGralloc.h @@ -44,6 +44,16 @@ C2Handle *WrapNativeCodec2GrallocHandle( uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride, uint32_t generation = 0, uint64_t igbp_id = 0, uint32_t igbp_slot = 0); +/** + * When the gralloc handle is migrated to another bufferqueue, update + * bufferqueue information. + * + * @return {@code true} when native_handle is a wrapped codec2 handle. + */ +bool MigrateNativeCodec2GrallocHandle( + native_handle_t *handle, + uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot); + /** * \todo Get this from the buffer */ diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h index 84ce70ad2d..4ae946ab7b 100644 --- a/media/codec2/vndk/internal/C2BlockInternal.h +++ b/media/codec2/vndk/internal/C2BlockInternal.h @@ -206,23 +206,19 @@ struct _C2BlockFactory { * * - GetBufferQueueData(): Returns generation, bqId and bqSlot. * - HoldBlockFromBufferQueue(): Sets "held" status to true. - * - YieldBlockToBufferQueue(): Sets "held" status to false. - * - AssignBlockToBufferQueue(): Sets the bufferqueue assignment and - * "held" status. + * - BeginTransferBlockToClient()/EndTransferBlockToClient(): + * Clear "held" status to false if transfer was successful, + * otherwise "held" status remains true. + * - BeginAttachBlockToBufferQueue()/EndAttachBlockToBufferQueue(): + * The will keep "held" status true if attach was eligible. + * Otherwise, "held" status is cleared to false. In that case, + * ownership of buffer should be transferred to bufferqueue. + * - DisplayBlockToBufferQueue() + * This will clear "held" status to false. * * All these functions operate on _C2BlockPoolData, which can be obtained by * calling GetGraphicBlockPoolData(). * - * HoldBlockFromBufferQueue() will mark the block as held, while - * YieldBlockToBufferQueue() will do the opposite. These two functions do - * not modify the bufferqueue assignment, so it is not wrong to call - * HoldBlockFromBufferQueue() after YieldBlockToBufferQueue() if it can be - * guaranteed that the block is not destroyed during the period between the - * two calls. - * - * AssingBlockToBufferQueue() has a "held" status as an optional argument. - * The default value is true. - * * Maintaining Consistency with IGraphicBufferProducer Operations * ============================================================== * @@ -232,16 +228,20 @@ struct _C2BlockFactory { * information for _C2BlockPoolData, with "held" status set to true. * * queueBuffer() - * - After queueBuffer() is called, YieldBlockToBufferQueue() should be - * called. - * - * attachBuffer() - * - After attachBuffer() is called, AssignBlockToBufferQueue() should be - * called with "held" status set to true. - * - * detachBuffer() - * - After detachBuffer() is called, HoldBlockFromBufferQueue() should be - * called. + * - Before queueBuffer() is called, DisplayBlockToBufferQueue() should be + * called to test eligibility. If it's not eligible, do not call + * queueBuffer(). + * + * attachBuffer() - remote migration only. + * - Local migration on blockpool side will be done automatically by + * blockpool. + * - Before attachBuffer(), BeginAttachBlockToBufferQueue() should be called + * to test eligiblity. + * - After attachBuffer() is called, EndAttachBlockToBufferQueue() should + * be called. This will set "held" status to true. If it returned + * false, cancelBuffer() should be called. + * + * detachBuffer() - no-op. */ /** @@ -261,76 +261,120 @@ struct _C2BlockFactory { */ static bool GetBufferQueueData( - const std::shared_ptr<_C2BlockPoolData>& poolData, + const std::shared_ptr& poolData, uint32_t* generation = nullptr, uint64_t* bqId = nullptr, int32_t* bqSlot = nullptr); /** - * Set bufferqueue assignment and "held" status to a block created by a - * bufferqueue-based blockpool. + * Hold a block from the designated bufferqueue. This causes the destruction + * of the block to trigger a call to cancelBuffer(). + * + * This function assumes that \p poolData comes from a bufferqueue-based + * block. It does not check if that is the case. * * \param poolData blockpool data associated to the block. - * \param igbp \c IGraphicBufferProducer instance from the designated - * bufferqueue. - * \param generation Generation number that the buffer belongs to. - * \param bqId Id of the bufferqueue that will own the buffer (block). - * \param bqSlot Slot number of the buffer. - * \param held Whether the block is held. This "held" status can be - * changed later by calling YieldBlockToBufferQueue() or - * HoldBlockFromBufferQueue(). - * - * \return \c true if \p poolData is valid bufferqueue data; - * \c false otherwise. + * \param owner block owner from client bufferqueue manager. + * If this is expired, the block is not owned by client + * anymore. + * \param igbp \c IGraphicBufferProducer instance to be assigned to the + * block. This is not needed when the block is local. * - * Note: \p generation should match the latest generation number set on the - * bufferqueue, and \p bqId should match the unique id for the bufferqueue - * (obtainable by calling igbp->getUniqueId()). + * \return The previous held status. */ static - bool AssignBlockToBufferQueue( + bool HoldBlockFromBufferQueue( const std::shared_ptr<_C2BlockPoolData>& poolData, + const std::shared_ptr& owner, const ::android::sp<::android::hardware::graphics::bufferqueue:: - V2_0::IGraphicBufferProducer>& igbp, - uint32_t generation, - uint64_t bqId, - int32_t bqSlot, - bool held = true); + V2_0::IGraphicBufferProducer>& igbp = nullptr); /** - * Hold a block from the designated bufferqueue. This causes the destruction - * of the block to trigger a call to cancelBuffer(). + * Prepare a block to be transferred to other process. This blocks + * bufferqueue migration from happening. The block should be in held. * * This function assumes that \p poolData comes from a bufferqueue-based * block. It does not check if that is the case. * * \param poolData blockpool data associated to the block. - * \param igbp \c IGraphicBufferProducer instance to be assigned to the - * block. This is not needed when the block is local. * - * \return The previous held status. + * \return true if transfer is eligible, false otherwise. */ static - bool HoldBlockFromBufferQueue( + bool BeginTransferBlockToClient( + const std::shared_ptr<_C2BlockPoolData>& poolData); + + /** + * Called after transferring the specified block is finished. Make sure + * that BeginTransferBlockToClient() was called before this call. + * + * This will unblock bufferqueue migration. If transfer result was + * successful, this causes the destruction of the block not to trigger a + * call to cancelBuffer(). + * This function assumes that \p poolData comes from a bufferqueue-based + * block. It does not check if that is the case. + * + * \param poolData blockpool data associated to the block. + * + * \return true if transfer began before, false otherwise. + */ + static + bool EndTransferBlockToClient( const std::shared_ptr<_C2BlockPoolData>& poolData, - const ::android::sp<::android::hardware::graphics::bufferqueue:: - V2_0::IGraphicBufferProducer>& igbp = nullptr); + bool transferred); /** - * Yield a block to the designated bufferqueue. This causes the destruction - * of the block not to trigger a call to cancelBuffer(); + * Prepare a block to be migrated to another bufferqueue. This blocks + * rendering until migration has been finished. The block should be in + * held. * * This function assumes that \p poolData comes from a bufferqueue-based * block. It does not check if that is the case. * * \param poolData blockpool data associated to the block. * - * \return The previous held status. + * \return true if migration is eligible, false otherwise. */ static - bool YieldBlockToBufferQueue( + bool BeginAttachBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& poolData); + /** + * Called after migration of the specified block is finished. Make sure + * that BeginAttachBlockToBufferQueue() was called before this call. + * + * This will unblock rendering. if redering is tried during migration, + * this returns false. In that case, cancelBuffer() should be called. + * This function assumes that \p poolData comes from a bufferqueue-based + * block. It does not check if that is the case. + * + * \param poolData blockpool data associated to the block. + * + * \return true if migration is eligible, false otherwise. + */ + static + bool EndAttachBlockToBufferQueue( + const std::shared_ptr<_C2BlockPoolData>& poolData, + const std::shared_ptr& owner, + const ::android::sp<::android::hardware::graphics::bufferqueue:: + V2_0::IGraphicBufferProducer>& igbp, + uint32_t generation, + uint64_t bqId, + int32_t bqSlot); + + /** + * Indicates a block to be rendered very soon. + * + * This function assumes that \p poolData comes from a bufferqueue-based + * block. It does not check if that is the case. + * + * \param poolData blockpool data associated to the block. + * + * \return true if migration is eligible, false otherwise. + */ + static + bool DisplayBlockToBufferQueue( + const std::shared_ptr<_C2BlockPoolData>& poolData); }; #endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_ diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 3f40e56399..5fa48a8225 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -61,8 +61,13 @@ struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { 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; @@ -71,7 +76,8 @@ struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { // Create a remote BlockPoolData. C2BufferQueueBlockPoolData( uint32_t generation, uint64_t bqId, int32_t bqSlot, - const sp& producer = nullptr); + const std::shared_ptr &owner, + const sp& producer); // Create a local BlockPoolData. C2BufferQueueBlockPoolData( @@ -80,15 +86,19 @@ struct C2BufferQueueBlockPoolData : public _C2BlockPoolData { 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<_C2BlockPoolData>& data, + 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); + const std::shared_ptr poolData = + std::static_pointer_cast(data); + std::scoped_lock lock(poolData->lock); *generation = poolData->generation; if (bqId) { *bqId = poolData->bqId; @@ -102,32 +112,15 @@ bool _C2BlockFactory::GetBufferQueueData( return false; } -bool _C2BlockFactory::AssignBlockToBufferQueue( - const std::shared_ptr<_C2BlockPoolData>& data, - const sp& igbp, - uint32_t generation, - uint64_t bqId, - int32_t bqSlot, - bool held) { - if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) { - const std::shared_ptr poolData = - std::static_pointer_cast(data); - poolData->igbp = igbp; - poolData->generation = generation; - poolData->bqId = bqId; - poolData->bqSlot = bqSlot; - poolData->held = held; - return true; - } - return false; -} - bool _C2BlockFactory::HoldBlockFromBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data, + const std::shared_ptr& owner, const sp& igbp) { 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) { @@ -138,14 +131,88 @@ bool _C2BlockFactory::HoldBlockFromBufferQueue( return true; } -bool _C2BlockFactory::YieldBlockToBufferQueue( +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; +} + +bool _C2BlockFactory::EndTransferBlockToClient( + const std::shared_ptr<_C2BlockPoolData>& data, + 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; +} + +bool _C2BlockFactory::BeginAttachBlockToBufferQueue( const std::shared_ptr<_C2BlockPoolData>& data) { const std::shared_ptr poolData = std::static_pointer_cast(data); - if (!poolData->held) { + 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; +} + +// if display was tried during attach, buffer should be retired ASAP. +bool _C2BlockFactory::EndAttachBlockToBufferQueue( + const std::shared_ptr<_C2BlockPoolData>& data, + const std::shared_ptr& owner, + const sp& igbp, + 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; +} + +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; } @@ -175,7 +242,9 @@ std::shared_ptr _C2BlockFactory::CreateGraphicBlock( std::shared_ptr poolData = std::make_shared(generation, bqId, - (int32_t)bqSlot); + (int32_t)bqSlot, + nullptr, + nullptr); block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); } else { block = _C2BlockFactory::CreateGraphicBlock(alloc); @@ -186,6 +255,78 @@ std::shared_ptr _C2BlockFactory::CreateGraphicBlock( return nullptr; } +namespace { + +int64_t getTimestampNow() { + int64_t stamp; + struct timespec ts; + // TODO: CLOCK_MONOTONIC_COARSE? + clock_gettime(CLOCK_MONOTONIC, &ts); + stamp = ts.tv_nsec / 1000; + stamp += (ts.tv_sec * 1000000LL); + return stamp; +} + +bool getGenerationNumber(const sp &producer, + uint32_t *generation) { + status_t status{}; + int slot{}; + bool bufferNeedsReallocation{}; + sp fence = new Fence(); + + using Input = HGraphicBufferProducer::DequeueBufferInput; + using Output = HGraphicBufferProducer::DequeueBufferOutput; + Return transResult = producer->dequeueBuffer( + Input{640, 480, HAL_PIXEL_FORMAT_YCBCR_420_888, 0}, + [&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) { + return false; + } + HFenceWrapper hFenceWrapper{}; + if (!b2h(fence, &hFenceWrapper)) { + (void)producer->detachBuffer(static_cast(slot)).isOk(); + ALOGE("Invalid fence received from dequeueBuffer."); + return false; + } + sp slotBuffer = new GraphicBuffer(); + // N.B. This assumes requestBuffer# returns an existing allocation + // instead of a new allocation. + transResult = producer->requestBuffer( + slot, + [&status, &slotBuffer, &generation]( + HStatus hStatus, + HBuffer const& hBuffer, + uint32_t generationNumber){ + if (h2b(hStatus, &status) && + h2b(hBuffer, &slotBuffer) && + slotBuffer) { + *generation = generationNumber; + slotBuffer->setGenerationNumber(generationNumber); + } else { + status = android::BAD_VALUE; + } + }); + if (!transResult.isOk()) { + return false; + } else if (status != android::NO_ERROR) { + (void)producer->detachBuffer(static_cast(slot)).isOk(); + return false; + } + (void)producer->detachBuffer(static_cast(slot)).isOk(); + return true; +} + +}; + class C2BufferQueueBlockPool::Impl : public std::enable_shared_from_this { private: @@ -227,6 +368,7 @@ private: }); if (!transResult.isOk() || status != android::OK) { if (transResult.isOk()) { + ++mDqFailure; if (status == android::INVALID_OPERATION || status == android::TIMED_OUT || status == android::WOULD_BLOCK) { @@ -238,6 +380,8 @@ private: ALOGD("cannot dequeue buffer %d", status); return C2_BAD_VALUE; } + mDqFailure = 0; + mLastDqTs = getTimestampNow(); } HFenceWrapper hFenceWrapper{}; if (!b2h(fence, &hFenceWrapper)) { @@ -319,6 +463,7 @@ private: slotBuffer->getGenerationNumber(), mProducerId, slot, shared_from_this()); + mPoolDatas[slot] = poolData; *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); return C2_OK; } @@ -331,7 +476,9 @@ private: public: Impl(const std::shared_ptr &allocator) - : mInit(C2_OK), mProducerId(0), mAllocator(allocator) { + : mInit(C2_OK), mProducerId(0), mGeneration(0), + mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0), + mAllocator(allocator) { } ~Impl() { @@ -361,6 +508,19 @@ public: static int kMaxIgbpRetryDelayUs = 10000; std::unique_lock lock(mMutex); + if (mLastDqLogTs == 0) { + mLastDqLogTs = getTimestampNow(); + } else { + int64_t now = getTimestampNow(); + if (now >= mLastDqLogTs + 5000000) { + if (now >= mLastDqTs + 1000000 || mDqFailure > 5) { + ALOGW("last successful dequeue was %lld us ago, " + "%zu consecutive failures", + (long long)(now - mLastDqTs), mDqFailure); + } + mLastDqLogTs = now; + } + } if (mProducerId == 0) { std::shared_ptr alloc; c2_status_t err = mAllocator->newGraphicAllocation( @@ -386,12 +546,14 @@ public: } void setRenderCallback(const OnRenderCallback &renderCallback) { - std::lock_guard lock(mMutex); + std::scoped_lock lock(mMutex); mRenderCallback = renderCallback; } void configureProducer(const sp &producer) { uint64_t producerId = 0; + uint32_t generation = 0; + bool haveGeneration = false; if (producer) { Return transResult = producer->getUniqueId(); if (!transResult.isOk()) { @@ -399,9 +561,15 @@ public: return; } producerId = static_cast(transResult); + // TODO: provide gneration number from parameter. + haveGeneration = getGenerationNumber(producer, &generation); } + int migrated = 0; { - std::lock_guard lock(mMutex); + sp buffers[NUM_BUFFER_SLOTS]; + std::weak_ptr + poolDatas[NUM_BUFFER_SLOTS]; + std::scoped_lock lock(mMutex); bool noInit = false; for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { if (!noInit && mProducer) { @@ -410,46 +578,88 @@ public: noInit = !transResult.isOk() || static_cast(transResult) == HStatus::NO_INIT; } - mBuffers[i].clear(); } - if (producer) { + int32_t oldGeneration = mGeneration; + if (producer && haveGeneration) { mProducer = producer; mProducerId = producerId; + mGeneration = generation; } else { mProducer = nullptr; mProducerId = 0; + mGeneration = 0; + ALOGW("invalid producer producer(%d), generation(%d)", + (bool)producer, haveGeneration); + } + if (mProducer) { // 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); + if (slot >= 0) { + buffers[slot] = mBuffers[i]; + poolDatas[slot] = data; + ++migrated; + } + } + } + } + for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { + mBuffers[i] = buffers[i]; + mPoolDatas[i] = poolDatas[i]; } } + if (producer && haveGeneration) { + ALOGD("local generation change %u , " + "bqId: %llu migrated buffers # %d", + generation, (unsigned long long)producerId, migrated); + } } private: friend struct C2BufferQueueBlockPoolData; - void cancel(uint64_t igbp_id, int32_t igbp_slot) { - std::lock_guard lock(mMutex); - if (igbp_id == mProducerId && mProducer) { + 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; OnRenderCallback mRenderCallback; + size_t mDqFailure; + int64_t mLastDqTs; + int64_t mLastDqLogTs; + const std::shared_ptr mAllocator; std::mutex mMutex; sp mProducer; + sp mSavedProducer; sp mBuffers[NUM_BUFFER_SLOTS]; + std::weak_ptr mPoolDatas[NUM_BUFFER_SLOTS]; }; 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), - igbp(producer), + transfer(false), attach(false), display(false), + owner(owner), igbp(producer), localPool() { } @@ -458,6 +668,7 @@ C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( 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) { } @@ -466,12 +677,78 @@ C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() { if (!held || bqId == 0) { return; } - if (local && localPool) { - localPool->cancel(bqId, bqSlot); - } else if (igbp) { + if (local) { + if (localPool) { + localPool->cancel(generation, bqId, bqSlot); + } + } else if (igbp && !owner.expired()) { igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk(); } } +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) { + ALOGV("buffer is not owned"); + return -1; + } + if (!local || !localPool) { + ALOGV("pool is not local"); + return -1; + } + if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) { + ALOGV("slot is not in effect"); + return -1; + } + if (toGeneration == generation && bqId == toBqId) { + ALOGV("cannot migrate to same bufferqueue"); + return -1; + } + if (oldGeneration != generation) { + ALOGV("cannot migrate stale buffer"); + } + if (transfer) { + // either transferred or detached. + ALOGV("buffer is in transfer"); + return -1; + } + sp const& graphicBuffer = buffers[bqSlot]; + graphicBuffer->setGenerationNumber(toGeneration); + + HBuffer hBuffer{}; + uint32_t hGenerationNumber{}; + if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) { + ALOGD("I to O conversion failed"); + return -1; + } + + bool converted{}; + status_t bStatus{}; + int slot; + int *outSlot = &slot; + Return transResult = + producer->attachBuffer(hBuffer, hGenerationNumber, + [&converted, &bStatus, outSlot]( + HStatus hStatus, int32_t hSlot, bool releaseAll) { + converted = h2b(hStatus, &bStatus); + *outSlot = static_cast(hSlot); + if (converted && releaseAll && bStatus == android::OK) { + bStatus = android::INVALID_OPERATION; + } + }); + if (!transResult.isOk() || !converted || bStatus != android::OK) { + ALOGD("attach failed %d", static_cast(bStatus)); + return -1; + } + ALOGV("local migration from gen %u : %u slot %d : %d", + generation, toGeneration, bqSlot, slot); + generation = toGeneration; + bqId = toBqId; + bqSlot = slot; + return slot; +} C2BufferQueueBlockPool::C2BufferQueueBlockPool( const std::shared_ptr &allocator, const local_id_t localId) -- GitLab From b0ffc5af95a6733dc8f43041ea01dd197e275069 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Mon, 17 Jun 2019 11:53:41 -0700 Subject: [PATCH 1511/1530] Visualizer Effect: Fix SCALING_MODE_NORMALIZED Test: Clarity test on sine wave, enable SCALING_MODE_NORMALIZED Bug: 135469767 Change-Id: Id31d34dccb8a4440600925c54bd2ca5645530d8f --- media/libeffects/visualizer/EffectVisualizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp index 00bc3714ba..f838892e55 100644 --- a/media/libeffects/visualizer/EffectVisualizer.cpp +++ b/media/libeffects/visualizer/EffectVisualizer.cpp @@ -388,7 +388,7 @@ int Visualizer_process( maxSample = fmax(maxSample, fabs(smp)); } if (maxSample > 0.f) { - fscale = 127.f / maxSample; + fscale = 0.99f / maxSample; int exp; // unused const float significand = frexp(fscale, &exp); if (significand == 0.5f) { -- GitLab From 442be4503ca2c1f281e0697b05c6f0eb04df307d Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Tue, 18 Jun 2019 10:20:54 -0700 Subject: [PATCH 1512/1530] Guard fd with unique_fd in C2OMXNode::emptyBuffer The fd cloned near the beginning of C2OMXNode::emptyBuffer should last until this function goes out of scope, but we are seeing the fd becomes invalid during this process. This causes a crash during Component::queue when we try to clone from this fd again. Use unique_fd to guard most of the life span of this fd in hope to catch the offender. bug: 133254412 test: camera recording; cts VideoEncoderTest;EncodeDecodeTest. Change-Id: I4bd7398c069e58915303bbc0f58b1471c2755e31 --- media/codec2/sfplugin/C2OMXNode.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index f0f62f66d7..3a93c2ad46 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -52,10 +53,12 @@ public: C2OMXNode::C2OMXNode(const std::shared_ptr &comp) : mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), mUsage(0), mAdjustTimestampGapUs(0), mFirstInputFrame(true) { + android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS); } status_t C2OMXNode::freeNode() { mComp.reset(); + android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE); return OK; } @@ -227,6 +230,7 @@ status_t C2OMXNode::emptyBuffer( ? C2FrameData::FLAG_END_OF_STREAM : 0; std::shared_ptr block; + android::base::unique_fd fd0, fd1; C2Handle *handle = nullptr; if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer && omxBuf.mGraphicBuffer != nullptr) { @@ -238,8 +242,19 @@ status_t C2OMXNode::emptyBuffer( omxBuf.mGraphicBuffer->format, omxBuf.mGraphicBuffer->usage, omxBuf.mGraphicBuffer->stride); + if (handle != nullptr) { + // unique_fd takes ownership of the fds, we'll get warning if these + // fds get closed by somebody else. Onwership will be released before + // we return, so that the fds get closed as usually when this function + // goes out of scope (when both items and block are gone). + native_handle_t *nativeHandle = reinterpret_cast(handle); + fd0.reset(nativeHandle->numFds > 0 ? nativeHandle->data[0] : -1); + fd1.reset(nativeHandle->numFds > 1 ? nativeHandle->data[1] : -1); + } c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc); if (err != OK) { + (void)fd0.release(); + (void)fd1.release(); return UNKNOWN_ERROR; } block = _C2BlockFactory::CreateGraphicBlock(alloc); @@ -290,10 +305,17 @@ status_t C2OMXNode::emptyBuffer( c2_status_t err = comp->queue(&items); if (err != C2_OK) { + (void)fd0.release(); + (void)fd1.release(); return UNKNOWN_ERROR; } mBufferIdsInUse.lock()->emplace(index, buffer); + + // release ownership of the fds + (void)fd0.release(); + (void)fd1.release(); + return OK; } -- GitLab From 932f63ef1219deb522f094c4cf8f1223a7fb38c2 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 13 Jun 2019 17:40:32 -0700 Subject: [PATCH 1513/1530] NuPlayer::GenericSource::onPrepareAsync():Change lock order mLock and mDisconnect lock order interleaving, which has potential risk of deadlock if other code locks/unlocks them in a different order. Bug: 135211710 Test: (1) android.hardware.camera2.cts.FastBasicsTest#testCamera1 (2) android.media.cts.MediaPlayerFlakyNetworkTest Change-Id: I9486f2c6780c2f246786299a3d6c7c2f7295b768 --- media/libmediaplayerservice/nuplayer/GenericSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 0f72c0dd80..2cd920a106 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -404,8 +404,8 @@ void NuPlayer::GenericSource::onPrepareAsync() { sp dataSource = DataSourceFactory::CreateFromURI( mHTTPService, uri, &mUriHeaders, &contentType, static_cast(mHttpSource.get())); - mLock.lock(); mDisconnectLock.lock(); + mLock.lock(); if (!mDisconnected) { mDataSource = dataSource; } -- GitLab From 6ce715b696f25fd1f0463f4ef8a7650622079d90 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Tue, 18 Jun 2019 10:14:14 -0700 Subject: [PATCH 1514/1530] Allow to disable buffer dropping from BufferQueueProducer This makes commit e3c87346e82aeaeed7f91e6b93351b16362debd5 opt-in. Also reworked this to do this on the looper vs the calling process to avoid any race conditions. Bug: 135390389 Change-Id: I9df20f1e60f0bd4ac4f8d8e7b5f02919b170682e --- media/libstagefright/MediaCodec.cpp | 29 ++++++++++++++----- .../include/media/stagefright/MediaCodec.h | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp index e56750befe..f579e9de6b 100644 --- a/media/libstagefright/MediaCodec.cpp +++ b/media/libstagefright/MediaCodec.cpp @@ -1110,12 +1110,10 @@ status_t MediaCodec::configure( reset(); } if (!isResourceError(err)) { - if (err == OK) { - disableLegacyBufferDropPostQ(surface); - } break; } } + return err; } @@ -1178,11 +1176,7 @@ status_t MediaCodec::setSurface(const sp &surface) { msg->setObject("surface", surface); sp response; - status_t result = PostAndAwaitResponse(msg, &response); - if (result == OK) { - disableLegacyBufferDropPostQ(surface); - } - return result; + return PostAndAwaitResponse(msg, &response); } status_t MediaCodec::createInputSurface( @@ -2005,6 +1999,13 @@ void MediaCodec::onMessageReceived(const sp &msg) { CHECK(msg->findMessage("input-format", &mInputFormat)); CHECK(msg->findMessage("output-format", &mOutputFormat)); + + // limit to confirming the opt-in behavior to minimize any behavioral change + if (mSurface != nullptr && !mAllowFrameDroppingBySurface) { + // signal frame dropping mode in the input format as this may also be + // meaningful and confusing for an encoder in a transcoder scenario + mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface); + } ALOGV("[%s] configured as input format: %s, output format: %s", mComponentName.c_str(), mInputFormat->debugString(4).c_str(), @@ -2437,6 +2438,11 @@ void MediaCodec::onMessageReceived(const sp &msg) { } if (obj != NULL) { + if (!format->findInt32("allow-frame-drop", &mAllowFrameDroppingBySurface)) { + // allow frame dropping by surface by default + mAllowFrameDroppingBySurface = true; + } + format->setObject("native-window", obj); status_t err = handleSetSurface(static_cast(obj.get())); if (err != OK) { @@ -2444,6 +2450,9 @@ void MediaCodec::onMessageReceived(const sp &msg) { break; } } else { + // we are not using surface so this variable is not used, but initialize sensibly anyway + mAllowFrameDroppingBySurface = false; + handleSetSurface(NULL); } @@ -3436,6 +3445,10 @@ status_t MediaCodec::connectToSurface(const sp &surface) { if (err != OK) { ALOGE("nativeWindowConnect returned an error: %s (%d)", strerror(-err), err); + } else { + if (!mAllowFrameDroppingBySurface) { + disableLegacyBufferDropPostQ(surface); + } } } // do not return ALREADY_EXISTS unless surfaces are the same diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h index 6cd4265b4e..89cca63dc6 100644 --- a/media/libstagefright/include/media/stagefright/MediaCodec.h +++ b/media/libstagefright/include/media/stagefright/MediaCodec.h @@ -340,6 +340,7 @@ private: int32_t mVideoWidth; int32_t mVideoHeight; int32_t mRotationDegrees; + int32_t mAllowFrameDroppingBySurface; // initial create parameters AString mInitName; -- GitLab From 9ee5a7cba5ffe9f55f1ab05b6a496b67743b4670 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Mon, 17 Jun 2019 11:33:24 -0700 Subject: [PATCH 1515/1530] CCodec: increase timeout for operations Do not interrupt apps when the system is slowed down transiently. Bug: 134707995 Test: builds Merged-In: I2cc946a5e3d66f64886954bb82e01dd5c72c0ffa Change-Id: I2cc946a5e3d66f64886954bb82e01dd5c72c0ffa --- media/codec2/sfplugin/CCodec.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 76f132cd53..aa7189cd17 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -1651,7 +1651,7 @@ void CCodec::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatAllocate: { // C2ComponentStore::createComponent() should return within 100ms. - setDeadline(now, 150ms, "allocate"); + setDeadline(now, 1500ms, "allocate"); sp obj; CHECK(msg->findObject("codecInfo", &obj)); allocate((MediaCodecInfo *)obj.get()); @@ -1659,7 +1659,7 @@ void CCodec::onMessageReceived(const sp &msg) { } case kWhatConfigure: { // C2Component::commit_sm() should return within 5ms. - setDeadline(now, 250ms, "configure"); + setDeadline(now, 1500ms, "configure"); sp format; CHECK(msg->findMessage("format", &format)); configure(format); @@ -1667,31 +1667,31 @@ void CCodec::onMessageReceived(const sp &msg) { } case kWhatStart: { // C2Component::start() should return within 500ms. - setDeadline(now, 550ms, "start"); + setDeadline(now, 1500ms, "start"); start(); break; } case kWhatStop: { // C2Component::stop() should return within 500ms. - setDeadline(now, 550ms, "stop"); + setDeadline(now, 1500ms, "stop"); stop(); break; } case kWhatFlush: { // C2Component::flush_sm() should return within 5ms. - setDeadline(now, 50ms, "flush"); + setDeadline(now, 1500ms, "flush"); flush(); break; } case kWhatCreateInputSurface: { // Surface operations may be briefly blocking. - setDeadline(now, 100ms, "createInputSurface"); + setDeadline(now, 1500ms, "createInputSurface"); createInputSurface(); break; } case kWhatSetInputSurface: { // Surface operations may be briefly blocking. - setDeadline(now, 100ms, "setInputSurface"); + setDeadline(now, 1500ms, "setInputSurface"); sp obj; CHECK(msg->findObject("surface", &obj)); sp surface(static_cast(obj.get())); -- GitLab From 94c98021b7e0ea614d92a1d1ea8bf163de9136bd Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Wed, 19 Jun 2019 09:11:51 -0700 Subject: [PATCH 1516/1530] Camera: Destroy jpeg handle in 'encodeGrayscaleJpeg' Make sure 'jpeg_destroy_compress()' is always called before leaving 'encodeGrayscaleJpeg()'. Additionally fix misspelling of 'intrinsic'. Bug: 135622974 Test: atest cts/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java#testDynamicDepth --generate-baseline=12 Change-Id: I55c1f86881ba05aac6aac6981df5fcb276c9d4da --- .../api2/DepthCompositeStream.cpp | 14 ++-- .../api2/DepthCompositeStream.h | 2 +- .../common/DepthPhotoProcessor.cpp | 75 ++++++++++--------- .../common/DepthPhotoProcessor.h | 8 +- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp index 9525ad2635..8ebaa2b2c7 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp @@ -58,8 +58,8 @@ DepthCompositeStream::DepthCompositeStream(wp device, entry = staticInfo.find(ANDROID_LENS_INTRINSIC_CALIBRATION); if (entry.count == 5) { - mInstrinsicCalibration.reserve(5); - mInstrinsicCalibration.insert(mInstrinsicCalibration.end(), entry.data.f, + mIntrinsicCalibration.reserve(5); + mIntrinsicCalibration.insert(mIntrinsicCalibration.end(), entry.data.f, entry.data.f + 5); } else { ALOGW("%s: Intrinsic calibration absent from camera characteristics!", __FUNCTION__); @@ -323,12 +323,12 @@ status_t DepthCompositeStream::processInputFrame(const InputFrame &inputFrame) { depthPhoto.mMaxJpegSize = maxDepthJpegSize; // The camera intrinsic calibration layout is as follows: // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] - if (mInstrinsicCalibration.size() == 5) { - memcpy(depthPhoto.mInstrinsicCalibration, mInstrinsicCalibration.data(), - sizeof(depthPhoto.mInstrinsicCalibration)); - depthPhoto.mIsInstrinsicCalibrationValid = 1; + if (mIntrinsicCalibration.size() == 5) { + memcpy(depthPhoto.mIntrinsicCalibration, mIntrinsicCalibration.data(), + sizeof(depthPhoto.mIntrinsicCalibration)); + depthPhoto.mIsIntrinsicCalibrationValid = 1; } else { - depthPhoto.mIsInstrinsicCalibrationValid = 0; + depthPhoto.mIsIntrinsicCalibrationValid = 0; } // The camera lens distortion contains the following lens correction coefficients. // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h index 1bf31f47b3..975c59bc5c 100644 --- a/services/camera/libcameraservice/api2/DepthCompositeStream.h +++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h @@ -124,7 +124,7 @@ private: ssize_t mMaxJpegSize; std::vector> mSupportedDepthSizes; - std::vector mInstrinsicCalibration, mLensDistortion; + std::vector mIntrinsicCalibration, mLensDistortion; bool mIsLogicalCamera; void* mDepthPhotoLibHandle; process_depth_photo_frame mDepthPhotoProcess; diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp index fc79150351..3c90de0d6e 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp @@ -61,6 +61,13 @@ using dynamic_depth::Pose; using dynamic_depth::Profile; using dynamic_depth::Profiles; +template<> +struct std::default_delete { + inline void operator()(jpeg_compress_struct* cinfo) const { + jpeg_destroy_compress(cinfo); + } +}; + namespace android { namespace camera3 { @@ -118,16 +125,16 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out bool mSuccess; } dmgr; - jpeg_compress_struct cinfo = {}; + std::unique_ptr cinfo = std::make_unique(); jpeg_error_mgr jerr; // Initialize error handling with standard callbacks, but // then override output_message (to print to ALOG) and // error_exit to set a flag and print a message instead // of killing the whole process. - cinfo.err = jpeg_std_error(&jerr); + cinfo->err = jpeg_std_error(&jerr); - cinfo.err->output_message = [](j_common_ptr cinfo) { + cinfo->err->output_message = [](j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; /* Create the message */ @@ -135,7 +142,7 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out ALOGE("libjpeg error: %s", buffer); }; - cinfo.err->error_exit = [](j_common_ptr cinfo) { + cinfo->err->error_exit = [](j_common_ptr cinfo) { (*cinfo->err->output_message)(cinfo); if(cinfo->client_data) { auto & dmgr = *static_cast(cinfo->client_data); @@ -144,12 +151,12 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out }; // Now that we initialized some callbacks, let's create our compressor - jpeg_create_compress(&cinfo); + jpeg_create_compress(cinfo.get()); dmgr.mBuffer = static_cast(out); dmgr.mBufferSize = maxOutSize; dmgr.mEncodedSize = 0; dmgr.mSuccess = true; - cinfo.client_data = static_cast(&dmgr); + cinfo->client_data = static_cast(&dmgr); // These lambdas become C-style function pointers and as per C++11 spec // may not capture anything. @@ -171,28 +178,28 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer; ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); }; - cinfo.dest = reinterpret_cast(&dmgr); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 1; - cinfo.in_color_space = JCS_GRAYSCALE; + cinfo->dest = static_cast(&dmgr); + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; // Initialize defaults and then override what we want - jpeg_set_defaults(&cinfo); + jpeg_set_defaults(cinfo.get()); - jpeg_set_quality(&cinfo, jpegQuality, 1); - jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); - cinfo.raw_data_in = 0; - cinfo.dct_method = JDCT_IFAST; + jpeg_set_quality(cinfo.get(), jpegQuality, 1); + jpeg_set_colorspace(cinfo.get(), JCS_GRAYSCALE); + cinfo->raw_data_in = 0; + cinfo->dct_method = JDCT_IFAST; - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; + cinfo->comp_info[0].h_samp_factor = 1; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[0].v_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; - jpeg_start_compress(&cinfo, TRUE); + jpeg_start_compress(cinfo.get(), TRUE); if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) { std::unique_ptr utils(ExifUtils::create()); @@ -204,19 +211,19 @@ status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out if (utils->generateApp1()) { const uint8_t* exifBuffer = utils->getApp1Buffer(); size_t exifBufferSize = utils->getApp1Length(); - jpeg_write_marker(&cinfo, JPEG_APP0 + 1, static_cast(exifBuffer), + jpeg_write_marker(cinfo.get(), JPEG_APP0 + 1, static_cast(exifBuffer), exifBufferSize); } else { ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__); } } - for (size_t i = 0; i < cinfo.image_height; i++) { + for (size_t i = 0; i < cinfo->image_height; i++) { auto currentRow = static_cast(in + i*width); - jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1); + jpeg_write_scanlines(cinfo.get(), ¤tRow, /*num_lines*/1); } - jpeg_finish_compress(&cinfo); + jpeg_finish_compress(cinfo.get()); actualSize = dmgr.mEncodedSize; if (dmgr.mSuccess) { @@ -430,12 +437,12 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de return BAD_VALUE; } - // It is not possible to generate an imaging model without instrinsic calibration. - if (inputFrame.mIsInstrinsicCalibrationValid) { + // It is not possible to generate an imaging model without intrinsic calibration. + if (inputFrame.mIsIntrinsicCalibrationValid) { // The camera intrinsic calibration layout is as follows: // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew] - const dynamic_depth::Point focalLength(inputFrame.mInstrinsicCalibration[0], - inputFrame.mInstrinsicCalibration[1]); + const dynamic_depth::Point focalLength(inputFrame.mIntrinsicCalibration[0], + inputFrame.mIntrinsicCalibration[1]); size_t width = inputFrame.mMainJpegWidth; size_t height = inputFrame.mMainJpegHeight; if (switchDimensions) { @@ -444,9 +451,9 @@ extern "C" int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t de } const Dimension imageSize(width, height); ImagingModelParams imagingParams(focalLength, imageSize); - imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2]; - imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3]; - imagingParams.skew = inputFrame.mInstrinsicCalibration[4]; + imagingParams.principal_point.x = inputFrame.mIntrinsicCalibration[2]; + imagingParams.principal_point.y = inputFrame.mIntrinsicCalibration[3]; + imagingParams.skew = inputFrame.mIntrinsicCalibration[4]; // The camera lens distortion contains the following lens correction coefficients. // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5] diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h index 6a2fbffb6b..ba5ca9ec34 100644 --- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h +++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h @@ -39,8 +39,8 @@ struct DepthPhotoInputFrame { size_t mMaxJpegSize; uint8_t mJpegQuality; uint8_t mIsLogical; - float mInstrinsicCalibration[5]; - uint8_t mIsInstrinsicCalibrationValid; + float mIntrinsicCalibration[5]; + uint8_t mIsIntrinsicCalibrationValid; float mLensDistortion[5]; uint8_t mIsLensDistortionValid; DepthPhotoOrientation mOrientation; @@ -57,8 +57,8 @@ struct DepthPhotoInputFrame { mMaxJpegSize(0), mJpegQuality(100), mIsLogical(0), - mInstrinsicCalibration{0.f}, - mIsInstrinsicCalibrationValid(0), + mIntrinsicCalibration{0.f}, + mIsIntrinsicCalibrationValid(0), mLensDistortion{0.f}, mIsLensDistortionValid(0), mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {} -- GitLab From 8e6c71f51392a795c1e38d32d9c1385c87cca5f5 Mon Sep 17 00:00:00 2001 From: Praveen Chavan Date: Tue, 18 Jun 2019 14:41:59 -0700 Subject: [PATCH 1517/1530] stagefright: Fix OmxInfoBuilder assignment of kFlagIsHardwareAccelerated OmxInfoBuilder would only add kFlagIsHardwareAccelerated to attr if attribute::software-code exists in the node attributes This change inverts this logic Author: Michael Gonzalez CRs-Fixed: 2430504 Bug: 132939417 Change-Id: I9813098c062732b2a992350d72d59e52fd67292b --- media/libstagefright/OmxInfoBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp index 8910463a2c..79ffdeb894 100644 --- a/media/libstagefright/OmxInfoBuilder.cpp +++ b/media/libstagefright/OmxInfoBuilder.cpp @@ -193,7 +193,7 @@ status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) { // treat OMX.google codecs as non-hardware-accelerated and non-vendor if (!isSoftware) { attrs |= MediaCodecInfo::kFlagIsVendor; - if (std::count_if( + if (!std::count_if( node.attributes.begin(), node.attributes.end(), [](const IOmxStore::Attribute &i) -> bool { return i.key == "attribute::software-codec"; -- GitLab From 23f4d6cc618576ac53da4ce9e761e9bee3109333 Mon Sep 17 00:00:00 2001 From: Ray Essick Date: Thu, 20 Jun 2019 10:16:37 -0700 Subject: [PATCH 1518/1530] Enable count-based expiration for media metrics in addition to the time-based expiration, enable count based expiration and set it to 2000 records, about 5x the daily average. Caps the memory footprint. Bug: 135552978 Test: boot, enough traffic to force expirations Change-Id: Ide96df24b00e8f115905678800c782f29a91bcba --- services/mediaanalytics/MediaAnalyticsService.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp index 3626ad1514..0e7edfdb44 100644 --- a/services/mediaanalytics/MediaAnalyticsService.cpp +++ b/services/mediaanalytics/MediaAnalyticsService.cpp @@ -82,7 +82,12 @@ namespace android { // (0 for either of these disables that threshold) // static constexpr nsecs_t kMaxRecordAgeNs = 28 * 3600 * (1000*1000*1000ll); -static constexpr int kMaxRecords = 0; +// 2019/6: average daily per device is currently 375-ish; +// setting this to 2000 is large enough to catch most devices +// we'll lose some data on very very media-active devices, but only for +// the gms collection; statsd will have already covered those for us. +// This also retains enough information to help with bugreports +static constexpr int kMaxRecords = 2000; // max we expire in a single call, to constrain how long we hold the // mutex, which also constrains how long a client might wait. -- GitLab From a39882b5d85353a2d308ed7c7d3bd3cec1301391 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 20 Jun 2019 16:13:56 -0700 Subject: [PATCH 1519/1530] CCodecBuffers: add support for reallocating graphic buffers Bug: 135261503 Test: modified AdaptivePlaybackTest Change-Id: I64de657175c671672ab67d8941e35f0ba35c8dbd --- media/codec2/sfplugin/CCodecBuffers.cpp | 21 +++++++++++++++++++-- media/codec2/sfplugin/CCodecBuffers.h | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp index 5ebd5bd317..26c702ddc0 100644 --- a/media/codec2/sfplugin/CCodecBuffers.cpp +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -439,6 +439,10 @@ size_t BuffersArrayImpl::numClientBuffers() const { }); } +size_t BuffersArrayImpl::arraySize() const { + return mBuffers.size(); +} + // InputBuffersArray void InputBuffersArray::initialize( @@ -883,11 +887,24 @@ void OutputBuffersArray::realloc(const std::shared_ptr &c2buffer) { mAlloc = [format = mFormat, size] { return new LocalLinearBuffer(format, new ABuffer(size)); }; + ALOGD("[%s] reallocating with linear buffer of size %u", mName, size); break; } - // TODO: add support - case C2BufferData::GRAPHIC: [[fallthrough]]; + case C2BufferData::GRAPHIC: { + // This is only called for RawGraphicOutputBuffers. + mAlloc = [format = mFormat, + lbp = LocalBufferPool::Create(kMaxLinearBufferSize * mImpl.arraySize())] { + return ConstGraphicBlockBuffer::AllocateEmpty( + format, + [lbp](size_t capacity) { + return lbp->newBuffer(capacity); + }); + }; + ALOGD("[%s] reallocating with graphic buffer: format = %s", + mName, mFormat->debugString().c_str()); + break; + } case C2BufferData::INVALID: [[fallthrough]]; case C2BufferData::LINEAR_CHUNKS: [[fallthrough]]; diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h index e4f2809456..2cb6b813c1 100644 --- a/media/codec2/sfplugin/CCodecBuffers.h +++ b/media/codec2/sfplugin/CCodecBuffers.h @@ -478,6 +478,11 @@ public: */ size_t numClientBuffers() const; + /** + * Return the size of the array. + */ + size_t arraySize() const; + private: std::string mImplName; ///< name for debugging const char *mName; ///< C-string version of name -- GitLab From 7802c68aebf7908983508fd4a52a7d53746a80eb Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 21 Jun 2019 14:17:58 -0700 Subject: [PATCH 1520/1530] m4v_h263: add a test for invalid/negative value Test: run poc with and without the patch. Bug: 134578122 Change-Id: I2d11826d1d9e2669aa5627065dc627729ddc823b --- .../libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp index 48414d7b32..5880e3260d 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp @@ -52,7 +52,11 @@ PV_STATUS PV_ReadVideoPacketHeader(VideoDecData *video, int *next_MB) PV_BitstreamByteAlign(stream); BitstreamReadBits32(stream, resync_marker_length); - *next_MB = (int) BitstreamReadBits16(stream, nbits); + int mbnum = (int) BitstreamReadBits16(stream, nbits); + if (mbnum < 0) { + return PV_FAIL; + } + *next_MB = mbnum; // if (*next_MB <= video->mbnum) /* needs more investigation */ // *next_MB = video->mbnum+1; -- GitLab From a8488c9c32de3d4cfa6f9b96bec362bbc337a91b Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Fri, 21 Jun 2019 12:45:34 -0700 Subject: [PATCH 1521/1530] camera2 ndk/vndk: cleanup->stop CameraDevice's looper in ~ACameraDevice() It's possible that the following sequence happens: 1) hwbinder / binder thread T1: onResultReceived() starts -> promotes wp to sp<>; 2) Some other app thread T2 : ACameraDevice_close() -> delete ACameraDevice -> doesn't result in CameraDevice's destructor running since mCameraDevice has another live reference, app destroys some object O1. 3) T3 (callback looper thread): callback is received since looper is still running which accesses dead app object O1 -> results in undefined behavior. 4) T1: onResultReceived completes and CameraDevice is destructed We need to stop CameraDevice's looper thread (that waits for all callbacks queued to complete) in ~ACameraDevice() so we receive no callbacks after ACameraDevice is closed. Bug: 135641415 Test: CTS native tests: no new failures Test: AImageReaderVendorTest; enroll; while(1) auth; Change-Id: Ia24de753f6ee409d941fff39616f09df2164880a Merged-In: Ia24de753f6ee409d941fff39616f09df2164880a Signed-off-by: Jayant Chowdhary (cherry picked from commit 174084011ca8b593a8cf35412928517b9e864be9) Signed-off-by: Jayant Chowdhary --- camera/ndk/impl/ACameraDevice.cpp | 22 ++++++++++++++------ camera/ndk/impl/ACameraDevice.h | 5 ++++- camera/ndk/ndk_vendor/impl/ACameraDevice.cpp | 22 ++++++++++++++------ camera/ndk/ndk_vendor/impl/ACameraDevice.h | 5 ++++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index 25a81ebc28..d24cb814a3 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -28,6 +28,10 @@ #include "ACameraCaptureSession.inc" +ACameraDevice::~ACameraDevice() { + mDevice->stopLooper(); +} + namespace android { namespace acam { @@ -116,14 +120,10 @@ CameraDevice::~CameraDevice() { if (!isClosed()) { disconnectLocked(session); } + LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr, + "CameraDevice looper should've been stopped before ~CameraDevice"); mCurrentSession = nullptr; - if (mCbLooper != nullptr) { - mCbLooper->unregisterHandler(mHandler->id()); - mCbLooper->stop(); - } } - mCbLooper.clear(); - mHandler.clear(); } void @@ -892,6 +892,16 @@ CameraDevice::onCaptureErrorLocked( return; } +void CameraDevice::stopLooper() { + Mutex::Autolock _l(mDeviceLock); + if (mCbLooper != nullptr) { + mCbLooper->unregisterHandler(mHandler->id()); + mCbLooper->stop(); + } + mCbLooper.clear(); + mHandler.clear(); +} + CameraDevice::CallbackHandler::CallbackHandler(const char* id) : mId(id) { } diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index c92a95f822..7a35bf0e81 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -109,6 +109,9 @@ class CameraDevice final : public RefBase { inline ACameraDevice* getWrapper() const { return mWrapper; }; + // Stop the looper thread and unregister the handler + void stopLooper(); + private: friend ACameraCaptureSession; camera_status_t checkCameraClosedOrErrorLocked() const; @@ -354,7 +357,7 @@ struct ACameraDevice { sp chars) : mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {} - ~ACameraDevice() {}; + ~ACameraDevice(); /******************* * NDK public APIs * diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 529c167edc..24b1cae2de 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -33,6 +33,10 @@ using namespace android; +ACameraDevice::~ACameraDevice() { + mDevice->stopLooper(); +} + namespace android { namespace acam { @@ -119,13 +123,9 @@ CameraDevice::~CameraDevice() { disconnectLocked(session); } mCurrentSession = nullptr; - if (mCbLooper != nullptr) { - mCbLooper->unregisterHandler(mHandler->id()); - mCbLooper->stop(); - } + LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr, + "CameraDevice looper should've been stopped before ~CameraDevice"); } - mCbLooper.clear(); - mHandler.clear(); } void @@ -1422,6 +1422,16 @@ CameraDevice::checkAndFireSequenceCompleteLocked() { } } +void CameraDevice::stopLooper() { + Mutex::Autolock _l(mDeviceLock); + if (mCbLooper != nullptr) { + mCbLooper->unregisterHandler(mHandler->id()); + mCbLooper->stop(); + } + mCbLooper.clear(); + mHandler.clear(); +} + /** * Camera service callback implementation */ diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 829b08452e..3328a85686 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -133,6 +133,9 @@ class CameraDevice final : public RefBase { bool setDeviceMetadataQueues(); inline ACameraDevice* getWrapper() const { return mWrapper; }; + // Stop the looper thread and unregister the handler + void stopLooper(); + private: friend ACameraCaptureSession; @@ -383,7 +386,7 @@ struct ACameraDevice { sp chars) : mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {} - ~ACameraDevice() {}; + ~ACameraDevice(); /******************* * NDK public APIs * -- GitLab From f08e84030e7c224ec2fd064d88759f426db88496 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Thu, 13 Jun 2019 15:07:47 +0700 Subject: [PATCH 1522/1530] Fix names in Codec2 VTS Test: vts-tradefed run vts -m VtsHalMediaC2V1_0Host Bug: 133187143 Change-Id: If4057a11563034415fe244294d36892feb7a62d7 --- .../hidl/1.0/vts/functional/audio/Android.bp | 12 +++++----- ...> VtsHalMediaC2V1_0TargetAudioDecTest.cpp} | 0 ...> VtsHalMediaC2V1_0TargetAudioEncTest.cpp} | 0 .../hidl/1.0/vts/functional/common/Android.bp | 6 ++--- .../hidl/1.0/vts/functional/common/README.md | 22 +++++++++---------- .../1.0/vts/functional/component/Android.bp | 6 ++--- ... VtsHalMediaC2V1_0TargetComponentTest.cpp} | 0 .../hidl/1.0/vts/functional/master/Android.bp | 6 ++--- ... => VtsHalMediaC2V1_0TargetMasterTest.cpp} | 0 .../hidl/1.0/vts/functional/video/Android.bp | 12 +++++----- ...> VtsHalMediaC2V1_0TargetVideoDecTest.cpp} | 0 ...> VtsHalMediaC2V1_0TargetVideoEncTest.cpp} | 0 12 files changed, 32 insertions(+), 32 deletions(-) rename media/codec2/hidl/1.0/vts/functional/audio/{VtsHidlC2V1_0TargetAudioDecTest.cpp => VtsHalMediaC2V1_0TargetAudioDecTest.cpp} (100%) rename media/codec2/hidl/1.0/vts/functional/audio/{VtsHidlC2V1_0TargetAudioEncTest.cpp => VtsHalMediaC2V1_0TargetAudioEncTest.cpp} (100%) rename media/codec2/hidl/1.0/vts/functional/component/{VtsHidlC2V1_0TargetComponentTest.cpp => VtsHalMediaC2V1_0TargetComponentTest.cpp} (100%) rename media/codec2/hidl/1.0/vts/functional/master/{VtsHidlC2V1_0TargetMasterTest.cpp => VtsHalMediaC2V1_0TargetMasterTest.cpp} (100%) rename media/codec2/hidl/1.0/vts/functional/video/{VtsHidlC2V1_0TargetVideoDecTest.cpp => VtsHalMediaC2V1_0TargetVideoDecTest.cpp} (100%) rename media/codec2/hidl/1.0/vts/functional/video/{VtsHidlC2V1_0TargetVideoEncTest.cpp => VtsHalMediaC2V1_0TargetVideoEncTest.cpp} (100%) diff --git a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp index 687754bcc0..65f0d09c6b 100644 --- a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp @@ -15,19 +15,19 @@ */ cc_test { - name: "VtsHidlC2V1_0TargetAudioDecTest", - defaults: ["VtsMediaC2V1_0Defaults"], + name: "VtsHalMediaC2V1_0TargetAudioDecTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], srcs: [ - "VtsHidlC2V1_0TargetAudioDecTest.cpp", + "VtsHalMediaC2V1_0TargetAudioDecTest.cpp", //"media_audio_hidl_test_common.cpp" ], } cc_test { - name: "VtsHidlC2V1_0TargetAudioEncTest", - defaults: ["VtsMediaC2V1_0Defaults"], + name: "VtsHalMediaC2V1_0TargetAudioEncTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], srcs: [ - "VtsHidlC2V1_0TargetAudioEncTest.cpp", + "VtsHalMediaC2V1_0TargetAudioEncTest.cpp", //"media_audio_hidl_test_common.cpp" ], } diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp index da0061ad05..a011ba394c 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp @@ -1,5 +1,5 @@ cc_library_static { - name: "VtsMediaC2V1_0CommonUtil", + name: "VtsHalMediaC2V1_0CommonUtil", defaults: [ "VtsHalTargetTestDefaults", "libcodec2-hidl-client-defaults", @@ -17,14 +17,14 @@ cc_library_static { } cc_defaults { - name: "VtsMediaC2V1_0Defaults", + name: "VtsHalMediaC2V1_0Defaults", defaults: [ "VtsHalTargetTestDefaults", "libcodec2-hidl-client-defaults", ], static_libs: [ - "VtsMediaC2V1_0CommonUtil", + "VtsHalMediaC2V1_0CommonUtil", ], shared_libs: [ diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md index 3deab1056f..50e8356f9e 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/README.md +++ b/media/codec2/hidl/1.0/vts/functional/common/README.md @@ -3,29 +3,29 @@ #### master : Functionality of master is to enumerate all the Codec2 components available in C2 media service. -usage: VtsHidlC2V1\_0TargetMasterTest -I default +usage: VtsHalMediaC2V1\_0TargetMasterTest -I default #### component : Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass. -usage: VtsHidlC2V1\_0TargetComponentTest -I software -C -example: VtsHidlC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder +usage: VtsHalMediaC2V1\_0TargetComponentTest -I software -C +example: VtsHalMediaC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder #### audio : Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests. -usage: VtsHidlC2V1\_0TargetAudioDecTest -I default -C -P /sdcard/res/ -usage: VtsHidlC2V1\_0TargetAudioEncTest -I software -C -P /sdcard/res/ +usage: VtsHalMediaC2V1\_0TargetAudioDecTest -I default -C -P /sdcard/media/ +usage: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C -P /sdcard/media/ -example: VtsHidlC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/res/ -example: VtsHidlC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/ +example: VtsHalMediaC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/media/ +example: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/media/ #### video : Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests. -usage: VtsHidlC2V1\_0TargetVideoDecTest -I default -C -P /sdcard/res/ -usage: VtsHidlC2V1\_0TargetVideoEncTest -I software -C -P /sdcard/res/ +usage: VtsHalMediaC2V1\_0TargetVideoDecTest -I default -C -P /sdcard/media/ +usage: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C -P /sdcard/media/ -example: VtsHidlC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/ -example: VtsHidlC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/res/ +example: VtsHalMediaC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/media/ +example: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/media/ diff --git a/media/codec2/hidl/1.0/vts/functional/component/Android.bp b/media/codec2/hidl/1.0/vts/functional/component/Android.bp index 4b913b6339..7ec64ee6e7 100644 --- a/media/codec2/hidl/1.0/vts/functional/component/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/component/Android.bp @@ -15,8 +15,8 @@ */ cc_test { - name: "VtsHidlC2V1_0TargetComponentTest", - defaults: ["VtsMediaC2V1_0Defaults"], - srcs: ["VtsHidlC2V1_0TargetComponentTest.cpp"], + name: "VtsHalMediaC2V1_0TargetComponentTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], + srcs: ["VtsHalMediaC2V1_0TargetComponentTest.cpp"], } diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp rename to media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp diff --git a/media/codec2/hidl/1.0/vts/functional/master/Android.bp b/media/codec2/hidl/1.0/vts/functional/master/Android.bp index e164d6847e..53e323e5db 100644 --- a/media/codec2/hidl/1.0/vts/functional/master/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/master/Android.bp @@ -15,8 +15,8 @@ */ cc_test { - name: "VtsHidlC2V1_0TargetMasterTest", - defaults: ["VtsMediaC2V1_0Defaults"], - srcs: ["VtsHidlC2V1_0TargetMasterTest.cpp"], + name: "VtsHalMediaC2V1_0TargetMasterTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], + srcs: ["VtsHalMediaC2V1_0TargetMasterTest.cpp"], } diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp rename to media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp 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 6e57ee7cb2..b951306077 100644 --- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp @@ -15,14 +15,14 @@ */ cc_test { - name: "VtsHidlC2V1_0TargetVideoDecTest", - defaults: ["VtsMediaC2V1_0Defaults"], - srcs: ["VtsHidlC2V1_0TargetVideoDecTest.cpp"], + name: "VtsHalMeidaC2V1_0TargetVideoDecTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], + srcs: ["VtsHalMediaC2V1_0TargetVideoDecTest.cpp"], } cc_test { - name: "VtsHidlC2V1_0TargetVideoEncTest", - defaults: ["VtsMediaC2V1_0Defaults"], - srcs: ["VtsHidlC2V1_0TargetVideoEncTest.cpp"], + name: "VtsHalMediaC2V1_0TargetVideoEncTest", + defaults: ["VtsHalMediaC2V1_0Defaults"], + srcs: ["VtsHalMediaC2V1_0TargetVideoEncTest.cpp"], } diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp similarity index 100% rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp -- GitLab From 0838494fc03397175c363763e3365a39ee790ed6 Mon Sep 17 00:00:00 2001 From: Pawin Vongmasa Date: Wed, 15 May 2019 08:42:44 -0700 Subject: [PATCH 1523/1530] Add support for graphics.mapper@3.0 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Bug: 128013727 Bug: 129670826 Bug: 135733310 Change-Id: Iaa7a3f06956f9f81b3d4338bbf32fea282cdd511 Merged-In: Iaa7a3f06956f9f81b3d4338bbf32fea282cdd511 --- media/codec2/vndk/Android.bp | 2 + media/codec2/vndk/C2AllocatorGralloc.cpp | 678 ++++++++++++++++------- 2 files changed, 483 insertions(+), 197 deletions(-) diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index ca69810039..b6ddfabf2d 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -50,8 +50,10 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.allocator@2.0", + "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", "android.hardware.media.bufferpool@2.0", "libbase", "libbinder", diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp index e698bf4b9a..dab469770a 100644 --- a/media/codec2/vndk/C2AllocatorGralloc.cpp +++ b/media/codec2/vndk/C2AllocatorGralloc.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include @@ -29,7 +31,7 @@ namespace android { -namespace { +namespace /* unnamed */ { enum : uint64_t { /** * Usage mask that is passed through from gralloc to Codec 2.0 usage. @@ -40,7 +42,7 @@ namespace { // verify that passthrough mask is within the platform mask static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, ""); -} +} // unnamed C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) { // gralloc does not support WRITE_PROTECTED @@ -59,39 +61,59 @@ uint64_t C2AndroidMemoryUsage::asGrallocUsage() const { (expected & PASSTHROUGH_USAGE_MASK)); } -using ::android::hardware::graphics::allocator::V2_0::IAllocator; -using ::android::hardware::graphics::common::V1_0::BufferUsage; -using ::android::hardware::graphics::common::V1_0::PixelFormat; -using ::android::hardware::graphics::mapper::V2_0::BufferDescriptor; -using ::android::hardware::graphics::mapper::V2_0::Error; -using ::android::hardware::graphics::mapper::V2_0::IMapper; -using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; +using ::android::hardware::graphics::common::V1_0::BufferUsage; +using PixelFormat2 = ::android::hardware::graphics::common::V1_0::PixelFormat; +using PixelFormat3 = ::android::hardware::graphics::common::V1_2::PixelFormat; + +using IAllocator2 = ::android::hardware::graphics::allocator::V2_0::IAllocator; +using BufferDescriptor2 = ::android::hardware::graphics::mapper::V2_0::BufferDescriptor; +using Error2 = ::android::hardware::graphics::mapper::V2_0::Error; +using IMapper2 = ::android::hardware::graphics::mapper::V2_0::IMapper; + +using IAllocator3 = ::android::hardware::graphics::allocator::V3_0::IAllocator; +using BufferDescriptor3 = ::android::hardware::graphics::mapper::V3_0::BufferDescriptor; +using Error3 = ::android::hardware::graphics::mapper::V3_0::Error; +using IMapper3 = ::android::hardware::graphics::mapper::V3_0::IMapper; -namespace { +namespace /* unnamed */ { -struct BufferDescriptorInfo { - IMapper::BufferDescriptorInfo mapperInfo; +struct BufferDescriptorInfo2 { + IMapper2::BufferDescriptorInfo mapperInfo; uint32_t stride; }; -} +struct BufferDescriptorInfo3 { + IMapper3::BufferDescriptorInfo mapperInfo; + uint32_t stride; +}; /* ===================================== GRALLOC ALLOCATION ==================================== */ -static c2_status_t maperr2error(Error maperr) { +c2_status_t maperr2error(Error2 maperr) { + switch (maperr) { + case Error2::NONE: return C2_OK; + case Error2::BAD_DESCRIPTOR: return C2_BAD_VALUE; + case Error2::BAD_BUFFER: return C2_BAD_VALUE; + case Error2::BAD_VALUE: return C2_BAD_VALUE; + case Error2::NO_RESOURCES: return C2_NO_MEMORY; + case Error2::UNSUPPORTED: return C2_CANNOT_DO; + } + return C2_CORRUPTED; +} + +c2_status_t maperr2error(Error3 maperr) { switch (maperr) { - case Error::NONE: return C2_OK; - case Error::BAD_DESCRIPTOR: return C2_BAD_VALUE; - case Error::BAD_BUFFER: return C2_BAD_VALUE; - case Error::BAD_VALUE: return C2_BAD_VALUE; - case Error::NO_RESOURCES: return C2_NO_MEMORY; - case Error::UNSUPPORTED: return C2_CANNOT_DO; + case Error3::NONE: return C2_OK; + case Error3::BAD_DESCRIPTOR: return C2_BAD_VALUE; + case Error3::BAD_BUFFER: return C2_BAD_VALUE; + case Error3::BAD_VALUE: return C2_BAD_VALUE; + case Error3::NO_RESOURCES: return C2_NO_MEMORY; + case Error3::UNSUPPORTED: return C2_CANNOT_DO; } return C2_CORRUPTED; } -static bool native_handle_is_invalid(const native_handle_t *const handle) { // perform basic validation of a native handle if (handle == nullptr) { @@ -214,23 +236,6 @@ public: return res; } - static native_handle_t* UnwrapNativeHandle( - const C2Handle *const handle, - uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) { - const ExtraData *xd = getExtraData(handle); - if (xd == nullptr || xd->magic != MAGIC) { - return nullptr; - } - *generation = xd->generation; - *igbp_id = unsigned(xd->igbp_id_lo) | uint64_t(unsigned(xd->igbp_id_hi)) << 32; - *igbp_slot = xd->igbp_slot; - native_handle_t *res = native_handle_create(handle->numFds, handle->numInts - NUM_INTS); - if (res != nullptr) { - memcpy(&res->data, &handle->data, sizeof(int) * (res->numFds + res->numInts)); - } - return res; - } - static const C2HandleGralloc* Import( const C2Handle *const handle, uint32_t *width, uint32_t *height, uint32_t *format, @@ -252,16 +257,12 @@ public: } }; +} // unnamed namespace + native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) { return C2HandleGralloc::UnwrapNativeHandle(handle); } -native_handle_t *UnwrapNativeCodec2GrallocHandle( - const C2Handle *const handle, - uint32_t *generation, uint64_t *igbp_id, uint32_t *igbp_slot) { - return C2HandleGralloc::UnwrapNativeHandle(handle, generation, igbp_id, igbp_slot); -} - C2Handle *WrapNativeCodec2GrallocHandle( const native_handle_t *const handle, uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride, @@ -286,8 +287,14 @@ public: // internal methods // |handle| will be moved. C2AllocationGralloc( - const BufferDescriptorInfo &info, - const sp &mapper, + const BufferDescriptorInfo2 &info, + const sp &mapper, + hidl_handle &hidlHandle, + const C2HandleGralloc *const handle, + C2Allocator::id_t allocatorId); + C2AllocationGralloc( + const BufferDescriptorInfo3 &info, + const sp &mapper, hidl_handle &hidlHandle, const C2HandleGralloc *const handle, C2Allocator::id_t allocatorId); @@ -295,8 +302,10 @@ public: c2_status_t status() const; private: - const BufferDescriptorInfo mInfo; - const sp mMapper; + const BufferDescriptorInfo2 mInfo2{}; + const sp mMapper2{nullptr}; + const BufferDescriptorInfo3 mInfo3{}; + const sp mMapper3{nullptr}; const hidl_handle mHidlHandle; const C2HandleGralloc *mHandle; buffer_handle_t mBuffer; @@ -307,14 +316,31 @@ private: }; C2AllocationGralloc::C2AllocationGralloc( - const BufferDescriptorInfo &info, - const sp &mapper, + const BufferDescriptorInfo2 &info, + const sp &mapper, hidl_handle &hidlHandle, const C2HandleGralloc *const handle, C2Allocator::id_t allocatorId) : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height), - mInfo(info), - mMapper(mapper), + mInfo2(info), + mMapper2(mapper), + mHidlHandle(std::move(hidlHandle)), + mHandle(handle), + mBuffer(nullptr), + mLockedHandle(nullptr), + mLocked(false), + mAllocatorId(allocatorId) { +} + +C2AllocationGralloc::C2AllocationGralloc( + const BufferDescriptorInfo3 &info, + const sp &mapper, + hidl_handle &hidlHandle, + const C2HandleGralloc *const handle, + C2Allocator::id_t allocatorId) + : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height), + mInfo3(info), + mMapper3(mapper), mHidlHandle(std::move(hidlHandle)), mHandle(handle), mBuffer(nullptr), @@ -330,7 +356,17 @@ C2AllocationGralloc::~C2AllocationGralloc() { unmap(addr, C2Rect(), nullptr); } if (mBuffer) { - mMapper->freeBuffer(const_cast(mBuffer)); + if (mMapper2) { + if (!mMapper2->freeBuffer(const_cast( + mBuffer)).isOk()) { + ALOGE("failed transaction: freeBuffer"); + } + } else { + if (!mMapper3->freeBuffer(const_cast( + mBuffer)).isOk()) { + ALOGE("failed transaction: freeBuffer"); + } + } } if (mHandle) { native_handle_delete( @@ -365,13 +401,29 @@ c2_status_t C2AllocationGralloc::map( c2_status_t err = C2_OK; if (!mBuffer) { - mMapper->importBuffer( - mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) { - err = maperr2error(maperr); - if (err == C2_OK) { - mBuffer = static_cast(buffer); - } - }); + if (mMapper2) { + if (!mMapper2->importBuffer( + mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) { + err = maperr2error(maperr); + if (err == C2_OK) { + mBuffer = static_cast(buffer); + } + }).isOk()) { + ALOGE("failed transaction: importBuffer"); + return C2_CORRUPTED; + } + } else { + if (!mMapper3->importBuffer( + mHidlHandle, [&err, this](const auto &maperr, const auto &buffer) { + err = maperr2error(maperr); + if (err == C2_OK) { + mBuffer = static_cast(buffer); + } + }).isOk()) { + ALOGE("failed transaction: importBuffer (@3.0)"); + return C2_CORRUPTED; + } + } if (err != C2_OK) { ALOGD("importBuffer failed: %d", err); return err; @@ -386,30 +438,66 @@ c2_status_t C2AllocationGralloc::map( if (mHandle) { mHandle->getIgbpData(&generation, &igbp_id, &igbp_slot); } - mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle( - mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height, - (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride, - generation, igbp_id, igbp_slot); + if (mMapper2) { + mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle( + mBuffer, mInfo2.mapperInfo.width, mInfo2.mapperInfo.height, + (uint32_t)mInfo2.mapperInfo.format, mInfo2.mapperInfo.usage, + mInfo2.stride, generation, igbp_id, igbp_slot); + } else { + mLockedHandle = C2HandleGralloc::WrapAndMoveNativeHandle( + mBuffer, mInfo3.mapperInfo.width, mInfo3.mapperInfo.height, + (uint32_t)mInfo3.mapperInfo.format, mInfo3.mapperInfo.usage, + mInfo3.stride, generation, igbp_id, igbp_slot); + } } - switch (mInfo.mapperInfo.format) { - case PixelFormat::RGBA_1010102: { + PixelFormat3 format = mMapper2 ? + PixelFormat3(mInfo2.mapperInfo.format) : + PixelFormat3(mInfo3.mapperInfo.format); + switch (format) { + case PixelFormat3::RGBA_1010102: { // TRICKY: this is used for media as YUV444 in the case when it is queued directly to a // Surface. In all other cases it is RGBA. We don't know which case it is here, so // default to YUV for now. void *pointer = nullptr; - mMapper->lock( - const_cast(mBuffer), - grallocUsage, - { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, - // TODO: fence - hidl_handle(), - [&err, &pointer](const auto &maperr, const auto &mapPointer) { - err = maperr2error(maperr); - if (err == C2_OK) { - pointer = mapPointer; - } - }); + if (mMapper2) { + if (!mMapper2->lock( + const_cast(mBuffer), + grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &pointer](const auto &maperr, const auto &mapPointer) { + err = maperr2error(maperr); + if (err == C2_OK) { + pointer = mapPointer; + } + }).isOk()) { + ALOGE("failed transaction: lock(RGBA_1010102)"); + return C2_CORRUPTED; + } + } else { + if (!mMapper3->lock( + const_cast(mBuffer), + grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &pointer](const auto &maperr, const auto &mapPointer, + int32_t bytesPerPixel, int32_t bytesPerStride) { + err = maperr2error(maperr); + if (err == C2_OK) { + pointer = mapPointer; + } + (void)bytesPerPixel; + (void)bytesPerStride; + }).isOk()) { + ALOGE("failed transaction: lock(RGBA_1010102) (@3.0)"); + return C2_CORRUPTED; + } + } if (err != C2_OK) { ALOGD("lock failed: %d", err); return err; @@ -422,10 +510,13 @@ c2_status_t C2AllocationGralloc::map( layout->type = C2PlanarLayout::TYPE_YUVA; layout->numPlanes = 4; layout->rootPlanes = 1; + int32_t stride = mMapper2 ? + int32_t(mInfo2.stride) : + int32_t(mInfo3.stride); layout->planes[C2PlanarLayout::PLANE_Y] = { C2PlaneInfo::CHANNEL_Y, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 32, // allocatedDepth @@ -438,7 +529,7 @@ c2_status_t C2AllocationGralloc::map( layout->planes[C2PlanarLayout::PLANE_U] = { C2PlaneInfo::CHANNEL_CB, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 32, // allocatedDepth @@ -451,7 +542,7 @@ c2_status_t C2AllocationGralloc::map( layout->planes[C2PlanarLayout::PLANE_V] = { C2PlaneInfo::CHANNEL_CR, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 32, // allocatedDepth @@ -464,7 +555,7 @@ c2_status_t C2AllocationGralloc::map( layout->planes[C2PlanarLayout::PLANE_A] = { C2PlaneInfo::CHANNEL_A, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 32, // allocatedDepth @@ -477,23 +568,49 @@ c2_status_t C2AllocationGralloc::map( break; } - case PixelFormat::RGBA_8888: + case PixelFormat3::RGBA_8888: // TODO: alpha channel // fall-through - case PixelFormat::RGBX_8888: { + case PixelFormat3::RGBX_8888: { void *pointer = nullptr; - mMapper->lock( - const_cast(mBuffer), - grallocUsage, - { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, - // TODO: fence - hidl_handle(), - [&err, &pointer](const auto &maperr, const auto &mapPointer) { - err = maperr2error(maperr); - if (err == C2_OK) { - pointer = mapPointer; - } - }); + if (mMapper2) { + if (!mMapper2->lock( + const_cast(mBuffer), + grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &pointer](const auto &maperr, const auto &mapPointer) { + err = maperr2error(maperr); + if (err == C2_OK) { + pointer = mapPointer; + } + }).isOk()) { + ALOGE("failed transaction: lock(RGBA_8888)"); + return C2_CORRUPTED; + } + } else { + if (!mMapper3->lock( + const_cast(mBuffer), + grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &pointer](const auto &maperr, const auto &mapPointer, + int32_t bytesPerPixel, int32_t bytesPerStride) { + err = maperr2error(maperr); + if (err == C2_OK) { + pointer = mapPointer; + } + (void)bytesPerPixel; + (void)bytesPerStride; + }).isOk()) { + ALOGE("failed transaction: lock(RGBA_8888) (@3.0)"); + return C2_CORRUPTED; + } + } if (err != C2_OK) { ALOGD("lock failed: %d", err); return err; @@ -504,10 +621,13 @@ c2_status_t C2AllocationGralloc::map( layout->type = C2PlanarLayout::TYPE_RGB; layout->numPlanes = 3; layout->rootPlanes = 1; + int32_t stride = mMapper2 ? + int32_t(mInfo2.stride) : + int32_t(mInfo3.stride); layout->planes[C2PlanarLayout::PLANE_R] = { C2PlaneInfo::CHANNEL_R, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 8, // allocatedDepth @@ -520,7 +640,7 @@ c2_status_t C2AllocationGralloc::map( layout->planes[C2PlanarLayout::PLANE_G] = { C2PlaneInfo::CHANNEL_G, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 8, // allocatedDepth @@ -533,7 +653,7 @@ c2_status_t C2AllocationGralloc::map( layout->planes[C2PlanarLayout::PLANE_B] = { C2PlaneInfo::CHANNEL_B, // channel 4, // colInc - 4 * (int32_t)mInfo.stride, // rowInc + 4 * stride, // rowInc 1, // mColSampling 1, // mRowSampling 8, // allocatedDepth @@ -546,23 +666,65 @@ c2_status_t C2AllocationGralloc::map( break; } - case PixelFormat::YCBCR_420_888: + case PixelFormat3::YCBCR_420_888: // fall-through - case PixelFormat::YV12: + case PixelFormat3::YV12: // fall-through default: { + struct YCbCrLayout { + void* y; + void* cb; + void* cr; + uint32_t yStride; + uint32_t cStride; + uint32_t chromaStep; + }; YCbCrLayout ycbcrLayout; - mMapper->lockYCbCr( - const_cast(mBuffer), grallocUsage, - { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height }, - // TODO: fence - hidl_handle(), - [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { - err = maperr2error(maperr); - if (err == C2_OK) { - ycbcrLayout = mapLayout; - } - }); + if (mMapper2) { + if (!mMapper2->lockYCbCr( + const_cast(mBuffer), grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { + err = maperr2error(maperr); + if (err == C2_OK) { + ycbcrLayout = YCbCrLayout{ + mapLayout.y, + mapLayout.cb, + mapLayout.cr, + mapLayout.yStride, + mapLayout.cStride, + mapLayout.chromaStep}; + } + }).isOk()) { + ALOGE("failed transaction: lockYCbCr"); + return C2_CORRUPTED; + } + } else { + if (!mMapper3->lockYCbCr( + const_cast(mBuffer), grallocUsage, + { (int32_t)rect.left, (int32_t)rect.top, + (int32_t)rect.width, (int32_t)rect.height }, + // TODO: fence + hidl_handle(), + [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) { + err = maperr2error(maperr); + if (err == C2_OK) { + ycbcrLayout = YCbCrLayout{ + mapLayout.y, + mapLayout.cb, + mapLayout.cr, + mapLayout.yStride, + mapLayout.cStride, + mapLayout.chromaStep}; + } + }).isOk()) { + ALOGE("failed transaction: lockYCbCr (@3.0)"); + return C2_CORRUPTED; + } + } if (err != C2_OK) { ALOGD("lockYCbCr failed: %d", err); return err; @@ -639,17 +801,37 @@ c2_status_t C2AllocationGralloc::unmap( std::lock_guard lock(mMappedLock); c2_status_t err = C2_OK; - mMapper->unlock( - const_cast(mBuffer), - [&err, &fence](const auto &maperr, const auto &releaseFence) { - // TODO - (void) fence; - (void) releaseFence; - err = maperr2error(maperr); - if (err == C2_OK) { - // TODO: fence - } - }); + if (mMapper2) { + if (!mMapper2->unlock( + const_cast(mBuffer), + [&err, &fence](const auto &maperr, const auto &releaseFence) { + // TODO + (void) fence; + (void) releaseFence; + err = maperr2error(maperr); + if (err == C2_OK) { + // TODO: fence + } + }).isOk()) { + ALOGE("failed transaction: unlock"); + return C2_CORRUPTED; + } + } else { + if (!mMapper3->unlock( + const_cast(mBuffer), + [&err, &fence](const auto &maperr, const auto &releaseFence) { + // TODO + (void) fence; + (void) releaseFence; + err = maperr2error(maperr); + if (err == C2_OK) { + // TODO: fence + } + }).isOk()) { + ALOGE("failed transaction: unlock (@3.0)"); + return C2_CORRUPTED; + } + } if (err == C2_OK) { mLocked = false; } @@ -690,8 +872,10 @@ public: private: std::shared_ptr mTraits; c2_status_t mInit; - sp mAllocator; - sp mMapper; + sp mAllocator2; + sp mMapper2; + sp mAllocator3; + sp mMapper3; const bool mBufferQueue; }; @@ -711,10 +895,18 @@ C2AllocatorGralloc::Impl::Impl(id_t id, bool bufferQueue) mTraits = std::make_shared(traits); // gralloc allocator is a singleton, so all objects share a global service - mAllocator = IAllocator::getService(); - mMapper = IMapper::getService(); - if (mAllocator == nullptr || mMapper == nullptr) { - mInit = C2_CORRUPTED; + mAllocator3 = IAllocator3::getService(); + mMapper3 = IMapper3::getService(); + if (!mAllocator3 || !mMapper3) { + mAllocator3 = nullptr; + mMapper3 = nullptr; + mAllocator2 = IAllocator2::getService(); + mMapper2 = IMapper2::getService(); + if (!mAllocator2 || !mMapper2) { + mAllocator2 = nullptr; + mMapper2 = nullptr; + mInit = C2_CORRUPTED; + } } } @@ -725,84 +917,176 @@ c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation( ALOGV("allocating buffer with usage %#llx => %#llx", (long long)usage.expected, (long long)grallocUsage); - BufferDescriptorInfo info = { - { - width, - height, - 1u, // layerCount - (PixelFormat)format, - grallocUsage, - }, - 0u, // stride placeholder - }; c2_status_t err = C2_OK; - BufferDescriptor desc; - mMapper->createDescriptor( - info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) { - err = maperr2error(maperr); - if (err == C2_OK) { - desc = descriptor; - } - }); - if (err != C2_OK) { - return err; - } - - // IAllocator shares IMapper error codes. - hidl_handle buffer; - mAllocator->allocate( - desc, - 1u, - [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) { - err = maperr2error(maperr); - if (err != C2_OK) { - return; - } - if (buffers.size() != 1u) { - err = C2_CORRUPTED; - return; - } - info.stride = stride; - buffer = buffers[0]; - }); - if (err != C2_OK) { - return err; - } + hidl_handle buffer{}; + + if (mMapper2) { + BufferDescriptorInfo2 info = { + { + width, + height, + 1u, // layerCount + PixelFormat2(format), + grallocUsage, + }, + 0u, // stride placeholder + }; + BufferDescriptor2 desc; + if (!mMapper2->createDescriptor( + info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) { + err = maperr2error(maperr); + if (err == C2_OK) { + desc = descriptor; + } + }).isOk()) { + ALOGE("failed transaction: createDescriptor"); + return C2_CORRUPTED; + } + if (err != C2_OK) { + return err; + } + // IAllocator shares IMapper error codes. + if (!mAllocator2->allocate( + desc, + 1u, + [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) { + err = maperr2error(maperr); + if (err != C2_OK) { + return; + } + if (buffers.size() != 1u) { + err = C2_CORRUPTED; + return; + } + info.stride = stride; + buffer = buffers[0]; + }).isOk()) { + ALOGE("failed transaction: allocate"); + return C2_CORRUPTED; + } + if (err != C2_OK) { + return err; + } + allocation->reset(new C2AllocationGralloc( + info, mMapper2, buffer, + C2HandleGralloc::WrapAndMoveNativeHandle( + buffer.getNativeHandle(), + width, height, + format, grallocUsage, info.stride, + 0, 0, mBufferQueue ? ~0 : 0), + mTraits->id)); + return C2_OK; + } else { + BufferDescriptorInfo3 info = { + { + width, + height, + 1u, // layerCount + PixelFormat3(format), + grallocUsage, + }, + 0u, // stride placeholder + }; + BufferDescriptor3 desc; + if (!mMapper3->createDescriptor( + info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) { + err = maperr2error(maperr); + if (err == C2_OK) { + desc = descriptor; + } + }).isOk()) { + ALOGE("failed transaction: createDescriptor"); + return C2_CORRUPTED; + } + if (err != C2_OK) { + return err; + } - allocation->reset(new C2AllocationGralloc( - info, mMapper, buffer, - C2HandleGralloc::WrapAndMoveNativeHandle( - buffer.getNativeHandle(), - info.mapperInfo.width, info.mapperInfo.height, - (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride, - 0, 0, mBufferQueue ? ~0 : 0), - mTraits->id)); - return C2_OK; + // IAllocator shares IMapper error codes. + if (!mAllocator3->allocate( + desc, + 1u, + [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) { + err = maperr2error(maperr); + if (err != C2_OK) { + return; + } + if (buffers.size() != 1u) { + err = C2_CORRUPTED; + return; + } + info.stride = stride; + buffer = buffers[0]; + }).isOk()) { + ALOGE("failed transaction: allocate"); + return C2_CORRUPTED; + } + if (err != C2_OK) { + return err; + } + allocation->reset(new C2AllocationGralloc( + info, mMapper3, buffer, + C2HandleGralloc::WrapAndMoveNativeHandle( + buffer.getNativeHandle(), + width, height, + format, grallocUsage, info.stride, + 0, 0, mBufferQueue ? ~0 : 0), + mTraits->id)); + return C2_OK; + } } c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation( const C2Handle *handle, std::shared_ptr *allocation) { - BufferDescriptorInfo info; - info.mapperInfo.layerCount = 1u; - uint32_t generation; - uint64_t igbp_id; - uint32_t igbp_slot; - const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import( - handle, - &info.mapperInfo.width, &info.mapperInfo.height, - (uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride, - &generation, &igbp_id, &igbp_slot); - if (grallocHandle == nullptr) { - return C2_BAD_VALUE; - } + if (mMapper2) { + BufferDescriptorInfo2 info; + info.mapperInfo.layerCount = 1u; + uint32_t generation; + uint64_t igbp_id; + uint32_t igbp_slot; + const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import( + handle, + &info.mapperInfo.width, &info.mapperInfo.height, + (uint32_t *)&info.mapperInfo.format, + (uint64_t *)&info.mapperInfo.usage, + &info.stride, + &generation, &igbp_id, &igbp_slot); + if (grallocHandle == nullptr) { + return C2_BAD_VALUE; + } - hidl_handle hidlHandle; - hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true); + hidl_handle hidlHandle; + hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true); - allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle, mTraits->id)); - return C2_OK; + allocation->reset(new C2AllocationGralloc( + info, mMapper2, hidlHandle, grallocHandle, mTraits->id)); + return C2_OK; + } else { + BufferDescriptorInfo3 info; + info.mapperInfo.layerCount = 1u; + uint32_t generation; + uint64_t igbp_id; + uint32_t igbp_slot; + const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import( + handle, + &info.mapperInfo.width, &info.mapperInfo.height, + (uint32_t *)&info.mapperInfo.format, + (uint64_t *)&info.mapperInfo.usage, + &info.stride, + &generation, &igbp_id, &igbp_slot); + if (grallocHandle == nullptr) { + return C2_BAD_VALUE; + } + + hidl_handle hidlHandle; + hidlHandle.setTo(C2HandleGralloc::UnwrapNativeHandle(grallocHandle), true); + + allocation->reset(new C2AllocationGralloc( + info, mMapper3, hidlHandle, grallocHandle, mTraits->id)); + return C2_OK; + } } C2AllocatorGralloc::C2AllocatorGralloc(id_t id, bool bufferQueue) -- GitLab From 6efc14faf68e3e7698adbe644d264c403d04dd0a Mon Sep 17 00:00:00 2001 From: Yuxiu Zhang Date: Mon, 10 Jun 2019 16:13:05 +0800 Subject: [PATCH 1524/1530] Http streaming cannot play video whose duration is less than 5s onPollBuffering() monitors EOS(finalStatus == ERROR_END_OF_STREAM), then notifyPreparedAndCleanup(finalStatus) notifies prepared error. Previous Google's patch "GenericSource: fix error handling during preparing or rebuffering" should focus on error except ERROR_END_OF_STREAM. Bug: 134911527 Test: http streaming can play a mp4 video whose duration is less than 5s Change-Id: I5c738bbe7242314b24463679b5bd996ab59d2b04 (cherry picked from commit 89703072ec4edf7a384ec07024debc0a6e085ae0) --- media/libmediaplayerservice/nuplayer/GenericSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index 2cd920a106..46537114f1 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -1578,7 +1578,7 @@ void NuPlayer::GenericSource::onPollBuffering() { } if (mPreparing) { - notifyPreparedAndCleanup(finalStatus); + notifyPreparedAndCleanup(finalStatus == ERROR_END_OF_STREAM ? OK : finalStatus); mPreparing = false; } else if (mSentPauseOnBuffering) { sendCacheStats(); -- GitLab From 4d641386b33e7323fe8c44d4fc538049736ee4ee Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 26 Jun 2019 14:46:02 -0700 Subject: [PATCH 1525/1530] Camera: Clarify manual capture behavior for logical camera Test: Build and read docs Bug: 136098496 Change-Id: I2d62c383798b86c3fa4f7bcb9d55cd990a6d1718 --- .../ndk/include/camera/NdkCameraMetadataTags.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 7cd832afbb..8dd6e00e49 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -7689,14 +7689,14 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * 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.

    - *

    To maintain backward compatibility, the capture request and result metadata tags - * required for basic camera functionalities will be solely based on the - * logical camera capabiltity. Other request and result metadata tags, on the other - * hand, will be based on current active physical camera. For example, the physical - * cameras' sensor sensitivity and lens capability could be different from each other. - * So when the application manually controls sensor exposure time/gain, or does manual - * focus control, it must checks the current active physical camera's exposure, gain, - * and focus distance range.

    + *

    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 + * 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, + * when the application manually sets exposure time and sensitivity while zooming in, + * the brightness of the camera images may suddenly change because HAL switches from one + * physical camera to the other.

    * * @see ACAMERA_LENS_DISTORTION * @see ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION @@ -8330,7 +8330,7 @@ typedef enum acamera_metadata_enum_acamera_info_supported_hardware_level { * fire the flash for flash power metering during precapture, and then fire the flash * for the final capture, if a flash is available on the device and the AE mode is set to * enable the flash.

    - *

    Devices that initially shipped with Android version Q or newer will not include any LEGACY-level devices.

    + *

    Devices that initially shipped with Android version Q or newer will not include any LEGACY-level devices.

    * * @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES -- GitLab From 831b8d791477982fc6c7bee4b477532d5e5e7fbe Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 11 Jun 2019 17:52:56 -0700 Subject: [PATCH 1526/1530] codec2: offload fence waiting for input surfaces into another thread Bug: 131800183 Test: manual Change-Id: I0d05764eea683210ace5a7d1d9108544cdbaee51 --- media/codec2/sfplugin/C2OMXNode.cpp | 138 +++++++++++++++++++++++----- media/codec2/sfplugin/C2OMXNode.h | 3 + 2 files changed, 118 insertions(+), 23 deletions(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index 3a93c2ad46..d27fe32438 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "C2OMXNode.h" @@ -50,16 +51,126 @@ public: } // namespace +class C2OMXNode::QueueThread : public Thread { +public: + QueueThread() : Thread(false) {} + ~QueueThread() override = default; + void queue( + const std::shared_ptr &comp, + int fenceFd, + std::unique_ptr &&work, + android::base::unique_fd &&fd0, + android::base::unique_fd &&fd1) { + Mutexed::Locked jobs(mJobs); + auto it = jobs->queues.try_emplace(comp, comp, systemTime()).first; + it->second.workList.emplace_back( + std::move(work), fenceFd, std::move(fd0), std::move(fd1)); + jobs->cond.broadcast(); + } + +protected: + bool threadLoop() override { + constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms + constexpr nsecs_t kWaitNs = kIntervalNs * 2; + for (int i = 0; i < 2; ++i) { + Mutexed::Locked jobs(mJobs); + nsecs_t nowNs = systemTime(); + bool queued = false; + for (auto it = jobs->queues.begin(); it != jobs->queues.end(); ++it) { + Queue &queue = it->second; + if (queue.workList.empty() + || nowNs - queue.lastQueuedTimestampNs < kIntervalNs) { + continue; + } + std::shared_ptr comp = queue.component.lock(); + if (!comp) { + it = jobs->queues.erase(it); + continue; + } + std::list> items; + std::vector fenceFds; + std::vector uniqueFds; + while (!queue.workList.empty()) { + items.push_back(std::move(queue.workList.front().work)); + fenceFds.push_back(queue.workList.front().fenceFd); + uniqueFds.push_back(std::move(queue.workList.front().fd0)); + uniqueFds.push_back(std::move(queue.workList.front().fd1)); + queue.workList.pop_front(); + } + + jobs.unlock(); + for (int fenceFd : fenceFds) { + sp fence(new Fence(fenceFd)); + fence->waitForever(LOG_TAG); + } + comp->queue(&items); + for (android::base::unique_fd &ufd : uniqueFds) { + (void)ufd.release(); + } + jobs.lock(); + + queued = true; + } + if (queued) { + return true; + } + if (i == 0) { + jobs.waitForConditionRelative(jobs->cond, kWaitNs); + } + } + return true; + } + +private: + struct WorkFence { + WorkFence(std::unique_ptr &&w, int fd) : work(std::move(w)), fenceFd(fd) {} + + WorkFence( + std::unique_ptr &&w, + int fd, + android::base::unique_fd &&uniqueFd0, + android::base::unique_fd &&uniqueFd1) + : work(std::move(w)), + fenceFd(fd), + fd0(std::move(uniqueFd0)), + fd1(std::move(uniqueFd1)) {} + + std::unique_ptr work; + int fenceFd; + android::base::unique_fd fd0; + android::base::unique_fd fd1; + }; + struct Queue { + Queue(const std::shared_ptr &comp, nsecs_t timestamp) + : component(comp), lastQueuedTimestampNs(timestamp) {} + Queue(const Queue &) = delete; + Queue &operator =(const Queue &) = delete; + + std::weak_ptr component; + std::list workList; + nsecs_t lastQueuedTimestampNs; + }; + struct Jobs { + std::map, + Queue, + std::owner_less>> queues; + Condition cond; + }; + Mutexed mJobs; +}; + C2OMXNode::C2OMXNode(const std::shared_ptr &comp) : mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), mUsage(0), - mAdjustTimestampGapUs(0), mFirstInputFrame(true) { + mAdjustTimestampGapUs(0), mFirstInputFrame(true), + mQueueThread(new QueueThread) { android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS); + mQueueThread->run("C2OMXNode", PRIORITY_AUDIO); } status_t C2OMXNode::freeNode() { mComp.reset(); android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE); - return OK; + return mQueueThread->requestExitAndWait(); } status_t C2OMXNode::sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) { @@ -216,11 +327,6 @@ status_t C2OMXNode::fillBuffer( status_t C2OMXNode::emptyBuffer( buffer_id buffer, const OMXBuffer &omxBuf, OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) { - // TODO: better fence handling - if (fenceFd >= 0) { - sp fence = new Fence(fenceFd); - fence->waitForever(LOG_TAG); - } std::shared_ptr comp = mComp.lock(); if (!comp) { return NO_INIT; @@ -299,22 +405,8 @@ status_t C2OMXNode::emptyBuffer( } work->worklets.clear(); work->worklets.emplace_back(new C2Worklet); - std::list> items; - uint64_t index = work->input.ordinal.frameIndex.peeku(); - items.push_back(std::move(work)); - - c2_status_t err = comp->queue(&items); - if (err != C2_OK) { - (void)fd0.release(); - (void)fd1.release(); - return UNKNOWN_ERROR; - } - - mBufferIdsInUse.lock()->emplace(index, buffer); - - // release ownership of the fds - (void)fd0.release(); - (void)fd1.release(); + mBufferIdsInUse.lock()->emplace(work->input.ordinal.frameIndex.peeku(), buffer); + mQueueThread->queue(comp, fenceFd, std::move(work), std::move(fd0), std::move(fd1)); return OK; } diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h index 3ca6c0aeca..1717c96483 100644 --- a/media/codec2/sfplugin/C2OMXNode.h +++ b/media/codec2/sfplugin/C2OMXNode.h @@ -113,6 +113,9 @@ private: c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame Mutexed> mBufferIdsInUse; + + class QueueThread; + sp mQueueThread; }; } // namespace android -- GitLab From 91cc4833c8befec65d7032b73ce214c0c444dd7c Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Thu, 27 Jun 2019 11:25:58 -0700 Subject: [PATCH 1527/1530] C2SoftVpxEnc: Make VPX_VBR as default mode Use VPX_VBR as default bitrate mode instead of VPX_CBR VideoEncoderDecoderTest in CTS doesn't set bitrate mode and VPX_CBR mode shows encoding artifacts when running that test Test: cts-tradefed run cts -m CtsVideoTestCases -t \ android.video.cts.VideoEncoderDecoderTest#testAvcGoog0Qual0640x0360 Bug: 135891350 Change-Id: I488273113b4d5cf4ae2f4b8208fcebdd095aa6f5 --- media/codec2/components/vpx/C2SoftVpxEnc.cpp | 10 +++++----- media/codec2/components/vpx/C2SoftVpxEnc.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp index 6509a88a94..6dab70b53b 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp +++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp @@ -127,14 +127,14 @@ status_t C2SoftVpxEnc::initEncoder() { } switch (mBitrateMode->value) { - case C2Config::BITRATE_VARIABLE: - mBitrateControlMode = VPX_VBR; - break; case C2Config::BITRATE_CONST: - default: mBitrateControlMode = VPX_CBR; break; - break; + case C2Config::BITRATE_VARIABLE: + [[fallthrough]]; + default: + mBitrateControlMode = VPX_VBR; + break; } setCodecSpecificInterface(); diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h index 90758f9d20..62ccd1b0fd 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.h +++ b/media/codec2/components/vpx/C2SoftVpxEnc.h @@ -275,7 +275,7 @@ class C2SoftVpxEnc::IntfImpl : public SimpleInterface::BaseParams { addParameter( DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE) .withDefault(new C2StreamBitrateModeTuning::output( - 0u, C2Config::BITRATE_CONST)) + 0u, C2Config::BITRATE_VARIABLE)) .withFields({ C2F(mBitrateMode, value).oneOf({ C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE }) -- GitLab From f0e7d226182e0549d625dea81d24974f38d8c449 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 28 Jun 2019 12:33:16 -0700 Subject: [PATCH 1528/1530] codec2: fix output delay update Bug: 134635546 Test: bug repro steps Change-Id: Ie14fc780246cdf404f323bf9a88ffcb1a403eb32 --- media/codec2/sfplugin/CCodecBufferChannel.cpp | 45 ++++++++++++------- media/codec2/sfplugin/CCodecBufferChannel.h | 3 +- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index 8be9a1d125..9778498be1 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -220,7 +220,6 @@ CCodecBufferChannel::CCodecBufferChannel( const std::shared_ptr &callback) : mHeapSeqNum(-1), mCCodecCallback(callback), - mDelay(0), mFrameIndex(0u), mFirstValidFrameIndex(0u), mMetaMode(MODE_NONE), @@ -814,7 +813,6 @@ status_t CCodecBufferChannel::start( size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor; size_t numOutputSlots = outputDelayValue + kSmoothnessFactor; - mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue; // TODO: get this from input format bool secure = mComponent->getName().find(".secure") != std::string::npos; @@ -1377,25 +1375,35 @@ bool CCodecBufferChannel::handleWork( (void)mPipelineWatcher.lock()->outputDelay(outputDelay.value); bool outputBuffersChanged = false; - Mutexed::Locked output(mOutput); - output->outputDelay = outputDelay.value; - size_t numOutputSlots = outputDelay.value + kSmoothnessFactor; - if (output->numSlots < numOutputSlots) { - output->numSlots = numOutputSlots; - if (output->buffers->isArrayMode()) { - OutputBuffersArray *array = - (OutputBuffersArray *)output->buffers.get(); - ALOGV("[%s] onWorkDone: growing output buffer array to %zu", - mName, numOutputSlots); - array->grow(numOutputSlots); - outputBuffersChanged = true; + size_t numOutputSlots = 0; + { + Mutexed::Locked output(mOutput); + output->outputDelay = outputDelay.value; + numOutputSlots = outputDelay.value + kSmoothnessFactor; + if (output->numSlots < numOutputSlots) { + output->numSlots = numOutputSlots; + if (output->buffers->isArrayMode()) { + OutputBuffersArray *array = + (OutputBuffersArray *)output->buffers.get(); + ALOGV("[%s] onWorkDone: growing output buffer array to %zu", + mName, numOutputSlots); + array->grow(numOutputSlots); + outputBuffersChanged = true; + } } + numOutputSlots = output->numSlots; } - output.unlock(); if (outputBuffersChanged) { mCCodecCallback->onOutputBuffersChanged(); } + + uint32_t depth = mReorderStash.lock()->depth(); + Mutexed::Locked output(mOutputSurface); + output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth; + if (output->surface) { + output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers); + } } } break; @@ -1620,7 +1628,12 @@ PipelineWatcher::Clock::duration CCodecBufferChannel::elapsed() { // When client pushed EOS, we want all the work to be done quickly. // Otherwise, component may have stalled work due to input starvation up to // the sum of the delay in the pipeline. - size_t n = mInputMetEos ? 0 : mDelay; + size_t n = 0; + if (!mInputMetEos) { + size_t outputDelay = mOutput.lock()->outputDelay; + Mutexed::Locked input(mInput); + n = input->inputDelay + input->pipelineDelay + outputDelay; + } return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now(), n); } diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index ae5767888a..ee3455d567 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -228,8 +228,6 @@ private: QueueSync mQueueSync; std::vector> mParamsToBeSet; - size_t mDelay; - struct Input { Input(); @@ -306,6 +304,7 @@ private: const C2WorkOrdinalStruct &ordinal); void defer(const Entry &entry); bool hasPending() const; + uint32_t depth() const { return mDepth; } private: std::list mPending; -- GitLab From 976d6e16ee3371a53e3bdec62a6e330cec509cb8 Mon Sep 17 00:00:00 2001 From: Sungtak Lee Date: Fri, 28 Jun 2019 14:49:19 -0700 Subject: [PATCH 1529/1530] C2BqPool: Handle generation # retrieval failure Handle dequeueBuffer failure during generation # retrieval. Bug: 136156498 Change-Id: I06c1ead59494b6b1bf5eef49b0c28003989b2094 --- media/codec2/vndk/platform/C2BqBuffer.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 5fa48a8225..8304f74e80 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -413,6 +413,7 @@ private: } sp &slotBuffer = mBuffers[slot]; + uint32_t outGeneration; if (bufferNeedsReallocation || !slotBuffer) { if (!slotBuffer) { slotBuffer = new GraphicBuffer(); @@ -421,7 +422,7 @@ private: // instead of a new allocation. Return transResult = mProducer->requestBuffer( slot, - [&status, &slotBuffer]( + [&status, &slotBuffer, &outGeneration]( HStatus hStatus, HBuffer const& hBuffer, uint32_t generationNumber){ @@ -429,17 +430,23 @@ private: h2b(hBuffer, &slotBuffer) && slotBuffer) { slotBuffer->setGenerationNumber(generationNumber); + outGeneration = generationNumber; } else { status = android::BAD_VALUE; } }); if (!transResult.isOk()) { + slotBuffer.clear(); return C2_BAD_VALUE; } else if (status != android::NO_ERROR) { slotBuffer.clear(); (void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk(); return C2_BAD_VALUE; } + if (mGeneration == 0) { + // getting generation # lazily due to dequeue failure. + mGeneration = outGeneration; + } } if (slotBuffer) { ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot); @@ -563,6 +570,10 @@ public: producerId = static_cast(transResult); // TODO: provide gneration number from parameter. haveGeneration = getGenerationNumber(producer, &generation); + if (!haveGeneration) { + ALOGW("get generationNumber failed %llu", + (unsigned long long)producerId); + } } int migrated = 0; { @@ -580,10 +591,10 @@ public: } } int32_t oldGeneration = mGeneration; - if (producer && haveGeneration) { + if (producer) { mProducer = producer; mProducerId = producerId; - mGeneration = generation; + mGeneration = haveGeneration ? generation : 0; } else { mProducer = nullptr; mProducerId = 0; @@ -591,7 +602,7 @@ public: ALOGW("invalid producer producer(%d), generation(%d)", (bool)producer, haveGeneration); } - if (mProducer) { // migrate buffers + if (mProducer && haveGeneration) { // migrate buffers for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { std::shared_ptr data = mPoolDatas[i].lock(); -- GitLab From 0ca30c332e72a7c3f2626d4487a76af8c74be9f7 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 28 Jun 2019 14:15:02 -0700 Subject: [PATCH 1530/1530] codec2: fix rare native crash in buffer queue thread Bug: 131800183 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I46a90751ff45577897bdcffbe912db12993a7467 --- media/codec2/sfplugin/C2OMXNode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp index d27fe32438..78d221ebb3 100644 --- a/media/codec2/sfplugin/C2OMXNode.cpp +++ b/media/codec2/sfplugin/C2OMXNode.cpp @@ -76,10 +76,11 @@ protected: Mutexed::Locked jobs(mJobs); nsecs_t nowNs = systemTime(); bool queued = false; - for (auto it = jobs->queues.begin(); it != jobs->queues.end(); ++it) { + for (auto it = jobs->queues.begin(); it != jobs->queues.end(); ) { Queue &queue = it->second; if (queue.workList.empty() || nowNs - queue.lastQueuedTimestampNs < kIntervalNs) { + ++it; continue; } std::shared_ptr comp = queue.component.lock(); @@ -109,6 +110,7 @@ protected: } jobs.lock(); + it = jobs->queues.upper_bound(comp); queued = true; } if (queued) { -- GitLab